summaryrefslogtreecommitdiffstats
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/.gitignore4
-rw-r--r--src/test/CMakeLists.txt983
-rw-r--r--src/test/ObjectMap/CMakeLists.txt42
-rw-r--r--src/test/ObjectMap/KeyValueDBMemory.cc264
-rw-r--r--src/test/ObjectMap/KeyValueDBMemory.h188
-rw-r--r--src/test/ObjectMap/test_keyvaluedb_atomicity.cc109
-rw-r--r--src/test/ObjectMap/test_keyvaluedb_iterators.cc1756
-rw-r--r--src/test/ObjectMap/test_object_map.cc1126
-rw-r--r--src/test/TestSignalHandlers.cc117
-rw-r--r--src/test/TestTimers.cc281
-rw-r--r--src/test/admin_socket.cc341
-rwxr-xr-xsrc/test/admin_socket/objecter_requests60
-rw-r--r--src/test/admin_socket/osd_requests28
-rw-r--r--src/test/admin_socket_output.cc242
-rw-r--r--src/test/admin_socket_output.h76
-rw-r--r--src/test/admin_socket_output_tests.cc107
-rw-r--r--src/test/admin_socket_output_tests.h28
-rw-r--r--src/test/barclass.cc48
-rw-r--r--src/test/base64.cc102
-rw-r--r--src/test/behave_tests/README.md50
-rw-r--r--src/test/behave_tests/features/ceph_osd_test.feature49
-rw-r--r--src/test/behave_tests/features/ceph_shell_test.feature64
-rw-r--r--src/test/behave_tests/features/cephadm_test.feature24
-rw-r--r--src/test/behave_tests/features/environment.py207
-rw-r--r--src/test/behave_tests/features/kcli_handler.py88
-rw-r--r--src/test/behave_tests/features/steps/ceph_steps.py106
-rw-r--r--src/test/behave_tests/features/validation_util.py19
-rw-r--r--src/test/behave_tests/template/bootstrap_script_template30
-rw-r--r--src/test/behave_tests/template/kcli_plan_template41
-rw-r--r--src/test/behave_tests/tox.ini22
-rw-r--r--src/test/bench_journald_logger.cc20
-rw-r--r--src/test/bench_log.cc86
-rw-r--r--src/test/bufferlist.cc3083
-rw-r--r--src/test/buildtest_skeleton.cc13
-rw-r--r--src/test/centos-8/Dockerfile.in31
l---------src/test/centos-8/ceph.spec.in1
l---------src/test/centos-8/install-deps.sh1
-rwxr-xr-xsrc/test/ceph-erasure-code-tool/test_ceph-erasure-code-tool.sh43
-rw-r--r--src/test/ceph_argparse.cc544
-rw-r--r--src/test/ceph_compatset.cc167
-rw-r--r--src/test/ceph_crypto.cc286
-rw-r--r--src/test/cli-integration/balancer/misplaced.t30
-rw-r--r--src/test/cli-integration/rbd/defaults.t310
-rw-r--r--src/test/cli-integration/rbd/formatted-output.t1171
-rw-r--r--src/test/cli-integration/rbd/gwcli_create.t78
-rw-r--r--src/test/cli-integration/rbd/gwcli_delete.t31
-rw-r--r--src/test/cli-integration/rbd/iscsi_client.t30
-rw-r--r--src/test/cli-integration/rbd/mon-command-help.t44
-rw-r--r--src/test/cli-integration/rbd/rest_api_create.t44
-rw-r--r--src/test/cli-integration/rbd/rest_api_delete.t19
-rw-r--r--src/test/cli-integration/rbd/snap-diff.t48
-rw-r--r--src/test/cli-integration/rbd/unmap.t488
-rw-r--r--src/test/cli/.gitignore1
-rw-r--r--src/test/cli/ceph-authtool/add-key-segv.t6
-rw-r--r--src/test/cli/ceph-authtool/add-key.t27
-rw-r--r--src/test/cli/ceph-authtool/cap-bin.t6
-rw-r--r--src/test/cli/ceph-authtool/cap-invalid.t12
-rw-r--r--src/test/cli/ceph-authtool/cap-overwrite.t11
-rw-r--r--src/test/cli/ceph-authtool/cap.t11
-rw-r--r--src/test/cli/ceph-authtool/create-gen-list-bin.t16
-rw-r--r--src/test/cli/ceph-authtool/create-gen-list.t20
-rw-r--r--src/test/cli/ceph-authtool/help.t25
-rw-r--r--src/test/cli/ceph-authtool/list-empty-bin.t5
-rw-r--r--src/test/cli/ceph-authtool/list-empty.t5
-rw-r--r--src/test/cli/ceph-authtool/list-nonexistent-bin.t7
-rw-r--r--src/test/cli/ceph-authtool/list-nonexistent.t7
-rw-r--r--src/test/cli/ceph-authtool/manpage.t32
-rw-r--r--src/test/cli/ceph-authtool/simple.t3
-rw-r--r--src/test/cli/ceph-conf/env-vs-args.t14
-rw-r--r--src/test/cli/ceph-conf/help.t44
-rw-r--r--src/test/cli/ceph-conf/invalid-args.t17
-rw-r--r--src/test/cli/ceph-conf/manpage.t33
-rw-r--r--src/test/cli/ceph-conf/option.t64
-rw-r--r--src/test/cli/ceph-conf/sections.t18
-rw-r--r--src/test/cli/ceph-conf/show-config-value.t39
-rw-r--r--src/test/cli/ceph-conf/show-config.t7
-rw-r--r--src/test/cli/ceph-conf/simple.t4
-rw-r--r--src/test/cli/ceph-kvstore-tool/help.t23
-rw-r--r--src/test/cli/crushtool/add-bucket.t63
-rw-r--r--src/test/cli/crushtool/add-item-in-tree.t10
-rw-r--r--src/test/cli/crushtool/add-item.t133
-rw-r--r--src/test/cli/crushtool/adjust-item-weight.t17
-rw-r--r--src/test/cli/crushtool/arg-order-checks.t730
-rw-r--r--src/test/cli/crushtool/bad-mappings.crushmap.txt35
-rw-r--r--src/test/cli/crushtool/bad-mappings.t6
-rw-r--r--src/test/cli/crushtool/build.t75
-rw-r--r--src/test/cli/crushtool/check-invalid-map.t3
-rw-r--r--src/test/cli/crushtool/check-names.empty.crushmap.txt11
-rw-r--r--src/test/cli/crushtool/check-names.empty.t5
-rw-r--r--src/test/cli/crushtool/check-names.max-id.t8
-rw-r--r--src/test/cli/crushtool/check-overlapped-rules.crushmap.txt75
-rw-r--r--src/test/cli/crushtool/choose-args.crush118
-rw-r--r--src/test/cli/crushtool/choose-args.t276
-rw-r--r--src/test/cli/crushtool/compile-decompile-recompile.t17
-rw-r--r--src/test/cli/crushtool/crush-classes/abin0 -> 2358 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/bbin0 -> 20656 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/beeslybin0 -> 64806 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/cbin0 -> 8801 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/dbin0 -> 3657 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/ebin0 -> 7094 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/fbin0 -> 61002 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/flaxbin0 -> 8184 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/gbin0 -> 43071 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/gabebin0 -> 61114 bytes
-rw-r--r--src/test/cli/crushtool/crush-classes/gabe2bin0 -> 61002 bytes
-rw-r--r--src/test/cli/crushtool/device-class.crush86
-rw-r--r--src/test/cli/crushtool/device-class.t6
-rw-r--r--src/test/cli/crushtool/empty-default.cushmap.txt42
-rw-r--r--src/test/cli/crushtool/empty-default.t4
-rw-r--r--src/test/cli/crushtool/five-devices.crushmapbin0 -> 368 bytes
-rw-r--r--src/test/cli/crushtool/help.t165
-rw-r--r--src/test/cli/crushtool/location.t16
-rw-r--r--src/test/cli/crushtool/missing-bucket.crushmap.txt39
-rw-r--r--src/test/cli/crushtool/multitype.after81
-rw-r--r--src/test/cli/crushtool/multitype.before81
-rw-r--r--src/test/cli/crushtool/need_tree_order.crush62
-rw-r--r--src/test/cli/crushtool/output-csv.t51
-rw-r--r--src/test/cli/crushtool/reclassify.t562
-rw-r--r--src/test/cli/crushtool/reweight.t8
-rw-r--r--src/test/cli/crushtool/reweight_multiple.t5
-rw-r--r--src/test/cli/crushtool/rules.t155
-rw-r--r--src/test/cli/crushtool/rules.txt54
-rw-r--r--src/test/cli/crushtool/set-choose.crushmap.txt132
-rw-r--r--src/test/cli/crushtool/set-choose.t36944
-rw-r--r--src/test/cli/crushtool/show-choose-tries.t109
-rw-r--r--src/test/cli/crushtool/show-choose-tries.txt43
-rw-r--r--src/test/cli/crushtool/simple.templatebin0 -> 316 bytes
-rw-r--r--src/test/cli/crushtool/simple.template.adj.one50
-rw-r--r--src/test/cli/crushtool/simple.template.adj.three58
-rw-r--r--src/test/cli/crushtool/simple.template.adj.two58
-rw-r--r--src/test/cli/crushtool/simple.template.five59
-rw-r--r--src/test/cli/crushtool/simple.template.four50
-rw-r--r--src/test/cli/crushtool/simple.template.multitree64
-rw-r--r--src/test/cli/crushtool/simple.template.multitree.reweighted67
-rw-r--r--src/test/cli/crushtool/simple.template.one52
-rw-r--r--src/test/cli/crushtool/simple.template.three52
-rw-r--r--src/test/cli/crushtool/simple.template.two52
-rw-r--r--src/test/cli/crushtool/straw2.t4
-rw-r--r--src/test/cli/crushtool/straw2.txt41
-rw-r--r--src/test/cli/crushtool/test-map-a.crushmapbin0 -> 31995 bytes
-rw-r--r--src/test/cli/crushtool/test-map-big-1.crushmapbin0 -> 8958 bytes
-rw-r--r--src/test/cli/crushtool/test-map-bobtail-tunables.t10253
-rw-r--r--src/test/cli/crushtool/test-map-firefly-tunables.t10259
-rw-r--r--src/test/cli/crushtool/test-map-firstn-indep.t14
-rw-r--r--src/test/cli/crushtool/test-map-firstn-indep.txt439
-rw-r--r--src/test/cli/crushtool/test-map-hammer-tunables.crushmapbin0 -> 3386 bytes
-rw-r--r--src/test/cli/crushtool/test-map-hammer-tunables.t10252
-rw-r--r--src/test/cli/crushtool/test-map-indep.crushmapbin0 -> 31995 bytes
-rw-r--r--src/test/cli/crushtool/test-map-indep.t10253
-rw-r--r--src/test/cli/crushtool/test-map-jewel-tunables.crushmapbin0 -> 3387 bytes
-rw-r--r--src/test/cli/crushtool/test-map-jewel-tunables.t10252
-rw-r--r--src/test/cli/crushtool/test-map-legacy-tunables.t10252
-rw-r--r--src/test/cli/crushtool/test-map-tries-vs-retries.crushmapbin0 -> 808 bytes
-rw-r--r--src/test/cli/crushtool/test-map-tries-vs-retries.t10259
-rw-r--r--src/test/cli/crushtool/test-map-vary-r-0.t3081
-rw-r--r--src/test/cli/crushtool/test-map-vary-r-1.t3078
-rw-r--r--src/test/cli/crushtool/test-map-vary-r-2.t3078
-rw-r--r--src/test/cli/crushtool/test-map-vary-r-3.t3078
-rw-r--r--src/test/cli/crushtool/test-map-vary-r-4.t3078
-rw-r--r--src/test/cli/crushtool/test-map-vary-r.crushmapbin0 -> 3892 bytes
-rw-r--r--src/test/cli/crushtool/tree.templatebin0 -> 376 bytes
-rw-r--r--src/test/cli/crushtool/tree.template.final64
-rw-r--r--src/test/cli/monmaptool/add-exists.t29
-rw-r--r--src/test/cli/monmaptool/add-many.t35
-rw-r--r--src/test/cli/monmaptool/clobber.t44
-rw-r--r--src/test/cli/monmaptool/create-print.t23
-rw-r--r--src/test/cli/monmaptool/create-with-add.t15
-rw-r--r--src/test/cli/monmaptool/feature-set-unset-list.t87
-rw-r--r--src/test/cli/monmaptool/help.t11
-rw-r--r--src/test/cli/monmaptool/print-empty.t5
-rw-r--r--src/test/cli/monmaptool/print-nonexistent.t4
-rw-r--r--src/test/cli/monmaptool/rm-nonexistent.t27
-rw-r--r--src/test/cli/monmaptool/rm.t24
-rw-r--r--src/test/cli/monmaptool/simple.t3
-rw-r--r--src/test/cli/osdmaptool/ceph.conf.withracks1480
-rw-r--r--src/test/cli/osdmaptool/clobber.t65
-rw-r--r--src/test/cli/osdmaptool/create-print.t97
-rw-r--r--src/test/cli/osdmaptool/create-racks.t810
-rw-r--r--src/test/cli/osdmaptool/crush.t17
-rw-r--r--src/test/cli/osdmaptool/help.t42
-rw-r--r--src/test/cli/osdmaptool/missing-argument.t3
-rw-r--r--src/test/cli/osdmaptool/pool.t54
-rw-r--r--src/test/cli/osdmaptool/print-empty.t5
-rw-r--r--src/test/cli/osdmaptool/print-nonexistent.t4
-rw-r--r--src/test/cli/osdmaptool/test-map-pgs.t43
-rw-r--r--src/test/cli/osdmaptool/tree.t95
-rw-r--r--src/test/cli/osdmaptool/upmap-out.t24
-rw-r--r--src/test/cli/osdmaptool/upmap.t37
-rw-r--r--src/test/cli/radosgw-admin/help.t393
-rw-r--r--src/test/cli/rbd/help.t2666
-rw-r--r--src/test/cli/rbd/invalid-snap-usage.t115
-rw-r--r--src/test/cli/rbd/not-enough-args.t222
-rw-r--r--src/test/cli/rbd/too-many-args.t33
-rw-r--r--src/test/client/CMakeLists.txt17
-rw-r--r--src/test/client/TestClient.h150
-rw-r--r--src/test/client/alternate_name.cc197
-rwxr-xr-xsrc/test/client/iozone.sh12
-rwxr-xr-xsrc/test/client/kernel_untar_build.sh13
-rw-r--r--src/test/client/main.cc28
-rw-r--r--src/test/client/ops.cc45
-rw-r--r--src/test/cls_2pc_queue/CMakeLists.txt18
-rw-r--r--src/test/cls_2pc_queue/test_cls_2pc_queue.cc968
-rw-r--r--src/test/cls_cas/CMakeLists.txt17
-rw-r--r--src/test/cls_cas/test_cls_cas.cc368
-rw-r--r--src/test/cls_cmpomap/CMakeLists.txt3
-rw-r--r--src/test/cls_cmpomap/test_cls_cmpomap.cc720
-rw-r--r--src/test/cls_hello/CMakeLists.txt15
-rw-r--r--src/test/cls_hello/test_cls_hello.cc283
-rw-r--r--src/test/cls_journal/CMakeLists.txt16
-rw-r--r--src/test/cls_journal/test_cls_journal.cc695
-rw-r--r--src/test/cls_lock/CMakeLists.txt17
-rw-r--r--src/test/cls_lock/test_cls_lock.cc589
-rw-r--r--src/test/cls_log/CMakeLists.txt16
-rw-r--r--src/test/cls_log/test_cls_log.cc385
-rw-r--r--src/test/cls_lua/CMakeLists.txt16
-rw-r--r--src/test/cls_lua/test_cls_lua.cc1108
-rw-r--r--src/test/cls_numops/CMakeLists.txt16
-rw-r--r--src/test/cls_numops/test_cls_numops.cc414
-rw-r--r--src/test/cls_queue/CMakeLists.txt17
-rw-r--r--src/test/cls_queue/test_cls_queue.cc607
-rw-r--r--src/test/cls_rbd/CMakeLists.txt18
-rw-r--r--src/test/cls_rbd/test_cls_rbd.cc3428
-rw-r--r--src/test/cls_refcount/CMakeLists.txt18
-rw-r--r--src/test/cls_refcount/test_cls_refcount.cc778
-rw-r--r--src/test/cls_rgw/CMakeLists.txt24
-rw-r--r--src/test/cls_rgw/test_cls_rgw.cc1342
-rw-r--r--src/test/cls_rgw/test_cls_rgw_stats.cc646
-rw-r--r--src/test/cls_rgw_gc/CMakeLists.txt18
-rw-r--r--src/test/cls_rgw_gc/test_cls_rgw_gc.cc700
-rw-r--r--src/test/cls_sdk/CMakeLists.txt15
-rw-r--r--src/test/cls_sdk/test_cls_sdk.cc35
-rw-r--r--src/test/cls_version/CMakeLists.txt16
-rw-r--r--src/test/cls_version/test_cls_version.cc322
-rw-r--r--src/test/common/CMakeLists.txt392
-rw-r--r--src/test/common/ObjectContents.cc128
-rw-r--r--src/test/common/ObjectContents.h122
-rw-r--r--src/test/common/Readahead.cc132
-rw-r--r--src/test/common/Throttle.cc386
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n117
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n1.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/cpach.sdn53
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/cpach.sdn.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda31
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb32
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/erwan131
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n125
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n1.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/gnit.sda46
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/gnit.sda.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/mira055.sda28
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/mira055.sda.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/mira055.sdb28
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/mira055.sdb.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/mira055.sde28
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/mira055.sde.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n116
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n1.devid1
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/stud.nvme0n129
-rw-r--r--src/test/common/blkdev-udevadm-info-samples/stud.nvme0n1.devid1
-rw-r--r--src/test/common/dns_messages.h100
-rw-r--r--src/test/common/dns_resolve.cc263
-rw-r--r--src/test/common/get_command_descriptions.cc131
-rw-r--r--src/test/common/histogram.cc129
-rw-r--r--src/test/common/test_allocate_unique.cc97
-rw-r--r--src/test/common/test_async_completion.cc256
-rw-r--r--src/test/common/test_async_shared_mutex.cc428
-rw-r--r--src/test/common/test_back_trace.cc44
-rw-r--r--src/test/common/test_bit_vector.cc308
-rw-r--r--src/test/common/test_blkdev.cc114
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/add_random1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_granularity1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_max_bytes1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_zeroes_data1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/hw_sector_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/fifo_batch1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/front_merges1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/read_expire1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/write_expire1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/writes_starved1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iostats1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/logical_block_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_hw_sectors_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_integrity_segments1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_sectors_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segment_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segments1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/minimum_io_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nomerges1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nr_requests1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/optimal_io_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/physical_block_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/read_ahead_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rotational1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rq_affinity1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/scheduler1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/write_same_max_bytes1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/bar0
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/dev0
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/device/model1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/foo0
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/add_random1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_granularity1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_max_bytes1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_zeroes_data1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/hw_sector_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/fifo_batch1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/front_merges1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/read_expire1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/write_expire1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/writes_starved1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iostats1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/logical_block_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_hw_sectors_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_integrity_segments1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_sectors_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segment_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segments1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/minimum_io_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nomerges1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nr_requests1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/optimal_io_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/physical_block_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/read_ahead_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rotational1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rq_affinity1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/scheduler1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sda/queue/write_same_max_bytes1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/bar0
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/dev0
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/device/model1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/foo0
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/add_random1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_granularity1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_max_bytes1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_zeroes_data1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/hw_sector_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/fifo_batch1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/front_merges1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/read_expire1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/write_expire1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/writes_starved1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iostats1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/logical_block_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_hw_sectors_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_integrity_segments1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_sectors_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segment_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segments1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/minimum_io_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nomerges1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nr_requests1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/optimal_io_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/physical_block_size1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/queue1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/read_ahead_kb1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rotational1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rq_affinity1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/scheduler1
-rw-r--r--src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/write_same_max_bytes1
-rw-r--r--src/test/common/test_blocked_completion.cc237
-rw-r--r--src/test/common/test_bloom_filter.cc323
-rw-r--r--src/test/common/test_bounded_key_counter.cc200
-rw-r--r--src/test/common/test_cdc.cc163
-rw-r--r--src/test/common/test_ceph_timer.cc163
-rw-r--r--src/test/common/test_config.cc313
-rw-r--r--src/test/common/test_context.cc145
-rw-r--r--src/test/common/test_convenience.cc69
-rw-r--r--src/test/common/test_counter.cc40
-rw-r--r--src/test/common/test_crc32c.cc365
-rw-r--r--src/test/common/test_fair_mutex.cc68
-rw-r--r--src/test/common/test_fault_injector.cc248
-rw-r--r--src/test/common/test_global_doublefree.cc30
-rw-r--r--src/test/common/test_hobject.cc11
-rw-r--r--src/test/common/test_hostname.cc71
-rw-r--r--src/test/common/test_interval_map.cc337
-rw-r--r--src/test/common/test_interval_set.cc600
-rw-r--r--src/test/common/test_intrusive_lru.cc208
-rw-r--r--src/test/common/test_iso_8601.cc60
-rw-r--r--src/test/common/test_journald_logger.cc41
-rw-r--r--src/test/common/test_json_formattable.cc453
-rw-r--r--src/test/common/test_json_formatter.cc81
-rw-r--r--src/test/common/test_lockdep.cc74
-rw-r--r--src/test/common/test_lru.cc158
-rw-r--r--src/test/common/test_lruset.cc109
-rw-r--r--src/test/common/test_mclock_priority_queue.cc320
-rw-r--r--src/test/common/test_mutex_debug.cc101
-rw-r--r--src/test/common/test_numa.cc72
-rw-r--r--src/test/common/test_option.cc73
-rw-r--r--src/test/common/test_perf_counters_key.cc129
-rw-r--r--src/test/common/test_perf_histogram.cc247
-rw-r--r--src/test/common/test_pretty_binary.cc50
-rw-r--r--src/test/common/test_prioritized_queue.cc206
-rw-r--r--src/test/common/test_rabin_chunk.cc151
-rw-r--r--src/test/common/test_random.cc246
-rw-r--r--src/test/common/test_safe_io.cc37
-rw-r--r--src/test/common/test_shared_cache.cc400
-rw-r--r--src/test/common/test_sharedptr_registry.cc330
-rw-r--r--src/test/common/test_shunique_lock.cc575
-rw-r--r--src/test/common/test_sloppy_crc_map.cc116
-rw-r--r--src/test/common/test_split.cc119
-rw-r--r--src/test/common/test_static_ptr.cc216
-rw-r--r--src/test/common/test_str_map.cc89
-rw-r--r--src/test/common/test_tableformatter.cc263
-rw-r--r--src/test/common/test_time.cc235
-rw-r--r--src/test/common/test_url_escape.cc36
-rw-r--r--src/test/common/test_util.cc42
-rw-r--r--src/test/common/test_weighted_priority_queue.cc240
-rw-r--r--src/test/common/test_xmlformatter.cc165
-rw-r--r--src/test/compressor/CMakeLists.txt11
-rw-r--r--src/test/compressor/compressor_example.h53
-rw-r--r--src/test/compressor/compressor_plugin_example.cc59
-rw-r--r--src/test/compressor/osdmaps/osdmap.2982809bin0 -> 612069 bytes
-rw-r--r--src/test/compressor/osdmaps/osdmap.2982809.h38257
-rw-r--r--src/test/compressor/test_compression.cc612
-rw-r--r--src/test/confutils.cc458
-rwxr-xr-xsrc/test/coverage.sh29
-rw-r--r--src/test/crimson/CMakeLists.txt105
-rw-r--r--src/test/crimson/cbt/radosbench_4K_read.yaml36
-rw-r--r--src/test/crimson/cbt/radosbench_4K_write.yaml34
-rwxr-xr-xsrc/test/crimson/cbt/t2c.py78
-rw-r--r--src/test/crimson/gtest_seastar.cc65
-rw-r--r--src/test/crimson/gtest_seastar.h35
-rw-r--r--src/test/crimson/seastar_runner.h102
-rw-r--r--src/test/crimson/seastore/CMakeLists.txt128
-rw-r--r--src/test/crimson/seastore/nvmedevice/test_nvmedevice.cc105
-rw-r--r--src/test/crimson/seastore/onode_tree/CMakeLists.txt15
-rw-r--r--src/test/crimson/seastore/onode_tree/test_fltree_onode_manager.cc330
-rw-r--r--src/test/crimson/seastore/onode_tree/test_staged_fltree.cc1792
-rw-r--r--src/test/crimson/seastore/onode_tree/test_value.h240
-rw-r--r--src/test/crimson/seastore/test_block.cc41
-rw-r--r--src/test/crimson/seastore/test_block.h154
-rw-r--r--src/test/crimson/seastore/test_btree_lba_manager.cc752
-rw-r--r--src/test/crimson/seastore/test_cbjournal.cc583
-rw-r--r--src/test/crimson/seastore/test_collection_manager.cc195
-rw-r--r--src/test/crimson/seastore/test_extent_allocator.cc181
-rw-r--r--src/test/crimson/seastore/test_object_data_handler.cc431
-rw-r--r--src/test/crimson/seastore/test_omap_manager.cc730
-rw-r--r--src/test/crimson/seastore/test_randomblock_manager.cc178
-rw-r--r--src/test/crimson/seastore/test_seastore.cc1268
-rw-r--r--src/test/crimson/seastore/test_seastore_cache.cc260
-rw-r--r--src/test/crimson/seastore/test_seastore_journal.cc343
-rw-r--r--src/test/crimson/seastore/test_transaction_manager.cc1995
-rw-r--r--src/test/crimson/seastore/transaction_manager_test_state.h450
-rw-r--r--src/test/crimson/test_alien_echo.cc294
-rw-r--r--src/test/crimson/test_alienstore_thread_pool.cc78
-rw-r--r--src/test/crimson/test_async_echo.cc234
-rw-r--r--src/test/crimson/test_backfill.cc501
-rw-r--r--src/test/crimson/test_buffer.cc50
-rw-r--r--src/test/crimson/test_config.cc109
-rw-r--r--src/test/crimson/test_denc.cc53
-rw-r--r--src/test/crimson/test_errorator.cc99
-rw-r--r--src/test/crimson/test_fixed_kv_node_layout.cc376
-rw-r--r--src/test/crimson/test_interruptible_future.cc301
-rw-r--r--src/test/crimson/test_lru.cc213
-rw-r--r--src/test/crimson/test_messenger.cc3874
-rw-r--r--src/test/crimson/test_messenger.h95
-rw-r--r--src/test/crimson/test_messenger_peer.cc462
-rw-r--r--src/test/crimson/test_messenger_thrash.cc672
-rw-r--r--src/test/crimson/test_monc.cc84
-rw-r--r--src/test/crimson/test_perfcounters.cc62
-rw-r--r--src/test/crimson/test_socket.cc558
-rw-r--r--src/test/crush/CMakeLists.txt13
-rw-r--r--src/test/crush/CrushWrapper.cc1458
-rw-r--r--src/test/crush/crush-choose-args-expected-one-more-0.txt75
-rw-r--r--src/test/crush/crush-choose-args-expected-one-more-3.txt75
-rw-r--r--src/test/crush/crush.cc660
-rwxr-xr-xsrc/test/crush/crush_weights.sh60
-rw-r--r--src/test/crypto.cc342
-rw-r--r--src/test/crypto_init.cc21
-rw-r--r--src/test/cxx11_client.cc6
-rw-r--r--src/test/daemon_config.cc380
-rw-r--r--src/test/debian-strech/Dockerfile.in31
l---------src/test/debian-strech/debian1
l---------src/test/debian-strech/install-deps.sh1
-rw-r--r--src/test/detect-build-env-vars.sh19
-rw-r--r--src/test/direct_messenger/CMakeLists.txt5
-rw-r--r--src/test/direct_messenger/DirectMessenger.cc252
-rw-r--r--src/test/direct_messenger/DirectMessenger.h98
-rw-r--r--src/test/direct_messenger/DispatchStrategy.h37
-rw-r--r--src/test/direct_messenger/FastStrategy.h35
-rw-r--r--src/test/direct_messenger/QueueStrategy.cc107
-rw-r--r--src/test/direct_messenger/QueueStrategy.h63
-rw-r--r--src/test/direct_messenger/test_direct_messenger.cc436
-rwxr-xr-xsrc/test/docker-test-helper.sh354
-rwxr-xr-xsrc/test/docker-test.sh19
-rw-r--r--src/test/dokan/CMakeLists.txt13
-rw-r--r--src/test/dokan/dokan.cc771
-rw-r--r--src/test/encoding.cc543
-rw-r--r--src/test/encoding/CMakeLists.txt3
-rwxr-xr-xsrc/test/encoding/check-generated.sh101
-rwxr-xr-xsrc/test/encoding/generate-corpus-objects.sh59
-rwxr-xr-xsrc/test/encoding/identity.sh32
-rwxr-xr-xsrc/test/encoding/import-generated.sh30
-rwxr-xr-xsrc/test/encoding/import.sh23
-rwxr-xr-xsrc/test/encoding/readable.sh241
-rw-r--r--src/test/erasure-code/CMakeLists.txt259
-rw-r--r--src/test/erasure-code/ErasureCodeExample.h195
-rw-r--r--src/test/erasure-code/ErasureCodePluginExample.cc45
-rw-r--r--src/test/erasure-code/ErasureCodePluginFailToInitialize.cc26
-rw-r--r--src/test/erasure-code/ErasureCodePluginFailToRegister.cc25
-rw-r--r--src/test/erasure-code/ErasureCodePluginHangs.cc27
-rw-r--r--src/test/erasure-code/ErasureCodePluginMissingEntryPoint.cc6
-rw-r--r--src/test/erasure-code/ErasureCodePluginMissingVersion.cc3
-rw-r--r--src/test/erasure-code/TestErasureCode.cc169
-rw-r--r--src/test/erasure-code/TestErasureCodeClay.cc596
-rw-r--r--src/test/erasure-code/TestErasureCodeExample.cc248
-rw-r--r--src/test/erasure-code/TestErasureCodeIsa.cc967
-rw-r--r--src/test/erasure-code/TestErasureCodeJerasure.cc370
-rw-r--r--src/test/erasure-code/TestErasureCodeLrc.cc925
-rw-r--r--src/test/erasure-code/TestErasureCodePlugin.cc131
-rw-r--r--src/test/erasure-code/TestErasureCodePluginClay.cc114
-rw-r--r--src/test/erasure-code/TestErasureCodePluginIsa.cc62
-rw-r--r--src/test/erasure-code/TestErasureCodePluginJerasure.cc71
-rw-r--r--src/test/erasure-code/TestErasureCodePluginLrc.cc50
-rw-r--r--src/test/erasure-code/TestErasureCodePluginShec.cc65
-rw-r--r--src/test/erasure-code/TestErasureCodeShec.cc2823
-rw-r--r--src/test/erasure-code/TestErasureCodeShec_all.cc332
-rw-r--r--src/test/erasure-code/TestErasureCodeShec_arguments.cc370
-rw-r--r--src/test/erasure-code/TestErasureCodeShec_thread.cc219
-rw-r--r--src/test/erasure-code/ceph_erasure_code_benchmark.cc354
-rw-r--r--src/test/erasure-code/ceph_erasure_code_benchmark.h62
-rw-r--r--src/test/erasure-code/ceph_erasure_code_non_regression.cc327
-rw-r--r--src/test/escape.cc128
-rw-r--r--src/test/exporter/CMakeLists.txt12
-rw-r--r--src/test/exporter/test_exporter.cc694
-rw-r--r--src/test/fedora-33/Dockerfile.in26
l---------src/test/fedora-33/ceph.spec.in1
l---------src/test/fedora-33/install-deps.sh1
l---------src/test/fedora-341
-rw-r--r--src/test/fio/CMakeLists.txt21
-rw-r--r--src/test/fio/README.md116
-rw-r--r--src/test/fio/ceph-bluestore.conf20
-rw-r--r--src/test/fio/ceph-bluestore.fio39
-rw-r--r--src/test/fio/ceph-filestore.conf26
-rw-r--r--src/test/fio/ceph-filestore.fio17
-rw-r--r--src/test/fio/ceph-librgw.fio28
-rw-r--r--src/test/fio/ceph-memstore.conf18
-rw-r--r--src/test/fio/ceph-memstore.fio17
-rw-r--r--src/test/fio/ceph-messenger.conf7
-rw-r--r--src/test/fio/ceph-messenger.fio22
-rw-r--r--src/test/fio/fio_ceph_messenger.cc700
-rw-r--r--src/test/fio/fio_ceph_objectstore.cc942
-rw-r--r--src/test/fio/fio_librgw.cc540
-rw-r--r--src/test/fio/ring_buffer.h102
-rw-r--r--src/test/fooclass.cc48
-rw-r--r--src/test/formatter.cc354
-rw-r--r--src/test/fs/CMakeLists.txt20
-rw-r--r--src/test/fs/mds_types.cc252
-rw-r--r--src/test/fs/test_ino_release_cb.cc84
-rw-r--r--src/test/fs/test_trim_caps.cc85
-rw-r--r--src/test/gather.cc103
-rw-r--r--src/test/gprof-helper.c119
-rw-r--r--src/test/heartbeat_map.cc44
-rw-r--r--src/test/immutable_object_cache/CMakeLists.txt37
-rw-r--r--src/test/immutable_object_cache/MockCacheDaemon.h45
-rw-r--r--src/test/immutable_object_cache/test_DomainSocket.cc177
-rw-r--r--src/test/immutable_object_cache/test_SimplePolicy.cc235
-rw-r--r--src/test/immutable_object_cache/test_common.h41
-rw-r--r--src/test/immutable_object_cache/test_main.cc29
-rw-r--r--src/test/immutable_object_cache/test_message.cc50
-rw-r--r--src/test/immutable_object_cache/test_multi_session.cc162
-rw-r--r--src/test/immutable_object_cache/test_object_store.cc99
-rw-r--r--src/test/journal/CMakeLists.txt32
-rw-r--r--src/test/journal/RadosTestFixture.cc135
-rw-r--r--src/test/journal/RadosTestFixture.h74
-rw-r--r--src/test/journal/mock/MockJournaler.cc16
-rw-r--r--src/test/journal/mock/MockJournaler.h313
-rw-r--r--src/test/journal/test_Entry.cc96
-rw-r--r--src/test/journal/test_FutureImpl.cc268
-rw-r--r--src/test/journal/test_JournalMetadata.cc210
-rw-r--r--src/test/journal/test_JournalPlayer.cc995
-rw-r--r--src/test/journal/test_JournalRecorder.cc174
-rw-r--r--src/test/journal/test_JournalTrimmer.cc197
-rw-r--r--src/test/journal/test_Journaler.cc198
-rw-r--r--src/test/journal/test_ObjectPlayer.cc281
-rw-r--r--src/test/journal/test_ObjectRecorder.cc464
-rw-r--r--src/test/journal/test_main.cc26
-rw-r--r--src/test/kv_store_bench.cc564
-rw-r--r--src/test/kv_store_bench.h192
-rw-r--r--src/test/lazy-omap-stats/CMakeLists.txt10
-rw-r--r--src/test/lazy-omap-stats/lazy_omap_stats_test.cc621
-rw-r--r--src/test/lazy-omap-stats/lazy_omap_stats_test.h88
-rw-r--r--src/test/lazy-omap-stats/main.cc21
-rw-r--r--src/test/libcephfs/CMakeLists.txt96
-rw-r--r--src/test/libcephfs/access.cc399
-rw-r--r--src/test/libcephfs/acl.cc367
-rw-r--r--src/test/libcephfs/caps.cc95
-rw-r--r--src/test/libcephfs/ceph_pthread_self.h31
-rw-r--r--src/test/libcephfs/deleg.cc401
-rw-r--r--src/test/libcephfs/flock.cc654
-rw-r--r--src/test/libcephfs/lazyio.cc356
-rw-r--r--src/test/libcephfs/main.cc50
-rw-r--r--src/test/libcephfs/monconfig.cc101
-rw-r--r--src/test/libcephfs/multiclient.cc180
-rw-r--r--src/test/libcephfs/newops.cc87
-rw-r--r--src/test/libcephfs/quota.cc167
-rw-r--r--src/test/libcephfs/readdir_r_cb.cc65
-rw-r--r--src/test/libcephfs/reclaim.cc163
-rw-r--r--src/test/libcephfs/recordlock.cc1105
-rw-r--r--src/test/libcephfs/snapdiff.cc1684
-rw-r--r--src/test/libcephfs/suidsgid.cc331
-rw-r--r--src/test/libcephfs/test.cc3775
-rw-r--r--src/test/libcephfs/vxattr.cc385
-rw-r--r--src/test/libcephfs_config.cc64
-rw-r--r--src/test/libcephsqlite/CMakeLists.txt15
-rw-r--r--src/test/libcephsqlite/main.cc1129
-rw-r--r--src/test/librados/CMakeLists.txt217
-rw-r--r--src/test/librados/TestCase.cc203
-rw-r--r--src/test/librados/TestCase.h124
-rw-r--r--src/test/librados/aio.cc1724
-rw-r--r--src/test/librados/aio_cxx.cc2467
-rw-r--r--src/test/librados/asio.cc369
-rw-r--r--src/test/librados/c_read_operations.cc895
-rw-r--r--src/test/librados/c_write_operations.cc279
-rw-r--r--src/test/librados/cls.cc36
-rw-r--r--src/test/librados/cls_remote_reads.cc55
-rw-r--r--src/test/librados/cmd.cc229
-rw-r--r--src/test/librados/cmd_cxx.cc92
-rw-r--r--src/test/librados/completion_speed.cc38
-rw-r--r--src/test/librados/crimson_utils.h15
-rw-r--r--src/test/librados/io.cc461
-rw-r--r--src/test/librados/io_cxx.cc986
-rw-r--r--src/test/librados/librados.cc13
-rw-r--r--src/test/librados/librados_config.cc98
-rw-r--r--src/test/librados/list.cc555
-rw-r--r--src/test/librados/list_cxx.cc782
-rw-r--r--src/test/librados/lock.cc237
-rw-r--r--src/test/librados/lock_cxx.cc203
-rw-r--r--src/test/librados/misc.cc358
-rw-r--r--src/test/librados/misc_cxx.cc923
-rw-r--r--src/test/librados/op_speed.cc24
-rw-r--r--src/test/librados/pool.cc186
-rw-r--r--src/test/librados/service.cc209
-rw-r--r--src/test/librados/service_cxx.cc105
-rw-r--r--src/test/librados/snapshots.cc356
-rw-r--r--src/test/librados/snapshots_cxx.cc790
-rw-r--r--src/test/librados/snapshots_stats.cc332
-rw-r--r--src/test/librados/snapshots_stats_cxx.cc324
-rw-r--r--src/test/librados/stat.cc153
-rw-r--r--src/test/librados/stat_cxx.cc168
-rw-r--r--src/test/librados/test.cc198
-rw-r--r--src/test/librados/test.h32
-rw-r--r--src/test/librados/test_common.cc167
-rw-r--r--src/test/librados/test_common.h9
-rw-r--r--src/test/librados/test_cxx.cc203
-rw-r--r--src/test/librados/test_cxx.h19
-rw-r--r--src/test/librados/test_shared.cc44
-rw-r--r--src/test/librados/test_shared.h58
-rw-r--r--src/test/librados/testcase_cxx.cc407
-rw-r--r--src/test/librados/testcase_cxx.h130
-rw-r--r--src/test/librados/tier_cxx.cc9304
-rw-r--r--src/test/librados/watch_notify.cc657
-rw-r--r--src/test/librados/watch_notify_cxx.cc416
-rw-r--r--src/test/librados_test_stub/CMakeLists.txt12
-rw-r--r--src/test/librados_test_stub/LibradosTestStub.cc1568
-rw-r--r--src/test/librados_test_stub/LibradosTestStub.h42
-rw-r--r--src/test/librados_test_stub/MockTestMemCluster.h36
-rw-r--r--src/test/librados_test_stub/MockTestMemIoCtxImpl.h252
-rw-r--r--src/test/librados_test_stub/MockTestMemRadosClient.h103
-rw-r--r--src/test/librados_test_stub/NeoradosTestStub.cc601
-rw-r--r--src/test/librados_test_stub/TestClassHandler.cc159
-rw-r--r--src/test/librados_test_stub/TestClassHandler.h79
-rw-r--r--src/test/librados_test_stub/TestCluster.h64
-rw-r--r--src/test/librados_test_stub/TestIoCtxImpl.cc394
-rw-r--r--src/test/librados_test_stub/TestIoCtxImpl.h221
-rw-r--r--src/test/librados_test_stub/TestMemCluster.cc203
-rw-r--r--src/test/librados_test_stub/TestMemCluster.h124
-rw-r--r--src/test/librados_test_stub/TestMemIoCtxImpl.cc924
-rw-r--r--src/test/librados_test_stub/TestMemIoCtxImpl.h104
-rw-r--r--src/test/librados_test_stub/TestMemRadosClient.cc118
-rw-r--r--src/test/librados_test_stub/TestMemRadosClient.h88
-rw-r--r--src/test/librados_test_stub/TestRadosClient.cc311
-rw-r--r--src/test/librados_test_stub/TestRadosClient.h162
-rw-r--r--src/test/librados_test_stub/TestWatchNotify.cc459
-rw-r--r--src/test/librados_test_stub/TestWatchNotify.h148
-rw-r--r--src/test/libradosstriper/CMakeLists.txt34
-rw-r--r--src/test/libradosstriper/TestCase.cc80
-rw-r--r--src/test/libradosstriper/TestCase.h82
-rw-r--r--src/test/libradosstriper/aio.cc581
-rw-r--r--src/test/libradosstriper/io.cc430
-rw-r--r--src/test/libradosstriper/striping.cc329
-rw-r--r--src/test/librbd/CMakeLists.txt232
-rw-r--r--src/test/librbd/cache/pwl/test_WriteLogMap.cc338
-rw-r--r--src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc743
-rw-r--r--src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc761
-rw-r--r--src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc427
-rw-r--r--src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc703
-rw-r--r--src/test/librbd/crypto/luks/test_mock_FlattenRequest.cc266
-rw-r--r--src/test/librbd/crypto/luks/test_mock_FormatRequest.cc230
-rw-r--r--src/test/librbd/crypto/luks/test_mock_LoadRequest.cc333
-rw-r--r--src/test/librbd/crypto/openssl/test_DataCryptor.cc118
-rw-r--r--src/test/librbd/crypto/test_mock_BlockCrypto.cc156
-rw-r--r--src/test/librbd/crypto/test_mock_CryptoContextPool.cc54
-rw-r--r--src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc800
-rw-r--r--src/test/librbd/crypto/test_mock_FormatRequest.cc231
-rw-r--r--src/test/librbd/crypto/test_mock_LoadRequest.cc382
-rw-r--r--src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc174
-rw-r--r--src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc807
-rw-r--r--src/test/librbd/deep_copy/test_mock_MetadataCopyRequest.cc220
-rw-r--r--src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc1108
-rw-r--r--src/test/librbd/deep_copy/test_mock_SetHeadRequest.cc296
-rw-r--r--src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc922
-rw-r--r--src/test/librbd/deep_copy/test_mock_SnapshotCreateRequest.cc262
-rw-r--r--src/test/librbd/exclusive_lock/test_mock_PostAcquireRequest.cc582
-rw-r--r--src/test/librbd/exclusive_lock/test_mock_PreAcquireRequest.cc92
-rw-r--r--src/test/librbd/exclusive_lock/test_mock_PreReleaseRequest.cc388
-rw-r--r--src/test/librbd/fsx.cc3472
-rw-r--r--src/test/librbd/image/test_mock_AttachChildRequest.cc275
-rw-r--r--src/test/librbd/image/test_mock_AttachParentRequest.cc155
-rw-r--r--src/test/librbd/image/test_mock_CloneRequest.cc960
-rw-r--r--src/test/librbd/image/test_mock_DetachChildRequest.cc454
-rw-r--r--src/test/librbd/image/test_mock_DetachParentRequest.cc135
-rw-r--r--src/test/librbd/image/test_mock_ListWatchersRequest.cc212
-rw-r--r--src/test/librbd/image/test_mock_PreRemoveRequest.cc465
-rw-r--r--src/test/librbd/image/test_mock_RefreshRequest.cc1757
-rw-r--r--src/test/librbd/image/test_mock_RemoveRequest.cc480
-rw-r--r--src/test/librbd/image/test_mock_ValidatePoolRequest.cc223
-rw-r--r--src/test/librbd/io/test_mock_CopyupRequest.cc1340
-rw-r--r--src/test/librbd/io/test_mock_ImageRequest.cc738
-rw-r--r--src/test/librbd/io/test_mock_ObjectRequest.cc1968
-rw-r--r--src/test/librbd/io/test_mock_QosImageDispatch.cc89
-rw-r--r--src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc823
-rw-r--r--src/test/librbd/journal/test_Entries.cc228
-rw-r--r--src/test/librbd/journal/test_Replay.cc886
-rw-r--r--src/test/librbd/journal/test_mock_OpenRequest.cc193
-rw-r--r--src/test/librbd/journal/test_mock_PromoteRequest.cc356
-rw-r--r--src/test/librbd/journal/test_mock_Replay.cc2041
-rw-r--r--src/test/librbd/journal/test_mock_ResetRequest.cc278
-rw-r--r--src/test/librbd/managed_lock/test_mock_AcquireRequest.cc271
-rw-r--r--src/test/librbd/managed_lock/test_mock_BreakRequest.cc484
-rw-r--r--src/test/librbd/managed_lock/test_mock_GetLockerRequest.cc309
-rw-r--r--src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc123
-rw-r--r--src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc91
-rw-r--r--src/test/librbd/migration/test_mock_FileStream.cc213
-rw-r--r--src/test/librbd/migration/test_mock_HttpClient.cc890
-rw-r--r--src/test/librbd/migration/test_mock_HttpStream.cc194
-rw-r--r--src/test/librbd/migration/test_mock_QCOWFormat.cc1259
-rw-r--r--src/test/librbd/migration/test_mock_RawFormat.cc523
-rw-r--r--src/test/librbd/migration/test_mock_RawSnapshot.cc255
-rw-r--r--src/test/librbd/migration/test_mock_S3Stream.cc238
-rw-r--r--src/test/librbd/migration/test_mock_Utils.cc47
-rw-r--r--src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc388
-rw-r--r--src/test/librbd/mirror/snapshot/test_mock_CreatePrimaryRequest.cc807
-rw-r--r--src/test/librbd/mirror/snapshot/test_mock_ImageMeta.cc159
-rw-r--r--src/test/librbd/mirror/snapshot/test_mock_PromoteRequest.cc389
-rw-r--r--src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc501
-rw-r--r--src/test/librbd/mirror/snapshot/test_mock_Utils.cc177
-rw-r--r--src/test/librbd/mirror/test_mock_DisableRequest.cc694
-rw-r--r--src/test/librbd/mock/MockContextWQ.h19
-rw-r--r--src/test/librbd/mock/MockExclusiveLock.h50
-rw-r--r--src/test/librbd/mock/MockImageCtx.cc149
-rw-r--r--src/test/librbd/mock/MockImageCtx.h261
-rw-r--r--src/test/librbd/mock/MockImageState.h39
-rw-r--r--src/test/librbd/mock/MockImageWatcher.h34
-rw-r--r--src/test/librbd/mock/MockJournal.cc10
-rw-r--r--src/test/librbd/mock/MockJournal.h96
-rw-r--r--src/test/librbd/mock/MockJournalPolicy.h22
-rw-r--r--src/test/librbd/mock/MockObjectMap.h70
-rw-r--r--src/test/librbd/mock/MockOperations.h72
-rw-r--r--src/test/librbd/mock/MockPluginRegistry.h21
-rw-r--r--src/test/librbd/mock/MockReadahead.h21
-rw-r--r--src/test/librbd/mock/MockSafeTimer.h20
-rw-r--r--src/test/librbd/mock/cache/MockImageCache.h58
-rw-r--r--src/test/librbd/mock/crypto/MockCryptoInterface.h36
-rw-r--r--src/test/librbd/mock/crypto/MockDataCryptor.h43
-rw-r--r--src/test/librbd/mock/crypto/MockEncryptionFormat.h26
-rw-r--r--src/test/librbd/mock/exclusive_lock/MockPolicy.h23
-rw-r--r--src/test/librbd/mock/io/MockImageDispatch.h98
-rw-r--r--src/test/librbd/mock/io/MockImageDispatcher.h50
-rw-r--r--src/test/librbd/mock/io/MockObjectDispatch.h137
-rw-r--r--src/test/librbd/mock/io/MockObjectDispatcher.h44
-rw-r--r--src/test/librbd/mock/io/MockQosImageDispatch.h24
-rw-r--r--src/test/librbd/mock/migration/MockSnapshotInterface.h44
-rw-r--r--src/test/librbd/mock/migration/MockStreamInterface.h29
-rw-r--r--src/test/librbd/object_map/mock/MockInvalidateRequest.h41
-rw-r--r--src/test/librbd/object_map/test_mock_DiffRequest.cc493
-rw-r--r--src/test/librbd/object_map/test_mock_InvalidateRequest.cc158
-rw-r--r--src/test/librbd/object_map/test_mock_LockRequest.cc221
-rw-r--r--src/test/librbd/object_map/test_mock_RefreshRequest.cc465
-rw-r--r--src/test/librbd/object_map/test_mock_ResizeRequest.cc154
-rw-r--r--src/test/librbd/object_map/test_mock_SnapshotCreateRequest.cc232
-rw-r--r--src/test/librbd/object_map/test_mock_SnapshotRemoveRequest.cc345
-rw-r--r--src/test/librbd/object_map/test_mock_SnapshotRollbackRequest.cc148
-rw-r--r--src/test/librbd/object_map/test_mock_UnlockRequest.cc69
-rw-r--r--src/test/librbd/object_map/test_mock_UpdateRequest.cc291
-rw-r--r--src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc538
-rw-r--r--src/test/librbd/operation/test_mock_EnableFeaturesRequest.cc644
-rw-r--r--src/test/librbd/operation/test_mock_Request.cc175
-rw-r--r--src/test/librbd/operation/test_mock_ResizeRequest.cc435
-rw-r--r--src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc495
-rw-r--r--src/test/librbd/operation/test_mock_SnapshotProtectRequest.cc193
-rw-r--r--src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc971
-rw-r--r--src/test/librbd/operation/test_mock_SnapshotRollbackRequest.cc367
-rw-r--r--src/test/librbd/operation/test_mock_SnapshotUnprotectRequest.cc277
-rw-r--r--src/test/librbd/operation/test_mock_TrimRequest.cc493
-rw-r--r--src/test/librbd/rbdrw.py30
-rw-r--r--src/test/librbd/test_BlockGuard.cc98
-rw-r--r--src/test/librbd/test_DeepCopy.cc763
-rw-r--r--src/test/librbd/test_Groups.cc445
-rw-r--r--src/test/librbd/test_ImageWatcher.cc940
-rw-r--r--src/test/librbd/test_Migration.cc1359
-rw-r--r--src/test/librbd/test_MirroringWatcher.cc101
-rw-r--r--src/test/librbd/test_ObjectMap.cc240
-rw-r--r--src/test/librbd/test_Operations.cc26
-rw-r--r--src/test/librbd/test_Trash.cc108
-rw-r--r--src/test/librbd/test_fixture.cc165
-rw-r--r--src/test/librbd/test_fixture.h60
-rw-r--r--src/test/librbd/test_internal.cc1868
-rw-r--r--src/test/librbd/test_librbd.cc12647
-rw-r--r--src/test/librbd/test_main.cc71
-rw-r--r--src/test/librbd/test_mirroring.cc1543
-rw-r--r--src/test/librbd/test_mock_ConfigWatcher.cc100
-rw-r--r--src/test/librbd/test_mock_DeepCopyRequest.cc466
-rw-r--r--src/test/librbd/test_mock_ExclusiveLock.cc831
-rw-r--r--src/test/librbd/test_mock_Journal.cc1632
-rw-r--r--src/test/librbd/test_mock_ManagedLock.cc723
-rw-r--r--src/test/librbd/test_mock_ObjectMap.cc285
-rw-r--r--src/test/librbd/test_mock_TrashWatcher.cc95
-rw-r--r--src/test/librbd/test_mock_Watcher.cc405
-rw-r--r--src/test/librbd/test_mock_fixture.cc134
-rw-r--r--src/test/librbd/test_mock_fixture.h89
-rwxr-xr-xsrc/test/librbd/test_notify.py183
-rw-r--r--src/test/librbd/test_support.cc137
-rw-r--r--src/test/librbd/test_support.h39
-rw-r--r--src/test/librbd/trash/test_mock_MoveRequest.cc230
-rw-r--r--src/test/librbd/trash/test_mock_RemoveRequest.cc230
-rw-r--r--src/test/librbd/watcher/test_mock_RewatchRequest.cc227
-rw-r--r--src/test/librgw_file.cc291
-rw-r--r--src/test/librgw_file_aw.cc436
-rw-r--r--src/test/librgw_file_cd.cc200
-rw-r--r--src/test/librgw_file_gp.cc519
-rw-r--r--src/test/librgw_file_marker.cc495
-rw-r--r--src/test/librgw_file_nfsns.cc1214
-rw-r--r--src/test/librgw_file_xattr.cc433
-rw-r--r--src/test/mds/CMakeLists.txt16
-rw-r--r--src/test/mds/TestMDSAuthCaps.cc312
-rw-r--r--src/test/mds/TestSessionFilter.cc142
-rwxr-xr-xsrc/test/memuse/test_pool_memuse.sh19
-rwxr-xr-xsrc/test/memuse/test_pool_memuse_tcmalloc.sh25
-rwxr-xr-xsrc/test/memuse/test_written_pool_memuse.sh12
-rwxr-xr-xsrc/test/memuse/test_written_pool_memuse_tcmalloc.sh54
-rw-r--r--src/test/mgr/CMakeLists.txt21
-rwxr-xr-xsrc/test/mgr/mgr-dashboard-smoke.sh81
-rw-r--r--src/test/mgr/test_mgrcap.cc300
-rw-r--r--src/test/mgr/test_ttlcache.cc70
-rw-r--r--src/test/mime.cc150
-rw-r--r--src/test/mon/CMakeLists.txt83
-rw-r--r--src/test/mon/MonMap.cc241
-rw-r--r--src/test/mon/PGMap.cc169
-rwxr-xr-xsrc/test/mon/bench_auth.py105
-rw-r--r--src/test/mon/moncap.cc378
-rw-r--r--src/test/mon/test-mon-msg.cc339
-rw-r--r--src/test/mon/test_election.cc1003
-rw-r--r--src/test/mon/test_log_rss_usage.cc102
-rw-r--r--src/test/mon/test_mon_memory_target.cc79
-rw-r--r--src/test/mon/test_mon_rss_usage.cc72
-rw-r--r--src/test/mon/test_mon_types.cc140
-rw-r--r--src/test/mon/test_mon_workloadgen.cc1074
-rw-r--r--src/test/msgr/CMakeLists.txt58
-rw-r--r--src/test/msgr/perf_msgr_client.cc219
-rw-r--r--src/test/msgr/perf_msgr_server.cc176
-rw-r--r--src/test/msgr/test_async_driver.cc354
-rw-r--r--src/test/msgr/test_async_networkstack.cc1072
-rw-r--r--src/test/msgr/test_comp_registry.cc98
-rw-r--r--src/test/msgr/test_frames_v2.cc483
-rw-r--r--src/test/msgr/test_msgr.cc2424
-rw-r--r--src/test/msgr/test_userspace_event.cc174
-rw-r--r--src/test/multi_stress_watch.cc169
-rw-r--r--src/test/neorados/CMakeLists.txt28
-rw-r--r--src/test/neorados/common_tests.cc34
-rw-r--r--src/test/neorados/common_tests.h41
-rw-r--r--src/test/neorados/completions.cc20
-rw-r--r--src/test/neorados/list_pool.cc166
-rw-r--r--src/test/neorados/op_speed.cc34
-rw-r--r--src/test/neorados/start_stop.cc174
-rw-r--r--src/test/neorados/test_neorados.cc47
-rwxr-xr-xsrc/test/objectstore/Allocator_aging_fragmentation.cc463
-rw-r--r--src/test/objectstore/Allocator_bench.cc368
-rw-r--r--src/test/objectstore/Allocator_test.cc566
-rw-r--r--src/test/objectstore/CMakeLists.txt140
-rw-r--r--src/test/objectstore/ObjectStoreTransactionBenchmark.cc266
-rw-r--r--src/test/objectstore/TestObjectStoreState.cc299
-rw-r--r--src/test/objectstore/TestObjectStoreState.h158
-rw-r--r--src/test/objectstore/TestRocksdbOptionParse.cc78
-rw-r--r--src/test/objectstore/allocator_replay_test.cc694
-rw-r--r--src/test/objectstore/fastbmap_allocator_test.cc1145
-rwxr-xr-xsrc/test/objectstore/hybrid_allocator_test.cc231
-rwxr-xr-xsrc/test/objectstore/run_seed_to.sh293
-rwxr-xr-xsrc/test/objectstore/run_seed_to_range.sh24
-rw-r--r--src/test/objectstore/run_smr_bluestore_test.sh48
-rwxr-xr-xsrc/test/objectstore/run_test_deferred.sh52
-rw-r--r--src/test/objectstore/store_test.cc10932
-rw-r--r--src/test/objectstore/store_test_fixture.cc135
-rw-r--r--src/test/objectstore/store_test_fixture.h52
-rwxr-xr-xsrc/test/objectstore/test_bdev.cc111
-rw-r--r--src/test/objectstore/test_bluefs.cc1422
-rw-r--r--src/test/objectstore/test_bluestore_types.cc2346
-rw-r--r--src/test/objectstore/test_deferred.cc146
-rw-r--r--src/test/objectstore/test_kv.cc1304
-rw-r--r--src/test/objectstore/test_memstore_clone.cc202
-rw-r--r--src/test/objectstore/test_transaction.cc215
-rw-r--r--src/test/objectstore_bench.cc329
-rw-r--r--src/test/old/test_disk_bw.cc62
-rw-r--r--src/test/old/test_setlayout.c24
-rw-r--r--src/test/old/testfilepath.cc22
-rw-r--r--src/test/omap_bench.cc431
-rw-r--r--src/test/omap_bench.h206
-rw-r--r--src/test/on_exit.cc115
-rw-r--r--src/test/opensuse-13.2/Dockerfile.in30
l---------src/test/opensuse-13.2/ceph.spec.in1
l---------src/test/opensuse-13.2/install-deps.sh1
-rw-r--r--src/test/osd/CMakeLists.txt141
-rw-r--r--src/test/osd/Object.cc200
-rw-r--r--src/test/osd/Object.h540
-rw-r--r--src/test/osd/RadosModel.cc36
-rw-r--r--src/test/osd/RadosModel.h3520
-rw-r--r--src/test/osd/TestECBackend.cc62
-rw-r--r--src/test/osd/TestMClockScheduler.cc256
-rw-r--r--src/test/osd/TestOSDMap.cc2719
-rw-r--r--src/test/osd/TestOSDScrub.cc203
-rw-r--r--src/test/osd/TestOpStat.cc58
-rw-r--r--src/test/osd/TestOpStat.h53
-rw-r--r--src/test/osd/TestPGLog.cc3249
-rw-r--r--src/test/osd/TestRados.cc729
-rw-r--r--src/test/osd/ceph_test_osd_stale_read.cc177
-rw-r--r--src/test/osd/hitset.cc197
-rw-r--r--src/test/osd/osdcap.cc1398
-rwxr-xr-xsrc/test/osd/safe-to-destroy.sh99
-rw-r--r--src/test/osd/scrubber_generators.cc168
-rw-r--r--src/test/osd/scrubber_generators.h266
-rw-r--r--src/test/osd/scrubber_test_datasets.cc120
-rw-r--r--src/test/osd/scrubber_test_datasets.h21
-rw-r--r--src/test/osd/test_ec_transaction.cc124
-rw-r--r--src/test/osd/test_extent_cache.cc282
-rw-r--r--src/test/osd/test_pg_transaction.cc131
-rw-r--r--src/test/osd/test_scrub_sched.cc402
-rw-r--r--src/test/osd/test_scrubber_be.cc669
-rw-r--r--src/test/osd/types.cc2204
-rw-r--r--src/test/osdc/CMakeLists.txt13
-rw-r--r--src/test/osdc/FakeWriteback.cc93
-rw-r--r--src/test/osdc/FakeWriteback.h47
-rw-r--r--src/test/osdc/MemWriteback.cc166
-rw-r--r--src/test/osdc/MemWriteback.h52
-rw-r--r--src/test/osdc/object_cacher_stress.cc425
-rw-r--r--src/test/perf_counters.cc692
-rw-r--r--src/test/perf_helper.cc52
-rw-r--r--src/test/perf_helper.h30
-rw-r--r--src/test/perf_local.cc1067
-rw-r--r--src/test/pybind/CMakeLists.txt4
-rw-r--r--src/test/pybind/assertions.py26
-rw-r--r--src/test/pybind/pytest.ini9
-rwxr-xr-xsrc/test/pybind/test_ceph_argparse.py1334
-rwxr-xr-xsrc/test/pybind/test_ceph_daemon.py52
-rw-r--r--src/test/pybind/test_cephfs.py909
-rw-r--r--src/test/pybind/test_rados.py1543
-rw-r--r--src/test/pybind/test_rbd.py2810
-rw-r--r--src/test/pybind/test_rgwfs.py137
-rwxr-xr-xsrc/test/rbd-ggate.sh25
-rw-r--r--src/test/rbd_mirror/CMakeLists.txt106
-rw-r--r--src/test/rbd_mirror/image_deleter/test_mock_SnapshotPurgeRequest.cc430
-rw-r--r--src/test/rbd_mirror/image_deleter/test_mock_TrashMoveRequest.cc901
-rw-r--r--src/test/rbd_mirror/image_deleter/test_mock_TrashRemoveRequest.cc453
-rw-r--r--src/test/rbd_mirror/image_deleter/test_mock_TrashWatcher.cc519
-rw-r--r--src/test/rbd_mirror/image_map/test_Policy.cc377
-rw-r--r--src/test/rbd_mirror/image_replayer/journal/test_mock_CreateLocalImageRequest.cc341
-rw-r--r--src/test/rbd_mirror/image_replayer/journal/test_mock_EventPreprocessor.cc266
-rw-r--r--src/test/rbd_mirror/image_replayer/journal/test_mock_PrepareReplayRequest.cc751
-rw-r--r--src/test/rbd_mirror/image_replayer/journal/test_mock_Replayer.cc2162
-rw-r--r--src/test/rbd_mirror/image_replayer/snapshot/test_mock_ApplyImageStateRequest.cc641
-rw-r--r--src/test/rbd_mirror/image_replayer/snapshot/test_mock_CreateLocalImageRequest.cc356
-rw-r--r--src/test/rbd_mirror/image_replayer/snapshot/test_mock_Replayer.cc3327
-rw-r--r--src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc1248
-rw-r--r--src/test/rbd_mirror/image_replayer/test_mock_CreateImageRequest.cc614
-rw-r--r--src/test/rbd_mirror/image_replayer/test_mock_GetMirrorImageIdRequest.cc107
-rw-r--r--src/test/rbd_mirror/image_replayer/test_mock_PrepareLocalImageRequest.cc505
-rw-r--r--src/test/rbd_mirror/image_replayer/test_mock_PrepareRemoteImageRequest.cc811
-rw-r--r--src/test/rbd_mirror/image_sync/test_mock_SyncPointCreateRequest.cc195
-rw-r--r--src/test/rbd_mirror/image_sync/test_mock_SyncPointPruneRequest.cc347
-rw-r--r--src/test/rbd_mirror/mock/MockBaseRequest.h26
-rw-r--r--src/test/rbd_mirror/mock/MockContextWQ.h18
-rw-r--r--src/test/rbd_mirror/mock/MockSafeTimer.h16
-rw-r--r--src/test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h29
-rw-r--r--src/test/rbd_mirror/pool_watcher/test_mock_RefreshImagesRequest.cc117
-rw-r--r--src/test/rbd_mirror/random_write.cc210
-rw-r--r--src/test/rbd_mirror/test_ClusterWatcher.cc265
-rw-r--r--src/test/rbd_mirror/test_ImageDeleter.cc313
-rw-r--r--src/test/rbd_mirror/test_ImageReplayer.cc1664
-rw-r--r--src/test/rbd_mirror/test_ImageSync.cc374
-rw-r--r--src/test/rbd_mirror/test_InstanceWatcher.cc132
-rw-r--r--src/test/rbd_mirror/test_Instances.cc164
-rw-r--r--src/test/rbd_mirror/test_LeaderWatcher.cc318
-rw-r--r--src/test/rbd_mirror/test_PoolWatcher.cc256
-rw-r--r--src/test/rbd_mirror/test_fixture.cc161
-rw-r--r--src/test/rbd_mirror/test_fixture.h65
-rw-r--r--src/test/rbd_mirror/test_main.cc53
-rw-r--r--src/test/rbd_mirror/test_mock_ImageMap.cc1587
-rw-r--r--src/test/rbd_mirror/test_mock_ImageReplayer.cc989
-rw-r--r--src/test/rbd_mirror/test_mock_ImageSync.cc468
-rw-r--r--src/test/rbd_mirror/test_mock_InstanceReplayer.cc382
-rw-r--r--src/test/rbd_mirror/test_mock_InstanceWatcher.cc987
-rw-r--r--src/test/rbd_mirror/test_mock_LeaderWatcher.cc614
-rw-r--r--src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc706
-rw-r--r--src/test/rbd_mirror/test_mock_NamespaceReplayer.cc611
-rw-r--r--src/test/rbd_mirror/test_mock_PoolReplayer.cc934
-rw-r--r--src/test/rbd_mirror/test_mock_PoolWatcher.cc730
-rw-r--r--src/test/rbd_mirror/test_mock_Throttler.cc253
-rw-r--r--src/test/rbd_mirror/test_mock_fixture.cc64
-rw-r--r--src/test/rbd_mirror/test_mock_fixture.h72
-rw-r--r--src/test/rgw/CMakeLists.txt282
-rw-r--r--src/test/rgw/amqp_mock.cc391
-rw-r--r--src/test/rgw/amqp_mock.h19
-rw-r--r--src/test/rgw/amqp_url.c221
-rw-r--r--src/test/rgw/bench_rgw_ratelimit.cc247
-rw-r--r--src/test/rgw/bench_rgw_ratelimit_gc.cc52
-rw-r--r--src/test/rgw/bucket_notification/README.rst96
-rw-r--r--src/test/rgw/bucket_notification/__init__.py48
-rw-r--r--src/test/rgw/bucket_notification/api.py234
-rw-r--r--src/test/rgw/bucket_notification/bntests.conf.SAMPLE10
-rwxr-xr-xsrc/test/rgw/bucket_notification/bootstrap45
-rwxr-xr-xsrc/test/rgw/bucket_notification/kafka-security.sh49
-rw-r--r--src/test/rgw/bucket_notification/requirements.txt8
-rw-r--r--src/test/rgw/bucket_notification/setup.py19
-rw-r--r--src/test/rgw/bucket_notification/test_bn.py4128
-rw-r--r--src/test/rgw/kafka_stub.cc68
-rw-r--r--src/test/rgw/rgw_cr_test.cc343
-rw-r--r--src/test/rgw/rgw_multi/__init__.py0
-rw-r--r--src/test/rgw/rgw_multi/conn.py41
-rw-r--r--src/test/rgw/rgw_multi/multisite.py407
-rw-r--r--src/test/rgw/rgw_multi/tests.py2861
-rw-r--r--src/test/rgw/rgw_multi/tests_az.py597
-rw-r--r--src/test/rgw/rgw_multi/tests_es.py276
-rw-r--r--src/test/rgw/rgw_multi/tools.py97
-rw-r--r--src/test/rgw/rgw_multi/zone_az.py42
-rw-r--r--src/test/rgw/rgw_multi/zone_cloud.py326
-rw-r--r--src/test/rgw/rgw_multi/zone_es.py256
-rw-r--r--src/test/rgw/rgw_multi/zone_rados.py134
-rwxr-xr-xsrc/test/rgw/test-ceph-diff-sorted.sh108
-rwxr-xr-xsrc/test/rgw/test-rgw-call.sh9
-rw-r--r--src/test/rgw/test-rgw-common.sh195
-rwxr-xr-xsrc/test/rgw/test-rgw-meta-sync.sh65
-rwxr-xr-xsrc/test/rgw/test-rgw-multisite.sh83
-rw-r--r--src/test/rgw/test_cls_fifo_legacy.cc1184
-rw-r--r--src/test/rgw/test_http_manager.cc148
-rw-r--r--src/test/rgw/test_log_backing.cc365
-rw-r--r--src/test/rgw/test_multen.py400
-rw-r--r--src/test/rgw/test_multi.md56
-rw-r--r--src/test/rgw/test_multi.py410
-rw-r--r--src/test/rgw/test_rgw_amqp.cc529
-rw-r--r--src/test/rgw/test_rgw_arn.cc107
-rw-r--r--src/test/rgw/test_rgw_bencode.cc65
-rw-r--r--src/test/rgw/test_rgw_bucket_sync_cache.cc189
-rw-r--r--src/test/rgw/test_rgw_common.cc91
-rw-r--r--src/test/rgw/test_rgw_common.h506
-rw-r--r--src/test/rgw/test_rgw_compression.cc186
-rw-r--r--src/test/rgw/test_rgw_crypto.cc816
-rw-r--r--src/test/rgw/test_rgw_dmclock_scheduler.cc428
-rw-r--r--src/test/rgw/test_rgw_gc_log.cc144
-rw-r--r--src/test/rgw/test_rgw_iam_policy.cc1321
-rw-r--r--src/test/rgw/test_rgw_kms.cc294
-rw-r--r--src/test/rgw/test_rgw_lc.cc109
-rw-r--r--src/test/rgw/test_rgw_lua.cc1338
-rw-r--r--src/test/rgw/test_rgw_manifest.cc397
-rw-r--r--src/test/rgw/test_rgw_obj.cc272
-rw-r--r--src/test/rgw/test_rgw_period_history.cc336
-rw-r--r--src/test/rgw/test_rgw_putobj.cc196
-rw-r--r--src/test/rgw/test_rgw_ratelimit.cc376
-rw-r--r--src/test/rgw/test_rgw_reshard.cc68
-rw-r--r--src/test/rgw/test_rgw_reshard_wait.cc164
-rw-r--r--src/test/rgw/test_rgw_string.cc76
-rw-r--r--src/test/rgw/test_rgw_throttle.cc221
-rw-r--r--src/test/rgw/test_rgw_url.cc111
-rw-r--r--src/test/rgw/test_rgw_xml.cc463
-rwxr-xr-xsrc/test/run-cli-tests64
-rwxr-xr-xsrc/test/run-cli-tests-maybe-unset-ccache20
-rwxr-xr-xsrc/test/run-rbd-tests66
-rwxr-xr-xsrc/test/run-rbd-unit-tests.sh25
-rwxr-xr-xsrc/test/run-rbd-valgrind-unit-tests.sh12
-rw-r--r--src/test/run_cmd.cc26
-rw-r--r--src/test/signals.cc158
-rw-r--r--src/test/simple_spin.cc135
-rwxr-xr-xsrc/test/smoke.sh64
-rw-r--r--src/test/strtol.cc649
-rw-r--r--src/test/system/CMakeLists.txt50
-rw-r--r--src/test/system/cross_process_sem.cc122
-rw-r--r--src/test/system/cross_process_sem.h40
-rw-r--r--src/test/system/rados_delete_pools_parallel.cc112
-rw-r--r--src/test/system/rados_list_parallel.cc348
-rw-r--r--src/test/system/rados_open_pools_parallel.cc143
-rw-r--r--src/test/system/rados_watch_notify.cc195
-rwxr-xr-xsrc/test/system/rerun.sh20
-rw-r--r--src/test/system/st_rados_create_pool.cc132
-rw-r--r--src/test/system/st_rados_create_pool.h53
-rw-r--r--src/test/system/st_rados_delete_objs.cc71
-rw-r--r--src/test/system/st_rados_delete_objs.h48
-rw-r--r--src/test/system/st_rados_delete_pool.cc59
-rw-r--r--src/test/system/st_rados_delete_pool.h43
-rw-r--r--src/test/system/st_rados_list_objects.cc107
-rw-r--r--src/test/system/st_rados_list_objects.h53
-rw-r--r--src/test/system/st_rados_notify.h52
-rw-r--r--src/test/system/st_rados_watch.h56
-rw-r--r--src/test/system/systest_runnable.cc233
-rw-r--r--src/test/system/systest_runnable.h94
-rw-r--r--src/test/system/systest_settings.cc71
-rw-r--r--src/test/system/systest_settings.h36
-rw-r--r--src/test/test_addrs.cc334
-rw-r--r--src/test/test_admin_socket_output.cc129
-rw-r--r--src/test/test_any.cc812
-rw-r--r--src/test/test_arch.cc93
-rw-r--r--src/test/test_auth.cc247
-rwxr-xr-xsrc/test/test_backfill.sh11
-rw-r--r--src/test/test_c2c.cc88
-rw-r--r--src/test/test_c_headers.c30
-rw-r--r--src/test/test_cfuse_cache_invalidate.cc54
-rwxr-xr-xsrc/test/test_common.sh174
-rw-r--r--src/test/test_cors.cc901
-rwxr-xr-xsrc/test/test_crush_bucket.sh52
-rw-r--r--src/test/test_csyn.sh36
-rw-r--r--src/test/test_denc.cc743
-rw-r--r--src/test/test_features.cc47
-rw-r--r--src/test/test_get_blkdev_props.cc89
-rw-r--r--src/test/test_intarith.cc40
-rw-r--r--src/test/test_ipaddr.cc997
-rwxr-xr-xsrc/test/test_lost.sh257
-rw-r--r--src/test/test_mempool.cc460
-rwxr-xr-xsrc/test/test_missing_unfound.sh29
-rw-r--r--src/test/test_mutate.cc112
-rwxr-xr-xsrc/test/test_objectstore_memstore.sh6
-rw-r--r--src/test/test_pageset.cc283
-rwxr-xr-xsrc/test/test_pidfile.sh90
-rwxr-xr-xsrc/test/test_pools.sh50
l---------src/test/test_rados_tool.sh1
-rw-r--r--src/test/test_random_string.cc120
-rw-r--r--src/test/test_rbd_replay.cc136
-rw-r--r--src/test/test_rewrite_latency.cc47
-rw-r--r--src/test/test_rgw_admin_log.cc1590
-rw-r--r--src/test/test_rgw_admin_meta.cc924
-rw-r--r--src/test/test_rgw_ldap.cc116
-rw-r--r--src/test/test_rgw_token.cc124
-rwxr-xr-xsrc/test/test_rw.sh53
-rw-r--r--src/test/test_snap_mapper.cc952
-rwxr-xr-xsrc/test/test_split.sh68
-rw-r--r--src/test/test_str_list.cc45
-rw-r--r--src/test/test_stress_watch.cc123
-rw-r--r--src/test/test_striper.cc89
-rw-r--r--src/test/test_subprocess.cc314
-rw-r--r--src/test/test_texttable.cc78
-rw-r--r--src/test/test_trans.cc80
-rwxr-xr-xsrc/test/test_unfound.sh139
-rw-r--r--src/test/test_utime.cc67
-rw-r--r--src/test/test_weighted_shuffle.cc39
-rw-r--r--src/test/test_workqueue.cc98
-rw-r--r--src/test/test_xlist.cc118
-rw-r--r--src/test/testclass.cc57
-rw-r--r--src/test/testcrypto.cc60
-rw-r--r--src/test/testkeys.cc69
-rw-r--r--src/test/testmsgr.cc144
-rw-r--r--src/test/ubuntu-18.04/Dockerfile.in31
l---------src/test/ubuntu-18.04/debian1
l---------src/test/ubuntu-18.04/install-deps.sh1
l---------src/test/ubuntu-20.041
-rw-r--r--src/test/unit.cc48
-rw-r--r--src/test/utf8.cc66
-rwxr-xr-xsrc/test/vstart_wrapper.sh75
-rw-r--r--src/test/xattr_bench.cc196
1166 files changed, 505859 insertions, 0 deletions
diff --git a/src/test/.gitignore b/src/test/.gitignore
new file mode 100644
index 000000000..d492cb42e
--- /dev/null
+++ b/src/test/.gitignore
@@ -0,0 +1,4 @@
+/cli/crushtool/foo
+/virtualenv
+*.log
+*.trs
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
new file mode 100644
index 000000000..841b7ffa6
--- /dev/null
+++ b/src/test/CMakeLists.txt
@@ -0,0 +1,983 @@
+include(AddCephTest)
+
+set(UNITTEST_LIBS GMock::Main GMock::GMock GTest::GTest ${CMAKE_THREAD_LIBS_INIT}
+ ${GSSAPI_LIBRARIES} ${CMAKE_DL_LIBS})
+
+if(WIN32)
+ # Needed by Boost.
+ list(APPEND UNITTEST_LIBS ws2_32)
+endif()
+
+add_library(unit-main OBJECT unit.cc)
+target_include_directories(unit-main PRIVATE
+ $<TARGET_PROPERTY:GTest::GTest,INTERFACE_INCLUDE_DIRECTORIES>)
+
+add_subdirectory(common)
+add_subdirectory(compressor)
+add_subdirectory(crush)
+add_subdirectory(direct_messenger)
+add_subdirectory(encoding)
+add_subdirectory(librados)
+add_subdirectory(librados_test_stub)
+if(WITH_LIBRADOSSTRIPER)
+ add_subdirectory(libradosstriper)
+endif()
+if(WITH_RBD AND NOT WIN32)
+ # librbd tests require libcls*, which in turn require libos and libosd, which
+ # haven't been ported to Windows yet.
+ add_subdirectory(librbd)
+endif(WITH_RBD AND NOT WIN32)
+if (WITH_CEPHFS)
+ add_subdirectory(mds)
+endif()
+add_subdirectory(pybind)
+add_subdirectory(dokan)
+add_subdirectory(libcephfs)
+
+# Not available on Windows for the time being.
+if(NOT WIN32)
+ # libcls_* dependencies cascade to osd, kv and other libs that are not
+ # available on Windows yet.
+ add_subdirectory(cls_hello)
+ add_subdirectory(cls_cas)
+ add_subdirectory(cls_lock)
+ add_subdirectory(cls_log)
+ add_subdirectory(cls_numops)
+ add_subdirectory(cls_sdk)
+ if(WITH_RBD)
+ add_subdirectory(cls_journal)
+ add_subdirectory(cls_rbd)
+ endif(WITH_RBD)
+ add_subdirectory(cls_refcount)
+ add_subdirectory(cls_rgw)
+ add_subdirectory(cls_version)
+ add_subdirectory(cls_lua)
+ add_subdirectory(cls_rgw_gc)
+ add_subdirectory(cls_queue)
+ add_subdirectory(cls_2pc_queue)
+ add_subdirectory(cls_cmpomap)
+ add_subdirectory(journal)
+
+ add_subdirectory(erasure-code)
+ add_subdirectory(fs)
+ add_subdirectory(libcephsqlite)
+ add_subdirectory(client)
+ add_subdirectory(mon)
+ if(WITH_MGR)
+ add_subdirectory(mgr)
+ endif()
+ add_subdirectory(msgr)
+ add_subdirectory(neorados)
+ add_subdirectory(objectstore)
+ add_subdirectory(ObjectMap)
+ add_subdirectory(osd)
+ add_subdirectory(osdc)
+ add_subdirectory(immutable_object_cache)
+ add_subdirectory(exporter)
+endif(NOT WIN32)
+
+if(WITH_RADOSGW)
+ set(rgw_libs rgw_a)
+ if(WITH_RADOSGW_AMQP_ENDPOINT)
+ list(APPEND rgw_libs amqp_mock)
+ endif()
+ if(WITH_RADOSGW_KAFKA_ENDPOINT)
+ list(APPEND rgw_libs kafka_stub)
+ endif()
+ add_subdirectory(rgw)
+endif(WITH_RADOSGW)
+if(WITH_RBD AND NOT WIN32)
+add_subdirectory(rbd_mirror)
+endif(WITH_RBD AND NOT WIN32)
+if(WITH_SEASTAR)
+ add_subdirectory(crimson)
+endif()
+add_subdirectory(system)
+if(WITH_FIO)
+ add_subdirectory(fio)
+endif()
+add_subdirectory(lazy-omap-stats)
+
+# test_timers
+add_executable(ceph_test_timers
+ TestTimers.cc
+ )
+target_link_libraries(ceph_test_timers global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+
+# test_signal_handlers
+add_executable(ceph_test_signal_handlers
+ TestSignalHandlers.cc
+ )
+target_link_libraries(ceph_test_signal_handlers global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+
+# test_rewrite_latency
+add_executable(ceph_test_rewrite_latency
+ test_rewrite_latency.cc
+ )
+target_link_libraries(ceph_test_rewrite_latency ceph-common)
+
+# test_crypt
+add_executable(test_crypto
+ testcrypto.cc)
+target_link_libraries(test_crypto
+ ceph-common)
+
+add_executable(test_build_libcommon buildtest_skeleton.cc)
+target_link_libraries(test_build_libcommon ceph-common pthread ${CRYPTO_LIBS} ${EXTRALIBS})
+
+if(WITH_RADOSGW)
+ add_executable(test_build_librgw buildtest_skeleton.cc)
+ target_link_libraries(test_build_librgw rgw_a pthread ${CRYPTO_LIBS} ${EXTRALIBS})
+endif(WITH_RADOSGW)
+
+if(WITH_LIBCEPHFS)
+ # From src/test/Makefile-client.am: I dont get this one... testing the osdc build but link in libcephfs?
+ add_executable(test_build_libcephfs buildtest_skeleton.cc)
+ target_link_libraries(test_build_libcephfs cephfs pthread ceph-common ${CRYPTO_LIBS} ${EXTRALIBS})
+endif(WITH_LIBCEPHFS)
+
+add_executable(test_build_librados buildtest_skeleton.cc)
+target_link_libraries(test_build_librados librados pthread ${CRYPTO_LIBS} ${EXTRALIBS} ceph-common ${BLKID_LIBRARIES})
+if(NOT WIN32)
+ target_link_libraries(test_build_librados os osdc osd cls_lock_client)
+endif()
+
+# bench_log
+set(bench_log_srcs
+ bench_log.cc
+ )
+add_executable(ceph_bench_log
+ ${bench_log_srcs}
+ )
+target_link_libraries(ceph_bench_log global pthread ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+if(NOT WIN32)
+ target_link_libraries(ceph_bench_log rt)
+endif()
+
+if(WITH_SYSTEMD)
+ add_executable(ceph_bench_journald_logger
+ bench_journald_logger.cc)
+ target_link_libraries(ceph_bench_journald_logger ceph-common)
+endif()
+
+# ceph_test_mutate
+add_executable(ceph_test_mutate
+ test_mutate.cc
+ )
+target_link_libraries(ceph_test_mutate global librados ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS})
+
+if(NOT WIN32)
+# test_trans
+add_executable(test_trans
+ test_trans.cc
+ )
+target_link_libraries(test_trans os global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+endif()
+
+## Benchmarks
+
+
+# ceph_omapbench
+set(omapbench_srcs
+ omap_bench.cc
+ )
+add_executable(ceph_omapbench
+ ${omapbench_srcs}
+ )
+target_link_libraries(ceph_omapbench
+ librados
+ ceph-common
+ Boost::program_options)
+
+if(WITH_KVS)
+ # ceph_kvstorebench
+ set(kvstorebench_srcs
+ kv_store_bench.cc
+ ${CMAKE_SOURCE_DIR}/src/key_value_store/kv_flat_btree_async.cc
+ )
+ add_executable(ceph_kvstorebench ${kvstorebench_srcs})
+ target_link_libraries(ceph_kvstorebench librados global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+ install(TARGETS ceph_kvstorebench DESTINATION bin)
+endif(WITH_KVS)
+
+if(NOT WIN32)
+ # ceph_objectstore_bench
+ add_executable(ceph_objectstore_bench objectstore_bench.cc)
+ target_link_libraries(ceph_objectstore_bench os global ${BLKID_LIBRARIES})
+endif()
+
+if(${WITH_RADOSGW})
+ # test_cors
+ set(test_cors_srcs test_cors.cc)
+ add_executable(test_cors
+ ${test_cors_srcs}
+ )
+ target_link_libraries(test_cors
+ librados
+ ${rgw_libs}
+ global
+ ${BLKID_LIBRARIES}
+ ${CURL_LIBRARIES}
+ ${EXPAT_LIBRARIES}
+ ${CMAKE_DL_LIBS} ${UNITTEST_LIBS})
+
+ # ceph_test_cls_rgw_meta
+ set(test_cls_rgw_meta_srcs test_rgw_admin_meta.cc)
+ add_executable(ceph_test_cls_rgw_meta
+ ${test_cls_rgw_meta_srcs}
+ )
+ target_link_libraries(ceph_test_cls_rgw_meta
+ librados
+ ${rgw_libs}
+ global
+ cls_version_client
+ cls_log_client
+ cls_refcount_client
+ cls_rgw_client
+ cls_user_client
+ cls_lock_client
+ ${BLKID_LIBRARIES}
+ ${CURL_LIBRARIES}
+ ${EXPAT_LIBRARIES}
+ ${CMAKE_DL_LIBS} ${UNITTEST_LIBS} ${CRYPTO_LIBS})
+
+ install(TARGETS
+ ceph_test_cls_rgw_meta
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+ # ceph_test_cls_rgw_log
+ set(ceph_test_cls_rgw_log_srcs
+ test_rgw_admin_log.cc
+ )
+ add_executable(ceph_test_cls_rgw_log
+ ${ceph_test_cls_rgw_log_srcs}
+ )
+ target_link_libraries(ceph_test_cls_rgw_log
+ librados
+ ${rgw_libs}
+ global
+ cls_version_client
+ cls_log_client
+ cls_refcount_client
+ cls_rgw_client
+ cls_user_client
+ cls_lock_client
+ ${BLKID_LIBRARIES}
+ ${CURL_LIBRARIES}
+ ${EXPAT_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CRYPTO_LIBS}
+ )
+
+# ceph_test_librgw_file (nfs-like RGW interface)
+add_executable(ceph_test_librgw_file
+ librgw_file.cc
+ )
+target_link_libraries(ceph_test_librgw_file
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${ALLOC_LIBS}
+ )
+install(TARGETS ceph_test_librgw_file DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_dependencies(ceph_test_librgw_file
+ ceph_test_librgw_file_cd
+ ceph_test_librgw_file_gp
+ ceph_test_librgw_file_nfsns
+ ceph_test_librgw_file_aw
+ ceph_test_librgw_file_marker)
+
+# ceph_test_librgw_file_cd (just the rgw_file create-delete bucket ops)
+add_executable(ceph_test_librgw_file_cd
+ librgw_file_cd.cc
+ )
+target_link_libraries(ceph_test_librgw_file_cd
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${ALLOC_LIBS}
+ )
+install(TARGETS ceph_test_librgw_file_cd DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_librgw_file_gp (just the rgw_file get-put bucket ops)
+add_executable(ceph_test_librgw_file_gp
+ librgw_file_gp.cc
+ )
+target_link_libraries(ceph_test_librgw_file_gp
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${ALLOC_LIBS}
+ )
+install(TARGETS ceph_test_librgw_file_gp DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_librgw_file_nfsns (nfs namespace tests)
+add_executable(ceph_test_librgw_file_nfsns
+ librgw_file_nfsns.cc
+ )
+target_include_directories(ceph_test_librgw_file_nfsns
+ PUBLIC "${LUA_INCLUDE_DIR}"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/driver/rados")
+target_link_libraries(ceph_test_librgw_file_nfsns
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${LUA_LIBRARIES}
+ ${ALLOC_LIBS}
+ )
+ target_link_libraries(ceph_test_librgw_file_nfsns spawn)
+install(TARGETS ceph_test_librgw_file_nfsns DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_librgw_file_aw (nfs write transaction [atomic write] tests)
+add_executable(ceph_test_librgw_file_aw
+ librgw_file_aw.cc
+ )
+target_link_libraries(ceph_test_librgw_file_aw
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${ALLOC_LIBS}
+ )
+install(TARGETS ceph_test_librgw_file_aw DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_librgw_file_marker (READDIR with string and uint64 offsets)
+add_executable(ceph_test_librgw_file_marker
+ librgw_file_marker.cc
+ )
+target_include_directories(ceph_test_librgw_file_marker
+ PUBLIC "${LUA_INCLUDE_DIR}"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/driver/rados")
+target_link_libraries(ceph_test_librgw_file_marker
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${LUA_LIBRARIES}
+ ${ALLOC_LIBS}
+ )
+ target_link_libraries(ceph_test_librgw_file_marker spawn)
+install(TARGETS ceph_test_librgw_file_marker DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_librgw_file_xattr (attribute ops)
+add_executable(ceph_test_librgw_file_xattr
+ librgw_file_xattr.cc)
+target_include_directories(ceph_test_librgw_file_xattr
+ PUBLIC "${LUA_INCLUDE_DIR}"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/driver/rados")
+target_link_libraries(ceph_test_librgw_file_xattr
+ rgw
+ librados
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${LUA_LIBRARIES}
+ ${ALLOC_LIBS}
+ )
+target_link_libraries(ceph_test_librgw_file_xattr spawn)
+
+# ceph_test_rgw_token
+add_executable(ceph_test_rgw_token
+ test_rgw_token.cc
+ )
+target_link_libraries(ceph_test_rgw_token
+ rgw
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ )
+
+# librgw_file_gp (just the rgw_file get-put bucket ops)
+add_executable(test_rgw_ldap
+ ${CMAKE_SOURCE_DIR}/src/rgw/rgw_ldap.cc
+ test_rgw_ldap.cc
+ )
+target_include_directories(test_rgw_ldap
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/driver/rados")
+target_link_libraries(test_rgw_ldap
+ librados
+ ceph-common
+ ${UNITTEST_LIBS})
+if(WITH_OPENLDAP)
+ target_link_libraries(test_rgw_ldap
+ OpenLDAP::OpenLDAP)
+endif()
+endif(${WITH_RADOSGW})
+
+# ceph_multi_stress_watch
+add_executable(ceph_multi_stress_watch
+ multi_stress_watch.cc
+ )
+target_link_libraries(ceph_multi_stress_watch librados global radostest-cxx)
+
+install(TARGETS
+ ceph_bench_log
+ ceph_multi_stress_watch
+ ceph_omapbench
+ DESTINATION bin)
+
+if(NOT WIN32)
+#ceph_perf_local
+add_executable(ceph_perf_local
+ perf_local.cc
+ perf_helper.cc)
+if(HAVE_SSE)
+ set(PERF_LOCAL_FLAGS ${SSE3_FLAGS})
+endif(HAVE_SSE)
+if(HAVE_NEON)
+ set(PERF_LOCAL_FLAGS ${ARM_NEON_FLAGS})
+endif(HAVE_NEON)
+if(PERF_LOCAL_FLAGS)
+ set_target_properties(ceph_perf_local PROPERTIES COMPILE_FLAGS
+ ${PERF_LOCAL_FLAGS})
+endif()
+target_link_libraries(ceph_perf_local global ${UNITTEST_LIBS})
+
+install(TARGETS
+ ceph_objectstore_bench
+ ceph_perf_local
+ DESTINATION bin)
+
+# ceph_test_keys
+add_executable(ceph_test_keys
+ testkeys.cc
+ )
+target_link_libraries(ceph_test_keys mon global ${CMAKE_DL_LIBS})
+
+# ceph_test_snap_mapper
+add_executable(ceph_test_snap_mapper
+ test_snap_mapper.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+target_link_libraries(ceph_test_snap_mapper osd global ${BLKID_LIBRARIES} ${UNITTEST_LIBS})
+endif(NOT WIN32)
+
+add_executable(ceph_test_stress_watch
+ test_stress_watch.cc
+ )
+target_link_libraries(ceph_test_stress_watch
+ librados
+ ${UNITTEST_LIBS}
+ radostest-cxx
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ )
+install(TARGETS
+ ceph_test_stress_watch
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# limited Windows signal support, skipped for now
+if(NOT WIN32)
+add_executable(ceph_test_c2c
+ test_c2c.cc
+ )
+target_link_libraries(ceph_test_c2c
+ global
+ ceph-common
+ pthread
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+install(TARGETS
+ ceph_test_c2c
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(NOT WIN32)
+
+if(WITH_FUSE)
+ add_executable(ceph_test_cfuse_cache_invalidate
+ test_cfuse_cache_invalidate.cc
+ )
+ target_link_libraries(ceph_test_cfuse_cache_invalidate
+ ceph-common
+ )
+endif(WITH_FUSE)
+
+if(${WITH_CEPHFS})
+ add_executable(test_c_headers
+ test_c_headers.c
+ )
+ target_link_libraries(test_c_headers
+ librados
+ cephfs)
+endif(${WITH_CEPHFS})
+
+if(HAVE_BLKID OR FREEBSD)
+ add_executable(ceph_test_get_blkdev_props
+ test_get_blkdev_props.cc
+ )
+ target_link_libraries(ceph_test_get_blkdev_props
+ ceph-common
+ pthread
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ )
+endif(HAVE_BLKID OR FREEBSD)
+
+# ceph_test_admin_socket_output
+
+if(StdFilesystem_FOUND)
+ add_executable(ceph_test_admin_socket_output
+ test_admin_socket_output.cc
+ admin_socket_output.cc
+ admin_socket_output_tests.cc)
+ target_link_libraries(ceph_test_admin_socket_output
+ ceph-common StdFilesystem::filesystem)
+ install(TARGETS
+ ceph_test_admin_socket_output
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+#make check starts here
+
+#following dependencies are run inside make check unit tests
+add_dependencies(tests
+ ceph-authtool
+ ceph-conf
+ rados
+ ceph_snappy)
+if(NOT WIN32)
+ # Not currently supported on Windows
+ add_dependencies(tests
+ ceph-mon
+ get_command_descriptions
+ ceph-dencoder
+ ceph-dencoder-modules
+ ceph-objectstore-tool
+ ceph-kvstore-tool
+ ceph-monstore-tool
+ ceph-osd
+ osdmaptool
+ ceph_example
+ ceph_snappy
+ cls_lock
+ ceph_test_objectstore
+ ceph_erasure_code_non_regression
+ cython_modules
+ crushtool
+ monmaptool)
+
+if (WITH_CEPHFS)
+ add_dependencies(tests ceph-mds)
+endif()
+if(WITH_MGR)
+ add_dependencies(tests ceph-mgr)
+endif()
+if(WITH_RBD)
+ add_dependencies(tests unittest_librbd rbd)
+ if(FREEBSD)
+ add_dependencies(tests rbd-ggate)
+ endif(FREEBSD)
+ if(WITH_RBD_MIRROR)
+ add_dependencies(tests unittest_librbd rbd-mirror)
+ endif(WITH_RBD_MIRROR)
+endif(WITH_RBD)
+if(WITH_RADOSGW)
+ add_dependencies(tests radosgw radosgw-admin)
+endif()
+#add dependency from fio just to ensure the plugin build isn't failing
+if(WITH_FIO)
+ add_dependencies(tests fio_ceph_objectstore)
+endif()
+
+if(WITH_RBD)
+ # Run rbd-unit-tests separate so they an run in parallel
+ # For values see: src/include/rbd/features.h
+ add_ceph_test(run-rbd-unit-tests-N.sh ${CMAKE_CURRENT_SOURCE_DIR}/run-rbd-unit-tests.sh N)
+ add_ceph_test(run-rbd-unit-tests-0.sh ${CMAKE_CURRENT_SOURCE_DIR}/run-rbd-unit-tests.sh 0)
+ add_ceph_test(run-rbd-unit-tests-1.sh ${CMAKE_CURRENT_SOURCE_DIR}/run-rbd-unit-tests.sh 1)
+ add_ceph_test(run-rbd-unit-tests-61.sh ${CMAKE_CURRENT_SOURCE_DIR}/run-rbd-unit-tests.sh 61)
+ add_ceph_test(run-rbd-unit-tests-109.sh ${CMAKE_CURRENT_SOURCE_DIR}/run-rbd-unit-tests.sh 109)
+ add_ceph_test(run-rbd-unit-tests-127.sh ${CMAKE_CURRENT_SOURCE_DIR}/run-rbd-unit-tests.sh 127)
+ if(FREEBSD)
+ add_ceph_test(rbd-ggate.sh ${CMAKE_CURRENT_SOURCE_DIR}/rbd-ggate.sh)
+ endif(FREEBSD)
+endif(WITH_RBD)
+
+endif(NOT WIN32)
+
+add_ceph_test(run-cli-tests ${CMAKE_CURRENT_SOURCE_DIR}/run-cli-tests)
+
+# flaky, see https://tracker.ceph.com/issues/44243
+#add_ceph_test(test_objectstore_memstore.sh ${CMAKE_CURRENT_SOURCE_DIR}/test_objectstore_memstore.sh)
+
+# flaky
+#add_ceph_test(test_pidfile.sh ${CMAKE_CURRENT_SOURCE_DIR}/test_pidfile.sh)
+
+add_ceph_test(smoke.sh ${CMAKE_CURRENT_SOURCE_DIR}/smoke.sh)
+
+set_property(
+ TEST ${tox_tests}
+ PROPERTY ENVIRONMENT ${env_vars_for_tox_tests})
+
+# unittest_admin_socket
+add_executable(unittest_admin_socket
+ admin_socket.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_admin_socket)
+target_link_libraries(unittest_admin_socket global)
+
+# unittest_encoding
+add_executable(unittest_encoding
+ encoding.cc
+ )
+add_ceph_unittest(unittest_encoding)
+target_link_libraries(unittest_encoding ceph-common)
+
+# unittest_addrs
+add_executable(unittest_addrs
+ test_addrs.cc
+ )
+add_ceph_unittest(unittest_addrs)
+target_link_libraries(unittest_addrs ceph-common)
+
+# unittest_auth
+add_executable(unittest_auth
+ test_auth.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_auth)
+target_link_libraries(unittest_auth global)
+
+# unittest_workqueue
+add_executable(unittest_workqueue
+ test_workqueue.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_workqueue)
+target_link_libraries(unittest_workqueue global)
+
+# unittest_striper
+if(WITH_LIBRADOSSTRIPER)
+ add_executable(unittest_striper
+ test_striper.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+ add_ceph_unittest(unittest_striper)
+ target_link_libraries(unittest_striper global ${BLKID_LIBRARIES})
+endif()
+
+# unittest_str_list
+add_executable(unittest_str_list
+ test_str_list.cc
+ )
+add_ceph_unittest(unittest_str_list)
+target_link_libraries(unittest_str_list global)
+
+# This test's usage of templates generates more sections than a PE file can
+# contain.
+if(NOT MINGW)
+# unittest_log
+add_executable(unittest_log
+ ${CMAKE_SOURCE_DIR}/src/log/test.cc
+ )
+add_ceph_unittest(unittest_log)
+target_link_libraries(unittest_log global)
+endif(NOT MINGW)
+
+# unittest_base64
+add_executable(unittest_base64
+ base64.cc
+ )
+add_ceph_unittest(unittest_base64)
+target_link_libraries(unittest_base64 global)
+
+# unittest_ceph_argparse
+add_executable(unittest_ceph_argparse
+ ceph_argparse.cc
+ )
+add_ceph_unittest(unittest_ceph_argparse)
+target_link_libraries(unittest_ceph_argparse global)
+
+# unittest_ceph_compatset
+add_executable(unittest_ceph_compatset
+ ceph_compatset.cc
+ )
+add_ceph_unittest(unittest_ceph_compatset)
+target_link_libraries(unittest_ceph_compatset global)
+
+# unittest_gather
+add_executable(unittest_gather
+ gather.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_gather)
+target_link_libraries(unittest_gather global)
+
+# unittest_run_cmd
+add_executable(unittest_run_cmd
+ run_cmd.cc
+ )
+add_ceph_unittest(unittest_run_cmd)
+target_link_libraries(unittest_run_cmd global)
+
+if(NOT WIN32)
+# signals
+add_executable(unittest_signals
+ signals.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_signals)
+target_link_libraries(unittest_signals global)
+endif()
+
+# unittest_simple_spin
+add_executable(unittest_simple_spin
+ simple_spin.cc
+ )
+add_ceph_unittest(unittest_simple_spin)
+target_link_libraries(unittest_simple_spin global)
+
+# unittest_bufferlist
+add_executable(unittest_bufferlist
+ bufferlist.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_bufferlist)
+target_link_libraries(unittest_bufferlist global)
+
+# compiletest_cxx11_client
+add_executable(compiletest_cxx11_client
+ cxx11_client.cc
+ )
+if(CMAKE_VERSION VERSION_LESS "3.8")
+ # this is ugly as we'll end with -std=c++11 overriding the previous -std=c++17
+ # I would love to have a better way for old Cmakes
+ set_target_properties(compiletest_cxx11_client
+ PROPERTIES COMPILE_FLAGS "-std=c++11 -Werror -pedantic"
+ )
+else()
+ set_target_properties(compiletest_cxx11_client
+ PROPERTIES COMPILE_FLAGS "-Werror -pedantic"
+ CMAKE_CXX_STANDARD 11
+ CXX_STANDARD_REQUIRED ON
+ )
+endif()
+add_ceph_test(compiletest_cxx11_client
+ "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/compiletest_cxx11_client"
+ )
+target_link_libraries(compiletest_cxx11_client global)
+
+# unittest_xlist
+add_executable(unittest_xlist
+ test_xlist.cc
+ )
+add_ceph_unittest(unittest_xlist)
+target_link_libraries(unittest_xlist ceph-common)
+
+# unittest_arch
+add_executable(unittest_arch
+ test_arch.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_arch)
+target_link_libraries(unittest_arch global)
+
+# unittest_denc
+add_executable(unittest_denc
+ test_denc.cc
+ )
+add_ceph_unittest(unittest_denc)
+target_link_libraries(unittest_denc global)
+
+# unittest_mempool
+add_executable(unittest_mempool
+ test_mempool.cc
+ )
+add_ceph_unittest(unittest_mempool)
+target_link_libraries(unittest_mempool global)
+
+# unittest_features
+add_executable(unittest_features
+ test_features.cc
+ )
+add_ceph_unittest(unittest_features)
+target_link_libraries(unittest_features global)
+
+# unittest_crypto
+add_executable(unittest_crypto
+ crypto.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_crypto)
+target_link_libraries(unittest_crypto global)
+
+# unittest_crypto_init
+add_executable(unittest_crypto_init
+ crypto_init.cc
+ )
+add_ceph_unittest(unittest_crypto_init)
+target_link_libraries(unittest_crypto_init global)
+
+# unittest_perf_counters
+add_executable(unittest_perf_counters
+ perf_counters.cc
+ )
+add_ceph_unittest(unittest_perf_counters)
+target_link_libraries(unittest_perf_counters global)
+
+# unittest_ceph_crypto
+add_executable(unittest_ceph_crypto
+ ceph_crypto.cc)
+add_ceph_unittest(unittest_ceph_crypto)
+target_link_libraries(unittest_ceph_crypto global)
+
+# unittest_utf8
+add_executable(unittest_utf8
+ utf8.cc)
+add_ceph_unittest(unittest_utf8)
+target_link_libraries(unittest_utf8 global)
+
+# unittest_mime
+add_executable(unittest_mime
+ mime.cc)
+add_ceph_unittest(unittest_mime)
+target_link_libraries(unittest_mime ceph-common)
+
+# unittest_escape
+add_executable(unittest_escape
+ escape.cc)
+add_ceph_unittest(unittest_escape)
+target_link_libraries(unittest_escape ceph-common)
+
+# unittest_strtol
+add_executable(unittest_strtol
+ strtol.cc)
+add_ceph_unittest(unittest_strtol)
+target_link_libraries(unittest_strtol ceph-common)
+
+# unittest_confutils
+add_executable(unittest_confutils
+ confutils.cc)
+add_ceph_unittest(unittest_confutils)
+target_link_libraries(unittest_confutils ceph-common)
+
+# unittest_heartbeatmap
+add_executable(unittest_heartbeatmap
+ heartbeat_map.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_heartbeatmap)
+target_link_libraries(unittest_heartbeatmap global ceph-common)
+
+if(${WITH_RADOSGW})
+ # unittest_formatter
+ add_executable(unittest_formatter
+ formatter.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+ add_ceph_unittest(unittest_formatter)
+ target_link_libraries(unittest_formatter global)
+endif(${WITH_RADOSGW})
+
+# unittest_daemon_config
+add_executable(unittest_daemon_config
+ daemon_config.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_daemon_config)
+target_link_libraries(unittest_daemon_config
+ ceph-common
+ global
+ ${BLKID_LIBRARIES}
+ ${EXTRALIBS}
+ )
+
+if(WITH_LIBCEPHFS)
+# unittest_libcephfs_config
+add_executable(unittest_libcephfs_config
+ libcephfs_config.cc
+ )
+add_ceph_unittest(unittest_libcephfs_config)
+target_link_libraries(unittest_libcephfs_config cephfs)
+endif(WITH_LIBCEPHFS)
+
+if(WITH_RBD)
+# unittest_rbd_replay
+add_executable(unittest_rbd_replay
+ test_rbd_replay.cc)
+add_ceph_unittest(unittest_rbd_replay)
+target_link_libraries(unittest_rbd_replay
+ librbd
+ librados
+ global
+ ceph-common
+ rbd_replay
+ rbd_replay_ios
+ ${BLKID_LIBRARIES}
+ )
+endif(WITH_RBD)
+
+# unittest_ipaddr
+add_executable(unittest_ipaddr
+ test_ipaddr.cc)
+add_ceph_unittest(unittest_ipaddr)
+target_link_libraries(unittest_ipaddr global GTest::Main)
+
+# unittest_utime
+add_executable(unittest_utime
+ test_utime.cc)
+add_ceph_unittest(unittest_utime)
+target_link_libraries(unittest_utime ceph-common)
+
+# unittest_texttable
+add_executable(unittest_texttable
+ test_texttable.cc
+ $<TARGET_OBJECTS:common_texttable_obj>)
+add_ceph_unittest(unittest_texttable)
+target_link_libraries(unittest_texttable ceph-common)
+
+if(NOT WIN32)
+# unittest_on_exit
+add_executable(unittest_on_exit
+ on_exit.cc)
+add_ceph_unittest(unittest_on_exit)
+target_link_libraries(unittest_on_exit ceph-common)
+endif()
+
+# unittest_subprocess
+add_executable(unittest_subprocess
+ test_subprocess.cc)
+add_ceph_unittest(unittest_subprocess)
+target_link_libraries(unittest_subprocess ceph-common)
+
+# unittest_pageset
+add_executable(unittest_pageset test_pageset.cc)
+add_ceph_unittest(unittest_pageset)
+target_link_libraries(unittest_pageset ceph-common)
+
+add_executable(unittest_random_string test_random_string.cc $<TARGET_OBJECTS:unit-main>)
+add_ceph_unittest(unittest_random_string)
+target_link_libraries(unittest_random_string global)
+
+# unittest_any_
+add_executable(unittest_any test_any.cc)
+add_ceph_unittest(unittest_any)
+
+# unittest_weighted_shuffle
+add_executable(unittest_weighted_shuffle test_weighted_shuffle.cc)
+add_ceph_unittest(unittest_weighted_shuffle)
+
+add_executable(unittest_intarith test_intarith.cc)
+add_ceph_unittest(unittest_intarith)
+#make check ends here
diff --git a/src/test/ObjectMap/CMakeLists.txt b/src/test/ObjectMap/CMakeLists.txt
new file mode 100644
index 000000000..837ec5434
--- /dev/null
+++ b/src/test/ObjectMap/CMakeLists.txt
@@ -0,0 +1,42 @@
+# ceph_test_object_map
+add_executable(ceph_test_object_map
+ test_object_map.cc
+ KeyValueDBMemory.cc
+ )
+add_ceph_unittest(ceph_test_object_map)
+target_link_libraries(ceph_test_object_map
+ os
+ ceph-common
+ ${UNITTEST_LIBS}
+ global
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+
+# ceph_test_keyvaluedb_atomicity
+add_executable(ceph_test_keyvaluedb_atomicity
+ test_keyvaluedb_atomicity.cc
+ )
+target_link_libraries(ceph_test_keyvaluedb_atomicity
+ os
+ ceph-common
+ ${UNITTEST_LIBS}
+ global
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+
+# ceph_test_keyvaluedb_iterators
+add_executable(ceph_test_keyvaluedb_iterators
+ test_keyvaluedb_iterators.cc
+ KeyValueDBMemory.cc
+ )
+target_link_libraries(ceph_test_keyvaluedb_iterators
+ os
+ ceph-common
+ ${UNITTEST_LIBS}
+ global
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+
diff --git a/src/test/ObjectMap/KeyValueDBMemory.cc b/src/test/ObjectMap/KeyValueDBMemory.cc
new file mode 100644
index 000000000..234e96339
--- /dev/null
+++ b/src/test/ObjectMap/KeyValueDBMemory.cc
@@ -0,0 +1,264 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/encoding.h"
+#include "KeyValueDBMemory.h"
+#include <map>
+#include <set>
+#include <iostream>
+
+using namespace std;
+
+/**
+ * Iterate over the whole key space of the in-memory store
+ *
+ * @note Removing keys from the store while iterating over the store key-space
+ * may result in unspecified behavior.
+ * If one wants to safely iterate over the store while updating the
+ * store, one should instead use a snapshot iterator, which provides
+ * strong read-consistency.
+ */
+class WholeSpaceMemIterator : public KeyValueDB::WholeSpaceIteratorImpl {
+protected:
+ KeyValueDBMemory *db;
+ bool ready;
+
+ map<pair<string,string>, bufferlist>::iterator it;
+
+public:
+ explicit WholeSpaceMemIterator(KeyValueDBMemory *db) : db(db), ready(false) { }
+ ~WholeSpaceMemIterator() override { }
+
+ int seek_to_first() override {
+ if (db->db.empty()) {
+ it = db->db.end();
+ ready = false;
+ return 0;
+ }
+ it = db->db.begin();
+ ready = true;
+ return 0;
+ }
+
+ int seek_to_first(const string &prefix) override {
+ it = db->db.lower_bound(make_pair(prefix, ""));
+ if (db->db.empty() || (it == db->db.end())) {
+ it = db->db.end();
+ ready = false;
+ return 0;
+ }
+ ready = true;
+ return 0;
+ }
+
+ int seek_to_last() override {
+ it = db->db.end();
+ if (db->db.empty()) {
+ ready = false;
+ return 0;
+ }
+ --it;
+ ceph_assert(it != db->db.end());
+ ready = true;
+ return 0;
+ }
+
+ int seek_to_last(const string &prefix) override {
+ string tmp(prefix);
+ tmp.append(1, (char) 0);
+ it = db->db.upper_bound(make_pair(tmp,""));
+
+ if (db->db.empty() || (it == db->db.end())) {
+ seek_to_last();
+ }
+ else {
+ ready = true;
+ prev();
+ }
+ return 0;
+ }
+
+ int lower_bound(const string &prefix, const string &to) override {
+ it = db->db.lower_bound(make_pair(prefix,to));
+ if ((db->db.empty()) || (it == db->db.end())) {
+ it = db->db.end();
+ ready = false;
+ return 0;
+ }
+
+ ceph_assert(it != db->db.end());
+
+ ready = true;
+ return 0;
+ }
+
+ int upper_bound(const string &prefix, const string &after) override {
+ it = db->db.upper_bound(make_pair(prefix,after));
+ if ((db->db.empty()) || (it == db->db.end())) {
+ it = db->db.end();
+ ready = false;
+ return 0;
+ }
+ ceph_assert(it != db->db.end());
+ ready = true;
+ return 0;
+ }
+
+ bool valid() override {
+ return ready && (it != db->db.end());
+ }
+
+ bool begin() {
+ return ready && (it == db->db.begin());
+ }
+
+ int prev() override {
+ if (!begin() && ready)
+ --it;
+ else
+ it = db->db.end();
+ return 0;
+ }
+
+ int next() override {
+ if (valid())
+ ++it;
+ return 0;
+ }
+
+ string key() override {
+ if (valid())
+ return (*it).first.second;
+ else
+ return "";
+ }
+
+ pair<string,string> raw_key() override {
+ if (valid())
+ return (*it).first;
+ else
+ return make_pair("", "");
+ }
+
+ bool raw_key_is_prefixed(const string &prefix) override {
+ return prefix == (*it).first.first;
+ }
+
+ bufferlist value() override {
+ if (valid())
+ return (*it).second;
+ else
+ return bufferlist();
+ }
+
+ int status() override {
+ return 0;
+ }
+};
+
+int KeyValueDBMemory::get(const string &prefix,
+ const std::set<string> &key,
+ map<string, bufferlist> *out) {
+ if (!exists_prefix(prefix))
+ return 0;
+
+ for (std::set<string>::const_iterator i = key.begin();
+ i != key.end();
+ ++i) {
+ pair<string,string> k(prefix, *i);
+ if (db.count(k))
+ (*out)[*i] = db[k];
+ }
+ return 0;
+}
+
+int KeyValueDBMemory::get_keys(const string &prefix,
+ const std::set<string> &key,
+ std::set<string> *out) {
+ if (!exists_prefix(prefix))
+ return 0;
+
+ for (std::set<string>::const_iterator i = key.begin();
+ i != key.end();
+ ++i) {
+ if (db.count(make_pair(prefix, *i)))
+ out->insert(*i);
+ }
+ return 0;
+}
+
+int KeyValueDBMemory::set(const string &prefix,
+ const string &key,
+ const bufferlist &bl) {
+ db[make_pair(prefix,key)] = bl;
+ return 0;
+}
+
+int KeyValueDBMemory::rmkey(const string &prefix,
+ const string &key) {
+ db.erase(make_pair(prefix,key));
+ return 0;
+}
+
+int KeyValueDBMemory::rmkeys_by_prefix(const string &prefix) {
+ map<std::pair<string,string>,bufferlist>::iterator i;
+ i = db.lower_bound(make_pair(prefix, ""));
+ if (i == db.end())
+ return 0;
+
+ while (i != db.end()) {
+ std::pair<string,string> key = (*i).first;
+ if (key.first != prefix)
+ break;
+
+ ++i;
+ rmkey(key.first, key.second);
+ }
+ return 0;
+}
+
+int KeyValueDBMemory::rm_range_keys(const string &prefix, const string &start, const string &end) {
+ map<std::pair<string,string>,bufferlist>::iterator i;
+ i = db.lower_bound(make_pair(prefix, start));
+ if (i == db.end())
+ return 0;
+
+ while (i != db.end()) {
+ std::pair<string,string> key = (*i).first;
+ if (key.first != prefix)
+ break;
+ if (key.second >= end)
+ break;
+ ++i;
+ rmkey(key.first, key.second);
+ }
+ return 0;
+}
+
+KeyValueDB::WholeSpaceIterator KeyValueDBMemory::get_wholespace_iterator(IteratorOpts opts) {
+ return std::shared_ptr<KeyValueDB::WholeSpaceIteratorImpl>(
+ new WholeSpaceMemIterator(this)
+ );
+}
+
+class WholeSpaceSnapshotMemIterator : public WholeSpaceMemIterator {
+public:
+
+ /**
+ * @note
+ * We perform a copy of the db map, which is populated by bufferlists.
+ *
+ * These are designed as shallow containers, thus there is a chance that
+ * changing the underlying memory pages will lead to the iterator seeing
+ * erroneous states.
+ *
+ * Although we haven't verified this yet, there is this chance, so we should
+ * keep it in mind.
+ */
+
+ explicit WholeSpaceSnapshotMemIterator(KeyValueDBMemory *db) :
+ WholeSpaceMemIterator(db) { }
+ ~WholeSpaceSnapshotMemIterator() override {
+ delete db;
+ }
+};
+
diff --git a/src/test/ObjectMap/KeyValueDBMemory.h b/src/test/ObjectMap/KeyValueDBMemory.h
new file mode 100644
index 000000000..de84ede90
--- /dev/null
+++ b/src/test/ObjectMap/KeyValueDBMemory.h
@@ -0,0 +1,188 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <map>
+#include <set>
+#include <string>
+
+#include "kv/KeyValueDB.h"
+#include "include/buffer.h"
+#include "include/Context.h"
+
+using std::string;
+
+class KeyValueDBMemory : public KeyValueDB {
+public:
+ std::map<std::pair<string,string>,bufferlist> db;
+
+ KeyValueDBMemory() { }
+ explicit KeyValueDBMemory(KeyValueDBMemory *db) : db(db->db) { }
+ ~KeyValueDBMemory() override { }
+
+ int init(string _opt) override {
+ return 0;
+ }
+ int open(std::ostream &out, const std::string& cfs="") override {
+ return 0;
+ }
+ int create_and_open(std::ostream &out, const std::string& cfs="") override {
+ return 0;
+ }
+
+ int get(
+ const std::string &prefix,
+ const std::set<std::string> &key,
+ std::map<std::string, bufferlist> *out
+ ) override;
+ using KeyValueDB::get;
+
+ int get_keys(
+ const std::string &prefix,
+ const std::set<std::string> &key,
+ std::set<std::string> *out
+ );
+
+ int set(
+ const std::string &prefix,
+ const std::string &key,
+ const bufferlist &bl
+ );
+
+ int rmkey(
+ const std::string &prefix,
+ const std::string &key
+ );
+
+ int rmkeys_by_prefix(
+ const std::string &prefix
+ );
+
+ int rm_range_keys(
+ const std::string &prefix,
+ const std::string &start,
+ const std::string &end
+ );
+
+ class TransactionImpl_ : public TransactionImpl {
+ public:
+ std::list<Context *> on_commit;
+ KeyValueDBMemory *db;
+
+ explicit TransactionImpl_(KeyValueDBMemory *db) : db(db) {}
+
+
+ struct SetOp : public Context {
+ KeyValueDBMemory *db;
+ std::pair<std::string,std::string> key;
+ bufferlist value;
+ SetOp(KeyValueDBMemory *db,
+ const std::pair<std::string,std::string> &key,
+ const bufferlist &value)
+ : db(db), key(key), value(value) {}
+ void finish(int r) override {
+ db->set(key.first, key.second, value);
+ }
+ };
+
+ void set(const std::string &prefix, const std::string &k, const bufferlist& bl) override {
+ on_commit.push_back(new SetOp(db, std::make_pair(prefix, k), bl));
+ }
+
+ struct RmKeysOp : public Context {
+ KeyValueDBMemory *db;
+ std::pair<std::string,std::string> key;
+ RmKeysOp(KeyValueDBMemory *db,
+ const std::pair<std::string,std::string> &key)
+ : db(db), key(key) {}
+ void finish(int r) override {
+ db->rmkey(key.first, key.second);
+ }
+ };
+
+ using KeyValueDB::TransactionImpl::rmkey;
+ using KeyValueDB::TransactionImpl::set;
+ void rmkey(const std::string &prefix, const std::string &key) override {
+ on_commit.push_back(new RmKeysOp(db, std::make_pair(prefix, key)));
+ }
+
+ struct RmKeysByPrefixOp : public Context {
+ KeyValueDBMemory *db;
+ std::string prefix;
+ RmKeysByPrefixOp(KeyValueDBMemory *db,
+ const std::string &prefix)
+ : db(db), prefix(prefix) {}
+ void finish(int r) override {
+ db->rmkeys_by_prefix(prefix);
+ }
+ };
+ void rmkeys_by_prefix(const std::string &prefix) override {
+ on_commit.push_back(new RmKeysByPrefixOp(db, prefix));
+ }
+
+ struct RmRangeKeys: public Context {
+ KeyValueDBMemory *db;
+ std::string prefix, start, end;
+ RmRangeKeys(KeyValueDBMemory *db, const std::string &prefix, const std::string &s, const std::string &e)
+ : db(db), prefix(prefix), start(s), end(e) {}
+ void finish(int r) {
+ db->rm_range_keys(prefix, start, end);
+ }
+ };
+
+ void rm_range_keys(const std::string &prefix, const std::string &start, const std::string &end) {
+ on_commit.push_back(new RmRangeKeys(db, prefix, start, end));
+ }
+
+ int complete() {
+ for (auto i = on_commit.begin();
+ i != on_commit.end();
+ on_commit.erase(i++)) {
+ (*i)->complete(0);
+ }
+ return 0;
+ }
+
+ ~TransactionImpl_() override {
+ for (auto i = on_commit.begin();
+ i != on_commit.end();
+ on_commit.erase(i++)) {
+ delete *i;
+ }
+ }
+ };
+
+ Transaction get_transaction() override {
+ return Transaction(new TransactionImpl_(this));
+ }
+
+ int submit_transaction(Transaction trans) override {
+ return static_cast<TransactionImpl_*>(trans.get())->complete();
+ }
+
+ uint64_t get_estimated_size(std::map<std::string,uint64_t> &extras) override {
+ uint64_t total_size = 0;
+
+ for (auto& [key, bl] : db) {
+ string prefix = key.first;
+
+ uint64_t sz = bl.length();
+ total_size += sz;
+ if (extras.count(prefix) == 0)
+ extras[prefix] = 0;
+ extras[prefix] += sz;
+ }
+
+ return total_size;
+ }
+
+private:
+ bool exists_prefix(const std::string &prefix) {
+ std::map<std::pair<std::string,std::string>,bufferlist>::iterator it;
+ it = db.lower_bound(std::make_pair(prefix, ""));
+ return ((it != db.end()) && ((*it).first.first == prefix));
+ }
+
+ friend class WholeSpaceMemIterator;
+
+public:
+ WholeSpaceIterator get_wholespace_iterator(IteratorOpts opts = 0) override;
+};
diff --git a/src/test/ObjectMap/test_keyvaluedb_atomicity.cc b/src/test/ObjectMap/test_keyvaluedb_atomicity.cc
new file mode 100644
index 000000000..f93e68c4a
--- /dev/null
+++ b/src/test/ObjectMap/test_keyvaluedb_atomicity.cc
@@ -0,0 +1,109 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include <pthread.h>
+#include "include/buffer.h"
+#include "kv/KeyValueDB.h"
+#include <sys/types.h>
+#include <dirent.h>
+#include <string>
+#include <vector>
+#include <boost/scoped_ptr.hpp>
+#include <iostream>
+#include <sstream>
+#include "stdlib.h"
+#include "global/global_context.h"
+
+using namespace std;
+
+const string CONTROL_PREFIX = "CONTROL";
+const string PRIMARY_PREFIX = "PREFIX";
+const int NUM_COPIES = 100;
+const int NUM_THREADS = 30;
+
+string prefix_gen(int i) {
+ stringstream ss;
+ ss << PRIMARY_PREFIX << "_" << i << std::endl;
+ return ss.str();
+}
+
+int verify(KeyValueDB *db) {
+ // Verify
+ {
+ map<int, KeyValueDB::Iterator> iterators;
+ for (int i = 0; i < NUM_COPIES; ++i) {
+ iterators[i] = db->get_iterator(prefix_gen(i));
+ iterators[i]->seek_to_first();
+ }
+ while (iterators.rbegin()->second->valid()) {
+ for (map<int, KeyValueDB::Iterator>::iterator i = iterators.begin();
+ i != iterators.end();
+ ++i) {
+ ceph_assert(i->second->valid());
+ ceph_assert(i->second->key() == iterators.rbegin()->second->key());
+ bufferlist r = i->second->value();
+ bufferlist l = iterators.rbegin()->second->value();
+ i->second->next();
+ }
+ }
+ for (map<int, KeyValueDB::Iterator>::iterator i = iterators.begin();
+ i != iterators.end();
+ ++i) {
+ ceph_assert(!i->second->valid());
+ }
+ }
+ return 0;
+}
+
+void *write(void *_db) {
+ KeyValueDB *db = static_cast<KeyValueDB*>(_db);
+ std::cout << "Writing..." << std::endl;
+ for (int i = 0; i < 12000; ++i) {
+ if (!(i % 10)) {
+ std::cout << "Iteration: " << i << std::endl;
+ }
+ int key_num = rand();
+ stringstream key;
+ key << key_num << std::endl;
+ map<string, bufferlist> to_set;
+ stringstream val;
+ val << i << std::endl;
+ bufferptr bp(val.str().c_str(), val.str().size() + 1);
+ to_set[key.str()].push_back(bp);
+
+ KeyValueDB::Transaction t = db->get_transaction();
+ for (int j = 0; j < NUM_COPIES; ++j) {
+ t->set(prefix_gen(j), to_set);
+ }
+ ceph_assert(!db->submit_transaction(t));
+ }
+ return 0;
+}
+
+int main() {
+ char *path = getenv("OBJECT_MAP_PATH");
+ boost::scoped_ptr< KeyValueDB > db;
+ if (!path) {
+ std::cerr << "No path found, OBJECT_MAP_PATH undefined" << std::endl;
+ return 0;
+ }
+ string strpath(path);
+ std::cerr << "Using path: " << strpath << std::endl;
+ KeyValueDB *store = KeyValueDB::create(g_ceph_context, "leveldb", strpath);
+ ceph_assert(!store->create_and_open(std::cerr));
+ db.reset(store);
+
+ verify(db.get());
+
+ vector<pthread_t> threads(NUM_THREADS);
+ for (vector<pthread_t>::iterator i = threads.begin();
+ i != threads.end();
+ ++i) {
+ pthread_create(&*i, 0, &write, static_cast<void *>(db.get()));
+ }
+ for (vector<pthread_t>::iterator i = threads.begin();
+ i != threads.end();
+ ++i) {
+ void *tmp;
+ pthread_join(*i, &tmp);
+ }
+ verify(db.get());
+}
diff --git a/src/test/ObjectMap/test_keyvaluedb_iterators.cc b/src/test/ObjectMap/test_keyvaluedb_iterators.cc
new file mode 100644
index 000000000..061639ad9
--- /dev/null
+++ b/src/test/ObjectMap/test_keyvaluedb_iterators.cc
@@ -0,0 +1,1756 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2012 Inktank, Inc.
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*/
+#include <map>
+#include <set>
+#include <deque>
+#include <boost/scoped_ptr.hpp>
+
+#include "test/ObjectMap/KeyValueDBMemory.h"
+#include "kv/KeyValueDB.h"
+#include <sys/types.h>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+string store_path;
+
+class IteratorTest : public ::testing::Test
+{
+public:
+ boost::scoped_ptr<KeyValueDB> db;
+ boost::scoped_ptr<KeyValueDBMemory> mock;
+
+ void SetUp() override {
+ ceph_assert(!store_path.empty());
+
+ KeyValueDB *db_ptr = KeyValueDB::create(g_ceph_context, "leveldb", store_path);
+ ceph_assert(!db_ptr->create_and_open(std::cerr));
+ db.reset(db_ptr);
+ mock.reset(new KeyValueDBMemory());
+ }
+
+ void TearDown() override { }
+
+ ::testing::AssertionResult validate_db_clear(KeyValueDB *store) {
+ KeyValueDB::WholeSpaceIterator it = store->get_wholespace_iterator();
+ it->seek_to_first();
+ while (it->valid()) {
+ pair<string,string> k = it->raw_key();
+ if (mock->db.count(k)) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " mock store count " << mock->db.count(k)
+ << " key(" << k.first << "," << k.second << ")";
+ }
+ it->next();
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+ ::testing::AssertionResult validate_db_match() {
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ it->seek_to_first();
+ while (it->valid()) {
+ pair<string, string> k = it->raw_key();
+ if (!mock->db.count(k)) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " mock db.count() " << mock->db.count(k)
+ << " key(" << k.first << "," << k.second << ")";
+ }
+
+ bufferlist it_bl = it->value();
+ bufferlist mock_bl = mock->db[k];
+
+ string it_val = _bl_to_str(it_bl);
+ string mock_val = _bl_to_str(mock_bl);
+
+ if (it_val != mock_val) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " key(" << k.first << "," << k.second << ")"
+ << " mismatch db value(" << it_val << ")"
+ << " mock value(" << mock_val << ")";
+ }
+ it->next();
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+ ::testing::AssertionResult validate_iterator(
+ KeyValueDB::WholeSpaceIterator it,
+ string expected_prefix,
+ const string &expected_key,
+ const string &expected_value) {
+ if (!it->valid()) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " iterator not valid";
+ }
+
+ if (!it->raw_key_is_prefixed(expected_prefix)) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " expected raw_key_is_prefixed() == TRUE"
+ << " got FALSE";
+ }
+
+ if (it->raw_key_is_prefixed("??__SomeUnexpectedValue__??")) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " expected raw_key_is_prefixed() == FALSE"
+ << " got TRUE";
+ }
+
+ pair<string,string> key = it->raw_key();
+
+ if (expected_prefix != key.first) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " expected prefix '" << expected_prefix << "'"
+ << " got prefix '" << key.first << "'";
+ }
+
+ if (expected_key != it->key()) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " expected key '" << expected_key << "'"
+ << " got key '" << it->key() << "'";
+ }
+
+ if (it->key() != key.second) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " key '" << it->key() << "'"
+ << " does not match"
+ << " pair key '" << key.second << "'";
+ }
+
+ if (_bl_to_str(it->value()) != expected_value) {
+ return ::testing::AssertionFailure()
+ << __func__
+ << " key '(" << key.first << "," << key.second << ")''"
+ << " expected value '" << expected_value << "'"
+ << " got value '" << _bl_to_str(it->value()) << "'";
+ }
+
+ return ::testing::AssertionSuccess();
+ }
+
+ /**
+ * Checks if each key in the queue can be forward sequentially read from
+ * the iterator iter. All keys must be present and be prefixed with prefix,
+ * otherwise the validation will fail.
+ *
+ * Assumes that each key value must be based on the key name and generated
+ * by _gen_val().
+ */
+ void validate_prefix(KeyValueDB::WholeSpaceIterator iter,
+ string &prefix, deque<string> &keys) {
+
+ while (!keys.empty()) {
+ ASSERT_TRUE(iter->valid());
+ string expected_key = keys.front();
+ keys.pop_front();
+ string expected_value = _gen_val_str(expected_key);
+
+ ASSERT_TRUE(validate_iterator(iter, prefix,
+ expected_key, expected_value));
+
+ iter->next();
+ }
+ }
+ /**
+ * Checks if each key in the queue can be backward sequentially read from
+ * the iterator iter. All keys must be present and be prefixed with prefix,
+ * otherwise the validation will fail.
+ *
+ * Assumes that each key value must be based on the key name and generated
+ * by _gen_val().
+ */
+ void validate_prefix_backwards(KeyValueDB::WholeSpaceIterator iter,
+ string &prefix, deque<string> &keys) {
+
+ while (!keys.empty()) {
+ ASSERT_TRUE(iter->valid());
+ string expected_key = keys.front();
+ keys.pop_front();
+ string expected_value = _gen_val_str(expected_key);
+
+ ASSERT_TRUE(validate_iterator(iter, prefix,
+ expected_key, expected_value));
+
+ iter->prev();
+ }
+ }
+
+ void clear(KeyValueDB *store) {
+ KeyValueDB::WholeSpaceIterator it = store->get_wholespace_iterator();
+ it->seek_to_first();
+ KeyValueDB::Transaction t = store->get_transaction();
+ while (it->valid()) {
+ pair<string,string> k = it->raw_key();
+ t->rmkey(k.first, k.second);
+ it->next();
+ }
+ store->submit_transaction_sync(t);
+ }
+
+ string _bl_to_str(bufferlist val) {
+ string str(val.c_str(), val.length());
+ return str;
+ }
+
+ string _gen_val_str(const string &key) {
+ ostringstream ss;
+ ss << "##value##" << key << "##";
+ return ss.str();
+ }
+
+ bufferlist _gen_val(const string &key) {
+ bufferlist bl;
+ bl.append(_gen_val_str(key));
+ return bl;
+ }
+
+ void print_iterator(KeyValueDB::WholeSpaceIterator iter) {
+ if (!iter->valid()) {
+ std::cerr << __func__ << " iterator is not valid; stop." << std::endl;
+ return;
+ }
+
+ int i = 0;
+ while (iter->valid()) {
+ pair<string,string> k = iter->raw_key();
+ std::cerr << __func__
+ << " pos " << (++i)
+ << " key (" << k.first << "," << k.second << ")"
+ << " value(" << _bl_to_str(iter->value()) << ")" << std::endl;
+ iter->next();
+ }
+ }
+
+ void print_db(KeyValueDB *store) {
+ KeyValueDB::WholeSpaceIterator it = store->get_wholespace_iterator();
+ it->seek_to_first();
+ print_iterator(it);
+ }
+};
+
+// ------- Remove Keys / Remove Keys By Prefix -------
+class RmKeysTest : public IteratorTest
+{
+public:
+ string prefix1;
+ string prefix2;
+ string prefix3;
+
+ void init(KeyValueDB *db) {
+ KeyValueDB::Transaction tx = db->get_transaction();
+
+ tx->set(prefix1, "11", _gen_val("11"));
+ tx->set(prefix1, "12", _gen_val("12"));
+ tx->set(prefix1, "13", _gen_val("13"));
+ tx->set(prefix2, "21", _gen_val("21"));
+ tx->set(prefix2, "22", _gen_val("22"));
+ tx->set(prefix2, "23", _gen_val("23"));
+ tx->set(prefix3, "31", _gen_val("31"));
+ tx->set(prefix3, "32", _gen_val("32"));
+ tx->set(prefix3, "33", _gen_val("33"));
+
+ db->submit_transaction_sync(tx);
+ }
+
+ void SetUp() override {
+ IteratorTest::SetUp();
+
+ prefix1 = "_PREFIX_1_";
+ prefix2 = "_PREFIX_2_";
+ prefix3 = "_PREFIX_3_";
+
+ clear(db.get());
+ ASSERT_TRUE(validate_db_clear(db.get()));
+ clear(mock.get());
+ ASSERT_TRUE(validate_db_match());
+
+ init(db.get());
+ init(mock.get());
+
+ ASSERT_TRUE(validate_db_match());
+ }
+
+ void TearDown() override {
+ IteratorTest::TearDown();
+ }
+
+
+ /**
+ * Test the transaction's rmkeys behavior when we remove a given prefix
+ * from the beginning of the key space, or from the end of the key space,
+ * or even simply in the middle.
+ */
+ void RmKeysByPrefix(KeyValueDB *store) {
+ // remove prefix2 ; check if prefix1 remains, and then prefix3
+ KeyValueDB::Transaction tx = store->get_transaction();
+ // remove the prefix in the middle of the key space
+ tx->rmkeys_by_prefix(prefix2);
+ store->submit_transaction_sync(tx);
+
+ deque<string> key_deque;
+ KeyValueDB::WholeSpaceIterator iter = store->get_wholespace_iterator();
+ iter->seek_to_first();
+
+ // check for prefix1
+ key_deque.push_back("11");
+ key_deque.push_back("12");
+ key_deque.push_back("13");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check for prefix3
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("31");
+ key_deque.push_back("32");
+ key_deque.push_back("33");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ASSERT_FALSE(iter->valid());
+
+ clear(store);
+ ASSERT_TRUE(validate_db_clear(store));
+ init(store);
+
+ // remove prefix1 ; check if prefix2 and then prefix3 remain
+ tx = store->get_transaction();
+ // remove the prefix at the beginning of the key space
+ tx->rmkeys_by_prefix(prefix1);
+ store->submit_transaction_sync(tx);
+
+ iter = store->get_wholespace_iterator();
+ iter->seek_to_first();
+
+ // check for prefix2
+ key_deque.clear();
+ key_deque.push_back("21");
+ key_deque.push_back("22");
+ key_deque.push_back("23");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check for prefix3
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("31");
+ key_deque.push_back("32");
+ key_deque.push_back("33");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ASSERT_FALSE(iter->valid());
+
+ clear(store);
+ ASSERT_TRUE(validate_db_clear(store));
+ init(store);
+
+ // remove prefix3 ; check if prefix1 and then prefix2 remain
+ tx = store->get_transaction();
+ // remove the prefix at the end of the key space
+ tx->rmkeys_by_prefix(prefix3);
+ store->submit_transaction_sync(tx);
+
+ iter = store->get_wholespace_iterator();
+ iter->seek_to_first();
+
+ // check for prefix1
+ key_deque.clear();
+ key_deque.push_back("11");
+ key_deque.push_back("12");
+ key_deque.push_back("13");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check for prefix2
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("21");
+ key_deque.push_back("22");
+ key_deque.push_back("23");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ASSERT_FALSE(iter->valid());
+ }
+
+ /**
+ * Test how the leveldb's whole-space iterator behaves when we remove
+ * keys from the store while iterating over them.
+ */
+ void RmKeysWhileIteratingSnapshot(KeyValueDB *store,
+ KeyValueDB::WholeSpaceIterator iter) {
+
+ SCOPED_TRACE("RmKeysWhileIteratingSnapshot");
+
+ iter->seek_to_first();
+ ASSERT_TRUE(iter->valid());
+
+ KeyValueDB::Transaction t = store->get_transaction();
+ t->rmkey(prefix1, "11");
+ t->rmkey(prefix1, "12");
+ t->rmkey(prefix2, "23");
+ t->rmkey(prefix3, "33");
+ store->submit_transaction_sync(t);
+
+ deque<string> key_deque;
+
+ // check for prefix1
+ key_deque.push_back("11");
+ key_deque.push_back("12");
+ key_deque.push_back("13");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check for prefix2
+ key_deque.clear();
+ key_deque.push_back("21");
+ key_deque.push_back("22");
+ key_deque.push_back("23");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check for prefix3
+ key_deque.clear();
+ key_deque.push_back("31");
+ key_deque.push_back("32");
+ key_deque.push_back("33");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ iter->next();
+ ASSERT_FALSE(iter->valid());
+
+ // make sure those keys were removed from the store
+ KeyValueDB::WholeSpaceIterator tmp_it = store->get_wholespace_iterator();
+ tmp_it->seek_to_first();
+ ASSERT_TRUE(tmp_it->valid());
+
+ key_deque.clear();
+ key_deque.push_back("13");
+ validate_prefix(tmp_it, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ASSERT_TRUE(tmp_it->valid());
+ key_deque.clear();
+ key_deque.push_back("21");
+ key_deque.push_back("22");
+ validate_prefix(tmp_it, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ASSERT_TRUE(tmp_it->valid());
+ key_deque.clear();
+ key_deque.push_back("31");
+ key_deque.push_back("32");
+ validate_prefix(tmp_it, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ASSERT_FALSE(tmp_it->valid());
+ }
+};
+
+TEST_F(RmKeysTest, RmKeysByPrefixLevelDB)
+{
+ SCOPED_TRACE("LevelDB");
+ RmKeysByPrefix(db.get());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(RmKeysTest, RmKeysByPrefixMockDB)
+{
+ SCOPED_TRACE("Mock DB");
+ RmKeysByPrefix(mock.get());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+/**
+ * If you refer to function RmKeysTest::RmKeysWhileIteratingSnapshot(),
+ * you will notice that we seek the iterator to the first key, and then
+ * we go on to remove several keys from the underlying store, including
+ * the first couple keys.
+ *
+ * We would expect that during this test, as soon as we removed the keys
+ * from the store, the iterator would get invalid, or cause some sort of
+ * unexpected mess.
+ *
+ * Instead, the current version of leveldb handles it perfectly, by making
+ * the iterator to use a snapshot instead of the store's real state. This
+ * way, LevelDBStore's whole-space iterator will behave much like its own
+ * whole-space snapshot iterator.
+ *
+ * However, this particular behavior of the iterator hasn't been documented
+ * on leveldb, and we should assume that it can be changed at any point in
+ * time.
+ *
+ * Therefore, we keep this test, being exactly the same as the one for the
+ * whole-space snapshot iterator, as we currently assume they should behave
+ * identically. If this test fails, at some point, and the whole-space
+ * snapshot iterator passes, then it probably means that leveldb changed
+ * how its iterator behaves.
+ */
+TEST_F(RmKeysTest, RmKeysWhileIteratingLevelDB)
+{
+ SCOPED_TRACE("LevelDB -- WholeSpaceIterator");
+ RmKeysWhileIteratingSnapshot(db.get(), db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(RmKeysTest, RmKeysWhileIteratingMockDB)
+{
+ std::cout << "There is no safe way to test key removal while iterating\n"
+ << "over the mock store without using snapshots" << std::endl;
+}
+
+// ------- Set Keys / Update Values -------
+class SetKeysTest : public IteratorTest
+{
+public:
+ string prefix1;
+ string prefix2;
+
+ void init(KeyValueDB *db) {
+ KeyValueDB::Transaction tx = db->get_transaction();
+
+ tx->set(prefix1, "aaa", _gen_val("aaa"));
+ tx->set(prefix1, "ccc", _gen_val("ccc"));
+ tx->set(prefix1, "eee", _gen_val("eee"));
+ tx->set(prefix2, "vvv", _gen_val("vvv"));
+ tx->set(prefix2, "xxx", _gen_val("xxx"));
+ tx->set(prefix2, "zzz", _gen_val("zzz"));
+
+ db->submit_transaction_sync(tx);
+ }
+
+ void SetUp() override {
+ IteratorTest::SetUp();
+
+ prefix1 = "_PREFIX_1_";
+ prefix2 = "_PREFIX_2_";
+
+ clear(db.get());
+ ASSERT_TRUE(validate_db_clear(db.get()));
+ clear(mock.get());
+ ASSERT_TRUE(validate_db_match());
+
+ init(db.get());
+ init(mock.get());
+
+ ASSERT_TRUE(validate_db_match());
+ }
+
+ void TearDown() override {
+ IteratorTest::TearDown();
+ }
+
+ /**
+ * Make sure that the iterator picks on new keys added if it hasn't yet
+ * iterated away from that position.
+ *
+ * This should only happen for the whole-space iterator when not using
+ * the snapshot version.
+ *
+ * We don't need to test the validity of all elements, but we do test
+ * inserting while moving from the first element to the last, using next()
+ * to move forward, and then we test the same behavior while iterating
+ * from the last element to the first, using prev() to move backwards.
+ */
+ void SetKeysWhileIterating(KeyValueDB *store,
+ KeyValueDB::WholeSpaceIterator iter) {
+ iter->seek_to_first();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa",
+ _gen_val_str("aaa")));
+ iter->next();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1, "ccc",
+ _bl_to_str(_gen_val("ccc"))));
+
+ // insert new key 'ddd' after 'ccc' and before 'eee'
+ KeyValueDB::Transaction tx = store->get_transaction();
+ tx->set(prefix1, "ddd", _gen_val("ddd"));
+ store->submit_transaction_sync(tx);
+
+ iter->next();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1, "ddd",
+ _gen_val_str("ddd")));
+
+ iter->seek_to_last();
+ ASSERT_TRUE(iter->valid());
+ tx = store->get_transaction();
+ tx->set(prefix2, "yyy", _gen_val("yyy"));
+ store->submit_transaction_sync(tx);
+
+ iter->prev();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix2,
+ "yyy", _gen_val_str("yyy")));
+ }
+
+ /**
+ * Make sure that the whole-space snapshot iterator does not pick on new keys
+ * added to the store since we created the iterator, thus guaranteeing
+ * read-consistency.
+ *
+ * We don't need to test the validity of all elements, but we do test
+ * inserting while moving from the first element to the last, using next()
+ * to move forward, and then we test the same behavior while iterating
+ * from the last element to the first, using prev() to move backwards.
+ */
+ void SetKeysWhileIteratingSnapshot(KeyValueDB *store,
+ KeyValueDB::WholeSpaceIterator iter) {
+ iter->seek_to_first();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa",
+ _gen_val_str("aaa")));
+ iter->next();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1, "ccc",
+ _bl_to_str(_gen_val("ccc"))));
+
+ // insert new key 'ddd' after 'ccc' and before 'eee'
+ KeyValueDB::Transaction tx = store->get_transaction();
+ tx->set(prefix1, "ddd", _gen_val("ddd"));
+ store->submit_transaction_sync(tx);
+
+ iter->next();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1, "eee",
+ _gen_val_str("eee")));
+
+ iter->seek_to_last();
+ ASSERT_TRUE(iter->valid());
+ tx = store->get_transaction();
+ tx->set(prefix2, "yyy", _gen_val("yyy"));
+ store->submit_transaction_sync(tx);
+
+ iter->prev();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix2,
+ "xxx", _gen_val_str("xxx")));
+ }
+
+ /**
+ * Make sure that the whole-space iterator is able to read values changed on
+ * the store, even after we moved to the updated position.
+ *
+ * This should only be possible when not using the whole-space snapshot
+ * version of the iterator.
+ */
+ void UpdateValuesWhileIterating(KeyValueDB *store,
+ KeyValueDB::WholeSpaceIterator iter) {
+ iter->seek_to_first();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1,
+ "aaa", _gen_val_str("aaa")));
+
+ KeyValueDB::Transaction tx = store->get_transaction();
+ tx->set(prefix1, "aaa", _gen_val("aaa_1"));
+ store->submit_transaction_sync(tx);
+
+ ASSERT_TRUE(validate_iterator(iter, prefix1,
+ "aaa", _gen_val_str("aaa_1")));
+
+ iter->seek_to_last();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix2,
+ "zzz", _gen_val_str("zzz")));
+
+ tx = store->get_transaction();
+ tx->set(prefix2, "zzz", _gen_val("zzz_1"));
+ store->submit_transaction_sync(tx);
+
+ ASSERT_TRUE(validate_iterator(iter, prefix2,
+ "zzz", _gen_val_str("zzz_1")));
+ }
+
+ /**
+ * Make sure that the whole-space iterator is able to read values changed on
+ * the store, even after we moved to the updated position.
+ *
+ * This should only be possible when not using the whole-space snapshot
+ * version of the iterator.
+ */
+ void UpdateValuesWhileIteratingSnapshot(
+ KeyValueDB *store,
+ KeyValueDB::WholeSpaceIterator iter) {
+ iter->seek_to_first();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix1,
+ "aaa", _gen_val_str("aaa")));
+
+ KeyValueDB::Transaction tx = store->get_transaction();
+ tx->set(prefix1, "aaa", _gen_val("aaa_1"));
+ store->submit_transaction_sync(tx);
+
+ ASSERT_TRUE(validate_iterator(iter, prefix1,
+ "aaa", _gen_val_str("aaa")));
+
+ iter->seek_to_last();
+ ASSERT_TRUE(iter->valid());
+ ASSERT_TRUE(validate_iterator(iter, prefix2,
+ "zzz", _gen_val_str("zzz")));
+
+ tx = store->get_transaction();
+ tx->set(prefix2, "zzz", _gen_val("zzz_1"));
+ store->submit_transaction_sync(tx);
+
+ ASSERT_TRUE(validate_iterator(iter, prefix2,
+ "zzz", _gen_val_str("zzz")));
+
+ // check those values were really changed in the store
+ KeyValueDB::WholeSpaceIterator tmp_iter = store->get_wholespace_iterator();
+ tmp_iter->seek_to_first();
+ ASSERT_TRUE(tmp_iter->valid());
+ ASSERT_TRUE(validate_iterator(tmp_iter, prefix1,
+ "aaa", _gen_val_str("aaa_1")));
+ tmp_iter->seek_to_last();
+ ASSERT_TRUE(tmp_iter->valid());
+ ASSERT_TRUE(validate_iterator(tmp_iter, prefix2,
+ "zzz", _gen_val_str("zzz_1")));
+ }
+
+
+};
+
+TEST_F(SetKeysTest, DISABLED_SetKeysWhileIteratingLevelDB)
+{
+ SCOPED_TRACE("LevelDB: SetKeysWhileIteratingLevelDB");
+ SetKeysWhileIterating(db.get(), db->get_wholespace_iterator());
+ ASSERT_TRUE(HasFatalFailure());
+}
+
+TEST_F(SetKeysTest, SetKeysWhileIteratingMockDB)
+{
+ SCOPED_TRACE("Mock DB: SetKeysWhileIteratingMockDB");
+ SetKeysWhileIterating(mock.get(), mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SetKeysTest, DISABLED_UpdateValuesWhileIteratingLevelDB)
+{
+ SCOPED_TRACE("LevelDB: UpdateValuesWhileIteratingLevelDB");
+ UpdateValuesWhileIterating(db.get(), db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SetKeysTest, UpdateValuesWhileIteratingMockDB)
+{
+ SCOPED_TRACE("MockDB: UpdateValuesWhileIteratingMockDB");
+ UpdateValuesWhileIterating(mock.get(), mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+class BoundsTest : public IteratorTest
+{
+public:
+ string prefix1;
+ string prefix2;
+ string prefix3;
+
+ void init(KeyValueDB *store) {
+ KeyValueDB::Transaction tx = store->get_transaction();
+
+ tx->set(prefix1, "aaa", _gen_val("aaa"));
+ tx->set(prefix1, "ccc", _gen_val("ccc"));
+ tx->set(prefix1, "eee", _gen_val("eee"));
+ tx->set(prefix2, "vvv", _gen_val("vvv"));
+ tx->set(prefix2, "xxx", _gen_val("xxx"));
+ tx->set(prefix2, "zzz", _gen_val("zzz"));
+ tx->set(prefix3, "aaa", _gen_val("aaa"));
+ tx->set(prefix3, "mmm", _gen_val("mmm"));
+ tx->set(prefix3, "yyy", _gen_val("yyy"));
+
+ store->submit_transaction_sync(tx);
+ }
+
+ void SetUp() override {
+ IteratorTest::SetUp();
+
+ prefix1 = "_PREFIX_1_";
+ prefix2 = "_PREFIX_2_";
+ prefix3 = "_PREFIX_4_";
+
+ clear(db.get());
+ ASSERT_TRUE(validate_db_clear(db.get()));
+ clear(mock.get());
+ ASSERT_TRUE(validate_db_match());
+
+ init(db.get());
+ init(mock.get());
+
+ ASSERT_TRUE(validate_db_match());
+ }
+
+ void TearDown() override {
+ IteratorTest::TearDown();
+ }
+
+ void LowerBoundWithEmptyKeyOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ // see what happens when we have an empty key and try to get to the
+ // first available prefix
+ iter->lower_bound(prefix1, "");
+ ASSERT_TRUE(iter->valid());
+
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ // if we got here without problems, then it is safe to assume the
+ // remaining prefixes are intact.
+
+ // see what happens when we have an empty key and try to get to the
+ // middle of the key-space
+ iter->lower_bound(prefix2, "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+
+ key_deque.push_back("vvv");
+ key_deque.push_back("xxx");
+ key_deque.push_back("zzz");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ // if we got here without problems, then it is safe to assume the
+ // remaining prefixes are intact.
+
+ // see what happens when we have an empty key and try to get to the
+ // last prefix on the key-space
+ iter->lower_bound(prefix3, "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+
+ key_deque.push_back("aaa");
+ key_deque.push_back("mmm");
+ key_deque.push_back("yyy");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+ // we reached the end of the key_space, so the iterator should no longer
+ // be valid
+
+ // see what happens when we look for an inexistent prefix, that will
+ // compare higher than the existing prefixes, with an empty key
+ // expected: reach the store's end; iterator becomes invalid
+ iter->lower_bound("_PREFIX_9_", "");
+ ASSERT_FALSE(iter->valid());
+
+ // see what happens when we look for an inexistent prefix, that will
+ // compare lower than the existing prefixes, with an empty key
+ // expected: find the first prefix; iterator is valid
+ iter->lower_bound("_PREFIX_0_", "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // see what happens when we look for an empty prefix (that should compare
+ // lower than any existing prefixes)
+ // expected: find the first prefix; iterator is valid
+ iter->lower_bound("", "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ }
+
+ void LowerBoundWithEmptyPrefixOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ // check for an empty prefix, with key 'aaa'. Since this key is shared
+ // among two different prefixes, it is relevant to check which will be
+ // found first.
+ // expected: find key (prefix1, aaa); iterator is valid
+ iter->lower_bound("", "aaa");
+ ASSERT_TRUE(iter->valid());
+
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ // since we found prefix1, it is safe to assume that the remaining
+ // prefixes (prefix2 and prefix3) will follow
+
+ // any lower_bound operation with an empty prefix should always put the
+ // iterator in the first key in the key-space, despite what key is
+ // specified. This means that looking for ("","AAAAAAAAAA") should
+ // also position the iterator on (prefix1, aaa).
+ // expected: find key (prefix1, aaa); iterator is valid
+ iter->lower_bound("", "AAAAAAAAAA");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // note: this test is a duplicate of the one in the function above. Why?
+ // Well, because it also fits here (being its prefix empty), and one could
+ // very well run solely this test (instead of the whole battery) and would
+ // certainly expect this case to be tested.
+
+ // see what happens when we look for an empty prefix (that should compare
+ // lower than any existing prefixes)
+ // expected: find the first prefix; iterator is valid
+ iter->lower_bound("", "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ }
+
+ void LowerBoundOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ // check that we find the first key in the store
+ // expected: find (prefix1, aaa); iterator is valid
+ iter->lower_bound(prefix1, "aaa");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // check that we find the last key in the store
+ // expected: find (prefix3, yyy); iterator is valid
+ iter->lower_bound(prefix3, "yyy");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("yyy");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+
+ // check that looking for non-existent prefix '_PREFIX_0_' will
+ // always result in the first value of prefix1 (prefix1,"aaa")
+ // expected: find (prefix1, aaa); iterator is valid
+ iter->lower_bound("_PREFIX_0_", "AAAAA");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // check that looking for non-existent prefix '_PREFIX_3_' will
+ // always result in the first value of prefix3 (prefix4,"aaa")
+ // expected: find (prefix3, aaa); iterator is valid
+ iter->lower_bound("_PREFIX_3_", "AAAAA");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // check that looking for non-existent prefix '_PREFIX_9_' will
+ // always result in an invalid iterator.
+ // expected: iterator is invalid
+ iter->lower_bound("_PREFIX_9_", "AAAAA");
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void UpperBoundWithEmptyKeyOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ // check that looking for (prefix1, "") will result in finding
+ // the first key in prefix1 (prefix1, "aaa")
+ // expected: find (prefix1, aaa); iterator is valid
+ iter->upper_bound(prefix1, "");
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // check that looking for (prefix2, "") will result in finding
+ // the first key in prefix2 (prefix2, vvv)
+ // expected: find (prefix2, aaa); iterator is valid
+ iter->upper_bound(prefix2, "");
+ key_deque.push_back("vvv");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+
+ // check that looking for (prefix3, "") will result in finding
+ // the first key in prefix3 (prefix3, aaa)
+ // expected: find (prefix3, aaa); iterator is valid
+ iter->upper_bound(prefix3, "");
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // see what happens when we look for an inexistent prefix, that will
+ // compare higher than the existing prefixes, with an empty key
+ // expected: reach the store's end; iterator becomes invalid
+ iter->upper_bound("_PREFIX_9_", "");
+ ASSERT_FALSE(iter->valid());
+
+ // see what happens when we look for an inexistent prefix, that will
+ // compare lower than the existing prefixes, with an empty key
+ // expected: find the first prefix; iterator is valid
+ iter->upper_bound("_PREFIX_0_", "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // see what happens when we look for an empty prefix (that should compare
+ // lower than any existing prefixes)
+ // expected: find the first prefix; iterator is valid
+ iter->upper_bound("", "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ }
+
+ void UpperBoundWithEmptyPrefixOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ // check for an empty prefix, with key 'aaa'. Since this key is shared
+ // among two different prefixes, it is relevant to check which will be
+ // found first.
+ // expected: find key (prefix1, aaa); iterator is valid
+ iter->upper_bound("", "aaa");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // any upper_bound operation with an empty prefix should always put the
+ // iterator in the first key whose prefix compares greater, despite the
+ // key that is specified. This means that looking for ("","AAAAAAAAAA")
+ // should position the iterator on (prefix1, aaa).
+ // expected: find key (prefix1, aaa); iterator is valid
+ iter->upper_bound("", "AAAAAAAAAA");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // note: this test is a duplicate of the one in the function above. Why?
+ // Well, because it also fits here (being its prefix empty), and one could
+ // very well run solely this test (instead of the whole battery) and would
+ // certainly expect this case to be tested.
+
+ // see what happens when we look for an empty prefix (that should compare
+ // lower than any existing prefixes)
+ // expected: find the first prefix; iterator is valid
+ iter->upper_bound("", "");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ }
+
+ void UpperBoundOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ // check that we find the second key in the store
+ // expected: find (prefix1, ccc); iterator is valid
+ iter->upper_bound(prefix1, "bbb");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("ccc");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // check that we find the last key in the store
+ // expected: find (prefix3, yyy); iterator is valid
+ iter->upper_bound(prefix3, "xxx");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("yyy");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+
+ // check that looking for non-existent prefix '_PREFIX_0_' will
+ // always result in the first value of prefix1 (prefix1,"aaa")
+ // expected: find (prefix1, aaa); iterator is valid
+ iter->upper_bound("_PREFIX_0_", "AAAAA");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // check that looking for non-existent prefix '_PREFIX_3_' will
+ // always result in the first value of prefix3 (prefix3,"aaa")
+ // expected: find (prefix3, aaa); iterator is valid
+ iter->upper_bound("_PREFIX_3_", "AAAAA");
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix3, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // check that looking for non-existent prefix '_PREFIX_9_' will
+ // always result in an invalid iterator.
+ // expected: iterator is invalid
+ iter->upper_bound("_PREFIX_9_", "AAAAA");
+ ASSERT_FALSE(iter->valid());
+ }
+};
+
+TEST_F(BoundsTest, LowerBoundWithEmptyKeyOnWholeSpaceIteratorLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Lower Bound, Empty Key, Whole-Space Iterator");
+ LowerBoundWithEmptyKeyOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundWithEmptyKeyOnWholeSpaceIteratorMockDB)
+{
+ SCOPED_TRACE("MockDB: Lower Bound, Empty Key, Whole-Space Iterator");
+ LowerBoundWithEmptyKeyOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundWithEmptyPrefixOnWholeSpaceIteratorLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Lower Bound, Empty Prefix, Whole-Space Iterator");
+ LowerBoundWithEmptyPrefixOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundWithEmptyPrefixOnWholeSpaceIteratorMockDB)
+{
+ SCOPED_TRACE("MockDB: Lower Bound, Empty Prefix, Whole-Space Iterator");
+ LowerBoundWithEmptyPrefixOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundOnWholeSpaceIteratorLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Lower Bound, Whole-Space Iterator");
+ LowerBoundOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundOnWholeSpaceIteratorMockDB)
+{
+ SCOPED_TRACE("MockDB: Lower Bound, Whole-Space Iterator");
+ LowerBoundOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyKeyOnWholeSpaceIteratorLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Upper Bound, Empty Key, Whole-Space Iterator");
+ UpperBoundWithEmptyKeyOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyKeyOnWholeSpaceIteratorMockDB)
+{
+ SCOPED_TRACE("MockDB: Upper Bound, Empty Key, Whole-Space Iterator");
+ UpperBoundWithEmptyKeyOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyPrefixOnWholeSpaceIteratorLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Upper Bound, Empty Prefix, Whole-Space Iterator");
+ UpperBoundWithEmptyPrefixOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyPrefixOnWholeSpaceIteratorMockDB)
+{
+ SCOPED_TRACE("MockDB: Upper Bound, Empty Prefix, Whole-Space Iterator");
+ UpperBoundWithEmptyPrefixOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundOnWholeSpaceIteratorLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Upper Bound, Whole-Space Iterator");
+ UpperBoundOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundOnWholeSpaceIteratorMockDB)
+{
+ SCOPED_TRACE("MockDB: Upper Bound, Whole-Space Iterator");
+ UpperBoundOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+
+class SeeksTest : public IteratorTest
+{
+public:
+ string prefix0;
+ string prefix1;
+ string prefix2;
+ string prefix3;
+ string prefix4;
+ string prefix5;
+
+ void init(KeyValueDB *store) {
+ KeyValueDB::Transaction tx = store->get_transaction();
+
+ tx->set(prefix1, "aaa", _gen_val("aaa"));
+ tx->set(prefix1, "ccc", _gen_val("ccc"));
+ tx->set(prefix1, "eee", _gen_val("eee"));
+ tx->set(prefix2, "vvv", _gen_val("vvv"));
+ tx->set(prefix2, "xxx", _gen_val("xxx"));
+ tx->set(prefix2, "zzz", _gen_val("zzz"));
+ tx->set(prefix4, "aaa", _gen_val("aaa"));
+ tx->set(prefix4, "mmm", _gen_val("mmm"));
+ tx->set(prefix4, "yyy", _gen_val("yyy"));
+
+ store->submit_transaction_sync(tx);
+ }
+
+ void SetUp() override {
+ IteratorTest::SetUp();
+
+ prefix0 = "_PREFIX_0_";
+ prefix1 = "_PREFIX_1_";
+ prefix2 = "_PREFIX_2_";
+ prefix3 = "_PREFIX_3_";
+ prefix4 = "_PREFIX_4_";
+ prefix5 = "_PREFIX_5_";
+
+ clear(db.get());
+ ASSERT_TRUE(validate_db_clear(db.get()));
+ clear(mock.get());
+ ASSERT_TRUE(validate_db_match());
+
+ init(db.get());
+ init(mock.get());
+
+ ASSERT_TRUE(validate_db_match());
+ }
+
+ void TearDown() override {
+ IteratorTest::TearDown();
+ }
+
+
+ void SeekToFirstOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ iter->seek_to_first();
+ ASSERT_TRUE(iter->valid());
+ deque<string> key_deque;
+ key_deque.push_back("aaa");
+ key_deque.push_back("ccc");
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ }
+
+ void SeekToFirstWithPrefixOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+
+ // if the prefix is empty, we must end up seeking to the first key.
+ // expected: seek to (prefix1, aaa); iterator is valid
+ iter->seek_to_first("");
+ ASSERT_TRUE(iter->valid());
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to non-existent prefix that compares lower than the
+ // first available prefix
+ // expected: seek to (prefix1, aaa); iterator is valid
+ iter->seek_to_first(prefix0);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to non-existent prefix
+ // expected: seek to (prefix4, aaa); iterator is valid
+ iter->seek_to_first(prefix3);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix4, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to non-existent prefix that compares greater than the
+ // last available prefix
+ // expected: iterator is invalid
+ iter->seek_to_first(prefix5);
+ ASSERT_FALSE(iter->valid());
+
+ // try seeking to the first prefix and make sure we end up in its first
+ // position
+ // expected: seek to (prefix1,aaa); iterator is valid
+ iter->seek_to_first(prefix1);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to the second prefix and make sure we end up in its
+ // first position
+ // expected: seek to (prefix2,vvv); iterator is valid
+ iter->seek_to_first(prefix2);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("vvv");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to the last prefix and make sure we end up in its
+ // first position
+ // expected: seek to (prefix4,aaa); iterator is valid
+ iter->seek_to_first(prefix4);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("aaa");
+ validate_prefix(iter, prefix4, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+ }
+
+ void SeekToLastOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ iter->seek_to_last();
+ key_deque.push_back("yyy");
+ validate_prefix(iter, prefix4, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void SeekToLastWithPrefixOnWholeSpaceIterator(
+ KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+
+ // if the prefix is empty, we must end up seeking to last position
+ // that has an empty prefix, or to the previous position to the first
+ // position whose prefix compares higher than empty.
+ // expected: iterator is invalid (because (prefix1,aaa) is the first
+ // position that compared higher than an empty prefix)
+ iter->seek_to_last("");
+ ASSERT_FALSE(iter->valid());
+
+ // try seeking to non-existent prefix that compares lower than the
+ // first available prefix
+ // expected: iterator is invalid (because (prefix1,aaa) is the first
+ // position that compared higher than prefix0)
+ iter->seek_to_last(prefix0);
+ ASSERT_FALSE(iter->valid());
+
+ // try seeking to non-existent prefix
+ // expected: seek to (prefix2, zzz); iterator is valid
+ iter->seek_to_last(prefix3);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("zzz");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to non-existent prefix that compares greater than the
+ // last available prefix
+ // expected: iterator is in the last position of the store;
+ // i.e., (prefix4,yyy)
+ iter->seek_to_last(prefix5);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("yyy");
+ validate_prefix(iter, prefix4, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+
+ // try seeking to the first prefix and make sure we end up in its last
+ // position
+ // expected: seek to (prefix1,eee); iterator is valid
+ iter->seek_to_last(prefix1);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("eee");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to the second prefix and make sure we end up in its
+ // last position
+ // expected: seek to (prefix2,vvv); iterator is valid
+ iter->seek_to_last(prefix2);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("zzz");
+ validate_prefix(iter, prefix2, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_TRUE(iter->valid());
+
+ // try seeking to the last prefix and make sure we end up in its
+ // last position
+ // expected: seek to (prefix4,aaa); iterator is valid
+ iter->seek_to_last(prefix4);
+ ASSERT_TRUE(iter->valid());
+ key_deque.clear();
+ key_deque.push_back("yyy");
+ validate_prefix(iter, prefix4, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+ }
+};
+
+TEST_F(SeeksTest, SeekToFirstOnWholeSpaceIteratorLevelDB) {
+ SCOPED_TRACE("LevelDB: Seek To First, Whole Space Iterator");
+ SeekToFirstOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToFirstOnWholeSpaceIteratorMockDB) {
+ SCOPED_TRACE("MockDB: Seek To First, Whole Space Iterator");
+ SeekToFirstOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToFirstWithPrefixOnWholeSpaceIteratorLevelDB) {
+ SCOPED_TRACE("LevelDB: Seek To First, With Prefix, Whole Space Iterator");
+ SeekToFirstWithPrefixOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToFirstWithPrefixOnWholeSpaceIteratorMockDB) {
+ SCOPED_TRACE("MockDB: Seek To First, With Prefix, Whole Space Iterator");
+ SeekToFirstWithPrefixOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastOnWholeSpaceIteratorLevelDB) {
+ SCOPED_TRACE("LevelDB: Seek To Last, Whole Space Iterator");
+ SeekToLastOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastOnWholeSpaceIteratorMockDB) {
+ SCOPED_TRACE("MockDB: Seek To Last, Whole Space Iterator");
+ SeekToLastOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastWithPrefixOnWholeSpaceIteratorLevelDB) {
+ SCOPED_TRACE("LevelDB: Seek To Last, With Prefix, Whole Space Iterator");
+ SeekToLastWithPrefixOnWholeSpaceIterator(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastWithPrefixOnWholeSpaceIteratorMockDB) {
+ SCOPED_TRACE("MockDB: Seek To Last, With Prefix, Whole Space Iterator");
+ SeekToLastWithPrefixOnWholeSpaceIterator(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+class KeySpaceIteration : public IteratorTest
+{
+public:
+ string prefix1;
+
+ void init(KeyValueDB *store) {
+ KeyValueDB::Transaction tx = store->get_transaction();
+
+ tx->set(prefix1, "aaa", _gen_val("aaa"));
+ tx->set(prefix1, "vvv", _gen_val("vvv"));
+ tx->set(prefix1, "zzz", _gen_val("zzz"));
+
+ store->submit_transaction_sync(tx);
+ }
+
+ void SetUp() override {
+ IteratorTest::SetUp();
+
+ prefix1 = "_PREFIX_1_";
+
+ clear(db.get());
+ ASSERT_TRUE(validate_db_clear(db.get()));
+ clear(mock.get());
+ ASSERT_TRUE(validate_db_match());
+
+ init(db.get());
+ init(mock.get());
+
+ ASSERT_TRUE(validate_db_match());
+ }
+
+ void TearDown() override {
+ IteratorTest::TearDown();
+ }
+
+ void ForwardIteration(KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ iter->seek_to_first();
+ key_deque.push_back("aaa");
+ key_deque.push_back("vvv");
+ key_deque.push_back("zzz");
+ validate_prefix(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void BackwardIteration(KeyValueDB::WholeSpaceIterator iter) {
+ deque<string> key_deque;
+ iter->seek_to_last();
+ key_deque.push_back("zzz");
+ key_deque.push_back("vvv");
+ key_deque.push_back("aaa");
+ validate_prefix_backwards(iter, prefix1, key_deque);
+ ASSERT_FALSE(HasFatalFailure());
+ ASSERT_FALSE(iter->valid());
+ }
+};
+
+TEST_F(KeySpaceIteration, ForwardIterationLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Forward Iteration, Whole Space Iterator");
+ ForwardIteration(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(KeySpaceIteration, ForwardIterationMockDB) {
+ SCOPED_TRACE("MockDB: Forward Iteration, Whole Space Iterator");
+ ForwardIteration(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(KeySpaceIteration, BackwardIterationLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Backward Iteration, Whole Space Iterator");
+ BackwardIteration(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(KeySpaceIteration, BackwardIterationMockDB) {
+ SCOPED_TRACE("MockDB: Backward Iteration, Whole Space Iterator");
+ BackwardIteration(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+class EmptyStore : public IteratorTest
+{
+public:
+ void SetUp() override {
+ IteratorTest::SetUp();
+
+ clear(db.get());
+ ASSERT_TRUE(validate_db_clear(db.get()));
+ clear(mock.get());
+ ASSERT_TRUE(validate_db_match());
+ }
+
+ void SeekToFirst(KeyValueDB::WholeSpaceIterator iter) {
+ // expected: iterator is invalid
+ iter->seek_to_first();
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void SeekToFirstWithPrefix(KeyValueDB::WholeSpaceIterator iter) {
+ // expected: iterator is invalid
+ iter->seek_to_first("prefix");
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void SeekToLast(KeyValueDB::WholeSpaceIterator iter) {
+ // expected: iterator is invalid
+ iter->seek_to_last();
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void SeekToLastWithPrefix(KeyValueDB::WholeSpaceIterator iter) {
+ // expected: iterator is invalid
+ iter->seek_to_last("prefix");
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void LowerBound(KeyValueDB::WholeSpaceIterator iter) {
+ // expected: iterator is invalid
+ iter->lower_bound("prefix", "");
+ ASSERT_FALSE(iter->valid());
+
+ // expected: iterator is invalid
+ iter->lower_bound("", "key");
+ ASSERT_FALSE(iter->valid());
+
+ // expected: iterator is invalid
+ iter->lower_bound("prefix", "key");
+ ASSERT_FALSE(iter->valid());
+ }
+
+ void UpperBound(KeyValueDB::WholeSpaceIterator iter) {
+ // expected: iterator is invalid
+ iter->upper_bound("prefix", "");
+ ASSERT_FALSE(iter->valid());
+
+ // expected: iterator is invalid
+ iter->upper_bound("", "key");
+ ASSERT_FALSE(iter->valid());
+
+ // expected: iterator is invalid
+ iter->upper_bound("prefix", "key");
+ ASSERT_FALSE(iter->valid());
+ }
+};
+
+TEST_F(EmptyStore, SeekToFirstLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Empty Store, Seek To First");
+ SeekToFirst(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToFirstMockDB)
+{
+ SCOPED_TRACE("MockDB: Empty Store, Seek To First");
+ SeekToFirst(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToFirstWithPrefixLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Empty Store, Seek To First With Prefix");
+ SeekToFirstWithPrefix(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToFirstWithPrefixMockDB)
+{
+ SCOPED_TRACE("MockDB: Empty Store, Seek To First With Prefix");
+ SeekToFirstWithPrefix(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Empty Store, Seek To Last");
+ SeekToLast(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastMockDB)
+{
+ SCOPED_TRACE("MockDB: Empty Store, Seek To Last");
+ SeekToLast(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastWithPrefixLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Empty Store, Seek To Last With Prefix");
+ SeekToLastWithPrefix(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastWithPrefixMockDB)
+{
+ SCOPED_TRACE("MockDB: Empty Store, Seek To Last With Prefix");
+ SeekToLastWithPrefix(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, LowerBoundLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Empty Store, Lower Bound");
+ LowerBound(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, LowerBoundMockDB)
+{
+ SCOPED_TRACE("MockDB: Empty Store, Lower Bound");
+ LowerBound(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, UpperBoundLevelDB)
+{
+ SCOPED_TRACE("LevelDB: Empty Store, Upper Bound");
+ UpperBound(db->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, UpperBoundMockDB)
+{
+ SCOPED_TRACE("MockDB: Empty Store, Upper Bound");
+ UpperBound(mock->get_wholespace_iterator());
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (argc < 2) {
+ std::cerr << "Usage: " << argv[0]
+ << "[ceph_options] [gtest_options] <store_path>" << std::endl;
+ return 1;
+ }
+ store_path = string(argv[1]);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/ObjectMap/test_object_map.cc b/src/test/ObjectMap/test_object_map.cc
new file mode 100644
index 000000000..37c1d7cd5
--- /dev/null
+++ b/src/test/ObjectMap/test_object_map.cc
@@ -0,0 +1,1126 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include <iterator>
+#include <map>
+#include <set>
+#include <boost/scoped_ptr.hpp>
+
+#include "include/buffer.h"
+#include "test/ObjectMap/KeyValueDBMemory.h"
+#include "kv/KeyValueDB.h"
+#include "os/DBObjectMap.h"
+#include <sys/types.h>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include <dirent.h>
+
+#include "gtest/gtest.h"
+#include "stdlib.h"
+
+using namespace std;
+
+template <typename T>
+typename T::iterator rand_choose(T &cont) {
+ if (std::empty(cont)) {
+ return std::end(cont);
+ }
+ return std::next(std::begin(cont), rand() % cont.size());
+}
+
+string num_str(unsigned i) {
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%.10u", i);
+ return string(buf);
+}
+
+class ObjectMapTester {
+public:
+ ObjectMap *db;
+ set<string> key_space;
+ set<string> object_name_space;
+ map<string, map<string, string> > omap;
+ map<string, string > hmap;
+ map<string, map<string, string> > xattrs;
+ unsigned seq;
+
+ ObjectMapTester() : db(0), seq(0) {}
+
+ string val_from_key(const string &object, const string &key) {
+ return object + "_" + key + "_" + num_str(seq++);
+ }
+
+ void set_key(const string &objname, const string &key, const string &value) {
+ set_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ key, value);
+ }
+
+ void set_xattr(const string &objname, const string &key, const string &value) {
+ set_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ key, value);
+ }
+
+ void set_key(ghobject_t hoid,
+ string key, string value) {
+ map<string, bufferlist> to_write;
+ bufferptr bp(value.c_str(), value.size());
+ bufferlist bl;
+ bl.append(bp);
+ to_write.insert(make_pair(key, bl));
+ db->set_keys(hoid, to_write);
+ }
+
+ void set_keys(ghobject_t hoid, const map<string, string> &to_set) {
+ map<string, bufferlist> to_write;
+ for (auto &&i: to_set) {
+ bufferptr bp(i.second.data(), i.second.size());
+ bufferlist bl;
+ bl.append(bp);
+ to_write.insert(make_pair(i.first, bl));
+ }
+ db->set_keys(hoid, to_write);
+ }
+
+ void set_xattr(ghobject_t hoid,
+ string key, string value) {
+ map<string, bufferlist> to_write;
+ bufferptr bp(value.c_str(), value.size());
+ bufferlist bl;
+ bl.append(bp);
+ to_write.insert(make_pair(key, bl));
+ db->set_xattrs(hoid, to_write);
+ }
+
+ void set_header(const string &objname, const string &value) {
+ set_header(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ value);
+ }
+
+ void set_header(ghobject_t hoid,
+ const string &value) {
+ bufferlist header;
+ header.append(bufferptr(value.c_str(), value.size() + 1));
+ db->set_header(hoid, header);
+ }
+
+ int get_header(const string &objname, string *value) {
+ return get_header(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ value);
+ }
+
+ int get_header(ghobject_t hoid,
+ string *value) {
+ bufferlist header;
+ int r = db->get_header(hoid, &header);
+ if (r < 0)
+ return r;
+ if (header.length())
+ *value = string(header.c_str());
+ else
+ *value = string("");
+ return 0;
+ }
+
+ int get_xattr(const string &objname, const string &key, string *value) {
+ return get_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ key, value);
+ }
+
+ int get_xattr(ghobject_t hoid,
+ string key, string *value) {
+ set<string> to_get;
+ to_get.insert(key);
+ map<string, bufferlist> got;
+ db->get_xattrs(hoid, to_get, &got);
+ if (!got.empty()) {
+ *value = string(got.begin()->second.c_str(),
+ got.begin()->second.length());
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ int get_key(const string &objname, const string &key, string *value) {
+ return get_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ key, value);
+ }
+
+ int get_key(ghobject_t hoid,
+ string key, string *value) {
+ set<string> to_get;
+ to_get.insert(key);
+ map<string, bufferlist> got;
+ db->get_values(hoid, to_get, &got);
+ if (!got.empty()) {
+ if (value) {
+ *value = string(got.begin()->second.c_str(),
+ got.begin()->second.length());
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ void remove_key(const string &objname, const string &key) {
+ remove_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ key);
+ }
+
+ void remove_keys(const string &objname, const set<string> &to_remove) {
+ remove_keys(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ to_remove);
+ }
+
+ void remove_key(ghobject_t hoid,
+ string key) {
+ set<string> to_remove;
+ to_remove.insert(key);
+ db->rm_keys(hoid, to_remove);
+ }
+
+ void remove_keys(ghobject_t hoid,
+ const set<string> &to_remove) {
+ db->rm_keys(hoid, to_remove);
+ }
+
+ void remove_xattr(const string &objname, const string &key) {
+ remove_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ key);
+ }
+
+ void remove_xattr(ghobject_t hoid,
+ string key) {
+ set<string> to_remove;
+ to_remove.insert(key);
+ db->remove_xattrs(hoid, to_remove);
+ }
+
+ void clone(const string &objname, const string &target) {
+ clone(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP))));
+ }
+
+ void clone(ghobject_t hoid,
+ ghobject_t hoid2) {
+ db->clone(hoid, hoid2);
+ }
+
+ void rename(const string &objname, const string &target) {
+ rename(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP))));
+ }
+
+ void rename(ghobject_t hoid,
+ ghobject_t hoid2) {
+ db->rename(hoid, hoid2);
+ }
+
+ void clear(const string &objname) {
+ clear(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))));
+ }
+
+ void legacy_clone(const string &objname, const string &target) {
+ legacy_clone(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))),
+ ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP))));
+ }
+
+ void legacy_clone(ghobject_t hoid,
+ ghobject_t hoid2) {
+ db->legacy_clone(hoid, hoid2);
+ }
+
+ void clear(ghobject_t hoid) {
+ db->clear(hoid);
+ }
+
+ void clear_omap(const string &objname) {
+ clear_omap(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))));
+ }
+
+ void clear_omap(const ghobject_t &objname) {
+ db->clear_keys_header(objname);
+ }
+
+ void def_init() {
+ for (unsigned i = 0; i < 10000; ++i) {
+ key_space.insert("key_" + num_str(i));
+ }
+ for (unsigned i = 0; i < 100; ++i) {
+ object_name_space.insert("name_" + num_str(i));
+ }
+ }
+
+ void init_key_set(const set<string> &keys) {
+ key_space = keys;
+ }
+
+ void init_object_name_space(const set<string> &onamespace) {
+ object_name_space = onamespace;
+ }
+
+ void auto_set_xattr(ostream &out) {
+ set<string>::iterator key = rand_choose(key_space);
+ set<string>::iterator object = rand_choose(object_name_space);
+
+ string value = val_from_key(*object, *key);
+
+ xattrs[*object][*key] = value;
+ set_xattr(*object, *key, value);
+
+ out << "auto_set_xattr " << *object << ": " << *key << " -> "
+ << value << std::endl;
+ }
+
+ void test_set_key(const string &obj, const string &key, const string &val) {
+ omap[obj][key] = val;
+ set_key(obj, key, val);
+ }
+
+ void test_set_keys(const string &obj, const map<string, string> &to_set) {
+ for (auto &&i: to_set) {
+ omap[obj][i.first] = i.second;
+ }
+ set_keys(
+ ghobject_t(hobject_t(sobject_t(obj, CEPH_NOSNAP))),
+ to_set);
+ }
+
+ void auto_set_keys(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+
+ map<string, string> to_set;
+ unsigned amount = (rand() % 10) + 1;
+ for (unsigned i = 0; i < amount; ++i) {
+ set<string>::iterator key = rand_choose(key_space);
+ string value = val_from_key(*object, *key);
+ out << "auto_set_key " << *object << ": " << *key << " -> "
+ << value << std::endl;
+ to_set.insert(make_pair(*key, value));
+ }
+
+
+ test_set_keys(*object, to_set);
+ }
+
+ void xattrs_on_object(const string &object, set<string> *out) {
+ if (!xattrs.count(object))
+ return;
+ const map<string, string> &xmap = xattrs.find(object)->second;
+ for (map<string, string>::const_iterator i = xmap.begin();
+ i != xmap.end();
+ ++i) {
+ out->insert(i->first);
+ }
+ }
+
+ void keys_on_object(const string &object, set<string> *out) {
+ if (!omap.count(object))
+ return;
+ const map<string, string> &kmap = omap.find(object)->second;
+ for (map<string, string>::const_iterator i = kmap.begin();
+ i != kmap.end();
+ ++i) {
+ out->insert(i->first);
+ }
+ }
+
+ void xattrs_off_object(const string &object, set<string> *out) {
+ *out = key_space;
+ set<string> xspace;
+ xattrs_on_object(object, &xspace);
+ for (set<string>::iterator i = xspace.begin();
+ i != xspace.end();
+ ++i) {
+ out->erase(*i);
+ }
+ }
+
+ void keys_off_object(const string &object, set<string> *out) {
+ *out = key_space;
+ set<string> kspace;
+ keys_on_object(object, &kspace);
+ for (set<string>::iterator i = kspace.begin();
+ i != kspace.end();
+ ++i) {
+ out->erase(*i);
+ }
+ }
+
+ int auto_check_present_xattr(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ set<string> xspace;
+ xattrs_on_object(*object, &xspace);
+ set<string>::iterator key = rand_choose(xspace);
+ if (key == xspace.end()) {
+ return 1;
+ }
+
+ string result;
+ int r = get_xattr(*object, *key, &result);
+ if (!r) {
+ out << "auto_check_present_key: failed to find key "
+ << *key << " on object " << *object << std::endl;
+ return 0;
+ }
+
+ if (result != xattrs[*object][*key]) {
+ out << "auto_check_present_key: for key "
+ << *key << " on object " << *object
+ << " found value " << result << " where we should have found "
+ << xattrs[*object][*key] << std::endl;
+ return 0;
+ }
+
+ out << "auto_check_present_key: for key "
+ << *key << " on object " << *object
+ << " found value " << result << " where we should have found "
+ << xattrs[*object][*key] << std::endl;
+ return 1;
+ }
+
+
+ int auto_check_present_key(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ set<string> kspace;
+ keys_on_object(*object, &kspace);
+ set<string>::iterator key = rand_choose(kspace);
+ if (key == kspace.end()) {
+ return 1;
+ }
+
+ string result;
+ int r = get_key(*object, *key, &result);
+ if (!r) {
+ out << "auto_check_present_key: failed to find key "
+ << *key << " on object " << *object << std::endl;
+ return 0;
+ }
+
+ if (result != omap[*object][*key]) {
+ out << "auto_check_present_key: for key "
+ << *key << " on object " << *object
+ << " found value " << result << " where we should have found "
+ << omap[*object][*key] << std::endl;
+ return 0;
+ }
+
+ out << "auto_check_present_key: for key "
+ << *key << " on object " << *object
+ << " found value " << result << " where we should have found "
+ << omap[*object][*key] << std::endl;
+ return 1;
+ }
+
+ int auto_check_absent_xattr(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ set<string> xspace;
+ xattrs_off_object(*object, &xspace);
+ set<string>::iterator key = rand_choose(xspace);
+ if (key == xspace.end()) {
+ return 1;
+ }
+
+ string result;
+ int r = get_xattr(*object, *key, &result);
+ if (!r) {
+ out << "auto_check_absent_key: did not find key "
+ << *key << " on object " << *object << std::endl;
+ return 1;
+ }
+
+ out << "auto_check_basent_key: for key "
+ << *key << " on object " << *object
+ << " found value " << result << " where we should have found nothing"
+ << std::endl;
+ return 0;
+ }
+
+ int auto_check_absent_key(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ set<string> kspace;
+ keys_off_object(*object, &kspace);
+ set<string>::iterator key = rand_choose(kspace);
+ if (key == kspace.end()) {
+ return 1;
+ }
+
+ string result;
+ int r = get_key(*object, *key, &result);
+ if (!r) {
+ out << "auto_check_absent_key: did not find key "
+ << *key << " on object " << *object << std::endl;
+ return 1;
+ }
+
+ out << "auto_check_basent_key: for key "
+ << *key << " on object " << *object
+ << " found value " << result << " where we should have found nothing"
+ << std::endl;
+ return 0;
+ }
+
+ void test_clone(const string &object, const string &target, ostream &out) {
+ clone(object, target);
+ if (!omap.count(object)) {
+ out << " source missing.";
+ omap.erase(target);
+ } else {
+ out << " source present.";
+ omap[target] = omap[object];
+ }
+ if (!hmap.count(object)) {
+ out << " hmap source missing." << std::endl;
+ hmap.erase(target);
+ } else {
+ out << " hmap source present." << std::endl;
+ hmap[target] = hmap[object];
+ }
+ if (!xattrs.count(object)) {
+ out << " hmap source missing." << std::endl;
+ xattrs.erase(target);
+ } else {
+ out << " hmap source present." << std::endl;
+ xattrs[target] = xattrs[object];
+ }
+ }
+
+ void auto_clone_key(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ set<string>::iterator target = rand_choose(object_name_space);
+ while (target == object) {
+ target = rand_choose(object_name_space);
+ }
+ out << "clone " << *object << " to " << *target;
+ test_clone(*object, *target, out);
+ }
+
+ void test_remove_keys(const string &obj, const set<string> &to_remove) {
+ for (auto &&k: to_remove)
+ omap[obj].erase(k);
+ remove_keys(obj, to_remove);
+ }
+
+ void test_remove_key(const string &obj, const string &key) {
+ omap[obj].erase(key);
+ remove_key(obj, key);
+ }
+
+ void auto_remove_keys(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ set<string> kspace;
+ keys_on_object(*object, &kspace);
+ set<string> to_remove;
+ for (unsigned i = 0; i < 3; ++i) {
+ set<string>::iterator key = rand_choose(kspace);
+ if (key == kspace.end())
+ continue;
+ out << "removing " << *key << " from " << *object << std::endl;
+ to_remove.insert(*key);
+ }
+ test_remove_keys(*object, to_remove);
+ }
+
+ void auto_remove_xattr(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ set<string> kspace;
+ xattrs_on_object(*object, &kspace);
+ set<string>::iterator key = rand_choose(kspace);
+ if (key == kspace.end()) {
+ return;
+ }
+ out << "removing xattr " << *key << " from " << *object << std::endl;
+ xattrs[*object].erase(*key);
+ remove_xattr(*object, *key);
+ }
+
+ void auto_delete_object(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ out << "auto_delete_object " << *object << std::endl;
+ clear(*object);
+ omap.erase(*object);
+ hmap.erase(*object);
+ xattrs.erase(*object);
+ }
+
+ void test_clear(const string &obj) {
+ clear_omap(obj);
+ omap.erase(obj);
+ hmap.erase(obj);
+ }
+
+ void auto_clear_omap(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ out << "auto_clear_object " << *object << std::endl;
+ test_clear(*object);
+ }
+
+ void auto_write_header(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ string header = val_from_key(*object, "HEADER");
+ out << "auto_write_header: " << *object << " -> " << header << std::endl;
+ set_header(*object, header);
+ hmap[*object] = header;
+ }
+
+ int auto_verify_header(ostream &out) {
+ set<string>::iterator object = rand_choose(object_name_space);
+ out << "verify_header: " << *object << " ";
+ string header;
+ int r = get_header(*object, &header);
+ if (r < 0) {
+ ceph_abort();
+ }
+ if (header.size() == 0) {
+ if (hmap.count(*object)) {
+ out << " failed to find header " << hmap[*object] << std::endl;
+ return 0;
+ } else {
+ out << " found no header" << std::endl;
+ return 1;
+ }
+ }
+
+ if (!hmap.count(*object)) {
+ out << " found header " << header << " should have been empty"
+ << std::endl;
+ return 0;
+ } else if (header == hmap[*object]) {
+ out << " found correct header " << header << std::endl;
+ return 1;
+ } else {
+ out << " found incorrect header " << header
+ << " where we should have found " << hmap[*object] << std::endl;
+ return 0;
+ }
+ }
+
+ void verify_keys(const std::string &obj, ostream &out) {
+ set<string> in_db;
+ ObjectMap::ObjectMapIterator iter = db->get_iterator(
+ ghobject_t(hobject_t(sobject_t(obj, CEPH_NOSNAP))));
+ for (iter->seek_to_first(); iter->valid(); iter->next()) {
+ in_db.insert(iter->key());
+ }
+ bool err = false;
+ for (auto &&i: omap[obj]) {
+ if (!in_db.count(i.first)) {
+ out << __func__ << ": obj " << obj << " missing key "
+ << i.first << std::endl;
+ err = true;
+ } else {
+ in_db.erase(i.first);
+ }
+ }
+ if (!in_db.empty()) {
+ out << __func__ << ": obj " << obj << " found extra keys "
+ << in_db << std::endl;
+ err = true;
+ }
+ ASSERT_FALSE(err);
+ }
+
+ void auto_verify_objects(ostream &out) {
+ for (auto &&i: omap) {
+ verify_keys(i.first, out);
+ }
+ }
+};
+
+class ObjectMapTest : public ::testing::Test {
+public:
+ boost::scoped_ptr< ObjectMap > db;
+ ObjectMapTester tester;
+ void SetUp() override {
+ char *path = getenv("OBJECT_MAP_PATH");
+ if (!path) {
+ db.reset(new DBObjectMap(g_ceph_context, new KeyValueDBMemory()));
+ tester.db = db.get();
+ return;
+ }
+
+ string strpath(path);
+
+ cerr << "using path " << strpath << std::endl;
+ KeyValueDB *store = KeyValueDB::create(g_ceph_context, "leveldb", strpath);
+ ceph_assert(!store->create_and_open(cerr));
+
+ db.reset(new DBObjectMap(g_ceph_context, store));
+ tester.db = db.get();
+ }
+
+ void TearDown() override {
+ std::cerr << "Checking..." << std::endl;
+ ASSERT_EQ(0, db->check(std::cerr));
+ }
+};
+
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+TEST_F(ObjectMapTest, CreateOneObject) {
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)), 100, shard_id_t(0));
+ map<string, bufferlist> to_set;
+ string key("test");
+ string val("test_val");
+ bufferptr bp(val.c_str(), val.size());
+ bufferlist bl;
+ bl.append(bp);
+ to_set.insert(make_pair(key, bl));
+ ASSERT_EQ(db->set_keys(hoid, to_set), 0);
+
+ map<string, bufferlist> got;
+ set<string> to_get;
+ to_get.insert(key);
+ to_get.insert("not there");
+ db->get_values(hoid, to_get, &got);
+ ASSERT_EQ(got.size(), (unsigned)1);
+ ASSERT_EQ(string(got[key].c_str(), got[key].length()), val);
+
+ bufferlist header;
+ got.clear();
+ db->get(hoid, &header, &got);
+ ASSERT_EQ(got.size(), (unsigned)1);
+ ASSERT_EQ(string(got[key].c_str(), got[key].length()), val);
+ ASSERT_EQ(header.length(), (unsigned)0);
+
+ db->rm_keys(hoid, to_get);
+ got.clear();
+ db->get(hoid, &header, &got);
+ ASSERT_EQ(got.size(), (unsigned)0);
+
+ map<string, bufferlist> attrs;
+ attrs["attr1"] = bl;
+ db->set_xattrs(hoid, attrs);
+
+ db->set_header(hoid, bl);
+
+ db->clear_keys_header(hoid);
+ set<string> attrs_got;
+ db->get_all_xattrs(hoid, &attrs_got);
+ ASSERT_EQ(attrs_got.size(), 1U);
+ ASSERT_EQ(*(attrs_got.begin()), "attr1");
+ db->get(hoid, &header, &got);
+ ASSERT_EQ(got.size(), (unsigned)0);
+ ASSERT_EQ(header.length(), 0U);
+ got.clear();
+
+ db->clear(hoid);
+ db->get(hoid, &header, &got);
+ ASSERT_EQ(got.size(), (unsigned)0);
+ attrs_got.clear();
+ db->get_all_xattrs(hoid, &attrs_got);
+ ASSERT_EQ(attrs_got.size(), 0U);
+}
+
+TEST_F(ObjectMapTest, CloneOneObject) {
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)), 200, shard_id_t(0));
+ ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP)), 201, shard_id_t(1));
+
+ tester.set_key(hoid, "foo", "bar");
+ tester.set_key(hoid, "foo2", "bar2");
+ string result;
+ int r = tester.get_key(hoid, "foo", &result);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(result, "bar");
+
+ db->clone(hoid, hoid2);
+ r = tester.get_key(hoid, "foo", &result);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(result, "bar");
+ r = tester.get_key(hoid2, "foo", &result);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(result, "bar");
+
+ tester.remove_key(hoid, "foo");
+ r = tester.get_key(hoid2, "foo", &result);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(result, "bar");
+ r = tester.get_key(hoid, "foo", &result);
+ ASSERT_EQ(r, 0);
+ r = tester.get_key(hoid, "foo2", &result);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(result, "bar2");
+
+ tester.set_key(hoid, "foo", "baz");
+ tester.remove_key(hoid, "foo");
+ r = tester.get_key(hoid, "foo", &result);
+ ASSERT_EQ(r, 0);
+
+ tester.set_key(hoid, "foo2", "baz");
+ tester.remove_key(hoid, "foo2");
+ r = tester.get_key(hoid, "foo2", &result);
+ ASSERT_EQ(r, 0);
+
+ map<string, bufferlist> got;
+ bufferlist header;
+
+ got.clear();
+ db->clear(hoid);
+ db->get(hoid, &header, &got);
+ ASSERT_EQ(got.size(), (unsigned)0);
+
+ got.clear();
+ r = db->clear(hoid2);
+ ASSERT_EQ(0, r);
+ db->get(hoid2, &header, &got);
+ ASSERT_EQ(got.size(), (unsigned)0);
+
+ tester.set_key(hoid, "baz", "bar");
+ got.clear();
+ db->get(hoid, &header, &got);
+ ASSERT_EQ(got.size(), (unsigned)1);
+ db->clear(hoid);
+ db->clear(hoid2);
+}
+
+TEST_F(ObjectMapTest, OddEvenClone) {
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP)));
+
+ for (unsigned i = 0; i < 1000; ++i) {
+ tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i));
+ }
+
+ db->clone(hoid, hoid2);
+
+ int r = 0;
+ for (unsigned i = 0; i < 1000; ++i) {
+ string result;
+ r = tester.get_key(hoid, "foo" + num_str(i), &result);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+ r = tester.get_key(hoid2, "foo" + num_str(i), &result);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+
+ if (i % 2) {
+ tester.remove_key(hoid, "foo" + num_str(i));
+ } else {
+ tester.remove_key(hoid2, "foo" + num_str(i));
+ }
+ }
+
+ for (unsigned i = 0; i < 1000; ++i) {
+ string result;
+ string result2;
+ r = tester.get_key(hoid, "foo" + num_str(i), &result);
+ int r2 = tester.get_key(hoid2, "foo" + num_str(i), &result2);
+ if (i % 2) {
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1, r2);
+ ASSERT_EQ("bar" + num_str(i), result2);
+ } else {
+ ASSERT_EQ(0, r2);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+ }
+ }
+
+ {
+ ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid);
+ iter->seek_to_first();
+ for (unsigned i = 0; i < 1000; ++i) {
+ if (!(i % 2)) {
+ ASSERT_TRUE(iter->valid());
+ ASSERT_EQ("foo" + num_str(i), iter->key());
+ iter->next();
+ }
+ }
+ }
+
+ {
+ ObjectMap::ObjectMapIterator iter2 = db->get_iterator(hoid2);
+ iter2->seek_to_first();
+ for (unsigned i = 0; i < 1000; ++i) {
+ if (i % 2) {
+ ASSERT_TRUE(iter2->valid());
+ ASSERT_EQ("foo" + num_str(i), iter2->key());
+ iter2->next();
+ }
+ }
+ }
+
+ db->clear(hoid);
+ db->clear(hoid2);
+}
+
+TEST_F(ObjectMapTest, Rename) {
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP)));
+
+ for (unsigned i = 0; i < 1000; ++i) {
+ tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i));
+ }
+
+ db->rename(hoid, hoid2);
+ // Verify rename where target exists
+ db->clone(hoid2, hoid);
+ db->rename(hoid, hoid2);
+
+ int r = 0;
+ for (unsigned i = 0; i < 1000; ++i) {
+ string result;
+ r = tester.get_key(hoid2, "foo" + num_str(i), &result);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+
+ if (i % 2) {
+ tester.remove_key(hoid2, "foo" + num_str(i));
+ }
+ }
+
+ for (unsigned i = 0; i < 1000; ++i) {
+ string result;
+ r = tester.get_key(hoid2, "foo" + num_str(i), &result);
+ if (i % 2) {
+ ASSERT_EQ(0, r);
+ } else {
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+ }
+ }
+
+ {
+ ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid2);
+ iter->seek_to_first();
+ for (unsigned i = 0; i < 1000; ++i) {
+ if (!(i % 2)) {
+ ASSERT_TRUE(iter->valid());
+ ASSERT_EQ("foo" + num_str(i), iter->key());
+ iter->next();
+ }
+ }
+ }
+
+ db->clear(hoid2);
+}
+
+TEST_F(ObjectMapTest, OddEvenOldClone) {
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP)));
+
+ for (unsigned i = 0; i < 1000; ++i) {
+ tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i));
+ }
+
+ db->legacy_clone(hoid, hoid2);
+
+ int r = 0;
+ for (unsigned i = 0; i < 1000; ++i) {
+ string result;
+ r = tester.get_key(hoid, "foo" + num_str(i), &result);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+ r = tester.get_key(hoid2, "foo" + num_str(i), &result);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+
+ if (i % 2) {
+ tester.remove_key(hoid, "foo" + num_str(i));
+ } else {
+ tester.remove_key(hoid2, "foo" + num_str(i));
+ }
+ }
+
+ for (unsigned i = 0; i < 1000; ++i) {
+ string result;
+ string result2;
+ r = tester.get_key(hoid, "foo" + num_str(i), &result);
+ int r2 = tester.get_key(hoid2, "foo" + num_str(i), &result2);
+ if (i % 2) {
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1, r2);
+ ASSERT_EQ("bar" + num_str(i), result2);
+ } else {
+ ASSERT_EQ(0, r2);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ("bar" + num_str(i), result);
+ }
+ }
+
+ {
+ ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid);
+ iter->seek_to_first();
+ for (unsigned i = 0; i < 1000; ++i) {
+ if (!(i % 2)) {
+ ASSERT_TRUE(iter->valid());
+ ASSERT_EQ("foo" + num_str(i), iter->key());
+ iter->next();
+ }
+ }
+ }
+
+ {
+ ObjectMap::ObjectMapIterator iter2 = db->get_iterator(hoid2);
+ iter2->seek_to_first();
+ for (unsigned i = 0; i < 1000; ++i) {
+ if (i % 2) {
+ ASSERT_TRUE(iter2->valid());
+ ASSERT_EQ("foo" + num_str(i), iter2->key());
+ iter2->next();
+ }
+ }
+ }
+
+ db->clear(hoid);
+ db->clear(hoid2);
+}
+
+TEST_F(ObjectMapTest, RandomTest) {
+ tester.def_init();
+ for (unsigned i = 0; i < 5000; ++i) {
+ unsigned val = rand();
+ val <<= 8;
+ val %= 100;
+ if (!(i%100))
+ std::cout << "on op " << i
+ << " val is " << val << std::endl;
+
+ if (val < 7) {
+ tester.auto_write_header(std::cerr);
+ } else if (val < 14) {
+ ASSERT_TRUE(tester.auto_verify_header(std::cerr));
+ } else if (val < 30) {
+ tester.auto_set_keys(std::cerr);
+ } else if (val < 42) {
+ tester.auto_set_xattr(std::cerr);
+ } else if (val < 55) {
+ ASSERT_TRUE(tester.auto_check_present_key(std::cerr));
+ } else if (val < 62) {
+ ASSERT_TRUE(tester.auto_check_present_xattr(std::cerr));
+ } else if (val < 70) {
+ ASSERT_TRUE(tester.auto_check_absent_key(std::cerr));
+ } else if (val < 72) {
+ ASSERT_TRUE(tester.auto_check_absent_xattr(std::cerr));
+ } else if (val < 73) {
+ tester.auto_clear_omap(std::cerr);
+ } else if (val < 76) {
+ tester.auto_delete_object(std::cerr);
+ } else if (val < 85) {
+ tester.auto_clone_key(std::cerr);
+ } else if (val < 92) {
+ tester.auto_remove_xattr(std::cerr);
+ } else {
+ tester.auto_remove_keys(std::cerr);
+ }
+
+ if (i % 500) {
+ tester.auto_verify_objects(std::cerr);
+ }
+ }
+}
+
+TEST_F(ObjectMapTest, RandomTestNoDeletesXattrs) {
+ tester.def_init();
+ for (unsigned i = 0; i < 5000; ++i) {
+ unsigned val = rand();
+ val <<= 8;
+ val %= 100;
+ if (!(i%100))
+ std::cout << "on op " << i
+ << " val is " << val << std::endl;
+
+ if (val < 45) {
+ tester.auto_set_keys(std::cerr);
+ } else if (val < 90) {
+ tester.auto_remove_keys(std::cerr);
+ } else {
+ tester.auto_clone_key(std::cerr);
+ }
+
+ if (i % 500) {
+ tester.auto_verify_objects(std::cerr);
+ }
+ }
+}
+
+string num_to_key(unsigned i) {
+ char buf[100];
+ int ret = snprintf(buf, sizeof(buf), "%010u", i);
+ ceph_assert(ret > 0);
+ return string(buf, ret);
+}
+
+TEST_F(ObjectMapTest, TestMergeNewCompleteContainBug) {
+ /* This test exploits a bug in kraken and earlier where merge_new_complete
+ * could miss complete entries fully contained by a new entry. To get this
+ * to actually result in an incorrect return value, you need to remove at
+ * least two values, one before a complete region, and one which occurs in
+ * the parent after the complete region (but within 20 not yet completed
+ * parent points of the first value).
+ */
+ for (unsigned i = 10; i < 160; i+=2) {
+ tester.test_set_key("foo", num_to_key(i), "asdf");
+ }
+ tester.test_clone("foo", "foo2", std::cout);
+ tester.test_clear("foo");
+
+ tester.test_set_key("foo2", num_to_key(15), "asdf");
+ tester.test_set_key("foo2", num_to_key(13), "asdf");
+ tester.test_set_key("foo2", num_to_key(57), "asdf");
+
+ tester.test_remove_key("foo2", num_to_key(15));
+
+ set<string> to_remove;
+ to_remove.insert(num_to_key(13));
+ to_remove.insert(num_to_key(58));
+ to_remove.insert(num_to_key(60));
+ to_remove.insert(num_to_key(62));
+ tester.test_remove_keys("foo2", to_remove);
+
+ tester.verify_keys("foo2", std::cout);
+ ASSERT_EQ(tester.get_key("foo2", num_to_key(10), nullptr), 1);
+ ASSERT_EQ(tester.get_key("foo2", num_to_key(1), nullptr), 0);
+ ASSERT_EQ(tester.get_key("foo2", num_to_key(56), nullptr), 1);
+ // this one triggers the bug
+ ASSERT_EQ(tester.get_key("foo2", num_to_key(58), nullptr), 0);
+}
+
+TEST_F(ObjectMapTest, TestIterateBug18533) {
+ /* This test starts with the one immediately above to create a pair of
+ * complete regions where one contains the other. Then, it deletes the
+ * key at the start of the contained region. The logic in next_parent()
+ * skips ahead to the end of the contained region, and we start copying
+ * values down again from the parent into the child -- including some
+ * that had actually been deleted. I think this works for any removal
+ * within the outer complete region after the start of the contained
+ * region.
+ */
+ for (unsigned i = 10; i < 160; i+=2) {
+ tester.test_set_key("foo", num_to_key(i), "asdf");
+ }
+ tester.test_clone("foo", "foo2", std::cout);
+ tester.test_clear("foo");
+
+ tester.test_set_key("foo2", num_to_key(15), "asdf");
+ tester.test_set_key("foo2", num_to_key(13), "asdf");
+ tester.test_set_key("foo2", num_to_key(57), "asdf");
+ tester.test_set_key("foo2", num_to_key(91), "asdf");
+
+ tester.test_remove_key("foo2", num_to_key(15));
+
+ set<string> to_remove;
+ to_remove.insert(num_to_key(13));
+ to_remove.insert(num_to_key(58));
+ to_remove.insert(num_to_key(60));
+ to_remove.insert(num_to_key(62));
+ to_remove.insert(num_to_key(82));
+ to_remove.insert(num_to_key(84));
+ tester.test_remove_keys("foo2", to_remove);
+
+ //tester.test_remove_key("foo2", num_to_key(15)); also does the trick
+ tester.test_remove_key("foo2", num_to_key(80));
+
+ // the iterator in verify_keys will return an extra value
+ tester.verify_keys("foo2", std::cout);
+}
+
diff --git a/src/test/TestSignalHandlers.cc b/src/test/TestSignalHandlers.cc
new file mode 100644
index 000000000..6a5579956
--- /dev/null
+++ b/src/test/TestSignalHandlers.cc
@@ -0,0 +1,117 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2010 Dreamhost
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+/*
+ * TestSignalHandlers
+ *
+ * Test the Ceph signal handlers
+ */
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "common/errno.h"
+#include "common/debug.h"
+#include "common/config.h"
+
+#include <errno.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#define dout_context g_ceph_context
+
+using namespace std;
+
+// avoid compiler warning about dereferencing NULL pointer
+static int* get_null()
+{
+ return 0;
+}
+
+static void simple_segv_test()
+{
+ generic_dout(-1) << "triggering SIGSEGV..." << dendl;
+ // cppcheck-suppress nullPointer
+ int i = *get_null();
+ std::cout << "i = " << i << std::endl;
+}
+
+// Given the name of the function, we can be pretty sure this is intentional.
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winfinite-recursion"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+
+static void infinite_recursion_test_impl()
+{
+ infinite_recursion_test_impl();
+}
+
+#pragma GCC diagnostic pop
+#pragma clang diagnostic pop
+
+static void infinite_recursion_test()
+{
+ generic_dout(0) << "triggering SIGSEGV with infinite recursion..." << dendl;
+ infinite_recursion_test_impl();
+}
+
+static void usage()
+{
+ cout << "usage: TestSignalHandlers [test]" << std::endl;
+ cout << "--simple_segv: run simple_segv test" << std::endl;
+ cout << "--infinite_recursion: run infinite_recursion test" << std::endl;
+ generic_client_usage();
+}
+
+typedef void (*test_fn_t)(void);
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ if (args.empty()) {
+ cerr << argv[0] << ": -h or --help for usage" << std::endl;
+ exit(1);
+ }
+ if (ceph_argparse_need_usage(args)) {
+ usage();
+ exit(0);
+ }
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ test_fn_t fn = NULL;
+ for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
+ if (ceph_argparse_double_dash(args, i)) {
+ break;
+ } else if (ceph_argparse_flag(args, i, "--infinite_recursion", (char*)NULL)) {
+ fn = infinite_recursion_test;
+ } else if (ceph_argparse_flag(args, i, "-s", "--simple_segv", (char*)NULL)) {
+ fn = simple_segv_test;
+ } else {
+ cerr << "unrecognized argument: " << *i << std::endl;
+ exit(1);
+ }
+ }
+ if (!fn) {
+ std::cerr << "Please select a test to run. Type -h for help." << std::endl;
+ exit(1);
+ }
+ fn();
+ return 0;
+}
diff --git a/src/test/TestTimers.cc b/src/test/TestTimers.cc
new file mode 100644
index 000000000..78bdfd3a4
--- /dev/null
+++ b/src/test/TestTimers.cc
@@ -0,0 +1,281 @@
+#include "common/ceph_argparse.h"
+#include "common/ceph_mutex.h"
+#include "common/Timer.h"
+#include "global/global_init.h"
+#include "include/Context.h"
+
+#include <iostream>
+
+/*
+ * TestTimers
+ *
+ * Tests the timer classes
+ */
+#define MAX_TEST_CONTEXTS 5
+
+using namespace std;
+
+class TestContext;
+
+namespace
+{
+ int test_array[MAX_TEST_CONTEXTS];
+ int array_idx;
+ TestContext* test_contexts[MAX_TEST_CONTEXTS];
+
+ ceph::mutex array_lock = ceph::make_mutex("test_timers_mutex");
+}
+
+class TestContext : public Context
+{
+public:
+ explicit TestContext(int num_)
+ : num(num_)
+ {
+ }
+
+ void finish(int r) override
+ {
+ std::lock_guard locker{array_lock};
+ cout << "TestContext " << num << std::endl;
+ test_array[array_idx++] = num;
+ }
+
+ ~TestContext() override
+ {
+ }
+
+protected:
+ int num;
+};
+
+class StrictOrderTestContext : public TestContext
+{
+public:
+ explicit StrictOrderTestContext (int num_)
+ : TestContext(num_)
+ {
+ }
+
+ void finish(int r) override
+ {
+ std::lock_guard locker{array_lock};
+ cout << "StrictOrderTestContext " << num << std::endl;
+ test_array[num] = num;
+ }
+
+ ~StrictOrderTestContext() override
+ {
+ }
+};
+
+static void print_status(const char *str, int ret)
+{
+ cout << str << ": ";
+ cout << ((ret == 0) ? "SUCCESS" : "FAILURE");
+ cout << std::endl;
+}
+
+template <typename T>
+static int basic_timer_test(T &timer, ceph::mutex *lock)
+{
+ int ret = 0;
+ memset(&test_array, 0, sizeof(test_array));
+ array_idx = 0;
+ memset(&test_contexts, 0, sizeof(test_contexts));
+
+ cout << __PRETTY_FUNCTION__ << std::endl;
+
+ for (int i = 0; i < MAX_TEST_CONTEXTS; ++i) {
+ test_contexts[i] = new TestContext(i);
+ }
+
+
+ for (int i = 0; i < MAX_TEST_CONTEXTS; ++i) {
+ if (lock)
+ lock->lock();
+ auto t = ceph::real_clock::now() + std::chrono::seconds(2 * i);
+ timer.add_event_at(t, test_contexts[i]);
+ if (lock)
+ lock->unlock();
+ }
+
+ bool done = false;
+ do {
+ sleep(1);
+ std::lock_guard locker{array_lock};
+ done = (array_idx == MAX_TEST_CONTEXTS);
+ } while (!done);
+
+ for (int i = 0; i < MAX_TEST_CONTEXTS; ++i) {
+ if (test_array[i] != i) {
+ ret = 1;
+ cout << "error: expected test_array[" << i << "] = " << i
+ << "; got " << test_array[i] << " instead." << std::endl;
+ }
+ }
+
+ return ret;
+}
+
+static int test_out_of_order_insertion(SafeTimer &timer, ceph::mutex *lock)
+{
+ int ret = 0;
+ memset(&test_array, 0, sizeof(test_array));
+ array_idx = 0;
+ memset(&test_contexts, 0, sizeof(test_contexts));
+
+ cout << __PRETTY_FUNCTION__ << std::endl;
+
+ test_contexts[0] = new StrictOrderTestContext(0);
+ test_contexts[1] = new StrictOrderTestContext(1);
+
+ {
+ auto t = ceph::real_clock::now() + 100s;
+ std::lock_guard locker{*lock};
+ timer.add_event_at(t, test_contexts[0]);
+ }
+
+ {
+ auto t = ceph::real_clock::now() + 2s;
+ std::lock_guard locker{*lock};
+ timer.add_event_at(t, test_contexts[1]);
+ }
+
+ int secs = 0;
+ for (; secs < 100 ; ++secs) {
+ sleep(1);
+ array_lock.lock();
+ int a = test_array[1];
+ array_lock.unlock();
+ if (a == 1)
+ break;
+ }
+
+ if (secs == 100) {
+ ret = 1;
+ cout << "error: expected test_array[" << 1 << "] = " << 1
+ << "; got " << test_array[1] << " instead." << std::endl;
+ }
+
+ return ret;
+}
+
+static int safe_timer_cancel_all_test(SafeTimer &safe_timer,
+ ceph::mutex& safe_timer_lock)
+{
+ cout << __PRETTY_FUNCTION__ << std::endl;
+
+ int ret = 0;
+ memset(&test_array, 0, sizeof(test_array));
+ array_idx = 0;
+ memset(&test_contexts, 0, sizeof(test_contexts));
+
+ for (int i = 0; i < MAX_TEST_CONTEXTS; ++i) {
+ test_contexts[i] = new TestContext(i);
+ }
+
+ safe_timer_lock.lock();
+ for (int i = 0; i < MAX_TEST_CONTEXTS; ++i) {
+ auto t = ceph::real_clock::now() + std::chrono::seconds(4 * i);
+ safe_timer.add_event_at(t, test_contexts[i]);
+ }
+ safe_timer_lock.unlock();
+
+ sleep(10);
+
+ safe_timer_lock.lock();
+ safe_timer.cancel_all_events();
+ safe_timer_lock.unlock();
+
+ for (int i = 0; i < array_idx; ++i) {
+ if (test_array[i] != i) {
+ ret = 1;
+ cout << "error: expected test_array[" << i << "] = " << i
+ << "; got " << test_array[i] << " instead." << std::endl;
+ }
+ }
+
+ return ret;
+}
+
+static int safe_timer_cancellation_test(SafeTimer &safe_timer,
+ ceph::mutex& safe_timer_lock)
+{
+ cout << __PRETTY_FUNCTION__ << std::endl;
+
+ int ret = 0;
+ memset(&test_array, 0, sizeof(test_array));
+ array_idx = 0;
+ memset(&test_contexts, 0, sizeof(test_contexts));
+
+ for (int i = 0; i < MAX_TEST_CONTEXTS; ++i) {
+ test_contexts[i] = new StrictOrderTestContext(i);
+ }
+
+ safe_timer_lock.lock();
+ for (int i = 0; i < MAX_TEST_CONTEXTS; ++i) {
+ auto t = ceph::real_clock::now() + std::chrono::seconds(4 * i);
+ safe_timer.add_event_at(t, test_contexts[i]);
+ }
+ safe_timer_lock.unlock();
+
+ // cancel the even-numbered events
+ for (int i = 0; i < MAX_TEST_CONTEXTS; i += 2) {
+ safe_timer_lock.lock();
+ safe_timer.cancel_event(test_contexts[i]);
+ safe_timer_lock.unlock();
+ }
+
+ sleep(20);
+
+ safe_timer_lock.lock();
+ safe_timer.cancel_all_events();
+ safe_timer_lock.unlock();
+
+ for (int i = 1; i < array_idx; i += 2) {
+ if (test_array[i] != i) {
+ ret = 1;
+ cout << "error: expected test_array[" << i << "] = " << i
+ << "; got " << test_array[i] << " instead." << std::endl;
+ }
+ }
+
+ return ret;
+}
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ int ret;
+ ceph::mutex safe_timer_lock = ceph::make_mutex("safe_timer_lock");
+ SafeTimer safe_timer(g_ceph_context, safe_timer_lock);
+ safe_timer.init();
+
+ ret = basic_timer_test <SafeTimer>(safe_timer, &safe_timer_lock);
+ if (ret)
+ goto done;
+
+ ret = safe_timer_cancel_all_test(safe_timer, safe_timer_lock);
+ if (ret)
+ goto done;
+
+ ret = safe_timer_cancellation_test(safe_timer, safe_timer_lock);
+ if (ret)
+ goto done;
+
+ ret = test_out_of_order_insertion(safe_timer, &safe_timer_lock);
+ if (ret)
+ goto done;
+
+done:
+ safe_timer.shutdown();
+ print_status(argv[0], ret);
+ return ret;
+}
diff --git a/src/test/admin_socket.cc b/src/test/admin_socket.cc
new file mode 100644
index 000000000..369e7abbf
--- /dev/null
+++ b/src/test/admin_socket.cc
@@ -0,0 +1,341 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "common/admin_socket.h"
+#include "common/admin_socket_client.h"
+#include "common/ceph_argparse.h"
+#include "gtest/gtest.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <sys/un.h>
+
+using namespace std;
+
+class AdminSocketTest
+{
+public:
+ explicit AdminSocketTest(AdminSocket *asokc)
+ : m_asokc(asokc)
+ {
+ }
+ bool init(const std::string &uri) {
+ return m_asokc->init(uri);
+ }
+ string bind_and_listen(const std::string &sock_path, int *fd) {
+ return m_asokc->bind_and_listen(sock_path, fd);
+ }
+ bool shutdown() {
+ m_asokc->shutdown();
+ return true;
+ }
+ AdminSocket *m_asokc;
+};
+
+TEST(AdminSocket, Teardown) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, TeardownSetup) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, SendHelp) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"help\"}", &help));
+ ASSERT_NE(string::npos, help.find("\"list available commands\""));
+ }
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{"
+ " \"prefix\":\"help\","
+ " \"format\":\"xml\","
+ "}", &help));
+ ASSERT_NE(string::npos, help.find(">list available commands<"));
+ }
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{"
+ " \"prefix\":\"help\","
+ " \"format\":\"UNSUPPORTED\","
+ "}", &help));
+ ASSERT_NE(string::npos, help.find("\"list available commands\""));
+ }
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, SendNoOp) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ string version;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"0\"}", &version));
+ ASSERT_EQ(CEPH_ADMIN_SOCK_VERSION, version);
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, SendTooLongRequest) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ string version;
+ string request(16384, 'a');
+ //if admin_socket cannot handle it, segfault will happened.
+ ASSERT_NE("", client.do_request(request, &version));
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+class MyTest : public AdminSocketHook {
+ int call(std::string_view command, const cmdmap_t& cmdmap,
+ const bufferlist&,
+ Formatter *f,
+ std::ostream& ss,
+ bufferlist& result) override {
+ std::vector<std::string> args;
+ TOPNSPC::common::cmd_getval(cmdmap, "args", args);
+ result.append(command);
+ result.append("|");
+ string resultstr;
+ for (std::vector<std::string>::iterator it = args.begin();
+ it != args.end(); ++it) {
+ if (it != args.begin())
+ resultstr += ' ';
+ resultstr += *it;
+ }
+ result.append(resultstr);
+ return 0;
+ }
+};
+
+TEST(AdminSocket, RegisterCommand) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ std::unique_ptr<AdminSocketHook> my_test_asok = std::make_unique<MyTest>();
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ ASSERT_EQ(0, asoct.m_asokc->register_command("test", my_test_asok.get(), ""));
+ string result;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\"}", &result));
+ ASSERT_EQ("test|", result);
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+class MyTest2 : public AdminSocketHook {
+ int call(std::string_view command, const cmdmap_t& cmdmap,
+ const bufferlist&,
+ Formatter *f,
+ std::ostream& ss,
+ bufferlist& result) override {
+ std::vector<std::string> args;
+ TOPNSPC::common::cmd_getval(cmdmap, "args", args);
+ result.append(command);
+ result.append("|");
+ string resultstr;
+ for (std::vector<std::string>::iterator it = args.begin();
+ it != args.end(); ++it) {
+ if (it != args.begin())
+ resultstr += ' ';
+ resultstr += *it;
+ }
+ result.append(resultstr);
+ ss << "error stream";
+ return 0;
+ }
+};
+
+TEST(AdminSocket, RegisterCommandPrefixes) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ std::unique_ptr<AdminSocketHook> my_test_asok = std::make_unique<MyTest>();
+ std::unique_ptr<AdminSocketHook> my_test2_asok = std::make_unique<MyTest2>();
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ ASSERT_EQ(0, asoct.m_asokc->register_command("test name=args,type=CephString,n=N", my_test_asok.get(), ""));
+ ASSERT_EQ(0, asoct.m_asokc->register_command("test command name=args,type=CephString,n=N", my_test2_asok.get(), ""));
+ string result;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\"}", &result));
+ ASSERT_EQ("test|", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\"}", &result));
+ ASSERT_EQ("test command|", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\",\"args\":[\"post\"]}", &result));
+ ASSERT_EQ("test command|post", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\",\"args\":[\" post\"]}", &result));
+ ASSERT_EQ("test command| post", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\"this thing\"]}", &result));
+ ASSERT_EQ("test|this thing", result);
+
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\" command post\"]}", &result));
+ ASSERT_EQ("test| command post", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\" this thing\"]}", &result));
+ ASSERT_EQ("test| this thing", result);
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+class BlockingHook : public AdminSocketHook {
+public:
+ ceph::mutex _lock = ceph::make_mutex("BlockingHook::_lock");
+ ceph::condition_variable _cond;
+
+ BlockingHook() = default;
+
+ int call(std::string_view command, const cmdmap_t& cmdmap,
+ const bufferlist&,
+ Formatter *f,
+ std::ostream& ss,
+ bufferlist& result) override {
+ std::unique_lock l{_lock};
+ _cond.wait(l);
+ return 0;
+ }
+};
+
+TEST(AdminSocketClient, Ping) {
+ string path = get_rand_socket_path();
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketClient client(path);
+ // no socket
+ {
+ bool ok;
+ std::string result = client.ping(&ok);
+#ifndef _WIN32
+// TODO: convert WSA errors.
+ EXPECT_NE(std::string::npos, result.find("No such file or directory"));
+#endif
+ ASSERT_FALSE(ok);
+ }
+ // file exists but does not allow connections (no process, wrong type...)
+ int fd = ::creat(path.c_str(), 0777);
+ ASSERT_TRUE(fd);
+ // On Windows, we won't be able to remove the file unless we close it
+ // first.
+ ASSERT_FALSE(::close(fd));
+ {
+ bool ok;
+ std::string result = client.ping(&ok);
+#ifndef _WIN32
+#if defined(__APPLE__) || defined(__FreeBSD__)
+ const char* errmsg = "Socket operation on non-socket";
+#else
+ const char* errmsg = "Connection refused";
+#endif
+ EXPECT_NE(std::string::npos, result.find(errmsg));
+#endif /* _WIN32 */
+ ASSERT_FALSE(ok);
+ }
+ // a daemon is connected to the socket
+ {
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_TRUE(asoct.init(path));
+ bool ok;
+ std::string result = client.ping(&ok);
+ EXPECT_EQ("", result);
+ ASSERT_TRUE(ok);
+ ASSERT_TRUE(asoct.shutdown());
+ }
+ // hardcoded five seconds timeout prevents infinite blockage
+ {
+ AdminSocketTest asoct(asokc.get());
+ BlockingHook *blocking = new BlockingHook();
+ ASSERT_EQ(0, asoct.m_asokc->register_command("0", blocking, ""));
+ ASSERT_TRUE(asoct.init(path));
+ bool ok;
+ std::string result = client.ping(&ok);
+ #ifndef _WIN32
+ EXPECT_NE(std::string::npos, result.find("Resource temporarily unavailable"));
+ #endif
+ ASSERT_FALSE(ok);
+ {
+ std::lock_guard l{blocking->_lock};
+ blocking->_cond.notify_all();
+ }
+ ASSERT_TRUE(asoct.shutdown());
+ delete blocking;
+ }
+}
+
+TEST(AdminSocket, bind_and_listen) {
+ string path = get_rand_socket_path();
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+
+ AdminSocketTest asoct(asokc.get());
+ // successfull bind
+ {
+ int fd = 0;
+ string message;
+ message = asoct.bind_and_listen(path, &fd);
+ ASSERT_NE(0, fd);
+ ASSERT_EQ("", message);
+ ASSERT_EQ(0, ::compat_closesocket(fd));
+ ASSERT_EQ(0, ::unlink(path.c_str()));
+ }
+ // silently discard an existing file
+ {
+ int fd = 0;
+ string message;
+ int fd2 = ::creat(path.c_str(), 0777);
+ ASSERT_TRUE(fd2);
+ // On Windows, we won't be able to remove the file unless we close it
+ // first.
+ ASSERT_FALSE(::close(fd2));
+ message = asoct.bind_and_listen(path, &fd);
+ ASSERT_NE(0, fd);
+ ASSERT_EQ("", message);
+ ASSERT_EQ(0, ::compat_closesocket(fd));
+ ASSERT_EQ(0, ::unlink(path.c_str()));
+ }
+ // do not take over a live socket
+ {
+ ASSERT_TRUE(asoct.init(path));
+ int fd = 0;
+ string message;
+ message = asoct.bind_and_listen(path, &fd);
+ std::cout << "message: " << message << std::endl;
+ EXPECT_NE(std::string::npos, message.find("File exists"));
+ ASSERT_TRUE(asoct.shutdown());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd .. ;
+ * make unittest_admin_socket &&
+ * valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_admin_socket --debug-asok 20 # --gtest_filter=AdminSocket*.*
+ * "
+ * End:
+ */
+
diff --git a/src/test/admin_socket/objecter_requests b/src/test/admin_socket/objecter_requests
new file mode 100755
index 000000000..c4c9edf96
--- /dev/null
+++ b/src/test/admin_socket/objecter_requests
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+
+import json
+import sys
+
+
+def main():
+ """
+ Read json output of admin socket command 'objecter_requests' from
+ stdin, and check it for internal consistency and presence of
+ fields.
+ """
+ read = sys.stdin.read()
+ reqs = json.loads(read)
+
+ op_types = ['linger_ops', 'ops', 'pool_ops', 'pool_stat_ops', 'statfs_ops', 'command_ops']
+ assert sorted(reqs.keys()) == sorted(op_types)
+
+ found_error = check_osd_ops(reqs['ops'] + reqs['linger_ops'])
+ assert not found_error, "ERRORS FOUND!"
+
+
+def check_osd_ops(ops):
+ pg_map = {}
+ locators = {}
+ osds = {}
+ found_error = [False]
+
+ def add_to_mapping(mapping, key, value, msg):
+ if key in mapping:
+ if mapping[key] != value:
+ print('%s != %s' % (mapping[key], value))
+ print(msg)
+ found_error[0] = True
+ else:
+ mapping[key] = value
+
+ for op in ops:
+ add_to_mapping(
+ mapping=pg_map,
+ key=(op['object_id'], op['object_locator']),
+ value=op['pg'],
+ msg='ERROR: two ops for the same object mapped to different pgs',
+ )
+ add_to_mapping(
+ mapping=locators,
+ key=op['object_id'],
+ value=op['object_locator'],
+ msg='ERROR: requests to the same object had different locators',
+ )
+ add_to_mapping(
+ mapping=osds,
+ key=op['pg'],
+ value=op['osd'],
+ msg='ERROR: two ops mapped a pg to different osds',
+ )
+ return found_error[0]
+
+if __name__ == '__main__':
+ main()
diff --git a/src/test/admin_socket/osd_requests b/src/test/admin_socket/osd_requests
new file mode 100644
index 000000000..4da514e6f
--- /dev/null
+++ b/src/test/admin_socket/osd_requests
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+
+import json
+import sys
+
+def main():
+ """
+ Read json output of admin socket command 'dump_ops_in_flight' from
+ stdin, and check that it is consistent.
+ """
+ read = sys.stdin.read()
+ records = json.loads(read)
+
+ info_types = ['num_ops', 'ops']
+ assert sorted(records.keys()) == sorted(info_types)
+ assert(records['num_ops'] == len(records['ops']))
+
+ for op in records['ops']:
+ assert op['description'] is not None
+ assert op['received_at'] is not None
+ assert op['age'] is not None
+ assert op['flag_point'] is not None
+ if op['client_info']:
+ assert op['client_info']['client'] is not None
+ assert op['client_info']['tid'] is not None
+
+if __name__ == '__main__':
+ main()
diff --git a/src/test/admin_socket_output.cc b/src/test/admin_socket_output.cc
new file mode 100644
index 000000000..1ce69094d
--- /dev/null
+++ b/src/test/admin_socket_output.cc
@@ -0,0 +1,242 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <regex> // For regex, regex_search
+
+#include "common/admin_socket_client.h" // For AdminSocketClient
+#include "common/ceph_json.h" // For JSONParser, JSONObjIter
+#include "include/buffer.h" // For bufferlist
+
+#include "admin_socket_output.h"
+
+using namespace std;
+
+void AdminSocketOutput::add_target(const std::string& target) {
+ if (target == "all") {
+ add_target("osd");
+ add_target("mon");
+ add_target("mgr");
+ add_target("mds");
+ add_target("client");
+ return;
+ }
+ targets.insert(target);
+}
+
+void AdminSocketOutput::add_command(const std::string& target,
+ const std::string& command) {
+ auto seek = custom_commands.find(target);
+ if (seek != custom_commands.end()) {
+ seek->second.push_back(command);
+ } else {
+ std::vector<std::string> vec;
+ vec.push_back(command);
+ custom_commands.insert(std::make_pair(target, vec));
+ }
+
+}
+
+void AdminSocketOutput::add_test(const std::string &target,
+ const std::string &command,
+ bool (*test)(std::string &)) {
+ auto seek = tests.find(target);
+ if (seek != tests.end()) {
+ seek->second.push_back(std::make_pair(command, test));
+ } else {
+ std::vector<std::pair<std::string, bool (*)(std::string &)>> vec;
+ vec.push_back(std::make_pair(command, test));
+ tests.insert(std::make_pair(target, vec));
+ }
+}
+
+void AdminSocketOutput::postpone(const std::string &target,
+ const std::string& command) {
+ auto seek = postponed_commands.find(target);
+ if (seek != postponed_commands.end()) {
+ seek->second.push_back(command);
+ } else {
+ std::vector<string> vec;
+ vec.push_back(command);
+ postponed_commands.insert(std::make_pair(target, vec));
+ }
+}
+
+bool AdminSocketOutput::init_sockets() {
+ std::cout << "Initialising sockets" << std::endl;
+ std::string socket_regex = R"(\..*\.asok)";
+ for (const auto &x : fs::recursive_directory_iterator(socketdir)) {
+ std::cout << x.path() << std::endl;
+ if (x.path().extension() == ".asok") {
+ for (auto target = targets.cbegin(); target != targets.cend();) {
+ std::regex reg(prefix + *target + socket_regex);
+ if (std::regex_search(x.path().filename().string(), reg)) {
+ std::cout << "Found " << *target << " socket " << x.path()
+ << std::endl;
+ sockets.insert(std::make_pair(*target, x.path().string()));
+ target = targets.erase(target);
+ }
+ else {
+ ++target;
+ }
+ }
+ if (targets.empty()) {
+ std::cout << "Found all required sockets" << std::endl;
+ break;
+ }
+ }
+ }
+
+ return !sockets.empty() && targets.empty();
+}
+
+std::pair<std::string, std::string>
+AdminSocketOutput::run_command(AdminSocketClient &client,
+ const std::string &raw_command,
+ bool send_untouched) {
+ std::cout << "Sending command \"" << raw_command << "\"" << std::endl;
+ std::string command;
+ std::string output;
+ if (send_untouched) {
+ command = raw_command;
+ } else {
+ command = "{\"prefix\":\"" + raw_command + "\"}";
+ }
+ std::string err = client.do_request(command, &output);
+ if (!err.empty()) {
+ std::cerr << __func__ << " AdminSocketClient::do_request errored with: "
+ << err << std::endl;
+ ceph_abort();
+ }
+ return std::make_pair(command, output);
+}
+
+bool AdminSocketOutput::gather_socket_output() {
+
+ std::cout << "Gathering socket output" << std::endl;
+ for (const auto& socket : sockets) {
+ std::string response;
+ AdminSocketClient client(socket.second);
+ std::cout << std::endl
+ << "Sending request to " << socket << std::endl
+ << std::endl;
+ std::string err = client.do_request("{\"prefix\":\"help\"}", &response);
+ if (!err.empty()) {
+ std::cerr << __func__ << " AdminSocketClient::do_request errored with: "
+ << err << std::endl;
+ return false;
+ }
+ std::cout << response << '\n';
+
+ JSONParser parser;
+ bool ret = parser.parse(response.c_str(), response.size());
+ if (!ret) {
+ cerr << "parse error" << std::endl;
+ return false;
+ }
+
+ socket_results sresults;
+ JSONObjIter iter = parser.find_first();
+ const auto postponed_iter = postponed_commands.find(socket.first);
+ std::vector<std::string> postponed;
+ if (postponed_iter != postponed_commands.end()) {
+ postponed = postponed_iter->second;
+ }
+ std::cout << "Sending commands to " << socket.first << " socket"
+ << std::endl;
+ for (; !iter.end(); ++iter) {
+ if (std::find(postponed.begin(), postponed.end(), (*iter)->get_name())
+ != std::end(postponed)) {
+ std::cout << "Command \"" << (*iter)->get_name() << "\" postponed"
+ << std::endl;
+ continue;
+ }
+ sresults.insert(run_command(client, (*iter)->get_name()));
+ }
+
+ if (sresults.empty()) {
+ return false;
+ }
+
+ // Custom commands
+ const auto seek = custom_commands.find(socket.first);
+ if (seek != custom_commands.end()) {
+ std::cout << std::endl << "Sending custom commands:" << std::endl;
+ for (const auto& raw_command : seek->second) {
+ sresults.insert(run_command(client, raw_command, true));
+ }
+ }
+
+ // Postponed commands
+ if (!postponed.empty())
+ std::cout << std::endl << "Sending postponed commands" << std::endl;
+ for (const auto& command : postponed) {
+ sresults.insert(run_command(client, command));
+ }
+
+ results.insert(
+ std::pair<std::string, socket_results>(socket.first, sresults));
+
+ }
+
+ return true;
+}
+
+std::string AdminSocketOutput::get_result(const std::string &target,
+ const std::string &command) const {
+ const auto& target_results = results.find(target);
+ if (target_results == results.end())
+ return std::string("");
+ else {
+ const auto& result = target_results->second.find(command);
+ if (result == target_results->second.end())
+ return std::string("");
+ else
+ return result->second;
+ }
+}
+
+bool AdminSocketOutput::run_tests() const {
+ for (const auto& socket : sockets) {
+ const auto& seek = tests.find(socket.first);
+ if (seek != tests.end()) {
+ std::cout << std::endl;
+ std::cout << "Running tests for " << socket.first << " socket" << std::endl;
+ for (const auto& test : seek->second) {
+ auto result = get_result(socket.first, test.first);
+ if(result.empty()) {
+ std::cout << "Failed to find result for command: " << test.first << std::endl;
+ return false;
+ } else {
+ std::cout << "Running test for command: " << test.first << std::endl;
+ const auto& test_func = test.second;
+ bool res = test_func(result);
+ if (res == false)
+ return false;
+ else
+ std::cout << "Test passed" << std::endl;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void AdminSocketOutput::exec() {
+ ceph_assert(init_directories());
+ ceph_assert(init_sockets());
+ ceph_assert(gather_socket_output());
+ ceph_assert(run_tests());
+}
diff --git a/src/test/admin_socket_output.h b/src/test/admin_socket_output.h
new file mode 100644
index 000000000..1df12e4a9
--- /dev/null
+++ b/src/test/admin_socket_output.h
@@ -0,0 +1,76 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_ADMIN_SOCKET_OUTPUT_H
+#define CEPH_ADMIN_SOCKET_OUTPUT_H
+
+#include <filesystem>
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+
+namespace fs = std::filesystem;
+
+using socket_results = std::map<std::string, std::string>;
+using test_functions =
+ std::vector<std::pair<std::string, bool (*)(std::string &)>>;
+
+class AdminSocketClient;
+
+class AdminSocketOutput {
+public:
+ AdminSocketOutput() {}
+
+ void add_target(const std::string &target);
+ void add_command(const std::string &target, const std::string &command);
+ void add_test(const std::string &target, const std::string &command,
+ bool (*test)(std::string &));
+ void postpone(const std::string &target, const std::string &command);
+
+ void exec();
+
+ void mod_for_vstart(const std::string& dir) {
+ socketdir = dir;
+ prefix = "";
+ }
+
+private:
+ bool init_directories() const {
+ std::cout << "Checking " << socketdir << std::endl;
+ return exists(socketdir) && is_directory(socketdir);
+ }
+
+ bool init_sockets();
+ bool gather_socket_output();
+ std::string get_result(const std::string &target, const std::string &command) const;
+
+ std::pair<std::string, std::string>
+ run_command(AdminSocketClient &client, const std::string &raw_command,
+ bool send_untouched = false);
+
+ bool run_tests() const;
+
+ std::set<std::string> targets;
+ std::map<std::string, std::string> sockets;
+ std::map<std::string, socket_results> results;
+ std::map<std::string, std::vector<std::string>> custom_commands;
+ std::map<std::string, std::vector<std::string>> postponed_commands;
+ std::map<std::string, test_functions> tests;
+
+ std::string prefix = "ceph-";
+ fs::path socketdir = "/var/run/ceph";
+};
+
+#endif // CEPH_ADMIN_SOCKET_OUTPUT_H
diff --git a/src/test/admin_socket_output_tests.cc b/src/test/admin_socket_output_tests.cc
new file mode 100644
index 000000000..5125a7db2
--- /dev/null
+++ b/src/test/admin_socket_output_tests.cc
@@ -0,0 +1,107 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <string>
+#include <iostream>
+
+#include "common/ceph_json.h"
+
+// Test functions
+
+// Example test function
+/*
+bool test_config_get_admin_socket(std::string& output) {
+ return std::string::npos != output.find("admin_socket") &&
+ std::string::npos != output.rfind(".asok");
+}
+*/
+
+bool test_dump_pgstate_history(std::string &output) {
+ JSONParser parser;
+ bool ret = parser.parse(output.c_str(), output.size());
+ if (!ret) {
+ std::cerr << "test_dump_pgstate_history: parse error" << std::endl;
+ return false;
+ }
+
+ JSONObjIter iterone = parser.find_first();
+ if (iterone.end()) { //Empty
+ std::cerr << "test_dump_pgstate_history: command output empty, failing"
+ << std::endl;
+ return false;
+ }
+
+ unsigned int total = 0;
+ if ((*iterone)->get_name() == "pgs") {
+ JSONObjIter iter = (*(*iterone)->find_first())->find_first();
+ for (; !iter.end(); ++iter) {
+ if ((*iter)->get_name() == "pg") {
+ ret = !(*iter)->get_data().empty();
+ if (ret == false) {
+ std::cerr << "test_dump_pgstate_history: pg value empty, failing"
+ << std::endl;
+ std::cerr << "Dumping full output: " << std::endl;
+ std::cerr << output << std::endl;
+ break;
+ }
+ total++;
+ } else if ((*iter)->get_name() == "history") {
+ ret = std::string::npos != (*iter)->get_data().find("epoch") &&
+ std::string::npos != (*iter)->get_data().find("state") &&
+ std::string::npos != (*iter)->get_data().find("enter") &&
+ std::string::npos != (*iter)->get_data().find("exit");
+ if (ret == false) {
+ std::cerr << "test_dump_pgstate_history: Can't find expected values in "
+ "history object, failing"
+ << std::endl;
+ std::cerr << "Problem output was:" << std::endl;
+ std::cerr << (*iter)->get_data() << std::endl;
+ break;
+ }
+ total++;
+ } else if ((*iter)->get_name() == "currently") {
+ ret = !(*iter)->get_data().empty();
+ if (ret == false) {
+ std::cerr << "test_dump_pgstate_history: currently value empty, failing"
+ << std::endl;
+ std::cerr << "Dumping full output: " << std::endl;
+ std::cerr << output << std::endl;
+ break;
+ }
+ total++;
+ } else {
+ std::cerr << "test_dump_pgstate_history: unrecognised field " << (*iter)->get_name()
+ << ", failing" << std::endl;
+ std::cerr << "Dumping full output: " << std::endl;
+ std::cerr << output << std::endl;
+ break;
+ }
+ }
+ } else {
+ std::cerr << "test_dump_pgstate_history: unrecognised format, failing"
+ << std::endl;
+ std::cerr << "Dumping full output: " << std::endl;
+ std::cerr << output << std::endl;
+ return false;
+ }
+
+ if (total != 3) {
+ std::cerr << "Could not find required elements, failing" << std::endl;
+ std::cerr << "Dumping full output: " << std::endl;
+ std::cerr << output << std::endl;
+ return false;
+ }
+
+ return ret;
+}
diff --git a/src/test/admin_socket_output_tests.h b/src/test/admin_socket_output_tests.h
new file mode 100644
index 000000000..ef13b55dc
--- /dev/null
+++ b/src/test/admin_socket_output_tests.h
@@ -0,0 +1,28 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_ADMIN_SOCKET_OUTPUT_TESTS_H
+#define CEPH_ADMIN_SOCKET_OUTPUT_TESTS_H
+
+// Test function declarations, definitions in admin_socket_output_tests.cc
+
+// Example test function
+
+/*
+bool test_config_get_admin_socket(std::string& output);
+*/
+
+bool test_dump_pgstate_history(std::string& output);
+
+#endif // CEPH_ADMIN_SOCKET_OUTPUT_TESTS_H
diff --git a/src/test/barclass.cc b/src/test/barclass.cc
new file mode 100644
index 000000000..f5354f1e0
--- /dev/null
+++ b/src/test/barclass.cc
@@ -0,0 +1,48 @@
+
+
+
+#include <iostream>
+#include <string.h>
+#include <stdlib.h>
+
+#include "objclass/objclass.h"
+
+CLS_VER(1,0)
+CLS_NAME(bar)
+
+cls_handle_t h_class;
+
+cls_method_handle_t h_foo;
+
+int foo_method(cls_method_context_t ctx, char *indata, int datalen,
+ char **outdata, int *outdatalen)
+{
+ int i;
+
+ cls_log("hello world, this is bar");
+ cls_log("indata=%s", indata);
+
+ *outdata = (char *)malloc(128);
+ for (i=0; i<strlen(indata) + 1; i++) {
+ if (indata[i] == '0') {
+ (*outdata)[i] = '*';
+ } else {
+ (*outdata)[i] = indata[i];
+ }
+ }
+ *outdatalen = strlen(*outdata) + 1;
+ cls_log("outdata=%s", *outdata);
+
+ return 0;
+}
+
+void class_init()
+{
+ cls_log("Loaded bar class!");
+
+ cls_register("bar", &h_class);
+ cls_register_method(h_class, "bar", foo_method, &h_foo);
+
+ return;
+}
+
diff --git a/src/test/base64.cc b/src/test/base64.cc
new file mode 100644
index 000000000..48035118a
--- /dev/null
+++ b/src/test/base64.cc
@@ -0,0 +1,102 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 Dreamhost
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/armor.h"
+#include "common/config.h"
+#include "include/buffer.h"
+#include "include/encoding.h"
+
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(RoundTrip, SimpleRoundTrip) {
+ static const int OUT_LEN = 4096;
+ const char * const original = "abracadabra";
+ const char * const correctly_encoded = "YWJyYWNhZGFicmE=";
+ char out[OUT_LEN];
+ memset(out, 0, sizeof(out));
+ int alen = ceph_armor(out, out + OUT_LEN, original, original + strlen(original));
+ ASSERT_STREQ(correctly_encoded, out);
+
+ char out2[OUT_LEN];
+ memset(out2, 0, sizeof(out2));
+ ceph_unarmor(out2, out2 + OUT_LEN, out, out + alen);
+ ASSERT_STREQ(original, out2);
+}
+
+TEST(RoundTrip, RandomRoundTrips) {
+ static const int IN_MAX = 1024;
+ static const int OUT_MAX = 4096;
+ static const int ITERS = 1000;
+ for (int i = 0; i < ITERS; ++i) {
+ unsigned int seed = i;
+ int in_len = rand_r(&seed) % IN_MAX;
+
+ char in[IN_MAX];
+ memset(in, 0, sizeof(in));
+ for (int j = 0; j < in_len; ++j) {
+ in[j] = rand_r(&seed) % 0xff;
+ }
+ char out[OUT_MAX];
+ memset(out, 0, sizeof(out));
+ int alen = ceph_armor(out, out + OUT_MAX, in, in + in_len);
+ ASSERT_GE(alen, 0);
+
+ char decoded[IN_MAX];
+ memset(decoded, 0, sizeof(decoded));
+ int blen = ceph_unarmor(decoded, decoded + IN_MAX, out, out + alen);
+ ASSERT_GE(blen, 0);
+
+ ASSERT_EQ(memcmp(in, decoded, in_len), 0);
+ }
+}
+
+TEST(EdgeCase, EndsInNewline) {
+ static const int OUT_MAX = 4096;
+
+ char b64[] =
+ "aaaa\n";
+
+ char decoded[OUT_MAX];
+ memset(decoded, 0, sizeof(decoded));
+ int blen = ceph_unarmor(decoded, decoded + OUT_MAX, b64, b64 + sizeof(b64)-1);
+ ASSERT_GE(blen, 0);
+}
+
+TEST(FuzzEncoding, BadDecode1) {
+ static const int OUT_LEN = 4096;
+ const char * const bad_encoded = "FAKEBASE64 foo";
+ char out[OUT_LEN];
+ memset(out, 0, sizeof(out));
+ int alen = ceph_unarmor(out, out + OUT_LEN, bad_encoded, bad_encoded + strlen(bad_encoded));
+ ASSERT_LT(alen, 0);
+}
+
+TEST(FuzzEncoding, BadDecode2) {
+ string str("FAKEBASE64 foo");
+ bool failed = false;
+ try {
+ bufferlist bl;
+ bl.append(str);
+
+ bufferlist cl;
+ cl.decode_base64(bl);
+ cl.hexdump(std::cerr);
+ }
+ catch (const buffer::error &err) {
+ failed = true;
+ }
+ ASSERT_EQ(failed, true);
+}
diff --git a/src/test/behave_tests/README.md b/src/test/behave_tests/README.md
new file mode 100644
index 000000000..570d1af67
--- /dev/null
+++ b/src/test/behave_tests/README.md
@@ -0,0 +1,50 @@
+# Integration testing using the behave framework
+
+
+## Introduction
+
+Behave framework is based on the Behaviour driven development where the test cases defined using gherkin language (written natural language style). The test cases are defined in .feature files in `feature` directory and python implementation defined under `/feature/steps`.
+
+`features/environment.py` file is used to set up environment for testing the scenario using the kcli tool. When behave command is execute before each feature, kcli plan is generated to create the virtual machines.
+
+## Issues
+
+* We can't run the behave test cases via tox command.
+
+## Executing the behave tests
+
+We can execute all test scenario's by executing `behave` command under `src/test/behave_test` where `features` directory is required.
+
+```bash
+$ behave
+```
+
+## Executing the behave tests with tags
+
+Tag's can be used to execute only specific type of test scenario's.
+
+```bash
+$ behave -t <tag_name>
+```
+
+We have included the following tag for implemented test cases.
+* osd
+* ceph_shell
+* cephadm
+
+## Steps used to define the test scenarios
+
+Python implementation of steps are defined in `steps` directory under `src/test/behave_tests/features/`.
+Following implemented gherkin language steps used in `.feature` files to define the test scenarios.
+
+@given steps
+* __I log as root into {`vm_name`}__ (vm_name is name of virtual machine)
+* __I execute in {`shell`}__ (shell should defined as `host` or `cephadm_shell`)
+
+@when steps
+* __I execute in {`shell`}__
+
+@then steps
+* __I execute in {`shell`}__
+* __I wait for {`timeout`} seconds until I get__ (timeout should be defined in seconds)
+* __I get results which contain__
diff --git a/src/test/behave_tests/features/ceph_osd_test.feature b/src/test/behave_tests/features/ceph_osd_test.feature
new file mode 100644
index 000000000..e9a37a4c9
--- /dev/null
+++ b/src/test/behave_tests/features/ceph_osd_test.feature
@@ -0,0 +1,49 @@
+@osd
+Feature: Tests related to OSD creation
+ In order to be able to provide storage services
+ As an system administrator
+ I want to install a Ceph cluster in the following server infrastructure:
+ - 3 nodes with 8Gb RAM, 4 CPUs, and 3 storage devices of 20Gb each.
+ - Using Fedora32 image in each node
+ - Configure ceph cluster in following way
+ - with number of OSD 0
+
+
+ Scenario: Create OSDs
+ Given I log as root into ceph-node-00
+ When I execute in cephadm_shell
+ """
+ ceph orch device ls
+ """
+ Then I wait for 60 seconds until I get
+ """
+ ceph-node-00.cephlab.com /dev/vdb hdd Unknown N/A N/A Yes
+ ceph-node-01.cephlab.com /dev/vdb hdd Unknown N/A N/A Yes
+ ceph-node-02.cephlab.com /dev/vdb hdd Unknown N/A N/A Yes
+ """
+ Then I execute in cephadm_shell
+ """
+ ceph orch daemon add osd ceph-node-00.cephlab.com:/dev/vdb
+ ceph orch daemon add osd ceph-node-01.cephlab.com:/dev/vdb
+ ceph orch daemon add osd ceph-node-02.cephlab.com:/dev/vdb
+ """
+ Then I execute in cephadm_shell
+ """
+ ceph orch device ls
+ """
+ Then I wait for 60 seconds until I get
+ """
+ ceph-node-00.cephlab.com /dev/vdb hdd Unknown N/A N/A No
+ ceph-node-01.cephlab.com /dev/vdb hdd Unknown N/A N/A No
+ ceph-node-02.cephlab.com /dev/vdb hdd Unknown N/A N/A No
+ """
+ Then I execute in cephadm_shell
+ """
+ ceph -s
+ """
+ Then I get results which contain
+ """
+ services:
+ mon: 3 daemons, quorum ceph-node-00.cephlab.com,ceph-node-01,ceph-node-02
+ osd: 3 osds: 3 up
+ """
diff --git a/src/test/behave_tests/features/ceph_shell_test.feature b/src/test/behave_tests/features/ceph_shell_test.feature
new file mode 100644
index 000000000..b158093a0
--- /dev/null
+++ b/src/test/behave_tests/features/ceph_shell_test.feature
@@ -0,0 +1,64 @@
+@ceph_shell
+Feature: Testing basic ceph shell commands
+ In order to be able to provide storage services
+ As an system administrator
+ I want to install a Ceph cluster in the following server infrastructure:
+ - 3 nodes with 8Gb RAM, 4 CPUs, and 3 storage devices of 20Gb each.
+ - Using Fedora32 image in each node
+
+
+ Scenario: Execute ceph command to check status
+ Given I log as root into ceph-node-00
+ When I execute in cephadm_shell
+ """
+ ceph orch status
+ """
+ Then I get results which contain
+ """
+ Backend: cephadm
+ Available: Yes
+ Paused: No
+ """
+
+
+ Scenario: Execute ceph command to check orch host list
+ Given I log as root into ceph-node-00
+ When I execute in cephadm_shell
+ """
+ ceph orch host ls
+ """
+ Then I get results which contain
+ """
+ HOST LABELS
+ ceph-node-00.cephlab.com _admin
+ """
+
+
+ Scenario: Execute ceph command to check orch device list
+ Given I log as root into ceph-node-00
+ When I execute in cephadm_shell
+ """
+ ceph orch device ls
+ """
+ Then I get results which contain
+ """
+ Hostname Path Type
+ ceph-node-00.cephlab.com /dev/vdb hdd
+ ceph-node-00.cephlab.com /dev/vdc hdd
+ """
+
+
+ Scenario: Execute ceph command to check orch
+ Given I log as root into ceph-node-00
+ When I execute in cephadm_shell
+ """
+ ceph orch ls
+ """
+ Then I wait for 60 seconds until I get
+ """
+ NAME RUNNING
+ grafana 1/1
+ mgr 2/2
+ mon 1/5
+ prometheus 1/1
+ """
diff --git a/src/test/behave_tests/features/cephadm_test.feature b/src/test/behave_tests/features/cephadm_test.feature
new file mode 100644
index 000000000..e3358bfbd
--- /dev/null
+++ b/src/test/behave_tests/features/cephadm_test.feature
@@ -0,0 +1,24 @@
+@cephadm
+Feature: Install a basic Ceph cluster
+ In order to be able to provide storage services
+ As an system administrator
+ I want to install a Ceph cluster in the following server infrastructure:
+ - 3 nodes with 8Gb RAM, 4 CPUs, and 3 storage devices of 20Gb each.
+ - Using Fedora32 image in each node
+
+
+ Scenario: Execute commands in cluster nodes
+ Given I log as root into ceph-node-00
+ And I execute in host
+ """
+ curl --silent --remote-name --location https://raw.githubusercontent.com/ceph/ceph/octopus/src/cephadm/cephadm
+ chmod +x cephadm
+ """
+ When I execute in host
+ """
+ cephadm version
+ """
+ Then I get results which contain
+ """
+ ceph version quincy (dev)
+ """
diff --git a/src/test/behave_tests/features/environment.py b/src/test/behave_tests/features/environment.py
new file mode 100644
index 000000000..fdd175e60
--- /dev/null
+++ b/src/test/behave_tests/features/environment.py
@@ -0,0 +1,207 @@
+import logging
+import os
+import re
+
+from jinja2 import Template
+from kcli_handler import is_bootstrap_script_complete, execute_kcli_cmd
+
+KCLI_PLANS_DIR = "generated_plans"
+KCLI_PLAN_NAME = "behave_test_plan"
+
+Kcli_Config = {
+ "nodes": 1,
+ "pool": "default",
+ "network": "default",
+ "domain": "cephlab.com",
+ "prefix": "ceph",
+ "numcpus": 1,
+ "memory": 1024,
+ "image": "fedora33",
+ "notify": False,
+ "admin_password": "password",
+ "disks": [150, 3],
+}
+
+Bootstrap_Config = {
+ "configure_osd": False
+}
+
+
+def _write_file(file_path, data):
+ with open(file_path, "w") as file:
+ file.write(data)
+
+
+def _read_file(file_path):
+ file = open(file_path, "r")
+ data = "".join(file.readlines())
+ file.close()
+ return data
+
+
+def _loaded_templates():
+ temp_dir = os.path.join(os.getcwd(), "template")
+ logging.info("Loading templates")
+ kcli = _read_file(os.path.join(temp_dir, "kcli_plan_template"))
+ script = _read_file(os.path.join(temp_dir, "bootstrap_script_template"))
+ return (
+ Template(kcli),
+ Template(script)
+ )
+
+
+def _clean_generated(dir_path):
+ logging.info("Deleting generated files")
+ for file in os.listdir(dir_path):
+ os.remove(os.path.join(dir_path, file))
+ os.rmdir(dir_path)
+
+
+def _parse_value(value):
+ if value.isnumeric():
+ return int(value)
+
+ if value.endswith("gb"):
+ return int(value.replace("gb", "")) * 1024
+ elif value.endswith("mb"):
+ return value.replace("mb", "")
+ return value
+
+
+def _parse_to_config_dict(values, config):
+ for key in values.keys():
+ config[key] = _parse_value(values[key])
+
+
+def _parse_vm_description(specs):
+ """
+ Parse's vm specfication description into configuration dictionary
+ """
+ kcli_config = Kcli_Config.copy()
+ parsed_str = re.search(
+ r"(?P<nodes>[\d]+) nodes with (?P<memory>[\w\.-]+) ram",
+ specs.lower(),
+ )
+ if parsed_str:
+ for spec_key in parsed_str.groupdict().keys():
+ kcli_config[spec_key] = _parse_value(parsed_str.group(spec_key))
+ parsed_str = re.search(r"(?P<numcpus>[\d]+) cpus", specs.lower())
+ if parsed_str:
+ kcli_config["numcpus"] = parsed_str.group("numcpus")
+ parsed_str = re.search(
+ r"(?P<disk>[\d]+) storage devices of (?P<volume>[\w\.-]+)Gb each",
+ specs,
+ )
+ if parsed_str:
+ kcli_config["disks"] = [
+ _parse_value(parsed_str.group("volume"))
+ ] * _parse_value(parsed_str.group("disk"))
+ parsed_str = re.search(r"(?P<image>[\w\.-]+) image", specs.lower())
+ if parsed_str:
+ kcli_config["image"] = parsed_str.group("image")
+ return kcli_config
+
+
+def _parse_ceph_description(specs):
+ """
+ Parse the ceph boostrap script configuration descriptions.
+ """
+ bootstrap_script_config = Bootstrap_Config.copy()
+ parsed_str = re.search(
+ r"OSD (?P<osd>[\w\.-]+)", specs
+ )
+ if parsed_str:
+ bootstrap_script_config["configure_osd"] = True if _parse_value(
+ parsed_str.group("osd")
+ ) else False
+ return bootstrap_script_config
+
+
+def _handle_kcli_plan(command_type, plan_file_path=None):
+ """
+ Executes the kcli vm create and delete command according
+ to the provided configuration.
+ """
+ op = None
+ if command_type == "create":
+ # TODO : Before creating kcli plan check for exisitng kcli plans
+ op, code = execute_kcli_cmd(
+ f"create plan -f {plan_file_path} {KCLI_PLAN_NAME}"
+ )
+ if code:
+ print(f"Failed to create kcli plan\n Message: {op}")
+ exit(1)
+ elif command_type == "delete":
+ op, code = execute_kcli_cmd(f"delete plan {KCLI_PLAN_NAME} -y")
+ print(op)
+
+
+def has_ceph_configuration(descriptions, config_line):
+ """
+ Checks for ceph cluster configuration in descriptions.
+ """
+ index_config = -1
+ for line in descriptions:
+ if line.lower().startswith(config_line):
+ index_config = descriptions.index(line)
+
+ if index_config != -1:
+ return (
+ descriptions[:index_config],
+ descriptions[index_config:],
+ )
+ return (
+ descriptions,
+ None,
+ )
+
+
+def before_feature(context, feature):
+ kcli_plans_dir_path = os.path.join(
+ os.getcwd(),
+ KCLI_PLANS_DIR,
+ )
+ if not os.path.exists(kcli_plans_dir_path):
+ os.mkdir(kcli_plans_dir_path)
+
+ vm_description, ceph_description = has_ceph_configuration(
+ feature.description,
+ "- configure ceph cluster",
+ )
+ loaded_kcli, loaded_script = _loaded_templates()
+
+ vm_feature_specs = " ".join(
+ [line for line in vm_description if line.startswith("-")]
+ )
+ vm_config = _parse_vm_description("".join(vm_feature_specs))
+ kcli_plan_path = os.path.join(kcli_plans_dir_path, "gen_kcli_plan.yml")
+ print(f"Kcli vm configureaton \n {vm_config}")
+ _write_file(
+ kcli_plan_path,
+ loaded_kcli.render(vm_config)
+ )
+
+ # Checks for ceph description if None set the default configurations
+ ceph_config = _parse_ceph_description(
+ "".join(ceph_description)
+ ) if ceph_description else Bootstrap_Config
+
+ print(f"Bootstrap configuraton \n {ceph_config}\n")
+ _write_file(
+ os.path.join(kcli_plans_dir_path, "bootstrap_cluster_dev.sh"),
+ loaded_script.render(ceph_config),
+ )
+
+ _handle_kcli_plan("create", os.path.relpath(kcli_plan_path))
+
+ if not is_bootstrap_script_complete():
+ print("Failed to complete bootstrap..")
+ _handle_kcli_plan("delete")
+ exit(1)
+ context.last_executed = {}
+
+
+def after_feature(context, feature):
+ if os.path.exists(KCLI_PLANS_DIR):
+ _clean_generated(os.path.abspath(KCLI_PLANS_DIR))
+ _handle_kcli_plan("delete")
diff --git a/src/test/behave_tests/features/kcli_handler.py b/src/test/behave_tests/features/kcli_handler.py
new file mode 100644
index 000000000..1e28c7ff4
--- /dev/null
+++ b/src/test/behave_tests/features/kcli_handler.py
@@ -0,0 +1,88 @@
+import subprocess
+import time
+import os
+
+
+kcli_exec = r"""
+podman run --net host -it --rm --security-opt label=disable
+ -v $HOME/.ssh:/root/.ssh -v $HOME/.kcli:/root/.kcli
+ -v /var/lib/libvirt/images:/var/lib/libvirt/images
+ -v /var/run/libvirt:/var/run/libvirt -v $PWD:/workdir
+ -v /var/tmp:/ignitiondir jolmomar/kcli
+"""
+
+
+def _create_kcli_cmd(command):
+ cmd = kcli_exec.replace("$HOME", os.getenv("HOME"))
+ cmd = cmd.replace("$PWD", os.getenv("PWD"))
+ kcli = cmd.replace("\n", "").split(" ")
+ return kcli + command.split(" ")
+
+
+def is_bootstrap_script_complete():
+ """
+ Checks for status of bootstrap script executions.
+ """
+ timeout = 0
+ command = " ".join(
+ [
+ f'"{cmd}"' for cmd in
+ "journalctl --no-tail --no-pager -t cloud-init".split(" ")
+ ]
+ )
+ cmd = _create_kcli_cmd(
+ f'ssh ceph-node-00 {command} | grep "Bootstrap complete."'
+ )
+ while timeout < 10: # Totally waits for 5 mins before giving up
+ proc = subprocess.run(cmd, capture_output=True, text=True)
+ if "Bootstrap complete." in proc.stdout:
+ print("Bootstrap script completed successfully")
+ return True
+ timeout += 1
+ print("Waiting for bootstrap_cluster script...")
+ print(proc.stdout[len(proc.stdout) - 240:])
+ time.sleep(30)
+ print(
+ f"Timeout reached {30*timeout}. Giving up for boostrap to complete"
+ )
+ return False
+
+
+def execute_kcli_cmd(command):
+ """
+ Executes the kcli command by combining the provided command
+ with kcli executable command.
+ """
+ cmd = _create_kcli_cmd(command)
+ print(f"Executing kcli command : {command}")
+ try:
+ proc = subprocess.run(
+ cmd,
+ capture_output=True,
+ text=True,
+ # env=dict(STORAGE_OPTS=''),
+ )
+ except Exception as ex:
+ print(f"Error executing kcli command\n{ex}")
+
+ op = proc.stderr if proc.stderr else proc.stdout
+ return (op, proc.returncode)
+
+
+def execute_ssh_cmd(vm_name, shell, command):
+ """
+ Executes the provided ssh command on the provided vm machine
+ """
+ if shell == "cephadm_shell":
+ command = f"cephadm shell {command}"
+ sudo_cmd = f"sudo -i {command}".split(" ")
+ sudo_cmd = " ".join([f'"{cmd}"' for cmd in sudo_cmd])
+ cmd = _create_kcli_cmd(f"ssh {vm_name} {sudo_cmd}")
+ print(f"Executing ssh command : {cmd}")
+ try:
+ proc = subprocess.run(cmd, capture_output=True, text=True)
+ except Exception as ex:
+ print(f"Error executing ssh command: {ex}")
+
+ op = proc.stderr if proc.stderr else proc.stdout
+ return (op, proc.returncode)
diff --git a/src/test/behave_tests/features/steps/ceph_steps.py b/src/test/behave_tests/features/steps/ceph_steps.py
new file mode 100644
index 000000000..a96aa48ad
--- /dev/null
+++ b/src/test/behave_tests/features/steps/ceph_steps.py
@@ -0,0 +1,106 @@
+import time
+
+from behave import given, when, then
+from kcli_handler import execute_ssh_cmd
+from validation_util import str_to_list
+
+
+@given("I log as root into {node}")
+def login_to_node(context, node):
+ context.node = node
+
+
+@given("I execute in {shell}")
+def init_step_execute(context, shell):
+ commands = context.text.split("\n")
+ for command in commands:
+ op, code = execute_ssh_cmd(context.node, shell, command)
+ if code:
+ raise Exception("Failed to execute")
+ context.last_executed["cmd"] = command
+ context.last_executed["shell"] = shell
+
+
+@when("I execute in {shell}")
+@then("I execute in {shell}")
+def execute_step(context, shell):
+ if context.node is None:
+ raise Exception("Failed not logged into virtual machine")
+ for command in context.text.split("\n"):
+ output, return_code = execute_ssh_cmd(context.node, shell, command)
+ context.last_executed["cmd"] = command
+ context.last_executed["shell"] = shell
+ if return_code != 0:
+ raise Exception(f"Failed to execute ssh\n Message:{output}")
+ context.output = str_to_list(output)
+ print(f"Executed output : {context.output}")
+
+
+@then("Execute in {shell} only {command}")
+def execute_only_one_step(context, shell, command):
+ """
+ Run's single command and doesn't use multi-line
+ :params command: given command to execute
+ """
+ if context.node is None:
+ raise Exception("Failed not logged into virtual machine")
+ output, return_code = execute_ssh_cmd(context.node, shell, command)
+ context.last_executed["cmd"] = command
+ context.last_executed["shell"] = shell
+ if return_code != 0:
+ raise Exception(f"Failed to execute ssh\nMessage:{output}")
+ context.output = str_to_list(output)
+ print(f"Executed output : {context.output}")
+
+
+@then("I wait for {time_out:n} seconds until I get")
+def execute_and_wait_until_step(context, time_out):
+ wait_time = int(time_out/4)
+ context.found_all_keywords = False
+ if context.node is None:
+ raise Exception("Failed not logged into virtual machine")
+ exec_shell = context.last_executed['shell']
+ exec_cmd = context.last_executed['cmd']
+ if exec_shell is None and exec_cmd is None:
+ raise Exception("Last executed command not found..")
+
+ expected_output = str_to_list(context.text)
+ while wait_time < time_out and not context.found_all_keywords:
+ found_keys = []
+ context.execute_steps(
+ f"then Execute in {exec_shell} only {exec_cmd}"
+ )
+
+ executed_output = context.output
+ for expected_line in expected_output:
+ for op_line in executed_output:
+ if set(expected_line).issubset(set(op_line)):
+ found_keys.append(" ".join(expected_line))
+
+ if len(found_keys) != len(expected_output):
+ print(f"Waiting for {int(time_out/4)} seconds")
+ time.sleep(int(time_out/4))
+ wait_time += int(time_out/4)
+ else:
+ print("Found all expected keywords")
+ context.found_all_keywords = True
+ break
+ if not context.found_all_keywords:
+ print(
+ f"Timeout reached {time_out}. Giving up on waiting for keywords"
+ )
+
+
+@then("I get results which contain")
+def validation_step(context):
+ expected_keywords = str_to_list(context.text)
+ output_lines = context.output
+
+ for keys_line in expected_keywords:
+ found_keyword = False
+ for op_line in output_lines:
+ if set(keys_line).issubset(set(op_line)):
+ found_keyword = True
+ output_lines.remove(op_line)
+ if not found_keyword:
+ assert False, f"Not found {keys_line}"
diff --git a/src/test/behave_tests/features/validation_util.py b/src/test/behave_tests/features/validation_util.py
new file mode 100644
index 000000000..abe441462
--- /dev/null
+++ b/src/test/behave_tests/features/validation_util.py
@@ -0,0 +1,19 @@
+
+def str_to_list(string):
+ """
+ Converts the string into list removing whitespaces
+ """
+ string = string.replace('\t', '\n')
+ return [
+ [
+ key for key in line.split(' ')
+ if key != ''
+ ]
+ for line in string.split('\n')
+ if line != ''
+ ]
+
+
+def assert_str_in_list(keyword_list, output_list):
+ for keyword in keyword_list:
+ assert keyword in output_list, f" Not found {keyword}"
diff --git a/src/test/behave_tests/template/bootstrap_script_template b/src/test/behave_tests/template/bootstrap_script_template
new file mode 100644
index 000000000..de2129e76
--- /dev/null
+++ b/src/test/behave_tests/template/bootstrap_script_template
@@ -0,0 +1,30 @@
+export PATH=/root/bin:$PATH
+mkdir /root/bin
+
+CEPHADM="/root/bin/cephadm"
+
+{% raw %}
+{% if ceph_dev_folder is defined %}
+ /mnt/{{ ceph_dev_folder }}/src/cephadm/build.sh $CEPHADM
+{% else %}
+ curl --silent -o $CEPHADM --location https://raw.githubusercontent.com/ceph/ceph/main/src/cephadm/cephadm.py
+{% endif %}
+chmod +x $CEPHADM
+mkdir -p /etc/ceph
+mon_ip=$(ifconfig eth0 | grep 'inet ' | awk '{ print $2}')
+{% if ceph_dev_folder is defined %}
+ echo "ceph_dev_folder is defined"
+ $CEPHADM bootstrap --mon-ip $mon_ip --initial-dashboard-password {{ admin_password }} --allow-fqdn-hostname --dashboard-password-noupdate --shared_ceph_folder /mnt/{{ ceph_dev_folder }}
+{% else %}
+echo "ceph_dev_folder is not defined"
+ $CEPHADM bootstrap --mon-ip $mon_ip --initial-dashboard-password {{ admin_password }} --allow-fqdn-hostname --dashboard-password-noupdate
+{% endif %}
+fsid=$(cat /etc/ceph/ceph.conf | grep fsid | awk '{ print $3}')
+{% for number in range(1, nodes) %}
+ ssh-copy-id -f -i /etc/ceph/ceph.pub -o StrictHostKeyChecking=no root@{{ prefix }}-node-0{{ number }}.{{ domain }}
+ $CEPHADM shell --fsid $fsid -c /etc/ceph/ceph.conf -k /etc/ceph/ceph.client.admin.keyring ceph orch host add {{ prefix }}-node-0{{ number }}.{{ domain }}
+{% endfor %}
+{% endraw %}
+{% if configure_osd %}
+$CEPHADM shell --fsid $fsid -c /etc/ceph/ceph.conf -k /etc/ceph/ceph.client.admin.keyring ceph orch apply osd --all-available-devices
+{% endif %}
diff --git a/src/test/behave_tests/template/kcli_plan_template b/src/test/behave_tests/template/kcli_plan_template
new file mode 100644
index 000000000..b28ee0568
--- /dev/null
+++ b/src/test/behave_tests/template/kcli_plan_template
@@ -0,0 +1,41 @@
+parameters:
+ nodes: {{ nodes }}
+ pool: default
+ network: default
+ domain: cephlab.com
+ prefix: ceph
+ numcpus: {{ numcpus }}
+ memory: {{ memory }}
+ image: {{ image }}
+ notify: false
+ admin_password: password
+ disks: {{ disks }}
+
+{% raw %}
+{% for number in range(0, nodes) %}
+{{ prefix }}-node-0{{ number }}:
+ image: {{ image }}
+ numcpus: {{ numcpus }}
+ memory: {{ memory }}
+ reserveip: true
+ reservedns: true
+ sharedkey: true
+ domain: {{ domain }}
+ nets:
+ - {{ network }}
+ disks: {{ disks }}
+ pool: {{ pool }}
+ {% if ceph_dev_folder is defined %}
+ sharedfolders: [{{ ceph_dev_folder }}]
+ {% endif %}
+ cmds:
+ - yum -y install python3 chrony lvm2 podman
+ - sed -i "s/SELINUX=enforcing/SELINUX=permissive/" /etc/selinux/config
+ - echo "after installing the python3"
+ - setenforce 0
+ {% if number == 0 %}
+ scripts:
+ - bootstrap_cluster_dev.sh
+ {% endif %}
+{% endfor %}
+{% endraw %} \ No newline at end of file
diff --git a/src/test/behave_tests/tox.ini b/src/test/behave_tests/tox.ini
new file mode 100644
index 000000000..24e4e3c37
--- /dev/null
+++ b/src/test/behave_tests/tox.ini
@@ -0,0 +1,22 @@
+[tox]
+envlist = py39, flake8
+skipsdist = true
+
+[base]
+setenv =
+ HOME = /root
+ PWD = {toxinidir}
+
+[testenv]
+setenv =
+ {[base]setenv}
+deps =
+ behave
+ jinja2
+# run the behave tests
+commands = behave
+
+[testenv:flake8]
+deps =
+ flake8==3.9.2
+commands = flake8 --statistics {posargs} features/ \ No newline at end of file
diff --git a/src/test/bench_journald_logger.cc b/src/test/bench_journald_logger.cc
new file mode 100644
index 000000000..97341f0e7
--- /dev/null
+++ b/src/test/bench_journald_logger.cc
@@ -0,0 +1,20 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/Journald.h"
+#include "log/Entry.h"
+#include "log/SubsystemMap.h"
+
+using namespace ceph::logging;
+
+int main()
+{
+ SubsystemMap subs;
+ JournaldLogger journald(&subs);
+
+ for (int i = 0; i < 100000; i++) {
+ MutableEntry entry(0, 0);
+ entry.get_ostream() << "This is log message " << i << ", which is a little bit looooooooo********ooooooooog and may contains multiple\nlines.";
+ journald.log_entry(entry);
+ }
+}
diff --git a/src/test/bench_log.cc b/src/test/bench_log.cc
new file mode 100644
index 000000000..60fda462e
--- /dev/null
+++ b/src/test/bench_log.cc
@@ -0,0 +1,86 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "common/Thread.h"
+#include "common/debug.h"
+#include "common/Clock.h"
+#include "common/config.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+
+#define dout_context g_ceph_context
+
+using namespace std;
+
+struct T : public Thread {
+ int num;
+ set<int> myset;
+ map<int,string> mymap;
+ explicit T(int n) : num(n) {
+ myset.insert(123);
+ myset.insert(456);
+ mymap[1] = "foo";
+ mymap[10] = "bar";
+ }
+
+ void *entry() override {
+ while (num-- > 0)
+ generic_dout(0) << "this is a typical log line. set "
+ << myset << " and map " << mymap << dendl;
+ return 0;
+ }
+};
+
+void usage(const char *name) {
+ cout << name << " <threads> <lines>\n"
+ << "\t threads: the number of threads for this test.\n"
+ << "\t lines: the number of log entries per thread.\n";
+}
+
+int main(int argc, const char **argv)
+{
+ if (argc < 3) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ int threads = atoi(argv[1]);
+ int num = atoi(argv[2]);
+
+ cout << threads << " threads, " << num << " lines per thread" << std::endl;
+
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_OSD,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+
+ utime_t start = ceph_clock_now();
+
+ list<T*> ls;
+ for (int i=0; i<threads; i++) {
+ T *t = new T(num);
+ t->create("t");
+ ls.push_back(t);
+ }
+
+ for (int i=0; i<threads; i++) {
+ T *t = ls.front();
+ ls.pop_front();
+ t->join();
+ delete t;
+ }
+
+ utime_t t = ceph_clock_now();
+ t -= start;
+ cout << " flushing.. " << t << " so far ..." << std::endl;
+
+ g_ceph_context->_log->flush();
+
+ utime_t end = ceph_clock_now();
+ utime_t dur = end - start;
+
+ cout << dur << std::endl;
+ return 0;
+}
diff --git a/src/test/bufferlist.cc b/src/test/bufferlist.cc
new file mode 100644
index 000000000..013335d81
--- /dev/null
+++ b/src/test/bufferlist.cc
@@ -0,0 +1,3083 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <limits.h>
+#include <errno.h>
+#include <sys/uio.h>
+
+#include "include/buffer.h"
+#include "include/buffer_raw.h"
+#include "include/compat.h"
+#include "include/utime.h"
+#include "include/coredumpctl.h"
+#include "include/encoding.h"
+#include "common/buffer_instrumentation.h"
+#include "common/environment.h"
+#include "common/Clock.h"
+#include "common/safe_io.h"
+
+#include "gtest/gtest.h"
+#include "stdlib.h"
+#include "fcntl.h"
+#include "sys/stat.h"
+#include "include/crc32c.h"
+#include "common/sctp_crc32.h"
+
+#define MAX_TEST 1000000
+#define FILENAME "bufferlist"
+
+using namespace std;
+
+static char cmd[128];
+
+using ceph::buffer_instrumentation::instrumented_bptr;
+
+TEST(Buffer, constructors) {
+ unsigned len = 17;
+ //
+ // buffer::create
+ //
+ {
+ bufferptr ptr(buffer::create(len));
+ EXPECT_EQ(len, ptr.length());
+ }
+ //
+ // buffer::claim_char
+ //
+ {
+ char* str = new char[len];
+ ::memset(str, 'X', len);
+ bufferptr ptr(buffer::claim_char(len, str));
+ EXPECT_EQ(len, ptr.length());
+ EXPECT_EQ(str, ptr.c_str());
+ EXPECT_EQ(0, ::memcmp(str, ptr.c_str(), len));
+ delete [] str;
+ }
+ //
+ // buffer::create_static
+ //
+ {
+ char* str = new char[len];
+ bufferptr ptr(buffer::create_static(len, str));
+ EXPECT_EQ(len, ptr.length());
+ EXPECT_EQ(str, ptr.c_str());
+ delete [] str;
+ }
+ //
+ // buffer::create_malloc
+ //
+ {
+ bufferptr ptr(buffer::create_malloc(len));
+ EXPECT_EQ(len, ptr.length());
+ // this doesn't throw on my x86_64 wheezy box --sage
+ //EXPECT_THROW(buffer::create_malloc((unsigned)ULLONG_MAX), buffer::bad_alloc);
+ }
+ //
+ // buffer::claim_malloc
+ //
+ {
+ char* str = (char*)malloc(len);
+ ::memset(str, 'X', len);
+ bufferptr ptr(buffer::claim_malloc(len, str));
+ EXPECT_EQ(len, ptr.length());
+ EXPECT_EQ(str, ptr.c_str());
+ EXPECT_EQ(0, ::memcmp(str, ptr.c_str(), len));
+ }
+ //
+ // buffer::copy
+ //
+ {
+ const std::string expected(len, 'X');
+ bufferptr ptr(buffer::copy(expected.c_str(), expected.size()));
+ EXPECT_NE(expected.c_str(), ptr.c_str());
+ EXPECT_EQ(0, ::memcmp(expected.c_str(), ptr.c_str(), len));
+ }
+ //
+ // buffer::create_page_aligned
+ //
+ {
+ bufferptr ptr(buffer::create_page_aligned(len));
+ ::memset(ptr.c_str(), 'X', len);
+ // doesn't throw on my x86_64 wheezy box --sage
+ //EXPECT_THROW(buffer::create_page_aligned((unsigned)ULLONG_MAX), buffer::bad_alloc);
+#ifndef DARWIN
+ ASSERT_TRUE(ptr.is_page_aligned());
+#endif // DARWIN
+ }
+}
+
+void bench_buffer_alloc(int size, int num)
+{
+ utime_t start = ceph_clock_now();
+ for (int i=0; i<num; ++i) {
+ bufferptr p = buffer::create(size);
+ p.zero();
+ }
+ utime_t end = ceph_clock_now();
+ cout << num << " alloc of size " << size
+ << " in " << (end - start) << std::endl;
+}
+
+TEST(Buffer, BenchAlloc) {
+ bench_buffer_alloc(16384, 1000000);
+ bench_buffer_alloc(4096, 1000000);
+ bench_buffer_alloc(1024, 1000000);
+ bench_buffer_alloc(256, 1000000);
+ bench_buffer_alloc(32, 1000000);
+ bench_buffer_alloc(4, 1000000);
+}
+
+TEST(BufferRaw, ostream) {
+ bufferptr ptr(1);
+ std::ostringstream stream;
+ stream << *static_cast<instrumented_bptr&>(ptr).get_raw();
+ EXPECT_GT(stream.str().size(), stream.str().find("buffer::raw("));
+ EXPECT_GT(stream.str().size(), stream.str().find("len 1 nref 1)"));
+}
+
+//
+// +-----------+ +-----+
+// | | | |
+// | offset +----------------+ |
+// | | | |
+// | length +---- | |
+// | | \------- | |
+// +-----------+ \---+ |
+// | ptr | +-----+
+// +-----------+ | raw |
+// +-----+
+//
+TEST(BufferPtr, constructors) {
+ unsigned len = 17;
+ //
+ // ptr::ptr()
+ //
+ {
+ buffer::ptr ptr;
+ EXPECT_FALSE(ptr.have_raw());
+ EXPECT_EQ((unsigned)0, ptr.offset());
+ EXPECT_EQ((unsigned)0, ptr.length());
+ }
+ //
+ // ptr::ptr(raw *r)
+ //
+ {
+ bufferptr ptr(buffer::create(len));
+ EXPECT_TRUE(ptr.have_raw());
+ EXPECT_EQ((unsigned)0, ptr.offset());
+ EXPECT_EQ(len, ptr.length());
+ EXPECT_EQ(ptr.raw_length(), ptr.length());
+ EXPECT_EQ(1, ptr.raw_nref());
+ }
+ //
+ // ptr::ptr(unsigned l)
+ //
+ {
+ bufferptr ptr(len);
+ EXPECT_TRUE(ptr.have_raw());
+ EXPECT_EQ((unsigned)0, ptr.offset());
+ EXPECT_EQ(len, ptr.length());
+ EXPECT_EQ(1, ptr.raw_nref());
+ }
+ //
+ // ptr(const char *d, unsigned l)
+ //
+ {
+ const std::string str(len, 'X');
+ bufferptr ptr(str.c_str(), len);
+ EXPECT_TRUE(ptr.have_raw());
+ EXPECT_EQ((unsigned)0, ptr.offset());
+ EXPECT_EQ(len, ptr.length());
+ EXPECT_EQ(1, ptr.raw_nref());
+ EXPECT_EQ(0, ::memcmp(str.c_str(), ptr.c_str(), len));
+ }
+ //
+ // ptr(const ptr& p)
+ //
+ {
+ const std::string str(len, 'X');
+ bufferptr original(str.c_str(), len);
+ bufferptr ptr(original);
+ EXPECT_TRUE(ptr.have_raw());
+ EXPECT_EQ(static_cast<instrumented_bptr&>(original).get_raw(),
+ static_cast<instrumented_bptr&>(ptr).get_raw());
+ EXPECT_EQ(2, ptr.raw_nref());
+ EXPECT_EQ(0, ::memcmp(original.c_str(), ptr.c_str(), len));
+ }
+ //
+ // ptr(const ptr& p, unsigned o, unsigned l)
+ //
+ {
+ const std::string str(len, 'X');
+ bufferptr original(str.c_str(), len);
+ bufferptr ptr(original, 0, 0);
+ EXPECT_TRUE(ptr.have_raw());
+ EXPECT_EQ(static_cast<instrumented_bptr&>(original).get_raw(),
+ static_cast<instrumented_bptr&>(ptr).get_raw());
+ EXPECT_EQ(2, ptr.raw_nref());
+ EXPECT_EQ(0, ::memcmp(original.c_str(), ptr.c_str(), len));
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(bufferptr(original, 0, original.length() + 1), "");
+ EXPECT_DEATH(bufferptr(bufferptr(), 0, 0), "");
+ }
+ //
+ // ptr(ptr&& p)
+ //
+ {
+ const std::string str(len, 'X');
+ bufferptr original(str.c_str(), len);
+ bufferptr ptr(std::move(original));
+ EXPECT_TRUE(ptr.have_raw());
+ EXPECT_FALSE(original.have_raw());
+ EXPECT_EQ(0, ::memcmp(str.c_str(), ptr.c_str(), len));
+ EXPECT_EQ(1, ptr.raw_nref());
+ }
+}
+
+TEST(BufferPtr, operator_assign) {
+ //
+ // ptr& operator= (const ptr& p)
+ //
+ bufferptr ptr(10);
+ ptr.copy_in(0, 3, "ABC");
+ char dest[1];
+ {
+ bufferptr copy = ptr;
+ copy.copy_out(1, 1, dest);
+ ASSERT_EQ('B', dest[0]);
+ }
+
+ //
+ // ptr& operator= (ptr&& p)
+ //
+ bufferptr move = std::move(ptr);
+ {
+ move.copy_out(1, 1, dest);
+ ASSERT_EQ('B', dest[0]);
+ }
+ EXPECT_FALSE(ptr.have_raw());
+}
+
+TEST(BufferPtr, assignment) {
+ unsigned len = 17;
+ //
+ // override a bufferptr set with the same raw
+ //
+ {
+ bufferptr original(len);
+ bufferptr same_raw(original);
+ unsigned offset = 5;
+ unsigned length = len - offset;
+ original.set_offset(offset);
+ original.set_length(length);
+ same_raw = original;
+ ASSERT_EQ(2, original.raw_nref());
+ ASSERT_EQ(static_cast<instrumented_bptr&>(same_raw).get_raw(),
+ static_cast<instrumented_bptr&>(original).get_raw());
+ ASSERT_EQ(same_raw.offset(), original.offset());
+ ASSERT_EQ(same_raw.length(), original.length());
+ }
+
+ //
+ // self assignment is a noop
+ //
+ {
+ bufferptr original(len);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-assign-overloaded"
+ original = original;
+#pragma clang diagnostic pop
+ ASSERT_EQ(1, original.raw_nref());
+ ASSERT_EQ((unsigned)0, original.offset());
+ ASSERT_EQ(len, original.length());
+ }
+
+ //
+ // a copy points to the same raw
+ //
+ {
+ bufferptr original(len);
+ unsigned offset = 5;
+ unsigned length = len - offset;
+ original.set_offset(offset);
+ original.set_length(length);
+ bufferptr ptr;
+ ptr = original;
+ ASSERT_EQ(2, original.raw_nref());
+ ASSERT_EQ(static_cast<instrumented_bptr&>(ptr).get_raw(),
+ static_cast<instrumented_bptr&>(original).get_raw());
+ ASSERT_EQ(original.offset(), ptr.offset());
+ ASSERT_EQ(original.length(), ptr.length());
+ }
+}
+
+TEST(BufferPtr, swap) {
+ unsigned len = 17;
+
+ bufferptr ptr1(len);
+ ::memset(ptr1.c_str(), 'X', len);
+ unsigned ptr1_offset = 4;
+ ptr1.set_offset(ptr1_offset);
+ unsigned ptr1_length = 3;
+ ptr1.set_length(ptr1_length);
+
+ bufferptr ptr2(len);
+ ::memset(ptr2.c_str(), 'Y', len);
+ unsigned ptr2_offset = 5;
+ ptr2.set_offset(ptr2_offset);
+ unsigned ptr2_length = 7;
+ ptr2.set_length(ptr2_length);
+
+ ptr1.swap(ptr2);
+
+ EXPECT_EQ(ptr2_length, ptr1.length());
+ EXPECT_EQ(ptr2_offset, ptr1.offset());
+ EXPECT_EQ('Y', ptr1[0]);
+
+ EXPECT_EQ(ptr1_length, ptr2.length());
+ EXPECT_EQ(ptr1_offset, ptr2.offset());
+ EXPECT_EQ('X', ptr2[0]);
+}
+
+TEST(BufferPtr, release) {
+ unsigned len = 17;
+
+ bufferptr ptr1(len);
+ {
+ bufferptr ptr2(ptr1);
+ EXPECT_EQ(2, ptr1.raw_nref());
+ }
+ EXPECT_EQ(1, ptr1.raw_nref());
+}
+
+TEST(BufferPtr, have_raw) {
+ {
+ bufferptr ptr;
+ EXPECT_FALSE(ptr.have_raw());
+ }
+ {
+ bufferptr ptr(1);
+ EXPECT_TRUE(ptr.have_raw());
+ }
+}
+
+TEST(BufferPtr, is_n_page_sized) {
+ {
+ bufferptr ptr(CEPH_PAGE_SIZE);
+ EXPECT_TRUE(ptr.is_n_page_sized());
+ }
+ {
+ bufferptr ptr(1);
+ EXPECT_FALSE(ptr.is_n_page_sized());
+ }
+}
+
+TEST(BufferPtr, is_partial) {
+ bufferptr a;
+ EXPECT_FALSE(a.is_partial());
+ bufferptr b(10);
+ EXPECT_FALSE(b.is_partial());
+ bufferptr c(b, 1, 9);
+ EXPECT_TRUE(c.is_partial());
+ bufferptr d(b, 0, 9);
+ EXPECT_TRUE(d.is_partial());
+}
+
+TEST(BufferPtr, accessors) {
+ unsigned len = 17;
+ bufferptr ptr(len);
+ ptr.c_str()[0] = 'X';
+ ptr[1] = 'Y';
+ const bufferptr const_ptr(ptr);
+
+ EXPECT_NE((void*)nullptr, (void*)static_cast<instrumented_bptr&>(ptr).get_raw());
+ EXPECT_EQ('X', ptr.c_str()[0]);
+ {
+ bufferptr ptr;
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr.c_str(), "");
+ EXPECT_DEATH(ptr[0], "");
+ }
+ EXPECT_EQ('X', const_ptr.c_str()[0]);
+ {
+ const bufferptr const_ptr;
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(const_ptr.c_str(), "");
+ EXPECT_DEATH(const_ptr[0], "");
+ }
+ EXPECT_EQ(len, const_ptr.length());
+ EXPECT_EQ((unsigned)0, const_ptr.offset());
+ EXPECT_EQ((unsigned)0, const_ptr.start());
+ EXPECT_EQ(len, const_ptr.end());
+ EXPECT_EQ(len, const_ptr.end());
+ {
+ bufferptr ptr(len);
+ unsigned unused = 1;
+ ptr.set_length(ptr.length() - unused);
+ EXPECT_EQ(unused, ptr.unused_tail_length());
+ }
+ {
+ bufferptr ptr;
+ EXPECT_EQ((unsigned)0, ptr.unused_tail_length());
+ }
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr[len], "");
+ EXPECT_DEATH(const_ptr[len], "");
+ }
+ {
+ const bufferptr const_ptr;
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(const_ptr.raw_c_str(), "");
+ EXPECT_DEATH(const_ptr.raw_length(), "");
+ EXPECT_DEATH(const_ptr.raw_nref(), "");
+ }
+ EXPECT_NE((const char *)NULL, const_ptr.raw_c_str());
+ EXPECT_EQ(len, const_ptr.raw_length());
+ EXPECT_EQ(2, const_ptr.raw_nref());
+ {
+ bufferptr ptr(len);
+ unsigned wasted = 1;
+ ptr.set_length(ptr.length() - wasted * 2);
+ ptr.set_offset(wasted);
+ EXPECT_EQ(wasted * 2, ptr.wasted());
+ }
+}
+
+TEST(BufferPtr, cmp) {
+ bufferptr empty;
+ bufferptr a("A", 1);
+ bufferptr ab("AB", 2);
+ bufferptr af("AF", 2);
+ bufferptr acc("ACC", 3);
+ EXPECT_GE(-1, empty.cmp(a));
+ EXPECT_LE(1, a.cmp(empty));
+ EXPECT_GE(-1, a.cmp(ab));
+ EXPECT_LE(1, ab.cmp(a));
+ EXPECT_EQ(0, ab.cmp(ab));
+ EXPECT_GE(-1, ab.cmp(af));
+ EXPECT_LE(1, af.cmp(ab));
+ EXPECT_GE(-1, acc.cmp(af));
+ EXPECT_LE(1, af.cmp(acc));
+}
+
+TEST(BufferPtr, is_zero) {
+ char str[2] = { '\0', 'X' };
+ {
+ const bufferptr ptr(buffer::create_static(2, str));
+ EXPECT_FALSE(ptr.is_zero());
+ }
+ {
+ const bufferptr ptr(buffer::create_static(1, str));
+ EXPECT_TRUE(ptr.is_zero());
+ }
+}
+
+TEST(BufferPtr, copy_out) {
+ {
+ const bufferptr ptr;
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr.copy_out((unsigned)0, (unsigned)0, NULL), "");
+ }
+ {
+ char in[] = "ABC";
+ const bufferptr ptr(buffer::create_static(strlen(in), in));
+ EXPECT_THROW(ptr.copy_out((unsigned)0, strlen(in) + 1, NULL), buffer::end_of_buffer);
+ EXPECT_THROW(ptr.copy_out(strlen(in) + 1, (unsigned)0, NULL), buffer::end_of_buffer);
+ char out[1] = { 'X' };
+ ptr.copy_out((unsigned)1, (unsigned)1, out);
+ EXPECT_EQ('B', out[0]);
+ }
+}
+
+TEST(BufferPtr, copy_out_bench) {
+ for (int s=1; s<=8; s*=2) {
+ utime_t start = ceph_clock_now();
+ int buflen = 1048576;
+ int count = 1000;
+ uint64_t v;
+ for (int i=0; i<count; ++i) {
+ bufferptr bp(buflen);
+ for (int64_t j=0; j<buflen; j += s) {
+ bp.copy_out(j, s, (char *)&v);
+ }
+ }
+ utime_t end = ceph_clock_now();
+ cout << count << " fills of buffer len " << buflen
+ << " with " << s << " byte copy_out in "
+ << (end - start) << std::endl;
+ }
+}
+
+TEST(BufferPtr, copy_in) {
+ {
+ bufferptr ptr;
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr.copy_in((unsigned)0, (unsigned)0, NULL), "");
+ }
+ {
+ char in[] = "ABCD";
+ bufferptr ptr(2);
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr.copy_in((unsigned)0, strlen(in) + 1, NULL), "");
+ EXPECT_DEATH(ptr.copy_in(strlen(in) + 1, (unsigned)0, NULL), "");
+ }
+ ptr.copy_in((unsigned)0, (unsigned)2, in);
+ EXPECT_EQ(in[0], ptr[0]);
+ EXPECT_EQ(in[1], ptr[1]);
+ }
+}
+
+TEST(BufferPtr, copy_in_bench) {
+ for (int s=1; s<=8; s*=2) {
+ utime_t start = ceph_clock_now();
+ int buflen = 1048576;
+ int count = 1000;
+ for (int i=0; i<count; ++i) {
+ bufferptr bp(buflen);
+ for (int64_t j=0; j<buflen; j += s) {
+ bp.copy_in(j, s, (char *)&j, false);
+ }
+ }
+ utime_t end = ceph_clock_now();
+ cout << count << " fills of buffer len " << buflen
+ << " with " << s << " byte copy_in in "
+ << (end - start) << std::endl;
+ }
+}
+
+TEST(BufferPtr, append) {
+ {
+ bufferptr ptr;
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr.append('A'), "");
+ EXPECT_DEATH(ptr.append("B", (unsigned)1), "");
+ }
+ {
+ bufferptr ptr(2);
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr.append('A'), "");
+ EXPECT_DEATH(ptr.append("B", (unsigned)1), "");
+ }
+ ptr.set_length(0);
+ ptr.append('A');
+ EXPECT_EQ((unsigned)1, ptr.length());
+ EXPECT_EQ('A', ptr[0]);
+ ptr.append("B", (unsigned)1);
+ EXPECT_EQ((unsigned)2, ptr.length());
+ EXPECT_EQ('B', ptr[1]);
+ }
+}
+
+TEST(BufferPtr, append_bench) {
+ char src[1048576];
+ memset(src, 0, sizeof(src));
+ for (int s=4; s<=16384; s*=4) {
+ utime_t start = ceph_clock_now();
+ int buflen = 1048576;
+ int count = 4000;
+ for (int i=0; i<count; ++i) {
+ bufferptr bp(buflen);
+ bp.set_length(0);
+ for (int64_t j=0; j<buflen; j += s) {
+ bp.append(src + j, s);
+ }
+ }
+ utime_t end = ceph_clock_now();
+ cout << count << " fills of buffer len " << buflen
+ << " with " << s << " byte appends in "
+ << (end - start) << std::endl;
+ }
+}
+
+TEST(BufferPtr, zero) {
+ char str[] = "XXXX";
+ bufferptr ptr(buffer::create_static(strlen(str), str));
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(ptr.zero(ptr.length() + 1, 0), "");
+ }
+ ptr.zero(1, 1);
+ EXPECT_EQ('X', ptr[0]);
+ EXPECT_EQ('\0', ptr[1]);
+ EXPECT_EQ('X', ptr[2]);
+ ptr.zero();
+ EXPECT_EQ('\0', ptr[0]);
+}
+
+TEST(BufferPtr, ostream) {
+ {
+ bufferptr ptr;
+ std::ostringstream stream;
+ stream << ptr;
+ EXPECT_GT(stream.str().size(), stream.str().find("buffer:ptr(0~0 no raw"));
+ }
+ {
+ char str[] = "XXXX";
+ bufferptr ptr(buffer::create_static(strlen(str), str));
+ std::ostringstream stream;
+ stream << ptr;
+ EXPECT_GT(stream.str().size(), stream.str().find("len 4 nref 1)"));
+ }
+}
+
+//
+// +---------+
+// | +-----+ |
+// list ptr | | | |
+// +----------+ +-----+ | | | |
+// | append_ >-------> >--------------------> | |
+// | buffer | +-----+ | | | |
+// +----------+ ptr | | | |
+// | _len | list +-----+ | | | |
+// +----------+ +------+ ,--->+ >-----> | |
+// | _buffers >----> >----- +-----+ | +-----+ |
+// +----------+ +----^-+ \ ptr | raw |
+// | last_p | / `-->+-----+ | +-----+ |
+// +--------+-+ / + >-----> | |
+// | ,- ,--->+-----+ | | | |
+// | / ,--- | | | |
+// | / ,--- | | | |
+// +-v--+-^--+--^+-------+ | | | |
+// | bl | ls | p | p_off >--------------->| | |
+// +----+----+-----+-----+ | +-----+ |
+// | | off >------------->| raw |
+// +---------------+-----+ | |
+// iterator +---------+
+//
+TEST(BufferListIterator, constructors) {
+ //
+ // iterator()
+ //
+ {
+ buffer::list::iterator i;
+ EXPECT_EQ((unsigned)0, i.get_off());
+ }
+
+ //
+ // iterator(list *l, unsigned o=0)
+ //
+ {
+ bufferlist bl;
+ bl.append("ABC", 3);
+
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_EQ((unsigned)0, i.get_off());
+ EXPECT_EQ('A', *i);
+ }
+ {
+ bufferlist::iterator i(&bl, 1);
+ EXPECT_EQ('B', *i);
+ EXPECT_EQ((unsigned)2, i.get_remaining());
+ }
+ }
+
+ //
+ // iterator(list *l, unsigned o, std::list<ptr>::iterator ip, unsigned po)
+ // not tested because of http://tracker.ceph.com/issues/4101
+
+ //
+ // iterator(const iterator& other)
+ //
+ {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ bufferlist::iterator i(&bl, 1);
+ bufferlist::iterator j(i);
+ EXPECT_EQ(*i, *j);
+ ++j;
+ EXPECT_NE(*i, *j);
+ EXPECT_EQ('B', *i);
+ EXPECT_EQ('C', *j);
+ bl.c_str()[1] = 'X';
+ }
+
+ //
+ // const_iterator(const iterator& other)
+ //
+ {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ bufferlist::iterator i(&bl);
+ bufferlist::const_iterator ci(i);
+ EXPECT_EQ(0u, ci.get_off());
+ EXPECT_EQ('A', *ci);
+ }
+}
+
+TEST(BufferListIterator, empty_create_append_copy) {
+ bufferlist bl, bl2, bl3, out;
+ bl2.append("bar");
+ bl.swap(bl2);
+ bl2.append("xxx");
+ bl.append(bl2);
+ bl.rebuild();
+ bl.begin().copy(6, out);
+ ASSERT_TRUE(out.contents_equal(bl));
+}
+
+TEST(BufferListIterator, operator_assign) {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ bufferlist::iterator i(&bl, 1);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-assign-overloaded"
+ i = i;
+#pragma clang diagnostic pop
+ EXPECT_EQ('B', *i);
+ bufferlist::iterator j;
+ j = i;
+ EXPECT_EQ('B', *j);
+}
+
+TEST(BufferListIterator, get_off) {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ bufferlist::iterator i(&bl, 1);
+ EXPECT_EQ((unsigned)1, i.get_off());
+}
+
+TEST(BufferListIterator, get_remaining) {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ bufferlist::iterator i(&bl, 1);
+ EXPECT_EQ((unsigned)2, i.get_remaining());
+}
+
+TEST(BufferListIterator, end) {
+ bufferlist bl;
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_TRUE(i.end());
+ }
+ bl.append("ABC", 3);
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_FALSE(i.end());
+ }
+}
+
+static void bench_bufferlistiter_deref(const size_t step,
+ const size_t bufsize,
+ const size_t bufnum) {
+ const std::string buf(bufsize, 'a');
+ ceph::bufferlist bl;
+
+ for (size_t i = 0; i < bufnum; i++) {
+ bl.append(ceph::bufferptr(buf.c_str(), buf.size()));
+ }
+
+ utime_t start = ceph_clock_now();
+ bufferlist::iterator iter = bl.begin();
+ while (iter != bl.end()) {
+ iter += step;
+ }
+ utime_t end = ceph_clock_now();
+ cout << bufsize * bufnum << " derefs over bl with " << bufnum
+ << " buffers, each " << bufsize << " bytes long"
+ << " in " << (end - start) << std::endl;
+}
+
+TEST(BufferListIterator, BenchDeref) {
+ bench_bufferlistiter_deref(1, 1, 4096000);
+ bench_bufferlistiter_deref(1, 10, 409600);
+ bench_bufferlistiter_deref(1, 100, 40960);
+ bench_bufferlistiter_deref(1, 1000, 4096);
+
+ bench_bufferlistiter_deref(4, 1, 1024000);
+ bench_bufferlistiter_deref(4, 10, 102400);
+ bench_bufferlistiter_deref(4, 100, 10240);
+ bench_bufferlistiter_deref(4, 1000, 1024);
+}
+
+TEST(BufferListIterator, advance) {
+ bufferlist bl;
+ const std::string one("ABC");
+ bl.append(bufferptr(one.c_str(), one.size()));
+ const std::string two("DEF");
+ bl.append(bufferptr(two.c_str(), two.size()));
+
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ }
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_EQ('A', *i);
+ i += 1u;
+ EXPECT_EQ('B', *i);
+ i += 3u;
+ EXPECT_EQ('E', *i);
+ }
+}
+
+TEST(BufferListIterator, iterate_with_empties) {
+ ceph::bufferlist bl;
+ EXPECT_EQ(bl.get_num_buffers(), 0u);
+
+ bl.push_back(ceph::buffer::create(0));
+ EXPECT_EQ(bl.length(), 0u);
+ EXPECT_EQ(bl.get_num_buffers(), 1u);
+
+ encode(int64_t(42), bl);
+ EXPECT_EQ(bl.get_num_buffers(), 2u);
+
+ bl.push_back(ceph::buffer::create(0));
+ EXPECT_EQ(bl.get_num_buffers(), 3u);
+
+ // append bufferlist with single, 0-sized ptr inside
+ {
+ ceph::bufferlist bl_with_empty_ptr;
+ bl_with_empty_ptr.push_back(ceph::buffer::create(0));
+ EXPECT_EQ(bl_with_empty_ptr.length(), 0u);
+ EXPECT_EQ(bl_with_empty_ptr.get_num_buffers(), 1u);
+
+ bl.append(bl_with_empty_ptr);
+ }
+
+ encode(int64_t(24), bl);
+ EXPECT_EQ(bl.get_num_buffers(), 5u);
+
+ auto i = bl.cbegin();
+ int64_t val;
+ decode(val, i);
+ EXPECT_EQ(val, 42l);
+
+ decode(val, i);
+ EXPECT_EQ(val, 24l);
+
+ val = 0;
+ i.seek(sizeof(val));
+ decode(val, i);
+ EXPECT_EQ(val, 24l);
+ EXPECT_TRUE(i == bl.end());
+
+ i.seek(0);
+ decode(val, i);
+ EXPECT_EQ(val, 42);
+ EXPECT_FALSE(i == bl.end());
+}
+
+TEST(BufferListIterator, get_ptr_and_advance)
+{
+ bufferptr a("one", 3);
+ bufferptr b("two", 3);
+ bufferptr c("three", 5);
+ bufferlist bl;
+ bl.append(a);
+ bl.append(b);
+ bl.append(c);
+ const char *ptr;
+ bufferlist::iterator p = bl.begin();
+ ASSERT_EQ(3u, p.get_ptr_and_advance(11u, &ptr));
+ ASSERT_EQ(bl.length() - 3u, p.get_remaining());
+ ASSERT_EQ(0, memcmp(ptr, "one", 3));
+ ASSERT_EQ(2u, p.get_ptr_and_advance(2u, &ptr));
+ ASSERT_EQ(0, memcmp(ptr, "tw", 2));
+ ASSERT_EQ(1u, p.get_ptr_and_advance(4u, &ptr));
+ ASSERT_EQ(0, memcmp(ptr, "o", 1));
+ ASSERT_EQ(5u, p.get_ptr_and_advance(5u, &ptr));
+ ASSERT_EQ(0, memcmp(ptr, "three", 5));
+ ASSERT_EQ(0u, p.get_remaining());
+}
+
+TEST(BufferListIterator, iterator_crc32c) {
+ bufferlist bl1;
+ bufferlist bl2;
+ bufferlist bl3;
+
+ string s1(100, 'a');
+ string s2(50, 'b');
+ string s3(7, 'c');
+ string s;
+ bl1.append(s1);
+ bl1.append(s2);
+ bl1.append(s3);
+ s = s1 + s2 + s3;
+ bl2.append(s);
+
+ bufferlist::iterator it = bl2.begin();
+ ASSERT_EQ(bl1.crc32c(0), it.crc32c(it.get_remaining(), 0));
+ ASSERT_EQ(0u, it.get_remaining());
+
+ it = bl1.begin();
+ ASSERT_EQ(bl2.crc32c(0), it.crc32c(it.get_remaining(), 0));
+
+ bl3.append(s.substr(98, 55));
+ it = bl1.begin();
+ it += 98u;
+ ASSERT_EQ(bl3.crc32c(0), it.crc32c(55, 0));
+ ASSERT_EQ(4u, it.get_remaining());
+
+ bl3.clear();
+ bl3.append(s.substr(98 + 55));
+ it = bl1.begin();
+ it += 98u + 55u;
+ ASSERT_EQ(bl3.crc32c(0), it.crc32c(10, 0));
+ ASSERT_EQ(0u, it.get_remaining());
+}
+
+TEST(BufferListIterator, seek) {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ bufferlist::iterator i(&bl, 1);
+ EXPECT_EQ('B', *i);
+ i.seek(2);
+ EXPECT_EQ('C', *i);
+}
+
+TEST(BufferListIterator, operator_star) {
+ bufferlist bl;
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_THROW(*i, buffer::end_of_buffer);
+ }
+ bl.append("ABC", 3);
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_EQ('A', *i);
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ EXPECT_THROW(*i, buffer::end_of_buffer);
+ }
+}
+
+TEST(BufferListIterator, operator_equal) {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ {
+ bufferlist::iterator i(&bl);
+ bufferlist::iterator j(&bl);
+ EXPECT_EQ(i, j);
+ }
+ {
+ bufferlist::const_iterator ci = bl.begin();
+ bufferlist::iterator i = bl.begin();
+ EXPECT_EQ(i, ci);
+ EXPECT_EQ(ci, i);
+ }
+}
+
+TEST(BufferListIterator, operator_nequal) {
+ bufferlist bl;
+ bl.append("ABC", 3);
+ {
+ bufferlist::iterator i(&bl);
+ bufferlist::iterator j(&bl);
+ EXPECT_NE(++i, j);
+ }
+ {
+ bufferlist::const_iterator ci = bl.begin();
+ bufferlist::const_iterator cj = bl.begin();
+ ++ci;
+ EXPECT_NE(ci, cj);
+ bufferlist::iterator i = bl.begin();
+ EXPECT_NE(i, ci);
+ EXPECT_NE(ci, i);
+ }
+ {
+ // tests begin(), end(), operator++() also
+ string s("ABC");
+ int i = 0;
+ for (auto c : bl) {
+ EXPECT_EQ(s[i++], c);
+ }
+ }
+}
+
+TEST(BufferListIterator, operator_plus_plus) {
+ bufferlist bl;
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_THROW(++i, buffer::end_of_buffer);
+ }
+ bl.append("ABC", 3);
+ {
+ bufferlist::iterator i(&bl);
+ ++i;
+ EXPECT_EQ('B', *i);
+ }
+}
+
+TEST(BufferListIterator, get_current_ptr) {
+ bufferlist bl;
+ {
+ bufferlist::iterator i(&bl);
+ EXPECT_THROW(++i, buffer::end_of_buffer);
+ }
+ bl.append("ABC", 3);
+ {
+ bufferlist::iterator i(&bl, 1);
+ const buffer::ptr ptr = i.get_current_ptr();
+ EXPECT_EQ('B', ptr[0]);
+ EXPECT_EQ((unsigned)1, ptr.offset());
+ EXPECT_EQ((unsigned)2, ptr.length());
+ }
+}
+
+TEST(BufferListIterator, copy) {
+ bufferlist bl;
+ const char *expected = "ABC";
+ bl.append(expected, 3);
+ //
+ // void copy(unsigned len, char *dest);
+ //
+ {
+ char* copy = (char*)malloc(3);
+ ::memset(copy, 'X', 3);
+ bufferlist::iterator i(&bl);
+ //
+ // demonstrates that it seeks back to offset if p == ls->end()
+ //
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ i.copy(2, copy);
+ EXPECT_EQ(0, ::memcmp(copy, expected, 2));
+ EXPECT_EQ('X', copy[2]);
+ i.seek(0);
+ i.copy(3, copy);
+ EXPECT_EQ(0, ::memcmp(copy, expected, 3));
+ free(copy);
+ }
+ //
+ // void copy(unsigned len, char *dest) via begin(size_t offset)
+ //
+ {
+ bufferlist bl;
+ EXPECT_THROW(bl.begin((unsigned)100).copy((unsigned)100, (char*)0), buffer::end_of_buffer);
+ const char *expected = "ABC";
+ bl.append(expected);
+ char *dest = new char[2];
+ bl.begin(1).copy(2, dest);
+ EXPECT_EQ(0, ::memcmp(expected + 1, dest, 2));
+ delete [] dest;
+ }
+ //
+ // void buffer::list::iterator::copy_deep(unsigned len, ptr &dest)
+ //
+ {
+ bufferptr ptr;
+ bufferlist::iterator i(&bl);
+ i.copy_deep(2, ptr);
+ EXPECT_EQ((unsigned)2, ptr.length());
+ EXPECT_EQ('A', ptr[0]);
+ EXPECT_EQ('B', ptr[1]);
+ }
+ //
+ // void buffer::list::iterator::copy_shallow(unsigned len, ptr &dest)
+ //
+ {
+ bufferptr ptr;
+ bufferlist::iterator i(&bl);
+ i.copy_shallow(2, ptr);
+ EXPECT_EQ((unsigned)2, ptr.length());
+ EXPECT_EQ('A', ptr[0]);
+ EXPECT_EQ('B', ptr[1]);
+ }
+ //
+ // void buffer::list::iterator::copy(unsigned len, list &dest)
+ //
+ {
+ bufferlist copy;
+ bufferlist::iterator i(&bl);
+ //
+ // demonstrates that it seeks back to offset if p == ls->end()
+ //
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ i.copy(2, copy);
+ EXPECT_EQ(0, ::memcmp(copy.c_str(), expected, 2));
+ i.seek(0);
+ i.copy(3, copy);
+ EXPECT_EQ('A', copy[0]);
+ EXPECT_EQ('B', copy[1]);
+ EXPECT_EQ('A', copy[2]);
+ EXPECT_EQ('B', copy[3]);
+ EXPECT_EQ('C', copy[4]);
+ EXPECT_EQ((unsigned)(2 + 3), copy.length());
+ }
+ //
+ // void buffer::list::iterator::copy(unsigned len, list &dest) via begin(size_t offset)
+ //
+ {
+ bufferlist bl;
+ bufferlist dest;
+ EXPECT_THROW(bl.begin((unsigned)100).copy((unsigned)100, dest), buffer::end_of_buffer);
+ const char *expected = "ABC";
+ bl.append(expected);
+ bl.begin(1).copy(2, dest);
+ EXPECT_EQ(0, ::memcmp(expected + 1, dest.c_str(), 2));
+ }
+ //
+ // void buffer::list::iterator::copy_all(list &dest)
+ //
+ {
+ bufferlist copy;
+ bufferlist::iterator i(&bl);
+ //
+ // demonstrates that it seeks back to offset if p == ls->end()
+ //
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ i.copy_all(copy);
+ EXPECT_EQ('A', copy[0]);
+ EXPECT_EQ('B', copy[1]);
+ EXPECT_EQ('C', copy[2]);
+ EXPECT_EQ((unsigned)3, copy.length());
+ }
+ //
+ // void copy(unsigned len, std::string &dest)
+ //
+ {
+ std::string copy;
+ bufferlist::iterator i(&bl);
+ //
+ // demonstrates that it seeks back to offset if p == ls->end()
+ //
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ i.copy(2, copy);
+ EXPECT_EQ(0, ::memcmp(copy.c_str(), expected, 2));
+ i.seek(0);
+ i.copy(3, copy);
+ EXPECT_EQ('A', copy[0]);
+ EXPECT_EQ('B', copy[1]);
+ EXPECT_EQ('A', copy[2]);
+ EXPECT_EQ('B', copy[3]);
+ EXPECT_EQ('C', copy[4]);
+ EXPECT_EQ((unsigned)(2 + 3), copy.length());
+ }
+ //
+ // void copy(unsigned len, std::string &dest) via begin(size_t offset)
+ //
+ {
+ bufferlist bl;
+ std::string dest;
+ EXPECT_THROW(bl.begin((unsigned)100).copy((unsigned)100, dest), buffer::end_of_buffer);
+ const char *expected = "ABC";
+ bl.append(expected);
+ bl.begin(1).copy(2, dest);
+ EXPECT_EQ(0, ::memcmp(expected + 1, dest.c_str(), 2));
+ }
+}
+
+TEST(BufferListIterator, copy_in) {
+ bufferlist bl;
+ const char *existing = "XXX";
+ bl.append(existing, 3);
+ //
+ // void buffer::list::iterator::copy_in(unsigned len, const char *src)
+ //
+ {
+ bufferlist::iterator i(&bl);
+ //
+ // demonstrates that it seeks back to offset if p == ls->end()
+ //
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ const char *expected = "ABC";
+ i.copy_in(3, expected);
+ EXPECT_EQ(0, ::memcmp(bl.c_str(), expected, 3));
+ EXPECT_EQ('A', bl[0]);
+ EXPECT_EQ('B', bl[1]);
+ EXPECT_EQ('C', bl[2]);
+ EXPECT_EQ((unsigned)3, bl.length());
+ }
+ //
+ // void copy_in(unsigned len, const char *src) via begin(size_t offset)
+ //
+ {
+ bufferlist bl;
+ bl.append("XXX");
+ EXPECT_THROW(bl.begin((unsigned)100).copy_in((unsigned)100, (char*)0), buffer::end_of_buffer);
+ bl.begin(1).copy_in(2, "AB");
+ EXPECT_EQ(0, ::memcmp("XAB", bl.c_str(), 3));
+ }
+ //
+ // void buffer::list::iterator::copy_in(unsigned len, const list& otherl)
+ //
+ {
+ bufferlist::iterator i(&bl);
+ //
+ // demonstrates that it seeks back to offset if p == ls->end()
+ //
+ EXPECT_THROW(i += 200u, buffer::end_of_buffer);
+ bufferlist expected;
+ expected.append("ABC", 3);
+ i.copy_in(3, expected);
+ EXPECT_EQ(0, ::memcmp(bl.c_str(), expected.c_str(), 3));
+ EXPECT_EQ('A', bl[0]);
+ EXPECT_EQ('B', bl[1]);
+ EXPECT_EQ('C', bl[2]);
+ EXPECT_EQ((unsigned)3, bl.length());
+ }
+ //
+ // void copy_in(unsigned len, const list& src) via begin(size_t offset)
+ //
+ {
+ bufferlist bl;
+ bl.append("XXX");
+ bufferlist src;
+ src.append("ABC");
+ EXPECT_THROW(bl.begin((unsigned)100).copy_in((unsigned)100, src), buffer::end_of_buffer);
+ bl.begin(1).copy_in(2, src);
+ EXPECT_EQ(0, ::memcmp("XAB", bl.c_str(), 3));
+ }
+}
+
+// iterator& buffer::list::const_iterator::operator++()
+TEST(BufferListConstIterator, operator_plus_plus) {
+ bufferlist bl;
+ {
+ bufferlist::const_iterator i(&bl);
+ EXPECT_THROW(++i, buffer::end_of_buffer);
+ }
+ bl.append("ABC", 3);
+ {
+ const bufferlist const_bl(bl);
+ bufferlist::const_iterator i(const_bl.begin());
+ ++i;
+ EXPECT_EQ('B', *i);
+ }
+
+}
+
+TEST(BufferList, constructors) {
+ //
+ // list()
+ //
+ {
+ bufferlist bl;
+ ASSERT_EQ((unsigned)0, bl.length());
+ }
+ //
+ // list(unsigned prealloc)
+ //
+ {
+ bufferlist bl(1);
+ ASSERT_EQ((unsigned)0, bl.length());
+ bl.append('A');
+ ASSERT_EQ('A', bl[0]);
+ }
+ //
+ // list(const list& other)
+ //
+ {
+ bufferlist bl(1);
+ bl.append('A');
+ ASSERT_EQ('A', bl[0]);
+ bufferlist copy(bl);
+ ASSERT_EQ('A', copy[0]);
+ }
+ //
+ // list(list&& other)
+ //
+ {
+ bufferlist bl(1);
+ bl.append('A');
+ bufferlist copy = std::move(bl);
+ ASSERT_EQ(0U, bl.length());
+ ASSERT_EQ(1U, copy.length());
+ ASSERT_EQ('A', copy[0]);
+ }
+}
+
+TEST(BufferList, append_after_move) {
+ bufferlist bl(6);
+ bl.append("ABC", 3);
+ EXPECT_EQ(1, bl.get_num_buffers());
+
+ bufferlist moved_to_bl(std::move(bl));
+ moved_to_bl.append("123", 3);
+ // it's expected that the list(list&&) ctor will preserve the _carriage
+ EXPECT_EQ(1, moved_to_bl.get_num_buffers());
+ EXPECT_EQ(0, ::memcmp("ABC123", moved_to_bl.c_str(), 6));
+}
+
+void bench_bufferlist_alloc(int size, int num, int per)
+{
+ utime_t start = ceph_clock_now();
+ for (int i=0; i<num; ++i) {
+ bufferlist bl;
+ for (int j=0; j<per; ++j)
+ bl.push_back(buffer::ptr_node::create(buffer::create(size)));
+ }
+ utime_t end = ceph_clock_now();
+ cout << num << " alloc of size " << size
+ << " in " << (end - start) << std::endl;
+}
+
+TEST(BufferList, BenchAlloc) {
+ bench_bufferlist_alloc(32768, 100000, 16);
+ bench_bufferlist_alloc(25000, 100000, 16);
+ bench_bufferlist_alloc(16384, 100000, 16);
+ bench_bufferlist_alloc(10000, 100000, 16);
+ bench_bufferlist_alloc(8192, 100000, 16);
+ bench_bufferlist_alloc(6000, 100000, 16);
+ bench_bufferlist_alloc(4096, 100000, 16);
+ bench_bufferlist_alloc(1024, 100000, 16);
+ bench_bufferlist_alloc(256, 100000, 16);
+ bench_bufferlist_alloc(32, 100000, 16);
+ bench_bufferlist_alloc(4, 100000, 16);
+}
+
+/*
+ * append_bench tests now have multiple variants:
+ *
+ * Version 1 tests allocate a single bufferlist during loop iteration.
+ * Ultimately very little memory is utilized since the bufferlist immediately
+ * drops out of scope. This was the original variant of these tests but showed
+ * unexpected performance characteristics that appears to be tied to tcmalloc
+ * and/or kernel behavior depending on the bufferlist size and step size.
+ *
+ * Version 2 tests allocate a configurable number of bufferlists that are
+ * replaced round-robin during loop iteration. Version 2 tests are designed
+ * to better mimic performance when multiple bufferlists are in memory at the
+ * same time. During testing this showed more consistent and seemingly
+ * accurate behavior across bufferlist and step sizes.
+ */
+
+TEST(BufferList, append_bench_with_size_hint) {
+ std::array<char, 1048576> src = { 0, };
+
+ for (size_t step = 4; step <= 16384; step *= 4) {
+ const utime_t start = ceph_clock_now();
+
+ constexpr size_t rounds = 4000;
+ for (size_t r = 0; r < rounds; ++r) {
+ ceph::bufferlist bl(std::size(src));
+ for (auto iter = std::begin(src);
+ iter != std::end(src);
+ iter = std::next(iter, step)) {
+ bl.append(&*iter, step);
+ }
+ }
+ cout << rounds << " fills of buffer len " << src.size()
+ << " with " << step << " byte appends in "
+ << (ceph_clock_now() - start) << std::endl;
+ }
+}
+
+TEST(BufferList, append_bench_with_size_hint2) {
+ std::array<char, 1048576> src = { 0, };
+ constexpr size_t rounds = 4000;
+ constexpr int conc_bl = 400;
+ std::vector<ceph::bufferlist*> bls(conc_bl);
+
+ for (int i = 0; i < conc_bl; i++) {
+ bls[i] = new ceph::bufferlist;
+ }
+ for (size_t step = 4; step <= 16384; step *= 4) {
+ const utime_t start = ceph_clock_now();
+ for (size_t r = 0; r < rounds; ++r) {
+ delete bls[r % conc_bl];
+ bls[r % conc_bl] = new ceph::bufferlist(std::size(src));
+ for (auto iter = std::begin(src);
+ iter != std::end(src);
+ iter = std::next(iter, step)) {
+ bls[r % conc_bl]->append(&*iter, step);
+ }
+ }
+ cout << rounds << " fills of buffer len " << src.size()
+ << " with " << step << " byte appends in "
+ << (ceph_clock_now() - start) << std::endl;
+ }
+ for (int i = 0; i < conc_bl; i++) {
+ delete bls[i];
+ }
+}
+
+TEST(BufferList, append_bench) {
+ std::array<char, 1048576> src = { 0, };
+ for (size_t step = 4; step <= 16384; step *= 4) {
+ const utime_t start = ceph_clock_now();
+ constexpr size_t rounds = 4000;
+ for (size_t r = 0; r < rounds; ++r) {
+ ceph::bufferlist bl;
+ for (auto iter = std::begin(src);
+ iter != std::end(src);
+ iter = std::next(iter, step)) {
+ bl.append(&*iter, step);
+ }
+ }
+ cout << rounds << " fills of buffer len " << src.size()
+ << " with " << step << " byte appends in "
+ << (ceph_clock_now() - start) << std::endl;
+ }
+}
+
+TEST(BufferList, append_bench2) {
+ std::array<char, 1048576> src = { 0, };
+ constexpr size_t rounds = 4000;
+ constexpr int conc_bl = 400;
+ std::vector<ceph::bufferlist*> bls(conc_bl);
+
+ for (int i = 0; i < conc_bl; i++) {
+ bls[i] = new ceph::bufferlist;
+ }
+ for (size_t step = 4; step <= 16384; step *= 4) {
+ const utime_t start = ceph_clock_now();
+ for (size_t r = 0; r < rounds; ++r) {
+ delete bls[r % conc_bl];
+ bls[r % conc_bl] = new ceph::bufferlist;
+ for (auto iter = std::begin(src);
+ iter != std::end(src);
+ iter = std::next(iter, step)) {
+ bls[r % conc_bl]->append(&*iter, step);
+ }
+ }
+ cout << rounds << " fills of buffer len " << src.size()
+ << " with " << step << " byte appends in "
+ << (ceph_clock_now() - start) << std::endl;
+ }
+ for (int i = 0; i < conc_bl; i++) {
+ delete bls[i];
+ }
+}
+
+TEST(BufferList, append_hole_bench) {
+ constexpr size_t targeted_bl_size = 1048576;
+
+ for (size_t step = 512; step <= 65536; step *= 2) {
+ const utime_t start = ceph_clock_now();
+ constexpr size_t rounds = 80000;
+ for (size_t r = 0; r < rounds; ++r) {
+ ceph::bufferlist bl;
+ while (bl.length() < targeted_bl_size) {
+ bl.append_hole(step);
+ }
+ }
+ cout << rounds << " fills of buffer len " << targeted_bl_size
+ << " with " << step << " byte long append_hole in "
+ << (ceph_clock_now() - start) << std::endl;
+ }
+}
+
+TEST(BufferList, append_hole_bench2) {
+ constexpr size_t targeted_bl_size = 1048576;
+ constexpr size_t rounds = 80000;
+ constexpr int conc_bl = 400;
+ std::vector<ceph::bufferlist*> bls(conc_bl);
+
+ for (int i = 0; i < conc_bl; i++) {
+ bls[i] = new ceph::bufferlist;
+ }
+ for (size_t step = 512; step <= 65536; step *= 2) {
+ const utime_t start = ceph_clock_now();
+ for (size_t r = 0; r < rounds; ++r) {
+ delete bls[r % conc_bl];
+ bls[r % conc_bl] = new ceph::bufferlist;
+ while (bls[r % conc_bl]->length() < targeted_bl_size) {
+ bls[r % conc_bl]->append_hole(step);
+ }
+ }
+ cout << rounds << " fills of buffer len " << targeted_bl_size
+ << " with " << step << " byte long append_hole in "
+ << (ceph_clock_now() - start) << std::endl;
+ }
+ for (int i = 0; i < conc_bl; i++) {
+ delete bls[i];
+ }
+}
+
+TEST(BufferList, operator_assign_rvalue) {
+ bufferlist from;
+ {
+ bufferptr ptr(2);
+ from.append(ptr);
+ }
+ bufferlist to;
+ {
+ bufferptr ptr(4);
+ to.append(ptr);
+ }
+ EXPECT_EQ((unsigned)4, to.length());
+ EXPECT_EQ((unsigned)1, to.get_num_buffers());
+ to = std::move(from);
+ EXPECT_EQ((unsigned)2, to.length());
+ EXPECT_EQ((unsigned)1, to.get_num_buffers());
+ EXPECT_EQ((unsigned)0, from.get_num_buffers());
+ EXPECT_EQ((unsigned)0, from.length());
+}
+
+TEST(BufferList, operator_equal) {
+ //
+ // list& operator= (const list& other)
+ //
+ bufferlist bl;
+ bl.append("ABC", 3);
+ {
+ std::string dest;
+ bl.begin(1).copy(1, dest);
+ ASSERT_EQ('B', dest[0]);
+ }
+ {
+ bufferlist copy = bl;
+ std::string dest;
+ copy.begin(1).copy(1, dest);
+ ASSERT_EQ('B', dest[0]);
+ }
+
+ //
+ // list& operator= (list&& other)
+ //
+ bufferlist move;
+ move = std::move(bl);
+ {
+ std::string dest;
+ move.begin(1).copy(1, dest);
+ ASSERT_EQ('B', dest[0]);
+ }
+ EXPECT_TRUE(move.length());
+ EXPECT_TRUE(!bl.length());
+}
+
+TEST(BufferList, buffers) {
+ bufferlist bl;
+ ASSERT_EQ((unsigned)0, bl.get_num_buffers());
+ bl.append('A');
+ ASSERT_EQ((unsigned)1, bl.get_num_buffers());
+}
+
+TEST(BufferList, to_str) {
+ {
+ bufferlist bl;
+ bl.append("foo");
+ ASSERT_EQ(bl.to_str(), string("foo"));
+ }
+ {
+ bufferptr a("foobarbaz", 9);
+ bufferptr b("123456789", 9);
+ bufferptr c("ABCDEFGHI", 9);
+ bufferlist bl;
+ bl.append(a);
+ bl.append(b);
+ bl.append(c);
+ ASSERT_EQ(bl.to_str(), string("foobarbaz123456789ABCDEFGHI"));
+ }
+}
+
+TEST(BufferList, swap) {
+ bufferlist b1;
+ b1.append('A');
+
+ bufferlist b2;
+ b2.append('B');
+
+ b1.swap(b2);
+
+ std::string s1;
+ b1.begin().copy(1, s1);
+ ASSERT_EQ('B', s1[0]);
+
+ std::string s2;
+ b2.begin().copy(1, s2);
+ ASSERT_EQ('A', s2[0]);
+}
+
+TEST(BufferList, length) {
+ bufferlist bl;
+ ASSERT_EQ((unsigned)0, bl.length());
+ bl.append('A');
+ ASSERT_EQ((unsigned)1, bl.length());
+}
+
+TEST(BufferList, contents_equal) {
+ //
+ // A BB
+ // AB B
+ //
+ bufferlist bl1;
+ bl1.append("A");
+ bl1.append("BB");
+ bufferlist bl2;
+ ASSERT_FALSE(bl1.contents_equal(bl2)); // different length
+ bl2.append("AB");
+ bl2.append("B");
+ ASSERT_TRUE(bl1.contents_equal(bl2)); // same length same content
+ //
+ // ABC
+ //
+ bufferlist bl3;
+ bl3.append("ABC");
+ ASSERT_FALSE(bl1.contents_equal(bl3)); // same length different content
+}
+
+TEST(BufferList, is_aligned) {
+ const int SIMD_ALIGN = 32;
+ {
+ bufferlist bl;
+ EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
+ }
+ {
+ bufferlist bl;
+ bufferptr ptr(buffer::create_aligned(2, SIMD_ALIGN));
+ ptr.set_offset(1);
+ ptr.set_length(1);
+ bl.append(ptr);
+ EXPECT_FALSE(bl.is_aligned(SIMD_ALIGN));
+ bl.rebuild_aligned(SIMD_ALIGN);
+ EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
+ }
+ {
+ bufferlist bl;
+ bufferptr ptr(buffer::create_aligned(SIMD_ALIGN + 1, SIMD_ALIGN));
+ ptr.set_offset(1);
+ ptr.set_length(SIMD_ALIGN);
+ bl.append(ptr);
+ EXPECT_FALSE(bl.is_aligned(SIMD_ALIGN));
+ bl.rebuild_aligned(SIMD_ALIGN);
+ EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
+ }
+}
+
+TEST(BufferList, is_n_align_sized) {
+ const int SIMD_ALIGN = 32;
+ {
+ bufferlist bl;
+ EXPECT_TRUE(bl.is_n_align_sized(SIMD_ALIGN));
+ }
+ {
+ bufferlist bl;
+ bl.append_zero(1);
+ EXPECT_FALSE(bl.is_n_align_sized(SIMD_ALIGN));
+ }
+ {
+ bufferlist bl;
+ bl.append_zero(SIMD_ALIGN);
+ EXPECT_TRUE(bl.is_n_align_sized(SIMD_ALIGN));
+ }
+}
+
+TEST(BufferList, is_page_aligned) {
+ {
+ bufferlist bl;
+ EXPECT_TRUE(bl.is_page_aligned());
+ }
+ {
+ bufferlist bl;
+ bufferptr ptr(buffer::create_page_aligned(2));
+ ptr.set_offset(1);
+ ptr.set_length(1);
+ bl.append(ptr);
+ EXPECT_FALSE(bl.is_page_aligned());
+ bl.rebuild_page_aligned();
+ EXPECT_TRUE(bl.is_page_aligned());
+ }
+ {
+ bufferlist bl;
+ bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
+ ptr.set_offset(1);
+ ptr.set_length(CEPH_PAGE_SIZE);
+ bl.append(ptr);
+ EXPECT_FALSE(bl.is_page_aligned());
+ bl.rebuild_page_aligned();
+ EXPECT_TRUE(bl.is_page_aligned());
+ }
+}
+
+TEST(BufferList, is_n_page_sized) {
+ {
+ bufferlist bl;
+ EXPECT_TRUE(bl.is_n_page_sized());
+ }
+ {
+ bufferlist bl;
+ bl.append_zero(1);
+ EXPECT_FALSE(bl.is_n_page_sized());
+ }
+ {
+ bufferlist bl;
+ bl.append_zero(CEPH_PAGE_SIZE);
+ EXPECT_TRUE(bl.is_n_page_sized());
+ }
+}
+
+TEST(BufferList, page_aligned_appender) {
+ bufferlist bl;
+ {
+ auto a = bl.get_page_aligned_appender(5);
+ a.append("asdf", 4);
+ cout << bl << std::endl;
+ ASSERT_EQ(1u, bl.get_num_buffers());
+ ASSERT_TRUE(bl.contents_equal("asdf", 4));
+ a.append("asdf", 4);
+ for (unsigned n = 0; n < 3 * CEPH_PAGE_SIZE; ++n) {
+ a.append("x", 1);
+ }
+ cout << bl << std::endl;
+ ASSERT_EQ(1u, bl.get_num_buffers());
+ // verify the beginning
+ {
+ bufferlist t;
+ t.substr_of(bl, 0, 10);
+ ASSERT_TRUE(t.contents_equal("asdfasdfxx", 10));
+ }
+ for (unsigned n = 0; n < 3 * CEPH_PAGE_SIZE; ++n) {
+ a.append("y", 1);
+ }
+ cout << bl << std::endl;
+ ASSERT_EQ(2u, bl.get_num_buffers());
+
+ a.append_zero(42);
+ // ensure append_zero didn't introduce a fragmentation
+ ASSERT_EQ(2u, bl.get_num_buffers());
+ // verify the end is actually zeroed
+ {
+ bufferlist t;
+ t.substr_of(bl, bl.length() - 42, 42);
+ ASSERT_TRUE(t.is_zero());
+ }
+
+ // let's check whether appending a bufferlist directly to `bl`
+ // doesn't fragment further C string appends via appender.
+ {
+ const auto& initial_back = bl.back();
+ {
+ bufferlist src;
+ src.append("abc", 3);
+ bl.claim_append(src);
+ // surely the extra `ptr_node` taken from `src` must get
+ // reflected in the `bl` instance
+ ASSERT_EQ(3u, bl.get_num_buffers());
+ }
+
+ // moreover, the next C string-taking `append()` had to
+ // create anoter `ptr_node` instance but...
+ a.append("xyz", 3);
+ ASSERT_EQ(4u, bl.get_num_buffers());
+
+ // ... it should point to the same `buffer::raw` instance
+ // (to the same same block of memory).
+ ASSERT_EQ(bl.back().raw_c_str(), initial_back.raw_c_str());
+ }
+
+ // check whether it'll take the first byte only and whether
+ // the auto-flushing works.
+ for (unsigned n = 0; n < 10 * CEPH_PAGE_SIZE - 3; ++n) {
+ a.append("zasdf", 1);
+ }
+ }
+
+ {
+ cout << bl << std::endl;
+ ASSERT_EQ(6u, bl.get_num_buffers());
+ }
+
+ // Verify that `page_aligned_appender` does respect the carrying
+ // `_carriage` over multiple allocations. Although `append_zero()`
+ // is used here, this affects other members of the append family.
+ // This part would be crucial for e.g. `encode()`.
+ {
+ bl.append_zero(42);
+ cout << bl << std::endl;
+ ASSERT_EQ(6u, bl.get_num_buffers());
+ }
+}
+
+TEST(BufferList, rebuild_aligned_size_and_memory) {
+ const unsigned SIMD_ALIGN = 32;
+ const unsigned BUFFER_SIZE = 67;
+
+ bufferlist bl;
+ // These two must be concatenated into one memory + size aligned
+ // bufferptr
+ {
+ bufferptr ptr(buffer::create_aligned(2, SIMD_ALIGN));
+ ptr.set_offset(1);
+ ptr.set_length(1);
+ bl.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_aligned(BUFFER_SIZE - 1, SIMD_ALIGN));
+ bl.append(ptr);
+ }
+ // This one must be left alone
+ {
+ bufferptr ptr(buffer::create_aligned(BUFFER_SIZE, SIMD_ALIGN));
+ bl.append(ptr);
+ }
+ // These two must be concatenated into one memory + size aligned
+ // bufferptr
+ {
+ bufferptr ptr(buffer::create_aligned(2, SIMD_ALIGN));
+ ptr.set_offset(1);
+ ptr.set_length(1);
+ bl.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_aligned(BUFFER_SIZE - 1, SIMD_ALIGN));
+ bl.append(ptr);
+ }
+ EXPECT_FALSE(bl.is_aligned(SIMD_ALIGN));
+ EXPECT_FALSE(bl.is_n_align_sized(BUFFER_SIZE));
+ EXPECT_EQ(BUFFER_SIZE * 3, bl.length());
+ EXPECT_FALSE(bl.front().is_aligned(SIMD_ALIGN));
+ EXPECT_FALSE(bl.front().is_n_align_sized(BUFFER_SIZE));
+ EXPECT_EQ(5U, bl.get_num_buffers());
+ bl.rebuild_aligned_size_and_memory(BUFFER_SIZE, SIMD_ALIGN);
+ EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
+ EXPECT_TRUE(bl.is_n_align_sized(BUFFER_SIZE));
+ EXPECT_EQ(3U, bl.get_num_buffers());
+
+ {
+ /* bug replicator, to test rebuild_aligned_size_and_memory() in the
+ * scenario where the first bptr is both size and memory aligned and
+ * the second is 0-length */
+ bl.clear();
+ bl.append(bufferptr{buffer::create_aligned(4096, 4096)});
+ bufferptr ptr(buffer::create_aligned(42, 4096));
+ /* bl.back().length() must be 0. offset set to 42 guarantees
+ * the entire list is unaligned. */
+ bl.append(ptr, 42, 0);
+ EXPECT_EQ(bl.get_num_buffers(), 2);
+ EXPECT_EQ(bl.back().length(), 0);
+ EXPECT_FALSE(bl.is_aligned(4096));
+ /* rebuild_aligned() calls rebuild_aligned_size_and_memory().
+ * we assume the rebuild always happens. */
+ EXPECT_TRUE(bl.rebuild_aligned(4096));
+ EXPECT_EQ(bl.get_num_buffers(), 1);
+ }
+}
+
+TEST(BufferList, is_zero) {
+ {
+ bufferlist bl;
+ EXPECT_TRUE(bl.is_zero());
+ }
+ {
+ bufferlist bl;
+ bl.append('A');
+ EXPECT_FALSE(bl.is_zero());
+ }
+ {
+ bufferlist bl;
+ bl.append_zero(1);
+ EXPECT_TRUE(bl.is_zero());
+ }
+
+ for (size_t i = 1; i <= 256; ++i) {
+ bufferlist bl;
+ bl.append_zero(i);
+ EXPECT_TRUE(bl.is_zero());
+ bl.append('A');
+ // ensure buffer is a single, contiguous before testing
+ bl.rebuild();
+ EXPECT_FALSE(bl.is_zero());
+ }
+
+}
+
+TEST(BufferList, clear) {
+ bufferlist bl;
+ unsigned len = 17;
+ bl.append_zero(len);
+ bl.clear();
+ EXPECT_EQ((unsigned)0, bl.length());
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+}
+
+TEST(BufferList, push_back) {
+ //
+ // void push_back(ptr& bp)
+ //
+ {
+ bufferlist bl;
+ bufferptr ptr;
+ bl.push_back(ptr);
+ EXPECT_EQ((unsigned)0, bl.length());
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ }
+ unsigned len = 17;
+ {
+ bufferlist bl;
+ bl.append('A');
+ bufferptr ptr(len);
+ ptr.c_str()[0] = 'B';
+ bl.push_back(ptr);
+ EXPECT_EQ((unsigned)(1 + len), bl.length());
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ EXPECT_EQ('B', bl.back()[0]);
+ const bufferptr& back_bp = bl.back();
+ EXPECT_EQ(static_cast<instrumented_bptr&>(ptr).get_raw(),
+ static_cast<const instrumented_bptr&>(back_bp).get_raw());
+ }
+ //
+ // void push_back(ptr&& bp)
+ //
+ {
+ bufferlist bl;
+ bufferptr ptr;
+ bl.push_back(std::move(ptr));
+ EXPECT_EQ((unsigned)0, bl.length());
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ }
+ {
+ bufferlist bl;
+ bl.append('A');
+ bufferptr ptr(len);
+ ptr.c_str()[0] = 'B';
+ bl.push_back(std::move(ptr));
+ EXPECT_EQ((unsigned)(1 + len), bl.length());
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ EXPECT_EQ('B', bl.buffers().back()[0]);
+ EXPECT_FALSE(static_cast<instrumented_bptr&>(ptr).get_raw());
+ }
+}
+
+TEST(BufferList, is_contiguous) {
+ bufferlist bl;
+ EXPECT_TRUE(bl.is_contiguous());
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ bl.append('A');
+ EXPECT_TRUE(bl.is_contiguous());
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ bufferptr ptr(1);
+ bl.push_back(ptr);
+ EXPECT_FALSE(bl.is_contiguous());
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+}
+
+TEST(BufferList, rebuild) {
+ {
+ bufferlist bl;
+ bufferptr ptr(buffer::create_page_aligned(2));
+ ptr[0] = 'X';
+ ptr[1] = 'Y';
+ ptr.set_offset(1);
+ ptr.set_length(1);
+ bl.append(ptr);
+ EXPECT_FALSE(bl.is_page_aligned());
+ bl.rebuild();
+ EXPECT_EQ(1U, bl.length());
+ EXPECT_EQ('Y', *bl.begin());
+ }
+ {
+ bufferlist bl;
+ const std::string str(CEPH_PAGE_SIZE, 'X');
+ bl.append(str.c_str(), str.size());
+ bl.append(str.c_str(), str.size());
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ //EXPECT_TRUE(bl.is_aligned(CEPH_BUFFER_APPEND_SIZE));
+ bl.rebuild();
+ EXPECT_TRUE(bl.is_page_aligned());
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ }
+ {
+ bufferlist bl;
+ char t1[] = "X";
+ bufferlist a2;
+ a2.append(t1, 1);
+ bl.rebuild();
+ bl.append(a2);
+ EXPECT_EQ((unsigned)1, bl.length());
+ bufferlist::iterator p = bl.begin();
+ char dst[1];
+ p.copy(1, dst);
+ EXPECT_EQ(0, memcmp(dst, "X", 1));
+ }
+}
+
+TEST(BufferList, rebuild_page_aligned) {
+ {
+ bufferlist bl;
+ {
+ bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
+ ptr.set_offset(1);
+ ptr.set_length(CEPH_PAGE_SIZE);
+ bl.append(ptr);
+ }
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_FALSE(bl.is_page_aligned());
+ bl.rebuild_page_aligned();
+ EXPECT_TRUE(bl.is_page_aligned());
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ }
+ {
+ bufferlist bl;
+ bufferptr ptr(buffer::create_page_aligned(1));
+ char *p = ptr.c_str();
+ bl.append(ptr);
+ bl.rebuild_page_aligned();
+ EXPECT_EQ(p, bl.front().c_str());
+ }
+ {
+ bufferlist bl;
+ {
+ bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE));
+ EXPECT_TRUE(ptr.is_page_aligned());
+ EXPECT_TRUE(ptr.is_n_page_sized());
+ bl.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
+ EXPECT_TRUE(ptr.is_page_aligned());
+ EXPECT_FALSE(ptr.is_n_page_sized());
+ bl.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_page_aligned(2));
+ ptr.set_offset(1);
+ ptr.set_length(1);
+ EXPECT_FALSE(ptr.is_page_aligned());
+ EXPECT_FALSE(ptr.is_n_page_sized());
+ bl.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE - 2));
+ EXPECT_TRUE(ptr.is_page_aligned());
+ EXPECT_FALSE(ptr.is_n_page_sized());
+ bl.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE));
+ EXPECT_TRUE(ptr.is_page_aligned());
+ EXPECT_TRUE(ptr.is_n_page_sized());
+ bl.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
+ ptr.set_offset(1);
+ ptr.set_length(CEPH_PAGE_SIZE);
+ EXPECT_FALSE(ptr.is_page_aligned());
+ EXPECT_TRUE(ptr.is_n_page_sized());
+ bl.append(ptr);
+ }
+ EXPECT_EQ((unsigned)6, bl.get_num_buffers());
+ EXPECT_TRUE((bl.length() & ~CEPH_PAGE_MASK) == 0);
+ EXPECT_FALSE(bl.is_page_aligned());
+ bl.rebuild_page_aligned();
+ EXPECT_TRUE(bl.is_page_aligned());
+ EXPECT_EQ((unsigned)4, bl.get_num_buffers());
+ }
+}
+
+TEST(BufferList, claim_append) {
+ bufferlist from;
+ {
+ bufferptr ptr(2);
+ from.append(ptr);
+ }
+ bufferlist to;
+ {
+ bufferptr ptr(4);
+ to.append(ptr);
+ }
+ EXPECT_EQ((unsigned)4, to.length());
+ EXPECT_EQ((unsigned)1, to.get_num_buffers());
+ to.claim_append(from);
+ EXPECT_EQ((unsigned)(4 + 2), to.length());
+ EXPECT_EQ((unsigned)4, to.front().length());
+ EXPECT_EQ((unsigned)2, to.back().length());
+ EXPECT_EQ((unsigned)2, to.get_num_buffers());
+ EXPECT_EQ((unsigned)0, from.get_num_buffers());
+ EXPECT_EQ((unsigned)0, from.length());
+}
+
+TEST(BufferList, begin) {
+ bufferlist bl;
+ bl.append("ABC");
+ bufferlist::iterator i = bl.begin();
+ EXPECT_EQ('A', *i);
+}
+
+TEST(BufferList, end) {
+ bufferlist bl;
+ bl.append("AB");
+ bufferlist::iterator i = bl.end();
+ bl.append("C");
+ EXPECT_EQ('C', bl[i.get_off()]);
+}
+
+TEST(BufferList, append) {
+ //
+ // void append(char c);
+ //
+ {
+ bufferlist bl;
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ bl.append('A');
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ //EXPECT_TRUE(bl.is_aligned(CEPH_BUFFER_APPEND_SIZE));
+ }
+ //
+ // void append(const char *data, unsigned len);
+ //
+ {
+ bufferlist bl(CEPH_PAGE_SIZE);
+ std::string str(CEPH_PAGE_SIZE * 2, 'X');
+ bl.append(str.c_str(), str.size());
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ EXPECT_EQ(CEPH_PAGE_SIZE, bl.front().length());
+ EXPECT_EQ(CEPH_PAGE_SIZE, bl.back().length());
+ }
+ //
+ // void append(const std::string& s);
+ //
+ {
+ bufferlist bl(CEPH_PAGE_SIZE);
+ std::string str(CEPH_PAGE_SIZE * 2, 'X');
+ bl.append(str);
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ EXPECT_EQ(CEPH_PAGE_SIZE, bl.front().length());
+ EXPECT_EQ(CEPH_PAGE_SIZE, bl.back().length());
+ }
+ //
+ // void append(const ptr& bp);
+ //
+ {
+ bufferlist bl;
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)0, bl.length());
+ {
+ bufferptr ptr;
+ bl.append(ptr);
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)0, bl.length());
+ }
+ {
+ bufferptr ptr(3);
+ bl.append(ptr);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)3, bl.length());
+ }
+ }
+ //
+ // void append(const ptr& bp, unsigned off, unsigned len);
+ //
+ {
+ bufferlist bl;
+ bl.append('A');
+ bufferptr back(bl.back());
+ bufferptr in(back);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)1, bl.length());
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(bl.append(in, (unsigned)100, (unsigned)100), "");
+ }
+ EXPECT_LT((unsigned)0, in.unused_tail_length());
+ in.append('B');
+ bl.append(in, back.end(), 1);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)2, bl.length());
+ EXPECT_EQ('B', bl[1]);
+ }
+ {
+ bufferlist bl;
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)0, bl.length());
+ bufferptr ptr(2);
+ ptr.set_length(0);
+ ptr.append("AB", 2);
+ bl.append(ptr, 1, 1);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)1, bl.length());
+ }
+ //
+ // void append(const list& bl);
+ //
+ {
+ bufferlist bl;
+ bl.append('A');
+ bufferlist other;
+ other.append('B');
+ bl.append(other);
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ EXPECT_EQ('B', bl[1]);
+ }
+ //
+ // void append(std::istream& in);
+ //
+ {
+ bufferlist bl;
+ std::string expected("ABC\nDEF\n");
+ std::istringstream is("ABC\n\nDEF");
+ bl.append(is);
+ EXPECT_EQ(0, ::memcmp(expected.c_str(), bl.c_str(), expected.size()));
+ EXPECT_EQ(expected.size(), bl.length());
+ }
+ //
+ // void append(ptr&& bp);
+ //
+ {
+ bufferlist bl;
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)0, bl.length());
+ {
+ bufferptr ptr;
+ bl.append(std::move(ptr));
+ EXPECT_EQ((unsigned)0, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)0, bl.length());
+ }
+ {
+ bufferptr ptr(3);
+ bl.append(std::move(ptr));
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)3, bl.length());
+ EXPECT_FALSE(static_cast<instrumented_bptr&>(ptr).get_raw());
+ }
+ }
+}
+
+TEST(BufferList, append_hole) {
+ {
+ bufferlist bl;
+ auto filler = bl.append_hole(1);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)1, bl.length());
+
+ bl.append("BC", 2);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)3, bl.length());
+
+ const char a = 'A';
+ filler.copy_in((unsigned)1, &a);
+ EXPECT_EQ((unsigned)3, bl.length());
+
+ EXPECT_EQ(0, ::memcmp("ABC", bl.c_str(), 3));
+ }
+
+ {
+ bufferlist bl;
+ bl.append('A');
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)1, bl.length());
+
+ auto filler = bl.append_hole(1);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)2, bl.length());
+
+ bl.append('C');
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)3, bl.length());
+
+ const char b = 'B';
+ filler.copy_in((unsigned)1, &b);
+ EXPECT_EQ((unsigned)3, bl.length());
+
+ EXPECT_EQ(0, ::memcmp("ABC", bl.c_str(), 3));
+ }
+}
+
+TEST(BufferList, append_zero) {
+ bufferlist bl;
+ bl.append('A');
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)1, bl.length());
+ bl.append_zero(1);
+ EXPECT_EQ((unsigned)1, bl.get_num_buffers());
+ EXPECT_EQ((unsigned)2, bl.length());
+ EXPECT_EQ('\0', bl[1]);
+}
+
+TEST(BufferList, operator_brackets) {
+ bufferlist bl;
+ EXPECT_THROW(bl[1], buffer::end_of_buffer);
+ bl.append('A');
+ bufferlist other;
+ other.append('B');
+ bl.append(other);
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ EXPECT_EQ('B', bl[1]);
+}
+
+TEST(BufferList, c_str) {
+ bufferlist bl;
+ EXPECT_EQ((const char*)NULL, bl.c_str());
+ bl.append('A');
+ bufferlist other;
+ other.append('B');
+ bl.append(other);
+ EXPECT_EQ((unsigned)2, bl.get_num_buffers());
+ EXPECT_EQ(0, ::memcmp("AB", bl.c_str(), 2));
+}
+
+TEST(BufferList, c_str_carriage) {
+ // verify the c_str() optimization for carriage handling
+ buffer::ptr bp("A", 1);
+ bufferlist bl;
+ bl.append(bp);
+ bl.append('B');
+ EXPECT_EQ(2U, bl.get_num_buffers());
+ EXPECT_EQ(2U, bl.length());
+
+ // this should leave an empty bptr for carriage at the end of the bl
+ bl.splice(1, 1);
+ EXPECT_EQ(2U, bl.get_num_buffers());
+ EXPECT_EQ(1U, bl.length());
+
+ std::ignore = bl.c_str();
+ // if we have an empty bptr at the end, we don't need to rebuild
+ EXPECT_EQ(2U, bl.get_num_buffers());
+}
+
+TEST(BufferList, substr_of) {
+ bufferlist bl;
+ EXPECT_THROW(bl.substr_of(bl, 1, 1), buffer::end_of_buffer);
+ const char *s[] = {
+ "ABC",
+ "DEF",
+ "GHI",
+ "JKL"
+ };
+ for (unsigned i = 0; i < 4; i++) {
+ bufferptr ptr(s[i], strlen(s[i]));
+ bl.push_back(ptr);
+ }
+ EXPECT_EQ((unsigned)4, bl.get_num_buffers());
+
+ bufferlist other;
+ other.append("TO BE CLEARED");
+ other.substr_of(bl, 4, 4);
+ EXPECT_EQ((unsigned)2, other.get_num_buffers());
+ EXPECT_EQ((unsigned)4, other.length());
+ EXPECT_EQ(0, ::memcmp("EFGH", other.c_str(), 4));
+}
+
+TEST(BufferList, splice) {
+ bufferlist bl;
+ EXPECT_THROW(bl.splice(1, 1), buffer::end_of_buffer);
+ const char *s[] = {
+ "ABC",
+ "DEF",
+ "GHI",
+ "JKL"
+ };
+ for (unsigned i = 0; i < 4; i++) {
+ bufferptr ptr(s[i], strlen(s[i]));
+ bl.push_back(ptr);
+ }
+ EXPECT_EQ((unsigned)4, bl.get_num_buffers());
+ bl.splice(0, 0);
+
+ bufferlist other;
+ other.append('X');
+ bl.splice(4, 4, &other);
+ EXPECT_EQ((unsigned)3, other.get_num_buffers());
+ EXPECT_EQ((unsigned)5, other.length());
+ EXPECT_EQ(0, ::memcmp("XEFGH", other.c_str(), other.length()));
+ EXPECT_EQ((unsigned)8, bl.length());
+ {
+ bufferlist tmp(bl);
+ EXPECT_EQ(0, ::memcmp("ABCDIJKL", tmp.c_str(), tmp.length()));
+ }
+
+ bl.splice(4, 4);
+ EXPECT_EQ((unsigned)4, bl.length());
+ EXPECT_EQ(0, ::memcmp("ABCD", bl.c_str(), bl.length()));
+
+ {
+ bl.clear();
+ bufferptr ptr1("0123456789", 10);
+ bl.push_back(ptr1);
+ bufferptr ptr2("abcdefghij", 10);
+ bl.append(ptr2, 5, 5);
+ other.clear();
+ bl.splice(10, 4, &other);
+ EXPECT_EQ((unsigned)11, bl.length());
+ EXPECT_EQ(0, ::memcmp("fghi", other.c_str(), other.length()));
+ }
+}
+
+TEST(BufferList, write) {
+ std::ostringstream stream;
+ bufferlist bl;
+ bl.append("ABC");
+ bl.write(1, 2, stream);
+ EXPECT_EQ("BC", stream.str());
+}
+
+TEST(BufferList, encode_base64) {
+ bufferlist bl;
+ bl.append("ABCD");
+ bufferlist other;
+ bl.encode_base64(other);
+ const char *expected = "QUJDRA==";
+ EXPECT_EQ(0, ::memcmp(expected, other.c_str(), strlen(expected)));
+}
+
+TEST(BufferList, decode_base64) {
+ bufferlist bl;
+ bl.append("QUJDRA==");
+ bufferlist other;
+ other.decode_base64(bl);
+ const char *expected = "ABCD";
+ EXPECT_EQ(0, ::memcmp(expected, other.c_str(), strlen(expected)));
+ bufferlist malformed;
+ malformed.append("QUJDRA");
+ EXPECT_THROW(other.decode_base64(malformed), buffer::malformed_input);
+}
+
+TEST(BufferList, hexdump) {
+ bufferlist bl;
+ std::ostringstream stream;
+ bl.append("013245678901234\0006789012345678901234", 32);
+ bl.hexdump(stream);
+ EXPECT_EQ("00000000 30 31 33 32 34 35 36 37 38 39 30 31 32 33 34 00 |013245678901234.|\n"
+ "00000010 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 |6789012345678901|\n"
+ "00000020\n",
+ stream.str());
+}
+
+TEST(BufferList, read_file) {
+ std::string error;
+ bufferlist bl;
+ ::unlink(FILENAME);
+ EXPECT_EQ(-ENOENT, bl.read_file("UNLIKELY", &error));
+ snprintf(cmd, sizeof(cmd), "echo ABC> %s", FILENAME);
+ EXPECT_EQ(0, ::system(cmd));
+ #ifndef _WIN32
+ snprintf(cmd, sizeof(cmd), "chmod 0 %s", FILENAME);
+ EXPECT_EQ(0, ::system(cmd));
+ if (getuid() != 0) {
+ EXPECT_EQ(-EACCES, bl.read_file(FILENAME, &error));
+ }
+ snprintf(cmd, sizeof(cmd), "chmod +r %s", FILENAME);
+ EXPECT_EQ(0, ::system(cmd));
+ #endif /* _WIN32 */
+ EXPECT_EQ(0, bl.read_file(FILENAME, &error));
+ ::unlink(FILENAME);
+ EXPECT_EQ((unsigned)4, bl.length());
+ std::string actual(bl.c_str(), bl.length());
+ EXPECT_EQ("ABC\n", actual);
+}
+
+TEST(BufferList, read_fd) {
+ unsigned len = 4;
+ ::unlink(FILENAME);
+ snprintf(cmd, sizeof(cmd), "echo ABC > %s", FILENAME);
+ EXPECT_EQ(0, ::system(cmd));
+ int fd = -1;
+ bufferlist bl;
+ EXPECT_EQ(-EBADF, bl.read_fd(fd, len));
+ fd = ::open(FILENAME, O_RDONLY);
+ ASSERT_NE(-1, fd);
+ EXPECT_EQ(len, (unsigned)bl.read_fd(fd, len));
+ //EXPECT_EQ(CEPH_BUFFER_APPEND_SIZE - len, bl.front().unused_tail_length());
+ EXPECT_EQ(len, bl.length());
+ ::close(fd);
+ ::unlink(FILENAME);
+}
+
+TEST(BufferList, write_file) {
+ ::unlink(FILENAME);
+ int mode = 0600;
+ bufferlist bl;
+ EXPECT_EQ(-ENOENT, bl.write_file("un/like/ly", mode));
+ bl.append("ABC");
+ EXPECT_EQ(0, bl.write_file(FILENAME, mode));
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ ASSERT_EQ(0, ::stat(FILENAME, &st));
+ #ifndef _WIN32
+ EXPECT_EQ((unsigned)(mode | S_IFREG), st.st_mode);
+ #endif
+ ::unlink(FILENAME);
+}
+
+TEST(BufferList, write_fd) {
+ ::unlink(FILENAME);
+ int fd = ::open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ ASSERT_NE(-1, fd);
+ bufferlist bl;
+ for (unsigned i = 0; i < IOV_MAX * 2; i++) {
+ bufferptr ptr("A", 1);
+ bl.push_back(ptr);
+ }
+ EXPECT_EQ(0, bl.write_fd(fd));
+ ::close(fd);
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ ASSERT_EQ(0, ::stat(FILENAME, &st));
+ EXPECT_EQ(IOV_MAX * 2, st.st_size);
+ ::unlink(FILENAME);
+}
+
+TEST(BufferList, write_fd_offset) {
+ ::unlink(FILENAME);
+ int fd = ::open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ ASSERT_NE(-1, fd);
+ bufferlist bl;
+ for (unsigned i = 0; i < IOV_MAX * 2; i++) {
+ bufferptr ptr("A", 1);
+ bl.push_back(ptr);
+ }
+ uint64_t offset = 200;
+ EXPECT_EQ(0, bl.write_fd(fd, offset));
+ ::close(fd);
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ ASSERT_EQ(0, ::stat(FILENAME, &st));
+ EXPECT_EQ(IOV_MAX * 2 + offset, (unsigned)st.st_size);
+ ::unlink(FILENAME);
+}
+
+TEST(BufferList, crc32c) {
+ bufferlist bl;
+ __u32 crc = 0;
+ bl.append("A");
+ crc = bl.crc32c(crc);
+ EXPECT_EQ((unsigned)0xB3109EBF, crc);
+ crc = bl.crc32c(crc);
+ EXPECT_EQ((unsigned)0x5FA5C0CC, crc);
+}
+
+TEST(BufferList, crc32c_append) {
+ bufferlist bl1;
+ bufferlist bl2;
+
+ for (int j = 0; j < 200; ++j) {
+ bufferlist bl;
+ for (int i = 0; i < 200; ++i) {
+ char x = rand();
+ bl.append(x);
+ bl1.append(x);
+ }
+ bl.crc32c(rand()); // mess with the cached bufferptr crc values
+ bl2.append(bl);
+ }
+ ASSERT_EQ(bl1.crc32c(0), bl2.crc32c(0));
+}
+
+TEST(BufferList, crc32c_zeros) {
+ char buffer[4*1024];
+ for (size_t i=0; i < sizeof(buffer); i++)
+ {
+ buffer[i] = i;
+ }
+
+ bufferlist bla;
+ bufferlist blb;
+
+ for (size_t j=0; j < 1000; j++)
+ {
+ bufferptr a(buffer, sizeof(buffer));
+
+ bla.push_back(a);
+ uint32_t crca = bla.crc32c(111);
+
+ blb.push_back(a);
+ uint32_t crcb = ceph_crc32c(111, (unsigned char*)blb.c_str(), blb.length());
+
+ EXPECT_EQ(crca, crcb);
+ }
+}
+
+TEST(BufferList, crc32c_append_perf) {
+ int len = 256 * 1024 * 1024;
+ bufferptr a(len);
+ bufferptr b(len);
+ bufferptr c(len);
+ bufferptr d(len);
+ std::cout << "populating large buffers (a, b=c=d)" << std::endl;
+ char *pa = a.c_str();
+ char *pb = b.c_str();
+ char *pc = c.c_str();
+ char *pd = c.c_str();
+ for (int i=0; i<len; i++) {
+ pa[i] = (i & 0xff) ^ 73;
+ pb[i] = (i & 0xff) ^ 123;
+ pc[i] = (i & 0xff) ^ 123;
+ pd[i] = (i & 0xff) ^ 123;
+ }
+
+ // track usage of cached crcs
+ buffer::track_cached_crc(true);
+
+ [[maybe_unused]] int base_cached = buffer::get_cached_crc();
+ [[maybe_unused]] int base_cached_adjusted = buffer::get_cached_crc_adjusted();
+
+ bufferlist bla;
+ bla.push_back(a);
+ bufferlist blb;
+ blb.push_back(b);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = bla.crc32c(0);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "a.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 1138817026u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 0 + base_cached);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = bla.crc32c(0);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "a.crc32c(0) (again) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 1138817026u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
+
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = bla.crc32c(5);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "a.crc32c(5) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 3239494520u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
+ ceph_assert(buffer::get_cached_crc_adjusted() == 1 + base_cached_adjusted);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = bla.crc32c(5);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "a.crc32c(5) (again) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 3239494520u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
+ ceph_assert(buffer::get_cached_crc_adjusted() == 2 + base_cached_adjusted);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = blb.crc32c(0);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "b.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 2481791210u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = blb.crc32c(0);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "b.crc32c(0) (again)= " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 2481791210u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 2 + base_cached);
+
+ bufferlist ab;
+ ab.push_back(a);
+ ab.push_back(b);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = ab.crc32c(0);
+ utime_t end = ceph_clock_now();
+ float rate = (float)ab.length() / (float)(1024*1024) / (float)(end - start);
+ std::cout << "ab.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 2988268779u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 3 + base_cached);
+ ceph_assert(buffer::get_cached_crc_adjusted() == 3 + base_cached_adjusted);
+ bufferlist ac;
+ ac.push_back(a);
+ ac.push_back(c);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = ac.crc32c(0);
+ utime_t end = ceph_clock_now();
+ float rate = (float)ac.length() / (float)(1024*1024) / (float)(end - start);
+ std::cout << "ac.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 2988268779u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 4 + base_cached);
+ ceph_assert(buffer::get_cached_crc_adjusted() == 3 + base_cached_adjusted);
+
+ bufferlist ba;
+ ba.push_back(b);
+ ba.push_back(a);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = ba.crc32c(0);
+ utime_t end = ceph_clock_now();
+ float rate = (float)ba.length() / (float)(1024*1024) / (float)(end - start);
+ std::cout << "ba.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 169240695u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 5 + base_cached);
+ ceph_assert(buffer::get_cached_crc_adjusted() == 4 + base_cached_adjusted);
+ {
+ utime_t start = ceph_clock_now();
+ uint32_t r = ba.crc32c(5);
+ utime_t end = ceph_clock_now();
+ float rate = (float)ba.length() / (float)(1024*1024) / (float)(end - start);
+ std::cout << "ba.crc32c(5) = " << r << " at " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(r, 1265464778u);
+ }
+ ceph_assert(buffer::get_cached_crc() == 5 + base_cached);
+ ceph_assert(buffer::get_cached_crc_adjusted() == 6 + base_cached_adjusted);
+
+ cout << "crc cache hits (same start) = " << buffer::get_cached_crc() << std::endl;
+ cout << "crc cache hits (adjusted) = " << buffer::get_cached_crc_adjusted() << std::endl;
+}
+
+TEST(BufferList, compare) {
+ bufferlist a;
+ a.append("A");
+ bufferlist ab; // AB in segments
+ ab.append(bufferptr("A", 1));
+ ab.append(bufferptr("B", 1));
+ bufferlist ac;
+ ac.append("AC");
+ //
+ // bool operator>(bufferlist& l, bufferlist& r)
+ //
+ ASSERT_FALSE(a > ab);
+ ASSERT_TRUE(ab > a);
+ ASSERT_TRUE(ac > ab);
+ ASSERT_FALSE(ab > ac);
+ ASSERT_FALSE(ab > ab);
+ //
+ // bool operator>=(bufferlist& l, bufferlist& r)
+ //
+ ASSERT_FALSE(a >= ab);
+ ASSERT_TRUE(ab >= a);
+ ASSERT_TRUE(ac >= ab);
+ ASSERT_FALSE(ab >= ac);
+ ASSERT_TRUE(ab >= ab);
+ //
+ // bool operator<(bufferlist& l, bufferlist& r)
+ //
+ ASSERT_TRUE(a < ab);
+ ASSERT_FALSE(ab < a);
+ ASSERT_FALSE(ac < ab);
+ ASSERT_TRUE(ab < ac);
+ ASSERT_FALSE(ab < ab);
+ //
+ // bool operator<=(bufferlist& l, bufferlist& r)
+ //
+ ASSERT_TRUE(a <= ab);
+ ASSERT_FALSE(ab <= a);
+ ASSERT_FALSE(ac <= ab);
+ ASSERT_TRUE(ab <= ac);
+ ASSERT_TRUE(ab <= ab);
+ //
+ // bool operator==(bufferlist &l, bufferlist &r)
+ //
+ ASSERT_FALSE(a == ab);
+ ASSERT_FALSE(ac == ab);
+ ASSERT_TRUE(ab == ab);
+}
+
+TEST(BufferList, ostream) {
+ std::ostringstream stream;
+ bufferlist bl;
+ const char *s[] = {
+ "ABC",
+ "DEF"
+ };
+ for (unsigned i = 0; i < 2; i++) {
+ bufferptr ptr(s[i], strlen(s[i]));
+ bl.push_back(ptr);
+ }
+ stream << bl;
+ std::cerr << stream.str() << std::endl;
+ EXPECT_GT(stream.str().size(), stream.str().find("list(len=6,"));
+ EXPECT_GT(stream.str().size(), stream.str().find("len 3 nref 1),\n"));
+ EXPECT_GT(stream.str().size(), stream.str().find("len 3 nref 1)\n"));
+}
+
+TEST(BufferList, zero) {
+ //
+ // void zero()
+ //
+ {
+ bufferlist bl;
+ bl.append('A');
+ EXPECT_EQ('A', bl[0]);
+ bl.zero();
+ EXPECT_EQ('\0', bl[0]);
+ }
+ //
+ // void zero(unsigned o, unsigned l)
+ //
+ const char *s[] = {
+ "ABC",
+ "DEF",
+ "GHI",
+ "KLM"
+ };
+ {
+ bufferlist bl;
+ bufferptr ptr(s[0], strlen(s[0]));
+ bl.push_back(ptr);
+ bl.zero((unsigned)0, (unsigned)1);
+ EXPECT_EQ(0, ::memcmp("\0BC", bl.c_str(), 3));
+ }
+ {
+ bufferlist bl;
+ for (unsigned i = 0; i < 4; i++) {
+ bufferptr ptr(s[i], strlen(s[i]));
+ bl.push_back(ptr);
+ }
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(bl.zero((unsigned)0, (unsigned)2000), "");
+ }
+ bl.zero((unsigned)2, (unsigned)5);
+ EXPECT_EQ(0, ::memcmp("AB\0\0\0\0\0HIKLM", bl.c_str(), 9));
+ }
+ {
+ bufferlist bl;
+ for (unsigned i = 0; i < 4; i++) {
+ bufferptr ptr(s[i], strlen(s[i]));
+ bl.push_back(ptr);
+ }
+ bl.zero((unsigned)3, (unsigned)3);
+ EXPECT_EQ(0, ::memcmp("ABC\0\0\0GHIKLM", bl.c_str(), 9));
+ }
+ {
+ bufferlist bl;
+ bufferptr ptr1(4);
+ bufferptr ptr2(4);
+ memset(ptr1.c_str(), 'a', 4);
+ memset(ptr2.c_str(), 'b', 4);
+ bl.append(ptr1);
+ bl.append(ptr2);
+ bl.zero((unsigned)2, (unsigned)4);
+ EXPECT_EQ(0, ::memcmp("aa\0\0\0\0bb", bl.c_str(), 8));
+ }
+}
+
+TEST(BufferList, EmptyAppend) {
+ bufferlist bl;
+ bufferptr ptr;
+ bl.push_back(ptr);
+ ASSERT_EQ(bl.begin().end(), 1);
+}
+
+TEST(BufferList, InternalCarriage) {
+ ceph::bufferlist bl;
+ EXPECT_EQ(bl.get_num_buffers(), 0u);
+
+ encode(int64_t(42), bl);
+ EXPECT_EQ(bl.get_num_buffers(), 1u);
+
+ {
+ ceph::bufferlist bl_with_foo;
+ bl_with_foo.append("foo", 3);
+ EXPECT_EQ(bl_with_foo.length(), 3u);
+ EXPECT_EQ(bl_with_foo.get_num_buffers(), 1u);
+
+ bl.append(bl_with_foo);
+ EXPECT_EQ(bl.get_num_buffers(), 2u);
+ }
+
+ encode(int64_t(24), bl);
+ EXPECT_EQ(bl.get_num_buffers(), 3u);
+}
+
+TEST(BufferList, ContiguousAppender) {
+ ceph::bufferlist bl;
+ EXPECT_EQ(bl.get_num_buffers(), 0u);
+
+ // we expect a flush in ~contiguous_appender
+ {
+ auto ap = bl.get_contiguous_appender(100);
+
+ denc(int64_t(42), ap);
+ EXPECT_EQ(bl.get_num_buffers(), 1u);
+
+ // append bufferlist with single ptr inside. This should
+ // commit changes to bl::_len and the underlying bp::len.
+ {
+ ceph::bufferlist bl_with_foo;
+ bl_with_foo.append("foo", 3);
+ EXPECT_EQ(bl_with_foo.length(), 3u);
+ EXPECT_EQ(bl_with_foo.get_num_buffers(), 1u);
+
+ ap.append(bl_with_foo);
+ // 3 as the ap::append(const bl&) splits the bp with free
+ // space.
+ EXPECT_EQ(bl.get_num_buffers(), 3u);
+ }
+
+ denc(int64_t(24), ap);
+ EXPECT_EQ(bl.get_num_buffers(), 3u);
+ EXPECT_EQ(bl.length(), sizeof(int64_t) + 3u);
+ }
+ EXPECT_EQ(bl.length(), 2u * sizeof(int64_t) + 3u);
+}
+
+TEST(BufferList, TestPtrAppend) {
+ bufferlist bl;
+ char correct[MAX_TEST];
+ int curpos = 0;
+ int length = random() % 5 > 0 ? random() % 1000 : 0;
+ while (curpos + length < MAX_TEST) {
+ if (!length) {
+ bufferptr ptr;
+ bl.push_back(ptr);
+ } else {
+ char *current = correct + curpos;
+ for (int i = 0; i < length; ++i) {
+ char next = random() % 255;
+ correct[curpos++] = next;
+ }
+ bufferptr ptr(current, length);
+ bl.append(ptr);
+ }
+ length = random() % 5 > 0 ? random() % 1000 : 0;
+ }
+ ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
+}
+
+TEST(BufferList, TestDirectAppend) {
+ bufferlist bl;
+ char correct[MAX_TEST];
+ int curpos = 0;
+ int length = random() % 5 > 0 ? random() % 1000 : 0;
+ while (curpos + length < MAX_TEST) {
+ char *current = correct + curpos;
+ for (int i = 0; i < length; ++i) {
+ char next = random() % 255;
+ correct[curpos++] = next;
+ }
+ bl.append(current, length);
+ length = random() % 5 > 0 ? random() % 1000 : 0;
+ }
+ ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
+}
+
+TEST(BufferList, TestCopyAll) {
+ const static size_t BIG_SZ = 10737414;
+ std::shared_ptr <unsigned char> big(
+ (unsigned char*)malloc(BIG_SZ), free);
+ unsigned char c = 0;
+ for (size_t i = 0; i < BIG_SZ; ++i) {
+ big.get()[i] = c++;
+ }
+ bufferlist bl;
+ bl.append((const char*)big.get(), BIG_SZ);
+ bufferlist::iterator i = bl.begin();
+ bufferlist bl2;
+ i.copy_all(bl2);
+ ASSERT_EQ(bl2.length(), BIG_SZ);
+ std::shared_ptr <unsigned char> big2(
+ (unsigned char*)malloc(BIG_SZ), free);
+ bl2.begin().copy(BIG_SZ, (char*)big2.get());
+ ASSERT_EQ(memcmp(big.get(), big2.get(), BIG_SZ), 0);
+}
+
+TEST(BufferList, InvalidateCrc) {
+ const static size_t buffer_size = 262144;
+ std::shared_ptr <unsigned char> big(
+ (unsigned char*)malloc(buffer_size), free);
+ unsigned char c = 0;
+ char* ptr = (char*) big.get();
+ char* inptr;
+ for (size_t i = 0; i < buffer_size; ++i) {
+ ptr[i] = c++;
+ }
+ bufferlist bl;
+
+ // test for crashes (shouldn't crash)
+ bl.invalidate_crc();
+
+ // put data into bufferlist
+ bl.append((const char*)big.get(), buffer_size);
+
+ // get its crc
+ __u32 crc = bl.crc32c(0);
+
+ // modify data in bl without its knowledge
+ inptr = (char*) bl.c_str();
+ c = 0;
+ for (size_t i = 0; i < buffer_size; ++i) {
+ inptr[i] = c--;
+ }
+
+ // make sure data in bl are now different than in big
+ EXPECT_NE(memcmp((void*) ptr, (void*) inptr, buffer_size), 0);
+
+ // crc should remain the same
+ __u32 new_crc = bl.crc32c(0);
+ EXPECT_EQ(crc, new_crc);
+
+ // force crc invalidate, check if it is updated
+ bl.invalidate_crc();
+ EXPECT_NE(crc, bl.crc32c(0));
+}
+
+TEST(BufferList, TestIsProvidedBuffer) {
+ char buff[100];
+ bufferlist bl;
+ bl.push_back(buffer::create_static(100, buff));
+ ASSERT_TRUE(bl.is_provided_buffer(buff));
+ bl.append_zero(100);
+ ASSERT_FALSE(bl.is_provided_buffer(buff));
+}
+
+TEST(BufferList, DISABLED_DanglingLastP) {
+ bufferlist bl;
+ {
+ // previously we're using the unsharable buffer type to distinguish
+ // the last_p-specific problem from the generic crosstalk issues we
+ // had since the very beginning:
+ // https://gist.github.com/rzarzynski/aed18372e88aed392101adac3bd87bbc
+ // this is no longer possible as `buffer::create_unsharable()` has
+ // been dropped.
+ bufferptr bp(buffer::create(10));
+ bp.copy_in(0, 3, "XXX");
+ bl.push_back(std::move(bp));
+ EXPECT_EQ(0, ::memcmp("XXX", bl.c_str(), 3));
+
+ // let `copy_in` to set `last_p` member of bufferlist
+ bl.begin().copy_in(2, "AB");
+ EXPECT_EQ(0, ::memcmp("ABX", bl.c_str(), 3));
+ }
+
+ bufferlist empty;
+ // before the fix this would have left `last_p` unchanged leading to
+ // the dangerous dangling state – keep in mind that the initial,
+ // unsharable bptr will be freed.
+ bl = const_cast<const bufferlist&>(empty);
+ bl.append("123");
+
+ // we must continue from where the previous copy_in had finished.
+ // Otherwise `bl::copy_in` will call `seek()` and refresh `last_p`.
+ bl.begin(2).copy_in(1, "C");
+ EXPECT_EQ(0, ::memcmp("12C", bl.c_str(), 3));
+}
+
+TEST(BufferHash, all) {
+ {
+ bufferlist bl;
+ bl.append("A");
+ bufferhash hash;
+ EXPECT_EQ((unsigned)0, hash.digest());
+ hash.update(bl);
+ EXPECT_EQ((unsigned)0xB3109EBF, hash.digest());
+ hash.update(bl);
+ EXPECT_EQ((unsigned)0x5FA5C0CC, hash.digest());
+ }
+ {
+ bufferlist bl;
+ bl.append("A");
+ bufferhash hash;
+ EXPECT_EQ((unsigned)0, hash.digest());
+ bufferhash& returned_hash = hash << bl;
+ EXPECT_EQ(&returned_hash, &hash);
+ EXPECT_EQ((unsigned)0xB3109EBF, hash.digest());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd .. ; make unittest_bufferlist &&
+ * ulimit -s unlimited ; valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_bufferlist # --gtest_filter=BufferList.constructors"
+ * End:
+ */
+
diff --git a/src/test/buildtest_skeleton.cc b/src/test/buildtest_skeleton.cc
new file mode 100644
index 000000000..8215a05db
--- /dev/null
+++ b/src/test/buildtest_skeleton.cc
@@ -0,0 +1,13 @@
+#include "common/common_init.h"
+
+/* This program exists to test that we can build libcommon without
+ * referencing g_ceph_context
+ *
+ * This program will go away as soon as we actually don't use g_ceph_context in
+ * more programs. Obviously, at that point, those programs will provide an
+ * equivalent test.
+ */
+int main(int argc, char **argv)
+{
+ return 0;
+}
diff --git a/src/test/centos-8/Dockerfile.in b/src/test/centos-8/Dockerfile.in
new file mode 100644
index 000000000..71f3eddc6
--- /dev/null
+++ b/src/test/centos-8/Dockerfile.in
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2014, 2015 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+# Environment variables are substituted via envsubst(1)
+#
+# user_id=$(id -u)
+# os_version= the desired REPOSITORY TAG
+#
+FROM centos:%%os_version%%
+
+COPY install-deps.sh /root/
+COPY ceph.spec.in /root/
+RUN dnf install -y redhat-lsb-core
+RUN dnf install -y yum-utils && dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8 && dnf update -y && dnf config-manager --enable cr
+# build dependencies
+RUN dnf install -y git sudo
+RUN cd /root ; ./install-deps.sh
+# development tools
+RUN if test %%USER%% != root ; then useradd -M --uid %%user_id%% %%USER%% && echo '%%USER%% ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ; fi
diff --git a/src/test/centos-8/ceph.spec.in b/src/test/centos-8/ceph.spec.in
new file mode 120000
index 000000000..9abcafd12
--- /dev/null
+++ b/src/test/centos-8/ceph.spec.in
@@ -0,0 +1 @@
+../../../ceph.spec.in \ No newline at end of file
diff --git a/src/test/centos-8/install-deps.sh b/src/test/centos-8/install-deps.sh
new file mode 120000
index 000000000..fc9c78b27
--- /dev/null
+++ b/src/test/centos-8/install-deps.sh
@@ -0,0 +1 @@
+../../../install-deps.sh \ No newline at end of file
diff --git a/src/test/ceph-erasure-code-tool/test_ceph-erasure-code-tool.sh b/src/test/ceph-erasure-code-tool/test_ceph-erasure-code-tool.sh
new file mode 100755
index 000000000..442e7ada5
--- /dev/null
+++ b/src/test/ceph-erasure-code-tool/test_ceph-erasure-code-tool.sh
@@ -0,0 +1,43 @@
+#!/bin/sh -ex
+
+TMPDIR=/tmp/test_ceph-erasure-code-tool.$$
+mkdir $TMPDIR
+trap "rm -fr $TMPDIR" 0
+
+ceph-erasure-code-tool test-plugin-exists INVALID_PLUGIN && exit 1
+ceph-erasure-code-tool test-plugin-exists jerasure
+
+ceph-erasure-code-tool validate-profile \
+ plugin=jerasure,technique=reed_sol_van,k=2,m=1
+
+test "$(ceph-erasure-code-tool validate-profile \
+ plugin=jerasure,technique=reed_sol_van,k=2,m=1 chunk_count)" = 3
+
+test "$(ceph-erasure-code-tool calc-chunk-size \
+ plugin=jerasure,technique=reed_sol_van,k=2,m=1 4194304)" = 2097152
+
+dd if="$(which ceph-erasure-code-tool)" of=$TMPDIR/data bs=770808 count=1
+cp $TMPDIR/data $TMPDIR/data.orig
+
+ceph-erasure-code-tool encode \
+ plugin=jerasure,technique=reed_sol_van,k=2,m=1 \
+ 4096 \
+ 0,1,2 \
+ $TMPDIR/data
+test -f $TMPDIR/data.0
+test -f $TMPDIR/data.1
+test -f $TMPDIR/data.2
+
+rm $TMPDIR/data
+
+ceph-erasure-code-tool decode \
+ plugin=jerasure,technique=reed_sol_van,k=2,m=1 \
+ 4096 \
+ 0,2 \
+ $TMPDIR/data
+
+size=$(stat -c '%s' $TMPDIR/data.orig)
+truncate -s "${size}" $TMPDIR/data # remove stripe width padding
+cmp $TMPDIR/data.orig $TMPDIR/data
+
+echo OK
diff --git a/src/test/ceph_argparse.cc b/src/test/ceph_argparse.cc
new file mode 100644
index 000000000..738879c5b
--- /dev/null
+++ b/src/test/ceph_argparse.cc
@@ -0,0 +1,544 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/ceph_argparse.h"
+
+#include "gtest/gtest.h"
+#include <vector>
+#include "include/stringify.h"
+
+using namespace std;
+
+/* Holds a std::vector with C-strings.
+ * Will free() them properly in the destructor.
+ *
+ * Note: the ceph_argparse functions modify the vector, removing elements as
+ * they find them. So we keep a parallel vector, orig, to make sure that we
+ * never forget to delete a string.
+ */
+class VectorContainer
+{
+public:
+ explicit VectorContainer(const char** arr_) {
+ for (const char **a = arr_; *a; ++a) {
+ const char *str = (const char*)strdup(*a);
+ arr.push_back(str);
+ orig.push_back(str);
+ }
+ }
+ ~VectorContainer() {
+ for (std::vector<const char*>::iterator i = orig.begin();
+ i != orig.end(); ++i)
+ {
+ free((void*)*i);
+ }
+ }
+ void refresh() {
+ arr.assign(orig.begin(), orig.end());
+ }
+ std::vector < const char* > arr;
+
+private:
+ std::vector < const char* > orig;
+};
+
+TEST(CephArgParse, SimpleArgParse) {
+ const char *BAR5[] = { "./myprog", "--bar", "5", NULL };
+ const char *FOO[] = { "./myprog", "--foo", "--baz", NULL };
+ const char *NONE[] = { "./myprog", NULL };
+
+ bool found_foo = false;
+ std::string found_bar;
+ VectorContainer bar5(BAR5);
+ for (std::vector<const char*>::iterator i = bar5.arr.begin();
+ i != bar5.arr.end(); )
+ {
+ if (ceph_argparse_flag(bar5.arr, i, "--foo", (char*)NULL)) {
+ found_foo = true;
+ }
+ else if (ceph_argparse_witharg(bar5.arr, i, &found_bar, "--bar", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_foo, false);
+ ASSERT_EQ(found_bar, "5");
+
+ found_foo = false;
+ found_bar = "";
+ bool baz_found = false;
+ std::string found_baz = "";
+ VectorContainer foo(FOO);
+ ostringstream err;
+ for (std::vector<const char*>::iterator i = foo.arr.begin();
+ i != foo.arr.end(); )
+ {
+ if (ceph_argparse_flag(foo.arr, i, "--foo", (char*)NULL)) {
+ found_foo = true;
+ }
+ else if (ceph_argparse_witharg(foo.arr, i, &found_bar, "--bar", (char*)NULL)) {
+ }
+ else if (ceph_argparse_witharg(foo.arr, i, &found_baz, err, "--baz", (char*)NULL)) {
+ ASSERT_NE(string(""), err.str());
+ baz_found = true;
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_foo, true);
+ ASSERT_EQ(found_bar, "");
+ ASSERT_EQ(baz_found, true);
+ ASSERT_EQ(found_baz, "");
+
+ found_foo = false;
+ found_bar = "";
+ VectorContainer none(NONE);
+ for (std::vector<const char*>::iterator i = none.arr.begin();
+ i != none.arr.end(); )
+ {
+ if (ceph_argparse_flag(none.arr, i, "--foo", (char*)NULL)) {
+ found_foo = true;
+ }
+ else if (ceph_argparse_witharg(none.arr, i, &found_bar, "--bar", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_foo, false);
+ ASSERT_EQ(found_bar, "");
+}
+
+TEST(CephArgParse, DoubleDash) {
+ const char *ARGS[] = { "./myprog", "--foo", "5", "--", "--bar", "6", NULL };
+
+ int foo = -1, bar = -1;
+ VectorContainer args(ARGS);
+ for (std::vector<const char*>::iterator i = args.arr.begin();
+ i != args.arr.end(); )
+ {
+ std::string myarg;
+ if (ceph_argparse_double_dash(args.arr, i)) {
+ break;
+ }
+ else if (ceph_argparse_witharg(args.arr, i, &myarg, "--foo", (char*)NULL)) {
+ foo = atoi(myarg.c_str());
+ }
+ else if (ceph_argparse_witharg(args.arr, i, &myarg, "--bar", (char*)NULL)) {
+ bar = atoi(myarg.c_str());
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(foo, 5);
+ ASSERT_EQ(bar, -1);
+}
+
+
+TEST(CephArgParse, WithDashesAndUnderscores) {
+ const char *BAZSTUFF1[] = { "./myprog", "--goo", "--baz-stuff", "50", "--end", NULL };
+ const char *BAZSTUFF2[] = { "./myprog", "--goo2", "--baz_stuff", "50", NULL };
+ const char *BAZSTUFF3[] = { "./myprog", "--goo2", "--baz-stuff=50", "50", NULL };
+ const char *BAZSTUFF4[] = { "./myprog", "--goo2", "--baz_stuff=50", "50", NULL };
+ const char *NONE1[] = { "./myprog", NULL };
+ const char *NONE2[] = { "./myprog", "--goo2", "--baz_stuff2", "50", NULL };
+ const char *NONE3[] = { "./myprog", "--goo2", "__baz_stuff", "50", NULL };
+
+ // as flag
+ std::string found_baz;
+ VectorContainer bazstuff1(BAZSTUFF1);
+ for (std::vector<const char*>::iterator i = bazstuff1.arr.begin();
+ i != bazstuff1.arr.end(); )
+ {
+ if (ceph_argparse_flag(bazstuff1.arr, i, "--baz-stuff", (char*)NULL)) {
+ found_baz = "true";
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "true");
+
+ // as flag
+ found_baz = "";
+ VectorContainer bazstuff2(BAZSTUFF2);
+ for (std::vector<const char*>::iterator i = bazstuff2.arr.begin();
+ i != bazstuff2.arr.end(); )
+ {
+ if (ceph_argparse_flag(bazstuff2.arr, i, "--baz-stuff", (char*)NULL)) {
+ found_baz = "true";
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "true");
+
+ // with argument
+ found_baz = "";
+ bazstuff1.refresh();
+ for (std::vector<const char*>::iterator i = bazstuff1.arr.begin();
+ i != bazstuff1.arr.end(); )
+ {
+ if (ceph_argparse_witharg(bazstuff1.arr, i, &found_baz, "--baz-stuff", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "50");
+
+ // with argument
+ found_baz = "";
+ bazstuff2.refresh();
+ for (std::vector<const char*>::iterator i = bazstuff2.arr.begin();
+ i != bazstuff2.arr.end(); )
+ {
+ if (ceph_argparse_witharg(bazstuff2.arr, i, &found_baz, "--baz-stuff", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "50");
+
+ // with argument
+ found_baz = "";
+ VectorContainer bazstuff3(BAZSTUFF3);
+ for (std::vector<const char*>::iterator i = bazstuff3.arr.begin();
+ i != bazstuff3.arr.end(); )
+ {
+ if (ceph_argparse_witharg(bazstuff3.arr, i, &found_baz, "--baz-stuff", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "50");
+
+ // with argument
+ found_baz = "";
+ VectorContainer bazstuff4(BAZSTUFF4);
+ for (std::vector<const char*>::iterator i = bazstuff4.arr.begin();
+ i != bazstuff4.arr.end(); )
+ {
+ if (ceph_argparse_witharg(bazstuff4.arr, i, &found_baz, "--baz-stuff", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "50");
+
+ // not found
+ found_baz = "";
+ VectorContainer none1(NONE1);
+ for (std::vector<const char*>::iterator i = none1.arr.begin();
+ i != none1.arr.end(); )
+ {
+ if (ceph_argparse_flag(none1.arr, i, "--baz-stuff", (char*)NULL)) {
+ found_baz = "true";
+ }
+ else if (ceph_argparse_witharg(none1.arr, i, &found_baz, "--baz-stuff", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "");
+
+ // not found
+ found_baz = "";
+ VectorContainer none2(NONE2);
+ for (std::vector<const char*>::iterator i = none2.arr.begin();
+ i != none2.arr.end(); )
+ {
+ if (ceph_argparse_flag(none2.arr, i, "--baz-stuff", (char*)NULL)) {
+ found_baz = "true";
+ }
+ else if (ceph_argparse_witharg(none2.arr, i, &found_baz, "--baz-stuff", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "");
+
+ // not found
+ found_baz = "";
+ VectorContainer none3(NONE3);
+ for (std::vector<const char*>::iterator i = none3.arr.begin();
+ i != none3.arr.end(); )
+ {
+ if (ceph_argparse_flag(none3.arr, i, "--baz-stuff", (char*)NULL)) {
+ found_baz = "true";
+ }
+ else if (ceph_argparse_witharg(none3.arr, i, &found_baz, "--baz-stuff", (char*)NULL)) {
+ }
+ else
+ ++i;
+ }
+ ASSERT_EQ(found_baz, "");
+}
+
+TEST(CephArgParse, WithFloat) {
+ const char *BAZSTUFF1[] = { "./myprog", "--foo", "50.5", "--bar", "52", NULL };
+
+ VectorContainer bazstuff1(BAZSTUFF1);
+ ostringstream err;
+ float foo;
+ int bar = -1;
+ for (std::vector<const char*>::iterator i = bazstuff1.arr.begin();
+ i != bazstuff1.arr.end(); )
+ {
+ if (ceph_argparse_double_dash(bazstuff1.arr, i)) {
+ break;
+ } else if (ceph_argparse_witharg(bazstuff1.arr, i, &foo, err, "--foo", (char*)NULL)) {
+ ASSERT_EQ(string(""), err.str());
+ } else if (ceph_argparse_witharg(bazstuff1.arr, i, &bar, err, "--bar", (char*)NULL)) {
+ ASSERT_EQ(string(""), err.str());
+ }
+ else {
+ ++i;
+ }
+ }
+ ASSERT_EQ(foo, 50.5);
+ ASSERT_EQ(bar, 52);
+}
+
+TEST(CephArgParse, WithInt) {
+ const char *BAZSTUFF1[] = { "./myprog", "--foo", "50", "--bar", "52", NULL };
+ const char *BAZSTUFF2[] = { "./myprog", "--foo", "--bar", "52", NULL };
+ const char *BAZSTUFF3[] = { "./myprog", "--foo", "40", "--", "--bar", "42", NULL };
+
+ // normal test
+ VectorContainer bazstuff1(BAZSTUFF1);
+ ostringstream err;
+ int foo = -1, bar = -1;
+ for (std::vector<const char*>::iterator i = bazstuff1.arr.begin();
+ i != bazstuff1.arr.end(); )
+ {
+ if (ceph_argparse_double_dash(bazstuff1.arr, i)) {
+ break;
+ } else if (ceph_argparse_witharg(bazstuff1.arr, i, &foo, err, "--foo", (char*)NULL)) {
+ ASSERT_EQ(string(""), err.str());
+ } else if (ceph_argparse_witharg(bazstuff1.arr, i, &bar, err, "--bar", (char*)NULL)) {
+ ASSERT_EQ(string(""), err.str());
+ }
+ else {
+ ++i;
+ }
+ }
+ ASSERT_EQ(foo, 50);
+ ASSERT_EQ(bar, 52);
+
+ // parse error test
+ VectorContainer bazstuff2(BAZSTUFF2);
+ ostringstream err2;
+ for (std::vector<const char*>::iterator i = bazstuff2.arr.begin();
+ i != bazstuff2.arr.end(); )
+ {
+ if (ceph_argparse_double_dash(bazstuff2.arr, i)) {
+ break;
+ } else if (ceph_argparse_witharg(bazstuff2.arr, i, &foo, err2, "--foo", (char*)NULL)) {
+ ASSERT_NE(string(""), err2.str());
+ }
+ else {
+ ++i;
+ }
+ }
+
+ // double dash test
+ VectorContainer bazstuff3(BAZSTUFF3);
+ foo = -1, bar = -1;
+ for (std::vector<const char*>::iterator i = bazstuff3.arr.begin();
+ i != bazstuff3.arr.end(); )
+ {
+ if (ceph_argparse_double_dash(bazstuff3.arr, i)) {
+ break;
+ } else if (ceph_argparse_witharg(bazstuff3.arr, i, &foo, err, "--foo", (char*)NULL)) {
+ ASSERT_EQ(string(""), err.str());
+ } else if (ceph_argparse_witharg(bazstuff3.arr, i, &bar, err, "--bar", (char*)NULL)) {
+ ASSERT_EQ(string(""), err.str());
+ }
+ else {
+ ++i;
+ }
+ }
+ ASSERT_EQ(foo, 40);
+ ASSERT_EQ(bar, -1);
+}
+
+TEST(CephArgParse, env_to_vec) {
+ {
+ std::vector<const char*> args;
+ unsetenv("CEPH_ARGS");
+ unsetenv("WHATEVER");
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(0u, args.size());
+ clear_g_str_vec();
+ env_to_vec(args, "WHATEVER");
+ EXPECT_EQ(0u, args.size());
+ args.push_back("a");
+ setenv("CEPH_ARGS", "b c", 0);
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(3u, args.size());
+ EXPECT_EQ(string("b"), args[0]);
+ EXPECT_EQ(string("c"), args[1]);
+ EXPECT_EQ(string("a"), args[2]);
+ setenv("WHATEVER", "d e", 0);
+ clear_g_str_vec();
+ env_to_vec(args, "WHATEVER");
+ EXPECT_EQ(5u, args.size());
+ EXPECT_EQ(string("d"), args[0]);
+ EXPECT_EQ(string("e"), args[1]);
+ }
+ {
+ std::vector<const char*> args;
+ unsetenv("CEPH_ARGS");
+ args.push_back("a");
+ args.push_back("--");
+ args.push_back("c");
+ setenv("CEPH_ARGS", "b -- d", 0);
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(5u, args.size());
+ EXPECT_EQ(string("b"), args[0]);
+ EXPECT_EQ(string("a"), args[1]);
+ EXPECT_EQ(string("--"), args[2]);
+ EXPECT_EQ(string("d"), args[3]);
+ EXPECT_EQ(string("c"), args[4]);
+ }
+ {
+ std::vector<const char*> args;
+ unsetenv("CEPH_ARGS");
+ args.push_back("a");
+ args.push_back("--");
+ setenv("CEPH_ARGS", "b -- c", 0);
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(4u, args.size());
+ EXPECT_EQ(string("b"), args[0]);
+ EXPECT_EQ(string("a"), args[1]);
+ EXPECT_EQ(string("--"), args[2]);
+ EXPECT_EQ(string("c"), args[3]);
+ }
+ {
+ std::vector<const char*> args;
+ unsetenv("CEPH_ARGS");
+ args.push_back("--");
+ args.push_back("c");
+ setenv("CEPH_ARGS", "b -- d", 0);
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(4u, args.size());
+ EXPECT_EQ(string("b"), args[0]);
+ EXPECT_EQ(string("--"), args[1]);
+ EXPECT_EQ(string("d"), args[2]);
+ EXPECT_EQ(string("c"), args[3]);
+ }
+ {
+ std::vector<const char*> args;
+ unsetenv("CEPH_ARGS");
+ args.push_back("b");
+ setenv("CEPH_ARGS", "c -- d", 0);
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(4u, args.size());
+ EXPECT_EQ(string("c"), args[0]);
+ EXPECT_EQ(string("b"), args[1]);
+ EXPECT_EQ(string("--"), args[2]);
+ EXPECT_EQ(string("d"), args[3]);
+ }
+ {
+ std::vector<const char*> args;
+ unsetenv("CEPH_ARGS");
+ args.push_back("a");
+ args.push_back("--");
+ args.push_back("c");
+ setenv("CEPH_ARGS", "-- d", 0);
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(4u, args.size());
+ EXPECT_EQ(string("a"), args[0]);
+ EXPECT_EQ(string("--"), args[1]);
+ EXPECT_EQ(string("d"), args[2]);
+ EXPECT_EQ(string("c"), args[3]);
+ }
+ {
+ std::vector<const char*> args;
+ unsetenv("CEPH_ARGS");
+ args.push_back("a");
+ args.push_back("--");
+ args.push_back("c");
+ setenv("CEPH_ARGS", "d", 0);
+ clear_g_str_vec();
+ env_to_vec(args);
+ EXPECT_EQ(4u, args.size());
+ EXPECT_EQ(string("d"), args[0]);
+ EXPECT_EQ(string("a"), args[1]);
+ EXPECT_EQ(string("--"), args[2]);
+ EXPECT_EQ(string("c"), args[3]);
+ }
+}
+
+TEST(CephArgParse, parse_ip_port_vec) {
+ struct {
+ const char *from;
+ int type;
+ const char *to;
+ } tests[] = {
+ { "1.2.3.4", entity_addr_t::TYPE_MSGR2,
+ "v2:1.2.3.4:0/0\n" },
+ { "v1:1.2.3.4", entity_addr_t::TYPE_MSGR2,
+ "v1:1.2.3.4:0/0\n" },
+ { "1.2.3.4", entity_addr_t::TYPE_LEGACY,
+ "v1:1.2.3.4:0/0\n" },
+ { "[::],1.2.3.4", entity_addr_t::TYPE_LEGACY,
+ "v1:[::]:0/0\nv1:1.2.3.4:0/0\n" },
+ { "v2:1.2.3.4:111,v1:5.6.7.8:222", entity_addr_t::TYPE_LEGACY,
+ "v2:1.2.3.4:111/0\nv1:5.6.7.8:222/0\n" },
+ { "v2:1.2.3.4:111 v1:5.6.7.8:222", entity_addr_t::TYPE_LEGACY,
+ "v2:1.2.3.4:111/0\nv1:5.6.7.8:222/0\n" },
+ { "[v2:1.2.3.4:111,v1:5.6.7.8:222] [v2:[::]:3300,v1:[::]:6789]",
+ entity_addr_t::TYPE_LEGACY,
+ "[v2:1.2.3.4:111/0,v1:5.6.7.8:222/0]\n[v2:[::]:3300/0,v1:[::]:6789/0]\n" },
+ { "[v2:1.2.3.4:111,v1:5.6.7.8:222],[v2:[::]:3300,v1:[::]:6789]",
+ entity_addr_t::TYPE_LEGACY,
+ "[v2:1.2.3.4:111/0,v1:5.6.7.8:222/0]\n[v2:[::]:3300/0,v1:[::]:6789/0]\n" },
+ { 0, 0, 0 },
+ };
+
+ for (unsigned i = 0; tests[i].from; ++i) {
+ vector<entity_addrvec_t> v;
+ cout << "-- " << tests[i].from << " type " << tests[i].type
+ << " ->\n" << tests[i].to;
+ ASSERT_TRUE(parse_ip_port_vec(tests[i].from, v, tests[i].type));
+ string actual;
+ for (auto s : v) {
+ actual += stringify(s) + "\n";
+ }
+ ASSERT_EQ(actual, tests[i].to);
+ }
+
+ const char *bad[] = {
+ "1.2.3.4 foo",
+ 0
+ };
+ for (unsigned i = 0; bad[i]; ++i) {
+ vector<entity_addrvec_t> v;
+ cout << "bad " << bad[i] << std::endl;
+ ASSERT_FALSE(parse_ip_port_vec(bad[i], v));
+ }
+}
+
+
+/*
+ * Local Variables:
+ * compile-command: "cd .. ; make unittest_ceph_argparse && ./unittest_ceph_argparse"
+ * End:
+ */
diff --git a/src/test/ceph_compatset.cc b/src/test/ceph_compatset.cc
new file mode 100644
index 000000000..28561bf2b
--- /dev/null
+++ b/src/test/ceph_compatset.cc
@@ -0,0 +1,167 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <fstream>
+#include <iostream>
+#include <errno.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <ctype.h>
+#include <boost/scoped_ptr.hpp>
+#include <string>
+
+#include "include/types.h"
+#include "include/compat.h"
+#include "include/coredumpctl.h"
+
+#include "include/CompatSet.h"
+
+#include "gtest/gtest.h"
+#include <vector>
+
+using namespace std;
+
+TEST(CephCompatSet, AllSet) {
+ CompatSet::FeatureSet compat;
+ CompatSet::FeatureSet ro;
+ CompatSet::FeatureSet incompat;
+
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(compat.insert(CompatSet::Feature(0, "test")), "");
+ EXPECT_DEATH(compat.insert(CompatSet::Feature(64, "test")), "");
+ }
+
+ for (int i = 1; i < 64; i++) {
+ stringstream cname;
+ cname << string("c") << i;
+ compat.insert(CompatSet::Feature(i,cname.str().c_str()));
+ stringstream roname;
+ roname << string("r") << i;
+ ro.insert(CompatSet::Feature(i,roname.str().c_str()));
+ stringstream iname;
+ iname << string("i") << i;
+ incompat.insert(CompatSet::Feature(i,iname.str().c_str()));
+ }
+ CompatSet tcs(compat, ro, incompat);
+
+ //cout << tcs << std::endl;
+
+ //Due to a workaround for a bug bit 0 is always set even though it is
+ //not a legal feature.
+ EXPECT_EQ(tcs.compat.mask, (uint64_t)0xffffffffffffffff);
+ EXPECT_EQ(tcs.ro_compat.mask, (uint64_t)0xffffffffffffffff);
+ EXPECT_EQ(tcs.incompat.mask, (uint64_t)0xffffffffffffffff);
+
+ for (int i = 1; i < 64; i++) {
+ EXPECT_TRUE(tcs.compat.contains(i));
+ stringstream cname;
+ cname << string("c") << i;
+ EXPECT_TRUE(tcs.compat.contains(CompatSet::Feature(i,cname.str().c_str())));
+ tcs.compat.remove(i);
+
+ EXPECT_TRUE(tcs.ro_compat.contains(i));
+ stringstream roname;
+ roname << string("r") << i;
+ EXPECT_TRUE(tcs.ro_compat.contains(CompatSet::Feature(i,roname.str().c_str())));
+ tcs.ro_compat.remove(i);
+
+ EXPECT_TRUE(tcs.incompat.contains(i));
+ stringstream iname;
+ iname << string("i") << i;
+ EXPECT_TRUE(tcs.incompat.contains(CompatSet::Feature(i,iname.str().c_str())));
+ tcs.incompat.remove(i);
+ }
+ //Due to a workaround for a bug bit 0 is always set even though it is
+ //not a legal feature.
+ EXPECT_EQ(tcs.compat.mask, (uint64_t)1);
+ EXPECT_TRUE(tcs.compat.names.empty());
+ EXPECT_EQ(tcs.ro_compat.mask, (uint64_t)1);
+ EXPECT_TRUE(tcs.ro_compat.names.empty());
+ EXPECT_EQ(tcs.incompat.mask, (uint64_t)1);
+ EXPECT_TRUE(tcs.incompat.names.empty());
+}
+
+TEST(CephCompatSet, other) {
+ CompatSet s1, s2, s1dup;
+
+ s1.compat.insert(CompatSet::Feature(1, "c1"));
+ s1.compat.insert(CompatSet::Feature(2, "c2"));
+ s1.compat.insert(CompatSet::Feature(32, "c32"));
+ s1.ro_compat.insert(CompatSet::Feature(63, "r63"));
+ s1.incompat.insert(CompatSet::Feature(1, "i1"));
+
+ s2.compat.insert(CompatSet::Feature(1, "c1"));
+ s2.compat.insert(CompatSet::Feature(32, "c32"));
+ s2.ro_compat.insert(CompatSet::Feature(63, "r63"));
+ s2.incompat.insert(CompatSet::Feature(1, "i1"));
+
+ s1dup = s1;
+
+ //Check exact match
+ EXPECT_EQ(s1.compare(s1dup), 0);
+
+ //Check superset
+ EXPECT_EQ(s1.compare(s2), 1);
+
+ //Check missing features
+ EXPECT_EQ(s2.compare(s1), -1);
+
+ CompatSet diff = s2.unsupported(s1);
+ EXPECT_EQ(diff.compat.mask, (uint64_t)1<<2 | 1);
+ EXPECT_EQ(diff.ro_compat.mask, (uint64_t)1);
+ EXPECT_EQ(diff.incompat.mask, (uint64_t)1);
+
+ CompatSet s3 = s1;
+ s3.incompat.insert(CompatSet::Feature(4, "i4"));
+
+ diff = s1.unsupported(s3);
+ EXPECT_EQ(diff.compat.mask, (uint64_t)1);
+ EXPECT_EQ(diff.ro_compat.mask, (uint64_t)1);
+ EXPECT_EQ(diff.incompat.mask, (uint64_t)1<<4 | 1);
+}
+
+TEST(CephCompatSet, merge) {
+ CompatSet s1, s2, s1dup, s2dup;
+
+ s1.compat.insert(CompatSet::Feature(1, "c1"));
+ s1.compat.insert(CompatSet::Feature(2, "c2"));
+ s1.compat.insert(CompatSet::Feature(32, "c32"));
+ s1.ro_compat.insert(CompatSet::Feature(63, "r63"));
+ s1.incompat.insert(CompatSet::Feature(1, "i1"));
+
+ s1dup = s1;
+
+ s2.compat.insert(CompatSet::Feature(1, "c1"));
+ s2.compat.insert(CompatSet::Feature(32, "c32"));
+ s2.ro_compat.insert(CompatSet::Feature(1, "r1"));
+ s2.ro_compat.insert(CompatSet::Feature(63, "r63"));
+ s2.incompat.insert(CompatSet::Feature(1, "i1"));
+
+ s2dup = s2;
+
+ //Nothing to merge if they are the same
+ EXPECT_FALSE(s1.merge(s1dup));
+ EXPECT_FALSE(s2.merge(s2dup));
+
+ EXPECT_TRUE(s1.merge(s2));
+ EXPECT_EQ(s1.compat.mask, (uint64_t)1<<1 | (uint64_t)1<<2 | (uint64_t)1<<32 | 1);
+ EXPECT_EQ(s1.ro_compat.mask, (uint64_t)1<<1 | (uint64_t)1<<63 | 1);
+ EXPECT_EQ(s1.incompat.mask, (uint64_t)1<<1 | 1);
+
+ EXPECT_TRUE(s2.merge(s1dup));
+ EXPECT_EQ(s2.compat.mask, (uint64_t)1<<1 | (uint64_t)1<<2 | (uint64_t)1<<32 | 1);
+ EXPECT_EQ(s2.ro_compat.mask, (uint64_t)1<<1 | (uint64_t)1<<63 | 1);
+ EXPECT_EQ(s2.incompat.mask, (uint64_t)1<<1 | 1);
+}
diff --git a/src/test/ceph_crypto.cc b/src/test/ceph_crypto.cc
new file mode 100644
index 000000000..477d0c0db
--- /dev/null
+++ b/src/test/ceph_crypto.cc
@@ -0,0 +1,286 @@
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_crypto.h"
+#include "common/common_init.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+
+class CryptoEnvironment: public ::testing::Environment {
+public:
+ void SetUp() override {
+ ceph::crypto::init();
+ }
+};
+
+TEST(MD5, Simple) {
+ ceph::crypto::MD5 h;
+ h.Update((const unsigned char*)"foo", 3);
+ unsigned char digest[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ h.Final(digest);
+ int err;
+ unsigned char want_digest[CEPH_CRYPTO_MD5_DIGESTSIZE] = {
+ 0xac, 0xbd, 0x18, 0xdb, 0x4c, 0xc2, 0xf8, 0x5c,
+ 0xed, 0xef, 0x65, 0x4f, 0xcc, 0xc4, 0xa4, 0xd8,
+ };
+ err = memcmp(digest, want_digest, CEPH_CRYPTO_MD5_DIGESTSIZE);
+ ASSERT_EQ(0, err);
+}
+
+TEST(MD5, MultiUpdate) {
+ ceph::crypto::MD5 h;
+ h.Update((const unsigned char*)"", 0);
+ h.Update((const unsigned char*)"fo", 2);
+ h.Update((const unsigned char*)"", 0);
+ h.Update((const unsigned char*)"o", 1);
+ h.Update((const unsigned char*)"", 0);
+ unsigned char digest[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ h.Final(digest);
+ int err;
+ unsigned char want_digest[CEPH_CRYPTO_MD5_DIGESTSIZE] = {
+ 0xac, 0xbd, 0x18, 0xdb, 0x4c, 0xc2, 0xf8, 0x5c,
+ 0xed, 0xef, 0x65, 0x4f, 0xcc, 0xc4, 0xa4, 0xd8,
+ };
+ err = memcmp(digest, want_digest, CEPH_CRYPTO_MD5_DIGESTSIZE);
+ ASSERT_EQ(0, err);
+}
+
+TEST(MD5, Restart) {
+ ceph::crypto::MD5 h;
+ h.Update((const unsigned char*)"bar", 3);
+ h.Restart();
+ h.Update((const unsigned char*)"foo", 3);
+ unsigned char digest[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ h.Final(digest);
+ int err;
+ unsigned char want_digest[CEPH_CRYPTO_MD5_DIGESTSIZE] = {
+ 0xac, 0xbd, 0x18, 0xdb, 0x4c, 0xc2, 0xf8, 0x5c,
+ 0xed, 0xef, 0x65, 0x4f, 0xcc, 0xc4, 0xa4, 0xd8,
+ };
+ err = memcmp(digest, want_digest, CEPH_CRYPTO_MD5_DIGESTSIZE);
+ ASSERT_EQ(0, err);
+}
+
+TEST(HMACSHA1, Simple) {
+ ceph::crypto::HMACSHA1 h((const unsigned char*)"sekrit", 6);
+ h.Update((const unsigned char*)"foo", 3);
+ unsigned char digest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
+ h.Final(digest);
+ int err;
+ unsigned char want_digest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE] = {
+ 0x04, 0xbc, 0x52, 0x66, 0xb6, 0xff, 0xad, 0xad, 0x9d, 0x57,
+ 0xce, 0x13, 0xea, 0x8c, 0xf5, 0x6b, 0xf9, 0x95, 0x2f, 0xd6,
+ };
+ err = memcmp(digest, want_digest, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+ ASSERT_EQ(0, err);
+}
+
+TEST(HMACSHA1, MultiUpdate) {
+ ceph::crypto::HMACSHA1 h((const unsigned char*)"sekrit", 6);
+ h.Update((const unsigned char*)"", 0);
+ h.Update((const unsigned char*)"fo", 2);
+ h.Update((const unsigned char*)"", 0);
+ h.Update((const unsigned char*)"o", 1);
+ h.Update((const unsigned char*)"", 0);
+ unsigned char digest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
+ h.Final(digest);
+ int err;
+ unsigned char want_digest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE] = {
+ 0x04, 0xbc, 0x52, 0x66, 0xb6, 0xff, 0xad, 0xad, 0x9d, 0x57,
+ 0xce, 0x13, 0xea, 0x8c, 0xf5, 0x6b, 0xf9, 0x95, 0x2f, 0xd6,
+ };
+ err = memcmp(digest, want_digest, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+ ASSERT_EQ(0, err);
+}
+
+TEST(HMACSHA1, Restart) {
+ ceph::crypto::HMACSHA1 h((const unsigned char*)"sekrit", 6);
+ h.Update((const unsigned char*)"bar", 3);
+ h.Restart();
+ h.Update((const unsigned char*)"foo", 3);
+ unsigned char digest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
+ h.Final(digest);
+ int err;
+ unsigned char want_digest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE] = {
+ 0x04, 0xbc, 0x52, 0x66, 0xb6, 0xff, 0xad, 0xad, 0x9d, 0x57,
+ 0xce, 0x13, 0xea, 0x8c, 0xf5, 0x6b, 0xf9, 0x95, 0x2f, 0xd6,
+ };
+ err = memcmp(digest, want_digest, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+ ASSERT_EQ(0, err);
+}
+
+TEST(Digest, SHA1) {
+ auto digest = [](const bufferlist& bl) {
+ return ceph::crypto::digest<ceph::crypto::SHA1>(bl);
+ };
+ {
+ bufferlist bl;
+ sha1_digest_t sha1 = digest(bl);
+ EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709", sha1.to_str());
+ }
+ {
+ bufferlist bl;
+ bl.append("");
+ sha1_digest_t sha1 = digest(bl);
+ EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709", sha1.to_str());
+ }
+ {
+ bufferlist bl;
+ bl.append("Hello");
+ sha1_digest_t sha1 = digest(bl);
+ EXPECT_EQ("f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0", sha1.to_str());
+ }
+ {
+ bufferlist bl, bl2;
+ bl.append("Hello");
+ bl2.append(", world!");
+ bl.claim_append(bl2);
+ sha1_digest_t sha1 = digest(bl);
+ EXPECT_EQ("943a702d06f34599aee1f8da8ef9f7296031d699", sha1.to_str());
+ bl2.append(" How are you today?");
+ bl.claim_append(bl2);
+ sha1 = digest(bl);
+ EXPECT_EQ("778b5d10e5133aa28fb8de71d35b6999b9a25eb4", sha1.to_str());
+ }
+ {
+ bufferptr p(65536);
+ memset(p.c_str(), 0, 65536);
+ bufferlist bl;
+ bl.append(p);
+ sha1_digest_t sha1 = digest(bl);
+ EXPECT_EQ("1adc95bebe9eea8c112d40cd04ab7a8d75c4f961", sha1.to_str());
+ }
+}
+
+TEST(Digest, SHA256) {
+ auto digest = [](const bufferlist& bl) {
+ return ceph::crypto::digest<ceph::crypto::SHA256>(bl);
+ };
+ {
+ bufferlist bl;
+ sha256_digest_t sha256 = digest(bl);
+ EXPECT_EQ("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", sha256.to_str());
+ }
+ {
+ bufferlist bl;
+ bl.append("");
+ sha256_digest_t sha256 = digest(bl);
+ EXPECT_EQ("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", sha256.to_str());
+ }
+ {
+ bufferlist bl;
+ bl.append("Hello");
+ sha256_digest_t sha256 = digest(bl);
+ EXPECT_EQ("185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969", sha256.to_str());
+ }
+ {
+ bufferlist bl, bl2;
+ bl.append("Hello");
+ bl2.append(", world!");
+ bl.claim_append(bl2);
+ sha256_digest_t sha256 = digest(bl);
+ EXPECT_EQ("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", sha256.to_str());
+ bl2.append(" How are you today?");
+ bl.claim_append(bl2);
+ sha256 = digest(bl);
+ EXPECT_EQ("e85f57f8bb018bd4f7beed6f27488cef22b13d5e06e8b8a27cac8b087c2a549e", sha256.to_str());
+ }
+ {
+ bufferptr p(65536);
+ memset(p.c_str(), 0, 65536);
+ bufferlist bl;
+ bl.append(p);
+ sha256_digest_t sha256 = digest(bl);
+ EXPECT_EQ("de2f256064a0af797747c2b97505dc0b9f3df0de4f489eac731c23ae9ca9cc31", sha256.to_str());
+ }
+}
+
+TEST(Digest, SHA512) {
+ auto digest = [](const bufferlist& bl) {
+ return ceph::crypto::digest<ceph::crypto::SHA512>(bl);
+ };
+ {
+ bufferlist bl;
+ sha512_digest_t sha512 = digest(bl);
+ EXPECT_EQ("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", sha512.to_str());
+ }
+ {
+ bufferlist bl;
+ bl.append("");
+ sha512_digest_t sha512 = digest(bl);
+ EXPECT_EQ("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", sha512.to_str());
+ }
+ {
+ bufferlist bl;
+ bl.append("Hello");
+ sha512_digest_t sha512 = digest(bl);
+ EXPECT_EQ("3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315", sha512.to_str());
+ }
+ {
+ bufferlist bl, bl2;
+ bl.append("Hello");
+ bl2.append(", world!");
+ bl.claim_append(bl2);
+ sha512_digest_t sha512 = digest(bl);
+ EXPECT_EQ("c1527cd893c124773d811911970c8fe6e857d6df5dc9226bd8a160614c0cd963a4ddea2b94bb7d36021ef9d865d5cea294a82dd49a0bb269f51f6e7a57f79421", sha512.to_str());
+ bl2.append(" How are you today?");
+ bl.claim_append(bl2);
+ sha512 = digest(bl);
+ EXPECT_EQ("7d50e299496754f9a0d158e018d4b733f2ef51c487b43b50719ffdabe3c3da5a347029741056887b4ffa2ddd0aa9e0dd358b8ed9da9a4f3455f44896fc8e5395", sha512.to_str());
+ }
+ {
+ bufferptr p(65536);
+ memset(p.c_str(), 0, 65536);
+ bufferlist bl;
+ bl.append(p);
+ sha512_digest_t sha512 = digest(bl);
+ EXPECT_EQ("73e4153936dab198397b74ee9efc26093dda721eaab2f8d92786891153b45b04265a161b169c988edb0db2c53124607b6eaaa816559c5ce54f3dbc9fa6a7a4b2", sha512.to_str());
+ }
+}
+
+class ForkDeathTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // shutdown NSS so it can be reinitialized after the fork
+ // some data structures used by NSPR are only initialized once, and they
+ // will be cleaned up with ceph::crypto::shutdown(false), so we need to
+ // keep them around after fork.
+ ceph::crypto::shutdown(true);
+ }
+
+ void TearDown() override {
+ // undo the NSS shutdown we did in the parent process, after the
+ // test is done
+ ceph::crypto::init();
+ }
+};
+
+void do_simple_crypto() {
+ // ensure that the shutdown/fork/init sequence results in a working
+ // NSS crypto library; this function is run in the child, after the
+ // fork, and if you comment out the ceph::crypto::init, or if the
+ // trick were to fail, you would see this ending in an assert and
+ // not exit status 0
+ ceph::crypto::init();
+ ceph::crypto::MD5 h;
+ h.Update((const unsigned char*)"foo", 3);
+ unsigned char digest[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ h.Final(digest);
+ exit(0);
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(_WIN32)
+TEST_F(ForkDeathTest, MD5) {
+ ASSERT_EXIT(do_simple_crypto(), ::testing::ExitedWithCode(0), "^$");
+}
+#endif // GTEST_HAS_DEATH_TEST && !defined(_WIN32)
+
+int main(int argc, char **argv) {
+ std::vector<const char*> args(argv, argv + argc);
+ auto cct = global_init(NULL, args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/cli-integration/balancer/misplaced.t b/src/test/cli-integration/balancer/misplaced.t
new file mode 100644
index 000000000..050cceb64
--- /dev/null
+++ b/src/test/cli-integration/balancer/misplaced.t
@@ -0,0 +1,30 @@
+ $ ceph balancer off
+ $ ceph balancer mode none
+ $ ceph osd pool create balancer_opt 128
+ pool 'balancer_opt' created
+ $ ceph osd pool application enable balancer_opt rados
+ enabled application 'rados' on pool 'balancer_opt'
+ $ rados bench -p balancer_opt 50 write --no-cleanup > /dev/null
+ $ ceph balancer on
+ $ ceph balancer mode crush-compat
+ $ ceph balancer ls
+ []
+ $ ceph config set osd.* target_max_misplaced_ratio .07
+ $ ceph balancer eval
+ current cluster score [0-9]*\.?[0-9]+.* (re)
+# Turn off active balancer to use manual commands
+ $ ceph balancer off
+ $ ceph balancer optimize test_plan balancer_opt
+ $ ceph balancer ls
+ [
+ "test_plan"
+ ]
+ $ ceph balancer execute test_plan
+ $ ceph balancer eval
+ current cluster score [0-9]*\.?[0-9]+.* (re)
+# Plan is gone after execution ?
+ $ ceph balancer execute test_plan
+ Error ENOENT: plan test_plan not found
+ [2]
+ $ ceph osd pool rm balancer_opt balancer_opt --yes-i-really-really-mean-it
+ pool 'balancer_opt' removed
diff --git a/src/test/cli-integration/rbd/defaults.t b/src/test/cli-integration/rbd/defaults.t
new file mode 100644
index 000000000..fe69997ce
--- /dev/null
+++ b/src/test/cli-integration/rbd/defaults.t
@@ -0,0 +1,310 @@
+Plain create with various options specified via usual cli arguments
+===================================================================
+ $ rbd create -s 1 test
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rb.0.*", (glob)
+ "format": 1,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 --object-size 1M test
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rb.0.*", (glob)
+ "format": 1,
+ "name": "test",
+ "object_size": 1048576,
+ "objects": 1,
+ "order": 20,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1G --object-size 4K test
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rb.0.*", (glob)
+ "format": 1,
+ "name": "test",
+ "object_size": 4096,
+ "objects": 262144,
+ "order": 12,
+ "size": 1073741824
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --image-format 2
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1G test --image-format 2
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 256,
+ "order": 22,
+ "size": 1073741824
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --image-format 2 --object-size 1M
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 1048576,
+ "objects": 1,
+ "order": 20,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --image-format 2 --stripe-unit 1048576 --stripe-count 8
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576,
+ "stripe_count": 8,
+ "stripe_unit": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --image-format 2 --stripe-unit 1048576B --stripe-count 8
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576,
+ "stripe_count": 8,
+ "stripe_unit": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1G test --image-format 2 --stripe-unit 4K --stripe-count 8
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 256,
+ "order": 22,
+ "size": 1073741824,
+ "stripe_count": 8,
+ "stripe_unit": 4096
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1G test --image-format 2 --stripe-unit 1M --stripe-count 8
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 256,
+ "order": 22,
+ "size": 1073741824,
+ "stripe_count": 8,
+ "stripe_unit": 1048576
+ }
+ $ rbd rm test --no-progress
+
+Format 2 Usual arguments with custom rbd_default_* params
+=========================================================
+ $ rbd create -s 1 test --image-format 2 --stripe-unit 1048576 --stripe-count 8 --rbd-default-order 21
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 2097152,
+ "objects": 1,
+ "order": 21,
+ "size": 1048576,
+ "stripe_count": 8,
+ "stripe_unit": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --image-format 2 --stripe-unit 1048576 --stripe-count 8 --object-size 8M --rbd-default-order 20
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 8388608,
+ "objects": 1,
+ "order": 23,
+ "size": 1048576,
+ "stripe_count": 8,
+ "stripe_unit": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --image-format 2 --rbd-default-stripe-unit 1048576 --rbd-default-stripe-count 8
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576,
+ "stripe_count": 8,
+ "stripe_unit": 1048576
+ }
+ $ rbd rm test --no-progress
+
+Format 1 Usual arguments with custom rbd_default_* params
+=========================================================
+ $ rbd create -s 1 test --rbd-default-order 20
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rb.0.*", (glob)
+ "format": 1,
+ "name": "test",
+ "object_size": 1048576,
+ "objects": 1,
+ "order": 20,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --rbd-default-format 2
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --rbd-default-format 2 --rbd-default-order 20
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 1048576,
+ "objects": 1,
+ "order": 20,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --rbd-default-format 2 --rbd-default-order 20 --rbd-default-features 1
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 1048576,
+ "objects": 1,
+ "order": 20,
+ "size": 1048576
+ }
+ $ rbd rm test --no-progress
+ $ rbd create -s 1 test --rbd-default-format 2 --stripe-unit 1048576 --stripe-count 8
+ $ rbd info test --format json | python3 -mjson.tool | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rbd_data.*", (glob)
+ "features": [
+ "layering",
+ "striping",
+ "exclusive"
+ ],
+ "format": 2,
+ "name": "test",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576,
+ "stripe_count": 8,
+ "stripe_unit": 1048576
+ }
+ $ rbd rm test --no-progress
diff --git a/src/test/cli-integration/rbd/formatted-output.t b/src/test/cli-integration/rbd/formatted-output.t
new file mode 100644
index 000000000..778cc2198
--- /dev/null
+++ b/src/test/cli-integration/rbd/formatted-output.t
@@ -0,0 +1,1171 @@
+ls on empty pool never containing images
+========================================
+ $ ceph osd pool create rbd_other
+ pool 'rbd_other' created
+ $ rbd pool init rbd_other
+ $ rados -p rbd rm rbd_directory >/dev/null 2>&1 || true
+ $ rbd ls
+ $ rbd ls --format json
+ []
+ $ rbd ls --format xml
+ <images></images>
+
+create
+=======
+ $ RBD_FORCE_ALLOW_V1=1 rbd create -s 1024 --image-format 1 foo --log-to-stderr=false
+ rbd: image format 1 is deprecated
+ $ rbd create -s 512 --image-format 2 bar
+ $ rbd create -s 2048 --image-format 2 --image-feature layering baz
+ $ RBD_FORCE_ALLOW_V1=1 rbd create -s 1 --image-format 1 quux --log-to-stderr=false
+ rbd: image format 1 is deprecated
+ $ rbd create -s 1G --image-format 2 quuy
+
+snapshot
+========
+ $ rbd snap create bar@snap --no-progress
+ $ rbd resize -s 1024 --no-progress bar
+ $ rbd resize -s 2G --no-progress quuy
+ $ rbd snap create bar@snap2 --no-progress
+ $ rbd snap create foo@snap --no-progress
+
+clone
+=====
+ $ rbd snap protect bar@snap
+ $ rbd clone --image-feature layering,exclusive-lock,object-map,fast-diff bar@snap rbd_other/child
+ $ rbd snap create rbd_other/child@snap --no-progress
+ $ rbd flatten rbd_other/child 2> /dev/null
+ $ rbd bench rbd_other/child --io-type write --io-pattern seq --io-total 1B > /dev/null 2>&1
+ $ rbd clone bar@snap rbd_other/deep-flatten-child
+ $ rbd snap create rbd_other/deep-flatten-child@snap --no-progress
+ $ rbd flatten rbd_other/deep-flatten-child 2> /dev/null
+
+lock
+====
+ $ rbd lock add quux id
+ $ rbd lock add baz id1 --shared tag
+ $ rbd lock add baz id2 --shared tag
+ $ rbd lock add baz id3 --shared tag
+
+test formatting
+===============
+ $ rbd children foo@snap
+ $ rbd children bar@snap
+ rbd_other/child
+ $ rbd children bar@snap2
+TODO: figure out why .* does not match the block_name_prefix line in rbd info.
+For now, use a more inclusive regex.
+ $ rbd info foo
+ rbd image 'foo':
+ \tsize 1 GiB in 256 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 1 (esc)
+ [^^]+ (re)
+ \tformat: 1 (esc)
+ $ rbd info foo --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rb.0.*", (glob)
+ "format": 1,
+ "id": "",
+ "name": "foo",
+ "object_size": 4194304,
+ "objects": 256,
+ "order": 22,
+ "size": 1073741824,
+ "snapshot_count": 1
+ }
+ $ rbd info foo --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>foo</name>
+ <id/>
+ <size>1073741824</size>
+ <objects>256</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>1</snapshot_count>
+ <block_name_prefix>rb.0.*</block_name_prefix> (glob)
+ <format>1</format>
+ </image>
+ $ rbd info foo@snap
+ rbd image 'foo':
+ \tsize 1 GiB in 256 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 1 (esc)
+ [^^]+ (re)
+ \tformat: 1 (esc)
+ \tprotected: False (esc)
+ $ rbd info foo@snap --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rb.0.*", (glob)
+ "format": 1,
+ "id": "",
+ "name": "foo",
+ "object_size": 4194304,
+ "objects": 256,
+ "order": 22,
+ "protected": "false",
+ "size": 1073741824,
+ "snapshot_count": 1
+ }
+ $ rbd info foo@snap --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>foo</name>
+ <id/>
+ <size>1073741824</size>
+ <objects>256</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>1</snapshot_count>
+ <block_name_prefix>rb.0*</block_name_prefix> (glob)
+ <format>1</format>
+ <protected>false</protected>
+ </image>
+ $ rbd info bar
+ rbd image 'bar':
+ \tsize 1 GiB in 256 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 2 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering, exclusive-lock, object-map, fast-diff, deep-flatten (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ $ rbd info bar --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering",
+ "exclusive-lock",
+ "object-map",
+ "fast-diff",
+ "deep-flatten"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "bar",
+ "object_size": 4194304,
+ "objects": 256,
+ "op_features": [],
+ "order": 22,
+ "size": 1073741824,
+ "snapshot_count": 2
+ }
+ $ rbd info bar --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>bar</name>
+ <id>*</id> (glob)
+ <size>1073741824</size>
+ <objects>256</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>2</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ <feature>exclusive-lock</feature>
+ <feature>object-map</feature>
+ <feature>fast-diff</feature>
+ <feature>deep-flatten</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ </image>
+ $ rbd info bar@snap
+ rbd image 'bar':
+ \tsize 512 MiB in 128 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 2 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering, exclusive-lock, object-map, fast-diff, deep-flatten (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ \tprotected: True (esc)
+ $ rbd info bar@snap --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering",
+ "exclusive-lock",
+ "object-map",
+ "fast-diff",
+ "deep-flatten"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "bar",
+ "object_size": 4194304,
+ "objects": 128,
+ "op_features": [],
+ "order": 22,
+ "protected": "true",
+ "size": 536870912,
+ "snapshot_count": 2
+ }
+ $ rbd info bar@snap --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>bar</name>
+ <id>*</id> (glob)
+ <size>536870912</size>
+ <objects>128</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>2</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ <feature>exclusive-lock</feature>
+ <feature>object-map</feature>
+ <feature>fast-diff</feature>
+ <feature>deep-flatten</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ <protected>true</protected>
+ </image>
+ $ rbd info bar@snap2
+ rbd image 'bar':
+ \tsize 1 GiB in 256 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 2 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering, exclusive-lock, object-map, fast-diff, deep-flatten (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ \tprotected: False (esc)
+ $ rbd info bar@snap2 --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering",
+ "exclusive-lock",
+ "object-map",
+ "fast-diff",
+ "deep-flatten"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "bar",
+ "object_size": 4194304,
+ "objects": 256,
+ "op_features": [],
+ "order": 22,
+ "protected": "false",
+ "size": 1073741824,
+ "snapshot_count": 2
+ }
+ $ rbd info bar@snap2 --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>bar</name>
+ <id>*</id> (glob)
+ <size>1073741824</size>
+ <objects>256</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>2</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ <feature>exclusive-lock</feature>
+ <feature>object-map</feature>
+ <feature>fast-diff</feature>
+ <feature>deep-flatten</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ <protected>false</protected>
+ </image>
+ $ rbd info baz
+ rbd image 'baz':
+ \tsize 2 GiB in 512 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 0 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ $ rbd info baz --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "baz",
+ "object_size": 4194304,
+ "objects": 512,
+ "op_features": [],
+ "order": 22,
+ "size": 2147483648,
+ "snapshot_count": 0
+ }
+ $ rbd info baz --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>baz</name>
+ <id>*</id> (glob)
+ <size>2147483648</size>
+ <objects>512</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>0</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ </image>
+ $ rbd info quux
+ rbd image 'quux':
+ \tsize 1 MiB in 1 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 0 (esc)
+ [^^]+ (re)
+ \tformat: 1 (esc)
+ $ rbd info quux --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "block_name_prefix": "rb.0.*", (glob)
+ "format": 1,
+ "id": "",
+ "name": "quux",
+ "object_size": 4194304,
+ "objects": 1,
+ "order": 22,
+ "size": 1048576,
+ "snapshot_count": 0
+ }
+ $ rbd info quux --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>quux</name>
+ <id/>
+ <size>1048576</size>
+ <objects>1</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>0</snapshot_count>
+ <block_name_prefix>rb.0.*</block_name_prefix> (glob)
+ <format>1</format>
+ </image>
+ $ rbd info rbd_other/child
+ rbd image 'child':
+ \tsize 512 MiB in 128 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 1 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering, exclusive-lock, object-map, fast-diff (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ $ rbd info rbd_other/child --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering",
+ "exclusive-lock",
+ "object-map",
+ "fast-diff"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "child",
+ "object_size": 4194304,
+ "objects": 128,
+ "op_features": [],
+ "order": 22,
+ "size": 536870912,
+ "snapshot_count": 1
+ }
+ $ rbd info rbd_other/child --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>child</name>
+ <id>*</id> (glob)
+ <size>536870912</size>
+ <objects>128</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>1</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ <feature>exclusive-lock</feature>
+ <feature>object-map</feature>
+ <feature>fast-diff</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ </image>
+ $ rbd info rbd_other/child@snap
+ rbd image 'child':
+ \tsize 512 MiB in 128 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 1 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering, exclusive-lock, object-map, fast-diff (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ \tprotected: False (esc)
+ \tparent: rbd/bar@snap (esc)
+ \toverlap: 512 MiB (esc)
+ $ rbd info rbd_other/child@snap --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering",
+ "exclusive-lock",
+ "object-map",
+ "fast-diff"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "child",
+ "object_size": 4194304,
+ "objects": 128,
+ "op_features": [],
+ "order": 22,
+ "parent": {
+ "id": "*", (glob)
+ "image": "bar",
+ "overlap": 536870912,
+ "pool": "rbd",
+ "pool_namespace": "",
+ "snapshot": "snap",
+ "trash": false
+ },
+ "protected": "false",
+ "size": 536870912,
+ "snapshot_count": 1
+ }
+ $ rbd info rbd_other/child@snap --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>child</name>
+ <id>*</id> (glob)
+ <size>536870912</size>
+ <objects>128</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>1</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ <feature>exclusive-lock</feature>
+ <feature>object-map</feature>
+ <feature>fast-diff</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ <protected>false</protected>
+ <parent>
+ <pool>rbd</pool>
+ <pool_namespace/>
+ <image>bar</image>
+ <id>*</id> (glob)
+ <snapshot>snap</snapshot>
+ <trash>false</trash>
+ <overlap>536870912</overlap>
+ </parent>
+ </image>
+ $ rbd info rbd_other/deep-flatten-child
+ rbd image 'deep-flatten-child':
+ \tsize 512 MiB in 128 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 1 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering, exclusive-lock, object-map, fast-diff, deep-flatten (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ $ rbd info rbd_other/deep-flatten-child --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering",
+ "exclusive-lock",
+ "object-map",
+ "fast-diff",
+ "deep-flatten"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "deep-flatten-child",
+ "object_size": 4194304,
+ "objects": 128,
+ "op_features": [],
+ "order": 22,
+ "size": 536870912,
+ "snapshot_count": 1
+ }
+ $ rbd info rbd_other/deep-flatten-child --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>deep-flatten-child</name>
+ <id>*</id> (glob)
+ <size>536870912</size>
+ <objects>128</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>1</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ <feature>exclusive-lock</feature>
+ <feature>object-map</feature>
+ <feature>fast-diff</feature>
+ <feature>deep-flatten</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ </image>
+ $ rbd info rbd_other/deep-flatten-child@snap
+ rbd image 'deep-flatten-child':
+ \tsize 512 MiB in 128 objects (esc)
+ \torder 22 (4 MiB objects) (esc)
+ \tsnapshot_count: 1 (esc)
+ \tid:* (glob)
+ [^^]+ (re)
+ \tformat: 2 (esc)
+ \tfeatures: layering, exclusive-lock, object-map, fast-diff, deep-flatten (esc)
+ \top_features: (esc)
+ \tflags: (esc)
+ \tcreate_timestamp:* (glob)
+ \taccess_timestamp:* (glob)
+ \tmodify_timestamp:* (glob)
+ \tprotected: False (esc)
+ $ rbd info rbd_other/deep-flatten-child@snap --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "access_timestamp": "*", (glob)
+ "block_name_prefix": "rbd_data.*", (glob)
+ "create_timestamp": "*", (glob)
+ "features": [
+ "layering",
+ "exclusive-lock",
+ "object-map",
+ "fast-diff",
+ "deep-flatten"
+ ],
+ "flags": [],
+ "format": 2,
+ "id": "*", (glob)
+ "modify_timestamp": "*", (glob)
+ "name": "deep-flatten-child",
+ "object_size": 4194304,
+ "objects": 128,
+ "op_features": [],
+ "order": 22,
+ "protected": "false",
+ "size": 536870912,
+ "snapshot_count": 1
+ }
+ $ rbd info rbd_other/deep-flatten-child@snap --format xml | xmlstarlet format -s 2 -o || true
+ <image>
+ <name>deep-flatten-child</name>
+ <id>*</id> (glob)
+ <size>536870912</size>
+ <objects>128</objects>
+ <order>22</order>
+ <object_size>4194304</object_size>
+ <snapshot_count>1</snapshot_count>
+ <block_name_prefix>rbd_data.*</block_name_prefix> (glob)
+ <format>2</format>
+ <features>
+ <feature>layering</feature>
+ <feature>exclusive-lock</feature>
+ <feature>object-map</feature>
+ <feature>fast-diff</feature>
+ <feature>deep-flatten</feature>
+ </features>
+ <op_features/>
+ <flags/>
+ <create_timestamp>*</create_timestamp> (glob)
+ <access_timestamp>*</access_timestamp> (glob)
+ <modify_timestamp>*</modify_timestamp> (glob)
+ <protected>false</protected>
+ </image>
+ $ rbd list
+ foo
+ quux
+ bar
+ baz
+ quuy
+ $ rbd list --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ "foo",
+ "quux",
+ "bar",
+ "baz",
+ "quuy"
+ ]
+ $ rbd list --format xml | xmlstarlet format -s 2 -o || true
+ <images>
+ <name>foo</name>
+ <name>quux</name>
+ <name>bar</name>
+ <name>baz</name>
+ <name>quuy</name>
+ </images>
+ $ rbd list -l
+ NAME SIZE PARENT FMT PROT LOCK
+ foo 1 GiB 1
+ foo@snap 1 GiB 1
+ quux 1 MiB 1 excl
+ bar 1 GiB 2
+ bar@snap 512 MiB 2 yes
+ bar@snap2 1 GiB 2
+ baz 2 GiB 2 shr
+ quuy 2 GiB 2
+ $ rbd list -l --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "format": 1,
+ "id": "",
+ "image": "foo",
+ "size": 1073741824
+ },
+ {
+ "format": 1,
+ "id": "",
+ "image": "foo",
+ "protected": "false",
+ "size": 1073741824,
+ "snapshot": "snap",
+ "snapshot_id": * (glob)
+ },
+ {
+ "format": 1,
+ "id": "",
+ "image": "quux",
+ "lock_type": "exclusive",
+ "size": 1048576
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "bar",
+ "size": 1073741824
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "bar",
+ "protected": "true",
+ "size": 536870912,
+ "snapshot": "snap",
+ "snapshot_id": * (glob)
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "bar",
+ "protected": "false",
+ "size": 1073741824,
+ "snapshot": "snap2",
+ "snapshot_id": * (glob)
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "baz",
+ "lock_type": "shared",
+ "size": 2147483648
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "quuy",
+ "size": 2147483648
+ }
+ ]
+ $ rbd list -l --format xml | xmlstarlet format -s 2 -o || true
+ <images>
+ <image>
+ <image>foo</image>
+ <id/>
+ <size>1073741824</size>
+ <format>1</format>
+ </image>
+ <snapshot>
+ <image>foo</image>
+ <id/>
+ <snapshot>snap</snapshot>
+ <snapshot_id>*</snapshot_id> (glob)
+ <size>1073741824</size>
+ <format>1</format>
+ <protected>false</protected>
+ </snapshot>
+ <image>
+ <image>quux</image>
+ <id/>
+ <size>1048576</size>
+ <format>1</format>
+ <lock_type>exclusive</lock_type>
+ </image>
+ <image>
+ <image>bar</image>
+ <id>*</id> (glob)
+ <size>1073741824</size>
+ <format>2</format>
+ </image>
+ <snapshot>
+ <image>bar</image>
+ <id>*</id> (glob)
+ <snapshot>snap</snapshot>
+ <snapshot_id>*</snapshot_id> (glob)
+ <size>536870912</size>
+ <format>2</format>
+ <protected>true</protected>
+ </snapshot>
+ <snapshot>
+ <image>bar</image>
+ <id>*</id> (glob)
+ <snapshot>snap2</snapshot>
+ <snapshot_id>*</snapshot_id> (glob)
+ <size>1073741824</size>
+ <format>2</format>
+ <protected>false</protected>
+ </snapshot>
+ <image>
+ <image>baz</image>
+ <id>*</id> (glob)
+ <size>2147483648</size>
+ <format>2</format>
+ <lock_type>shared</lock_type>
+ </image>
+ <image>
+ <image>quuy</image>
+ <id>*</id> (glob)
+ <size>2147483648</size>
+ <format>2</format>
+ </image>
+ </images>
+ $ rbd list rbd_other
+ child
+ deep-flatten-child
+ $ rbd list rbd_other --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ "child",
+ "deep-flatten-child"
+ ]
+ $ rbd list rbd_other --format xml | xmlstarlet format -s 2 -o || true
+ <images>
+ <name>child</name>
+ <name>deep-flatten-child</name>
+ </images>
+ $ rbd list rbd_other -l
+ NAME SIZE PARENT FMT PROT LOCK
+ child 512 MiB 2
+ child@snap 512 MiB rbd/bar@snap 2
+ deep-flatten-child 512 MiB 2
+ deep-flatten-child@snap 512 MiB 2
+ $ rbd list rbd_other -l --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "child",
+ "size": 536870912
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "child",
+ "parent": {
+ "image": "bar",
+ "pool": "rbd",
+ "pool_namespace": "",
+ "snapshot": "snap"
+ },
+ "protected": "false",
+ "size": 536870912,
+ "snapshot": "snap",
+ "snapshot_id": * (glob)
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "deep-flatten-child",
+ "size": 536870912
+ },
+ {
+ "format": 2,
+ "id": "*", (glob)
+ "image": "deep-flatten-child",
+ "protected": "false",
+ "size": 536870912,
+ "snapshot": "snap",
+ "snapshot_id": * (glob)
+ }
+ ]
+ $ rbd list rbd_other -l --format xml | xmlstarlet format -s 2 -o || true
+ <images>
+ <image>
+ <image>child</image>
+ <id>*</id> (glob)
+ <size>536870912</size>
+ <format>2</format>
+ </image>
+ <snapshot>
+ <image>child</image>
+ <id>*</id> (glob)
+ <snapshot>snap</snapshot>
+ <snapshot_id>*</snapshot_id> (glob)
+ <size>536870912</size>
+ <parent>
+ <pool>rbd</pool>
+ <pool_namespace/>
+ <image>bar</image>
+ <snapshot>snap</snapshot>
+ </parent>
+ <format>2</format>
+ <protected>false</protected>
+ </snapshot>
+ <image>
+ <image>deep-flatten-child</image>
+ <id>*</id> (glob)
+ <size>536870912</size>
+ <format>2</format>
+ </image>
+ <snapshot>
+ <image>deep-flatten-child</image>
+ <id>*</id> (glob)
+ <snapshot>snap</snapshot>
+ <snapshot_id>*</snapshot_id> (glob)
+ <size>536870912</size>
+ <format>2</format>
+ <protected>false</protected>
+ </snapshot>
+ </images>
+ $ rbd lock list foo
+ $ rbd lock list foo --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ []
+ $ rbd lock list foo --format xml | xmlstarlet format -s 2 -o || true
+ <locks/>
+ $ rbd lock list quux
+ There is 1 exclusive lock on this image.
+ Locker*ID*Address* (glob)
+ client.* id * (glob)
+ $ rbd lock list quux --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "address": "*", (glob)
+ "id": "id",
+ "locker": "client.*" (glob)
+ }
+ ]
+ $ rbd lock list quux --format xml | xmlstarlet format -s 2 -o || true
+ <locks>
+ <lock>
+ <id>id</id>
+ <locker>client.*</locker> (glob)
+ <address>*</address> (glob)
+ </lock>
+ </locks>
+ $ rbd lock list baz
+ There are 3 shared locks on this image.
+ Lock tag: tag
+ Locker*ID*Address* (glob)
+ client.*id[123].* (re)
+ client.*id[123].* (re)
+ client.*id[123].* (re)
+ $ rbd lock list baz --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "address": "*", (glob)
+ "id": "id*", (glob)
+ "locker": "client.*" (glob)
+ },
+ {
+ "address": "*", (glob)
+ "id": "id*", (glob)
+ "locker": "client.*" (glob)
+ },
+ {
+ "address": "*", (glob)
+ "id": "id*", (glob)
+ "locker": "client.*" (glob)
+ }
+ ]
+ $ rbd lock list baz --format xml | xmlstarlet format -s 2 -o || true
+ <locks>
+ <lock>
+ <id>id*</id> (glob)
+ <locker>client.*</locker> (glob)
+ <address>*</address> (glob)
+ </lock>
+ <lock>
+ <id>id*</id> (glob)
+ <locker>client.*</locker> (glob)
+ <address>*</address> (glob)
+ </lock>
+ <lock>
+ <id>id*</id> (glob)
+ <locker>client.*</locker> (glob)
+ <address>*</address> (glob)
+ </lock>
+ </locks>
+ $ rbd snap list foo
+ SNAPID*NAME*SIZE*PROTECTED*TIMESTAMP* (glob)
+ *snap*1 GiB* (glob)
+ $ rbd snap list foo --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "id": *, (glob)
+ "name": "snap",
+ "protected": "false",
+ "size": 1073741824,
+ "timestamp": ""
+ }
+ ]
+ $ rbd snap list foo --format xml | xmlstarlet format -s 2 -o || true
+ <snapshots>
+ <snapshot>
+ <id>*</id> (glob)
+ <name>snap</name>
+ <size>1073741824</size>
+ <protected>false</protected>
+ <timestamp/>
+ </snapshot>
+ </snapshots>
+ $ rbd snap list bar
+ SNAPID*NAME*SIZE*PROTECTED*TIMESTAMP* (glob)
+ *snap*512 MiB*yes* (glob)
+ *snap2*1 GiB* (glob)
+ $ rbd snap list bar --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "id": *, (glob)
+ "name": "snap",
+ "protected": "true",
+ "size": 536870912,
+ "timestamp": * (glob)
+ },
+ {
+ "id": *, (glob)
+ "name": "snap2",
+ "protected": "false",
+ "size": 1073741824,
+ "timestamp": * (glob)
+ }
+ ]
+ $ rbd snap list bar --format xml | xmlstarlet format -s 2 -o || true
+ <snapshots>
+ <snapshot>
+ <id>*</id> (glob)
+ <name>snap</name>
+ <size>536870912</size>
+ <protected>true</protected>
+ <timestamp>*</timestamp> (glob)
+ </snapshot>
+ <snapshot>
+ <id>*</id> (glob)
+ <name>snap2</name>
+ <size>1073741824</size>
+ <protected>false</protected>
+ <timestamp>*</timestamp> (glob)
+ </snapshot>
+ </snapshots>
+ $ rbd snap list baz
+ $ rbd snap list baz --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ []
+ $ rbd snap list baz --format xml | xmlstarlet format -s 2 -o || true
+ <snapshots/>
+ $ rbd snap list rbd_other/child
+ SNAPID*NAME*SIZE*PROTECTED*TIMESTAMP* (glob)
+ *snap*512 MiB* (glob)
+ $ rbd snap list rbd_other/child --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "id": *, (glob)
+ "name": "snap",
+ "protected": "false",
+ "size": 536870912,
+ "timestamp": * (glob)
+ }
+ ]
+ $ rbd snap list rbd_other/child --format xml | xmlstarlet format -s 2 -o || true
+ <snapshots>
+ <snapshot>
+ <id>*</id> (glob)
+ <name>snap</name>
+ <size>536870912</size>
+ <protected>false</protected>
+ <timestamp>*</timestamp> (glob)
+ </snapshot>
+ </snapshots>
+ $ rbd disk-usage --pool rbd_other 2>/dev/null
+ NAME PROVISIONED USED
+ child@snap 512 MiB 0 B
+ child 512 MiB 4 MiB
+ deep-flatten-child@snap 512 MiB 0 B
+ deep-flatten-child 512 MiB 0 B
+ <TOTAL> 1 GiB 4 MiB
+ $ rbd disk-usage --pool rbd_other --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ {
+ "images": [
+ {
+ "id": "*", (glob)
+ "name": "child",
+ "provisioned_size": 536870912,
+ "snapshot": "snap",
+ "snapshot_id": *, (glob)
+ "used_size": 0
+ },
+ {
+ "id": "*", (glob)
+ "name": "child",
+ "provisioned_size": 536870912,
+ "used_size": 4194304
+ },
+ {
+ "id": "*", (glob)
+ "name": "deep-flatten-child",
+ "provisioned_size": 536870912,
+ "snapshot": "snap",
+ "snapshot_id": *, (glob)
+ "used_size": 0
+ },
+ {
+ "id": "*", (glob)
+ "name": "deep-flatten-child",
+ "provisioned_size": 536870912,
+ "used_size": 0
+ }
+ ],
+ "total_provisioned_size": 1073741824,
+ "total_used_size": 4194304
+ }
+ $ rbd disk-usage --pool rbd_other --format xml | xmlstarlet format -s 2 -o || true
+ <stats>
+ <images>
+ <image>
+ <name>child</name>
+ <id>*</id> (glob)
+ <snapshot>snap</snapshot>
+ <snapshot_id>*</snapshot_id> (glob)
+ <provisioned_size>536870912</provisioned_size>
+ <used_size>0</used_size>
+ </image>
+ <image>
+ <name>child</name>
+ <id>*</id> (glob)
+ <provisioned_size>536870912</provisioned_size>
+ <used_size>4194304</used_size>
+ </image>
+ <image>
+ <name>deep-flatten-child</name>
+ <id>*</id> (glob)
+ <snapshot>snap</snapshot>
+ <snapshot_id>*</snapshot_id> (glob)
+ <provisioned_size>536870912</provisioned_size>
+ <used_size>0</used_size>
+ </image>
+ <image>
+ <name>deep-flatten-child</name>
+ <id>*</id> (glob)
+ <provisioned_size>536870912</provisioned_size>
+ <used_size>0</used_size>
+ </image>
+ </images>
+ <total_provisioned_size>1073741824</total_provisioned_size>
+ <total_used_size>4194304</total_used_size>
+ </stats>
+
+# cleanup
+ $ rbd snap remove --no-progress rbd_other/deep-flatten-child@snap
+ $ rbd snap remove --no-progress rbd_other/child@snap
+ $ rbd snap unprotect bar@snap
+ $ rbd snap purge bar 2> /dev/null
+ $ rbd snap purge foo 2> /dev/null
+ $ rbd rm rbd_other/deep-flatten-child 2> /dev/null
+ $ rbd rm rbd_other/child 2> /dev/null
+ $ rbd rm foo 2> /dev/null
+ $ rbd rm bar 2> /dev/null
+ $ rbd rm quux 2> /dev/null
+ $ rbd rm quuy 2> /dev/null
+ $ rbd rm baz 2> /dev/null
+ $ ceph osd pool delete rbd_other rbd_other --yes-i-really-really-mean-it
+ pool 'rbd_other' removed
diff --git a/src/test/cli-integration/rbd/gwcli_create.t b/src/test/cli-integration/rbd/gwcli_create.t
new file mode 100644
index 000000000..b464681fb
--- /dev/null
+++ b/src/test/cli-integration/rbd/gwcli_create.t
@@ -0,0 +1,78 @@
+Podman find iSCSI container
+===========================
+ $ ISCSI_CONTAINER=$(sudo podman ps -a | grep -F 'iscsi' | grep -Fv 'tcmu' | awk '{print $1}')
+
+Dismiss the "could not load preferences file .gwcli/prefs.bin" warning
+======================================================================
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls >/dev/null 2>&1
+
+Create a datapool/block0 disk
+=============================
+ $ sudo podman exec $ISCSI_CONTAINER gwcli disks/ create pool=datapool image=block0 size=300M wwn=36001405da17b74481464e9fa968746d3
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls disks/ | grep 'o- disks' | awk -F'[' '{print $2}'
+ 300M, Disks: 1]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls disks/ | grep 'o- datapool' | awk -F'[' '{print $2}'
+ datapool (300M)]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls disks/ | grep 'o- block0' | awk -F'[' '{print $2}'
+ datapool/block0 (Unknown, 300M)]
+
+Create the target IQN
+=====================
+ $ sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/ create target_iqn=iqn.2003-01.com.redhat.iscsi-gw:ceph-gw
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- iscsi-targets' | awk -F'[' '{print $2}'
+ DiscoveryAuth: None, Targets: 1]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- iqn.2003-01.com.redhat.iscsi-gw:ceph-gw' | awk -F'[' '{print $2}'
+ Auth: None, Gateways: 0]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- disks' | awk -F'[' '{print $2}'
+ Disks: 0]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- gateways' | awk -F'[' '{print $2}'
+ Up: 0/0, Portals: 0]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- host-groups' | awk -F'[' '{print $2}'
+ Groups : 0]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- hosts' | awk -F'[' '{print $2}'
+ Auth: ACL_ENABLED, Hosts: 0]
+
+Create the first gateway
+========================
+ $ HOST=$(python3 -c "import socket; print(socket.getfqdn())")
+ > IP=`hostname -i | awk '{print $1}'`
+ > sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/gateways create ip_addresses=$IP gateway_name=$HOST
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- gateways' | awk -F'[' '{print $2}'
+ Up: 1/1, Portals: 1]
+
+Create the second gateway
+========================
+ $ IP=`cat /etc/ceph/iscsi-gateway.cfg |grep 'trusted_ip_list' | awk -F'[, ]' '{print $3}'`
+ > if [ "$IP" != `hostname -i | awk '{print $1}'` ]; then
+ > HOST=$(python3 -c "import socket; print(socket.getfqdn('$IP'))")
+ > sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/gateways create ip_addresses=$IP gateway_name=$HOST
+ > fi
+ $ IP=`cat /etc/ceph/iscsi-gateway.cfg |grep 'trusted_ip_list' | awk -F'[, ]' '{print $4}'`
+ > if [ "$IP" != `hostname -i | awk '{print $1}'` ]; then
+ > HOST=$(python3 -c "import socket; print(socket.getfqdn('$IP'))")
+ > sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/gateways create ip_addresses=$IP gateway_name=$HOST
+ > fi
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- gateways' | awk -F'[' '{print $2}'
+ Up: 2/2, Portals: 2]
+
+Attach the disk
+===============
+ $ sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/disks/ add disk=datapool/block0
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- disks' | awk -F'[' '{print $2}'
+ Disks: 1]
+
+Create a host
+=============
+ $ sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/hosts create client_iqn=iqn.1994-05.com.redhat:client
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- hosts' | awk -F'[' '{print $2}'
+ Auth: ACL_ENABLED, Hosts: 1]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- iqn.1994-05.com.redhat:client' | awk -F'[' '{print $2}'
+ Auth: None, Disks: 0(0.00Y)]
+
+Map the LUN
+===========
+ $ sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/hosts/iqn.1994-05.com.redhat:client disk disk=datapool/block0
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- hosts' | awk -F'[' '{print $2}'
+ Auth: ACL_ENABLED, Hosts: 1]
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- iqn.1994-05.com.redhat:client' | awk -F'[' '{print $2}'
+ Auth: None, Disks: 1(300M)]
diff --git a/src/test/cli-integration/rbd/gwcli_delete.t b/src/test/cli-integration/rbd/gwcli_delete.t
new file mode 100644
index 000000000..e973d87a3
--- /dev/null
+++ b/src/test/cli-integration/rbd/gwcli_delete.t
@@ -0,0 +1,31 @@
+Podman find iSCSI container
+===========================
+ $ ISCSI_CONTAINER=$(sudo podman ps -a | grep -F 'iscsi' | grep -Fv 'tcmu' | awk '{print $1}')
+
+Dismiss the "could not load preferences file .gwcli/prefs.bin" warning
+======================================================================
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls >/dev/null 2>&1
+
+Delete the host
+===============
+ $ sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/hosts delete client_iqn=iqn.1994-05.com.redhat:client
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- hosts' | awk -F'[' '{print $2}'
+ Auth: ACL_ENABLED, Hosts: 0]
+
+Delete the iscsi-targets disk
+=============================
+ $ sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/disks/ delete disk=datapool/block0
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- disks' | awk -F'[' '{print $2}'
+ Disks: 0]
+
+Delete the target IQN
+=====================
+ $ sudo podman exec $ISCSI_CONTAINER gwcli iscsi-targets/ delete target_iqn=iqn.2003-01.com.redhat.iscsi-gw:ceph-gw
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls iscsi-targets/ | grep 'o- iscsi-targets' | awk -F'[' '{print $2}'
+ DiscoveryAuth: None, Targets: 0]
+
+Delete the disks
+================
+ $ sudo podman exec $ISCSI_CONTAINER gwcli disks/ delete image_id=datapool/block0
+ $ sudo podman exec $ISCSI_CONTAINER gwcli ls disks/ | grep 'o- disks' | awk -F'[' '{print $2}'
+ 0.00Y, Disks: 0]
diff --git a/src/test/cli-integration/rbd/iscsi_client.t b/src/test/cli-integration/rbd/iscsi_client.t
new file mode 100644
index 000000000..f636d540d
--- /dev/null
+++ b/src/test/cli-integration/rbd/iscsi_client.t
@@ -0,0 +1,30 @@
+Login to the target
+===================
+ $ IP=`cat /etc/ceph/iscsi-gateway.cfg |grep 'trusted_ip_list' | awk -F'[, ]' '{print $3}'`
+ > sudo iscsiadm -m discovery -t st -p $IP -l 2&> /dev/null
+ $ sleep 10
+ $ sudo ls /dev/disk/by-path/ |grep 'iscsi-iqn.2003-01.com.redhat.iscsi-gw:ceph-gw' |wc -l
+ 2
+
+Make filesystem
+===============
+ $ device=`sudo multipath -l | grep 'LIO-ORG,TCMU device' | awk '{print $1}'`
+ > sudo mkfs.xfs /dev/mapper/$device -f | grep 'meta-data=/dev/mapper/mpath' | awk '{print $2}'
+ isize=512
+
+Write/Read test
+===============
+ $ device=`sudo multipath -l | grep 'LIO-ORG,TCMU device' | awk '{print $1}'`
+ > sudo dd if=/dev/random of=/tmp/iscsi_tmpfile bs=1 count=1K status=none
+ > sudo dd if=/tmp/iscsi_tmpfile of=/dev/mapper/$device bs=1 count=1K status=none
+ > sudo dd if=/dev/mapper/$device of=/tmp/iscsi_tmpfile1 bs=1 count=1K status=none
+ $ sudo diff /tmp/iscsi_tmpfile /tmp/iscsi_tmpfile1
+
+Logout the targets
+==================
+ $ IP=`cat /etc/ceph/iscsi-gateway.cfg |grep 'trusted_ip_list' | awk -F'[, ]' '{print $3}'`
+ > sudo iscsiadm -m node -T iqn.2003-01.com.redhat.iscsi-gw:ceph-gw -p $IP:3260 -u | grep 'successful' | awk -F']' '{print $2}'
+ successful.
+ $ IP=`cat /etc/ceph/iscsi-gateway.cfg |grep 'trusted_ip_list' | awk -F'[, ]' '{print $4}'`
+ > sudo iscsiadm -m node -T iqn.2003-01.com.redhat.iscsi-gw:ceph-gw -p $IP:3260 -u | grep 'successful' | awk -F']' '{print $2}'
+ successful.
diff --git a/src/test/cli-integration/rbd/mon-command-help.t b/src/test/cli-integration/rbd/mon-command-help.t
new file mode 100644
index 000000000..ec7740cbe
--- /dev/null
+++ b/src/test/cli-integration/rbd/mon-command-help.t
@@ -0,0 +1,44 @@
+ $ ceph rbd -h | sed -n '/^ Monitor commands: $/,$p'
+ Monitor commands:
+ =================
+ rbd mirror snapshot schedule add Add rbd mirror snapshot schedule
+ <level_spec> <interval> [<start_time>]
+ rbd mirror snapshot schedule list List rbd mirror snapshot schedule
+ [<level_spec>]
+ rbd mirror snapshot schedule remove Remove rbd mirror snapshot schedule
+ <level_spec> [<interval>] [<start_
+ time>]
+ rbd mirror snapshot schedule status Show rbd mirror snapshot schedule status
+ [<level_spec>]
+ rbd perf image counters [<pool_spec>] Retrieve current RBD IO performance
+ [<sort_by:write_ops|write_bytes|write_ counters
+ latency|read_ops|read_bytes|read_
+ latency>]
+ rbd perf image stats [<pool_spec>] Retrieve current RBD IO performance
+ [<sort_by:write_ops|write_bytes|write_ stats
+ latency|read_ops|read_bytes|read_
+ latency>]
+ rbd task add flatten <image_spec> Flatten a cloned image asynchronously
+ in the background
+ rbd task add migration abort <image_ Abort a prepared migration
+ spec> asynchronously in the background
+ rbd task add migration commit <image_ Commit an executed migration
+ spec> asynchronously in the background
+ rbd task add migration execute <image_ Execute an image migration
+ spec> asynchronously in the background
+ rbd task add remove <image_spec> Remove an image asynchronously in the
+ background
+ rbd task add trash remove <image_id_ Remove an image from the trash
+ spec> asynchronously in the background
+ rbd task cancel <task_id> Cancel a pending or running
+ asynchronous task
+ rbd task list [<task_id>] List pending or running asynchronous
+ tasks
+ rbd trash purge schedule add <level_ Add rbd trash purge schedule
+ spec> <interval> [<start_time>]
+ rbd trash purge schedule list [<level_ List rbd trash purge schedule
+ spec>]
+ rbd trash purge schedule remove <level_ Remove rbd trash purge schedule
+ spec> [<interval>] [<start_time>]
+ rbd trash purge schedule status Show rbd trash purge schedule status
+ [<level_spec>]
diff --git a/src/test/cli-integration/rbd/rest_api_create.t b/src/test/cli-integration/rbd/rest_api_create.t
new file mode 100644
index 000000000..d0308eef3
--- /dev/null
+++ b/src/test/cli-integration/rbd/rest_api_create.t
@@ -0,0 +1,44 @@
+Create a datapool/block1 disk
+=============================
+ $ sudo curl --user admin:admin -d mode=create -d size=300M -X PUT http://127.0.0.1:5000/api/disk/datapool/block1 -s
+ {"message":"disk create/update successful"}
+
+Create the target IQN
+=====================
+ $ sudo curl --user admin:admin -X PUT http://127.0.0.1:5000/api/target/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw -s
+ {"message":"Target defined successfully"}
+
+Create the first gateway
+========================
+ $ HOST=`python3 -c "import socket; print(socket.getfqdn())"`
+ > IP=`hostname -i | awk '{print $1}'`
+ > sudo curl --user admin:admin -d ip_address=$IP -X PUT http://127.0.0.1:5000/api/gateway/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/$HOST -s
+ {"message":"Gateway creation successful"}
+
+Create the second gateway
+========================
+ $ IP=`cat /etc/ceph/iscsi-gateway.cfg |grep 'trusted_ip_list' | awk -F'[, ]' '{print $3}'`
+ > if [ "$IP" != `hostname -i | awk '{print $1}'` ]; then
+ > HOST=`python3 -c "import socket; print(socket.getfqdn('$IP'))"`
+ > sudo curl --user admin:admin -d ip_address=$IP -X PUT http://127.0.0.1:5000/api/gateway/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/$HOST -s
+ > else
+ > IP=`cat /etc/ceph/iscsi-gateway.cfg |grep 'trusted_ip_list' | awk -F'[, ]' '{print $4}'`
+ > HOST=`python3 -c "import socket; print(socket.getfqdn('$IP'))"`
+ > sudo curl --user admin:admin -d ip_address=$IP -X PUT http://127.0.0.1:5000/api/gateway/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/$HOST -s
+ > fi
+ {"message":"Gateway creation successful"}
+
+Attach the disk
+===============
+ $ sudo curl --user admin:admin -d disk=datapool/block1 -X PUT http://127.0.0.1:5000/api/targetlun/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw -s
+ {"message":"Target LUN mapping updated successfully"}
+
+Create a host
+=============
+ $ sudo curl --user admin:admin -X PUT http://127.0.0.1:5000/api/client/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/iqn.1994-05.com.redhat:client -s
+ {"message":"client create/update successful"}
+
+Map the LUN
+===========
+ $ sudo curl --user admin:admin -d disk=datapool/block1 -X PUT http://127.0.0.1:5000/api/clientlun/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/iqn.1994-05.com.redhat:client -s
+ {"message":"client masking update successful"}
diff --git a/src/test/cli-integration/rbd/rest_api_delete.t b/src/test/cli-integration/rbd/rest_api_delete.t
new file mode 100644
index 000000000..0e66d25d9
--- /dev/null
+++ b/src/test/cli-integration/rbd/rest_api_delete.t
@@ -0,0 +1,19 @@
+Delete the host
+===============
+ $ sudo curl --user admin:admin -X DELETE http://127.0.0.1:5000/api/client/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw/iqn.1994-05.com.redhat:client -s
+ {"message":"client delete successful"}
+
+Delete the iscsi-targets disk
+=============================
+ $ sudo curl --user admin:admin -d disk=datapool/block1 -X DELETE http://127.0.0.1:5000/api/targetlun/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw -s
+ {"message":"Target LUN mapping updated successfully"}
+
+Delete the target IQN
+=====================
+ $ sudo curl --user admin:admin -X DELETE http://127.0.0.1:5000/api/target/iqn.2003-01.com.redhat.iscsi-gw:ceph-gw -s
+ {"message":"Target deleted."}
+
+Delete the disks
+================
+ $ sudo curl --user admin:admin -X DELETE http://127.0.0.1:5000/api/disk/datapool/block1 -s
+ {"message":"disk map deletion successful"}
diff --git a/src/test/cli-integration/rbd/snap-diff.t b/src/test/cli-integration/rbd/snap-diff.t
new file mode 100644
index 000000000..1ca2fb04d
--- /dev/null
+++ b/src/test/cli-integration/rbd/snap-diff.t
@@ -0,0 +1,48 @@
+ $ ceph osd pool create xrbddiff1
+ pool 'xrbddiff1' created
+ $ rbd pool init xrbddiff1
+ $ rbd create --thick-provision --size 1M xrbddiff1/xtestdiff1 --no-progress
+ $ rbd diff xrbddiff1/xtestdiff1 --format json
+ [{"offset":0,"length":1048576,"exists":"true"}]
+ $ rbd rm xrbddiff1/xtestdiff1 --no-progress
+ $ rbd create --size 1M xrbddiff1/xtestdiff1
+ $ rbd diff xrbddiff1/xtestdiff1 --format json
+ []
+ $ rbd snap create xrbddiff1/xtestdiff1 --snap=allzeroes --no-progress
+ $ rbd diff xrbddiff1/xtestdiff1 --format json
+ []
+ $ rbd diff --from-snap=allzeroes xrbddiff1/xtestdiff1 --format json
+ []
+ $ rbd bench --io-type write --io-size 1M --io-total 1M xrbddiff1/xtestdiff1 > /dev/null 2>&1
+ $ rbd diff xrbddiff1/xtestdiff1 --format json
+ [{"offset":0,"length":1048576,"exists":"true"}]
+ $ rbd diff --from-snap=allzeroes xrbddiff1/xtestdiff1 --format json
+ [{"offset":0,"length":1048576,"exists":"true"}]
+ $ rbd snap create xrbddiff1/xtestdiff1 --snap=snap1 --no-progress
+ $ rbd snap list xrbddiff1/xtestdiff1 --format json | python3 -mjson.tool --sort-keys | sed 's/,$/, /'
+ [
+ {
+ "id": *, (glob)
+ "name": "allzeroes",
+ "protected": "false",
+ "size": 1048576,
+ "timestamp": * (glob)
+ },
+ {
+ "id": *, (glob)
+ "name": "snap1",
+ "protected": "false",
+ "size": 1048576,
+ "timestamp": * (glob)
+ }
+ ]
+ $ rbd diff --from-snap=snap1 xrbddiff1/xtestdiff1 --format json
+ []
+ $ rbd snap rollback xrbddiff1/xtestdiff1@snap1 --no-progress
+ $ rbd diff --from-snap=snap1 xrbddiff1/xtestdiff1 --format json
+ []
+ $ rbd snap rollback xrbddiff1/xtestdiff1@allzeroes --no-progress
+ $ rbd diff --from-snap=allzeroes xrbddiff1/xtestdiff1 --format json
+ [{"offset":0,"length":1048576,"exists":"false"}]
+ $ ceph osd pool rm xrbddiff1 xrbddiff1 --yes-i-really-really-mean-it
+ pool 'xrbddiff1' removed
diff --git a/src/test/cli-integration/rbd/unmap.t b/src/test/cli-integration/rbd/unmap.t
new file mode 100644
index 000000000..3fdac43d7
--- /dev/null
+++ b/src/test/cli-integration/rbd/unmap.t
@@ -0,0 +1,488 @@
+
+Setup
+=====
+
+ $ rbd create --size 1 img
+ $ rbd snap create img@snap --no-progress
+ $ rbd create --size 1 anotherimg
+ $ ceph osd pool create custom >/dev/null 2>&1
+ $ rbd pool init custom
+ $ rbd create --size 1 custom/img
+ $ rbd snap create custom/img@snap --no-progress
+ $ rbd snap create custom/img@anothersnap --no-progress
+
+Spell out device instead of using $DEV - sfdisk is not a joke.
+
+ $ DEV=$(sudo rbd device map img)
+ $ cat <<EOF | sudo sfdisk /dev/rbd[01] >/dev/null 2>&1
+ > unit: sectors
+ > /dev/rbd0p1 : start= 2, size= 2, Id=83
+ > /dev/rbd0p2 : start= 5, size= 2043, Id= 5
+ > /dev/rbd0p3 : start= 0, size= 0, Id= 0
+ > /dev/rbd0p4 : start= 0, size= 0, Id= 0
+ > /dev/rbd0p5 : start= 7, size= 2, Id=83
+ > EOF
+
+
+Unmap by device
+===============
+
+Unmap by device (img is already mapped):
+
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap $DEV
+ $ rbd device list
+
+Unmap by device partition:
+
+ $ DEV=$(sudo rbd device map img)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap ${DEV}p1
+ $ rbd device list
+
+ $ DEV=$(sudo rbd device map img)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap ${DEV}p5
+ $ rbd device list
+
+Not a block device - random junk prefixed with /dev/ (so it's not
+interpreted as a spec):
+
+ $ sudo rbd device unmap /dev/foobar
+ rbd: '/dev/foobar' is not a block device
+ rbd: unmap failed: (22) Invalid argument
+ [22]
+
+Not a block device - device that's just been unmapped:
+
+ $ DEV=$(sudo rbd device map img)
+ $ sudo rbd device unmap $DEV
+ $ sudo rbd device unmap $DEV
+ rbd: '/dev/rbd?' is not a block device (glob)
+ rbd: unmap failed: (22) Invalid argument
+ [22]
+
+A block device, but not rbd:
+
+ $ sudo rbd device unmap /dev/[sv]da
+ rbd: '/dev/?da' is not an rbd device (glob)
+ rbd: unmap failed: (22) Invalid argument
+ [22]
+
+
+Unmap by spec
+=============
+
+img:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ rbd device list
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd --image img device unmap
+ $ rbd device list
+
+img@snap:
+
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap img@snap
+ $ rbd device list
+
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd --snap snap device unmap img
+ $ rbd device list
+
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd --image img --snap snap device unmap
+ $ rbd device list
+
+pool/img@snap, default pool:
+
+ $ sudo rbd device map rbd/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap rbd/img@snap
+ $ rbd device list
+
+ $ sudo rbd device map rbd/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd --pool rbd device unmap img@snap
+ $ rbd device list
+
+ $ sudo rbd device map rbd/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd --pool rbd --snap snap device unmap img
+ $ rbd device list
+
+ $ sudo rbd device map rbd/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd --pool rbd --image img --snap snap device unmap
+ $ rbd device list
+
+pool/img@snap, custom pool:
+
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@snap
+ $ rbd device list
+
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd --pool custom device unmap img@snap
+ $ rbd device list
+
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd --pool custom --snap snap device unmap img
+ $ rbd device list
+
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd --pool custom --image img --snap snap device unmap
+ $ rbd device list
+
+Not a mapped spec - random junk (which gets interpreted as a spec):
+
+ $ sudo rbd device unmap foobar
+ rbd: rbd/foobar: not a mapped image or snapshot
+ rbd: unmap failed: (22) Invalid argument
+ [22]
+
+ $ sudo rbd --image foobar device unmap
+ rbd: rbd/foobar: not a mapped image or snapshot
+ rbd: unmap failed: (22) Invalid argument
+ [22]
+
+Not a mapped spec - spec that's just been unmapped:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ sudo rbd device unmap img
+ rbd: rbd/img: not a mapped image or snapshot
+ rbd: unmap failed: (22) Invalid argument
+ [22]
+
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ sudo rbd device unmap img@snap
+ $ sudo rbd device unmap img@snap
+ rbd: rbd/img@snap: not a mapped image or snapshot
+ rbd: unmap failed: (22) Invalid argument
+ [22]
+
+Need an arg:
+
+ $ sudo rbd device unmap
+ rbd: unmap requires either image name or device path
+ [22]
+
+
+Two images
+==========
+
+Unmap img first:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ sudo rbd device map anotherimg
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ ? rbd anotherimg - /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd anotherimg - /dev/rbd? (glob)
+ $ sudo rbd device unmap anotherimg
+ $ rbd device list
+
+Unmap anotherimg first:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ sudo rbd device map anotherimg
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ ? rbd anotherimg - /dev/rbd? (glob)
+ $ sudo rbd device unmap anotherimg
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ rbd device list
+
+
+Image and its snap
+==================
+
+Unmap the image first:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap img@snap
+ $ rbd device list
+
+Unmap the snap first:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap img@snap
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ rbd device list
+
+
+Two snaps of the same image
+===========================
+
+Unmap snap first:
+
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ sudo rbd device map custom/img@anothersnap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ ? custom img anothersnap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@snap
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img anothersnap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@anothersnap
+ $ rbd device list
+
+Unmap anothersnap first:
+
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ sudo rbd device map custom/img@anothersnap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ ? custom img anothersnap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@anothersnap
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@snap
+ $ rbd device list
+
+
+Same img and snap in different pools
+====================================
+
+img:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ sudo rbd device map custom/img
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ ? custom img - /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img - /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img
+ $ rbd device list
+
+img@snap:
+
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@snap
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap img@snap
+ $ rbd device list
+
+
+Same spec mapped twice
+======================
+
+img:
+
+ $ sudo rbd device map img
+ /dev/rbd? (glob)
+ $ sudo rbd device map img
+ rbd: warning: image already mapped as /dev/rbd? (glob)
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ rbd: rbd/img: mapped more than once, unmapping /dev/rbd? only (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img - /dev/rbd? (glob)
+ $ sudo rbd device unmap img
+ $ rbd device list
+
+img@snap:
+
+ $ sudo rbd device map img@snap
+ /dev/rbd? (glob)
+ $ sudo rbd device map img@snap
+ rbd: warning: image already mapped as /dev/rbd? (glob)
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap img@snap
+ rbd: rbd/img@snap: mapped more than once, unmapping /dev/rbd? only (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap img@snap
+ $ rbd device list
+
+pool/img@snap, default pool:
+
+ $ sudo rbd device map rbd/img@snap
+ /dev/rbd? (glob)
+ $ sudo rbd device map rbd/img@snap
+ rbd: warning: image already mapped as /dev/rbd? (glob)
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap rbd/img@snap
+ rbd: rbd/img@snap: mapped more than once, unmapping /dev/rbd? only (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? rbd img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap rbd/img@snap
+ $ rbd device list
+
+pool/img@snap, custom pool:
+
+ $ sudo rbd device map custom/img@snap
+ /dev/rbd? (glob)
+ $ sudo rbd device map custom/img@snap
+ rbd: warning: image already mapped as /dev/rbd? (glob)
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@snap
+ rbd: custom/img@snap: mapped more than once, unmapping /dev/rbd? only (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? custom img snap /dev/rbd? (glob)
+ $ sudo rbd device unmap custom/img@snap
+ $ rbd device list
+
+
+Odd names
+=========
+
+ $ ceph osd pool create foo\* >/dev/null 2>&1
+ $ rbd pool init foo\*
+ $ rbd create --size 1 foo\*/[0.0.0.0]
+ $ rbd snap create foo\*/[0.0.0.0]@\?bar --no-progress
+ $ sudo rbd device map foo\*/[0.0.0.0]@\?bar
+ /dev/rbd? (glob)
+ $ rbd device list
+ id pool namespace image snap device
+ ? foo* [0.0.0.0] ?bar /dev/rbd? (glob)
+ $ sudo rbd device unmap foo\*/[0.0.0.0]@\?bar
+ $ rbd device list
+ $ ceph osd pool delete foo\* foo\* --yes-i-really-really-mean-it >/dev/null 2>&1
+
+
+Teardown
+========
+
+ $ ceph osd pool delete custom custom --yes-i-really-really-mean-it >/dev/null 2>&1
+ $ rbd snap purge anotherimg >/dev/null 2>&1
+ $ rbd rm anotherimg >/dev/null 2>&1
+ $ rbd snap purge img >/dev/null 2>&1
+ $ rbd rm img >/dev/null 2>&1
+
diff --git a/src/test/cli/.gitignore b/src/test/cli/.gitignore
new file mode 100644
index 000000000..d2bfb7e0d
--- /dev/null
+++ b/src/test/cli/.gitignore
@@ -0,0 +1 @@
+*.t.err
diff --git a/src/test/cli/ceph-authtool/add-key-segv.t b/src/test/cli/ceph-authtool/add-key-segv.t
new file mode 100644
index 000000000..6914593f3
--- /dev/null
+++ b/src/test/cli/ceph-authtool/add-key-segv.t
@@ -0,0 +1,6 @@
+ $ ceph-authtool kring --create-keyring --mode 0644
+ creating kring
+
+ $ ceph-authtool kring --add-key 'FAKEBASE64 foo'
+ can't decode key 'FAKEBASE64 foo'
+ [1]
diff --git a/src/test/cli/ceph-authtool/add-key.t b/src/test/cli/ceph-authtool/add-key.t
new file mode 100644
index 000000000..0da2365c0
--- /dev/null
+++ b/src/test/cli/ceph-authtool/add-key.t
@@ -0,0 +1,27 @@
+ $ ceph-authtool kring --create-keyring --mode 0644
+ creating kring
+
+ $ ceph-authtool kring --add-key 'AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ== 18446744073709551615'
+ added entity client.admin auth(key=AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ==)
+
+# cram makes matching escape-containing lines with regexps a bit ugly
+ $ ceph-authtool kring --list
+ [client.admin]
+ \tkey = AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ== (esc)
+
+ $ cat kring
+ [client.admin]
+ \tkey = AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ== (esc)
+
+Test --add-key with empty argument
+
+ $ ceph-authtool kring -C --name=mon.* --add-key= --cap mon 'allow *'
+ Option --add-key requires an argument
+ [1]
+
+ $ ceph-authtool test.keyring --create-keyring --mode 0644
+ creating test.keyring
+
+ $ ceph-authtool test.keyring --name client.test --cap osd 'allow rwx' --cap mon 'allow r' --add-key 'AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ== 18446744073709551615'
+ added entity client.test auth(key=AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ==)
+ added 2 caps to entity client.test
diff --git a/src/test/cli/ceph-authtool/cap-bin.t b/src/test/cli/ceph-authtool/cap-bin.t
new file mode 100644
index 000000000..7a6383b8b
--- /dev/null
+++ b/src/test/cli/ceph-authtool/cap-bin.t
@@ -0,0 +1,6 @@
+ $ ceph-authtool kring --create-keyring --gen-key --mode 0644
+ creating kring
+
+ $ ceph-authtool --cap osd 'allow rx pool=swimming' kring
+ $ ceph-authtool kring --list|grep -E '^[[:space:]]caps '
+ \tcaps osd = "allow rx pool=swimming" (esc)
diff --git a/src/test/cli/ceph-authtool/cap-invalid.t b/src/test/cli/ceph-authtool/cap-invalid.t
new file mode 100644
index 000000000..d72f34fed
--- /dev/null
+++ b/src/test/cli/ceph-authtool/cap-invalid.t
@@ -0,0 +1,12 @@
+ $ ceph-authtool kring --create-keyring --gen-key --mode 0644
+ creating kring
+
+# TODO is this nice?
+ $ ceph-authtool --cap osd 'broken' kring
+ $ ceph-authtool kring --list|grep -E '^[[:space:]]caps '
+ \tcaps osd = "broken" (esc)
+
+# TODO is this nice?
+ $ ceph-authtool --cap xyzzy 'broken' kring
+ $ ceph-authtool kring --list|grep -E '^[[:space:]]caps '
+ \tcaps xyzzy = "broken" (esc)
diff --git a/src/test/cli/ceph-authtool/cap-overwrite.t b/src/test/cli/ceph-authtool/cap-overwrite.t
new file mode 100644
index 000000000..9bc5b07ea
--- /dev/null
+++ b/src/test/cli/ceph-authtool/cap-overwrite.t
@@ -0,0 +1,11 @@
+ $ ceph-authtool kring --create-keyring --gen-key --mode 0644
+ creating kring
+
+ $ ceph-authtool --cap osd 'allow rx pool=swimming' kring
+ $ ceph-authtool kring --list|grep -E '^[[:space:]]caps '
+ \tcaps osd = "allow rx pool=swimming" (esc)
+
+# TODO it seems --cap overwrites all previous caps; is this wanted?
+ $ ceph-authtool --cap mds 'allow' kring
+ $ ceph-authtool kring --list|grep -E '^[[:space:]]caps '
+ \tcaps mds = "allow" (esc)
diff --git a/src/test/cli/ceph-authtool/cap.t b/src/test/cli/ceph-authtool/cap.t
new file mode 100644
index 000000000..b9748d9af
--- /dev/null
+++ b/src/test/cli/ceph-authtool/cap.t
@@ -0,0 +1,11 @@
+ $ ceph-authtool kring --create-keyring --gen-key --mode 0644
+ creating kring
+
+ $ ceph-authtool --cap osd 'allow rx pool=swimming' kring
+ $ ceph-authtool kring --list|grep -E '^[[:space:]]caps '
+ \tcaps osd = "allow rx pool=swimming" (esc)
+
+ $ cat kring
+ [client.admin]
+ \\tkey = [a-zA-Z0-9+/]+=* \(esc\) (re)
+ \tcaps osd = "allow rx pool=swimming" (esc)
diff --git a/src/test/cli/ceph-authtool/create-gen-list-bin.t b/src/test/cli/ceph-authtool/create-gen-list-bin.t
new file mode 100644
index 000000000..1d4925af5
--- /dev/null
+++ b/src/test/cli/ceph-authtool/create-gen-list-bin.t
@@ -0,0 +1,16 @@
+ $ ceph-authtool kring --create-keyring --mode 0600
+ creating kring
+
+ $ ceph-authtool kring --list
+
+ $ ceph-authtool kring --gen-key
+
+# cram makes matching escape-containing lines with regexps a bit ugly
+ $ ceph-authtool kring --list
+ [client.admin]
+ \\tkey = [a-zA-Z0-9+/]+=* \(esc\) (re)
+
+# synonym
+ $ ceph-authtool kring -l
+ [client.admin]
+ \\tkey = [a-zA-Z0-9+/]+=* \(esc\) (re)
diff --git a/src/test/cli/ceph-authtool/create-gen-list.t b/src/test/cli/ceph-authtool/create-gen-list.t
new file mode 100644
index 000000000..2801b0502
--- /dev/null
+++ b/src/test/cli/ceph-authtool/create-gen-list.t
@@ -0,0 +1,20 @@
+ $ ceph-authtool kring --create-keyring --mode 0644
+ creating kring
+
+ $ ceph-authtool kring --list
+
+ $ ceph-authtool kring --gen-key
+
+# cram makes matching escape-containing lines with regexps a bit ugly
+ $ ceph-authtool kring --list
+ [client.admin]
+ \\tkey = [a-zA-Z0-9+/]+=* \(esc\) (re)
+
+# synonym
+ $ ceph-authtool kring -l
+ [client.admin]
+ \\tkey = [a-zA-Z0-9+/]+=* \(esc\) (re)
+
+ $ cat kring
+ [client.admin]
+ \\tkey = [a-zA-Z0-9+/]+=* \(esc\) (re)
diff --git a/src/test/cli/ceph-authtool/help.t b/src/test/cli/ceph-authtool/help.t
new file mode 100644
index 000000000..68f4a9699
--- /dev/null
+++ b/src/test/cli/ceph-authtool/help.t
@@ -0,0 +1,25 @@
+# TODO synchronize with man page
+ $ ceph-authtool --help
+ usage: ceph-authtool keyringfile [OPTIONS]...
+ where the options are:
+ -l, --list will list all keys and capabilities present in
+ the keyring
+ -p, --print-key will print an encoded key for the specified
+ entityname. This is suitable for the
+ 'mount -o secret=..' argument
+ -C, --create-keyring will create a new keyring, overwriting any
+ existing keyringfile
+ -g, --gen-key will generate a new secret key for the
+ specified entityname
+ --gen-print-key will generate a new secret key without set it
+ to the keyringfile, prints the secret to stdout
+ --import-keyring FILE will import the content of a given keyring
+ into the keyringfile
+ -n NAME, --name NAME specify entityname to operate on
+ -a BASE64, --add-key BASE64 will add an encoded key to the keyring
+ --cap SUBSYSTEM CAPABILITY will set the capability for given subsystem
+ --caps CAPSFILE will set all of capabilities associated with a
+ given key, for all subsystems
+ --mode MODE will set the desired file mode to the keyring
+ e.g: '0644', defaults to '0600'
+ [1]
diff --git a/src/test/cli/ceph-authtool/list-empty-bin.t b/src/test/cli/ceph-authtool/list-empty-bin.t
new file mode 100644
index 000000000..1b465fe65
--- /dev/null
+++ b/src/test/cli/ceph-authtool/list-empty-bin.t
@@ -0,0 +1,5 @@
+ $ touch empty
+
+ $ ceph-authtool --list empty
+
+ $ ceph-authtool -l empty
diff --git a/src/test/cli/ceph-authtool/list-empty.t b/src/test/cli/ceph-authtool/list-empty.t
new file mode 100644
index 000000000..1b465fe65
--- /dev/null
+++ b/src/test/cli/ceph-authtool/list-empty.t
@@ -0,0 +1,5 @@
+ $ touch empty
+
+ $ ceph-authtool --list empty
+
+ $ ceph-authtool -l empty
diff --git a/src/test/cli/ceph-authtool/list-nonexistent-bin.t b/src/test/cli/ceph-authtool/list-nonexistent-bin.t
new file mode 100644
index 000000000..4aecd3e27
--- /dev/null
+++ b/src/test/cli/ceph-authtool/list-nonexistent-bin.t
@@ -0,0 +1,7 @@
+ $ ceph-authtool --list nonexistent
+ can't open nonexistent: can't open nonexistent: (2) No such file or directory
+ [1]
+
+ $ ceph-authtool -l nonexistent
+ can't open nonexistent: can't open nonexistent: (2) No such file or directory
+ [1]
diff --git a/src/test/cli/ceph-authtool/list-nonexistent.t b/src/test/cli/ceph-authtool/list-nonexistent.t
new file mode 100644
index 000000000..4aecd3e27
--- /dev/null
+++ b/src/test/cli/ceph-authtool/list-nonexistent.t
@@ -0,0 +1,7 @@
+ $ ceph-authtool --list nonexistent
+ can't open nonexistent: can't open nonexistent: (2) No such file or directory
+ [1]
+
+ $ ceph-authtool -l nonexistent
+ can't open nonexistent: can't open nonexistent: (2) No such file or directory
+ [1]
diff --git a/src/test/cli/ceph-authtool/manpage.t b/src/test/cli/ceph-authtool/manpage.t
new file mode 100644
index 000000000..3201aa37b
--- /dev/null
+++ b/src/test/cli/ceph-authtool/manpage.t
@@ -0,0 +1,32 @@
+ $ ceph-authtool
+ ceph-authtool: -h or --help for usage
+ [1]
+
+# demonstrate that manpage examples fail without config
+# TODO fix the manpage
+ $ ceph-authtool --create-keyring --name client.foo --gen-key keyring
+ creating keyring
+
+# work around the above
+ $ touch ceph.conf
+
+To create a new keyring containing a key for client.foo:
+
+ $ ceph-authtool --create-keyring --id foo --gen-key keyring
+ creating keyring
+
+ $ ceph-authtool --create-keyring --name client.foo --gen-key keyring
+ creating keyring
+
+To associate some capabilities with the key (namely, the ability to mount a Ceph filesystem):
+
+ $ ceph-authtool -n client.foo --cap mds 'allow' --cap osd 'allow rw pool=data' --cap mon 'allow r' keyring
+
+To display the contents of the keyring:
+
+ $ ceph-authtool -l keyring
+ [client.foo]
+ \\tkey = [a-zA-Z0-9+/]+=* \(esc\) (re)
+ \tcaps mds = "allow" (esc)
+ \tcaps mon = "allow r" (esc)
+ \tcaps osd = "allow rw pool=data" (esc)
diff --git a/src/test/cli/ceph-authtool/simple.t b/src/test/cli/ceph-authtool/simple.t
new file mode 100644
index 000000000..bf3fc1036
--- /dev/null
+++ b/src/test/cli/ceph-authtool/simple.t
@@ -0,0 +1,3 @@
+ $ ceph-authtool
+ ceph-authtool: -h or --help for usage
+ [1]
diff --git a/src/test/cli/ceph-conf/env-vs-args.t b/src/test/cli/ceph-conf/env-vs-args.t
new file mode 100644
index 000000000..edb42ad6d
--- /dev/null
+++ b/src/test/cli/ceph-conf/env-vs-args.t
@@ -0,0 +1,14 @@
+# we can use CEPH_CONF to override the normal configuration file location.
+ $ env CEPH_CONF=from-env ceph-conf -s foo bar
+ did not load config file, using default settings.
+ .* \-1 Errors while parsing config file! (re)
+ .* \-1 can't open from-env: \(2\) (No such file or directory)? (re)
+ .* \-1 Errors while parsing config file! (re)
+ .* \-1 can't open from-env: \(2\) (No such file or directory)? (re)
+ [1]
+
+# command-line arguments should override environment
+ $ env -u CEPH_CONF ceph-conf -c from-args
+ global_init: unable to open config file from search list from-args
+ [1]
+
diff --git a/src/test/cli/ceph-conf/help.t b/src/test/cli/ceph-conf/help.t
new file mode 100644
index 000000000..751b3a54a
--- /dev/null
+++ b/src/test/cli/ceph-conf/help.t
@@ -0,0 +1,44 @@
+ $ ceph-conf --help
+ Ceph configuration query tool
+
+ USAGE
+ ceph-conf <flags> <action>
+
+ ACTIONS
+ -L|--list-all-sections List all sections
+ -l|--list-sections <prefix> List sections with the given prefix
+ --filter-key <key> Filter section list to only include sections
+ with given key defined.
+ --filter-key-value <key>=<val> Filter section list to only include sections
+ with given key/value pair.
+ --lookup <key> Print a configuration setting to stdout.
+ Returns 0 (success) if the configuration setting is
+ found; 1 otherwise.
+ -r|--resolve-search search for the first file that exists and
+ can be opened in the resulted comma
+ delimited search list.
+ -D|--dump-all dump all variables.
+ --show-config-value <key> Print the corresponding ceph.conf value
+ that matches the specified key. Also searches
+ global defaults.
+
+ FLAGS
+ --name name Set type.id
+ [-s <section>] Add to list of sections to search
+ [--format plain|json|json-pretty]
+ dump variables in plain text, json or pretty
+ json
+ [--pid <pid>] Override the $pid when expanding options
+
+ If there is no action given, the action will default to --lookup.
+
+ EXAMPLES
+ [$] ceph-conf --name mon.0 -c /etc/ceph/ceph.conf 'mon addr' (re)
+ Find out what the value of 'mon addr' is for monitor 0.
+
+ [$] ceph-conf -l mon (re)
+ List sections beginning with 'mon'.
+
+ RETURN CODE
+ Return code will be 0 on success; error code otherwise.
+
diff --git a/src/test/cli/ceph-conf/invalid-args.t b/src/test/cli/ceph-conf/invalid-args.t
new file mode 100644
index 000000000..43cedf2da
--- /dev/null
+++ b/src/test/cli/ceph-conf/invalid-args.t
@@ -0,0 +1,17 @@
+ $ cat >test.conf <<EOF
+ > [bar]
+ > bar = green
+ > EOF
+
+# TODO output an error
+ $ ceph-conf -c test.conf broken
+ [1]
+
+ $ ceph-conf -c test.conf --name total.garbage
+ error parsing 'total.garbage': expected string of the form TYPE.ID, valid types are: auth, mon, osd, mds, mgr, client
+ [1]
+
+ $ ceph-conf -c test.conf -s bar
+ You must give an action, such as --lookup or --list-all-sections.
+ Pass --help for more help.
+ [1]
diff --git a/src/test/cli/ceph-conf/manpage.t b/src/test/cli/ceph-conf/manpage.t
new file mode 100644
index 000000000..425f27189
--- /dev/null
+++ b/src/test/cli/ceph-conf/manpage.t
@@ -0,0 +1,33 @@
+# setup
+ $ cat >foo.conf <<'EOF'
+ > ; ---------------------
+ > [group cephnet]
+ > addr = 10.3.14.0/24
+ >
+ > [global]
+ > pid file = /home/sage/ceph/src/out/$name.pid
+ >
+ > [osd]
+ > osd data = /mnt/osd$id
+ > [osd.3]
+ > host = cosd3
+ > EOF
+
+To extract the value of the "osd data" option for the osd0 daemon,
+
+ $ ceph-conf -c foo.conf "osd data" --name osd.0
+ /mnt/osd0
+
+This is equivalent to doing specifying sections [osd0], [osd.0],
+[osd], or [global], in that order of preference:
+
+# TODO the "admin" here seems like an actual bug
+
+ $ ceph-conf -c foo.conf "osd data" -s osd0 -s osd.0 -s osd -s global
+ /mnt/osdadmin
+
+To list all sections that begin with osd:
+
+ $ ceph-conf -c foo.conf -l osd
+ osd
+ osd.3
diff --git a/src/test/cli/ceph-conf/option.t b/src/test/cli/ceph-conf/option.t
new file mode 100644
index 000000000..194488438
--- /dev/null
+++ b/src/test/cli/ceph-conf/option.t
@@ -0,0 +1,64 @@
+ $ cat >test.conf <<EOF
+ > [bar]
+ > bar = green
+ > [foo]
+ > bar = blue
+ > [baz]
+ > bar = yellow
+ > [thud]
+ > bar = red
+ > [nobar]
+ > other = 42
+ > EOF
+
+ $ ceph-conf -c test.conf bar -s foo
+ blue
+
+# test the funny "equals sign" argument passing convention
+ $ ceph-conf --conf=test.conf bar -s foo
+ blue
+
+ $ ceph-conf --conf=test.conf -L
+ bar
+ baz
+ foo
+ nobar
+ thud
+
+ $ ceph-conf --conf=test.conf --list-all-sections
+ bar
+ baz
+ foo
+ nobar
+ thud
+
+ $ ceph-conf --conf=test.conf --list_all_sections
+ bar
+ baz
+ foo
+ nobar
+ thud
+
+# TODO man page stops in the middle of a sentence
+
+ $ ceph-conf -c test.conf bar -s xyzzy
+ [1]
+
+ $ ceph-conf -c test.conf bar -s xyzzy
+ [1]
+
+ $ ceph-conf -c test.conf bar -s xyzzy -s thud
+ red
+
+ $ ceph-conf -c test.conf bar -s nobar -s thud
+ red
+
+ $ ceph-conf -c test.conf bar -s thud -s baz
+ red
+
+ $ ceph-conf -c test.conf bar -s baz -s thud
+ yellow
+
+ $ ceph-conf -c test.conf bar -s xyzzy -s nobar -s thud -s baz
+ red
+
diff --git a/src/test/cli/ceph-conf/sections.t b/src/test/cli/ceph-conf/sections.t
new file mode 100644
index 000000000..63063cc9e
--- /dev/null
+++ b/src/test/cli/ceph-conf/sections.t
@@ -0,0 +1,18 @@
+ $ cat >test.conf <<EOF
+ > [bar]
+ > bar = green
+ > [foo]
+ > bar = blue
+ > [baz]
+ > bar = yellow
+ > [thud]
+ > bar = yellow
+ > EOF
+
+ $ ceph-conf -c test.conf -l bar
+ bar
+
+ $ ceph-conf -c test.conf -l b
+ bar
+ baz
+
diff --git a/src/test/cli/ceph-conf/show-config-value.t b/src/test/cli/ceph-conf/show-config-value.t
new file mode 100644
index 000000000..2e0528e93
--- /dev/null
+++ b/src/test/cli/ceph-conf/show-config-value.t
@@ -0,0 +1,39 @@
+
+# should reflect daemon defaults
+
+ $ ceph-conf -n osd.0 --show-config-value log_file -c /dev/null
+ /var/log/ceph/ceph-osd.0.log
+ $ CEPH_ARGS="--fsid 96a3abe6-7552-4635-a79b-f3c096ff8b95" ceph-conf -n osd.0 --show-config-value fsid -c /dev/null
+ 96a3abe6-7552-4635-a79b-f3c096ff8b95
+ $ ceph-conf -n osd.0 --show-config-value INVALID -c /dev/null
+ failed to get config option 'INVALID': option not found
+ [1]
+
+ $ cat > $TESTDIR/ceph.conf <<EOF
+ > [global]
+ > mon_host = \$public_network
+ > public_network = \$mon_host
+ > EOF
+ $ ceph-conf --show-config-value mon_host -c $TESTDIR/ceph.conf
+ variable expansion loop at mon_host=$public_network
+ expansion stack:
+ public_network=$mon_host
+ mon_host=$public_network
+ $mon_host
+ $ rm $TESTDIR/ceph.conf
+
+Name option test to strip the PID
+=================================
+ $ cat > $TESTDIR/ceph.conf <<EOF
+ > [client]
+ > admin socket = \$name.\$pid.asok
+ > [global]
+ > admin socket = \$name.asok
+ > EOF
+ $ ceph-conf --name client.admin --pid 133423 --show-config-value admin_socket -c $TESTDIR/ceph.conf
+ client.admin.133423.asok
+ $ ceph-conf --name mds.a --show-config-value admin_socket -c $TESTDIR/ceph.conf
+ mds.a.asok
+ $ ceph-conf --name osd.0 --show-config-value admin_socket -c $TESTDIR/ceph.conf
+ osd.0.asok
+ $ rm $TESTDIR/ceph.conf
diff --git a/src/test/cli/ceph-conf/show-config.t b/src/test/cli/ceph-conf/show-config.t
new file mode 100644
index 000000000..45405e4b7
--- /dev/null
+++ b/src/test/cli/ceph-conf/show-config.t
@@ -0,0 +1,7 @@
+ $ ceph-conf -n osd.0 --show-config -c /dev/null | grep ceph-osd
+ admin_socket = /var/run/ceph/ceph-osd.0.asok
+ log_file = /var/log/ceph/ceph-osd.0.log
+ mon_debug_dump_location = /var/log/ceph/ceph-osd.0.tdump
+ rgw_ops_log_file_path = /var/log/ceph/ops-log-ceph-osd.0.log
+ $ CEPH_ARGS="--fsid 96a3abe6-7552-4635-a79b-f3c096ff8b95" ceph-conf -n osd.0 --show-config -c /dev/null | grep fsid
+ fsid = 96a3abe6-7552-4635-a79b-f3c096ff8b95
diff --git a/src/test/cli/ceph-conf/simple.t b/src/test/cli/ceph-conf/simple.t
new file mode 100644
index 000000000..043ca2035
--- /dev/null
+++ b/src/test/cli/ceph-conf/simple.t
@@ -0,0 +1,4 @@
+ $ ceph-conf
+ You must give an action, such as --lookup or --list-all-sections.
+ Pass --help for more help.
+ [1]
diff --git a/src/test/cli/ceph-kvstore-tool/help.t b/src/test/cli/ceph-kvstore-tool/help.t
new file mode 100644
index 000000000..e86c7cd23
--- /dev/null
+++ b/src/test/cli/ceph-kvstore-tool/help.t
@@ -0,0 +1,23 @@
+ $ ceph-kvstore-tool --help
+ Usage: ceph-kvstore-tool <leveldb|rocksdb|bluestore-kv> <store path> command [args...]
+
+ Commands:
+ list [prefix]
+ list-crc [prefix]
+ dump [prefix]
+ exists <prefix> [key]
+ get <prefix> <key> [out <file>]
+ crc <prefix> <key>
+ get-size [<prefix> <key>]
+ set <prefix> <key> [ver <N>|in <file>]
+ rm <prefix> <key>
+ rm-prefix <prefix>
+ store-copy <path> [num-keys-per-tx] [leveldb|rocksdb|...]
+ store-crc <path>
+ compact
+ compact-prefix <prefix>
+ compact-range <prefix> <start> <end>
+ destructive-repair (use only as last resort! may corrupt healthy data)
+ stats
+ histogram [prefix]
+
diff --git a/src/test/cli/crushtool/add-bucket.t b/src/test/cli/crushtool/add-bucket.t
new file mode 100644
index 000000000..f5e215be1
--- /dev/null
+++ b/src/test/cli/crushtool/add-bucket.t
@@ -0,0 +1,63 @@
+ $ crushtool -i "$TESTDIR/simple.template" --add-bucket host0 host --loc cluster cluster0 -o map0 > /dev/null
+ $ crushtool -i map0 --add-bucket host1 host -o map1 > /dev/null
+ $ crushtool -i map1 --move host1 --loc cluster cluster0 -o map2 > /dev/null
+ $ crushtool -i map2 --add-item 1 1.0 device1 --loc cluster cluster0 -o map3 > /dev/null
+ $ crushtool -i map3 --move device1 --loc host host0 -o map4 > /dev/null
+ $ crushtool -d map4
+ # begin crush map
+
+ # devices
+ device 1 device1
+
+ # types
+ type 0 device
+ type 1 host
+ type 2 cluster
+
+ # buckets
+ host host0 {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \t# weight 1.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem device1 weight 1.00000 (esc)
+ }
+ host host1 {
+ \tid -3\t\t# do not change unnecessarily (esc)
+ \t# weight 0.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ }
+ cluster cluster0 {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \t# weight 1.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem host0 weight 1.00000 (esc)
+ \titem host1 weight 0.00000 (esc)
+ }
+
+ # rules
+ rule data {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule metadata {
+ \tid 1 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule rbd {
+ \tid 2 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
diff --git a/src/test/cli/crushtool/add-item-in-tree.t b/src/test/cli/crushtool/add-item-in-tree.t
new file mode 100644
index 000000000..4689efc6e
--- /dev/null
+++ b/src/test/cli/crushtool/add-item-in-tree.t
@@ -0,0 +1,10 @@
+ $ crushtool -i "$TESTDIR/tree.template" --add-item 0 1.0 device0 --loc host host0 --loc cluster cluster0 -o one > /dev/null
+ $ crushtool -i one --add-item 1 1.0 device1 --loc host host0 --loc cluster cluster0 -o two > /dev/null
+ $ crushtool -i two --add-item 2 1.0 device2 --loc host host0 --loc cluster cluster0 -o tree > /dev/null
+ $ crushtool -i tree --add-item 3 1.0 device3 --loc host host0 --loc cluster cluster0 -o four > /dev/null
+ $ crushtool -i four --add-item 4 1.0 device4 --loc host host0 --loc cluster cluster0 -o five > /dev/null
+ $ crushtool -i five --add-item 5 1.0 device5 --loc host host0 --loc cluster cluster0 -o six > /dev/null
+ $ crushtool -i six --add-item 6 1.0 device6 --loc host host0 --loc cluster cluster0 -o seven > /dev/null
+ $ crushtool -i seven --add-item 7 1.0 device7 --loc host host0 --loc cluster cluster0 -o eight > /dev/null
+ $ crushtool -d eight -o final
+ $ diff final "$TESTDIR/tree.template.final"
diff --git a/src/test/cli/crushtool/add-item.t b/src/test/cli/crushtool/add-item.t
new file mode 100644
index 000000000..dc056d005
--- /dev/null
+++ b/src/test/cli/crushtool/add-item.t
@@ -0,0 +1,133 @@
+ $ crushtool -i "$TESTDIR/simple.template" --add-item 0 1.0 device0 --loc host host0 --loc cluster cluster0 -o one > /dev/null
+ $ crushtool -i one --add-item 1 1.0 device1 --loc host host0 --loc cluster cluster0 -o two > /dev/null
+ $ crushtool -i two --create-simple-rule simple-rule cluster0 host firstn -o two > /dev/null
+ $ crushtool -d two
+ # begin crush map
+
+ # devices
+ device 0 device0
+ device 1 device1
+
+ # types
+ type 0 device
+ type 1 host
+ type 2 cluster
+
+ # buckets
+ host host0 {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \t# weight 2.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem device0 weight 1.00000 (esc)
+ \titem device1 weight 1.00000 (esc)
+ }
+ cluster cluster0 {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \t# weight 2.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem host0 weight 2.00000 (esc)
+ }
+
+ # rules
+ rule data {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule metadata {
+ \tid 1 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule rbd {
+ \tid 2 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule simple-rule {
+ \tid 3 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+ $ crushtool -i two --remove-rule simple-rule -o two > /dev/null
+ $ crushtool -d two
+ # begin crush map
+
+ # devices
+ device 0 device0
+ device 1 device1
+
+ # types
+ type 0 device
+ type 1 host
+ type 2 cluster
+
+ # buckets
+ host host0 {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \t# weight 2.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem device0 weight 1.00000 (esc)
+ \titem device1 weight 1.00000 (esc)
+ }
+ cluster cluster0 {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \t# weight 2.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem host0 weight 2.00000 (esc)
+ }
+
+ # rules
+ rule data {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule metadata {
+ \tid 1 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule rbd {
+ \tid 2 (esc)
+ \ttype replicated (esc)
+ \tstep take cluster0 (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+ $ crushtool -d two -o final
+ $ diff final "$TESTDIR/simple.template.two"
+ $ crushtool -i two --add-item 1 1.0 device1 --loc host host0 --loc cluster cluster0 -o three 2>/dev/null >/dev/null || echo FAIL
+ FAIL
+ $ crushtool -i two --remove-item device1 -o four > /dev/null
+ $ crushtool -d four -o final
+ $ diff final "$TESTDIR/simple.template.four"
+ $ crushtool -i two --update-item 1 2.0 osd1 --loc host host1 --loc cluster cluster0 -o five > /dev/null
+ $ crushtool -d five -o final
+ $ diff final "$TESTDIR/simple.template.five"
+ $ crushtool -i five --update-item 1 2.0 osd1 --loc host host1 --loc cluster cluster0 -o six > /dev/null
+ $ crushtool -i five --show-location 1
+ cluster\tcluster0 (esc)
+ host\thost1 (esc)
+ $ crushtool -d six -o final
+ $ diff final "$TESTDIR/simple.template.five"
diff --git a/src/test/cli/crushtool/adjust-item-weight.t b/src/test/cli/crushtool/adjust-item-weight.t
new file mode 100644
index 000000000..a6f2161b9
--- /dev/null
+++ b/src/test/cli/crushtool/adjust-item-weight.t
@@ -0,0 +1,17 @@
+ $ crushtool -i "$TESTDIR/simple.template" --add-item 0 1.0 device0 --loc host host0 --loc cluster cluster0 -o one > /dev/null
+
+#
+# add device0 into host=fake, the weight of device0 in host=host0 is 1.0, the weight of device0 in host=fake is 2.0
+#
+
+ $ crushtool -i one --add-item 0 2.0 device0 --loc host fake --loc cluster cluster0 -o two > /dev/null
+ $ crushtool -d two -o final
+ $ diff final "$TESTDIR/simple.template.adj.two"
+
+#
+# update the weight of device0 in host=host0, it will not affect the weight of device0 in host=fake
+#
+
+ $ crushtool -i two --update-item 0 3.0 device0 --loc host host0 --loc cluster cluster0 -o three > /dev/null
+ $ crushtool -d three -o final
+ $ diff final "$TESTDIR/simple.template.adj.three"
diff --git a/src/test/cli/crushtool/arg-order-checks.t b/src/test/cli/crushtool/arg-order-checks.t
new file mode 100644
index 000000000..2182f87e7
--- /dev/null
+++ b/src/test/cli/crushtool/arg-order-checks.t
@@ -0,0 +1,730 @@
+# tunables before decompile
+ $ crushtool -d "$TESTDIR/simple.template" --set-straw-calc-version 1 | head -2
+ # begin crush map
+ tunable straw_calc_version 1
+# build then reweight-item then tree
+ $ map="$TESTDIR/foo"
+ $ crushtool --outfn "$map" --build --set-chooseleaf-vary-r 0 --set-chooseleaf-stable 0 --num_osds 25 node straw 5 rack straw 1 root straw 0 --reweight-item osd.2 99 -o "$map" --tree
+ crushtool reweighting item osd.2 to 99
+ ID CLASS WEIGHT TYPE NAME
+ -11 123.00000 root root
+ -6 103.00000 rack rack0
+ -1 103.00000 node node0
+ 0 1.00000 osd.0
+ 1 1.00000 osd.1
+ 2 99.00000 osd.2
+ 3 1.00000 osd.3
+ 4 1.00000 osd.4
+ -7 5.00000 rack rack1
+ -2 5.00000 node node1
+ 5 1.00000 osd.5
+ 6 1.00000 osd.6
+ 7 1.00000 osd.7
+ 8 1.00000 osd.8
+ 9 1.00000 osd.9
+ -8 5.00000 rack rack2
+ -3 5.00000 node node2
+ 10 1.00000 osd.10
+ 11 1.00000 osd.11
+ 12 1.00000 osd.12
+ 13 1.00000 osd.13
+ 14 1.00000 osd.14
+ -9 5.00000 rack rack3
+ -4 5.00000 node node3
+ 15 1.00000 osd.15
+ 16 1.00000 osd.16
+ 17 1.00000 osd.17
+ 18 1.00000 osd.18
+ 19 1.00000 osd.19
+ -10 5.00000 rack rack4
+ -5 5.00000 node node4
+ 20 1.00000 osd.20
+ 21 1.00000 osd.21
+ 22 1.00000 osd.22
+ 23 1.00000 osd.23
+ 24 1.00000 osd.24
+ $ crushtool -d "$map"
+ # begin crush map
+ tunable choose_local_tries 0
+ tunable choose_local_fallback_tries 0
+ tunable choose_total_tries 50
+ tunable chooseleaf_descend_once 1
+ tunable straw_calc_version 1
+ tunable allowed_bucket_algs 54
+
+ # devices
+ device 0 osd.0
+ device 1 osd.1
+ device 2 osd.2
+ device 3 osd.3
+ device 4 osd.4
+ device 5 osd.5
+ device 6 osd.6
+ device 7 osd.7
+ device 8 osd.8
+ device 9 osd.9
+ device 10 osd.10
+ device 11 osd.11
+ device 12 osd.12
+ device 13 osd.13
+ device 14 osd.14
+ device 15 osd.15
+ device 16 osd.16
+ device 17 osd.17
+ device 18 osd.18
+ device 19 osd.19
+ device 20 osd.20
+ device 21 osd.21
+ device 22 osd.22
+ device 23 osd.23
+ device 24 osd.24
+
+ # types
+ type 0 osd
+ type 1 node
+ type 2 rack
+ type 3 root
+
+ # buckets
+ node node0 {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \t# weight 103.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.0 weight 1.00000 (esc)
+ \titem osd.1 weight 1.00000 (esc)
+ \titem osd.2 weight 99.00000 (esc)
+ \titem osd.3 weight 1.00000 (esc)
+ \titem osd.4 weight 1.00000 (esc)
+ }
+ node node1 {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.5 weight 1.00000 (esc)
+ \titem osd.6 weight 1.00000 (esc)
+ \titem osd.7 weight 1.00000 (esc)
+ \titem osd.8 weight 1.00000 (esc)
+ \titem osd.9 weight 1.00000 (esc)
+ }
+ node node2 {
+ \tid -3\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.10 weight 1.00000 (esc)
+ \titem osd.11 weight 1.00000 (esc)
+ \titem osd.12 weight 1.00000 (esc)
+ \titem osd.13 weight 1.00000 (esc)
+ \titem osd.14 weight 1.00000 (esc)
+ }
+ node node3 {
+ \tid -4\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.15 weight 1.00000 (esc)
+ \titem osd.16 weight 1.00000 (esc)
+ \titem osd.17 weight 1.00000 (esc)
+ \titem osd.18 weight 1.00000 (esc)
+ \titem osd.19 weight 1.00000 (esc)
+ }
+ node node4 {
+ \tid -5\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.20 weight 1.00000 (esc)
+ \titem osd.21 weight 1.00000 (esc)
+ \titem osd.22 weight 1.00000 (esc)
+ \titem osd.23 weight 1.00000 (esc)
+ \titem osd.24 weight 1.00000 (esc)
+ }
+ rack rack0 {
+ \tid -6\t\t# do not change unnecessarily (esc)
+ \t# weight 103.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem node0 weight 103.00000 (esc)
+ }
+ rack rack1 {
+ \tid -7\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem node1 weight 5.00000 (esc)
+ }
+ rack rack2 {
+ \tid -8\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem node2 weight 5.00000 (esc)
+ }
+ rack rack3 {
+ \tid -9\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem node3 weight 5.00000 (esc)
+ }
+ rack rack4 {
+ \tid -10\t\t# do not change unnecessarily (esc)
+ \t# weight 5.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem node4 weight 5.00000 (esc)
+ }
+ root root {
+ \tid -11\t\t# do not change unnecessarily (esc)
+ \t# weight 123.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem rack0 weight 103.00000 (esc)
+ \titem rack1 weight 5.00000 (esc)
+ \titem rack2 weight 5.00000 (esc)
+ \titem rack3 weight 5.00000 (esc)
+ \titem rack4 weight 5.00000 (esc)
+ }
+
+ # rules
+ rule replicated_rule {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take root (esc)
+ \tstep chooseleaf firstn 0 type node (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+# tunables before reweight
+ $ crushtool -i "$map" --set-straw-calc-version 0 --reweight --test --show-utilization --max-x 100 --min-x 1 --min-rep 1 --max-rep 10
+ rule 0 (replicated_rule), x = 1..100, numrep = 1..10
+ rule 0 (replicated_rule) num_rep 1 result size == 1:\t100/100 (esc)
+ device 0:\t\t stored : 4\t expected : 4 (esc)
+ device 1:\t\t stored : 4\t expected : 4 (esc)
+ device 2:\t\t stored : 40\t expected : 4 (esc)
+ device 3:\t\t stored : 6\t expected : 4 (esc)
+ device 4:\t\t stored : 1\t expected : 4 (esc)
+ device 5:\t\t stored : 2\t expected : 4 (esc)
+ device 7:\t\t stored : 2\t expected : 4 (esc)
+ device 8:\t\t stored : 3\t expected : 4 (esc)
+ device 9:\t\t stored : 4\t expected : 4 (esc)
+ device 12:\t\t stored : 2\t expected : 4 (esc)
+ device 13:\t\t stored : 1\t expected : 4 (esc)
+ device 14:\t\t stored : 4\t expected : 4 (esc)
+ device 15:\t\t stored : 2\t expected : 4 (esc)
+ device 16:\t\t stored : 5\t expected : 4 (esc)
+ device 17:\t\t stored : 3\t expected : 4 (esc)
+ device 19:\t\t stored : 5\t expected : 4 (esc)
+ device 20:\t\t stored : 5\t expected : 4 (esc)
+ device 21:\t\t stored : 1\t expected : 4 (esc)
+ device 22:\t\t stored : 2\t expected : 4 (esc)
+ device 23:\t\t stored : 2\t expected : 4 (esc)
+ device 24:\t\t stored : 2\t expected : 4 (esc)
+ rule 0 (replicated_rule) num_rep 2 result size == 2:\t100/100 (esc)
+ device 0:\t\t stored : 6\t expected : 8 (esc)
+ device 1:\t\t stored : 6\t expected : 8 (esc)
+ device 2:\t\t stored : 60\t expected : 8 (esc)
+ device 3:\t\t stored : 6\t expected : 8 (esc)
+ device 4:\t\t stored : 6\t expected : 8 (esc)
+ device 5:\t\t stored : 4\t expected : 8 (esc)
+ device 6:\t\t stored : 2\t expected : 8 (esc)
+ device 7:\t\t stored : 4\t expected : 8 (esc)
+ device 8:\t\t stored : 5\t expected : 8 (esc)
+ device 9:\t\t stored : 10\t expected : 8 (esc)
+ device 10:\t\t stored : 3\t expected : 8 (esc)
+ device 11:\t\t stored : 5\t expected : 8 (esc)
+ device 12:\t\t stored : 6\t expected : 8 (esc)
+ device 13:\t\t stored : 3\t expected : 8 (esc)
+ device 14:\t\t stored : 7\t expected : 8 (esc)
+ device 15:\t\t stored : 8\t expected : 8 (esc)
+ device 16:\t\t stored : 7\t expected : 8 (esc)
+ device 17:\t\t stored : 7\t expected : 8 (esc)
+ device 18:\t\t stored : 6\t expected : 8 (esc)
+ device 19:\t\t stored : 11\t expected : 8 (esc)
+ device 20:\t\t stored : 12\t expected : 8 (esc)
+ device 21:\t\t stored : 1\t expected : 8 (esc)
+ device 22:\t\t stored : 4\t expected : 8 (esc)
+ device 23:\t\t stored : 5\t expected : 8 (esc)
+ device 24:\t\t stored : 6\t expected : 8 (esc)
+ rule 0 (replicated_rule) num_rep 3 result size == 3:\t100/100 (esc)
+ device 0:\t\t stored : 8\t expected : 12 (esc)
+ device 1:\t\t stored : 6\t expected : 12 (esc)
+ device 2:\t\t stored : 69\t expected : 12 (esc)
+ device 3:\t\t stored : 6\t expected : 12 (esc)
+ device 4:\t\t stored : 6\t expected : 12 (esc)
+ device 5:\t\t stored : 8\t expected : 12 (esc)
+ device 6:\t\t stored : 9\t expected : 12 (esc)
+ device 7:\t\t stored : 7\t expected : 12 (esc)
+ device 8:\t\t stored : 14\t expected : 12 (esc)
+ device 9:\t\t stored : 16\t expected : 12 (esc)
+ device 10:\t\t stored : 6\t expected : 12 (esc)
+ device 11:\t\t stored : 11\t expected : 12 (esc)
+ device 12:\t\t stored : 9\t expected : 12 (esc)
+ device 13:\t\t stored : 8\t expected : 12 (esc)
+ device 14:\t\t stored : 7\t expected : 12 (esc)
+ device 15:\t\t stored : 8\t expected : 12 (esc)
+ device 16:\t\t stored : 9\t expected : 12 (esc)
+ device 17:\t\t stored : 11\t expected : 12 (esc)
+ device 18:\t\t stored : 9\t expected : 12 (esc)
+ device 19:\t\t stored : 16\t expected : 12 (esc)
+ device 20:\t\t stored : 18\t expected : 12 (esc)
+ device 21:\t\t stored : 5\t expected : 12 (esc)
+ device 22:\t\t stored : 15\t expected : 12 (esc)
+ device 23:\t\t stored : 8\t expected : 12 (esc)
+ device 24:\t\t stored : 11\t expected : 12 (esc)
+ rule 0 (replicated_rule) num_rep 4 result size == 4:\t100/100 (esc)
+ device 0:\t\t stored : 8\t expected : 16 (esc)
+ device 1:\t\t stored : 6\t expected : 16 (esc)
+ device 2:\t\t stored : 72\t expected : 16 (esc)
+ device 3:\t\t stored : 6\t expected : 16 (esc)
+ device 4:\t\t stored : 6\t expected : 16 (esc)
+ device 5:\t\t stored : 13\t expected : 16 (esc)
+ device 6:\t\t stored : 13\t expected : 16 (esc)
+ device 7:\t\t stored : 13\t expected : 16 (esc)
+ device 8:\t\t stored : 15\t expected : 16 (esc)
+ device 9:\t\t stored : 20\t expected : 16 (esc)
+ device 10:\t\t stored : 11\t expected : 16 (esc)
+ device 11:\t\t stored : 20\t expected : 16 (esc)
+ device 12:\t\t stored : 13\t expected : 16 (esc)
+ device 13:\t\t stored : 13\t expected : 16 (esc)
+ device 14:\t\t stored : 11\t expected : 16 (esc)
+ device 15:\t\t stored : 19\t expected : 16 (esc)
+ device 16:\t\t stored : 12\t expected : 16 (esc)
+ device 17:\t\t stored : 13\t expected : 16 (esc)
+ device 18:\t\t stored : 17\t expected : 16 (esc)
+ device 19:\t\t stored : 22\t expected : 16 (esc)
+ device 20:\t\t stored : 21\t expected : 16 (esc)
+ device 21:\t\t stored : 11\t expected : 16 (esc)
+ device 22:\t\t stored : 20\t expected : 16 (esc)
+ device 23:\t\t stored : 10\t expected : 16 (esc)
+ device 24:\t\t stored : 15\t expected : 16 (esc)
+ rule 0 (replicated_rule) num_rep 5 result size == 4:\t3/100 (esc)
+ rule 0 (replicated_rule) num_rep 5 result size == 5:\t97/100 (esc)
+ device 0:\t\t stored : 8\t expected : 20 (esc)
+ device 1:\t\t stored : 6\t expected : 20 (esc)
+ device 2:\t\t stored : 74\t expected : 20 (esc)
+ device 3:\t\t stored : 6\t expected : 20 (esc)
+ device 4:\t\t stored : 6\t expected : 20 (esc)
+ device 5:\t\t stored : 17\t expected : 20 (esc)
+ device 6:\t\t stored : 17\t expected : 20 (esc)
+ device 7:\t\t stored : 19\t expected : 20 (esc)
+ device 8:\t\t stored : 18\t expected : 20 (esc)
+ device 9:\t\t stored : 27\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 28\t expected : 20 (esc)
+ device 12:\t\t stored : 22\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 17\t expected : 20 (esc)
+ device 15:\t\t stored : 22\t expected : 20 (esc)
+ device 16:\t\t stored : 14\t expected : 20 (esc)
+ device 17:\t\t stored : 19\t expected : 20 (esc)
+ device 18:\t\t stored : 20\t expected : 20 (esc)
+ device 19:\t\t stored : 25\t expected : 20 (esc)
+ device 20:\t\t stored : 24\t expected : 20 (esc)
+ device 21:\t\t stored : 19\t expected : 20 (esc)
+ device 22:\t\t stored : 25\t expected : 20 (esc)
+ device 23:\t\t stored : 13\t expected : 20 (esc)
+ device 24:\t\t stored : 18\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 6 result size == 4:\t3/100 (esc)
+ rule 0 (replicated_rule) num_rep 6 result size == 5:\t97/100 (esc)
+ device 0:\t\t stored : 8\t expected : 20 (esc)
+ device 1:\t\t stored : 6\t expected : 20 (esc)
+ device 2:\t\t stored : 74\t expected : 20 (esc)
+ device 3:\t\t stored : 6\t expected : 20 (esc)
+ device 4:\t\t stored : 6\t expected : 20 (esc)
+ device 5:\t\t stored : 17\t expected : 20 (esc)
+ device 6:\t\t stored : 17\t expected : 20 (esc)
+ device 7:\t\t stored : 19\t expected : 20 (esc)
+ device 8:\t\t stored : 18\t expected : 20 (esc)
+ device 9:\t\t stored : 27\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 28\t expected : 20 (esc)
+ device 12:\t\t stored : 22\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 17\t expected : 20 (esc)
+ device 15:\t\t stored : 22\t expected : 20 (esc)
+ device 16:\t\t stored : 14\t expected : 20 (esc)
+ device 17:\t\t stored : 19\t expected : 20 (esc)
+ device 18:\t\t stored : 20\t expected : 20 (esc)
+ device 19:\t\t stored : 25\t expected : 20 (esc)
+ device 20:\t\t stored : 24\t expected : 20 (esc)
+ device 21:\t\t stored : 19\t expected : 20 (esc)
+ device 22:\t\t stored : 25\t expected : 20 (esc)
+ device 23:\t\t stored : 13\t expected : 20 (esc)
+ device 24:\t\t stored : 18\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 7 result size == 4:\t3/100 (esc)
+ rule 0 (replicated_rule) num_rep 7 result size == 5:\t97/100 (esc)
+ device 0:\t\t stored : 8\t expected : 20 (esc)
+ device 1:\t\t stored : 6\t expected : 20 (esc)
+ device 2:\t\t stored : 74\t expected : 20 (esc)
+ device 3:\t\t stored : 6\t expected : 20 (esc)
+ device 4:\t\t stored : 6\t expected : 20 (esc)
+ device 5:\t\t stored : 17\t expected : 20 (esc)
+ device 6:\t\t stored : 17\t expected : 20 (esc)
+ device 7:\t\t stored : 19\t expected : 20 (esc)
+ device 8:\t\t stored : 18\t expected : 20 (esc)
+ device 9:\t\t stored : 27\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 28\t expected : 20 (esc)
+ device 12:\t\t stored : 22\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 17\t expected : 20 (esc)
+ device 15:\t\t stored : 22\t expected : 20 (esc)
+ device 16:\t\t stored : 14\t expected : 20 (esc)
+ device 17:\t\t stored : 19\t expected : 20 (esc)
+ device 18:\t\t stored : 20\t expected : 20 (esc)
+ device 19:\t\t stored : 25\t expected : 20 (esc)
+ device 20:\t\t stored : 24\t expected : 20 (esc)
+ device 21:\t\t stored : 19\t expected : 20 (esc)
+ device 22:\t\t stored : 25\t expected : 20 (esc)
+ device 23:\t\t stored : 13\t expected : 20 (esc)
+ device 24:\t\t stored : 18\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 8 result size == 4:\t3/100 (esc)
+ rule 0 (replicated_rule) num_rep 8 result size == 5:\t97/100 (esc)
+ device 0:\t\t stored : 8\t expected : 20 (esc)
+ device 1:\t\t stored : 6\t expected : 20 (esc)
+ device 2:\t\t stored : 74\t expected : 20 (esc)
+ device 3:\t\t stored : 6\t expected : 20 (esc)
+ device 4:\t\t stored : 6\t expected : 20 (esc)
+ device 5:\t\t stored : 17\t expected : 20 (esc)
+ device 6:\t\t stored : 17\t expected : 20 (esc)
+ device 7:\t\t stored : 19\t expected : 20 (esc)
+ device 8:\t\t stored : 18\t expected : 20 (esc)
+ device 9:\t\t stored : 27\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 28\t expected : 20 (esc)
+ device 12:\t\t stored : 22\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 17\t expected : 20 (esc)
+ device 15:\t\t stored : 22\t expected : 20 (esc)
+ device 16:\t\t stored : 14\t expected : 20 (esc)
+ device 17:\t\t stored : 19\t expected : 20 (esc)
+ device 18:\t\t stored : 20\t expected : 20 (esc)
+ device 19:\t\t stored : 25\t expected : 20 (esc)
+ device 20:\t\t stored : 24\t expected : 20 (esc)
+ device 21:\t\t stored : 19\t expected : 20 (esc)
+ device 22:\t\t stored : 25\t expected : 20 (esc)
+ device 23:\t\t stored : 13\t expected : 20 (esc)
+ device 24:\t\t stored : 18\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 9 result size == 4:\t2/100 (esc)
+ rule 0 (replicated_rule) num_rep 9 result size == 5:\t98/100 (esc)
+ device 0:\t\t stored : 8\t expected : 20 (esc)
+ device 1:\t\t stored : 6\t expected : 20 (esc)
+ device 2:\t\t stored : 74\t expected : 20 (esc)
+ device 3:\t\t stored : 6\t expected : 20 (esc)
+ device 4:\t\t stored : 6\t expected : 20 (esc)
+ device 5:\t\t stored : 17\t expected : 20 (esc)
+ device 6:\t\t stored : 17\t expected : 20 (esc)
+ device 7:\t\t stored : 19\t expected : 20 (esc)
+ device 8:\t\t stored : 18\t expected : 20 (esc)
+ device 9:\t\t stored : 28\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 28\t expected : 20 (esc)
+ device 12:\t\t stored : 22\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 17\t expected : 20 (esc)
+ device 15:\t\t stored : 22\t expected : 20 (esc)
+ device 16:\t\t stored : 14\t expected : 20 (esc)
+ device 17:\t\t stored : 19\t expected : 20 (esc)
+ device 18:\t\t stored : 20\t expected : 20 (esc)
+ device 19:\t\t stored : 25\t expected : 20 (esc)
+ device 20:\t\t stored : 24\t expected : 20 (esc)
+ device 21:\t\t stored : 19\t expected : 20 (esc)
+ device 22:\t\t stored : 25\t expected : 20 (esc)
+ device 23:\t\t stored : 13\t expected : 20 (esc)
+ device 24:\t\t stored : 18\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 10 result size == 4:\t2/100 (esc)
+ rule 0 (replicated_rule) num_rep 10 result size == 5:\t98/100 (esc)
+ device 0:\t\t stored : 8\t expected : 20 (esc)
+ device 1:\t\t stored : 6\t expected : 20 (esc)
+ device 2:\t\t stored : 74\t expected : 20 (esc)
+ device 3:\t\t stored : 6\t expected : 20 (esc)
+ device 4:\t\t stored : 6\t expected : 20 (esc)
+ device 5:\t\t stored : 17\t expected : 20 (esc)
+ device 6:\t\t stored : 17\t expected : 20 (esc)
+ device 7:\t\t stored : 19\t expected : 20 (esc)
+ device 8:\t\t stored : 18\t expected : 20 (esc)
+ device 9:\t\t stored : 28\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 28\t expected : 20 (esc)
+ device 12:\t\t stored : 22\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 17\t expected : 20 (esc)
+ device 15:\t\t stored : 22\t expected : 20 (esc)
+ device 16:\t\t stored : 14\t expected : 20 (esc)
+ device 17:\t\t stored : 19\t expected : 20 (esc)
+ device 18:\t\t stored : 20\t expected : 20 (esc)
+ device 19:\t\t stored : 25\t expected : 20 (esc)
+ device 20:\t\t stored : 24\t expected : 20 (esc)
+ device 21:\t\t stored : 19\t expected : 20 (esc)
+ device 22:\t\t stored : 25\t expected : 20 (esc)
+ device 23:\t\t stored : 13\t expected : 20 (esc)
+ device 24:\t\t stored : 18\t expected : 20 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
+ $ crushtool -i "$map" --set-straw-calc-version 1 --reweight --test --show-utilization --max-x 100 --min-x 1 --min-rep 1 --max-rep 10
+ rule 0 (replicated_rule), x = 1..100, numrep = 1..10
+ rule 0 (replicated_rule) num_rep 1 result size == 1:\t100/100 (esc)
+ device 1:\t\t stored : 1\t expected : 4 (esc)
+ device 2:\t\t stored : 75\t expected : 4 (esc)
+ device 3:\t\t stored : 2\t expected : 4 (esc)
+ device 4:\t\t stored : 1\t expected : 4 (esc)
+ device 5:\t\t stored : 2\t expected : 4 (esc)
+ device 7:\t\t stored : 2\t expected : 4 (esc)
+ device 8:\t\t stored : 1\t expected : 4 (esc)
+ device 9:\t\t stored : 2\t expected : 4 (esc)
+ device 14:\t\t stored : 3\t expected : 4 (esc)
+ device 16:\t\t stored : 3\t expected : 4 (esc)
+ device 19:\t\t stored : 4\t expected : 4 (esc)
+ device 20:\t\t stored : 2\t expected : 4 (esc)
+ device 22:\t\t stored : 1\t expected : 4 (esc)
+ device 23:\t\t stored : 1\t expected : 4 (esc)
+ rule 0 (replicated_rule) num_rep 2 result size == 2:\t100/100 (esc)
+ device 0:\t\t stored : 1\t expected : 8 (esc)
+ device 1:\t\t stored : 1\t expected : 8 (esc)
+ device 2:\t\t stored : 95\t expected : 8 (esc)
+ device 3:\t\t stored : 2\t expected : 8 (esc)
+ device 4:\t\t stored : 1\t expected : 8 (esc)
+ device 5:\t\t stored : 3\t expected : 8 (esc)
+ device 6:\t\t stored : 3\t expected : 8 (esc)
+ device 7:\t\t stored : 7\t expected : 8 (esc)
+ device 8:\t\t stored : 4\t expected : 8 (esc)
+ device 9:\t\t stored : 8\t expected : 8 (esc)
+ device 11:\t\t stored : 1\t expected : 8 (esc)
+ device 12:\t\t stored : 4\t expected : 8 (esc)
+ device 13:\t\t stored : 2\t expected : 8 (esc)
+ device 14:\t\t stored : 6\t expected : 8 (esc)
+ device 15:\t\t stored : 5\t expected : 8 (esc)
+ device 16:\t\t stored : 4\t expected : 8 (esc)
+ device 17:\t\t stored : 8\t expected : 8 (esc)
+ device 18:\t\t stored : 5\t expected : 8 (esc)
+ device 19:\t\t stored : 9\t expected : 8 (esc)
+ device 20:\t\t stored : 7\t expected : 8 (esc)
+ device 21:\t\t stored : 5\t expected : 8 (esc)
+ device 22:\t\t stored : 6\t expected : 8 (esc)
+ device 23:\t\t stored : 5\t expected : 8 (esc)
+ device 24:\t\t stored : 8\t expected : 8 (esc)
+ rule 0 (replicated_rule) num_rep 3 result size == 3:\t100/100 (esc)
+ device 0:\t\t stored : 1\t expected : 12 (esc)
+ device 1:\t\t stored : 1\t expected : 12 (esc)
+ device 2:\t\t stored : 95\t expected : 12 (esc)
+ device 3:\t\t stored : 2\t expected : 12 (esc)
+ device 4:\t\t stored : 1\t expected : 12 (esc)
+ device 5:\t\t stored : 4\t expected : 12 (esc)
+ device 6:\t\t stored : 5\t expected : 12 (esc)
+ device 7:\t\t stored : 10\t expected : 12 (esc)
+ device 8:\t\t stored : 16\t expected : 12 (esc)
+ device 9:\t\t stored : 13\t expected : 12 (esc)
+ device 10:\t\t stored : 8\t expected : 12 (esc)
+ device 11:\t\t stored : 5\t expected : 12 (esc)
+ device 12:\t\t stored : 5\t expected : 12 (esc)
+ device 13:\t\t stored : 5\t expected : 12 (esc)
+ device 14:\t\t stored : 8\t expected : 12 (esc)
+ device 15:\t\t stored : 11\t expected : 12 (esc)
+ device 16:\t\t stored : 17\t expected : 12 (esc)
+ device 17:\t\t stored : 12\t expected : 12 (esc)
+ device 18:\t\t stored : 9\t expected : 12 (esc)
+ device 19:\t\t stored : 15\t expected : 12 (esc)
+ device 20:\t\t stored : 16\t expected : 12 (esc)
+ device 21:\t\t stored : 8\t expected : 12 (esc)
+ device 22:\t\t stored : 11\t expected : 12 (esc)
+ device 23:\t\t stored : 11\t expected : 12 (esc)
+ device 24:\t\t stored : 11\t expected : 12 (esc)
+ rule 0 (replicated_rule) num_rep 4 result size == 3:\t3/100 (esc)
+ rule 0 (replicated_rule) num_rep 4 result size == 4:\t97/100 (esc)
+ device 0:\t\t stored : 1\t expected : 16 (esc)
+ device 1:\t\t stored : 1\t expected : 16 (esc)
+ device 2:\t\t stored : 95\t expected : 16 (esc)
+ device 3:\t\t stored : 2\t expected : 16 (esc)
+ device 4:\t\t stored : 1\t expected : 16 (esc)
+ device 5:\t\t stored : 11\t expected : 16 (esc)
+ device 6:\t\t stored : 12\t expected : 16 (esc)
+ device 7:\t\t stored : 16\t expected : 16 (esc)
+ device 8:\t\t stored : 19\t expected : 16 (esc)
+ device 9:\t\t stored : 18\t expected : 16 (esc)
+ device 10:\t\t stored : 12\t expected : 16 (esc)
+ device 11:\t\t stored : 12\t expected : 16 (esc)
+ device 12:\t\t stored : 13\t expected : 16 (esc)
+ device 13:\t\t stored : 11\t expected : 16 (esc)
+ device 14:\t\t stored : 16\t expected : 16 (esc)
+ device 15:\t\t stored : 19\t expected : 16 (esc)
+ device 16:\t\t stored : 19\t expected : 16 (esc)
+ device 17:\t\t stored : 15\t expected : 16 (esc)
+ device 18:\t\t stored : 11\t expected : 16 (esc)
+ device 19:\t\t stored : 18\t expected : 16 (esc)
+ device 20:\t\t stored : 22\t expected : 16 (esc)
+ device 21:\t\t stored : 12\t expected : 16 (esc)
+ device 22:\t\t stored : 14\t expected : 16 (esc)
+ device 23:\t\t stored : 13\t expected : 16 (esc)
+ device 24:\t\t stored : 14\t expected : 16 (esc)
+ rule 0 (replicated_rule) num_rep 5 result size == 3:\t3/100 (esc)
+ rule 0 (replicated_rule) num_rep 5 result size == 4:\t43/100 (esc)
+ rule 0 (replicated_rule) num_rep 5 result size == 5:\t54/100 (esc)
+ device 0:\t\t stored : 1\t expected : 20 (esc)
+ device 1:\t\t stored : 1\t expected : 20 (esc)
+ device 2:\t\t stored : 95\t expected : 20 (esc)
+ device 3:\t\t stored : 2\t expected : 20 (esc)
+ device 4:\t\t stored : 1\t expected : 20 (esc)
+ device 5:\t\t stored : 14\t expected : 20 (esc)
+ device 6:\t\t stored : 14\t expected : 20 (esc)
+ device 7:\t\t stored : 16\t expected : 20 (esc)
+ device 8:\t\t stored : 19\t expected : 20 (esc)
+ device 9:\t\t stored : 22\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 16\t expected : 20 (esc)
+ device 12:\t\t stored : 17\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 19\t expected : 20 (esc)
+ device 15:\t\t stored : 19\t expected : 20 (esc)
+ device 16:\t\t stored : 20\t expected : 20 (esc)
+ device 17:\t\t stored : 17\t expected : 20 (esc)
+ device 18:\t\t stored : 15\t expected : 20 (esc)
+ device 19:\t\t stored : 20\t expected : 20 (esc)
+ device 20:\t\t stored : 26\t expected : 20 (esc)
+ device 21:\t\t stored : 17\t expected : 20 (esc)
+ device 22:\t\t stored : 16\t expected : 20 (esc)
+ device 23:\t\t stored : 15\t expected : 20 (esc)
+ device 24:\t\t stored : 16\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 6 result size == 3:\t2/100 (esc)
+ rule 0 (replicated_rule) num_rep 6 result size == 4:\t43/100 (esc)
+ rule 0 (replicated_rule) num_rep 6 result size == 5:\t55/100 (esc)
+ device 0:\t\t stored : 1\t expected : 20 (esc)
+ device 1:\t\t stored : 1\t expected : 20 (esc)
+ device 2:\t\t stored : 95\t expected : 20 (esc)
+ device 3:\t\t stored : 2\t expected : 20 (esc)
+ device 4:\t\t stored : 1\t expected : 20 (esc)
+ device 5:\t\t stored : 14\t expected : 20 (esc)
+ device 6:\t\t stored : 14\t expected : 20 (esc)
+ device 7:\t\t stored : 16\t expected : 20 (esc)
+ device 8:\t\t stored : 19\t expected : 20 (esc)
+ device 9:\t\t stored : 22\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 16\t expected : 20 (esc)
+ device 12:\t\t stored : 17\t expected : 20 (esc)
+ device 13:\t\t stored : 18\t expected : 20 (esc)
+ device 14:\t\t stored : 20\t expected : 20 (esc)
+ device 15:\t\t stored : 19\t expected : 20 (esc)
+ device 16:\t\t stored : 20\t expected : 20 (esc)
+ device 17:\t\t stored : 17\t expected : 20 (esc)
+ device 18:\t\t stored : 15\t expected : 20 (esc)
+ device 19:\t\t stored : 20\t expected : 20 (esc)
+ device 20:\t\t stored : 26\t expected : 20 (esc)
+ device 21:\t\t stored : 17\t expected : 20 (esc)
+ device 22:\t\t stored : 16\t expected : 20 (esc)
+ device 23:\t\t stored : 16\t expected : 20 (esc)
+ device 24:\t\t stored : 16\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 7 result size == 3:\t2/100 (esc)
+ rule 0 (replicated_rule) num_rep 7 result size == 4:\t42/100 (esc)
+ rule 0 (replicated_rule) num_rep 7 result size == 5:\t56/100 (esc)
+ device 0:\t\t stored : 1\t expected : 20 (esc)
+ device 1:\t\t stored : 1\t expected : 20 (esc)
+ device 2:\t\t stored : 95\t expected : 20 (esc)
+ device 3:\t\t stored : 2\t expected : 20 (esc)
+ device 4:\t\t stored : 1\t expected : 20 (esc)
+ device 5:\t\t stored : 14\t expected : 20 (esc)
+ device 6:\t\t stored : 14\t expected : 20 (esc)
+ device 7:\t\t stored : 16\t expected : 20 (esc)
+ device 8:\t\t stored : 19\t expected : 20 (esc)
+ device 9:\t\t stored : 22\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 16\t expected : 20 (esc)
+ device 12:\t\t stored : 17\t expected : 20 (esc)
+ device 13:\t\t stored : 19\t expected : 20 (esc)
+ device 14:\t\t stored : 20\t expected : 20 (esc)
+ device 15:\t\t stored : 19\t expected : 20 (esc)
+ device 16:\t\t stored : 20\t expected : 20 (esc)
+ device 17:\t\t stored : 17\t expected : 20 (esc)
+ device 18:\t\t stored : 15\t expected : 20 (esc)
+ device 19:\t\t stored : 20\t expected : 20 (esc)
+ device 20:\t\t stored : 26\t expected : 20 (esc)
+ device 21:\t\t stored : 17\t expected : 20 (esc)
+ device 22:\t\t stored : 16\t expected : 20 (esc)
+ device 23:\t\t stored : 16\t expected : 20 (esc)
+ device 24:\t\t stored : 16\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 8 result size == 3:\t2/100 (esc)
+ rule 0 (replicated_rule) num_rep 8 result size == 4:\t40/100 (esc)
+ rule 0 (replicated_rule) num_rep 8 result size == 5:\t58/100 (esc)
+ device 0:\t\t stored : 1\t expected : 20 (esc)
+ device 1:\t\t stored : 1\t expected : 20 (esc)
+ device 2:\t\t stored : 95\t expected : 20 (esc)
+ device 3:\t\t stored : 2\t expected : 20 (esc)
+ device 4:\t\t stored : 1\t expected : 20 (esc)
+ device 5:\t\t stored : 14\t expected : 20 (esc)
+ device 6:\t\t stored : 14\t expected : 20 (esc)
+ device 7:\t\t stored : 16\t expected : 20 (esc)
+ device 8:\t\t stored : 19\t expected : 20 (esc)
+ device 9:\t\t stored : 22\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 16\t expected : 20 (esc)
+ device 12:\t\t stored : 17\t expected : 20 (esc)
+ device 13:\t\t stored : 20\t expected : 20 (esc)
+ device 14:\t\t stored : 20\t expected : 20 (esc)
+ device 15:\t\t stored : 19\t expected : 20 (esc)
+ device 16:\t\t stored : 20\t expected : 20 (esc)
+ device 17:\t\t stored : 17\t expected : 20 (esc)
+ device 18:\t\t stored : 16\t expected : 20 (esc)
+ device 19:\t\t stored : 20\t expected : 20 (esc)
+ device 20:\t\t stored : 26\t expected : 20 (esc)
+ device 21:\t\t stored : 17\t expected : 20 (esc)
+ device 22:\t\t stored : 16\t expected : 20 (esc)
+ device 23:\t\t stored : 16\t expected : 20 (esc)
+ device 24:\t\t stored : 16\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 9 result size == 3:\t2/100 (esc)
+ rule 0 (replicated_rule) num_rep 9 result size == 4:\t37/100 (esc)
+ rule 0 (replicated_rule) num_rep 9 result size == 5:\t61/100 (esc)
+ device 0:\t\t stored : 1\t expected : 20 (esc)
+ device 1:\t\t stored : 1\t expected : 20 (esc)
+ device 2:\t\t stored : 95\t expected : 20 (esc)
+ device 3:\t\t stored : 2\t expected : 20 (esc)
+ device 4:\t\t stored : 1\t expected : 20 (esc)
+ device 5:\t\t stored : 14\t expected : 20 (esc)
+ device 6:\t\t stored : 14\t expected : 20 (esc)
+ device 7:\t\t stored : 16\t expected : 20 (esc)
+ device 8:\t\t stored : 19\t expected : 20 (esc)
+ device 9:\t\t stored : 23\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 16\t expected : 20 (esc)
+ device 12:\t\t stored : 17\t expected : 20 (esc)
+ device 13:\t\t stored : 20\t expected : 20 (esc)
+ device 14:\t\t stored : 21\t expected : 20 (esc)
+ device 15:\t\t stored : 19\t expected : 20 (esc)
+ device 16:\t\t stored : 20\t expected : 20 (esc)
+ device 17:\t\t stored : 18\t expected : 20 (esc)
+ device 18:\t\t stored : 16\t expected : 20 (esc)
+ device 19:\t\t stored : 20\t expected : 20 (esc)
+ device 20:\t\t stored : 26\t expected : 20 (esc)
+ device 21:\t\t stored : 17\t expected : 20 (esc)
+ device 22:\t\t stored : 16\t expected : 20 (esc)
+ device 23:\t\t stored : 16\t expected : 20 (esc)
+ device 24:\t\t stored : 16\t expected : 20 (esc)
+ rule 0 (replicated_rule) num_rep 10 result size == 3:\t2/100 (esc)
+ rule 0 (replicated_rule) num_rep 10 result size == 4:\t36/100 (esc)
+ rule 0 (replicated_rule) num_rep 10 result size == 5:\t62/100 (esc)
+ device 0:\t\t stored : 1\t expected : 20 (esc)
+ device 1:\t\t stored : 1\t expected : 20 (esc)
+ device 2:\t\t stored : 95\t expected : 20 (esc)
+ device 3:\t\t stored : 2\t expected : 20 (esc)
+ device 4:\t\t stored : 1\t expected : 20 (esc)
+ device 5:\t\t stored : 14\t expected : 20 (esc)
+ device 6:\t\t stored : 14\t expected : 20 (esc)
+ device 7:\t\t stored : 16\t expected : 20 (esc)
+ device 8:\t\t stored : 19\t expected : 20 (esc)
+ device 9:\t\t stored : 23\t expected : 20 (esc)
+ device 10:\t\t stored : 15\t expected : 20 (esc)
+ device 11:\t\t stored : 17\t expected : 20 (esc)
+ device 12:\t\t stored : 17\t expected : 20 (esc)
+ device 13:\t\t stored : 20\t expected : 20 (esc)
+ device 14:\t\t stored : 21\t expected : 20 (esc)
+ device 15:\t\t stored : 19\t expected : 20 (esc)
+ device 16:\t\t stored : 20\t expected : 20 (esc)
+ device 17:\t\t stored : 18\t expected : 20 (esc)
+ device 18:\t\t stored : 16\t expected : 20 (esc)
+ device 19:\t\t stored : 20\t expected : 20 (esc)
+ device 20:\t\t stored : 26\t expected : 20 (esc)
+ device 21:\t\t stored : 17\t expected : 20 (esc)
+ device 22:\t\t stored : 16\t expected : 20 (esc)
+ device 23:\t\t stored : 16\t expected : 20 (esc)
+ device 24:\t\t stored : 16\t expected : 20 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/bad-mappings.crushmap.txt b/src/test/cli/crushtool/bad-mappings.crushmap.txt
new file mode 100644
index 000000000..69718ffba
--- /dev/null
+++ b/src/test/cli/crushtool/bad-mappings.crushmap.txt
@@ -0,0 +1,35 @@
+device 0 device0
+device 1 device1
+device 2 device2
+device 3 device3
+device 4 device4
+
+type 0 osd
+type 1 domain
+
+domain root {
+ id -1
+ alg straw
+ hash 0
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+ item device2 weight 1.00000
+ item device3 weight 1.00000
+ item device4 weight 1.00000
+}
+
+rule rule-firstn {
+ id 0
+ type replicated
+ step take root
+ step choose firstn 0 type osd
+ step emit
+}
+
+rule rule-indep {
+ id 1
+ type erasure
+ step take root
+ step choose indep 0 type osd
+ step emit
+}
diff --git a/src/test/cli/crushtool/bad-mappings.t b/src/test/cli/crushtool/bad-mappings.t
new file mode 100644
index 000000000..dbdcd576b
--- /dev/null
+++ b/src/test/cli/crushtool/bad-mappings.t
@@ -0,0 +1,6 @@
+ $ crushtool -c "$TESTDIR/bad-mappings.crushmap.txt" -o "$TESTDIR/bad-mappings.crushmap"
+ $ crushtool -i "$TESTDIR/bad-mappings.crushmap" --test --show-bad-mappings --rule 0 --x 1 --num-rep 10
+ bad mapping rule 0 x 1 num_rep 10 result [4,0,2,3,1]
+ $ crushtool -i "$TESTDIR/bad-mappings.crushmap" --test --show-bad-mappings --rule 1 --x 1 --num-rep 10
+ bad mapping rule 1 x 1 num_rep 10 result [4,0,2,1,3,2147483647,2147483647,2147483647,2147483647,2147483647]
+ $ rm -f "$TESTDIR/bad-mappings.crushmap"
diff --git a/src/test/cli/crushtool/build.t b/src/test/cli/crushtool/build.t
new file mode 100644
index 000000000..89d56abee
--- /dev/null
+++ b/src/test/cli/crushtool/build.t
@@ -0,0 +1,75 @@
+ $ map="$TESTDIR/build.crushmap"
+
+#
+# display the crush tree by default
+#
+ $ crushtool --outfn "$map" --build --num_osds 5 node straw 2 rack straw 1 root straw 0
+
+#
+# silence all messages with --debug-crush 0
+#
+ $ CEPH_ARGS="--debug-crush 0" crushtool --outfn "$map" --build --num_osds 5 node straw 2 rack straw 1 root straw 0
+
+#
+# display a warning if there is more than one root
+#
+ $ crushtool --outfn "$map" --build --num_osds 5 node straw 2 rack straw 1
+ The crush rules will use the root rack0 (re)
+ and ignore the others.
+ There are 3 roots, they can be
+ grouped into a single root by appending something like:
+ root straw 0
+
+#
+# crush rules are generated using the OSDMap helpers
+#
+ $ CEPH_ARGS="--debug-crush 0" crushtool --outfn "$map" --set-straw-calc-version 0 --build --num_osds 1 root straw 0 --set-chooseleaf-stable 0
+ $ crushtool -o "$map.txt" -d "$map"
+ $ cat "$map.txt"
+ # begin crush map
+ tunable choose_local_tries 0
+ tunable choose_local_fallback_tries 0
+ tunable choose_total_tries 50
+ tunable chooseleaf_descend_once 1
+ tunable chooseleaf_vary_r 1
+ tunable allowed_bucket_algs 54
+
+ # devices
+ device 0 osd.0
+
+ # types
+ type 0 osd
+ type 1 root
+
+ # buckets
+ root root {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \t# weight 1.00000 (esc)
+ \talg straw (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.0 weight 1.00000 (esc)
+ }
+
+ # rules
+ rule replicated_rule {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take root (esc)
+ \tstep chooseleaf firstn 0 type root (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+ $ rm "$map" "$map.txt"
+
+#
+# Wrong number of arguments
+#
+ $ crushtool --outfn "$map" --debug-crush 0 --build --num_osds 5 node straw 0
+ remaining args: [--debug-crush,0,node,straw,0]
+ layers must be specified with 3-tuples of (name, buckettype, size)
+ [1]
+
+# Local Variables:
+# compile-command: "cd ../../.. ; make crushtool && test/run-cli-tests"
+# End:
diff --git a/src/test/cli/crushtool/check-invalid-map.t b/src/test/cli/crushtool/check-invalid-map.t
new file mode 100644
index 000000000..d4b6b06f8
--- /dev/null
+++ b/src/test/cli/crushtool/check-invalid-map.t
@@ -0,0 +1,3 @@
+ $ crushtool -d /etc/hosts
+ crushtool: unable to decode /etc/hosts
+ [1]
diff --git a/src/test/cli/crushtool/check-names.empty.crushmap.txt b/src/test/cli/crushtool/check-names.empty.crushmap.txt
new file mode 100644
index 000000000..6ba00cda9
--- /dev/null
+++ b/src/test/cli/crushtool/check-names.empty.crushmap.txt
@@ -0,0 +1,11 @@
+# begin crush map
+
+# devices
+
+# types
+
+# buckets
+
+# rules
+
+# end crush map
diff --git a/src/test/cli/crushtool/check-names.empty.t b/src/test/cli/crushtool/check-names.empty.t
new file mode 100644
index 000000000..755e9317b
--- /dev/null
+++ b/src/test/cli/crushtool/check-names.empty.t
@@ -0,0 +1,5 @@
+ $ crushtool -c "$TESTDIR/check-names.empty.crushmap.txt" -o "$TESTDIR/check-names.empty.crushmap"
+ $ crushtool -i "$TESTDIR/check-names.empty.crushmap" --check 0
+ unknown type name: item#0
+ [1]
+ $ rm -f "$TESTDIR/check-names.empty.crushmap"
diff --git a/src/test/cli/crushtool/check-names.max-id.t b/src/test/cli/crushtool/check-names.max-id.t
new file mode 100644
index 000000000..ee04fdcc8
--- /dev/null
+++ b/src/test/cli/crushtool/check-names.max-id.t
@@ -0,0 +1,8 @@
+ $ crushtool -i "$TESTDIR/simple.template" --add-item 0 1.0 device0 --loc host host0 --loc cluster cluster0 -o check-names.crushmap > /dev/null
+ $ crushtool -i check-names.crushmap --add-item 1 1.0 device1 --loc host host0 --loc cluster cluster0 -o check-names.crushmap > /dev/null
+ $ crushtool -i check-names.crushmap --check 2
+ $ crushtool -i check-names.crushmap --add-item 2 1.0 device2 --loc host host0 --loc cluster cluster0 -o check-names.crushmap > /dev/null
+ $ crushtool -i check-names.crushmap --check 2
+ item id too large: item#2
+ [1]
+ $ crushtool -i check-names.crushmap --check
diff --git a/src/test/cli/crushtool/check-overlapped-rules.crushmap.txt b/src/test/cli/crushtool/check-overlapped-rules.crushmap.txt
new file mode 100644
index 000000000..b87342c70
--- /dev/null
+++ b/src/test/cli/crushtool/check-overlapped-rules.crushmap.txt
@@ -0,0 +1,75 @@
+device 0 device0
+device 1 device1
+device 2 device2
+device 3 device3
+device 4 device4
+
+type 0 osd
+type 1 host
+
+host host0 {
+ id -1
+ alg straw
+ hash 0
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+ item device2 weight 1.00000
+ item device3 weight 1.00000
+ item device4 weight 1.00000
+}
+
+rule rule-r0 {
+ id 0
+ type replicated
+ step take host0
+ step choose firstn 0 type osd
+ step emit
+}
+
+rule rule-r1 {
+ id 0
+ type replicated
+ step take host0
+ step choose firstn 0 type osd
+ step emit
+}
+
+rule rule-r2 {
+ id 0
+ type replicated
+ step take host0
+ step choose firstn 0 type osd
+ step emit
+}
+
+rule rule-r3 {
+ id 0
+ type replicated
+ step take host0
+ step choose indep 0 type osd
+ step emit
+}
+
+rule rule-r4 {
+ id 0
+ type replicated
+ step take host0
+ step choose indep 0 type osd
+ step emit
+}
+
+rule rule-e0 {
+ id 0
+ type erasure
+ step take host0
+ step choose indep 0 type osd
+ step emit
+}
+
+rule rule-e1 {
+ id 1
+ type erasure
+ step take host0
+ step choose indep 0 type osd
+ step emit
+}
diff --git a/src/test/cli/crushtool/choose-args.crush b/src/test/cli/crushtool/choose-args.crush
new file mode 100644
index 000000000..06da21f32
--- /dev/null
+++ b/src/test/cli/crushtool/choose-args.crush
@@ -0,0 +1,118 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 device1
+device 2 device2
+
+# types
+type 0 device
+type 1 host
+type 2 rack
+type 3 root
+
+# buckets
+host host0 {
+ id -1 # do not change unnecessarily
+ # weight 1.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+host host1 {
+ id -2 # do not change unnecessarily
+ # weight 1.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item device1 weight 1.00000
+}
+host host2 {
+ id -5 # do not change unnecessarily
+ # weight 1.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item device2 weight 1.00000
+}
+rack rack0 {
+ id -3 # do not change unnecessarily
+ # weight 3.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 1.00000
+ item host2 weight 1.00000
+}
+root root {
+ id -4 # do not change unnecessarily
+ # weight 4.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item rack0 weight 4.00000
+}
+
+# rules
+rule data {
+ id 3
+ type replicated
+ step take root
+ step chooseleaf firstn 0 type rack
+ step emit
+}
+
+# choose_args
+choose_args 1 {
+}
+choose_args 2 {
+ {
+ bucket_id -3
+ ids [ -20 30 -25 ]
+ }
+}
+choose_args 3 {
+ {
+ bucket_id -3
+ weight_set [
+ [ 1.00000 2.00000 5.00000 ]
+ [ 3.00000 2.00000 5.00000 ]
+ ]
+ ids [ -20 -30 -25 ]
+ }
+}
+choose_args 4 {
+ {
+ bucket_id -2
+ weight_set [
+ [ 1.00000 ]
+ [ 3.00000 ]
+ ]
+ }
+}
+choose_args 5 {
+ {
+ bucket_id -1
+ ids [ -450 ]
+ }
+}
+choose_args 6 {
+ {
+ bucket_id -1
+ ids [ -450 ]
+ }
+ {
+ bucket_id -2
+ weight_set [
+ [ 1.00000 ]
+ [ 3.00000 ]
+ ]
+ }
+ {
+ bucket_id -3
+ weight_set [
+ [ 1.00000 2.00000 5.00000 ]
+ [ 3.00000 2.00000 5.00000 ]
+ ]
+ ids [ -20 -30 -25 ]
+ }
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/choose-args.t b/src/test/cli/crushtool/choose-args.t
new file mode 100644
index 000000000..e0956ec0a
--- /dev/null
+++ b/src/test/cli/crushtool/choose-args.t
@@ -0,0 +1,276 @@
+ $ cp "$TESTDIR/choose-args.crush" .
+ $ crushtool -c choose-args.crush -o choose-args.compiled
+ $ crushtool -d choose-args.compiled -o choose-args.conf
+ $ crushtool -c choose-args.conf -o choose-args.recompiled
+ $ cmp choose-args.crush choose-args.conf
+ $ cmp choose-args.compiled choose-args.recompiled
+ $ crushtool -c choose-args.conf -o /dev/null --dump
+ {
+ "devices": [
+ {
+ "id": 0,
+ "name": "device0"
+ },
+ {
+ "id": 1,
+ "name": "device1"
+ },
+ {
+ "id": 2,
+ "name": "device2"
+ }
+ ],
+ "types": [
+ {
+ "type_id": 0,
+ "name": "device"
+ },
+ {
+ "type_id": 1,
+ "name": "host"
+ },
+ {
+ "type_id": 2,
+ "name": "rack"
+ },
+ {
+ "type_id": 3,
+ "name": "root"
+ }
+ ],
+ "buckets": [
+ {
+ "id": -1,
+ "name": "host0",
+ "type_id": 1,
+ "type_name": "host",
+ "weight": 65536,
+ "alg": "straw2",
+ "hash": "rjenkins1",
+ "items": [
+ {
+ "id": 0,
+ "weight": 65536,
+ "pos": 0
+ }
+ ]
+ },
+ {
+ "id": -2,
+ "name": "host1",
+ "type_id": 1,
+ "type_name": "host",
+ "weight": 65536,
+ "alg": "straw2",
+ "hash": "rjenkins1",
+ "items": [
+ {
+ "id": 1,
+ "weight": 65536,
+ "pos": 0
+ }
+ ]
+ },
+ {
+ "id": -3,
+ "name": "rack0",
+ "type_id": 2,
+ "type_name": "rack",
+ "weight": 196608,
+ "alg": "straw2",
+ "hash": "rjenkins1",
+ "items": [
+ {
+ "id": -1,
+ "weight": 65536,
+ "pos": 0
+ },
+ {
+ "id": -2,
+ "weight": 65536,
+ "pos": 1
+ },
+ {
+ "id": -5,
+ "weight": 65536,
+ "pos": 2
+ }
+ ]
+ },
+ {
+ "id": -4,
+ "name": "root",
+ "type_id": 3,
+ "type_name": "root",
+ "weight": 262144,
+ "alg": "straw2",
+ "hash": "rjenkins1",
+ "items": [
+ {
+ "id": -3,
+ "weight": 262144,
+ "pos": 0
+ }
+ ]
+ },
+ {
+ "id": -5,
+ "name": "host2",
+ "type_id": 1,
+ "type_name": "host",
+ "weight": 65536,
+ "alg": "straw2",
+ "hash": "rjenkins1",
+ "items": [
+ {
+ "id": 2,
+ "weight": 65536,
+ "pos": 0
+ }
+ ]
+ }
+ ],
+ "rules": [
+ {
+ "rule_id": 3,
+ "rule_name": "data",
+ "type": 1,
+ "steps": [
+ {
+ "op": "take",
+ "item": -4,
+ "item_name": "root"
+ },
+ {
+ "op": "chooseleaf_firstn",
+ "num": 0,
+ "type": "rack"
+ },
+ {
+ "op": "emit"
+ }
+ ]
+ }
+ ],
+ "tunables": {
+ "choose_local_tries": 2,
+ "choose_local_fallback_tries": 5,
+ "choose_total_tries": 19,
+ "chooseleaf_descend_once": 0,
+ "chooseleaf_vary_r": 0,
+ "chooseleaf_stable": 0,
+ "straw_calc_version": 0,
+ "allowed_bucket_algs": 22,
+ "profile": "argonaut",
+ "optimal_tunables": 0,
+ "legacy_tunables": 1,
+ "minimum_required_version": "hammer",
+ "require_feature_tunables": 0,
+ "require_feature_tunables2": 0,
+ "has_v2_rules": 0,
+ "require_feature_tunables3": 0,
+ "has_v3_rules": 0,
+ "has_v4_buckets": 1,
+ "require_feature_tunables5": 0,
+ "has_v5_rules": 0
+ },
+ "choose_args": {
+ "1": [],
+ "2": [
+ {
+ "bucket_id": -3,
+ "ids": [
+ -20,
+ 30,
+ -25
+ ]
+ }
+ ],
+ "3": [
+ {
+ "bucket_id": -3,
+ "weight_set": [
+ [
+ 1,
+ 2,
+ 5
+ ],
+ [
+ 3,
+ 2,
+ 5
+ ]
+ ],
+ "ids": [
+ -20,
+ -30,
+ -25
+ ]
+ }
+ ],
+ "4": [
+ {
+ "bucket_id": -2,
+ "weight_set": [
+ [
+ 1
+ ],
+ [
+ 3
+ ]
+ ]
+ }
+ ],
+ "5": [
+ {
+ "bucket_id": -1,
+ "ids": [
+ -450
+ ]
+ }
+ ],
+ "6": [
+ {
+ "bucket_id": -1,
+ "ids": [
+ -450
+ ]
+ },
+ {
+ "bucket_id": -2,
+ "weight_set": [
+ [
+ 1
+ ],
+ [
+ 3
+ ]
+ ]
+ },
+ {
+ "bucket_id": -3,
+ "weight_set": [
+ [
+ 1,
+ 2,
+ 5
+ ],
+ [
+ 3,
+ 2,
+ 5
+ ]
+ ],
+ "ids": [
+ -20,
+ -30,
+ -25
+ ]
+ }
+ ]
+ }
+ }
+
+
+ $ crushtool -c choose-args.conf -o /dev/null --dump | jq .for_json_validation
+ null
diff --git a/src/test/cli/crushtool/compile-decompile-recompile.t b/src/test/cli/crushtool/compile-decompile-recompile.t
new file mode 100644
index 000000000..dac7ee9c4
--- /dev/null
+++ b/src/test/cli/crushtool/compile-decompile-recompile.t
@@ -0,0 +1,17 @@
+ $ cp "$TESTDIR/need_tree_order.crush" .
+ $ crushtool -c need_tree_order.crush -o nto.compiled
+ $ crushtool -d nto.compiled -o nto.conf
+ $ crushtool -c nto.conf -o nto.recompiled
+
+# as the input file is actually exactly what decompilation will spit
+# back out, comments and all, and the compiled format is completely
+# deterministic, we can compare the files to make sure everything
+# worked
+ $ cmp need_tree_order.crush nto.conf
+ $ cmp nto.compiled nto.recompiled
+
+ $ crushtool -c "$TESTDIR/missing-bucket.crushmap.txt"
+ WARNING: min_size is no longer supported, ignoring
+ WARNING: max_size is no longer supported, ignoring
+ in rule 'rule-bad' item 'root-404' not defined
+ [1]
diff --git a/src/test/cli/crushtool/crush-classes/a b/src/test/cli/crushtool/crush-classes/a
new file mode 100644
index 000000000..3e7d168e1
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/a
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/b b/src/test/cli/crushtool/crush-classes/b
new file mode 100644
index 000000000..68c900f65
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/b
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/beesly b/src/test/cli/crushtool/crush-classes/beesly
new file mode 100644
index 000000000..0048b148d
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/beesly
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/c b/src/test/cli/crushtool/crush-classes/c
new file mode 100644
index 000000000..e709cb9a3
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/c
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/d b/src/test/cli/crushtool/crush-classes/d
new file mode 100644
index 000000000..e43163673
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/d
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/e b/src/test/cli/crushtool/crush-classes/e
new file mode 100644
index 000000000..a1720c773
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/e
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/f b/src/test/cli/crushtool/crush-classes/f
new file mode 100644
index 000000000..c91e42666
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/f
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/flax b/src/test/cli/crushtool/crush-classes/flax
new file mode 100644
index 000000000..4f579dd7a
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/flax
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/g b/src/test/cli/crushtool/crush-classes/g
new file mode 100644
index 000000000..0e27a9d99
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/g
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/gabe b/src/test/cli/crushtool/crush-classes/gabe
new file mode 100644
index 000000000..90e8ed7b9
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/gabe
Binary files differ
diff --git a/src/test/cli/crushtool/crush-classes/gabe2 b/src/test/cli/crushtool/crush-classes/gabe2
new file mode 100644
index 000000000..c91e42666
--- /dev/null
+++ b/src/test/cli/crushtool/crush-classes/gabe2
Binary files differ
diff --git a/src/test/cli/crushtool/device-class.crush b/src/test/cli/crushtool/device-class.crush
new file mode 100644
index 000000000..adc4403aa
--- /dev/null
+++ b/src/test/cli/crushtool/device-class.crush
@@ -0,0 +1,86 @@
+# begin crush map
+
+# devices
+device 0 device0 class ssd
+device 1 device1 class ssd
+device 2 device2 class hdd
+
+# types
+type 0 device
+type 1 host
+type 2 rack
+type 3 root
+
+# buckets
+host host0 {
+ id -1 # do not change unnecessarily
+ id -6 class ssd # do not change unnecessarily
+ id -11 class hdd # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+host host1 {
+ id -2 # do not change unnecessarily
+ id -7 class ssd # do not change unnecessarily
+ id -12 class hdd # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device1 weight 1.00000
+}
+host host2 {
+ id -5 # do not change unnecessarily
+ id -8 class ssd # do not change unnecessarily
+ id -13 class hdd # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device2 weight 1.00000
+}
+rack rack0 {
+ id -3 # do not change unnecessarily
+ id -9 class ssd # do not change unnecessarily
+ id -14 class hdd # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 1.00000
+ item host2 weight 1.00000
+}
+root root {
+ id -4 # do not change unnecessarily
+ id -10 class ssd # do not change unnecessarily
+ id -15 class hdd # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item rack0 weight 4.00000
+}
+
+# rules
+rule data-ssd {
+ id 1
+ type replicated
+ step take root class ssd
+ step chooseleaf firstn 0 type rack
+ step emit
+}
+rule data-hdd {
+ id 2
+ type replicated
+ step take root class hdd
+ step chooseleaf firstn 0 type rack
+ step emit
+}
+rule data {
+ id 3
+ type replicated
+ step take root
+ step chooseleaf firstn 0 type rack
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/device-class.t b/src/test/cli/crushtool/device-class.t
new file mode 100644
index 000000000..9dcf95e1c
--- /dev/null
+++ b/src/test/cli/crushtool/device-class.t
@@ -0,0 +1,6 @@
+ $ cp "$TESTDIR/device-class.crush" .
+ $ crushtool -c device-class.crush -o device-class.compiled
+ $ crushtool -d device-class.compiled -o device-class.conf
+ $ crushtool -c device-class.conf -o device-class.recompiled
+ $ cmp device-class.crush device-class.conf
+ $ cmp device-class.compiled device-class.recompiled
diff --git a/src/test/cli/crushtool/empty-default.cushmap.txt b/src/test/cli/crushtool/empty-default.cushmap.txt
new file mode 100644
index 000000000..f52a2cb1d
--- /dev/null
+++ b/src/test/cli/crushtool/empty-default.cushmap.txt
@@ -0,0 +1,42 @@
+# begin crush map
+tunable choose_local_tries 0
+tunable choose_local_fallback_tries 0
+tunable choose_total_tries 50
+tunable chooseleaf_descend_once 1
+tunable chooseleaf_vary_r 1
+tunable chooseleaf_stable 0
+tunable straw_calc_version 1
+tunable allowed_bucket_algs 22
+
+# types
+type 0 osd
+type 1 host
+type 2 chassis
+type 3 rack
+type 4 row
+type 5 pdu
+type 6 pod
+type 7 room
+type 8 datacenter
+type 9 region
+type 10 root
+
+# default bucket
+root default {
+ id -1 # do not change unnecessarily
+ alg straw
+ hash 0 # rjenkins1
+}
+
+# rules
+rule replicated_rule {
+ id 0
+ type replicated
+ min_size 1
+ max_size 10
+ step take default
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/empty-default.t b/src/test/cli/crushtool/empty-default.t
new file mode 100644
index 000000000..e0becef7d
--- /dev/null
+++ b/src/test/cli/crushtool/empty-default.t
@@ -0,0 +1,4 @@
+ $ crushtool -c "$TESTDIR/empty-default.cushmap.txt"
+ WARNING: min_size is no longer supported, ignoring
+ WARNING: max_size is no longer supported, ignoring
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/five-devices.crushmap b/src/test/cli/crushtool/five-devices.crushmap
new file mode 100644
index 000000000..ced76fc37
--- /dev/null
+++ b/src/test/cli/crushtool/five-devices.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/help.t b/src/test/cli/crushtool/help.t
new file mode 100644
index 000000000..72085688c
--- /dev/null
+++ b/src/test/cli/crushtool/help.t
@@ -0,0 +1,165 @@
+ $ crushtool --help
+ usage: crushtool ...
+
+ Display, modify and test a crush map
+
+ There are five stages, running one after the other:
+
+ - input/build
+ - tunables adjustments
+ - modifications
+ - display/test
+ - output
+
+ Options that are not specific to a stage.
+
+ [--infn|-i infile]
+ read the crush map from infile
+
+ Options for the input/build stage
+
+ --decompile|-d map decompile a crush map to source
+ [--outfn|-o outfile]
+ specify output for for (de)compilation
+ --compile|-c map.txt compile a map from source
+ --enable-unsafe-tunables
+ compile with unsafe tunables
+ --build --num_osds N layer1 ...
+ build a new map, where each 'layer' is
+ 'name (uniform|straw2|straw|list|tree) size'
+
+ Options for the tunables adjustments stage
+
+ --set-choose-local-tries N
+ set choose local retries before re-descent
+ --set-choose-local-fallback-tries N
+ set choose local retries using fallback
+ permutation before re-descent
+ --set-choose-total-tries N
+ set choose total descent attempts
+ --set-chooseleaf-descend-once <0|1>
+ set chooseleaf to (not) retry the recursive descent
+ --set-chooseleaf-vary-r <0|1>
+ set chooseleaf to (not) vary r based on parent
+ --set-chooseleaf-stable <0|1>
+ set chooseleaf firstn to (not) return stable results
+
+ Options for the modifications stage
+
+ -i mapfn --add-item id weight name [--loc type name ...]
+ insert an item into the hierarchy at the
+ given location
+ -i mapfn --update-item id weight name [--loc type name ...]
+ insert or move an item into the hierarchy at the
+ given location
+ -i mapfn --remove-item name
+ remove the given item
+ -i mapfn --reweight-item name weight
+ reweight a given item (and adjust ancestor
+ weights as needed)
+ -i mapfn --add-bucket name type [--loc type name ...]
+ insert a bucket into the hierarchy at the given
+ location
+ -i mapfn --move name --loc type name ...
+ move the given item to specified location
+ -i mapfn --reweight recalculate all bucket weights
+ -i mapfn --rebuild-class-roots
+ rebuild the per-class shadow trees (normally a no-op)
+ -i mapfn --create-simple-rule name root type mode
+ create crush rule <name> to start from <root>,
+ replicate across buckets of type <type>, using
+ a choose mode of <firstn|indep>
+ -i mapfn --create-replicated-rule name root type
+ create crush rule <name> to start from <root>,
+ replicate across buckets of type <type>
+ --device-class <class>
+ use device class <class> for new rule
+ -i mapfn --remove-rule name
+ remove the specified crush rule
+
+ Options for the display/test stage
+
+ -f --format the format of --dump, defaults to json-pretty
+ can be one of json, json-pretty, xml, xml-pretty,
+ table, table-kv, html, html-pretty
+ --dump dump the crush map
+ --tree print map summary as a tree
+ --bucket-tree print bucket map summary as a tree
+ --bucket-name specify bucket bucket name for bucket-tree
+ --check [max_id] check if any item is referencing an unknown name/type
+ -i mapfn --show-location id
+ show location for given device id
+ -i mapfn --test test a range of inputs on the map
+ [--min-x x] [--max-x x] [--x x]
+ [--min-rule r] [--max-rule r] [--rule r]
+ [--min-rep n] [--max-rep n] [--num-rep n]
+ [--pool-id n] specifies pool id
+ [--batches b] split the CRUSH mapping into b > 1 rounds
+ [--weight|-w devno weight]
+ where weight is 0 to 1.0
+ [--simulate] simulate placements using a random
+ number generator in place of the CRUSH
+ algorithm
+ --show-utilization show OSD usage
+ --show-utilization-all
+ include zero weight items
+ --show-statistics show chi squared statistics
+ --show-mappings show mappings
+ --show-bad-mappings show bad mappings
+ --show-choose-tries show choose tries histogram
+ --output-name name
+ prepend the data file(s) generated during the
+ testing routine with name
+ --output-csv
+ export select data generated during testing routine
+ to CSV files for off-line post-processing
+ use --help-output for more information
+ --reclassify transform legacy CRUSH map buckets and rules
+ by adding classes
+ --reclassify-bucket <bucket-match> <class> <default-parent>
+ --reclassify-root <bucket-name> <class>
+ --set-subtree-class <bucket-name> <class>
+ set class for all items beneath bucket-name
+ --compare <otherfile> compare two maps using --test parameters
+
+ Options for the output stage
+
+ [--outfn|-o outfile]
+ specify output for modified crush map
+
+
+ $ crushtool --help-output
+ data output from testing routine ...
+ absolute_weights
+ the decimal weight of each OSD
+ data layout: ROW MAJOR
+ OSD id (int), weight (int)
+ batch_device_expected_utilization_all
+ the expected number of objects each OSD should receive per placement batch
+ which may be a decimal value
+ data layout: COLUMN MAJOR
+ round (int), objects expected on OSD 0...OSD n (float)
+ batch_device_utilization_all
+ the number of objects stored on each OSD during each placement round
+ data layout: COLUMN MAJOR
+ round (int), objects stored on OSD 0...OSD n (int)
+ device_utilization_all
+ the number of objects stored on each OSD at the end of placements
+ data_layout: ROW MAJOR
+ OSD id (int), objects stored (int), objects expected (float)
+ device_utilization
+ the number of objects stored on each OSD marked 'up' at the end of placements
+ data_layout: ROW MAJOR
+ OSD id (int), objects stored (int), objects expected (float)
+ placement_information
+ the map of input -> OSD
+ data_layout: ROW MAJOR
+ input (int), OSD's mapped (int)
+ proportional_weights_all
+ the proportional weight of each OSD specified in the CRUSH map
+ data_layout: ROW MAJOR
+ OSD id (int), proportional weight (float)
+ proportional_weights
+ the proportional weight of each 'up' OSD specified in the CRUSH map
+ data_layout: ROW MAJOR
+ OSD id (int), proportional weight (float)
diff --git a/src/test/cli/crushtool/location.t b/src/test/cli/crushtool/location.t
new file mode 100644
index 000000000..4252f5c27
--- /dev/null
+++ b/src/test/cli/crushtool/location.t
@@ -0,0 +1,16 @@
+ $ crushtool -i $TESTDIR/test-map-big-1.crushmap --show-location 44
+ $ crushtool -i $TESTDIR/test-map-big-1.crushmap --show-location 16
+ $ crushtool -i $TESTDIR/test-map-big-1.crushmap --show-location 167
+ host\tp05151113587529 (esc)
+ rack\tRJ45 (esc)
+ room\t0513-R-0050 (esc)
+ root\tdefault (esc)
+ $ crushtool -i $TESTDIR/test-map-big-1.crushmap --show-location 258
+ host\tlxfssi44a06 (esc)
+ rack\tSI44 (esc)
+ root\tcastor (esc)
+ $ crushtool -i $TESTDIR/test-map-big-1.crushmap --show-location 87
+ host\tp05151113576052 (esc)
+ rack\tRJ43 (esc)
+ room\t0513-R-0050 (esc)
+ root\tdefault (esc)
diff --git a/src/test/cli/crushtool/missing-bucket.crushmap.txt b/src/test/cli/crushtool/missing-bucket.crushmap.txt
new file mode 100644
index 000000000..80f44cd7a
--- /dev/null
+++ b/src/test/cli/crushtool/missing-bucket.crushmap.txt
@@ -0,0 +1,39 @@
+device 0 device0
+device 1 device1
+device 2 device2
+device 3 device3
+device 4 device4
+
+type 0 osd
+type 1 domain
+
+domain root {
+ id -1
+ alg straw
+ hash 0
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+ item device2 weight 1.00000
+ item device3 weight 1.00000
+ item device4 weight 1.00000
+}
+
+rule rule-bad {
+ id 0
+ type replicated
+ min_size 1
+ max_size 10
+ step take root-404
+ step choose firstn 0 type osd
+ step emit
+}
+
+rule rule-good {
+ id 1
+ type erasure
+ min_size 1
+ max_size 10
+ step take root
+ step choose indep 0 type osd
+ step emit
+}
diff --git a/src/test/cli/crushtool/multitype.after b/src/test/cli/crushtool/multitype.after
new file mode 100644
index 000000000..b571ad287
--- /dev/null
+++ b/src/test/cli/crushtool/multitype.after
@@ -0,0 +1,81 @@
+# begin crush map
+
+# devices
+device 0 osd0
+device 1 osd1
+device 2 osd2
+device 3 osd3
+device 4 osd4
+device 5 osd5
+device 6 osd6
+device 7 osd7
+device 8 osd8
+device 9 osd9
+
+# types
+type 0 osd
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item osd0 weight 2.00000
+ item osd1 weight 1.00000
+}
+host host1 {
+ id -3 # do not change unnecessarily
+ # weight 3.00000
+ alg list # add new items at the end; do not change order unnecessarily
+ hash 0 # rjenkins1
+ item osd3 weight 2.00000
+ item osd4 weight 1.00000
+}
+host host2 {
+ id -4 # do not change unnecessarily
+ # weight 5.50000
+ alg tree # do not change pos for existing items unnecessarily
+ hash 0 # rjenkins1
+ item osd5 weight 1.00000 pos 0
+ item osd6 weight 2.00000 pos 1
+ item osd7 weight 0.50000 pos 2
+ item osd8 weight 1.00000 pos 3
+ item osd9 weight 1.00000 pos 4
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 11.50000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 3.00000
+ item host1 weight 3.00000
+ item host2 weight 5.50000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/multitype.before b/src/test/cli/crushtool/multitype.before
new file mode 100644
index 000000000..30a1e6b9a
--- /dev/null
+++ b/src/test/cli/crushtool/multitype.before
@@ -0,0 +1,81 @@
+# begin crush map
+
+# devices
+device 0 osd0
+device 1 osd1
+device 2 osd2
+device 3 osd3
+device 4 osd4
+device 5 osd5
+device 6 osd6
+device 7 osd7
+device 8 osd8
+device 9 osd9
+
+# types
+type 0 osd
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item osd0 weight 1.00000
+ item osd1 weight 1.00000
+}
+host host1 {
+ id -3 # do not change unnecessarily
+ # weight 2.00000
+ alg list
+ hash 0 # rjenkins1
+ item osd3 weight 1.00000
+ item osd4 weight 1.00000
+}
+host host2 {
+ id -4 # do not change unnecessarily
+ # weight 2.00000
+ alg tree
+ hash 0 # rjenkins1
+ item osd5 weight 1.00000
+ item osd6 weight 1.00000
+ item osd7 weight 1.00000
+ item osd8 weight 1.00000
+ item osd9 weight 1.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 2.00000
+ item host1 weight 2.00000
+ item host2 weight 5.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/need_tree_order.crush b/src/test/cli/crushtool/need_tree_order.crush
new file mode 100644
index 000000000..7d7830064
--- /dev/null
+++ b/src/test/cli/crushtool/need_tree_order.crush
@@ -0,0 +1,62 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 device1
+device 2 device2
+
+# types
+type 0 device
+type 1 host
+type 2 rack
+type 3 root
+
+# buckets
+host host0 {
+ id -1 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+host host1 {
+ id -2 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device1 weight 1.00000
+}
+host host2 {
+ id -5 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device2 weight 1.00000
+}
+rack rack0 {
+ id -3 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 1.00000
+ item host2 weight 1.00000
+}
+root root {
+ id -4 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item rack0 weight 4.00000
+}
+
+# rules
+rule data {
+ id 1
+ type replicated
+ step take root
+ step chooseleaf firstn 0 type rack
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/output-csv.t b/src/test/cli/crushtool/output-csv.t
new file mode 100644
index 000000000..0769dacd5
--- /dev/null
+++ b/src/test/cli/crushtool/output-csv.t
@@ -0,0 +1,51 @@
+# first test that CSV files are created for each rule
+$ crushtool -i five-devices.crushmap --test --num-rep 1 --min-x 0 --max-x 9 --output-csv
+$ if [ ! -f data-absolute_weights.csv ]; then echo FAIL; fi
+$ if [ ! -f data-batch_device_expected_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f data-batch_device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f data-device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f data-device_utilization.csv ]; then echo FAIL; fi
+$ if [ ! -f data-placement_information.csv ]; then echo FAIL; fi
+$ if [ ! -f data-proportional_weights_all.csv ]; then echo FAIL; fi
+$ if [ ! -f data-proportional_weights.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-absolute_weights.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-batch_device_expected_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-batch_device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-device_utilization.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-placement_information.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-proportional_weights_all.csv ]; then echo FAIL; fi
+$ if [ ! -f metadata-proportional_weights.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-absolute_weights.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-batch_device_expected_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-batch_device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-device_utilization.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-placement_information.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-proportional_weights_all.csv ]; then echo FAIL; fi
+$ if [ ! -f rbd-proportional_weights.csv ]; then echo FAIL; fi
+$ rm data*csv
+$ rm metadata*csv
+$ rm rbd*csv
+# now check that the CSV files are made to the proper length
+$ crushtool -i five-devices.crushmap --test --rule 0 --num-rep 1 --min-x 0 --max-x 9 --output-csv
+$ if [ $(wc -l data-absolute_weights.csv | awk '{print $1}') != "5" ]; then echo FAIL; fi
+$ if [ $(wc -l data-batch_device_expected_utilization_all.csv | awk '{print $1}') != "5" ]; then echo FAIL; fi
+$ if [ $(wc -l data-batch_device_utilization_all.csv | awk '{print $1}') != "5" ]; then echo FAIL; fi
+$ if [ $(wc -l data-device_utilization_all.csv | awk '{print $1}') != "5" ]; then echo FAIL; fi
+$ if [ $(wc -l data-device_utilization.csv | awk '{print $1}') != "5" ]; then echo FAIL; fi
+$ if [ $(wc -l data-placement_information.csv | awk '{print $1}') != "10" ]; then echo FAIL; fi
+$ if [ $(wc -l data-proportional_weights_all.csv | awk '{print $1}') != "5" ]; then echo FAIL; fi
+$ if [ $(wc -l data-proportional_weights.csv | awk '{print $1}') != "5" ]; then echo FAIL; fi
+$ rm data*csv
+# finally check that user supplied tags are prepended correctly
+$ crushtool -i five-devices.crushmap --test --rule 0 --num-rep 1 --min-x 0 --max-x 9 --output-name "test-tag" --output-csv
+$ if [ ! -f test-tag-data-absolute_weights.csv ]; then echo FAIL; fi
+$ if [ ! -f test-tag-data-batch_device_expected_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f test-tag-data-batch_device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f test-tag-data-device_utilization_all.csv ]; then echo FAIL; fi
+$ if [ ! -f test-tag-data-device_utilization.csv ]; then echo FAIL; fi
+$ if [ ! -f test-tag-data-placement_information.csv ]; then echo FAIL; fi
+$ if [ ! -f test-tag-data-proportional_weights_all.csv ]; then echo FAIL; fi
+$ if [ ! -f test-tag-data-proportional_weights.csv ]; then echo FAIL; fi
+$ rm test-tag*csv
diff --git a/src/test/cli/crushtool/reclassify.t b/src/test/cli/crushtool/reclassify.t
new file mode 100644
index 000000000..ea04ff10e
--- /dev/null
+++ b/src/test/cli/crushtool/reclassify.t
@@ -0,0 +1,562 @@
+ $ crushtool -i $TESTDIR/crush-classes/a --set-subtree-class default hdd --reclassify --reclassify-bucket %-ssd ssd default --reclassify-bucket ssd ssd default --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ renumbering bucket -1 -> -5
+ renumbering bucket -4 -> -6
+ renumbering bucket -3 -> -7
+ renumbering bucket -2 -> -8
+ classify_bucket %-ssd as ssd default bucket default (root)
+ match %-ssd to ttipod001-cephosd-2-ssd basename ttipod001-cephosd-2
+ have base -8
+ match %-ssd to ttipod001-cephosd-1-ssd basename ttipod001-cephosd-1
+ have base -7
+ match %-ssd to ttipod001-cephosd-3-ssd basename ttipod001-cephosd-3
+ have base -6
+ classify_bucket ssd as ssd default bucket default (root)
+ match ssd to ssd basename default
+ have base -5
+ moving items from -24 (ttipod001-cephosd-3-ssd) to -6 (ttipod001-cephosd-3)
+ moving items from -23 (ttipod001-cephosd-1-ssd) to -7 (ttipod001-cephosd-1)
+ moving items from -22 (ttipod001-cephosd-2-ssd) to -8 (ttipod001-cephosd-2)
+ moving items from -21 (ssd) to -5 (default)
+ $ crushtool -i $TESTDIR/crush-classes/a --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 0/10240 mismatched mappings (0)
+ rule 1 had 0/10240 mismatched mappings (0)
+ maps appear equivalent
+
+ $ crushtool -i $TESTDIR/crush-classes/d --set-subtree-class default hdd --reclassify --reclassify-bucket %-ssd ssd default --reclassify-bucket ssd ssd default --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ renumbering bucket -1 -> -13
+ renumbering bucket -6 -> -14
+ renumbering bucket -5 -> -15
+ renumbering bucket -4 -> -16
+ renumbering bucket -3 -> -17
+ renumbering bucket -2 -> -18
+ classify_bucket %-ssd as ssd default bucket default (root)
+ match %-ssd to node-20-ssd basename node-20
+ have base -18
+ match %-ssd to node-21-ssd basename node-21
+ created base -25
+ match %-ssd to node-22-ssd basename node-22
+ created base -26
+ match %-ssd to node-23-ssd basename node-23
+ created base -27
+ match %-ssd to node-27-ssd basename node-27
+ created base -28
+ classify_bucket ssd as ssd default bucket default (root)
+ match ssd to ssd basename default
+ have base -13
+ moving items from -12 (node-27-ssd) to -28 (node-27)
+ moving items from -11 (node-23-ssd) to -27 (node-23)
+ moving items from -10 (node-22-ssd) to -26 (node-22)
+ moving items from -9 (node-21-ssd) to -25 (node-21)
+ moving items from -8 (node-20-ssd) to -18 (node-20)
+ moving items from -7 (ssd) to -13 (default)
+ $ crushtool -i $TESTDIR/crush-classes/d --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 0/10240 mismatched mappings (0)
+ rule 1 had 0/10240 mismatched mappings (0)
+ maps appear equivalent
+
+ $ crushtool -i $TESTDIR/crush-classes/e --reclassify --reclassify-bucket ceph-osd-ssd-% ssd default --reclassify-bucket ssd-root ssd default --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ renumbering bucket -1 -> -55
+ renumbering bucket -34 -> -56
+ renumbering bucket -20 -> -57
+ renumbering bucket -14 -> -58
+ renumbering bucket -15 -> -59
+ renumbering bucket -16 -> -60
+ renumbering bucket -52 -> -61
+ renumbering bucket -46 -> -62
+ renumbering bucket -40 -> -63
+ classify_bucket ceph-osd-ssd-% as ssd default bucket default (root)
+ match ceph-osd-ssd-% to ceph-osd-ssd-node4 basename node4
+ have base -57
+ match ceph-osd-ssd-% to ceph-osd-ssd-node3 basename node3
+ have base -58
+ match ceph-osd-ssd-% to ceph-osd-ssd-node1 basename node1
+ have base -60
+ match ceph-osd-ssd-% to ceph-osd-ssd-node2 basename node2
+ have base -59
+ match ceph-osd-ssd-% to ceph-osd-ssd-node5 basename node5
+ have base -56
+ match ceph-osd-ssd-% to ceph-osd-ssd-node6 basename node6
+ have base -63
+ match ceph-osd-ssd-% to ceph-osd-ssd-node7 basename node7
+ have base -62
+ match ceph-osd-ssd-% to ceph-osd-ssd-node8 basename node8
+ have base -61
+ classify_bucket ssd-root as ssd default bucket default (root)
+ match ssd-root to ssd-root basename default
+ have base -55
+ moving items from -49 (ceph-osd-ssd-node8) to -61 (node8)
+ moving items from -43 (ceph-osd-ssd-node7) to -62 (node7)
+ moving items from -37 (ceph-osd-ssd-node6) to -63 (node6)
+ moving items from -31 (ceph-osd-ssd-node5) to -56 (node5)
+ moving items from -18 (ssd-root) to -55 (default)
+ moving items from -9 (ceph-osd-ssd-node2) to -59 (node2)
+ moving items from -7 (ceph-osd-ssd-node1) to -60 (node1)
+ moving items from -5 (ceph-osd-ssd-node3) to -58 (node3)
+ moving items from -3 (ceph-osd-ssd-node4) to -57 (node4)
+
+this one has weird node weights, so *lots* of mappings change...
+
+ $ crushtool -i $TESTDIR/crush-classes/e --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 6540/10240 mismatched mappings (0.638672)
+ rule 1 had 8417/10240 mismatched mappings (0.821973)
+ warning: maps are NOT equivalent
+ [1]
+
+ $ crushtool -i $TESTDIR/crush-classes/c --reclassify --reclassify-bucket %-SSD ssd default --reclassify-bucket ssd ssd default --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ renumbering bucket -1 -> -55
+ renumbering bucket -9 -> -56
+ renumbering bucket -8 -> -57
+ renumbering bucket -7 -> -58
+ renumbering bucket -6 -> -59
+ renumbering bucket -5 -> -60
+ renumbering bucket -4 -> -61
+ renumbering bucket -3 -> -62
+ renumbering bucket -2 -> -63
+ classify_bucket %-SSD as ssd default bucket default (root)
+ match %-SSD to Ceph-Stor1-SSD basename Ceph-Stor1
+ have base -63
+ match %-SSD to Ceph-Stor2-SSD basename Ceph-Stor2
+ have base -62
+ match %-SSD to Ceph-Stor3-SSD basename Ceph-Stor3
+ have base -61
+ match %-SSD to Ceph-Stor4-SSD basename Ceph-Stor4
+ have base -60
+ match %-SSD to Ceph-Stor5-SSD basename Ceph-Stor5
+ have base -59
+ match %-SSD to Ceph-Stor6-SSD basename Ceph-Stor6
+ have base -58
+ match %-SSD to Ceph-Stor7-SSD basename Ceph-Stor7
+ have base -57
+ match %-SSD to Ceph-Stor8-SSD basename Ceph-Stor8
+ have base -56
+ classify_bucket ssd as ssd default bucket default (root)
+ match ssd to ssd basename default
+ have base -55
+ moving items from -18 (ssd) to -55 (default)
+ moving items from -17 (Ceph-Stor8-SSD) to -56 (Ceph-Stor8)
+ moving items from -16 (Ceph-Stor7-SSD) to -57 (Ceph-Stor7)
+ moving items from -15 (Ceph-Stor6-SSD) to -58 (Ceph-Stor6)
+ moving items from -14 (Ceph-Stor5-SSD) to -59 (Ceph-Stor5)
+ moving items from -13 (Ceph-Stor4-SSD) to -60 (Ceph-Stor4)
+ moving items from -12 (Ceph-Stor3-SSD) to -61 (Ceph-Stor3)
+ moving items from -11 (Ceph-Stor2-SSD) to -62 (Ceph-Stor2)
+ moving items from -10 (Ceph-Stor1-SSD) to -63 (Ceph-Stor1)
+
+wonky crush weights on Ceph-Stor1, so a small number of mappings change
+because the new map has a strictly summing hierarchy.
+
+ $ crushtool -i $TESTDIR/crush-classes/c --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 158/10240 mismatched mappings (0.0154297)
+ rule 1 had 138/10240 mismatched mappings (0.0134766)
+ rule 2 had 0/10240 mismatched mappings (0)
+ warning: maps are NOT equivalent
+ [1]
+
+ $ crushtool -i $TESTDIR/crush-classes/beesly --set-subtree-class 0513-R-0060 hdd --set-subtree-class 0513-R-0050 hdd --reclassify --reclassify-root 0513-R-0050 hdd --reclassify-root 0513-R-0060 hdd -o foo
+ classify_root 0513-R-0050 (-2) as hdd
+ renumbering bucket -2 -> -131
+ renumbering bucket -14 -> -132
+ renumbering bucket -34 -> -133
+ renumbering bucket -33 -> -134
+ renumbering bucket -30 -> -135
+ renumbering bucket -26 -> -136
+ renumbering bucket -22 -> -137
+ renumbering bucket -18 -> -138
+ renumbering bucket -13 -> -139
+ renumbering bucket -9 -> -140
+ renumbering bucket -12 -> -141
+ renumbering bucket -11 -> -142
+ renumbering bucket -32 -> -143
+ renumbering bucket -31 -> -144
+ renumbering bucket -10 -> -145
+ renumbering bucket -8 -> -146
+ renumbering bucket -6 -> -147
+ renumbering bucket -28 -> -148
+ renumbering bucket -27 -> -149
+ renumbering bucket -21 -> -150
+ renumbering bucket -20 -> -151
+ renumbering bucket -19 -> -152
+ renumbering bucket -7 -> -153
+ renumbering bucket -5 -> -154
+ renumbering bucket -4 -> -155
+ renumbering bucket -25 -> -156
+ renumbering bucket -24 -> -157
+ renumbering bucket -23 -> -158
+ renumbering bucket -17 -> -159
+ renumbering bucket -16 -> -160
+ renumbering bucket -15 -> -161
+ renumbering bucket -3 -> -162
+ renumbering bucket -72 -> -163
+ renumbering bucket -98 -> -164
+ renumbering bucket -97 -> -165
+ renumbering bucket -96 -> -166
+ renumbering bucket -95 -> -167
+ renumbering bucket -94 -> -168
+ renumbering bucket -93 -> -169
+ renumbering bucket -68 -> -170
+ classify_root 0513-R-0060 (-65) as hdd
+ renumbering bucket -65 -> -35
+ renumbering bucket -76 -> -36
+ renumbering bucket -78 -> -37
+ renumbering bucket -87 -> -38
+ renumbering bucket -82 -> -39
+ renumbering bucket -81 -> -40
+ renumbering bucket -77 -> -41
+ renumbering bucket -75 -> -42
+ renumbering bucket -89 -> -43
+ renumbering bucket -85 -> -44
+ renumbering bucket -84 -> -45
+ renumbering bucket -74 -> -46
+ renumbering bucket -71 -> -47
+ renumbering bucket -80 -> -48
+ renumbering bucket -91 -> -49
+ renumbering bucket -90 -> -50
+ renumbering bucket -88 -> -51
+ renumbering bucket -79 -> -52
+ renumbering bucket -70 -> -53
+ renumbering bucket -86 -> -54
+ renumbering bucket -83 -> -55
+ renumbering bucket -73 -> -56
+ renumbering bucket -69 -> -57
+ $ crushtool -i $TESTDIR/crush-classes/beesly --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 0/10240 mismatched mappings (0)
+ rule 1 had 0/10240 mismatched mappings (0)
+ rule 2 had 0/10240 mismatched mappings (0)
+ rule 4 had 0/10240 mismatched mappings (0)
+ maps appear equivalent
+
+ $ crushtool -i $TESTDIR/crush-classes/flax --reclassify --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ renumbering bucket -1 -> -5
+ renumbering bucket -12 -> -7
+ renumbering bucket -9 -> -8
+ renumbering bucket -6 -> -10
+ renumbering bucket -4 -> -11
+ renumbering bucket -3 -> -13
+ renumbering bucket -2 -> -14
+ $ crushtool -i $TESTDIR/crush-classes/flax --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 0/10240 mismatched mappings (0)
+ maps appear equivalent
+
+ $ crushtool -i $TESTDIR/crush-classes/gabe --reclassify --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ rule 3 includes take on root default class 0
+ failed to reclassify map
+ [1]
+
+above fails because of ec-rack-by-2-hdd also has take default class hdd.
+
+below is an adjusted version of the same cluster's map
+
+ $ crushtool -i $TESTDIR/crush-classes/gabe2 --reclassify --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ renumbering bucket -1 -> -178
+ renumbering bucket -4 -> -179
+ renumbering bucket -25 -> -180
+ renumbering bucket -16 -> -181
+ renumbering bucket -21 -> -182
+ renumbering bucket -19 -> -183
+ renumbering bucket -15 -> -184
+ renumbering bucket -7 -> -185
+ renumbering bucket -47 -> -186
+ renumbering bucket -18 -> -187
+ renumbering bucket -8 -> -188
+ renumbering bucket -6 -> -189
+ renumbering bucket -12 -> -190
+ renumbering bucket -23 -> -191
+ renumbering bucket -22 -> -192
+ renumbering bucket -20 -> -193
+ renumbering bucket -11 -> -194
+ renumbering bucket -10 -> -195
+ renumbering bucket -17 -> -196
+ renumbering bucket -13 -> -197
+ renumbering bucket -9 -> -198
+ renumbering bucket -3 -> -199
+ renumbering bucket -14 -> -200
+ renumbering bucket -5 -> -201
+ renumbering bucket -2 -> -202
+ $ crushtool -i $TESTDIR/crush-classes/gabe2 --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 627/10240 mismatched mappings (0.0612305)
+ rule 1 had 652/10240 mismatched mappings (0.0636719)
+ warning: maps are NOT equivalent
+ [1]
+
+
+
+ $ crushtool -i $TESTDIR/crush-classes/b --reclassify --reclassify-bucket %-hdd hdd default --reclassify-bucket %-ssd ssd default --reclassify-bucket ssd ssd default --reclassify-bucket hdd hdd default -o foo
+ classify_bucket %-hdd as hdd default bucket default (root)
+ match %-hdd to berta-hdd basename berta
+ have base -37
+ match %-hdd to oelgard-hdd basename oelgard
+ have base -36
+ match %-hdd to leonhard-hdd basename leonhard
+ have base -33
+ match %-hdd to gottlieb-hdd basename gottlieb
+ have base -30
+ match %-hdd to hieronymus-hdd basename hieronymus
+ have base -31
+ match %-hdd to uhu-hdd basename uhu
+ have base -34
+ match %-hdd to euphrosyne-hdd basename euphrosyne
+ have base -35
+ match %-hdd to frauenhaus-hdd basename frauenhaus
+ created base -145
+ match %-hdd to herrenhaus-hdd basename herrenhaus
+ created base -146
+ match %-hdd to zoo-hdd basename zoo
+ created base -147
+ match %-hdd to borkenkaefer-hdd basename borkenkaefer
+ have base -4
+ match %-hdd to hirsch-hdd basename hirsch
+ have base -41
+ match %-hdd to cassowary-hdd basename cassowary
+ created base -148
+ match %-hdd to fuchs-hdd basename fuchs
+ created base -149
+ match %-hdd to analia-hdd basename analia
+ created base -150
+ match %-hdd to gundula-hdd basename gundula
+ created base -151
+ match %-hdd to achim-hdd basename achim
+ created base -152
+ match %-hdd to hugo-hdd basename hugo
+ created base -153
+ match %-hdd to carl-hdd basename carl
+ have base -32
+ classify_bucket %-ssd as ssd default bucket default (root)
+ match %-ssd to frauenhaus-ssd basename frauenhaus
+ already creating base -145
+ match %-ssd to herrenhaus-ssd basename herrenhaus
+ already creating base -146
+ match %-ssd to zoo-ssd basename zoo
+ already creating base -147
+ match %-ssd to berta-ssd basename berta
+ have base -37
+ match %-ssd to euphrosyne-ssd basename euphrosyne
+ have base -35
+ match %-ssd to oelgard-ssd basename oelgard
+ have base -36
+ match %-ssd to leonhard-ssd basename leonhard
+ have base -33
+ match %-ssd to hieronymus-ssd basename hieronymus
+ have base -31
+ match %-ssd to gottlieb-ssd basename gottlieb
+ have base -30
+ match %-ssd to uhu-ssd basename uhu
+ have base -34
+ match %-ssd to borkenkaefer-ssd basename borkenkaefer
+ have base -4
+ match %-ssd to hirsch-ssd basename hirsch
+ have base -41
+ match %-ssd to phaidon-ssd basename phaidon
+ created base -154
+ match %-ssd to glykera-ssd basename glykera
+ created base -155
+ match %-ssd to bonobo-ssd basename bonobo
+ created base -156
+ classify_bucket hdd as hdd default bucket default (root)
+ match hdd to hdd basename default
+ have base -1
+ classify_bucket ssd as ssd default bucket default (root)
+ match ssd to ssd basename default
+ have base -1
+ moving items from -124 (bonobo-ssd) to -156 (bonobo)
+ moving items from -123 (glykera-ssd) to -155 (glykera)
+ moving items from -122 (phaidon-ssd) to -154 (phaidon)
+ moving items from -121 (carl-hdd) to -32 (carl)
+ moving items from -120 (hugo-hdd) to -153 (hugo)
+ moving items from -119 (achim-hdd) to -152 (achim)
+ moving items from -118 (gundula-hdd) to -151 (gundula)
+ moving items from -117 (analia-hdd) to -150 (analia)
+ moving items from -116 (fuchs-hdd) to -149 (fuchs)
+ moving items from -115 (cassowary-hdd) to -148 (cassowary)
+ moving items from -39 (hirsch-ssd) to -41 (hirsch)
+ moving items from -38 (hirsch-hdd) to -41 (hirsch)
+ moving items from -29 (borkenkaefer-ssd) to -4 (borkenkaefer)
+ moving items from -28 (hdd) to -1 (default)
+ moving items from -27 (ssd) to -1 (default)
+ moving items from -26 (uhu-ssd) to -34 (uhu)
+ moving items from -25 (gottlieb-ssd) to -30 (gottlieb)
+ moving items from -24 (hieronymus-ssd) to -31 (hieronymus)
+ moving items from -23 (leonhard-ssd) to -33 (leonhard)
+ moving items from -22 (borkenkaefer-hdd) to -4 (borkenkaefer)
+ moving items from -21 (oelgard-ssd) to -36 (oelgard)
+ moving items from -20 (euphrosyne-ssd) to -35 (euphrosyne)
+ moving items from -19 (berta-ssd) to -37 (berta)
+ moving items from -17 (zoo-ssd) to -147 (zoo)
+ moving items from -16 (herrenhaus-ssd) to -146 (herrenhaus)
+ moving items from -15 (frauenhaus-ssd) to -145 (frauenhaus)
+ moving items from -12 (zoo-hdd) to -147 (zoo)
+ moving items from -11 (herrenhaus-hdd) to -146 (herrenhaus)
+ moving items from -10 (frauenhaus-hdd) to -145 (frauenhaus)
+ moving items from -9 (euphrosyne-hdd) to -35 (euphrosyne)
+ moving items from -8 (uhu-hdd) to -34 (uhu)
+ moving items from -7 (hieronymus-hdd) to -31 (hieronymus)
+ moving items from -6 (gottlieb-hdd) to -30 (gottlieb)
+ moving items from -5 (leonhard-hdd) to -33 (leonhard)
+ moving items from -3 (oelgard-hdd) to -36 (oelgard)
+ moving items from -2 (berta-hdd) to -37 (berta)
+ new bucket -156 missing parent, adding at {root=default}
+ new bucket -155 missing parent, adding at {root=default}
+ new bucket -154 missing parent, adding at {root=default}
+
+ $ crushtool -i $TESTDIR/crush-classes/b --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 0/10240 mismatched mappings (0)
+ rule 1 had 0/10240 mismatched mappings (0)
+ maps appear equivalent
+
+ $ crushtool -i $TESTDIR/crush-classes/f --reclassify --reclassify-root default hdd -o foo
+ classify_root default (-1) as hdd
+ renumbering bucket -1 -> -178
+ renumbering bucket -4 -> -179
+ renumbering bucket -25 -> -180
+ renumbering bucket -16 -> -181
+ renumbering bucket -21 -> -182
+ renumbering bucket -19 -> -183
+ renumbering bucket -15 -> -184
+ renumbering bucket -7 -> -185
+ renumbering bucket -47 -> -186
+ renumbering bucket -18 -> -187
+ renumbering bucket -8 -> -188
+ renumbering bucket -6 -> -189
+ renumbering bucket -12 -> -190
+ renumbering bucket -23 -> -191
+ renumbering bucket -22 -> -192
+ renumbering bucket -20 -> -193
+ renumbering bucket -11 -> -194
+ renumbering bucket -10 -> -195
+ renumbering bucket -17 -> -196
+ renumbering bucket -13 -> -197
+ renumbering bucket -9 -> -198
+ renumbering bucket -3 -> -199
+ renumbering bucket -14 -> -200
+ renumbering bucket -5 -> -201
+ renumbering bucket -2 -> -202
+
+We expect some mismatches below because there are some ssd-labeled nodes under
+default that we aren't changing the class on.
+
+ $ crushtool -i $TESTDIR/crush-classes/f --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 627/10240 mismatched mappings (0.0612305)
+ rule 1 had 652/10240 mismatched mappings (0.0636719)
+ warning: maps are NOT equivalent
+ [1]
+
+ $ crushtool -i $TESTDIR/crush-classes/g --reclassify --reclassify-bucket sata-% hdd-sata default --reclassify-bucket sas-% hdd-sas default --reclassify-bucket sas hdd-sas default --reclassify-bucket sata hdd-sata default -o foo
+ classify_bucket sas as hdd-sas default bucket default (root)
+ match sas to sas basename default
+ have base -1
+ classify_bucket sas-% as hdd-sas default bucket default (root)
+ match sas-% to sas-osd01 basename osd01
+ created base -73
+ match sas-% to sas-osd02 basename osd02
+ created base -74
+ match sas-% to sas-osd03 basename osd03
+ created base -75
+ match sas-% to sas-osd04 basename osd04
+ created base -76
+ match sas-% to sas-osd05 basename osd05
+ created base -77
+ match sas-% to sas-osd06 basename osd06
+ created base -78
+ match sas-% to sas-osd07 basename osd07
+ created base -79
+ match sas-% to sas-osd08 basename osd08
+ created base -80
+ match sas-% to sas-osd09 basename osd09
+ created base -81
+ match sas-% to sas-rack1 basename rack1
+ created base -82
+ match sas-% to sas-rack2 basename rack2
+ created base -83
+ match sas-% to sas-rack3 basename rack3
+ created base -84
+ classify_bucket sata as hdd-sata default bucket default (root)
+ match sata to sata basename default
+ have base -1
+ classify_bucket sata-% as hdd-sata default bucket default (root)
+ match sata-% to sata-osd11 basename osd11
+ created base -85
+ match sata-% to sata-osd10 basename osd10
+ created base -86
+ match sata-% to sata-osd14 basename osd14
+ created base -87
+ match sata-% to sata-osd13 basename osd13
+ created base -88
+ match sata-% to sata-osd12 basename osd12
+ created base -89
+ match sata-% to sata-osd15 basename osd15
+ created base -90
+ match sata-% to sata-osd16 basename osd16
+ created base -91
+ match sata-% to sata-osd18 basename osd18
+ created base -92
+ match sata-% to sata-osd19 basename osd19
+ created base -93
+ match sata-% to sata-osd17 basename osd17
+ created base -94
+ match sata-% to sata-osd20 basename osd20
+ created base -95
+ match sata-% to sata-osd21 basename osd21
+ created base -96
+ match sata-% to sata-osd22 basename osd22
+ created base -97
+ match sata-% to sata-osd23 basename osd23
+ created base -98
+ match sata-% to sata-osd24 basename osd24
+ created base -99
+ match sata-% to sata-osd25 basename osd25
+ created base -100
+ match sata-% to sata-osd26 basename osd26
+ created base -101
+ match sata-% to sata-osd27 basename osd27
+ created base -102
+ match sata-% to sata-rack1 basename rack1
+ already creating base -82
+ match sata-% to sata-rack2 basename rack2
+ already creating base -83
+ match sata-% to sata-rack3 basename rack3
+ already creating base -84
+ moving items from -36 (sas) to -1 (default)
+ moving items from -35 (sata) to -1 (default)
+ moving items from -34 (sas-rack3) to -84 (rack3)
+ moving items from -33 (sas-rack2) to -83 (rack2)
+ moving items from -32 (sas-rack1) to -82 (rack1)
+ moving items from -31 (sata-rack3) to -84 (rack3)
+ moving items from -30 (sata-rack2) to -83 (rack2)
+ moving items from -29 (sata-rack1) to -82 (rack1)
+ moving items from -28 (sas-osd09) to -81 (osd09)
+ moving items from -27 (sas-osd08) to -80 (osd08)
+ moving items from -26 (sas-osd07) to -79 (osd07)
+ moving items from -25 (sas-osd06) to -78 (osd06)
+ moving items from -24 (sas-osd05) to -77 (osd05)
+ moving items from -23 (sas-osd04) to -76 (osd04)
+ moving items from -22 (sas-osd03) to -75 (osd03)
+ moving items from -21 (sas-osd02) to -74 (osd02)
+ moving items from -20 (sata-osd27) to -102 (osd27)
+ moving items from -19 (sas-osd01) to -73 (osd01)
+ moving items from -18 (sata-osd26) to -101 (osd26)
+ moving items from -17 (sata-osd25) to -100 (osd25)
+ moving items from -16 (sata-osd24) to -99 (osd24)
+ moving items from -15 (sata-osd23) to -98 (osd23)
+ moving items from -14 (sata-osd22) to -97 (osd22)
+ moving items from -13 (sata-osd21) to -96 (osd21)
+ moving items from -12 (sata-osd20) to -95 (osd20)
+ moving items from -11 (sata-osd17) to -94 (osd17)
+ moving items from -10 (sata-osd19) to -93 (osd19)
+ moving items from -9 (sata-osd18) to -92 (osd18)
+ moving items from -8 (sata-osd16) to -91 (osd16)
+ moving items from -7 (sata-osd15) to -90 (osd15)
+ moving items from -6 (sata-osd12) to -89 (osd12)
+ moving items from -5 (sata-osd13) to -88 (osd13)
+ moving items from -4 (sata-osd14) to -87 (osd14)
+ moving items from -3 (sata-osd10) to -86 (osd10)
+ moving items from -2 (sata-osd11) to -85 (osd11)
+ $ crushtool -i $TESTDIR/crush-classes/g --compare foo --min-rep 1 --max-rep 10
+ rule 0 had 0/10240 mismatched mappings (0)
+ rule 1 had 0/10240 mismatched mappings (0)
+ maps appear equivalent
diff --git a/src/test/cli/crushtool/reweight.t b/src/test/cli/crushtool/reweight.t
new file mode 100644
index 000000000..f4da5c9cb
--- /dev/null
+++ b/src/test/cli/crushtool/reweight.t
@@ -0,0 +1,8 @@
+ $ crushtool -c "$TESTDIR/multitype.before" -o mt > /dev/null
+ $ crushtool -i mt --reweight-item osd0 2.0 -o mt > /dev/null
+ $ crushtool -i mt --reweight-item osd3 2.0 -o mt > /dev/null
+ $ crushtool -i mt --reweight-item osd6 2.0 -o mt > /dev/null
+ $ crushtool -i mt --reweight-item osd7 .5 -o mt > /dev/null
+ $ crushtool -d mt -o final
+ $ diff final "$TESTDIR/multitype.after"
+ $ rm mt final
diff --git a/src/test/cli/crushtool/reweight_multiple.t b/src/test/cli/crushtool/reweight_multiple.t
new file mode 100644
index 000000000..7eff6733c
--- /dev/null
+++ b/src/test/cli/crushtool/reweight_multiple.t
@@ -0,0 +1,5 @@
+ $ crushtool -c "$TESTDIR/simple.template.multitree" -o mt
+ $ crushtool -i mt --reweight-item osd1 2.5 -o mt
+ crushtool reweighting item osd1 to 2.5
+ $ crushtool -d mt -o mt.txt
+ $ diff mt.txt "$TESTDIR/simple.template.multitree.reweighted"
diff --git a/src/test/cli/crushtool/rules.t b/src/test/cli/crushtool/rules.t
new file mode 100644
index 000000000..2db354f20
--- /dev/null
+++ b/src/test/cli/crushtool/rules.t
@@ -0,0 +1,155 @@
+ $ crushtool -c $TESTDIR/rules.txt --create-replicated-rule foo default host -o one > /dev/null
+ WARNING: min_size is no longer supported, ignoring
+ WARNING: max_size is no longer supported, ignoring
+ $ crushtool -d one
+ # begin crush map
+
+ # devices
+ device 0 osd.0 class ssd
+ device 1 osd.1 class ssd
+ device 2 osd.2 class ssd
+ device 3 osd.3 class hdd
+ device 4 osd.4 class hdd
+ device 5 osd.5 class hdd
+
+ # types
+ type 0 osd
+ type 1 host
+ type 2 root
+
+ # buckets
+ host foo {
+ \tid -3\t\t# do not change unnecessarily (esc)
+ \tid -4 class ssd\t\t# do not change unnecessarily (esc)
+ \tid -7 class hdd\t\t# do not change unnecessarily (esc)
+ \t# weight 3.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.0 weight 1.00000 (esc)
+ \titem osd.1 weight 1.00000 (esc)
+ \titem osd.2 weight 1.00000 (esc)
+ }
+ host bar {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \tid -5 class ssd\t\t# do not change unnecessarily (esc)
+ \tid -8 class hdd\t\t# do not change unnecessarily (esc)
+ \t# weight 3.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.3 weight 1.00000 (esc)
+ \titem osd.4 weight 1.00000 (esc)
+ \titem osd.5 weight 1.00000 (esc)
+ }
+ root default {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \tid -6 class ssd\t\t# do not change unnecessarily (esc)
+ \tid -9 class hdd\t\t# do not change unnecessarily (esc)
+ \t# weight 6.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem foo weight 3.00000 (esc)
+ \titem bar weight 3.00000 (esc)
+ }
+
+ # rules
+ rule data {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take default (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule foo {
+ \tid 1 (esc)
+ \ttype replicated (esc)
+ \tstep take default (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+
+
+
+
+
+
+
+
+
+
+ $ crushtool -c $TESTDIR/rules.txt --create-replicated-rule foo-ssd default host -o two --device-class ssd > /dev/null
+ WARNING: min_size is no longer supported, ignoring
+ WARNING: max_size is no longer supported, ignoring
+ $ crushtool -d two
+ # begin crush map
+
+ # devices
+ device 0 osd.0 class ssd
+ device 1 osd.1 class ssd
+ device 2 osd.2 class ssd
+ device 3 osd.3 class hdd
+ device 4 osd.4 class hdd
+ device 5 osd.5 class hdd
+
+ # types
+ type 0 osd
+ type 1 host
+ type 2 root
+
+ # buckets
+ host foo {
+ \tid -3\t\t# do not change unnecessarily (esc)
+ \tid -4 class ssd\t\t# do not change unnecessarily (esc)
+ \tid -7 class hdd\t\t# do not change unnecessarily (esc)
+ \t# weight 3.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.0 weight 1.00000 (esc)
+ \titem osd.1 weight 1.00000 (esc)
+ \titem osd.2 weight 1.00000 (esc)
+ }
+ host bar {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \tid -5 class ssd\t\t# do not change unnecessarily (esc)
+ \tid -8 class hdd\t\t# do not change unnecessarily (esc)
+ \t# weight 3.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.3 weight 1.00000 (esc)
+ \titem osd.4 weight 1.00000 (esc)
+ \titem osd.5 weight 1.00000 (esc)
+ }
+ root default {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \tid -6 class ssd\t\t# do not change unnecessarily (esc)
+ \tid -9 class hdd\t\t# do not change unnecessarily (esc)
+ \t# weight 6.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem foo weight 3.00000 (esc)
+ \titem bar weight 3.00000 (esc)
+ }
+
+ # rules
+ rule data {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take default (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+ rule foo-ssd {
+ \tid 1 (esc)
+ \ttype replicated (esc)
+ \tstep take default class ssd (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+
+
+
+
+
diff --git a/src/test/cli/crushtool/rules.txt b/src/test/cli/crushtool/rules.txt
new file mode 100644
index 000000000..4844cf891
--- /dev/null
+++ b/src/test/cli/crushtool/rules.txt
@@ -0,0 +1,54 @@
+# begin crush map
+
+# devices
+device 0 osd.0 class ssd
+device 1 osd.1 class ssd
+device 2 osd.2 class ssd
+device 3 osd.3 class hdd
+device 4 osd.4 class hdd
+device 5 osd.5 class hdd
+
+# types
+type 0 osd
+type 1 host
+type 2 root
+
+# buckets
+host foo {
+ id -3
+ alg straw2
+ hash 0
+ item osd.0 weight 1.00000
+ item osd.1 weight 1.00000
+ item osd.2 weight 1.00000
+}
+
+host bar {
+ id -2
+ alg straw2
+ hash 0
+ item osd.3 weight 1.00000
+ item osd.4 weight 1.00000
+ item osd.5 weight 1.00000
+}
+
+root default {
+ id -1
+ alg straw2
+ hash 0
+ item foo weight 3.00000
+ item bar weight 3.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ min_size 1
+ max_size 10
+ step take default
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/set-choose.crushmap.txt b/src/test/cli/crushtool/set-choose.crushmap.txt
new file mode 100644
index 000000000..b8f9fd25b
--- /dev/null
+++ b/src/test/cli/crushtool/set-choose.crushmap.txt
@@ -0,0 +1,132 @@
+# begin crush map
+tunable choose_local_tries 1
+tunable choose_local_fallback_tries 2
+tunable choose_total_tries 35
+tunable chooseleaf_descend_once 1
+
+# devices
+device 0 device0
+device 1 device1
+device 2 device2
+device 3 device3
+device 4 device4
+device 5 device5
+device 6 device6
+device 7 device7
+device 8 device8
+
+# types
+type 0 device
+type 1 host
+type 2 rack
+type 3 root
+
+# buckets
+host host0 {
+ id -1 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+ item device2 weight 1.00000
+}
+host host1 {
+ id -2 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device3 weight 1.00000
+ item device4 weight 1.00000
+ item device5 weight 1.00000
+}
+host host2 {
+ id -3 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device6 weight 1.00000
+ item device7 weight 1.00000
+ item device8 weight 1.00000
+}
+rack rack0 {
+ id -4 # do not change unnecessarily
+ # weight 9.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 3.00000
+ item host1 weight 3.00000
+ item host2 weight 3.00000
+}
+root root0 {
+ id -5 # do not change unnecessarily
+ # weight 9.00000
+ alg straw
+ hash 0 # rjenkins1
+ item rack0 weight 9.00000
+}
+
+# rules
+rule choose {
+ id 0
+ type replicated
+ step take root0
+ step choose firstn 0 type host
+ step choose firstn 1 type device
+ step emit
+}
+
+rule choose-two {
+ id 1
+ type replicated
+ step take root0
+ step choose firstn 0 type device
+ step emit
+}
+
+rule chooseleaf {
+ id 2
+ type replicated
+ step take root0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+rule choose-set {
+ id 3
+ type replicated
+ step set_choose_tries 3
+ step set_choose_local_tries 2
+ step set_choose_local_fallback_tries 2
+ step set_chooseleaf_tries 3
+ step take root0
+ step choose firstn 0 type host
+ step choose firstn 1 type device
+ step emit
+}
+
+rule choose-set-two {
+ id 4
+ type replicated
+ step set_choose_tries 3
+ step set_choose_local_tries 2
+ step set_choose_local_fallback_tries 2
+ step set_chooseleaf_tries 3
+ step take root0
+ step choose firstn 0 type device
+ step emit
+}
+
+rule chooseleaf-set {
+ id 5
+ type replicated
+ step set_choose_tries 3
+ step set_choose_local_tries 2
+ step set_choose_local_fallback_tries 2
+ step set_chooseleaf_tries 3
+ step take root0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/set-choose.t b/src/test/cli/crushtool/set-choose.t
new file mode 100644
index 000000000..bd0e80040
--- /dev/null
+++ b/src/test/cli/crushtool/set-choose.t
@@ -0,0 +1,36944 @@
+ $ crushtool -c "$TESTDIR/set-choose.crushmap.txt" -o set-choose.crushmap
+ $ crushtool -i set-choose.crushmap --test --show-mappings --show-statistics --set-straw-calc-version 0 --min-rep 2 --max-rep 3
+ rule 0 (choose), x = 0..1023, numrep = 2..3
+ CRUSH rule 0 x 0 [0,3]
+ CRUSH rule 0 x 1 [0,8]
+ CRUSH rule 0 x 2 [1,4]
+ CRUSH rule 0 x 3 [8,0]
+ CRUSH rule 0 x 4 [5,1]
+ CRUSH rule 0 x 5 [7,0]
+ CRUSH rule 0 x 6 [2,6]
+ CRUSH rule 0 x 7 [5,6]
+ CRUSH rule 0 x 8 [5,7]
+ CRUSH rule 0 x 9 [2,4]
+ CRUSH rule 0 x 10 [0,8]
+ CRUSH rule 0 x 11 [0,6]
+ CRUSH rule 0 x 12 [0,3]
+ CRUSH rule 0 x 13 [3,8]
+ CRUSH rule 0 x 14 [7,1]
+ CRUSH rule 0 x 15 [7,1]
+ CRUSH rule 0 x 16 [3,7]
+ CRUSH rule 0 x 17 [5,1]
+ CRUSH rule 0 x 18 [1,3]
+ CRUSH rule 0 x 19 [7,5]
+ CRUSH rule 0 x 20 [2,4]
+ CRUSH rule 0 x 21 [3,6]
+ CRUSH rule 0 x 22 [8,5]
+ CRUSH rule 0 x 23 [3,7]
+ CRUSH rule 0 x 24 [1,6]
+ CRUSH rule 0 x 25 [3,8]
+ CRUSH rule 0 x 26 [2,7]
+ CRUSH rule 0 x 27 [3,1]
+ CRUSH rule 0 x 28 [6,2]
+ CRUSH rule 0 x 29 [8,5]
+ CRUSH rule 0 x 30 [5,6]
+ CRUSH rule 0 x 31 [8,2]
+ CRUSH rule 0 x 32 [3,7]
+ CRUSH rule 0 x 33 [2,7]
+ CRUSH rule 0 x 34 [2,3]
+ CRUSH rule 0 x 35 [0,6]
+ CRUSH rule 0 x 36 [3,6]
+ CRUSH rule 0 x 37 [0,4]
+ CRUSH rule 0 x 38 [4,6]
+ CRUSH rule 0 x 39 [3,7]
+ CRUSH rule 0 x 40 [7,2]
+ CRUSH rule 0 x 41 [0,7]
+ CRUSH rule 0 x 42 [4,7]
+ CRUSH rule 0 x 43 [0,3]
+ CRUSH rule 0 x 44 [1,8]
+ CRUSH rule 0 x 45 [8,2]
+ CRUSH rule 0 x 46 [2,4]
+ CRUSH rule 0 x 47 [4,1]
+ CRUSH rule 0 x 48 [4,6]
+ CRUSH rule 0 x 49 [5,6]
+ CRUSH rule 0 x 50 [3,1]
+ CRUSH rule 0 x 51 [3,6]
+ CRUSH rule 0 x 52 [8,2]
+ CRUSH rule 0 x 53 [3,6]
+ CRUSH rule 0 x 54 [7,4]
+ CRUSH rule 0 x 55 [8,0]
+ CRUSH rule 0 x 56 [6,5]
+ CRUSH rule 0 x 57 [5,8]
+ CRUSH rule 0 x 58 [1,8]
+ CRUSH rule 0 x 59 [4,0]
+ CRUSH rule 0 x 60 [3,1]
+ CRUSH rule 0 x 61 [4,8]
+ CRUSH rule 0 x 62 [7,0]
+ CRUSH rule 0 x 63 [5,6]
+ CRUSH rule 0 x 64 [4,1]
+ CRUSH rule 0 x 65 [7,4]
+ CRUSH rule 0 x 66 [5,6]
+ CRUSH rule 0 x 67 [5,0]
+ CRUSH rule 0 x 68 [0,3]
+ CRUSH rule 0 x 69 [5,2]
+ CRUSH rule 0 x 70 [7,2]
+ CRUSH rule 0 x 71 [2,7]
+ CRUSH rule 0 x 72 [6,2]
+ CRUSH rule 0 x 73 [2,7]
+ CRUSH rule 0 x 74 [0,8]
+ CRUSH rule 0 x 75 [3,0]
+ CRUSH rule 0 x 76 [5,2]
+ CRUSH rule 0 x 77 [7,0]
+ CRUSH rule 0 x 78 [1,4]
+ CRUSH rule 0 x 79 [5,0]
+ CRUSH rule 0 x 80 [0,4]
+ CRUSH rule 0 x 81 [0,5]
+ CRUSH rule 0 x 82 [7,2]
+ CRUSH rule 0 x 83 [2,6]
+ CRUSH rule 0 x 84 [7,0]
+ CRUSH rule 0 x 85 [3,7]
+ CRUSH rule 0 x 86 [0,6]
+ CRUSH rule 0 x 87 [0,6]
+ CRUSH rule 0 x 88 [1,8]
+ CRUSH rule 0 x 89 [3,1]
+ CRUSH rule 0 x 90 [6,3]
+ CRUSH rule 0 x 91 [3,6]
+ CRUSH rule 0 x 92 [1,6]
+ CRUSH rule 0 x 93 [7,4]
+ CRUSH rule 0 x 94 [0,3]
+ CRUSH rule 0 x 95 [7,3]
+ CRUSH rule 0 x 96 [3,8]
+ CRUSH rule 0 x 97 [8,4]
+ CRUSH rule 0 x 98 [2,7]
+ CRUSH rule 0 x 99 [0,8]
+ CRUSH rule 0 x 100 [1,6]
+ CRUSH rule 0 x 101 [3,8]
+ CRUSH rule 0 x 102 [4,0]
+ CRUSH rule 0 x 103 [4,7]
+ CRUSH rule 0 x 104 [7,4]
+ CRUSH rule 0 x 105 [2,3]
+ CRUSH rule 0 x 106 [1,6]
+ CRUSH rule 0 x 107 [3,1]
+ CRUSH rule 0 x 108 [7,1]
+ CRUSH rule 0 x 109 [1,3]
+ CRUSH rule 0 x 110 [3,2]
+ CRUSH rule 0 x 111 [2,4]
+ CRUSH rule 0 x 112 [2,6]
+ CRUSH rule 0 x 113 [6,1]
+ CRUSH rule 0 x 114 [7,5]
+ CRUSH rule 0 x 115 [8,2]
+ CRUSH rule 0 x 116 [1,7]
+ CRUSH rule 0 x 117 [7,5]
+ CRUSH rule 0 x 118 [0,3]
+ CRUSH rule 0 x 119 [5,7]
+ CRUSH rule 0 x 120 [0,5]
+ CRUSH rule 0 x 121 [2,8]
+ CRUSH rule 0 x 122 [8,4]
+ CRUSH rule 0 x 123 [2,4]
+ CRUSH rule 0 x 124 [3,0]
+ CRUSH rule 0 x 125 [0,7]
+ CRUSH rule 0 x 126 [4,1]
+ CRUSH rule 0 x 127 [3,6]
+ CRUSH rule 0 x 128 [3,8]
+ CRUSH rule 0 x 129 [0,4]
+ CRUSH rule 0 x 130 [3,8]
+ CRUSH rule 0 x 131 [1,5]
+ CRUSH rule 0 x 132 [1,3]
+ CRUSH rule 0 x 133 [3,6]
+ CRUSH rule 0 x 134 [1,7]
+ CRUSH rule 0 x 135 [5,7]
+ CRUSH rule 0 x 136 [2,3]
+ CRUSH rule 0 x 137 [7,3]
+ CRUSH rule 0 x 138 [8,3]
+ CRUSH rule 0 x 139 [3,0]
+ CRUSH rule 0 x 140 [1,8]
+ CRUSH rule 0 x 141 [6,0]
+ CRUSH rule 0 x 142 [3,2]
+ CRUSH rule 0 x 143 [5,7]
+ CRUSH rule 0 x 144 [8,2]
+ CRUSH rule 0 x 145 [8,4]
+ CRUSH rule 0 x 146 [2,8]
+ CRUSH rule 0 x 147 [2,6]
+ CRUSH rule 0 x 148 [3,0]
+ CRUSH rule 0 x 149 [4,6]
+ CRUSH rule 0 x 150 [1,8]
+ CRUSH rule 0 x 151 [3,8]
+ CRUSH rule 0 x 152 [8,3]
+ CRUSH rule 0 x 153 [8,3]
+ CRUSH rule 0 x 154 [3,0]
+ CRUSH rule 0 x 155 [3,6]
+ CRUSH rule 0 x 156 [4,2]
+ CRUSH rule 0 x 157 [4,1]
+ CRUSH rule 0 x 158 [2,6]
+ CRUSH rule 0 x 159 [7,0]
+ CRUSH rule 0 x 160 [2,8]
+ CRUSH rule 0 x 161 [1,3]
+ CRUSH rule 0 x 162 [0,8]
+ CRUSH rule 0 x 163 [5,8]
+ CRUSH rule 0 x 164 [7,2]
+ CRUSH rule 0 x 165 [7,2]
+ CRUSH rule 0 x 166 [2,3]
+ CRUSH rule 0 x 167 [0,6]
+ CRUSH rule 0 x 168 [4,0]
+ CRUSH rule 0 x 169 [2,7]
+ CRUSH rule 0 x 170 [1,3]
+ CRUSH rule 0 x 171 [7,3]
+ CRUSH rule 0 x 172 [0,8]
+ CRUSH rule 0 x 173 [8,5]
+ CRUSH rule 0 x 174 [1,4]
+ CRUSH rule 0 x 175 [6,0]
+ CRUSH rule 0 x 176 [4,2]
+ CRUSH rule 0 x 177 [5,1]
+ CRUSH rule 0 x 178 [3,0]
+ CRUSH rule 0 x 179 [4,1]
+ CRUSH rule 0 x 180 [3,7]
+ CRUSH rule 0 x 181 [6,0]
+ CRUSH rule 0 x 182 [8,3]
+ CRUSH rule 0 x 183 [7,5]
+ CRUSH rule 0 x 184 [5,6]
+ CRUSH rule 0 x 185 [6,0]
+ CRUSH rule 0 x 186 [2,4]
+ CRUSH rule 0 x 187 [1,6]
+ CRUSH rule 0 x 188 [1,6]
+ CRUSH rule 0 x 189 [0,6]
+ CRUSH rule 0 x 190 [4,1]
+ CRUSH rule 0 x 191 [7,0]
+ CRUSH rule 0 x 192 [5,2]
+ CRUSH rule 0 x 193 [4,0]
+ CRUSH rule 0 x 194 [1,3]
+ CRUSH rule 0 x 195 [6,5]
+ CRUSH rule 0 x 196 [6,1]
+ CRUSH rule 0 x 197 [6,5]
+ CRUSH rule 0 x 198 [2,3]
+ CRUSH rule 0 x 199 [0,5]
+ CRUSH rule 0 x 200 [0,3]
+ CRUSH rule 0 x 201 [7,1]
+ CRUSH rule 0 x 202 [6,4]
+ CRUSH rule 0 x 203 [4,6]
+ CRUSH rule 0 x 204 [2,4]
+ CRUSH rule 0 x 205 [0,7]
+ CRUSH rule 0 x 206 [0,8]
+ CRUSH rule 0 x 207 [3,2]
+ CRUSH rule 0 x 208 [7,2]
+ CRUSH rule 0 x 209 [1,8]
+ CRUSH rule 0 x 210 [1,4]
+ CRUSH rule 0 x 211 [5,2]
+ CRUSH rule 0 x 212 [7,5]
+ CRUSH rule 0 x 213 [8,4]
+ CRUSH rule 0 x 214 [4,7]
+ CRUSH rule 0 x 215 [8,0]
+ CRUSH rule 0 x 216 [5,2]
+ CRUSH rule 0 x 217 [0,7]
+ CRUSH rule 0 x 218 [0,7]
+ CRUSH rule 0 x 219 [4,6]
+ CRUSH rule 0 x 220 [5,8]
+ CRUSH rule 0 x 221 [3,7]
+ CRUSH rule 0 x 222 [6,4]
+ CRUSH rule 0 x 223 [1,3]
+ CRUSH rule 0 x 224 [1,3]
+ CRUSH rule 0 x 225 [8,0]
+ CRUSH rule 0 x 226 [7,0]
+ CRUSH rule 0 x 227 [3,0]
+ CRUSH rule 0 x 228 [5,6]
+ CRUSH rule 0 x 229 [3,7]
+ CRUSH rule 0 x 230 [4,6]
+ CRUSH rule 0 x 231 [4,7]
+ CRUSH rule 0 x 232 [2,8]
+ CRUSH rule 0 x 233 [3,6]
+ CRUSH rule 0 x 234 [0,4]
+ CRUSH rule 0 x 235 [3,6]
+ CRUSH rule 0 x 236 [5,0]
+ CRUSH rule 0 x 237 [4,8]
+ CRUSH rule 0 x 238 [4,2]
+ CRUSH rule 0 x 239 [8,3]
+ CRUSH rule 0 x 240 [5,8]
+ CRUSH rule 0 x 241 [3,1]
+ CRUSH rule 0 x 242 [3,2]
+ CRUSH rule 0 x 243 [4,8]
+ CRUSH rule 0 x 244 [4,6]
+ CRUSH rule 0 x 245 [7,2]
+ CRUSH rule 0 x 246 [1,3]
+ CRUSH rule 0 x 247 [6,1]
+ CRUSH rule 0 x 248 [8,0]
+ CRUSH rule 0 x 249 [2,5]
+ CRUSH rule 0 x 250 [2,3]
+ CRUSH rule 0 x 251 [2,3]
+ CRUSH rule 0 x 252 [3,7]
+ CRUSH rule 0 x 253 [3,0]
+ CRUSH rule 0 x 254 [3,2]
+ CRUSH rule 0 x 255 [1,7]
+ CRUSH rule 0 x 256 [5,6]
+ CRUSH rule 0 x 257 [2,6]
+ CRUSH rule 0 x 258 [5,2]
+ CRUSH rule 0 x 259 [4,6]
+ CRUSH rule 0 x 260 [3,8]
+ CRUSH rule 0 x 261 [8,5]
+ CRUSH rule 0 x 262 [5,6]
+ CRUSH rule 0 x 263 [6,1]
+ CRUSH rule 0 x 264 [3,6]
+ CRUSH rule 0 x 265 [8,4]
+ CRUSH rule 0 x 266 [8,0]
+ CRUSH rule 0 x 267 [2,4]
+ CRUSH rule 0 x 268 [0,6]
+ CRUSH rule 0 x 269 [0,6]
+ CRUSH rule 0 x 270 [5,1]
+ CRUSH rule 0 x 271 [7,4]
+ CRUSH rule 0 x 272 [2,6]
+ CRUSH rule 0 x 273 [3,2]
+ CRUSH rule 0 x 274 [6,3]
+ CRUSH rule 0 x 275 [4,8]
+ CRUSH rule 0 x 276 [7,0]
+ CRUSH rule 0 x 277 [6,4]
+ CRUSH rule 0 x 278 [6,2]
+ CRUSH rule 0 x 279 [8,5]
+ CRUSH rule 0 x 280 [0,7]
+ CRUSH rule 0 x 281 [8,1]
+ CRUSH rule 0 x 282 [3,2]
+ CRUSH rule 0 x 283 [8,1]
+ CRUSH rule 0 x 284 [6,3]
+ CRUSH rule 0 x 285 [5,7]
+ CRUSH rule 0 x 286 [2,8]
+ CRUSH rule 0 x 287 [0,3]
+ CRUSH rule 0 x 288 [8,1]
+ CRUSH rule 0 x 289 [4,6]
+ CRUSH rule 0 x 290 [1,5]
+ CRUSH rule 0 x 291 [0,5]
+ CRUSH rule 0 x 292 [8,1]
+ CRUSH rule 0 x 293 [6,2]
+ CRUSH rule 0 x 294 [7,5]
+ CRUSH rule 0 x 295 [4,6]
+ CRUSH rule 0 x 296 [3,1]
+ CRUSH rule 0 x 297 [6,0]
+ CRUSH rule 0 x 298 [1,5]
+ CRUSH rule 0 x 299 [2,8]
+ CRUSH rule 0 x 300 [8,4]
+ CRUSH rule 0 x 301 [0,7]
+ CRUSH rule 0 x 302 [3,1]
+ CRUSH rule 0 x 303 [7,4]
+ CRUSH rule 0 x 304 [2,6]
+ CRUSH rule 0 x 305 [5,6]
+ CRUSH rule 0 x 306 [0,8]
+ CRUSH rule 0 x 307 [0,7]
+ CRUSH rule 0 x 308 [0,8]
+ CRUSH rule 0 x 309 [7,4]
+ CRUSH rule 0 x 310 [4,0]
+ CRUSH rule 0 x 311 [3,8]
+ CRUSH rule 0 x 312 [2,6]
+ CRUSH rule 0 x 313 [5,1]
+ CRUSH rule 0 x 314 [4,0]
+ CRUSH rule 0 x 315 [2,3]
+ CRUSH rule 0 x 316 [6,4]
+ CRUSH rule 0 x 317 [2,7]
+ CRUSH rule 0 x 318 [8,1]
+ CRUSH rule 0 x 319 [5,2]
+ CRUSH rule 0 x 320 [3,8]
+ CRUSH rule 0 x 321 [1,5]
+ CRUSH rule 0 x 322 [2,6]
+ CRUSH rule 0 x 323 [4,7]
+ CRUSH rule 0 x 324 [7,2]
+ CRUSH rule 0 x 325 [4,6]
+ CRUSH rule 0 x 326 [3,1]
+ CRUSH rule 0 x 327 [0,8]
+ CRUSH rule 0 x 328 [7,3]
+ CRUSH rule 0 x 329 [5,7]
+ CRUSH rule 0 x 330 [3,7]
+ CRUSH rule 0 x 331 [2,7]
+ CRUSH rule 0 x 332 [2,3]
+ CRUSH rule 0 x 333 [6,4]
+ CRUSH rule 0 x 334 [8,3]
+ CRUSH rule 0 x 335 [7,2]
+ CRUSH rule 0 x 336 [4,6]
+ CRUSH rule 0 x 337 [7,0]
+ CRUSH rule 0 x 338 [5,8]
+ CRUSH rule 0 x 339 [7,5]
+ CRUSH rule 0 x 340 [2,6]
+ CRUSH rule 0 x 341 [5,1]
+ CRUSH rule 0 x 342 [0,8]
+ CRUSH rule 0 x 343 [6,5]
+ CRUSH rule 0 x 344 [6,1]
+ CRUSH rule 0 x 345 [4,7]
+ CRUSH rule 0 x 346 [8,0]
+ CRUSH rule 0 x 347 [3,0]
+ CRUSH rule 0 x 348 [8,0]
+ CRUSH rule 0 x 349 [1,7]
+ CRUSH rule 0 x 350 [8,5]
+ CRUSH rule 0 x 351 [3,8]
+ CRUSH rule 0 x 352 [1,8]
+ CRUSH rule 0 x 353 [6,5]
+ CRUSH rule 0 x 354 [0,5]
+ CRUSH rule 0 x 355 [3,8]
+ CRUSH rule 0 x 356 [3,1]
+ CRUSH rule 0 x 357 [6,1]
+ CRUSH rule 0 x 358 [2,8]
+ CRUSH rule 0 x 359 [6,0]
+ CRUSH rule 0 x 360 [5,0]
+ CRUSH rule 0 x 361 [8,5]
+ CRUSH rule 0 x 362 [4,0]
+ CRUSH rule 0 x 363 [4,2]
+ CRUSH rule 0 x 364 [2,5]
+ CRUSH rule 0 x 365 [6,4]
+ CRUSH rule 0 x 366 [7,0]
+ CRUSH rule 0 x 367 [4,2]
+ CRUSH rule 0 x 368 [7,3]
+ CRUSH rule 0 x 369 [3,7]
+ CRUSH rule 0 x 370 [8,2]
+ CRUSH rule 0 x 371 [1,5]
+ CRUSH rule 0 x 372 [3,1]
+ CRUSH rule 0 x 373 [0,6]
+ CRUSH rule 0 x 374 [3,8]
+ CRUSH rule 0 x 375 [6,5]
+ CRUSH rule 0 x 376 [7,1]
+ CRUSH rule 0 x 377 [1,4]
+ CRUSH rule 0 x 378 [0,6]
+ CRUSH rule 0 x 379 [8,3]
+ CRUSH rule 0 x 380 [2,3]
+ CRUSH rule 0 x 381 [0,3]
+ CRUSH rule 0 x 382 [1,3]
+ CRUSH rule 0 x 383 [4,7]
+ CRUSH rule 0 x 384 [7,0]
+ CRUSH rule 0 x 385 [7,4]
+ CRUSH rule 0 x 386 [0,4]
+ CRUSH rule 0 x 387 [1,5]
+ CRUSH rule 0 x 388 [5,2]
+ CRUSH rule 0 x 389 [1,4]
+ CRUSH rule 0 x 390 [5,8]
+ CRUSH rule 0 x 391 [5,6]
+ CRUSH rule 0 x 392 [1,7]
+ CRUSH rule 0 x 393 [4,0]
+ CRUSH rule 0 x 394 [4,8]
+ CRUSH rule 0 x 395 [4,0]
+ CRUSH rule 0 x 396 [4,2]
+ CRUSH rule 0 x 397 [2,4]
+ CRUSH rule 0 x 398 [2,3]
+ CRUSH rule 0 x 399 [8,5]
+ CRUSH rule 0 x 400 [8,1]
+ CRUSH rule 0 x 401 [0,3]
+ CRUSH rule 0 x 402 [7,4]
+ CRUSH rule 0 x 403 [0,4]
+ CRUSH rule 0 x 404 [4,0]
+ CRUSH rule 0 x 405 [6,4]
+ CRUSH rule 0 x 406 [2,6]
+ CRUSH rule 0 x 407 [2,7]
+ CRUSH rule 0 x 408 [4,1]
+ CRUSH rule 0 x 409 [7,5]
+ CRUSH rule 0 x 410 [8,3]
+ CRUSH rule 0 x 411 [2,7]
+ CRUSH rule 0 x 412 [0,3]
+ CRUSH rule 0 x 413 [5,2]
+ CRUSH rule 0 x 414 [4,0]
+ CRUSH rule 0 x 415 [0,6]
+ CRUSH rule 0 x 416 [2,8]
+ CRUSH rule 0 x 417 [8,0]
+ CRUSH rule 0 x 418 [7,1]
+ CRUSH rule 0 x 419 [8,5]
+ CRUSH rule 0 x 420 [1,4]
+ CRUSH rule 0 x 421 [8,3]
+ CRUSH rule 0 x 422 [6,3]
+ CRUSH rule 0 x 423 [0,3]
+ CRUSH rule 0 x 424 [8,5]
+ CRUSH rule 0 x 425 [1,4]
+ CRUSH rule 0 x 426 [6,2]
+ CRUSH rule 0 x 427 [0,8]
+ CRUSH rule 0 x 428 [5,8]
+ CRUSH rule 0 x 429 [4,8]
+ CRUSH rule 0 x 430 [3,7]
+ CRUSH rule 0 x 431 [5,0]
+ CRUSH rule 0 x 432 [7,0]
+ CRUSH rule 0 x 433 [6,4]
+ CRUSH rule 0 x 434 [5,0]
+ CRUSH rule 0 x 435 [0,4]
+ CRUSH rule 0 x 436 [4,0]
+ CRUSH rule 0 x 437 [7,3]
+ CRUSH rule 0 x 438 [0,5]
+ CRUSH rule 0 x 439 [1,3]
+ CRUSH rule 0 x 440 [2,6]
+ CRUSH rule 0 x 441 [5,8]
+ CRUSH rule 0 x 442 [2,3]
+ CRUSH rule 0 x 443 [6,0]
+ CRUSH rule 0 x 444 [7,1]
+ CRUSH rule 0 x 445 [6,4]
+ CRUSH rule 0 x 446 [4,0]
+ CRUSH rule 0 x 447 [2,4]
+ CRUSH rule 0 x 448 [7,0]
+ CRUSH rule 0 x 449 [7,4]
+ CRUSH rule 0 x 450 [4,0]
+ CRUSH rule 0 x 451 [6,5]
+ CRUSH rule 0 x 452 [8,4]
+ CRUSH rule 0 x 453 [6,5]
+ CRUSH rule 0 x 454 [6,5]
+ CRUSH rule 0 x 455 [2,8]
+ CRUSH rule 0 x 456 [6,2]
+ CRUSH rule 0 x 457 [7,1]
+ CRUSH rule 0 x 458 [2,8]
+ CRUSH rule 0 x 459 [2,6]
+ CRUSH rule 0 x 460 [6,5]
+ CRUSH rule 0 x 461 [6,4]
+ CRUSH rule 0 x 462 [8,0]
+ CRUSH rule 0 x 463 [6,0]
+ CRUSH rule 0 x 464 [7,4]
+ CRUSH rule 0 x 465 [7,1]
+ CRUSH rule 0 x 466 [5,6]
+ CRUSH rule 0 x 467 [6,5]
+ CRUSH rule 0 x 468 [7,2]
+ CRUSH rule 0 x 469 [7,2]
+ CRUSH rule 0 x 470 [3,0]
+ CRUSH rule 0 x 471 [0,6]
+ CRUSH rule 0 x 472 [5,0]
+ CRUSH rule 0 x 473 [1,4]
+ CRUSH rule 0 x 474 [6,1]
+ CRUSH rule 0 x 475 [6,2]
+ CRUSH rule 0 x 476 [4,7]
+ CRUSH rule 0 x 477 [5,6]
+ CRUSH rule 0 x 478 [6,1]
+ CRUSH rule 0 x 479 [0,3]
+ CRUSH rule 0 x 480 [1,6]
+ CRUSH rule 0 x 481 [2,5]
+ CRUSH rule 0 x 482 [4,8]
+ CRUSH rule 0 x 483 [0,6]
+ CRUSH rule 0 x 484 [1,8]
+ CRUSH rule 0 x 485 [4,8]
+ CRUSH rule 0 x 486 [4,0]
+ CRUSH rule 0 x 487 [5,0]
+ CRUSH rule 0 x 488 [5,7]
+ CRUSH rule 0 x 489 [2,8]
+ CRUSH rule 0 x 490 [6,4]
+ CRUSH rule 0 x 491 [1,6]
+ CRUSH rule 0 x 492 [6,5]
+ CRUSH rule 0 x 493 [0,8]
+ CRUSH rule 0 x 494 [1,6]
+ CRUSH rule 0 x 495 [3,0]
+ CRUSH rule 0 x 496 [7,5]
+ CRUSH rule 0 x 497 [5,7]
+ CRUSH rule 0 x 498 [0,4]
+ CRUSH rule 0 x 499 [8,5]
+ CRUSH rule 0 x 500 [3,6]
+ CRUSH rule 0 x 501 [0,7]
+ CRUSH rule 0 x 502 [7,1]
+ CRUSH rule 0 x 503 [2,3]
+ CRUSH rule 0 x 504 [5,8]
+ CRUSH rule 0 x 505 [0,7]
+ CRUSH rule 0 x 506 [5,1]
+ CRUSH rule 0 x 507 [6,0]
+ CRUSH rule 0 x 508 [0,4]
+ CRUSH rule 0 x 509 [7,4]
+ CRUSH rule 0 x 510 [6,2]
+ CRUSH rule 0 x 511 [5,6]
+ CRUSH rule 0 x 512 [7,2]
+ CRUSH rule 0 x 513 [7,2]
+ CRUSH rule 0 x 514 [4,7]
+ CRUSH rule 0 x 515 [8,3]
+ CRUSH rule 0 x 516 [4,1]
+ CRUSH rule 0 x 517 [7,0]
+ CRUSH rule 0 x 518 [4,6]
+ CRUSH rule 0 x 519 [7,3]
+ CRUSH rule 0 x 520 [2,8]
+ CRUSH rule 0 x 521 [8,0]
+ CRUSH rule 0 x 522 [6,0]
+ CRUSH rule 0 x 523 [4,1]
+ CRUSH rule 0 x 524 [0,4]
+ CRUSH rule 0 x 525 [0,3]
+ CRUSH rule 0 x 526 [1,3]
+ CRUSH rule 0 x 527 [0,4]
+ CRUSH rule 0 x 528 [5,2]
+ CRUSH rule 0 x 529 [5,6]
+ CRUSH rule 0 x 530 [6,4]
+ CRUSH rule 0 x 531 [6,0]
+ CRUSH rule 0 x 532 [6,5]
+ CRUSH rule 0 x 533 [5,8]
+ CRUSH rule 0 x 534 [7,4]
+ CRUSH rule 0 x 535 [8,0]
+ CRUSH rule 0 x 536 [6,2]
+ CRUSH rule 0 x 537 [3,8]
+ CRUSH rule 0 x 538 [6,4]
+ CRUSH rule 0 x 539 [8,4]
+ CRUSH rule 0 x 540 [0,7]
+ CRUSH rule 0 x 541 [2,5]
+ CRUSH rule 0 x 542 [3,0]
+ CRUSH rule 0 x 543 [6,2]
+ CRUSH rule 0 x 544 [3,7]
+ CRUSH rule 0 x 545 [5,7]
+ CRUSH rule 0 x 546 [6,2]
+ CRUSH rule 0 x 547 [8,1]
+ CRUSH rule 0 x 548 [5,1]
+ CRUSH rule 0 x 549 [5,7]
+ CRUSH rule 0 x 550 [0,5]
+ CRUSH rule 0 x 551 [7,4]
+ CRUSH rule 0 x 552 [5,7]
+ CRUSH rule 0 x 553 [4,2]
+ CRUSH rule 0 x 554 [0,6]
+ CRUSH rule 0 x 555 [5,0]
+ CRUSH rule 0 x 556 [3,6]
+ CRUSH rule 0 x 557 [7,4]
+ CRUSH rule 0 x 558 [3,2]
+ CRUSH rule 0 x 559 [4,1]
+ CRUSH rule 0 x 560 [8,4]
+ CRUSH rule 0 x 561 [6,4]
+ CRUSH rule 0 x 562 [3,0]
+ CRUSH rule 0 x 563 [2,7]
+ CRUSH rule 0 x 564 [5,1]
+ CRUSH rule 0 x 565 [3,8]
+ CRUSH rule 0 x 566 [4,6]
+ CRUSH rule 0 x 567 [3,7]
+ CRUSH rule 0 x 568 [7,4]
+ CRUSH rule 0 x 569 [3,1]
+ CRUSH rule 0 x 570 [1,4]
+ CRUSH rule 0 x 571 [3,6]
+ CRUSH rule 0 x 572 [3,0]
+ CRUSH rule 0 x 573 [3,1]
+ CRUSH rule 0 x 574 [2,3]
+ CRUSH rule 0 x 575 [8,1]
+ CRUSH rule 0 x 576 [4,7]
+ CRUSH rule 0 x 577 [8,2]
+ CRUSH rule 0 x 578 [6,0]
+ CRUSH rule 0 x 579 [3,2]
+ CRUSH rule 0 x 580 [3,0]
+ CRUSH rule 0 x 581 [7,1]
+ CRUSH rule 0 x 582 [2,8]
+ CRUSH rule 0 x 583 [6,2]
+ CRUSH rule 0 x 584 [8,0]
+ CRUSH rule 0 x 585 [7,0]
+ CRUSH rule 0 x 586 [0,8]
+ CRUSH rule 0 x 587 [2,5]
+ CRUSH rule 0 x 588 [3,8]
+ CRUSH rule 0 x 589 [7,1]
+ CRUSH rule 0 x 590 [6,2]
+ CRUSH rule 0 x 591 [5,2]
+ CRUSH rule 0 x 592 [2,4]
+ CRUSH rule 0 x 593 [0,8]
+ CRUSH rule 0 x 594 [0,6]
+ CRUSH rule 0 x 595 [7,1]
+ CRUSH rule 0 x 596 [4,0]
+ CRUSH rule 0 x 597 [3,1]
+ CRUSH rule 0 x 598 [3,0]
+ CRUSH rule 0 x 599 [5,1]
+ CRUSH rule 0 x 600 [7,0]
+ CRUSH rule 0 x 601 [0,6]
+ CRUSH rule 0 x 602 [3,8]
+ CRUSH rule 0 x 603 [5,0]
+ CRUSH rule 0 x 604 [7,4]
+ CRUSH rule 0 x 605 [3,2]
+ CRUSH rule 0 x 606 [2,7]
+ CRUSH rule 0 x 607 [0,5]
+ CRUSH rule 0 x 608 [5,1]
+ CRUSH rule 0 x 609 [5,0]
+ CRUSH rule 0 x 610 [3,8]
+ CRUSH rule 0 x 611 [1,8]
+ CRUSH rule 0 x 612 [2,8]
+ CRUSH rule 0 x 613 [7,1]
+ CRUSH rule 0 x 614 [7,2]
+ CRUSH rule 0 x 615 [6,2]
+ CRUSH rule 0 x 616 [0,7]
+ CRUSH rule 0 x 617 [6,2]
+ CRUSH rule 0 x 618 [7,3]
+ CRUSH rule 0 x 619 [5,0]
+ CRUSH rule 0 x 620 [4,1]
+ CRUSH rule 0 x 621 [5,6]
+ CRUSH rule 0 x 622 [0,3]
+ CRUSH rule 0 x 623 [0,8]
+ CRUSH rule 0 x 624 [3,2]
+ CRUSH rule 0 x 625 [2,5]
+ CRUSH rule 0 x 626 [7,2]
+ CRUSH rule 0 x 627 [2,6]
+ CRUSH rule 0 x 628 [8,1]
+ CRUSH rule 0 x 629 [2,6]
+ CRUSH rule 0 x 630 [2,6]
+ CRUSH rule 0 x 631 [0,6]
+ CRUSH rule 0 x 632 [7,0]
+ CRUSH rule 0 x 633 [8,4]
+ CRUSH rule 0 x 634 [0,5]
+ CRUSH rule 0 x 635 [5,6]
+ CRUSH rule 0 x 636 [1,3]
+ CRUSH rule 0 x 637 [4,0]
+ CRUSH rule 0 x 638 [6,2]
+ CRUSH rule 0 x 639 [4,0]
+ CRUSH rule 0 x 640 [3,2]
+ CRUSH rule 0 x 641 [7,2]
+ CRUSH rule 0 x 642 [2,8]
+ CRUSH rule 0 x 643 [3,0]
+ CRUSH rule 0 x 644 [8,0]
+ CRUSH rule 0 x 645 [5,7]
+ CRUSH rule 0 x 646 [8,0]
+ CRUSH rule 0 x 647 [7,0]
+ CRUSH rule 0 x 648 [0,8]
+ CRUSH rule 0 x 649 [4,7]
+ CRUSH rule 0 x 650 [7,5]
+ CRUSH rule 0 x 651 [3,6]
+ CRUSH rule 0 x 652 [3,6]
+ CRUSH rule 0 x 653 [8,3]
+ CRUSH rule 0 x 654 [7,4]
+ CRUSH rule 0 x 655 [0,5]
+ CRUSH rule 0 x 656 [4,7]
+ CRUSH rule 0 x 657 [6,0]
+ CRUSH rule 0 x 658 [5,8]
+ CRUSH rule 0 x 659 [4,7]
+ CRUSH rule 0 x 660 [7,3]
+ CRUSH rule 0 x 661 [1,7]
+ CRUSH rule 0 x 662 [4,2]
+ CRUSH rule 0 x 663 [1,3]
+ CRUSH rule 0 x 664 [1,3]
+ CRUSH rule 0 x 665 [5,6]
+ CRUSH rule 0 x 666 [2,7]
+ CRUSH rule 0 x 667 [1,3]
+ CRUSH rule 0 x 668 [3,7]
+ CRUSH rule 0 x 669 [6,3]
+ CRUSH rule 0 x 670 [4,1]
+ CRUSH rule 0 x 671 [0,8]
+ CRUSH rule 0 x 672 [4,2]
+ CRUSH rule 0 x 673 [5,0]
+ CRUSH rule 0 x 674 [3,0]
+ CRUSH rule 0 x 675 [0,8]
+ CRUSH rule 0 x 676 [0,3]
+ CRUSH rule 0 x 677 [4,1]
+ CRUSH rule 0 x 678 [2,3]
+ CRUSH rule 0 x 679 [6,0]
+ CRUSH rule 0 x 680 [0,3]
+ CRUSH rule 0 x 681 [4,6]
+ CRUSH rule 0 x 682 [0,4]
+ CRUSH rule 0 x 683 [0,3]
+ CRUSH rule 0 x 684 [7,2]
+ CRUSH rule 0 x 685 [7,2]
+ CRUSH rule 0 x 686 [1,5]
+ CRUSH rule 0 x 687 [3,7]
+ CRUSH rule 0 x 688 [5,6]
+ CRUSH rule 0 x 689 [6,5]
+ CRUSH rule 0 x 690 [8,0]
+ CRUSH rule 0 x 691 [3,1]
+ CRUSH rule 0 x 692 [7,1]
+ CRUSH rule 0 x 693 [6,5]
+ CRUSH rule 0 x 694 [6,4]
+ CRUSH rule 0 x 695 [0,6]
+ CRUSH rule 0 x 696 [1,5]
+ CRUSH rule 0 x 697 [6,0]
+ CRUSH rule 0 x 698 [6,0]
+ CRUSH rule 0 x 699 [1,8]
+ CRUSH rule 0 x 700 [0,4]
+ CRUSH rule 0 x 701 [4,0]
+ CRUSH rule 0 x 702 [3,0]
+ CRUSH rule 0 x 703 [8,4]
+ CRUSH rule 0 x 704 [0,4]
+ CRUSH rule 0 x 705 [8,0]
+ CRUSH rule 0 x 706 [1,5]
+ CRUSH rule 0 x 707 [7,3]
+ CRUSH rule 0 x 708 [3,7]
+ CRUSH rule 0 x 709 [6,5]
+ CRUSH rule 0 x 710 [8,5]
+ CRUSH rule 0 x 711 [2,4]
+ CRUSH rule 0 x 712 [2,3]
+ CRUSH rule 0 x 713 [6,3]
+ CRUSH rule 0 x 714 [3,0]
+ CRUSH rule 0 x 715 [1,3]
+ CRUSH rule 0 x 716 [3,6]
+ CRUSH rule 0 x 717 [8,0]
+ CRUSH rule 0 x 718 [3,7]
+ CRUSH rule 0 x 719 [2,6]
+ CRUSH rule 0 x 720 [6,0]
+ CRUSH rule 0 x 721 [5,7]
+ CRUSH rule 0 x 722 [5,7]
+ CRUSH rule 0 x 723 [5,2]
+ CRUSH rule 0 x 724 [0,7]
+ CRUSH rule 0 x 725 [0,4]
+ CRUSH rule 0 x 726 [3,7]
+ CRUSH rule 0 x 727 [4,7]
+ CRUSH rule 0 x 728 [2,6]
+ CRUSH rule 0 x 729 [5,6]
+ CRUSH rule 0 x 730 [3,8]
+ CRUSH rule 0 x 731 [4,1]
+ CRUSH rule 0 x 732 [1,4]
+ CRUSH rule 0 x 733 [5,6]
+ CRUSH rule 0 x 734 [6,5]
+ CRUSH rule 0 x 735 [4,6]
+ CRUSH rule 0 x 736 [3,8]
+ CRUSH rule 0 x 737 [1,8]
+ CRUSH rule 0 x 738 [5,1]
+ CRUSH rule 0 x 739 [0,7]
+ CRUSH rule 0 x 740 [0,7]
+ CRUSH rule 0 x 741 [7,2]
+ CRUSH rule 0 x 742 [8,2]
+ CRUSH rule 0 x 743 [7,0]
+ CRUSH rule 0 x 744 [4,6]
+ CRUSH rule 0 x 745 [3,0]
+ CRUSH rule 0 x 746 [4,1]
+ CRUSH rule 0 x 747 [6,2]
+ CRUSH rule 0 x 748 [2,6]
+ CRUSH rule 0 x 749 [4,7]
+ CRUSH rule 0 x 750 [1,7]
+ CRUSH rule 0 x 751 [2,7]
+ CRUSH rule 0 x 752 [8,0]
+ CRUSH rule 0 x 753 [7,4]
+ CRUSH rule 0 x 754 [8,5]
+ CRUSH rule 0 x 755 [1,6]
+ CRUSH rule 0 x 756 [5,8]
+ CRUSH rule 0 x 757 [8,0]
+ CRUSH rule 0 x 758 [6,1]
+ CRUSH rule 0 x 759 [8,5]
+ CRUSH rule 0 x 760 [1,5]
+ CRUSH rule 0 x 761 [4,2]
+ CRUSH rule 0 x 762 [2,8]
+ CRUSH rule 0 x 763 [8,3]
+ CRUSH rule 0 x 764 [1,7]
+ CRUSH rule 0 x 765 [6,4]
+ CRUSH rule 0 x 766 [8,3]
+ CRUSH rule 0 x 767 [1,8]
+ CRUSH rule 0 x 768 [8,5]
+ CRUSH rule 0 x 769 [6,2]
+ CRUSH rule 0 x 770 [6,1]
+ CRUSH rule 0 x 771 [7,2]
+ CRUSH rule 0 x 772 [8,4]
+ CRUSH rule 0 x 773 [3,2]
+ CRUSH rule 0 x 774 [4,7]
+ CRUSH rule 0 x 775 [6,5]
+ CRUSH rule 0 x 776 [7,0]
+ CRUSH rule 0 x 777 [3,0]
+ CRUSH rule 0 x 778 [1,6]
+ CRUSH rule 0 x 779 [2,6]
+ CRUSH rule 0 x 780 [0,5]
+ CRUSH rule 0 x 781 [6,5]
+ CRUSH rule 0 x 782 [5,2]
+ CRUSH rule 0 x 783 [7,0]
+ CRUSH rule 0 x 784 [0,5]
+ CRUSH rule 0 x 785 [6,1]
+ CRUSH rule 0 x 786 [7,3]
+ CRUSH rule 0 x 787 [1,8]
+ CRUSH rule 0 x 788 [6,0]
+ CRUSH rule 0 x 789 [0,5]
+ CRUSH rule 0 x 790 [8,3]
+ CRUSH rule 0 x 791 [3,6]
+ CRUSH rule 0 x 792 [5,6]
+ CRUSH rule 0 x 793 [6,2]
+ CRUSH rule 0 x 794 [2,8]
+ CRUSH rule 0 x 795 [0,4]
+ CRUSH rule 0 x 796 [3,7]
+ CRUSH rule 0 x 797 [2,3]
+ CRUSH rule 0 x 798 [6,0]
+ CRUSH rule 0 x 799 [5,2]
+ CRUSH rule 0 x 800 [5,2]
+ CRUSH rule 0 x 801 [3,7]
+ CRUSH rule 0 x 802 [1,6]
+ CRUSH rule 0 x 803 [0,4]
+ CRUSH rule 0 x 804 [6,0]
+ CRUSH rule 0 x 805 [3,8]
+ CRUSH rule 0 x 806 [1,5]
+ CRUSH rule 0 x 807 [5,7]
+ CRUSH rule 0 x 808 [4,7]
+ CRUSH rule 0 x 809 [1,3]
+ CRUSH rule 0 x 810 [5,7]
+ CRUSH rule 0 x 811 [8,4]
+ CRUSH rule 0 x 812 [8,3]
+ CRUSH rule 0 x 813 [6,4]
+ CRUSH rule 0 x 814 [3,8]
+ CRUSH rule 0 x 815 [3,1]
+ CRUSH rule 0 x 816 [2,6]
+ CRUSH rule 0 x 817 [4,6]
+ CRUSH rule 0 x 818 [3,1]
+ CRUSH rule 0 x 819 [5,0]
+ CRUSH rule 0 x 820 [3,7]
+ CRUSH rule 0 x 821 [4,8]
+ CRUSH rule 0 x 822 [2,3]
+ CRUSH rule 0 x 823 [4,7]
+ CRUSH rule 0 x 824 [3,7]
+ CRUSH rule 0 x 825 [2,8]
+ CRUSH rule 0 x 826 [7,1]
+ CRUSH rule 0 x 827 [0,6]
+ CRUSH rule 0 x 828 [2,5]
+ CRUSH rule 0 x 829 [5,6]
+ CRUSH rule 0 x 830 [2,4]
+ CRUSH rule 0 x 831 [1,6]
+ CRUSH rule 0 x 832 [4,8]
+ CRUSH rule 0 x 833 [2,6]
+ CRUSH rule 0 x 834 [3,0]
+ CRUSH rule 0 x 835 [8,5]
+ CRUSH rule 0 x 836 [3,8]
+ CRUSH rule 0 x 837 [6,4]
+ CRUSH rule 0 x 838 [6,0]
+ CRUSH rule 0 x 839 [5,2]
+ CRUSH rule 0 x 840 [7,3]
+ CRUSH rule 0 x 841 [4,8]
+ CRUSH rule 0 x 842 [2,5]
+ CRUSH rule 0 x 843 [6,4]
+ CRUSH rule 0 x 844 [4,8]
+ CRUSH rule 0 x 845 [3,6]
+ CRUSH rule 0 x 846 [3,0]
+ CRUSH rule 0 x 847 [0,8]
+ CRUSH rule 0 x 848 [2,6]
+ CRUSH rule 0 x 849 [4,8]
+ CRUSH rule 0 x 850 [1,5]
+ CRUSH rule 0 x 851 [6,5]
+ CRUSH rule 0 x 852 [7,4]
+ CRUSH rule 0 x 853 [6,2]
+ CRUSH rule 0 x 854 [7,0]
+ CRUSH rule 0 x 855 [5,7]
+ CRUSH rule 0 x 856 [6,3]
+ CRUSH rule 0 x 857 [8,4]
+ CRUSH rule 0 x 858 [6,5]
+ CRUSH rule 0 x 859 [6,0]
+ CRUSH rule 0 x 860 [4,2]
+ CRUSH rule 0 x 861 [8,4]
+ CRUSH rule 0 x 862 [6,0]
+ CRUSH rule 0 x 863 [8,1]
+ CRUSH rule 0 x 864 [5,6]
+ CRUSH rule 0 x 865 [8,0]
+ CRUSH rule 0 x 866 [3,6]
+ CRUSH rule 0 x 867 [6,3]
+ CRUSH rule 0 x 868 [6,4]
+ CRUSH rule 0 x 869 [8,4]
+ CRUSH rule 0 x 870 [0,5]
+ CRUSH rule 0 x 871 [3,1]
+ CRUSH rule 0 x 872 [5,0]
+ CRUSH rule 0 x 873 [4,6]
+ CRUSH rule 0 x 874 [2,6]
+ CRUSH rule 0 x 875 [2,6]
+ CRUSH rule 0 x 876 [5,8]
+ CRUSH rule 0 x 877 [6,4]
+ CRUSH rule 0 x 878 [5,0]
+ CRUSH rule 0 x 879 [7,3]
+ CRUSH rule 0 x 880 [3,1]
+ CRUSH rule 0 x 881 [5,8]
+ CRUSH rule 0 x 882 [4,1]
+ CRUSH rule 0 x 883 [2,5]
+ CRUSH rule 0 x 884 [6,0]
+ CRUSH rule 0 x 885 [5,1]
+ CRUSH rule 0 x 886 [3,7]
+ CRUSH rule 0 x 887 [7,5]
+ CRUSH rule 0 x 888 [6,1]
+ CRUSH rule 0 x 889 [2,8]
+ CRUSH rule 0 x 890 [7,1]
+ CRUSH rule 0 x 891 [1,7]
+ CRUSH rule 0 x 892 [6,0]
+ CRUSH rule 0 x 893 [2,3]
+ CRUSH rule 0 x 894 [7,4]
+ CRUSH rule 0 x 895 [5,1]
+ CRUSH rule 0 x 896 [1,6]
+ CRUSH rule 0 x 897 [4,0]
+ CRUSH rule 0 x 898 [0,3]
+ CRUSH rule 0 x 899 [1,6]
+ CRUSH rule 0 x 900 [4,0]
+ CRUSH rule 0 x 901 [5,2]
+ CRUSH rule 0 x 902 [8,4]
+ CRUSH rule 0 x 903 [5,6]
+ CRUSH rule 0 x 904 [5,7]
+ CRUSH rule 0 x 905 [6,0]
+ CRUSH rule 0 x 906 [1,7]
+ CRUSH rule 0 x 907 [7,2]
+ CRUSH rule 0 x 908 [5,6]
+ CRUSH rule 0 x 909 [2,3]
+ CRUSH rule 0 x 910 [6,5]
+ CRUSH rule 0 x 911 [5,7]
+ CRUSH rule 0 x 912 [0,8]
+ CRUSH rule 0 x 913 [7,1]
+ CRUSH rule 0 x 914 [6,4]
+ CRUSH rule 0 x 915 [8,2]
+ CRUSH rule 0 x 916 [3,0]
+ CRUSH rule 0 x 917 [1,3]
+ CRUSH rule 0 x 918 [8,0]
+ CRUSH rule 0 x 919 [6,2]
+ CRUSH rule 0 x 920 [7,4]
+ CRUSH rule 0 x 921 [1,4]
+ CRUSH rule 0 x 922 [6,3]
+ CRUSH rule 0 x 923 [5,6]
+ CRUSH rule 0 x 924 [3,1]
+ CRUSH rule 0 x 925 [5,6]
+ CRUSH rule 0 x 926 [3,0]
+ CRUSH rule 0 x 927 [1,6]
+ CRUSH rule 0 x 928 [8,1]
+ CRUSH rule 0 x 929 [4,2]
+ CRUSH rule 0 x 930 [2,5]
+ CRUSH rule 0 x 931 [5,2]
+ CRUSH rule 0 x 932 [4,2]
+ CRUSH rule 0 x 933 [8,4]
+ CRUSH rule 0 x 934 [5,8]
+ CRUSH rule 0 x 935 [6,3]
+ CRUSH rule 0 x 936 [0,7]
+ CRUSH rule 0 x 937 [5,8]
+ CRUSH rule 0 x 938 [6,4]
+ CRUSH rule 0 x 939 [2,8]
+ CRUSH rule 0 x 940 [8,5]
+ CRUSH rule 0 x 941 [5,0]
+ CRUSH rule 0 x 942 [1,6]
+ CRUSH rule 0 x 943 [8,2]
+ CRUSH rule 0 x 944 [4,8]
+ CRUSH rule 0 x 945 [7,0]
+ CRUSH rule 0 x 946 [2,8]
+ CRUSH rule 0 x 947 [4,2]
+ CRUSH rule 0 x 948 [7,4]
+ CRUSH rule 0 x 949 [6,2]
+ CRUSH rule 0 x 950 [3,7]
+ CRUSH rule 0 x 951 [4,6]
+ CRUSH rule 0 x 952 [2,7]
+ CRUSH rule 0 x 953 [1,3]
+ CRUSH rule 0 x 954 [4,0]
+ CRUSH rule 0 x 955 [8,1]
+ CRUSH rule 0 x 956 [1,7]
+ CRUSH rule 0 x 957 [7,0]
+ CRUSH rule 0 x 958 [8,3]
+ CRUSH rule 0 x 959 [5,1]
+ CRUSH rule 0 x 960 [3,6]
+ CRUSH rule 0 x 961 [4,1]
+ CRUSH rule 0 x 962 [7,5]
+ CRUSH rule 0 x 963 [0,5]
+ CRUSH rule 0 x 964 [3,2]
+ CRUSH rule 0 x 965 [7,3]
+ CRUSH rule 0 x 966 [3,6]
+ CRUSH rule 0 x 967 [8,4]
+ CRUSH rule 0 x 968 [7,0]
+ CRUSH rule 0 x 969 [8,0]
+ CRUSH rule 0 x 970 [0,8]
+ CRUSH rule 0 x 971 [1,8]
+ CRUSH rule 0 x 972 [1,7]
+ CRUSH rule 0 x 973 [1,8]
+ CRUSH rule 0 x 974 [5,1]
+ CRUSH rule 0 x 975 [3,8]
+ CRUSH rule 0 x 976 [4,7]
+ CRUSH rule 0 x 977 [8,3]
+ CRUSH rule 0 x 978 [7,0]
+ CRUSH rule 0 x 979 [7,2]
+ CRUSH rule 0 x 980 [6,2]
+ CRUSH rule 0 x 981 [7,5]
+ CRUSH rule 0 x 982 [4,1]
+ CRUSH rule 0 x 983 [3,6]
+ CRUSH rule 0 x 984 [0,8]
+ CRUSH rule 0 x 985 [2,4]
+ CRUSH rule 0 x 986 [8,4]
+ CRUSH rule 0 x 987 [0,4]
+ CRUSH rule 0 x 988 [1,4]
+ CRUSH rule 0 x 989 [0,8]
+ CRUSH rule 0 x 990 [1,6]
+ CRUSH rule 0 x 991 [0,4]
+ CRUSH rule 0 x 992 [7,0]
+ CRUSH rule 0 x 993 [0,6]
+ CRUSH rule 0 x 994 [3,6]
+ CRUSH rule 0 x 995 [7,0]
+ CRUSH rule 0 x 996 [6,5]
+ CRUSH rule 0 x 997 [6,3]
+ CRUSH rule 0 x 998 [8,0]
+ CRUSH rule 0 x 999 [0,7]
+ CRUSH rule 0 x 1000 [8,4]
+ CRUSH rule 0 x 1001 [2,3]
+ CRUSH rule 0 x 1002 [1,3]
+ CRUSH rule 0 x 1003 [2,7]
+ CRUSH rule 0 x 1004 [6,0]
+ CRUSH rule 0 x 1005 [6,1]
+ CRUSH rule 0 x 1006 [1,8]
+ CRUSH rule 0 x 1007 [1,3]
+ CRUSH rule 0 x 1008 [1,7]
+ CRUSH rule 0 x 1009 [6,5]
+ CRUSH rule 0 x 1010 [3,1]
+ CRUSH rule 0 x 1011 [3,0]
+ CRUSH rule 0 x 1012 [3,1]
+ CRUSH rule 0 x 1013 [5,2]
+ CRUSH rule 0 x 1014 [2,8]
+ CRUSH rule 0 x 1015 [6,3]
+ CRUSH rule 0 x 1016 [2,5]
+ CRUSH rule 0 x 1017 [6,1]
+ CRUSH rule 0 x 1018 [5,1]
+ CRUSH rule 0 x 1019 [5,8]
+ CRUSH rule 0 x 1020 [5,0]
+ CRUSH rule 0 x 1021 [5,2]
+ CRUSH rule 0 x 1022 [1,7]
+ CRUSH rule 0 x 1023 [3,0]
+ rule 0 (choose) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [0,3,7]
+ CRUSH rule 0 x 1 [0,8,5]
+ CRUSH rule 0 x 2 [1,4,8]
+ CRUSH rule 0 x 3 [8,0,4]
+ CRUSH rule 0 x 4 [5,1,8]
+ CRUSH rule 0 x 5 [7,0,3]
+ CRUSH rule 0 x 6 [2,6,3]
+ CRUSH rule 0 x 7 [5,6,1]
+ CRUSH rule 0 x 8 [5,7,2]
+ CRUSH rule 0 x 9 [2,4,8]
+ CRUSH rule 0 x 10 [0,8,4]
+ CRUSH rule 0 x 11 [0,6,3]
+ CRUSH rule 0 x 12 [0,3,7]
+ CRUSH rule 0 x 13 [3,8,0]
+ CRUSH rule 0 x 14 [7,1,5]
+ CRUSH rule 0 x 15 [7,1,3]
+ CRUSH rule 0 x 16 [3,7,1]
+ CRUSH rule 0 x 17 [5,1,7]
+ CRUSH rule 0 x 18 [1,3,6]
+ CRUSH rule 0 x 19 [7,5,2]
+ CRUSH rule 0 x 20 [2,4,7]
+ CRUSH rule 0 x 21 [3,6,0]
+ CRUSH rule 0 x 22 [8,5,1]
+ CRUSH rule 0 x 23 [3,7,2]
+ CRUSH rule 0 x 24 [1,6,3]
+ CRUSH rule 0 x 25 [3,8,1]
+ CRUSH rule 0 x 26 [2,7,3]
+ CRUSH rule 0 x 27 [3,1,7]
+ CRUSH rule 0 x 28 [6,2,4]
+ CRUSH rule 0 x 29 [8,5,2]
+ CRUSH rule 0 x 30 [5,6,0]
+ CRUSH rule 0 x 31 [8,2,5]
+ CRUSH rule 0 x 32 [3,7,2]
+ CRUSH rule 0 x 33 [2,7,4]
+ CRUSH rule 0 x 34 [2,3,7]
+ CRUSH rule 0 x 35 [0,6,3]
+ CRUSH rule 0 x 36 [3,6,1]
+ CRUSH rule 0 x 37 [0,4,7]
+ CRUSH rule 0 x 38 [4,6,1]
+ CRUSH rule 0 x 39 [3,7,2]
+ CRUSH rule 0 x 40 [7,2,5]
+ CRUSH rule 0 x 41 [0,7,4]
+ CRUSH rule 0 x 42 [4,7,2]
+ CRUSH rule 0 x 43 [0,3,8]
+ CRUSH rule 0 x 44 [1,8,4]
+ CRUSH rule 0 x 45 [8,2,3]
+ CRUSH rule 0 x 46 [2,4,8]
+ CRUSH rule 0 x 47 [4,1,7]
+ CRUSH rule 0 x 48 [4,6,0]
+ CRUSH rule 0 x 49 [5,6,0]
+ CRUSH rule 0 x 50 [3,1,7]
+ CRUSH rule 0 x 51 [3,6,1]
+ CRUSH rule 0 x 52 [8,2,3]
+ CRUSH rule 0 x 53 [3,6,2]
+ CRUSH rule 0 x 54 [7,4,0]
+ CRUSH rule 0 x 55 [8,0,3]
+ CRUSH rule 0 x 56 [6,5,1]
+ CRUSH rule 0 x 57 [5,8,0]
+ CRUSH rule 0 x 58 [1,8,5]
+ CRUSH rule 0 x 59 [4,0,7]
+ CRUSH rule 0 x 60 [3,1,8]
+ CRUSH rule 0 x 61 [4,8,0]
+ CRUSH rule 0 x 62 [7,0,4]
+ CRUSH rule 0 x 63 [5,6,2]
+ CRUSH rule 0 x 64 [4,1,8]
+ CRUSH rule 0 x 65 [7,4,2]
+ CRUSH rule 0 x 66 [5,6,0]
+ CRUSH rule 0 x 67 [5,0,6]
+ CRUSH rule 0 x 68 [0,3,8]
+ CRUSH rule 0 x 69 [5,2,7]
+ CRUSH rule 0 x 70 [7,2,3]
+ CRUSH rule 0 x 71 [2,7,4]
+ CRUSH rule 0 x 72 [6,2,4]
+ CRUSH rule 0 x 73 [2,7,3]
+ CRUSH rule 0 x 74 [0,8,3]
+ CRUSH rule 0 x 75 [3,0,7]
+ CRUSH rule 0 x 76 [5,2,6]
+ CRUSH rule 0 x 77 [7,0,5]
+ CRUSH rule 0 x 78 [1,4,7]
+ CRUSH rule 0 x 79 [5,0,7]
+ CRUSH rule 0 x 80 [0,4,6]
+ CRUSH rule 0 x 81 [0,5,8]
+ CRUSH rule 0 x 82 [7,2,5]
+ CRUSH rule 0 x 83 [2,6,4]
+ CRUSH rule 0 x 84 [7,0,3]
+ CRUSH rule 0 x 85 [3,7,0]
+ CRUSH rule 0 x 86 [0,6,4]
+ CRUSH rule 0 x 87 [0,6,5]
+ CRUSH rule 0 x 88 [1,8,4]
+ CRUSH rule 0 x 89 [3,1,8]
+ CRUSH rule 0 x 90 [6,3,2]
+ CRUSH rule 0 x 91 [3,6,2]
+ CRUSH rule 0 x 92 [1,6,3]
+ CRUSH rule 0 x 93 [7,4,0]
+ CRUSH rule 0 x 94 [0,3,7]
+ CRUSH rule 0 x 95 [7,3,1]
+ CRUSH rule 0 x 96 [3,8,2]
+ CRUSH rule 0 x 97 [8,4,1]
+ CRUSH rule 0 x 98 [2,7,4]
+ CRUSH rule 0 x 99 [0,8,5]
+ CRUSH rule 0 x 100 [1,6,3]
+ CRUSH rule 0 x 101 [3,8,1]
+ CRUSH rule 0 x 102 [4,0,7]
+ CRUSH rule 0 x 103 [4,7,0]
+ CRUSH rule 0 x 104 [7,4,0]
+ CRUSH rule 0 x 105 [2,3,8]
+ CRUSH rule 0 x 106 [1,6,5]
+ CRUSH rule 0 x 107 [3,1,7]
+ CRUSH rule 0 x 108 [7,1,3]
+ CRUSH rule 0 x 109 [1,3,8]
+ CRUSH rule 0 x 110 [3,2,6]
+ CRUSH rule 0 x 111 [2,4,7]
+ CRUSH rule 0 x 112 [2,6,3]
+ CRUSH rule 0 x 113 [6,1,4]
+ CRUSH rule 0 x 114 [7,5,0]
+ CRUSH rule 0 x 115 [8,2,3]
+ CRUSH rule 0 x 116 [1,7,4]
+ CRUSH rule 0 x 117 [7,5,1]
+ CRUSH rule 0 x 118 [0,3,6]
+ CRUSH rule 0 x 119 [5,7,1]
+ CRUSH rule 0 x 120 [0,5,6]
+ CRUSH rule 0 x 121 [2,8,4]
+ CRUSH rule 0 x 122 [8,4,2]
+ CRUSH rule 0 x 123 [2,4,7]
+ CRUSH rule 0 x 124 [3,0,8]
+ CRUSH rule 0 x 125 [0,7,3]
+ CRUSH rule 0 x 126 [4,1,7]
+ CRUSH rule 0 x 127 [3,6,1]
+ CRUSH rule 0 x 128 [3,8,1]
+ CRUSH rule 0 x 129 [0,4,7]
+ CRUSH rule 0 x 130 [3,8,1]
+ CRUSH rule 0 x 131 [1,5,6]
+ CRUSH rule 0 x 132 [1,3,8]
+ CRUSH rule 0 x 133 [3,6,1]
+ CRUSH rule 0 x 134 [1,7,4]
+ CRUSH rule 0 x 135 [5,7,0]
+ CRUSH rule 0 x 136 [2,3,8]
+ CRUSH rule 0 x 137 [7,3,1]
+ CRUSH rule 0 x 138 [8,3,0]
+ CRUSH rule 0 x 139 [3,0,8]
+ CRUSH rule 0 x 140 [1,8,3]
+ CRUSH rule 0 x 141 [6,0,4]
+ CRUSH rule 0 x 142 [3,2,8]
+ CRUSH rule 0 x 143 [5,7,0]
+ CRUSH rule 0 x 144 [8,2,5]
+ CRUSH rule 0 x 145 [8,4,2]
+ CRUSH rule 0 x 146 [2,8,4]
+ CRUSH rule 0 x 147 [2,6,4]
+ CRUSH rule 0 x 148 [3,0,8]
+ CRUSH rule 0 x 149 [4,6,2]
+ CRUSH rule 0 x 150 [1,8,4]
+ CRUSH rule 0 x 151 [3,8,1]
+ CRUSH rule 0 x 152 [8,3,0]
+ CRUSH rule 0 x 153 [8,3,0]
+ CRUSH rule 0 x 154 [3,0,6]
+ CRUSH rule 0 x 155 [3,6,0]
+ CRUSH rule 0 x 156 [4,2,7]
+ CRUSH rule 0 x 157 [4,1,7]
+ CRUSH rule 0 x 158 [2,6,3]
+ CRUSH rule 0 x 159 [7,0,3]
+ CRUSH rule 0 x 160 [2,8,5]
+ CRUSH rule 0 x 161 [1,3,6]
+ CRUSH rule 0 x 162 [0,8,5]
+ CRUSH rule 0 x 163 [5,8,2]
+ CRUSH rule 0 x 164 [7,2,4]
+ CRUSH rule 0 x 165 [7,2,4]
+ CRUSH rule 0 x 166 [2,3,7]
+ CRUSH rule 0 x 167 [0,6,3]
+ CRUSH rule 0 x 168 [4,0,7]
+ CRUSH rule 0 x 169 [2,7,3]
+ CRUSH rule 0 x 170 [1,3,8]
+ CRUSH rule 0 x 171 [7,3,2]
+ CRUSH rule 0 x 172 [0,8,4]
+ CRUSH rule 0 x 173 [8,5,1]
+ CRUSH rule 0 x 174 [1,4,8]
+ CRUSH rule 0 x 175 [6,0,3]
+ CRUSH rule 0 x 176 [4,2,7]
+ CRUSH rule 0 x 177 [5,1,8]
+ CRUSH rule 0 x 178 [3,0,6]
+ CRUSH rule 0 x 179 [4,1,7]
+ CRUSH rule 0 x 180 [3,7,2]
+ CRUSH rule 0 x 181 [6,0,5]
+ CRUSH rule 0 x 182 [8,3,1]
+ CRUSH rule 0 x 183 [7,5,1]
+ CRUSH rule 0 x 184 [5,6,1]
+ CRUSH rule 0 x 185 [6,0,3]
+ CRUSH rule 0 x 186 [2,4,8]
+ CRUSH rule 0 x 187 [1,6,3]
+ CRUSH rule 0 x 188 [1,6,3]
+ CRUSH rule 0 x 189 [0,6,5]
+ CRUSH rule 0 x 190 [4,1,8]
+ CRUSH rule 0 x 191 [7,0,5]
+ CRUSH rule 0 x 192 [5,2,6]
+ CRUSH rule 0 x 193 [4,0,6]
+ CRUSH rule 0 x 194 [1,3,7]
+ CRUSH rule 0 x 195 [6,5,2]
+ CRUSH rule 0 x 196 [6,1,3]
+ CRUSH rule 0 x 197 [6,5,1]
+ CRUSH rule 0 x 198 [2,3,6]
+ CRUSH rule 0 x 199 [0,5,7]
+ CRUSH rule 0 x 200 [0,3,8]
+ CRUSH rule 0 x 201 [7,1,5]
+ CRUSH rule 0 x 202 [6,4,1]
+ CRUSH rule 0 x 203 [4,6,1]
+ CRUSH rule 0 x 204 [2,4,8]
+ CRUSH rule 0 x 205 [0,7,4]
+ CRUSH rule 0 x 206 [0,8,4]
+ CRUSH rule 0 x 207 [3,2,7]
+ CRUSH rule 0 x 208 [7,2,4]
+ CRUSH rule 0 x 209 [1,8,3]
+ CRUSH rule 0 x 210 [1,4,6]
+ CRUSH rule 0 x 211 [5,2,7]
+ CRUSH rule 0 x 212 [7,5,0]
+ CRUSH rule 0 x 213 [8,4,0]
+ CRUSH rule 0 x 214 [4,7,1]
+ CRUSH rule 0 x 215 [8,0,5]
+ CRUSH rule 0 x 216 [5,2,8]
+ CRUSH rule 0 x 217 [0,7,5]
+ CRUSH rule 0 x 218 [0,7,4]
+ CRUSH rule 0 x 219 [4,6,0]
+ CRUSH rule 0 x 220 [5,8,0]
+ CRUSH rule 0 x 221 [3,7,0]
+ CRUSH rule 0 x 222 [6,4,1]
+ CRUSH rule 0 x 223 [1,3,6]
+ CRUSH rule 0 x 224 [1,3,8]
+ CRUSH rule 0 x 225 [8,0,3]
+ CRUSH rule 0 x 226 [7,0,4]
+ CRUSH rule 0 x 227 [3,0,7]
+ CRUSH rule 0 x 228 [5,6,1]
+ CRUSH rule 0 x 229 [3,7,0]
+ CRUSH rule 0 x 230 [4,6,1]
+ CRUSH rule 0 x 231 [4,7,1]
+ CRUSH rule 0 x 232 [2,8,4]
+ CRUSH rule 0 x 233 [3,6,0]
+ CRUSH rule 0 x 234 [0,4,6]
+ CRUSH rule 0 x 235 [3,6,1]
+ CRUSH rule 0 x 236 [5,0,8]
+ CRUSH rule 0 x 237 [4,8,0]
+ CRUSH rule 0 x 238 [4,2,6]
+ CRUSH rule 0 x 239 [8,3,2]
+ CRUSH rule 0 x 240 [5,8,2]
+ CRUSH rule 0 x 241 [3,1,7]
+ CRUSH rule 0 x 242 [3,2,6]
+ CRUSH rule 0 x 243 [4,8,2]
+ CRUSH rule 0 x 244 [4,6,0]
+ CRUSH rule 0 x 245 [7,2,3]
+ CRUSH rule 0 x 246 [1,3,7]
+ CRUSH rule 0 x 247 [6,1,4]
+ CRUSH rule 0 x 248 [8,0,3]
+ CRUSH rule 0 x 249 [2,5,8]
+ CRUSH rule 0 x 250 [2,3,8]
+ CRUSH rule 0 x 251 [2,3,7]
+ CRUSH rule 0 x 252 [3,7,2]
+ CRUSH rule 0 x 253 [3,0,7]
+ CRUSH rule 0 x 254 [3,2,8]
+ CRUSH rule 0 x 255 [1,7,4]
+ CRUSH rule 0 x 256 [5,6,1]
+ CRUSH rule 0 x 257 [2,6,4]
+ CRUSH rule 0 x 258 [5,2,8]
+ CRUSH rule 0 x 259 [4,6,1]
+ CRUSH rule 0 x 260 [3,8,1]
+ CRUSH rule 0 x 261 [8,5,2]
+ CRUSH rule 0 x 262 [5,6,2]
+ CRUSH rule 0 x 263 [6,1,5]
+ CRUSH rule 0 x 264 [3,6,1]
+ CRUSH rule 0 x 265 [8,4,0]
+ CRUSH rule 0 x 266 [8,0,4]
+ CRUSH rule 0 x 267 [2,4,6]
+ CRUSH rule 0 x 268 [0,6,4]
+ CRUSH rule 0 x 269 [0,6,5]
+ CRUSH rule 0 x 270 [5,1,8]
+ CRUSH rule 0 x 271 [7,4,2]
+ CRUSH rule 0 x 272 [2,6,5]
+ CRUSH rule 0 x 273 [3,2,7]
+ CRUSH rule 0 x 274 [6,3,2]
+ CRUSH rule 0 x 275 [4,8,1]
+ CRUSH rule 0 x 276 [7,0,5]
+ CRUSH rule 0 x 277 [6,4,1]
+ CRUSH rule 0 x 278 [6,2,3]
+ CRUSH rule 0 x 279 [8,5,0]
+ CRUSH rule 0 x 280 [0,7,4]
+ CRUSH rule 0 x 281 [8,1,5]
+ CRUSH rule 0 x 282 [3,2,8]
+ CRUSH rule 0 x 283 [8,1,3]
+ CRUSH rule 0 x 284 [6,3,1]
+ CRUSH rule 0 x 285 [5,7,2]
+ CRUSH rule 0 x 286 [2,8,4]
+ CRUSH rule 0 x 287 [0,3,8]
+ CRUSH rule 0 x 288 [8,1,4]
+ CRUSH rule 0 x 289 [4,6,0]
+ CRUSH rule 0 x 290 [1,5,6]
+ CRUSH rule 0 x 291 [0,5,8]
+ CRUSH rule 0 x 292 [8,1,4]
+ CRUSH rule 0 x 293 [6,2,3]
+ CRUSH rule 0 x 294 [7,5,2]
+ CRUSH rule 0 x 295 [4,6,0]
+ CRUSH rule 0 x 296 [3,1,6]
+ CRUSH rule 0 x 297 [6,0,5]
+ CRUSH rule 0 x 298 [1,5,6]
+ CRUSH rule 0 x 299 [2,8,4]
+ CRUSH rule 0 x 300 [8,4,1]
+ CRUSH rule 0 x 301 [0,7,3]
+ CRUSH rule 0 x 302 [3,1,6]
+ CRUSH rule 0 x 303 [7,4,1]
+ CRUSH rule 0 x 304 [2,6,4]
+ CRUSH rule 0 x 305 [5,6,1]
+ CRUSH rule 0 x 306 [0,8,3]
+ CRUSH rule 0 x 307 [0,7,4]
+ CRUSH rule 0 x 308 [0,8,5]
+ CRUSH rule 0 x 309 [7,4,2]
+ CRUSH rule 0 x 310 [4,0,6]
+ CRUSH rule 0 x 311 [3,8,2]
+ CRUSH rule 0 x 312 [2,6,5]
+ CRUSH rule 0 x 313 [5,1,7]
+ CRUSH rule 0 x 314 [4,0,6]
+ CRUSH rule 0 x 315 [2,3,7]
+ CRUSH rule 0 x 316 [6,4,1]
+ CRUSH rule 0 x 317 [2,7,4]
+ CRUSH rule 0 x 318 [8,1,4]
+ CRUSH rule 0 x 319 [5,2,6]
+ CRUSH rule 0 x 320 [3,8,0]
+ CRUSH rule 0 x 321 [1,5,8]
+ CRUSH rule 0 x 322 [2,6,3]
+ CRUSH rule 0 x 323 [4,7,0]
+ CRUSH rule 0 x 324 [7,2,5]
+ CRUSH rule 0 x 325 [4,6,0]
+ CRUSH rule 0 x 326 [3,1,7]
+ CRUSH rule 0 x 327 [0,8,3]
+ CRUSH rule 0 x 328 [7,3,1]
+ CRUSH rule 0 x 329 [5,7,0]
+ CRUSH rule 0 x 330 [3,7,0]
+ CRUSH rule 0 x 331 [2,7,3]
+ CRUSH rule 0 x 332 [2,3,8]
+ CRUSH rule 0 x 333 [6,4,1]
+ CRUSH rule 0 x 334 [8,3,2]
+ CRUSH rule 0 x 335 [7,2,3]
+ CRUSH rule 0 x 336 [4,6,2]
+ CRUSH rule 0 x 337 [7,0,4]
+ CRUSH rule 0 x 338 [5,8,2]
+ CRUSH rule 0 x 339 [7,5,0]
+ CRUSH rule 0 x 340 [2,6,5]
+ CRUSH rule 0 x 341 [5,1,7]
+ CRUSH rule 0 x 342 [0,8,5]
+ CRUSH rule 0 x 343 [6,5,0]
+ CRUSH rule 0 x 344 [6,1,4]
+ CRUSH rule 0 x 345 [4,7,0]
+ CRUSH rule 0 x 346 [8,0,3]
+ CRUSH rule 0 x 347 [3,0,8]
+ CRUSH rule 0 x 348 [8,0,3]
+ CRUSH rule 0 x 349 [1,7,3]
+ CRUSH rule 0 x 350 [8,5,1]
+ CRUSH rule 0 x 351 [3,8,0]
+ CRUSH rule 0 x 352 [1,8,4]
+ CRUSH rule 0 x 353 [6,5,1]
+ CRUSH rule 0 x 354 [0,5,6]
+ CRUSH rule 0 x 355 [3,8,0]
+ CRUSH rule 0 x 356 [3,1,8]
+ CRUSH rule 0 x 357 [6,1,3]
+ CRUSH rule 0 x 358 [2,8,5]
+ CRUSH rule 0 x 359 [6,0,5]
+ CRUSH rule 0 x 360 [5,0,8]
+ CRUSH rule 0 x 361 [8,5,0]
+ CRUSH rule 0 x 362 [4,0,8]
+ CRUSH rule 0 x 363 [4,2,8]
+ CRUSH rule 0 x 364 [2,5,7]
+ CRUSH rule 0 x 365 [6,4,2]
+ CRUSH rule 0 x 366 [7,0,3]
+ CRUSH rule 0 x 367 [4,2,7]
+ CRUSH rule 0 x 368 [7,3,1]
+ CRUSH rule 0 x 369 [3,7,2]
+ CRUSH rule 0 x 370 [8,2,4]
+ CRUSH rule 0 x 371 [1,5,8]
+ CRUSH rule 0 x 372 [3,1,8]
+ CRUSH rule 0 x 373 [0,6,4]
+ CRUSH rule 0 x 374 [3,8,1]
+ CRUSH rule 0 x 375 [6,5,2]
+ CRUSH rule 0 x 376 [7,1,3]
+ CRUSH rule 0 x 377 [1,4,7]
+ CRUSH rule 0 x 378 [0,6,4]
+ CRUSH rule 0 x 379 [8,3,0]
+ CRUSH rule 0 x 380 [2,3,8]
+ CRUSH rule 0 x 381 [0,3,7]
+ CRUSH rule 0 x 382 [1,3,7]
+ CRUSH rule 0 x 383 [4,7,1]
+ CRUSH rule 0 x 384 [7,0,4]
+ CRUSH rule 0 x 385 [7,4,0]
+ CRUSH rule 0 x 386 [0,4,6]
+ CRUSH rule 0 x 387 [1,5,8]
+ CRUSH rule 0 x 388 [5,2,7]
+ CRUSH rule 0 x 389 [1,4,8]
+ CRUSH rule 0 x 390 [5,8,1]
+ CRUSH rule 0 x 391 [5,6,0]
+ CRUSH rule 0 x 392 [1,7,5]
+ CRUSH rule 0 x 393 [4,0,6]
+ CRUSH rule 0 x 394 [4,8,2]
+ CRUSH rule 0 x 395 [4,0,8]
+ CRUSH rule 0 x 396 [4,2,6]
+ CRUSH rule 0 x 397 [2,4,7]
+ CRUSH rule 0 x 398 [2,3,6]
+ CRUSH rule 0 x 399 [8,5,2]
+ CRUSH rule 0 x 400 [8,1,3]
+ CRUSH rule 0 x 401 [0,3,6]
+ CRUSH rule 0 x 402 [7,4,2]
+ CRUSH rule 0 x 403 [0,4,7]
+ CRUSH rule 0 x 404 [4,0,6]
+ CRUSH rule 0 x 405 [6,4,0]
+ CRUSH rule 0 x 406 [2,6,4]
+ CRUSH rule 0 x 407 [2,7,5]
+ CRUSH rule 0 x 408 [4,1,7]
+ CRUSH rule 0 x 409 [7,5,0]
+ CRUSH rule 0 x 410 [8,3,1]
+ CRUSH rule 0 x 411 [2,7,4]
+ CRUSH rule 0 x 412 [0,3,7]
+ CRUSH rule 0 x 413 [5,2,8]
+ CRUSH rule 0 x 414 [4,0,8]
+ CRUSH rule 0 x 415 [0,6,3]
+ CRUSH rule 0 x 416 [2,8,5]
+ CRUSH rule 0 x 417 [8,0,3]
+ CRUSH rule 0 x 418 [7,1,3]
+ CRUSH rule 0 x 419 [8,5,2]
+ CRUSH rule 0 x 420 [1,4,7]
+ CRUSH rule 0 x 421 [8,3,0]
+ CRUSH rule 0 x 422 [6,3,1]
+ CRUSH rule 0 x 423 [0,3,7]
+ CRUSH rule 0 x 424 [8,5,1]
+ CRUSH rule 0 x 425 [1,4,8]
+ CRUSH rule 0 x 426 [6,2,4]
+ CRUSH rule 0 x 427 [0,8,3]
+ CRUSH rule 0 x 428 [5,8,1]
+ CRUSH rule 0 x 429 [4,8,0]
+ CRUSH rule 0 x 430 [3,7,0]
+ CRUSH rule 0 x 431 [5,0,7]
+ CRUSH rule 0 x 432 [7,0,4]
+ CRUSH rule 0 x 433 [6,4,0]
+ CRUSH rule 0 x 434 [5,0,7]
+ CRUSH rule 0 x 435 [0,4,6]
+ CRUSH rule 0 x 436 [4,0,7]
+ CRUSH rule 0 x 437 [7,3,1]
+ CRUSH rule 0 x 438 [0,5,8]
+ CRUSH rule 0 x 439 [1,3,8]
+ CRUSH rule 0 x 440 [2,6,5]
+ CRUSH rule 0 x 441 [5,8,0]
+ CRUSH rule 0 x 442 [2,3,6]
+ CRUSH rule 0 x 443 [6,0,3]
+ CRUSH rule 0 x 444 [7,1,4]
+ CRUSH rule 0 x 445 [6,4,0]
+ CRUSH rule 0 x 446 [4,0,8]
+ CRUSH rule 0 x 447 [2,4,6]
+ CRUSH rule 0 x 448 [7,0,5]
+ CRUSH rule 0 x 449 [7,4,2]
+ CRUSH rule 0 x 450 [4,0,6]
+ CRUSH rule 0 x 451 [6,5,1]
+ CRUSH rule 0 x 452 [8,4,0]
+ CRUSH rule 0 x 453 [6,5,0]
+ CRUSH rule 0 x 454 [6,5,0]
+ CRUSH rule 0 x 455 [2,8,4]
+ CRUSH rule 0 x 456 [6,2,5]
+ CRUSH rule 0 x 457 [7,1,5]
+ CRUSH rule 0 x 458 [2,8,5]
+ CRUSH rule 0 x 459 [2,6,5]
+ CRUSH rule 0 x 460 [6,5,2]
+ CRUSH rule 0 x 461 [6,4,2]
+ CRUSH rule 0 x 462 [8,0,4]
+ CRUSH rule 0 x 463 [6,0,4]
+ CRUSH rule 0 x 464 [7,4,1]
+ CRUSH rule 0 x 465 [7,1,5]
+ CRUSH rule 0 x 466 [5,6,2]
+ CRUSH rule 0 x 467 [6,5,1]
+ CRUSH rule 0 x 468 [7,2,3]
+ CRUSH rule 0 x 469 [7,2,4]
+ CRUSH rule 0 x 470 [3,0,8]
+ CRUSH rule 0 x 471 [0,6,4]
+ CRUSH rule 0 x 472 [5,0,8]
+ CRUSH rule 0 x 473 [1,4,7]
+ CRUSH rule 0 x 474 [6,1,4]
+ CRUSH rule 0 x 475 [6,2,3]
+ CRUSH rule 0 x 476 [4,7,1]
+ CRUSH rule 0 x 477 [5,6,1]
+ CRUSH rule 0 x 478 [6,1,3]
+ CRUSH rule 0 x 479 [0,3,6]
+ CRUSH rule 0 x 480 [1,6,4]
+ CRUSH rule 0 x 481 [2,5,7]
+ CRUSH rule 0 x 482 [4,8,0]
+ CRUSH rule 0 x 483 [0,6,4]
+ CRUSH rule 0 x 484 [1,8,5]
+ CRUSH rule 0 x 485 [4,8,1]
+ CRUSH rule 0 x 486 [4,0,8]
+ CRUSH rule 0 x 487 [5,0,8]
+ CRUSH rule 0 x 488 [5,7,1]
+ CRUSH rule 0 x 489 [2,8,4]
+ CRUSH rule 0 x 490 [6,4,0]
+ CRUSH rule 0 x 491 [1,6,5]
+ CRUSH rule 0 x 492 [6,5,2]
+ CRUSH rule 0 x 493 [0,8,3]
+ CRUSH rule 0 x 494 [1,6,4]
+ CRUSH rule 0 x 495 [3,0,6]
+ CRUSH rule 0 x 496 [7,5,1]
+ CRUSH rule 0 x 497 [5,7,2]
+ CRUSH rule 0 x 498 [0,4,6]
+ CRUSH rule 0 x 499 [8,5,2]
+ CRUSH rule 0 x 500 [3,6,1]
+ CRUSH rule 0 x 501 [0,7,5]
+ CRUSH rule 0 x 502 [7,1,3]
+ CRUSH rule 0 x 503 [2,3,7]
+ CRUSH rule 0 x 504 [5,8,2]
+ CRUSH rule 0 x 505 [0,7,5]
+ CRUSH rule 0 x 506 [5,1,7]
+ CRUSH rule 0 x 507 [6,0,3]
+ CRUSH rule 0 x 508 [0,4,7]
+ CRUSH rule 0 x 509 [7,4,0]
+ CRUSH rule 0 x 510 [6,2,5]
+ CRUSH rule 0 x 511 [5,6,2]
+ CRUSH rule 0 x 512 [7,2,4]
+ CRUSH rule 0 x 513 [7,2,4]
+ CRUSH rule 0 x 514 [4,7,0]
+ CRUSH rule 0 x 515 [8,3,0]
+ CRUSH rule 0 x 516 [4,1,7]
+ CRUSH rule 0 x 517 [7,0,4]
+ CRUSH rule 0 x 518 [4,6,0]
+ CRUSH rule 0 x 519 [7,3,1]
+ CRUSH rule 0 x 520 [2,8,5]
+ CRUSH rule 0 x 521 [8,0,3]
+ CRUSH rule 0 x 522 [6,0,4]
+ CRUSH rule 0 x 523 [4,1,7]
+ CRUSH rule 0 x 524 [0,4,7]
+ CRUSH rule 0 x 525 [0,3,8]
+ CRUSH rule 0 x 526 [1,3,8]
+ CRUSH rule 0 x 527 [0,4,6]
+ CRUSH rule 0 x 528 [5,2,7]
+ CRUSH rule 0 x 529 [5,6,1]
+ CRUSH rule 0 x 530 [6,4,1]
+ CRUSH rule 0 x 531 [6,0,3]
+ CRUSH rule 0 x 532 [6,5,1]
+ CRUSH rule 0 x 533 [5,8,0]
+ CRUSH rule 0 x 534 [7,4,2]
+ CRUSH rule 0 x 535 [8,0,3]
+ CRUSH rule 0 x 536 [6,2,3]
+ CRUSH rule 0 x 537 [3,8,0]
+ CRUSH rule 0 x 538 [6,4,2]
+ CRUSH rule 0 x 539 [8,4,2]
+ CRUSH rule 0 x 540 [0,7,4]
+ CRUSH rule 0 x 541 [2,5,7]
+ CRUSH rule 0 x 542 [3,0,8]
+ CRUSH rule 0 x 543 [6,2,4]
+ CRUSH rule 0 x 544 [3,7,1]
+ CRUSH rule 0 x 545 [5,7,1]
+ CRUSH rule 0 x 546 [6,2,3]
+ CRUSH rule 0 x 547 [8,1,5]
+ CRUSH rule 0 x 548 [5,1,8]
+ CRUSH rule 0 x 549 [5,7,1]
+ CRUSH rule 0 x 550 [0,5,6]
+ CRUSH rule 0 x 551 [7,4,0]
+ CRUSH rule 0 x 552 [5,7,2]
+ CRUSH rule 0 x 553 [4,2,8]
+ CRUSH rule 0 x 554 [0,6,5]
+ CRUSH rule 0 x 555 [5,0,8]
+ CRUSH rule 0 x 556 [3,6,1]
+ CRUSH rule 0 x 557 [7,4,0]
+ CRUSH rule 0 x 558 [3,2,6]
+ CRUSH rule 0 x 559 [4,1,8]
+ CRUSH rule 0 x 560 [8,4,2]
+ CRUSH rule 0 x 561 [6,4,1]
+ CRUSH rule 0 x 562 [3,0,8]
+ CRUSH rule 0 x 563 [2,7,4]
+ CRUSH rule 0 x 564 [5,1,7]
+ CRUSH rule 0 x 565 [3,8,0]
+ CRUSH rule 0 x 566 [4,6,0]
+ CRUSH rule 0 x 567 [3,7,0]
+ CRUSH rule 0 x 568 [7,4,0]
+ CRUSH rule 0 x 569 [3,1,6]
+ CRUSH rule 0 x 570 [1,4,7]
+ CRUSH rule 0 x 571 [3,6,0]
+ CRUSH rule 0 x 572 [3,0,7]
+ CRUSH rule 0 x 573 [3,1,7]
+ CRUSH rule 0 x 574 [2,3,6]
+ CRUSH rule 0 x 575 [8,1,5]
+ CRUSH rule 0 x 576 [4,7,1]
+ CRUSH rule 0 x 577 [8,2,3]
+ CRUSH rule 0 x 578 [6,0,4]
+ CRUSH rule 0 x 579 [3,2,6]
+ CRUSH rule 0 x 580 [3,0,8]
+ CRUSH rule 0 x 581 [7,1,5]
+ CRUSH rule 0 x 582 [2,8,5]
+ CRUSH rule 0 x 583 [6,2,3]
+ CRUSH rule 0 x 584 [8,0,5]
+ CRUSH rule 0 x 585 [7,0,5]
+ CRUSH rule 0 x 586 [0,8,5]
+ CRUSH rule 0 x 587 [2,5,6]
+ CRUSH rule 0 x 588 [3,8,0]
+ CRUSH rule 0 x 589 [7,1,5]
+ CRUSH rule 0 x 590 [6,2,5]
+ CRUSH rule 0 x 591 [5,2,7]
+ CRUSH rule 0 x 592 [2,4,6]
+ CRUSH rule 0 x 593 [0,8,4]
+ CRUSH rule 0 x 594 [0,6,3]
+ CRUSH rule 0 x 595 [7,1,5]
+ CRUSH rule 0 x 596 [4,0,6]
+ CRUSH rule 0 x 597 [3,1,7]
+ CRUSH rule 0 x 598 [3,0,7]
+ CRUSH rule 0 x 599 [5,1,6]
+ CRUSH rule 0 x 600 [7,0,4]
+ CRUSH rule 0 x 601 [0,6,3]
+ CRUSH rule 0 x 602 [3,8,2]
+ CRUSH rule 0 x 603 [5,0,8]
+ CRUSH rule 0 x 604 [7,4,0]
+ CRUSH rule 0 x 605 [3,2,6]
+ CRUSH rule 0 x 606 [2,7,3]
+ CRUSH rule 0 x 607 [0,5,8]
+ CRUSH rule 0 x 608 [5,1,8]
+ CRUSH rule 0 x 609 [5,0,6]
+ CRUSH rule 0 x 610 [3,8,2]
+ CRUSH rule 0 x 611 [1,8,3]
+ CRUSH rule 0 x 612 [2,8,3]
+ CRUSH rule 0 x 613 [7,1,3]
+ CRUSH rule 0 x 614 [7,2,3]
+ CRUSH rule 0 x 615 [6,2,3]
+ CRUSH rule 0 x 616 [0,7,5]
+ CRUSH rule 0 x 617 [6,2,3]
+ CRUSH rule 0 x 618 [7,3,1]
+ CRUSH rule 0 x 619 [5,0,6]
+ CRUSH rule 0 x 620 [4,1,8]
+ CRUSH rule 0 x 621 [5,6,0]
+ CRUSH rule 0 x 622 [0,3,8]
+ CRUSH rule 0 x 623 [0,8,4]
+ CRUSH rule 0 x 624 [3,2,8]
+ CRUSH rule 0 x 625 [2,5,8]
+ CRUSH rule 0 x 626 [7,2,3]
+ CRUSH rule 0 x 627 [2,6,5]
+ CRUSH rule 0 x 628 [8,1,3]
+ CRUSH rule 0 x 629 [2,6,3]
+ CRUSH rule 0 x 630 [2,6,4]
+ CRUSH rule 0 x 631 [0,6,5]
+ CRUSH rule 0 x 632 [7,0,4]
+ CRUSH rule 0 x 633 [8,4,2]
+ CRUSH rule 0 x 634 [0,5,6]
+ CRUSH rule 0 x 635 [5,6,2]
+ CRUSH rule 0 x 636 [1,3,7]
+ CRUSH rule 0 x 637 [4,0,8]
+ CRUSH rule 0 x 638 [6,2,5]
+ CRUSH rule 0 x 639 [4,0,6]
+ CRUSH rule 0 x 640 [3,2,7]
+ CRUSH rule 0 x 641 [7,2,3]
+ CRUSH rule 0 x 642 [2,8,4]
+ CRUSH rule 0 x 643 [3,0,8]
+ CRUSH rule 0 x 644 [8,0,4]
+ CRUSH rule 0 x 645 [5,7,2]
+ CRUSH rule 0 x 646 [8,0,3]
+ CRUSH rule 0 x 647 [7,0,5]
+ CRUSH rule 0 x 648 [0,8,3]
+ CRUSH rule 0 x 649 [4,7,1]
+ CRUSH rule 0 x 650 [7,5,1]
+ CRUSH rule 0 x 651 [3,6,1]
+ CRUSH rule 0 x 652 [3,6,1]
+ CRUSH rule 0 x 653 [8,3,0]
+ CRUSH rule 0 x 654 [7,4,0]
+ CRUSH rule 0 x 655 [0,5,6]
+ CRUSH rule 0 x 656 [4,7,0]
+ CRUSH rule 0 x 657 [6,0,5]
+ CRUSH rule 0 x 658 [5,8,0]
+ CRUSH rule 0 x 659 [4,7,0]
+ CRUSH rule 0 x 660 [7,3,0]
+ CRUSH rule 0 x 661 [1,7,3]
+ CRUSH rule 0 x 662 [4,2,8]
+ CRUSH rule 0 x 663 [1,3,7]
+ CRUSH rule 0 x 664 [1,3,6]
+ CRUSH rule 0 x 665 [5,6,1]
+ CRUSH rule 0 x 666 [2,7,4]
+ CRUSH rule 0 x 667 [1,3,8]
+ CRUSH rule 0 x 668 [3,7,0]
+ CRUSH rule 0 x 669 [6,3,0]
+ CRUSH rule 0 x 670 [4,1,6]
+ CRUSH rule 0 x 671 [0,8,5]
+ CRUSH rule 0 x 672 [4,2,8]
+ CRUSH rule 0 x 673 [5,0,7]
+ CRUSH rule 0 x 674 [3,0,7]
+ CRUSH rule 0 x 675 [0,8,5]
+ CRUSH rule 0 x 676 [0,3,7]
+ CRUSH rule 0 x 677 [4,1,6]
+ CRUSH rule 0 x 678 [2,3,8]
+ CRUSH rule 0 x 679 [6,0,5]
+ CRUSH rule 0 x 680 [0,3,8]
+ CRUSH rule 0 x 681 [4,6,1]
+ CRUSH rule 0 x 682 [0,4,8]
+ CRUSH rule 0 x 683 [0,3,8]
+ CRUSH rule 0 x 684 [7,2,5]
+ CRUSH rule 0 x 685 [7,2,5]
+ CRUSH rule 0 x 686 [1,5,8]
+ CRUSH rule 0 x 687 [3,7,2]
+ CRUSH rule 0 x 688 [5,6,2]
+ CRUSH rule 0 x 689 [6,5,1]
+ CRUSH rule 0 x 690 [8,0,4]
+ CRUSH rule 0 x 691 [3,1,6]
+ CRUSH rule 0 x 692 [7,1,4]
+ CRUSH rule 0 x 693 [6,5,2]
+ CRUSH rule 0 x 694 [6,4,2]
+ CRUSH rule 0 x 695 [0,6,3]
+ CRUSH rule 0 x 696 [1,5,8]
+ CRUSH rule 0 x 697 [6,0,4]
+ CRUSH rule 0 x 698 [6,0,3]
+ CRUSH rule 0 x 699 [1,8,3]
+ CRUSH rule 0 x 700 [0,4,6]
+ CRUSH rule 0 x 701 [4,0,6]
+ CRUSH rule 0 x 702 [3,0,8]
+ CRUSH rule 0 x 703 [8,4,0]
+ CRUSH rule 0 x 704 [0,4,6]
+ CRUSH rule 0 x 705 [8,0,4]
+ CRUSH rule 0 x 706 [1,5,8]
+ CRUSH rule 0 x 707 [7,3,0]
+ CRUSH rule 0 x 708 [3,7,1]
+ CRUSH rule 0 x 709 [6,5,1]
+ CRUSH rule 0 x 710 [8,5,1]
+ CRUSH rule 0 x 711 [2,4,7]
+ CRUSH rule 0 x 712 [2,3,6]
+ CRUSH rule 0 x 713 [6,3,1]
+ CRUSH rule 0 x 714 [3,0,6]
+ CRUSH rule 0 x 715 [1,3,6]
+ CRUSH rule 0 x 716 [3,6,1]
+ CRUSH rule 0 x 717 [8,0,5]
+ CRUSH rule 0 x 718 [3,7,0]
+ CRUSH rule 0 x 719 [2,6,3]
+ CRUSH rule 0 x 720 [6,0,4]
+ CRUSH rule 0 x 721 [5,7,0]
+ CRUSH rule 0 x 722 [5,7,1]
+ CRUSH rule 0 x 723 [5,2,7]
+ CRUSH rule 0 x 724 [0,7,3]
+ CRUSH rule 0 x 725 [0,4,6]
+ CRUSH rule 0 x 726 [3,7,2]
+ CRUSH rule 0 x 727 [4,7,2]
+ CRUSH rule 0 x 728 [2,6,3]
+ CRUSH rule 0 x 729 [5,6,1]
+ CRUSH rule 0 x 730 [3,8,2]
+ CRUSH rule 0 x 731 [4,1,6]
+ CRUSH rule 0 x 732 [1,4,8]
+ CRUSH rule 0 x 733 [5,6,2]
+ CRUSH rule 0 x 734 [6,5,0]
+ CRUSH rule 0 x 735 [4,6,1]
+ CRUSH rule 0 x 736 [3,8,1]
+ CRUSH rule 0 x 737 [1,8,3]
+ CRUSH rule 0 x 738 [5,1,6]
+ CRUSH rule 0 x 739 [0,7,4]
+ CRUSH rule 0 x 740 [0,7,3]
+ CRUSH rule 0 x 741 [7,2,3]
+ CRUSH rule 0 x 742 [8,2,3]
+ CRUSH rule 0 x 743 [7,0,5]
+ CRUSH rule 0 x 744 [4,6,1]
+ CRUSH rule 0 x 745 [3,0,8]
+ CRUSH rule 0 x 746 [4,1,7]
+ CRUSH rule 0 x 747 [6,2,4]
+ CRUSH rule 0 x 748 [2,6,5]
+ CRUSH rule 0 x 749 [4,7,2]
+ CRUSH rule 0 x 750 [1,7,4]
+ CRUSH rule 0 x 751 [2,7,3]
+ CRUSH rule 0 x 752 [8,0,3]
+ CRUSH rule 0 x 753 [7,4,2]
+ CRUSH rule 0 x 754 [8,5,2]
+ CRUSH rule 0 x 755 [1,6,5]
+ CRUSH rule 0 x 756 [5,8,1]
+ CRUSH rule 0 x 757 [8,0,4]
+ CRUSH rule 0 x 758 [6,1,5]
+ CRUSH rule 0 x 759 [8,5,0]
+ CRUSH rule 0 x 760 [1,5,7]
+ CRUSH rule 0 x 761 [4,2,6]
+ CRUSH rule 0 x 762 [2,8,3]
+ CRUSH rule 0 x 763 [8,3,1]
+ CRUSH rule 0 x 764 [1,7,4]
+ CRUSH rule 0 x 765 [6,4,0]
+ CRUSH rule 0 x 766 [8,3,0]
+ CRUSH rule 0 x 767 [1,8,5]
+ CRUSH rule 0 x 768 [8,5,0]
+ CRUSH rule 0 x 769 [6,2,4]
+ CRUSH rule 0 x 770 [6,1,4]
+ CRUSH rule 0 x 771 [7,2,4]
+ CRUSH rule 0 x 772 [8,4,2]
+ CRUSH rule 0 x 773 [3,2,6]
+ CRUSH rule 0 x 774 [4,7,1]
+ CRUSH rule 0 x 775 [6,5,0]
+ CRUSH rule 0 x 776 [7,0,3]
+ CRUSH rule 0 x 777 [3,0,6]
+ CRUSH rule 0 x 778 [1,6,3]
+ CRUSH rule 0 x 779 [2,6,5]
+ CRUSH rule 0 x 780 [0,5,8]
+ CRUSH rule 0 x 781 [6,5,0]
+ CRUSH rule 0 x 782 [5,2,8]
+ CRUSH rule 0 x 783 [7,0,3]
+ CRUSH rule 0 x 784 [0,5,8]
+ CRUSH rule 0 x 785 [6,1,3]
+ CRUSH rule 0 x 786 [7,3,0]
+ CRUSH rule 0 x 787 [1,8,5]
+ CRUSH rule 0 x 788 [6,0,4]
+ CRUSH rule 0 x 789 [0,5,8]
+ CRUSH rule 0 x 790 [8,3,1]
+ CRUSH rule 0 x 791 [3,6,2]
+ CRUSH rule 0 x 792 [5,6,1]
+ CRUSH rule 0 x 793 [6,2,3]
+ CRUSH rule 0 x 794 [2,8,3]
+ CRUSH rule 0 x 795 [0,4,6]
+ CRUSH rule 0 x 796 [3,7,1]
+ CRUSH rule 0 x 797 [2,3,6]
+ CRUSH rule 0 x 798 [6,0,3]
+ CRUSH rule 0 x 799 [5,2,7]
+ CRUSH rule 0 x 800 [5,2,8]
+ CRUSH rule 0 x 801 [3,7,2]
+ CRUSH rule 0 x 802 [1,6,4]
+ CRUSH rule 0 x 803 [0,4,7]
+ CRUSH rule 0 x 804 [6,0,5]
+ CRUSH rule 0 x 805 [3,8,0]
+ CRUSH rule 0 x 806 [1,5,6]
+ CRUSH rule 0 x 807 [5,7,2]
+ CRUSH rule 0 x 808 [4,7,2]
+ CRUSH rule 0 x 809 [1,3,6]
+ CRUSH rule 0 x 810 [5,7,2]
+ CRUSH rule 0 x 811 [8,4,1]
+ CRUSH rule 0 x 812 [8,3,1]
+ CRUSH rule 0 x 813 [6,4,2]
+ CRUSH rule 0 x 814 [3,8,0]
+ CRUSH rule 0 x 815 [3,1,6]
+ CRUSH rule 0 x 816 [2,6,5]
+ CRUSH rule 0 x 817 [4,6,2]
+ CRUSH rule 0 x 818 [3,1,7]
+ CRUSH rule 0 x 819 [5,0,7]
+ CRUSH rule 0 x 820 [3,7,1]
+ CRUSH rule 0 x 821 [4,8,0]
+ CRUSH rule 0 x 822 [2,3,7]
+ CRUSH rule 0 x 823 [4,7,2]
+ CRUSH rule 0 x 824 [3,7,1]
+ CRUSH rule 0 x 825 [2,8,4]
+ CRUSH rule 0 x 826 [7,1,5]
+ CRUSH rule 0 x 827 [0,6,3]
+ CRUSH rule 0 x 828 [2,5,8]
+ CRUSH rule 0 x 829 [5,6,0]
+ CRUSH rule 0 x 830 [2,4,7]
+ CRUSH rule 0 x 831 [1,6,4]
+ CRUSH rule 0 x 832 [4,8,2]
+ CRUSH rule 0 x 833 [2,6,5]
+ CRUSH rule 0 x 834 [3,0,8]
+ CRUSH rule 0 x 835 [8,5,0]
+ CRUSH rule 0 x 836 [3,8,1]
+ CRUSH rule 0 x 837 [6,4,0]
+ CRUSH rule 0 x 838 [6,0,4]
+ CRUSH rule 0 x 839 [5,2,8]
+ CRUSH rule 0 x 840 [7,3,2]
+ CRUSH rule 0 x 841 [4,8,0]
+ CRUSH rule 0 x 842 [2,5,8]
+ CRUSH rule 0 x 843 [6,4,2]
+ CRUSH rule 0 x 844 [4,8,2]
+ CRUSH rule 0 x 845 [3,6,2]
+ CRUSH rule 0 x 846 [3,0,6]
+ CRUSH rule 0 x 847 [0,8,5]
+ CRUSH rule 0 x 848 [2,6,5]
+ CRUSH rule 0 x 849 [4,8,1]
+ CRUSH rule 0 x 850 [1,5,7]
+ CRUSH rule 0 x 851 [6,5,1]
+ CRUSH rule 0 x 852 [7,4,2]
+ CRUSH rule 0 x 853 [6,2,5]
+ CRUSH rule 0 x 854 [7,0,3]
+ CRUSH rule 0 x 855 [5,7,2]
+ CRUSH rule 0 x 856 [6,3,1]
+ CRUSH rule 0 x 857 [8,4,0]
+ CRUSH rule 0 x 858 [6,5,2]
+ CRUSH rule 0 x 859 [6,0,5]
+ CRUSH rule 0 x 860 [4,2,8]
+ CRUSH rule 0 x 861 [8,4,1]
+ CRUSH rule 0 x 862 [6,0,5]
+ CRUSH rule 0 x 863 [8,1,5]
+ CRUSH rule 0 x 864 [5,6,1]
+ CRUSH rule 0 x 865 [8,0,3]
+ CRUSH rule 0 x 866 [3,6,2]
+ CRUSH rule 0 x 867 [6,3,1]
+ CRUSH rule 0 x 868 [6,4,2]
+ CRUSH rule 0 x 869 [8,4,2]
+ CRUSH rule 0 x 870 [0,5,7]
+ CRUSH rule 0 x 871 [3,1,6]
+ CRUSH rule 0 x 872 [5,0,6]
+ CRUSH rule 0 x 873 [4,6,1]
+ CRUSH rule 0 x 874 [2,6,4]
+ CRUSH rule 0 x 875 [2,6,5]
+ CRUSH rule 0 x 876 [5,8,1]
+ CRUSH rule 0 x 877 [6,4,0]
+ CRUSH rule 0 x 878 [5,0,7]
+ CRUSH rule 0 x 879 [7,3,1]
+ CRUSH rule 0 x 880 [3,1,7]
+ CRUSH rule 0 x 881 [5,8,2]
+ CRUSH rule 0 x 882 [4,1,6]
+ CRUSH rule 0 x 883 [2,5,6]
+ CRUSH rule 0 x 884 [6,0,3]
+ CRUSH rule 0 x 885 [5,1,6]
+ CRUSH rule 0 x 886 [3,7,2]
+ CRUSH rule 0 x 887 [7,5,2]
+ CRUSH rule 0 x 888 [6,1,4]
+ CRUSH rule 0 x 889 [2,8,3]
+ CRUSH rule 0 x 890 [7,1,4]
+ CRUSH rule 0 x 891 [1,7,4]
+ CRUSH rule 0 x 892 [6,0,5]
+ CRUSH rule 0 x 893 [2,3,6]
+ CRUSH rule 0 x 894 [7,4,1]
+ CRUSH rule 0 x 895 [5,1,6]
+ CRUSH rule 0 x 896 [1,6,3]
+ CRUSH rule 0 x 897 [4,0,8]
+ CRUSH rule 0 x 898 [0,3,8]
+ CRUSH rule 0 x 899 [1,6,4]
+ CRUSH rule 0 x 900 [4,0,7]
+ CRUSH rule 0 x 901 [5,2,6]
+ CRUSH rule 0 x 902 [8,4,2]
+ CRUSH rule 0 x 903 [5,6,1]
+ CRUSH rule 0 x 904 [5,7,0]
+ CRUSH rule 0 x 905 [6,0,3]
+ CRUSH rule 0 x 906 [1,7,4]
+ CRUSH rule 0 x 907 [7,2,3]
+ CRUSH rule 0 x 908 [5,6,1]
+ CRUSH rule 0 x 909 [2,3,8]
+ CRUSH rule 0 x 910 [6,5,0]
+ CRUSH rule 0 x 911 [5,7,1]
+ CRUSH rule 0 x 912 [0,8,5]
+ CRUSH rule 0 x 913 [7,1,4]
+ CRUSH rule 0 x 914 [6,4,0]
+ CRUSH rule 0 x 915 [8,2,3]
+ CRUSH rule 0 x 916 [3,0,6]
+ CRUSH rule 0 x 917 [1,3,6]
+ CRUSH rule 0 x 918 [8,0,5]
+ CRUSH rule 0 x 919 [6,2,3]
+ CRUSH rule 0 x 920 [7,4,2]
+ CRUSH rule 0 x 921 [1,4,8]
+ CRUSH rule 0 x 922 [6,3,1]
+ CRUSH rule 0 x 923 [5,6,0]
+ CRUSH rule 0 x 924 [3,1,8]
+ CRUSH rule 0 x 925 [5,6,0]
+ CRUSH rule 0 x 926 [3,0,7]
+ CRUSH rule 0 x 927 [1,6,5]
+ CRUSH rule 0 x 928 [8,1,4]
+ CRUSH rule 0 x 929 [4,2,6]
+ CRUSH rule 0 x 930 [2,5,8]
+ CRUSH rule 0 x 931 [5,2,8]
+ CRUSH rule 0 x 932 [4,2,7]
+ CRUSH rule 0 x 933 [8,4,2]
+ CRUSH rule 0 x 934 [5,8,0]
+ CRUSH rule 0 x 935 [6,3,0]
+ CRUSH rule 0 x 936 [0,7,3]
+ CRUSH rule 0 x 937 [5,8,1]
+ CRUSH rule 0 x 938 [6,4,0]
+ CRUSH rule 0 x 939 [2,8,3]
+ CRUSH rule 0 x 940 [8,5,2]
+ CRUSH rule 0 x 941 [5,0,6]
+ CRUSH rule 0 x 942 [1,6,4]
+ CRUSH rule 0 x 943 [8,2,4]
+ CRUSH rule 0 x 944 [4,8,1]
+ CRUSH rule 0 x 945 [7,0,3]
+ CRUSH rule 0 x 946 [2,8,3]
+ CRUSH rule 0 x 947 [4,2,6]
+ CRUSH rule 0 x 948 [7,4,1]
+ CRUSH rule 0 x 949 [6,2,5]
+ CRUSH rule 0 x 950 [3,7,0]
+ CRUSH rule 0 x 951 [4,6,2]
+ CRUSH rule 0 x 952 [2,7,4]
+ CRUSH rule 0 x 953 [1,3,6]
+ CRUSH rule 0 x 954 [4,0,8]
+ CRUSH rule 0 x 955 [8,1,4]
+ CRUSH rule 0 x 956 [1,7,4]
+ CRUSH rule 0 x 957 [7,0,3]
+ CRUSH rule 0 x 958 [8,3,2]
+ CRUSH rule 0 x 959 [5,1,6]
+ CRUSH rule 0 x 960 [3,6,2]
+ CRUSH rule 0 x 961 [4,1,6]
+ CRUSH rule 0 x 962 [7,5,0]
+ CRUSH rule 0 x 963 [0,5,8]
+ CRUSH rule 0 x 964 [3,2,7]
+ CRUSH rule 0 x 965 [7,3,1]
+ CRUSH rule 0 x 966 [3,6,0]
+ CRUSH rule 0 x 967 [8,4,2]
+ CRUSH rule 0 x 968 [7,0,4]
+ CRUSH rule 0 x 969 [8,0,4]
+ CRUSH rule 0 x 970 [0,8,3]
+ CRUSH rule 0 x 971 [1,8,5]
+ CRUSH rule 0 x 972 [1,7,5]
+ CRUSH rule 0 x 973 [1,8,3]
+ CRUSH rule 0 x 974 [5,1,7]
+ CRUSH rule 0 x 975 [3,8,0]
+ CRUSH rule 0 x 976 [4,7,2]
+ CRUSH rule 0 x 977 [8,3,2]
+ CRUSH rule 0 x 978 [7,0,3]
+ CRUSH rule 0 x 979 [7,2,5]
+ CRUSH rule 0 x 980 [6,2,3]
+ CRUSH rule 0 x 981 [7,5,1]
+ CRUSH rule 0 x 982 [4,1,8]
+ CRUSH rule 0 x 983 [3,6,0]
+ CRUSH rule 0 x 984 [0,8,4]
+ CRUSH rule 0 x 985 [2,4,8]
+ CRUSH rule 0 x 986 [8,4,2]
+ CRUSH rule 0 x 987 [0,4,8]
+ CRUSH rule 0 x 988 [1,4,6]
+ CRUSH rule 0 x 989 [0,8,5]
+ CRUSH rule 0 x 990 [1,6,4]
+ CRUSH rule 0 x 991 [0,4,8]
+ CRUSH rule 0 x 992 [7,0,4]
+ CRUSH rule 0 x 993 [0,6,4]
+ CRUSH rule 0 x 994 [3,6,1]
+ CRUSH rule 0 x 995 [7,0,4]
+ CRUSH rule 0 x 996 [6,5,1]
+ CRUSH rule 0 x 997 [6,3,1]
+ CRUSH rule 0 x 998 [8,0,4]
+ CRUSH rule 0 x 999 [0,7,5]
+ CRUSH rule 0 x 1000 [8,4,1]
+ CRUSH rule 0 x 1001 [2,3,6]
+ CRUSH rule 0 x 1002 [1,3,8]
+ CRUSH rule 0 x 1003 [2,7,3]
+ CRUSH rule 0 x 1004 [6,0,3]
+ CRUSH rule 0 x 1005 [6,1,4]
+ CRUSH rule 0 x 1006 [1,8,4]
+ CRUSH rule 0 x 1007 [1,3,8]
+ CRUSH rule 0 x 1008 [1,7,4]
+ CRUSH rule 0 x 1009 [6,5,1]
+ CRUSH rule 0 x 1010 [3,1,6]
+ CRUSH rule 0 x 1011 [3,0,8]
+ CRUSH rule 0 x 1012 [3,1,8]
+ CRUSH rule 0 x 1013 [5,2,8]
+ CRUSH rule 0 x 1014 [2,8,3]
+ CRUSH rule 0 x 1015 [6,3,2]
+ CRUSH rule 0 x 1016 [2,5,6]
+ CRUSH rule 0 x 1017 [6,1,5]
+ CRUSH rule 0 x 1018 [5,1,6]
+ CRUSH rule 0 x 1019 [5,8,1]
+ CRUSH rule 0 x 1020 [5,0,7]
+ CRUSH rule 0 x 1021 [5,2,7]
+ CRUSH rule 0 x 1022 [1,7,4]
+ CRUSH rule 0 x 1023 [3,0,7]
+ rule 0 (choose) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 1 (choose-two), x = 0..1023, numrep = 2..3
+ CRUSH rule 1 x 0 [0,2]
+ CRUSH rule 1 x 1 [0,8]
+ CRUSH rule 1 x 2 [1,3]
+ CRUSH rule 1 x 3 [8,0]
+ CRUSH rule 1 x 4 [5,4]
+ CRUSH rule 1 x 5 [7,8]
+ CRUSH rule 1 x 6 [2,6]
+ CRUSH rule 1 x 7 [5,4]
+ CRUSH rule 1 x 8 [5,3]
+ CRUSH rule 1 x 9 [2,3]
+ CRUSH rule 1 x 10 [0,2]
+ CRUSH rule 1 x 11 [0,7]
+ CRUSH rule 1 x 12 [0,2]
+ CRUSH rule 1 x 13 [3,8]
+ CRUSH rule 1 x 14 [7,6]
+ CRUSH rule 1 x 15 [7,2]
+ CRUSH rule 1 x 16 [3,6]
+ CRUSH rule 1 x 17 [5,4]
+ CRUSH rule 1 x 18 [1,4]
+ CRUSH rule 1 x 19 [7,5]
+ CRUSH rule 1 x 20 [2,4]
+ CRUSH rule 1 x 21 [3,7]
+ CRUSH rule 1 x 22 [8,3]
+ CRUSH rule 1 x 23 [3,6]
+ CRUSH rule 1 x 24 [1,0]
+ CRUSH rule 1 x 25 [3,7]
+ CRUSH rule 1 x 26 [2,8]
+ CRUSH rule 1 x 27 [3,1]
+ CRUSH rule 1 x 28 [6,0]
+ CRUSH rule 1 x 29 [8,5]
+ CRUSH rule 1 x 30 [5,7]
+ CRUSH rule 1 x 31 [8,7]
+ CRUSH rule 1 x 32 [3,6]
+ CRUSH rule 1 x 33 [2,7]
+ CRUSH rule 1 x 34 [2,5]
+ CRUSH rule 1 x 35 [0,8]
+ CRUSH rule 1 x 36 [3,8]
+ CRUSH rule 1 x 37 [0,4]
+ CRUSH rule 1 x 38 [4,8]
+ CRUSH rule 1 x 39 [3,7]
+ CRUSH rule 1 x 40 [7,8]
+ CRUSH rule 1 x 41 [0,2]
+ CRUSH rule 1 x 42 [4,3]
+ CRUSH rule 1 x 43 [0,3]
+ CRUSH rule 1 x 44 [1,6]
+ CRUSH rule 1 x 45 [8,0]
+ CRUSH rule 1 x 46 [2,4]
+ CRUSH rule 1 x 47 [4,5]
+ CRUSH rule 1 x 48 [4,6]
+ CRUSH rule 1 x 49 [5,4]
+ CRUSH rule 1 x 50 [3,4]
+ CRUSH rule 1 x 51 [3,6]
+ CRUSH rule 1 x 52 [8,6]
+ CRUSH rule 1 x 53 [3,5]
+ CRUSH rule 1 x 54 [7,6]
+ CRUSH rule 1 x 55 [8,7]
+ CRUSH rule 1 x 56 [6,4]
+ CRUSH rule 1 x 57 [5,3]
+ CRUSH rule 1 x 58 [1,0]
+ CRUSH rule 1 x 59 [4,2]
+ CRUSH rule 1 x 60 [3,5]
+ CRUSH rule 1 x 61 [4,6]
+ CRUSH rule 1 x 62 [7,0]
+ CRUSH rule 1 x 63 [5,6]
+ CRUSH rule 1 x 64 [4,5]
+ CRUSH rule 1 x 65 [7,3]
+ CRUSH rule 1 x 66 [5,4]
+ CRUSH rule 1 x 67 [5,0]
+ CRUSH rule 1 x 68 [0,5]
+ CRUSH rule 1 x 69 [5,1]
+ CRUSH rule 1 x 70 [7,0]
+ CRUSH rule 1 x 71 [2,8]
+ CRUSH rule 1 x 72 [6,1]
+ CRUSH rule 1 x 73 [2,7]
+ CRUSH rule 1 x 74 [0,7]
+ CRUSH rule 1 x 75 [3,2]
+ CRUSH rule 1 x 76 [5,1]
+ CRUSH rule 1 x 77 [7,2]
+ CRUSH rule 1 x 78 [1,4]
+ CRUSH rule 1 x 79 [5,4]
+ CRUSH rule 1 x 80 [0,3]
+ CRUSH rule 1 x 81 [0,2]
+ CRUSH rule 1 x 82 [7,1]
+ CRUSH rule 1 x 83 [2,6]
+ CRUSH rule 1 x 84 [7,2]
+ CRUSH rule 1 x 85 [3,4]
+ CRUSH rule 1 x 86 [0,2]
+ CRUSH rule 1 x 87 [0,7]
+ CRUSH rule 1 x 88 [1,6]
+ CRUSH rule 1 x 89 [3,0]
+ CRUSH rule 1 x 90 [6,7]
+ CRUSH rule 1 x 91 [3,8]
+ CRUSH rule 1 x 92 [1,8]
+ CRUSH rule 1 x 93 [7,4]
+ CRUSH rule 1 x 94 [0,4]
+ CRUSH rule 1 x 95 [7,5]
+ CRUSH rule 1 x 96 [3,6]
+ CRUSH rule 1 x 97 [8,7]
+ CRUSH rule 1 x 98 [2,0]
+ CRUSH rule 1 x 99 [0,7]
+ CRUSH rule 1 x 100 [1,7]
+ CRUSH rule 1 x 101 [3,7]
+ CRUSH rule 1 x 102 [4,2]
+ CRUSH rule 1 x 103 [4,7]
+ CRUSH rule 1 x 104 [7,4]
+ CRUSH rule 1 x 105 [2,4]
+ CRUSH rule 1 x 106 [1,6]
+ CRUSH rule 1 x 107 [3,2]
+ CRUSH rule 1 x 108 [7,2]
+ CRUSH rule 1 x 109 [1,2]
+ CRUSH rule 1 x 110 [3,2]
+ CRUSH rule 1 x 111 [2,1]
+ CRUSH rule 1 x 112 [2,0]
+ CRUSH rule 1 x 113 [6,2]
+ CRUSH rule 1 x 114 [7,6]
+ CRUSH rule 1 x 115 [8,2]
+ CRUSH rule 1 x 116 [1,2]
+ CRUSH rule 1 x 117 [7,3]
+ CRUSH rule 1 x 118 [0,3]
+ CRUSH rule 1 x 119 [5,6]
+ CRUSH rule 1 x 120 [0,2]
+ CRUSH rule 1 x 121 [2,0]
+ CRUSH rule 1 x 122 [8,5]
+ CRUSH rule 1 x 123 [2,5]
+ CRUSH rule 1 x 124 [3,5]
+ CRUSH rule 1 x 125 [0,7]
+ CRUSH rule 1 x 126 [4,2]
+ CRUSH rule 1 x 127 [3,6]
+ CRUSH rule 1 x 128 [3,5]
+ CRUSH rule 1 x 129 [0,2]
+ CRUSH rule 1 x 130 [3,8]
+ CRUSH rule 1 x 131 [1,2]
+ CRUSH rule 1 x 132 [1,2]
+ CRUSH rule 1 x 133 [3,6]
+ CRUSH rule 1 x 134 [1,8]
+ CRUSH rule 1 x 135 [5,6]
+ CRUSH rule 1 x 136 [2,1]
+ CRUSH rule 1 x 137 [7,3]
+ CRUSH rule 1 x 138 [8,7]
+ CRUSH rule 1 x 139 [3,0]
+ CRUSH rule 1 x 140 [1,6]
+ CRUSH rule 1 x 141 [6,8]
+ CRUSH rule 1 x 142 [3,5]
+ CRUSH rule 1 x 143 [5,8]
+ CRUSH rule 1 x 144 [8,1]
+ CRUSH rule 1 x 145 [8,5]
+ CRUSH rule 1 x 146 [2,6]
+ CRUSH rule 1 x 147 [2,8]
+ CRUSH rule 1 x 148 [3,4]
+ CRUSH rule 1 x 149 [4,8]
+ CRUSH rule 1 x 150 [1,6]
+ CRUSH rule 1 x 151 [3,4]
+ CRUSH rule 1 x 152 [8,4]
+ CRUSH rule 1 x 153 [8,6]
+ CRUSH rule 1 x 154 [3,2]
+ CRUSH rule 1 x 155 [3,5]
+ CRUSH rule 1 x 156 [4,5]
+ CRUSH rule 1 x 157 [4,1]
+ CRUSH rule 1 x 158 [2,8]
+ CRUSH rule 1 x 159 [7,0]
+ CRUSH rule 1 x 160 [2,8]
+ CRUSH rule 1 x 161 [1,5]
+ CRUSH rule 1 x 162 [0,6]
+ CRUSH rule 1 x 163 [5,6]
+ CRUSH rule 1 x 164 [7,8]
+ CRUSH rule 1 x 165 [7,0]
+ CRUSH rule 1 x 166 [2,4]
+ CRUSH rule 1 x 167 [0,1]
+ CRUSH rule 1 x 168 [4,2]
+ CRUSH rule 1 x 169 [2,6]
+ CRUSH rule 1 x 170 [1,0]
+ CRUSH rule 1 x 171 [7,5]
+ CRUSH rule 1 x 172 [0,7]
+ CRUSH rule 1 x 173 [8,5]
+ CRUSH rule 1 x 174 [1,4]
+ CRUSH rule 1 x 175 [6,0]
+ CRUSH rule 1 x 176 [4,3]
+ CRUSH rule 1 x 177 [5,3]
+ CRUSH rule 1 x 178 [3,0]
+ CRUSH rule 1 x 179 [4,1]
+ CRUSH rule 1 x 180 [3,8]
+ CRUSH rule 1 x 181 [6,2]
+ CRUSH rule 1 x 182 [8,5]
+ CRUSH rule 1 x 183 [7,8]
+ CRUSH rule 1 x 184 [5,7]
+ CRUSH rule 1 x 185 [6,8]
+ CRUSH rule 1 x 186 [2,1]
+ CRUSH rule 1 x 187 [1,6]
+ CRUSH rule 1 x 188 [1,8]
+ CRUSH rule 1 x 189 [0,7]
+ CRUSH rule 1 x 190 [4,0]
+ CRUSH rule 1 x 191 [7,6]
+ CRUSH rule 1 x 192 [5,0]
+ CRUSH rule 1 x 193 [4,2]
+ CRUSH rule 1 x 194 [1,3]
+ CRUSH rule 1 x 195 [6,4]
+ CRUSH rule 1 x 196 [6,7]
+ CRUSH rule 1 x 197 [6,5]
+ CRUSH rule 1 x 198 [2,5]
+ CRUSH rule 1 x 199 [0,5]
+ CRUSH rule 1 x 200 [0,5]
+ CRUSH rule 1 x 201 [7,1]
+ CRUSH rule 1 x 202 [6,3]
+ CRUSH rule 1 x 203 [4,8]
+ CRUSH rule 1 x 204 [2,1]
+ CRUSH rule 1 x 205 [0,7]
+ CRUSH rule 1 x 206 [0,1]
+ CRUSH rule 1 x 207 [3,0]
+ CRUSH rule 1 x 208 [7,1]
+ CRUSH rule 1 x 209 [1,2]
+ CRUSH rule 1 x 210 [1,2]
+ CRUSH rule 1 x 211 [5,4]
+ CRUSH rule 1 x 212 [7,5]
+ CRUSH rule 1 x 213 [8,4]
+ CRUSH rule 1 x 214 [4,5]
+ CRUSH rule 1 x 215 [8,1]
+ CRUSH rule 1 x 216 [5,0]
+ CRUSH rule 1 x 217 [0,1]
+ CRUSH rule 1 x 218 [0,7]
+ CRUSH rule 1 x 219 [4,8]
+ CRUSH rule 1 x 220 [5,7]
+ CRUSH rule 1 x 221 [3,6]
+ CRUSH rule 1 x 222 [6,8]
+ CRUSH rule 1 x 223 [1,3]
+ CRUSH rule 1 x 224 [1,5]
+ CRUSH rule 1 x 225 [8,6]
+ CRUSH rule 1 x 226 [7,2]
+ CRUSH rule 1 x 227 [3,4]
+ CRUSH rule 1 x 228 [5,4]
+ CRUSH rule 1 x 229 [3,4]
+ CRUSH rule 1 x 230 [4,7]
+ CRUSH rule 1 x 231 [4,3]
+ CRUSH rule 1 x 232 [2,7]
+ CRUSH rule 1 x 233 [3,4]
+ CRUSH rule 1 x 234 [0,1]
+ CRUSH rule 1 x 235 [3,8]
+ CRUSH rule 1 x 236 [5,2]
+ CRUSH rule 1 x 237 [4,7]
+ CRUSH rule 1 x 238 [4,3]
+ CRUSH rule 1 x 239 [8,7]
+ CRUSH rule 1 x 240 [5,7]
+ CRUSH rule 1 x 241 [3,1]
+ CRUSH rule 1 x 242 [3,5]
+ CRUSH rule 1 x 243 [4,7]
+ CRUSH rule 1 x 244 [4,6]
+ CRUSH rule 1 x 245 [7,6]
+ CRUSH rule 1 x 246 [1,5]
+ CRUSH rule 1 x 247 [6,0]
+ CRUSH rule 1 x 248 [8,0]
+ CRUSH rule 1 x 249 [2,0]
+ CRUSH rule 1 x 250 [2,1]
+ CRUSH rule 1 x 251 [2,3]
+ CRUSH rule 1 x 252 [3,7]
+ CRUSH rule 1 x 253 [3,2]
+ CRUSH rule 1 x 254 [3,5]
+ CRUSH rule 1 x 255 [1,7]
+ CRUSH rule 1 x 256 [5,7]
+ CRUSH rule 1 x 257 [2,8]
+ CRUSH rule 1 x 258 [5,3]
+ CRUSH rule 1 x 259 [4,3]
+ CRUSH rule 1 x 260 [3,6]
+ CRUSH rule 1 x 261 [8,7]
+ CRUSH rule 1 x 262 [5,4]
+ CRUSH rule 1 x 263 [6,8]
+ CRUSH rule 1 x 264 [3,6]
+ CRUSH rule 1 x 265 [8,6]
+ CRUSH rule 1 x 266 [8,2]
+ CRUSH rule 1 x 267 [2,3]
+ CRUSH rule 1 x 268 [0,7]
+ CRUSH rule 1 x 269 [0,8]
+ CRUSH rule 1 x 270 [5,0]
+ CRUSH rule 1 x 271 [7,5]
+ CRUSH rule 1 x 272 [2,8]
+ CRUSH rule 1 x 273 [3,5]
+ CRUSH rule 1 x 274 [6,8]
+ CRUSH rule 1 x 275 [4,3]
+ CRUSH rule 1 x 276 [7,1]
+ CRUSH rule 1 x 277 [6,4]
+ CRUSH rule 1 x 278 [6,8]
+ CRUSH rule 1 x 279 [8,3]
+ CRUSH rule 1 x 280 [0,6]
+ CRUSH rule 1 x 281 [8,0]
+ CRUSH rule 1 x 282 [3,1]
+ CRUSH rule 1 x 283 [8,2]
+ CRUSH rule 1 x 284 [6,3]
+ CRUSH rule 1 x 285 [5,3]
+ CRUSH rule 1 x 286 [2,1]
+ CRUSH rule 1 x 287 [0,4]
+ CRUSH rule 1 x 288 [8,0]
+ CRUSH rule 1 x 289 [4,6]
+ CRUSH rule 1 x 290 [1,3]
+ CRUSH rule 1 x 291 [0,1]
+ CRUSH rule 1 x 292 [8,0]
+ CRUSH rule 1 x 293 [6,0]
+ CRUSH rule 1 x 294 [7,4]
+ CRUSH rule 1 x 295 [4,8]
+ CRUSH rule 1 x 296 [3,1]
+ CRUSH rule 1 x 297 [6,2]
+ CRUSH rule 1 x 298 [1,2]
+ CRUSH rule 1 x 299 [2,0]
+ CRUSH rule 1 x 300 [8,7]
+ CRUSH rule 1 x 301 [0,8]
+ CRUSH rule 1 x 302 [3,0]
+ CRUSH rule 1 x 303 [7,5]
+ CRUSH rule 1 x 304 [2,7]
+ CRUSH rule 1 x 305 [5,8]
+ CRUSH rule 1 x 306 [0,7]
+ CRUSH rule 1 x 307 [0,2]
+ CRUSH rule 1 x 308 [0,8]
+ CRUSH rule 1 x 309 [7,4]
+ CRUSH rule 1 x 310 [4,3]
+ CRUSH rule 1 x 311 [3,4]
+ CRUSH rule 1 x 312 [2,1]
+ CRUSH rule 1 x 313 [5,3]
+ CRUSH rule 1 x 314 [4,5]
+ CRUSH rule 1 x 315 [2,0]
+ CRUSH rule 1 x 316 [6,3]
+ CRUSH rule 1 x 317 [2,6]
+ CRUSH rule 1 x 318 [8,6]
+ CRUSH rule 1 x 319 [5,0]
+ CRUSH rule 1 x 320 [3,7]
+ CRUSH rule 1 x 321 [1,3]
+ CRUSH rule 1 x 322 [2,7]
+ CRUSH rule 1 x 323 [4,7]
+ CRUSH rule 1 x 324 [7,0]
+ CRUSH rule 1 x 325 [4,6]
+ CRUSH rule 1 x 326 [3,4]
+ CRUSH rule 1 x 327 [0,6]
+ CRUSH rule 1 x 328 [7,4]
+ CRUSH rule 1 x 329 [5,6]
+ CRUSH rule 1 x 330 [3,7]
+ CRUSH rule 1 x 331 [2,6]
+ CRUSH rule 1 x 332 [2,0]
+ CRUSH rule 1 x 333 [6,8]
+ CRUSH rule 1 x 334 [8,3]
+ CRUSH rule 1 x 335 [7,1]
+ CRUSH rule 1 x 336 [4,6]
+ CRUSH rule 1 x 337 [7,2]
+ CRUSH rule 1 x 338 [5,6]
+ CRUSH rule 1 x 339 [7,5]
+ CRUSH rule 1 x 340 [2,0]
+ CRUSH rule 1 x 341 [5,1]
+ CRUSH rule 1 x 342 [0,7]
+ CRUSH rule 1 x 343 [6,7]
+ CRUSH rule 1 x 344 [6,0]
+ CRUSH rule 1 x 345 [4,3]
+ CRUSH rule 1 x 346 [8,0]
+ CRUSH rule 1 x 347 [3,1]
+ CRUSH rule 1 x 348 [8,0]
+ CRUSH rule 1 x 349 [1,6]
+ CRUSH rule 1 x 350 [8,5]
+ CRUSH rule 1 x 351 [3,6]
+ CRUSH rule 1 x 352 [1,0]
+ CRUSH rule 1 x 353 [6,4]
+ CRUSH rule 1 x 354 [0,3]
+ CRUSH rule 1 x 355 [3,4]
+ CRUSH rule 1 x 356 [3,5]
+ CRUSH rule 1 x 357 [6,1]
+ CRUSH rule 1 x 358 [2,1]
+ CRUSH rule 1 x 359 [6,7]
+ CRUSH rule 1 x 360 [5,3]
+ CRUSH rule 1 x 361 [8,4]
+ CRUSH rule 1 x 362 [4,5]
+ CRUSH rule 1 x 363 [4,0]
+ CRUSH rule 1 x 364 [2,5]
+ CRUSH rule 1 x 365 [6,7]
+ CRUSH rule 1 x 366 [7,2]
+ CRUSH rule 1 x 367 [4,5]
+ CRUSH rule 1 x 368 [7,4]
+ CRUSH rule 1 x 369 [3,7]
+ CRUSH rule 1 x 370 [8,7]
+ CRUSH rule 1 x 371 [1,3]
+ CRUSH rule 1 x 372 [3,1]
+ CRUSH rule 1 x 373 [0,6]
+ CRUSH rule 1 x 374 [3,8]
+ CRUSH rule 1 x 375 [6,4]
+ CRUSH rule 1 x 376 [7,1]
+ CRUSH rule 1 x 377 [1,2]
+ CRUSH rule 1 x 378 [0,1]
+ CRUSH rule 1 x 379 [8,5]
+ CRUSH rule 1 x 380 [2,5]
+ CRUSH rule 1 x 381 [0,4]
+ CRUSH rule 1 x 382 [1,5]
+ CRUSH rule 1 x 383 [4,3]
+ CRUSH rule 1 x 384 [7,0]
+ CRUSH rule 1 x 385 [7,4]
+ CRUSH rule 1 x 386 [0,3]
+ CRUSH rule 1 x 387 [1,3]
+ CRUSH rule 1 x 388 [5,0]
+ CRUSH rule 1 x 389 [1,5]
+ CRUSH rule 1 x 390 [5,6]
+ CRUSH rule 1 x 391 [5,6]
+ CRUSH rule 1 x 392 [1,8]
+ CRUSH rule 1 x 393 [4,2]
+ CRUSH rule 1 x 394 [4,7]
+ CRUSH rule 1 x 395 [4,0]
+ CRUSH rule 1 x 396 [4,2]
+ CRUSH rule 1 x 397 [2,1]
+ CRUSH rule 1 x 398 [2,4]
+ CRUSH rule 1 x 399 [8,7]
+ CRUSH rule 1 x 400 [8,1]
+ CRUSH rule 1 x 401 [0,1]
+ CRUSH rule 1 x 402 [7,8]
+ CRUSH rule 1 x 403 [0,1]
+ CRUSH rule 1 x 404 [4,3]
+ CRUSH rule 1 x 405 [6,5]
+ CRUSH rule 1 x 406 [2,0]
+ CRUSH rule 1 x 407 [2,8]
+ CRUSH rule 1 x 408 [4,1]
+ CRUSH rule 1 x 409 [7,3]
+ CRUSH rule 1 x 410 [8,6]
+ CRUSH rule 1 x 411 [2,0]
+ CRUSH rule 1 x 412 [0,5]
+ CRUSH rule 1 x 413 [5,0]
+ CRUSH rule 1 x 414 [4,1]
+ CRUSH rule 1 x 415 [0,6]
+ CRUSH rule 1 x 416 [2,1]
+ CRUSH rule 1 x 417 [8,7]
+ CRUSH rule 1 x 418 [7,6]
+ CRUSH rule 1 x 419 [8,3]
+ CRUSH rule 1 x 420 [1,4]
+ CRUSH rule 1 x 421 [8,6]
+ CRUSH rule 1 x 422 [6,7]
+ CRUSH rule 1 x 423 [0,5]
+ CRUSH rule 1 x 424 [8,4]
+ CRUSH rule 1 x 425 [1,3]
+ CRUSH rule 1 x 426 [6,7]
+ CRUSH rule 1 x 427 [0,7]
+ CRUSH rule 1 x 428 [5,4]
+ CRUSH rule 1 x 429 [4,6]
+ CRUSH rule 1 x 430 [3,6]
+ CRUSH rule 1 x 431 [5,3]
+ CRUSH rule 1 x 432 [7,1]
+ CRUSH rule 1 x 433 [6,5]
+ CRUSH rule 1 x 434 [5,2]
+ CRUSH rule 1 x 435 [0,5]
+ CRUSH rule 1 x 436 [4,0]
+ CRUSH rule 1 x 437 [7,5]
+ CRUSH rule 1 x 438 [0,3]
+ CRUSH rule 1 x 439 [1,3]
+ CRUSH rule 1 x 440 [2,7]
+ CRUSH rule 1 x 441 [5,7]
+ CRUSH rule 1 x 442 [2,4]
+ CRUSH rule 1 x 443 [6,8]
+ CRUSH rule 1 x 444 [7,0]
+ CRUSH rule 1 x 445 [6,3]
+ CRUSH rule 1 x 446 [4,3]
+ CRUSH rule 1 x 447 [2,1]
+ CRUSH rule 1 x 448 [7,2]
+ CRUSH rule 1 x 449 [7,8]
+ CRUSH rule 1 x 450 [4,5]
+ CRUSH rule 1 x 451 [6,8]
+ CRUSH rule 1 x 452 [8,3]
+ CRUSH rule 1 x 453 [6,8]
+ CRUSH rule 1 x 454 [6,7]
+ CRUSH rule 1 x 455 [2,7]
+ CRUSH rule 1 x 456 [6,8]
+ CRUSH rule 1 x 457 [7,2]
+ CRUSH rule 1 x 458 [2,8]
+ CRUSH rule 1 x 459 [2,0]
+ CRUSH rule 1 x 460 [6,5]
+ CRUSH rule 1 x 461 [6,5]
+ CRUSH rule 1 x 462 [8,1]
+ CRUSH rule 1 x 463 [6,7]
+ CRUSH rule 1 x 464 [7,4]
+ CRUSH rule 1 x 465 [7,6]
+ CRUSH rule 1 x 466 [5,8]
+ CRUSH rule 1 x 467 [6,4]
+ CRUSH rule 1 x 468 [7,8]
+ CRUSH rule 1 x 469 [7,0]
+ CRUSH rule 1 x 470 [3,0]
+ CRUSH rule 1 x 471 [0,1]
+ CRUSH rule 1 x 472 [5,4]
+ CRUSH rule 1 x 473 [1,0]
+ CRUSH rule 1 x 474 [6,0]
+ CRUSH rule 1 x 475 [6,7]
+ CRUSH rule 1 x 476 [4,3]
+ CRUSH rule 1 x 477 [5,8]
+ CRUSH rule 1 x 478 [6,7]
+ CRUSH rule 1 x 479 [0,5]
+ CRUSH rule 1 x 480 [1,8]
+ CRUSH rule 1 x 481 [2,4]
+ CRUSH rule 1 x 482 [4,3]
+ CRUSH rule 1 x 483 [0,2]
+ CRUSH rule 1 x 484 [1,2]
+ CRUSH rule 1 x 485 [4,7]
+ CRUSH rule 1 x 486 [4,1]
+ CRUSH rule 1 x 487 [5,0]
+ CRUSH rule 1 x 488 [5,7]
+ CRUSH rule 1 x 489 [2,8]
+ CRUSH rule 1 x 490 [6,4]
+ CRUSH rule 1 x 491 [1,0]
+ CRUSH rule 1 x 492 [6,5]
+ CRUSH rule 1 x 493 [0,2]
+ CRUSH rule 1 x 494 [1,0]
+ CRUSH rule 1 x 495 [3,4]
+ CRUSH rule 1 x 496 [7,5]
+ CRUSH rule 1 x 497 [5,7]
+ CRUSH rule 1 x 498 [0,5]
+ CRUSH rule 1 x 499 [8,4]
+ CRUSH rule 1 x 500 [3,6]
+ CRUSH rule 1 x 501 [0,7]
+ CRUSH rule 1 x 502 [7,1]
+ CRUSH rule 1 x 503 [2,3]
+ CRUSH rule 1 x 504 [5,6]
+ CRUSH rule 1 x 505 [0,7]
+ CRUSH rule 1 x 506 [5,3]
+ CRUSH rule 1 x 507 [6,0]
+ CRUSH rule 1 x 508 [0,1]
+ CRUSH rule 1 x 509 [7,5]
+ CRUSH rule 1 x 510 [6,0]
+ CRUSH rule 1 x 511 [5,8]
+ CRUSH rule 1 x 512 [7,6]
+ CRUSH rule 1 x 513 [7,2]
+ CRUSH rule 1 x 514 [4,3]
+ CRUSH rule 1 x 515 [8,5]
+ CRUSH rule 1 x 516 [4,0]
+ CRUSH rule 1 x 517 [7,8]
+ CRUSH rule 1 x 518 [4,6]
+ CRUSH rule 1 x 519 [7,3]
+ CRUSH rule 1 x 520 [2,6]
+ CRUSH rule 1 x 521 [8,7]
+ CRUSH rule 1 x 522 [6,8]
+ CRUSH rule 1 x 523 [4,2]
+ CRUSH rule 1 x 524 [0,4]
+ CRUSH rule 1 x 525 [0,4]
+ CRUSH rule 1 x 526 [1,5]
+ CRUSH rule 1 x 527 [0,2]
+ CRUSH rule 1 x 528 [5,3]
+ CRUSH rule 1 x 529 [5,7]
+ CRUSH rule 1 x 530 [6,7]
+ CRUSH rule 1 x 531 [6,1]
+ CRUSH rule 1 x 532 [6,3]
+ CRUSH rule 1 x 533 [5,6]
+ CRUSH rule 1 x 534 [7,3]
+ CRUSH rule 1 x 535 [8,6]
+ CRUSH rule 1 x 536 [6,7]
+ CRUSH rule 1 x 537 [3,7]
+ CRUSH rule 1 x 538 [6,8]
+ CRUSH rule 1 x 539 [8,3]
+ CRUSH rule 1 x 540 [0,6]
+ CRUSH rule 1 x 541 [2,3]
+ CRUSH rule 1 x 542 [3,5]
+ CRUSH rule 1 x 543 [6,0]
+ CRUSH rule 1 x 544 [3,7]
+ CRUSH rule 1 x 545 [5,7]
+ CRUSH rule 1 x 546 [6,1]
+ CRUSH rule 1 x 547 [8,2]
+ CRUSH rule 1 x 548 [5,2]
+ CRUSH rule 1 x 549 [5,8]
+ CRUSH rule 1 x 550 [0,5]
+ CRUSH rule 1 x 551 [7,5]
+ CRUSH rule 1 x 552 [5,4]
+ CRUSH rule 1 x 553 [4,2]
+ CRUSH rule 1 x 554 [0,8]
+ CRUSH rule 1 x 555 [5,0]
+ CRUSH rule 1 x 556 [3,4]
+ CRUSH rule 1 x 557 [7,4]
+ CRUSH rule 1 x 558 [3,1]
+ CRUSH rule 1 x 559 [4,2]
+ CRUSH rule 1 x 560 [8,3]
+ CRUSH rule 1 x 561 [6,3]
+ CRUSH rule 1 x 562 [3,4]
+ CRUSH rule 1 x 563 [2,6]
+ CRUSH rule 1 x 564 [5,1]
+ CRUSH rule 1 x 565 [3,6]
+ CRUSH rule 1 x 566 [4,7]
+ CRUSH rule 1 x 567 [3,6]
+ CRUSH rule 1 x 568 [7,4]
+ CRUSH rule 1 x 569 [3,4]
+ CRUSH rule 1 x 570 [1,5]
+ CRUSH rule 1 x 571 [3,7]
+ CRUSH rule 1 x 572 [3,4]
+ CRUSH rule 1 x 573 [3,0]
+ CRUSH rule 1 x 574 [2,0]
+ CRUSH rule 1 x 575 [8,6]
+ CRUSH rule 1 x 576 [4,6]
+ CRUSH rule 1 x 577 [8,2]
+ CRUSH rule 1 x 578 [6,8]
+ CRUSH rule 1 x 579 [3,1]
+ CRUSH rule 1 x 580 [3,0]
+ CRUSH rule 1 x 581 [7,2]
+ CRUSH rule 1 x 582 [2,8]
+ CRUSH rule 1 x 583 [6,0]
+ CRUSH rule 1 x 584 [8,1]
+ CRUSH rule 1 x 585 [7,0]
+ CRUSH rule 1 x 586 [0,1]
+ CRUSH rule 1 x 587 [2,5]
+ CRUSH rule 1 x 588 [3,4]
+ CRUSH rule 1 x 589 [7,1]
+ CRUSH rule 1 x 590 [6,2]
+ CRUSH rule 1 x 591 [5,2]
+ CRUSH rule 1 x 592 [2,0]
+ CRUSH rule 1 x 593 [0,8]
+ CRUSH rule 1 x 594 [0,7]
+ CRUSH rule 1 x 595 [7,1]
+ CRUSH rule 1 x 596 [4,3]
+ CRUSH rule 1 x 597 [3,1]
+ CRUSH rule 1 x 598 [3,2]
+ CRUSH rule 1 x 599 [5,2]
+ CRUSH rule 1 x 600 [7,0]
+ CRUSH rule 1 x 601 [0,7]
+ CRUSH rule 1 x 602 [3,7]
+ CRUSH rule 1 x 603 [5,1]
+ CRUSH rule 1 x 604 [7,5]
+ CRUSH rule 1 x 605 [3,0]
+ CRUSH rule 1 x 606 [2,0]
+ CRUSH rule 1 x 607 [0,4]
+ CRUSH rule 1 x 608 [5,3]
+ CRUSH rule 1 x 609 [5,2]
+ CRUSH rule 1 x 610 [3,7]
+ CRUSH rule 1 x 611 [1,0]
+ CRUSH rule 1 x 612 [2,0]
+ CRUSH rule 1 x 613 [7,2]
+ CRUSH rule 1 x 614 [7,8]
+ CRUSH rule 1 x 615 [6,8]
+ CRUSH rule 1 x 616 [0,8]
+ CRUSH rule 1 x 617 [6,1]
+ CRUSH rule 1 x 618 [7,6]
+ CRUSH rule 1 x 619 [5,1]
+ CRUSH rule 1 x 620 [4,1]
+ CRUSH rule 1 x 621 [5,8]
+ CRUSH rule 1 x 622 [0,4]
+ CRUSH rule 1 x 623 [0,6]
+ CRUSH rule 1 x 624 [3,5]
+ CRUSH rule 1 x 625 [2,3]
+ CRUSH rule 1 x 626 [7,8]
+ CRUSH rule 1 x 627 [2,7]
+ CRUSH rule 1 x 628 [8,0]
+ CRUSH rule 1 x 629 [2,6]
+ CRUSH rule 1 x 630 [2,7]
+ CRUSH rule 1 x 631 [0,7]
+ CRUSH rule 1 x 632 [7,0]
+ CRUSH rule 1 x 633 [8,6]
+ CRUSH rule 1 x 634 [0,4]
+ CRUSH rule 1 x 635 [5,6]
+ CRUSH rule 1 x 636 [1,4]
+ CRUSH rule 1 x 637 [4,1]
+ CRUSH rule 1 x 638 [6,8]
+ CRUSH rule 1 x 639 [4,2]
+ CRUSH rule 1 x 640 [3,1]
+ CRUSH rule 1 x 641 [7,2]
+ CRUSH rule 1 x 642 [2,0]
+ CRUSH rule 1 x 643 [3,5]
+ CRUSH rule 1 x 644 [8,1]
+ CRUSH rule 1 x 645 [5,4]
+ CRUSH rule 1 x 646 [8,0]
+ CRUSH rule 1 x 647 [7,1]
+ CRUSH rule 1 x 648 [0,6]
+ CRUSH rule 1 x 649 [4,7]
+ CRUSH rule 1 x 650 [7,8]
+ CRUSH rule 1 x 651 [3,7]
+ CRUSH rule 1 x 652 [3,4]
+ CRUSH rule 1 x 653 [8,5]
+ CRUSH rule 1 x 654 [7,5]
+ CRUSH rule 1 x 655 [0,3]
+ CRUSH rule 1 x 656 [4,3]
+ CRUSH rule 1 x 657 [6,1]
+ CRUSH rule 1 x 658 [5,4]
+ CRUSH rule 1 x 659 [4,6]
+ CRUSH rule 1 x 660 [7,8]
+ CRUSH rule 1 x 661 [1,8]
+ CRUSH rule 1 x 662 [4,5]
+ CRUSH rule 1 x 663 [1,3]
+ CRUSH rule 1 x 664 [1,4]
+ CRUSH rule 1 x 665 [5,7]
+ CRUSH rule 1 x 666 [2,8]
+ CRUSH rule 1 x 667 [1,3]
+ CRUSH rule 1 x 668 [3,7]
+ CRUSH rule 1 x 669 [6,4]
+ CRUSH rule 1 x 670 [4,0]
+ CRUSH rule 1 x 671 [0,2]
+ CRUSH rule 1 x 672 [4,3]
+ CRUSH rule 1 x 673 [5,3]
+ CRUSH rule 1 x 674 [3,1]
+ CRUSH rule 1 x 675 [0,8]
+ CRUSH rule 1 x 676 [0,2]
+ CRUSH rule 1 x 677 [4,1]
+ CRUSH rule 1 x 678 [2,3]
+ CRUSH rule 1 x 679 [6,0]
+ CRUSH rule 1 x 680 [0,4]
+ CRUSH rule 1 x 681 [4,7]
+ CRUSH rule 1 x 682 [0,5]
+ CRUSH rule 1 x 683 [0,1]
+ CRUSH rule 1 x 684 [7,1]
+ CRUSH rule 1 x 685 [7,1]
+ CRUSH rule 1 x 686 [1,4]
+ CRUSH rule 1 x 687 [3,5]
+ CRUSH rule 1 x 688 [5,7]
+ CRUSH rule 1 x 689 [6,5]
+ CRUSH rule 1 x 690 [8,1]
+ CRUSH rule 1 x 691 [3,0]
+ CRUSH rule 1 x 692 [7,2]
+ CRUSH rule 1 x 693 [6,7]
+ CRUSH rule 1 x 694 [6,5]
+ CRUSH rule 1 x 695 [0,8]
+ CRUSH rule 1 x 696 [1,4]
+ CRUSH rule 1 x 697 [6,1]
+ CRUSH rule 1 x 698 [6,2]
+ CRUSH rule 1 x 699 [1,6]
+ CRUSH rule 1 x 700 [0,3]
+ CRUSH rule 1 x 701 [4,3]
+ CRUSH rule 1 x 702 [3,5]
+ CRUSH rule 1 x 703 [8,3]
+ CRUSH rule 1 x 704 [0,3]
+ CRUSH rule 1 x 705 [8,6]
+ CRUSH rule 1 x 706 [1,2]
+ CRUSH rule 1 x 707 [7,8]
+ CRUSH rule 1 x 708 [3,5]
+ CRUSH rule 1 x 709 [6,3]
+ CRUSH rule 1 x 710 [8,4]
+ CRUSH rule 1 x 711 [2,3]
+ CRUSH rule 1 x 712 [2,3]
+ CRUSH rule 1 x 713 [6,7]
+ CRUSH rule 1 x 714 [3,2]
+ CRUSH rule 1 x 715 [1,2]
+ CRUSH rule 1 x 716 [3,6]
+ CRUSH rule 1 x 717 [8,7]
+ CRUSH rule 1 x 718 [3,7]
+ CRUSH rule 1 x 719 [2,6]
+ CRUSH rule 1 x 720 [6,8]
+ CRUSH rule 1 x 721 [5,4]
+ CRUSH rule 1 x 722 [5,4]
+ CRUSH rule 1 x 723 [5,1]
+ CRUSH rule 1 x 724 [0,6]
+ CRUSH rule 1 x 725 [0,1]
+ CRUSH rule 1 x 726 [3,8]
+ CRUSH rule 1 x 727 [4,6]
+ CRUSH rule 1 x 728 [2,1]
+ CRUSH rule 1 x 729 [5,3]
+ CRUSH rule 1 x 730 [3,7]
+ CRUSH rule 1 x 731 [4,1]
+ CRUSH rule 1 x 732 [1,5]
+ CRUSH rule 1 x 733 [5,4]
+ CRUSH rule 1 x 734 [6,4]
+ CRUSH rule 1 x 735 [4,8]
+ CRUSH rule 1 x 736 [3,5]
+ CRUSH rule 1 x 737 [1,0]
+ CRUSH rule 1 x 738 [5,2]
+ CRUSH rule 1 x 739 [0,1]
+ CRUSH rule 1 x 740 [0,1]
+ CRUSH rule 1 x 741 [7,8]
+ CRUSH rule 1 x 742 [8,2]
+ CRUSH rule 1 x 743 [7,0]
+ CRUSH rule 1 x 744 [4,7]
+ CRUSH rule 1 x 745 [3,4]
+ CRUSH rule 1 x 746 [4,1]
+ CRUSH rule 1 x 747 [6,0]
+ CRUSH rule 1 x 748 [2,7]
+ CRUSH rule 1 x 749 [4,5]
+ CRUSH rule 1 x 750 [1,6]
+ CRUSH rule 1 x 751 [2,1]
+ CRUSH rule 1 x 752 [8,1]
+ CRUSH rule 1 x 753 [7,8]
+ CRUSH rule 1 x 754 [8,6]
+ CRUSH rule 1 x 755 [1,2]
+ CRUSH rule 1 x 756 [5,6]
+ CRUSH rule 1 x 757 [8,6]
+ CRUSH rule 1 x 758 [6,0]
+ CRUSH rule 1 x 759 [8,5]
+ CRUSH rule 1 x 760 [1,5]
+ CRUSH rule 1 x 761 [4,1]
+ CRUSH rule 1 x 762 [2,7]
+ CRUSH rule 1 x 763 [8,6]
+ CRUSH rule 1 x 764 [1,7]
+ CRUSH rule 1 x 765 [6,5]
+ CRUSH rule 1 x 766 [8,5]
+ CRUSH rule 1 x 767 [1,2]
+ CRUSH rule 1 x 768 [8,3]
+ CRUSH rule 1 x 769 [6,2]
+ CRUSH rule 1 x 770 [6,0]
+ CRUSH rule 1 x 771 [7,0]
+ CRUSH rule 1 x 772 [8,3]
+ CRUSH rule 1 x 773 [3,1]
+ CRUSH rule 1 x 774 [4,6]
+ CRUSH rule 1 x 775 [6,8]
+ CRUSH rule 1 x 776 [7,2]
+ CRUSH rule 1 x 777 [3,1]
+ CRUSH rule 1 x 778 [1,8]
+ CRUSH rule 1 x 779 [2,7]
+ CRUSH rule 1 x 780 [0,2]
+ CRUSH rule 1 x 781 [6,3]
+ CRUSH rule 1 x 782 [5,4]
+ CRUSH rule 1 x 783 [7,1]
+ CRUSH rule 1 x 784 [0,1]
+ CRUSH rule 1 x 785 [6,1]
+ CRUSH rule 1 x 786 [7,6]
+ CRUSH rule 1 x 787 [1,0]
+ CRUSH rule 1 x 788 [6,0]
+ CRUSH rule 1 x 789 [0,4]
+ CRUSH rule 1 x 790 [8,4]
+ CRUSH rule 1 x 791 [3,8]
+ CRUSH rule 1 x 792 [5,8]
+ CRUSH rule 1 x 793 [6,1]
+ CRUSH rule 1 x 794 [2,6]
+ CRUSH rule 1 x 795 [0,3]
+ CRUSH rule 1 x 796 [3,4]
+ CRUSH rule 1 x 797 [2,3]
+ CRUSH rule 1 x 798 [6,8]
+ CRUSH rule 1 x 799 [5,1]
+ CRUSH rule 1 x 800 [5,0]
+ CRUSH rule 1 x 801 [3,6]
+ CRUSH rule 1 x 802 [1,8]
+ CRUSH rule 1 x 803 [0,5]
+ CRUSH rule 1 x 804 [6,2]
+ CRUSH rule 1 x 805 [3,6]
+ CRUSH rule 1 x 806 [1,3]
+ CRUSH rule 1 x 807 [5,4]
+ CRUSH rule 1 x 808 [4,6]
+ CRUSH rule 1 x 809 [1,4]
+ CRUSH rule 1 x 810 [5,7]
+ CRUSH rule 1 x 811 [8,4]
+ CRUSH rule 1 x 812 [8,5]
+ CRUSH rule 1 x 813 [6,4]
+ CRUSH rule 1 x 814 [3,6]
+ CRUSH rule 1 x 815 [3,1]
+ CRUSH rule 1 x 816 [2,1]
+ CRUSH rule 1 x 817 [4,3]
+ CRUSH rule 1 x 818 [3,5]
+ CRUSH rule 1 x 819 [5,1]
+ CRUSH rule 1 x 820 [3,5]
+ CRUSH rule 1 x 821 [4,5]
+ CRUSH rule 1 x 822 [2,0]
+ CRUSH rule 1 x 823 [4,8]
+ CRUSH rule 1 x 824 [3,7]
+ CRUSH rule 1 x 825 [2,8]
+ CRUSH rule 1 x 826 [7,0]
+ CRUSH rule 1 x 827 [0,8]
+ CRUSH rule 1 x 828 [2,3]
+ CRUSH rule 1 x 829 [5,6]
+ CRUSH rule 1 x 830 [2,3]
+ CRUSH rule 1 x 831 [1,6]
+ CRUSH rule 1 x 832 [4,5]
+ CRUSH rule 1 x 833 [2,1]
+ CRUSH rule 1 x 834 [3,4]
+ CRUSH rule 1 x 835 [8,4]
+ CRUSH rule 1 x 836 [3,4]
+ CRUSH rule 1 x 837 [6,3]
+ CRUSH rule 1 x 838 [6,7]
+ CRUSH rule 1 x 839 [5,0]
+ CRUSH rule 1 x 840 [7,8]
+ CRUSH rule 1 x 841 [4,8]
+ CRUSH rule 1 x 842 [2,4]
+ CRUSH rule 1 x 843 [6,4]
+ CRUSH rule 1 x 844 [4,8]
+ CRUSH rule 1 x 845 [3,8]
+ CRUSH rule 1 x 846 [3,2]
+ CRUSH rule 1 x 847 [0,2]
+ CRUSH rule 1 x 848 [2,6]
+ CRUSH rule 1 x 849 [4,5]
+ CRUSH rule 1 x 850 [1,0]
+ CRUSH rule 1 x 851 [6,8]
+ CRUSH rule 1 x 852 [7,3]
+ CRUSH rule 1 x 853 [6,8]
+ CRUSH rule 1 x 854 [7,6]
+ CRUSH rule 1 x 855 [5,7]
+ CRUSH rule 1 x 856 [6,7]
+ CRUSH rule 1 x 857 [8,5]
+ CRUSH rule 1 x 858 [6,4]
+ CRUSH rule 1 x 859 [6,0]
+ CRUSH rule 1 x 860 [4,1]
+ CRUSH rule 1 x 861 [8,7]
+ CRUSH rule 1 x 862 [6,1]
+ CRUSH rule 1 x 863 [8,7]
+ CRUSH rule 1 x 864 [5,6]
+ CRUSH rule 1 x 865 [8,1]
+ CRUSH rule 1 x 866 [3,4]
+ CRUSH rule 1 x 867 [6,5]
+ CRUSH rule 1 x 868 [6,3]
+ CRUSH rule 1 x 869 [8,7]
+ CRUSH rule 1 x 870 [0,4]
+ CRUSH rule 1 x 871 [3,4]
+ CRUSH rule 1 x 872 [5,1]
+ CRUSH rule 1 x 873 [4,6]
+ CRUSH rule 1 x 874 [2,6]
+ CRUSH rule 1 x 875 [2,6]
+ CRUSH rule 1 x 876 [5,8]
+ CRUSH rule 1 x 877 [6,4]
+ CRUSH rule 1 x 878 [5,4]
+ CRUSH rule 1 x 879 [7,4]
+ CRUSH rule 1 x 880 [3,5]
+ CRUSH rule 1 x 881 [5,6]
+ CRUSH rule 1 x 882 [4,0]
+ CRUSH rule 1 x 883 [2,1]
+ CRUSH rule 1 x 884 [6,0]
+ CRUSH rule 1 x 885 [5,1]
+ CRUSH rule 1 x 886 [3,6]
+ CRUSH rule 1 x 887 [7,4]
+ CRUSH rule 1 x 888 [6,8]
+ CRUSH rule 1 x 889 [2,1]
+ CRUSH rule 1 x 890 [7,2]
+ CRUSH rule 1 x 891 [1,8]
+ CRUSH rule 1 x 892 [6,2]
+ CRUSH rule 1 x 893 [2,3]
+ CRUSH rule 1 x 894 [7,5]
+ CRUSH rule 1 x 895 [5,3]
+ CRUSH rule 1 x 896 [1,8]
+ CRUSH rule 1 x 897 [4,2]
+ CRUSH rule 1 x 898 [0,5]
+ CRUSH rule 1 x 899 [1,7]
+ CRUSH rule 1 x 900 [4,1]
+ CRUSH rule 1 x 901 [5,0]
+ CRUSH rule 1 x 902 [8,5]
+ CRUSH rule 1 x 903 [5,7]
+ CRUSH rule 1 x 904 [5,6]
+ CRUSH rule 1 x 905 [6,2]
+ CRUSH rule 1 x 906 [1,2]
+ CRUSH rule 1 x 907 [7,1]
+ CRUSH rule 1 x 908 [5,8]
+ CRUSH rule 1 x 909 [2,3]
+ CRUSH rule 1 x 910 [6,4]
+ CRUSH rule 1 x 911 [5,8]
+ CRUSH rule 1 x 912 [0,1]
+ CRUSH rule 1 x 913 [7,6]
+ CRUSH rule 1 x 914 [6,4]
+ CRUSH rule 1 x 915 [8,2]
+ CRUSH rule 1 x 916 [3,1]
+ CRUSH rule 1 x 917 [1,5]
+ CRUSH rule 1 x 918 [8,2]
+ CRUSH rule 1 x 919 [6,2]
+ CRUSH rule 1 x 920 [7,6]
+ CRUSH rule 1 x 921 [1,4]
+ CRUSH rule 1 x 922 [6,7]
+ CRUSH rule 1 x 923 [5,3]
+ CRUSH rule 1 x 924 [3,5]
+ CRUSH rule 1 x 925 [5,7]
+ CRUSH rule 1 x 926 [3,4]
+ CRUSH rule 1 x 927 [1,6]
+ CRUSH rule 1 x 928 [8,1]
+ CRUSH rule 1 x 929 [4,5]
+ CRUSH rule 1 x 930 [2,4]
+ CRUSH rule 1 x 931 [5,0]
+ CRUSH rule 1 x 932 [4,3]
+ CRUSH rule 1 x 933 [8,5]
+ CRUSH rule 1 x 934 [5,3]
+ CRUSH rule 1 x 935 [6,3]
+ CRUSH rule 1 x 936 [0,6]
+ CRUSH rule 1 x 937 [5,4]
+ CRUSH rule 1 x 938 [6,5]
+ CRUSH rule 1 x 939 [2,7]
+ CRUSH rule 1 x 940 [8,7]
+ CRUSH rule 1 x 941 [5,2]
+ CRUSH rule 1 x 942 [1,0]
+ CRUSH rule 1 x 943 [8,2]
+ CRUSH rule 1 x 944 [4,3]
+ CRUSH rule 1 x 945 [7,2]
+ CRUSH rule 1 x 946 [2,0]
+ CRUSH rule 1 x 947 [4,5]
+ CRUSH rule 1 x 948 [7,8]
+ CRUSH rule 1 x 949 [6,1]
+ CRUSH rule 1 x 950 [3,5]
+ CRUSH rule 1 x 951 [4,5]
+ CRUSH rule 1 x 952 [2,0]
+ CRUSH rule 1 x 953 [1,3]
+ CRUSH rule 1 x 954 [4,2]
+ CRUSH rule 1 x 955 [8,6]
+ CRUSH rule 1 x 956 [1,0]
+ CRUSH rule 1 x 957 [7,6]
+ CRUSH rule 1 x 958 [8,7]
+ CRUSH rule 1 x 959 [5,2]
+ CRUSH rule 1 x 960 [3,6]
+ CRUSH rule 1 x 961 [4,0]
+ CRUSH rule 1 x 962 [7,4]
+ CRUSH rule 1 x 963 [0,5]
+ CRUSH rule 1 x 964 [3,1]
+ CRUSH rule 1 x 965 [7,6]
+ CRUSH rule 1 x 966 [3,8]
+ CRUSH rule 1 x 967 [8,6]
+ CRUSH rule 1 x 968 [7,2]
+ CRUSH rule 1 x 969 [8,0]
+ CRUSH rule 1 x 970 [0,6]
+ CRUSH rule 1 x 971 [1,7]
+ CRUSH rule 1 x 972 [1,8]
+ CRUSH rule 1 x 973 [1,2]
+ CRUSH rule 1 x 974 [5,3]
+ CRUSH rule 1 x 975 [3,7]
+ CRUSH rule 1 x 976 [4,3]
+ CRUSH rule 1 x 977 [8,3]
+ CRUSH rule 1 x 978 [7,2]
+ CRUSH rule 1 x 979 [7,6]
+ CRUSH rule 1 x 980 [6,0]
+ CRUSH rule 1 x 981 [7,3]
+ CRUSH rule 1 x 982 [4,2]
+ CRUSH rule 1 x 983 [3,5]
+ CRUSH rule 1 x 984 [0,2]
+ CRUSH rule 1 x 985 [2,5]
+ CRUSH rule 1 x 986 [8,7]
+ CRUSH rule 1 x 987 [0,5]
+ CRUSH rule 1 x 988 [1,3]
+ CRUSH rule 1 x 989 [0,6]
+ CRUSH rule 1 x 990 [1,0]
+ CRUSH rule 1 x 991 [0,4]
+ CRUSH rule 1 x 992 [7,1]
+ CRUSH rule 1 x 993 [0,6]
+ CRUSH rule 1 x 994 [3,4]
+ CRUSH rule 1 x 995 [7,6]
+ CRUSH rule 1 x 996 [6,7]
+ CRUSH rule 1 x 997 [6,4]
+ CRUSH rule 1 x 998 [8,1]
+ CRUSH rule 1 x 999 [0,7]
+ CRUSH rule 1 x 1000 [8,5]
+ CRUSH rule 1 x 1001 [2,0]
+ CRUSH rule 1 x 1002 [1,3]
+ CRUSH rule 1 x 1003 [2,8]
+ CRUSH rule 1 x 1004 [6,1]
+ CRUSH rule 1 x 1005 [6,1]
+ CRUSH rule 1 x 1006 [1,0]
+ CRUSH rule 1 x 1007 [1,2]
+ CRUSH rule 1 x 1008 [1,7]
+ CRUSH rule 1 x 1009 [6,8]
+ CRUSH rule 1 x 1010 [3,4]
+ CRUSH rule 1 x 1011 [3,0]
+ CRUSH rule 1 x 1012 [3,0]
+ CRUSH rule 1 x 1013 [5,1]
+ CRUSH rule 1 x 1014 [2,8]
+ CRUSH rule 1 x 1015 [6,8]
+ CRUSH rule 1 x 1016 [2,0]
+ CRUSH rule 1 x 1017 [6,0]
+ CRUSH rule 1 x 1018 [5,4]
+ CRUSH rule 1 x 1019 [5,3]
+ CRUSH rule 1 x 1020 [5,1]
+ CRUSH rule 1 x 1021 [5,2]
+ CRUSH rule 1 x 1022 [1,6]
+ CRUSH rule 1 x 1023 [3,2]
+ rule 1 (choose-two) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [0,2,3]
+ CRUSH rule 1 x 1 [0,8,2]
+ CRUSH rule 1 x 2 [1,3,5]
+ CRUSH rule 1 x 3 [8,0,6]
+ CRUSH rule 1 x 4 [5,4,0]
+ CRUSH rule 1 x 5 [7,8,0]
+ CRUSH rule 1 x 6 [2,6,7]
+ CRUSH rule 1 x 7 [5,4,3]
+ CRUSH rule 1 x 8 [5,3,8]
+ CRUSH rule 1 x 9 [2,3,1]
+ CRUSH rule 1 x 10 [0,2,8]
+ CRUSH rule 1 x 11 [0,7,2]
+ CRUSH rule 1 x 12 [0,2,4]
+ CRUSH rule 1 x 13 [3,8,5]
+ CRUSH rule 1 x 14 [7,6,8]
+ CRUSH rule 1 x 15 [7,2,4]
+ CRUSH rule 1 x 16 [3,6,0]
+ CRUSH rule 1 x 17 [5,4,0]
+ CRUSH rule 1 x 18 [1,4,5]
+ CRUSH rule 1 x 19 [7,5,4]
+ CRUSH rule 1 x 20 [2,4,7]
+ CRUSH rule 1 x 21 [3,7,2]
+ CRUSH rule 1 x 22 [8,3,7]
+ CRUSH rule 1 x 23 [3,6,8]
+ CRUSH rule 1 x 24 [1,0,6]
+ CRUSH rule 1 x 25 [3,7,0]
+ CRUSH rule 1 x 26 [2,8,7]
+ CRUSH rule 1 x 27 [3,1,5]
+ CRUSH rule 1 x 28 [6,0,3]
+ CRUSH rule 1 x 29 [8,5,7]
+ CRUSH rule 1 x 30 [5,7,3]
+ CRUSH rule 1 x 31 [8,7,6]
+ CRUSH rule 1 x 32 [3,6,8]
+ CRUSH rule 1 x 33 [2,7,8]
+ CRUSH rule 1 x 34 [2,5,4]
+ CRUSH rule 1 x 35 [0,8,1]
+ CRUSH rule 1 x 36 [3,8,2]
+ CRUSH rule 1 x 37 [0,4,5]
+ CRUSH rule 1 x 38 [4,8,7]
+ CRUSH rule 1 x 39 [3,7,0]
+ CRUSH rule 1 x 40 [7,8,1]
+ CRUSH rule 1 x 41 [0,2,6]
+ CRUSH rule 1 x 42 [4,3,6]
+ CRUSH rule 1 x 43 [0,3,7]
+ CRUSH rule 1 x 44 [1,6,7]
+ CRUSH rule 1 x 45 [8,0,4]
+ CRUSH rule 1 x 46 [2,4,3]
+ CRUSH rule 1 x 47 [4,5,3]
+ CRUSH rule 1 x 48 [4,6,5]
+ CRUSH rule 1 x 49 [5,4,8]
+ CRUSH rule 1 x 50 [3,4,1]
+ CRUSH rule 1 x 51 [3,6,0]
+ CRUSH rule 1 x 52 [8,6,1]
+ CRUSH rule 1 x 53 [3,5,4]
+ CRUSH rule 1 x 54 [7,6,8]
+ CRUSH rule 1 x 55 [8,7,0]
+ CRUSH rule 1 x 56 [6,4,8]
+ CRUSH rule 1 x 57 [5,3,6]
+ CRUSH rule 1 x 58 [1,0,2]
+ CRUSH rule 1 x 59 [4,2,1]
+ CRUSH rule 1 x 60 [3,5,0]
+ CRUSH rule 1 x 61 [4,6,8]
+ CRUSH rule 1 x 62 [7,0,1]
+ CRUSH rule 1 x 63 [5,6,8]
+ CRUSH rule 1 x 64 [4,5,1]
+ CRUSH rule 1 x 65 [7,3,6]
+ CRUSH rule 1 x 66 [5,4,3]
+ CRUSH rule 1 x 67 [5,0,8]
+ CRUSH rule 1 x 68 [0,5,8]
+ CRUSH rule 1 x 69 [5,1,6]
+ CRUSH rule 1 x 70 [7,0,2]
+ CRUSH rule 1 x 71 [2,8,6]
+ CRUSH rule 1 x 72 [6,1,5]
+ CRUSH rule 1 x 73 [2,7,3]
+ CRUSH rule 1 x 74 [0,7,1]
+ CRUSH rule 1 x 75 [3,2,0]
+ CRUSH rule 1 x 76 [5,1,7]
+ CRUSH rule 1 x 77 [7,2,6]
+ CRUSH rule 1 x 78 [1,4,2]
+ CRUSH rule 1 x 79 [5,4,0]
+ CRUSH rule 1 x 80 [0,3,5]
+ CRUSH rule 1 x 81 [0,2,5]
+ CRUSH rule 1 x 82 [7,1,6]
+ CRUSH rule 1 x 83 [2,6,8]
+ CRUSH rule 1 x 84 [7,2,1]
+ CRUSH rule 1 x 85 [3,4,8]
+ CRUSH rule 1 x 86 [0,2,1]
+ CRUSH rule 1 x 87 [0,7,4]
+ CRUSH rule 1 x 88 [1,6,0]
+ CRUSH rule 1 x 89 [3,0,2]
+ CRUSH rule 1 x 90 [6,7,5]
+ CRUSH rule 1 x 91 [3,8,7]
+ CRUSH rule 1 x 92 [1,8,4]
+ CRUSH rule 1 x 93 [7,4,5]
+ CRUSH rule 1 x 94 [0,4,3]
+ CRUSH rule 1 x 95 [7,5,1]
+ CRUSH rule 1 x 96 [3,6,4]
+ CRUSH rule 1 x 97 [8,7,4]
+ CRUSH rule 1 x 98 [2,0,7]
+ CRUSH rule 1 x 99 [0,7,2]
+ CRUSH rule 1 x 100 [1,7,4]
+ CRUSH rule 1 x 101 [3,7,2]
+ CRUSH rule 1 x 102 [4,2,7]
+ CRUSH rule 1 x 103 [4,7,6]
+ CRUSH rule 1 x 104 [7,4,3]
+ CRUSH rule 1 x 105 [2,4,1]
+ CRUSH rule 1 x 106 [1,6,2]
+ CRUSH rule 1 x 107 [3,2,1]
+ CRUSH rule 1 x 108 [7,2,8]
+ CRUSH rule 1 x 109 [1,2,5]
+ CRUSH rule 1 x 110 [3,2,7]
+ CRUSH rule 1 x 111 [2,1,0]
+ CRUSH rule 1 x 112 [2,0,6]
+ CRUSH rule 1 x 113 [6,2,1]
+ CRUSH rule 1 x 114 [7,6,3]
+ CRUSH rule 1 x 115 [8,2,3]
+ CRUSH rule 1 x 116 [1,2,6]
+ CRUSH rule 1 x 117 [7,3,6]
+ CRUSH rule 1 x 118 [0,3,8]
+ CRUSH rule 1 x 119 [5,6,1]
+ CRUSH rule 1 x 120 [0,2,5]
+ CRUSH rule 1 x 121 [2,0,1]
+ CRUSH rule 1 x 122 [8,5,0]
+ CRUSH rule 1 x 123 [2,5,1]
+ CRUSH rule 1 x 124 [3,5,1]
+ CRUSH rule 1 x 125 [0,7,3]
+ CRUSH rule 1 x 126 [4,2,6]
+ CRUSH rule 1 x 127 [3,6,7]
+ CRUSH rule 1 x 128 [3,5,8]
+ CRUSH rule 1 x 129 [0,2,1]
+ CRUSH rule 1 x 130 [3,8,5]
+ CRUSH rule 1 x 131 [1,2,0]
+ CRUSH rule 1 x 132 [1,2,3]
+ CRUSH rule 1 x 133 [3,6,7]
+ CRUSH rule 1 x 134 [1,8,2]
+ CRUSH rule 1 x 135 [5,6,4]
+ CRUSH rule 1 x 136 [2,1,5]
+ CRUSH rule 1 x 137 [7,3,4]
+ CRUSH rule 1 x 138 [8,7,6]
+ CRUSH rule 1 x 139 [3,0,5]
+ CRUSH rule 1 x 140 [1,6,7]
+ CRUSH rule 1 x 141 [6,8,1]
+ CRUSH rule 1 x 142 [3,5,0]
+ CRUSH rule 1 x 143 [5,8,7]
+ CRUSH rule 1 x 144 [8,1,0]
+ CRUSH rule 1 x 145 [8,5,6]
+ CRUSH rule 1 x 146 [2,6,4]
+ CRUSH rule 1 x 147 [2,8,7]
+ CRUSH rule 1 x 148 [3,4,0]
+ CRUSH rule 1 x 149 [4,8,6]
+ CRUSH rule 1 x 150 [1,6,5]
+ CRUSH rule 1 x 151 [3,4,5]
+ CRUSH rule 1 x 152 [8,4,6]
+ CRUSH rule 1 x 153 [8,6,7]
+ CRUSH rule 1 x 154 [3,2,0]
+ CRUSH rule 1 x 155 [3,5,8]
+ CRUSH rule 1 x 156 [4,5,3]
+ CRUSH rule 1 x 157 [4,1,2]
+ CRUSH rule 1 x 158 [2,8,1]
+ CRUSH rule 1 x 159 [7,0,5]
+ CRUSH rule 1 x 160 [2,8,1]
+ CRUSH rule 1 x 161 [1,5,2]
+ CRUSH rule 1 x 162 [0,6,2]
+ CRUSH rule 1 x 163 [5,6,1]
+ CRUSH rule 1 x 164 [7,8,1]
+ CRUSH rule 1 x 165 [7,0,5]
+ CRUSH rule 1 x 166 [2,4,1]
+ CRUSH rule 1 x 167 [0,1,6]
+ CRUSH rule 1 x 168 [4,2,3]
+ CRUSH rule 1 x 169 [2,6,1]
+ CRUSH rule 1 x 170 [1,0,5]
+ CRUSH rule 1 x 171 [7,5,4]
+ CRUSH rule 1 x 172 [0,7,8]
+ CRUSH rule 1 x 173 [8,5,3]
+ CRUSH rule 1 x 174 [1,4,7]
+ CRUSH rule 1 x 175 [6,0,4]
+ CRUSH rule 1 x 176 [4,3,5]
+ CRUSH rule 1 x 177 [5,3,0]
+ CRUSH rule 1 x 178 [3,0,2]
+ CRUSH rule 1 x 179 [4,1,2]
+ CRUSH rule 1 x 180 [3,8,7]
+ CRUSH rule 1 x 181 [6,2,0]
+ CRUSH rule 1 x 182 [8,5,3]
+ CRUSH rule 1 x 183 [7,8,6]
+ CRUSH rule 1 x 184 [5,7,3]
+ CRUSH rule 1 x 185 [6,8,0]
+ CRUSH rule 1 x 186 [2,1,4]
+ CRUSH rule 1 x 187 [1,6,7]
+ CRUSH rule 1 x 188 [1,8,7]
+ CRUSH rule 1 x 189 [0,7,8]
+ CRUSH rule 1 x 190 [4,0,1]
+ CRUSH rule 1 x 191 [7,6,8]
+ CRUSH rule 1 x 192 [5,0,1]
+ CRUSH rule 1 x 193 [4,2,6]
+ CRUSH rule 1 x 194 [1,3,4]
+ CRUSH rule 1 x 195 [6,4,3]
+ CRUSH rule 1 x 196 [6,7,2]
+ CRUSH rule 1 x 197 [6,5,2]
+ CRUSH rule 1 x 198 [2,5,6]
+ CRUSH rule 1 x 199 [0,5,7]
+ CRUSH rule 1 x 200 [0,5,1]
+ CRUSH rule 1 x 201 [7,1,0]
+ CRUSH rule 1 x 202 [6,3,4]
+ CRUSH rule 1 x 203 [4,8,7]
+ CRUSH rule 1 x 204 [2,1,5]
+ CRUSH rule 1 x 205 [0,7,6]
+ CRUSH rule 1 x 206 [0,1,7]
+ CRUSH rule 1 x 207 [3,0,5]
+ CRUSH rule 1 x 208 [7,1,0]
+ CRUSH rule 1 x 209 [1,2,0]
+ CRUSH rule 1 x 210 [1,2,3]
+ CRUSH rule 1 x 211 [5,4,2]
+ CRUSH rule 1 x 212 [7,5,0]
+ CRUSH rule 1 x 213 [8,4,0]
+ CRUSH rule 1 x 214 [4,5,7]
+ CRUSH rule 1 x 215 [8,1,7]
+ CRUSH rule 1 x 216 [5,0,2]
+ CRUSH rule 1 x 217 [0,1,7]
+ CRUSH rule 1 x 218 [0,7,8]
+ CRUSH rule 1 x 219 [4,8,2]
+ CRUSH rule 1 x 220 [5,7,3]
+ CRUSH rule 1 x 221 [3,6,5]
+ CRUSH rule 1 x 222 [6,8,3]
+ CRUSH rule 1 x 223 [1,3,6]
+ CRUSH rule 1 x 224 [1,5,0]
+ CRUSH rule 1 x 225 [8,6,2]
+ CRUSH rule 1 x 226 [7,2,3]
+ CRUSH rule 1 x 227 [3,4,0]
+ CRUSH rule 1 x 228 [5,4,8]
+ CRUSH rule 1 x 229 [3,4,7]
+ CRUSH rule 1 x 230 [4,7,2]
+ CRUSH rule 1 x 231 [4,3,5]
+ CRUSH rule 1 x 232 [2,7,4]
+ CRUSH rule 1 x 233 [3,4,5]
+ CRUSH rule 1 x 234 [0,1,2]
+ CRUSH rule 1 x 235 [3,8,0]
+ CRUSH rule 1 x 236 [5,2,7]
+ CRUSH rule 1 x 237 [4,7,6]
+ CRUSH rule 1 x 238 [4,3,5]
+ CRUSH rule 1 x 239 [8,7,6]
+ CRUSH rule 1 x 240 [5,7,4]
+ CRUSH rule 1 x 241 [3,1,5]
+ CRUSH rule 1 x 242 [3,5,4]
+ CRUSH rule 1 x 243 [4,7,3]
+ CRUSH rule 1 x 244 [4,6,0]
+ CRUSH rule 1 x 245 [7,6,1]
+ CRUSH rule 1 x 246 [1,5,2]
+ CRUSH rule 1 x 247 [6,0,2]
+ CRUSH rule 1 x 248 [8,0,7]
+ CRUSH rule 1 x 249 [2,0,1]
+ CRUSH rule 1 x 250 [2,1,0]
+ CRUSH rule 1 x 251 [2,3,6]
+ CRUSH rule 1 x 252 [3,7,8]
+ CRUSH rule 1 x 253 [3,2,4]
+ CRUSH rule 1 x 254 [3,5,4]
+ CRUSH rule 1 x 255 [1,7,8]
+ CRUSH rule 1 x 256 [5,7,0]
+ CRUSH rule 1 x 257 [2,8,4]
+ CRUSH rule 1 x 258 [5,3,0]
+ CRUSH rule 1 x 259 [4,3,6]
+ CRUSH rule 1 x 260 [3,6,2]
+ CRUSH rule 1 x 261 [8,7,4]
+ CRUSH rule 1 x 262 [5,4,8]
+ CRUSH rule 1 x 263 [6,8,2]
+ CRUSH rule 1 x 264 [3,6,5]
+ CRUSH rule 1 x 265 [8,6,7]
+ CRUSH rule 1 x 266 [8,2,1]
+ CRUSH rule 1 x 267 [2,3,0]
+ CRUSH rule 1 x 268 [0,7,2]
+ CRUSH rule 1 x 269 [0,8,2]
+ CRUSH rule 1 x 270 [5,0,1]
+ CRUSH rule 1 x 271 [7,5,4]
+ CRUSH rule 1 x 272 [2,8,3]
+ CRUSH rule 1 x 273 [3,5,0]
+ CRUSH rule 1 x 274 [6,8,3]
+ CRUSH rule 1 x 275 [4,3,6]
+ CRUSH rule 1 x 276 [7,1,2]
+ CRUSH rule 1 x 277 [6,4,0]
+ CRUSH rule 1 x 278 [6,8,0]
+ CRUSH rule 1 x 279 [8,3,6]
+ CRUSH rule 1 x 280 [0,6,2]
+ CRUSH rule 1 x 281 [8,0,2]
+ CRUSH rule 1 x 282 [3,1,6]
+ CRUSH rule 1 x 283 [8,2,0]
+ CRUSH rule 1 x 284 [6,3,0]
+ CRUSH rule 1 x 285 [5,3,7]
+ CRUSH rule 1 x 286 [2,1,6]
+ CRUSH rule 1 x 287 [0,4,1]
+ CRUSH rule 1 x 288 [8,0,7]
+ CRUSH rule 1 x 289 [4,6,2]
+ CRUSH rule 1 x 290 [1,3,7]
+ CRUSH rule 1 x 291 [0,1,4]
+ CRUSH rule 1 x 292 [8,0,2]
+ CRUSH rule 1 x 293 [6,0,8]
+ CRUSH rule 1 x 294 [7,4,5]
+ CRUSH rule 1 x 295 [4,8,7]
+ CRUSH rule 1 x 296 [3,1,5]
+ CRUSH rule 1 x 297 [6,2,8]
+ CRUSH rule 1 x 298 [1,2,3]
+ CRUSH rule 1 x 299 [2,0,7]
+ CRUSH rule 1 x 300 [8,7,3]
+ CRUSH rule 1 x 301 [0,8,1]
+ CRUSH rule 1 x 302 [3,0,6]
+ CRUSH rule 1 x 303 [7,5,8]
+ CRUSH rule 1 x 304 [2,7,5]
+ CRUSH rule 1 x 305 [5,8,2]
+ CRUSH rule 1 x 306 [0,7,1]
+ CRUSH rule 1 x 307 [0,2,8]
+ CRUSH rule 1 x 308 [0,8,4]
+ CRUSH rule 1 x 309 [7,4,5]
+ CRUSH rule 1 x 310 [4,3,1]
+ CRUSH rule 1 x 311 [3,4,5]
+ CRUSH rule 1 x 312 [2,1,0]
+ CRUSH rule 1 x 313 [5,3,4]
+ CRUSH rule 1 x 314 [4,5,0]
+ CRUSH rule 1 x 315 [2,0,1]
+ CRUSH rule 1 x 316 [6,3,8]
+ CRUSH rule 1 x 317 [2,6,0]
+ CRUSH rule 1 x 318 [8,6,7]
+ CRUSH rule 1 x 319 [5,0,8]
+ CRUSH rule 1 x 320 [3,7,1]
+ CRUSH rule 1 x 321 [1,3,0]
+ CRUSH rule 1 x 322 [2,7,3]
+ CRUSH rule 1 x 323 [4,7,0]
+ CRUSH rule 1 x 324 [7,0,8]
+ CRUSH rule 1 x 325 [4,6,0]
+ CRUSH rule 1 x 326 [3,4,1]
+ CRUSH rule 1 x 327 [0,6,7]
+ CRUSH rule 1 x 328 [7,4,8]
+ CRUSH rule 1 x 329 [5,6,3]
+ CRUSH rule 1 x 330 [3,7,4]
+ CRUSH rule 1 x 331 [2,6,3]
+ CRUSH rule 1 x 332 [2,0,3]
+ CRUSH rule 1 x 333 [6,8,5]
+ CRUSH rule 1 x 334 [8,3,5]
+ CRUSH rule 1 x 335 [7,1,2]
+ CRUSH rule 1 x 336 [4,6,0]
+ CRUSH rule 1 x 337 [7,2,6]
+ CRUSH rule 1 x 338 [5,6,4]
+ CRUSH rule 1 x 339 [7,5,2]
+ CRUSH rule 1 x 340 [2,0,1]
+ CRUSH rule 1 x 341 [5,1,7]
+ CRUSH rule 1 x 342 [0,7,4]
+ CRUSH rule 1 x 343 [6,7,3]
+ CRUSH rule 1 x 344 [6,0,5]
+ CRUSH rule 1 x 345 [4,3,5]
+ CRUSH rule 1 x 346 [8,0,5]
+ CRUSH rule 1 x 347 [3,1,5]
+ CRUSH rule 1 x 348 [8,0,2]
+ CRUSH rule 1 x 349 [1,6,7]
+ CRUSH rule 1 x 350 [8,5,7]
+ CRUSH rule 1 x 351 [3,6,4]
+ CRUSH rule 1 x 352 [1,0,2]
+ CRUSH rule 1 x 353 [6,4,5]
+ CRUSH rule 1 x 354 [0,3,1]
+ CRUSH rule 1 x 355 [3,4,6]
+ CRUSH rule 1 x 356 [3,5,4]
+ CRUSH rule 1 x 357 [6,1,2]
+ CRUSH rule 1 x 358 [2,1,8]
+ CRUSH rule 1 x 359 [6,7,8]
+ CRUSH rule 1 x 360 [5,3,1]
+ CRUSH rule 1 x 361 [8,4,5]
+ CRUSH rule 1 x 362 [4,5,3]
+ CRUSH rule 1 x 363 [4,0,3]
+ CRUSH rule 1 x 364 [2,5,0]
+ CRUSH rule 1 x 365 [6,7,8]
+ CRUSH rule 1 x 366 [7,2,8]
+ CRUSH rule 1 x 367 [4,5,0]
+ CRUSH rule 1 x 368 [7,4,6]
+ CRUSH rule 1 x 369 [3,7,1]
+ CRUSH rule 1 x 370 [8,7,6]
+ CRUSH rule 1 x 371 [1,3,5]
+ CRUSH rule 1 x 372 [3,1,8]
+ CRUSH rule 1 x 373 [0,6,8]
+ CRUSH rule 1 x 374 [3,8,4]
+ CRUSH rule 1 x 375 [6,4,5]
+ CRUSH rule 1 x 376 [7,1,2]
+ CRUSH rule 1 x 377 [1,2,4]
+ CRUSH rule 1 x 378 [0,1,2]
+ CRUSH rule 1 x 379 [8,5,4]
+ CRUSH rule 1 x 380 [2,5,0]
+ CRUSH rule 1 x 381 [0,4,7]
+ CRUSH rule 1 x 382 [1,5,2]
+ CRUSH rule 1 x 383 [4,3,5]
+ CRUSH rule 1 x 384 [7,0,6]
+ CRUSH rule 1 x 385 [7,4,6]
+ CRUSH rule 1 x 386 [0,3,5]
+ CRUSH rule 1 x 387 [1,3,6]
+ CRUSH rule 1 x 388 [5,0,3]
+ CRUSH rule 1 x 389 [1,5,4]
+ CRUSH rule 1 x 390 [5,6,0]
+ CRUSH rule 1 x 391 [5,6,2]
+ CRUSH rule 1 x 392 [1,8,6]
+ CRUSH rule 1 x 393 [4,2,6]
+ CRUSH rule 1 x 394 [4,7,6]
+ CRUSH rule 1 x 395 [4,0,1]
+ CRUSH rule 1 x 396 [4,2,5]
+ CRUSH rule 1 x 397 [2,1,5]
+ CRUSH rule 1 x 398 [2,4,5]
+ CRUSH rule 1 x 399 [8,7,3]
+ CRUSH rule 1 x 400 [8,1,4]
+ CRUSH rule 1 x 401 [0,1,2]
+ CRUSH rule 1 x 402 [7,8,6]
+ CRUSH rule 1 x 403 [0,1,4]
+ CRUSH rule 1 x 404 [4,3,2]
+ CRUSH rule 1 x 405 [6,5,2]
+ CRUSH rule 1 x 406 [2,0,1]
+ CRUSH rule 1 x 407 [2,8,7]
+ CRUSH rule 1 x 408 [4,1,6]
+ CRUSH rule 1 x 409 [7,3,6]
+ CRUSH rule 1 x 410 [8,6,3]
+ CRUSH rule 1 x 411 [2,0,6]
+ CRUSH rule 1 x 412 [0,5,8]
+ CRUSH rule 1 x 413 [5,0,8]
+ CRUSH rule 1 x 414 [4,1,3]
+ CRUSH rule 1 x 415 [0,6,1]
+ CRUSH rule 1 x 416 [2,1,7]
+ CRUSH rule 1 x 417 [8,7,2]
+ CRUSH rule 1 x 418 [7,6,8]
+ CRUSH rule 1 x 419 [8,3,0]
+ CRUSH rule 1 x 420 [1,4,2]
+ CRUSH rule 1 x 421 [8,6,7]
+ CRUSH rule 1 x 422 [6,7,8]
+ CRUSH rule 1 x 423 [0,5,6]
+ CRUSH rule 1 x 424 [8,4,7]
+ CRUSH rule 1 x 425 [1,3,5]
+ CRUSH rule 1 x 426 [6,7,2]
+ CRUSH rule 1 x 427 [0,7,5]
+ CRUSH rule 1 x 428 [5,4,3]
+ CRUSH rule 1 x 429 [4,6,7]
+ CRUSH rule 1 x 430 [3,6,5]
+ CRUSH rule 1 x 431 [5,3,0]
+ CRUSH rule 1 x 432 [7,1,2]
+ CRUSH rule 1 x 433 [6,5,1]
+ CRUSH rule 1 x 434 [5,2,1]
+ CRUSH rule 1 x 435 [0,5,3]
+ CRUSH rule 1 x 436 [4,0,5]
+ CRUSH rule 1 x 437 [7,5,3]
+ CRUSH rule 1 x 438 [0,3,8]
+ CRUSH rule 1 x 439 [1,3,4]
+ CRUSH rule 1 x 440 [2,7,1]
+ CRUSH rule 1 x 441 [5,7,2]
+ CRUSH rule 1 x 442 [2,4,5]
+ CRUSH rule 1 x 443 [6,8,0]
+ CRUSH rule 1 x 444 [7,0,1]
+ CRUSH rule 1 x 445 [6,3,5]
+ CRUSH rule 1 x 446 [4,3,5]
+ CRUSH rule 1 x 447 [2,1,4]
+ CRUSH rule 1 x 448 [7,2,5]
+ CRUSH rule 1 x 449 [7,8,3]
+ CRUSH rule 1 x 450 [4,5,2]
+ CRUSH rule 1 x 451 [6,8,3]
+ CRUSH rule 1 x 452 [8,3,5]
+ CRUSH rule 1 x 453 [6,8,7]
+ CRUSH rule 1 x 454 [6,7,5]
+ CRUSH rule 1 x 455 [2,7,5]
+ CRUSH rule 1 x 456 [6,8,7]
+ CRUSH rule 1 x 457 [7,2,8]
+ CRUSH rule 1 x 458 [2,8,1]
+ CRUSH rule 1 x 459 [2,0,6]
+ CRUSH rule 1 x 460 [6,5,7]
+ CRUSH rule 1 x 461 [6,5,4]
+ CRUSH rule 1 x 462 [8,1,5]
+ CRUSH rule 1 x 463 [6,7,2]
+ CRUSH rule 1 x 464 [7,4,2]
+ CRUSH rule 1 x 465 [7,6,0]
+ CRUSH rule 1 x 466 [5,8,4]
+ CRUSH rule 1 x 467 [6,4,7]
+ CRUSH rule 1 x 468 [7,8,0]
+ CRUSH rule 1 x 469 [7,0,1]
+ CRUSH rule 1 x 470 [3,0,5]
+ CRUSH rule 1 x 471 [0,1,2]
+ CRUSH rule 1 x 472 [5,4,0]
+ CRUSH rule 1 x 473 [1,0,4]
+ CRUSH rule 1 x 474 [6,0,7]
+ CRUSH rule 1 x 475 [6,7,8]
+ CRUSH rule 1 x 476 [4,3,5]
+ CRUSH rule 1 x 477 [5,8,6]
+ CRUSH rule 1 x 478 [6,7,2]
+ CRUSH rule 1 x 479 [0,5,8]
+ CRUSH rule 1 x 480 [1,8,6]
+ CRUSH rule 1 x 481 [2,4,5]
+ CRUSH rule 1 x 482 [4,3,5]
+ CRUSH rule 1 x 483 [0,2,6]
+ CRUSH rule 1 x 484 [1,2,7]
+ CRUSH rule 1 x 485 [4,7,3]
+ CRUSH rule 1 x 486 [4,1,7]
+ CRUSH rule 1 x 487 [5,0,1]
+ CRUSH rule 1 x 488 [5,7,1]
+ CRUSH rule 1 x 489 [2,8,5]
+ CRUSH rule 1 x 490 [6,4,1]
+ CRUSH rule 1 x 491 [1,0,6]
+ CRUSH rule 1 x 492 [6,5,4]
+ CRUSH rule 1 x 493 [0,2,7]
+ CRUSH rule 1 x 494 [1,0,8]
+ CRUSH rule 1 x 495 [3,4,5]
+ CRUSH rule 1 x 496 [7,5,4]
+ CRUSH rule 1 x 497 [5,7,3]
+ CRUSH rule 1 x 498 [0,5,8]
+ CRUSH rule 1 x 499 [8,4,3]
+ CRUSH rule 1 x 500 [3,6,5]
+ CRUSH rule 1 x 501 [0,7,2]
+ CRUSH rule 1 x 502 [7,1,4]
+ CRUSH rule 1 x 503 [2,3,5]
+ CRUSH rule 1 x 504 [5,6,3]
+ CRUSH rule 1 x 505 [0,7,6]
+ CRUSH rule 1 x 506 [5,3,0]
+ CRUSH rule 1 x 507 [6,0,8]
+ CRUSH rule 1 x 508 [0,1,2]
+ CRUSH rule 1 x 509 [7,5,8]
+ CRUSH rule 1 x 510 [6,0,2]
+ CRUSH rule 1 x 511 [5,8,7]
+ CRUSH rule 1 x 512 [7,6,2]
+ CRUSH rule 1 x 513 [7,2,1]
+ CRUSH rule 1 x 514 [4,3,5]
+ CRUSH rule 1 x 515 [8,5,6]
+ CRUSH rule 1 x 516 [4,0,1]
+ CRUSH rule 1 x 517 [7,8,6]
+ CRUSH rule 1 x 518 [4,6,7]
+ CRUSH rule 1 x 519 [7,3,5]
+ CRUSH rule 1 x 520 [2,6,3]
+ CRUSH rule 1 x 521 [8,7,6]
+ CRUSH rule 1 x 522 [6,8,0]
+ CRUSH rule 1 x 523 [4,2,5]
+ CRUSH rule 1 x 524 [0,4,5]
+ CRUSH rule 1 x 525 [0,4,1]
+ CRUSH rule 1 x 526 [1,5,8]
+ CRUSH rule 1 x 527 [0,2,4]
+ CRUSH rule 1 x 528 [5,3,0]
+ CRUSH rule 1 x 529 [5,7,0]
+ CRUSH rule 1 x 530 [6,7,8]
+ CRUSH rule 1 x 531 [6,1,0]
+ CRUSH rule 1 x 532 [6,3,7]
+ CRUSH rule 1 x 533 [5,6,3]
+ CRUSH rule 1 x 534 [7,3,5]
+ CRUSH rule 1 x 535 [8,6,0]
+ CRUSH rule 1 x 536 [6,7,0]
+ CRUSH rule 1 x 537 [3,7,5]
+ CRUSH rule 1 x 538 [6,8,3]
+ CRUSH rule 1 x 539 [8,3,7]
+ CRUSH rule 1 x 540 [0,6,1]
+ CRUSH rule 1 x 541 [2,3,1]
+ CRUSH rule 1 x 542 [3,5,4]
+ CRUSH rule 1 x 543 [6,0,8]
+ CRUSH rule 1 x 544 [3,7,6]
+ CRUSH rule 1 x 545 [5,7,0]
+ CRUSH rule 1 x 546 [6,1,7]
+ CRUSH rule 1 x 547 [8,2,0]
+ CRUSH rule 1 x 548 [5,2,0]
+ CRUSH rule 1 x 549 [5,8,2]
+ CRUSH rule 1 x 550 [0,5,4]
+ CRUSH rule 1 x 551 [7,5,4]
+ CRUSH rule 1 x 552 [5,4,3]
+ CRUSH rule 1 x 553 [4,2,3]
+ CRUSH rule 1 x 554 [0,8,5]
+ CRUSH rule 1 x 555 [5,0,8]
+ CRUSH rule 1 x 556 [3,4,5]
+ CRUSH rule 1 x 557 [7,4,6]
+ CRUSH rule 1 x 558 [3,1,0]
+ CRUSH rule 1 x 559 [4,2,6]
+ CRUSH rule 1 x 560 [8,3,5]
+ CRUSH rule 1 x 561 [6,3,7]
+ CRUSH rule 1 x 562 [3,4,1]
+ CRUSH rule 1 x 563 [2,6,0]
+ CRUSH rule 1 x 564 [5,1,7]
+ CRUSH rule 1 x 565 [3,6,5]
+ CRUSH rule 1 x 566 [4,7,2]
+ CRUSH rule 1 x 567 [3,6,1]
+ CRUSH rule 1 x 568 [7,4,1]
+ CRUSH rule 1 x 569 [3,4,2]
+ CRUSH rule 1 x 570 [1,5,4]
+ CRUSH rule 1 x 571 [3,7,5]
+ CRUSH rule 1 x 572 [3,4,0]
+ CRUSH rule 1 x 573 [3,0,7]
+ CRUSH rule 1 x 574 [2,0,1]
+ CRUSH rule 1 x 575 [8,6,0]
+ CRUSH rule 1 x 576 [4,6,8]
+ CRUSH rule 1 x 577 [8,2,6]
+ CRUSH rule 1 x 578 [6,8,7]
+ CRUSH rule 1 x 579 [3,1,6]
+ CRUSH rule 1 x 580 [3,0,7]
+ CRUSH rule 1 x 581 [7,2,6]
+ CRUSH rule 1 x 582 [2,8,7]
+ CRUSH rule 1 x 583 [6,0,8]
+ CRUSH rule 1 x 584 [8,1,2]
+ CRUSH rule 1 x 585 [7,0,5]
+ CRUSH rule 1 x 586 [0,1,7]
+ CRUSH rule 1 x 587 [2,5,0]
+ CRUSH rule 1 x 588 [3,4,7]
+ CRUSH rule 1 x 589 [7,1,4]
+ CRUSH rule 1 x 590 [6,2,3]
+ CRUSH rule 1 x 591 [5,2,4]
+ CRUSH rule 1 x 592 [2,0,3]
+ CRUSH rule 1 x 593 [0,8,1]
+ CRUSH rule 1 x 594 [0,7,4]
+ CRUSH rule 1 x 595 [7,1,8]
+ CRUSH rule 1 x 596 [4,3,5]
+ CRUSH rule 1 x 597 [3,1,2]
+ CRUSH rule 1 x 598 [3,2,4]
+ CRUSH rule 1 x 599 [5,2,1]
+ CRUSH rule 1 x 600 [7,0,8]
+ CRUSH rule 1 x 601 [0,7,8]
+ CRUSH rule 1 x 602 [3,7,5]
+ CRUSH rule 1 x 603 [5,1,6]
+ CRUSH rule 1 x 604 [7,5,4]
+ CRUSH rule 1 x 605 [3,0,5]
+ CRUSH rule 1 x 606 [2,0,1]
+ CRUSH rule 1 x 607 [0,4,8]
+ CRUSH rule 1 x 608 [5,3,2]
+ CRUSH rule 1 x 609 [5,2,4]
+ CRUSH rule 1 x 610 [3,7,6]
+ CRUSH rule 1 x 611 [1,0,2]
+ CRUSH rule 1 x 612 [2,0,8]
+ CRUSH rule 1 x 613 [7,2,0]
+ CRUSH rule 1 x 614 [7,8,6]
+ CRUSH rule 1 x 615 [6,8,2]
+ CRUSH rule 1 x 616 [0,8,1]
+ CRUSH rule 1 x 617 [6,1,2]
+ CRUSH rule 1 x 618 [7,6,5]
+ CRUSH rule 1 x 619 [5,1,8]
+ CRUSH rule 1 x 620 [4,1,0]
+ CRUSH rule 1 x 621 [5,8,3]
+ CRUSH rule 1 x 622 [0,4,3]
+ CRUSH rule 1 x 623 [0,6,2]
+ CRUSH rule 1 x 624 [3,5,1]
+ CRUSH rule 1 x 625 [2,3,7]
+ CRUSH rule 1 x 626 [7,8,0]
+ CRUSH rule 1 x 627 [2,7,1]
+ CRUSH rule 1 x 628 [8,0,5]
+ CRUSH rule 1 x 629 [2,6,5]
+ CRUSH rule 1 x 630 [2,7,1]
+ CRUSH rule 1 x 631 [0,7,8]
+ CRUSH rule 1 x 632 [7,0,2]
+ CRUSH rule 1 x 633 [8,6,5]
+ CRUSH rule 1 x 634 [0,4,2]
+ CRUSH rule 1 x 635 [5,6,4]
+ CRUSH rule 1 x 636 [1,4,2]
+ CRUSH rule 1 x 637 [4,1,2]
+ CRUSH rule 1 x 638 [6,8,1]
+ CRUSH rule 1 x 639 [4,2,1]
+ CRUSH rule 1 x 640 [3,1,0]
+ CRUSH rule 1 x 641 [7,2,0]
+ CRUSH rule 1 x 642 [2,0,1]
+ CRUSH rule 1 x 643 [3,5,1]
+ CRUSH rule 1 x 644 [8,1,5]
+ CRUSH rule 1 x 645 [5,4,6]
+ CRUSH rule 1 x 646 [8,0,3]
+ CRUSH rule 1 x 647 [7,1,3]
+ CRUSH rule 1 x 648 [0,6,3]
+ CRUSH rule 1 x 649 [4,7,5]
+ CRUSH rule 1 x 650 [7,8,6]
+ CRUSH rule 1 x 651 [3,7,6]
+ CRUSH rule 1 x 652 [3,4,8]
+ CRUSH rule 1 x 653 [8,5,2]
+ CRUSH rule 1 x 654 [7,5,2]
+ CRUSH rule 1 x 655 [0,3,4]
+ CRUSH rule 1 x 656 [4,3,8]
+ CRUSH rule 1 x 657 [6,1,2]
+ CRUSH rule 1 x 658 [5,4,6]
+ CRUSH rule 1 x 659 [4,6,7]
+ CRUSH rule 1 x 660 [7,8,6]
+ CRUSH rule 1 x 661 [1,8,3]
+ CRUSH rule 1 x 662 [4,5,3]
+ CRUSH rule 1 x 663 [1,3,8]
+ CRUSH rule 1 x 664 [1,4,0]
+ CRUSH rule 1 x 665 [5,7,6]
+ CRUSH rule 1 x 666 [2,8,4]
+ CRUSH rule 1 x 667 [1,3,5]
+ CRUSH rule 1 x 668 [3,7,6]
+ CRUSH rule 1 x 669 [6,4,5]
+ CRUSH rule 1 x 670 [4,0,3]
+ CRUSH rule 1 x 671 [0,2,1]
+ CRUSH rule 1 x 672 [4,3,2]
+ CRUSH rule 1 x 673 [5,3,0]
+ CRUSH rule 1 x 674 [3,1,8]
+ CRUSH rule 1 x 675 [0,8,6]
+ CRUSH rule 1 x 676 [0,2,4]
+ CRUSH rule 1 x 677 [4,1,3]
+ CRUSH rule 1 x 678 [2,3,1]
+ CRUSH rule 1 x 679 [6,0,2]
+ CRUSH rule 1 x 680 [0,4,6]
+ CRUSH rule 1 x 681 [4,7,3]
+ CRUSH rule 1 x 682 [0,5,2]
+ CRUSH rule 1 x 683 [0,1,5]
+ CRUSH rule 1 x 684 [7,1,6]
+ CRUSH rule 1 x 685 [7,1,6]
+ CRUSH rule 1 x 686 [1,4,3]
+ CRUSH rule 1 x 687 [3,5,7]
+ CRUSH rule 1 x 688 [5,7,2]
+ CRUSH rule 1 x 689 [6,5,0]
+ CRUSH rule 1 x 690 [8,1,7]
+ CRUSH rule 1 x 691 [3,0,5]
+ CRUSH rule 1 x 692 [7,2,8]
+ CRUSH rule 1 x 693 [6,7,3]
+ CRUSH rule 1 x 694 [6,5,1]
+ CRUSH rule 1 x 695 [0,8,7]
+ CRUSH rule 1 x 696 [1,4,3]
+ CRUSH rule 1 x 697 [6,1,2]
+ CRUSH rule 1 x 698 [6,2,0]
+ CRUSH rule 1 x 699 [1,6,8]
+ CRUSH rule 1 x 700 [0,3,1]
+ CRUSH rule 1 x 701 [4,3,2]
+ CRUSH rule 1 x 702 [3,5,0]
+ CRUSH rule 1 x 703 [8,3,4]
+ CRUSH rule 1 x 704 [0,3,8]
+ CRUSH rule 1 x 705 [8,6,0]
+ CRUSH rule 1 x 706 [1,2,4]
+ CRUSH rule 1 x 707 [7,8,6]
+ CRUSH rule 1 x 708 [3,5,8]
+ CRUSH rule 1 x 709 [6,3,0]
+ CRUSH rule 1 x 710 [8,4,3]
+ CRUSH rule 1 x 711 [2,3,8]
+ CRUSH rule 1 x 712 [2,3,7]
+ CRUSH rule 1 x 713 [6,7,8]
+ CRUSH rule 1 x 714 [3,2,0]
+ CRUSH rule 1 x 715 [1,2,4]
+ CRUSH rule 1 x 716 [3,6,0]
+ CRUSH rule 1 x 717 [8,7,2]
+ CRUSH rule 1 x 718 [3,7,8]
+ CRUSH rule 1 x 719 [2,6,3]
+ CRUSH rule 1 x 720 [6,8,2]
+ CRUSH rule 1 x 721 [5,4,7]
+ CRUSH rule 1 x 722 [5,4,6]
+ CRUSH rule 1 x 723 [5,1,0]
+ CRUSH rule 1 x 724 [0,6,2]
+ CRUSH rule 1 x 725 [0,1,5]
+ CRUSH rule 1 x 726 [3,8,4]
+ CRUSH rule 1 x 727 [4,6,8]
+ CRUSH rule 1 x 728 [2,1,0]
+ CRUSH rule 1 x 729 [5,3,7]
+ CRUSH rule 1 x 730 [3,7,5]
+ CRUSH rule 1 x 731 [4,1,5]
+ CRUSH rule 1 x 732 [1,5,3]
+ CRUSH rule 1 x 733 [5,4,7]
+ CRUSH rule 1 x 734 [6,4,2]
+ CRUSH rule 1 x 735 [4,8,3]
+ CRUSH rule 1 x 736 [3,5,6]
+ CRUSH rule 1 x 737 [1,0,8]
+ CRUSH rule 1 x 738 [5,2,3]
+ CRUSH rule 1 x 739 [0,1,2]
+ CRUSH rule 1 x 740 [0,1,7]
+ CRUSH rule 1 x 741 [7,8,0]
+ CRUSH rule 1 x 742 [8,2,1]
+ CRUSH rule 1 x 743 [7,0,1]
+ CRUSH rule 1 x 744 [4,7,3]
+ CRUSH rule 1 x 745 [3,4,1]
+ CRUSH rule 1 x 746 [4,1,3]
+ CRUSH rule 1 x 747 [6,0,3]
+ CRUSH rule 1 x 748 [2,7,0]
+ CRUSH rule 1 x 749 [4,5,8]
+ CRUSH rule 1 x 750 [1,6,3]
+ CRUSH rule 1 x 751 [2,1,6]
+ CRUSH rule 1 x 752 [8,1,5]
+ CRUSH rule 1 x 753 [7,8,3]
+ CRUSH rule 1 x 754 [8,6,7]
+ CRUSH rule 1 x 755 [1,2,0]
+ CRUSH rule 1 x 756 [5,6,1]
+ CRUSH rule 1 x 757 [8,6,1]
+ CRUSH rule 1 x 758 [6,0,3]
+ CRUSH rule 1 x 759 [8,5,3]
+ CRUSH rule 1 x 760 [1,5,4]
+ CRUSH rule 1 x 761 [4,1,2]
+ CRUSH rule 1 x 762 [2,7,8]
+ CRUSH rule 1 x 763 [8,6,7]
+ CRUSH rule 1 x 764 [1,7,0]
+ CRUSH rule 1 x 765 [6,5,2]
+ CRUSH rule 1 x 766 [8,5,7]
+ CRUSH rule 1 x 767 [1,2,0]
+ CRUSH rule 1 x 768 [8,3,2]
+ CRUSH rule 1 x 769 [6,2,8]
+ CRUSH rule 1 x 770 [6,0,7]
+ CRUSH rule 1 x 771 [7,0,3]
+ CRUSH rule 1 x 772 [8,3,7]
+ CRUSH rule 1 x 773 [3,1,5]
+ CRUSH rule 1 x 774 [4,6,5]
+ CRUSH rule 1 x 775 [6,8,4]
+ CRUSH rule 1 x 776 [7,2,1]
+ CRUSH rule 1 x 777 [3,1,6]
+ CRUSH rule 1 x 778 [1,8,0]
+ CRUSH rule 1 x 779 [2,7,3]
+ CRUSH rule 1 x 780 [0,2,3]
+ CRUSH rule 1 x 781 [6,3,7]
+ CRUSH rule 1 x 782 [5,4,0]
+ CRUSH rule 1 x 783 [7,1,8]
+ CRUSH rule 1 x 784 [0,1,5]
+ CRUSH rule 1 x 785 [6,1,2]
+ CRUSH rule 1 x 786 [7,6,5]
+ CRUSH rule 1 x 787 [1,0,6]
+ CRUSH rule 1 x 788 [6,0,8]
+ CRUSH rule 1 x 789 [0,4,3]
+ CRUSH rule 1 x 790 [8,4,7]
+ CRUSH rule 1 x 791 [3,8,7]
+ CRUSH rule 1 x 792 [5,8,4]
+ CRUSH rule 1 x 793 [6,1,3]
+ CRUSH rule 1 x 794 [2,6,8]
+ CRUSH rule 1 x 795 [0,3,2]
+ CRUSH rule 1 x 796 [3,4,5]
+ CRUSH rule 1 x 797 [2,3,4]
+ CRUSH rule 1 x 798 [6,8,7]
+ CRUSH rule 1 x 799 [5,1,4]
+ CRUSH rule 1 x 800 [5,0,3]
+ CRUSH rule 1 x 801 [3,6,8]
+ CRUSH rule 1 x 802 [1,8,0]
+ CRUSH rule 1 x 803 [0,5,7]
+ CRUSH rule 1 x 804 [6,2,5]
+ CRUSH rule 1 x 805 [3,6,7]
+ CRUSH rule 1 x 806 [1,3,4]
+ CRUSH rule 1 x 807 [5,4,7]
+ CRUSH rule 1 x 808 [4,6,2]
+ CRUSH rule 1 x 809 [1,4,5]
+ CRUSH rule 1 x 810 [5,7,3]
+ CRUSH rule 1 x 811 [8,4,5]
+ CRUSH rule 1 x 812 [8,5,4]
+ CRUSH rule 1 x 813 [6,4,8]
+ CRUSH rule 1 x 814 [3,6,8]
+ CRUSH rule 1 x 815 [3,1,2]
+ CRUSH rule 1 x 816 [2,1,8]
+ CRUSH rule 1 x 817 [4,3,7]
+ CRUSH rule 1 x 818 [3,5,0]
+ CRUSH rule 1 x 819 [5,1,4]
+ CRUSH rule 1 x 820 [3,5,4]
+ CRUSH rule 1 x 821 [4,5,8]
+ CRUSH rule 1 x 822 [2,0,5]
+ CRUSH rule 1 x 823 [4,8,2]
+ CRUSH rule 1 x 824 [3,7,4]
+ CRUSH rule 1 x 825 [2,8,7]
+ CRUSH rule 1 x 826 [7,0,5]
+ CRUSH rule 1 x 827 [0,8,2]
+ CRUSH rule 1 x 828 [2,3,1]
+ CRUSH rule 1 x 829 [5,6,7]
+ CRUSH rule 1 x 830 [2,3,5]
+ CRUSH rule 1 x 831 [1,6,0]
+ CRUSH rule 1 x 832 [4,5,3]
+ CRUSH rule 1 x 833 [2,1,7]
+ CRUSH rule 1 x 834 [3,4,2]
+ CRUSH rule 1 x 835 [8,4,3]
+ CRUSH rule 1 x 836 [3,4,5]
+ CRUSH rule 1 x 837 [6,3,1]
+ CRUSH rule 1 x 838 [6,7,2]
+ CRUSH rule 1 x 839 [5,0,6]
+ CRUSH rule 1 x 840 [7,8,5]
+ CRUSH rule 1 x 841 [4,8,5]
+ CRUSH rule 1 x 842 [2,4,0]
+ CRUSH rule 1 x 843 [6,4,7]
+ CRUSH rule 1 x 844 [4,8,1]
+ CRUSH rule 1 x 845 [3,8,6]
+ CRUSH rule 1 x 846 [3,2,0]
+ CRUSH rule 1 x 847 [0,2,7]
+ CRUSH rule 1 x 848 [2,6,1]
+ CRUSH rule 1 x 849 [4,5,3]
+ CRUSH rule 1 x 850 [1,0,5]
+ CRUSH rule 1 x 851 [6,8,7]
+ CRUSH rule 1 x 852 [7,3,8]
+ CRUSH rule 1 x 853 [6,8,1]
+ CRUSH rule 1 x 854 [7,6,0]
+ CRUSH rule 1 x 855 [5,7,2]
+ CRUSH rule 1 x 856 [6,7,3]
+ CRUSH rule 1 x 857 [8,5,0]
+ CRUSH rule 1 x 858 [6,4,1]
+ CRUSH rule 1 x 859 [6,0,7]
+ CRUSH rule 1 x 860 [4,1,2]
+ CRUSH rule 1 x 861 [8,7,6]
+ CRUSH rule 1 x 862 [6,1,7]
+ CRUSH rule 1 x 863 [8,7,0]
+ CRUSH rule 1 x 864 [5,6,8]
+ CRUSH rule 1 x 865 [8,1,0]
+ CRUSH rule 1 x 866 [3,4,8]
+ CRUSH rule 1 x 867 [6,5,1]
+ CRUSH rule 1 x 868 [6,3,0]
+ CRUSH rule 1 x 869 [8,7,3]
+ CRUSH rule 1 x 870 [0,4,8]
+ CRUSH rule 1 x 871 [3,4,5]
+ CRUSH rule 1 x 872 [5,1,3]
+ CRUSH rule 1 x 873 [4,6,5]
+ CRUSH rule 1 x 874 [2,6,1]
+ CRUSH rule 1 x 875 [2,6,4]
+ CRUSH rule 1 x 876 [5,8,1]
+ CRUSH rule 1 x 877 [6,4,2]
+ CRUSH rule 1 x 878 [5,4,0]
+ CRUSH rule 1 x 879 [7,4,8]
+ CRUSH rule 1 x 880 [3,5,0]
+ CRUSH rule 1 x 881 [5,6,1]
+ CRUSH rule 1 x 882 [4,0,2]
+ CRUSH rule 1 x 883 [2,1,0]
+ CRUSH rule 1 x 884 [6,0,4]
+ CRUSH rule 1 x 885 [5,1,4]
+ CRUSH rule 1 x 886 [3,6,4]
+ CRUSH rule 1 x 887 [7,4,0]
+ CRUSH rule 1 x 888 [6,8,0]
+ CRUSH rule 1 x 889 [2,1,7]
+ CRUSH rule 1 x 890 [7,2,0]
+ CRUSH rule 1 x 891 [1,8,0]
+ CRUSH rule 1 x 892 [6,2,3]
+ CRUSH rule 1 x 893 [2,3,7]
+ CRUSH rule 1 x 894 [7,5,0]
+ CRUSH rule 1 x 895 [5,3,2]
+ CRUSH rule 1 x 896 [1,8,2]
+ CRUSH rule 1 x 897 [4,2,6]
+ CRUSH rule 1 x 898 [0,5,4]
+ CRUSH rule 1 x 899 [1,7,6]
+ CRUSH rule 1 x 900 [4,1,0]
+ CRUSH rule 1 x 901 [5,0,1]
+ CRUSH rule 1 x 902 [8,5,7]
+ CRUSH rule 1 x 903 [5,7,3]
+ CRUSH rule 1 x 904 [5,6,8]
+ CRUSH rule 1 x 905 [6,2,5]
+ CRUSH rule 1 x 906 [1,2,0]
+ CRUSH rule 1 x 907 [7,1,0]
+ CRUSH rule 1 x 908 [5,8,1]
+ CRUSH rule 1 x 909 [2,3,4]
+ CRUSH rule 1 x 910 [6,4,0]
+ CRUSH rule 1 x 911 [5,8,4]
+ CRUSH rule 1 x 912 [0,1,7]
+ CRUSH rule 1 x 913 [7,6,8]
+ CRUSH rule 1 x 914 [6,4,7]
+ CRUSH rule 1 x 915 [8,2,6]
+ CRUSH rule 1 x 916 [3,1,4]
+ CRUSH rule 1 x 917 [1,5,3]
+ CRUSH rule 1 x 918 [8,2,1]
+ CRUSH rule 1 x 919 [6,2,8]
+ CRUSH rule 1 x 920 [7,6,4]
+ CRUSH rule 1 x 921 [1,4,5]
+ CRUSH rule 1 x 922 [6,7,8]
+ CRUSH rule 1 x 923 [5,3,6]
+ CRUSH rule 1 x 924 [3,5,4]
+ CRUSH rule 1 x 925 [5,7,3]
+ CRUSH rule 1 x 926 [3,4,5]
+ CRUSH rule 1 x 927 [1,6,3]
+ CRUSH rule 1 x 928 [8,1,2]
+ CRUSH rule 1 x 929 [4,5,1]
+ CRUSH rule 1 x 930 [2,4,6]
+ CRUSH rule 1 x 931 [5,0,1]
+ CRUSH rule 1 x 932 [4,3,0]
+ CRUSH rule 1 x 933 [8,5,4]
+ CRUSH rule 1 x 934 [5,3,8]
+ CRUSH rule 1 x 935 [6,3,4]
+ CRUSH rule 1 x 936 [0,6,7]
+ CRUSH rule 1 x 937 [5,4,3]
+ CRUSH rule 1 x 938 [6,5,8]
+ CRUSH rule 1 x 939 [2,7,0]
+ CRUSH rule 1 x 940 [8,7,6]
+ CRUSH rule 1 x 941 [5,2,0]
+ CRUSH rule 1 x 942 [1,0,2]
+ CRUSH rule 1 x 943 [8,2,4]
+ CRUSH rule 1 x 944 [4,3,7]
+ CRUSH rule 1 x 945 [7,2,4]
+ CRUSH rule 1 x 946 [2,0,7]
+ CRUSH rule 1 x 947 [4,5,3]
+ CRUSH rule 1 x 948 [7,8,6]
+ CRUSH rule 1 x 949 [6,1,7]
+ CRUSH rule 1 x 950 [3,5,8]
+ CRUSH rule 1 x 951 [4,5,3]
+ CRUSH rule 1 x 952 [2,0,7]
+ CRUSH rule 1 x 953 [1,3,5]
+ CRUSH rule 1 x 954 [4,2,5]
+ CRUSH rule 1 x 955 [8,6,0]
+ CRUSH rule 1 x 956 [1,0,8]
+ CRUSH rule 1 x 957 [7,6,1]
+ CRUSH rule 1 x 958 [8,7,5]
+ CRUSH rule 1 x 959 [5,2,7]
+ CRUSH rule 1 x 960 [3,6,5]
+ CRUSH rule 1 x 961 [4,0,2]
+ CRUSH rule 1 x 962 [7,4,3]
+ CRUSH rule 1 x 963 [0,5,2]
+ CRUSH rule 1 x 964 [3,1,4]
+ CRUSH rule 1 x 965 [7,6,5]
+ CRUSH rule 1 x 966 [3,8,4]
+ CRUSH rule 1 x 967 [8,6,5]
+ CRUSH rule 1 x 968 [7,2,4]
+ CRUSH rule 1 x 969 [8,0,6]
+ CRUSH rule 1 x 970 [0,6,3]
+ CRUSH rule 1 x 971 [1,7,8]
+ CRUSH rule 1 x 972 [1,8,4]
+ CRUSH rule 1 x 973 [1,2,0]
+ CRUSH rule 1 x 974 [5,3,2]
+ CRUSH rule 1 x 975 [3,7,4]
+ CRUSH rule 1 x 976 [4,3,5]
+ CRUSH rule 1 x 977 [8,3,2]
+ CRUSH rule 1 x 978 [7,2,8]
+ CRUSH rule 1 x 979 [7,6,0]
+ CRUSH rule 1 x 980 [6,0,7]
+ CRUSH rule 1 x 981 [7,3,2]
+ CRUSH rule 1 x 982 [4,2,0]
+ CRUSH rule 1 x 983 [3,5,6]
+ CRUSH rule 1 x 984 [0,2,1]
+ CRUSH rule 1 x 985 [2,5,4]
+ CRUSH rule 1 x 986 [8,7,3]
+ CRUSH rule 1 x 987 [0,5,1]
+ CRUSH rule 1 x 988 [1,3,5]
+ CRUSH rule 1 x 989 [0,6,3]
+ CRUSH rule 1 x 990 [1,0,8]
+ CRUSH rule 1 x 991 [0,4,1]
+ CRUSH rule 1 x 992 [7,1,5]
+ CRUSH rule 1 x 993 [0,6,2]
+ CRUSH rule 1 x 994 [3,4,5]
+ CRUSH rule 1 x 995 [7,6,2]
+ CRUSH rule 1 x 996 [6,7,5]
+ CRUSH rule 1 x 997 [6,4,1]
+ CRUSH rule 1 x 998 [8,1,2]
+ CRUSH rule 1 x 999 [0,7,8]
+ CRUSH rule 1 x 1000 [8,5,0]
+ CRUSH rule 1 x 1001 [2,0,4]
+ CRUSH rule 1 x 1002 [1,3,2]
+ CRUSH rule 1 x 1003 [2,8,7]
+ CRUSH rule 1 x 1004 [6,1,2]
+ CRUSH rule 1 x 1005 [6,1,2]
+ CRUSH rule 1 x 1006 [1,0,2]
+ CRUSH rule 1 x 1007 [1,2,4]
+ CRUSH rule 1 x 1008 [1,7,0]
+ CRUSH rule 1 x 1009 [6,8,5]
+ CRUSH rule 1 x 1010 [3,4,0]
+ CRUSH rule 1 x 1011 [3,0,4]
+ CRUSH rule 1 x 1012 [3,0,7]
+ CRUSH rule 1 x 1013 [5,1,0]
+ CRUSH rule 1 x 1014 [2,8,4]
+ CRUSH rule 1 x 1015 [6,8,4]
+ CRUSH rule 1 x 1016 [2,0,1]
+ CRUSH rule 1 x 1017 [6,0,2]
+ CRUSH rule 1 x 1018 [5,4,3]
+ CRUSH rule 1 x 1019 [5,3,8]
+ CRUSH rule 1 x 1020 [5,1,3]
+ CRUSH rule 1 x 1021 [5,2,1]
+ CRUSH rule 1 x 1022 [1,6,7]
+ CRUSH rule 1 x 1023 [3,2,0]
+ rule 1 (choose-two) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 2 (chooseleaf), x = 0..1023, numrep = 2..3
+ CRUSH rule 2 x 0 [0,5]
+ CRUSH rule 2 x 1 [0,8]
+ CRUSH rule 2 x 2 [1,3]
+ CRUSH rule 2 x 3 [8,0]
+ CRUSH rule 2 x 4 [5,0]
+ CRUSH rule 2 x 5 [7,0]
+ CRUSH rule 2 x 6 [2,6]
+ CRUSH rule 2 x 7 [5,8]
+ CRUSH rule 2 x 8 [5,6]
+ CRUSH rule 2 x 9 [2,3]
+ CRUSH rule 2 x 10 [0,7]
+ CRUSH rule 2 x 11 [0,7]
+ CRUSH rule 2 x 12 [0,4]
+ CRUSH rule 2 x 13 [3,8]
+ CRUSH rule 2 x 14 [7,0]
+ CRUSH rule 2 x 15 [7,2]
+ CRUSH rule 2 x 16 [3,6]
+ CRUSH rule 2 x 17 [5,1]
+ CRUSH rule 2 x 18 [1,4]
+ CRUSH rule 2 x 19 [7,5]
+ CRUSH rule 2 x 20 [2,4]
+ CRUSH rule 2 x 21 [3,7]
+ CRUSH rule 2 x 22 [8,3]
+ CRUSH rule 2 x 23 [3,6]
+ CRUSH rule 2 x 24 [1,7]
+ CRUSH rule 2 x 25 [3,7]
+ CRUSH rule 2 x 26 [2,8]
+ CRUSH rule 2 x 27 [3,1]
+ CRUSH rule 2 x 28 [6,0]
+ CRUSH rule 2 x 29 [8,5]
+ CRUSH rule 2 x 30 [5,7]
+ CRUSH rule 2 x 31 [8,0]
+ CRUSH rule 2 x 32 [3,6]
+ CRUSH rule 2 x 33 [2,7]
+ CRUSH rule 2 x 34 [2,5]
+ CRUSH rule 2 x 35 [0,8]
+ CRUSH rule 2 x 36 [3,8]
+ CRUSH rule 2 x 37 [0,4]
+ CRUSH rule 2 x 38 [4,8]
+ CRUSH rule 2 x 39 [3,7]
+ CRUSH rule 2 x 40 [7,2]
+ CRUSH rule 2 x 41 [0,6]
+ CRUSH rule 2 x 42 [4,6]
+ CRUSH rule 2 x 43 [0,3]
+ CRUSH rule 2 x 44 [1,6]
+ CRUSH rule 2 x 45 [8,0]
+ CRUSH rule 2 x 46 [2,4]
+ CRUSH rule 2 x 47 [4,2]
+ CRUSH rule 2 x 48 [4,6]
+ CRUSH rule 2 x 49 [5,7]
+ CRUSH rule 2 x 50 [3,1]
+ CRUSH rule 2 x 51 [3,6]
+ CRUSH rule 2 x 52 [8,1]
+ CRUSH rule 2 x 53 [3,8]
+ CRUSH rule 2 x 54 [7,3]
+ CRUSH rule 2 x 55 [8,2]
+ CRUSH rule 2 x 56 [6,4]
+ CRUSH rule 2 x 57 [5,8]
+ CRUSH rule 2 x 58 [1,8]
+ CRUSH rule 2 x 59 [4,2]
+ CRUSH rule 2 x 60 [3,2]
+ CRUSH rule 2 x 61 [4,6]
+ CRUSH rule 2 x 62 [7,0]
+ CRUSH rule 2 x 63 [5,6]
+ CRUSH rule 2 x 64 [4,2]
+ CRUSH rule 2 x 65 [7,3]
+ CRUSH rule 2 x 66 [5,6]
+ CRUSH rule 2 x 67 [5,0]
+ CRUSH rule 2 x 68 [0,5]
+ CRUSH rule 2 x 69 [5,1]
+ CRUSH rule 2 x 70 [7,0]
+ CRUSH rule 2 x 71 [2,8]
+ CRUSH rule 2 x 72 [6,1]
+ CRUSH rule 2 x 73 [2,7]
+ CRUSH rule 2 x 74 [0,7]
+ CRUSH rule 2 x 75 [3,2]
+ CRUSH rule 2 x 76 [5,1]
+ CRUSH rule 2 x 77 [7,2]
+ CRUSH rule 2 x 78 [1,4]
+ CRUSH rule 2 x 79 [5,1]
+ CRUSH rule 2 x 80 [0,3]
+ CRUSH rule 2 x 81 [0,3]
+ CRUSH rule 2 x 82 [7,1]
+ CRUSH rule 2 x 83 [2,6]
+ CRUSH rule 2 x 84 [7,2]
+ CRUSH rule 2 x 85 [3,8]
+ CRUSH rule 2 x 86 [0,6]
+ CRUSH rule 2 x 87 [0,7]
+ CRUSH rule 2 x 88 [1,6]
+ CRUSH rule 2 x 89 [3,0]
+ CRUSH rule 2 x 90 [6,4]
+ CRUSH rule 2 x 91 [3,8]
+ CRUSH rule 2 x 92 [1,8]
+ CRUSH rule 2 x 93 [7,4]
+ CRUSH rule 2 x 94 [0,4]
+ CRUSH rule 2 x 95 [7,5]
+ CRUSH rule 2 x 96 [3,6]
+ CRUSH rule 2 x 97 [8,4]
+ CRUSH rule 2 x 98 [2,7]
+ CRUSH rule 2 x 99 [0,7]
+ CRUSH rule 2 x 100 [1,7]
+ CRUSH rule 2 x 101 [3,7]
+ CRUSH rule 2 x 102 [4,2]
+ CRUSH rule 2 x 103 [4,7]
+ CRUSH rule 2 x 104 [7,4]
+ CRUSH rule 2 x 105 [2,4]
+ CRUSH rule 2 x 106 [1,6]
+ CRUSH rule 2 x 107 [3,2]
+ CRUSH rule 2 x 108 [7,2]
+ CRUSH rule 2 x 109 [1,4]
+ CRUSH rule 2 x 110 [3,2]
+ CRUSH rule 2 x 111 [2,3]
+ CRUSH rule 2 x 112 [2,6]
+ CRUSH rule 2 x 113 [6,2]
+ CRUSH rule 2 x 114 [7,3]
+ CRUSH rule 2 x 115 [8,2]
+ CRUSH rule 2 x 116 [1,6]
+ CRUSH rule 2 x 117 [7,3]
+ CRUSH rule 2 x 118 [0,3]
+ CRUSH rule 2 x 119 [5,6]
+ CRUSH rule 2 x 120 [0,3]
+ CRUSH rule 2 x 121 [2,7]
+ CRUSH rule 2 x 122 [8,5]
+ CRUSH rule 2 x 123 [2,5]
+ CRUSH rule 2 x 124 [3,2]
+ CRUSH rule 2 x 125 [0,7]
+ CRUSH rule 2 x 126 [4,2]
+ CRUSH rule 2 x 127 [3,6]
+ CRUSH rule 2 x 128 [3,6]
+ CRUSH rule 2 x 129 [0,3]
+ CRUSH rule 2 x 130 [3,8]
+ CRUSH rule 2 x 131 [1,3]
+ CRUSH rule 2 x 132 [1,4]
+ CRUSH rule 2 x 133 [3,6]
+ CRUSH rule 2 x 134 [1,8]
+ CRUSH rule 2 x 135 [5,6]
+ CRUSH rule 2 x 136 [2,3]
+ CRUSH rule 2 x 137 [7,3]
+ CRUSH rule 2 x 138 [8,4]
+ CRUSH rule 2 x 139 [3,0]
+ CRUSH rule 2 x 140 [1,6]
+ CRUSH rule 2 x 141 [6,2]
+ CRUSH rule 2 x 142 [3,0]
+ CRUSH rule 2 x 143 [5,8]
+ CRUSH rule 2 x 144 [8,1]
+ CRUSH rule 2 x 145 [8,5]
+ CRUSH rule 2 x 146 [2,6]
+ CRUSH rule 2 x 147 [2,8]
+ CRUSH rule 2 x 148 [3,1]
+ CRUSH rule 2 x 149 [4,8]
+ CRUSH rule 2 x 150 [1,6]
+ CRUSH rule 2 x 151 [3,6]
+ CRUSH rule 2 x 152 [8,4]
+ CRUSH rule 2 x 153 [8,4]
+ CRUSH rule 2 x 154 [3,2]
+ CRUSH rule 2 x 155 [3,7]
+ CRUSH rule 2 x 156 [4,2]
+ CRUSH rule 2 x 157 [4,1]
+ CRUSH rule 2 x 158 [2,8]
+ CRUSH rule 2 x 159 [7,0]
+ CRUSH rule 2 x 160 [2,8]
+ CRUSH rule 2 x 161 [1,5]
+ CRUSH rule 2 x 162 [0,6]
+ CRUSH rule 2 x 163 [5,6]
+ CRUSH rule 2 x 164 [7,1]
+ CRUSH rule 2 x 165 [7,0]
+ CRUSH rule 2 x 166 [2,4]
+ CRUSH rule 2 x 167 [0,7]
+ CRUSH rule 2 x 168 [4,2]
+ CRUSH rule 2 x 169 [2,6]
+ CRUSH rule 2 x 170 [1,4]
+ CRUSH rule 2 x 171 [7,5]
+ CRUSH rule 2 x 172 [0,7]
+ CRUSH rule 2 x 173 [8,5]
+ CRUSH rule 2 x 174 [1,4]
+ CRUSH rule 2 x 175 [6,0]
+ CRUSH rule 2 x 176 [4,1]
+ CRUSH rule 2 x 177 [5,1]
+ CRUSH rule 2 x 178 [3,0]
+ CRUSH rule 2 x 179 [4,1]
+ CRUSH rule 2 x 180 [3,8]
+ CRUSH rule 2 x 181 [6,2]
+ CRUSH rule 2 x 182 [8,5]
+ CRUSH rule 2 x 183 [7,5]
+ CRUSH rule 2 x 184 [5,7]
+ CRUSH rule 2 x 185 [6,1]
+ CRUSH rule 2 x 186 [2,5]
+ CRUSH rule 2 x 187 [1,6]
+ CRUSH rule 2 x 188 [1,8]
+ CRUSH rule 2 x 189 [0,7]
+ CRUSH rule 2 x 190 [4,0]
+ CRUSH rule 2 x 191 [7,1]
+ CRUSH rule 2 x 192 [5,0]
+ CRUSH rule 2 x 193 [4,2]
+ CRUSH rule 2 x 194 [1,3]
+ CRUSH rule 2 x 195 [6,4]
+ CRUSH rule 2 x 196 [6,0]
+ CRUSH rule 2 x 197 [6,5]
+ CRUSH rule 2 x 198 [2,5]
+ CRUSH rule 2 x 199 [0,5]
+ CRUSH rule 2 x 200 [0,5]
+ CRUSH rule 2 x 201 [7,1]
+ CRUSH rule 2 x 202 [6,3]
+ CRUSH rule 2 x 203 [4,8]
+ CRUSH rule 2 x 204 [2,3]
+ CRUSH rule 2 x 205 [0,7]
+ CRUSH rule 2 x 206 [0,7]
+ CRUSH rule 2 x 207 [3,0]
+ CRUSH rule 2 x 208 [7,1]
+ CRUSH rule 2 x 209 [1,8]
+ CRUSH rule 2 x 210 [1,4]
+ CRUSH rule 2 x 211 [5,2]
+ CRUSH rule 2 x 212 [7,5]
+ CRUSH rule 2 x 213 [8,4]
+ CRUSH rule 2 x 214 [4,8]
+ CRUSH rule 2 x 215 [8,1]
+ CRUSH rule 2 x 216 [5,0]
+ CRUSH rule 2 x 217 [0,7]
+ CRUSH rule 2 x 218 [0,7]
+ CRUSH rule 2 x 219 [4,8]
+ CRUSH rule 2 x 220 [5,7]
+ CRUSH rule 2 x 221 [3,6]
+ CRUSH rule 2 x 222 [6,4]
+ CRUSH rule 2 x 223 [1,3]
+ CRUSH rule 2 x 224 [1,5]
+ CRUSH rule 2 x 225 [8,2]
+ CRUSH rule 2 x 226 [7,2]
+ CRUSH rule 2 x 227 [3,1]
+ CRUSH rule 2 x 228 [5,6]
+ CRUSH rule 2 x 229 [3,8]
+ CRUSH rule 2 x 230 [4,7]
+ CRUSH rule 2 x 231 [4,7]
+ CRUSH rule 2 x 232 [2,7]
+ CRUSH rule 2 x 233 [3,7]
+ CRUSH rule 2 x 234 [0,3]
+ CRUSH rule 2 x 235 [3,8]
+ CRUSH rule 2 x 236 [5,2]
+ CRUSH rule 2 x 237 [4,7]
+ CRUSH rule 2 x 238 [4,2]
+ CRUSH rule 2 x 239 [8,4]
+ CRUSH rule 2 x 240 [5,7]
+ CRUSH rule 2 x 241 [3,1]
+ CRUSH rule 2 x 242 [3,2]
+ CRUSH rule 2 x 243 [4,7]
+ CRUSH rule 2 x 244 [4,6]
+ CRUSH rule 2 x 245 [7,0]
+ CRUSH rule 2 x 246 [1,5]
+ CRUSH rule 2 x 247 [6,0]
+ CRUSH rule 2 x 248 [8,0]
+ CRUSH rule 2 x 249 [2,4]
+ CRUSH rule 2 x 250 [2,5]
+ CRUSH rule 2 x 251 [2,3]
+ CRUSH rule 2 x 252 [3,7]
+ CRUSH rule 2 x 253 [3,2]
+ CRUSH rule 2 x 254 [3,2]
+ CRUSH rule 2 x 255 [1,7]
+ CRUSH rule 2 x 256 [5,7]
+ CRUSH rule 2 x 257 [2,8]
+ CRUSH rule 2 x 258 [5,0]
+ CRUSH rule 2 x 259 [4,6]
+ CRUSH rule 2 x 260 [3,6]
+ CRUSH rule 2 x 261 [8,5]
+ CRUSH rule 2 x 262 [5,6]
+ CRUSH rule 2 x 263 [6,1]
+ CRUSH rule 2 x 264 [3,6]
+ CRUSH rule 2 x 265 [8,5]
+ CRUSH rule 2 x 266 [8,2]
+ CRUSH rule 2 x 267 [2,3]
+ CRUSH rule 2 x 268 [0,7]
+ CRUSH rule 2 x 269 [0,8]
+ CRUSH rule 2 x 270 [5,0]
+ CRUSH rule 2 x 271 [7,5]
+ CRUSH rule 2 x 272 [2,8]
+ CRUSH rule 2 x 273 [3,1]
+ CRUSH rule 2 x 274 [6,3]
+ CRUSH rule 2 x 275 [4,7]
+ CRUSH rule 2 x 276 [7,1]
+ CRUSH rule 2 x 277 [6,4]
+ CRUSH rule 2 x 278 [6,1]
+ CRUSH rule 2 x 279 [8,3]
+ CRUSH rule 2 x 280 [0,6]
+ CRUSH rule 2 x 281 [8,0]
+ CRUSH rule 2 x 282 [3,1]
+ CRUSH rule 2 x 283 [8,2]
+ CRUSH rule 2 x 284 [6,3]
+ CRUSH rule 2 x 285 [5,7]
+ CRUSH rule 2 x 286 [2,6]
+ CRUSH rule 2 x 287 [0,4]
+ CRUSH rule 2 x 288 [8,0]
+ CRUSH rule 2 x 289 [4,6]
+ CRUSH rule 2 x 290 [1,3]
+ CRUSH rule 2 x 291 [0,3]
+ CRUSH rule 2 x 292 [8,0]
+ CRUSH rule 2 x 293 [6,0]
+ CRUSH rule 2 x 294 [7,4]
+ CRUSH rule 2 x 295 [4,8]
+ CRUSH rule 2 x 296 [3,1]
+ CRUSH rule 2 x 297 [6,2]
+ CRUSH rule 2 x 298 [1,5]
+ CRUSH rule 2 x 299 [2,8]
+ CRUSH rule 2 x 300 [8,3]
+ CRUSH rule 2 x 301 [0,8]
+ CRUSH rule 2 x 302 [3,0]
+ CRUSH rule 2 x 303 [7,5]
+ CRUSH rule 2 x 304 [2,7]
+ CRUSH rule 2 x 305 [5,8]
+ CRUSH rule 2 x 306 [0,7]
+ CRUSH rule 2 x 307 [0,7]
+ CRUSH rule 2 x 308 [0,8]
+ CRUSH rule 2 x 309 [7,4]
+ CRUSH rule 2 x 310 [4,1]
+ CRUSH rule 2 x 311 [3,6]
+ CRUSH rule 2 x 312 [2,6]
+ CRUSH rule 2 x 313 [5,1]
+ CRUSH rule 2 x 314 [4,2]
+ CRUSH rule 2 x 315 [2,4]
+ CRUSH rule 2 x 316 [6,3]
+ CRUSH rule 2 x 317 [2,6]
+ CRUSH rule 2 x 318 [8,1]
+ CRUSH rule 2 x 319 [5,0]
+ CRUSH rule 2 x 320 [3,7]
+ CRUSH rule 2 x 321 [1,3]
+ CRUSH rule 2 x 322 [2,7]
+ CRUSH rule 2 x 323 [4,7]
+ CRUSH rule 2 x 324 [7,0]
+ CRUSH rule 2 x 325 [4,6]
+ CRUSH rule 2 x 326 [3,2]
+ CRUSH rule 2 x 327 [0,6]
+ CRUSH rule 2 x 328 [7,4]
+ CRUSH rule 2 x 329 [5,6]
+ CRUSH rule 2 x 330 [3,7]
+ CRUSH rule 2 x 331 [2,6]
+ CRUSH rule 2 x 332 [2,4]
+ CRUSH rule 2 x 333 [6,5]
+ CRUSH rule 2 x 334 [8,3]
+ CRUSH rule 2 x 335 [7,1]
+ CRUSH rule 2 x 336 [4,6]
+ CRUSH rule 2 x 337 [7,2]
+ CRUSH rule 2 x 338 [5,6]
+ CRUSH rule 2 x 339 [7,5]
+ CRUSH rule 2 x 340 [2,8]
+ CRUSH rule 2 x 341 [5,1]
+ CRUSH rule 2 x 342 [0,7]
+ CRUSH rule 2 x 343 [6,3]
+ CRUSH rule 2 x 344 [6,0]
+ CRUSH rule 2 x 345 [4,7]
+ CRUSH rule 2 x 346 [8,0]
+ CRUSH rule 2 x 347 [3,1]
+ CRUSH rule 2 x 348 [8,0]
+ CRUSH rule 2 x 349 [1,6]
+ CRUSH rule 2 x 350 [8,5]
+ CRUSH rule 2 x 351 [3,6]
+ CRUSH rule 2 x 352 [1,8]
+ CRUSH rule 2 x 353 [6,4]
+ CRUSH rule 2 x 354 [0,3]
+ CRUSH rule 2 x 355 [3,8]
+ CRUSH rule 2 x 356 [3,0]
+ CRUSH rule 2 x 357 [6,1]
+ CRUSH rule 2 x 358 [2,8]
+ CRUSH rule 2 x 359 [6,1]
+ CRUSH rule 2 x 360 [5,2]
+ CRUSH rule 2 x 361 [8,4]
+ CRUSH rule 2 x 362 [4,1]
+ CRUSH rule 2 x 363 [4,0]
+ CRUSH rule 2 x 364 [2,5]
+ CRUSH rule 2 x 365 [6,5]
+ CRUSH rule 2 x 366 [7,2]
+ CRUSH rule 2 x 367 [4,0]
+ CRUSH rule 2 x 368 [7,4]
+ CRUSH rule 2 x 369 [3,7]
+ CRUSH rule 2 x 370 [8,2]
+ CRUSH rule 2 x 371 [1,3]
+ CRUSH rule 2 x 372 [3,1]
+ CRUSH rule 2 x 373 [0,6]
+ CRUSH rule 2 x 374 [3,8]
+ CRUSH rule 2 x 375 [6,4]
+ CRUSH rule 2 x 376 [7,1]
+ CRUSH rule 2 x 377 [1,3]
+ CRUSH rule 2 x 378 [0,8]
+ CRUSH rule 2 x 379 [8,5]
+ CRUSH rule 2 x 380 [2,5]
+ CRUSH rule 2 x 381 [0,4]
+ CRUSH rule 2 x 382 [1,5]
+ CRUSH rule 2 x 383 [4,6]
+ CRUSH rule 2 x 384 [7,0]
+ CRUSH rule 2 x 385 [7,4]
+ CRUSH rule 2 x 386 [0,3]
+ CRUSH rule 2 x 387 [1,3]
+ CRUSH rule 2 x 388 [5,0]
+ CRUSH rule 2 x 389 [1,5]
+ CRUSH rule 2 x 390 [5,6]
+ CRUSH rule 2 x 391 [5,6]
+ CRUSH rule 2 x 392 [1,8]
+ CRUSH rule 2 x 393 [4,2]
+ CRUSH rule 2 x 394 [4,7]
+ CRUSH rule 2 x 395 [4,0]
+ CRUSH rule 2 x 396 [4,2]
+ CRUSH rule 2 x 397 [2,4]
+ CRUSH rule 2 x 398 [2,4]
+ CRUSH rule 2 x 399 [8,4]
+ CRUSH rule 2 x 400 [8,1]
+ CRUSH rule 2 x 401 [0,5]
+ CRUSH rule 2 x 402 [7,5]
+ CRUSH rule 2 x 403 [0,3]
+ CRUSH rule 2 x 404 [4,2]
+ CRUSH rule 2 x 405 [6,5]
+ CRUSH rule 2 x 406 [2,6]
+ CRUSH rule 2 x 407 [2,8]
+ CRUSH rule 2 x 408 [4,1]
+ CRUSH rule 2 x 409 [7,3]
+ CRUSH rule 2 x 410 [8,3]
+ CRUSH rule 2 x 411 [2,8]
+ CRUSH rule 2 x 412 [0,5]
+ CRUSH rule 2 x 413 [5,0]
+ CRUSH rule 2 x 414 [4,1]
+ CRUSH rule 2 x 415 [0,6]
+ CRUSH rule 2 x 416 [2,6]
+ CRUSH rule 2 x 417 [8,2]
+ CRUSH rule 2 x 418 [7,1]
+ CRUSH rule 2 x 419 [8,3]
+ CRUSH rule 2 x 420 [1,4]
+ CRUSH rule 2 x 421 [8,4]
+ CRUSH rule 2 x 422 [6,4]
+ CRUSH rule 2 x 423 [0,5]
+ CRUSH rule 2 x 424 [8,4]
+ CRUSH rule 2 x 425 [1,3]
+ CRUSH rule 2 x 426 [6,0]
+ CRUSH rule 2 x 427 [0,7]
+ CRUSH rule 2 x 428 [5,7]
+ CRUSH rule 2 x 429 [4,6]
+ CRUSH rule 2 x 430 [3,6]
+ CRUSH rule 2 x 431 [5,0]
+ CRUSH rule 2 x 432 [7,1]
+ CRUSH rule 2 x 433 [6,5]
+ CRUSH rule 2 x 434 [5,2]
+ CRUSH rule 2 x 435 [0,5]
+ CRUSH rule 2 x 436 [4,0]
+ CRUSH rule 2 x 437 [7,5]
+ CRUSH rule 2 x 438 [0,3]
+ CRUSH rule 2 x 439 [1,3]
+ CRUSH rule 2 x 440 [2,7]
+ CRUSH rule 2 x 441 [5,7]
+ CRUSH rule 2 x 442 [2,4]
+ CRUSH rule 2 x 443 [6,0]
+ CRUSH rule 2 x 444 [7,0]
+ CRUSH rule 2 x 445 [6,3]
+ CRUSH rule 2 x 446 [4,1]
+ CRUSH rule 2 x 447 [2,3]
+ CRUSH rule 2 x 448 [7,2]
+ CRUSH rule 2 x 449 [7,5]
+ CRUSH rule 2 x 450 [4,1]
+ CRUSH rule 2 x 451 [6,5]
+ CRUSH rule 2 x 452 [8,3]
+ CRUSH rule 2 x 453 [6,5]
+ CRUSH rule 2 x 454 [6,4]
+ CRUSH rule 2 x 455 [2,7]
+ CRUSH rule 2 x 456 [6,2]
+ CRUSH rule 2 x 457 [7,2]
+ CRUSH rule 2 x 458 [2,8]
+ CRUSH rule 2 x 459 [2,7]
+ CRUSH rule 2 x 460 [6,5]
+ CRUSH rule 2 x 461 [6,5]
+ CRUSH rule 2 x 462 [8,1]
+ CRUSH rule 2 x 463 [6,0]
+ CRUSH rule 2 x 464 [7,4]
+ CRUSH rule 2 x 465 [7,2]
+ CRUSH rule 2 x 466 [5,8]
+ CRUSH rule 2 x 467 [6,4]
+ CRUSH rule 2 x 468 [7,0]
+ CRUSH rule 2 x 469 [7,0]
+ CRUSH rule 2 x 470 [3,0]
+ CRUSH rule 2 x 471 [0,7]
+ CRUSH rule 2 x 472 [5,1]
+ CRUSH rule 2 x 473 [1,4]
+ CRUSH rule 2 x 474 [6,0]
+ CRUSH rule 2 x 475 [6,2]
+ CRUSH rule 2 x 476 [4,6]
+ CRUSH rule 2 x 477 [5,8]
+ CRUSH rule 2 x 478 [6,2]
+ CRUSH rule 2 x 479 [0,5]
+ CRUSH rule 2 x 480 [1,8]
+ CRUSH rule 2 x 481 [2,4]
+ CRUSH rule 2 x 482 [4,7]
+ CRUSH rule 2 x 483 [0,6]
+ CRUSH rule 2 x 484 [1,7]
+ CRUSH rule 2 x 485 [4,7]
+ CRUSH rule 2 x 486 [4,1]
+ CRUSH rule 2 x 487 [5,0]
+ CRUSH rule 2 x 488 [5,7]
+ CRUSH rule 2 x 489 [2,8]
+ CRUSH rule 2 x 490 [6,4]
+ CRUSH rule 2 x 491 [1,7]
+ CRUSH rule 2 x 492 [6,5]
+ CRUSH rule 2 x 493 [0,7]
+ CRUSH rule 2 x 494 [1,7]
+ CRUSH rule 2 x 495 [3,1]
+ CRUSH rule 2 x 496 [7,5]
+ CRUSH rule 2 x 497 [5,7]
+ CRUSH rule 2 x 498 [0,5]
+ CRUSH rule 2 x 499 [8,4]
+ CRUSH rule 2 x 500 [3,6]
+ CRUSH rule 2 x 501 [0,7]
+ CRUSH rule 2 x 502 [7,1]
+ CRUSH rule 2 x 503 [2,3]
+ CRUSH rule 2 x 504 [5,6]
+ CRUSH rule 2 x 505 [0,7]
+ CRUSH rule 2 x 506 [5,2]
+ CRUSH rule 2 x 507 [6,0]
+ CRUSH rule 2 x 508 [0,3]
+ CRUSH rule 2 x 509 [7,5]
+ CRUSH rule 2 x 510 [6,0]
+ CRUSH rule 2 x 511 [5,8]
+ CRUSH rule 2 x 512 [7,0]
+ CRUSH rule 2 x 513 [7,2]
+ CRUSH rule 2 x 514 [4,6]
+ CRUSH rule 2 x 515 [8,5]
+ CRUSH rule 2 x 516 [4,0]
+ CRUSH rule 2 x 517 [7,2]
+ CRUSH rule 2 x 518 [4,6]
+ CRUSH rule 2 x 519 [7,3]
+ CRUSH rule 2 x 520 [2,6]
+ CRUSH rule 2 x 521 [8,0]
+ CRUSH rule 2 x 522 [6,0]
+ CRUSH rule 2 x 523 [4,2]
+ CRUSH rule 2 x 524 [0,4]
+ CRUSH rule 2 x 525 [0,4]
+ CRUSH rule 2 x 526 [1,5]
+ CRUSH rule 2 x 527 [0,5]
+ CRUSH rule 2 x 528 [5,0]
+ CRUSH rule 2 x 529 [5,7]
+ CRUSH rule 2 x 530 [6,5]
+ CRUSH rule 2 x 531 [6,1]
+ CRUSH rule 2 x 532 [6,3]
+ CRUSH rule 2 x 533 [5,6]
+ CRUSH rule 2 x 534 [7,3]
+ CRUSH rule 2 x 535 [8,1]
+ CRUSH rule 2 x 536 [6,2]
+ CRUSH rule 2 x 537 [3,7]
+ CRUSH rule 2 x 538 [6,3]
+ CRUSH rule 2 x 539 [8,3]
+ CRUSH rule 2 x 540 [0,6]
+ CRUSH rule 2 x 541 [2,3]
+ CRUSH rule 2 x 542 [3,2]
+ CRUSH rule 2 x 543 [6,0]
+ CRUSH rule 2 x 544 [3,7]
+ CRUSH rule 2 x 545 [5,7]
+ CRUSH rule 2 x 546 [6,1]
+ CRUSH rule 2 x 547 [8,2]
+ CRUSH rule 2 x 548 [5,2]
+ CRUSH rule 2 x 549 [5,8]
+ CRUSH rule 2 x 550 [0,5]
+ CRUSH rule 2 x 551 [7,5]
+ CRUSH rule 2 x 552 [5,8]
+ CRUSH rule 2 x 553 [4,2]
+ CRUSH rule 2 x 554 [0,8]
+ CRUSH rule 2 x 555 [5,0]
+ CRUSH rule 2 x 556 [3,6]
+ CRUSH rule 2 x 557 [7,4]
+ CRUSH rule 2 x 558 [3,1]
+ CRUSH rule 2 x 559 [4,2]
+ CRUSH rule 2 x 560 [8,3]
+ CRUSH rule 2 x 561 [6,3]
+ CRUSH rule 2 x 562 [3,0]
+ CRUSH rule 2 x 563 [2,6]
+ CRUSH rule 2 x 564 [5,1]
+ CRUSH rule 2 x 565 [3,6]
+ CRUSH rule 2 x 566 [4,7]
+ CRUSH rule 2 x 567 [3,6]
+ CRUSH rule 2 x 568 [7,4]
+ CRUSH rule 2 x 569 [3,1]
+ CRUSH rule 2 x 570 [1,5]
+ CRUSH rule 2 x 571 [3,7]
+ CRUSH rule 2 x 572 [3,2]
+ CRUSH rule 2 x 573 [3,0]
+ CRUSH rule 2 x 574 [2,5]
+ CRUSH rule 2 x 575 [8,2]
+ CRUSH rule 2 x 576 [4,6]
+ CRUSH rule 2 x 577 [8,2]
+ CRUSH rule 2 x 578 [6,1]
+ CRUSH rule 2 x 579 [3,1]
+ CRUSH rule 2 x 580 [3,0]
+ CRUSH rule 2 x 581 [7,2]
+ CRUSH rule 2 x 582 [2,8]
+ CRUSH rule 2 x 583 [6,0]
+ CRUSH rule 2 x 584 [8,1]
+ CRUSH rule 2 x 585 [7,0]
+ CRUSH rule 2 x 586 [0,7]
+ CRUSH rule 2 x 587 [2,5]
+ CRUSH rule 2 x 588 [3,7]
+ CRUSH rule 2 x 589 [7,1]
+ CRUSH rule 2 x 590 [6,2]
+ CRUSH rule 2 x 591 [5,2]
+ CRUSH rule 2 x 592 [2,4]
+ CRUSH rule 2 x 593 [0,8]
+ CRUSH rule 2 x 594 [0,7]
+ CRUSH rule 2 x 595 [7,1]
+ CRUSH rule 2 x 596 [4,0]
+ CRUSH rule 2 x 597 [3,1]
+ CRUSH rule 2 x 598 [3,2]
+ CRUSH rule 2 x 599 [5,2]
+ CRUSH rule 2 x 600 [7,0]
+ CRUSH rule 2 x 601 [0,7]
+ CRUSH rule 2 x 602 [3,7]
+ CRUSH rule 2 x 603 [5,1]
+ CRUSH rule 2 x 604 [7,5]
+ CRUSH rule 2 x 605 [3,0]
+ CRUSH rule 2 x 606 [2,7]
+ CRUSH rule 2 x 607 [0,4]
+ CRUSH rule 2 x 608 [5,2]
+ CRUSH rule 2 x 609 [5,2]
+ CRUSH rule 2 x 610 [3,7]
+ CRUSH rule 2 x 611 [1,8]
+ CRUSH rule 2 x 612 [2,6]
+ CRUSH rule 2 x 613 [7,2]
+ CRUSH rule 2 x 614 [7,2]
+ CRUSH rule 2 x 615 [6,0]
+ CRUSH rule 2 x 616 [0,8]
+ CRUSH rule 2 x 617 [6,1]
+ CRUSH rule 2 x 618 [7,4]
+ CRUSH rule 2 x 619 [5,1]
+ CRUSH rule 2 x 620 [4,1]
+ CRUSH rule 2 x 621 [5,8]
+ CRUSH rule 2 x 622 [0,4]
+ CRUSH rule 2 x 623 [0,6]
+ CRUSH rule 2 x 624 [3,2]
+ CRUSH rule 2 x 625 [2,3]
+ CRUSH rule 2 x 626 [7,0]
+ CRUSH rule 2 x 627 [2,7]
+ CRUSH rule 2 x 628 [8,0]
+ CRUSH rule 2 x 629 [2,6]
+ CRUSH rule 2 x 630 [2,7]
+ CRUSH rule 2 x 631 [0,7]
+ CRUSH rule 2 x 632 [7,0]
+ CRUSH rule 2 x 633 [8,3]
+ CRUSH rule 2 x 634 [0,4]
+ CRUSH rule 2 x 635 [5,6]
+ CRUSH rule 2 x 636 [1,4]
+ CRUSH rule 2 x 637 [4,1]
+ CRUSH rule 2 x 638 [6,0]
+ CRUSH rule 2 x 639 [4,2]
+ CRUSH rule 2 x 640 [3,1]
+ CRUSH rule 2 x 641 [7,2]
+ CRUSH rule 2 x 642 [2,7]
+ CRUSH rule 2 x 643 [3,0]
+ CRUSH rule 2 x 644 [8,1]
+ CRUSH rule 2 x 645 [5,7]
+ CRUSH rule 2 x 646 [8,0]
+ CRUSH rule 2 x 647 [7,1]
+ CRUSH rule 2 x 648 [0,6]
+ CRUSH rule 2 x 649 [4,7]
+ CRUSH rule 2 x 650 [7,3]
+ CRUSH rule 2 x 651 [3,7]
+ CRUSH rule 2 x 652 [3,7]
+ CRUSH rule 2 x 653 [8,5]
+ CRUSH rule 2 x 654 [7,5]
+ CRUSH rule 2 x 655 [0,3]
+ CRUSH rule 2 x 656 [4,7]
+ CRUSH rule 2 x 657 [6,1]
+ CRUSH rule 2 x 658 [5,6]
+ CRUSH rule 2 x 659 [4,6]
+ CRUSH rule 2 x 660 [7,4]
+ CRUSH rule 2 x 661 [1,8]
+ CRUSH rule 2 x 662 [4,2]
+ CRUSH rule 2 x 663 [1,3]
+ CRUSH rule 2 x 664 [1,4]
+ CRUSH rule 2 x 665 [5,7]
+ CRUSH rule 2 x 666 [2,8]
+ CRUSH rule 2 x 667 [1,3]
+ CRUSH rule 2 x 668 [3,7]
+ CRUSH rule 2 x 669 [6,4]
+ CRUSH rule 2 x 670 [4,0]
+ CRUSH rule 2 x 671 [0,7]
+ CRUSH rule 2 x 672 [4,2]
+ CRUSH rule 2 x 673 [5,2]
+ CRUSH rule 2 x 674 [3,1]
+ CRUSH rule 2 x 675 [0,8]
+ CRUSH rule 2 x 676 [0,4]
+ CRUSH rule 2 x 677 [4,1]
+ CRUSH rule 2 x 678 [2,3]
+ CRUSH rule 2 x 679 [6,0]
+ CRUSH rule 2 x 680 [0,4]
+ CRUSH rule 2 x 681 [4,7]
+ CRUSH rule 2 x 682 [0,5]
+ CRUSH rule 2 x 683 [0,5]
+ CRUSH rule 2 x 684 [7,1]
+ CRUSH rule 2 x 685 [7,1]
+ CRUSH rule 2 x 686 [1,4]
+ CRUSH rule 2 x 687 [3,6]
+ CRUSH rule 2 x 688 [5,7]
+ CRUSH rule 2 x 689 [6,5]
+ CRUSH rule 2 x 690 [8,1]
+ CRUSH rule 2 x 691 [3,0]
+ CRUSH rule 2 x 692 [7,2]
+ CRUSH rule 2 x 693 [6,3]
+ CRUSH rule 2 x 694 [6,5]
+ CRUSH rule 2 x 695 [0,8]
+ CRUSH rule 2 x 696 [1,4]
+ CRUSH rule 2 x 697 [6,1]
+ CRUSH rule 2 x 698 [6,2]
+ CRUSH rule 2 x 699 [1,6]
+ CRUSH rule 2 x 700 [0,3]
+ CRUSH rule 2 x 701 [4,1]
+ CRUSH rule 2 x 702 [3,2]
+ CRUSH rule 2 x 703 [8,3]
+ CRUSH rule 2 x 704 [0,3]
+ CRUSH rule 2 x 705 [8,0]
+ CRUSH rule 2 x 706 [1,5]
+ CRUSH rule 2 x 707 [7,3]
+ CRUSH rule 2 x 708 [3,7]
+ CRUSH rule 2 x 709 [6,3]
+ CRUSH rule 2 x 710 [8,4]
+ CRUSH rule 2 x 711 [2,3]
+ CRUSH rule 2 x 712 [2,3]
+ CRUSH rule 2 x 713 [6,3]
+ CRUSH rule 2 x 714 [3,2]
+ CRUSH rule 2 x 715 [1,3]
+ CRUSH rule 2 x 716 [3,6]
+ CRUSH rule 2 x 717 [8,2]
+ CRUSH rule 2 x 718 [3,7]
+ CRUSH rule 2 x 719 [2,6]
+ CRUSH rule 2 x 720 [6,1]
+ CRUSH rule 2 x 721 [5,7]
+ CRUSH rule 2 x 722 [5,7]
+ CRUSH rule 2 x 723 [5,1]
+ CRUSH rule 2 x 724 [0,6]
+ CRUSH rule 2 x 725 [0,3]
+ CRUSH rule 2 x 726 [3,8]
+ CRUSH rule 2 x 727 [4,6]
+ CRUSH rule 2 x 728 [2,7]
+ CRUSH rule 2 x 729 [5,6]
+ CRUSH rule 2 x 730 [3,7]
+ CRUSH rule 2 x 731 [4,1]
+ CRUSH rule 2 x 732 [1,5]
+ CRUSH rule 2 x 733 [5,7]
+ CRUSH rule 2 x 734 [6,4]
+ CRUSH rule 2 x 735 [4,8]
+ CRUSH rule 2 x 736 [3,8]
+ CRUSH rule 2 x 737 [1,6]
+ CRUSH rule 2 x 738 [5,2]
+ CRUSH rule 2 x 739 [0,7]
+ CRUSH rule 2 x 740 [0,8]
+ CRUSH rule 2 x 741 [7,1]
+ CRUSH rule 2 x 742 [8,2]
+ CRUSH rule 2 x 743 [7,0]
+ CRUSH rule 2 x 744 [4,7]
+ CRUSH rule 2 x 745 [3,1]
+ CRUSH rule 2 x 746 [4,1]
+ CRUSH rule 2 x 747 [6,0]
+ CRUSH rule 2 x 748 [2,7]
+ CRUSH rule 2 x 749 [4,8]
+ CRUSH rule 2 x 750 [1,6]
+ CRUSH rule 2 x 751 [2,8]
+ CRUSH rule 2 x 752 [8,1]
+ CRUSH rule 2 x 753 [7,3]
+ CRUSH rule 2 x 754 [8,5]
+ CRUSH rule 2 x 755 [1,6]
+ CRUSH rule 2 x 756 [5,6]
+ CRUSH rule 2 x 757 [8,0]
+ CRUSH rule 2 x 758 [6,0]
+ CRUSH rule 2 x 759 [8,5]
+ CRUSH rule 2 x 760 [1,5]
+ CRUSH rule 2 x 761 [4,1]
+ CRUSH rule 2 x 762 [2,7]
+ CRUSH rule 2 x 763 [8,5]
+ CRUSH rule 2 x 764 [1,7]
+ CRUSH rule 2 x 765 [6,5]
+ CRUSH rule 2 x 766 [8,5]
+ CRUSH rule 2 x 767 [1,8]
+ CRUSH rule 2 x 768 [8,3]
+ CRUSH rule 2 x 769 [6,2]
+ CRUSH rule 2 x 770 [6,0]
+ CRUSH rule 2 x 771 [7,0]
+ CRUSH rule 2 x 772 [8,3]
+ CRUSH rule 2 x 773 [3,1]
+ CRUSH rule 2 x 774 [4,6]
+ CRUSH rule 2 x 775 [6,4]
+ CRUSH rule 2 x 776 [7,2]
+ CRUSH rule 2 x 777 [3,1]
+ CRUSH rule 2 x 778 [1,8]
+ CRUSH rule 2 x 779 [2,7]
+ CRUSH rule 2 x 780 [0,5]
+ CRUSH rule 2 x 781 [6,3]
+ CRUSH rule 2 x 782 [5,0]
+ CRUSH rule 2 x 783 [7,1]
+ CRUSH rule 2 x 784 [0,4]
+ CRUSH rule 2 x 785 [6,1]
+ CRUSH rule 2 x 786 [7,3]
+ CRUSH rule 2 x 787 [1,6]
+ CRUSH rule 2 x 788 [6,0]
+ CRUSH rule 2 x 789 [0,4]
+ CRUSH rule 2 x 790 [8,4]
+ CRUSH rule 2 x 791 [3,8]
+ CRUSH rule 2 x 792 [5,8]
+ CRUSH rule 2 x 793 [6,1]
+ CRUSH rule 2 x 794 [2,6]
+ CRUSH rule 2 x 795 [0,3]
+ CRUSH rule 2 x 796 [3,7]
+ CRUSH rule 2 x 797 [2,3]
+ CRUSH rule 2 x 798 [6,1]
+ CRUSH rule 2 x 799 [5,1]
+ CRUSH rule 2 x 800 [5,0]
+ CRUSH rule 2 x 801 [3,6]
+ CRUSH rule 2 x 802 [1,8]
+ CRUSH rule 2 x 803 [0,5]
+ CRUSH rule 2 x 804 [6,2]
+ CRUSH rule 2 x 805 [3,6]
+ CRUSH rule 2 x 806 [1,3]
+ CRUSH rule 2 x 807 [5,7]
+ CRUSH rule 2 x 808 [4,6]
+ CRUSH rule 2 x 809 [1,4]
+ CRUSH rule 2 x 810 [5,7]
+ CRUSH rule 2 x 811 [8,4]
+ CRUSH rule 2 x 812 [8,5]
+ CRUSH rule 2 x 813 [6,4]
+ CRUSH rule 2 x 814 [3,6]
+ CRUSH rule 2 x 815 [3,1]
+ CRUSH rule 2 x 816 [2,7]
+ CRUSH rule 2 x 817 [4,8]
+ CRUSH rule 2 x 818 [3,0]
+ CRUSH rule 2 x 819 [5,1]
+ CRUSH rule 2 x 820 [3,6]
+ CRUSH rule 2 x 821 [4,6]
+ CRUSH rule 2 x 822 [2,5]
+ CRUSH rule 2 x 823 [4,8]
+ CRUSH rule 2 x 824 [3,7]
+ CRUSH rule 2 x 825 [2,8]
+ CRUSH rule 2 x 826 [7,0]
+ CRUSH rule 2 x 827 [0,8]
+ CRUSH rule 2 x 828 [2,3]
+ CRUSH rule 2 x 829 [5,6]
+ CRUSH rule 2 x 830 [2,3]
+ CRUSH rule 2 x 831 [1,6]
+ CRUSH rule 2 x 832 [4,7]
+ CRUSH rule 2 x 833 [2,7]
+ CRUSH rule 2 x 834 [3,1]
+ CRUSH rule 2 x 835 [8,4]
+ CRUSH rule 2 x 836 [3,7]
+ CRUSH rule 2 x 837 [6,3]
+ CRUSH rule 2 x 838 [6,2]
+ CRUSH rule 2 x 839 [5,0]
+ CRUSH rule 2 x 840 [7,3]
+ CRUSH rule 2 x 841 [4,8]
+ CRUSH rule 2 x 842 [2,4]
+ CRUSH rule 2 x 843 [6,4]
+ CRUSH rule 2 x 844 [4,8]
+ CRUSH rule 2 x 845 [3,8]
+ CRUSH rule 2 x 846 [3,2]
+ CRUSH rule 2 x 847 [0,8]
+ CRUSH rule 2 x 848 [2,6]
+ CRUSH rule 2 x 849 [4,6]
+ CRUSH rule 2 x 850 [1,3]
+ CRUSH rule 2 x 851 [6,4]
+ CRUSH rule 2 x 852 [7,3]
+ CRUSH rule 2 x 853 [6,0]
+ CRUSH rule 2 x 854 [7,0]
+ CRUSH rule 2 x 855 [5,7]
+ CRUSH rule 2 x 856 [6,3]
+ CRUSH rule 2 x 857 [8,5]
+ CRUSH rule 2 x 858 [6,4]
+ CRUSH rule 2 x 859 [6,0]
+ CRUSH rule 2 x 860 [4,1]
+ CRUSH rule 2 x 861 [8,3]
+ CRUSH rule 2 x 862 [6,1]
+ CRUSH rule 2 x 863 [8,2]
+ CRUSH rule 2 x 864 [5,6]
+ CRUSH rule 2 x 865 [8,1]
+ CRUSH rule 2 x 866 [3,6]
+ CRUSH rule 2 x 867 [6,5]
+ CRUSH rule 2 x 868 [6,3]
+ CRUSH rule 2 x 869 [8,5]
+ CRUSH rule 2 x 870 [0,4]
+ CRUSH rule 2 x 871 [3,2]
+ CRUSH rule 2 x 872 [5,1]
+ CRUSH rule 2 x 873 [4,6]
+ CRUSH rule 2 x 874 [2,6]
+ CRUSH rule 2 x 875 [2,6]
+ CRUSH rule 2 x 876 [5,8]
+ CRUSH rule 2 x 877 [6,4]
+ CRUSH rule 2 x 878 [5,2]
+ CRUSH rule 2 x 879 [7,4]
+ CRUSH rule 2 x 880 [3,2]
+ CRUSH rule 2 x 881 [5,6]
+ CRUSH rule 2 x 882 [4,0]
+ CRUSH rule 2 x 883 [2,3]
+ CRUSH rule 2 x 884 [6,0]
+ CRUSH rule 2 x 885 [5,1]
+ CRUSH rule 2 x 886 [3,6]
+ CRUSH rule 2 x 887 [7,4]
+ CRUSH rule 2 x 888 [6,2]
+ CRUSH rule 2 x 889 [2,6]
+ CRUSH rule 2 x 890 [7,2]
+ CRUSH rule 2 x 891 [1,8]
+ CRUSH rule 2 x 892 [6,2]
+ CRUSH rule 2 x 893 [2,3]
+ CRUSH rule 2 x 894 [7,5]
+ CRUSH rule 2 x 895 [5,1]
+ CRUSH rule 2 x 896 [1,8]
+ CRUSH rule 2 x 897 [4,2]
+ CRUSH rule 2 x 898 [0,5]
+ CRUSH rule 2 x 899 [1,7]
+ CRUSH rule 2 x 900 [4,1]
+ CRUSH rule 2 x 901 [5,0]
+ CRUSH rule 2 x 902 [8,5]
+ CRUSH rule 2 x 903 [5,7]
+ CRUSH rule 2 x 904 [5,6]
+ CRUSH rule 2 x 905 [6,2]
+ CRUSH rule 2 x 906 [1,6]
+ CRUSH rule 2 x 907 [7,1]
+ CRUSH rule 2 x 908 [5,8]
+ CRUSH rule 2 x 909 [2,3]
+ CRUSH rule 2 x 910 [6,4]
+ CRUSH rule 2 x 911 [5,8]
+ CRUSH rule 2 x 912 [0,7]
+ CRUSH rule 2 x 913 [7,2]
+ CRUSH rule 2 x 914 [6,4]
+ CRUSH rule 2 x 915 [8,2]
+ CRUSH rule 2 x 916 [3,1]
+ CRUSH rule 2 x 917 [1,5]
+ CRUSH rule 2 x 918 [8,2]
+ CRUSH rule 2 x 919 [6,2]
+ CRUSH rule 2 x 920 [7,4]
+ CRUSH rule 2 x 921 [1,4]
+ CRUSH rule 2 x 922 [6,4]
+ CRUSH rule 2 x 923 [5,8]
+ CRUSH rule 2 x 924 [3,1]
+ CRUSH rule 2 x 925 [5,7]
+ CRUSH rule 2 x 926 [3,0]
+ CRUSH rule 2 x 927 [1,6]
+ CRUSH rule 2 x 928 [8,1]
+ CRUSH rule 2 x 929 [4,1]
+ CRUSH rule 2 x 930 [2,4]
+ CRUSH rule 2 x 931 [5,0]
+ CRUSH rule 2 x 932 [4,1]
+ CRUSH rule 2 x 933 [8,5]
+ CRUSH rule 2 x 934 [5,6]
+ CRUSH rule 2 x 935 [6,3]
+ CRUSH rule 2 x 936 [0,6]
+ CRUSH rule 2 x 937 [5,8]
+ CRUSH rule 2 x 938 [6,5]
+ CRUSH rule 2 x 939 [2,7]
+ CRUSH rule 2 x 940 [8,5]
+ CRUSH rule 2 x 941 [5,2]
+ CRUSH rule 2 x 942 [1,8]
+ CRUSH rule 2 x 943 [8,2]
+ CRUSH rule 2 x 944 [4,8]
+ CRUSH rule 2 x 945 [7,2]
+ CRUSH rule 2 x 946 [2,8]
+ CRUSH rule 2 x 947 [4,2]
+ CRUSH rule 2 x 948 [7,5]
+ CRUSH rule 2 x 949 [6,1]
+ CRUSH rule 2 x 950 [3,6]
+ CRUSH rule 2 x 951 [4,8]
+ CRUSH rule 2 x 952 [2,7]
+ CRUSH rule 2 x 953 [1,3]
+ CRUSH rule 2 x 954 [4,2]
+ CRUSH rule 2 x 955 [8,0]
+ CRUSH rule 2 x 956 [1,6]
+ CRUSH rule 2 x 957 [7,1]
+ CRUSH rule 2 x 958 [8,4]
+ CRUSH rule 2 x 959 [5,2]
+ CRUSH rule 2 x 960 [3,6]
+ CRUSH rule 2 x 961 [4,0]
+ CRUSH rule 2 x 962 [7,4]
+ CRUSH rule 2 x 963 [0,5]
+ CRUSH rule 2 x 964 [3,1]
+ CRUSH rule 2 x 965 [7,4]
+ CRUSH rule 2 x 966 [3,8]
+ CRUSH rule 2 x 967 [8,5]
+ CRUSH rule 2 x 968 [7,2]
+ CRUSH rule 2 x 969 [8,0]
+ CRUSH rule 2 x 970 [0,6]
+ CRUSH rule 2 x 971 [1,7]
+ CRUSH rule 2 x 972 [1,8]
+ CRUSH rule 2 x 973 [1,6]
+ CRUSH rule 2 x 974 [5,1]
+ CRUSH rule 2 x 975 [3,7]
+ CRUSH rule 2 x 976 [4,8]
+ CRUSH rule 2 x 977 [8,3]
+ CRUSH rule 2 x 978 [7,2]
+ CRUSH rule 2 x 979 [7,1]
+ CRUSH rule 2 x 980 [6,0]
+ CRUSH rule 2 x 981 [7,3]
+ CRUSH rule 2 x 982 [4,2]
+ CRUSH rule 2 x 983 [3,7]
+ CRUSH rule 2 x 984 [0,7]
+ CRUSH rule 2 x 985 [2,5]
+ CRUSH rule 2 x 986 [8,3]
+ CRUSH rule 2 x 987 [0,5]
+ CRUSH rule 2 x 988 [1,3]
+ CRUSH rule 2 x 989 [0,6]
+ CRUSH rule 2 x 990 [1,6]
+ CRUSH rule 2 x 991 [0,4]
+ CRUSH rule 2 x 992 [7,1]
+ CRUSH rule 2 x 993 [0,6]
+ CRUSH rule 2 x 994 [3,7]
+ CRUSH rule 2 x 995 [7,1]
+ CRUSH rule 2 x 996 [6,5]
+ CRUSH rule 2 x 997 [6,4]
+ CRUSH rule 2 x 998 [8,1]
+ CRUSH rule 2 x 999 [0,7]
+ CRUSH rule 2 x 1000 [8,5]
+ CRUSH rule 2 x 1001 [2,5]
+ CRUSH rule 2 x 1002 [1,3]
+ CRUSH rule 2 x 1003 [2,8]
+ CRUSH rule 2 x 1004 [6,1]
+ CRUSH rule 2 x 1005 [6,1]
+ CRUSH rule 2 x 1006 [1,6]
+ CRUSH rule 2 x 1007 [1,5]
+ CRUSH rule 2 x 1008 [1,7]
+ CRUSH rule 2 x 1009 [6,4]
+ CRUSH rule 2 x 1010 [3,1]
+ CRUSH rule 2 x 1011 [3,0]
+ CRUSH rule 2 x 1012 [3,0]
+ CRUSH rule 2 x 1013 [5,1]
+ CRUSH rule 2 x 1014 [2,8]
+ CRUSH rule 2 x 1015 [6,5]
+ CRUSH rule 2 x 1016 [2,4]
+ CRUSH rule 2 x 1017 [6,0]
+ CRUSH rule 2 x 1018 [5,0]
+ CRUSH rule 2 x 1019 [5,8]
+ CRUSH rule 2 x 1020 [5,1]
+ CRUSH rule 2 x 1021 [5,2]
+ CRUSH rule 2 x 1022 [1,6]
+ CRUSH rule 2 x 1023 [3,2]
+ rule 2 (chooseleaf) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 2 x 0 [0,5,7]
+ CRUSH rule 2 x 1 [0,8,5]
+ CRUSH rule 2 x 2 [1,3,7]
+ CRUSH rule 2 x 3 [8,0,4]
+ CRUSH rule 2 x 4 [5,0,7]
+ CRUSH rule 2 x 5 [7,0,4]
+ CRUSH rule 2 x 6 [2,6,3]
+ CRUSH rule 2 x 7 [5,8,2]
+ CRUSH rule 2 x 8 [5,6,0]
+ CRUSH rule 2 x 9 [2,3,8]
+ CRUSH rule 2 x 10 [0,7,4]
+ CRUSH rule 2 x 11 [0,7,4]
+ CRUSH rule 2 x 12 [0,4,6]
+ CRUSH rule 2 x 13 [3,8,1]
+ CRUSH rule 2 x 14 [7,0,5]
+ CRUSH rule 2 x 15 [7,2,4]
+ CRUSH rule 2 x 16 [3,6,0]
+ CRUSH rule 2 x 17 [5,1,6]
+ CRUSH rule 2 x 18 [1,4,6]
+ CRUSH rule 2 x 19 [7,5,0]
+ CRUSH rule 2 x 20 [2,4,7]
+ CRUSH rule 2 x 21 [3,7,2]
+ CRUSH rule 2 x 22 [8,3,1]
+ CRUSH rule 2 x 23 [3,6,2]
+ CRUSH rule 2 x 24 [1,7,3]
+ CRUSH rule 2 x 25 [3,7,0]
+ CRUSH rule 2 x 26 [2,8,4]
+ CRUSH rule 2 x 27 [3,1,8]
+ CRUSH rule 2 x 28 [6,0,3]
+ CRUSH rule 2 x 29 [8,5,2]
+ CRUSH rule 2 x 30 [5,7,0]
+ CRUSH rule 2 x 31 [8,0,4]
+ CRUSH rule 2 x 32 [3,6,2]
+ CRUSH rule 2 x 33 [2,7,5]
+ CRUSH rule 2 x 34 [2,5,7]
+ CRUSH rule 2 x 35 [0,8,5]
+ CRUSH rule 2 x 36 [3,8,2]
+ CRUSH rule 2 x 37 [0,4,6]
+ CRUSH rule 2 x 38 [4,8,2]
+ CRUSH rule 2 x 39 [3,7,0]
+ CRUSH rule 2 x 40 [7,2,3]
+ CRUSH rule 2 x 41 [0,6,4]
+ CRUSH rule 2 x 42 [4,6,2]
+ CRUSH rule 2 x 43 [0,3,7]
+ CRUSH rule 2 x 44 [1,6,5]
+ CRUSH rule 2 x 45 [8,0,4]
+ CRUSH rule 2 x 46 [2,4,8]
+ CRUSH rule 2 x 47 [4,2,8]
+ CRUSH rule 2 x 48 [4,6,2]
+ CRUSH rule 2 x 49 [5,7,2]
+ CRUSH rule 2 x 50 [3,1,7]
+ CRUSH rule 2 x 51 [3,6,0]
+ CRUSH rule 2 x 52 [8,1,4]
+ CRUSH rule 2 x 53 [3,8,0]
+ CRUSH rule 2 x 54 [7,3,1]
+ CRUSH rule 2 x 55 [8,2,4]
+ CRUSH rule 2 x 56 [6,4,2]
+ CRUSH rule 2 x 57 [5,8,1]
+ CRUSH rule 2 x 58 [1,8,5]
+ CRUSH rule 2 x 59 [4,2,7]
+ CRUSH rule 2 x 60 [3,2,6]
+ CRUSH rule 2 x 61 [4,6,0]
+ CRUSH rule 2 x 62 [7,0,5]
+ CRUSH rule 2 x 63 [5,6,0]
+ CRUSH rule 2 x 64 [4,2,8]
+ CRUSH rule 2 x 65 [7,3,0]
+ CRUSH rule 2 x 66 [5,6,2]
+ CRUSH rule 2 x 67 [5,0,8]
+ CRUSH rule 2 x 68 [0,5,8]
+ CRUSH rule 2 x 69 [5,1,6]
+ CRUSH rule 2 x 70 [7,0,5]
+ CRUSH rule 2 x 71 [2,8,5]
+ CRUSH rule 2 x 72 [6,1,5]
+ CRUSH rule 2 x 73 [2,7,3]
+ CRUSH rule 2 x 74 [0,7,3]
+ CRUSH rule 2 x 75 [3,2,6]
+ CRUSH rule 2 x 76 [5,1,7]
+ CRUSH rule 2 x 77 [7,2,5]
+ CRUSH rule 2 x 78 [1,4,8]
+ CRUSH rule 2 x 79 [5,1,7]
+ CRUSH rule 2 x 80 [0,3,6]
+ CRUSH rule 2 x 81 [0,3,6]
+ CRUSH rule 2 x 82 [7,1,5]
+ CRUSH rule 2 x 83 [2,6,3]
+ CRUSH rule 2 x 84 [7,2,4]
+ CRUSH rule 2 x 85 [3,8,0]
+ CRUSH rule 2 x 86 [0,6,3]
+ CRUSH rule 2 x 87 [0,7,4]
+ CRUSH rule 2 x 88 [1,6,5]
+ CRUSH rule 2 x 89 [3,0,7]
+ CRUSH rule 2 x 90 [6,4,1]
+ CRUSH rule 2 x 91 [3,8,1]
+ CRUSH rule 2 x 92 [1,8,4]
+ CRUSH rule 2 x 93 [7,4,2]
+ CRUSH rule 2 x 94 [0,4,8]
+ CRUSH rule 2 x 95 [7,5,1]
+ CRUSH rule 2 x 96 [3,6,1]
+ CRUSH rule 2 x 97 [8,4,0]
+ CRUSH rule 2 x 98 [2,7,5]
+ CRUSH rule 2 x 99 [0,7,3]
+ CRUSH rule 2 x 100 [1,7,4]
+ CRUSH rule 2 x 101 [3,7,2]
+ CRUSH rule 2 x 102 [4,2,7]
+ CRUSH rule 2 x 103 [4,7,0]
+ CRUSH rule 2 x 104 [7,4,1]
+ CRUSH rule 2 x 105 [2,4,6]
+ CRUSH rule 2 x 106 [1,6,5]
+ CRUSH rule 2 x 107 [3,2,6]
+ CRUSH rule 2 x 108 [7,2,5]
+ CRUSH rule 2 x 109 [1,4,6]
+ CRUSH rule 2 x 110 [3,2,7]
+ CRUSH rule 2 x 111 [2,3,6]
+ CRUSH rule 2 x 112 [2,6,4]
+ CRUSH rule 2 x 113 [6,2,4]
+ CRUSH rule 2 x 114 [7,3,1]
+ CRUSH rule 2 x 115 [8,2,3]
+ CRUSH rule 2 x 116 [1,6,3]
+ CRUSH rule 2 x 117 [7,3,2]
+ CRUSH rule 2 x 118 [0,3,8]
+ CRUSH rule 2 x 119 [5,6,1]
+ CRUSH rule 2 x 120 [0,3,7]
+ CRUSH rule 2 x 121 [2,7,5]
+ CRUSH rule 2 x 122 [8,5,0]
+ CRUSH rule 2 x 123 [2,5,8]
+ CRUSH rule 2 x 124 [3,2,8]
+ CRUSH rule 2 x 125 [0,7,3]
+ CRUSH rule 2 x 126 [4,2,6]
+ CRUSH rule 2 x 127 [3,6,2]
+ CRUSH rule 2 x 128 [3,6,1]
+ CRUSH rule 2 x 129 [0,3,7]
+ CRUSH rule 2 x 130 [3,8,2]
+ CRUSH rule 2 x 131 [1,3,8]
+ CRUSH rule 2 x 132 [1,4,6]
+ CRUSH rule 2 x 133 [3,6,2]
+ CRUSH rule 2 x 134 [1,8,5]
+ CRUSH rule 2 x 135 [5,6,2]
+ CRUSH rule 2 x 136 [2,3,6]
+ CRUSH rule 2 x 137 [7,3,2]
+ CRUSH rule 2 x 138 [8,4,2]
+ CRUSH rule 2 x 139 [3,0,7]
+ CRUSH rule 2 x 140 [1,6,4]
+ CRUSH rule 2 x 141 [6,2,3]
+ CRUSH rule 2 x 142 [3,0,8]
+ CRUSH rule 2 x 143 [5,8,1]
+ CRUSH rule 2 x 144 [8,1,3]
+ CRUSH rule 2 x 145 [8,5,0]
+ CRUSH rule 2 x 146 [2,6,4]
+ CRUSH rule 2 x 147 [2,8,4]
+ CRUSH rule 2 x 148 [3,1,7]
+ CRUSH rule 2 x 149 [4,8,1]
+ CRUSH rule 2 x 150 [1,6,5]
+ CRUSH rule 2 x 151 [3,6,0]
+ CRUSH rule 2 x 152 [8,4,2]
+ CRUSH rule 2 x 153 [8,4,0]
+ CRUSH rule 2 x 154 [3,2,7]
+ CRUSH rule 2 x 155 [3,7,2]
+ CRUSH rule 2 x 156 [4,2,6]
+ CRUSH rule 2 x 157 [4,1,6]
+ CRUSH rule 2 x 158 [2,8,5]
+ CRUSH rule 2 x 159 [7,0,5]
+ CRUSH rule 2 x 160 [2,8,5]
+ CRUSH rule 2 x 161 [1,5,6]
+ CRUSH rule 2 x 162 [0,6,4]
+ CRUSH rule 2 x 163 [5,6,1]
+ CRUSH rule 2 x 164 [7,1,5]
+ CRUSH rule 2 x 165 [7,0,5]
+ CRUSH rule 2 x 166 [2,4,6]
+ CRUSH rule 2 x 167 [0,7,4]
+ CRUSH rule 2 x 168 [4,2,7]
+ CRUSH rule 2 x 169 [2,6,4]
+ CRUSH rule 2 x 170 [1,4,8]
+ CRUSH rule 2 x 171 [7,5,0]
+ CRUSH rule 2 x 172 [0,7,5]
+ CRUSH rule 2 x 173 [8,5,1]
+ CRUSH rule 2 x 174 [1,4,7]
+ CRUSH rule 2 x 175 [6,0,4]
+ CRUSH rule 2 x 176 [4,1,7]
+ CRUSH rule 2 x 177 [5,1,6]
+ CRUSH rule 2 x 178 [3,0,8]
+ CRUSH rule 2 x 179 [4,1,7]
+ CRUSH rule 2 x 180 [3,8,1]
+ CRUSH rule 2 x 181 [6,2,4]
+ CRUSH rule 2 x 182 [8,5,1]
+ CRUSH rule 2 x 183 [7,5,1]
+ CRUSH rule 2 x 184 [5,7,2]
+ CRUSH rule 2 x 185 [6,1,4]
+ CRUSH rule 2 x 186 [2,5,7]
+ CRUSH rule 2 x 187 [1,6,4]
+ CRUSH rule 2 x 188 [1,8,5]
+ CRUSH rule 2 x 189 [0,7,4]
+ CRUSH rule 2 x 190 [4,0,8]
+ CRUSH rule 2 x 191 [7,1,3]
+ CRUSH rule 2 x 192 [5,0,8]
+ CRUSH rule 2 x 193 [4,2,6]
+ CRUSH rule 2 x 194 [1,3,8]
+ CRUSH rule 2 x 195 [6,4,1]
+ CRUSH rule 2 x 196 [6,0,3]
+ CRUSH rule 2 x 197 [6,5,2]
+ CRUSH rule 2 x 198 [2,5,6]
+ CRUSH rule 2 x 199 [0,5,7]
+ CRUSH rule 2 x 200 [0,5,6]
+ CRUSH rule 2 x 201 [7,1,5]
+ CRUSH rule 2 x 202 [6,3,1]
+ CRUSH rule 2 x 203 [4,8,1]
+ CRUSH rule 2 x 204 [2,3,7]
+ CRUSH rule 2 x 205 [0,7,5]
+ CRUSH rule 2 x 206 [0,7,5]
+ CRUSH rule 2 x 207 [3,0,7]
+ CRUSH rule 2 x 208 [7,1,4]
+ CRUSH rule 2 x 209 [1,8,4]
+ CRUSH rule 2 x 210 [1,4,8]
+ CRUSH rule 2 x 211 [5,2,8]
+ CRUSH rule 2 x 212 [7,5,0]
+ CRUSH rule 2 x 213 [8,4,0]
+ CRUSH rule 2 x 214 [4,8,2]
+ CRUSH rule 2 x 215 [8,1,4]
+ CRUSH rule 2 x 216 [5,0,6]
+ CRUSH rule 2 x 217 [0,7,3]
+ CRUSH rule 2 x 218 [0,7,5]
+ CRUSH rule 2 x 219 [4,8,2]
+ CRUSH rule 2 x 220 [5,7,1]
+ CRUSH rule 2 x 221 [3,6,0]
+ CRUSH rule 2 x 222 [6,4,1]
+ CRUSH rule 2 x 223 [1,3,6]
+ CRUSH rule 2 x 224 [1,5,8]
+ CRUSH rule 2 x 225 [8,2,3]
+ CRUSH rule 2 x 226 [7,2,3]
+ CRUSH rule 2 x 227 [3,1,6]
+ CRUSH rule 2 x 228 [5,6,0]
+ CRUSH rule 2 x 229 [3,8,2]
+ CRUSH rule 2 x 230 [4,7,2]
+ CRUSH rule 2 x 231 [4,7,2]
+ CRUSH rule 2 x 232 [2,7,4]
+ CRUSH rule 2 x 233 [3,7,2]
+ CRUSH rule 2 x 234 [0,3,6]
+ CRUSH rule 2 x 235 [3,8,0]
+ CRUSH rule 2 x 236 [5,2,7]
+ CRUSH rule 2 x 237 [4,7,1]
+ CRUSH rule 2 x 238 [4,2,6]
+ CRUSH rule 2 x 239 [8,4,1]
+ CRUSH rule 2 x 240 [5,7,0]
+ CRUSH rule 2 x 241 [3,1,8]
+ CRUSH rule 2 x 242 [3,2,6]
+ CRUSH rule 2 x 243 [4,7,0]
+ CRUSH rule 2 x 244 [4,6,0]
+ CRUSH rule 2 x 245 [7,0,5]
+ CRUSH rule 2 x 246 [1,5,8]
+ CRUSH rule 2 x 247 [6,0,4]
+ CRUSH rule 2 x 248 [8,0,3]
+ CRUSH rule 2 x 249 [2,4,7]
+ CRUSH rule 2 x 250 [2,5,6]
+ CRUSH rule 2 x 251 [2,3,6]
+ CRUSH rule 2 x 252 [3,7,0]
+ CRUSH rule 2 x 253 [3,2,6]
+ CRUSH rule 2 x 254 [3,2,7]
+ CRUSH rule 2 x 255 [1,7,5]
+ CRUSH rule 2 x 256 [5,7,0]
+ CRUSH rule 2 x 257 [2,8,4]
+ CRUSH rule 2 x 258 [5,0,6]
+ CRUSH rule 2 x 259 [4,6,2]
+ CRUSH rule 2 x 260 [3,6,2]
+ CRUSH rule 2 x 261 [8,5,2]
+ CRUSH rule 2 x 262 [5,6,0]
+ CRUSH rule 2 x 263 [6,1,5]
+ CRUSH rule 2 x 264 [3,6,2]
+ CRUSH rule 2 x 265 [8,5,2]
+ CRUSH rule 2 x 266 [8,2,5]
+ CRUSH rule 2 x 267 [2,3,8]
+ CRUSH rule 2 x 268 [0,7,4]
+ CRUSH rule 2 x 269 [0,8,4]
+ CRUSH rule 2 x 270 [5,0,7]
+ CRUSH rule 2 x 271 [7,5,1]
+ CRUSH rule 2 x 272 [2,8,3]
+ CRUSH rule 2 x 273 [3,1,7]
+ CRUSH rule 2 x 274 [6,3,1]
+ CRUSH rule 2 x 275 [4,7,0]
+ CRUSH rule 2 x 276 [7,1,4]
+ CRUSH rule 2 x 277 [6,4,0]
+ CRUSH rule 2 x 278 [6,1,4]
+ CRUSH rule 2 x 279 [8,3,2]
+ CRUSH rule 2 x 280 [0,6,4]
+ CRUSH rule 2 x 281 [8,0,3]
+ CRUSH rule 2 x 282 [3,1,6]
+ CRUSH rule 2 x 283 [8,2,5]
+ CRUSH rule 2 x 284 [6,3,0]
+ CRUSH rule 2 x 285 [5,7,0]
+ CRUSH rule 2 x 286 [2,6,3]
+ CRUSH rule 2 x 287 [0,4,6]
+ CRUSH rule 2 x 288 [8,0,4]
+ CRUSH rule 2 x 289 [4,6,2]
+ CRUSH rule 2 x 290 [1,3,7]
+ CRUSH rule 2 x 291 [0,3,8]
+ CRUSH rule 2 x 292 [8,0,5]
+ CRUSH rule 2 x 293 [6,0,4]
+ CRUSH rule 2 x 294 [7,4,1]
+ CRUSH rule 2 x 295 [4,8,0]
+ CRUSH rule 2 x 296 [3,1,6]
+ CRUSH rule 2 x 297 [6,2,4]
+ CRUSH rule 2 x 298 [1,5,7]
+ CRUSH rule 2 x 299 [2,8,3]
+ CRUSH rule 2 x 300 [8,3,0]
+ CRUSH rule 2 x 301 [0,8,4]
+ CRUSH rule 2 x 302 [3,0,6]
+ CRUSH rule 2 x 303 [7,5,1]
+ CRUSH rule 2 x 304 [2,7,5]
+ CRUSH rule 2 x 305 [5,8,2]
+ CRUSH rule 2 x 306 [0,7,3]
+ CRUSH rule 2 x 307 [0,7,5]
+ CRUSH rule 2 x 308 [0,8,4]
+ CRUSH rule 2 x 309 [7,4,0]
+ CRUSH rule 2 x 310 [4,1,7]
+ CRUSH rule 2 x 311 [3,6,0]
+ CRUSH rule 2 x 312 [2,6,3]
+ CRUSH rule 2 x 313 [5,1,6]
+ CRUSH rule 2 x 314 [4,2,6]
+ CRUSH rule 2 x 315 [2,4,8]
+ CRUSH rule 2 x 316 [6,3,1]
+ CRUSH rule 2 x 317 [2,6,5]
+ CRUSH rule 2 x 318 [8,1,4]
+ CRUSH rule 2 x 319 [5,0,8]
+ CRUSH rule 2 x 320 [3,7,1]
+ CRUSH rule 2 x 321 [1,3,8]
+ CRUSH rule 2 x 322 [2,7,3]
+ CRUSH rule 2 x 323 [4,7,0]
+ CRUSH rule 2 x 324 [7,0,3]
+ CRUSH rule 2 x 325 [4,6,0]
+ CRUSH rule 2 x 326 [3,2,6]
+ CRUSH rule 2 x 327 [0,6,3]
+ CRUSH rule 2 x 328 [7,4,1]
+ CRUSH rule 2 x 329 [5,6,2]
+ CRUSH rule 2 x 330 [3,7,2]
+ CRUSH rule 2 x 331 [2,6,3]
+ CRUSH rule 2 x 332 [2,4,6]
+ CRUSH rule 2 x 333 [6,5,1]
+ CRUSH rule 2 x 334 [8,3,2]
+ CRUSH rule 2 x 335 [7,1,4]
+ CRUSH rule 2 x 336 [4,6,0]
+ CRUSH rule 2 x 337 [7,2,3]
+ CRUSH rule 2 x 338 [5,6,1]
+ CRUSH rule 2 x 339 [7,5,2]
+ CRUSH rule 2 x 340 [2,8,4]
+ CRUSH rule 2 x 341 [5,1,7]
+ CRUSH rule 2 x 342 [0,7,4]
+ CRUSH rule 2 x 343 [6,3,0]
+ CRUSH rule 2 x 344 [6,0,5]
+ CRUSH rule 2 x 345 [4,7,1]
+ CRUSH rule 2 x 346 [8,0,5]
+ CRUSH rule 2 x 347 [3,1,7]
+ CRUSH rule 2 x 348 [8,0,5]
+ CRUSH rule 2 x 349 [1,6,4]
+ CRUSH rule 2 x 350 [8,5,1]
+ CRUSH rule 2 x 351 [3,6,0]
+ CRUSH rule 2 x 352 [1,8,3]
+ CRUSH rule 2 x 353 [6,4,0]
+ CRUSH rule 2 x 354 [0,3,6]
+ CRUSH rule 2 x 355 [3,8,2]
+ CRUSH rule 2 x 356 [3,0,6]
+ CRUSH rule 2 x 357 [6,1,4]
+ CRUSH rule 2 x 358 [2,8,4]
+ CRUSH rule 2 x 359 [6,1,3]
+ CRUSH rule 2 x 360 [5,2,7]
+ CRUSH rule 2 x 361 [8,4,1]
+ CRUSH rule 2 x 362 [4,1,6]
+ CRUSH rule 2 x 363 [4,0,6]
+ CRUSH rule 2 x 364 [2,5,6]
+ CRUSH rule 2 x 365 [6,5,2]
+ CRUSH rule 2 x 366 [7,2,3]
+ CRUSH rule 2 x 367 [4,0,6]
+ CRUSH rule 2 x 368 [7,4,0]
+ CRUSH rule 2 x 369 [3,7,1]
+ CRUSH rule 2 x 370 [8,2,5]
+ CRUSH rule 2 x 371 [1,3,6]
+ CRUSH rule 2 x 372 [3,1,8]
+ CRUSH rule 2 x 373 [0,6,4]
+ CRUSH rule 2 x 374 [3,8,1]
+ CRUSH rule 2 x 375 [6,4,1]
+ CRUSH rule 2 x 376 [7,1,5]
+ CRUSH rule 2 x 377 [1,3,6]
+ CRUSH rule 2 x 378 [0,8,3]
+ CRUSH rule 2 x 379 [8,5,2]
+ CRUSH rule 2 x 380 [2,5,8]
+ CRUSH rule 2 x 381 [0,4,7]
+ CRUSH rule 2 x 382 [1,5,8]
+ CRUSH rule 2 x 383 [4,6,0]
+ CRUSH rule 2 x 384 [7,0,4]
+ CRUSH rule 2 x 385 [7,4,0]
+ CRUSH rule 2 x 386 [0,3,6]
+ CRUSH rule 2 x 387 [1,3,6]
+ CRUSH rule 2 x 388 [5,0,8]
+ CRUSH rule 2 x 389 [1,5,7]
+ CRUSH rule 2 x 390 [5,6,0]
+ CRUSH rule 2 x 391 [5,6,2]
+ CRUSH rule 2 x 392 [1,8,5]
+ CRUSH rule 2 x 393 [4,2,6]
+ CRUSH rule 2 x 394 [4,7,0]
+ CRUSH rule 2 x 395 [4,0,8]
+ CRUSH rule 2 x 396 [4,2,7]
+ CRUSH rule 2 x 397 [2,4,8]
+ CRUSH rule 2 x 398 [2,4,8]
+ CRUSH rule 2 x 399 [8,4,2]
+ CRUSH rule 2 x 400 [8,1,4]
+ CRUSH rule 2 x 401 [0,5,8]
+ CRUSH rule 2 x 402 [7,5,2]
+ CRUSH rule 2 x 403 [0,3,8]
+ CRUSH rule 2 x 404 [4,2,8]
+ CRUSH rule 2 x 405 [6,5,2]
+ CRUSH rule 2 x 406 [2,6,5]
+ CRUSH rule 2 x 407 [2,8,5]
+ CRUSH rule 2 x 408 [4,1,6]
+ CRUSH rule 2 x 409 [7,3,0]
+ CRUSH rule 2 x 410 [8,3,2]
+ CRUSH rule 2 x 411 [2,8,3]
+ CRUSH rule 2 x 412 [0,5,8]
+ CRUSH rule 2 x 413 [5,0,8]
+ CRUSH rule 2 x 414 [4,1,6]
+ CRUSH rule 2 x 415 [0,6,4]
+ CRUSH rule 2 x 416 [2,6,3]
+ CRUSH rule 2 x 417 [8,2,4]
+ CRUSH rule 2 x 418 [7,1,4]
+ CRUSH rule 2 x 419 [8,3,0]
+ CRUSH rule 2 x 420 [1,4,6]
+ CRUSH rule 2 x 421 [8,4,0]
+ CRUSH rule 2 x 422 [6,4,1]
+ CRUSH rule 2 x 423 [0,5,6]
+ CRUSH rule 2 x 424 [8,4,1]
+ CRUSH rule 2 x 425 [1,3,7]
+ CRUSH rule 2 x 426 [6,0,5]
+ CRUSH rule 2 x 427 [0,7,5]
+ CRUSH rule 2 x 428 [5,7,1]
+ CRUSH rule 2 x 429 [4,6,0]
+ CRUSH rule 2 x 430 [3,6,2]
+ CRUSH rule 2 x 431 [5,0,7]
+ CRUSH rule 2 x 432 [7,1,3]
+ CRUSH rule 2 x 433 [6,5,1]
+ CRUSH rule 2 x 434 [5,2,8]
+ CRUSH rule 2 x 435 [0,5,6]
+ CRUSH rule 2 x 436 [4,0,7]
+ CRUSH rule 2 x 437 [7,5,2]
+ CRUSH rule 2 x 438 [0,3,8]
+ CRUSH rule 2 x 439 [1,3,7]
+ CRUSH rule 2 x 440 [2,7,5]
+ CRUSH rule 2 x 441 [5,7,2]
+ CRUSH rule 2 x 442 [2,4,7]
+ CRUSH rule 2 x 443 [6,0,4]
+ CRUSH rule 2 x 444 [7,0,4]
+ CRUSH rule 2 x 445 [6,3,1]
+ CRUSH rule 2 x 446 [4,1,8]
+ CRUSH rule 2 x 447 [2,3,7]
+ CRUSH rule 2 x 448 [7,2,5]
+ CRUSH rule 2 x 449 [7,5,1]
+ CRUSH rule 2 x 450 [4,1,8]
+ CRUSH rule 2 x 451 [6,5,0]
+ CRUSH rule 2 x 452 [8,3,0]
+ CRUSH rule 2 x 453 [6,5,1]
+ CRUSH rule 2 x 454 [6,4,2]
+ CRUSH rule 2 x 455 [2,7,5]
+ CRUSH rule 2 x 456 [6,2,5]
+ CRUSH rule 2 x 457 [7,2,5]
+ CRUSH rule 2 x 458 [2,8,5]
+ CRUSH rule 2 x 459 [2,7,5]
+ CRUSH rule 2 x 460 [6,5,0]
+ CRUSH rule 2 x 461 [6,5,2]
+ CRUSH rule 2 x 462 [8,1,5]
+ CRUSH rule 2 x 463 [6,0,4]
+ CRUSH rule 2 x 464 [7,4,2]
+ CRUSH rule 2 x 465 [7,2,5]
+ CRUSH rule 2 x 466 [5,8,2]
+ CRUSH rule 2 x 467 [6,4,0]
+ CRUSH rule 2 x 468 [7,0,3]
+ CRUSH rule 2 x 469 [7,0,5]
+ CRUSH rule 2 x 470 [3,0,6]
+ CRUSH rule 2 x 471 [0,7,5]
+ CRUSH rule 2 x 472 [5,1,8]
+ CRUSH rule 2 x 473 [1,4,6]
+ CRUSH rule 2 x 474 [6,0,3]
+ CRUSH rule 2 x 475 [6,2,3]
+ CRUSH rule 2 x 476 [4,6,1]
+ CRUSH rule 2 x 477 [5,8,2]
+ CRUSH rule 2 x 478 [6,2,3]
+ CRUSH rule 2 x 479 [0,5,8]
+ CRUSH rule 2 x 480 [1,8,3]
+ CRUSH rule 2 x 481 [2,4,7]
+ CRUSH rule 2 x 482 [4,7,0]
+ CRUSH rule 2 x 483 [0,6,4]
+ CRUSH rule 2 x 484 [1,7,4]
+ CRUSH rule 2 x 485 [4,7,0]
+ CRUSH rule 2 x 486 [4,1,7]
+ CRUSH rule 2 x 487 [5,0,8]
+ CRUSH rule 2 x 488 [5,7,1]
+ CRUSH rule 2 x 489 [2,8,5]
+ CRUSH rule 2 x 490 [6,4,1]
+ CRUSH rule 2 x 491 [1,7,5]
+ CRUSH rule 2 x 492 [6,5,0]
+ CRUSH rule 2 x 493 [0,7,3]
+ CRUSH rule 2 x 494 [1,7,4]
+ CRUSH rule 2 x 495 [3,1,8]
+ CRUSH rule 2 x 496 [7,5,0]
+ CRUSH rule 2 x 497 [5,7,0]
+ CRUSH rule 2 x 498 [0,5,8]
+ CRUSH rule 2 x 499 [8,4,2]
+ CRUSH rule 2 x 500 [3,6,0]
+ CRUSH rule 2 x 501 [0,7,3]
+ CRUSH rule 2 x 502 [7,1,4]
+ CRUSH rule 2 x 503 [2,3,7]
+ CRUSH rule 2 x 504 [5,6,2]
+ CRUSH rule 2 x 505 [0,7,3]
+ CRUSH rule 2 x 506 [5,2,8]
+ CRUSH rule 2 x 507 [6,0,3]
+ CRUSH rule 2 x 508 [0,3,8]
+ CRUSH rule 2 x 509 [7,5,2]
+ CRUSH rule 2 x 510 [6,0,4]
+ CRUSH rule 2 x 511 [5,8,2]
+ CRUSH rule 2 x 512 [7,0,4]
+ CRUSH rule 2 x 513 [7,2,4]
+ CRUSH rule 2 x 514 [4,6,1]
+ CRUSH rule 2 x 515 [8,5,0]
+ CRUSH rule 2 x 516 [4,0,8]
+ CRUSH rule 2 x 517 [7,2,4]
+ CRUSH rule 2 x 518 [4,6,1]
+ CRUSH rule 2 x 519 [7,3,1]
+ CRUSH rule 2 x 520 [2,6,3]
+ CRUSH rule 2 x 521 [8,0,3]
+ CRUSH rule 2 x 522 [6,0,4]
+ CRUSH rule 2 x 523 [4,2,7]
+ CRUSH rule 2 x 524 [0,4,8]
+ CRUSH rule 2 x 525 [0,4,6]
+ CRUSH rule 2 x 526 [1,5,8]
+ CRUSH rule 2 x 527 [0,5,6]
+ CRUSH rule 2 x 528 [5,0,6]
+ CRUSH rule 2 x 529 [5,7,0]
+ CRUSH rule 2 x 530 [6,5,2]
+ CRUSH rule 2 x 531 [6,1,3]
+ CRUSH rule 2 x 532 [6,3,0]
+ CRUSH rule 2 x 533 [5,6,2]
+ CRUSH rule 2 x 534 [7,3,1]
+ CRUSH rule 2 x 535 [8,1,5]
+ CRUSH rule 2 x 536 [6,2,4]
+ CRUSH rule 2 x 537 [3,7,2]
+ CRUSH rule 2 x 538 [6,3,0]
+ CRUSH rule 2 x 539 [8,3,1]
+ CRUSH rule 2 x 540 [0,6,3]
+ CRUSH rule 2 x 541 [2,3,8]
+ CRUSH rule 2 x 542 [3,2,8]
+ CRUSH rule 2 x 543 [6,0,4]
+ CRUSH rule 2 x 544 [3,7,0]
+ CRUSH rule 2 x 545 [5,7,0]
+ CRUSH rule 2 x 546 [6,1,5]
+ CRUSH rule 2 x 547 [8,2,4]
+ CRUSH rule 2 x 548 [5,2,8]
+ CRUSH rule 2 x 549 [5,8,2]
+ CRUSH rule 2 x 550 [0,5,7]
+ CRUSH rule 2 x 551 [7,5,0]
+ CRUSH rule 2 x 552 [5,8,1]
+ CRUSH rule 2 x 553 [4,2,7]
+ CRUSH rule 2 x 554 [0,8,5]
+ CRUSH rule 2 x 555 [5,0,8]
+ CRUSH rule 2 x 556 [3,6,0]
+ CRUSH rule 2 x 557 [7,4,0]
+ CRUSH rule 2 x 558 [3,1,6]
+ CRUSH rule 2 x 559 [4,2,6]
+ CRUSH rule 2 x 560 [8,3,2]
+ CRUSH rule 2 x 561 [6,3,1]
+ CRUSH rule 2 x 562 [3,0,6]
+ CRUSH rule 2 x 563 [2,6,4]
+ CRUSH rule 2 x 564 [5,1,7]
+ CRUSH rule 2 x 565 [3,6,2]
+ CRUSH rule 2 x 566 [4,7,2]
+ CRUSH rule 2 x 567 [3,6,1]
+ CRUSH rule 2 x 568 [7,4,1]
+ CRUSH rule 2 x 569 [3,1,7]
+ CRUSH rule 2 x 570 [1,5,8]
+ CRUSH rule 2 x 571 [3,7,1]
+ CRUSH rule 2 x 572 [3,2,8]
+ CRUSH rule 2 x 573 [3,0,7]
+ CRUSH rule 2 x 574 [2,5,8]
+ CRUSH rule 2 x 575 [8,2,5]
+ CRUSH rule 2 x 576 [4,6,0]
+ CRUSH rule 2 x 577 [8,2,5]
+ CRUSH rule 2 x 578 [6,1,4]
+ CRUSH rule 2 x 579 [3,1,6]
+ CRUSH rule 2 x 580 [3,0,7]
+ CRUSH rule 2 x 581 [7,2,4]
+ CRUSH rule 2 x 582 [2,8,5]
+ CRUSH rule 2 x 583 [6,0,3]
+ CRUSH rule 2 x 584 [8,1,3]
+ CRUSH rule 2 x 585 [7,0,5]
+ CRUSH rule 2 x 586 [0,7,5]
+ CRUSH rule 2 x 587 [2,5,7]
+ CRUSH rule 2 x 588 [3,7,1]
+ CRUSH rule 2 x 589 [7,1,4]
+ CRUSH rule 2 x 590 [6,2,3]
+ CRUSH rule 2 x 591 [5,2,8]
+ CRUSH rule 2 x 592 [2,4,7]
+ CRUSH rule 2 x 593 [0,8,3]
+ CRUSH rule 2 x 594 [0,7,4]
+ CRUSH rule 2 x 595 [7,1,3]
+ CRUSH rule 2 x 596 [4,0,6]
+ CRUSH rule 2 x 597 [3,1,7]
+ CRUSH rule 2 x 598 [3,2,6]
+ CRUSH rule 2 x 599 [5,2,8]
+ CRUSH rule 2 x 600 [7,0,3]
+ CRUSH rule 2 x 601 [0,7,3]
+ CRUSH rule 2 x 602 [3,7,1]
+ CRUSH rule 2 x 603 [5,1,6]
+ CRUSH rule 2 x 604 [7,5,0]
+ CRUSH rule 2 x 605 [3,0,7]
+ CRUSH rule 2 x 606 [2,7,4]
+ CRUSH rule 2 x 607 [0,4,8]
+ CRUSH rule 2 x 608 [5,2,7]
+ CRUSH rule 2 x 609 [5,2,8]
+ CRUSH rule 2 x 610 [3,7,0]
+ CRUSH rule 2 x 611 [1,8,5]
+ CRUSH rule 2 x 612 [2,6,5]
+ CRUSH rule 2 x 613 [7,2,3]
+ CRUSH rule 2 x 614 [7,2,3]
+ CRUSH rule 2 x 615 [6,0,5]
+ CRUSH rule 2 x 616 [0,8,5]
+ CRUSH rule 2 x 617 [6,1,4]
+ CRUSH rule 2 x 618 [7,4,0]
+ CRUSH rule 2 x 619 [5,1,8]
+ CRUSH rule 2 x 620 [4,1,6]
+ CRUSH rule 2 x 621 [5,8,2]
+ CRUSH rule 2 x 622 [0,4,8]
+ CRUSH rule 2 x 623 [0,6,3]
+ CRUSH rule 2 x 624 [3,2,8]
+ CRUSH rule 2 x 625 [2,3,7]
+ CRUSH rule 2 x 626 [7,0,3]
+ CRUSH rule 2 x 627 [2,7,3]
+ CRUSH rule 2 x 628 [8,0,5]
+ CRUSH rule 2 x 629 [2,6,5]
+ CRUSH rule 2 x 630 [2,7,5]
+ CRUSH rule 2 x 631 [0,7,3]
+ CRUSH rule 2 x 632 [7,0,3]
+ CRUSH rule 2 x 633 [8,3,1]
+ CRUSH rule 2 x 634 [0,4,7]
+ CRUSH rule 2 x 635 [5,6,2]
+ CRUSH rule 2 x 636 [1,4,8]
+ CRUSH rule 2 x 637 [4,1,7]
+ CRUSH rule 2 x 638 [6,0,4]
+ CRUSH rule 2 x 639 [4,2,8]
+ CRUSH rule 2 x 640 [3,1,8]
+ CRUSH rule 2 x 641 [7,2,3]
+ CRUSH rule 2 x 642 [2,7,3]
+ CRUSH rule 2 x 643 [3,0,8]
+ CRUSH rule 2 x 644 [8,1,5]
+ CRUSH rule 2 x 645 [5,7,1]
+ CRUSH rule 2 x 646 [8,0,3]
+ CRUSH rule 2 x 647 [7,1,3]
+ CRUSH rule 2 x 648 [0,6,3]
+ CRUSH rule 2 x 649 [4,7,0]
+ CRUSH rule 2 x 650 [7,3,1]
+ CRUSH rule 2 x 651 [3,7,0]
+ CRUSH rule 2 x 652 [3,7,0]
+ CRUSH rule 2 x 653 [8,5,2]
+ CRUSH rule 2 x 654 [7,5,2]
+ CRUSH rule 2 x 655 [0,3,7]
+ CRUSH rule 2 x 656 [4,7,1]
+ CRUSH rule 2 x 657 [6,1,4]
+ CRUSH rule 2 x 658 [5,6,0]
+ CRUSH rule 2 x 659 [4,6,2]
+ CRUSH rule 2 x 660 [7,4,1]
+ CRUSH rule 2 x 661 [1,8,3]
+ CRUSH rule 2 x 662 [4,2,7]
+ CRUSH rule 2 x 663 [1,3,8]
+ CRUSH rule 2 x 664 [1,4,7]
+ CRUSH rule 2 x 665 [5,7,0]
+ CRUSH rule 2 x 666 [2,8,4]
+ CRUSH rule 2 x 667 [1,3,7]
+ CRUSH rule 2 x 668 [3,7,1]
+ CRUSH rule 2 x 669 [6,4,0]
+ CRUSH rule 2 x 670 [4,0,6]
+ CRUSH rule 2 x 671 [0,7,3]
+ CRUSH rule 2 x 672 [4,2,7]
+ CRUSH rule 2 x 673 [5,2,7]
+ CRUSH rule 2 x 674 [3,1,8]
+ CRUSH rule 2 x 675 [0,8,3]
+ CRUSH rule 2 x 676 [0,4,8]
+ CRUSH rule 2 x 677 [4,1,7]
+ CRUSH rule 2 x 678 [2,3,8]
+ CRUSH rule 2 x 679 [6,0,5]
+ CRUSH rule 2 x 680 [0,4,6]
+ CRUSH rule 2 x 681 [4,7,1]
+ CRUSH rule 2 x 682 [0,5,7]
+ CRUSH rule 2 x 683 [0,5,6]
+ CRUSH rule 2 x 684 [7,1,5]
+ CRUSH rule 2 x 685 [7,1,5]
+ CRUSH rule 2 x 686 [1,4,8]
+ CRUSH rule 2 x 687 [3,6,1]
+ CRUSH rule 2 x 688 [5,7,2]
+ CRUSH rule 2 x 689 [6,5,0]
+ CRUSH rule 2 x 690 [8,1,3]
+ CRUSH rule 2 x 691 [3,0,6]
+ CRUSH rule 2 x 692 [7,2,3]
+ CRUSH rule 2 x 693 [6,3,1]
+ CRUSH rule 2 x 694 [6,5,1]
+ CRUSH rule 2 x 695 [0,8,4]
+ CRUSH rule 2 x 696 [1,4,8]
+ CRUSH rule 2 x 697 [6,1,3]
+ CRUSH rule 2 x 698 [6,2,4]
+ CRUSH rule 2 x 699 [1,6,3]
+ CRUSH rule 2 x 700 [0,3,7]
+ CRUSH rule 2 x 701 [4,1,7]
+ CRUSH rule 2 x 702 [3,2,8]
+ CRUSH rule 2 x 703 [8,3,1]
+ CRUSH rule 2 x 704 [0,3,8]
+ CRUSH rule 2 x 705 [8,0,4]
+ CRUSH rule 2 x 706 [1,5,6]
+ CRUSH rule 2 x 707 [7,3,1]
+ CRUSH rule 2 x 708 [3,7,1]
+ CRUSH rule 2 x 709 [6,3,0]
+ CRUSH rule 2 x 710 [8,4,0]
+ CRUSH rule 2 x 711 [2,3,8]
+ CRUSH rule 2 x 712 [2,3,7]
+ CRUSH rule 2 x 713 [6,3,0]
+ CRUSH rule 2 x 714 [3,2,7]
+ CRUSH rule 2 x 715 [1,3,6]
+ CRUSH rule 2 x 716 [3,6,0]
+ CRUSH rule 2 x 717 [8,2,5]
+ CRUSH rule 2 x 718 [3,7,2]
+ CRUSH rule 2 x 719 [2,6,3]
+ CRUSH rule 2 x 720 [6,1,4]
+ CRUSH rule 2 x 721 [5,7,2]
+ CRUSH rule 2 x 722 [5,7,1]
+ CRUSH rule 2 x 723 [5,1,7]
+ CRUSH rule 2 x 724 [0,6,3]
+ CRUSH rule 2 x 725 [0,3,7]
+ CRUSH rule 2 x 726 [3,8,1]
+ CRUSH rule 2 x 727 [4,6,1]
+ CRUSH rule 2 x 728 [2,7,4]
+ CRUSH rule 2 x 729 [5,6,2]
+ CRUSH rule 2 x 730 [3,7,2]
+ CRUSH rule 2 x 731 [4,1,8]
+ CRUSH rule 2 x 732 [1,5,6]
+ CRUSH rule 2 x 733 [5,7,0]
+ CRUSH rule 2 x 734 [6,4,2]
+ CRUSH rule 2 x 735 [4,8,1]
+ CRUSH rule 2 x 736 [3,8,1]
+ CRUSH rule 2 x 737 [1,6,4]
+ CRUSH rule 2 x 738 [5,2,7]
+ CRUSH rule 2 x 739 [0,7,4]
+ CRUSH rule 2 x 740 [0,8,4]
+ CRUSH rule 2 x 741 [7,1,4]
+ CRUSH rule 2 x 742 [8,2,3]
+ CRUSH rule 2 x 743 [7,0,5]
+ CRUSH rule 2 x 744 [4,7,1]
+ CRUSH rule 2 x 745 [3,1,8]
+ CRUSH rule 2 x 746 [4,1,7]
+ CRUSH rule 2 x 747 [6,0,3]
+ CRUSH rule 2 x 748 [2,7,5]
+ CRUSH rule 2 x 749 [4,8,0]
+ CRUSH rule 2 x 750 [1,6,3]
+ CRUSH rule 2 x 751 [2,8,3]
+ CRUSH rule 2 x 752 [8,1,5]
+ CRUSH rule 2 x 753 [7,3,1]
+ CRUSH rule 2 x 754 [8,5,2]
+ CRUSH rule 2 x 755 [1,6,3]
+ CRUSH rule 2 x 756 [5,6,1]
+ CRUSH rule 2 x 757 [8,0,5]
+ CRUSH rule 2 x 758 [6,0,3]
+ CRUSH rule 2 x 759 [8,5,2]
+ CRUSH rule 2 x 760 [1,5,7]
+ CRUSH rule 2 x 761 [4,1,8]
+ CRUSH rule 2 x 762 [2,7,5]
+ CRUSH rule 2 x 763 [8,5,1]
+ CRUSH rule 2 x 764 [1,7,5]
+ CRUSH rule 2 x 765 [6,5,2]
+ CRUSH rule 2 x 766 [8,5,1]
+ CRUSH rule 2 x 767 [1,8,3]
+ CRUSH rule 2 x 768 [8,3,2]
+ CRUSH rule 2 x 769 [6,2,5]
+ CRUSH rule 2 x 770 [6,0,4]
+ CRUSH rule 2 x 771 [7,0,3]
+ CRUSH rule 2 x 772 [8,3,1]
+ CRUSH rule 2 x 773 [3,1,7]
+ CRUSH rule 2 x 774 [4,6,2]
+ CRUSH rule 2 x 775 [6,4,2]
+ CRUSH rule 2 x 776 [7,2,5]
+ CRUSH rule 2 x 777 [3,1,6]
+ CRUSH rule 2 x 778 [1,8,4]
+ CRUSH rule 2 x 779 [2,7,3]
+ CRUSH rule 2 x 780 [0,5,7]
+ CRUSH rule 2 x 781 [6,3,2]
+ CRUSH rule 2 x 782 [5,0,8]
+ CRUSH rule 2 x 783 [7,1,3]
+ CRUSH rule 2 x 784 [0,4,6]
+ CRUSH rule 2 x 785 [6,1,3]
+ CRUSH rule 2 x 786 [7,3,1]
+ CRUSH rule 2 x 787 [1,6,4]
+ CRUSH rule 2 x 788 [6,0,3]
+ CRUSH rule 2 x 789 [0,4,8]
+ CRUSH rule 2 x 790 [8,4,0]
+ CRUSH rule 2 x 791 [3,8,0]
+ CRUSH rule 2 x 792 [5,8,0]
+ CRUSH rule 2 x 793 [6,1,3]
+ CRUSH rule 2 x 794 [2,6,4]
+ CRUSH rule 2 x 795 [0,3,8]
+ CRUSH rule 2 x 796 [3,7,2]
+ CRUSH rule 2 x 797 [2,3,8]
+ CRUSH rule 2 x 798 [6,1,5]
+ CRUSH rule 2 x 799 [5,1,8]
+ CRUSH rule 2 x 800 [5,0,7]
+ CRUSH rule 2 x 801 [3,6,1]
+ CRUSH rule 2 x 802 [1,8,5]
+ CRUSH rule 2 x 803 [0,5,7]
+ CRUSH rule 2 x 804 [6,2,5]
+ CRUSH rule 2 x 805 [3,6,1]
+ CRUSH rule 2 x 806 [1,3,7]
+ CRUSH rule 2 x 807 [5,7,2]
+ CRUSH rule 2 x 808 [4,6,2]
+ CRUSH rule 2 x 809 [1,4,8]
+ CRUSH rule 2 x 810 [5,7,2]
+ CRUSH rule 2 x 811 [8,4,0]
+ CRUSH rule 2 x 812 [8,5,2]
+ CRUSH rule 2 x 813 [6,4,2]
+ CRUSH rule 2 x 814 [3,6,1]
+ CRUSH rule 2 x 815 [3,1,8]
+ CRUSH rule 2 x 816 [2,7,3]
+ CRUSH rule 2 x 817 [4,8,2]
+ CRUSH rule 2 x 818 [3,0,7]
+ CRUSH rule 2 x 819 [5,1,8]
+ CRUSH rule 2 x 820 [3,6,0]
+ CRUSH rule 2 x 821 [4,6,2]
+ CRUSH rule 2 x 822 [2,5,8]
+ CRUSH rule 2 x 823 [4,8,2]
+ CRUSH rule 2 x 824 [3,7,2]
+ CRUSH rule 2 x 825 [2,8,5]
+ CRUSH rule 2 x 826 [7,0,5]
+ CRUSH rule 2 x 827 [0,8,3]
+ CRUSH rule 2 x 828 [2,3,8]
+ CRUSH rule 2 x 829 [5,6,1]
+ CRUSH rule 2 x 830 [2,3,8]
+ CRUSH rule 2 x 831 [1,6,3]
+ CRUSH rule 2 x 832 [4,7,0]
+ CRUSH rule 2 x 833 [2,7,3]
+ CRUSH rule 2 x 834 [3,1,7]
+ CRUSH rule 2 x 835 [8,4,1]
+ CRUSH rule 2 x 836 [3,7,1]
+ CRUSH rule 2 x 837 [6,3,1]
+ CRUSH rule 2 x 838 [6,2,4]
+ CRUSH rule 2 x 839 [5,0,6]
+ CRUSH rule 2 x 840 [7,3,2]
+ CRUSH rule 2 x 841 [4,8,2]
+ CRUSH rule 2 x 842 [2,4,6]
+ CRUSH rule 2 x 843 [6,4,1]
+ CRUSH rule 2 x 844 [4,8,1]
+ CRUSH rule 2 x 845 [3,8,2]
+ CRUSH rule 2 x 846 [3,2,7]
+ CRUSH rule 2 x 847 [0,8,4]
+ CRUSH rule 2 x 848 [2,6,5]
+ CRUSH rule 2 x 849 [4,6,2]
+ CRUSH rule 2 x 850 [1,3,6]
+ CRUSH rule 2 x 851 [6,4,0]
+ CRUSH rule 2 x 852 [7,3,0]
+ CRUSH rule 2 x 853 [6,0,4]
+ CRUSH rule 2 x 854 [7,0,4]
+ CRUSH rule 2 x 855 [5,7,2]
+ CRUSH rule 2 x 856 [6,3,2]
+ CRUSH rule 2 x 857 [8,5,0]
+ CRUSH rule 2 x 858 [6,4,1]
+ CRUSH rule 2 x 859 [6,0,5]
+ CRUSH rule 2 x 860 [4,1,7]
+ CRUSH rule 2 x 861 [8,3,1]
+ CRUSH rule 2 x 862 [6,1,4]
+ CRUSH rule 2 x 863 [8,2,3]
+ CRUSH rule 2 x 864 [5,6,2]
+ CRUSH rule 2 x 865 [8,1,3]
+ CRUSH rule 2 x 866 [3,6,0]
+ CRUSH rule 2 x 867 [6,5,1]
+ CRUSH rule 2 x 868 [6,3,0]
+ CRUSH rule 2 x 869 [8,5,2]
+ CRUSH rule 2 x 870 [0,4,8]
+ CRUSH rule 2 x 871 [3,2,8]
+ CRUSH rule 2 x 872 [5,1,8]
+ CRUSH rule 2 x 873 [4,6,2]
+ CRUSH rule 2 x 874 [2,6,4]
+ CRUSH rule 2 x 875 [2,6,4]
+ CRUSH rule 2 x 876 [5,8,1]
+ CRUSH rule 2 x 877 [6,4,2]
+ CRUSH rule 2 x 878 [5,2,7]
+ CRUSH rule 2 x 879 [7,4,2]
+ CRUSH rule 2 x 880 [3,2,8]
+ CRUSH rule 2 x 881 [5,6,1]
+ CRUSH rule 2 x 882 [4,0,7]
+ CRUSH rule 2 x 883 [2,3,7]
+ CRUSH rule 2 x 884 [6,0,4]
+ CRUSH rule 2 x 885 [5,1,8]
+ CRUSH rule 2 x 886 [3,6,0]
+ CRUSH rule 2 x 887 [7,4,0]
+ CRUSH rule 2 x 888 [6,2,5]
+ CRUSH rule 2 x 889 [2,6,4]
+ CRUSH rule 2 x 890 [7,2,4]
+ CRUSH rule 2 x 891 [1,8,5]
+ CRUSH rule 2 x 892 [6,2,3]
+ CRUSH rule 2 x 893 [2,3,7]
+ CRUSH rule 2 x 894 [7,5,0]
+ CRUSH rule 2 x 895 [5,1,8]
+ CRUSH rule 2 x 896 [1,8,5]
+ CRUSH rule 2 x 897 [4,2,6]
+ CRUSH rule 2 x 898 [0,5,7]
+ CRUSH rule 2 x 899 [1,7,5]
+ CRUSH rule 2 x 900 [4,1,6]
+ CRUSH rule 2 x 901 [5,0,8]
+ CRUSH rule 2 x 902 [8,5,0]
+ CRUSH rule 2 x 903 [5,7,1]
+ CRUSH rule 2 x 904 [5,6,2]
+ CRUSH rule 2 x 905 [6,2,5]
+ CRUSH rule 2 x 906 [1,6,3]
+ CRUSH rule 2 x 907 [7,1,5]
+ CRUSH rule 2 x 908 [5,8,1]
+ CRUSH rule 2 x 909 [2,3,7]
+ CRUSH rule 2 x 910 [6,4,0]
+ CRUSH rule 2 x 911 [5,8,1]
+ CRUSH rule 2 x 912 [0,7,3]
+ CRUSH rule 2 x 913 [7,2,4]
+ CRUSH rule 2 x 914 [6,4,0]
+ CRUSH rule 2 x 915 [8,2,3]
+ CRUSH rule 2 x 916 [3,1,8]
+ CRUSH rule 2 x 917 [1,5,8]
+ CRUSH rule 2 x 918 [8,2,4]
+ CRUSH rule 2 x 919 [6,2,3]
+ CRUSH rule 2 x 920 [7,4,0]
+ CRUSH rule 2 x 921 [1,4,6]
+ CRUSH rule 2 x 922 [6,4,0]
+ CRUSH rule 2 x 923 [5,8,2]
+ CRUSH rule 2 x 924 [3,1,7]
+ CRUSH rule 2 x 925 [5,7,2]
+ CRUSH rule 2 x 926 [3,0,8]
+ CRUSH rule 2 x 927 [1,6,3]
+ CRUSH rule 2 x 928 [8,1,3]
+ CRUSH rule 2 x 929 [4,1,7]
+ CRUSH rule 2 x 930 [2,4,6]
+ CRUSH rule 2 x 931 [5,0,7]
+ CRUSH rule 2 x 932 [4,1,8]
+ CRUSH rule 2 x 933 [8,5,0]
+ CRUSH rule 2 x 934 [5,6,0]
+ CRUSH rule 2 x 935 [6,3,1]
+ CRUSH rule 2 x 936 [0,6,5]
+ CRUSH rule 2 x 937 [5,8,2]
+ CRUSH rule 2 x 938 [6,5,2]
+ CRUSH rule 2 x 939 [2,7,5]
+ CRUSH rule 2 x 940 [8,5,0]
+ CRUSH rule 2 x 941 [5,2,8]
+ CRUSH rule 2 x 942 [1,8,4]
+ CRUSH rule 2 x 943 [8,2,4]
+ CRUSH rule 2 x 944 [4,8,2]
+ CRUSH rule 2 x 945 [7,2,4]
+ CRUSH rule 2 x 946 [2,8,5]
+ CRUSH rule 2 x 947 [4,2,8]
+ CRUSH rule 2 x 948 [7,5,0]
+ CRUSH rule 2 x 949 [6,1,3]
+ CRUSH rule 2 x 950 [3,6,0]
+ CRUSH rule 2 x 951 [4,8,1]
+ CRUSH rule 2 x 952 [2,7,3]
+ CRUSH rule 2 x 953 [1,3,6]
+ CRUSH rule 2 x 954 [4,2,7]
+ CRUSH rule 2 x 955 [8,0,4]
+ CRUSH rule 2 x 956 [1,6,4]
+ CRUSH rule 2 x 957 [7,1,3]
+ CRUSH rule 2 x 958 [8,4,1]
+ CRUSH rule 2 x 959 [5,2,7]
+ CRUSH rule 2 x 960 [3,6,0]
+ CRUSH rule 2 x 961 [4,0,8]
+ CRUSH rule 2 x 962 [7,4,0]
+ CRUSH rule 2 x 963 [0,5,6]
+ CRUSH rule 2 x 964 [3,1,8]
+ CRUSH rule 2 x 965 [7,4,0]
+ CRUSH rule 2 x 966 [3,8,0]
+ CRUSH rule 2 x 967 [8,5,0]
+ CRUSH rule 2 x 968 [7,2,4]
+ CRUSH rule 2 x 969 [8,0,5]
+ CRUSH rule 2 x 970 [0,6,3]
+ CRUSH rule 2 x 971 [1,7,3]
+ CRUSH rule 2 x 972 [1,8,4]
+ CRUSH rule 2 x 973 [1,6,3]
+ CRUSH rule 2 x 974 [5,1,8]
+ CRUSH rule 2 x 975 [3,7,0]
+ CRUSH rule 2 x 976 [4,8,2]
+ CRUSH rule 2 x 977 [8,3,2]
+ CRUSH rule 2 x 978 [7,2,4]
+ CRUSH rule 2 x 979 [7,1,5]
+ CRUSH rule 2 x 980 [6,0,5]
+ CRUSH rule 2 x 981 [7,3,2]
+ CRUSH rule 2 x 982 [4,2,8]
+ CRUSH rule 2 x 983 [3,7,0]
+ CRUSH rule 2 x 984 [0,7,3]
+ CRUSH rule 2 x 985 [2,5,7]
+ CRUSH rule 2 x 986 [8,3,0]
+ CRUSH rule 2 x 987 [0,5,8]
+ CRUSH rule 2 x 988 [1,3,7]
+ CRUSH rule 2 x 989 [0,6,3]
+ CRUSH rule 2 x 990 [1,6,5]
+ CRUSH rule 2 x 991 [0,4,8]
+ CRUSH rule 2 x 992 [7,1,5]
+ CRUSH rule 2 x 993 [0,6,3]
+ CRUSH rule 2 x 994 [3,7,2]
+ CRUSH rule 2 x 995 [7,1,5]
+ CRUSH rule 2 x 996 [6,5,0]
+ CRUSH rule 2 x 997 [6,4,1]
+ CRUSH rule 2 x 998 [8,1,5]
+ CRUSH rule 2 x 999 [0,7,4]
+ CRUSH rule 2 x 1000 [8,5,0]
+ CRUSH rule 2 x 1001 [2,5,6]
+ CRUSH rule 2 x 1002 [1,3,7]
+ CRUSH rule 2 x 1003 [2,8,3]
+ CRUSH rule 2 x 1004 [6,1,3]
+ CRUSH rule 2 x 1005 [6,1,5]
+ CRUSH rule 2 x 1006 [1,6,5]
+ CRUSH rule 2 x 1007 [1,5,7]
+ CRUSH rule 2 x 1008 [1,7,3]
+ CRUSH rule 2 x 1009 [6,4,1]
+ CRUSH rule 2 x 1010 [3,1,7]
+ CRUSH rule 2 x 1011 [3,0,8]
+ CRUSH rule 2 x 1012 [3,0,7]
+ CRUSH rule 2 x 1013 [5,1,7]
+ CRUSH rule 2 x 1014 [2,8,4]
+ CRUSH rule 2 x 1015 [6,5,0]
+ CRUSH rule 2 x 1016 [2,4,7]
+ CRUSH rule 2 x 1017 [6,0,3]
+ CRUSH rule 2 x 1018 [5,0,6]
+ CRUSH rule 2 x 1019 [5,8,2]
+ CRUSH rule 2 x 1020 [5,1,7]
+ CRUSH rule 2 x 1021 [5,2,6]
+ CRUSH rule 2 x 1022 [1,6,4]
+ CRUSH rule 2 x 1023 [3,2,8]
+ rule 2 (chooseleaf) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 3 (choose-set), x = 0..1023, numrep = 2..3
+ CRUSH rule 3 x 0 [0,3]
+ CRUSH rule 3 x 1 [0,8]
+ CRUSH rule 3 x 2 [1,4]
+ CRUSH rule 3 x 3 [8,0]
+ CRUSH rule 3 x 4 [5,1]
+ CRUSH rule 3 x 5 [7,0]
+ CRUSH rule 3 x 6 [2,6]
+ CRUSH rule 3 x 7 [5,6]
+ CRUSH rule 3 x 8 [5,7]
+ CRUSH rule 3 x 9 [2,4]
+ CRUSH rule 3 x 10 [0,8]
+ CRUSH rule 3 x 11 [0,6]
+ CRUSH rule 3 x 12 [0,3]
+ CRUSH rule 3 x 13 [3,8]
+ CRUSH rule 3 x 14 [7,1]
+ CRUSH rule 3 x 15 [7,1]
+ CRUSH rule 3 x 16 [3,7]
+ CRUSH rule 3 x 17 [5,1]
+ CRUSH rule 3 x 18 [1,3]
+ CRUSH rule 3 x 19 [7,5]
+ CRUSH rule 3 x 20 [2,4]
+ CRUSH rule 3 x 21 [3,6]
+ CRUSH rule 3 x 22 [8,5]
+ CRUSH rule 3 x 23 [3,7]
+ CRUSH rule 3 x 24 [1,6]
+ CRUSH rule 3 x 25 [3,8]
+ CRUSH rule 3 x 26 [2,7]
+ CRUSH rule 3 x 27 [3,1]
+ CRUSH rule 3 x 28 [6,2]
+ CRUSH rule 3 x 29 [8,5]
+ CRUSH rule 3 x 30 [5,6]
+ CRUSH rule 3 x 31 [8,2]
+ CRUSH rule 3 x 32 [3,7]
+ CRUSH rule 3 x 33 [2,7]
+ CRUSH rule 3 x 34 [2,3]
+ CRUSH rule 3 x 35 [0,6]
+ CRUSH rule 3 x 36 [3,6]
+ CRUSH rule 3 x 37 [0,4]
+ CRUSH rule 3 x 38 [4,6]
+ CRUSH rule 3 x 39 [3,7]
+ CRUSH rule 3 x 40 [7,2]
+ CRUSH rule 3 x 41 [0,7]
+ CRUSH rule 3 x 42 [4,7]
+ CRUSH rule 3 x 43 [0,3]
+ CRUSH rule 3 x 44 [1,8]
+ CRUSH rule 3 x 45 [8,2]
+ CRUSH rule 3 x 46 [2,4]
+ CRUSH rule 3 x 47 [4,1]
+ CRUSH rule 3 x 48 [4,6]
+ CRUSH rule 3 x 49 [5,6]
+ CRUSH rule 3 x 50 [3,1]
+ CRUSH rule 3 x 51 [3,6]
+ CRUSH rule 3 x 52 [8,2]
+ CRUSH rule 3 x 53 [3,6]
+ CRUSH rule 3 x 54 [7,4]
+ CRUSH rule 3 x 55 [8,0]
+ CRUSH rule 3 x 56 [6,5]
+ CRUSH rule 3 x 57 [5,8]
+ CRUSH rule 3 x 58 [1,8]
+ CRUSH rule 3 x 59 [4,0]
+ CRUSH rule 3 x 60 [3,1]
+ CRUSH rule 3 x 61 [4,8]
+ CRUSH rule 3 x 62 [7,0]
+ CRUSH rule 3 x 63 [5,6]
+ CRUSH rule 3 x 64 [4,1]
+ CRUSH rule 3 x 65 [7,4]
+ CRUSH rule 3 x 66 [5,6]
+ CRUSH rule 3 x 67 [5,0]
+ CRUSH rule 3 x 68 [0,3]
+ CRUSH rule 3 x 69 [5,2]
+ CRUSH rule 3 x 70 [7,2]
+ CRUSH rule 3 x 71 [2,7]
+ CRUSH rule 3 x 72 [6,2]
+ CRUSH rule 3 x 73 [2,7]
+ CRUSH rule 3 x 74 [0,8]
+ CRUSH rule 3 x 75 [3,0]
+ CRUSH rule 3 x 76 [5,2]
+ CRUSH rule 3 x 77 [7,0]
+ CRUSH rule 3 x 78 [1,4]
+ CRUSH rule 3 x 79 [5,0]
+ CRUSH rule 3 x 80 [0,4]
+ CRUSH rule 3 x 81 [0,5]
+ CRUSH rule 3 x 82 [7,2]
+ CRUSH rule 3 x 83 [2,6]
+ CRUSH rule 3 x 84 [7,0]
+ CRUSH rule 3 x 85 [3,7]
+ CRUSH rule 3 x 86 [0,6]
+ CRUSH rule 3 x 87 [0,6]
+ CRUSH rule 3 x 88 [1,8]
+ CRUSH rule 3 x 89 [3,1]
+ CRUSH rule 3 x 90 [6,3]
+ CRUSH rule 3 x 91 [3,6]
+ CRUSH rule 3 x 92 [1,6]
+ CRUSH rule 3 x 93 [7,4]
+ CRUSH rule 3 x 94 [0,3]
+ CRUSH rule 3 x 95 [7,3]
+ CRUSH rule 3 x 96 [3,8]
+ CRUSH rule 3 x 97 [8,4]
+ CRUSH rule 3 x 98 [2,7]
+ CRUSH rule 3 x 99 [0,8]
+ CRUSH rule 3 x 100 [1,6]
+ CRUSH rule 3 x 101 [3,8]
+ CRUSH rule 3 x 102 [4,0]
+ CRUSH rule 3 x 103 [4,7]
+ CRUSH rule 3 x 104 [7,4]
+ CRUSH rule 3 x 105 [2,3]
+ CRUSH rule 3 x 106 [1,6]
+ CRUSH rule 3 x 107 [3,1]
+ CRUSH rule 3 x 108 [7,1]
+ CRUSH rule 3 x 109 [1,3]
+ CRUSH rule 3 x 110 [3,2]
+ CRUSH rule 3 x 111 [2,4]
+ CRUSH rule 3 x 112 [2,6]
+ CRUSH rule 3 x 113 [6,1]
+ CRUSH rule 3 x 114 [7,5]
+ CRUSH rule 3 x 115 [8,2]
+ CRUSH rule 3 x 116 [1,7]
+ CRUSH rule 3 x 117 [7,5]
+ CRUSH rule 3 x 118 [0,3]
+ CRUSH rule 3 x 119 [5,7]
+ CRUSH rule 3 x 120 [0,5]
+ CRUSH rule 3 x 121 [2,8]
+ CRUSH rule 3 x 122 [8,4]
+ CRUSH rule 3 x 123 [2,4]
+ CRUSH rule 3 x 124 [3,0]
+ CRUSH rule 3 x 125 [0,7]
+ CRUSH rule 3 x 126 [4,1]
+ CRUSH rule 3 x 127 [3,6]
+ CRUSH rule 3 x 128 [3,8]
+ CRUSH rule 3 x 129 [0,4]
+ CRUSH rule 3 x 130 [3,8]
+ CRUSH rule 3 x 131 [1,5]
+ CRUSH rule 3 x 132 [1,3]
+ CRUSH rule 3 x 133 [3,6]
+ CRUSH rule 3 x 134 [1,7]
+ CRUSH rule 3 x 135 [5,7]
+ CRUSH rule 3 x 136 [2,3]
+ CRUSH rule 3 x 137 [7,3]
+ CRUSH rule 3 x 138 [8,3]
+ CRUSH rule 3 x 139 [3,0]
+ CRUSH rule 3 x 140 [1,8]
+ CRUSH rule 3 x 141 [6,0]
+ CRUSH rule 3 x 142 [3,2]
+ CRUSH rule 3 x 143 [5,7]
+ CRUSH rule 3 x 144 [8,2]
+ CRUSH rule 3 x 145 [8,4]
+ CRUSH rule 3 x 146 [2,8]
+ CRUSH rule 3 x 147 [2,6]
+ CRUSH rule 3 x 148 [3,0]
+ CRUSH rule 3 x 149 [4,6]
+ CRUSH rule 3 x 150 [1,8]
+ CRUSH rule 3 x 151 [3,8]
+ CRUSH rule 3 x 152 [8,3]
+ CRUSH rule 3 x 153 [8,3]
+ CRUSH rule 3 x 154 [3,0]
+ CRUSH rule 3 x 155 [3,6]
+ CRUSH rule 3 x 156 [4,2]
+ CRUSH rule 3 x 157 [4,1]
+ CRUSH rule 3 x 158 [2,6]
+ CRUSH rule 3 x 159 [7,0]
+ CRUSH rule 3 x 160 [2,8]
+ CRUSH rule 3 x 161 [1,3]
+ CRUSH rule 3 x 162 [0,8]
+ CRUSH rule 3 x 163 [5,8]
+ CRUSH rule 3 x 164 [7,2]
+ CRUSH rule 3 x 165 [7,2]
+ CRUSH rule 3 x 166 [2,3]
+ CRUSH rule 3 x 167 [0,6]
+ CRUSH rule 3 x 168 [4,0]
+ CRUSH rule 3 x 169 [2,7]
+ CRUSH rule 3 x 170 [1,3]
+ CRUSH rule 3 x 171 [7,3]
+ CRUSH rule 3 x 172 [0,8]
+ CRUSH rule 3 x 173 [8,5]
+ CRUSH rule 3 x 174 [1,4]
+ CRUSH rule 3 x 175 [6,0]
+ CRUSH rule 3 x 176 [4,2]
+ CRUSH rule 3 x 177 [5,1]
+ CRUSH rule 3 x 178 [3,0]
+ CRUSH rule 3 x 179 [4,1]
+ CRUSH rule 3 x 180 [3,7]
+ CRUSH rule 3 x 181 [6,0]
+ CRUSH rule 3 x 182 [8,3]
+ CRUSH rule 3 x 183 [7,5]
+ CRUSH rule 3 x 184 [5,6]
+ CRUSH rule 3 x 185 [6,0]
+ CRUSH rule 3 x 186 [2,4]
+ CRUSH rule 3 x 187 [1,6]
+ CRUSH rule 3 x 188 [1,6]
+ CRUSH rule 3 x 189 [0,6]
+ CRUSH rule 3 x 190 [4,1]
+ CRUSH rule 3 x 191 [7,0]
+ CRUSH rule 3 x 192 [5,2]
+ CRUSH rule 3 x 193 [4,0]
+ CRUSH rule 3 x 194 [1,3]
+ CRUSH rule 3 x 195 [6,5]
+ CRUSH rule 3 x 196 [6,1]
+ CRUSH rule 3 x 197 [6,5]
+ CRUSH rule 3 x 198 [2,3]
+ CRUSH rule 3 x 199 [0,5]
+ CRUSH rule 3 x 200 [0,3]
+ CRUSH rule 3 x 201 [7,1]
+ CRUSH rule 3 x 202 [6,4]
+ CRUSH rule 3 x 203 [4,6]
+ CRUSH rule 3 x 204 [2,4]
+ CRUSH rule 3 x 205 [0,7]
+ CRUSH rule 3 x 206 [0,8]
+ CRUSH rule 3 x 207 [3,2]
+ CRUSH rule 3 x 208 [7,2]
+ CRUSH rule 3 x 209 [1,8]
+ CRUSH rule 3 x 210 [1,4]
+ CRUSH rule 3 x 211 [5,2]
+ CRUSH rule 3 x 212 [7,5]
+ CRUSH rule 3 x 213 [8,4]
+ CRUSH rule 3 x 214 [4,7]
+ CRUSH rule 3 x 215 [8,0]
+ CRUSH rule 3 x 216 [5,2]
+ CRUSH rule 3 x 217 [0,7]
+ CRUSH rule 3 x 218 [0,7]
+ CRUSH rule 3 x 219 [4,6]
+ CRUSH rule 3 x 220 [5,8]
+ CRUSH rule 3 x 221 [3,7]
+ CRUSH rule 3 x 222 [6,4]
+ CRUSH rule 3 x 223 [1,3]
+ CRUSH rule 3 x 224 [1,3]
+ CRUSH rule 3 x 225 [8,0]
+ CRUSH rule 3 x 226 [7,0]
+ CRUSH rule 3 x 227 [3,0]
+ CRUSH rule 3 x 228 [5,6]
+ CRUSH rule 3 x 229 [3,7]
+ CRUSH rule 3 x 230 [4,6]
+ CRUSH rule 3 x 231 [4,7]
+ CRUSH rule 3 x 232 [2,8]
+ CRUSH rule 3 x 233 [3,6]
+ CRUSH rule 3 x 234 [0,4]
+ CRUSH rule 3 x 235 [3,6]
+ CRUSH rule 3 x 236 [5,0]
+ CRUSH rule 3 x 237 [4,8]
+ CRUSH rule 3 x 238 [4,2]
+ CRUSH rule 3 x 239 [8,3]
+ CRUSH rule 3 x 240 [5,8]
+ CRUSH rule 3 x 241 [3,1]
+ CRUSH rule 3 x 242 [3,2]
+ CRUSH rule 3 x 243 [4,8]
+ CRUSH rule 3 x 244 [4,6]
+ CRUSH rule 3 x 245 [7,2]
+ CRUSH rule 3 x 246 [1,3]
+ CRUSH rule 3 x 247 [6,1]
+ CRUSH rule 3 x 248 [8,0]
+ CRUSH rule 3 x 249 [2,5]
+ CRUSH rule 3 x 250 [2,3]
+ CRUSH rule 3 x 251 [2,3]
+ CRUSH rule 3 x 252 [3,7]
+ CRUSH rule 3 x 253 [3,0]
+ CRUSH rule 3 x 254 [3,2]
+ CRUSH rule 3 x 255 [1,7]
+ CRUSH rule 3 x 256 [5,6]
+ CRUSH rule 3 x 257 [2,6]
+ CRUSH rule 3 x 258 [5,2]
+ CRUSH rule 3 x 259 [4,6]
+ CRUSH rule 3 x 260 [3,8]
+ CRUSH rule 3 x 261 [8,5]
+ CRUSH rule 3 x 262 [5,6]
+ CRUSH rule 3 x 263 [6,1]
+ CRUSH rule 3 x 264 [3,6]
+ CRUSH rule 3 x 265 [8,4]
+ CRUSH rule 3 x 266 [8,0]
+ CRUSH rule 3 x 267 [2,4]
+ CRUSH rule 3 x 268 [0,6]
+ CRUSH rule 3 x 269 [0,6]
+ CRUSH rule 3 x 270 [5,1]
+ CRUSH rule 3 x 271 [7,4]
+ CRUSH rule 3 x 272 [2,6]
+ CRUSH rule 3 x 273 [3,2]
+ CRUSH rule 3 x 274 [6,3]
+ CRUSH rule 3 x 275 [4,8]
+ CRUSH rule 3 x 276 [7,0]
+ CRUSH rule 3 x 277 [6,4]
+ CRUSH rule 3 x 278 [6,2]
+ CRUSH rule 3 x 279 [8,5]
+ CRUSH rule 3 x 280 [0,7]
+ CRUSH rule 3 x 281 [8,1]
+ CRUSH rule 3 x 282 [3,2]
+ CRUSH rule 3 x 283 [8,1]
+ CRUSH rule 3 x 284 [6,3]
+ CRUSH rule 3 x 285 [5,7]
+ CRUSH rule 3 x 286 [2,8]
+ CRUSH rule 3 x 287 [0,3]
+ CRUSH rule 3 x 288 [8,1]
+ CRUSH rule 3 x 289 [4,6]
+ CRUSH rule 3 x 290 [1,5]
+ CRUSH rule 3 x 291 [0,5]
+ CRUSH rule 3 x 292 [8,1]
+ CRUSH rule 3 x 293 [6,2]
+ CRUSH rule 3 x 294 [7,5]
+ CRUSH rule 3 x 295 [4,6]
+ CRUSH rule 3 x 296 [3,1]
+ CRUSH rule 3 x 297 [6,0]
+ CRUSH rule 3 x 298 [1,5]
+ CRUSH rule 3 x 299 [2,8]
+ CRUSH rule 3 x 300 [8,4]
+ CRUSH rule 3 x 301 [0,7]
+ CRUSH rule 3 x 302 [3,1]
+ CRUSH rule 3 x 303 [7,4]
+ CRUSH rule 3 x 304 [2,6]
+ CRUSH rule 3 x 305 [5,6]
+ CRUSH rule 3 x 306 [0,8]
+ CRUSH rule 3 x 307 [0,7]
+ CRUSH rule 3 x 308 [0,8]
+ CRUSH rule 3 x 309 [7,4]
+ CRUSH rule 3 x 310 [4,0]
+ CRUSH rule 3 x 311 [3,8]
+ CRUSH rule 3 x 312 [2,6]
+ CRUSH rule 3 x 313 [5,1]
+ CRUSH rule 3 x 314 [4,0]
+ CRUSH rule 3 x 315 [2,3]
+ CRUSH rule 3 x 316 [6,4]
+ CRUSH rule 3 x 317 [2,7]
+ CRUSH rule 3 x 318 [8,1]
+ CRUSH rule 3 x 319 [5,2]
+ CRUSH rule 3 x 320 [3,8]
+ CRUSH rule 3 x 321 [1,5]
+ CRUSH rule 3 x 322 [2,6]
+ CRUSH rule 3 x 323 [4,7]
+ CRUSH rule 3 x 324 [7,2]
+ CRUSH rule 3 x 325 [4,6]
+ CRUSH rule 3 x 326 [3,1]
+ CRUSH rule 3 x 327 [0,8]
+ CRUSH rule 3 x 328 [7,3]
+ CRUSH rule 3 x 329 [5,7]
+ CRUSH rule 3 x 330 [3,7]
+ CRUSH rule 3 x 331 [2,7]
+ CRUSH rule 3 x 332 [2,3]
+ CRUSH rule 3 x 333 [6,4]
+ CRUSH rule 3 x 334 [8,3]
+ CRUSH rule 3 x 335 [7,2]
+ CRUSH rule 3 x 336 [4,6]
+ CRUSH rule 3 x 337 [7,0]
+ CRUSH rule 3 x 338 [5,8]
+ CRUSH rule 3 x 339 [7,5]
+ CRUSH rule 3 x 340 [2,6]
+ CRUSH rule 3 x 341 [5,1]
+ CRUSH rule 3 x 342 [0,8]
+ CRUSH rule 3 x 343 [6,5]
+ CRUSH rule 3 x 344 [6,1]
+ CRUSH rule 3 x 345 [4,7]
+ CRUSH rule 3 x 346 [8,0]
+ CRUSH rule 3 x 347 [3,0]
+ CRUSH rule 3 x 348 [8,0]
+ CRUSH rule 3 x 349 [1,7]
+ CRUSH rule 3 x 350 [8,5]
+ CRUSH rule 3 x 351 [3,8]
+ CRUSH rule 3 x 352 [1,8]
+ CRUSH rule 3 x 353 [6,5]
+ CRUSH rule 3 x 354 [0,5]
+ CRUSH rule 3 x 355 [3,8]
+ CRUSH rule 3 x 356 [3,1]
+ CRUSH rule 3 x 357 [6,1]
+ CRUSH rule 3 x 358 [2,8]
+ CRUSH rule 3 x 359 [6,0]
+ CRUSH rule 3 x 360 [5,0]
+ CRUSH rule 3 x 361 [8,5]
+ CRUSH rule 3 x 362 [4,0]
+ CRUSH rule 3 x 363 [4,2]
+ CRUSH rule 3 x 364 [2,5]
+ CRUSH rule 3 x 365 [6,4]
+ CRUSH rule 3 x 366 [7,0]
+ CRUSH rule 3 x 367 [4,2]
+ CRUSH rule 3 x 368 [7,3]
+ CRUSH rule 3 x 369 [3,7]
+ CRUSH rule 3 x 370 [8,2]
+ CRUSH rule 3 x 371 [1,5]
+ CRUSH rule 3 x 372 [3,1]
+ CRUSH rule 3 x 373 [0,6]
+ CRUSH rule 3 x 374 [3,8]
+ CRUSH rule 3 x 375 [6,5]
+ CRUSH rule 3 x 376 [7,1]
+ CRUSH rule 3 x 377 [1,4]
+ CRUSH rule 3 x 378 [0,6]
+ CRUSH rule 3 x 379 [8,3]
+ CRUSH rule 3 x 380 [2,3]
+ CRUSH rule 3 x 381 [0,3]
+ CRUSH rule 3 x 382 [1,3]
+ CRUSH rule 3 x 383 [4,7]
+ CRUSH rule 3 x 384 [7,0]
+ CRUSH rule 3 x 385 [7,4]
+ CRUSH rule 3 x 386 [0,4]
+ CRUSH rule 3 x 387 [1,5]
+ CRUSH rule 3 x 388 [5,2]
+ CRUSH rule 3 x 389 [1,4]
+ CRUSH rule 3 x 390 [5,8]
+ CRUSH rule 3 x 391 [5,6]
+ CRUSH rule 3 x 392 [1,7]
+ CRUSH rule 3 x 393 [4,0]
+ CRUSH rule 3 x 394 [4,8]
+ CRUSH rule 3 x 395 [4,0]
+ CRUSH rule 3 x 396 [4,2]
+ CRUSH rule 3 x 397 [2,4]
+ CRUSH rule 3 x 398 [2,3]
+ CRUSH rule 3 x 399 [8,5]
+ CRUSH rule 3 x 400 [8,1]
+ CRUSH rule 3 x 401 [0,3]
+ CRUSH rule 3 x 402 [7,4]
+ CRUSH rule 3 x 403 [0,4]
+ CRUSH rule 3 x 404 [4,0]
+ CRUSH rule 3 x 405 [6,4]
+ CRUSH rule 3 x 406 [2,6]
+ CRUSH rule 3 x 407 [2,7]
+ CRUSH rule 3 x 408 [4,1]
+ CRUSH rule 3 x 409 [7,5]
+ CRUSH rule 3 x 410 [8,3]
+ CRUSH rule 3 x 411 [2,7]
+ CRUSH rule 3 x 412 [0,3]
+ CRUSH rule 3 x 413 [5,2]
+ CRUSH rule 3 x 414 [4,0]
+ CRUSH rule 3 x 415 [0,6]
+ CRUSH rule 3 x 416 [2,8]
+ CRUSH rule 3 x 417 [8,0]
+ CRUSH rule 3 x 418 [7,1]
+ CRUSH rule 3 x 419 [8,5]
+ CRUSH rule 3 x 420 [1,4]
+ CRUSH rule 3 x 421 [8,3]
+ CRUSH rule 3 x 422 [6,3]
+ CRUSH rule 3 x 423 [0,3]
+ CRUSH rule 3 x 424 [8,5]
+ CRUSH rule 3 x 425 [1,4]
+ CRUSH rule 3 x 426 [6,2]
+ CRUSH rule 3 x 427 [0,8]
+ CRUSH rule 3 x 428 [5,8]
+ CRUSH rule 3 x 429 [4,8]
+ CRUSH rule 3 x 430 [3,7]
+ CRUSH rule 3 x 431 [5,0]
+ CRUSH rule 3 x 432 [7,0]
+ CRUSH rule 3 x 433 [6,4]
+ CRUSH rule 3 x 434 [5,0]
+ CRUSH rule 3 x 435 [0,4]
+ CRUSH rule 3 x 436 [4,0]
+ CRUSH rule 3 x 437 [7,3]
+ CRUSH rule 3 x 438 [0,5]
+ CRUSH rule 3 x 439 [1,3]
+ CRUSH rule 3 x 440 [2,6]
+ CRUSH rule 3 x 441 [5,8]
+ CRUSH rule 3 x 442 [2,3]
+ CRUSH rule 3 x 443 [6,0]
+ CRUSH rule 3 x 444 [7,1]
+ CRUSH rule 3 x 445 [6,4]
+ CRUSH rule 3 x 446 [4,0]
+ CRUSH rule 3 x 447 [2,4]
+ CRUSH rule 3 x 448 [7,0]
+ CRUSH rule 3 x 449 [7,4]
+ CRUSH rule 3 x 450 [4,0]
+ CRUSH rule 3 x 451 [6,5]
+ CRUSH rule 3 x 452 [8,4]
+ CRUSH rule 3 x 453 [6,5]
+ CRUSH rule 3 x 454 [6,5]
+ CRUSH rule 3 x 455 [2,8]
+ CRUSH rule 3 x 456 [6,2]
+ CRUSH rule 3 x 457 [7,1]
+ CRUSH rule 3 x 458 [2,8]
+ CRUSH rule 3 x 459 [2,6]
+ CRUSH rule 3 x 460 [6,5]
+ CRUSH rule 3 x 461 [6,4]
+ CRUSH rule 3 x 462 [8,0]
+ CRUSH rule 3 x 463 [6,0]
+ CRUSH rule 3 x 464 [7,4]
+ CRUSH rule 3 x 465 [7,1]
+ CRUSH rule 3 x 466 [5,6]
+ CRUSH rule 3 x 467 [6,5]
+ CRUSH rule 3 x 468 [7,2]
+ CRUSH rule 3 x 469 [7,2]
+ CRUSH rule 3 x 470 [3,0]
+ CRUSH rule 3 x 471 [0,6]
+ CRUSH rule 3 x 472 [5,0]
+ CRUSH rule 3 x 473 [1,4]
+ CRUSH rule 3 x 474 [6,1]
+ CRUSH rule 3 x 475 [6,2]
+ CRUSH rule 3 x 476 [4,7]
+ CRUSH rule 3 x 477 [5,6]
+ CRUSH rule 3 x 478 [6,1]
+ CRUSH rule 3 x 479 [0,3]
+ CRUSH rule 3 x 480 [1,6]
+ CRUSH rule 3 x 481 [2,5]
+ CRUSH rule 3 x 482 [4,8]
+ CRUSH rule 3 x 483 [0,6]
+ CRUSH rule 3 x 484 [1,8]
+ CRUSH rule 3 x 485 [4,8]
+ CRUSH rule 3 x 486 [4,0]
+ CRUSH rule 3 x 487 [5,0]
+ CRUSH rule 3 x 488 [5,7]
+ CRUSH rule 3 x 489 [2,8]
+ CRUSH rule 3 x 490 [6,4]
+ CRUSH rule 3 x 491 [1,6]
+ CRUSH rule 3 x 492 [6,5]
+ CRUSH rule 3 x 493 [0,8]
+ CRUSH rule 3 x 494 [1,6]
+ CRUSH rule 3 x 495 [3,0]
+ CRUSH rule 3 x 496 [7,5]
+ CRUSH rule 3 x 497 [5,7]
+ CRUSH rule 3 x 498 [0,4]
+ CRUSH rule 3 x 499 [8,5]
+ CRUSH rule 3 x 500 [3,6]
+ CRUSH rule 3 x 501 [0,7]
+ CRUSH rule 3 x 502 [7,1]
+ CRUSH rule 3 x 503 [2,3]
+ CRUSH rule 3 x 504 [5,8]
+ CRUSH rule 3 x 505 [0,7]
+ CRUSH rule 3 x 506 [5,1]
+ CRUSH rule 3 x 507 [6,0]
+ CRUSH rule 3 x 508 [0,4]
+ CRUSH rule 3 x 509 [7,4]
+ CRUSH rule 3 x 510 [6,2]
+ CRUSH rule 3 x 511 [5,6]
+ CRUSH rule 3 x 512 [7,2]
+ CRUSH rule 3 x 513 [7,2]
+ CRUSH rule 3 x 514 [4,7]
+ CRUSH rule 3 x 515 [8,3]
+ CRUSH rule 3 x 516 [4,1]
+ CRUSH rule 3 x 517 [7,0]
+ CRUSH rule 3 x 518 [4,6]
+ CRUSH rule 3 x 519 [7,3]
+ CRUSH rule 3 x 520 [2,8]
+ CRUSH rule 3 x 521 [8,0]
+ CRUSH rule 3 x 522 [6,0]
+ CRUSH rule 3 x 523 [4,1]
+ CRUSH rule 3 x 524 [0,4]
+ CRUSH rule 3 x 525 [0,3]
+ CRUSH rule 3 x 526 [1,3]
+ CRUSH rule 3 x 527 [0,4]
+ CRUSH rule 3 x 528 [5,2]
+ CRUSH rule 3 x 529 [5,6]
+ CRUSH rule 3 x 530 [6,4]
+ CRUSH rule 3 x 531 [6,0]
+ CRUSH rule 3 x 532 [6,5]
+ CRUSH rule 3 x 533 [5,8]
+ CRUSH rule 3 x 534 [7,4]
+ CRUSH rule 3 x 535 [8,0]
+ CRUSH rule 3 x 536 [6,2]
+ CRUSH rule 3 x 537 [3,8]
+ CRUSH rule 3 x 538 [6,4]
+ CRUSH rule 3 x 539 [8,4]
+ CRUSH rule 3 x 540 [0,7]
+ CRUSH rule 3 x 541 [2,5]
+ CRUSH rule 3 x 542 [3,0]
+ CRUSH rule 3 x 543 [6,2]
+ CRUSH rule 3 x 544 [3,7]
+ CRUSH rule 3 x 545 [5,7]
+ CRUSH rule 3 x 546 [6,2]
+ CRUSH rule 3 x 547 [8,1]
+ CRUSH rule 3 x 548 [5,1]
+ CRUSH rule 3 x 549 [5,7]
+ CRUSH rule 3 x 550 [0,5]
+ CRUSH rule 3 x 551 [7,4]
+ CRUSH rule 3 x 552 [5,7]
+ CRUSH rule 3 x 553 [4,2]
+ CRUSH rule 3 x 554 [0,6]
+ CRUSH rule 3 x 555 [5,0]
+ CRUSH rule 3 x 556 [3,6]
+ CRUSH rule 3 x 557 [7,4]
+ CRUSH rule 3 x 558 [3,2]
+ CRUSH rule 3 x 559 [4,1]
+ CRUSH rule 3 x 560 [8,4]
+ CRUSH rule 3 x 561 [6,4]
+ CRUSH rule 3 x 562 [3,0]
+ CRUSH rule 3 x 563 [2,7]
+ CRUSH rule 3 x 564 [5,1]
+ CRUSH rule 3 x 565 [3,8]
+ CRUSH rule 3 x 566 [4,6]
+ CRUSH rule 3 x 567 [3,7]
+ CRUSH rule 3 x 568 [7,4]
+ CRUSH rule 3 x 569 [3,1]
+ CRUSH rule 3 x 570 [1,4]
+ CRUSH rule 3 x 571 [3,6]
+ CRUSH rule 3 x 572 [3,0]
+ CRUSH rule 3 x 573 [3,1]
+ CRUSH rule 3 x 574 [2,3]
+ CRUSH rule 3 x 575 [8,1]
+ CRUSH rule 3 x 576 [4,7]
+ CRUSH rule 3 x 577 [8,2]
+ CRUSH rule 3 x 578 [6,0]
+ CRUSH rule 3 x 579 [3,2]
+ CRUSH rule 3 x 580 [3,0]
+ CRUSH rule 3 x 581 [7,1]
+ CRUSH rule 3 x 582 [2,8]
+ CRUSH rule 3 x 583 [6,2]
+ CRUSH rule 3 x 584 [8,0]
+ CRUSH rule 3 x 585 [7,0]
+ CRUSH rule 3 x 586 [0,8]
+ CRUSH rule 3 x 587 [2,5]
+ CRUSH rule 3 x 588 [3,8]
+ CRUSH rule 3 x 589 [7,1]
+ CRUSH rule 3 x 590 [6,2]
+ CRUSH rule 3 x 591 [5,2]
+ CRUSH rule 3 x 592 [2,4]
+ CRUSH rule 3 x 593 [0,8]
+ CRUSH rule 3 x 594 [0,6]
+ CRUSH rule 3 x 595 [7,1]
+ CRUSH rule 3 x 596 [4,0]
+ CRUSH rule 3 x 597 [3,1]
+ CRUSH rule 3 x 598 [3,0]
+ CRUSH rule 3 x 599 [5,1]
+ CRUSH rule 3 x 600 [7,0]
+ CRUSH rule 3 x 601 [0,6]
+ CRUSH rule 3 x 602 [3,8]
+ CRUSH rule 3 x 603 [5,0]
+ CRUSH rule 3 x 604 [7,4]
+ CRUSH rule 3 x 605 [3,2]
+ CRUSH rule 3 x 606 [2,7]
+ CRUSH rule 3 x 607 [0,5]
+ CRUSH rule 3 x 608 [5,1]
+ CRUSH rule 3 x 609 [5,0]
+ CRUSH rule 3 x 610 [3,8]
+ CRUSH rule 3 x 611 [1,8]
+ CRUSH rule 3 x 612 [2,8]
+ CRUSH rule 3 x 613 [7,1]
+ CRUSH rule 3 x 614 [7,2]
+ CRUSH rule 3 x 615 [6,2]
+ CRUSH rule 3 x 616 [0,7]
+ CRUSH rule 3 x 617 [6,2]
+ CRUSH rule 3 x 618 [7,3]
+ CRUSH rule 3 x 619 [5,0]
+ CRUSH rule 3 x 620 [4,1]
+ CRUSH rule 3 x 621 [5,6]
+ CRUSH rule 3 x 622 [0,3]
+ CRUSH rule 3 x 623 [0,8]
+ CRUSH rule 3 x 624 [3,2]
+ CRUSH rule 3 x 625 [2,5]
+ CRUSH rule 3 x 626 [7,2]
+ CRUSH rule 3 x 627 [2,6]
+ CRUSH rule 3 x 628 [8,1]
+ CRUSH rule 3 x 629 [2,6]
+ CRUSH rule 3 x 630 [2,6]
+ CRUSH rule 3 x 631 [0,6]
+ CRUSH rule 3 x 632 [7,0]
+ CRUSH rule 3 x 633 [8,4]
+ CRUSH rule 3 x 634 [0,5]
+ CRUSH rule 3 x 635 [5,6]
+ CRUSH rule 3 x 636 [1,3]
+ CRUSH rule 3 x 637 [4,0]
+ CRUSH rule 3 x 638 [6,2]
+ CRUSH rule 3 x 639 [4,0]
+ CRUSH rule 3 x 640 [3,2]
+ CRUSH rule 3 x 641 [7,2]
+ CRUSH rule 3 x 642 [2,8]
+ CRUSH rule 3 x 643 [3,0]
+ CRUSH rule 3 x 644 [8,0]
+ CRUSH rule 3 x 645 [5,7]
+ CRUSH rule 3 x 646 [8,0]
+ CRUSH rule 3 x 647 [7,0]
+ CRUSH rule 3 x 648 [0,8]
+ CRUSH rule 3 x 649 [4,7]
+ CRUSH rule 3 x 650 [7,5]
+ CRUSH rule 3 x 651 [3,6]
+ CRUSH rule 3 x 652 [3,6]
+ CRUSH rule 3 x 653 [8,3]
+ CRUSH rule 3 x 654 [7,4]
+ CRUSH rule 3 x 655 [0,5]
+ CRUSH rule 3 x 656 [4,7]
+ CRUSH rule 3 x 657 [6,0]
+ CRUSH rule 3 x 658 [5,8]
+ CRUSH rule 3 x 659 [4,7]
+ CRUSH rule 3 x 660 [7,3]
+ CRUSH rule 3 x 661 [1,7]
+ CRUSH rule 3 x 662 [4,2]
+ CRUSH rule 3 x 663 [1,3]
+ CRUSH rule 3 x 664 [1,3]
+ CRUSH rule 3 x 665 [5,6]
+ CRUSH rule 3 x 666 [2,7]
+ CRUSH rule 3 x 667 [1,3]
+ CRUSH rule 3 x 668 [3,7]
+ CRUSH rule 3 x 669 [6,3]
+ CRUSH rule 3 x 670 [4,1]
+ CRUSH rule 3 x 671 [0,8]
+ CRUSH rule 3 x 672 [4,2]
+ CRUSH rule 3 x 673 [5,0]
+ CRUSH rule 3 x 674 [3,0]
+ CRUSH rule 3 x 675 [0,8]
+ CRUSH rule 3 x 676 [0,3]
+ CRUSH rule 3 x 677 [4,1]
+ CRUSH rule 3 x 678 [2,3]
+ CRUSH rule 3 x 679 [6,0]
+ CRUSH rule 3 x 680 [0,3]
+ CRUSH rule 3 x 681 [4,6]
+ CRUSH rule 3 x 682 [0,4]
+ CRUSH rule 3 x 683 [0,3]
+ CRUSH rule 3 x 684 [7,2]
+ CRUSH rule 3 x 685 [7,2]
+ CRUSH rule 3 x 686 [1,5]
+ CRUSH rule 3 x 687 [3,7]
+ CRUSH rule 3 x 688 [5,6]
+ CRUSH rule 3 x 689 [6,5]
+ CRUSH rule 3 x 690 [8,0]
+ CRUSH rule 3 x 691 [3,1]
+ CRUSH rule 3 x 692 [7,1]
+ CRUSH rule 3 x 693 [6,5]
+ CRUSH rule 3 x 694 [6,4]
+ CRUSH rule 3 x 695 [0,6]
+ CRUSH rule 3 x 696 [1,5]
+ CRUSH rule 3 x 697 [6,0]
+ CRUSH rule 3 x 698 [6,0]
+ CRUSH rule 3 x 699 [1,8]
+ CRUSH rule 3 x 700 [0,4]
+ CRUSH rule 3 x 701 [4,0]
+ CRUSH rule 3 x 702 [3,0]
+ CRUSH rule 3 x 703 [8,4]
+ CRUSH rule 3 x 704 [0,4]
+ CRUSH rule 3 x 705 [8,0]
+ CRUSH rule 3 x 706 [1,5]
+ CRUSH rule 3 x 707 [7,3]
+ CRUSH rule 3 x 708 [3,7]
+ CRUSH rule 3 x 709 [6,5]
+ CRUSH rule 3 x 710 [8,5]
+ CRUSH rule 3 x 711 [2,4]
+ CRUSH rule 3 x 712 [2,3]
+ CRUSH rule 3 x 713 [6,3]
+ CRUSH rule 3 x 714 [3,0]
+ CRUSH rule 3 x 715 [1,3]
+ CRUSH rule 3 x 716 [3,6]
+ CRUSH rule 3 x 717 [8,0]
+ CRUSH rule 3 x 718 [3,7]
+ CRUSH rule 3 x 719 [2,6]
+ CRUSH rule 3 x 720 [6,0]
+ CRUSH rule 3 x 721 [5,7]
+ CRUSH rule 3 x 722 [5,7]
+ CRUSH rule 3 x 723 [5,2]
+ CRUSH rule 3 x 724 [0,7]
+ CRUSH rule 3 x 725 [0,4]
+ CRUSH rule 3 x 726 [3,7]
+ CRUSH rule 3 x 727 [4,7]
+ CRUSH rule 3 x 728 [2,6]
+ CRUSH rule 3 x 729 [5,6]
+ CRUSH rule 3 x 730 [3,8]
+ CRUSH rule 3 x 731 [4,1]
+ CRUSH rule 3 x 732 [1,4]
+ CRUSH rule 3 x 733 [5,6]
+ CRUSH rule 3 x 734 [6,5]
+ CRUSH rule 3 x 735 [4,6]
+ CRUSH rule 3 x 736 [3,8]
+ CRUSH rule 3 x 737 [1,8]
+ CRUSH rule 3 x 738 [5,1]
+ CRUSH rule 3 x 739 [0,7]
+ CRUSH rule 3 x 740 [0,7]
+ CRUSH rule 3 x 741 [7,2]
+ CRUSH rule 3 x 742 [8,2]
+ CRUSH rule 3 x 743 [7,0]
+ CRUSH rule 3 x 744 [4,6]
+ CRUSH rule 3 x 745 [3,0]
+ CRUSH rule 3 x 746 [4,1]
+ CRUSH rule 3 x 747 [6,2]
+ CRUSH rule 3 x 748 [2,6]
+ CRUSH rule 3 x 749 [4,7]
+ CRUSH rule 3 x 750 [1,7]
+ CRUSH rule 3 x 751 [2,7]
+ CRUSH rule 3 x 752 [8,0]
+ CRUSH rule 3 x 753 [7,4]
+ CRUSH rule 3 x 754 [8,5]
+ CRUSH rule 3 x 755 [1,6]
+ CRUSH rule 3 x 756 [5,8]
+ CRUSH rule 3 x 757 [8,0]
+ CRUSH rule 3 x 758 [6,1]
+ CRUSH rule 3 x 759 [8,5]
+ CRUSH rule 3 x 760 [1,5]
+ CRUSH rule 3 x 761 [4,2]
+ CRUSH rule 3 x 762 [2,8]
+ CRUSH rule 3 x 763 [8,3]
+ CRUSH rule 3 x 764 [1,7]
+ CRUSH rule 3 x 765 [6,4]
+ CRUSH rule 3 x 766 [8,3]
+ CRUSH rule 3 x 767 [1,8]
+ CRUSH rule 3 x 768 [8,5]
+ CRUSH rule 3 x 769 [6,2]
+ CRUSH rule 3 x 770 [6,1]
+ CRUSH rule 3 x 771 [7,2]
+ CRUSH rule 3 x 772 [8,4]
+ CRUSH rule 3 x 773 [3,2]
+ CRUSH rule 3 x 774 [4,7]
+ CRUSH rule 3 x 775 [6,5]
+ CRUSH rule 3 x 776 [7,0]
+ CRUSH rule 3 x 777 [3,0]
+ CRUSH rule 3 x 778 [1,6]
+ CRUSH rule 3 x 779 [2,6]
+ CRUSH rule 3 x 780 [0,5]
+ CRUSH rule 3 x 781 [6,5]
+ CRUSH rule 3 x 782 [5,2]
+ CRUSH rule 3 x 783 [7,0]
+ CRUSH rule 3 x 784 [0,5]
+ CRUSH rule 3 x 785 [6,1]
+ CRUSH rule 3 x 786 [7,3]
+ CRUSH rule 3 x 787 [1,8]
+ CRUSH rule 3 x 788 [6,0]
+ CRUSH rule 3 x 789 [0,5]
+ CRUSH rule 3 x 790 [8,3]
+ CRUSH rule 3 x 791 [3,6]
+ CRUSH rule 3 x 792 [5,6]
+ CRUSH rule 3 x 793 [6,2]
+ CRUSH rule 3 x 794 [2,8]
+ CRUSH rule 3 x 795 [0,4]
+ CRUSH rule 3 x 796 [3,7]
+ CRUSH rule 3 x 797 [2,3]
+ CRUSH rule 3 x 798 [6,0]
+ CRUSH rule 3 x 799 [5,2]
+ CRUSH rule 3 x 800 [5,2]
+ CRUSH rule 3 x 801 [3,7]
+ CRUSH rule 3 x 802 [1,6]
+ CRUSH rule 3 x 803 [0,4]
+ CRUSH rule 3 x 804 [6,0]
+ CRUSH rule 3 x 805 [3,8]
+ CRUSH rule 3 x 806 [1,5]
+ CRUSH rule 3 x 807 [5,7]
+ CRUSH rule 3 x 808 [4,7]
+ CRUSH rule 3 x 809 [1,3]
+ CRUSH rule 3 x 810 [5,7]
+ CRUSH rule 3 x 811 [8,4]
+ CRUSH rule 3 x 812 [8,3]
+ CRUSH rule 3 x 813 [6,4]
+ CRUSH rule 3 x 814 [3,8]
+ CRUSH rule 3 x 815 [3,1]
+ CRUSH rule 3 x 816 [2,6]
+ CRUSH rule 3 x 817 [4,6]
+ CRUSH rule 3 x 818 [3,1]
+ CRUSH rule 3 x 819 [5,0]
+ CRUSH rule 3 x 820 [3,7]
+ CRUSH rule 3 x 821 [4,8]
+ CRUSH rule 3 x 822 [2,3]
+ CRUSH rule 3 x 823 [4,7]
+ CRUSH rule 3 x 824 [3,7]
+ CRUSH rule 3 x 825 [2,8]
+ CRUSH rule 3 x 826 [7,1]
+ CRUSH rule 3 x 827 [0,6]
+ CRUSH rule 3 x 828 [2,5]
+ CRUSH rule 3 x 829 [5,6]
+ CRUSH rule 3 x 830 [2,4]
+ CRUSH rule 3 x 831 [1,6]
+ CRUSH rule 3 x 832 [4,8]
+ CRUSH rule 3 x 833 [2,6]
+ CRUSH rule 3 x 834 [3,0]
+ CRUSH rule 3 x 835 [8,5]
+ CRUSH rule 3 x 836 [3,8]
+ CRUSH rule 3 x 837 [6,4]
+ CRUSH rule 3 x 838 [6,0]
+ CRUSH rule 3 x 839 [5,2]
+ CRUSH rule 3 x 840 [7,3]
+ CRUSH rule 3 x 841 [4,8]
+ CRUSH rule 3 x 842 [2,5]
+ CRUSH rule 3 x 843 [6,4]
+ CRUSH rule 3 x 844 [4,8]
+ CRUSH rule 3 x 845 [3,6]
+ CRUSH rule 3 x 846 [3,0]
+ CRUSH rule 3 x 847 [0,8]
+ CRUSH rule 3 x 848 [2,6]
+ CRUSH rule 3 x 849 [4,8]
+ CRUSH rule 3 x 850 [1,5]
+ CRUSH rule 3 x 851 [6,5]
+ CRUSH rule 3 x 852 [7,4]
+ CRUSH rule 3 x 853 [6,2]
+ CRUSH rule 3 x 854 [7,0]
+ CRUSH rule 3 x 855 [5,7]
+ CRUSH rule 3 x 856 [6,3]
+ CRUSH rule 3 x 857 [8,4]
+ CRUSH rule 3 x 858 [6,5]
+ CRUSH rule 3 x 859 [6,0]
+ CRUSH rule 3 x 860 [4,2]
+ CRUSH rule 3 x 861 [8,4]
+ CRUSH rule 3 x 862 [6,0]
+ CRUSH rule 3 x 863 [8,1]
+ CRUSH rule 3 x 864 [5,6]
+ CRUSH rule 3 x 865 [8,0]
+ CRUSH rule 3 x 866 [3,6]
+ CRUSH rule 3 x 867 [6,3]
+ CRUSH rule 3 x 868 [6,4]
+ CRUSH rule 3 x 869 [8,4]
+ CRUSH rule 3 x 870 [0,5]
+ CRUSH rule 3 x 871 [3,1]
+ CRUSH rule 3 x 872 [5,0]
+ CRUSH rule 3 x 873 [4,6]
+ CRUSH rule 3 x 874 [2,6]
+ CRUSH rule 3 x 875 [2,6]
+ CRUSH rule 3 x 876 [5,8]
+ CRUSH rule 3 x 877 [6,4]
+ CRUSH rule 3 x 878 [5,0]
+ CRUSH rule 3 x 879 [7,3]
+ CRUSH rule 3 x 880 [3,1]
+ CRUSH rule 3 x 881 [5,8]
+ CRUSH rule 3 x 882 [4,1]
+ CRUSH rule 3 x 883 [2,5]
+ CRUSH rule 3 x 884 [6,0]
+ CRUSH rule 3 x 885 [5,1]
+ CRUSH rule 3 x 886 [3,7]
+ CRUSH rule 3 x 887 [7,5]
+ CRUSH rule 3 x 888 [6,1]
+ CRUSH rule 3 x 889 [2,8]
+ CRUSH rule 3 x 890 [7,1]
+ CRUSH rule 3 x 891 [1,7]
+ CRUSH rule 3 x 892 [6,0]
+ CRUSH rule 3 x 893 [2,3]
+ CRUSH rule 3 x 894 [7,4]
+ CRUSH rule 3 x 895 [5,1]
+ CRUSH rule 3 x 896 [1,6]
+ CRUSH rule 3 x 897 [4,0]
+ CRUSH rule 3 x 898 [0,3]
+ CRUSH rule 3 x 899 [1,6]
+ CRUSH rule 3 x 900 [4,0]
+ CRUSH rule 3 x 901 [5,2]
+ CRUSH rule 3 x 902 [8,4]
+ CRUSH rule 3 x 903 [5,6]
+ CRUSH rule 3 x 904 [5,7]
+ CRUSH rule 3 x 905 [6,0]
+ CRUSH rule 3 x 906 [1,7]
+ CRUSH rule 3 x 907 [7,2]
+ CRUSH rule 3 x 908 [5,6]
+ CRUSH rule 3 x 909 [2,3]
+ CRUSH rule 3 x 910 [6,5]
+ CRUSH rule 3 x 911 [5,7]
+ CRUSH rule 3 x 912 [0,8]
+ CRUSH rule 3 x 913 [7,1]
+ CRUSH rule 3 x 914 [6,4]
+ CRUSH rule 3 x 915 [8,2]
+ CRUSH rule 3 x 916 [3,0]
+ CRUSH rule 3 x 917 [1,3]
+ CRUSH rule 3 x 918 [8,0]
+ CRUSH rule 3 x 919 [6,2]
+ CRUSH rule 3 x 920 [7,4]
+ CRUSH rule 3 x 921 [1,4]
+ CRUSH rule 3 x 922 [6,3]
+ CRUSH rule 3 x 923 [5,6]
+ CRUSH rule 3 x 924 [3,1]
+ CRUSH rule 3 x 925 [5,6]
+ CRUSH rule 3 x 926 [3,0]
+ CRUSH rule 3 x 927 [1,6]
+ CRUSH rule 3 x 928 [8,1]
+ CRUSH rule 3 x 929 [4,2]
+ CRUSH rule 3 x 930 [2,5]
+ CRUSH rule 3 x 931 [5,2]
+ CRUSH rule 3 x 932 [4,2]
+ CRUSH rule 3 x 933 [8,4]
+ CRUSH rule 3 x 934 [5,8]
+ CRUSH rule 3 x 935 [6,3]
+ CRUSH rule 3 x 936 [0,7]
+ CRUSH rule 3 x 937 [5,8]
+ CRUSH rule 3 x 938 [6,4]
+ CRUSH rule 3 x 939 [2,8]
+ CRUSH rule 3 x 940 [8,5]
+ CRUSH rule 3 x 941 [5,0]
+ CRUSH rule 3 x 942 [1,6]
+ CRUSH rule 3 x 943 [8,2]
+ CRUSH rule 3 x 944 [4,8]
+ CRUSH rule 3 x 945 [7,0]
+ CRUSH rule 3 x 946 [2,8]
+ CRUSH rule 3 x 947 [4,2]
+ CRUSH rule 3 x 948 [7,4]
+ CRUSH rule 3 x 949 [6,2]
+ CRUSH rule 3 x 950 [3,7]
+ CRUSH rule 3 x 951 [4,6]
+ CRUSH rule 3 x 952 [2,7]
+ CRUSH rule 3 x 953 [1,3]
+ CRUSH rule 3 x 954 [4,0]
+ CRUSH rule 3 x 955 [8,1]
+ CRUSH rule 3 x 956 [1,7]
+ CRUSH rule 3 x 957 [7,0]
+ CRUSH rule 3 x 958 [8,3]
+ CRUSH rule 3 x 959 [5,1]
+ CRUSH rule 3 x 960 [3,6]
+ CRUSH rule 3 x 961 [4,1]
+ CRUSH rule 3 x 962 [7,5]
+ CRUSH rule 3 x 963 [0,5]
+ CRUSH rule 3 x 964 [3,2]
+ CRUSH rule 3 x 965 [7,3]
+ CRUSH rule 3 x 966 [3,6]
+ CRUSH rule 3 x 967 [8,4]
+ CRUSH rule 3 x 968 [7,0]
+ CRUSH rule 3 x 969 [8,0]
+ CRUSH rule 3 x 970 [0,8]
+ CRUSH rule 3 x 971 [1,8]
+ CRUSH rule 3 x 972 [1,7]
+ CRUSH rule 3 x 973 [1,8]
+ CRUSH rule 3 x 974 [5,1]
+ CRUSH rule 3 x 975 [3,8]
+ CRUSH rule 3 x 976 [4,7]
+ CRUSH rule 3 x 977 [8,3]
+ CRUSH rule 3 x 978 [7,0]
+ CRUSH rule 3 x 979 [7,2]
+ CRUSH rule 3 x 980 [6,2]
+ CRUSH rule 3 x 981 [7,5]
+ CRUSH rule 3 x 982 [4,1]
+ CRUSH rule 3 x 983 [3,6]
+ CRUSH rule 3 x 984 [0,8]
+ CRUSH rule 3 x 985 [2,4]
+ CRUSH rule 3 x 986 [8,4]
+ CRUSH rule 3 x 987 [0,4]
+ CRUSH rule 3 x 988 [1,4]
+ CRUSH rule 3 x 989 [0,8]
+ CRUSH rule 3 x 990 [1,6]
+ CRUSH rule 3 x 991 [0,4]
+ CRUSH rule 3 x 992 [7,0]
+ CRUSH rule 3 x 993 [0,6]
+ CRUSH rule 3 x 994 [3,6]
+ CRUSH rule 3 x 995 [7,0]
+ CRUSH rule 3 x 996 [6,5]
+ CRUSH rule 3 x 997 [6,3]
+ CRUSH rule 3 x 998 [8,0]
+ CRUSH rule 3 x 999 [0,7]
+ CRUSH rule 3 x 1000 [8,4]
+ CRUSH rule 3 x 1001 [2,3]
+ CRUSH rule 3 x 1002 [1,3]
+ CRUSH rule 3 x 1003 [2,7]
+ CRUSH rule 3 x 1004 [6,0]
+ CRUSH rule 3 x 1005 [6,1]
+ CRUSH rule 3 x 1006 [1,8]
+ CRUSH rule 3 x 1007 [1,3]
+ CRUSH rule 3 x 1008 [1,7]
+ CRUSH rule 3 x 1009 [6,5]
+ CRUSH rule 3 x 1010 [3,1]
+ CRUSH rule 3 x 1011 [3,0]
+ CRUSH rule 3 x 1012 [3,1]
+ CRUSH rule 3 x 1013 [5,2]
+ CRUSH rule 3 x 1014 [2,8]
+ CRUSH rule 3 x 1015 [6,3]
+ CRUSH rule 3 x 1016 [2,5]
+ CRUSH rule 3 x 1017 [6,1]
+ CRUSH rule 3 x 1018 [5,1]
+ CRUSH rule 3 x 1019 [5,8]
+ CRUSH rule 3 x 1020 [5,0]
+ CRUSH rule 3 x 1021 [5,2]
+ CRUSH rule 3 x 1022 [1,7]
+ CRUSH rule 3 x 1023 [3,0]
+ rule 3 (choose-set) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [0,3,7]
+ CRUSH rule 3 x 1 [0,8,5]
+ CRUSH rule 3 x 2 [1,4,8]
+ CRUSH rule 3 x 3 [8,0,4]
+ CRUSH rule 3 x 4 [5,1,8]
+ CRUSH rule 3 x 5 [7,0,3]
+ CRUSH rule 3 x 6 [2,6,3]
+ CRUSH rule 3 x 7 [5,6,1]
+ CRUSH rule 3 x 8 [5,7,2]
+ CRUSH rule 3 x 9 [2,4,8]
+ CRUSH rule 3 x 10 [0,8,4]
+ CRUSH rule 3 x 11 [0,6,3]
+ CRUSH rule 3 x 12 [0,3,7]
+ CRUSH rule 3 x 13 [3,8,0]
+ CRUSH rule 3 x 14 [7,1,5]
+ CRUSH rule 3 x 15 [7,1,3]
+ CRUSH rule 3 x 16 [3,7,1]
+ CRUSH rule 3 x 17 [5,1,7]
+ CRUSH rule 3 x 18 [1,3,6]
+ CRUSH rule 3 x 19 [7,5,2]
+ CRUSH rule 3 x 20 [2,4,7]
+ CRUSH rule 3 x 21 [3,6,0]
+ CRUSH rule 3 x 22 [8,5,1]
+ CRUSH rule 3 x 23 [3,7,2]
+ CRUSH rule 3 x 24 [1,6,3]
+ CRUSH rule 3 x 25 [3,8,1]
+ CRUSH rule 3 x 26 [2,7,3]
+ CRUSH rule 3 x 27 [3,1,7]
+ CRUSH rule 3 x 28 [6,2,4]
+ CRUSH rule 3 x 29 [8,5,2]
+ CRUSH rule 3 x 30 [5,6,0]
+ CRUSH rule 3 x 31 [8,2,5]
+ CRUSH rule 3 x 32 [3,7,2]
+ CRUSH rule 3 x 33 [2,7,4]
+ CRUSH rule 3 x 34 [2,3,7]
+ CRUSH rule 3 x 35 [0,6,3]
+ CRUSH rule 3 x 36 [3,6,1]
+ CRUSH rule 3 x 37 [0,4,7]
+ CRUSH rule 3 x 38 [4,6,1]
+ CRUSH rule 3 x 39 [3,7,2]
+ CRUSH rule 3 x 40 [7,2,5]
+ CRUSH rule 3 x 41 [0,7,4]
+ CRUSH rule 3 x 42 [4,7,2]
+ CRUSH rule 3 x 43 [0,3,8]
+ CRUSH rule 3 x 44 [1,8,4]
+ CRUSH rule 3 x 45 [8,2,3]
+ CRUSH rule 3 x 46 [2,4,8]
+ CRUSH rule 3 x 47 [4,1,7]
+ CRUSH rule 3 x 48 [4,6,0]
+ CRUSH rule 3 x 49 [5,6,0]
+ CRUSH rule 3 x 50 [3,1,7]
+ CRUSH rule 3 x 51 [3,6,1]
+ CRUSH rule 3 x 52 [8,2,3]
+ CRUSH rule 3 x 53 [3,6,2]
+ CRUSH rule 3 x 54 [7,4,0]
+ CRUSH rule 3 x 55 [8,0,3]
+ CRUSH rule 3 x 56 [6,5,1]
+ CRUSH rule 3 x 57 [5,8,0]
+ CRUSH rule 3 x 58 [1,8,5]
+ CRUSH rule 3 x 59 [4,0,7]
+ CRUSH rule 3 x 60 [3,1,8]
+ CRUSH rule 3 x 61 [4,8,0]
+ CRUSH rule 3 x 62 [7,0,4]
+ CRUSH rule 3 x 63 [5,6,2]
+ CRUSH rule 3 x 64 [4,1,8]
+ CRUSH rule 3 x 65 [7,4,2]
+ CRUSH rule 3 x 66 [5,6,0]
+ CRUSH rule 3 x 67 [5,0,6]
+ CRUSH rule 3 x 68 [0,3,8]
+ CRUSH rule 3 x 69 [5,2,7]
+ CRUSH rule 3 x 70 [7,2,3]
+ CRUSH rule 3 x 71 [2,7,4]
+ CRUSH rule 3 x 72 [6,2,4]
+ CRUSH rule 3 x 73 [2,7,3]
+ CRUSH rule 3 x 74 [0,8,3]
+ CRUSH rule 3 x 75 [3,0,7]
+ CRUSH rule 3 x 76 [5,2,6]
+ CRUSH rule 3 x 77 [7,0,5]
+ CRUSH rule 3 x 78 [1,4,7]
+ CRUSH rule 3 x 79 [5,0,7]
+ CRUSH rule 3 x 80 [0,4,6]
+ CRUSH rule 3 x 81 [0,5,8]
+ CRUSH rule 3 x 82 [7,2,5]
+ CRUSH rule 3 x 83 [2,6,4]
+ CRUSH rule 3 x 84 [7,0,3]
+ CRUSH rule 3 x 85 [3,7,0]
+ CRUSH rule 3 x 86 [0,6,4]
+ CRUSH rule 3 x 87 [0,6,5]
+ CRUSH rule 3 x 88 [1,8,4]
+ CRUSH rule 3 x 89 [3,1,8]
+ CRUSH rule 3 x 90 [6,3,2]
+ CRUSH rule 3 x 91 [3,6,2]
+ CRUSH rule 3 x 92 [1,6,3]
+ CRUSH rule 3 x 93 [7,4,0]
+ CRUSH rule 3 x 94 [0,3,7]
+ CRUSH rule 3 x 95 [7,3,1]
+ CRUSH rule 3 x 96 [3,8,2]
+ CRUSH rule 3 x 97 [8,4,1]
+ CRUSH rule 3 x 98 [2,7,4]
+ CRUSH rule 3 x 99 [0,8,5]
+ CRUSH rule 3 x 100 [1,6,3]
+ CRUSH rule 3 x 101 [3,8,1]
+ CRUSH rule 3 x 102 [4,0,7]
+ CRUSH rule 3 x 103 [4,7,0]
+ CRUSH rule 3 x 104 [7,4,0]
+ CRUSH rule 3 x 105 [2,3,8]
+ CRUSH rule 3 x 106 [1,6,5]
+ CRUSH rule 3 x 107 [3,1,7]
+ CRUSH rule 3 x 108 [7,1,3]
+ CRUSH rule 3 x 109 [1,3,8]
+ CRUSH rule 3 x 110 [3,2,6]
+ CRUSH rule 3 x 111 [2,4,7]
+ CRUSH rule 3 x 112 [2,6,3]
+ CRUSH rule 3 x 113 [6,1,4]
+ CRUSH rule 3 x 114 [7,5,0]
+ CRUSH rule 3 x 115 [8,2,3]
+ CRUSH rule 3 x 116 [1,7,4]
+ CRUSH rule 3 x 117 [7,5,1]
+ CRUSH rule 3 x 118 [0,3,6]
+ CRUSH rule 3 x 119 [5,7,1]
+ CRUSH rule 3 x 120 [0,5,6]
+ CRUSH rule 3 x 121 [2,8,4]
+ CRUSH rule 3 x 122 [8,4,2]
+ CRUSH rule 3 x 123 [2,4,7]
+ CRUSH rule 3 x 124 [3,0,8]
+ CRUSH rule 3 x 125 [0,7,3]
+ CRUSH rule 3 x 126 [4,1,7]
+ CRUSH rule 3 x 127 [3,6,1]
+ CRUSH rule 3 x 128 [3,8,1]
+ CRUSH rule 3 x 129 [0,4,7]
+ CRUSH rule 3 x 130 [3,8,1]
+ CRUSH rule 3 x 131 [1,5,6]
+ CRUSH rule 3 x 132 [1,3,8]
+ CRUSH rule 3 x 133 [3,6,1]
+ CRUSH rule 3 x 134 [1,7,4]
+ CRUSH rule 3 x 135 [5,7,0]
+ CRUSH rule 3 x 136 [2,3,8]
+ CRUSH rule 3 x 137 [7,3,1]
+ CRUSH rule 3 x 138 [8,3,0]
+ CRUSH rule 3 x 139 [3,0,8]
+ CRUSH rule 3 x 140 [1,8,3]
+ CRUSH rule 3 x 141 [6,0,4]
+ CRUSH rule 3 x 142 [3,2,8]
+ CRUSH rule 3 x 143 [5,7,0]
+ CRUSH rule 3 x 144 [8,2,5]
+ CRUSH rule 3 x 145 [8,4,2]
+ CRUSH rule 3 x 146 [2,8,4]
+ CRUSH rule 3 x 147 [2,6,4]
+ CRUSH rule 3 x 148 [3,0,8]
+ CRUSH rule 3 x 149 [4,6,2]
+ CRUSH rule 3 x 150 [1,8,4]
+ CRUSH rule 3 x 151 [3,8,1]
+ CRUSH rule 3 x 152 [8,3,0]
+ CRUSH rule 3 x 153 [8,3,0]
+ CRUSH rule 3 x 154 [3,0,6]
+ CRUSH rule 3 x 155 [3,6,0]
+ CRUSH rule 3 x 156 [4,2,7]
+ CRUSH rule 3 x 157 [4,1,7]
+ CRUSH rule 3 x 158 [2,6,3]
+ CRUSH rule 3 x 159 [7,0,3]
+ CRUSH rule 3 x 160 [2,8,5]
+ CRUSH rule 3 x 161 [1,3,6]
+ CRUSH rule 3 x 162 [0,8,5]
+ CRUSH rule 3 x 163 [5,8,2]
+ CRUSH rule 3 x 164 [7,2,4]
+ CRUSH rule 3 x 165 [7,2,4]
+ CRUSH rule 3 x 166 [2,3,7]
+ CRUSH rule 3 x 167 [0,6,3]
+ CRUSH rule 3 x 168 [4,0,7]
+ CRUSH rule 3 x 169 [2,7,3]
+ CRUSH rule 3 x 170 [1,3,8]
+ CRUSH rule 3 x 171 [7,3,2]
+ CRUSH rule 3 x 172 [0,8,4]
+ CRUSH rule 3 x 173 [8,5,1]
+ CRUSH rule 3 x 174 [1,4,8]
+ CRUSH rule 3 x 175 [6,0,3]
+ CRUSH rule 3 x 176 [4,2,7]
+ CRUSH rule 3 x 177 [5,1,8]
+ CRUSH rule 3 x 178 [3,0,6]
+ CRUSH rule 3 x 179 [4,1,7]
+ CRUSH rule 3 x 180 [3,7,2]
+ CRUSH rule 3 x 181 [6,0,5]
+ CRUSH rule 3 x 182 [8,3,1]
+ CRUSH rule 3 x 183 [7,5,1]
+ CRUSH rule 3 x 184 [5,6,1]
+ CRUSH rule 3 x 185 [6,0,3]
+ CRUSH rule 3 x 186 [2,4,8]
+ CRUSH rule 3 x 187 [1,6,3]
+ CRUSH rule 3 x 188 [1,6,3]
+ CRUSH rule 3 x 189 [0,6,5]
+ CRUSH rule 3 x 190 [4,1,8]
+ CRUSH rule 3 x 191 [7,0,5]
+ CRUSH rule 3 x 192 [5,2,6]
+ CRUSH rule 3 x 193 [4,0,6]
+ CRUSH rule 3 x 194 [1,3,7]
+ CRUSH rule 3 x 195 [6,5,2]
+ CRUSH rule 3 x 196 [6,1,3]
+ CRUSH rule 3 x 197 [6,5,1]
+ CRUSH rule 3 x 198 [2,3,6]
+ CRUSH rule 3 x 199 [0,5,7]
+ CRUSH rule 3 x 200 [0,3,8]
+ CRUSH rule 3 x 201 [7,1,5]
+ CRUSH rule 3 x 202 [6,4,1]
+ CRUSH rule 3 x 203 [4,6,1]
+ CRUSH rule 3 x 204 [2,4,8]
+ CRUSH rule 3 x 205 [0,7,4]
+ CRUSH rule 3 x 206 [0,8,4]
+ CRUSH rule 3 x 207 [3,2,7]
+ CRUSH rule 3 x 208 [7,2,4]
+ CRUSH rule 3 x 209 [1,8,3]
+ CRUSH rule 3 x 210 [1,4,6]
+ CRUSH rule 3 x 211 [5,2,7]
+ CRUSH rule 3 x 212 [7,5,0]
+ CRUSH rule 3 x 213 [8,4,0]
+ CRUSH rule 3 x 214 [4,7,1]
+ CRUSH rule 3 x 215 [8,0,5]
+ CRUSH rule 3 x 216 [5,2,8]
+ CRUSH rule 3 x 217 [0,7,5]
+ CRUSH rule 3 x 218 [0,7,4]
+ CRUSH rule 3 x 219 [4,6,0]
+ CRUSH rule 3 x 220 [5,8,0]
+ CRUSH rule 3 x 221 [3,7,0]
+ CRUSH rule 3 x 222 [6,4,1]
+ CRUSH rule 3 x 223 [1,3,6]
+ CRUSH rule 3 x 224 [1,3,8]
+ CRUSH rule 3 x 225 [8,0,3]
+ CRUSH rule 3 x 226 [7,0,4]
+ CRUSH rule 3 x 227 [3,0,7]
+ CRUSH rule 3 x 228 [5,6,1]
+ CRUSH rule 3 x 229 [3,7,0]
+ CRUSH rule 3 x 230 [4,6,1]
+ CRUSH rule 3 x 231 [4,7,1]
+ CRUSH rule 3 x 232 [2,8,4]
+ CRUSH rule 3 x 233 [3,6,0]
+ CRUSH rule 3 x 234 [0,4,6]
+ CRUSH rule 3 x 235 [3,6,1]
+ CRUSH rule 3 x 236 [5,0,8]
+ CRUSH rule 3 x 237 [4,8,0]
+ CRUSH rule 3 x 238 [4,2,6]
+ CRUSH rule 3 x 239 [8,3,2]
+ CRUSH rule 3 x 240 [5,8,2]
+ CRUSH rule 3 x 241 [3,1,7]
+ CRUSH rule 3 x 242 [3,2,6]
+ CRUSH rule 3 x 243 [4,8,2]
+ CRUSH rule 3 x 244 [4,6,0]
+ CRUSH rule 3 x 245 [7,2,3]
+ CRUSH rule 3 x 246 [1,3,7]
+ CRUSH rule 3 x 247 [6,1,4]
+ CRUSH rule 3 x 248 [8,0,3]
+ CRUSH rule 3 x 249 [2,5,8]
+ CRUSH rule 3 x 250 [2,3,8]
+ CRUSH rule 3 x 251 [2,3,7]
+ CRUSH rule 3 x 252 [3,7,2]
+ CRUSH rule 3 x 253 [3,0,7]
+ CRUSH rule 3 x 254 [3,2,8]
+ CRUSH rule 3 x 255 [1,7,4]
+ CRUSH rule 3 x 256 [5,6,1]
+ CRUSH rule 3 x 257 [2,6,4]
+ CRUSH rule 3 x 258 [5,2,8]
+ CRUSH rule 3 x 259 [4,6,1]
+ CRUSH rule 3 x 260 [3,8,1]
+ CRUSH rule 3 x 261 [8,5,2]
+ CRUSH rule 3 x 262 [5,6,2]
+ CRUSH rule 3 x 263 [6,1,5]
+ CRUSH rule 3 x 264 [3,6,1]
+ CRUSH rule 3 x 265 [8,4,0]
+ CRUSH rule 3 x 266 [8,0,4]
+ CRUSH rule 3 x 267 [2,4,6]
+ CRUSH rule 3 x 268 [0,6,4]
+ CRUSH rule 3 x 269 [0,6,5]
+ CRUSH rule 3 x 270 [5,1,8]
+ CRUSH rule 3 x 271 [7,4,2]
+ CRUSH rule 3 x 272 [2,6,5]
+ CRUSH rule 3 x 273 [3,2,7]
+ CRUSH rule 3 x 274 [6,3,2]
+ CRUSH rule 3 x 275 [4,8,1]
+ CRUSH rule 3 x 276 [7,0,5]
+ CRUSH rule 3 x 277 [6,4,1]
+ CRUSH rule 3 x 278 [6,2,3]
+ CRUSH rule 3 x 279 [8,5,0]
+ CRUSH rule 3 x 280 [0,7,4]
+ CRUSH rule 3 x 281 [8,1,5]
+ CRUSH rule 3 x 282 [3,2,8]
+ CRUSH rule 3 x 283 [8,1,3]
+ CRUSH rule 3 x 284 [6,3,1]
+ CRUSH rule 3 x 285 [5,7,2]
+ CRUSH rule 3 x 286 [2,8,4]
+ CRUSH rule 3 x 287 [0,3,8]
+ CRUSH rule 3 x 288 [8,1,4]
+ CRUSH rule 3 x 289 [4,6,0]
+ CRUSH rule 3 x 290 [1,5,6]
+ CRUSH rule 3 x 291 [0,5,8]
+ CRUSH rule 3 x 292 [8,1,4]
+ CRUSH rule 3 x 293 [6,2,3]
+ CRUSH rule 3 x 294 [7,5,2]
+ CRUSH rule 3 x 295 [4,6,0]
+ CRUSH rule 3 x 296 [3,1,6]
+ CRUSH rule 3 x 297 [6,0,5]
+ CRUSH rule 3 x 298 [1,5,6]
+ CRUSH rule 3 x 299 [2,8,4]
+ CRUSH rule 3 x 300 [8,4,1]
+ CRUSH rule 3 x 301 [0,7,3]
+ CRUSH rule 3 x 302 [3,1,6]
+ CRUSH rule 3 x 303 [7,4,1]
+ CRUSH rule 3 x 304 [2,6,4]
+ CRUSH rule 3 x 305 [5,6,1]
+ CRUSH rule 3 x 306 [0,8,3]
+ CRUSH rule 3 x 307 [0,7,4]
+ CRUSH rule 3 x 308 [0,8,5]
+ CRUSH rule 3 x 309 [7,4,2]
+ CRUSH rule 3 x 310 [4,0,6]
+ CRUSH rule 3 x 311 [3,8,2]
+ CRUSH rule 3 x 312 [2,6,5]
+ CRUSH rule 3 x 313 [5,1,7]
+ CRUSH rule 3 x 314 [4,0,6]
+ CRUSH rule 3 x 315 [2,3,7]
+ CRUSH rule 3 x 316 [6,4,1]
+ CRUSH rule 3 x 317 [2,7,4]
+ CRUSH rule 3 x 318 [8,1,4]
+ CRUSH rule 3 x 319 [5,2,6]
+ CRUSH rule 3 x 320 [3,8,0]
+ CRUSH rule 3 x 321 [1,5,8]
+ CRUSH rule 3 x 322 [2,6,3]
+ CRUSH rule 3 x 323 [4,7,0]
+ CRUSH rule 3 x 324 [7,2,5]
+ CRUSH rule 3 x 325 [4,6,0]
+ CRUSH rule 3 x 326 [3,1,7]
+ CRUSH rule 3 x 327 [0,8,3]
+ CRUSH rule 3 x 328 [7,3,1]
+ CRUSH rule 3 x 329 [5,7,0]
+ CRUSH rule 3 x 330 [3,7,0]
+ CRUSH rule 3 x 331 [2,7,3]
+ CRUSH rule 3 x 332 [2,3,8]
+ CRUSH rule 3 x 333 [6,4,1]
+ CRUSH rule 3 x 334 [8,3,2]
+ CRUSH rule 3 x 335 [7,2,3]
+ CRUSH rule 3 x 336 [4,6,2]
+ CRUSH rule 3 x 337 [7,0,4]
+ CRUSH rule 3 x 338 [5,8,2]
+ CRUSH rule 3 x 339 [7,5,0]
+ CRUSH rule 3 x 340 [2,6,5]
+ CRUSH rule 3 x 341 [5,1,7]
+ CRUSH rule 3 x 342 [0,8,5]
+ CRUSH rule 3 x 343 [6,5,0]
+ CRUSH rule 3 x 344 [6,1,4]
+ CRUSH rule 3 x 345 [4,7,0]
+ CRUSH rule 3 x 346 [8,0,3]
+ CRUSH rule 3 x 347 [3,0,8]
+ CRUSH rule 3 x 348 [8,0,3]
+ CRUSH rule 3 x 349 [1,7,3]
+ CRUSH rule 3 x 350 [8,5,1]
+ CRUSH rule 3 x 351 [3,8,0]
+ CRUSH rule 3 x 352 [1,8,4]
+ CRUSH rule 3 x 353 [6,5,1]
+ CRUSH rule 3 x 354 [0,5,6]
+ CRUSH rule 3 x 355 [3,8,0]
+ CRUSH rule 3 x 356 [3,1,8]
+ CRUSH rule 3 x 357 [6,1,3]
+ CRUSH rule 3 x 358 [2,8,5]
+ CRUSH rule 3 x 359 [6,0,5]
+ CRUSH rule 3 x 360 [5,0,8]
+ CRUSH rule 3 x 361 [8,5,0]
+ CRUSH rule 3 x 362 [4,0,8]
+ CRUSH rule 3 x 363 [4,2,8]
+ CRUSH rule 3 x 364 [2,5,7]
+ CRUSH rule 3 x 365 [6,4,2]
+ CRUSH rule 3 x 366 [7,0,3]
+ CRUSH rule 3 x 367 [4,2,7]
+ CRUSH rule 3 x 368 [7,3,1]
+ CRUSH rule 3 x 369 [3,7,2]
+ CRUSH rule 3 x 370 [8,2,4]
+ CRUSH rule 3 x 371 [1,5,8]
+ CRUSH rule 3 x 372 [3,1,8]
+ CRUSH rule 3 x 373 [0,6,4]
+ CRUSH rule 3 x 374 [3,8,1]
+ CRUSH rule 3 x 375 [6,5,2]
+ CRUSH rule 3 x 376 [7,1,3]
+ CRUSH rule 3 x 377 [1,4,7]
+ CRUSH rule 3 x 378 [0,6,4]
+ CRUSH rule 3 x 379 [8,3,0]
+ CRUSH rule 3 x 380 [2,3,8]
+ CRUSH rule 3 x 381 [0,3,7]
+ CRUSH rule 3 x 382 [1,3,7]
+ CRUSH rule 3 x 383 [4,7,1]
+ CRUSH rule 3 x 384 [7,0,4]
+ CRUSH rule 3 x 385 [7,4,0]
+ CRUSH rule 3 x 386 [0,4,6]
+ CRUSH rule 3 x 387 [1,5,8]
+ CRUSH rule 3 x 388 [5,2,7]
+ CRUSH rule 3 x 389 [1,4,8]
+ CRUSH rule 3 x 390 [5,8,1]
+ CRUSH rule 3 x 391 [5,6,0]
+ CRUSH rule 3 x 392 [1,7,5]
+ CRUSH rule 3 x 393 [4,0,6]
+ CRUSH rule 3 x 394 [4,8,2]
+ CRUSH rule 3 x 395 [4,0,8]
+ CRUSH rule 3 x 396 [4,2,6]
+ CRUSH rule 3 x 397 [2,4,7]
+ CRUSH rule 3 x 398 [2,3,6]
+ CRUSH rule 3 x 399 [8,5,2]
+ CRUSH rule 3 x 400 [8,1,3]
+ CRUSH rule 3 x 401 [0,3,6]
+ CRUSH rule 3 x 402 [7,4,2]
+ CRUSH rule 3 x 403 [0,4,7]
+ CRUSH rule 3 x 404 [4,0,6]
+ CRUSH rule 3 x 405 [6,4,0]
+ CRUSH rule 3 x 406 [2,6,4]
+ CRUSH rule 3 x 407 [2,7,5]
+ CRUSH rule 3 x 408 [4,1,7]
+ CRUSH rule 3 x 409 [7,5,0]
+ CRUSH rule 3 x 410 [8,3,1]
+ CRUSH rule 3 x 411 [2,7,4]
+ CRUSH rule 3 x 412 [0,3,7]
+ CRUSH rule 3 x 413 [5,2,8]
+ CRUSH rule 3 x 414 [4,0,8]
+ CRUSH rule 3 x 415 [0,6,3]
+ CRUSH rule 3 x 416 [2,8,5]
+ CRUSH rule 3 x 417 [8,0,3]
+ CRUSH rule 3 x 418 [7,1,3]
+ CRUSH rule 3 x 419 [8,5,2]
+ CRUSH rule 3 x 420 [1,4,7]
+ CRUSH rule 3 x 421 [8,3,0]
+ CRUSH rule 3 x 422 [6,3,1]
+ CRUSH rule 3 x 423 [0,3,7]
+ CRUSH rule 3 x 424 [8,5,1]
+ CRUSH rule 3 x 425 [1,4,8]
+ CRUSH rule 3 x 426 [6,2,4]
+ CRUSH rule 3 x 427 [0,8,3]
+ CRUSH rule 3 x 428 [5,8,1]
+ CRUSH rule 3 x 429 [4,8,0]
+ CRUSH rule 3 x 430 [3,7,0]
+ CRUSH rule 3 x 431 [5,0,7]
+ CRUSH rule 3 x 432 [7,0,4]
+ CRUSH rule 3 x 433 [6,4,0]
+ CRUSH rule 3 x 434 [5,0,7]
+ CRUSH rule 3 x 435 [0,4,6]
+ CRUSH rule 3 x 436 [4,0,7]
+ CRUSH rule 3 x 437 [7,3,1]
+ CRUSH rule 3 x 438 [0,5,8]
+ CRUSH rule 3 x 439 [1,3,8]
+ CRUSH rule 3 x 440 [2,6,5]
+ CRUSH rule 3 x 441 [5,8,0]
+ CRUSH rule 3 x 442 [2,3,6]
+ CRUSH rule 3 x 443 [6,0,3]
+ CRUSH rule 3 x 444 [7,1,4]
+ CRUSH rule 3 x 445 [6,4,0]
+ CRUSH rule 3 x 446 [4,0,8]
+ CRUSH rule 3 x 447 [2,4,6]
+ CRUSH rule 3 x 448 [7,0,5]
+ CRUSH rule 3 x 449 [7,4,2]
+ CRUSH rule 3 x 450 [4,0,6]
+ CRUSH rule 3 x 451 [6,5,1]
+ CRUSH rule 3 x 452 [8,4,0]
+ CRUSH rule 3 x 453 [6,5,0]
+ CRUSH rule 3 x 454 [6,5,0]
+ CRUSH rule 3 x 455 [2,8,4]
+ CRUSH rule 3 x 456 [6,2,5]
+ CRUSH rule 3 x 457 [7,1,5]
+ CRUSH rule 3 x 458 [2,8,5]
+ CRUSH rule 3 x 459 [2,6,5]
+ CRUSH rule 3 x 460 [6,5,2]
+ CRUSH rule 3 x 461 [6,4,2]
+ CRUSH rule 3 x 462 [8,0,4]
+ CRUSH rule 3 x 463 [6,0,4]
+ CRUSH rule 3 x 464 [7,4,1]
+ CRUSH rule 3 x 465 [7,1,5]
+ CRUSH rule 3 x 466 [5,6,2]
+ CRUSH rule 3 x 467 [6,5,1]
+ CRUSH rule 3 x 468 [7,2,3]
+ CRUSH rule 3 x 469 [7,2,4]
+ CRUSH rule 3 x 470 [3,0,8]
+ CRUSH rule 3 x 471 [0,6,4]
+ CRUSH rule 3 x 472 [5,0,8]
+ CRUSH rule 3 x 473 [1,4,7]
+ CRUSH rule 3 x 474 [6,1,4]
+ CRUSH rule 3 x 475 [6,2,3]
+ CRUSH rule 3 x 476 [4,7,1]
+ CRUSH rule 3 x 477 [5,6,1]
+ CRUSH rule 3 x 478 [6,1,3]
+ CRUSH rule 3 x 479 [0,3,6]
+ CRUSH rule 3 x 480 [1,6,4]
+ CRUSH rule 3 x 481 [2,5,7]
+ CRUSH rule 3 x 482 [4,8,0]
+ CRUSH rule 3 x 483 [0,6,4]
+ CRUSH rule 3 x 484 [1,8,5]
+ CRUSH rule 3 x 485 [4,8,1]
+ CRUSH rule 3 x 486 [4,0,8]
+ CRUSH rule 3 x 487 [5,0,8]
+ CRUSH rule 3 x 488 [5,7,1]
+ CRUSH rule 3 x 489 [2,8,4]
+ CRUSH rule 3 x 490 [6,4,0]
+ CRUSH rule 3 x 491 [1,6,5]
+ CRUSH rule 3 x 492 [6,5,2]
+ CRUSH rule 3 x 493 [0,8,3]
+ CRUSH rule 3 x 494 [1,6,4]
+ CRUSH rule 3 x 495 [3,0,6]
+ CRUSH rule 3 x 496 [7,5,1]
+ CRUSH rule 3 x 497 [5,7,2]
+ CRUSH rule 3 x 498 [0,4,6]
+ CRUSH rule 3 x 499 [8,5,2]
+ CRUSH rule 3 x 500 [3,6,1]
+ CRUSH rule 3 x 501 [0,7,5]
+ CRUSH rule 3 x 502 [7,1,3]
+ CRUSH rule 3 x 503 [2,3,7]
+ CRUSH rule 3 x 504 [5,8,2]
+ CRUSH rule 3 x 505 [0,7,5]
+ CRUSH rule 3 x 506 [5,1,7]
+ CRUSH rule 3 x 507 [6,0,3]
+ CRUSH rule 3 x 508 [0,4,7]
+ CRUSH rule 3 x 509 [7,4,0]
+ CRUSH rule 3 x 510 [6,2,5]
+ CRUSH rule 3 x 511 [5,6,2]
+ CRUSH rule 3 x 512 [7,2,4]
+ CRUSH rule 3 x 513 [7,2,4]
+ CRUSH rule 3 x 514 [4,7,0]
+ CRUSH rule 3 x 515 [8,3,0]
+ CRUSH rule 3 x 516 [4,1,7]
+ CRUSH rule 3 x 517 [7,0,4]
+ CRUSH rule 3 x 518 [4,6,0]
+ CRUSH rule 3 x 519 [7,3,1]
+ CRUSH rule 3 x 520 [2,8,5]
+ CRUSH rule 3 x 521 [8,0,3]
+ CRUSH rule 3 x 522 [6,0,4]
+ CRUSH rule 3 x 523 [4,1,7]
+ CRUSH rule 3 x 524 [0,4,7]
+ CRUSH rule 3 x 525 [0,3,8]
+ CRUSH rule 3 x 526 [1,3,8]
+ CRUSH rule 3 x 527 [0,4,6]
+ CRUSH rule 3 x 528 [5,2,7]
+ CRUSH rule 3 x 529 [5,6,1]
+ CRUSH rule 3 x 530 [6,4,1]
+ CRUSH rule 3 x 531 [6,0,3]
+ CRUSH rule 3 x 532 [6,5,1]
+ CRUSH rule 3 x 533 [5,8,0]
+ CRUSH rule 3 x 534 [7,4,2]
+ CRUSH rule 3 x 535 [8,0,3]
+ CRUSH rule 3 x 536 [6,2,3]
+ CRUSH rule 3 x 537 [3,8,0]
+ CRUSH rule 3 x 538 [6,4,2]
+ CRUSH rule 3 x 539 [8,4,2]
+ CRUSH rule 3 x 540 [0,7,4]
+ CRUSH rule 3 x 541 [2,5,7]
+ CRUSH rule 3 x 542 [3,0,8]
+ CRUSH rule 3 x 543 [6,2,4]
+ CRUSH rule 3 x 544 [3,7,1]
+ CRUSH rule 3 x 545 [5,7,1]
+ CRUSH rule 3 x 546 [6,2,3]
+ CRUSH rule 3 x 547 [8,1,5]
+ CRUSH rule 3 x 548 [5,1,8]
+ CRUSH rule 3 x 549 [5,7,1]
+ CRUSH rule 3 x 550 [0,5,6]
+ CRUSH rule 3 x 551 [7,4,0]
+ CRUSH rule 3 x 552 [5,7,2]
+ CRUSH rule 3 x 553 [4,2,8]
+ CRUSH rule 3 x 554 [0,6,5]
+ CRUSH rule 3 x 555 [5,0,8]
+ CRUSH rule 3 x 556 [3,6,1]
+ CRUSH rule 3 x 557 [7,4,0]
+ CRUSH rule 3 x 558 [3,2,6]
+ CRUSH rule 3 x 559 [4,1,8]
+ CRUSH rule 3 x 560 [8,4,2]
+ CRUSH rule 3 x 561 [6,4,1]
+ CRUSH rule 3 x 562 [3,0,8]
+ CRUSH rule 3 x 563 [2,7,4]
+ CRUSH rule 3 x 564 [5,1,7]
+ CRUSH rule 3 x 565 [3,8,0]
+ CRUSH rule 3 x 566 [4,6,0]
+ CRUSH rule 3 x 567 [3,7,0]
+ CRUSH rule 3 x 568 [7,4,0]
+ CRUSH rule 3 x 569 [3,1,6]
+ CRUSH rule 3 x 570 [1,4,7]
+ CRUSH rule 3 x 571 [3,6,0]
+ CRUSH rule 3 x 572 [3,0,7]
+ CRUSH rule 3 x 573 [3,1,7]
+ CRUSH rule 3 x 574 [2,3,6]
+ CRUSH rule 3 x 575 [8,1,5]
+ CRUSH rule 3 x 576 [4,7,1]
+ CRUSH rule 3 x 577 [8,2,3]
+ CRUSH rule 3 x 578 [6,0,4]
+ CRUSH rule 3 x 579 [3,2,6]
+ CRUSH rule 3 x 580 [3,0,8]
+ CRUSH rule 3 x 581 [7,1,5]
+ CRUSH rule 3 x 582 [2,8,5]
+ CRUSH rule 3 x 583 [6,2,3]
+ CRUSH rule 3 x 584 [8,0,5]
+ CRUSH rule 3 x 585 [7,0,5]
+ CRUSH rule 3 x 586 [0,8,5]
+ CRUSH rule 3 x 587 [2,5,6]
+ CRUSH rule 3 x 588 [3,8,0]
+ CRUSH rule 3 x 589 [7,1,5]
+ CRUSH rule 3 x 590 [6,2,5]
+ CRUSH rule 3 x 591 [5,2,7]
+ CRUSH rule 3 x 592 [2,4,6]
+ CRUSH rule 3 x 593 [0,8,4]
+ CRUSH rule 3 x 594 [0,6,3]
+ CRUSH rule 3 x 595 [7,1,5]
+ CRUSH rule 3 x 596 [4,0,6]
+ CRUSH rule 3 x 597 [3,1,7]
+ CRUSH rule 3 x 598 [3,0,7]
+ CRUSH rule 3 x 599 [5,1,6]
+ CRUSH rule 3 x 600 [7,0,4]
+ CRUSH rule 3 x 601 [0,6,3]
+ CRUSH rule 3 x 602 [3,8,2]
+ CRUSH rule 3 x 603 [5,0,8]
+ CRUSH rule 3 x 604 [7,4,0]
+ CRUSH rule 3 x 605 [3,2,6]
+ CRUSH rule 3 x 606 [2,7,3]
+ CRUSH rule 3 x 607 [0,5,8]
+ CRUSH rule 3 x 608 [5,1,8]
+ CRUSH rule 3 x 609 [5,0,6]
+ CRUSH rule 3 x 610 [3,8,2]
+ CRUSH rule 3 x 611 [1,8,3]
+ CRUSH rule 3 x 612 [2,8,3]
+ CRUSH rule 3 x 613 [7,1,3]
+ CRUSH rule 3 x 614 [7,2,3]
+ CRUSH rule 3 x 615 [6,2,3]
+ CRUSH rule 3 x 616 [0,7,5]
+ CRUSH rule 3 x 617 [6,2,3]
+ CRUSH rule 3 x 618 [7,3,1]
+ CRUSH rule 3 x 619 [5,0,6]
+ CRUSH rule 3 x 620 [4,1,8]
+ CRUSH rule 3 x 621 [5,6,0]
+ CRUSH rule 3 x 622 [0,3,8]
+ CRUSH rule 3 x 623 [0,8,4]
+ CRUSH rule 3 x 624 [3,2,8]
+ CRUSH rule 3 x 625 [2,5,8]
+ CRUSH rule 3 x 626 [7,2,3]
+ CRUSH rule 3 x 627 [2,6,5]
+ CRUSH rule 3 x 628 [8,1,3]
+ CRUSH rule 3 x 629 [2,6,3]
+ CRUSH rule 3 x 630 [2,6,4]
+ CRUSH rule 3 x 631 [0,6,5]
+ CRUSH rule 3 x 632 [7,0,4]
+ CRUSH rule 3 x 633 [8,4,2]
+ CRUSH rule 3 x 634 [0,5,6]
+ CRUSH rule 3 x 635 [5,6,2]
+ CRUSH rule 3 x 636 [1,3,7]
+ CRUSH rule 3 x 637 [4,0,8]
+ CRUSH rule 3 x 638 [6,2,5]
+ CRUSH rule 3 x 639 [4,0,6]
+ CRUSH rule 3 x 640 [3,2,7]
+ CRUSH rule 3 x 641 [7,2,3]
+ CRUSH rule 3 x 642 [2,8,4]
+ CRUSH rule 3 x 643 [3,0,8]
+ CRUSH rule 3 x 644 [8,0,4]
+ CRUSH rule 3 x 645 [5,7,2]
+ CRUSH rule 3 x 646 [8,0,3]
+ CRUSH rule 3 x 647 [7,0,5]
+ CRUSH rule 3 x 648 [0,8,3]
+ CRUSH rule 3 x 649 [4,7,1]
+ CRUSH rule 3 x 650 [7,5,1]
+ CRUSH rule 3 x 651 [3,6,1]
+ CRUSH rule 3 x 652 [3,6,1]
+ CRUSH rule 3 x 653 [8,3,0]
+ CRUSH rule 3 x 654 [7,4,0]
+ CRUSH rule 3 x 655 [0,5,6]
+ CRUSH rule 3 x 656 [4,7,0]
+ CRUSH rule 3 x 657 [6,0,5]
+ CRUSH rule 3 x 658 [5,8,0]
+ CRUSH rule 3 x 659 [4,7,0]
+ CRUSH rule 3 x 660 [7,3,0]
+ CRUSH rule 3 x 661 [1,7,3]
+ CRUSH rule 3 x 662 [4,2,8]
+ CRUSH rule 3 x 663 [1,3,7]
+ CRUSH rule 3 x 664 [1,3,6]
+ CRUSH rule 3 x 665 [5,6,1]
+ CRUSH rule 3 x 666 [2,7,4]
+ CRUSH rule 3 x 667 [1,3,8]
+ CRUSH rule 3 x 668 [3,7,0]
+ CRUSH rule 3 x 669 [6,3,0]
+ CRUSH rule 3 x 670 [4,1,6]
+ CRUSH rule 3 x 671 [0,8,5]
+ CRUSH rule 3 x 672 [4,2,8]
+ CRUSH rule 3 x 673 [5,0,7]
+ CRUSH rule 3 x 674 [3,0,7]
+ CRUSH rule 3 x 675 [0,8,5]
+ CRUSH rule 3 x 676 [0,3,7]
+ CRUSH rule 3 x 677 [4,1,6]
+ CRUSH rule 3 x 678 [2,3,8]
+ CRUSH rule 3 x 679 [6,0,5]
+ CRUSH rule 3 x 680 [0,3,8]
+ CRUSH rule 3 x 681 [4,6,1]
+ CRUSH rule 3 x 682 [0,4,8]
+ CRUSH rule 3 x 683 [0,3,8]
+ CRUSH rule 3 x 684 [7,2,5]
+ CRUSH rule 3 x 685 [7,2,5]
+ CRUSH rule 3 x 686 [1,5,8]
+ CRUSH rule 3 x 687 [3,7,2]
+ CRUSH rule 3 x 688 [5,6,2]
+ CRUSH rule 3 x 689 [6,5,1]
+ CRUSH rule 3 x 690 [8,0,4]
+ CRUSH rule 3 x 691 [3,1,6]
+ CRUSH rule 3 x 692 [7,1,4]
+ CRUSH rule 3 x 693 [6,5,2]
+ CRUSH rule 3 x 694 [6,4,2]
+ CRUSH rule 3 x 695 [0,6,3]
+ CRUSH rule 3 x 696 [1,5,8]
+ CRUSH rule 3 x 697 [6,0,4]
+ CRUSH rule 3 x 698 [6,0,3]
+ CRUSH rule 3 x 699 [1,8,3]
+ CRUSH rule 3 x 700 [0,4,6]
+ CRUSH rule 3 x 701 [4,0,6]
+ CRUSH rule 3 x 702 [3,0,8]
+ CRUSH rule 3 x 703 [8,4,0]
+ CRUSH rule 3 x 704 [0,4,6]
+ CRUSH rule 3 x 705 [8,0,4]
+ CRUSH rule 3 x 706 [1,5,8]
+ CRUSH rule 3 x 707 [7,3,0]
+ CRUSH rule 3 x 708 [3,7,1]
+ CRUSH rule 3 x 709 [6,5,1]
+ CRUSH rule 3 x 710 [8,5,1]
+ CRUSH rule 3 x 711 [2,4,7]
+ CRUSH rule 3 x 712 [2,3,6]
+ CRUSH rule 3 x 713 [6,3,1]
+ CRUSH rule 3 x 714 [3,0,6]
+ CRUSH rule 3 x 715 [1,3,6]
+ CRUSH rule 3 x 716 [3,6,1]
+ CRUSH rule 3 x 717 [8,0,5]
+ CRUSH rule 3 x 718 [3,7,0]
+ CRUSH rule 3 x 719 [2,6,3]
+ CRUSH rule 3 x 720 [6,0,4]
+ CRUSH rule 3 x 721 [5,7,0]
+ CRUSH rule 3 x 722 [5,7,1]
+ CRUSH rule 3 x 723 [5,2,7]
+ CRUSH rule 3 x 724 [0,7,3]
+ CRUSH rule 3 x 725 [0,4,6]
+ CRUSH rule 3 x 726 [3,7,2]
+ CRUSH rule 3 x 727 [4,7,2]
+ CRUSH rule 3 x 728 [2,6,3]
+ CRUSH rule 3 x 729 [5,6,1]
+ CRUSH rule 3 x 730 [3,8,2]
+ CRUSH rule 3 x 731 [4,1,6]
+ CRUSH rule 3 x 732 [1,4,8]
+ CRUSH rule 3 x 733 [5,6,2]
+ CRUSH rule 3 x 734 [6,5,0]
+ CRUSH rule 3 x 735 [4,6,1]
+ CRUSH rule 3 x 736 [3,8,1]
+ CRUSH rule 3 x 737 [1,8,3]
+ CRUSH rule 3 x 738 [5,1,6]
+ CRUSH rule 3 x 739 [0,7,4]
+ CRUSH rule 3 x 740 [0,7,3]
+ CRUSH rule 3 x 741 [7,2,3]
+ CRUSH rule 3 x 742 [8,2,3]
+ CRUSH rule 3 x 743 [7,0,5]
+ CRUSH rule 3 x 744 [4,6,1]
+ CRUSH rule 3 x 745 [3,0,8]
+ CRUSH rule 3 x 746 [4,1,7]
+ CRUSH rule 3 x 747 [6,2,4]
+ CRUSH rule 3 x 748 [2,6,5]
+ CRUSH rule 3 x 749 [4,7,2]
+ CRUSH rule 3 x 750 [1,7,4]
+ CRUSH rule 3 x 751 [2,7,3]
+ CRUSH rule 3 x 752 [8,0,3]
+ CRUSH rule 3 x 753 [7,4,2]
+ CRUSH rule 3 x 754 [8,5,2]
+ CRUSH rule 3 x 755 [1,6,5]
+ CRUSH rule 3 x 756 [5,8,1]
+ CRUSH rule 3 x 757 [8,0,4]
+ CRUSH rule 3 x 758 [6,1,5]
+ CRUSH rule 3 x 759 [8,5,0]
+ CRUSH rule 3 x 760 [1,5,7]
+ CRUSH rule 3 x 761 [4,2,6]
+ CRUSH rule 3 x 762 [2,8,3]
+ CRUSH rule 3 x 763 [8,3,1]
+ CRUSH rule 3 x 764 [1,7,4]
+ CRUSH rule 3 x 765 [6,4,0]
+ CRUSH rule 3 x 766 [8,3,0]
+ CRUSH rule 3 x 767 [1,8,5]
+ CRUSH rule 3 x 768 [8,5,0]
+ CRUSH rule 3 x 769 [6,2,4]
+ CRUSH rule 3 x 770 [6,1,4]
+ CRUSH rule 3 x 771 [7,2,4]
+ CRUSH rule 3 x 772 [8,4,2]
+ CRUSH rule 3 x 773 [3,2,6]
+ CRUSH rule 3 x 774 [4,7,1]
+ CRUSH rule 3 x 775 [6,5,0]
+ CRUSH rule 3 x 776 [7,0,3]
+ CRUSH rule 3 x 777 [3,0,6]
+ CRUSH rule 3 x 778 [1,6,3]
+ CRUSH rule 3 x 779 [2,6,5]
+ CRUSH rule 3 x 780 [0,5,8]
+ CRUSH rule 3 x 781 [6,5,0]
+ CRUSH rule 3 x 782 [5,2,8]
+ CRUSH rule 3 x 783 [7,0,3]
+ CRUSH rule 3 x 784 [0,5,8]
+ CRUSH rule 3 x 785 [6,1,3]
+ CRUSH rule 3 x 786 [7,3,0]
+ CRUSH rule 3 x 787 [1,8,5]
+ CRUSH rule 3 x 788 [6,0,4]
+ CRUSH rule 3 x 789 [0,5,8]
+ CRUSH rule 3 x 790 [8,3,1]
+ CRUSH rule 3 x 791 [3,6,2]
+ CRUSH rule 3 x 792 [5,6,1]
+ CRUSH rule 3 x 793 [6,2,3]
+ CRUSH rule 3 x 794 [2,8,3]
+ CRUSH rule 3 x 795 [0,4,6]
+ CRUSH rule 3 x 796 [3,7,1]
+ CRUSH rule 3 x 797 [2,3,6]
+ CRUSH rule 3 x 798 [6,0,3]
+ CRUSH rule 3 x 799 [5,2,7]
+ CRUSH rule 3 x 800 [5,2,8]
+ CRUSH rule 3 x 801 [3,7,2]
+ CRUSH rule 3 x 802 [1,6,4]
+ CRUSH rule 3 x 803 [0,4,7]
+ CRUSH rule 3 x 804 [6,0,5]
+ CRUSH rule 3 x 805 [3,8,0]
+ CRUSH rule 3 x 806 [1,5,6]
+ CRUSH rule 3 x 807 [5,7,2]
+ CRUSH rule 3 x 808 [4,7,2]
+ CRUSH rule 3 x 809 [1,3,6]
+ CRUSH rule 3 x 810 [5,7,2]
+ CRUSH rule 3 x 811 [8,4,1]
+ CRUSH rule 3 x 812 [8,3,1]
+ CRUSH rule 3 x 813 [6,4,2]
+ CRUSH rule 3 x 814 [3,8,0]
+ CRUSH rule 3 x 815 [3,1,6]
+ CRUSH rule 3 x 816 [2,6,5]
+ CRUSH rule 3 x 817 [4,6,2]
+ CRUSH rule 3 x 818 [3,1,7]
+ CRUSH rule 3 x 819 [5,0,7]
+ CRUSH rule 3 x 820 [3,7,1]
+ CRUSH rule 3 x 821 [4,8,0]
+ CRUSH rule 3 x 822 [2,3,7]
+ CRUSH rule 3 x 823 [4,7,2]
+ CRUSH rule 3 x 824 [3,7,1]
+ CRUSH rule 3 x 825 [2,8,4]
+ CRUSH rule 3 x 826 [7,1,5]
+ CRUSH rule 3 x 827 [0,6,3]
+ CRUSH rule 3 x 828 [2,5,8]
+ CRUSH rule 3 x 829 [5,6,0]
+ CRUSH rule 3 x 830 [2,4,7]
+ CRUSH rule 3 x 831 [1,6,4]
+ CRUSH rule 3 x 832 [4,8,2]
+ CRUSH rule 3 x 833 [2,6,5]
+ CRUSH rule 3 x 834 [3,0,8]
+ CRUSH rule 3 x 835 [8,5,0]
+ CRUSH rule 3 x 836 [3,8,1]
+ CRUSH rule 3 x 837 [6,4,0]
+ CRUSH rule 3 x 838 [6,0,4]
+ CRUSH rule 3 x 839 [5,2,8]
+ CRUSH rule 3 x 840 [7,3,2]
+ CRUSH rule 3 x 841 [4,8,0]
+ CRUSH rule 3 x 842 [2,5,8]
+ CRUSH rule 3 x 843 [6,4,2]
+ CRUSH rule 3 x 844 [4,8,2]
+ CRUSH rule 3 x 845 [3,6,2]
+ CRUSH rule 3 x 846 [3,0,6]
+ CRUSH rule 3 x 847 [0,8,5]
+ CRUSH rule 3 x 848 [2,6,5]
+ CRUSH rule 3 x 849 [4,8,1]
+ CRUSH rule 3 x 850 [1,5,7]
+ CRUSH rule 3 x 851 [6,5,1]
+ CRUSH rule 3 x 852 [7,4,2]
+ CRUSH rule 3 x 853 [6,2,5]
+ CRUSH rule 3 x 854 [7,0,3]
+ CRUSH rule 3 x 855 [5,7,2]
+ CRUSH rule 3 x 856 [6,3,1]
+ CRUSH rule 3 x 857 [8,4,0]
+ CRUSH rule 3 x 858 [6,5,2]
+ CRUSH rule 3 x 859 [6,0,5]
+ CRUSH rule 3 x 860 [4,2,8]
+ CRUSH rule 3 x 861 [8,4,1]
+ CRUSH rule 3 x 862 [6,0,5]
+ CRUSH rule 3 x 863 [8,1,5]
+ CRUSH rule 3 x 864 [5,6,1]
+ CRUSH rule 3 x 865 [8,0,3]
+ CRUSH rule 3 x 866 [3,6,2]
+ CRUSH rule 3 x 867 [6,3,1]
+ CRUSH rule 3 x 868 [6,4,2]
+ CRUSH rule 3 x 869 [8,4,2]
+ CRUSH rule 3 x 870 [0,5,7]
+ CRUSH rule 3 x 871 [3,1,6]
+ CRUSH rule 3 x 872 [5,0,6]
+ CRUSH rule 3 x 873 [4,6,1]
+ CRUSH rule 3 x 874 [2,6,4]
+ CRUSH rule 3 x 875 [2,6,5]
+ CRUSH rule 3 x 876 [5,8,1]
+ CRUSH rule 3 x 877 [6,4,0]
+ CRUSH rule 3 x 878 [5,0,7]
+ CRUSH rule 3 x 879 [7,3,1]
+ CRUSH rule 3 x 880 [3,1,7]
+ CRUSH rule 3 x 881 [5,8,2]
+ CRUSH rule 3 x 882 [4,1,6]
+ CRUSH rule 3 x 883 [2,5,6]
+ CRUSH rule 3 x 884 [6,0,3]
+ CRUSH rule 3 x 885 [5,1,6]
+ CRUSH rule 3 x 886 [3,7,2]
+ CRUSH rule 3 x 887 [7,5,2]
+ CRUSH rule 3 x 888 [6,1,4]
+ CRUSH rule 3 x 889 [2,8,3]
+ CRUSH rule 3 x 890 [7,1,4]
+ CRUSH rule 3 x 891 [1,7,4]
+ CRUSH rule 3 x 892 [6,0,5]
+ CRUSH rule 3 x 893 [2,3,6]
+ CRUSH rule 3 x 894 [7,4,1]
+ CRUSH rule 3 x 895 [5,1,6]
+ CRUSH rule 3 x 896 [1,6,3]
+ CRUSH rule 3 x 897 [4,0,8]
+ CRUSH rule 3 x 898 [0,3,8]
+ CRUSH rule 3 x 899 [1,6,4]
+ CRUSH rule 3 x 900 [4,0,7]
+ CRUSH rule 3 x 901 [5,2,6]
+ CRUSH rule 3 x 902 [8,4,2]
+ CRUSH rule 3 x 903 [5,6,1]
+ CRUSH rule 3 x 904 [5,7,0]
+ CRUSH rule 3 x 905 [6,0,3]
+ CRUSH rule 3 x 906 [1,7,4]
+ CRUSH rule 3 x 907 [7,2,3]
+ CRUSH rule 3 x 908 [5,6,1]
+ CRUSH rule 3 x 909 [2,3,8]
+ CRUSH rule 3 x 910 [6,5,0]
+ CRUSH rule 3 x 911 [5,7,1]
+ CRUSH rule 3 x 912 [0,8,5]
+ CRUSH rule 3 x 913 [7,1,4]
+ CRUSH rule 3 x 914 [6,4,0]
+ CRUSH rule 3 x 915 [8,2,3]
+ CRUSH rule 3 x 916 [3,0,6]
+ CRUSH rule 3 x 917 [1,3,6]
+ CRUSH rule 3 x 918 [8,0,5]
+ CRUSH rule 3 x 919 [6,2,3]
+ CRUSH rule 3 x 920 [7,4,2]
+ CRUSH rule 3 x 921 [1,4,8]
+ CRUSH rule 3 x 922 [6,3,1]
+ CRUSH rule 3 x 923 [5,6,0]
+ CRUSH rule 3 x 924 [3,1,8]
+ CRUSH rule 3 x 925 [5,6,0]
+ CRUSH rule 3 x 926 [3,0,7]
+ CRUSH rule 3 x 927 [1,6,5]
+ CRUSH rule 3 x 928 [8,1,4]
+ CRUSH rule 3 x 929 [4,2,6]
+ CRUSH rule 3 x 930 [2,5,8]
+ CRUSH rule 3 x 931 [5,2,8]
+ CRUSH rule 3 x 932 [4,2,7]
+ CRUSH rule 3 x 933 [8,4,2]
+ CRUSH rule 3 x 934 [5,8,0]
+ CRUSH rule 3 x 935 [6,3,0]
+ CRUSH rule 3 x 936 [0,7,3]
+ CRUSH rule 3 x 937 [5,8,1]
+ CRUSH rule 3 x 938 [6,4,0]
+ CRUSH rule 3 x 939 [2,8,3]
+ CRUSH rule 3 x 940 [8,5,2]
+ CRUSH rule 3 x 941 [5,0,6]
+ CRUSH rule 3 x 942 [1,6,4]
+ CRUSH rule 3 x 943 [8,2,4]
+ CRUSH rule 3 x 944 [4,8,1]
+ CRUSH rule 3 x 945 [7,0,3]
+ CRUSH rule 3 x 946 [2,8,3]
+ CRUSH rule 3 x 947 [4,2,6]
+ CRUSH rule 3 x 948 [7,4,1]
+ CRUSH rule 3 x 949 [6,2,5]
+ CRUSH rule 3 x 950 [3,7,0]
+ CRUSH rule 3 x 951 [4,6,2]
+ CRUSH rule 3 x 952 [2,7,4]
+ CRUSH rule 3 x 953 [1,3,6]
+ CRUSH rule 3 x 954 [4,0,8]
+ CRUSH rule 3 x 955 [8,1,4]
+ CRUSH rule 3 x 956 [1,7,4]
+ CRUSH rule 3 x 957 [7,0,3]
+ CRUSH rule 3 x 958 [8,3,2]
+ CRUSH rule 3 x 959 [5,1,6]
+ CRUSH rule 3 x 960 [3,6,2]
+ CRUSH rule 3 x 961 [4,1,6]
+ CRUSH rule 3 x 962 [7,5,0]
+ CRUSH rule 3 x 963 [0,5,8]
+ CRUSH rule 3 x 964 [3,2,7]
+ CRUSH rule 3 x 965 [7,3,1]
+ CRUSH rule 3 x 966 [3,6,0]
+ CRUSH rule 3 x 967 [8,4,2]
+ CRUSH rule 3 x 968 [7,0,4]
+ CRUSH rule 3 x 969 [8,0,4]
+ CRUSH rule 3 x 970 [0,8,3]
+ CRUSH rule 3 x 971 [1,8,5]
+ CRUSH rule 3 x 972 [1,7,5]
+ CRUSH rule 3 x 973 [1,8,3]
+ CRUSH rule 3 x 974 [5,1,7]
+ CRUSH rule 3 x 975 [3,8,0]
+ CRUSH rule 3 x 976 [4,7,2]
+ CRUSH rule 3 x 977 [8,3,2]
+ CRUSH rule 3 x 978 [7,0,3]
+ CRUSH rule 3 x 979 [7,2,5]
+ CRUSH rule 3 x 980 [6,2,3]
+ CRUSH rule 3 x 981 [7,5,1]
+ CRUSH rule 3 x 982 [4,1,8]
+ CRUSH rule 3 x 983 [3,6,0]
+ CRUSH rule 3 x 984 [0,8,4]
+ CRUSH rule 3 x 985 [2,4,8]
+ CRUSH rule 3 x 986 [8,4,2]
+ CRUSH rule 3 x 987 [0,4,8]
+ CRUSH rule 3 x 988 [1,4,6]
+ CRUSH rule 3 x 989 [0,8,5]
+ CRUSH rule 3 x 990 [1,6,4]
+ CRUSH rule 3 x 991 [0,4,8]
+ CRUSH rule 3 x 992 [7,0,4]
+ CRUSH rule 3 x 993 [0,6,4]
+ CRUSH rule 3 x 994 [3,6,1]
+ CRUSH rule 3 x 995 [7,0,4]
+ CRUSH rule 3 x 996 [6,5,1]
+ CRUSH rule 3 x 997 [6,3,1]
+ CRUSH rule 3 x 998 [8,0,4]
+ CRUSH rule 3 x 999 [0,7,5]
+ CRUSH rule 3 x 1000 [8,4,1]
+ CRUSH rule 3 x 1001 [2,3,6]
+ CRUSH rule 3 x 1002 [1,3,8]
+ CRUSH rule 3 x 1003 [2,7,3]
+ CRUSH rule 3 x 1004 [6,0,3]
+ CRUSH rule 3 x 1005 [6,1,4]
+ CRUSH rule 3 x 1006 [1,8,4]
+ CRUSH rule 3 x 1007 [1,3,8]
+ CRUSH rule 3 x 1008 [1,7,4]
+ CRUSH rule 3 x 1009 [6,5,1]
+ CRUSH rule 3 x 1010 [3,1,6]
+ CRUSH rule 3 x 1011 [3,0,8]
+ CRUSH rule 3 x 1012 [3,1,8]
+ CRUSH rule 3 x 1013 [5,2,8]
+ CRUSH rule 3 x 1014 [2,8,3]
+ CRUSH rule 3 x 1015 [6,3,2]
+ CRUSH rule 3 x 1016 [2,5,6]
+ CRUSH rule 3 x 1017 [6,1,5]
+ CRUSH rule 3 x 1018 [5,1,6]
+ CRUSH rule 3 x 1019 [5,8,1]
+ CRUSH rule 3 x 1020 [5,0,7]
+ CRUSH rule 3 x 1021 [5,2,7]
+ CRUSH rule 3 x 1022 [1,7,4]
+ CRUSH rule 3 x 1023 [3,0,7]
+ rule 3 (choose-set) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 4 (choose-set-two), x = 0..1023, numrep = 2..3
+ CRUSH rule 4 x 0 [0,2]
+ CRUSH rule 4 x 1 [0,8]
+ CRUSH rule 4 x 2 [1,3]
+ CRUSH rule 4 x 3 [8,0]
+ CRUSH rule 4 x 4 [5,4]
+ CRUSH rule 4 x 5 [7,8]
+ CRUSH rule 4 x 6 [2,6]
+ CRUSH rule 4 x 7 [5,4]
+ CRUSH rule 4 x 8 [5,3]
+ CRUSH rule 4 x 9 [2,3]
+ CRUSH rule 4 x 10 [0,2]
+ CRUSH rule 4 x 11 [0,7]
+ CRUSH rule 4 x 12 [0,2]
+ CRUSH rule 4 x 13 [3,8]
+ CRUSH rule 4 x 14 [7,6]
+ CRUSH rule 4 x 15 [7,2]
+ CRUSH rule 4 x 16 [3,6]
+ CRUSH rule 4 x 17 [5,4]
+ CRUSH rule 4 x 18 [1,4]
+ CRUSH rule 4 x 19 [7,5]
+ CRUSH rule 4 x 20 [2,4]
+ CRUSH rule 4 x 21 [3,7]
+ CRUSH rule 4 x 22 [8,3]
+ CRUSH rule 4 x 23 [3,6]
+ CRUSH rule 4 x 24 [1,0]
+ CRUSH rule 4 x 25 [3,7]
+ CRUSH rule 4 x 26 [2,8]
+ CRUSH rule 4 x 27 [3,1]
+ CRUSH rule 4 x 28 [6,0]
+ CRUSH rule 4 x 29 [8,5]
+ CRUSH rule 4 x 30 [5,7]
+ CRUSH rule 4 x 31 [8,7]
+ CRUSH rule 4 x 32 [3,6]
+ CRUSH rule 4 x 33 [2,7]
+ CRUSH rule 4 x 34 [2,5]
+ CRUSH rule 4 x 35 [0,8]
+ CRUSH rule 4 x 36 [3,8]
+ CRUSH rule 4 x 37 [0,4]
+ CRUSH rule 4 x 38 [4,8]
+ CRUSH rule 4 x 39 [3,7]
+ CRUSH rule 4 x 40 [7,8]
+ CRUSH rule 4 x 41 [0,2]
+ CRUSH rule 4 x 42 [4,3]
+ CRUSH rule 4 x 43 [0,3]
+ CRUSH rule 4 x 44 [1,6]
+ CRUSH rule 4 x 45 [8,0]
+ CRUSH rule 4 x 46 [2,4]
+ CRUSH rule 4 x 47 [4,5]
+ CRUSH rule 4 x 48 [4,6]
+ CRUSH rule 4 x 49 [5,4]
+ CRUSH rule 4 x 50 [3,4]
+ CRUSH rule 4 x 51 [3,6]
+ CRUSH rule 4 x 52 [8,6]
+ CRUSH rule 4 x 53 [3,5]
+ CRUSH rule 4 x 54 [7,6]
+ CRUSH rule 4 x 55 [8,7]
+ CRUSH rule 4 x 56 [6,4]
+ CRUSH rule 4 x 57 [5,3]
+ CRUSH rule 4 x 58 [1,0]
+ CRUSH rule 4 x 59 [4,2]
+ CRUSH rule 4 x 60 [3,5]
+ CRUSH rule 4 x 61 [4,6]
+ CRUSH rule 4 x 62 [7,0]
+ CRUSH rule 4 x 63 [5,6]
+ CRUSH rule 4 x 64 [4,5]
+ CRUSH rule 4 x 65 [7,3]
+ CRUSH rule 4 x 66 [5,4]
+ CRUSH rule 4 x 67 [5,0]
+ CRUSH rule 4 x 68 [0,5]
+ CRUSH rule 4 x 69 [5,1]
+ CRUSH rule 4 x 70 [7,0]
+ CRUSH rule 4 x 71 [2,8]
+ CRUSH rule 4 x 72 [6,1]
+ CRUSH rule 4 x 73 [2,7]
+ CRUSH rule 4 x 74 [0,7]
+ CRUSH rule 4 x 75 [3,2]
+ CRUSH rule 4 x 76 [5,1]
+ CRUSH rule 4 x 77 [7,2]
+ CRUSH rule 4 x 78 [1,4]
+ CRUSH rule 4 x 79 [5,4]
+ CRUSH rule 4 x 80 [0,3]
+ CRUSH rule 4 x 81 [0,2]
+ CRUSH rule 4 x 82 [7,1]
+ CRUSH rule 4 x 83 [2,6]
+ CRUSH rule 4 x 84 [7,2]
+ CRUSH rule 4 x 85 [3,4]
+ CRUSH rule 4 x 86 [0,2]
+ CRUSH rule 4 x 87 [0,7]
+ CRUSH rule 4 x 88 [1,6]
+ CRUSH rule 4 x 89 [3,0]
+ CRUSH rule 4 x 90 [6,7]
+ CRUSH rule 4 x 91 [3,8]
+ CRUSH rule 4 x 92 [1,8]
+ CRUSH rule 4 x 93 [7,4]
+ CRUSH rule 4 x 94 [0,4]
+ CRUSH rule 4 x 95 [7,5]
+ CRUSH rule 4 x 96 [3,6]
+ CRUSH rule 4 x 97 [8,7]
+ CRUSH rule 4 x 98 [2,0]
+ CRUSH rule 4 x 99 [0,7]
+ CRUSH rule 4 x 100 [1,7]
+ CRUSH rule 4 x 101 [3,7]
+ CRUSH rule 4 x 102 [4,2]
+ CRUSH rule 4 x 103 [4,7]
+ CRUSH rule 4 x 104 [7,4]
+ CRUSH rule 4 x 105 [2,4]
+ CRUSH rule 4 x 106 [1,6]
+ CRUSH rule 4 x 107 [3,2]
+ CRUSH rule 4 x 108 [7,2]
+ CRUSH rule 4 x 109 [1,2]
+ CRUSH rule 4 x 110 [3,2]
+ CRUSH rule 4 x 111 [2,1]
+ CRUSH rule 4 x 112 [2,0]
+ CRUSH rule 4 x 113 [6,2]
+ CRUSH rule 4 x 114 [7,6]
+ CRUSH rule 4 x 115 [8,2]
+ CRUSH rule 4 x 116 [1,2]
+ CRUSH rule 4 x 117 [7,3]
+ CRUSH rule 4 x 118 [0,3]
+ CRUSH rule 4 x 119 [5,6]
+ CRUSH rule 4 x 120 [0,2]
+ CRUSH rule 4 x 121 [2,0]
+ CRUSH rule 4 x 122 [8,5]
+ CRUSH rule 4 x 123 [2,5]
+ CRUSH rule 4 x 124 [3,5]
+ CRUSH rule 4 x 125 [0,7]
+ CRUSH rule 4 x 126 [4,2]
+ CRUSH rule 4 x 127 [3,6]
+ CRUSH rule 4 x 128 [3,5]
+ CRUSH rule 4 x 129 [0,2]
+ CRUSH rule 4 x 130 [3,8]
+ CRUSH rule 4 x 131 [1,2]
+ CRUSH rule 4 x 132 [1,2]
+ CRUSH rule 4 x 133 [3,6]
+ CRUSH rule 4 x 134 [1,8]
+ CRUSH rule 4 x 135 [5,6]
+ CRUSH rule 4 x 136 [2,1]
+ CRUSH rule 4 x 137 [7,3]
+ CRUSH rule 4 x 138 [8,7]
+ CRUSH rule 4 x 139 [3,0]
+ CRUSH rule 4 x 140 [1,6]
+ CRUSH rule 4 x 141 [6,8]
+ CRUSH rule 4 x 142 [3,5]
+ CRUSH rule 4 x 143 [5,8]
+ CRUSH rule 4 x 144 [8,1]
+ CRUSH rule 4 x 145 [8,5]
+ CRUSH rule 4 x 146 [2,6]
+ CRUSH rule 4 x 147 [2,8]
+ CRUSH rule 4 x 148 [3,4]
+ CRUSH rule 4 x 149 [4,8]
+ CRUSH rule 4 x 150 [1,6]
+ CRUSH rule 4 x 151 [3,4]
+ CRUSH rule 4 x 152 [8,4]
+ CRUSH rule 4 x 153 [8,6]
+ CRUSH rule 4 x 154 [3,2]
+ CRUSH rule 4 x 155 [3,5]
+ CRUSH rule 4 x 156 [4,5]
+ CRUSH rule 4 x 157 [4,1]
+ CRUSH rule 4 x 158 [2,8]
+ CRUSH rule 4 x 159 [7,0]
+ CRUSH rule 4 x 160 [2,8]
+ CRUSH rule 4 x 161 [1,5]
+ CRUSH rule 4 x 162 [0,6]
+ CRUSH rule 4 x 163 [5,6]
+ CRUSH rule 4 x 164 [7,8]
+ CRUSH rule 4 x 165 [7,0]
+ CRUSH rule 4 x 166 [2,4]
+ CRUSH rule 4 x 167 [0,1]
+ CRUSH rule 4 x 168 [4,2]
+ CRUSH rule 4 x 169 [2,6]
+ CRUSH rule 4 x 170 [1,0]
+ CRUSH rule 4 x 171 [7,5]
+ CRUSH rule 4 x 172 [0,7]
+ CRUSH rule 4 x 173 [8,5]
+ CRUSH rule 4 x 174 [1,4]
+ CRUSH rule 4 x 175 [6,0]
+ CRUSH rule 4 x 176 [4,3]
+ CRUSH rule 4 x 177 [5,3]
+ CRUSH rule 4 x 178 [3,0]
+ CRUSH rule 4 x 179 [4,1]
+ CRUSH rule 4 x 180 [3,8]
+ CRUSH rule 4 x 181 [6,2]
+ CRUSH rule 4 x 182 [8,5]
+ CRUSH rule 4 x 183 [7,8]
+ CRUSH rule 4 x 184 [5,7]
+ CRUSH rule 4 x 185 [6,8]
+ CRUSH rule 4 x 186 [2,1]
+ CRUSH rule 4 x 187 [1,6]
+ CRUSH rule 4 x 188 [1,8]
+ CRUSH rule 4 x 189 [0,7]
+ CRUSH rule 4 x 190 [4,0]
+ CRUSH rule 4 x 191 [7,6]
+ CRUSH rule 4 x 192 [5,0]
+ CRUSH rule 4 x 193 [4,2]
+ CRUSH rule 4 x 194 [1,3]
+ CRUSH rule 4 x 195 [6,4]
+ CRUSH rule 4 x 196 [6,7]
+ CRUSH rule 4 x 197 [6,5]
+ CRUSH rule 4 x 198 [2,5]
+ CRUSH rule 4 x 199 [0,5]
+ CRUSH rule 4 x 200 [0,5]
+ CRUSH rule 4 x 201 [7,1]
+ CRUSH rule 4 x 202 [6,3]
+ CRUSH rule 4 x 203 [4,8]
+ CRUSH rule 4 x 204 [2,1]
+ CRUSH rule 4 x 205 [0,7]
+ CRUSH rule 4 x 206 [0,1]
+ CRUSH rule 4 x 207 [3,0]
+ CRUSH rule 4 x 208 [7,1]
+ CRUSH rule 4 x 209 [1,2]
+ CRUSH rule 4 x 210 [1,2]
+ CRUSH rule 4 x 211 [5,4]
+ CRUSH rule 4 x 212 [7,5]
+ CRUSH rule 4 x 213 [8,4]
+ CRUSH rule 4 x 214 [4,5]
+ CRUSH rule 4 x 215 [8,1]
+ CRUSH rule 4 x 216 [5,0]
+ CRUSH rule 4 x 217 [0,1]
+ CRUSH rule 4 x 218 [0,7]
+ CRUSH rule 4 x 219 [4,8]
+ CRUSH rule 4 x 220 [5,7]
+ CRUSH rule 4 x 221 [3,6]
+ CRUSH rule 4 x 222 [6,8]
+ CRUSH rule 4 x 223 [1,3]
+ CRUSH rule 4 x 224 [1,5]
+ CRUSH rule 4 x 225 [8,6]
+ CRUSH rule 4 x 226 [7,2]
+ CRUSH rule 4 x 227 [3,4]
+ CRUSH rule 4 x 228 [5,4]
+ CRUSH rule 4 x 229 [3,4]
+ CRUSH rule 4 x 230 [4,7]
+ CRUSH rule 4 x 231 [4,3]
+ CRUSH rule 4 x 232 [2,7]
+ CRUSH rule 4 x 233 [3,4]
+ CRUSH rule 4 x 234 [0,1]
+ CRUSH rule 4 x 235 [3,8]
+ CRUSH rule 4 x 236 [5,2]
+ CRUSH rule 4 x 237 [4,7]
+ CRUSH rule 4 x 238 [4,3]
+ CRUSH rule 4 x 239 [8,7]
+ CRUSH rule 4 x 240 [5,7]
+ CRUSH rule 4 x 241 [3,1]
+ CRUSH rule 4 x 242 [3,5]
+ CRUSH rule 4 x 243 [4,7]
+ CRUSH rule 4 x 244 [4,6]
+ CRUSH rule 4 x 245 [7,6]
+ CRUSH rule 4 x 246 [1,5]
+ CRUSH rule 4 x 247 [6,0]
+ CRUSH rule 4 x 248 [8,0]
+ CRUSH rule 4 x 249 [2,0]
+ CRUSH rule 4 x 250 [2,1]
+ CRUSH rule 4 x 251 [2,3]
+ CRUSH rule 4 x 252 [3,7]
+ CRUSH rule 4 x 253 [3,2]
+ CRUSH rule 4 x 254 [3,5]
+ CRUSH rule 4 x 255 [1,7]
+ CRUSH rule 4 x 256 [5,7]
+ CRUSH rule 4 x 257 [2,8]
+ CRUSH rule 4 x 258 [5,3]
+ CRUSH rule 4 x 259 [4,3]
+ CRUSH rule 4 x 260 [3,6]
+ CRUSH rule 4 x 261 [8,7]
+ CRUSH rule 4 x 262 [5,4]
+ CRUSH rule 4 x 263 [6,8]
+ CRUSH rule 4 x 264 [3,6]
+ CRUSH rule 4 x 265 [8,6]
+ CRUSH rule 4 x 266 [8,2]
+ CRUSH rule 4 x 267 [2,3]
+ CRUSH rule 4 x 268 [0,7]
+ CRUSH rule 4 x 269 [0,8]
+ CRUSH rule 4 x 270 [5,0]
+ CRUSH rule 4 x 271 [7,5]
+ CRUSH rule 4 x 272 [2,8]
+ CRUSH rule 4 x 273 [3,5]
+ CRUSH rule 4 x 274 [6,8]
+ CRUSH rule 4 x 275 [4,3]
+ CRUSH rule 4 x 276 [7,1]
+ CRUSH rule 4 x 277 [6,4]
+ CRUSH rule 4 x 278 [6,8]
+ CRUSH rule 4 x 279 [8,3]
+ CRUSH rule 4 x 280 [0,6]
+ CRUSH rule 4 x 281 [8,0]
+ CRUSH rule 4 x 282 [3,1]
+ CRUSH rule 4 x 283 [8,2]
+ CRUSH rule 4 x 284 [6,3]
+ CRUSH rule 4 x 285 [5,3]
+ CRUSH rule 4 x 286 [2,1]
+ CRUSH rule 4 x 287 [0,4]
+ CRUSH rule 4 x 288 [8,0]
+ CRUSH rule 4 x 289 [4,6]
+ CRUSH rule 4 x 290 [1,3]
+ CRUSH rule 4 x 291 [0,1]
+ CRUSH rule 4 x 292 [8,0]
+ CRUSH rule 4 x 293 [6,0]
+ CRUSH rule 4 x 294 [7,4]
+ CRUSH rule 4 x 295 [4,8]
+ CRUSH rule 4 x 296 [3,1]
+ CRUSH rule 4 x 297 [6,2]
+ CRUSH rule 4 x 298 [1,2]
+ CRUSH rule 4 x 299 [2,0]
+ CRUSH rule 4 x 300 [8,7]
+ CRUSH rule 4 x 301 [0,8]
+ CRUSH rule 4 x 302 [3,0]
+ CRUSH rule 4 x 303 [7,5]
+ CRUSH rule 4 x 304 [2,7]
+ CRUSH rule 4 x 305 [5,8]
+ CRUSH rule 4 x 306 [0,7]
+ CRUSH rule 4 x 307 [0,2]
+ CRUSH rule 4 x 308 [0,8]
+ CRUSH rule 4 x 309 [7,4]
+ CRUSH rule 4 x 310 [4,3]
+ CRUSH rule 4 x 311 [3,4]
+ CRUSH rule 4 x 312 [2,1]
+ CRUSH rule 4 x 313 [5,3]
+ CRUSH rule 4 x 314 [4,5]
+ CRUSH rule 4 x 315 [2,0]
+ CRUSH rule 4 x 316 [6,3]
+ CRUSH rule 4 x 317 [2,6]
+ CRUSH rule 4 x 318 [8,6]
+ CRUSH rule 4 x 319 [5,0]
+ CRUSH rule 4 x 320 [3,7]
+ CRUSH rule 4 x 321 [1,3]
+ CRUSH rule 4 x 322 [2,7]
+ CRUSH rule 4 x 323 [4,7]
+ CRUSH rule 4 x 324 [7,0]
+ CRUSH rule 4 x 325 [4,6]
+ CRUSH rule 4 x 326 [3,4]
+ CRUSH rule 4 x 327 [0,6]
+ CRUSH rule 4 x 328 [7,4]
+ CRUSH rule 4 x 329 [5,6]
+ CRUSH rule 4 x 330 [3,7]
+ CRUSH rule 4 x 331 [2,6]
+ CRUSH rule 4 x 332 [2,0]
+ CRUSH rule 4 x 333 [6,8]
+ CRUSH rule 4 x 334 [8,3]
+ CRUSH rule 4 x 335 [7,1]
+ CRUSH rule 4 x 336 [4,6]
+ CRUSH rule 4 x 337 [7,2]
+ CRUSH rule 4 x 338 [5,6]
+ CRUSH rule 4 x 339 [7,5]
+ CRUSH rule 4 x 340 [2,0]
+ CRUSH rule 4 x 341 [5,1]
+ CRUSH rule 4 x 342 [0,7]
+ CRUSH rule 4 x 343 [6,7]
+ CRUSH rule 4 x 344 [6,0]
+ CRUSH rule 4 x 345 [4,3]
+ CRUSH rule 4 x 346 [8,0]
+ CRUSH rule 4 x 347 [3,1]
+ CRUSH rule 4 x 348 [8,0]
+ CRUSH rule 4 x 349 [1,6]
+ CRUSH rule 4 x 350 [8,5]
+ CRUSH rule 4 x 351 [3,6]
+ CRUSH rule 4 x 352 [1,0]
+ CRUSH rule 4 x 353 [6,4]
+ CRUSH rule 4 x 354 [0,3]
+ CRUSH rule 4 x 355 [3,4]
+ CRUSH rule 4 x 356 [3,5]
+ CRUSH rule 4 x 357 [6,1]
+ CRUSH rule 4 x 358 [2,1]
+ CRUSH rule 4 x 359 [6,7]
+ CRUSH rule 4 x 360 [5,3]
+ CRUSH rule 4 x 361 [8,4]
+ CRUSH rule 4 x 362 [4,5]
+ CRUSH rule 4 x 363 [4,0]
+ CRUSH rule 4 x 364 [2,5]
+ CRUSH rule 4 x 365 [6,7]
+ CRUSH rule 4 x 366 [7,2]
+ CRUSH rule 4 x 367 [4,5]
+ CRUSH rule 4 x 368 [7,4]
+ CRUSH rule 4 x 369 [3,7]
+ CRUSH rule 4 x 370 [8,7]
+ CRUSH rule 4 x 371 [1,3]
+ CRUSH rule 4 x 372 [3,1]
+ CRUSH rule 4 x 373 [0,6]
+ CRUSH rule 4 x 374 [3,8]
+ CRUSH rule 4 x 375 [6,4]
+ CRUSH rule 4 x 376 [7,1]
+ CRUSH rule 4 x 377 [1,2]
+ CRUSH rule 4 x 378 [0,1]
+ CRUSH rule 4 x 379 [8,5]
+ CRUSH rule 4 x 380 [2,5]
+ CRUSH rule 4 x 381 [0,4]
+ CRUSH rule 4 x 382 [1,5]
+ CRUSH rule 4 x 383 [4,3]
+ CRUSH rule 4 x 384 [7,0]
+ CRUSH rule 4 x 385 [7,4]
+ CRUSH rule 4 x 386 [0,3]
+ CRUSH rule 4 x 387 [1,3]
+ CRUSH rule 4 x 388 [5,0]
+ CRUSH rule 4 x 389 [1,5]
+ CRUSH rule 4 x 390 [5,6]
+ CRUSH rule 4 x 391 [5,6]
+ CRUSH rule 4 x 392 [1,8]
+ CRUSH rule 4 x 393 [4,2]
+ CRUSH rule 4 x 394 [4,7]
+ CRUSH rule 4 x 395 [4,0]
+ CRUSH rule 4 x 396 [4,2]
+ CRUSH rule 4 x 397 [2,1]
+ CRUSH rule 4 x 398 [2,4]
+ CRUSH rule 4 x 399 [8,7]
+ CRUSH rule 4 x 400 [8,1]
+ CRUSH rule 4 x 401 [0,1]
+ CRUSH rule 4 x 402 [7,8]
+ CRUSH rule 4 x 403 [0,1]
+ CRUSH rule 4 x 404 [4,3]
+ CRUSH rule 4 x 405 [6,5]
+ CRUSH rule 4 x 406 [2,0]
+ CRUSH rule 4 x 407 [2,8]
+ CRUSH rule 4 x 408 [4,1]
+ CRUSH rule 4 x 409 [7,3]
+ CRUSH rule 4 x 410 [8,6]
+ CRUSH rule 4 x 411 [2,0]
+ CRUSH rule 4 x 412 [0,5]
+ CRUSH rule 4 x 413 [5,0]
+ CRUSH rule 4 x 414 [4,1]
+ CRUSH rule 4 x 415 [0,6]
+ CRUSH rule 4 x 416 [2,1]
+ CRUSH rule 4 x 417 [8,7]
+ CRUSH rule 4 x 418 [7,6]
+ CRUSH rule 4 x 419 [8,3]
+ CRUSH rule 4 x 420 [1,4]
+ CRUSH rule 4 x 421 [8,6]
+ CRUSH rule 4 x 422 [6,7]
+ CRUSH rule 4 x 423 [0,5]
+ CRUSH rule 4 x 424 [8,4]
+ CRUSH rule 4 x 425 [1,3]
+ CRUSH rule 4 x 426 [6,7]
+ CRUSH rule 4 x 427 [0,7]
+ CRUSH rule 4 x 428 [5,4]
+ CRUSH rule 4 x 429 [4,6]
+ CRUSH rule 4 x 430 [3,6]
+ CRUSH rule 4 x 431 [5,3]
+ CRUSH rule 4 x 432 [7,1]
+ CRUSH rule 4 x 433 [6,5]
+ CRUSH rule 4 x 434 [5,2]
+ CRUSH rule 4 x 435 [0,5]
+ CRUSH rule 4 x 436 [4,0]
+ CRUSH rule 4 x 437 [7,5]
+ CRUSH rule 4 x 438 [0,3]
+ CRUSH rule 4 x 439 [1,3]
+ CRUSH rule 4 x 440 [2,7]
+ CRUSH rule 4 x 441 [5,7]
+ CRUSH rule 4 x 442 [2,4]
+ CRUSH rule 4 x 443 [6,8]
+ CRUSH rule 4 x 444 [7,0]
+ CRUSH rule 4 x 445 [6,3]
+ CRUSH rule 4 x 446 [4,3]
+ CRUSH rule 4 x 447 [2,1]
+ CRUSH rule 4 x 448 [7,2]
+ CRUSH rule 4 x 449 [7,8]
+ CRUSH rule 4 x 450 [4,5]
+ CRUSH rule 4 x 451 [6,8]
+ CRUSH rule 4 x 452 [8,3]
+ CRUSH rule 4 x 453 [6,8]
+ CRUSH rule 4 x 454 [6,7]
+ CRUSH rule 4 x 455 [2,7]
+ CRUSH rule 4 x 456 [6,8]
+ CRUSH rule 4 x 457 [7,2]
+ CRUSH rule 4 x 458 [2,8]
+ CRUSH rule 4 x 459 [2,0]
+ CRUSH rule 4 x 460 [6,5]
+ CRUSH rule 4 x 461 [6,5]
+ CRUSH rule 4 x 462 [8,1]
+ CRUSH rule 4 x 463 [6,7]
+ CRUSH rule 4 x 464 [7,4]
+ CRUSH rule 4 x 465 [7,6]
+ CRUSH rule 4 x 466 [5,8]
+ CRUSH rule 4 x 467 [6,4]
+ CRUSH rule 4 x 468 [7,8]
+ CRUSH rule 4 x 469 [7,0]
+ CRUSH rule 4 x 470 [3,0]
+ CRUSH rule 4 x 471 [0,1]
+ CRUSH rule 4 x 472 [5,4]
+ CRUSH rule 4 x 473 [1,0]
+ CRUSH rule 4 x 474 [6,0]
+ CRUSH rule 4 x 475 [6,7]
+ CRUSH rule 4 x 476 [4,3]
+ CRUSH rule 4 x 477 [5,8]
+ CRUSH rule 4 x 478 [6,7]
+ CRUSH rule 4 x 479 [0,5]
+ CRUSH rule 4 x 480 [1,8]
+ CRUSH rule 4 x 481 [2,4]
+ CRUSH rule 4 x 482 [4,3]
+ CRUSH rule 4 x 483 [0,2]
+ CRUSH rule 4 x 484 [1,2]
+ CRUSH rule 4 x 485 [4,7]
+ CRUSH rule 4 x 486 [4,1]
+ CRUSH rule 4 x 487 [5,0]
+ CRUSH rule 4 x 488 [5,7]
+ CRUSH rule 4 x 489 [2,8]
+ CRUSH rule 4 x 490 [6,4]
+ CRUSH rule 4 x 491 [1,0]
+ CRUSH rule 4 x 492 [6,5]
+ CRUSH rule 4 x 493 [0,2]
+ CRUSH rule 4 x 494 [1,0]
+ CRUSH rule 4 x 495 [3,4]
+ CRUSH rule 4 x 496 [7,5]
+ CRUSH rule 4 x 497 [5,7]
+ CRUSH rule 4 x 498 [0,5]
+ CRUSH rule 4 x 499 [8,4]
+ CRUSH rule 4 x 500 [3,6]
+ CRUSH rule 4 x 501 [0,7]
+ CRUSH rule 4 x 502 [7,1]
+ CRUSH rule 4 x 503 [2,3]
+ CRUSH rule 4 x 504 [5,6]
+ CRUSH rule 4 x 505 [0,7]
+ CRUSH rule 4 x 506 [5,3]
+ CRUSH rule 4 x 507 [6,0]
+ CRUSH rule 4 x 508 [0,1]
+ CRUSH rule 4 x 509 [7,5]
+ CRUSH rule 4 x 510 [6,0]
+ CRUSH rule 4 x 511 [5,8]
+ CRUSH rule 4 x 512 [7,6]
+ CRUSH rule 4 x 513 [7,2]
+ CRUSH rule 4 x 514 [4,3]
+ CRUSH rule 4 x 515 [8,5]
+ CRUSH rule 4 x 516 [4,0]
+ CRUSH rule 4 x 517 [7,8]
+ CRUSH rule 4 x 518 [4,6]
+ CRUSH rule 4 x 519 [7,3]
+ CRUSH rule 4 x 520 [2,6]
+ CRUSH rule 4 x 521 [8,7]
+ CRUSH rule 4 x 522 [6,8]
+ CRUSH rule 4 x 523 [4,2]
+ CRUSH rule 4 x 524 [0,4]
+ CRUSH rule 4 x 525 [0,4]
+ CRUSH rule 4 x 526 [1,5]
+ CRUSH rule 4 x 527 [0,2]
+ CRUSH rule 4 x 528 [5,3]
+ CRUSH rule 4 x 529 [5,7]
+ CRUSH rule 4 x 530 [6,7]
+ CRUSH rule 4 x 531 [6,1]
+ CRUSH rule 4 x 532 [6,3]
+ CRUSH rule 4 x 533 [5,6]
+ CRUSH rule 4 x 534 [7,3]
+ CRUSH rule 4 x 535 [8,6]
+ CRUSH rule 4 x 536 [6,7]
+ CRUSH rule 4 x 537 [3,7]
+ CRUSH rule 4 x 538 [6,8]
+ CRUSH rule 4 x 539 [8,3]
+ CRUSH rule 4 x 540 [0,6]
+ CRUSH rule 4 x 541 [2,3]
+ CRUSH rule 4 x 542 [3,5]
+ CRUSH rule 4 x 543 [6,0]
+ CRUSH rule 4 x 544 [3,7]
+ CRUSH rule 4 x 545 [5,7]
+ CRUSH rule 4 x 546 [6,1]
+ CRUSH rule 4 x 547 [8,2]
+ CRUSH rule 4 x 548 [5,2]
+ CRUSH rule 4 x 549 [5,8]
+ CRUSH rule 4 x 550 [0,5]
+ CRUSH rule 4 x 551 [7,5]
+ CRUSH rule 4 x 552 [5,4]
+ CRUSH rule 4 x 553 [4,2]
+ CRUSH rule 4 x 554 [0,8]
+ CRUSH rule 4 x 555 [5,0]
+ CRUSH rule 4 x 556 [3,4]
+ CRUSH rule 4 x 557 [7,4]
+ CRUSH rule 4 x 558 [3,1]
+ CRUSH rule 4 x 559 [4,2]
+ CRUSH rule 4 x 560 [8,3]
+ CRUSH rule 4 x 561 [6,3]
+ CRUSH rule 4 x 562 [3,4]
+ CRUSH rule 4 x 563 [2,6]
+ CRUSH rule 4 x 564 [5,1]
+ CRUSH rule 4 x 565 [3,6]
+ CRUSH rule 4 x 566 [4,7]
+ CRUSH rule 4 x 567 [3,6]
+ CRUSH rule 4 x 568 [7,4]
+ CRUSH rule 4 x 569 [3,4]
+ CRUSH rule 4 x 570 [1,5]
+ CRUSH rule 4 x 571 [3,7]
+ CRUSH rule 4 x 572 [3,4]
+ CRUSH rule 4 x 573 [3,0]
+ CRUSH rule 4 x 574 [2,0]
+ CRUSH rule 4 x 575 [8,6]
+ CRUSH rule 4 x 576 [4,6]
+ CRUSH rule 4 x 577 [8,2]
+ CRUSH rule 4 x 578 [6,8]
+ CRUSH rule 4 x 579 [3,1]
+ CRUSH rule 4 x 580 [3,0]
+ CRUSH rule 4 x 581 [7,2]
+ CRUSH rule 4 x 582 [2,8]
+ CRUSH rule 4 x 583 [6,0]
+ CRUSH rule 4 x 584 [8,1]
+ CRUSH rule 4 x 585 [7,0]
+ CRUSH rule 4 x 586 [0,1]
+ CRUSH rule 4 x 587 [2,5]
+ CRUSH rule 4 x 588 [3,4]
+ CRUSH rule 4 x 589 [7,1]
+ CRUSH rule 4 x 590 [6,2]
+ CRUSH rule 4 x 591 [5,2]
+ CRUSH rule 4 x 592 [2,0]
+ CRUSH rule 4 x 593 [0,8]
+ CRUSH rule 4 x 594 [0,7]
+ CRUSH rule 4 x 595 [7,1]
+ CRUSH rule 4 x 596 [4,3]
+ CRUSH rule 4 x 597 [3,1]
+ CRUSH rule 4 x 598 [3,2]
+ CRUSH rule 4 x 599 [5,2]
+ CRUSH rule 4 x 600 [7,0]
+ CRUSH rule 4 x 601 [0,7]
+ CRUSH rule 4 x 602 [3,7]
+ CRUSH rule 4 x 603 [5,1]
+ CRUSH rule 4 x 604 [7,5]
+ CRUSH rule 4 x 605 [3,0]
+ CRUSH rule 4 x 606 [2,0]
+ CRUSH rule 4 x 607 [0,4]
+ CRUSH rule 4 x 608 [5,3]
+ CRUSH rule 4 x 609 [5,2]
+ CRUSH rule 4 x 610 [3,7]
+ CRUSH rule 4 x 611 [1,0]
+ CRUSH rule 4 x 612 [2,0]
+ CRUSH rule 4 x 613 [7,2]
+ CRUSH rule 4 x 614 [7,8]
+ CRUSH rule 4 x 615 [6,8]
+ CRUSH rule 4 x 616 [0,8]
+ CRUSH rule 4 x 617 [6,1]
+ CRUSH rule 4 x 618 [7,6]
+ CRUSH rule 4 x 619 [5,1]
+ CRUSH rule 4 x 620 [4,1]
+ CRUSH rule 4 x 621 [5,8]
+ CRUSH rule 4 x 622 [0,4]
+ CRUSH rule 4 x 623 [0,6]
+ CRUSH rule 4 x 624 [3,5]
+ CRUSH rule 4 x 625 [2,3]
+ CRUSH rule 4 x 626 [7,8]
+ CRUSH rule 4 x 627 [2,7]
+ CRUSH rule 4 x 628 [8,0]
+ CRUSH rule 4 x 629 [2,6]
+ CRUSH rule 4 x 630 [2,7]
+ CRUSH rule 4 x 631 [0,7]
+ CRUSH rule 4 x 632 [7,0]
+ CRUSH rule 4 x 633 [8,6]
+ CRUSH rule 4 x 634 [0,4]
+ CRUSH rule 4 x 635 [5,6]
+ CRUSH rule 4 x 636 [1,4]
+ CRUSH rule 4 x 637 [4,1]
+ CRUSH rule 4 x 638 [6,8]
+ CRUSH rule 4 x 639 [4,2]
+ CRUSH rule 4 x 640 [3,1]
+ CRUSH rule 4 x 641 [7,2]
+ CRUSH rule 4 x 642 [2,0]
+ CRUSH rule 4 x 643 [3,5]
+ CRUSH rule 4 x 644 [8,1]
+ CRUSH rule 4 x 645 [5,4]
+ CRUSH rule 4 x 646 [8,0]
+ CRUSH rule 4 x 647 [7,1]
+ CRUSH rule 4 x 648 [0,6]
+ CRUSH rule 4 x 649 [4,7]
+ CRUSH rule 4 x 650 [7,8]
+ CRUSH rule 4 x 651 [3,7]
+ CRUSH rule 4 x 652 [3,4]
+ CRUSH rule 4 x 653 [8,5]
+ CRUSH rule 4 x 654 [7,5]
+ CRUSH rule 4 x 655 [0,3]
+ CRUSH rule 4 x 656 [4,3]
+ CRUSH rule 4 x 657 [6,1]
+ CRUSH rule 4 x 658 [5,4]
+ CRUSH rule 4 x 659 [4,6]
+ CRUSH rule 4 x 660 [7,8]
+ CRUSH rule 4 x 661 [1,8]
+ CRUSH rule 4 x 662 [4,5]
+ CRUSH rule 4 x 663 [1,3]
+ CRUSH rule 4 x 664 [1,4]
+ CRUSH rule 4 x 665 [5,7]
+ CRUSH rule 4 x 666 [2,8]
+ CRUSH rule 4 x 667 [1,3]
+ CRUSH rule 4 x 668 [3,7]
+ CRUSH rule 4 x 669 [6,4]
+ CRUSH rule 4 x 670 [4,0]
+ CRUSH rule 4 x 671 [0,2]
+ CRUSH rule 4 x 672 [4,3]
+ CRUSH rule 4 x 673 [5,3]
+ CRUSH rule 4 x 674 [3,1]
+ CRUSH rule 4 x 675 [0,8]
+ CRUSH rule 4 x 676 [0,2]
+ CRUSH rule 4 x 677 [4,1]
+ CRUSH rule 4 x 678 [2,3]
+ CRUSH rule 4 x 679 [6,0]
+ CRUSH rule 4 x 680 [0,4]
+ CRUSH rule 4 x 681 [4,7]
+ CRUSH rule 4 x 682 [0,5]
+ CRUSH rule 4 x 683 [0,1]
+ CRUSH rule 4 x 684 [7,1]
+ CRUSH rule 4 x 685 [7,1]
+ CRUSH rule 4 x 686 [1,4]
+ CRUSH rule 4 x 687 [3,5]
+ CRUSH rule 4 x 688 [5,7]
+ CRUSH rule 4 x 689 [6,5]
+ CRUSH rule 4 x 690 [8,1]
+ CRUSH rule 4 x 691 [3,0]
+ CRUSH rule 4 x 692 [7,2]
+ CRUSH rule 4 x 693 [6,7]
+ CRUSH rule 4 x 694 [6,5]
+ CRUSH rule 4 x 695 [0,8]
+ CRUSH rule 4 x 696 [1,4]
+ CRUSH rule 4 x 697 [6,1]
+ CRUSH rule 4 x 698 [6,2]
+ CRUSH rule 4 x 699 [1,6]
+ CRUSH rule 4 x 700 [0,3]
+ CRUSH rule 4 x 701 [4,3]
+ CRUSH rule 4 x 702 [3,5]
+ CRUSH rule 4 x 703 [8,3]
+ CRUSH rule 4 x 704 [0,3]
+ CRUSH rule 4 x 705 [8,6]
+ CRUSH rule 4 x 706 [1,2]
+ CRUSH rule 4 x 707 [7,8]
+ CRUSH rule 4 x 708 [3,5]
+ CRUSH rule 4 x 709 [6,3]
+ CRUSH rule 4 x 710 [8,4]
+ CRUSH rule 4 x 711 [2,3]
+ CRUSH rule 4 x 712 [2,3]
+ CRUSH rule 4 x 713 [6,7]
+ CRUSH rule 4 x 714 [3,2]
+ CRUSH rule 4 x 715 [1,2]
+ CRUSH rule 4 x 716 [3,6]
+ CRUSH rule 4 x 717 [8,7]
+ CRUSH rule 4 x 718 [3,7]
+ CRUSH rule 4 x 719 [2,6]
+ CRUSH rule 4 x 720 [6,8]
+ CRUSH rule 4 x 721 [5,4]
+ CRUSH rule 4 x 722 [5,4]
+ CRUSH rule 4 x 723 [5,1]
+ CRUSH rule 4 x 724 [0,6]
+ CRUSH rule 4 x 725 [0,1]
+ CRUSH rule 4 x 726 [3,8]
+ CRUSH rule 4 x 727 [4,6]
+ CRUSH rule 4 x 728 [2,1]
+ CRUSH rule 4 x 729 [5,3]
+ CRUSH rule 4 x 730 [3,7]
+ CRUSH rule 4 x 731 [4,1]
+ CRUSH rule 4 x 732 [1,5]
+ CRUSH rule 4 x 733 [5,4]
+ CRUSH rule 4 x 734 [6,4]
+ CRUSH rule 4 x 735 [4,8]
+ CRUSH rule 4 x 736 [3,5]
+ CRUSH rule 4 x 737 [1,0]
+ CRUSH rule 4 x 738 [5,2]
+ CRUSH rule 4 x 739 [0,1]
+ CRUSH rule 4 x 740 [0,1]
+ CRUSH rule 4 x 741 [7,8]
+ CRUSH rule 4 x 742 [8,2]
+ CRUSH rule 4 x 743 [7,0]
+ CRUSH rule 4 x 744 [4,7]
+ CRUSH rule 4 x 745 [3,4]
+ CRUSH rule 4 x 746 [4,1]
+ CRUSH rule 4 x 747 [6,0]
+ CRUSH rule 4 x 748 [2,7]
+ CRUSH rule 4 x 749 [4,5]
+ CRUSH rule 4 x 750 [1,6]
+ CRUSH rule 4 x 751 [2,1]
+ CRUSH rule 4 x 752 [8,1]
+ CRUSH rule 4 x 753 [7,8]
+ CRUSH rule 4 x 754 [8,6]
+ CRUSH rule 4 x 755 [1,2]
+ CRUSH rule 4 x 756 [5,6]
+ CRUSH rule 4 x 757 [8,6]
+ CRUSH rule 4 x 758 [6,0]
+ CRUSH rule 4 x 759 [8,5]
+ CRUSH rule 4 x 760 [1,5]
+ CRUSH rule 4 x 761 [4,1]
+ CRUSH rule 4 x 762 [2,7]
+ CRUSH rule 4 x 763 [8,6]
+ CRUSH rule 4 x 764 [1,7]
+ CRUSH rule 4 x 765 [6,5]
+ CRUSH rule 4 x 766 [8,5]
+ CRUSH rule 4 x 767 [1,2]
+ CRUSH rule 4 x 768 [8,3]
+ CRUSH rule 4 x 769 [6,2]
+ CRUSH rule 4 x 770 [6,0]
+ CRUSH rule 4 x 771 [7,0]
+ CRUSH rule 4 x 772 [8,3]
+ CRUSH rule 4 x 773 [3,1]
+ CRUSH rule 4 x 774 [4,6]
+ CRUSH rule 4 x 775 [6,8]
+ CRUSH rule 4 x 776 [7,2]
+ CRUSH rule 4 x 777 [3,1]
+ CRUSH rule 4 x 778 [1,8]
+ CRUSH rule 4 x 779 [2,7]
+ CRUSH rule 4 x 780 [0,2]
+ CRUSH rule 4 x 781 [6,3]
+ CRUSH rule 4 x 782 [5,4]
+ CRUSH rule 4 x 783 [7,1]
+ CRUSH rule 4 x 784 [0,1]
+ CRUSH rule 4 x 785 [6,1]
+ CRUSH rule 4 x 786 [7,6]
+ CRUSH rule 4 x 787 [1,0]
+ CRUSH rule 4 x 788 [6,0]
+ CRUSH rule 4 x 789 [0,4]
+ CRUSH rule 4 x 790 [8,4]
+ CRUSH rule 4 x 791 [3,8]
+ CRUSH rule 4 x 792 [5,8]
+ CRUSH rule 4 x 793 [6,1]
+ CRUSH rule 4 x 794 [2,6]
+ CRUSH rule 4 x 795 [0,3]
+ CRUSH rule 4 x 796 [3,4]
+ CRUSH rule 4 x 797 [2,3]
+ CRUSH rule 4 x 798 [6,8]
+ CRUSH rule 4 x 799 [5,1]
+ CRUSH rule 4 x 800 [5,0]
+ CRUSH rule 4 x 801 [3,6]
+ CRUSH rule 4 x 802 [1,8]
+ CRUSH rule 4 x 803 [0,5]
+ CRUSH rule 4 x 804 [6,2]
+ CRUSH rule 4 x 805 [3,6]
+ CRUSH rule 4 x 806 [1,3]
+ CRUSH rule 4 x 807 [5,4]
+ CRUSH rule 4 x 808 [4,6]
+ CRUSH rule 4 x 809 [1,4]
+ CRUSH rule 4 x 810 [5,7]
+ CRUSH rule 4 x 811 [8,4]
+ CRUSH rule 4 x 812 [8,5]
+ CRUSH rule 4 x 813 [6,4]
+ CRUSH rule 4 x 814 [3,6]
+ CRUSH rule 4 x 815 [3,1]
+ CRUSH rule 4 x 816 [2,1]
+ CRUSH rule 4 x 817 [4,3]
+ CRUSH rule 4 x 818 [3,5]
+ CRUSH rule 4 x 819 [5,1]
+ CRUSH rule 4 x 820 [3,5]
+ CRUSH rule 4 x 821 [4,5]
+ CRUSH rule 4 x 822 [2,0]
+ CRUSH rule 4 x 823 [4,8]
+ CRUSH rule 4 x 824 [3,7]
+ CRUSH rule 4 x 825 [2,8]
+ CRUSH rule 4 x 826 [7,0]
+ CRUSH rule 4 x 827 [0,8]
+ CRUSH rule 4 x 828 [2,3]
+ CRUSH rule 4 x 829 [5,6]
+ CRUSH rule 4 x 830 [2,3]
+ CRUSH rule 4 x 831 [1,6]
+ CRUSH rule 4 x 832 [4,5]
+ CRUSH rule 4 x 833 [2,1]
+ CRUSH rule 4 x 834 [3,4]
+ CRUSH rule 4 x 835 [8,4]
+ CRUSH rule 4 x 836 [3,4]
+ CRUSH rule 4 x 837 [6,3]
+ CRUSH rule 4 x 838 [6,7]
+ CRUSH rule 4 x 839 [5,0]
+ CRUSH rule 4 x 840 [7,8]
+ CRUSH rule 4 x 841 [4,8]
+ CRUSH rule 4 x 842 [2,4]
+ CRUSH rule 4 x 843 [6,4]
+ CRUSH rule 4 x 844 [4,8]
+ CRUSH rule 4 x 845 [3,8]
+ CRUSH rule 4 x 846 [3,2]
+ CRUSH rule 4 x 847 [0,2]
+ CRUSH rule 4 x 848 [2,6]
+ CRUSH rule 4 x 849 [4,5]
+ CRUSH rule 4 x 850 [1,0]
+ CRUSH rule 4 x 851 [6,8]
+ CRUSH rule 4 x 852 [7,3]
+ CRUSH rule 4 x 853 [6,8]
+ CRUSH rule 4 x 854 [7,6]
+ CRUSH rule 4 x 855 [5,7]
+ CRUSH rule 4 x 856 [6,7]
+ CRUSH rule 4 x 857 [8,5]
+ CRUSH rule 4 x 858 [6,4]
+ CRUSH rule 4 x 859 [6,0]
+ CRUSH rule 4 x 860 [4,1]
+ CRUSH rule 4 x 861 [8,7]
+ CRUSH rule 4 x 862 [6,1]
+ CRUSH rule 4 x 863 [8,7]
+ CRUSH rule 4 x 864 [5,6]
+ CRUSH rule 4 x 865 [8,1]
+ CRUSH rule 4 x 866 [3,4]
+ CRUSH rule 4 x 867 [6,5]
+ CRUSH rule 4 x 868 [6,3]
+ CRUSH rule 4 x 869 [8,7]
+ CRUSH rule 4 x 870 [0,4]
+ CRUSH rule 4 x 871 [3,4]
+ CRUSH rule 4 x 872 [5,1]
+ CRUSH rule 4 x 873 [4,6]
+ CRUSH rule 4 x 874 [2,6]
+ CRUSH rule 4 x 875 [2,6]
+ CRUSH rule 4 x 876 [5,8]
+ CRUSH rule 4 x 877 [6,4]
+ CRUSH rule 4 x 878 [5,4]
+ CRUSH rule 4 x 879 [7,4]
+ CRUSH rule 4 x 880 [3,5]
+ CRUSH rule 4 x 881 [5,6]
+ CRUSH rule 4 x 882 [4,0]
+ CRUSH rule 4 x 883 [2,1]
+ CRUSH rule 4 x 884 [6,0]
+ CRUSH rule 4 x 885 [5,1]
+ CRUSH rule 4 x 886 [3,6]
+ CRUSH rule 4 x 887 [7,4]
+ CRUSH rule 4 x 888 [6,8]
+ CRUSH rule 4 x 889 [2,1]
+ CRUSH rule 4 x 890 [7,2]
+ CRUSH rule 4 x 891 [1,8]
+ CRUSH rule 4 x 892 [6,2]
+ CRUSH rule 4 x 893 [2,3]
+ CRUSH rule 4 x 894 [7,5]
+ CRUSH rule 4 x 895 [5,3]
+ CRUSH rule 4 x 896 [1,8]
+ CRUSH rule 4 x 897 [4,2]
+ CRUSH rule 4 x 898 [0,5]
+ CRUSH rule 4 x 899 [1,7]
+ CRUSH rule 4 x 900 [4,1]
+ CRUSH rule 4 x 901 [5,0]
+ CRUSH rule 4 x 902 [8,5]
+ CRUSH rule 4 x 903 [5,7]
+ CRUSH rule 4 x 904 [5,6]
+ CRUSH rule 4 x 905 [6,2]
+ CRUSH rule 4 x 906 [1,2]
+ CRUSH rule 4 x 907 [7,1]
+ CRUSH rule 4 x 908 [5,8]
+ CRUSH rule 4 x 909 [2,3]
+ CRUSH rule 4 x 910 [6,4]
+ CRUSH rule 4 x 911 [5,8]
+ CRUSH rule 4 x 912 [0,1]
+ CRUSH rule 4 x 913 [7,6]
+ CRUSH rule 4 x 914 [6,4]
+ CRUSH rule 4 x 915 [8,2]
+ CRUSH rule 4 x 916 [3,1]
+ CRUSH rule 4 x 917 [1,5]
+ CRUSH rule 4 x 918 [8,2]
+ CRUSH rule 4 x 919 [6,2]
+ CRUSH rule 4 x 920 [7,6]
+ CRUSH rule 4 x 921 [1,4]
+ CRUSH rule 4 x 922 [6,7]
+ CRUSH rule 4 x 923 [5,3]
+ CRUSH rule 4 x 924 [3,5]
+ CRUSH rule 4 x 925 [5,7]
+ CRUSH rule 4 x 926 [3,4]
+ CRUSH rule 4 x 927 [1,6]
+ CRUSH rule 4 x 928 [8,1]
+ CRUSH rule 4 x 929 [4,5]
+ CRUSH rule 4 x 930 [2,4]
+ CRUSH rule 4 x 931 [5,0]
+ CRUSH rule 4 x 932 [4,3]
+ CRUSH rule 4 x 933 [8,5]
+ CRUSH rule 4 x 934 [5,3]
+ CRUSH rule 4 x 935 [6,3]
+ CRUSH rule 4 x 936 [0,6]
+ CRUSH rule 4 x 937 [5,4]
+ CRUSH rule 4 x 938 [6,5]
+ CRUSH rule 4 x 939 [2,7]
+ CRUSH rule 4 x 940 [8,7]
+ CRUSH rule 4 x 941 [5,2]
+ CRUSH rule 4 x 942 [1,0]
+ CRUSH rule 4 x 943 [8,2]
+ CRUSH rule 4 x 944 [4,3]
+ CRUSH rule 4 x 945 [7,2]
+ CRUSH rule 4 x 946 [2,0]
+ CRUSH rule 4 x 947 [4,5]
+ CRUSH rule 4 x 948 [7,8]
+ CRUSH rule 4 x 949 [6,1]
+ CRUSH rule 4 x 950 [3,5]
+ CRUSH rule 4 x 951 [4,5]
+ CRUSH rule 4 x 952 [2,0]
+ CRUSH rule 4 x 953 [1,3]
+ CRUSH rule 4 x 954 [4,2]
+ CRUSH rule 4 x 955 [8,6]
+ CRUSH rule 4 x 956 [1,0]
+ CRUSH rule 4 x 957 [7,6]
+ CRUSH rule 4 x 958 [8,7]
+ CRUSH rule 4 x 959 [5,2]
+ CRUSH rule 4 x 960 [3,6]
+ CRUSH rule 4 x 961 [4,0]
+ CRUSH rule 4 x 962 [7,4]
+ CRUSH rule 4 x 963 [0,5]
+ CRUSH rule 4 x 964 [3,1]
+ CRUSH rule 4 x 965 [7,6]
+ CRUSH rule 4 x 966 [3,8]
+ CRUSH rule 4 x 967 [8,6]
+ CRUSH rule 4 x 968 [7,2]
+ CRUSH rule 4 x 969 [8,0]
+ CRUSH rule 4 x 970 [0,6]
+ CRUSH rule 4 x 971 [1,7]
+ CRUSH rule 4 x 972 [1,8]
+ CRUSH rule 4 x 973 [1,2]
+ CRUSH rule 4 x 974 [5,3]
+ CRUSH rule 4 x 975 [3,7]
+ CRUSH rule 4 x 976 [4,3]
+ CRUSH rule 4 x 977 [8,3]
+ CRUSH rule 4 x 978 [7,2]
+ CRUSH rule 4 x 979 [7,6]
+ CRUSH rule 4 x 980 [6,0]
+ CRUSH rule 4 x 981 [7,3]
+ CRUSH rule 4 x 982 [4,2]
+ CRUSH rule 4 x 983 [3,5]
+ CRUSH rule 4 x 984 [0,2]
+ CRUSH rule 4 x 985 [2,5]
+ CRUSH rule 4 x 986 [8,7]
+ CRUSH rule 4 x 987 [0,5]
+ CRUSH rule 4 x 988 [1,3]
+ CRUSH rule 4 x 989 [0,6]
+ CRUSH rule 4 x 990 [1,0]
+ CRUSH rule 4 x 991 [0,4]
+ CRUSH rule 4 x 992 [7,1]
+ CRUSH rule 4 x 993 [0,6]
+ CRUSH rule 4 x 994 [3,4]
+ CRUSH rule 4 x 995 [7,6]
+ CRUSH rule 4 x 996 [6,7]
+ CRUSH rule 4 x 997 [6,4]
+ CRUSH rule 4 x 998 [8,1]
+ CRUSH rule 4 x 999 [0,7]
+ CRUSH rule 4 x 1000 [8,5]
+ CRUSH rule 4 x 1001 [2,0]
+ CRUSH rule 4 x 1002 [1,3]
+ CRUSH rule 4 x 1003 [2,8]
+ CRUSH rule 4 x 1004 [6,1]
+ CRUSH rule 4 x 1005 [6,1]
+ CRUSH rule 4 x 1006 [1,0]
+ CRUSH rule 4 x 1007 [1,2]
+ CRUSH rule 4 x 1008 [1,7]
+ CRUSH rule 4 x 1009 [6,8]
+ CRUSH rule 4 x 1010 [3,4]
+ CRUSH rule 4 x 1011 [3,0]
+ CRUSH rule 4 x 1012 [3,0]
+ CRUSH rule 4 x 1013 [5,1]
+ CRUSH rule 4 x 1014 [2,8]
+ CRUSH rule 4 x 1015 [6,8]
+ CRUSH rule 4 x 1016 [2,0]
+ CRUSH rule 4 x 1017 [6,0]
+ CRUSH rule 4 x 1018 [5,4]
+ CRUSH rule 4 x 1019 [5,3]
+ CRUSH rule 4 x 1020 [5,1]
+ CRUSH rule 4 x 1021 [5,2]
+ CRUSH rule 4 x 1022 [1,6]
+ CRUSH rule 4 x 1023 [3,2]
+ rule 4 (choose-set-two) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 4 x 0 [0,2,3]
+ CRUSH rule 4 x 1 [0,8,2]
+ CRUSH rule 4 x 2 [1,3,5]
+ CRUSH rule 4 x 3 [8,0,6]
+ CRUSH rule 4 x 4 [5,4,0]
+ CRUSH rule 4 x 5 [7,8,0]
+ CRUSH rule 4 x 6 [2,6,7]
+ CRUSH rule 4 x 7 [5,4,3]
+ CRUSH rule 4 x 8 [5,3,8]
+ CRUSH rule 4 x 9 [2,3,1]
+ CRUSH rule 4 x 10 [0,2,8]
+ CRUSH rule 4 x 11 [0,7,2]
+ CRUSH rule 4 x 12 [0,2,4]
+ CRUSH rule 4 x 13 [3,8,5]
+ CRUSH rule 4 x 14 [7,6,8]
+ CRUSH rule 4 x 15 [7,2,4]
+ CRUSH rule 4 x 16 [3,6,0]
+ CRUSH rule 4 x 17 [5,4,0]
+ CRUSH rule 4 x 18 [1,4,5]
+ CRUSH rule 4 x 19 [7,5,4]
+ CRUSH rule 4 x 20 [2,4,7]
+ CRUSH rule 4 x 21 [3,7,2]
+ CRUSH rule 4 x 22 [8,3,7]
+ CRUSH rule 4 x 23 [3,6,8]
+ CRUSH rule 4 x 24 [1,0,6]
+ CRUSH rule 4 x 25 [3,7,0]
+ CRUSH rule 4 x 26 [2,8,7]
+ CRUSH rule 4 x 27 [3,1,5]
+ CRUSH rule 4 x 28 [6,0,3]
+ CRUSH rule 4 x 29 [8,5,7]
+ CRUSH rule 4 x 30 [5,7,3]
+ CRUSH rule 4 x 31 [8,7,6]
+ CRUSH rule 4 x 32 [3,6,8]
+ CRUSH rule 4 x 33 [2,7,8]
+ CRUSH rule 4 x 34 [2,5,4]
+ CRUSH rule 4 x 35 [0,8,1]
+ CRUSH rule 4 x 36 [3,8,2]
+ CRUSH rule 4 x 37 [0,4,5]
+ CRUSH rule 4 x 38 [4,8,7]
+ CRUSH rule 4 x 39 [3,7,0]
+ CRUSH rule 4 x 40 [7,8,1]
+ CRUSH rule 4 x 41 [0,2,6]
+ CRUSH rule 4 x 42 [4,3,6]
+ CRUSH rule 4 x 43 [0,3,7]
+ CRUSH rule 4 x 44 [1,6,7]
+ CRUSH rule 4 x 45 [8,0,4]
+ CRUSH rule 4 x 46 [2,4,3]
+ CRUSH rule 4 x 47 [4,5,3]
+ CRUSH rule 4 x 48 [4,6,5]
+ CRUSH rule 4 x 49 [5,4,8]
+ CRUSH rule 4 x 50 [3,4,1]
+ CRUSH rule 4 x 51 [3,6,0]
+ CRUSH rule 4 x 52 [8,6,1]
+ CRUSH rule 4 x 53 [3,5,4]
+ CRUSH rule 4 x 54 [7,6,8]
+ CRUSH rule 4 x 55 [8,7,0]
+ CRUSH rule 4 x 56 [6,4,8]
+ CRUSH rule 4 x 57 [5,3,6]
+ CRUSH rule 4 x 58 [1,0,2]
+ CRUSH rule 4 x 59 [4,2,1]
+ CRUSH rule 4 x 60 [3,5,0]
+ CRUSH rule 4 x 61 [4,6,8]
+ CRUSH rule 4 x 62 [7,0,1]
+ CRUSH rule 4 x 63 [5,6,8]
+ CRUSH rule 4 x 64 [4,5,1]
+ CRUSH rule 4 x 65 [7,3,6]
+ CRUSH rule 4 x 66 [5,4,3]
+ CRUSH rule 4 x 67 [5,0,8]
+ CRUSH rule 4 x 68 [0,5,8]
+ CRUSH rule 4 x 69 [5,1,6]
+ CRUSH rule 4 x 70 [7,0,2]
+ CRUSH rule 4 x 71 [2,8,6]
+ CRUSH rule 4 x 72 [6,1,5]
+ CRUSH rule 4 x 73 [2,7,3]
+ CRUSH rule 4 x 74 [0,7,1]
+ CRUSH rule 4 x 75 [3,2,0]
+ CRUSH rule 4 x 76 [5,1,7]
+ CRUSH rule 4 x 77 [7,2,6]
+ CRUSH rule 4 x 78 [1,4,2]
+ CRUSH rule 4 x 79 [5,4,0]
+ CRUSH rule 4 x 80 [0,3,5]
+ CRUSH rule 4 x 81 [0,2,5]
+ CRUSH rule 4 x 82 [7,1,6]
+ CRUSH rule 4 x 83 [2,6,8]
+ CRUSH rule 4 x 84 [7,2,1]
+ CRUSH rule 4 x 85 [3,4,8]
+ CRUSH rule 4 x 86 [0,2,1]
+ CRUSH rule 4 x 87 [0,7,4]
+ CRUSH rule 4 x 88 [1,6,0]
+ CRUSH rule 4 x 89 [3,0,2]
+ CRUSH rule 4 x 90 [6,7,5]
+ CRUSH rule 4 x 91 [3,8,7]
+ CRUSH rule 4 x 92 [1,8,4]
+ CRUSH rule 4 x 93 [7,4,5]
+ CRUSH rule 4 x 94 [0,4,3]
+ CRUSH rule 4 x 95 [7,5,1]
+ CRUSH rule 4 x 96 [3,6,4]
+ CRUSH rule 4 x 97 [8,7,4]
+ CRUSH rule 4 x 98 [2,0,7]
+ CRUSH rule 4 x 99 [0,7,2]
+ CRUSH rule 4 x 100 [1,7,4]
+ CRUSH rule 4 x 101 [3,7,2]
+ CRUSH rule 4 x 102 [4,2,7]
+ CRUSH rule 4 x 103 [4,7,6]
+ CRUSH rule 4 x 104 [7,4,3]
+ CRUSH rule 4 x 105 [2,4,1]
+ CRUSH rule 4 x 106 [1,6,2]
+ CRUSH rule 4 x 107 [3,2,1]
+ CRUSH rule 4 x 108 [7,2,8]
+ CRUSH rule 4 x 109 [1,2,5]
+ CRUSH rule 4 x 110 [3,2,7]
+ CRUSH rule 4 x 111 [2,1,0]
+ CRUSH rule 4 x 112 [2,0,6]
+ CRUSH rule 4 x 113 [6,2,1]
+ CRUSH rule 4 x 114 [7,6,3]
+ CRUSH rule 4 x 115 [8,2,3]
+ CRUSH rule 4 x 116 [1,2,6]
+ CRUSH rule 4 x 117 [7,3,6]
+ CRUSH rule 4 x 118 [0,3,8]
+ CRUSH rule 4 x 119 [5,6,1]
+ CRUSH rule 4 x 120 [0,2,5]
+ CRUSH rule 4 x 121 [2,0,1]
+ CRUSH rule 4 x 122 [8,5,0]
+ CRUSH rule 4 x 123 [2,5,1]
+ CRUSH rule 4 x 124 [3,5,1]
+ CRUSH rule 4 x 125 [0,7,3]
+ CRUSH rule 4 x 126 [4,2,6]
+ CRUSH rule 4 x 127 [3,6,7]
+ CRUSH rule 4 x 128 [3,5,8]
+ CRUSH rule 4 x 129 [0,2,1]
+ CRUSH rule 4 x 130 [3,8,5]
+ CRUSH rule 4 x 131 [1,2,0]
+ CRUSH rule 4 x 132 [1,2,3]
+ CRUSH rule 4 x 133 [3,6,7]
+ CRUSH rule 4 x 134 [1,8,2]
+ CRUSH rule 4 x 135 [5,6,4]
+ CRUSH rule 4 x 136 [2,1,5]
+ CRUSH rule 4 x 137 [7,3,4]
+ CRUSH rule 4 x 138 [8,7,6]
+ CRUSH rule 4 x 139 [3,0,5]
+ CRUSH rule 4 x 140 [1,6,7]
+ CRUSH rule 4 x 141 [6,8,1]
+ CRUSH rule 4 x 142 [3,5,0]
+ CRUSH rule 4 x 143 [5,8,7]
+ CRUSH rule 4 x 144 [8,1,0]
+ CRUSH rule 4 x 145 [8,5,6]
+ CRUSH rule 4 x 146 [2,6,4]
+ CRUSH rule 4 x 147 [2,8,7]
+ CRUSH rule 4 x 148 [3,4,0]
+ CRUSH rule 4 x 149 [4,8,6]
+ CRUSH rule 4 x 150 [1,6,5]
+ CRUSH rule 4 x 151 [3,4,5]
+ CRUSH rule 4 x 152 [8,4,6]
+ CRUSH rule 4 x 153 [8,6,7]
+ CRUSH rule 4 x 154 [3,2,0]
+ CRUSH rule 4 x 155 [3,5,8]
+ CRUSH rule 4 x 156 [4,5,3]
+ CRUSH rule 4 x 157 [4,1,2]
+ CRUSH rule 4 x 158 [2,8,1]
+ CRUSH rule 4 x 159 [7,0,5]
+ CRUSH rule 4 x 160 [2,8,1]
+ CRUSH rule 4 x 161 [1,5,2]
+ CRUSH rule 4 x 162 [0,6,2]
+ CRUSH rule 4 x 163 [5,6,1]
+ CRUSH rule 4 x 164 [7,8,1]
+ CRUSH rule 4 x 165 [7,0,5]
+ CRUSH rule 4 x 166 [2,4,1]
+ CRUSH rule 4 x 167 [0,1,6]
+ CRUSH rule 4 x 168 [4,2,3]
+ CRUSH rule 4 x 169 [2,6,1]
+ CRUSH rule 4 x 170 [1,0,5]
+ CRUSH rule 4 x 171 [7,5,4]
+ CRUSH rule 4 x 172 [0,7,8]
+ CRUSH rule 4 x 173 [8,5,3]
+ CRUSH rule 4 x 174 [1,4,7]
+ CRUSH rule 4 x 175 [6,0,4]
+ CRUSH rule 4 x 176 [4,3,5]
+ CRUSH rule 4 x 177 [5,3,0]
+ CRUSH rule 4 x 178 [3,0,2]
+ CRUSH rule 4 x 179 [4,1,2]
+ CRUSH rule 4 x 180 [3,8,7]
+ CRUSH rule 4 x 181 [6,2,0]
+ CRUSH rule 4 x 182 [8,5,3]
+ CRUSH rule 4 x 183 [7,8,6]
+ CRUSH rule 4 x 184 [5,7,3]
+ CRUSH rule 4 x 185 [6,8,0]
+ CRUSH rule 4 x 186 [2,1,4]
+ CRUSH rule 4 x 187 [1,6,7]
+ CRUSH rule 4 x 188 [1,8,7]
+ CRUSH rule 4 x 189 [0,7,8]
+ CRUSH rule 4 x 190 [4,0,1]
+ CRUSH rule 4 x 191 [7,6,8]
+ CRUSH rule 4 x 192 [5,0,1]
+ CRUSH rule 4 x 193 [4,2,6]
+ CRUSH rule 4 x 194 [1,3,4]
+ CRUSH rule 4 x 195 [6,4,3]
+ CRUSH rule 4 x 196 [6,7,2]
+ CRUSH rule 4 x 197 [6,5,2]
+ CRUSH rule 4 x 198 [2,5,6]
+ CRUSH rule 4 x 199 [0,5,7]
+ CRUSH rule 4 x 200 [0,5,1]
+ CRUSH rule 4 x 201 [7,1,0]
+ CRUSH rule 4 x 202 [6,3,4]
+ CRUSH rule 4 x 203 [4,8,7]
+ CRUSH rule 4 x 204 [2,1,5]
+ CRUSH rule 4 x 205 [0,7,6]
+ CRUSH rule 4 x 206 [0,1,7]
+ CRUSH rule 4 x 207 [3,0,5]
+ CRUSH rule 4 x 208 [7,1,0]
+ CRUSH rule 4 x 209 [1,2,0]
+ CRUSH rule 4 x 210 [1,2,3]
+ CRUSH rule 4 x 211 [5,4,2]
+ CRUSH rule 4 x 212 [7,5,0]
+ CRUSH rule 4 x 213 [8,4,0]
+ CRUSH rule 4 x 214 [4,5,7]
+ CRUSH rule 4 x 215 [8,1,7]
+ CRUSH rule 4 x 216 [5,0,2]
+ CRUSH rule 4 x 217 [0,1,7]
+ CRUSH rule 4 x 218 [0,7,8]
+ CRUSH rule 4 x 219 [4,8,2]
+ CRUSH rule 4 x 220 [5,7,3]
+ CRUSH rule 4 x 221 [3,6,5]
+ CRUSH rule 4 x 222 [6,8,3]
+ CRUSH rule 4 x 223 [1,3,6]
+ CRUSH rule 4 x 224 [1,5,0]
+ CRUSH rule 4 x 225 [8,6,2]
+ CRUSH rule 4 x 226 [7,2,3]
+ CRUSH rule 4 x 227 [3,4,0]
+ CRUSH rule 4 x 228 [5,4,8]
+ CRUSH rule 4 x 229 [3,4,7]
+ CRUSH rule 4 x 230 [4,7,2]
+ CRUSH rule 4 x 231 [4,3,5]
+ CRUSH rule 4 x 232 [2,7,4]
+ CRUSH rule 4 x 233 [3,4,5]
+ CRUSH rule 4 x 234 [0,1,2]
+ CRUSH rule 4 x 235 [3,8,0]
+ CRUSH rule 4 x 236 [5,2,7]
+ CRUSH rule 4 x 237 [4,7,6]
+ CRUSH rule 4 x 238 [4,3,5]
+ CRUSH rule 4 x 239 [8,7,6]
+ CRUSH rule 4 x 240 [5,7,4]
+ CRUSH rule 4 x 241 [3,1,5]
+ CRUSH rule 4 x 242 [3,5,4]
+ CRUSH rule 4 x 243 [4,7,3]
+ CRUSH rule 4 x 244 [4,6,0]
+ CRUSH rule 4 x 245 [7,6,1]
+ CRUSH rule 4 x 246 [1,5,2]
+ CRUSH rule 4 x 247 [6,0,2]
+ CRUSH rule 4 x 248 [8,0,7]
+ CRUSH rule 4 x 249 [2,0,1]
+ CRUSH rule 4 x 250 [2,1,0]
+ CRUSH rule 4 x 251 [2,3,6]
+ CRUSH rule 4 x 252 [3,7,8]
+ CRUSH rule 4 x 253 [3,2,4]
+ CRUSH rule 4 x 254 [3,5,4]
+ CRUSH rule 4 x 255 [1,7,8]
+ CRUSH rule 4 x 256 [5,7,0]
+ CRUSH rule 4 x 257 [2,8,4]
+ CRUSH rule 4 x 258 [5,3,0]
+ CRUSH rule 4 x 259 [4,3,6]
+ CRUSH rule 4 x 260 [3,6,2]
+ CRUSH rule 4 x 261 [8,7,4]
+ CRUSH rule 4 x 262 [5,4,8]
+ CRUSH rule 4 x 263 [6,8,2]
+ CRUSH rule 4 x 264 [3,6,5]
+ CRUSH rule 4 x 265 [8,6,7]
+ CRUSH rule 4 x 266 [8,2,1]
+ CRUSH rule 4 x 267 [2,3,0]
+ CRUSH rule 4 x 268 [0,7,2]
+ CRUSH rule 4 x 269 [0,8,2]
+ CRUSH rule 4 x 270 [5,0,1]
+ CRUSH rule 4 x 271 [7,5,4]
+ CRUSH rule 4 x 272 [2,8,3]
+ CRUSH rule 4 x 273 [3,5,0]
+ CRUSH rule 4 x 274 [6,8,3]
+ CRUSH rule 4 x 275 [4,3,6]
+ CRUSH rule 4 x 276 [7,1,2]
+ CRUSH rule 4 x 277 [6,4,0]
+ CRUSH rule 4 x 278 [6,8,0]
+ CRUSH rule 4 x 279 [8,3,6]
+ CRUSH rule 4 x 280 [0,6,2]
+ CRUSH rule 4 x 281 [8,0,2]
+ CRUSH rule 4 x 282 [3,1,6]
+ CRUSH rule 4 x 283 [8,2,0]
+ CRUSH rule 4 x 284 [6,3,0]
+ CRUSH rule 4 x 285 [5,3,7]
+ CRUSH rule 4 x 286 [2,1,6]
+ CRUSH rule 4 x 287 [0,4,1]
+ CRUSH rule 4 x 288 [8,0,7]
+ CRUSH rule 4 x 289 [4,6,2]
+ CRUSH rule 4 x 290 [1,3,7]
+ CRUSH rule 4 x 291 [0,1,4]
+ CRUSH rule 4 x 292 [8,0,2]
+ CRUSH rule 4 x 293 [6,0,8]
+ CRUSH rule 4 x 294 [7,4,5]
+ CRUSH rule 4 x 295 [4,8,7]
+ CRUSH rule 4 x 296 [3,1,5]
+ CRUSH rule 4 x 297 [6,2,8]
+ CRUSH rule 4 x 298 [1,2,3]
+ CRUSH rule 4 x 299 [2,0,7]
+ CRUSH rule 4 x 300 [8,7,3]
+ CRUSH rule 4 x 301 [0,8,1]
+ CRUSH rule 4 x 302 [3,0,6]
+ CRUSH rule 4 x 303 [7,5,8]
+ CRUSH rule 4 x 304 [2,7,5]
+ CRUSH rule 4 x 305 [5,8,2]
+ CRUSH rule 4 x 306 [0,7,1]
+ CRUSH rule 4 x 307 [0,2,8]
+ CRUSH rule 4 x 308 [0,8,4]
+ CRUSH rule 4 x 309 [7,4,5]
+ CRUSH rule 4 x 310 [4,3,1]
+ CRUSH rule 4 x 311 [3,4,5]
+ CRUSH rule 4 x 312 [2,1,0]
+ CRUSH rule 4 x 313 [5,3,4]
+ CRUSH rule 4 x 314 [4,5,0]
+ CRUSH rule 4 x 315 [2,0,1]
+ CRUSH rule 4 x 316 [6,3,8]
+ CRUSH rule 4 x 317 [2,6,0]
+ CRUSH rule 4 x 318 [8,6,7]
+ CRUSH rule 4 x 319 [5,0,8]
+ CRUSH rule 4 x 320 [3,7,1]
+ CRUSH rule 4 x 321 [1,3,0]
+ CRUSH rule 4 x 322 [2,7,3]
+ CRUSH rule 4 x 323 [4,7,0]
+ CRUSH rule 4 x 324 [7,0,8]
+ CRUSH rule 4 x 325 [4,6,0]
+ CRUSH rule 4 x 326 [3,4,1]
+ CRUSH rule 4 x 327 [0,6,7]
+ CRUSH rule 4 x 328 [7,4,8]
+ CRUSH rule 4 x 329 [5,6,3]
+ CRUSH rule 4 x 330 [3,7,4]
+ CRUSH rule 4 x 331 [2,6,3]
+ CRUSH rule 4 x 332 [2,0,3]
+ CRUSH rule 4 x 333 [6,8,5]
+ CRUSH rule 4 x 334 [8,3,5]
+ CRUSH rule 4 x 335 [7,1,2]
+ CRUSH rule 4 x 336 [4,6,0]
+ CRUSH rule 4 x 337 [7,2,6]
+ CRUSH rule 4 x 338 [5,6,4]
+ CRUSH rule 4 x 339 [7,5,2]
+ CRUSH rule 4 x 340 [2,0,1]
+ CRUSH rule 4 x 341 [5,1,7]
+ CRUSH rule 4 x 342 [0,7,4]
+ CRUSH rule 4 x 343 [6,7,3]
+ CRUSH rule 4 x 344 [6,0,5]
+ CRUSH rule 4 x 345 [4,3,5]
+ CRUSH rule 4 x 346 [8,0,5]
+ CRUSH rule 4 x 347 [3,1,5]
+ CRUSH rule 4 x 348 [8,0,2]
+ CRUSH rule 4 x 349 [1,6,7]
+ CRUSH rule 4 x 350 [8,5,7]
+ CRUSH rule 4 x 351 [3,6,4]
+ CRUSH rule 4 x 352 [1,0,2]
+ CRUSH rule 4 x 353 [6,4,5]
+ CRUSH rule 4 x 354 [0,3,1]
+ CRUSH rule 4 x 355 [3,4,6]
+ CRUSH rule 4 x 356 [3,5,4]
+ CRUSH rule 4 x 357 [6,1,2]
+ CRUSH rule 4 x 358 [2,1,8]
+ CRUSH rule 4 x 359 [6,7,8]
+ CRUSH rule 4 x 360 [5,3,1]
+ CRUSH rule 4 x 361 [8,4,5]
+ CRUSH rule 4 x 362 [4,5,3]
+ CRUSH rule 4 x 363 [4,0,3]
+ CRUSH rule 4 x 364 [2,5,0]
+ CRUSH rule 4 x 365 [6,7,8]
+ CRUSH rule 4 x 366 [7,2,8]
+ CRUSH rule 4 x 367 [4,5,0]
+ CRUSH rule 4 x 368 [7,4,6]
+ CRUSH rule 4 x 369 [3,7,1]
+ CRUSH rule 4 x 370 [8,7,6]
+ CRUSH rule 4 x 371 [1,3,5]
+ CRUSH rule 4 x 372 [3,1,8]
+ CRUSH rule 4 x 373 [0,6,8]
+ CRUSH rule 4 x 374 [3,8,4]
+ CRUSH rule 4 x 375 [6,4,5]
+ CRUSH rule 4 x 376 [7,1,2]
+ CRUSH rule 4 x 377 [1,2,4]
+ CRUSH rule 4 x 378 [0,1,2]
+ CRUSH rule 4 x 379 [8,5,4]
+ CRUSH rule 4 x 380 [2,5,0]
+ CRUSH rule 4 x 381 [0,4,7]
+ CRUSH rule 4 x 382 [1,5,2]
+ CRUSH rule 4 x 383 [4,3,5]
+ CRUSH rule 4 x 384 [7,0,6]
+ CRUSH rule 4 x 385 [7,4,6]
+ CRUSH rule 4 x 386 [0,3,5]
+ CRUSH rule 4 x 387 [1,3,6]
+ CRUSH rule 4 x 388 [5,0,3]
+ CRUSH rule 4 x 389 [1,5,4]
+ CRUSH rule 4 x 390 [5,6,0]
+ CRUSH rule 4 x 391 [5,6,2]
+ CRUSH rule 4 x 392 [1,8,6]
+ CRUSH rule 4 x 393 [4,2,6]
+ CRUSH rule 4 x 394 [4,7,6]
+ CRUSH rule 4 x 395 [4,0,1]
+ CRUSH rule 4 x 396 [4,2,5]
+ CRUSH rule 4 x 397 [2,1,5]
+ CRUSH rule 4 x 398 [2,4,5]
+ CRUSH rule 4 x 399 [8,7,3]
+ CRUSH rule 4 x 400 [8,1,4]
+ CRUSH rule 4 x 401 [0,1,2]
+ CRUSH rule 4 x 402 [7,8,6]
+ CRUSH rule 4 x 403 [0,1,4]
+ CRUSH rule 4 x 404 [4,3,2]
+ CRUSH rule 4 x 405 [6,5,2]
+ CRUSH rule 4 x 406 [2,0,1]
+ CRUSH rule 4 x 407 [2,8,7]
+ CRUSH rule 4 x 408 [4,1,6]
+ CRUSH rule 4 x 409 [7,3,6]
+ CRUSH rule 4 x 410 [8,6,3]
+ CRUSH rule 4 x 411 [2,0,6]
+ CRUSH rule 4 x 412 [0,5,8]
+ CRUSH rule 4 x 413 [5,0,8]
+ CRUSH rule 4 x 414 [4,1,3]
+ CRUSH rule 4 x 415 [0,6,1]
+ CRUSH rule 4 x 416 [2,1,7]
+ CRUSH rule 4 x 417 [8,7,2]
+ CRUSH rule 4 x 418 [7,6,8]
+ CRUSH rule 4 x 419 [8,3,0]
+ CRUSH rule 4 x 420 [1,4,2]
+ CRUSH rule 4 x 421 [8,6,7]
+ CRUSH rule 4 x 422 [6,7,8]
+ CRUSH rule 4 x 423 [0,5,6]
+ CRUSH rule 4 x 424 [8,4,7]
+ CRUSH rule 4 x 425 [1,3,5]
+ CRUSH rule 4 x 426 [6,7,2]
+ CRUSH rule 4 x 427 [0,7,5]
+ CRUSH rule 4 x 428 [5,4,3]
+ CRUSH rule 4 x 429 [4,6,7]
+ CRUSH rule 4 x 430 [3,6,5]
+ CRUSH rule 4 x 431 [5,3,0]
+ CRUSH rule 4 x 432 [7,1,2]
+ CRUSH rule 4 x 433 [6,5,1]
+ CRUSH rule 4 x 434 [5,2,1]
+ CRUSH rule 4 x 435 [0,5,3]
+ CRUSH rule 4 x 436 [4,0,5]
+ CRUSH rule 4 x 437 [7,5,3]
+ CRUSH rule 4 x 438 [0,3,8]
+ CRUSH rule 4 x 439 [1,3,4]
+ CRUSH rule 4 x 440 [2,7,1]
+ CRUSH rule 4 x 441 [5,7,2]
+ CRUSH rule 4 x 442 [2,4,5]
+ CRUSH rule 4 x 443 [6,8,0]
+ CRUSH rule 4 x 444 [7,0,1]
+ CRUSH rule 4 x 445 [6,3,5]
+ CRUSH rule 4 x 446 [4,3,5]
+ CRUSH rule 4 x 447 [2,1,4]
+ CRUSH rule 4 x 448 [7,2,5]
+ CRUSH rule 4 x 449 [7,8,3]
+ CRUSH rule 4 x 450 [4,5,2]
+ CRUSH rule 4 x 451 [6,8,3]
+ CRUSH rule 4 x 452 [8,3,5]
+ CRUSH rule 4 x 453 [6,8,7]
+ CRUSH rule 4 x 454 [6,7,5]
+ CRUSH rule 4 x 455 [2,7,5]
+ CRUSH rule 4 x 456 [6,8,7]
+ CRUSH rule 4 x 457 [7,2,8]
+ CRUSH rule 4 x 458 [2,8,1]
+ CRUSH rule 4 x 459 [2,0,6]
+ CRUSH rule 4 x 460 [6,5,7]
+ CRUSH rule 4 x 461 [6,5,4]
+ CRUSH rule 4 x 462 [8,1,5]
+ CRUSH rule 4 x 463 [6,7,2]
+ CRUSH rule 4 x 464 [7,4,2]
+ CRUSH rule 4 x 465 [7,6,0]
+ CRUSH rule 4 x 466 [5,8,4]
+ CRUSH rule 4 x 467 [6,4,7]
+ CRUSH rule 4 x 468 [7,8,0]
+ CRUSH rule 4 x 469 [7,0,1]
+ CRUSH rule 4 x 470 [3,0,5]
+ CRUSH rule 4 x 471 [0,1,2]
+ CRUSH rule 4 x 472 [5,4,0]
+ CRUSH rule 4 x 473 [1,0,4]
+ CRUSH rule 4 x 474 [6,0,7]
+ CRUSH rule 4 x 475 [6,7,8]
+ CRUSH rule 4 x 476 [4,3,5]
+ CRUSH rule 4 x 477 [5,8,6]
+ CRUSH rule 4 x 478 [6,7,2]
+ CRUSH rule 4 x 479 [0,5,8]
+ CRUSH rule 4 x 480 [1,8,6]
+ CRUSH rule 4 x 481 [2,4,5]
+ CRUSH rule 4 x 482 [4,3,5]
+ CRUSH rule 4 x 483 [0,2,6]
+ CRUSH rule 4 x 484 [1,2,7]
+ CRUSH rule 4 x 485 [4,7,3]
+ CRUSH rule 4 x 486 [4,1,7]
+ CRUSH rule 4 x 487 [5,0,1]
+ CRUSH rule 4 x 488 [5,7,1]
+ CRUSH rule 4 x 489 [2,8,5]
+ CRUSH rule 4 x 490 [6,4,1]
+ CRUSH rule 4 x 491 [1,0,6]
+ CRUSH rule 4 x 492 [6,5,4]
+ CRUSH rule 4 x 493 [0,2,7]
+ CRUSH rule 4 x 494 [1,0,8]
+ CRUSH rule 4 x 495 [3,4,5]
+ CRUSH rule 4 x 496 [7,5,4]
+ CRUSH rule 4 x 497 [5,7,3]
+ CRUSH rule 4 x 498 [0,5,8]
+ CRUSH rule 4 x 499 [8,4,3]
+ CRUSH rule 4 x 500 [3,6,5]
+ CRUSH rule 4 x 501 [0,7,2]
+ CRUSH rule 4 x 502 [7,1,4]
+ CRUSH rule 4 x 503 [2,3,5]
+ CRUSH rule 4 x 504 [5,6,3]
+ CRUSH rule 4 x 505 [0,7,6]
+ CRUSH rule 4 x 506 [5,3,0]
+ CRUSH rule 4 x 507 [6,0,8]
+ CRUSH rule 4 x 508 [0,1,2]
+ CRUSH rule 4 x 509 [7,5,8]
+ CRUSH rule 4 x 510 [6,0,2]
+ CRUSH rule 4 x 511 [5,8,7]
+ CRUSH rule 4 x 512 [7,6,2]
+ CRUSH rule 4 x 513 [7,2,1]
+ CRUSH rule 4 x 514 [4,3,5]
+ CRUSH rule 4 x 515 [8,5,6]
+ CRUSH rule 4 x 516 [4,0,1]
+ CRUSH rule 4 x 517 [7,8,6]
+ CRUSH rule 4 x 518 [4,6,7]
+ CRUSH rule 4 x 519 [7,3,5]
+ CRUSH rule 4 x 520 [2,6,3]
+ CRUSH rule 4 x 521 [8,7,6]
+ CRUSH rule 4 x 522 [6,8,0]
+ CRUSH rule 4 x 523 [4,2,5]
+ CRUSH rule 4 x 524 [0,4,5]
+ CRUSH rule 4 x 525 [0,4,1]
+ CRUSH rule 4 x 526 [1,5,8]
+ CRUSH rule 4 x 527 [0,2,4]
+ CRUSH rule 4 x 528 [5,3,0]
+ CRUSH rule 4 x 529 [5,7,0]
+ CRUSH rule 4 x 530 [6,7,8]
+ CRUSH rule 4 x 531 [6,1,0]
+ CRUSH rule 4 x 532 [6,3,7]
+ CRUSH rule 4 x 533 [5,6,3]
+ CRUSH rule 4 x 534 [7,3,5]
+ CRUSH rule 4 x 535 [8,6,0]
+ CRUSH rule 4 x 536 [6,7,0]
+ CRUSH rule 4 x 537 [3,7,5]
+ CRUSH rule 4 x 538 [6,8,3]
+ CRUSH rule 4 x 539 [8,3,7]
+ CRUSH rule 4 x 540 [0,6,1]
+ CRUSH rule 4 x 541 [2,3,1]
+ CRUSH rule 4 x 542 [3,5,4]
+ CRUSH rule 4 x 543 [6,0,8]
+ CRUSH rule 4 x 544 [3,7,6]
+ CRUSH rule 4 x 545 [5,7,0]
+ CRUSH rule 4 x 546 [6,1,7]
+ CRUSH rule 4 x 547 [8,2,0]
+ CRUSH rule 4 x 548 [5,2,0]
+ CRUSH rule 4 x 549 [5,8,2]
+ CRUSH rule 4 x 550 [0,5,4]
+ CRUSH rule 4 x 551 [7,5,4]
+ CRUSH rule 4 x 552 [5,4,3]
+ CRUSH rule 4 x 553 [4,2,3]
+ CRUSH rule 4 x 554 [0,8,5]
+ CRUSH rule 4 x 555 [5,0,8]
+ CRUSH rule 4 x 556 [3,4,5]
+ CRUSH rule 4 x 557 [7,4,6]
+ CRUSH rule 4 x 558 [3,1,0]
+ CRUSH rule 4 x 559 [4,2,6]
+ CRUSH rule 4 x 560 [8,3,5]
+ CRUSH rule 4 x 561 [6,3,7]
+ CRUSH rule 4 x 562 [3,4,1]
+ CRUSH rule 4 x 563 [2,6,0]
+ CRUSH rule 4 x 564 [5,1,7]
+ CRUSH rule 4 x 565 [3,6,5]
+ CRUSH rule 4 x 566 [4,7,2]
+ CRUSH rule 4 x 567 [3,6,1]
+ CRUSH rule 4 x 568 [7,4,1]
+ CRUSH rule 4 x 569 [3,4,2]
+ CRUSH rule 4 x 570 [1,5,4]
+ CRUSH rule 4 x 571 [3,7,5]
+ CRUSH rule 4 x 572 [3,4,0]
+ CRUSH rule 4 x 573 [3,0,7]
+ CRUSH rule 4 x 574 [2,0,1]
+ CRUSH rule 4 x 575 [8,6,0]
+ CRUSH rule 4 x 576 [4,6,8]
+ CRUSH rule 4 x 577 [8,2,6]
+ CRUSH rule 4 x 578 [6,8,7]
+ CRUSH rule 4 x 579 [3,1,6]
+ CRUSH rule 4 x 580 [3,0,7]
+ CRUSH rule 4 x 581 [7,2,6]
+ CRUSH rule 4 x 582 [2,8,7]
+ CRUSH rule 4 x 583 [6,0,8]
+ CRUSH rule 4 x 584 [8,1,2]
+ CRUSH rule 4 x 585 [7,0,5]
+ CRUSH rule 4 x 586 [0,1,7]
+ CRUSH rule 4 x 587 [2,5,0]
+ CRUSH rule 4 x 588 [3,4,7]
+ CRUSH rule 4 x 589 [7,1,4]
+ CRUSH rule 4 x 590 [6,2,3]
+ CRUSH rule 4 x 591 [5,2,4]
+ CRUSH rule 4 x 592 [2,0,3]
+ CRUSH rule 4 x 593 [0,8,1]
+ CRUSH rule 4 x 594 [0,7,4]
+ CRUSH rule 4 x 595 [7,1,8]
+ CRUSH rule 4 x 596 [4,3,5]
+ CRUSH rule 4 x 597 [3,1,2]
+ CRUSH rule 4 x 598 [3,2,4]
+ CRUSH rule 4 x 599 [5,2,1]
+ CRUSH rule 4 x 600 [7,0,8]
+ CRUSH rule 4 x 601 [0,7,8]
+ CRUSH rule 4 x 602 [3,7,5]
+ CRUSH rule 4 x 603 [5,1,6]
+ CRUSH rule 4 x 604 [7,5,4]
+ CRUSH rule 4 x 605 [3,0,5]
+ CRUSH rule 4 x 606 [2,0,1]
+ CRUSH rule 4 x 607 [0,4,8]
+ CRUSH rule 4 x 608 [5,3,2]
+ CRUSH rule 4 x 609 [5,2,4]
+ CRUSH rule 4 x 610 [3,7,6]
+ CRUSH rule 4 x 611 [1,0,2]
+ CRUSH rule 4 x 612 [2,0,8]
+ CRUSH rule 4 x 613 [7,2,0]
+ CRUSH rule 4 x 614 [7,8,6]
+ CRUSH rule 4 x 615 [6,8,2]
+ CRUSH rule 4 x 616 [0,8,1]
+ CRUSH rule 4 x 617 [6,1,2]
+ CRUSH rule 4 x 618 [7,6,5]
+ CRUSH rule 4 x 619 [5,1,8]
+ CRUSH rule 4 x 620 [4,1,0]
+ CRUSH rule 4 x 621 [5,8,3]
+ CRUSH rule 4 x 622 [0,4,3]
+ CRUSH rule 4 x 623 [0,6,2]
+ CRUSH rule 4 x 624 [3,5,1]
+ CRUSH rule 4 x 625 [2,3,7]
+ CRUSH rule 4 x 626 [7,8,0]
+ CRUSH rule 4 x 627 [2,7,1]
+ CRUSH rule 4 x 628 [8,0,5]
+ CRUSH rule 4 x 629 [2,6,5]
+ CRUSH rule 4 x 630 [2,7,1]
+ CRUSH rule 4 x 631 [0,7,8]
+ CRUSH rule 4 x 632 [7,0,2]
+ CRUSH rule 4 x 633 [8,6,5]
+ CRUSH rule 4 x 634 [0,4,2]
+ CRUSH rule 4 x 635 [5,6,4]
+ CRUSH rule 4 x 636 [1,4,2]
+ CRUSH rule 4 x 637 [4,1,2]
+ CRUSH rule 4 x 638 [6,8,1]
+ CRUSH rule 4 x 639 [4,2,1]
+ CRUSH rule 4 x 640 [3,1,0]
+ CRUSH rule 4 x 641 [7,2,0]
+ CRUSH rule 4 x 642 [2,0,1]
+ CRUSH rule 4 x 643 [3,5,1]
+ CRUSH rule 4 x 644 [8,1,5]
+ CRUSH rule 4 x 645 [5,4,6]
+ CRUSH rule 4 x 646 [8,0,3]
+ CRUSH rule 4 x 647 [7,1,3]
+ CRUSH rule 4 x 648 [0,6,3]
+ CRUSH rule 4 x 649 [4,7,5]
+ CRUSH rule 4 x 650 [7,8,6]
+ CRUSH rule 4 x 651 [3,7,6]
+ CRUSH rule 4 x 652 [3,4,8]
+ CRUSH rule 4 x 653 [8,5,2]
+ CRUSH rule 4 x 654 [7,5,2]
+ CRUSH rule 4 x 655 [0,3,4]
+ CRUSH rule 4 x 656 [4,3,8]
+ CRUSH rule 4 x 657 [6,1,2]
+ CRUSH rule 4 x 658 [5,4,6]
+ CRUSH rule 4 x 659 [4,6,7]
+ CRUSH rule 4 x 660 [7,8,6]
+ CRUSH rule 4 x 661 [1,8,3]
+ CRUSH rule 4 x 662 [4,5,3]
+ CRUSH rule 4 x 663 [1,3,8]
+ CRUSH rule 4 x 664 [1,4,0]
+ CRUSH rule 4 x 665 [5,7,6]
+ CRUSH rule 4 x 666 [2,8,4]
+ CRUSH rule 4 x 667 [1,3,5]
+ CRUSH rule 4 x 668 [3,7,6]
+ CRUSH rule 4 x 669 [6,4,5]
+ CRUSH rule 4 x 670 [4,0,3]
+ CRUSH rule 4 x 671 [0,2,1]
+ CRUSH rule 4 x 672 [4,3,2]
+ CRUSH rule 4 x 673 [5,3,0]
+ CRUSH rule 4 x 674 [3,1,8]
+ CRUSH rule 4 x 675 [0,8,6]
+ CRUSH rule 4 x 676 [0,2,4]
+ CRUSH rule 4 x 677 [4,1,3]
+ CRUSH rule 4 x 678 [2,3,1]
+ CRUSH rule 4 x 679 [6,0,2]
+ CRUSH rule 4 x 680 [0,4,6]
+ CRUSH rule 4 x 681 [4,7,3]
+ CRUSH rule 4 x 682 [0,5,2]
+ CRUSH rule 4 x 683 [0,1,5]
+ CRUSH rule 4 x 684 [7,1,6]
+ CRUSH rule 4 x 685 [7,1,6]
+ CRUSH rule 4 x 686 [1,4,3]
+ CRUSH rule 4 x 687 [3,5,7]
+ CRUSH rule 4 x 688 [5,7,2]
+ CRUSH rule 4 x 689 [6,5,0]
+ CRUSH rule 4 x 690 [8,1,7]
+ CRUSH rule 4 x 691 [3,0,5]
+ CRUSH rule 4 x 692 [7,2,8]
+ CRUSH rule 4 x 693 [6,7,3]
+ CRUSH rule 4 x 694 [6,5,1]
+ CRUSH rule 4 x 695 [0,8,7]
+ CRUSH rule 4 x 696 [1,4,3]
+ CRUSH rule 4 x 697 [6,1,2]
+ CRUSH rule 4 x 698 [6,2,0]
+ CRUSH rule 4 x 699 [1,6,8]
+ CRUSH rule 4 x 700 [0,3,1]
+ CRUSH rule 4 x 701 [4,3,2]
+ CRUSH rule 4 x 702 [3,5,0]
+ CRUSH rule 4 x 703 [8,3,4]
+ CRUSH rule 4 x 704 [0,3,8]
+ CRUSH rule 4 x 705 [8,6,0]
+ CRUSH rule 4 x 706 [1,2,4]
+ CRUSH rule 4 x 707 [7,8,6]
+ CRUSH rule 4 x 708 [3,5,8]
+ CRUSH rule 4 x 709 [6,3,0]
+ CRUSH rule 4 x 710 [8,4,3]
+ CRUSH rule 4 x 711 [2,3,8]
+ CRUSH rule 4 x 712 [2,3,7]
+ CRUSH rule 4 x 713 [6,7,8]
+ CRUSH rule 4 x 714 [3,2,0]
+ CRUSH rule 4 x 715 [1,2,4]
+ CRUSH rule 4 x 716 [3,6,0]
+ CRUSH rule 4 x 717 [8,7,2]
+ CRUSH rule 4 x 718 [3,7,8]
+ CRUSH rule 4 x 719 [2,6,3]
+ CRUSH rule 4 x 720 [6,8,2]
+ CRUSH rule 4 x 721 [5,4,7]
+ CRUSH rule 4 x 722 [5,4,6]
+ CRUSH rule 4 x 723 [5,1,0]
+ CRUSH rule 4 x 724 [0,6,2]
+ CRUSH rule 4 x 725 [0,1,5]
+ CRUSH rule 4 x 726 [3,8,4]
+ CRUSH rule 4 x 727 [4,6,8]
+ CRUSH rule 4 x 728 [2,1,0]
+ CRUSH rule 4 x 729 [5,3,7]
+ CRUSH rule 4 x 730 [3,7,5]
+ CRUSH rule 4 x 731 [4,1,5]
+ CRUSH rule 4 x 732 [1,5,3]
+ CRUSH rule 4 x 733 [5,4,7]
+ CRUSH rule 4 x 734 [6,4,2]
+ CRUSH rule 4 x 735 [4,8,3]
+ CRUSH rule 4 x 736 [3,5,6]
+ CRUSH rule 4 x 737 [1,0,8]
+ CRUSH rule 4 x 738 [5,2,3]
+ CRUSH rule 4 x 739 [0,1,2]
+ CRUSH rule 4 x 740 [0,1,7]
+ CRUSH rule 4 x 741 [7,8,0]
+ CRUSH rule 4 x 742 [8,2,1]
+ CRUSH rule 4 x 743 [7,0,1]
+ CRUSH rule 4 x 744 [4,7,3]
+ CRUSH rule 4 x 745 [3,4,1]
+ CRUSH rule 4 x 746 [4,1,3]
+ CRUSH rule 4 x 747 [6,0,3]
+ CRUSH rule 4 x 748 [2,7,0]
+ CRUSH rule 4 x 749 [4,5,8]
+ CRUSH rule 4 x 750 [1,6,3]
+ CRUSH rule 4 x 751 [2,1,6]
+ CRUSH rule 4 x 752 [8,1,5]
+ CRUSH rule 4 x 753 [7,8,3]
+ CRUSH rule 4 x 754 [8,6,7]
+ CRUSH rule 4 x 755 [1,2,0]
+ CRUSH rule 4 x 756 [5,6,1]
+ CRUSH rule 4 x 757 [8,6,1]
+ CRUSH rule 4 x 758 [6,0,3]
+ CRUSH rule 4 x 759 [8,5,3]
+ CRUSH rule 4 x 760 [1,5,4]
+ CRUSH rule 4 x 761 [4,1,2]
+ CRUSH rule 4 x 762 [2,7,8]
+ CRUSH rule 4 x 763 [8,6,7]
+ CRUSH rule 4 x 764 [1,7,0]
+ CRUSH rule 4 x 765 [6,5,2]
+ CRUSH rule 4 x 766 [8,5,7]
+ CRUSH rule 4 x 767 [1,2,0]
+ CRUSH rule 4 x 768 [8,3,2]
+ CRUSH rule 4 x 769 [6,2,8]
+ CRUSH rule 4 x 770 [6,0,7]
+ CRUSH rule 4 x 771 [7,0,3]
+ CRUSH rule 4 x 772 [8,3,7]
+ CRUSH rule 4 x 773 [3,1,5]
+ CRUSH rule 4 x 774 [4,6,5]
+ CRUSH rule 4 x 775 [6,8,4]
+ CRUSH rule 4 x 776 [7,2,1]
+ CRUSH rule 4 x 777 [3,1,6]
+ CRUSH rule 4 x 778 [1,8,0]
+ CRUSH rule 4 x 779 [2,7,3]
+ CRUSH rule 4 x 780 [0,2,3]
+ CRUSH rule 4 x 781 [6,3,7]
+ CRUSH rule 4 x 782 [5,4,0]
+ CRUSH rule 4 x 783 [7,1,8]
+ CRUSH rule 4 x 784 [0,1,5]
+ CRUSH rule 4 x 785 [6,1,2]
+ CRUSH rule 4 x 786 [7,6,5]
+ CRUSH rule 4 x 787 [1,0,6]
+ CRUSH rule 4 x 788 [6,0,8]
+ CRUSH rule 4 x 789 [0,4,3]
+ CRUSH rule 4 x 790 [8,4,7]
+ CRUSH rule 4 x 791 [3,8,7]
+ CRUSH rule 4 x 792 [5,8,4]
+ CRUSH rule 4 x 793 [6,1,3]
+ CRUSH rule 4 x 794 [2,6,8]
+ CRUSH rule 4 x 795 [0,3,2]
+ CRUSH rule 4 x 796 [3,4,5]
+ CRUSH rule 4 x 797 [2,3,4]
+ CRUSH rule 4 x 798 [6,8,7]
+ CRUSH rule 4 x 799 [5,1,4]
+ CRUSH rule 4 x 800 [5,0,3]
+ CRUSH rule 4 x 801 [3,6,8]
+ CRUSH rule 4 x 802 [1,8,0]
+ CRUSH rule 4 x 803 [0,5,7]
+ CRUSH rule 4 x 804 [6,2,5]
+ CRUSH rule 4 x 805 [3,6,7]
+ CRUSH rule 4 x 806 [1,3,4]
+ CRUSH rule 4 x 807 [5,4,7]
+ CRUSH rule 4 x 808 [4,6,2]
+ CRUSH rule 4 x 809 [1,4,5]
+ CRUSH rule 4 x 810 [5,7,3]
+ CRUSH rule 4 x 811 [8,4,5]
+ CRUSH rule 4 x 812 [8,5,4]
+ CRUSH rule 4 x 813 [6,4,8]
+ CRUSH rule 4 x 814 [3,6,8]
+ CRUSH rule 4 x 815 [3,1,2]
+ CRUSH rule 4 x 816 [2,1,8]
+ CRUSH rule 4 x 817 [4,3,7]
+ CRUSH rule 4 x 818 [3,5,0]
+ CRUSH rule 4 x 819 [5,1,4]
+ CRUSH rule 4 x 820 [3,5,4]
+ CRUSH rule 4 x 821 [4,5,8]
+ CRUSH rule 4 x 822 [2,0,5]
+ CRUSH rule 4 x 823 [4,8,2]
+ CRUSH rule 4 x 824 [3,7,4]
+ CRUSH rule 4 x 825 [2,8,7]
+ CRUSH rule 4 x 826 [7,0,5]
+ CRUSH rule 4 x 827 [0,8,2]
+ CRUSH rule 4 x 828 [2,3,1]
+ CRUSH rule 4 x 829 [5,6,7]
+ CRUSH rule 4 x 830 [2,3,5]
+ CRUSH rule 4 x 831 [1,6,0]
+ CRUSH rule 4 x 832 [4,5,3]
+ CRUSH rule 4 x 833 [2,1,7]
+ CRUSH rule 4 x 834 [3,4,2]
+ CRUSH rule 4 x 835 [8,4,3]
+ CRUSH rule 4 x 836 [3,4,5]
+ CRUSH rule 4 x 837 [6,3,1]
+ CRUSH rule 4 x 838 [6,7,2]
+ CRUSH rule 4 x 839 [5,0,6]
+ CRUSH rule 4 x 840 [7,8,5]
+ CRUSH rule 4 x 841 [4,8,5]
+ CRUSH rule 4 x 842 [2,4,0]
+ CRUSH rule 4 x 843 [6,4,7]
+ CRUSH rule 4 x 844 [4,8,1]
+ CRUSH rule 4 x 845 [3,8,6]
+ CRUSH rule 4 x 846 [3,2,0]
+ CRUSH rule 4 x 847 [0,2,7]
+ CRUSH rule 4 x 848 [2,6,1]
+ CRUSH rule 4 x 849 [4,5,3]
+ CRUSH rule 4 x 850 [1,0,5]
+ CRUSH rule 4 x 851 [6,8,7]
+ CRUSH rule 4 x 852 [7,3,8]
+ CRUSH rule 4 x 853 [6,8,1]
+ CRUSH rule 4 x 854 [7,6,0]
+ CRUSH rule 4 x 855 [5,7,2]
+ CRUSH rule 4 x 856 [6,7,3]
+ CRUSH rule 4 x 857 [8,5,0]
+ CRUSH rule 4 x 858 [6,4,1]
+ CRUSH rule 4 x 859 [6,0,7]
+ CRUSH rule 4 x 860 [4,1,2]
+ CRUSH rule 4 x 861 [8,7,6]
+ CRUSH rule 4 x 862 [6,1,7]
+ CRUSH rule 4 x 863 [8,7,0]
+ CRUSH rule 4 x 864 [5,6,8]
+ CRUSH rule 4 x 865 [8,1,0]
+ CRUSH rule 4 x 866 [3,4,8]
+ CRUSH rule 4 x 867 [6,5,1]
+ CRUSH rule 4 x 868 [6,3,0]
+ CRUSH rule 4 x 869 [8,7,3]
+ CRUSH rule 4 x 870 [0,4,8]
+ CRUSH rule 4 x 871 [3,4,5]
+ CRUSH rule 4 x 872 [5,1,3]
+ CRUSH rule 4 x 873 [4,6,5]
+ CRUSH rule 4 x 874 [2,6,1]
+ CRUSH rule 4 x 875 [2,6,4]
+ CRUSH rule 4 x 876 [5,8,1]
+ CRUSH rule 4 x 877 [6,4,2]
+ CRUSH rule 4 x 878 [5,4,0]
+ CRUSH rule 4 x 879 [7,4,8]
+ CRUSH rule 4 x 880 [3,5,0]
+ CRUSH rule 4 x 881 [5,6,1]
+ CRUSH rule 4 x 882 [4,0,2]
+ CRUSH rule 4 x 883 [2,1,0]
+ CRUSH rule 4 x 884 [6,0,4]
+ CRUSH rule 4 x 885 [5,1,4]
+ CRUSH rule 4 x 886 [3,6,4]
+ CRUSH rule 4 x 887 [7,4,0]
+ CRUSH rule 4 x 888 [6,8,0]
+ CRUSH rule 4 x 889 [2,1,7]
+ CRUSH rule 4 x 890 [7,2,0]
+ CRUSH rule 4 x 891 [1,8,0]
+ CRUSH rule 4 x 892 [6,2,3]
+ CRUSH rule 4 x 893 [2,3,7]
+ CRUSH rule 4 x 894 [7,5,0]
+ CRUSH rule 4 x 895 [5,3,2]
+ CRUSH rule 4 x 896 [1,8,2]
+ CRUSH rule 4 x 897 [4,2,6]
+ CRUSH rule 4 x 898 [0,5,4]
+ CRUSH rule 4 x 899 [1,7,6]
+ CRUSH rule 4 x 900 [4,1,0]
+ CRUSH rule 4 x 901 [5,0,1]
+ CRUSH rule 4 x 902 [8,5,7]
+ CRUSH rule 4 x 903 [5,7,3]
+ CRUSH rule 4 x 904 [5,6,8]
+ CRUSH rule 4 x 905 [6,2,5]
+ CRUSH rule 4 x 906 [1,2,0]
+ CRUSH rule 4 x 907 [7,1,0]
+ CRUSH rule 4 x 908 [5,8,1]
+ CRUSH rule 4 x 909 [2,3,4]
+ CRUSH rule 4 x 910 [6,4,0]
+ CRUSH rule 4 x 911 [5,8,4]
+ CRUSH rule 4 x 912 [0,1,7]
+ CRUSH rule 4 x 913 [7,6,8]
+ CRUSH rule 4 x 914 [6,4,7]
+ CRUSH rule 4 x 915 [8,2,6]
+ CRUSH rule 4 x 916 [3,1,4]
+ CRUSH rule 4 x 917 [1,5,3]
+ CRUSH rule 4 x 918 [8,2,1]
+ CRUSH rule 4 x 919 [6,2,8]
+ CRUSH rule 4 x 920 [7,6,4]
+ CRUSH rule 4 x 921 [1,4,5]
+ CRUSH rule 4 x 922 [6,7,8]
+ CRUSH rule 4 x 923 [5,3,6]
+ CRUSH rule 4 x 924 [3,5,4]
+ CRUSH rule 4 x 925 [5,7,3]
+ CRUSH rule 4 x 926 [3,4,5]
+ CRUSH rule 4 x 927 [1,6,3]
+ CRUSH rule 4 x 928 [8,1,2]
+ CRUSH rule 4 x 929 [4,5,1]
+ CRUSH rule 4 x 930 [2,4,6]
+ CRUSH rule 4 x 931 [5,0,1]
+ CRUSH rule 4 x 932 [4,3,0]
+ CRUSH rule 4 x 933 [8,5,4]
+ CRUSH rule 4 x 934 [5,3,8]
+ CRUSH rule 4 x 935 [6,3,4]
+ CRUSH rule 4 x 936 [0,6,7]
+ CRUSH rule 4 x 937 [5,4,3]
+ CRUSH rule 4 x 938 [6,5,8]
+ CRUSH rule 4 x 939 [2,7,0]
+ CRUSH rule 4 x 940 [8,7,6]
+ CRUSH rule 4 x 941 [5,2,0]
+ CRUSH rule 4 x 942 [1,0,2]
+ CRUSH rule 4 x 943 [8,2,4]
+ CRUSH rule 4 x 944 [4,3,7]
+ CRUSH rule 4 x 945 [7,2,4]
+ CRUSH rule 4 x 946 [2,0,7]
+ CRUSH rule 4 x 947 [4,5,3]
+ CRUSH rule 4 x 948 [7,8,6]
+ CRUSH rule 4 x 949 [6,1,7]
+ CRUSH rule 4 x 950 [3,5,8]
+ CRUSH rule 4 x 951 [4,5,3]
+ CRUSH rule 4 x 952 [2,0,7]
+ CRUSH rule 4 x 953 [1,3,5]
+ CRUSH rule 4 x 954 [4,2,5]
+ CRUSH rule 4 x 955 [8,6,0]
+ CRUSH rule 4 x 956 [1,0,8]
+ CRUSH rule 4 x 957 [7,6,1]
+ CRUSH rule 4 x 958 [8,7,5]
+ CRUSH rule 4 x 959 [5,2,7]
+ CRUSH rule 4 x 960 [3,6,5]
+ CRUSH rule 4 x 961 [4,0,2]
+ CRUSH rule 4 x 962 [7,4,3]
+ CRUSH rule 4 x 963 [0,5,2]
+ CRUSH rule 4 x 964 [3,1,4]
+ CRUSH rule 4 x 965 [7,6,5]
+ CRUSH rule 4 x 966 [3,8,4]
+ CRUSH rule 4 x 967 [8,6,5]
+ CRUSH rule 4 x 968 [7,2,4]
+ CRUSH rule 4 x 969 [8,0,6]
+ CRUSH rule 4 x 970 [0,6,3]
+ CRUSH rule 4 x 971 [1,7,8]
+ CRUSH rule 4 x 972 [1,8,4]
+ CRUSH rule 4 x 973 [1,2,0]
+ CRUSH rule 4 x 974 [5,3,2]
+ CRUSH rule 4 x 975 [3,7,4]
+ CRUSH rule 4 x 976 [4,3,5]
+ CRUSH rule 4 x 977 [8,3,2]
+ CRUSH rule 4 x 978 [7,2,8]
+ CRUSH rule 4 x 979 [7,6,0]
+ CRUSH rule 4 x 980 [6,0,7]
+ CRUSH rule 4 x 981 [7,3,2]
+ CRUSH rule 4 x 982 [4,2,0]
+ CRUSH rule 4 x 983 [3,5,6]
+ CRUSH rule 4 x 984 [0,2,1]
+ CRUSH rule 4 x 985 [2,5,4]
+ CRUSH rule 4 x 986 [8,7,3]
+ CRUSH rule 4 x 987 [0,5,1]
+ CRUSH rule 4 x 988 [1,3,5]
+ CRUSH rule 4 x 989 [0,6,3]
+ CRUSH rule 4 x 990 [1,0,8]
+ CRUSH rule 4 x 991 [0,4,1]
+ CRUSH rule 4 x 992 [7,1,5]
+ CRUSH rule 4 x 993 [0,6,2]
+ CRUSH rule 4 x 994 [3,4,5]
+ CRUSH rule 4 x 995 [7,6,2]
+ CRUSH rule 4 x 996 [6,7,5]
+ CRUSH rule 4 x 997 [6,4,1]
+ CRUSH rule 4 x 998 [8,1,2]
+ CRUSH rule 4 x 999 [0,7,8]
+ CRUSH rule 4 x 1000 [8,5,0]
+ CRUSH rule 4 x 1001 [2,0,4]
+ CRUSH rule 4 x 1002 [1,3,2]
+ CRUSH rule 4 x 1003 [2,8,7]
+ CRUSH rule 4 x 1004 [6,1,2]
+ CRUSH rule 4 x 1005 [6,1,2]
+ CRUSH rule 4 x 1006 [1,0,2]
+ CRUSH rule 4 x 1007 [1,2,4]
+ CRUSH rule 4 x 1008 [1,7,0]
+ CRUSH rule 4 x 1009 [6,8,5]
+ CRUSH rule 4 x 1010 [3,4,0]
+ CRUSH rule 4 x 1011 [3,0,4]
+ CRUSH rule 4 x 1012 [3,0,7]
+ CRUSH rule 4 x 1013 [5,1,0]
+ CRUSH rule 4 x 1014 [2,8,4]
+ CRUSH rule 4 x 1015 [6,8,4]
+ CRUSH rule 4 x 1016 [2,0,1]
+ CRUSH rule 4 x 1017 [6,0,2]
+ CRUSH rule 4 x 1018 [5,4,3]
+ CRUSH rule 4 x 1019 [5,3,8]
+ CRUSH rule 4 x 1020 [5,1,3]
+ CRUSH rule 4 x 1021 [5,2,1]
+ CRUSH rule 4 x 1022 [1,6,7]
+ CRUSH rule 4 x 1023 [3,2,0]
+ rule 4 (choose-set-two) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 5 (chooseleaf-set), x = 0..1023, numrep = 2..3
+ CRUSH rule 5 x 0 [0,5]
+ CRUSH rule 5 x 1 [0,8]
+ CRUSH rule 5 x 2 [1,3]
+ CRUSH rule 5 x 3 [8,0]
+ CRUSH rule 5 x 4 [5,0]
+ CRUSH rule 5 x 5 [7,0]
+ CRUSH rule 5 x 6 [2,6]
+ CRUSH rule 5 x 7 [5,8]
+ CRUSH rule 5 x 8 [5,6]
+ CRUSH rule 5 x 9 [2,3]
+ CRUSH rule 5 x 10 [0,7]
+ CRUSH rule 5 x 11 [0,7]
+ CRUSH rule 5 x 12 [0,4]
+ CRUSH rule 5 x 13 [3,8]
+ CRUSH rule 5 x 14 [7,0]
+ CRUSH rule 5 x 15 [7,2]
+ CRUSH rule 5 x 16 [3,6]
+ CRUSH rule 5 x 17 [5,1]
+ CRUSH rule 5 x 18 [1,4]
+ CRUSH rule 5 x 19 [7,5]
+ CRUSH rule 5 x 20 [2,4]
+ CRUSH rule 5 x 21 [3,7]
+ CRUSH rule 5 x 22 [8,3]
+ CRUSH rule 5 x 23 [3,6]
+ CRUSH rule 5 x 24 [1,7]
+ CRUSH rule 5 x 25 [3,7]
+ CRUSH rule 5 x 26 [2,8]
+ CRUSH rule 5 x 27 [3,1]
+ CRUSH rule 5 x 28 [6,0]
+ CRUSH rule 5 x 29 [8,5]
+ CRUSH rule 5 x 30 [5,7]
+ CRUSH rule 5 x 31 [8,0]
+ CRUSH rule 5 x 32 [3,6]
+ CRUSH rule 5 x 33 [2,7]
+ CRUSH rule 5 x 34 [2,5]
+ CRUSH rule 5 x 35 [0,8]
+ CRUSH rule 5 x 36 [3,8]
+ CRUSH rule 5 x 37 [0,4]
+ CRUSH rule 5 x 38 [4,8]
+ CRUSH rule 5 x 39 [3,7]
+ CRUSH rule 5 x 40 [7,2]
+ CRUSH rule 5 x 41 [0,6]
+ CRUSH rule 5 x 42 [4,6]
+ CRUSH rule 5 x 43 [0,3]
+ CRUSH rule 5 x 44 [1,6]
+ CRUSH rule 5 x 45 [8,0]
+ CRUSH rule 5 x 46 [2,4]
+ CRUSH rule 5 x 47 [4,2]
+ CRUSH rule 5 x 48 [4,6]
+ CRUSH rule 5 x 49 [5,7]
+ CRUSH rule 5 x 50 [3,1]
+ CRUSH rule 5 x 51 [3,6]
+ CRUSH rule 5 x 52 [8,1]
+ CRUSH rule 5 x 53 [3,8]
+ CRUSH rule 5 x 54 [7,3]
+ CRUSH rule 5 x 55 [8,2]
+ CRUSH rule 5 x 56 [6,4]
+ CRUSH rule 5 x 57 [5,8]
+ CRUSH rule 5 x 58 [1,8]
+ CRUSH rule 5 x 59 [4,2]
+ CRUSH rule 5 x 60 [3,2]
+ CRUSH rule 5 x 61 [4,6]
+ CRUSH rule 5 x 62 [7,0]
+ CRUSH rule 5 x 63 [5,6]
+ CRUSH rule 5 x 64 [4,2]
+ CRUSH rule 5 x 65 [7,3]
+ CRUSH rule 5 x 66 [5,6]
+ CRUSH rule 5 x 67 [5,0]
+ CRUSH rule 5 x 68 [0,5]
+ CRUSH rule 5 x 69 [5,1]
+ CRUSH rule 5 x 70 [7,0]
+ CRUSH rule 5 x 71 [2,8]
+ CRUSH rule 5 x 72 [6,1]
+ CRUSH rule 5 x 73 [2,7]
+ CRUSH rule 5 x 74 [0,7]
+ CRUSH rule 5 x 75 [3,2]
+ CRUSH rule 5 x 76 [5,1]
+ CRUSH rule 5 x 77 [7,2]
+ CRUSH rule 5 x 78 [1,4]
+ CRUSH rule 5 x 79 [5,1]
+ CRUSH rule 5 x 80 [0,3]
+ CRUSH rule 5 x 81 [0,3]
+ CRUSH rule 5 x 82 [7,1]
+ CRUSH rule 5 x 83 [2,6]
+ CRUSH rule 5 x 84 [7,2]
+ CRUSH rule 5 x 85 [3,8]
+ CRUSH rule 5 x 86 [0,6]
+ CRUSH rule 5 x 87 [0,7]
+ CRUSH rule 5 x 88 [1,6]
+ CRUSH rule 5 x 89 [3,0]
+ CRUSH rule 5 x 90 [6,4]
+ CRUSH rule 5 x 91 [3,8]
+ CRUSH rule 5 x 92 [1,8]
+ CRUSH rule 5 x 93 [7,4]
+ CRUSH rule 5 x 94 [0,4]
+ CRUSH rule 5 x 95 [7,5]
+ CRUSH rule 5 x 96 [3,6]
+ CRUSH rule 5 x 97 [8,4]
+ CRUSH rule 5 x 98 [2,7]
+ CRUSH rule 5 x 99 [0,7]
+ CRUSH rule 5 x 100 [1,7]
+ CRUSH rule 5 x 101 [3,7]
+ CRUSH rule 5 x 102 [4,2]
+ CRUSH rule 5 x 103 [4,7]
+ CRUSH rule 5 x 104 [7,4]
+ CRUSH rule 5 x 105 [2,4]
+ CRUSH rule 5 x 106 [1,6]
+ CRUSH rule 5 x 107 [3,2]
+ CRUSH rule 5 x 108 [7,2]
+ CRUSH rule 5 x 109 [1,4]
+ CRUSH rule 5 x 110 [3,2]
+ CRUSH rule 5 x 111 [2,3]
+ CRUSH rule 5 x 112 [2,6]
+ CRUSH rule 5 x 113 [6,2]
+ CRUSH rule 5 x 114 [7,3]
+ CRUSH rule 5 x 115 [8,2]
+ CRUSH rule 5 x 116 [1,6]
+ CRUSH rule 5 x 117 [7,3]
+ CRUSH rule 5 x 118 [0,3]
+ CRUSH rule 5 x 119 [5,6]
+ CRUSH rule 5 x 120 [0,3]
+ CRUSH rule 5 x 121 [2,7]
+ CRUSH rule 5 x 122 [8,5]
+ CRUSH rule 5 x 123 [2,5]
+ CRUSH rule 5 x 124 [3,2]
+ CRUSH rule 5 x 125 [0,7]
+ CRUSH rule 5 x 126 [4,2]
+ CRUSH rule 5 x 127 [3,6]
+ CRUSH rule 5 x 128 [3,6]
+ CRUSH rule 5 x 129 [0,3]
+ CRUSH rule 5 x 130 [3,8]
+ CRUSH rule 5 x 131 [1,3]
+ CRUSH rule 5 x 132 [1,4]
+ CRUSH rule 5 x 133 [3,6]
+ CRUSH rule 5 x 134 [1,8]
+ CRUSH rule 5 x 135 [5,6]
+ CRUSH rule 5 x 136 [2,3]
+ CRUSH rule 5 x 137 [7,3]
+ CRUSH rule 5 x 138 [8,4]
+ CRUSH rule 5 x 139 [3,0]
+ CRUSH rule 5 x 140 [1,6]
+ CRUSH rule 5 x 141 [6,2]
+ CRUSH rule 5 x 142 [3,0]
+ CRUSH rule 5 x 143 [5,8]
+ CRUSH rule 5 x 144 [8,1]
+ CRUSH rule 5 x 145 [8,5]
+ CRUSH rule 5 x 146 [2,6]
+ CRUSH rule 5 x 147 [2,8]
+ CRUSH rule 5 x 148 [3,1]
+ CRUSH rule 5 x 149 [4,8]
+ CRUSH rule 5 x 150 [1,6]
+ CRUSH rule 5 x 151 [3,6]
+ CRUSH rule 5 x 152 [8,4]
+ CRUSH rule 5 x 153 [8,4]
+ CRUSH rule 5 x 154 [3,2]
+ CRUSH rule 5 x 155 [3,7]
+ CRUSH rule 5 x 156 [4,2]
+ CRUSH rule 5 x 157 [4,1]
+ CRUSH rule 5 x 158 [2,8]
+ CRUSH rule 5 x 159 [7,0]
+ CRUSH rule 5 x 160 [2,8]
+ CRUSH rule 5 x 161 [1,5]
+ CRUSH rule 5 x 162 [0,6]
+ CRUSH rule 5 x 163 [5,6]
+ CRUSH rule 5 x 164 [7,1]
+ CRUSH rule 5 x 165 [7,0]
+ CRUSH rule 5 x 166 [2,4]
+ CRUSH rule 5 x 167 [0,7]
+ CRUSH rule 5 x 168 [4,2]
+ CRUSH rule 5 x 169 [2,6]
+ CRUSH rule 5 x 170 [1,4]
+ CRUSH rule 5 x 171 [7,5]
+ CRUSH rule 5 x 172 [0,7]
+ CRUSH rule 5 x 173 [8,5]
+ CRUSH rule 5 x 174 [1,4]
+ CRUSH rule 5 x 175 [6,0]
+ CRUSH rule 5 x 176 [4,1]
+ CRUSH rule 5 x 177 [5,1]
+ CRUSH rule 5 x 178 [3,0]
+ CRUSH rule 5 x 179 [4,1]
+ CRUSH rule 5 x 180 [3,8]
+ CRUSH rule 5 x 181 [6,2]
+ CRUSH rule 5 x 182 [8,5]
+ CRUSH rule 5 x 183 [7,5]
+ CRUSH rule 5 x 184 [5,7]
+ CRUSH rule 5 x 185 [6,1]
+ CRUSH rule 5 x 186 [2,5]
+ CRUSH rule 5 x 187 [1,6]
+ CRUSH rule 5 x 188 [1,8]
+ CRUSH rule 5 x 189 [0,7]
+ CRUSH rule 5 x 190 [4,0]
+ CRUSH rule 5 x 191 [7,1]
+ CRUSH rule 5 x 192 [5,0]
+ CRUSH rule 5 x 193 [4,2]
+ CRUSH rule 5 x 194 [1,3]
+ CRUSH rule 5 x 195 [6,4]
+ CRUSH rule 5 x 196 [6,0]
+ CRUSH rule 5 x 197 [6,5]
+ CRUSH rule 5 x 198 [2,5]
+ CRUSH rule 5 x 199 [0,5]
+ CRUSH rule 5 x 200 [0,5]
+ CRUSH rule 5 x 201 [7,1]
+ CRUSH rule 5 x 202 [6,3]
+ CRUSH rule 5 x 203 [4,8]
+ CRUSH rule 5 x 204 [2,3]
+ CRUSH rule 5 x 205 [0,7]
+ CRUSH rule 5 x 206 [0,7]
+ CRUSH rule 5 x 207 [3,0]
+ CRUSH rule 5 x 208 [7,1]
+ CRUSH rule 5 x 209 [1,8]
+ CRUSH rule 5 x 210 [1,4]
+ CRUSH rule 5 x 211 [5,2]
+ CRUSH rule 5 x 212 [7,5]
+ CRUSH rule 5 x 213 [8,4]
+ CRUSH rule 5 x 214 [4,8]
+ CRUSH rule 5 x 215 [8,1]
+ CRUSH rule 5 x 216 [5,0]
+ CRUSH rule 5 x 217 [0,7]
+ CRUSH rule 5 x 218 [0,7]
+ CRUSH rule 5 x 219 [4,8]
+ CRUSH rule 5 x 220 [5,7]
+ CRUSH rule 5 x 221 [3,6]
+ CRUSH rule 5 x 222 [6,4]
+ CRUSH rule 5 x 223 [1,3]
+ CRUSH rule 5 x 224 [1,5]
+ CRUSH rule 5 x 225 [8,2]
+ CRUSH rule 5 x 226 [7,2]
+ CRUSH rule 5 x 227 [3,1]
+ CRUSH rule 5 x 228 [5,6]
+ CRUSH rule 5 x 229 [3,8]
+ CRUSH rule 5 x 230 [4,7]
+ CRUSH rule 5 x 231 [4,7]
+ CRUSH rule 5 x 232 [2,7]
+ CRUSH rule 5 x 233 [3,7]
+ CRUSH rule 5 x 234 [0,3]
+ CRUSH rule 5 x 235 [3,8]
+ CRUSH rule 5 x 236 [5,2]
+ CRUSH rule 5 x 237 [4,7]
+ CRUSH rule 5 x 238 [4,2]
+ CRUSH rule 5 x 239 [8,4]
+ CRUSH rule 5 x 240 [5,7]
+ CRUSH rule 5 x 241 [3,1]
+ CRUSH rule 5 x 242 [3,2]
+ CRUSH rule 5 x 243 [4,7]
+ CRUSH rule 5 x 244 [4,6]
+ CRUSH rule 5 x 245 [7,0]
+ CRUSH rule 5 x 246 [1,5]
+ CRUSH rule 5 x 247 [6,0]
+ CRUSH rule 5 x 248 [8,0]
+ CRUSH rule 5 x 249 [2,4]
+ CRUSH rule 5 x 250 [2,5]
+ CRUSH rule 5 x 251 [2,3]
+ CRUSH rule 5 x 252 [3,7]
+ CRUSH rule 5 x 253 [3,2]
+ CRUSH rule 5 x 254 [3,2]
+ CRUSH rule 5 x 255 [1,7]
+ CRUSH rule 5 x 256 [5,7]
+ CRUSH rule 5 x 257 [2,8]
+ CRUSH rule 5 x 258 [5,0]
+ CRUSH rule 5 x 259 [4,6]
+ CRUSH rule 5 x 260 [3,6]
+ CRUSH rule 5 x 261 [8,5]
+ CRUSH rule 5 x 262 [5,6]
+ CRUSH rule 5 x 263 [6,1]
+ CRUSH rule 5 x 264 [3,6]
+ CRUSH rule 5 x 265 [8,5]
+ CRUSH rule 5 x 266 [8,2]
+ CRUSH rule 5 x 267 [2,3]
+ CRUSH rule 5 x 268 [0,7]
+ CRUSH rule 5 x 269 [0,8]
+ CRUSH rule 5 x 270 [5,0]
+ CRUSH rule 5 x 271 [7,5]
+ CRUSH rule 5 x 272 [2,8]
+ CRUSH rule 5 x 273 [3,1]
+ CRUSH rule 5 x 274 [6,3]
+ CRUSH rule 5 x 275 [4,7]
+ CRUSH rule 5 x 276 [7,1]
+ CRUSH rule 5 x 277 [6,4]
+ CRUSH rule 5 x 278 [6,1]
+ CRUSH rule 5 x 279 [8,3]
+ CRUSH rule 5 x 280 [0,6]
+ CRUSH rule 5 x 281 [8,0]
+ CRUSH rule 5 x 282 [3,1]
+ CRUSH rule 5 x 283 [8,2]
+ CRUSH rule 5 x 284 [6,3]
+ CRUSH rule 5 x 285 [5,7]
+ CRUSH rule 5 x 286 [2,6]
+ CRUSH rule 5 x 287 [0,4]
+ CRUSH rule 5 x 288 [8,0]
+ CRUSH rule 5 x 289 [4,6]
+ CRUSH rule 5 x 290 [1,3]
+ CRUSH rule 5 x 291 [0,3]
+ CRUSH rule 5 x 292 [8,0]
+ CRUSH rule 5 x 293 [6,0]
+ CRUSH rule 5 x 294 [7,4]
+ CRUSH rule 5 x 295 [4,8]
+ CRUSH rule 5 x 296 [3,1]
+ CRUSH rule 5 x 297 [6,2]
+ CRUSH rule 5 x 298 [1,5]
+ CRUSH rule 5 x 299 [2,8]
+ CRUSH rule 5 x 300 [8,3]
+ CRUSH rule 5 x 301 [0,8]
+ CRUSH rule 5 x 302 [3,0]
+ CRUSH rule 5 x 303 [7,5]
+ CRUSH rule 5 x 304 [2,7]
+ CRUSH rule 5 x 305 [5,8]
+ CRUSH rule 5 x 306 [0,7]
+ CRUSH rule 5 x 307 [0,7]
+ CRUSH rule 5 x 308 [0,8]
+ CRUSH rule 5 x 309 [7,4]
+ CRUSH rule 5 x 310 [4,1]
+ CRUSH rule 5 x 311 [3,6]
+ CRUSH rule 5 x 312 [2,6]
+ CRUSH rule 5 x 313 [5,1]
+ CRUSH rule 5 x 314 [4,2]
+ CRUSH rule 5 x 315 [2,4]
+ CRUSH rule 5 x 316 [6,3]
+ CRUSH rule 5 x 317 [2,6]
+ CRUSH rule 5 x 318 [8,1]
+ CRUSH rule 5 x 319 [5,0]
+ CRUSH rule 5 x 320 [3,7]
+ CRUSH rule 5 x 321 [1,3]
+ CRUSH rule 5 x 322 [2,7]
+ CRUSH rule 5 x 323 [4,7]
+ CRUSH rule 5 x 324 [7,0]
+ CRUSH rule 5 x 325 [4,6]
+ CRUSH rule 5 x 326 [3,2]
+ CRUSH rule 5 x 327 [0,6]
+ CRUSH rule 5 x 328 [7,4]
+ CRUSH rule 5 x 329 [5,6]
+ CRUSH rule 5 x 330 [3,7]
+ CRUSH rule 5 x 331 [2,6]
+ CRUSH rule 5 x 332 [2,4]
+ CRUSH rule 5 x 333 [6,5]
+ CRUSH rule 5 x 334 [8,3]
+ CRUSH rule 5 x 335 [7,1]
+ CRUSH rule 5 x 336 [4,6]
+ CRUSH rule 5 x 337 [7,2]
+ CRUSH rule 5 x 338 [5,6]
+ CRUSH rule 5 x 339 [7,5]
+ CRUSH rule 5 x 340 [2,8]
+ CRUSH rule 5 x 341 [5,1]
+ CRUSH rule 5 x 342 [0,7]
+ CRUSH rule 5 x 343 [6,3]
+ CRUSH rule 5 x 344 [6,0]
+ CRUSH rule 5 x 345 [4,7]
+ CRUSH rule 5 x 346 [8,0]
+ CRUSH rule 5 x 347 [3,1]
+ CRUSH rule 5 x 348 [8,0]
+ CRUSH rule 5 x 349 [1,6]
+ CRUSH rule 5 x 350 [8,5]
+ CRUSH rule 5 x 351 [3,6]
+ CRUSH rule 5 x 352 [1,8]
+ CRUSH rule 5 x 353 [6,4]
+ CRUSH rule 5 x 354 [0,3]
+ CRUSH rule 5 x 355 [3,8]
+ CRUSH rule 5 x 356 [3,0]
+ CRUSH rule 5 x 357 [6,1]
+ CRUSH rule 5 x 358 [2,8]
+ CRUSH rule 5 x 359 [6,1]
+ CRUSH rule 5 x 360 [5,2]
+ CRUSH rule 5 x 361 [8,4]
+ CRUSH rule 5 x 362 [4,1]
+ CRUSH rule 5 x 363 [4,0]
+ CRUSH rule 5 x 364 [2,5]
+ CRUSH rule 5 x 365 [6,5]
+ CRUSH rule 5 x 366 [7,2]
+ CRUSH rule 5 x 367 [4,0]
+ CRUSH rule 5 x 368 [7,4]
+ CRUSH rule 5 x 369 [3,7]
+ CRUSH rule 5 x 370 [8,2]
+ CRUSH rule 5 x 371 [1,3]
+ CRUSH rule 5 x 372 [3,1]
+ CRUSH rule 5 x 373 [0,6]
+ CRUSH rule 5 x 374 [3,8]
+ CRUSH rule 5 x 375 [6,4]
+ CRUSH rule 5 x 376 [7,1]
+ CRUSH rule 5 x 377 [1,3]
+ CRUSH rule 5 x 378 [0,8]
+ CRUSH rule 5 x 379 [8,5]
+ CRUSH rule 5 x 380 [2,5]
+ CRUSH rule 5 x 381 [0,4]
+ CRUSH rule 5 x 382 [1,5]
+ CRUSH rule 5 x 383 [4,6]
+ CRUSH rule 5 x 384 [7,0]
+ CRUSH rule 5 x 385 [7,4]
+ CRUSH rule 5 x 386 [0,3]
+ CRUSH rule 5 x 387 [1,3]
+ CRUSH rule 5 x 388 [5,0]
+ CRUSH rule 5 x 389 [1,5]
+ CRUSH rule 5 x 390 [5,6]
+ CRUSH rule 5 x 391 [5,6]
+ CRUSH rule 5 x 392 [1,8]
+ CRUSH rule 5 x 393 [4,2]
+ CRUSH rule 5 x 394 [4,7]
+ CRUSH rule 5 x 395 [4,0]
+ CRUSH rule 5 x 396 [4,2]
+ CRUSH rule 5 x 397 [2,4]
+ CRUSH rule 5 x 398 [2,4]
+ CRUSH rule 5 x 399 [8,4]
+ CRUSH rule 5 x 400 [8,1]
+ CRUSH rule 5 x 401 [0,5]
+ CRUSH rule 5 x 402 [7,5]
+ CRUSH rule 5 x 403 [0,3]
+ CRUSH rule 5 x 404 [4,2]
+ CRUSH rule 5 x 405 [6,5]
+ CRUSH rule 5 x 406 [2,6]
+ CRUSH rule 5 x 407 [2,8]
+ CRUSH rule 5 x 408 [4,1]
+ CRUSH rule 5 x 409 [7,3]
+ CRUSH rule 5 x 410 [8,3]
+ CRUSH rule 5 x 411 [2,8]
+ CRUSH rule 5 x 412 [0,5]
+ CRUSH rule 5 x 413 [5,0]
+ CRUSH rule 5 x 414 [4,1]
+ CRUSH rule 5 x 415 [0,6]
+ CRUSH rule 5 x 416 [2,6]
+ CRUSH rule 5 x 417 [8,2]
+ CRUSH rule 5 x 418 [7,1]
+ CRUSH rule 5 x 419 [8,3]
+ CRUSH rule 5 x 420 [1,4]
+ CRUSH rule 5 x 421 [8,4]
+ CRUSH rule 5 x 422 [6,4]
+ CRUSH rule 5 x 423 [0,5]
+ CRUSH rule 5 x 424 [8,4]
+ CRUSH rule 5 x 425 [1,3]
+ CRUSH rule 5 x 426 [6,0]
+ CRUSH rule 5 x 427 [0,7]
+ CRUSH rule 5 x 428 [5,7]
+ CRUSH rule 5 x 429 [4,6]
+ CRUSH rule 5 x 430 [3,6]
+ CRUSH rule 5 x 431 [5,0]
+ CRUSH rule 5 x 432 [7,1]
+ CRUSH rule 5 x 433 [6,5]
+ CRUSH rule 5 x 434 [5,2]
+ CRUSH rule 5 x 435 [0,5]
+ CRUSH rule 5 x 436 [4,0]
+ CRUSH rule 5 x 437 [7,5]
+ CRUSH rule 5 x 438 [0,3]
+ CRUSH rule 5 x 439 [1,3]
+ CRUSH rule 5 x 440 [2,7]
+ CRUSH rule 5 x 441 [5,7]
+ CRUSH rule 5 x 442 [2,4]
+ CRUSH rule 5 x 443 [6,0]
+ CRUSH rule 5 x 444 [7,0]
+ CRUSH rule 5 x 445 [6,3]
+ CRUSH rule 5 x 446 [4,1]
+ CRUSH rule 5 x 447 [2,3]
+ CRUSH rule 5 x 448 [7,2]
+ CRUSH rule 5 x 449 [7,5]
+ CRUSH rule 5 x 450 [4,1]
+ CRUSH rule 5 x 451 [6,5]
+ CRUSH rule 5 x 452 [8,3]
+ CRUSH rule 5 x 453 [6,5]
+ CRUSH rule 5 x 454 [6,4]
+ CRUSH rule 5 x 455 [2,7]
+ CRUSH rule 5 x 456 [6,2]
+ CRUSH rule 5 x 457 [7,2]
+ CRUSH rule 5 x 458 [2,8]
+ CRUSH rule 5 x 459 [2,7]
+ CRUSH rule 5 x 460 [6,5]
+ CRUSH rule 5 x 461 [6,5]
+ CRUSH rule 5 x 462 [8,1]
+ CRUSH rule 5 x 463 [6,0]
+ CRUSH rule 5 x 464 [7,4]
+ CRUSH rule 5 x 465 [7,2]
+ CRUSH rule 5 x 466 [5,8]
+ CRUSH rule 5 x 467 [6,4]
+ CRUSH rule 5 x 468 [7,0]
+ CRUSH rule 5 x 469 [7,0]
+ CRUSH rule 5 x 470 [3,0]
+ CRUSH rule 5 x 471 [0,7]
+ CRUSH rule 5 x 472 [5,1]
+ CRUSH rule 5 x 473 [1,4]
+ CRUSH rule 5 x 474 [6,0]
+ CRUSH rule 5 x 475 [6,2]
+ CRUSH rule 5 x 476 [4,6]
+ CRUSH rule 5 x 477 [5,8]
+ CRUSH rule 5 x 478 [6,2]
+ CRUSH rule 5 x 479 [0,5]
+ CRUSH rule 5 x 480 [1,8]
+ CRUSH rule 5 x 481 [2,4]
+ CRUSH rule 5 x 482 [4,7]
+ CRUSH rule 5 x 483 [0,6]
+ CRUSH rule 5 x 484 [1,7]
+ CRUSH rule 5 x 485 [4,7]
+ CRUSH rule 5 x 486 [4,1]
+ CRUSH rule 5 x 487 [5,0]
+ CRUSH rule 5 x 488 [5,7]
+ CRUSH rule 5 x 489 [2,8]
+ CRUSH rule 5 x 490 [6,4]
+ CRUSH rule 5 x 491 [1,7]
+ CRUSH rule 5 x 492 [6,5]
+ CRUSH rule 5 x 493 [0,7]
+ CRUSH rule 5 x 494 [1,7]
+ CRUSH rule 5 x 495 [3,1]
+ CRUSH rule 5 x 496 [7,5]
+ CRUSH rule 5 x 497 [5,7]
+ CRUSH rule 5 x 498 [0,5]
+ CRUSH rule 5 x 499 [8,4]
+ CRUSH rule 5 x 500 [3,6]
+ CRUSH rule 5 x 501 [0,7]
+ CRUSH rule 5 x 502 [7,1]
+ CRUSH rule 5 x 503 [2,3]
+ CRUSH rule 5 x 504 [5,6]
+ CRUSH rule 5 x 505 [0,7]
+ CRUSH rule 5 x 506 [5,2]
+ CRUSH rule 5 x 507 [6,0]
+ CRUSH rule 5 x 508 [0,3]
+ CRUSH rule 5 x 509 [7,5]
+ CRUSH rule 5 x 510 [6,0]
+ CRUSH rule 5 x 511 [5,8]
+ CRUSH rule 5 x 512 [7,0]
+ CRUSH rule 5 x 513 [7,2]
+ CRUSH rule 5 x 514 [4,6]
+ CRUSH rule 5 x 515 [8,5]
+ CRUSH rule 5 x 516 [4,0]
+ CRUSH rule 5 x 517 [7,2]
+ CRUSH rule 5 x 518 [4,6]
+ CRUSH rule 5 x 519 [7,3]
+ CRUSH rule 5 x 520 [2,6]
+ CRUSH rule 5 x 521 [8,0]
+ CRUSH rule 5 x 522 [6,0]
+ CRUSH rule 5 x 523 [4,2]
+ CRUSH rule 5 x 524 [0,4]
+ CRUSH rule 5 x 525 [0,4]
+ CRUSH rule 5 x 526 [1,5]
+ CRUSH rule 5 x 527 [0,5]
+ CRUSH rule 5 x 528 [5,0]
+ CRUSH rule 5 x 529 [5,7]
+ CRUSH rule 5 x 530 [6,5]
+ CRUSH rule 5 x 531 [6,1]
+ CRUSH rule 5 x 532 [6,3]
+ CRUSH rule 5 x 533 [5,6]
+ CRUSH rule 5 x 534 [7,3]
+ CRUSH rule 5 x 535 [8,1]
+ CRUSH rule 5 x 536 [6,2]
+ CRUSH rule 5 x 537 [3,7]
+ CRUSH rule 5 x 538 [6,3]
+ CRUSH rule 5 x 539 [8,3]
+ CRUSH rule 5 x 540 [0,6]
+ CRUSH rule 5 x 541 [2,3]
+ CRUSH rule 5 x 542 [3,2]
+ CRUSH rule 5 x 543 [6,0]
+ CRUSH rule 5 x 544 [3,7]
+ CRUSH rule 5 x 545 [5,7]
+ CRUSH rule 5 x 546 [6,1]
+ CRUSH rule 5 x 547 [8,2]
+ CRUSH rule 5 x 548 [5,2]
+ CRUSH rule 5 x 549 [5,8]
+ CRUSH rule 5 x 550 [0,5]
+ CRUSH rule 5 x 551 [7,5]
+ CRUSH rule 5 x 552 [5,8]
+ CRUSH rule 5 x 553 [4,2]
+ CRUSH rule 5 x 554 [0,8]
+ CRUSH rule 5 x 555 [5,0]
+ CRUSH rule 5 x 556 [3,6]
+ CRUSH rule 5 x 557 [7,4]
+ CRUSH rule 5 x 558 [3,1]
+ CRUSH rule 5 x 559 [4,2]
+ CRUSH rule 5 x 560 [8,3]
+ CRUSH rule 5 x 561 [6,3]
+ CRUSH rule 5 x 562 [3,0]
+ CRUSH rule 5 x 563 [2,6]
+ CRUSH rule 5 x 564 [5,1]
+ CRUSH rule 5 x 565 [3,6]
+ CRUSH rule 5 x 566 [4,7]
+ CRUSH rule 5 x 567 [3,6]
+ CRUSH rule 5 x 568 [7,4]
+ CRUSH rule 5 x 569 [3,1]
+ CRUSH rule 5 x 570 [1,5]
+ CRUSH rule 5 x 571 [3,7]
+ CRUSH rule 5 x 572 [3,2]
+ CRUSH rule 5 x 573 [3,0]
+ CRUSH rule 5 x 574 [2,5]
+ CRUSH rule 5 x 575 [8,2]
+ CRUSH rule 5 x 576 [4,6]
+ CRUSH rule 5 x 577 [8,2]
+ CRUSH rule 5 x 578 [6,1]
+ CRUSH rule 5 x 579 [3,1]
+ CRUSH rule 5 x 580 [3,0]
+ CRUSH rule 5 x 581 [7,2]
+ CRUSH rule 5 x 582 [2,8]
+ CRUSH rule 5 x 583 [6,0]
+ CRUSH rule 5 x 584 [8,1]
+ CRUSH rule 5 x 585 [7,0]
+ CRUSH rule 5 x 586 [0,7]
+ CRUSH rule 5 x 587 [2,5]
+ CRUSH rule 5 x 588 [3,7]
+ CRUSH rule 5 x 589 [7,1]
+ CRUSH rule 5 x 590 [6,2]
+ CRUSH rule 5 x 591 [5,2]
+ CRUSH rule 5 x 592 [2,4]
+ CRUSH rule 5 x 593 [0,8]
+ CRUSH rule 5 x 594 [0,7]
+ CRUSH rule 5 x 595 [7,1]
+ CRUSH rule 5 x 596 [4,0]
+ CRUSH rule 5 x 597 [3,1]
+ CRUSH rule 5 x 598 [3,2]
+ CRUSH rule 5 x 599 [5,2]
+ CRUSH rule 5 x 600 [7,0]
+ CRUSH rule 5 x 601 [0,7]
+ CRUSH rule 5 x 602 [3,7]
+ CRUSH rule 5 x 603 [5,1]
+ CRUSH rule 5 x 604 [7,5]
+ CRUSH rule 5 x 605 [3,0]
+ CRUSH rule 5 x 606 [2,7]
+ CRUSH rule 5 x 607 [0,4]
+ CRUSH rule 5 x 608 [5,2]
+ CRUSH rule 5 x 609 [5,2]
+ CRUSH rule 5 x 610 [3,7]
+ CRUSH rule 5 x 611 [1,8]
+ CRUSH rule 5 x 612 [2,6]
+ CRUSH rule 5 x 613 [7,2]
+ CRUSH rule 5 x 614 [7,2]
+ CRUSH rule 5 x 615 [6,0]
+ CRUSH rule 5 x 616 [0,8]
+ CRUSH rule 5 x 617 [6,1]
+ CRUSH rule 5 x 618 [7,4]
+ CRUSH rule 5 x 619 [5,1]
+ CRUSH rule 5 x 620 [4,1]
+ CRUSH rule 5 x 621 [5,8]
+ CRUSH rule 5 x 622 [0,4]
+ CRUSH rule 5 x 623 [0,6]
+ CRUSH rule 5 x 624 [3,2]
+ CRUSH rule 5 x 625 [2,3]
+ CRUSH rule 5 x 626 [7,0]
+ CRUSH rule 5 x 627 [2,7]
+ CRUSH rule 5 x 628 [8,0]
+ CRUSH rule 5 x 629 [2,6]
+ CRUSH rule 5 x 630 [2,7]
+ CRUSH rule 5 x 631 [0,7]
+ CRUSH rule 5 x 632 [7,0]
+ CRUSH rule 5 x 633 [8,3]
+ CRUSH rule 5 x 634 [0,4]
+ CRUSH rule 5 x 635 [5,6]
+ CRUSH rule 5 x 636 [1,4]
+ CRUSH rule 5 x 637 [4,1]
+ CRUSH rule 5 x 638 [6,0]
+ CRUSH rule 5 x 639 [4,2]
+ CRUSH rule 5 x 640 [3,1]
+ CRUSH rule 5 x 641 [7,2]
+ CRUSH rule 5 x 642 [2,7]
+ CRUSH rule 5 x 643 [3,0]
+ CRUSH rule 5 x 644 [8,1]
+ CRUSH rule 5 x 645 [5,7]
+ CRUSH rule 5 x 646 [8,0]
+ CRUSH rule 5 x 647 [7,1]
+ CRUSH rule 5 x 648 [0,6]
+ CRUSH rule 5 x 649 [4,7]
+ CRUSH rule 5 x 650 [7,3]
+ CRUSH rule 5 x 651 [3,7]
+ CRUSH rule 5 x 652 [3,7]
+ CRUSH rule 5 x 653 [8,5]
+ CRUSH rule 5 x 654 [7,5]
+ CRUSH rule 5 x 655 [0,3]
+ CRUSH rule 5 x 656 [4,7]
+ CRUSH rule 5 x 657 [6,1]
+ CRUSH rule 5 x 658 [5,6]
+ CRUSH rule 5 x 659 [4,6]
+ CRUSH rule 5 x 660 [7,4]
+ CRUSH rule 5 x 661 [1,8]
+ CRUSH rule 5 x 662 [4,2]
+ CRUSH rule 5 x 663 [1,3]
+ CRUSH rule 5 x 664 [1,4]
+ CRUSH rule 5 x 665 [5,7]
+ CRUSH rule 5 x 666 [2,8]
+ CRUSH rule 5 x 667 [1,3]
+ CRUSH rule 5 x 668 [3,7]
+ CRUSH rule 5 x 669 [6,4]
+ CRUSH rule 5 x 670 [4,0]
+ CRUSH rule 5 x 671 [0,7]
+ CRUSH rule 5 x 672 [4,2]
+ CRUSH rule 5 x 673 [5,2]
+ CRUSH rule 5 x 674 [3,1]
+ CRUSH rule 5 x 675 [0,8]
+ CRUSH rule 5 x 676 [0,4]
+ CRUSH rule 5 x 677 [4,1]
+ CRUSH rule 5 x 678 [2,3]
+ CRUSH rule 5 x 679 [6,0]
+ CRUSH rule 5 x 680 [0,4]
+ CRUSH rule 5 x 681 [4,7]
+ CRUSH rule 5 x 682 [0,5]
+ CRUSH rule 5 x 683 [0,5]
+ CRUSH rule 5 x 684 [7,1]
+ CRUSH rule 5 x 685 [7,1]
+ CRUSH rule 5 x 686 [1,4]
+ CRUSH rule 5 x 687 [3,6]
+ CRUSH rule 5 x 688 [5,7]
+ CRUSH rule 5 x 689 [6,5]
+ CRUSH rule 5 x 690 [8,1]
+ CRUSH rule 5 x 691 [3,0]
+ CRUSH rule 5 x 692 [7,2]
+ CRUSH rule 5 x 693 [6,3]
+ CRUSH rule 5 x 694 [6,5]
+ CRUSH rule 5 x 695 [0,8]
+ CRUSH rule 5 x 696 [1,4]
+ CRUSH rule 5 x 697 [6,1]
+ CRUSH rule 5 x 698 [6,2]
+ CRUSH rule 5 x 699 [1,6]
+ CRUSH rule 5 x 700 [0,3]
+ CRUSH rule 5 x 701 [4,1]
+ CRUSH rule 5 x 702 [3,2]
+ CRUSH rule 5 x 703 [8,3]
+ CRUSH rule 5 x 704 [0,3]
+ CRUSH rule 5 x 705 [8,0]
+ CRUSH rule 5 x 706 [1,5]
+ CRUSH rule 5 x 707 [7,3]
+ CRUSH rule 5 x 708 [3,7]
+ CRUSH rule 5 x 709 [6,3]
+ CRUSH rule 5 x 710 [8,4]
+ CRUSH rule 5 x 711 [2,3]
+ CRUSH rule 5 x 712 [2,3]
+ CRUSH rule 5 x 713 [6,3]
+ CRUSH rule 5 x 714 [3,2]
+ CRUSH rule 5 x 715 [1,3]
+ CRUSH rule 5 x 716 [3,6]
+ CRUSH rule 5 x 717 [8,2]
+ CRUSH rule 5 x 718 [3,7]
+ CRUSH rule 5 x 719 [2,6]
+ CRUSH rule 5 x 720 [6,1]
+ CRUSH rule 5 x 721 [5,7]
+ CRUSH rule 5 x 722 [5,7]
+ CRUSH rule 5 x 723 [5,1]
+ CRUSH rule 5 x 724 [0,6]
+ CRUSH rule 5 x 725 [0,3]
+ CRUSH rule 5 x 726 [3,8]
+ CRUSH rule 5 x 727 [4,6]
+ CRUSH rule 5 x 728 [2,7]
+ CRUSH rule 5 x 729 [5,6]
+ CRUSH rule 5 x 730 [3,7]
+ CRUSH rule 5 x 731 [4,1]
+ CRUSH rule 5 x 732 [1,5]
+ CRUSH rule 5 x 733 [5,7]
+ CRUSH rule 5 x 734 [6,4]
+ CRUSH rule 5 x 735 [4,8]
+ CRUSH rule 5 x 736 [3,8]
+ CRUSH rule 5 x 737 [1,6]
+ CRUSH rule 5 x 738 [5,2]
+ CRUSH rule 5 x 739 [0,7]
+ CRUSH rule 5 x 740 [0,8]
+ CRUSH rule 5 x 741 [7,1]
+ CRUSH rule 5 x 742 [8,2]
+ CRUSH rule 5 x 743 [7,0]
+ CRUSH rule 5 x 744 [4,7]
+ CRUSH rule 5 x 745 [3,1]
+ CRUSH rule 5 x 746 [4,1]
+ CRUSH rule 5 x 747 [6,0]
+ CRUSH rule 5 x 748 [2,7]
+ CRUSH rule 5 x 749 [4,8]
+ CRUSH rule 5 x 750 [1,6]
+ CRUSH rule 5 x 751 [2,8]
+ CRUSH rule 5 x 752 [8,1]
+ CRUSH rule 5 x 753 [7,3]
+ CRUSH rule 5 x 754 [8,5]
+ CRUSH rule 5 x 755 [1,6]
+ CRUSH rule 5 x 756 [5,6]
+ CRUSH rule 5 x 757 [8,0]
+ CRUSH rule 5 x 758 [6,0]
+ CRUSH rule 5 x 759 [8,5]
+ CRUSH rule 5 x 760 [1,5]
+ CRUSH rule 5 x 761 [4,1]
+ CRUSH rule 5 x 762 [2,7]
+ CRUSH rule 5 x 763 [8,5]
+ CRUSH rule 5 x 764 [1,7]
+ CRUSH rule 5 x 765 [6,5]
+ CRUSH rule 5 x 766 [8,5]
+ CRUSH rule 5 x 767 [1,8]
+ CRUSH rule 5 x 768 [8,3]
+ CRUSH rule 5 x 769 [6,2]
+ CRUSH rule 5 x 770 [6,0]
+ CRUSH rule 5 x 771 [7,0]
+ CRUSH rule 5 x 772 [8,3]
+ CRUSH rule 5 x 773 [3,1]
+ CRUSH rule 5 x 774 [4,6]
+ CRUSH rule 5 x 775 [6,4]
+ CRUSH rule 5 x 776 [7,2]
+ CRUSH rule 5 x 777 [3,1]
+ CRUSH rule 5 x 778 [1,8]
+ CRUSH rule 5 x 779 [2,7]
+ CRUSH rule 5 x 780 [0,5]
+ CRUSH rule 5 x 781 [6,3]
+ CRUSH rule 5 x 782 [5,0]
+ CRUSH rule 5 x 783 [7,1]
+ CRUSH rule 5 x 784 [0,4]
+ CRUSH rule 5 x 785 [6,1]
+ CRUSH rule 5 x 786 [7,3]
+ CRUSH rule 5 x 787 [1,6]
+ CRUSH rule 5 x 788 [6,0]
+ CRUSH rule 5 x 789 [0,4]
+ CRUSH rule 5 x 790 [8,4]
+ CRUSH rule 5 x 791 [3,8]
+ CRUSH rule 5 x 792 [5,8]
+ CRUSH rule 5 x 793 [6,1]
+ CRUSH rule 5 x 794 [2,6]
+ CRUSH rule 5 x 795 [0,3]
+ CRUSH rule 5 x 796 [3,7]
+ CRUSH rule 5 x 797 [2,3]
+ CRUSH rule 5 x 798 [6,1]
+ CRUSH rule 5 x 799 [5,1]
+ CRUSH rule 5 x 800 [5,0]
+ CRUSH rule 5 x 801 [3,6]
+ CRUSH rule 5 x 802 [1,8]
+ CRUSH rule 5 x 803 [0,5]
+ CRUSH rule 5 x 804 [6,2]
+ CRUSH rule 5 x 805 [3,6]
+ CRUSH rule 5 x 806 [1,3]
+ CRUSH rule 5 x 807 [5,7]
+ CRUSH rule 5 x 808 [4,6]
+ CRUSH rule 5 x 809 [1,4]
+ CRUSH rule 5 x 810 [5,7]
+ CRUSH rule 5 x 811 [8,4]
+ CRUSH rule 5 x 812 [8,5]
+ CRUSH rule 5 x 813 [6,4]
+ CRUSH rule 5 x 814 [3,6]
+ CRUSH rule 5 x 815 [3,1]
+ CRUSH rule 5 x 816 [2,7]
+ CRUSH rule 5 x 817 [4,8]
+ CRUSH rule 5 x 818 [3,0]
+ CRUSH rule 5 x 819 [5,1]
+ CRUSH rule 5 x 820 [3,6]
+ CRUSH rule 5 x 821 [4,6]
+ CRUSH rule 5 x 822 [2,5]
+ CRUSH rule 5 x 823 [4,8]
+ CRUSH rule 5 x 824 [3,7]
+ CRUSH rule 5 x 825 [2,8]
+ CRUSH rule 5 x 826 [7,0]
+ CRUSH rule 5 x 827 [0,8]
+ CRUSH rule 5 x 828 [2,3]
+ CRUSH rule 5 x 829 [5,6]
+ CRUSH rule 5 x 830 [2,3]
+ CRUSH rule 5 x 831 [1,6]
+ CRUSH rule 5 x 832 [4,7]
+ CRUSH rule 5 x 833 [2,7]
+ CRUSH rule 5 x 834 [3,1]
+ CRUSH rule 5 x 835 [8,4]
+ CRUSH rule 5 x 836 [3,7]
+ CRUSH rule 5 x 837 [6,3]
+ CRUSH rule 5 x 838 [6,2]
+ CRUSH rule 5 x 839 [5,0]
+ CRUSH rule 5 x 840 [7,3]
+ CRUSH rule 5 x 841 [4,8]
+ CRUSH rule 5 x 842 [2,4]
+ CRUSH rule 5 x 843 [6,4]
+ CRUSH rule 5 x 844 [4,8]
+ CRUSH rule 5 x 845 [3,8]
+ CRUSH rule 5 x 846 [3,2]
+ CRUSH rule 5 x 847 [0,8]
+ CRUSH rule 5 x 848 [2,6]
+ CRUSH rule 5 x 849 [4,6]
+ CRUSH rule 5 x 850 [1,3]
+ CRUSH rule 5 x 851 [6,4]
+ CRUSH rule 5 x 852 [7,3]
+ CRUSH rule 5 x 853 [6,0]
+ CRUSH rule 5 x 854 [7,0]
+ CRUSH rule 5 x 855 [5,7]
+ CRUSH rule 5 x 856 [6,3]
+ CRUSH rule 5 x 857 [8,5]
+ CRUSH rule 5 x 858 [6,4]
+ CRUSH rule 5 x 859 [6,0]
+ CRUSH rule 5 x 860 [4,1]
+ CRUSH rule 5 x 861 [8,3]
+ CRUSH rule 5 x 862 [6,1]
+ CRUSH rule 5 x 863 [8,2]
+ CRUSH rule 5 x 864 [5,6]
+ CRUSH rule 5 x 865 [8,1]
+ CRUSH rule 5 x 866 [3,6]
+ CRUSH rule 5 x 867 [6,5]
+ CRUSH rule 5 x 868 [6,3]
+ CRUSH rule 5 x 869 [8,5]
+ CRUSH rule 5 x 870 [0,4]
+ CRUSH rule 5 x 871 [3,2]
+ CRUSH rule 5 x 872 [5,1]
+ CRUSH rule 5 x 873 [4,6]
+ CRUSH rule 5 x 874 [2,6]
+ CRUSH rule 5 x 875 [2,6]
+ CRUSH rule 5 x 876 [5,8]
+ CRUSH rule 5 x 877 [6,4]
+ CRUSH rule 5 x 878 [5,2]
+ CRUSH rule 5 x 879 [7,4]
+ CRUSH rule 5 x 880 [3,2]
+ CRUSH rule 5 x 881 [5,6]
+ CRUSH rule 5 x 882 [4,0]
+ CRUSH rule 5 x 883 [2,3]
+ CRUSH rule 5 x 884 [6,0]
+ CRUSH rule 5 x 885 [5,1]
+ CRUSH rule 5 x 886 [3,6]
+ CRUSH rule 5 x 887 [7,4]
+ CRUSH rule 5 x 888 [6,2]
+ CRUSH rule 5 x 889 [2,6]
+ CRUSH rule 5 x 890 [7,2]
+ CRUSH rule 5 x 891 [1,8]
+ CRUSH rule 5 x 892 [6,2]
+ CRUSH rule 5 x 893 [2,3]
+ CRUSH rule 5 x 894 [7,5]
+ CRUSH rule 5 x 895 [5,1]
+ CRUSH rule 5 x 896 [1,8]
+ CRUSH rule 5 x 897 [4,2]
+ CRUSH rule 5 x 898 [0,5]
+ CRUSH rule 5 x 899 [1,7]
+ CRUSH rule 5 x 900 [4,1]
+ CRUSH rule 5 x 901 [5,0]
+ CRUSH rule 5 x 902 [8,5]
+ CRUSH rule 5 x 903 [5,7]
+ CRUSH rule 5 x 904 [5,6]
+ CRUSH rule 5 x 905 [6,2]
+ CRUSH rule 5 x 906 [1,6]
+ CRUSH rule 5 x 907 [7,1]
+ CRUSH rule 5 x 908 [5,8]
+ CRUSH rule 5 x 909 [2,3]
+ CRUSH rule 5 x 910 [6,4]
+ CRUSH rule 5 x 911 [5,8]
+ CRUSH rule 5 x 912 [0,7]
+ CRUSH rule 5 x 913 [7,2]
+ CRUSH rule 5 x 914 [6,4]
+ CRUSH rule 5 x 915 [8,2]
+ CRUSH rule 5 x 916 [3,1]
+ CRUSH rule 5 x 917 [1,5]
+ CRUSH rule 5 x 918 [8,2]
+ CRUSH rule 5 x 919 [6,2]
+ CRUSH rule 5 x 920 [7,4]
+ CRUSH rule 5 x 921 [1,4]
+ CRUSH rule 5 x 922 [6,4]
+ CRUSH rule 5 x 923 [5,8]
+ CRUSH rule 5 x 924 [3,1]
+ CRUSH rule 5 x 925 [5,7]
+ CRUSH rule 5 x 926 [3,0]
+ CRUSH rule 5 x 927 [1,6]
+ CRUSH rule 5 x 928 [8,1]
+ CRUSH rule 5 x 929 [4,1]
+ CRUSH rule 5 x 930 [2,4]
+ CRUSH rule 5 x 931 [5,0]
+ CRUSH rule 5 x 932 [4,1]
+ CRUSH rule 5 x 933 [8,5]
+ CRUSH rule 5 x 934 [5,6]
+ CRUSH rule 5 x 935 [6,3]
+ CRUSH rule 5 x 936 [0,6]
+ CRUSH rule 5 x 937 [5,8]
+ CRUSH rule 5 x 938 [6,5]
+ CRUSH rule 5 x 939 [2,7]
+ CRUSH rule 5 x 940 [8,5]
+ CRUSH rule 5 x 941 [5,2]
+ CRUSH rule 5 x 942 [1,8]
+ CRUSH rule 5 x 943 [8,2]
+ CRUSH rule 5 x 944 [4,8]
+ CRUSH rule 5 x 945 [7,2]
+ CRUSH rule 5 x 946 [2,8]
+ CRUSH rule 5 x 947 [4,2]
+ CRUSH rule 5 x 948 [7,5]
+ CRUSH rule 5 x 949 [6,1]
+ CRUSH rule 5 x 950 [3,6]
+ CRUSH rule 5 x 951 [4,8]
+ CRUSH rule 5 x 952 [2,7]
+ CRUSH rule 5 x 953 [1,3]
+ CRUSH rule 5 x 954 [4,2]
+ CRUSH rule 5 x 955 [8,0]
+ CRUSH rule 5 x 956 [1,6]
+ CRUSH rule 5 x 957 [7,1]
+ CRUSH rule 5 x 958 [8,4]
+ CRUSH rule 5 x 959 [5,2]
+ CRUSH rule 5 x 960 [3,6]
+ CRUSH rule 5 x 961 [4,0]
+ CRUSH rule 5 x 962 [7,4]
+ CRUSH rule 5 x 963 [0,5]
+ CRUSH rule 5 x 964 [3,1]
+ CRUSH rule 5 x 965 [7,4]
+ CRUSH rule 5 x 966 [3,8]
+ CRUSH rule 5 x 967 [8,5]
+ CRUSH rule 5 x 968 [7,2]
+ CRUSH rule 5 x 969 [8,0]
+ CRUSH rule 5 x 970 [0,6]
+ CRUSH rule 5 x 971 [1,7]
+ CRUSH rule 5 x 972 [1,8]
+ CRUSH rule 5 x 973 [1,6]
+ CRUSH rule 5 x 974 [5,1]
+ CRUSH rule 5 x 975 [3,7]
+ CRUSH rule 5 x 976 [4,8]
+ CRUSH rule 5 x 977 [8,3]
+ CRUSH rule 5 x 978 [7,2]
+ CRUSH rule 5 x 979 [7,1]
+ CRUSH rule 5 x 980 [6,0]
+ CRUSH rule 5 x 981 [7,3]
+ CRUSH rule 5 x 982 [4,2]
+ CRUSH rule 5 x 983 [3,7]
+ CRUSH rule 5 x 984 [0,7]
+ CRUSH rule 5 x 985 [2,5]
+ CRUSH rule 5 x 986 [8,3]
+ CRUSH rule 5 x 987 [0,5]
+ CRUSH rule 5 x 988 [1,3]
+ CRUSH rule 5 x 989 [0,6]
+ CRUSH rule 5 x 990 [1,6]
+ CRUSH rule 5 x 991 [0,4]
+ CRUSH rule 5 x 992 [7,1]
+ CRUSH rule 5 x 993 [0,6]
+ CRUSH rule 5 x 994 [3,7]
+ CRUSH rule 5 x 995 [7,1]
+ CRUSH rule 5 x 996 [6,5]
+ CRUSH rule 5 x 997 [6,4]
+ CRUSH rule 5 x 998 [8,1]
+ CRUSH rule 5 x 999 [0,7]
+ CRUSH rule 5 x 1000 [8,5]
+ CRUSH rule 5 x 1001 [2,5]
+ CRUSH rule 5 x 1002 [1,3]
+ CRUSH rule 5 x 1003 [2,8]
+ CRUSH rule 5 x 1004 [6,1]
+ CRUSH rule 5 x 1005 [6,1]
+ CRUSH rule 5 x 1006 [1,6]
+ CRUSH rule 5 x 1007 [1,5]
+ CRUSH rule 5 x 1008 [1,7]
+ CRUSH rule 5 x 1009 [6,4]
+ CRUSH rule 5 x 1010 [3,1]
+ CRUSH rule 5 x 1011 [3,0]
+ CRUSH rule 5 x 1012 [3,0]
+ CRUSH rule 5 x 1013 [5,1]
+ CRUSH rule 5 x 1014 [2,8]
+ CRUSH rule 5 x 1015 [6,5]
+ CRUSH rule 5 x 1016 [2,4]
+ CRUSH rule 5 x 1017 [6,0]
+ CRUSH rule 5 x 1018 [5,0]
+ CRUSH rule 5 x 1019 [5,8]
+ CRUSH rule 5 x 1020 [5,1]
+ CRUSH rule 5 x 1021 [5,2]
+ CRUSH rule 5 x 1022 [1,6]
+ CRUSH rule 5 x 1023 [3,2]
+ rule 5 (chooseleaf-set) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 5 x 0 [0,5,7]
+ CRUSH rule 5 x 1 [0,8,5]
+ CRUSH rule 5 x 2 [1,3,7]
+ CRUSH rule 5 x 3 [8,0,4]
+ CRUSH rule 5 x 4 [5,0,7]
+ CRUSH rule 5 x 5 [7,0,4]
+ CRUSH rule 5 x 6 [2,6,3]
+ CRUSH rule 5 x 7 [5,8,2]
+ CRUSH rule 5 x 8 [5,6,0]
+ CRUSH rule 5 x 9 [2,3,8]
+ CRUSH rule 5 x 10 [0,7,4]
+ CRUSH rule 5 x 11 [0,7,4]
+ CRUSH rule 5 x 12 [0,4,6]
+ CRUSH rule 5 x 13 [3,8,1]
+ CRUSH rule 5 x 14 [7,0,5]
+ CRUSH rule 5 x 15 [7,2,4]
+ CRUSH rule 5 x 16 [3,6,0]
+ CRUSH rule 5 x 17 [5,1,6]
+ CRUSH rule 5 x 18 [1,4,6]
+ CRUSH rule 5 x 19 [7,5,0]
+ CRUSH rule 5 x 20 [2,4,7]
+ CRUSH rule 5 x 21 [3,7,2]
+ CRUSH rule 5 x 22 [8,3,1]
+ CRUSH rule 5 x 23 [3,6,2]
+ CRUSH rule 5 x 24 [1,7,3]
+ CRUSH rule 5 x 25 [3,7,0]
+ CRUSH rule 5 x 26 [2,8,4]
+ CRUSH rule 5 x 27 [3,1,8]
+ CRUSH rule 5 x 28 [6,0,3]
+ CRUSH rule 5 x 29 [8,5,2]
+ CRUSH rule 5 x 30 [5,7,0]
+ CRUSH rule 5 x 31 [8,0,4]
+ CRUSH rule 5 x 32 [3,6,2]
+ CRUSH rule 5 x 33 [2,7,5]
+ CRUSH rule 5 x 34 [2,5,7]
+ CRUSH rule 5 x 35 [0,8,5]
+ CRUSH rule 5 x 36 [3,8,2]
+ CRUSH rule 5 x 37 [0,4,6]
+ CRUSH rule 5 x 38 [4,8,2]
+ CRUSH rule 5 x 39 [3,7,0]
+ CRUSH rule 5 x 40 [7,2,3]
+ CRUSH rule 5 x 41 [0,6,4]
+ CRUSH rule 5 x 42 [4,6,2]
+ CRUSH rule 5 x 43 [0,3,7]
+ CRUSH rule 5 x 44 [1,6,5]
+ CRUSH rule 5 x 45 [8,0,4]
+ CRUSH rule 5 x 46 [2,4,8]
+ CRUSH rule 5 x 47 [4,2,8]
+ CRUSH rule 5 x 48 [4,6,2]
+ CRUSH rule 5 x 49 [5,7,2]
+ CRUSH rule 5 x 50 [3,1,7]
+ CRUSH rule 5 x 51 [3,6,0]
+ CRUSH rule 5 x 52 [8,1,4]
+ CRUSH rule 5 x 53 [3,8,0]
+ CRUSH rule 5 x 54 [7,3,1]
+ CRUSH rule 5 x 55 [8,2,4]
+ CRUSH rule 5 x 56 [6,4,2]
+ CRUSH rule 5 x 57 [5,8,1]
+ CRUSH rule 5 x 58 [1,8,5]
+ CRUSH rule 5 x 59 [4,2,7]
+ CRUSH rule 5 x 60 [3,2,6]
+ CRUSH rule 5 x 61 [4,6,0]
+ CRUSH rule 5 x 62 [7,0,5]
+ CRUSH rule 5 x 63 [5,6,0]
+ CRUSH rule 5 x 64 [4,2,8]
+ CRUSH rule 5 x 65 [7,3,0]
+ CRUSH rule 5 x 66 [5,6,2]
+ CRUSH rule 5 x 67 [5,0,8]
+ CRUSH rule 5 x 68 [0,5,8]
+ CRUSH rule 5 x 69 [5,1,6]
+ CRUSH rule 5 x 70 [7,0,5]
+ CRUSH rule 5 x 71 [2,8,5]
+ CRUSH rule 5 x 72 [6,1,5]
+ CRUSH rule 5 x 73 [2,7,3]
+ CRUSH rule 5 x 74 [0,7,3]
+ CRUSH rule 5 x 75 [3,2,6]
+ CRUSH rule 5 x 76 [5,1,7]
+ CRUSH rule 5 x 77 [7,2,5]
+ CRUSH rule 5 x 78 [1,4,8]
+ CRUSH rule 5 x 79 [5,1,7]
+ CRUSH rule 5 x 80 [0,3,6]
+ CRUSH rule 5 x 81 [0,3,6]
+ CRUSH rule 5 x 82 [7,1,5]
+ CRUSH rule 5 x 83 [2,6,3]
+ CRUSH rule 5 x 84 [7,2,4]
+ CRUSH rule 5 x 85 [3,8,0]
+ CRUSH rule 5 x 86 [0,6,3]
+ CRUSH rule 5 x 87 [0,7,4]
+ CRUSH rule 5 x 88 [1,6,5]
+ CRUSH rule 5 x 89 [3,0,7]
+ CRUSH rule 5 x 90 [6,4,1]
+ CRUSH rule 5 x 91 [3,8,1]
+ CRUSH rule 5 x 92 [1,8,4]
+ CRUSH rule 5 x 93 [7,4,2]
+ CRUSH rule 5 x 94 [0,4,8]
+ CRUSH rule 5 x 95 [7,5,1]
+ CRUSH rule 5 x 96 [3,6,1]
+ CRUSH rule 5 x 97 [8,4,0]
+ CRUSH rule 5 x 98 [2,7,5]
+ CRUSH rule 5 x 99 [0,7,3]
+ CRUSH rule 5 x 100 [1,7,4]
+ CRUSH rule 5 x 101 [3,7,2]
+ CRUSH rule 5 x 102 [4,2,7]
+ CRUSH rule 5 x 103 [4,7,0]
+ CRUSH rule 5 x 104 [7,4,1]
+ CRUSH rule 5 x 105 [2,4,6]
+ CRUSH rule 5 x 106 [1,6,5]
+ CRUSH rule 5 x 107 [3,2,6]
+ CRUSH rule 5 x 108 [7,2,5]
+ CRUSH rule 5 x 109 [1,4,6]
+ CRUSH rule 5 x 110 [3,2,7]
+ CRUSH rule 5 x 111 [2,3,6]
+ CRUSH rule 5 x 112 [2,6,4]
+ CRUSH rule 5 x 113 [6,2,4]
+ CRUSH rule 5 x 114 [7,3,1]
+ CRUSH rule 5 x 115 [8,2,3]
+ CRUSH rule 5 x 116 [1,6,3]
+ CRUSH rule 5 x 117 [7,3,2]
+ CRUSH rule 5 x 118 [0,3,8]
+ CRUSH rule 5 x 119 [5,6,1]
+ CRUSH rule 5 x 120 [0,3,7]
+ CRUSH rule 5 x 121 [2,7,5]
+ CRUSH rule 5 x 122 [8,5,0]
+ CRUSH rule 5 x 123 [2,5,8]
+ CRUSH rule 5 x 124 [3,2,8]
+ CRUSH rule 5 x 125 [0,7,3]
+ CRUSH rule 5 x 126 [4,2,6]
+ CRUSH rule 5 x 127 [3,6,2]
+ CRUSH rule 5 x 128 [3,6,1]
+ CRUSH rule 5 x 129 [0,3,7]
+ CRUSH rule 5 x 130 [3,8,2]
+ CRUSH rule 5 x 131 [1,3,8]
+ CRUSH rule 5 x 132 [1,4,6]
+ CRUSH rule 5 x 133 [3,6,2]
+ CRUSH rule 5 x 134 [1,8,5]
+ CRUSH rule 5 x 135 [5,6,2]
+ CRUSH rule 5 x 136 [2,3,6]
+ CRUSH rule 5 x 137 [7,3,2]
+ CRUSH rule 5 x 138 [8,4,2]
+ CRUSH rule 5 x 139 [3,0,7]
+ CRUSH rule 5 x 140 [1,6,4]
+ CRUSH rule 5 x 141 [6,2,3]
+ CRUSH rule 5 x 142 [3,0,8]
+ CRUSH rule 5 x 143 [5,8,1]
+ CRUSH rule 5 x 144 [8,1,3]
+ CRUSH rule 5 x 145 [8,5,0]
+ CRUSH rule 5 x 146 [2,6,4]
+ CRUSH rule 5 x 147 [2,8,4]
+ CRUSH rule 5 x 148 [3,1,7]
+ CRUSH rule 5 x 149 [4,8,1]
+ CRUSH rule 5 x 150 [1,6,5]
+ CRUSH rule 5 x 151 [3,6,0]
+ CRUSH rule 5 x 152 [8,4,2]
+ CRUSH rule 5 x 153 [8,4,0]
+ CRUSH rule 5 x 154 [3,2,7]
+ CRUSH rule 5 x 155 [3,7,2]
+ CRUSH rule 5 x 156 [4,2,6]
+ CRUSH rule 5 x 157 [4,1,6]
+ CRUSH rule 5 x 158 [2,8,5]
+ CRUSH rule 5 x 159 [7,0,5]
+ CRUSH rule 5 x 160 [2,8,5]
+ CRUSH rule 5 x 161 [1,5,6]
+ CRUSH rule 5 x 162 [0,6,4]
+ CRUSH rule 5 x 163 [5,6,1]
+ CRUSH rule 5 x 164 [7,1,5]
+ CRUSH rule 5 x 165 [7,0,5]
+ CRUSH rule 5 x 166 [2,4,6]
+ CRUSH rule 5 x 167 [0,7,4]
+ CRUSH rule 5 x 168 [4,2,7]
+ CRUSH rule 5 x 169 [2,6,4]
+ CRUSH rule 5 x 170 [1,4,8]
+ CRUSH rule 5 x 171 [7,5,0]
+ CRUSH rule 5 x 172 [0,7,5]
+ CRUSH rule 5 x 173 [8,5,1]
+ CRUSH rule 5 x 174 [1,4,7]
+ CRUSH rule 5 x 175 [6,0,4]
+ CRUSH rule 5 x 176 [4,1,7]
+ CRUSH rule 5 x 177 [5,1,6]
+ CRUSH rule 5 x 178 [3,0,8]
+ CRUSH rule 5 x 179 [4,1,7]
+ CRUSH rule 5 x 180 [3,8,1]
+ CRUSH rule 5 x 181 [6,2,4]
+ CRUSH rule 5 x 182 [8,5,1]
+ CRUSH rule 5 x 183 [7,5,1]
+ CRUSH rule 5 x 184 [5,7,2]
+ CRUSH rule 5 x 185 [6,1,4]
+ CRUSH rule 5 x 186 [2,5,7]
+ CRUSH rule 5 x 187 [1,6,4]
+ CRUSH rule 5 x 188 [1,8,5]
+ CRUSH rule 5 x 189 [0,7,4]
+ CRUSH rule 5 x 190 [4,0,8]
+ CRUSH rule 5 x 191 [7,1,3]
+ CRUSH rule 5 x 192 [5,0,8]
+ CRUSH rule 5 x 193 [4,2,6]
+ CRUSH rule 5 x 194 [1,3,8]
+ CRUSH rule 5 x 195 [6,4,1]
+ CRUSH rule 5 x 196 [6,0,3]
+ CRUSH rule 5 x 197 [6,5,2]
+ CRUSH rule 5 x 198 [2,5,6]
+ CRUSH rule 5 x 199 [0,5,7]
+ CRUSH rule 5 x 200 [0,5,6]
+ CRUSH rule 5 x 201 [7,1,5]
+ CRUSH rule 5 x 202 [6,3,1]
+ CRUSH rule 5 x 203 [4,8,1]
+ CRUSH rule 5 x 204 [2,3,7]
+ CRUSH rule 5 x 205 [0,7,5]
+ CRUSH rule 5 x 206 [0,7,5]
+ CRUSH rule 5 x 207 [3,0,7]
+ CRUSH rule 5 x 208 [7,1,4]
+ CRUSH rule 5 x 209 [1,8,4]
+ CRUSH rule 5 x 210 [1,4,8]
+ CRUSH rule 5 x 211 [5,2,8]
+ CRUSH rule 5 x 212 [7,5,0]
+ CRUSH rule 5 x 213 [8,4,0]
+ CRUSH rule 5 x 214 [4,8,2]
+ CRUSH rule 5 x 215 [8,1,4]
+ CRUSH rule 5 x 216 [5,0,6]
+ CRUSH rule 5 x 217 [0,7,3]
+ CRUSH rule 5 x 218 [0,7,5]
+ CRUSH rule 5 x 219 [4,8,2]
+ CRUSH rule 5 x 220 [5,7,1]
+ CRUSH rule 5 x 221 [3,6,0]
+ CRUSH rule 5 x 222 [6,4,1]
+ CRUSH rule 5 x 223 [1,3,6]
+ CRUSH rule 5 x 224 [1,5,8]
+ CRUSH rule 5 x 225 [8,2,3]
+ CRUSH rule 5 x 226 [7,2,3]
+ CRUSH rule 5 x 227 [3,1,6]
+ CRUSH rule 5 x 228 [5,6,0]
+ CRUSH rule 5 x 229 [3,8,2]
+ CRUSH rule 5 x 230 [4,7,2]
+ CRUSH rule 5 x 231 [4,7,2]
+ CRUSH rule 5 x 232 [2,7,4]
+ CRUSH rule 5 x 233 [3,7,2]
+ CRUSH rule 5 x 234 [0,3,6]
+ CRUSH rule 5 x 235 [3,8,0]
+ CRUSH rule 5 x 236 [5,2,7]
+ CRUSH rule 5 x 237 [4,7,1]
+ CRUSH rule 5 x 238 [4,2,6]
+ CRUSH rule 5 x 239 [8,4,1]
+ CRUSH rule 5 x 240 [5,7,0]
+ CRUSH rule 5 x 241 [3,1,8]
+ CRUSH rule 5 x 242 [3,2,6]
+ CRUSH rule 5 x 243 [4,7,0]
+ CRUSH rule 5 x 244 [4,6,0]
+ CRUSH rule 5 x 245 [7,0,5]
+ CRUSH rule 5 x 246 [1,5,8]
+ CRUSH rule 5 x 247 [6,0,4]
+ CRUSH rule 5 x 248 [8,0,3]
+ CRUSH rule 5 x 249 [2,4,7]
+ CRUSH rule 5 x 250 [2,5,6]
+ CRUSH rule 5 x 251 [2,3,6]
+ CRUSH rule 5 x 252 [3,7,0]
+ CRUSH rule 5 x 253 [3,2,6]
+ CRUSH rule 5 x 254 [3,2,7]
+ CRUSH rule 5 x 255 [1,7,5]
+ CRUSH rule 5 x 256 [5,7,0]
+ CRUSH rule 5 x 257 [2,8,4]
+ CRUSH rule 5 x 258 [5,0,6]
+ CRUSH rule 5 x 259 [4,6,2]
+ CRUSH rule 5 x 260 [3,6,2]
+ CRUSH rule 5 x 261 [8,5,2]
+ CRUSH rule 5 x 262 [5,6,0]
+ CRUSH rule 5 x 263 [6,1,5]
+ CRUSH rule 5 x 264 [3,6,2]
+ CRUSH rule 5 x 265 [8,5,2]
+ CRUSH rule 5 x 266 [8,2,5]
+ CRUSH rule 5 x 267 [2,3,8]
+ CRUSH rule 5 x 268 [0,7,4]
+ CRUSH rule 5 x 269 [0,8,4]
+ CRUSH rule 5 x 270 [5,0,7]
+ CRUSH rule 5 x 271 [7,5,1]
+ CRUSH rule 5 x 272 [2,8,3]
+ CRUSH rule 5 x 273 [3,1,7]
+ CRUSH rule 5 x 274 [6,3,1]
+ CRUSH rule 5 x 275 [4,7,0]
+ CRUSH rule 5 x 276 [7,1,4]
+ CRUSH rule 5 x 277 [6,4,0]
+ CRUSH rule 5 x 278 [6,1,4]
+ CRUSH rule 5 x 279 [8,3,2]
+ CRUSH rule 5 x 280 [0,6,4]
+ CRUSH rule 5 x 281 [8,0,3]
+ CRUSH rule 5 x 282 [3,1,6]
+ CRUSH rule 5 x 283 [8,2,5]
+ CRUSH rule 5 x 284 [6,3,0]
+ CRUSH rule 5 x 285 [5,7,0]
+ CRUSH rule 5 x 286 [2,6,3]
+ CRUSH rule 5 x 287 [0,4,6]
+ CRUSH rule 5 x 288 [8,0,4]
+ CRUSH rule 5 x 289 [4,6,2]
+ CRUSH rule 5 x 290 [1,3,7]
+ CRUSH rule 5 x 291 [0,3,8]
+ CRUSH rule 5 x 292 [8,0,5]
+ CRUSH rule 5 x 293 [6,0,4]
+ CRUSH rule 5 x 294 [7,4,1]
+ CRUSH rule 5 x 295 [4,8,0]
+ CRUSH rule 5 x 296 [3,1,6]
+ CRUSH rule 5 x 297 [6,2,4]
+ CRUSH rule 5 x 298 [1,5,7]
+ CRUSH rule 5 x 299 [2,8,3]
+ CRUSH rule 5 x 300 [8,3,0]
+ CRUSH rule 5 x 301 [0,8,4]
+ CRUSH rule 5 x 302 [3,0,6]
+ CRUSH rule 5 x 303 [7,5,1]
+ CRUSH rule 5 x 304 [2,7,5]
+ CRUSH rule 5 x 305 [5,8,2]
+ CRUSH rule 5 x 306 [0,7,3]
+ CRUSH rule 5 x 307 [0,7,5]
+ CRUSH rule 5 x 308 [0,8,4]
+ CRUSH rule 5 x 309 [7,4,0]
+ CRUSH rule 5 x 310 [4,1,7]
+ CRUSH rule 5 x 311 [3,6,0]
+ CRUSH rule 5 x 312 [2,6,3]
+ CRUSH rule 5 x 313 [5,1,6]
+ CRUSH rule 5 x 314 [4,2,6]
+ CRUSH rule 5 x 315 [2,4,8]
+ CRUSH rule 5 x 316 [6,3,1]
+ CRUSH rule 5 x 317 [2,6,5]
+ CRUSH rule 5 x 318 [8,1,4]
+ CRUSH rule 5 x 319 [5,0,8]
+ CRUSH rule 5 x 320 [3,7,1]
+ CRUSH rule 5 x 321 [1,3,8]
+ CRUSH rule 5 x 322 [2,7,3]
+ CRUSH rule 5 x 323 [4,7,0]
+ CRUSH rule 5 x 324 [7,0,3]
+ CRUSH rule 5 x 325 [4,6,0]
+ CRUSH rule 5 x 326 [3,2,6]
+ CRUSH rule 5 x 327 [0,6,3]
+ CRUSH rule 5 x 328 [7,4,1]
+ CRUSH rule 5 x 329 [5,6,2]
+ CRUSH rule 5 x 330 [3,7,2]
+ CRUSH rule 5 x 331 [2,6,3]
+ CRUSH rule 5 x 332 [2,4,6]
+ CRUSH rule 5 x 333 [6,5,1]
+ CRUSH rule 5 x 334 [8,3,2]
+ CRUSH rule 5 x 335 [7,1,4]
+ CRUSH rule 5 x 336 [4,6,0]
+ CRUSH rule 5 x 337 [7,2,3]
+ CRUSH rule 5 x 338 [5,6,1]
+ CRUSH rule 5 x 339 [7,5,2]
+ CRUSH rule 5 x 340 [2,8,4]
+ CRUSH rule 5 x 341 [5,1,7]
+ CRUSH rule 5 x 342 [0,7,4]
+ CRUSH rule 5 x 343 [6,3,0]
+ CRUSH rule 5 x 344 [6,0,5]
+ CRUSH rule 5 x 345 [4,7,1]
+ CRUSH rule 5 x 346 [8,0,5]
+ CRUSH rule 5 x 347 [3,1,7]
+ CRUSH rule 5 x 348 [8,0,5]
+ CRUSH rule 5 x 349 [1,6,4]
+ CRUSH rule 5 x 350 [8,5,1]
+ CRUSH rule 5 x 351 [3,6,0]
+ CRUSH rule 5 x 352 [1,8,3]
+ CRUSH rule 5 x 353 [6,4,0]
+ CRUSH rule 5 x 354 [0,3,6]
+ CRUSH rule 5 x 355 [3,8,2]
+ CRUSH rule 5 x 356 [3,0,6]
+ CRUSH rule 5 x 357 [6,1,4]
+ CRUSH rule 5 x 358 [2,8,4]
+ CRUSH rule 5 x 359 [6,1,3]
+ CRUSH rule 5 x 360 [5,2,7]
+ CRUSH rule 5 x 361 [8,4,1]
+ CRUSH rule 5 x 362 [4,1,6]
+ CRUSH rule 5 x 363 [4,0,6]
+ CRUSH rule 5 x 364 [2,5,6]
+ CRUSH rule 5 x 365 [6,5,2]
+ CRUSH rule 5 x 366 [7,2,3]
+ CRUSH rule 5 x 367 [4,0,6]
+ CRUSH rule 5 x 368 [7,4,0]
+ CRUSH rule 5 x 369 [3,7,1]
+ CRUSH rule 5 x 370 [8,2,5]
+ CRUSH rule 5 x 371 [1,3,6]
+ CRUSH rule 5 x 372 [3,1,8]
+ CRUSH rule 5 x 373 [0,6,4]
+ CRUSH rule 5 x 374 [3,8,1]
+ CRUSH rule 5 x 375 [6,4,1]
+ CRUSH rule 5 x 376 [7,1,5]
+ CRUSH rule 5 x 377 [1,3,6]
+ CRUSH rule 5 x 378 [0,8,3]
+ CRUSH rule 5 x 379 [8,5,2]
+ CRUSH rule 5 x 380 [2,5,8]
+ CRUSH rule 5 x 381 [0,4,7]
+ CRUSH rule 5 x 382 [1,5,8]
+ CRUSH rule 5 x 383 [4,6,0]
+ CRUSH rule 5 x 384 [7,0,4]
+ CRUSH rule 5 x 385 [7,4,0]
+ CRUSH rule 5 x 386 [0,3,6]
+ CRUSH rule 5 x 387 [1,3,6]
+ CRUSH rule 5 x 388 [5,0,8]
+ CRUSH rule 5 x 389 [1,5,7]
+ CRUSH rule 5 x 390 [5,6,0]
+ CRUSH rule 5 x 391 [5,6,2]
+ CRUSH rule 5 x 392 [1,8,5]
+ CRUSH rule 5 x 393 [4,2,6]
+ CRUSH rule 5 x 394 [4,7,0]
+ CRUSH rule 5 x 395 [4,0,8]
+ CRUSH rule 5 x 396 [4,2,7]
+ CRUSH rule 5 x 397 [2,4,8]
+ CRUSH rule 5 x 398 [2,4,8]
+ CRUSH rule 5 x 399 [8,4,2]
+ CRUSH rule 5 x 400 [8,1,4]
+ CRUSH rule 5 x 401 [0,5,8]
+ CRUSH rule 5 x 402 [7,5,2]
+ CRUSH rule 5 x 403 [0,3,8]
+ CRUSH rule 5 x 404 [4,2,8]
+ CRUSH rule 5 x 405 [6,5,2]
+ CRUSH rule 5 x 406 [2,6,5]
+ CRUSH rule 5 x 407 [2,8,5]
+ CRUSH rule 5 x 408 [4,1,6]
+ CRUSH rule 5 x 409 [7,3,0]
+ CRUSH rule 5 x 410 [8,3,2]
+ CRUSH rule 5 x 411 [2,8,3]
+ CRUSH rule 5 x 412 [0,5,8]
+ CRUSH rule 5 x 413 [5,0,8]
+ CRUSH rule 5 x 414 [4,1,6]
+ CRUSH rule 5 x 415 [0,6,4]
+ CRUSH rule 5 x 416 [2,6,3]
+ CRUSH rule 5 x 417 [8,2,4]
+ CRUSH rule 5 x 418 [7,1,4]
+ CRUSH rule 5 x 419 [8,3,0]
+ CRUSH rule 5 x 420 [1,4,6]
+ CRUSH rule 5 x 421 [8,4,0]
+ CRUSH rule 5 x 422 [6,4,1]
+ CRUSH rule 5 x 423 [0,5,6]
+ CRUSH rule 5 x 424 [8,4,1]
+ CRUSH rule 5 x 425 [1,3,7]
+ CRUSH rule 5 x 426 [6,0,5]
+ CRUSH rule 5 x 427 [0,7,5]
+ CRUSH rule 5 x 428 [5,7,1]
+ CRUSH rule 5 x 429 [4,6,0]
+ CRUSH rule 5 x 430 [3,6,2]
+ CRUSH rule 5 x 431 [5,0,7]
+ CRUSH rule 5 x 432 [7,1,3]
+ CRUSH rule 5 x 433 [6,5,1]
+ CRUSH rule 5 x 434 [5,2,8]
+ CRUSH rule 5 x 435 [0,5,6]
+ CRUSH rule 5 x 436 [4,0,7]
+ CRUSH rule 5 x 437 [7,5,2]
+ CRUSH rule 5 x 438 [0,3,8]
+ CRUSH rule 5 x 439 [1,3,7]
+ CRUSH rule 5 x 440 [2,7,5]
+ CRUSH rule 5 x 441 [5,7,2]
+ CRUSH rule 5 x 442 [2,4,7]
+ CRUSH rule 5 x 443 [6,0,4]
+ CRUSH rule 5 x 444 [7,0,4]
+ CRUSH rule 5 x 445 [6,3,1]
+ CRUSH rule 5 x 446 [4,1,8]
+ CRUSH rule 5 x 447 [2,3,7]
+ CRUSH rule 5 x 448 [7,2,5]
+ CRUSH rule 5 x 449 [7,5,1]
+ CRUSH rule 5 x 450 [4,1,8]
+ CRUSH rule 5 x 451 [6,5,0]
+ CRUSH rule 5 x 452 [8,3,0]
+ CRUSH rule 5 x 453 [6,5,1]
+ CRUSH rule 5 x 454 [6,4,2]
+ CRUSH rule 5 x 455 [2,7,5]
+ CRUSH rule 5 x 456 [6,2,5]
+ CRUSH rule 5 x 457 [7,2,5]
+ CRUSH rule 5 x 458 [2,8,5]
+ CRUSH rule 5 x 459 [2,7,5]
+ CRUSH rule 5 x 460 [6,5,0]
+ CRUSH rule 5 x 461 [6,5,2]
+ CRUSH rule 5 x 462 [8,1,5]
+ CRUSH rule 5 x 463 [6,0,4]
+ CRUSH rule 5 x 464 [7,4,2]
+ CRUSH rule 5 x 465 [7,2,5]
+ CRUSH rule 5 x 466 [5,8,2]
+ CRUSH rule 5 x 467 [6,4,0]
+ CRUSH rule 5 x 468 [7,0,3]
+ CRUSH rule 5 x 469 [7,0,5]
+ CRUSH rule 5 x 470 [3,0,6]
+ CRUSH rule 5 x 471 [0,7,5]
+ CRUSH rule 5 x 472 [5,1,8]
+ CRUSH rule 5 x 473 [1,4,6]
+ CRUSH rule 5 x 474 [6,0,3]
+ CRUSH rule 5 x 475 [6,2,3]
+ CRUSH rule 5 x 476 [4,6,1]
+ CRUSH rule 5 x 477 [5,8,2]
+ CRUSH rule 5 x 478 [6,2,3]
+ CRUSH rule 5 x 479 [0,5,8]
+ CRUSH rule 5 x 480 [1,8,3]
+ CRUSH rule 5 x 481 [2,4,7]
+ CRUSH rule 5 x 482 [4,7,0]
+ CRUSH rule 5 x 483 [0,6,4]
+ CRUSH rule 5 x 484 [1,7,4]
+ CRUSH rule 5 x 485 [4,7,0]
+ CRUSH rule 5 x 486 [4,1,7]
+ CRUSH rule 5 x 487 [5,0,8]
+ CRUSH rule 5 x 488 [5,7,1]
+ CRUSH rule 5 x 489 [2,8,5]
+ CRUSH rule 5 x 490 [6,4,1]
+ CRUSH rule 5 x 491 [1,7,5]
+ CRUSH rule 5 x 492 [6,5,0]
+ CRUSH rule 5 x 493 [0,7,3]
+ CRUSH rule 5 x 494 [1,7,4]
+ CRUSH rule 5 x 495 [3,1,8]
+ CRUSH rule 5 x 496 [7,5,0]
+ CRUSH rule 5 x 497 [5,7,0]
+ CRUSH rule 5 x 498 [0,5,8]
+ CRUSH rule 5 x 499 [8,4,2]
+ CRUSH rule 5 x 500 [3,6,0]
+ CRUSH rule 5 x 501 [0,7,3]
+ CRUSH rule 5 x 502 [7,1,4]
+ CRUSH rule 5 x 503 [2,3,7]
+ CRUSH rule 5 x 504 [5,6,2]
+ CRUSH rule 5 x 505 [0,7,3]
+ CRUSH rule 5 x 506 [5,2,8]
+ CRUSH rule 5 x 507 [6,0,3]
+ CRUSH rule 5 x 508 [0,3,8]
+ CRUSH rule 5 x 509 [7,5,2]
+ CRUSH rule 5 x 510 [6,0,4]
+ CRUSH rule 5 x 511 [5,8,2]
+ CRUSH rule 5 x 512 [7,0,4]
+ CRUSH rule 5 x 513 [7,2,4]
+ CRUSH rule 5 x 514 [4,6,1]
+ CRUSH rule 5 x 515 [8,5,0]
+ CRUSH rule 5 x 516 [4,0,8]
+ CRUSH rule 5 x 517 [7,2,4]
+ CRUSH rule 5 x 518 [4,6,1]
+ CRUSH rule 5 x 519 [7,3,1]
+ CRUSH rule 5 x 520 [2,6,3]
+ CRUSH rule 5 x 521 [8,0,3]
+ CRUSH rule 5 x 522 [6,0,4]
+ CRUSH rule 5 x 523 [4,2,7]
+ CRUSH rule 5 x 524 [0,4,8]
+ CRUSH rule 5 x 525 [0,4,6]
+ CRUSH rule 5 x 526 [1,5,8]
+ CRUSH rule 5 x 527 [0,5,6]
+ CRUSH rule 5 x 528 [5,0,6]
+ CRUSH rule 5 x 529 [5,7,0]
+ CRUSH rule 5 x 530 [6,5,2]
+ CRUSH rule 5 x 531 [6,1,3]
+ CRUSH rule 5 x 532 [6,3,0]
+ CRUSH rule 5 x 533 [5,6,2]
+ CRUSH rule 5 x 534 [7,3,1]
+ CRUSH rule 5 x 535 [8,1,5]
+ CRUSH rule 5 x 536 [6,2,4]
+ CRUSH rule 5 x 537 [3,7,2]
+ CRUSH rule 5 x 538 [6,3,0]
+ CRUSH rule 5 x 539 [8,3,1]
+ CRUSH rule 5 x 540 [0,6,3]
+ CRUSH rule 5 x 541 [2,3,8]
+ CRUSH rule 5 x 542 [3,2,8]
+ CRUSH rule 5 x 543 [6,0,4]
+ CRUSH rule 5 x 544 [3,7,0]
+ CRUSH rule 5 x 545 [5,7,0]
+ CRUSH rule 5 x 546 [6,1,5]
+ CRUSH rule 5 x 547 [8,2,4]
+ CRUSH rule 5 x 548 [5,2,8]
+ CRUSH rule 5 x 549 [5,8,2]
+ CRUSH rule 5 x 550 [0,5,7]
+ CRUSH rule 5 x 551 [7,5,0]
+ CRUSH rule 5 x 552 [5,8,1]
+ CRUSH rule 5 x 553 [4,2,7]
+ CRUSH rule 5 x 554 [0,8,5]
+ CRUSH rule 5 x 555 [5,0,8]
+ CRUSH rule 5 x 556 [3,6,0]
+ CRUSH rule 5 x 557 [7,4,0]
+ CRUSH rule 5 x 558 [3,1,6]
+ CRUSH rule 5 x 559 [4,2,6]
+ CRUSH rule 5 x 560 [8,3,2]
+ CRUSH rule 5 x 561 [6,3,1]
+ CRUSH rule 5 x 562 [3,0,6]
+ CRUSH rule 5 x 563 [2,6,4]
+ CRUSH rule 5 x 564 [5,1,7]
+ CRUSH rule 5 x 565 [3,6,2]
+ CRUSH rule 5 x 566 [4,7,2]
+ CRUSH rule 5 x 567 [3,6,1]
+ CRUSH rule 5 x 568 [7,4,1]
+ CRUSH rule 5 x 569 [3,1,7]
+ CRUSH rule 5 x 570 [1,5,8]
+ CRUSH rule 5 x 571 [3,7,1]
+ CRUSH rule 5 x 572 [3,2,8]
+ CRUSH rule 5 x 573 [3,0,7]
+ CRUSH rule 5 x 574 [2,5,8]
+ CRUSH rule 5 x 575 [8,2,5]
+ CRUSH rule 5 x 576 [4,6,0]
+ CRUSH rule 5 x 577 [8,2,5]
+ CRUSH rule 5 x 578 [6,1,4]
+ CRUSH rule 5 x 579 [3,1,6]
+ CRUSH rule 5 x 580 [3,0,7]
+ CRUSH rule 5 x 581 [7,2,4]
+ CRUSH rule 5 x 582 [2,8,5]
+ CRUSH rule 5 x 583 [6,0,3]
+ CRUSH rule 5 x 584 [8,1,3]
+ CRUSH rule 5 x 585 [7,0,5]
+ CRUSH rule 5 x 586 [0,7,5]
+ CRUSH rule 5 x 587 [2,5,7]
+ CRUSH rule 5 x 588 [3,7,1]
+ CRUSH rule 5 x 589 [7,1,4]
+ CRUSH rule 5 x 590 [6,2,3]
+ CRUSH rule 5 x 591 [5,2,8]
+ CRUSH rule 5 x 592 [2,4,7]
+ CRUSH rule 5 x 593 [0,8,3]
+ CRUSH rule 5 x 594 [0,7,4]
+ CRUSH rule 5 x 595 [7,1,3]
+ CRUSH rule 5 x 596 [4,0,6]
+ CRUSH rule 5 x 597 [3,1,7]
+ CRUSH rule 5 x 598 [3,2,6]
+ CRUSH rule 5 x 599 [5,2,8]
+ CRUSH rule 5 x 600 [7,0,3]
+ CRUSH rule 5 x 601 [0,7,3]
+ CRUSH rule 5 x 602 [3,7,1]
+ CRUSH rule 5 x 603 [5,1,6]
+ CRUSH rule 5 x 604 [7,5,0]
+ CRUSH rule 5 x 605 [3,0,7]
+ CRUSH rule 5 x 606 [2,7,4]
+ CRUSH rule 5 x 607 [0,4,8]
+ CRUSH rule 5 x 608 [5,2,7]
+ CRUSH rule 5 x 609 [5,2,8]
+ CRUSH rule 5 x 610 [3,7,0]
+ CRUSH rule 5 x 611 [1,8,5]
+ CRUSH rule 5 x 612 [2,6,5]
+ CRUSH rule 5 x 613 [7,2,3]
+ CRUSH rule 5 x 614 [7,2,3]
+ CRUSH rule 5 x 615 [6,0,5]
+ CRUSH rule 5 x 616 [0,8,5]
+ CRUSH rule 5 x 617 [6,1,4]
+ CRUSH rule 5 x 618 [7,4,0]
+ CRUSH rule 5 x 619 [5,1,8]
+ CRUSH rule 5 x 620 [4,1,6]
+ CRUSH rule 5 x 621 [5,8,2]
+ CRUSH rule 5 x 622 [0,4,8]
+ CRUSH rule 5 x 623 [0,6,3]
+ CRUSH rule 5 x 624 [3,2,8]
+ CRUSH rule 5 x 625 [2,3,7]
+ CRUSH rule 5 x 626 [7,0,3]
+ CRUSH rule 5 x 627 [2,7,3]
+ CRUSH rule 5 x 628 [8,0,5]
+ CRUSH rule 5 x 629 [2,6,5]
+ CRUSH rule 5 x 630 [2,7,5]
+ CRUSH rule 5 x 631 [0,7,3]
+ CRUSH rule 5 x 632 [7,0,3]
+ CRUSH rule 5 x 633 [8,3,1]
+ CRUSH rule 5 x 634 [0,4,7]
+ CRUSH rule 5 x 635 [5,6,2]
+ CRUSH rule 5 x 636 [1,4,8]
+ CRUSH rule 5 x 637 [4,1,7]
+ CRUSH rule 5 x 638 [6,0,4]
+ CRUSH rule 5 x 639 [4,2,8]
+ CRUSH rule 5 x 640 [3,1,8]
+ CRUSH rule 5 x 641 [7,2,3]
+ CRUSH rule 5 x 642 [2,7,3]
+ CRUSH rule 5 x 643 [3,0,8]
+ CRUSH rule 5 x 644 [8,1,5]
+ CRUSH rule 5 x 645 [5,7,1]
+ CRUSH rule 5 x 646 [8,0,3]
+ CRUSH rule 5 x 647 [7,1,3]
+ CRUSH rule 5 x 648 [0,6,3]
+ CRUSH rule 5 x 649 [4,7,0]
+ CRUSH rule 5 x 650 [7,3,1]
+ CRUSH rule 5 x 651 [3,7,0]
+ CRUSH rule 5 x 652 [3,7,0]
+ CRUSH rule 5 x 653 [8,5,2]
+ CRUSH rule 5 x 654 [7,5,2]
+ CRUSH rule 5 x 655 [0,3,7]
+ CRUSH rule 5 x 656 [4,7,1]
+ CRUSH rule 5 x 657 [6,1,4]
+ CRUSH rule 5 x 658 [5,6,0]
+ CRUSH rule 5 x 659 [4,6,2]
+ CRUSH rule 5 x 660 [7,4,1]
+ CRUSH rule 5 x 661 [1,8,3]
+ CRUSH rule 5 x 662 [4,2,7]
+ CRUSH rule 5 x 663 [1,3,8]
+ CRUSH rule 5 x 664 [1,4,7]
+ CRUSH rule 5 x 665 [5,7,0]
+ CRUSH rule 5 x 666 [2,8,4]
+ CRUSH rule 5 x 667 [1,3,7]
+ CRUSH rule 5 x 668 [3,7,1]
+ CRUSH rule 5 x 669 [6,4,0]
+ CRUSH rule 5 x 670 [4,0,6]
+ CRUSH rule 5 x 671 [0,7,3]
+ CRUSH rule 5 x 672 [4,2,7]
+ CRUSH rule 5 x 673 [5,2,7]
+ CRUSH rule 5 x 674 [3,1,8]
+ CRUSH rule 5 x 675 [0,8,3]
+ CRUSH rule 5 x 676 [0,4,8]
+ CRUSH rule 5 x 677 [4,1,7]
+ CRUSH rule 5 x 678 [2,3,8]
+ CRUSH rule 5 x 679 [6,0,5]
+ CRUSH rule 5 x 680 [0,4,6]
+ CRUSH rule 5 x 681 [4,7,1]
+ CRUSH rule 5 x 682 [0,5,7]
+ CRUSH rule 5 x 683 [0,5,6]
+ CRUSH rule 5 x 684 [7,1,5]
+ CRUSH rule 5 x 685 [7,1,5]
+ CRUSH rule 5 x 686 [1,4,8]
+ CRUSH rule 5 x 687 [3,6,1]
+ CRUSH rule 5 x 688 [5,7,2]
+ CRUSH rule 5 x 689 [6,5,0]
+ CRUSH rule 5 x 690 [8,1,3]
+ CRUSH rule 5 x 691 [3,0,6]
+ CRUSH rule 5 x 692 [7,2,3]
+ CRUSH rule 5 x 693 [6,3,1]
+ CRUSH rule 5 x 694 [6,5,1]
+ CRUSH rule 5 x 695 [0,8,4]
+ CRUSH rule 5 x 696 [1,4,8]
+ CRUSH rule 5 x 697 [6,1,3]
+ CRUSH rule 5 x 698 [6,2,4]
+ CRUSH rule 5 x 699 [1,6,3]
+ CRUSH rule 5 x 700 [0,3,7]
+ CRUSH rule 5 x 701 [4,1,7]
+ CRUSH rule 5 x 702 [3,2,8]
+ CRUSH rule 5 x 703 [8,3,1]
+ CRUSH rule 5 x 704 [0,3,8]
+ CRUSH rule 5 x 705 [8,0,4]
+ CRUSH rule 5 x 706 [1,5,6]
+ CRUSH rule 5 x 707 [7,3,1]
+ CRUSH rule 5 x 708 [3,7,1]
+ CRUSH rule 5 x 709 [6,3,0]
+ CRUSH rule 5 x 710 [8,4,0]
+ CRUSH rule 5 x 711 [2,3,8]
+ CRUSH rule 5 x 712 [2,3,7]
+ CRUSH rule 5 x 713 [6,3,0]
+ CRUSH rule 5 x 714 [3,2,7]
+ CRUSH rule 5 x 715 [1,3,6]
+ CRUSH rule 5 x 716 [3,6,0]
+ CRUSH rule 5 x 717 [8,2,5]
+ CRUSH rule 5 x 718 [3,7,2]
+ CRUSH rule 5 x 719 [2,6,3]
+ CRUSH rule 5 x 720 [6,1,4]
+ CRUSH rule 5 x 721 [5,7,2]
+ CRUSH rule 5 x 722 [5,7,1]
+ CRUSH rule 5 x 723 [5,1,7]
+ CRUSH rule 5 x 724 [0,6,3]
+ CRUSH rule 5 x 725 [0,3,7]
+ CRUSH rule 5 x 726 [3,8,1]
+ CRUSH rule 5 x 727 [4,6,1]
+ CRUSH rule 5 x 728 [2,7,4]
+ CRUSH rule 5 x 729 [5,6,2]
+ CRUSH rule 5 x 730 [3,7,2]
+ CRUSH rule 5 x 731 [4,1,8]
+ CRUSH rule 5 x 732 [1,5,6]
+ CRUSH rule 5 x 733 [5,7,0]
+ CRUSH rule 5 x 734 [6,4,2]
+ CRUSH rule 5 x 735 [4,8,1]
+ CRUSH rule 5 x 736 [3,8,1]
+ CRUSH rule 5 x 737 [1,6,4]
+ CRUSH rule 5 x 738 [5,2,7]
+ CRUSH rule 5 x 739 [0,7,4]
+ CRUSH rule 5 x 740 [0,8,4]
+ CRUSH rule 5 x 741 [7,1,4]
+ CRUSH rule 5 x 742 [8,2,3]
+ CRUSH rule 5 x 743 [7,0,5]
+ CRUSH rule 5 x 744 [4,7,1]
+ CRUSH rule 5 x 745 [3,1,8]
+ CRUSH rule 5 x 746 [4,1,7]
+ CRUSH rule 5 x 747 [6,0,3]
+ CRUSH rule 5 x 748 [2,7,5]
+ CRUSH rule 5 x 749 [4,8,0]
+ CRUSH rule 5 x 750 [1,6,3]
+ CRUSH rule 5 x 751 [2,8,3]
+ CRUSH rule 5 x 752 [8,1,5]
+ CRUSH rule 5 x 753 [7,3,1]
+ CRUSH rule 5 x 754 [8,5,2]
+ CRUSH rule 5 x 755 [1,6,3]
+ CRUSH rule 5 x 756 [5,6,1]
+ CRUSH rule 5 x 757 [8,0,5]
+ CRUSH rule 5 x 758 [6,0,3]
+ CRUSH rule 5 x 759 [8,5,2]
+ CRUSH rule 5 x 760 [1,5,7]
+ CRUSH rule 5 x 761 [4,1,8]
+ CRUSH rule 5 x 762 [2,7,5]
+ CRUSH rule 5 x 763 [8,5,1]
+ CRUSH rule 5 x 764 [1,7,5]
+ CRUSH rule 5 x 765 [6,5,2]
+ CRUSH rule 5 x 766 [8,5,1]
+ CRUSH rule 5 x 767 [1,8,3]
+ CRUSH rule 5 x 768 [8,3,2]
+ CRUSH rule 5 x 769 [6,2,5]
+ CRUSH rule 5 x 770 [6,0,4]
+ CRUSH rule 5 x 771 [7,0,3]
+ CRUSH rule 5 x 772 [8,3,1]
+ CRUSH rule 5 x 773 [3,1,7]
+ CRUSH rule 5 x 774 [4,6,2]
+ CRUSH rule 5 x 775 [6,4,2]
+ CRUSH rule 5 x 776 [7,2,5]
+ CRUSH rule 5 x 777 [3,1,6]
+ CRUSH rule 5 x 778 [1,8,4]
+ CRUSH rule 5 x 779 [2,7,3]
+ CRUSH rule 5 x 780 [0,5,7]
+ CRUSH rule 5 x 781 [6,3,2]
+ CRUSH rule 5 x 782 [5,0,8]
+ CRUSH rule 5 x 783 [7,1,3]
+ CRUSH rule 5 x 784 [0,4,6]
+ CRUSH rule 5 x 785 [6,1,3]
+ CRUSH rule 5 x 786 [7,3,1]
+ CRUSH rule 5 x 787 [1,6,4]
+ CRUSH rule 5 x 788 [6,0,3]
+ CRUSH rule 5 x 789 [0,4,8]
+ CRUSH rule 5 x 790 [8,4,0]
+ CRUSH rule 5 x 791 [3,8,0]
+ CRUSH rule 5 x 792 [5,8,0]
+ CRUSH rule 5 x 793 [6,1,3]
+ CRUSH rule 5 x 794 [2,6,4]
+ CRUSH rule 5 x 795 [0,3,8]
+ CRUSH rule 5 x 796 [3,7,2]
+ CRUSH rule 5 x 797 [2,3,8]
+ CRUSH rule 5 x 798 [6,1,5]
+ CRUSH rule 5 x 799 [5,1,8]
+ CRUSH rule 5 x 800 [5,0,7]
+ CRUSH rule 5 x 801 [3,6,1]
+ CRUSH rule 5 x 802 [1,8,5]
+ CRUSH rule 5 x 803 [0,5,7]
+ CRUSH rule 5 x 804 [6,2,5]
+ CRUSH rule 5 x 805 [3,6,1]
+ CRUSH rule 5 x 806 [1,3,7]
+ CRUSH rule 5 x 807 [5,7,2]
+ CRUSH rule 5 x 808 [4,6,2]
+ CRUSH rule 5 x 809 [1,4,8]
+ CRUSH rule 5 x 810 [5,7,2]
+ CRUSH rule 5 x 811 [8,4,0]
+ CRUSH rule 5 x 812 [8,5,2]
+ CRUSH rule 5 x 813 [6,4,2]
+ CRUSH rule 5 x 814 [3,6,1]
+ CRUSH rule 5 x 815 [3,1,8]
+ CRUSH rule 5 x 816 [2,7,3]
+ CRUSH rule 5 x 817 [4,8,2]
+ CRUSH rule 5 x 818 [3,0,7]
+ CRUSH rule 5 x 819 [5,1,8]
+ CRUSH rule 5 x 820 [3,6,0]
+ CRUSH rule 5 x 821 [4,6,2]
+ CRUSH rule 5 x 822 [2,5,8]
+ CRUSH rule 5 x 823 [4,8,2]
+ CRUSH rule 5 x 824 [3,7,2]
+ CRUSH rule 5 x 825 [2,8,5]
+ CRUSH rule 5 x 826 [7,0,5]
+ CRUSH rule 5 x 827 [0,8,3]
+ CRUSH rule 5 x 828 [2,3,8]
+ CRUSH rule 5 x 829 [5,6,1]
+ CRUSH rule 5 x 830 [2,3,8]
+ CRUSH rule 5 x 831 [1,6,3]
+ CRUSH rule 5 x 832 [4,7,0]
+ CRUSH rule 5 x 833 [2,7,3]
+ CRUSH rule 5 x 834 [3,1,7]
+ CRUSH rule 5 x 835 [8,4,1]
+ CRUSH rule 5 x 836 [3,7,1]
+ CRUSH rule 5 x 837 [6,3,1]
+ CRUSH rule 5 x 838 [6,2,4]
+ CRUSH rule 5 x 839 [5,0,6]
+ CRUSH rule 5 x 840 [7,3,2]
+ CRUSH rule 5 x 841 [4,8,2]
+ CRUSH rule 5 x 842 [2,4,6]
+ CRUSH rule 5 x 843 [6,4,1]
+ CRUSH rule 5 x 844 [4,8,1]
+ CRUSH rule 5 x 845 [3,8,2]
+ CRUSH rule 5 x 846 [3,2,7]
+ CRUSH rule 5 x 847 [0,8,4]
+ CRUSH rule 5 x 848 [2,6,5]
+ CRUSH rule 5 x 849 [4,6,2]
+ CRUSH rule 5 x 850 [1,3,6]
+ CRUSH rule 5 x 851 [6,4,0]
+ CRUSH rule 5 x 852 [7,3,0]
+ CRUSH rule 5 x 853 [6,0,4]
+ CRUSH rule 5 x 854 [7,0,4]
+ CRUSH rule 5 x 855 [5,7,2]
+ CRUSH rule 5 x 856 [6,3,2]
+ CRUSH rule 5 x 857 [8,5,0]
+ CRUSH rule 5 x 858 [6,4,1]
+ CRUSH rule 5 x 859 [6,0,5]
+ CRUSH rule 5 x 860 [4,1,7]
+ CRUSH rule 5 x 861 [8,3,1]
+ CRUSH rule 5 x 862 [6,1,4]
+ CRUSH rule 5 x 863 [8,2,3]
+ CRUSH rule 5 x 864 [5,6,2]
+ CRUSH rule 5 x 865 [8,1,3]
+ CRUSH rule 5 x 866 [3,6,0]
+ CRUSH rule 5 x 867 [6,5,1]
+ CRUSH rule 5 x 868 [6,3,0]
+ CRUSH rule 5 x 869 [8,5,2]
+ CRUSH rule 5 x 870 [0,4,8]
+ CRUSH rule 5 x 871 [3,2,8]
+ CRUSH rule 5 x 872 [5,1,8]
+ CRUSH rule 5 x 873 [4,6,2]
+ CRUSH rule 5 x 874 [2,6,4]
+ CRUSH rule 5 x 875 [2,6,4]
+ CRUSH rule 5 x 876 [5,8,1]
+ CRUSH rule 5 x 877 [6,4,2]
+ CRUSH rule 5 x 878 [5,2,7]
+ CRUSH rule 5 x 879 [7,4,2]
+ CRUSH rule 5 x 880 [3,2,8]
+ CRUSH rule 5 x 881 [5,6,1]
+ CRUSH rule 5 x 882 [4,0,7]
+ CRUSH rule 5 x 883 [2,3,7]
+ CRUSH rule 5 x 884 [6,0,4]
+ CRUSH rule 5 x 885 [5,1,8]
+ CRUSH rule 5 x 886 [3,6,0]
+ CRUSH rule 5 x 887 [7,4,0]
+ CRUSH rule 5 x 888 [6,2,5]
+ CRUSH rule 5 x 889 [2,6,4]
+ CRUSH rule 5 x 890 [7,2,4]
+ CRUSH rule 5 x 891 [1,8,5]
+ CRUSH rule 5 x 892 [6,2,3]
+ CRUSH rule 5 x 893 [2,3,7]
+ CRUSH rule 5 x 894 [7,5,0]
+ CRUSH rule 5 x 895 [5,1,8]
+ CRUSH rule 5 x 896 [1,8,5]
+ CRUSH rule 5 x 897 [4,2,6]
+ CRUSH rule 5 x 898 [0,5,7]
+ CRUSH rule 5 x 899 [1,7,5]
+ CRUSH rule 5 x 900 [4,1,6]
+ CRUSH rule 5 x 901 [5,0,8]
+ CRUSH rule 5 x 902 [8,5,0]
+ CRUSH rule 5 x 903 [5,7,1]
+ CRUSH rule 5 x 904 [5,6,2]
+ CRUSH rule 5 x 905 [6,2,5]
+ CRUSH rule 5 x 906 [1,6,3]
+ CRUSH rule 5 x 907 [7,1,5]
+ CRUSH rule 5 x 908 [5,8,1]
+ CRUSH rule 5 x 909 [2,3,7]
+ CRUSH rule 5 x 910 [6,4,0]
+ CRUSH rule 5 x 911 [5,8,1]
+ CRUSH rule 5 x 912 [0,7,3]
+ CRUSH rule 5 x 913 [7,2,4]
+ CRUSH rule 5 x 914 [6,4,0]
+ CRUSH rule 5 x 915 [8,2,3]
+ CRUSH rule 5 x 916 [3,1,8]
+ CRUSH rule 5 x 917 [1,5,8]
+ CRUSH rule 5 x 918 [8,2,4]
+ CRUSH rule 5 x 919 [6,2,3]
+ CRUSH rule 5 x 920 [7,4,0]
+ CRUSH rule 5 x 921 [1,4,6]
+ CRUSH rule 5 x 922 [6,4,0]
+ CRUSH rule 5 x 923 [5,8,2]
+ CRUSH rule 5 x 924 [3,1,7]
+ CRUSH rule 5 x 925 [5,7,2]
+ CRUSH rule 5 x 926 [3,0,8]
+ CRUSH rule 5 x 927 [1,6,3]
+ CRUSH rule 5 x 928 [8,1,3]
+ CRUSH rule 5 x 929 [4,1,7]
+ CRUSH rule 5 x 930 [2,4,6]
+ CRUSH rule 5 x 931 [5,0,7]
+ CRUSH rule 5 x 932 [4,1,8]
+ CRUSH rule 5 x 933 [8,5,0]
+ CRUSH rule 5 x 934 [5,6,0]
+ CRUSH rule 5 x 935 [6,3,1]
+ CRUSH rule 5 x 936 [0,6,5]
+ CRUSH rule 5 x 937 [5,8,2]
+ CRUSH rule 5 x 938 [6,5,2]
+ CRUSH rule 5 x 939 [2,7,5]
+ CRUSH rule 5 x 940 [8,5,0]
+ CRUSH rule 5 x 941 [5,2,8]
+ CRUSH rule 5 x 942 [1,8,4]
+ CRUSH rule 5 x 943 [8,2,4]
+ CRUSH rule 5 x 944 [4,8,2]
+ CRUSH rule 5 x 945 [7,2,4]
+ CRUSH rule 5 x 946 [2,8,5]
+ CRUSH rule 5 x 947 [4,2,8]
+ CRUSH rule 5 x 948 [7,5,0]
+ CRUSH rule 5 x 949 [6,1,3]
+ CRUSH rule 5 x 950 [3,6,0]
+ CRUSH rule 5 x 951 [4,8,1]
+ CRUSH rule 5 x 952 [2,7,3]
+ CRUSH rule 5 x 953 [1,3,6]
+ CRUSH rule 5 x 954 [4,2,7]
+ CRUSH rule 5 x 955 [8,0,4]
+ CRUSH rule 5 x 956 [1,6,4]
+ CRUSH rule 5 x 957 [7,1,3]
+ CRUSH rule 5 x 958 [8,4,1]
+ CRUSH rule 5 x 959 [5,2,7]
+ CRUSH rule 5 x 960 [3,6,0]
+ CRUSH rule 5 x 961 [4,0,8]
+ CRUSH rule 5 x 962 [7,4,0]
+ CRUSH rule 5 x 963 [0,5,6]
+ CRUSH rule 5 x 964 [3,1,8]
+ CRUSH rule 5 x 965 [7,4,0]
+ CRUSH rule 5 x 966 [3,8,0]
+ CRUSH rule 5 x 967 [8,5,0]
+ CRUSH rule 5 x 968 [7,2,4]
+ CRUSH rule 5 x 969 [8,0,5]
+ CRUSH rule 5 x 970 [0,6,3]
+ CRUSH rule 5 x 971 [1,7,3]
+ CRUSH rule 5 x 972 [1,8,4]
+ CRUSH rule 5 x 973 [1,6,3]
+ CRUSH rule 5 x 974 [5,1,8]
+ CRUSH rule 5 x 975 [3,7,0]
+ CRUSH rule 5 x 976 [4,8,2]
+ CRUSH rule 5 x 977 [8,3,2]
+ CRUSH rule 5 x 978 [7,2,4]
+ CRUSH rule 5 x 979 [7,1,5]
+ CRUSH rule 5 x 980 [6,0,5]
+ CRUSH rule 5 x 981 [7,3,2]
+ CRUSH rule 5 x 982 [4,2,8]
+ CRUSH rule 5 x 983 [3,7,0]
+ CRUSH rule 5 x 984 [0,7,3]
+ CRUSH rule 5 x 985 [2,5,7]
+ CRUSH rule 5 x 986 [8,3,0]
+ CRUSH rule 5 x 987 [0,5,8]
+ CRUSH rule 5 x 988 [1,3,7]
+ CRUSH rule 5 x 989 [0,6,3]
+ CRUSH rule 5 x 990 [1,6,5]
+ CRUSH rule 5 x 991 [0,4,8]
+ CRUSH rule 5 x 992 [7,1,5]
+ CRUSH rule 5 x 993 [0,6,3]
+ CRUSH rule 5 x 994 [3,7,2]
+ CRUSH rule 5 x 995 [7,1,5]
+ CRUSH rule 5 x 996 [6,5,0]
+ CRUSH rule 5 x 997 [6,4,1]
+ CRUSH rule 5 x 998 [8,1,5]
+ CRUSH rule 5 x 999 [0,7,4]
+ CRUSH rule 5 x 1000 [8,5,0]
+ CRUSH rule 5 x 1001 [2,5,6]
+ CRUSH rule 5 x 1002 [1,3,7]
+ CRUSH rule 5 x 1003 [2,8,3]
+ CRUSH rule 5 x 1004 [6,1,3]
+ CRUSH rule 5 x 1005 [6,1,5]
+ CRUSH rule 5 x 1006 [1,6,5]
+ CRUSH rule 5 x 1007 [1,5,7]
+ CRUSH rule 5 x 1008 [1,7,3]
+ CRUSH rule 5 x 1009 [6,4,1]
+ CRUSH rule 5 x 1010 [3,1,7]
+ CRUSH rule 5 x 1011 [3,0,8]
+ CRUSH rule 5 x 1012 [3,0,7]
+ CRUSH rule 5 x 1013 [5,1,7]
+ CRUSH rule 5 x 1014 [2,8,4]
+ CRUSH rule 5 x 1015 [6,5,0]
+ CRUSH rule 5 x 1016 [2,4,7]
+ CRUSH rule 5 x 1017 [6,0,3]
+ CRUSH rule 5 x 1018 [5,0,6]
+ CRUSH rule 5 x 1019 [5,8,2]
+ CRUSH rule 5 x 1020 [5,1,7]
+ CRUSH rule 5 x 1021 [5,2,6]
+ CRUSH rule 5 x 1022 [1,6,4]
+ CRUSH rule 5 x 1023 [3,2,8]
+ rule 5 (chooseleaf-set) num_rep 3 result size == 3:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
+ $ crushtool -i set-choose.crushmap --test --show-mappings --show-statistics --weight 0 0 --weight 1 0 --weight 3 0 --weight 4 0 --set-straw-calc-version 0 --min-rep 2 --max-rep 3
+ rule 0 (choose), x = 0..1023, numrep = 2..3
+ CRUSH rule 0 x 0 [2,5]
+ CRUSH rule 0 x 1 [2,8]
+ CRUSH rule 0 x 2 [2,5]
+ CRUSH rule 0 x 3 [8,2]
+ CRUSH rule 0 x 4 [5,2]
+ CRUSH rule 0 x 5 [7,2]
+ CRUSH rule 0 x 6 [2,6]
+ CRUSH rule 0 x 7 [5,6]
+ CRUSH rule 0 x 8 [5,7]
+ CRUSH rule 0 x 9 [2,5]
+ CRUSH rule 0 x 10 [2,8]
+ CRUSH rule 0 x 11 [2,6]
+ CRUSH rule 0 x 12 [2,5]
+ CRUSH rule 0 x 13 [5,8]
+ CRUSH rule 0 x 14 [7,2]
+ CRUSH rule 0 x 15 [7,2]
+ CRUSH rule 0 x 16 [5,7]
+ CRUSH rule 0 x 17 [5,2]
+ CRUSH rule 0 x 18 [2,5]
+ CRUSH rule 0 x 19 [7,5]
+ CRUSH rule 0 x 20 [2,5]
+ CRUSH rule 0 x 21 [5,6]
+ CRUSH rule 0 x 22 [8,5]
+ CRUSH rule 0 x 23 [5,7]
+ CRUSH rule 0 x 24 [2,6]
+ CRUSH rule 0 x 25 [5,8]
+ CRUSH rule 0 x 26 [2,7]
+ CRUSH rule 0 x 27 [5,2]
+ CRUSH rule 0 x 28 [6,2]
+ CRUSH rule 0 x 29 [8,5]
+ CRUSH rule 0 x 30 [5,6]
+ CRUSH rule 0 x 31 [8,2]
+ CRUSH rule 0 x 32 [5,7]
+ CRUSH rule 0 x 33 [2,7]
+ CRUSH rule 0 x 34 [2,5]
+ CRUSH rule 0 x 35 [2,6]
+ CRUSH rule 0 x 36 [5,6]
+ CRUSH rule 0 x 37 [2,5]
+ CRUSH rule 0 x 38 [5,6]
+ CRUSH rule 0 x 39 [5,7]
+ CRUSH rule 0 x 40 [7,2]
+ CRUSH rule 0 x 41 [2,7]
+ CRUSH rule 0 x 42 [5,7]
+ CRUSH rule 0 x 43 [2,5]
+ CRUSH rule 0 x 44 [2,8]
+ CRUSH rule 0 x 45 [8,2]
+ CRUSH rule 0 x 46 [2,5]
+ CRUSH rule 0 x 47 [5,2]
+ CRUSH rule 0 x 48 [5,6]
+ CRUSH rule 0 x 49 [5,6]
+ CRUSH rule 0 x 50 [5,2]
+ CRUSH rule 0 x 51 [5,6]
+ CRUSH rule 0 x 52 [8,2]
+ CRUSH rule 0 x 53 [5,6]
+ CRUSH rule 0 x 54 [7,5]
+ CRUSH rule 0 x 55 [8,2]
+ CRUSH rule 0 x 56 [6,5]
+ CRUSH rule 0 x 57 [5,8]
+ CRUSH rule 0 x 58 [2,8]
+ CRUSH rule 0 x 59 [5,2]
+ CRUSH rule 0 x 60 [5,2]
+ CRUSH rule 0 x 61 [5,8]
+ CRUSH rule 0 x 62 [7,2]
+ CRUSH rule 0 x 63 [5,6]
+ CRUSH rule 0 x 64 [5,2]
+ CRUSH rule 0 x 65 [7,5]
+ CRUSH rule 0 x 66 [5,6]
+ CRUSH rule 0 x 67 [5,2]
+ CRUSH rule 0 x 68 [2,5]
+ CRUSH rule 0 x 69 [5,2]
+ CRUSH rule 0 x 70 [7,2]
+ CRUSH rule 0 x 71 [2,7]
+ CRUSH rule 0 x 72 [6,2]
+ CRUSH rule 0 x 73 [2,7]
+ CRUSH rule 0 x 74 [2,8]
+ CRUSH rule 0 x 75 [5,2]
+ CRUSH rule 0 x 76 [5,2]
+ CRUSH rule 0 x 77 [7,2]
+ CRUSH rule 0 x 78 [2,5]
+ CRUSH rule 0 x 79 [5,2]
+ CRUSH rule 0 x 80 [2,5]
+ CRUSH rule 0 x 81 [2,5]
+ CRUSH rule 0 x 82 [7,2]
+ CRUSH rule 0 x 83 [2,6]
+ CRUSH rule 0 x 84 [7,2]
+ CRUSH rule 0 x 85 [5,7]
+ CRUSH rule 0 x 86 [2,6]
+ CRUSH rule 0 x 87 [2,6]
+ CRUSH rule 0 x 88 [2,8]
+ CRUSH rule 0 x 89 [5,2]
+ CRUSH rule 0 x 90 [6,5]
+ CRUSH rule 0 x 91 [5,6]
+ CRUSH rule 0 x 92 [2,6]
+ CRUSH rule 0 x 93 [7,5]
+ CRUSH rule 0 x 94 [2,5]
+ CRUSH rule 0 x 95 [7,5]
+ CRUSH rule 0 x 96 [5,8]
+ CRUSH rule 0 x 97 [8,5]
+ CRUSH rule 0 x 98 [2,7]
+ CRUSH rule 0 x 99 [2,8]
+ CRUSH rule 0 x 100 [2,6]
+ CRUSH rule 0 x 101 [5,8]
+ CRUSH rule 0 x 102 [5,2]
+ CRUSH rule 0 x 103 [5,7]
+ CRUSH rule 0 x 104 [7,5]
+ CRUSH rule 0 x 105 [2,5]
+ CRUSH rule 0 x 106 [2,6]
+ CRUSH rule 0 x 107 [5,2]
+ CRUSH rule 0 x 108 [7,2]
+ CRUSH rule 0 x 109 [2,5]
+ CRUSH rule 0 x 110 [5,2]
+ CRUSH rule 0 x 111 [2,5]
+ CRUSH rule 0 x 112 [2,6]
+ CRUSH rule 0 x 113 [6,2]
+ CRUSH rule 0 x 114 [7,5]
+ CRUSH rule 0 x 115 [8,2]
+ CRUSH rule 0 x 116 [2,7]
+ CRUSH rule 0 x 117 [7,5]
+ CRUSH rule 0 x 118 [2,5]
+ CRUSH rule 0 x 119 [5,7]
+ CRUSH rule 0 x 120 [2,5]
+ CRUSH rule 0 x 121 [2,8]
+ CRUSH rule 0 x 122 [8,5]
+ CRUSH rule 0 x 123 [2,5]
+ CRUSH rule 0 x 124 [5,2]
+ CRUSH rule 0 x 125 [2,7]
+ CRUSH rule 0 x 126 [5,2]
+ CRUSH rule 0 x 127 [5,6]
+ CRUSH rule 0 x 128 [5,8]
+ CRUSH rule 0 x 129 [2,5]
+ CRUSH rule 0 x 130 [5,8]
+ CRUSH rule 0 x 131 [2,5]
+ CRUSH rule 0 x 132 [2,5]
+ CRUSH rule 0 x 133 [5,6]
+ CRUSH rule 0 x 134 [2,7]
+ CRUSH rule 0 x 135 [5,7]
+ CRUSH rule 0 x 136 [2,5]
+ CRUSH rule 0 x 137 [7,5]
+ CRUSH rule 0 x 138 [8,5]
+ CRUSH rule 0 x 139 [5,2]
+ CRUSH rule 0 x 140 [2,8]
+ CRUSH rule 0 x 141 [6,2]
+ CRUSH rule 0 x 142 [5,2]
+ CRUSH rule 0 x 143 [5,7]
+ CRUSH rule 0 x 144 [8,2]
+ CRUSH rule 0 x 145 [8,5]
+ CRUSH rule 0 x 146 [2,8]
+ CRUSH rule 0 x 147 [2,6]
+ CRUSH rule 0 x 148 [5,2]
+ CRUSH rule 0 x 149 [5,6]
+ CRUSH rule 0 x 150 [2,8]
+ CRUSH rule 0 x 151 [5,8]
+ CRUSH rule 0 x 152 [8,5]
+ CRUSH rule 0 x 153 [8,5]
+ CRUSH rule 0 x 154 [5,2]
+ CRUSH rule 0 x 155 [5,6]
+ CRUSH rule 0 x 156 [5,2]
+ CRUSH rule 0 x 157 [5,2]
+ CRUSH rule 0 x 158 [2,6]
+ CRUSH rule 0 x 159 [7,2]
+ CRUSH rule 0 x 160 [2,8]
+ CRUSH rule 0 x 161 [2,5]
+ CRUSH rule 0 x 162 [2,8]
+ CRUSH rule 0 x 163 [5,8]
+ CRUSH rule 0 x 164 [7,2]
+ CRUSH rule 0 x 165 [7,2]
+ CRUSH rule 0 x 166 [2,5]
+ CRUSH rule 0 x 167 [2,6]
+ CRUSH rule 0 x 168 [5,2]
+ CRUSH rule 0 x 169 [2,7]
+ CRUSH rule 0 x 170 [2,5]
+ CRUSH rule 0 x 171 [7,5]
+ CRUSH rule 0 x 172 [2,8]
+ CRUSH rule 0 x 173 [8,5]
+ CRUSH rule 0 x 174 [2,5]
+ CRUSH rule 0 x 175 [6,2]
+ CRUSH rule 0 x 176 [5,2]
+ CRUSH rule 0 x 177 [5,2]
+ CRUSH rule 0 x 178 [5,2]
+ CRUSH rule 0 x 179 [5,2]
+ CRUSH rule 0 x 180 [5,7]
+ CRUSH rule 0 x 181 [6,2]
+ CRUSH rule 0 x 182 [8,5]
+ CRUSH rule 0 x 183 [7,5]
+ CRUSH rule 0 x 184 [5,6]
+ CRUSH rule 0 x 185 [6,2]
+ CRUSH rule 0 x 186 [2,5]
+ CRUSH rule 0 x 187 [2,6]
+ CRUSH rule 0 x 188 [2,6]
+ CRUSH rule 0 x 189 [2,6]
+ CRUSH rule 0 x 190 [5,2]
+ CRUSH rule 0 x 191 [7,2]
+ CRUSH rule 0 x 192 [5,2]
+ CRUSH rule 0 x 193 [5,2]
+ CRUSH rule 0 x 194 [2,5]
+ CRUSH rule 0 x 195 [6,5]
+ CRUSH rule 0 x 196 [6,2]
+ CRUSH rule 0 x 197 [6,5]
+ CRUSH rule 0 x 198 [2,5]
+ CRUSH rule 0 x 199 [2,5]
+ CRUSH rule 0 x 200 [2,5]
+ CRUSH rule 0 x 201 [7,2]
+ CRUSH rule 0 x 202 [6,5]
+ CRUSH rule 0 x 203 [5,6]
+ CRUSH rule 0 x 204 [2,5]
+ CRUSH rule 0 x 205 [2,7]
+ CRUSH rule 0 x 206 [2,8]
+ CRUSH rule 0 x 207 [5,2]
+ CRUSH rule 0 x 208 [7,2]
+ CRUSH rule 0 x 209 [2,8]
+ CRUSH rule 0 x 210 [2,5]
+ CRUSH rule 0 x 211 [5,2]
+ CRUSH rule 0 x 212 [7,5]
+ CRUSH rule 0 x 213 [8,5]
+ CRUSH rule 0 x 214 [5,7]
+ CRUSH rule 0 x 215 [8,2]
+ CRUSH rule 0 x 216 [5,2]
+ CRUSH rule 0 x 217 [2,7]
+ CRUSH rule 0 x 218 [2,7]
+ CRUSH rule 0 x 219 [5,6]
+ CRUSH rule 0 x 220 [5,8]
+ CRUSH rule 0 x 221 [5,7]
+ CRUSH rule 0 x 222 [6,5]
+ CRUSH rule 0 x 223 [2,5]
+ CRUSH rule 0 x 224 [2,5]
+ CRUSH rule 0 x 225 [8,2]
+ CRUSH rule 0 x 226 [7,2]
+ CRUSH rule 0 x 227 [5,2]
+ CRUSH rule 0 x 228 [5,6]
+ CRUSH rule 0 x 229 [5,7]
+ CRUSH rule 0 x 230 [5,6]
+ CRUSH rule 0 x 231 [5,7]
+ CRUSH rule 0 x 232 [2,8]
+ CRUSH rule 0 x 233 [5,6]
+ CRUSH rule 0 x 234 [2,5]
+ CRUSH rule 0 x 235 [5,6]
+ CRUSH rule 0 x 236 [5,2]
+ CRUSH rule 0 x 237 [5,8]
+ CRUSH rule 0 x 238 [5,2]
+ CRUSH rule 0 x 239 [8,5]
+ CRUSH rule 0 x 240 [5,8]
+ CRUSH rule 0 x 241 [5,2]
+ CRUSH rule 0 x 242 [5,2]
+ CRUSH rule 0 x 243 [5,8]
+ CRUSH rule 0 x 244 [5,6]
+ CRUSH rule 0 x 245 [7,2]
+ CRUSH rule 0 x 246 [2,5]
+ CRUSH rule 0 x 247 [6,2]
+ CRUSH rule 0 x 248 [8,2]
+ CRUSH rule 0 x 249 [2,5]
+ CRUSH rule 0 x 250 [2,5]
+ CRUSH rule 0 x 251 [2,5]
+ CRUSH rule 0 x 252 [5,7]
+ CRUSH rule 0 x 253 [5,2]
+ CRUSH rule 0 x 254 [5,2]
+ CRUSH rule 0 x 255 [2,7]
+ CRUSH rule 0 x 256 [5,6]
+ CRUSH rule 0 x 257 [2,6]
+ CRUSH rule 0 x 258 [5,2]
+ CRUSH rule 0 x 259 [5,6]
+ CRUSH rule 0 x 260 [5,8]
+ CRUSH rule 0 x 261 [8,5]
+ CRUSH rule 0 x 262 [5,6]
+ CRUSH rule 0 x 263 [6,2]
+ CRUSH rule 0 x 264 [5,6]
+ CRUSH rule 0 x 265 [8,5]
+ CRUSH rule 0 x 266 [8,2]
+ CRUSH rule 0 x 267 [2,5]
+ CRUSH rule 0 x 268 [2,6]
+ CRUSH rule 0 x 269 [2,6]
+ CRUSH rule 0 x 270 [5,2]
+ CRUSH rule 0 x 271 [7,5]
+ CRUSH rule 0 x 272 [2,6]
+ CRUSH rule 0 x 273 [5,2]
+ CRUSH rule 0 x 274 [6,5]
+ CRUSH rule 0 x 275 [5,8]
+ CRUSH rule 0 x 276 [7,2]
+ CRUSH rule 0 x 277 [6,5]
+ CRUSH rule 0 x 278 [6,2]
+ CRUSH rule 0 x 279 [8,5]
+ CRUSH rule 0 x 280 [2,7]
+ CRUSH rule 0 x 281 [8,2]
+ CRUSH rule 0 x 282 [5,2]
+ CRUSH rule 0 x 283 [8,2]
+ CRUSH rule 0 x 284 [6,5]
+ CRUSH rule 0 x 285 [5,7]
+ CRUSH rule 0 x 286 [2,8]
+ CRUSH rule 0 x 287 [2,5]
+ CRUSH rule 0 x 288 [8,2]
+ CRUSH rule 0 x 289 [5,6]
+ CRUSH rule 0 x 290 [2,5]
+ CRUSH rule 0 x 291 [2,5]
+ CRUSH rule 0 x 292 [8,2]
+ CRUSH rule 0 x 293 [6,2]
+ CRUSH rule 0 x 294 [7,5]
+ CRUSH rule 0 x 295 [5,6]
+ CRUSH rule 0 x 296 [5,2]
+ CRUSH rule 0 x 297 [6,2]
+ CRUSH rule 0 x 298 [2,5]
+ CRUSH rule 0 x 299 [2,8]
+ CRUSH rule 0 x 300 [8,5]
+ CRUSH rule 0 x 301 [2,7]
+ CRUSH rule 0 x 302 [5,2]
+ CRUSH rule 0 x 303 [7,5]
+ CRUSH rule 0 x 304 [2,6]
+ CRUSH rule 0 x 305 [5,6]
+ CRUSH rule 0 x 306 [2,8]
+ CRUSH rule 0 x 307 [2,7]
+ CRUSH rule 0 x 308 [2,8]
+ CRUSH rule 0 x 309 [7,5]
+ CRUSH rule 0 x 310 [5,2]
+ CRUSH rule 0 x 311 [5,8]
+ CRUSH rule 0 x 312 [2,6]
+ CRUSH rule 0 x 313 [5,2]
+ CRUSH rule 0 x 314 [5,2]
+ CRUSH rule 0 x 315 [2,5]
+ CRUSH rule 0 x 316 [6,5]
+ CRUSH rule 0 x 317 [2,7]
+ CRUSH rule 0 x 318 [8,2]
+ CRUSH rule 0 x 319 [5,2]
+ CRUSH rule 0 x 320 [5,8]
+ CRUSH rule 0 x 321 [2,5]
+ CRUSH rule 0 x 322 [2,6]
+ CRUSH rule 0 x 323 [5,7]
+ CRUSH rule 0 x 324 [7,2]
+ CRUSH rule 0 x 325 [5,6]
+ CRUSH rule 0 x 326 [5,2]
+ CRUSH rule 0 x 327 [2,8]
+ CRUSH rule 0 x 328 [7,5]
+ CRUSH rule 0 x 329 [5,7]
+ CRUSH rule 0 x 330 [5,7]
+ CRUSH rule 0 x 331 [2,7]
+ CRUSH rule 0 x 332 [2,5]
+ CRUSH rule 0 x 333 [6,5]
+ CRUSH rule 0 x 334 [8,5]
+ CRUSH rule 0 x 335 [7,2]
+ CRUSH rule 0 x 336 [5,6]
+ CRUSH rule 0 x 337 [7,2]
+ CRUSH rule 0 x 338 [5,8]
+ CRUSH rule 0 x 339 [7,5]
+ CRUSH rule 0 x 340 [2,6]
+ CRUSH rule 0 x 341 [5,2]
+ CRUSH rule 0 x 342 [2,8]
+ CRUSH rule 0 x 343 [6,5]
+ CRUSH rule 0 x 344 [6,2]
+ CRUSH rule 0 x 345 [5,7]
+ CRUSH rule 0 x 346 [8,2]
+ CRUSH rule 0 x 347 [5,2]
+ CRUSH rule 0 x 348 [8,2]
+ CRUSH rule 0 x 349 [2,7]
+ CRUSH rule 0 x 350 [8,5]
+ CRUSH rule 0 x 351 [5,8]
+ CRUSH rule 0 x 352 [2,8]
+ CRUSH rule 0 x 353 [6,5]
+ CRUSH rule 0 x 354 [2,5]
+ CRUSH rule 0 x 355 [5,8]
+ CRUSH rule 0 x 356 [5,2]
+ CRUSH rule 0 x 357 [6,2]
+ CRUSH rule 0 x 358 [2,8]
+ CRUSH rule 0 x 359 [6,2]
+ CRUSH rule 0 x 360 [5,2]
+ CRUSH rule 0 x 361 [8,5]
+ CRUSH rule 0 x 362 [5,2]
+ CRUSH rule 0 x 363 [5,2]
+ CRUSH rule 0 x 364 [2,5]
+ CRUSH rule 0 x 365 [6,5]
+ CRUSH rule 0 x 366 [7,2]
+ CRUSH rule 0 x 367 [5,2]
+ CRUSH rule 0 x 368 [7,5]
+ CRUSH rule 0 x 369 [5,7]
+ CRUSH rule 0 x 370 [8,2]
+ CRUSH rule 0 x 371 [2,5]
+ CRUSH rule 0 x 372 [5,2]
+ CRUSH rule 0 x 373 [2,6]
+ CRUSH rule 0 x 374 [5,8]
+ CRUSH rule 0 x 375 [6,5]
+ CRUSH rule 0 x 376 [7,2]
+ CRUSH rule 0 x 377 [2,5]
+ CRUSH rule 0 x 378 [2,6]
+ CRUSH rule 0 x 379 [8,5]
+ CRUSH rule 0 x 380 [2,5]
+ CRUSH rule 0 x 381 [2,5]
+ CRUSH rule 0 x 382 [2,5]
+ CRUSH rule 0 x 383 [5,7]
+ CRUSH rule 0 x 384 [7,2]
+ CRUSH rule 0 x 385 [7,5]
+ CRUSH rule 0 x 386 [2,5]
+ CRUSH rule 0 x 387 [2,5]
+ CRUSH rule 0 x 388 [5,2]
+ CRUSH rule 0 x 389 [2,5]
+ CRUSH rule 0 x 390 [5,8]
+ CRUSH rule 0 x 391 [5,6]
+ CRUSH rule 0 x 392 [2,7]
+ CRUSH rule 0 x 393 [5,2]
+ CRUSH rule 0 x 394 [5,8]
+ CRUSH rule 0 x 395 [5,2]
+ CRUSH rule 0 x 396 [5,2]
+ CRUSH rule 0 x 397 [2,5]
+ CRUSH rule 0 x 398 [2,5]
+ CRUSH rule 0 x 399 [8,5]
+ CRUSH rule 0 x 400 [8,2]
+ CRUSH rule 0 x 401 [2,5]
+ CRUSH rule 0 x 402 [7,5]
+ CRUSH rule 0 x 403 [2,5]
+ CRUSH rule 0 x 404 [5,2]
+ CRUSH rule 0 x 405 [6,5]
+ CRUSH rule 0 x 406 [2,6]
+ CRUSH rule 0 x 407 [2,7]
+ CRUSH rule 0 x 408 [5,2]
+ CRUSH rule 0 x 409 [7,5]
+ CRUSH rule 0 x 410 [8,5]
+ CRUSH rule 0 x 411 [2,7]
+ CRUSH rule 0 x 412 [2,5]
+ CRUSH rule 0 x 413 [5,2]
+ CRUSH rule 0 x 414 [5,2]
+ CRUSH rule 0 x 415 [2,6]
+ CRUSH rule 0 x 416 [2,8]
+ CRUSH rule 0 x 417 [8,2]
+ CRUSH rule 0 x 418 [7,2]
+ CRUSH rule 0 x 419 [8,5]
+ CRUSH rule 0 x 420 [2,5]
+ CRUSH rule 0 x 421 [8,5]
+ CRUSH rule 0 x 422 [6,5]
+ CRUSH rule 0 x 423 [2,5]
+ CRUSH rule 0 x 424 [8,5]
+ CRUSH rule 0 x 425 [2,5]
+ CRUSH rule 0 x 426 [6,2]
+ CRUSH rule 0 x 427 [2,8]
+ CRUSH rule 0 x 428 [5,8]
+ CRUSH rule 0 x 429 [5,8]
+ CRUSH rule 0 x 430 [5,7]
+ CRUSH rule 0 x 431 [5,2]
+ CRUSH rule 0 x 432 [7,2]
+ CRUSH rule 0 x 433 [6,5]
+ CRUSH rule 0 x 434 [5,2]
+ CRUSH rule 0 x 435 [2,5]
+ CRUSH rule 0 x 436 [5,2]
+ CRUSH rule 0 x 437 [7,5]
+ CRUSH rule 0 x 438 [2,5]
+ CRUSH rule 0 x 439 [2,5]
+ CRUSH rule 0 x 440 [2,6]
+ CRUSH rule 0 x 441 [5,8]
+ CRUSH rule 0 x 442 [2,5]
+ CRUSH rule 0 x 443 [6,2]
+ CRUSH rule 0 x 444 [7,2]
+ CRUSH rule 0 x 445 [6,5]
+ CRUSH rule 0 x 446 [5,2]
+ CRUSH rule 0 x 447 [2,5]
+ CRUSH rule 0 x 448 [7,2]
+ CRUSH rule 0 x 449 [7,5]
+ CRUSH rule 0 x 450 [5,2]
+ CRUSH rule 0 x 451 [6,5]
+ CRUSH rule 0 x 452 [8,5]
+ CRUSH rule 0 x 453 [6,5]
+ CRUSH rule 0 x 454 [6,5]
+ CRUSH rule 0 x 455 [2,8]
+ CRUSH rule 0 x 456 [6,2]
+ CRUSH rule 0 x 457 [7,2]
+ CRUSH rule 0 x 458 [2,8]
+ CRUSH rule 0 x 459 [2,6]
+ CRUSH rule 0 x 460 [6,5]
+ CRUSH rule 0 x 461 [6,5]
+ CRUSH rule 0 x 462 [8,2]
+ CRUSH rule 0 x 463 [6,2]
+ CRUSH rule 0 x 464 [7,5]
+ CRUSH rule 0 x 465 [7,2]
+ CRUSH rule 0 x 466 [5,6]
+ CRUSH rule 0 x 467 [6,5]
+ CRUSH rule 0 x 468 [7,2]
+ CRUSH rule 0 x 469 [7,2]
+ CRUSH rule 0 x 470 [5,2]
+ CRUSH rule 0 x 471 [2,6]
+ CRUSH rule 0 x 472 [5,2]
+ CRUSH rule 0 x 473 [2,5]
+ CRUSH rule 0 x 474 [6,2]
+ CRUSH rule 0 x 475 [6,2]
+ CRUSH rule 0 x 476 [5,7]
+ CRUSH rule 0 x 477 [5,6]
+ CRUSH rule 0 x 478 [6,2]
+ CRUSH rule 0 x 479 [2,5]
+ CRUSH rule 0 x 480 [2,6]
+ CRUSH rule 0 x 481 [2,5]
+ CRUSH rule 0 x 482 [5,8]
+ CRUSH rule 0 x 483 [2,6]
+ CRUSH rule 0 x 484 [2,8]
+ CRUSH rule 0 x 485 [5,8]
+ CRUSH rule 0 x 486 [5,2]
+ CRUSH rule 0 x 487 [5,2]
+ CRUSH rule 0 x 488 [5,7]
+ CRUSH rule 0 x 489 [2,8]
+ CRUSH rule 0 x 490 [6,5]
+ CRUSH rule 0 x 491 [2,6]
+ CRUSH rule 0 x 492 [6,5]
+ CRUSH rule 0 x 493 [2,8]
+ CRUSH rule 0 x 494 [2,6]
+ CRUSH rule 0 x 495 [5,2]
+ CRUSH rule 0 x 496 [7,5]
+ CRUSH rule 0 x 497 [5,7]
+ CRUSH rule 0 x 498 [2,5]
+ CRUSH rule 0 x 499 [8,5]
+ CRUSH rule 0 x 500 [5,6]
+ CRUSH rule 0 x 501 [2,7]
+ CRUSH rule 0 x 502 [7,2]
+ CRUSH rule 0 x 503 [2,5]
+ CRUSH rule 0 x 504 [5,8]
+ CRUSH rule 0 x 505 [2,7]
+ CRUSH rule 0 x 506 [5,2]
+ CRUSH rule 0 x 507 [6,2]
+ CRUSH rule 0 x 508 [2,5]
+ CRUSH rule 0 x 509 [7,5]
+ CRUSH rule 0 x 510 [6,2]
+ CRUSH rule 0 x 511 [5,6]
+ CRUSH rule 0 x 512 [7,2]
+ CRUSH rule 0 x 513 [7,2]
+ CRUSH rule 0 x 514 [5,7]
+ CRUSH rule 0 x 515 [8,5]
+ CRUSH rule 0 x 516 [5,2]
+ CRUSH rule 0 x 517 [7,2]
+ CRUSH rule 0 x 518 [5,6]
+ CRUSH rule 0 x 519 [7,5]
+ CRUSH rule 0 x 520 [2,8]
+ CRUSH rule 0 x 521 [8,2]
+ CRUSH rule 0 x 522 [6,2]
+ CRUSH rule 0 x 523 [5,2]
+ CRUSH rule 0 x 524 [2,5]
+ CRUSH rule 0 x 525 [2,5]
+ CRUSH rule 0 x 526 [2,5]
+ CRUSH rule 0 x 527 [2,5]
+ CRUSH rule 0 x 528 [5,2]
+ CRUSH rule 0 x 529 [5,6]
+ CRUSH rule 0 x 530 [6,5]
+ CRUSH rule 0 x 531 [6,2]
+ CRUSH rule 0 x 532 [6,5]
+ CRUSH rule 0 x 533 [5,8]
+ CRUSH rule 0 x 534 [7,5]
+ CRUSH rule 0 x 535 [8,2]
+ CRUSH rule 0 x 536 [6,2]
+ CRUSH rule 0 x 537 [5,8]
+ CRUSH rule 0 x 538 [6,5]
+ CRUSH rule 0 x 539 [8,5]
+ CRUSH rule 0 x 540 [2,7]
+ CRUSH rule 0 x 541 [2,5]
+ CRUSH rule 0 x 542 [5,2]
+ CRUSH rule 0 x 543 [6,2]
+ CRUSH rule 0 x 544 [5,7]
+ CRUSH rule 0 x 545 [5,7]
+ CRUSH rule 0 x 546 [6,2]
+ CRUSH rule 0 x 547 [8,2]
+ CRUSH rule 0 x 548 [5,2]
+ CRUSH rule 0 x 549 [5,7]
+ CRUSH rule 0 x 550 [2,5]
+ CRUSH rule 0 x 551 [7,5]
+ CRUSH rule 0 x 552 [5,7]
+ CRUSH rule 0 x 553 [5,2]
+ CRUSH rule 0 x 554 [2,6]
+ CRUSH rule 0 x 555 [5,2]
+ CRUSH rule 0 x 556 [5,6]
+ CRUSH rule 0 x 557 [7,5]
+ CRUSH rule 0 x 558 [5,2]
+ CRUSH rule 0 x 559 [5,2]
+ CRUSH rule 0 x 560 [8,5]
+ CRUSH rule 0 x 561 [6,5]
+ CRUSH rule 0 x 562 [5,2]
+ CRUSH rule 0 x 563 [2,7]
+ CRUSH rule 0 x 564 [5,2]
+ CRUSH rule 0 x 565 [5,8]
+ CRUSH rule 0 x 566 [5,6]
+ CRUSH rule 0 x 567 [5,7]
+ CRUSH rule 0 x 568 [7,5]
+ CRUSH rule 0 x 569 [5,2]
+ CRUSH rule 0 x 570 [2,5]
+ CRUSH rule 0 x 571 [5,6]
+ CRUSH rule 0 x 572 [5,2]
+ CRUSH rule 0 x 573 [5,2]
+ CRUSH rule 0 x 574 [2,5]
+ CRUSH rule 0 x 575 [8,2]
+ CRUSH rule 0 x 576 [5,7]
+ CRUSH rule 0 x 577 [8,2]
+ CRUSH rule 0 x 578 [6,2]
+ CRUSH rule 0 x 579 [5,2]
+ CRUSH rule 0 x 580 [5,2]
+ CRUSH rule 0 x 581 [7,2]
+ CRUSH rule 0 x 582 [2,8]
+ CRUSH rule 0 x 583 [6,2]
+ CRUSH rule 0 x 584 [8,2]
+ CRUSH rule 0 x 585 [7,2]
+ CRUSH rule 0 x 586 [2,8]
+ CRUSH rule 0 x 587 [2,5]
+ CRUSH rule 0 x 588 [5,8]
+ CRUSH rule 0 x 589 [7,2]
+ CRUSH rule 0 x 590 [6,2]
+ CRUSH rule 0 x 591 [5,2]
+ CRUSH rule 0 x 592 [2,5]
+ CRUSH rule 0 x 593 [2,8]
+ CRUSH rule 0 x 594 [2,6]
+ CRUSH rule 0 x 595 [7,2]
+ CRUSH rule 0 x 596 [5,2]
+ CRUSH rule 0 x 597 [5,2]
+ CRUSH rule 0 x 598 [5,2]
+ CRUSH rule 0 x 599 [5,2]
+ CRUSH rule 0 x 600 [7,2]
+ CRUSH rule 0 x 601 [2,6]
+ CRUSH rule 0 x 602 [5,8]
+ CRUSH rule 0 x 603 [5,2]
+ CRUSH rule 0 x 604 [7,5]
+ CRUSH rule 0 x 605 [5,2]
+ CRUSH rule 0 x 606 [2,7]
+ CRUSH rule 0 x 607 [2,5]
+ CRUSH rule 0 x 608 [5,2]
+ CRUSH rule 0 x 609 [5,2]
+ CRUSH rule 0 x 610 [5,8]
+ CRUSH rule 0 x 611 [2,8]
+ CRUSH rule 0 x 612 [2,8]
+ CRUSH rule 0 x 613 [7,2]
+ CRUSH rule 0 x 614 [7,2]
+ CRUSH rule 0 x 615 [6,2]
+ CRUSH rule 0 x 616 [2,7]
+ CRUSH rule 0 x 617 [6,2]
+ CRUSH rule 0 x 618 [7,5]
+ CRUSH rule 0 x 619 [5,2]
+ CRUSH rule 0 x 620 [5,2]
+ CRUSH rule 0 x 621 [5,6]
+ CRUSH rule 0 x 622 [2,5]
+ CRUSH rule 0 x 623 [2,8]
+ CRUSH rule 0 x 624 [5,2]
+ CRUSH rule 0 x 625 [2,5]
+ CRUSH rule 0 x 626 [7,2]
+ CRUSH rule 0 x 627 [2,6]
+ CRUSH rule 0 x 628 [8,2]
+ CRUSH rule 0 x 629 [2,6]
+ CRUSH rule 0 x 630 [2,6]
+ CRUSH rule 0 x 631 [2,6]
+ CRUSH rule 0 x 632 [7,2]
+ CRUSH rule 0 x 633 [8,5]
+ CRUSH rule 0 x 634 [2,5]
+ CRUSH rule 0 x 635 [5,6]
+ CRUSH rule 0 x 636 [2,5]
+ CRUSH rule 0 x 637 [5,2]
+ CRUSH rule 0 x 638 [6,2]
+ CRUSH rule 0 x 639 [5,2]
+ CRUSH rule 0 x 640 [5,2]
+ CRUSH rule 0 x 641 [7,2]
+ CRUSH rule 0 x 642 [2,8]
+ CRUSH rule 0 x 643 [5,2]
+ CRUSH rule 0 x 644 [8,2]
+ CRUSH rule 0 x 645 [5,7]
+ CRUSH rule 0 x 646 [8,2]
+ CRUSH rule 0 x 647 [7,2]
+ CRUSH rule 0 x 648 [2,8]
+ CRUSH rule 0 x 649 [5,7]
+ CRUSH rule 0 x 650 [7,5]
+ CRUSH rule 0 x 651 [5,6]
+ CRUSH rule 0 x 652 [5,6]
+ CRUSH rule 0 x 653 [8,5]
+ CRUSH rule 0 x 654 [7,5]
+ CRUSH rule 0 x 655 [2,5]
+ CRUSH rule 0 x 656 [5,7]
+ CRUSH rule 0 x 657 [6,2]
+ CRUSH rule 0 x 658 [5,8]
+ CRUSH rule 0 x 659 [5,7]
+ CRUSH rule 0 x 660 [7,5]
+ CRUSH rule 0 x 661 [2,7]
+ CRUSH rule 0 x 662 [5,2]
+ CRUSH rule 0 x 663 [2,5]
+ CRUSH rule 0 x 664 [2,5]
+ CRUSH rule 0 x 665 [5,6]
+ CRUSH rule 0 x 666 [2,7]
+ CRUSH rule 0 x 667 [2,5]
+ CRUSH rule 0 x 668 [5,7]
+ CRUSH rule 0 x 669 [6,5]
+ CRUSH rule 0 x 670 [5,2]
+ CRUSH rule 0 x 671 [2,8]
+ CRUSH rule 0 x 672 [5,2]
+ CRUSH rule 0 x 673 [5,2]
+ CRUSH rule 0 x 674 [5,2]
+ CRUSH rule 0 x 675 [2,8]
+ CRUSH rule 0 x 676 [2,5]
+ CRUSH rule 0 x 677 [5,2]
+ CRUSH rule 0 x 678 [2,5]
+ CRUSH rule 0 x 679 [6,2]
+ CRUSH rule 0 x 680 [2,5]
+ CRUSH rule 0 x 681 [5,6]
+ CRUSH rule 0 x 682 [2,5]
+ CRUSH rule 0 x 683 [2,5]
+ CRUSH rule 0 x 684 [7,2]
+ CRUSH rule 0 x 685 [7,2]
+ CRUSH rule 0 x 686 [2,5]
+ CRUSH rule 0 x 687 [5,7]
+ CRUSH rule 0 x 688 [5,6]
+ CRUSH rule 0 x 689 [6,5]
+ CRUSH rule 0 x 690 [8,2]
+ CRUSH rule 0 x 691 [5,2]
+ CRUSH rule 0 x 692 [7,2]
+ CRUSH rule 0 x 693 [6,5]
+ CRUSH rule 0 x 694 [6,5]
+ CRUSH rule 0 x 695 [2,6]
+ CRUSH rule 0 x 696 [2,5]
+ CRUSH rule 0 x 697 [6,2]
+ CRUSH rule 0 x 698 [6,2]
+ CRUSH rule 0 x 699 [2,8]
+ CRUSH rule 0 x 700 [2,5]
+ CRUSH rule 0 x 701 [5,2]
+ CRUSH rule 0 x 702 [5,2]
+ CRUSH rule 0 x 703 [8,5]
+ CRUSH rule 0 x 704 [2,5]
+ CRUSH rule 0 x 705 [8,2]
+ CRUSH rule 0 x 706 [2,5]
+ CRUSH rule 0 x 707 [7,5]
+ CRUSH rule 0 x 708 [5,7]
+ CRUSH rule 0 x 709 [6,5]
+ CRUSH rule 0 x 710 [8,5]
+ CRUSH rule 0 x 711 [2,5]
+ CRUSH rule 0 x 712 [2,5]
+ CRUSH rule 0 x 713 [6,5]
+ CRUSH rule 0 x 714 [5,2]
+ CRUSH rule 0 x 715 [2,5]
+ CRUSH rule 0 x 716 [5,6]
+ CRUSH rule 0 x 717 [8,2]
+ CRUSH rule 0 x 718 [5,7]
+ CRUSH rule 0 x 719 [2,6]
+ CRUSH rule 0 x 720 [6,2]
+ CRUSH rule 0 x 721 [5,7]
+ CRUSH rule 0 x 722 [5,7]
+ CRUSH rule 0 x 723 [5,2]
+ CRUSH rule 0 x 724 [2,7]
+ CRUSH rule 0 x 725 [2,5]
+ CRUSH rule 0 x 726 [5,7]
+ CRUSH rule 0 x 727 [5,7]
+ CRUSH rule 0 x 728 [2,6]
+ CRUSH rule 0 x 729 [5,6]
+ CRUSH rule 0 x 730 [5,8]
+ CRUSH rule 0 x 731 [5,2]
+ CRUSH rule 0 x 732 [2,5]
+ CRUSH rule 0 x 733 [5,6]
+ CRUSH rule 0 x 734 [6,5]
+ CRUSH rule 0 x 735 [5,6]
+ CRUSH rule 0 x 736 [5,8]
+ CRUSH rule 0 x 737 [2,8]
+ CRUSH rule 0 x 738 [5,2]
+ CRUSH rule 0 x 739 [2,7]
+ CRUSH rule 0 x 740 [2,7]
+ CRUSH rule 0 x 741 [7,2]
+ CRUSH rule 0 x 742 [8,2]
+ CRUSH rule 0 x 743 [7,2]
+ CRUSH rule 0 x 744 [5,6]
+ CRUSH rule 0 x 745 [5,2]
+ CRUSH rule 0 x 746 [5,2]
+ CRUSH rule 0 x 747 [6,2]
+ CRUSH rule 0 x 748 [2,6]
+ CRUSH rule 0 x 749 [5,7]
+ CRUSH rule 0 x 750 [2,7]
+ CRUSH rule 0 x 751 [2,7]
+ CRUSH rule 0 x 752 [8,2]
+ CRUSH rule 0 x 753 [7,5]
+ CRUSH rule 0 x 754 [8,5]
+ CRUSH rule 0 x 755 [2,6]
+ CRUSH rule 0 x 756 [5,8]
+ CRUSH rule 0 x 757 [8,2]
+ CRUSH rule 0 x 758 [6,2]
+ CRUSH rule 0 x 759 [8,5]
+ CRUSH rule 0 x 760 [2,5]
+ CRUSH rule 0 x 761 [5,2]
+ CRUSH rule 0 x 762 [2,8]
+ CRUSH rule 0 x 763 [8,5]
+ CRUSH rule 0 x 764 [2,7]
+ CRUSH rule 0 x 765 [6,5]
+ CRUSH rule 0 x 766 [8,5]
+ CRUSH rule 0 x 767 [2,8]
+ CRUSH rule 0 x 768 [8,5]
+ CRUSH rule 0 x 769 [6,2]
+ CRUSH rule 0 x 770 [6,2]
+ CRUSH rule 0 x 771 [7,2]
+ CRUSH rule 0 x 772 [8,5]
+ CRUSH rule 0 x 773 [5,2]
+ CRUSH rule 0 x 774 [5,7]
+ CRUSH rule 0 x 775 [6,5]
+ CRUSH rule 0 x 776 [7,2]
+ CRUSH rule 0 x 777 [5,2]
+ CRUSH rule 0 x 778 [2,6]
+ CRUSH rule 0 x 779 [2,6]
+ CRUSH rule 0 x 780 [2,5]
+ CRUSH rule 0 x 781 [6,5]
+ CRUSH rule 0 x 782 [5,2]
+ CRUSH rule 0 x 783 [7,2]
+ CRUSH rule 0 x 784 [2,5]
+ CRUSH rule 0 x 785 [6,2]
+ CRUSH rule 0 x 786 [7,5]
+ CRUSH rule 0 x 787 [2,8]
+ CRUSH rule 0 x 788 [6,2]
+ CRUSH rule 0 x 789 [2,5]
+ CRUSH rule 0 x 790 [8,5]
+ CRUSH rule 0 x 791 [5,6]
+ CRUSH rule 0 x 792 [5,6]
+ CRUSH rule 0 x 793 [6,2]
+ CRUSH rule 0 x 794 [2,8]
+ CRUSH rule 0 x 795 [2,5]
+ CRUSH rule 0 x 796 [5,7]
+ CRUSH rule 0 x 797 [2,5]
+ CRUSH rule 0 x 798 [6,2]
+ CRUSH rule 0 x 799 [5,2]
+ CRUSH rule 0 x 800 [5,2]
+ CRUSH rule 0 x 801 [5,7]
+ CRUSH rule 0 x 802 [2,6]
+ CRUSH rule 0 x 803 [2,5]
+ CRUSH rule 0 x 804 [6,2]
+ CRUSH rule 0 x 805 [5,8]
+ CRUSH rule 0 x 806 [2,5]
+ CRUSH rule 0 x 807 [5,7]
+ CRUSH rule 0 x 808 [5,7]
+ CRUSH rule 0 x 809 [2,5]
+ CRUSH rule 0 x 810 [5,7]
+ CRUSH rule 0 x 811 [8,5]
+ CRUSH rule 0 x 812 [8,5]
+ CRUSH rule 0 x 813 [6,5]
+ CRUSH rule 0 x 814 [5,8]
+ CRUSH rule 0 x 815 [5,2]
+ CRUSH rule 0 x 816 [2,6]
+ CRUSH rule 0 x 817 [5,6]
+ CRUSH rule 0 x 818 [5,2]
+ CRUSH rule 0 x 819 [5,2]
+ CRUSH rule 0 x 820 [5,7]
+ CRUSH rule 0 x 821 [5,8]
+ CRUSH rule 0 x 822 [2,5]
+ CRUSH rule 0 x 823 [5,7]
+ CRUSH rule 0 x 824 [5,7]
+ CRUSH rule 0 x 825 [2,8]
+ CRUSH rule 0 x 826 [7,2]
+ CRUSH rule 0 x 827 [2,6]
+ CRUSH rule 0 x 828 [2,5]
+ CRUSH rule 0 x 829 [5,6]
+ CRUSH rule 0 x 830 [2,5]
+ CRUSH rule 0 x 831 [2,6]
+ CRUSH rule 0 x 832 [5,8]
+ CRUSH rule 0 x 833 [2,6]
+ CRUSH rule 0 x 834 [5,2]
+ CRUSH rule 0 x 835 [8,5]
+ CRUSH rule 0 x 836 [5,8]
+ CRUSH rule 0 x 837 [6,5]
+ CRUSH rule 0 x 838 [6,2]
+ CRUSH rule 0 x 839 [5,2]
+ CRUSH rule 0 x 840 [7,5]
+ CRUSH rule 0 x 841 [5,8]
+ CRUSH rule 0 x 842 [2,5]
+ CRUSH rule 0 x 843 [6,5]
+ CRUSH rule 0 x 844 [5,8]
+ CRUSH rule 0 x 845 [5,6]
+ CRUSH rule 0 x 846 [5,2]
+ CRUSH rule 0 x 847 [2,8]
+ CRUSH rule 0 x 848 [2,6]
+ CRUSH rule 0 x 849 [5,8]
+ CRUSH rule 0 x 850 [2,5]
+ CRUSH rule 0 x 851 [6,5]
+ CRUSH rule 0 x 852 [7,5]
+ CRUSH rule 0 x 853 [6,2]
+ CRUSH rule 0 x 854 [7,2]
+ CRUSH rule 0 x 855 [5,7]
+ CRUSH rule 0 x 856 [6,5]
+ CRUSH rule 0 x 857 [8,5]
+ CRUSH rule 0 x 858 [6,5]
+ CRUSH rule 0 x 859 [6,2]
+ CRUSH rule 0 x 860 [5,2]
+ CRUSH rule 0 x 861 [8,5]
+ CRUSH rule 0 x 862 [6,2]
+ CRUSH rule 0 x 863 [8,2]
+ CRUSH rule 0 x 864 [5,6]
+ CRUSH rule 0 x 865 [8,2]
+ CRUSH rule 0 x 866 [5,6]
+ CRUSH rule 0 x 867 [6,5]
+ CRUSH rule 0 x 868 [6,5]
+ CRUSH rule 0 x 869 [8,5]
+ CRUSH rule 0 x 870 [2,5]
+ CRUSH rule 0 x 871 [5,2]
+ CRUSH rule 0 x 872 [5,2]
+ CRUSH rule 0 x 873 [5,6]
+ CRUSH rule 0 x 874 [2,6]
+ CRUSH rule 0 x 875 [2,6]
+ CRUSH rule 0 x 876 [5,8]
+ CRUSH rule 0 x 877 [6,5]
+ CRUSH rule 0 x 878 [5,2]
+ CRUSH rule 0 x 879 [7,5]
+ CRUSH rule 0 x 880 [5,2]
+ CRUSH rule 0 x 881 [5,8]
+ CRUSH rule 0 x 882 [5,2]
+ CRUSH rule 0 x 883 [2,5]
+ CRUSH rule 0 x 884 [6,2]
+ CRUSH rule 0 x 885 [5,2]
+ CRUSH rule 0 x 886 [5,7]
+ CRUSH rule 0 x 887 [7,5]
+ CRUSH rule 0 x 888 [6,2]
+ CRUSH rule 0 x 889 [2,8]
+ CRUSH rule 0 x 890 [7,2]
+ CRUSH rule 0 x 891 [2,7]
+ CRUSH rule 0 x 892 [6,2]
+ CRUSH rule 0 x 893 [2,5]
+ CRUSH rule 0 x 894 [7,5]
+ CRUSH rule 0 x 895 [5,2]
+ CRUSH rule 0 x 896 [2,6]
+ CRUSH rule 0 x 897 [5,2]
+ CRUSH rule 0 x 898 [2,5]
+ CRUSH rule 0 x 899 [2,6]
+ CRUSH rule 0 x 900 [5,2]
+ CRUSH rule 0 x 901 [5,2]
+ CRUSH rule 0 x 902 [8,5]
+ CRUSH rule 0 x 903 [5,6]
+ CRUSH rule 0 x 904 [5,7]
+ CRUSH rule 0 x 905 [6,2]
+ CRUSH rule 0 x 906 [2,7]
+ CRUSH rule 0 x 907 [7,2]
+ CRUSH rule 0 x 908 [5,6]
+ CRUSH rule 0 x 909 [2,5]
+ CRUSH rule 0 x 910 [6,5]
+ CRUSH rule 0 x 911 [5,7]
+ CRUSH rule 0 x 912 [2,8]
+ CRUSH rule 0 x 913 [7,2]
+ CRUSH rule 0 x 914 [6,5]
+ CRUSH rule 0 x 915 [8,2]
+ CRUSH rule 0 x 916 [5,2]
+ CRUSH rule 0 x 917 [2,5]
+ CRUSH rule 0 x 918 [8,2]
+ CRUSH rule 0 x 919 [6,2]
+ CRUSH rule 0 x 920 [7,5]
+ CRUSH rule 0 x 921 [2,5]
+ CRUSH rule 0 x 922 [6,5]
+ CRUSH rule 0 x 923 [5,6]
+ CRUSH rule 0 x 924 [5,2]
+ CRUSH rule 0 x 925 [5,6]
+ CRUSH rule 0 x 926 [5,2]
+ CRUSH rule 0 x 927 [2,6]
+ CRUSH rule 0 x 928 [8,2]
+ CRUSH rule 0 x 929 [5,2]
+ CRUSH rule 0 x 930 [2,5]
+ CRUSH rule 0 x 931 [5,2]
+ CRUSH rule 0 x 932 [5,2]
+ CRUSH rule 0 x 933 [8,5]
+ CRUSH rule 0 x 934 [5,8]
+ CRUSH rule 0 x 935 [6,5]
+ CRUSH rule 0 x 936 [2,7]
+ CRUSH rule 0 x 937 [5,8]
+ CRUSH rule 0 x 938 [6,5]
+ CRUSH rule 0 x 939 [2,8]
+ CRUSH rule 0 x 940 [8,5]
+ CRUSH rule 0 x 941 [5,2]
+ CRUSH rule 0 x 942 [2,6]
+ CRUSH rule 0 x 943 [8,2]
+ CRUSH rule 0 x 944 [5,8]
+ CRUSH rule 0 x 945 [7,2]
+ CRUSH rule 0 x 946 [2,8]
+ CRUSH rule 0 x 947 [5,2]
+ CRUSH rule 0 x 948 [7,5]
+ CRUSH rule 0 x 949 [6,2]
+ CRUSH rule 0 x 950 [5,7]
+ CRUSH rule 0 x 951 [5,6]
+ CRUSH rule 0 x 952 [2,7]
+ CRUSH rule 0 x 953 [2,5]
+ CRUSH rule 0 x 954 [5,2]
+ CRUSH rule 0 x 955 [8,2]
+ CRUSH rule 0 x 956 [2,7]
+ CRUSH rule 0 x 957 [7,2]
+ CRUSH rule 0 x 958 [8,5]
+ CRUSH rule 0 x 959 [5,2]
+ CRUSH rule 0 x 960 [5,6]
+ CRUSH rule 0 x 961 [5,2]
+ CRUSH rule 0 x 962 [7,5]
+ CRUSH rule 0 x 963 [2,5]
+ CRUSH rule 0 x 964 [5,2]
+ CRUSH rule 0 x 965 [7,5]
+ CRUSH rule 0 x 966 [5,6]
+ CRUSH rule 0 x 967 [8,5]
+ CRUSH rule 0 x 968 [7,2]
+ CRUSH rule 0 x 969 [8,2]
+ CRUSH rule 0 x 970 [2,8]
+ CRUSH rule 0 x 971 [2,8]
+ CRUSH rule 0 x 972 [2,7]
+ CRUSH rule 0 x 973 [2,8]
+ CRUSH rule 0 x 974 [5,2]
+ CRUSH rule 0 x 975 [5,8]
+ CRUSH rule 0 x 976 [5,7]
+ CRUSH rule 0 x 977 [8,5]
+ CRUSH rule 0 x 978 [7,2]
+ CRUSH rule 0 x 979 [7,2]
+ CRUSH rule 0 x 980 [6,2]
+ CRUSH rule 0 x 981 [7,5]
+ CRUSH rule 0 x 982 [5,2]
+ CRUSH rule 0 x 983 [5,6]
+ CRUSH rule 0 x 984 [2,8]
+ CRUSH rule 0 x 985 [2,5]
+ CRUSH rule 0 x 986 [8,5]
+ CRUSH rule 0 x 987 [2,5]
+ CRUSH rule 0 x 988 [2,5]
+ CRUSH rule 0 x 989 [2,8]
+ CRUSH rule 0 x 990 [2,6]
+ CRUSH rule 0 x 991 [2,5]
+ CRUSH rule 0 x 992 [7,2]
+ CRUSH rule 0 x 993 [2,6]
+ CRUSH rule 0 x 994 [5,6]
+ CRUSH rule 0 x 995 [7,2]
+ CRUSH rule 0 x 996 [6,5]
+ CRUSH rule 0 x 997 [6,5]
+ CRUSH rule 0 x 998 [8,2]
+ CRUSH rule 0 x 999 [2,7]
+ CRUSH rule 0 x 1000 [8,5]
+ CRUSH rule 0 x 1001 [2,5]
+ CRUSH rule 0 x 1002 [2,5]
+ CRUSH rule 0 x 1003 [2,7]
+ CRUSH rule 0 x 1004 [6,2]
+ CRUSH rule 0 x 1005 [6,2]
+ CRUSH rule 0 x 1006 [2,8]
+ CRUSH rule 0 x 1007 [2,5]
+ CRUSH rule 0 x 1008 [2,7]
+ CRUSH rule 0 x 1009 [6,5]
+ CRUSH rule 0 x 1010 [5,2]
+ CRUSH rule 0 x 1011 [5,2]
+ CRUSH rule 0 x 1012 [5,2]
+ CRUSH rule 0 x 1013 [5,2]
+ CRUSH rule 0 x 1014 [2,8]
+ CRUSH rule 0 x 1015 [6,5]
+ CRUSH rule 0 x 1016 [2,5]
+ CRUSH rule 0 x 1017 [6,2]
+ CRUSH rule 0 x 1018 [5,2]
+ CRUSH rule 0 x 1019 [5,8]
+ CRUSH rule 0 x 1020 [5,2]
+ CRUSH rule 0 x 1021 [5,2]
+ CRUSH rule 0 x 1022 [2,7]
+ CRUSH rule 0 x 1023 [5,2]
+ rule 0 (choose) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [2,5,7]
+ CRUSH rule 0 x 1 [2,8,5]
+ CRUSH rule 0 x 2 [2,5,8]
+ CRUSH rule 0 x 3 [8,2,5]
+ CRUSH rule 0 x 4 [5,2,8]
+ CRUSH rule 0 x 5 [7,2,5]
+ CRUSH rule 0 x 6 [2,6,5]
+ CRUSH rule 0 x 7 [5,6,2]
+ CRUSH rule 0 x 8 [5,7,2]
+ CRUSH rule 0 x 9 [2,5,8]
+ CRUSH rule 0 x 10 [2,8,5]
+ CRUSH rule 0 x 11 [2,6,5]
+ CRUSH rule 0 x 12 [2,5,7]
+ CRUSH rule 0 x 13 [5,8,2]
+ CRUSH rule 0 x 14 [7,2,5]
+ CRUSH rule 0 x 15 [7,2,5]
+ CRUSH rule 0 x 16 [5,7,2]
+ CRUSH rule 0 x 17 [5,2,7]
+ CRUSH rule 0 x 18 [2,5,6]
+ CRUSH rule 0 x 19 [7,5,2]
+ CRUSH rule 0 x 20 [2,5,7]
+ CRUSH rule 0 x 21 [5,6,2]
+ CRUSH rule 0 x 22 [8,5,2]
+ CRUSH rule 0 x 23 [5,7,2]
+ CRUSH rule 0 x 24 [2,6,5]
+ CRUSH rule 0 x 25 [5,8,2]
+ CRUSH rule 0 x 26 [2,7,5]
+ CRUSH rule 0 x 27 [5,2,7]
+ CRUSH rule 0 x 28 [6,2,5]
+ CRUSH rule 0 x 29 [8,5,2]
+ CRUSH rule 0 x 30 [5,6,2]
+ CRUSH rule 0 x 31 [8,2,5]
+ CRUSH rule 0 x 32 [5,7,2]
+ CRUSH rule 0 x 33 [2,7,5]
+ CRUSH rule 0 x 34 [2,5,7]
+ CRUSH rule 0 x 35 [2,6,5]
+ CRUSH rule 0 x 36 [5,6,2]
+ CRUSH rule 0 x 37 [2,5,7]
+ CRUSH rule 0 x 38 [5,6,2]
+ CRUSH rule 0 x 39 [5,7,2]
+ CRUSH rule 0 x 40 [7,2,5]
+ CRUSH rule 0 x 41 [2,7,5]
+ CRUSH rule 0 x 42 [5,7,2]
+ CRUSH rule 0 x 43 [2,5,8]
+ CRUSH rule 0 x 44 [2,8,5]
+ CRUSH rule 0 x 45 [8,2,5]
+ CRUSH rule 0 x 46 [2,5,8]
+ CRUSH rule 0 x 47 [5,2,7]
+ CRUSH rule 0 x 48 [5,6,2]
+ CRUSH rule 0 x 49 [5,6,2]
+ CRUSH rule 0 x 50 [5,2,7]
+ CRUSH rule 0 x 51 [5,6,2]
+ CRUSH rule 0 x 52 [8,2,5]
+ CRUSH rule 0 x 53 [5,6,2]
+ CRUSH rule 0 x 54 [7,5,2]
+ CRUSH rule 0 x 55 [8,2,5]
+ CRUSH rule 0 x 56 [6,5,2]
+ CRUSH rule 0 x 57 [5,8,2]
+ CRUSH rule 0 x 58 [2,8,5]
+ CRUSH rule 0 x 59 [5,2,7]
+ CRUSH rule 0 x 60 [5,2,8]
+ CRUSH rule 0 x 61 [5,8,2]
+ CRUSH rule 0 x 62 [7,2,5]
+ CRUSH rule 0 x 63 [5,6,2]
+ CRUSH rule 0 x 64 [5,2,8]
+ CRUSH rule 0 x 65 [7,5,2]
+ CRUSH rule 0 x 66 [5,6,2]
+ CRUSH rule 0 x 67 [5,2,6]
+ CRUSH rule 0 x 68 [2,5,8]
+ CRUSH rule 0 x 69 [5,2,7]
+ CRUSH rule 0 x 70 [7,2,5]
+ CRUSH rule 0 x 71 [2,7,5]
+ CRUSH rule 0 x 72 [6,2,5]
+ CRUSH rule 0 x 73 [2,7,5]
+ CRUSH rule 0 x 74 [2,8,5]
+ CRUSH rule 0 x 75 [5,2,7]
+ CRUSH rule 0 x 76 [5,2,6]
+ CRUSH rule 0 x 77 [7,2,5]
+ CRUSH rule 0 x 78 [2,5,7]
+ CRUSH rule 0 x 79 [5,2,7]
+ CRUSH rule 0 x 80 [2,5,6]
+ CRUSH rule 0 x 81 [2,5,8]
+ CRUSH rule 0 x 82 [7,2,5]
+ CRUSH rule 0 x 83 [2,6,5]
+ CRUSH rule 0 x 84 [7,2,5]
+ CRUSH rule 0 x 85 [5,7,2]
+ CRUSH rule 0 x 86 [2,6,5]
+ CRUSH rule 0 x 87 [2,6,5]
+ CRUSH rule 0 x 88 [2,8,5]
+ CRUSH rule 0 x 89 [5,2,8]
+ CRUSH rule 0 x 90 [6,5,2]
+ CRUSH rule 0 x 91 [5,6,2]
+ CRUSH rule 0 x 92 [2,6,5]
+ CRUSH rule 0 x 93 [7,5,2]
+ CRUSH rule 0 x 94 [2,5,7]
+ CRUSH rule 0 x 95 [7,5,2]
+ CRUSH rule 0 x 96 [5,8,2]
+ CRUSH rule 0 x 97 [8,5,2]
+ CRUSH rule 0 x 98 [2,7,5]
+ CRUSH rule 0 x 99 [2,8,5]
+ CRUSH rule 0 x 100 [2,6,5]
+ CRUSH rule 0 x 101 [5,8,2]
+ CRUSH rule 0 x 102 [5,2,7]
+ CRUSH rule 0 x 103 [5,7,2]
+ CRUSH rule 0 x 104 [7,5,2]
+ CRUSH rule 0 x 105 [2,5,8]
+ CRUSH rule 0 x 106 [2,6,5]
+ CRUSH rule 0 x 107 [5,2,7]
+ CRUSH rule 0 x 108 [7,2,5]
+ CRUSH rule 0 x 109 [2,5,8]
+ CRUSH rule 0 x 110 [5,2,6]
+ CRUSH rule 0 x 111 [2,5,7]
+ CRUSH rule 0 x 112 [2,6,5]
+ CRUSH rule 0 x 113 [6,2,5]
+ CRUSH rule 0 x 114 [7,5,2]
+ CRUSH rule 0 x 115 [8,2,5]
+ CRUSH rule 0 x 116 [2,7,5]
+ CRUSH rule 0 x 117 [7,5,2]
+ CRUSH rule 0 x 118 [2,5,6]
+ CRUSH rule 0 x 119 [5,7,2]
+ CRUSH rule 0 x 120 [2,5,6]
+ CRUSH rule 0 x 121 [2,8,5]
+ CRUSH rule 0 x 122 [8,5,2]
+ CRUSH rule 0 x 123 [2,5,7]
+ CRUSH rule 0 x 124 [5,2,8]
+ CRUSH rule 0 x 125 [2,7,5]
+ CRUSH rule 0 x 126 [5,2,7]
+ CRUSH rule 0 x 127 [5,6,2]
+ CRUSH rule 0 x 128 [5,8,2]
+ CRUSH rule 0 x 129 [2,5,7]
+ CRUSH rule 0 x 130 [5,8,2]
+ CRUSH rule 0 x 131 [2,5,6]
+ CRUSH rule 0 x 132 [2,5,8]
+ CRUSH rule 0 x 133 [5,6,2]
+ CRUSH rule 0 x 134 [2,7,5]
+ CRUSH rule 0 x 135 [5,7,2]
+ CRUSH rule 0 x 136 [2,5,8]
+ CRUSH rule 0 x 137 [7,5,2]
+ CRUSH rule 0 x 138 [8,5,2]
+ CRUSH rule 0 x 139 [5,2,8]
+ CRUSH rule 0 x 140 [2,8,5]
+ CRUSH rule 0 x 141 [6,2,5]
+ CRUSH rule 0 x 142 [5,2,8]
+ CRUSH rule 0 x 143 [5,7,2]
+ CRUSH rule 0 x 144 [8,2,5]
+ CRUSH rule 0 x 145 [8,5,2]
+ CRUSH rule 0 x 146 [2,8,5]
+ CRUSH rule 0 x 147 [2,6,5]
+ CRUSH rule 0 x 148 [5,2,8]
+ CRUSH rule 0 x 149 [5,6,2]
+ CRUSH rule 0 x 150 [2,8,5]
+ CRUSH rule 0 x 151 [5,8,2]
+ CRUSH rule 0 x 152 [8,5,2]
+ CRUSH rule 0 x 153 [8,5,2]
+ CRUSH rule 0 x 154 [5,2,6]
+ CRUSH rule 0 x 155 [5,6,2]
+ CRUSH rule 0 x 156 [5,2,7]
+ CRUSH rule 0 x 157 [5,2,7]
+ CRUSH rule 0 x 158 [2,6,5]
+ CRUSH rule 0 x 159 [7,2,5]
+ CRUSH rule 0 x 160 [2,8,5]
+ CRUSH rule 0 x 161 [2,5,6]
+ CRUSH rule 0 x 162 [2,8,5]
+ CRUSH rule 0 x 163 [5,8,2]
+ CRUSH rule 0 x 164 [7,2,5]
+ CRUSH rule 0 x 165 [7,2,5]
+ CRUSH rule 0 x 166 [2,5,7]
+ CRUSH rule 0 x 167 [2,6,5]
+ CRUSH rule 0 x 168 [5,2,7]
+ CRUSH rule 0 x 169 [2,7,5]
+ CRUSH rule 0 x 170 [2,5,8]
+ CRUSH rule 0 x 171 [7,5,2]
+ CRUSH rule 0 x 172 [2,8,5]
+ CRUSH rule 0 x 173 [8,5,2]
+ CRUSH rule 0 x 174 [2,5,8]
+ CRUSH rule 0 x 175 [6,2,5]
+ CRUSH rule 0 x 176 [5,2,7]
+ CRUSH rule 0 x 177 [5,2,8]
+ CRUSH rule 0 x 178 [5,2,6]
+ CRUSH rule 0 x 179 [5,2,7]
+ CRUSH rule 0 x 180 [5,7,2]
+ CRUSH rule 0 x 181 [6,2,5]
+ CRUSH rule 0 x 182 [8,5,2]
+ CRUSH rule 0 x 183 [7,5,2]
+ CRUSH rule 0 x 184 [5,6,2]
+ CRUSH rule 0 x 185 [6,2,5]
+ CRUSH rule 0 x 186 [2,5,8]
+ CRUSH rule 0 x 187 [2,6,5]
+ CRUSH rule 0 x 188 [2,6,5]
+ CRUSH rule 0 x 189 [2,6,5]
+ CRUSH rule 0 x 190 [5,2,8]
+ CRUSH rule 0 x 191 [7,2,5]
+ CRUSH rule 0 x 192 [5,2,6]
+ CRUSH rule 0 x 193 [5,2,6]
+ CRUSH rule 0 x 194 [2,5,7]
+ CRUSH rule 0 x 195 [6,5,2]
+ CRUSH rule 0 x 196 [6,2,5]
+ CRUSH rule 0 x 197 [6,5,2]
+ CRUSH rule 0 x 198 [2,5,6]
+ CRUSH rule 0 x 199 [2,5,7]
+ CRUSH rule 0 x 200 [2,5,8]
+ CRUSH rule 0 x 201 [7,2,5]
+ CRUSH rule 0 x 202 [6,5,2]
+ CRUSH rule 0 x 203 [5,6,2]
+ CRUSH rule 0 x 204 [2,5,8]
+ CRUSH rule 0 x 205 [2,7,5]
+ CRUSH rule 0 x 206 [2,8,5]
+ CRUSH rule 0 x 207 [5,2,7]
+ CRUSH rule 0 x 208 [7,2,5]
+ CRUSH rule 0 x 209 [2,8,5]
+ CRUSH rule 0 x 210 [2,5,6]
+ CRUSH rule 0 x 211 [5,2,7]
+ CRUSH rule 0 x 212 [7,5,2]
+ CRUSH rule 0 x 213 [8,5,2]
+ CRUSH rule 0 x 214 [5,7,2]
+ CRUSH rule 0 x 215 [8,2,5]
+ CRUSH rule 0 x 216 [5,2,8]
+ CRUSH rule 0 x 217 [2,7,5]
+ CRUSH rule 0 x 218 [2,7,5]
+ CRUSH rule 0 x 219 [5,6,2]
+ CRUSH rule 0 x 220 [5,8,2]
+ CRUSH rule 0 x 221 [5,7,2]
+ CRUSH rule 0 x 222 [6,5,2]
+ CRUSH rule 0 x 223 [2,5,6]
+ CRUSH rule 0 x 224 [2,5,8]
+ CRUSH rule 0 x 225 [8,2,5]
+ CRUSH rule 0 x 226 [7,2,5]
+ CRUSH rule 0 x 227 [5,2,7]
+ CRUSH rule 0 x 228 [5,6,2]
+ CRUSH rule 0 x 229 [5,7,2]
+ CRUSH rule 0 x 230 [5,6,2]
+ CRUSH rule 0 x 231 [5,7,2]
+ CRUSH rule 0 x 232 [2,8,5]
+ CRUSH rule 0 x 233 [5,6,2]
+ CRUSH rule 0 x 234 [2,5,6]
+ CRUSH rule 0 x 235 [5,6,2]
+ CRUSH rule 0 x 236 [5,2,8]
+ CRUSH rule 0 x 237 [5,8,2]
+ CRUSH rule 0 x 238 [5,2,6]
+ CRUSH rule 0 x 239 [8,5,2]
+ CRUSH rule 0 x 240 [5,8,2]
+ CRUSH rule 0 x 241 [5,2,7]
+ CRUSH rule 0 x 242 [5,2,6]
+ CRUSH rule 0 x 243 [5,8,2]
+ CRUSH rule 0 x 244 [5,6,2]
+ CRUSH rule 0 x 245 [7,2,5]
+ CRUSH rule 0 x 246 [2,5,7]
+ CRUSH rule 0 x 247 [6,2,5]
+ CRUSH rule 0 x 248 [8,2,5]
+ CRUSH rule 0 x 249 [2,5,8]
+ CRUSH rule 0 x 250 [2,5,8]
+ CRUSH rule 0 x 251 [2,5,7]
+ CRUSH rule 0 x 252 [5,7,2]
+ CRUSH rule 0 x 253 [5,2,7]
+ CRUSH rule 0 x 254 [5,2,8]
+ CRUSH rule 0 x 255 [2,7,5]
+ CRUSH rule 0 x 256 [5,6,2]
+ CRUSH rule 0 x 257 [2,6,5]
+ CRUSH rule 0 x 258 [5,2,8]
+ CRUSH rule 0 x 259 [5,6,2]
+ CRUSH rule 0 x 260 [5,8,2]
+ CRUSH rule 0 x 261 [8,5,2]
+ CRUSH rule 0 x 262 [5,6,2]
+ CRUSH rule 0 x 263 [6,2,5]
+ CRUSH rule 0 x 264 [5,6,2]
+ CRUSH rule 0 x 265 [8,5,2]
+ CRUSH rule 0 x 266 [8,2,5]
+ CRUSH rule 0 x 267 [2,5,6]
+ CRUSH rule 0 x 268 [2,6,5]
+ CRUSH rule 0 x 269 [2,6,5]
+ CRUSH rule 0 x 270 [5,2,8]
+ CRUSH rule 0 x 271 [7,5,2]
+ CRUSH rule 0 x 272 [2,6,5]
+ CRUSH rule 0 x 273 [5,2,7]
+ CRUSH rule 0 x 274 [6,5,2]
+ CRUSH rule 0 x 275 [5,8,2]
+ CRUSH rule 0 x 276 [7,2,5]
+ CRUSH rule 0 x 277 [6,5,2]
+ CRUSH rule 0 x 278 [6,2,5]
+ CRUSH rule 0 x 279 [8,5,2]
+ CRUSH rule 0 x 280 [2,7,5]
+ CRUSH rule 0 x 281 [8,2,5]
+ CRUSH rule 0 x 282 [5,2,8]
+ CRUSH rule 0 x 283 [8,2,5]
+ CRUSH rule 0 x 284 [6,5,2]
+ CRUSH rule 0 x 285 [5,7,2]
+ CRUSH rule 0 x 286 [2,8,5]
+ CRUSH rule 0 x 287 [2,5,8]
+ CRUSH rule 0 x 288 [8,2,5]
+ CRUSH rule 0 x 289 [5,6,2]
+ CRUSH rule 0 x 290 [2,5,6]
+ CRUSH rule 0 x 291 [2,5,8]
+ CRUSH rule 0 x 292 [8,2,5]
+ CRUSH rule 0 x 293 [6,2,5]
+ CRUSH rule 0 x 294 [7,5,2]
+ CRUSH rule 0 x 295 [5,6,2]
+ CRUSH rule 0 x 296 [5,2,6]
+ CRUSH rule 0 x 297 [6,2,5]
+ CRUSH rule 0 x 298 [2,5,6]
+ CRUSH rule 0 x 299 [2,8,5]
+ CRUSH rule 0 x 300 [8,5,2]
+ CRUSH rule 0 x 301 [2,7,5]
+ CRUSH rule 0 x 302 [5,2,6]
+ CRUSH rule 0 x 303 [7,5,2]
+ CRUSH rule 0 x 304 [2,6,5]
+ CRUSH rule 0 x 305 [5,6,2]
+ CRUSH rule 0 x 306 [2,8,5]
+ CRUSH rule 0 x 307 [2,7,5]
+ CRUSH rule 0 x 308 [2,8,5]
+ CRUSH rule 0 x 309 [7,5,2]
+ CRUSH rule 0 x 310 [5,2,6]
+ CRUSH rule 0 x 311 [5,8,2]
+ CRUSH rule 0 x 312 [2,6,5]
+ CRUSH rule 0 x 313 [5,2,7]
+ CRUSH rule 0 x 314 [5,2,6]
+ CRUSH rule 0 x 315 [2,5,7]
+ CRUSH rule 0 x 316 [6,5,2]
+ CRUSH rule 0 x 317 [2,7,5]
+ CRUSH rule 0 x 318 [8,2,5]
+ CRUSH rule 0 x 319 [5,2,6]
+ CRUSH rule 0 x 320 [5,8,2]
+ CRUSH rule 0 x 321 [2,5,8]
+ CRUSH rule 0 x 322 [2,6,5]
+ CRUSH rule 0 x 323 [5,7,2]
+ CRUSH rule 0 x 324 [7,2,5]
+ CRUSH rule 0 x 325 [5,6,2]
+ CRUSH rule 0 x 326 [5,2,7]
+ CRUSH rule 0 x 327 [2,8,5]
+ CRUSH rule 0 x 328 [7,5,2]
+ CRUSH rule 0 x 329 [5,7,2]
+ CRUSH rule 0 x 330 [5,7,2]
+ CRUSH rule 0 x 331 [2,7,5]
+ CRUSH rule 0 x 332 [2,5,8]
+ CRUSH rule 0 x 333 [6,5,2]
+ CRUSH rule 0 x 334 [8,5,2]
+ CRUSH rule 0 x 335 [7,2,5]
+ CRUSH rule 0 x 336 [5,6,2]
+ CRUSH rule 0 x 337 [7,2,5]
+ CRUSH rule 0 x 338 [5,8,2]
+ CRUSH rule 0 x 339 [7,5,2]
+ CRUSH rule 0 x 340 [2,6,5]
+ CRUSH rule 0 x 341 [5,2,7]
+ CRUSH rule 0 x 342 [2,8,5]
+ CRUSH rule 0 x 343 [6,5,2]
+ CRUSH rule 0 x 344 [6,2,5]
+ CRUSH rule 0 x 345 [5,7,2]
+ CRUSH rule 0 x 346 [8,2,5]
+ CRUSH rule 0 x 347 [5,2,8]
+ CRUSH rule 0 x 348 [8,2,5]
+ CRUSH rule 0 x 349 [2,7,5]
+ CRUSH rule 0 x 350 [8,5,2]
+ CRUSH rule 0 x 351 [5,8,2]
+ CRUSH rule 0 x 352 [2,8,5]
+ CRUSH rule 0 x 353 [6,5,2]
+ CRUSH rule 0 x 354 [2,5,6]
+ CRUSH rule 0 x 355 [5,8,2]
+ CRUSH rule 0 x 356 [5,2,8]
+ CRUSH rule 0 x 357 [6,2,5]
+ CRUSH rule 0 x 358 [2,8,5]
+ CRUSH rule 0 x 359 [6,2,5]
+ CRUSH rule 0 x 360 [5,2,8]
+ CRUSH rule 0 x 361 [8,5,2]
+ CRUSH rule 0 x 362 [5,2,8]
+ CRUSH rule 0 x 363 [5,2,8]
+ CRUSH rule 0 x 364 [2,5,7]
+ CRUSH rule 0 x 365 [6,5,2]
+ CRUSH rule 0 x 366 [7,2,5]
+ CRUSH rule 0 x 367 [5,2,7]
+ CRUSH rule 0 x 368 [7,5,2]
+ CRUSH rule 0 x 369 [5,7,2]
+ CRUSH rule 0 x 370 [8,2,5]
+ CRUSH rule 0 x 371 [2,5,8]
+ CRUSH rule 0 x 372 [5,2,8]
+ CRUSH rule 0 x 373 [2,6,5]
+ CRUSH rule 0 x 374 [5,8,2]
+ CRUSH rule 0 x 375 [6,5,2]
+ CRUSH rule 0 x 376 [7,2,5]
+ CRUSH rule 0 x 377 [2,5,7]
+ CRUSH rule 0 x 378 [2,6,5]
+ CRUSH rule 0 x 379 [8,5,2]
+ CRUSH rule 0 x 380 [2,5,8]
+ CRUSH rule 0 x 381 [2,5,7]
+ CRUSH rule 0 x 382 [2,5,7]
+ CRUSH rule 0 x 383 [5,7,2]
+ CRUSH rule 0 x 384 [7,2,5]
+ CRUSH rule 0 x 385 [7,5,2]
+ CRUSH rule 0 x 386 [2,5,6]
+ CRUSH rule 0 x 387 [2,5,8]
+ CRUSH rule 0 x 388 [5,2,7]
+ CRUSH rule 0 x 389 [2,5,8]
+ CRUSH rule 0 x 390 [5,8,2]
+ CRUSH rule 0 x 391 [5,6,2]
+ CRUSH rule 0 x 392 [2,7,5]
+ CRUSH rule 0 x 393 [5,2,6]
+ CRUSH rule 0 x 394 [5,8,2]
+ CRUSH rule 0 x 395 [5,2,8]
+ CRUSH rule 0 x 396 [5,2,6]
+ CRUSH rule 0 x 397 [2,5,7]
+ CRUSH rule 0 x 398 [2,5,6]
+ CRUSH rule 0 x 399 [8,5,2]
+ CRUSH rule 0 x 400 [8,2,5]
+ CRUSH rule 0 x 401 [2,5,6]
+ CRUSH rule 0 x 402 [7,5,2]
+ CRUSH rule 0 x 403 [2,5,7]
+ CRUSH rule 0 x 404 [5,2,6]
+ CRUSH rule 0 x 405 [6,5,2]
+ CRUSH rule 0 x 406 [2,6,5]
+ CRUSH rule 0 x 407 [2,7,5]
+ CRUSH rule 0 x 408 [5,2,7]
+ CRUSH rule 0 x 409 [7,5,2]
+ CRUSH rule 0 x 410 [8,5,2]
+ CRUSH rule 0 x 411 [2,7,5]
+ CRUSH rule 0 x 412 [2,5,7]
+ CRUSH rule 0 x 413 [5,2,8]
+ CRUSH rule 0 x 414 [5,2,8]
+ CRUSH rule 0 x 415 [2,6,5]
+ CRUSH rule 0 x 416 [2,8,5]
+ CRUSH rule 0 x 417 [8,2,5]
+ CRUSH rule 0 x 418 [7,2,5]
+ CRUSH rule 0 x 419 [8,5,2]
+ CRUSH rule 0 x 420 [2,5,7]
+ CRUSH rule 0 x 421 [8,5,2]
+ CRUSH rule 0 x 422 [6,5,2]
+ CRUSH rule 0 x 423 [2,5,7]
+ CRUSH rule 0 x 424 [8,5,2]
+ CRUSH rule 0 x 425 [2,5,8]
+ CRUSH rule 0 x 426 [6,2,5]
+ CRUSH rule 0 x 427 [2,8,5]
+ CRUSH rule 0 x 428 [5,8,2]
+ CRUSH rule 0 x 429 [5,8,2]
+ CRUSH rule 0 x 430 [5,7,2]
+ CRUSH rule 0 x 431 [5,2,7]
+ CRUSH rule 0 x 432 [7,2,5]
+ CRUSH rule 0 x 433 [6,5,2]
+ CRUSH rule 0 x 434 [5,2,7]
+ CRUSH rule 0 x 435 [2,5,6]
+ CRUSH rule 0 x 436 [5,2,7]
+ CRUSH rule 0 x 437 [7,5,2]
+ CRUSH rule 0 x 438 [2,5,8]
+ CRUSH rule 0 x 439 [2,5,8]
+ CRUSH rule 0 x 440 [2,6,5]
+ CRUSH rule 0 x 441 [5,8,2]
+ CRUSH rule 0 x 442 [2,5,6]
+ CRUSH rule 0 x 443 [6,2,5]
+ CRUSH rule 0 x 444 [7,2,5]
+ CRUSH rule 0 x 445 [6,5,2]
+ CRUSH rule 0 x 446 [5,2,8]
+ CRUSH rule 0 x 447 [2,5,6]
+ CRUSH rule 0 x 448 [7,2,5]
+ CRUSH rule 0 x 449 [7,5,2]
+ CRUSH rule 0 x 450 [5,2,6]
+ CRUSH rule 0 x 451 [6,5,2]
+ CRUSH rule 0 x 452 [8,5,2]
+ CRUSH rule 0 x 453 [6,5,2]
+ CRUSH rule 0 x 454 [6,5,2]
+ CRUSH rule 0 x 455 [2,8,5]
+ CRUSH rule 0 x 456 [6,2,5]
+ CRUSH rule 0 x 457 [7,2,5]
+ CRUSH rule 0 x 458 [2,8,5]
+ CRUSH rule 0 x 459 [2,6,5]
+ CRUSH rule 0 x 460 [6,5,2]
+ CRUSH rule 0 x 461 [6,5,2]
+ CRUSH rule 0 x 462 [8,2,5]
+ CRUSH rule 0 x 463 [6,2,5]
+ CRUSH rule 0 x 464 [7,5,2]
+ CRUSH rule 0 x 465 [7,2,5]
+ CRUSH rule 0 x 466 [5,6,2]
+ CRUSH rule 0 x 467 [6,5,2]
+ CRUSH rule 0 x 468 [7,2,5]
+ CRUSH rule 0 x 469 [7,2,5]
+ CRUSH rule 0 x 470 [5,2,8]
+ CRUSH rule 0 x 471 [2,6,5]
+ CRUSH rule 0 x 472 [5,2,8]
+ CRUSH rule 0 x 473 [2,5,7]
+ CRUSH rule 0 x 474 [6,2,5]
+ CRUSH rule 0 x 475 [6,2,5]
+ CRUSH rule 0 x 476 [5,7,2]
+ CRUSH rule 0 x 477 [5,6,2]
+ CRUSH rule 0 x 478 [6,2,5]
+ CRUSH rule 0 x 479 [2,5,6]
+ CRUSH rule 0 x 480 [2,6,5]
+ CRUSH rule 0 x 481 [2,5,7]
+ CRUSH rule 0 x 482 [5,8,2]
+ CRUSH rule 0 x 483 [2,6,5]
+ CRUSH rule 0 x 484 [2,8,5]
+ CRUSH rule 0 x 485 [5,8,2]
+ CRUSH rule 0 x 486 [5,2,8]
+ CRUSH rule 0 x 487 [5,2,8]
+ CRUSH rule 0 x 488 [5,7,2]
+ CRUSH rule 0 x 489 [2,8,5]
+ CRUSH rule 0 x 490 [6,5,2]
+ CRUSH rule 0 x 491 [2,6,5]
+ CRUSH rule 0 x 492 [6,5,2]
+ CRUSH rule 0 x 493 [2,8,5]
+ CRUSH rule 0 x 494 [2,6,5]
+ CRUSH rule 0 x 495 [5,2,6]
+ CRUSH rule 0 x 496 [7,5,2]
+ CRUSH rule 0 x 497 [5,7,2]
+ CRUSH rule 0 x 498 [2,5,6]
+ CRUSH rule 0 x 499 [8,5,2]
+ CRUSH rule 0 x 500 [5,6,2]
+ CRUSH rule 0 x 501 [2,7,5]
+ CRUSH rule 0 x 502 [7,2,5]
+ CRUSH rule 0 x 503 [2,5,7]
+ CRUSH rule 0 x 504 [5,8,2]
+ CRUSH rule 0 x 505 [2,7,5]
+ CRUSH rule 0 x 506 [5,2,7]
+ CRUSH rule 0 x 507 [6,2,5]
+ CRUSH rule 0 x 508 [2,5,7]
+ CRUSH rule 0 x 509 [7,5,2]
+ CRUSH rule 0 x 510 [6,2,5]
+ CRUSH rule 0 x 511 [5,6,2]
+ CRUSH rule 0 x 512 [7,2,5]
+ CRUSH rule 0 x 513 [7,2,5]
+ CRUSH rule 0 x 514 [5,7,2]
+ CRUSH rule 0 x 515 [8,5,2]
+ CRUSH rule 0 x 516 [5,2,7]
+ CRUSH rule 0 x 517 [7,2,5]
+ CRUSH rule 0 x 518 [5,6,2]
+ CRUSH rule 0 x 519 [7,5,2]
+ CRUSH rule 0 x 520 [2,8,5]
+ CRUSH rule 0 x 521 [8,2,5]
+ CRUSH rule 0 x 522 [6,2,5]
+ CRUSH rule 0 x 523 [5,2,7]
+ CRUSH rule 0 x 524 [2,5,7]
+ CRUSH rule 0 x 525 [2,5,8]
+ CRUSH rule 0 x 526 [2,5,8]
+ CRUSH rule 0 x 527 [2,5,6]
+ CRUSH rule 0 x 528 [5,2,7]
+ CRUSH rule 0 x 529 [5,6,2]
+ CRUSH rule 0 x 530 [6,5,2]
+ CRUSH rule 0 x 531 [6,2,5]
+ CRUSH rule 0 x 532 [6,5,2]
+ CRUSH rule 0 x 533 [5,8,2]
+ CRUSH rule 0 x 534 [7,5,2]
+ CRUSH rule 0 x 535 [8,2,5]
+ CRUSH rule 0 x 536 [6,2,5]
+ CRUSH rule 0 x 537 [5,8,2]
+ CRUSH rule 0 x 538 [6,5,2]
+ CRUSH rule 0 x 539 [8,5,2]
+ CRUSH rule 0 x 540 [2,7,5]
+ CRUSH rule 0 x 541 [2,5,7]
+ CRUSH rule 0 x 542 [5,2,8]
+ CRUSH rule 0 x 543 [6,2,5]
+ CRUSH rule 0 x 544 [5,7,2]
+ CRUSH rule 0 x 545 [5,7,2]
+ CRUSH rule 0 x 546 [6,2,5]
+ CRUSH rule 0 x 547 [8,2,5]
+ CRUSH rule 0 x 548 [5,2,8]
+ CRUSH rule 0 x 549 [5,7,2]
+ CRUSH rule 0 x 550 [2,5,6]
+ CRUSH rule 0 x 551 [7,5,2]
+ CRUSH rule 0 x 552 [5,7,2]
+ CRUSH rule 0 x 553 [5,2,8]
+ CRUSH rule 0 x 554 [2,6,5]
+ CRUSH rule 0 x 555 [5,2,8]
+ CRUSH rule 0 x 556 [5,6,2]
+ CRUSH rule 0 x 557 [7,5,2]
+ CRUSH rule 0 x 558 [5,2,6]
+ CRUSH rule 0 x 559 [5,2,8]
+ CRUSH rule 0 x 560 [8,5,2]
+ CRUSH rule 0 x 561 [6,5,2]
+ CRUSH rule 0 x 562 [5,2,8]
+ CRUSH rule 0 x 563 [2,7,5]
+ CRUSH rule 0 x 564 [5,2,7]
+ CRUSH rule 0 x 565 [5,8,2]
+ CRUSH rule 0 x 566 [5,6,2]
+ CRUSH rule 0 x 567 [5,7,2]
+ CRUSH rule 0 x 568 [7,5,2]
+ CRUSH rule 0 x 569 [5,2,6]
+ CRUSH rule 0 x 570 [2,5,7]
+ CRUSH rule 0 x 571 [5,6,2]
+ CRUSH rule 0 x 572 [5,2,7]
+ CRUSH rule 0 x 573 [5,2,7]
+ CRUSH rule 0 x 574 [2,5,6]
+ CRUSH rule 0 x 575 [8,2,5]
+ CRUSH rule 0 x 576 [5,7,2]
+ CRUSH rule 0 x 577 [8,2,5]
+ CRUSH rule 0 x 578 [6,2,5]
+ CRUSH rule 0 x 579 [5,2,6]
+ CRUSH rule 0 x 580 [5,2,8]
+ CRUSH rule 0 x 581 [7,2,5]
+ CRUSH rule 0 x 582 [2,8,5]
+ CRUSH rule 0 x 583 [6,2,5]
+ CRUSH rule 0 x 584 [8,2,5]
+ CRUSH rule 0 x 585 [7,2,5]
+ CRUSH rule 0 x 586 [2,8,5]
+ CRUSH rule 0 x 587 [2,5,6]
+ CRUSH rule 0 x 588 [5,8,2]
+ CRUSH rule 0 x 589 [7,2,5]
+ CRUSH rule 0 x 590 [6,2,5]
+ CRUSH rule 0 x 591 [5,2,7]
+ CRUSH rule 0 x 592 [2,5,6]
+ CRUSH rule 0 x 593 [2,8,5]
+ CRUSH rule 0 x 594 [2,6,5]
+ CRUSH rule 0 x 595 [7,2,5]
+ CRUSH rule 0 x 596 [5,2,6]
+ CRUSH rule 0 x 597 [5,2,7]
+ CRUSH rule 0 x 598 [5,2,7]
+ CRUSH rule 0 x 599 [5,2,6]
+ CRUSH rule 0 x 600 [7,2,5]
+ CRUSH rule 0 x 601 [2,6,5]
+ CRUSH rule 0 x 602 [5,8,2]
+ CRUSH rule 0 x 603 [5,2,8]
+ CRUSH rule 0 x 604 [7,5,2]
+ CRUSH rule 0 x 605 [5,2,6]
+ CRUSH rule 0 x 606 [2,7,5]
+ CRUSH rule 0 x 607 [2,5,8]
+ CRUSH rule 0 x 608 [5,2,8]
+ CRUSH rule 0 x 609 [5,2,6]
+ CRUSH rule 0 x 610 [5,8,2]
+ CRUSH rule 0 x 611 [2,8,5]
+ CRUSH rule 0 x 612 [2,8,5]
+ CRUSH rule 0 x 613 [7,2,5]
+ CRUSH rule 0 x 614 [7,2,5]
+ CRUSH rule 0 x 615 [6,2,5]
+ CRUSH rule 0 x 616 [2,7,5]
+ CRUSH rule 0 x 617 [6,2,5]
+ CRUSH rule 0 x 618 [7,5,2]
+ CRUSH rule 0 x 619 [5,2,6]
+ CRUSH rule 0 x 620 [5,2,8]
+ CRUSH rule 0 x 621 [5,6,2]
+ CRUSH rule 0 x 622 [2,5,8]
+ CRUSH rule 0 x 623 [2,8,5]
+ CRUSH rule 0 x 624 [5,2,8]
+ CRUSH rule 0 x 625 [2,5,8]
+ CRUSH rule 0 x 626 [7,2,5]
+ CRUSH rule 0 x 627 [2,6,5]
+ CRUSH rule 0 x 628 [8,2,5]
+ CRUSH rule 0 x 629 [2,6,5]
+ CRUSH rule 0 x 630 [2,6,5]
+ CRUSH rule 0 x 631 [2,6,5]
+ CRUSH rule 0 x 632 [7,2,5]
+ CRUSH rule 0 x 633 [8,5,2]
+ CRUSH rule 0 x 634 [2,5,6]
+ CRUSH rule 0 x 635 [5,6,2]
+ CRUSH rule 0 x 636 [2,5,7]
+ CRUSH rule 0 x 637 [5,2,8]
+ CRUSH rule 0 x 638 [6,2,5]
+ CRUSH rule 0 x 639 [5,2,6]
+ CRUSH rule 0 x 640 [5,2,7]
+ CRUSH rule 0 x 641 [7,2,5]
+ CRUSH rule 0 x 642 [2,8,5]
+ CRUSH rule 0 x 643 [5,2,8]
+ CRUSH rule 0 x 644 [8,2,5]
+ CRUSH rule 0 x 645 [5,7,2]
+ CRUSH rule 0 x 646 [8,2,5]
+ CRUSH rule 0 x 647 [7,2,5]
+ CRUSH rule 0 x 648 [2,8,5]
+ CRUSH rule 0 x 649 [5,7,2]
+ CRUSH rule 0 x 650 [7,5,2]
+ CRUSH rule 0 x 651 [5,6,2]
+ CRUSH rule 0 x 652 [5,6,2]
+ CRUSH rule 0 x 653 [8,5,2]
+ CRUSH rule 0 x 654 [7,5,2]
+ CRUSH rule 0 x 655 [2,5,6]
+ CRUSH rule 0 x 656 [5,7,2]
+ CRUSH rule 0 x 657 [6,2,5]
+ CRUSH rule 0 x 658 [5,8,2]
+ CRUSH rule 0 x 659 [5,7,2]
+ CRUSH rule 0 x 660 [7,5,2]
+ CRUSH rule 0 x 661 [2,7,5]
+ CRUSH rule 0 x 662 [5,2,8]
+ CRUSH rule 0 x 663 [2,5,7]
+ CRUSH rule 0 x 664 [2,5,6]
+ CRUSH rule 0 x 665 [5,6,2]
+ CRUSH rule 0 x 666 [2,7,5]
+ CRUSH rule 0 x 667 [2,5,8]
+ CRUSH rule 0 x 668 [5,7,2]
+ CRUSH rule 0 x 669 [6,5,2]
+ CRUSH rule 0 x 670 [5,2,6]
+ CRUSH rule 0 x 671 [2,8,5]
+ CRUSH rule 0 x 672 [5,2,8]
+ CRUSH rule 0 x 673 [5,2,7]
+ CRUSH rule 0 x 674 [5,2,7]
+ CRUSH rule 0 x 675 [2,8,5]
+ CRUSH rule 0 x 676 [2,5,7]
+ CRUSH rule 0 x 677 [5,2,6]
+ CRUSH rule 0 x 678 [2,5,8]
+ CRUSH rule 0 x 679 [6,2,5]
+ CRUSH rule 0 x 680 [2,5,8]
+ CRUSH rule 0 x 681 [5,6,2]
+ CRUSH rule 0 x 682 [2,5,8]
+ CRUSH rule 0 x 683 [2,5,8]
+ CRUSH rule 0 x 684 [7,2,5]
+ CRUSH rule 0 x 685 [7,2,5]
+ CRUSH rule 0 x 686 [2,5,8]
+ CRUSH rule 0 x 687 [5,7,2]
+ CRUSH rule 0 x 688 [5,6,2]
+ CRUSH rule 0 x 689 [6,5,2]
+ CRUSH rule 0 x 690 [8,2,5]
+ CRUSH rule 0 x 691 [5,2,6]
+ CRUSH rule 0 x 692 [7,2,5]
+ CRUSH rule 0 x 693 [6,5,2]
+ CRUSH rule 0 x 694 [6,5,2]
+ CRUSH rule 0 x 695 [2,6,5]
+ CRUSH rule 0 x 696 [2,5,8]
+ CRUSH rule 0 x 697 [6,2,5]
+ CRUSH rule 0 x 698 [6,2,5]
+ CRUSH rule 0 x 699 [2,8,5]
+ CRUSH rule 0 x 700 [2,5,6]
+ CRUSH rule 0 x 701 [5,2,6]
+ CRUSH rule 0 x 702 [5,2,8]
+ CRUSH rule 0 x 703 [8,5,2]
+ CRUSH rule 0 x 704 [2,5,6]
+ CRUSH rule 0 x 705 [8,2,5]
+ CRUSH rule 0 x 706 [2,5,8]
+ CRUSH rule 0 x 707 [7,5,2]
+ CRUSH rule 0 x 708 [5,7,2]
+ CRUSH rule 0 x 709 [6,5,2]
+ CRUSH rule 0 x 710 [8,5,2]
+ CRUSH rule 0 x 711 [2,5,7]
+ CRUSH rule 0 x 712 [2,5,6]
+ CRUSH rule 0 x 713 [6,5,2]
+ CRUSH rule 0 x 714 [5,2,6]
+ CRUSH rule 0 x 715 [2,5,6]
+ CRUSH rule 0 x 716 [5,6,2]
+ CRUSH rule 0 x 717 [8,2,5]
+ CRUSH rule 0 x 718 [5,7,2]
+ CRUSH rule 0 x 719 [2,6,5]
+ CRUSH rule 0 x 720 [6,2,5]
+ CRUSH rule 0 x 721 [5,7,2]
+ CRUSH rule 0 x 722 [5,7,2]
+ CRUSH rule 0 x 723 [5,2,7]
+ CRUSH rule 0 x 724 [2,7,5]
+ CRUSH rule 0 x 725 [2,5,6]
+ CRUSH rule 0 x 726 [5,7,2]
+ CRUSH rule 0 x 727 [5,7,2]
+ CRUSH rule 0 x 728 [2,6,5]
+ CRUSH rule 0 x 729 [5,6,2]
+ CRUSH rule 0 x 730 [5,8,2]
+ CRUSH rule 0 x 731 [5,2,6]
+ CRUSH rule 0 x 732 [2,5,8]
+ CRUSH rule 0 x 733 [5,6,2]
+ CRUSH rule 0 x 734 [6,5,2]
+ CRUSH rule 0 x 735 [5,6,2]
+ CRUSH rule 0 x 736 [5,8,2]
+ CRUSH rule 0 x 737 [2,8,5]
+ CRUSH rule 0 x 738 [5,2,6]
+ CRUSH rule 0 x 739 [2,7,5]
+ CRUSH rule 0 x 740 [2,7,5]
+ CRUSH rule 0 x 741 [7,2,5]
+ CRUSH rule 0 x 742 [8,2,5]
+ CRUSH rule 0 x 743 [7,2,5]
+ CRUSH rule 0 x 744 [5,6,2]
+ CRUSH rule 0 x 745 [5,2,8]
+ CRUSH rule 0 x 746 [5,2,7]
+ CRUSH rule 0 x 747 [6,2,5]
+ CRUSH rule 0 x 748 [2,6,5]
+ CRUSH rule 0 x 749 [5,7,2]
+ CRUSH rule 0 x 750 [2,7,5]
+ CRUSH rule 0 x 751 [2,7,5]
+ CRUSH rule 0 x 752 [8,2,5]
+ CRUSH rule 0 x 753 [7,5,2]
+ CRUSH rule 0 x 754 [8,5,2]
+ CRUSH rule 0 x 755 [2,6,5]
+ CRUSH rule 0 x 756 [5,8,2]
+ CRUSH rule 0 x 757 [8,2,5]
+ CRUSH rule 0 x 758 [6,2,5]
+ CRUSH rule 0 x 759 [8,5,2]
+ CRUSH rule 0 x 760 [2,5,7]
+ CRUSH rule 0 x 761 [5,2,6]
+ CRUSH rule 0 x 762 [2,8,5]
+ CRUSH rule 0 x 763 [8,5,2]
+ CRUSH rule 0 x 764 [2,7,5]
+ CRUSH rule 0 x 765 [6,5,2]
+ CRUSH rule 0 x 766 [8,5,2]
+ CRUSH rule 0 x 767 [2,8,5]
+ CRUSH rule 0 x 768 [8,5,2]
+ CRUSH rule 0 x 769 [6,2,5]
+ CRUSH rule 0 x 770 [6,2,5]
+ CRUSH rule 0 x 771 [7,2,5]
+ CRUSH rule 0 x 772 [8,5,2]
+ CRUSH rule 0 x 773 [5,2,6]
+ CRUSH rule 0 x 774 [5,7,2]
+ CRUSH rule 0 x 775 [6,5,2]
+ CRUSH rule 0 x 776 [7,2,5]
+ CRUSH rule 0 x 777 [5,2,6]
+ CRUSH rule 0 x 778 [2,6,5]
+ CRUSH rule 0 x 779 [2,6,5]
+ CRUSH rule 0 x 780 [2,5,8]
+ CRUSH rule 0 x 781 [6,5,2]
+ CRUSH rule 0 x 782 [5,2,8]
+ CRUSH rule 0 x 783 [7,2,5]
+ CRUSH rule 0 x 784 [2,5,8]
+ CRUSH rule 0 x 785 [6,2,5]
+ CRUSH rule 0 x 786 [7,5,2]
+ CRUSH rule 0 x 787 [2,8,5]
+ CRUSH rule 0 x 788 [6,2,5]
+ CRUSH rule 0 x 789 [2,5,8]
+ CRUSH rule 0 x 790 [8,5,2]
+ CRUSH rule 0 x 791 [5,6,2]
+ CRUSH rule 0 x 792 [5,6,2]
+ CRUSH rule 0 x 793 [6,2,5]
+ CRUSH rule 0 x 794 [2,8,5]
+ CRUSH rule 0 x 795 [2,5,6]
+ CRUSH rule 0 x 796 [5,7,2]
+ CRUSH rule 0 x 797 [2,5,6]
+ CRUSH rule 0 x 798 [6,2,5]
+ CRUSH rule 0 x 799 [5,2,7]
+ CRUSH rule 0 x 800 [5,2,8]
+ CRUSH rule 0 x 801 [5,7,2]
+ CRUSH rule 0 x 802 [2,6,5]
+ CRUSH rule 0 x 803 [2,5,7]
+ CRUSH rule 0 x 804 [6,2,5]
+ CRUSH rule 0 x 805 [5,8,2]
+ CRUSH rule 0 x 806 [2,5,6]
+ CRUSH rule 0 x 807 [5,7,2]
+ CRUSH rule 0 x 808 [5,7,2]
+ CRUSH rule 0 x 809 [2,5,6]
+ CRUSH rule 0 x 810 [5,7,2]
+ CRUSH rule 0 x 811 [8,5,2]
+ CRUSH rule 0 x 812 [8,5,2]
+ CRUSH rule 0 x 813 [6,5,2]
+ CRUSH rule 0 x 814 [5,8,2]
+ CRUSH rule 0 x 815 [5,2,6]
+ CRUSH rule 0 x 816 [2,6,5]
+ CRUSH rule 0 x 817 [5,6,2]
+ CRUSH rule 0 x 818 [5,2,7]
+ CRUSH rule 0 x 819 [5,2,7]
+ CRUSH rule 0 x 820 [5,7,2]
+ CRUSH rule 0 x 821 [5,8,2]
+ CRUSH rule 0 x 822 [2,5,7]
+ CRUSH rule 0 x 823 [5,7,2]
+ CRUSH rule 0 x 824 [5,7,2]
+ CRUSH rule 0 x 825 [2,8,5]
+ CRUSH rule 0 x 826 [7,2,5]
+ CRUSH rule 0 x 827 [2,6,5]
+ CRUSH rule 0 x 828 [2,5,8]
+ CRUSH rule 0 x 829 [5,6,2]
+ CRUSH rule 0 x 830 [2,5,7]
+ CRUSH rule 0 x 831 [2,6,5]
+ CRUSH rule 0 x 832 [5,8,2]
+ CRUSH rule 0 x 833 [2,6,5]
+ CRUSH rule 0 x 834 [5,2,8]
+ CRUSH rule 0 x 835 [8,5,2]
+ CRUSH rule 0 x 836 [5,8,2]
+ CRUSH rule 0 x 837 [6,5,2]
+ CRUSH rule 0 x 838 [6,2,5]
+ CRUSH rule 0 x 839 [5,2,8]
+ CRUSH rule 0 x 840 [7,5,2]
+ CRUSH rule 0 x 841 [5,8,2]
+ CRUSH rule 0 x 842 [2,5,8]
+ CRUSH rule 0 x 843 [6,5,2]
+ CRUSH rule 0 x 844 [5,8,2]
+ CRUSH rule 0 x 845 [5,6,2]
+ CRUSH rule 0 x 846 [5,2,6]
+ CRUSH rule 0 x 847 [2,8,5]
+ CRUSH rule 0 x 848 [2,6,5]
+ CRUSH rule 0 x 849 [5,8,2]
+ CRUSH rule 0 x 850 [2,5,7]
+ CRUSH rule 0 x 851 [6,5,2]
+ CRUSH rule 0 x 852 [7,5,2]
+ CRUSH rule 0 x 853 [6,2,5]
+ CRUSH rule 0 x 854 [7,2,5]
+ CRUSH rule 0 x 855 [5,7,2]
+ CRUSH rule 0 x 856 [6,5,2]
+ CRUSH rule 0 x 857 [8,5,2]
+ CRUSH rule 0 x 858 [6,5,2]
+ CRUSH rule 0 x 859 [6,2,5]
+ CRUSH rule 0 x 860 [5,2,8]
+ CRUSH rule 0 x 861 [8,5,2]
+ CRUSH rule 0 x 862 [6,2,5]
+ CRUSH rule 0 x 863 [8,2,5]
+ CRUSH rule 0 x 864 [5,6,2]
+ CRUSH rule 0 x 865 [8,2,5]
+ CRUSH rule 0 x 866 [5,6,2]
+ CRUSH rule 0 x 867 [6,5,2]
+ CRUSH rule 0 x 868 [6,5,2]
+ CRUSH rule 0 x 869 [8,5,2]
+ CRUSH rule 0 x 870 [2,5,7]
+ CRUSH rule 0 x 871 [5,2,6]
+ CRUSH rule 0 x 872 [5,2,6]
+ CRUSH rule 0 x 873 [5,6,2]
+ CRUSH rule 0 x 874 [2,6,5]
+ CRUSH rule 0 x 875 [2,6,5]
+ CRUSH rule 0 x 876 [5,8,2]
+ CRUSH rule 0 x 877 [6,5,2]
+ CRUSH rule 0 x 878 [5,2,7]
+ CRUSH rule 0 x 879 [7,5,2]
+ CRUSH rule 0 x 880 [5,2,7]
+ CRUSH rule 0 x 881 [5,8,2]
+ CRUSH rule 0 x 882 [5,2,6]
+ CRUSH rule 0 x 883 [2,5,6]
+ CRUSH rule 0 x 884 [6,2,5]
+ CRUSH rule 0 x 885 [5,2,6]
+ CRUSH rule 0 x 886 [5,7,2]
+ CRUSH rule 0 x 887 [7,5,2]
+ CRUSH rule 0 x 888 [6,2,5]
+ CRUSH rule 0 x 889 [2,8,5]
+ CRUSH rule 0 x 890 [7,2,5]
+ CRUSH rule 0 x 891 [2,7,5]
+ CRUSH rule 0 x 892 [6,2,5]
+ CRUSH rule 0 x 893 [2,5,6]
+ CRUSH rule 0 x 894 [7,5,2]
+ CRUSH rule 0 x 895 [5,2,6]
+ CRUSH rule 0 x 896 [2,6,5]
+ CRUSH rule 0 x 897 [5,2,8]
+ CRUSH rule 0 x 898 [2,5,8]
+ CRUSH rule 0 x 899 [2,6,5]
+ CRUSH rule 0 x 900 [5,2,7]
+ CRUSH rule 0 x 901 [5,2,6]
+ CRUSH rule 0 x 902 [8,5,2]
+ CRUSH rule 0 x 903 [5,6,2]
+ CRUSH rule 0 x 904 [5,7,2]
+ CRUSH rule 0 x 905 [6,2,5]
+ CRUSH rule 0 x 906 [2,7,5]
+ CRUSH rule 0 x 907 [7,2,5]
+ CRUSH rule 0 x 908 [5,6,2]
+ CRUSH rule 0 x 909 [2,5,8]
+ CRUSH rule 0 x 910 [6,5,2]
+ CRUSH rule 0 x 911 [5,7,2]
+ CRUSH rule 0 x 912 [2,8,5]
+ CRUSH rule 0 x 913 [7,2,5]
+ CRUSH rule 0 x 914 [6,5,2]
+ CRUSH rule 0 x 915 [8,2,5]
+ CRUSH rule 0 x 916 [5,2,6]
+ CRUSH rule 0 x 917 [2,5,6]
+ CRUSH rule 0 x 918 [8,2,5]
+ CRUSH rule 0 x 919 [6,2,5]
+ CRUSH rule 0 x 920 [7,5,2]
+ CRUSH rule 0 x 921 [2,5,8]
+ CRUSH rule 0 x 922 [6,5,2]
+ CRUSH rule 0 x 923 [5,6,2]
+ CRUSH rule 0 x 924 [5,2,8]
+ CRUSH rule 0 x 925 [5,6,2]
+ CRUSH rule 0 x 926 [5,2,7]
+ CRUSH rule 0 x 927 [2,6,5]
+ CRUSH rule 0 x 928 [8,2,5]
+ CRUSH rule 0 x 929 [5,2,6]
+ CRUSH rule 0 x 930 [2,5,8]
+ CRUSH rule 0 x 931 [5,2,8]
+ CRUSH rule 0 x 932 [5,2,7]
+ CRUSH rule 0 x 933 [8,5,2]
+ CRUSH rule 0 x 934 [5,8,2]
+ CRUSH rule 0 x 935 [6,5,2]
+ CRUSH rule 0 x 936 [2,7,5]
+ CRUSH rule 0 x 937 [5,8,2]
+ CRUSH rule 0 x 938 [6,5,2]
+ CRUSH rule 0 x 939 [2,8,5]
+ CRUSH rule 0 x 940 [8,5,2]
+ CRUSH rule 0 x 941 [5,2,6]
+ CRUSH rule 0 x 942 [2,6,5]
+ CRUSH rule 0 x 943 [8,2,5]
+ CRUSH rule 0 x 944 [5,8,2]
+ CRUSH rule 0 x 945 [7,2,5]
+ CRUSH rule 0 x 946 [2,8,5]
+ CRUSH rule 0 x 947 [5,2,6]
+ CRUSH rule 0 x 948 [7,5,2]
+ CRUSH rule 0 x 949 [6,2,5]
+ CRUSH rule 0 x 950 [5,7,2]
+ CRUSH rule 0 x 951 [5,6,2]
+ CRUSH rule 0 x 952 [2,7,5]
+ CRUSH rule 0 x 953 [2,5,6]
+ CRUSH rule 0 x 954 [5,2,8]
+ CRUSH rule 0 x 955 [8,2,5]
+ CRUSH rule 0 x 956 [2,7,5]
+ CRUSH rule 0 x 957 [7,2,5]
+ CRUSH rule 0 x 958 [8,5,2]
+ CRUSH rule 0 x 959 [5,2,6]
+ CRUSH rule 0 x 960 [5,6,2]
+ CRUSH rule 0 x 961 [5,2,6]
+ CRUSH rule 0 x 962 [7,5,2]
+ CRUSH rule 0 x 963 [2,5,8]
+ CRUSH rule 0 x 964 [5,2,7]
+ CRUSH rule 0 x 965 [7,5,2]
+ CRUSH rule 0 x 966 [5,6,2]
+ CRUSH rule 0 x 967 [8,5,2]
+ CRUSH rule 0 x 968 [7,2,5]
+ CRUSH rule 0 x 969 [8,2,5]
+ CRUSH rule 0 x 970 [2,8,5]
+ CRUSH rule 0 x 971 [2,8,5]
+ CRUSH rule 0 x 972 [2,7,5]
+ CRUSH rule 0 x 973 [2,8,5]
+ CRUSH rule 0 x 974 [5,2,7]
+ CRUSH rule 0 x 975 [5,8,2]
+ CRUSH rule 0 x 976 [5,7,2]
+ CRUSH rule 0 x 977 [8,5,2]
+ CRUSH rule 0 x 978 [7,2,5]
+ CRUSH rule 0 x 979 [7,2,5]
+ CRUSH rule 0 x 980 [6,2,5]
+ CRUSH rule 0 x 981 [7,5,2]
+ CRUSH rule 0 x 982 [5,2,8]
+ CRUSH rule 0 x 983 [5,6,2]
+ CRUSH rule 0 x 984 [2,8,5]
+ CRUSH rule 0 x 985 [2,5,8]
+ CRUSH rule 0 x 986 [8,5,2]
+ CRUSH rule 0 x 987 [2,5,8]
+ CRUSH rule 0 x 988 [2,5,6]
+ CRUSH rule 0 x 989 [2,8,5]
+ CRUSH rule 0 x 990 [2,6,5]
+ CRUSH rule 0 x 991 [2,5,8]
+ CRUSH rule 0 x 992 [7,2,5]
+ CRUSH rule 0 x 993 [2,6,5]
+ CRUSH rule 0 x 994 [5,6,2]
+ CRUSH rule 0 x 995 [7,2,5]
+ CRUSH rule 0 x 996 [6,5,2]
+ CRUSH rule 0 x 997 [6,5,2]
+ CRUSH rule 0 x 998 [8,2,5]
+ CRUSH rule 0 x 999 [2,7,5]
+ CRUSH rule 0 x 1000 [8,5,2]
+ CRUSH rule 0 x 1001 [2,5,6]
+ CRUSH rule 0 x 1002 [2,5,8]
+ CRUSH rule 0 x 1003 [2,7,5]
+ CRUSH rule 0 x 1004 [6,2,5]
+ CRUSH rule 0 x 1005 [6,2,5]
+ CRUSH rule 0 x 1006 [2,8,5]
+ CRUSH rule 0 x 1007 [2,5,8]
+ CRUSH rule 0 x 1008 [2,7,5]
+ CRUSH rule 0 x 1009 [6,5,2]
+ CRUSH rule 0 x 1010 [5,2,6]
+ CRUSH rule 0 x 1011 [5,2,8]
+ CRUSH rule 0 x 1012 [5,2,8]
+ CRUSH rule 0 x 1013 [5,2,8]
+ CRUSH rule 0 x 1014 [2,8,5]
+ CRUSH rule 0 x 1015 [6,5,2]
+ CRUSH rule 0 x 1016 [2,5,6]
+ CRUSH rule 0 x 1017 [6,2,5]
+ CRUSH rule 0 x 1018 [5,2,6]
+ CRUSH rule 0 x 1019 [5,8,2]
+ CRUSH rule 0 x 1020 [5,2,7]
+ CRUSH rule 0 x 1021 [5,2,7]
+ CRUSH rule 0 x 1022 [2,7,5]
+ CRUSH rule 0 x 1023 [5,2,7]
+ rule 0 (choose) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 1 (choose-two), x = 0..1023, numrep = 2..3
+ CRUSH rule 1 x 0 [2,8]
+ CRUSH rule 1 x 1 [2,8]
+ CRUSH rule 1 x 2 [2,5]
+ CRUSH rule 1 x 3 [8,2]
+ CRUSH rule 1 x 4 [5,7]
+ CRUSH rule 1 x 5 [7,8]
+ CRUSH rule 1 x 6 [2,6]
+ CRUSH rule 1 x 7 [5,7]
+ CRUSH rule 1 x 8 [5,2]
+ CRUSH rule 1 x 9 [2,5]
+ CRUSH rule 1 x 10 [2,8]
+ CRUSH rule 1 x 11 [2,7]
+ CRUSH rule 1 x 12 [2,7]
+ CRUSH rule 1 x 13 [5,8]
+ CRUSH rule 1 x 14 [7,6]
+ CRUSH rule 1 x 15 [7,2]
+ CRUSH rule 1 x 16 [5,6]
+ CRUSH rule 1 x 17 [5,7]
+ CRUSH rule 1 x 18 [2,5]
+ CRUSH rule 1 x 19 [7,5]
+ CRUSH rule 1 x 20 [2,5]
+ CRUSH rule 1 x 21 [5,7]
+ CRUSH rule 1 x 22 [8,5]
+ CRUSH rule 1 x 23 [5,6]
+ CRUSH rule 1 x 24 [2,8]
+ CRUSH rule 1 x 25 [5,7]
+ CRUSH rule 1 x 26 [2,8]
+ CRUSH rule 1 x 27 [5,2]
+ CRUSH rule 1 x 28 [6,2]
+ CRUSH rule 1 x 29 [8,5]
+ CRUSH rule 1 x 30 [5,7]
+ CRUSH rule 1 x 31 [8,7]
+ CRUSH rule 1 x 32 [5,6]
+ CRUSH rule 1 x 33 [2,7]
+ CRUSH rule 1 x 34 [2,5]
+ CRUSH rule 1 x 35 [2,8]
+ CRUSH rule 1 x 36 [5,8]
+ CRUSH rule 1 x 37 [2,5]
+ CRUSH rule 1 x 38 [5,8]
+ CRUSH rule 1 x 39 [5,7]
+ CRUSH rule 1 x 40 [7,8]
+ CRUSH rule 1 x 41 [2,6]
+ CRUSH rule 1 x 42 [5,2]
+ CRUSH rule 1 x 43 [2,5]
+ CRUSH rule 1 x 44 [2,6]
+ CRUSH rule 1 x 45 [8,2]
+ CRUSH rule 1 x 46 [2,5]
+ CRUSH rule 1 x 47 [5,2]
+ CRUSH rule 1 x 48 [5,6]
+ CRUSH rule 1 x 49 [5,2]
+ CRUSH rule 1 x 50 [5,6]
+ CRUSH rule 1 x 51 [5,6]
+ CRUSH rule 1 x 52 [8,6]
+ CRUSH rule 1 x 53 [5,2]
+ CRUSH rule 1 x 54 [7,6]
+ CRUSH rule 1 x 55 [8,7]
+ CRUSH rule 1 x 56 [6,5]
+ CRUSH rule 1 x 57 [5,2]
+ CRUSH rule 1 x 58 [2,5]
+ CRUSH rule 1 x 59 [5,2]
+ CRUSH rule 1 x 60 [5,7]
+ CRUSH rule 1 x 61 [5,6]
+ CRUSH rule 1 x 62 [7,2]
+ CRUSH rule 1 x 63 [5,6]
+ CRUSH rule 1 x 64 [5,2]
+ CRUSH rule 1 x 65 [7,5]
+ CRUSH rule 1 x 66 [5,2]
+ CRUSH rule 1 x 67 [5,2]
+ CRUSH rule 1 x 68 [2,5]
+ CRUSH rule 1 x 69 [5,2]
+ CRUSH rule 1 x 70 [7,2]
+ CRUSH rule 1 x 71 [2,8]
+ CRUSH rule 1 x 72 [6,2]
+ CRUSH rule 1 x 73 [2,7]
+ CRUSH rule 1 x 74 [2,7]
+ CRUSH rule 1 x 75 [5,2]
+ CRUSH rule 1 x 76 [5,2]
+ CRUSH rule 1 x 77 [7,2]
+ CRUSH rule 1 x 78 [2,5]
+ CRUSH rule 1 x 79 [5,2]
+ CRUSH rule 1 x 80 [2,5]
+ CRUSH rule 1 x 81 [2]
+ CRUSH rule 1 x 82 [7,2]
+ CRUSH rule 1 x 83 [2,6]
+ CRUSH rule 1 x 84 [7,2]
+ CRUSH rule 1 x 85 [5,8]
+ CRUSH rule 1 x 86 [2,7]
+ CRUSH rule 1 x 87 [2,7]
+ CRUSH rule 1 x 88 [2,6]
+ CRUSH rule 1 x 89 [5,2]
+ CRUSH rule 1 x 90 [6,7]
+ CRUSH rule 1 x 91 [5,8]
+ CRUSH rule 1 x 92 [2,8]
+ CRUSH rule 1 x 93 [7,5]
+ CRUSH rule 1 x 94 [2,5]
+ CRUSH rule 1 x 95 [7,5]
+ CRUSH rule 1 x 96 [5,6]
+ CRUSH rule 1 x 97 [8,7]
+ CRUSH rule 1 x 98 [2,6]
+ CRUSH rule 1 x 99 [2,7]
+ CRUSH rule 1 x 100 [2,7]
+ CRUSH rule 1 x 101 [5,7]
+ CRUSH rule 1 x 102 [5,2]
+ CRUSH rule 1 x 103 [5,7]
+ CRUSH rule 1 x 104 [7,5]
+ CRUSH rule 1 x 105 [2,5]
+ CRUSH rule 1 x 106 [2,6]
+ CRUSH rule 1 x 107 [5,2]
+ CRUSH rule 1 x 108 [7,2]
+ CRUSH rule 1 x 109 [2,5]
+ CRUSH rule 1 x 110 [5,2]
+ CRUSH rule 1 x 111 [2,5]
+ CRUSH rule 1 x 112 [2,5]
+ CRUSH rule 1 x 113 [6,2]
+ CRUSH rule 1 x 114 [7,6]
+ CRUSH rule 1 x 115 [8,2]
+ CRUSH rule 1 x 116 [2,6]
+ CRUSH rule 1 x 117 [7,5]
+ CRUSH rule 1 x 118 [2,5]
+ CRUSH rule 1 x 119 [5,6]
+ CRUSH rule 1 x 120 [2,5]
+ CRUSH rule 1 x 121 [2,5]
+ CRUSH rule 1 x 122 [8,5]
+ CRUSH rule 1 x 123 [2,5]
+ CRUSH rule 1 x 124 [5,8]
+ CRUSH rule 1 x 125 [2,7]
+ CRUSH rule 1 x 126 [5,2]
+ CRUSH rule 1 x 127 [5,6]
+ CRUSH rule 1 x 128 [5,8]
+ CRUSH rule 1 x 129 [2,7]
+ CRUSH rule 1 x 130 [5,8]
+ CRUSH rule 1 x 131 [2,8]
+ CRUSH rule 1 x 132 [2,7]
+ CRUSH rule 1 x 133 [5,6]
+ CRUSH rule 1 x 134 [2,8]
+ CRUSH rule 1 x 135 [5,6]
+ CRUSH rule 1 x 136 [2,5]
+ CRUSH rule 1 x 137 [7,5]
+ CRUSH rule 1 x 138 [8,7]
+ CRUSH rule 1 x 139 [5,2]
+ CRUSH rule 1 x 140 [2,6]
+ CRUSH rule 1 x 141 [6,8]
+ CRUSH rule 1 x 142 [5,6]
+ CRUSH rule 1 x 143 [5,8]
+ CRUSH rule 1 x 144 [8,2]
+ CRUSH rule 1 x 145 [8,5]
+ CRUSH rule 1 x 146 [2,6]
+ CRUSH rule 1 x 147 [2,8]
+ CRUSH rule 1 x 148 [5,2]
+ CRUSH rule 1 x 149 [5,8]
+ CRUSH rule 1 x 150 [2,6]
+ CRUSH rule 1 x 151 [5,2]
+ CRUSH rule 1 x 152 [8,5]
+ CRUSH rule 1 x 153 [8,6]
+ CRUSH rule 1 x 154 [5,2]
+ CRUSH rule 1 x 155 [5,7]
+ CRUSH rule 1 x 156 [5,8]
+ CRUSH rule 1 x 157 [5,2]
+ CRUSH rule 1 x 158 [2,8]
+ CRUSH rule 1 x 159 [7,2]
+ CRUSH rule 1 x 160 [2,8]
+ CRUSH rule 1 x 161 [2,5]
+ CRUSH rule 1 x 162 [2,6]
+ CRUSH rule 1 x 163 [5,6]
+ CRUSH rule 1 x 164 [7,8]
+ CRUSH rule 1 x 165 [7,2]
+ CRUSH rule 1 x 166 [2,5]
+ CRUSH rule 1 x 167 [2,6]
+ CRUSH rule 1 x 168 [5,2]
+ CRUSH rule 1 x 169 [2,6]
+ CRUSH rule 1 x 170 [2,8]
+ CRUSH rule 1 x 171 [7,5]
+ CRUSH rule 1 x 172 [2,7]
+ CRUSH rule 1 x 173 [8,5]
+ CRUSH rule 1 x 174 [2,5]
+ CRUSH rule 1 x 175 [6,2]
+ CRUSH rule 1 x 176 [5,2]
+ CRUSH rule 1 x 177 [5,8]
+ CRUSH rule 1 x 178 [5,2]
+ CRUSH rule 1 x 179 [5,2]
+ CRUSH rule 1 x 180 [5,8]
+ CRUSH rule 1 x 181 [6,2]
+ CRUSH rule 1 x 182 [8,5]
+ CRUSH rule 1 x 183 [7,8]
+ CRUSH rule 1 x 184 [5,7]
+ CRUSH rule 1 x 185 [6,8]
+ CRUSH rule 1 x 186 [2,8]
+ CRUSH rule 1 x 187 [2,6]
+ CRUSH rule 1 x 188 [2,8]
+ CRUSH rule 1 x 189 [2,7]
+ CRUSH rule 1 x 190 [5,2]
+ CRUSH rule 1 x 191 [7,6]
+ CRUSH rule 1 x 192 [5,2]
+ CRUSH rule 1 x 193 [5,2]
+ CRUSH rule 1 x 194 [2,5]
+ CRUSH rule 1 x 195 [6,5]
+ CRUSH rule 1 x 196 [6,7]
+ CRUSH rule 1 x 197 [6,5]
+ CRUSH rule 1 x 198 [2,5]
+ CRUSH rule 1 x 199 [2,5]
+ CRUSH rule 1 x 200 [2,5]
+ CRUSH rule 1 x 201 [7,2]
+ CRUSH rule 1 x 202 [6,5]
+ CRUSH rule 1 x 203 [5,8]
+ CRUSH rule 1 x 204 [2,5]
+ CRUSH rule 1 x 205 [2,7]
+ CRUSH rule 1 x 206 [2,5]
+ CRUSH rule 1 x 207 [5,2]
+ CRUSH rule 1 x 208 [7,2]
+ CRUSH rule 1 x 209 [2,5]
+ CRUSH rule 1 x 210 [2,8]
+ CRUSH rule 1 x 211 [5,6]
+ CRUSH rule 1 x 212 [7,5]
+ CRUSH rule 1 x 213 [8,5]
+ CRUSH rule 1 x 214 [5,7]
+ CRUSH rule 1 x 215 [8,2]
+ CRUSH rule 1 x 216 [5,2]
+ CRUSH rule 1 x 217 [2,5]
+ CRUSH rule 1 x 218 [2,7]
+ CRUSH rule 1 x 219 [5,8]
+ CRUSH rule 1 x 220 [5,7]
+ CRUSH rule 1 x 221 [5,6]
+ CRUSH rule 1 x 222 [6,8]
+ CRUSH rule 1 x 223 [2,5]
+ CRUSH rule 1 x 224 [2,5]
+ CRUSH rule 1 x 225 [8,6]
+ CRUSH rule 1 x 226 [7,2]
+ CRUSH rule 1 x 227 [5,6]
+ CRUSH rule 1 x 228 [5,6]
+ CRUSH rule 1 x 229 [5,2]
+ CRUSH rule 1 x 230 [5,7]
+ CRUSH rule 1 x 231 [5,6]
+ CRUSH rule 1 x 232 [2,7]
+ CRUSH rule 1 x 233 [5,8]
+ CRUSH rule 1 x 234 [2,7]
+ CRUSH rule 1 x 235 [5,8]
+ CRUSH rule 1 x 236 [5,2]
+ CRUSH rule 1 x 237 [5,7]
+ CRUSH rule 1 x 238 [5,6]
+ CRUSH rule 1 x 239 [8,7]
+ CRUSH rule 1 x 240 [5,7]
+ CRUSH rule 1 x 241 [5,2]
+ CRUSH rule 1 x 242 [5,8]
+ CRUSH rule 1 x 243 [5,7]
+ CRUSH rule 1 x 244 [5,6]
+ CRUSH rule 1 x 245 [7,6]
+ CRUSH rule 1 x 246 [2,5]
+ CRUSH rule 1 x 247 [6,2]
+ CRUSH rule 1 x 248 [8,2]
+ CRUSH rule 1 x 249 [2,5]
+ CRUSH rule 1 x 250 [2,7]
+ CRUSH rule 1 x 251 [2,5]
+ CRUSH rule 1 x 252 [5,7]
+ CRUSH rule 1 x 253 [5,2]
+ CRUSH rule 1 x 254 [5,6]
+ CRUSH rule 1 x 255 [2,7]
+ CRUSH rule 1 x 256 [5,7]
+ CRUSH rule 1 x 257 [2,8]
+ CRUSH rule 1 x 258 [5]
+ CRUSH rule 1 x 259 [5,7]
+ CRUSH rule 1 x 260 [5,6]
+ CRUSH rule 1 x 261 [8,7]
+ CRUSH rule 1 x 262 [5,2]
+ CRUSH rule 1 x 263 [6,8]
+ CRUSH rule 1 x 264 [5,6]
+ CRUSH rule 1 x 265 [8,6]
+ CRUSH rule 1 x 266 [8,2]
+ CRUSH rule 1 x 267 [2,5]
+ CRUSH rule 1 x 268 [2,7]
+ CRUSH rule 1 x 269 [2,8]
+ CRUSH rule 1 x 270 [5,2]
+ CRUSH rule 1 x 271 [7,5]
+ CRUSH rule 1 x 272 [2,8]
+ CRUSH rule 1 x 273 [5,2]
+ CRUSH rule 1 x 274 [6,8]
+ CRUSH rule 1 x 275 [5,8]
+ CRUSH rule 1 x 276 [7,2]
+ CRUSH rule 1 x 277 [6,5]
+ CRUSH rule 1 x 278 [6,8]
+ CRUSH rule 1 x 279 [8,5]
+ CRUSH rule 1 x 280 [2,6]
+ CRUSH rule 1 x 281 [8,2]
+ CRUSH rule 1 x 282 [5,2]
+ CRUSH rule 1 x 283 [8,2]
+ CRUSH rule 1 x 284 [6,5]
+ CRUSH rule 1 x 285 [5,6]
+ CRUSH rule 1 x 286 [2,5]
+ CRUSH rule 1 x 287 [2,5]
+ CRUSH rule 1 x 288 [8,2]
+ CRUSH rule 1 x 289 [5,6]
+ CRUSH rule 1 x 290 [2,5]
+ CRUSH rule 1 x 291 [2,5]
+ CRUSH rule 1 x 292 [8,2]
+ CRUSH rule 1 x 293 [6,2]
+ CRUSH rule 1 x 294 [7,5]
+ CRUSH rule 1 x 295 [5,8]
+ CRUSH rule 1 x 296 [5,2]
+ CRUSH rule 1 x 297 [6,2]
+ CRUSH rule 1 x 298 [2,8]
+ CRUSH rule 1 x 299 [2,5]
+ CRUSH rule 1 x 300 [8,7]
+ CRUSH rule 1 x 301 [2,8]
+ CRUSH rule 1 x 302 [5,2]
+ CRUSH rule 1 x 303 [7,5]
+ CRUSH rule 1 x 304 [2,7]
+ CRUSH rule 1 x 305 [5,8]
+ CRUSH rule 1 x 306 [2,7]
+ CRUSH rule 1 x 307 [2,7]
+ CRUSH rule 1 x 308 [2,8]
+ CRUSH rule 1 x 309 [7,5]
+ CRUSH rule 1 x 310 [5,6]
+ CRUSH rule 1 x 311 [5,2]
+ CRUSH rule 1 x 312 [2,6]
+ CRUSH rule 1 x 313 [5,8]
+ CRUSH rule 1 x 314 [5,2]
+ CRUSH rule 1 x 315 [2,5]
+ CRUSH rule 1 x 316 [6,5]
+ CRUSH rule 1 x 317 [2,6]
+ CRUSH rule 1 x 318 [8,6]
+ CRUSH rule 1 x 319 [5,2]
+ CRUSH rule 1 x 320 [5,7]
+ CRUSH rule 1 x 321 [2,5]
+ CRUSH rule 1 x 322 [2,7]
+ CRUSH rule 1 x 323 [5,7]
+ CRUSH rule 1 x 324 [7,2]
+ CRUSH rule 1 x 325 [5,6]
+ CRUSH rule 1 x 326 [5,6]
+ CRUSH rule 1 x 327 [2,6]
+ CRUSH rule 1 x 328 [7,5]
+ CRUSH rule 1 x 329 [5,6]
+ CRUSH rule 1 x 330 [5,7]
+ CRUSH rule 1 x 331 [2,6]
+ CRUSH rule 1 x 332 [2,5]
+ CRUSH rule 1 x 333 [6,8]
+ CRUSH rule 1 x 334 [8,5]
+ CRUSH rule 1 x 335 [7,2]
+ CRUSH rule 1 x 336 [5,6]
+ CRUSH rule 1 x 337 [7,2]
+ CRUSH rule 1 x 338 [5,6]
+ CRUSH rule 1 x 339 [7,5]
+ CRUSH rule 1 x 340 [2,5]
+ CRUSH rule 1 x 341 [5,2]
+ CRUSH rule 1 x 342 [2,7]
+ CRUSH rule 1 x 343 [6,7]
+ CRUSH rule 1 x 344 [6,2]
+ CRUSH rule 1 x 345 [5,2]
+ CRUSH rule 1 x 346 [8,2]
+ CRUSH rule 1 x 347 [5,2]
+ CRUSH rule 1 x 348 [8,2]
+ CRUSH rule 1 x 349 [2,6]
+ CRUSH rule 1 x 350 [8,5]
+ CRUSH rule 1 x 351 [5,6]
+ CRUSH rule 1 x 352 [2,7]
+ CRUSH rule 1 x 353 [6,5]
+ CRUSH rule 1 x 354 [2,5]
+ CRUSH rule 1 x 355 [5,2]
+ CRUSH rule 1 x 356 [5,8]
+ CRUSH rule 1 x 357 [6,2]
+ CRUSH rule 1 x 358 [2,5]
+ CRUSH rule 1 x 359 [6,7]
+ CRUSH rule 1 x 360 [5,2]
+ CRUSH rule 1 x 361 [8,5]
+ CRUSH rule 1 x 362 [5,7]
+ CRUSH rule 1 x 363 [5,2]
+ CRUSH rule 1 x 364 [2,5]
+ CRUSH rule 1 x 365 [6,7]
+ CRUSH rule 1 x 366 [7,2]
+ CRUSH rule 1 x 367 [5,7]
+ CRUSH rule 1 x 368 [7,5]
+ CRUSH rule 1 x 369 [5,7]
+ CRUSH rule 1 x 370 [8,7]
+ CRUSH rule 1 x 371 [2,5]
+ CRUSH rule 1 x 372 [5,2]
+ CRUSH rule 1 x 373 [2,6]
+ CRUSH rule 1 x 374 [5,8]
+ CRUSH rule 1 x 375 [6,5]
+ CRUSH rule 1 x 376 [7,2]
+ CRUSH rule 1 x 377 [2,5]
+ CRUSH rule 1 x 378 [2,6]
+ CRUSH rule 1 x 379 [8,5]
+ CRUSH rule 1 x 380 [2,5]
+ CRUSH rule 1 x 381 [2,5]
+ CRUSH rule 1 x 382 [2,5]
+ CRUSH rule 1 x 383 [5,2]
+ CRUSH rule 1 x 384 [7,2]
+ CRUSH rule 1 x 385 [7,5]
+ CRUSH rule 1 x 386 [2,5]
+ CRUSH rule 1 x 387 [2,5]
+ CRUSH rule 1 x 388 [5,2]
+ CRUSH rule 1 x 389 [2,5]
+ CRUSH rule 1 x 390 [5,6]
+ CRUSH rule 1 x 391 [5,6]
+ CRUSH rule 1 x 392 [2,8]
+ CRUSH rule 1 x 393 [5,2]
+ CRUSH rule 1 x 394 [5,7]
+ CRUSH rule 1 x 395 [5,2]
+ CRUSH rule 1 x 396 [5,2]
+ CRUSH rule 1 x 397 [2,7]
+ CRUSH rule 1 x 398 [2,5]
+ CRUSH rule 1 x 399 [8,7]
+ CRUSH rule 1 x 400 [8,2]
+ CRUSH rule 1 x 401 [2,5]
+ CRUSH rule 1 x 402 [7,8]
+ CRUSH rule 1 x 403 [2,7]
+ CRUSH rule 1 x 404 [5,2]
+ CRUSH rule 1 x 405 [6,5]
+ CRUSH rule 1 x 406 [2,6]
+ CRUSH rule 1 x 407 [2,8]
+ CRUSH rule 1 x 408 [5,2]
+ CRUSH rule 1 x 409 [7,5]
+ CRUSH rule 1 x 410 [8,6]
+ CRUSH rule 1 x 411 [2,6]
+ CRUSH rule 1 x 412 [2,5]
+ CRUSH rule 1 x 413 [5,2]
+ CRUSH rule 1 x 414 [5,2]
+ CRUSH rule 1 x 415 [2,6]
+ CRUSH rule 1 x 416 [2,5]
+ CRUSH rule 1 x 417 [8,7]
+ CRUSH rule 1 x 418 [7,6]
+ CRUSH rule 1 x 419 [8,5]
+ CRUSH rule 1 x 420 [2,5]
+ CRUSH rule 1 x 421 [8,6]
+ CRUSH rule 1 x 422 [6,7]
+ CRUSH rule 1 x 423 [2,5]
+ CRUSH rule 1 x 424 [8,5]
+ CRUSH rule 1 x 425 [2,5]
+ CRUSH rule 1 x 426 [6,7]
+ CRUSH rule 1 x 427 [2,7]
+ CRUSH rule 1 x 428 [5,6]
+ CRUSH rule 1 x 429 [5,6]
+ CRUSH rule 1 x 430 [5,6]
+ CRUSH rule 1 x 431 [5,2]
+ CRUSH rule 1 x 432 [7,2]
+ CRUSH rule 1 x 433 [6,5]
+ CRUSH rule 1 x 434 [5,2]
+ CRUSH rule 1 x 435 [2,5]
+ CRUSH rule 1 x 436 [5,2]
+ CRUSH rule 1 x 437 [7,5]
+ CRUSH rule 1 x 438 [2,5]
+ CRUSH rule 1 x 439 [2,5]
+ CRUSH rule 1 x 440 [2,7]
+ CRUSH rule 1 x 441 [5,7]
+ CRUSH rule 1 x 442 [2,5]
+ CRUSH rule 1 x 443 [6,8]
+ CRUSH rule 1 x 444 [7,2]
+ CRUSH rule 1 x 445 [6,5]
+ CRUSH rule 1 x 446 [5,7]
+ CRUSH rule 1 x 447 [2,5]
+ CRUSH rule 1 x 448 [7,2]
+ CRUSH rule 1 x 449 [7,8]
+ CRUSH rule 1 x 450 [5,8]
+ CRUSH rule 1 x 451 [6,8]
+ CRUSH rule 1 x 452 [8,5]
+ CRUSH rule 1 x 453 [6,8]
+ CRUSH rule 1 x 454 [6,7]
+ CRUSH rule 1 x 455 [2,7]
+ CRUSH rule 1 x 456 [6,8]
+ CRUSH rule 1 x 457 [7,2]
+ CRUSH rule 1 x 458 [2,8]
+ CRUSH rule 1 x 459 [2,5]
+ CRUSH rule 1 x 460 [6,5]
+ CRUSH rule 1 x 461 [6,5]
+ CRUSH rule 1 x 462 [8,2]
+ CRUSH rule 1 x 463 [6,7]
+ CRUSH rule 1 x 464 [7,5]
+ CRUSH rule 1 x 465 [7,6]
+ CRUSH rule 1 x 466 [5,8]
+ CRUSH rule 1 x 467 [6,5]
+ CRUSH rule 1 x 468 [7,8]
+ CRUSH rule 1 x 469 [7,2]
+ CRUSH rule 1 x 470 [5,2]
+ CRUSH rule 1 x 471 [2,6]
+ CRUSH rule 1 x 472 [5,6]
+ CRUSH rule 1 x 473 [2,5]
+ CRUSH rule 1 x 474 [6,2]
+ CRUSH rule 1 x 475 [6,7]
+ CRUSH rule 1 x 476 [5,2]
+ CRUSH rule 1 x 477 [5,8]
+ CRUSH rule 1 x 478 [6,7]
+ CRUSH rule 1 x 479 [2,5]
+ CRUSH rule 1 x 480 [2,8]
+ CRUSH rule 1 x 481 [2,5]
+ CRUSH rule 1 x 482 [5,7]
+ CRUSH rule 1 x 483 [2,7]
+ CRUSH rule 1 x 484 [2,7]
+ CRUSH rule 1 x 485 [5,7]
+ CRUSH rule 1 x 486 [5,2]
+ CRUSH rule 1 x 487 [5,2]
+ CRUSH rule 1 x 488 [5,7]
+ CRUSH rule 1 x 489 [2,8]
+ CRUSH rule 1 x 490 [6,5]
+ CRUSH rule 1 x 491 [2,6]
+ CRUSH rule 1 x 492 [6,5]
+ CRUSH rule 1 x 493 [2,8]
+ CRUSH rule 1 x 494 [2,7]
+ CRUSH rule 1 x 495 [5,6]
+ CRUSH rule 1 x 496 [7,5]
+ CRUSH rule 1 x 497 [5,7]
+ CRUSH rule 1 x 498 [2,5]
+ CRUSH rule 1 x 499 [8,5]
+ CRUSH rule 1 x 500 [5,6]
+ CRUSH rule 1 x 501 [2,7]
+ CRUSH rule 1 x 502 [7,2]
+ CRUSH rule 1 x 503 [2,5]
+ CRUSH rule 1 x 504 [5,6]
+ CRUSH rule 1 x 505 [2,7]
+ CRUSH rule 1 x 506 [5,2]
+ CRUSH rule 1 x 507 [6,2]
+ CRUSH rule 1 x 508 [2,7]
+ CRUSH rule 1 x 509 [7,5]
+ CRUSH rule 1 x 510 [6,2]
+ CRUSH rule 1 x 511 [5,8]
+ CRUSH rule 1 x 512 [7,6]
+ CRUSH rule 1 x 513 [7,2]
+ CRUSH rule 1 x 514 [5,7]
+ CRUSH rule 1 x 515 [8,5]
+ CRUSH rule 1 x 516 [5,2]
+ CRUSH rule 1 x 517 [7,8]
+ CRUSH rule 1 x 518 [5,6]
+ CRUSH rule 1 x 519 [7,5]
+ CRUSH rule 1 x 520 [2,6]
+ CRUSH rule 1 x 521 [8,7]
+ CRUSH rule 1 x 522 [6,8]
+ CRUSH rule 1 x 523 [5,2]
+ CRUSH rule 1 x 524 [2,5]
+ CRUSH rule 1 x 525 [2,5]
+ CRUSH rule 1 x 526 [2,5]
+ CRUSH rule 1 x 527 [2,5]
+ CRUSH rule 1 x 528 [5,2]
+ CRUSH rule 1 x 529 [5,7]
+ CRUSH rule 1 x 530 [6,7]
+ CRUSH rule 1 x 531 [6,2]
+ CRUSH rule 1 x 532 [6,5]
+ CRUSH rule 1 x 533 [5,6]
+ CRUSH rule 1 x 534 [7,5]
+ CRUSH rule 1 x 535 [8,6]
+ CRUSH rule 1 x 536 [6,7]
+ CRUSH rule 1 x 537 [5,7]
+ CRUSH rule 1 x 538 [6,8]
+ CRUSH rule 1 x 539 [8,5]
+ CRUSH rule 1 x 540 [2,6]
+ CRUSH rule 1 x 541 [2,5]
+ CRUSH rule 1 x 542 [5,2]
+ CRUSH rule 1 x 543 [6,2]
+ CRUSH rule 1 x 544 [5,7]
+ CRUSH rule 1 x 545 [5,7]
+ CRUSH rule 1 x 546 [6,2]
+ CRUSH rule 1 x 547 [8,2]
+ CRUSH rule 1 x 548 [5,2]
+ CRUSH rule 1 x 549 [5,8]
+ CRUSH rule 1 x 550 [2,5]
+ CRUSH rule 1 x 551 [7,5]
+ CRUSH rule 1 x 552 [5,2]
+ CRUSH rule 1 x 553 [5,2]
+ CRUSH rule 1 x 554 [2,8]
+ CRUSH rule 1 x 555 [5,2]
+ CRUSH rule 1 x 556 [5,6]
+ CRUSH rule 1 x 557 [7,5]
+ CRUSH rule 1 x 558 [5,2]
+ CRUSH rule 1 x 559 [5,2]
+ CRUSH rule 1 x 560 [8,5]
+ CRUSH rule 1 x 561 [6,5]
+ CRUSH rule 1 x 562 [5,7]
+ CRUSH rule 1 x 563 [2,6]
+ CRUSH rule 1 x 564 [5,2]
+ CRUSH rule 1 x 565 [5,6]
+ CRUSH rule 1 x 566 [5,7]
+ CRUSH rule 1 x 567 [5,6]
+ CRUSH rule 1 x 568 [7,5]
+ CRUSH rule 1 x 569 [5,2]
+ CRUSH rule 1 x 570 [2,5]
+ CRUSH rule 1 x 571 [5,7]
+ CRUSH rule 1 x 572 [5]
+ CRUSH rule 1 x 573 [5,2]
+ CRUSH rule 1 x 574 [2,8]
+ CRUSH rule 1 x 575 [8,6]
+ CRUSH rule 1 x 576 [5,6]
+ CRUSH rule 1 x 577 [8,2]
+ CRUSH rule 1 x 578 [6,8]
+ CRUSH rule 1 x 579 [5,2]
+ CRUSH rule 1 x 580 [5,2]
+ CRUSH rule 1 x 581 [7,2]
+ CRUSH rule 1 x 582 [2,8]
+ CRUSH rule 1 x 583 [6,2]
+ CRUSH rule 1 x 584 [8,2]
+ CRUSH rule 1 x 585 [7,2]
+ CRUSH rule 1 x 586 [2,6]
+ CRUSH rule 1 x 587 [2,5]
+ CRUSH rule 1 x 588 [5,2]
+ CRUSH rule 1 x 589 [7,2]
+ CRUSH rule 1 x 590 [6,2]
+ CRUSH rule 1 x 591 [5,2]
+ CRUSH rule 1 x 592 [2,5]
+ CRUSH rule 1 x 593 [2,8]
+ CRUSH rule 1 x 594 [2,7]
+ CRUSH rule 1 x 595 [7,2]
+ CRUSH rule 1 x 596 [5,7]
+ CRUSH rule 1 x 597 [5,2]
+ CRUSH rule 1 x 598 [5,2]
+ CRUSH rule 1 x 599 [5,2]
+ CRUSH rule 1 x 600 [7,2]
+ CRUSH rule 1 x 601 [2,7]
+ CRUSH rule 1 x 602 [5,7]
+ CRUSH rule 1 x 603 [5,2]
+ CRUSH rule 1 x 604 [7,5]
+ CRUSH rule 1 x 605 [5,2]
+ CRUSH rule 1 x 606 [2,6]
+ CRUSH rule 1 x 607 [2,5]
+ CRUSH rule 1 x 608 [5,2]
+ CRUSH rule 1 x 609 [5,2]
+ CRUSH rule 1 x 610 [5,7]
+ CRUSH rule 1 x 611 [2,5]
+ CRUSH rule 1 x 612 [2,8]
+ CRUSH rule 1 x 613 [7,2]
+ CRUSH rule 1 x 614 [7,8]
+ CRUSH rule 1 x 615 [6,8]
+ CRUSH rule 1 x 616 [2,8]
+ CRUSH rule 1 x 617 [6,2]
+ CRUSH rule 1 x 618 [7,6]
+ CRUSH rule 1 x 619 [5,2]
+ CRUSH rule 1 x 620 [5,2]
+ CRUSH rule 1 x 621 [5,8]
+ CRUSH rule 1 x 622 [2,5]
+ CRUSH rule 1 x 623 [2,6]
+ CRUSH rule 1 x 624 [5,7]
+ CRUSH rule 1 x 625 [2,5]
+ CRUSH rule 1 x 626 [7,8]
+ CRUSH rule 1 x 627 [2,7]
+ CRUSH rule 1 x 628 [8,2]
+ CRUSH rule 1 x 629 [2,6]
+ CRUSH rule 1 x 630 [2,7]
+ CRUSH rule 1 x 631 [2,7]
+ CRUSH rule 1 x 632 [7,2]
+ CRUSH rule 1 x 633 [8,6]
+ CRUSH rule 1 x 634 [2,5]
+ CRUSH rule 1 x 635 [5,6]
+ CRUSH rule 1 x 636 [2,5]
+ CRUSH rule 1 x 637 [5,2]
+ CRUSH rule 1 x 638 [6,8]
+ CRUSH rule 1 x 639 [5,2]
+ CRUSH rule 1 x 640 [5,2]
+ CRUSH rule 1 x 641 [7,2]
+ CRUSH rule 1 x 642 [2,8]
+ CRUSH rule 1 x 643 [5,7]
+ CRUSH rule 1 x 644 [8,2]
+ CRUSH rule 1 x 645 [5,2]
+ CRUSH rule 1 x 646 [8,2]
+ CRUSH rule 1 x 647 [7,2]
+ CRUSH rule 1 x 648 [2,6]
+ CRUSH rule 1 x 649 [5,7]
+ CRUSH rule 1 x 650 [7,8]
+ CRUSH rule 1 x 651 [5,7]
+ CRUSH rule 1 x 652 [5,6]
+ CRUSH rule 1 x 653 [8,5]
+ CRUSH rule 1 x 654 [7,5]
+ CRUSH rule 1 x 655 [2,5]
+ CRUSH rule 1 x 656 [5,2]
+ CRUSH rule 1 x 657 [6,2]
+ CRUSH rule 1 x 658 [5,2]
+ CRUSH rule 1 x 659 [5,6]
+ CRUSH rule 1 x 660 [7,8]
+ CRUSH rule 1 x 661 [2,8]
+ CRUSH rule 1 x 662 [5,2]
+ CRUSH rule 1 x 663 [2,5]
+ CRUSH rule 1 x 664 [2,5]
+ CRUSH rule 1 x 665 [5,7]
+ CRUSH rule 1 x 666 [2,8]
+ CRUSH rule 1 x 667 [2,5]
+ CRUSH rule 1 x 668 [5,7]
+ CRUSH rule 1 x 669 [6,5]
+ CRUSH rule 1 x 670 [5,2]
+ CRUSH rule 1 x 671 [2,5]
+ CRUSH rule 1 x 672 [5,6]
+ CRUSH rule 1 x 673 [5,2]
+ CRUSH rule 1 x 674 [5,2]
+ CRUSH rule 1 x 675 [2,8]
+ CRUSH rule 1 x 676 [2,5]
+ CRUSH rule 1 x 677 [5,2]
+ CRUSH rule 1 x 678 [2,5]
+ CRUSH rule 1 x 679 [6,2]
+ CRUSH rule 1 x 680 [2,5]
+ CRUSH rule 1 x 681 [5,7]
+ CRUSH rule 1 x 682 [2,5]
+ CRUSH rule 1 x 683 [2,8]
+ CRUSH rule 1 x 684 [7,2]
+ CRUSH rule 1 x 685 [7,2]
+ CRUSH rule 1 x 686 [2,5]
+ CRUSH rule 1 x 687 [5,7]
+ CRUSH rule 1 x 688 [5,7]
+ CRUSH rule 1 x 689 [6,5]
+ CRUSH rule 1 x 690 [8,2]
+ CRUSH rule 1 x 691 [5,2]
+ CRUSH rule 1 x 692 [7,2]
+ CRUSH rule 1 x 693 [6,7]
+ CRUSH rule 1 x 694 [6,5]
+ CRUSH rule 1 x 695 [2,8]
+ CRUSH rule 1 x 696 [2,5]
+ CRUSH rule 1 x 697 [6,2]
+ CRUSH rule 1 x 698 [6,2]
+ CRUSH rule 1 x 699 [2,6]
+ CRUSH rule 1 x 700 [2,5]
+ CRUSH rule 1 x 701 [5,2]
+ CRUSH rule 1 x 702 [5,6]
+ CRUSH rule 1 x 703 [8,5]
+ CRUSH rule 1 x 704 [2,5]
+ CRUSH rule 1 x 705 [8,6]
+ CRUSH rule 1 x 706 [2,7]
+ CRUSH rule 1 x 707 [7,8]
+ CRUSH rule 1 x 708 [5,7]
+ CRUSH rule 1 x 709 [6,5]
+ CRUSH rule 1 x 710 [8,5]
+ CRUSH rule 1 x 711 [2,5]
+ CRUSH rule 1 x 712 [2,5]
+ CRUSH rule 1 x 713 [6,7]
+ CRUSH rule 1 x 714 [5,2]
+ CRUSH rule 1 x 715 [2,5]
+ CRUSH rule 1 x 716 [5,6]
+ CRUSH rule 1 x 717 [8,7]
+ CRUSH rule 1 x 718 [5,7]
+ CRUSH rule 1 x 719 [2,6]
+ CRUSH rule 1 x 720 [6,8]
+ CRUSH rule 1 x 721 [5,6]
+ CRUSH rule 1 x 722 [5,2]
+ CRUSH rule 1 x 723 [5,2]
+ CRUSH rule 1 x 724 [2,6]
+ CRUSH rule 1 x 725 [2,8]
+ CRUSH rule 1 x 726 [5,8]
+ CRUSH rule 1 x 727 [5,6]
+ CRUSH rule 1 x 728 [2,6]
+ CRUSH rule 1 x 729 [5,6]
+ CRUSH rule 1 x 730 [5,7]
+ CRUSH rule 1 x 731 [5,2]
+ CRUSH rule 1 x 732 [2,5]
+ CRUSH rule 1 x 733 [5,2]
+ CRUSH rule 1 x 734 [6,5]
+ CRUSH rule 1 x 735 [5,8]
+ CRUSH rule 1 x 736 [5,8]
+ CRUSH rule 1 x 737 [2,5]
+ CRUSH rule 1 x 738 [5,2]
+ CRUSH rule 1 x 739 [2,8]
+ CRUSH rule 1 x 740 [2,6]
+ CRUSH rule 1 x 741 [7,8]
+ CRUSH rule 1 x 742 [8,2]
+ CRUSH rule 1 x 743 [7,2]
+ CRUSH rule 1 x 744 [5,7]
+ CRUSH rule 1 x 745 [5,6]
+ CRUSH rule 1 x 746 [5,2]
+ CRUSH rule 1 x 747 [6,2]
+ CRUSH rule 1 x 748 [2,7]
+ CRUSH rule 1 x 749 [5,8]
+ CRUSH rule 1 x 750 [2,6]
+ CRUSH rule 1 x 751 [2,8]
+ CRUSH rule 1 x 752 [8,2]
+ CRUSH rule 1 x 753 [7,8]
+ CRUSH rule 1 x 754 [8,6]
+ CRUSH rule 1 x 755 [2,5]
+ CRUSH rule 1 x 756 [5,6]
+ CRUSH rule 1 x 757 [8,6]
+ CRUSH rule 1 x 758 [6,2]
+ CRUSH rule 1 x 759 [8,5]
+ CRUSH rule 1 x 760 [2,5]
+ CRUSH rule 1 x 761 [5,2]
+ CRUSH rule 1 x 762 [2,7]
+ CRUSH rule 1 x 763 [8,6]
+ CRUSH rule 1 x 764 [2,7]
+ CRUSH rule 1 x 765 [6,5]
+ CRUSH rule 1 x 766 [8,5]
+ CRUSH rule 1 x 767 [2,6]
+ CRUSH rule 1 x 768 [8,5]
+ CRUSH rule 1 x 769 [6,2]
+ CRUSH rule 1 x 770 [6,2]
+ CRUSH rule 1 x 771 [7,2]
+ CRUSH rule 1 x 772 [8,5]
+ CRUSH rule 1 x 773 [5,2]
+ CRUSH rule 1 x 774 [5,6]
+ CRUSH rule 1 x 775 [6,8]
+ CRUSH rule 1 x 776 [7,2]
+ CRUSH rule 1 x 777 [5,2]
+ CRUSH rule 1 x 778 [2,8]
+ CRUSH rule 1 x 779 [2,7]
+ CRUSH rule 1 x 780 [2,5]
+ CRUSH rule 1 x 781 [6,5]
+ CRUSH rule 1 x 782 [5,2]
+ CRUSH rule 1 x 783 [7,2]
+ CRUSH rule 1 x 784 [2,7]
+ CRUSH rule 1 x 785 [6,2]
+ CRUSH rule 1 x 786 [7,6]
+ CRUSH rule 1 x 787 [2,7]
+ CRUSH rule 1 x 788 [6,2]
+ CRUSH rule 1 x 789 [2,5]
+ CRUSH rule 1 x 790 [8,5]
+ CRUSH rule 1 x 791 [5,8]
+ CRUSH rule 1 x 792 [5,8]
+ CRUSH rule 1 x 793 [6,2]
+ CRUSH rule 1 x 794 [2,6]
+ CRUSH rule 1 x 795 [2,5]
+ CRUSH rule 1 x 796 [5,6]
+ CRUSH rule 1 x 797 [2,5]
+ CRUSH rule 1 x 798 [6,8]
+ CRUSH rule 1 x 799 [5,2]
+ CRUSH rule 1 x 800 [5,2]
+ CRUSH rule 1 x 801 [5,6]
+ CRUSH rule 1 x 802 [2,8]
+ CRUSH rule 1 x 803 [2,5]
+ CRUSH rule 1 x 804 [6,2]
+ CRUSH rule 1 x 805 [5,6]
+ CRUSH rule 1 x 806 [2,5]
+ CRUSH rule 1 x 807 [5,7]
+ CRUSH rule 1 x 808 [5,6]
+ CRUSH rule 1 x 809 [2,5]
+ CRUSH rule 1 x 810 [5,7]
+ CRUSH rule 1 x 811 [8,5]
+ CRUSH rule 1 x 812 [8,5]
+ CRUSH rule 1 x 813 [6,5]
+ CRUSH rule 1 x 814 [5,6]
+ CRUSH rule 1 x 815 [5,2]
+ CRUSH rule 1 x 816 [2,5]
+ CRUSH rule 1 x 817 [5,7]
+ CRUSH rule 1 x 818 [5,2]
+ CRUSH rule 1 x 819 [5,2]
+ CRUSH rule 1 x 820 [5,2]
+ CRUSH rule 1 x 821 [5,2]
+ CRUSH rule 1 x 822 [2,7]
+ CRUSH rule 1 x 823 [5,8]
+ CRUSH rule 1 x 824 [5,7]
+ CRUSH rule 1 x 825 [2,8]
+ CRUSH rule 1 x 826 [7,2]
+ CRUSH rule 1 x 827 [2,8]
+ CRUSH rule 1 x 828 [2,5]
+ CRUSH rule 1 x 829 [5,6]
+ CRUSH rule 1 x 830 [2,5]
+ CRUSH rule 1 x 831 [2,6]
+ CRUSH rule 1 x 832 [5,7]
+ CRUSH rule 1 x 833 [2,7]
+ CRUSH rule 1 x 834 [5]
+ CRUSH rule 1 x 835 [8,5]
+ CRUSH rule 1 x 836 [5,2]
+ CRUSH rule 1 x 837 [6,5]
+ CRUSH rule 1 x 838 [6,7]
+ CRUSH rule 1 x 839 [5,2]
+ CRUSH rule 1 x 840 [7,8]
+ CRUSH rule 1 x 841 [5,8]
+ CRUSH rule 1 x 842 [2,5]
+ CRUSH rule 1 x 843 [6,5]
+ CRUSH rule 1 x 844 [5,8]
+ CRUSH rule 1 x 845 [5,8]
+ CRUSH rule 1 x 846 [5,2]
+ CRUSH rule 1 x 847 [2,8]
+ CRUSH rule 1 x 848 [2,6]
+ CRUSH rule 1 x 849 [5,8]
+ CRUSH rule 1 x 850 [2,5]
+ CRUSH rule 1 x 851 [6,8]
+ CRUSH rule 1 x 852 [7,5]
+ CRUSH rule 1 x 853 [6,8]
+ CRUSH rule 1 x 854 [7,6]
+ CRUSH rule 1 x 855 [5,7]
+ CRUSH rule 1 x 856 [6,7]
+ CRUSH rule 1 x 857 [8,5]
+ CRUSH rule 1 x 858 [6,5]
+ CRUSH rule 1 x 859 [6,2]
+ CRUSH rule 1 x 860 [5,2]
+ CRUSH rule 1 x 861 [8,7]
+ CRUSH rule 1 x 862 [6,2]
+ CRUSH rule 1 x 863 [8,7]
+ CRUSH rule 1 x 864 [5,6]
+ CRUSH rule 1 x 865 [8,2]
+ CRUSH rule 1 x 866 [5,8]
+ CRUSH rule 1 x 867 [6,5]
+ CRUSH rule 1 x 868 [6,5]
+ CRUSH rule 1 x 869 [8,7]
+ CRUSH rule 1 x 870 [2,5]
+ CRUSH rule 1 x 871 [5,7]
+ CRUSH rule 1 x 872 [5,2]
+ CRUSH rule 1 x 873 [5,6]
+ CRUSH rule 1 x 874 [2,6]
+ CRUSH rule 1 x 875 [2,6]
+ CRUSH rule 1 x 876 [5,8]
+ CRUSH rule 1 x 877 [6,5]
+ CRUSH rule 1 x 878 [5,2]
+ CRUSH rule 1 x 879 [7,5]
+ CRUSH rule 1 x 880 [5,2]
+ CRUSH rule 1 x 881 [5,6]
+ CRUSH rule 1 x 882 [5,2]
+ CRUSH rule 1 x 883 [2,7]
+ CRUSH rule 1 x 884 [6,2]
+ CRUSH rule 1 x 885 [5,2]
+ CRUSH rule 1 x 886 [5,6]
+ CRUSH rule 1 x 887 [7,5]
+ CRUSH rule 1 x 888 [6,8]
+ CRUSH rule 1 x 889 [2,5]
+ CRUSH rule 1 x 890 [7,2]
+ CRUSH rule 1 x 891 [2,8]
+ CRUSH rule 1 x 892 [6,2]
+ CRUSH rule 1 x 893 [2,5]
+ CRUSH rule 1 x 894 [7,5]
+ CRUSH rule 1 x 895 [5,7]
+ CRUSH rule 1 x 896 [2,8]
+ CRUSH rule 1 x 897 [5,2]
+ CRUSH rule 1 x 898 [2,5]
+ CRUSH rule 1 x 899 [2,7]
+ CRUSH rule 1 x 900 [5,2]
+ CRUSH rule 1 x 901 [5,2]
+ CRUSH rule 1 x 902 [8,5]
+ CRUSH rule 1 x 903 [5,7]
+ CRUSH rule 1 x 904 [5,6]
+ CRUSH rule 1 x 905 [6,2]
+ CRUSH rule 1 x 906 [2,5]
+ CRUSH rule 1 x 907 [7,2]
+ CRUSH rule 1 x 908 [5,8]
+ CRUSH rule 1 x 909 [2,5]
+ CRUSH rule 1 x 910 [6,5]
+ CRUSH rule 1 x 911 [5,8]
+ CRUSH rule 1 x 912 [2,6]
+ CRUSH rule 1 x 913 [7,6]
+ CRUSH rule 1 x 914 [6,5]
+ CRUSH rule 1 x 915 [8,2]
+ CRUSH rule 1 x 916 [5,2]
+ CRUSH rule 1 x 917 [2,5]
+ CRUSH rule 1 x 918 [8,2]
+ CRUSH rule 1 x 919 [6,2]
+ CRUSH rule 1 x 920 [7,6]
+ CRUSH rule 1 x 921 [2,5]
+ CRUSH rule 1 x 922 [6,7]
+ CRUSH rule 1 x 923 [5,8]
+ CRUSH rule 1 x 924 [5,6]
+ CRUSH rule 1 x 925 [5,7]
+ CRUSH rule 1 x 926 [5,8]
+ CRUSH rule 1 x 927 [2,6]
+ CRUSH rule 1 x 928 [8,2]
+ CRUSH rule 1 x 929 [5,2]
+ CRUSH rule 1 x 930 [2,5]
+ CRUSH rule 1 x 931 [5,2]
+ CRUSH rule 1 x 932 [5,8]
+ CRUSH rule 1 x 933 [8,5]
+ CRUSH rule 1 x 934 [5,2]
+ CRUSH rule 1 x 935 [6,5]
+ CRUSH rule 1 x 936 [2,6]
+ CRUSH rule 1 x 937 [5,6]
+ CRUSH rule 1 x 938 [6,5]
+ CRUSH rule 1 x 939 [2,7]
+ CRUSH rule 1 x 940 [8,7]
+ CRUSH rule 1 x 941 [5,2]
+ CRUSH rule 1 x 942 [2,8]
+ CRUSH rule 1 x 943 [8,2]
+ CRUSH rule 1 x 944 [5,2]
+ CRUSH rule 1 x 945 [7,2]
+ CRUSH rule 1 x 946 [2,5]
+ CRUSH rule 1 x 947 [5,6]
+ CRUSH rule 1 x 948 [7,8]
+ CRUSH rule 1 x 949 [6,2]
+ CRUSH rule 1 x 950 [5,7]
+ CRUSH rule 1 x 951 [5,8]
+ CRUSH rule 1 x 952 [2,5]
+ CRUSH rule 1 x 953 [2,5]
+ CRUSH rule 1 x 954 [5,2]
+ CRUSH rule 1 x 955 [8,6]
+ CRUSH rule 1 x 956 [2,8]
+ CRUSH rule 1 x 957 [7,6]
+ CRUSH rule 1 x 958 [8,7]
+ CRUSH rule 1 x 959 [5,2]
+ CRUSH rule 1 x 960 [5,6]
+ CRUSH rule 1 x 961 [5,2]
+ CRUSH rule 1 x 962 [7,5]
+ CRUSH rule 1 x 963 [2,5]
+ CRUSH rule 1 x 964 [5,2]
+ CRUSH rule 1 x 965 [7,6]
+ CRUSH rule 1 x 966 [5,8]
+ CRUSH rule 1 x 967 [8,6]
+ CRUSH rule 1 x 968 [7,2]
+ CRUSH rule 1 x 969 [8,2]
+ CRUSH rule 1 x 970 [2,6]
+ CRUSH rule 1 x 971 [2,7]
+ CRUSH rule 1 x 972 [2,8]
+ CRUSH rule 1 x 973 [2,6]
+ CRUSH rule 1 x 974 [5,2]
+ CRUSH rule 1 x 975 [5,7]
+ CRUSH rule 1 x 976 [5,6]
+ CRUSH rule 1 x 977 [8,5]
+ CRUSH rule 1 x 978 [7,2]
+ CRUSH rule 1 x 979 [7,6]
+ CRUSH rule 1 x 980 [6,2]
+ CRUSH rule 1 x 981 [7,5]
+ CRUSH rule 1 x 982 [5,2]
+ CRUSH rule 1 x 983 [5,6]
+ CRUSH rule 1 x 984 [2,8]
+ CRUSH rule 1 x 985 [2,5]
+ CRUSH rule 1 x 986 [8,7]
+ CRUSH rule 1 x 987 [2,5]
+ CRUSH rule 1 x 988 [2,5]
+ CRUSH rule 1 x 989 [2,6]
+ CRUSH rule 1 x 990 [2,5]
+ CRUSH rule 1 x 991 [2,5]
+ CRUSH rule 1 x 992 [7,2]
+ CRUSH rule 1 x 993 [2,6]
+ CRUSH rule 1 x 994 [5,2]
+ CRUSH rule 1 x 995 [7,6]
+ CRUSH rule 1 x 996 [6,7]
+ CRUSH rule 1 x 997 [6,5]
+ CRUSH rule 1 x 998 [8,2]
+ CRUSH rule 1 x 999 [2,7]
+ CRUSH rule 1 x 1000 [8,5]
+ CRUSH rule 1 x 1001 [2,5]
+ CRUSH rule 1 x 1002 [2,5]
+ CRUSH rule 1 x 1003 [2,8]
+ CRUSH rule 1 x 1004 [6,2]
+ CRUSH rule 1 x 1005 [6,2]
+ CRUSH rule 1 x 1006 [2,5]
+ CRUSH rule 1 x 1007 [2,8]
+ CRUSH rule 1 x 1008 [2,7]
+ CRUSH rule 1 x 1009 [6,8]
+ CRUSH rule 1 x 1010 [5,6]
+ CRUSH rule 1 x 1011 [5,2]
+ CRUSH rule 1 x 1012 [5,2]
+ CRUSH rule 1 x 1013 [5,2]
+ CRUSH rule 1 x 1014 [2,8]
+ CRUSH rule 1 x 1015 [6,8]
+ CRUSH rule 1 x 1016 [2,6]
+ CRUSH rule 1 x 1017 [6,2]
+ CRUSH rule 1 x 1018 [5,2]
+ CRUSH rule 1 x 1019 [5,7]
+ CRUSH rule 1 x 1020 [5,2]
+ CRUSH rule 1 x 1021 [5,2]
+ CRUSH rule 1 x 1022 [2,6]
+ CRUSH rule 1 x 1023 [5,2]
+ rule 1 (choose-two) num_rep 2 result size == 1:\t4/1024 (esc)
+ rule 1 (choose-two) num_rep 2 result size == 2:\t1020/1024 (esc)
+ CRUSH rule 1 x 0 [2,8,5]
+ CRUSH rule 1 x 1 [2,8,6]
+ CRUSH rule 1 x 2 [2,5,8]
+ CRUSH rule 1 x 3 [8,2,6]
+ CRUSH rule 1 x 4 [5,7,2]
+ CRUSH rule 1 x 5 [7,8,2]
+ CRUSH rule 1 x 6 [2,6,7]
+ CRUSH rule 1 x 7 [5,7,8]
+ CRUSH rule 1 x 8 [5,2,8]
+ CRUSH rule 1 x 9 [2,5,6]
+ CRUSH rule 1 x 10 [2,8,6]
+ CRUSH rule 1 x 11 [2,7,5]
+ CRUSH rule 1 x 12 [2,7,5]
+ CRUSH rule 1 x 13 [5,8,2]
+ CRUSH rule 1 x 14 [7,6,8]
+ CRUSH rule 1 x 15 [7,2,5]
+ CRUSH rule 1 x 16 [5,6,2]
+ CRUSH rule 1 x 17 [5,7,2]
+ CRUSH rule 1 x 18 [2,5,8]
+ CRUSH rule 1 x 19 [7,5,8]
+ CRUSH rule 1 x 20 [2,5,7]
+ CRUSH rule 1 x 21 [5,7,2]
+ CRUSH rule 1 x 22 [8,5,7]
+ CRUSH rule 1 x 23 [5,6,8]
+ CRUSH rule 1 x 24 [2,8,6]
+ CRUSH rule 1 x 25 [5,7,2]
+ CRUSH rule 1 x 26 [2,8,7]
+ CRUSH rule 1 x 27 [5,2]
+ CRUSH rule 1 x 28 [6,2,5]
+ CRUSH rule 1 x 29 [8,5,7]
+ CRUSH rule 1 x 30 [5,7,2]
+ CRUSH rule 1 x 31 [8,7,6]
+ CRUSH rule 1 x 32 [5,6,8]
+ CRUSH rule 1 x 33 [2,7,8]
+ CRUSH rule 1 x 34 [2,5,7]
+ CRUSH rule 1 x 35 [2,8,6]
+ CRUSH rule 1 x 36 [5,8,2]
+ CRUSH rule 1 x 37 [2,5,8]
+ CRUSH rule 1 x 38 [5,8,7]
+ CRUSH rule 1 x 39 [5,7,2]
+ CRUSH rule 1 x 40 [7,8,2]
+ CRUSH rule 1 x 41 [2,6,8]
+ CRUSH rule 1 x 42 [5,2,6]
+ CRUSH rule 1 x 43 [2,5,7]
+ CRUSH rule 1 x 44 [2,6,7]
+ CRUSH rule 1 x 45 [8,2,5]
+ CRUSH rule 1 x 46 [2,5,7]
+ CRUSH rule 1 x 47 [5,2,8]
+ CRUSH rule 1 x 48 [5,6,7]
+ CRUSH rule 1 x 49 [5,2,8]
+ CRUSH rule 1 x 50 [5,6,2]
+ CRUSH rule 1 x 51 [5,6,2]
+ CRUSH rule 1 x 52 [8,6,2]
+ CRUSH rule 1 x 53 [5,2,8]
+ CRUSH rule 1 x 54 [7,6,8]
+ CRUSH rule 1 x 55 [8,7,2]
+ CRUSH rule 1 x 56 [6,5,8]
+ CRUSH rule 1 x 57 [5,2,6]
+ CRUSH rule 1 x 58 [2,5,6]
+ CRUSH rule 1 x 59 [5,2,7]
+ CRUSH rule 1 x 60 [5,7,2]
+ CRUSH rule 1 x 61 [5,6,8]
+ CRUSH rule 1 x 62 [7,2,6]
+ CRUSH rule 1 x 63 [5,6,8]
+ CRUSH rule 1 x 64 [5,2,8]
+ CRUSH rule 1 x 65 [7,5,6]
+ CRUSH rule 1 x 66 [5,2,6]
+ CRUSH rule 1 x 67 [5,2,8]
+ CRUSH rule 1 x 68 [2,5,8]
+ CRUSH rule 1 x 69 [5,2,6]
+ CRUSH rule 1 x 70 [7,2,5]
+ CRUSH rule 1 x 71 [2,8,6]
+ CRUSH rule 1 x 72 [6,2,5]
+ CRUSH rule 1 x 73 [2,7,5]
+ CRUSH rule 1 x 74 [2,7,5]
+ CRUSH rule 1 x 75 [5,2,7]
+ CRUSH rule 1 x 76 [5,2,7]
+ CRUSH rule 1 x 77 [7,2,6]
+ CRUSH rule 1 x 78 [2,5,8]
+ CRUSH rule 1 x 79 [5,2,7]
+ CRUSH rule 1 x 80 [2,5,7]
+ CRUSH rule 1 x 81 [2,5]
+ CRUSH rule 1 x 82 [7,2,6]
+ CRUSH rule 1 x 83 [2,6,8]
+ CRUSH rule 1 x 84 [7,2,6]
+ CRUSH rule 1 x 85 [5,8,7]
+ CRUSH rule 1 x 86 [2,7,8]
+ CRUSH rule 1 x 87 [2,7,5]
+ CRUSH rule 1 x 88 [2,6,5]
+ CRUSH rule 1 x 89 [5,2,6]
+ CRUSH rule 1 x 90 [6,7,5]
+ CRUSH rule 1 x 91 [5,8,7]
+ CRUSH rule 1 x 92 [2,8,5]
+ CRUSH rule 1 x 93 [7,5,8]
+ CRUSH rule 1 x 94 [2,5,7]
+ CRUSH rule 1 x 95 [7,5,2]
+ CRUSH rule 1 x 96 [5,6,7]
+ CRUSH rule 1 x 97 [8,7,5]
+ CRUSH rule 1 x 98 [2,6,7]
+ CRUSH rule 1 x 99 [2,7,8]
+ CRUSH rule 1 x 100 [2,7,5]
+ CRUSH rule 1 x 101 [5,7,2]
+ CRUSH rule 1 x 102 [5,2,7]
+ CRUSH rule 1 x 103 [5,7,6]
+ CRUSH rule 1 x 104 [7,5,2]
+ CRUSH rule 1 x 105 [2,5,6]
+ CRUSH rule 1 x 106 [2,6,5]
+ CRUSH rule 1 x 107 [5,2,7]
+ CRUSH rule 1 x 108 [7,2,8]
+ CRUSH rule 1 x 109 [2,5,8]
+ CRUSH rule 1 x 110 [5,2,7]
+ CRUSH rule 1 x 111 [2,5,8]
+ CRUSH rule 1 x 112 [2,5,6]
+ CRUSH rule 1 x 113 [6,2,8]
+ CRUSH rule 1 x 114 [7,6,5]
+ CRUSH rule 1 x 115 [8,2,5]
+ CRUSH rule 1 x 116 [2,6,7]
+ CRUSH rule 1 x 117 [7,5,6]
+ CRUSH rule 1 x 118 [2,5,8]
+ CRUSH rule 1 x 119 [5,6,2]
+ CRUSH rule 1 x 120 [2,5,8]
+ CRUSH rule 1 x 121 [2,5,7]
+ CRUSH rule 1 x 122 [8,5,2]
+ CRUSH rule 1 x 123 [2,5]
+ CRUSH rule 1 x 124 [5,8,2]
+ CRUSH rule 1 x 125 [2,7,5]
+ CRUSH rule 1 x 126 [5,2,6]
+ CRUSH rule 1 x 127 [5,6,7]
+ CRUSH rule 1 x 128 [5,8,6]
+ CRUSH rule 1 x 129 [2,7,6]
+ CRUSH rule 1 x 130 [5,8,2]
+ CRUSH rule 1 x 131 [2,8,7]
+ CRUSH rule 1 x 132 [2,7,5]
+ CRUSH rule 1 x 133 [5,6,7]
+ CRUSH rule 1 x 134 [2,8,7]
+ CRUSH rule 1 x 135 [5,6,2]
+ CRUSH rule 1 x 136 [2,5,6]
+ CRUSH rule 1 x 137 [7,5,6]
+ CRUSH rule 1 x 138 [8,7,6]
+ CRUSH rule 1 x 139 [5,2,7]
+ CRUSH rule 1 x 140 [2,6,7]
+ CRUSH rule 1 x 141 [6,8,2]
+ CRUSH rule 1 x 142 [5,6,2]
+ CRUSH rule 1 x 143 [5,8,7]
+ CRUSH rule 1 x 144 [8,2,5]
+ CRUSH rule 1 x 145 [8,5,6]
+ CRUSH rule 1 x 146 [2,6,5]
+ CRUSH rule 1 x 147 [2,8,7]
+ CRUSH rule 1 x 148 [5,2,6]
+ CRUSH rule 1 x 149 [5,8,6]
+ CRUSH rule 1 x 150 [2,6,5]
+ CRUSH rule 1 x 151 [5,2]
+ CRUSH rule 1 x 152 [8,5,6]
+ CRUSH rule 1 x 153 [8,6,7]
+ CRUSH rule 1 x 154 [5,2,6]
+ CRUSH rule 1 x 155 [5,7,8]
+ CRUSH rule 1 x 156 [5,8,7]
+ CRUSH rule 1 x 157 [5,2,7]
+ CRUSH rule 1 x 158 [2,8,7]
+ CRUSH rule 1 x 159 [7,2,5]
+ CRUSH rule 1 x 160 [2,8,7]
+ CRUSH rule 1 x 161 [2,5,6]
+ CRUSH rule 1 x 162 [2,6,7]
+ CRUSH rule 1 x 163 [5,6,2]
+ CRUSH rule 1 x 164 [7,8,2]
+ CRUSH rule 1 x 165 [7,2,5]
+ CRUSH rule 1 x 166 [2,5,8]
+ CRUSH rule 1 x 167 [2,6,8]
+ CRUSH rule 1 x 168 [5,2,6]
+ CRUSH rule 1 x 169 [2,6,8]
+ CRUSH rule 1 x 170 [2,8,5]
+ CRUSH rule 1 x 171 [7,5,8]
+ CRUSH rule 1 x 172 [2,7,8]
+ CRUSH rule 1 x 173 [8,5,6]
+ CRUSH rule 1 x 174 [2,5,7]
+ CRUSH rule 1 x 175 [6,2,5]
+ CRUSH rule 1 x 176 [5,2,6]
+ CRUSH rule 1 x 177 [5,8,2]
+ CRUSH rule 1 x 178 [5,2,7]
+ CRUSH rule 1 x 179 [5,2,8]
+ CRUSH rule 1 x 180 [5,8,7]
+ CRUSH rule 1 x 181 [6,2,8]
+ CRUSH rule 1 x 182 [8,5,2]
+ CRUSH rule 1 x 183 [7,8,6]
+ CRUSH rule 1 x 184 [5,7,6]
+ CRUSH rule 1 x 185 [6,8,2]
+ CRUSH rule 1 x 186 [2,8,5]
+ CRUSH rule 1 x 187 [2,6,7]
+ CRUSH rule 1 x 188 [2,8,7]
+ CRUSH rule 1 x 189 [2,7,8]
+ CRUSH rule 1 x 190 [5,2,8]
+ CRUSH rule 1 x 191 [7,6,8]
+ CRUSH rule 1 x 192 [5,2,7]
+ CRUSH rule 1 x 193 [5,2,6]
+ CRUSH rule 1 x 194 [2,5,6]
+ CRUSH rule 1 x 195 [6,5,8]
+ CRUSH rule 1 x 196 [6,7,2]
+ CRUSH rule 1 x 197 [6,5,2]
+ CRUSH rule 1 x 198 [2,5,6]
+ CRUSH rule 1 x 199 [2,5,7]
+ CRUSH rule 1 x 200 [2,5,7]
+ CRUSH rule 1 x 201 [7,2,8]
+ CRUSH rule 1 x 202 [6,5,2]
+ CRUSH rule 1 x 203 [5,8,7]
+ CRUSH rule 1 x 204 [2,5,8]
+ CRUSH rule 1 x 205 [2,7,6]
+ CRUSH rule 1 x 206 [2,5,7]
+ CRUSH rule 1 x 207 [5,2,8]
+ CRUSH rule 1 x 208 [7,2,8]
+ CRUSH rule 1 x 209 [2,5,7]
+ CRUSH rule 1 x 210 [2,8,5]
+ CRUSH rule 1 x 211 [5,6,2]
+ CRUSH rule 1 x 212 [7,5,2]
+ CRUSH rule 1 x 213 [8,5,2]
+ CRUSH rule 1 x 214 [5,7,8]
+ CRUSH rule 1 x 215 [8,2,7]
+ CRUSH rule 1 x 216 [5,2,6]
+ CRUSH rule 1 x 217 [2,5,7]
+ CRUSH rule 1 x 218 [2,7,8]
+ CRUSH rule 1 x 219 [5,8,2]
+ CRUSH rule 1 x 220 [5,7,8]
+ CRUSH rule 1 x 221 [5,6,8]
+ CRUSH rule 1 x 222 [6,8,5]
+ CRUSH rule 1 x 223 [2,5,6]
+ CRUSH rule 1 x 224 [2,5,8]
+ CRUSH rule 1 x 225 [8,6,2]
+ CRUSH rule 1 x 226 [7,2,5]
+ CRUSH rule 1 x 227 [5,6,2]
+ CRUSH rule 1 x 228 [5,6,8]
+ CRUSH rule 1 x 229 [5,2,7]
+ CRUSH rule 1 x 230 [5,7,2]
+ CRUSH rule 1 x 231 [5,6,2]
+ CRUSH rule 1 x 232 [2,7,5]
+ CRUSH rule 1 x 233 [5,8,7]
+ CRUSH rule 1 x 234 [2,7,5]
+ CRUSH rule 1 x 235 [5,8,2]
+ CRUSH rule 1 x 236 [5,2,7]
+ CRUSH rule 1 x 237 [5,7,6]
+ CRUSH rule 1 x 238 [5,6,2]
+ CRUSH rule 1 x 239 [8,7,6]
+ CRUSH rule 1 x 240 [5,7,2]
+ CRUSH rule 1 x 241 [5,2,8]
+ CRUSH rule 1 x 242 [5,8,2]
+ CRUSH rule 1 x 243 [5,7,8]
+ CRUSH rule 1 x 244 [5,6,2]
+ CRUSH rule 1 x 245 [7,6,2]
+ CRUSH rule 1 x 246 [2,5,7]
+ CRUSH rule 1 x 247 [6,2,7]
+ CRUSH rule 1 x 248 [8,2,7]
+ CRUSH rule 1 x 249 [2,5,8]
+ CRUSH rule 1 x 250 [2,7,5]
+ CRUSH rule 1 x 251 [2,5,6]
+ CRUSH rule 1 x 252 [5,7,8]
+ CRUSH rule 1 x 253 [5,2,8]
+ CRUSH rule 1 x 254 [5,6,7]
+ CRUSH rule 1 x 255 [2,7,8]
+ CRUSH rule 1 x 256 [5,7,2]
+ CRUSH rule 1 x 257 [2,8,5]
+ CRUSH rule 1 x 258 [5,2]
+ CRUSH rule 1 x 259 [5,7,6]
+ CRUSH rule 1 x 260 [5,6,2]
+ CRUSH rule 1 x 261 [8,7,5]
+ CRUSH rule 1 x 262 [5,2,8]
+ CRUSH rule 1 x 263 [6,8,2]
+ CRUSH rule 1 x 264 [5,6,2]
+ CRUSH rule 1 x 265 [8,6,7]
+ CRUSH rule 1 x 266 [8,2,5]
+ CRUSH rule 1 x 267 [2,5,8]
+ CRUSH rule 1 x 268 [2,7,5]
+ CRUSH rule 1 x 269 [2,8,7]
+ CRUSH rule 1 x 270 [5,2,7]
+ CRUSH rule 1 x 271 [7,5,6]
+ CRUSH rule 1 x 272 [2,8,5]
+ CRUSH rule 1 x 273 [5,2]
+ CRUSH rule 1 x 274 [6,8,5]
+ CRUSH rule 1 x 275 [5,8,6]
+ CRUSH rule 1 x 276 [7,2,6]
+ CRUSH rule 1 x 277 [6,5,2]
+ CRUSH rule 1 x 278 [6,8,2]
+ CRUSH rule 1 x 279 [8,5,6]
+ CRUSH rule 1 x 280 [2,6,7]
+ CRUSH rule 1 x 281 [8,2,6]
+ CRUSH rule 1 x 282 [5,2,6]
+ CRUSH rule 1 x 283 [8,2,7]
+ CRUSH rule 1 x 284 [6,5,2]
+ CRUSH rule 1 x 285 [5,6,7]
+ CRUSH rule 1 x 286 [2,5,6]
+ CRUSH rule 1 x 287 [2,5,6]
+ CRUSH rule 1 x 288 [8,2,7]
+ CRUSH rule 1 x 289 [5,6,2]
+ CRUSH rule 1 x 290 [2,5,7]
+ CRUSH rule 1 x 291 [2,5,7]
+ CRUSH rule 1 x 292 [8,2,7]
+ CRUSH rule 1 x 293 [6,2,8]
+ CRUSH rule 1 x 294 [7,5,2]
+ CRUSH rule 1 x 295 [5,8,7]
+ CRUSH rule 1 x 296 [5,2,6]
+ CRUSH rule 1 x 297 [6,2,8]
+ CRUSH rule 1 x 298 [2,8,5]
+ CRUSH rule 1 x 299 [2,5,7]
+ CRUSH rule 1 x 300 [8,7,5]
+ CRUSH rule 1 x 301 [2,8,7]
+ CRUSH rule 1 x 302 [5,2,6]
+ CRUSH rule 1 x 303 [7,5,8]
+ CRUSH rule 1 x 304 [2,7,5]
+ CRUSH rule 1 x 305 [5,8,2]
+ CRUSH rule 1 x 306 [2,7,5]
+ CRUSH rule 1 x 307 [2,7,8]
+ CRUSH rule 1 x 308 [2,8,5]
+ CRUSH rule 1 x 309 [7,5,6]
+ CRUSH rule 1 x 310 [5,6,2]
+ CRUSH rule 1 x 311 [5,2,7]
+ CRUSH rule 1 x 312 [2,6,5]
+ CRUSH rule 1 x 313 [5,8,6]
+ CRUSH rule 1 x 314 [5,2,6]
+ CRUSH rule 1 x 315 [2,5,8]
+ CRUSH rule 1 x 316 [6,5,8]
+ CRUSH rule 1 x 317 [2,6,7]
+ CRUSH rule 1 x 318 [8,6,7]
+ CRUSH rule 1 x 319 [5,2,8]
+ CRUSH rule 1 x 320 [5,7,2]
+ CRUSH rule 1 x 321 [2,5]
+ CRUSH rule 1 x 322 [2,7,5]
+ CRUSH rule 1 x 323 [5,7,2]
+ CRUSH rule 1 x 324 [7,2,8]
+ CRUSH rule 1 x 325 [5,6,2]
+ CRUSH rule 1 x 326 [5,6,2]
+ CRUSH rule 1 x 327 [2,6,7]
+ CRUSH rule 1 x 328 [7,5,8]
+ CRUSH rule 1 x 329 [5,6,2]
+ CRUSH rule 1 x 330 [5,7,2]
+ CRUSH rule 1 x 331 [2,6,5]
+ CRUSH rule 1 x 332 [2,5,8]
+ CRUSH rule 1 x 333 [6,8,5]
+ CRUSH rule 1 x 334 [8,5,6]
+ CRUSH rule 1 x 335 [7,2,5]
+ CRUSH rule 1 x 336 [5,6,2]
+ CRUSH rule 1 x 337 [7,2,6]
+ CRUSH rule 1 x 338 [5,6,8]
+ CRUSH rule 1 x 339 [7,5,2]
+ CRUSH rule 1 x 340 [2,5,6]
+ CRUSH rule 1 x 341 [5,2,7]
+ CRUSH rule 1 x 342 [2,7,5]
+ CRUSH rule 1 x 343 [6,7,5]
+ CRUSH rule 1 x 344 [6,2,5]
+ CRUSH rule 1 x 345 [5,2,7]
+ CRUSH rule 1 x 346 [8,2,5]
+ CRUSH rule 1 x 347 [5,2,6]
+ CRUSH rule 1 x 348 [8,2,7]
+ CRUSH rule 1 x 349 [2,6,7]
+ CRUSH rule 1 x 350 [8,5,7]
+ CRUSH rule 1 x 351 [5,6,2]
+ CRUSH rule 1 x 352 [2,7,5]
+ CRUSH rule 1 x 353 [6,5,8]
+ CRUSH rule 1 x 354 [2,5,8]
+ CRUSH rule 1 x 355 [5,2,6]
+ CRUSH rule 1 x 356 [5,8,2]
+ CRUSH rule 1 x 357 [6,2,7]
+ CRUSH rule 1 x 358 [2,5,8]
+ CRUSH rule 1 x 359 [6,7,8]
+ CRUSH rule 1 x 360 [5,2,6]
+ CRUSH rule 1 x 361 [8,5,2]
+ CRUSH rule 1 x 362 [5,7,6]
+ CRUSH rule 1 x 363 [5,2,6]
+ CRUSH rule 1 x 364 [2,5,7]
+ CRUSH rule 1 x 365 [6,7,8]
+ CRUSH rule 1 x 366 [7,2,8]
+ CRUSH rule 1 x 367 [5,7,2]
+ CRUSH rule 1 x 368 [7,5,6]
+ CRUSH rule 1 x 369 [5,7,2]
+ CRUSH rule 1 x 370 [8,7,6]
+ CRUSH rule 1 x 371 [2,5,8]
+ CRUSH rule 1 x 372 [5,2,8]
+ CRUSH rule 1 x 373 [2,6,8]
+ CRUSH rule 1 x 374 [5,8,7]
+ CRUSH rule 1 x 375 [6,5,7]
+ CRUSH rule 1 x 376 [7,2,5]
+ CRUSH rule 1 x 377 [2,5,6]
+ CRUSH rule 1 x 378 [2,6,7]
+ CRUSH rule 1 x 379 [8,5,7]
+ CRUSH rule 1 x 380 [2,5]
+ CRUSH rule 1 x 381 [2,5,7]
+ CRUSH rule 1 x 382 [2,5,6]
+ CRUSH rule 1 x 383 [5,2]
+ CRUSH rule 1 x 384 [7,2,6]
+ CRUSH rule 1 x 385 [7,5,6]
+ CRUSH rule 1 x 386 [2,5,8]
+ CRUSH rule 1 x 387 [2,5,6]
+ CRUSH rule 1 x 388 [5,2,8]
+ CRUSH rule 1 x 389 [2,5,8]
+ CRUSH rule 1 x 390 [5,6,2]
+ CRUSH rule 1 x 391 [5,6,2]
+ CRUSH rule 1 x 392 [2,8,6]
+ CRUSH rule 1 x 393 [5,2,6]
+ CRUSH rule 1 x 394 [5,7,6]
+ CRUSH rule 1 x 395 [5,2,8]
+ CRUSH rule 1 x 396 [5,2,8]
+ CRUSH rule 1 x 397 [2,7,5]
+ CRUSH rule 1 x 398 [2,5,6]
+ CRUSH rule 1 x 399 [8,7,5]
+ CRUSH rule 1 x 400 [8,2,5]
+ CRUSH rule 1 x 401 [2,5,6]
+ CRUSH rule 1 x 402 [7,8,6]
+ CRUSH rule 1 x 403 [2,7,5]
+ CRUSH rule 1 x 404 [5,2,7]
+ CRUSH rule 1 x 405 [6,5,2]
+ CRUSH rule 1 x 406 [2,6,5]
+ CRUSH rule 1 x 407 [2,8,7]
+ CRUSH rule 1 x 408 [5,2,6]
+ CRUSH rule 1 x 409 [7,5,6]
+ CRUSH rule 1 x 410 [8,6,5]
+ CRUSH rule 1 x 411 [2,6,8]
+ CRUSH rule 1 x 412 [2,5,8]
+ CRUSH rule 1 x 413 [5,2,8]
+ CRUSH rule 1 x 414 [5,2,8]
+ CRUSH rule 1 x 415 [2,6,5]
+ CRUSH rule 1 x 416 [2,5,7]
+ CRUSH rule 1 x 417 [8,7,2]
+ CRUSH rule 1 x 418 [7,6,8]
+ CRUSH rule 1 x 419 [8,5,2]
+ CRUSH rule 1 x 420 [2,5,8]
+ CRUSH rule 1 x 421 [8,6,7]
+ CRUSH rule 1 x 422 [6,7,8]
+ CRUSH rule 1 x 423 [2,5,6]
+ CRUSH rule 1 x 424 [8,5,7]
+ CRUSH rule 1 x 425 [2,5,7]
+ CRUSH rule 1 x 426 [6,7,2]
+ CRUSH rule 1 x 427 [2,7,5]
+ CRUSH rule 1 x 428 [5,6,7]
+ CRUSH rule 1 x 429 [5,6,7]
+ CRUSH rule 1 x 430 [5,6,2]
+ CRUSH rule 1 x 431 [5,2]
+ CRUSH rule 1 x 432 [7,2,8]
+ CRUSH rule 1 x 433 [6,5,2]
+ CRUSH rule 1 x 434 [5,2,6]
+ CRUSH rule 1 x 435 [2,5,8]
+ CRUSH rule 1 x 436 [5,2,6]
+ CRUSH rule 1 x 437 [7,5,2]
+ CRUSH rule 1 x 438 [2,5,8]
+ CRUSH rule 1 x 439 [2,5,7]
+ CRUSH rule 1 x 440 [2,7,6]
+ CRUSH rule 1 x 441 [5,7,2]
+ CRUSH rule 1 x 442 [2,5,7]
+ CRUSH rule 1 x 443 [6,8,2]
+ CRUSH rule 1 x 444 [7,2,8]
+ CRUSH rule 1 x 445 [6,5,7]
+ CRUSH rule 1 x 446 [5,7,6]
+ CRUSH rule 1 x 447 [2,5,8]
+ CRUSH rule 1 x 448 [7,2,5]
+ CRUSH rule 1 x 449 [7,8,5]
+ CRUSH rule 1 x 450 [5,8,2]
+ CRUSH rule 1 x 451 [6,8,5]
+ CRUSH rule 1 x 452 [8,5,6]
+ CRUSH rule 1 x 453 [6,8,7]
+ CRUSH rule 1 x 454 [6,7,5]
+ CRUSH rule 1 x 455 [2,7,5]
+ CRUSH rule 1 x 456 [6,8,7]
+ CRUSH rule 1 x 457 [7,2,8]
+ CRUSH rule 1 x 458 [2,8,7]
+ CRUSH rule 1 x 459 [2,5,6]
+ CRUSH rule 1 x 460 [6,5,7]
+ CRUSH rule 1 x 461 [6,5,8]
+ CRUSH rule 1 x 462 [8,2,5]
+ CRUSH rule 1 x 463 [6,7,2]
+ CRUSH rule 1 x 464 [7,5,2]
+ CRUSH rule 1 x 465 [7,6,2]
+ CRUSH rule 1 x 466 [5,8,7]
+ CRUSH rule 1 x 467 [6,5,7]
+ CRUSH rule 1 x 468 [7,8,2]
+ CRUSH rule 1 x 469 [7,2,6]
+ CRUSH rule 1 x 470 [5,2,6]
+ CRUSH rule 1 x 471 [2,6,8]
+ CRUSH rule 1 x 472 [5,6,2]
+ CRUSH rule 1 x 473 [2,5,6]
+ CRUSH rule 1 x 474 [6,2,7]
+ CRUSH rule 1 x 475 [6,7,8]
+ CRUSH rule 1 x 476 [5,2]
+ CRUSH rule 1 x 477 [5,8,6]
+ CRUSH rule 1 x 478 [6,7,2]
+ CRUSH rule 1 x 479 [2,5,8]
+ CRUSH rule 1 x 480 [2,8,6]
+ CRUSH rule 1 x 481 [2,5,7]
+ CRUSH rule 1 x 482 [5,7,2]
+ CRUSH rule 1 x 483 [2,7,6]
+ CRUSH rule 1 x 484 [2,7,6]
+ CRUSH rule 1 x 485 [5,7,2]
+ CRUSH rule 1 x 486 [5,2,7]
+ CRUSH rule 1 x 487 [5,2]
+ CRUSH rule 1 x 488 [5,7,2]
+ CRUSH rule 1 x 489 [2,8,5]
+ CRUSH rule 1 x 490 [6,5,2]
+ CRUSH rule 1 x 491 [2,6,7]
+ CRUSH rule 1 x 492 [6,5,2]
+ CRUSH rule 1 x 493 [2,8,7]
+ CRUSH rule 1 x 494 [2,7,8]
+ CRUSH rule 1 x 495 [5,6,2]
+ CRUSH rule 1 x 496 [7,5,6]
+ CRUSH rule 1 x 497 [5,7,6]
+ CRUSH rule 1 x 498 [2,5,8]
+ CRUSH rule 1 x 499 [8,5,2]
+ CRUSH rule 1 x 500 [5,6,2]
+ CRUSH rule 1 x 501 [2,7,5]
+ CRUSH rule 1 x 502 [7,2,5]
+ CRUSH rule 1 x 503 [2,5,6]
+ CRUSH rule 1 x 504 [5,6,2]
+ CRUSH rule 1 x 505 [2,7,6]
+ CRUSH rule 1 x 506 [5,2]
+ CRUSH rule 1 x 507 [6,2,8]
+ CRUSH rule 1 x 508 [2,7,8]
+ CRUSH rule 1 x 509 [7,5,8]
+ CRUSH rule 1 x 510 [6,2,5]
+ CRUSH rule 1 x 511 [5,8,7]
+ CRUSH rule 1 x 512 [7,6,2]
+ CRUSH rule 1 x 513 [7,2,5]
+ CRUSH rule 1 x 514 [5,7,8]
+ CRUSH rule 1 x 515 [8,5,6]
+ CRUSH rule 1 x 516 [5,2,6]
+ CRUSH rule 1 x 517 [7,8,6]
+ CRUSH rule 1 x 518 [5,6,7]
+ CRUSH rule 1 x 519 [7,5,8]
+ CRUSH rule 1 x 520 [2,6,5]
+ CRUSH rule 1 x 521 [8,7,6]
+ CRUSH rule 1 x 522 [6,8,2]
+ CRUSH rule 1 x 523 [5,2,6]
+ CRUSH rule 1 x 524 [2,5,8]
+ CRUSH rule 1 x 525 [2,5,7]
+ CRUSH rule 1 x 526 [2,5,8]
+ CRUSH rule 1 x 527 [2,5,8]
+ CRUSH rule 1 x 528 [5,2,7]
+ CRUSH rule 1 x 529 [5,7,2]
+ CRUSH rule 1 x 530 [6,7,8]
+ CRUSH rule 1 x 531 [6,2,5]
+ CRUSH rule 1 x 532 [6,5,7]
+ CRUSH rule 1 x 533 [5,6,2]
+ CRUSH rule 1 x 534 [7,5,2]
+ CRUSH rule 1 x 535 [8,6,2]
+ CRUSH rule 1 x 536 [6,7,2]
+ CRUSH rule 1 x 537 [5,7,6]
+ CRUSH rule 1 x 538 [6,8,5]
+ CRUSH rule 1 x 539 [8,5,7]
+ CRUSH rule 1 x 540 [2,6,5]
+ CRUSH rule 1 x 541 [2,5,7]
+ CRUSH rule 1 x 542 [5,2,8]
+ CRUSH rule 1 x 543 [6,2,8]
+ CRUSH rule 1 x 544 [5,7,6]
+ CRUSH rule 1 x 545 [5,7,2]
+ CRUSH rule 1 x 546 [6,2,7]
+ CRUSH rule 1 x 547 [8,2,5]
+ CRUSH rule 1 x 548 [5,2,7]
+ CRUSH rule 1 x 549 [5,8,2]
+ CRUSH rule 1 x 550 [2,5,8]
+ CRUSH rule 1 x 551 [7,5,6]
+ CRUSH rule 1 x 552 [5,2]
+ CRUSH rule 1 x 553 [5,2,6]
+ CRUSH rule 1 x 554 [2,8,5]
+ CRUSH rule 1 x 555 [5,2,8]
+ CRUSH rule 1 x 556 [5,6,7]
+ CRUSH rule 1 x 557 [7,5,6]
+ CRUSH rule 1 x 558 [5,2,6]
+ CRUSH rule 1 x 559 [5,2,6]
+ CRUSH rule 1 x 560 [8,5,7]
+ CRUSH rule 1 x 561 [6,5,7]
+ CRUSH rule 1 x 562 [5,7,2]
+ CRUSH rule 1 x 563 [2,6,8]
+ CRUSH rule 1 x 564 [5,2,7]
+ CRUSH rule 1 x 565 [5,6,7]
+ CRUSH rule 1 x 566 [5,7,2]
+ CRUSH rule 1 x 567 [5,6,2]
+ CRUSH rule 1 x 568 [7,5,2]
+ CRUSH rule 1 x 569 [5,2,8]
+ CRUSH rule 1 x 570 [2,5]
+ CRUSH rule 1 x 571 [5,7,8]
+ CRUSH rule 1 x 572 [5,2]
+ CRUSH rule 1 x 573 [5,2,7]
+ CRUSH rule 1 x 574 [2,8,6]
+ CRUSH rule 1 x 575 [8,6,2]
+ CRUSH rule 1 x 576 [5,6,8]
+ CRUSH rule 1 x 577 [8,2,6]
+ CRUSH rule 1 x 578 [6,8,7]
+ CRUSH rule 1 x 579 [5,2,6]
+ CRUSH rule 1 x 580 [5,2,7]
+ CRUSH rule 1 x 581 [7,2,6]
+ CRUSH rule 1 x 582 [2,8,7]
+ CRUSH rule 1 x 583 [6,2,8]
+ CRUSH rule 1 x 584 [8,2,6]
+ CRUSH rule 1 x 585 [7,2,5]
+ CRUSH rule 1 x 586 [2,6,7]
+ CRUSH rule 1 x 587 [2,5,6]
+ CRUSH rule 1 x 588 [5,2,7]
+ CRUSH rule 1 x 589 [7,2,5]
+ CRUSH rule 1 x 590 [6,2,5]
+ CRUSH rule 1 x 591 [5,2,8]
+ CRUSH rule 1 x 592 [2,5,7]
+ CRUSH rule 1 x 593 [2,8,6]
+ CRUSH rule 1 x 594 [2,7,5]
+ CRUSH rule 1 x 595 [7,2,8]
+ CRUSH rule 1 x 596 [5,7,2]
+ CRUSH rule 1 x 597 [5,2,7]
+ CRUSH rule 1 x 598 [5,2,8]
+ CRUSH rule 1 x 599 [5,2,6]
+ CRUSH rule 1 x 600 [7,2,8]
+ CRUSH rule 1 x 601 [2,7,8]
+ CRUSH rule 1 x 602 [5,7,8]
+ CRUSH rule 1 x 603 [5,2,6]
+ CRUSH rule 1 x 604 [7,5,2]
+ CRUSH rule 1 x 605 [5,2,8]
+ CRUSH rule 1 x 606 [2,6,8]
+ CRUSH rule 1 x 607 [2,5,8]
+ CRUSH rule 1 x 608 [5,2,6]
+ CRUSH rule 1 x 609 [5,2]
+ CRUSH rule 1 x 610 [5,7,6]
+ CRUSH rule 1 x 611 [2,5,7]
+ CRUSH rule 1 x 612 [2,8,7]
+ CRUSH rule 1 x 613 [7,2,5]
+ CRUSH rule 1 x 614 [7,8,6]
+ CRUSH rule 1 x 615 [6,8,2]
+ CRUSH rule 1 x 616 [2,8,7]
+ CRUSH rule 1 x 617 [6,2,7]
+ CRUSH rule 1 x 618 [7,6,5]
+ CRUSH rule 1 x 619 [5,2,8]
+ CRUSH rule 1 x 620 [5,2,6]
+ CRUSH rule 1 x 621 [5,8,2]
+ CRUSH rule 1 x 622 [2,5,8]
+ CRUSH rule 1 x 623 [2,6,5]
+ CRUSH rule 1 x 624 [5,7,2]
+ CRUSH rule 1 x 625 [2,5,7]
+ CRUSH rule 1 x 626 [7,8,2]
+ CRUSH rule 1 x 627 [2,7,8]
+ CRUSH rule 1 x 628 [8,2,5]
+ CRUSH rule 1 x 629 [2,6,5]
+ CRUSH rule 1 x 630 [2,7,5]
+ CRUSH rule 1 x 631 [2,7,8]
+ CRUSH rule 1 x 632 [7,2,5]
+ CRUSH rule 1 x 633 [8,6,5]
+ CRUSH rule 1 x 634 [2,5,8]
+ CRUSH rule 1 x 635 [5,6,2]
+ CRUSH rule 1 x 636 [2,5,6]
+ CRUSH rule 1 x 637 [5,2,7]
+ CRUSH rule 1 x 638 [6,8,2]
+ CRUSH rule 1 x 639 [5,2,7]
+ CRUSH rule 1 x 640 [5,2,6]
+ CRUSH rule 1 x 641 [7,2,6]
+ CRUSH rule 1 x 642 [2,8,5]
+ CRUSH rule 1 x 643 [5,7,2]
+ CRUSH rule 1 x 644 [8,2,5]
+ CRUSH rule 1 x 645 [5,2,6]
+ CRUSH rule 1 x 646 [8,2,5]
+ CRUSH rule 1 x 647 [7,2,5]
+ CRUSH rule 1 x 648 [2,6,5]
+ CRUSH rule 1 x 649 [5,7,6]
+ CRUSH rule 1 x 650 [7,8,6]
+ CRUSH rule 1 x 651 [5,7,6]
+ CRUSH rule 1 x 652 [5,6,8]
+ CRUSH rule 1 x 653 [8,5,2]
+ CRUSH rule 1 x 654 [7,5,2]
+ CRUSH rule 1 x 655 [2,5,6]
+ CRUSH rule 1 x 656 [5,2,8]
+ CRUSH rule 1 x 657 [6,2,8]
+ CRUSH rule 1 x 658 [5,2,6]
+ CRUSH rule 1 x 659 [5,6,7]
+ CRUSH rule 1 x 660 [7,8,6]
+ CRUSH rule 1 x 661 [2,8,5]
+ CRUSH rule 1 x 662 [5,2]
+ CRUSH rule 1 x 663 [2,5,8]
+ CRUSH rule 1 x 664 [2,5]
+ CRUSH rule 1 x 665 [5,7,6]
+ CRUSH rule 1 x 666 [2,8,5]
+ CRUSH rule 1 x 667 [2,5,6]
+ CRUSH rule 1 x 668 [5,7,6]
+ CRUSH rule 1 x 669 [6,5,2]
+ CRUSH rule 1 x 670 [5,2]
+ CRUSH rule 1 x 671 [2,5,8]
+ CRUSH rule 1 x 672 [5,6,2]
+ CRUSH rule 1 x 673 [5,2,7]
+ CRUSH rule 1 x 674 [5,2,8]
+ CRUSH rule 1 x 675 [2,8,6]
+ CRUSH rule 1 x 676 [2,5]
+ CRUSH rule 1 x 677 [5,2,7]
+ CRUSH rule 1 x 678 [2,5,6]
+ CRUSH rule 1 x 679 [6,2,7]
+ CRUSH rule 1 x 680 [2,5,6]
+ CRUSH rule 1 x 681 [5,7,2]
+ CRUSH rule 1 x 682 [2,5,7]
+ CRUSH rule 1 x 683 [2,8,5]
+ CRUSH rule 1 x 684 [7,2,6]
+ CRUSH rule 1 x 685 [7,2,6]
+ CRUSH rule 1 x 686 [2,5,8]
+ CRUSH rule 1 x 687 [5,7,6]
+ CRUSH rule 1 x 688 [5,7,2]
+ CRUSH rule 1 x 689 [6,5,2]
+ CRUSH rule 1 x 690 [8,2,7]
+ CRUSH rule 1 x 691 [5,2]
+ CRUSH rule 1 x 692 [7,2,8]
+ CRUSH rule 1 x 693 [6,7,5]
+ CRUSH rule 1 x 694 [6,5,2]
+ CRUSH rule 1 x 695 [2,8,7]
+ CRUSH rule 1 x 696 [2,5,8]
+ CRUSH rule 1 x 697 [6,2,7]
+ CRUSH rule 1 x 698 [6,2,5]
+ CRUSH rule 1 x 699 [2,6,8]
+ CRUSH rule 1 x 700 [2,5,7]
+ CRUSH rule 1 x 701 [5,2,7]
+ CRUSH rule 1 x 702 [5,6,2]
+ CRUSH rule 1 x 703 [8,5,2]
+ CRUSH rule 1 x 704 [2,5,8]
+ CRUSH rule 1 x 705 [8,6,2]
+ CRUSH rule 1 x 706 [2,7,5]
+ CRUSH rule 1 x 707 [7,8,6]
+ CRUSH rule 1 x 708 [5,7,8]
+ CRUSH rule 1 x 709 [6,5,2]
+ CRUSH rule 1 x 710 [8,5,2]
+ CRUSH rule 1 x 711 [2,5,8]
+ CRUSH rule 1 x 712 [2,5,7]
+ CRUSH rule 1 x 713 [6,7,8]
+ CRUSH rule 1 x 714 [5,2,6]
+ CRUSH rule 1 x 715 [2,5,6]
+ CRUSH rule 1 x 716 [5,6,2]
+ CRUSH rule 1 x 717 [8,7,2]
+ CRUSH rule 1 x 718 [5,7,8]
+ CRUSH rule 1 x 719 [2,6,5]
+ CRUSH rule 1 x 720 [6,8,2]
+ CRUSH rule 1 x 721 [5,6,7]
+ CRUSH rule 1 x 722 [5,2,6]
+ CRUSH rule 1 x 723 [5,2,6]
+ CRUSH rule 1 x 724 [2,6,5]
+ CRUSH rule 1 x 725 [2,8,5]
+ CRUSH rule 1 x 726 [5,8,2]
+ CRUSH rule 1 x 727 [5,6,8]
+ CRUSH rule 1 x 728 [2,6,8]
+ CRUSH rule 1 x 729 [5,6,7]
+ CRUSH rule 1 x 730 [5,7,6]
+ CRUSH rule 1 x 731 [5,2,6]
+ CRUSH rule 1 x 732 [2,5,6]
+ CRUSH rule 1 x 733 [5,2,7]
+ CRUSH rule 1 x 734 [6,5,2]
+ CRUSH rule 1 x 735 [5,8,2]
+ CRUSH rule 1 x 736 [5,8,6]
+ CRUSH rule 1 x 737 [2,5,8]
+ CRUSH rule 1 x 738 [5,2,7]
+ CRUSH rule 1 x 739 [2,8,5]
+ CRUSH rule 1 x 740 [2,6,7]
+ CRUSH rule 1 x 741 [7,8,2]
+ CRUSH rule 1 x 742 [8,2,5]
+ CRUSH rule 1 x 743 [7,2,8]
+ CRUSH rule 1 x 744 [5,7,2]
+ CRUSH rule 1 x 745 [5,6,2]
+ CRUSH rule 1 x 746 [5,2,8]
+ CRUSH rule 1 x 747 [6,2,5]
+ CRUSH rule 1 x 748 [2,7,8]
+ CRUSH rule 1 x 749 [5,8,7]
+ CRUSH rule 1 x 750 [2,6,5]
+ CRUSH rule 1 x 751 [2,8,6]
+ CRUSH rule 1 x 752 [8,2,5]
+ CRUSH rule 1 x 753 [7,8,5]
+ CRUSH rule 1 x 754 [8,6,7]
+ CRUSH rule 1 x 755 [2,5]
+ CRUSH rule 1 x 756 [5,6,2]
+ CRUSH rule 1 x 757 [8,6,2]
+ CRUSH rule 1 x 758 [6,2,5]
+ CRUSH rule 1 x 759 [8,5,6]
+ CRUSH rule 1 x 760 [2,5,8]
+ CRUSH rule 1 x 761 [5,2,7]
+ CRUSH rule 1 x 762 [2,7,8]
+ CRUSH rule 1 x 763 [8,6,7]
+ CRUSH rule 1 x 764 [2,7,5]
+ CRUSH rule 1 x 765 [6,5,2]
+ CRUSH rule 1 x 766 [8,5,7]
+ CRUSH rule 1 x 767 [2,6,5]
+ CRUSH rule 1 x 768 [8,5,2]
+ CRUSH rule 1 x 769 [6,2,8]
+ CRUSH rule 1 x 770 [6,2,7]
+ CRUSH rule 1 x 771 [7,2,5]
+ CRUSH rule 1 x 772 [8,5,7]
+ CRUSH rule 1 x 773 [5,2,7]
+ CRUSH rule 1 x 774 [5,6,2]
+ CRUSH rule 1 x 775 [6,8,5]
+ CRUSH rule 1 x 776 [7,2,5]
+ CRUSH rule 1 x 777 [5,2,6]
+ CRUSH rule 1 x 778 [2,8,5]
+ CRUSH rule 1 x 779 [2,7,5]
+ CRUSH rule 1 x 780 [2,5,6]
+ CRUSH rule 1 x 781 [6,5,7]
+ CRUSH rule 1 x 782 [5,2,8]
+ CRUSH rule 1 x 783 [7,2,8]
+ CRUSH rule 1 x 784 [2,7,5]
+ CRUSH rule 1 x 785 [6,2,5]
+ CRUSH rule 1 x 786 [7,6,5]
+ CRUSH rule 1 x 787 [2,7,6]
+ CRUSH rule 1 x 788 [6,2,8]
+ CRUSH rule 1 x 789 [2,5,8]
+ CRUSH rule 1 x 790 [8,5,7]
+ CRUSH rule 1 x 791 [5,8,7]
+ CRUSH rule 1 x 792 [5,8,7]
+ CRUSH rule 1 x 793 [6,2,5]
+ CRUSH rule 1 x 794 [2,6,8]
+ CRUSH rule 1 x 795 [2,5,6]
+ CRUSH rule 1 x 796 [5,6,7]
+ CRUSH rule 1 x 797 [2,5,7]
+ CRUSH rule 1 x 798 [6,8,7]
+ CRUSH rule 1 x 799 [5,2,8]
+ CRUSH rule 1 x 800 [5,2,7]
+ CRUSH rule 1 x 801 [5,6,8]
+ CRUSH rule 1 x 802 [2,8,7]
+ CRUSH rule 1 x 803 [2,5,7]
+ CRUSH rule 1 x 804 [6,2,5]
+ CRUSH rule 1 x 805 [5,6,7]
+ CRUSH rule 1 x 806 [2,5,8]
+ CRUSH rule 1 x 807 [5,7,8]
+ CRUSH rule 1 x 808 [5,6,2]
+ CRUSH rule 1 x 809 [2,5,7]
+ CRUSH rule 1 x 810 [5,7,2]
+ CRUSH rule 1 x 811 [8,5,2]
+ CRUSH rule 1 x 812 [8,5,2]
+ CRUSH rule 1 x 813 [6,5,8]
+ CRUSH rule 1 x 814 [5,6,8]
+ CRUSH rule 1 x 815 [5,2,8]
+ CRUSH rule 1 x 816 [2,5,8]
+ CRUSH rule 1 x 817 [5,7,6]
+ CRUSH rule 1 x 818 [5,2,8]
+ CRUSH rule 1 x 819 [5,2,6]
+ CRUSH rule 1 x 820 [5,2,8]
+ CRUSH rule 1 x 821 [5,2,8]
+ CRUSH rule 1 x 822 [2,7,5]
+ CRUSH rule 1 x 823 [5,8,2]
+ CRUSH rule 1 x 824 [5,7,2]
+ CRUSH rule 1 x 825 [2,8,7]
+ CRUSH rule 1 x 826 [7,2,5]
+ CRUSH rule 1 x 827 [2,8,7]
+ CRUSH rule 1 x 828 [2,5,7]
+ CRUSH rule 1 x 829 [5,6,7]
+ CRUSH rule 1 x 830 [2,5,8]
+ CRUSH rule 1 x 831 [2,6,8]
+ CRUSH rule 1 x 832 [5,7,2]
+ CRUSH rule 1 x 833 [2,7,8]
+ CRUSH rule 1 x 834 [5,2]
+ CRUSH rule 1 x 835 [8,5,6]
+ CRUSH rule 1 x 836 [5,2]
+ CRUSH rule 1 x 837 [6,5,2]
+ CRUSH rule 1 x 838 [6,7,2]
+ CRUSH rule 1 x 839 [5,2,6]
+ CRUSH rule 1 x 840 [7,8,5]
+ CRUSH rule 1 x 841 [5,8,7]
+ CRUSH rule 1 x 842 [2,5,7]
+ CRUSH rule 1 x 843 [6,5,7]
+ CRUSH rule 1 x 844 [5,8,2]
+ CRUSH rule 1 x 845 [5,8,6]
+ CRUSH rule 1 x 846 [5,2,7]
+ CRUSH rule 1 x 847 [2,8,7]
+ CRUSH rule 1 x 848 [2,6,8]
+ CRUSH rule 1 x 849 [5,8,2]
+ CRUSH rule 1 x 850 [2,5,6]
+ CRUSH rule 1 x 851 [6,8,7]
+ CRUSH rule 1 x 852 [7,5,8]
+ CRUSH rule 1 x 853 [6,8,2]
+ CRUSH rule 1 x 854 [7,6,2]
+ CRUSH rule 1 x 855 [5,7,2]
+ CRUSH rule 1 x 856 [6,7,5]
+ CRUSH rule 1 x 857 [8,5,2]
+ CRUSH rule 1 x 858 [6,5,2]
+ CRUSH rule 1 x 859 [6,2,7]
+ CRUSH rule 1 x 860 [5,2,6]
+ CRUSH rule 1 x 861 [8,7,6]
+ CRUSH rule 1 x 862 [6,2,7]
+ CRUSH rule 1 x 863 [8,7,2]
+ CRUSH rule 1 x 864 [5,6,8]
+ CRUSH rule 1 x 865 [8,2,6]
+ CRUSH rule 1 x 866 [5,8,7]
+ CRUSH rule 1 x 867 [6,5,2]
+ CRUSH rule 1 x 868 [6,5,2]
+ CRUSH rule 1 x 869 [8,7,5]
+ CRUSH rule 1 x 870 [2,5,8]
+ CRUSH rule 1 x 871 [5,7,8]
+ CRUSH rule 1 x 872 [5,2,7]
+ CRUSH rule 1 x 873 [5,6,7]
+ CRUSH rule 1 x 874 [2,6,7]
+ CRUSH rule 1 x 875 [2,6,5]
+ CRUSH rule 1 x 876 [5,8,2]
+ CRUSH rule 1 x 877 [6,5,2]
+ CRUSH rule 1 x 878 [5,2]
+ CRUSH rule 1 x 879 [7,5,8]
+ CRUSH rule 1 x 880 [5,2,6]
+ CRUSH rule 1 x 881 [5,6,2]
+ CRUSH rule 1 x 882 [5,2]
+ CRUSH rule 1 x 883 [2,7,5]
+ CRUSH rule 1 x 884 [6,2,5]
+ CRUSH rule 1 x 885 [5,2,6]
+ CRUSH rule 1 x 886 [5,6,8]
+ CRUSH rule 1 x 887 [7,5,2]
+ CRUSH rule 1 x 888 [6,8,2]
+ CRUSH rule 1 x 889 [2,5,7]
+ CRUSH rule 1 x 890 [7,2,6]
+ CRUSH rule 1 x 891 [2,8,5]
+ CRUSH rule 1 x 892 [6,2,5]
+ CRUSH rule 1 x 893 [2,5,7]
+ CRUSH rule 1 x 894 [7,5,2]
+ CRUSH rule 1 x 895 [5,7,2]
+ CRUSH rule 1 x 896 [2,8,6]
+ CRUSH rule 1 x 897 [5,2,6]
+ CRUSH rule 1 x 898 [2,5,7]
+ CRUSH rule 1 x 899 [2,7,6]
+ CRUSH rule 1 x 900 [5,2,6]
+ CRUSH rule 1 x 901 [5,2,7]
+ CRUSH rule 1 x 902 [8,5,7]
+ CRUSH rule 1 x 903 [5,7,2]
+ CRUSH rule 1 x 904 [5,6,8]
+ CRUSH rule 1 x 905 [6,2,5]
+ CRUSH rule 1 x 906 [2,5,7]
+ CRUSH rule 1 x 907 [7,2]
+ CRUSH rule 1 x 908 [5,8,2]
+ CRUSH rule 1 x 909 [2,5,8]
+ CRUSH rule 1 x 910 [6,5,2]
+ CRUSH rule 1 x 911 [5,8,2]
+ CRUSH rule 1 x 912 [2,6,7]
+ CRUSH rule 1 x 913 [7,6,8]
+ CRUSH rule 1 x 914 [6,5,7]
+ CRUSH rule 1 x 915 [8,2,6]
+ CRUSH rule 1 x 916 [5,2,7]
+ CRUSH rule 1 x 917 [2,5,6]
+ CRUSH rule 1 x 918 [8,2,7]
+ CRUSH rule 1 x 919 [6,2,8]
+ CRUSH rule 1 x 920 [7,6,5]
+ CRUSH rule 1 x 921 [2,5,7]
+ CRUSH rule 1 x 922 [6,7,8]
+ CRUSH rule 1 x 923 [5,8,6]
+ CRUSH rule 1 x 924 [5,6,2]
+ CRUSH rule 1 x 925 [5,7,2]
+ CRUSH rule 1 x 926 [5,8,2]
+ CRUSH rule 1 x 927 [2,6,5]
+ CRUSH rule 1 x 928 [8,2,5]
+ CRUSH rule 1 x 929 [5,2]
+ CRUSH rule 1 x 930 [2,5,6]
+ CRUSH rule 1 x 931 [5,2]
+ CRUSH rule 1 x 932 [5,8,2]
+ CRUSH rule 1 x 933 [8,5,2]
+ CRUSH rule 1 x 934 [5,2,8]
+ CRUSH rule 1 x 935 [6,5,2]
+ CRUSH rule 1 x 936 [2,6,7]
+ CRUSH rule 1 x 937 [5,6,7]
+ CRUSH rule 1 x 938 [6,5,8]
+ CRUSH rule 1 x 939 [2,7,6]
+ CRUSH rule 1 x 940 [8,7,6]
+ CRUSH rule 1 x 941 [5,2,8]
+ CRUSH rule 1 x 942 [2,8,7]
+ CRUSH rule 1 x 943 [8,2,5]
+ CRUSH rule 1 x 944 [5,2,7]
+ CRUSH rule 1 x 945 [7,2,5]
+ CRUSH rule 1 x 946 [2,5,7]
+ CRUSH rule 1 x 947 [5,6,2]
+ CRUSH rule 1 x 948 [7,8,6]
+ CRUSH rule 1 x 949 [6,2,7]
+ CRUSH rule 1 x 950 [5,7,8]
+ CRUSH rule 1 x 951 [5,8,7]
+ CRUSH rule 1 x 952 [2,5,7]
+ CRUSH rule 1 x 953 [2,5,6]
+ CRUSH rule 1 x 954 [5,2,7]
+ CRUSH rule 1 x 955 [8,6,2]
+ CRUSH rule 1 x 956 [2,8,6]
+ CRUSH rule 1 x 957 [7,6,2]
+ CRUSH rule 1 x 958 [8,7,5]
+ CRUSH rule 1 x 959 [5,2,7]
+ CRUSH rule 1 x 960 [5,6,7]
+ CRUSH rule 1 x 961 [5,2,6]
+ CRUSH rule 1 x 962 [7,5,2]
+ CRUSH rule 1 x 963 [2,5,8]
+ CRUSH rule 1 x 964 [5,2,6]
+ CRUSH rule 1 x 965 [7,6,5]
+ CRUSH rule 1 x 966 [5,8,6]
+ CRUSH rule 1 x 967 [8,6,5]
+ CRUSH rule 1 x 968 [7,2,5]
+ CRUSH rule 1 x 969 [8,2,6]
+ CRUSH rule 1 x 970 [2,6,5]
+ CRUSH rule 1 x 971 [2,7,8]
+ CRUSH rule 1 x 972 [2,8,5]
+ CRUSH rule 1 x 973 [2,6,5]
+ CRUSH rule 1 x 974 [5,2,7]
+ CRUSH rule 1 x 975 [5,7,8]
+ CRUSH rule 1 x 976 [5,6,7]
+ CRUSH rule 1 x 977 [8,5,2]
+ CRUSH rule 1 x 978 [7,2,8]
+ CRUSH rule 1 x 979 [7,6,2]
+ CRUSH rule 1 x 980 [6,2,7]
+ CRUSH rule 1 x 981 [7,5,2]
+ CRUSH rule 1 x 982 [5,2,6]
+ CRUSH rule 1 x 983 [5,6,8]
+ CRUSH rule 1 x 984 [2,8,5]
+ CRUSH rule 1 x 985 [2,5,6]
+ CRUSH rule 1 x 986 [8,7,5]
+ CRUSH rule 1 x 987 [2,5,8]
+ CRUSH rule 1 x 988 [2,5,6]
+ CRUSH rule 1 x 989 [2,6,5]
+ CRUSH rule 1 x 990 [2,5,8]
+ CRUSH rule 1 x 991 [2,5,8]
+ CRUSH rule 1 x 992 [7,2,5]
+ CRUSH rule 1 x 993 [2,6,5]
+ CRUSH rule 1 x 994 [5,2,8]
+ CRUSH rule 1 x 995 [7,6,2]
+ CRUSH rule 1 x 996 [6,7,5]
+ CRUSH rule 1 x 997 [6,5,2]
+ CRUSH rule 1 x 998 [8,2,5]
+ CRUSH rule 1 x 999 [2,7,8]
+ CRUSH rule 1 x 1000 [8,5,2]
+ CRUSH rule 1 x 1001 [2,5]
+ CRUSH rule 1 x 1002 [2,5,7]
+ CRUSH rule 1 x 1003 [2,8,7]
+ CRUSH rule 1 x 1004 [6,2,8]
+ CRUSH rule 1 x 1005 [6,2,8]
+ CRUSH rule 1 x 1006 [2,5]
+ CRUSH rule 1 x 1007 [2,8,5]
+ CRUSH rule 1 x 1008 [2,7,5]
+ CRUSH rule 1 x 1009 [6,8,5]
+ CRUSH rule 1 x 1010 [5,6,2]
+ CRUSH rule 1 x 1011 [5,2,7]
+ CRUSH rule 1 x 1012 [5,2,7]
+ CRUSH rule 1 x 1013 [5,2,7]
+ CRUSH rule 1 x 1014 [2,8,5]
+ CRUSH rule 1 x 1015 [6,8,5]
+ CRUSH rule 1 x 1016 [2,6,5]
+ CRUSH rule 1 x 1017 [6,2,5]
+ CRUSH rule 1 x 1018 [5,2,7]
+ CRUSH rule 1 x 1019 [5,7,8]
+ CRUSH rule 1 x 1020 [5,2,7]
+ CRUSH rule 1 x 1021 [5,2,8]
+ CRUSH rule 1 x 1022 [2,6,7]
+ CRUSH rule 1 x 1023 [5,2,8]
+ rule 1 (choose-two) num_rep 3 result size == 2:\t32/1024 (esc)
+ rule 1 (choose-two) num_rep 3 result size == 3:\t992/1024 (esc)
+ rule 2 (chooseleaf), x = 0..1023, numrep = 2..3
+ CRUSH rule 2 x 0 [2,5]
+ CRUSH rule 2 x 1 [2,8]
+ CRUSH rule 2 x 2 [2,5]
+ CRUSH rule 2 x 3 [8,2]
+ CRUSH rule 2 x 4 [5,2]
+ CRUSH rule 2 x 5 [7,2]
+ CRUSH rule 2 x 6 [2,6]
+ CRUSH rule 2 x 7 [5,8]
+ CRUSH rule 2 x 8 [5,6]
+ CRUSH rule 2 x 9 [2,5]
+ CRUSH rule 2 x 10 [2,7]
+ CRUSH rule 2 x 11 [2,7]
+ CRUSH rule 2 x 12 [2,5]
+ CRUSH rule 2 x 13 [5,8]
+ CRUSH rule 2 x 14 [7,2]
+ CRUSH rule 2 x 15 [7,2]
+ CRUSH rule 2 x 16 [5,6]
+ CRUSH rule 2 x 17 [5,2]
+ CRUSH rule 2 x 18 [2,5]
+ CRUSH rule 2 x 19 [7,5]
+ CRUSH rule 2 x 20 [2,5]
+ CRUSH rule 2 x 21 [5,7]
+ CRUSH rule 2 x 22 [8,5]
+ CRUSH rule 2 x 23 [5,6]
+ CRUSH rule 2 x 24 [2,7]
+ CRUSH rule 2 x 25 [5,7]
+ CRUSH rule 2 x 26 [2,8]
+ CRUSH rule 2 x 27 [5,2]
+ CRUSH rule 2 x 28 [6,2]
+ CRUSH rule 2 x 29 [8,5]
+ CRUSH rule 2 x 30 [5,7]
+ CRUSH rule 2 x 31 [8,2]
+ CRUSH rule 2 x 32 [5,6]
+ CRUSH rule 2 x 33 [2,7]
+ CRUSH rule 2 x 34 [2,5]
+ CRUSH rule 2 x 35 [2,8]
+ CRUSH rule 2 x 36 [5,8]
+ CRUSH rule 2 x 37 [2,5]
+ CRUSH rule 2 x 38 [5,8]
+ CRUSH rule 2 x 39 [5,7]
+ CRUSH rule 2 x 40 [7,2]
+ CRUSH rule 2 x 41 [2,6]
+ CRUSH rule 2 x 42 [5,6]
+ CRUSH rule 2 x 43 [2,5]
+ CRUSH rule 2 x 44 [2,6]
+ CRUSH rule 2 x 45 [8,2]
+ CRUSH rule 2 x 46 [2,5]
+ CRUSH rule 2 x 47 [5,2]
+ CRUSH rule 2 x 48 [5,6]
+ CRUSH rule 2 x 49 [5,7]
+ CRUSH rule 2 x 50 [5,2]
+ CRUSH rule 2 x 51 [5,6]
+ CRUSH rule 2 x 52 [8,2]
+ CRUSH rule 2 x 53 [5,8]
+ CRUSH rule 2 x 54 [7,5]
+ CRUSH rule 2 x 55 [8,2]
+ CRUSH rule 2 x 56 [6,5]
+ CRUSH rule 2 x 57 [5,8]
+ CRUSH rule 2 x 58 [2,8]
+ CRUSH rule 2 x 59 [5,2]
+ CRUSH rule 2 x 60 [5,2]
+ CRUSH rule 2 x 61 [5,6]
+ CRUSH rule 2 x 62 [7,2]
+ CRUSH rule 2 x 63 [5,6]
+ CRUSH rule 2 x 64 [5,2]
+ CRUSH rule 2 x 65 [7,5]
+ CRUSH rule 2 x 66 [5,6]
+ CRUSH rule 2 x 67 [5,2]
+ CRUSH rule 2 x 68 [2,5]
+ CRUSH rule 2 x 69 [5,2]
+ CRUSH rule 2 x 70 [7,2]
+ CRUSH rule 2 x 71 [2,8]
+ CRUSH rule 2 x 72 [6,2]
+ CRUSH rule 2 x 73 [2,7]
+ CRUSH rule 2 x 74 [2,7]
+ CRUSH rule 2 x 75 [5,2]
+ CRUSH rule 2 x 76 [5,2]
+ CRUSH rule 2 x 77 [7,2]
+ CRUSH rule 2 x 78 [2,5]
+ CRUSH rule 2 x 79 [5,2]
+ CRUSH rule 2 x 80 [2,5]
+ CRUSH rule 2 x 81 [2,5]
+ CRUSH rule 2 x 82 [7,2]
+ CRUSH rule 2 x 83 [2,6]
+ CRUSH rule 2 x 84 [7,2]
+ CRUSH rule 2 x 85 [5,8]
+ CRUSH rule 2 x 86 [2,6]
+ CRUSH rule 2 x 87 [2,7]
+ CRUSH rule 2 x 88 [2,6]
+ CRUSH rule 2 x 89 [5,2]
+ CRUSH rule 2 x 90 [6,5]
+ CRUSH rule 2 x 91 [5,8]
+ CRUSH rule 2 x 92 [2,8]
+ CRUSH rule 2 x 93 [7,5]
+ CRUSH rule 2 x 94 [2,5]
+ CRUSH rule 2 x 95 [7,5]
+ CRUSH rule 2 x 96 [5,6]
+ CRUSH rule 2 x 97 [8,5]
+ CRUSH rule 2 x 98 [2,7]
+ CRUSH rule 2 x 99 [2,7]
+ CRUSH rule 2 x 100 [2,7]
+ CRUSH rule 2 x 101 [5,7]
+ CRUSH rule 2 x 102 [5,2]
+ CRUSH rule 2 x 103 [5,7]
+ CRUSH rule 2 x 104 [7,5]
+ CRUSH rule 2 x 105 [2,5]
+ CRUSH rule 2 x 106 [2,6]
+ CRUSH rule 2 x 107 [5,2]
+ CRUSH rule 2 x 108 [7,2]
+ CRUSH rule 2 x 109 [2,5]
+ CRUSH rule 2 x 110 [5,2]
+ CRUSH rule 2 x 111 [2,5]
+ CRUSH rule 2 x 112 [2,6]
+ CRUSH rule 2 x 113 [6,2]
+ CRUSH rule 2 x 114 [7,5]
+ CRUSH rule 2 x 115 [8,2]
+ CRUSH rule 2 x 116 [2,6]
+ CRUSH rule 2 x 117 [7,5]
+ CRUSH rule 2 x 118 [2,5]
+ CRUSH rule 2 x 119 [5,6]
+ CRUSH rule 2 x 120 [2,5]
+ CRUSH rule 2 x 121 [2,7]
+ CRUSH rule 2 x 122 [8,5]
+ CRUSH rule 2 x 123 [2,5]
+ CRUSH rule 2 x 124 [5,2]
+ CRUSH rule 2 x 125 [2,7]
+ CRUSH rule 2 x 126 [5,2]
+ CRUSH rule 2 x 127 [5,6]
+ CRUSH rule 2 x 128 [5,6]
+ CRUSH rule 2 x 129 [2,5]
+ CRUSH rule 2 x 130 [5,8]
+ CRUSH rule 2 x 131 [2,5]
+ CRUSH rule 2 x 132 [2,5]
+ CRUSH rule 2 x 133 [5,6]
+ CRUSH rule 2 x 134 [2,8]
+ CRUSH rule 2 x 135 [5,6]
+ CRUSH rule 2 x 136 [2,5]
+ CRUSH rule 2 x 137 [7,5]
+ CRUSH rule 2 x 138 [8,5]
+ CRUSH rule 2 x 139 [5,2]
+ CRUSH rule 2 x 140 [2,6]
+ CRUSH rule 2 x 141 [6,2]
+ CRUSH rule 2 x 142 [5,2]
+ CRUSH rule 2 x 143 [5,8]
+ CRUSH rule 2 x 144 [8,2]
+ CRUSH rule 2 x 145 [8,5]
+ CRUSH rule 2 x 146 [2,6]
+ CRUSH rule 2 x 147 [2,8]
+ CRUSH rule 2 x 148 [5,2]
+ CRUSH rule 2 x 149 [5,8]
+ CRUSH rule 2 x 150 [2,6]
+ CRUSH rule 2 x 151 [5,6]
+ CRUSH rule 2 x 152 [8,5]
+ CRUSH rule 2 x 153 [8,5]
+ CRUSH rule 2 x 154 [5,2]
+ CRUSH rule 2 x 155 [5,7]
+ CRUSH rule 2 x 156 [5,2]
+ CRUSH rule 2 x 157 [5,2]
+ CRUSH rule 2 x 158 [2,8]
+ CRUSH rule 2 x 159 [7,2]
+ CRUSH rule 2 x 160 [2,8]
+ CRUSH rule 2 x 161 [2,5]
+ CRUSH rule 2 x 162 [2,6]
+ CRUSH rule 2 x 163 [5,6]
+ CRUSH rule 2 x 164 [7,2]
+ CRUSH rule 2 x 165 [7,2]
+ CRUSH rule 2 x 166 [2,5]
+ CRUSH rule 2 x 167 [2,7]
+ CRUSH rule 2 x 168 [5,2]
+ CRUSH rule 2 x 169 [2,6]
+ CRUSH rule 2 x 170 [2,5]
+ CRUSH rule 2 x 171 [7,5]
+ CRUSH rule 2 x 172 [2,7]
+ CRUSH rule 2 x 173 [8,5]
+ CRUSH rule 2 x 174 [2,5]
+ CRUSH rule 2 x 175 [6,2]
+ CRUSH rule 2 x 176 [5,2]
+ CRUSH rule 2 x 177 [5,2]
+ CRUSH rule 2 x 178 [5,2]
+ CRUSH rule 2 x 179 [5,2]
+ CRUSH rule 2 x 180 [5,8]
+ CRUSH rule 2 x 181 [6,2]
+ CRUSH rule 2 x 182 [8,5]
+ CRUSH rule 2 x 183 [7,5]
+ CRUSH rule 2 x 184 [5,7]
+ CRUSH rule 2 x 185 [6,2]
+ CRUSH rule 2 x 186 [2,5]
+ CRUSH rule 2 x 187 [2,6]
+ CRUSH rule 2 x 188 [2,8]
+ CRUSH rule 2 x 189 [2,7]
+ CRUSH rule 2 x 190 [5,2]
+ CRUSH rule 2 x 191 [7,2]
+ CRUSH rule 2 x 192 [5,2]
+ CRUSH rule 2 x 193 [5,2]
+ CRUSH rule 2 x 194 [2,5]
+ CRUSH rule 2 x 195 [6,5]
+ CRUSH rule 2 x 196 [6,2]
+ CRUSH rule 2 x 197 [6,5]
+ CRUSH rule 2 x 198 [2,5]
+ CRUSH rule 2 x 199 [2,5]
+ CRUSH rule 2 x 200 [2,5]
+ CRUSH rule 2 x 201 [7,2]
+ CRUSH rule 2 x 202 [6,5]
+ CRUSH rule 2 x 203 [5,8]
+ CRUSH rule 2 x 204 [2,5]
+ CRUSH rule 2 x 205 [2,7]
+ CRUSH rule 2 x 206 [2,7]
+ CRUSH rule 2 x 207 [5,2]
+ CRUSH rule 2 x 208 [7,2]
+ CRUSH rule 2 x 209 [2,8]
+ CRUSH rule 2 x 210 [2,5]
+ CRUSH rule 2 x 211 [5,2]
+ CRUSH rule 2 x 212 [7,5]
+ CRUSH rule 2 x 213 [8,5]
+ CRUSH rule 2 x 214 [5,8]
+ CRUSH rule 2 x 215 [8,2]
+ CRUSH rule 2 x 216 [5,2]
+ CRUSH rule 2 x 217 [2,7]
+ CRUSH rule 2 x 218 [2,7]
+ CRUSH rule 2 x 219 [5,8]
+ CRUSH rule 2 x 220 [5,7]
+ CRUSH rule 2 x 221 [5,6]
+ CRUSH rule 2 x 222 [6,5]
+ CRUSH rule 2 x 223 [2,5]
+ CRUSH rule 2 x 224 [2,5]
+ CRUSH rule 2 x 225 [8,2]
+ CRUSH rule 2 x 226 [7,2]
+ CRUSH rule 2 x 227 [5,2]
+ CRUSH rule 2 x 228 [5,6]
+ CRUSH rule 2 x 229 [5,8]
+ CRUSH rule 2 x 230 [5,7]
+ CRUSH rule 2 x 231 [5,7]
+ CRUSH rule 2 x 232 [2,7]
+ CRUSH rule 2 x 233 [5,7]
+ CRUSH rule 2 x 234 [2,5]
+ CRUSH rule 2 x 235 [5,8]
+ CRUSH rule 2 x 236 [5,2]
+ CRUSH rule 2 x 237 [5,7]
+ CRUSH rule 2 x 238 [5,2]
+ CRUSH rule 2 x 239 [8,5]
+ CRUSH rule 2 x 240 [5,7]
+ CRUSH rule 2 x 241 [5,2]
+ CRUSH rule 2 x 242 [5,2]
+ CRUSH rule 2 x 243 [5,7]
+ CRUSH rule 2 x 244 [5,6]
+ CRUSH rule 2 x 245 [7,2]
+ CRUSH rule 2 x 246 [2,5]
+ CRUSH rule 2 x 247 [6,2]
+ CRUSH rule 2 x 248 [8,2]
+ CRUSH rule 2 x 249 [2,5]
+ CRUSH rule 2 x 250 [2,5]
+ CRUSH rule 2 x 251 [2,5]
+ CRUSH rule 2 x 252 [5,7]
+ CRUSH rule 2 x 253 [5,2]
+ CRUSH rule 2 x 254 [5,2]
+ CRUSH rule 2 x 255 [2,7]
+ CRUSH rule 2 x 256 [5,7]
+ CRUSH rule 2 x 257 [2,8]
+ CRUSH rule 2 x 258 [5,2]
+ CRUSH rule 2 x 259 [5,6]
+ CRUSH rule 2 x 260 [5,6]
+ CRUSH rule 2 x 261 [8,5]
+ CRUSH rule 2 x 262 [5,6]
+ CRUSH rule 2 x 263 [6,2]
+ CRUSH rule 2 x 264 [5,6]
+ CRUSH rule 2 x 265 [8,5]
+ CRUSH rule 2 x 266 [8,2]
+ CRUSH rule 2 x 267 [2,5]
+ CRUSH rule 2 x 268 [2,7]
+ CRUSH rule 2 x 269 [2,8]
+ CRUSH rule 2 x 270 [5,2]
+ CRUSH rule 2 x 271 [7,5]
+ CRUSH rule 2 x 272 [2,8]
+ CRUSH rule 2 x 273 [5,2]
+ CRUSH rule 2 x 274 [6,5]
+ CRUSH rule 2 x 275 [5,7]
+ CRUSH rule 2 x 276 [7,2]
+ CRUSH rule 2 x 277 [6,5]
+ CRUSH rule 2 x 278 [6,2]
+ CRUSH rule 2 x 279 [8,5]
+ CRUSH rule 2 x 280 [2,6]
+ CRUSH rule 2 x 281 [8,2]
+ CRUSH rule 2 x 282 [5,2]
+ CRUSH rule 2 x 283 [8,2]
+ CRUSH rule 2 x 284 [6,5]
+ CRUSH rule 2 x 285 [5,7]
+ CRUSH rule 2 x 286 [2,6]
+ CRUSH rule 2 x 287 [2,5]
+ CRUSH rule 2 x 288 [8,2]
+ CRUSH rule 2 x 289 [5,6]
+ CRUSH rule 2 x 290 [2,5]
+ CRUSH rule 2 x 291 [2,5]
+ CRUSH rule 2 x 292 [8,2]
+ CRUSH rule 2 x 293 [6,2]
+ CRUSH rule 2 x 294 [7,5]
+ CRUSH rule 2 x 295 [5,8]
+ CRUSH rule 2 x 296 [5,2]
+ CRUSH rule 2 x 297 [6,2]
+ CRUSH rule 2 x 298 [2,5]
+ CRUSH rule 2 x 299 [2,8]
+ CRUSH rule 2 x 300 [8,5]
+ CRUSH rule 2 x 301 [2,8]
+ CRUSH rule 2 x 302 [5,2]
+ CRUSH rule 2 x 303 [7,5]
+ CRUSH rule 2 x 304 [2,7]
+ CRUSH rule 2 x 305 [5,8]
+ CRUSH rule 2 x 306 [2,7]
+ CRUSH rule 2 x 307 [2,7]
+ CRUSH rule 2 x 308 [2,8]
+ CRUSH rule 2 x 309 [7,5]
+ CRUSH rule 2 x 310 [5,2]
+ CRUSH rule 2 x 311 [5,6]
+ CRUSH rule 2 x 312 [2,6]
+ CRUSH rule 2 x 313 [5,2]
+ CRUSH rule 2 x 314 [5,2]
+ CRUSH rule 2 x 315 [2,5]
+ CRUSH rule 2 x 316 [6,5]
+ CRUSH rule 2 x 317 [2,6]
+ CRUSH rule 2 x 318 [8,2]
+ CRUSH rule 2 x 319 [5,2]
+ CRUSH rule 2 x 320 [5,7]
+ CRUSH rule 2 x 321 [2,5]
+ CRUSH rule 2 x 322 [2,7]
+ CRUSH rule 2 x 323 [5,7]
+ CRUSH rule 2 x 324 [7,2]
+ CRUSH rule 2 x 325 [5,6]
+ CRUSH rule 2 x 326 [5,2]
+ CRUSH rule 2 x 327 [2,6]
+ CRUSH rule 2 x 328 [7,5]
+ CRUSH rule 2 x 329 [5,6]
+ CRUSH rule 2 x 330 [5,7]
+ CRUSH rule 2 x 331 [2,6]
+ CRUSH rule 2 x 332 [2,5]
+ CRUSH rule 2 x 333 [6,5]
+ CRUSH rule 2 x 334 [8,5]
+ CRUSH rule 2 x 335 [7,2]
+ CRUSH rule 2 x 336 [5,6]
+ CRUSH rule 2 x 337 [7,2]
+ CRUSH rule 2 x 338 [5,6]
+ CRUSH rule 2 x 339 [7,5]
+ CRUSH rule 2 x 340 [2,8]
+ CRUSH rule 2 x 341 [5,2]
+ CRUSH rule 2 x 342 [2,7]
+ CRUSH rule 2 x 343 [6,5]
+ CRUSH rule 2 x 344 [6,2]
+ CRUSH rule 2 x 345 [5,7]
+ CRUSH rule 2 x 346 [8,2]
+ CRUSH rule 2 x 347 [5,2]
+ CRUSH rule 2 x 348 [8,2]
+ CRUSH rule 2 x 349 [2,6]
+ CRUSH rule 2 x 350 [8,5]
+ CRUSH rule 2 x 351 [5,6]
+ CRUSH rule 2 x 352 [2,8]
+ CRUSH rule 2 x 353 [6,5]
+ CRUSH rule 2 x 354 [2,5]
+ CRUSH rule 2 x 355 [5,8]
+ CRUSH rule 2 x 356 [5,2]
+ CRUSH rule 2 x 357 [6,2]
+ CRUSH rule 2 x 358 [2,8]
+ CRUSH rule 2 x 359 [6,2]
+ CRUSH rule 2 x 360 [5,2]
+ CRUSH rule 2 x 361 [8,5]
+ CRUSH rule 2 x 362 [5,2]
+ CRUSH rule 2 x 363 [5,2]
+ CRUSH rule 2 x 364 [2,5]
+ CRUSH rule 2 x 365 [6,5]
+ CRUSH rule 2 x 366 [7,2]
+ CRUSH rule 2 x 367 [5,2]
+ CRUSH rule 2 x 368 [7,5]
+ CRUSH rule 2 x 369 [5,7]
+ CRUSH rule 2 x 370 [8,2]
+ CRUSH rule 2 x 371 [2,5]
+ CRUSH rule 2 x 372 [5,2]
+ CRUSH rule 2 x 373 [2,6]
+ CRUSH rule 2 x 374 [5,8]
+ CRUSH rule 2 x 375 [6,5]
+ CRUSH rule 2 x 376 [7,2]
+ CRUSH rule 2 x 377 [2,5]
+ CRUSH rule 2 x 378 [2,8]
+ CRUSH rule 2 x 379 [8,5]
+ CRUSH rule 2 x 380 [2,5]
+ CRUSH rule 2 x 381 [2,5]
+ CRUSH rule 2 x 382 [2,5]
+ CRUSH rule 2 x 383 [5,6]
+ CRUSH rule 2 x 384 [7,2]
+ CRUSH rule 2 x 385 [7,5]
+ CRUSH rule 2 x 386 [2,5]
+ CRUSH rule 2 x 387 [2,5]
+ CRUSH rule 2 x 388 [5,2]
+ CRUSH rule 2 x 389 [2,5]
+ CRUSH rule 2 x 390 [5,6]
+ CRUSH rule 2 x 391 [5,6]
+ CRUSH rule 2 x 392 [2,8]
+ CRUSH rule 2 x 393 [5,2]
+ CRUSH rule 2 x 394 [5,7]
+ CRUSH rule 2 x 395 [5,2]
+ CRUSH rule 2 x 396 [5,2]
+ CRUSH rule 2 x 397 [2,5]
+ CRUSH rule 2 x 398 [2,5]
+ CRUSH rule 2 x 399 [8,5]
+ CRUSH rule 2 x 400 [8,2]
+ CRUSH rule 2 x 401 [2,5]
+ CRUSH rule 2 x 402 [7,5]
+ CRUSH rule 2 x 403 [2,5]
+ CRUSH rule 2 x 404 [5,2]
+ CRUSH rule 2 x 405 [6,5]
+ CRUSH rule 2 x 406 [2,6]
+ CRUSH rule 2 x 407 [2,8]
+ CRUSH rule 2 x 408 [5,2]
+ CRUSH rule 2 x 409 [7,5]
+ CRUSH rule 2 x 410 [8,5]
+ CRUSH rule 2 x 411 [2,8]
+ CRUSH rule 2 x 412 [2,5]
+ CRUSH rule 2 x 413 [5,2]
+ CRUSH rule 2 x 414 [5,2]
+ CRUSH rule 2 x 415 [2,6]
+ CRUSH rule 2 x 416 [2,6]
+ CRUSH rule 2 x 417 [8,2]
+ CRUSH rule 2 x 418 [7,2]
+ CRUSH rule 2 x 419 [8,5]
+ CRUSH rule 2 x 420 [2,5]
+ CRUSH rule 2 x 421 [8,5]
+ CRUSH rule 2 x 422 [6,5]
+ CRUSH rule 2 x 423 [2,5]
+ CRUSH rule 2 x 424 [8,5]
+ CRUSH rule 2 x 425 [2,5]
+ CRUSH rule 2 x 426 [6,2]
+ CRUSH rule 2 x 427 [2,7]
+ CRUSH rule 2 x 428 [5,7]
+ CRUSH rule 2 x 429 [5,6]
+ CRUSH rule 2 x 430 [5,6]
+ CRUSH rule 2 x 431 [5,2]
+ CRUSH rule 2 x 432 [7,2]
+ CRUSH rule 2 x 433 [6,5]
+ CRUSH rule 2 x 434 [5,2]
+ CRUSH rule 2 x 435 [2,5]
+ CRUSH rule 2 x 436 [5,2]
+ CRUSH rule 2 x 437 [7,5]
+ CRUSH rule 2 x 438 [2,5]
+ CRUSH rule 2 x 439 [2,5]
+ CRUSH rule 2 x 440 [2,7]
+ CRUSH rule 2 x 441 [5,7]
+ CRUSH rule 2 x 442 [2,5]
+ CRUSH rule 2 x 443 [6,2]
+ CRUSH rule 2 x 444 [7,2]
+ CRUSH rule 2 x 445 [6,5]
+ CRUSH rule 2 x 446 [5,2]
+ CRUSH rule 2 x 447 [2,5]
+ CRUSH rule 2 x 448 [7,2]
+ CRUSH rule 2 x 449 [7,5]
+ CRUSH rule 2 x 450 [5,2]
+ CRUSH rule 2 x 451 [6,5]
+ CRUSH rule 2 x 452 [8,5]
+ CRUSH rule 2 x 453 [6,5]
+ CRUSH rule 2 x 454 [6,5]
+ CRUSH rule 2 x 455 [2,7]
+ CRUSH rule 2 x 456 [6,2]
+ CRUSH rule 2 x 457 [7,2]
+ CRUSH rule 2 x 458 [2,8]
+ CRUSH rule 2 x 459 [2,7]
+ CRUSH rule 2 x 460 [6,5]
+ CRUSH rule 2 x 461 [6,5]
+ CRUSH rule 2 x 462 [8,2]
+ CRUSH rule 2 x 463 [6,2]
+ CRUSH rule 2 x 464 [7,5]
+ CRUSH rule 2 x 465 [7,2]
+ CRUSH rule 2 x 466 [5,8]
+ CRUSH rule 2 x 467 [6,5]
+ CRUSH rule 2 x 468 [7,2]
+ CRUSH rule 2 x 469 [7,2]
+ CRUSH rule 2 x 470 [5,2]
+ CRUSH rule 2 x 471 [2,7]
+ CRUSH rule 2 x 472 [5,2]
+ CRUSH rule 2 x 473 [2,5]
+ CRUSH rule 2 x 474 [6,2]
+ CRUSH rule 2 x 475 [6,2]
+ CRUSH rule 2 x 476 [5,6]
+ CRUSH rule 2 x 477 [5,8]
+ CRUSH rule 2 x 478 [6,2]
+ CRUSH rule 2 x 479 [2,5]
+ CRUSH rule 2 x 480 [2,8]
+ CRUSH rule 2 x 481 [2,5]
+ CRUSH rule 2 x 482 [5,7]
+ CRUSH rule 2 x 483 [2,6]
+ CRUSH rule 2 x 484 [2,7]
+ CRUSH rule 2 x 485 [5,7]
+ CRUSH rule 2 x 486 [5,2]
+ CRUSH rule 2 x 487 [5,2]
+ CRUSH rule 2 x 488 [5,7]
+ CRUSH rule 2 x 489 [2,8]
+ CRUSH rule 2 x 490 [6,5]
+ CRUSH rule 2 x 491 [2,7]
+ CRUSH rule 2 x 492 [6,5]
+ CRUSH rule 2 x 493 [2,7]
+ CRUSH rule 2 x 494 [2,7]
+ CRUSH rule 2 x 495 [5,2]
+ CRUSH rule 2 x 496 [7,5]
+ CRUSH rule 2 x 497 [5,7]
+ CRUSH rule 2 x 498 [2,5]
+ CRUSH rule 2 x 499 [8,5]
+ CRUSH rule 2 x 500 [5,6]
+ CRUSH rule 2 x 501 [2,7]
+ CRUSH rule 2 x 502 [7,2]
+ CRUSH rule 2 x 503 [2,5]
+ CRUSH rule 2 x 504 [5,6]
+ CRUSH rule 2 x 505 [2,7]
+ CRUSH rule 2 x 506 [5,2]
+ CRUSH rule 2 x 507 [6,2]
+ CRUSH rule 2 x 508 [2,5]
+ CRUSH rule 2 x 509 [7,5]
+ CRUSH rule 2 x 510 [6,2]
+ CRUSH rule 2 x 511 [5,8]
+ CRUSH rule 2 x 512 [7,2]
+ CRUSH rule 2 x 513 [7,2]
+ CRUSH rule 2 x 514 [5,6]
+ CRUSH rule 2 x 515 [8,5]
+ CRUSH rule 2 x 516 [5,2]
+ CRUSH rule 2 x 517 [7,2]
+ CRUSH rule 2 x 518 [5,6]
+ CRUSH rule 2 x 519 [7,5]
+ CRUSH rule 2 x 520 [2,6]
+ CRUSH rule 2 x 521 [8,2]
+ CRUSH rule 2 x 522 [6,2]
+ CRUSH rule 2 x 523 [5,2]
+ CRUSH rule 2 x 524 [2,5]
+ CRUSH rule 2 x 525 [2,5]
+ CRUSH rule 2 x 526 [2,5]
+ CRUSH rule 2 x 527 [2,5]
+ CRUSH rule 2 x 528 [5,2]
+ CRUSH rule 2 x 529 [5,7]
+ CRUSH rule 2 x 530 [6,5]
+ CRUSH rule 2 x 531 [6,2]
+ CRUSH rule 2 x 532 [6,5]
+ CRUSH rule 2 x 533 [5,6]
+ CRUSH rule 2 x 534 [7,5]
+ CRUSH rule 2 x 535 [8,2]
+ CRUSH rule 2 x 536 [6,2]
+ CRUSH rule 2 x 537 [5,7]
+ CRUSH rule 2 x 538 [6,5]
+ CRUSH rule 2 x 539 [8,5]
+ CRUSH rule 2 x 540 [2,6]
+ CRUSH rule 2 x 541 [2,5]
+ CRUSH rule 2 x 542 [5,2]
+ CRUSH rule 2 x 543 [6,2]
+ CRUSH rule 2 x 544 [5,7]
+ CRUSH rule 2 x 545 [5,7]
+ CRUSH rule 2 x 546 [6,2]
+ CRUSH rule 2 x 547 [8,2]
+ CRUSH rule 2 x 548 [5,2]
+ CRUSH rule 2 x 549 [5,8]
+ CRUSH rule 2 x 550 [2,5]
+ CRUSH rule 2 x 551 [7,5]
+ CRUSH rule 2 x 552 [5,8]
+ CRUSH rule 2 x 553 [5,2]
+ CRUSH rule 2 x 554 [2,8]
+ CRUSH rule 2 x 555 [5,2]
+ CRUSH rule 2 x 556 [5,6]
+ CRUSH rule 2 x 557 [7,5]
+ CRUSH rule 2 x 558 [5,2]
+ CRUSH rule 2 x 559 [5,2]
+ CRUSH rule 2 x 560 [8,5]
+ CRUSH rule 2 x 561 [6,5]
+ CRUSH rule 2 x 562 [5,2]
+ CRUSH rule 2 x 563 [2,6]
+ CRUSH rule 2 x 564 [5,2]
+ CRUSH rule 2 x 565 [5,6]
+ CRUSH rule 2 x 566 [5,7]
+ CRUSH rule 2 x 567 [5,6]
+ CRUSH rule 2 x 568 [7,5]
+ CRUSH rule 2 x 569 [5,2]
+ CRUSH rule 2 x 570 [2,5]
+ CRUSH rule 2 x 571 [5,7]
+ CRUSH rule 2 x 572 [5,2]
+ CRUSH rule 2 x 573 [5,2]
+ CRUSH rule 2 x 574 [2,5]
+ CRUSH rule 2 x 575 [8,2]
+ CRUSH rule 2 x 576 [5,6]
+ CRUSH rule 2 x 577 [8,2]
+ CRUSH rule 2 x 578 [6,2]
+ CRUSH rule 2 x 579 [5,2]
+ CRUSH rule 2 x 580 [5,2]
+ CRUSH rule 2 x 581 [7,2]
+ CRUSH rule 2 x 582 [2,8]
+ CRUSH rule 2 x 583 [6,2]
+ CRUSH rule 2 x 584 [8,2]
+ CRUSH rule 2 x 585 [7,2]
+ CRUSH rule 2 x 586 [2,7]
+ CRUSH rule 2 x 587 [2,5]
+ CRUSH rule 2 x 588 [5,7]
+ CRUSH rule 2 x 589 [7,2]
+ CRUSH rule 2 x 590 [6,2]
+ CRUSH rule 2 x 591 [5,2]
+ CRUSH rule 2 x 592 [2,5]
+ CRUSH rule 2 x 593 [2,8]
+ CRUSH rule 2 x 594 [2,7]
+ CRUSH rule 2 x 595 [7,2]
+ CRUSH rule 2 x 596 [5,2]
+ CRUSH rule 2 x 597 [5,2]
+ CRUSH rule 2 x 598 [5,2]
+ CRUSH rule 2 x 599 [5,2]
+ CRUSH rule 2 x 600 [7,2]
+ CRUSH rule 2 x 601 [2,7]
+ CRUSH rule 2 x 602 [5,7]
+ CRUSH rule 2 x 603 [5,2]
+ CRUSH rule 2 x 604 [7,5]
+ CRUSH rule 2 x 605 [5,2]
+ CRUSH rule 2 x 606 [2,7]
+ CRUSH rule 2 x 607 [2,5]
+ CRUSH rule 2 x 608 [5,2]
+ CRUSH rule 2 x 609 [5,2]
+ CRUSH rule 2 x 610 [5,7]
+ CRUSH rule 2 x 611 [2,8]
+ CRUSH rule 2 x 612 [2,6]
+ CRUSH rule 2 x 613 [7,2]
+ CRUSH rule 2 x 614 [7,2]
+ CRUSH rule 2 x 615 [6,2]
+ CRUSH rule 2 x 616 [2,8]
+ CRUSH rule 2 x 617 [6,2]
+ CRUSH rule 2 x 618 [7,5]
+ CRUSH rule 2 x 619 [5,2]
+ CRUSH rule 2 x 620 [5,2]
+ CRUSH rule 2 x 621 [5,8]
+ CRUSH rule 2 x 622 [2,5]
+ CRUSH rule 2 x 623 [2,6]
+ CRUSH rule 2 x 624 [5,2]
+ CRUSH rule 2 x 625 [2,5]
+ CRUSH rule 2 x 626 [7,2]
+ CRUSH rule 2 x 627 [2,7]
+ CRUSH rule 2 x 628 [8,2]
+ CRUSH rule 2 x 629 [2,6]
+ CRUSH rule 2 x 630 [2,7]
+ CRUSH rule 2 x 631 [2,7]
+ CRUSH rule 2 x 632 [7,2]
+ CRUSH rule 2 x 633 [8,5]
+ CRUSH rule 2 x 634 [2,5]
+ CRUSH rule 2 x 635 [5,6]
+ CRUSH rule 2 x 636 [2,5]
+ CRUSH rule 2 x 637 [5,2]
+ CRUSH rule 2 x 638 [6,2]
+ CRUSH rule 2 x 639 [5,2]
+ CRUSH rule 2 x 640 [5,2]
+ CRUSH rule 2 x 641 [7,2]
+ CRUSH rule 2 x 642 [2,7]
+ CRUSH rule 2 x 643 [5,2]
+ CRUSH rule 2 x 644 [8,2]
+ CRUSH rule 2 x 645 [5,7]
+ CRUSH rule 2 x 646 [8,2]
+ CRUSH rule 2 x 647 [7,2]
+ CRUSH rule 2 x 648 [2,6]
+ CRUSH rule 2 x 649 [5,7]
+ CRUSH rule 2 x 650 [7,5]
+ CRUSH rule 2 x 651 [5,7]
+ CRUSH rule 2 x 652 [5,7]
+ CRUSH rule 2 x 653 [8,5]
+ CRUSH rule 2 x 654 [7,5]
+ CRUSH rule 2 x 655 [2,5]
+ CRUSH rule 2 x 656 [5,7]
+ CRUSH rule 2 x 657 [6,2]
+ CRUSH rule 2 x 658 [5,6]
+ CRUSH rule 2 x 659 [5,6]
+ CRUSH rule 2 x 660 [7,5]
+ CRUSH rule 2 x 661 [2,8]
+ CRUSH rule 2 x 662 [5,2]
+ CRUSH rule 2 x 663 [2,5]
+ CRUSH rule 2 x 664 [2,5]
+ CRUSH rule 2 x 665 [5,7]
+ CRUSH rule 2 x 666 [2,8]
+ CRUSH rule 2 x 667 [2,5]
+ CRUSH rule 2 x 668 [5,7]
+ CRUSH rule 2 x 669 [6,5]
+ CRUSH rule 2 x 670 [5,2]
+ CRUSH rule 2 x 671 [2,7]
+ CRUSH rule 2 x 672 [5,2]
+ CRUSH rule 2 x 673 [5,2]
+ CRUSH rule 2 x 674 [5,2]
+ CRUSH rule 2 x 675 [2,8]
+ CRUSH rule 2 x 676 [2,5]
+ CRUSH rule 2 x 677 [5,2]
+ CRUSH rule 2 x 678 [2,5]
+ CRUSH rule 2 x 679 [6,2]
+ CRUSH rule 2 x 680 [2,5]
+ CRUSH rule 2 x 681 [5,7]
+ CRUSH rule 2 x 682 [2,5]
+ CRUSH rule 2 x 683 [2,5]
+ CRUSH rule 2 x 684 [7,2]
+ CRUSH rule 2 x 685 [7,2]
+ CRUSH rule 2 x 686 [2,5]
+ CRUSH rule 2 x 687 [5,6]
+ CRUSH rule 2 x 688 [5,7]
+ CRUSH rule 2 x 689 [6,5]
+ CRUSH rule 2 x 690 [8,2]
+ CRUSH rule 2 x 691 [5,2]
+ CRUSH rule 2 x 692 [7,2]
+ CRUSH rule 2 x 693 [6,5]
+ CRUSH rule 2 x 694 [6,5]
+ CRUSH rule 2 x 695 [2,8]
+ CRUSH rule 2 x 696 [2,5]
+ CRUSH rule 2 x 697 [6,2]
+ CRUSH rule 2 x 698 [6,2]
+ CRUSH rule 2 x 699 [2,6]
+ CRUSH rule 2 x 700 [2,5]
+ CRUSH rule 2 x 701 [5,2]
+ CRUSH rule 2 x 702 [5,2]
+ CRUSH rule 2 x 703 [8,5]
+ CRUSH rule 2 x 704 [2,5]
+ CRUSH rule 2 x 705 [8,2]
+ CRUSH rule 2 x 706 [2,5]
+ CRUSH rule 2 x 707 [7,5]
+ CRUSH rule 2 x 708 [5,7]
+ CRUSH rule 2 x 709 [6,5]
+ CRUSH rule 2 x 710 [8,5]
+ CRUSH rule 2 x 711 [2,5]
+ CRUSH rule 2 x 712 [2,5]
+ CRUSH rule 2 x 713 [6,5]
+ CRUSH rule 2 x 714 [5,2]
+ CRUSH rule 2 x 715 [2,5]
+ CRUSH rule 2 x 716 [5,6]
+ CRUSH rule 2 x 717 [8,2]
+ CRUSH rule 2 x 718 [5,7]
+ CRUSH rule 2 x 719 [2,6]
+ CRUSH rule 2 x 720 [6,2]
+ CRUSH rule 2 x 721 [5,7]
+ CRUSH rule 2 x 722 [5,7]
+ CRUSH rule 2 x 723 [5,2]
+ CRUSH rule 2 x 724 [2,6]
+ CRUSH rule 2 x 725 [2,5]
+ CRUSH rule 2 x 726 [5,8]
+ CRUSH rule 2 x 727 [5,6]
+ CRUSH rule 2 x 728 [2,7]
+ CRUSH rule 2 x 729 [5,6]
+ CRUSH rule 2 x 730 [5,7]
+ CRUSH rule 2 x 731 [5,2]
+ CRUSH rule 2 x 732 [2,5]
+ CRUSH rule 2 x 733 [5,7]
+ CRUSH rule 2 x 734 [6,5]
+ CRUSH rule 2 x 735 [5,8]
+ CRUSH rule 2 x 736 [5,8]
+ CRUSH rule 2 x 737 [2,6]
+ CRUSH rule 2 x 738 [5,2]
+ CRUSH rule 2 x 739 [2,7]
+ CRUSH rule 2 x 740 [2,8]
+ CRUSH rule 2 x 741 [7,2]
+ CRUSH rule 2 x 742 [8,2]
+ CRUSH rule 2 x 743 [7,2]
+ CRUSH rule 2 x 744 [5,7]
+ CRUSH rule 2 x 745 [5,2]
+ CRUSH rule 2 x 746 [5,2]
+ CRUSH rule 2 x 747 [6,2]
+ CRUSH rule 2 x 748 [2,7]
+ CRUSH rule 2 x 749 [5,8]
+ CRUSH rule 2 x 750 [2,6]
+ CRUSH rule 2 x 751 [2,8]
+ CRUSH rule 2 x 752 [8,2]
+ CRUSH rule 2 x 753 [7,5]
+ CRUSH rule 2 x 754 [8,5]
+ CRUSH rule 2 x 755 [2,6]
+ CRUSH rule 2 x 756 [5,6]
+ CRUSH rule 2 x 757 [8,2]
+ CRUSH rule 2 x 758 [6,2]
+ CRUSH rule 2 x 759 [8,5]
+ CRUSH rule 2 x 760 [2,5]
+ CRUSH rule 2 x 761 [5,2]
+ CRUSH rule 2 x 762 [2,7]
+ CRUSH rule 2 x 763 [8,5]
+ CRUSH rule 2 x 764 [2,7]
+ CRUSH rule 2 x 765 [6,5]
+ CRUSH rule 2 x 766 [8,5]
+ CRUSH rule 2 x 767 [2,8]
+ CRUSH rule 2 x 768 [8,5]
+ CRUSH rule 2 x 769 [6,2]
+ CRUSH rule 2 x 770 [6,2]
+ CRUSH rule 2 x 771 [7,2]
+ CRUSH rule 2 x 772 [8,5]
+ CRUSH rule 2 x 773 [5,2]
+ CRUSH rule 2 x 774 [5,6]
+ CRUSH rule 2 x 775 [6,5]
+ CRUSH rule 2 x 776 [7,2]
+ CRUSH rule 2 x 777 [5,2]
+ CRUSH rule 2 x 778 [2,8]
+ CRUSH rule 2 x 779 [2,7]
+ CRUSH rule 2 x 780 [2,5]
+ CRUSH rule 2 x 781 [6,5]
+ CRUSH rule 2 x 782 [5,2]
+ CRUSH rule 2 x 783 [7,2]
+ CRUSH rule 2 x 784 [2,5]
+ CRUSH rule 2 x 785 [6,2]
+ CRUSH rule 2 x 786 [7,5]
+ CRUSH rule 2 x 787 [2,6]
+ CRUSH rule 2 x 788 [6,2]
+ CRUSH rule 2 x 789 [2,5]
+ CRUSH rule 2 x 790 [8,5]
+ CRUSH rule 2 x 791 [5,8]
+ CRUSH rule 2 x 792 [5,8]
+ CRUSH rule 2 x 793 [6,2]
+ CRUSH rule 2 x 794 [2,6]
+ CRUSH rule 2 x 795 [2,5]
+ CRUSH rule 2 x 796 [5,7]
+ CRUSH rule 2 x 797 [2,5]
+ CRUSH rule 2 x 798 [6,2]
+ CRUSH rule 2 x 799 [5,2]
+ CRUSH rule 2 x 800 [5,2]
+ CRUSH rule 2 x 801 [5,6]
+ CRUSH rule 2 x 802 [2,8]
+ CRUSH rule 2 x 803 [2,5]
+ CRUSH rule 2 x 804 [6,2]
+ CRUSH rule 2 x 805 [5,6]
+ CRUSH rule 2 x 806 [2,5]
+ CRUSH rule 2 x 807 [5,7]
+ CRUSH rule 2 x 808 [5,6]
+ CRUSH rule 2 x 809 [2,5]
+ CRUSH rule 2 x 810 [5,7]
+ CRUSH rule 2 x 811 [8,5]
+ CRUSH rule 2 x 812 [8,5]
+ CRUSH rule 2 x 813 [6,5]
+ CRUSH rule 2 x 814 [5,6]
+ CRUSH rule 2 x 815 [5,2]
+ CRUSH rule 2 x 816 [2,7]
+ CRUSH rule 2 x 817 [5,8]
+ CRUSH rule 2 x 818 [5,2]
+ CRUSH rule 2 x 819 [5,2]
+ CRUSH rule 2 x 820 [5,6]
+ CRUSH rule 2 x 821 [5,6]
+ CRUSH rule 2 x 822 [2,5]
+ CRUSH rule 2 x 823 [5,8]
+ CRUSH rule 2 x 824 [5,7]
+ CRUSH rule 2 x 825 [2,8]
+ CRUSH rule 2 x 826 [7,2]
+ CRUSH rule 2 x 827 [2,8]
+ CRUSH rule 2 x 828 [2,5]
+ CRUSH rule 2 x 829 [5,6]
+ CRUSH rule 2 x 830 [2,5]
+ CRUSH rule 2 x 831 [2,6]
+ CRUSH rule 2 x 832 [5,7]
+ CRUSH rule 2 x 833 [2,7]
+ CRUSH rule 2 x 834 [5,2]
+ CRUSH rule 2 x 835 [8,5]
+ CRUSH rule 2 x 836 [5,7]
+ CRUSH rule 2 x 837 [6,5]
+ CRUSH rule 2 x 838 [6,2]
+ CRUSH rule 2 x 839 [5,2]
+ CRUSH rule 2 x 840 [7,5]
+ CRUSH rule 2 x 841 [5,8]
+ CRUSH rule 2 x 842 [2,5]
+ CRUSH rule 2 x 843 [6,5]
+ CRUSH rule 2 x 844 [5,8]
+ CRUSH rule 2 x 845 [5,8]
+ CRUSH rule 2 x 846 [5,2]
+ CRUSH rule 2 x 847 [2,8]
+ CRUSH rule 2 x 848 [2,6]
+ CRUSH rule 2 x 849 [5,6]
+ CRUSH rule 2 x 850 [2,5]
+ CRUSH rule 2 x 851 [6,5]
+ CRUSH rule 2 x 852 [7,5]
+ CRUSH rule 2 x 853 [6,2]
+ CRUSH rule 2 x 854 [7,2]
+ CRUSH rule 2 x 855 [5,7]
+ CRUSH rule 2 x 856 [6,5]
+ CRUSH rule 2 x 857 [8,5]
+ CRUSH rule 2 x 858 [6,5]
+ CRUSH rule 2 x 859 [6,2]
+ CRUSH rule 2 x 860 [5,2]
+ CRUSH rule 2 x 861 [8,5]
+ CRUSH rule 2 x 862 [6,2]
+ CRUSH rule 2 x 863 [8,2]
+ CRUSH rule 2 x 864 [5,6]
+ CRUSH rule 2 x 865 [8,2]
+ CRUSH rule 2 x 866 [5,6]
+ CRUSH rule 2 x 867 [6,5]
+ CRUSH rule 2 x 868 [6,5]
+ CRUSH rule 2 x 869 [8,5]
+ CRUSH rule 2 x 870 [2,5]
+ CRUSH rule 2 x 871 [5,2]
+ CRUSH rule 2 x 872 [5,2]
+ CRUSH rule 2 x 873 [5,6]
+ CRUSH rule 2 x 874 [2,6]
+ CRUSH rule 2 x 875 [2,6]
+ CRUSH rule 2 x 876 [5,8]
+ CRUSH rule 2 x 877 [6,5]
+ CRUSH rule 2 x 878 [5,2]
+ CRUSH rule 2 x 879 [7,5]
+ CRUSH rule 2 x 880 [5,2]
+ CRUSH rule 2 x 881 [5,6]
+ CRUSH rule 2 x 882 [5,2]
+ CRUSH rule 2 x 883 [2,5]
+ CRUSH rule 2 x 884 [6,2]
+ CRUSH rule 2 x 885 [5,2]
+ CRUSH rule 2 x 886 [5,6]
+ CRUSH rule 2 x 887 [7,5]
+ CRUSH rule 2 x 888 [6,2]
+ CRUSH rule 2 x 889 [2,6]
+ CRUSH rule 2 x 890 [7,2]
+ CRUSH rule 2 x 891 [2,8]
+ CRUSH rule 2 x 892 [6,2]
+ CRUSH rule 2 x 893 [2,5]
+ CRUSH rule 2 x 894 [7,5]
+ CRUSH rule 2 x 895 [5,2]
+ CRUSH rule 2 x 896 [2,8]
+ CRUSH rule 2 x 897 [5,2]
+ CRUSH rule 2 x 898 [2,5]
+ CRUSH rule 2 x 899 [2,7]
+ CRUSH rule 2 x 900 [5,2]
+ CRUSH rule 2 x 901 [5,2]
+ CRUSH rule 2 x 902 [8,5]
+ CRUSH rule 2 x 903 [5,7]
+ CRUSH rule 2 x 904 [5,6]
+ CRUSH rule 2 x 905 [6,2]
+ CRUSH rule 2 x 906 [2,6]
+ CRUSH rule 2 x 907 [7,2]
+ CRUSH rule 2 x 908 [5,8]
+ CRUSH rule 2 x 909 [2,5]
+ CRUSH rule 2 x 910 [6,5]
+ CRUSH rule 2 x 911 [5,8]
+ CRUSH rule 2 x 912 [2,7]
+ CRUSH rule 2 x 913 [7,2]
+ CRUSH rule 2 x 914 [6,5]
+ CRUSH rule 2 x 915 [8,2]
+ CRUSH rule 2 x 916 [5,2]
+ CRUSH rule 2 x 917 [2,5]
+ CRUSH rule 2 x 918 [8,2]
+ CRUSH rule 2 x 919 [6,2]
+ CRUSH rule 2 x 920 [7,5]
+ CRUSH rule 2 x 921 [2,5]
+ CRUSH rule 2 x 922 [6,5]
+ CRUSH rule 2 x 923 [5,8]
+ CRUSH rule 2 x 924 [5,2]
+ CRUSH rule 2 x 925 [5,7]
+ CRUSH rule 2 x 926 [5,2]
+ CRUSH rule 2 x 927 [2,6]
+ CRUSH rule 2 x 928 [8,2]
+ CRUSH rule 2 x 929 [5,2]
+ CRUSH rule 2 x 930 [2,5]
+ CRUSH rule 2 x 931 [5,2]
+ CRUSH rule 2 x 932 [5,2]
+ CRUSH rule 2 x 933 [8,5]
+ CRUSH rule 2 x 934 [5,6]
+ CRUSH rule 2 x 935 [6,5]
+ CRUSH rule 2 x 936 [2,6]
+ CRUSH rule 2 x 937 [5,8]
+ CRUSH rule 2 x 938 [6,5]
+ CRUSH rule 2 x 939 [2,7]
+ CRUSH rule 2 x 940 [8,5]
+ CRUSH rule 2 x 941 [5,2]
+ CRUSH rule 2 x 942 [2,8]
+ CRUSH rule 2 x 943 [8,2]
+ CRUSH rule 2 x 944 [5,8]
+ CRUSH rule 2 x 945 [7,2]
+ CRUSH rule 2 x 946 [2,8]
+ CRUSH rule 2 x 947 [5,2]
+ CRUSH rule 2 x 948 [7,5]
+ CRUSH rule 2 x 949 [6,2]
+ CRUSH rule 2 x 950 [5,6]
+ CRUSH rule 2 x 951 [5,8]
+ CRUSH rule 2 x 952 [2,7]
+ CRUSH rule 2 x 953 [2,5]
+ CRUSH rule 2 x 954 [5,2]
+ CRUSH rule 2 x 955 [8,2]
+ CRUSH rule 2 x 956 [2,6]
+ CRUSH rule 2 x 957 [7,2]
+ CRUSH rule 2 x 958 [8,5]
+ CRUSH rule 2 x 959 [5,2]
+ CRUSH rule 2 x 960 [5,6]
+ CRUSH rule 2 x 961 [5,2]
+ CRUSH rule 2 x 962 [7,5]
+ CRUSH rule 2 x 963 [2,5]
+ CRUSH rule 2 x 964 [5,2]
+ CRUSH rule 2 x 965 [7,5]
+ CRUSH rule 2 x 966 [5,8]
+ CRUSH rule 2 x 967 [8,5]
+ CRUSH rule 2 x 968 [7,2]
+ CRUSH rule 2 x 969 [8,2]
+ CRUSH rule 2 x 970 [2,6]
+ CRUSH rule 2 x 971 [2,7]
+ CRUSH rule 2 x 972 [2,8]
+ CRUSH rule 2 x 973 [2,6]
+ CRUSH rule 2 x 974 [5,2]
+ CRUSH rule 2 x 975 [5,7]
+ CRUSH rule 2 x 976 [5,8]
+ CRUSH rule 2 x 977 [8,5]
+ CRUSH rule 2 x 978 [7,2]
+ CRUSH rule 2 x 979 [7,2]
+ CRUSH rule 2 x 980 [6,2]
+ CRUSH rule 2 x 981 [7,5]
+ CRUSH rule 2 x 982 [5,2]
+ CRUSH rule 2 x 983 [5,7]
+ CRUSH rule 2 x 984 [2,7]
+ CRUSH rule 2 x 985 [2,5]
+ CRUSH rule 2 x 986 [8,5]
+ CRUSH rule 2 x 987 [2,5]
+ CRUSH rule 2 x 988 [2,5]
+ CRUSH rule 2 x 989 [2,6]
+ CRUSH rule 2 x 990 [2,6]
+ CRUSH rule 2 x 991 [2,5]
+ CRUSH rule 2 x 992 [7,2]
+ CRUSH rule 2 x 993 [2,6]
+ CRUSH rule 2 x 994 [5,7]
+ CRUSH rule 2 x 995 [7,2]
+ CRUSH rule 2 x 996 [6,5]
+ CRUSH rule 2 x 997 [6,5]
+ CRUSH rule 2 x 998 [8,2]
+ CRUSH rule 2 x 999 [2,7]
+ CRUSH rule 2 x 1000 [8,5]
+ CRUSH rule 2 x 1001 [2,5]
+ CRUSH rule 2 x 1002 [2,5]
+ CRUSH rule 2 x 1003 [2,8]
+ CRUSH rule 2 x 1004 [6,2]
+ CRUSH rule 2 x 1005 [6,2]
+ CRUSH rule 2 x 1006 [2,6]
+ CRUSH rule 2 x 1007 [2,5]
+ CRUSH rule 2 x 1008 [2,7]
+ CRUSH rule 2 x 1009 [6,5]
+ CRUSH rule 2 x 1010 [5,2]
+ CRUSH rule 2 x 1011 [5,2]
+ CRUSH rule 2 x 1012 [5,2]
+ CRUSH rule 2 x 1013 [5,2]
+ CRUSH rule 2 x 1014 [2,8]
+ CRUSH rule 2 x 1015 [6,5]
+ CRUSH rule 2 x 1016 [2,5]
+ CRUSH rule 2 x 1017 [6,2]
+ CRUSH rule 2 x 1018 [5,2]
+ CRUSH rule 2 x 1019 [5,8]
+ CRUSH rule 2 x 1020 [5,2]
+ CRUSH rule 2 x 1021 [5,2]
+ CRUSH rule 2 x 1022 [2,6]
+ CRUSH rule 2 x 1023 [5,2]
+ rule 2 (chooseleaf) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 2 x 0 [2,5,7]
+ CRUSH rule 2 x 1 [2,8,5]
+ CRUSH rule 2 x 2 [2,5,7]
+ CRUSH rule 2 x 3 [8,2,5]
+ CRUSH rule 2 x 4 [5,2,7]
+ CRUSH rule 2 x 5 [7,2,5]
+ CRUSH rule 2 x 6 [2,6,5]
+ CRUSH rule 2 x 7 [5,8,2]
+ CRUSH rule 2 x 8 [5,6,2]
+ CRUSH rule 2 x 9 [2,5,8]
+ CRUSH rule 2 x 10 [2,7,5]
+ CRUSH rule 2 x 11 [2,7,5]
+ CRUSH rule 2 x 12 [2,5,6]
+ CRUSH rule 2 x 13 [5,8,2]
+ CRUSH rule 2 x 14 [7,2,5]
+ CRUSH rule 2 x 15 [7,2,5]
+ CRUSH rule 2 x 16 [5,6,2]
+ CRUSH rule 2 x 17 [5,2,6]
+ CRUSH rule 2 x 18 [2,5,6]
+ CRUSH rule 2 x 19 [7,5,2]
+ CRUSH rule 2 x 20 [2,5,7]
+ CRUSH rule 2 x 21 [5,7,2]
+ CRUSH rule 2 x 22 [8,5,2]
+ CRUSH rule 2 x 23 [5,6,2]
+ CRUSH rule 2 x 24 [2,7,5]
+ CRUSH rule 2 x 25 [5,7,2]
+ CRUSH rule 2 x 26 [2,8,5]
+ CRUSH rule 2 x 27 [5,2,8]
+ CRUSH rule 2 x 28 [6,2,5]
+ CRUSH rule 2 x 29 [8,5,2]
+ CRUSH rule 2 x 30 [5,7,2]
+ CRUSH rule 2 x 31 [8,2,5]
+ CRUSH rule 2 x 32 [5,6,2]
+ CRUSH rule 2 x 33 [2,7,5]
+ CRUSH rule 2 x 34 [2,5,7]
+ CRUSH rule 2 x 35 [2,8,5]
+ CRUSH rule 2 x 36 [5,8,2]
+ CRUSH rule 2 x 37 [2,5,6]
+ CRUSH rule 2 x 38 [5,8,2]
+ CRUSH rule 2 x 39 [5,7,2]
+ CRUSH rule 2 x 40 [7,2,5]
+ CRUSH rule 2 x 41 [2,6,5]
+ CRUSH rule 2 x 42 [5,6,2]
+ CRUSH rule 2 x 43 [2,5,7]
+ CRUSH rule 2 x 44 [2,6,5]
+ CRUSH rule 2 x 45 [8,2,5]
+ CRUSH rule 2 x 46 [2,5,8]
+ CRUSH rule 2 x 47 [5,2,8]
+ CRUSH rule 2 x 48 [5,6,2]
+ CRUSH rule 2 x 49 [5,7,2]
+ CRUSH rule 2 x 50 [5,2,7]
+ CRUSH rule 2 x 51 [5,6,2]
+ CRUSH rule 2 x 52 [8,2,5]
+ CRUSH rule 2 x 53 [5,8,2]
+ CRUSH rule 2 x 54 [7,5,2]
+ CRUSH rule 2 x 55 [8,2,5]
+ CRUSH rule 2 x 56 [6,5,2]
+ CRUSH rule 2 x 57 [5,8,2]
+ CRUSH rule 2 x 58 [2,8,5]
+ CRUSH rule 2 x 59 [5,2,7]
+ CRUSH rule 2 x 60 [5,2,6]
+ CRUSH rule 2 x 61 [5,6,2]
+ CRUSH rule 2 x 62 [7,2,5]
+ CRUSH rule 2 x 63 [5,6,2]
+ CRUSH rule 2 x 64 [5,2,8]
+ CRUSH rule 2 x 65 [7,5,2]
+ CRUSH rule 2 x 66 [5,6,2]
+ CRUSH rule 2 x 67 [5,2,8]
+ CRUSH rule 2 x 68 [2,5,8]
+ CRUSH rule 2 x 69 [5,2,6]
+ CRUSH rule 2 x 70 [7,2,5]
+ CRUSH rule 2 x 71 [2,8,5]
+ CRUSH rule 2 x 72 [6,2,5]
+ CRUSH rule 2 x 73 [2,7,5]
+ CRUSH rule 2 x 74 [2,7,5]
+ CRUSH rule 2 x 75 [5,2,6]
+ CRUSH rule 2 x 76 [5,2,7]
+ CRUSH rule 2 x 77 [7,2,5]
+ CRUSH rule 2 x 78 [2,5,8]
+ CRUSH rule 2 x 79 [5,2,7]
+ CRUSH rule 2 x 80 [2,5,6]
+ CRUSH rule 2 x 81 [2,5,6]
+ CRUSH rule 2 x 82 [7,2,5]
+ CRUSH rule 2 x 83 [2,6,5]
+ CRUSH rule 2 x 84 [7,2,5]
+ CRUSH rule 2 x 85 [5,8,2]
+ CRUSH rule 2 x 86 [2,6,5]
+ CRUSH rule 2 x 87 [2,7,5]
+ CRUSH rule 2 x 88 [2,6,5]
+ CRUSH rule 2 x 89 [5,2,7]
+ CRUSH rule 2 x 90 [6,5,2]
+ CRUSH rule 2 x 91 [5,8,2]
+ CRUSH rule 2 x 92 [2,8,5]
+ CRUSH rule 2 x 93 [7,5,2]
+ CRUSH rule 2 x 94 [2,5,8]
+ CRUSH rule 2 x 95 [7,5,2]
+ CRUSH rule 2 x 96 [5,6,2]
+ CRUSH rule 2 x 97 [8,5,2]
+ CRUSH rule 2 x 98 [2,7,5]
+ CRUSH rule 2 x 99 [2,7,5]
+ CRUSH rule 2 x 100 [2,7,5]
+ CRUSH rule 2 x 101 [5,7,2]
+ CRUSH rule 2 x 102 [5,2,7]
+ CRUSH rule 2 x 103 [5,7,2]
+ CRUSH rule 2 x 104 [7,5,2]
+ CRUSH rule 2 x 105 [2,5,6]
+ CRUSH rule 2 x 106 [2,6,5]
+ CRUSH rule 2 x 107 [5,2,6]
+ CRUSH rule 2 x 108 [7,2,5]
+ CRUSH rule 2 x 109 [2,5,6]
+ CRUSH rule 2 x 110 [5,2,7]
+ CRUSH rule 2 x 111 [2,5,6]
+ CRUSH rule 2 x 112 [2,6,5]
+ CRUSH rule 2 x 113 [6,2,5]
+ CRUSH rule 2 x 114 [7,5,2]
+ CRUSH rule 2 x 115 [8,2,5]
+ CRUSH rule 2 x 116 [2,6,5]
+ CRUSH rule 2 x 117 [7,5,2]
+ CRUSH rule 2 x 118 [2,5,8]
+ CRUSH rule 2 x 119 [5,6,2]
+ CRUSH rule 2 x 120 [2,5,7]
+ CRUSH rule 2 x 121 [2,7,5]
+ CRUSH rule 2 x 122 [8,5,2]
+ CRUSH rule 2 x 123 [2,5,8]
+ CRUSH rule 2 x 124 [5,2,8]
+ CRUSH rule 2 x 125 [2,7,5]
+ CRUSH rule 2 x 126 [5,2,6]
+ CRUSH rule 2 x 127 [5,6,2]
+ CRUSH rule 2 x 128 [5,6,2]
+ CRUSH rule 2 x 129 [2,5,7]
+ CRUSH rule 2 x 130 [5,8,2]
+ CRUSH rule 2 x 131 [2,5,8]
+ CRUSH rule 2 x 132 [2,5,6]
+ CRUSH rule 2 x 133 [5,6,2]
+ CRUSH rule 2 x 134 [2,8,5]
+ CRUSH rule 2 x 135 [5,6,2]
+ CRUSH rule 2 x 136 [2,5,6]
+ CRUSH rule 2 x 137 [7,5,2]
+ CRUSH rule 2 x 138 [8,5,2]
+ CRUSH rule 2 x 139 [5,2,7]
+ CRUSH rule 2 x 140 [2,6,5]
+ CRUSH rule 2 x 141 [6,2,5]
+ CRUSH rule 2 x 142 [5,2,8]
+ CRUSH rule 2 x 143 [5,8,2]
+ CRUSH rule 2 x 144 [8,2,5]
+ CRUSH rule 2 x 145 [8,5,2]
+ CRUSH rule 2 x 146 [2,6,5]
+ CRUSH rule 2 x 147 [2,8,5]
+ CRUSH rule 2 x 148 [5,2,7]
+ CRUSH rule 2 x 149 [5,8,2]
+ CRUSH rule 2 x 150 [2,6,5]
+ CRUSH rule 2 x 151 [5,6,2]
+ CRUSH rule 2 x 152 [8,5,2]
+ CRUSH rule 2 x 153 [8,5,2]
+ CRUSH rule 2 x 154 [5,2,7]
+ CRUSH rule 2 x 155 [5,7,2]
+ CRUSH rule 2 x 156 [5,2,6]
+ CRUSH rule 2 x 157 [5,2,6]
+ CRUSH rule 2 x 158 [2,8,5]
+ CRUSH rule 2 x 159 [7,2,5]
+ CRUSH rule 2 x 160 [2,8,5]
+ CRUSH rule 2 x 161 [2,5,6]
+ CRUSH rule 2 x 162 [2,6,5]
+ CRUSH rule 2 x 163 [5,6,2]
+ CRUSH rule 2 x 164 [7,2,5]
+ CRUSH rule 2 x 165 [7,2,5]
+ CRUSH rule 2 x 166 [2,5,6]
+ CRUSH rule 2 x 167 [2,7,5]
+ CRUSH rule 2 x 168 [5,2,7]
+ CRUSH rule 2 x 169 [2,6,5]
+ CRUSH rule 2 x 170 [2,5,8]
+ CRUSH rule 2 x 171 [7,5,2]
+ CRUSH rule 2 x 172 [2,7,5]
+ CRUSH rule 2 x 173 [8,5,2]
+ CRUSH rule 2 x 174 [2,5,7]
+ CRUSH rule 2 x 175 [6,2,5]
+ CRUSH rule 2 x 176 [5,2,7]
+ CRUSH rule 2 x 177 [5,2,6]
+ CRUSH rule 2 x 178 [5,2,8]
+ CRUSH rule 2 x 179 [5,2,7]
+ CRUSH rule 2 x 180 [5,8,2]
+ CRUSH rule 2 x 181 [6,2,5]
+ CRUSH rule 2 x 182 [8,5,2]
+ CRUSH rule 2 x 183 [7,5,2]
+ CRUSH rule 2 x 184 [5,7,2]
+ CRUSH rule 2 x 185 [6,2,5]
+ CRUSH rule 2 x 186 [2,5,7]
+ CRUSH rule 2 x 187 [2,6,5]
+ CRUSH rule 2 x 188 [2,8,5]
+ CRUSH rule 2 x 189 [2,7,5]
+ CRUSH rule 2 x 190 [5,2,8]
+ CRUSH rule 2 x 191 [7,2,5]
+ CRUSH rule 2 x 192 [5,2,8]
+ CRUSH rule 2 x 193 [5,2,6]
+ CRUSH rule 2 x 194 [2,5,8]
+ CRUSH rule 2 x 195 [6,5,2]
+ CRUSH rule 2 x 196 [6,2,5]
+ CRUSH rule 2 x 197 [6,5,2]
+ CRUSH rule 2 x 198 [2,5,6]
+ CRUSH rule 2 x 199 [2,5,7]
+ CRUSH rule 2 x 200 [2,5,6]
+ CRUSH rule 2 x 201 [7,2,5]
+ CRUSH rule 2 x 202 [6,5,2]
+ CRUSH rule 2 x 203 [5,8,2]
+ CRUSH rule 2 x 204 [2,5,7]
+ CRUSH rule 2 x 205 [2,7,5]
+ CRUSH rule 2 x 206 [2,7,5]
+ CRUSH rule 2 x 207 [5,2,7]
+ CRUSH rule 2 x 208 [7,2,5]
+ CRUSH rule 2 x 209 [2,8,5]
+ CRUSH rule 2 x 210 [2,5,8]
+ CRUSH rule 2 x 211 [5,2,8]
+ CRUSH rule 2 x 212 [7,5,2]
+ CRUSH rule 2 x 213 [8,5,2]
+ CRUSH rule 2 x 214 [5,8,2]
+ CRUSH rule 2 x 215 [8,2,5]
+ CRUSH rule 2 x 216 [5,2,6]
+ CRUSH rule 2 x 217 [2,7,5]
+ CRUSH rule 2 x 218 [2,7,5]
+ CRUSH rule 2 x 219 [5,8,2]
+ CRUSH rule 2 x 220 [5,7,2]
+ CRUSH rule 2 x 221 [5,6,2]
+ CRUSH rule 2 x 222 [6,5,2]
+ CRUSH rule 2 x 223 [2,5,6]
+ CRUSH rule 2 x 224 [2,5,8]
+ CRUSH rule 2 x 225 [8,2,5]
+ CRUSH rule 2 x 226 [7,2,5]
+ CRUSH rule 2 x 227 [5,2,6]
+ CRUSH rule 2 x 228 [5,6,2]
+ CRUSH rule 2 x 229 [5,8,2]
+ CRUSH rule 2 x 230 [5,7,2]
+ CRUSH rule 2 x 231 [5,7,2]
+ CRUSH rule 2 x 232 [2,7,5]
+ CRUSH rule 2 x 233 [5,7,2]
+ CRUSH rule 2 x 234 [2,5,6]
+ CRUSH rule 2 x 235 [5,8,2]
+ CRUSH rule 2 x 236 [5,2,7]
+ CRUSH rule 2 x 237 [5,7,2]
+ CRUSH rule 2 x 238 [5,2,6]
+ CRUSH rule 2 x 239 [8,5,2]
+ CRUSH rule 2 x 240 [5,7,2]
+ CRUSH rule 2 x 241 [5,2,8]
+ CRUSH rule 2 x 242 [5,2,6]
+ CRUSH rule 2 x 243 [5,7,2]
+ CRUSH rule 2 x 244 [5,6,2]
+ CRUSH rule 2 x 245 [7,2,5]
+ CRUSH rule 2 x 246 [2,5,8]
+ CRUSH rule 2 x 247 [6,2,5]
+ CRUSH rule 2 x 248 [8,2,5]
+ CRUSH rule 2 x 249 [2,5,7]
+ CRUSH rule 2 x 250 [2,5,6]
+ CRUSH rule 2 x 251 [2,5,6]
+ CRUSH rule 2 x 252 [5,7,2]
+ CRUSH rule 2 x 253 [5,2,6]
+ CRUSH rule 2 x 254 [5,2,7]
+ CRUSH rule 2 x 255 [2,7,5]
+ CRUSH rule 2 x 256 [5,7,2]
+ CRUSH rule 2 x 257 [2,8,5]
+ CRUSH rule 2 x 258 [5,2,6]
+ CRUSH rule 2 x 259 [5,6,2]
+ CRUSH rule 2 x 260 [5,6,2]
+ CRUSH rule 2 x 261 [8,5,2]
+ CRUSH rule 2 x 262 [5,6,2]
+ CRUSH rule 2 x 263 [6,2,5]
+ CRUSH rule 2 x 264 [5,6,2]
+ CRUSH rule 2 x 265 [8,5,2]
+ CRUSH rule 2 x 266 [8,2,5]
+ CRUSH rule 2 x 267 [2,5,8]
+ CRUSH rule 2 x 268 [2,7,5]
+ CRUSH rule 2 x 269 [2,8,5]
+ CRUSH rule 2 x 270 [5,2,7]
+ CRUSH rule 2 x 271 [7,5,2]
+ CRUSH rule 2 x 272 [2,8,5]
+ CRUSH rule 2 x 273 [5,2,7]
+ CRUSH rule 2 x 274 [6,5,2]
+ CRUSH rule 2 x 275 [5,7,2]
+ CRUSH rule 2 x 276 [7,2,5]
+ CRUSH rule 2 x 277 [6,5,2]
+ CRUSH rule 2 x 278 [6,2,5]
+ CRUSH rule 2 x 279 [8,5,2]
+ CRUSH rule 2 x 280 [2,6,5]
+ CRUSH rule 2 x 281 [8,2,5]
+ CRUSH rule 2 x 282 [5,2,6]
+ CRUSH rule 2 x 283 [8,2,5]
+ CRUSH rule 2 x 284 [6,5,2]
+ CRUSH rule 2 x 285 [5,7,2]
+ CRUSH rule 2 x 286 [2,6,5]
+ CRUSH rule 2 x 287 [2,5,6]
+ CRUSH rule 2 x 288 [8,2,5]
+ CRUSH rule 2 x 289 [5,6,2]
+ CRUSH rule 2 x 290 [2,5,7]
+ CRUSH rule 2 x 291 [2,5,8]
+ CRUSH rule 2 x 292 [8,2,5]
+ CRUSH rule 2 x 293 [6,2,5]
+ CRUSH rule 2 x 294 [7,5,2]
+ CRUSH rule 2 x 295 [5,8,2]
+ CRUSH rule 2 x 296 [5,2,6]
+ CRUSH rule 2 x 297 [6,2,5]
+ CRUSH rule 2 x 298 [2,5,7]
+ CRUSH rule 2 x 299 [2,8,5]
+ CRUSH rule 2 x 300 [8,5,2]
+ CRUSH rule 2 x 301 [2,8,5]
+ CRUSH rule 2 x 302 [5,2,6]
+ CRUSH rule 2 x 303 [7,5,2]
+ CRUSH rule 2 x 304 [2,7,5]
+ CRUSH rule 2 x 305 [5,8,2]
+ CRUSH rule 2 x 306 [2,7,5]
+ CRUSH rule 2 x 307 [2,7,5]
+ CRUSH rule 2 x 308 [2,8,5]
+ CRUSH rule 2 x 309 [7,5,2]
+ CRUSH rule 2 x 310 [5,2,7]
+ CRUSH rule 2 x 311 [5,6,2]
+ CRUSH rule 2 x 312 [2,6,5]
+ CRUSH rule 2 x 313 [5,2,6]
+ CRUSH rule 2 x 314 [5,2,6]
+ CRUSH rule 2 x 315 [2,5,8]
+ CRUSH rule 2 x 316 [6,5,2]
+ CRUSH rule 2 x 317 [2,6,5]
+ CRUSH rule 2 x 318 [8,2,5]
+ CRUSH rule 2 x 319 [5,2,8]
+ CRUSH rule 2 x 320 [5,7,2]
+ CRUSH rule 2 x 321 [2,5,8]
+ CRUSH rule 2 x 322 [2,7,5]
+ CRUSH rule 2 x 323 [5,7,2]
+ CRUSH rule 2 x 324 [7,2,5]
+ CRUSH rule 2 x 325 [5,6,2]
+ CRUSH rule 2 x 326 [5,2,6]
+ CRUSH rule 2 x 327 [2,6,5]
+ CRUSH rule 2 x 328 [7,5,2]
+ CRUSH rule 2 x 329 [5,6,2]
+ CRUSH rule 2 x 330 [5,7,2]
+ CRUSH rule 2 x 331 [2,6,5]
+ CRUSH rule 2 x 332 [2,5,6]
+ CRUSH rule 2 x 333 [6,5,2]
+ CRUSH rule 2 x 334 [8,5,2]
+ CRUSH rule 2 x 335 [7,2,5]
+ CRUSH rule 2 x 336 [5,6,2]
+ CRUSH rule 2 x 337 [7,2,5]
+ CRUSH rule 2 x 338 [5,6,2]
+ CRUSH rule 2 x 339 [7,5,2]
+ CRUSH rule 2 x 340 [2,8,5]
+ CRUSH rule 2 x 341 [5,2,7]
+ CRUSH rule 2 x 342 [2,7,5]
+ CRUSH rule 2 x 343 [6,5,2]
+ CRUSH rule 2 x 344 [6,2,5]
+ CRUSH rule 2 x 345 [5,7,2]
+ CRUSH rule 2 x 346 [8,2,5]
+ CRUSH rule 2 x 347 [5,2,7]
+ CRUSH rule 2 x 348 [8,2,5]
+ CRUSH rule 2 x 349 [2,6,5]
+ CRUSH rule 2 x 350 [8,5,2]
+ CRUSH rule 2 x 351 [5,6,2]
+ CRUSH rule 2 x 352 [2,8,5]
+ CRUSH rule 2 x 353 [6,5,2]
+ CRUSH rule 2 x 354 [2,5,6]
+ CRUSH rule 2 x 355 [5,8,2]
+ CRUSH rule 2 x 356 [5,2,6]
+ CRUSH rule 2 x 357 [6,2,5]
+ CRUSH rule 2 x 358 [2,8,5]
+ CRUSH rule 2 x 359 [6,2,5]
+ CRUSH rule 2 x 360 [5,2,7]
+ CRUSH rule 2 x 361 [8,5,2]
+ CRUSH rule 2 x 362 [5,2,6]
+ CRUSH rule 2 x 363 [5,2,6]
+ CRUSH rule 2 x 364 [2,5,6]
+ CRUSH rule 2 x 365 [6,5,2]
+ CRUSH rule 2 x 366 [7,2,5]
+ CRUSH rule 2 x 367 [5,2,6]
+ CRUSH rule 2 x 368 [7,5,2]
+ CRUSH rule 2 x 369 [5,7,2]
+ CRUSH rule 2 x 370 [8,2,5]
+ CRUSH rule 2 x 371 [2,5,6]
+ CRUSH rule 2 x 372 [5,2,8]
+ CRUSH rule 2 x 373 [2,6,5]
+ CRUSH rule 2 x 374 [5,8,2]
+ CRUSH rule 2 x 375 [6,5,2]
+ CRUSH rule 2 x 376 [7,2,5]
+ CRUSH rule 2 x 377 [2,5,6]
+ CRUSH rule 2 x 378 [2,8,5]
+ CRUSH rule 2 x 379 [8,5,2]
+ CRUSH rule 2 x 380 [2,5,8]
+ CRUSH rule 2 x 381 [2,5,7]
+ CRUSH rule 2 x 382 [2,5,8]
+ CRUSH rule 2 x 383 [5,6,2]
+ CRUSH rule 2 x 384 [7,2,5]
+ CRUSH rule 2 x 385 [7,5,2]
+ CRUSH rule 2 x 386 [2,5,6]
+ CRUSH rule 2 x 387 [2,5,6]
+ CRUSH rule 2 x 388 [5,2,8]
+ CRUSH rule 2 x 389 [2,5,7]
+ CRUSH rule 2 x 390 [5,6,2]
+ CRUSH rule 2 x 391 [5,6,2]
+ CRUSH rule 2 x 392 [2,8,5]
+ CRUSH rule 2 x 393 [5,2,6]
+ CRUSH rule 2 x 394 [5,7,2]
+ CRUSH rule 2 x 395 [5,2,8]
+ CRUSH rule 2 x 396 [5,2,7]
+ CRUSH rule 2 x 397 [2,5,8]
+ CRUSH rule 2 x 398 [2,5,8]
+ CRUSH rule 2 x 399 [8,5,2]
+ CRUSH rule 2 x 400 [8,2,5]
+ CRUSH rule 2 x 401 [2,5,8]
+ CRUSH rule 2 x 402 [7,5,2]
+ CRUSH rule 2 x 403 [2,5,8]
+ CRUSH rule 2 x 404 [5,2,8]
+ CRUSH rule 2 x 405 [6,5,2]
+ CRUSH rule 2 x 406 [2,6,5]
+ CRUSH rule 2 x 407 [2,8,5]
+ CRUSH rule 2 x 408 [5,2,6]
+ CRUSH rule 2 x 409 [7,5,2]
+ CRUSH rule 2 x 410 [8,5,2]
+ CRUSH rule 2 x 411 [2,8,5]
+ CRUSH rule 2 x 412 [2,5,8]
+ CRUSH rule 2 x 413 [5,2,8]
+ CRUSH rule 2 x 414 [5,2,6]
+ CRUSH rule 2 x 415 [2,6,5]
+ CRUSH rule 2 x 416 [2,6,5]
+ CRUSH rule 2 x 417 [8,2,5]
+ CRUSH rule 2 x 418 [7,2,5]
+ CRUSH rule 2 x 419 [8,5,2]
+ CRUSH rule 2 x 420 [2,5,6]
+ CRUSH rule 2 x 421 [8,5,2]
+ CRUSH rule 2 x 422 [6,5,2]
+ CRUSH rule 2 x 423 [2,5,6]
+ CRUSH rule 2 x 424 [8,5,2]
+ CRUSH rule 2 x 425 [2,5,7]
+ CRUSH rule 2 x 426 [6,2,5]
+ CRUSH rule 2 x 427 [2,7,5]
+ CRUSH rule 2 x 428 [5,7,2]
+ CRUSH rule 2 x 429 [5,6,2]
+ CRUSH rule 2 x 430 [5,6,2]
+ CRUSH rule 2 x 431 [5,2,7]
+ CRUSH rule 2 x 432 [7,2,5]
+ CRUSH rule 2 x 433 [6,5,2]
+ CRUSH rule 2 x 434 [5,2,8]
+ CRUSH rule 2 x 435 [2,5,6]
+ CRUSH rule 2 x 436 [5,2,7]
+ CRUSH rule 2 x 437 [7,5,2]
+ CRUSH rule 2 x 438 [2,5,8]
+ CRUSH rule 2 x 439 [2,5,7]
+ CRUSH rule 2 x 440 [2,7,5]
+ CRUSH rule 2 x 441 [5,7,2]
+ CRUSH rule 2 x 442 [2,5,7]
+ CRUSH rule 2 x 443 [6,2,5]
+ CRUSH rule 2 x 444 [7,2,5]
+ CRUSH rule 2 x 445 [6,5,2]
+ CRUSH rule 2 x 446 [5,2,8]
+ CRUSH rule 2 x 447 [2,5,7]
+ CRUSH rule 2 x 448 [7,2,5]
+ CRUSH rule 2 x 449 [7,5,2]
+ CRUSH rule 2 x 450 [5,2,8]
+ CRUSH rule 2 x 451 [6,5,2]
+ CRUSH rule 2 x 452 [8,5,2]
+ CRUSH rule 2 x 453 [6,5,2]
+ CRUSH rule 2 x 454 [6,5,2]
+ CRUSH rule 2 x 455 [2,7,5]
+ CRUSH rule 2 x 456 [6,2,5]
+ CRUSH rule 2 x 457 [7,2,5]
+ CRUSH rule 2 x 458 [2,8,5]
+ CRUSH rule 2 x 459 [2,7,5]
+ CRUSH rule 2 x 460 [6,5,2]
+ CRUSH rule 2 x 461 [6,5,2]
+ CRUSH rule 2 x 462 [8,2,5]
+ CRUSH rule 2 x 463 [6,2,5]
+ CRUSH rule 2 x 464 [7,5,2]
+ CRUSH rule 2 x 465 [7,2,5]
+ CRUSH rule 2 x 466 [5,8,2]
+ CRUSH rule 2 x 467 [6,5,2]
+ CRUSH rule 2 x 468 [7,2,5]
+ CRUSH rule 2 x 469 [7,2,5]
+ CRUSH rule 2 x 470 [5,2,6]
+ CRUSH rule 2 x 471 [2,7,5]
+ CRUSH rule 2 x 472 [5,2,8]
+ CRUSH rule 2 x 473 [2,5,6]
+ CRUSH rule 2 x 474 [6,2,5]
+ CRUSH rule 2 x 475 [6,2,5]
+ CRUSH rule 2 x 476 [5,6,2]
+ CRUSH rule 2 x 477 [5,8,2]
+ CRUSH rule 2 x 478 [6,2,5]
+ CRUSH rule 2 x 479 [2,5,8]
+ CRUSH rule 2 x 480 [2,8,5]
+ CRUSH rule 2 x 481 [2,5,7]
+ CRUSH rule 2 x 482 [5,7,2]
+ CRUSH rule 2 x 483 [2,6,5]
+ CRUSH rule 2 x 484 [2,7,5]
+ CRUSH rule 2 x 485 [5,7,2]
+ CRUSH rule 2 x 486 [5,2,7]
+ CRUSH rule 2 x 487 [5,2,8]
+ CRUSH rule 2 x 488 [5,7,2]
+ CRUSH rule 2 x 489 [2,8,5]
+ CRUSH rule 2 x 490 [6,5,2]
+ CRUSH rule 2 x 491 [2,7,5]
+ CRUSH rule 2 x 492 [6,5,2]
+ CRUSH rule 2 x 493 [2,7,5]
+ CRUSH rule 2 x 494 [2,7,5]
+ CRUSH rule 2 x 495 [5,2,8]
+ CRUSH rule 2 x 496 [7,5,2]
+ CRUSH rule 2 x 497 [5,7,2]
+ CRUSH rule 2 x 498 [2,5,8]
+ CRUSH rule 2 x 499 [8,5,2]
+ CRUSH rule 2 x 500 [5,6,2]
+ CRUSH rule 2 x 501 [2,7,5]
+ CRUSH rule 2 x 502 [7,2,5]
+ CRUSH rule 2 x 503 [2,5,7]
+ CRUSH rule 2 x 504 [5,6,2]
+ CRUSH rule 2 x 505 [2,7,5]
+ CRUSH rule 2 x 506 [5,2,8]
+ CRUSH rule 2 x 507 [6,2,5]
+ CRUSH rule 2 x 508 [2,5,8]
+ CRUSH rule 2 x 509 [7,5,2]
+ CRUSH rule 2 x 510 [6,2,5]
+ CRUSH rule 2 x 511 [5,8,2]
+ CRUSH rule 2 x 512 [7,2,5]
+ CRUSH rule 2 x 513 [7,2,5]
+ CRUSH rule 2 x 514 [5,6,2]
+ CRUSH rule 2 x 515 [8,5,2]
+ CRUSH rule 2 x 516 [5,2,8]
+ CRUSH rule 2 x 517 [7,2,5]
+ CRUSH rule 2 x 518 [5,6,2]
+ CRUSH rule 2 x 519 [7,5,2]
+ CRUSH rule 2 x 520 [2,6,5]
+ CRUSH rule 2 x 521 [8,2,5]
+ CRUSH rule 2 x 522 [6,2,5]
+ CRUSH rule 2 x 523 [5,2,7]
+ CRUSH rule 2 x 524 [2,5,8]
+ CRUSH rule 2 x 525 [2,5,6]
+ CRUSH rule 2 x 526 [2,5,8]
+ CRUSH rule 2 x 527 [2,5,6]
+ CRUSH rule 2 x 528 [5,2,6]
+ CRUSH rule 2 x 529 [5,7,2]
+ CRUSH rule 2 x 530 [6,5,2]
+ CRUSH rule 2 x 531 [6,2,5]
+ CRUSH rule 2 x 532 [6,5,2]
+ CRUSH rule 2 x 533 [5,6,2]
+ CRUSH rule 2 x 534 [7,5,2]
+ CRUSH rule 2 x 535 [8,2,5]
+ CRUSH rule 2 x 536 [6,2,5]
+ CRUSH rule 2 x 537 [5,7,2]
+ CRUSH rule 2 x 538 [6,5,2]
+ CRUSH rule 2 x 539 [8,5,2]
+ CRUSH rule 2 x 540 [2,6,5]
+ CRUSH rule 2 x 541 [2,5,8]
+ CRUSH rule 2 x 542 [5,2,8]
+ CRUSH rule 2 x 543 [6,2,5]
+ CRUSH rule 2 x 544 [5,7,2]
+ CRUSH rule 2 x 545 [5,7,2]
+ CRUSH rule 2 x 546 [6,2,5]
+ CRUSH rule 2 x 547 [8,2,5]
+ CRUSH rule 2 x 548 [5,2,8]
+ CRUSH rule 2 x 549 [5,8,2]
+ CRUSH rule 2 x 550 [2,5,7]
+ CRUSH rule 2 x 551 [7,5,2]
+ CRUSH rule 2 x 552 [5,8,2]
+ CRUSH rule 2 x 553 [5,2,7]
+ CRUSH rule 2 x 554 [2,8,5]
+ CRUSH rule 2 x 555 [5,2,8]
+ CRUSH rule 2 x 556 [5,6,2]
+ CRUSH rule 2 x 557 [7,5,2]
+ CRUSH rule 2 x 558 [5,2,6]
+ CRUSH rule 2 x 559 [5,2,6]
+ CRUSH rule 2 x 560 [8,5,2]
+ CRUSH rule 2 x 561 [6,5,2]
+ CRUSH rule 2 x 562 [5,2,6]
+ CRUSH rule 2 x 563 [2,6,5]
+ CRUSH rule 2 x 564 [5,2,7]
+ CRUSH rule 2 x 565 [5,6,2]
+ CRUSH rule 2 x 566 [5,7,2]
+ CRUSH rule 2 x 567 [5,6,2]
+ CRUSH rule 2 x 568 [7,5,2]
+ CRUSH rule 2 x 569 [5,2,7]
+ CRUSH rule 2 x 570 [2,5,8]
+ CRUSH rule 2 x 571 [5,7,2]
+ CRUSH rule 2 x 572 [5,2,8]
+ CRUSH rule 2 x 573 [5,2,7]
+ CRUSH rule 2 x 574 [2,5,8]
+ CRUSH rule 2 x 575 [8,2,5]
+ CRUSH rule 2 x 576 [5,6,2]
+ CRUSH rule 2 x 577 [8,2,5]
+ CRUSH rule 2 x 578 [6,2,5]
+ CRUSH rule 2 x 579 [5,2,6]
+ CRUSH rule 2 x 580 [5,2,7]
+ CRUSH rule 2 x 581 [7,2,5]
+ CRUSH rule 2 x 582 [2,8,5]
+ CRUSH rule 2 x 583 [6,2,5]
+ CRUSH rule 2 x 584 [8,2,5]
+ CRUSH rule 2 x 585 [7,2,5]
+ CRUSH rule 2 x 586 [2,7,5]
+ CRUSH rule 2 x 587 [2,5,7]
+ CRUSH rule 2 x 588 [5,7,2]
+ CRUSH rule 2 x 589 [7,2,5]
+ CRUSH rule 2 x 590 [6,2,5]
+ CRUSH rule 2 x 591 [5,2,8]
+ CRUSH rule 2 x 592 [2,5,7]
+ CRUSH rule 2 x 593 [2,8,5]
+ CRUSH rule 2 x 594 [2,7,5]
+ CRUSH rule 2 x 595 [7,2,5]
+ CRUSH rule 2 x 596 [5,2,6]
+ CRUSH rule 2 x 597 [5,2,7]
+ CRUSH rule 2 x 598 [5,2,6]
+ CRUSH rule 2 x 599 [5,2,8]
+ CRUSH rule 2 x 600 [7,2,5]
+ CRUSH rule 2 x 601 [2,7,5]
+ CRUSH rule 2 x 602 [5,7,2]
+ CRUSH rule 2 x 603 [5,2,6]
+ CRUSH rule 2 x 604 [7,5,2]
+ CRUSH rule 2 x 605 [5,2,7]
+ CRUSH rule 2 x 606 [2,7,5]
+ CRUSH rule 2 x 607 [2,5,8]
+ CRUSH rule 2 x 608 [5,2,7]
+ CRUSH rule 2 x 609 [5,2,8]
+ CRUSH rule 2 x 610 [5,7,2]
+ CRUSH rule 2 x 611 [2,8,5]
+ CRUSH rule 2 x 612 [2,6,5]
+ CRUSH rule 2 x 613 [7,2,5]
+ CRUSH rule 2 x 614 [7,2,5]
+ CRUSH rule 2 x 615 [6,2,5]
+ CRUSH rule 2 x 616 [2,8,5]
+ CRUSH rule 2 x 617 [6,2,5]
+ CRUSH rule 2 x 618 [7,5,2]
+ CRUSH rule 2 x 619 [5,2,8]
+ CRUSH rule 2 x 620 [5,2,6]
+ CRUSH rule 2 x 621 [5,8,2]
+ CRUSH rule 2 x 622 [2,5,8]
+ CRUSH rule 2 x 623 [2,6,5]
+ CRUSH rule 2 x 624 [5,2,8]
+ CRUSH rule 2 x 625 [2,5,7]
+ CRUSH rule 2 x 626 [7,2,5]
+ CRUSH rule 2 x 627 [2,7,5]
+ CRUSH rule 2 x 628 [8,2,5]
+ CRUSH rule 2 x 629 [2,6,5]
+ CRUSH rule 2 x 630 [2,7,5]
+ CRUSH rule 2 x 631 [2,7,5]
+ CRUSH rule 2 x 632 [7,2,5]
+ CRUSH rule 2 x 633 [8,5,2]
+ CRUSH rule 2 x 634 [2,5,7]
+ CRUSH rule 2 x 635 [5,6,2]
+ CRUSH rule 2 x 636 [2,5,8]
+ CRUSH rule 2 x 637 [5,2,7]
+ CRUSH rule 2 x 638 [6,2,5]
+ CRUSH rule 2 x 639 [5,2,8]
+ CRUSH rule 2 x 640 [5,2,8]
+ CRUSH rule 2 x 641 [7,2,5]
+ CRUSH rule 2 x 642 [2,7,5]
+ CRUSH rule 2 x 643 [5,2,8]
+ CRUSH rule 2 x 644 [8,2,5]
+ CRUSH rule 2 x 645 [5,7,2]
+ CRUSH rule 2 x 646 [8,2,5]
+ CRUSH rule 2 x 647 [7,2,5]
+ CRUSH rule 2 x 648 [2,6,5]
+ CRUSH rule 2 x 649 [5,7,2]
+ CRUSH rule 2 x 650 [7,5,2]
+ CRUSH rule 2 x 651 [5,7,2]
+ CRUSH rule 2 x 652 [5,7,2]
+ CRUSH rule 2 x 653 [8,5,2]
+ CRUSH rule 2 x 654 [7,5,2]
+ CRUSH rule 2 x 655 [2,5,7]
+ CRUSH rule 2 x 656 [5,7,2]
+ CRUSH rule 2 x 657 [6,2,5]
+ CRUSH rule 2 x 658 [5,6,2]
+ CRUSH rule 2 x 659 [5,6,2]
+ CRUSH rule 2 x 660 [7,5,2]
+ CRUSH rule 2 x 661 [2,8,5]
+ CRUSH rule 2 x 662 [5,2,7]
+ CRUSH rule 2 x 663 [2,5,8]
+ CRUSH rule 2 x 664 [2,5,7]
+ CRUSH rule 2 x 665 [5,7,2]
+ CRUSH rule 2 x 666 [2,8,5]
+ CRUSH rule 2 x 667 [2,5,7]
+ CRUSH rule 2 x 668 [5,7,2]
+ CRUSH rule 2 x 669 [6,5,2]
+ CRUSH rule 2 x 670 [5,2,6]
+ CRUSH rule 2 x 671 [2,7,5]
+ CRUSH rule 2 x 672 [5,2,7]
+ CRUSH rule 2 x 673 [5,2,7]
+ CRUSH rule 2 x 674 [5,2,8]
+ CRUSH rule 2 x 675 [2,8,5]
+ CRUSH rule 2 x 676 [2,5,8]
+ CRUSH rule 2 x 677 [5,2,7]
+ CRUSH rule 2 x 678 [2,5,8]
+ CRUSH rule 2 x 679 [6,2,5]
+ CRUSH rule 2 x 680 [2,5,6]
+ CRUSH rule 2 x 681 [5,7,2]
+ CRUSH rule 2 x 682 [2,5,7]
+ CRUSH rule 2 x 683 [2,5,6]
+ CRUSH rule 2 x 684 [7,2,5]
+ CRUSH rule 2 x 685 [7,2,5]
+ CRUSH rule 2 x 686 [2,5,8]
+ CRUSH rule 2 x 687 [5,6,2]
+ CRUSH rule 2 x 688 [5,7,2]
+ CRUSH rule 2 x 689 [6,5,2]
+ CRUSH rule 2 x 690 [8,2,5]
+ CRUSH rule 2 x 691 [5,2,6]
+ CRUSH rule 2 x 692 [7,2,5]
+ CRUSH rule 2 x 693 [6,5,2]
+ CRUSH rule 2 x 694 [6,5,2]
+ CRUSH rule 2 x 695 [2,8,5]
+ CRUSH rule 2 x 696 [2,5,8]
+ CRUSH rule 2 x 697 [6,2,5]
+ CRUSH rule 2 x 698 [6,2,5]
+ CRUSH rule 2 x 699 [2,6,5]
+ CRUSH rule 2 x 700 [2,5,7]
+ CRUSH rule 2 x 701 [5,2,7]
+ CRUSH rule 2 x 702 [5,2,8]
+ CRUSH rule 2 x 703 [8,5,2]
+ CRUSH rule 2 x 704 [2,5,8]
+ CRUSH rule 2 x 705 [8,2,5]
+ CRUSH rule 2 x 706 [2,5,6]
+ CRUSH rule 2 x 707 [7,5,2]
+ CRUSH rule 2 x 708 [5,7,2]
+ CRUSH rule 2 x 709 [6,5,2]
+ CRUSH rule 2 x 710 [8,5,2]
+ CRUSH rule 2 x 711 [2,5,8]
+ CRUSH rule 2 x 712 [2,5,7]
+ CRUSH rule 2 x 713 [6,5,2]
+ CRUSH rule 2 x 714 [5,2,7]
+ CRUSH rule 2 x 715 [2,5,6]
+ CRUSH rule 2 x 716 [5,6,2]
+ CRUSH rule 2 x 717 [8,2,5]
+ CRUSH rule 2 x 718 [5,7,2]
+ CRUSH rule 2 x 719 [2,6,5]
+ CRUSH rule 2 x 720 [6,2,5]
+ CRUSH rule 2 x 721 [5,7,2]
+ CRUSH rule 2 x 722 [5,7,2]
+ CRUSH rule 2 x 723 [5,2,7]
+ CRUSH rule 2 x 724 [2,6,5]
+ CRUSH rule 2 x 725 [2,5,7]
+ CRUSH rule 2 x 726 [5,8,2]
+ CRUSH rule 2 x 727 [5,6,2]
+ CRUSH rule 2 x 728 [2,7,5]
+ CRUSH rule 2 x 729 [5,6,2]
+ CRUSH rule 2 x 730 [5,7,2]
+ CRUSH rule 2 x 731 [5,2,8]
+ CRUSH rule 2 x 732 [2,5,6]
+ CRUSH rule 2 x 733 [5,7,2]
+ CRUSH rule 2 x 734 [6,5,2]
+ CRUSH rule 2 x 735 [5,8,2]
+ CRUSH rule 2 x 736 [5,8,2]
+ CRUSH rule 2 x 737 [2,6,5]
+ CRUSH rule 2 x 738 [5,2,7]
+ CRUSH rule 2 x 739 [2,7,5]
+ CRUSH rule 2 x 740 [2,8,5]
+ CRUSH rule 2 x 741 [7,2,5]
+ CRUSH rule 2 x 742 [8,2,5]
+ CRUSH rule 2 x 743 [7,2,5]
+ CRUSH rule 2 x 744 [5,7,2]
+ CRUSH rule 2 x 745 [5,2,8]
+ CRUSH rule 2 x 746 [5,2,7]
+ CRUSH rule 2 x 747 [6,2,5]
+ CRUSH rule 2 x 748 [2,7,5]
+ CRUSH rule 2 x 749 [5,8,2]
+ CRUSH rule 2 x 750 [2,6,5]
+ CRUSH rule 2 x 751 [2,8,5]
+ CRUSH rule 2 x 752 [8,2,5]
+ CRUSH rule 2 x 753 [7,5,2]
+ CRUSH rule 2 x 754 [8,5,2]
+ CRUSH rule 2 x 755 [2,6,5]
+ CRUSH rule 2 x 756 [5,6,2]
+ CRUSH rule 2 x 757 [8,2,5]
+ CRUSH rule 2 x 758 [6,2,5]
+ CRUSH rule 2 x 759 [8,5,2]
+ CRUSH rule 2 x 760 [2,5,7]
+ CRUSH rule 2 x 761 [5,2,8]
+ CRUSH rule 2 x 762 [2,7,5]
+ CRUSH rule 2 x 763 [8,5,2]
+ CRUSH rule 2 x 764 [2,7,5]
+ CRUSH rule 2 x 765 [6,5,2]
+ CRUSH rule 2 x 766 [8,5,2]
+ CRUSH rule 2 x 767 [2,8,5]
+ CRUSH rule 2 x 768 [8,5,2]
+ CRUSH rule 2 x 769 [6,2,5]
+ CRUSH rule 2 x 770 [6,2,5]
+ CRUSH rule 2 x 771 [7,2,5]
+ CRUSH rule 2 x 772 [8,5,2]
+ CRUSH rule 2 x 773 [5,2,7]
+ CRUSH rule 2 x 774 [5,6,2]
+ CRUSH rule 2 x 775 [6,5,2]
+ CRUSH rule 2 x 776 [7,2,5]
+ CRUSH rule 2 x 777 [5,2,6]
+ CRUSH rule 2 x 778 [2,8,5]
+ CRUSH rule 2 x 779 [2,7,5]
+ CRUSH rule 2 x 780 [2,5,7]
+ CRUSH rule 2 x 781 [6,5,2]
+ CRUSH rule 2 x 782 [5,2,8]
+ CRUSH rule 2 x 783 [7,2,5]
+ CRUSH rule 2 x 784 [2,5,6]
+ CRUSH rule 2 x 785 [6,2,5]
+ CRUSH rule 2 x 786 [7,5,2]
+ CRUSH rule 2 x 787 [2,6,5]
+ CRUSH rule 2 x 788 [6,2,5]
+ CRUSH rule 2 x 789 [2,5,8]
+ CRUSH rule 2 x 790 [8,5,2]
+ CRUSH rule 2 x 791 [5,8,2]
+ CRUSH rule 2 x 792 [5,8,2]
+ CRUSH rule 2 x 793 [6,2,5]
+ CRUSH rule 2 x 794 [2,6,5]
+ CRUSH rule 2 x 795 [2,5,8]
+ CRUSH rule 2 x 796 [5,7,2]
+ CRUSH rule 2 x 797 [2,5,8]
+ CRUSH rule 2 x 798 [6,2,5]
+ CRUSH rule 2 x 799 [5,2,8]
+ CRUSH rule 2 x 800 [5,2,7]
+ CRUSH rule 2 x 801 [5,6,2]
+ CRUSH rule 2 x 802 [2,8,5]
+ CRUSH rule 2 x 803 [2,5,7]
+ CRUSH rule 2 x 804 [6,2,5]
+ CRUSH rule 2 x 805 [5,6,2]
+ CRUSH rule 2 x 806 [2,5,7]
+ CRUSH rule 2 x 807 [5,7,2]
+ CRUSH rule 2 x 808 [5,6,2]
+ CRUSH rule 2 x 809 [2,5,8]
+ CRUSH rule 2 x 810 [5,7,2]
+ CRUSH rule 2 x 811 [8,5,2]
+ CRUSH rule 2 x 812 [8,5,2]
+ CRUSH rule 2 x 813 [6,5,2]
+ CRUSH rule 2 x 814 [5,6,2]
+ CRUSH rule 2 x 815 [5,2,8]
+ CRUSH rule 2 x 816 [2,7,5]
+ CRUSH rule 2 x 817 [5,8,2]
+ CRUSH rule 2 x 818 [5,2,7]
+ CRUSH rule 2 x 819 [5,2,8]
+ CRUSH rule 2 x 820 [5,6,2]
+ CRUSH rule 2 x 821 [5,6,2]
+ CRUSH rule 2 x 822 [2,5,8]
+ CRUSH rule 2 x 823 [5,8,2]
+ CRUSH rule 2 x 824 [5,7,2]
+ CRUSH rule 2 x 825 [2,8,5]
+ CRUSH rule 2 x 826 [7,2,5]
+ CRUSH rule 2 x 827 [2,8,5]
+ CRUSH rule 2 x 828 [2,5,8]
+ CRUSH rule 2 x 829 [5,6,2]
+ CRUSH rule 2 x 830 [2,5,8]
+ CRUSH rule 2 x 831 [2,6,5]
+ CRUSH rule 2 x 832 [5,7,2]
+ CRUSH rule 2 x 833 [2,7,5]
+ CRUSH rule 2 x 834 [5,2,7]
+ CRUSH rule 2 x 835 [8,5,2]
+ CRUSH rule 2 x 836 [5,7,2]
+ CRUSH rule 2 x 837 [6,5,2]
+ CRUSH rule 2 x 838 [6,2,5]
+ CRUSH rule 2 x 839 [5,2,6]
+ CRUSH rule 2 x 840 [7,5,2]
+ CRUSH rule 2 x 841 [5,8,2]
+ CRUSH rule 2 x 842 [2,5,6]
+ CRUSH rule 2 x 843 [6,5,2]
+ CRUSH rule 2 x 844 [5,8,2]
+ CRUSH rule 2 x 845 [5,8,2]
+ CRUSH rule 2 x 846 [5,2,7]
+ CRUSH rule 2 x 847 [2,8,5]
+ CRUSH rule 2 x 848 [2,6,5]
+ CRUSH rule 2 x 849 [5,6,2]
+ CRUSH rule 2 x 850 [2,5,6]
+ CRUSH rule 2 x 851 [6,5,2]
+ CRUSH rule 2 x 852 [7,5,2]
+ CRUSH rule 2 x 853 [6,2,5]
+ CRUSH rule 2 x 854 [7,2,5]
+ CRUSH rule 2 x 855 [5,7,2]
+ CRUSH rule 2 x 856 [6,5,2]
+ CRUSH rule 2 x 857 [8,5,2]
+ CRUSH rule 2 x 858 [6,5,2]
+ CRUSH rule 2 x 859 [6,2,5]
+ CRUSH rule 2 x 860 [5,2,7]
+ CRUSH rule 2 x 861 [8,5,2]
+ CRUSH rule 2 x 862 [6,2,5]
+ CRUSH rule 2 x 863 [8,2,5]
+ CRUSH rule 2 x 864 [5,6,2]
+ CRUSH rule 2 x 865 [8,2,5]
+ CRUSH rule 2 x 866 [5,6,2]
+ CRUSH rule 2 x 867 [6,5,2]
+ CRUSH rule 2 x 868 [6,5,2]
+ CRUSH rule 2 x 869 [8,5,2]
+ CRUSH rule 2 x 870 [2,5,8]
+ CRUSH rule 2 x 871 [5,2,8]
+ CRUSH rule 2 x 872 [5,2,8]
+ CRUSH rule 2 x 873 [5,6,2]
+ CRUSH rule 2 x 874 [2,6,5]
+ CRUSH rule 2 x 875 [2,6,5]
+ CRUSH rule 2 x 876 [5,8,2]
+ CRUSH rule 2 x 877 [6,5,2]
+ CRUSH rule 2 x 878 [5,2,7]
+ CRUSH rule 2 x 879 [7,5,2]
+ CRUSH rule 2 x 880 [5,2,8]
+ CRUSH rule 2 x 881 [5,6,2]
+ CRUSH rule 2 x 882 [5,2,7]
+ CRUSH rule 2 x 883 [2,5,7]
+ CRUSH rule 2 x 884 [6,2,5]
+ CRUSH rule 2 x 885 [5,2,8]
+ CRUSH rule 2 x 886 [5,6,2]
+ CRUSH rule 2 x 887 [7,5,2]
+ CRUSH rule 2 x 888 [6,2,5]
+ CRUSH rule 2 x 889 [2,6,5]
+ CRUSH rule 2 x 890 [7,2,5]
+ CRUSH rule 2 x 891 [2,8,5]
+ CRUSH rule 2 x 892 [6,2,5]
+ CRUSH rule 2 x 893 [2,5,7]
+ CRUSH rule 2 x 894 [7,5,2]
+ CRUSH rule 2 x 895 [5,2,8]
+ CRUSH rule 2 x 896 [2,8,5]
+ CRUSH rule 2 x 897 [5,2,6]
+ CRUSH rule 2 x 898 [2,5,7]
+ CRUSH rule 2 x 899 [2,7,5]
+ CRUSH rule 2 x 900 [5,2,6]
+ CRUSH rule 2 x 901 [5,2,8]
+ CRUSH rule 2 x 902 [8,5,2]
+ CRUSH rule 2 x 903 [5,7,2]
+ CRUSH rule 2 x 904 [5,6,2]
+ CRUSH rule 2 x 905 [6,2,5]
+ CRUSH rule 2 x 906 [2,6,5]
+ CRUSH rule 2 x 907 [7,2,5]
+ CRUSH rule 2 x 908 [5,8,2]
+ CRUSH rule 2 x 909 [2,5,7]
+ CRUSH rule 2 x 910 [6,5,2]
+ CRUSH rule 2 x 911 [5,8,2]
+ CRUSH rule 2 x 912 [2,7,5]
+ CRUSH rule 2 x 913 [7,2,5]
+ CRUSH rule 2 x 914 [6,5,2]
+ CRUSH rule 2 x 915 [8,2,5]
+ CRUSH rule 2 x 916 [5,2,8]
+ CRUSH rule 2 x 917 [2,5,8]
+ CRUSH rule 2 x 918 [8,2,5]
+ CRUSH rule 2 x 919 [6,2,5]
+ CRUSH rule 2 x 920 [7,5,2]
+ CRUSH rule 2 x 921 [2,5,6]
+ CRUSH rule 2 x 922 [6,5,2]
+ CRUSH rule 2 x 923 [5,8,2]
+ CRUSH rule 2 x 924 [5,2,7]
+ CRUSH rule 2 x 925 [5,7,2]
+ CRUSH rule 2 x 926 [5,2,8]
+ CRUSH rule 2 x 927 [2,6,5]
+ CRUSH rule 2 x 928 [8,2,5]
+ CRUSH rule 2 x 929 [5,2,7]
+ CRUSH rule 2 x 930 [2,5,6]
+ CRUSH rule 2 x 931 [5,2,7]
+ CRUSH rule 2 x 932 [5,2,8]
+ CRUSH rule 2 x 933 [8,5,2]
+ CRUSH rule 2 x 934 [5,6,2]
+ CRUSH rule 2 x 935 [6,5,2]
+ CRUSH rule 2 x 936 [2,6,5]
+ CRUSH rule 2 x 937 [5,8,2]
+ CRUSH rule 2 x 938 [6,5,2]
+ CRUSH rule 2 x 939 [2,7,5]
+ CRUSH rule 2 x 940 [8,5,2]
+ CRUSH rule 2 x 941 [5,2,8]
+ CRUSH rule 2 x 942 [2,8,5]
+ CRUSH rule 2 x 943 [8,2,5]
+ CRUSH rule 2 x 944 [5,8,2]
+ CRUSH rule 2 x 945 [7,2,5]
+ CRUSH rule 2 x 946 [2,8,5]
+ CRUSH rule 2 x 947 [5,2,8]
+ CRUSH rule 2 x 948 [7,5,2]
+ CRUSH rule 2 x 949 [6,2,5]
+ CRUSH rule 2 x 950 [5,6,2]
+ CRUSH rule 2 x 951 [5,8,2]
+ CRUSH rule 2 x 952 [2,7,5]
+ CRUSH rule 2 x 953 [2,5,6]
+ CRUSH rule 2 x 954 [5,2,7]
+ CRUSH rule 2 x 955 [8,2,5]
+ CRUSH rule 2 x 956 [2,6,5]
+ CRUSH rule 2 x 957 [7,2,5]
+ CRUSH rule 2 x 958 [8,5,2]
+ CRUSH rule 2 x 959 [5,2,7]
+ CRUSH rule 2 x 960 [5,6,2]
+ CRUSH rule 2 x 961 [5,2,8]
+ CRUSH rule 2 x 962 [7,5,2]
+ CRUSH rule 2 x 963 [2,5,6]
+ CRUSH rule 2 x 964 [5,2,8]
+ CRUSH rule 2 x 965 [7,5,2]
+ CRUSH rule 2 x 966 [5,8,2]
+ CRUSH rule 2 x 967 [8,5,2]
+ CRUSH rule 2 x 968 [7,2,5]
+ CRUSH rule 2 x 969 [8,2,5]
+ CRUSH rule 2 x 970 [2,6,5]
+ CRUSH rule 2 x 971 [2,7,5]
+ CRUSH rule 2 x 972 [2,8,5]
+ CRUSH rule 2 x 973 [2,6,5]
+ CRUSH rule 2 x 974 [5,2,8]
+ CRUSH rule 2 x 975 [5,7,2]
+ CRUSH rule 2 x 976 [5,8,2]
+ CRUSH rule 2 x 977 [8,5,2]
+ CRUSH rule 2 x 978 [7,2,5]
+ CRUSH rule 2 x 979 [7,2,5]
+ CRUSH rule 2 x 980 [6,2,5]
+ CRUSH rule 2 x 981 [7,5,2]
+ CRUSH rule 2 x 982 [5,2,8]
+ CRUSH rule 2 x 983 [5,7,2]
+ CRUSH rule 2 x 984 [2,7,5]
+ CRUSH rule 2 x 985 [2,5,7]
+ CRUSH rule 2 x 986 [8,5,2]
+ CRUSH rule 2 x 987 [2,5,8]
+ CRUSH rule 2 x 988 [2,5,7]
+ CRUSH rule 2 x 989 [2,6,5]
+ CRUSH rule 2 x 990 [2,6,5]
+ CRUSH rule 2 x 991 [2,5,8]
+ CRUSH rule 2 x 992 [7,2,5]
+ CRUSH rule 2 x 993 [2,6,5]
+ CRUSH rule 2 x 994 [5,7,2]
+ CRUSH rule 2 x 995 [7,2,5]
+ CRUSH rule 2 x 996 [6,5,2]
+ CRUSH rule 2 x 997 [6,5,2]
+ CRUSH rule 2 x 998 [8,2,5]
+ CRUSH rule 2 x 999 [2,7,5]
+ CRUSH rule 2 x 1000 [8,5,2]
+ CRUSH rule 2 x 1001 [2,5,6]
+ CRUSH rule 2 x 1002 [2,5,7]
+ CRUSH rule 2 x 1003 [2,8,5]
+ CRUSH rule 2 x 1004 [6,2,5]
+ CRUSH rule 2 x 1005 [6,2,5]
+ CRUSH rule 2 x 1006 [2,6,5]
+ CRUSH rule 2 x 1007 [2,5,7]
+ CRUSH rule 2 x 1008 [2,7,5]
+ CRUSH rule 2 x 1009 [6,5,2]
+ CRUSH rule 2 x 1010 [5,2,7]
+ CRUSH rule 2 x 1011 [5,2,8]
+ CRUSH rule 2 x 1012 [5,2,7]
+ CRUSH rule 2 x 1013 [5,2,7]
+ CRUSH rule 2 x 1014 [2,8,5]
+ CRUSH rule 2 x 1015 [6,5,2]
+ CRUSH rule 2 x 1016 [2,5,7]
+ CRUSH rule 2 x 1017 [6,2,5]
+ CRUSH rule 2 x 1018 [5,2,6]
+ CRUSH rule 2 x 1019 [5,8,2]
+ CRUSH rule 2 x 1020 [5,2,7]
+ CRUSH rule 2 x 1021 [5,2,6]
+ CRUSH rule 2 x 1022 [2,6,5]
+ CRUSH rule 2 x 1023 [5,2,8]
+ rule 2 (chooseleaf) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 3 (choose-set), x = 0..1023, numrep = 2..3
+ CRUSH rule 3 x 0 [2,5]
+ CRUSH rule 3 x 1 [2,8]
+ CRUSH rule 3 x 2 [2,5]
+ CRUSH rule 3 x 3 [8,2]
+ CRUSH rule 3 x 4 [5,2]
+ CRUSH rule 3 x 5 [7,2]
+ CRUSH rule 3 x 6 [2,6]
+ CRUSH rule 3 x 7 [5,6]
+ CRUSH rule 3 x 8 [5,7]
+ CRUSH rule 3 x 9 [2,5]
+ CRUSH rule 3 x 10 [2,8]
+ CRUSH rule 3 x 11 [2,6]
+ CRUSH rule 3 x 12 [2,5]
+ CRUSH rule 3 x 13 [5,8]
+ CRUSH rule 3 x 14 [7,2]
+ CRUSH rule 3 x 15 [7,2]
+ CRUSH rule 3 x 16 [5,7]
+ CRUSH rule 3 x 17 [5,2]
+ CRUSH rule 3 x 18 [2,5]
+ CRUSH rule 3 x 19 [7,5]
+ CRUSH rule 3 x 20 [2,5]
+ CRUSH rule 3 x 21 [5,6]
+ CRUSH rule 3 x 22 [8,5]
+ CRUSH rule 3 x 23 [5,7]
+ CRUSH rule 3 x 24 [2,6]
+ CRUSH rule 3 x 25 [5,8]
+ CRUSH rule 3 x 26 [2,7]
+ CRUSH rule 3 x 27 [5,2]
+ CRUSH rule 3 x 28 [6,2]
+ CRUSH rule 3 x 29 [8,5]
+ CRUSH rule 3 x 30 [5,6]
+ CRUSH rule 3 x 31 [8,2]
+ CRUSH rule 3 x 32 [5,7]
+ CRUSH rule 3 x 33 [2,7]
+ CRUSH rule 3 x 34 [2,5]
+ CRUSH rule 3 x 35 [2,6]
+ CRUSH rule 3 x 36 [5,6]
+ CRUSH rule 3 x 37 [2,5]
+ CRUSH rule 3 x 38 [5,6]
+ CRUSH rule 3 x 39 [5,7]
+ CRUSH rule 3 x 40 [7,2]
+ CRUSH rule 3 x 41 [2,7]
+ CRUSH rule 3 x 42 [5,7]
+ CRUSH rule 3 x 43 [2,5]
+ CRUSH rule 3 x 44 [2,8]
+ CRUSH rule 3 x 45 [8,2]
+ CRUSH rule 3 x 46 [2,5]
+ CRUSH rule 3 x 47 [5,2]
+ CRUSH rule 3 x 48 [5,6]
+ CRUSH rule 3 x 49 [5,6]
+ CRUSH rule 3 x 50 [5,2]
+ CRUSH rule 3 x 51 [5,6]
+ CRUSH rule 3 x 52 [8,2]
+ CRUSH rule 3 x 53 [5,6]
+ CRUSH rule 3 x 54 [7,5]
+ CRUSH rule 3 x 55 [8,2]
+ CRUSH rule 3 x 56 [6,5]
+ CRUSH rule 3 x 57 [5,8]
+ CRUSH rule 3 x 58 [2,8]
+ CRUSH rule 3 x 59 [5,2]
+ CRUSH rule 3 x 60 [5,2]
+ CRUSH rule 3 x 61 [5,8]
+ CRUSH rule 3 x 62 [7,2]
+ CRUSH rule 3 x 63 [5,6]
+ CRUSH rule 3 x 64 [5,2]
+ CRUSH rule 3 x 65 [7,5]
+ CRUSH rule 3 x 66 [5,6]
+ CRUSH rule 3 x 67 [5,2]
+ CRUSH rule 3 x 68 [2,5]
+ CRUSH rule 3 x 69 [5,2]
+ CRUSH rule 3 x 70 [7,2]
+ CRUSH rule 3 x 71 [2,7]
+ CRUSH rule 3 x 72 [6,2]
+ CRUSH rule 3 x 73 [2,7]
+ CRUSH rule 3 x 74 [2,8]
+ CRUSH rule 3 x 75 [5,2]
+ CRUSH rule 3 x 76 [5,2]
+ CRUSH rule 3 x 77 [7,2]
+ CRUSH rule 3 x 78 [2,5]
+ CRUSH rule 3 x 79 [5,2]
+ CRUSH rule 3 x 80 [2,5]
+ CRUSH rule 3 x 81 [2,5]
+ CRUSH rule 3 x 82 [7,2]
+ CRUSH rule 3 x 83 [2,6]
+ CRUSH rule 3 x 84 [7,2]
+ CRUSH rule 3 x 85 [5,7]
+ CRUSH rule 3 x 86 [2,6]
+ CRUSH rule 3 x 87 [2,6]
+ CRUSH rule 3 x 88 [2,8]
+ CRUSH rule 3 x 89 [5,2]
+ CRUSH rule 3 x 90 [6,5]
+ CRUSH rule 3 x 91 [5,6]
+ CRUSH rule 3 x 92 [2,6]
+ CRUSH rule 3 x 93 [7,5]
+ CRUSH rule 3 x 94 [2,5]
+ CRUSH rule 3 x 95 [7,5]
+ CRUSH rule 3 x 96 [5,8]
+ CRUSH rule 3 x 97 [8,5]
+ CRUSH rule 3 x 98 [2,7]
+ CRUSH rule 3 x 99 [2,8]
+ CRUSH rule 3 x 100 [2,6]
+ CRUSH rule 3 x 101 [5,8]
+ CRUSH rule 3 x 102 [5,2]
+ CRUSH rule 3 x 103 [5,7]
+ CRUSH rule 3 x 104 [7,5]
+ CRUSH rule 3 x 105 [2,5]
+ CRUSH rule 3 x 106 [2,6]
+ CRUSH rule 3 x 107 [5,2]
+ CRUSH rule 3 x 108 [7,2]
+ CRUSH rule 3 x 109 [2,5]
+ CRUSH rule 3 x 110 [5,2]
+ CRUSH rule 3 x 111 [2,5]
+ CRUSH rule 3 x 112 [2,6]
+ CRUSH rule 3 x 113 [6,2]
+ CRUSH rule 3 x 114 [7,5]
+ CRUSH rule 3 x 115 [8,2]
+ CRUSH rule 3 x 116 [2,7]
+ CRUSH rule 3 x 117 [7,5]
+ CRUSH rule 3 x 118 [2,5]
+ CRUSH rule 3 x 119 [5,7]
+ CRUSH rule 3 x 120 [2,5]
+ CRUSH rule 3 x 121 [2,8]
+ CRUSH rule 3 x 122 [8,5]
+ CRUSH rule 3 x 123 [2,5]
+ CRUSH rule 3 x 124 [5,2]
+ CRUSH rule 3 x 125 [2,7]
+ CRUSH rule 3 x 126 [5,2]
+ CRUSH rule 3 x 127 [5,6]
+ CRUSH rule 3 x 128 [5,8]
+ CRUSH rule 3 x 129 [2,5]
+ CRUSH rule 3 x 130 [5,8]
+ CRUSH rule 3 x 131 [2,5]
+ CRUSH rule 3 x 132 [2,5]
+ CRUSH rule 3 x 133 [5,6]
+ CRUSH rule 3 x 134 [2,7]
+ CRUSH rule 3 x 135 [5,7]
+ CRUSH rule 3 x 136 [2,5]
+ CRUSH rule 3 x 137 [7,5]
+ CRUSH rule 3 x 138 [8,5]
+ CRUSH rule 3 x 139 [5,2]
+ CRUSH rule 3 x 140 [2,8]
+ CRUSH rule 3 x 141 [6,2]
+ CRUSH rule 3 x 142 [5,2]
+ CRUSH rule 3 x 143 [5,7]
+ CRUSH rule 3 x 144 [8,2]
+ CRUSH rule 3 x 145 [8,5]
+ CRUSH rule 3 x 146 [2,8]
+ CRUSH rule 3 x 147 [2,6]
+ CRUSH rule 3 x 148 [5,2]
+ CRUSH rule 3 x 149 [5,6]
+ CRUSH rule 3 x 150 [2,8]
+ CRUSH rule 3 x 151 [5,8]
+ CRUSH rule 3 x 152 [8,5]
+ CRUSH rule 3 x 153 [8,5]
+ CRUSH rule 3 x 154 [5,2]
+ CRUSH rule 3 x 155 [5,6]
+ CRUSH rule 3 x 156 [5,2]
+ CRUSH rule 3 x 157 [5,2]
+ CRUSH rule 3 x 158 [2,6]
+ CRUSH rule 3 x 159 [7,2]
+ CRUSH rule 3 x 160 [2,8]
+ CRUSH rule 3 x 161 [2,5]
+ CRUSH rule 3 x 162 [2,8]
+ CRUSH rule 3 x 163 [5,8]
+ CRUSH rule 3 x 164 [7,2]
+ CRUSH rule 3 x 165 [7,2]
+ CRUSH rule 3 x 166 [2,5]
+ CRUSH rule 3 x 167 [2,6]
+ CRUSH rule 3 x 168 [5,2]
+ CRUSH rule 3 x 169 [2,7]
+ CRUSH rule 3 x 170 [2,5]
+ CRUSH rule 3 x 171 [7,5]
+ CRUSH rule 3 x 172 [2,8]
+ CRUSH rule 3 x 173 [8,5]
+ CRUSH rule 3 x 174 [2,5]
+ CRUSH rule 3 x 175 [6,2]
+ CRUSH rule 3 x 176 [5,2]
+ CRUSH rule 3 x 177 [5,2]
+ CRUSH rule 3 x 178 [5,2]
+ CRUSH rule 3 x 179 [5,2]
+ CRUSH rule 3 x 180 [5,7]
+ CRUSH rule 3 x 181 [6,2]
+ CRUSH rule 3 x 182 [8,5]
+ CRUSH rule 3 x 183 [7,5]
+ CRUSH rule 3 x 184 [5,6]
+ CRUSH rule 3 x 185 [6,2]
+ CRUSH rule 3 x 186 [2,5]
+ CRUSH rule 3 x 187 [2,6]
+ CRUSH rule 3 x 188 [2,6]
+ CRUSH rule 3 x 189 [2,6]
+ CRUSH rule 3 x 190 [5,2]
+ CRUSH rule 3 x 191 [7,2]
+ CRUSH rule 3 x 192 [5,2]
+ CRUSH rule 3 x 193 [5,2]
+ CRUSH rule 3 x 194 [2,5]
+ CRUSH rule 3 x 195 [6,5]
+ CRUSH rule 3 x 196 [6,2]
+ CRUSH rule 3 x 197 [6,5]
+ CRUSH rule 3 x 198 [2,5]
+ CRUSH rule 3 x 199 [2,5]
+ CRUSH rule 3 x 200 [2,5]
+ CRUSH rule 3 x 201 [7,2]
+ CRUSH rule 3 x 202 [6,5]
+ CRUSH rule 3 x 203 [5,6]
+ CRUSH rule 3 x 204 [2,5]
+ CRUSH rule 3 x 205 [2,7]
+ CRUSH rule 3 x 206 [2,8]
+ CRUSH rule 3 x 207 [5,2]
+ CRUSH rule 3 x 208 [7,2]
+ CRUSH rule 3 x 209 [2,8]
+ CRUSH rule 3 x 210 [2,5]
+ CRUSH rule 3 x 211 [5,2]
+ CRUSH rule 3 x 212 [7,5]
+ CRUSH rule 3 x 213 [8,5]
+ CRUSH rule 3 x 214 [5,7]
+ CRUSH rule 3 x 215 [8,2]
+ CRUSH rule 3 x 216 [5,2]
+ CRUSH rule 3 x 217 [2,7]
+ CRUSH rule 3 x 218 [2,7]
+ CRUSH rule 3 x 219 [5,6]
+ CRUSH rule 3 x 220 [5,8]
+ CRUSH rule 3 x 221 [5,7]
+ CRUSH rule 3 x 222 [6,5]
+ CRUSH rule 3 x 223 [2,5]
+ CRUSH rule 3 x 224 [2,5]
+ CRUSH rule 3 x 225 [8,2]
+ CRUSH rule 3 x 226 [7,2]
+ CRUSH rule 3 x 227 [5,2]
+ CRUSH rule 3 x 228 [5,6]
+ CRUSH rule 3 x 229 [5,7]
+ CRUSH rule 3 x 230 [5,6]
+ CRUSH rule 3 x 231 [5,7]
+ CRUSH rule 3 x 232 [2,8]
+ CRUSH rule 3 x 233 [5,6]
+ CRUSH rule 3 x 234 [2,5]
+ CRUSH rule 3 x 235 [5,6]
+ CRUSH rule 3 x 236 [5,2]
+ CRUSH rule 3 x 237 [5,8]
+ CRUSH rule 3 x 238 [5,2]
+ CRUSH rule 3 x 239 [8,5]
+ CRUSH rule 3 x 240 [5,8]
+ CRUSH rule 3 x 241 [5,2]
+ CRUSH rule 3 x 242 [5,2]
+ CRUSH rule 3 x 243 [5,8]
+ CRUSH rule 3 x 244 [5,6]
+ CRUSH rule 3 x 245 [7,2]
+ CRUSH rule 3 x 246 [2,5]
+ CRUSH rule 3 x 247 [6,2]
+ CRUSH rule 3 x 248 [8,2]
+ CRUSH rule 3 x 249 [2,5]
+ CRUSH rule 3 x 250 [2,5]
+ CRUSH rule 3 x 251 [2,5]
+ CRUSH rule 3 x 252 [5,7]
+ CRUSH rule 3 x 253 [5,2]
+ CRUSH rule 3 x 254 [5,2]
+ CRUSH rule 3 x 255 [2,7]
+ CRUSH rule 3 x 256 [5,6]
+ CRUSH rule 3 x 257 [2,6]
+ CRUSH rule 3 x 258 [5,2]
+ CRUSH rule 3 x 259 [5,6]
+ CRUSH rule 3 x 260 [5,8]
+ CRUSH rule 3 x 261 [8,5]
+ CRUSH rule 3 x 262 [5,6]
+ CRUSH rule 3 x 263 [6,2]
+ CRUSH rule 3 x 264 [5,6]
+ CRUSH rule 3 x 265 [8,5]
+ CRUSH rule 3 x 266 [8,2]
+ CRUSH rule 3 x 267 [2,5]
+ CRUSH rule 3 x 268 [2,6]
+ CRUSH rule 3 x 269 [2,6]
+ CRUSH rule 3 x 270 [5,2]
+ CRUSH rule 3 x 271 [7,5]
+ CRUSH rule 3 x 272 [2,6]
+ CRUSH rule 3 x 273 [5,2]
+ CRUSH rule 3 x 274 [6,5]
+ CRUSH rule 3 x 275 [5,8]
+ CRUSH rule 3 x 276 [7,2]
+ CRUSH rule 3 x 277 [6,5]
+ CRUSH rule 3 x 278 [6,2]
+ CRUSH rule 3 x 279 [8,5]
+ CRUSH rule 3 x 280 [2,7]
+ CRUSH rule 3 x 281 [8,2]
+ CRUSH rule 3 x 282 [5,2]
+ CRUSH rule 3 x 283 [8,2]
+ CRUSH rule 3 x 284 [6,5]
+ CRUSH rule 3 x 285 [5,7]
+ CRUSH rule 3 x 286 [2,8]
+ CRUSH rule 3 x 287 [2,5]
+ CRUSH rule 3 x 288 [8,2]
+ CRUSH rule 3 x 289 [5,6]
+ CRUSH rule 3 x 290 [2,5]
+ CRUSH rule 3 x 291 [2,5]
+ CRUSH rule 3 x 292 [8,2]
+ CRUSH rule 3 x 293 [6,2]
+ CRUSH rule 3 x 294 [7,5]
+ CRUSH rule 3 x 295 [5,6]
+ CRUSH rule 3 x 296 [5,2]
+ CRUSH rule 3 x 297 [6,2]
+ CRUSH rule 3 x 298 [2,5]
+ CRUSH rule 3 x 299 [2,8]
+ CRUSH rule 3 x 300 [8,5]
+ CRUSH rule 3 x 301 [2,7]
+ CRUSH rule 3 x 302 [5,2]
+ CRUSH rule 3 x 303 [7,5]
+ CRUSH rule 3 x 304 [2,6]
+ CRUSH rule 3 x 305 [5,6]
+ CRUSH rule 3 x 306 [2,8]
+ CRUSH rule 3 x 307 [2,7]
+ CRUSH rule 3 x 308 [2,8]
+ CRUSH rule 3 x 309 [7,5]
+ CRUSH rule 3 x 310 [5,2]
+ CRUSH rule 3 x 311 [5,8]
+ CRUSH rule 3 x 312 [2,6]
+ CRUSH rule 3 x 313 [5,2]
+ CRUSH rule 3 x 314 [5,2]
+ CRUSH rule 3 x 315 [2,5]
+ CRUSH rule 3 x 316 [6,5]
+ CRUSH rule 3 x 317 [2,7]
+ CRUSH rule 3 x 318 [8,2]
+ CRUSH rule 3 x 319 [5,2]
+ CRUSH rule 3 x 320 [5,8]
+ CRUSH rule 3 x 321 [2,5]
+ CRUSH rule 3 x 322 [2,6]
+ CRUSH rule 3 x 323 [5,7]
+ CRUSH rule 3 x 324 [7,2]
+ CRUSH rule 3 x 325 [5,6]
+ CRUSH rule 3 x 326 [5,2]
+ CRUSH rule 3 x 327 [2,8]
+ CRUSH rule 3 x 328 [7,5]
+ CRUSH rule 3 x 329 [5,7]
+ CRUSH rule 3 x 330 [5,7]
+ CRUSH rule 3 x 331 [2,7]
+ CRUSH rule 3 x 332 [2,5]
+ CRUSH rule 3 x 333 [6,5]
+ CRUSH rule 3 x 334 [8,5]
+ CRUSH rule 3 x 335 [7,2]
+ CRUSH rule 3 x 336 [5,6]
+ CRUSH rule 3 x 337 [7,2]
+ CRUSH rule 3 x 338 [5,8]
+ CRUSH rule 3 x 339 [7,5]
+ CRUSH rule 3 x 340 [2,6]
+ CRUSH rule 3 x 341 [5,2]
+ CRUSH rule 3 x 342 [2,8]
+ CRUSH rule 3 x 343 [6,5]
+ CRUSH rule 3 x 344 [6,2]
+ CRUSH rule 3 x 345 [5,7]
+ CRUSH rule 3 x 346 [8,2]
+ CRUSH rule 3 x 347 [5,2]
+ CRUSH rule 3 x 348 [8,2]
+ CRUSH rule 3 x 349 [2,7]
+ CRUSH rule 3 x 350 [8,5]
+ CRUSH rule 3 x 351 [5,8]
+ CRUSH rule 3 x 352 [2,8]
+ CRUSH rule 3 x 353 [6,5]
+ CRUSH rule 3 x 354 [2,5]
+ CRUSH rule 3 x 355 [5,8]
+ CRUSH rule 3 x 356 [5,2]
+ CRUSH rule 3 x 357 [6,2]
+ CRUSH rule 3 x 358 [2,8]
+ CRUSH rule 3 x 359 [6,2]
+ CRUSH rule 3 x 360 [5,2]
+ CRUSH rule 3 x 361 [8,5]
+ CRUSH rule 3 x 362 [5,2]
+ CRUSH rule 3 x 363 [5,2]
+ CRUSH rule 3 x 364 [2,5]
+ CRUSH rule 3 x 365 [6,5]
+ CRUSH rule 3 x 366 [7,2]
+ CRUSH rule 3 x 367 [5,2]
+ CRUSH rule 3 x 368 [7,5]
+ CRUSH rule 3 x 369 [5,7]
+ CRUSH rule 3 x 370 [8,2]
+ CRUSH rule 3 x 371 [2,5]
+ CRUSH rule 3 x 372 [5,2]
+ CRUSH rule 3 x 373 [2,6]
+ CRUSH rule 3 x 374 [5,8]
+ CRUSH rule 3 x 375 [6,5]
+ CRUSH rule 3 x 376 [7,2]
+ CRUSH rule 3 x 377 [2,5]
+ CRUSH rule 3 x 378 [2,6]
+ CRUSH rule 3 x 379 [8,5]
+ CRUSH rule 3 x 380 [2,5]
+ CRUSH rule 3 x 381 [2,5]
+ CRUSH rule 3 x 382 [2,5]
+ CRUSH rule 3 x 383 [5,7]
+ CRUSH rule 3 x 384 [7,2]
+ CRUSH rule 3 x 385 [7,5]
+ CRUSH rule 3 x 386 [2,5]
+ CRUSH rule 3 x 387 [2,5]
+ CRUSH rule 3 x 388 [5,2]
+ CRUSH rule 3 x 389 [2,5]
+ CRUSH rule 3 x 390 [5,8]
+ CRUSH rule 3 x 391 [5,6]
+ CRUSH rule 3 x 392 [2,7]
+ CRUSH rule 3 x 393 [5,2]
+ CRUSH rule 3 x 394 [5,8]
+ CRUSH rule 3 x 395 [5,2]
+ CRUSH rule 3 x 396 [5,2]
+ CRUSH rule 3 x 397 [2,5]
+ CRUSH rule 3 x 398 [2,5]
+ CRUSH rule 3 x 399 [8,5]
+ CRUSH rule 3 x 400 [8,2]
+ CRUSH rule 3 x 401 [2,5]
+ CRUSH rule 3 x 402 [7,5]
+ CRUSH rule 3 x 403 [2,5]
+ CRUSH rule 3 x 404 [5,2]
+ CRUSH rule 3 x 405 [6,5]
+ CRUSH rule 3 x 406 [2,6]
+ CRUSH rule 3 x 407 [2,7]
+ CRUSH rule 3 x 408 [5,2]
+ CRUSH rule 3 x 409 [7,5]
+ CRUSH rule 3 x 410 [8,5]
+ CRUSH rule 3 x 411 [2,7]
+ CRUSH rule 3 x 412 [2,5]
+ CRUSH rule 3 x 413 [5,2]
+ CRUSH rule 3 x 414 [5,2]
+ CRUSH rule 3 x 415 [2,6]
+ CRUSH rule 3 x 416 [2,8]
+ CRUSH rule 3 x 417 [8,2]
+ CRUSH rule 3 x 418 [7,2]
+ CRUSH rule 3 x 419 [8,5]
+ CRUSH rule 3 x 420 [2,5]
+ CRUSH rule 3 x 421 [8,5]
+ CRUSH rule 3 x 422 [6,5]
+ CRUSH rule 3 x 423 [2,5]
+ CRUSH rule 3 x 424 [8,5]
+ CRUSH rule 3 x 425 [2,5]
+ CRUSH rule 3 x 426 [6,2]
+ CRUSH rule 3 x 427 [2,8]
+ CRUSH rule 3 x 428 [5,8]
+ CRUSH rule 3 x 429 [5,8]
+ CRUSH rule 3 x 430 [5,7]
+ CRUSH rule 3 x 431 [5,2]
+ CRUSH rule 3 x 432 [7,2]
+ CRUSH rule 3 x 433 [6,5]
+ CRUSH rule 3 x 434 [5,2]
+ CRUSH rule 3 x 435 [2,5]
+ CRUSH rule 3 x 436 [5,2]
+ CRUSH rule 3 x 437 [7,5]
+ CRUSH rule 3 x 438 [2,5]
+ CRUSH rule 3 x 439 [2,5]
+ CRUSH rule 3 x 440 [2,6]
+ CRUSH rule 3 x 441 [5,8]
+ CRUSH rule 3 x 442 [2,5]
+ CRUSH rule 3 x 443 [6,2]
+ CRUSH rule 3 x 444 [7,2]
+ CRUSH rule 3 x 445 [6,5]
+ CRUSH rule 3 x 446 [5,2]
+ CRUSH rule 3 x 447 [2,5]
+ CRUSH rule 3 x 448 [7,2]
+ CRUSH rule 3 x 449 [7,5]
+ CRUSH rule 3 x 450 [5,2]
+ CRUSH rule 3 x 451 [6,5]
+ CRUSH rule 3 x 452 [8,5]
+ CRUSH rule 3 x 453 [6,5]
+ CRUSH rule 3 x 454 [6,5]
+ CRUSH rule 3 x 455 [2,8]
+ CRUSH rule 3 x 456 [6,2]
+ CRUSH rule 3 x 457 [7,2]
+ CRUSH rule 3 x 458 [2,8]
+ CRUSH rule 3 x 459 [2,6]
+ CRUSH rule 3 x 460 [6,5]
+ CRUSH rule 3 x 461 [6,5]
+ CRUSH rule 3 x 462 [8,2]
+ CRUSH rule 3 x 463 [6,2]
+ CRUSH rule 3 x 464 [7,5]
+ CRUSH rule 3 x 465 [7,2]
+ CRUSH rule 3 x 466 [5,6]
+ CRUSH rule 3 x 467 [6,5]
+ CRUSH rule 3 x 468 [7,2]
+ CRUSH rule 3 x 469 [7,2]
+ CRUSH rule 3 x 470 [5,2]
+ CRUSH rule 3 x 471 [2,6]
+ CRUSH rule 3 x 472 [5,2]
+ CRUSH rule 3 x 473 [2,5]
+ CRUSH rule 3 x 474 [6,2]
+ CRUSH rule 3 x 475 [6,2]
+ CRUSH rule 3 x 476 [5,7]
+ CRUSH rule 3 x 477 [5,6]
+ CRUSH rule 3 x 478 [6,2]
+ CRUSH rule 3 x 479 [2,5]
+ CRUSH rule 3 x 480 [2,6]
+ CRUSH rule 3 x 481 [2,5]
+ CRUSH rule 3 x 482 [5,8]
+ CRUSH rule 3 x 483 [2,6]
+ CRUSH rule 3 x 484 [2,8]
+ CRUSH rule 3 x 485 [5,8]
+ CRUSH rule 3 x 486 [5,2]
+ CRUSH rule 3 x 487 [5,2]
+ CRUSH rule 3 x 488 [5,7]
+ CRUSH rule 3 x 489 [2,8]
+ CRUSH rule 3 x 490 [6,5]
+ CRUSH rule 3 x 491 [2,6]
+ CRUSH rule 3 x 492 [6,5]
+ CRUSH rule 3 x 493 [2,8]
+ CRUSH rule 3 x 494 [2,6]
+ CRUSH rule 3 x 495 [5,2]
+ CRUSH rule 3 x 496 [7,5]
+ CRUSH rule 3 x 497 [5,7]
+ CRUSH rule 3 x 498 [2,5]
+ CRUSH rule 3 x 499 [8,5]
+ CRUSH rule 3 x 500 [5,6]
+ CRUSH rule 3 x 501 [2,7]
+ CRUSH rule 3 x 502 [7,2]
+ CRUSH rule 3 x 503 [2,5]
+ CRUSH rule 3 x 504 [5,8]
+ CRUSH rule 3 x 505 [2,7]
+ CRUSH rule 3 x 506 [5,2]
+ CRUSH rule 3 x 507 [6,2]
+ CRUSH rule 3 x 508 [2,5]
+ CRUSH rule 3 x 509 [7,5]
+ CRUSH rule 3 x 510 [6,2]
+ CRUSH rule 3 x 511 [5,6]
+ CRUSH rule 3 x 512 [7,2]
+ CRUSH rule 3 x 513 [7,2]
+ CRUSH rule 3 x 514 [5,7]
+ CRUSH rule 3 x 515 [8,5]
+ CRUSH rule 3 x 516 [5,2]
+ CRUSH rule 3 x 517 [7,2]
+ CRUSH rule 3 x 518 [5,6]
+ CRUSH rule 3 x 519 [7,5]
+ CRUSH rule 3 x 520 [2,8]
+ CRUSH rule 3 x 521 [8,2]
+ CRUSH rule 3 x 522 [6,2]
+ CRUSH rule 3 x 523 [5,2]
+ CRUSH rule 3 x 524 [2,5]
+ CRUSH rule 3 x 525 [2,5]
+ CRUSH rule 3 x 526 [2,5]
+ CRUSH rule 3 x 527 [2,5]
+ CRUSH rule 3 x 528 [5,2]
+ CRUSH rule 3 x 529 [5,6]
+ CRUSH rule 3 x 530 [6,5]
+ CRUSH rule 3 x 531 [6,2]
+ CRUSH rule 3 x 532 [6,5]
+ CRUSH rule 3 x 533 [5,8]
+ CRUSH rule 3 x 534 [7,5]
+ CRUSH rule 3 x 535 [8,2]
+ CRUSH rule 3 x 536 [6,2]
+ CRUSH rule 3 x 537 [5,8]
+ CRUSH rule 3 x 538 [6,5]
+ CRUSH rule 3 x 539 [8,5]
+ CRUSH rule 3 x 540 [2,7]
+ CRUSH rule 3 x 541 [2,5]
+ CRUSH rule 3 x 542 [5,2]
+ CRUSH rule 3 x 543 [6,2]
+ CRUSH rule 3 x 544 [5,7]
+ CRUSH rule 3 x 545 [5,7]
+ CRUSH rule 3 x 546 [6,2]
+ CRUSH rule 3 x 547 [8,2]
+ CRUSH rule 3 x 548 [5,2]
+ CRUSH rule 3 x 549 [5,7]
+ CRUSH rule 3 x 550 [2,5]
+ CRUSH rule 3 x 551 [7,5]
+ CRUSH rule 3 x 552 [5,7]
+ CRUSH rule 3 x 553 [5,2]
+ CRUSH rule 3 x 554 [2,6]
+ CRUSH rule 3 x 555 [5,2]
+ CRUSH rule 3 x 556 [5,6]
+ CRUSH rule 3 x 557 [7,5]
+ CRUSH rule 3 x 558 [5,2]
+ CRUSH rule 3 x 559 [5,2]
+ CRUSH rule 3 x 560 [8,5]
+ CRUSH rule 3 x 561 [6,5]
+ CRUSH rule 3 x 562 [5,2]
+ CRUSH rule 3 x 563 [2,7]
+ CRUSH rule 3 x 564 [5,2]
+ CRUSH rule 3 x 565 [5,8]
+ CRUSH rule 3 x 566 [5,6]
+ CRUSH rule 3 x 567 [5,7]
+ CRUSH rule 3 x 568 [7,5]
+ CRUSH rule 3 x 569 [5,2]
+ CRUSH rule 3 x 570 [2,5]
+ CRUSH rule 3 x 571 [5,6]
+ CRUSH rule 3 x 572 [5,2]
+ CRUSH rule 3 x 573 [5,2]
+ CRUSH rule 3 x 574 [2,5]
+ CRUSH rule 3 x 575 [8,2]
+ CRUSH rule 3 x 576 [5,7]
+ CRUSH rule 3 x 577 [8,2]
+ CRUSH rule 3 x 578 [6,2]
+ CRUSH rule 3 x 579 [5,2]
+ CRUSH rule 3 x 580 [5,2]
+ CRUSH rule 3 x 581 [7,2]
+ CRUSH rule 3 x 582 [2,8]
+ CRUSH rule 3 x 583 [6,2]
+ CRUSH rule 3 x 584 [8,2]
+ CRUSH rule 3 x 585 [7,2]
+ CRUSH rule 3 x 586 [2,8]
+ CRUSH rule 3 x 587 [2,5]
+ CRUSH rule 3 x 588 [5,8]
+ CRUSH rule 3 x 589 [7,2]
+ CRUSH rule 3 x 590 [6,2]
+ CRUSH rule 3 x 591 [5,2]
+ CRUSH rule 3 x 592 [2,5]
+ CRUSH rule 3 x 593 [2,8]
+ CRUSH rule 3 x 594 [2,6]
+ CRUSH rule 3 x 595 [7,2]
+ CRUSH rule 3 x 596 [5,2]
+ CRUSH rule 3 x 597 [5,2]
+ CRUSH rule 3 x 598 [5,2]
+ CRUSH rule 3 x 599 [5,2]
+ CRUSH rule 3 x 600 [7,2]
+ CRUSH rule 3 x 601 [2,6]
+ CRUSH rule 3 x 602 [5,8]
+ CRUSH rule 3 x 603 [5,2]
+ CRUSH rule 3 x 604 [7,5]
+ CRUSH rule 3 x 605 [5,2]
+ CRUSH rule 3 x 606 [2,7]
+ CRUSH rule 3 x 607 [2,5]
+ CRUSH rule 3 x 608 [5,2]
+ CRUSH rule 3 x 609 [5,2]
+ CRUSH rule 3 x 610 [5,8]
+ CRUSH rule 3 x 611 [2,8]
+ CRUSH rule 3 x 612 [2,8]
+ CRUSH rule 3 x 613 [7,2]
+ CRUSH rule 3 x 614 [7,2]
+ CRUSH rule 3 x 615 [6,2]
+ CRUSH rule 3 x 616 [2,7]
+ CRUSH rule 3 x 617 [6,2]
+ CRUSH rule 3 x 618 [7,5]
+ CRUSH rule 3 x 619 [5,2]
+ CRUSH rule 3 x 620 [5,2]
+ CRUSH rule 3 x 621 [5,6]
+ CRUSH rule 3 x 622 [2,5]
+ CRUSH rule 3 x 623 [2,8]
+ CRUSH rule 3 x 624 [5,2]
+ CRUSH rule 3 x 625 [2,5]
+ CRUSH rule 3 x 626 [7,2]
+ CRUSH rule 3 x 627 [2,6]
+ CRUSH rule 3 x 628 [8,2]
+ CRUSH rule 3 x 629 [2,6]
+ CRUSH rule 3 x 630 [2,6]
+ CRUSH rule 3 x 631 [2,6]
+ CRUSH rule 3 x 632 [7,2]
+ CRUSH rule 3 x 633 [8,5]
+ CRUSH rule 3 x 634 [2,5]
+ CRUSH rule 3 x 635 [5,6]
+ CRUSH rule 3 x 636 [2,5]
+ CRUSH rule 3 x 637 [5,2]
+ CRUSH rule 3 x 638 [6,2]
+ CRUSH rule 3 x 639 [5,2]
+ CRUSH rule 3 x 640 [5,2]
+ CRUSH rule 3 x 641 [7,2]
+ CRUSH rule 3 x 642 [2,8]
+ CRUSH rule 3 x 643 [5,2]
+ CRUSH rule 3 x 644 [8,2]
+ CRUSH rule 3 x 645 [5,7]
+ CRUSH rule 3 x 646 [8,2]
+ CRUSH rule 3 x 647 [7,2]
+ CRUSH rule 3 x 648 [2,8]
+ CRUSH rule 3 x 649 [5,7]
+ CRUSH rule 3 x 650 [7,5]
+ CRUSH rule 3 x 651 [5,6]
+ CRUSH rule 3 x 652 [5,6]
+ CRUSH rule 3 x 653 [8,5]
+ CRUSH rule 3 x 654 [7,5]
+ CRUSH rule 3 x 655 [2,5]
+ CRUSH rule 3 x 656 [5,7]
+ CRUSH rule 3 x 657 [6,2]
+ CRUSH rule 3 x 658 [5,8]
+ CRUSH rule 3 x 659 [5,7]
+ CRUSH rule 3 x 660 [7,5]
+ CRUSH rule 3 x 661 [2,7]
+ CRUSH rule 3 x 662 [5,2]
+ CRUSH rule 3 x 663 [2,5]
+ CRUSH rule 3 x 664 [2,5]
+ CRUSH rule 3 x 665 [5,6]
+ CRUSH rule 3 x 666 [2,7]
+ CRUSH rule 3 x 667 [2,5]
+ CRUSH rule 3 x 668 [5,7]
+ CRUSH rule 3 x 669 [6,5]
+ CRUSH rule 3 x 670 [5,2]
+ CRUSH rule 3 x 671 [2,8]
+ CRUSH rule 3 x 672 [5,2]
+ CRUSH rule 3 x 673 [5,2]
+ CRUSH rule 3 x 674 [5,2]
+ CRUSH rule 3 x 675 [2,8]
+ CRUSH rule 3 x 676 [2,5]
+ CRUSH rule 3 x 677 [5,2]
+ CRUSH rule 3 x 678 [2,5]
+ CRUSH rule 3 x 679 [6,2]
+ CRUSH rule 3 x 680 [2,5]
+ CRUSH rule 3 x 681 [5,6]
+ CRUSH rule 3 x 682 [2,5]
+ CRUSH rule 3 x 683 [2,5]
+ CRUSH rule 3 x 684 [7,2]
+ CRUSH rule 3 x 685 [7,2]
+ CRUSH rule 3 x 686 [2,5]
+ CRUSH rule 3 x 687 [5,7]
+ CRUSH rule 3 x 688 [5,6]
+ CRUSH rule 3 x 689 [6,5]
+ CRUSH rule 3 x 690 [8,2]
+ CRUSH rule 3 x 691 [5,2]
+ CRUSH rule 3 x 692 [7,2]
+ CRUSH rule 3 x 693 [6,5]
+ CRUSH rule 3 x 694 [6,5]
+ CRUSH rule 3 x 695 [2,6]
+ CRUSH rule 3 x 696 [2,5]
+ CRUSH rule 3 x 697 [6,2]
+ CRUSH rule 3 x 698 [6,2]
+ CRUSH rule 3 x 699 [2,8]
+ CRUSH rule 3 x 700 [2,5]
+ CRUSH rule 3 x 701 [5,2]
+ CRUSH rule 3 x 702 [5,2]
+ CRUSH rule 3 x 703 [8,5]
+ CRUSH rule 3 x 704 [2,5]
+ CRUSH rule 3 x 705 [8,2]
+ CRUSH rule 3 x 706 [2,5]
+ CRUSH rule 3 x 707 [7,5]
+ CRUSH rule 3 x 708 [5,7]
+ CRUSH rule 3 x 709 [6,5]
+ CRUSH rule 3 x 710 [8,5]
+ CRUSH rule 3 x 711 [2,5]
+ CRUSH rule 3 x 712 [2,5]
+ CRUSH rule 3 x 713 [6,5]
+ CRUSH rule 3 x 714 [5,2]
+ CRUSH rule 3 x 715 [2,5]
+ CRUSH rule 3 x 716 [5,6]
+ CRUSH rule 3 x 717 [8,2]
+ CRUSH rule 3 x 718 [5,7]
+ CRUSH rule 3 x 719 [2,6]
+ CRUSH rule 3 x 720 [6,2]
+ CRUSH rule 3 x 721 [5,7]
+ CRUSH rule 3 x 722 [5,7]
+ CRUSH rule 3 x 723 [5,2]
+ CRUSH rule 3 x 724 [2,7]
+ CRUSH rule 3 x 725 [2,5]
+ CRUSH rule 3 x 726 [5,7]
+ CRUSH rule 3 x 727 [5,7]
+ CRUSH rule 3 x 728 [2,6]
+ CRUSH rule 3 x 729 [5,6]
+ CRUSH rule 3 x 730 [5,8]
+ CRUSH rule 3 x 731 [5,2]
+ CRUSH rule 3 x 732 [2,5]
+ CRUSH rule 3 x 733 [5,6]
+ CRUSH rule 3 x 734 [6,5]
+ CRUSH rule 3 x 735 [5,6]
+ CRUSH rule 3 x 736 [5,8]
+ CRUSH rule 3 x 737 [2,8]
+ CRUSH rule 3 x 738 [5,2]
+ CRUSH rule 3 x 739 [2,7]
+ CRUSH rule 3 x 740 [2,7]
+ CRUSH rule 3 x 741 [7,2]
+ CRUSH rule 3 x 742 [8,2]
+ CRUSH rule 3 x 743 [7,2]
+ CRUSH rule 3 x 744 [5,6]
+ CRUSH rule 3 x 745 [5,2]
+ CRUSH rule 3 x 746 [5,2]
+ CRUSH rule 3 x 747 [6,2]
+ CRUSH rule 3 x 748 [2,6]
+ CRUSH rule 3 x 749 [5,7]
+ CRUSH rule 3 x 750 [2,7]
+ CRUSH rule 3 x 751 [2,7]
+ CRUSH rule 3 x 752 [8,2]
+ CRUSH rule 3 x 753 [7,5]
+ CRUSH rule 3 x 754 [8,5]
+ CRUSH rule 3 x 755 [2,6]
+ CRUSH rule 3 x 756 [5,8]
+ CRUSH rule 3 x 757 [8,2]
+ CRUSH rule 3 x 758 [6,2]
+ CRUSH rule 3 x 759 [8,5]
+ CRUSH rule 3 x 760 [2,5]
+ CRUSH rule 3 x 761 [5,2]
+ CRUSH rule 3 x 762 [2,8]
+ CRUSH rule 3 x 763 [8,5]
+ CRUSH rule 3 x 764 [2,7]
+ CRUSH rule 3 x 765 [6,5]
+ CRUSH rule 3 x 766 [8,5]
+ CRUSH rule 3 x 767 [2,8]
+ CRUSH rule 3 x 768 [8,5]
+ CRUSH rule 3 x 769 [6,2]
+ CRUSH rule 3 x 770 [6,2]
+ CRUSH rule 3 x 771 [7,2]
+ CRUSH rule 3 x 772 [8,5]
+ CRUSH rule 3 x 773 [5,2]
+ CRUSH rule 3 x 774 [5,7]
+ CRUSH rule 3 x 775 [6,5]
+ CRUSH rule 3 x 776 [7,2]
+ CRUSH rule 3 x 777 [5,2]
+ CRUSH rule 3 x 778 [2,6]
+ CRUSH rule 3 x 779 [2,6]
+ CRUSH rule 3 x 780 [2,5]
+ CRUSH rule 3 x 781 [6,5]
+ CRUSH rule 3 x 782 [5,2]
+ CRUSH rule 3 x 783 [7,2]
+ CRUSH rule 3 x 784 [2,5]
+ CRUSH rule 3 x 785 [6,2]
+ CRUSH rule 3 x 786 [7,5]
+ CRUSH rule 3 x 787 [2,8]
+ CRUSH rule 3 x 788 [6,2]
+ CRUSH rule 3 x 789 [2,5]
+ CRUSH rule 3 x 790 [8,5]
+ CRUSH rule 3 x 791 [5,6]
+ CRUSH rule 3 x 792 [5,6]
+ CRUSH rule 3 x 793 [6,2]
+ CRUSH rule 3 x 794 [2,8]
+ CRUSH rule 3 x 795 [2,5]
+ CRUSH rule 3 x 796 [5,7]
+ CRUSH rule 3 x 797 [2,5]
+ CRUSH rule 3 x 798 [6,2]
+ CRUSH rule 3 x 799 [5,2]
+ CRUSH rule 3 x 800 [5,2]
+ CRUSH rule 3 x 801 [5,7]
+ CRUSH rule 3 x 802 [2,6]
+ CRUSH rule 3 x 803 [2,5]
+ CRUSH rule 3 x 804 [6,2]
+ CRUSH rule 3 x 805 [5,8]
+ CRUSH rule 3 x 806 [2,5]
+ CRUSH rule 3 x 807 [5,7]
+ CRUSH rule 3 x 808 [5,7]
+ CRUSH rule 3 x 809 [2,5]
+ CRUSH rule 3 x 810 [5,7]
+ CRUSH rule 3 x 811 [8,5]
+ CRUSH rule 3 x 812 [8,5]
+ CRUSH rule 3 x 813 [6,5]
+ CRUSH rule 3 x 814 [5,8]
+ CRUSH rule 3 x 815 [5,2]
+ CRUSH rule 3 x 816 [2,6]
+ CRUSH rule 3 x 817 [5,6]
+ CRUSH rule 3 x 818 [5,2]
+ CRUSH rule 3 x 819 [5,2]
+ CRUSH rule 3 x 820 [5,7]
+ CRUSH rule 3 x 821 [5,8]
+ CRUSH rule 3 x 822 [2,5]
+ CRUSH rule 3 x 823 [5,7]
+ CRUSH rule 3 x 824 [5,7]
+ CRUSH rule 3 x 825 [2,8]
+ CRUSH rule 3 x 826 [7,2]
+ CRUSH rule 3 x 827 [2,6]
+ CRUSH rule 3 x 828 [2,5]
+ CRUSH rule 3 x 829 [5,6]
+ CRUSH rule 3 x 830 [2,5]
+ CRUSH rule 3 x 831 [2,6]
+ CRUSH rule 3 x 832 [5,8]
+ CRUSH rule 3 x 833 [2,6]
+ CRUSH rule 3 x 834 [5,2]
+ CRUSH rule 3 x 835 [8,5]
+ CRUSH rule 3 x 836 [5,8]
+ CRUSH rule 3 x 837 [6,5]
+ CRUSH rule 3 x 838 [6,2]
+ CRUSH rule 3 x 839 [5,2]
+ CRUSH rule 3 x 840 [7,5]
+ CRUSH rule 3 x 841 [5,8]
+ CRUSH rule 3 x 842 [2,5]
+ CRUSH rule 3 x 843 [6,5]
+ CRUSH rule 3 x 844 [5,8]
+ CRUSH rule 3 x 845 [5,6]
+ CRUSH rule 3 x 846 [5,2]
+ CRUSH rule 3 x 847 [2,8]
+ CRUSH rule 3 x 848 [2,6]
+ CRUSH rule 3 x 849 [5,8]
+ CRUSH rule 3 x 850 [2,5]
+ CRUSH rule 3 x 851 [6,5]
+ CRUSH rule 3 x 852 [7,5]
+ CRUSH rule 3 x 853 [6,2]
+ CRUSH rule 3 x 854 [7,2]
+ CRUSH rule 3 x 855 [5,7]
+ CRUSH rule 3 x 856 [6,5]
+ CRUSH rule 3 x 857 [8,5]
+ CRUSH rule 3 x 858 [6,5]
+ CRUSH rule 3 x 859 [6,2]
+ CRUSH rule 3 x 860 [5,2]
+ CRUSH rule 3 x 861 [8,5]
+ CRUSH rule 3 x 862 [6,2]
+ CRUSH rule 3 x 863 [8,2]
+ CRUSH rule 3 x 864 [5,6]
+ CRUSH rule 3 x 865 [8,2]
+ CRUSH rule 3 x 866 [5,6]
+ CRUSH rule 3 x 867 [6,5]
+ CRUSH rule 3 x 868 [6,5]
+ CRUSH rule 3 x 869 [8,5]
+ CRUSH rule 3 x 870 [2,5]
+ CRUSH rule 3 x 871 [5,2]
+ CRUSH rule 3 x 872 [5,2]
+ CRUSH rule 3 x 873 [5,6]
+ CRUSH rule 3 x 874 [2,6]
+ CRUSH rule 3 x 875 [2,6]
+ CRUSH rule 3 x 876 [5,8]
+ CRUSH rule 3 x 877 [6,5]
+ CRUSH rule 3 x 878 [5,2]
+ CRUSH rule 3 x 879 [7,5]
+ CRUSH rule 3 x 880 [5,2]
+ CRUSH rule 3 x 881 [5,8]
+ CRUSH rule 3 x 882 [5,2]
+ CRUSH rule 3 x 883 [2,5]
+ CRUSH rule 3 x 884 [6,2]
+ CRUSH rule 3 x 885 [5,2]
+ CRUSH rule 3 x 886 [5,7]
+ CRUSH rule 3 x 887 [7,5]
+ CRUSH rule 3 x 888 [6,2]
+ CRUSH rule 3 x 889 [2,8]
+ CRUSH rule 3 x 890 [7,2]
+ CRUSH rule 3 x 891 [2,7]
+ CRUSH rule 3 x 892 [6,2]
+ CRUSH rule 3 x 893 [2,5]
+ CRUSH rule 3 x 894 [7,5]
+ CRUSH rule 3 x 895 [5,2]
+ CRUSH rule 3 x 896 [2,6]
+ CRUSH rule 3 x 897 [5,2]
+ CRUSH rule 3 x 898 [2,5]
+ CRUSH rule 3 x 899 [2,6]
+ CRUSH rule 3 x 900 [5,2]
+ CRUSH rule 3 x 901 [5,2]
+ CRUSH rule 3 x 902 [8,5]
+ CRUSH rule 3 x 903 [5,6]
+ CRUSH rule 3 x 904 [5,7]
+ CRUSH rule 3 x 905 [6,2]
+ CRUSH rule 3 x 906 [2,7]
+ CRUSH rule 3 x 907 [7,2]
+ CRUSH rule 3 x 908 [5,6]
+ CRUSH rule 3 x 909 [2,5]
+ CRUSH rule 3 x 910 [6,5]
+ CRUSH rule 3 x 911 [5,7]
+ CRUSH rule 3 x 912 [2,8]
+ CRUSH rule 3 x 913 [7,2]
+ CRUSH rule 3 x 914 [6,5]
+ CRUSH rule 3 x 915 [8,2]
+ CRUSH rule 3 x 916 [5,2]
+ CRUSH rule 3 x 917 [2,5]
+ CRUSH rule 3 x 918 [8,2]
+ CRUSH rule 3 x 919 [6,2]
+ CRUSH rule 3 x 920 [7,5]
+ CRUSH rule 3 x 921 [2,5]
+ CRUSH rule 3 x 922 [6,5]
+ CRUSH rule 3 x 923 [5,6]
+ CRUSH rule 3 x 924 [5,2]
+ CRUSH rule 3 x 925 [5,6]
+ CRUSH rule 3 x 926 [5,2]
+ CRUSH rule 3 x 927 [2,6]
+ CRUSH rule 3 x 928 [8,2]
+ CRUSH rule 3 x 929 [5,2]
+ CRUSH rule 3 x 930 [2,5]
+ CRUSH rule 3 x 931 [5,2]
+ CRUSH rule 3 x 932 [5,2]
+ CRUSH rule 3 x 933 [8,5]
+ CRUSH rule 3 x 934 [5,8]
+ CRUSH rule 3 x 935 [6,5]
+ CRUSH rule 3 x 936 [2,7]
+ CRUSH rule 3 x 937 [5,8]
+ CRUSH rule 3 x 938 [6,5]
+ CRUSH rule 3 x 939 [2,8]
+ CRUSH rule 3 x 940 [8,5]
+ CRUSH rule 3 x 941 [5,2]
+ CRUSH rule 3 x 942 [2,6]
+ CRUSH rule 3 x 943 [8,2]
+ CRUSH rule 3 x 944 [5,8]
+ CRUSH rule 3 x 945 [7,2]
+ CRUSH rule 3 x 946 [2,8]
+ CRUSH rule 3 x 947 [5,2]
+ CRUSH rule 3 x 948 [7,5]
+ CRUSH rule 3 x 949 [6,2]
+ CRUSH rule 3 x 950 [5,7]
+ CRUSH rule 3 x 951 [5,6]
+ CRUSH rule 3 x 952 [2,7]
+ CRUSH rule 3 x 953 [2,5]
+ CRUSH rule 3 x 954 [5,2]
+ CRUSH rule 3 x 955 [8,2]
+ CRUSH rule 3 x 956 [2,7]
+ CRUSH rule 3 x 957 [7,2]
+ CRUSH rule 3 x 958 [8,5]
+ CRUSH rule 3 x 959 [5,2]
+ CRUSH rule 3 x 960 [5,6]
+ CRUSH rule 3 x 961 [5,2]
+ CRUSH rule 3 x 962 [7,5]
+ CRUSH rule 3 x 963 [2,5]
+ CRUSH rule 3 x 964 [5,2]
+ CRUSH rule 3 x 965 [7,5]
+ CRUSH rule 3 x 966 [5,6]
+ CRUSH rule 3 x 967 [8,5]
+ CRUSH rule 3 x 968 [7,2]
+ CRUSH rule 3 x 969 [8,2]
+ CRUSH rule 3 x 970 [2,8]
+ CRUSH rule 3 x 971 [2,8]
+ CRUSH rule 3 x 972 [2,7]
+ CRUSH rule 3 x 973 [2,8]
+ CRUSH rule 3 x 974 [5,2]
+ CRUSH rule 3 x 975 [5,8]
+ CRUSH rule 3 x 976 [5,7]
+ CRUSH rule 3 x 977 [8,5]
+ CRUSH rule 3 x 978 [7,2]
+ CRUSH rule 3 x 979 [7,2]
+ CRUSH rule 3 x 980 [6,2]
+ CRUSH rule 3 x 981 [7,5]
+ CRUSH rule 3 x 982 [5,2]
+ CRUSH rule 3 x 983 [5,6]
+ CRUSH rule 3 x 984 [2,8]
+ CRUSH rule 3 x 985 [2,5]
+ CRUSH rule 3 x 986 [8,5]
+ CRUSH rule 3 x 987 [2,5]
+ CRUSH rule 3 x 988 [2,5]
+ CRUSH rule 3 x 989 [2,8]
+ CRUSH rule 3 x 990 [2,6]
+ CRUSH rule 3 x 991 [2,5]
+ CRUSH rule 3 x 992 [7,2]
+ CRUSH rule 3 x 993 [2,6]
+ CRUSH rule 3 x 994 [5,6]
+ CRUSH rule 3 x 995 [7,2]
+ CRUSH rule 3 x 996 [6,5]
+ CRUSH rule 3 x 997 [6,5]
+ CRUSH rule 3 x 998 [8,2]
+ CRUSH rule 3 x 999 [2,7]
+ CRUSH rule 3 x 1000 [8,5]
+ CRUSH rule 3 x 1001 [2,5]
+ CRUSH rule 3 x 1002 [2,5]
+ CRUSH rule 3 x 1003 [2,7]
+ CRUSH rule 3 x 1004 [6,2]
+ CRUSH rule 3 x 1005 [6,2]
+ CRUSH rule 3 x 1006 [2,8]
+ CRUSH rule 3 x 1007 [2,5]
+ CRUSH rule 3 x 1008 [2,7]
+ CRUSH rule 3 x 1009 [6,5]
+ CRUSH rule 3 x 1010 [5,2]
+ CRUSH rule 3 x 1011 [5,2]
+ CRUSH rule 3 x 1012 [5,2]
+ CRUSH rule 3 x 1013 [5,2]
+ CRUSH rule 3 x 1014 [2,8]
+ CRUSH rule 3 x 1015 [6,5]
+ CRUSH rule 3 x 1016 [2,5]
+ CRUSH rule 3 x 1017 [6,2]
+ CRUSH rule 3 x 1018 [5,2]
+ CRUSH rule 3 x 1019 [5,8]
+ CRUSH rule 3 x 1020 [5,2]
+ CRUSH rule 3 x 1021 [5,2]
+ CRUSH rule 3 x 1022 [2,7]
+ CRUSH rule 3 x 1023 [5,2]
+ rule 3 (choose-set) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [2,5,7]
+ CRUSH rule 3 x 1 [2,8,5]
+ CRUSH rule 3 x 2 [2,5,8]
+ CRUSH rule 3 x 3 [8,2,5]
+ CRUSH rule 3 x 4 [5,2,8]
+ CRUSH rule 3 x 5 [7,2,5]
+ CRUSH rule 3 x 6 [2,6,5]
+ CRUSH rule 3 x 7 [5,6,2]
+ CRUSH rule 3 x 8 [5,7,2]
+ CRUSH rule 3 x 9 [2,5,8]
+ CRUSH rule 3 x 10 [2,8,5]
+ CRUSH rule 3 x 11 [2,6,5]
+ CRUSH rule 3 x 12 [2,5,7]
+ CRUSH rule 3 x 13 [5,8,2]
+ CRUSH rule 3 x 14 [7,2,5]
+ CRUSH rule 3 x 15 [7,2,5]
+ CRUSH rule 3 x 16 [5,7,2]
+ CRUSH rule 3 x 17 [5,2,7]
+ CRUSH rule 3 x 18 [2,5,6]
+ CRUSH rule 3 x 19 [7,5,2]
+ CRUSH rule 3 x 20 [2,5,7]
+ CRUSH rule 3 x 21 [5,6,2]
+ CRUSH rule 3 x 22 [8,5,2]
+ CRUSH rule 3 x 23 [5,7,2]
+ CRUSH rule 3 x 24 [2,6,5]
+ CRUSH rule 3 x 25 [5,8,2]
+ CRUSH rule 3 x 26 [2,7,5]
+ CRUSH rule 3 x 27 [5,2,7]
+ CRUSH rule 3 x 28 [6,2,5]
+ CRUSH rule 3 x 29 [8,5,2]
+ CRUSH rule 3 x 30 [5,6,2]
+ CRUSH rule 3 x 31 [8,2,5]
+ CRUSH rule 3 x 32 [5,7,2]
+ CRUSH rule 3 x 33 [2,7,5]
+ CRUSH rule 3 x 34 [2,5,7]
+ CRUSH rule 3 x 35 [2,6,5]
+ CRUSH rule 3 x 36 [5,6,2]
+ CRUSH rule 3 x 37 [2,5,7]
+ CRUSH rule 3 x 38 [5,6,2]
+ CRUSH rule 3 x 39 [5,7,2]
+ CRUSH rule 3 x 40 [7,2,5]
+ CRUSH rule 3 x 41 [2,7,5]
+ CRUSH rule 3 x 42 [5,7,2]
+ CRUSH rule 3 x 43 [2,5,8]
+ CRUSH rule 3 x 44 [2,8,5]
+ CRUSH rule 3 x 45 [8,2,5]
+ CRUSH rule 3 x 46 [2,5,8]
+ CRUSH rule 3 x 47 [5,2,7]
+ CRUSH rule 3 x 48 [5,6,2]
+ CRUSH rule 3 x 49 [5,6,2]
+ CRUSH rule 3 x 50 [5,2,7]
+ CRUSH rule 3 x 51 [5,6,2]
+ CRUSH rule 3 x 52 [8,2,5]
+ CRUSH rule 3 x 53 [5,6,2]
+ CRUSH rule 3 x 54 [7,5,2]
+ CRUSH rule 3 x 55 [8,2,5]
+ CRUSH rule 3 x 56 [6,5,2]
+ CRUSH rule 3 x 57 [5,8,2]
+ CRUSH rule 3 x 58 [2,8,5]
+ CRUSH rule 3 x 59 [5,2,7]
+ CRUSH rule 3 x 60 [5,2,8]
+ CRUSH rule 3 x 61 [5,8,2]
+ CRUSH rule 3 x 62 [7,2,5]
+ CRUSH rule 3 x 63 [5,6,2]
+ CRUSH rule 3 x 64 [5,2,8]
+ CRUSH rule 3 x 65 [7,5,2]
+ CRUSH rule 3 x 66 [5,6,2]
+ CRUSH rule 3 x 67 [5,2,6]
+ CRUSH rule 3 x 68 [2,5,8]
+ CRUSH rule 3 x 69 [5,2,7]
+ CRUSH rule 3 x 70 [7,2,5]
+ CRUSH rule 3 x 71 [2,7,5]
+ CRUSH rule 3 x 72 [6,2,5]
+ CRUSH rule 3 x 73 [2,7,5]
+ CRUSH rule 3 x 74 [2,8,5]
+ CRUSH rule 3 x 75 [5,2,7]
+ CRUSH rule 3 x 76 [5,2,6]
+ CRUSH rule 3 x 77 [7,2,5]
+ CRUSH rule 3 x 78 [2,5,7]
+ CRUSH rule 3 x 79 [5,2,7]
+ CRUSH rule 3 x 80 [2,5,6]
+ CRUSH rule 3 x 81 [2,5,8]
+ CRUSH rule 3 x 82 [7,2,5]
+ CRUSH rule 3 x 83 [2,6,5]
+ CRUSH rule 3 x 84 [7,2,5]
+ CRUSH rule 3 x 85 [5,7,2]
+ CRUSH rule 3 x 86 [2,6,5]
+ CRUSH rule 3 x 87 [2,6,5]
+ CRUSH rule 3 x 88 [2,8,5]
+ CRUSH rule 3 x 89 [5,2,8]
+ CRUSH rule 3 x 90 [6,5,2]
+ CRUSH rule 3 x 91 [5,6,2]
+ CRUSH rule 3 x 92 [2,6,5]
+ CRUSH rule 3 x 93 [7,5,2]
+ CRUSH rule 3 x 94 [2,5,7]
+ CRUSH rule 3 x 95 [7,5,2]
+ CRUSH rule 3 x 96 [5,8,2]
+ CRUSH rule 3 x 97 [8,5,2]
+ CRUSH rule 3 x 98 [2,7,5]
+ CRUSH rule 3 x 99 [2,8,5]
+ CRUSH rule 3 x 100 [2,6,5]
+ CRUSH rule 3 x 101 [5,8,2]
+ CRUSH rule 3 x 102 [5,2,7]
+ CRUSH rule 3 x 103 [5,7,2]
+ CRUSH rule 3 x 104 [7,5,2]
+ CRUSH rule 3 x 105 [2,5,8]
+ CRUSH rule 3 x 106 [2,6,5]
+ CRUSH rule 3 x 107 [5,2,7]
+ CRUSH rule 3 x 108 [7,2,5]
+ CRUSH rule 3 x 109 [2,5,8]
+ CRUSH rule 3 x 110 [5,2,6]
+ CRUSH rule 3 x 111 [2,5,7]
+ CRUSH rule 3 x 112 [2,6,5]
+ CRUSH rule 3 x 113 [6,2,5]
+ CRUSH rule 3 x 114 [7,5,2]
+ CRUSH rule 3 x 115 [8,2,5]
+ CRUSH rule 3 x 116 [2,7,5]
+ CRUSH rule 3 x 117 [7,5,2]
+ CRUSH rule 3 x 118 [2,5,6]
+ CRUSH rule 3 x 119 [5,7,2]
+ CRUSH rule 3 x 120 [2,5,6]
+ CRUSH rule 3 x 121 [2,8,5]
+ CRUSH rule 3 x 122 [8,5,2]
+ CRUSH rule 3 x 123 [2,5,7]
+ CRUSH rule 3 x 124 [5,2,8]
+ CRUSH rule 3 x 125 [2,7,5]
+ CRUSH rule 3 x 126 [5,2,7]
+ CRUSH rule 3 x 127 [5,6,2]
+ CRUSH rule 3 x 128 [5,8,2]
+ CRUSH rule 3 x 129 [2,5,7]
+ CRUSH rule 3 x 130 [5,8,2]
+ CRUSH rule 3 x 131 [2,5,6]
+ CRUSH rule 3 x 132 [2,5,8]
+ CRUSH rule 3 x 133 [5,6,2]
+ CRUSH rule 3 x 134 [2,7,5]
+ CRUSH rule 3 x 135 [5,7,2]
+ CRUSH rule 3 x 136 [2,5,8]
+ CRUSH rule 3 x 137 [7,5,2]
+ CRUSH rule 3 x 138 [8,5,2]
+ CRUSH rule 3 x 139 [5,2,8]
+ CRUSH rule 3 x 140 [2,8,5]
+ CRUSH rule 3 x 141 [6,2,5]
+ CRUSH rule 3 x 142 [5,2,8]
+ CRUSH rule 3 x 143 [5,7,2]
+ CRUSH rule 3 x 144 [8,2,5]
+ CRUSH rule 3 x 145 [8,5,2]
+ CRUSH rule 3 x 146 [2,8,5]
+ CRUSH rule 3 x 147 [2,6,5]
+ CRUSH rule 3 x 148 [5,2,8]
+ CRUSH rule 3 x 149 [5,6,2]
+ CRUSH rule 3 x 150 [2,8,5]
+ CRUSH rule 3 x 151 [5,8,2]
+ CRUSH rule 3 x 152 [8,5,2]
+ CRUSH rule 3 x 153 [8,5,2]
+ CRUSH rule 3 x 154 [5,2,6]
+ CRUSH rule 3 x 155 [5,6,2]
+ CRUSH rule 3 x 156 [5,2,7]
+ CRUSH rule 3 x 157 [5,2,7]
+ CRUSH rule 3 x 158 [2,6,5]
+ CRUSH rule 3 x 159 [7,2,5]
+ CRUSH rule 3 x 160 [2,8,5]
+ CRUSH rule 3 x 161 [2,5,6]
+ CRUSH rule 3 x 162 [2,8,5]
+ CRUSH rule 3 x 163 [5,8,2]
+ CRUSH rule 3 x 164 [7,2,5]
+ CRUSH rule 3 x 165 [7,2,5]
+ CRUSH rule 3 x 166 [2,5,7]
+ CRUSH rule 3 x 167 [2,6,5]
+ CRUSH rule 3 x 168 [5,2,7]
+ CRUSH rule 3 x 169 [2,7,5]
+ CRUSH rule 3 x 170 [2,5,8]
+ CRUSH rule 3 x 171 [7,5,2]
+ CRUSH rule 3 x 172 [2,8,5]
+ CRUSH rule 3 x 173 [8,5,2]
+ CRUSH rule 3 x 174 [2,5,8]
+ CRUSH rule 3 x 175 [6,2,5]
+ CRUSH rule 3 x 176 [5,2,7]
+ CRUSH rule 3 x 177 [5,2,8]
+ CRUSH rule 3 x 178 [5,2,6]
+ CRUSH rule 3 x 179 [5,2,7]
+ CRUSH rule 3 x 180 [5,7,2]
+ CRUSH rule 3 x 181 [6,2,5]
+ CRUSH rule 3 x 182 [8,5,2]
+ CRUSH rule 3 x 183 [7,5,2]
+ CRUSH rule 3 x 184 [5,6,2]
+ CRUSH rule 3 x 185 [6,2,5]
+ CRUSH rule 3 x 186 [2,5,8]
+ CRUSH rule 3 x 187 [2,6,5]
+ CRUSH rule 3 x 188 [2,6,5]
+ CRUSH rule 3 x 189 [2,6,5]
+ CRUSH rule 3 x 190 [5,2,8]
+ CRUSH rule 3 x 191 [7,2,5]
+ CRUSH rule 3 x 192 [5,2,6]
+ CRUSH rule 3 x 193 [5,2,6]
+ CRUSH rule 3 x 194 [2,5,7]
+ CRUSH rule 3 x 195 [6,5,2]
+ CRUSH rule 3 x 196 [6,2,5]
+ CRUSH rule 3 x 197 [6,5,2]
+ CRUSH rule 3 x 198 [2,5,6]
+ CRUSH rule 3 x 199 [2,5,7]
+ CRUSH rule 3 x 200 [2,5,8]
+ CRUSH rule 3 x 201 [7,2,5]
+ CRUSH rule 3 x 202 [6,5,2]
+ CRUSH rule 3 x 203 [5,6,2]
+ CRUSH rule 3 x 204 [2,5,8]
+ CRUSH rule 3 x 205 [2,7,5]
+ CRUSH rule 3 x 206 [2,8,5]
+ CRUSH rule 3 x 207 [5,2,7]
+ CRUSH rule 3 x 208 [7,2,5]
+ CRUSH rule 3 x 209 [2,8,5]
+ CRUSH rule 3 x 210 [2,5,6]
+ CRUSH rule 3 x 211 [5,2,7]
+ CRUSH rule 3 x 212 [7,5,2]
+ CRUSH rule 3 x 213 [8,5,2]
+ CRUSH rule 3 x 214 [5,7,2]
+ CRUSH rule 3 x 215 [8,2,5]
+ CRUSH rule 3 x 216 [5,2,8]
+ CRUSH rule 3 x 217 [2,7,5]
+ CRUSH rule 3 x 218 [2,7,5]
+ CRUSH rule 3 x 219 [5,6,2]
+ CRUSH rule 3 x 220 [5,8,2]
+ CRUSH rule 3 x 221 [5,7,2]
+ CRUSH rule 3 x 222 [6,5,2]
+ CRUSH rule 3 x 223 [2,5,6]
+ CRUSH rule 3 x 224 [2,5,8]
+ CRUSH rule 3 x 225 [8,2,5]
+ CRUSH rule 3 x 226 [7,2,5]
+ CRUSH rule 3 x 227 [5,2,7]
+ CRUSH rule 3 x 228 [5,6,2]
+ CRUSH rule 3 x 229 [5,7,2]
+ CRUSH rule 3 x 230 [5,6,2]
+ CRUSH rule 3 x 231 [5,7,2]
+ CRUSH rule 3 x 232 [2,8,5]
+ CRUSH rule 3 x 233 [5,6,2]
+ CRUSH rule 3 x 234 [2,5,6]
+ CRUSH rule 3 x 235 [5,6,2]
+ CRUSH rule 3 x 236 [5,2,8]
+ CRUSH rule 3 x 237 [5,8,2]
+ CRUSH rule 3 x 238 [5,2,6]
+ CRUSH rule 3 x 239 [8,5,2]
+ CRUSH rule 3 x 240 [5,8,2]
+ CRUSH rule 3 x 241 [5,2,7]
+ CRUSH rule 3 x 242 [5,2,6]
+ CRUSH rule 3 x 243 [5,8,2]
+ CRUSH rule 3 x 244 [5,6,2]
+ CRUSH rule 3 x 245 [7,2,5]
+ CRUSH rule 3 x 246 [2,5,7]
+ CRUSH rule 3 x 247 [6,2,5]
+ CRUSH rule 3 x 248 [8,2,5]
+ CRUSH rule 3 x 249 [2,5,8]
+ CRUSH rule 3 x 250 [2,5,8]
+ CRUSH rule 3 x 251 [2,5,7]
+ CRUSH rule 3 x 252 [5,7,2]
+ CRUSH rule 3 x 253 [5,2,7]
+ CRUSH rule 3 x 254 [5,2,8]
+ CRUSH rule 3 x 255 [2,7,5]
+ CRUSH rule 3 x 256 [5,6,2]
+ CRUSH rule 3 x 257 [2,6,5]
+ CRUSH rule 3 x 258 [5,2,8]
+ CRUSH rule 3 x 259 [5,6,2]
+ CRUSH rule 3 x 260 [5,8,2]
+ CRUSH rule 3 x 261 [8,5,2]
+ CRUSH rule 3 x 262 [5,6,2]
+ CRUSH rule 3 x 263 [6,2,5]
+ CRUSH rule 3 x 264 [5,6,2]
+ CRUSH rule 3 x 265 [8,5,2]
+ CRUSH rule 3 x 266 [8,2,5]
+ CRUSH rule 3 x 267 [2,5,6]
+ CRUSH rule 3 x 268 [2,6,5]
+ CRUSH rule 3 x 269 [2,6,5]
+ CRUSH rule 3 x 270 [5,2,8]
+ CRUSH rule 3 x 271 [7,5,2]
+ CRUSH rule 3 x 272 [2,6,5]
+ CRUSH rule 3 x 273 [5,2,7]
+ CRUSH rule 3 x 274 [6,5,2]
+ CRUSH rule 3 x 275 [5,8,2]
+ CRUSH rule 3 x 276 [7,2,5]
+ CRUSH rule 3 x 277 [6,5,2]
+ CRUSH rule 3 x 278 [6,2,5]
+ CRUSH rule 3 x 279 [8,5,2]
+ CRUSH rule 3 x 280 [2,7,5]
+ CRUSH rule 3 x 281 [8,2,5]
+ CRUSH rule 3 x 282 [5,2,8]
+ CRUSH rule 3 x 283 [8,2,5]
+ CRUSH rule 3 x 284 [6,5,2]
+ CRUSH rule 3 x 285 [5,7,2]
+ CRUSH rule 3 x 286 [2,8,5]
+ CRUSH rule 3 x 287 [2,5,8]
+ CRUSH rule 3 x 288 [8,2,5]
+ CRUSH rule 3 x 289 [5,6,2]
+ CRUSH rule 3 x 290 [2,5,6]
+ CRUSH rule 3 x 291 [2,5,8]
+ CRUSH rule 3 x 292 [8,2,5]
+ CRUSH rule 3 x 293 [6,2,5]
+ CRUSH rule 3 x 294 [7,5,2]
+ CRUSH rule 3 x 295 [5,6,2]
+ CRUSH rule 3 x 296 [5,2,6]
+ CRUSH rule 3 x 297 [6,2,5]
+ CRUSH rule 3 x 298 [2,5,6]
+ CRUSH rule 3 x 299 [2,8,5]
+ CRUSH rule 3 x 300 [8,5,2]
+ CRUSH rule 3 x 301 [2,7,5]
+ CRUSH rule 3 x 302 [5,2,6]
+ CRUSH rule 3 x 303 [7,5,2]
+ CRUSH rule 3 x 304 [2,6,5]
+ CRUSH rule 3 x 305 [5,6,2]
+ CRUSH rule 3 x 306 [2,8,5]
+ CRUSH rule 3 x 307 [2,7,5]
+ CRUSH rule 3 x 308 [2,8,5]
+ CRUSH rule 3 x 309 [7,5,2]
+ CRUSH rule 3 x 310 [5,2,6]
+ CRUSH rule 3 x 311 [5,8,2]
+ CRUSH rule 3 x 312 [2,6,5]
+ CRUSH rule 3 x 313 [5,2,7]
+ CRUSH rule 3 x 314 [5,2,6]
+ CRUSH rule 3 x 315 [2,5,7]
+ CRUSH rule 3 x 316 [6,5,2]
+ CRUSH rule 3 x 317 [2,7,5]
+ CRUSH rule 3 x 318 [8,2,5]
+ CRUSH rule 3 x 319 [5,2,6]
+ CRUSH rule 3 x 320 [5,8,2]
+ CRUSH rule 3 x 321 [2,5,8]
+ CRUSH rule 3 x 322 [2,6,5]
+ CRUSH rule 3 x 323 [5,7,2]
+ CRUSH rule 3 x 324 [7,2,5]
+ CRUSH rule 3 x 325 [5,6,2]
+ CRUSH rule 3 x 326 [5,2,7]
+ CRUSH rule 3 x 327 [2,8,5]
+ CRUSH rule 3 x 328 [7,5,2]
+ CRUSH rule 3 x 329 [5,7,2]
+ CRUSH rule 3 x 330 [5,7,2]
+ CRUSH rule 3 x 331 [2,7,5]
+ CRUSH rule 3 x 332 [2,5,8]
+ CRUSH rule 3 x 333 [6,5,2]
+ CRUSH rule 3 x 334 [8,5,2]
+ CRUSH rule 3 x 335 [7,2,5]
+ CRUSH rule 3 x 336 [5,6,2]
+ CRUSH rule 3 x 337 [7,2,5]
+ CRUSH rule 3 x 338 [5,8,2]
+ CRUSH rule 3 x 339 [7,5,2]
+ CRUSH rule 3 x 340 [2,6,5]
+ CRUSH rule 3 x 341 [5,2,7]
+ CRUSH rule 3 x 342 [2,8,5]
+ CRUSH rule 3 x 343 [6,5,2]
+ CRUSH rule 3 x 344 [6,2,5]
+ CRUSH rule 3 x 345 [5,7,2]
+ CRUSH rule 3 x 346 [8,2,5]
+ CRUSH rule 3 x 347 [5,2,8]
+ CRUSH rule 3 x 348 [8,2,5]
+ CRUSH rule 3 x 349 [2,7,5]
+ CRUSH rule 3 x 350 [8,5,2]
+ CRUSH rule 3 x 351 [5,8,2]
+ CRUSH rule 3 x 352 [2,8,5]
+ CRUSH rule 3 x 353 [6,5,2]
+ CRUSH rule 3 x 354 [2,5,6]
+ CRUSH rule 3 x 355 [5,8,2]
+ CRUSH rule 3 x 356 [5,2,8]
+ CRUSH rule 3 x 357 [6,2,5]
+ CRUSH rule 3 x 358 [2,8,5]
+ CRUSH rule 3 x 359 [6,2,5]
+ CRUSH rule 3 x 360 [5,2,8]
+ CRUSH rule 3 x 361 [8,5,2]
+ CRUSH rule 3 x 362 [5,2,8]
+ CRUSH rule 3 x 363 [5,2,8]
+ CRUSH rule 3 x 364 [2,5,7]
+ CRUSH rule 3 x 365 [6,5,2]
+ CRUSH rule 3 x 366 [7,2,5]
+ CRUSH rule 3 x 367 [5,2,7]
+ CRUSH rule 3 x 368 [7,5,2]
+ CRUSH rule 3 x 369 [5,7,2]
+ CRUSH rule 3 x 370 [8,2,5]
+ CRUSH rule 3 x 371 [2,5,8]
+ CRUSH rule 3 x 372 [5,2,8]
+ CRUSH rule 3 x 373 [2,6,5]
+ CRUSH rule 3 x 374 [5,8,2]
+ CRUSH rule 3 x 375 [6,5,2]
+ CRUSH rule 3 x 376 [7,2,5]
+ CRUSH rule 3 x 377 [2,5,7]
+ CRUSH rule 3 x 378 [2,6,5]
+ CRUSH rule 3 x 379 [8,5,2]
+ CRUSH rule 3 x 380 [2,5,8]
+ CRUSH rule 3 x 381 [2,5,7]
+ CRUSH rule 3 x 382 [2,5,7]
+ CRUSH rule 3 x 383 [5,7,2]
+ CRUSH rule 3 x 384 [7,2,5]
+ CRUSH rule 3 x 385 [7,5,2]
+ CRUSH rule 3 x 386 [2,5,6]
+ CRUSH rule 3 x 387 [2,5,8]
+ CRUSH rule 3 x 388 [5,2,7]
+ CRUSH rule 3 x 389 [2,5,8]
+ CRUSH rule 3 x 390 [5,8,2]
+ CRUSH rule 3 x 391 [5,6,2]
+ CRUSH rule 3 x 392 [2,7,5]
+ CRUSH rule 3 x 393 [5,2,6]
+ CRUSH rule 3 x 394 [5,8,2]
+ CRUSH rule 3 x 395 [5,2,8]
+ CRUSH rule 3 x 396 [5,2,6]
+ CRUSH rule 3 x 397 [2,5,7]
+ CRUSH rule 3 x 398 [2,5,6]
+ CRUSH rule 3 x 399 [8,5,2]
+ CRUSH rule 3 x 400 [8,2,5]
+ CRUSH rule 3 x 401 [2,5,6]
+ CRUSH rule 3 x 402 [7,5,2]
+ CRUSH rule 3 x 403 [2,5,7]
+ CRUSH rule 3 x 404 [5,2,6]
+ CRUSH rule 3 x 405 [6,5,2]
+ CRUSH rule 3 x 406 [2,6,5]
+ CRUSH rule 3 x 407 [2,7,5]
+ CRUSH rule 3 x 408 [5,2,7]
+ CRUSH rule 3 x 409 [7,5,2]
+ CRUSH rule 3 x 410 [8,5,2]
+ CRUSH rule 3 x 411 [2,7,5]
+ CRUSH rule 3 x 412 [2,5,7]
+ CRUSH rule 3 x 413 [5,2,8]
+ CRUSH rule 3 x 414 [5,2,8]
+ CRUSH rule 3 x 415 [2,6,5]
+ CRUSH rule 3 x 416 [2,8,5]
+ CRUSH rule 3 x 417 [8,2,5]
+ CRUSH rule 3 x 418 [7,2,5]
+ CRUSH rule 3 x 419 [8,5,2]
+ CRUSH rule 3 x 420 [2,5,7]
+ CRUSH rule 3 x 421 [8,5,2]
+ CRUSH rule 3 x 422 [6,5,2]
+ CRUSH rule 3 x 423 [2,5,7]
+ CRUSH rule 3 x 424 [8,5,2]
+ CRUSH rule 3 x 425 [2,5,8]
+ CRUSH rule 3 x 426 [6,2,5]
+ CRUSH rule 3 x 427 [2,8,5]
+ CRUSH rule 3 x 428 [5,8,2]
+ CRUSH rule 3 x 429 [5,8,2]
+ CRUSH rule 3 x 430 [5,7,2]
+ CRUSH rule 3 x 431 [5,2,7]
+ CRUSH rule 3 x 432 [7,2,5]
+ CRUSH rule 3 x 433 [6,5,2]
+ CRUSH rule 3 x 434 [5,2,7]
+ CRUSH rule 3 x 435 [2,5,6]
+ CRUSH rule 3 x 436 [5,2,7]
+ CRUSH rule 3 x 437 [7,5,2]
+ CRUSH rule 3 x 438 [2,5,8]
+ CRUSH rule 3 x 439 [2,5,8]
+ CRUSH rule 3 x 440 [2,6,5]
+ CRUSH rule 3 x 441 [5,8,2]
+ CRUSH rule 3 x 442 [2,5,6]
+ CRUSH rule 3 x 443 [6,2,5]
+ CRUSH rule 3 x 444 [7,2,5]
+ CRUSH rule 3 x 445 [6,5,2]
+ CRUSH rule 3 x 446 [5,2,8]
+ CRUSH rule 3 x 447 [2,5,6]
+ CRUSH rule 3 x 448 [7,2,5]
+ CRUSH rule 3 x 449 [7,5,2]
+ CRUSH rule 3 x 450 [5,2,6]
+ CRUSH rule 3 x 451 [6,5,2]
+ CRUSH rule 3 x 452 [8,5,2]
+ CRUSH rule 3 x 453 [6,5,2]
+ CRUSH rule 3 x 454 [6,5,2]
+ CRUSH rule 3 x 455 [2,8,5]
+ CRUSH rule 3 x 456 [6,2,5]
+ CRUSH rule 3 x 457 [7,2,5]
+ CRUSH rule 3 x 458 [2,8,5]
+ CRUSH rule 3 x 459 [2,6,5]
+ CRUSH rule 3 x 460 [6,5,2]
+ CRUSH rule 3 x 461 [6,5,2]
+ CRUSH rule 3 x 462 [8,2,5]
+ CRUSH rule 3 x 463 [6,2,5]
+ CRUSH rule 3 x 464 [7,5,2]
+ CRUSH rule 3 x 465 [7,2,5]
+ CRUSH rule 3 x 466 [5,6,2]
+ CRUSH rule 3 x 467 [6,5,2]
+ CRUSH rule 3 x 468 [7,2,5]
+ CRUSH rule 3 x 469 [7,2,5]
+ CRUSH rule 3 x 470 [5,2,8]
+ CRUSH rule 3 x 471 [2,6,5]
+ CRUSH rule 3 x 472 [5,2,8]
+ CRUSH rule 3 x 473 [2,5,7]
+ CRUSH rule 3 x 474 [6,2,5]
+ CRUSH rule 3 x 475 [6,2,5]
+ CRUSH rule 3 x 476 [5,7,2]
+ CRUSH rule 3 x 477 [5,6,2]
+ CRUSH rule 3 x 478 [6,2,5]
+ CRUSH rule 3 x 479 [2,5,6]
+ CRUSH rule 3 x 480 [2,6,5]
+ CRUSH rule 3 x 481 [2,5,7]
+ CRUSH rule 3 x 482 [5,8,2]
+ CRUSH rule 3 x 483 [2,6,5]
+ CRUSH rule 3 x 484 [2,8,5]
+ CRUSH rule 3 x 485 [5,8,2]
+ CRUSH rule 3 x 486 [5,2,8]
+ CRUSH rule 3 x 487 [5,2,8]
+ CRUSH rule 3 x 488 [5,7,2]
+ CRUSH rule 3 x 489 [2,8,5]
+ CRUSH rule 3 x 490 [6,5,2]
+ CRUSH rule 3 x 491 [2,6,5]
+ CRUSH rule 3 x 492 [6,5,2]
+ CRUSH rule 3 x 493 [2,8,5]
+ CRUSH rule 3 x 494 [2,6,5]
+ CRUSH rule 3 x 495 [5,2,6]
+ CRUSH rule 3 x 496 [7,5,2]
+ CRUSH rule 3 x 497 [5,7,2]
+ CRUSH rule 3 x 498 [2,5,6]
+ CRUSH rule 3 x 499 [8,5,2]
+ CRUSH rule 3 x 500 [5,6,2]
+ CRUSH rule 3 x 501 [2,7,5]
+ CRUSH rule 3 x 502 [7,2,5]
+ CRUSH rule 3 x 503 [2,5,7]
+ CRUSH rule 3 x 504 [5,8,2]
+ CRUSH rule 3 x 505 [2,7,5]
+ CRUSH rule 3 x 506 [5,2,7]
+ CRUSH rule 3 x 507 [6,2,5]
+ CRUSH rule 3 x 508 [2,5,7]
+ CRUSH rule 3 x 509 [7,5,2]
+ CRUSH rule 3 x 510 [6,2,5]
+ CRUSH rule 3 x 511 [5,6,2]
+ CRUSH rule 3 x 512 [7,2,5]
+ CRUSH rule 3 x 513 [7,2,5]
+ CRUSH rule 3 x 514 [5,7,2]
+ CRUSH rule 3 x 515 [8,5,2]
+ CRUSH rule 3 x 516 [5,2,7]
+ CRUSH rule 3 x 517 [7,2,5]
+ CRUSH rule 3 x 518 [5,6,2]
+ CRUSH rule 3 x 519 [7,5,2]
+ CRUSH rule 3 x 520 [2,8,5]
+ CRUSH rule 3 x 521 [8,2,5]
+ CRUSH rule 3 x 522 [6,2,5]
+ CRUSH rule 3 x 523 [5,2,7]
+ CRUSH rule 3 x 524 [2,5,7]
+ CRUSH rule 3 x 525 [2,5,8]
+ CRUSH rule 3 x 526 [2,5,8]
+ CRUSH rule 3 x 527 [2,5,6]
+ CRUSH rule 3 x 528 [5,2,7]
+ CRUSH rule 3 x 529 [5,6,2]
+ CRUSH rule 3 x 530 [6,5,2]
+ CRUSH rule 3 x 531 [6,2,5]
+ CRUSH rule 3 x 532 [6,5,2]
+ CRUSH rule 3 x 533 [5,8,2]
+ CRUSH rule 3 x 534 [7,5,2]
+ CRUSH rule 3 x 535 [8,2,5]
+ CRUSH rule 3 x 536 [6,2,5]
+ CRUSH rule 3 x 537 [5,8,2]
+ CRUSH rule 3 x 538 [6,5,2]
+ CRUSH rule 3 x 539 [8,5,2]
+ CRUSH rule 3 x 540 [2,7,5]
+ CRUSH rule 3 x 541 [2,5,7]
+ CRUSH rule 3 x 542 [5,2,8]
+ CRUSH rule 3 x 543 [6,2,5]
+ CRUSH rule 3 x 544 [5,7,2]
+ CRUSH rule 3 x 545 [5,7,2]
+ CRUSH rule 3 x 546 [6,2,5]
+ CRUSH rule 3 x 547 [8,2,5]
+ CRUSH rule 3 x 548 [5,2,8]
+ CRUSH rule 3 x 549 [5,7,2]
+ CRUSH rule 3 x 550 [2,5,6]
+ CRUSH rule 3 x 551 [7,5,2]
+ CRUSH rule 3 x 552 [5,7,2]
+ CRUSH rule 3 x 553 [5,2,8]
+ CRUSH rule 3 x 554 [2,6,5]
+ CRUSH rule 3 x 555 [5,2,8]
+ CRUSH rule 3 x 556 [5,6,2]
+ CRUSH rule 3 x 557 [7,5,2]
+ CRUSH rule 3 x 558 [5,2,6]
+ CRUSH rule 3 x 559 [5,2,8]
+ CRUSH rule 3 x 560 [8,5,2]
+ CRUSH rule 3 x 561 [6,5,2]
+ CRUSH rule 3 x 562 [5,2,8]
+ CRUSH rule 3 x 563 [2,7,5]
+ CRUSH rule 3 x 564 [5,2,7]
+ CRUSH rule 3 x 565 [5,8,2]
+ CRUSH rule 3 x 566 [5,6,2]
+ CRUSH rule 3 x 567 [5,7,2]
+ CRUSH rule 3 x 568 [7,5,2]
+ CRUSH rule 3 x 569 [5,2,6]
+ CRUSH rule 3 x 570 [2,5,7]
+ CRUSH rule 3 x 571 [5,6,2]
+ CRUSH rule 3 x 572 [5,2,7]
+ CRUSH rule 3 x 573 [5,2,7]
+ CRUSH rule 3 x 574 [2,5,6]
+ CRUSH rule 3 x 575 [8,2,5]
+ CRUSH rule 3 x 576 [5,7,2]
+ CRUSH rule 3 x 577 [8,2,5]
+ CRUSH rule 3 x 578 [6,2,5]
+ CRUSH rule 3 x 579 [5,2,6]
+ CRUSH rule 3 x 580 [5,2,8]
+ CRUSH rule 3 x 581 [7,2,5]
+ CRUSH rule 3 x 582 [2,8,5]
+ CRUSH rule 3 x 583 [6,2,5]
+ CRUSH rule 3 x 584 [8,2,5]
+ CRUSH rule 3 x 585 [7,2,5]
+ CRUSH rule 3 x 586 [2,8,5]
+ CRUSH rule 3 x 587 [2,5,6]
+ CRUSH rule 3 x 588 [5,8,2]
+ CRUSH rule 3 x 589 [7,2,5]
+ CRUSH rule 3 x 590 [6,2,5]
+ CRUSH rule 3 x 591 [5,2,7]
+ CRUSH rule 3 x 592 [2,5,6]
+ CRUSH rule 3 x 593 [2,8,5]
+ CRUSH rule 3 x 594 [2,6,5]
+ CRUSH rule 3 x 595 [7,2,5]
+ CRUSH rule 3 x 596 [5,2,6]
+ CRUSH rule 3 x 597 [5,2,7]
+ CRUSH rule 3 x 598 [5,2,7]
+ CRUSH rule 3 x 599 [5,2,6]
+ CRUSH rule 3 x 600 [7,2,5]
+ CRUSH rule 3 x 601 [2,6,5]
+ CRUSH rule 3 x 602 [5,8,2]
+ CRUSH rule 3 x 603 [5,2,8]
+ CRUSH rule 3 x 604 [7,5,2]
+ CRUSH rule 3 x 605 [5,2,6]
+ CRUSH rule 3 x 606 [2,7,5]
+ CRUSH rule 3 x 607 [2,5,8]
+ CRUSH rule 3 x 608 [5,2,8]
+ CRUSH rule 3 x 609 [5,2,6]
+ CRUSH rule 3 x 610 [5,8,2]
+ CRUSH rule 3 x 611 [2,8,5]
+ CRUSH rule 3 x 612 [2,8,5]
+ CRUSH rule 3 x 613 [7,2,5]
+ CRUSH rule 3 x 614 [7,2,5]
+ CRUSH rule 3 x 615 [6,2,5]
+ CRUSH rule 3 x 616 [2,7,5]
+ CRUSH rule 3 x 617 [6,2,5]
+ CRUSH rule 3 x 618 [7,5,2]
+ CRUSH rule 3 x 619 [5,2,6]
+ CRUSH rule 3 x 620 [5,2,8]
+ CRUSH rule 3 x 621 [5,6,2]
+ CRUSH rule 3 x 622 [2,5,8]
+ CRUSH rule 3 x 623 [2,8,5]
+ CRUSH rule 3 x 624 [5,2,8]
+ CRUSH rule 3 x 625 [2,5,8]
+ CRUSH rule 3 x 626 [7,2,5]
+ CRUSH rule 3 x 627 [2,6,5]
+ CRUSH rule 3 x 628 [8,2,5]
+ CRUSH rule 3 x 629 [2,6,5]
+ CRUSH rule 3 x 630 [2,6,5]
+ CRUSH rule 3 x 631 [2,6,5]
+ CRUSH rule 3 x 632 [7,2,5]
+ CRUSH rule 3 x 633 [8,5,2]
+ CRUSH rule 3 x 634 [2,5,6]
+ CRUSH rule 3 x 635 [5,6,2]
+ CRUSH rule 3 x 636 [2,5,7]
+ CRUSH rule 3 x 637 [5,2,8]
+ CRUSH rule 3 x 638 [6,2,5]
+ CRUSH rule 3 x 639 [5,2,6]
+ CRUSH rule 3 x 640 [5,2,7]
+ CRUSH rule 3 x 641 [7,2,5]
+ CRUSH rule 3 x 642 [2,8,5]
+ CRUSH rule 3 x 643 [5,2,8]
+ CRUSH rule 3 x 644 [8,2,5]
+ CRUSH rule 3 x 645 [5,7,2]
+ CRUSH rule 3 x 646 [8,2,5]
+ CRUSH rule 3 x 647 [7,2,5]
+ CRUSH rule 3 x 648 [2,8,5]
+ CRUSH rule 3 x 649 [5,7,2]
+ CRUSH rule 3 x 650 [7,5,2]
+ CRUSH rule 3 x 651 [5,6,2]
+ CRUSH rule 3 x 652 [5,6,2]
+ CRUSH rule 3 x 653 [8,5,2]
+ CRUSH rule 3 x 654 [7,5,2]
+ CRUSH rule 3 x 655 [2,5,6]
+ CRUSH rule 3 x 656 [5,7,2]
+ CRUSH rule 3 x 657 [6,2,5]
+ CRUSH rule 3 x 658 [5,8,2]
+ CRUSH rule 3 x 659 [5,7,2]
+ CRUSH rule 3 x 660 [7,5,2]
+ CRUSH rule 3 x 661 [2,7,5]
+ CRUSH rule 3 x 662 [5,2,8]
+ CRUSH rule 3 x 663 [2,5,7]
+ CRUSH rule 3 x 664 [2,5,6]
+ CRUSH rule 3 x 665 [5,6,2]
+ CRUSH rule 3 x 666 [2,7,5]
+ CRUSH rule 3 x 667 [2,5,8]
+ CRUSH rule 3 x 668 [5,7,2]
+ CRUSH rule 3 x 669 [6,5,2]
+ CRUSH rule 3 x 670 [5,2,6]
+ CRUSH rule 3 x 671 [2,8,5]
+ CRUSH rule 3 x 672 [5,2,8]
+ CRUSH rule 3 x 673 [5,2,7]
+ CRUSH rule 3 x 674 [5,2,7]
+ CRUSH rule 3 x 675 [2,8,5]
+ CRUSH rule 3 x 676 [2,5,7]
+ CRUSH rule 3 x 677 [5,2,6]
+ CRUSH rule 3 x 678 [2,5,8]
+ CRUSH rule 3 x 679 [6,2,5]
+ CRUSH rule 3 x 680 [2,5,8]
+ CRUSH rule 3 x 681 [5,6,2]
+ CRUSH rule 3 x 682 [2,5,8]
+ CRUSH rule 3 x 683 [2,5,8]
+ CRUSH rule 3 x 684 [7,2,5]
+ CRUSH rule 3 x 685 [7,2,5]
+ CRUSH rule 3 x 686 [2,5,8]
+ CRUSH rule 3 x 687 [5,7,2]
+ CRUSH rule 3 x 688 [5,6,2]
+ CRUSH rule 3 x 689 [6,5,2]
+ CRUSH rule 3 x 690 [8,2,5]
+ CRUSH rule 3 x 691 [5,2,6]
+ CRUSH rule 3 x 692 [7,2,5]
+ CRUSH rule 3 x 693 [6,5,2]
+ CRUSH rule 3 x 694 [6,5,2]
+ CRUSH rule 3 x 695 [2,6,5]
+ CRUSH rule 3 x 696 [2,5,8]
+ CRUSH rule 3 x 697 [6,2,5]
+ CRUSH rule 3 x 698 [6,2,5]
+ CRUSH rule 3 x 699 [2,8,5]
+ CRUSH rule 3 x 700 [2,5,6]
+ CRUSH rule 3 x 701 [5,2,6]
+ CRUSH rule 3 x 702 [5,2,8]
+ CRUSH rule 3 x 703 [8,5,2]
+ CRUSH rule 3 x 704 [2,5,6]
+ CRUSH rule 3 x 705 [8,2,5]
+ CRUSH rule 3 x 706 [2,5,8]
+ CRUSH rule 3 x 707 [7,5,2]
+ CRUSH rule 3 x 708 [5,7,2]
+ CRUSH rule 3 x 709 [6,5,2]
+ CRUSH rule 3 x 710 [8,5,2]
+ CRUSH rule 3 x 711 [2,5,7]
+ CRUSH rule 3 x 712 [2,5,6]
+ CRUSH rule 3 x 713 [6,5,2]
+ CRUSH rule 3 x 714 [5,2,6]
+ CRUSH rule 3 x 715 [2,5,6]
+ CRUSH rule 3 x 716 [5,6,2]
+ CRUSH rule 3 x 717 [8,2,5]
+ CRUSH rule 3 x 718 [5,7,2]
+ CRUSH rule 3 x 719 [2,6,5]
+ CRUSH rule 3 x 720 [6,2,5]
+ CRUSH rule 3 x 721 [5,7,2]
+ CRUSH rule 3 x 722 [5,7,2]
+ CRUSH rule 3 x 723 [5,2,7]
+ CRUSH rule 3 x 724 [2,7,5]
+ CRUSH rule 3 x 725 [2,5,6]
+ CRUSH rule 3 x 726 [5,7,2]
+ CRUSH rule 3 x 727 [5,7,2]
+ CRUSH rule 3 x 728 [2,6,5]
+ CRUSH rule 3 x 729 [5,6,2]
+ CRUSH rule 3 x 730 [5,8,2]
+ CRUSH rule 3 x 731 [5,2,6]
+ CRUSH rule 3 x 732 [2,5,8]
+ CRUSH rule 3 x 733 [5,6,2]
+ CRUSH rule 3 x 734 [6,5,2]
+ CRUSH rule 3 x 735 [5,6,2]
+ CRUSH rule 3 x 736 [5,8,2]
+ CRUSH rule 3 x 737 [2,8,5]
+ CRUSH rule 3 x 738 [5,2,6]
+ CRUSH rule 3 x 739 [2,7,5]
+ CRUSH rule 3 x 740 [2,7,5]
+ CRUSH rule 3 x 741 [7,2,5]
+ CRUSH rule 3 x 742 [8,2,5]
+ CRUSH rule 3 x 743 [7,2,5]
+ CRUSH rule 3 x 744 [5,6,2]
+ CRUSH rule 3 x 745 [5,2,8]
+ CRUSH rule 3 x 746 [5,2,7]
+ CRUSH rule 3 x 747 [6,2,5]
+ CRUSH rule 3 x 748 [2,6,5]
+ CRUSH rule 3 x 749 [5,7,2]
+ CRUSH rule 3 x 750 [2,7,5]
+ CRUSH rule 3 x 751 [2,7,5]
+ CRUSH rule 3 x 752 [8,2,5]
+ CRUSH rule 3 x 753 [7,5,2]
+ CRUSH rule 3 x 754 [8,5,2]
+ CRUSH rule 3 x 755 [2,6,5]
+ CRUSH rule 3 x 756 [5,8,2]
+ CRUSH rule 3 x 757 [8,2,5]
+ CRUSH rule 3 x 758 [6,2,5]
+ CRUSH rule 3 x 759 [8,5,2]
+ CRUSH rule 3 x 760 [2,5,7]
+ CRUSH rule 3 x 761 [5,2,6]
+ CRUSH rule 3 x 762 [2,8,5]
+ CRUSH rule 3 x 763 [8,5,2]
+ CRUSH rule 3 x 764 [2,7,5]
+ CRUSH rule 3 x 765 [6,5,2]
+ CRUSH rule 3 x 766 [8,5,2]
+ CRUSH rule 3 x 767 [2,8,5]
+ CRUSH rule 3 x 768 [8,5,2]
+ CRUSH rule 3 x 769 [6,2,5]
+ CRUSH rule 3 x 770 [6,2,5]
+ CRUSH rule 3 x 771 [7,2,5]
+ CRUSH rule 3 x 772 [8,5,2]
+ CRUSH rule 3 x 773 [5,2,6]
+ CRUSH rule 3 x 774 [5,7,2]
+ CRUSH rule 3 x 775 [6,5,2]
+ CRUSH rule 3 x 776 [7,2,5]
+ CRUSH rule 3 x 777 [5,2,6]
+ CRUSH rule 3 x 778 [2,6,5]
+ CRUSH rule 3 x 779 [2,6,5]
+ CRUSH rule 3 x 780 [2,5,8]
+ CRUSH rule 3 x 781 [6,5,2]
+ CRUSH rule 3 x 782 [5,2,8]
+ CRUSH rule 3 x 783 [7,2,5]
+ CRUSH rule 3 x 784 [2,5,8]
+ CRUSH rule 3 x 785 [6,2,5]
+ CRUSH rule 3 x 786 [7,5,2]
+ CRUSH rule 3 x 787 [2,8,5]
+ CRUSH rule 3 x 788 [6,2,5]
+ CRUSH rule 3 x 789 [2,5,8]
+ CRUSH rule 3 x 790 [8,5,2]
+ CRUSH rule 3 x 791 [5,6,2]
+ CRUSH rule 3 x 792 [5,6,2]
+ CRUSH rule 3 x 793 [6,2,5]
+ CRUSH rule 3 x 794 [2,8,5]
+ CRUSH rule 3 x 795 [2,5,6]
+ CRUSH rule 3 x 796 [5,7,2]
+ CRUSH rule 3 x 797 [2,5,6]
+ CRUSH rule 3 x 798 [6,2,5]
+ CRUSH rule 3 x 799 [5,2,7]
+ CRUSH rule 3 x 800 [5,2,8]
+ CRUSH rule 3 x 801 [5,7,2]
+ CRUSH rule 3 x 802 [2,6,5]
+ CRUSH rule 3 x 803 [2,5,7]
+ CRUSH rule 3 x 804 [6,2,5]
+ CRUSH rule 3 x 805 [5,8,2]
+ CRUSH rule 3 x 806 [2,5,6]
+ CRUSH rule 3 x 807 [5,7,2]
+ CRUSH rule 3 x 808 [5,7,2]
+ CRUSH rule 3 x 809 [2,5,6]
+ CRUSH rule 3 x 810 [5,7,2]
+ CRUSH rule 3 x 811 [8,5,2]
+ CRUSH rule 3 x 812 [8,5,2]
+ CRUSH rule 3 x 813 [6,5,2]
+ CRUSH rule 3 x 814 [5,8,2]
+ CRUSH rule 3 x 815 [5,2,6]
+ CRUSH rule 3 x 816 [2,6,5]
+ CRUSH rule 3 x 817 [5,6,2]
+ CRUSH rule 3 x 818 [5,2,7]
+ CRUSH rule 3 x 819 [5,2,7]
+ CRUSH rule 3 x 820 [5,7,2]
+ CRUSH rule 3 x 821 [5,8,2]
+ CRUSH rule 3 x 822 [2,5,7]
+ CRUSH rule 3 x 823 [5,7,2]
+ CRUSH rule 3 x 824 [5,7,2]
+ CRUSH rule 3 x 825 [2,8,5]
+ CRUSH rule 3 x 826 [7,2,5]
+ CRUSH rule 3 x 827 [2,6,5]
+ CRUSH rule 3 x 828 [2,5,8]
+ CRUSH rule 3 x 829 [5,6,2]
+ CRUSH rule 3 x 830 [2,5,7]
+ CRUSH rule 3 x 831 [2,6,5]
+ CRUSH rule 3 x 832 [5,8,2]
+ CRUSH rule 3 x 833 [2,6,5]
+ CRUSH rule 3 x 834 [5,2,8]
+ CRUSH rule 3 x 835 [8,5,2]
+ CRUSH rule 3 x 836 [5,8,2]
+ CRUSH rule 3 x 837 [6,5,2]
+ CRUSH rule 3 x 838 [6,2,5]
+ CRUSH rule 3 x 839 [5,2,8]
+ CRUSH rule 3 x 840 [7,5,2]
+ CRUSH rule 3 x 841 [5,8,2]
+ CRUSH rule 3 x 842 [2,5,8]
+ CRUSH rule 3 x 843 [6,5,2]
+ CRUSH rule 3 x 844 [5,8,2]
+ CRUSH rule 3 x 845 [5,6,2]
+ CRUSH rule 3 x 846 [5,2,6]
+ CRUSH rule 3 x 847 [2,8,5]
+ CRUSH rule 3 x 848 [2,6,5]
+ CRUSH rule 3 x 849 [5,8,2]
+ CRUSH rule 3 x 850 [2,5,7]
+ CRUSH rule 3 x 851 [6,5,2]
+ CRUSH rule 3 x 852 [7,5,2]
+ CRUSH rule 3 x 853 [6,2,5]
+ CRUSH rule 3 x 854 [7,2,5]
+ CRUSH rule 3 x 855 [5,7,2]
+ CRUSH rule 3 x 856 [6,5,2]
+ CRUSH rule 3 x 857 [8,5,2]
+ CRUSH rule 3 x 858 [6,5,2]
+ CRUSH rule 3 x 859 [6,2,5]
+ CRUSH rule 3 x 860 [5,2,8]
+ CRUSH rule 3 x 861 [8,5,2]
+ CRUSH rule 3 x 862 [6,2,5]
+ CRUSH rule 3 x 863 [8,2,5]
+ CRUSH rule 3 x 864 [5,6,2]
+ CRUSH rule 3 x 865 [8,2,5]
+ CRUSH rule 3 x 866 [5,6,2]
+ CRUSH rule 3 x 867 [6,5,2]
+ CRUSH rule 3 x 868 [6,5,2]
+ CRUSH rule 3 x 869 [8,5,2]
+ CRUSH rule 3 x 870 [2,5,7]
+ CRUSH rule 3 x 871 [5,2,6]
+ CRUSH rule 3 x 872 [5,2,6]
+ CRUSH rule 3 x 873 [5,6,2]
+ CRUSH rule 3 x 874 [2,6,5]
+ CRUSH rule 3 x 875 [2,6,5]
+ CRUSH rule 3 x 876 [5,8,2]
+ CRUSH rule 3 x 877 [6,5,2]
+ CRUSH rule 3 x 878 [5,2,7]
+ CRUSH rule 3 x 879 [7,5,2]
+ CRUSH rule 3 x 880 [5,2,7]
+ CRUSH rule 3 x 881 [5,8,2]
+ CRUSH rule 3 x 882 [5,2,6]
+ CRUSH rule 3 x 883 [2,5,6]
+ CRUSH rule 3 x 884 [6,2,5]
+ CRUSH rule 3 x 885 [5,2,6]
+ CRUSH rule 3 x 886 [5,7,2]
+ CRUSH rule 3 x 887 [7,5,2]
+ CRUSH rule 3 x 888 [6,2,5]
+ CRUSH rule 3 x 889 [2,8,5]
+ CRUSH rule 3 x 890 [7,2,5]
+ CRUSH rule 3 x 891 [2,7,5]
+ CRUSH rule 3 x 892 [6,2,5]
+ CRUSH rule 3 x 893 [2,5,6]
+ CRUSH rule 3 x 894 [7,5,2]
+ CRUSH rule 3 x 895 [5,2,6]
+ CRUSH rule 3 x 896 [2,6,5]
+ CRUSH rule 3 x 897 [5,2,8]
+ CRUSH rule 3 x 898 [2,5,8]
+ CRUSH rule 3 x 899 [2,6,5]
+ CRUSH rule 3 x 900 [5,2,7]
+ CRUSH rule 3 x 901 [5,2,6]
+ CRUSH rule 3 x 902 [8,5,2]
+ CRUSH rule 3 x 903 [5,6,2]
+ CRUSH rule 3 x 904 [5,7,2]
+ CRUSH rule 3 x 905 [6,2,5]
+ CRUSH rule 3 x 906 [2,7,5]
+ CRUSH rule 3 x 907 [7,2,5]
+ CRUSH rule 3 x 908 [5,6,2]
+ CRUSH rule 3 x 909 [2,5,8]
+ CRUSH rule 3 x 910 [6,5,2]
+ CRUSH rule 3 x 911 [5,7,2]
+ CRUSH rule 3 x 912 [2,8,5]
+ CRUSH rule 3 x 913 [7,2,5]
+ CRUSH rule 3 x 914 [6,5,2]
+ CRUSH rule 3 x 915 [8,2,5]
+ CRUSH rule 3 x 916 [5,2,6]
+ CRUSH rule 3 x 917 [2,5,6]
+ CRUSH rule 3 x 918 [8,2,5]
+ CRUSH rule 3 x 919 [6,2,5]
+ CRUSH rule 3 x 920 [7,5,2]
+ CRUSH rule 3 x 921 [2,5,8]
+ CRUSH rule 3 x 922 [6,5,2]
+ CRUSH rule 3 x 923 [5,6,2]
+ CRUSH rule 3 x 924 [5,2,8]
+ CRUSH rule 3 x 925 [5,6,2]
+ CRUSH rule 3 x 926 [5,2,7]
+ CRUSH rule 3 x 927 [2,6,5]
+ CRUSH rule 3 x 928 [8,2,5]
+ CRUSH rule 3 x 929 [5,2,6]
+ CRUSH rule 3 x 930 [2,5,8]
+ CRUSH rule 3 x 931 [5,2,8]
+ CRUSH rule 3 x 932 [5,2,7]
+ CRUSH rule 3 x 933 [8,5,2]
+ CRUSH rule 3 x 934 [5,8,2]
+ CRUSH rule 3 x 935 [6,5,2]
+ CRUSH rule 3 x 936 [2,7,5]
+ CRUSH rule 3 x 937 [5,8,2]
+ CRUSH rule 3 x 938 [6,5,2]
+ CRUSH rule 3 x 939 [2,8,5]
+ CRUSH rule 3 x 940 [8,5,2]
+ CRUSH rule 3 x 941 [5,2,6]
+ CRUSH rule 3 x 942 [2,6,5]
+ CRUSH rule 3 x 943 [8,2,5]
+ CRUSH rule 3 x 944 [5,8,2]
+ CRUSH rule 3 x 945 [7,2,5]
+ CRUSH rule 3 x 946 [2,8,5]
+ CRUSH rule 3 x 947 [5,2,6]
+ CRUSH rule 3 x 948 [7,5,2]
+ CRUSH rule 3 x 949 [6,2,5]
+ CRUSH rule 3 x 950 [5,7,2]
+ CRUSH rule 3 x 951 [5,6,2]
+ CRUSH rule 3 x 952 [2,7,5]
+ CRUSH rule 3 x 953 [2,5,6]
+ CRUSH rule 3 x 954 [5,2,8]
+ CRUSH rule 3 x 955 [8,2,5]
+ CRUSH rule 3 x 956 [2,7,5]
+ CRUSH rule 3 x 957 [7,2,5]
+ CRUSH rule 3 x 958 [8,5,2]
+ CRUSH rule 3 x 959 [5,2,6]
+ CRUSH rule 3 x 960 [5,6,2]
+ CRUSH rule 3 x 961 [5,2,6]
+ CRUSH rule 3 x 962 [7,5,2]
+ CRUSH rule 3 x 963 [2,5,8]
+ CRUSH rule 3 x 964 [5,2,7]
+ CRUSH rule 3 x 965 [7,5,2]
+ CRUSH rule 3 x 966 [5,6,2]
+ CRUSH rule 3 x 967 [8,5,2]
+ CRUSH rule 3 x 968 [7,2,5]
+ CRUSH rule 3 x 969 [8,2,5]
+ CRUSH rule 3 x 970 [2,8,5]
+ CRUSH rule 3 x 971 [2,8,5]
+ CRUSH rule 3 x 972 [2,7,5]
+ CRUSH rule 3 x 973 [2,8,5]
+ CRUSH rule 3 x 974 [5,2,7]
+ CRUSH rule 3 x 975 [5,8,2]
+ CRUSH rule 3 x 976 [5,7,2]
+ CRUSH rule 3 x 977 [8,5,2]
+ CRUSH rule 3 x 978 [7,2,5]
+ CRUSH rule 3 x 979 [7,2,5]
+ CRUSH rule 3 x 980 [6,2,5]
+ CRUSH rule 3 x 981 [7,5,2]
+ CRUSH rule 3 x 982 [5,2,8]
+ CRUSH rule 3 x 983 [5,6,2]
+ CRUSH rule 3 x 984 [2,8,5]
+ CRUSH rule 3 x 985 [2,5,8]
+ CRUSH rule 3 x 986 [8,5,2]
+ CRUSH rule 3 x 987 [2,5,8]
+ CRUSH rule 3 x 988 [2,5,6]
+ CRUSH rule 3 x 989 [2,8,5]
+ CRUSH rule 3 x 990 [2,6,5]
+ CRUSH rule 3 x 991 [2,5,8]
+ CRUSH rule 3 x 992 [7,2,5]
+ CRUSH rule 3 x 993 [2,6,5]
+ CRUSH rule 3 x 994 [5,6,2]
+ CRUSH rule 3 x 995 [7,2,5]
+ CRUSH rule 3 x 996 [6,5,2]
+ CRUSH rule 3 x 997 [6,5,2]
+ CRUSH rule 3 x 998 [8,2,5]
+ CRUSH rule 3 x 999 [2,7,5]
+ CRUSH rule 3 x 1000 [8,5,2]
+ CRUSH rule 3 x 1001 [2,5,6]
+ CRUSH rule 3 x 1002 [2,5,8]
+ CRUSH rule 3 x 1003 [2,7,5]
+ CRUSH rule 3 x 1004 [6,2,5]
+ CRUSH rule 3 x 1005 [6,2,5]
+ CRUSH rule 3 x 1006 [2,8,5]
+ CRUSH rule 3 x 1007 [2,5,8]
+ CRUSH rule 3 x 1008 [2,7,5]
+ CRUSH rule 3 x 1009 [6,5,2]
+ CRUSH rule 3 x 1010 [5,2,6]
+ CRUSH rule 3 x 1011 [5,2,8]
+ CRUSH rule 3 x 1012 [5,2,8]
+ CRUSH rule 3 x 1013 [5,2,8]
+ CRUSH rule 3 x 1014 [2,8,5]
+ CRUSH rule 3 x 1015 [6,5,2]
+ CRUSH rule 3 x 1016 [2,5,6]
+ CRUSH rule 3 x 1017 [6,2,5]
+ CRUSH rule 3 x 1018 [5,2,6]
+ CRUSH rule 3 x 1019 [5,8,2]
+ CRUSH rule 3 x 1020 [5,2,7]
+ CRUSH rule 3 x 1021 [5,2,7]
+ CRUSH rule 3 x 1022 [2,7,5]
+ CRUSH rule 3 x 1023 [5,2,7]
+ rule 3 (choose-set) num_rep 3 result size == 3:\t1024/1024 (esc)
+ rule 4 (choose-set-two), x = 0..1023, numrep = 2..3
+ CRUSH rule 4 x 0 [2]
+ CRUSH rule 4 x 1 [2,8]
+ CRUSH rule 4 x 2 [2,5]
+ CRUSH rule 4 x 3 [8,2]
+ CRUSH rule 4 x 4 [5]
+ CRUSH rule 4 x 5 [7,8]
+ CRUSH rule 4 x 6 [2,6]
+ CRUSH rule 4 x 7 [5]
+ CRUSH rule 4 x 8 [5]
+ CRUSH rule 4 x 9 [2,5]
+ CRUSH rule 4 x 10 [2]
+ CRUSH rule 4 x 11 [2,7]
+ CRUSH rule 4 x 12 [2]
+ CRUSH rule 4 x 13 [5,8]
+ CRUSH rule 4 x 14 [7,6]
+ CRUSH rule 4 x 15 [7,2]
+ CRUSH rule 4 x 16 [5,6]
+ CRUSH rule 4 x 17 [5]
+ CRUSH rule 4 x 18 [2,5]
+ CRUSH rule 4 x 19 [7,5]
+ CRUSH rule 4 x 20 [2,5]
+ CRUSH rule 4 x 21 [5,7]
+ CRUSH rule 4 x 22 [8,5]
+ CRUSH rule 4 x 23 [5,6]
+ CRUSH rule 4 x 24 [2]
+ CRUSH rule 4 x 25 [5,7]
+ CRUSH rule 4 x 26 [2,8]
+ CRUSH rule 4 x 27 [5,2]
+ CRUSH rule 4 x 28 [6,2]
+ CRUSH rule 4 x 29 [8,5]
+ CRUSH rule 4 x 30 [5,7]
+ CRUSH rule 4 x 31 [8,7]
+ CRUSH rule 4 x 32 [5,6]
+ CRUSH rule 4 x 33 [2,7]
+ CRUSH rule 4 x 34 [2,5]
+ CRUSH rule 4 x 35 [2,8]
+ CRUSH rule 4 x 36 [5,8]
+ CRUSH rule 4 x 37 [2,5]
+ CRUSH rule 4 x 38 [5,8]
+ CRUSH rule 4 x 39 [5,7]
+ CRUSH rule 4 x 40 [7,8]
+ CRUSH rule 4 x 41 [2]
+ CRUSH rule 4 x 42 [5]
+ CRUSH rule 4 x 43 [2,5]
+ CRUSH rule 4 x 44 [2,6]
+ CRUSH rule 4 x 45 [8,2]
+ CRUSH rule 4 x 46 [2,5]
+ CRUSH rule 4 x 47 [5]
+ CRUSH rule 4 x 48 [5,6]
+ CRUSH rule 4 x 49 [5]
+ CRUSH rule 4 x 50 [5]
+ CRUSH rule 4 x 51 [5,6]
+ CRUSH rule 4 x 52 [8,6]
+ CRUSH rule 4 x 53 [5]
+ CRUSH rule 4 x 54 [7,6]
+ CRUSH rule 4 x 55 [8,7]
+ CRUSH rule 4 x 56 [6,5]
+ CRUSH rule 4 x 57 [5]
+ CRUSH rule 4 x 58 [2]
+ CRUSH rule 4 x 59 [5,2]
+ CRUSH rule 4 x 60 [5]
+ CRUSH rule 4 x 61 [5,6]
+ CRUSH rule 4 x 62 [7,2]
+ CRUSH rule 4 x 63 [5,6]
+ CRUSH rule 4 x 64 [5]
+ CRUSH rule 4 x 65 [7,5]
+ CRUSH rule 4 x 66 [5]
+ CRUSH rule 4 x 67 [5,2]
+ CRUSH rule 4 x 68 [2,5]
+ CRUSH rule 4 x 69 [5,2]
+ CRUSH rule 4 x 70 [7,2]
+ CRUSH rule 4 x 71 [2,8]
+ CRUSH rule 4 x 72 [6,2]
+ CRUSH rule 4 x 73 [2,7]
+ CRUSH rule 4 x 74 [2,7]
+ CRUSH rule 4 x 75 [5,2]
+ CRUSH rule 4 x 76 [5,2]
+ CRUSH rule 4 x 77 [7,2]
+ CRUSH rule 4 x 78 [2,5]
+ CRUSH rule 4 x 79 [5]
+ CRUSH rule 4 x 80 [2,5]
+ CRUSH rule 4 x 81 [2]
+ CRUSH rule 4 x 82 [7,2]
+ CRUSH rule 4 x 83 [2,6]
+ CRUSH rule 4 x 84 [7,2]
+ CRUSH rule 4 x 85 [5]
+ CRUSH rule 4 x 86 [2]
+ CRUSH rule 4 x 87 [2,7]
+ CRUSH rule 4 x 88 [2,6]
+ CRUSH rule 4 x 89 [5,2]
+ CRUSH rule 4 x 90 [6,7]
+ CRUSH rule 4 x 91 [5,8]
+ CRUSH rule 4 x 92 [2,8]
+ CRUSH rule 4 x 93 [7,5]
+ CRUSH rule 4 x 94 [2,5]
+ CRUSH rule 4 x 95 [7,5]
+ CRUSH rule 4 x 96 [5,6]
+ CRUSH rule 4 x 97 [8,7]
+ CRUSH rule 4 x 98 [2]
+ CRUSH rule 4 x 99 [2,7]
+ CRUSH rule 4 x 100 [2,7]
+ CRUSH rule 4 x 101 [5,7]
+ CRUSH rule 4 x 102 [5,2]
+ CRUSH rule 4 x 103 [5,7]
+ CRUSH rule 4 x 104 [7,5]
+ CRUSH rule 4 x 105 [2,5]
+ CRUSH rule 4 x 106 [2,6]
+ CRUSH rule 4 x 107 [5,2]
+ CRUSH rule 4 x 108 [7,2]
+ CRUSH rule 4 x 109 [2]
+ CRUSH rule 4 x 110 [5,2]
+ CRUSH rule 4 x 111 [2]
+ CRUSH rule 4 x 112 [2]
+ CRUSH rule 4 x 113 [6,2]
+ CRUSH rule 4 x 114 [7,6]
+ CRUSH rule 4 x 115 [8,2]
+ CRUSH rule 4 x 116 [2]
+ CRUSH rule 4 x 117 [7,5]
+ CRUSH rule 4 x 118 [2,5]
+ CRUSH rule 4 x 119 [5,6]
+ CRUSH rule 4 x 120 [2]
+ CRUSH rule 4 x 121 [2]
+ CRUSH rule 4 x 122 [8,5]
+ CRUSH rule 4 x 123 [2,5]
+ CRUSH rule 4 x 124 [5]
+ CRUSH rule 4 x 125 [2,7]
+ CRUSH rule 4 x 126 [5,2]
+ CRUSH rule 4 x 127 [5,6]
+ CRUSH rule 4 x 128 [5]
+ CRUSH rule 4 x 129 [2]
+ CRUSH rule 4 x 130 [5,8]
+ CRUSH rule 4 x 131 [2]
+ CRUSH rule 4 x 132 [2]
+ CRUSH rule 4 x 133 [5,6]
+ CRUSH rule 4 x 134 [2,8]
+ CRUSH rule 4 x 135 [5,6]
+ CRUSH rule 4 x 136 [2]
+ CRUSH rule 4 x 137 [7,5]
+ CRUSH rule 4 x 138 [8,7]
+ CRUSH rule 4 x 139 [5,2]
+ CRUSH rule 4 x 140 [2,6]
+ CRUSH rule 4 x 141 [6,8]
+ CRUSH rule 4 x 142 [5]
+ CRUSH rule 4 x 143 [5,8]
+ CRUSH rule 4 x 144 [8,2]
+ CRUSH rule 4 x 145 [8,5]
+ CRUSH rule 4 x 146 [2,6]
+ CRUSH rule 4 x 147 [2,8]
+ CRUSH rule 4 x 148 [5]
+ CRUSH rule 4 x 149 [5,8]
+ CRUSH rule 4 x 150 [2,6]
+ CRUSH rule 4 x 151 [5]
+ CRUSH rule 4 x 152 [8,5]
+ CRUSH rule 4 x 153 [8,6]
+ CRUSH rule 4 x 154 [5,2]
+ CRUSH rule 4 x 155 [5]
+ CRUSH rule 4 x 156 [5]
+ CRUSH rule 4 x 157 [5,2]
+ CRUSH rule 4 x 158 [2,8]
+ CRUSH rule 4 x 159 [7,2]
+ CRUSH rule 4 x 160 [2,8]
+ CRUSH rule 4 x 161 [2,5]
+ CRUSH rule 4 x 162 [2,6]
+ CRUSH rule 4 x 163 [5,6]
+ CRUSH rule 4 x 164 [7,8]
+ CRUSH rule 4 x 165 [7,2]
+ CRUSH rule 4 x 166 [2,5]
+ CRUSH rule 4 x 167 [2]
+ CRUSH rule 4 x 168 [5,2]
+ CRUSH rule 4 x 169 [2,6]
+ CRUSH rule 4 x 170 [2]
+ CRUSH rule 4 x 171 [7,5]
+ CRUSH rule 4 x 172 [2,7]
+ CRUSH rule 4 x 173 [8,5]
+ CRUSH rule 4 x 174 [2,5]
+ CRUSH rule 4 x 175 [6,2]
+ CRUSH rule 4 x 176 [5]
+ CRUSH rule 4 x 177 [5]
+ CRUSH rule 4 x 178 [5,2]
+ CRUSH rule 4 x 179 [5,2]
+ CRUSH rule 4 x 180 [5,8]
+ CRUSH rule 4 x 181 [6,2]
+ CRUSH rule 4 x 182 [8,5]
+ CRUSH rule 4 x 183 [7,8]
+ CRUSH rule 4 x 184 [5,7]
+ CRUSH rule 4 x 185 [6,8]
+ CRUSH rule 4 x 186 [2]
+ CRUSH rule 4 x 187 [2,6]
+ CRUSH rule 4 x 188 [2,8]
+ CRUSH rule 4 x 189 [2,7]
+ CRUSH rule 4 x 190 [5,2]
+ CRUSH rule 4 x 191 [7,6]
+ CRUSH rule 4 x 192 [5,2]
+ CRUSH rule 4 x 193 [5,2]
+ CRUSH rule 4 x 194 [2,5]
+ CRUSH rule 4 x 195 [6,5]
+ CRUSH rule 4 x 196 [6,7]
+ CRUSH rule 4 x 197 [6,5]
+ CRUSH rule 4 x 198 [2,5]
+ CRUSH rule 4 x 199 [2,5]
+ CRUSH rule 4 x 200 [2,5]
+ CRUSH rule 4 x 201 [7,2]
+ CRUSH rule 4 x 202 [6,5]
+ CRUSH rule 4 x 203 [5,8]
+ CRUSH rule 4 x 204 [2]
+ CRUSH rule 4 x 205 [2,7]
+ CRUSH rule 4 x 206 [2]
+ CRUSH rule 4 x 207 [5,2]
+ CRUSH rule 4 x 208 [7,2]
+ CRUSH rule 4 x 209 [2]
+ CRUSH rule 4 x 210 [2]
+ CRUSH rule 4 x 211 [5]
+ CRUSH rule 4 x 212 [7,5]
+ CRUSH rule 4 x 213 [8,5]
+ CRUSH rule 4 x 214 [5]
+ CRUSH rule 4 x 215 [8,2]
+ CRUSH rule 4 x 216 [5,2]
+ CRUSH rule 4 x 217 [2]
+ CRUSH rule 4 x 218 [2,7]
+ CRUSH rule 4 x 219 [5,8]
+ CRUSH rule 4 x 220 [5,7]
+ CRUSH rule 4 x 221 [5,6]
+ CRUSH rule 4 x 222 [6,8]
+ CRUSH rule 4 x 223 [2,5]
+ CRUSH rule 4 x 224 [2,5]
+ CRUSH rule 4 x 225 [8,6]
+ CRUSH rule 4 x 226 [7,2]
+ CRUSH rule 4 x 227 [5]
+ CRUSH rule 4 x 228 [5]
+ CRUSH rule 4 x 229 [5]
+ CRUSH rule 4 x 230 [5,7]
+ CRUSH rule 4 x 231 [5]
+ CRUSH rule 4 x 232 [2,7]
+ CRUSH rule 4 x 233 [5]
+ CRUSH rule 4 x 234 [2]
+ CRUSH rule 4 x 235 [5,8]
+ CRUSH rule 4 x 236 [5,2]
+ CRUSH rule 4 x 237 [5,7]
+ CRUSH rule 4 x 238 [5]
+ CRUSH rule 4 x 239 [8,7]
+ CRUSH rule 4 x 240 [5,7]
+ CRUSH rule 4 x 241 [5,2]
+ CRUSH rule 4 x 242 [5]
+ CRUSH rule 4 x 243 [5,7]
+ CRUSH rule 4 x 244 [5,6]
+ CRUSH rule 4 x 245 [7,6]
+ CRUSH rule 4 x 246 [2,5]
+ CRUSH rule 4 x 247 [6,2]
+ CRUSH rule 4 x 248 [8,2]
+ CRUSH rule 4 x 249 [2]
+ CRUSH rule 4 x 250 [2]
+ CRUSH rule 4 x 251 [2,5]
+ CRUSH rule 4 x 252 [5,7]
+ CRUSH rule 4 x 253 [5,2]
+ CRUSH rule 4 x 254 [5]
+ CRUSH rule 4 x 255 [2,7]
+ CRUSH rule 4 x 256 [5,7]
+ CRUSH rule 4 x 257 [2,8]
+ CRUSH rule 4 x 258 [5]
+ CRUSH rule 4 x 259 [5]
+ CRUSH rule 4 x 260 [5,6]
+ CRUSH rule 4 x 261 [8,7]
+ CRUSH rule 4 x 262 [5]
+ CRUSH rule 4 x 263 [6,8]
+ CRUSH rule 4 x 264 [5,6]
+ CRUSH rule 4 x 265 [8,6]
+ CRUSH rule 4 x 266 [8,2]
+ CRUSH rule 4 x 267 [2,5]
+ CRUSH rule 4 x 268 [2,7]
+ CRUSH rule 4 x 269 [2,8]
+ CRUSH rule 4 x 270 [5,2]
+ CRUSH rule 4 x 271 [7,5]
+ CRUSH rule 4 x 272 [2,8]
+ CRUSH rule 4 x 273 [5]
+ CRUSH rule 4 x 274 [6,8]
+ CRUSH rule 4 x 275 [5]
+ CRUSH rule 4 x 276 [7,2]
+ CRUSH rule 4 x 277 [6,5]
+ CRUSH rule 4 x 278 [6,8]
+ CRUSH rule 4 x 279 [8,5]
+ CRUSH rule 4 x 280 [2,6]
+ CRUSH rule 4 x 281 [8,2]
+ CRUSH rule 4 x 282 [5,2]
+ CRUSH rule 4 x 283 [8,2]
+ CRUSH rule 4 x 284 [6,5]
+ CRUSH rule 4 x 285 [5]
+ CRUSH rule 4 x 286 [2]
+ CRUSH rule 4 x 287 [2,5]
+ CRUSH rule 4 x 288 [8,2]
+ CRUSH rule 4 x 289 [5,6]
+ CRUSH rule 4 x 290 [2,5]
+ CRUSH rule 4 x 291 [2]
+ CRUSH rule 4 x 292 [8,2]
+ CRUSH rule 4 x 293 [6,2]
+ CRUSH rule 4 x 294 [7,5]
+ CRUSH rule 4 x 295 [5,8]
+ CRUSH rule 4 x 296 [5,2]
+ CRUSH rule 4 x 297 [6,2]
+ CRUSH rule 4 x 298 [2]
+ CRUSH rule 4 x 299 [2]
+ CRUSH rule 4 x 300 [8,7]
+ CRUSH rule 4 x 301 [2,8]
+ CRUSH rule 4 x 302 [5,2]
+ CRUSH rule 4 x 303 [7,5]
+ CRUSH rule 4 x 304 [2,7]
+ CRUSH rule 4 x 305 [5,8]
+ CRUSH rule 4 x 306 [2,7]
+ CRUSH rule 4 x 307 [2]
+ CRUSH rule 4 x 308 [2,8]
+ CRUSH rule 4 x 309 [7,5]
+ CRUSH rule 4 x 310 [5]
+ CRUSH rule 4 x 311 [5]
+ CRUSH rule 4 x 312 [2]
+ CRUSH rule 4 x 313 [5]
+ CRUSH rule 4 x 314 [5]
+ CRUSH rule 4 x 315 [2]
+ CRUSH rule 4 x 316 [6,5]
+ CRUSH rule 4 x 317 [2,6]
+ CRUSH rule 4 x 318 [8,6]
+ CRUSH rule 4 x 319 [5,2]
+ CRUSH rule 4 x 320 [5,7]
+ CRUSH rule 4 x 321 [2,5]
+ CRUSH rule 4 x 322 [2,7]
+ CRUSH rule 4 x 323 [5,7]
+ CRUSH rule 4 x 324 [7,2]
+ CRUSH rule 4 x 325 [5,6]
+ CRUSH rule 4 x 326 [5]
+ CRUSH rule 4 x 327 [2,6]
+ CRUSH rule 4 x 328 [7,5]
+ CRUSH rule 4 x 329 [5,6]
+ CRUSH rule 4 x 330 [5,7]
+ CRUSH rule 4 x 331 [2,6]
+ CRUSH rule 4 x 332 [2]
+ CRUSH rule 4 x 333 [6,8]
+ CRUSH rule 4 x 334 [8,5]
+ CRUSH rule 4 x 335 [7,2]
+ CRUSH rule 4 x 336 [5,6]
+ CRUSH rule 4 x 337 [7,2]
+ CRUSH rule 4 x 338 [5,6]
+ CRUSH rule 4 x 339 [7,5]
+ CRUSH rule 4 x 340 [2]
+ CRUSH rule 4 x 341 [5,2]
+ CRUSH rule 4 x 342 [2,7]
+ CRUSH rule 4 x 343 [6,7]
+ CRUSH rule 4 x 344 [6,2]
+ CRUSH rule 4 x 345 [5]
+ CRUSH rule 4 x 346 [8,2]
+ CRUSH rule 4 x 347 [5,2]
+ CRUSH rule 4 x 348 [8,2]
+ CRUSH rule 4 x 349 [2,6]
+ CRUSH rule 4 x 350 [8,5]
+ CRUSH rule 4 x 351 [5,6]
+ CRUSH rule 4 x 352 [2]
+ CRUSH rule 4 x 353 [6,5]
+ CRUSH rule 4 x 354 [2,5]
+ CRUSH rule 4 x 355 [5]
+ CRUSH rule 4 x 356 [5]
+ CRUSH rule 4 x 357 [6,2]
+ CRUSH rule 4 x 358 [2]
+ CRUSH rule 4 x 359 [6,7]
+ CRUSH rule 4 x 360 [5]
+ CRUSH rule 4 x 361 [8,5]
+ CRUSH rule 4 x 362 [5]
+ CRUSH rule 4 x 363 [5,2]
+ CRUSH rule 4 x 364 [2,5]
+ CRUSH rule 4 x 365 [6,7]
+ CRUSH rule 4 x 366 [7,2]
+ CRUSH rule 4 x 367 [5]
+ CRUSH rule 4 x 368 [7,5]
+ CRUSH rule 4 x 369 [5,7]
+ CRUSH rule 4 x 370 [8,7]
+ CRUSH rule 4 x 371 [2,5]
+ CRUSH rule 4 x 372 [5,2]
+ CRUSH rule 4 x 373 [2,6]
+ CRUSH rule 4 x 374 [5,8]
+ CRUSH rule 4 x 375 [6,5]
+ CRUSH rule 4 x 376 [7,2]
+ CRUSH rule 4 x 377 [2]
+ CRUSH rule 4 x 378 [2]
+ CRUSH rule 4 x 379 [8,5]
+ CRUSH rule 4 x 380 [2,5]
+ CRUSH rule 4 x 381 [2,5]
+ CRUSH rule 4 x 382 [2,5]
+ CRUSH rule 4 x 383 [5]
+ CRUSH rule 4 x 384 [7,2]
+ CRUSH rule 4 x 385 [7,5]
+ CRUSH rule 4 x 386 [2,5]
+ CRUSH rule 4 x 387 [2,5]
+ CRUSH rule 4 x 388 [5,2]
+ CRUSH rule 4 x 389 [2,5]
+ CRUSH rule 4 x 390 [5,6]
+ CRUSH rule 4 x 391 [5,6]
+ CRUSH rule 4 x 392 [2,8]
+ CRUSH rule 4 x 393 [5,2]
+ CRUSH rule 4 x 394 [5,7]
+ CRUSH rule 4 x 395 [5,2]
+ CRUSH rule 4 x 396 [5,2]
+ CRUSH rule 4 x 397 [2]
+ CRUSH rule 4 x 398 [2,5]
+ CRUSH rule 4 x 399 [8,7]
+ CRUSH rule 4 x 400 [8,2]
+ CRUSH rule 4 x 401 [2]
+ CRUSH rule 4 x 402 [7,8]
+ CRUSH rule 4 x 403 [2]
+ CRUSH rule 4 x 404 [5]
+ CRUSH rule 4 x 405 [6,5]
+ CRUSH rule 4 x 406 [2]
+ CRUSH rule 4 x 407 [2,8]
+ CRUSH rule 4 x 408 [5,2]
+ CRUSH rule 4 x 409 [7,5]
+ CRUSH rule 4 x 410 [8,6]
+ CRUSH rule 4 x 411 [2]
+ CRUSH rule 4 x 412 [2,5]
+ CRUSH rule 4 x 413 [5,2]
+ CRUSH rule 4 x 414 [5,2]
+ CRUSH rule 4 x 415 [2,6]
+ CRUSH rule 4 x 416 [2]
+ CRUSH rule 4 x 417 [8,7]
+ CRUSH rule 4 x 418 [7,6]
+ CRUSH rule 4 x 419 [8,5]
+ CRUSH rule 4 x 420 [2,5]
+ CRUSH rule 4 x 421 [8,6]
+ CRUSH rule 4 x 422 [6,7]
+ CRUSH rule 4 x 423 [2,5]
+ CRUSH rule 4 x 424 [8,5]
+ CRUSH rule 4 x 425 [2,5]
+ CRUSH rule 4 x 426 [6,7]
+ CRUSH rule 4 x 427 [2,7]
+ CRUSH rule 4 x 428 [5]
+ CRUSH rule 4 x 429 [5,6]
+ CRUSH rule 4 x 430 [5,6]
+ CRUSH rule 4 x 431 [5]
+ CRUSH rule 4 x 432 [7,2]
+ CRUSH rule 4 x 433 [6,5]
+ CRUSH rule 4 x 434 [5,2]
+ CRUSH rule 4 x 435 [2,5]
+ CRUSH rule 4 x 436 [5,2]
+ CRUSH rule 4 x 437 [7,5]
+ CRUSH rule 4 x 438 [2,5]
+ CRUSH rule 4 x 439 [2,5]
+ CRUSH rule 4 x 440 [2,7]
+ CRUSH rule 4 x 441 [5,7]
+ CRUSH rule 4 x 442 [2,5]
+ CRUSH rule 4 x 443 [6,8]
+ CRUSH rule 4 x 444 [7,2]
+ CRUSH rule 4 x 445 [6,5]
+ CRUSH rule 4 x 446 [5]
+ CRUSH rule 4 x 447 [2]
+ CRUSH rule 4 x 448 [7,2]
+ CRUSH rule 4 x 449 [7,8]
+ CRUSH rule 4 x 450 [5]
+ CRUSH rule 4 x 451 [6,8]
+ CRUSH rule 4 x 452 [8,5]
+ CRUSH rule 4 x 453 [6,8]
+ CRUSH rule 4 x 454 [6,7]
+ CRUSH rule 4 x 455 [2,7]
+ CRUSH rule 4 x 456 [6,8]
+ CRUSH rule 4 x 457 [7,2]
+ CRUSH rule 4 x 458 [2,8]
+ CRUSH rule 4 x 459 [2]
+ CRUSH rule 4 x 460 [6,5]
+ CRUSH rule 4 x 461 [6,5]
+ CRUSH rule 4 x 462 [8,2]
+ CRUSH rule 4 x 463 [6,7]
+ CRUSH rule 4 x 464 [7,5]
+ CRUSH rule 4 x 465 [7,6]
+ CRUSH rule 4 x 466 [5,8]
+ CRUSH rule 4 x 467 [6,5]
+ CRUSH rule 4 x 468 [7,8]
+ CRUSH rule 4 x 469 [7,2]
+ CRUSH rule 4 x 470 [5,2]
+ CRUSH rule 4 x 471 [2]
+ CRUSH rule 4 x 472 [5]
+ CRUSH rule 4 x 473 [2]
+ CRUSH rule 4 x 474 [6,2]
+ CRUSH rule 4 x 475 [6,7]
+ CRUSH rule 4 x 476 [5]
+ CRUSH rule 4 x 477 [5,8]
+ CRUSH rule 4 x 478 [6,7]
+ CRUSH rule 4 x 479 [2,5]
+ CRUSH rule 4 x 480 [2,8]
+ CRUSH rule 4 x 481 [2,5]
+ CRUSH rule 4 x 482 [5]
+ CRUSH rule 4 x 483 [2]
+ CRUSH rule 4 x 484 [2]
+ CRUSH rule 4 x 485 [5,7]
+ CRUSH rule 4 x 486 [5,2]
+ CRUSH rule 4 x 487 [5,2]
+ CRUSH rule 4 x 488 [5,7]
+ CRUSH rule 4 x 489 [2,8]
+ CRUSH rule 4 x 490 [6,5]
+ CRUSH rule 4 x 491 [2]
+ CRUSH rule 4 x 492 [6,5]
+ CRUSH rule 4 x 493 [2]
+ CRUSH rule 4 x 494 [2]
+ CRUSH rule 4 x 495 [5]
+ CRUSH rule 4 x 496 [7,5]
+ CRUSH rule 4 x 497 [5,7]
+ CRUSH rule 4 x 498 [2,5]
+ CRUSH rule 4 x 499 [8,5]
+ CRUSH rule 4 x 500 [5,6]
+ CRUSH rule 4 x 501 [2,7]
+ CRUSH rule 4 x 502 [7,2]
+ CRUSH rule 4 x 503 [2,5]
+ CRUSH rule 4 x 504 [5,6]
+ CRUSH rule 4 x 505 [2,7]
+ CRUSH rule 4 x 506 [5]
+ CRUSH rule 4 x 507 [6,2]
+ CRUSH rule 4 x 508 [2]
+ CRUSH rule 4 x 509 [7,5]
+ CRUSH rule 4 x 510 [6,2]
+ CRUSH rule 4 x 511 [5,8]
+ CRUSH rule 4 x 512 [7,6]
+ CRUSH rule 4 x 513 [7,2]
+ CRUSH rule 4 x 514 [5]
+ CRUSH rule 4 x 515 [8,5]
+ CRUSH rule 4 x 516 [5,2]
+ CRUSH rule 4 x 517 [7,8]
+ CRUSH rule 4 x 518 [5,6]
+ CRUSH rule 4 x 519 [7,5]
+ CRUSH rule 4 x 520 [2,6]
+ CRUSH rule 4 x 521 [8,7]
+ CRUSH rule 4 x 522 [6,8]
+ CRUSH rule 4 x 523 [5,2]
+ CRUSH rule 4 x 524 [2,5]
+ CRUSH rule 4 x 525 [2,5]
+ CRUSH rule 4 x 526 [2,5]
+ CRUSH rule 4 x 527 [2]
+ CRUSH rule 4 x 528 [5]
+ CRUSH rule 4 x 529 [5,7]
+ CRUSH rule 4 x 530 [6,7]
+ CRUSH rule 4 x 531 [6,2]
+ CRUSH rule 4 x 532 [6,5]
+ CRUSH rule 4 x 533 [5,6]
+ CRUSH rule 4 x 534 [7,5]
+ CRUSH rule 4 x 535 [8,6]
+ CRUSH rule 4 x 536 [6,7]
+ CRUSH rule 4 x 537 [5,7]
+ CRUSH rule 4 x 538 [6,8]
+ CRUSH rule 4 x 539 [8,5]
+ CRUSH rule 4 x 540 [2,6]
+ CRUSH rule 4 x 541 [2,5]
+ CRUSH rule 4 x 542 [5]
+ CRUSH rule 4 x 543 [6,2]
+ CRUSH rule 4 x 544 [5,7]
+ CRUSH rule 4 x 545 [5,7]
+ CRUSH rule 4 x 546 [6,2]
+ CRUSH rule 4 x 547 [8,2]
+ CRUSH rule 4 x 548 [5,2]
+ CRUSH rule 4 x 549 [5,8]
+ CRUSH rule 4 x 550 [2,5]
+ CRUSH rule 4 x 551 [7,5]
+ CRUSH rule 4 x 552 [5]
+ CRUSH rule 4 x 553 [5,2]
+ CRUSH rule 4 x 554 [2,8]
+ CRUSH rule 4 x 555 [5,2]
+ CRUSH rule 4 x 556 [5]
+ CRUSH rule 4 x 557 [7,5]
+ CRUSH rule 4 x 558 [5,2]
+ CRUSH rule 4 x 559 [5,2]
+ CRUSH rule 4 x 560 [8,5]
+ CRUSH rule 4 x 561 [6,5]
+ CRUSH rule 4 x 562 [5]
+ CRUSH rule 4 x 563 [2,6]
+ CRUSH rule 4 x 564 [5,2]
+ CRUSH rule 4 x 565 [5,6]
+ CRUSH rule 4 x 566 [5,7]
+ CRUSH rule 4 x 567 [5,6]
+ CRUSH rule 4 x 568 [7,5]
+ CRUSH rule 4 x 569 [5]
+ CRUSH rule 4 x 570 [2,5]
+ CRUSH rule 4 x 571 [5,7]
+ CRUSH rule 4 x 572 [5]
+ CRUSH rule 4 x 573 [5,2]
+ CRUSH rule 4 x 574 [2]
+ CRUSH rule 4 x 575 [8,6]
+ CRUSH rule 4 x 576 [5,6]
+ CRUSH rule 4 x 577 [8,2]
+ CRUSH rule 4 x 578 [6,8]
+ CRUSH rule 4 x 579 [5,2]
+ CRUSH rule 4 x 580 [5,2]
+ CRUSH rule 4 x 581 [7,2]
+ CRUSH rule 4 x 582 [2,8]
+ CRUSH rule 4 x 583 [6,2]
+ CRUSH rule 4 x 584 [8,2]
+ CRUSH rule 4 x 585 [7,2]
+ CRUSH rule 4 x 586 [2]
+ CRUSH rule 4 x 587 [2,5]
+ CRUSH rule 4 x 588 [5]
+ CRUSH rule 4 x 589 [7,2]
+ CRUSH rule 4 x 590 [6,2]
+ CRUSH rule 4 x 591 [5,2]
+ CRUSH rule 4 x 592 [2]
+ CRUSH rule 4 x 593 [2,8]
+ CRUSH rule 4 x 594 [2,7]
+ CRUSH rule 4 x 595 [7,2]
+ CRUSH rule 4 x 596 [5]
+ CRUSH rule 4 x 597 [5,2]
+ CRUSH rule 4 x 598 [5,2]
+ CRUSH rule 4 x 599 [5,2]
+ CRUSH rule 4 x 600 [7,2]
+ CRUSH rule 4 x 601 [2,7]
+ CRUSH rule 4 x 602 [5,7]
+ CRUSH rule 4 x 603 [5,2]
+ CRUSH rule 4 x 604 [7,5]
+ CRUSH rule 4 x 605 [5,2]
+ CRUSH rule 4 x 606 [2]
+ CRUSH rule 4 x 607 [2,5]
+ CRUSH rule 4 x 608 [5]
+ CRUSH rule 4 x 609 [5,2]
+ CRUSH rule 4 x 610 [5,7]
+ CRUSH rule 4 x 611 [2]
+ CRUSH rule 4 x 612 [2]
+ CRUSH rule 4 x 613 [7,2]
+ CRUSH rule 4 x 614 [7,8]
+ CRUSH rule 4 x 615 [6,8]
+ CRUSH rule 4 x 616 [2,8]
+ CRUSH rule 4 x 617 [6,2]
+ CRUSH rule 4 x 618 [7,6]
+ CRUSH rule 4 x 619 [5,2]
+ CRUSH rule 4 x 620 [5,2]
+ CRUSH rule 4 x 621 [5,8]
+ CRUSH rule 4 x 622 [2,5]
+ CRUSH rule 4 x 623 [2,6]
+ CRUSH rule 4 x 624 [5]
+ CRUSH rule 4 x 625 [2,5]
+ CRUSH rule 4 x 626 [7,8]
+ CRUSH rule 4 x 627 [2,7]
+ CRUSH rule 4 x 628 [8,2]
+ CRUSH rule 4 x 629 [2,6]
+ CRUSH rule 4 x 630 [2,7]
+ CRUSH rule 4 x 631 [2,7]
+ CRUSH rule 4 x 632 [7,2]
+ CRUSH rule 4 x 633 [8,6]
+ CRUSH rule 4 x 634 [2,5]
+ CRUSH rule 4 x 635 [5,6]
+ CRUSH rule 4 x 636 [2,5]
+ CRUSH rule 4 x 637 [5,2]
+ CRUSH rule 4 x 638 [6,8]
+ CRUSH rule 4 x 639 [5,2]
+ CRUSH rule 4 x 640 [5,2]
+ CRUSH rule 4 x 641 [7,2]
+ CRUSH rule 4 x 642 [2]
+ CRUSH rule 4 x 643 [5]
+ CRUSH rule 4 x 644 [8,2]
+ CRUSH rule 4 x 645 [5]
+ CRUSH rule 4 x 646 [8,2]
+ CRUSH rule 4 x 647 [7,2]
+ CRUSH rule 4 x 648 [2,6]
+ CRUSH rule 4 x 649 [5,7]
+ CRUSH rule 4 x 650 [7,8]
+ CRUSH rule 4 x 651 [5,7]
+ CRUSH rule 4 x 652 [5]
+ CRUSH rule 4 x 653 [8,5]
+ CRUSH rule 4 x 654 [7,5]
+ CRUSH rule 4 x 655 [2,5]
+ CRUSH rule 4 x 656 [5]
+ CRUSH rule 4 x 657 [6,2]
+ CRUSH rule 4 x 658 [5]
+ CRUSH rule 4 x 659 [5,6]
+ CRUSH rule 4 x 660 [7,8]
+ CRUSH rule 4 x 661 [2,8]
+ CRUSH rule 4 x 662 [5]
+ CRUSH rule 4 x 663 [2,5]
+ CRUSH rule 4 x 664 [2,5]
+ CRUSH rule 4 x 665 [5,7]
+ CRUSH rule 4 x 666 [2,8]
+ CRUSH rule 4 x 667 [2,5]
+ CRUSH rule 4 x 668 [5,7]
+ CRUSH rule 4 x 669 [6,5]
+ CRUSH rule 4 x 670 [5,2]
+ CRUSH rule 4 x 671 [2]
+ CRUSH rule 4 x 672 [5]
+ CRUSH rule 4 x 673 [5]
+ CRUSH rule 4 x 674 [5,2]
+ CRUSH rule 4 x 675 [2,8]
+ CRUSH rule 4 x 676 [2]
+ CRUSH rule 4 x 677 [5,2]
+ CRUSH rule 4 x 678 [2,5]
+ CRUSH rule 4 x 679 [6,2]
+ CRUSH rule 4 x 680 [2,5]
+ CRUSH rule 4 x 681 [5,7]
+ CRUSH rule 4 x 682 [2,5]
+ CRUSH rule 4 x 683 [2]
+ CRUSH rule 4 x 684 [7,2]
+ CRUSH rule 4 x 685 [7,2]
+ CRUSH rule 4 x 686 [2,5]
+ CRUSH rule 4 x 687 [5]
+ CRUSH rule 4 x 688 [5,7]
+ CRUSH rule 4 x 689 [6,5]
+ CRUSH rule 4 x 690 [8,2]
+ CRUSH rule 4 x 691 [5,2]
+ CRUSH rule 4 x 692 [7,2]
+ CRUSH rule 4 x 693 [6,7]
+ CRUSH rule 4 x 694 [6,5]
+ CRUSH rule 4 x 695 [2,8]
+ CRUSH rule 4 x 696 [2,5]
+ CRUSH rule 4 x 697 [6,2]
+ CRUSH rule 4 x 698 [6,2]
+ CRUSH rule 4 x 699 [2,6]
+ CRUSH rule 4 x 700 [2,5]
+ CRUSH rule 4 x 701 [5]
+ CRUSH rule 4 x 702 [5]
+ CRUSH rule 4 x 703 [8,5]
+ CRUSH rule 4 x 704 [2,5]
+ CRUSH rule 4 x 705 [8,6]
+ CRUSH rule 4 x 706 [2]
+ CRUSH rule 4 x 707 [7,8]
+ CRUSH rule 4 x 708 [5]
+ CRUSH rule 4 x 709 [6,5]
+ CRUSH rule 4 x 710 [8,5]
+ CRUSH rule 4 x 711 [2,5]
+ CRUSH rule 4 x 712 [2,5]
+ CRUSH rule 4 x 713 [6,7]
+ CRUSH rule 4 x 714 [5,2]
+ CRUSH rule 4 x 715 [2]
+ CRUSH rule 4 x 716 [5,6]
+ CRUSH rule 4 x 717 [8,7]
+ CRUSH rule 4 x 718 [5,7]
+ CRUSH rule 4 x 719 [2,6]
+ CRUSH rule 4 x 720 [6,8]
+ CRUSH rule 4 x 721 [5]
+ CRUSH rule 4 x 722 [5]
+ CRUSH rule 4 x 723 [5,2]
+ CRUSH rule 4 x 724 [2,6]
+ CRUSH rule 4 x 725 [2]
+ CRUSH rule 4 x 726 [5,8]
+ CRUSH rule 4 x 727 [5,6]
+ CRUSH rule 4 x 728 [2]
+ CRUSH rule 4 x 729 [5]
+ CRUSH rule 4 x 730 [5,7]
+ CRUSH rule 4 x 731 [5,2]
+ CRUSH rule 4 x 732 [2,5]
+ CRUSH rule 4 x 733 [5]
+ CRUSH rule 4 x 734 [6,5]
+ CRUSH rule 4 x 735 [5,8]
+ CRUSH rule 4 x 736 [5]
+ CRUSH rule 4 x 737 [2]
+ CRUSH rule 4 x 738 [5,2]
+ CRUSH rule 4 x 739 [2]
+ CRUSH rule 4 x 740 [2]
+ CRUSH rule 4 x 741 [7,8]
+ CRUSH rule 4 x 742 [8,2]
+ CRUSH rule 4 x 743 [7,2]
+ CRUSH rule 4 x 744 [5,7]
+ CRUSH rule 4 x 745 [5]
+ CRUSH rule 4 x 746 [5,2]
+ CRUSH rule 4 x 747 [6,2]
+ CRUSH rule 4 x 748 [2,7]
+ CRUSH rule 4 x 749 [5]
+ CRUSH rule 4 x 750 [2,6]
+ CRUSH rule 4 x 751 [2]
+ CRUSH rule 4 x 752 [8,2]
+ CRUSH rule 4 x 753 [7,8]
+ CRUSH rule 4 x 754 [8,6]
+ CRUSH rule 4 x 755 [2]
+ CRUSH rule 4 x 756 [5,6]
+ CRUSH rule 4 x 757 [8,6]
+ CRUSH rule 4 x 758 [6,2]
+ CRUSH rule 4 x 759 [8,5]
+ CRUSH rule 4 x 760 [2,5]
+ CRUSH rule 4 x 761 [5,2]
+ CRUSH rule 4 x 762 [2,7]
+ CRUSH rule 4 x 763 [8,6]
+ CRUSH rule 4 x 764 [2,7]
+ CRUSH rule 4 x 765 [6,5]
+ CRUSH rule 4 x 766 [8,5]
+ CRUSH rule 4 x 767 [2]
+ CRUSH rule 4 x 768 [8,5]
+ CRUSH rule 4 x 769 [6,2]
+ CRUSH rule 4 x 770 [6,2]
+ CRUSH rule 4 x 771 [7,2]
+ CRUSH rule 4 x 772 [8,5]
+ CRUSH rule 4 x 773 [5,2]
+ CRUSH rule 4 x 774 [5,6]
+ CRUSH rule 4 x 775 [6,8]
+ CRUSH rule 4 x 776 [7,2]
+ CRUSH rule 4 x 777 [5,2]
+ CRUSH rule 4 x 778 [2,8]
+ CRUSH rule 4 x 779 [2,7]
+ CRUSH rule 4 x 780 [2]
+ CRUSH rule 4 x 781 [6,5]
+ CRUSH rule 4 x 782 [5]
+ CRUSH rule 4 x 783 [7,2]
+ CRUSH rule 4 x 784 [2]
+ CRUSH rule 4 x 785 [6,2]
+ CRUSH rule 4 x 786 [7,6]
+ CRUSH rule 4 x 787 [2]
+ CRUSH rule 4 x 788 [6,2]
+ CRUSH rule 4 x 789 [2,5]
+ CRUSH rule 4 x 790 [8,5]
+ CRUSH rule 4 x 791 [5,8]
+ CRUSH rule 4 x 792 [5,8]
+ CRUSH rule 4 x 793 [6,2]
+ CRUSH rule 4 x 794 [2,6]
+ CRUSH rule 4 x 795 [2,5]
+ CRUSH rule 4 x 796 [5]
+ CRUSH rule 4 x 797 [2,5]
+ CRUSH rule 4 x 798 [6,8]
+ CRUSH rule 4 x 799 [5,2]
+ CRUSH rule 4 x 800 [5,2]
+ CRUSH rule 4 x 801 [5,6]
+ CRUSH rule 4 x 802 [2,8]
+ CRUSH rule 4 x 803 [2,5]
+ CRUSH rule 4 x 804 [6,2]
+ CRUSH rule 4 x 805 [5,6]
+ CRUSH rule 4 x 806 [2,5]
+ CRUSH rule 4 x 807 [5]
+ CRUSH rule 4 x 808 [5,6]
+ CRUSH rule 4 x 809 [2,5]
+ CRUSH rule 4 x 810 [5,7]
+ CRUSH rule 4 x 811 [8,5]
+ CRUSH rule 4 x 812 [8,5]
+ CRUSH rule 4 x 813 [6,5]
+ CRUSH rule 4 x 814 [5,6]
+ CRUSH rule 4 x 815 [5,2]
+ CRUSH rule 4 x 816 [2]
+ CRUSH rule 4 x 817 [5]
+ CRUSH rule 4 x 818 [5]
+ CRUSH rule 4 x 819 [5,2]
+ CRUSH rule 4 x 820 [5]
+ CRUSH rule 4 x 821 [5]
+ CRUSH rule 4 x 822 [2]
+ CRUSH rule 4 x 823 [5,8]
+ CRUSH rule 4 x 824 [5,7]
+ CRUSH rule 4 x 825 [2,8]
+ CRUSH rule 4 x 826 [7,2]
+ CRUSH rule 4 x 827 [2,8]
+ CRUSH rule 4 x 828 [2,5]
+ CRUSH rule 4 x 829 [5,6]
+ CRUSH rule 4 x 830 [2,5]
+ CRUSH rule 4 x 831 [2,6]
+ CRUSH rule 4 x 832 [5]
+ CRUSH rule 4 x 833 [2]
+ CRUSH rule 4 x 834 [5]
+ CRUSH rule 4 x 835 [8,5]
+ CRUSH rule 4 x 836 [5]
+ CRUSH rule 4 x 837 [6,5]
+ CRUSH rule 4 x 838 [6,7]
+ CRUSH rule 4 x 839 [5,2]
+ CRUSH rule 4 x 840 [7,8]
+ CRUSH rule 4 x 841 [5,8]
+ CRUSH rule 4 x 842 [2,5]
+ CRUSH rule 4 x 843 [6,5]
+ CRUSH rule 4 x 844 [5,8]
+ CRUSH rule 4 x 845 [5,8]
+ CRUSH rule 4 x 846 [5,2]
+ CRUSH rule 4 x 847 [2]
+ CRUSH rule 4 x 848 [2,6]
+ CRUSH rule 4 x 849 [5]
+ CRUSH rule 4 x 850 [2]
+ CRUSH rule 4 x 851 [6,8]
+ CRUSH rule 4 x 852 [7,5]
+ CRUSH rule 4 x 853 [6,8]
+ CRUSH rule 4 x 854 [7,6]
+ CRUSH rule 4 x 855 [5,7]
+ CRUSH rule 4 x 856 [6,7]
+ CRUSH rule 4 x 857 [8,5]
+ CRUSH rule 4 x 858 [6,5]
+ CRUSH rule 4 x 859 [6,2]
+ CRUSH rule 4 x 860 [5,2]
+ CRUSH rule 4 x 861 [8,7]
+ CRUSH rule 4 x 862 [6,2]
+ CRUSH rule 4 x 863 [8,7]
+ CRUSH rule 4 x 864 [5,6]
+ CRUSH rule 4 x 865 [8,2]
+ CRUSH rule 4 x 866 [5]
+ CRUSH rule 4 x 867 [6,5]
+ CRUSH rule 4 x 868 [6,5]
+ CRUSH rule 4 x 869 [8,7]
+ CRUSH rule 4 x 870 [2,5]
+ CRUSH rule 4 x 871 [5]
+ CRUSH rule 4 x 872 [5,2]
+ CRUSH rule 4 x 873 [5,6]
+ CRUSH rule 4 x 874 [2,6]
+ CRUSH rule 4 x 875 [2,6]
+ CRUSH rule 4 x 876 [5,8]
+ CRUSH rule 4 x 877 [6,5]
+ CRUSH rule 4 x 878 [5]
+ CRUSH rule 4 x 879 [7,5]
+ CRUSH rule 4 x 880 [5]
+ CRUSH rule 4 x 881 [5,6]
+ CRUSH rule 4 x 882 [5,2]
+ CRUSH rule 4 x 883 [2]
+ CRUSH rule 4 x 884 [6,2]
+ CRUSH rule 4 x 885 [5,2]
+ CRUSH rule 4 x 886 [5,6]
+ CRUSH rule 4 x 887 [7,5]
+ CRUSH rule 4 x 888 [6,8]
+ CRUSH rule 4 x 889 [2]
+ CRUSH rule 4 x 890 [7,2]
+ CRUSH rule 4 x 891 [2,8]
+ CRUSH rule 4 x 892 [6,2]
+ CRUSH rule 4 x 893 [2,5]
+ CRUSH rule 4 x 894 [7,5]
+ CRUSH rule 4 x 895 [5]
+ CRUSH rule 4 x 896 [2,8]
+ CRUSH rule 4 x 897 [5,2]
+ CRUSH rule 4 x 898 [2,5]
+ CRUSH rule 4 x 899 [2,7]
+ CRUSH rule 4 x 900 [5,2]
+ CRUSH rule 4 x 901 [5,2]
+ CRUSH rule 4 x 902 [8,5]
+ CRUSH rule 4 x 903 [5,7]
+ CRUSH rule 4 x 904 [5,6]
+ CRUSH rule 4 x 905 [6,2]
+ CRUSH rule 4 x 906 [2]
+ CRUSH rule 4 x 907 [7,2]
+ CRUSH rule 4 x 908 [5,8]
+ CRUSH rule 4 x 909 [2,5]
+ CRUSH rule 4 x 910 [6,5]
+ CRUSH rule 4 x 911 [5,8]
+ CRUSH rule 4 x 912 [2]
+ CRUSH rule 4 x 913 [7,6]
+ CRUSH rule 4 x 914 [6,5]
+ CRUSH rule 4 x 915 [8,2]
+ CRUSH rule 4 x 916 [5,2]
+ CRUSH rule 4 x 917 [2,5]
+ CRUSH rule 4 x 918 [8,2]
+ CRUSH rule 4 x 919 [6,2]
+ CRUSH rule 4 x 920 [7,6]
+ CRUSH rule 4 x 921 [2,5]
+ CRUSH rule 4 x 922 [6,7]
+ CRUSH rule 4 x 923 [5]
+ CRUSH rule 4 x 924 [5]
+ CRUSH rule 4 x 925 [5,7]
+ CRUSH rule 4 x 926 [5]
+ CRUSH rule 4 x 927 [2,6]
+ CRUSH rule 4 x 928 [8,2]
+ CRUSH rule 4 x 929 [5]
+ CRUSH rule 4 x 930 [2,5]
+ CRUSH rule 4 x 931 [5,2]
+ CRUSH rule 4 x 932 [5]
+ CRUSH rule 4 x 933 [8,5]
+ CRUSH rule 4 x 934 [5]
+ CRUSH rule 4 x 935 [6,5]
+ CRUSH rule 4 x 936 [2,6]
+ CRUSH rule 4 x 937 [5]
+ CRUSH rule 4 x 938 [6,5]
+ CRUSH rule 4 x 939 [2,7]
+ CRUSH rule 4 x 940 [8,7]
+ CRUSH rule 4 x 941 [5,2]
+ CRUSH rule 4 x 942 [2]
+ CRUSH rule 4 x 943 [8,2]
+ CRUSH rule 4 x 944 [5]
+ CRUSH rule 4 x 945 [7,2]
+ CRUSH rule 4 x 946 [2]
+ CRUSH rule 4 x 947 [5]
+ CRUSH rule 4 x 948 [7,8]
+ CRUSH rule 4 x 949 [6,2]
+ CRUSH rule 4 x 950 [5]
+ CRUSH rule 4 x 951 [5]
+ CRUSH rule 4 x 952 [2]
+ CRUSH rule 4 x 953 [2,5]
+ CRUSH rule 4 x 954 [5,2]
+ CRUSH rule 4 x 955 [8,6]
+ CRUSH rule 4 x 956 [2]
+ CRUSH rule 4 x 957 [7,6]
+ CRUSH rule 4 x 958 [8,7]
+ CRUSH rule 4 x 959 [5,2]
+ CRUSH rule 4 x 960 [5,6]
+ CRUSH rule 4 x 961 [5,2]
+ CRUSH rule 4 x 962 [7,5]
+ CRUSH rule 4 x 963 [2,5]
+ CRUSH rule 4 x 964 [5,2]
+ CRUSH rule 4 x 965 [7,6]
+ CRUSH rule 4 x 966 [5,8]
+ CRUSH rule 4 x 967 [8,6]
+ CRUSH rule 4 x 968 [7,2]
+ CRUSH rule 4 x 969 [8,2]
+ CRUSH rule 4 x 970 [2,6]
+ CRUSH rule 4 x 971 [2,7]
+ CRUSH rule 4 x 972 [2,8]
+ CRUSH rule 4 x 973 [2]
+ CRUSH rule 4 x 974 [5]
+ CRUSH rule 4 x 975 [5,7]
+ CRUSH rule 4 x 976 [5]
+ CRUSH rule 4 x 977 [8,5]
+ CRUSH rule 4 x 978 [7,2]
+ CRUSH rule 4 x 979 [7,6]
+ CRUSH rule 4 x 980 [6,2]
+ CRUSH rule 4 x 981 [7,5]
+ CRUSH rule 4 x 982 [5,2]
+ CRUSH rule 4 x 983 [5]
+ CRUSH rule 4 x 984 [2]
+ CRUSH rule 4 x 985 [2,5]
+ CRUSH rule 4 x 986 [8,7]
+ CRUSH rule 4 x 987 [2,5]
+ CRUSH rule 4 x 988 [2,5]
+ CRUSH rule 4 x 989 [2,6]
+ CRUSH rule 4 x 990 [2]
+ CRUSH rule 4 x 991 [2,5]
+ CRUSH rule 4 x 992 [7,2]
+ CRUSH rule 4 x 993 [2,6]
+ CRUSH rule 4 x 994 [5]
+ CRUSH rule 4 x 995 [7,6]
+ CRUSH rule 4 x 996 [6,7]
+ CRUSH rule 4 x 997 [6,5]
+ CRUSH rule 4 x 998 [8,2]
+ CRUSH rule 4 x 999 [2,7]
+ CRUSH rule 4 x 1000 [8,5]
+ CRUSH rule 4 x 1001 [2]
+ CRUSH rule 4 x 1002 [2,5]
+ CRUSH rule 4 x 1003 [2,8]
+ CRUSH rule 4 x 1004 [6,2]
+ CRUSH rule 4 x 1005 [6,2]
+ CRUSH rule 4 x 1006 [2]
+ CRUSH rule 4 x 1007 [2]
+ CRUSH rule 4 x 1008 [2,7]
+ CRUSH rule 4 x 1009 [6,8]
+ CRUSH rule 4 x 1010 [5]
+ CRUSH rule 4 x 1011 [5,2]
+ CRUSH rule 4 x 1012 [5,2]
+ CRUSH rule 4 x 1013 [5,2]
+ CRUSH rule 4 x 1014 [2,8]
+ CRUSH rule 4 x 1015 [6,8]
+ CRUSH rule 4 x 1016 [2]
+ CRUSH rule 4 x 1017 [6,2]
+ CRUSH rule 4 x 1018 [5]
+ CRUSH rule 4 x 1019 [5]
+ CRUSH rule 4 x 1020 [5,2]
+ CRUSH rule 4 x 1021 [5,2]
+ CRUSH rule 4 x 1022 [2,6]
+ CRUSH rule 4 x 1023 [5,2]
+ rule 4 (choose-set-two) num_rep 2 result size == 1:\t230/1024 (esc)
+ rule 4 (choose-set-two) num_rep 2 result size == 2:\t794/1024 (esc)
+ CRUSH rule 4 x 0 [2,5]
+ CRUSH rule 4 x 1 [2,8]
+ CRUSH rule 4 x 2 [2,5]
+ CRUSH rule 4 x 3 [8,2,6]
+ CRUSH rule 4 x 4 [5,2]
+ CRUSH rule 4 x 5 [7,8,2]
+ CRUSH rule 4 x 6 [2,6,7]
+ CRUSH rule 4 x 7 [5]
+ CRUSH rule 4 x 8 [5,8]
+ CRUSH rule 4 x 9 [2,5]
+ CRUSH rule 4 x 10 [2,8]
+ CRUSH rule 4 x 11 [2,7]
+ CRUSH rule 4 x 12 [2,5]
+ CRUSH rule 4 x 13 [5,8]
+ CRUSH rule 4 x 14 [7,6,8]
+ CRUSH rule 4 x 15 [7,2,5]
+ CRUSH rule 4 x 16 [5,6,2]
+ CRUSH rule 4 x 17 [5,2]
+ CRUSH rule 4 x 18 [2,5]
+ CRUSH rule 4 x 19 [7,5]
+ CRUSH rule 4 x 20 [2,5,7]
+ CRUSH rule 4 x 21 [5,7,2]
+ CRUSH rule 4 x 22 [8,5,7]
+ CRUSH rule 4 x 23 [5,6,8]
+ CRUSH rule 4 x 24 [2,6]
+ CRUSH rule 4 x 25 [5,7,2]
+ CRUSH rule 4 x 26 [2,8,7]
+ CRUSH rule 4 x 27 [5,2]
+ CRUSH rule 4 x 28 [6,2,5]
+ CRUSH rule 4 x 29 [8,5,7]
+ CRUSH rule 4 x 30 [5,7]
+ CRUSH rule 4 x 31 [8,7,6]
+ CRUSH rule 4 x 32 [5,6,8]
+ CRUSH rule 4 x 33 [2,7,8]
+ CRUSH rule 4 x 34 [2,5]
+ CRUSH rule 4 x 35 [2,8]
+ CRUSH rule 4 x 36 [5,8,2]
+ CRUSH rule 4 x 37 [2,5]
+ CRUSH rule 4 x 38 [5,8,7]
+ CRUSH rule 4 x 39 [5,7,2]
+ CRUSH rule 4 x 40 [7,8,2]
+ CRUSH rule 4 x 41 [2,6]
+ CRUSH rule 4 x 42 [5,6]
+ CRUSH rule 4 x 43 [2,5,7]
+ CRUSH rule 4 x 44 [2,6,7]
+ CRUSH rule 4 x 45 [8,2,5]
+ CRUSH rule 4 x 46 [2,5]
+ CRUSH rule 4 x 47 [5]
+ CRUSH rule 4 x 48 [5,6]
+ CRUSH rule 4 x 49 [5,8]
+ CRUSH rule 4 x 50 [5,2]
+ CRUSH rule 4 x 51 [5,6,2]
+ CRUSH rule 4 x 52 [8,6,2]
+ CRUSH rule 4 x 53 [5]
+ CRUSH rule 4 x 54 [7,6,8]
+ CRUSH rule 4 x 55 [8,7,2]
+ CRUSH rule 4 x 56 [6,5,8]
+ CRUSH rule 4 x 57 [5,6]
+ CRUSH rule 4 x 58 [2]
+ CRUSH rule 4 x 59 [5,2]
+ CRUSH rule 4 x 60 [5,2]
+ CRUSH rule 4 x 61 [5,6,8]
+ CRUSH rule 4 x 62 [7,2]
+ CRUSH rule 4 x 63 [5,6,8]
+ CRUSH rule 4 x 64 [5,2]
+ CRUSH rule 4 x 65 [7,5,6]
+ CRUSH rule 4 x 66 [5]
+ CRUSH rule 4 x 67 [5,2,8]
+ CRUSH rule 4 x 68 [2,5,8]
+ CRUSH rule 4 x 69 [5,2,6]
+ CRUSH rule 4 x 70 [7,2]
+ CRUSH rule 4 x 71 [2,8,6]
+ CRUSH rule 4 x 72 [6,2,5]
+ CRUSH rule 4 x 73 [2,7,5]
+ CRUSH rule 4 x 74 [2,7]
+ CRUSH rule 4 x 75 [5,2]
+ CRUSH rule 4 x 76 [5,2,7]
+ CRUSH rule 4 x 77 [7,2,6]
+ CRUSH rule 4 x 78 [2,5]
+ CRUSH rule 4 x 79 [5,2]
+ CRUSH rule 4 x 80 [2,5]
+ CRUSH rule 4 x 81 [2,5]
+ CRUSH rule 4 x 82 [7,2,6]
+ CRUSH rule 4 x 83 [2,6,8]
+ CRUSH rule 4 x 84 [7,2]
+ CRUSH rule 4 x 85 [5,8]
+ CRUSH rule 4 x 86 [2]
+ CRUSH rule 4 x 87 [2,7,5]
+ CRUSH rule 4 x 88 [2,6]
+ CRUSH rule 4 x 89 [5,2]
+ CRUSH rule 4 x 90 [6,7,5]
+ CRUSH rule 4 x 91 [5,8,7]
+ CRUSH rule 4 x 92 [2,8,5]
+ CRUSH rule 4 x 93 [7,5]
+ CRUSH rule 4 x 94 [2,5]
+ CRUSH rule 4 x 95 [7,5,2]
+ CRUSH rule 4 x 96 [5,6]
+ CRUSH rule 4 x 97 [8,7,5]
+ CRUSH rule 4 x 98 [2,7]
+ CRUSH rule 4 x 99 [2,7]
+ CRUSH rule 4 x 100 [2,7,5]
+ CRUSH rule 4 x 101 [5,7,2]
+ CRUSH rule 4 x 102 [5,2,7]
+ CRUSH rule 4 x 103 [5,7,6]
+ CRUSH rule 4 x 104 [7,5]
+ CRUSH rule 4 x 105 [2,5]
+ CRUSH rule 4 x 106 [2,6]
+ CRUSH rule 4 x 107 [5,2]
+ CRUSH rule 4 x 108 [7,2,8]
+ CRUSH rule 4 x 109 [2,5]
+ CRUSH rule 4 x 110 [5,2,7]
+ CRUSH rule 4 x 111 [2]
+ CRUSH rule 4 x 112 [2,6]
+ CRUSH rule 4 x 113 [6,2]
+ CRUSH rule 4 x 114 [7,6,5]
+ CRUSH rule 4 x 115 [8,2,5]
+ CRUSH rule 4 x 116 [2,6]
+ CRUSH rule 4 x 117 [7,5,6]
+ CRUSH rule 4 x 118 [2,5,8]
+ CRUSH rule 4 x 119 [5,6,2]
+ CRUSH rule 4 x 120 [2,5]
+ CRUSH rule 4 x 121 [2]
+ CRUSH rule 4 x 122 [8,5,2]
+ CRUSH rule 4 x 123 [2,5]
+ CRUSH rule 4 x 124 [5,2]
+ CRUSH rule 4 x 125 [2,7,5]
+ CRUSH rule 4 x 126 [5,2,6]
+ CRUSH rule 4 x 127 [5,6,7]
+ CRUSH rule 4 x 128 [5,8]
+ CRUSH rule 4 x 129 [2]
+ CRUSH rule 4 x 130 [5,8]
+ CRUSH rule 4 x 131 [2]
+ CRUSH rule 4 x 132 [2,5]
+ CRUSH rule 4 x 133 [5,6,7]
+ CRUSH rule 4 x 134 [2,8]
+ CRUSH rule 4 x 135 [5,6]
+ CRUSH rule 4 x 136 [2,5]
+ CRUSH rule 4 x 137 [7,5]
+ CRUSH rule 4 x 138 [8,7,6]
+ CRUSH rule 4 x 139 [5,2]
+ CRUSH rule 4 x 140 [2,6,7]
+ CRUSH rule 4 x 141 [6,8,2]
+ CRUSH rule 4 x 142 [5,2]
+ CRUSH rule 4 x 143 [5,8,7]
+ CRUSH rule 4 x 144 [8,2]
+ CRUSH rule 4 x 145 [8,5,6]
+ CRUSH rule 4 x 146 [2,6,5]
+ CRUSH rule 4 x 147 [2,8,7]
+ CRUSH rule 4 x 148 [5,2]
+ CRUSH rule 4 x 149 [5,8,6]
+ CRUSH rule 4 x 150 [2,6,5]
+ CRUSH rule 4 x 151 [5]
+ CRUSH rule 4 x 152 [8,5,6]
+ CRUSH rule 4 x 153 [8,6,7]
+ CRUSH rule 4 x 154 [5,2]
+ CRUSH rule 4 x 155 [5,8]
+ CRUSH rule 4 x 156 [5]
+ CRUSH rule 4 x 157 [5,2]
+ CRUSH rule 4 x 158 [2,8]
+ CRUSH rule 4 x 159 [7,2,5]
+ CRUSH rule 4 x 160 [2,8]
+ CRUSH rule 4 x 161 [2,5]
+ CRUSH rule 4 x 162 [2,6]
+ CRUSH rule 4 x 163 [5,6,2]
+ CRUSH rule 4 x 164 [7,8,2]
+ CRUSH rule 4 x 165 [7,2,5]
+ CRUSH rule 4 x 166 [2,5]
+ CRUSH rule 4 x 167 [2,6]
+ CRUSH rule 4 x 168 [5,2]
+ CRUSH rule 4 x 169 [2,6]
+ CRUSH rule 4 x 170 [2,5]
+ CRUSH rule 4 x 171 [7,5]
+ CRUSH rule 4 x 172 [2,7,8]
+ CRUSH rule 4 x 173 [8,5]
+ CRUSH rule 4 x 174 [2,5,7]
+ CRUSH rule 4 x 175 [6,2,5]
+ CRUSH rule 4 x 176 [5]
+ CRUSH rule 4 x 177 [5,2]
+ CRUSH rule 4 x 178 [5,2]
+ CRUSH rule 4 x 179 [5,2]
+ CRUSH rule 4 x 180 [5,8,7]
+ CRUSH rule 4 x 181 [6,2]
+ CRUSH rule 4 x 182 [8,5]
+ CRUSH rule 4 x 183 [7,8,6]
+ CRUSH rule 4 x 184 [5,7]
+ CRUSH rule 4 x 185 [6,8,2]
+ CRUSH rule 4 x 186 [2,5]
+ CRUSH rule 4 x 187 [2,6,7]
+ CRUSH rule 4 x 188 [2,8,7]
+ CRUSH rule 4 x 189 [2,7,8]
+ CRUSH rule 4 x 190 [5,2]
+ CRUSH rule 4 x 191 [7,6,8]
+ CRUSH rule 4 x 192 [5,2]
+ CRUSH rule 4 x 193 [5,2,6]
+ CRUSH rule 4 x 194 [2,5]
+ CRUSH rule 4 x 195 [6,5]
+ CRUSH rule 4 x 196 [6,7,2]
+ CRUSH rule 4 x 197 [6,5,2]
+ CRUSH rule 4 x 198 [2,5,6]
+ CRUSH rule 4 x 199 [2,5,7]
+ CRUSH rule 4 x 200 [2,5]
+ CRUSH rule 4 x 201 [7,2]
+ CRUSH rule 4 x 202 [6,5]
+ CRUSH rule 4 x 203 [5,8,7]
+ CRUSH rule 4 x 204 [2,5]
+ CRUSH rule 4 x 205 [2,7,6]
+ CRUSH rule 4 x 206 [2,7]
+ CRUSH rule 4 x 207 [5,2]
+ CRUSH rule 4 x 208 [7,2]
+ CRUSH rule 4 x 209 [2]
+ CRUSH rule 4 x 210 [2,5]
+ CRUSH rule 4 x 211 [5,2]
+ CRUSH rule 4 x 212 [7,5,2]
+ CRUSH rule 4 x 213 [8,5,2]
+ CRUSH rule 4 x 214 [5,7]
+ CRUSH rule 4 x 215 [8,2,7]
+ CRUSH rule 4 x 216 [5,2]
+ CRUSH rule 4 x 217 [2,7]
+ CRUSH rule 4 x 218 [2,7,8]
+ CRUSH rule 4 x 219 [5,8,2]
+ CRUSH rule 4 x 220 [5,7]
+ CRUSH rule 4 x 221 [5,6]
+ CRUSH rule 4 x 222 [6,8,5]
+ CRUSH rule 4 x 223 [2,5,6]
+ CRUSH rule 4 x 224 [2,5]
+ CRUSH rule 4 x 225 [8,6,2]
+ CRUSH rule 4 x 226 [7,2,5]
+ CRUSH rule 4 x 227 [5,2]
+ CRUSH rule 4 x 228 [5,8]
+ CRUSH rule 4 x 229 [5,7]
+ CRUSH rule 4 x 230 [5,7,2]
+ CRUSH rule 4 x 231 [5]
+ CRUSH rule 4 x 232 [2,7,5]
+ CRUSH rule 4 x 233 [5]
+ CRUSH rule 4 x 234 [2]
+ CRUSH rule 4 x 235 [5,8,2]
+ CRUSH rule 4 x 236 [5,2,7]
+ CRUSH rule 4 x 237 [5,7,6]
+ CRUSH rule 4 x 238 [5]
+ CRUSH rule 4 x 239 [8,7,6]
+ CRUSH rule 4 x 240 [5,7]
+ CRUSH rule 4 x 241 [5,2]
+ CRUSH rule 4 x 242 [5]
+ CRUSH rule 4 x 243 [5,7]
+ CRUSH rule 4 x 244 [5,6,2]
+ CRUSH rule 4 x 245 [7,6,2]
+ CRUSH rule 4 x 246 [2,5]
+ CRUSH rule 4 x 247 [6,2]
+ CRUSH rule 4 x 248 [8,2,7]
+ CRUSH rule 4 x 249 [2]
+ CRUSH rule 4 x 250 [2]
+ CRUSH rule 4 x 251 [2,5,6]
+ CRUSH rule 4 x 252 [5,7,8]
+ CRUSH rule 4 x 253 [5,2]
+ CRUSH rule 4 x 254 [5]
+ CRUSH rule 4 x 255 [2,7,8]
+ CRUSH rule 4 x 256 [5,7,2]
+ CRUSH rule 4 x 257 [2,8,5]
+ CRUSH rule 4 x 258 [5,2]
+ CRUSH rule 4 x 259 [5,6]
+ CRUSH rule 4 x 260 [5,6,2]
+ CRUSH rule 4 x 261 [8,7,5]
+ CRUSH rule 4 x 262 [5,8]
+ CRUSH rule 4 x 263 [6,8,2]
+ CRUSH rule 4 x 264 [5,6]
+ CRUSH rule 4 x 265 [8,6,7]
+ CRUSH rule 4 x 266 [8,2]
+ CRUSH rule 4 x 267 [2,5]
+ CRUSH rule 4 x 268 [2,7]
+ CRUSH rule 4 x 269 [2,8]
+ CRUSH rule 4 x 270 [5,2]
+ CRUSH rule 4 x 271 [7,5]
+ CRUSH rule 4 x 272 [2,8,5]
+ CRUSH rule 4 x 273 [5,2]
+ CRUSH rule 4 x 274 [6,8,5]
+ CRUSH rule 4 x 275 [5,6]
+ CRUSH rule 4 x 276 [7,2]
+ CRUSH rule 4 x 277 [6,5,2]
+ CRUSH rule 4 x 278 [6,8,2]
+ CRUSH rule 4 x 279 [8,5,6]
+ CRUSH rule 4 x 280 [2,6]
+ CRUSH rule 4 x 281 [8,2]
+ CRUSH rule 4 x 282 [5,2,6]
+ CRUSH rule 4 x 283 [8,2]
+ CRUSH rule 4 x 284 [6,5,2]
+ CRUSH rule 4 x 285 [5,7]
+ CRUSH rule 4 x 286 [2,6]
+ CRUSH rule 4 x 287 [2,5]
+ CRUSH rule 4 x 288 [8,2,7]
+ CRUSH rule 4 x 289 [5,6,2]
+ CRUSH rule 4 x 290 [2,5,7]
+ CRUSH rule 4 x 291 [2,5]
+ CRUSH rule 4 x 292 [8,2]
+ CRUSH rule 4 x 293 [6,2,8]
+ CRUSH rule 4 x 294 [7,5]
+ CRUSH rule 4 x 295 [5,8,7]
+ CRUSH rule 4 x 296 [5,2]
+ CRUSH rule 4 x 297 [6,2,8]
+ CRUSH rule 4 x 298 [2,5]
+ CRUSH rule 4 x 299 [2,7]
+ CRUSH rule 4 x 300 [8,7,5]
+ CRUSH rule 4 x 301 [2,8]
+ CRUSH rule 4 x 302 [5,2,6]
+ CRUSH rule 4 x 303 [7,5,8]
+ CRUSH rule 4 x 304 [2,7,5]
+ CRUSH rule 4 x 305 [5,8,2]
+ CRUSH rule 4 x 306 [2,7]
+ CRUSH rule 4 x 307 [2,8]
+ CRUSH rule 4 x 308 [2,8,5]
+ CRUSH rule 4 x 309 [7,5]
+ CRUSH rule 4 x 310 [5,2]
+ CRUSH rule 4 x 311 [5]
+ CRUSH rule 4 x 312 [2]
+ CRUSH rule 4 x 313 [5]
+ CRUSH rule 4 x 314 [5,2]
+ CRUSH rule 4 x 315 [2]
+ CRUSH rule 4 x 316 [6,5,8]
+ CRUSH rule 4 x 317 [2,6]
+ CRUSH rule 4 x 318 [8,6,7]
+ CRUSH rule 4 x 319 [5,2,8]
+ CRUSH rule 4 x 320 [5,7,2]
+ CRUSH rule 4 x 321 [2,5]
+ CRUSH rule 4 x 322 [2,7,5]
+ CRUSH rule 4 x 323 [5,7,2]
+ CRUSH rule 4 x 324 [7,2,8]
+ CRUSH rule 4 x 325 [5,6,2]
+ CRUSH rule 4 x 326 [5,2]
+ CRUSH rule 4 x 327 [2,6,7]
+ CRUSH rule 4 x 328 [7,5,8]
+ CRUSH rule 4 x 329 [5,6]
+ CRUSH rule 4 x 330 [5,7]
+ CRUSH rule 4 x 331 [2,6,5]
+ CRUSH rule 4 x 332 [2,5]
+ CRUSH rule 4 x 333 [6,8,5]
+ CRUSH rule 4 x 334 [8,5]
+ CRUSH rule 4 x 335 [7,2]
+ CRUSH rule 4 x 336 [5,6,2]
+ CRUSH rule 4 x 337 [7,2,6]
+ CRUSH rule 4 x 338 [5,6]
+ CRUSH rule 4 x 339 [7,5,2]
+ CRUSH rule 4 x 340 [2]
+ CRUSH rule 4 x 341 [5,2,7]
+ CRUSH rule 4 x 342 [2,7,5]
+ CRUSH rule 4 x 343 [6,7,5]
+ CRUSH rule 4 x 344 [6,2,5]
+ CRUSH rule 4 x 345 [5]
+ CRUSH rule 4 x 346 [8,2,5]
+ CRUSH rule 4 x 347 [5,2]
+ CRUSH rule 4 x 348 [8,2]
+ CRUSH rule 4 x 349 [2,6,7]
+ CRUSH rule 4 x 350 [8,5,7]
+ CRUSH rule 4 x 351 [5,6]
+ CRUSH rule 4 x 352 [2]
+ CRUSH rule 4 x 353 [6,5]
+ CRUSH rule 4 x 354 [2,5]
+ CRUSH rule 4 x 355 [5,6]
+ CRUSH rule 4 x 356 [5]
+ CRUSH rule 4 x 357 [6,2]
+ CRUSH rule 4 x 358 [2,8]
+ CRUSH rule 4 x 359 [6,7,8]
+ CRUSH rule 4 x 360 [5,2]
+ CRUSH rule 4 x 361 [8,5]
+ CRUSH rule 4 x 362 [5]
+ CRUSH rule 4 x 363 [5,2]
+ CRUSH rule 4 x 364 [2,5]
+ CRUSH rule 4 x 365 [6,7,8]
+ CRUSH rule 4 x 366 [7,2,8]
+ CRUSH rule 4 x 367 [5,2]
+ CRUSH rule 4 x 368 [7,5,6]
+ CRUSH rule 4 x 369 [5,7,2]
+ CRUSH rule 4 x 370 [8,7,6]
+ CRUSH rule 4 x 371 [2,5]
+ CRUSH rule 4 x 372 [5,2,8]
+ CRUSH rule 4 x 373 [2,6,8]
+ CRUSH rule 4 x 374 [5,8]
+ CRUSH rule 4 x 375 [6,5]
+ CRUSH rule 4 x 376 [7,2]
+ CRUSH rule 4 x 377 [2,5]
+ CRUSH rule 4 x 378 [2]
+ CRUSH rule 4 x 379 [8,5]
+ CRUSH rule 4 x 380 [2,5]
+ CRUSH rule 4 x 381 [2,5,7]
+ CRUSH rule 4 x 382 [2,5]
+ CRUSH rule 4 x 383 [5]
+ CRUSH rule 4 x 384 [7,2,6]
+ CRUSH rule 4 x 385 [7,5,6]
+ CRUSH rule 4 x 386 [2,5]
+ CRUSH rule 4 x 387 [2,5,6]
+ CRUSH rule 4 x 388 [5,2]
+ CRUSH rule 4 x 389 [2,5]
+ CRUSH rule 4 x 390 [5,6,2]
+ CRUSH rule 4 x 391 [5,6,2]
+ CRUSH rule 4 x 392 [2,8,6]
+ CRUSH rule 4 x 393 [5,2,6]
+ CRUSH rule 4 x 394 [5,7,6]
+ CRUSH rule 4 x 395 [5,2]
+ CRUSH rule 4 x 396 [5,2]
+ CRUSH rule 4 x 397 [2,5]
+ CRUSH rule 4 x 398 [2,5]
+ CRUSH rule 4 x 399 [8,7,5]
+ CRUSH rule 4 x 400 [8,2,5]
+ CRUSH rule 4 x 401 [2]
+ CRUSH rule 4 x 402 [7,8,6]
+ CRUSH rule 4 x 403 [2,5]
+ CRUSH rule 4 x 404 [5,2]
+ CRUSH rule 4 x 405 [6,5,2]
+ CRUSH rule 4 x 406 [2]
+ CRUSH rule 4 x 407 [2,8,7]
+ CRUSH rule 4 x 408 [5,2,6]
+ CRUSH rule 4 x 409 [7,5,6]
+ CRUSH rule 4 x 410 [8,6,5]
+ CRUSH rule 4 x 411 [2,6]
+ CRUSH rule 4 x 412 [2,5,8]
+ CRUSH rule 4 x 413 [5,2,8]
+ CRUSH rule 4 x 414 [5,2]
+ CRUSH rule 4 x 415 [2,6]
+ CRUSH rule 4 x 416 [2,7]
+ CRUSH rule 4 x 417 [8,7,2]
+ CRUSH rule 4 x 418 [7,6,8]
+ CRUSH rule 4 x 419 [8,5,2]
+ CRUSH rule 4 x 420 [2,5]
+ CRUSH rule 4 x 421 [8,6,7]
+ CRUSH rule 4 x 422 [6,7,8]
+ CRUSH rule 4 x 423 [2,5,6]
+ CRUSH rule 4 x 424 [8,5,7]
+ CRUSH rule 4 x 425 [2,5]
+ CRUSH rule 4 x 426 [6,7,2]
+ CRUSH rule 4 x 427 [2,7,5]
+ CRUSH rule 4 x 428 [5]
+ CRUSH rule 4 x 429 [5,6,7]
+ CRUSH rule 4 x 430 [5,6]
+ CRUSH rule 4 x 431 [5,2]
+ CRUSH rule 4 x 432 [7,2]
+ CRUSH rule 4 x 433 [6,5,2]
+ CRUSH rule 4 x 434 [5,2]
+ CRUSH rule 4 x 435 [2,5]
+ CRUSH rule 4 x 436 [5,2]
+ CRUSH rule 4 x 437 [7,5]
+ CRUSH rule 4 x 438 [2,5,8]
+ CRUSH rule 4 x 439 [2,5]
+ CRUSH rule 4 x 440 [2,7]
+ CRUSH rule 4 x 441 [5,7,2]
+ CRUSH rule 4 x 442 [2,5]
+ CRUSH rule 4 x 443 [6,8,2]
+ CRUSH rule 4 x 444 [7,2]
+ CRUSH rule 4 x 445 [6,5]
+ CRUSH rule 4 x 446 [5]
+ CRUSH rule 4 x 447 [2,5]
+ CRUSH rule 4 x 448 [7,2,5]
+ CRUSH rule 4 x 449 [7,8,5]
+ CRUSH rule 4 x 450 [5,2]
+ CRUSH rule 4 x 451 [6,8,5]
+ CRUSH rule 4 x 452 [8,5]
+ CRUSH rule 4 x 453 [6,8,7]
+ CRUSH rule 4 x 454 [6,7,5]
+ CRUSH rule 4 x 455 [2,7,5]
+ CRUSH rule 4 x 456 [6,8,7]
+ CRUSH rule 4 x 457 [7,2,8]
+ CRUSH rule 4 x 458 [2,8]
+ CRUSH rule 4 x 459 [2,6]
+ CRUSH rule 4 x 460 [6,5,7]
+ CRUSH rule 4 x 461 [6,5]
+ CRUSH rule 4 x 462 [8,2,5]
+ CRUSH rule 4 x 463 [6,7,2]
+ CRUSH rule 4 x 464 [7,5,2]
+ CRUSH rule 4 x 465 [7,6,2]
+ CRUSH rule 4 x 466 [5,8]
+ CRUSH rule 4 x 467 [6,5,7]
+ CRUSH rule 4 x 468 [7,8,2]
+ CRUSH rule 4 x 469 [7,2]
+ CRUSH rule 4 x 470 [5,2]
+ CRUSH rule 4 x 471 [2]
+ CRUSH rule 4 x 472 [5,2]
+ CRUSH rule 4 x 473 [2,5]
+ CRUSH rule 4 x 474 [6,2,7]
+ CRUSH rule 4 x 475 [6,7,8]
+ CRUSH rule 4 x 476 [5]
+ CRUSH rule 4 x 477 [5,8,6]
+ CRUSH rule 4 x 478 [6,7,2]
+ CRUSH rule 4 x 479 [2,5,8]
+ CRUSH rule 4 x 480 [2,8,6]
+ CRUSH rule 4 x 481 [2,5]
+ CRUSH rule 4 x 482 [5]
+ CRUSH rule 4 x 483 [2,6]
+ CRUSH rule 4 x 484 [2,7]
+ CRUSH rule 4 x 485 [5,7]
+ CRUSH rule 4 x 486 [5,2,7]
+ CRUSH rule 4 x 487 [5,2]
+ CRUSH rule 4 x 488 [5,7,2]
+ CRUSH rule 4 x 489 [2,8,5]
+ CRUSH rule 4 x 490 [6,5,2]
+ CRUSH rule 4 x 491 [2,6]
+ CRUSH rule 4 x 492 [6,5]
+ CRUSH rule 4 x 493 [2,7]
+ CRUSH rule 4 x 494 [2,8]
+ CRUSH rule 4 x 495 [5]
+ CRUSH rule 4 x 496 [7,5]
+ CRUSH rule 4 x 497 [5,7]
+ CRUSH rule 4 x 498 [2,5,8]
+ CRUSH rule 4 x 499 [8,5]
+ CRUSH rule 4 x 500 [5,6]
+ CRUSH rule 4 x 501 [2,7]
+ CRUSH rule 4 x 502 [7,2,5]
+ CRUSH rule 4 x 503 [2,5]
+ CRUSH rule 4 x 504 [5,6]
+ CRUSH rule 4 x 505 [2,7,6]
+ CRUSH rule 4 x 506 [5,2]
+ CRUSH rule 4 x 507 [6,2,8]
+ CRUSH rule 4 x 508 [2]
+ CRUSH rule 4 x 509 [7,5,8]
+ CRUSH rule 4 x 510 [6,2]
+ CRUSH rule 4 x 511 [5,8,7]
+ CRUSH rule 4 x 512 [7,6,2]
+ CRUSH rule 4 x 513 [7,2]
+ CRUSH rule 4 x 514 [5]
+ CRUSH rule 4 x 515 [8,5,6]
+ CRUSH rule 4 x 516 [5,2]
+ CRUSH rule 4 x 517 [7,8,6]
+ CRUSH rule 4 x 518 [5,6,7]
+ CRUSH rule 4 x 519 [7,5]
+ CRUSH rule 4 x 520 [2,6,5]
+ CRUSH rule 4 x 521 [8,7,6]
+ CRUSH rule 4 x 522 [6,8,2]
+ CRUSH rule 4 x 523 [5,2]
+ CRUSH rule 4 x 524 [2,5]
+ CRUSH rule 4 x 525 [2,5]
+ CRUSH rule 4 x 526 [2,5,8]
+ CRUSH rule 4 x 527 [2,5]
+ CRUSH rule 4 x 528 [5,2]
+ CRUSH rule 4 x 529 [5,7,2]
+ CRUSH rule 4 x 530 [6,7,8]
+ CRUSH rule 4 x 531 [6,2]
+ CRUSH rule 4 x 532 [6,5,7]
+ CRUSH rule 4 x 533 [5,6]
+ CRUSH rule 4 x 534 [7,5]
+ CRUSH rule 4 x 535 [8,6,2]
+ CRUSH rule 4 x 536 [6,7,2]
+ CRUSH rule 4 x 537 [5,7]
+ CRUSH rule 4 x 538 [6,8,5]
+ CRUSH rule 4 x 539 [8,5,7]
+ CRUSH rule 4 x 540 [2,6]
+ CRUSH rule 4 x 541 [2,5]
+ CRUSH rule 4 x 542 [5]
+ CRUSH rule 4 x 543 [6,2,8]
+ CRUSH rule 4 x 544 [5,7,6]
+ CRUSH rule 4 x 545 [5,7,2]
+ CRUSH rule 4 x 546 [6,2,7]
+ CRUSH rule 4 x 547 [8,2]
+ CRUSH rule 4 x 548 [5,2]
+ CRUSH rule 4 x 549 [5,8,2]
+ CRUSH rule 4 x 550 [2,5]
+ CRUSH rule 4 x 551 [7,5]
+ CRUSH rule 4 x 552 [5]
+ CRUSH rule 4 x 553 [5,2]
+ CRUSH rule 4 x 554 [2,8,5]
+ CRUSH rule 4 x 555 [5,2,8]
+ CRUSH rule 4 x 556 [5]
+ CRUSH rule 4 x 557 [7,5,6]
+ CRUSH rule 4 x 558 [5,2]
+ CRUSH rule 4 x 559 [5,2,6]
+ CRUSH rule 4 x 560 [8,5]
+ CRUSH rule 4 x 561 [6,5,7]
+ CRUSH rule 4 x 562 [5,2]
+ CRUSH rule 4 x 563 [2,6]
+ CRUSH rule 4 x 564 [5,2,7]
+ CRUSH rule 4 x 565 [5,6]
+ CRUSH rule 4 x 566 [5,7,2]
+ CRUSH rule 4 x 567 [5,6,2]
+ CRUSH rule 4 x 568 [7,5,2]
+ CRUSH rule 4 x 569 [5,2]
+ CRUSH rule 4 x 570 [2,5]
+ CRUSH rule 4 x 571 [5,7]
+ CRUSH rule 4 x 572 [5,2]
+ CRUSH rule 4 x 573 [5,2,7]
+ CRUSH rule 4 x 574 [2]
+ CRUSH rule 4 x 575 [8,6,2]
+ CRUSH rule 4 x 576 [5,6,8]
+ CRUSH rule 4 x 577 [8,2,6]
+ CRUSH rule 4 x 578 [6,8,7]
+ CRUSH rule 4 x 579 [5,2,6]
+ CRUSH rule 4 x 580 [5,2,7]
+ CRUSH rule 4 x 581 [7,2,6]
+ CRUSH rule 4 x 582 [2,8,7]
+ CRUSH rule 4 x 583 [6,2,8]
+ CRUSH rule 4 x 584 [8,2]
+ CRUSH rule 4 x 585 [7,2,5]
+ CRUSH rule 4 x 586 [2,7]
+ CRUSH rule 4 x 587 [2,5]
+ CRUSH rule 4 x 588 [5,7]
+ CRUSH rule 4 x 589 [7,2,5]
+ CRUSH rule 4 x 590 [6,2,5]
+ CRUSH rule 4 x 591 [5,2]
+ CRUSH rule 4 x 592 [2,5]
+ CRUSH rule 4 x 593 [2,8]
+ CRUSH rule 4 x 594 [2,7,5]
+ CRUSH rule 4 x 595 [7,2,8]
+ CRUSH rule 4 x 596 [5]
+ CRUSH rule 4 x 597 [5,2]
+ CRUSH rule 4 x 598 [5,2]
+ CRUSH rule 4 x 599 [5,2]
+ CRUSH rule 4 x 600 [7,2,8]
+ CRUSH rule 4 x 601 [2,7,8]
+ CRUSH rule 4 x 602 [5,7]
+ CRUSH rule 4 x 603 [5,2,6]
+ CRUSH rule 4 x 604 [7,5]
+ CRUSH rule 4 x 605 [5,2]
+ CRUSH rule 4 x 606 [2]
+ CRUSH rule 4 x 607 [2,5,8]
+ CRUSH rule 4 x 608 [5,2]
+ CRUSH rule 4 x 609 [5,2]
+ CRUSH rule 4 x 610 [5,7,6]
+ CRUSH rule 4 x 611 [2]
+ CRUSH rule 4 x 612 [2,8]
+ CRUSH rule 4 x 613 [7,2]
+ CRUSH rule 4 x 614 [7,8,6]
+ CRUSH rule 4 x 615 [6,8,2]
+ CRUSH rule 4 x 616 [2,8]
+ CRUSH rule 4 x 617 [6,2]
+ CRUSH rule 4 x 618 [7,6,5]
+ CRUSH rule 4 x 619 [5,2,8]
+ CRUSH rule 4 x 620 [5,2]
+ CRUSH rule 4 x 621 [5,8]
+ CRUSH rule 4 x 622 [2,5]
+ CRUSH rule 4 x 623 [2,6]
+ CRUSH rule 4 x 624 [5,2]
+ CRUSH rule 4 x 625 [2,5,7]
+ CRUSH rule 4 x 626 [7,8,2]
+ CRUSH rule 4 x 627 [2,7]
+ CRUSH rule 4 x 628 [8,2,5]
+ CRUSH rule 4 x 629 [2,6,5]
+ CRUSH rule 4 x 630 [2,7]
+ CRUSH rule 4 x 631 [2,7,8]
+ CRUSH rule 4 x 632 [7,2]
+ CRUSH rule 4 x 633 [8,6,5]
+ CRUSH rule 4 x 634 [2,5]
+ CRUSH rule 4 x 635 [5,6]
+ CRUSH rule 4 x 636 [2,5]
+ CRUSH rule 4 x 637 [5,2]
+ CRUSH rule 4 x 638 [6,8,2]
+ CRUSH rule 4 x 639 [5,2]
+ CRUSH rule 4 x 640 [5,2]
+ CRUSH rule 4 x 641 [7,2]
+ CRUSH rule 4 x 642 [2]
+ CRUSH rule 4 x 643 [5,2]
+ CRUSH rule 4 x 644 [8,2,5]
+ CRUSH rule 4 x 645 [5,6]
+ CRUSH rule 4 x 646 [8,2,5]
+ CRUSH rule 4 x 647 [7,2,5]
+ CRUSH rule 4 x 648 [2,6,5]
+ CRUSH rule 4 x 649 [5,7]
+ CRUSH rule 4 x 650 [7,8,6]
+ CRUSH rule 4 x 651 [5,7,6]
+ CRUSH rule 4 x 652 [5,8]
+ CRUSH rule 4 x 653 [8,5,2]
+ CRUSH rule 4 x 654 [7,5,2]
+ CRUSH rule 4 x 655 [2,5]
+ CRUSH rule 4 x 656 [5,8]
+ CRUSH rule 4 x 657 [6,2]
+ CRUSH rule 4 x 658 [5,6]
+ CRUSH rule 4 x 659 [5,6,7]
+ CRUSH rule 4 x 660 [7,8,6]
+ CRUSH rule 4 x 661 [2,8,5]
+ CRUSH rule 4 x 662 [5]
+ CRUSH rule 4 x 663 [2,5,8]
+ CRUSH rule 4 x 664 [2,5]
+ CRUSH rule 4 x 665 [5,7,6]
+ CRUSH rule 4 x 666 [2,8,5]
+ CRUSH rule 4 x 667 [2,5]
+ CRUSH rule 4 x 668 [5,7,6]
+ CRUSH rule 4 x 669 [6,5]
+ CRUSH rule 4 x 670 [5,2]
+ CRUSH rule 4 x 671 [2]
+ CRUSH rule 4 x 672 [5,2]
+ CRUSH rule 4 x 673 [5,2]
+ CRUSH rule 4 x 674 [5,2,8]
+ CRUSH rule 4 x 675 [2,8,6]
+ CRUSH rule 4 x 676 [2,5]
+ CRUSH rule 4 x 677 [5,2]
+ CRUSH rule 4 x 678 [2,5]
+ CRUSH rule 4 x 679 [6,2]
+ CRUSH rule 4 x 680 [2,5,6]
+ CRUSH rule 4 x 681 [5,7]
+ CRUSH rule 4 x 682 [2,5]
+ CRUSH rule 4 x 683 [2,5]
+ CRUSH rule 4 x 684 [7,2,6]
+ CRUSH rule 4 x 685 [7,2,6]
+ CRUSH rule 4 x 686 [2,5]
+ CRUSH rule 4 x 687 [5,7]
+ CRUSH rule 4 x 688 [5,7,2]
+ CRUSH rule 4 x 689 [6,5,2]
+ CRUSH rule 4 x 690 [8,2,7]
+ CRUSH rule 4 x 691 [5,2]
+ CRUSH rule 4 x 692 [7,2,8]
+ CRUSH rule 4 x 693 [6,7,5]
+ CRUSH rule 4 x 694 [6,5,2]
+ CRUSH rule 4 x 695 [2,8,7]
+ CRUSH rule 4 x 696 [2,5]
+ CRUSH rule 4 x 697 [6,2]
+ CRUSH rule 4 x 698 [6,2]
+ CRUSH rule 4 x 699 [2,6,8]
+ CRUSH rule 4 x 700 [2,5]
+ CRUSH rule 4 x 701 [5,2]
+ CRUSH rule 4 x 702 [5,2]
+ CRUSH rule 4 x 703 [8,5]
+ CRUSH rule 4 x 704 [2,5,8]
+ CRUSH rule 4 x 705 [8,6,2]
+ CRUSH rule 4 x 706 [2,5]
+ CRUSH rule 4 x 707 [7,8,6]
+ CRUSH rule 4 x 708 [5,8]
+ CRUSH rule 4 x 709 [6,5,2]
+ CRUSH rule 4 x 710 [8,5]
+ CRUSH rule 4 x 711 [2,5,8]
+ CRUSH rule 4 x 712 [2,5,7]
+ CRUSH rule 4 x 713 [6,7,8]
+ CRUSH rule 4 x 714 [5,2]
+ CRUSH rule 4 x 715 [2,5]
+ CRUSH rule 4 x 716 [5,6,2]
+ CRUSH rule 4 x 717 [8,7,2]
+ CRUSH rule 4 x 718 [5,7,8]
+ CRUSH rule 4 x 719 [2,6,5]
+ CRUSH rule 4 x 720 [6,8,2]
+ CRUSH rule 4 x 721 [5,7]
+ CRUSH rule 4 x 722 [5,6]
+ CRUSH rule 4 x 723 [5,2]
+ CRUSH rule 4 x 724 [2,6]
+ CRUSH rule 4 x 725 [2,5]
+ CRUSH rule 4 x 726 [5,8]
+ CRUSH rule 4 x 727 [5,6,8]
+ CRUSH rule 4 x 728 [2]
+ CRUSH rule 4 x 729 [5,7]
+ CRUSH rule 4 x 730 [5,7]
+ CRUSH rule 4 x 731 [5,2]
+ CRUSH rule 4 x 732 [2,5]
+ CRUSH rule 4 x 733 [5,7]
+ CRUSH rule 4 x 734 [6,5,2]
+ CRUSH rule 4 x 735 [5,8]
+ CRUSH rule 4 x 736 [5,6]
+ CRUSH rule 4 x 737 [2,8]
+ CRUSH rule 4 x 738 [5,2]
+ CRUSH rule 4 x 739 [2]
+ CRUSH rule 4 x 740 [2,7]
+ CRUSH rule 4 x 741 [7,8,2]
+ CRUSH rule 4 x 742 [8,2]
+ CRUSH rule 4 x 743 [7,2]
+ CRUSH rule 4 x 744 [5,7]
+ CRUSH rule 4 x 745 [5,2]
+ CRUSH rule 4 x 746 [5,2]
+ CRUSH rule 4 x 747 [6,2,5]
+ CRUSH rule 4 x 748 [2,7]
+ CRUSH rule 4 x 749 [5,8]
+ CRUSH rule 4 x 750 [2,6,5]
+ CRUSH rule 4 x 751 [2,6]
+ CRUSH rule 4 x 752 [8,2,5]
+ CRUSH rule 4 x 753 [7,8,5]
+ CRUSH rule 4 x 754 [8,6,7]
+ CRUSH rule 4 x 755 [2]
+ CRUSH rule 4 x 756 [5,6,2]
+ CRUSH rule 4 x 757 [8,6,2]
+ CRUSH rule 4 x 758 [6,2,5]
+ CRUSH rule 4 x 759 [8,5]
+ CRUSH rule 4 x 760 [2,5]
+ CRUSH rule 4 x 761 [5,2]
+ CRUSH rule 4 x 762 [2,7,8]
+ CRUSH rule 4 x 763 [8,6,7]
+ CRUSH rule 4 x 764 [2,7]
+ CRUSH rule 4 x 765 [6,5,2]
+ CRUSH rule 4 x 766 [8,5,7]
+ CRUSH rule 4 x 767 [2]
+ CRUSH rule 4 x 768 [8,5,2]
+ CRUSH rule 4 x 769 [6,2,8]
+ CRUSH rule 4 x 770 [6,2,7]
+ CRUSH rule 4 x 771 [7,2,5]
+ CRUSH rule 4 x 772 [8,5,7]
+ CRUSH rule 4 x 773 [5,2]
+ CRUSH rule 4 x 774 [5,6]
+ CRUSH rule 4 x 775 [6,8,5]
+ CRUSH rule 4 x 776 [7,2]
+ CRUSH rule 4 x 777 [5,2,6]
+ CRUSH rule 4 x 778 [2,8]
+ CRUSH rule 4 x 779 [2,7,5]
+ CRUSH rule 4 x 780 [2,5]
+ CRUSH rule 4 x 781 [6,5,7]
+ CRUSH rule 4 x 782 [5,2]
+ CRUSH rule 4 x 783 [7,2,8]
+ CRUSH rule 4 x 784 [2,5]
+ CRUSH rule 4 x 785 [6,2]
+ CRUSH rule 4 x 786 [7,6,5]
+ CRUSH rule 4 x 787 [2,6]
+ CRUSH rule 4 x 788 [6,2,8]
+ CRUSH rule 4 x 789 [2,5]
+ CRUSH rule 4 x 790 [8,5,7]
+ CRUSH rule 4 x 791 [5,8,7]
+ CRUSH rule 4 x 792 [5,8]
+ CRUSH rule 4 x 793 [6,2,5]
+ CRUSH rule 4 x 794 [2,6,8]
+ CRUSH rule 4 x 795 [2,5]
+ CRUSH rule 4 x 796 [5]
+ CRUSH rule 4 x 797 [2,5]
+ CRUSH rule 4 x 798 [6,8,7]
+ CRUSH rule 4 x 799 [5,2]
+ CRUSH rule 4 x 800 [5,2]
+ CRUSH rule 4 x 801 [5,6,8]
+ CRUSH rule 4 x 802 [2,8]
+ CRUSH rule 4 x 803 [2,5,7]
+ CRUSH rule 4 x 804 [6,2,5]
+ CRUSH rule 4 x 805 [5,6,7]
+ CRUSH rule 4 x 806 [2,5]
+ CRUSH rule 4 x 807 [5,7]
+ CRUSH rule 4 x 808 [5,6,2]
+ CRUSH rule 4 x 809 [2,5]
+ CRUSH rule 4 x 810 [5,7]
+ CRUSH rule 4 x 811 [8,5]
+ CRUSH rule 4 x 812 [8,5]
+ CRUSH rule 4 x 813 [6,5,8]
+ CRUSH rule 4 x 814 [5,6,8]
+ CRUSH rule 4 x 815 [5,2]
+ CRUSH rule 4 x 816 [2,8]
+ CRUSH rule 4 x 817 [5,7]
+ CRUSH rule 4 x 818 [5,2]
+ CRUSH rule 4 x 819 [5,2]
+ CRUSH rule 4 x 820 [5]
+ CRUSH rule 4 x 821 [5,8]
+ CRUSH rule 4 x 822 [2,5]
+ CRUSH rule 4 x 823 [5,8,2]
+ CRUSH rule 4 x 824 [5,7]
+ CRUSH rule 4 x 825 [2,8,7]
+ CRUSH rule 4 x 826 [7,2,5]
+ CRUSH rule 4 x 827 [2,8]
+ CRUSH rule 4 x 828 [2,5]
+ CRUSH rule 4 x 829 [5,6,7]
+ CRUSH rule 4 x 830 [2,5]
+ CRUSH rule 4 x 831 [2,6]
+ CRUSH rule 4 x 832 [5]
+ CRUSH rule 4 x 833 [2,7]
+ CRUSH rule 4 x 834 [5,2]
+ CRUSH rule 4 x 835 [8,5]
+ CRUSH rule 4 x 836 [5]
+ CRUSH rule 4 x 837 [6,5,2]
+ CRUSH rule 4 x 838 [6,7,2]
+ CRUSH rule 4 x 839 [5,2,6]
+ CRUSH rule 4 x 840 [7,8,5]
+ CRUSH rule 4 x 841 [5,8]
+ CRUSH rule 4 x 842 [2,5]
+ CRUSH rule 4 x 843 [6,5,7]
+ CRUSH rule 4 x 844 [5,8,2]
+ CRUSH rule 4 x 845 [5,8,6]
+ CRUSH rule 4 x 846 [5,2]
+ CRUSH rule 4 x 847 [2,7]
+ CRUSH rule 4 x 848 [2,6]
+ CRUSH rule 4 x 849 [5]
+ CRUSH rule 4 x 850 [2,5]
+ CRUSH rule 4 x 851 [6,8,7]
+ CRUSH rule 4 x 852 [7,5,8]
+ CRUSH rule 4 x 853 [6,8,2]
+ CRUSH rule 4 x 854 [7,6,2]
+ CRUSH rule 4 x 855 [5,7,2]
+ CRUSH rule 4 x 856 [6,7,5]
+ CRUSH rule 4 x 857 [8,5,2]
+ CRUSH rule 4 x 858 [6,5,2]
+ CRUSH rule 4 x 859 [6,2,7]
+ CRUSH rule 4 x 860 [5,2]
+ CRUSH rule 4 x 861 [8,7,6]
+ CRUSH rule 4 x 862 [6,2,7]
+ CRUSH rule 4 x 863 [8,7,2]
+ CRUSH rule 4 x 864 [5,6,8]
+ CRUSH rule 4 x 865 [8,2]
+ CRUSH rule 4 x 866 [5,8]
+ CRUSH rule 4 x 867 [6,5,2]
+ CRUSH rule 4 x 868 [6,5,2]
+ CRUSH rule 4 x 869 [8,7,5]
+ CRUSH rule 4 x 870 [2,5,8]
+ CRUSH rule 4 x 871 [5]
+ CRUSH rule 4 x 872 [5,2]
+ CRUSH rule 4 x 873 [5,6]
+ CRUSH rule 4 x 874 [2,6]
+ CRUSH rule 4 x 875 [2,6,5]
+ CRUSH rule 4 x 876 [5,8,2]
+ CRUSH rule 4 x 877 [6,5,2]
+ CRUSH rule 4 x 878 [5,2]
+ CRUSH rule 4 x 879 [7,5,8]
+ CRUSH rule 4 x 880 [5,2]
+ CRUSH rule 4 x 881 [5,6,2]
+ CRUSH rule 4 x 882 [5,2]
+ CRUSH rule 4 x 883 [2]
+ CRUSH rule 4 x 884 [6,2,5]
+ CRUSH rule 4 x 885 [5,2]
+ CRUSH rule 4 x 886 [5,6]
+ CRUSH rule 4 x 887 [7,5,2]
+ CRUSH rule 4 x 888 [6,8,2]
+ CRUSH rule 4 x 889 [2,7]
+ CRUSH rule 4 x 890 [7,2]
+ CRUSH rule 4 x 891 [2,8]
+ CRUSH rule 4 x 892 [6,2,5]
+ CRUSH rule 4 x 893 [2,5,7]
+ CRUSH rule 4 x 894 [7,5,2]
+ CRUSH rule 4 x 895 [5,2]
+ CRUSH rule 4 x 896 [2,8]
+ CRUSH rule 4 x 897 [5,2,6]
+ CRUSH rule 4 x 898 [2,5]
+ CRUSH rule 4 x 899 [2,7,6]
+ CRUSH rule 4 x 900 [5,2]
+ CRUSH rule 4 x 901 [5,2]
+ CRUSH rule 4 x 902 [8,5,7]
+ CRUSH rule 4 x 903 [5,7]
+ CRUSH rule 4 x 904 [5,6,8]
+ CRUSH rule 4 x 905 [6,2,5]
+ CRUSH rule 4 x 906 [2]
+ CRUSH rule 4 x 907 [7,2]
+ CRUSH rule 4 x 908 [5,8,2]
+ CRUSH rule 4 x 909 [2,5]
+ CRUSH rule 4 x 910 [6,5,2]
+ CRUSH rule 4 x 911 [5,8]
+ CRUSH rule 4 x 912 [2,7]
+ CRUSH rule 4 x 913 [7,6,8]
+ CRUSH rule 4 x 914 [6,5,7]
+ CRUSH rule 4 x 915 [8,2,6]
+ CRUSH rule 4 x 916 [5,2]
+ CRUSH rule 4 x 917 [2,5]
+ CRUSH rule 4 x 918 [8,2]
+ CRUSH rule 4 x 919 [6,2,8]
+ CRUSH rule 4 x 920 [7,6,5]
+ CRUSH rule 4 x 921 [2,5]
+ CRUSH rule 4 x 922 [6,7,8]
+ CRUSH rule 4 x 923 [5,6]
+ CRUSH rule 4 x 924 [5]
+ CRUSH rule 4 x 925 [5,7]
+ CRUSH rule 4 x 926 [5]
+ CRUSH rule 4 x 927 [2,6,5]
+ CRUSH rule 4 x 928 [8,2]
+ CRUSH rule 4 x 929 [5,2]
+ CRUSH rule 4 x 930 [2,5,6]
+ CRUSH rule 4 x 931 [5,2]
+ CRUSH rule 4 x 932 [5,2]
+ CRUSH rule 4 x 933 [8,5]
+ CRUSH rule 4 x 934 [5,8]
+ CRUSH rule 4 x 935 [6,5]
+ CRUSH rule 4 x 936 [2,6,7]
+ CRUSH rule 4 x 937 [5]
+ CRUSH rule 4 x 938 [6,5,8]
+ CRUSH rule 4 x 939 [2,7]
+ CRUSH rule 4 x 940 [8,7,6]
+ CRUSH rule 4 x 941 [5,2]
+ CRUSH rule 4 x 942 [2]
+ CRUSH rule 4 x 943 [8,2,5]
+ CRUSH rule 4 x 944 [5,7]
+ CRUSH rule 4 x 945 [7,2,5]
+ CRUSH rule 4 x 946 [2,7]
+ CRUSH rule 4 x 947 [5]
+ CRUSH rule 4 x 948 [7,8,6]
+ CRUSH rule 4 x 949 [6,2,7]
+ CRUSH rule 4 x 950 [5,8]
+ CRUSH rule 4 x 951 [5]
+ CRUSH rule 4 x 952 [2,7]
+ CRUSH rule 4 x 953 [2,5]
+ CRUSH rule 4 x 954 [5,2]
+ CRUSH rule 4 x 955 [8,6,2]
+ CRUSH rule 4 x 956 [2,8]
+ CRUSH rule 4 x 957 [7,6,2]
+ CRUSH rule 4 x 958 [8,7,5]
+ CRUSH rule 4 x 959 [5,2,7]
+ CRUSH rule 4 x 960 [5,6]
+ CRUSH rule 4 x 961 [5,2]
+ CRUSH rule 4 x 962 [7,5]
+ CRUSH rule 4 x 963 [2,5]
+ CRUSH rule 4 x 964 [5,2]
+ CRUSH rule 4 x 965 [7,6,5]
+ CRUSH rule 4 x 966 [5,8]
+ CRUSH rule 4 x 967 [8,6,5]
+ CRUSH rule 4 x 968 [7,2,5]
+ CRUSH rule 4 x 969 [8,2,6]
+ CRUSH rule 4 x 970 [2,6,5]
+ CRUSH rule 4 x 971 [2,7,8]
+ CRUSH rule 4 x 972 [2,8,5]
+ CRUSH rule 4 x 973 [2]
+ CRUSH rule 4 x 974 [5,2]
+ CRUSH rule 4 x 975 [5,7]
+ CRUSH rule 4 x 976 [5]
+ CRUSH rule 4 x 977 [8,5,2]
+ CRUSH rule 4 x 978 [7,2,8]
+ CRUSH rule 4 x 979 [7,6,2]
+ CRUSH rule 4 x 980 [6,2,7]
+ CRUSH rule 4 x 981 [7,5,2]
+ CRUSH rule 4 x 982 [5,2]
+ CRUSH rule 4 x 983 [5,6]
+ CRUSH rule 4 x 984 [2]
+ CRUSH rule 4 x 985 [2,5]
+ CRUSH rule 4 x 986 [8,7,5]
+ CRUSH rule 4 x 987 [2,5]
+ CRUSH rule 4 x 988 [2,5]
+ CRUSH rule 4 x 989 [2,6,5]
+ CRUSH rule 4 x 990 [2,8]
+ CRUSH rule 4 x 991 [2,5]
+ CRUSH rule 4 x 992 [7,2,5]
+ CRUSH rule 4 x 993 [2,6]
+ CRUSH rule 4 x 994 [5]
+ CRUSH rule 4 x 995 [7,6,2]
+ CRUSH rule 4 x 996 [6,7,5]
+ CRUSH rule 4 x 997 [6,5,2]
+ CRUSH rule 4 x 998 [8,2]
+ CRUSH rule 4 x 999 [2,7,8]
+ CRUSH rule 4 x 1000 [8,5,2]
+ CRUSH rule 4 x 1001 [2,5]
+ CRUSH rule 4 x 1002 [2,5]
+ CRUSH rule 4 x 1003 [2,8,7]
+ CRUSH rule 4 x 1004 [6,2]
+ CRUSH rule 4 x 1005 [6,2]
+ CRUSH rule 4 x 1006 [2]
+ CRUSH rule 4 x 1007 [2,5]
+ CRUSH rule 4 x 1008 [2,7]
+ CRUSH rule 4 x 1009 [6,8,5]
+ CRUSH rule 4 x 1010 [5,2]
+ CRUSH rule 4 x 1011 [5,2]
+ CRUSH rule 4 x 1012 [5,2,7]
+ CRUSH rule 4 x 1013 [5,2]
+ CRUSH rule 4 x 1014 [2,8,5]
+ CRUSH rule 4 x 1015 [6,8,5]
+ CRUSH rule 4 x 1016 [2]
+ CRUSH rule 4 x 1017 [6,2]
+ CRUSH rule 4 x 1018 [5]
+ CRUSH rule 4 x 1019 [5,8]
+ CRUSH rule 4 x 1020 [5,2]
+ CRUSH rule 4 x 1021 [5,2]
+ CRUSH rule 4 x 1022 [2,6,7]
+ CRUSH rule 4 x 1023 [5,2]
+ rule 4 (choose-set-two) num_rep 3 result size == 1:\t78/1024 (esc)
+ rule 4 (choose-set-two) num_rep 3 result size == 2:\t480/1024 (esc)
+ rule 4 (choose-set-two) num_rep 3 result size == 3:\t466/1024 (esc)
+ rule 5 (chooseleaf-set), x = 0..1023, numrep = 2..3
+ CRUSH rule 5 x 0 [2,5]
+ CRUSH rule 5 x 1 [2,8]
+ CRUSH rule 5 x 2 [2,5]
+ CRUSH rule 5 x 3 [8,2]
+ CRUSH rule 5 x 4 [5,2]
+ CRUSH rule 5 x 5 [7,2]
+ CRUSH rule 5 x 6 [2,6]
+ CRUSH rule 5 x 7 [5,8]
+ CRUSH rule 5 x 8 [5,6]
+ CRUSH rule 5 x 9 [2,5]
+ CRUSH rule 5 x 10 [2,7]
+ CRUSH rule 5 x 11 [2,7]
+ CRUSH rule 5 x 12 [2,5]
+ CRUSH rule 5 x 13 [5,8]
+ CRUSH rule 5 x 14 [7,2]
+ CRUSH rule 5 x 15 [7,2]
+ CRUSH rule 5 x 16 [5,6]
+ CRUSH rule 5 x 17 [5,2]
+ CRUSH rule 5 x 18 [2,5]
+ CRUSH rule 5 x 19 [7,5]
+ CRUSH rule 5 x 20 [2,5]
+ CRUSH rule 5 x 21 [5,7]
+ CRUSH rule 5 x 22 [8,5]
+ CRUSH rule 5 x 23 [5,6]
+ CRUSH rule 5 x 24 [2,7]
+ CRUSH rule 5 x 25 [5,7]
+ CRUSH rule 5 x 26 [2,8]
+ CRUSH rule 5 x 27 [5,2]
+ CRUSH rule 5 x 28 [6,2]
+ CRUSH rule 5 x 29 [8,5]
+ CRUSH rule 5 x 30 [5,7]
+ CRUSH rule 5 x 31 [8,2]
+ CRUSH rule 5 x 32 [5,6]
+ CRUSH rule 5 x 33 [2,7]
+ CRUSH rule 5 x 34 [2,5]
+ CRUSH rule 5 x 35 [2,8]
+ CRUSH rule 5 x 36 [5,8]
+ CRUSH rule 5 x 37 [2,5]
+ CRUSH rule 5 x 38 [5,8]
+ CRUSH rule 5 x 39 [5,7]
+ CRUSH rule 5 x 40 [7,2]
+ CRUSH rule 5 x 41 [2,6]
+ CRUSH rule 5 x 42 [5,6]
+ CRUSH rule 5 x 43 [2,5]
+ CRUSH rule 5 x 44 [2,6]
+ CRUSH rule 5 x 45 [8,2]
+ CRUSH rule 5 x 46 [2,5]
+ CRUSH rule 5 x 47 [5,2]
+ CRUSH rule 5 x 48 [5,6]
+ CRUSH rule 5 x 49 [5,7]
+ CRUSH rule 5 x 50 [5,2]
+ CRUSH rule 5 x 51 [5,6]
+ CRUSH rule 5 x 52 [8,2]
+ CRUSH rule 5 x 53 [5,8]
+ CRUSH rule 5 x 54 [7,5]
+ CRUSH rule 5 x 55 [8,2]
+ CRUSH rule 5 x 56 [6,5]
+ CRUSH rule 5 x 57 [5,8]
+ CRUSH rule 5 x 58 [2,8]
+ CRUSH rule 5 x 59 [5,2]
+ CRUSH rule 5 x 60 [5,2]
+ CRUSH rule 5 x 61 [5,6]
+ CRUSH rule 5 x 62 [7,2]
+ CRUSH rule 5 x 63 [5,6]
+ CRUSH rule 5 x 64 [5,2]
+ CRUSH rule 5 x 65 [7,5]
+ CRUSH rule 5 x 66 [5,6]
+ CRUSH rule 5 x 67 [5,2]
+ CRUSH rule 5 x 68 [2,5]
+ CRUSH rule 5 x 69 [5,2]
+ CRUSH rule 5 x 70 [7,2]
+ CRUSH rule 5 x 71 [2,8]
+ CRUSH rule 5 x 72 [6,2]
+ CRUSH rule 5 x 73 [2,7]
+ CRUSH rule 5 x 74 [2,7]
+ CRUSH rule 5 x 75 [5,2]
+ CRUSH rule 5 x 76 [5,2]
+ CRUSH rule 5 x 77 [7,2]
+ CRUSH rule 5 x 78 [2,5]
+ CRUSH rule 5 x 79 [5,2]
+ CRUSH rule 5 x 80 [2,5]
+ CRUSH rule 5 x 81 [2,5]
+ CRUSH rule 5 x 82 [7,2]
+ CRUSH rule 5 x 83 [2,6]
+ CRUSH rule 5 x 84 [7,2]
+ CRUSH rule 5 x 85 [5,8]
+ CRUSH rule 5 x 86 [2,6]
+ CRUSH rule 5 x 87 [2,7]
+ CRUSH rule 5 x 88 [2,6]
+ CRUSH rule 5 x 89 [5,2]
+ CRUSH rule 5 x 90 [6,5]
+ CRUSH rule 5 x 91 [5,8]
+ CRUSH rule 5 x 92 [2,8]
+ CRUSH rule 5 x 93 [7,5]
+ CRUSH rule 5 x 94 [2,5]
+ CRUSH rule 5 x 95 [7,5]
+ CRUSH rule 5 x 96 [5,6]
+ CRUSH rule 5 x 97 [8,5]
+ CRUSH rule 5 x 98 [2,7]
+ CRUSH rule 5 x 99 [2,7]
+ CRUSH rule 5 x 100 [2,7]
+ CRUSH rule 5 x 101 [5,7]
+ CRUSH rule 5 x 102 [5,2]
+ CRUSH rule 5 x 103 [5,7]
+ CRUSH rule 5 x 104 [7,5]
+ CRUSH rule 5 x 105 [2,5]
+ CRUSH rule 5 x 106 [2,6]
+ CRUSH rule 5 x 107 [5,2]
+ CRUSH rule 5 x 108 [7,2]
+ CRUSH rule 5 x 109 [2,5]
+ CRUSH rule 5 x 110 [5,2]
+ CRUSH rule 5 x 111 [2,5]
+ CRUSH rule 5 x 112 [2,6]
+ CRUSH rule 5 x 113 [6,2]
+ CRUSH rule 5 x 114 [7,5]
+ CRUSH rule 5 x 115 [8,2]
+ CRUSH rule 5 x 116 [2,6]
+ CRUSH rule 5 x 117 [7,5]
+ CRUSH rule 5 x 118 [2,5]
+ CRUSH rule 5 x 119 [5,6]
+ CRUSH rule 5 x 120 [2,5]
+ CRUSH rule 5 x 121 [2,7]
+ CRUSH rule 5 x 122 [8,5]
+ CRUSH rule 5 x 123 [2,5]
+ CRUSH rule 5 x 124 [5,2]
+ CRUSH rule 5 x 125 [2,7]
+ CRUSH rule 5 x 126 [5,2]
+ CRUSH rule 5 x 127 [5,6]
+ CRUSH rule 5 x 128 [5,6]
+ CRUSH rule 5 x 129 [2,5]
+ CRUSH rule 5 x 130 [5,8]
+ CRUSH rule 5 x 131 [2,5]
+ CRUSH rule 5 x 132 [2,5]
+ CRUSH rule 5 x 133 [5,6]
+ CRUSH rule 5 x 134 [2,8]
+ CRUSH rule 5 x 135 [5,6]
+ CRUSH rule 5 x 136 [2,5]
+ CRUSH rule 5 x 137 [7,5]
+ CRUSH rule 5 x 138 [8,5]
+ CRUSH rule 5 x 139 [5,2]
+ CRUSH rule 5 x 140 [2,6]
+ CRUSH rule 5 x 141 [6,2]
+ CRUSH rule 5 x 142 [5,2]
+ CRUSH rule 5 x 143 [5,8]
+ CRUSH rule 5 x 144 [8,2]
+ CRUSH rule 5 x 145 [8,5]
+ CRUSH rule 5 x 146 [2,6]
+ CRUSH rule 5 x 147 [2,8]
+ CRUSH rule 5 x 148 [5,2]
+ CRUSH rule 5 x 149 [5,8]
+ CRUSH rule 5 x 150 [2,6]
+ CRUSH rule 5 x 151 [5,6]
+ CRUSH rule 5 x 152 [8,5]
+ CRUSH rule 5 x 153 [8,5]
+ CRUSH rule 5 x 154 [5,2]
+ CRUSH rule 5 x 155 [5,7]
+ CRUSH rule 5 x 156 [5,2]
+ CRUSH rule 5 x 157 [5,2]
+ CRUSH rule 5 x 158 [2,8]
+ CRUSH rule 5 x 159 [7,2]
+ CRUSH rule 5 x 160 [2,8]
+ CRUSH rule 5 x 161 [2,5]
+ CRUSH rule 5 x 162 [2,6]
+ CRUSH rule 5 x 163 [5,6]
+ CRUSH rule 5 x 164 [7,2]
+ CRUSH rule 5 x 165 [7,2]
+ CRUSH rule 5 x 166 [2,5]
+ CRUSH rule 5 x 167 [2,7]
+ CRUSH rule 5 x 168 [5,2]
+ CRUSH rule 5 x 169 [2,6]
+ CRUSH rule 5 x 170 [2,5]
+ CRUSH rule 5 x 171 [7,5]
+ CRUSH rule 5 x 172 [2,7]
+ CRUSH rule 5 x 173 [8,5]
+ CRUSH rule 5 x 174 [2,5]
+ CRUSH rule 5 x 175 [6,2]
+ CRUSH rule 5 x 176 [5,2]
+ CRUSH rule 5 x 177 [5,2]
+ CRUSH rule 5 x 178 [5,2]
+ CRUSH rule 5 x 179 [5,2]
+ CRUSH rule 5 x 180 [5,8]
+ CRUSH rule 5 x 181 [6,2]
+ CRUSH rule 5 x 182 [8,5]
+ CRUSH rule 5 x 183 [7,5]
+ CRUSH rule 5 x 184 [5,7]
+ CRUSH rule 5 x 185 [6,2]
+ CRUSH rule 5 x 186 [2,5]
+ CRUSH rule 5 x 187 [2,6]
+ CRUSH rule 5 x 188 [2,8]
+ CRUSH rule 5 x 189 [2,7]
+ CRUSH rule 5 x 190 [5,2]
+ CRUSH rule 5 x 191 [7,2]
+ CRUSH rule 5 x 192 [5,2]
+ CRUSH rule 5 x 193 [5,2]
+ CRUSH rule 5 x 194 [2,5]
+ CRUSH rule 5 x 195 [6,5]
+ CRUSH rule 5 x 196 [6,2]
+ CRUSH rule 5 x 197 [6,5]
+ CRUSH rule 5 x 198 [2,5]
+ CRUSH rule 5 x 199 [2,5]
+ CRUSH rule 5 x 200 [2,5]
+ CRUSH rule 5 x 201 [7,2]
+ CRUSH rule 5 x 202 [6,5]
+ CRUSH rule 5 x 203 [5,8]
+ CRUSH rule 5 x 204 [2,5]
+ CRUSH rule 5 x 205 [2,7]
+ CRUSH rule 5 x 206 [2,7]
+ CRUSH rule 5 x 207 [5,2]
+ CRUSH rule 5 x 208 [7,2]
+ CRUSH rule 5 x 209 [2,8]
+ CRUSH rule 5 x 210 [2,5]
+ CRUSH rule 5 x 211 [5,2]
+ CRUSH rule 5 x 212 [7,5]
+ CRUSH rule 5 x 213 [8,5]
+ CRUSH rule 5 x 214 [5,8]
+ CRUSH rule 5 x 215 [8,2]
+ CRUSH rule 5 x 216 [5,2]
+ CRUSH rule 5 x 217 [2,7]
+ CRUSH rule 5 x 218 [2,7]
+ CRUSH rule 5 x 219 [5,8]
+ CRUSH rule 5 x 220 [5,7]
+ CRUSH rule 5 x 221 [5,6]
+ CRUSH rule 5 x 222 [6,5]
+ CRUSH rule 5 x 223 [2,5]
+ CRUSH rule 5 x 224 [2,5]
+ CRUSH rule 5 x 225 [8,2]
+ CRUSH rule 5 x 226 [7,2]
+ CRUSH rule 5 x 227 [5,2]
+ CRUSH rule 5 x 228 [5,6]
+ CRUSH rule 5 x 229 [5,8]
+ CRUSH rule 5 x 230 [5,7]
+ CRUSH rule 5 x 231 [5,7]
+ CRUSH rule 5 x 232 [2,7]
+ CRUSH rule 5 x 233 [5,7]
+ CRUSH rule 5 x 234 [2,5]
+ CRUSH rule 5 x 235 [5,8]
+ CRUSH rule 5 x 236 [5,2]
+ CRUSH rule 5 x 237 [5,7]
+ CRUSH rule 5 x 238 [5,2]
+ CRUSH rule 5 x 239 [8,5]
+ CRUSH rule 5 x 240 [5,7]
+ CRUSH rule 5 x 241 [5,2]
+ CRUSH rule 5 x 242 [5,2]
+ CRUSH rule 5 x 243 [5,7]
+ CRUSH rule 5 x 244 [5,6]
+ CRUSH rule 5 x 245 [7,2]
+ CRUSH rule 5 x 246 [2,5]
+ CRUSH rule 5 x 247 [6,2]
+ CRUSH rule 5 x 248 [8,2]
+ CRUSH rule 5 x 249 [2,5]
+ CRUSH rule 5 x 250 [2,5]
+ CRUSH rule 5 x 251 [2,5]
+ CRUSH rule 5 x 252 [5,7]
+ CRUSH rule 5 x 253 [5,2]
+ CRUSH rule 5 x 254 [5,2]
+ CRUSH rule 5 x 255 [2,7]
+ CRUSH rule 5 x 256 [5,7]
+ CRUSH rule 5 x 257 [2,8]
+ CRUSH rule 5 x 258 [5,2]
+ CRUSH rule 5 x 259 [5,6]
+ CRUSH rule 5 x 260 [5,6]
+ CRUSH rule 5 x 261 [8,5]
+ CRUSH rule 5 x 262 [5,6]
+ CRUSH rule 5 x 263 [6,2]
+ CRUSH rule 5 x 264 [5,6]
+ CRUSH rule 5 x 265 [8,5]
+ CRUSH rule 5 x 266 [8,2]
+ CRUSH rule 5 x 267 [2,5]
+ CRUSH rule 5 x 268 [2,7]
+ CRUSH rule 5 x 269 [2,8]
+ CRUSH rule 5 x 270 [5,2]
+ CRUSH rule 5 x 271 [7,5]
+ CRUSH rule 5 x 272 [2,8]
+ CRUSH rule 5 x 273 [5,2]
+ CRUSH rule 5 x 274 [6,5]
+ CRUSH rule 5 x 275 [5,7]
+ CRUSH rule 5 x 276 [7,2]
+ CRUSH rule 5 x 277 [6,5]
+ CRUSH rule 5 x 278 [6,2]
+ CRUSH rule 5 x 279 [8,5]
+ CRUSH rule 5 x 280 [2,6]
+ CRUSH rule 5 x 281 [8,2]
+ CRUSH rule 5 x 282 [5,2]
+ CRUSH rule 5 x 283 [8,2]
+ CRUSH rule 5 x 284 [6,5]
+ CRUSH rule 5 x 285 [5,7]
+ CRUSH rule 5 x 286 [2,6]
+ CRUSH rule 5 x 287 [2,5]
+ CRUSH rule 5 x 288 [8,2]
+ CRUSH rule 5 x 289 [5,6]
+ CRUSH rule 5 x 290 [2,5]
+ CRUSH rule 5 x 291 [2,5]
+ CRUSH rule 5 x 292 [8,2]
+ CRUSH rule 5 x 293 [6,2]
+ CRUSH rule 5 x 294 [7,5]
+ CRUSH rule 5 x 295 [5,8]
+ CRUSH rule 5 x 296 [5,2]
+ CRUSH rule 5 x 297 [6,2]
+ CRUSH rule 5 x 298 [2,5]
+ CRUSH rule 5 x 299 [2,8]
+ CRUSH rule 5 x 300 [8,5]
+ CRUSH rule 5 x 301 [2,8]
+ CRUSH rule 5 x 302 [5,2]
+ CRUSH rule 5 x 303 [7,5]
+ CRUSH rule 5 x 304 [2,7]
+ CRUSH rule 5 x 305 [5,8]
+ CRUSH rule 5 x 306 [2,7]
+ CRUSH rule 5 x 307 [2,7]
+ CRUSH rule 5 x 308 [2,8]
+ CRUSH rule 5 x 309 [7,5]
+ CRUSH rule 5 x 310 [5,2]
+ CRUSH rule 5 x 311 [5,6]
+ CRUSH rule 5 x 312 [2,6]
+ CRUSH rule 5 x 313 [5,2]
+ CRUSH rule 5 x 314 [5,2]
+ CRUSH rule 5 x 315 [2,5]
+ CRUSH rule 5 x 316 [6,5]
+ CRUSH rule 5 x 317 [2,6]
+ CRUSH rule 5 x 318 [8,2]
+ CRUSH rule 5 x 319 [5,2]
+ CRUSH rule 5 x 320 [5,7]
+ CRUSH rule 5 x 321 [2,5]
+ CRUSH rule 5 x 322 [2,7]
+ CRUSH rule 5 x 323 [5,7]
+ CRUSH rule 5 x 324 [7,2]
+ CRUSH rule 5 x 325 [5,6]
+ CRUSH rule 5 x 326 [5,2]
+ CRUSH rule 5 x 327 [2,6]
+ CRUSH rule 5 x 328 [7,5]
+ CRUSH rule 5 x 329 [5,6]
+ CRUSH rule 5 x 330 [5,7]
+ CRUSH rule 5 x 331 [2,6]
+ CRUSH rule 5 x 332 [2,5]
+ CRUSH rule 5 x 333 [6,5]
+ CRUSH rule 5 x 334 [8,5]
+ CRUSH rule 5 x 335 [7,2]
+ CRUSH rule 5 x 336 [5,6]
+ CRUSH rule 5 x 337 [7,2]
+ CRUSH rule 5 x 338 [5,6]
+ CRUSH rule 5 x 339 [7,5]
+ CRUSH rule 5 x 340 [2,8]
+ CRUSH rule 5 x 341 [5,2]
+ CRUSH rule 5 x 342 [2,7]
+ CRUSH rule 5 x 343 [6,5]
+ CRUSH rule 5 x 344 [6,2]
+ CRUSH rule 5 x 345 [5,7]
+ CRUSH rule 5 x 346 [8,2]
+ CRUSH rule 5 x 347 [5,2]
+ CRUSH rule 5 x 348 [8,2]
+ CRUSH rule 5 x 349 [2,6]
+ CRUSH rule 5 x 350 [8,5]
+ CRUSH rule 5 x 351 [5,6]
+ CRUSH rule 5 x 352 [2,8]
+ CRUSH rule 5 x 353 [6,5]
+ CRUSH rule 5 x 354 [2,5]
+ CRUSH rule 5 x 355 [5,8]
+ CRUSH rule 5 x 356 [5,2]
+ CRUSH rule 5 x 357 [6,2]
+ CRUSH rule 5 x 358 [2,8]
+ CRUSH rule 5 x 359 [6,2]
+ CRUSH rule 5 x 360 [5,2]
+ CRUSH rule 5 x 361 [8,5]
+ CRUSH rule 5 x 362 [5,2]
+ CRUSH rule 5 x 363 [5,2]
+ CRUSH rule 5 x 364 [2,5]
+ CRUSH rule 5 x 365 [6,5]
+ CRUSH rule 5 x 366 [7,2]
+ CRUSH rule 5 x 367 [5,2]
+ CRUSH rule 5 x 368 [7,5]
+ CRUSH rule 5 x 369 [5,7]
+ CRUSH rule 5 x 370 [8,2]
+ CRUSH rule 5 x 371 [2,5]
+ CRUSH rule 5 x 372 [5,2]
+ CRUSH rule 5 x 373 [2,6]
+ CRUSH rule 5 x 374 [5,8]
+ CRUSH rule 5 x 375 [6,5]
+ CRUSH rule 5 x 376 [7,2]
+ CRUSH rule 5 x 377 [2,5]
+ CRUSH rule 5 x 378 [2,8]
+ CRUSH rule 5 x 379 [8,5]
+ CRUSH rule 5 x 380 [2,5]
+ CRUSH rule 5 x 381 [2,5]
+ CRUSH rule 5 x 382 [2,5]
+ CRUSH rule 5 x 383 [5,6]
+ CRUSH rule 5 x 384 [7,2]
+ CRUSH rule 5 x 385 [7,5]
+ CRUSH rule 5 x 386 [2,5]
+ CRUSH rule 5 x 387 [2,5]
+ CRUSH rule 5 x 388 [5,2]
+ CRUSH rule 5 x 389 [2,5]
+ CRUSH rule 5 x 390 [5,6]
+ CRUSH rule 5 x 391 [5,6]
+ CRUSH rule 5 x 392 [2,8]
+ CRUSH rule 5 x 393 [5,2]
+ CRUSH rule 5 x 394 [5,7]
+ CRUSH rule 5 x 395 [5,2]
+ CRUSH rule 5 x 396 [5,2]
+ CRUSH rule 5 x 397 [2,5]
+ CRUSH rule 5 x 398 [2,5]
+ CRUSH rule 5 x 399 [8,5]
+ CRUSH rule 5 x 400 [8,2]
+ CRUSH rule 5 x 401 [2,5]
+ CRUSH rule 5 x 402 [7,5]
+ CRUSH rule 5 x 403 [2,5]
+ CRUSH rule 5 x 404 [5,2]
+ CRUSH rule 5 x 405 [6,5]
+ CRUSH rule 5 x 406 [2,6]
+ CRUSH rule 5 x 407 [2,8]
+ CRUSH rule 5 x 408 [5,2]
+ CRUSH rule 5 x 409 [7,5]
+ CRUSH rule 5 x 410 [8,5]
+ CRUSH rule 5 x 411 [2,8]
+ CRUSH rule 5 x 412 [2,5]
+ CRUSH rule 5 x 413 [5,2]
+ CRUSH rule 5 x 414 [5,2]
+ CRUSH rule 5 x 415 [2,6]
+ CRUSH rule 5 x 416 [2,6]
+ CRUSH rule 5 x 417 [8,2]
+ CRUSH rule 5 x 418 [7,2]
+ CRUSH rule 5 x 419 [8,5]
+ CRUSH rule 5 x 420 [2,5]
+ CRUSH rule 5 x 421 [8,5]
+ CRUSH rule 5 x 422 [6,5]
+ CRUSH rule 5 x 423 [2,5]
+ CRUSH rule 5 x 424 [8,5]
+ CRUSH rule 5 x 425 [2,5]
+ CRUSH rule 5 x 426 [6,2]
+ CRUSH rule 5 x 427 [2,7]
+ CRUSH rule 5 x 428 [5,7]
+ CRUSH rule 5 x 429 [5,6]
+ CRUSH rule 5 x 430 [5,6]
+ CRUSH rule 5 x 431 [5,2]
+ CRUSH rule 5 x 432 [7,2]
+ CRUSH rule 5 x 433 [6,5]
+ CRUSH rule 5 x 434 [5,2]
+ CRUSH rule 5 x 435 [2,5]
+ CRUSH rule 5 x 436 [5,2]
+ CRUSH rule 5 x 437 [7,5]
+ CRUSH rule 5 x 438 [2,5]
+ CRUSH rule 5 x 439 [2,5]
+ CRUSH rule 5 x 440 [2,7]
+ CRUSH rule 5 x 441 [5,7]
+ CRUSH rule 5 x 442 [2,5]
+ CRUSH rule 5 x 443 [6,2]
+ CRUSH rule 5 x 444 [7,2]
+ CRUSH rule 5 x 445 [6,5]
+ CRUSH rule 5 x 446 [5,2]
+ CRUSH rule 5 x 447 [2,5]
+ CRUSH rule 5 x 448 [7,2]
+ CRUSH rule 5 x 449 [7,5]
+ CRUSH rule 5 x 450 [5,2]
+ CRUSH rule 5 x 451 [6,5]
+ CRUSH rule 5 x 452 [8,5]
+ CRUSH rule 5 x 453 [6,5]
+ CRUSH rule 5 x 454 [6,5]
+ CRUSH rule 5 x 455 [2,7]
+ CRUSH rule 5 x 456 [6,2]
+ CRUSH rule 5 x 457 [7,2]
+ CRUSH rule 5 x 458 [2,8]
+ CRUSH rule 5 x 459 [2,7]
+ CRUSH rule 5 x 460 [6,5]
+ CRUSH rule 5 x 461 [6,5]
+ CRUSH rule 5 x 462 [8,2]
+ CRUSH rule 5 x 463 [6,2]
+ CRUSH rule 5 x 464 [7,5]
+ CRUSH rule 5 x 465 [7,2]
+ CRUSH rule 5 x 466 [5,8]
+ CRUSH rule 5 x 467 [6,5]
+ CRUSH rule 5 x 468 [7,2]
+ CRUSH rule 5 x 469 [7,2]
+ CRUSH rule 5 x 470 [5,2]
+ CRUSH rule 5 x 471 [2,7]
+ CRUSH rule 5 x 472 [5,2]
+ CRUSH rule 5 x 473 [2,5]
+ CRUSH rule 5 x 474 [6,2]
+ CRUSH rule 5 x 475 [6,2]
+ CRUSH rule 5 x 476 [5,6]
+ CRUSH rule 5 x 477 [5,8]
+ CRUSH rule 5 x 478 [6,2]
+ CRUSH rule 5 x 479 [2,5]
+ CRUSH rule 5 x 480 [2,8]
+ CRUSH rule 5 x 481 [2,5]
+ CRUSH rule 5 x 482 [5,7]
+ CRUSH rule 5 x 483 [2,6]
+ CRUSH rule 5 x 484 [2,7]
+ CRUSH rule 5 x 485 [5,7]
+ CRUSH rule 5 x 486 [5,2]
+ CRUSH rule 5 x 487 [5,2]
+ CRUSH rule 5 x 488 [5,7]
+ CRUSH rule 5 x 489 [2,8]
+ CRUSH rule 5 x 490 [6,5]
+ CRUSH rule 5 x 491 [2,7]
+ CRUSH rule 5 x 492 [6,5]
+ CRUSH rule 5 x 493 [2,7]
+ CRUSH rule 5 x 494 [2,7]
+ CRUSH rule 5 x 495 [5,2]
+ CRUSH rule 5 x 496 [7,5]
+ CRUSH rule 5 x 497 [5,7]
+ CRUSH rule 5 x 498 [2,5]
+ CRUSH rule 5 x 499 [8,5]
+ CRUSH rule 5 x 500 [5,6]
+ CRUSH rule 5 x 501 [2,7]
+ CRUSH rule 5 x 502 [7,2]
+ CRUSH rule 5 x 503 [2,5]
+ CRUSH rule 5 x 504 [5,6]
+ CRUSH rule 5 x 505 [2,7]
+ CRUSH rule 5 x 506 [5,2]
+ CRUSH rule 5 x 507 [6,2]
+ CRUSH rule 5 x 508 [2,5]
+ CRUSH rule 5 x 509 [7,5]
+ CRUSH rule 5 x 510 [6,2]
+ CRUSH rule 5 x 511 [5,8]
+ CRUSH rule 5 x 512 [7,2]
+ CRUSH rule 5 x 513 [7,2]
+ CRUSH rule 5 x 514 [5,6]
+ CRUSH rule 5 x 515 [8,5]
+ CRUSH rule 5 x 516 [5,2]
+ CRUSH rule 5 x 517 [7,2]
+ CRUSH rule 5 x 518 [5,6]
+ CRUSH rule 5 x 519 [7,5]
+ CRUSH rule 5 x 520 [2,6]
+ CRUSH rule 5 x 521 [8,2]
+ CRUSH rule 5 x 522 [6,2]
+ CRUSH rule 5 x 523 [5,2]
+ CRUSH rule 5 x 524 [2,5]
+ CRUSH rule 5 x 525 [2,5]
+ CRUSH rule 5 x 526 [2,5]
+ CRUSH rule 5 x 527 [2,5]
+ CRUSH rule 5 x 528 [5,2]
+ CRUSH rule 5 x 529 [5,7]
+ CRUSH rule 5 x 530 [6,5]
+ CRUSH rule 5 x 531 [6,2]
+ CRUSH rule 5 x 532 [6,5]
+ CRUSH rule 5 x 533 [5,6]
+ CRUSH rule 5 x 534 [7,5]
+ CRUSH rule 5 x 535 [8,2]
+ CRUSH rule 5 x 536 [6,2]
+ CRUSH rule 5 x 537 [5,7]
+ CRUSH rule 5 x 538 [6,5]
+ CRUSH rule 5 x 539 [8,5]
+ CRUSH rule 5 x 540 [2,6]
+ CRUSH rule 5 x 541 [2,5]
+ CRUSH rule 5 x 542 [5,2]
+ CRUSH rule 5 x 543 [6,2]
+ CRUSH rule 5 x 544 [5,7]
+ CRUSH rule 5 x 545 [5,7]
+ CRUSH rule 5 x 546 [6,2]
+ CRUSH rule 5 x 547 [8,2]
+ CRUSH rule 5 x 548 [5,2]
+ CRUSH rule 5 x 549 [5,8]
+ CRUSH rule 5 x 550 [2,5]
+ CRUSH rule 5 x 551 [7,5]
+ CRUSH rule 5 x 552 [5,8]
+ CRUSH rule 5 x 553 [5,2]
+ CRUSH rule 5 x 554 [2,8]
+ CRUSH rule 5 x 555 [5,2]
+ CRUSH rule 5 x 556 [5,6]
+ CRUSH rule 5 x 557 [7,5]
+ CRUSH rule 5 x 558 [5,2]
+ CRUSH rule 5 x 559 [5,2]
+ CRUSH rule 5 x 560 [8,5]
+ CRUSH rule 5 x 561 [6,5]
+ CRUSH rule 5 x 562 [5,2]
+ CRUSH rule 5 x 563 [2,6]
+ CRUSH rule 5 x 564 [5,2]
+ CRUSH rule 5 x 565 [5,6]
+ CRUSH rule 5 x 566 [5,7]
+ CRUSH rule 5 x 567 [5,6]
+ CRUSH rule 5 x 568 [7,5]
+ CRUSH rule 5 x 569 [5,2]
+ CRUSH rule 5 x 570 [2,5]
+ CRUSH rule 5 x 571 [5,7]
+ CRUSH rule 5 x 572 [5,2]
+ CRUSH rule 5 x 573 [5,2]
+ CRUSH rule 5 x 574 [2,5]
+ CRUSH rule 5 x 575 [8,2]
+ CRUSH rule 5 x 576 [5,6]
+ CRUSH rule 5 x 577 [8,2]
+ CRUSH rule 5 x 578 [6,2]
+ CRUSH rule 5 x 579 [5,2]
+ CRUSH rule 5 x 580 [5,2]
+ CRUSH rule 5 x 581 [7,2]
+ CRUSH rule 5 x 582 [2,8]
+ CRUSH rule 5 x 583 [6,2]
+ CRUSH rule 5 x 584 [8,2]
+ CRUSH rule 5 x 585 [7,2]
+ CRUSH rule 5 x 586 [2,7]
+ CRUSH rule 5 x 587 [2,5]
+ CRUSH rule 5 x 588 [5,7]
+ CRUSH rule 5 x 589 [7,2]
+ CRUSH rule 5 x 590 [6,2]
+ CRUSH rule 5 x 591 [5,2]
+ CRUSH rule 5 x 592 [2,5]
+ CRUSH rule 5 x 593 [2,8]
+ CRUSH rule 5 x 594 [2,7]
+ CRUSH rule 5 x 595 [7,2]
+ CRUSH rule 5 x 596 [5,2]
+ CRUSH rule 5 x 597 [5,2]
+ CRUSH rule 5 x 598 [5,2]
+ CRUSH rule 5 x 599 [5,2]
+ CRUSH rule 5 x 600 [7,2]
+ CRUSH rule 5 x 601 [2,7]
+ CRUSH rule 5 x 602 [5,7]
+ CRUSH rule 5 x 603 [5,2]
+ CRUSH rule 5 x 604 [7,5]
+ CRUSH rule 5 x 605 [5,2]
+ CRUSH rule 5 x 606 [2,7]
+ CRUSH rule 5 x 607 [2,5]
+ CRUSH rule 5 x 608 [5,2]
+ CRUSH rule 5 x 609 [5,2]
+ CRUSH rule 5 x 610 [5,7]
+ CRUSH rule 5 x 611 [2,8]
+ CRUSH rule 5 x 612 [2,6]
+ CRUSH rule 5 x 613 [7,2]
+ CRUSH rule 5 x 614 [7,2]
+ CRUSH rule 5 x 615 [6,2]
+ CRUSH rule 5 x 616 [2,8]
+ CRUSH rule 5 x 617 [6,2]
+ CRUSH rule 5 x 618 [7,5]
+ CRUSH rule 5 x 619 [5,2]
+ CRUSH rule 5 x 620 [5,2]
+ CRUSH rule 5 x 621 [5,8]
+ CRUSH rule 5 x 622 [2,5]
+ CRUSH rule 5 x 623 [2,6]
+ CRUSH rule 5 x 624 [5,2]
+ CRUSH rule 5 x 625 [2,5]
+ CRUSH rule 5 x 626 [7,2]
+ CRUSH rule 5 x 627 [2,7]
+ CRUSH rule 5 x 628 [8,2]
+ CRUSH rule 5 x 629 [2,6]
+ CRUSH rule 5 x 630 [2,7]
+ CRUSH rule 5 x 631 [2,7]
+ CRUSH rule 5 x 632 [7,2]
+ CRUSH rule 5 x 633 [8,5]
+ CRUSH rule 5 x 634 [2,5]
+ CRUSH rule 5 x 635 [5,6]
+ CRUSH rule 5 x 636 [2,5]
+ CRUSH rule 5 x 637 [5,2]
+ CRUSH rule 5 x 638 [6,2]
+ CRUSH rule 5 x 639 [5,2]
+ CRUSH rule 5 x 640 [5,2]
+ CRUSH rule 5 x 641 [7,2]
+ CRUSH rule 5 x 642 [2,7]
+ CRUSH rule 5 x 643 [5,2]
+ CRUSH rule 5 x 644 [8,2]
+ CRUSH rule 5 x 645 [5,7]
+ CRUSH rule 5 x 646 [8,2]
+ CRUSH rule 5 x 647 [7,2]
+ CRUSH rule 5 x 648 [2,6]
+ CRUSH rule 5 x 649 [5,7]
+ CRUSH rule 5 x 650 [7,5]
+ CRUSH rule 5 x 651 [5,7]
+ CRUSH rule 5 x 652 [5,7]
+ CRUSH rule 5 x 653 [8,5]
+ CRUSH rule 5 x 654 [7,5]
+ CRUSH rule 5 x 655 [2,5]
+ CRUSH rule 5 x 656 [5,7]
+ CRUSH rule 5 x 657 [6,2]
+ CRUSH rule 5 x 658 [5,6]
+ CRUSH rule 5 x 659 [5,6]
+ CRUSH rule 5 x 660 [7,5]
+ CRUSH rule 5 x 661 [2,8]
+ CRUSH rule 5 x 662 [5,2]
+ CRUSH rule 5 x 663 [2,5]
+ CRUSH rule 5 x 664 [2,5]
+ CRUSH rule 5 x 665 [5,7]
+ CRUSH rule 5 x 666 [2,8]
+ CRUSH rule 5 x 667 [2,5]
+ CRUSH rule 5 x 668 [5,7]
+ CRUSH rule 5 x 669 [6,5]
+ CRUSH rule 5 x 670 [5,2]
+ CRUSH rule 5 x 671 [2,7]
+ CRUSH rule 5 x 672 [5,2]
+ CRUSH rule 5 x 673 [5,2]
+ CRUSH rule 5 x 674 [5,2]
+ CRUSH rule 5 x 675 [2,8]
+ CRUSH rule 5 x 676 [2,5]
+ CRUSH rule 5 x 677 [5,2]
+ CRUSH rule 5 x 678 [2,5]
+ CRUSH rule 5 x 679 [6,2]
+ CRUSH rule 5 x 680 [2,5]
+ CRUSH rule 5 x 681 [5,7]
+ CRUSH rule 5 x 682 [2,5]
+ CRUSH rule 5 x 683 [2,5]
+ CRUSH rule 5 x 684 [7,2]
+ CRUSH rule 5 x 685 [7,2]
+ CRUSH rule 5 x 686 [2,5]
+ CRUSH rule 5 x 687 [5,6]
+ CRUSH rule 5 x 688 [5,7]
+ CRUSH rule 5 x 689 [6,5]
+ CRUSH rule 5 x 690 [8,2]
+ CRUSH rule 5 x 691 [5,2]
+ CRUSH rule 5 x 692 [7,2]
+ CRUSH rule 5 x 693 [6,5]
+ CRUSH rule 5 x 694 [6,5]
+ CRUSH rule 5 x 695 [2,8]
+ CRUSH rule 5 x 696 [2,5]
+ CRUSH rule 5 x 697 [6,2]
+ CRUSH rule 5 x 698 [6,2]
+ CRUSH rule 5 x 699 [2,6]
+ CRUSH rule 5 x 700 [2,5]
+ CRUSH rule 5 x 701 [5,2]
+ CRUSH rule 5 x 702 [5,2]
+ CRUSH rule 5 x 703 [8,5]
+ CRUSH rule 5 x 704 [2,5]
+ CRUSH rule 5 x 705 [8,2]
+ CRUSH rule 5 x 706 [2,5]
+ CRUSH rule 5 x 707 [7,5]
+ CRUSH rule 5 x 708 [5,7]
+ CRUSH rule 5 x 709 [6,5]
+ CRUSH rule 5 x 710 [8,5]
+ CRUSH rule 5 x 711 [2,5]
+ CRUSH rule 5 x 712 [2,5]
+ CRUSH rule 5 x 713 [6,5]
+ CRUSH rule 5 x 714 [5,2]
+ CRUSH rule 5 x 715 [2,5]
+ CRUSH rule 5 x 716 [5,6]
+ CRUSH rule 5 x 717 [8,2]
+ CRUSH rule 5 x 718 [5,7]
+ CRUSH rule 5 x 719 [2,6]
+ CRUSH rule 5 x 720 [6,2]
+ CRUSH rule 5 x 721 [5,7]
+ CRUSH rule 5 x 722 [5,7]
+ CRUSH rule 5 x 723 [5,2]
+ CRUSH rule 5 x 724 [2,6]
+ CRUSH rule 5 x 725 [2,5]
+ CRUSH rule 5 x 726 [5,8]
+ CRUSH rule 5 x 727 [5,6]
+ CRUSH rule 5 x 728 [2,7]
+ CRUSH rule 5 x 729 [5,6]
+ CRUSH rule 5 x 730 [5,7]
+ CRUSH rule 5 x 731 [5,2]
+ CRUSH rule 5 x 732 [2,5]
+ CRUSH rule 5 x 733 [5,7]
+ CRUSH rule 5 x 734 [6,5]
+ CRUSH rule 5 x 735 [5,8]
+ CRUSH rule 5 x 736 [5,8]
+ CRUSH rule 5 x 737 [2,6]
+ CRUSH rule 5 x 738 [5,2]
+ CRUSH rule 5 x 739 [2,7]
+ CRUSH rule 5 x 740 [2,8]
+ CRUSH rule 5 x 741 [7,2]
+ CRUSH rule 5 x 742 [8,2]
+ CRUSH rule 5 x 743 [7,2]
+ CRUSH rule 5 x 744 [5,7]
+ CRUSH rule 5 x 745 [5,2]
+ CRUSH rule 5 x 746 [5,2]
+ CRUSH rule 5 x 747 [6,2]
+ CRUSH rule 5 x 748 [2,7]
+ CRUSH rule 5 x 749 [5,8]
+ CRUSH rule 5 x 750 [2,6]
+ CRUSH rule 5 x 751 [2,8]
+ CRUSH rule 5 x 752 [8,2]
+ CRUSH rule 5 x 753 [7,5]
+ CRUSH rule 5 x 754 [8,5]
+ CRUSH rule 5 x 755 [2,6]
+ CRUSH rule 5 x 756 [5,6]
+ CRUSH rule 5 x 757 [8,2]
+ CRUSH rule 5 x 758 [6,2]
+ CRUSH rule 5 x 759 [8,5]
+ CRUSH rule 5 x 760 [2,5]
+ CRUSH rule 5 x 761 [5,2]
+ CRUSH rule 5 x 762 [2,7]
+ CRUSH rule 5 x 763 [8,5]
+ CRUSH rule 5 x 764 [2,7]
+ CRUSH rule 5 x 765 [6,5]
+ CRUSH rule 5 x 766 [8,5]
+ CRUSH rule 5 x 767 [2,8]
+ CRUSH rule 5 x 768 [8,5]
+ CRUSH rule 5 x 769 [6,2]
+ CRUSH rule 5 x 770 [6,2]
+ CRUSH rule 5 x 771 [7,2]
+ CRUSH rule 5 x 772 [8,5]
+ CRUSH rule 5 x 773 [5,2]
+ CRUSH rule 5 x 774 [5,6]
+ CRUSH rule 5 x 775 [6,5]
+ CRUSH rule 5 x 776 [7,2]
+ CRUSH rule 5 x 777 [5,2]
+ CRUSH rule 5 x 778 [2,8]
+ CRUSH rule 5 x 779 [2,7]
+ CRUSH rule 5 x 780 [2,5]
+ CRUSH rule 5 x 781 [6,5]
+ CRUSH rule 5 x 782 [5,2]
+ CRUSH rule 5 x 783 [7,2]
+ CRUSH rule 5 x 784 [2,5]
+ CRUSH rule 5 x 785 [6,2]
+ CRUSH rule 5 x 786 [7,5]
+ CRUSH rule 5 x 787 [2,6]
+ CRUSH rule 5 x 788 [6,2]
+ CRUSH rule 5 x 789 [2,5]
+ CRUSH rule 5 x 790 [8,5]
+ CRUSH rule 5 x 791 [5,8]
+ CRUSH rule 5 x 792 [5,8]
+ CRUSH rule 5 x 793 [6,2]
+ CRUSH rule 5 x 794 [2,6]
+ CRUSH rule 5 x 795 [2,5]
+ CRUSH rule 5 x 796 [5,7]
+ CRUSH rule 5 x 797 [2,5]
+ CRUSH rule 5 x 798 [6,2]
+ CRUSH rule 5 x 799 [5,2]
+ CRUSH rule 5 x 800 [5,2]
+ CRUSH rule 5 x 801 [5,6]
+ CRUSH rule 5 x 802 [2,8]
+ CRUSH rule 5 x 803 [2,5]
+ CRUSH rule 5 x 804 [6,2]
+ CRUSH rule 5 x 805 [5,6]
+ CRUSH rule 5 x 806 [2,5]
+ CRUSH rule 5 x 807 [5,7]
+ CRUSH rule 5 x 808 [5,6]
+ CRUSH rule 5 x 809 [2,5]
+ CRUSH rule 5 x 810 [5,7]
+ CRUSH rule 5 x 811 [8,5]
+ CRUSH rule 5 x 812 [8,5]
+ CRUSH rule 5 x 813 [6,5]
+ CRUSH rule 5 x 814 [5,6]
+ CRUSH rule 5 x 815 [5,2]
+ CRUSH rule 5 x 816 [2,7]
+ CRUSH rule 5 x 817 [5,8]
+ CRUSH rule 5 x 818 [5,2]
+ CRUSH rule 5 x 819 [5,2]
+ CRUSH rule 5 x 820 [5,6]
+ CRUSH rule 5 x 821 [5,6]
+ CRUSH rule 5 x 822 [2,5]
+ CRUSH rule 5 x 823 [5,8]
+ CRUSH rule 5 x 824 [5,7]
+ CRUSH rule 5 x 825 [2,8]
+ CRUSH rule 5 x 826 [7,2]
+ CRUSH rule 5 x 827 [2,8]
+ CRUSH rule 5 x 828 [2,5]
+ CRUSH rule 5 x 829 [5,6]
+ CRUSH rule 5 x 830 [2,5]
+ CRUSH rule 5 x 831 [2,6]
+ CRUSH rule 5 x 832 [5,7]
+ CRUSH rule 5 x 833 [2,7]
+ CRUSH rule 5 x 834 [5,2]
+ CRUSH rule 5 x 835 [8,5]
+ CRUSH rule 5 x 836 [5,7]
+ CRUSH rule 5 x 837 [6,5]
+ CRUSH rule 5 x 838 [6,2]
+ CRUSH rule 5 x 839 [5,2]
+ CRUSH rule 5 x 840 [7,5]
+ CRUSH rule 5 x 841 [5,8]
+ CRUSH rule 5 x 842 [2,5]
+ CRUSH rule 5 x 843 [6,5]
+ CRUSH rule 5 x 844 [5,8]
+ CRUSH rule 5 x 845 [5,8]
+ CRUSH rule 5 x 846 [5,2]
+ CRUSH rule 5 x 847 [2,8]
+ CRUSH rule 5 x 848 [2,6]
+ CRUSH rule 5 x 849 [5,6]
+ CRUSH rule 5 x 850 [2,5]
+ CRUSH rule 5 x 851 [6,5]
+ CRUSH rule 5 x 852 [7,5]
+ CRUSH rule 5 x 853 [6,2]
+ CRUSH rule 5 x 854 [7,2]
+ CRUSH rule 5 x 855 [5,7]
+ CRUSH rule 5 x 856 [6,5]
+ CRUSH rule 5 x 857 [8,5]
+ CRUSH rule 5 x 858 [6,5]
+ CRUSH rule 5 x 859 [6,2]
+ CRUSH rule 5 x 860 [5,2]
+ CRUSH rule 5 x 861 [8,5]
+ CRUSH rule 5 x 862 [6,2]
+ CRUSH rule 5 x 863 [8,2]
+ CRUSH rule 5 x 864 [5,6]
+ CRUSH rule 5 x 865 [8,2]
+ CRUSH rule 5 x 866 [5,6]
+ CRUSH rule 5 x 867 [6,5]
+ CRUSH rule 5 x 868 [6,5]
+ CRUSH rule 5 x 869 [8,5]
+ CRUSH rule 5 x 870 [2,5]
+ CRUSH rule 5 x 871 [5,2]
+ CRUSH rule 5 x 872 [5,2]
+ CRUSH rule 5 x 873 [5,6]
+ CRUSH rule 5 x 874 [2,6]
+ CRUSH rule 5 x 875 [2,6]
+ CRUSH rule 5 x 876 [5,8]
+ CRUSH rule 5 x 877 [6,5]
+ CRUSH rule 5 x 878 [5,2]
+ CRUSH rule 5 x 879 [7,5]
+ CRUSH rule 5 x 880 [5,2]
+ CRUSH rule 5 x 881 [5,6]
+ CRUSH rule 5 x 882 [5,2]
+ CRUSH rule 5 x 883 [2,5]
+ CRUSH rule 5 x 884 [6,2]
+ CRUSH rule 5 x 885 [5,2]
+ CRUSH rule 5 x 886 [5,6]
+ CRUSH rule 5 x 887 [7,5]
+ CRUSH rule 5 x 888 [6,2]
+ CRUSH rule 5 x 889 [2,6]
+ CRUSH rule 5 x 890 [7,2]
+ CRUSH rule 5 x 891 [2,8]
+ CRUSH rule 5 x 892 [6,2]
+ CRUSH rule 5 x 893 [2,5]
+ CRUSH rule 5 x 894 [7,5]
+ CRUSH rule 5 x 895 [5,2]
+ CRUSH rule 5 x 896 [2,8]
+ CRUSH rule 5 x 897 [5,2]
+ CRUSH rule 5 x 898 [2,5]
+ CRUSH rule 5 x 899 [2,7]
+ CRUSH rule 5 x 900 [5,2]
+ CRUSH rule 5 x 901 [5,2]
+ CRUSH rule 5 x 902 [8,5]
+ CRUSH rule 5 x 903 [5,7]
+ CRUSH rule 5 x 904 [5,6]
+ CRUSH rule 5 x 905 [6,2]
+ CRUSH rule 5 x 906 [2,6]
+ CRUSH rule 5 x 907 [7,2]
+ CRUSH rule 5 x 908 [5,8]
+ CRUSH rule 5 x 909 [2,5]
+ CRUSH rule 5 x 910 [6,5]
+ CRUSH rule 5 x 911 [5,8]
+ CRUSH rule 5 x 912 [2,7]
+ CRUSH rule 5 x 913 [7,2]
+ CRUSH rule 5 x 914 [6,5]
+ CRUSH rule 5 x 915 [8,2]
+ CRUSH rule 5 x 916 [5,2]
+ CRUSH rule 5 x 917 [2,5]
+ CRUSH rule 5 x 918 [8,2]
+ CRUSH rule 5 x 919 [6,2]
+ CRUSH rule 5 x 920 [7,5]
+ CRUSH rule 5 x 921 [2,5]
+ CRUSH rule 5 x 922 [6,5]
+ CRUSH rule 5 x 923 [5,8]
+ CRUSH rule 5 x 924 [5,2]
+ CRUSH rule 5 x 925 [5,7]
+ CRUSH rule 5 x 926 [5,2]
+ CRUSH rule 5 x 927 [2,6]
+ CRUSH rule 5 x 928 [8,2]
+ CRUSH rule 5 x 929 [5,2]
+ CRUSH rule 5 x 930 [2,5]
+ CRUSH rule 5 x 931 [5,2]
+ CRUSH rule 5 x 932 [5,2]
+ CRUSH rule 5 x 933 [8,5]
+ CRUSH rule 5 x 934 [5,6]
+ CRUSH rule 5 x 935 [6,5]
+ CRUSH rule 5 x 936 [2,6]
+ CRUSH rule 5 x 937 [5,8]
+ CRUSH rule 5 x 938 [6,5]
+ CRUSH rule 5 x 939 [2,7]
+ CRUSH rule 5 x 940 [8,5]
+ CRUSH rule 5 x 941 [5,2]
+ CRUSH rule 5 x 942 [2,8]
+ CRUSH rule 5 x 943 [8,2]
+ CRUSH rule 5 x 944 [5,8]
+ CRUSH rule 5 x 945 [7,2]
+ CRUSH rule 5 x 946 [2,8]
+ CRUSH rule 5 x 947 [5,2]
+ CRUSH rule 5 x 948 [7,5]
+ CRUSH rule 5 x 949 [6,2]
+ CRUSH rule 5 x 950 [5,6]
+ CRUSH rule 5 x 951 [5,8]
+ CRUSH rule 5 x 952 [2,7]
+ CRUSH rule 5 x 953 [2,5]
+ CRUSH rule 5 x 954 [5,2]
+ CRUSH rule 5 x 955 [8,2]
+ CRUSH rule 5 x 956 [2,6]
+ CRUSH rule 5 x 957 [7,2]
+ CRUSH rule 5 x 958 [8,5]
+ CRUSH rule 5 x 959 [5,2]
+ CRUSH rule 5 x 960 [5,6]
+ CRUSH rule 5 x 961 [5,2]
+ CRUSH rule 5 x 962 [7,5]
+ CRUSH rule 5 x 963 [2,5]
+ CRUSH rule 5 x 964 [5,2]
+ CRUSH rule 5 x 965 [7,5]
+ CRUSH rule 5 x 966 [5,8]
+ CRUSH rule 5 x 967 [8,5]
+ CRUSH rule 5 x 968 [7,2]
+ CRUSH rule 5 x 969 [8,2]
+ CRUSH rule 5 x 970 [2,6]
+ CRUSH rule 5 x 971 [2,7]
+ CRUSH rule 5 x 972 [2,8]
+ CRUSH rule 5 x 973 [2,6]
+ CRUSH rule 5 x 974 [5,2]
+ CRUSH rule 5 x 975 [5,7]
+ CRUSH rule 5 x 976 [5,8]
+ CRUSH rule 5 x 977 [8,5]
+ CRUSH rule 5 x 978 [7,2]
+ CRUSH rule 5 x 979 [7,2]
+ CRUSH rule 5 x 980 [6,2]
+ CRUSH rule 5 x 981 [7,5]
+ CRUSH rule 5 x 982 [5,2]
+ CRUSH rule 5 x 983 [5,7]
+ CRUSH rule 5 x 984 [2,7]
+ CRUSH rule 5 x 985 [2,5]
+ CRUSH rule 5 x 986 [8,5]
+ CRUSH rule 5 x 987 [2,5]
+ CRUSH rule 5 x 988 [2,5]
+ CRUSH rule 5 x 989 [2,6]
+ CRUSH rule 5 x 990 [2,6]
+ CRUSH rule 5 x 991 [2,5]
+ CRUSH rule 5 x 992 [7,2]
+ CRUSH rule 5 x 993 [2,6]
+ CRUSH rule 5 x 994 [5,7]
+ CRUSH rule 5 x 995 [7,2]
+ CRUSH rule 5 x 996 [6,5]
+ CRUSH rule 5 x 997 [6,5]
+ CRUSH rule 5 x 998 [8,2]
+ CRUSH rule 5 x 999 [2,7]
+ CRUSH rule 5 x 1000 [8,5]
+ CRUSH rule 5 x 1001 [2,5]
+ CRUSH rule 5 x 1002 [2,5]
+ CRUSH rule 5 x 1003 [2,8]
+ CRUSH rule 5 x 1004 [6,2]
+ CRUSH rule 5 x 1005 [6,2]
+ CRUSH rule 5 x 1006 [2,6]
+ CRUSH rule 5 x 1007 [2,5]
+ CRUSH rule 5 x 1008 [2,7]
+ CRUSH rule 5 x 1009 [6,5]
+ CRUSH rule 5 x 1010 [5,2]
+ CRUSH rule 5 x 1011 [5,2]
+ CRUSH rule 5 x 1012 [5,2]
+ CRUSH rule 5 x 1013 [5,2]
+ CRUSH rule 5 x 1014 [2,8]
+ CRUSH rule 5 x 1015 [6,5]
+ CRUSH rule 5 x 1016 [2,5]
+ CRUSH rule 5 x 1017 [6,2]
+ CRUSH rule 5 x 1018 [5,2]
+ CRUSH rule 5 x 1019 [5,8]
+ CRUSH rule 5 x 1020 [5,2]
+ CRUSH rule 5 x 1021 [5,2]
+ CRUSH rule 5 x 1022 [2,6]
+ CRUSH rule 5 x 1023 [5,2]
+ rule 5 (chooseleaf-set) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 5 x 0 [2,5,7]
+ CRUSH rule 5 x 1 [2,8,5]
+ CRUSH rule 5 x 2 [2,5,7]
+ CRUSH rule 5 x 3 [8,2,5]
+ CRUSH rule 5 x 4 [5,2,7]
+ CRUSH rule 5 x 5 [7,2,5]
+ CRUSH rule 5 x 6 [2,6,5]
+ CRUSH rule 5 x 7 [5,8,2]
+ CRUSH rule 5 x 8 [5,6,2]
+ CRUSH rule 5 x 9 [2,5,8]
+ CRUSH rule 5 x 10 [2,7,5]
+ CRUSH rule 5 x 11 [2,7,5]
+ CRUSH rule 5 x 12 [2,5,6]
+ CRUSH rule 5 x 13 [5,8,2]
+ CRUSH rule 5 x 14 [7,2,5]
+ CRUSH rule 5 x 15 [7,2,5]
+ CRUSH rule 5 x 16 [5,6,2]
+ CRUSH rule 5 x 17 [5,2,6]
+ CRUSH rule 5 x 18 [2,5,6]
+ CRUSH rule 5 x 19 [7,5,2]
+ CRUSH rule 5 x 20 [2,5,7]
+ CRUSH rule 5 x 21 [5,7,2]
+ CRUSH rule 5 x 22 [8,5,2]
+ CRUSH rule 5 x 23 [5,6,2]
+ CRUSH rule 5 x 24 [2,7,5]
+ CRUSH rule 5 x 25 [5,7,2]
+ CRUSH rule 5 x 26 [2,8,5]
+ CRUSH rule 5 x 27 [5,2,8]
+ CRUSH rule 5 x 28 [6,2,5]
+ CRUSH rule 5 x 29 [8,5,2]
+ CRUSH rule 5 x 30 [5,7,2]
+ CRUSH rule 5 x 31 [8,2,5]
+ CRUSH rule 5 x 32 [5,6,2]
+ CRUSH rule 5 x 33 [2,7,5]
+ CRUSH rule 5 x 34 [2,5,7]
+ CRUSH rule 5 x 35 [2,8,5]
+ CRUSH rule 5 x 36 [5,8,2]
+ CRUSH rule 5 x 37 [2,5,6]
+ CRUSH rule 5 x 38 [5,8,2]
+ CRUSH rule 5 x 39 [5,7,2]
+ CRUSH rule 5 x 40 [7,2,5]
+ CRUSH rule 5 x 41 [2,6,5]
+ CRUSH rule 5 x 42 [5,6,2]
+ CRUSH rule 5 x 43 [2,5,7]
+ CRUSH rule 5 x 44 [2,6,5]
+ CRUSH rule 5 x 45 [8,2,5]
+ CRUSH rule 5 x 46 [2,5,8]
+ CRUSH rule 5 x 47 [5,2,8]
+ CRUSH rule 5 x 48 [5,6,2]
+ CRUSH rule 5 x 49 [5,7,2]
+ CRUSH rule 5 x 50 [5,2,7]
+ CRUSH rule 5 x 51 [5,6,2]
+ CRUSH rule 5 x 52 [8,2,5]
+ CRUSH rule 5 x 53 [5,8,2]
+ CRUSH rule 5 x 54 [7,5,2]
+ CRUSH rule 5 x 55 [8,2,5]
+ CRUSH rule 5 x 56 [6,5,2]
+ CRUSH rule 5 x 57 [5,8,2]
+ CRUSH rule 5 x 58 [2,8,5]
+ CRUSH rule 5 x 59 [5,2,7]
+ CRUSH rule 5 x 60 [5,2,6]
+ CRUSH rule 5 x 61 [5,6,2]
+ CRUSH rule 5 x 62 [7,2,5]
+ CRUSH rule 5 x 63 [5,6,2]
+ CRUSH rule 5 x 64 [5,2,8]
+ CRUSH rule 5 x 65 [7,5,2]
+ CRUSH rule 5 x 66 [5,6,2]
+ CRUSH rule 5 x 67 [5,2,8]
+ CRUSH rule 5 x 68 [2,5,8]
+ CRUSH rule 5 x 69 [5,2,6]
+ CRUSH rule 5 x 70 [7,2,5]
+ CRUSH rule 5 x 71 [2,8,5]
+ CRUSH rule 5 x 72 [6,2,5]
+ CRUSH rule 5 x 73 [2,7,5]
+ CRUSH rule 5 x 74 [2,7,5]
+ CRUSH rule 5 x 75 [5,2,6]
+ CRUSH rule 5 x 76 [5,2,7]
+ CRUSH rule 5 x 77 [7,2,5]
+ CRUSH rule 5 x 78 [2,5,8]
+ CRUSH rule 5 x 79 [5,2,7]
+ CRUSH rule 5 x 80 [2,5,6]
+ CRUSH rule 5 x 81 [2,5,6]
+ CRUSH rule 5 x 82 [7,2,5]
+ CRUSH rule 5 x 83 [2,6,5]
+ CRUSH rule 5 x 84 [7,2,5]
+ CRUSH rule 5 x 85 [5,8,2]
+ CRUSH rule 5 x 86 [2,6,5]
+ CRUSH rule 5 x 87 [2,7,5]
+ CRUSH rule 5 x 88 [2,6,5]
+ CRUSH rule 5 x 89 [5,2,7]
+ CRUSH rule 5 x 90 [6,5,2]
+ CRUSH rule 5 x 91 [5,8,2]
+ CRUSH rule 5 x 92 [2,8,5]
+ CRUSH rule 5 x 93 [7,5,2]
+ CRUSH rule 5 x 94 [2,5,8]
+ CRUSH rule 5 x 95 [7,5,2]
+ CRUSH rule 5 x 96 [5,6,2]
+ CRUSH rule 5 x 97 [8,5,2]
+ CRUSH rule 5 x 98 [2,7,5]
+ CRUSH rule 5 x 99 [2,7,5]
+ CRUSH rule 5 x 100 [2,7,5]
+ CRUSH rule 5 x 101 [5,7,2]
+ CRUSH rule 5 x 102 [5,2,7]
+ CRUSH rule 5 x 103 [5,7,2]
+ CRUSH rule 5 x 104 [7,5,2]
+ CRUSH rule 5 x 105 [2,5,6]
+ CRUSH rule 5 x 106 [2,6,5]
+ CRUSH rule 5 x 107 [5,2,6]
+ CRUSH rule 5 x 108 [7,2,5]
+ CRUSH rule 5 x 109 [2,5,6]
+ CRUSH rule 5 x 110 [5,2,7]
+ CRUSH rule 5 x 111 [2,5,6]
+ CRUSH rule 5 x 112 [2,6,5]
+ CRUSH rule 5 x 113 [6,2,5]
+ CRUSH rule 5 x 114 [7,5,2]
+ CRUSH rule 5 x 115 [8,2,5]
+ CRUSH rule 5 x 116 [2,6,5]
+ CRUSH rule 5 x 117 [7,5,2]
+ CRUSH rule 5 x 118 [2,5,8]
+ CRUSH rule 5 x 119 [5,6,2]
+ CRUSH rule 5 x 120 [2,5,7]
+ CRUSH rule 5 x 121 [2,7,5]
+ CRUSH rule 5 x 122 [8,5,2]
+ CRUSH rule 5 x 123 [2,5,8]
+ CRUSH rule 5 x 124 [5,2,8]
+ CRUSH rule 5 x 125 [2,7,5]
+ CRUSH rule 5 x 126 [5,2,6]
+ CRUSH rule 5 x 127 [5,6,2]
+ CRUSH rule 5 x 128 [5,6,2]
+ CRUSH rule 5 x 129 [2,5,7]
+ CRUSH rule 5 x 130 [5,8,2]
+ CRUSH rule 5 x 131 [2,5,8]
+ CRUSH rule 5 x 132 [2,5,6]
+ CRUSH rule 5 x 133 [5,6,2]
+ CRUSH rule 5 x 134 [2,8,5]
+ CRUSH rule 5 x 135 [5,6,2]
+ CRUSH rule 5 x 136 [2,5,6]
+ CRUSH rule 5 x 137 [7,5,2]
+ CRUSH rule 5 x 138 [8,5,2]
+ CRUSH rule 5 x 139 [5,2,7]
+ CRUSH rule 5 x 140 [2,6,5]
+ CRUSH rule 5 x 141 [6,2,5]
+ CRUSH rule 5 x 142 [5,2,8]
+ CRUSH rule 5 x 143 [5,8,2]
+ CRUSH rule 5 x 144 [8,2,5]
+ CRUSH rule 5 x 145 [8,5,2]
+ CRUSH rule 5 x 146 [2,6,5]
+ CRUSH rule 5 x 147 [2,8,5]
+ CRUSH rule 5 x 148 [5,2,7]
+ CRUSH rule 5 x 149 [5,8,2]
+ CRUSH rule 5 x 150 [2,6,5]
+ CRUSH rule 5 x 151 [5,6,2]
+ CRUSH rule 5 x 152 [8,5,2]
+ CRUSH rule 5 x 153 [8,5,2]
+ CRUSH rule 5 x 154 [5,2,7]
+ CRUSH rule 5 x 155 [5,7,2]
+ CRUSH rule 5 x 156 [5,2,6]
+ CRUSH rule 5 x 157 [5,2,6]
+ CRUSH rule 5 x 158 [2,8,5]
+ CRUSH rule 5 x 159 [7,2,5]
+ CRUSH rule 5 x 160 [2,8,5]
+ CRUSH rule 5 x 161 [2,5,6]
+ CRUSH rule 5 x 162 [2,6,5]
+ CRUSH rule 5 x 163 [5,6,2]
+ CRUSH rule 5 x 164 [7,2,5]
+ CRUSH rule 5 x 165 [7,2,5]
+ CRUSH rule 5 x 166 [2,5,6]
+ CRUSH rule 5 x 167 [2,7,5]
+ CRUSH rule 5 x 168 [5,2,7]
+ CRUSH rule 5 x 169 [2,6,5]
+ CRUSH rule 5 x 170 [2,5,8]
+ CRUSH rule 5 x 171 [7,5,2]
+ CRUSH rule 5 x 172 [2,7,5]
+ CRUSH rule 5 x 173 [8,5,2]
+ CRUSH rule 5 x 174 [2,5,7]
+ CRUSH rule 5 x 175 [6,2,5]
+ CRUSH rule 5 x 176 [5,2,7]
+ CRUSH rule 5 x 177 [5,2,6]
+ CRUSH rule 5 x 178 [5,2,8]
+ CRUSH rule 5 x 179 [5,2,7]
+ CRUSH rule 5 x 180 [5,8,2]
+ CRUSH rule 5 x 181 [6,2,5]
+ CRUSH rule 5 x 182 [8,5,2]
+ CRUSH rule 5 x 183 [7,5,2]
+ CRUSH rule 5 x 184 [5,7,2]
+ CRUSH rule 5 x 185 [6,2,5]
+ CRUSH rule 5 x 186 [2,5,7]
+ CRUSH rule 5 x 187 [2,6,5]
+ CRUSH rule 5 x 188 [2,8,5]
+ CRUSH rule 5 x 189 [2,7,5]
+ CRUSH rule 5 x 190 [5,2,8]
+ CRUSH rule 5 x 191 [7,2,5]
+ CRUSH rule 5 x 192 [5,2,8]
+ CRUSH rule 5 x 193 [5,2,6]
+ CRUSH rule 5 x 194 [2,5,8]
+ CRUSH rule 5 x 195 [6,5,2]
+ CRUSH rule 5 x 196 [6,2,5]
+ CRUSH rule 5 x 197 [6,5,2]
+ CRUSH rule 5 x 198 [2,5,6]
+ CRUSH rule 5 x 199 [2,5,7]
+ CRUSH rule 5 x 200 [2,5,6]
+ CRUSH rule 5 x 201 [7,2,5]
+ CRUSH rule 5 x 202 [6,5,2]
+ CRUSH rule 5 x 203 [5,8,2]
+ CRUSH rule 5 x 204 [2,5,7]
+ CRUSH rule 5 x 205 [2,7,5]
+ CRUSH rule 5 x 206 [2,7,5]
+ CRUSH rule 5 x 207 [5,2,7]
+ CRUSH rule 5 x 208 [7,2,5]
+ CRUSH rule 5 x 209 [2,8,5]
+ CRUSH rule 5 x 210 [2,5,8]
+ CRUSH rule 5 x 211 [5,2,8]
+ CRUSH rule 5 x 212 [7,5,2]
+ CRUSH rule 5 x 213 [8,5,2]
+ CRUSH rule 5 x 214 [5,8,2]
+ CRUSH rule 5 x 215 [8,2,5]
+ CRUSH rule 5 x 216 [5,2,6]
+ CRUSH rule 5 x 217 [2,7,5]
+ CRUSH rule 5 x 218 [2,7,5]
+ CRUSH rule 5 x 219 [5,8,2]
+ CRUSH rule 5 x 220 [5,7,2]
+ CRUSH rule 5 x 221 [5,6,2]
+ CRUSH rule 5 x 222 [6,5,2]
+ CRUSH rule 5 x 223 [2,5,6]
+ CRUSH rule 5 x 224 [2,5,8]
+ CRUSH rule 5 x 225 [8,2,5]
+ CRUSH rule 5 x 226 [7,2,5]
+ CRUSH rule 5 x 227 [5,2,6]
+ CRUSH rule 5 x 228 [5,6,2]
+ CRUSH rule 5 x 229 [5,8,2]
+ CRUSH rule 5 x 230 [5,7,2]
+ CRUSH rule 5 x 231 [5,7,2]
+ CRUSH rule 5 x 232 [2,7,5]
+ CRUSH rule 5 x 233 [5,7,2]
+ CRUSH rule 5 x 234 [2,5,6]
+ CRUSH rule 5 x 235 [5,8,2]
+ CRUSH rule 5 x 236 [5,2,7]
+ CRUSH rule 5 x 237 [5,7,2]
+ CRUSH rule 5 x 238 [5,2,6]
+ CRUSH rule 5 x 239 [8,5,2]
+ CRUSH rule 5 x 240 [5,7,2]
+ CRUSH rule 5 x 241 [5,2,8]
+ CRUSH rule 5 x 242 [5,2,6]
+ CRUSH rule 5 x 243 [5,7,2]
+ CRUSH rule 5 x 244 [5,6,2]
+ CRUSH rule 5 x 245 [7,2,5]
+ CRUSH rule 5 x 246 [2,5,8]
+ CRUSH rule 5 x 247 [6,2,5]
+ CRUSH rule 5 x 248 [8,2,5]
+ CRUSH rule 5 x 249 [2,5,7]
+ CRUSH rule 5 x 250 [2,5,6]
+ CRUSH rule 5 x 251 [2,5,6]
+ CRUSH rule 5 x 252 [5,7,2]
+ CRUSH rule 5 x 253 [5,2,6]
+ CRUSH rule 5 x 254 [5,2,7]
+ CRUSH rule 5 x 255 [2,7,5]
+ CRUSH rule 5 x 256 [5,7,2]
+ CRUSH rule 5 x 257 [2,8,5]
+ CRUSH rule 5 x 258 [5,2,6]
+ CRUSH rule 5 x 259 [5,6,2]
+ CRUSH rule 5 x 260 [5,6,2]
+ CRUSH rule 5 x 261 [8,5,2]
+ CRUSH rule 5 x 262 [5,6,2]
+ CRUSH rule 5 x 263 [6,2,5]
+ CRUSH rule 5 x 264 [5,6,2]
+ CRUSH rule 5 x 265 [8,5,2]
+ CRUSH rule 5 x 266 [8,2,5]
+ CRUSH rule 5 x 267 [2,5,8]
+ CRUSH rule 5 x 268 [2,7,5]
+ CRUSH rule 5 x 269 [2,8,5]
+ CRUSH rule 5 x 270 [5,2,7]
+ CRUSH rule 5 x 271 [7,5,2]
+ CRUSH rule 5 x 272 [2,8,5]
+ CRUSH rule 5 x 273 [5,2,7]
+ CRUSH rule 5 x 274 [6,5,2]
+ CRUSH rule 5 x 275 [5,7,2]
+ CRUSH rule 5 x 276 [7,2,5]
+ CRUSH rule 5 x 277 [6,5,2]
+ CRUSH rule 5 x 278 [6,2,5]
+ CRUSH rule 5 x 279 [8,5,2]
+ CRUSH rule 5 x 280 [2,6,5]
+ CRUSH rule 5 x 281 [8,2,5]
+ CRUSH rule 5 x 282 [5,2,6]
+ CRUSH rule 5 x 283 [8,2,5]
+ CRUSH rule 5 x 284 [6,5,2]
+ CRUSH rule 5 x 285 [5,7,2]
+ CRUSH rule 5 x 286 [2,6,5]
+ CRUSH rule 5 x 287 [2,5,6]
+ CRUSH rule 5 x 288 [8,2,5]
+ CRUSH rule 5 x 289 [5,6,2]
+ CRUSH rule 5 x 290 [2,5,7]
+ CRUSH rule 5 x 291 [2,5,8]
+ CRUSH rule 5 x 292 [8,2,5]
+ CRUSH rule 5 x 293 [6,2,5]
+ CRUSH rule 5 x 294 [7,5,2]
+ CRUSH rule 5 x 295 [5,8,2]
+ CRUSH rule 5 x 296 [5,2,6]
+ CRUSH rule 5 x 297 [6,2,5]
+ CRUSH rule 5 x 298 [2,5,7]
+ CRUSH rule 5 x 299 [2,8,5]
+ CRUSH rule 5 x 300 [8,5,2]
+ CRUSH rule 5 x 301 [2,8,5]
+ CRUSH rule 5 x 302 [5,2,6]
+ CRUSH rule 5 x 303 [7,5,2]
+ CRUSH rule 5 x 304 [2,7,5]
+ CRUSH rule 5 x 305 [5,8,2]
+ CRUSH rule 5 x 306 [2,7,5]
+ CRUSH rule 5 x 307 [2,7,5]
+ CRUSH rule 5 x 308 [2,8,5]
+ CRUSH rule 5 x 309 [7,5,2]
+ CRUSH rule 5 x 310 [5,2,7]
+ CRUSH rule 5 x 311 [5,6,2]
+ CRUSH rule 5 x 312 [2,6,5]
+ CRUSH rule 5 x 313 [5,2,6]
+ CRUSH rule 5 x 314 [5,2,6]
+ CRUSH rule 5 x 315 [2,5,8]
+ CRUSH rule 5 x 316 [6,5,2]
+ CRUSH rule 5 x 317 [2,6,5]
+ CRUSH rule 5 x 318 [8,2,5]
+ CRUSH rule 5 x 319 [5,2,8]
+ CRUSH rule 5 x 320 [5,7,2]
+ CRUSH rule 5 x 321 [2,5,8]
+ CRUSH rule 5 x 322 [2,7,5]
+ CRUSH rule 5 x 323 [5,7,2]
+ CRUSH rule 5 x 324 [7,2,5]
+ CRUSH rule 5 x 325 [5,6,2]
+ CRUSH rule 5 x 326 [5,2,6]
+ CRUSH rule 5 x 327 [2,6,5]
+ CRUSH rule 5 x 328 [7,5,2]
+ CRUSH rule 5 x 329 [5,6,2]
+ CRUSH rule 5 x 330 [5,7,2]
+ CRUSH rule 5 x 331 [2,6,5]
+ CRUSH rule 5 x 332 [2,5,6]
+ CRUSH rule 5 x 333 [6,5,2]
+ CRUSH rule 5 x 334 [8,5,2]
+ CRUSH rule 5 x 335 [7,2,5]
+ CRUSH rule 5 x 336 [5,6,2]
+ CRUSH rule 5 x 337 [7,2,5]
+ CRUSH rule 5 x 338 [5,6,2]
+ CRUSH rule 5 x 339 [7,5,2]
+ CRUSH rule 5 x 340 [2,8,5]
+ CRUSH rule 5 x 341 [5,2,7]
+ CRUSH rule 5 x 342 [2,7,5]
+ CRUSH rule 5 x 343 [6,5,2]
+ CRUSH rule 5 x 344 [6,2,5]
+ CRUSH rule 5 x 345 [5,7,2]
+ CRUSH rule 5 x 346 [8,2,5]
+ CRUSH rule 5 x 347 [5,2,7]
+ CRUSH rule 5 x 348 [8,2,5]
+ CRUSH rule 5 x 349 [2,6,5]
+ CRUSH rule 5 x 350 [8,5,2]
+ CRUSH rule 5 x 351 [5,6,2]
+ CRUSH rule 5 x 352 [2,8,5]
+ CRUSH rule 5 x 353 [6,5,2]
+ CRUSH rule 5 x 354 [2,5,6]
+ CRUSH rule 5 x 355 [5,8,2]
+ CRUSH rule 5 x 356 [5,2,6]
+ CRUSH rule 5 x 357 [6,2,5]
+ CRUSH rule 5 x 358 [2,8,5]
+ CRUSH rule 5 x 359 [6,2,5]
+ CRUSH rule 5 x 360 [5,2,7]
+ CRUSH rule 5 x 361 [8,5,2]
+ CRUSH rule 5 x 362 [5,2,6]
+ CRUSH rule 5 x 363 [5,2,6]
+ CRUSH rule 5 x 364 [2,5,6]
+ CRUSH rule 5 x 365 [6,5,2]
+ CRUSH rule 5 x 366 [7,2,5]
+ CRUSH rule 5 x 367 [5,2,6]
+ CRUSH rule 5 x 368 [7,5,2]
+ CRUSH rule 5 x 369 [5,7,2]
+ CRUSH rule 5 x 370 [8,2,5]
+ CRUSH rule 5 x 371 [2,5,6]
+ CRUSH rule 5 x 372 [5,2,8]
+ CRUSH rule 5 x 373 [2,6,5]
+ CRUSH rule 5 x 374 [5,8,2]
+ CRUSH rule 5 x 375 [6,5,2]
+ CRUSH rule 5 x 376 [7,2,5]
+ CRUSH rule 5 x 377 [2,5,6]
+ CRUSH rule 5 x 378 [2,8,5]
+ CRUSH rule 5 x 379 [8,5,2]
+ CRUSH rule 5 x 380 [2,5,8]
+ CRUSH rule 5 x 381 [2,5,7]
+ CRUSH rule 5 x 382 [2,5,8]
+ CRUSH rule 5 x 383 [5,6,2]
+ CRUSH rule 5 x 384 [7,2,5]
+ CRUSH rule 5 x 385 [7,5,2]
+ CRUSH rule 5 x 386 [2,5,6]
+ CRUSH rule 5 x 387 [2,5,6]
+ CRUSH rule 5 x 388 [5,2,8]
+ CRUSH rule 5 x 389 [2,5,7]
+ CRUSH rule 5 x 390 [5,6,2]
+ CRUSH rule 5 x 391 [5,6,2]
+ CRUSH rule 5 x 392 [2,8,5]
+ CRUSH rule 5 x 393 [5,2,6]
+ CRUSH rule 5 x 394 [5,7,2]
+ CRUSH rule 5 x 395 [5,2,8]
+ CRUSH rule 5 x 396 [5,2,7]
+ CRUSH rule 5 x 397 [2,5,8]
+ CRUSH rule 5 x 398 [2,5,8]
+ CRUSH rule 5 x 399 [8,5,2]
+ CRUSH rule 5 x 400 [8,2,5]
+ CRUSH rule 5 x 401 [2,5,8]
+ CRUSH rule 5 x 402 [7,5,2]
+ CRUSH rule 5 x 403 [2,5,8]
+ CRUSH rule 5 x 404 [5,2,8]
+ CRUSH rule 5 x 405 [6,5,2]
+ CRUSH rule 5 x 406 [2,6,5]
+ CRUSH rule 5 x 407 [2,8,5]
+ CRUSH rule 5 x 408 [5,2,6]
+ CRUSH rule 5 x 409 [7,5,2]
+ CRUSH rule 5 x 410 [8,5,2]
+ CRUSH rule 5 x 411 [2,8,5]
+ CRUSH rule 5 x 412 [2,5,8]
+ CRUSH rule 5 x 413 [5,2,8]
+ CRUSH rule 5 x 414 [5,2,6]
+ CRUSH rule 5 x 415 [2,6,5]
+ CRUSH rule 5 x 416 [2,6,5]
+ CRUSH rule 5 x 417 [8,2,5]
+ CRUSH rule 5 x 418 [7,2,5]
+ CRUSH rule 5 x 419 [8,5,2]
+ CRUSH rule 5 x 420 [2,5,6]
+ CRUSH rule 5 x 421 [8,5,2]
+ CRUSH rule 5 x 422 [6,5,2]
+ CRUSH rule 5 x 423 [2,5,6]
+ CRUSH rule 5 x 424 [8,5,2]
+ CRUSH rule 5 x 425 [2,5,7]
+ CRUSH rule 5 x 426 [6,2,5]
+ CRUSH rule 5 x 427 [2,7,5]
+ CRUSH rule 5 x 428 [5,7,2]
+ CRUSH rule 5 x 429 [5,6,2]
+ CRUSH rule 5 x 430 [5,6,2]
+ CRUSH rule 5 x 431 [5,2,7]
+ CRUSH rule 5 x 432 [7,2,5]
+ CRUSH rule 5 x 433 [6,5,2]
+ CRUSH rule 5 x 434 [5,2,8]
+ CRUSH rule 5 x 435 [2,5,6]
+ CRUSH rule 5 x 436 [5,2,7]
+ CRUSH rule 5 x 437 [7,5,2]
+ CRUSH rule 5 x 438 [2,5,8]
+ CRUSH rule 5 x 439 [2,5,7]
+ CRUSH rule 5 x 440 [2,7,5]
+ CRUSH rule 5 x 441 [5,7,2]
+ CRUSH rule 5 x 442 [2,5,7]
+ CRUSH rule 5 x 443 [6,2,5]
+ CRUSH rule 5 x 444 [7,2,5]
+ CRUSH rule 5 x 445 [6,5,2]
+ CRUSH rule 5 x 446 [5,2,8]
+ CRUSH rule 5 x 447 [2,5,7]
+ CRUSH rule 5 x 448 [7,2,5]
+ CRUSH rule 5 x 449 [7,5,2]
+ CRUSH rule 5 x 450 [5,2,8]
+ CRUSH rule 5 x 451 [6,5,2]
+ CRUSH rule 5 x 452 [8,5,2]
+ CRUSH rule 5 x 453 [6,5,2]
+ CRUSH rule 5 x 454 [6,5,2]
+ CRUSH rule 5 x 455 [2,7,5]
+ CRUSH rule 5 x 456 [6,2,5]
+ CRUSH rule 5 x 457 [7,2,5]
+ CRUSH rule 5 x 458 [2,8,5]
+ CRUSH rule 5 x 459 [2,7,5]
+ CRUSH rule 5 x 460 [6,5,2]
+ CRUSH rule 5 x 461 [6,5,2]
+ CRUSH rule 5 x 462 [8,2,5]
+ CRUSH rule 5 x 463 [6,2,5]
+ CRUSH rule 5 x 464 [7,5,2]
+ CRUSH rule 5 x 465 [7,2,5]
+ CRUSH rule 5 x 466 [5,8,2]
+ CRUSH rule 5 x 467 [6,5,2]
+ CRUSH rule 5 x 468 [7,2,5]
+ CRUSH rule 5 x 469 [7,2,5]
+ CRUSH rule 5 x 470 [5,2,6]
+ CRUSH rule 5 x 471 [2,7,5]
+ CRUSH rule 5 x 472 [5,2,8]
+ CRUSH rule 5 x 473 [2,5,6]
+ CRUSH rule 5 x 474 [6,2,5]
+ CRUSH rule 5 x 475 [6,2,5]
+ CRUSH rule 5 x 476 [5,6,2]
+ CRUSH rule 5 x 477 [5,8,2]
+ CRUSH rule 5 x 478 [6,2,5]
+ CRUSH rule 5 x 479 [2,5,8]
+ CRUSH rule 5 x 480 [2,8,5]
+ CRUSH rule 5 x 481 [2,5,7]
+ CRUSH rule 5 x 482 [5,7,2]
+ CRUSH rule 5 x 483 [2,6,5]
+ CRUSH rule 5 x 484 [2,7,5]
+ CRUSH rule 5 x 485 [5,7,2]
+ CRUSH rule 5 x 486 [5,2,7]
+ CRUSH rule 5 x 487 [5,2,8]
+ CRUSH rule 5 x 488 [5,7,2]
+ CRUSH rule 5 x 489 [2,8,5]
+ CRUSH rule 5 x 490 [6,5,2]
+ CRUSH rule 5 x 491 [2,7,5]
+ CRUSH rule 5 x 492 [6,5,2]
+ CRUSH rule 5 x 493 [2,7,5]
+ CRUSH rule 5 x 494 [2,7,5]
+ CRUSH rule 5 x 495 [5,2,8]
+ CRUSH rule 5 x 496 [7,5,2]
+ CRUSH rule 5 x 497 [5,7,2]
+ CRUSH rule 5 x 498 [2,5,8]
+ CRUSH rule 5 x 499 [8,5,2]
+ CRUSH rule 5 x 500 [5,6,2]
+ CRUSH rule 5 x 501 [2,7,5]
+ CRUSH rule 5 x 502 [7,2,5]
+ CRUSH rule 5 x 503 [2,5,7]
+ CRUSH rule 5 x 504 [5,6,2]
+ CRUSH rule 5 x 505 [2,7,5]
+ CRUSH rule 5 x 506 [5,2,8]
+ CRUSH rule 5 x 507 [6,2,5]
+ CRUSH rule 5 x 508 [2,5,8]
+ CRUSH rule 5 x 509 [7,5,2]
+ CRUSH rule 5 x 510 [6,2,5]
+ CRUSH rule 5 x 511 [5,8,2]
+ CRUSH rule 5 x 512 [7,2,5]
+ CRUSH rule 5 x 513 [7,2,5]
+ CRUSH rule 5 x 514 [5,6,2]
+ CRUSH rule 5 x 515 [8,5,2]
+ CRUSH rule 5 x 516 [5,2,8]
+ CRUSH rule 5 x 517 [7,2,5]
+ CRUSH rule 5 x 518 [5,6,2]
+ CRUSH rule 5 x 519 [7,5,2]
+ CRUSH rule 5 x 520 [2,6,5]
+ CRUSH rule 5 x 521 [8,2,5]
+ CRUSH rule 5 x 522 [6,2,5]
+ CRUSH rule 5 x 523 [5,2,7]
+ CRUSH rule 5 x 524 [2,5,8]
+ CRUSH rule 5 x 525 [2,5,6]
+ CRUSH rule 5 x 526 [2,5,8]
+ CRUSH rule 5 x 527 [2,5,6]
+ CRUSH rule 5 x 528 [5,2,6]
+ CRUSH rule 5 x 529 [5,7,2]
+ CRUSH rule 5 x 530 [6,5,2]
+ CRUSH rule 5 x 531 [6,2,5]
+ CRUSH rule 5 x 532 [6,5,2]
+ CRUSH rule 5 x 533 [5,6,2]
+ CRUSH rule 5 x 534 [7,5,2]
+ CRUSH rule 5 x 535 [8,2,5]
+ CRUSH rule 5 x 536 [6,2,5]
+ CRUSH rule 5 x 537 [5,7,2]
+ CRUSH rule 5 x 538 [6,5,2]
+ CRUSH rule 5 x 539 [8,5,2]
+ CRUSH rule 5 x 540 [2,6,5]
+ CRUSH rule 5 x 541 [2,5,8]
+ CRUSH rule 5 x 542 [5,2,8]
+ CRUSH rule 5 x 543 [6,2,5]
+ CRUSH rule 5 x 544 [5,7,2]
+ CRUSH rule 5 x 545 [5,7,2]
+ CRUSH rule 5 x 546 [6,2,5]
+ CRUSH rule 5 x 547 [8,2,5]
+ CRUSH rule 5 x 548 [5,2,8]
+ CRUSH rule 5 x 549 [5,8,2]
+ CRUSH rule 5 x 550 [2,5,7]
+ CRUSH rule 5 x 551 [7,5,2]
+ CRUSH rule 5 x 552 [5,8,2]
+ CRUSH rule 5 x 553 [5,2,7]
+ CRUSH rule 5 x 554 [2,8,5]
+ CRUSH rule 5 x 555 [5,2,8]
+ CRUSH rule 5 x 556 [5,6,2]
+ CRUSH rule 5 x 557 [7,5,2]
+ CRUSH rule 5 x 558 [5,2,6]
+ CRUSH rule 5 x 559 [5,2,6]
+ CRUSH rule 5 x 560 [8,5,2]
+ CRUSH rule 5 x 561 [6,5,2]
+ CRUSH rule 5 x 562 [5,2,6]
+ CRUSH rule 5 x 563 [2,6,5]
+ CRUSH rule 5 x 564 [5,2,7]
+ CRUSH rule 5 x 565 [5,6,2]
+ CRUSH rule 5 x 566 [5,7,2]
+ CRUSH rule 5 x 567 [5,6,2]
+ CRUSH rule 5 x 568 [7,5,2]
+ CRUSH rule 5 x 569 [5,2,7]
+ CRUSH rule 5 x 570 [2,5,8]
+ CRUSH rule 5 x 571 [5,7,2]
+ CRUSH rule 5 x 572 [5,2,8]
+ CRUSH rule 5 x 573 [5,2,7]
+ CRUSH rule 5 x 574 [2,5,8]
+ CRUSH rule 5 x 575 [8,2,5]
+ CRUSH rule 5 x 576 [5,6,2]
+ CRUSH rule 5 x 577 [8,2,5]
+ CRUSH rule 5 x 578 [6,2,5]
+ CRUSH rule 5 x 579 [5,2,6]
+ CRUSH rule 5 x 580 [5,2,7]
+ CRUSH rule 5 x 581 [7,2,5]
+ CRUSH rule 5 x 582 [2,8,5]
+ CRUSH rule 5 x 583 [6,2,5]
+ CRUSH rule 5 x 584 [8,2,5]
+ CRUSH rule 5 x 585 [7,2,5]
+ CRUSH rule 5 x 586 [2,7,5]
+ CRUSH rule 5 x 587 [2,5,7]
+ CRUSH rule 5 x 588 [5,7,2]
+ CRUSH rule 5 x 589 [7,2,5]
+ CRUSH rule 5 x 590 [6,2,5]
+ CRUSH rule 5 x 591 [5,2,8]
+ CRUSH rule 5 x 592 [2,5,7]
+ CRUSH rule 5 x 593 [2,8,5]
+ CRUSH rule 5 x 594 [2,7,5]
+ CRUSH rule 5 x 595 [7,2,5]
+ CRUSH rule 5 x 596 [5,2,6]
+ CRUSH rule 5 x 597 [5,2,7]
+ CRUSH rule 5 x 598 [5,2,6]
+ CRUSH rule 5 x 599 [5,2,8]
+ CRUSH rule 5 x 600 [7,2,5]
+ CRUSH rule 5 x 601 [2,7,5]
+ CRUSH rule 5 x 602 [5,7,2]
+ CRUSH rule 5 x 603 [5,2,6]
+ CRUSH rule 5 x 604 [7,5,2]
+ CRUSH rule 5 x 605 [5,2,7]
+ CRUSH rule 5 x 606 [2,7,5]
+ CRUSH rule 5 x 607 [2,5,8]
+ CRUSH rule 5 x 608 [5,2,7]
+ CRUSH rule 5 x 609 [5,2,8]
+ CRUSH rule 5 x 610 [5,7,2]
+ CRUSH rule 5 x 611 [2,8,5]
+ CRUSH rule 5 x 612 [2,6,5]
+ CRUSH rule 5 x 613 [7,2,5]
+ CRUSH rule 5 x 614 [7,2,5]
+ CRUSH rule 5 x 615 [6,2,5]
+ CRUSH rule 5 x 616 [2,8,5]
+ CRUSH rule 5 x 617 [6,2,5]
+ CRUSH rule 5 x 618 [7,5,2]
+ CRUSH rule 5 x 619 [5,2,8]
+ CRUSH rule 5 x 620 [5,2,6]
+ CRUSH rule 5 x 621 [5,8,2]
+ CRUSH rule 5 x 622 [2,5,8]
+ CRUSH rule 5 x 623 [2,6,5]
+ CRUSH rule 5 x 624 [5,2,8]
+ CRUSH rule 5 x 625 [2,5,7]
+ CRUSH rule 5 x 626 [7,2,5]
+ CRUSH rule 5 x 627 [2,7,5]
+ CRUSH rule 5 x 628 [8,2,5]
+ CRUSH rule 5 x 629 [2,6,5]
+ CRUSH rule 5 x 630 [2,7,5]
+ CRUSH rule 5 x 631 [2,7,5]
+ CRUSH rule 5 x 632 [7,2,5]
+ CRUSH rule 5 x 633 [8,5,2]
+ CRUSH rule 5 x 634 [2,5,7]
+ CRUSH rule 5 x 635 [5,6,2]
+ CRUSH rule 5 x 636 [2,5,8]
+ CRUSH rule 5 x 637 [5,2,7]
+ CRUSH rule 5 x 638 [6,2,5]
+ CRUSH rule 5 x 639 [5,2,8]
+ CRUSH rule 5 x 640 [5,2,8]
+ CRUSH rule 5 x 641 [7,2,5]
+ CRUSH rule 5 x 642 [2,7,5]
+ CRUSH rule 5 x 643 [5,2,8]
+ CRUSH rule 5 x 644 [8,2,5]
+ CRUSH rule 5 x 645 [5,7,2]
+ CRUSH rule 5 x 646 [8,2,5]
+ CRUSH rule 5 x 647 [7,2,5]
+ CRUSH rule 5 x 648 [2,6,5]
+ CRUSH rule 5 x 649 [5,7,2]
+ CRUSH rule 5 x 650 [7,5,2]
+ CRUSH rule 5 x 651 [5,7,2]
+ CRUSH rule 5 x 652 [5,7,2]
+ CRUSH rule 5 x 653 [8,5,2]
+ CRUSH rule 5 x 654 [7,5,2]
+ CRUSH rule 5 x 655 [2,5,7]
+ CRUSH rule 5 x 656 [5,7,2]
+ CRUSH rule 5 x 657 [6,2,5]
+ CRUSH rule 5 x 658 [5,6,2]
+ CRUSH rule 5 x 659 [5,6,2]
+ CRUSH rule 5 x 660 [7,5,2]
+ CRUSH rule 5 x 661 [2,8,5]
+ CRUSH rule 5 x 662 [5,2,7]
+ CRUSH rule 5 x 663 [2,5,8]
+ CRUSH rule 5 x 664 [2,5,7]
+ CRUSH rule 5 x 665 [5,7,2]
+ CRUSH rule 5 x 666 [2,8,5]
+ CRUSH rule 5 x 667 [2,5,7]
+ CRUSH rule 5 x 668 [5,7,2]
+ CRUSH rule 5 x 669 [6,5,2]
+ CRUSH rule 5 x 670 [5,2,6]
+ CRUSH rule 5 x 671 [2,7,5]
+ CRUSH rule 5 x 672 [5,2,7]
+ CRUSH rule 5 x 673 [5,2,7]
+ CRUSH rule 5 x 674 [5,2,8]
+ CRUSH rule 5 x 675 [2,8,5]
+ CRUSH rule 5 x 676 [2,5,8]
+ CRUSH rule 5 x 677 [5,2,7]
+ CRUSH rule 5 x 678 [2,5,8]
+ CRUSH rule 5 x 679 [6,2,5]
+ CRUSH rule 5 x 680 [2,5,6]
+ CRUSH rule 5 x 681 [5,7,2]
+ CRUSH rule 5 x 682 [2,5,7]
+ CRUSH rule 5 x 683 [2,5,6]
+ CRUSH rule 5 x 684 [7,2,5]
+ CRUSH rule 5 x 685 [7,2,5]
+ CRUSH rule 5 x 686 [2,5,8]
+ CRUSH rule 5 x 687 [5,6,2]
+ CRUSH rule 5 x 688 [5,7,2]
+ CRUSH rule 5 x 689 [6,5,2]
+ CRUSH rule 5 x 690 [8,2,5]
+ CRUSH rule 5 x 691 [5,2,6]
+ CRUSH rule 5 x 692 [7,2,5]
+ CRUSH rule 5 x 693 [6,5,2]
+ CRUSH rule 5 x 694 [6,5,2]
+ CRUSH rule 5 x 695 [2,8,5]
+ CRUSH rule 5 x 696 [2,5,8]
+ CRUSH rule 5 x 697 [6,2,5]
+ CRUSH rule 5 x 698 [6,2,5]
+ CRUSH rule 5 x 699 [2,6,5]
+ CRUSH rule 5 x 700 [2,5,7]
+ CRUSH rule 5 x 701 [5,2,7]
+ CRUSH rule 5 x 702 [5,2,8]
+ CRUSH rule 5 x 703 [8,5,2]
+ CRUSH rule 5 x 704 [2,5,8]
+ CRUSH rule 5 x 705 [8,2,5]
+ CRUSH rule 5 x 706 [2,5,6]
+ CRUSH rule 5 x 707 [7,5,2]
+ CRUSH rule 5 x 708 [5,7,2]
+ CRUSH rule 5 x 709 [6,5,2]
+ CRUSH rule 5 x 710 [8,5,2]
+ CRUSH rule 5 x 711 [2,5,8]
+ CRUSH rule 5 x 712 [2,5,7]
+ CRUSH rule 5 x 713 [6,5,2]
+ CRUSH rule 5 x 714 [5,2,7]
+ CRUSH rule 5 x 715 [2,5,6]
+ CRUSH rule 5 x 716 [5,6,2]
+ CRUSH rule 5 x 717 [8,2,5]
+ CRUSH rule 5 x 718 [5,7,2]
+ CRUSH rule 5 x 719 [2,6,5]
+ CRUSH rule 5 x 720 [6,2,5]
+ CRUSH rule 5 x 721 [5,7,2]
+ CRUSH rule 5 x 722 [5,7,2]
+ CRUSH rule 5 x 723 [5,2,7]
+ CRUSH rule 5 x 724 [2,6,5]
+ CRUSH rule 5 x 725 [2,5,7]
+ CRUSH rule 5 x 726 [5,8,2]
+ CRUSH rule 5 x 727 [5,6,2]
+ CRUSH rule 5 x 728 [2,7,5]
+ CRUSH rule 5 x 729 [5,6,2]
+ CRUSH rule 5 x 730 [5,7,2]
+ CRUSH rule 5 x 731 [5,2,8]
+ CRUSH rule 5 x 732 [2,5,6]
+ CRUSH rule 5 x 733 [5,7,2]
+ CRUSH rule 5 x 734 [6,5,2]
+ CRUSH rule 5 x 735 [5,8,2]
+ CRUSH rule 5 x 736 [5,8,2]
+ CRUSH rule 5 x 737 [2,6,5]
+ CRUSH rule 5 x 738 [5,2,7]
+ CRUSH rule 5 x 739 [2,7,5]
+ CRUSH rule 5 x 740 [2,8,5]
+ CRUSH rule 5 x 741 [7,2,5]
+ CRUSH rule 5 x 742 [8,2,5]
+ CRUSH rule 5 x 743 [7,2,5]
+ CRUSH rule 5 x 744 [5,7,2]
+ CRUSH rule 5 x 745 [5,2,8]
+ CRUSH rule 5 x 746 [5,2,7]
+ CRUSH rule 5 x 747 [6,2,5]
+ CRUSH rule 5 x 748 [2,7,5]
+ CRUSH rule 5 x 749 [5,8,2]
+ CRUSH rule 5 x 750 [2,6,5]
+ CRUSH rule 5 x 751 [2,8,5]
+ CRUSH rule 5 x 752 [8,2,5]
+ CRUSH rule 5 x 753 [7,5,2]
+ CRUSH rule 5 x 754 [8,5,2]
+ CRUSH rule 5 x 755 [2,6,5]
+ CRUSH rule 5 x 756 [5,6,2]
+ CRUSH rule 5 x 757 [8,2,5]
+ CRUSH rule 5 x 758 [6,2,5]
+ CRUSH rule 5 x 759 [8,5,2]
+ CRUSH rule 5 x 760 [2,5,7]
+ CRUSH rule 5 x 761 [5,2,8]
+ CRUSH rule 5 x 762 [2,7,5]
+ CRUSH rule 5 x 763 [8,5,2]
+ CRUSH rule 5 x 764 [2,7,5]
+ CRUSH rule 5 x 765 [6,5,2]
+ CRUSH rule 5 x 766 [8,5,2]
+ CRUSH rule 5 x 767 [2,8,5]
+ CRUSH rule 5 x 768 [8,5,2]
+ CRUSH rule 5 x 769 [6,2,5]
+ CRUSH rule 5 x 770 [6,2,5]
+ CRUSH rule 5 x 771 [7,2,5]
+ CRUSH rule 5 x 772 [8,5,2]
+ CRUSH rule 5 x 773 [5,2,7]
+ CRUSH rule 5 x 774 [5,6,2]
+ CRUSH rule 5 x 775 [6,5,2]
+ CRUSH rule 5 x 776 [7,2,5]
+ CRUSH rule 5 x 777 [5,2,6]
+ CRUSH rule 5 x 778 [2,8,5]
+ CRUSH rule 5 x 779 [2,7,5]
+ CRUSH rule 5 x 780 [2,5,7]
+ CRUSH rule 5 x 781 [6,5,2]
+ CRUSH rule 5 x 782 [5,2,8]
+ CRUSH rule 5 x 783 [7,2,5]
+ CRUSH rule 5 x 784 [2,5,6]
+ CRUSH rule 5 x 785 [6,2,5]
+ CRUSH rule 5 x 786 [7,5,2]
+ CRUSH rule 5 x 787 [2,6,5]
+ CRUSH rule 5 x 788 [6,2,5]
+ CRUSH rule 5 x 789 [2,5,8]
+ CRUSH rule 5 x 790 [8,5,2]
+ CRUSH rule 5 x 791 [5,8,2]
+ CRUSH rule 5 x 792 [5,8,2]
+ CRUSH rule 5 x 793 [6,2,5]
+ CRUSH rule 5 x 794 [2,6,5]
+ CRUSH rule 5 x 795 [2,5,8]
+ CRUSH rule 5 x 796 [5,7,2]
+ CRUSH rule 5 x 797 [2,5,8]
+ CRUSH rule 5 x 798 [6,2,5]
+ CRUSH rule 5 x 799 [5,2,8]
+ CRUSH rule 5 x 800 [5,2,7]
+ CRUSH rule 5 x 801 [5,6,2]
+ CRUSH rule 5 x 802 [2,8,5]
+ CRUSH rule 5 x 803 [2,5,7]
+ CRUSH rule 5 x 804 [6,2,5]
+ CRUSH rule 5 x 805 [5,6,2]
+ CRUSH rule 5 x 806 [2,5,7]
+ CRUSH rule 5 x 807 [5,7,2]
+ CRUSH rule 5 x 808 [5,6,2]
+ CRUSH rule 5 x 809 [2,5,8]
+ CRUSH rule 5 x 810 [5,7,2]
+ CRUSH rule 5 x 811 [8,5,2]
+ CRUSH rule 5 x 812 [8,5,2]
+ CRUSH rule 5 x 813 [6,5,2]
+ CRUSH rule 5 x 814 [5,6,2]
+ CRUSH rule 5 x 815 [5,2,8]
+ CRUSH rule 5 x 816 [2,7,5]
+ CRUSH rule 5 x 817 [5,8,2]
+ CRUSH rule 5 x 818 [5,2,7]
+ CRUSH rule 5 x 819 [5,2,8]
+ CRUSH rule 5 x 820 [5,6,2]
+ CRUSH rule 5 x 821 [5,6,2]
+ CRUSH rule 5 x 822 [2,5,8]
+ CRUSH rule 5 x 823 [5,8,2]
+ CRUSH rule 5 x 824 [5,7,2]
+ CRUSH rule 5 x 825 [2,8,5]
+ CRUSH rule 5 x 826 [7,2,5]
+ CRUSH rule 5 x 827 [2,8,5]
+ CRUSH rule 5 x 828 [2,5,8]
+ CRUSH rule 5 x 829 [5,6,2]
+ CRUSH rule 5 x 830 [2,5,8]
+ CRUSH rule 5 x 831 [2,6,5]
+ CRUSH rule 5 x 832 [5,7,2]
+ CRUSH rule 5 x 833 [2,7,5]
+ CRUSH rule 5 x 834 [5,2,7]
+ CRUSH rule 5 x 835 [8,5,2]
+ CRUSH rule 5 x 836 [5,7,2]
+ CRUSH rule 5 x 837 [6,5,2]
+ CRUSH rule 5 x 838 [6,2,5]
+ CRUSH rule 5 x 839 [5,2,6]
+ CRUSH rule 5 x 840 [7,5,2]
+ CRUSH rule 5 x 841 [5,8,2]
+ CRUSH rule 5 x 842 [2,5,6]
+ CRUSH rule 5 x 843 [6,5,2]
+ CRUSH rule 5 x 844 [5,8,2]
+ CRUSH rule 5 x 845 [5,8,2]
+ CRUSH rule 5 x 846 [5,2,7]
+ CRUSH rule 5 x 847 [2,8,5]
+ CRUSH rule 5 x 848 [2,6,5]
+ CRUSH rule 5 x 849 [5,6,2]
+ CRUSH rule 5 x 850 [2,5,6]
+ CRUSH rule 5 x 851 [6,5,2]
+ CRUSH rule 5 x 852 [7,5,2]
+ CRUSH rule 5 x 853 [6,2,5]
+ CRUSH rule 5 x 854 [7,2,5]
+ CRUSH rule 5 x 855 [5,7,2]
+ CRUSH rule 5 x 856 [6,5,2]
+ CRUSH rule 5 x 857 [8,5,2]
+ CRUSH rule 5 x 858 [6,5,2]
+ CRUSH rule 5 x 859 [6,2,5]
+ CRUSH rule 5 x 860 [5,2,7]
+ CRUSH rule 5 x 861 [8,5,2]
+ CRUSH rule 5 x 862 [6,2,5]
+ CRUSH rule 5 x 863 [8,2,5]
+ CRUSH rule 5 x 864 [5,6,2]
+ CRUSH rule 5 x 865 [8,2,5]
+ CRUSH rule 5 x 866 [5,6,2]
+ CRUSH rule 5 x 867 [6,5,2]
+ CRUSH rule 5 x 868 [6,5,2]
+ CRUSH rule 5 x 869 [8,5,2]
+ CRUSH rule 5 x 870 [2,5,8]
+ CRUSH rule 5 x 871 [5,2,8]
+ CRUSH rule 5 x 872 [5,2,8]
+ CRUSH rule 5 x 873 [5,6,2]
+ CRUSH rule 5 x 874 [2,6,5]
+ CRUSH rule 5 x 875 [2,6,5]
+ CRUSH rule 5 x 876 [5,8,2]
+ CRUSH rule 5 x 877 [6,5,2]
+ CRUSH rule 5 x 878 [5,2,7]
+ CRUSH rule 5 x 879 [7,5,2]
+ CRUSH rule 5 x 880 [5,2,8]
+ CRUSH rule 5 x 881 [5,6,2]
+ CRUSH rule 5 x 882 [5,2,7]
+ CRUSH rule 5 x 883 [2,5,7]
+ CRUSH rule 5 x 884 [6,2,5]
+ CRUSH rule 5 x 885 [5,2,8]
+ CRUSH rule 5 x 886 [5,6,2]
+ CRUSH rule 5 x 887 [7,5,2]
+ CRUSH rule 5 x 888 [6,2,5]
+ CRUSH rule 5 x 889 [2,6,5]
+ CRUSH rule 5 x 890 [7,2,5]
+ CRUSH rule 5 x 891 [2,8,5]
+ CRUSH rule 5 x 892 [6,2,5]
+ CRUSH rule 5 x 893 [2,5,7]
+ CRUSH rule 5 x 894 [7,5,2]
+ CRUSH rule 5 x 895 [5,2,8]
+ CRUSH rule 5 x 896 [2,8,5]
+ CRUSH rule 5 x 897 [5,2,6]
+ CRUSH rule 5 x 898 [2,5,7]
+ CRUSH rule 5 x 899 [2,7,5]
+ CRUSH rule 5 x 900 [5,2,6]
+ CRUSH rule 5 x 901 [5,2,8]
+ CRUSH rule 5 x 902 [8,5,2]
+ CRUSH rule 5 x 903 [5,7,2]
+ CRUSH rule 5 x 904 [5,6,2]
+ CRUSH rule 5 x 905 [6,2,5]
+ CRUSH rule 5 x 906 [2,6,5]
+ CRUSH rule 5 x 907 [7,2,5]
+ CRUSH rule 5 x 908 [5,8,2]
+ CRUSH rule 5 x 909 [2,5,7]
+ CRUSH rule 5 x 910 [6,5,2]
+ CRUSH rule 5 x 911 [5,8,2]
+ CRUSH rule 5 x 912 [2,7,5]
+ CRUSH rule 5 x 913 [7,2,5]
+ CRUSH rule 5 x 914 [6,5,2]
+ CRUSH rule 5 x 915 [8,2,5]
+ CRUSH rule 5 x 916 [5,2,8]
+ CRUSH rule 5 x 917 [2,5,8]
+ CRUSH rule 5 x 918 [8,2,5]
+ CRUSH rule 5 x 919 [6,2,5]
+ CRUSH rule 5 x 920 [7,5,2]
+ CRUSH rule 5 x 921 [2,5,6]
+ CRUSH rule 5 x 922 [6,5,2]
+ CRUSH rule 5 x 923 [5,8,2]
+ CRUSH rule 5 x 924 [5,2,7]
+ CRUSH rule 5 x 925 [5,7,2]
+ CRUSH rule 5 x 926 [5,2,8]
+ CRUSH rule 5 x 927 [2,6,5]
+ CRUSH rule 5 x 928 [8,2,5]
+ CRUSH rule 5 x 929 [5,2,7]
+ CRUSH rule 5 x 930 [2,5,6]
+ CRUSH rule 5 x 931 [5,2,7]
+ CRUSH rule 5 x 932 [5,2,8]
+ CRUSH rule 5 x 933 [8,5,2]
+ CRUSH rule 5 x 934 [5,6,2]
+ CRUSH rule 5 x 935 [6,5,2]
+ CRUSH rule 5 x 936 [2,6,5]
+ CRUSH rule 5 x 937 [5,8,2]
+ CRUSH rule 5 x 938 [6,5,2]
+ CRUSH rule 5 x 939 [2,7,5]
+ CRUSH rule 5 x 940 [8,5,2]
+ CRUSH rule 5 x 941 [5,2,8]
+ CRUSH rule 5 x 942 [2,8,5]
+ CRUSH rule 5 x 943 [8,2,5]
+ CRUSH rule 5 x 944 [5,8,2]
+ CRUSH rule 5 x 945 [7,2,5]
+ CRUSH rule 5 x 946 [2,8,5]
+ CRUSH rule 5 x 947 [5,2,8]
+ CRUSH rule 5 x 948 [7,5,2]
+ CRUSH rule 5 x 949 [6,2,5]
+ CRUSH rule 5 x 950 [5,6,2]
+ CRUSH rule 5 x 951 [5,8,2]
+ CRUSH rule 5 x 952 [2,7,5]
+ CRUSH rule 5 x 953 [2,5,6]
+ CRUSH rule 5 x 954 [5,2,7]
+ CRUSH rule 5 x 955 [8,2,5]
+ CRUSH rule 5 x 956 [2,6,5]
+ CRUSH rule 5 x 957 [7,2,5]
+ CRUSH rule 5 x 958 [8,5,2]
+ CRUSH rule 5 x 959 [5,2,7]
+ CRUSH rule 5 x 960 [5,6,2]
+ CRUSH rule 5 x 961 [5,2,8]
+ CRUSH rule 5 x 962 [7,5,2]
+ CRUSH rule 5 x 963 [2,5,6]
+ CRUSH rule 5 x 964 [5,2,8]
+ CRUSH rule 5 x 965 [7,5,2]
+ CRUSH rule 5 x 966 [5,8,2]
+ CRUSH rule 5 x 967 [8,5,2]
+ CRUSH rule 5 x 968 [7,2,5]
+ CRUSH rule 5 x 969 [8,2,5]
+ CRUSH rule 5 x 970 [2,6,5]
+ CRUSH rule 5 x 971 [2,7,5]
+ CRUSH rule 5 x 972 [2,8,5]
+ CRUSH rule 5 x 973 [2,6,5]
+ CRUSH rule 5 x 974 [5,2,8]
+ CRUSH rule 5 x 975 [5,7,2]
+ CRUSH rule 5 x 976 [5,8,2]
+ CRUSH rule 5 x 977 [8,5,2]
+ CRUSH rule 5 x 978 [7,2,5]
+ CRUSH rule 5 x 979 [7,2,5]
+ CRUSH rule 5 x 980 [6,2,5]
+ CRUSH rule 5 x 981 [7,5,2]
+ CRUSH rule 5 x 982 [5,2,8]
+ CRUSH rule 5 x 983 [5,7,2]
+ CRUSH rule 5 x 984 [2,7,5]
+ CRUSH rule 5 x 985 [2,5,7]
+ CRUSH rule 5 x 986 [8,5,2]
+ CRUSH rule 5 x 987 [2,5,8]
+ CRUSH rule 5 x 988 [2,5,7]
+ CRUSH rule 5 x 989 [2,6,5]
+ CRUSH rule 5 x 990 [2,6,5]
+ CRUSH rule 5 x 991 [2,5,8]
+ CRUSH rule 5 x 992 [7,2,5]
+ CRUSH rule 5 x 993 [2,6,5]
+ CRUSH rule 5 x 994 [5,7,2]
+ CRUSH rule 5 x 995 [7,2,5]
+ CRUSH rule 5 x 996 [6,5,2]
+ CRUSH rule 5 x 997 [6,5,2]
+ CRUSH rule 5 x 998 [8,2,5]
+ CRUSH rule 5 x 999 [2,7,5]
+ CRUSH rule 5 x 1000 [8,5,2]
+ CRUSH rule 5 x 1001 [2,5,6]
+ CRUSH rule 5 x 1002 [2,5,7]
+ CRUSH rule 5 x 1003 [2,8,5]
+ CRUSH rule 5 x 1004 [6,2,5]
+ CRUSH rule 5 x 1005 [6,2,5]
+ CRUSH rule 5 x 1006 [2,6,5]
+ CRUSH rule 5 x 1007 [2,5,7]
+ CRUSH rule 5 x 1008 [2,7,5]
+ CRUSH rule 5 x 1009 [6,5,2]
+ CRUSH rule 5 x 1010 [5,2,7]
+ CRUSH rule 5 x 1011 [5,2,8]
+ CRUSH rule 5 x 1012 [5,2,7]
+ CRUSH rule 5 x 1013 [5,2,7]
+ CRUSH rule 5 x 1014 [2,8,5]
+ CRUSH rule 5 x 1015 [6,5,2]
+ CRUSH rule 5 x 1016 [2,5,7]
+ CRUSH rule 5 x 1017 [6,2,5]
+ CRUSH rule 5 x 1018 [5,2,6]
+ CRUSH rule 5 x 1019 [5,8,2]
+ CRUSH rule 5 x 1020 [5,2,7]
+ CRUSH rule 5 x 1021 [5,2,6]
+ CRUSH rule 5 x 1022 [2,6,5]
+ CRUSH rule 5 x 1023 [5,2,8]
+ rule 5 (chooseleaf-set) num_rep 3 result size == 3:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
+ $ crushtool -i set-choose.crushmap --test --show-mappings --show-statistics --weight 0 0 --weight 3 0 --weight 4 .5 --weight 5 0 --weight 6 .1 --weight 7 0 --set-straw-calc-version 0 --min-rep 2 --max-rep 3
+ rule 0 (choose), x = 0..1023, numrep = 2..3
+ CRUSH rule 0 x 0 [2,4]
+ CRUSH rule 0 x 1 [2,8]
+ CRUSH rule 0 x 2 [1]
+ CRUSH rule 0 x 3 [8,1]
+ CRUSH rule 0 x 4 [4,1]
+ CRUSH rule 0 x 5 [8,1]
+ CRUSH rule 0 x 6 [2,8]
+ CRUSH rule 0 x 7 [4,8]
+ CRUSH rule 0 x 8 [4,8]
+ CRUSH rule 0 x 9 [2,4]
+ CRUSH rule 0 x 10 [2,8]
+ CRUSH rule 0 x 11 [2,8]
+ CRUSH rule 0 x 12 [2]
+ CRUSH rule 0 x 13 [4,8]
+ CRUSH rule 0 x 14 [8,1]
+ CRUSH rule 0 x 15 [8,1]
+ CRUSH rule 0 x 16 [8]
+ CRUSH rule 0 x 17 [4,1]
+ CRUSH rule 0 x 18 [1]
+ CRUSH rule 0 x 19 [8,4]
+ CRUSH rule 0 x 20 [2]
+ CRUSH rule 0 x 21 [8]
+ CRUSH rule 0 x 22 [8]
+ CRUSH rule 0 x 23 [4,8]
+ CRUSH rule 0 x 24 [1,8]
+ CRUSH rule 0 x 25 [4,8]
+ CRUSH rule 0 x 26 [2,8]
+ CRUSH rule 0 x 27 [4,1]
+ CRUSH rule 0 x 28 [8,2]
+ CRUSH rule 0 x 29 [8,4]
+ CRUSH rule 0 x 30 [4,8]
+ CRUSH rule 0 x 31 [8,2]
+ CRUSH rule 0 x 32 [6]
+ CRUSH rule 0 x 33 [2,8]
+ CRUSH rule 0 x 34 [2]
+ CRUSH rule 0 x 35 [1,8]
+ CRUSH rule 0 x 36 [8]
+ CRUSH rule 0 x 37 [1]
+ CRUSH rule 0 x 38 [4,8]
+ CRUSH rule 0 x 39 [8]
+ CRUSH rule 0 x 40 [8,2]
+ CRUSH rule 0 x 41 [2,8]
+ CRUSH rule 0 x 42 [8]
+ CRUSH rule 0 x 43 [1]
+ CRUSH rule 0 x 44 [1,8]
+ CRUSH rule 0 x 45 [8,2]
+ CRUSH rule 0 x 46 [2]
+ CRUSH rule 0 x 47 [4,1]
+ CRUSH rule 0 x 48 [8]
+ CRUSH rule 0 x 49 [8]
+ CRUSH rule 0 x 50 [4,1]
+ CRUSH rule 0 x 51 [8]
+ CRUSH rule 0 x 52 [8,2]
+ CRUSH rule 0 x 53 [4,8]
+ CRUSH rule 0 x 54 [8,4]
+ CRUSH rule 0 x 55 [8,2]
+ CRUSH rule 0 x 56 [8,4]
+ CRUSH rule 0 x 57 [8]
+ CRUSH rule 0 x 58 [1,8]
+ CRUSH rule 0 x 59 [2]
+ CRUSH rule 0 x 60 [4,1]
+ CRUSH rule 0 x 61 [4,8]
+ CRUSH rule 0 x 62 [8,1]
+ CRUSH rule 0 x 63 [8]
+ CRUSH rule 0 x 64 [4,1]
+ CRUSH rule 0 x 65 [8,4]
+ CRUSH rule 0 x 66 [4,8]
+ CRUSH rule 0 x 67 [4,2]
+ CRUSH rule 0 x 68 [1]
+ CRUSH rule 0 x 69 [2]
+ CRUSH rule 0 x 70 [8,2]
+ CRUSH rule 0 x 71 [2,8]
+ CRUSH rule 0 x 72 [8,2]
+ CRUSH rule 0 x 73 [2,8]
+ CRUSH rule 0 x 74 [1,8]
+ CRUSH rule 0 x 75 [4,2]
+ CRUSH rule 0 x 76 [4,2]
+ CRUSH rule 0 x 77 [8,2]
+ CRUSH rule 0 x 78 [1]
+ CRUSH rule 0 x 79 [4,1]
+ CRUSH rule 0 x 80 [2,4]
+ CRUSH rule 0 x 81 [2]
+ CRUSH rule 0 x 82 [6,2]
+ CRUSH rule 0 x 83 [2,8]
+ CRUSH rule 0 x 84 [8,2]
+ CRUSH rule 0 x 85 [4,8]
+ CRUSH rule 0 x 86 [2,8]
+ CRUSH rule 0 x 87 [2,8]
+ CRUSH rule 0 x 88 [1,8]
+ CRUSH rule 0 x 89 [1]
+ CRUSH rule 0 x 90 [8,4]
+ CRUSH rule 0 x 91 [4,8]
+ CRUSH rule 0 x 92 [1,8]
+ CRUSH rule 0 x 93 [8,4]
+ CRUSH rule 0 x 94 [1]
+ CRUSH rule 0 x 95 [8]
+ CRUSH rule 0 x 96 [8]
+ CRUSH rule 0 x 97 [8]
+ CRUSH rule 0 x 98 [2,8]
+ CRUSH rule 0 x 99 [2,8]
+ CRUSH rule 0 x 100 [1,8]
+ CRUSH rule 0 x 101 [8]
+ CRUSH rule 0 x 102 [2]
+ CRUSH rule 0 x 103 [8]
+ CRUSH rule 0 x 104 [8,4]
+ CRUSH rule 0 x 105 [2,4]
+ CRUSH rule 0 x 106 [1,8]
+ CRUSH rule 0 x 107 [1]
+ CRUSH rule 0 x 108 [8,1]
+ CRUSH rule 0 x 109 [1,4]
+ CRUSH rule 0 x 110 [4,2]
+ CRUSH rule 0 x 111 [2,4]
+ CRUSH rule 0 x 112 [2,8]
+ CRUSH rule 0 x 113 [8,1]
+ CRUSH rule 0 x 114 [8,4]
+ CRUSH rule 0 x 115 [8,2]
+ CRUSH rule 0 x 116 [1,8]
+ CRUSH rule 0 x 117 [6]
+ CRUSH rule 0 x 118 [2]
+ CRUSH rule 0 x 119 [8]
+ CRUSH rule 0 x 120 [2,4]
+ CRUSH rule 0 x 121 [2,8]
+ CRUSH rule 0 x 122 [8]
+ CRUSH rule 0 x 123 [2]
+ CRUSH rule 0 x 124 [2]
+ CRUSH rule 0 x 125 [1,8]
+ CRUSH rule 0 x 126 [1]
+ CRUSH rule 0 x 127 [4,8]
+ CRUSH rule 0 x 128 [8]
+ CRUSH rule 0 x 129 [2,4]
+ CRUSH rule 0 x 130 [4,8]
+ CRUSH rule 0 x 131 [1,4]
+ CRUSH rule 0 x 132 [1]
+ CRUSH rule 0 x 133 [8]
+ CRUSH rule 0 x 134 [1,8]
+ CRUSH rule 0 x 135 [4,8]
+ CRUSH rule 0 x 136 [2,4]
+ CRUSH rule 0 x 137 [8,4]
+ CRUSH rule 0 x 138 [8,4]
+ CRUSH rule 0 x 139 [4,2]
+ CRUSH rule 0 x 140 [1,8]
+ CRUSH rule 0 x 141 [8,2]
+ CRUSH rule 0 x 142 [4,2]
+ CRUSH rule 0 x 143 [4,8]
+ CRUSH rule 0 x 144 [8,2]
+ CRUSH rule 0 x 145 [8]
+ CRUSH rule 0 x 146 [2,8]
+ CRUSH rule 0 x 147 [2,8]
+ CRUSH rule 0 x 148 [4,1]
+ CRUSH rule 0 x 149 [4,8]
+ CRUSH rule 0 x 150 [1,8]
+ CRUSH rule 0 x 151 [8]
+ CRUSH rule 0 x 152 [8]
+ CRUSH rule 0 x 153 [8,4]
+ CRUSH rule 0 x 154 [4,2]
+ CRUSH rule 0 x 155 [4,8]
+ CRUSH rule 0 x 156 [4,2]
+ CRUSH rule 0 x 157 [1]
+ CRUSH rule 0 x 158 [2,8]
+ CRUSH rule 0 x 159 [8,2]
+ CRUSH rule 0 x 160 [2,8]
+ CRUSH rule 0 x 161 [1,4]
+ CRUSH rule 0 x 162 [1,8]
+ CRUSH rule 0 x 163 [4,8]
+ CRUSH rule 0 x 164 [8,2]
+ CRUSH rule 0 x 165 [8,2]
+ CRUSH rule 0 x 166 [2]
+ CRUSH rule 0 x 167 [1,8]
+ CRUSH rule 0 x 168 [4,2]
+ CRUSH rule 0 x 169 [2,8]
+ CRUSH rule 0 x 170 [1]
+ CRUSH rule 0 x 171 [8,4]
+ CRUSH rule 0 x 172 [1,8]
+ CRUSH rule 0 x 173 [8,4]
+ CRUSH rule 0 x 174 [1]
+ CRUSH rule 0 x 175 [8,1]
+ CRUSH rule 0 x 176 [2]
+ CRUSH rule 0 x 177 [1]
+ CRUSH rule 0 x 178 [4,2]
+ CRUSH rule 0 x 179 [1]
+ CRUSH rule 0 x 180 [8]
+ CRUSH rule 0 x 181 [8,2]
+ CRUSH rule 0 x 182 [8]
+ CRUSH rule 0 x 183 [8,4]
+ CRUSH rule 0 x 184 [4,8]
+ CRUSH rule 0 x 185 [8,1]
+ CRUSH rule 0 x 186 [2,4]
+ CRUSH rule 0 x 187 [1,8]
+ CRUSH rule 0 x 188 [1,8]
+ CRUSH rule 0 x 189 [1,8]
+ CRUSH rule 0 x 190 [1]
+ CRUSH rule 0 x 191 [8,1]
+ CRUSH rule 0 x 192 [4,2]
+ CRUSH rule 0 x 193 [4,2]
+ CRUSH rule 0 x 194 [1]
+ CRUSH rule 0 x 195 [8,4]
+ CRUSH rule 0 x 196 [8,1]
+ CRUSH rule 0 x 197 [8,4]
+ CRUSH rule 0 x 198 [2]
+ CRUSH rule 0 x 199 [1,4]
+ CRUSH rule 0 x 200 [1]
+ CRUSH rule 0 x 201 [8,1]
+ CRUSH rule 0 x 202 [8]
+ CRUSH rule 0 x 203 [8]
+ CRUSH rule 0 x 204 [2,4]
+ CRUSH rule 0 x 205 [1,8]
+ CRUSH rule 0 x 206 [1,8]
+ CRUSH rule 0 x 207 [2]
+ CRUSH rule 0 x 208 [8,2]
+ CRUSH rule 0 x 209 [1,8]
+ CRUSH rule 0 x 210 [1,4]
+ CRUSH rule 0 x 211 [4,2]
+ CRUSH rule 0 x 212 [8]
+ CRUSH rule 0 x 213 [8,4]
+ CRUSH rule 0 x 214 [8]
+ CRUSH rule 0 x 215 [8,1]
+ CRUSH rule 0 x 216 [2]
+ CRUSH rule 0 x 217 [1,8]
+ CRUSH rule 0 x 218 [2,8]
+ CRUSH rule 0 x 219 [8]
+ CRUSH rule 0 x 220 [4,8]
+ CRUSH rule 0 x 221 [8]
+ CRUSH rule 0 x 222 [8]
+ CRUSH rule 0 x 223 [1]
+ CRUSH rule 0 x 224 [1,4]
+ CRUSH rule 0 x 225 [8,2]
+ CRUSH rule 0 x 226 [8,2]
+ CRUSH rule 0 x 227 [4,1]
+ CRUSH rule 0 x 228 [8]
+ CRUSH rule 0 x 229 [4,8]
+ CRUSH rule 0 x 230 [4,8]
+ CRUSH rule 0 x 231 [4,8]
+ CRUSH rule 0 x 232 [2,8]
+ CRUSH rule 0 x 233 [8]
+ CRUSH rule 0 x 234 [1]
+ CRUSH rule 0 x 235 [4,8]
+ CRUSH rule 0 x 236 [2]
+ CRUSH rule 0 x 237 [4,8]
+ CRUSH rule 0 x 238 [2]
+ CRUSH rule 0 x 239 [8]
+ CRUSH rule 0 x 240 [4,8]
+ CRUSH rule 0 x 241 [1]
+ CRUSH rule 0 x 242 [2]
+ CRUSH rule 0 x 243 [8]
+ CRUSH rule 0 x 244 [4,8]
+ CRUSH rule 0 x 245 [8,2]
+ CRUSH rule 0 x 246 [1]
+ CRUSH rule 0 x 247 [8,1]
+ CRUSH rule 0 x 248 [8,1]
+ CRUSH rule 0 x 249 [2]
+ CRUSH rule 0 x 250 [2,4]
+ CRUSH rule 0 x 251 [2]
+ CRUSH rule 0 x 252 [4,8]
+ CRUSH rule 0 x 253 [2]
+ CRUSH rule 0 x 254 [4,2]
+ CRUSH rule 0 x 255 [1,8]
+ CRUSH rule 0 x 256 [4,8]
+ CRUSH rule 0 x 257 [2,6]
+ CRUSH rule 0 x 258 [4,2]
+ CRUSH rule 0 x 259 [6]
+ CRUSH rule 0 x 260 [8]
+ CRUSH rule 0 x 261 [8]
+ CRUSH rule 0 x 262 [8]
+ CRUSH rule 0 x 263 [8,1]
+ CRUSH rule 0 x 264 [8]
+ CRUSH rule 0 x 265 [8]
+ CRUSH rule 0 x 266 [8,2]
+ CRUSH rule 0 x 267 [2]
+ CRUSH rule 0 x 268 [1,8]
+ CRUSH rule 0 x 269 [1,8]
+ CRUSH rule 0 x 270 [4,1]
+ CRUSH rule 0 x 271 [8,4]
+ CRUSH rule 0 x 272 [2,8]
+ CRUSH rule 0 x 273 [4,2]
+ CRUSH rule 0 x 274 [8,4]
+ CRUSH rule 0 x 275 [4,8]
+ CRUSH rule 0 x 276 [8,1]
+ CRUSH rule 0 x 277 [8]
+ CRUSH rule 0 x 278 [8,2]
+ CRUSH rule 0 x 279 [8,4]
+ CRUSH rule 0 x 280 [2,8]
+ CRUSH rule 0 x 281 [8,1]
+ CRUSH rule 0 x 282 [2]
+ CRUSH rule 0 x 283 [8,1]
+ CRUSH rule 0 x 284 [8]
+ CRUSH rule 0 x 285 [4,8]
+ CRUSH rule 0 x 286 [2,8]
+ CRUSH rule 0 x 287 [1]
+ CRUSH rule 0 x 288 [8,1]
+ CRUSH rule 0 x 289 [4,8]
+ CRUSH rule 0 x 290 [1,4]
+ CRUSH rule 0 x 291 [1,4]
+ CRUSH rule 0 x 292 [8,1]
+ CRUSH rule 0 x 293 [8,2]
+ CRUSH rule 0 x 294 [8,4]
+ CRUSH rule 0 x 295 [4,8]
+ CRUSH rule 0 x 296 [4,1]
+ CRUSH rule 0 x 297 [8,2]
+ CRUSH rule 0 x 298 [1]
+ CRUSH rule 0 x 299 [2,8]
+ CRUSH rule 0 x 300 [8]
+ CRUSH rule 0 x 301 [1,8]
+ CRUSH rule 0 x 302 [1]
+ CRUSH rule 0 x 303 [8,4]
+ CRUSH rule 0 x 304 [2,8]
+ CRUSH rule 0 x 305 [8]
+ CRUSH rule 0 x 306 [1,8]
+ CRUSH rule 0 x 307 [2,8]
+ CRUSH rule 0 x 308 [2,8]
+ CRUSH rule 0 x 309 [8]
+ CRUSH rule 0 x 310 [4,1]
+ CRUSH rule 0 x 311 [4,8]
+ CRUSH rule 0 x 312 [2,8]
+ CRUSH rule 0 x 313 [4,1]
+ CRUSH rule 0 x 314 [2]
+ CRUSH rule 0 x 315 [2]
+ CRUSH rule 0 x 316 [8]
+ CRUSH rule 0 x 317 [2,8]
+ CRUSH rule 0 x 318 [8,1]
+ CRUSH rule 0 x 319 [2]
+ CRUSH rule 0 x 320 [8]
+ CRUSH rule 0 x 321 [1]
+ CRUSH rule 0 x 322 [2,6]
+ CRUSH rule 0 x 323 [4,8]
+ CRUSH rule 0 x 324 [8,2]
+ CRUSH rule 0 x 325 [4,8]
+ CRUSH rule 0 x 326 [1]
+ CRUSH rule 0 x 327 [1,8]
+ CRUSH rule 0 x 328 [8,4]
+ CRUSH rule 0 x 329 [4,8]
+ CRUSH rule 0 x 330 [4,8]
+ CRUSH rule 0 x 331 [2,8]
+ CRUSH rule 0 x 332 [2]
+ CRUSH rule 0 x 333 [8]
+ CRUSH rule 0 x 334 [8]
+ CRUSH rule 0 x 335 [8,2]
+ CRUSH rule 0 x 336 [4,8]
+ CRUSH rule 0 x 337 [8,2]
+ CRUSH rule 0 x 338 [8]
+ CRUSH rule 0 x 339 [8]
+ CRUSH rule 0 x 340 [2,8]
+ CRUSH rule 0 x 341 [4,1]
+ CRUSH rule 0 x 342 [2,8]
+ CRUSH rule 0 x 343 [8]
+ CRUSH rule 0 x 344 [6,1]
+ CRUSH rule 0 x 345 [8]
+ CRUSH rule 0 x 346 [8,2]
+ CRUSH rule 0 x 347 [4,1]
+ CRUSH rule 0 x 348 [8,2]
+ CRUSH rule 0 x 349 [1,8]
+ CRUSH rule 0 x 350 [8]
+ CRUSH rule 0 x 351 [8]
+ CRUSH rule 0 x 352 [1,8]
+ CRUSH rule 0 x 353 [8]
+ CRUSH rule 0 x 354 [1]
+ CRUSH rule 0 x 355 [8]
+ CRUSH rule 0 x 356 [4,1]
+ CRUSH rule 0 x 357 [8,1]
+ CRUSH rule 0 x 358 [2,8]
+ CRUSH rule 0 x 359 [6,1]
+ CRUSH rule 0 x 360 [2]
+ CRUSH rule 0 x 361 [8,4]
+ CRUSH rule 0 x 362 [4,1]
+ CRUSH rule 0 x 363 [4,2]
+ CRUSH rule 0 x 364 [2]
+ CRUSH rule 0 x 365 [8]
+ CRUSH rule 0 x 366 [8,2]
+ CRUSH rule 0 x 367 [4,2]
+ CRUSH rule 0 x 368 [8,4]
+ CRUSH rule 0 x 369 [8]
+ CRUSH rule 0 x 370 [8,2]
+ CRUSH rule 0 x 371 [1,4]
+ CRUSH rule 0 x 372 [1]
+ CRUSH rule 0 x 373 [1,8]
+ CRUSH rule 0 x 374 [8]
+ CRUSH rule 0 x 375 [8,4]
+ CRUSH rule 0 x 376 [8,1]
+ CRUSH rule 0 x 377 [1,4]
+ CRUSH rule 0 x 378 [1,8]
+ CRUSH rule 0 x 379 [8]
+ CRUSH rule 0 x 380 [2]
+ CRUSH rule 0 x 381 [1,4]
+ CRUSH rule 0 x 382 [1,4]
+ CRUSH rule 0 x 383 [4,8]
+ CRUSH rule 0 x 384 [8,2]
+ CRUSH rule 0 x 385 [8]
+ CRUSH rule 0 x 386 [1]
+ CRUSH rule 0 x 387 [1,4]
+ CRUSH rule 0 x 388 [2]
+ CRUSH rule 0 x 389 [1,4]
+ CRUSH rule 0 x 390 [4,8]
+ CRUSH rule 0 x 391 [4,8]
+ CRUSH rule 0 x 392 [1,8]
+ CRUSH rule 0 x 393 [2]
+ CRUSH rule 0 x 394 [8]
+ CRUSH rule 0 x 395 [1]
+ CRUSH rule 0 x 396 [4,2]
+ CRUSH rule 0 x 397 [2,4]
+ CRUSH rule 0 x 398 [2,4]
+ CRUSH rule 0 x 399 [8,4]
+ CRUSH rule 0 x 400 [8,1]
+ CRUSH rule 0 x 401 [1,4]
+ CRUSH rule 0 x 402 [8,4]
+ CRUSH rule 0 x 403 [1,4]
+ CRUSH rule 0 x 404 [4,2]
+ CRUSH rule 0 x 405 [8,4]
+ CRUSH rule 0 x 406 [2,8]
+ CRUSH rule 0 x 407 [2,8]
+ CRUSH rule 0 x 408 [4,1]
+ CRUSH rule 0 x 409 [8,4]
+ CRUSH rule 0 x 410 [8,4]
+ CRUSH rule 0 x 411 [2,8]
+ CRUSH rule 0 x 412 [2]
+ CRUSH rule 0 x 413 [2]
+ CRUSH rule 0 x 414 [4,1]
+ CRUSH rule 0 x 415 [2,8]
+ CRUSH rule 0 x 416 [2,8]
+ CRUSH rule 0 x 417 [8,2]
+ CRUSH rule 0 x 418 [8,1]
+ CRUSH rule 0 x 419 [8,4]
+ CRUSH rule 0 x 420 [1,4]
+ CRUSH rule 0 x 421 [8,4]
+ CRUSH rule 0 x 422 [6]
+ CRUSH rule 0 x 423 [2,4]
+ CRUSH rule 0 x 424 [8]
+ CRUSH rule 0 x 425 [1]
+ CRUSH rule 0 x 426 [8,2]
+ CRUSH rule 0 x 427 [1,8]
+ CRUSH rule 0 x 428 [4,8]
+ CRUSH rule 0 x 429 [4,8]
+ CRUSH rule 0 x 430 [4,8]
+ CRUSH rule 0 x 431 [4,2]
+ CRUSH rule 0 x 432 [8,1]
+ CRUSH rule 0 x 433 [8]
+ CRUSH rule 0 x 434 [2]
+ CRUSH rule 0 x 435 [2]
+ CRUSH rule 0 x 436 [4,1]
+ CRUSH rule 0 x 437 [8]
+ CRUSH rule 0 x 438 [2,4]
+ CRUSH rule 0 x 439 [1]
+ CRUSH rule 0 x 440 [2,8]
+ CRUSH rule 0 x 441 [4,8]
+ CRUSH rule 0 x 442 [2]
+ CRUSH rule 0 x 443 [8,1]
+ CRUSH rule 0 x 444 [8,1]
+ CRUSH rule 0 x 445 [8]
+ CRUSH rule 0 x 446 [1]
+ CRUSH rule 0 x 447 [2,4]
+ CRUSH rule 0 x 448 [8,2]
+ CRUSH rule 0 x 449 [8]
+ CRUSH rule 0 x 450 [1]
+ CRUSH rule 0 x 451 [8,4]
+ CRUSH rule 0 x 452 [8]
+ CRUSH rule 0 x 453 [6]
+ CRUSH rule 0 x 454 [8]
+ CRUSH rule 0 x 455 [2,8]
+ CRUSH rule 0 x 456 [8,2]
+ CRUSH rule 0 x 457 [8,1]
+ CRUSH rule 0 x 458 [2,8]
+ CRUSH rule 0 x 459 [2,8]
+ CRUSH rule 0 x 460 [8]
+ CRUSH rule 0 x 461 [8]
+ CRUSH rule 0 x 462 [8,1]
+ CRUSH rule 0 x 463 [8,2]
+ CRUSH rule 0 x 464 [8,4]
+ CRUSH rule 0 x 465 [6,1]
+ CRUSH rule 0 x 466 [8]
+ CRUSH rule 0 x 467 [8]
+ CRUSH rule 0 x 468 [8,2]
+ CRUSH rule 0 x 469 [8,2]
+ CRUSH rule 0 x 470 [4,1]
+ CRUSH rule 0 x 471 [1,8]
+ CRUSH rule 0 x 472 [1]
+ CRUSH rule 0 x 473 [1,4]
+ CRUSH rule 0 x 474 [8,1]
+ CRUSH rule 0 x 475 [8,2]
+ CRUSH rule 0 x 476 [4,8]
+ CRUSH rule 0 x 477 [4,8]
+ CRUSH rule 0 x 478 [8,1]
+ CRUSH rule 0 x 479 [2]
+ CRUSH rule 0 x 480 [1,8]
+ CRUSH rule 0 x 481 [2,4]
+ CRUSH rule 0 x 482 [8]
+ CRUSH rule 0 x 483 [2,8]
+ CRUSH rule 0 x 484 [1,8]
+ CRUSH rule 0 x 485 [8]
+ CRUSH rule 0 x 486 [4,1]
+ CRUSH rule 0 x 487 [1]
+ CRUSH rule 0 x 488 [8]
+ CRUSH rule 0 x 489 [2,8]
+ CRUSH rule 0 x 490 [6]
+ CRUSH rule 0 x 491 [1,8]
+ CRUSH rule 0 x 492 [8]
+ CRUSH rule 0 x 493 [2,8]
+ CRUSH rule 0 x 494 [1,8]
+ CRUSH rule 0 x 495 [4,1]
+ CRUSH rule 0 x 496 [8,4]
+ CRUSH rule 0 x 497 [4,8]
+ CRUSH rule 0 x 498 [2,4]
+ CRUSH rule 0 x 499 [8,4]
+ CRUSH rule 0 x 500 [4,8]
+ CRUSH rule 0 x 501 [2,8]
+ CRUSH rule 0 x 502 [6,1]
+ CRUSH rule 0 x 503 [2]
+ CRUSH rule 0 x 504 [8]
+ CRUSH rule 0 x 505 [1,8]
+ CRUSH rule 0 x 506 [4,1]
+ CRUSH rule 0 x 507 [8,2]
+ CRUSH rule 0 x 508 [1]
+ CRUSH rule 0 x 509 [8]
+ CRUSH rule 0 x 510 [8,2]
+ CRUSH rule 0 x 511 [4,8]
+ CRUSH rule 0 x 512 [8,2]
+ CRUSH rule 0 x 513 [8,2]
+ CRUSH rule 0 x 514 [8]
+ CRUSH rule 0 x 515 [8,4]
+ CRUSH rule 0 x 516 [4,1]
+ CRUSH rule 0 x 517 [8,2]
+ CRUSH rule 0 x 518 [4,8]
+ CRUSH rule 0 x 519 [8,4]
+ CRUSH rule 0 x 520 [2,8]
+ CRUSH rule 0 x 521 [8,2]
+ CRUSH rule 0 x 522 [8,1]
+ CRUSH rule 0 x 523 [4,1]
+ CRUSH rule 0 x 524 [2]
+ CRUSH rule 0 x 525 [2]
+ CRUSH rule 0 x 526 [1]
+ CRUSH rule 0 x 527 [1,4]
+ CRUSH rule 0 x 528 [2]
+ CRUSH rule 0 x 529 [4,8]
+ CRUSH rule 0 x 530 [8]
+ CRUSH rule 0 x 531 [8,1]
+ CRUSH rule 0 x 532 [6,4]
+ CRUSH rule 0 x 533 [4,8]
+ CRUSH rule 0 x 534 [8]
+ CRUSH rule 0 x 535 [8,1]
+ CRUSH rule 0 x 536 [8,2]
+ CRUSH rule 0 x 537 [4,8]
+ CRUSH rule 0 x 538 [8,4]
+ CRUSH rule 0 x 539 [8]
+ CRUSH rule 0 x 540 [1,8]
+ CRUSH rule 0 x 541 [2,4]
+ CRUSH rule 0 x 542 [2]
+ CRUSH rule 0 x 543 [8,2]
+ CRUSH rule 0 x 544 [4,8]
+ CRUSH rule 0 x 545 [8]
+ CRUSH rule 0 x 546 [8,2]
+ CRUSH rule 0 x 547 [8,1]
+ CRUSH rule 0 x 548 [4,1]
+ CRUSH rule 0 x 549 [8]
+ CRUSH rule 0 x 550 [2,4]
+ CRUSH rule 0 x 551 [8]
+ CRUSH rule 0 x 552 [4,8]
+ CRUSH rule 0 x 553 [2]
+ CRUSH rule 0 x 554 [1,8]
+ CRUSH rule 0 x 555 [4,2]
+ CRUSH rule 0 x 556 [8]
+ CRUSH rule 0 x 557 [8]
+ CRUSH rule 0 x 558 [4,2]
+ CRUSH rule 0 x 559 [1]
+ CRUSH rule 0 x 560 [8]
+ CRUSH rule 0 x 561 [8,4]
+ CRUSH rule 0 x 562 [4,1]
+ CRUSH rule 0 x 563 [2,8]
+ CRUSH rule 0 x 564 [1]
+ CRUSH rule 0 x 565 [4,8]
+ CRUSH rule 0 x 566 [4,8]
+ CRUSH rule 0 x 567 [4,8]
+ CRUSH rule 0 x 568 [8]
+ CRUSH rule 0 x 569 [4,1]
+ CRUSH rule 0 x 570 [1]
+ CRUSH rule 0 x 571 [6]
+ CRUSH rule 0 x 572 [4,2]
+ CRUSH rule 0 x 573 [1]
+ CRUSH rule 0 x 574 [2]
+ CRUSH rule 0 x 575 [8,1]
+ CRUSH rule 0 x 576 [4,8]
+ CRUSH rule 0 x 577 [8,2]
+ CRUSH rule 0 x 578 [8,1]
+ CRUSH rule 0 x 579 [4,2]
+ CRUSH rule 0 x 580 [1]
+ CRUSH rule 0 x 581 [8,1]
+ CRUSH rule 0 x 582 [2,8]
+ CRUSH rule 0 x 583 [8,2]
+ CRUSH rule 0 x 584 [8,1]
+ CRUSH rule 0 x 585 [8,1]
+ CRUSH rule 0 x 586 [1,8]
+ CRUSH rule 0 x 587 [2,4]
+ CRUSH rule 0 x 588 [4,8]
+ CRUSH rule 0 x 589 [8,1]
+ CRUSH rule 0 x 590 [8,2]
+ CRUSH rule 0 x 591 [4,2]
+ CRUSH rule 0 x 592 [2,4]
+ CRUSH rule 0 x 593 [1,8]
+ CRUSH rule 0 x 594 [2,8]
+ CRUSH rule 0 x 595 [8,1]
+ CRUSH rule 0 x 596 [2]
+ CRUSH rule 0 x 597 [1]
+ CRUSH rule 0 x 598 [2]
+ CRUSH rule 0 x 599 [4,1]
+ CRUSH rule 0 x 600 [8,1]
+ CRUSH rule 0 x 601 [1,8]
+ CRUSH rule 0 x 602 [8]
+ CRUSH rule 0 x 603 [1]
+ CRUSH rule 0 x 604 [8]
+ CRUSH rule 0 x 605 [2]
+ CRUSH rule 0 x 606 [2,6]
+ CRUSH rule 0 x 607 [2,4]
+ CRUSH rule 0 x 608 [4,1]
+ CRUSH rule 0 x 609 [4,2]
+ CRUSH rule 0 x 610 [8]
+ CRUSH rule 0 x 611 [1,8]
+ CRUSH rule 0 x 612 [2,8]
+ CRUSH rule 0 x 613 [8,1]
+ CRUSH rule 0 x 614 [8,2]
+ CRUSH rule 0 x 615 [8,2]
+ CRUSH rule 0 x 616 [1,8]
+ CRUSH rule 0 x 617 [8,2]
+ CRUSH rule 0 x 618 [8,4]
+ CRUSH rule 0 x 619 [4,1]
+ CRUSH rule 0 x 620 [1]
+ CRUSH rule 0 x 621 [8]
+ CRUSH rule 0 x 622 [2,4]
+ CRUSH rule 0 x 623 [2,8]
+ CRUSH rule 0 x 624 [4,2]
+ CRUSH rule 0 x 625 [2]
+ CRUSH rule 0 x 626 [8,2]
+ CRUSH rule 0 x 627 [2,8]
+ CRUSH rule 0 x 628 [8,1]
+ CRUSH rule 0 x 629 [2,8]
+ CRUSH rule 0 x 630 [2,8]
+ CRUSH rule 0 x 631 [1,8]
+ CRUSH rule 0 x 632 [8,2]
+ CRUSH rule 0 x 633 [8]
+ CRUSH rule 0 x 634 [1]
+ CRUSH rule 0 x 635 [4,8]
+ CRUSH rule 0 x 636 [1,4]
+ CRUSH rule 0 x 637 [1]
+ CRUSH rule 0 x 638 [8,2]
+ CRUSH rule 0 x 639 [2]
+ CRUSH rule 0 x 640 [2]
+ CRUSH rule 0 x 641 [8,2]
+ CRUSH rule 0 x 642 [2,8]
+ CRUSH rule 0 x 643 [1]
+ CRUSH rule 0 x 644 [8,1]
+ CRUSH rule 0 x 645 [8]
+ CRUSH rule 0 x 646 [8,1]
+ CRUSH rule 0 x 647 [8,1]
+ CRUSH rule 0 x 648 [1,8]
+ CRUSH rule 0 x 649 [4,8]
+ CRUSH rule 0 x 650 [8,4]
+ CRUSH rule 0 x 651 [4,6]
+ CRUSH rule 0 x 652 [4,8]
+ CRUSH rule 0 x 653 [8]
+ CRUSH rule 0 x 654 [6]
+ CRUSH rule 0 x 655 [1,4]
+ CRUSH rule 0 x 656 [8]
+ CRUSH rule 0 x 657 [6,1]
+ CRUSH rule 0 x 658 [8]
+ CRUSH rule 0 x 659 [4,8]
+ CRUSH rule 0 x 660 [8]
+ CRUSH rule 0 x 661 [1,8]
+ CRUSH rule 0 x 662 [2]
+ CRUSH rule 0 x 663 [1,4]
+ CRUSH rule 0 x 664 [1,4]
+ CRUSH rule 0 x 665 [4,6]
+ CRUSH rule 0 x 666 [2,8]
+ CRUSH rule 0 x 667 [1,4]
+ CRUSH rule 0 x 668 [4,8]
+ CRUSH rule 0 x 669 [6,4]
+ CRUSH rule 0 x 670 [4,1]
+ CRUSH rule 0 x 671 [2,8]
+ CRUSH rule 0 x 672 [4,2]
+ CRUSH rule 0 x 673 [4,2]
+ CRUSH rule 0 x 674 [1]
+ CRUSH rule 0 x 675 [1,8]
+ CRUSH rule 0 x 676 [2,4]
+ CRUSH rule 0 x 677 [4,1]
+ CRUSH rule 0 x 678 [2,4]
+ CRUSH rule 0 x 679 [8,2]
+ CRUSH rule 0 x 680 [2]
+ CRUSH rule 0 x 681 [8]
+ CRUSH rule 0 x 682 [1,4]
+ CRUSH rule 0 x 683 [1,4]
+ CRUSH rule 0 x 684 [8,2]
+ CRUSH rule 0 x 685 [8,2]
+ CRUSH rule 0 x 686 [1,4]
+ CRUSH rule 0 x 687 [6]
+ CRUSH rule 0 x 688 [4,8]
+ CRUSH rule 0 x 689 [8,4]
+ CRUSH rule 0 x 690 [8,1]
+ CRUSH rule 0 x 691 [1]
+ CRUSH rule 0 x 692 [8,1]
+ CRUSH rule 0 x 693 [8,4]
+ CRUSH rule 0 x 694 [8,4]
+ CRUSH rule 0 x 695 [2,8]
+ CRUSH rule 0 x 696 [1]
+ CRUSH rule 0 x 697 [8,1]
+ CRUSH rule 0 x 698 [8,2]
+ CRUSH rule 0 x 699 [1,8]
+ CRUSH rule 0 x 700 [1]
+ CRUSH rule 0 x 701 [1]
+ CRUSH rule 0 x 702 [2]
+ CRUSH rule 0 x 703 [8]
+ CRUSH rule 0 x 704 [1,4]
+ CRUSH rule 0 x 705 [8,1]
+ CRUSH rule 0 x 706 [1,4]
+ CRUSH rule 0 x 707 [8,4]
+ CRUSH rule 0 x 708 [4,8]
+ CRUSH rule 0 x 709 [8]
+ CRUSH rule 0 x 710 [8]
+ CRUSH rule 0 x 711 [2,4]
+ CRUSH rule 0 x 712 [2]
+ CRUSH rule 0 x 713 [8,4]
+ CRUSH rule 0 x 714 [2]
+ CRUSH rule 0 x 715 [1]
+ CRUSH rule 0 x 716 [4,8]
+ CRUSH rule 0 x 717 [8,2]
+ CRUSH rule 0 x 718 [8]
+ CRUSH rule 0 x 719 [2,6]
+ CRUSH rule 0 x 720 [8,1]
+ CRUSH rule 0 x 721 [4,6]
+ CRUSH rule 0 x 722 [8]
+ CRUSH rule 0 x 723 [4,2]
+ CRUSH rule 0 x 724 [2,6]
+ CRUSH rule 0 x 725 [1]
+ CRUSH rule 0 x 726 [4,8]
+ CRUSH rule 0 x 727 [4,8]
+ CRUSH rule 0 x 728 [2,8]
+ CRUSH rule 0 x 729 [8]
+ CRUSH rule 0 x 730 [4,8]
+ CRUSH rule 0 x 731 [4,1]
+ CRUSH rule 0 x 732 [1]
+ CRUSH rule 0 x 733 [4,8]
+ CRUSH rule 0 x 734 [8,4]
+ CRUSH rule 0 x 735 [4,8]
+ CRUSH rule 0 x 736 [4,8]
+ CRUSH rule 0 x 737 [1,8]
+ CRUSH rule 0 x 738 [4,1]
+ CRUSH rule 0 x 739 [2,8]
+ CRUSH rule 0 x 740 [1,8]
+ CRUSH rule 0 x 741 [8,2]
+ CRUSH rule 0 x 742 [8,2]
+ CRUSH rule 0 x 743 [8,1]
+ CRUSH rule 0 x 744 [4,8]
+ CRUSH rule 0 x 745 [1]
+ CRUSH rule 0 x 746 [1]
+ CRUSH rule 0 x 747 [8,2]
+ CRUSH rule 0 x 748 [2,8]
+ CRUSH rule 0 x 749 [4,8]
+ CRUSH rule 0 x 750 [1,8]
+ CRUSH rule 0 x 751 [2,8]
+ CRUSH rule 0 x 752 [8,1]
+ CRUSH rule 0 x 753 [8,4]
+ CRUSH rule 0 x 754 [8,4]
+ CRUSH rule 0 x 755 [1,8]
+ CRUSH rule 0 x 756 [8]
+ CRUSH rule 0 x 757 [8,1]
+ CRUSH rule 0 x 758 [8,1]
+ CRUSH rule 0 x 759 [8,4]
+ CRUSH rule 0 x 760 [1,4]
+ CRUSH rule 0 x 761 [2]
+ CRUSH rule 0 x 762 [2,8]
+ CRUSH rule 0 x 763 [8,4]
+ CRUSH rule 0 x 764 [1,8]
+ CRUSH rule 0 x 765 [8]
+ CRUSH rule 0 x 766 [8]
+ CRUSH rule 0 x 767 [1,8]
+ CRUSH rule 0 x 768 [8,4]
+ CRUSH rule 0 x 769 [8,2]
+ CRUSH rule 0 x 770 [8,1]
+ CRUSH rule 0 x 771 [8,2]
+ CRUSH rule 0 x 772 [8,4]
+ CRUSH rule 0 x 773 [4,2]
+ CRUSH rule 0 x 774 [8]
+ CRUSH rule 0 x 775 [8,4]
+ CRUSH rule 0 x 776 [6,2]
+ CRUSH rule 0 x 777 [4,1]
+ CRUSH rule 0 x 778 [1,6]
+ CRUSH rule 0 x 779 [2,8]
+ CRUSH rule 0 x 780 [2,4]
+ CRUSH rule 0 x 781 [8]
+ CRUSH rule 0 x 782 [4,2]
+ CRUSH rule 0 x 783 [8,1]
+ CRUSH rule 0 x 784 [1,4]
+ CRUSH rule 0 x 785 [8,1]
+ CRUSH rule 0 x 786 [8]
+ CRUSH rule 0 x 787 [1,8]
+ CRUSH rule 0 x 788 [8,2]
+ CRUSH rule 0 x 789 [1]
+ CRUSH rule 0 x 790 [8]
+ CRUSH rule 0 x 791 [4,8]
+ CRUSH rule 0 x 792 [4,8]
+ CRUSH rule 0 x 793 [8,2]
+ CRUSH rule 0 x 794 [2,8]
+ CRUSH rule 0 x 795 [1]
+ CRUSH rule 0 x 796 [8]
+ CRUSH rule 0 x 797 [2,4]
+ CRUSH rule 0 x 798 [6,1]
+ CRUSH rule 0 x 799 [4,2]
+ CRUSH rule 0 x 800 [2]
+ CRUSH rule 0 x 801 [4,8]
+ CRUSH rule 0 x 802 [1,8]
+ CRUSH rule 0 x 803 [2]
+ CRUSH rule 0 x 804 [8,2]
+ CRUSH rule 0 x 805 [8]
+ CRUSH rule 0 x 806 [1,4]
+ CRUSH rule 0 x 807 [4,8]
+ CRUSH rule 0 x 808 [8]
+ CRUSH rule 0 x 809 [1]
+ CRUSH rule 0 x 810 [8]
+ CRUSH rule 0 x 811 [8]
+ CRUSH rule 0 x 812 [8,4]
+ CRUSH rule 0 x 813 [8,4]
+ CRUSH rule 0 x 814 [8]
+ CRUSH rule 0 x 815 [4,1]
+ CRUSH rule 0 x 816 [2,8]
+ CRUSH rule 0 x 817 [8]
+ CRUSH rule 0 x 818 [1]
+ CRUSH rule 0 x 819 [1]
+ CRUSH rule 0 x 820 [4,8]
+ CRUSH rule 0 x 821 [4,8]
+ CRUSH rule 0 x 822 [2,4]
+ CRUSH rule 0 x 823 [4,8]
+ CRUSH rule 0 x 824 [8]
+ CRUSH rule 0 x 825 [2,8]
+ CRUSH rule 0 x 826 [8,1]
+ CRUSH rule 0 x 827 [2,6]
+ CRUSH rule 0 x 828 [2]
+ CRUSH rule 0 x 829 [8]
+ CRUSH rule 0 x 830 [2,4]
+ CRUSH rule 0 x 831 [1,8]
+ CRUSH rule 0 x 832 [4,8]
+ CRUSH rule 0 x 833 [2,8]
+ CRUSH rule 0 x 834 [1]
+ CRUSH rule 0 x 835 [8,4]
+ CRUSH rule 0 x 836 [4,8]
+ CRUSH rule 0 x 837 [8,4]
+ CRUSH rule 0 x 838 [6,2]
+ CRUSH rule 0 x 839 [2]
+ CRUSH rule 0 x 840 [8]
+ CRUSH rule 0 x 841 [4,8]
+ CRUSH rule 0 x 842 [2]
+ CRUSH rule 0 x 843 [8,4]
+ CRUSH rule 0 x 844 [8]
+ CRUSH rule 0 x 845 [4,8]
+ CRUSH rule 0 x 846 [4,2]
+ CRUSH rule 0 x 847 [2,8]
+ CRUSH rule 0 x 848 [2,8]
+ CRUSH rule 0 x 849 [4,8]
+ CRUSH rule 0 x 850 [1]
+ CRUSH rule 0 x 851 [6]
+ CRUSH rule 0 x 852 [8,4]
+ CRUSH rule 0 x 853 [6,2]
+ CRUSH rule 0 x 854 [8,2]
+ CRUSH rule 0 x 855 [8]
+ CRUSH rule 0 x 856 [8,4]
+ CRUSH rule 0 x 857 [8]
+ CRUSH rule 0 x 858 [6]
+ CRUSH rule 0 x 859 [8,2]
+ CRUSH rule 0 x 860 [2]
+ CRUSH rule 0 x 861 [8]
+ CRUSH rule 0 x 862 [8,1]
+ CRUSH rule 0 x 863 [8,1]
+ CRUSH rule 0 x 864 [8]
+ CRUSH rule 0 x 865 [8,1]
+ CRUSH rule 0 x 866 [8]
+ CRUSH rule 0 x 867 [8]
+ CRUSH rule 0 x 868 [8]
+ CRUSH rule 0 x 869 [8,4]
+ CRUSH rule 0 x 870 [2]
+ CRUSH rule 0 x 871 [1]
+ CRUSH rule 0 x 872 [1]
+ CRUSH rule 0 x 873 [4,8]
+ CRUSH rule 0 x 874 [2,6]
+ CRUSH rule 0 x 875 [2,8]
+ CRUSH rule 0 x 876 [4,8]
+ CRUSH rule 0 x 877 [8,4]
+ CRUSH rule 0 x 878 [2]
+ CRUSH rule 0 x 879 [8]
+ CRUSH rule 0 x 880 [1]
+ CRUSH rule 0 x 881 [4,8]
+ CRUSH rule 0 x 882 [1]
+ CRUSH rule 0 x 883 [2,4]
+ CRUSH rule 0 x 884 [8,1]
+ CRUSH rule 0 x 885 [4,1]
+ CRUSH rule 0 x 886 [8]
+ CRUSH rule 0 x 887 [8,4]
+ CRUSH rule 0 x 888 [8,1]
+ CRUSH rule 0 x 889 [2,8]
+ CRUSH rule 0 x 890 [8,1]
+ CRUSH rule 0 x 891 [1,8]
+ CRUSH rule 0 x 892 [8,2]
+ CRUSH rule 0 x 893 [2]
+ CRUSH rule 0 x 894 [8,4]
+ CRUSH rule 0 x 895 [4,1]
+ CRUSH rule 0 x 896 [1,8]
+ CRUSH rule 0 x 897 [2]
+ CRUSH rule 0 x 898 [1,4]
+ CRUSH rule 0 x 899 [1,8]
+ CRUSH rule 0 x 900 [4,1]
+ CRUSH rule 0 x 901 [2]
+ CRUSH rule 0 x 902 [8,4]
+ CRUSH rule 0 x 903 [8]
+ CRUSH rule 0 x 904 [8]
+ CRUSH rule 0 x 905 [8,2]
+ CRUSH rule 0 x 906 [1,8]
+ CRUSH rule 0 x 907 [8,2]
+ CRUSH rule 0 x 908 [8]
+ CRUSH rule 0 x 909 [2]
+ CRUSH rule 0 x 910 [8]
+ CRUSH rule 0 x 911 [8]
+ CRUSH rule 0 x 912 [1,8]
+ CRUSH rule 0 x 913 [8,1]
+ CRUSH rule 0 x 914 [6,4]
+ CRUSH rule 0 x 915 [8,2]
+ CRUSH rule 0 x 916 [4,1]
+ CRUSH rule 0 x 917 [1,4]
+ CRUSH rule 0 x 918 [8,2]
+ CRUSH rule 0 x 919 [8,2]
+ CRUSH rule 0 x 920 [8]
+ CRUSH rule 0 x 921 [1]
+ CRUSH rule 0 x 922 [8,4]
+ CRUSH rule 0 x 923 [4,8]
+ CRUSH rule 0 x 924 [1]
+ CRUSH rule 0 x 925 [4,8]
+ CRUSH rule 0 x 926 [2]
+ CRUSH rule 0 x 927 [1,8]
+ CRUSH rule 0 x 928 [8,1]
+ CRUSH rule 0 x 929 [4,2]
+ CRUSH rule 0 x 930 [2]
+ CRUSH rule 0 x 931 [2]
+ CRUSH rule 0 x 932 [4,2]
+ CRUSH rule 0 x 933 [8,4]
+ CRUSH rule 0 x 934 [8]
+ CRUSH rule 0 x 935 [8]
+ CRUSH rule 0 x 936 [1,8]
+ CRUSH rule 0 x 937 [4,8]
+ CRUSH rule 0 x 938 [8,4]
+ CRUSH rule 0 x 939 [2,8]
+ CRUSH rule 0 x 940 [8]
+ CRUSH rule 0 x 941 [2]
+ CRUSH rule 0 x 942 [1,8]
+ CRUSH rule 0 x 943 [8,2]
+ CRUSH rule 0 x 944 [8]
+ CRUSH rule 0 x 945 [8,2]
+ CRUSH rule 0 x 946 [2,8]
+ CRUSH rule 0 x 947 [2]
+ CRUSH rule 0 x 948 [8]
+ CRUSH rule 0 x 949 [6,2]
+ CRUSH rule 0 x 950 [8]
+ CRUSH rule 0 x 951 [8]
+ CRUSH rule 0 x 952 [2,8]
+ CRUSH rule 0 x 953 [1,4]
+ CRUSH rule 0 x 954 [2]
+ CRUSH rule 0 x 955 [8,1]
+ CRUSH rule 0 x 956 [1,8]
+ CRUSH rule 0 x 957 [8,1]
+ CRUSH rule 0 x 958 [8,4]
+ CRUSH rule 0 x 959 [4,1]
+ CRUSH rule 0 x 960 [6]
+ CRUSH rule 0 x 961 [1]
+ CRUSH rule 0 x 962 [8,4]
+ CRUSH rule 0 x 963 [2,4]
+ CRUSH rule 0 x 964 [2]
+ CRUSH rule 0 x 965 [8]
+ CRUSH rule 0 x 966 [4,8]
+ CRUSH rule 0 x 967 [8,4]
+ CRUSH rule 0 x 968 [8,2]
+ CRUSH rule 0 x 969 [8,2]
+ CRUSH rule 0 x 970 [2,8]
+ CRUSH rule 0 x 971 [1,8]
+ CRUSH rule 0 x 972 [1,8]
+ CRUSH rule 0 x 973 [1,8]
+ CRUSH rule 0 x 974 [4,1]
+ CRUSH rule 0 x 975 [4,8]
+ CRUSH rule 0 x 976 [4,8]
+ CRUSH rule 0 x 977 [8,4]
+ CRUSH rule 0 x 978 [8,2]
+ CRUSH rule 0 x 979 [8,2]
+ CRUSH rule 0 x 980 [8,2]
+ CRUSH rule 0 x 981 [8]
+ CRUSH rule 0 x 982 [1]
+ CRUSH rule 0 x 983 [4,8]
+ CRUSH rule 0 x 984 [2,8]
+ CRUSH rule 0 x 985 [2,4]
+ CRUSH rule 0 x 986 [8,4]
+ CRUSH rule 0 x 987 [2]
+ CRUSH rule 0 x 988 [1,4]
+ CRUSH rule 0 x 989 [1,8]
+ CRUSH rule 0 x 990 [1,8]
+ CRUSH rule 0 x 991 [1,4]
+ CRUSH rule 0 x 992 [8,1]
+ CRUSH rule 0 x 993 [2,8]
+ CRUSH rule 0 x 994 [4,8]
+ CRUSH rule 0 x 995 [8,1]
+ CRUSH rule 0 x 996 [8,4]
+ CRUSH rule 0 x 997 [8,4]
+ CRUSH rule 0 x 998 [8,1]
+ CRUSH rule 0 x 999 [1,8]
+ CRUSH rule 0 x 1000 [8,4]
+ CRUSH rule 0 x 1001 [2]
+ CRUSH rule 0 x 1002 [1]
+ CRUSH rule 0 x 1003 [2,8]
+ CRUSH rule 0 x 1004 [8,1]
+ CRUSH rule 0 x 1005 [8,1]
+ CRUSH rule 0 x 1006 [1,8]
+ CRUSH rule 0 x 1007 [1,4]
+ CRUSH rule 0 x 1008 [1,8]
+ CRUSH rule 0 x 1009 [6,4]
+ CRUSH rule 0 x 1010 [1]
+ CRUSH rule 0 x 1011 [4,2]
+ CRUSH rule 0 x 1012 [1]
+ CRUSH rule 0 x 1013 [2]
+ CRUSH rule 0 x 1014 [2,8]
+ CRUSH rule 0 x 1015 [8]
+ CRUSH rule 0 x 1016 [2,4]
+ CRUSH rule 0 x 1017 [6,1]
+ CRUSH rule 0 x 1018 [4,1]
+ CRUSH rule 0 x 1019 [4,8]
+ CRUSH rule 0 x 1020 [1]
+ CRUSH rule 0 x 1021 [2]
+ CRUSH rule 0 x 1022 [1,8]
+ CRUSH rule 0 x 1023 [4,2]
+ rule 0 (choose) num_rep 2 result size == 1:\t325/1024 (esc)
+ rule 0 (choose) num_rep 2 result size == 2:\t699/1024 (esc)
+ CRUSH rule 0 x 0 [2,4,8]
+ CRUSH rule 0 x 1 [2,8,4]
+ CRUSH rule 0 x 2 [1,8]
+ CRUSH rule 0 x 3 [8,1]
+ CRUSH rule 0 x 4 [4,1,8]
+ CRUSH rule 0 x 5 [8,1]
+ CRUSH rule 0 x 6 [2,8,4]
+ CRUSH rule 0 x 7 [4,8,1]
+ CRUSH rule 0 x 8 [4,8,2]
+ CRUSH rule 0 x 9 [2,4,8]
+ CRUSH rule 0 x 10 [2,8]
+ CRUSH rule 0 x 11 [2,8]
+ CRUSH rule 0 x 12 [2,8]
+ CRUSH rule 0 x 13 [4,8,1]
+ CRUSH rule 0 x 14 [8,1]
+ CRUSH rule 0 x 15 [8,1]
+ CRUSH rule 0 x 16 [8,1]
+ CRUSH rule 0 x 17 [4,1,8]
+ CRUSH rule 0 x 18 [1,6]
+ CRUSH rule 0 x 19 [8,4,2]
+ CRUSH rule 0 x 20 [2,8]
+ CRUSH rule 0 x 21 [8,2]
+ CRUSH rule 0 x 22 [8,1]
+ CRUSH rule 0 x 23 [4,8,2]
+ CRUSH rule 0 x 24 [1,8,4]
+ CRUSH rule 0 x 25 [4,8,1]
+ CRUSH rule 0 x 26 [2,8,4]
+ CRUSH rule 0 x 27 [4,1,8]
+ CRUSH rule 0 x 28 [8,2]
+ CRUSH rule 0 x 29 [8,4,2]
+ CRUSH rule 0 x 30 [4,8,1]
+ CRUSH rule 0 x 31 [8,2]
+ CRUSH rule 0 x 32 [6,2]
+ CRUSH rule 0 x 33 [2,8]
+ CRUSH rule 0 x 34 [2,8]
+ CRUSH rule 0 x 35 [1,8,4]
+ CRUSH rule 0 x 36 [8,1]
+ CRUSH rule 0 x 37 [1,8]
+ CRUSH rule 0 x 38 [4,8,1]
+ CRUSH rule 0 x 39 [8,2]
+ CRUSH rule 0 x 40 [8,2,4]
+ CRUSH rule 0 x 41 [2,8,4]
+ CRUSH rule 0 x 42 [8,2]
+ CRUSH rule 0 x 43 [1,8]
+ CRUSH rule 0 x 44 [1,8,4]
+ CRUSH rule 0 x 45 [8,2,4]
+ CRUSH rule 0 x 46 [2,8]
+ CRUSH rule 0 x 47 [4,1,8]
+ CRUSH rule 0 x 48 [8,1]
+ CRUSH rule 0 x 49 [8,2]
+ CRUSH rule 0 x 50 [4,1,8]
+ CRUSH rule 0 x 51 [8,1]
+ CRUSH rule 0 x 52 [8,2,4]
+ CRUSH rule 0 x 53 [4,8,2]
+ CRUSH rule 0 x 54 [8,4,2]
+ CRUSH rule 0 x 55 [8,2]
+ CRUSH rule 0 x 56 [8,4,1]
+ CRUSH rule 0 x 57 [8,1]
+ CRUSH rule 0 x 58 [1,8]
+ CRUSH rule 0 x 59 [2,8]
+ CRUSH rule 0 x 60 [4,1,8]
+ CRUSH rule 0 x 61 [4,8,2]
+ CRUSH rule 0 x 62 [8,1]
+ CRUSH rule 0 x 63 [8,2]
+ CRUSH rule 0 x 64 [4,1,8]
+ CRUSH rule 0 x 65 [8,4,2]
+ CRUSH rule 0 x 66 [4,8,1]
+ CRUSH rule 0 x 67 [4,2,8]
+ CRUSH rule 0 x 68 [1,8]
+ CRUSH rule 0 x 69 [2,8]
+ CRUSH rule 0 x 70 [8,2]
+ CRUSH rule 0 x 71 [2,8,4]
+ CRUSH rule 0 x 72 [8,2,4]
+ CRUSH rule 0 x 73 [2,8]
+ CRUSH rule 0 x 74 [1,8]
+ CRUSH rule 0 x 75 [4,2,8]
+ CRUSH rule 0 x 76 [4,2,6]
+ CRUSH rule 0 x 77 [8,2,4]
+ CRUSH rule 0 x 78 [1,6]
+ CRUSH rule 0 x 79 [4,1,8]
+ CRUSH rule 0 x 80 [2,4,8]
+ CRUSH rule 0 x 81 [2,8]
+ CRUSH rule 0 x 82 [6,2]
+ CRUSH rule 0 x 83 [2,8]
+ CRUSH rule 0 x 84 [8,2]
+ CRUSH rule 0 x 85 [4,8,2]
+ CRUSH rule 0 x 86 [2,8]
+ CRUSH rule 0 x 87 [2,8,4]
+ CRUSH rule 0 x 88 [1,8]
+ CRUSH rule 0 x 89 [1,8]
+ CRUSH rule 0 x 90 [8,4,2]
+ CRUSH rule 0 x 91 [4,8,2]
+ CRUSH rule 0 x 92 [1,8]
+ CRUSH rule 0 x 93 [8,4,1]
+ CRUSH rule 0 x 94 [1,8]
+ CRUSH rule 0 x 95 [8,1]
+ CRUSH rule 0 x 96 [8,2]
+ CRUSH rule 0 x 97 [8,1]
+ CRUSH rule 0 x 98 [2,8]
+ CRUSH rule 0 x 99 [2,8]
+ CRUSH rule 0 x 100 [1,8,4]
+ CRUSH rule 0 x 101 [8,1]
+ CRUSH rule 0 x 102 [2,8]
+ CRUSH rule 0 x 103 [8,1]
+ CRUSH rule 0 x 104 [8,4,1]
+ CRUSH rule 0 x 105 [2,4,8]
+ CRUSH rule 0 x 106 [1,8,4]
+ CRUSH rule 0 x 107 [1,8]
+ CRUSH rule 0 x 108 [8,1]
+ CRUSH rule 0 x 109 [1,4,8]
+ CRUSH rule 0 x 110 [4,2,8]
+ CRUSH rule 0 x 111 [2,4,8]
+ CRUSH rule 0 x 112 [2,8]
+ CRUSH rule 0 x 113 [8,1]
+ CRUSH rule 0 x 114 [8,4,2]
+ CRUSH rule 0 x 115 [8,2,4]
+ CRUSH rule 0 x 116 [1,8]
+ CRUSH rule 0 x 117 [6,1]
+ CRUSH rule 0 x 118 [2,8]
+ CRUSH rule 0 x 119 [8,1]
+ CRUSH rule 0 x 120 [2,4,8]
+ CRUSH rule 0 x 121 [2,8,4]
+ CRUSH rule 0 x 122 [8,2]
+ CRUSH rule 0 x 123 [2,8]
+ CRUSH rule 0 x 124 [2,8]
+ CRUSH rule 0 x 125 [1,8,4]
+ CRUSH rule 0 x 126 [1,8]
+ CRUSH rule 0 x 127 [4,8,1]
+ CRUSH rule 0 x 128 [8,1]
+ CRUSH rule 0 x 129 [2,4,8]
+ CRUSH rule 0 x 130 [4,8,1]
+ CRUSH rule 0 x 131 [1,4,8]
+ CRUSH rule 0 x 132 [1,8]
+ CRUSH rule 0 x 133 [8,1]
+ CRUSH rule 0 x 134 [1,8,4]
+ CRUSH rule 0 x 135 [4,8,2]
+ CRUSH rule 0 x 136 [2,4,8]
+ CRUSH rule 0 x 137 [8,4,1]
+ CRUSH rule 0 x 138 [8,4,2]
+ CRUSH rule 0 x 139 [4,2,8]
+ CRUSH rule 0 x 140 [1,8,4]
+ CRUSH rule 0 x 141 [8,2]
+ CRUSH rule 0 x 142 [4,2,8]
+ CRUSH rule 0 x 143 [4,8,1]
+ CRUSH rule 0 x 144 [8,2]
+ CRUSH rule 0 x 145 [8,2]
+ CRUSH rule 0 x 146 [2,8]
+ CRUSH rule 0 x 147 [2,8,4]
+ CRUSH rule 0 x 148 [4,1,8]
+ CRUSH rule 0 x 149 [4,8,2]
+ CRUSH rule 0 x 150 [1,8]
+ CRUSH rule 0 x 151 [8,1]
+ CRUSH rule 0 x 152 [8,2]
+ CRUSH rule 0 x 153 [8,4,1]
+ CRUSH rule 0 x 154 [4,2,8]
+ CRUSH rule 0 x 155 [4,8,1]
+ CRUSH rule 0 x 156 [4,2,8]
+ CRUSH rule 0 x 157 [1,8]
+ CRUSH rule 0 x 158 [2,8,4]
+ CRUSH rule 0 x 159 [8,2,4]
+ CRUSH rule 0 x 160 [2,8,4]
+ CRUSH rule 0 x 161 [1,4,8]
+ CRUSH rule 0 x 162 [1,8]
+ CRUSH rule 0 x 163 [4,8,2]
+ CRUSH rule 0 x 164 [8,2]
+ CRUSH rule 0 x 165 [8,2,4]
+ CRUSH rule 0 x 166 [2,8]
+ CRUSH rule 0 x 167 [1,8,4]
+ CRUSH rule 0 x 168 [4,2,8]
+ CRUSH rule 0 x 169 [2,8,4]
+ CRUSH rule 0 x 170 [1,8]
+ CRUSH rule 0 x 171 [8,4,2]
+ CRUSH rule 0 x 172 [1,8]
+ CRUSH rule 0 x 173 [8,4,1]
+ CRUSH rule 0 x 174 [1,8]
+ CRUSH rule 0 x 175 [8,1]
+ CRUSH rule 0 x 176 [2,8]
+ CRUSH rule 0 x 177 [1,8]
+ CRUSH rule 0 x 178 [4,2,8]
+ CRUSH rule 0 x 179 [1,8]
+ CRUSH rule 0 x 180 [8,2]
+ CRUSH rule 0 x 181 [8,2,4]
+ CRUSH rule 0 x 182 [8,1]
+ CRUSH rule 0 x 183 [8,4,1]
+ CRUSH rule 0 x 184 [4,8,1]
+ CRUSH rule 0 x 185 [8,1,4]
+ CRUSH rule 0 x 186 [2,4,8]
+ CRUSH rule 0 x 187 [1,8]
+ CRUSH rule 0 x 188 [1,8,4]
+ CRUSH rule 0 x 189 [1,8,4]
+ CRUSH rule 0 x 190 [1,8]
+ CRUSH rule 0 x 191 [8,1,4]
+ CRUSH rule 0 x 192 [4,2,8]
+ CRUSH rule 0 x 193 [4,2,8]
+ CRUSH rule 0 x 194 [1,8]
+ CRUSH rule 0 x 195 [8,4,2]
+ CRUSH rule 0 x 196 [8,1]
+ CRUSH rule 0 x 197 [8,4,1]
+ CRUSH rule 0 x 198 [2,8]
+ CRUSH rule 0 x 199 [1,4,8]
+ CRUSH rule 0 x 200 [1,8]
+ CRUSH rule 0 x 201 [8,1,4]
+ CRUSH rule 0 x 202 [8,1]
+ CRUSH rule 0 x 203 [8,1]
+ CRUSH rule 0 x 204 [2,4,8]
+ CRUSH rule 0 x 205 [1,8]
+ CRUSH rule 0 x 206 [1,8,4]
+ CRUSH rule 0 x 207 [2,8]
+ CRUSH rule 0 x 208 [8,2]
+ CRUSH rule 0 x 209 [1,8]
+ CRUSH rule 0 x 210 [1,4,8]
+ CRUSH rule 0 x 211 [4,2,8]
+ CRUSH rule 0 x 212 [8,1]
+ CRUSH rule 0 x 213 [8,4,1]
+ CRUSH rule 0 x 214 [8,1]
+ CRUSH rule 0 x 215 [8,1]
+ CRUSH rule 0 x 216 [2,8]
+ CRUSH rule 0 x 217 [1,8,4]
+ CRUSH rule 0 x 218 [2,8]
+ CRUSH rule 0 x 219 [8,2]
+ CRUSH rule 0 x 220 [4,8,1]
+ CRUSH rule 0 x 221 [8,1]
+ CRUSH rule 0 x 222 [8,1]
+ CRUSH rule 0 x 223 [1,8]
+ CRUSH rule 0 x 224 [1,4,8]
+ CRUSH rule 0 x 225 [8,2]
+ CRUSH rule 0 x 226 [8,2,4]
+ CRUSH rule 0 x 227 [4,1,8]
+ CRUSH rule 0 x 228 [8,1]
+ CRUSH rule 0 x 229 [4,8,1]
+ CRUSH rule 0 x 230 [4,8,1]
+ CRUSH rule 0 x 231 [4,8,1]
+ CRUSH rule 0 x 232 [2,8,4]
+ CRUSH rule 0 x 233 [8,2]
+ CRUSH rule 0 x 234 [1,8]
+ CRUSH rule 0 x 235 [4,8,1]
+ CRUSH rule 0 x 236 [2,8]
+ CRUSH rule 0 x 237 [4,8,2]
+ CRUSH rule 0 x 238 [2,8]
+ CRUSH rule 0 x 239 [8,2]
+ CRUSH rule 0 x 240 [4,8,2]
+ CRUSH rule 0 x 241 [1,8]
+ CRUSH rule 0 x 242 [2,8]
+ CRUSH rule 0 x 243 [8,2]
+ CRUSH rule 0 x 244 [4,8,2]
+ CRUSH rule 0 x 245 [8,2]
+ CRUSH rule 0 x 246 [1,8]
+ CRUSH rule 0 x 247 [8,1]
+ CRUSH rule 0 x 248 [8,1,4]
+ CRUSH rule 0 x 249 [2,8]
+ CRUSH rule 0 x 250 [2,4,8]
+ CRUSH rule 0 x 251 [2,8]
+ CRUSH rule 0 x 252 [4,8,2]
+ CRUSH rule 0 x 253 [2,8]
+ CRUSH rule 0 x 254 [4,2,8]
+ CRUSH rule 0 x 255 [1,8]
+ CRUSH rule 0 x 256 [4,8,1]
+ CRUSH rule 0 x 257 [2,6,4]
+ CRUSH rule 0 x 258 [4,2,8]
+ CRUSH rule 0 x 259 [6,1]
+ CRUSH rule 0 x 260 [8,1]
+ CRUSH rule 0 x 261 [8,2]
+ CRUSH rule 0 x 262 [8,2]
+ CRUSH rule 0 x 263 [8,1,4]
+ CRUSH rule 0 x 264 [8,1]
+ CRUSH rule 0 x 265 [8,2]
+ CRUSH rule 0 x 266 [8,2,4]
+ CRUSH rule 0 x 267 [2,8]
+ CRUSH rule 0 x 268 [1,8]
+ CRUSH rule 0 x 269 [1,8,4]
+ CRUSH rule 0 x 270 [4,1,8]
+ CRUSH rule 0 x 271 [8,4,2]
+ CRUSH rule 0 x 272 [2,8,4]
+ CRUSH rule 0 x 273 [4,2,8]
+ CRUSH rule 0 x 274 [8,4,2]
+ CRUSH rule 0 x 275 [4,8,1]
+ CRUSH rule 0 x 276 [8,1,4]
+ CRUSH rule 0 x 277 [8,1]
+ CRUSH rule 0 x 278 [8,2,4]
+ CRUSH rule 0 x 279 [8,4,1]
+ CRUSH rule 0 x 280 [2,8,4]
+ CRUSH rule 0 x 281 [8,1]
+ CRUSH rule 0 x 282 [2,8]
+ CRUSH rule 0 x 283 [8,1]
+ CRUSH rule 0 x 284 [8,1]
+ CRUSH rule 0 x 285 [4,8,2]
+ CRUSH rule 0 x 286 [2,8,4]
+ CRUSH rule 0 x 287 [1,8]
+ CRUSH rule 0 x 288 [8,1,4]
+ CRUSH rule 0 x 289 [4,8,2]
+ CRUSH rule 0 x 290 [1,4,8]
+ CRUSH rule 0 x 291 [1,4,8]
+ CRUSH rule 0 x 292 [8,1,4]
+ CRUSH rule 0 x 293 [8,2]
+ CRUSH rule 0 x 294 [8,4,2]
+ CRUSH rule 0 x 295 [4,8,1]
+ CRUSH rule 0 x 296 [4,1,8]
+ CRUSH rule 0 x 297 [8,2,4]
+ CRUSH rule 0 x 298 [1,8]
+ CRUSH rule 0 x 299 [2,8]
+ CRUSH rule 0 x 300 [8,1]
+ CRUSH rule 0 x 301 [1,8]
+ CRUSH rule 0 x 302 [1,8]
+ CRUSH rule 0 x 303 [8,4,1]
+ CRUSH rule 0 x 304 [2,8]
+ CRUSH rule 0 x 305 [8,1]
+ CRUSH rule 0 x 306 [1,8]
+ CRUSH rule 0 x 307 [2,8]
+ CRUSH rule 0 x 308 [2,8,4]
+ CRUSH rule 0 x 309 [8,2]
+ CRUSH rule 0 x 310 [4,1,6]
+ CRUSH rule 0 x 311 [4,8,2]
+ CRUSH rule 0 x 312 [2,8]
+ CRUSH rule 0 x 313 [4,1,8]
+ CRUSH rule 0 x 314 [2,6]
+ CRUSH rule 0 x 315 [2,8]
+ CRUSH rule 0 x 316 [8,1]
+ CRUSH rule 0 x 317 [2,8]
+ CRUSH rule 0 x 318 [8,1]
+ CRUSH rule 0 x 319 [2,8]
+ CRUSH rule 0 x 320 [8,1]
+ CRUSH rule 0 x 321 [1,8]
+ CRUSH rule 0 x 322 [2,6,4]
+ CRUSH rule 0 x 323 [4,8,2]
+ CRUSH rule 0 x 324 [8,2,4]
+ CRUSH rule 0 x 325 [4,8,1]
+ CRUSH rule 0 x 326 [1,6]
+ CRUSH rule 0 x 327 [1,8]
+ CRUSH rule 0 x 328 [8,4,1]
+ CRUSH rule 0 x 329 [4,8,2]
+ CRUSH rule 0 x 330 [4,8,1]
+ CRUSH rule 0 x 331 [2,8]
+ CRUSH rule 0 x 332 [2,8]
+ CRUSH rule 0 x 333 [8,1]
+ CRUSH rule 0 x 334 [8,2]
+ CRUSH rule 0 x 335 [8,2]
+ CRUSH rule 0 x 336 [4,8,2]
+ CRUSH rule 0 x 337 [8,2,4]
+ CRUSH rule 0 x 338 [8,2]
+ CRUSH rule 0 x 339 [8,2]
+ CRUSH rule 0 x 340 [2,8,4]
+ CRUSH rule 0 x 341 [4,1,6]
+ CRUSH rule 0 x 342 [2,8,4]
+ CRUSH rule 0 x 343 [8,1]
+ CRUSH rule 0 x 344 [6,1,4]
+ CRUSH rule 0 x 345 [8,2]
+ CRUSH rule 0 x 346 [8,2,4]
+ CRUSH rule 0 x 347 [4,1,8]
+ CRUSH rule 0 x 348 [8,2,4]
+ CRUSH rule 0 x 349 [1,8]
+ CRUSH rule 0 x 350 [8,1]
+ CRUSH rule 0 x 351 [8,2]
+ CRUSH rule 0 x 352 [1,8,4]
+ CRUSH rule 0 x 353 [8,1]
+ CRUSH rule 0 x 354 [1,8]
+ CRUSH rule 0 x 355 [8,2]
+ CRUSH rule 0 x 356 [4,1,8]
+ CRUSH rule 0 x 357 [8,1,4]
+ CRUSH rule 0 x 358 [2,8,4]
+ CRUSH rule 0 x 359 [6,1,4]
+ CRUSH rule 0 x 360 [2,8]
+ CRUSH rule 0 x 361 [8,4,1]
+ CRUSH rule 0 x 362 [4,1,8]
+ CRUSH rule 0 x 363 [4,2,8]
+ CRUSH rule 0 x 364 [2,8]
+ CRUSH rule 0 x 365 [8,2]
+ CRUSH rule 0 x 366 [8,2]
+ CRUSH rule 0 x 367 [4,2,8]
+ CRUSH rule 0 x 368 [8,4,1]
+ CRUSH rule 0 x 369 [8,2]
+ CRUSH rule 0 x 370 [8,2]
+ CRUSH rule 0 x 371 [1,4,8]
+ CRUSH rule 0 x 372 [1,8]
+ CRUSH rule 0 x 373 [1,8]
+ CRUSH rule 0 x 374 [8,1]
+ CRUSH rule 0 x 375 [8,4,2]
+ CRUSH rule 0 x 376 [8,1,4]
+ CRUSH rule 0 x 377 [1,4,8]
+ CRUSH rule 0 x 378 [1,8]
+ CRUSH rule 0 x 379 [8,2]
+ CRUSH rule 0 x 380 [2,8]
+ CRUSH rule 0 x 381 [1,4,8]
+ CRUSH rule 0 x 382 [1,4,8]
+ CRUSH rule 0 x 383 [4,8,1]
+ CRUSH rule 0 x 384 [8,2,4]
+ CRUSH rule 0 x 385 [8,1]
+ CRUSH rule 0 x 386 [1,8]
+ CRUSH rule 0 x 387 [1,4,8]
+ CRUSH rule 0 x 388 [2,6]
+ CRUSH rule 0 x 389 [1,4,8]
+ CRUSH rule 0 x 390 [4,8,1]
+ CRUSH rule 0 x 391 [4,8,2]
+ CRUSH rule 0 x 392 [1,8,4]
+ CRUSH rule 0 x 393 [2,8]
+ CRUSH rule 0 x 394 [8,2]
+ CRUSH rule 0 x 395 [1,8]
+ CRUSH rule 0 x 396 [4,2,8]
+ CRUSH rule 0 x 397 [2,4,6]
+ CRUSH rule 0 x 398 [2,4,8]
+ CRUSH rule 0 x 399 [8,4,2]
+ CRUSH rule 0 x 400 [8,1,4]
+ CRUSH rule 0 x 401 [1,4,8]
+ CRUSH rule 0 x 402 [8,4,2]
+ CRUSH rule 0 x 403 [1,4,8]
+ CRUSH rule 0 x 404 [4,2,8]
+ CRUSH rule 0 x 405 [8,4,2]
+ CRUSH rule 0 x 406 [2,8]
+ CRUSH rule 0 x 407 [2,8,4]
+ CRUSH rule 0 x 408 [4,1,8]
+ CRUSH rule 0 x 409 [8,4,2]
+ CRUSH rule 0 x 410 [8,4,1]
+ CRUSH rule 0 x 411 [2,8,4]
+ CRUSH rule 0 x 412 [2,6]
+ CRUSH rule 0 x 413 [2,8]
+ CRUSH rule 0 x 414 [4,1,8]
+ CRUSH rule 0 x 415 [2,8]
+ CRUSH rule 0 x 416 [2,8]
+ CRUSH rule 0 x 417 [8,2]
+ CRUSH rule 0 x 418 [8,1,4]
+ CRUSH rule 0 x 419 [8,4,2]
+ CRUSH rule 0 x 420 [1,4,8]
+ CRUSH rule 0 x 421 [8,4,1]
+ CRUSH rule 0 x 422 [6,1]
+ CRUSH rule 0 x 423 [2,4,8]
+ CRUSH rule 0 x 424 [8,1]
+ CRUSH rule 0 x 425 [1,8]
+ CRUSH rule 0 x 426 [8,2]
+ CRUSH rule 0 x 427 [1,8]
+ CRUSH rule 0 x 428 [4,8,1]
+ CRUSH rule 0 x 429 [4,8,1]
+ CRUSH rule 0 x 430 [4,8,2]
+ CRUSH rule 0 x 431 [4,2,8]
+ CRUSH rule 0 x 432 [8,1]
+ CRUSH rule 0 x 433 [8,1]
+ CRUSH rule 0 x 434 [2,8]
+ CRUSH rule 0 x 435 [2,6]
+ CRUSH rule 0 x 436 [4,1,8]
+ CRUSH rule 0 x 437 [8,1]
+ CRUSH rule 0 x 438 [2,4,8]
+ CRUSH rule 0 x 439 [1,8]
+ CRUSH rule 0 x 440 [2,8]
+ CRUSH rule 0 x 441 [4,8,2]
+ CRUSH rule 0 x 442 [2,8]
+ CRUSH rule 0 x 443 [8,1,4]
+ CRUSH rule 0 x 444 [8,1]
+ CRUSH rule 0 x 445 [8,2]
+ CRUSH rule 0 x 446 [1,8]
+ CRUSH rule 0 x 447 [2,4,8]
+ CRUSH rule 0 x 448 [8,2,4]
+ CRUSH rule 0 x 449 [8,2]
+ CRUSH rule 0 x 450 [1,8]
+ CRUSH rule 0 x 451 [8,4,1]
+ CRUSH rule 0 x 452 [8,2]
+ CRUSH rule 0 x 453 [6,2]
+ CRUSH rule 0 x 454 [8,2]
+ CRUSH rule 0 x 455 [2,8,4]
+ CRUSH rule 0 x 456 [8,2]
+ CRUSH rule 0 x 457 [8,1]
+ CRUSH rule 0 x 458 [2,8]
+ CRUSH rule 0 x 459 [2,8,4]
+ CRUSH rule 0 x 460 [8,2]
+ CRUSH rule 0 x 461 [8,2]
+ CRUSH rule 0 x 462 [8,1]
+ CRUSH rule 0 x 463 [8,2]
+ CRUSH rule 0 x 464 [8,4,1]
+ CRUSH rule 0 x 465 [6,1,4]
+ CRUSH rule 0 x 466 [8,2]
+ CRUSH rule 0 x 467 [8,1]
+ CRUSH rule 0 x 468 [8,2,4]
+ CRUSH rule 0 x 469 [8,2]
+ CRUSH rule 0 x 470 [4,1,8]
+ CRUSH rule 0 x 471 [1,8]
+ CRUSH rule 0 x 472 [1,8]
+ CRUSH rule 0 x 473 [1,4,8]
+ CRUSH rule 0 x 474 [8,1]
+ CRUSH rule 0 x 475 [8,2,4]
+ CRUSH rule 0 x 476 [4,8,1]
+ CRUSH rule 0 x 477 [4,8,1]
+ CRUSH rule 0 x 478 [8,1,4]
+ CRUSH rule 0 x 479 [2,8]
+ CRUSH rule 0 x 480 [1,8]
+ CRUSH rule 0 x 481 [2,4,6]
+ CRUSH rule 0 x 482 [8,1]
+ CRUSH rule 0 x 483 [2,8,4]
+ CRUSH rule 0 x 484 [1,8]
+ CRUSH rule 0 x 485 [8,1]
+ CRUSH rule 0 x 486 [4,1,8]
+ CRUSH rule 0 x 487 [1,8]
+ CRUSH rule 0 x 488 [8,1]
+ CRUSH rule 0 x 489 [2,8]
+ CRUSH rule 0 x 490 [6,2]
+ CRUSH rule 0 x 491 [1,8]
+ CRUSH rule 0 x 492 [8,2]
+ CRUSH rule 0 x 493 [2,8]
+ CRUSH rule 0 x 494 [1,8]
+ CRUSH rule 0 x 495 [4,1,6]
+ CRUSH rule 0 x 496 [8,4,1]
+ CRUSH rule 0 x 497 [4,8,2]
+ CRUSH rule 0 x 498 [2,4,8]
+ CRUSH rule 0 x 499 [8,4,2]
+ CRUSH rule 0 x 500 [4,8,1]
+ CRUSH rule 0 x 501 [2,8]
+ CRUSH rule 0 x 502 [6,1]
+ CRUSH rule 0 x 503 [2,8]
+ CRUSH rule 0 x 504 [8,2]
+ CRUSH rule 0 x 505 [1,8]
+ CRUSH rule 0 x 506 [4,1,8]
+ CRUSH rule 0 x 507 [8,2,4]
+ CRUSH rule 0 x 508 [1,8]
+ CRUSH rule 0 x 509 [8,1]
+ CRUSH rule 0 x 510 [8,2]
+ CRUSH rule 0 x 511 [4,8,2]
+ CRUSH rule 0 x 512 [8,2]
+ CRUSH rule 0 x 513 [8,2]
+ CRUSH rule 0 x 514 [8,2]
+ CRUSH rule 0 x 515 [8,4,2]
+ CRUSH rule 0 x 516 [4,1,8]
+ CRUSH rule 0 x 517 [8,2]
+ CRUSH rule 0 x 518 [4,8,1]
+ CRUSH rule 0 x 519 [8,4,1]
+ CRUSH rule 0 x 520 [2,8,4]
+ CRUSH rule 0 x 521 [8,2,4]
+ CRUSH rule 0 x 522 [8,1,4]
+ CRUSH rule 0 x 523 [4,1,8]
+ CRUSH rule 0 x 524 [2,6]
+ CRUSH rule 0 x 525 [2,8]
+ CRUSH rule 0 x 526 [1,8]
+ CRUSH rule 0 x 527 [1,4,6]
+ CRUSH rule 0 x 528 [2,8]
+ CRUSH rule 0 x 529 [4,8,1]
+ CRUSH rule 0 x 530 [8,1]
+ CRUSH rule 0 x 531 [8,1,4]
+ CRUSH rule 0 x 532 [6,4,1]
+ CRUSH rule 0 x 533 [4,8,1]
+ CRUSH rule 0 x 534 [8,2]
+ CRUSH rule 0 x 535 [8,1]
+ CRUSH rule 0 x 536 [8,2]
+ CRUSH rule 0 x 537 [4,8,1]
+ CRUSH rule 0 x 538 [8,4,2]
+ CRUSH rule 0 x 539 [8,2]
+ CRUSH rule 0 x 540 [1,8,4]
+ CRUSH rule 0 x 541 [2,4,8]
+ CRUSH rule 0 x 542 [2,8]
+ CRUSH rule 0 x 543 [8,2]
+ CRUSH rule 0 x 544 [4,8,1]
+ CRUSH rule 0 x 545 [8,1]
+ CRUSH rule 0 x 546 [8,2,4]
+ CRUSH rule 0 x 547 [8,1,4]
+ CRUSH rule 0 x 548 [4,1,8]
+ CRUSH rule 0 x 549 [8,1]
+ CRUSH rule 0 x 550 [2,4,8]
+ CRUSH rule 0 x 551 [8,1]
+ CRUSH rule 0 x 552 [4,8,2]
+ CRUSH rule 0 x 553 [2,8]
+ CRUSH rule 0 x 554 [1,8]
+ CRUSH rule 0 x 555 [4,2,8]
+ CRUSH rule 0 x 556 [8,1]
+ CRUSH rule 0 x 557 [8,2]
+ CRUSH rule 0 x 558 [4,2,8]
+ CRUSH rule 0 x 559 [1,8]
+ CRUSH rule 0 x 560 [8,2]
+ CRUSH rule 0 x 561 [8,4,1]
+ CRUSH rule 0 x 562 [4,1,8]
+ CRUSH rule 0 x 563 [2,8]
+ CRUSH rule 0 x 564 [1,8]
+ CRUSH rule 0 x 565 [4,8,1]
+ CRUSH rule 0 x 566 [4,8,2]
+ CRUSH rule 0 x 567 [4,8,1]
+ CRUSH rule 0 x 568 [8,1]
+ CRUSH rule 0 x 569 [4,1,8]
+ CRUSH rule 0 x 570 [1,8]
+ CRUSH rule 0 x 571 [6,1]
+ CRUSH rule 0 x 572 [4,2,8]
+ CRUSH rule 0 x 573 [1,8]
+ CRUSH rule 0 x 574 [2,8]
+ CRUSH rule 0 x 575 [8,1,4]
+ CRUSH rule 0 x 576 [4,8,1]
+ CRUSH rule 0 x 577 [8,2]
+ CRUSH rule 0 x 578 [8,1]
+ CRUSH rule 0 x 579 [4,2,8]
+ CRUSH rule 0 x 580 [1,8]
+ CRUSH rule 0 x 581 [8,1,4]
+ CRUSH rule 0 x 582 [2,8,4]
+ CRUSH rule 0 x 583 [8,2]
+ CRUSH rule 0 x 584 [8,1,4]
+ CRUSH rule 0 x 585 [8,1,4]
+ CRUSH rule 0 x 586 [1,8,4]
+ CRUSH rule 0 x 587 [2,4,8]
+ CRUSH rule 0 x 588 [4,8,1]
+ CRUSH rule 0 x 589 [8,1]
+ CRUSH rule 0 x 590 [8,2]
+ CRUSH rule 0 x 591 [4,2,8]
+ CRUSH rule 0 x 592 [2,4,8]
+ CRUSH rule 0 x 593 [1,8,4]
+ CRUSH rule 0 x 594 [2,8]
+ CRUSH rule 0 x 595 [8,1]
+ CRUSH rule 0 x 596 [2,8]
+ CRUSH rule 0 x 597 [1,8]
+ CRUSH rule 0 x 598 [2,8]
+ CRUSH rule 0 x 599 [4,1,8]
+ CRUSH rule 0 x 600 [8,1,4]
+ CRUSH rule 0 x 601 [1,8,4]
+ CRUSH rule 0 x 602 [8,2]
+ CRUSH rule 0 x 603 [1,8]
+ CRUSH rule 0 x 604 [8,2]
+ CRUSH rule 0 x 605 [2,8]
+ CRUSH rule 0 x 606 [2,6,4]
+ CRUSH rule 0 x 607 [2,4,8]
+ CRUSH rule 0 x 608 [4,1,8]
+ CRUSH rule 0 x 609 [4,2,8]
+ CRUSH rule 0 x 610 [8,2]
+ CRUSH rule 0 x 611 [1,8]
+ CRUSH rule 0 x 612 [2,8]
+ CRUSH rule 0 x 613 [8,1,4]
+ CRUSH rule 0 x 614 [8,2,4]
+ CRUSH rule 0 x 615 [8,2,4]
+ CRUSH rule 0 x 616 [1,8]
+ CRUSH rule 0 x 617 [8,2,4]
+ CRUSH rule 0 x 618 [8,4,1]
+ CRUSH rule 0 x 619 [4,1,8]
+ CRUSH rule 0 x 620 [1,8]
+ CRUSH rule 0 x 621 [8,1]
+ CRUSH rule 0 x 622 [2,4,8]
+ CRUSH rule 0 x 623 [2,8]
+ CRUSH rule 0 x 624 [4,2,8]
+ CRUSH rule 0 x 625 [2,8]
+ CRUSH rule 0 x 626 [8,2,4]
+ CRUSH rule 0 x 627 [2,8,4]
+ CRUSH rule 0 x 628 [8,1]
+ CRUSH rule 0 x 629 [2,8,4]
+ CRUSH rule 0 x 630 [2,8]
+ CRUSH rule 0 x 631 [1,8,4]
+ CRUSH rule 0 x 632 [8,2]
+ CRUSH rule 0 x 633 [8,2]
+ CRUSH rule 0 x 634 [1,8]
+ CRUSH rule 0 x 635 [4,8,2]
+ CRUSH rule 0 x 636 [1,4,8]
+ CRUSH rule 0 x 637 [1,8]
+ CRUSH rule 0 x 638 [8,2,4]
+ CRUSH rule 0 x 639 [2,8]
+ CRUSH rule 0 x 640 [2,8]
+ CRUSH rule 0 x 641 [8,2]
+ CRUSH rule 0 x 642 [2,8]
+ CRUSH rule 0 x 643 [1,8]
+ CRUSH rule 0 x 644 [8,1]
+ CRUSH rule 0 x 645 [8,2]
+ CRUSH rule 0 x 646 [8,1,4]
+ CRUSH rule 0 x 647 [8,1]
+ CRUSH rule 0 x 648 [1,8]
+ CRUSH rule 0 x 649 [4,8,1]
+ CRUSH rule 0 x 650 [8,4,1]
+ CRUSH rule 0 x 651 [4,6,1]
+ CRUSH rule 0 x 652 [4,8,1]
+ CRUSH rule 0 x 653 [8,2]
+ CRUSH rule 0 x 654 [6,2]
+ CRUSH rule 0 x 655 [1,4,8]
+ CRUSH rule 0 x 656 [8,1]
+ CRUSH rule 0 x 657 [6,1]
+ CRUSH rule 0 x 658 [8,2]
+ CRUSH rule 0 x 659 [4,8,1]
+ CRUSH rule 0 x 660 [8,2]
+ CRUSH rule 0 x 661 [1,8]
+ CRUSH rule 0 x 662 [2,8]
+ CRUSH rule 0 x 663 [1,4,8]
+ CRUSH rule 0 x 664 [1,4,8]
+ CRUSH rule 0 x 665 [4,6,1]
+ CRUSH rule 0 x 666 [2,8]
+ CRUSH rule 0 x 667 [1,4,8]
+ CRUSH rule 0 x 668 [4,8,2]
+ CRUSH rule 0 x 669 [6,4,1]
+ CRUSH rule 0 x 670 [4,1,8]
+ CRUSH rule 0 x 671 [2,8]
+ CRUSH rule 0 x 672 [4,2,8]
+ CRUSH rule 0 x 673 [4,2,8]
+ CRUSH rule 0 x 674 [1,8]
+ CRUSH rule 0 x 675 [1,8,4]
+ CRUSH rule 0 x 676 [2,4,8]
+ CRUSH rule 0 x 677 [4,1,8]
+ CRUSH rule 0 x 678 [2,4,8]
+ CRUSH rule 0 x 679 [8,2]
+ CRUSH rule 0 x 680 [2,8]
+ CRUSH rule 0 x 681 [8,1]
+ CRUSH rule 0 x 682 [1,4,8]
+ CRUSH rule 0 x 683 [1,4,8]
+ CRUSH rule 0 x 684 [8,2,4]
+ CRUSH rule 0 x 685 [8,2,4]
+ CRUSH rule 0 x 686 [1,4,8]
+ CRUSH rule 0 x 687 [6,2]
+ CRUSH rule 0 x 688 [4,8,2]
+ CRUSH rule 0 x 689 [8,4,1]
+ CRUSH rule 0 x 690 [8,1,4]
+ CRUSH rule 0 x 691 [1,8]
+ CRUSH rule 0 x 692 [8,1]
+ CRUSH rule 0 x 693 [8,4,2]
+ CRUSH rule 0 x 694 [8,4,2]
+ CRUSH rule 0 x 695 [2,8,4]
+ CRUSH rule 0 x 696 [1,8]
+ CRUSH rule 0 x 697 [8,1,4]
+ CRUSH rule 0 x 698 [8,2,4]
+ CRUSH rule 0 x 699 [1,8,4]
+ CRUSH rule 0 x 700 [1,6]
+ CRUSH rule 0 x 701 [1,8]
+ CRUSH rule 0 x 702 [2,8]
+ CRUSH rule 0 x 703 [8,1]
+ CRUSH rule 0 x 704 [1,4,8]
+ CRUSH rule 0 x 705 [8,1,4]
+ CRUSH rule 0 x 706 [1,4,8]
+ CRUSH rule 0 x 707 [8,4,1]
+ CRUSH rule 0 x 708 [4,8,1]
+ CRUSH rule 0 x 709 [8,1]
+ CRUSH rule 0 x 710 [8,1]
+ CRUSH rule 0 x 711 [2,4,8]
+ CRUSH rule 0 x 712 [2,8]
+ CRUSH rule 0 x 713 [8,4,1]
+ CRUSH rule 0 x 714 [2,8]
+ CRUSH rule 0 x 715 [1,8]
+ CRUSH rule 0 x 716 [4,8,1]
+ CRUSH rule 0 x 717 [8,2,4]
+ CRUSH rule 0 x 718 [8,1]
+ CRUSH rule 0 x 719 [2,6,4]
+ CRUSH rule 0 x 720 [8,1,4]
+ CRUSH rule 0 x 721 [4,6,1]
+ CRUSH rule 0 x 722 [8,1]
+ CRUSH rule 0 x 723 [4,2,8]
+ CRUSH rule 0 x 724 [2,6]
+ CRUSH rule 0 x 725 [1,8]
+ CRUSH rule 0 x 726 [4,8,2]
+ CRUSH rule 0 x 727 [4,8,2]
+ CRUSH rule 0 x 728 [2,8,4]
+ CRUSH rule 0 x 729 [8,1]
+ CRUSH rule 0 x 730 [4,8,2]
+ CRUSH rule 0 x 731 [4,1,8]
+ CRUSH rule 0 x 732 [1,8]
+ CRUSH rule 0 x 733 [4,8,2]
+ CRUSH rule 0 x 734 [8,4,1]
+ CRUSH rule 0 x 735 [4,8,1]
+ CRUSH rule 0 x 736 [4,8,1]
+ CRUSH rule 0 x 737 [1,8,4]
+ CRUSH rule 0 x 738 [4,1,8]
+ CRUSH rule 0 x 739 [2,8]
+ CRUSH rule 0 x 740 [1,8,4]
+ CRUSH rule 0 x 741 [8,2]
+ CRUSH rule 0 x 742 [8,2]
+ CRUSH rule 0 x 743 [8,1,4]
+ CRUSH rule 0 x 744 [4,8,1]
+ CRUSH rule 0 x 745 [1,8]
+ CRUSH rule 0 x 746 [1,8]
+ CRUSH rule 0 x 747 [8,2]
+ CRUSH rule 0 x 748 [2,8,4]
+ CRUSH rule 0 x 749 [4,8,2]
+ CRUSH rule 0 x 750 [1,8,4]
+ CRUSH rule 0 x 751 [2,8]
+ CRUSH rule 0 x 752 [8,1]
+ CRUSH rule 0 x 753 [8,4,2]
+ CRUSH rule 0 x 754 [8,4,2]
+ CRUSH rule 0 x 755 [1,8,4]
+ CRUSH rule 0 x 756 [8,1]
+ CRUSH rule 0 x 757 [8,1,4]
+ CRUSH rule 0 x 758 [8,1]
+ CRUSH rule 0 x 759 [8,4,2]
+ CRUSH rule 0 x 760 [1,4,8]
+ CRUSH rule 0 x 761 [2,6]
+ CRUSH rule 0 x 762 [2,8]
+ CRUSH rule 0 x 763 [8,4,1]
+ CRUSH rule 0 x 764 [1,8]
+ CRUSH rule 0 x 765 [8,2]
+ CRUSH rule 0 x 766 [8,1]
+ CRUSH rule 0 x 767 [1,8,4]
+ CRUSH rule 0 x 768 [8,4,2]
+ CRUSH rule 0 x 769 [8,2,4]
+ CRUSH rule 0 x 770 [8,1,4]
+ CRUSH rule 0 x 771 [8,2,4]
+ CRUSH rule 0 x 772 [8,4,2]
+ CRUSH rule 0 x 773 [4,2,8]
+ CRUSH rule 0 x 774 [8,1]
+ CRUSH rule 0 x 775 [8,4,2]
+ CRUSH rule 0 x 776 [6,2]
+ CRUSH rule 0 x 777 [4,1,8]
+ CRUSH rule 0 x 778 [1,6,4]
+ CRUSH rule 0 x 779 [2,8]
+ CRUSH rule 0 x 780 [2,4,8]
+ CRUSH rule 0 x 781 [8,2]
+ CRUSH rule 0 x 782 [4,2,8]
+ CRUSH rule 0 x 783 [8,1,4]
+ CRUSH rule 0 x 784 [1,4,8]
+ CRUSH rule 0 x 785 [8,1,4]
+ CRUSH rule 0 x 786 [8,1]
+ CRUSH rule 0 x 787 [1,8,4]
+ CRUSH rule 0 x 788 [8,2,4]
+ CRUSH rule 0 x 789 [1,8]
+ CRUSH rule 0 x 790 [8,1]
+ CRUSH rule 0 x 791 [4,8,2]
+ CRUSH rule 0 x 792 [4,8,1]
+ CRUSH rule 0 x 793 [8,2,4]
+ CRUSH rule 0 x 794 [2,8,4]
+ CRUSH rule 0 x 795 [1,8]
+ CRUSH rule 0 x 796 [8,1]
+ CRUSH rule 0 x 797 [2,4,8]
+ CRUSH rule 0 x 798 [6,1]
+ CRUSH rule 0 x 799 [4,2,8]
+ CRUSH rule 0 x 800 [2,8]
+ CRUSH rule 0 x 801 [4,8,2]
+ CRUSH rule 0 x 802 [1,8,4]
+ CRUSH rule 0 x 803 [2,8]
+ CRUSH rule 0 x 804 [8,2]
+ CRUSH rule 0 x 805 [8,2]
+ CRUSH rule 0 x 806 [1,4,8]
+ CRUSH rule 0 x 807 [4,8,2]
+ CRUSH rule 0 x 808 [8,2]
+ CRUSH rule 0 x 809 [1,8]
+ CRUSH rule 0 x 810 [8,2]
+ CRUSH rule 0 x 811 [8,1]
+ CRUSH rule 0 x 812 [8,4,1]
+ CRUSH rule 0 x 813 [8,4,2]
+ CRUSH rule 0 x 814 [8,2]
+ CRUSH rule 0 x 815 [4,1,8]
+ CRUSH rule 0 x 816 [2,8]
+ CRUSH rule 0 x 817 [8,2]
+ CRUSH rule 0 x 818 [1,8]
+ CRUSH rule 0 x 819 [1,8]
+ CRUSH rule 0 x 820 [4,8,1]
+ CRUSH rule 0 x 821 [4,8,2]
+ CRUSH rule 0 x 822 [2,4,8]
+ CRUSH rule 0 x 823 [4,8,2]
+ CRUSH rule 0 x 824 [8,1]
+ CRUSH rule 0 x 825 [2,8,4]
+ CRUSH rule 0 x 826 [8,1,4]
+ CRUSH rule 0 x 827 [2,6,4]
+ CRUSH rule 0 x 828 [2,8]
+ CRUSH rule 0 x 829 [8,1]
+ CRUSH rule 0 x 830 [2,4,8]
+ CRUSH rule 0 x 831 [1,8]
+ CRUSH rule 0 x 832 [4,8,2]
+ CRUSH rule 0 x 833 [2,8]
+ CRUSH rule 0 x 834 [1,8]
+ CRUSH rule 0 x 835 [8,4,1]
+ CRUSH rule 0 x 836 [4,8,1]
+ CRUSH rule 0 x 837 [8,4,1]
+ CRUSH rule 0 x 838 [6,2,4]
+ CRUSH rule 0 x 839 [2,8]
+ CRUSH rule 0 x 840 [8,2]
+ CRUSH rule 0 x 841 [4,8,1]
+ CRUSH rule 0 x 842 [2,8]
+ CRUSH rule 0 x 843 [8,4,2]
+ CRUSH rule 0 x 844 [8,2]
+ CRUSH rule 0 x 845 [4,8,2]
+ CRUSH rule 0 x 846 [4,2,8]
+ CRUSH rule 0 x 847 [2,8]
+ CRUSH rule 0 x 848 [2,8,4]
+ CRUSH rule 0 x 849 [4,8,1]
+ CRUSH rule 0 x 850 [1,6]
+ CRUSH rule 0 x 851 [6,1]
+ CRUSH rule 0 x 852 [8,4,2]
+ CRUSH rule 0 x 853 [6,2]
+ CRUSH rule 0 x 854 [8,2]
+ CRUSH rule 0 x 855 [8,2]
+ CRUSH rule 0 x 856 [8,4,1]
+ CRUSH rule 0 x 857 [8,1]
+ CRUSH rule 0 x 858 [6,2]
+ CRUSH rule 0 x 859 [8,2,4]
+ CRUSH rule 0 x 860 [2,8]
+ CRUSH rule 0 x 861 [8,1]
+ CRUSH rule 0 x 862 [8,1]
+ CRUSH rule 0 x 863 [8,1]
+ CRUSH rule 0 x 864 [8,1]
+ CRUSH rule 0 x 865 [8,1]
+ CRUSH rule 0 x 866 [8,2]
+ CRUSH rule 0 x 867 [8,1]
+ CRUSH rule 0 x 868 [8,2]
+ CRUSH rule 0 x 869 [8,4,2]
+ CRUSH rule 0 x 870 [2,8]
+ CRUSH rule 0 x 871 [1,8]
+ CRUSH rule 0 x 872 [1,8]
+ CRUSH rule 0 x 873 [4,8,1]
+ CRUSH rule 0 x 874 [2,6]
+ CRUSH rule 0 x 875 [2,8,4]
+ CRUSH rule 0 x 876 [4,8,1]
+ CRUSH rule 0 x 877 [8,4,2]
+ CRUSH rule 0 x 878 [2,8]
+ CRUSH rule 0 x 879 [8,1]
+ CRUSH rule 0 x 880 [1,8]
+ CRUSH rule 0 x 881 [4,8,2]
+ CRUSH rule 0 x 882 [1,8]
+ CRUSH rule 0 x 883 [2,4,8]
+ CRUSH rule 0 x 884 [8,1,4]
+ CRUSH rule 0 x 885 [4,1,8]
+ CRUSH rule 0 x 886 [8,2]
+ CRUSH rule 0 x 887 [8,4,2]
+ CRUSH rule 0 x 888 [8,1]
+ CRUSH rule 0 x 889 [2,8]
+ CRUSH rule 0 x 890 [8,1,4]
+ CRUSH rule 0 x 891 [1,8]
+ CRUSH rule 0 x 892 [8,2,4]
+ CRUSH rule 0 x 893 [2,6]
+ CRUSH rule 0 x 894 [8,4,1]
+ CRUSH rule 0 x 895 [4,1,8]
+ CRUSH rule 0 x 896 [1,8]
+ CRUSH rule 0 x 897 [2,8]
+ CRUSH rule 0 x 898 [1,4,8]
+ CRUSH rule 0 x 899 [1,8]
+ CRUSH rule 0 x 900 [4,1,8]
+ CRUSH rule 0 x 901 [2,8]
+ CRUSH rule 0 x 902 [8,4,2]
+ CRUSH rule 0 x 903 [8,1]
+ CRUSH rule 0 x 904 [8,2]
+ CRUSH rule 0 x 905 [8,2]
+ CRUSH rule 0 x 906 [1,8]
+ CRUSH rule 0 x 907 [8,2]
+ CRUSH rule 0 x 908 [8,1]
+ CRUSH rule 0 x 909 [2,8]
+ CRUSH rule 0 x 910 [8,1]
+ CRUSH rule 0 x 911 [8,1]
+ CRUSH rule 0 x 912 [1,8]
+ CRUSH rule 0 x 913 [8,1,4]
+ CRUSH rule 0 x 914 [6,4,1]
+ CRUSH rule 0 x 915 [8,2]
+ CRUSH rule 0 x 916 [4,1,8]
+ CRUSH rule 0 x 917 [1,4,8]
+ CRUSH rule 0 x 918 [8,2]
+ CRUSH rule 0 x 919 [8,2]
+ CRUSH rule 0 x 920 [8,2]
+ CRUSH rule 0 x 921 [1,8]
+ CRUSH rule 0 x 922 [8,4,1]
+ CRUSH rule 0 x 923 [4,8,2]
+ CRUSH rule 0 x 924 [1,8]
+ CRUSH rule 0 x 925 [4,8,2]
+ CRUSH rule 0 x 926 [2,8]
+ CRUSH rule 0 x 927 [1,8,4]
+ CRUSH rule 0 x 928 [8,1]
+ CRUSH rule 0 x 929 [4,2,8]
+ CRUSH rule 0 x 930 [2,8]
+ CRUSH rule 0 x 931 [2,8]
+ CRUSH rule 0 x 932 [4,2,8]
+ CRUSH rule 0 x 933 [8,4,2]
+ CRUSH rule 0 x 934 [8,2]
+ CRUSH rule 0 x 935 [8,2]
+ CRUSH rule 0 x 936 [1,8]
+ CRUSH rule 0 x 937 [4,8,1]
+ CRUSH rule 0 x 938 [8,4,2]
+ CRUSH rule 0 x 939 [2,8,4]
+ CRUSH rule 0 x 940 [8,2]
+ CRUSH rule 0 x 941 [2,6]
+ CRUSH rule 0 x 942 [1,8]
+ CRUSH rule 0 x 943 [8,2]
+ CRUSH rule 0 x 944 [8,1]
+ CRUSH rule 0 x 945 [8,2,4]
+ CRUSH rule 0 x 946 [2,8,4]
+ CRUSH rule 0 x 947 [2,8]
+ CRUSH rule 0 x 948 [8,1]
+ CRUSH rule 0 x 949 [6,2]
+ CRUSH rule 0 x 950 [8,1]
+ CRUSH rule 0 x 951 [8,2]
+ CRUSH rule 0 x 952 [2,8,4]
+ CRUSH rule 0 x 953 [1,4,8]
+ CRUSH rule 0 x 954 [2,8]
+ CRUSH rule 0 x 955 [8,1,4]
+ CRUSH rule 0 x 956 [1,8,4]
+ CRUSH rule 0 x 957 [8,1,4]
+ CRUSH rule 0 x 958 [8,4,2]
+ CRUSH rule 0 x 959 [4,1,8]
+ CRUSH rule 0 x 960 [6,2]
+ CRUSH rule 0 x 961 [1,8]
+ CRUSH rule 0 x 962 [8,4,2]
+ CRUSH rule 0 x 963 [2,4,8]
+ CRUSH rule 0 x 964 [2,8]
+ CRUSH rule 0 x 965 [8,1]
+ CRUSH rule 0 x 966 [4,8,1]
+ CRUSH rule 0 x 967 [8,4,2]
+ CRUSH rule 0 x 968 [8,2]
+ CRUSH rule 0 x 969 [8,2,4]
+ CRUSH rule 0 x 970 [2,8,4]
+ CRUSH rule 0 x 971 [1,8]
+ CRUSH rule 0 x 972 [1,8]
+ CRUSH rule 0 x 973 [1,8]
+ CRUSH rule 0 x 974 [4,1,8]
+ CRUSH rule 0 x 975 [4,8,1]
+ CRUSH rule 0 x 976 [4,8,2]
+ CRUSH rule 0 x 977 [8,4,2]
+ CRUSH rule 0 x 978 [8,2,4]
+ CRUSH rule 0 x 979 [8,2,4]
+ CRUSH rule 0 x 980 [8,2,4]
+ CRUSH rule 0 x 981 [8,1]
+ CRUSH rule 0 x 982 [1,8]
+ CRUSH rule 0 x 983 [4,8,1]
+ CRUSH rule 0 x 984 [2,8]
+ CRUSH rule 0 x 985 [2,4,8]
+ CRUSH rule 0 x 986 [8,4,2]
+ CRUSH rule 0 x 987 [2,8]
+ CRUSH rule 0 x 988 [1,4,6]
+ CRUSH rule 0 x 989 [1,8]
+ CRUSH rule 0 x 990 [1,8,4]
+ CRUSH rule 0 x 991 [1,4,8]
+ CRUSH rule 0 x 992 [8,1,4]
+ CRUSH rule 0 x 993 [2,8,4]
+ CRUSH rule 0 x 994 [4,8,1]
+ CRUSH rule 0 x 995 [8,1,4]
+ CRUSH rule 0 x 996 [8,4,1]
+ CRUSH rule 0 x 997 [8,4,1]
+ CRUSH rule 0 x 998 [8,1,4]
+ CRUSH rule 0 x 999 [1,8,4]
+ CRUSH rule 0 x 1000 [8,4,1]
+ CRUSH rule 0 x 1001 [2,8]
+ CRUSH rule 0 x 1002 [1,8]
+ CRUSH rule 0 x 1003 [2,8]
+ CRUSH rule 0 x 1004 [8,1,4]
+ CRUSH rule 0 x 1005 [8,1]
+ CRUSH rule 0 x 1006 [1,8,4]
+ CRUSH rule 0 x 1007 [1,4,8]
+ CRUSH rule 0 x 1008 [1,8]
+ CRUSH rule 0 x 1009 [6,4,1]
+ CRUSH rule 0 x 1010 [1,8]
+ CRUSH rule 0 x 1011 [4,2,8]
+ CRUSH rule 0 x 1012 [1,8]
+ CRUSH rule 0 x 1013 [2,8]
+ CRUSH rule 0 x 1014 [2,8,4]
+ CRUSH rule 0 x 1015 [8,2]
+ CRUSH rule 0 x 1016 [2,4,8]
+ CRUSH rule 0 x 1017 [6,1,4]
+ CRUSH rule 0 x 1018 [4,1,8]
+ CRUSH rule 0 x 1019 [4,8,1]
+ CRUSH rule 0 x 1020 [1,8]
+ CRUSH rule 0 x 1021 [2,8]
+ CRUSH rule 0 x 1022 [1,8,4]
+ CRUSH rule 0 x 1023 [4,2,8]
+ rule 0 (choose) num_rep 3 result size == 2:\t501/1024 (esc)
+ rule 0 (choose) num_rep 3 result size == 3:\t523/1024 (esc)
+ rule 1 (choose-two), x = 0..1023, numrep = 2..3
+ CRUSH rule 1 x 0 [2,1]
+ CRUSH rule 1 x 1 [2,8]
+ CRUSH rule 1 x 2 [1,8]
+ CRUSH rule 1 x 3 [8,1]
+ CRUSH rule 1 x 4 [4,8]
+ CRUSH rule 1 x 5 [8,2]
+ CRUSH rule 1 x 6 [2,8]
+ CRUSH rule 1 x 7 [4,8]
+ CRUSH rule 1 x 8 [4,2]
+ CRUSH rule 1 x 9 [2,4]
+ CRUSH rule 1 x 10 [2,1]
+ CRUSH rule 1 x 11 [2,8]
+ CRUSH rule 1 x 12 [2,1]
+ CRUSH rule 1 x 13 [4,8]
+ CRUSH rule 1 x 14 [8,1]
+ CRUSH rule 1 x 15 [8,2]
+ CRUSH rule 1 x 16 [8,1]
+ CRUSH rule 1 x 17 [4,8]
+ CRUSH rule 1 x 18 [1,6]
+ CRUSH rule 1 x 19 [8,4]
+ CRUSH rule 1 x 20 [2,1]
+ CRUSH rule 1 x 21 [8,1]
+ CRUSH rule 1 x 22 [8]
+ CRUSH rule 1 x 23 [4,8]
+ CRUSH rule 1 x 24 [1,2]
+ CRUSH rule 1 x 25 [4,8]
+ CRUSH rule 1 x 26 [2,8]
+ CRUSH rule 1 x 27 [4,1]
+ CRUSH rule 1 x 28 [8,2]
+ CRUSH rule 1 x 29 [8,4]
+ CRUSH rule 1 x 30 [4,8]
+ CRUSH rule 1 x 31 [8,2]
+ CRUSH rule 1 x 32 [2,6]
+ CRUSH rule 1 x 33 [2,8]
+ CRUSH rule 1 x 34 [2,1]
+ CRUSH rule 1 x 35 [1,8]
+ CRUSH rule 1 x 36 [8,1]
+ CRUSH rule 1 x 37 [1,8]
+ CRUSH rule 1 x 38 [4,8]
+ CRUSH rule 1 x 39 [8,1]
+ CRUSH rule 1 x 40 [8,2]
+ CRUSH rule 1 x 41 [2,1]
+ CRUSH rule 1 x 42 [1,2]
+ CRUSH rule 1 x 43 [1,2]
+ CRUSH rule 1 x 44 [1,8]
+ CRUSH rule 1 x 45 [8,2]
+ CRUSH rule 1 x 46 [2,1]
+ CRUSH rule 1 x 47 [4,2]
+ CRUSH rule 1 x 48 [8,1]
+ CRUSH rule 1 x 49 [8,2]
+ CRUSH rule 1 x 50 [4,8]
+ CRUSH rule 1 x 51 [2,8]
+ CRUSH rule 1 x 52 [8,2]
+ CRUSH rule 1 x 53 [4,2]
+ CRUSH rule 1 x 54 [8,4]
+ CRUSH rule 1 x 55 [8,1]
+ CRUSH rule 1 x 56 [8,4]
+ CRUSH rule 1 x 57 [2,1]
+ CRUSH rule 1 x 58 [1,2]
+ CRUSH rule 1 x 59 [8,2]
+ CRUSH rule 1 x 60 [4,8]
+ CRUSH rule 1 x 61 [4,8]
+ CRUSH rule 1 x 62 [8,1]
+ CRUSH rule 1 x 63 [8,1]
+ CRUSH rule 1 x 64 [4,2]
+ CRUSH rule 1 x 65 [8,4]
+ CRUSH rule 1 x 66 [4,2]
+ CRUSH rule 1 x 67 [4,2]
+ CRUSH rule 1 x 68 [1,2]
+ CRUSH rule 1 x 69 [1,2]
+ CRUSH rule 1 x 70 [8,2]
+ CRUSH rule 1 x 71 [2,8]
+ CRUSH rule 1 x 72 [8,1]
+ CRUSH rule 1 x 73 [2,8]
+ CRUSH rule 1 x 74 [1,8]
+ CRUSH rule 1 x 75 [4,2]
+ CRUSH rule 1 x 76 [4,1]
+ CRUSH rule 1 x 77 [8,2]
+ CRUSH rule 1 x 78 [1]
+ CRUSH rule 1 x 79 [4,1]
+ CRUSH rule 1 x 80 [2,4]
+ CRUSH rule 1 x 81 [2,1]
+ CRUSH rule 1 x 82 [6,1]
+ CRUSH rule 1 x 83 [2,8]
+ CRUSH rule 1 x 84 [8,2]
+ CRUSH rule 1 x 85 [4,8]
+ CRUSH rule 1 x 86 [2,1]
+ CRUSH rule 1 x 87 [2,8]
+ CRUSH rule 1 x 88 [1,6]
+ CRUSH rule 1 x 89 [2,1]
+ CRUSH rule 1 x 90 [8,2]
+ CRUSH rule 1 x 91 [4,8]
+ CRUSH rule 1 x 92 [1,8]
+ CRUSH rule 1 x 93 [8,4]
+ CRUSH rule 1 x 94 [1,2]
+ CRUSH rule 1 x 95 [8,1]
+ CRUSH rule 1 x 96 [1,8]
+ CRUSH rule 1 x 97 [8,1]
+ CRUSH rule 1 x 98 [2,1]
+ CRUSH rule 1 x 99 [2,8]
+ CRUSH rule 1 x 100 [1,8]
+ CRUSH rule 1 x 101 [8,2]
+ CRUSH rule 1 x 102 [2,1]
+ CRUSH rule 1 x 103 [2,8]
+ CRUSH rule 1 x 104 [8,4]
+ CRUSH rule 1 x 105 [2,4]
+ CRUSH rule 1 x 106 [1,8]
+ CRUSH rule 1 x 107 [2,1]
+ CRUSH rule 1 x 108 [8,2]
+ CRUSH rule 1 x 109 [1,2]
+ CRUSH rule 1 x 110 [4,2]
+ CRUSH rule 1 x 111 [2,1]
+ CRUSH rule 1 x 112 [2,1]
+ CRUSH rule 1 x 113 [8,2]
+ CRUSH rule 1 x 114 [8,4]
+ CRUSH rule 1 x 115 [8,2]
+ CRUSH rule 1 x 116 [1,2]
+ CRUSH rule 1 x 117 [6,8]
+ CRUSH rule 1 x 118 [2,8]
+ CRUSH rule 1 x 119 [2,8]
+ CRUSH rule 1 x 120 [2,1]
+ CRUSH rule 1 x 121 [2,1]
+ CRUSH rule 1 x 122 [8,1]
+ CRUSH rule 1 x 123 [2,8]
+ CRUSH rule 1 x 124 [8]
+ CRUSH rule 1 x 125 [1,8]
+ CRUSH rule 1 x 126 [8,2]
+ CRUSH rule 1 x 127 [4,8]
+ CRUSH rule 1 x 128 [2,8]
+ CRUSH rule 1 x 129 [2,1]
+ CRUSH rule 1 x 130 [4,8]
+ CRUSH rule 1 x 131 [1,2]
+ CRUSH rule 1 x 132 [1,2]
+ CRUSH rule 1 x 133 [8,2]
+ CRUSH rule 1 x 134 [1,8]
+ CRUSH rule 1 x 135 [4,8]
+ CRUSH rule 1 x 136 [2,1]
+ CRUSH rule 1 x 137 [8,4]
+ CRUSH rule 1 x 138 [8,4]
+ CRUSH rule 1 x 139 [4,2]
+ CRUSH rule 1 x 140 [1,8]
+ CRUSH rule 1 x 141 [8,1]
+ CRUSH rule 1 x 142 [4,6]
+ CRUSH rule 1 x 143 [4,8]
+ CRUSH rule 1 x 144 [8,1]
+ CRUSH rule 1 x 145 [8,2]
+ CRUSH rule 1 x 146 [2,8]
+ CRUSH rule 1 x 147 [2,8]
+ CRUSH rule 1 x 148 [4,1]
+ CRUSH rule 1 x 149 [4,8]
+ CRUSH rule 1 x 150 [1,8]
+ CRUSH rule 1 x 151 [1,2]
+ CRUSH rule 1 x 152 [8,1]
+ CRUSH rule 1 x 153 [8,4]
+ CRUSH rule 1 x 154 [4,2]
+ CRUSH rule 1 x 155 [4,8]
+ CRUSH rule 1 x 156 [4,8]
+ CRUSH rule 1 x 157 [2,1]
+ CRUSH rule 1 x 158 [2,8]
+ CRUSH rule 1 x 159 [8,2]
+ CRUSH rule 1 x 160 [2,8]
+ CRUSH rule 1 x 161 [1,4]
+ CRUSH rule 1 x 162 [1,8]
+ CRUSH rule 1 x 163 [4,8]
+ CRUSH rule 1 x 164 [8,2]
+ CRUSH rule 1 x 165 [8,2]
+ CRUSH rule 1 x 166 [2,1]
+ CRUSH rule 1 x 167 [1,2]
+ CRUSH rule 1 x 168 [4,2]
+ CRUSH rule 1 x 169 [2,8]
+ CRUSH rule 1 x 170 [1,2]
+ CRUSH rule 1 x 171 [8,4]
+ CRUSH rule 1 x 172 [1,8]
+ CRUSH rule 1 x 173 [8,4]
+ CRUSH rule 1 x 174 [1,2]
+ CRUSH rule 1 x 175 [8,1]
+ CRUSH rule 1 x 176 [2,1]
+ CRUSH rule 1 x 177 [8,2]
+ CRUSH rule 1 x 178 [4,2]
+ CRUSH rule 1 x 179 [8,1]
+ CRUSH rule 1 x 180 [1,8]
+ CRUSH rule 1 x 181 [8,2]
+ CRUSH rule 1 x 182 [8,1]
+ CRUSH rule 1 x 183 [8,1]
+ CRUSH rule 1 x 184 [4,8]
+ CRUSH rule 1 x 185 [8,4]
+ CRUSH rule 1 x 186 [2,1]
+ CRUSH rule 1 x 187 [1,8]
+ CRUSH rule 1 x 188 [1,8]
+ CRUSH rule 1 x 189 [1,8]
+ CRUSH rule 1 x 190 [1,2]
+ CRUSH rule 1 x 191 [8,4]
+ CRUSH rule 1 x 192 [4,1]
+ CRUSH rule 1 x 193 [4,2]
+ CRUSH rule 1 x 194 [1,8]
+ CRUSH rule 1 x 195 [8,4]
+ CRUSH rule 1 x 196 [8,1]
+ CRUSH rule 1 x 197 [8,4]
+ CRUSH rule 1 x 198 [2,1]
+ CRUSH rule 1 x 199 [1,4]
+ CRUSH rule 1 x 200 [1,8]
+ CRUSH rule 1 x 201 [8,1]
+ CRUSH rule 1 x 202 [8,2]
+ CRUSH rule 1 x 203 [1,8]
+ CRUSH rule 1 x 204 [2,1]
+ CRUSH rule 1 x 205 [1,8]
+ CRUSH rule 1 x 206 [1,2]
+ CRUSH rule 1 x 207 [2,1]
+ CRUSH rule 1 x 208 [8,1]
+ CRUSH rule 1 x 209 [1,2]
+ CRUSH rule 1 x 210 [1,2]
+ CRUSH rule 1 x 211 [4,8]
+ CRUSH rule 1 x 212 [8,1]
+ CRUSH rule 1 x 213 [8,4]
+ CRUSH rule 1 x 214 [8]
+ CRUSH rule 1 x 215 [8,1]
+ CRUSH rule 1 x 216 [8,1]
+ CRUSH rule 1 x 217 [1,2]
+ CRUSH rule 1 x 218 [2,8]
+ CRUSH rule 1 x 219 [2,8]
+ CRUSH rule 1 x 220 [4,8]
+ CRUSH rule 1 x 221 [1,8]
+ CRUSH rule 1 x 222 [8,1]
+ CRUSH rule 1 x 223 [1,2]
+ CRUSH rule 1 x 224 [1,4]
+ CRUSH rule 1 x 225 [8,2]
+ CRUSH rule 1 x 226 [8,2]
+ CRUSH rule 1 x 227 [4,8]
+ CRUSH rule 1 x 228 [1,8]
+ CRUSH rule 1 x 229 [4,1]
+ CRUSH rule 1 x 230 [4,8]
+ CRUSH rule 1 x 231 [4,8]
+ CRUSH rule 1 x 232 [2,8]
+ CRUSH rule 1 x 233 [8,2]
+ CRUSH rule 1 x 234 [1,2]
+ CRUSH rule 1 x 235 [4,8]
+ CRUSH rule 1 x 236 [1,2]
+ CRUSH rule 1 x 237 [4,8]
+ CRUSH rule 1 x 238 [8]
+ CRUSH rule 1 x 239 [8,6]
+ CRUSH rule 1 x 240 [4,8]
+ CRUSH rule 1 x 241 [2,1]
+ CRUSH rule 1 x 242 [2,8]
+ CRUSH rule 1 x 243 [8,2]
+ CRUSH rule 1 x 244 [4,8]
+ CRUSH rule 1 x 245 [8,2]
+ CRUSH rule 1 x 246 [1,2]
+ CRUSH rule 1 x 247 [8,2]
+ CRUSH rule 1 x 248 [8,2]
+ CRUSH rule 1 x 249 [2,1]
+ CRUSH rule 1 x 250 [2,1]
+ CRUSH rule 1 x 251 [2,8]
+ CRUSH rule 1 x 252 [4,8]
+ CRUSH rule 1 x 253 [1,2]
+ CRUSH rule 1 x 254 [4,8]
+ CRUSH rule 1 x 255 [1,8]
+ CRUSH rule 1 x 256 [4,8]
+ CRUSH rule 1 x 257 [2,8]
+ CRUSH rule 1 x 258 [4]
+ CRUSH rule 1 x 259 [8,6]
+ CRUSH rule 1 x 260 [8,2]
+ CRUSH rule 1 x 261 [8,1]
+ CRUSH rule 1 x 262 [8,2]
+ CRUSH rule 1 x 263 [8,4]
+ CRUSH rule 1 x 264 [1,8]
+ CRUSH rule 1 x 265 [8]
+ CRUSH rule 1 x 266 [8,2]
+ CRUSH rule 1 x 267 [2,8]
+ CRUSH rule 1 x 268 [1,8]
+ CRUSH rule 1 x 269 [1,8]
+ CRUSH rule 1 x 270 [4,1]
+ CRUSH rule 1 x 271 [8,4]
+ CRUSH rule 1 x 272 [2,8]
+ CRUSH rule 1 x 273 [4,1]
+ CRUSH rule 1 x 274 [8,2]
+ CRUSH rule 1 x 275 [4,8]
+ CRUSH rule 1 x 276 [8,1]
+ CRUSH rule 1 x 277 [8,1]
+ CRUSH rule 1 x 278 [8,2]
+ CRUSH rule 1 x 279 [8,4]
+ CRUSH rule 1 x 280 [2,8]
+ CRUSH rule 1 x 281 [8,2]
+ CRUSH rule 1 x 282 [1,2]
+ CRUSH rule 1 x 283 [8,2]
+ CRUSH rule 1 x 284 [8]
+ CRUSH rule 1 x 285 [4,8]
+ CRUSH rule 1 x 286 [2,1]
+ CRUSH rule 1 x 287 [1,2]
+ CRUSH rule 1 x 288 [8,1]
+ CRUSH rule 1 x 289 [4,8]
+ CRUSH rule 1 x 290 [1,4]
+ CRUSH rule 1 x 291 [1,2]
+ CRUSH rule 1 x 292 [8,2]
+ CRUSH rule 1 x 293 [8,1]
+ CRUSH rule 1 x 294 [8,4]
+ CRUSH rule 1 x 295 [4,8]
+ CRUSH rule 1 x 296 [4,1]
+ CRUSH rule 1 x 297 [8,2]
+ CRUSH rule 1 x 298 [1,2]
+ CRUSH rule 1 x 299 [2,1]
+ CRUSH rule 1 x 300 [8,2]
+ CRUSH rule 1 x 301 [1,8]
+ CRUSH rule 1 x 302 [8,1]
+ CRUSH rule 1 x 303 [8,4]
+ CRUSH rule 1 x 304 [2,8]
+ CRUSH rule 1 x 305 [2,8]
+ CRUSH rule 1 x 306 [1,8]
+ CRUSH rule 1 x 307 [2,1]
+ CRUSH rule 1 x 308 [2,8]
+ CRUSH rule 1 x 309 [8,2]
+ CRUSH rule 1 x 310 [4,6]
+ CRUSH rule 1 x 311 [4,2]
+ CRUSH rule 1 x 312 [2,1]
+ CRUSH rule 1 x 313 [4,8]
+ CRUSH rule 1 x 314 [6,1]
+ CRUSH rule 1 x 315 [2,1]
+ CRUSH rule 1 x 316 [8,2]
+ CRUSH rule 1 x 317 [2,8]
+ CRUSH rule 1 x 318 [8,2]
+ CRUSH rule 1 x 319 [8,2]
+ CRUSH rule 1 x 320 [8,2]
+ CRUSH rule 1 x 321 [1,2]
+ CRUSH rule 1 x 322 [2,8]
+ CRUSH rule 1 x 323 [4,8]
+ CRUSH rule 1 x 324 [8,1]
+ CRUSH rule 1 x 325 [4,8]
+ CRUSH rule 1 x 326 [8,6]
+ CRUSH rule 1 x 327 [1,8]
+ CRUSH rule 1 x 328 [8,4]
+ CRUSH rule 1 x 329 [4,8]
+ CRUSH rule 1 x 330 [4,8]
+ CRUSH rule 1 x 331 [2,8]
+ CRUSH rule 1 x 332 [2,1]
+ CRUSH rule 1 x 333 [8,1]
+ CRUSH rule 1 x 334 [8,1]
+ CRUSH rule 1 x 335 [8,1]
+ CRUSH rule 1 x 336 [4,8]
+ CRUSH rule 1 x 337 [8,2]
+ CRUSH rule 1 x 338 [1,8]
+ CRUSH rule 1 x 339 [8]
+ CRUSH rule 1 x 340 [2,1]
+ CRUSH rule 1 x 341 [4,1]
+ CRUSH rule 1 x 342 [2,8]
+ CRUSH rule 1 x 343 [8]
+ CRUSH rule 1 x 344 [6,2]
+ CRUSH rule 1 x 345 [2,1]
+ CRUSH rule 1 x 346 [8,2]
+ CRUSH rule 1 x 347 [4,1]
+ CRUSH rule 1 x 348 [8,2]
+ CRUSH rule 1 x 349 [1,8]
+ CRUSH rule 1 x 350 [8,1]
+ CRUSH rule 1 x 351 [1,8]
+ CRUSH rule 1 x 352 [1,2]
+ CRUSH rule 1 x 353 [8,1]
+ CRUSH rule 1 x 354 [1,8]
+ CRUSH rule 1 x 355 [1,2]
+ CRUSH rule 1 x 356 [4,8]
+ CRUSH rule 1 x 357 [8,1]
+ CRUSH rule 1 x 358 [2,1]
+ CRUSH rule 1 x 359 [6,8]
+ CRUSH rule 1 x 360 [1,2]
+ CRUSH rule 1 x 361 [8,4]
+ CRUSH rule 1 x 362 [4,6]
+ CRUSH rule 1 x 363 [4,1]
+ CRUSH rule 1 x 364 [2,8]
+ CRUSH rule 1 x 365 [8,1]
+ CRUSH rule 1 x 366 [8,2]
+ CRUSH rule 1 x 367 [4,8]
+ CRUSH rule 1 x 368 [8,4]
+ CRUSH rule 1 x 369 [8]
+ CRUSH rule 1 x 370 [8,2]
+ CRUSH rule 1 x 371 [1,4]
+ CRUSH rule 1 x 372 [8,1]
+ CRUSH rule 1 x 373 [1,8]
+ CRUSH rule 1 x 374 [2,8]
+ CRUSH rule 1 x 375 [8,4]
+ CRUSH rule 1 x 376 [8,1]
+ CRUSH rule 1 x 377 [1,2]
+ CRUSH rule 1 x 378 [1,2]
+ CRUSH rule 1 x 379 [8,1]
+ CRUSH rule 1 x 380 [2,1]
+ CRUSH rule 1 x 381 [1,4]
+ CRUSH rule 1 x 382 [1,4]
+ CRUSH rule 1 x 383 [4,1]
+ CRUSH rule 1 x 384 [8,2]
+ CRUSH rule 1 x 385 [8,1]
+ CRUSH rule 1 x 386 [1,8]
+ CRUSH rule 1 x 387 [1,4]
+ CRUSH rule 1 x 388 [8,1]
+ CRUSH rule 1 x 389 [1,4]
+ CRUSH rule 1 x 390 [4,8]
+ CRUSH rule 1 x 391 [4,8]
+ CRUSH rule 1 x 392 [1,8]
+ CRUSH rule 1 x 393 [8,2]
+ CRUSH rule 1 x 394 [8,1]
+ CRUSH rule 1 x 395 [8,1]
+ CRUSH rule 1 x 396 [4,2]
+ CRUSH rule 1 x 397 [2,1]
+ CRUSH rule 1 x 398 [2,4]
+ CRUSH rule 1 x 399 [8,2]
+ CRUSH rule 1 x 400 [8,1]
+ CRUSH rule 1 x 401 [1,2]
+ CRUSH rule 1 x 402 [8,1]
+ CRUSH rule 1 x 403 [1,2]
+ CRUSH rule 1 x 404 [4,2]
+ CRUSH rule 1 x 405 [8,4]
+ CRUSH rule 1 x 406 [2,1]
+ CRUSH rule 1 x 407 [2,8]
+ CRUSH rule 1 x 408 [4,1]
+ CRUSH rule 1 x 409 [8,4]
+ CRUSH rule 1 x 410 [8,1]
+ CRUSH rule 1 x 411 [2,1]
+ CRUSH rule 1 x 412 [2,6]
+ CRUSH rule 1 x 413 [8,2]
+ CRUSH rule 1 x 414 [4,1]
+ CRUSH rule 1 x 415 [2,8]
+ CRUSH rule 1 x 416 [2,1]
+ CRUSH rule 1 x 417 [8,6]
+ CRUSH rule 1 x 418 [8,2]
+ CRUSH rule 1 x 419 [8,4]
+ CRUSH rule 1 x 420 [1,4]
+ CRUSH rule 1 x 421 [8,4]
+ CRUSH rule 1 x 422 [6,8]
+ CRUSH rule 1 x 423 [2,4]
+ CRUSH rule 1 x 424 [8,2]
+ CRUSH rule 1 x 425 [1,2]
+ CRUSH rule 1 x 426 [8]
+ CRUSH rule 1 x 427 [1,8]
+ CRUSH rule 1 x 428 [4,8]
+ CRUSH rule 1 x 429 [4,8]
+ CRUSH rule 1 x 430 [4,8]
+ CRUSH rule 1 x 431 [4,1]
+ CRUSH rule 1 x 432 [8,1]
+ CRUSH rule 1 x 433 [8,2]
+ CRUSH rule 1 x 434 [8,2]
+ CRUSH rule 1 x 435 [2,6]
+ CRUSH rule 1 x 436 [4,1]
+ CRUSH rule 1 x 437 [8,2]
+ CRUSH rule 1 x 438 [2,4]
+ CRUSH rule 1 x 439 [1,6]
+ CRUSH rule 1 x 440 [2,8]
+ CRUSH rule 1 x 441 [4,6]
+ CRUSH rule 1 x 442 [2,1]
+ CRUSH rule 1 x 443 [8,4]
+ CRUSH rule 1 x 444 [8,1]
+ CRUSH rule 1 x 445 [8,1]
+ CRUSH rule 1 x 446 [2,8]
+ CRUSH rule 1 x 447 [2,1]
+ CRUSH rule 1 x 448 [8,2]
+ CRUSH rule 1 x 449 [8,6]
+ CRUSH rule 1 x 450 [1,8]
+ CRUSH rule 1 x 451 [8,4]
+ CRUSH rule 1 x 452 [8]
+ CRUSH rule 1 x 453 [6,8]
+ CRUSH rule 1 x 454 [8,2]
+ CRUSH rule 1 x 455 [2,8]
+ CRUSH rule 1 x 456 [8,2]
+ CRUSH rule 1 x 457 [8,2]
+ CRUSH rule 1 x 458 [2,8]
+ CRUSH rule 1 x 459 [2,1]
+ CRUSH rule 1 x 460 [8,2]
+ CRUSH rule 1 x 461 [8,2]
+ CRUSH rule 1 x 462 [8,1]
+ CRUSH rule 1 x 463 [8,2]
+ CRUSH rule 1 x 464 [8,4]
+ CRUSH rule 1 x 465 [6,8]
+ CRUSH rule 1 x 466 [8,2]
+ CRUSH rule 1 x 467 [8,2]
+ CRUSH rule 1 x 468 [8,4]
+ CRUSH rule 1 x 469 [8,1]
+ CRUSH rule 1 x 470 [4,2]
+ CRUSH rule 1 x 471 [1,2]
+ CRUSH rule 1 x 472 [1,8]
+ CRUSH rule 1 x 473 [1,2]
+ CRUSH rule 1 x 474 [8,1]
+ CRUSH rule 1 x 475 [8,4]
+ CRUSH rule 1 x 476 [4,1]
+ CRUSH rule 1 x 477 [4,8]
+ CRUSH rule 1 x 478 [8,1]
+ CRUSH rule 1 x 479 [2,1]
+ CRUSH rule 1 x 480 [1,8]
+ CRUSH rule 1 x 481 [2,4]
+ CRUSH rule 1 x 482 [2,8]
+ CRUSH rule 1 x 483 [2,1]
+ CRUSH rule 1 x 484 [1,2]
+ CRUSH rule 1 x 485 [1,8]
+ CRUSH rule 1 x 486 [4,1]
+ CRUSH rule 1 x 487 [8,1]
+ CRUSH rule 1 x 488 [2,8]
+ CRUSH rule 1 x 489 [2,8]
+ CRUSH rule 1 x 490 [6,2]
+ CRUSH rule 1 x 491 [1,2]
+ CRUSH rule 1 x 492 [8,2]
+ CRUSH rule 1 x 493 [2,1]
+ CRUSH rule 1 x 494 [1,2]
+ CRUSH rule 1 x 495 [4,6]
+ CRUSH rule 1 x 496 [8,4]
+ CRUSH rule 1 x 497 [4,8]
+ CRUSH rule 1 x 498 [2,4]
+ CRUSH rule 1 x 499 [8,4]
+ CRUSH rule 1 x 500 [4,8]
+ CRUSH rule 1 x 501 [2,8]
+ CRUSH rule 1 x 502 [6,1]
+ CRUSH rule 1 x 503 [2,1]
+ CRUSH rule 1 x 504 [8,2]
+ CRUSH rule 1 x 505 [1,8]
+ CRUSH rule 1 x 506 [4,1]
+ CRUSH rule 1 x 507 [8,1]
+ CRUSH rule 1 x 508 [1,2]
+ CRUSH rule 1 x 509 [8]
+ CRUSH rule 1 x 510 [8,2]
+ CRUSH rule 1 x 511 [4,8]
+ CRUSH rule 1 x 512 [8,2]
+ CRUSH rule 1 x 513 [8,2]
+ CRUSH rule 1 x 514 [1,8]
+ CRUSH rule 1 x 515 [8,4]
+ CRUSH rule 1 x 516 [4,1]
+ CRUSH rule 1 x 517 [8,2]
+ CRUSH rule 1 x 518 [4,8]
+ CRUSH rule 1 x 519 [8,4]
+ CRUSH rule 1 x 520 [2,8]
+ CRUSH rule 1 x 521 [8,2]
+ CRUSH rule 1 x 522 [8,4]
+ CRUSH rule 1 x 523 [4,2]
+ CRUSH rule 1 x 524 [2,1]
+ CRUSH rule 1 x 525 [2,8]
+ CRUSH rule 1 x 526 [1,2]
+ CRUSH rule 1 x 527 [1,2]
+ CRUSH rule 1 x 528 [8,2]
+ CRUSH rule 1 x 529 [4,8]
+ CRUSH rule 1 x 530 [8]
+ CRUSH rule 1 x 531 [8,1]
+ CRUSH rule 1 x 532 [6,4]
+ CRUSH rule 1 x 533 [4,8]
+ CRUSH rule 1 x 534 [8,2]
+ CRUSH rule 1 x 535 [8,6]
+ CRUSH rule 1 x 536 [8,2]
+ CRUSH rule 1 x 537 [4,8]
+ CRUSH rule 1 x 538 [8,4]
+ CRUSH rule 1 x 539 [8,2]
+ CRUSH rule 1 x 540 [1,8]
+ CRUSH rule 1 x 541 [2,4]
+ CRUSH rule 1 x 542 [2,1]
+ CRUSH rule 1 x 543 [8,2]
+ CRUSH rule 1 x 544 [4,8]
+ CRUSH rule 1 x 545 [1,8]
+ CRUSH rule 1 x 546 [8,1]
+ CRUSH rule 1 x 547 [8,2]
+ CRUSH rule 1 x 548 [4,2]
+ CRUSH rule 1 x 549 [1,8]
+ CRUSH rule 1 x 550 [2,4]
+ CRUSH rule 1 x 551 [8,2]
+ CRUSH rule 1 x 552 [4,1]
+ CRUSH rule 1 x 553 [8,2]
+ CRUSH rule 1 x 554 [1,8]
+ CRUSH rule 1 x 555 [4,1]
+ CRUSH rule 1 x 556 [2,8]
+ CRUSH rule 1 x 557 [8,1]
+ CRUSH rule 1 x 558 [4,1]
+ CRUSH rule 1 x 559 [1,2]
+ CRUSH rule 1 x 560 [8,1]
+ CRUSH rule 1 x 561 [8,4]
+ CRUSH rule 1 x 562 [4,8]
+ CRUSH rule 1 x 563 [2,8]
+ CRUSH rule 1 x 564 [8,1]
+ CRUSH rule 1 x 565 [4,8]
+ CRUSH rule 1 x 566 [4,8]
+ CRUSH rule 1 x 567 [4,8]
+ CRUSH rule 1 x 568 [8,2]
+ CRUSH rule 1 x 569 [4,2]
+ CRUSH rule 1 x 570 [1,8]
+ CRUSH rule 1 x 571 [6,8]
+ CRUSH rule 1 x 572 [4]
+ CRUSH rule 1 x 573 [1,2]
+ CRUSH rule 1 x 574 [2,1]
+ CRUSH rule 1 x 575 [8,2]
+ CRUSH rule 1 x 576 [4,8]
+ CRUSH rule 1 x 577 [8,2]
+ CRUSH rule 1 x 578 [8,1]
+ CRUSH rule 1 x 579 [4,1]
+ CRUSH rule 1 x 580 [8,2]
+ CRUSH rule 1 x 581 [8,2]
+ CRUSH rule 1 x 582 [2,8]
+ CRUSH rule 1 x 583 [8,1]
+ CRUSH rule 1 x 584 [8,1]
+ CRUSH rule 1 x 585 [8,1]
+ CRUSH rule 1 x 586 [1,2]
+ CRUSH rule 1 x 587 [2,4]
+ CRUSH rule 1 x 588 [4,1]
+ CRUSH rule 1 x 589 [8,1]
+ CRUSH rule 1 x 590 [8,2]
+ CRUSH rule 1 x 591 [4,2]
+ CRUSH rule 1 x 592 [2,1]
+ CRUSH rule 1 x 593 [1,8]
+ CRUSH rule 1 x 594 [2,8]
+ CRUSH rule 1 x 595 [8,1]
+ CRUSH rule 1 x 596 [2,8]
+ CRUSH rule 1 x 597 [1,2]
+ CRUSH rule 1 x 598 [8,2]
+ CRUSH rule 1 x 599 [4,2]
+ CRUSH rule 1 x 600 [8,1]
+ CRUSH rule 1 x 601 [1,8]
+ CRUSH rule 1 x 602 [2,8]
+ CRUSH rule 1 x 603 [1,2]
+ CRUSH rule 1 x 604 [8,2]
+ CRUSH rule 1 x 605 [8,2]
+ CRUSH rule 1 x 606 [2,1]
+ CRUSH rule 1 x 607 [2,4]
+ CRUSH rule 1 x 608 [4,1]
+ CRUSH rule 1 x 609 [4,2]
+ CRUSH rule 1 x 610 [1,8]
+ CRUSH rule 1 x 611 [1,2]
+ CRUSH rule 1 x 612 [2,1]
+ CRUSH rule 1 x 613 [8,2]
+ CRUSH rule 1 x 614 [8,4]
+ CRUSH rule 1 x 615 [8,1]
+ CRUSH rule 1 x 616 [1,8]
+ CRUSH rule 1 x 617 [8,1]
+ CRUSH rule 1 x 618 [8,1]
+ CRUSH rule 1 x 619 [4,1]
+ CRUSH rule 1 x 620 [8,1]
+ CRUSH rule 1 x 621 [2,8]
+ CRUSH rule 1 x 622 [2,4]
+ CRUSH rule 1 x 623 [2,8]
+ CRUSH rule 1 x 624 [4,8]
+ CRUSH rule 1 x 625 [2,1]
+ CRUSH rule 1 x 626 [8,1]
+ CRUSH rule 1 x 627 [2,8]
+ CRUSH rule 1 x 628 [8,2]
+ CRUSH rule 1 x 629 [2,8]
+ CRUSH rule 1 x 630 [2,8]
+ CRUSH rule 1 x 631 [1,8]
+ CRUSH rule 1 x 632 [8,2]
+ CRUSH rule 1 x 633 [8,2]
+ CRUSH rule 1 x 634 [1,8]
+ CRUSH rule 1 x 635 [4,8]
+ CRUSH rule 1 x 636 [1,4]
+ CRUSH rule 1 x 637 [1,2]
+ CRUSH rule 1 x 638 [8,1]
+ CRUSH rule 1 x 639 [2,1]
+ CRUSH rule 1 x 640 [1,2]
+ CRUSH rule 1 x 641 [8,2]
+ CRUSH rule 1 x 642 [2,1]
+ CRUSH rule 1 x 643 [8,2]
+ CRUSH rule 1 x 644 [8,1]
+ CRUSH rule 1 x 645 [2,1]
+ CRUSH rule 1 x 646 [8,1]
+ CRUSH rule 1 x 647 [8,1]
+ CRUSH rule 1 x 648 [1,8]
+ CRUSH rule 1 x 649 [4,8]
+ CRUSH rule 1 x 650 [8,4]
+ CRUSH rule 1 x 651 [4,6]
+ CRUSH rule 1 x 652 [4,8]
+ CRUSH rule 1 x 653 [8,1]
+ CRUSH rule 1 x 654 [6,1]
+ CRUSH rule 1 x 655 [1,4]
+ CRUSH rule 1 x 656 [8,2]
+ CRUSH rule 1 x 657 [6,1]
+ CRUSH rule 1 x 658 [8,1]
+ CRUSH rule 1 x 659 [4,8]
+ CRUSH rule 1 x 660 [8,1]
+ CRUSH rule 1 x 661 [1,8]
+ CRUSH rule 1 x 662 [8,2]
+ CRUSH rule 1 x 663 [1,4]
+ CRUSH rule 1 x 664 [1,4]
+ CRUSH rule 1 x 665 [4,6]
+ CRUSH rule 1 x 666 [2,8]
+ CRUSH rule 1 x 667 [1,4]
+ CRUSH rule 1 x 668 [4,8]
+ CRUSH rule 1 x 669 [6,4]
+ CRUSH rule 1 x 670 [4,2]
+ CRUSH rule 1 x 671 [2,1]
+ CRUSH rule 1 x 672 [4,8]
+ CRUSH rule 1 x 673 [4,2]
+ CRUSH rule 1 x 674 [8,1]
+ CRUSH rule 1 x 675 [1,8]
+ CRUSH rule 1 x 676 [2,1]
+ CRUSH rule 1 x 677 [4,1]
+ CRUSH rule 1 x 678 [2,4]
+ CRUSH rule 1 x 679 [8,2]
+ CRUSH rule 1 x 680 [2]
+ CRUSH rule 1 x 681 [2,8]
+ CRUSH rule 1 x 682 [1,4]
+ CRUSH rule 1 x 683 [1,2]
+ CRUSH rule 1 x 684 [8,1]
+ CRUSH rule 1 x 685 [8,1]
+ CRUSH rule 1 x 686 [1,4]
+ CRUSH rule 1 x 687 [1,6]
+ CRUSH rule 1 x 688 [4,8]
+ CRUSH rule 1 x 689 [8,4]
+ CRUSH rule 1 x 690 [8,1]
+ CRUSH rule 1 x 691 [8,1]
+ CRUSH rule 1 x 692 [8,2]
+ CRUSH rule 1 x 693 [8,4]
+ CRUSH rule 1 x 694 [8,4]
+ CRUSH rule 1 x 695 [2,8]
+ CRUSH rule 1 x 696 [1,2]
+ CRUSH rule 1 x 697 [8,1]
+ CRUSH rule 1 x 698 [8,2]
+ CRUSH rule 1 x 699 [1,8]
+ CRUSH rule 1 x 700 [1,2]
+ CRUSH rule 1 x 701 [2,1]
+ CRUSH rule 1 x 702 [8]
+ CRUSH rule 1 x 703 [8,1]
+ CRUSH rule 1 x 704 [1,4]
+ CRUSH rule 1 x 705 [8,4]
+ CRUSH rule 1 x 706 [1,2]
+ CRUSH rule 1 x 707 [8,2]
+ CRUSH rule 1 x 708 [4,8]
+ CRUSH rule 1 x 709 [8,2]
+ CRUSH rule 1 x 710 [8,1]
+ CRUSH rule 1 x 711 [2,4]
+ CRUSH rule 1 x 712 [2,8]
+ CRUSH rule 1 x 713 [8,2]
+ CRUSH rule 1 x 714 [1,2]
+ CRUSH rule 1 x 715 [1,2]
+ CRUSH rule 1 x 716 [4,8]
+ CRUSH rule 1 x 717 [8,4]
+ CRUSH rule 1 x 718 [2,8]
+ CRUSH rule 1 x 719 [2,6]
+ CRUSH rule 1 x 720 [8,1]
+ CRUSH rule 1 x 721 [4,6]
+ CRUSH rule 1 x 722 [8,1]
+ CRUSH rule 1 x 723 [4,1]
+ CRUSH rule 1 x 724 [2,6]
+ CRUSH rule 1 x 725 [1,2]
+ CRUSH rule 1 x 726 [4,8]
+ CRUSH rule 1 x 727 [4,8]
+ CRUSH rule 1 x 728 [2,1]
+ CRUSH rule 1 x 729 [2,8]
+ CRUSH rule 1 x 730 [4,8]
+ CRUSH rule 1 x 731 [4,1]
+ CRUSH rule 1 x 732 [1,2]
+ CRUSH rule 1 x 733 [4,1]
+ CRUSH rule 1 x 734 [8,4]
+ CRUSH rule 1 x 735 [4,8]
+ CRUSH rule 1 x 736 [4,8]
+ CRUSH rule 1 x 737 [1,2]
+ CRUSH rule 1 x 738 [4,2]
+ CRUSH rule 1 x 739 [2,1]
+ CRUSH rule 1 x 740 [1,2]
+ CRUSH rule 1 x 741 [8,2]
+ CRUSH rule 1 x 742 [8,2]
+ CRUSH rule 1 x 743 [8,1]
+ CRUSH rule 1 x 744 [4,8]
+ CRUSH rule 1 x 745 [8,2]
+ CRUSH rule 1 x 746 [8,1]
+ CRUSH rule 1 x 747 [8,1]
+ CRUSH rule 1 x 748 [2,8]
+ CRUSH rule 1 x 749 [4,8]
+ CRUSH rule 1 x 750 [1,8]
+ CRUSH rule 1 x 751 [2,1]
+ CRUSH rule 1 x 752 [8,1]
+ CRUSH rule 1 x 753 [8,1]
+ CRUSH rule 1 x 754 [8,4]
+ CRUSH rule 1 x 755 [1,2]
+ CRUSH rule 1 x 756 [8,2]
+ CRUSH rule 1 x 757 [8,4]
+ CRUSH rule 1 x 758 [8,2]
+ CRUSH rule 1 x 759 [8,4]
+ CRUSH rule 1 x 760 [1,4]
+ CRUSH rule 1 x 761 [1,2]
+ CRUSH rule 1 x 762 [2,8]
+ CRUSH rule 1 x 763 [8,2]
+ CRUSH rule 1 x 764 [1,8]
+ CRUSH rule 1 x 765 [8,2]
+ CRUSH rule 1 x 766 [8]
+ CRUSH rule 1 x 767 [1,2]
+ CRUSH rule 1 x 768 [8,4]
+ CRUSH rule 1 x 769 [8,2]
+ CRUSH rule 1 x 770 [8,2]
+ CRUSH rule 1 x 771 [8,1]
+ CRUSH rule 1 x 772 [8,4]
+ CRUSH rule 1 x 773 [4,1]
+ CRUSH rule 1 x 774 [8,1]
+ CRUSH rule 1 x 775 [8,2]
+ CRUSH rule 1 x 776 [6,2]
+ CRUSH rule 1 x 777 [4,1]
+ CRUSH rule 1 x 778 [1,8]
+ CRUSH rule 1 x 779 [2,8]
+ CRUSH rule 1 x 780 [2,1]
+ CRUSH rule 1 x 781 [8,2]
+ CRUSH rule 1 x 782 [4,1]
+ CRUSH rule 1 x 783 [8,1]
+ CRUSH rule 1 x 784 [1,2]
+ CRUSH rule 1 x 785 [8,1]
+ CRUSH rule 1 x 786 [8,2]
+ CRUSH rule 1 x 787 [1,2]
+ CRUSH rule 1 x 788 [8,2]
+ CRUSH rule 1 x 789 [1,6]
+ CRUSH rule 1 x 790 [8,2]
+ CRUSH rule 1 x 791 [4,8]
+ CRUSH rule 1 x 792 [4,8]
+ CRUSH rule 1 x 793 [8,1]
+ CRUSH rule 1 x 794 [2,8]
+ CRUSH rule 1 x 795 [1,8]
+ CRUSH rule 1 x 796 [2,8]
+ CRUSH rule 1 x 797 [2,4]
+ CRUSH rule 1 x 798 [6,8]
+ CRUSH rule 1 x 799 [4,1]
+ CRUSH rule 1 x 800 [8,2]
+ CRUSH rule 1 x 801 [4,8]
+ CRUSH rule 1 x 802 [1,8]
+ CRUSH rule 1 x 803 [2,8]
+ CRUSH rule 1 x 804 [8,2]
+ CRUSH rule 1 x 805 [2,8]
+ CRUSH rule 1 x 806 [1,4]
+ CRUSH rule 1 x 807 [4,8]
+ CRUSH rule 1 x 808 [2,8]
+ CRUSH rule 1 x 809 [1,8]
+ CRUSH rule 1 x 810 [2,8]
+ CRUSH rule 1 x 811 [8,2]
+ CRUSH rule 1 x 812 [8,4]
+ CRUSH rule 1 x 813 [8,4]
+ CRUSH rule 1 x 814 [8,1]
+ CRUSH rule 1 x 815 [4,1]
+ CRUSH rule 1 x 816 [2,1]
+ CRUSH rule 1 x 817 [8,1]
+ CRUSH rule 1 x 818 [8,2]
+ CRUSH rule 1 x 819 [8,1]
+ CRUSH rule 1 x 820 [4,1]
+ CRUSH rule 1 x 821 [4,1]
+ CRUSH rule 1 x 822 [2,1]
+ CRUSH rule 1 x 823 [4,8]
+ CRUSH rule 1 x 824 [8,2]
+ CRUSH rule 1 x 825 [2,8]
+ CRUSH rule 1 x 826 [8,2]
+ CRUSH rule 1 x 827 [2,8]
+ CRUSH rule 1 x 828 [2,1]
+ CRUSH rule 1 x 829 [8,2]
+ CRUSH rule 1 x 830 [2,4]
+ CRUSH rule 1 x 831 [1,8]
+ CRUSH rule 1 x 832 [4,8]
+ CRUSH rule 1 x 833 [2,1]
+ CRUSH rule 1 x 834 [2]
+ CRUSH rule 1 x 835 [8,4]
+ CRUSH rule 1 x 836 [4,1]
+ CRUSH rule 1 x 837 [8,4]
+ CRUSH rule 1 x 838 [6,8]
+ CRUSH rule 1 x 839 [8,2]
+ CRUSH rule 1 x 840 [8,2]
+ CRUSH rule 1 x 841 [4,8]
+ CRUSH rule 1 x 842 [2]
+ CRUSH rule 1 x 843 [8,4]
+ CRUSH rule 1 x 844 [1,8]
+ CRUSH rule 1 x 845 [4,8]
+ CRUSH rule 1 x 846 [4,2]
+ CRUSH rule 1 x 847 [2,1]
+ CRUSH rule 1 x 848 [2,8]
+ CRUSH rule 1 x 849 [4,8]
+ CRUSH rule 1 x 850 [1,2]
+ CRUSH rule 1 x 851 [6,8]
+ CRUSH rule 1 x 852 [8,4]
+ CRUSH rule 1 x 853 [6,8]
+ CRUSH rule 1 x 854 [8,1]
+ CRUSH rule 1 x 855 [8,1]
+ CRUSH rule 1 x 856 [8,4]
+ CRUSH rule 1 x 857 [8,1]
+ CRUSH rule 1 x 858 [6,2]
+ CRUSH rule 1 x 859 [8,2]
+ CRUSH rule 1 x 860 [8,1]
+ CRUSH rule 1 x 861 [8,1]
+ CRUSH rule 1 x 862 [8,1]
+ CRUSH rule 1 x 863 [8,1]
+ CRUSH rule 1 x 864 [8,2]
+ CRUSH rule 1 x 865 [8,1]
+ CRUSH rule 1 x 866 [2,8]
+ CRUSH rule 1 x 867 [8]
+ CRUSH rule 1 x 868 [8,2]
+ CRUSH rule 1 x 869 [8,6]
+ CRUSH rule 1 x 870 [2,1]
+ CRUSH rule 1 x 871 [2,8]
+ CRUSH rule 1 x 872 [8,1]
+ CRUSH rule 1 x 873 [4,8]
+ CRUSH rule 1 x 874 [2,6]
+ CRUSH rule 1 x 875 [2,8]
+ CRUSH rule 1 x 876 [4,8]
+ CRUSH rule 1 x 877 [8,4]
+ CRUSH rule 1 x 878 [8,1]
+ CRUSH rule 1 x 879 [8,2]
+ CRUSH rule 1 x 880 [6,1]
+ CRUSH rule 1 x 881 [4,8]
+ CRUSH rule 1 x 882 [8,2]
+ CRUSH rule 1 x 883 [2,1]
+ CRUSH rule 1 x 884 [8,2]
+ CRUSH rule 1 x 885 [4,1]
+ CRUSH rule 1 x 886 [2,8]
+ CRUSH rule 1 x 887 [8,4]
+ CRUSH rule 1 x 888 [8,2]
+ CRUSH rule 1 x 889 [2,1]
+ CRUSH rule 1 x 890 [8,2]
+ CRUSH rule 1 x 891 [1,8]
+ CRUSH rule 1 x 892 [8,2]
+ CRUSH rule 1 x 893 [2,8]
+ CRUSH rule 1 x 894 [8,4]
+ CRUSH rule 1 x 895 [4,8]
+ CRUSH rule 1 x 896 [1,8]
+ CRUSH rule 1 x 897 [8,2]
+ CRUSH rule 1 x 898 [1,4]
+ CRUSH rule 1 x 899 [1,8]
+ CRUSH rule 1 x 900 [4,1]
+ CRUSH rule 1 x 901 [8,1]
+ CRUSH rule 1 x 902 [8,4]
+ CRUSH rule 1 x 903 [1,8]
+ CRUSH rule 1 x 904 [1,8]
+ CRUSH rule 1 x 905 [8,2]
+ CRUSH rule 1 x 906 [1,2]
+ CRUSH rule 1 x 907 [8,1]
+ CRUSH rule 1 x 908 [8,1]
+ CRUSH rule 1 x 909 [2,1]
+ CRUSH rule 1 x 910 [8,2]
+ CRUSH rule 1 x 911 [8,1]
+ CRUSH rule 1 x 912 [1,2]
+ CRUSH rule 1 x 913 [8,4]
+ CRUSH rule 1 x 914 [6,4]
+ CRUSH rule 1 x 915 [8,2]
+ CRUSH rule 1 x 916 [4,1]
+ CRUSH rule 1 x 917 [1,4]
+ CRUSH rule 1 x 918 [8,2]
+ CRUSH rule 1 x 919 [8,2]
+ CRUSH rule 1 x 920 [8,1]
+ CRUSH rule 1 x 921 [1,2]
+ CRUSH rule 1 x 922 [8,4]
+ CRUSH rule 1 x 923 [4,8]
+ CRUSH rule 1 x 924 [1,8]
+ CRUSH rule 1 x 925 [4,8]
+ CRUSH rule 1 x 926 [1,8]
+ CRUSH rule 1 x 927 [1,8]
+ CRUSH rule 1 x 928 [8,1]
+ CRUSH rule 1 x 929 [4,1]
+ CRUSH rule 1 x 930 [2,1]
+ CRUSH rule 1 x 931 [8,1]
+ CRUSH rule 1 x 932 [4,8]
+ CRUSH rule 1 x 933 [8,4]
+ CRUSH rule 1 x 934 [8,1]
+ CRUSH rule 1 x 935 [8,1]
+ CRUSH rule 1 x 936 [1,8]
+ CRUSH rule 1 x 937 [4,8]
+ CRUSH rule 1 x 938 [8,4]
+ CRUSH rule 1 x 939 [2,8]
+ CRUSH rule 1 x 940 [8,2]
+ CRUSH rule 1 x 941 [8,2]
+ CRUSH rule 1 x 942 [1,2]
+ CRUSH rule 1 x 943 [8,2]
+ CRUSH rule 1 x 944 [2,1]
+ CRUSH rule 1 x 945 [8,2]
+ CRUSH rule 1 x 946 [2,1]
+ CRUSH rule 1 x 947 [8,1]
+ CRUSH rule 1 x 948 [8,1]
+ CRUSH rule 1 x 949 [6,1]
+ CRUSH rule 1 x 950 [8,1]
+ CRUSH rule 1 x 951 [2,8]
+ CRUSH rule 1 x 952 [2,1]
+ CRUSH rule 1 x 953 [1,4]
+ CRUSH rule 1 x 954 [8,2]
+ CRUSH rule 1 x 955 [8,1]
+ CRUSH rule 1 x 956 [1,2]
+ CRUSH rule 1 x 957 [8,1]
+ CRUSH rule 1 x 958 [8,2]
+ CRUSH rule 1 x 959 [4,2]
+ CRUSH rule 1 x 960 [2,6]
+ CRUSH rule 1 x 961 [1,2]
+ CRUSH rule 1 x 962 [8,4]
+ CRUSH rule 1 x 963 [2,4]
+ CRUSH rule 1 x 964 [8,1]
+ CRUSH rule 1 x 965 [8,2]
+ CRUSH rule 1 x 966 [4,8]
+ CRUSH rule 1 x 967 [8,1]
+ CRUSH rule 1 x 968 [8,2]
+ CRUSH rule 1 x 969 [8,2]
+ CRUSH rule 1 x 970 [2,8]
+ CRUSH rule 1 x 971 [1,8]
+ CRUSH rule 1 x 972 [1,8]
+ CRUSH rule 1 x 973 [1,2]
+ CRUSH rule 1 x 974 [4,1]
+ CRUSH rule 1 x 975 [4,8]
+ CRUSH rule 1 x 976 [4,8]
+ CRUSH rule 1 x 977 [8,4]
+ CRUSH rule 1 x 978 [8,2]
+ CRUSH rule 1 x 979 [8,1]
+ CRUSH rule 1 x 980 [8,2]
+ CRUSH rule 1 x 981 [8]
+ CRUSH rule 1 x 982 [1,2]
+ CRUSH rule 1 x 983 [4,8]
+ CRUSH rule 1 x 984 [2,1]
+ CRUSH rule 1 x 985 [2,4]
+ CRUSH rule 1 x 986 [8,4]
+ CRUSH rule 1 x 987 [2,1]
+ CRUSH rule 1 x 988 [1,4]
+ CRUSH rule 1 x 989 [1,8]
+ CRUSH rule 1 x 990 [1,2]
+ CRUSH rule 1 x 991 [1,4]
+ CRUSH rule 1 x 992 [8,1]
+ CRUSH rule 1 x 993 [2,8]
+ CRUSH rule 1 x 994 [4,2]
+ CRUSH rule 1 x 995 [8,1]
+ CRUSH rule 1 x 996 [8,2]
+ CRUSH rule 1 x 997 [8,4]
+ CRUSH rule 1 x 998 [8,1]
+ CRUSH rule 1 x 999 [1,8]
+ CRUSH rule 1 x 1000 [8,4]
+ CRUSH rule 1 x 1001 [2,1]
+ CRUSH rule 1 x 1002 [1,2]
+ CRUSH rule 1 x 1003 [2,8]
+ CRUSH rule 1 x 1004 [8,1]
+ CRUSH rule 1 x 1005 [8,1]
+ CRUSH rule 1 x 1006 [1,2]
+ CRUSH rule 1 x 1007 [1,2]
+ CRUSH rule 1 x 1008 [1,8]
+ CRUSH rule 1 x 1009 [6,8]
+ CRUSH rule 1 x 1010 [2,8]
+ CRUSH rule 1 x 1011 [4,2]
+ CRUSH rule 1 x 1012 [1,2]
+ CRUSH rule 1 x 1013 [1,2]
+ CRUSH rule 1 x 1014 [2,8]
+ CRUSH rule 1 x 1015 [8,1]
+ CRUSH rule 1 x 1016 [2,1]
+ CRUSH rule 1 x 1017 [6,2]
+ CRUSH rule 1 x 1018 [4,2]
+ CRUSH rule 1 x 1019 [4,8]
+ CRUSH rule 1 x 1020 [1,2]
+ CRUSH rule 1 x 1021 [8,2]
+ CRUSH rule 1 x 1022 [1,8]
+ CRUSH rule 1 x 1023 [4,2]
+ rule 1 (choose-two) num_rep 2 result size == 1:\t23/1024 (esc)
+ rule 1 (choose-two) num_rep 2 result size == 2:\t1001/1024 (esc)
+ CRUSH rule 1 x 0 [2,1,4]
+ CRUSH rule 1 x 1 [2,8,1]
+ CRUSH rule 1 x 2 [1,8,2]
+ CRUSH rule 1 x 3 [8,1]
+ CRUSH rule 1 x 4 [4,8,2]
+ CRUSH rule 1 x 5 [8,2,1]
+ CRUSH rule 1 x 6 [2,8,1]
+ CRUSH rule 1 x 7 [4,8,2]
+ CRUSH rule 1 x 8 [4,2,8]
+ CRUSH rule 1 x 9 [2,4,1]
+ CRUSH rule 1 x 10 [2,1,8]
+ CRUSH rule 1 x 11 [2,8,1]
+ CRUSH rule 1 x 12 [2,1,8]
+ CRUSH rule 1 x 13 [4,8,2]
+ CRUSH rule 1 x 14 [8,1,2]
+ CRUSH rule 1 x 15 [8,2,1]
+ CRUSH rule 1 x 16 [8,1,2]
+ CRUSH rule 1 x 17 [4,8,2]
+ CRUSH rule 1 x 18 [1,6,8]
+ CRUSH rule 1 x 19 [8,4]
+ CRUSH rule 1 x 20 [2,1,8]
+ CRUSH rule 1 x 21 [8,1,2]
+ CRUSH rule 1 x 22 [8,2]
+ CRUSH rule 1 x 23 [4,8,1]
+ CRUSH rule 1 x 24 [1,2,8]
+ CRUSH rule 1 x 25 [4,8,1]
+ CRUSH rule 1 x 26 [2,8,1]
+ CRUSH rule 1 x 27 [4,1]
+ CRUSH rule 1 x 28 [8,2,1]
+ CRUSH rule 1 x 29 [8,4,1]
+ CRUSH rule 1 x 30 [4,8,1]
+ CRUSH rule 1 x 31 [8,2,1]
+ CRUSH rule 1 x 32 [2,6,8]
+ CRUSH rule 1 x 33 [2,8,1]
+ CRUSH rule 1 x 34 [2,1,8]
+ CRUSH rule 1 x 35 [1,8,2]
+ CRUSH rule 1 x 36 [8,1,2]
+ CRUSH rule 1 x 37 [1,8,2]
+ CRUSH rule 1 x 38 [4,8,1]
+ CRUSH rule 1 x 39 [8,1,2]
+ CRUSH rule 1 x 40 [8,2,1]
+ CRUSH rule 1 x 41 [2,1,8]
+ CRUSH rule 1 x 42 [1,2,8]
+ CRUSH rule 1 x 43 [1,2,8]
+ CRUSH rule 1 x 44 [1,8,2]
+ CRUSH rule 1 x 45 [8,2,4]
+ CRUSH rule 1 x 46 [2,1,8]
+ CRUSH rule 1 x 47 [4,2,8]
+ CRUSH rule 1 x 48 [8,1]
+ CRUSH rule 1 x 49 [8,2,1]
+ CRUSH rule 1 x 50 [4,8,1]
+ CRUSH rule 1 x 51 [2,8,1]
+ CRUSH rule 1 x 52 [8,2,1]
+ CRUSH rule 1 x 53 [4,2,8]
+ CRUSH rule 1 x 54 [8,4]
+ CRUSH rule 1 x 55 [8,1,2]
+ CRUSH rule 1 x 56 [8,4,2]
+ CRUSH rule 1 x 57 [2,1,8]
+ CRUSH rule 1 x 58 [1,2,8]
+ CRUSH rule 1 x 59 [8,2,1]
+ CRUSH rule 1 x 60 [4,8,1]
+ CRUSH rule 1 x 61 [4,8,2]
+ CRUSH rule 1 x 62 [8,1,2]
+ CRUSH rule 1 x 63 [8,1,2]
+ CRUSH rule 1 x 64 [4,2,1]
+ CRUSH rule 1 x 65 [8,4]
+ CRUSH rule 1 x 66 [4,2,1]
+ CRUSH rule 1 x 67 [4,2,8]
+ CRUSH rule 1 x 68 [1,2,8]
+ CRUSH rule 1 x 69 [1,2,8]
+ CRUSH rule 1 x 70 [8,2,1]
+ CRUSH rule 1 x 71 [2,8,1]
+ CRUSH rule 1 x 72 [8,1,4]
+ CRUSH rule 1 x 73 [2,8,1]
+ CRUSH rule 1 x 74 [1,8,2]
+ CRUSH rule 1 x 75 [4,2,1]
+ CRUSH rule 1 x 76 [4,1,6]
+ CRUSH rule 1 x 77 [8,2,4]
+ CRUSH rule 1 x 78 [1,2]
+ CRUSH rule 1 x 79 [4,1,2]
+ CRUSH rule 1 x 80 [2,4,1]
+ CRUSH rule 1 x 81 [2,1]
+ CRUSH rule 1 x 82 [6,1,8]
+ CRUSH rule 1 x 83 [2,8]
+ CRUSH rule 1 x 84 [8,2,1]
+ CRUSH rule 1 x 85 [4,8,1]
+ CRUSH rule 1 x 86 [2,1,8]
+ CRUSH rule 1 x 87 [2,8,4]
+ CRUSH rule 1 x 88 [1,6,2]
+ CRUSH rule 1 x 89 [2,1,8]
+ CRUSH rule 1 x 90 [8,2,4]
+ CRUSH rule 1 x 91 [4,8,1]
+ CRUSH rule 1 x 92 [1,8,2]
+ CRUSH rule 1 x 93 [8,4,1]
+ CRUSH rule 1 x 94 [1,2,8]
+ CRUSH rule 1 x 95 [8,1,2]
+ CRUSH rule 1 x 96 [1,8,2]
+ CRUSH rule 1 x 97 [8,1,2]
+ CRUSH rule 1 x 98 [2,1,8]
+ CRUSH rule 1 x 99 [2,8,1]
+ CRUSH rule 1 x 100 [1,8,4]
+ CRUSH rule 1 x 101 [8,2,1]
+ CRUSH rule 1 x 102 [2,1,8]
+ CRUSH rule 1 x 103 [2,8,1]
+ CRUSH rule 1 x 104 [8,4,1]
+ CRUSH rule 1 x 105 [2,4,1]
+ CRUSH rule 1 x 106 [1,8,2]
+ CRUSH rule 1 x 107 [2,1,8]
+ CRUSH rule 1 x 108 [8,2]
+ CRUSH rule 1 x 109 [1,2,4]
+ CRUSH rule 1 x 110 [4,2,8]
+ CRUSH rule 1 x 111 [2,1,4]
+ CRUSH rule 1 x 112 [2,1,8]
+ CRUSH rule 1 x 113 [8,2,1]
+ CRUSH rule 1 x 114 [8,4,1]
+ CRUSH rule 1 x 115 [8,2,4]
+ CRUSH rule 1 x 116 [1,2,8]
+ CRUSH rule 1 x 117 [6,8,1]
+ CRUSH rule 1 x 118 [2,8,1]
+ CRUSH rule 1 x 119 [2,8,1]
+ CRUSH rule 1 x 120 [2,1,4]
+ CRUSH rule 1 x 121 [2,1,8]
+ CRUSH rule 1 x 122 [8,1,2]
+ CRUSH rule 1 x 123 [2,8,1]
+ CRUSH rule 1 x 124 [8,1]
+ CRUSH rule 1 x 125 [1,8,4]
+ CRUSH rule 1 x 126 [8,2,1]
+ CRUSH rule 1 x 127 [4,8,2]
+ CRUSH rule 1 x 128 [2,8,6]
+ CRUSH rule 1 x 129 [2,1,8]
+ CRUSH rule 1 x 130 [4,8,1]
+ CRUSH rule 1 x 131 [1,2,8]
+ CRUSH rule 1 x 132 [1,2,8]
+ CRUSH rule 1 x 133 [8,2,1]
+ CRUSH rule 1 x 134 [1,8,2]
+ CRUSH rule 1 x 135 [4,8,2]
+ CRUSH rule 1 x 136 [2,1,4]
+ CRUSH rule 1 x 137 [8,4]
+ CRUSH rule 1 x 138 [8,4,2]
+ CRUSH rule 1 x 139 [4,2,6]
+ CRUSH rule 1 x 140 [1,8,2]
+ CRUSH rule 1 x 141 [8,1,2]
+ CRUSH rule 1 x 142 [4,6,1]
+ CRUSH rule 1 x 143 [4,8,1]
+ CRUSH rule 1 x 144 [8,1,2]
+ CRUSH rule 1 x 145 [8,2,1]
+ CRUSH rule 1 x 146 [2,8,1]
+ CRUSH rule 1 x 147 [2,8,4]
+ CRUSH rule 1 x 148 [4,1,2]
+ CRUSH rule 1 x 149 [4,8,2]
+ CRUSH rule 1 x 150 [1,8,2]
+ CRUSH rule 1 x 151 [1,2]
+ CRUSH rule 1 x 152 [8,1,2]
+ CRUSH rule 1 x 153 [8,4,1]
+ CRUSH rule 1 x 154 [4,2,1]
+ CRUSH rule 1 x 155 [4,8,2]
+ CRUSH rule 1 x 156 [4,8,2]
+ CRUSH rule 1 x 157 [2,1,8]
+ CRUSH rule 1 x 158 [2,8,1]
+ CRUSH rule 1 x 159 [8,2,4]
+ CRUSH rule 1 x 160 [2,8,1]
+ CRUSH rule 1 x 161 [1,4,2]
+ CRUSH rule 1 x 162 [1,8,2]
+ CRUSH rule 1 x 163 [4,8,1]
+ CRUSH rule 1 x 164 [8,2,1]
+ CRUSH rule 1 x 165 [8,2,4]
+ CRUSH rule 1 x 166 [2,1,8]
+ CRUSH rule 1 x 167 [1,2,8]
+ CRUSH rule 1 x 168 [4,2,8]
+ CRUSH rule 1 x 169 [2,8,1]
+ CRUSH rule 1 x 170 [1,2,8]
+ CRUSH rule 1 x 171 [8,4,1]
+ CRUSH rule 1 x 172 [1,8]
+ CRUSH rule 1 x 173 [8,4]
+ CRUSH rule 1 x 174 [1,2,6]
+ CRUSH rule 1 x 175 [8,1,2]
+ CRUSH rule 1 x 176 [2,1,8]
+ CRUSH rule 1 x 177 [8,2,1]
+ CRUSH rule 1 x 178 [4,2,1]
+ CRUSH rule 1 x 179 [8,1,2]
+ CRUSH rule 1 x 180 [1,8,2]
+ CRUSH rule 1 x 181 [8,2,1]
+ CRUSH rule 1 x 182 [8,1,2]
+ CRUSH rule 1 x 183 [8,1,2]
+ CRUSH rule 1 x 184 [4,8]
+ CRUSH rule 1 x 185 [8,4,1]
+ CRUSH rule 1 x 186 [2,1,4]
+ CRUSH rule 1 x 187 [1,8,2]
+ CRUSH rule 1 x 188 [1,8,2]
+ CRUSH rule 1 x 189 [1,8,2]
+ CRUSH rule 1 x 190 [1,2,8]
+ CRUSH rule 1 x 191 [8,4,1]
+ CRUSH rule 1 x 192 [4,1,2]
+ CRUSH rule 1 x 193 [4,2,8]
+ CRUSH rule 1 x 194 [1,8,2]
+ CRUSH rule 1 x 195 [8,4,1]
+ CRUSH rule 1 x 196 [8,1,2]
+ CRUSH rule 1 x 197 [8,4,2]
+ CRUSH rule 1 x 198 [2,1,8]
+ CRUSH rule 1 x 199 [1,4,8]
+ CRUSH rule 1 x 200 [1,8,2]
+ CRUSH rule 1 x 201 [8,1,2]
+ CRUSH rule 1 x 202 [8,2,1]
+ CRUSH rule 1 x 203 [1,8]
+ CRUSH rule 1 x 204 [2,1,4]
+ CRUSH rule 1 x 205 [1,8,2]
+ CRUSH rule 1 x 206 [1,2,8]
+ CRUSH rule 1 x 207 [2,1,8]
+ CRUSH rule 1 x 208 [8,1,2]
+ CRUSH rule 1 x 209 [1,2,8]
+ CRUSH rule 1 x 210 [1,2,4]
+ CRUSH rule 1 x 211 [4,8,2]
+ CRUSH rule 1 x 212 [8,1,2]
+ CRUSH rule 1 x 213 [8,4,2]
+ CRUSH rule 1 x 214 [8,1]
+ CRUSH rule 1 x 215 [8,1,2]
+ CRUSH rule 1 x 216 [8,1,2]
+ CRUSH rule 1 x 217 [1,2,8]
+ CRUSH rule 1 x 218 [2,8,1]
+ CRUSH rule 1 x 219 [2,8,1]
+ CRUSH rule 1 x 220 [4,8,2]
+ CRUSH rule 1 x 221 [1,8,2]
+ CRUSH rule 1 x 222 [8,1,2]
+ CRUSH rule 1 x 223 [1,2,8]
+ CRUSH rule 1 x 224 [1,4,2]
+ CRUSH rule 1 x 225 [8,2,1]
+ CRUSH rule 1 x 226 [8,2,4]
+ CRUSH rule 1 x 227 [4,8,1]
+ CRUSH rule 1 x 228 [1,8,2]
+ CRUSH rule 1 x 229 [4,1,6]
+ CRUSH rule 1 x 230 [4,8,2]
+ CRUSH rule 1 x 231 [4,8,2]
+ CRUSH rule 1 x 232 [2,8,4]
+ CRUSH rule 1 x 233 [8,2,1]
+ CRUSH rule 1 x 234 [1,2,8]
+ CRUSH rule 1 x 235 [4,8,1]
+ CRUSH rule 1 x 236 [1,2,6]
+ CRUSH rule 1 x 237 [4,8,2]
+ CRUSH rule 1 x 238 [8,1]
+ CRUSH rule 1 x 239 [8,6,1]
+ CRUSH rule 1 x 240 [4,8,1]
+ CRUSH rule 1 x 241 [2,1,8]
+ CRUSH rule 1 x 242 [2,8,1]
+ CRUSH rule 1 x 243 [8,2,1]
+ CRUSH rule 1 x 244 [4,8,2]
+ CRUSH rule 1 x 245 [8,2,1]
+ CRUSH rule 1 x 246 [1,2,8]
+ CRUSH rule 1 x 247 [8,2,1]
+ CRUSH rule 1 x 248 [8,2,4]
+ CRUSH rule 1 x 249 [2,1,8]
+ CRUSH rule 1 x 250 [2,1,4]
+ CRUSH rule 1 x 251 [2,8,1]
+ CRUSH rule 1 x 252 [4,8,2]
+ CRUSH rule 1 x 253 [1,2,8]
+ CRUSH rule 1 x 254 [4,8,1]
+ CRUSH rule 1 x 255 [1,8,2]
+ CRUSH rule 1 x 256 [4,8,1]
+ CRUSH rule 1 x 257 [2,8,4]
+ CRUSH rule 1 x 258 [4,1]
+ CRUSH rule 1 x 259 [8,6,2]
+ CRUSH rule 1 x 260 [8,2,1]
+ CRUSH rule 1 x 261 [8,1,2]
+ CRUSH rule 1 x 262 [8,2,1]
+ CRUSH rule 1 x 263 [8,4,2]
+ CRUSH rule 1 x 264 [1,8,2]
+ CRUSH rule 1 x 265 [8,1]
+ CRUSH rule 1 x 266 [8,2,1]
+ CRUSH rule 1 x 267 [2,8,1]
+ CRUSH rule 1 x 268 [1,8,2]
+ CRUSH rule 1 x 269 [1,8,2]
+ CRUSH rule 1 x 270 [4,1,2]
+ CRUSH rule 1 x 271 [8,4,2]
+ CRUSH rule 1 x 272 [2,8,4]
+ CRUSH rule 1 x 273 [4,1,2]
+ CRUSH rule 1 x 274 [8,2,4]
+ CRUSH rule 1 x 275 [4,8,1]
+ CRUSH rule 1 x 276 [8,1,2]
+ CRUSH rule 1 x 277 [8,1,2]
+ CRUSH rule 1 x 278 [8,2,1]
+ CRUSH rule 1 x 279 [8,4,2]
+ CRUSH rule 1 x 280 [2,8,1]
+ CRUSH rule 1 x 281 [8,2,1]
+ CRUSH rule 1 x 282 [1,2,8]
+ CRUSH rule 1 x 283 [8,2,1]
+ CRUSH rule 1 x 284 [8,2]
+ CRUSH rule 1 x 285 [4,8,1]
+ CRUSH rule 1 x 286 [2,1,8]
+ CRUSH rule 1 x 287 [1,2,8]
+ CRUSH rule 1 x 288 [8,1,4]
+ CRUSH rule 1 x 289 [4,8,2]
+ CRUSH rule 1 x 290 [1,4,8]
+ CRUSH rule 1 x 291 [1,2,4]
+ CRUSH rule 1 x 292 [8,2,1]
+ CRUSH rule 1 x 293 [8,1,2]
+ CRUSH rule 1 x 294 [8,4,2]
+ CRUSH rule 1 x 295 [4,8,1]
+ CRUSH rule 1 x 296 [4,1,8]
+ CRUSH rule 1 x 297 [8,2,1]
+ CRUSH rule 1 x 298 [1,2,8]
+ CRUSH rule 1 x 299 [2,1,8]
+ CRUSH rule 1 x 300 [8,2]
+ CRUSH rule 1 x 301 [1,8,2]
+ CRUSH rule 1 x 302 [8,1,2]
+ CRUSH rule 1 x 303 [8,4,1]
+ CRUSH rule 1 x 304 [2,8,1]
+ CRUSH rule 1 x 305 [2,8,1]
+ CRUSH rule 1 x 306 [1,8,2]
+ CRUSH rule 1 x 307 [2,1,8]
+ CRUSH rule 1 x 308 [2,8,4]
+ CRUSH rule 1 x 309 [8,2]
+ CRUSH rule 1 x 310 [4,6,1]
+ CRUSH rule 1 x 311 [4,2,1]
+ CRUSH rule 1 x 312 [2,1,8]
+ CRUSH rule 1 x 313 [4,8]
+ CRUSH rule 1 x 314 [6,1,2]
+ CRUSH rule 1 x 315 [2,1,8]
+ CRUSH rule 1 x 316 [8,2,1]
+ CRUSH rule 1 x 317 [2,8,1]
+ CRUSH rule 1 x 318 [8,2,1]
+ CRUSH rule 1 x 319 [8,2,1]
+ CRUSH rule 1 x 320 [8,2,1]
+ CRUSH rule 1 x 321 [1,2]
+ CRUSH rule 1 x 322 [2,8,4]
+ CRUSH rule 1 x 323 [4,8,1]
+ CRUSH rule 1 x 324 [8,1,4]
+ CRUSH rule 1 x 325 [4,8,2]
+ CRUSH rule 1 x 326 [8,6,1]
+ CRUSH rule 1 x 327 [1,8,2]
+ CRUSH rule 1 x 328 [8,4,1]
+ CRUSH rule 1 x 329 [4,8,2]
+ CRUSH rule 1 x 330 [4,8,1]
+ CRUSH rule 1 x 331 [2,8,1]
+ CRUSH rule 1 x 332 [2,1,8]
+ CRUSH rule 1 x 333 [8,1,2]
+ CRUSH rule 1 x 334 [8,1]
+ CRUSH rule 1 x 335 [8,1,2]
+ CRUSH rule 1 x 336 [4,8,2]
+ CRUSH rule 1 x 337 [8,2,4]
+ CRUSH rule 1 x 338 [1,8]
+ CRUSH rule 1 x 339 [8,2]
+ CRUSH rule 1 x 340 [2,1,8]
+ CRUSH rule 1 x 341 [4,1,8]
+ CRUSH rule 1 x 342 [2,8,4]
+ CRUSH rule 1 x 343 [8,1]
+ CRUSH rule 1 x 344 [6,2,4]
+ CRUSH rule 1 x 345 [2,1,8]
+ CRUSH rule 1 x 346 [8,2,4]
+ CRUSH rule 1 x 347 [4,1,8]
+ CRUSH rule 1 x 348 [8,2,1]
+ CRUSH rule 1 x 349 [1,8,2]
+ CRUSH rule 1 x 350 [8,1,2]
+ CRUSH rule 1 x 351 [1,8,2]
+ CRUSH rule 1 x 352 [1,2,4]
+ CRUSH rule 1 x 353 [8,1,2]
+ CRUSH rule 1 x 354 [1,8,2]
+ CRUSH rule 1 x 355 [1,2,8]
+ CRUSH rule 1 x 356 [4,8,1]
+ CRUSH rule 1 x 357 [8,1,2]
+ CRUSH rule 1 x 358 [2,1,8]
+ CRUSH rule 1 x 359 [6,8,1]
+ CRUSH rule 1 x 360 [1,2,8]
+ CRUSH rule 1 x 361 [8,4,2]
+ CRUSH rule 1 x 362 [4,6,8]
+ CRUSH rule 1 x 363 [4,1,2]
+ CRUSH rule 1 x 364 [2,8,1]
+ CRUSH rule 1 x 365 [8,1,2]
+ CRUSH rule 1 x 366 [8,2,1]
+ CRUSH rule 1 x 367 [4,8,2]
+ CRUSH rule 1 x 368 [8,4,2]
+ CRUSH rule 1 x 369 [8,1]
+ CRUSH rule 1 x 370 [8,2]
+ CRUSH rule 1 x 371 [1,4,2]
+ CRUSH rule 1 x 372 [8,1,2]
+ CRUSH rule 1 x 373 [1,8]
+ CRUSH rule 1 x 374 [2,8]
+ CRUSH rule 1 x 375 [8,4]
+ CRUSH rule 1 x 376 [8,1,2]
+ CRUSH rule 1 x 377 [1,2,4]
+ CRUSH rule 1 x 378 [1,2,8]
+ CRUSH rule 1 x 379 [8,1,2]
+ CRUSH rule 1 x 380 [2,1]
+ CRUSH rule 1 x 381 [1,4,8]
+ CRUSH rule 1 x 382 [1,4,2]
+ CRUSH rule 1 x 383 [4,1,2]
+ CRUSH rule 1 x 384 [8,2,1]
+ CRUSH rule 1 x 385 [8,1,6]
+ CRUSH rule 1 x 386 [1,8,2]
+ CRUSH rule 1 x 387 [1,4,8]
+ CRUSH rule 1 x 388 [8,1,6]
+ CRUSH rule 1 x 389 [1,4,8]
+ CRUSH rule 1 x 390 [4,8,1]
+ CRUSH rule 1 x 391 [4,8,2]
+ CRUSH rule 1 x 392 [1,8,2]
+ CRUSH rule 1 x 393 [8,2]
+ CRUSH rule 1 x 394 [8,1,2]
+ CRUSH rule 1 x 395 [8,1,2]
+ CRUSH rule 1 x 396 [4,2,8]
+ CRUSH rule 1 x 397 [2,1,4]
+ CRUSH rule 1 x 398 [2,4,1]
+ CRUSH rule 1 x 399 [8,2,4]
+ CRUSH rule 1 x 400 [8,1,4]
+ CRUSH rule 1 x 401 [1,2,4]
+ CRUSH rule 1 x 402 [8,1,2]
+ CRUSH rule 1 x 403 [1,2,4]
+ CRUSH rule 1 x 404 [4,2,1]
+ CRUSH rule 1 x 405 [8,4,2]
+ CRUSH rule 1 x 406 [2,1]
+ CRUSH rule 1 x 407 [2,8,4]
+ CRUSH rule 1 x 408 [4,1,8]
+ CRUSH rule 1 x 409 [8,4,2]
+ CRUSH rule 1 x 410 [8,1,4]
+ CRUSH rule 1 x 411 [2,1,8]
+ CRUSH rule 1 x 412 [2,6,8]
+ CRUSH rule 1 x 413 [8,2]
+ CRUSH rule 1 x 414 [4,1,8]
+ CRUSH rule 1 x 415 [2,8,1]
+ CRUSH rule 1 x 416 [2,1,8]
+ CRUSH rule 1 x 417 [8,6,2]
+ CRUSH rule 1 x 418 [8,2,4]
+ CRUSH rule 1 x 419 [8,4,1]
+ CRUSH rule 1 x 420 [1,4,2]
+ CRUSH rule 1 x 421 [8,4]
+ CRUSH rule 1 x 422 [6,8,2]
+ CRUSH rule 1 x 423 [2,4,8]
+ CRUSH rule 1 x 424 [8,2,1]
+ CRUSH rule 1 x 425 [1,2,8]
+ CRUSH rule 1 x 426 [8,2]
+ CRUSH rule 1 x 427 [1,8,2]
+ CRUSH rule 1 x 428 [4,8]
+ CRUSH rule 1 x 429 [4,8,2]
+ CRUSH rule 1 x 430 [4,8,1]
+ CRUSH rule 1 x 431 [4,1,2]
+ CRUSH rule 1 x 432 [8,1,2]
+ CRUSH rule 1 x 433 [8,2,1]
+ CRUSH rule 1 x 434 [8,2,1]
+ CRUSH rule 1 x 435 [2,6,1]
+ CRUSH rule 1 x 436 [4,1,8]
+ CRUSH rule 1 x 437 [8,2,1]
+ CRUSH rule 1 x 438 [2,4,8]
+ CRUSH rule 1 x 439 [1,6,8]
+ CRUSH rule 1 x 440 [2,8,1]
+ CRUSH rule 1 x 441 [4,6,2]
+ CRUSH rule 1 x 442 [2,1,8]
+ CRUSH rule 1 x 443 [8,4,2]
+ CRUSH rule 1 x 444 [8,1,2]
+ CRUSH rule 1 x 445 [8,1,2]
+ CRUSH rule 1 x 446 [2,8,1]
+ CRUSH rule 1 x 447 [2,1,4]
+ CRUSH rule 1 x 448 [8,2,4]
+ CRUSH rule 1 x 449 [8,6,2]
+ CRUSH rule 1 x 450 [1,8,2]
+ CRUSH rule 1 x 451 [8,4]
+ CRUSH rule 1 x 452 [8]
+ CRUSH rule 1 x 453 [6,8,2]
+ CRUSH rule 1 x 454 [8,2,1]
+ CRUSH rule 1 x 455 [2,8,4]
+ CRUSH rule 1 x 456 [8,2]
+ CRUSH rule 1 x 457 [8,2,1]
+ CRUSH rule 1 x 458 [2,8,1]
+ CRUSH rule 1 x 459 [2,1,8]
+ CRUSH rule 1 x 460 [8,2,1]
+ CRUSH rule 1 x 461 [8,2,1]
+ CRUSH rule 1 x 462 [8,1,2]
+ CRUSH rule 1 x 463 [8,2,1]
+ CRUSH rule 1 x 464 [8,4,2]
+ CRUSH rule 1 x 465 [6,8,1]
+ CRUSH rule 1 x 466 [8,2,1]
+ CRUSH rule 1 x 467 [8,2,1]
+ CRUSH rule 1 x 468 [8,4,1]
+ CRUSH rule 1 x 469 [8,1,2]
+ CRUSH rule 1 x 470 [4,2,6]
+ CRUSH rule 1 x 471 [1,2,8]
+ CRUSH rule 1 x 472 [1,8,2]
+ CRUSH rule 1 x 473 [1,2,4]
+ CRUSH rule 1 x 474 [8,1]
+ CRUSH rule 1 x 475 [8,4,1]
+ CRUSH rule 1 x 476 [4,1,2]
+ CRUSH rule 1 x 477 [4,8,1]
+ CRUSH rule 1 x 478 [8,1,2]
+ CRUSH rule 1 x 479 [2,1,8]
+ CRUSH rule 1 x 480 [1,8,2]
+ CRUSH rule 1 x 481 [2,4,1]
+ CRUSH rule 1 x 482 [2,8,1]
+ CRUSH rule 1 x 483 [2,1,8]
+ CRUSH rule 1 x 484 [1,2,8]
+ CRUSH rule 1 x 485 [1,8,2]
+ CRUSH rule 1 x 486 [4,1,8]
+ CRUSH rule 1 x 487 [8,1,2]
+ CRUSH rule 1 x 488 [2,8,1]
+ CRUSH rule 1 x 489 [2,8,1]
+ CRUSH rule 1 x 490 [6,2,1]
+ CRUSH rule 1 x 491 [1,2,8]
+ CRUSH rule 1 x 492 [8,2,1]
+ CRUSH rule 1 x 493 [2,1,8]
+ CRUSH rule 1 x 494 [1,2,8]
+ CRUSH rule 1 x 495 [4,6,1]
+ CRUSH rule 1 x 496 [8,4]
+ CRUSH rule 1 x 497 [4,8,2]
+ CRUSH rule 1 x 498 [2,4,8]
+ CRUSH rule 1 x 499 [8,4,1]
+ CRUSH rule 1 x 500 [4,8,1]
+ CRUSH rule 1 x 501 [2,8,1]
+ CRUSH rule 1 x 502 [6,1,8]
+ CRUSH rule 1 x 503 [2,1,8]
+ CRUSH rule 1 x 504 [8,2,1]
+ CRUSH rule 1 x 505 [1,8,2]
+ CRUSH rule 1 x 506 [4,1,2]
+ CRUSH rule 1 x 507 [8,1,2]
+ CRUSH rule 1 x 508 [1,2,8]
+ CRUSH rule 1 x 509 [8,2]
+ CRUSH rule 1 x 510 [8,2,1]
+ CRUSH rule 1 x 511 [4,8]
+ CRUSH rule 1 x 512 [8,2,1]
+ CRUSH rule 1 x 513 [8,2,1]
+ CRUSH rule 1 x 514 [1,8,2]
+ CRUSH rule 1 x 515 [8,4,2]
+ CRUSH rule 1 x 516 [4,1,2]
+ CRUSH rule 1 x 517 [8,2,1]
+ CRUSH rule 1 x 518 [4,8,1]
+ CRUSH rule 1 x 519 [8,4,1]
+ CRUSH rule 1 x 520 [2,8,4]
+ CRUSH rule 1 x 521 [8,2,1]
+ CRUSH rule 1 x 522 [8,4,1]
+ CRUSH rule 1 x 523 [4,2,1]
+ CRUSH rule 1 x 524 [2,1,8]
+ CRUSH rule 1 x 525 [2,8,1]
+ CRUSH rule 1 x 526 [1,2,8]
+ CRUSH rule 1 x 527 [1,2,4]
+ CRUSH rule 1 x 528 [8,2,1]
+ CRUSH rule 1 x 529 [4,8,2]
+ CRUSH rule 1 x 530 [8,2]
+ CRUSH rule 1 x 531 [8,1,2]
+ CRUSH rule 1 x 532 [6,4,8]
+ CRUSH rule 1 x 533 [4,8,2]
+ CRUSH rule 1 x 534 [8,2,1]
+ CRUSH rule 1 x 535 [8,6,1]
+ CRUSH rule 1 x 536 [8,2,1]
+ CRUSH rule 1 x 537 [4,8]
+ CRUSH rule 1 x 538 [8,4,1]
+ CRUSH rule 1 x 539 [8,2,1]
+ CRUSH rule 1 x 540 [1,8,2]
+ CRUSH rule 1 x 541 [2,4,1]
+ CRUSH rule 1 x 542 [2,1,8]
+ CRUSH rule 1 x 543 [8,2,1]
+ CRUSH rule 1 x 544 [4,8,1]
+ CRUSH rule 1 x 545 [1,8,2]
+ CRUSH rule 1 x 546 [8,1,2]
+ CRUSH rule 1 x 547 [8,2,1]
+ CRUSH rule 1 x 548 [4,2,1]
+ CRUSH rule 1 x 549 [1,8,2]
+ CRUSH rule 1 x 550 [2,4,1]
+ CRUSH rule 1 x 551 [8,2]
+ CRUSH rule 1 x 552 [4,1,2]
+ CRUSH rule 1 x 553 [8,2]
+ CRUSH rule 1 x 554 [1,8,2]
+ CRUSH rule 1 x 555 [4,1,8]
+ CRUSH rule 1 x 556 [2,8,1]
+ CRUSH rule 1 x 557 [8,1]
+ CRUSH rule 1 x 558 [4,1,2]
+ CRUSH rule 1 x 559 [1,2,8]
+ CRUSH rule 1 x 560 [8,1]
+ CRUSH rule 1 x 561 [8,4,1]
+ CRUSH rule 1 x 562 [4,8,1]
+ CRUSH rule 1 x 563 [2,8,1]
+ CRUSH rule 1 x 564 [8,1,2]
+ CRUSH rule 1 x 565 [4,8,2]
+ CRUSH rule 1 x 566 [4,8,2]
+ CRUSH rule 1 x 567 [4,8,1]
+ CRUSH rule 1 x 568 [8,2,1]
+ CRUSH rule 1 x 569 [4,2,1]
+ CRUSH rule 1 x 570 [1,8,2]
+ CRUSH rule 1 x 571 [6,8,1]
+ CRUSH rule 1 x 572 [4,1]
+ CRUSH rule 1 x 573 [1,2,8]
+ CRUSH rule 1 x 574 [2,1,8]
+ CRUSH rule 1 x 575 [8,2,1]
+ CRUSH rule 1 x 576 [4,8,2]
+ CRUSH rule 1 x 577 [8,2,1]
+ CRUSH rule 1 x 578 [8,1,2]
+ CRUSH rule 1 x 579 [4,1,8]
+ CRUSH rule 1 x 580 [8,2,1]
+ CRUSH rule 1 x 581 [8,2,1]
+ CRUSH rule 1 x 582 [2,8,4]
+ CRUSH rule 1 x 583 [8,1,2]
+ CRUSH rule 1 x 584 [8,1,2]
+ CRUSH rule 1 x 585 [8,1,4]
+ CRUSH rule 1 x 586 [1,2,8]
+ CRUSH rule 1 x 587 [2,4,1]
+ CRUSH rule 1 x 588 [4,1,8]
+ CRUSH rule 1 x 589 [8,1]
+ CRUSH rule 1 x 590 [8,2,1]
+ CRUSH rule 1 x 591 [4,2,1]
+ CRUSH rule 1 x 592 [2,1,4]
+ CRUSH rule 1 x 593 [1,8,2]
+ CRUSH rule 1 x 594 [2,8,1]
+ CRUSH rule 1 x 595 [8,1,2]
+ CRUSH rule 1 x 596 [2,8,1]
+ CRUSH rule 1 x 597 [1,2,8]
+ CRUSH rule 1 x 598 [8,2,1]
+ CRUSH rule 1 x 599 [4,2,1]
+ CRUSH rule 1 x 600 [8,1,2]
+ CRUSH rule 1 x 601 [1,8,4]
+ CRUSH rule 1 x 602 [2,8,1]
+ CRUSH rule 1 x 603 [1,2,8]
+ CRUSH rule 1 x 604 [8,2,1]
+ CRUSH rule 1 x 605 [8,2,1]
+ CRUSH rule 1 x 606 [2,1,8]
+ CRUSH rule 1 x 607 [2,4,8]
+ CRUSH rule 1 x 608 [4,1,2]
+ CRUSH rule 1 x 609 [4,2,1]
+ CRUSH rule 1 x 610 [1,8,2]
+ CRUSH rule 1 x 611 [1,2,8]
+ CRUSH rule 1 x 612 [2,1,8]
+ CRUSH rule 1 x 613 [8,2,1]
+ CRUSH rule 1 x 614 [8,4,1]
+ CRUSH rule 1 x 615 [8,1,2]
+ CRUSH rule 1 x 616 [1,8,2]
+ CRUSH rule 1 x 617 [8,1,2]
+ CRUSH rule 1 x 618 [8,1,4]
+ CRUSH rule 1 x 619 [4,1,8]
+ CRUSH rule 1 x 620 [8,1,2]
+ CRUSH rule 1 x 621 [2,8,1]
+ CRUSH rule 1 x 622 [2,4,1]
+ CRUSH rule 1 x 623 [2,8,1]
+ CRUSH rule 1 x 624 [4,8,1]
+ CRUSH rule 1 x 625 [2,1,8]
+ CRUSH rule 1 x 626 [8,1,2]
+ CRUSH rule 1 x 627 [2,8,1]
+ CRUSH rule 1 x 628 [8,2,1]
+ CRUSH rule 1 x 629 [2,8,4]
+ CRUSH rule 1 x 630 [2,8,1]
+ CRUSH rule 1 x 631 [1,8,4]
+ CRUSH rule 1 x 632 [8,2,1]
+ CRUSH rule 1 x 633 [8,2,1]
+ CRUSH rule 1 x 634 [1,8,2]
+ CRUSH rule 1 x 635 [4,8,1]
+ CRUSH rule 1 x 636 [1,4,2]
+ CRUSH rule 1 x 637 [1,2,8]
+ CRUSH rule 1 x 638 [8,1,2]
+ CRUSH rule 1 x 639 [2,1,8]
+ CRUSH rule 1 x 640 [1,2,8]
+ CRUSH rule 1 x 641 [8,2,1]
+ CRUSH rule 1 x 642 [2,1,8]
+ CRUSH rule 1 x 643 [8,2,1]
+ CRUSH rule 1 x 644 [8,1,2]
+ CRUSH rule 1 x 645 [2,1,8]
+ CRUSH rule 1 x 646 [8,1,4]
+ CRUSH rule 1 x 647 [8,1,2]
+ CRUSH rule 1 x 648 [1,8,2]
+ CRUSH rule 1 x 649 [4,8,1]
+ CRUSH rule 1 x 650 [8,4,1]
+ CRUSH rule 1 x 651 [4,6,8]
+ CRUSH rule 1 x 652 [4,8,1]
+ CRUSH rule 1 x 653 [8,1,2]
+ CRUSH rule 1 x 654 [6,1,2]
+ CRUSH rule 1 x 655 [1,4,8]
+ CRUSH rule 1 x 656 [8,2,1]
+ CRUSH rule 1 x 657 [6,1,2]
+ CRUSH rule 1 x 658 [8,1,2]
+ CRUSH rule 1 x 659 [4,8,1]
+ CRUSH rule 1 x 660 [8,1,2]
+ CRUSH rule 1 x 661 [1,8,2]
+ CRUSH rule 1 x 662 [8,2]
+ CRUSH rule 1 x 663 [1,4,8]
+ CRUSH rule 1 x 664 [1,4,2]
+ CRUSH rule 1 x 665 [4,6,8]
+ CRUSH rule 1 x 666 [2,8,1]
+ CRUSH rule 1 x 667 [1,4,2]
+ CRUSH rule 1 x 668 [4,8,2]
+ CRUSH rule 1 x 669 [6,4,2]
+ CRUSH rule 1 x 670 [4,2,1]
+ CRUSH rule 1 x 671 [2,1,8]
+ CRUSH rule 1 x 672 [4,8,2]
+ CRUSH rule 1 x 673 [4,2,1]
+ CRUSH rule 1 x 674 [8,1,2]
+ CRUSH rule 1 x 675 [1,8,6]
+ CRUSH rule 1 x 676 [2,1,4]
+ CRUSH rule 1 x 677 [4,1,8]
+ CRUSH rule 1 x 678 [2,4,1]
+ CRUSH rule 1 x 679 [8,2,1]
+ CRUSH rule 1 x 680 [2,8]
+ CRUSH rule 1 x 681 [2,8,1]
+ CRUSH rule 1 x 682 [1,4,2]
+ CRUSH rule 1 x 683 [1,2,4]
+ CRUSH rule 1 x 684 [8,1,4]
+ CRUSH rule 1 x 685 [8,1,2]
+ CRUSH rule 1 x 686 [1,4,2]
+ CRUSH rule 1 x 687 [1,6,8]
+ CRUSH rule 1 x 688 [4,8,2]
+ CRUSH rule 1 x 689 [8,4,2]
+ CRUSH rule 1 x 690 [8,1,4]
+ CRUSH rule 1 x 691 [8,1,2]
+ CRUSH rule 1 x 692 [8,2,1]
+ CRUSH rule 1 x 693 [8,4,1]
+ CRUSH rule 1 x 694 [8,4,1]
+ CRUSH rule 1 x 695 [2,8,4]
+ CRUSH rule 1 x 696 [1,2,8]
+ CRUSH rule 1 x 697 [8,1,2]
+ CRUSH rule 1 x 698 [8,2,1]
+ CRUSH rule 1 x 699 [1,8,2]
+ CRUSH rule 1 x 700 [1,2,8]
+ CRUSH rule 1 x 701 [2,1,8]
+ CRUSH rule 1 x 702 [8,1]
+ CRUSH rule 1 x 703 [8,1,2]
+ CRUSH rule 1 x 704 [1,4,8]
+ CRUSH rule 1 x 705 [8,4,2]
+ CRUSH rule 1 x 706 [1,2,4]
+ CRUSH rule 1 x 707 [8,2,1]
+ CRUSH rule 1 x 708 [4,8,1]
+ CRUSH rule 1 x 709 [8,2,1]
+ CRUSH rule 1 x 710 [8,1,2]
+ CRUSH rule 1 x 711 [2,4,8]
+ CRUSH rule 1 x 712 [2,8,1]
+ CRUSH rule 1 x 713 [8,2,4]
+ CRUSH rule 1 x 714 [1,2,8]
+ CRUSH rule 1 x 715 [1,2,8]
+ CRUSH rule 1 x 716 [4,8,2]
+ CRUSH rule 1 x 717 [8,4,2]
+ CRUSH rule 1 x 718 [2,8,6]
+ CRUSH rule 1 x 719 [2,6,4]
+ CRUSH rule 1 x 720 [8,1,2]
+ CRUSH rule 1 x 721 [4,6,8]
+ CRUSH rule 1 x 722 [8,1,2]
+ CRUSH rule 1 x 723 [4,1,2]
+ CRUSH rule 1 x 724 [2,6,1]
+ CRUSH rule 1 x 725 [1,2,8]
+ CRUSH rule 1 x 726 [4,8,1]
+ CRUSH rule 1 x 727 [4,8,1]
+ CRUSH rule 1 x 728 [2,1,8]
+ CRUSH rule 1 x 729 [2,8]
+ CRUSH rule 1 x 730 [4,8,2]
+ CRUSH rule 1 x 731 [4,1,8]
+ CRUSH rule 1 x 732 [1,2,8]
+ CRUSH rule 1 x 733 [4,1,8]
+ CRUSH rule 1 x 734 [8,4,2]
+ CRUSH rule 1 x 735 [4,8,2]
+ CRUSH rule 1 x 736 [4,8]
+ CRUSH rule 1 x 737 [1,2,8]
+ CRUSH rule 1 x 738 [4,2,8]
+ CRUSH rule 1 x 739 [2,1,8]
+ CRUSH rule 1 x 740 [1,2,8]
+ CRUSH rule 1 x 741 [8,2,1]
+ CRUSH rule 1 x 742 [8,2,1]
+ CRUSH rule 1 x 743 [8,1,2]
+ CRUSH rule 1 x 744 [4,8,2]
+ CRUSH rule 1 x 745 [8,2,1]
+ CRUSH rule 1 x 746 [8,1,2]
+ CRUSH rule 1 x 747 [8,1,2]
+ CRUSH rule 1 x 748 [2,8,1]
+ CRUSH rule 1 x 749 [4,8,1]
+ CRUSH rule 1 x 750 [1,8,4]
+ CRUSH rule 1 x 751 [2,1,8]
+ CRUSH rule 1 x 752 [8,1]
+ CRUSH rule 1 x 753 [8,1,4]
+ CRUSH rule 1 x 754 [8,4,2]
+ CRUSH rule 1 x 755 [1,2,4]
+ CRUSH rule 1 x 756 [8,2,1]
+ CRUSH rule 1 x 757 [8,4,1]
+ CRUSH rule 1 x 758 [8,2]
+ CRUSH rule 1 x 759 [8,4,1]
+ CRUSH rule 1 x 760 [1,4,2]
+ CRUSH rule 1 x 761 [1,2,6]
+ CRUSH rule 1 x 762 [2,8,1]
+ CRUSH rule 1 x 763 [8,2,4]
+ CRUSH rule 1 x 764 [1,8,2]
+ CRUSH rule 1 x 765 [8,2,1]
+ CRUSH rule 1 x 766 [8]
+ CRUSH rule 1 x 767 [1,2,4]
+ CRUSH rule 1 x 768 [8,4,2]
+ CRUSH rule 1 x 769 [8,2,4]
+ CRUSH rule 1 x 770 [8,2,4]
+ CRUSH rule 1 x 771 [8,1,4]
+ CRUSH rule 1 x 772 [8,4,2]
+ CRUSH rule 1 x 773 [4,1,8]
+ CRUSH rule 1 x 774 [8,1,2]
+ CRUSH rule 1 x 775 [8,2,4]
+ CRUSH rule 1 x 776 [6,2,1]
+ CRUSH rule 1 x 777 [4,1,8]
+ CRUSH rule 1 x 778 [1,8,2]
+ CRUSH rule 1 x 779 [2,8,1]
+ CRUSH rule 1 x 780 [2,1,4]
+ CRUSH rule 1 x 781 [8,2,1]
+ CRUSH rule 1 x 782 [4,1,2]
+ CRUSH rule 1 x 783 [8,1,4]
+ CRUSH rule 1 x 784 [1,2,4]
+ CRUSH rule 1 x 785 [8,1,2]
+ CRUSH rule 1 x 786 [8,2,1]
+ CRUSH rule 1 x 787 [1,2,6]
+ CRUSH rule 1 x 788 [8,2,1]
+ CRUSH rule 1 x 789 [1,6,8]
+ CRUSH rule 1 x 790 [8,2,1]
+ CRUSH rule 1 x 791 [4,8,1]
+ CRUSH rule 1 x 792 [4,8]
+ CRUSH rule 1 x 793 [8,1,4]
+ CRUSH rule 1 x 794 [2,8,4]
+ CRUSH rule 1 x 795 [1,8,2]
+ CRUSH rule 1 x 796 [2,8,1]
+ CRUSH rule 1 x 797 [2,4,8]
+ CRUSH rule 1 x 798 [6,8,1]
+ CRUSH rule 1 x 799 [4,1,8]
+ CRUSH rule 1 x 800 [8,2,1]
+ CRUSH rule 1 x 801 [4,8,2]
+ CRUSH rule 1 x 802 [1,8,2]
+ CRUSH rule 1 x 803 [2,8,1]
+ CRUSH rule 1 x 804 [8,2,1]
+ CRUSH rule 1 x 805 [2,8,1]
+ CRUSH rule 1 x 806 [1,4,2]
+ CRUSH rule 1 x 807 [4,8]
+ CRUSH rule 1 x 808 [2,8,1]
+ CRUSH rule 1 x 809 [1,8,2]
+ CRUSH rule 1 x 810 [2,8,1]
+ CRUSH rule 1 x 811 [8,2,1]
+ CRUSH rule 1 x 812 [8,4,1]
+ CRUSH rule 1 x 813 [8,4,2]
+ CRUSH rule 1 x 814 [8,1]
+ CRUSH rule 1 x 815 [4,1,2]
+ CRUSH rule 1 x 816 [2,1,8]
+ CRUSH rule 1 x 817 [8,1,2]
+ CRUSH rule 1 x 818 [8,2,1]
+ CRUSH rule 1 x 819 [8,1,2]
+ CRUSH rule 1 x 820 [4,1,2]
+ CRUSH rule 1 x 821 [4,1,8]
+ CRUSH rule 1 x 822 [2,1,4]
+ CRUSH rule 1 x 823 [4,8,2]
+ CRUSH rule 1 x 824 [8,2,1]
+ CRUSH rule 1 x 825 [2,8,4]
+ CRUSH rule 1 x 826 [8,2,4]
+ CRUSH rule 1 x 827 [2,8,1]
+ CRUSH rule 1 x 828 [2,1,8]
+ CRUSH rule 1 x 829 [8,2]
+ CRUSH rule 1 x 830 [2,4,1]
+ CRUSH rule 1 x 831 [1,8,2]
+ CRUSH rule 1 x 832 [4,8,1]
+ CRUSH rule 1 x 833 [2,1,8]
+ CRUSH rule 1 x 834 [2,1]
+ CRUSH rule 1 x 835 [8,4,2]
+ CRUSH rule 1 x 836 [4,1,2]
+ CRUSH rule 1 x 837 [8,4,1]
+ CRUSH rule 1 x 838 [6,8,2]
+ CRUSH rule 1 x 839 [8,2,6]
+ CRUSH rule 1 x 840 [8,2,1]
+ CRUSH rule 1 x 841 [4,8,2]
+ CRUSH rule 1 x 842 [2,1]
+ CRUSH rule 1 x 843 [8,4,1]
+ CRUSH rule 1 x 844 [1,8,2]
+ CRUSH rule 1 x 845 [4,8,2]
+ CRUSH rule 1 x 846 [4,2,1]
+ CRUSH rule 1 x 847 [2,1,8]
+ CRUSH rule 1 x 848 [2,8,1]
+ CRUSH rule 1 x 849 [4,8,1]
+ CRUSH rule 1 x 850 [1,2,6]
+ CRUSH rule 1 x 851 [6,8]
+ CRUSH rule 1 x 852 [8,4,2]
+ CRUSH rule 1 x 853 [6,8,1]
+ CRUSH rule 1 x 854 [8,1,2]
+ CRUSH rule 1 x 855 [8,1,2]
+ CRUSH rule 1 x 856 [8,4,1]
+ CRUSH rule 1 x 857 [8,1,2]
+ CRUSH rule 1 x 858 [6,2,1]
+ CRUSH rule 1 x 859 [8,2,1]
+ CRUSH rule 1 x 860 [8,1,2]
+ CRUSH rule 1 x 861 [8,1]
+ CRUSH rule 1 x 862 [8,1]
+ CRUSH rule 1 x 863 [8,1,2]
+ CRUSH rule 1 x 864 [8,2,1]
+ CRUSH rule 1 x 865 [8,1,2]
+ CRUSH rule 1 x 866 [2,8]
+ CRUSH rule 1 x 867 [8,1]
+ CRUSH rule 1 x 868 [8,2,1]
+ CRUSH rule 1 x 869 [8,6,4]
+ CRUSH rule 1 x 870 [2,1,8]
+ CRUSH rule 1 x 871 [2,8,1]
+ CRUSH rule 1 x 872 [8,1,2]
+ CRUSH rule 1 x 873 [4,8,2]
+ CRUSH rule 1 x 874 [2,6,1]
+ CRUSH rule 1 x 875 [2,8,4]
+ CRUSH rule 1 x 876 [4,8,1]
+ CRUSH rule 1 x 877 [8,4,2]
+ CRUSH rule 1 x 878 [8,1,2]
+ CRUSH rule 1 x 879 [8,2,1]
+ CRUSH rule 1 x 880 [6,1,2]
+ CRUSH rule 1 x 881 [4,8,1]
+ CRUSH rule 1 x 882 [8,2,1]
+ CRUSH rule 1 x 883 [2,1,4]
+ CRUSH rule 1 x 884 [8,2,4]
+ CRUSH rule 1 x 885 [4,1,8]
+ CRUSH rule 1 x 886 [2,8,1]
+ CRUSH rule 1 x 887 [8,4,1]
+ CRUSH rule 1 x 888 [8,2,1]
+ CRUSH rule 1 x 889 [2,1,8]
+ CRUSH rule 1 x 890 [8,2,1]
+ CRUSH rule 1 x 891 [1,8,2]
+ CRUSH rule 1 x 892 [8,2,4]
+ CRUSH rule 1 x 893 [2,8,6]
+ CRUSH rule 1 x 894 [8,4,2]
+ CRUSH rule 1 x 895 [4,8,2]
+ CRUSH rule 1 x 896 [1,8,2]
+ CRUSH rule 1 x 897 [8,2,1]
+ CRUSH rule 1 x 898 [1,4,8]
+ CRUSH rule 1 x 899 [1,8,2]
+ CRUSH rule 1 x 900 [4,1,2]
+ CRUSH rule 1 x 901 [8,1,2]
+ CRUSH rule 1 x 902 [8,4,2]
+ CRUSH rule 1 x 903 [1,8,2]
+ CRUSH rule 1 x 904 [1,8,2]
+ CRUSH rule 1 x 905 [8,2,1]
+ CRUSH rule 1 x 906 [1,2,8]
+ CRUSH rule 1 x 907 [8,1,2]
+ CRUSH rule 1 x 908 [8,1,2]
+ CRUSH rule 1 x 909 [2,1,8]
+ CRUSH rule 1 x 910 [8,2,1]
+ CRUSH rule 1 x 911 [8,1,2]
+ CRUSH rule 1 x 912 [1,2,8]
+ CRUSH rule 1 x 913 [8,4,1]
+ CRUSH rule 1 x 914 [6,4,8]
+ CRUSH rule 1 x 915 [8,2]
+ CRUSH rule 1 x 916 [4,1,2]
+ CRUSH rule 1 x 917 [1,4,8]
+ CRUSH rule 1 x 918 [8,2,1]
+ CRUSH rule 1 x 919 [8,2,1]
+ CRUSH rule 1 x 920 [8,1,2]
+ CRUSH rule 1 x 921 [1,2,8]
+ CRUSH rule 1 x 922 [8,4,2]
+ CRUSH rule 1 x 923 [4,8,1]
+ CRUSH rule 1 x 924 [1,8,2]
+ CRUSH rule 1 x 925 [4,8,2]
+ CRUSH rule 1 x 926 [1,8,2]
+ CRUSH rule 1 x 927 [1,8,4]
+ CRUSH rule 1 x 928 [8,1,2]
+ CRUSH rule 1 x 929 [4,1,2]
+ CRUSH rule 1 x 930 [2,1,8]
+ CRUSH rule 1 x 931 [8,1,2]
+ CRUSH rule 1 x 932 [4,8,1]
+ CRUSH rule 1 x 933 [8,4,2]
+ CRUSH rule 1 x 934 [8,1,2]
+ CRUSH rule 1 x 935 [8,1,2]
+ CRUSH rule 1 x 936 [1,8,2]
+ CRUSH rule 1 x 937 [4,8]
+ CRUSH rule 1 x 938 [8,4,2]
+ CRUSH rule 1 x 939 [2,8,1]
+ CRUSH rule 1 x 940 [8,2,1]
+ CRUSH rule 1 x 941 [8,2,1]
+ CRUSH rule 1 x 942 [1,2,8]
+ CRUSH rule 1 x 943 [8,2,1]
+ CRUSH rule 1 x 944 [2,1,8]
+ CRUSH rule 1 x 945 [8,2,4]
+ CRUSH rule 1 x 946 [2,1,8]
+ CRUSH rule 1 x 947 [8,1,2]
+ CRUSH rule 1 x 948 [8,1,2]
+ CRUSH rule 1 x 949 [6,1,8]
+ CRUSH rule 1 x 950 [8,1,2]
+ CRUSH rule 1 x 951 [2,8,1]
+ CRUSH rule 1 x 952 [2,1,8]
+ CRUSH rule 1 x 953 [1,4,2]
+ CRUSH rule 1 x 954 [8,2,1]
+ CRUSH rule 1 x 955 [8,1,2]
+ CRUSH rule 1 x 956 [1,2,8]
+ CRUSH rule 1 x 957 [8,1,2]
+ CRUSH rule 1 x 958 [8,2,4]
+ CRUSH rule 1 x 959 [4,2,8]
+ CRUSH rule 1 x 960 [2,6,8]
+ CRUSH rule 1 x 961 [1,2,8]
+ CRUSH rule 1 x 962 [8,4,1]
+ CRUSH rule 1 x 963 [2,4,1]
+ CRUSH rule 1 x 964 [8,1,2]
+ CRUSH rule 1 x 965 [8,2,1]
+ CRUSH rule 1 x 966 [4,8,1]
+ CRUSH rule 1 x 967 [8,1,4]
+ CRUSH rule 1 x 968 [8,2,1]
+ CRUSH rule 1 x 969 [8,2,4]
+ CRUSH rule 1 x 970 [2,8,4]
+ CRUSH rule 1 x 971 [1,8,2]
+ CRUSH rule 1 x 972 [1,8,2]
+ CRUSH rule 1 x 973 [1,2]
+ CRUSH rule 1 x 974 [4,1,2]
+ CRUSH rule 1 x 975 [4,8]
+ CRUSH rule 1 x 976 [4,8,2]
+ CRUSH rule 1 x 977 [8,4,2]
+ CRUSH rule 1 x 978 [8,2,1]
+ CRUSH rule 1 x 979 [8,1,2]
+ CRUSH rule 1 x 980 [8,2,1]
+ CRUSH rule 1 x 981 [8,2]
+ CRUSH rule 1 x 982 [1,2,8]
+ CRUSH rule 1 x 983 [4,8,1]
+ CRUSH rule 1 x 984 [2,1,8]
+ CRUSH rule 1 x 985 [2,4,8]
+ CRUSH rule 1 x 986 [8,4,1]
+ CRUSH rule 1 x 987 [2,1,8]
+ CRUSH rule 1 x 988 [1,4,6]
+ CRUSH rule 1 x 989 [1,8,2]
+ CRUSH rule 1 x 990 [1,2,8]
+ CRUSH rule 1 x 991 [1,4,2]
+ CRUSH rule 1 x 992 [8,1,4]
+ CRUSH rule 1 x 993 [2,8,1]
+ CRUSH rule 1 x 994 [4,2,1]
+ CRUSH rule 1 x 995 [8,1,2]
+ CRUSH rule 1 x 996 [8,2,4]
+ CRUSH rule 1 x 997 [8,4,1]
+ CRUSH rule 1 x 998 [8,1,2]
+ CRUSH rule 1 x 999 [1,8,4]
+ CRUSH rule 1 x 1000 [8,4,2]
+ CRUSH rule 1 x 1001 [2,1]
+ CRUSH rule 1 x 1002 [1,2,8]
+ CRUSH rule 1 x 1003 [2,8]
+ CRUSH rule 1 x 1004 [8,1,2]
+ CRUSH rule 1 x 1005 [8,1,2]
+ CRUSH rule 1 x 1006 [1,2,4]
+ CRUSH rule 1 x 1007 [1,2,4]
+ CRUSH rule 1 x 1008 [1,8,2]
+ CRUSH rule 1 x 1009 [6,8,4]
+ CRUSH rule 1 x 1010 [2,8,1]
+ CRUSH rule 1 x 1011 [4,2,8]
+ CRUSH rule 1 x 1012 [1,2,8]
+ CRUSH rule 1 x 1013 [1,2,8]
+ CRUSH rule 1 x 1014 [2,8,4]
+ CRUSH rule 1 x 1015 [8,1,2]
+ CRUSH rule 1 x 1016 [2,1,4]
+ CRUSH rule 1 x 1017 [6,2,1]
+ CRUSH rule 1 x 1018 [4,2,1]
+ CRUSH rule 1 x 1019 [4,8,2]
+ CRUSH rule 1 x 1020 [1,2,8]
+ CRUSH rule 1 x 1021 [8,2,1]
+ CRUSH rule 1 x 1022 [1,8,4]
+ CRUSH rule 1 x 1023 [4,2,1]
+ rule 1 (choose-two) num_rep 3 result size == 1:\t2/1024 (esc)
+ rule 1 (choose-two) num_rep 3 result size == 2:\t82/1024 (esc)
+ rule 1 (choose-two) num_rep 3 result size == 3:\t940/1024 (esc)
+ rule 2 (chooseleaf), x = 0..1023, numrep = 2..3
+ CRUSH rule 2 x 0 [2,4]
+ CRUSH rule 2 x 1 [2,8]
+ CRUSH rule 2 x 2 [1,8]
+ CRUSH rule 2 x 3 [8,1]
+ CRUSH rule 2 x 4 [4,2]
+ CRUSH rule 2 x 5 [8,2]
+ CRUSH rule 2 x 6 [2,8]
+ CRUSH rule 2 x 7 [4,8]
+ CRUSH rule 2 x 8 [4,8]
+ CRUSH rule 2 x 9 [2,4]
+ CRUSH rule 2 x 10 [2,8]
+ CRUSH rule 2 x 11 [2,8]
+ CRUSH rule 2 x 12 [2,8]
+ CRUSH rule 2 x 13 [4,8]
+ CRUSH rule 2 x 14 [8,2]
+ CRUSH rule 2 x 15 [8,2]
+ CRUSH rule 2 x 16 [8,2]
+ CRUSH rule 2 x 17 [4,1]
+ CRUSH rule 2 x 18 [1,8]
+ CRUSH rule 2 x 19 [8,4]
+ CRUSH rule 2 x 20 [2,8]
+ CRUSH rule 2 x 21 [8,2]
+ CRUSH rule 2 x 22 [8,1]
+ CRUSH rule 2 x 23 [4,8]
+ CRUSH rule 2 x 24 [1,8]
+ CRUSH rule 2 x 25 [4,8]
+ CRUSH rule 2 x 26 [2,8]
+ CRUSH rule 2 x 27 [4,1]
+ CRUSH rule 2 x 28 [8,2]
+ CRUSH rule 2 x 29 [8,4]
+ CRUSH rule 2 x 30 [4,8]
+ CRUSH rule 2 x 31 [8,1]
+ CRUSH rule 2 x 32 [6,1]
+ CRUSH rule 2 x 33 [2,8]
+ CRUSH rule 2 x 34 [2,8]
+ CRUSH rule 2 x 35 [1,8]
+ CRUSH rule 2 x 36 [8,2]
+ CRUSH rule 2 x 37 [1,8]
+ CRUSH rule 2 x 38 [4,8]
+ CRUSH rule 2 x 39 [8,2]
+ CRUSH rule 2 x 40 [8,2]
+ CRUSH rule 2 x 41 [2,8]
+ CRUSH rule 2 x 42 [8,2]
+ CRUSH rule 2 x 43 [1,8]
+ CRUSH rule 2 x 44 [1,8]
+ CRUSH rule 2 x 45 [8,2]
+ CRUSH rule 2 x 46 [2,8]
+ CRUSH rule 2 x 47 [4,2]
+ CRUSH rule 2 x 48 [8,1]
+ CRUSH rule 2 x 49 [8,2]
+ CRUSH rule 2 x 50 [4,1]
+ CRUSH rule 2 x 51 [8,2]
+ CRUSH rule 2 x 52 [8,1]
+ CRUSH rule 2 x 53 [4,8]
+ CRUSH rule 2 x 54 [8,4]
+ CRUSH rule 2 x 55 [8,2]
+ CRUSH rule 2 x 56 [8,4]
+ CRUSH rule 2 x 57 [8,1]
+ CRUSH rule 2 x 58 [1,8]
+ CRUSH rule 2 x 59 [2,8]
+ CRUSH rule 2 x 60 [4,2]
+ CRUSH rule 2 x 61 [4,8]
+ CRUSH rule 2 x 62 [8,1]
+ CRUSH rule 2 x 63 [8,2]
+ CRUSH rule 2 x 64 [4,2]
+ CRUSH rule 2 x 65 [8,4]
+ CRUSH rule 2 x 66 [4,8]
+ CRUSH rule 2 x 67 [4,2]
+ CRUSH rule 2 x 68 [1,8]
+ CRUSH rule 2 x 69 [2,8]
+ CRUSH rule 2 x 70 [8,2]
+ CRUSH rule 2 x 71 [2,8]
+ CRUSH rule 2 x 72 [8,1]
+ CRUSH rule 2 x 73 [2,8]
+ CRUSH rule 2 x 74 [1,8]
+ CRUSH rule 2 x 75 [4,2]
+ CRUSH rule 2 x 76 [4,1]
+ CRUSH rule 2 x 77 [8,2]
+ CRUSH rule 2 x 78 [1,6]
+ CRUSH rule 2 x 79 [4,1]
+ CRUSH rule 2 x 80 [2,4]
+ CRUSH rule 2 x 81 [2,8]
+ CRUSH rule 2 x 82 [6,1]
+ CRUSH rule 2 x 83 [2,8]
+ CRUSH rule 2 x 84 [8,2]
+ CRUSH rule 2 x 85 [4,8]
+ CRUSH rule 2 x 86 [2,8]
+ CRUSH rule 2 x 87 [2,8]
+ CRUSH rule 2 x 88 [1,6]
+ CRUSH rule 2 x 89 [1,8]
+ CRUSH rule 2 x 90 [8,4]
+ CRUSH rule 2 x 91 [4,8]
+ CRUSH rule 2 x 92 [1,8]
+ CRUSH rule 2 x 93 [8,4]
+ CRUSH rule 2 x 94 [1,8]
+ CRUSH rule 2 x 95 [8,1]
+ CRUSH rule 2 x 96 [8,2]
+ CRUSH rule 2 x 97 [8,1]
+ CRUSH rule 2 x 98 [2,8]
+ CRUSH rule 2 x 99 [2,8]
+ CRUSH rule 2 x 100 [1,8]
+ CRUSH rule 2 x 101 [8,1]
+ CRUSH rule 2 x 102 [2,8]
+ CRUSH rule 2 x 103 [8,2]
+ CRUSH rule 2 x 104 [8,4]
+ CRUSH rule 2 x 105 [2,4]
+ CRUSH rule 2 x 106 [1,8]
+ CRUSH rule 2 x 107 [1,8]
+ CRUSH rule 2 x 108 [8,2]
+ CRUSH rule 2 x 109 [1,4]
+ CRUSH rule 2 x 110 [4,2]
+ CRUSH rule 2 x 111 [2,4]
+ CRUSH rule 2 x 112 [2,8]
+ CRUSH rule 2 x 113 [8,2]
+ CRUSH rule 2 x 114 [8,4]
+ CRUSH rule 2 x 115 [8,2]
+ CRUSH rule 2 x 116 [1,8]
+ CRUSH rule 2 x 117 [6,1]
+ CRUSH rule 2 x 118 [2,8]
+ CRUSH rule 2 x 119 [8,1]
+ CRUSH rule 2 x 120 [2,4]
+ CRUSH rule 2 x 121 [2,8]
+ CRUSH rule 2 x 122 [8,1]
+ CRUSH rule 2 x 123 [2,8]
+ CRUSH rule 2 x 124 [2,8]
+ CRUSH rule 2 x 125 [1,8]
+ CRUSH rule 2 x 126 [1,8]
+ CRUSH rule 2 x 127 [4,8]
+ CRUSH rule 2 x 128 [8,2]
+ CRUSH rule 2 x 129 [2,4]
+ CRUSH rule 2 x 130 [4,8]
+ CRUSH rule 2 x 131 [1,4]
+ CRUSH rule 2 x 132 [1,8]
+ CRUSH rule 2 x 133 [8,1]
+ CRUSH rule 2 x 134 [1,8]
+ CRUSH rule 2 x 135 [4,8]
+ CRUSH rule 2 x 136 [2,4]
+ CRUSH rule 2 x 137 [8,4]
+ CRUSH rule 2 x 138 [8,4]
+ CRUSH rule 2 x 139 [4,2]
+ CRUSH rule 2 x 140 [1,8]
+ CRUSH rule 2 x 141 [8,2]
+ CRUSH rule 2 x 142 [4,1]
+ CRUSH rule 2 x 143 [4,8]
+ CRUSH rule 2 x 144 [8,1]
+ CRUSH rule 2 x 145 [8,1]
+ CRUSH rule 2 x 146 [2,8]
+ CRUSH rule 2 x 147 [2,8]
+ CRUSH rule 2 x 148 [4,1]
+ CRUSH rule 2 x 149 [4,8]
+ CRUSH rule 2 x 150 [1,8]
+ CRUSH rule 2 x 151 [1,8]
+ CRUSH rule 2 x 152 [8,2]
+ CRUSH rule 2 x 153 [8,4]
+ CRUSH rule 2 x 154 [4,2]
+ CRUSH rule 2 x 155 [4,8]
+ CRUSH rule 2 x 156 [4,2]
+ CRUSH rule 2 x 157 [1,8]
+ CRUSH rule 2 x 158 [2,8]
+ CRUSH rule 2 x 159 [8,2]
+ CRUSH rule 2 x 160 [2,8]
+ CRUSH rule 2 x 161 [1,4]
+ CRUSH rule 2 x 162 [1,8]
+ CRUSH rule 2 x 163 [4,8]
+ CRUSH rule 2 x 164 [8,1]
+ CRUSH rule 2 x 165 [8,2]
+ CRUSH rule 2 x 166 [2,8]
+ CRUSH rule 2 x 167 [1,8]
+ CRUSH rule 2 x 168 [4,2]
+ CRUSH rule 2 x 169 [2,8]
+ CRUSH rule 2 x 170 [1,8]
+ CRUSH rule 2 x 171 [8,4]
+ CRUSH rule 2 x 172 [1,8]
+ CRUSH rule 2 x 173 [8,4]
+ CRUSH rule 2 x 174 [1,6]
+ CRUSH rule 2 x 175 [8,1]
+ CRUSH rule 2 x 176 [2,8]
+ CRUSH rule 2 x 177 [1,8]
+ CRUSH rule 2 x 178 [4,2]
+ CRUSH rule 2 x 179 [1,8]
+ CRUSH rule 2 x 180 [8,1]
+ CRUSH rule 2 x 181 [8,2]
+ CRUSH rule 2 x 182 [8,1]
+ CRUSH rule 2 x 183 [8,4]
+ CRUSH rule 2 x 184 [4,8]
+ CRUSH rule 2 x 185 [8,1]
+ CRUSH rule 2 x 186 [2,4]
+ CRUSH rule 2 x 187 [1,8]
+ CRUSH rule 2 x 188 [1,8]
+ CRUSH rule 2 x 189 [1,8]
+ CRUSH rule 2 x 190 [1,8]
+ CRUSH rule 2 x 191 [8,1]
+ CRUSH rule 2 x 192 [4,1]
+ CRUSH rule 2 x 193 [4,2]
+ CRUSH rule 2 x 194 [1,8]
+ CRUSH rule 2 x 195 [8,4]
+ CRUSH rule 2 x 196 [8,2]
+ CRUSH rule 2 x 197 [8,4]
+ CRUSH rule 2 x 198 [2,8]
+ CRUSH rule 2 x 199 [1,4]
+ CRUSH rule 2 x 200 [1,8]
+ CRUSH rule 2 x 201 [8,1]
+ CRUSH rule 2 x 202 [8,1]
+ CRUSH rule 2 x 203 [8,1]
+ CRUSH rule 2 x 204 [2,4]
+ CRUSH rule 2 x 205 [1,8]
+ CRUSH rule 2 x 206 [1,8]
+ CRUSH rule 2 x 207 [2,8]
+ CRUSH rule 2 x 208 [8,1]
+ CRUSH rule 2 x 209 [1,8]
+ CRUSH rule 2 x 210 [1,4]
+ CRUSH rule 2 x 211 [4,2]
+ CRUSH rule 2 x 212 [8,1]
+ CRUSH rule 2 x 213 [8,4]
+ CRUSH rule 2 x 214 [8,2]
+ CRUSH rule 2 x 215 [8,1]
+ CRUSH rule 2 x 216 [2,8]
+ CRUSH rule 2 x 217 [1,8]
+ CRUSH rule 2 x 218 [2,8]
+ CRUSH rule 2 x 219 [8,2]
+ CRUSH rule 2 x 220 [4,8]
+ CRUSH rule 2 x 221 [8,1]
+ CRUSH rule 2 x 222 [8,1]
+ CRUSH rule 2 x 223 [1,8]
+ CRUSH rule 2 x 224 [1,4]
+ CRUSH rule 2 x 225 [8,2]
+ CRUSH rule 2 x 226 [8,2]
+ CRUSH rule 2 x 227 [4,1]
+ CRUSH rule 2 x 228 [8,2]
+ CRUSH rule 2 x 229 [4,8]
+ CRUSH rule 2 x 230 [4,8]
+ CRUSH rule 2 x 231 [4,8]
+ CRUSH rule 2 x 232 [2,8]
+ CRUSH rule 2 x 233 [2,8]
+ CRUSH rule 2 x 234 [1,8]
+ CRUSH rule 2 x 235 [4,8]
+ CRUSH rule 2 x 236 [2,6]
+ CRUSH rule 2 x 237 [4,8]
+ CRUSH rule 2 x 238 [2,8]
+ CRUSH rule 2 x 239 [8,1]
+ CRUSH rule 2 x 240 [4,8]
+ CRUSH rule 2 x 241 [1,8]
+ CRUSH rule 2 x 242 [8,2]
+ CRUSH rule 2 x 243 [8,2]
+ CRUSH rule 2 x 244 [4,8]
+ CRUSH rule 2 x 245 [8,1]
+ CRUSH rule 2 x 246 [1,8]
+ CRUSH rule 2 x 247 [8,2]
+ CRUSH rule 2 x 248 [8,2]
+ CRUSH rule 2 x 249 [2,8]
+ CRUSH rule 2 x 250 [2,4]
+ CRUSH rule 2 x 251 [2,8]
+ CRUSH rule 2 x 252 [4,8]
+ CRUSH rule 2 x 253 [2,8]
+ CRUSH rule 2 x 254 [4,2]
+ CRUSH rule 2 x 255 [1,8]
+ CRUSH rule 2 x 256 [4,8]
+ CRUSH rule 2 x 257 [2,8]
+ CRUSH rule 2 x 258 [4,2]
+ CRUSH rule 2 x 259 [6,2]
+ CRUSH rule 2 x 260 [8,2]
+ CRUSH rule 2 x 261 [8,1]
+ CRUSH rule 2 x 262 [8,1]
+ CRUSH rule 2 x 263 [8,1]
+ CRUSH rule 2 x 264 [8,2]
+ CRUSH rule 2 x 265 [8,2]
+ CRUSH rule 2 x 266 [8,2]
+ CRUSH rule 2 x 267 [2,8]
+ CRUSH rule 2 x 268 [1,8]
+ CRUSH rule 2 x 269 [1,8]
+ CRUSH rule 2 x 270 [4,1]
+ CRUSH rule 2 x 271 [8,4]
+ CRUSH rule 2 x 272 [2,8]
+ CRUSH rule 2 x 273 [4,1]
+ CRUSH rule 2 x 274 [8,4]
+ CRUSH rule 2 x 275 [4,8]
+ CRUSH rule 2 x 276 [8,1]
+ CRUSH rule 2 x 277 [8,1]
+ CRUSH rule 2 x 278 [8,1]
+ CRUSH rule 2 x 279 [8,4]
+ CRUSH rule 2 x 280 [2,8]
+ CRUSH rule 2 x 281 [8,2]
+ CRUSH rule 2 x 282 [2,8]
+ CRUSH rule 2 x 283 [8,2]
+ CRUSH rule 2 x 284 [8,2]
+ CRUSH rule 2 x 285 [4,8]
+ CRUSH rule 2 x 286 [2,8]
+ CRUSH rule 2 x 287 [1,8]
+ CRUSH rule 2 x 288 [8,1]
+ CRUSH rule 2 x 289 [4,8]
+ CRUSH rule 2 x 290 [1,4]
+ CRUSH rule 2 x 291 [1,4]
+ CRUSH rule 2 x 292 [8,2]
+ CRUSH rule 2 x 293 [8,1]
+ CRUSH rule 2 x 294 [8,4]
+ CRUSH rule 2 x 295 [4,8]
+ CRUSH rule 2 x 296 [4,1]
+ CRUSH rule 2 x 297 [8,2]
+ CRUSH rule 2 x 298 [1,8]
+ CRUSH rule 2 x 299 [2,8]
+ CRUSH rule 2 x 300 [8,2]
+ CRUSH rule 2 x 301 [1,8]
+ CRUSH rule 2 x 302 [1,8]
+ CRUSH rule 2 x 303 [8,4]
+ CRUSH rule 2 x 304 [2,8]
+ CRUSH rule 2 x 305 [8,2]
+ CRUSH rule 2 x 306 [1,8]
+ CRUSH rule 2 x 307 [2,8]
+ CRUSH rule 2 x 308 [2,8]
+ CRUSH rule 2 x 309 [8,1]
+ CRUSH rule 2 x 310 [4,1]
+ CRUSH rule 2 x 311 [4,8]
+ CRUSH rule 2 x 312 [2,8]
+ CRUSH rule 2 x 313 [4,1]
+ CRUSH rule 2 x 314 [2,8]
+ CRUSH rule 2 x 315 [2,8]
+ CRUSH rule 2 x 316 [8,1]
+ CRUSH rule 2 x 317 [2,8]
+ CRUSH rule 2 x 318 [8,1]
+ CRUSH rule 2 x 319 [2,8]
+ CRUSH rule 2 x 320 [8,1]
+ CRUSH rule 2 x 321 [1,8]
+ CRUSH rule 2 x 322 [2,8]
+ CRUSH rule 2 x 323 [4,8]
+ CRUSH rule 2 x 324 [8,1]
+ CRUSH rule 2 x 325 [4,8]
+ CRUSH rule 2 x 326 [1,6]
+ CRUSH rule 2 x 327 [1,8]
+ CRUSH rule 2 x 328 [8,4]
+ CRUSH rule 2 x 329 [4,8]
+ CRUSH rule 2 x 330 [4,8]
+ CRUSH rule 2 x 331 [2,8]
+ CRUSH rule 2 x 332 [2,8]
+ CRUSH rule 2 x 333 [8,1]
+ CRUSH rule 2 x 334 [8,2]
+ CRUSH rule 2 x 335 [8,1]
+ CRUSH rule 2 x 336 [4,8]
+ CRUSH rule 2 x 337 [8,2]
+ CRUSH rule 2 x 338 [8,1]
+ CRUSH rule 2 x 339 [8,2]
+ CRUSH rule 2 x 340 [2,8]
+ CRUSH rule 2 x 341 [4,1]
+ CRUSH rule 2 x 342 [2,8]
+ CRUSH rule 2 x 343 [8,1]
+ CRUSH rule 2 x 344 [6,2]
+ CRUSH rule 2 x 345 [2,8]
+ CRUSH rule 2 x 346 [8,2]
+ CRUSH rule 2 x 347 [4,1]
+ CRUSH rule 2 x 348 [8,2]
+ CRUSH rule 2 x 349 [1,8]
+ CRUSH rule 2 x 350 [8,1]
+ CRUSH rule 2 x 351 [8,2]
+ CRUSH rule 2 x 352 [1,8]
+ CRUSH rule 2 x 353 [8,1]
+ CRUSH rule 2 x 354 [1,8]
+ CRUSH rule 2 x 355 [8,2]
+ CRUSH rule 2 x 356 [4,1]
+ CRUSH rule 2 x 357 [8,1]
+ CRUSH rule 2 x 358 [2,8]
+ CRUSH rule 2 x 359 [6,1]
+ CRUSH rule 2 x 360 [2,8]
+ CRUSH rule 2 x 361 [8,4]
+ CRUSH rule 2 x 362 [4,1]
+ CRUSH rule 2 x 363 [4,1]
+ CRUSH rule 2 x 364 [2,8]
+ CRUSH rule 2 x 365 [8,1]
+ CRUSH rule 2 x 366 [8,2]
+ CRUSH rule 2 x 367 [4,2]
+ CRUSH rule 2 x 368 [8,4]
+ CRUSH rule 2 x 369 [8,1]
+ CRUSH rule 2 x 370 [8,2]
+ CRUSH rule 2 x 371 [1,4]
+ CRUSH rule 2 x 372 [1,8]
+ CRUSH rule 2 x 373 [1,8]
+ CRUSH rule 2 x 374 [8,1]
+ CRUSH rule 2 x 375 [8,4]
+ CRUSH rule 2 x 376 [8,1]
+ CRUSH rule 2 x 377 [1,4]
+ CRUSH rule 2 x 378 [1,8]
+ CRUSH rule 2 x 379 [8,2]
+ CRUSH rule 2 x 380 [2,8]
+ CRUSH rule 2 x 381 [1,4]
+ CRUSH rule 2 x 382 [1,4]
+ CRUSH rule 2 x 383 [4,8]
+ CRUSH rule 2 x 384 [8,2]
+ CRUSH rule 2 x 385 [8,1]
+ CRUSH rule 2 x 386 [1,8]
+ CRUSH rule 2 x 387 [1,4]
+ CRUSH rule 2 x 388 [2,6]
+ CRUSH rule 2 x 389 [1,4]
+ CRUSH rule 2 x 390 [4,8]
+ CRUSH rule 2 x 391 [4,8]
+ CRUSH rule 2 x 392 [1,8]
+ CRUSH rule 2 x 393 [2,8]
+ CRUSH rule 2 x 394 [8,2]
+ CRUSH rule 2 x 395 [1,8]
+ CRUSH rule 2 x 396 [4,2]
+ CRUSH rule 2 x 397 [2,4]
+ CRUSH rule 2 x 398 [2,4]
+ CRUSH rule 2 x 399 [8,4]
+ CRUSH rule 2 x 400 [8,1]
+ CRUSH rule 2 x 401 [1,4]
+ CRUSH rule 2 x 402 [8,4]
+ CRUSH rule 2 x 403 [1,4]
+ CRUSH rule 2 x 404 [4,2]
+ CRUSH rule 2 x 405 [8,4]
+ CRUSH rule 2 x 406 [2,8]
+ CRUSH rule 2 x 407 [2,8]
+ CRUSH rule 2 x 408 [4,1]
+ CRUSH rule 2 x 409 [8,4]
+ CRUSH rule 2 x 410 [8,4]
+ CRUSH rule 2 x 411 [2,8]
+ CRUSH rule 2 x 412 [2,6]
+ CRUSH rule 2 x 413 [2,8]
+ CRUSH rule 2 x 414 [4,1]
+ CRUSH rule 2 x 415 [2,8]
+ CRUSH rule 2 x 416 [2,8]
+ CRUSH rule 2 x 417 [8,2]
+ CRUSH rule 2 x 418 [8,1]
+ CRUSH rule 2 x 419 [8,4]
+ CRUSH rule 2 x 420 [1,4]
+ CRUSH rule 2 x 421 [8,4]
+ CRUSH rule 2 x 422 [6,2]
+ CRUSH rule 2 x 423 [2,4]
+ CRUSH rule 2 x 424 [8,1]
+ CRUSH rule 2 x 425 [1,8]
+ CRUSH rule 2 x 426 [8,2]
+ CRUSH rule 2 x 427 [1,8]
+ CRUSH rule 2 x 428 [4,8]
+ CRUSH rule 2 x 429 [4,8]
+ CRUSH rule 2 x 430 [4,8]
+ CRUSH rule 2 x 431 [4,1]
+ CRUSH rule 2 x 432 [8,1]
+ CRUSH rule 2 x 433 [8,1]
+ CRUSH rule 2 x 434 [2,8]
+ CRUSH rule 2 x 435 [2,8]
+ CRUSH rule 2 x 436 [4,1]
+ CRUSH rule 2 x 437 [8,2]
+ CRUSH rule 2 x 438 [2,4]
+ CRUSH rule 2 x 439 [1,6]
+ CRUSH rule 2 x 440 [2,8]
+ CRUSH rule 2 x 441 [4,6]
+ CRUSH rule 2 x 442 [2,8]
+ CRUSH rule 2 x 443 [8,2]
+ CRUSH rule 2 x 444 [8,1]
+ CRUSH rule 2 x 445 [8,2]
+ CRUSH rule 2 x 446 [8,1]
+ CRUSH rule 2 x 447 [2,4]
+ CRUSH rule 2 x 448 [8,2]
+ CRUSH rule 2 x 449 [8,1]
+ CRUSH rule 2 x 450 [1,8]
+ CRUSH rule 2 x 451 [8,4]
+ CRUSH rule 2 x 452 [8,2]
+ CRUSH rule 2 x 453 [6,2]
+ CRUSH rule 2 x 454 [8,2]
+ CRUSH rule 2 x 455 [2,8]
+ CRUSH rule 2 x 456 [8,2]
+ CRUSH rule 2 x 457 [8,2]
+ CRUSH rule 2 x 458 [2,8]
+ CRUSH rule 2 x 459 [2,8]
+ CRUSH rule 2 x 460 [8,2]
+ CRUSH rule 2 x 461 [8,1]
+ CRUSH rule 2 x 462 [8,1]
+ CRUSH rule 2 x 463 [8,2]
+ CRUSH rule 2 x 464 [8,4]
+ CRUSH rule 2 x 465 [6,2]
+ CRUSH rule 2 x 466 [8,1]
+ CRUSH rule 2 x 467 [8,2]
+ CRUSH rule 2 x 468 [8,1]
+ CRUSH rule 2 x 469 [8,1]
+ CRUSH rule 2 x 470 [4,2]
+ CRUSH rule 2 x 471 [1,8]
+ CRUSH rule 2 x 472 [1,8]
+ CRUSH rule 2 x 473 [1,4]
+ CRUSH rule 2 x 474 [8,1]
+ CRUSH rule 2 x 475 [8,2]
+ CRUSH rule 2 x 476 [4,8]
+ CRUSH rule 2 x 477 [4,8]
+ CRUSH rule 2 x 478 [8,2]
+ CRUSH rule 2 x 479 [2,8]
+ CRUSH rule 2 x 480 [1,8]
+ CRUSH rule 2 x 481 [2,4]
+ CRUSH rule 2 x 482 [1,8]
+ CRUSH rule 2 x 483 [2,8]
+ CRUSH rule 2 x 484 [1,8]
+ CRUSH rule 2 x 485 [8,1]
+ CRUSH rule 2 x 486 [4,1]
+ CRUSH rule 2 x 487 [1,8]
+ CRUSH rule 2 x 488 [8,1]
+ CRUSH rule 2 x 489 [2,8]
+ CRUSH rule 2 x 490 [6,2]
+ CRUSH rule 2 x 491 [1,8]
+ CRUSH rule 2 x 492 [8,1]
+ CRUSH rule 2 x 493 [2,8]
+ CRUSH rule 2 x 494 [1,8]
+ CRUSH rule 2 x 495 [4,1]
+ CRUSH rule 2 x 496 [8,4]
+ CRUSH rule 2 x 497 [4,8]
+ CRUSH rule 2 x 498 [2,4]
+ CRUSH rule 2 x 499 [8,4]
+ CRUSH rule 2 x 500 [4,8]
+ CRUSH rule 2 x 501 [2,8]
+ CRUSH rule 2 x 502 [6,1]
+ CRUSH rule 2 x 503 [2,8]
+ CRUSH rule 2 x 504 [8,1]
+ CRUSH rule 2 x 505 [1,8]
+ CRUSH rule 2 x 506 [4,2]
+ CRUSH rule 2 x 507 [8,1]
+ CRUSH rule 2 x 508 [1,8]
+ CRUSH rule 2 x 509 [8,1]
+ CRUSH rule 2 x 510 [8,2]
+ CRUSH rule 2 x 511 [4,8]
+ CRUSH rule 2 x 512 [8,2]
+ CRUSH rule 2 x 513 [8,2]
+ CRUSH rule 2 x 514 [2,8]
+ CRUSH rule 2 x 515 [8,4]
+ CRUSH rule 2 x 516 [4,1]
+ CRUSH rule 2 x 517 [8,2]
+ CRUSH rule 2 x 518 [4,8]
+ CRUSH rule 2 x 519 [8,4]
+ CRUSH rule 2 x 520 [2,8]
+ CRUSH rule 2 x 521 [8,2]
+ CRUSH rule 2 x 522 [8,1]
+ CRUSH rule 2 x 523 [4,2]
+ CRUSH rule 2 x 524 [2,6]
+ CRUSH rule 2 x 525 [2,8]
+ CRUSH rule 2 x 526 [1,8]
+ CRUSH rule 2 x 527 [1,4]
+ CRUSH rule 2 x 528 [2,8]
+ CRUSH rule 2 x 529 [4,8]
+ CRUSH rule 2 x 530 [8,1]
+ CRUSH rule 2 x 531 [8,1]
+ CRUSH rule 2 x 532 [6,4]
+ CRUSH rule 2 x 533 [4,8]
+ CRUSH rule 2 x 534 [8,1]
+ CRUSH rule 2 x 535 [8,1]
+ CRUSH rule 2 x 536 [8,2]
+ CRUSH rule 2 x 537 [4,8]
+ CRUSH rule 2 x 538 [8,4]
+ CRUSH rule 2 x 539 [8,1]
+ CRUSH rule 2 x 540 [1,8]
+ CRUSH rule 2 x 541 [2,4]
+ CRUSH rule 2 x 542 [2,8]
+ CRUSH rule 2 x 543 [8,2]
+ CRUSH rule 2 x 544 [4,8]
+ CRUSH rule 2 x 545 [8,1]
+ CRUSH rule 2 x 546 [8,1]
+ CRUSH rule 2 x 547 [8,2]
+ CRUSH rule 2 x 548 [4,2]
+ CRUSH rule 2 x 549 [8,2]
+ CRUSH rule 2 x 550 [2,4]
+ CRUSH rule 2 x 551 [8,1]
+ CRUSH rule 2 x 552 [4,8]
+ CRUSH rule 2 x 553 [2,8]
+ CRUSH rule 2 x 554 [1,8]
+ CRUSH rule 2 x 555 [4,1]
+ CRUSH rule 2 x 556 [8,1]
+ CRUSH rule 2 x 557 [8,2]
+ CRUSH rule 2 x 558 [4,1]
+ CRUSH rule 2 x 559 [1,8]
+ CRUSH rule 2 x 560 [8,1]
+ CRUSH rule 2 x 561 [8,4]
+ CRUSH rule 2 x 562 [4,1]
+ CRUSH rule 2 x 563 [2,8]
+ CRUSH rule 2 x 564 [1,8]
+ CRUSH rule 2 x 565 [4,8]
+ CRUSH rule 2 x 566 [4,8]
+ CRUSH rule 2 x 567 [4,8]
+ CRUSH rule 2 x 568 [8,1]
+ CRUSH rule 2 x 569 [4,1]
+ CRUSH rule 2 x 570 [1,8]
+ CRUSH rule 2 x 571 [6,1]
+ CRUSH rule 2 x 572 [4,2]
+ CRUSH rule 2 x 573 [1,8]
+ CRUSH rule 2 x 574 [2,8]
+ CRUSH rule 2 x 575 [8,2]
+ CRUSH rule 2 x 576 [4,8]
+ CRUSH rule 2 x 577 [8,2]
+ CRUSH rule 2 x 578 [8,1]
+ CRUSH rule 2 x 579 [4,1]
+ CRUSH rule 2 x 580 [1,8]
+ CRUSH rule 2 x 581 [8,2]
+ CRUSH rule 2 x 582 [2,8]
+ CRUSH rule 2 x 583 [8,1]
+ CRUSH rule 2 x 584 [8,1]
+ CRUSH rule 2 x 585 [8,1]
+ CRUSH rule 2 x 586 [1,8]
+ CRUSH rule 2 x 587 [2,4]
+ CRUSH rule 2 x 588 [4,8]
+ CRUSH rule 2 x 589 [8,1]
+ CRUSH rule 2 x 590 [8,2]
+ CRUSH rule 2 x 591 [4,2]
+ CRUSH rule 2 x 592 [2,4]
+ CRUSH rule 2 x 593 [1,8]
+ CRUSH rule 2 x 594 [2,8]
+ CRUSH rule 2 x 595 [8,1]
+ CRUSH rule 2 x 596 [8,2]
+ CRUSH rule 2 x 597 [1,8]
+ CRUSH rule 2 x 598 [2,8]
+ CRUSH rule 2 x 599 [4,2]
+ CRUSH rule 2 x 600 [8,1]
+ CRUSH rule 2 x 601 [1,8]
+ CRUSH rule 2 x 602 [8,2]
+ CRUSH rule 2 x 603 [1,8]
+ CRUSH rule 2 x 604 [8,2]
+ CRUSH rule 2 x 605 [2,8]
+ CRUSH rule 2 x 606 [2,6]
+ CRUSH rule 2 x 607 [2,4]
+ CRUSH rule 2 x 608 [4,2]
+ CRUSH rule 2 x 609 [4,2]
+ CRUSH rule 2 x 610 [8,1]
+ CRUSH rule 2 x 611 [1,8]
+ CRUSH rule 2 x 612 [2,8]
+ CRUSH rule 2 x 613 [8,2]
+ CRUSH rule 2 x 614 [8,2]
+ CRUSH rule 2 x 615 [8,2]
+ CRUSH rule 2 x 616 [1,8]
+ CRUSH rule 2 x 617 [8,1]
+ CRUSH rule 2 x 618 [8,4]
+ CRUSH rule 2 x 619 [4,1]
+ CRUSH rule 2 x 620 [1,8]
+ CRUSH rule 2 x 621 [8,1]
+ CRUSH rule 2 x 622 [2,4]
+ CRUSH rule 2 x 623 [2,8]
+ CRUSH rule 2 x 624 [4,2]
+ CRUSH rule 2 x 625 [2,8]
+ CRUSH rule 2 x 626 [8,2]
+ CRUSH rule 2 x 627 [2,8]
+ CRUSH rule 2 x 628 [8,2]
+ CRUSH rule 2 x 629 [2,8]
+ CRUSH rule 2 x 630 [2,8]
+ CRUSH rule 2 x 631 [1,8]
+ CRUSH rule 2 x 632 [8,2]
+ CRUSH rule 2 x 633 [8,2]
+ CRUSH rule 2 x 634 [1,8]
+ CRUSH rule 2 x 635 [4,8]
+ CRUSH rule 2 x 636 [1,4]
+ CRUSH rule 2 x 637 [1,8]
+ CRUSH rule 2 x 638 [8,1]
+ CRUSH rule 2 x 639 [2,8]
+ CRUSH rule 2 x 640 [2,8]
+ CRUSH rule 2 x 641 [8,2]
+ CRUSH rule 2 x 642 [2,8]
+ CRUSH rule 2 x 643 [1,8]
+ CRUSH rule 2 x 644 [8,1]
+ CRUSH rule 2 x 645 [8,1]
+ CRUSH rule 2 x 646 [8,1]
+ CRUSH rule 2 x 647 [8,1]
+ CRUSH rule 2 x 648 [1,8]
+ CRUSH rule 2 x 649 [4,8]
+ CRUSH rule 2 x 650 [8,4]
+ CRUSH rule 2 x 651 [4,6]
+ CRUSH rule 2 x 652 [4,8]
+ CRUSH rule 2 x 653 [8,2]
+ CRUSH rule 2 x 654 [6,2]
+ CRUSH rule 2 x 655 [1,4]
+ CRUSH rule 2 x 656 [8,1]
+ CRUSH rule 2 x 657 [6,1]
+ CRUSH rule 2 x 658 [8,2]
+ CRUSH rule 2 x 659 [4,8]
+ CRUSH rule 2 x 660 [8,2]
+ CRUSH rule 2 x 661 [1,8]
+ CRUSH rule 2 x 662 [8,2]
+ CRUSH rule 2 x 663 [1,4]
+ CRUSH rule 2 x 664 [1,4]
+ CRUSH rule 2 x 665 [4,6]
+ CRUSH rule 2 x 666 [2,8]
+ CRUSH rule 2 x 667 [1,4]
+ CRUSH rule 2 x 668 [4,8]
+ CRUSH rule 2 x 669 [6,4]
+ CRUSH rule 2 x 670 [4,2]
+ CRUSH rule 2 x 671 [2,8]
+ CRUSH rule 2 x 672 [4,2]
+ CRUSH rule 2 x 673 [4,2]
+ CRUSH rule 2 x 674 [1,8]
+ CRUSH rule 2 x 675 [1,8]
+ CRUSH rule 2 x 676 [2,4]
+ CRUSH rule 2 x 677 [4,1]
+ CRUSH rule 2 x 678 [2,4]
+ CRUSH rule 2 x 679 [8,2]
+ CRUSH rule 2 x 680 [2,8]
+ CRUSH rule 2 x 681 [8,1]
+ CRUSH rule 2 x 682 [1,4]
+ CRUSH rule 2 x 683 [1,4]
+ CRUSH rule 2 x 684 [8,1]
+ CRUSH rule 2 x 685 [8,1]
+ CRUSH rule 2 x 686 [1,4]
+ CRUSH rule 2 x 687 [6,1]
+ CRUSH rule 2 x 688 [4,8]
+ CRUSH rule 2 x 689 [8,4]
+ CRUSH rule 2 x 690 [8,1]
+ CRUSH rule 2 x 691 [1,8]
+ CRUSH rule 2 x 692 [8,2]
+ CRUSH rule 2 x 693 [8,4]
+ CRUSH rule 2 x 694 [8,4]
+ CRUSH rule 2 x 695 [2,8]
+ CRUSH rule 2 x 696 [1,8]
+ CRUSH rule 2 x 697 [8,1]
+ CRUSH rule 2 x 698 [8,2]
+ CRUSH rule 2 x 699 [1,8]
+ CRUSH rule 2 x 700 [1,8]
+ CRUSH rule 2 x 701 [1,8]
+ CRUSH rule 2 x 702 [2,8]
+ CRUSH rule 2 x 703 [8,1]
+ CRUSH rule 2 x 704 [1,4]
+ CRUSH rule 2 x 705 [8,1]
+ CRUSH rule 2 x 706 [1,4]
+ CRUSH rule 2 x 707 [8,4]
+ CRUSH rule 2 x 708 [4,8]
+ CRUSH rule 2 x 709 [8,2]
+ CRUSH rule 2 x 710 [8,2]
+ CRUSH rule 2 x 711 [2,4]
+ CRUSH rule 2 x 712 [2,8]
+ CRUSH rule 2 x 713 [8,4]
+ CRUSH rule 2 x 714 [2,8]
+ CRUSH rule 2 x 715 [1,8]
+ CRUSH rule 2 x 716 [4,8]
+ CRUSH rule 2 x 717 [8,2]
+ CRUSH rule 2 x 718 [8,1]
+ CRUSH rule 2 x 719 [2,6]
+ CRUSH rule 2 x 720 [8,1]
+ CRUSH rule 2 x 721 [4,6]
+ CRUSH rule 2 x 722 [8,2]
+ CRUSH rule 2 x 723 [4,1]
+ CRUSH rule 2 x 724 [2,6]
+ CRUSH rule 2 x 725 [1,8]
+ CRUSH rule 2 x 726 [4,8]
+ CRUSH rule 2 x 727 [4,8]
+ CRUSH rule 2 x 728 [2,8]
+ CRUSH rule 2 x 729 [8,2]
+ CRUSH rule 2 x 730 [4,8]
+ CRUSH rule 2 x 731 [4,1]
+ CRUSH rule 2 x 732 [1,8]
+ CRUSH rule 2 x 733 [4,8]
+ CRUSH rule 2 x 734 [8,4]
+ CRUSH rule 2 x 735 [4,8]
+ CRUSH rule 2 x 736 [4,8]
+ CRUSH rule 2 x 737 [1,8]
+ CRUSH rule 2 x 738 [4,2]
+ CRUSH rule 2 x 739 [2,8]
+ CRUSH rule 2 x 740 [1,8]
+ CRUSH rule 2 x 741 [8,1]
+ CRUSH rule 2 x 742 [8,2]
+ CRUSH rule 2 x 743 [8,1]
+ CRUSH rule 2 x 744 [4,8]
+ CRUSH rule 2 x 745 [1,8]
+ CRUSH rule 2 x 746 [1,8]
+ CRUSH rule 2 x 747 [8,1]
+ CRUSH rule 2 x 748 [2,8]
+ CRUSH rule 2 x 749 [4,8]
+ CRUSH rule 2 x 750 [1,8]
+ CRUSH rule 2 x 751 [2,8]
+ CRUSH rule 2 x 752 [8,1]
+ CRUSH rule 2 x 753 [8,4]
+ CRUSH rule 2 x 754 [8,4]
+ CRUSH rule 2 x 755 [1,8]
+ CRUSH rule 2 x 756 [8,1]
+ CRUSH rule 2 x 757 [8,1]
+ CRUSH rule 2 x 758 [8,2]
+ CRUSH rule 2 x 759 [8,4]
+ CRUSH rule 2 x 760 [1,4]
+ CRUSH rule 2 x 761 [2,8]
+ CRUSH rule 2 x 762 [2,8]
+ CRUSH rule 2 x 763 [8,4]
+ CRUSH rule 2 x 764 [1,8]
+ CRUSH rule 2 x 765 [8,2]
+ CRUSH rule 2 x 766 [8,1]
+ CRUSH rule 2 x 767 [1,8]
+ CRUSH rule 2 x 768 [8,4]
+ CRUSH rule 2 x 769 [8,2]
+ CRUSH rule 2 x 770 [8,2]
+ CRUSH rule 2 x 771 [8,1]
+ CRUSH rule 2 x 772 [8,4]
+ CRUSH rule 2 x 773 [4,1]
+ CRUSH rule 2 x 774 [8,1]
+ CRUSH rule 2 x 775 [8,4]
+ CRUSH rule 2 x 776 [6,2]
+ CRUSH rule 2 x 777 [4,1]
+ CRUSH rule 2 x 778 [1,8]
+ CRUSH rule 2 x 779 [2,8]
+ CRUSH rule 2 x 780 [2,4]
+ CRUSH rule 2 x 781 [8,2]
+ CRUSH rule 2 x 782 [4,1]
+ CRUSH rule 2 x 783 [8,1]
+ CRUSH rule 2 x 784 [1,4]
+ CRUSH rule 2 x 785 [8,1]
+ CRUSH rule 2 x 786 [8,1]
+ CRUSH rule 2 x 787 [1,6]
+ CRUSH rule 2 x 788 [8,2]
+ CRUSH rule 2 x 789 [1,8]
+ CRUSH rule 2 x 790 [8,1]
+ CRUSH rule 2 x 791 [4,8]
+ CRUSH rule 2 x 792 [4,8]
+ CRUSH rule 2 x 793 [8,1]
+ CRUSH rule 2 x 794 [2,8]
+ CRUSH rule 2 x 795 [1,8]
+ CRUSH rule 2 x 796 [8,2]
+ CRUSH rule 2 x 797 [2,4]
+ CRUSH rule 2 x 798 [6,1]
+ CRUSH rule 2 x 799 [4,1]
+ CRUSH rule 2 x 800 [2,8]
+ CRUSH rule 2 x 801 [4,8]
+ CRUSH rule 2 x 802 [1,8]
+ CRUSH rule 2 x 803 [2,8]
+ CRUSH rule 2 x 804 [8,2]
+ CRUSH rule 2 x 805 [8,2]
+ CRUSH rule 2 x 806 [1,4]
+ CRUSH rule 2 x 807 [4,8]
+ CRUSH rule 2 x 808 [8,2]
+ CRUSH rule 2 x 809 [1,8]
+ CRUSH rule 2 x 810 [8,2]
+ CRUSH rule 2 x 811 [8,1]
+ CRUSH rule 2 x 812 [8,4]
+ CRUSH rule 2 x 813 [8,4]
+ CRUSH rule 2 x 814 [8,2]
+ CRUSH rule 2 x 815 [4,1]
+ CRUSH rule 2 x 816 [2,8]
+ CRUSH rule 2 x 817 [8,1]
+ CRUSH rule 2 x 818 [1,8]
+ CRUSH rule 2 x 819 [1,8]
+ CRUSH rule 2 x 820 [4,8]
+ CRUSH rule 2 x 821 [4,8]
+ CRUSH rule 2 x 822 [2,4]
+ CRUSH rule 2 x 823 [4,8]
+ CRUSH rule 2 x 824 [8,2]
+ CRUSH rule 2 x 825 [2,8]
+ CRUSH rule 2 x 826 [8,2]
+ CRUSH rule 2 x 827 [2,8]
+ CRUSH rule 2 x 828 [2,8]
+ CRUSH rule 2 x 829 [8,1]
+ CRUSH rule 2 x 830 [2,4]
+ CRUSH rule 2 x 831 [1,8]
+ CRUSH rule 2 x 832 [4,8]
+ CRUSH rule 2 x 833 [2,8]
+ CRUSH rule 2 x 834 [1,8]
+ CRUSH rule 2 x 835 [8,4]
+ CRUSH rule 2 x 836 [4,8]
+ CRUSH rule 2 x 837 [8,4]
+ CRUSH rule 2 x 838 [6,2]
+ CRUSH rule 2 x 839 [2,8]
+ CRUSH rule 2 x 840 [8,1]
+ CRUSH rule 2 x 841 [4,8]
+ CRUSH rule 2 x 842 [2,8]
+ CRUSH rule 2 x 843 [8,4]
+ CRUSH rule 2 x 844 [8,2]
+ CRUSH rule 2 x 845 [4,8]
+ CRUSH rule 2 x 846 [4,2]
+ CRUSH rule 2 x 847 [2,8]
+ CRUSH rule 2 x 848 [2,8]
+ CRUSH rule 2 x 849 [4,8]
+ CRUSH rule 2 x 850 [1,6]
+ CRUSH rule 2 x 851 [6,1]
+ CRUSH rule 2 x 852 [8,4]
+ CRUSH rule 2 x 853 [6,1]
+ CRUSH rule 2 x 854 [8,1]
+ CRUSH rule 2 x 855 [8,1]
+ CRUSH rule 2 x 856 [8,4]
+ CRUSH rule 2 x 857 [8,2]
+ CRUSH rule 2 x 858 [6,1]
+ CRUSH rule 2 x 859 [8,2]
+ CRUSH rule 2 x 860 [2,8]
+ CRUSH rule 2 x 861 [8,2]
+ CRUSH rule 2 x 862 [8,1]
+ CRUSH rule 2 x 863 [8,2]
+ CRUSH rule 2 x 864 [8,2]
+ CRUSH rule 2 x 865 [8,1]
+ CRUSH rule 2 x 866 [8,2]
+ CRUSH rule 2 x 867 [8,2]
+ CRUSH rule 2 x 868 [8,1]
+ CRUSH rule 2 x 869 [8,4]
+ CRUSH rule 2 x 870 [2,8]
+ CRUSH rule 2 x 871 [1,8]
+ CRUSH rule 2 x 872 [1,8]
+ CRUSH rule 2 x 873 [4,8]
+ CRUSH rule 2 x 874 [2,6]
+ CRUSH rule 2 x 875 [2,8]
+ CRUSH rule 2 x 876 [4,8]
+ CRUSH rule 2 x 877 [8,4]
+ CRUSH rule 2 x 878 [2,8]
+ CRUSH rule 2 x 879 [8,1]
+ CRUSH rule 2 x 880 [1,8]
+ CRUSH rule 2 x 881 [4,8]
+ CRUSH rule 2 x 882 [1,8]
+ CRUSH rule 2 x 883 [2,4]
+ CRUSH rule 2 x 884 [8,2]
+ CRUSH rule 2 x 885 [4,1]
+ CRUSH rule 2 x 886 [8,2]
+ CRUSH rule 2 x 887 [8,4]
+ CRUSH rule 2 x 888 [8,2]
+ CRUSH rule 2 x 889 [2,6]
+ CRUSH rule 2 x 890 [8,2]
+ CRUSH rule 2 x 891 [1,8]
+ CRUSH rule 2 x 892 [8,2]
+ CRUSH rule 2 x 893 [2,6]
+ CRUSH rule 2 x 894 [8,4]
+ CRUSH rule 2 x 895 [4,1]
+ CRUSH rule 2 x 896 [1,8]
+ CRUSH rule 2 x 897 [2,8]
+ CRUSH rule 2 x 898 [1,4]
+ CRUSH rule 2 x 899 [1,8]
+ CRUSH rule 2 x 900 [4,1]
+ CRUSH rule 2 x 901 [2,8]
+ CRUSH rule 2 x 902 [8,4]
+ CRUSH rule 2 x 903 [8,2]
+ CRUSH rule 2 x 904 [8,2]
+ CRUSH rule 2 x 905 [8,2]
+ CRUSH rule 2 x 906 [1,8]
+ CRUSH rule 2 x 907 [8,1]
+ CRUSH rule 2 x 908 [8,1]
+ CRUSH rule 2 x 909 [2,8]
+ CRUSH rule 2 x 910 [8,2]
+ CRUSH rule 2 x 911 [8,1]
+ CRUSH rule 2 x 912 [1,8]
+ CRUSH rule 2 x 913 [8,2]
+ CRUSH rule 2 x 914 [6,4]
+ CRUSH rule 2 x 915 [8,2]
+ CRUSH rule 2 x 916 [4,1]
+ CRUSH rule 2 x 917 [1,4]
+ CRUSH rule 2 x 918 [8,2]
+ CRUSH rule 2 x 919 [8,2]
+ CRUSH rule 2 x 920 [8,1]
+ CRUSH rule 2 x 921 [1,8]
+ CRUSH rule 2 x 922 [8,4]
+ CRUSH rule 2 x 923 [4,8]
+ CRUSH rule 2 x 924 [8,1]
+ CRUSH rule 2 x 925 [4,8]
+ CRUSH rule 2 x 926 [2,8]
+ CRUSH rule 2 x 927 [1,8]
+ CRUSH rule 2 x 928 [8,1]
+ CRUSH rule 2 x 929 [4,1]
+ CRUSH rule 2 x 930 [2,8]
+ CRUSH rule 2 x 931 [2,8]
+ CRUSH rule 2 x 932 [4,1]
+ CRUSH rule 2 x 933 [8,4]
+ CRUSH rule 2 x 934 [8,2]
+ CRUSH rule 2 x 935 [8,2]
+ CRUSH rule 2 x 936 [1,8]
+ CRUSH rule 2 x 937 [4,8]
+ CRUSH rule 2 x 938 [8,4]
+ CRUSH rule 2 x 939 [2,8]
+ CRUSH rule 2 x 940 [8,1]
+ CRUSH rule 2 x 941 [2,8]
+ CRUSH rule 2 x 942 [1,8]
+ CRUSH rule 2 x 943 [8,2]
+ CRUSH rule 2 x 944 [8,2]
+ CRUSH rule 2 x 945 [8,2]
+ CRUSH rule 2 x 946 [2,8]
+ CRUSH rule 2 x 947 [8,2]
+ CRUSH rule 2 x 948 [8,1]
+ CRUSH rule 2 x 949 [6,1]
+ CRUSH rule 2 x 950 [8,1]
+ CRUSH rule 2 x 951 [8,1]
+ CRUSH rule 2 x 952 [2,8]
+ CRUSH rule 2 x 953 [1,4]
+ CRUSH rule 2 x 954 [2,8]
+ CRUSH rule 2 x 955 [8,1]
+ CRUSH rule 2 x 956 [1,8]
+ CRUSH rule 2 x 957 [8,1]
+ CRUSH rule 2 x 958 [8,4]
+ CRUSH rule 2 x 959 [4,2]
+ CRUSH rule 2 x 960 [6,1]
+ CRUSH rule 2 x 961 [1,8]
+ CRUSH rule 2 x 962 [8,4]
+ CRUSH rule 2 x 963 [2,4]
+ CRUSH rule 2 x 964 [2,8]
+ CRUSH rule 2 x 965 [8,2]
+ CRUSH rule 2 x 966 [4,8]
+ CRUSH rule 2 x 967 [8,4]
+ CRUSH rule 2 x 968 [8,2]
+ CRUSH rule 2 x 969 [8,2]
+ CRUSH rule 2 x 970 [2,8]
+ CRUSH rule 2 x 971 [1,8]
+ CRUSH rule 2 x 972 [1,8]
+ CRUSH rule 2 x 973 [1,8]
+ CRUSH rule 2 x 974 [4,1]
+ CRUSH rule 2 x 975 [4,8]
+ CRUSH rule 2 x 976 [4,8]
+ CRUSH rule 2 x 977 [8,4]
+ CRUSH rule 2 x 978 [8,2]
+ CRUSH rule 2 x 979 [8,1]
+ CRUSH rule 2 x 980 [8,2]
+ CRUSH rule 2 x 981 [8,2]
+ CRUSH rule 2 x 982 [1,8]
+ CRUSH rule 2 x 983 [4,8]
+ CRUSH rule 2 x 984 [2,8]
+ CRUSH rule 2 x 985 [2,4]
+ CRUSH rule 2 x 986 [8,4]
+ CRUSH rule 2 x 987 [2,8]
+ CRUSH rule 2 x 988 [1,4]
+ CRUSH rule 2 x 989 [1,8]
+ CRUSH rule 2 x 990 [1,8]
+ CRUSH rule 2 x 991 [1,4]
+ CRUSH rule 2 x 992 [8,1]
+ CRUSH rule 2 x 993 [2,8]
+ CRUSH rule 2 x 994 [4,8]
+ CRUSH rule 2 x 995 [8,1]
+ CRUSH rule 2 x 996 [8,4]
+ CRUSH rule 2 x 997 [8,4]
+ CRUSH rule 2 x 998 [8,1]
+ CRUSH rule 2 x 999 [1,8]
+ CRUSH rule 2 x 1000 [8,4]
+ CRUSH rule 2 x 1001 [2,8]
+ CRUSH rule 2 x 1002 [1,8]
+ CRUSH rule 2 x 1003 [2,8]
+ CRUSH rule 2 x 1004 [8,1]
+ CRUSH rule 2 x 1005 [8,1]
+ CRUSH rule 2 x 1006 [1,8]
+ CRUSH rule 2 x 1007 [1,4]
+ CRUSH rule 2 x 1008 [1,8]
+ CRUSH rule 2 x 1009 [6,4]
+ CRUSH rule 2 x 1010 [1,8]
+ CRUSH rule 2 x 1011 [4,2]
+ CRUSH rule 2 x 1012 [1,8]
+ CRUSH rule 2 x 1013 [2,8]
+ CRUSH rule 2 x 1014 [2,8]
+ CRUSH rule 2 x 1015 [8,1]
+ CRUSH rule 2 x 1016 [2,4]
+ CRUSH rule 2 x 1017 [6,2]
+ CRUSH rule 2 x 1018 [4,1]
+ CRUSH rule 2 x 1019 [4,8]
+ CRUSH rule 2 x 1020 [1,8]
+ CRUSH rule 2 x 1021 [2,8]
+ CRUSH rule 2 x 1022 [1,8]
+ CRUSH rule 2 x 1023 [4,2]
+ rule 2 (chooseleaf) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 2 x 0 [2,4,8]
+ CRUSH rule 2 x 1 [2,8,4]
+ CRUSH rule 2 x 2 [1,8]
+ CRUSH rule 2 x 3 [8,1]
+ CRUSH rule 2 x 4 [4,2,6]
+ CRUSH rule 2 x 5 [8,2]
+ CRUSH rule 2 x 6 [2,8,4]
+ CRUSH rule 2 x 7 [4,8,2]
+ CRUSH rule 2 x 8 [4,8,1]
+ CRUSH rule 2 x 9 [2,4,8]
+ CRUSH rule 2 x 10 [2,8]
+ CRUSH rule 2 x 11 [2,8]
+ CRUSH rule 2 x 12 [2,8]
+ CRUSH rule 2 x 13 [4,8,1]
+ CRUSH rule 2 x 14 [8,2]
+ CRUSH rule 2 x 15 [8,2]
+ CRUSH rule 2 x 16 [8,2]
+ CRUSH rule 2 x 17 [4,1,8]
+ CRUSH rule 2 x 18 [1,8]
+ CRUSH rule 2 x 19 [8,4,2]
+ CRUSH rule 2 x 20 [2,8]
+ CRUSH rule 2 x 21 [8,2]
+ CRUSH rule 2 x 22 [8,1]
+ CRUSH rule 2 x 23 [4,8,2]
+ CRUSH rule 2 x 24 [1,8,4]
+ CRUSH rule 2 x 25 [4,8,1]
+ CRUSH rule 2 x 26 [2,8,4]
+ CRUSH rule 2 x 27 [4,1,8]
+ CRUSH rule 2 x 28 [8,2]
+ CRUSH rule 2 x 29 [8,4,2]
+ CRUSH rule 2 x 30 [4,8,2]
+ CRUSH rule 2 x 31 [8,1]
+ CRUSH rule 2 x 32 [6,1]
+ CRUSH rule 2 x 33 [2,8]
+ CRUSH rule 2 x 34 [2,8]
+ CRUSH rule 2 x 35 [1,8,4]
+ CRUSH rule 2 x 36 [8,2]
+ CRUSH rule 2 x 37 [1,8]
+ CRUSH rule 2 x 38 [4,8,2]
+ CRUSH rule 2 x 39 [8,2]
+ CRUSH rule 2 x 40 [8,2,4]
+ CRUSH rule 2 x 41 [2,8,4]
+ CRUSH rule 2 x 42 [8,2]
+ CRUSH rule 2 x 43 [1,8]
+ CRUSH rule 2 x 44 [1,8,4]
+ CRUSH rule 2 x 45 [8,2,4]
+ CRUSH rule 2 x 46 [2,8]
+ CRUSH rule 2 x 47 [4,2,8]
+ CRUSH rule 2 x 48 [8,1]
+ CRUSH rule 2 x 49 [8,2]
+ CRUSH rule 2 x 50 [4,1,8]
+ CRUSH rule 2 x 51 [8,2]
+ CRUSH rule 2 x 52 [8,1,4]
+ CRUSH rule 2 x 53 [4,8,2]
+ CRUSH rule 2 x 54 [8,4,1]
+ CRUSH rule 2 x 55 [8,2]
+ CRUSH rule 2 x 56 [8,4,2]
+ CRUSH rule 2 x 57 [8,1]
+ CRUSH rule 2 x 58 [1,8]
+ CRUSH rule 2 x 59 [2,8]
+ CRUSH rule 2 x 60 [4,2,8]
+ CRUSH rule 2 x 61 [4,8,2]
+ CRUSH rule 2 x 62 [8,1]
+ CRUSH rule 2 x 63 [8,2]
+ CRUSH rule 2 x 64 [4,2,8]
+ CRUSH rule 2 x 65 [8,4,2]
+ CRUSH rule 2 x 66 [4,8,2]
+ CRUSH rule 2 x 67 [4,2,8]
+ CRUSH rule 2 x 68 [1,8]
+ CRUSH rule 2 x 69 [2,8]
+ CRUSH rule 2 x 70 [8,2]
+ CRUSH rule 2 x 71 [2,8,4]
+ CRUSH rule 2 x 72 [8,1,4]
+ CRUSH rule 2 x 73 [2,8]
+ CRUSH rule 2 x 74 [1,8]
+ CRUSH rule 2 x 75 [4,2,8]
+ CRUSH rule 2 x 76 [4,1,6]
+ CRUSH rule 2 x 77 [8,2,4]
+ CRUSH rule 2 x 78 [1,6]
+ CRUSH rule 2 x 79 [4,1,8]
+ CRUSH rule 2 x 80 [2,4,8]
+ CRUSH rule 2 x 81 [2,8]
+ CRUSH rule 2 x 82 [6,1]
+ CRUSH rule 2 x 83 [2,8]
+ CRUSH rule 2 x 84 [8,2]
+ CRUSH rule 2 x 85 [4,8,1]
+ CRUSH rule 2 x 86 [2,8]
+ CRUSH rule 2 x 87 [2,8,4]
+ CRUSH rule 2 x 88 [1,6]
+ CRUSH rule 2 x 89 [1,8]
+ CRUSH rule 2 x 90 [8,4,1]
+ CRUSH rule 2 x 91 [4,8,1]
+ CRUSH rule 2 x 92 [1,8]
+ CRUSH rule 2 x 93 [8,4,2]
+ CRUSH rule 2 x 94 [1,8]
+ CRUSH rule 2 x 95 [8,1]
+ CRUSH rule 2 x 96 [8,2]
+ CRUSH rule 2 x 97 [8,1]
+ CRUSH rule 2 x 98 [2,8]
+ CRUSH rule 2 x 99 [2,8]
+ CRUSH rule 2 x 100 [1,8,4]
+ CRUSH rule 2 x 101 [8,1]
+ CRUSH rule 2 x 102 [2,8]
+ CRUSH rule 2 x 103 [8,2]
+ CRUSH rule 2 x 104 [8,4,1]
+ CRUSH rule 2 x 105 [2,4,8]
+ CRUSH rule 2 x 106 [1,8,4]
+ CRUSH rule 2 x 107 [1,8]
+ CRUSH rule 2 x 108 [8,2]
+ CRUSH rule 2 x 109 [1,4,8]
+ CRUSH rule 2 x 110 [4,2,8]
+ CRUSH rule 2 x 111 [2,4,8]
+ CRUSH rule 2 x 112 [2,8]
+ CRUSH rule 2 x 113 [8,2]
+ CRUSH rule 2 x 114 [8,4,1]
+ CRUSH rule 2 x 115 [8,2,4]
+ CRUSH rule 2 x 116 [1,8]
+ CRUSH rule 2 x 117 [6,1]
+ CRUSH rule 2 x 118 [2,8]
+ CRUSH rule 2 x 119 [8,1]
+ CRUSH rule 2 x 120 [2,4,8]
+ CRUSH rule 2 x 121 [2,8,4]
+ CRUSH rule 2 x 122 [8,1]
+ CRUSH rule 2 x 123 [2,8]
+ CRUSH rule 2 x 124 [2,8]
+ CRUSH rule 2 x 125 [1,8,4]
+ CRUSH rule 2 x 126 [1,8]
+ CRUSH rule 2 x 127 [4,8,2]
+ CRUSH rule 2 x 128 [8,2]
+ CRUSH rule 2 x 129 [2,4,8]
+ CRUSH rule 2 x 130 [4,8,2]
+ CRUSH rule 2 x 131 [1,4,8]
+ CRUSH rule 2 x 132 [1,8]
+ CRUSH rule 2 x 133 [8,1]
+ CRUSH rule 2 x 134 [1,8,4]
+ CRUSH rule 2 x 135 [4,8,2]
+ CRUSH rule 2 x 136 [2,4,8]
+ CRUSH rule 2 x 137 [8,4,2]
+ CRUSH rule 2 x 138 [8,4,2]
+ CRUSH rule 2 x 139 [4,2,8]
+ CRUSH rule 2 x 140 [1,8,4]
+ CRUSH rule 2 x 141 [8,2]
+ CRUSH rule 2 x 142 [4,1,8]
+ CRUSH rule 2 x 143 [4,8,1]
+ CRUSH rule 2 x 144 [8,1]
+ CRUSH rule 2 x 145 [8,1]
+ CRUSH rule 2 x 146 [2,8]
+ CRUSH rule 2 x 147 [2,8,4]
+ CRUSH rule 2 x 148 [4,1,8]
+ CRUSH rule 2 x 149 [4,8,1]
+ CRUSH rule 2 x 150 [1,8]
+ CRUSH rule 2 x 151 [1,8]
+ CRUSH rule 2 x 152 [8,2]
+ CRUSH rule 2 x 153 [8,4,2]
+ CRUSH rule 2 x 154 [4,2,8]
+ CRUSH rule 2 x 155 [4,8,2]
+ CRUSH rule 2 x 156 [4,2,8]
+ CRUSH rule 2 x 157 [1,8]
+ CRUSH rule 2 x 158 [2,8,4]
+ CRUSH rule 2 x 159 [8,2,4]
+ CRUSH rule 2 x 160 [2,8,4]
+ CRUSH rule 2 x 161 [1,4,8]
+ CRUSH rule 2 x 162 [1,8]
+ CRUSH rule 2 x 163 [4,8,1]
+ CRUSH rule 2 x 164 [8,1]
+ CRUSH rule 2 x 165 [8,2,4]
+ CRUSH rule 2 x 166 [2,8]
+ CRUSH rule 2 x 167 [1,8,4]
+ CRUSH rule 2 x 168 [4,2,8]
+ CRUSH rule 2 x 169 [2,8,4]
+ CRUSH rule 2 x 170 [1,8]
+ CRUSH rule 2 x 171 [8,4,2]
+ CRUSH rule 2 x 172 [1,8]
+ CRUSH rule 2 x 173 [8,4,1]
+ CRUSH rule 2 x 174 [1,6]
+ CRUSH rule 2 x 175 [8,1]
+ CRUSH rule 2 x 176 [2,8]
+ CRUSH rule 2 x 177 [1,8]
+ CRUSH rule 2 x 178 [4,2,8]
+ CRUSH rule 2 x 179 [1,8]
+ CRUSH rule 2 x 180 [8,1]
+ CRUSH rule 2 x 181 [8,2,4]
+ CRUSH rule 2 x 182 [8,1]
+ CRUSH rule 2 x 183 [8,4,1]
+ CRUSH rule 2 x 184 [4,8,2]
+ CRUSH rule 2 x 185 [8,1,4]
+ CRUSH rule 2 x 186 [2,4,8]
+ CRUSH rule 2 x 187 [1,8]
+ CRUSH rule 2 x 188 [1,8,4]
+ CRUSH rule 2 x 189 [1,8,4]
+ CRUSH rule 2 x 190 [1,8]
+ CRUSH rule 2 x 191 [8,1,4]
+ CRUSH rule 2 x 192 [4,1,8]
+ CRUSH rule 2 x 193 [4,2,8]
+ CRUSH rule 2 x 194 [1,8]
+ CRUSH rule 2 x 195 [8,4,1]
+ CRUSH rule 2 x 196 [8,2]
+ CRUSH rule 2 x 197 [8,4,2]
+ CRUSH rule 2 x 198 [2,8]
+ CRUSH rule 2 x 199 [1,4,8]
+ CRUSH rule 2 x 200 [1,8]
+ CRUSH rule 2 x 201 [8,1,4]
+ CRUSH rule 2 x 202 [8,1]
+ CRUSH rule 2 x 203 [8,1]
+ CRUSH rule 2 x 204 [2,4,8]
+ CRUSH rule 2 x 205 [1,8]
+ CRUSH rule 2 x 206 [1,8,4]
+ CRUSH rule 2 x 207 [2,8]
+ CRUSH rule 2 x 208 [8,1]
+ CRUSH rule 2 x 209 [1,8]
+ CRUSH rule 2 x 210 [1,4,8]
+ CRUSH rule 2 x 211 [4,2,8]
+ CRUSH rule 2 x 212 [8,1]
+ CRUSH rule 2 x 213 [8,4,2]
+ CRUSH rule 2 x 214 [8,2]
+ CRUSH rule 2 x 215 [8,1]
+ CRUSH rule 2 x 216 [2,8]
+ CRUSH rule 2 x 217 [1,8,4]
+ CRUSH rule 2 x 218 [2,8]
+ CRUSH rule 2 x 219 [8,2]
+ CRUSH rule 2 x 220 [4,8,1]
+ CRUSH rule 2 x 221 [8,1]
+ CRUSH rule 2 x 222 [8,1]
+ CRUSH rule 2 x 223 [1,8]
+ CRUSH rule 2 x 224 [1,4,8]
+ CRUSH rule 2 x 225 [8,2]
+ CRUSH rule 2 x 226 [8,2,4]
+ CRUSH rule 2 x 227 [4,1,8]
+ CRUSH rule 2 x 228 [8,2]
+ CRUSH rule 2 x 229 [4,8,2]
+ CRUSH rule 2 x 230 [4,8,2]
+ CRUSH rule 2 x 231 [4,8,2]
+ CRUSH rule 2 x 232 [2,8,4]
+ CRUSH rule 2 x 233 [2,8]
+ CRUSH rule 2 x 234 [1,8]
+ CRUSH rule 2 x 235 [4,8,1]
+ CRUSH rule 2 x 236 [2,6]
+ CRUSH rule 2 x 237 [4,8,1]
+ CRUSH rule 2 x 238 [2,8]
+ CRUSH rule 2 x 239 [8,1]
+ CRUSH rule 2 x 240 [4,8,2]
+ CRUSH rule 2 x 241 [1,8]
+ CRUSH rule 2 x 242 [8,2]
+ CRUSH rule 2 x 243 [8,2]
+ CRUSH rule 2 x 244 [4,8,2]
+ CRUSH rule 2 x 245 [8,1]
+ CRUSH rule 2 x 246 [1,8]
+ CRUSH rule 2 x 247 [8,2]
+ CRUSH rule 2 x 248 [8,2,4]
+ CRUSH rule 2 x 249 [2,8]
+ CRUSH rule 2 x 250 [2,4,6]
+ CRUSH rule 2 x 251 [2,8]
+ CRUSH rule 2 x 252 [4,8,1]
+ CRUSH rule 2 x 253 [2,8]
+ CRUSH rule 2 x 254 [4,2,8]
+ CRUSH rule 2 x 255 [1,8]
+ CRUSH rule 2 x 256 [4,8,1]
+ CRUSH rule 2 x 257 [2,8,4]
+ CRUSH rule 2 x 258 [4,2,8]
+ CRUSH rule 2 x 259 [6,2]
+ CRUSH rule 2 x 260 [8,2]
+ CRUSH rule 2 x 261 [8,1]
+ CRUSH rule 2 x 262 [8,1]
+ CRUSH rule 2 x 263 [8,1,4]
+ CRUSH rule 2 x 264 [8,2]
+ CRUSH rule 2 x 265 [8,2]
+ CRUSH rule 2 x 266 [8,2,4]
+ CRUSH rule 2 x 267 [2,8]
+ CRUSH rule 2 x 268 [1,8]
+ CRUSH rule 2 x 269 [1,8,4]
+ CRUSH rule 2 x 270 [4,1,8]
+ CRUSH rule 2 x 271 [8,4,1]
+ CRUSH rule 2 x 272 [2,8,4]
+ CRUSH rule 2 x 273 [4,1,8]
+ CRUSH rule 2 x 274 [8,4,1]
+ CRUSH rule 2 x 275 [4,8,1]
+ CRUSH rule 2 x 276 [8,1,4]
+ CRUSH rule 2 x 277 [8,1]
+ CRUSH rule 2 x 278 [8,1,4]
+ CRUSH rule 2 x 279 [8,4,2]
+ CRUSH rule 2 x 280 [2,8,4]
+ CRUSH rule 2 x 281 [8,2]
+ CRUSH rule 2 x 282 [2,8]
+ CRUSH rule 2 x 283 [8,2]
+ CRUSH rule 2 x 284 [8,2]
+ CRUSH rule 2 x 285 [4,8,2]
+ CRUSH rule 2 x 286 [2,8,4]
+ CRUSH rule 2 x 287 [1,8]
+ CRUSH rule 2 x 288 [8,1,4]
+ CRUSH rule 2 x 289 [4,8,2]
+ CRUSH rule 2 x 290 [1,4,8]
+ CRUSH rule 2 x 291 [1,4,8]
+ CRUSH rule 2 x 292 [8,2,4]
+ CRUSH rule 2 x 293 [8,1]
+ CRUSH rule 2 x 294 [8,4,1]
+ CRUSH rule 2 x 295 [4,8,2]
+ CRUSH rule 2 x 296 [4,1,8]
+ CRUSH rule 2 x 297 [8,2,4]
+ CRUSH rule 2 x 298 [1,8]
+ CRUSH rule 2 x 299 [2,8]
+ CRUSH rule 2 x 300 [8,2]
+ CRUSH rule 2 x 301 [1,8]
+ CRUSH rule 2 x 302 [1,8]
+ CRUSH rule 2 x 303 [8,4,1]
+ CRUSH rule 2 x 304 [2,8]
+ CRUSH rule 2 x 305 [8,2]
+ CRUSH rule 2 x 306 [1,8]
+ CRUSH rule 2 x 307 [2,8]
+ CRUSH rule 2 x 308 [2,8,4]
+ CRUSH rule 2 x 309 [8,1]
+ CRUSH rule 2 x 310 [4,1,6]
+ CRUSH rule 2 x 311 [4,8,1]
+ CRUSH rule 2 x 312 [2,8]
+ CRUSH rule 2 x 313 [4,1,8]
+ CRUSH rule 2 x 314 [2,8]
+ CRUSH rule 2 x 315 [2,8]
+ CRUSH rule 2 x 316 [8,1]
+ CRUSH rule 2 x 317 [2,8]
+ CRUSH rule 2 x 318 [8,1]
+ CRUSH rule 2 x 319 [2,8]
+ CRUSH rule 2 x 320 [8,1]
+ CRUSH rule 2 x 321 [1,8]
+ CRUSH rule 2 x 322 [2,8,4]
+ CRUSH rule 2 x 323 [4,8,1]
+ CRUSH rule 2 x 324 [8,1,4]
+ CRUSH rule 2 x 325 [4,8,2]
+ CRUSH rule 2 x 326 [1,6]
+ CRUSH rule 2 x 327 [1,8]
+ CRUSH rule 2 x 328 [8,4,1]
+ CRUSH rule 2 x 329 [4,8,2]
+ CRUSH rule 2 x 330 [4,8,2]
+ CRUSH rule 2 x 331 [2,8]
+ CRUSH rule 2 x 332 [2,8]
+ CRUSH rule 2 x 333 [8,1]
+ CRUSH rule 2 x 334 [8,2]
+ CRUSH rule 2 x 335 [8,1]
+ CRUSH rule 2 x 336 [4,8,2]
+ CRUSH rule 2 x 337 [8,2,4]
+ CRUSH rule 2 x 338 [8,1]
+ CRUSH rule 2 x 339 [8,2]
+ CRUSH rule 2 x 340 [2,8,4]
+ CRUSH rule 2 x 341 [4,1,8]
+ CRUSH rule 2 x 342 [2,8,4]
+ CRUSH rule 2 x 343 [8,1]
+ CRUSH rule 2 x 344 [6,2,4]
+ CRUSH rule 2 x 345 [2,8]
+ CRUSH rule 2 x 346 [8,2,4]
+ CRUSH rule 2 x 347 [4,1,8]
+ CRUSH rule 2 x 348 [8,2,4]
+ CRUSH rule 2 x 349 [1,8]
+ CRUSH rule 2 x 350 [8,1]
+ CRUSH rule 2 x 351 [8,2]
+ CRUSH rule 2 x 352 [1,8,4]
+ CRUSH rule 2 x 353 [8,1]
+ CRUSH rule 2 x 354 [1,8]
+ CRUSH rule 2 x 355 [8,2]
+ CRUSH rule 2 x 356 [4,1,8]
+ CRUSH rule 2 x 357 [8,1,4]
+ CRUSH rule 2 x 358 [2,8,4]
+ CRUSH rule 2 x 359 [6,1,4]
+ CRUSH rule 2 x 360 [2,8]
+ CRUSH rule 2 x 361 [8,4,1]
+ CRUSH rule 2 x 362 [4,1,6]
+ CRUSH rule 2 x 363 [4,1,8]
+ CRUSH rule 2 x 364 [2,8]
+ CRUSH rule 2 x 365 [8,1]
+ CRUSH rule 2 x 366 [8,2]
+ CRUSH rule 2 x 367 [4,2,8]
+ CRUSH rule 2 x 368 [8,4,1]
+ CRUSH rule 2 x 369 [8,1]
+ CRUSH rule 2 x 370 [8,2]
+ CRUSH rule 2 x 371 [1,4,8]
+ CRUSH rule 2 x 372 [1,8]
+ CRUSH rule 2 x 373 [1,8]
+ CRUSH rule 2 x 374 [8,1]
+ CRUSH rule 2 x 375 [8,4,1]
+ CRUSH rule 2 x 376 [8,1,4]
+ CRUSH rule 2 x 377 [1,4,8]
+ CRUSH rule 2 x 378 [1,8]
+ CRUSH rule 2 x 379 [8,2]
+ CRUSH rule 2 x 380 [2,8]
+ CRUSH rule 2 x 381 [1,4,8]
+ CRUSH rule 2 x 382 [1,4,8]
+ CRUSH rule 2 x 383 [4,8,2]
+ CRUSH rule 2 x 384 [8,2,4]
+ CRUSH rule 2 x 385 [8,1]
+ CRUSH rule 2 x 386 [1,8]
+ CRUSH rule 2 x 387 [1,4,8]
+ CRUSH rule 2 x 388 [2,6]
+ CRUSH rule 2 x 389 [1,4,8]
+ CRUSH rule 2 x 390 [4,8,1]
+ CRUSH rule 2 x 391 [4,8,2]
+ CRUSH rule 2 x 392 [1,8,4]
+ CRUSH rule 2 x 393 [2,8]
+ CRUSH rule 2 x 394 [8,2]
+ CRUSH rule 2 x 395 [1,8]
+ CRUSH rule 2 x 396 [4,2,8]
+ CRUSH rule 2 x 397 [2,4,8]
+ CRUSH rule 2 x 398 [2,4,8]
+ CRUSH rule 2 x 399 [8,4,2]
+ CRUSH rule 2 x 400 [8,1,4]
+ CRUSH rule 2 x 401 [1,4,8]
+ CRUSH rule 2 x 402 [8,4,2]
+ CRUSH rule 2 x 403 [1,4,8]
+ CRUSH rule 2 x 404 [4,2,8]
+ CRUSH rule 2 x 405 [8,4,2]
+ CRUSH rule 2 x 406 [2,8]
+ CRUSH rule 2 x 407 [2,8,4]
+ CRUSH rule 2 x 408 [4,1,8]
+ CRUSH rule 2 x 409 [8,4,1]
+ CRUSH rule 2 x 410 [8,4,2]
+ CRUSH rule 2 x 411 [2,8,4]
+ CRUSH rule 2 x 412 [2,6]
+ CRUSH rule 2 x 413 [2,8]
+ CRUSH rule 2 x 414 [4,1,6]
+ CRUSH rule 2 x 415 [2,8]
+ CRUSH rule 2 x 416 [2,8]
+ CRUSH rule 2 x 417 [8,2]
+ CRUSH rule 2 x 418 [8,1,4]
+ CRUSH rule 2 x 419 [8,4,1]
+ CRUSH rule 2 x 420 [1,4,8]
+ CRUSH rule 2 x 421 [8,4,1]
+ CRUSH rule 2 x 422 [6,2]
+ CRUSH rule 2 x 423 [2,4,8]
+ CRUSH rule 2 x 424 [8,1]
+ CRUSH rule 2 x 425 [1,8]
+ CRUSH rule 2 x 426 [8,2]
+ CRUSH rule 2 x 427 [1,8]
+ CRUSH rule 2 x 428 [4,8,1]
+ CRUSH rule 2 x 429 [4,8,2]
+ CRUSH rule 2 x 430 [4,8,2]
+ CRUSH rule 2 x 431 [4,1,8]
+ CRUSH rule 2 x 432 [8,1]
+ CRUSH rule 2 x 433 [8,1]
+ CRUSH rule 2 x 434 [2,8]
+ CRUSH rule 2 x 435 [2,8]
+ CRUSH rule 2 x 436 [4,1,8]
+ CRUSH rule 2 x 437 [8,2]
+ CRUSH rule 2 x 438 [2,4,8]
+ CRUSH rule 2 x 439 [1,6]
+ CRUSH rule 2 x 440 [2,8]
+ CRUSH rule 2 x 441 [4,6,2]
+ CRUSH rule 2 x 442 [2,8]
+ CRUSH rule 2 x 443 [8,2,4]
+ CRUSH rule 2 x 444 [8,1]
+ CRUSH rule 2 x 445 [8,2]
+ CRUSH rule 2 x 446 [8,1]
+ CRUSH rule 2 x 447 [2,4,8]
+ CRUSH rule 2 x 448 [8,2,4]
+ CRUSH rule 2 x 449 [8,1]
+ CRUSH rule 2 x 450 [1,8]
+ CRUSH rule 2 x 451 [8,4,2]
+ CRUSH rule 2 x 452 [8,2]
+ CRUSH rule 2 x 453 [6,2]
+ CRUSH rule 2 x 454 [8,2]
+ CRUSH rule 2 x 455 [2,8,4]
+ CRUSH rule 2 x 456 [8,2]
+ CRUSH rule 2 x 457 [8,2]
+ CRUSH rule 2 x 458 [2,8]
+ CRUSH rule 2 x 459 [2,8,4]
+ CRUSH rule 2 x 460 [8,2]
+ CRUSH rule 2 x 461 [8,1]
+ CRUSH rule 2 x 462 [8,1]
+ CRUSH rule 2 x 463 [8,2]
+ CRUSH rule 2 x 464 [8,4,2]
+ CRUSH rule 2 x 465 [6,2,4]
+ CRUSH rule 2 x 466 [8,1]
+ CRUSH rule 2 x 467 [8,2]
+ CRUSH rule 2 x 468 [8,1,4]
+ CRUSH rule 2 x 469 [8,1]
+ CRUSH rule 2 x 470 [4,2,6]
+ CRUSH rule 2 x 471 [1,8]
+ CRUSH rule 2 x 472 [1,8]
+ CRUSH rule 2 x 473 [1,4,8]
+ CRUSH rule 2 x 474 [8,1]
+ CRUSH rule 2 x 475 [8,2,4]
+ CRUSH rule 2 x 476 [4,8,1]
+ CRUSH rule 2 x 477 [4,8,2]
+ CRUSH rule 2 x 478 [8,2,4]
+ CRUSH rule 2 x 479 [2,8]
+ CRUSH rule 2 x 480 [1,8]
+ CRUSH rule 2 x 481 [2,4,6]
+ CRUSH rule 2 x 482 [1,8]
+ CRUSH rule 2 x 483 [2,8,4]
+ CRUSH rule 2 x 484 [1,8]
+ CRUSH rule 2 x 485 [8,1]
+ CRUSH rule 2 x 486 [4,1,8]
+ CRUSH rule 2 x 487 [1,8]
+ CRUSH rule 2 x 488 [8,1]
+ CRUSH rule 2 x 489 [2,8]
+ CRUSH rule 2 x 490 [6,2]
+ CRUSH rule 2 x 491 [1,8]
+ CRUSH rule 2 x 492 [8,1]
+ CRUSH rule 2 x 493 [2,8]
+ CRUSH rule 2 x 494 [1,8]
+ CRUSH rule 2 x 495 [4,1,8]
+ CRUSH rule 2 x 496 [8,4,1]
+ CRUSH rule 2 x 497 [4,8,1]
+ CRUSH rule 2 x 498 [2,4,8]
+ CRUSH rule 2 x 499 [8,4,2]
+ CRUSH rule 2 x 500 [4,8,2]
+ CRUSH rule 2 x 501 [2,8]
+ CRUSH rule 2 x 502 [6,1]
+ CRUSH rule 2 x 503 [2,8]
+ CRUSH rule 2 x 504 [8,1]
+ CRUSH rule 2 x 505 [1,8]
+ CRUSH rule 2 x 506 [4,2,8]
+ CRUSH rule 2 x 507 [8,1,4]
+ CRUSH rule 2 x 508 [1,8]
+ CRUSH rule 2 x 509 [8,1]
+ CRUSH rule 2 x 510 [8,2]
+ CRUSH rule 2 x 511 [4,8,2]
+ CRUSH rule 2 x 512 [8,2]
+ CRUSH rule 2 x 513 [8,2]
+ CRUSH rule 2 x 514 [2,8]
+ CRUSH rule 2 x 515 [8,4,1]
+ CRUSH rule 2 x 516 [4,1,8]
+ CRUSH rule 2 x 517 [8,2]
+ CRUSH rule 2 x 518 [4,8,1]
+ CRUSH rule 2 x 519 [8,4,1]
+ CRUSH rule 2 x 520 [2,8,4]
+ CRUSH rule 2 x 521 [8,2,4]
+ CRUSH rule 2 x 522 [8,1,4]
+ CRUSH rule 2 x 523 [4,2,8]
+ CRUSH rule 2 x 524 [2,6]
+ CRUSH rule 2 x 525 [2,8]
+ CRUSH rule 2 x 526 [1,8]
+ CRUSH rule 2 x 527 [1,4,6]
+ CRUSH rule 2 x 528 [2,8]
+ CRUSH rule 2 x 529 [4,8,2]
+ CRUSH rule 2 x 530 [8,1]
+ CRUSH rule 2 x 531 [8,1,4]
+ CRUSH rule 2 x 532 [6,4,1]
+ CRUSH rule 2 x 533 [4,8,2]
+ CRUSH rule 2 x 534 [8,1]
+ CRUSH rule 2 x 535 [8,1]
+ CRUSH rule 2 x 536 [8,2]
+ CRUSH rule 2 x 537 [4,8,2]
+ CRUSH rule 2 x 538 [8,4,1]
+ CRUSH rule 2 x 539 [8,1]
+ CRUSH rule 2 x 540 [1,8,4]
+ CRUSH rule 2 x 541 [2,4,8]
+ CRUSH rule 2 x 542 [2,8]
+ CRUSH rule 2 x 543 [8,2]
+ CRUSH rule 2 x 544 [4,8,2]
+ CRUSH rule 2 x 545 [8,1]
+ CRUSH rule 2 x 546 [8,1,4]
+ CRUSH rule 2 x 547 [8,2,4]
+ CRUSH rule 2 x 548 [4,2,8]
+ CRUSH rule 2 x 549 [8,2]
+ CRUSH rule 2 x 550 [2,4,8]
+ CRUSH rule 2 x 551 [8,1]
+ CRUSH rule 2 x 552 [4,8,1]
+ CRUSH rule 2 x 553 [2,8]
+ CRUSH rule 2 x 554 [1,8]
+ CRUSH rule 2 x 555 [4,1,8]
+ CRUSH rule 2 x 556 [8,1]
+ CRUSH rule 2 x 557 [8,2]
+ CRUSH rule 2 x 558 [4,1,8]
+ CRUSH rule 2 x 559 [1,8]
+ CRUSH rule 2 x 560 [8,1]
+ CRUSH rule 2 x 561 [8,4,1]
+ CRUSH rule 2 x 562 [4,1,8]
+ CRUSH rule 2 x 563 [2,8]
+ CRUSH rule 2 x 564 [1,8]
+ CRUSH rule 2 x 565 [4,8,2]
+ CRUSH rule 2 x 566 [4,8,2]
+ CRUSH rule 2 x 567 [4,8,1]
+ CRUSH rule 2 x 568 [8,1]
+ CRUSH rule 2 x 569 [4,1,8]
+ CRUSH rule 2 x 570 [1,8]
+ CRUSH rule 2 x 571 [6,1]
+ CRUSH rule 2 x 572 [4,2,8]
+ CRUSH rule 2 x 573 [1,8]
+ CRUSH rule 2 x 574 [2,8]
+ CRUSH rule 2 x 575 [8,2,4]
+ CRUSH rule 2 x 576 [4,8,2]
+ CRUSH rule 2 x 577 [8,2]
+ CRUSH rule 2 x 578 [8,1]
+ CRUSH rule 2 x 579 [4,1,8]
+ CRUSH rule 2 x 580 [1,8]
+ CRUSH rule 2 x 581 [8,2,4]
+ CRUSH rule 2 x 582 [2,8,4]
+ CRUSH rule 2 x 583 [8,1]
+ CRUSH rule 2 x 584 [8,1,4]
+ CRUSH rule 2 x 585 [8,1,4]
+ CRUSH rule 2 x 586 [1,8,4]
+ CRUSH rule 2 x 587 [2,4,8]
+ CRUSH rule 2 x 588 [4,8,1]
+ CRUSH rule 2 x 589 [8,1]
+ CRUSH rule 2 x 590 [8,2]
+ CRUSH rule 2 x 591 [4,2,8]
+ CRUSH rule 2 x 592 [2,4,8]
+ CRUSH rule 2 x 593 [1,8,4]
+ CRUSH rule 2 x 594 [2,8]
+ CRUSH rule 2 x 595 [8,1]
+ CRUSH rule 2 x 596 [8,2]
+ CRUSH rule 2 x 597 [1,8]
+ CRUSH rule 2 x 598 [2,8]
+ CRUSH rule 2 x 599 [4,2,8]
+ CRUSH rule 2 x 600 [8,1,4]
+ CRUSH rule 2 x 601 [1,8,4]
+ CRUSH rule 2 x 602 [8,2]
+ CRUSH rule 2 x 603 [1,8]
+ CRUSH rule 2 x 604 [8,2]
+ CRUSH rule 2 x 605 [2,8]
+ CRUSH rule 2 x 606 [2,6,4]
+ CRUSH rule 2 x 607 [2,4,8]
+ CRUSH rule 2 x 608 [4,2,8]
+ CRUSH rule 2 x 609 [4,2,8]
+ CRUSH rule 2 x 610 [8,1]
+ CRUSH rule 2 x 611 [1,8]
+ CRUSH rule 2 x 612 [2,8]
+ CRUSH rule 2 x 613 [8,2,4]
+ CRUSH rule 2 x 614 [8,2,4]
+ CRUSH rule 2 x 615 [8,2,4]
+ CRUSH rule 2 x 616 [1,8]
+ CRUSH rule 2 x 617 [8,1,4]
+ CRUSH rule 2 x 618 [8,4,2]
+ CRUSH rule 2 x 619 [4,1,8]
+ CRUSH rule 2 x 620 [1,8]
+ CRUSH rule 2 x 621 [8,1]
+ CRUSH rule 2 x 622 [2,4,8]
+ CRUSH rule 2 x 623 [2,8]
+ CRUSH rule 2 x 624 [4,2,8]
+ CRUSH rule 2 x 625 [2,8]
+ CRUSH rule 2 x 626 [8,2,4]
+ CRUSH rule 2 x 627 [2,8,4]
+ CRUSH rule 2 x 628 [8,2]
+ CRUSH rule 2 x 629 [2,8,4]
+ CRUSH rule 2 x 630 [2,8]
+ CRUSH rule 2 x 631 [1,8,4]
+ CRUSH rule 2 x 632 [8,2]
+ CRUSH rule 2 x 633 [8,2]
+ CRUSH rule 2 x 634 [1,8]
+ CRUSH rule 2 x 635 [4,8,2]
+ CRUSH rule 2 x 636 [1,4,8]
+ CRUSH rule 2 x 637 [1,8]
+ CRUSH rule 2 x 638 [8,1,4]
+ CRUSH rule 2 x 639 [2,8]
+ CRUSH rule 2 x 640 [2,8]
+ CRUSH rule 2 x 641 [8,2]
+ CRUSH rule 2 x 642 [2,8]
+ CRUSH rule 2 x 643 [1,8]
+ CRUSH rule 2 x 644 [8,1]
+ CRUSH rule 2 x 645 [8,1]
+ CRUSH rule 2 x 646 [8,1,4]
+ CRUSH rule 2 x 647 [8,1]
+ CRUSH rule 2 x 648 [1,8]
+ CRUSH rule 2 x 649 [4,8,2]
+ CRUSH rule 2 x 650 [8,4,1]
+ CRUSH rule 2 x 651 [4,6,1]
+ CRUSH rule 2 x 652 [4,8,1]
+ CRUSH rule 2 x 653 [8,2]
+ CRUSH rule 2 x 654 [6,2]
+ CRUSH rule 2 x 655 [1,4,8]
+ CRUSH rule 2 x 656 [8,1]
+ CRUSH rule 2 x 657 [6,1]
+ CRUSH rule 2 x 658 [8,2]
+ CRUSH rule 2 x 659 [4,8,2]
+ CRUSH rule 2 x 660 [8,2]
+ CRUSH rule 2 x 661 [1,8]
+ CRUSH rule 2 x 662 [8,2]
+ CRUSH rule 2 x 663 [1,4,8]
+ CRUSH rule 2 x 664 [1,4,8]
+ CRUSH rule 2 x 665 [4,6,1]
+ CRUSH rule 2 x 666 [2,8]
+ CRUSH rule 2 x 667 [1,4,8]
+ CRUSH rule 2 x 668 [4,8,1]
+ CRUSH rule 2 x 669 [6,4,2]
+ CRUSH rule 2 x 670 [4,2,8]
+ CRUSH rule 2 x 671 [2,8]
+ CRUSH rule 2 x 672 [4,2,8]
+ CRUSH rule 2 x 673 [4,2,8]
+ CRUSH rule 2 x 674 [1,8]
+ CRUSH rule 2 x 675 [1,8,4]
+ CRUSH rule 2 x 676 [2,4,8]
+ CRUSH rule 2 x 677 [4,1,8]
+ CRUSH rule 2 x 678 [2,4,8]
+ CRUSH rule 2 x 679 [8,2]
+ CRUSH rule 2 x 680 [2,8]
+ CRUSH rule 2 x 681 [8,1]
+ CRUSH rule 2 x 682 [1,4,8]
+ CRUSH rule 2 x 683 [1,4,8]
+ CRUSH rule 2 x 684 [8,1,4]
+ CRUSH rule 2 x 685 [8,1,4]
+ CRUSH rule 2 x 686 [1,4,8]
+ CRUSH rule 2 x 687 [6,1]
+ CRUSH rule 2 x 688 [4,8,2]
+ CRUSH rule 2 x 689 [8,4,2]
+ CRUSH rule 2 x 690 [8,1,4]
+ CRUSH rule 2 x 691 [1,8]
+ CRUSH rule 2 x 692 [8,2]
+ CRUSH rule 2 x 693 [8,4,1]
+ CRUSH rule 2 x 694 [8,4,1]
+ CRUSH rule 2 x 695 [2,8,4]
+ CRUSH rule 2 x 696 [1,8]
+ CRUSH rule 2 x 697 [8,1,4]
+ CRUSH rule 2 x 698 [8,2,4]
+ CRUSH rule 2 x 699 [1,8,4]
+ CRUSH rule 2 x 700 [1,8]
+ CRUSH rule 2 x 701 [1,8]
+ CRUSH rule 2 x 702 [2,8]
+ CRUSH rule 2 x 703 [8,1]
+ CRUSH rule 2 x 704 [1,4,8]
+ CRUSH rule 2 x 705 [8,1,4]
+ CRUSH rule 2 x 706 [1,4,8]
+ CRUSH rule 2 x 707 [8,4,1]
+ CRUSH rule 2 x 708 [4,8,1]
+ CRUSH rule 2 x 709 [8,2]
+ CRUSH rule 2 x 710 [8,2]
+ CRUSH rule 2 x 711 [2,4,8]
+ CRUSH rule 2 x 712 [2,8]
+ CRUSH rule 2 x 713 [8,4,1]
+ CRUSH rule 2 x 714 [2,8]
+ CRUSH rule 2 x 715 [1,8]
+ CRUSH rule 2 x 716 [4,8,2]
+ CRUSH rule 2 x 717 [8,2,4]
+ CRUSH rule 2 x 718 [8,1]
+ CRUSH rule 2 x 719 [2,6,4]
+ CRUSH rule 2 x 720 [8,1,4]
+ CRUSH rule 2 x 721 [4,6,2]
+ CRUSH rule 2 x 722 [8,2]
+ CRUSH rule 2 x 723 [4,1,8]
+ CRUSH rule 2 x 724 [2,6]
+ CRUSH rule 2 x 725 [1,8]
+ CRUSH rule 2 x 726 [4,8,1]
+ CRUSH rule 2 x 727 [4,8,1]
+ CRUSH rule 2 x 728 [2,8,4]
+ CRUSH rule 2 x 729 [8,2]
+ CRUSH rule 2 x 730 [4,8,2]
+ CRUSH rule 2 x 731 [4,1,8]
+ CRUSH rule 2 x 732 [1,8]
+ CRUSH rule 2 x 733 [4,8,1]
+ CRUSH rule 2 x 734 [8,4,2]
+ CRUSH rule 2 x 735 [4,8,1]
+ CRUSH rule 2 x 736 [4,8,1]
+ CRUSH rule 2 x 737 [1,8,4]
+ CRUSH rule 2 x 738 [4,2,8]
+ CRUSH rule 2 x 739 [2,8]
+ CRUSH rule 2 x 740 [1,8,4]
+ CRUSH rule 2 x 741 [8,1]
+ CRUSH rule 2 x 742 [8,2]
+ CRUSH rule 2 x 743 [8,1,4]
+ CRUSH rule 2 x 744 [4,8,1]
+ CRUSH rule 2 x 745 [1,8]
+ CRUSH rule 2 x 746 [1,8]
+ CRUSH rule 2 x 747 [8,1]
+ CRUSH rule 2 x 748 [2,8,4]
+ CRUSH rule 2 x 749 [4,8,2]
+ CRUSH rule 2 x 750 [1,8,4]
+ CRUSH rule 2 x 751 [2,8]
+ CRUSH rule 2 x 752 [8,1]
+ CRUSH rule 2 x 753 [8,4,1]
+ CRUSH rule 2 x 754 [8,4,2]
+ CRUSH rule 2 x 755 [1,8,4]
+ CRUSH rule 2 x 756 [8,1]
+ CRUSH rule 2 x 757 [8,1,4]
+ CRUSH rule 2 x 758 [8,2]
+ CRUSH rule 2 x 759 [8,4,2]
+ CRUSH rule 2 x 760 [1,4,8]
+ CRUSH rule 2 x 761 [2,8]
+ CRUSH rule 2 x 762 [2,8]
+ CRUSH rule 2 x 763 [8,4,1]
+ CRUSH rule 2 x 764 [1,8]
+ CRUSH rule 2 x 765 [8,2]
+ CRUSH rule 2 x 766 [8,1]
+ CRUSH rule 2 x 767 [1,8,4]
+ CRUSH rule 2 x 768 [8,4,2]
+ CRUSH rule 2 x 769 [8,2,4]
+ CRUSH rule 2 x 770 [8,2,4]
+ CRUSH rule 2 x 771 [8,1,4]
+ CRUSH rule 2 x 772 [8,4,1]
+ CRUSH rule 2 x 773 [4,1,8]
+ CRUSH rule 2 x 774 [8,1]
+ CRUSH rule 2 x 775 [8,4,2]
+ CRUSH rule 2 x 776 [6,2]
+ CRUSH rule 2 x 777 [4,1,8]
+ CRUSH rule 2 x 778 [1,8,4]
+ CRUSH rule 2 x 779 [2,8]
+ CRUSH rule 2 x 780 [2,4,8]
+ CRUSH rule 2 x 781 [8,2]
+ CRUSH rule 2 x 782 [4,1,8]
+ CRUSH rule 2 x 783 [8,1,4]
+ CRUSH rule 2 x 784 [1,4,8]
+ CRUSH rule 2 x 785 [8,1,4]
+ CRUSH rule 2 x 786 [8,1]
+ CRUSH rule 2 x 787 [1,6,4]
+ CRUSH rule 2 x 788 [8,2,4]
+ CRUSH rule 2 x 789 [1,8]
+ CRUSH rule 2 x 790 [8,1]
+ CRUSH rule 2 x 791 [4,8,2]
+ CRUSH rule 2 x 792 [4,8,2]
+ CRUSH rule 2 x 793 [8,1,4]
+ CRUSH rule 2 x 794 [2,8,4]
+ CRUSH rule 2 x 795 [1,8]
+ CRUSH rule 2 x 796 [8,2]
+ CRUSH rule 2 x 797 [2,4,8]
+ CRUSH rule 2 x 798 [6,1]
+ CRUSH rule 2 x 799 [4,1,8]
+ CRUSH rule 2 x 800 [2,8]
+ CRUSH rule 2 x 801 [4,8,1]
+ CRUSH rule 2 x 802 [1,8,4]
+ CRUSH rule 2 x 803 [2,8]
+ CRUSH rule 2 x 804 [8,2]
+ CRUSH rule 2 x 805 [8,2]
+ CRUSH rule 2 x 806 [1,4,8]
+ CRUSH rule 2 x 807 [4,8,2]
+ CRUSH rule 2 x 808 [8,2]
+ CRUSH rule 2 x 809 [1,8]
+ CRUSH rule 2 x 810 [8,2]
+ CRUSH rule 2 x 811 [8,1]
+ CRUSH rule 2 x 812 [8,4,2]
+ CRUSH rule 2 x 813 [8,4,2]
+ CRUSH rule 2 x 814 [8,2]
+ CRUSH rule 2 x 815 [4,1,8]
+ CRUSH rule 2 x 816 [2,8]
+ CRUSH rule 2 x 817 [8,1]
+ CRUSH rule 2 x 818 [1,8]
+ CRUSH rule 2 x 819 [1,8]
+ CRUSH rule 2 x 820 [4,8,2]
+ CRUSH rule 2 x 821 [4,8,2]
+ CRUSH rule 2 x 822 [2,4,8]
+ CRUSH rule 2 x 823 [4,8,2]
+ CRUSH rule 2 x 824 [8,2]
+ CRUSH rule 2 x 825 [2,8,4]
+ CRUSH rule 2 x 826 [8,2,4]
+ CRUSH rule 2 x 827 [2,8,4]
+ CRUSH rule 2 x 828 [2,8]
+ CRUSH rule 2 x 829 [8,1]
+ CRUSH rule 2 x 830 [2,4,8]
+ CRUSH rule 2 x 831 [1,8]
+ CRUSH rule 2 x 832 [4,8,2]
+ CRUSH rule 2 x 833 [2,8]
+ CRUSH rule 2 x 834 [1,8]
+ CRUSH rule 2 x 835 [8,4,1]
+ CRUSH rule 2 x 836 [4,8,1]
+ CRUSH rule 2 x 837 [8,4,1]
+ CRUSH rule 2 x 838 [6,2,4]
+ CRUSH rule 2 x 839 [2,8]
+ CRUSH rule 2 x 840 [8,1]
+ CRUSH rule 2 x 841 [4,8,2]
+ CRUSH rule 2 x 842 [2,8]
+ CRUSH rule 2 x 843 [8,4,1]
+ CRUSH rule 2 x 844 [8,2]
+ CRUSH rule 2 x 845 [4,8,2]
+ CRUSH rule 2 x 846 [4,2,8]
+ CRUSH rule 2 x 847 [2,8]
+ CRUSH rule 2 x 848 [2,8,4]
+ CRUSH rule 2 x 849 [4,8,2]
+ CRUSH rule 2 x 850 [1,6]
+ CRUSH rule 2 x 851 [6,1]
+ CRUSH rule 2 x 852 [8,4,2]
+ CRUSH rule 2 x 853 [6,1]
+ CRUSH rule 2 x 854 [8,1]
+ CRUSH rule 2 x 855 [8,1]
+ CRUSH rule 2 x 856 [8,4,2]
+ CRUSH rule 2 x 857 [8,2]
+ CRUSH rule 2 x 858 [6,1]
+ CRUSH rule 2 x 859 [8,2,4]
+ CRUSH rule 2 x 860 [2,8]
+ CRUSH rule 2 x 861 [8,2]
+ CRUSH rule 2 x 862 [8,1]
+ CRUSH rule 2 x 863 [8,2]
+ CRUSH rule 2 x 864 [8,2]
+ CRUSH rule 2 x 865 [8,1]
+ CRUSH rule 2 x 866 [8,2]
+ CRUSH rule 2 x 867 [8,2]
+ CRUSH rule 2 x 868 [8,1]
+ CRUSH rule 2 x 869 [8,4,2]
+ CRUSH rule 2 x 870 [2,8]
+ CRUSH rule 2 x 871 [1,8]
+ CRUSH rule 2 x 872 [1,8]
+ CRUSH rule 2 x 873 [4,8,2]
+ CRUSH rule 2 x 874 [2,6]
+ CRUSH rule 2 x 875 [2,8,4]
+ CRUSH rule 2 x 876 [4,8,1]
+ CRUSH rule 2 x 877 [8,4,2]
+ CRUSH rule 2 x 878 [2,8]
+ CRUSH rule 2 x 879 [8,1]
+ CRUSH rule 2 x 880 [1,8]
+ CRUSH rule 2 x 881 [4,8,1]
+ CRUSH rule 2 x 882 [1,8]
+ CRUSH rule 2 x 883 [2,4,8]
+ CRUSH rule 2 x 884 [8,2,4]
+ CRUSH rule 2 x 885 [4,1,8]
+ CRUSH rule 2 x 886 [8,2]
+ CRUSH rule 2 x 887 [8,4,1]
+ CRUSH rule 2 x 888 [8,2]
+ CRUSH rule 2 x 889 [2,6]
+ CRUSH rule 2 x 890 [8,2,4]
+ CRUSH rule 2 x 891 [1,8]
+ CRUSH rule 2 x 892 [8,2,4]
+ CRUSH rule 2 x 893 [2,6]
+ CRUSH rule 2 x 894 [8,4,2]
+ CRUSH rule 2 x 895 [4,1,8]
+ CRUSH rule 2 x 896 [1,8]
+ CRUSH rule 2 x 897 [2,8]
+ CRUSH rule 2 x 898 [1,4,8]
+ CRUSH rule 2 x 899 [1,8]
+ CRUSH rule 2 x 900 [4,1,8]
+ CRUSH rule 2 x 901 [2,8]
+ CRUSH rule 2 x 902 [8,4,1]
+ CRUSH rule 2 x 903 [8,2]
+ CRUSH rule 2 x 904 [8,2]
+ CRUSH rule 2 x 905 [8,2]
+ CRUSH rule 2 x 906 [1,8]
+ CRUSH rule 2 x 907 [8,1]
+ CRUSH rule 2 x 908 [8,1]
+ CRUSH rule 2 x 909 [2,8]
+ CRUSH rule 2 x 910 [8,2]
+ CRUSH rule 2 x 911 [8,1]
+ CRUSH rule 2 x 912 [1,8]
+ CRUSH rule 2 x 913 [8,2,4]
+ CRUSH rule 2 x 914 [6,4,2]
+ CRUSH rule 2 x 915 [8,2]
+ CRUSH rule 2 x 916 [4,1,8]
+ CRUSH rule 2 x 917 [1,4,8]
+ CRUSH rule 2 x 918 [8,2]
+ CRUSH rule 2 x 919 [8,2]
+ CRUSH rule 2 x 920 [8,1]
+ CRUSH rule 2 x 921 [1,8]
+ CRUSH rule 2 x 922 [8,4,2]
+ CRUSH rule 2 x 923 [4,8,2]
+ CRUSH rule 2 x 924 [8,1]
+ CRUSH rule 2 x 925 [4,8,2]
+ CRUSH rule 2 x 926 [2,8]
+ CRUSH rule 2 x 927 [1,8,4]
+ CRUSH rule 2 x 928 [8,1]
+ CRUSH rule 2 x 929 [4,1,8]
+ CRUSH rule 2 x 930 [2,8]
+ CRUSH rule 2 x 931 [2,8]
+ CRUSH rule 2 x 932 [4,1,8]
+ CRUSH rule 2 x 933 [8,4,1]
+ CRUSH rule 2 x 934 [8,2]
+ CRUSH rule 2 x 935 [8,2]
+ CRUSH rule 2 x 936 [1,8]
+ CRUSH rule 2 x 937 [4,8,2]
+ CRUSH rule 2 x 938 [8,4,2]
+ CRUSH rule 2 x 939 [2,8,4]
+ CRUSH rule 2 x 940 [8,1]
+ CRUSH rule 2 x 941 [2,8]
+ CRUSH rule 2 x 942 [1,8]
+ CRUSH rule 2 x 943 [8,2]
+ CRUSH rule 2 x 944 [8,2]
+ CRUSH rule 2 x 945 [8,2,4]
+ CRUSH rule 2 x 946 [2,8,4]
+ CRUSH rule 2 x 947 [8,2]
+ CRUSH rule 2 x 948 [8,1]
+ CRUSH rule 2 x 949 [6,1]
+ CRUSH rule 2 x 950 [8,1]
+ CRUSH rule 2 x 951 [8,1]
+ CRUSH rule 2 x 952 [2,8,4]
+ CRUSH rule 2 x 953 [1,4,8]
+ CRUSH rule 2 x 954 [2,8]
+ CRUSH rule 2 x 955 [8,1,4]
+ CRUSH rule 2 x 956 [1,8,4]
+ CRUSH rule 2 x 957 [8,1,4]
+ CRUSH rule 2 x 958 [8,4,1]
+ CRUSH rule 2 x 959 [4,2,8]
+ CRUSH rule 2 x 960 [6,1]
+ CRUSH rule 2 x 961 [1,8]
+ CRUSH rule 2 x 962 [8,4,2]
+ CRUSH rule 2 x 963 [2,4,6]
+ CRUSH rule 2 x 964 [2,8]
+ CRUSH rule 2 x 965 [8,2]
+ CRUSH rule 2 x 966 [4,8,1]
+ CRUSH rule 2 x 967 [8,4,2]
+ CRUSH rule 2 x 968 [8,2]
+ CRUSH rule 2 x 969 [8,2,4]
+ CRUSH rule 2 x 970 [2,8,4]
+ CRUSH rule 2 x 971 [1,8]
+ CRUSH rule 2 x 972 [1,8]
+ CRUSH rule 2 x 973 [1,8]
+ CRUSH rule 2 x 974 [4,1,8]
+ CRUSH rule 2 x 975 [4,8,1]
+ CRUSH rule 2 x 976 [4,8,2]
+ CRUSH rule 2 x 977 [8,4,2]
+ CRUSH rule 2 x 978 [8,2,4]
+ CRUSH rule 2 x 979 [8,1,4]
+ CRUSH rule 2 x 980 [8,2,4]
+ CRUSH rule 2 x 981 [8,2]
+ CRUSH rule 2 x 982 [1,8]
+ CRUSH rule 2 x 983 [4,8,2]
+ CRUSH rule 2 x 984 [2,8]
+ CRUSH rule 2 x 985 [2,4,8]
+ CRUSH rule 2 x 986 [8,4,1]
+ CRUSH rule 2 x 987 [2,8]
+ CRUSH rule 2 x 988 [1,4,6]
+ CRUSH rule 2 x 989 [1,8]
+ CRUSH rule 2 x 990 [1,8,4]
+ CRUSH rule 2 x 991 [1,4,8]
+ CRUSH rule 2 x 992 [8,1,4]
+ CRUSH rule 2 x 993 [2,8,4]
+ CRUSH rule 2 x 994 [4,8,2]
+ CRUSH rule 2 x 995 [8,1,4]
+ CRUSH rule 2 x 996 [8,4,1]
+ CRUSH rule 2 x 997 [8,4,1]
+ CRUSH rule 2 x 998 [8,1,4]
+ CRUSH rule 2 x 999 [1,8,4]
+ CRUSH rule 2 x 1000 [8,4,2]
+ CRUSH rule 2 x 1001 [2,8]
+ CRUSH rule 2 x 1002 [1,8]
+ CRUSH rule 2 x 1003 [2,8]
+ CRUSH rule 2 x 1004 [8,1,4]
+ CRUSH rule 2 x 1005 [8,1]
+ CRUSH rule 2 x 1006 [1,8,4]
+ CRUSH rule 2 x 1007 [1,4,8]
+ CRUSH rule 2 x 1008 [1,8]
+ CRUSH rule 2 x 1009 [6,4,1]
+ CRUSH rule 2 x 1010 [1,8]
+ CRUSH rule 2 x 1011 [4,2,8]
+ CRUSH rule 2 x 1012 [1,8]
+ CRUSH rule 2 x 1013 [2,8]
+ CRUSH rule 2 x 1014 [2,8,4]
+ CRUSH rule 2 x 1015 [8,1]
+ CRUSH rule 2 x 1016 [2,4,8]
+ CRUSH rule 2 x 1017 [6,2,4]
+ CRUSH rule 2 x 1018 [4,1,8]
+ CRUSH rule 2 x 1019 [4,8,2]
+ CRUSH rule 2 x 1020 [1,8]
+ CRUSH rule 2 x 1021 [2,8]
+ CRUSH rule 2 x 1022 [1,8,4]
+ CRUSH rule 2 x 1023 [4,2,8]
+ rule 2 (chooseleaf) num_rep 3 result size == 2:\t501/1024 (esc)
+ rule 2 (chooseleaf) num_rep 3 result size == 3:\t523/1024 (esc)
+ rule 3 (choose-set), x = 0..1023, numrep = 2..3
+ CRUSH rule 3 x 0 [2,4]
+ CRUSH rule 3 x 1 [2,8]
+ CRUSH rule 3 x 2 [1]
+ CRUSH rule 3 x 3 [8,1]
+ CRUSH rule 3 x 4 [4,1]
+ CRUSH rule 3 x 5 [8,1]
+ CRUSH rule 3 x 6 [2,8]
+ CRUSH rule 3 x 7 [4,8]
+ CRUSH rule 3 x 8 [4,8]
+ CRUSH rule 3 x 9 [2,4]
+ CRUSH rule 3 x 10 [2,8]
+ CRUSH rule 3 x 11 [2,8]
+ CRUSH rule 3 x 12 [2]
+ CRUSH rule 3 x 13 [4,8]
+ CRUSH rule 3 x 14 [8,1]
+ CRUSH rule 3 x 15 [8,1]
+ CRUSH rule 3 x 16 [8]
+ CRUSH rule 3 x 17 [4,1]
+ CRUSH rule 3 x 18 [1]
+ CRUSH rule 3 x 19 [8,4]
+ CRUSH rule 3 x 20 [2]
+ CRUSH rule 3 x 21 [8]
+ CRUSH rule 3 x 22 [8]
+ CRUSH rule 3 x 23 [4,8]
+ CRUSH rule 3 x 24 [1,8]
+ CRUSH rule 3 x 25 [4,8]
+ CRUSH rule 3 x 26 [2,8]
+ CRUSH rule 3 x 27 [4,1]
+ CRUSH rule 3 x 28 [8,2]
+ CRUSH rule 3 x 29 [8,4]
+ CRUSH rule 3 x 30 [4,8]
+ CRUSH rule 3 x 31 [8,2]
+ CRUSH rule 3 x 32 [6]
+ CRUSH rule 3 x 33 [2,8]
+ CRUSH rule 3 x 34 [2]
+ CRUSH rule 3 x 35 [1,8]
+ CRUSH rule 3 x 36 [8]
+ CRUSH rule 3 x 37 [1]
+ CRUSH rule 3 x 38 [4,8]
+ CRUSH rule 3 x 39 [8]
+ CRUSH rule 3 x 40 [8,2]
+ CRUSH rule 3 x 41 [2,8]
+ CRUSH rule 3 x 42 [8]
+ CRUSH rule 3 x 43 [1]
+ CRUSH rule 3 x 44 [1,8]
+ CRUSH rule 3 x 45 [8,2]
+ CRUSH rule 3 x 46 [2]
+ CRUSH rule 3 x 47 [4,1]
+ CRUSH rule 3 x 48 [8]
+ CRUSH rule 3 x 49 [8]
+ CRUSH rule 3 x 50 [4,1]
+ CRUSH rule 3 x 51 [8]
+ CRUSH rule 3 x 52 [8,2]
+ CRUSH rule 3 x 53 [4,8]
+ CRUSH rule 3 x 54 [8,4]
+ CRUSH rule 3 x 55 [8,2]
+ CRUSH rule 3 x 56 [8,4]
+ CRUSH rule 3 x 57 [8]
+ CRUSH rule 3 x 58 [1,8]
+ CRUSH rule 3 x 59 [2]
+ CRUSH rule 3 x 60 [4,1]
+ CRUSH rule 3 x 61 [4,8]
+ CRUSH rule 3 x 62 [8,1]
+ CRUSH rule 3 x 63 [8]
+ CRUSH rule 3 x 64 [4,1]
+ CRUSH rule 3 x 65 [8,4]
+ CRUSH rule 3 x 66 [4,8]
+ CRUSH rule 3 x 67 [4,2]
+ CRUSH rule 3 x 68 [1]
+ CRUSH rule 3 x 69 [2]
+ CRUSH rule 3 x 70 [8,2]
+ CRUSH rule 3 x 71 [2,8]
+ CRUSH rule 3 x 72 [8,2]
+ CRUSH rule 3 x 73 [2,8]
+ CRUSH rule 3 x 74 [1,8]
+ CRUSH rule 3 x 75 [4,2]
+ CRUSH rule 3 x 76 [4,2]
+ CRUSH rule 3 x 77 [8,2]
+ CRUSH rule 3 x 78 [1]
+ CRUSH rule 3 x 79 [4,1]
+ CRUSH rule 3 x 80 [2,4]
+ CRUSH rule 3 x 81 [2]
+ CRUSH rule 3 x 82 [6,2]
+ CRUSH rule 3 x 83 [2,8]
+ CRUSH rule 3 x 84 [8,2]
+ CRUSH rule 3 x 85 [4,8]
+ CRUSH rule 3 x 86 [2,8]
+ CRUSH rule 3 x 87 [2,8]
+ CRUSH rule 3 x 88 [1,8]
+ CRUSH rule 3 x 89 [1]
+ CRUSH rule 3 x 90 [8,4]
+ CRUSH rule 3 x 91 [4,8]
+ CRUSH rule 3 x 92 [1,8]
+ CRUSH rule 3 x 93 [8,4]
+ CRUSH rule 3 x 94 [1]
+ CRUSH rule 3 x 95 [8]
+ CRUSH rule 3 x 96 [8]
+ CRUSH rule 3 x 97 [8]
+ CRUSH rule 3 x 98 [2,8]
+ CRUSH rule 3 x 99 [2,8]
+ CRUSH rule 3 x 100 [1,8]
+ CRUSH rule 3 x 101 [8]
+ CRUSH rule 3 x 102 [2]
+ CRUSH rule 3 x 103 [8]
+ CRUSH rule 3 x 104 [8,4]
+ CRUSH rule 3 x 105 [2,4]
+ CRUSH rule 3 x 106 [1,8]
+ CRUSH rule 3 x 107 [1]
+ CRUSH rule 3 x 108 [8,1]
+ CRUSH rule 3 x 109 [1,4]
+ CRUSH rule 3 x 110 [4,2]
+ CRUSH rule 3 x 111 [2,4]
+ CRUSH rule 3 x 112 [2,8]
+ CRUSH rule 3 x 113 [8,1]
+ CRUSH rule 3 x 114 [8,4]
+ CRUSH rule 3 x 115 [8,2]
+ CRUSH rule 3 x 116 [1,8]
+ CRUSH rule 3 x 117 [6]
+ CRUSH rule 3 x 118 [2]
+ CRUSH rule 3 x 119 [8]
+ CRUSH rule 3 x 120 [2,4]
+ CRUSH rule 3 x 121 [2,8]
+ CRUSH rule 3 x 122 [8]
+ CRUSH rule 3 x 123 [2]
+ CRUSH rule 3 x 124 [2]
+ CRUSH rule 3 x 125 [1,8]
+ CRUSH rule 3 x 126 [1]
+ CRUSH rule 3 x 127 [4,8]
+ CRUSH rule 3 x 128 [8]
+ CRUSH rule 3 x 129 [2,4]
+ CRUSH rule 3 x 130 [4,8]
+ CRUSH rule 3 x 131 [1,4]
+ CRUSH rule 3 x 132 [1]
+ CRUSH rule 3 x 133 [8]
+ CRUSH rule 3 x 134 [1,8]
+ CRUSH rule 3 x 135 [4,8]
+ CRUSH rule 3 x 136 [2,4]
+ CRUSH rule 3 x 137 [8,4]
+ CRUSH rule 3 x 138 [8,4]
+ CRUSH rule 3 x 139 [4,2]
+ CRUSH rule 3 x 140 [1,8]
+ CRUSH rule 3 x 141 [8,2]
+ CRUSH rule 3 x 142 [4,2]
+ CRUSH rule 3 x 143 [4,8]
+ CRUSH rule 3 x 144 [8,2]
+ CRUSH rule 3 x 145 [8]
+ CRUSH rule 3 x 146 [2,8]
+ CRUSH rule 3 x 147 [2,8]
+ CRUSH rule 3 x 148 [4,1]
+ CRUSH rule 3 x 149 [4,8]
+ CRUSH rule 3 x 150 [1,8]
+ CRUSH rule 3 x 151 [8]
+ CRUSH rule 3 x 152 [8]
+ CRUSH rule 3 x 153 [8,4]
+ CRUSH rule 3 x 154 [4,2]
+ CRUSH rule 3 x 155 [4,8]
+ CRUSH rule 3 x 156 [4,2]
+ CRUSH rule 3 x 157 [1]
+ CRUSH rule 3 x 158 [2,8]
+ CRUSH rule 3 x 159 [8,2]
+ CRUSH rule 3 x 160 [2,8]
+ CRUSH rule 3 x 161 [1,4]
+ CRUSH rule 3 x 162 [1,8]
+ CRUSH rule 3 x 163 [4,8]
+ CRUSH rule 3 x 164 [8,2]
+ CRUSH rule 3 x 165 [8,2]
+ CRUSH rule 3 x 166 [2]
+ CRUSH rule 3 x 167 [1,8]
+ CRUSH rule 3 x 168 [4,2]
+ CRUSH rule 3 x 169 [2,8]
+ CRUSH rule 3 x 170 [1]
+ CRUSH rule 3 x 171 [8,4]
+ CRUSH rule 3 x 172 [1,8]
+ CRUSH rule 3 x 173 [8,4]
+ CRUSH rule 3 x 174 [1]
+ CRUSH rule 3 x 175 [8,1]
+ CRUSH rule 3 x 176 [2]
+ CRUSH rule 3 x 177 [1]
+ CRUSH rule 3 x 178 [4,2]
+ CRUSH rule 3 x 179 [1]
+ CRUSH rule 3 x 180 [8]
+ CRUSH rule 3 x 181 [8,2]
+ CRUSH rule 3 x 182 [8]
+ CRUSH rule 3 x 183 [8,4]
+ CRUSH rule 3 x 184 [4,8]
+ CRUSH rule 3 x 185 [8,1]
+ CRUSH rule 3 x 186 [2,4]
+ CRUSH rule 3 x 187 [1,8]
+ CRUSH rule 3 x 188 [1,8]
+ CRUSH rule 3 x 189 [1,8]
+ CRUSH rule 3 x 190 [1]
+ CRUSH rule 3 x 191 [8,1]
+ CRUSH rule 3 x 192 [4,2]
+ CRUSH rule 3 x 193 [4,2]
+ CRUSH rule 3 x 194 [1]
+ CRUSH rule 3 x 195 [8,4]
+ CRUSH rule 3 x 196 [8,1]
+ CRUSH rule 3 x 197 [8,4]
+ CRUSH rule 3 x 198 [2]
+ CRUSH rule 3 x 199 [1,4]
+ CRUSH rule 3 x 200 [1]
+ CRUSH rule 3 x 201 [8,1]
+ CRUSH rule 3 x 202 [8]
+ CRUSH rule 3 x 203 [8]
+ CRUSH rule 3 x 204 [2,4]
+ CRUSH rule 3 x 205 [1,8]
+ CRUSH rule 3 x 206 [1,8]
+ CRUSH rule 3 x 207 [2]
+ CRUSH rule 3 x 208 [8,2]
+ CRUSH rule 3 x 209 [1,8]
+ CRUSH rule 3 x 210 [1,4]
+ CRUSH rule 3 x 211 [4,2]
+ CRUSH rule 3 x 212 [8]
+ CRUSH rule 3 x 213 [8,4]
+ CRUSH rule 3 x 214 [8]
+ CRUSH rule 3 x 215 [8,1]
+ CRUSH rule 3 x 216 [2]
+ CRUSH rule 3 x 217 [1,8]
+ CRUSH rule 3 x 218 [2,8]
+ CRUSH rule 3 x 219 [8]
+ CRUSH rule 3 x 220 [4,8]
+ CRUSH rule 3 x 221 [8]
+ CRUSH rule 3 x 222 [8]
+ CRUSH rule 3 x 223 [1]
+ CRUSH rule 3 x 224 [1,4]
+ CRUSH rule 3 x 225 [8,2]
+ CRUSH rule 3 x 226 [8,2]
+ CRUSH rule 3 x 227 [4,1]
+ CRUSH rule 3 x 228 [8]
+ CRUSH rule 3 x 229 [4,8]
+ CRUSH rule 3 x 230 [4,8]
+ CRUSH rule 3 x 231 [4,8]
+ CRUSH rule 3 x 232 [2,8]
+ CRUSH rule 3 x 233 [8]
+ CRUSH rule 3 x 234 [1]
+ CRUSH rule 3 x 235 [4,8]
+ CRUSH rule 3 x 236 [2]
+ CRUSH rule 3 x 237 [4,8]
+ CRUSH rule 3 x 238 [2]
+ CRUSH rule 3 x 239 [8]
+ CRUSH rule 3 x 240 [4,8]
+ CRUSH rule 3 x 241 [1]
+ CRUSH rule 3 x 242 [2]
+ CRUSH rule 3 x 243 [8]
+ CRUSH rule 3 x 244 [4,8]
+ CRUSH rule 3 x 245 [8,2]
+ CRUSH rule 3 x 246 [1]
+ CRUSH rule 3 x 247 [8,1]
+ CRUSH rule 3 x 248 [8,1]
+ CRUSH rule 3 x 249 [2]
+ CRUSH rule 3 x 250 [2,4]
+ CRUSH rule 3 x 251 [2]
+ CRUSH rule 3 x 252 [4,8]
+ CRUSH rule 3 x 253 [2]
+ CRUSH rule 3 x 254 [4,2]
+ CRUSH rule 3 x 255 [1,8]
+ CRUSH rule 3 x 256 [4,8]
+ CRUSH rule 3 x 257 [2,6]
+ CRUSH rule 3 x 258 [4,2]
+ CRUSH rule 3 x 259 [6]
+ CRUSH rule 3 x 260 [8]
+ CRUSH rule 3 x 261 [8]
+ CRUSH rule 3 x 262 [8]
+ CRUSH rule 3 x 263 [8,1]
+ CRUSH rule 3 x 264 [8]
+ CRUSH rule 3 x 265 [8]
+ CRUSH rule 3 x 266 [8,2]
+ CRUSH rule 3 x 267 [2]
+ CRUSH rule 3 x 268 [1,8]
+ CRUSH rule 3 x 269 [1,8]
+ CRUSH rule 3 x 270 [4,1]
+ CRUSH rule 3 x 271 [8,4]
+ CRUSH rule 3 x 272 [2,8]
+ CRUSH rule 3 x 273 [4,2]
+ CRUSH rule 3 x 274 [8,4]
+ CRUSH rule 3 x 275 [4,8]
+ CRUSH rule 3 x 276 [8,1]
+ CRUSH rule 3 x 277 [8]
+ CRUSH rule 3 x 278 [8,2]
+ CRUSH rule 3 x 279 [8,4]
+ CRUSH rule 3 x 280 [2,8]
+ CRUSH rule 3 x 281 [8,1]
+ CRUSH rule 3 x 282 [2]
+ CRUSH rule 3 x 283 [8,1]
+ CRUSH rule 3 x 284 [8]
+ CRUSH rule 3 x 285 [4,8]
+ CRUSH rule 3 x 286 [2,8]
+ CRUSH rule 3 x 287 [1]
+ CRUSH rule 3 x 288 [8,1]
+ CRUSH rule 3 x 289 [4,8]
+ CRUSH rule 3 x 290 [1,4]
+ CRUSH rule 3 x 291 [1,4]
+ CRUSH rule 3 x 292 [8,1]
+ CRUSH rule 3 x 293 [8,2]
+ CRUSH rule 3 x 294 [8,4]
+ CRUSH rule 3 x 295 [4,8]
+ CRUSH rule 3 x 296 [4,1]
+ CRUSH rule 3 x 297 [8,2]
+ CRUSH rule 3 x 298 [1]
+ CRUSH rule 3 x 299 [2,8]
+ CRUSH rule 3 x 300 [8]
+ CRUSH rule 3 x 301 [1,8]
+ CRUSH rule 3 x 302 [1]
+ CRUSH rule 3 x 303 [8,4]
+ CRUSH rule 3 x 304 [2,8]
+ CRUSH rule 3 x 305 [8]
+ CRUSH rule 3 x 306 [1,8]
+ CRUSH rule 3 x 307 [2,8]
+ CRUSH rule 3 x 308 [2,8]
+ CRUSH rule 3 x 309 [8]
+ CRUSH rule 3 x 310 [4,1]
+ CRUSH rule 3 x 311 [4,8]
+ CRUSH rule 3 x 312 [2,8]
+ CRUSH rule 3 x 313 [4,1]
+ CRUSH rule 3 x 314 [2]
+ CRUSH rule 3 x 315 [2]
+ CRUSH rule 3 x 316 [8]
+ CRUSH rule 3 x 317 [2,8]
+ CRUSH rule 3 x 318 [8,1]
+ CRUSH rule 3 x 319 [2]
+ CRUSH rule 3 x 320 [8]
+ CRUSH rule 3 x 321 [1]
+ CRUSH rule 3 x 322 [2,6]
+ CRUSH rule 3 x 323 [4,8]
+ CRUSH rule 3 x 324 [8,2]
+ CRUSH rule 3 x 325 [4,8]
+ CRUSH rule 3 x 326 [1]
+ CRUSH rule 3 x 327 [1,8]
+ CRUSH rule 3 x 328 [8,4]
+ CRUSH rule 3 x 329 [4,8]
+ CRUSH rule 3 x 330 [4,8]
+ CRUSH rule 3 x 331 [2,8]
+ CRUSH rule 3 x 332 [2]
+ CRUSH rule 3 x 333 [8]
+ CRUSH rule 3 x 334 [8]
+ CRUSH rule 3 x 335 [8,2]
+ CRUSH rule 3 x 336 [4,8]
+ CRUSH rule 3 x 337 [8,2]
+ CRUSH rule 3 x 338 [8]
+ CRUSH rule 3 x 339 [8]
+ CRUSH rule 3 x 340 [2,8]
+ CRUSH rule 3 x 341 [4,1]
+ CRUSH rule 3 x 342 [2,8]
+ CRUSH rule 3 x 343 [8]
+ CRUSH rule 3 x 344 [6,1]
+ CRUSH rule 3 x 345 [8]
+ CRUSH rule 3 x 346 [8,2]
+ CRUSH rule 3 x 347 [4,1]
+ CRUSH rule 3 x 348 [8,2]
+ CRUSH rule 3 x 349 [1,8]
+ CRUSH rule 3 x 350 [8]
+ CRUSH rule 3 x 351 [8]
+ CRUSH rule 3 x 352 [1,8]
+ CRUSH rule 3 x 353 [8]
+ CRUSH rule 3 x 354 [1]
+ CRUSH rule 3 x 355 [8]
+ CRUSH rule 3 x 356 [4,1]
+ CRUSH rule 3 x 357 [8,1]
+ CRUSH rule 3 x 358 [2,8]
+ CRUSH rule 3 x 359 [6,1]
+ CRUSH rule 3 x 360 [2]
+ CRUSH rule 3 x 361 [8,4]
+ CRUSH rule 3 x 362 [4,1]
+ CRUSH rule 3 x 363 [4,2]
+ CRUSH rule 3 x 364 [2]
+ CRUSH rule 3 x 365 [8]
+ CRUSH rule 3 x 366 [8,2]
+ CRUSH rule 3 x 367 [4,2]
+ CRUSH rule 3 x 368 [8,4]
+ CRUSH rule 3 x 369 [8]
+ CRUSH rule 3 x 370 [8,2]
+ CRUSH rule 3 x 371 [1,4]
+ CRUSH rule 3 x 372 [1]
+ CRUSH rule 3 x 373 [1,8]
+ CRUSH rule 3 x 374 [8]
+ CRUSH rule 3 x 375 [8,4]
+ CRUSH rule 3 x 376 [8,1]
+ CRUSH rule 3 x 377 [1,4]
+ CRUSH rule 3 x 378 [1,8]
+ CRUSH rule 3 x 379 [8]
+ CRUSH rule 3 x 380 [2]
+ CRUSH rule 3 x 381 [1,4]
+ CRUSH rule 3 x 382 [1,4]
+ CRUSH rule 3 x 383 [4,8]
+ CRUSH rule 3 x 384 [8,2]
+ CRUSH rule 3 x 385 [8]
+ CRUSH rule 3 x 386 [1]
+ CRUSH rule 3 x 387 [1,4]
+ CRUSH rule 3 x 388 [2]
+ CRUSH rule 3 x 389 [1,4]
+ CRUSH rule 3 x 390 [4,8]
+ CRUSH rule 3 x 391 [4,8]
+ CRUSH rule 3 x 392 [1,8]
+ CRUSH rule 3 x 393 [2]
+ CRUSH rule 3 x 394 [8]
+ CRUSH rule 3 x 395 [1]
+ CRUSH rule 3 x 396 [4,2]
+ CRUSH rule 3 x 397 [2,4]
+ CRUSH rule 3 x 398 [2,4]
+ CRUSH rule 3 x 399 [8,4]
+ CRUSH rule 3 x 400 [8,1]
+ CRUSH rule 3 x 401 [1,4]
+ CRUSH rule 3 x 402 [8,4]
+ CRUSH rule 3 x 403 [1,4]
+ CRUSH rule 3 x 404 [4,2]
+ CRUSH rule 3 x 405 [8,4]
+ CRUSH rule 3 x 406 [2,8]
+ CRUSH rule 3 x 407 [2,8]
+ CRUSH rule 3 x 408 [4,1]
+ CRUSH rule 3 x 409 [8,4]
+ CRUSH rule 3 x 410 [8,4]
+ CRUSH rule 3 x 411 [2,8]
+ CRUSH rule 3 x 412 [2]
+ CRUSH rule 3 x 413 [2]
+ CRUSH rule 3 x 414 [4,1]
+ CRUSH rule 3 x 415 [2,8]
+ CRUSH rule 3 x 416 [2,8]
+ CRUSH rule 3 x 417 [8,2]
+ CRUSH rule 3 x 418 [8,1]
+ CRUSH rule 3 x 419 [8,4]
+ CRUSH rule 3 x 420 [1,4]
+ CRUSH rule 3 x 421 [8,4]
+ CRUSH rule 3 x 422 [6]
+ CRUSH rule 3 x 423 [2,4]
+ CRUSH rule 3 x 424 [8]
+ CRUSH rule 3 x 425 [1]
+ CRUSH rule 3 x 426 [8,2]
+ CRUSH rule 3 x 427 [1,8]
+ CRUSH rule 3 x 428 [4,8]
+ CRUSH rule 3 x 429 [4,8]
+ CRUSH rule 3 x 430 [4,8]
+ CRUSH rule 3 x 431 [4,2]
+ CRUSH rule 3 x 432 [8,1]
+ CRUSH rule 3 x 433 [8]
+ CRUSH rule 3 x 434 [2]
+ CRUSH rule 3 x 435 [2]
+ CRUSH rule 3 x 436 [4,1]
+ CRUSH rule 3 x 437 [8]
+ CRUSH rule 3 x 438 [2,4]
+ CRUSH rule 3 x 439 [1]
+ CRUSH rule 3 x 440 [2,8]
+ CRUSH rule 3 x 441 [4,8]
+ CRUSH rule 3 x 442 [2]
+ CRUSH rule 3 x 443 [8,1]
+ CRUSH rule 3 x 444 [8,1]
+ CRUSH rule 3 x 445 [8]
+ CRUSH rule 3 x 446 [1]
+ CRUSH rule 3 x 447 [2,4]
+ CRUSH rule 3 x 448 [8,2]
+ CRUSH rule 3 x 449 [8]
+ CRUSH rule 3 x 450 [1]
+ CRUSH rule 3 x 451 [8,4]
+ CRUSH rule 3 x 452 [8]
+ CRUSH rule 3 x 453 [6]
+ CRUSH rule 3 x 454 [8]
+ CRUSH rule 3 x 455 [2,8]
+ CRUSH rule 3 x 456 [8,2]
+ CRUSH rule 3 x 457 [8,1]
+ CRUSH rule 3 x 458 [2,8]
+ CRUSH rule 3 x 459 [2,8]
+ CRUSH rule 3 x 460 [8]
+ CRUSH rule 3 x 461 [8]
+ CRUSH rule 3 x 462 [8,1]
+ CRUSH rule 3 x 463 [8,2]
+ CRUSH rule 3 x 464 [8,4]
+ CRUSH rule 3 x 465 [6,1]
+ CRUSH rule 3 x 466 [8]
+ CRUSH rule 3 x 467 [8]
+ CRUSH rule 3 x 468 [8,2]
+ CRUSH rule 3 x 469 [8,2]
+ CRUSH rule 3 x 470 [4,1]
+ CRUSH rule 3 x 471 [1,8]
+ CRUSH rule 3 x 472 [1]
+ CRUSH rule 3 x 473 [1,4]
+ CRUSH rule 3 x 474 [8,1]
+ CRUSH rule 3 x 475 [8,2]
+ CRUSH rule 3 x 476 [4,8]
+ CRUSH rule 3 x 477 [4,8]
+ CRUSH rule 3 x 478 [8,1]
+ CRUSH rule 3 x 479 [2]
+ CRUSH rule 3 x 480 [1,8]
+ CRUSH rule 3 x 481 [2,4]
+ CRUSH rule 3 x 482 [8]
+ CRUSH rule 3 x 483 [2,8]
+ CRUSH rule 3 x 484 [1,8]
+ CRUSH rule 3 x 485 [8]
+ CRUSH rule 3 x 486 [4,1]
+ CRUSH rule 3 x 487 [1]
+ CRUSH rule 3 x 488 [8]
+ CRUSH rule 3 x 489 [2,8]
+ CRUSH rule 3 x 490 [6]
+ CRUSH rule 3 x 491 [1,8]
+ CRUSH rule 3 x 492 [8]
+ CRUSH rule 3 x 493 [2,8]
+ CRUSH rule 3 x 494 [1,8]
+ CRUSH rule 3 x 495 [4,1]
+ CRUSH rule 3 x 496 [8,4]
+ CRUSH rule 3 x 497 [4,8]
+ CRUSH rule 3 x 498 [2,4]
+ CRUSH rule 3 x 499 [8,4]
+ CRUSH rule 3 x 500 [4,8]
+ CRUSH rule 3 x 501 [2,8]
+ CRUSH rule 3 x 502 [6,1]
+ CRUSH rule 3 x 503 [2]
+ CRUSH rule 3 x 504 [8]
+ CRUSH rule 3 x 505 [1,8]
+ CRUSH rule 3 x 506 [4,1]
+ CRUSH rule 3 x 507 [8,2]
+ CRUSH rule 3 x 508 [1]
+ CRUSH rule 3 x 509 [8]
+ CRUSH rule 3 x 510 [8,2]
+ CRUSH rule 3 x 511 [4,8]
+ CRUSH rule 3 x 512 [8,2]
+ CRUSH rule 3 x 513 [8,2]
+ CRUSH rule 3 x 514 [8]
+ CRUSH rule 3 x 515 [8,4]
+ CRUSH rule 3 x 516 [4,1]
+ CRUSH rule 3 x 517 [8,2]
+ CRUSH rule 3 x 518 [4,8]
+ CRUSH rule 3 x 519 [8,4]
+ CRUSH rule 3 x 520 [2,8]
+ CRUSH rule 3 x 521 [8,2]
+ CRUSH rule 3 x 522 [8,1]
+ CRUSH rule 3 x 523 [4,1]
+ CRUSH rule 3 x 524 [2]
+ CRUSH rule 3 x 525 [2]
+ CRUSH rule 3 x 526 [1]
+ CRUSH rule 3 x 527 [1,4]
+ CRUSH rule 3 x 528 [2]
+ CRUSH rule 3 x 529 [4,8]
+ CRUSH rule 3 x 530 [8]
+ CRUSH rule 3 x 531 [8,1]
+ CRUSH rule 3 x 532 [6,4]
+ CRUSH rule 3 x 533 [4,8]
+ CRUSH rule 3 x 534 [8]
+ CRUSH rule 3 x 535 [8,1]
+ CRUSH rule 3 x 536 [8,2]
+ CRUSH rule 3 x 537 [4,8]
+ CRUSH rule 3 x 538 [8,4]
+ CRUSH rule 3 x 539 [8]
+ CRUSH rule 3 x 540 [1,8]
+ CRUSH rule 3 x 541 [2,4]
+ CRUSH rule 3 x 542 [2]
+ CRUSH rule 3 x 543 [8,2]
+ CRUSH rule 3 x 544 [4,8]
+ CRUSH rule 3 x 545 [8]
+ CRUSH rule 3 x 546 [8,2]
+ CRUSH rule 3 x 547 [8,1]
+ CRUSH rule 3 x 548 [4,1]
+ CRUSH rule 3 x 549 [8]
+ CRUSH rule 3 x 550 [2,4]
+ CRUSH rule 3 x 551 [8]
+ CRUSH rule 3 x 552 [4,8]
+ CRUSH rule 3 x 553 [2]
+ CRUSH rule 3 x 554 [1,8]
+ CRUSH rule 3 x 555 [4,2]
+ CRUSH rule 3 x 556 [8]
+ CRUSH rule 3 x 557 [8]
+ CRUSH rule 3 x 558 [4,2]
+ CRUSH rule 3 x 559 [1]
+ CRUSH rule 3 x 560 [8]
+ CRUSH rule 3 x 561 [8,4]
+ CRUSH rule 3 x 562 [4,1]
+ CRUSH rule 3 x 563 [2,8]
+ CRUSH rule 3 x 564 [1]
+ CRUSH rule 3 x 565 [4,8]
+ CRUSH rule 3 x 566 [4,8]
+ CRUSH rule 3 x 567 [4,8]
+ CRUSH rule 3 x 568 [8]
+ CRUSH rule 3 x 569 [4,1]
+ CRUSH rule 3 x 570 [1]
+ CRUSH rule 3 x 571 [6]
+ CRUSH rule 3 x 572 [4,2]
+ CRUSH rule 3 x 573 [1]
+ CRUSH rule 3 x 574 [2]
+ CRUSH rule 3 x 575 [8,1]
+ CRUSH rule 3 x 576 [4,8]
+ CRUSH rule 3 x 577 [8,2]
+ CRUSH rule 3 x 578 [8,1]
+ CRUSH rule 3 x 579 [4,2]
+ CRUSH rule 3 x 580 [1]
+ CRUSH rule 3 x 581 [8,1]
+ CRUSH rule 3 x 582 [2,8]
+ CRUSH rule 3 x 583 [8,2]
+ CRUSH rule 3 x 584 [8,1]
+ CRUSH rule 3 x 585 [8,1]
+ CRUSH rule 3 x 586 [1,8]
+ CRUSH rule 3 x 587 [2,4]
+ CRUSH rule 3 x 588 [4,8]
+ CRUSH rule 3 x 589 [8,1]
+ CRUSH rule 3 x 590 [8,2]
+ CRUSH rule 3 x 591 [4,2]
+ CRUSH rule 3 x 592 [2,4]
+ CRUSH rule 3 x 593 [1,8]
+ CRUSH rule 3 x 594 [2,8]
+ CRUSH rule 3 x 595 [8,1]
+ CRUSH rule 3 x 596 [2]
+ CRUSH rule 3 x 597 [1]
+ CRUSH rule 3 x 598 [2]
+ CRUSH rule 3 x 599 [4,1]
+ CRUSH rule 3 x 600 [8,1]
+ CRUSH rule 3 x 601 [1,8]
+ CRUSH rule 3 x 602 [8]
+ CRUSH rule 3 x 603 [1]
+ CRUSH rule 3 x 604 [8]
+ CRUSH rule 3 x 605 [2]
+ CRUSH rule 3 x 606 [2,6]
+ CRUSH rule 3 x 607 [2,4]
+ CRUSH rule 3 x 608 [4,1]
+ CRUSH rule 3 x 609 [4,2]
+ CRUSH rule 3 x 610 [8]
+ CRUSH rule 3 x 611 [1,8]
+ CRUSH rule 3 x 612 [2,8]
+ CRUSH rule 3 x 613 [8,1]
+ CRUSH rule 3 x 614 [8,2]
+ CRUSH rule 3 x 615 [8,2]
+ CRUSH rule 3 x 616 [1,8]
+ CRUSH rule 3 x 617 [8,2]
+ CRUSH rule 3 x 618 [8,4]
+ CRUSH rule 3 x 619 [4,1]
+ CRUSH rule 3 x 620 [1]
+ CRUSH rule 3 x 621 [8]
+ CRUSH rule 3 x 622 [2,4]
+ CRUSH rule 3 x 623 [2,8]
+ CRUSH rule 3 x 624 [4,2]
+ CRUSH rule 3 x 625 [2]
+ CRUSH rule 3 x 626 [8,2]
+ CRUSH rule 3 x 627 [2,8]
+ CRUSH rule 3 x 628 [8,1]
+ CRUSH rule 3 x 629 [2,8]
+ CRUSH rule 3 x 630 [2,8]
+ CRUSH rule 3 x 631 [1,8]
+ CRUSH rule 3 x 632 [8,2]
+ CRUSH rule 3 x 633 [8]
+ CRUSH rule 3 x 634 [1]
+ CRUSH rule 3 x 635 [4,8]
+ CRUSH rule 3 x 636 [1,4]
+ CRUSH rule 3 x 637 [1]
+ CRUSH rule 3 x 638 [8,2]
+ CRUSH rule 3 x 639 [2]
+ CRUSH rule 3 x 640 [2]
+ CRUSH rule 3 x 641 [8,2]
+ CRUSH rule 3 x 642 [2,8]
+ CRUSH rule 3 x 643 [1]
+ CRUSH rule 3 x 644 [8,1]
+ CRUSH rule 3 x 645 [8]
+ CRUSH rule 3 x 646 [8,1]
+ CRUSH rule 3 x 647 [8,1]
+ CRUSH rule 3 x 648 [1,8]
+ CRUSH rule 3 x 649 [4,8]
+ CRUSH rule 3 x 650 [8,4]
+ CRUSH rule 3 x 651 [4,6]
+ CRUSH rule 3 x 652 [4,8]
+ CRUSH rule 3 x 653 [8]
+ CRUSH rule 3 x 654 [6]
+ CRUSH rule 3 x 655 [1,4]
+ CRUSH rule 3 x 656 [8]
+ CRUSH rule 3 x 657 [6,1]
+ CRUSH rule 3 x 658 [8]
+ CRUSH rule 3 x 659 [4,8]
+ CRUSH rule 3 x 660 [8]
+ CRUSH rule 3 x 661 [1,8]
+ CRUSH rule 3 x 662 [2]
+ CRUSH rule 3 x 663 [1,4]
+ CRUSH rule 3 x 664 [1,4]
+ CRUSH rule 3 x 665 [4,6]
+ CRUSH rule 3 x 666 [2,8]
+ CRUSH rule 3 x 667 [1,4]
+ CRUSH rule 3 x 668 [4,8]
+ CRUSH rule 3 x 669 [6,4]
+ CRUSH rule 3 x 670 [4,1]
+ CRUSH rule 3 x 671 [2,8]
+ CRUSH rule 3 x 672 [4,2]
+ CRUSH rule 3 x 673 [4,2]
+ CRUSH rule 3 x 674 [1]
+ CRUSH rule 3 x 675 [1,8]
+ CRUSH rule 3 x 676 [2,4]
+ CRUSH rule 3 x 677 [4,1]
+ CRUSH rule 3 x 678 [2,4]
+ CRUSH rule 3 x 679 [8,2]
+ CRUSH rule 3 x 680 [2]
+ CRUSH rule 3 x 681 [8]
+ CRUSH rule 3 x 682 [1,4]
+ CRUSH rule 3 x 683 [1,4]
+ CRUSH rule 3 x 684 [8,2]
+ CRUSH rule 3 x 685 [8,2]
+ CRUSH rule 3 x 686 [1,4]
+ CRUSH rule 3 x 687 [6]
+ CRUSH rule 3 x 688 [4,8]
+ CRUSH rule 3 x 689 [8,4]
+ CRUSH rule 3 x 690 [8,1]
+ CRUSH rule 3 x 691 [1]
+ CRUSH rule 3 x 692 [8,1]
+ CRUSH rule 3 x 693 [8,4]
+ CRUSH rule 3 x 694 [8,4]
+ CRUSH rule 3 x 695 [2,8]
+ CRUSH rule 3 x 696 [1]
+ CRUSH rule 3 x 697 [8,1]
+ CRUSH rule 3 x 698 [8,2]
+ CRUSH rule 3 x 699 [1,8]
+ CRUSH rule 3 x 700 [1]
+ CRUSH rule 3 x 701 [1]
+ CRUSH rule 3 x 702 [2]
+ CRUSH rule 3 x 703 [8]
+ CRUSH rule 3 x 704 [1,4]
+ CRUSH rule 3 x 705 [8,1]
+ CRUSH rule 3 x 706 [1,4]
+ CRUSH rule 3 x 707 [8,4]
+ CRUSH rule 3 x 708 [4,8]
+ CRUSH rule 3 x 709 [8]
+ CRUSH rule 3 x 710 [8]
+ CRUSH rule 3 x 711 [2,4]
+ CRUSH rule 3 x 712 [2]
+ CRUSH rule 3 x 713 [8,4]
+ CRUSH rule 3 x 714 [2]
+ CRUSH rule 3 x 715 [1]
+ CRUSH rule 3 x 716 [4,8]
+ CRUSH rule 3 x 717 [8,2]
+ CRUSH rule 3 x 718 [8]
+ CRUSH rule 3 x 719 [2,6]
+ CRUSH rule 3 x 720 [8,1]
+ CRUSH rule 3 x 721 [4,6]
+ CRUSH rule 3 x 722 [8]
+ CRUSH rule 3 x 723 [4,2]
+ CRUSH rule 3 x 724 [2,6]
+ CRUSH rule 3 x 725 [1]
+ CRUSH rule 3 x 726 [4,8]
+ CRUSH rule 3 x 727 [4,8]
+ CRUSH rule 3 x 728 [2,8]
+ CRUSH rule 3 x 729 [8]
+ CRUSH rule 3 x 730 [4,8]
+ CRUSH rule 3 x 731 [4,1]
+ CRUSH rule 3 x 732 [1]
+ CRUSH rule 3 x 733 [4,8]
+ CRUSH rule 3 x 734 [8,4]
+ CRUSH rule 3 x 735 [4,8]
+ CRUSH rule 3 x 736 [4,8]
+ CRUSH rule 3 x 737 [1,8]
+ CRUSH rule 3 x 738 [4,1]
+ CRUSH rule 3 x 739 [2,8]
+ CRUSH rule 3 x 740 [1,8]
+ CRUSH rule 3 x 741 [8,2]
+ CRUSH rule 3 x 742 [8,2]
+ CRUSH rule 3 x 743 [8,1]
+ CRUSH rule 3 x 744 [4,8]
+ CRUSH rule 3 x 745 [1]
+ CRUSH rule 3 x 746 [1]
+ CRUSH rule 3 x 747 [8,2]
+ CRUSH rule 3 x 748 [2,8]
+ CRUSH rule 3 x 749 [4,8]
+ CRUSH rule 3 x 750 [1,8]
+ CRUSH rule 3 x 751 [2,8]
+ CRUSH rule 3 x 752 [8,1]
+ CRUSH rule 3 x 753 [8,4]
+ CRUSH rule 3 x 754 [8,4]
+ CRUSH rule 3 x 755 [1,8]
+ CRUSH rule 3 x 756 [8]
+ CRUSH rule 3 x 757 [8,1]
+ CRUSH rule 3 x 758 [8,1]
+ CRUSH rule 3 x 759 [8,4]
+ CRUSH rule 3 x 760 [1,4]
+ CRUSH rule 3 x 761 [2]
+ CRUSH rule 3 x 762 [2,8]
+ CRUSH rule 3 x 763 [8,4]
+ CRUSH rule 3 x 764 [1,8]
+ CRUSH rule 3 x 765 [8]
+ CRUSH rule 3 x 766 [8]
+ CRUSH rule 3 x 767 [1,8]
+ CRUSH rule 3 x 768 [8,4]
+ CRUSH rule 3 x 769 [8,2]
+ CRUSH rule 3 x 770 [8,1]
+ CRUSH rule 3 x 771 [8,2]
+ CRUSH rule 3 x 772 [8,4]
+ CRUSH rule 3 x 773 [4,2]
+ CRUSH rule 3 x 774 [8]
+ CRUSH rule 3 x 775 [8,4]
+ CRUSH rule 3 x 776 [6,2]
+ CRUSH rule 3 x 777 [4,1]
+ CRUSH rule 3 x 778 [1,6]
+ CRUSH rule 3 x 779 [2,8]
+ CRUSH rule 3 x 780 [2,4]
+ CRUSH rule 3 x 781 [8]
+ CRUSH rule 3 x 782 [4,2]
+ CRUSH rule 3 x 783 [8,1]
+ CRUSH rule 3 x 784 [1,4]
+ CRUSH rule 3 x 785 [8,1]
+ CRUSH rule 3 x 786 [8]
+ CRUSH rule 3 x 787 [1,8]
+ CRUSH rule 3 x 788 [8,2]
+ CRUSH rule 3 x 789 [1]
+ CRUSH rule 3 x 790 [8]
+ CRUSH rule 3 x 791 [4,8]
+ CRUSH rule 3 x 792 [4,8]
+ CRUSH rule 3 x 793 [8,2]
+ CRUSH rule 3 x 794 [2,8]
+ CRUSH rule 3 x 795 [1]
+ CRUSH rule 3 x 796 [8]
+ CRUSH rule 3 x 797 [2,4]
+ CRUSH rule 3 x 798 [6,1]
+ CRUSH rule 3 x 799 [4,2]
+ CRUSH rule 3 x 800 [2]
+ CRUSH rule 3 x 801 [4,8]
+ CRUSH rule 3 x 802 [1,8]
+ CRUSH rule 3 x 803 [2]
+ CRUSH rule 3 x 804 [8,2]
+ CRUSH rule 3 x 805 [8]
+ CRUSH rule 3 x 806 [1,4]
+ CRUSH rule 3 x 807 [4,8]
+ CRUSH rule 3 x 808 [8]
+ CRUSH rule 3 x 809 [1]
+ CRUSH rule 3 x 810 [8]
+ CRUSH rule 3 x 811 [8]
+ CRUSH rule 3 x 812 [8,4]
+ CRUSH rule 3 x 813 [8,4]
+ CRUSH rule 3 x 814 [8]
+ CRUSH rule 3 x 815 [4,1]
+ CRUSH rule 3 x 816 [2,8]
+ CRUSH rule 3 x 817 [8]
+ CRUSH rule 3 x 818 [1]
+ CRUSH rule 3 x 819 [1]
+ CRUSH rule 3 x 820 [4,8]
+ CRUSH rule 3 x 821 [4,8]
+ CRUSH rule 3 x 822 [2,4]
+ CRUSH rule 3 x 823 [4,8]
+ CRUSH rule 3 x 824 [8]
+ CRUSH rule 3 x 825 [2,8]
+ CRUSH rule 3 x 826 [8,1]
+ CRUSH rule 3 x 827 [2,6]
+ CRUSH rule 3 x 828 [2]
+ CRUSH rule 3 x 829 [8]
+ CRUSH rule 3 x 830 [2,4]
+ CRUSH rule 3 x 831 [1,8]
+ CRUSH rule 3 x 832 [4,8]
+ CRUSH rule 3 x 833 [2,8]
+ CRUSH rule 3 x 834 [1]
+ CRUSH rule 3 x 835 [8,4]
+ CRUSH rule 3 x 836 [4,8]
+ CRUSH rule 3 x 837 [8,4]
+ CRUSH rule 3 x 838 [6,2]
+ CRUSH rule 3 x 839 [2]
+ CRUSH rule 3 x 840 [8]
+ CRUSH rule 3 x 841 [4,8]
+ CRUSH rule 3 x 842 [2]
+ CRUSH rule 3 x 843 [8,4]
+ CRUSH rule 3 x 844 [8]
+ CRUSH rule 3 x 845 [4,8]
+ CRUSH rule 3 x 846 [4,2]
+ CRUSH rule 3 x 847 [2,8]
+ CRUSH rule 3 x 848 [2,8]
+ CRUSH rule 3 x 849 [4,8]
+ CRUSH rule 3 x 850 [1]
+ CRUSH rule 3 x 851 [6]
+ CRUSH rule 3 x 852 [8,4]
+ CRUSH rule 3 x 853 [6,2]
+ CRUSH rule 3 x 854 [8,2]
+ CRUSH rule 3 x 855 [8]
+ CRUSH rule 3 x 856 [8,4]
+ CRUSH rule 3 x 857 [8]
+ CRUSH rule 3 x 858 [6]
+ CRUSH rule 3 x 859 [8,2]
+ CRUSH rule 3 x 860 [2]
+ CRUSH rule 3 x 861 [8]
+ CRUSH rule 3 x 862 [8,1]
+ CRUSH rule 3 x 863 [8,1]
+ CRUSH rule 3 x 864 [8]
+ CRUSH rule 3 x 865 [8,1]
+ CRUSH rule 3 x 866 [8]
+ CRUSH rule 3 x 867 [8]
+ CRUSH rule 3 x 868 [8]
+ CRUSH rule 3 x 869 [8,4]
+ CRUSH rule 3 x 870 [2]
+ CRUSH rule 3 x 871 [1]
+ CRUSH rule 3 x 872 [1]
+ CRUSH rule 3 x 873 [4,8]
+ CRUSH rule 3 x 874 [2,6]
+ CRUSH rule 3 x 875 [2,8]
+ CRUSH rule 3 x 876 [4,8]
+ CRUSH rule 3 x 877 [8,4]
+ CRUSH rule 3 x 878 [2]
+ CRUSH rule 3 x 879 [8]
+ CRUSH rule 3 x 880 [1]
+ CRUSH rule 3 x 881 [4,8]
+ CRUSH rule 3 x 882 [1]
+ CRUSH rule 3 x 883 [2,4]
+ CRUSH rule 3 x 884 [8,1]
+ CRUSH rule 3 x 885 [4,1]
+ CRUSH rule 3 x 886 [8]
+ CRUSH rule 3 x 887 [8,4]
+ CRUSH rule 3 x 888 [8,1]
+ CRUSH rule 3 x 889 [2,8]
+ CRUSH rule 3 x 890 [8,1]
+ CRUSH rule 3 x 891 [1,8]
+ CRUSH rule 3 x 892 [8,2]
+ CRUSH rule 3 x 893 [2]
+ CRUSH rule 3 x 894 [8,4]
+ CRUSH rule 3 x 895 [4,1]
+ CRUSH rule 3 x 896 [1,8]
+ CRUSH rule 3 x 897 [2]
+ CRUSH rule 3 x 898 [1,4]
+ CRUSH rule 3 x 899 [1,8]
+ CRUSH rule 3 x 900 [4,1]
+ CRUSH rule 3 x 901 [2]
+ CRUSH rule 3 x 902 [8,4]
+ CRUSH rule 3 x 903 [8]
+ CRUSH rule 3 x 904 [8]
+ CRUSH rule 3 x 905 [8,2]
+ CRUSH rule 3 x 906 [1,8]
+ CRUSH rule 3 x 907 [8,2]
+ CRUSH rule 3 x 908 [8]
+ CRUSH rule 3 x 909 [2]
+ CRUSH rule 3 x 910 [8]
+ CRUSH rule 3 x 911 [8]
+ CRUSH rule 3 x 912 [1,8]
+ CRUSH rule 3 x 913 [8,1]
+ CRUSH rule 3 x 914 [6,4]
+ CRUSH rule 3 x 915 [8,2]
+ CRUSH rule 3 x 916 [4,1]
+ CRUSH rule 3 x 917 [1,4]
+ CRUSH rule 3 x 918 [8,2]
+ CRUSH rule 3 x 919 [8,2]
+ CRUSH rule 3 x 920 [8]
+ CRUSH rule 3 x 921 [1]
+ CRUSH rule 3 x 922 [8,4]
+ CRUSH rule 3 x 923 [4,8]
+ CRUSH rule 3 x 924 [1]
+ CRUSH rule 3 x 925 [4,8]
+ CRUSH rule 3 x 926 [2]
+ CRUSH rule 3 x 927 [1,8]
+ CRUSH rule 3 x 928 [8,1]
+ CRUSH rule 3 x 929 [4,2]
+ CRUSH rule 3 x 930 [2]
+ CRUSH rule 3 x 931 [2]
+ CRUSH rule 3 x 932 [4,2]
+ CRUSH rule 3 x 933 [8,4]
+ CRUSH rule 3 x 934 [8]
+ CRUSH rule 3 x 935 [8]
+ CRUSH rule 3 x 936 [1,8]
+ CRUSH rule 3 x 937 [4,8]
+ CRUSH rule 3 x 938 [8,4]
+ CRUSH rule 3 x 939 [2,8]
+ CRUSH rule 3 x 940 [8]
+ CRUSH rule 3 x 941 [2]
+ CRUSH rule 3 x 942 [1,8]
+ CRUSH rule 3 x 943 [8,2]
+ CRUSH rule 3 x 944 [8]
+ CRUSH rule 3 x 945 [8,2]
+ CRUSH rule 3 x 946 [2,8]
+ CRUSH rule 3 x 947 [2]
+ CRUSH rule 3 x 948 [8]
+ CRUSH rule 3 x 949 [6,2]
+ CRUSH rule 3 x 950 [8]
+ CRUSH rule 3 x 951 [8]
+ CRUSH rule 3 x 952 [2,8]
+ CRUSH rule 3 x 953 [1,4]
+ CRUSH rule 3 x 954 [2]
+ CRUSH rule 3 x 955 [8,1]
+ CRUSH rule 3 x 956 [1,8]
+ CRUSH rule 3 x 957 [8,1]
+ CRUSH rule 3 x 958 [8,4]
+ CRUSH rule 3 x 959 [4,1]
+ CRUSH rule 3 x 960 [6]
+ CRUSH rule 3 x 961 [1]
+ CRUSH rule 3 x 962 [8,4]
+ CRUSH rule 3 x 963 [2,4]
+ CRUSH rule 3 x 964 [2]
+ CRUSH rule 3 x 965 [8]
+ CRUSH rule 3 x 966 [4,8]
+ CRUSH rule 3 x 967 [8,4]
+ CRUSH rule 3 x 968 [8,2]
+ CRUSH rule 3 x 969 [8,2]
+ CRUSH rule 3 x 970 [2,8]
+ CRUSH rule 3 x 971 [1,8]
+ CRUSH rule 3 x 972 [1,8]
+ CRUSH rule 3 x 973 [1,8]
+ CRUSH rule 3 x 974 [4,1]
+ CRUSH rule 3 x 975 [4,8]
+ CRUSH rule 3 x 976 [4,8]
+ CRUSH rule 3 x 977 [8,4]
+ CRUSH rule 3 x 978 [8,2]
+ CRUSH rule 3 x 979 [8,2]
+ CRUSH rule 3 x 980 [8,2]
+ CRUSH rule 3 x 981 [8]
+ CRUSH rule 3 x 982 [1]
+ CRUSH rule 3 x 983 [4,8]
+ CRUSH rule 3 x 984 [2,8]
+ CRUSH rule 3 x 985 [2,4]
+ CRUSH rule 3 x 986 [8,4]
+ CRUSH rule 3 x 987 [2]
+ CRUSH rule 3 x 988 [1,4]
+ CRUSH rule 3 x 989 [1,8]
+ CRUSH rule 3 x 990 [1,8]
+ CRUSH rule 3 x 991 [1,4]
+ CRUSH rule 3 x 992 [8,1]
+ CRUSH rule 3 x 993 [2,8]
+ CRUSH rule 3 x 994 [4,8]
+ CRUSH rule 3 x 995 [8,1]
+ CRUSH rule 3 x 996 [8,4]
+ CRUSH rule 3 x 997 [8,4]
+ CRUSH rule 3 x 998 [8,1]
+ CRUSH rule 3 x 999 [1,8]
+ CRUSH rule 3 x 1000 [8,4]
+ CRUSH rule 3 x 1001 [2]
+ CRUSH rule 3 x 1002 [1]
+ CRUSH rule 3 x 1003 [2,8]
+ CRUSH rule 3 x 1004 [8,1]
+ CRUSH rule 3 x 1005 [8,1]
+ CRUSH rule 3 x 1006 [1,8]
+ CRUSH rule 3 x 1007 [1,4]
+ CRUSH rule 3 x 1008 [1,8]
+ CRUSH rule 3 x 1009 [6,4]
+ CRUSH rule 3 x 1010 [1]
+ CRUSH rule 3 x 1011 [4,2]
+ CRUSH rule 3 x 1012 [1]
+ CRUSH rule 3 x 1013 [2]
+ CRUSH rule 3 x 1014 [2,8]
+ CRUSH rule 3 x 1015 [8]
+ CRUSH rule 3 x 1016 [2,4]
+ CRUSH rule 3 x 1017 [6,1]
+ CRUSH rule 3 x 1018 [4,1]
+ CRUSH rule 3 x 1019 [4,8]
+ CRUSH rule 3 x 1020 [1]
+ CRUSH rule 3 x 1021 [2]
+ CRUSH rule 3 x 1022 [1,8]
+ CRUSH rule 3 x 1023 [4,2]
+ rule 3 (choose-set) num_rep 2 result size == 1:\t325/1024 (esc)
+ rule 3 (choose-set) num_rep 2 result size == 2:\t699/1024 (esc)
+ CRUSH rule 3 x 0 [2,4,8]
+ CRUSH rule 3 x 1 [2,8,4]
+ CRUSH rule 3 x 2 [1,8]
+ CRUSH rule 3 x 3 [8,1]
+ CRUSH rule 3 x 4 [4,1,8]
+ CRUSH rule 3 x 5 [8,1]
+ CRUSH rule 3 x 6 [2,8,4]
+ CRUSH rule 3 x 7 [4,8,1]
+ CRUSH rule 3 x 8 [4,8,2]
+ CRUSH rule 3 x 9 [2,4,8]
+ CRUSH rule 3 x 10 [2,8]
+ CRUSH rule 3 x 11 [2,8]
+ CRUSH rule 3 x 12 [2,8]
+ CRUSH rule 3 x 13 [4,8,1]
+ CRUSH rule 3 x 14 [8,1]
+ CRUSH rule 3 x 15 [8,1]
+ CRUSH rule 3 x 16 [8,1]
+ CRUSH rule 3 x 17 [4,1,8]
+ CRUSH rule 3 x 18 [1,6]
+ CRUSH rule 3 x 19 [8,4,2]
+ CRUSH rule 3 x 20 [2,8]
+ CRUSH rule 3 x 21 [8,2]
+ CRUSH rule 3 x 22 [8,1]
+ CRUSH rule 3 x 23 [4,8,2]
+ CRUSH rule 3 x 24 [1,8,4]
+ CRUSH rule 3 x 25 [4,8,1]
+ CRUSH rule 3 x 26 [2,8,4]
+ CRUSH rule 3 x 27 [4,1,8]
+ CRUSH rule 3 x 28 [8,2]
+ CRUSH rule 3 x 29 [8,4,2]
+ CRUSH rule 3 x 30 [4,8,1]
+ CRUSH rule 3 x 31 [8,2]
+ CRUSH rule 3 x 32 [6,2]
+ CRUSH rule 3 x 33 [2,8]
+ CRUSH rule 3 x 34 [2,8]
+ CRUSH rule 3 x 35 [1,8,4]
+ CRUSH rule 3 x 36 [8,1]
+ CRUSH rule 3 x 37 [1,8]
+ CRUSH rule 3 x 38 [4,8,1]
+ CRUSH rule 3 x 39 [8,2]
+ CRUSH rule 3 x 40 [8,2,4]
+ CRUSH rule 3 x 41 [2,8,4]
+ CRUSH rule 3 x 42 [8,2]
+ CRUSH rule 3 x 43 [1,8]
+ CRUSH rule 3 x 44 [1,8,4]
+ CRUSH rule 3 x 45 [8,2,4]
+ CRUSH rule 3 x 46 [2,8]
+ CRUSH rule 3 x 47 [4,1,8]
+ CRUSH rule 3 x 48 [8,1]
+ CRUSH rule 3 x 49 [8,2]
+ CRUSH rule 3 x 50 [4,1,8]
+ CRUSH rule 3 x 51 [8,1]
+ CRUSH rule 3 x 52 [8,2,4]
+ CRUSH rule 3 x 53 [4,8,2]
+ CRUSH rule 3 x 54 [8,4,2]
+ CRUSH rule 3 x 55 [8,2]
+ CRUSH rule 3 x 56 [8,4,1]
+ CRUSH rule 3 x 57 [8,1]
+ CRUSH rule 3 x 58 [1,8]
+ CRUSH rule 3 x 59 [2,8]
+ CRUSH rule 3 x 60 [4,1,8]
+ CRUSH rule 3 x 61 [4,8,2]
+ CRUSH rule 3 x 62 [8,1]
+ CRUSH rule 3 x 63 [8,2]
+ CRUSH rule 3 x 64 [4,1,8]
+ CRUSH rule 3 x 65 [8,4,2]
+ CRUSH rule 3 x 66 [4,8,1]
+ CRUSH rule 3 x 67 [4,2,8]
+ CRUSH rule 3 x 68 [1,8]
+ CRUSH rule 3 x 69 [2,8]
+ CRUSH rule 3 x 70 [8,2]
+ CRUSH rule 3 x 71 [2,8,4]
+ CRUSH rule 3 x 72 [8,2,4]
+ CRUSH rule 3 x 73 [2,8]
+ CRUSH rule 3 x 74 [1,8]
+ CRUSH rule 3 x 75 [4,2,8]
+ CRUSH rule 3 x 76 [4,2,6]
+ CRUSH rule 3 x 77 [8,2,4]
+ CRUSH rule 3 x 78 [1,6]
+ CRUSH rule 3 x 79 [4,1,8]
+ CRUSH rule 3 x 80 [2,4,8]
+ CRUSH rule 3 x 81 [2,8]
+ CRUSH rule 3 x 82 [6,2]
+ CRUSH rule 3 x 83 [2,8]
+ CRUSH rule 3 x 84 [8,2]
+ CRUSH rule 3 x 85 [4,8,2]
+ CRUSH rule 3 x 86 [2,8]
+ CRUSH rule 3 x 87 [2,8,4]
+ CRUSH rule 3 x 88 [1,8]
+ CRUSH rule 3 x 89 [1,8]
+ CRUSH rule 3 x 90 [8,4,2]
+ CRUSH rule 3 x 91 [4,8,2]
+ CRUSH rule 3 x 92 [1,8]
+ CRUSH rule 3 x 93 [8,4,1]
+ CRUSH rule 3 x 94 [1,8]
+ CRUSH rule 3 x 95 [8,1]
+ CRUSH rule 3 x 96 [8,2]
+ CRUSH rule 3 x 97 [8,1]
+ CRUSH rule 3 x 98 [2,8]
+ CRUSH rule 3 x 99 [2,8]
+ CRUSH rule 3 x 100 [1,8,4]
+ CRUSH rule 3 x 101 [8,1]
+ CRUSH rule 3 x 102 [2,8]
+ CRUSH rule 3 x 103 [8,1]
+ CRUSH rule 3 x 104 [8,4,1]
+ CRUSH rule 3 x 105 [2,4,8]
+ CRUSH rule 3 x 106 [1,8,4]
+ CRUSH rule 3 x 107 [1,8]
+ CRUSH rule 3 x 108 [8,1]
+ CRUSH rule 3 x 109 [1,4,8]
+ CRUSH rule 3 x 110 [4,2,8]
+ CRUSH rule 3 x 111 [2,4,8]
+ CRUSH rule 3 x 112 [2,8]
+ CRUSH rule 3 x 113 [8,1]
+ CRUSH rule 3 x 114 [8,4,2]
+ CRUSH rule 3 x 115 [8,2,4]
+ CRUSH rule 3 x 116 [1,8]
+ CRUSH rule 3 x 117 [6,1]
+ CRUSH rule 3 x 118 [2,8]
+ CRUSH rule 3 x 119 [8,1]
+ CRUSH rule 3 x 120 [2,4,8]
+ CRUSH rule 3 x 121 [2,8,4]
+ CRUSH rule 3 x 122 [8,2]
+ CRUSH rule 3 x 123 [2,8]
+ CRUSH rule 3 x 124 [2,8]
+ CRUSH rule 3 x 125 [1,8,4]
+ CRUSH rule 3 x 126 [1,8]
+ CRUSH rule 3 x 127 [4,8,1]
+ CRUSH rule 3 x 128 [8,1]
+ CRUSH rule 3 x 129 [2,4,8]
+ CRUSH rule 3 x 130 [4,8,1]
+ CRUSH rule 3 x 131 [1,4,8]
+ CRUSH rule 3 x 132 [1,8]
+ CRUSH rule 3 x 133 [8,1]
+ CRUSH rule 3 x 134 [1,8,4]
+ CRUSH rule 3 x 135 [4,8,2]
+ CRUSH rule 3 x 136 [2,4,8]
+ CRUSH rule 3 x 137 [8,4,1]
+ CRUSH rule 3 x 138 [8,4,2]
+ CRUSH rule 3 x 139 [4,2,8]
+ CRUSH rule 3 x 140 [1,8,4]
+ CRUSH rule 3 x 141 [8,2]
+ CRUSH rule 3 x 142 [4,2,8]
+ CRUSH rule 3 x 143 [4,8,1]
+ CRUSH rule 3 x 144 [8,2]
+ CRUSH rule 3 x 145 [8,2]
+ CRUSH rule 3 x 146 [2,8]
+ CRUSH rule 3 x 147 [2,8,4]
+ CRUSH rule 3 x 148 [4,1,8]
+ CRUSH rule 3 x 149 [4,8,2]
+ CRUSH rule 3 x 150 [1,8]
+ CRUSH rule 3 x 151 [8,1]
+ CRUSH rule 3 x 152 [8,2]
+ CRUSH rule 3 x 153 [8,4,1]
+ CRUSH rule 3 x 154 [4,2,8]
+ CRUSH rule 3 x 155 [4,8,1]
+ CRUSH rule 3 x 156 [4,2,8]
+ CRUSH rule 3 x 157 [1,8]
+ CRUSH rule 3 x 158 [2,8,4]
+ CRUSH rule 3 x 159 [8,2,4]
+ CRUSH rule 3 x 160 [2,8,4]
+ CRUSH rule 3 x 161 [1,4,8]
+ CRUSH rule 3 x 162 [1,8]
+ CRUSH rule 3 x 163 [4,8,2]
+ CRUSH rule 3 x 164 [8,2]
+ CRUSH rule 3 x 165 [8,2,4]
+ CRUSH rule 3 x 166 [2,8]
+ CRUSH rule 3 x 167 [1,8,4]
+ CRUSH rule 3 x 168 [4,2,8]
+ CRUSH rule 3 x 169 [2,8,4]
+ CRUSH rule 3 x 170 [1,8]
+ CRUSH rule 3 x 171 [8,4,2]
+ CRUSH rule 3 x 172 [1,8]
+ CRUSH rule 3 x 173 [8,4,1]
+ CRUSH rule 3 x 174 [1,8]
+ CRUSH rule 3 x 175 [8,1]
+ CRUSH rule 3 x 176 [2,8]
+ CRUSH rule 3 x 177 [1,8]
+ CRUSH rule 3 x 178 [4,2,8]
+ CRUSH rule 3 x 179 [1,8]
+ CRUSH rule 3 x 180 [8,2]
+ CRUSH rule 3 x 181 [8,2,4]
+ CRUSH rule 3 x 182 [8,1]
+ CRUSH rule 3 x 183 [8,4,1]
+ CRUSH rule 3 x 184 [4,8,1]
+ CRUSH rule 3 x 185 [8,1,4]
+ CRUSH rule 3 x 186 [2,4,8]
+ CRUSH rule 3 x 187 [1,8]
+ CRUSH rule 3 x 188 [1,8,4]
+ CRUSH rule 3 x 189 [1,8,4]
+ CRUSH rule 3 x 190 [1,8]
+ CRUSH rule 3 x 191 [8,1,4]
+ CRUSH rule 3 x 192 [4,2,8]
+ CRUSH rule 3 x 193 [4,2,8]
+ CRUSH rule 3 x 194 [1,8]
+ CRUSH rule 3 x 195 [8,4,2]
+ CRUSH rule 3 x 196 [8,1]
+ CRUSH rule 3 x 197 [8,4,1]
+ CRUSH rule 3 x 198 [2,8]
+ CRUSH rule 3 x 199 [1,4,8]
+ CRUSH rule 3 x 200 [1,8]
+ CRUSH rule 3 x 201 [8,1,4]
+ CRUSH rule 3 x 202 [8,1]
+ CRUSH rule 3 x 203 [8,1]
+ CRUSH rule 3 x 204 [2,4,8]
+ CRUSH rule 3 x 205 [1,8]
+ CRUSH rule 3 x 206 [1,8,4]
+ CRUSH rule 3 x 207 [2,8]
+ CRUSH rule 3 x 208 [8,2]
+ CRUSH rule 3 x 209 [1,8]
+ CRUSH rule 3 x 210 [1,4,8]
+ CRUSH rule 3 x 211 [4,2,8]
+ CRUSH rule 3 x 212 [8,1]
+ CRUSH rule 3 x 213 [8,4,1]
+ CRUSH rule 3 x 214 [8,1]
+ CRUSH rule 3 x 215 [8,1]
+ CRUSH rule 3 x 216 [2,8]
+ CRUSH rule 3 x 217 [1,8,4]
+ CRUSH rule 3 x 218 [2,8]
+ CRUSH rule 3 x 219 [8,2]
+ CRUSH rule 3 x 220 [4,8,1]
+ CRUSH rule 3 x 221 [8,1]
+ CRUSH rule 3 x 222 [8,1]
+ CRUSH rule 3 x 223 [1,8]
+ CRUSH rule 3 x 224 [1,4,8]
+ CRUSH rule 3 x 225 [8,2]
+ CRUSH rule 3 x 226 [8,2,4]
+ CRUSH rule 3 x 227 [4,1,8]
+ CRUSH rule 3 x 228 [8,1]
+ CRUSH rule 3 x 229 [4,8,1]
+ CRUSH rule 3 x 230 [4,8,1]
+ CRUSH rule 3 x 231 [4,8,1]
+ CRUSH rule 3 x 232 [2,8,4]
+ CRUSH rule 3 x 233 [8,2]
+ CRUSH rule 3 x 234 [1,8]
+ CRUSH rule 3 x 235 [4,8,1]
+ CRUSH rule 3 x 236 [2,8]
+ CRUSH rule 3 x 237 [4,8,2]
+ CRUSH rule 3 x 238 [2,8]
+ CRUSH rule 3 x 239 [8,2]
+ CRUSH rule 3 x 240 [4,8,2]
+ CRUSH rule 3 x 241 [1,8]
+ CRUSH rule 3 x 242 [2,8]
+ CRUSH rule 3 x 243 [8,2]
+ CRUSH rule 3 x 244 [4,8,2]
+ CRUSH rule 3 x 245 [8,2]
+ CRUSH rule 3 x 246 [1,8]
+ CRUSH rule 3 x 247 [8,1]
+ CRUSH rule 3 x 248 [8,1,4]
+ CRUSH rule 3 x 249 [2,8]
+ CRUSH rule 3 x 250 [2,4,8]
+ CRUSH rule 3 x 251 [2,8]
+ CRUSH rule 3 x 252 [4,8,2]
+ CRUSH rule 3 x 253 [2,8]
+ CRUSH rule 3 x 254 [4,2,8]
+ CRUSH rule 3 x 255 [1,8]
+ CRUSH rule 3 x 256 [4,8,1]
+ CRUSH rule 3 x 257 [2,6,4]
+ CRUSH rule 3 x 258 [4,2,8]
+ CRUSH rule 3 x 259 [6,1]
+ CRUSH rule 3 x 260 [8,1]
+ CRUSH rule 3 x 261 [8,2]
+ CRUSH rule 3 x 262 [8,2]
+ CRUSH rule 3 x 263 [8,1,4]
+ CRUSH rule 3 x 264 [8,1]
+ CRUSH rule 3 x 265 [8,2]
+ CRUSH rule 3 x 266 [8,2,4]
+ CRUSH rule 3 x 267 [2,8]
+ CRUSH rule 3 x 268 [1,8]
+ CRUSH rule 3 x 269 [1,8,4]
+ CRUSH rule 3 x 270 [4,1,8]
+ CRUSH rule 3 x 271 [8,4,2]
+ CRUSH rule 3 x 272 [2,8,4]
+ CRUSH rule 3 x 273 [4,2,8]
+ CRUSH rule 3 x 274 [8,4,2]
+ CRUSH rule 3 x 275 [4,8,1]
+ CRUSH rule 3 x 276 [8,1,4]
+ CRUSH rule 3 x 277 [8,1]
+ CRUSH rule 3 x 278 [8,2,4]
+ CRUSH rule 3 x 279 [8,4,1]
+ CRUSH rule 3 x 280 [2,8,4]
+ CRUSH rule 3 x 281 [8,1]
+ CRUSH rule 3 x 282 [2,8]
+ CRUSH rule 3 x 283 [8,1]
+ CRUSH rule 3 x 284 [8,1]
+ CRUSH rule 3 x 285 [4,8,2]
+ CRUSH rule 3 x 286 [2,8,4]
+ CRUSH rule 3 x 287 [1,8]
+ CRUSH rule 3 x 288 [8,1,4]
+ CRUSH rule 3 x 289 [4,8,2]
+ CRUSH rule 3 x 290 [1,4,8]
+ CRUSH rule 3 x 291 [1,4,8]
+ CRUSH rule 3 x 292 [8,1,4]
+ CRUSH rule 3 x 293 [8,2]
+ CRUSH rule 3 x 294 [8,4,2]
+ CRUSH rule 3 x 295 [4,8,1]
+ CRUSH rule 3 x 296 [4,1,8]
+ CRUSH rule 3 x 297 [8,2,4]
+ CRUSH rule 3 x 298 [1,8]
+ CRUSH rule 3 x 299 [2,8]
+ CRUSH rule 3 x 300 [8,1]
+ CRUSH rule 3 x 301 [1,8]
+ CRUSH rule 3 x 302 [1,8]
+ CRUSH rule 3 x 303 [8,4,1]
+ CRUSH rule 3 x 304 [2,8]
+ CRUSH rule 3 x 305 [8,1]
+ CRUSH rule 3 x 306 [1,8]
+ CRUSH rule 3 x 307 [2,8]
+ CRUSH rule 3 x 308 [2,8,4]
+ CRUSH rule 3 x 309 [8,2]
+ CRUSH rule 3 x 310 [4,1,6]
+ CRUSH rule 3 x 311 [4,8,2]
+ CRUSH rule 3 x 312 [2,8]
+ CRUSH rule 3 x 313 [4,1,8]
+ CRUSH rule 3 x 314 [2,6]
+ CRUSH rule 3 x 315 [2,8]
+ CRUSH rule 3 x 316 [8,1]
+ CRUSH rule 3 x 317 [2,8]
+ CRUSH rule 3 x 318 [8,1]
+ CRUSH rule 3 x 319 [2,8]
+ CRUSH rule 3 x 320 [8,1]
+ CRUSH rule 3 x 321 [1,8]
+ CRUSH rule 3 x 322 [2,6,4]
+ CRUSH rule 3 x 323 [4,8,2]
+ CRUSH rule 3 x 324 [8,2,4]
+ CRUSH rule 3 x 325 [4,8,1]
+ CRUSH rule 3 x 326 [1,6]
+ CRUSH rule 3 x 327 [1,8]
+ CRUSH rule 3 x 328 [8,4,1]
+ CRUSH rule 3 x 329 [4,8,2]
+ CRUSH rule 3 x 330 [4,8,1]
+ CRUSH rule 3 x 331 [2,8]
+ CRUSH rule 3 x 332 [2,8]
+ CRUSH rule 3 x 333 [8,1]
+ CRUSH rule 3 x 334 [8,2]
+ CRUSH rule 3 x 335 [8,2]
+ CRUSH rule 3 x 336 [4,8,2]
+ CRUSH rule 3 x 337 [8,2,4]
+ CRUSH rule 3 x 338 [8,2]
+ CRUSH rule 3 x 339 [8,2]
+ CRUSH rule 3 x 340 [2,8,4]
+ CRUSH rule 3 x 341 [4,1,6]
+ CRUSH rule 3 x 342 [2,8,4]
+ CRUSH rule 3 x 343 [8,1]
+ CRUSH rule 3 x 344 [6,1,4]
+ CRUSH rule 3 x 345 [8,2]
+ CRUSH rule 3 x 346 [8,2,4]
+ CRUSH rule 3 x 347 [4,1,8]
+ CRUSH rule 3 x 348 [8,2,4]
+ CRUSH rule 3 x 349 [1,8]
+ CRUSH rule 3 x 350 [8,1]
+ CRUSH rule 3 x 351 [8,2]
+ CRUSH rule 3 x 352 [1,8,4]
+ CRUSH rule 3 x 353 [8,1]
+ CRUSH rule 3 x 354 [1,8]
+ CRUSH rule 3 x 355 [8,2]
+ CRUSH rule 3 x 356 [4,1,8]
+ CRUSH rule 3 x 357 [8,1,4]
+ CRUSH rule 3 x 358 [2,8,4]
+ CRUSH rule 3 x 359 [6,1,4]
+ CRUSH rule 3 x 360 [2,8]
+ CRUSH rule 3 x 361 [8,4,1]
+ CRUSH rule 3 x 362 [4,1,8]
+ CRUSH rule 3 x 363 [4,2,8]
+ CRUSH rule 3 x 364 [2,8]
+ CRUSH rule 3 x 365 [8,2]
+ CRUSH rule 3 x 366 [8,2]
+ CRUSH rule 3 x 367 [4,2,8]
+ CRUSH rule 3 x 368 [8,4,1]
+ CRUSH rule 3 x 369 [8,2]
+ CRUSH rule 3 x 370 [8,2]
+ CRUSH rule 3 x 371 [1,4,8]
+ CRUSH rule 3 x 372 [1,8]
+ CRUSH rule 3 x 373 [1,8]
+ CRUSH rule 3 x 374 [8,1]
+ CRUSH rule 3 x 375 [8,4,2]
+ CRUSH rule 3 x 376 [8,1,4]
+ CRUSH rule 3 x 377 [1,4,8]
+ CRUSH rule 3 x 378 [1,8]
+ CRUSH rule 3 x 379 [8,2]
+ CRUSH rule 3 x 380 [2,8]
+ CRUSH rule 3 x 381 [1,4,8]
+ CRUSH rule 3 x 382 [1,4,8]
+ CRUSH rule 3 x 383 [4,8,1]
+ CRUSH rule 3 x 384 [8,2,4]
+ CRUSH rule 3 x 385 [8,1]
+ CRUSH rule 3 x 386 [1,8]
+ CRUSH rule 3 x 387 [1,4,8]
+ CRUSH rule 3 x 388 [2,6]
+ CRUSH rule 3 x 389 [1,4,8]
+ CRUSH rule 3 x 390 [4,8,1]
+ CRUSH rule 3 x 391 [4,8,2]
+ CRUSH rule 3 x 392 [1,8,4]
+ CRUSH rule 3 x 393 [2,8]
+ CRUSH rule 3 x 394 [8,2]
+ CRUSH rule 3 x 395 [1,8]
+ CRUSH rule 3 x 396 [4,2,8]
+ CRUSH rule 3 x 397 [2,4,6]
+ CRUSH rule 3 x 398 [2,4,8]
+ CRUSH rule 3 x 399 [8,4,2]
+ CRUSH rule 3 x 400 [8,1,4]
+ CRUSH rule 3 x 401 [1,4,8]
+ CRUSH rule 3 x 402 [8,4,2]
+ CRUSH rule 3 x 403 [1,4,8]
+ CRUSH rule 3 x 404 [4,2,8]
+ CRUSH rule 3 x 405 [8,4,2]
+ CRUSH rule 3 x 406 [2,8]
+ CRUSH rule 3 x 407 [2,8,4]
+ CRUSH rule 3 x 408 [4,1,8]
+ CRUSH rule 3 x 409 [8,4,2]
+ CRUSH rule 3 x 410 [8,4,1]
+ CRUSH rule 3 x 411 [2,8,4]
+ CRUSH rule 3 x 412 [2,6]
+ CRUSH rule 3 x 413 [2,8]
+ CRUSH rule 3 x 414 [4,1,8]
+ CRUSH rule 3 x 415 [2,8]
+ CRUSH rule 3 x 416 [2,8]
+ CRUSH rule 3 x 417 [8,2]
+ CRUSH rule 3 x 418 [8,1,4]
+ CRUSH rule 3 x 419 [8,4,2]
+ CRUSH rule 3 x 420 [1,4,8]
+ CRUSH rule 3 x 421 [8,4,1]
+ CRUSH rule 3 x 422 [6,1]
+ CRUSH rule 3 x 423 [2,4,8]
+ CRUSH rule 3 x 424 [8,1]
+ CRUSH rule 3 x 425 [1,8]
+ CRUSH rule 3 x 426 [8,2]
+ CRUSH rule 3 x 427 [1,8]
+ CRUSH rule 3 x 428 [4,8,1]
+ CRUSH rule 3 x 429 [4,8,1]
+ CRUSH rule 3 x 430 [4,8,2]
+ CRUSH rule 3 x 431 [4,2,8]
+ CRUSH rule 3 x 432 [8,1]
+ CRUSH rule 3 x 433 [8,1]
+ CRUSH rule 3 x 434 [2,8]
+ CRUSH rule 3 x 435 [2,6]
+ CRUSH rule 3 x 436 [4,1,8]
+ CRUSH rule 3 x 437 [8,1]
+ CRUSH rule 3 x 438 [2,4,8]
+ CRUSH rule 3 x 439 [1,8]
+ CRUSH rule 3 x 440 [2,8]
+ CRUSH rule 3 x 441 [4,8,2]
+ CRUSH rule 3 x 442 [2,8]
+ CRUSH rule 3 x 443 [8,1,4]
+ CRUSH rule 3 x 444 [8,1]
+ CRUSH rule 3 x 445 [8,2]
+ CRUSH rule 3 x 446 [1,8]
+ CRUSH rule 3 x 447 [2,4,8]
+ CRUSH rule 3 x 448 [8,2,4]
+ CRUSH rule 3 x 449 [8,2]
+ CRUSH rule 3 x 450 [1,8]
+ CRUSH rule 3 x 451 [8,4,1]
+ CRUSH rule 3 x 452 [8,2]
+ CRUSH rule 3 x 453 [6,2]
+ CRUSH rule 3 x 454 [8,2]
+ CRUSH rule 3 x 455 [2,8,4]
+ CRUSH rule 3 x 456 [8,2]
+ CRUSH rule 3 x 457 [8,1]
+ CRUSH rule 3 x 458 [2,8]
+ CRUSH rule 3 x 459 [2,8,4]
+ CRUSH rule 3 x 460 [8,2]
+ CRUSH rule 3 x 461 [8,2]
+ CRUSH rule 3 x 462 [8,1]
+ CRUSH rule 3 x 463 [8,2]
+ CRUSH rule 3 x 464 [8,4,1]
+ CRUSH rule 3 x 465 [6,1,4]
+ CRUSH rule 3 x 466 [8,2]
+ CRUSH rule 3 x 467 [8,1]
+ CRUSH rule 3 x 468 [8,2,4]
+ CRUSH rule 3 x 469 [8,2]
+ CRUSH rule 3 x 470 [4,1,8]
+ CRUSH rule 3 x 471 [1,8]
+ CRUSH rule 3 x 472 [1,8]
+ CRUSH rule 3 x 473 [1,4,8]
+ CRUSH rule 3 x 474 [8,1]
+ CRUSH rule 3 x 475 [8,2,4]
+ CRUSH rule 3 x 476 [4,8,1]
+ CRUSH rule 3 x 477 [4,8,1]
+ CRUSH rule 3 x 478 [8,1,4]
+ CRUSH rule 3 x 479 [2,8]
+ CRUSH rule 3 x 480 [1,8]
+ CRUSH rule 3 x 481 [2,4,6]
+ CRUSH rule 3 x 482 [8,1]
+ CRUSH rule 3 x 483 [2,8,4]
+ CRUSH rule 3 x 484 [1,8]
+ CRUSH rule 3 x 485 [8,1]
+ CRUSH rule 3 x 486 [4,1,8]
+ CRUSH rule 3 x 487 [1,8]
+ CRUSH rule 3 x 488 [8,1]
+ CRUSH rule 3 x 489 [2,8]
+ CRUSH rule 3 x 490 [6,2]
+ CRUSH rule 3 x 491 [1,8]
+ CRUSH rule 3 x 492 [8,2]
+ CRUSH rule 3 x 493 [2,8]
+ CRUSH rule 3 x 494 [1,8]
+ CRUSH rule 3 x 495 [4,1,6]
+ CRUSH rule 3 x 496 [8,4,1]
+ CRUSH rule 3 x 497 [4,8,2]
+ CRUSH rule 3 x 498 [2,4,8]
+ CRUSH rule 3 x 499 [8,4,2]
+ CRUSH rule 3 x 500 [4,8,1]
+ CRUSH rule 3 x 501 [2,8]
+ CRUSH rule 3 x 502 [6,1]
+ CRUSH rule 3 x 503 [2,8]
+ CRUSH rule 3 x 504 [8,2]
+ CRUSH rule 3 x 505 [1,8]
+ CRUSH rule 3 x 506 [4,1,8]
+ CRUSH rule 3 x 507 [8,2,4]
+ CRUSH rule 3 x 508 [1,8]
+ CRUSH rule 3 x 509 [8,1]
+ CRUSH rule 3 x 510 [8,2]
+ CRUSH rule 3 x 511 [4,8,2]
+ CRUSH rule 3 x 512 [8,2]
+ CRUSH rule 3 x 513 [8,2]
+ CRUSH rule 3 x 514 [8,2]
+ CRUSH rule 3 x 515 [8,4,2]
+ CRUSH rule 3 x 516 [4,1,8]
+ CRUSH rule 3 x 517 [8,2]
+ CRUSH rule 3 x 518 [4,8,1]
+ CRUSH rule 3 x 519 [8,4,1]
+ CRUSH rule 3 x 520 [2,8,4]
+ CRUSH rule 3 x 521 [8,2,4]
+ CRUSH rule 3 x 522 [8,1,4]
+ CRUSH rule 3 x 523 [4,1,8]
+ CRUSH rule 3 x 524 [2,6]
+ CRUSH rule 3 x 525 [2,8]
+ CRUSH rule 3 x 526 [1,8]
+ CRUSH rule 3 x 527 [1,4,6]
+ CRUSH rule 3 x 528 [2,8]
+ CRUSH rule 3 x 529 [4,8,1]
+ CRUSH rule 3 x 530 [8,1]
+ CRUSH rule 3 x 531 [8,1,4]
+ CRUSH rule 3 x 532 [6,4,1]
+ CRUSH rule 3 x 533 [4,8,1]
+ CRUSH rule 3 x 534 [8,2]
+ CRUSH rule 3 x 535 [8,1]
+ CRUSH rule 3 x 536 [8,2]
+ CRUSH rule 3 x 537 [4,8,1]
+ CRUSH rule 3 x 538 [8,4,2]
+ CRUSH rule 3 x 539 [8,2]
+ CRUSH rule 3 x 540 [1,8,4]
+ CRUSH rule 3 x 541 [2,4,8]
+ CRUSH rule 3 x 542 [2,8]
+ CRUSH rule 3 x 543 [8,2]
+ CRUSH rule 3 x 544 [4,8,1]
+ CRUSH rule 3 x 545 [8,1]
+ CRUSH rule 3 x 546 [8,2,4]
+ CRUSH rule 3 x 547 [8,1,4]
+ CRUSH rule 3 x 548 [4,1,8]
+ CRUSH rule 3 x 549 [8,1]
+ CRUSH rule 3 x 550 [2,4,8]
+ CRUSH rule 3 x 551 [8,1]
+ CRUSH rule 3 x 552 [4,8,2]
+ CRUSH rule 3 x 553 [2,8]
+ CRUSH rule 3 x 554 [1,8]
+ CRUSH rule 3 x 555 [4,2,8]
+ CRUSH rule 3 x 556 [8,1]
+ CRUSH rule 3 x 557 [8,2]
+ CRUSH rule 3 x 558 [4,2,8]
+ CRUSH rule 3 x 559 [1,8]
+ CRUSH rule 3 x 560 [8,2]
+ CRUSH rule 3 x 561 [8,4,1]
+ CRUSH rule 3 x 562 [4,1,8]
+ CRUSH rule 3 x 563 [2,8]
+ CRUSH rule 3 x 564 [1,8]
+ CRUSH rule 3 x 565 [4,8,1]
+ CRUSH rule 3 x 566 [4,8,2]
+ CRUSH rule 3 x 567 [4,8,1]
+ CRUSH rule 3 x 568 [8,1]
+ CRUSH rule 3 x 569 [4,1,8]
+ CRUSH rule 3 x 570 [1,8]
+ CRUSH rule 3 x 571 [6,1]
+ CRUSH rule 3 x 572 [4,2,8]
+ CRUSH rule 3 x 573 [1,8]
+ CRUSH rule 3 x 574 [2,8]
+ CRUSH rule 3 x 575 [8,1,4]
+ CRUSH rule 3 x 576 [4,8,1]
+ CRUSH rule 3 x 577 [8,2]
+ CRUSH rule 3 x 578 [8,1]
+ CRUSH rule 3 x 579 [4,2,8]
+ CRUSH rule 3 x 580 [1,8]
+ CRUSH rule 3 x 581 [8,1,4]
+ CRUSH rule 3 x 582 [2,8,4]
+ CRUSH rule 3 x 583 [8,2]
+ CRUSH rule 3 x 584 [8,1,4]
+ CRUSH rule 3 x 585 [8,1,4]
+ CRUSH rule 3 x 586 [1,8,4]
+ CRUSH rule 3 x 587 [2,4,8]
+ CRUSH rule 3 x 588 [4,8,1]
+ CRUSH rule 3 x 589 [8,1]
+ CRUSH rule 3 x 590 [8,2]
+ CRUSH rule 3 x 591 [4,2,8]
+ CRUSH rule 3 x 592 [2,4,8]
+ CRUSH rule 3 x 593 [1,8,4]
+ CRUSH rule 3 x 594 [2,8]
+ CRUSH rule 3 x 595 [8,1]
+ CRUSH rule 3 x 596 [2,8]
+ CRUSH rule 3 x 597 [1,8]
+ CRUSH rule 3 x 598 [2,8]
+ CRUSH rule 3 x 599 [4,1,8]
+ CRUSH rule 3 x 600 [8,1,4]
+ CRUSH rule 3 x 601 [1,8,4]
+ CRUSH rule 3 x 602 [8,2]
+ CRUSH rule 3 x 603 [1,8]
+ CRUSH rule 3 x 604 [8,2]
+ CRUSH rule 3 x 605 [2,8]
+ CRUSH rule 3 x 606 [2,6,4]
+ CRUSH rule 3 x 607 [2,4,8]
+ CRUSH rule 3 x 608 [4,1,8]
+ CRUSH rule 3 x 609 [4,2,8]
+ CRUSH rule 3 x 610 [8,2]
+ CRUSH rule 3 x 611 [1,8]
+ CRUSH rule 3 x 612 [2,8]
+ CRUSH rule 3 x 613 [8,1,4]
+ CRUSH rule 3 x 614 [8,2,4]
+ CRUSH rule 3 x 615 [8,2,4]
+ CRUSH rule 3 x 616 [1,8]
+ CRUSH rule 3 x 617 [8,2,4]
+ CRUSH rule 3 x 618 [8,4,1]
+ CRUSH rule 3 x 619 [4,1,8]
+ CRUSH rule 3 x 620 [1,8]
+ CRUSH rule 3 x 621 [8,1]
+ CRUSH rule 3 x 622 [2,4,8]
+ CRUSH rule 3 x 623 [2,8]
+ CRUSH rule 3 x 624 [4,2,8]
+ CRUSH rule 3 x 625 [2,8]
+ CRUSH rule 3 x 626 [8,2,4]
+ CRUSH rule 3 x 627 [2,8,4]
+ CRUSH rule 3 x 628 [8,1]
+ CRUSH rule 3 x 629 [2,8,4]
+ CRUSH rule 3 x 630 [2,8]
+ CRUSH rule 3 x 631 [1,8,4]
+ CRUSH rule 3 x 632 [8,2]
+ CRUSH rule 3 x 633 [8,2]
+ CRUSH rule 3 x 634 [1,8]
+ CRUSH rule 3 x 635 [4,8,2]
+ CRUSH rule 3 x 636 [1,4,8]
+ CRUSH rule 3 x 637 [1,8]
+ CRUSH rule 3 x 638 [8,2,4]
+ CRUSH rule 3 x 639 [2,8]
+ CRUSH rule 3 x 640 [2,8]
+ CRUSH rule 3 x 641 [8,2]
+ CRUSH rule 3 x 642 [2,8]
+ CRUSH rule 3 x 643 [1,8]
+ CRUSH rule 3 x 644 [8,1]
+ CRUSH rule 3 x 645 [8,2]
+ CRUSH rule 3 x 646 [8,1,4]
+ CRUSH rule 3 x 647 [8,1]
+ CRUSH rule 3 x 648 [1,8]
+ CRUSH rule 3 x 649 [4,8,1]
+ CRUSH rule 3 x 650 [8,4,1]
+ CRUSH rule 3 x 651 [4,6,1]
+ CRUSH rule 3 x 652 [4,8,1]
+ CRUSH rule 3 x 653 [8,2]
+ CRUSH rule 3 x 654 [6,2]
+ CRUSH rule 3 x 655 [1,4,8]
+ CRUSH rule 3 x 656 [8,1]
+ CRUSH rule 3 x 657 [6,1]
+ CRUSH rule 3 x 658 [8,2]
+ CRUSH rule 3 x 659 [4,8,1]
+ CRUSH rule 3 x 660 [8,2]
+ CRUSH rule 3 x 661 [1,8]
+ CRUSH rule 3 x 662 [2,8]
+ CRUSH rule 3 x 663 [1,4,8]
+ CRUSH rule 3 x 664 [1,4,8]
+ CRUSH rule 3 x 665 [4,6,1]
+ CRUSH rule 3 x 666 [2,8]
+ CRUSH rule 3 x 667 [1,4,8]
+ CRUSH rule 3 x 668 [4,8,2]
+ CRUSH rule 3 x 669 [6,4,1]
+ CRUSH rule 3 x 670 [4,1,8]
+ CRUSH rule 3 x 671 [2,8]
+ CRUSH rule 3 x 672 [4,2,8]
+ CRUSH rule 3 x 673 [4,2,8]
+ CRUSH rule 3 x 674 [1,8]
+ CRUSH rule 3 x 675 [1,8,4]
+ CRUSH rule 3 x 676 [2,4,8]
+ CRUSH rule 3 x 677 [4,1,8]
+ CRUSH rule 3 x 678 [2,4,8]
+ CRUSH rule 3 x 679 [8,2]
+ CRUSH rule 3 x 680 [2,8]
+ CRUSH rule 3 x 681 [8,1]
+ CRUSH rule 3 x 682 [1,4,8]
+ CRUSH rule 3 x 683 [1,4,8]
+ CRUSH rule 3 x 684 [8,2,4]
+ CRUSH rule 3 x 685 [8,2,4]
+ CRUSH rule 3 x 686 [1,4,8]
+ CRUSH rule 3 x 687 [6,2]
+ CRUSH rule 3 x 688 [4,8,2]
+ CRUSH rule 3 x 689 [8,4,1]
+ CRUSH rule 3 x 690 [8,1,4]
+ CRUSH rule 3 x 691 [1,8]
+ CRUSH rule 3 x 692 [8,1]
+ CRUSH rule 3 x 693 [8,4,2]
+ CRUSH rule 3 x 694 [8,4,2]
+ CRUSH rule 3 x 695 [2,8,4]
+ CRUSH rule 3 x 696 [1,8]
+ CRUSH rule 3 x 697 [8,1,4]
+ CRUSH rule 3 x 698 [8,2,4]
+ CRUSH rule 3 x 699 [1,8,4]
+ CRUSH rule 3 x 700 [1,6]
+ CRUSH rule 3 x 701 [1,8]
+ CRUSH rule 3 x 702 [2,8]
+ CRUSH rule 3 x 703 [8,1]
+ CRUSH rule 3 x 704 [1,4,8]
+ CRUSH rule 3 x 705 [8,1,4]
+ CRUSH rule 3 x 706 [1,4,8]
+ CRUSH rule 3 x 707 [8,4,1]
+ CRUSH rule 3 x 708 [4,8,1]
+ CRUSH rule 3 x 709 [8,1]
+ CRUSH rule 3 x 710 [8,1]
+ CRUSH rule 3 x 711 [2,4,8]
+ CRUSH rule 3 x 712 [2,8]
+ CRUSH rule 3 x 713 [8,4,1]
+ CRUSH rule 3 x 714 [2,8]
+ CRUSH rule 3 x 715 [1,8]
+ CRUSH rule 3 x 716 [4,8,1]
+ CRUSH rule 3 x 717 [8,2,4]
+ CRUSH rule 3 x 718 [8,1]
+ CRUSH rule 3 x 719 [2,6,4]
+ CRUSH rule 3 x 720 [8,1,4]
+ CRUSH rule 3 x 721 [4,6,1]
+ CRUSH rule 3 x 722 [8,1]
+ CRUSH rule 3 x 723 [4,2,8]
+ CRUSH rule 3 x 724 [2,6]
+ CRUSH rule 3 x 725 [1,8]
+ CRUSH rule 3 x 726 [4,8,2]
+ CRUSH rule 3 x 727 [4,8,2]
+ CRUSH rule 3 x 728 [2,8,4]
+ CRUSH rule 3 x 729 [8,1]
+ CRUSH rule 3 x 730 [4,8,2]
+ CRUSH rule 3 x 731 [4,1,8]
+ CRUSH rule 3 x 732 [1,8]
+ CRUSH rule 3 x 733 [4,8,2]
+ CRUSH rule 3 x 734 [8,4,1]
+ CRUSH rule 3 x 735 [4,8,1]
+ CRUSH rule 3 x 736 [4,8,1]
+ CRUSH rule 3 x 737 [1,8,4]
+ CRUSH rule 3 x 738 [4,1,8]
+ CRUSH rule 3 x 739 [2,8]
+ CRUSH rule 3 x 740 [1,8,4]
+ CRUSH rule 3 x 741 [8,2]
+ CRUSH rule 3 x 742 [8,2]
+ CRUSH rule 3 x 743 [8,1,4]
+ CRUSH rule 3 x 744 [4,8,1]
+ CRUSH rule 3 x 745 [1,8]
+ CRUSH rule 3 x 746 [1,8]
+ CRUSH rule 3 x 747 [8,2]
+ CRUSH rule 3 x 748 [2,8,4]
+ CRUSH rule 3 x 749 [4,8,2]
+ CRUSH rule 3 x 750 [1,8,4]
+ CRUSH rule 3 x 751 [2,8]
+ CRUSH rule 3 x 752 [8,1]
+ CRUSH rule 3 x 753 [8,4,2]
+ CRUSH rule 3 x 754 [8,4,2]
+ CRUSH rule 3 x 755 [1,8,4]
+ CRUSH rule 3 x 756 [8,1]
+ CRUSH rule 3 x 757 [8,1,4]
+ CRUSH rule 3 x 758 [8,1]
+ CRUSH rule 3 x 759 [8,4,2]
+ CRUSH rule 3 x 760 [1,4,8]
+ CRUSH rule 3 x 761 [2,6]
+ CRUSH rule 3 x 762 [2,8]
+ CRUSH rule 3 x 763 [8,4,1]
+ CRUSH rule 3 x 764 [1,8]
+ CRUSH rule 3 x 765 [8,2]
+ CRUSH rule 3 x 766 [8,1]
+ CRUSH rule 3 x 767 [1,8,4]
+ CRUSH rule 3 x 768 [8,4,2]
+ CRUSH rule 3 x 769 [8,2,4]
+ CRUSH rule 3 x 770 [8,1,4]
+ CRUSH rule 3 x 771 [8,2,4]
+ CRUSH rule 3 x 772 [8,4,2]
+ CRUSH rule 3 x 773 [4,2,8]
+ CRUSH rule 3 x 774 [8,1]
+ CRUSH rule 3 x 775 [8,4,2]
+ CRUSH rule 3 x 776 [6,2]
+ CRUSH rule 3 x 777 [4,1,8]
+ CRUSH rule 3 x 778 [1,6,4]
+ CRUSH rule 3 x 779 [2,8]
+ CRUSH rule 3 x 780 [2,4,8]
+ CRUSH rule 3 x 781 [8,2]
+ CRUSH rule 3 x 782 [4,2,8]
+ CRUSH rule 3 x 783 [8,1,4]
+ CRUSH rule 3 x 784 [1,4,8]
+ CRUSH rule 3 x 785 [8,1,4]
+ CRUSH rule 3 x 786 [8,1]
+ CRUSH rule 3 x 787 [1,8,4]
+ CRUSH rule 3 x 788 [8,2,4]
+ CRUSH rule 3 x 789 [1,8]
+ CRUSH rule 3 x 790 [8,1]
+ CRUSH rule 3 x 791 [4,8,2]
+ CRUSH rule 3 x 792 [4,8,1]
+ CRUSH rule 3 x 793 [8,2,4]
+ CRUSH rule 3 x 794 [2,8,4]
+ CRUSH rule 3 x 795 [1,8]
+ CRUSH rule 3 x 796 [8,1]
+ CRUSH rule 3 x 797 [2,4,8]
+ CRUSH rule 3 x 798 [6,1]
+ CRUSH rule 3 x 799 [4,2,8]
+ CRUSH rule 3 x 800 [2,8]
+ CRUSH rule 3 x 801 [4,8,2]
+ CRUSH rule 3 x 802 [1,8,4]
+ CRUSH rule 3 x 803 [2,8]
+ CRUSH rule 3 x 804 [8,2]
+ CRUSH rule 3 x 805 [8,2]
+ CRUSH rule 3 x 806 [1,4,8]
+ CRUSH rule 3 x 807 [4,8,2]
+ CRUSH rule 3 x 808 [8,2]
+ CRUSH rule 3 x 809 [1,8]
+ CRUSH rule 3 x 810 [8,2]
+ CRUSH rule 3 x 811 [8,1]
+ CRUSH rule 3 x 812 [8,4,1]
+ CRUSH rule 3 x 813 [8,4,2]
+ CRUSH rule 3 x 814 [8,2]
+ CRUSH rule 3 x 815 [4,1,8]
+ CRUSH rule 3 x 816 [2,8]
+ CRUSH rule 3 x 817 [8,2]
+ CRUSH rule 3 x 818 [1,8]
+ CRUSH rule 3 x 819 [1,8]
+ CRUSH rule 3 x 820 [4,8,1]
+ CRUSH rule 3 x 821 [4,8,2]
+ CRUSH rule 3 x 822 [2,4,8]
+ CRUSH rule 3 x 823 [4,8,2]
+ CRUSH rule 3 x 824 [8,1]
+ CRUSH rule 3 x 825 [2,8,4]
+ CRUSH rule 3 x 826 [8,1,4]
+ CRUSH rule 3 x 827 [2,6,4]
+ CRUSH rule 3 x 828 [2,8]
+ CRUSH rule 3 x 829 [8,1]
+ CRUSH rule 3 x 830 [2,4,8]
+ CRUSH rule 3 x 831 [1,8]
+ CRUSH rule 3 x 832 [4,8,2]
+ CRUSH rule 3 x 833 [2,8]
+ CRUSH rule 3 x 834 [1,8]
+ CRUSH rule 3 x 835 [8,4,1]
+ CRUSH rule 3 x 836 [4,8,1]
+ CRUSH rule 3 x 837 [8,4,1]
+ CRUSH rule 3 x 838 [6,2,4]
+ CRUSH rule 3 x 839 [2,8]
+ CRUSH rule 3 x 840 [8,2]
+ CRUSH rule 3 x 841 [4,8,1]
+ CRUSH rule 3 x 842 [2,8]
+ CRUSH rule 3 x 843 [8,4,2]
+ CRUSH rule 3 x 844 [8,2]
+ CRUSH rule 3 x 845 [4,8,2]
+ CRUSH rule 3 x 846 [4,2,8]
+ CRUSH rule 3 x 847 [2,8]
+ CRUSH rule 3 x 848 [2,8,4]
+ CRUSH rule 3 x 849 [4,8,1]
+ CRUSH rule 3 x 850 [1,6]
+ CRUSH rule 3 x 851 [6,1]
+ CRUSH rule 3 x 852 [8,4,2]
+ CRUSH rule 3 x 853 [6,2]
+ CRUSH rule 3 x 854 [8,2]
+ CRUSH rule 3 x 855 [8,2]
+ CRUSH rule 3 x 856 [8,4,1]
+ CRUSH rule 3 x 857 [8,1]
+ CRUSH rule 3 x 858 [6,2]
+ CRUSH rule 3 x 859 [8,2,4]
+ CRUSH rule 3 x 860 [2,8]
+ CRUSH rule 3 x 861 [8,1]
+ CRUSH rule 3 x 862 [8,1]
+ CRUSH rule 3 x 863 [8,1]
+ CRUSH rule 3 x 864 [8,1]
+ CRUSH rule 3 x 865 [8,1]
+ CRUSH rule 3 x 866 [8,2]
+ CRUSH rule 3 x 867 [8,1]
+ CRUSH rule 3 x 868 [8,2]
+ CRUSH rule 3 x 869 [8,4,2]
+ CRUSH rule 3 x 870 [2,8]
+ CRUSH rule 3 x 871 [1,8]
+ CRUSH rule 3 x 872 [1,8]
+ CRUSH rule 3 x 873 [4,8,1]
+ CRUSH rule 3 x 874 [2,6]
+ CRUSH rule 3 x 875 [2,8,4]
+ CRUSH rule 3 x 876 [4,8,1]
+ CRUSH rule 3 x 877 [8,4,2]
+ CRUSH rule 3 x 878 [2,8]
+ CRUSH rule 3 x 879 [8,1]
+ CRUSH rule 3 x 880 [1,8]
+ CRUSH rule 3 x 881 [4,8,2]
+ CRUSH rule 3 x 882 [1,8]
+ CRUSH rule 3 x 883 [2,4,8]
+ CRUSH rule 3 x 884 [8,1,4]
+ CRUSH rule 3 x 885 [4,1,8]
+ CRUSH rule 3 x 886 [8,2]
+ CRUSH rule 3 x 887 [8,4,2]
+ CRUSH rule 3 x 888 [8,1]
+ CRUSH rule 3 x 889 [2,8]
+ CRUSH rule 3 x 890 [8,1,4]
+ CRUSH rule 3 x 891 [1,8]
+ CRUSH rule 3 x 892 [8,2,4]
+ CRUSH rule 3 x 893 [2,6]
+ CRUSH rule 3 x 894 [8,4,1]
+ CRUSH rule 3 x 895 [4,1,8]
+ CRUSH rule 3 x 896 [1,8]
+ CRUSH rule 3 x 897 [2,8]
+ CRUSH rule 3 x 898 [1,4,8]
+ CRUSH rule 3 x 899 [1,8]
+ CRUSH rule 3 x 900 [4,1,8]
+ CRUSH rule 3 x 901 [2,8]
+ CRUSH rule 3 x 902 [8,4,2]
+ CRUSH rule 3 x 903 [8,1]
+ CRUSH rule 3 x 904 [8,2]
+ CRUSH rule 3 x 905 [8,2]
+ CRUSH rule 3 x 906 [1,8]
+ CRUSH rule 3 x 907 [8,2]
+ CRUSH rule 3 x 908 [8,1]
+ CRUSH rule 3 x 909 [2,8]
+ CRUSH rule 3 x 910 [8,1]
+ CRUSH rule 3 x 911 [8,1]
+ CRUSH rule 3 x 912 [1,8]
+ CRUSH rule 3 x 913 [8,1,4]
+ CRUSH rule 3 x 914 [6,4,1]
+ CRUSH rule 3 x 915 [8,2]
+ CRUSH rule 3 x 916 [4,1,8]
+ CRUSH rule 3 x 917 [1,4,8]
+ CRUSH rule 3 x 918 [8,2]
+ CRUSH rule 3 x 919 [8,2]
+ CRUSH rule 3 x 920 [8,2]
+ CRUSH rule 3 x 921 [1,8]
+ CRUSH rule 3 x 922 [8,4,1]
+ CRUSH rule 3 x 923 [4,8,2]
+ CRUSH rule 3 x 924 [1,8]
+ CRUSH rule 3 x 925 [4,8,2]
+ CRUSH rule 3 x 926 [2,8]
+ CRUSH rule 3 x 927 [1,8,4]
+ CRUSH rule 3 x 928 [8,1]
+ CRUSH rule 3 x 929 [4,2,8]
+ CRUSH rule 3 x 930 [2,8]
+ CRUSH rule 3 x 931 [2,8]
+ CRUSH rule 3 x 932 [4,2,8]
+ CRUSH rule 3 x 933 [8,4,2]
+ CRUSH rule 3 x 934 [8,2]
+ CRUSH rule 3 x 935 [8,2]
+ CRUSH rule 3 x 936 [1,8]
+ CRUSH rule 3 x 937 [4,8,1]
+ CRUSH rule 3 x 938 [8,4,2]
+ CRUSH rule 3 x 939 [2,8,4]
+ CRUSH rule 3 x 940 [8,2]
+ CRUSH rule 3 x 941 [2,6]
+ CRUSH rule 3 x 942 [1,8]
+ CRUSH rule 3 x 943 [8,2]
+ CRUSH rule 3 x 944 [8,1]
+ CRUSH rule 3 x 945 [8,2,4]
+ CRUSH rule 3 x 946 [2,8,4]
+ CRUSH rule 3 x 947 [2,8]
+ CRUSH rule 3 x 948 [8,1]
+ CRUSH rule 3 x 949 [6,2]
+ CRUSH rule 3 x 950 [8,1]
+ CRUSH rule 3 x 951 [8,2]
+ CRUSH rule 3 x 952 [2,8,4]
+ CRUSH rule 3 x 953 [1,4,8]
+ CRUSH rule 3 x 954 [2,8]
+ CRUSH rule 3 x 955 [8,1,4]
+ CRUSH rule 3 x 956 [1,8,4]
+ CRUSH rule 3 x 957 [8,1,4]
+ CRUSH rule 3 x 958 [8,4,2]
+ CRUSH rule 3 x 959 [4,1,8]
+ CRUSH rule 3 x 960 [6,2]
+ CRUSH rule 3 x 961 [1,8]
+ CRUSH rule 3 x 962 [8,4,2]
+ CRUSH rule 3 x 963 [2,4,8]
+ CRUSH rule 3 x 964 [2,8]
+ CRUSH rule 3 x 965 [8,1]
+ CRUSH rule 3 x 966 [4,8,1]
+ CRUSH rule 3 x 967 [8,4,2]
+ CRUSH rule 3 x 968 [8,2]
+ CRUSH rule 3 x 969 [8,2,4]
+ CRUSH rule 3 x 970 [2,8,4]
+ CRUSH rule 3 x 971 [1,8]
+ CRUSH rule 3 x 972 [1,8]
+ CRUSH rule 3 x 973 [1,8]
+ CRUSH rule 3 x 974 [4,1,8]
+ CRUSH rule 3 x 975 [4,8,1]
+ CRUSH rule 3 x 976 [4,8,2]
+ CRUSH rule 3 x 977 [8,4,2]
+ CRUSH rule 3 x 978 [8,2,4]
+ CRUSH rule 3 x 979 [8,2,4]
+ CRUSH rule 3 x 980 [8,2,4]
+ CRUSH rule 3 x 981 [8,1]
+ CRUSH rule 3 x 982 [1,8]
+ CRUSH rule 3 x 983 [4,8,1]
+ CRUSH rule 3 x 984 [2,8]
+ CRUSH rule 3 x 985 [2,4,8]
+ CRUSH rule 3 x 986 [8,4,2]
+ CRUSH rule 3 x 987 [2,8]
+ CRUSH rule 3 x 988 [1,4,6]
+ CRUSH rule 3 x 989 [1,8]
+ CRUSH rule 3 x 990 [1,8,4]
+ CRUSH rule 3 x 991 [1,4,8]
+ CRUSH rule 3 x 992 [8,1,4]
+ CRUSH rule 3 x 993 [2,8,4]
+ CRUSH rule 3 x 994 [4,8,1]
+ CRUSH rule 3 x 995 [8,1,4]
+ CRUSH rule 3 x 996 [8,4,1]
+ CRUSH rule 3 x 997 [8,4,1]
+ CRUSH rule 3 x 998 [8,1,4]
+ CRUSH rule 3 x 999 [1,8,4]
+ CRUSH rule 3 x 1000 [8,4,1]
+ CRUSH rule 3 x 1001 [2,8]
+ CRUSH rule 3 x 1002 [1,8]
+ CRUSH rule 3 x 1003 [2,8]
+ CRUSH rule 3 x 1004 [8,1,4]
+ CRUSH rule 3 x 1005 [8,1]
+ CRUSH rule 3 x 1006 [1,8,4]
+ CRUSH rule 3 x 1007 [1,4,8]
+ CRUSH rule 3 x 1008 [1,8]
+ CRUSH rule 3 x 1009 [6,4,1]
+ CRUSH rule 3 x 1010 [1,8]
+ CRUSH rule 3 x 1011 [4,2,8]
+ CRUSH rule 3 x 1012 [1,8]
+ CRUSH rule 3 x 1013 [2,8]
+ CRUSH rule 3 x 1014 [2,8,4]
+ CRUSH rule 3 x 1015 [8,2]
+ CRUSH rule 3 x 1016 [2,4,8]
+ CRUSH rule 3 x 1017 [6,1,4]
+ CRUSH rule 3 x 1018 [4,1,8]
+ CRUSH rule 3 x 1019 [4,8,1]
+ CRUSH rule 3 x 1020 [1,8]
+ CRUSH rule 3 x 1021 [2,8]
+ CRUSH rule 3 x 1022 [1,8,4]
+ CRUSH rule 3 x 1023 [4,2,8]
+ rule 3 (choose-set) num_rep 3 result size == 2:\t501/1024 (esc)
+ rule 3 (choose-set) num_rep 3 result size == 3:\t523/1024 (esc)
+ rule 4 (choose-set-two), x = 0..1023, numrep = 2..3
+ CRUSH rule 4 x 0 [2,1]
+ CRUSH rule 4 x 1 [2,8]
+ CRUSH rule 4 x 2 [1]
+ CRUSH rule 4 x 3 [8,1]
+ CRUSH rule 4 x 4 [4]
+ CRUSH rule 4 x 5 [8]
+ CRUSH rule 4 x 6 [2,8]
+ CRUSH rule 4 x 7 [4]
+ CRUSH rule 4 x 8 [4]
+ CRUSH rule 4 x 9 [2,4]
+ CRUSH rule 4 x 10 [2,1]
+ CRUSH rule 4 x 11 [2,8]
+ CRUSH rule 4 x 12 [2,1]
+ CRUSH rule 4 x 13 [4,8]
+ CRUSH rule 4 x 14 [8]
+ CRUSH rule 4 x 15 [8,2]
+ CRUSH rule 4 x 16 [8]
+ CRUSH rule 4 x 17 [4]
+ CRUSH rule 4 x 18 [1]
+ CRUSH rule 4 x 19 [8,4]
+ CRUSH rule 4 x 20 [2]
+ CRUSH rule 4 x 21 [8]
+ CRUSH rule 4 x 22 [8]
+ CRUSH rule 4 x 23 [4,8]
+ CRUSH rule 4 x 24 [1,2]
+ CRUSH rule 4 x 25 [4,8]
+ CRUSH rule 4 x 26 [2,8]
+ CRUSH rule 4 x 27 [4,1]
+ CRUSH rule 4 x 28 [8,2]
+ CRUSH rule 4 x 29 [8,4]
+ CRUSH rule 4 x 30 [4,8]
+ CRUSH rule 4 x 31 [8]
+ CRUSH rule 4 x 32 [6]
+ CRUSH rule 4 x 33 [2,8]
+ CRUSH rule 4 x 34 [2]
+ CRUSH rule 4 x 35 [1,8]
+ CRUSH rule 4 x 36 [8]
+ CRUSH rule 4 x 37 [1]
+ CRUSH rule 4 x 38 [4,8]
+ CRUSH rule 4 x 39 [8]
+ CRUSH rule 4 x 40 [8]
+ CRUSH rule 4 x 41 [2,1]
+ CRUSH rule 4 x 42 []
+ CRUSH rule 4 x 43 [1]
+ CRUSH rule 4 x 44 [1,8]
+ CRUSH rule 4 x 45 [8,2]
+ CRUSH rule 4 x 46 [2]
+ CRUSH rule 4 x 47 [4]
+ CRUSH rule 4 x 48 [8]
+ CRUSH rule 4 x 49 []
+ CRUSH rule 4 x 50 [4]
+ CRUSH rule 4 x 51 [8]
+ CRUSH rule 4 x 52 [8]
+ CRUSH rule 4 x 53 [4]
+ CRUSH rule 4 x 54 [8]
+ CRUSH rule 4 x 55 [8]
+ CRUSH rule 4 x 56 [8,4]
+ CRUSH rule 4 x 57 []
+ CRUSH rule 4 x 58 [1,2]
+ CRUSH rule 4 x 59 [2]
+ CRUSH rule 4 x 60 [4]
+ CRUSH rule 4 x 61 [4,8]
+ CRUSH rule 4 x 62 [8,1]
+ CRUSH rule 4 x 63 [8]
+ CRUSH rule 4 x 64 [4]
+ CRUSH rule 4 x 65 [8,4]
+ CRUSH rule 4 x 66 [4]
+ CRUSH rule 4 x 67 [4,2]
+ CRUSH rule 4 x 68 [1]
+ CRUSH rule 4 x 69 [1]
+ CRUSH rule 4 x 70 [8,2]
+ CRUSH rule 4 x 71 [2,8]
+ CRUSH rule 4 x 72 [8,1]
+ CRUSH rule 4 x 73 [2,8]
+ CRUSH rule 4 x 74 [1,8]
+ CRUSH rule 4 x 75 [4,2]
+ CRUSH rule 4 x 76 [4,1]
+ CRUSH rule 4 x 77 [8,2]
+ CRUSH rule 4 x 78 [1]
+ CRUSH rule 4 x 79 [4]
+ CRUSH rule 4 x 80 [2,4]
+ CRUSH rule 4 x 81 [2,1]
+ CRUSH rule 4 x 82 [6,1]
+ CRUSH rule 4 x 83 [2,8]
+ CRUSH rule 4 x 84 [8,2]
+ CRUSH rule 4 x 85 [4]
+ CRUSH rule 4 x 86 [2,1]
+ CRUSH rule 4 x 87 [2,8]
+ CRUSH rule 4 x 88 [1,6]
+ CRUSH rule 4 x 89 [2]
+ CRUSH rule 4 x 90 [8]
+ CRUSH rule 4 x 91 [4,8]
+ CRUSH rule 4 x 92 [1,8]
+ CRUSH rule 4 x 93 [8,4]
+ CRUSH rule 4 x 94 [1]
+ CRUSH rule 4 x 95 [8]
+ CRUSH rule 4 x 96 [8]
+ CRUSH rule 4 x 97 [8]
+ CRUSH rule 4 x 98 [2,1]
+ CRUSH rule 4 x 99 [2,8]
+ CRUSH rule 4 x 100 [1,8]
+ CRUSH rule 4 x 101 [8]
+ CRUSH rule 4 x 102 [2]
+ CRUSH rule 4 x 103 [8]
+ CRUSH rule 4 x 104 [8,4]
+ CRUSH rule 4 x 105 [2,4]
+ CRUSH rule 4 x 106 [1,8]
+ CRUSH rule 4 x 107 [2]
+ CRUSH rule 4 x 108 [8,2]
+ CRUSH rule 4 x 109 [1,2]
+ CRUSH rule 4 x 110 [4,2]
+ CRUSH rule 4 x 111 [2,1]
+ CRUSH rule 4 x 112 [2,1]
+ CRUSH rule 4 x 113 [8,2]
+ CRUSH rule 4 x 114 [8]
+ CRUSH rule 4 x 115 [8,2]
+ CRUSH rule 4 x 116 [1,2]
+ CRUSH rule 4 x 117 [6]
+ CRUSH rule 4 x 118 [2]
+ CRUSH rule 4 x 119 [8]
+ CRUSH rule 4 x 120 [2,1]
+ CRUSH rule 4 x 121 [2,1]
+ CRUSH rule 4 x 122 [8]
+ CRUSH rule 4 x 123 [2]
+ CRUSH rule 4 x 124 []
+ CRUSH rule 4 x 125 [1,8]
+ CRUSH rule 4 x 126 [2]
+ CRUSH rule 4 x 127 [4,8]
+ CRUSH rule 4 x 128 []
+ CRUSH rule 4 x 129 [2,1]
+ CRUSH rule 4 x 130 [4,8]
+ CRUSH rule 4 x 131 [1,2]
+ CRUSH rule 4 x 132 [1,2]
+ CRUSH rule 4 x 133 [8]
+ CRUSH rule 4 x 134 [1,8]
+ CRUSH rule 4 x 135 [4,8]
+ CRUSH rule 4 x 136 [2,1]
+ CRUSH rule 4 x 137 [8,4]
+ CRUSH rule 4 x 138 [8]
+ CRUSH rule 4 x 139 [4,2]
+ CRUSH rule 4 x 140 [1,8]
+ CRUSH rule 4 x 141 [8]
+ CRUSH rule 4 x 142 [4]
+ CRUSH rule 4 x 143 [4,8]
+ CRUSH rule 4 x 144 [8,1]
+ CRUSH rule 4 x 145 [8]
+ CRUSH rule 4 x 146 [2,8]
+ CRUSH rule 4 x 147 [2,8]
+ CRUSH rule 4 x 148 [4]
+ CRUSH rule 4 x 149 [4,8]
+ CRUSH rule 4 x 150 [1,8]
+ CRUSH rule 4 x 151 []
+ CRUSH rule 4 x 152 [8]
+ CRUSH rule 4 x 153 [8]
+ CRUSH rule 4 x 154 [4,2]
+ CRUSH rule 4 x 155 [4]
+ CRUSH rule 4 x 156 [4]
+ CRUSH rule 4 x 157 [1]
+ CRUSH rule 4 x 158 [2,8]
+ CRUSH rule 4 x 159 [8,2]
+ CRUSH rule 4 x 160 [2,8]
+ CRUSH rule 4 x 161 [1,4]
+ CRUSH rule 4 x 162 [1,8]
+ CRUSH rule 4 x 163 [4,8]
+ CRUSH rule 4 x 164 [8]
+ CRUSH rule 4 x 165 [8,2]
+ CRUSH rule 4 x 166 [2]
+ CRUSH rule 4 x 167 [1,2]
+ CRUSH rule 4 x 168 [4,2]
+ CRUSH rule 4 x 169 [2,8]
+ CRUSH rule 4 x 170 [1,2]
+ CRUSH rule 4 x 171 [8,4]
+ CRUSH rule 4 x 172 [1,8]
+ CRUSH rule 4 x 173 [8,4]
+ CRUSH rule 4 x 174 [1]
+ CRUSH rule 4 x 175 [8,1]
+ CRUSH rule 4 x 176 []
+ CRUSH rule 4 x 177 []
+ CRUSH rule 4 x 178 [4,2]
+ CRUSH rule 4 x 179 [1]
+ CRUSH rule 4 x 180 [8]
+ CRUSH rule 4 x 181 [8,2]
+ CRUSH rule 4 x 182 [8]
+ CRUSH rule 4 x 183 [8]
+ CRUSH rule 4 x 184 [4,8]
+ CRUSH rule 4 x 185 [8]
+ CRUSH rule 4 x 186 [2,1]
+ CRUSH rule 4 x 187 [1,8]
+ CRUSH rule 4 x 188 [1,8]
+ CRUSH rule 4 x 189 [1,8]
+ CRUSH rule 4 x 190 [1]
+ CRUSH rule 4 x 191 [8]
+ CRUSH rule 4 x 192 [4,1]
+ CRUSH rule 4 x 193 [4,2]
+ CRUSH rule 4 x 194 [1]
+ CRUSH rule 4 x 195 [8,4]
+ CRUSH rule 4 x 196 [8]
+ CRUSH rule 4 x 197 [8,4]
+ CRUSH rule 4 x 198 [2]
+ CRUSH rule 4 x 199 [1,4]
+ CRUSH rule 4 x 200 [1]
+ CRUSH rule 4 x 201 [8,1]
+ CRUSH rule 4 x 202 [8]
+ CRUSH rule 4 x 203 [8]
+ CRUSH rule 4 x 204 [2,1]
+ CRUSH rule 4 x 205 [1,8]
+ CRUSH rule 4 x 206 [1,2]
+ CRUSH rule 4 x 207 [2]
+ CRUSH rule 4 x 208 [8,1]
+ CRUSH rule 4 x 209 [1,2]
+ CRUSH rule 4 x 210 [1,2]
+ CRUSH rule 4 x 211 [4]
+ CRUSH rule 4 x 212 [8]
+ CRUSH rule 4 x 213 [8,4]
+ CRUSH rule 4 x 214 []
+ CRUSH rule 4 x 215 [8,1]
+ CRUSH rule 4 x 216 [1]
+ CRUSH rule 4 x 217 [1,2]
+ CRUSH rule 4 x 218 [2,8]
+ CRUSH rule 4 x 219 [8]
+ CRUSH rule 4 x 220 [4,8]
+ CRUSH rule 4 x 221 [8]
+ CRUSH rule 4 x 222 [8]
+ CRUSH rule 4 x 223 [1]
+ CRUSH rule 4 x 224 [1,4]
+ CRUSH rule 4 x 225 [8]
+ CRUSH rule 4 x 226 [8,2]
+ CRUSH rule 4 x 227 [4]
+ CRUSH rule 4 x 228 []
+ CRUSH rule 4 x 229 [4]
+ CRUSH rule 4 x 230 [4,8]
+ CRUSH rule 4 x 231 [4]
+ CRUSH rule 4 x 232 [2,8]
+ CRUSH rule 4 x 233 []
+ CRUSH rule 4 x 234 [1,2]
+ CRUSH rule 4 x 235 [4,8]
+ CRUSH rule 4 x 236 [2]
+ CRUSH rule 4 x 237 [4,8]
+ CRUSH rule 4 x 238 []
+ CRUSH rule 4 x 239 [8,6]
+ CRUSH rule 4 x 240 [4,8]
+ CRUSH rule 4 x 241 [1]
+ CRUSH rule 4 x 242 []
+ CRUSH rule 4 x 243 [8]
+ CRUSH rule 4 x 244 [4,8]
+ CRUSH rule 4 x 245 [8]
+ CRUSH rule 4 x 246 [1]
+ CRUSH rule 4 x 247 [8,2]
+ CRUSH rule 4 x 248 [8,2]
+ CRUSH rule 4 x 249 [2,1]
+ CRUSH rule 4 x 250 [2,1]
+ CRUSH rule 4 x 251 [2]
+ CRUSH rule 4 x 252 [4,8]
+ CRUSH rule 4 x 253 [2]
+ CRUSH rule 4 x 254 [4]
+ CRUSH rule 4 x 255 [1,8]
+ CRUSH rule 4 x 256 [4,8]
+ CRUSH rule 4 x 257 [2,8]
+ CRUSH rule 4 x 258 [4]
+ CRUSH rule 4 x 259 []
+ CRUSH rule 4 x 260 [8]
+ CRUSH rule 4 x 261 [8]
+ CRUSH rule 4 x 262 []
+ CRUSH rule 4 x 263 [8]
+ CRUSH rule 4 x 264 [8]
+ CRUSH rule 4 x 265 [8]
+ CRUSH rule 4 x 266 [8,2]
+ CRUSH rule 4 x 267 [2]
+ CRUSH rule 4 x 268 [1,8]
+ CRUSH rule 4 x 269 [1,8]
+ CRUSH rule 4 x 270 [4,1]
+ CRUSH rule 4 x 271 [8,4]
+ CRUSH rule 4 x 272 [2,8]
+ CRUSH rule 4 x 273 [4]
+ CRUSH rule 4 x 274 [8]
+ CRUSH rule 4 x 275 [4]
+ CRUSH rule 4 x 276 [8,1]
+ CRUSH rule 4 x 277 [8]
+ CRUSH rule 4 x 278 [8]
+ CRUSH rule 4 x 279 [8,4]
+ CRUSH rule 4 x 280 [2,8]
+ CRUSH rule 4 x 281 [8,2]
+ CRUSH rule 4 x 282 [1]
+ CRUSH rule 4 x 283 [8,2]
+ CRUSH rule 4 x 284 [8]
+ CRUSH rule 4 x 285 [4]
+ CRUSH rule 4 x 286 [2,1]
+ CRUSH rule 4 x 287 [1]
+ CRUSH rule 4 x 288 [8,1]
+ CRUSH rule 4 x 289 [4,8]
+ CRUSH rule 4 x 290 [1,4]
+ CRUSH rule 4 x 291 [1,2]
+ CRUSH rule 4 x 292 [8,2]
+ CRUSH rule 4 x 293 [8,1]
+ CRUSH rule 4 x 294 [8,4]
+ CRUSH rule 4 x 295 [4,8]
+ CRUSH rule 4 x 296 [4,1]
+ CRUSH rule 4 x 297 [8,2]
+ CRUSH rule 4 x 298 [1,2]
+ CRUSH rule 4 x 299 [2,1]
+ CRUSH rule 4 x 300 [8]
+ CRUSH rule 4 x 301 [1,8]
+ CRUSH rule 4 x 302 [1]
+ CRUSH rule 4 x 303 [8,4]
+ CRUSH rule 4 x 304 [2,8]
+ CRUSH rule 4 x 305 [8]
+ CRUSH rule 4 x 306 [1,8]
+ CRUSH rule 4 x 307 [2,1]
+ CRUSH rule 4 x 308 [2,8]
+ CRUSH rule 4 x 309 [8]
+ CRUSH rule 4 x 310 [4]
+ CRUSH rule 4 x 311 [4]
+ CRUSH rule 4 x 312 [2,1]
+ CRUSH rule 4 x 313 [4]
+ CRUSH rule 4 x 314 []
+ CRUSH rule 4 x 315 [2,1]
+ CRUSH rule 4 x 316 [8]
+ CRUSH rule 4 x 317 [2,8]
+ CRUSH rule 4 x 318 [8]
+ CRUSH rule 4 x 319 [2]
+ CRUSH rule 4 x 320 [8]
+ CRUSH rule 4 x 321 [1]
+ CRUSH rule 4 x 322 [2,8]
+ CRUSH rule 4 x 323 [4,8]
+ CRUSH rule 4 x 324 [8,1]
+ CRUSH rule 4 x 325 [4,8]
+ CRUSH rule 4 x 326 []
+ CRUSH rule 4 x 327 [1,8]
+ CRUSH rule 4 x 328 [8,4]
+ CRUSH rule 4 x 329 [4,8]
+ CRUSH rule 4 x 330 [4,8]
+ CRUSH rule 4 x 331 [2,8]
+ CRUSH rule 4 x 332 [2,1]
+ CRUSH rule 4 x 333 [8]
+ CRUSH rule 4 x 334 [8]
+ CRUSH rule 4 x 335 [8,1]
+ CRUSH rule 4 x 336 [4,8]
+ CRUSH rule 4 x 337 [8,2]
+ CRUSH rule 4 x 338 [8]
+ CRUSH rule 4 x 339 [8]
+ CRUSH rule 4 x 340 [2,1]
+ CRUSH rule 4 x 341 [4,1]
+ CRUSH rule 4 x 342 [2,8]
+ CRUSH rule 4 x 343 [8]
+ CRUSH rule 4 x 344 [6,2]
+ CRUSH rule 4 x 345 []
+ CRUSH rule 4 x 346 [8,2]
+ CRUSH rule 4 x 347 [4,1]
+ CRUSH rule 4 x 348 [8,2]
+ CRUSH rule 4 x 349 [1,8]
+ CRUSH rule 4 x 350 [8]
+ CRUSH rule 4 x 351 [8]
+ CRUSH rule 4 x 352 [1,2]
+ CRUSH rule 4 x 353 [8]
+ CRUSH rule 4 x 354 [1]
+ CRUSH rule 4 x 355 []
+ CRUSH rule 4 x 356 [4]
+ CRUSH rule 4 x 357 [8,1]
+ CRUSH rule 4 x 358 [2,1]
+ CRUSH rule 4 x 359 [6,8]
+ CRUSH rule 4 x 360 []
+ CRUSH rule 4 x 361 [8,4]
+ CRUSH rule 4 x 362 [4]
+ CRUSH rule 4 x 363 [4,1]
+ CRUSH rule 4 x 364 [2]
+ CRUSH rule 4 x 365 [8]
+ CRUSH rule 4 x 366 [8,2]
+ CRUSH rule 4 x 367 [4]
+ CRUSH rule 4 x 368 [8,4]
+ CRUSH rule 4 x 369 [8]
+ CRUSH rule 4 x 370 [8]
+ CRUSH rule 4 x 371 [1,4]
+ CRUSH rule 4 x 372 [1]
+ CRUSH rule 4 x 373 [1,8]
+ CRUSH rule 4 x 374 [8]
+ CRUSH rule 4 x 375 [8,4]
+ CRUSH rule 4 x 376 [8,1]
+ CRUSH rule 4 x 377 [1,2]
+ CRUSH rule 4 x 378 [1,2]
+ CRUSH rule 4 x 379 [8]
+ CRUSH rule 4 x 380 [2]
+ CRUSH rule 4 x 381 [1,4]
+ CRUSH rule 4 x 382 [1,4]
+ CRUSH rule 4 x 383 [4]
+ CRUSH rule 4 x 384 [8,2]
+ CRUSH rule 4 x 385 [8]
+ CRUSH rule 4 x 386 [1]
+ CRUSH rule 4 x 387 [1,4]
+ CRUSH rule 4 x 388 [1]
+ CRUSH rule 4 x 389 [1,4]
+ CRUSH rule 4 x 390 [4,8]
+ CRUSH rule 4 x 391 [4,8]
+ CRUSH rule 4 x 392 [1,8]
+ CRUSH rule 4 x 393 [2]
+ CRUSH rule 4 x 394 [8]
+ CRUSH rule 4 x 395 [1]
+ CRUSH rule 4 x 396 [4,2]
+ CRUSH rule 4 x 397 [2,1]
+ CRUSH rule 4 x 398 [2,4]
+ CRUSH rule 4 x 399 [8]
+ CRUSH rule 4 x 400 [8,1]
+ CRUSH rule 4 x 401 [1,2]
+ CRUSH rule 4 x 402 [8]
+ CRUSH rule 4 x 403 [1,2]
+ CRUSH rule 4 x 404 [4]
+ CRUSH rule 4 x 405 [8,4]
+ CRUSH rule 4 x 406 [2,1]
+ CRUSH rule 4 x 407 [2,8]
+ CRUSH rule 4 x 408 [4,1]
+ CRUSH rule 4 x 409 [8,4]
+ CRUSH rule 4 x 410 [8]
+ CRUSH rule 4 x 411 [2,1]
+ CRUSH rule 4 x 412 [2]
+ CRUSH rule 4 x 413 [2]
+ CRUSH rule 4 x 414 [4,1]
+ CRUSH rule 4 x 415 [2,8]
+ CRUSH rule 4 x 416 [2,1]
+ CRUSH rule 4 x 417 [8,6]
+ CRUSH rule 4 x 418 [8]
+ CRUSH rule 4 x 419 [8,4]
+ CRUSH rule 4 x 420 [1,4]
+ CRUSH rule 4 x 421 [8]
+ CRUSH rule 4 x 422 [6,8]
+ CRUSH rule 4 x 423 [2,4]
+ CRUSH rule 4 x 424 [8]
+ CRUSH rule 4 x 425 [1]
+ CRUSH rule 4 x 426 [8]
+ CRUSH rule 4 x 427 [1,8]
+ CRUSH rule 4 x 428 [4]
+ CRUSH rule 4 x 429 [4,8]
+ CRUSH rule 4 x 430 [4,8]
+ CRUSH rule 4 x 431 [4]
+ CRUSH rule 4 x 432 [8,1]
+ CRUSH rule 4 x 433 [8]
+ CRUSH rule 4 x 434 [2]
+ CRUSH rule 4 x 435 [2]
+ CRUSH rule 4 x 436 [4,1]
+ CRUSH rule 4 x 437 [8]
+ CRUSH rule 4 x 438 [2,4]
+ CRUSH rule 4 x 439 [1]
+ CRUSH rule 4 x 440 [2,8]
+ CRUSH rule 4 x 441 [4,6]
+ CRUSH rule 4 x 442 [2]
+ CRUSH rule 4 x 443 [8]
+ CRUSH rule 4 x 444 [8,1]
+ CRUSH rule 4 x 445 [8]
+ CRUSH rule 4 x 446 []
+ CRUSH rule 4 x 447 [2,1]
+ CRUSH rule 4 x 448 [8,2]
+ CRUSH rule 4 x 449 [8,6]
+ CRUSH rule 4 x 450 []
+ CRUSH rule 4 x 451 [8]
+ CRUSH rule 4 x 452 [8]
+ CRUSH rule 4 x 453 [6,8]
+ CRUSH rule 4 x 454 [8]
+ CRUSH rule 4 x 455 [2,8]
+ CRUSH rule 4 x 456 [8]
+ CRUSH rule 4 x 457 [8,2]
+ CRUSH rule 4 x 458 [2,8]
+ CRUSH rule 4 x 459 [2,1]
+ CRUSH rule 4 x 460 [8]
+ CRUSH rule 4 x 461 [8]
+ CRUSH rule 4 x 462 [8,1]
+ CRUSH rule 4 x 463 [8]
+ CRUSH rule 4 x 464 [8,4]
+ CRUSH rule 4 x 465 [6,8]
+ CRUSH rule 4 x 466 [8]
+ CRUSH rule 4 x 467 [8]
+ CRUSH rule 4 x 468 [8]
+ CRUSH rule 4 x 469 [8,1]
+ CRUSH rule 4 x 470 [4,2]
+ CRUSH rule 4 x 471 [1,2]
+ CRUSH rule 4 x 472 []
+ CRUSH rule 4 x 473 [1,2]
+ CRUSH rule 4 x 474 [8,1]
+ CRUSH rule 4 x 475 [8]
+ CRUSH rule 4 x 476 [4]
+ CRUSH rule 4 x 477 [4,8]
+ CRUSH rule 4 x 478 [8]
+ CRUSH rule 4 x 479 [2]
+ CRUSH rule 4 x 480 [1,8]
+ CRUSH rule 4 x 481 [2,4]
+ CRUSH rule 4 x 482 []
+ CRUSH rule 4 x 483 [2,1]
+ CRUSH rule 4 x 484 [1,2]
+ CRUSH rule 4 x 485 [8]
+ CRUSH rule 4 x 486 [4,1]
+ CRUSH rule 4 x 487 [1]
+ CRUSH rule 4 x 488 [8]
+ CRUSH rule 4 x 489 [2,8]
+ CRUSH rule 4 x 490 [6]
+ CRUSH rule 4 x 491 [1,2]
+ CRUSH rule 4 x 492 [8]
+ CRUSH rule 4 x 493 [2,1]
+ CRUSH rule 4 x 494 [1,2]
+ CRUSH rule 4 x 495 [4]
+ CRUSH rule 4 x 496 [8,4]
+ CRUSH rule 4 x 497 [4,8]
+ CRUSH rule 4 x 498 [2,4]
+ CRUSH rule 4 x 499 [8,4]
+ CRUSH rule 4 x 500 [4,8]
+ CRUSH rule 4 x 501 [2,8]
+ CRUSH rule 4 x 502 [6,1]
+ CRUSH rule 4 x 503 [2]
+ CRUSH rule 4 x 504 [8]
+ CRUSH rule 4 x 505 [1,8]
+ CRUSH rule 4 x 506 [4]
+ CRUSH rule 4 x 507 [8,1]
+ CRUSH rule 4 x 508 [1,2]
+ CRUSH rule 4 x 509 [8]
+ CRUSH rule 4 x 510 [8,2]
+ CRUSH rule 4 x 511 [4,8]
+ CRUSH rule 4 x 512 [8]
+ CRUSH rule 4 x 513 [8,2]
+ CRUSH rule 4 x 514 []
+ CRUSH rule 4 x 515 [8,4]
+ CRUSH rule 4 x 516 [4,1]
+ CRUSH rule 4 x 517 [8]
+ CRUSH rule 4 x 518 [4,8]
+ CRUSH rule 4 x 519 [8,4]
+ CRUSH rule 4 x 520 [2,8]
+ CRUSH rule 4 x 521 [8]
+ CRUSH rule 4 x 522 [8]
+ CRUSH rule 4 x 523 [4,2]
+ CRUSH rule 4 x 524 [2]
+ CRUSH rule 4 x 525 [2]
+ CRUSH rule 4 x 526 [1]
+ CRUSH rule 4 x 527 [1,2]
+ CRUSH rule 4 x 528 []
+ CRUSH rule 4 x 529 [4,8]
+ CRUSH rule 4 x 530 [8]
+ CRUSH rule 4 x 531 [8,1]
+ CRUSH rule 4 x 532 [6,4]
+ CRUSH rule 4 x 533 [4,8]
+ CRUSH rule 4 x 534 [8]
+ CRUSH rule 4 x 535 [8,6]
+ CRUSH rule 4 x 536 [8]
+ CRUSH rule 4 x 537 [4,8]
+ CRUSH rule 4 x 538 [8]
+ CRUSH rule 4 x 539 [8]
+ CRUSH rule 4 x 540 [1,8]
+ CRUSH rule 4 x 541 [2,4]
+ CRUSH rule 4 x 542 []
+ CRUSH rule 4 x 543 [8,2]
+ CRUSH rule 4 x 544 [4,8]
+ CRUSH rule 4 x 545 [8]
+ CRUSH rule 4 x 546 [8,1]
+ CRUSH rule 4 x 547 [8,2]
+ CRUSH rule 4 x 548 [4,2]
+ CRUSH rule 4 x 549 [8]
+ CRUSH rule 4 x 550 [2,4]
+ CRUSH rule 4 x 551 [8]
+ CRUSH rule 4 x 552 [4]
+ CRUSH rule 4 x 553 [2]
+ CRUSH rule 4 x 554 [1,8]
+ CRUSH rule 4 x 555 [4,1]
+ CRUSH rule 4 x 556 []
+ CRUSH rule 4 x 557 [8]
+ CRUSH rule 4 x 558 [4,1]
+ CRUSH rule 4 x 559 [2]
+ CRUSH rule 4 x 560 [8]
+ CRUSH rule 4 x 561 [8,4]
+ CRUSH rule 4 x 562 [4]
+ CRUSH rule 4 x 563 [2,8]
+ CRUSH rule 4 x 564 [1]
+ CRUSH rule 4 x 565 [4,8]
+ CRUSH rule 4 x 566 [4,8]
+ CRUSH rule 4 x 567 [4,8]
+ CRUSH rule 4 x 568 [8]
+ CRUSH rule 4 x 569 [4]
+ CRUSH rule 4 x 570 [1]
+ CRUSH rule 4 x 571 [8]
+ CRUSH rule 4 x 572 [4]
+ CRUSH rule 4 x 573 [1]
+ CRUSH rule 4 x 574 [2,1]
+ CRUSH rule 4 x 575 [8]
+ CRUSH rule 4 x 576 [4,8]
+ CRUSH rule 4 x 577 [8,2]
+ CRUSH rule 4 x 578 [8]
+ CRUSH rule 4 x 579 [4,1]
+ CRUSH rule 4 x 580 [2]
+ CRUSH rule 4 x 581 [8,2]
+ CRUSH rule 4 x 582 [2,8]
+ CRUSH rule 4 x 583 [8,1]
+ CRUSH rule 4 x 584 [8,1]
+ CRUSH rule 4 x 585 [8,1]
+ CRUSH rule 4 x 586 [1,2]
+ CRUSH rule 4 x 587 [2,4]
+ CRUSH rule 4 x 588 [4]
+ CRUSH rule 4 x 589 [8,1]
+ CRUSH rule 4 x 590 [8,2]
+ CRUSH rule 4 x 591 [4,2]
+ CRUSH rule 4 x 592 [2,1]
+ CRUSH rule 4 x 593 [1,8]
+ CRUSH rule 4 x 594 [2,8]
+ CRUSH rule 4 x 595 [8,1]
+ CRUSH rule 4 x 596 []
+ CRUSH rule 4 x 597 [1]
+ CRUSH rule 4 x 598 [2]
+ CRUSH rule 4 x 599 [4,2]
+ CRUSH rule 4 x 600 [8,1]
+ CRUSH rule 4 x 601 [1,8]
+ CRUSH rule 4 x 602 [8]
+ CRUSH rule 4 x 603 [1]
+ CRUSH rule 4 x 604 [8]
+ CRUSH rule 4 x 605 [2]
+ CRUSH rule 4 x 606 [2,1]
+ CRUSH rule 4 x 607 [2,4]
+ CRUSH rule 4 x 608 [4]
+ CRUSH rule 4 x 609 [4,2]
+ CRUSH rule 4 x 610 [8]
+ CRUSH rule 4 x 611 [1,2]
+ CRUSH rule 4 x 612 [2,1]
+ CRUSH rule 4 x 613 [8,2]
+ CRUSH rule 4 x 614 [8]
+ CRUSH rule 4 x 615 [8]
+ CRUSH rule 4 x 616 [1,8]
+ CRUSH rule 4 x 617 [8,1]
+ CRUSH rule 4 x 618 [8]
+ CRUSH rule 4 x 619 [4,1]
+ CRUSH rule 4 x 620 [1]
+ CRUSH rule 4 x 621 [8]
+ CRUSH rule 4 x 622 [2,4]
+ CRUSH rule 4 x 623 [2,8]
+ CRUSH rule 4 x 624 [4]
+ CRUSH rule 4 x 625 [2]
+ CRUSH rule 4 x 626 [8]
+ CRUSH rule 4 x 627 [2,8]
+ CRUSH rule 4 x 628 [8,2]
+ CRUSH rule 4 x 629 [2,8]
+ CRUSH rule 4 x 630 [2,8]
+ CRUSH rule 4 x 631 [1,8]
+ CRUSH rule 4 x 632 [8,2]
+ CRUSH rule 4 x 633 [8]
+ CRUSH rule 4 x 634 [1]
+ CRUSH rule 4 x 635 [4,8]
+ CRUSH rule 4 x 636 [1,4]
+ CRUSH rule 4 x 637 [1]
+ CRUSH rule 4 x 638 [8]
+ CRUSH rule 4 x 639 [2]
+ CRUSH rule 4 x 640 [1]
+ CRUSH rule 4 x 641 [8,2]
+ CRUSH rule 4 x 642 [2,1]
+ CRUSH rule 4 x 643 []
+ CRUSH rule 4 x 644 [8,1]
+ CRUSH rule 4 x 645 []
+ CRUSH rule 4 x 646 [8,1]
+ CRUSH rule 4 x 647 [8,1]
+ CRUSH rule 4 x 648 [1,8]
+ CRUSH rule 4 x 649 [4,8]
+ CRUSH rule 4 x 650 [8]
+ CRUSH rule 4 x 651 [4,6]
+ CRUSH rule 4 x 652 [4]
+ CRUSH rule 4 x 653 [8]
+ CRUSH rule 4 x 654 [6]
+ CRUSH rule 4 x 655 [1,4]
+ CRUSH rule 4 x 656 []
+ CRUSH rule 4 x 657 [6,1]
+ CRUSH rule 4 x 658 []
+ CRUSH rule 4 x 659 [4,8]
+ CRUSH rule 4 x 660 [8]
+ CRUSH rule 4 x 661 [1,8]
+ CRUSH rule 4 x 662 []
+ CRUSH rule 4 x 663 [1,4]
+ CRUSH rule 4 x 664 [1,4]
+ CRUSH rule 4 x 665 [4,6]
+ CRUSH rule 4 x 666 [2,8]
+ CRUSH rule 4 x 667 [1,4]
+ CRUSH rule 4 x 668 [4,8]
+ CRUSH rule 4 x 669 [6,4]
+ CRUSH rule 4 x 670 [4,2]
+ CRUSH rule 4 x 671 [2,1]
+ CRUSH rule 4 x 672 [4]
+ CRUSH rule 4 x 673 [4]
+ CRUSH rule 4 x 674 [1]
+ CRUSH rule 4 x 675 [1,8]
+ CRUSH rule 4 x 676 [2,1]
+ CRUSH rule 4 x 677 [4,1]
+ CRUSH rule 4 x 678 [2,4]
+ CRUSH rule 4 x 679 [8,2]
+ CRUSH rule 4 x 680 [2]
+ CRUSH rule 4 x 681 [8]
+ CRUSH rule 4 x 682 [1,4]
+ CRUSH rule 4 x 683 [1,2]
+ CRUSH rule 4 x 684 [8,1]
+ CRUSH rule 4 x 685 [8,1]
+ CRUSH rule 4 x 686 [1,4]
+ CRUSH rule 4 x 687 []
+ CRUSH rule 4 x 688 [4,8]
+ CRUSH rule 4 x 689 [8,4]
+ CRUSH rule 4 x 690 [8,1]
+ CRUSH rule 4 x 691 [1]
+ CRUSH rule 4 x 692 [8,2]
+ CRUSH rule 4 x 693 [8]
+ CRUSH rule 4 x 694 [8,4]
+ CRUSH rule 4 x 695 [2,8]
+ CRUSH rule 4 x 696 [1]
+ CRUSH rule 4 x 697 [8,1]
+ CRUSH rule 4 x 698 [8,2]
+ CRUSH rule 4 x 699 [1,8]
+ CRUSH rule 4 x 700 [1]
+ CRUSH rule 4 x 701 []
+ CRUSH rule 4 x 702 []
+ CRUSH rule 4 x 703 [8]
+ CRUSH rule 4 x 704 [1,4]
+ CRUSH rule 4 x 705 [8]
+ CRUSH rule 4 x 706 [1,2]
+ CRUSH rule 4 x 707 [8]
+ CRUSH rule 4 x 708 [4]
+ CRUSH rule 4 x 709 [8]
+ CRUSH rule 4 x 710 [8]
+ CRUSH rule 4 x 711 [2,4]
+ CRUSH rule 4 x 712 [2]
+ CRUSH rule 4 x 713 [8]
+ CRUSH rule 4 x 714 [2]
+ CRUSH rule 4 x 715 [1,2]
+ CRUSH rule 4 x 716 [4,8]
+ CRUSH rule 4 x 717 [8]
+ CRUSH rule 4 x 718 [8]
+ CRUSH rule 4 x 719 [2,6]
+ CRUSH rule 4 x 720 [8]
+ CRUSH rule 4 x 721 [4]
+ CRUSH rule 4 x 722 []
+ CRUSH rule 4 x 723 [4,1]
+ CRUSH rule 4 x 724 [2,6]
+ CRUSH rule 4 x 725 [1,2]
+ CRUSH rule 4 x 726 [4,8]
+ CRUSH rule 4 x 727 [4,8]
+ CRUSH rule 4 x 728 [2,1]
+ CRUSH rule 4 x 729 []
+ CRUSH rule 4 x 730 [4,8]
+ CRUSH rule 4 x 731 [4,1]
+ CRUSH rule 4 x 732 [1]
+ CRUSH rule 4 x 733 [4]
+ CRUSH rule 4 x 734 [8,4]
+ CRUSH rule 4 x 735 [4,8]
+ CRUSH rule 4 x 736 [4]
+ CRUSH rule 4 x 737 [1,2]
+ CRUSH rule 4 x 738 [4,2]
+ CRUSH rule 4 x 739 [2,1]
+ CRUSH rule 4 x 740 [1,2]
+ CRUSH rule 4 x 741 [8]
+ CRUSH rule 4 x 742 [8,2]
+ CRUSH rule 4 x 743 [8,1]
+ CRUSH rule 4 x 744 [4,8]
+ CRUSH rule 4 x 745 []
+ CRUSH rule 4 x 746 [1]
+ CRUSH rule 4 x 747 [8,1]
+ CRUSH rule 4 x 748 [2,8]
+ CRUSH rule 4 x 749 [4]
+ CRUSH rule 4 x 750 [1,8]
+ CRUSH rule 4 x 751 [2,1]
+ CRUSH rule 4 x 752 [8,1]
+ CRUSH rule 4 x 753 [8]
+ CRUSH rule 4 x 754 [8]
+ CRUSH rule 4 x 755 [1,2]
+ CRUSH rule 4 x 756 [8]
+ CRUSH rule 4 x 757 [8]
+ CRUSH rule 4 x 758 [8,2]
+ CRUSH rule 4 x 759 [8,4]
+ CRUSH rule 4 x 760 [1,4]
+ CRUSH rule 4 x 761 [1]
+ CRUSH rule 4 x 762 [2,8]
+ CRUSH rule 4 x 763 [8]
+ CRUSH rule 4 x 764 [1,8]
+ CRUSH rule 4 x 765 [8]
+ CRUSH rule 4 x 766 [8]
+ CRUSH rule 4 x 767 [1,2]
+ CRUSH rule 4 x 768 [8,4]
+ CRUSH rule 4 x 769 [8,2]
+ CRUSH rule 4 x 770 [8,2]
+ CRUSH rule 4 x 771 [8,1]
+ CRUSH rule 4 x 772 [8,4]
+ CRUSH rule 4 x 773 [4,1]
+ CRUSH rule 4 x 774 [8]
+ CRUSH rule 4 x 775 [8]
+ CRUSH rule 4 x 776 [6,2]
+ CRUSH rule 4 x 777 [4,1]
+ CRUSH rule 4 x 778 [1,8]
+ CRUSH rule 4 x 779 [2,8]
+ CRUSH rule 4 x 780 [2,1]
+ CRUSH rule 4 x 781 [8]
+ CRUSH rule 4 x 782 [4]
+ CRUSH rule 4 x 783 [8,1]
+ CRUSH rule 4 x 784 [1,2]
+ CRUSH rule 4 x 785 [8,1]
+ CRUSH rule 4 x 786 [8]
+ CRUSH rule 4 x 787 [1,2]
+ CRUSH rule 4 x 788 [8,2]
+ CRUSH rule 4 x 789 [1]
+ CRUSH rule 4 x 790 [8]
+ CRUSH rule 4 x 791 [4,8]
+ CRUSH rule 4 x 792 [4,8]
+ CRUSH rule 4 x 793 [8,1]
+ CRUSH rule 4 x 794 [2,8]
+ CRUSH rule 4 x 795 [1]
+ CRUSH rule 4 x 796 []
+ CRUSH rule 4 x 797 [2,4]
+ CRUSH rule 4 x 798 [6,8]
+ CRUSH rule 4 x 799 [4,1]
+ CRUSH rule 4 x 800 [2]
+ CRUSH rule 4 x 801 [4,8]
+ CRUSH rule 4 x 802 [1,8]
+ CRUSH rule 4 x 803 [2]
+ CRUSH rule 4 x 804 [8,2]
+ CRUSH rule 4 x 805 [8]
+ CRUSH rule 4 x 806 [1,4]
+ CRUSH rule 4 x 807 [4]
+ CRUSH rule 4 x 808 [8]
+ CRUSH rule 4 x 809 [1]
+ CRUSH rule 4 x 810 [8]
+ CRUSH rule 4 x 811 [8]
+ CRUSH rule 4 x 812 [8,4]
+ CRUSH rule 4 x 813 [8,4]
+ CRUSH rule 4 x 814 [8]
+ CRUSH rule 4 x 815 [4,1]
+ CRUSH rule 4 x 816 [2,1]
+ CRUSH rule 4 x 817 []
+ CRUSH rule 4 x 818 []
+ CRUSH rule 4 x 819 [1]
+ CRUSH rule 4 x 820 [4]
+ CRUSH rule 4 x 821 [4]
+ CRUSH rule 4 x 822 [2,1]
+ CRUSH rule 4 x 823 [4,8]
+ CRUSH rule 4 x 824 [8]
+ CRUSH rule 4 x 825 [2,8]
+ CRUSH rule 4 x 826 [8,2]
+ CRUSH rule 4 x 827 [2,8]
+ CRUSH rule 4 x 828 [2]
+ CRUSH rule 4 x 829 [8]
+ CRUSH rule 4 x 830 [2,4]
+ CRUSH rule 4 x 831 [1,8]
+ CRUSH rule 4 x 832 [4]
+ CRUSH rule 4 x 833 [2,1]
+ CRUSH rule 4 x 834 []
+ CRUSH rule 4 x 835 [8,4]
+ CRUSH rule 4 x 836 [4]
+ CRUSH rule 4 x 837 [8,4]
+ CRUSH rule 4 x 838 [6,8]
+ CRUSH rule 4 x 839 [2]
+ CRUSH rule 4 x 840 [8]
+ CRUSH rule 4 x 841 [4,8]
+ CRUSH rule 4 x 842 [2]
+ CRUSH rule 4 x 843 [8,4]
+ CRUSH rule 4 x 844 [8]
+ CRUSH rule 4 x 845 [4,8]
+ CRUSH rule 4 x 846 [4,2]
+ CRUSH rule 4 x 847 [2,1]
+ CRUSH rule 4 x 848 [2,8]
+ CRUSH rule 4 x 849 [4]
+ CRUSH rule 4 x 850 [1,2]
+ CRUSH rule 4 x 851 [6,8]
+ CRUSH rule 4 x 852 [8,4]
+ CRUSH rule 4 x 853 [6,8]
+ CRUSH rule 4 x 854 [8]
+ CRUSH rule 4 x 855 [8]
+ CRUSH rule 4 x 856 [8]
+ CRUSH rule 4 x 857 [8]
+ CRUSH rule 4 x 858 [6]
+ CRUSH rule 4 x 859 [8,2]
+ CRUSH rule 4 x 860 [1]
+ CRUSH rule 4 x 861 [8]
+ CRUSH rule 4 x 862 [8,1]
+ CRUSH rule 4 x 863 [8]
+ CRUSH rule 4 x 864 [8]
+ CRUSH rule 4 x 865 [8,1]
+ CRUSH rule 4 x 866 []
+ CRUSH rule 4 x 867 [8]
+ CRUSH rule 4 x 868 [8]
+ CRUSH rule 4 x 869 [8,6]
+ CRUSH rule 4 x 870 [2]
+ CRUSH rule 4 x 871 []
+ CRUSH rule 4 x 872 [1]
+ CRUSH rule 4 x 873 [4,8]
+ CRUSH rule 4 x 874 [2,6]
+ CRUSH rule 4 x 875 [2,8]
+ CRUSH rule 4 x 876 [4,8]
+ CRUSH rule 4 x 877 [8,4]
+ CRUSH rule 4 x 878 []
+ CRUSH rule 4 x 879 [8]
+ CRUSH rule 4 x 880 []
+ CRUSH rule 4 x 881 [4,8]
+ CRUSH rule 4 x 882 [2]
+ CRUSH rule 4 x 883 [2,1]
+ CRUSH rule 4 x 884 [8,2]
+ CRUSH rule 4 x 885 [4,1]
+ CRUSH rule 4 x 886 [8]
+ CRUSH rule 4 x 887 [8,4]
+ CRUSH rule 4 x 888 [8]
+ CRUSH rule 4 x 889 [2,1]
+ CRUSH rule 4 x 890 [8,2]
+ CRUSH rule 4 x 891 [1,8]
+ CRUSH rule 4 x 892 [8,2]
+ CRUSH rule 4 x 893 [2]
+ CRUSH rule 4 x 894 [8,4]
+ CRUSH rule 4 x 895 [4]
+ CRUSH rule 4 x 896 [1,8]
+ CRUSH rule 4 x 897 [2]
+ CRUSH rule 4 x 898 [1,4]
+ CRUSH rule 4 x 899 [1,8]
+ CRUSH rule 4 x 900 [4,1]
+ CRUSH rule 4 x 901 [1]
+ CRUSH rule 4 x 902 [8,4]
+ CRUSH rule 4 x 903 [8]
+ CRUSH rule 4 x 904 [8]
+ CRUSH rule 4 x 905 [8,2]
+ CRUSH rule 4 x 906 [1,2]
+ CRUSH rule 4 x 907 [8,1]
+ CRUSH rule 4 x 908 [8]
+ CRUSH rule 4 x 909 [2]
+ CRUSH rule 4 x 910 [8]
+ CRUSH rule 4 x 911 [8]
+ CRUSH rule 4 x 912 [1,2]
+ CRUSH rule 4 x 913 [8]
+ CRUSH rule 4 x 914 [6,4]
+ CRUSH rule 4 x 915 [8,2]
+ CRUSH rule 4 x 916 [4,1]
+ CRUSH rule 4 x 917 [1,4]
+ CRUSH rule 4 x 918 [8,2]
+ CRUSH rule 4 x 919 [8,2]
+ CRUSH rule 4 x 920 [8]
+ CRUSH rule 4 x 921 [1]
+ CRUSH rule 4 x 922 [8]
+ CRUSH rule 4 x 923 [4]
+ CRUSH rule 4 x 924 []
+ CRUSH rule 4 x 925 [4,8]
+ CRUSH rule 4 x 926 []
+ CRUSH rule 4 x 927 [1,8]
+ CRUSH rule 4 x 928 [8,1]
+ CRUSH rule 4 x 929 [4]
+ CRUSH rule 4 x 930 [2]
+ CRUSH rule 4 x 931 [1]
+ CRUSH rule 4 x 932 [4]
+ CRUSH rule 4 x 933 [8,4]
+ CRUSH rule 4 x 934 []
+ CRUSH rule 4 x 935 [8]
+ CRUSH rule 4 x 936 [1,8]
+ CRUSH rule 4 x 937 [4]
+ CRUSH rule 4 x 938 [8,4]
+ CRUSH rule 4 x 939 [2,8]
+ CRUSH rule 4 x 940 [8]
+ CRUSH rule 4 x 941 [2]
+ CRUSH rule 4 x 942 [1,2]
+ CRUSH rule 4 x 943 [8,2]
+ CRUSH rule 4 x 944 []
+ CRUSH rule 4 x 945 [8,2]
+ CRUSH rule 4 x 946 [2,1]
+ CRUSH rule 4 x 947 []
+ CRUSH rule 4 x 948 [8]
+ CRUSH rule 4 x 949 [6,1]
+ CRUSH rule 4 x 950 []
+ CRUSH rule 4 x 951 []
+ CRUSH rule 4 x 952 [2,1]
+ CRUSH rule 4 x 953 [1,4]
+ CRUSH rule 4 x 954 [2]
+ CRUSH rule 4 x 955 [8]
+ CRUSH rule 4 x 956 [1,2]
+ CRUSH rule 4 x 957 [8]
+ CRUSH rule 4 x 958 [8]
+ CRUSH rule 4 x 959 [4,2]
+ CRUSH rule 4 x 960 [6]
+ CRUSH rule 4 x 961 [1]
+ CRUSH rule 4 x 962 [8,4]
+ CRUSH rule 4 x 963 [2,4]
+ CRUSH rule 4 x 964 [1]
+ CRUSH rule 4 x 965 [8]
+ CRUSH rule 4 x 966 [4,8]
+ CRUSH rule 4 x 967 [8]
+ CRUSH rule 4 x 968 [8,2]
+ CRUSH rule 4 x 969 [8,2]
+ CRUSH rule 4 x 970 [2,8]
+ CRUSH rule 4 x 971 [1,8]
+ CRUSH rule 4 x 972 [1,8]
+ CRUSH rule 4 x 973 [1,2]
+ CRUSH rule 4 x 974 [4]
+ CRUSH rule 4 x 975 [4,8]
+ CRUSH rule 4 x 976 [4]
+ CRUSH rule 4 x 977 [8,4]
+ CRUSH rule 4 x 978 [8,2]
+ CRUSH rule 4 x 979 [8]
+ CRUSH rule 4 x 980 [8,2]
+ CRUSH rule 4 x 981 [8]
+ CRUSH rule 4 x 982 [2]
+ CRUSH rule 4 x 983 [4]
+ CRUSH rule 4 x 984 [2,1]
+ CRUSH rule 4 x 985 [2,4]
+ CRUSH rule 4 x 986 [8]
+ CRUSH rule 4 x 987 [2]
+ CRUSH rule 4 x 988 [1,4]
+ CRUSH rule 4 x 989 [1,8]
+ CRUSH rule 4 x 990 [1,2]
+ CRUSH rule 4 x 991 [1,4]
+ CRUSH rule 4 x 992 [8,1]
+ CRUSH rule 4 x 993 [2,8]
+ CRUSH rule 4 x 994 [4]
+ CRUSH rule 4 x 995 [8]
+ CRUSH rule 4 x 996 [8]
+ CRUSH rule 4 x 997 [8,4]
+ CRUSH rule 4 x 998 [8,1]
+ CRUSH rule 4 x 999 [1,8]
+ CRUSH rule 4 x 1000 [8,4]
+ CRUSH rule 4 x 1001 [2,1]
+ CRUSH rule 4 x 1002 [1]
+ CRUSH rule 4 x 1003 [2,8]
+ CRUSH rule 4 x 1004 [8,1]
+ CRUSH rule 4 x 1005 [8,1]
+ CRUSH rule 4 x 1006 [1,2]
+ CRUSH rule 4 x 1007 [1,2]
+ CRUSH rule 4 x 1008 [1,8]
+ CRUSH rule 4 x 1009 [6,8]
+ CRUSH rule 4 x 1010 []
+ CRUSH rule 4 x 1011 [4,2]
+ CRUSH rule 4 x 1012 [2]
+ CRUSH rule 4 x 1013 [1]
+ CRUSH rule 4 x 1014 [2,8]
+ CRUSH rule 4 x 1015 [8]
+ CRUSH rule 4 x 1016 [2,1]
+ CRUSH rule 4 x 1017 [6,2]
+ CRUSH rule 4 x 1018 [4]
+ CRUSH rule 4 x 1019 [4]
+ CRUSH rule 4 x 1020 [1]
+ CRUSH rule 4 x 1021 [2]
+ CRUSH rule 4 x 1022 [1,8]
+ CRUSH rule 4 x 1023 [4,2]
+ rule 4 (choose-set-two) num_rep 2 result size == 0:\t56/1024 (esc)
+ rule 4 (choose-set-two) num_rep 2 result size == 1:\t397/1024 (esc)
+ rule 4 (choose-set-two) num_rep 2 result size == 2:\t571/1024 (esc)
+ CRUSH rule 4 x 0 [2,1,4]
+ CRUSH rule 4 x 1 [2,8,1]
+ CRUSH rule 4 x 2 [1]
+ CRUSH rule 4 x 3 [8,1]
+ CRUSH rule 4 x 4 [4,2]
+ CRUSH rule 4 x 5 [8,1]
+ CRUSH rule 4 x 6 [2,8]
+ CRUSH rule 4 x 7 [4]
+ CRUSH rule 4 x 8 [4,8]
+ CRUSH rule 4 x 9 [2,4,1]
+ CRUSH rule 4 x 10 [2,1,8]
+ CRUSH rule 4 x 11 [2,8,1]
+ CRUSH rule 4 x 12 [2,1]
+ CRUSH rule 4 x 13 [4,8]
+ CRUSH rule 4 x 14 [8]
+ CRUSH rule 4 x 15 [8,2]
+ CRUSH rule 4 x 16 [8,2]
+ CRUSH rule 4 x 17 [4,2]
+ CRUSH rule 4 x 18 [1]
+ CRUSH rule 4 x 19 [8,4]
+ CRUSH rule 4 x 20 [2,8]
+ CRUSH rule 4 x 21 [8,2]
+ CRUSH rule 4 x 22 [8]
+ CRUSH rule 4 x 23 [4,8]
+ CRUSH rule 4 x 24 [1,2,8]
+ CRUSH rule 4 x 25 [4,8,1]
+ CRUSH rule 4 x 26 [2,8]
+ CRUSH rule 4 x 27 [4,1]
+ CRUSH rule 4 x 28 [8,2]
+ CRUSH rule 4 x 29 [8,4]
+ CRUSH rule 4 x 30 [4,8]
+ CRUSH rule 4 x 31 [8]
+ CRUSH rule 4 x 32 [6,8]
+ CRUSH rule 4 x 33 [2,8]
+ CRUSH rule 4 x 34 [2]
+ CRUSH rule 4 x 35 [1,8,2]
+ CRUSH rule 4 x 36 [8,2]
+ CRUSH rule 4 x 37 [1]
+ CRUSH rule 4 x 38 [4,8]
+ CRUSH rule 4 x 39 [8,1]
+ CRUSH rule 4 x 40 [8,1]
+ CRUSH rule 4 x 41 [2,1,8]
+ CRUSH rule 4 x 42 [8]
+ CRUSH rule 4 x 43 [1,8]
+ CRUSH rule 4 x 44 [1,8]
+ CRUSH rule 4 x 45 [8,2,4]
+ CRUSH rule 4 x 46 [2]
+ CRUSH rule 4 x 47 [4]
+ CRUSH rule 4 x 48 [8]
+ CRUSH rule 4 x 49 [8]
+ CRUSH rule 4 x 50 [4,1]
+ CRUSH rule 4 x 51 [8,2]
+ CRUSH rule 4 x 52 [8,1]
+ CRUSH rule 4 x 53 [4]
+ CRUSH rule 4 x 54 [8]
+ CRUSH rule 4 x 55 [8,2]
+ CRUSH rule 4 x 56 [8,4]
+ CRUSH rule 4 x 57 [8]
+ CRUSH rule 4 x 58 [1,2]
+ CRUSH rule 4 x 59 [2,1]
+ CRUSH rule 4 x 60 [4,1]
+ CRUSH rule 4 x 61 [4,8]
+ CRUSH rule 4 x 62 [8,1,2]
+ CRUSH rule 4 x 63 [8]
+ CRUSH rule 4 x 64 [4,1]
+ CRUSH rule 4 x 65 [8,4]
+ CRUSH rule 4 x 66 [4]
+ CRUSH rule 4 x 67 [4,2,8]
+ CRUSH rule 4 x 68 [1,8]
+ CRUSH rule 4 x 69 [1,8]
+ CRUSH rule 4 x 70 [8,2,1]
+ CRUSH rule 4 x 71 [2,8]
+ CRUSH rule 4 x 72 [8,1,4]
+ CRUSH rule 4 x 73 [2,8]
+ CRUSH rule 4 x 74 [1,8,2]
+ CRUSH rule 4 x 75 [4,2,1]
+ CRUSH rule 4 x 76 [4,1,6]
+ CRUSH rule 4 x 77 [8,2]
+ CRUSH rule 4 x 78 [1,2]
+ CRUSH rule 4 x 79 [4,1]
+ CRUSH rule 4 x 80 [2,4]
+ CRUSH rule 4 x 81 [2,1]
+ CRUSH rule 4 x 82 [6,1,8]
+ CRUSH rule 4 x 83 [2,8]
+ CRUSH rule 4 x 84 [8,2,1]
+ CRUSH rule 4 x 85 [4,8]
+ CRUSH rule 4 x 86 [2,1]
+ CRUSH rule 4 x 87 [2,8,4]
+ CRUSH rule 4 x 88 [1,6,2]
+ CRUSH rule 4 x 89 [2,1]
+ CRUSH rule 4 x 90 [8,4]
+ CRUSH rule 4 x 91 [4,8]
+ CRUSH rule 4 x 92 [1,8]
+ CRUSH rule 4 x 93 [8,4]
+ CRUSH rule 4 x 94 [1]
+ CRUSH rule 4 x 95 [8,1]
+ CRUSH rule 4 x 96 [8]
+ CRUSH rule 4 x 97 [8]
+ CRUSH rule 4 x 98 [2,1,8]
+ CRUSH rule 4 x 99 [2,8,1]
+ CRUSH rule 4 x 100 [1,8,4]
+ CRUSH rule 4 x 101 [8,2]
+ CRUSH rule 4 x 102 [2,8]
+ CRUSH rule 4 x 103 [8]
+ CRUSH rule 4 x 104 [8,4]
+ CRUSH rule 4 x 105 [2,4,1]
+ CRUSH rule 4 x 106 [1,8,2]
+ CRUSH rule 4 x 107 [2,1]
+ CRUSH rule 4 x 108 [8,2]
+ CRUSH rule 4 x 109 [1,2,4]
+ CRUSH rule 4 x 110 [4,2,8]
+ CRUSH rule 4 x 111 [2,1]
+ CRUSH rule 4 x 112 [2,1,8]
+ CRUSH rule 4 x 113 [8,2,1]
+ CRUSH rule 4 x 114 [8,4]
+ CRUSH rule 4 x 115 [8,2,4]
+ CRUSH rule 4 x 116 [1,2,8]
+ CRUSH rule 4 x 117 [6,8]
+ CRUSH rule 4 x 118 [2,8]
+ CRUSH rule 4 x 119 [8,1]
+ CRUSH rule 4 x 120 [2,1,4]
+ CRUSH rule 4 x 121 [2,1]
+ CRUSH rule 4 x 122 [8,2]
+ CRUSH rule 4 x 123 [2,1]
+ CRUSH rule 4 x 124 [1]
+ CRUSH rule 4 x 125 [1,8,4]
+ CRUSH rule 4 x 126 [2,8]
+ CRUSH rule 4 x 127 [4,8]
+ CRUSH rule 4 x 128 [8]
+ CRUSH rule 4 x 129 [2,1]
+ CRUSH rule 4 x 130 [4,8]
+ CRUSH rule 4 x 131 [1,2]
+ CRUSH rule 4 x 132 [1,2]
+ CRUSH rule 4 x 133 [8]
+ CRUSH rule 4 x 134 [1,8,2]
+ CRUSH rule 4 x 135 [4,8]
+ CRUSH rule 4 x 136 [2,1,4]
+ CRUSH rule 4 x 137 [8,4]
+ CRUSH rule 4 x 138 [8]
+ CRUSH rule 4 x 139 [4,2]
+ CRUSH rule 4 x 140 [1,8]
+ CRUSH rule 4 x 141 [8,1]
+ CRUSH rule 4 x 142 [4,1]
+ CRUSH rule 4 x 143 [4,8]
+ CRUSH rule 4 x 144 [8,1,2]
+ CRUSH rule 4 x 145 [8]
+ CRUSH rule 4 x 146 [2,8]
+ CRUSH rule 4 x 147 [2,8]
+ CRUSH rule 4 x 148 [4,1]
+ CRUSH rule 4 x 149 [4,8]
+ CRUSH rule 4 x 150 [1,8]
+ CRUSH rule 4 x 151 []
+ CRUSH rule 4 x 152 [8]
+ CRUSH rule 4 x 153 [8]
+ CRUSH rule 4 x 154 [4,2,1]
+ CRUSH rule 4 x 155 [4,8]
+ CRUSH rule 4 x 156 [4]
+ CRUSH rule 4 x 157 [1,2]
+ CRUSH rule 4 x 158 [2,8,1]
+ CRUSH rule 4 x 159 [8,2,4]
+ CRUSH rule 4 x 160 [2,8,1]
+ CRUSH rule 4 x 161 [1,4,2]
+ CRUSH rule 4 x 162 [1,8,2]
+ CRUSH rule 4 x 163 [4,8,1]
+ CRUSH rule 4 x 164 [8,1]
+ CRUSH rule 4 x 165 [8,2,4]
+ CRUSH rule 4 x 166 [2,1]
+ CRUSH rule 4 x 167 [1,2,8]
+ CRUSH rule 4 x 168 [4,2]
+ CRUSH rule 4 x 169 [2,8,1]
+ CRUSH rule 4 x 170 [1,2]
+ CRUSH rule 4 x 171 [8,4]
+ CRUSH rule 4 x 172 [1,8]
+ CRUSH rule 4 x 173 [8,4]
+ CRUSH rule 4 x 174 [1,6]
+ CRUSH rule 4 x 175 [8,1]
+ CRUSH rule 4 x 176 []
+ CRUSH rule 4 x 177 [2]
+ CRUSH rule 4 x 178 [4,2,1]
+ CRUSH rule 4 x 179 [1,2]
+ CRUSH rule 4 x 180 [8]
+ CRUSH rule 4 x 181 [8,2,1]
+ CRUSH rule 4 x 182 [8]
+ CRUSH rule 4 x 183 [8]
+ CRUSH rule 4 x 184 [4,8]
+ CRUSH rule 4 x 185 [8,1]
+ CRUSH rule 4 x 186 [2,1,4]
+ CRUSH rule 4 x 187 [1,8]
+ CRUSH rule 4 x 188 [1,8]
+ CRUSH rule 4 x 189 [1,8]
+ CRUSH rule 4 x 190 [1,2]
+ CRUSH rule 4 x 191 [8]
+ CRUSH rule 4 x 192 [4,1,2]
+ CRUSH rule 4 x 193 [4,2,8]
+ CRUSH rule 4 x 194 [1]
+ CRUSH rule 4 x 195 [8,4]
+ CRUSH rule 4 x 196 [8,2]
+ CRUSH rule 4 x 197 [8,4,2]
+ CRUSH rule 4 x 198 [2,8]
+ CRUSH rule 4 x 199 [1,4,8]
+ CRUSH rule 4 x 200 [1,2]
+ CRUSH rule 4 x 201 [8,1,2]
+ CRUSH rule 4 x 202 [8]
+ CRUSH rule 4 x 203 [8]
+ CRUSH rule 4 x 204 [2,1,4]
+ CRUSH rule 4 x 205 [1,8]
+ CRUSH rule 4 x 206 [1,2,8]
+ CRUSH rule 4 x 207 [2]
+ CRUSH rule 4 x 208 [8,1,2]
+ CRUSH rule 4 x 209 [1,2]
+ CRUSH rule 4 x 210 [1,2,4]
+ CRUSH rule 4 x 211 [4,2]
+ CRUSH rule 4 x 212 [8,1]
+ CRUSH rule 4 x 213 [8,4,2]
+ CRUSH rule 4 x 214 [8]
+ CRUSH rule 4 x 215 [8,1]
+ CRUSH rule 4 x 216 [1,2]
+ CRUSH rule 4 x 217 [1,2,8]
+ CRUSH rule 4 x 218 [2,8]
+ CRUSH rule 4 x 219 [8,2]
+ CRUSH rule 4 x 220 [4,8]
+ CRUSH rule 4 x 221 [8]
+ CRUSH rule 4 x 222 [8]
+ CRUSH rule 4 x 223 [1,8]
+ CRUSH rule 4 x 224 [1,4,2]
+ CRUSH rule 4 x 225 [8,2]
+ CRUSH rule 4 x 226 [8,2,4]
+ CRUSH rule 4 x 227 [4,1]
+ CRUSH rule 4 x 228 [8]
+ CRUSH rule 4 x 229 [4,6]
+ CRUSH rule 4 x 230 [4,8,2]
+ CRUSH rule 4 x 231 [4]
+ CRUSH rule 4 x 232 [2,8,4]
+ CRUSH rule 4 x 233 []
+ CRUSH rule 4 x 234 [1,2]
+ CRUSH rule 4 x 235 [4,8,1]
+ CRUSH rule 4 x 236 [2,6]
+ CRUSH rule 4 x 237 [4,8]
+ CRUSH rule 4 x 238 []
+ CRUSH rule 4 x 239 [8,6]
+ CRUSH rule 4 x 240 [4,8]
+ CRUSH rule 4 x 241 [1]
+ CRUSH rule 4 x 242 []
+ CRUSH rule 4 x 243 [8]
+ CRUSH rule 4 x 244 [4,8,2]
+ CRUSH rule 4 x 245 [8,1]
+ CRUSH rule 4 x 246 [1,2]
+ CRUSH rule 4 x 247 [8,2,1]
+ CRUSH rule 4 x 248 [8,2]
+ CRUSH rule 4 x 249 [2,1]
+ CRUSH rule 4 x 250 [2,1]
+ CRUSH rule 4 x 251 [2,8]
+ CRUSH rule 4 x 252 [4,8]
+ CRUSH rule 4 x 253 [2]
+ CRUSH rule 4 x 254 [4]
+ CRUSH rule 4 x 255 [1,8]
+ CRUSH rule 4 x 256 [4,8,1]
+ CRUSH rule 4 x 257 [2,8,4]
+ CRUSH rule 4 x 258 [4,1]
+ CRUSH rule 4 x 259 [6]
+ CRUSH rule 4 x 260 [8,2]
+ CRUSH rule 4 x 261 [8]
+ CRUSH rule 4 x 262 [8]
+ CRUSH rule 4 x 263 [8,2]
+ CRUSH rule 4 x 264 [8]
+ CRUSH rule 4 x 265 [8]
+ CRUSH rule 4 x 266 [8,2,1]
+ CRUSH rule 4 x 267 [2,1]
+ CRUSH rule 4 x 268 [1,8,2]
+ CRUSH rule 4 x 269 [1,8,2]
+ CRUSH rule 4 x 270 [4,1,2]
+ CRUSH rule 4 x 271 [8,4]
+ CRUSH rule 4 x 272 [2,8,4]
+ CRUSH rule 4 x 273 [4,1]
+ CRUSH rule 4 x 274 [8,4]
+ CRUSH rule 4 x 275 [4,8]
+ CRUSH rule 4 x 276 [8,1,2]
+ CRUSH rule 4 x 277 [8,1]
+ CRUSH rule 4 x 278 [8,1]
+ CRUSH rule 4 x 279 [8,4]
+ CRUSH rule 4 x 280 [2,8,1]
+ CRUSH rule 4 x 281 [8,2,1]
+ CRUSH rule 4 x 282 [1,8]
+ CRUSH rule 4 x 283 [8,2,1]
+ CRUSH rule 4 x 284 [8,2]
+ CRUSH rule 4 x 285 [4,8]
+ CRUSH rule 4 x 286 [2,1,8]
+ CRUSH rule 4 x 287 [1,2]
+ CRUSH rule 4 x 288 [8,1]
+ CRUSH rule 4 x 289 [4,8,2]
+ CRUSH rule 4 x 290 [1,4,8]
+ CRUSH rule 4 x 291 [1,2,4]
+ CRUSH rule 4 x 292 [8,2,1]
+ CRUSH rule 4 x 293 [8,1]
+ CRUSH rule 4 x 294 [8,4]
+ CRUSH rule 4 x 295 [4,8]
+ CRUSH rule 4 x 296 [4,1]
+ CRUSH rule 4 x 297 [8,2]
+ CRUSH rule 4 x 298 [1,2]
+ CRUSH rule 4 x 299 [2,1,8]
+ CRUSH rule 4 x 300 [8]
+ CRUSH rule 4 x 301 [1,8,2]
+ CRUSH rule 4 x 302 [1,8]
+ CRUSH rule 4 x 303 [8,4]
+ CRUSH rule 4 x 304 [2,8]
+ CRUSH rule 4 x 305 [8,2]
+ CRUSH rule 4 x 306 [1,8,2]
+ CRUSH rule 4 x 307 [2,1,8]
+ CRUSH rule 4 x 308 [2,8,4]
+ CRUSH rule 4 x 309 [8]
+ CRUSH rule 4 x 310 [4,1]
+ CRUSH rule 4 x 311 [4]
+ CRUSH rule 4 x 312 [2,1]
+ CRUSH rule 4 x 313 [4]
+ CRUSH rule 4 x 314 [1]
+ CRUSH rule 4 x 315 [2,1]
+ CRUSH rule 4 x 316 [8]
+ CRUSH rule 4 x 317 [2,8,1]
+ CRUSH rule 4 x 318 [8]
+ CRUSH rule 4 x 319 [2,8]
+ CRUSH rule 4 x 320 [8,1]
+ CRUSH rule 4 x 321 [1,2]
+ CRUSH rule 4 x 322 [2,8,4]
+ CRUSH rule 4 x 323 [4,8,1]
+ CRUSH rule 4 x 324 [8,1]
+ CRUSH rule 4 x 325 [4,8,2]
+ CRUSH rule 4 x 326 [1]
+ CRUSH rule 4 x 327 [1,8]
+ CRUSH rule 4 x 328 [8,4]
+ CRUSH rule 4 x 329 [4,8]
+ CRUSH rule 4 x 330 [4,8]
+ CRUSH rule 4 x 331 [2,8]
+ CRUSH rule 4 x 332 [2,1]
+ CRUSH rule 4 x 333 [8]
+ CRUSH rule 4 x 334 [8]
+ CRUSH rule 4 x 335 [8,1,2]
+ CRUSH rule 4 x 336 [4,8,2]
+ CRUSH rule 4 x 337 [8,2]
+ CRUSH rule 4 x 338 [8]
+ CRUSH rule 4 x 339 [8,2]
+ CRUSH rule 4 x 340 [2,1]
+ CRUSH rule 4 x 341 [4,1,8]
+ CRUSH rule 4 x 342 [2,8,4]
+ CRUSH rule 4 x 343 [8]
+ CRUSH rule 4 x 344 [6,2,4]
+ CRUSH rule 4 x 345 []
+ CRUSH rule 4 x 346 [8,2,4]
+ CRUSH rule 4 x 347 [4,1]
+ CRUSH rule 4 x 348 [8,2,1]
+ CRUSH rule 4 x 349 [1,8]
+ CRUSH rule 4 x 350 [8]
+ CRUSH rule 4 x 351 [8]
+ CRUSH rule 4 x 352 [1,2]
+ CRUSH rule 4 x 353 [8]
+ CRUSH rule 4 x 354 [1,2]
+ CRUSH rule 4 x 355 [8]
+ CRUSH rule 4 x 356 [4]
+ CRUSH rule 4 x 357 [8,1,2]
+ CRUSH rule 4 x 358 [2,1,8]
+ CRUSH rule 4 x 359 [6,8]
+ CRUSH rule 4 x 360 [1]
+ CRUSH rule 4 x 361 [8,4]
+ CRUSH rule 4 x 362 [4]
+ CRUSH rule 4 x 363 [4,1]
+ CRUSH rule 4 x 364 [2,1]
+ CRUSH rule 4 x 365 [8]
+ CRUSH rule 4 x 366 [8,2]
+ CRUSH rule 4 x 367 [4,2]
+ CRUSH rule 4 x 368 [8,4]
+ CRUSH rule 4 x 369 [8,1]
+ CRUSH rule 4 x 370 [8]
+ CRUSH rule 4 x 371 [1,4]
+ CRUSH rule 4 x 372 [1,8]
+ CRUSH rule 4 x 373 [1,8]
+ CRUSH rule 4 x 374 [8]
+ CRUSH rule 4 x 375 [8,4]
+ CRUSH rule 4 x 376 [8,1,2]
+ CRUSH rule 4 x 377 [1,2,4]
+ CRUSH rule 4 x 378 [1,2]
+ CRUSH rule 4 x 379 [8]
+ CRUSH rule 4 x 380 [2,1]
+ CRUSH rule 4 x 381 [1,4,8]
+ CRUSH rule 4 x 382 [1,4,2]
+ CRUSH rule 4 x 383 [4]
+ CRUSH rule 4 x 384 [8,2]
+ CRUSH rule 4 x 385 [8,6]
+ CRUSH rule 4 x 386 [1]
+ CRUSH rule 4 x 387 [1,4,8]
+ CRUSH rule 4 x 388 [1]
+ CRUSH rule 4 x 389 [1,4]
+ CRUSH rule 4 x 390 [4,8,1]
+ CRUSH rule 4 x 391 [4,8,2]
+ CRUSH rule 4 x 392 [1,8]
+ CRUSH rule 4 x 393 [2,8]
+ CRUSH rule 4 x 394 [8]
+ CRUSH rule 4 x 395 [1,2]
+ CRUSH rule 4 x 396 [4,2]
+ CRUSH rule 4 x 397 [2,1,4]
+ CRUSH rule 4 x 398 [2,4]
+ CRUSH rule 4 x 399 [8,4]
+ CRUSH rule 4 x 400 [8,1,4]
+ CRUSH rule 4 x 401 [1,2]
+ CRUSH rule 4 x 402 [8]
+ CRUSH rule 4 x 403 [1,2,4]
+ CRUSH rule 4 x 404 [4,2]
+ CRUSH rule 4 x 405 [8,4,2]
+ CRUSH rule 4 x 406 [2,1]
+ CRUSH rule 4 x 407 [2,8]
+ CRUSH rule 4 x 408 [4,1,8]
+ CRUSH rule 4 x 409 [8,4]
+ CRUSH rule 4 x 410 [8,4]
+ CRUSH rule 4 x 411 [2,1,8]
+ CRUSH rule 4 x 412 [2,8]
+ CRUSH rule 4 x 413 [2,8]
+ CRUSH rule 4 x 414 [4,1]
+ CRUSH rule 4 x 415 [2,8,1]
+ CRUSH rule 4 x 416 [2,1,8]
+ CRUSH rule 4 x 417 [8,6,2]
+ CRUSH rule 4 x 418 [8]
+ CRUSH rule 4 x 419 [8,4,1]
+ CRUSH rule 4 x 420 [1,4,2]
+ CRUSH rule 4 x 421 [8]
+ CRUSH rule 4 x 422 [6,8]
+ CRUSH rule 4 x 423 [2,4,8]
+ CRUSH rule 4 x 424 [8]
+ CRUSH rule 4 x 425 [1]
+ CRUSH rule 4 x 426 [8,2]
+ CRUSH rule 4 x 427 [1,8]
+ CRUSH rule 4 x 428 [4]
+ CRUSH rule 4 x 429 [4,8]
+ CRUSH rule 4 x 430 [4,8]
+ CRUSH rule 4 x 431 [4,1]
+ CRUSH rule 4 x 432 [8,1,2]
+ CRUSH rule 4 x 433 [8,1]
+ CRUSH rule 4 x 434 [2,1]
+ CRUSH rule 4 x 435 [2]
+ CRUSH rule 4 x 436 [4,1]
+ CRUSH rule 4 x 437 [8]
+ CRUSH rule 4 x 438 [2,4,8]
+ CRUSH rule 4 x 439 [1]
+ CRUSH rule 4 x 440 [2,8,1]
+ CRUSH rule 4 x 441 [4,6,2]
+ CRUSH rule 4 x 442 [2]
+ CRUSH rule 4 x 443 [8,2]
+ CRUSH rule 4 x 444 [8,1,2]
+ CRUSH rule 4 x 445 [8]
+ CRUSH rule 4 x 446 []
+ CRUSH rule 4 x 447 [2,1,4]
+ CRUSH rule 4 x 448 [8,2,4]
+ CRUSH rule 4 x 449 [8,6]
+ CRUSH rule 4 x 450 [2]
+ CRUSH rule 4 x 451 [8,4]
+ CRUSH rule 4 x 452 [8]
+ CRUSH rule 4 x 453 [6,8]
+ CRUSH rule 4 x 454 [8]
+ CRUSH rule 4 x 455 [2,8,4]
+ CRUSH rule 4 x 456 [8]
+ CRUSH rule 4 x 457 [8,2]
+ CRUSH rule 4 x 458 [2,8,1]
+ CRUSH rule 4 x 459 [2,1,8]
+ CRUSH rule 4 x 460 [8]
+ CRUSH rule 4 x 461 [8]
+ CRUSH rule 4 x 462 [8,1]
+ CRUSH rule 4 x 463 [8,2]
+ CRUSH rule 4 x 464 [8,4,2]
+ CRUSH rule 4 x 465 [6,8,1]
+ CRUSH rule 4 x 466 [8]
+ CRUSH rule 4 x 467 [8]
+ CRUSH rule 4 x 468 [8,1]
+ CRUSH rule 4 x 469 [8,1,2]
+ CRUSH rule 4 x 470 [4,2]
+ CRUSH rule 4 x 471 [1,2]
+ CRUSH rule 4 x 472 [1]
+ CRUSH rule 4 x 473 [1,2,4]
+ CRUSH rule 4 x 474 [8,1]
+ CRUSH rule 4 x 475 [8]
+ CRUSH rule 4 x 476 [4]
+ CRUSH rule 4 x 477 [4,8]
+ CRUSH rule 4 x 478 [8,2]
+ CRUSH rule 4 x 479 [2,8]
+ CRUSH rule 4 x 480 [1,8]
+ CRUSH rule 4 x 481 [2,4]
+ CRUSH rule 4 x 482 []
+ CRUSH rule 4 x 483 [2,1,8]
+ CRUSH rule 4 x 484 [1,2,8]
+ CRUSH rule 4 x 485 [8]
+ CRUSH rule 4 x 486 [4,1,8]
+ CRUSH rule 4 x 487 [1,2]
+ CRUSH rule 4 x 488 [8,1]
+ CRUSH rule 4 x 489 [2,8]
+ CRUSH rule 4 x 490 [6,1]
+ CRUSH rule 4 x 491 [1,2,8]
+ CRUSH rule 4 x 492 [8]
+ CRUSH rule 4 x 493 [2,1,8]
+ CRUSH rule 4 x 494 [1,2,8]
+ CRUSH rule 4 x 495 [4]
+ CRUSH rule 4 x 496 [8,4]
+ CRUSH rule 4 x 497 [4,8]
+ CRUSH rule 4 x 498 [2,4,8]
+ CRUSH rule 4 x 499 [8,4]
+ CRUSH rule 4 x 500 [4,8]
+ CRUSH rule 4 x 501 [2,8,1]
+ CRUSH rule 4 x 502 [6,1]
+ CRUSH rule 4 x 503 [2]
+ CRUSH rule 4 x 504 [8]
+ CRUSH rule 4 x 505 [1,8]
+ CRUSH rule 4 x 506 [4,2]
+ CRUSH rule 4 x 507 [8,1]
+ CRUSH rule 4 x 508 [1,2]
+ CRUSH rule 4 x 509 [8]
+ CRUSH rule 4 x 510 [8,2,1]
+ CRUSH rule 4 x 511 [4,8]
+ CRUSH rule 4 x 512 [8,2]
+ CRUSH rule 4 x 513 [8,2,1]
+ CRUSH rule 4 x 514 []
+ CRUSH rule 4 x 515 [8,4]
+ CRUSH rule 4 x 516 [4,1,2]
+ CRUSH rule 4 x 517 [8]
+ CRUSH rule 4 x 518 [4,8]
+ CRUSH rule 4 x 519 [8,4]
+ CRUSH rule 4 x 520 [2,8,4]
+ CRUSH rule 4 x 521 [8]
+ CRUSH rule 4 x 522 [8,1]
+ CRUSH rule 4 x 523 [4,2]
+ CRUSH rule 4 x 524 [2]
+ CRUSH rule 4 x 525 [2,1]
+ CRUSH rule 4 x 526 [1,8]
+ CRUSH rule 4 x 527 [1,2,4]
+ CRUSH rule 4 x 528 [1]
+ CRUSH rule 4 x 529 [4,8,2]
+ CRUSH rule 4 x 530 [8]
+ CRUSH rule 4 x 531 [8,1,2]
+ CRUSH rule 4 x 532 [6,4,8]
+ CRUSH rule 4 x 533 [4,8]
+ CRUSH rule 4 x 534 [8]
+ CRUSH rule 4 x 535 [8,6,1]
+ CRUSH rule 4 x 536 [8,2]
+ CRUSH rule 4 x 537 [4,8]
+ CRUSH rule 4 x 538 [8,4]
+ CRUSH rule 4 x 539 [8]
+ CRUSH rule 4 x 540 [1,8,2]
+ CRUSH rule 4 x 541 [2,4,1]
+ CRUSH rule 4 x 542 []
+ CRUSH rule 4 x 543 [8,2]
+ CRUSH rule 4 x 544 [4,8]
+ CRUSH rule 4 x 545 [8,2]
+ CRUSH rule 4 x 546 [8,1]
+ CRUSH rule 4 x 547 [8,2,1]
+ CRUSH rule 4 x 548 [4,2,1]
+ CRUSH rule 4 x 549 [8,2]
+ CRUSH rule 4 x 550 [2,4]
+ CRUSH rule 4 x 551 [8]
+ CRUSH rule 4 x 552 [4]
+ CRUSH rule 4 x 553 [2]
+ CRUSH rule 4 x 554 [1,8]
+ CRUSH rule 4 x 555 [4,1,8]
+ CRUSH rule 4 x 556 []
+ CRUSH rule 4 x 557 [8]
+ CRUSH rule 4 x 558 [4,1,2]
+ CRUSH rule 4 x 559 [2,8]
+ CRUSH rule 4 x 560 [8]
+ CRUSH rule 4 x 561 [8,4]
+ CRUSH rule 4 x 562 [4,1]
+ CRUSH rule 4 x 563 [2,8,1]
+ CRUSH rule 4 x 564 [1,8]
+ CRUSH rule 4 x 565 [4,8]
+ CRUSH rule 4 x 566 [4,8,2]
+ CRUSH rule 4 x 567 [4,8,1]
+ CRUSH rule 4 x 568 [8,1]
+ CRUSH rule 4 x 569 [4,2]
+ CRUSH rule 4 x 570 [1]
+ CRUSH rule 4 x 571 [8]
+ CRUSH rule 4 x 572 [4,1]
+ CRUSH rule 4 x 573 [1,8]
+ CRUSH rule 4 x 574 [2,1]
+ CRUSH rule 4 x 575 [8,1]
+ CRUSH rule 4 x 576 [4,8]
+ CRUSH rule 4 x 577 [8,2]
+ CRUSH rule 4 x 578 [8]
+ CRUSH rule 4 x 579 [4,1,8]
+ CRUSH rule 4 x 580 [2,8]
+ CRUSH rule 4 x 581 [8,2]
+ CRUSH rule 4 x 582 [2,8]
+ CRUSH rule 4 x 583 [8,1]
+ CRUSH rule 4 x 584 [8,1,2]
+ CRUSH rule 4 x 585 [8,1,4]
+ CRUSH rule 4 x 586 [1,2,8]
+ CRUSH rule 4 x 587 [2,4,1]
+ CRUSH rule 4 x 588 [4,8]
+ CRUSH rule 4 x 589 [8,1]
+ CRUSH rule 4 x 590 [8,2]
+ CRUSH rule 4 x 591 [4,2]
+ CRUSH rule 4 x 592 [2,1,4]
+ CRUSH rule 4 x 593 [1,8,2]
+ CRUSH rule 4 x 594 [2,8]
+ CRUSH rule 4 x 595 [8,1]
+ CRUSH rule 4 x 596 []
+ CRUSH rule 4 x 597 [1,2]
+ CRUSH rule 4 x 598 [2]
+ CRUSH rule 4 x 599 [4,2,1]
+ CRUSH rule 4 x 600 [8,1]
+ CRUSH rule 4 x 601 [1,8]
+ CRUSH rule 4 x 602 [8]
+ CRUSH rule 4 x 603 [1,8]
+ CRUSH rule 4 x 604 [8]
+ CRUSH rule 4 x 605 [2]
+ CRUSH rule 4 x 606 [2,1]
+ CRUSH rule 4 x 607 [2,4,8]
+ CRUSH rule 4 x 608 [4,2]
+ CRUSH rule 4 x 609 [4,2]
+ CRUSH rule 4 x 610 [8]
+ CRUSH rule 4 x 611 [1,2]
+ CRUSH rule 4 x 612 [2,1,8]
+ CRUSH rule 4 x 613 [8,2,1]
+ CRUSH rule 4 x 614 [8]
+ CRUSH rule 4 x 615 [8,2]
+ CRUSH rule 4 x 616 [1,8,2]
+ CRUSH rule 4 x 617 [8,1,2]
+ CRUSH rule 4 x 618 [8,4]
+ CRUSH rule 4 x 619 [4,1,8]
+ CRUSH rule 4 x 620 [1,2]
+ CRUSH rule 4 x 621 [8]
+ CRUSH rule 4 x 622 [2,4]
+ CRUSH rule 4 x 623 [2,8,1]
+ CRUSH rule 4 x 624 [4,1]
+ CRUSH rule 4 x 625 [2,8]
+ CRUSH rule 4 x 626 [8,2]
+ CRUSH rule 4 x 627 [2,8,1]
+ CRUSH rule 4 x 628 [8,2]
+ CRUSH rule 4 x 629 [2,8,4]
+ CRUSH rule 4 x 630 [2,8,1]
+ CRUSH rule 4 x 631 [1,8]
+ CRUSH rule 4 x 632 [8,2,1]
+ CRUSH rule 4 x 633 [8]
+ CRUSH rule 4 x 634 [1,2]
+ CRUSH rule 4 x 635 [4,8]
+ CRUSH rule 4 x 636 [1,4,2]
+ CRUSH rule 4 x 637 [1,2]
+ CRUSH rule 4 x 638 [8,1]
+ CRUSH rule 4 x 639 [2,1]
+ CRUSH rule 4 x 640 [1,2]
+ CRUSH rule 4 x 641 [8,2,1]
+ CRUSH rule 4 x 642 [2,1]
+ CRUSH rule 4 x 643 [1]
+ CRUSH rule 4 x 644 [8,1]
+ CRUSH rule 4 x 645 [8]
+ CRUSH rule 4 x 646 [8,1,4]
+ CRUSH rule 4 x 647 [8,1]
+ CRUSH rule 4 x 648 [1,8]
+ CRUSH rule 4 x 649 [4,8]
+ CRUSH rule 4 x 650 [8]
+ CRUSH rule 4 x 651 [4,6,8]
+ CRUSH rule 4 x 652 [4,8]
+ CRUSH rule 4 x 653 [8,2]
+ CRUSH rule 4 x 654 [6,2]
+ CRUSH rule 4 x 655 [1,4]
+ CRUSH rule 4 x 656 [8]
+ CRUSH rule 4 x 657 [6,1,2]
+ CRUSH rule 4 x 658 [8]
+ CRUSH rule 4 x 659 [4,8]
+ CRUSH rule 4 x 660 [8]
+ CRUSH rule 4 x 661 [1,8]
+ CRUSH rule 4 x 662 []
+ CRUSH rule 4 x 663 [1,4,8]
+ CRUSH rule 4 x 664 [1,4,2]
+ CRUSH rule 4 x 665 [4,6,8]
+ CRUSH rule 4 x 666 [2,8]
+ CRUSH rule 4 x 667 [1,4]
+ CRUSH rule 4 x 668 [4,8]
+ CRUSH rule 4 x 669 [6,4]
+ CRUSH rule 4 x 670 [4,2]
+ CRUSH rule 4 x 671 [2,1]
+ CRUSH rule 4 x 672 [4,2]
+ CRUSH rule 4 x 673 [4,2]
+ CRUSH rule 4 x 674 [1,8]
+ CRUSH rule 4 x 675 [1,8,6]
+ CRUSH rule 4 x 676 [2,1,4]
+ CRUSH rule 4 x 677 [4,1]
+ CRUSH rule 4 x 678 [2,4,1]
+ CRUSH rule 4 x 679 [8,2,1]
+ CRUSH rule 4 x 680 [2,8]
+ CRUSH rule 4 x 681 [8]
+ CRUSH rule 4 x 682 [1,4,2]
+ CRUSH rule 4 x 683 [1,2,4]
+ CRUSH rule 4 x 684 [8,1]
+ CRUSH rule 4 x 685 [8,1]
+ CRUSH rule 4 x 686 [1,4]
+ CRUSH rule 4 x 687 [6]
+ CRUSH rule 4 x 688 [4,8,2]
+ CRUSH rule 4 x 689 [8,4,2]
+ CRUSH rule 4 x 690 [8,1]
+ CRUSH rule 4 x 691 [1]
+ CRUSH rule 4 x 692 [8,2]
+ CRUSH rule 4 x 693 [8,4]
+ CRUSH rule 4 x 694 [8,4,1]
+ CRUSH rule 4 x 695 [2,8]
+ CRUSH rule 4 x 696 [1]
+ CRUSH rule 4 x 697 [8,1,2]
+ CRUSH rule 4 x 698 [8,2,1]
+ CRUSH rule 4 x 699 [1,8]
+ CRUSH rule 4 x 700 [1,2]
+ CRUSH rule 4 x 701 [2]
+ CRUSH rule 4 x 702 [1]
+ CRUSH rule 4 x 703 [8]
+ CRUSH rule 4 x 704 [1,4,8]
+ CRUSH rule 4 x 705 [8,2]
+ CRUSH rule 4 x 706 [1,2,4]
+ CRUSH rule 4 x 707 [8]
+ CRUSH rule 4 x 708 [4,8]
+ CRUSH rule 4 x 709 [8,2]
+ CRUSH rule 4 x 710 [8]
+ CRUSH rule 4 x 711 [2,4,8]
+ CRUSH rule 4 x 712 [2,8]
+ CRUSH rule 4 x 713 [8]
+ CRUSH rule 4 x 714 [2,1]
+ CRUSH rule 4 x 715 [1,2]
+ CRUSH rule 4 x 716 [4,8,2]
+ CRUSH rule 4 x 717 [8,2]
+ CRUSH rule 4 x 718 [8,6]
+ CRUSH rule 4 x 719 [2,6,4]
+ CRUSH rule 4 x 720 [8,2]
+ CRUSH rule 4 x 721 [4,6]
+ CRUSH rule 4 x 722 [8]
+ CRUSH rule 4 x 723 [4,1,2]
+ CRUSH rule 4 x 724 [2,6,1]
+ CRUSH rule 4 x 725 [1,2]
+ CRUSH rule 4 x 726 [4,8]
+ CRUSH rule 4 x 727 [4,8]
+ CRUSH rule 4 x 728 [2,1]
+ CRUSH rule 4 x 729 [8]
+ CRUSH rule 4 x 730 [4,8]
+ CRUSH rule 4 x 731 [4,1]
+ CRUSH rule 4 x 732 [1]
+ CRUSH rule 4 x 733 [4,8]
+ CRUSH rule 4 x 734 [8,4,2]
+ CRUSH rule 4 x 735 [4,8]
+ CRUSH rule 4 x 736 [4,8]
+ CRUSH rule 4 x 737 [1,2,8]
+ CRUSH rule 4 x 738 [4,2]
+ CRUSH rule 4 x 739 [2,1]
+ CRUSH rule 4 x 740 [1,2,8]
+ CRUSH rule 4 x 741 [8,1]
+ CRUSH rule 4 x 742 [8,2,1]
+ CRUSH rule 4 x 743 [8,1,2]
+ CRUSH rule 4 x 744 [4,8]
+ CRUSH rule 4 x 745 [1]
+ CRUSH rule 4 x 746 [1]
+ CRUSH rule 4 x 747 [8,1]
+ CRUSH rule 4 x 748 [2,8,1]
+ CRUSH rule 4 x 749 [4,8]
+ CRUSH rule 4 x 750 [1,8,4]
+ CRUSH rule 4 x 751 [2,1,8]
+ CRUSH rule 4 x 752 [8,1]
+ CRUSH rule 4 x 753 [8,4]
+ CRUSH rule 4 x 754 [8]
+ CRUSH rule 4 x 755 [1,2]
+ CRUSH rule 4 x 756 [8,1]
+ CRUSH rule 4 x 757 [8,1]
+ CRUSH rule 4 x 758 [8,2]
+ CRUSH rule 4 x 759 [8,4]
+ CRUSH rule 4 x 760 [1,4]
+ CRUSH rule 4 x 761 [1,2]
+ CRUSH rule 4 x 762 [2,8]
+ CRUSH rule 4 x 763 [8]
+ CRUSH rule 4 x 764 [1,8,2]
+ CRUSH rule 4 x 765 [8,2]
+ CRUSH rule 4 x 766 [8]
+ CRUSH rule 4 x 767 [1,2]
+ CRUSH rule 4 x 768 [8,4,2]
+ CRUSH rule 4 x 769 [8,2]
+ CRUSH rule 4 x 770 [8,2]
+ CRUSH rule 4 x 771 [8,1,4]
+ CRUSH rule 4 x 772 [8,4]
+ CRUSH rule 4 x 773 [4,1]
+ CRUSH rule 4 x 774 [8]
+ CRUSH rule 4 x 775 [8,4]
+ CRUSH rule 4 x 776 [6,2,1]
+ CRUSH rule 4 x 777 [4,1,8]
+ CRUSH rule 4 x 778 [1,8,2]
+ CRUSH rule 4 x 779 [2,8]
+ CRUSH rule 4 x 780 [2,1,4]
+ CRUSH rule 4 x 781 [8]
+ CRUSH rule 4 x 782 [4,1]
+ CRUSH rule 4 x 783 [8,1]
+ CRUSH rule 4 x 784 [1,2,4]
+ CRUSH rule 4 x 785 [8,1,2]
+ CRUSH rule 4 x 786 [8]
+ CRUSH rule 4 x 787 [1,2,6]
+ CRUSH rule 4 x 788 [8,2]
+ CRUSH rule 4 x 789 [1]
+ CRUSH rule 4 x 790 [8]
+ CRUSH rule 4 x 791 [4,8]
+ CRUSH rule 4 x 792 [4,8]
+ CRUSH rule 4 x 793 [8,1,4]
+ CRUSH rule 4 x 794 [2,8]
+ CRUSH rule 4 x 795 [1,2]
+ CRUSH rule 4 x 796 []
+ CRUSH rule 4 x 797 [2,4]
+ CRUSH rule 4 x 798 [6,8]
+ CRUSH rule 4 x 799 [4,1]
+ CRUSH rule 4 x 800 [2]
+ CRUSH rule 4 x 801 [4,8]
+ CRUSH rule 4 x 802 [1,8,2]
+ CRUSH rule 4 x 803 [2,8]
+ CRUSH rule 4 x 804 [8,2]
+ CRUSH rule 4 x 805 [8]
+ CRUSH rule 4 x 806 [1,4]
+ CRUSH rule 4 x 807 [4,8]
+ CRUSH rule 4 x 808 [8,2]
+ CRUSH rule 4 x 809 [1]
+ CRUSH rule 4 x 810 [8]
+ CRUSH rule 4 x 811 [8]
+ CRUSH rule 4 x 812 [8,4]
+ CRUSH rule 4 x 813 [8,4]
+ CRUSH rule 4 x 814 [8]
+ CRUSH rule 4 x 815 [4,1,2]
+ CRUSH rule 4 x 816 [2,1,8]
+ CRUSH rule 4 x 817 [8]
+ CRUSH rule 4 x 818 [1]
+ CRUSH rule 4 x 819 [1]
+ CRUSH rule 4 x 820 [4]
+ CRUSH rule 4 x 821 [4,8]
+ CRUSH rule 4 x 822 [2,1,4]
+ CRUSH rule 4 x 823 [4,8,2]
+ CRUSH rule 4 x 824 [8]
+ CRUSH rule 4 x 825 [2,8]
+ CRUSH rule 4 x 826 [8,2,4]
+ CRUSH rule 4 x 827 [2,8,1]
+ CRUSH rule 4 x 828 [2,1]
+ CRUSH rule 4 x 829 [8]
+ CRUSH rule 4 x 830 [2,4]
+ CRUSH rule 4 x 831 [1,8,2]
+ CRUSH rule 4 x 832 [4]
+ CRUSH rule 4 x 833 [2,1,8]
+ CRUSH rule 4 x 834 [2]
+ CRUSH rule 4 x 835 [8,4]
+ CRUSH rule 4 x 836 [4]
+ CRUSH rule 4 x 837 [8,4,1]
+ CRUSH rule 4 x 838 [6,8,2]
+ CRUSH rule 4 x 839 [2,6]
+ CRUSH rule 4 x 840 [8]
+ CRUSH rule 4 x 841 [4,8]
+ CRUSH rule 4 x 842 [2,1]
+ CRUSH rule 4 x 843 [8,4]
+ CRUSH rule 4 x 844 [8,1]
+ CRUSH rule 4 x 845 [4,8]
+ CRUSH rule 4 x 846 [4,2,1]
+ CRUSH rule 4 x 847 [2,1,8]
+ CRUSH rule 4 x 848 [2,8,1]
+ CRUSH rule 4 x 849 [4]
+ CRUSH rule 4 x 850 [1,2]
+ CRUSH rule 4 x 851 [6,8]
+ CRUSH rule 4 x 852 [8,4]
+ CRUSH rule 4 x 853 [6,8,1]
+ CRUSH rule 4 x 854 [8,1]
+ CRUSH rule 4 x 855 [8,2]
+ CRUSH rule 4 x 856 [8,4]
+ CRUSH rule 4 x 857 [8,2]
+ CRUSH rule 4 x 858 [6,1]
+ CRUSH rule 4 x 859 [8,2]
+ CRUSH rule 4 x 860 [1,2]
+ CRUSH rule 4 x 861 [8]
+ CRUSH rule 4 x 862 [8,1]
+ CRUSH rule 4 x 863 [8,2]
+ CRUSH rule 4 x 864 [8]
+ CRUSH rule 4 x 865 [8,1,2]
+ CRUSH rule 4 x 866 [8]
+ CRUSH rule 4 x 867 [8,1]
+ CRUSH rule 4 x 868 [8,1]
+ CRUSH rule 4 x 869 [8,6,4]
+ CRUSH rule 4 x 870 [2,8]
+ CRUSH rule 4 x 871 []
+ CRUSH rule 4 x 872 [1]
+ CRUSH rule 4 x 873 [4,8]
+ CRUSH rule 4 x 874 [2,6,1]
+ CRUSH rule 4 x 875 [2,8,4]
+ CRUSH rule 4 x 876 [4,8,1]
+ CRUSH rule 4 x 877 [8,4,2]
+ CRUSH rule 4 x 878 [2]
+ CRUSH rule 4 x 879 [8]
+ CRUSH rule 4 x 880 [1]
+ CRUSH rule 4 x 881 [4,8,1]
+ CRUSH rule 4 x 882 [2,1]
+ CRUSH rule 4 x 883 [2,1]
+ CRUSH rule 4 x 884 [8,2,4]
+ CRUSH rule 4 x 885 [4,1]
+ CRUSH rule 4 x 886 [8]
+ CRUSH rule 4 x 887 [8,4,1]
+ CRUSH rule 4 x 888 [8,1]
+ CRUSH rule 4 x 889 [2,1,8]
+ CRUSH rule 4 x 890 [8,2,1]
+ CRUSH rule 4 x 891 [1,8,2]
+ CRUSH rule 4 x 892 [8,2,4]
+ CRUSH rule 4 x 893 [2,6]
+ CRUSH rule 4 x 894 [8,4,2]
+ CRUSH rule 4 x 895 [4,2]
+ CRUSH rule 4 x 896 [1,8,2]
+ CRUSH rule 4 x 897 [2,8]
+ CRUSH rule 4 x 898 [1,4]
+ CRUSH rule 4 x 899 [1,8]
+ CRUSH rule 4 x 900 [4,1,2]
+ CRUSH rule 4 x 901 [1,2]
+ CRUSH rule 4 x 902 [8,4]
+ CRUSH rule 4 x 903 [8]
+ CRUSH rule 4 x 904 [8]
+ CRUSH rule 4 x 905 [8,2]
+ CRUSH rule 4 x 906 [1,2]
+ CRUSH rule 4 x 907 [8,1,2]
+ CRUSH rule 4 x 908 [8,1]
+ CRUSH rule 4 x 909 [2]
+ CRUSH rule 4 x 910 [8,2]
+ CRUSH rule 4 x 911 [8]
+ CRUSH rule 4 x 912 [1,2,8]
+ CRUSH rule 4 x 913 [8]
+ CRUSH rule 4 x 914 [6,4,8]
+ CRUSH rule 4 x 915 [8,2]
+ CRUSH rule 4 x 916 [4,1]
+ CRUSH rule 4 x 917 [1,4]
+ CRUSH rule 4 x 918 [8,2,1]
+ CRUSH rule 4 x 919 [8,2]
+ CRUSH rule 4 x 920 [8]
+ CRUSH rule 4 x 921 [1]
+ CRUSH rule 4 x 922 [8]
+ CRUSH rule 4 x 923 [4,8]
+ CRUSH rule 4 x 924 []
+ CRUSH rule 4 x 925 [4,8]
+ CRUSH rule 4 x 926 []
+ CRUSH rule 4 x 927 [1,8,4]
+ CRUSH rule 4 x 928 [8,1,2]
+ CRUSH rule 4 x 929 [4,1]
+ CRUSH rule 4 x 930 [2,8]
+ CRUSH rule 4 x 931 [1,2]
+ CRUSH rule 4 x 932 [4,1]
+ CRUSH rule 4 x 933 [8,4]
+ CRUSH rule 4 x 934 [8]
+ CRUSH rule 4 x 935 [8]
+ CRUSH rule 4 x 936 [1,8]
+ CRUSH rule 4 x 937 [4]
+ CRUSH rule 4 x 938 [8,4]
+ CRUSH rule 4 x 939 [2,8,1]
+ CRUSH rule 4 x 940 [8]
+ CRUSH rule 4 x 941 [2,1]
+ CRUSH rule 4 x 942 [1,2]
+ CRUSH rule 4 x 943 [8,2]
+ CRUSH rule 4 x 944 [8]
+ CRUSH rule 4 x 945 [8,2,4]
+ CRUSH rule 4 x 946 [2,1,8]
+ CRUSH rule 4 x 947 []
+ CRUSH rule 4 x 948 [8]
+ CRUSH rule 4 x 949 [6,1,8]
+ CRUSH rule 4 x 950 [8]
+ CRUSH rule 4 x 951 []
+ CRUSH rule 4 x 952 [2,1,8]
+ CRUSH rule 4 x 953 [1,4]
+ CRUSH rule 4 x 954 [2]
+ CRUSH rule 4 x 955 [8,1]
+ CRUSH rule 4 x 956 [1,2,8]
+ CRUSH rule 4 x 957 [8,1]
+ CRUSH rule 4 x 958 [8,4]
+ CRUSH rule 4 x 959 [4,2,8]
+ CRUSH rule 4 x 960 [6]
+ CRUSH rule 4 x 961 [1,2]
+ CRUSH rule 4 x 962 [8,4]
+ CRUSH rule 4 x 963 [2,4,1]
+ CRUSH rule 4 x 964 [1]
+ CRUSH rule 4 x 965 [8]
+ CRUSH rule 4 x 966 [4,8]
+ CRUSH rule 4 x 967 [8,4]
+ CRUSH rule 4 x 968 [8,2]
+ CRUSH rule 4 x 969 [8,2]
+ CRUSH rule 4 x 970 [2,8,4]
+ CRUSH rule 4 x 971 [1,8]
+ CRUSH rule 4 x 972 [1,8]
+ CRUSH rule 4 x 973 [1,2]
+ CRUSH rule 4 x 974 [4,2]
+ CRUSH rule 4 x 975 [4,8]
+ CRUSH rule 4 x 976 [4]
+ CRUSH rule 4 x 977 [8,4,2]
+ CRUSH rule 4 x 978 [8,2]
+ CRUSH rule 4 x 979 [8,2]
+ CRUSH rule 4 x 980 [8,2]
+ CRUSH rule 4 x 981 [8,2]
+ CRUSH rule 4 x 982 [2,1]
+ CRUSH rule 4 x 983 [4,8]
+ CRUSH rule 4 x 984 [2,1]
+ CRUSH rule 4 x 985 [2,4]
+ CRUSH rule 4 x 986 [8,4]
+ CRUSH rule 4 x 987 [2,1]
+ CRUSH rule 4 x 988 [1,4]
+ CRUSH rule 4 x 989 [1,8]
+ CRUSH rule 4 x 990 [1,2,8]
+ CRUSH rule 4 x 991 [1,4,2]
+ CRUSH rule 4 x 992 [8,1,4]
+ CRUSH rule 4 x 993 [2,8,1]
+ CRUSH rule 4 x 994 [4]
+ CRUSH rule 4 x 995 [8,2]
+ CRUSH rule 4 x 996 [8,4]
+ CRUSH rule 4 x 997 [8,4,1]
+ CRUSH rule 4 x 998 [8,1,2]
+ CRUSH rule 4 x 999 [1,8]
+ CRUSH rule 4 x 1000 [8,4,2]
+ CRUSH rule 4 x 1001 [2,1]
+ CRUSH rule 4 x 1002 [1,2]
+ CRUSH rule 4 x 1003 [2,8]
+ CRUSH rule 4 x 1004 [8,1,2]
+ CRUSH rule 4 x 1005 [8,1,2]
+ CRUSH rule 4 x 1006 [1,2]
+ CRUSH rule 4 x 1007 [1,2,4]
+ CRUSH rule 4 x 1008 [1,8,2]
+ CRUSH rule 4 x 1009 [6,8,4]
+ CRUSH rule 4 x 1010 [1]
+ CRUSH rule 4 x 1011 [4,2]
+ CRUSH rule 4 x 1012 [2,8]
+ CRUSH rule 4 x 1013 [1,2]
+ CRUSH rule 4 x 1014 [2,8,4]
+ CRUSH rule 4 x 1015 [8]
+ CRUSH rule 4 x 1016 [2,1]
+ CRUSH rule 4 x 1017 [6,2,1]
+ CRUSH rule 4 x 1018 [4]
+ CRUSH rule 4 x 1019 [4,8]
+ CRUSH rule 4 x 1020 [1]
+ CRUSH rule 4 x 1021 [2,1]
+ CRUSH rule 4 x 1022 [1,8]
+ CRUSH rule 4 x 1023 [4,2,1]
+ rule 4 (choose-set-two) num_rep 3 result size == 0:\t19/1024 (esc)
+ rule 4 (choose-set-two) num_rep 3 result size == 1:\t210/1024 (esc)
+ rule 4 (choose-set-two) num_rep 3 result size == 2:\t508/1024 (esc)
+ rule 4 (choose-set-two) num_rep 3 result size == 3:\t287/1024 (esc)
+ rule 5 (chooseleaf-set), x = 0..1023, numrep = 2..3
+ CRUSH rule 5 x 0 [2,4]
+ CRUSH rule 5 x 1 [2,8]
+ CRUSH rule 5 x 2 [1,8]
+ CRUSH rule 5 x 3 [8,1]
+ CRUSH rule 5 x 4 [4,2]
+ CRUSH rule 5 x 5 [8,2]
+ CRUSH rule 5 x 6 [2,8]
+ CRUSH rule 5 x 7 [4,8]
+ CRUSH rule 5 x 8 [4,8]
+ CRUSH rule 5 x 9 [2,4]
+ CRUSH rule 5 x 10 [2,8]
+ CRUSH rule 5 x 11 [2,8]
+ CRUSH rule 5 x 12 [2,8]
+ CRUSH rule 5 x 13 [4,8]
+ CRUSH rule 5 x 14 [8,2]
+ CRUSH rule 5 x 15 [8,2]
+ CRUSH rule 5 x 16 [8,2]
+ CRUSH rule 5 x 17 [4,1]
+ CRUSH rule 5 x 18 [1,8]
+ CRUSH rule 5 x 19 [8,4]
+ CRUSH rule 5 x 20 [2,8]
+ CRUSH rule 5 x 21 [8,2]
+ CRUSH rule 5 x 22 [8,1]
+ CRUSH rule 5 x 23 [4,8]
+ CRUSH rule 5 x 24 [1,8]
+ CRUSH rule 5 x 25 [4,8]
+ CRUSH rule 5 x 26 [2,8]
+ CRUSH rule 5 x 27 [4,1]
+ CRUSH rule 5 x 28 [8,2]
+ CRUSH rule 5 x 29 [8,4]
+ CRUSH rule 5 x 30 [4,8]
+ CRUSH rule 5 x 31 [8,1]
+ CRUSH rule 5 x 32 [6,1]
+ CRUSH rule 5 x 33 [2,8]
+ CRUSH rule 5 x 34 [2,8]
+ CRUSH rule 5 x 35 [1,8]
+ CRUSH rule 5 x 36 [8,2]
+ CRUSH rule 5 x 37 [1,8]
+ CRUSH rule 5 x 38 [4,8]
+ CRUSH rule 5 x 39 [8,2]
+ CRUSH rule 5 x 40 [8,2]
+ CRUSH rule 5 x 41 [2,8]
+ CRUSH rule 5 x 42 [8,2]
+ CRUSH rule 5 x 43 [1,8]
+ CRUSH rule 5 x 44 [1,8]
+ CRUSH rule 5 x 45 [8,2]
+ CRUSH rule 5 x 46 [2,8]
+ CRUSH rule 5 x 47 [4,2]
+ CRUSH rule 5 x 48 [8,1]
+ CRUSH rule 5 x 49 [8,2]
+ CRUSH rule 5 x 50 [4,1]
+ CRUSH rule 5 x 51 [8,2]
+ CRUSH rule 5 x 52 [8,1]
+ CRUSH rule 5 x 53 [4,8]
+ CRUSH rule 5 x 54 [8,4]
+ CRUSH rule 5 x 55 [8,2]
+ CRUSH rule 5 x 56 [8,4]
+ CRUSH rule 5 x 57 [8,1]
+ CRUSH rule 5 x 58 [1,8]
+ CRUSH rule 5 x 59 [2,8]
+ CRUSH rule 5 x 60 [4,2]
+ CRUSH rule 5 x 61 [4,8]
+ CRUSH rule 5 x 62 [8,1]
+ CRUSH rule 5 x 63 [8,2]
+ CRUSH rule 5 x 64 [4,2]
+ CRUSH rule 5 x 65 [8,4]
+ CRUSH rule 5 x 66 [4,8]
+ CRUSH rule 5 x 67 [4,2]
+ CRUSH rule 5 x 68 [1,8]
+ CRUSH rule 5 x 69 [2,8]
+ CRUSH rule 5 x 70 [8,2]
+ CRUSH rule 5 x 71 [2,8]
+ CRUSH rule 5 x 72 [8,1]
+ CRUSH rule 5 x 73 [2,8]
+ CRUSH rule 5 x 74 [1,8]
+ CRUSH rule 5 x 75 [4,2]
+ CRUSH rule 5 x 76 [4,1]
+ CRUSH rule 5 x 77 [8,2]
+ CRUSH rule 5 x 78 [1,6]
+ CRUSH rule 5 x 79 [4,1]
+ CRUSH rule 5 x 80 [2,4]
+ CRUSH rule 5 x 81 [2,8]
+ CRUSH rule 5 x 82 [6,1]
+ CRUSH rule 5 x 83 [2,8]
+ CRUSH rule 5 x 84 [8,2]
+ CRUSH rule 5 x 85 [4,8]
+ CRUSH rule 5 x 86 [2,8]
+ CRUSH rule 5 x 87 [2,8]
+ CRUSH rule 5 x 88 [1,6]
+ CRUSH rule 5 x 89 [1,8]
+ CRUSH rule 5 x 90 [8,4]
+ CRUSH rule 5 x 91 [4,8]
+ CRUSH rule 5 x 92 [1,8]
+ CRUSH rule 5 x 93 [8,4]
+ CRUSH rule 5 x 94 [1,8]
+ CRUSH rule 5 x 95 [8,1]
+ CRUSH rule 5 x 96 [8,2]
+ CRUSH rule 5 x 97 [8,1]
+ CRUSH rule 5 x 98 [2,8]
+ CRUSH rule 5 x 99 [2,8]
+ CRUSH rule 5 x 100 [1,8]
+ CRUSH rule 5 x 101 [8,1]
+ CRUSH rule 5 x 102 [2,8]
+ CRUSH rule 5 x 103 [8,2]
+ CRUSH rule 5 x 104 [8,4]
+ CRUSH rule 5 x 105 [2,4]
+ CRUSH rule 5 x 106 [1,8]
+ CRUSH rule 5 x 107 [1,8]
+ CRUSH rule 5 x 108 [8,2]
+ CRUSH rule 5 x 109 [1,4]
+ CRUSH rule 5 x 110 [4,2]
+ CRUSH rule 5 x 111 [2,4]
+ CRUSH rule 5 x 112 [2,8]
+ CRUSH rule 5 x 113 [8,2]
+ CRUSH rule 5 x 114 [8,4]
+ CRUSH rule 5 x 115 [8,2]
+ CRUSH rule 5 x 116 [1,8]
+ CRUSH rule 5 x 117 [6,1]
+ CRUSH rule 5 x 118 [2,8]
+ CRUSH rule 5 x 119 [8,1]
+ CRUSH rule 5 x 120 [2,4]
+ CRUSH rule 5 x 121 [2,8]
+ CRUSH rule 5 x 122 [8,1]
+ CRUSH rule 5 x 123 [2,8]
+ CRUSH rule 5 x 124 [2,8]
+ CRUSH rule 5 x 125 [1,8]
+ CRUSH rule 5 x 126 [1,8]
+ CRUSH rule 5 x 127 [4,8]
+ CRUSH rule 5 x 128 [8,2]
+ CRUSH rule 5 x 129 [2,4]
+ CRUSH rule 5 x 130 [4,8]
+ CRUSH rule 5 x 131 [1,4]
+ CRUSH rule 5 x 132 [1,8]
+ CRUSH rule 5 x 133 [8,1]
+ CRUSH rule 5 x 134 [1,8]
+ CRUSH rule 5 x 135 [4,8]
+ CRUSH rule 5 x 136 [2,4]
+ CRUSH rule 5 x 137 [8,4]
+ CRUSH rule 5 x 138 [8,4]
+ CRUSH rule 5 x 139 [4,2]
+ CRUSH rule 5 x 140 [1,8]
+ CRUSH rule 5 x 141 [8,2]
+ CRUSH rule 5 x 142 [4,1]
+ CRUSH rule 5 x 143 [4,8]
+ CRUSH rule 5 x 144 [8,1]
+ CRUSH rule 5 x 145 [8,1]
+ CRUSH rule 5 x 146 [2,8]
+ CRUSH rule 5 x 147 [2,8]
+ CRUSH rule 5 x 148 [4,1]
+ CRUSH rule 5 x 149 [4,8]
+ CRUSH rule 5 x 150 [1,8]
+ CRUSH rule 5 x 151 [1,8]
+ CRUSH rule 5 x 152 [8,2]
+ CRUSH rule 5 x 153 [8,4]
+ CRUSH rule 5 x 154 [4,2]
+ CRUSH rule 5 x 155 [4,8]
+ CRUSH rule 5 x 156 [4,2]
+ CRUSH rule 5 x 157 [1,8]
+ CRUSH rule 5 x 158 [2,8]
+ CRUSH rule 5 x 159 [8,2]
+ CRUSH rule 5 x 160 [2,8]
+ CRUSH rule 5 x 161 [1,4]
+ CRUSH rule 5 x 162 [1,8]
+ CRUSH rule 5 x 163 [4,8]
+ CRUSH rule 5 x 164 [8,1]
+ CRUSH rule 5 x 165 [8,2]
+ CRUSH rule 5 x 166 [2,8]
+ CRUSH rule 5 x 167 [1,8]
+ CRUSH rule 5 x 168 [4,2]
+ CRUSH rule 5 x 169 [2,8]
+ CRUSH rule 5 x 170 [1,8]
+ CRUSH rule 5 x 171 [8,4]
+ CRUSH rule 5 x 172 [1,8]
+ CRUSH rule 5 x 173 [8,4]
+ CRUSH rule 5 x 174 [1,6]
+ CRUSH rule 5 x 175 [8,1]
+ CRUSH rule 5 x 176 [2,8]
+ CRUSH rule 5 x 177 [1,8]
+ CRUSH rule 5 x 178 [4,2]
+ CRUSH rule 5 x 179 [1,8]
+ CRUSH rule 5 x 180 [8,1]
+ CRUSH rule 5 x 181 [8,2]
+ CRUSH rule 5 x 182 [8,1]
+ CRUSH rule 5 x 183 [8,4]
+ CRUSH rule 5 x 184 [4,8]
+ CRUSH rule 5 x 185 [8,1]
+ CRUSH rule 5 x 186 [2,4]
+ CRUSH rule 5 x 187 [1,8]
+ CRUSH rule 5 x 188 [1,8]
+ CRUSH rule 5 x 189 [1,8]
+ CRUSH rule 5 x 190 [1,8]
+ CRUSH rule 5 x 191 [8,1]
+ CRUSH rule 5 x 192 [4,1]
+ CRUSH rule 5 x 193 [4,2]
+ CRUSH rule 5 x 194 [1,8]
+ CRUSH rule 5 x 195 [8,4]
+ CRUSH rule 5 x 196 [8,2]
+ CRUSH rule 5 x 197 [8,4]
+ CRUSH rule 5 x 198 [2,8]
+ CRUSH rule 5 x 199 [1,4]
+ CRUSH rule 5 x 200 [1,8]
+ CRUSH rule 5 x 201 [8,1]
+ CRUSH rule 5 x 202 [8,1]
+ CRUSH rule 5 x 203 [8,1]
+ CRUSH rule 5 x 204 [2,4]
+ CRUSH rule 5 x 205 [1,8]
+ CRUSH rule 5 x 206 [1,8]
+ CRUSH rule 5 x 207 [2,8]
+ CRUSH rule 5 x 208 [8,1]
+ CRUSH rule 5 x 209 [1,8]
+ CRUSH rule 5 x 210 [1,4]
+ CRUSH rule 5 x 211 [4,2]
+ CRUSH rule 5 x 212 [8,1]
+ CRUSH rule 5 x 213 [8,4]
+ CRUSH rule 5 x 214 [8,2]
+ CRUSH rule 5 x 215 [8,1]
+ CRUSH rule 5 x 216 [2,8]
+ CRUSH rule 5 x 217 [1,8]
+ CRUSH rule 5 x 218 [2,8]
+ CRUSH rule 5 x 219 [8,2]
+ CRUSH rule 5 x 220 [4,8]
+ CRUSH rule 5 x 221 [8,1]
+ CRUSH rule 5 x 222 [8,1]
+ CRUSH rule 5 x 223 [1,8]
+ CRUSH rule 5 x 224 [1,4]
+ CRUSH rule 5 x 225 [8,2]
+ CRUSH rule 5 x 226 [8,2]
+ CRUSH rule 5 x 227 [4,1]
+ CRUSH rule 5 x 228 [8,2]
+ CRUSH rule 5 x 229 [4,8]
+ CRUSH rule 5 x 230 [4,8]
+ CRUSH rule 5 x 231 [4,8]
+ CRUSH rule 5 x 232 [2,8]
+ CRUSH rule 5 x 233 [2,8]
+ CRUSH rule 5 x 234 [1,8]
+ CRUSH rule 5 x 235 [4,8]
+ CRUSH rule 5 x 236 [2,6]
+ CRUSH rule 5 x 237 [4,8]
+ CRUSH rule 5 x 238 [2,8]
+ CRUSH rule 5 x 239 [8,1]
+ CRUSH rule 5 x 240 [4,8]
+ CRUSH rule 5 x 241 [1,8]
+ CRUSH rule 5 x 242 [8,2]
+ CRUSH rule 5 x 243 [8,2]
+ CRUSH rule 5 x 244 [4,8]
+ CRUSH rule 5 x 245 [8,1]
+ CRUSH rule 5 x 246 [1,8]
+ CRUSH rule 5 x 247 [8,2]
+ CRUSH rule 5 x 248 [8,2]
+ CRUSH rule 5 x 249 [2,8]
+ CRUSH rule 5 x 250 [2,4]
+ CRUSH rule 5 x 251 [2,8]
+ CRUSH rule 5 x 252 [4,8]
+ CRUSH rule 5 x 253 [2,8]
+ CRUSH rule 5 x 254 [4,2]
+ CRUSH rule 5 x 255 [1,8]
+ CRUSH rule 5 x 256 [4,8]
+ CRUSH rule 5 x 257 [2,8]
+ CRUSH rule 5 x 258 [4,2]
+ CRUSH rule 5 x 259 [6,2]
+ CRUSH rule 5 x 260 [8,2]
+ CRUSH rule 5 x 261 [8,1]
+ CRUSH rule 5 x 262 [8,1]
+ CRUSH rule 5 x 263 [8,1]
+ CRUSH rule 5 x 264 [8,2]
+ CRUSH rule 5 x 265 [8,2]
+ CRUSH rule 5 x 266 [8,2]
+ CRUSH rule 5 x 267 [2,8]
+ CRUSH rule 5 x 268 [1,8]
+ CRUSH rule 5 x 269 [1,8]
+ CRUSH rule 5 x 270 [4,1]
+ CRUSH rule 5 x 271 [8,4]
+ CRUSH rule 5 x 272 [2,8]
+ CRUSH rule 5 x 273 [4,1]
+ CRUSH rule 5 x 274 [8,4]
+ CRUSH rule 5 x 275 [4,8]
+ CRUSH rule 5 x 276 [8,1]
+ CRUSH rule 5 x 277 [8,1]
+ CRUSH rule 5 x 278 [8,1]
+ CRUSH rule 5 x 279 [8,4]
+ CRUSH rule 5 x 280 [2,8]
+ CRUSH rule 5 x 281 [8,2]
+ CRUSH rule 5 x 282 [2,8]
+ CRUSH rule 5 x 283 [8,2]
+ CRUSH rule 5 x 284 [8,2]
+ CRUSH rule 5 x 285 [4,8]
+ CRUSH rule 5 x 286 [2,8]
+ CRUSH rule 5 x 287 [1,8]
+ CRUSH rule 5 x 288 [8,1]
+ CRUSH rule 5 x 289 [4,8]
+ CRUSH rule 5 x 290 [1,4]
+ CRUSH rule 5 x 291 [1,4]
+ CRUSH rule 5 x 292 [8,2]
+ CRUSH rule 5 x 293 [8,1]
+ CRUSH rule 5 x 294 [8,4]
+ CRUSH rule 5 x 295 [4,8]
+ CRUSH rule 5 x 296 [4,1]
+ CRUSH rule 5 x 297 [8,2]
+ CRUSH rule 5 x 298 [1,8]
+ CRUSH rule 5 x 299 [2,8]
+ CRUSH rule 5 x 300 [8,2]
+ CRUSH rule 5 x 301 [1,8]
+ CRUSH rule 5 x 302 [1,8]
+ CRUSH rule 5 x 303 [8,4]
+ CRUSH rule 5 x 304 [2,8]
+ CRUSH rule 5 x 305 [8,2]
+ CRUSH rule 5 x 306 [1,8]
+ CRUSH rule 5 x 307 [2,8]
+ CRUSH rule 5 x 308 [2,8]
+ CRUSH rule 5 x 309 [8,1]
+ CRUSH rule 5 x 310 [4,1]
+ CRUSH rule 5 x 311 [4,8]
+ CRUSH rule 5 x 312 [2,8]
+ CRUSH rule 5 x 313 [4,1]
+ CRUSH rule 5 x 314 [2,8]
+ CRUSH rule 5 x 315 [2,8]
+ CRUSH rule 5 x 316 [8,1]
+ CRUSH rule 5 x 317 [2,8]
+ CRUSH rule 5 x 318 [8,1]
+ CRUSH rule 5 x 319 [2,8]
+ CRUSH rule 5 x 320 [8,1]
+ CRUSH rule 5 x 321 [1,8]
+ CRUSH rule 5 x 322 [2,8]
+ CRUSH rule 5 x 323 [4,8]
+ CRUSH rule 5 x 324 [8,1]
+ CRUSH rule 5 x 325 [4,8]
+ CRUSH rule 5 x 326 [1,6]
+ CRUSH rule 5 x 327 [1,8]
+ CRUSH rule 5 x 328 [8,4]
+ CRUSH rule 5 x 329 [4,8]
+ CRUSH rule 5 x 330 [4,8]
+ CRUSH rule 5 x 331 [2,8]
+ CRUSH rule 5 x 332 [2,8]
+ CRUSH rule 5 x 333 [8,1]
+ CRUSH rule 5 x 334 [8,2]
+ CRUSH rule 5 x 335 [8,1]
+ CRUSH rule 5 x 336 [4,8]
+ CRUSH rule 5 x 337 [8,2]
+ CRUSH rule 5 x 338 [8,1]
+ CRUSH rule 5 x 339 [8,2]
+ CRUSH rule 5 x 340 [2,8]
+ CRUSH rule 5 x 341 [4,1]
+ CRUSH rule 5 x 342 [2,8]
+ CRUSH rule 5 x 343 [8,1]
+ CRUSH rule 5 x 344 [6,2]
+ CRUSH rule 5 x 345 [2,8]
+ CRUSH rule 5 x 346 [8,2]
+ CRUSH rule 5 x 347 [4,1]
+ CRUSH rule 5 x 348 [8,2]
+ CRUSH rule 5 x 349 [1,8]
+ CRUSH rule 5 x 350 [8,1]
+ CRUSH rule 5 x 351 [8,2]
+ CRUSH rule 5 x 352 [1,8]
+ CRUSH rule 5 x 353 [8,1]
+ CRUSH rule 5 x 354 [1,8]
+ CRUSH rule 5 x 355 [8,2]
+ CRUSH rule 5 x 356 [4,1]
+ CRUSH rule 5 x 357 [8,1]
+ CRUSH rule 5 x 358 [2,8]
+ CRUSH rule 5 x 359 [6,1]
+ CRUSH rule 5 x 360 [2,8]
+ CRUSH rule 5 x 361 [8,4]
+ CRUSH rule 5 x 362 [4,1]
+ CRUSH rule 5 x 363 [4,1]
+ CRUSH rule 5 x 364 [2,8]
+ CRUSH rule 5 x 365 [8,1]
+ CRUSH rule 5 x 366 [8,2]
+ CRUSH rule 5 x 367 [4,2]
+ CRUSH rule 5 x 368 [8,4]
+ CRUSH rule 5 x 369 [8,1]
+ CRUSH rule 5 x 370 [8,2]
+ CRUSH rule 5 x 371 [1,4]
+ CRUSH rule 5 x 372 [1,8]
+ CRUSH rule 5 x 373 [1,8]
+ CRUSH rule 5 x 374 [8,1]
+ CRUSH rule 5 x 375 [8,4]
+ CRUSH rule 5 x 376 [8,1]
+ CRUSH rule 5 x 377 [1,4]
+ CRUSH rule 5 x 378 [1,8]
+ CRUSH rule 5 x 379 [8,2]
+ CRUSH rule 5 x 380 [2,8]
+ CRUSH rule 5 x 381 [1,4]
+ CRUSH rule 5 x 382 [1,4]
+ CRUSH rule 5 x 383 [4,8]
+ CRUSH rule 5 x 384 [8,2]
+ CRUSH rule 5 x 385 [8,1]
+ CRUSH rule 5 x 386 [1,8]
+ CRUSH rule 5 x 387 [1,4]
+ CRUSH rule 5 x 388 [2,6]
+ CRUSH rule 5 x 389 [1,4]
+ CRUSH rule 5 x 390 [4,8]
+ CRUSH rule 5 x 391 [4,8]
+ CRUSH rule 5 x 392 [1,8]
+ CRUSH rule 5 x 393 [2,8]
+ CRUSH rule 5 x 394 [8,2]
+ CRUSH rule 5 x 395 [1,8]
+ CRUSH rule 5 x 396 [4,2]
+ CRUSH rule 5 x 397 [2,4]
+ CRUSH rule 5 x 398 [2,4]
+ CRUSH rule 5 x 399 [8,4]
+ CRUSH rule 5 x 400 [8,1]
+ CRUSH rule 5 x 401 [1,4]
+ CRUSH rule 5 x 402 [8,4]
+ CRUSH rule 5 x 403 [1,4]
+ CRUSH rule 5 x 404 [4,2]
+ CRUSH rule 5 x 405 [8,4]
+ CRUSH rule 5 x 406 [2,8]
+ CRUSH rule 5 x 407 [2,8]
+ CRUSH rule 5 x 408 [4,1]
+ CRUSH rule 5 x 409 [8,4]
+ CRUSH rule 5 x 410 [8,4]
+ CRUSH rule 5 x 411 [2,8]
+ CRUSH rule 5 x 412 [2,6]
+ CRUSH rule 5 x 413 [2,8]
+ CRUSH rule 5 x 414 [4,1]
+ CRUSH rule 5 x 415 [2,8]
+ CRUSH rule 5 x 416 [2,8]
+ CRUSH rule 5 x 417 [8,2]
+ CRUSH rule 5 x 418 [8,1]
+ CRUSH rule 5 x 419 [8,4]
+ CRUSH rule 5 x 420 [1,4]
+ CRUSH rule 5 x 421 [8,4]
+ CRUSH rule 5 x 422 [6,2]
+ CRUSH rule 5 x 423 [2,4]
+ CRUSH rule 5 x 424 [8,1]
+ CRUSH rule 5 x 425 [1,8]
+ CRUSH rule 5 x 426 [8,2]
+ CRUSH rule 5 x 427 [1,8]
+ CRUSH rule 5 x 428 [4,8]
+ CRUSH rule 5 x 429 [4,8]
+ CRUSH rule 5 x 430 [4,8]
+ CRUSH rule 5 x 431 [4,1]
+ CRUSH rule 5 x 432 [8,1]
+ CRUSH rule 5 x 433 [8,1]
+ CRUSH rule 5 x 434 [2,8]
+ CRUSH rule 5 x 435 [2,8]
+ CRUSH rule 5 x 436 [4,1]
+ CRUSH rule 5 x 437 [8,2]
+ CRUSH rule 5 x 438 [2,4]
+ CRUSH rule 5 x 439 [1,6]
+ CRUSH rule 5 x 440 [2,8]
+ CRUSH rule 5 x 441 [4,6]
+ CRUSH rule 5 x 442 [2,8]
+ CRUSH rule 5 x 443 [8,2]
+ CRUSH rule 5 x 444 [8,1]
+ CRUSH rule 5 x 445 [8,2]
+ CRUSH rule 5 x 446 [8,1]
+ CRUSH rule 5 x 447 [2,4]
+ CRUSH rule 5 x 448 [8,2]
+ CRUSH rule 5 x 449 [8,1]
+ CRUSH rule 5 x 450 [1,8]
+ CRUSH rule 5 x 451 [8,4]
+ CRUSH rule 5 x 452 [8,2]
+ CRUSH rule 5 x 453 [6,2]
+ CRUSH rule 5 x 454 [8,2]
+ CRUSH rule 5 x 455 [2,8]
+ CRUSH rule 5 x 456 [8,2]
+ CRUSH rule 5 x 457 [8,2]
+ CRUSH rule 5 x 458 [2,8]
+ CRUSH rule 5 x 459 [2,8]
+ CRUSH rule 5 x 460 [8,2]
+ CRUSH rule 5 x 461 [8,1]
+ CRUSH rule 5 x 462 [8,1]
+ CRUSH rule 5 x 463 [8,2]
+ CRUSH rule 5 x 464 [8,4]
+ CRUSH rule 5 x 465 [6,2]
+ CRUSH rule 5 x 466 [8,1]
+ CRUSH rule 5 x 467 [8,2]
+ CRUSH rule 5 x 468 [8,1]
+ CRUSH rule 5 x 469 [8,1]
+ CRUSH rule 5 x 470 [4,2]
+ CRUSH rule 5 x 471 [1,8]
+ CRUSH rule 5 x 472 [1,8]
+ CRUSH rule 5 x 473 [1,4]
+ CRUSH rule 5 x 474 [8,1]
+ CRUSH rule 5 x 475 [8,2]
+ CRUSH rule 5 x 476 [4,8]
+ CRUSH rule 5 x 477 [4,8]
+ CRUSH rule 5 x 478 [8,2]
+ CRUSH rule 5 x 479 [2,8]
+ CRUSH rule 5 x 480 [1,8]
+ CRUSH rule 5 x 481 [2,4]
+ CRUSH rule 5 x 482 [1,8]
+ CRUSH rule 5 x 483 [2,8]
+ CRUSH rule 5 x 484 [1,8]
+ CRUSH rule 5 x 485 [8,1]
+ CRUSH rule 5 x 486 [4,1]
+ CRUSH rule 5 x 487 [1,8]
+ CRUSH rule 5 x 488 [8,1]
+ CRUSH rule 5 x 489 [2,8]
+ CRUSH rule 5 x 490 [6,2]
+ CRUSH rule 5 x 491 [1,8]
+ CRUSH rule 5 x 492 [8,1]
+ CRUSH rule 5 x 493 [2,8]
+ CRUSH rule 5 x 494 [1,8]
+ CRUSH rule 5 x 495 [4,1]
+ CRUSH rule 5 x 496 [8,4]
+ CRUSH rule 5 x 497 [4,8]
+ CRUSH rule 5 x 498 [2,4]
+ CRUSH rule 5 x 499 [8,4]
+ CRUSH rule 5 x 500 [4,8]
+ CRUSH rule 5 x 501 [2,8]
+ CRUSH rule 5 x 502 [6,1]
+ CRUSH rule 5 x 503 [2,8]
+ CRUSH rule 5 x 504 [8,1]
+ CRUSH rule 5 x 505 [1,8]
+ CRUSH rule 5 x 506 [4,2]
+ CRUSH rule 5 x 507 [8,1]
+ CRUSH rule 5 x 508 [1,8]
+ CRUSH rule 5 x 509 [8,1]
+ CRUSH rule 5 x 510 [8,2]
+ CRUSH rule 5 x 511 [4,8]
+ CRUSH rule 5 x 512 [8,2]
+ CRUSH rule 5 x 513 [8,2]
+ CRUSH rule 5 x 514 [2,8]
+ CRUSH rule 5 x 515 [8,4]
+ CRUSH rule 5 x 516 [4,1]
+ CRUSH rule 5 x 517 [8,2]
+ CRUSH rule 5 x 518 [4,8]
+ CRUSH rule 5 x 519 [8,4]
+ CRUSH rule 5 x 520 [2,8]
+ CRUSH rule 5 x 521 [8,2]
+ CRUSH rule 5 x 522 [8,1]
+ CRUSH rule 5 x 523 [4,2]
+ CRUSH rule 5 x 524 [2,6]
+ CRUSH rule 5 x 525 [2,8]
+ CRUSH rule 5 x 526 [1,8]
+ CRUSH rule 5 x 527 [1,4]
+ CRUSH rule 5 x 528 [2,8]
+ CRUSH rule 5 x 529 [4,8]
+ CRUSH rule 5 x 530 [8,1]
+ CRUSH rule 5 x 531 [8,1]
+ CRUSH rule 5 x 532 [6,4]
+ CRUSH rule 5 x 533 [4,8]
+ CRUSH rule 5 x 534 [8,1]
+ CRUSH rule 5 x 535 [8,1]
+ CRUSH rule 5 x 536 [8,2]
+ CRUSH rule 5 x 537 [4,8]
+ CRUSH rule 5 x 538 [8,4]
+ CRUSH rule 5 x 539 [8,1]
+ CRUSH rule 5 x 540 [1,8]
+ CRUSH rule 5 x 541 [2,4]
+ CRUSH rule 5 x 542 [2,8]
+ CRUSH rule 5 x 543 [8,2]
+ CRUSH rule 5 x 544 [4,8]
+ CRUSH rule 5 x 545 [8,1]
+ CRUSH rule 5 x 546 [8,1]
+ CRUSH rule 5 x 547 [8,2]
+ CRUSH rule 5 x 548 [4,2]
+ CRUSH rule 5 x 549 [8,2]
+ CRUSH rule 5 x 550 [2,4]
+ CRUSH rule 5 x 551 [8,1]
+ CRUSH rule 5 x 552 [4,8]
+ CRUSH rule 5 x 553 [2,8]
+ CRUSH rule 5 x 554 [1,8]
+ CRUSH rule 5 x 555 [4,1]
+ CRUSH rule 5 x 556 [8,1]
+ CRUSH rule 5 x 557 [8,2]
+ CRUSH rule 5 x 558 [4,1]
+ CRUSH rule 5 x 559 [1,8]
+ CRUSH rule 5 x 560 [8,1]
+ CRUSH rule 5 x 561 [8,4]
+ CRUSH rule 5 x 562 [4,1]
+ CRUSH rule 5 x 563 [2,8]
+ CRUSH rule 5 x 564 [1,8]
+ CRUSH rule 5 x 565 [4,8]
+ CRUSH rule 5 x 566 [4,8]
+ CRUSH rule 5 x 567 [4,8]
+ CRUSH rule 5 x 568 [8,1]
+ CRUSH rule 5 x 569 [4,1]
+ CRUSH rule 5 x 570 [1,8]
+ CRUSH rule 5 x 571 [6,1]
+ CRUSH rule 5 x 572 [4,2]
+ CRUSH rule 5 x 573 [1,8]
+ CRUSH rule 5 x 574 [2,8]
+ CRUSH rule 5 x 575 [8,2]
+ CRUSH rule 5 x 576 [4,8]
+ CRUSH rule 5 x 577 [8,2]
+ CRUSH rule 5 x 578 [8,1]
+ CRUSH rule 5 x 579 [4,1]
+ CRUSH rule 5 x 580 [1,8]
+ CRUSH rule 5 x 581 [8,2]
+ CRUSH rule 5 x 582 [2,8]
+ CRUSH rule 5 x 583 [8,1]
+ CRUSH rule 5 x 584 [8,1]
+ CRUSH rule 5 x 585 [8,1]
+ CRUSH rule 5 x 586 [1,8]
+ CRUSH rule 5 x 587 [2,4]
+ CRUSH rule 5 x 588 [4,8]
+ CRUSH rule 5 x 589 [8,1]
+ CRUSH rule 5 x 590 [8,2]
+ CRUSH rule 5 x 591 [4,2]
+ CRUSH rule 5 x 592 [2,4]
+ CRUSH rule 5 x 593 [1,8]
+ CRUSH rule 5 x 594 [2,8]
+ CRUSH rule 5 x 595 [8,1]
+ CRUSH rule 5 x 596 [8,2]
+ CRUSH rule 5 x 597 [1,8]
+ CRUSH rule 5 x 598 [2,8]
+ CRUSH rule 5 x 599 [4,2]
+ CRUSH rule 5 x 600 [8,1]
+ CRUSH rule 5 x 601 [1,8]
+ CRUSH rule 5 x 602 [8,2]
+ CRUSH rule 5 x 603 [1,8]
+ CRUSH rule 5 x 604 [8,2]
+ CRUSH rule 5 x 605 [2,8]
+ CRUSH rule 5 x 606 [2,6]
+ CRUSH rule 5 x 607 [2,4]
+ CRUSH rule 5 x 608 [4,2]
+ CRUSH rule 5 x 609 [4,2]
+ CRUSH rule 5 x 610 [8,1]
+ CRUSH rule 5 x 611 [1,8]
+ CRUSH rule 5 x 612 [2,8]
+ CRUSH rule 5 x 613 [8,2]
+ CRUSH rule 5 x 614 [8,2]
+ CRUSH rule 5 x 615 [8,2]
+ CRUSH rule 5 x 616 [1,8]
+ CRUSH rule 5 x 617 [8,1]
+ CRUSH rule 5 x 618 [8,4]
+ CRUSH rule 5 x 619 [4,1]
+ CRUSH rule 5 x 620 [1,8]
+ CRUSH rule 5 x 621 [8,1]
+ CRUSH rule 5 x 622 [2,4]
+ CRUSH rule 5 x 623 [2,8]
+ CRUSH rule 5 x 624 [4,2]
+ CRUSH rule 5 x 625 [2,8]
+ CRUSH rule 5 x 626 [8,2]
+ CRUSH rule 5 x 627 [2,8]
+ CRUSH rule 5 x 628 [8,2]
+ CRUSH rule 5 x 629 [2,8]
+ CRUSH rule 5 x 630 [2,8]
+ CRUSH rule 5 x 631 [1,8]
+ CRUSH rule 5 x 632 [8,2]
+ CRUSH rule 5 x 633 [8,2]
+ CRUSH rule 5 x 634 [1,8]
+ CRUSH rule 5 x 635 [4,8]
+ CRUSH rule 5 x 636 [1,4]
+ CRUSH rule 5 x 637 [1,8]
+ CRUSH rule 5 x 638 [8,1]
+ CRUSH rule 5 x 639 [2,8]
+ CRUSH rule 5 x 640 [2,8]
+ CRUSH rule 5 x 641 [8,2]
+ CRUSH rule 5 x 642 [2,8]
+ CRUSH rule 5 x 643 [1,8]
+ CRUSH rule 5 x 644 [8,1]
+ CRUSH rule 5 x 645 [8,1]
+ CRUSH rule 5 x 646 [8,1]
+ CRUSH rule 5 x 647 [8,1]
+ CRUSH rule 5 x 648 [1,8]
+ CRUSH rule 5 x 649 [4,8]
+ CRUSH rule 5 x 650 [8,4]
+ CRUSH rule 5 x 651 [4,6]
+ CRUSH rule 5 x 652 [4,8]
+ CRUSH rule 5 x 653 [8,2]
+ CRUSH rule 5 x 654 [6,2]
+ CRUSH rule 5 x 655 [1,4]
+ CRUSH rule 5 x 656 [8,1]
+ CRUSH rule 5 x 657 [6,1]
+ CRUSH rule 5 x 658 [8,2]
+ CRUSH rule 5 x 659 [4,8]
+ CRUSH rule 5 x 660 [8,2]
+ CRUSH rule 5 x 661 [1,8]
+ CRUSH rule 5 x 662 [8,2]
+ CRUSH rule 5 x 663 [1,4]
+ CRUSH rule 5 x 664 [1,4]
+ CRUSH rule 5 x 665 [4,6]
+ CRUSH rule 5 x 666 [2,8]
+ CRUSH rule 5 x 667 [1,4]
+ CRUSH rule 5 x 668 [4,8]
+ CRUSH rule 5 x 669 [6,4]
+ CRUSH rule 5 x 670 [4,2]
+ CRUSH rule 5 x 671 [2,8]
+ CRUSH rule 5 x 672 [4,2]
+ CRUSH rule 5 x 673 [4,2]
+ CRUSH rule 5 x 674 [1,8]
+ CRUSH rule 5 x 675 [1,8]
+ CRUSH rule 5 x 676 [2,4]
+ CRUSH rule 5 x 677 [4,1]
+ CRUSH rule 5 x 678 [2,4]
+ CRUSH rule 5 x 679 [8,2]
+ CRUSH rule 5 x 680 [2,8]
+ CRUSH rule 5 x 681 [8,1]
+ CRUSH rule 5 x 682 [1,4]
+ CRUSH rule 5 x 683 [1,4]
+ CRUSH rule 5 x 684 [8,1]
+ CRUSH rule 5 x 685 [8,1]
+ CRUSH rule 5 x 686 [1,4]
+ CRUSH rule 5 x 687 [6,1]
+ CRUSH rule 5 x 688 [4,8]
+ CRUSH rule 5 x 689 [8,4]
+ CRUSH rule 5 x 690 [8,1]
+ CRUSH rule 5 x 691 [1,8]
+ CRUSH rule 5 x 692 [8,2]
+ CRUSH rule 5 x 693 [8,4]
+ CRUSH rule 5 x 694 [8,4]
+ CRUSH rule 5 x 695 [2,8]
+ CRUSH rule 5 x 696 [1,8]
+ CRUSH rule 5 x 697 [8,1]
+ CRUSH rule 5 x 698 [8,2]
+ CRUSH rule 5 x 699 [1,8]
+ CRUSH rule 5 x 700 [1,8]
+ CRUSH rule 5 x 701 [1,8]
+ CRUSH rule 5 x 702 [2,8]
+ CRUSH rule 5 x 703 [8,1]
+ CRUSH rule 5 x 704 [1,4]
+ CRUSH rule 5 x 705 [8,1]
+ CRUSH rule 5 x 706 [1,4]
+ CRUSH rule 5 x 707 [8,4]
+ CRUSH rule 5 x 708 [4,8]
+ CRUSH rule 5 x 709 [8,2]
+ CRUSH rule 5 x 710 [8,2]
+ CRUSH rule 5 x 711 [2,4]
+ CRUSH rule 5 x 712 [2,8]
+ CRUSH rule 5 x 713 [8,4]
+ CRUSH rule 5 x 714 [2,8]
+ CRUSH rule 5 x 715 [1,8]
+ CRUSH rule 5 x 716 [4,8]
+ CRUSH rule 5 x 717 [8,2]
+ CRUSH rule 5 x 718 [8,1]
+ CRUSH rule 5 x 719 [2,6]
+ CRUSH rule 5 x 720 [8,1]
+ CRUSH rule 5 x 721 [4,6]
+ CRUSH rule 5 x 722 [8,2]
+ CRUSH rule 5 x 723 [4,1]
+ CRUSH rule 5 x 724 [2,6]
+ CRUSH rule 5 x 725 [1,8]
+ CRUSH rule 5 x 726 [4,8]
+ CRUSH rule 5 x 727 [4,8]
+ CRUSH rule 5 x 728 [2,8]
+ CRUSH rule 5 x 729 [8,2]
+ CRUSH rule 5 x 730 [4,8]
+ CRUSH rule 5 x 731 [4,1]
+ CRUSH rule 5 x 732 [1,8]
+ CRUSH rule 5 x 733 [4,8]
+ CRUSH rule 5 x 734 [8,4]
+ CRUSH rule 5 x 735 [4,8]
+ CRUSH rule 5 x 736 [4,8]
+ CRUSH rule 5 x 737 [1,8]
+ CRUSH rule 5 x 738 [4,2]
+ CRUSH rule 5 x 739 [2,8]
+ CRUSH rule 5 x 740 [1,8]
+ CRUSH rule 5 x 741 [8,1]
+ CRUSH rule 5 x 742 [8,2]
+ CRUSH rule 5 x 743 [8,1]
+ CRUSH rule 5 x 744 [4,8]
+ CRUSH rule 5 x 745 [1,8]
+ CRUSH rule 5 x 746 [1,8]
+ CRUSH rule 5 x 747 [8,1]
+ CRUSH rule 5 x 748 [2,8]
+ CRUSH rule 5 x 749 [4,8]
+ CRUSH rule 5 x 750 [1,8]
+ CRUSH rule 5 x 751 [2,8]
+ CRUSH rule 5 x 752 [8,1]
+ CRUSH rule 5 x 753 [8,4]
+ CRUSH rule 5 x 754 [8,4]
+ CRUSH rule 5 x 755 [1,8]
+ CRUSH rule 5 x 756 [8,1]
+ CRUSH rule 5 x 757 [8,1]
+ CRUSH rule 5 x 758 [8,2]
+ CRUSH rule 5 x 759 [8,4]
+ CRUSH rule 5 x 760 [1,4]
+ CRUSH rule 5 x 761 [2,8]
+ CRUSH rule 5 x 762 [2,8]
+ CRUSH rule 5 x 763 [8,4]
+ CRUSH rule 5 x 764 [1,8]
+ CRUSH rule 5 x 765 [8,2]
+ CRUSH rule 5 x 766 [8,1]
+ CRUSH rule 5 x 767 [1,8]
+ CRUSH rule 5 x 768 [8,4]
+ CRUSH rule 5 x 769 [8,2]
+ CRUSH rule 5 x 770 [8,2]
+ CRUSH rule 5 x 771 [8,1]
+ CRUSH rule 5 x 772 [8,4]
+ CRUSH rule 5 x 773 [4,1]
+ CRUSH rule 5 x 774 [8,1]
+ CRUSH rule 5 x 775 [8,4]
+ CRUSH rule 5 x 776 [6,2]
+ CRUSH rule 5 x 777 [4,1]
+ CRUSH rule 5 x 778 [1,8]
+ CRUSH rule 5 x 779 [2,8]
+ CRUSH rule 5 x 780 [2,4]
+ CRUSH rule 5 x 781 [8,2]
+ CRUSH rule 5 x 782 [4,1]
+ CRUSH rule 5 x 783 [8,1]
+ CRUSH rule 5 x 784 [1,4]
+ CRUSH rule 5 x 785 [8,1]
+ CRUSH rule 5 x 786 [8,1]
+ CRUSH rule 5 x 787 [1,6]
+ CRUSH rule 5 x 788 [8,2]
+ CRUSH rule 5 x 789 [1,8]
+ CRUSH rule 5 x 790 [8,1]
+ CRUSH rule 5 x 791 [4,8]
+ CRUSH rule 5 x 792 [4,8]
+ CRUSH rule 5 x 793 [8,1]
+ CRUSH rule 5 x 794 [2,8]
+ CRUSH rule 5 x 795 [1,8]
+ CRUSH rule 5 x 796 [8,2]
+ CRUSH rule 5 x 797 [2,4]
+ CRUSH rule 5 x 798 [6,1]
+ CRUSH rule 5 x 799 [4,1]
+ CRUSH rule 5 x 800 [2,8]
+ CRUSH rule 5 x 801 [4,8]
+ CRUSH rule 5 x 802 [1,8]
+ CRUSH rule 5 x 803 [2,8]
+ CRUSH rule 5 x 804 [8,2]
+ CRUSH rule 5 x 805 [8,2]
+ CRUSH rule 5 x 806 [1,4]
+ CRUSH rule 5 x 807 [4,8]
+ CRUSH rule 5 x 808 [8,2]
+ CRUSH rule 5 x 809 [1,8]
+ CRUSH rule 5 x 810 [8,2]
+ CRUSH rule 5 x 811 [8,1]
+ CRUSH rule 5 x 812 [8,4]
+ CRUSH rule 5 x 813 [8,4]
+ CRUSH rule 5 x 814 [8,2]
+ CRUSH rule 5 x 815 [4,1]
+ CRUSH rule 5 x 816 [2,8]
+ CRUSH rule 5 x 817 [8,1]
+ CRUSH rule 5 x 818 [1,8]
+ CRUSH rule 5 x 819 [1,8]
+ CRUSH rule 5 x 820 [4,8]
+ CRUSH rule 5 x 821 [4,8]
+ CRUSH rule 5 x 822 [2,4]
+ CRUSH rule 5 x 823 [4,8]
+ CRUSH rule 5 x 824 [8,2]
+ CRUSH rule 5 x 825 [2,8]
+ CRUSH rule 5 x 826 [8,2]
+ CRUSH rule 5 x 827 [2,8]
+ CRUSH rule 5 x 828 [2,8]
+ CRUSH rule 5 x 829 [8,1]
+ CRUSH rule 5 x 830 [2,4]
+ CRUSH rule 5 x 831 [1,8]
+ CRUSH rule 5 x 832 [4,8]
+ CRUSH rule 5 x 833 [2,8]
+ CRUSH rule 5 x 834 [1,8]
+ CRUSH rule 5 x 835 [8,4]
+ CRUSH rule 5 x 836 [4,8]
+ CRUSH rule 5 x 837 [8,4]
+ CRUSH rule 5 x 838 [6,2]
+ CRUSH rule 5 x 839 [2,8]
+ CRUSH rule 5 x 840 [8,1]
+ CRUSH rule 5 x 841 [4,8]
+ CRUSH rule 5 x 842 [2,8]
+ CRUSH rule 5 x 843 [8,4]
+ CRUSH rule 5 x 844 [8,2]
+ CRUSH rule 5 x 845 [4,8]
+ CRUSH rule 5 x 846 [4,2]
+ CRUSH rule 5 x 847 [2,8]
+ CRUSH rule 5 x 848 [2,8]
+ CRUSH rule 5 x 849 [4,8]
+ CRUSH rule 5 x 850 [1,6]
+ CRUSH rule 5 x 851 [6,1]
+ CRUSH rule 5 x 852 [8,4]
+ CRUSH rule 5 x 853 [6,1]
+ CRUSH rule 5 x 854 [8,1]
+ CRUSH rule 5 x 855 [8,1]
+ CRUSH rule 5 x 856 [8,4]
+ CRUSH rule 5 x 857 [8,2]
+ CRUSH rule 5 x 858 [6,1]
+ CRUSH rule 5 x 859 [8,2]
+ CRUSH rule 5 x 860 [2,8]
+ CRUSH rule 5 x 861 [8,2]
+ CRUSH rule 5 x 862 [8,1]
+ CRUSH rule 5 x 863 [8,2]
+ CRUSH rule 5 x 864 [8,2]
+ CRUSH rule 5 x 865 [8,1]
+ CRUSH rule 5 x 866 [8,2]
+ CRUSH rule 5 x 867 [8,2]
+ CRUSH rule 5 x 868 [8,1]
+ CRUSH rule 5 x 869 [8,4]
+ CRUSH rule 5 x 870 [2,8]
+ CRUSH rule 5 x 871 [1,8]
+ CRUSH rule 5 x 872 [1,8]
+ CRUSH rule 5 x 873 [4,8]
+ CRUSH rule 5 x 874 [2,6]
+ CRUSH rule 5 x 875 [2,8]
+ CRUSH rule 5 x 876 [4,8]
+ CRUSH rule 5 x 877 [8,4]
+ CRUSH rule 5 x 878 [2,8]
+ CRUSH rule 5 x 879 [8,1]
+ CRUSH rule 5 x 880 [1,8]
+ CRUSH rule 5 x 881 [4,8]
+ CRUSH rule 5 x 882 [1,8]
+ CRUSH rule 5 x 883 [2,4]
+ CRUSH rule 5 x 884 [8,2]
+ CRUSH rule 5 x 885 [4,1]
+ CRUSH rule 5 x 886 [8,2]
+ CRUSH rule 5 x 887 [8,4]
+ CRUSH rule 5 x 888 [8,2]
+ CRUSH rule 5 x 889 [2,6]
+ CRUSH rule 5 x 890 [8,2]
+ CRUSH rule 5 x 891 [1,8]
+ CRUSH rule 5 x 892 [8,2]
+ CRUSH rule 5 x 893 [2,6]
+ CRUSH rule 5 x 894 [8,4]
+ CRUSH rule 5 x 895 [4,1]
+ CRUSH rule 5 x 896 [1,8]
+ CRUSH rule 5 x 897 [2,8]
+ CRUSH rule 5 x 898 [1,4]
+ CRUSH rule 5 x 899 [1,8]
+ CRUSH rule 5 x 900 [4,1]
+ CRUSH rule 5 x 901 [2,8]
+ CRUSH rule 5 x 902 [8,4]
+ CRUSH rule 5 x 903 [8,2]
+ CRUSH rule 5 x 904 [8,2]
+ CRUSH rule 5 x 905 [8,2]
+ CRUSH rule 5 x 906 [1,8]
+ CRUSH rule 5 x 907 [8,1]
+ CRUSH rule 5 x 908 [8,1]
+ CRUSH rule 5 x 909 [2,8]
+ CRUSH rule 5 x 910 [8,2]
+ CRUSH rule 5 x 911 [8,1]
+ CRUSH rule 5 x 912 [1,8]
+ CRUSH rule 5 x 913 [8,2]
+ CRUSH rule 5 x 914 [6,4]
+ CRUSH rule 5 x 915 [8,2]
+ CRUSH rule 5 x 916 [4,1]
+ CRUSH rule 5 x 917 [1,4]
+ CRUSH rule 5 x 918 [8,2]
+ CRUSH rule 5 x 919 [8,2]
+ CRUSH rule 5 x 920 [8,1]
+ CRUSH rule 5 x 921 [1,8]
+ CRUSH rule 5 x 922 [8,4]
+ CRUSH rule 5 x 923 [4,8]
+ CRUSH rule 5 x 924 [8,1]
+ CRUSH rule 5 x 925 [4,8]
+ CRUSH rule 5 x 926 [2,8]
+ CRUSH rule 5 x 927 [1,8]
+ CRUSH rule 5 x 928 [8,1]
+ CRUSH rule 5 x 929 [4,1]
+ CRUSH rule 5 x 930 [2,8]
+ CRUSH rule 5 x 931 [2,8]
+ CRUSH rule 5 x 932 [4,1]
+ CRUSH rule 5 x 933 [8,4]
+ CRUSH rule 5 x 934 [8,2]
+ CRUSH rule 5 x 935 [8,2]
+ CRUSH rule 5 x 936 [1,8]
+ CRUSH rule 5 x 937 [4,8]
+ CRUSH rule 5 x 938 [8,4]
+ CRUSH rule 5 x 939 [2,8]
+ CRUSH rule 5 x 940 [8,1]
+ CRUSH rule 5 x 941 [2,8]
+ CRUSH rule 5 x 942 [1,8]
+ CRUSH rule 5 x 943 [8,2]
+ CRUSH rule 5 x 944 [8,2]
+ CRUSH rule 5 x 945 [8,2]
+ CRUSH rule 5 x 946 [2,8]
+ CRUSH rule 5 x 947 [8,2]
+ CRUSH rule 5 x 948 [8,1]
+ CRUSH rule 5 x 949 [6,1]
+ CRUSH rule 5 x 950 [8,1]
+ CRUSH rule 5 x 951 [8,1]
+ CRUSH rule 5 x 952 [2,8]
+ CRUSH rule 5 x 953 [1,4]
+ CRUSH rule 5 x 954 [2,8]
+ CRUSH rule 5 x 955 [8,1]
+ CRUSH rule 5 x 956 [1,8]
+ CRUSH rule 5 x 957 [8,1]
+ CRUSH rule 5 x 958 [8,4]
+ CRUSH rule 5 x 959 [4,2]
+ CRUSH rule 5 x 960 [6,1]
+ CRUSH rule 5 x 961 [1,8]
+ CRUSH rule 5 x 962 [8,4]
+ CRUSH rule 5 x 963 [2,4]
+ CRUSH rule 5 x 964 [2,8]
+ CRUSH rule 5 x 965 [8,2]
+ CRUSH rule 5 x 966 [4,8]
+ CRUSH rule 5 x 967 [8,4]
+ CRUSH rule 5 x 968 [8,2]
+ CRUSH rule 5 x 969 [8,2]
+ CRUSH rule 5 x 970 [2,8]
+ CRUSH rule 5 x 971 [1,8]
+ CRUSH rule 5 x 972 [1,8]
+ CRUSH rule 5 x 973 [1,8]
+ CRUSH rule 5 x 974 [4,1]
+ CRUSH rule 5 x 975 [4,8]
+ CRUSH rule 5 x 976 [4,8]
+ CRUSH rule 5 x 977 [8,4]
+ CRUSH rule 5 x 978 [8,2]
+ CRUSH rule 5 x 979 [8,1]
+ CRUSH rule 5 x 980 [8,2]
+ CRUSH rule 5 x 981 [8,2]
+ CRUSH rule 5 x 982 [1,8]
+ CRUSH rule 5 x 983 [4,8]
+ CRUSH rule 5 x 984 [2,8]
+ CRUSH rule 5 x 985 [2,4]
+ CRUSH rule 5 x 986 [8,4]
+ CRUSH rule 5 x 987 [2,8]
+ CRUSH rule 5 x 988 [1,4]
+ CRUSH rule 5 x 989 [1,8]
+ CRUSH rule 5 x 990 [1,8]
+ CRUSH rule 5 x 991 [1,4]
+ CRUSH rule 5 x 992 [8,1]
+ CRUSH rule 5 x 993 [2,8]
+ CRUSH rule 5 x 994 [4,8]
+ CRUSH rule 5 x 995 [8,1]
+ CRUSH rule 5 x 996 [8,4]
+ CRUSH rule 5 x 997 [8,4]
+ CRUSH rule 5 x 998 [8,1]
+ CRUSH rule 5 x 999 [1,8]
+ CRUSH rule 5 x 1000 [8,4]
+ CRUSH rule 5 x 1001 [2,8]
+ CRUSH rule 5 x 1002 [1,8]
+ CRUSH rule 5 x 1003 [2,8]
+ CRUSH rule 5 x 1004 [8,1]
+ CRUSH rule 5 x 1005 [8,1]
+ CRUSH rule 5 x 1006 [1,8]
+ CRUSH rule 5 x 1007 [1,4]
+ CRUSH rule 5 x 1008 [1,8]
+ CRUSH rule 5 x 1009 [6,4]
+ CRUSH rule 5 x 1010 [1,8]
+ CRUSH rule 5 x 1011 [4,2]
+ CRUSH rule 5 x 1012 [1,8]
+ CRUSH rule 5 x 1013 [2,8]
+ CRUSH rule 5 x 1014 [2,8]
+ CRUSH rule 5 x 1015 [8,1]
+ CRUSH rule 5 x 1016 [2,4]
+ CRUSH rule 5 x 1017 [6,2]
+ CRUSH rule 5 x 1018 [4,1]
+ CRUSH rule 5 x 1019 [4,8]
+ CRUSH rule 5 x 1020 [1,8]
+ CRUSH rule 5 x 1021 [2,8]
+ CRUSH rule 5 x 1022 [1,8]
+ CRUSH rule 5 x 1023 [4,2]
+ rule 5 (chooseleaf-set) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 5 x 0 [2,4,8]
+ CRUSH rule 5 x 1 [2,8,4]
+ CRUSH rule 5 x 2 [1,8]
+ CRUSH rule 5 x 3 [8,1]
+ CRUSH rule 5 x 4 [4,2,6]
+ CRUSH rule 5 x 5 [8,2]
+ CRUSH rule 5 x 6 [2,8,4]
+ CRUSH rule 5 x 7 [4,8,2]
+ CRUSH rule 5 x 8 [4,8,1]
+ CRUSH rule 5 x 9 [2,4,8]
+ CRUSH rule 5 x 10 [2,8]
+ CRUSH rule 5 x 11 [2,8]
+ CRUSH rule 5 x 12 [2,8]
+ CRUSH rule 5 x 13 [4,8,1]
+ CRUSH rule 5 x 14 [8,2]
+ CRUSH rule 5 x 15 [8,2]
+ CRUSH rule 5 x 16 [8,2]
+ CRUSH rule 5 x 17 [4,1,8]
+ CRUSH rule 5 x 18 [1,8]
+ CRUSH rule 5 x 19 [8,4,2]
+ CRUSH rule 5 x 20 [2,8]
+ CRUSH rule 5 x 21 [8,2]
+ CRUSH rule 5 x 22 [8,1]
+ CRUSH rule 5 x 23 [4,8,2]
+ CRUSH rule 5 x 24 [1,8,4]
+ CRUSH rule 5 x 25 [4,8,1]
+ CRUSH rule 5 x 26 [2,8,4]
+ CRUSH rule 5 x 27 [4,1,8]
+ CRUSH rule 5 x 28 [8,2]
+ CRUSH rule 5 x 29 [8,4,2]
+ CRUSH rule 5 x 30 [4,8,2]
+ CRUSH rule 5 x 31 [8,1]
+ CRUSH rule 5 x 32 [6,1]
+ CRUSH rule 5 x 33 [2,8]
+ CRUSH rule 5 x 34 [2,8]
+ CRUSH rule 5 x 35 [1,8,4]
+ CRUSH rule 5 x 36 [8,2]
+ CRUSH rule 5 x 37 [1,8]
+ CRUSH rule 5 x 38 [4,8,2]
+ CRUSH rule 5 x 39 [8,2]
+ CRUSH rule 5 x 40 [8,2,4]
+ CRUSH rule 5 x 41 [2,8,4]
+ CRUSH rule 5 x 42 [8,2]
+ CRUSH rule 5 x 43 [1,8]
+ CRUSH rule 5 x 44 [1,8,4]
+ CRUSH rule 5 x 45 [8,2,4]
+ CRUSH rule 5 x 46 [2,8]
+ CRUSH rule 5 x 47 [4,2,8]
+ CRUSH rule 5 x 48 [8,1]
+ CRUSH rule 5 x 49 [8,2]
+ CRUSH rule 5 x 50 [4,1,8]
+ CRUSH rule 5 x 51 [8,2]
+ CRUSH rule 5 x 52 [8,1,4]
+ CRUSH rule 5 x 53 [4,8,2]
+ CRUSH rule 5 x 54 [8,4,1]
+ CRUSH rule 5 x 55 [8,2]
+ CRUSH rule 5 x 56 [8,4,2]
+ CRUSH rule 5 x 57 [8,1]
+ CRUSH rule 5 x 58 [1,8]
+ CRUSH rule 5 x 59 [2,8]
+ CRUSH rule 5 x 60 [4,2,8]
+ CRUSH rule 5 x 61 [4,8,2]
+ CRUSH rule 5 x 62 [8,1]
+ CRUSH rule 5 x 63 [8,2]
+ CRUSH rule 5 x 64 [4,2,8]
+ CRUSH rule 5 x 65 [8,4,2]
+ CRUSH rule 5 x 66 [4,8,2]
+ CRUSH rule 5 x 67 [4,2,8]
+ CRUSH rule 5 x 68 [1,8]
+ CRUSH rule 5 x 69 [2,8]
+ CRUSH rule 5 x 70 [8,2]
+ CRUSH rule 5 x 71 [2,8,4]
+ CRUSH rule 5 x 72 [8,1,4]
+ CRUSH rule 5 x 73 [2,8]
+ CRUSH rule 5 x 74 [1,8]
+ CRUSH rule 5 x 75 [4,2,8]
+ CRUSH rule 5 x 76 [4,1,6]
+ CRUSH rule 5 x 77 [8,2,4]
+ CRUSH rule 5 x 78 [1,6]
+ CRUSH rule 5 x 79 [4,1,8]
+ CRUSH rule 5 x 80 [2,4,8]
+ CRUSH rule 5 x 81 [2,8]
+ CRUSH rule 5 x 82 [6,1]
+ CRUSH rule 5 x 83 [2,8]
+ CRUSH rule 5 x 84 [8,2]
+ CRUSH rule 5 x 85 [4,8,1]
+ CRUSH rule 5 x 86 [2,8]
+ CRUSH rule 5 x 87 [2,8,4]
+ CRUSH rule 5 x 88 [1,6]
+ CRUSH rule 5 x 89 [1,8]
+ CRUSH rule 5 x 90 [8,4,1]
+ CRUSH rule 5 x 91 [4,8,1]
+ CRUSH rule 5 x 92 [1,8]
+ CRUSH rule 5 x 93 [8,4,2]
+ CRUSH rule 5 x 94 [1,8]
+ CRUSH rule 5 x 95 [8,1]
+ CRUSH rule 5 x 96 [8,2]
+ CRUSH rule 5 x 97 [8,1]
+ CRUSH rule 5 x 98 [2,8]
+ CRUSH rule 5 x 99 [2,8]
+ CRUSH rule 5 x 100 [1,8,4]
+ CRUSH rule 5 x 101 [8,1]
+ CRUSH rule 5 x 102 [2,8]
+ CRUSH rule 5 x 103 [8,2]
+ CRUSH rule 5 x 104 [8,4,1]
+ CRUSH rule 5 x 105 [2,4,8]
+ CRUSH rule 5 x 106 [1,8,4]
+ CRUSH rule 5 x 107 [1,8]
+ CRUSH rule 5 x 108 [8,2]
+ CRUSH rule 5 x 109 [1,4,8]
+ CRUSH rule 5 x 110 [4,2,8]
+ CRUSH rule 5 x 111 [2,4,8]
+ CRUSH rule 5 x 112 [2,8]
+ CRUSH rule 5 x 113 [8,2]
+ CRUSH rule 5 x 114 [8,4,1]
+ CRUSH rule 5 x 115 [8,2,4]
+ CRUSH rule 5 x 116 [1,8]
+ CRUSH rule 5 x 117 [6,1]
+ CRUSH rule 5 x 118 [2,8]
+ CRUSH rule 5 x 119 [8,1]
+ CRUSH rule 5 x 120 [2,4,8]
+ CRUSH rule 5 x 121 [2,8,4]
+ CRUSH rule 5 x 122 [8,1]
+ CRUSH rule 5 x 123 [2,8]
+ CRUSH rule 5 x 124 [2,8]
+ CRUSH rule 5 x 125 [1,8,4]
+ CRUSH rule 5 x 126 [1,8]
+ CRUSH rule 5 x 127 [4,8,2]
+ CRUSH rule 5 x 128 [8,2]
+ CRUSH rule 5 x 129 [2,4,8]
+ CRUSH rule 5 x 130 [4,8,2]
+ CRUSH rule 5 x 131 [1,4,8]
+ CRUSH rule 5 x 132 [1,8]
+ CRUSH rule 5 x 133 [8,1]
+ CRUSH rule 5 x 134 [1,8,4]
+ CRUSH rule 5 x 135 [4,8,2]
+ CRUSH rule 5 x 136 [2,4,8]
+ CRUSH rule 5 x 137 [8,4,2]
+ CRUSH rule 5 x 138 [8,4,2]
+ CRUSH rule 5 x 139 [4,2,8]
+ CRUSH rule 5 x 140 [1,8,4]
+ CRUSH rule 5 x 141 [8,2]
+ CRUSH rule 5 x 142 [4,1,8]
+ CRUSH rule 5 x 143 [4,8,1]
+ CRUSH rule 5 x 144 [8,1]
+ CRUSH rule 5 x 145 [8,1]
+ CRUSH rule 5 x 146 [2,8]
+ CRUSH rule 5 x 147 [2,8,4]
+ CRUSH rule 5 x 148 [4,1,8]
+ CRUSH rule 5 x 149 [4,8,1]
+ CRUSH rule 5 x 150 [1,8]
+ CRUSH rule 5 x 151 [1,8]
+ CRUSH rule 5 x 152 [8,2]
+ CRUSH rule 5 x 153 [8,4,2]
+ CRUSH rule 5 x 154 [4,2,8]
+ CRUSH rule 5 x 155 [4,8,2]
+ CRUSH rule 5 x 156 [4,2,8]
+ CRUSH rule 5 x 157 [1,8]
+ CRUSH rule 5 x 158 [2,8,4]
+ CRUSH rule 5 x 159 [8,2,4]
+ CRUSH rule 5 x 160 [2,8,4]
+ CRUSH rule 5 x 161 [1,4,8]
+ CRUSH rule 5 x 162 [1,8]
+ CRUSH rule 5 x 163 [4,8,1]
+ CRUSH rule 5 x 164 [8,1]
+ CRUSH rule 5 x 165 [8,2,4]
+ CRUSH rule 5 x 166 [2,8]
+ CRUSH rule 5 x 167 [1,8,4]
+ CRUSH rule 5 x 168 [4,2,8]
+ CRUSH rule 5 x 169 [2,8,4]
+ CRUSH rule 5 x 170 [1,8]
+ CRUSH rule 5 x 171 [8,4,2]
+ CRUSH rule 5 x 172 [1,8]
+ CRUSH rule 5 x 173 [8,4,1]
+ CRUSH rule 5 x 174 [1,6]
+ CRUSH rule 5 x 175 [8,1]
+ CRUSH rule 5 x 176 [2,8]
+ CRUSH rule 5 x 177 [1,8]
+ CRUSH rule 5 x 178 [4,2,8]
+ CRUSH rule 5 x 179 [1,8]
+ CRUSH rule 5 x 180 [8,1]
+ CRUSH rule 5 x 181 [8,2,4]
+ CRUSH rule 5 x 182 [8,1]
+ CRUSH rule 5 x 183 [8,4,1]
+ CRUSH rule 5 x 184 [4,8,2]
+ CRUSH rule 5 x 185 [8,1,4]
+ CRUSH rule 5 x 186 [2,4,8]
+ CRUSH rule 5 x 187 [1,8]
+ CRUSH rule 5 x 188 [1,8,4]
+ CRUSH rule 5 x 189 [1,8,4]
+ CRUSH rule 5 x 190 [1,8]
+ CRUSH rule 5 x 191 [8,1,4]
+ CRUSH rule 5 x 192 [4,1,8]
+ CRUSH rule 5 x 193 [4,2,8]
+ CRUSH rule 5 x 194 [1,8]
+ CRUSH rule 5 x 195 [8,4,1]
+ CRUSH rule 5 x 196 [8,2]
+ CRUSH rule 5 x 197 [8,4,2]
+ CRUSH rule 5 x 198 [2,8]
+ CRUSH rule 5 x 199 [1,4,8]
+ CRUSH rule 5 x 200 [1,8]
+ CRUSH rule 5 x 201 [8,1,4]
+ CRUSH rule 5 x 202 [8,1]
+ CRUSH rule 5 x 203 [8,1]
+ CRUSH rule 5 x 204 [2,4,8]
+ CRUSH rule 5 x 205 [1,8]
+ CRUSH rule 5 x 206 [1,8,4]
+ CRUSH rule 5 x 207 [2,8]
+ CRUSH rule 5 x 208 [8,1]
+ CRUSH rule 5 x 209 [1,8]
+ CRUSH rule 5 x 210 [1,4,8]
+ CRUSH rule 5 x 211 [4,2,8]
+ CRUSH rule 5 x 212 [8,1]
+ CRUSH rule 5 x 213 [8,4,2]
+ CRUSH rule 5 x 214 [8,2]
+ CRUSH rule 5 x 215 [8,1]
+ CRUSH rule 5 x 216 [2,8]
+ CRUSH rule 5 x 217 [1,8,4]
+ CRUSH rule 5 x 218 [2,8]
+ CRUSH rule 5 x 219 [8,2]
+ CRUSH rule 5 x 220 [4,8,1]
+ CRUSH rule 5 x 221 [8,1]
+ CRUSH rule 5 x 222 [8,1]
+ CRUSH rule 5 x 223 [1,8]
+ CRUSH rule 5 x 224 [1,4,8]
+ CRUSH rule 5 x 225 [8,2]
+ CRUSH rule 5 x 226 [8,2,4]
+ CRUSH rule 5 x 227 [4,1,8]
+ CRUSH rule 5 x 228 [8,2]
+ CRUSH rule 5 x 229 [4,8,2]
+ CRUSH rule 5 x 230 [4,8,2]
+ CRUSH rule 5 x 231 [4,8,2]
+ CRUSH rule 5 x 232 [2,8,4]
+ CRUSH rule 5 x 233 [2,8]
+ CRUSH rule 5 x 234 [1,8]
+ CRUSH rule 5 x 235 [4,8,1]
+ CRUSH rule 5 x 236 [2,6]
+ CRUSH rule 5 x 237 [4,8,1]
+ CRUSH rule 5 x 238 [2,8]
+ CRUSH rule 5 x 239 [8,1]
+ CRUSH rule 5 x 240 [4,8,2]
+ CRUSH rule 5 x 241 [1,8]
+ CRUSH rule 5 x 242 [8,2]
+ CRUSH rule 5 x 243 [8,2]
+ CRUSH rule 5 x 244 [4,8,2]
+ CRUSH rule 5 x 245 [8,1]
+ CRUSH rule 5 x 246 [1,8]
+ CRUSH rule 5 x 247 [8,2]
+ CRUSH rule 5 x 248 [8,2,4]
+ CRUSH rule 5 x 249 [2,8]
+ CRUSH rule 5 x 250 [2,4,6]
+ CRUSH rule 5 x 251 [2,8]
+ CRUSH rule 5 x 252 [4,8,1]
+ CRUSH rule 5 x 253 [2,8]
+ CRUSH rule 5 x 254 [4,2,8]
+ CRUSH rule 5 x 255 [1,8]
+ CRUSH rule 5 x 256 [4,8,1]
+ CRUSH rule 5 x 257 [2,8,4]
+ CRUSH rule 5 x 258 [4,2,8]
+ CRUSH rule 5 x 259 [6,2]
+ CRUSH rule 5 x 260 [8,2]
+ CRUSH rule 5 x 261 [8,1]
+ CRUSH rule 5 x 262 [8,1]
+ CRUSH rule 5 x 263 [8,1,4]
+ CRUSH rule 5 x 264 [8,2]
+ CRUSH rule 5 x 265 [8,2]
+ CRUSH rule 5 x 266 [8,2,4]
+ CRUSH rule 5 x 267 [2,8]
+ CRUSH rule 5 x 268 [1,8]
+ CRUSH rule 5 x 269 [1,8,4]
+ CRUSH rule 5 x 270 [4,1,8]
+ CRUSH rule 5 x 271 [8,4,1]
+ CRUSH rule 5 x 272 [2,8,4]
+ CRUSH rule 5 x 273 [4,1,8]
+ CRUSH rule 5 x 274 [8,4,1]
+ CRUSH rule 5 x 275 [4,8,1]
+ CRUSH rule 5 x 276 [8,1,4]
+ CRUSH rule 5 x 277 [8,1]
+ CRUSH rule 5 x 278 [8,1,4]
+ CRUSH rule 5 x 279 [8,4,2]
+ CRUSH rule 5 x 280 [2,8,4]
+ CRUSH rule 5 x 281 [8,2]
+ CRUSH rule 5 x 282 [2,8]
+ CRUSH rule 5 x 283 [8,2]
+ CRUSH rule 5 x 284 [8,2]
+ CRUSH rule 5 x 285 [4,8,2]
+ CRUSH rule 5 x 286 [2,8,4]
+ CRUSH rule 5 x 287 [1,8]
+ CRUSH rule 5 x 288 [8,1,4]
+ CRUSH rule 5 x 289 [4,8,2]
+ CRUSH rule 5 x 290 [1,4,8]
+ CRUSH rule 5 x 291 [1,4,8]
+ CRUSH rule 5 x 292 [8,2,4]
+ CRUSH rule 5 x 293 [8,1]
+ CRUSH rule 5 x 294 [8,4,1]
+ CRUSH rule 5 x 295 [4,8,2]
+ CRUSH rule 5 x 296 [4,1,8]
+ CRUSH rule 5 x 297 [8,2,4]
+ CRUSH rule 5 x 298 [1,8]
+ CRUSH rule 5 x 299 [2,8]
+ CRUSH rule 5 x 300 [8,2]
+ CRUSH rule 5 x 301 [1,8]
+ CRUSH rule 5 x 302 [1,8]
+ CRUSH rule 5 x 303 [8,4,1]
+ CRUSH rule 5 x 304 [2,8]
+ CRUSH rule 5 x 305 [8,2]
+ CRUSH rule 5 x 306 [1,8]
+ CRUSH rule 5 x 307 [2,8]
+ CRUSH rule 5 x 308 [2,8,4]
+ CRUSH rule 5 x 309 [8,1]
+ CRUSH rule 5 x 310 [4,1,6]
+ CRUSH rule 5 x 311 [4,8,1]
+ CRUSH rule 5 x 312 [2,8]
+ CRUSH rule 5 x 313 [4,1,8]
+ CRUSH rule 5 x 314 [2,8]
+ CRUSH rule 5 x 315 [2,8]
+ CRUSH rule 5 x 316 [8,1]
+ CRUSH rule 5 x 317 [2,8]
+ CRUSH rule 5 x 318 [8,1]
+ CRUSH rule 5 x 319 [2,8]
+ CRUSH rule 5 x 320 [8,1]
+ CRUSH rule 5 x 321 [1,8]
+ CRUSH rule 5 x 322 [2,8,4]
+ CRUSH rule 5 x 323 [4,8,1]
+ CRUSH rule 5 x 324 [8,1,4]
+ CRUSH rule 5 x 325 [4,8,2]
+ CRUSH rule 5 x 326 [1,6]
+ CRUSH rule 5 x 327 [1,8]
+ CRUSH rule 5 x 328 [8,4,1]
+ CRUSH rule 5 x 329 [4,8,2]
+ CRUSH rule 5 x 330 [4,8,2]
+ CRUSH rule 5 x 331 [2,8]
+ CRUSH rule 5 x 332 [2,8]
+ CRUSH rule 5 x 333 [8,1]
+ CRUSH rule 5 x 334 [8,2]
+ CRUSH rule 5 x 335 [8,1]
+ CRUSH rule 5 x 336 [4,8,2]
+ CRUSH rule 5 x 337 [8,2,4]
+ CRUSH rule 5 x 338 [8,1]
+ CRUSH rule 5 x 339 [8,2]
+ CRUSH rule 5 x 340 [2,8,4]
+ CRUSH rule 5 x 341 [4,1,8]
+ CRUSH rule 5 x 342 [2,8,4]
+ CRUSH rule 5 x 343 [8,1]
+ CRUSH rule 5 x 344 [6,2,4]
+ CRUSH rule 5 x 345 [2,8]
+ CRUSH rule 5 x 346 [8,2,4]
+ CRUSH rule 5 x 347 [4,1,8]
+ CRUSH rule 5 x 348 [8,2,4]
+ CRUSH rule 5 x 349 [1,8]
+ CRUSH rule 5 x 350 [8,1]
+ CRUSH rule 5 x 351 [8,2]
+ CRUSH rule 5 x 352 [1,8,4]
+ CRUSH rule 5 x 353 [8,1]
+ CRUSH rule 5 x 354 [1,8]
+ CRUSH rule 5 x 355 [8,2]
+ CRUSH rule 5 x 356 [4,1,8]
+ CRUSH rule 5 x 357 [8,1,4]
+ CRUSH rule 5 x 358 [2,8,4]
+ CRUSH rule 5 x 359 [6,1,4]
+ CRUSH rule 5 x 360 [2,8]
+ CRUSH rule 5 x 361 [8,4,1]
+ CRUSH rule 5 x 362 [4,1,6]
+ CRUSH rule 5 x 363 [4,1,8]
+ CRUSH rule 5 x 364 [2,8]
+ CRUSH rule 5 x 365 [8,1]
+ CRUSH rule 5 x 366 [8,2]
+ CRUSH rule 5 x 367 [4,2,8]
+ CRUSH rule 5 x 368 [8,4,1]
+ CRUSH rule 5 x 369 [8,1]
+ CRUSH rule 5 x 370 [8,2]
+ CRUSH rule 5 x 371 [1,4,8]
+ CRUSH rule 5 x 372 [1,8]
+ CRUSH rule 5 x 373 [1,8]
+ CRUSH rule 5 x 374 [8,1]
+ CRUSH rule 5 x 375 [8,4,1]
+ CRUSH rule 5 x 376 [8,1,4]
+ CRUSH rule 5 x 377 [1,4,8]
+ CRUSH rule 5 x 378 [1,8]
+ CRUSH rule 5 x 379 [8,2]
+ CRUSH rule 5 x 380 [2,8]
+ CRUSH rule 5 x 381 [1,4,8]
+ CRUSH rule 5 x 382 [1,4,8]
+ CRUSH rule 5 x 383 [4,8,2]
+ CRUSH rule 5 x 384 [8,2,4]
+ CRUSH rule 5 x 385 [8,1]
+ CRUSH rule 5 x 386 [1,8]
+ CRUSH rule 5 x 387 [1,4,8]
+ CRUSH rule 5 x 388 [2,6]
+ CRUSH rule 5 x 389 [1,4,8]
+ CRUSH rule 5 x 390 [4,8,1]
+ CRUSH rule 5 x 391 [4,8,2]
+ CRUSH rule 5 x 392 [1,8,4]
+ CRUSH rule 5 x 393 [2,8]
+ CRUSH rule 5 x 394 [8,2]
+ CRUSH rule 5 x 395 [1,8]
+ CRUSH rule 5 x 396 [4,2,8]
+ CRUSH rule 5 x 397 [2,4,8]
+ CRUSH rule 5 x 398 [2,4,8]
+ CRUSH rule 5 x 399 [8,4,2]
+ CRUSH rule 5 x 400 [8,1,4]
+ CRUSH rule 5 x 401 [1,4,8]
+ CRUSH rule 5 x 402 [8,4,2]
+ CRUSH rule 5 x 403 [1,4,8]
+ CRUSH rule 5 x 404 [4,2,8]
+ CRUSH rule 5 x 405 [8,4,2]
+ CRUSH rule 5 x 406 [2,8]
+ CRUSH rule 5 x 407 [2,8,4]
+ CRUSH rule 5 x 408 [4,1,8]
+ CRUSH rule 5 x 409 [8,4,1]
+ CRUSH rule 5 x 410 [8,4,2]
+ CRUSH rule 5 x 411 [2,8,4]
+ CRUSH rule 5 x 412 [2,6]
+ CRUSH rule 5 x 413 [2,8]
+ CRUSH rule 5 x 414 [4,1,6]
+ CRUSH rule 5 x 415 [2,8]
+ CRUSH rule 5 x 416 [2,8]
+ CRUSH rule 5 x 417 [8,2]
+ CRUSH rule 5 x 418 [8,1,4]
+ CRUSH rule 5 x 419 [8,4,1]
+ CRUSH rule 5 x 420 [1,4,8]
+ CRUSH rule 5 x 421 [8,4,1]
+ CRUSH rule 5 x 422 [6,2]
+ CRUSH rule 5 x 423 [2,4,8]
+ CRUSH rule 5 x 424 [8,1]
+ CRUSH rule 5 x 425 [1,8]
+ CRUSH rule 5 x 426 [8,2]
+ CRUSH rule 5 x 427 [1,8]
+ CRUSH rule 5 x 428 [4,8,1]
+ CRUSH rule 5 x 429 [4,8,2]
+ CRUSH rule 5 x 430 [4,8,2]
+ CRUSH rule 5 x 431 [4,1,8]
+ CRUSH rule 5 x 432 [8,1]
+ CRUSH rule 5 x 433 [8,1]
+ CRUSH rule 5 x 434 [2,8]
+ CRUSH rule 5 x 435 [2,8]
+ CRUSH rule 5 x 436 [4,1,8]
+ CRUSH rule 5 x 437 [8,2]
+ CRUSH rule 5 x 438 [2,4,8]
+ CRUSH rule 5 x 439 [1,6]
+ CRUSH rule 5 x 440 [2,8]
+ CRUSH rule 5 x 441 [4,6,2]
+ CRUSH rule 5 x 442 [2,8]
+ CRUSH rule 5 x 443 [8,2,4]
+ CRUSH rule 5 x 444 [8,1]
+ CRUSH rule 5 x 445 [8,2]
+ CRUSH rule 5 x 446 [8,1]
+ CRUSH rule 5 x 447 [2,4,8]
+ CRUSH rule 5 x 448 [8,2,4]
+ CRUSH rule 5 x 449 [8,1]
+ CRUSH rule 5 x 450 [1,8]
+ CRUSH rule 5 x 451 [8,4,2]
+ CRUSH rule 5 x 452 [8,2]
+ CRUSH rule 5 x 453 [6,2]
+ CRUSH rule 5 x 454 [8,2]
+ CRUSH rule 5 x 455 [2,8,4]
+ CRUSH rule 5 x 456 [8,2]
+ CRUSH rule 5 x 457 [8,2]
+ CRUSH rule 5 x 458 [2,8]
+ CRUSH rule 5 x 459 [2,8,4]
+ CRUSH rule 5 x 460 [8,2]
+ CRUSH rule 5 x 461 [8,1]
+ CRUSH rule 5 x 462 [8,1]
+ CRUSH rule 5 x 463 [8,2]
+ CRUSH rule 5 x 464 [8,4,2]
+ CRUSH rule 5 x 465 [6,2,4]
+ CRUSH rule 5 x 466 [8,1]
+ CRUSH rule 5 x 467 [8,2]
+ CRUSH rule 5 x 468 [8,1,4]
+ CRUSH rule 5 x 469 [8,1]
+ CRUSH rule 5 x 470 [4,2,6]
+ CRUSH rule 5 x 471 [1,8]
+ CRUSH rule 5 x 472 [1,8]
+ CRUSH rule 5 x 473 [1,4,8]
+ CRUSH rule 5 x 474 [8,1]
+ CRUSH rule 5 x 475 [8,2,4]
+ CRUSH rule 5 x 476 [4,8,1]
+ CRUSH rule 5 x 477 [4,8,2]
+ CRUSH rule 5 x 478 [8,2,4]
+ CRUSH rule 5 x 479 [2,8]
+ CRUSH rule 5 x 480 [1,8]
+ CRUSH rule 5 x 481 [2,4,6]
+ CRUSH rule 5 x 482 [1,8]
+ CRUSH rule 5 x 483 [2,8,4]
+ CRUSH rule 5 x 484 [1,8]
+ CRUSH rule 5 x 485 [8,1]
+ CRUSH rule 5 x 486 [4,1,8]
+ CRUSH rule 5 x 487 [1,8]
+ CRUSH rule 5 x 488 [8,1]
+ CRUSH rule 5 x 489 [2,8]
+ CRUSH rule 5 x 490 [6,2]
+ CRUSH rule 5 x 491 [1,8]
+ CRUSH rule 5 x 492 [8,1]
+ CRUSH rule 5 x 493 [2,8]
+ CRUSH rule 5 x 494 [1,8]
+ CRUSH rule 5 x 495 [4,1,8]
+ CRUSH rule 5 x 496 [8,4,1]
+ CRUSH rule 5 x 497 [4,8,1]
+ CRUSH rule 5 x 498 [2,4,8]
+ CRUSH rule 5 x 499 [8,4,2]
+ CRUSH rule 5 x 500 [4,8,2]
+ CRUSH rule 5 x 501 [2,8]
+ CRUSH rule 5 x 502 [6,1]
+ CRUSH rule 5 x 503 [2,8]
+ CRUSH rule 5 x 504 [8,1]
+ CRUSH rule 5 x 505 [1,8]
+ CRUSH rule 5 x 506 [4,2,8]
+ CRUSH rule 5 x 507 [8,1,4]
+ CRUSH rule 5 x 508 [1,8]
+ CRUSH rule 5 x 509 [8,1]
+ CRUSH rule 5 x 510 [8,2]
+ CRUSH rule 5 x 511 [4,8,2]
+ CRUSH rule 5 x 512 [8,2]
+ CRUSH rule 5 x 513 [8,2]
+ CRUSH rule 5 x 514 [2,8]
+ CRUSH rule 5 x 515 [8,4,1]
+ CRUSH rule 5 x 516 [4,1,8]
+ CRUSH rule 5 x 517 [8,2]
+ CRUSH rule 5 x 518 [4,8,1]
+ CRUSH rule 5 x 519 [8,4,1]
+ CRUSH rule 5 x 520 [2,8,4]
+ CRUSH rule 5 x 521 [8,2,4]
+ CRUSH rule 5 x 522 [8,1,4]
+ CRUSH rule 5 x 523 [4,2,8]
+ CRUSH rule 5 x 524 [2,6]
+ CRUSH rule 5 x 525 [2,8]
+ CRUSH rule 5 x 526 [1,8]
+ CRUSH rule 5 x 527 [1,4,6]
+ CRUSH rule 5 x 528 [2,8]
+ CRUSH rule 5 x 529 [4,8,2]
+ CRUSH rule 5 x 530 [8,1]
+ CRUSH rule 5 x 531 [8,1,4]
+ CRUSH rule 5 x 532 [6,4,1]
+ CRUSH rule 5 x 533 [4,8,2]
+ CRUSH rule 5 x 534 [8,1]
+ CRUSH rule 5 x 535 [8,1]
+ CRUSH rule 5 x 536 [8,2]
+ CRUSH rule 5 x 537 [4,8,2]
+ CRUSH rule 5 x 538 [8,4,1]
+ CRUSH rule 5 x 539 [8,1]
+ CRUSH rule 5 x 540 [1,8,4]
+ CRUSH rule 5 x 541 [2,4,8]
+ CRUSH rule 5 x 542 [2,8]
+ CRUSH rule 5 x 543 [8,2]
+ CRUSH rule 5 x 544 [4,8,2]
+ CRUSH rule 5 x 545 [8,1]
+ CRUSH rule 5 x 546 [8,1,4]
+ CRUSH rule 5 x 547 [8,2,4]
+ CRUSH rule 5 x 548 [4,2,8]
+ CRUSH rule 5 x 549 [8,2]
+ CRUSH rule 5 x 550 [2,4,8]
+ CRUSH rule 5 x 551 [8,1]
+ CRUSH rule 5 x 552 [4,8,1]
+ CRUSH rule 5 x 553 [2,8]
+ CRUSH rule 5 x 554 [1,8]
+ CRUSH rule 5 x 555 [4,1,8]
+ CRUSH rule 5 x 556 [8,1]
+ CRUSH rule 5 x 557 [8,2]
+ CRUSH rule 5 x 558 [4,1,8]
+ CRUSH rule 5 x 559 [1,8]
+ CRUSH rule 5 x 560 [8,1]
+ CRUSH rule 5 x 561 [8,4,1]
+ CRUSH rule 5 x 562 [4,1,8]
+ CRUSH rule 5 x 563 [2,8]
+ CRUSH rule 5 x 564 [1,8]
+ CRUSH rule 5 x 565 [4,8,2]
+ CRUSH rule 5 x 566 [4,8,2]
+ CRUSH rule 5 x 567 [4,8,1]
+ CRUSH rule 5 x 568 [8,1]
+ CRUSH rule 5 x 569 [4,1,8]
+ CRUSH rule 5 x 570 [1,8]
+ CRUSH rule 5 x 571 [6,1]
+ CRUSH rule 5 x 572 [4,2,8]
+ CRUSH rule 5 x 573 [1,8]
+ CRUSH rule 5 x 574 [2,8]
+ CRUSH rule 5 x 575 [8,2,4]
+ CRUSH rule 5 x 576 [4,8,2]
+ CRUSH rule 5 x 577 [8,2]
+ CRUSH rule 5 x 578 [8,1]
+ CRUSH rule 5 x 579 [4,1,8]
+ CRUSH rule 5 x 580 [1,8]
+ CRUSH rule 5 x 581 [8,2,4]
+ CRUSH rule 5 x 582 [2,8,4]
+ CRUSH rule 5 x 583 [8,1]
+ CRUSH rule 5 x 584 [8,1,4]
+ CRUSH rule 5 x 585 [8,1,4]
+ CRUSH rule 5 x 586 [1,8,4]
+ CRUSH rule 5 x 587 [2,4,8]
+ CRUSH rule 5 x 588 [4,8,1]
+ CRUSH rule 5 x 589 [8,1]
+ CRUSH rule 5 x 590 [8,2]
+ CRUSH rule 5 x 591 [4,2,8]
+ CRUSH rule 5 x 592 [2,4,8]
+ CRUSH rule 5 x 593 [1,8,4]
+ CRUSH rule 5 x 594 [2,8]
+ CRUSH rule 5 x 595 [8,1]
+ CRUSH rule 5 x 596 [8,2]
+ CRUSH rule 5 x 597 [1,8]
+ CRUSH rule 5 x 598 [2,8]
+ CRUSH rule 5 x 599 [4,2,8]
+ CRUSH rule 5 x 600 [8,1,4]
+ CRUSH rule 5 x 601 [1,8,4]
+ CRUSH rule 5 x 602 [8,2]
+ CRUSH rule 5 x 603 [1,8]
+ CRUSH rule 5 x 604 [8,2]
+ CRUSH rule 5 x 605 [2,8]
+ CRUSH rule 5 x 606 [2,6,4]
+ CRUSH rule 5 x 607 [2,4,8]
+ CRUSH rule 5 x 608 [4,2,8]
+ CRUSH rule 5 x 609 [4,2,8]
+ CRUSH rule 5 x 610 [8,1]
+ CRUSH rule 5 x 611 [1,8]
+ CRUSH rule 5 x 612 [2,8]
+ CRUSH rule 5 x 613 [8,2,4]
+ CRUSH rule 5 x 614 [8,2,4]
+ CRUSH rule 5 x 615 [8,2,4]
+ CRUSH rule 5 x 616 [1,8]
+ CRUSH rule 5 x 617 [8,1,4]
+ CRUSH rule 5 x 618 [8,4,2]
+ CRUSH rule 5 x 619 [4,1,8]
+ CRUSH rule 5 x 620 [1,8]
+ CRUSH rule 5 x 621 [8,1]
+ CRUSH rule 5 x 622 [2,4,8]
+ CRUSH rule 5 x 623 [2,8]
+ CRUSH rule 5 x 624 [4,2,8]
+ CRUSH rule 5 x 625 [2,8]
+ CRUSH rule 5 x 626 [8,2,4]
+ CRUSH rule 5 x 627 [2,8,4]
+ CRUSH rule 5 x 628 [8,2]
+ CRUSH rule 5 x 629 [2,8,4]
+ CRUSH rule 5 x 630 [2,8]
+ CRUSH rule 5 x 631 [1,8,4]
+ CRUSH rule 5 x 632 [8,2]
+ CRUSH rule 5 x 633 [8,2]
+ CRUSH rule 5 x 634 [1,8]
+ CRUSH rule 5 x 635 [4,8,2]
+ CRUSH rule 5 x 636 [1,4,8]
+ CRUSH rule 5 x 637 [1,8]
+ CRUSH rule 5 x 638 [8,1,4]
+ CRUSH rule 5 x 639 [2,8]
+ CRUSH rule 5 x 640 [2,8]
+ CRUSH rule 5 x 641 [8,2]
+ CRUSH rule 5 x 642 [2,8]
+ CRUSH rule 5 x 643 [1,8]
+ CRUSH rule 5 x 644 [8,1]
+ CRUSH rule 5 x 645 [8,1]
+ CRUSH rule 5 x 646 [8,1,4]
+ CRUSH rule 5 x 647 [8,1]
+ CRUSH rule 5 x 648 [1,8]
+ CRUSH rule 5 x 649 [4,8,2]
+ CRUSH rule 5 x 650 [8,4,1]
+ CRUSH rule 5 x 651 [4,6,1]
+ CRUSH rule 5 x 652 [4,8,1]
+ CRUSH rule 5 x 653 [8,2]
+ CRUSH rule 5 x 654 [6,2]
+ CRUSH rule 5 x 655 [1,4,8]
+ CRUSH rule 5 x 656 [8,1]
+ CRUSH rule 5 x 657 [6,1]
+ CRUSH rule 5 x 658 [8,2]
+ CRUSH rule 5 x 659 [4,8,2]
+ CRUSH rule 5 x 660 [8,2]
+ CRUSH rule 5 x 661 [1,8]
+ CRUSH rule 5 x 662 [8,2]
+ CRUSH rule 5 x 663 [1,4,8]
+ CRUSH rule 5 x 664 [1,4,8]
+ CRUSH rule 5 x 665 [4,6,1]
+ CRUSH rule 5 x 666 [2,8]
+ CRUSH rule 5 x 667 [1,4,8]
+ CRUSH rule 5 x 668 [4,8,1]
+ CRUSH rule 5 x 669 [6,4,2]
+ CRUSH rule 5 x 670 [4,2,8]
+ CRUSH rule 5 x 671 [2,8]
+ CRUSH rule 5 x 672 [4,2,8]
+ CRUSH rule 5 x 673 [4,2,8]
+ CRUSH rule 5 x 674 [1,8]
+ CRUSH rule 5 x 675 [1,8,4]
+ CRUSH rule 5 x 676 [2,4,8]
+ CRUSH rule 5 x 677 [4,1,8]
+ CRUSH rule 5 x 678 [2,4,8]
+ CRUSH rule 5 x 679 [8,2]
+ CRUSH rule 5 x 680 [2,8]
+ CRUSH rule 5 x 681 [8,1]
+ CRUSH rule 5 x 682 [1,4,8]
+ CRUSH rule 5 x 683 [1,4,8]
+ CRUSH rule 5 x 684 [8,1,4]
+ CRUSH rule 5 x 685 [8,1,4]
+ CRUSH rule 5 x 686 [1,4,8]
+ CRUSH rule 5 x 687 [6,1]
+ CRUSH rule 5 x 688 [4,8,2]
+ CRUSH rule 5 x 689 [8,4,2]
+ CRUSH rule 5 x 690 [8,1,4]
+ CRUSH rule 5 x 691 [1,8]
+ CRUSH rule 5 x 692 [8,2]
+ CRUSH rule 5 x 693 [8,4,1]
+ CRUSH rule 5 x 694 [8,4,1]
+ CRUSH rule 5 x 695 [2,8,4]
+ CRUSH rule 5 x 696 [1,8]
+ CRUSH rule 5 x 697 [8,1,4]
+ CRUSH rule 5 x 698 [8,2,4]
+ CRUSH rule 5 x 699 [1,8,4]
+ CRUSH rule 5 x 700 [1,8]
+ CRUSH rule 5 x 701 [1,8]
+ CRUSH rule 5 x 702 [2,8]
+ CRUSH rule 5 x 703 [8,1]
+ CRUSH rule 5 x 704 [1,4,8]
+ CRUSH rule 5 x 705 [8,1,4]
+ CRUSH rule 5 x 706 [1,4,8]
+ CRUSH rule 5 x 707 [8,4,1]
+ CRUSH rule 5 x 708 [4,8,1]
+ CRUSH rule 5 x 709 [8,2]
+ CRUSH rule 5 x 710 [8,2]
+ CRUSH rule 5 x 711 [2,4,8]
+ CRUSH rule 5 x 712 [2,8]
+ CRUSH rule 5 x 713 [8,4,1]
+ CRUSH rule 5 x 714 [2,8]
+ CRUSH rule 5 x 715 [1,8]
+ CRUSH rule 5 x 716 [4,8,2]
+ CRUSH rule 5 x 717 [8,2,4]
+ CRUSH rule 5 x 718 [8,1]
+ CRUSH rule 5 x 719 [2,6,4]
+ CRUSH rule 5 x 720 [8,1,4]
+ CRUSH rule 5 x 721 [4,6,2]
+ CRUSH rule 5 x 722 [8,2]
+ CRUSH rule 5 x 723 [4,1,8]
+ CRUSH rule 5 x 724 [2,6]
+ CRUSH rule 5 x 725 [1,8]
+ CRUSH rule 5 x 726 [4,8,1]
+ CRUSH rule 5 x 727 [4,8,1]
+ CRUSH rule 5 x 728 [2,8,4]
+ CRUSH rule 5 x 729 [8,2]
+ CRUSH rule 5 x 730 [4,8,2]
+ CRUSH rule 5 x 731 [4,1,8]
+ CRUSH rule 5 x 732 [1,8]
+ CRUSH rule 5 x 733 [4,8,1]
+ CRUSH rule 5 x 734 [8,4,2]
+ CRUSH rule 5 x 735 [4,8,1]
+ CRUSH rule 5 x 736 [4,8,1]
+ CRUSH rule 5 x 737 [1,8,4]
+ CRUSH rule 5 x 738 [4,2,8]
+ CRUSH rule 5 x 739 [2,8]
+ CRUSH rule 5 x 740 [1,8,4]
+ CRUSH rule 5 x 741 [8,1]
+ CRUSH rule 5 x 742 [8,2]
+ CRUSH rule 5 x 743 [8,1,4]
+ CRUSH rule 5 x 744 [4,8,1]
+ CRUSH rule 5 x 745 [1,8]
+ CRUSH rule 5 x 746 [1,8]
+ CRUSH rule 5 x 747 [8,1]
+ CRUSH rule 5 x 748 [2,8,4]
+ CRUSH rule 5 x 749 [4,8,2]
+ CRUSH rule 5 x 750 [1,8,4]
+ CRUSH rule 5 x 751 [2,8]
+ CRUSH rule 5 x 752 [8,1]
+ CRUSH rule 5 x 753 [8,4,1]
+ CRUSH rule 5 x 754 [8,4,2]
+ CRUSH rule 5 x 755 [1,8,4]
+ CRUSH rule 5 x 756 [8,1]
+ CRUSH rule 5 x 757 [8,1,4]
+ CRUSH rule 5 x 758 [8,2]
+ CRUSH rule 5 x 759 [8,4,2]
+ CRUSH rule 5 x 760 [1,4,8]
+ CRUSH rule 5 x 761 [2,8]
+ CRUSH rule 5 x 762 [2,8]
+ CRUSH rule 5 x 763 [8,4,1]
+ CRUSH rule 5 x 764 [1,8]
+ CRUSH rule 5 x 765 [8,2]
+ CRUSH rule 5 x 766 [8,1]
+ CRUSH rule 5 x 767 [1,8,4]
+ CRUSH rule 5 x 768 [8,4,2]
+ CRUSH rule 5 x 769 [8,2,4]
+ CRUSH rule 5 x 770 [8,2,4]
+ CRUSH rule 5 x 771 [8,1,4]
+ CRUSH rule 5 x 772 [8,4,1]
+ CRUSH rule 5 x 773 [4,1,8]
+ CRUSH rule 5 x 774 [8,1]
+ CRUSH rule 5 x 775 [8,4,2]
+ CRUSH rule 5 x 776 [6,2]
+ CRUSH rule 5 x 777 [4,1,8]
+ CRUSH rule 5 x 778 [1,8,4]
+ CRUSH rule 5 x 779 [2,8]
+ CRUSH rule 5 x 780 [2,4,8]
+ CRUSH rule 5 x 781 [8,2]
+ CRUSH rule 5 x 782 [4,1,8]
+ CRUSH rule 5 x 783 [8,1,4]
+ CRUSH rule 5 x 784 [1,4,8]
+ CRUSH rule 5 x 785 [8,1,4]
+ CRUSH rule 5 x 786 [8,1]
+ CRUSH rule 5 x 787 [1,6,4]
+ CRUSH rule 5 x 788 [8,2,4]
+ CRUSH rule 5 x 789 [1,8]
+ CRUSH rule 5 x 790 [8,1]
+ CRUSH rule 5 x 791 [4,8,2]
+ CRUSH rule 5 x 792 [4,8,2]
+ CRUSH rule 5 x 793 [8,1,4]
+ CRUSH rule 5 x 794 [2,8,4]
+ CRUSH rule 5 x 795 [1,8]
+ CRUSH rule 5 x 796 [8,2]
+ CRUSH rule 5 x 797 [2,4,8]
+ CRUSH rule 5 x 798 [6,1]
+ CRUSH rule 5 x 799 [4,1,8]
+ CRUSH rule 5 x 800 [2,8]
+ CRUSH rule 5 x 801 [4,8,1]
+ CRUSH rule 5 x 802 [1,8,4]
+ CRUSH rule 5 x 803 [2,8]
+ CRUSH rule 5 x 804 [8,2]
+ CRUSH rule 5 x 805 [8,2]
+ CRUSH rule 5 x 806 [1,4,8]
+ CRUSH rule 5 x 807 [4,8,2]
+ CRUSH rule 5 x 808 [8,2]
+ CRUSH rule 5 x 809 [1,8]
+ CRUSH rule 5 x 810 [8,2]
+ CRUSH rule 5 x 811 [8,1]
+ CRUSH rule 5 x 812 [8,4,2]
+ CRUSH rule 5 x 813 [8,4,2]
+ CRUSH rule 5 x 814 [8,2]
+ CRUSH rule 5 x 815 [4,1,8]
+ CRUSH rule 5 x 816 [2,8]
+ CRUSH rule 5 x 817 [8,1]
+ CRUSH rule 5 x 818 [1,8]
+ CRUSH rule 5 x 819 [1,8]
+ CRUSH rule 5 x 820 [4,8,2]
+ CRUSH rule 5 x 821 [4,8,2]
+ CRUSH rule 5 x 822 [2,4,8]
+ CRUSH rule 5 x 823 [4,8,2]
+ CRUSH rule 5 x 824 [8,2]
+ CRUSH rule 5 x 825 [2,8,4]
+ CRUSH rule 5 x 826 [8,2,4]
+ CRUSH rule 5 x 827 [2,8,4]
+ CRUSH rule 5 x 828 [2,8]
+ CRUSH rule 5 x 829 [8,1]
+ CRUSH rule 5 x 830 [2,4,8]
+ CRUSH rule 5 x 831 [1,8]
+ CRUSH rule 5 x 832 [4,8,2]
+ CRUSH rule 5 x 833 [2,8]
+ CRUSH rule 5 x 834 [1,8]
+ CRUSH rule 5 x 835 [8,4,1]
+ CRUSH rule 5 x 836 [4,8,1]
+ CRUSH rule 5 x 837 [8,4,1]
+ CRUSH rule 5 x 838 [6,2,4]
+ CRUSH rule 5 x 839 [2,8]
+ CRUSH rule 5 x 840 [8,1]
+ CRUSH rule 5 x 841 [4,8,2]
+ CRUSH rule 5 x 842 [2,8]
+ CRUSH rule 5 x 843 [8,4,1]
+ CRUSH rule 5 x 844 [8,2]
+ CRUSH rule 5 x 845 [4,8,2]
+ CRUSH rule 5 x 846 [4,2,8]
+ CRUSH rule 5 x 847 [2,8]
+ CRUSH rule 5 x 848 [2,8,4]
+ CRUSH rule 5 x 849 [4,8,2]
+ CRUSH rule 5 x 850 [1,6]
+ CRUSH rule 5 x 851 [6,1]
+ CRUSH rule 5 x 852 [8,4,2]
+ CRUSH rule 5 x 853 [6,1]
+ CRUSH rule 5 x 854 [8,1]
+ CRUSH rule 5 x 855 [8,1]
+ CRUSH rule 5 x 856 [8,4,2]
+ CRUSH rule 5 x 857 [8,2]
+ CRUSH rule 5 x 858 [6,1]
+ CRUSH rule 5 x 859 [8,2,4]
+ CRUSH rule 5 x 860 [2,8]
+ CRUSH rule 5 x 861 [8,2]
+ CRUSH rule 5 x 862 [8,1]
+ CRUSH rule 5 x 863 [8,2]
+ CRUSH rule 5 x 864 [8,2]
+ CRUSH rule 5 x 865 [8,1]
+ CRUSH rule 5 x 866 [8,2]
+ CRUSH rule 5 x 867 [8,2]
+ CRUSH rule 5 x 868 [8,1]
+ CRUSH rule 5 x 869 [8,4,2]
+ CRUSH rule 5 x 870 [2,8]
+ CRUSH rule 5 x 871 [1,8]
+ CRUSH rule 5 x 872 [1,8]
+ CRUSH rule 5 x 873 [4,8,2]
+ CRUSH rule 5 x 874 [2,6]
+ CRUSH rule 5 x 875 [2,8,4]
+ CRUSH rule 5 x 876 [4,8,1]
+ CRUSH rule 5 x 877 [8,4,2]
+ CRUSH rule 5 x 878 [2,8]
+ CRUSH rule 5 x 879 [8,1]
+ CRUSH rule 5 x 880 [1,8]
+ CRUSH rule 5 x 881 [4,8,1]
+ CRUSH rule 5 x 882 [1,8]
+ CRUSH rule 5 x 883 [2,4,8]
+ CRUSH rule 5 x 884 [8,2,4]
+ CRUSH rule 5 x 885 [4,1,8]
+ CRUSH rule 5 x 886 [8,2]
+ CRUSH rule 5 x 887 [8,4,1]
+ CRUSH rule 5 x 888 [8,2]
+ CRUSH rule 5 x 889 [2,6]
+ CRUSH rule 5 x 890 [8,2,4]
+ CRUSH rule 5 x 891 [1,8]
+ CRUSH rule 5 x 892 [8,2,4]
+ CRUSH rule 5 x 893 [2,6]
+ CRUSH rule 5 x 894 [8,4,2]
+ CRUSH rule 5 x 895 [4,1,8]
+ CRUSH rule 5 x 896 [1,8]
+ CRUSH rule 5 x 897 [2,8]
+ CRUSH rule 5 x 898 [1,4,8]
+ CRUSH rule 5 x 899 [1,8]
+ CRUSH rule 5 x 900 [4,1,8]
+ CRUSH rule 5 x 901 [2,8]
+ CRUSH rule 5 x 902 [8,4,1]
+ CRUSH rule 5 x 903 [8,2]
+ CRUSH rule 5 x 904 [8,2]
+ CRUSH rule 5 x 905 [8,2]
+ CRUSH rule 5 x 906 [1,8]
+ CRUSH rule 5 x 907 [8,1]
+ CRUSH rule 5 x 908 [8,1]
+ CRUSH rule 5 x 909 [2,8]
+ CRUSH rule 5 x 910 [8,2]
+ CRUSH rule 5 x 911 [8,1]
+ CRUSH rule 5 x 912 [1,8]
+ CRUSH rule 5 x 913 [8,2,4]
+ CRUSH rule 5 x 914 [6,4,2]
+ CRUSH rule 5 x 915 [8,2]
+ CRUSH rule 5 x 916 [4,1,8]
+ CRUSH rule 5 x 917 [1,4,8]
+ CRUSH rule 5 x 918 [8,2]
+ CRUSH rule 5 x 919 [8,2]
+ CRUSH rule 5 x 920 [8,1]
+ CRUSH rule 5 x 921 [1,8]
+ CRUSH rule 5 x 922 [8,4,2]
+ CRUSH rule 5 x 923 [4,8,2]
+ CRUSH rule 5 x 924 [8,1]
+ CRUSH rule 5 x 925 [4,8,2]
+ CRUSH rule 5 x 926 [2,8]
+ CRUSH rule 5 x 927 [1,8,4]
+ CRUSH rule 5 x 928 [8,1]
+ CRUSH rule 5 x 929 [4,1,8]
+ CRUSH rule 5 x 930 [2,8]
+ CRUSH rule 5 x 931 [2,8]
+ CRUSH rule 5 x 932 [4,1,8]
+ CRUSH rule 5 x 933 [8,4,1]
+ CRUSH rule 5 x 934 [8,2]
+ CRUSH rule 5 x 935 [8,2]
+ CRUSH rule 5 x 936 [1,8]
+ CRUSH rule 5 x 937 [4,8,2]
+ CRUSH rule 5 x 938 [8,4,2]
+ CRUSH rule 5 x 939 [2,8,4]
+ CRUSH rule 5 x 940 [8,1]
+ CRUSH rule 5 x 941 [2,8]
+ CRUSH rule 5 x 942 [1,8]
+ CRUSH rule 5 x 943 [8,2]
+ CRUSH rule 5 x 944 [8,2]
+ CRUSH rule 5 x 945 [8,2,4]
+ CRUSH rule 5 x 946 [2,8,4]
+ CRUSH rule 5 x 947 [8,2]
+ CRUSH rule 5 x 948 [8,1]
+ CRUSH rule 5 x 949 [6,1]
+ CRUSH rule 5 x 950 [8,1]
+ CRUSH rule 5 x 951 [8,1]
+ CRUSH rule 5 x 952 [2,8,4]
+ CRUSH rule 5 x 953 [1,4,8]
+ CRUSH rule 5 x 954 [2,8]
+ CRUSH rule 5 x 955 [8,1,4]
+ CRUSH rule 5 x 956 [1,8,4]
+ CRUSH rule 5 x 957 [8,1,4]
+ CRUSH rule 5 x 958 [8,4,1]
+ CRUSH rule 5 x 959 [4,2,8]
+ CRUSH rule 5 x 960 [6,1]
+ CRUSH rule 5 x 961 [1,8]
+ CRUSH rule 5 x 962 [8,4,2]
+ CRUSH rule 5 x 963 [2,4,6]
+ CRUSH rule 5 x 964 [2,8]
+ CRUSH rule 5 x 965 [8,2]
+ CRUSH rule 5 x 966 [4,8,1]
+ CRUSH rule 5 x 967 [8,4,2]
+ CRUSH rule 5 x 968 [8,2]
+ CRUSH rule 5 x 969 [8,2,4]
+ CRUSH rule 5 x 970 [2,8,4]
+ CRUSH rule 5 x 971 [1,8]
+ CRUSH rule 5 x 972 [1,8]
+ CRUSH rule 5 x 973 [1,8]
+ CRUSH rule 5 x 974 [4,1,8]
+ CRUSH rule 5 x 975 [4,8,1]
+ CRUSH rule 5 x 976 [4,8,2]
+ CRUSH rule 5 x 977 [8,4,2]
+ CRUSH rule 5 x 978 [8,2,4]
+ CRUSH rule 5 x 979 [8,1,4]
+ CRUSH rule 5 x 980 [8,2,4]
+ CRUSH rule 5 x 981 [8,2]
+ CRUSH rule 5 x 982 [1,8]
+ CRUSH rule 5 x 983 [4,8,2]
+ CRUSH rule 5 x 984 [2,8]
+ CRUSH rule 5 x 985 [2,4,8]
+ CRUSH rule 5 x 986 [8,4,1]
+ CRUSH rule 5 x 987 [2,8]
+ CRUSH rule 5 x 988 [1,4,6]
+ CRUSH rule 5 x 989 [1,8]
+ CRUSH rule 5 x 990 [1,8,4]
+ CRUSH rule 5 x 991 [1,4,8]
+ CRUSH rule 5 x 992 [8,1,4]
+ CRUSH rule 5 x 993 [2,8,4]
+ CRUSH rule 5 x 994 [4,8,2]
+ CRUSH rule 5 x 995 [8,1,4]
+ CRUSH rule 5 x 996 [8,4,1]
+ CRUSH rule 5 x 997 [8,4,1]
+ CRUSH rule 5 x 998 [8,1,4]
+ CRUSH rule 5 x 999 [1,8,4]
+ CRUSH rule 5 x 1000 [8,4,2]
+ CRUSH rule 5 x 1001 [2,8]
+ CRUSH rule 5 x 1002 [1,8]
+ CRUSH rule 5 x 1003 [2,8]
+ CRUSH rule 5 x 1004 [8,1,4]
+ CRUSH rule 5 x 1005 [8,1]
+ CRUSH rule 5 x 1006 [1,8,4]
+ CRUSH rule 5 x 1007 [1,4,8]
+ CRUSH rule 5 x 1008 [1,8]
+ CRUSH rule 5 x 1009 [6,4,1]
+ CRUSH rule 5 x 1010 [1,8]
+ CRUSH rule 5 x 1011 [4,2,8]
+ CRUSH rule 5 x 1012 [1,8]
+ CRUSH rule 5 x 1013 [2,8]
+ CRUSH rule 5 x 1014 [2,8,4]
+ CRUSH rule 5 x 1015 [8,1]
+ CRUSH rule 5 x 1016 [2,4,8]
+ CRUSH rule 5 x 1017 [6,2,4]
+ CRUSH rule 5 x 1018 [4,1,8]
+ CRUSH rule 5 x 1019 [4,8,2]
+ CRUSH rule 5 x 1020 [1,8]
+ CRUSH rule 5 x 1021 [2,8]
+ CRUSH rule 5 x 1022 [1,8,4]
+ CRUSH rule 5 x 1023 [4,2,8]
+ rule 5 (chooseleaf-set) num_rep 3 result size == 2:\t501/1024 (esc)
+ rule 5 (chooseleaf-set) num_rep 3 result size == 3:\t523/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/show-choose-tries.t b/src/test/cli/crushtool/show-choose-tries.t
new file mode 100644
index 000000000..6a7ecaec0
--- /dev/null
+++ b/src/test/cli/crushtool/show-choose-tries.t
@@ -0,0 +1,109 @@
+ $ crushtool -c "$TESTDIR/show-choose-tries.txt" -o "$TESTDIR/show-choose-tries.crushmap"
+ $ FIRSTN_RULESET=0
+ $ crushtool -i "$TESTDIR/show-choose-tries.crushmap" --test --show-choose-tries --rule $FIRSTN_RULESET --x 1 --num-rep 2
+ 0: 1
+ 1: 1
+ 2: 0
+ 3: 0
+ 4: 0
+ 5: 0
+ 6: 0
+ 7: 0
+ 8: 0
+ 9: 0
+ 10: 0
+ 11: 0
+ 12: 0
+ 13: 0
+ 14: 0
+ 15: 0
+ 16: 0
+ 17: 0
+ 18: 0
+ 19: 0
+ 20: 0
+ 21: 0
+ 22: 0
+ 23: 0
+ 24: 0
+ 25: 0
+ 26: 0
+ 27: 0
+ 28: 0
+ 29: 0
+ 30: 0
+ 31: 0
+ 32: 0
+ 33: 0
+ 34: 0
+ 35: 0
+ 36: 0
+ 37: 0
+ 38: 0
+ 39: 0
+ 40: 0
+ 41: 0
+ 42: 0
+ 43: 0
+ 44: 0
+ 45: 0
+ 46: 0
+ 47: 0
+ 48: 0
+ 49: 0
+ $ INDEP_RULESET=1
+ $ crushtool -i "$TESTDIR/show-choose-tries.crushmap" --test --show-choose-tries --rule $INDEP_RULESET --x 1 --num-rep 1
+ 0: 0
+ 1: 1
+ 2: 0
+ 3: 0
+ 4: 0
+ 5: 0
+ 6: 0
+ 7: 0
+ 8: 0
+ 9: 0
+ 10: 0
+ 11: 0
+ 12: 0
+ 13: 0
+ 14: 0
+ 15: 0
+ 16: 0
+ 17: 0
+ 18: 0
+ 19: 0
+ 20: 0
+ 21: 0
+ 22: 0
+ 23: 0
+ 24: 0
+ 25: 0
+ 26: 0
+ 27: 0
+ 28: 0
+ 29: 0
+ 30: 0
+ 31: 0
+ 32: 0
+ 33: 0
+ 34: 0
+ 35: 0
+ 36: 0
+ 37: 0
+ 38: 0
+ 39: 0
+ 40: 0
+ 41: 0
+ 42: 0
+ 43: 0
+ 44: 0
+ 45: 0
+ 46: 0
+ 47: 0
+ 48: 0
+ 49: 0
+ $ rm -f "$TESTDIR/show-choose-tries.crushmap"
+# Local Variables:
+# compile-command: "cd ../../.. ; make -j4 crushtool && test/run-cli-tests"
+# End:
diff --git a/src/test/cli/crushtool/show-choose-tries.txt b/src/test/cli/crushtool/show-choose-tries.txt
new file mode 100644
index 000000000..c467408b4
--- /dev/null
+++ b/src/test/cli/crushtool/show-choose-tries.txt
@@ -0,0 +1,43 @@
+# begin crush map
+tunable choose_local_tries 0
+tunable choose_local_fallback_tries 0
+tunable choose_total_tries 50
+tunable chooseleaf_descend_once 1
+
+# devices
+device 0 device0
+device 1 device1
+device 2 device2
+
+# types
+type 0 device
+type 1 root
+
+# buckets
+root root {
+ id -1 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+ item device2 weight 1.00000
+}
+
+rule first_rule {
+ id 0
+ type replicated
+ step take root
+ step choose firstn 0 type device
+ step emit
+}
+
+rule indep_rule {
+ id 1
+ type erasure
+ step set_chooseleaf_tries 5
+ step take root
+ step choose indep 0 type device
+ step emit
+}
+
diff --git a/src/test/cli/crushtool/simple.template b/src/test/cli/crushtool/simple.template
new file mode 100644
index 000000000..8f0076f54
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template
Binary files differ
diff --git a/src/test/cli/crushtool/simple.template.adj.one b/src/test/cli/crushtool/simple.template.adj.one
new file mode 100644
index 000000000..faf02c72c
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.adj.one
@@ -0,0 +1,50 @@
+# begin crush map
+
+# devices
+device 0 device0
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.adj.three b/src/test/cli/crushtool/simple.template.adj.three
new file mode 100644
index 000000000..52fed1f14
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.adj.three
@@ -0,0 +1,58 @@
+# begin crush map
+
+# devices
+device 0 device0
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 3.00000
+}
+host fake {
+ id -3 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 2.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 5.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 3.00000
+ item fake weight 2.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.adj.two b/src/test/cli/crushtool/simple.template.adj.two
new file mode 100644
index 000000000..00cb81055
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.adj.two
@@ -0,0 +1,58 @@
+# begin crush map
+
+# devices
+device 0 device0
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+host fake {
+ id -3 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 2.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item fake weight 2.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.five b/src/test/cli/crushtool/simple.template.five
new file mode 100644
index 000000000..74c53abeb
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.five
@@ -0,0 +1,59 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 osd1
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+host host1 {
+ id -3 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item osd1 weight 2.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 2.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.four b/src/test/cli/crushtool/simple.template.four
new file mode 100644
index 000000000..faf02c72c
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.four
@@ -0,0 +1,50 @@
+# begin crush map
+
+# devices
+device 0 device0
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.multitree b/src/test/cli/crushtool/simple.template.multitree
new file mode 100644
index 000000000..c3ab1ff53
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.multitree
@@ -0,0 +1,64 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 osd1
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+host host1 {
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item osd1 weight 2.00000
+}
+cluster cluster0 {
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 2.00000
+}
+
+cluster cluster1 {
+ # weight 3.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 2.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster1
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.multitree.reweighted b/src/test/cli/crushtool/simple.template.multitree.reweighted
new file mode 100644
index 000000000..daaf34ac7
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.multitree.reweighted
@@ -0,0 +1,67 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 osd1
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -1 # do not change unnecessarily
+ # weight 1.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+host host1 {
+ id -2 # do not change unnecessarily
+ # weight 2.50000
+ alg straw
+ hash 0 # rjenkins1
+ item osd1 weight 2.50000
+}
+cluster cluster0 {
+ id -3 # do not change unnecessarily
+ # weight 3.50000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 2.50000
+}
+cluster cluster1 {
+ id -4 # do not change unnecessarily
+ # weight 3.50000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+ item host1 weight 2.50000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster1
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.one b/src/test/cli/crushtool/simple.template.one
new file mode 100644
index 000000000..33ebc662a
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.one
@@ -0,0 +1,52 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 device1
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 2.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.three b/src/test/cli/crushtool/simple.template.three
new file mode 100644
index 000000000..33ebc662a
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.three
@@ -0,0 +1,52 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 device1
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 2.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/simple.template.two b/src/test/cli/crushtool/simple.template.two
new file mode 100644
index 000000000..33ebc662a
--- /dev/null
+++ b/src/test/cli/crushtool/simple.template.two
@@ -0,0 +1,52 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 device1
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 2.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 2.00000
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/straw2.t b/src/test/cli/crushtool/straw2.t
new file mode 100644
index 000000000..868133840
--- /dev/null
+++ b/src/test/cli/crushtool/straw2.t
@@ -0,0 +1,4 @@
+ $ crushtool -c $TESTDIR/straw2.txt -o straw2
+ $ crushtool -d straw2 -o straw2.txt.new
+ $ diff -b $TESTDIR/straw2.txt straw2.txt.new
+ $ rm straw2 straw2.txt.new
diff --git a/src/test/cli/crushtool/straw2.txt b/src/test/cli/crushtool/straw2.txt
new file mode 100644
index 000000000..c87db75d0
--- /dev/null
+++ b/src/test/cli/crushtool/straw2.txt
@@ -0,0 +1,41 @@
+# begin crush map
+tunable choose_local_tries 0
+tunable choose_local_fallback_tries 0
+tunable choose_total_tries 50
+tunable chooseleaf_descend_once 1
+tunable straw_calc_version 1
+
+# devices
+device 0 device0
+
+# types
+type 0 device
+type 1 host
+type 2 default
+
+# buckets
+host host0 {
+ id -1 # do not change unnecessarily
+ # weight 1.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+}
+default default {
+ id -2 # do not change unnecessarily
+ # weight 1.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item host0 weight 1.00000
+}
+
+# rules
+rule replicated_rule {
+ id 0
+ type replicated
+ step take default
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/test-map-a.crushmap b/src/test/cli/crushtool/test-map-a.crushmap
new file mode 100644
index 000000000..00f7579ac
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-a.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/test-map-big-1.crushmap b/src/test/cli/crushtool/test-map-big-1.crushmap
new file mode 100644
index 000000000..191573218
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-big-1.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/test-map-bobtail-tunables.t b/src/test/cli/crushtool/test-map-bobtail-tunables.t
new file mode 100644
index 000000000..3bec87a74
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-bobtail-tunables.t
@@ -0,0 +1,10253 @@
+ $ crushtool -i "$TESTDIR/test-map-a.crushmap" --test --show-mappings --show-statistics --rule 0 --set-choose-local-tries 0 --set-choose-local-fallback-tries 0 --set-choose-total-tries 50 --set-chooseleaf-descend-once 1 --min-rep 1 --max-rep 10
+ rule 0 (data), x = 0..1023, numrep = 1..10
+ CRUSH rule 0 x 0 [36]
+ CRUSH rule 0 x 1 [876]
+ CRUSH rule 0 x 2 [292]
+ CRUSH rule 0 x 3 [623]
+ CRUSH rule 0 x 4 [61]
+ CRUSH rule 0 x 5 [946]
+ CRUSH rule 0 x 6 [576]
+ CRUSH rule 0 x 7 [645]
+ CRUSH rule 0 x 8 [243]
+ CRUSH rule 0 x 9 [22]
+ CRUSH rule 0 x 10 [758]
+ CRUSH rule 0 x 11 [769]
+ CRUSH rule 0 x 12 [780]
+ CRUSH rule 0 x 13 [557]
+ CRUSH rule 0 x 14 [59]
+ CRUSH rule 0 x 15 [718]
+ CRUSH rule 0 x 16 [673]
+ CRUSH rule 0 x 17 [648]
+ CRUSH rule 0 x 18 [654]
+ CRUSH rule 0 x 19 [850]
+ CRUSH rule 0 x 20 [717]
+ CRUSH rule 0 x 21 [420]
+ CRUSH rule 0 x 22 [503]
+ CRUSH rule 0 x 23 [411]
+ CRUSH rule 0 x 24 [266]
+ CRUSH rule 0 x 25 [760]
+ CRUSH rule 0 x 26 [903]
+ CRUSH rule 0 x 27 [946]
+ CRUSH rule 0 x 28 [69]
+ CRUSH rule 0 x 29 [844]
+ CRUSH rule 0 x 30 [621]
+ CRUSH rule 0 x 31 [784]
+ CRUSH rule 0 x 32 [173]
+ CRUSH rule 0 x 33 [698]
+ CRUSH rule 0 x 34 [168]
+ CRUSH rule 0 x 35 [274]
+ CRUSH rule 0 x 36 [318]
+ CRUSH rule 0 x 37 [173]
+ CRUSH rule 0 x 38 [708]
+ CRUSH rule 0 x 39 [662]
+ CRUSH rule 0 x 40 [620]
+ CRUSH rule 0 x 41 [811]
+ CRUSH rule 0 x 42 [863]
+ CRUSH rule 0 x 43 [686]
+ CRUSH rule 0 x 44 [396]
+ CRUSH rule 0 x 45 [991]
+ CRUSH rule 0 x 46 [420]
+ CRUSH rule 0 x 47 [467]
+ CRUSH rule 0 x 48 [955]
+ CRUSH rule 0 x 49 [974]
+ CRUSH rule 0 x 50 [870]
+ CRUSH rule 0 x 51 [182]
+ CRUSH rule 0 x 52 [704]
+ CRUSH rule 0 x 53 [185]
+ CRUSH rule 0 x 54 [270]
+ CRUSH rule 0 x 55 [895]
+ CRUSH rule 0 x 56 [564]
+ CRUSH rule 0 x 57 [738]
+ CRUSH rule 0 x 58 [524]
+ CRUSH rule 0 x 59 [408]
+ CRUSH rule 0 x 60 [228]
+ CRUSH rule 0 x 61 [154]
+ CRUSH rule 0 x 62 [594]
+ CRUSH rule 0 x 63 [646]
+ CRUSH rule 0 x 64 [175]
+ CRUSH rule 0 x 65 [745]
+ CRUSH rule 0 x 66 [275]
+ CRUSH rule 0 x 67 [246]
+ CRUSH rule 0 x 68 [711]
+ CRUSH rule 0 x 69 [493]
+ CRUSH rule 0 x 70 [30]
+ CRUSH rule 0 x 71 [984]
+ CRUSH rule 0 x 72 [71]
+ CRUSH rule 0 x 73 [922]
+ CRUSH rule 0 x 74 [629]
+ CRUSH rule 0 x 75 [222]
+ CRUSH rule 0 x 76 [262]
+ CRUSH rule 0 x 77 [638]
+ CRUSH rule 0 x 78 [324]
+ CRUSH rule 0 x 79 [577]
+ CRUSH rule 0 x 80 [501]
+ CRUSH rule 0 x 81 [506]
+ CRUSH rule 0 x 82 [222]
+ CRUSH rule 0 x 83 [71]
+ CRUSH rule 0 x 84 [49]
+ CRUSH rule 0 x 85 [985]
+ CRUSH rule 0 x 86 [537]
+ CRUSH rule 0 x 87 [997]
+ CRUSH rule 0 x 88 [957]
+ CRUSH rule 0 x 89 [399]
+ CRUSH rule 0 x 90 [943]
+ CRUSH rule 0 x 91 [22]
+ CRUSH rule 0 x 92 [532]
+ CRUSH rule 0 x 93 [218]
+ CRUSH rule 0 x 94 [181]
+ CRUSH rule 0 x 95 [343]
+ CRUSH rule 0 x 96 [861]
+ CRUSH rule 0 x 97 [459]
+ CRUSH rule 0 x 98 [327]
+ CRUSH rule 0 x 99 [974]
+ CRUSH rule 0 x 100 [32]
+ CRUSH rule 0 x 101 [142]
+ CRUSH rule 0 x 102 [172]
+ CRUSH rule 0 x 103 [630]
+ CRUSH rule 0 x 104 [758]
+ CRUSH rule 0 x 105 [843]
+ CRUSH rule 0 x 106 [28]
+ CRUSH rule 0 x 107 [74]
+ CRUSH rule 0 x 108 [875]
+ CRUSH rule 0 x 109 [411]
+ CRUSH rule 0 x 110 [440]
+ CRUSH rule 0 x 111 [405]
+ CRUSH rule 0 x 112 [143]
+ CRUSH rule 0 x 113 [153]
+ CRUSH rule 0 x 114 [804]
+ CRUSH rule 0 x 115 [588]
+ CRUSH rule 0 x 116 [327]
+ CRUSH rule 0 x 117 [95]
+ CRUSH rule 0 x 118 [80]
+ CRUSH rule 0 x 119 [386]
+ CRUSH rule 0 x 120 [366]
+ CRUSH rule 0 x 121 [129]
+ CRUSH rule 0 x 122 [873]
+ CRUSH rule 0 x 123 [533]
+ CRUSH rule 0 x 124 [461]
+ CRUSH rule 0 x 125 [342]
+ CRUSH rule 0 x 126 [819]
+ CRUSH rule 0 x 127 [437]
+ CRUSH rule 0 x 128 [679]
+ CRUSH rule 0 x 129 [380]
+ CRUSH rule 0 x 130 [992]
+ CRUSH rule 0 x 131 [469]
+ CRUSH rule 0 x 132 [571]
+ CRUSH rule 0 x 133 [964]
+ CRUSH rule 0 x 134 [999]
+ CRUSH rule 0 x 135 [634]
+ CRUSH rule 0 x 136 [114]
+ CRUSH rule 0 x 137 [839]
+ CRUSH rule 0 x 138 [967]
+ CRUSH rule 0 x 139 [308]
+ CRUSH rule 0 x 140 [764]
+ CRUSH rule 0 x 141 [423]
+ CRUSH rule 0 x 142 [252]
+ CRUSH rule 0 x 143 [33]
+ CRUSH rule 0 x 144 [472]
+ CRUSH rule 0 x 145 [242]
+ CRUSH rule 0 x 146 [290]
+ CRUSH rule 0 x 147 [447]
+ CRUSH rule 0 x 148 [212]
+ CRUSH rule 0 x 149 [9]
+ CRUSH rule 0 x 150 [166]
+ CRUSH rule 0 x 151 [811]
+ CRUSH rule 0 x 152 [449]
+ CRUSH rule 0 x 153 [523]
+ CRUSH rule 0 x 154 [208]
+ CRUSH rule 0 x 155 [569]
+ CRUSH rule 0 x 156 [488]
+ CRUSH rule 0 x 157 [140]
+ CRUSH rule 0 x 158 [786]
+ CRUSH rule 0 x 159 [134]
+ CRUSH rule 0 x 160 [690]
+ CRUSH rule 0 x 161 [324]
+ CRUSH rule 0 x 162 [748]
+ CRUSH rule 0 x 163 [575]
+ CRUSH rule 0 x 164 [314]
+ CRUSH rule 0 x 165 [116]
+ CRUSH rule 0 x 166 [352]
+ CRUSH rule 0 x 167 [27]
+ CRUSH rule 0 x 168 [953]
+ CRUSH rule 0 x 169 [912]
+ CRUSH rule 0 x 170 [421]
+ CRUSH rule 0 x 171 [488]
+ CRUSH rule 0 x 172 [366]
+ CRUSH rule 0 x 173 [863]
+ CRUSH rule 0 x 174 [263]
+ CRUSH rule 0 x 175 [875]
+ CRUSH rule 0 x 176 [745]
+ CRUSH rule 0 x 177 [128]
+ CRUSH rule 0 x 178 [155]
+ CRUSH rule 0 x 179 [593]
+ CRUSH rule 0 x 180 [154]
+ CRUSH rule 0 x 181 [289]
+ CRUSH rule 0 x 182 [730]
+ CRUSH rule 0 x 183 [639]
+ CRUSH rule 0 x 184 [704]
+ CRUSH rule 0 x 185 [97]
+ CRUSH rule 0 x 186 [26]
+ CRUSH rule 0 x 187 [649]
+ CRUSH rule 0 x 188 [682]
+ CRUSH rule 0 x 189 [325]
+ CRUSH rule 0 x 190 [399]
+ CRUSH rule 0 x 191 [629]
+ CRUSH rule 0 x 192 [503]
+ CRUSH rule 0 x 193 [546]
+ CRUSH rule 0 x 194 [242]
+ CRUSH rule 0 x 195 [625]
+ CRUSH rule 0 x 196 [357]
+ CRUSH rule 0 x 197 [306]
+ CRUSH rule 0 x 198 [863]
+ CRUSH rule 0 x 199 [935]
+ CRUSH rule 0 x 200 [373]
+ CRUSH rule 0 x 201 [659]
+ CRUSH rule 0 x 202 [260]
+ CRUSH rule 0 x 203 [36]
+ CRUSH rule 0 x 204 [92]
+ CRUSH rule 0 x 205 [68]
+ CRUSH rule 0 x 206 [570]
+ CRUSH rule 0 x 207 [834]
+ CRUSH rule 0 x 208 [927]
+ CRUSH rule 0 x 209 [878]
+ CRUSH rule 0 x 210 [572]
+ CRUSH rule 0 x 211 [107]
+ CRUSH rule 0 x 212 [389]
+ CRUSH rule 0 x 213 [497]
+ CRUSH rule 0 x 214 [798]
+ CRUSH rule 0 x 215 [233]
+ CRUSH rule 0 x 216 [494]
+ CRUSH rule 0 x 217 [352]
+ CRUSH rule 0 x 218 [895]
+ CRUSH rule 0 x 219 [222]
+ CRUSH rule 0 x 220 [281]
+ CRUSH rule 0 x 221 [64]
+ CRUSH rule 0 x 222 [40]
+ CRUSH rule 0 x 223 [645]
+ CRUSH rule 0 x 224 [647]
+ CRUSH rule 0 x 225 [219]
+ CRUSH rule 0 x 226 [372]
+ CRUSH rule 0 x 227 [925]
+ CRUSH rule 0 x 228 [682]
+ CRUSH rule 0 x 229 [880]
+ CRUSH rule 0 x 230 [328]
+ CRUSH rule 0 x 231 [320]
+ CRUSH rule 0 x 232 [924]
+ CRUSH rule 0 x 233 [948]
+ CRUSH rule 0 x 234 [484]
+ CRUSH rule 0 x 235 [750]
+ CRUSH rule 0 x 236 [551]
+ CRUSH rule 0 x 237 [390]
+ CRUSH rule 0 x 238 [570]
+ CRUSH rule 0 x 239 [729]
+ CRUSH rule 0 x 240 [981]
+ CRUSH rule 0 x 241 [310]
+ CRUSH rule 0 x 242 [161]
+ CRUSH rule 0 x 243 [180]
+ CRUSH rule 0 x 244 [52]
+ CRUSH rule 0 x 245 [523]
+ CRUSH rule 0 x 246 [362]
+ CRUSH rule 0 x 247 [382]
+ CRUSH rule 0 x 248 [129]
+ CRUSH rule 0 x 249 [159]
+ CRUSH rule 0 x 250 [404]
+ CRUSH rule 0 x 251 [661]
+ CRUSH rule 0 x 252 [961]
+ CRUSH rule 0 x 253 [651]
+ CRUSH rule 0 x 254 [123]
+ CRUSH rule 0 x 255 [314]
+ CRUSH rule 0 x 256 [315]
+ CRUSH rule 0 x 257 [825]
+ CRUSH rule 0 x 258 [624]
+ CRUSH rule 0 x 259 [602]
+ CRUSH rule 0 x 260 [717]
+ CRUSH rule 0 x 261 [145]
+ CRUSH rule 0 x 262 [223]
+ CRUSH rule 0 x 263 [462]
+ CRUSH rule 0 x 264 [654]
+ CRUSH rule 0 x 265 [302]
+ CRUSH rule 0 x 266 [202]
+ CRUSH rule 0 x 267 [282]
+ CRUSH rule 0 x 268 [338]
+ CRUSH rule 0 x 269 [738]
+ CRUSH rule 0 x 270 [707]
+ CRUSH rule 0 x 271 [705]
+ CRUSH rule 0 x 272 [756]
+ CRUSH rule 0 x 273 [197]
+ CRUSH rule 0 x 274 [992]
+ CRUSH rule 0 x 275 [544]
+ CRUSH rule 0 x 276 [658]
+ CRUSH rule 0 x 277 [143]
+ CRUSH rule 0 x 278 [492]
+ CRUSH rule 0 x 279 [517]
+ CRUSH rule 0 x 280 [825]
+ CRUSH rule 0 x 281 [224]
+ CRUSH rule 0 x 282 [298]
+ CRUSH rule 0 x 283 [311]
+ CRUSH rule 0 x 284 [771]
+ CRUSH rule 0 x 285 [693]
+ CRUSH rule 0 x 286 [364]
+ CRUSH rule 0 x 287 [591]
+ CRUSH rule 0 x 288 [965]
+ CRUSH rule 0 x 289 [225]
+ CRUSH rule 0 x 290 [577]
+ CRUSH rule 0 x 291 [160]
+ CRUSH rule 0 x 292 [873]
+ CRUSH rule 0 x 293 [100]
+ CRUSH rule 0 x 294 [285]
+ CRUSH rule 0 x 295 [938]
+ CRUSH rule 0 x 296 [850]
+ CRUSH rule 0 x 297 [951]
+ CRUSH rule 0 x 298 [173]
+ CRUSH rule 0 x 299 [598]
+ CRUSH rule 0 x 300 [531]
+ CRUSH rule 0 x 301 [823]
+ CRUSH rule 0 x 302 [184]
+ CRUSH rule 0 x 303 [521]
+ CRUSH rule 0 x 304 [980]
+ CRUSH rule 0 x 305 [153]
+ CRUSH rule 0 x 306 [423]
+ CRUSH rule 0 x 307 [997]
+ CRUSH rule 0 x 308 [991]
+ CRUSH rule 0 x 309 [860]
+ CRUSH rule 0 x 310 [589]
+ CRUSH rule 0 x 311 [477]
+ CRUSH rule 0 x 312 [887]
+ CRUSH rule 0 x 313 [802]
+ CRUSH rule 0 x 314 [654]
+ CRUSH rule 0 x 315 [767]
+ CRUSH rule 0 x 316 [778]
+ CRUSH rule 0 x 317 [184]
+ CRUSH rule 0 x 318 [525]
+ CRUSH rule 0 x 319 [476]
+ CRUSH rule 0 x 320 [149]
+ CRUSH rule 0 x 321 [710]
+ CRUSH rule 0 x 322 [175]
+ CRUSH rule 0 x 323 [819]
+ CRUSH rule 0 x 324 [16]
+ CRUSH rule 0 x 325 [486]
+ CRUSH rule 0 x 326 [613]
+ CRUSH rule 0 x 327 [125]
+ CRUSH rule 0 x 328 [807]
+ CRUSH rule 0 x 329 [588]
+ CRUSH rule 0 x 330 [932]
+ CRUSH rule 0 x 331 [341]
+ CRUSH rule 0 x 332 [153]
+ CRUSH rule 0 x 333 [745]
+ CRUSH rule 0 x 334 [614]
+ CRUSH rule 0 x 335 [518]
+ CRUSH rule 0 x 336 [389]
+ CRUSH rule 0 x 337 [753]
+ CRUSH rule 0 x 338 [128]
+ CRUSH rule 0 x 339 [430]
+ CRUSH rule 0 x 340 [541]
+ CRUSH rule 0 x 341 [402]
+ CRUSH rule 0 x 342 [982]
+ CRUSH rule 0 x 343 [833]
+ CRUSH rule 0 x 344 [784]
+ CRUSH rule 0 x 345 [546]
+ CRUSH rule 0 x 346 [302]
+ CRUSH rule 0 x 347 [488]
+ CRUSH rule 0 x 348 [903]
+ CRUSH rule 0 x 349 [471]
+ CRUSH rule 0 x 350 [348]
+ CRUSH rule 0 x 351 [961]
+ CRUSH rule 0 x 352 [728]
+ CRUSH rule 0 x 353 [904]
+ CRUSH rule 0 x 354 [345]
+ CRUSH rule 0 x 355 [50]
+ CRUSH rule 0 x 356 [87]
+ CRUSH rule 0 x 357 [762]
+ CRUSH rule 0 x 358 [908]
+ CRUSH rule 0 x 359 [484]
+ CRUSH rule 0 x 360 [173]
+ CRUSH rule 0 x 361 [404]
+ CRUSH rule 0 x 362 [403]
+ CRUSH rule 0 x 363 [639]
+ CRUSH rule 0 x 364 [752]
+ CRUSH rule 0 x 365 [956]
+ CRUSH rule 0 x 366 [860]
+ CRUSH rule 0 x 367 [205]
+ CRUSH rule 0 x 368 [301]
+ CRUSH rule 0 x 369 [452]
+ CRUSH rule 0 x 370 [11]
+ CRUSH rule 0 x 371 [124]
+ CRUSH rule 0 x 372 [253]
+ CRUSH rule 0 x 373 [715]
+ CRUSH rule 0 x 374 [191]
+ CRUSH rule 0 x 375 [711]
+ CRUSH rule 0 x 376 [597]
+ CRUSH rule 0 x 377 [294]
+ CRUSH rule 0 x 378 [34]
+ CRUSH rule 0 x 379 [869]
+ CRUSH rule 0 x 380 [294]
+ CRUSH rule 0 x 381 [119]
+ CRUSH rule 0 x 382 [69]
+ CRUSH rule 0 x 383 [922]
+ CRUSH rule 0 x 384 [221]
+ CRUSH rule 0 x 385 [561]
+ CRUSH rule 0 x 386 [335]
+ CRUSH rule 0 x 387 [514]
+ CRUSH rule 0 x 388 [587]
+ CRUSH rule 0 x 389 [109]
+ CRUSH rule 0 x 390 [925]
+ CRUSH rule 0 x 391 [267]
+ CRUSH rule 0 x 392 [382]
+ CRUSH rule 0 x 393 [425]
+ CRUSH rule 0 x 394 [898]
+ CRUSH rule 0 x 395 [806]
+ CRUSH rule 0 x 396 [790]
+ CRUSH rule 0 x 397 [136]
+ CRUSH rule 0 x 398 [914]
+ CRUSH rule 0 x 399 [261]
+ CRUSH rule 0 x 400 [661]
+ CRUSH rule 0 x 401 [953]
+ CRUSH rule 0 x 402 [738]
+ CRUSH rule 0 x 403 [573]
+ CRUSH rule 0 x 404 [526]
+ CRUSH rule 0 x 405 [582]
+ CRUSH rule 0 x 406 [768]
+ CRUSH rule 0 x 407 [260]
+ CRUSH rule 0 x 408 [657]
+ CRUSH rule 0 x 409 [498]
+ CRUSH rule 0 x 410 [28]
+ CRUSH rule 0 x 411 [684]
+ CRUSH rule 0 x 412 [261]
+ CRUSH rule 0 x 413 [891]
+ CRUSH rule 0 x 414 [127]
+ CRUSH rule 0 x 415 [272]
+ CRUSH rule 0 x 416 [739]
+ CRUSH rule 0 x 417 [106]
+ CRUSH rule 0 x 418 [525]
+ CRUSH rule 0 x 419 [603]
+ CRUSH rule 0 x 420 [988]
+ CRUSH rule 0 x 421 [761]
+ CRUSH rule 0 x 422 [317]
+ CRUSH rule 0 x 423 [137]
+ CRUSH rule 0 x 424 [920]
+ CRUSH rule 0 x 425 [277]
+ CRUSH rule 0 x 426 [485]
+ CRUSH rule 0 x 427 [242]
+ CRUSH rule 0 x 428 [632]
+ CRUSH rule 0 x 429 [641]
+ CRUSH rule 0 x 430 [626]
+ CRUSH rule 0 x 431 [697]
+ CRUSH rule 0 x 432 [590]
+ CRUSH rule 0 x 433 [284]
+ CRUSH rule 0 x 434 [538]
+ CRUSH rule 0 x 435 [30]
+ CRUSH rule 0 x 436 [164]
+ CRUSH rule 0 x 437 [322]
+ CRUSH rule 0 x 438 [142]
+ CRUSH rule 0 x 439 [119]
+ CRUSH rule 0 x 440 [333]
+ CRUSH rule 0 x 441 [477]
+ CRUSH rule 0 x 442 [274]
+ CRUSH rule 0 x 443 [983]
+ CRUSH rule 0 x 444 [536]
+ CRUSH rule 0 x 445 [485]
+ CRUSH rule 0 x 446 [345]
+ CRUSH rule 0 x 447 [61]
+ CRUSH rule 0 x 448 [333]
+ CRUSH rule 0 x 449 [680]
+ CRUSH rule 0 x 450 [235]
+ CRUSH rule 0 x 451 [961]
+ CRUSH rule 0 x 452 [525]
+ CRUSH rule 0 x 453 [138]
+ CRUSH rule 0 x 454 [137]
+ CRUSH rule 0 x 455 [173]
+ CRUSH rule 0 x 456 [235]
+ CRUSH rule 0 x 457 [450]
+ CRUSH rule 0 x 458 [195]
+ CRUSH rule 0 x 459 [381]
+ CRUSH rule 0 x 460 [972]
+ CRUSH rule 0 x 461 [506]
+ CRUSH rule 0 x 462 [692]
+ CRUSH rule 0 x 463 [788]
+ CRUSH rule 0 x 464 [133]
+ CRUSH rule 0 x 465 [971]
+ CRUSH rule 0 x 466 [394]
+ CRUSH rule 0 x 467 [517]
+ CRUSH rule 0 x 468 [829]
+ CRUSH rule 0 x 469 [987]
+ CRUSH rule 0 x 470 [107]
+ CRUSH rule 0 x 471 [181]
+ CRUSH rule 0 x 472 [547]
+ CRUSH rule 0 x 473 [760]
+ CRUSH rule 0 x 474 [787]
+ CRUSH rule 0 x 475 [662]
+ CRUSH rule 0 x 476 [110]
+ CRUSH rule 0 x 477 [393]
+ CRUSH rule 0 x 478 [246]
+ CRUSH rule 0 x 479 [70]
+ CRUSH rule 0 x 480 [753]
+ CRUSH rule 0 x 481 [470]
+ CRUSH rule 0 x 482 [451]
+ CRUSH rule 0 x 483 [816]
+ CRUSH rule 0 x 484 [540]
+ CRUSH rule 0 x 485 [74]
+ CRUSH rule 0 x 486 [958]
+ CRUSH rule 0 x 487 [228]
+ CRUSH rule 0 x 488 [180]
+ CRUSH rule 0 x 489 [47]
+ CRUSH rule 0 x 490 [905]
+ CRUSH rule 0 x 491 [892]
+ CRUSH rule 0 x 492 [588]
+ CRUSH rule 0 x 493 [353]
+ CRUSH rule 0 x 494 [378]
+ CRUSH rule 0 x 495 [845]
+ CRUSH rule 0 x 496 [13]
+ CRUSH rule 0 x 497 [796]
+ CRUSH rule 0 x 498 [412]
+ CRUSH rule 0 x 499 [330]
+ CRUSH rule 0 x 500 [820]
+ CRUSH rule 0 x 501 [110]
+ CRUSH rule 0 x 502 [336]
+ CRUSH rule 0 x 503 [922]
+ CRUSH rule 0 x 504 [483]
+ CRUSH rule 0 x 505 [482]
+ CRUSH rule 0 x 506 [493]
+ CRUSH rule 0 x 507 [12]
+ CRUSH rule 0 x 508 [227]
+ CRUSH rule 0 x 509 [807]
+ CRUSH rule 0 x 510 [134]
+ CRUSH rule 0 x 511 [212]
+ CRUSH rule 0 x 512 [236]
+ CRUSH rule 0 x 513 [994]
+ CRUSH rule 0 x 514 [45]
+ CRUSH rule 0 x 515 [504]
+ CRUSH rule 0 x 516 [285]
+ CRUSH rule 0 x 517 [300]
+ CRUSH rule 0 x 518 [397]
+ CRUSH rule 0 x 519 [86]
+ CRUSH rule 0 x 520 [900]
+ CRUSH rule 0 x 521 [31]
+ CRUSH rule 0 x 522 [390]
+ CRUSH rule 0 x 523 [618]
+ CRUSH rule 0 x 524 [635]
+ CRUSH rule 0 x 525 [311]
+ CRUSH rule 0 x 526 [48]
+ CRUSH rule 0 x 527 [202]
+ CRUSH rule 0 x 528 [565]
+ CRUSH rule 0 x 529 [934]
+ CRUSH rule 0 x 530 [502]
+ CRUSH rule 0 x 531 [681]
+ CRUSH rule 0 x 532 [422]
+ CRUSH rule 0 x 533 [863]
+ CRUSH rule 0 x 534 [962]
+ CRUSH rule 0 x 535 [89]
+ CRUSH rule 0 x 536 [499]
+ CRUSH rule 0 x 537 [676]
+ CRUSH rule 0 x 538 [58]
+ CRUSH rule 0 x 539 [837]
+ CRUSH rule 0 x 540 [831]
+ CRUSH rule 0 x 541 [582]
+ CRUSH rule 0 x 542 [472]
+ CRUSH rule 0 x 543 [382]
+ CRUSH rule 0 x 544 [947]
+ CRUSH rule 0 x 545 [425]
+ CRUSH rule 0 x 546 [18]
+ CRUSH rule 0 x 547 [445]
+ CRUSH rule 0 x 548 [367]
+ CRUSH rule 0 x 549 [125]
+ CRUSH rule 0 x 550 [425]
+ CRUSH rule 0 x 551 [44]
+ CRUSH rule 0 x 552 [246]
+ CRUSH rule 0 x 553 [71]
+ CRUSH rule 0 x 554 [207]
+ CRUSH rule 0 x 555 [570]
+ CRUSH rule 0 x 556 [674]
+ CRUSH rule 0 x 557 [347]
+ CRUSH rule 0 x 558 [627]
+ CRUSH rule 0 x 559 [940]
+ CRUSH rule 0 x 560 [295]
+ CRUSH rule 0 x 561 [506]
+ CRUSH rule 0 x 562 [718]
+ CRUSH rule 0 x 563 [552]
+ CRUSH rule 0 x 564 [835]
+ CRUSH rule 0 x 565 [8]
+ CRUSH rule 0 x 566 [600]
+ CRUSH rule 0 x 567 [999]
+ CRUSH rule 0 x 568 [252]
+ CRUSH rule 0 x 569 [643]
+ CRUSH rule 0 x 570 [617]
+ CRUSH rule 0 x 571 [757]
+ CRUSH rule 0 x 572 [299]
+ CRUSH rule 0 x 573 [25]
+ CRUSH rule 0 x 574 [215]
+ CRUSH rule 0 x 575 [225]
+ CRUSH rule 0 x 576 [627]
+ CRUSH rule 0 x 577 [237]
+ CRUSH rule 0 x 578 [885]
+ CRUSH rule 0 x 579 [924]
+ CRUSH rule 0 x 580 [718]
+ CRUSH rule 0 x 581 [219]
+ CRUSH rule 0 x 582 [893]
+ CRUSH rule 0 x 583 [246]
+ CRUSH rule 0 x 584 [336]
+ CRUSH rule 0 x 585 [324]
+ CRUSH rule 0 x 586 [558]
+ CRUSH rule 0 x 587 [985]
+ CRUSH rule 0 x 588 [211]
+ CRUSH rule 0 x 589 [129]
+ CRUSH rule 0 x 590 [467]
+ CRUSH rule 0 x 591 [758]
+ CRUSH rule 0 x 592 [525]
+ CRUSH rule 0 x 593 [601]
+ CRUSH rule 0 x 594 [227]
+ CRUSH rule 0 x 595 [720]
+ CRUSH rule 0 x 596 [751]
+ CRUSH rule 0 x 597 [129]
+ CRUSH rule 0 x 598 [679]
+ CRUSH rule 0 x 599 [668]
+ CRUSH rule 0 x 600 [143]
+ CRUSH rule 0 x 601 [326]
+ CRUSH rule 0 x 602 [860]
+ CRUSH rule 0 x 603 [709]
+ CRUSH rule 0 x 604 [571]
+ CRUSH rule 0 x 605 [252]
+ CRUSH rule 0 x 606 [339]
+ CRUSH rule 0 x 607 [590]
+ CRUSH rule 0 x 608 [145]
+ CRUSH rule 0 x 609 [973]
+ CRUSH rule 0 x 610 [435]
+ CRUSH rule 0 x 611 [559]
+ CRUSH rule 0 x 612 [273]
+ CRUSH rule 0 x 613 [828]
+ CRUSH rule 0 x 614 [478]
+ CRUSH rule 0 x 615 [392]
+ CRUSH rule 0 x 616 [778]
+ CRUSH rule 0 x 617 [622]
+ CRUSH rule 0 x 618 [149]
+ CRUSH rule 0 x 619 [604]
+ CRUSH rule 0 x 620 [181]
+ CRUSH rule 0 x 621 [735]
+ CRUSH rule 0 x 622 [661]
+ CRUSH rule 0 x 623 [142]
+ CRUSH rule 0 x 624 [360]
+ CRUSH rule 0 x 625 [541]
+ CRUSH rule 0 x 626 [364]
+ CRUSH rule 0 x 627 [458]
+ CRUSH rule 0 x 628 [250]
+ CRUSH rule 0 x 629 [928]
+ CRUSH rule 0 x 630 [243]
+ CRUSH rule 0 x 631 [438]
+ CRUSH rule 0 x 632 [797]
+ CRUSH rule 0 x 633 [993]
+ CRUSH rule 0 x 634 [239]
+ CRUSH rule 0 x 635 [640]
+ CRUSH rule 0 x 636 [173]
+ CRUSH rule 0 x 637 [0]
+ CRUSH rule 0 x 638 [702]
+ CRUSH rule 0 x 639 [475]
+ CRUSH rule 0 x 640 [31]
+ CRUSH rule 0 x 641 [296]
+ CRUSH rule 0 x 642 [894]
+ CRUSH rule 0 x 643 [117]
+ CRUSH rule 0 x 644 [438]
+ CRUSH rule 0 x 645 [982]
+ CRUSH rule 0 x 646 [334]
+ CRUSH rule 0 x 647 [933]
+ CRUSH rule 0 x 648 [22]
+ CRUSH rule 0 x 649 [503]
+ CRUSH rule 0 x 650 [328]
+ CRUSH rule 0 x 651 [3]
+ CRUSH rule 0 x 652 [495]
+ CRUSH rule 0 x 653 [185]
+ CRUSH rule 0 x 654 [130]
+ CRUSH rule 0 x 655 [560]
+ CRUSH rule 0 x 656 [219]
+ CRUSH rule 0 x 657 [233]
+ CRUSH rule 0 x 658 [778]
+ CRUSH rule 0 x 659 [240]
+ CRUSH rule 0 x 660 [244]
+ CRUSH rule 0 x 661 [184]
+ CRUSH rule 0 x 662 [65]
+ CRUSH rule 0 x 663 [323]
+ CRUSH rule 0 x 664 [865]
+ CRUSH rule 0 x 665 [420]
+ CRUSH rule 0 x 666 [319]
+ CRUSH rule 0 x 667 [875]
+ CRUSH rule 0 x 668 [331]
+ CRUSH rule 0 x 669 [915]
+ CRUSH rule 0 x 670 [845]
+ CRUSH rule 0 x 671 [108]
+ CRUSH rule 0 x 672 [578]
+ CRUSH rule 0 x 673 [442]
+ CRUSH rule 0 x 674 [588]
+ CRUSH rule 0 x 675 [489]
+ CRUSH rule 0 x 676 [928]
+ CRUSH rule 0 x 677 [399]
+ CRUSH rule 0 x 678 [546]
+ CRUSH rule 0 x 679 [988]
+ CRUSH rule 0 x 680 [335]
+ CRUSH rule 0 x 681 [690]
+ CRUSH rule 0 x 682 [196]
+ CRUSH rule 0 x 683 [627]
+ CRUSH rule 0 x 684 [38]
+ CRUSH rule 0 x 685 [841]
+ CRUSH rule 0 x 686 [336]
+ CRUSH rule 0 x 687 [20]
+ CRUSH rule 0 x 688 [463]
+ CRUSH rule 0 x 689 [569]
+ CRUSH rule 0 x 690 [551]
+ CRUSH rule 0 x 691 [766]
+ CRUSH rule 0 x 692 [739]
+ CRUSH rule 0 x 693 [339]
+ CRUSH rule 0 x 694 [405]
+ CRUSH rule 0 x 695 [622]
+ CRUSH rule 0 x 696 [558]
+ CRUSH rule 0 x 697 [818]
+ CRUSH rule 0 x 698 [178]
+ CRUSH rule 0 x 699 [450]
+ CRUSH rule 0 x 700 [502]
+ CRUSH rule 0 x 701 [4]
+ CRUSH rule 0 x 702 [177]
+ CRUSH rule 0 x 703 [354]
+ CRUSH rule 0 x 704 [646]
+ CRUSH rule 0 x 705 [921]
+ CRUSH rule 0 x 706 [652]
+ CRUSH rule 0 x 707 [345]
+ CRUSH rule 0 x 708 [333]
+ CRUSH rule 0 x 709 [45]
+ CRUSH rule 0 x 710 [94]
+ CRUSH rule 0 x 711 [227]
+ CRUSH rule 0 x 712 [398]
+ CRUSH rule 0 x 713 [116]
+ CRUSH rule 0 x 714 [111]
+ CRUSH rule 0 x 715 [531]
+ CRUSH rule 0 x 716 [169]
+ CRUSH rule 0 x 717 [417]
+ CRUSH rule 0 x 718 [992]
+ CRUSH rule 0 x 719 [936]
+ CRUSH rule 0 x 720 [370]
+ CRUSH rule 0 x 721 [320]
+ CRUSH rule 0 x 722 [7]
+ CRUSH rule 0 x 723 [270]
+ CRUSH rule 0 x 724 [666]
+ CRUSH rule 0 x 725 [794]
+ CRUSH rule 0 x 726 [420]
+ CRUSH rule 0 x 727 [561]
+ CRUSH rule 0 x 728 [951]
+ CRUSH rule 0 x 729 [656]
+ CRUSH rule 0 x 730 [3]
+ CRUSH rule 0 x 731 [852]
+ CRUSH rule 0 x 732 [983]
+ CRUSH rule 0 x 733 [285]
+ CRUSH rule 0 x 734 [125]
+ CRUSH rule 0 x 735 [417]
+ CRUSH rule 0 x 736 [749]
+ CRUSH rule 0 x 737 [644]
+ CRUSH rule 0 x 738 [449]
+ CRUSH rule 0 x 739 [341]
+ CRUSH rule 0 x 740 [874]
+ CRUSH rule 0 x 741 [189]
+ CRUSH rule 0 x 742 [912]
+ CRUSH rule 0 x 743 [654]
+ CRUSH rule 0 x 744 [725]
+ CRUSH rule 0 x 745 [787]
+ CRUSH rule 0 x 746 [757]
+ CRUSH rule 0 x 747 [700]
+ CRUSH rule 0 x 748 [557]
+ CRUSH rule 0 x 749 [772]
+ CRUSH rule 0 x 750 [946]
+ CRUSH rule 0 x 751 [996]
+ CRUSH rule 0 x 752 [746]
+ CRUSH rule 0 x 753 [741]
+ CRUSH rule 0 x 754 [648]
+ CRUSH rule 0 x 755 [157]
+ CRUSH rule 0 x 756 [416]
+ CRUSH rule 0 x 757 [599]
+ CRUSH rule 0 x 758 [994]
+ CRUSH rule 0 x 759 [959]
+ CRUSH rule 0 x 760 [518]
+ CRUSH rule 0 x 761 [285]
+ CRUSH rule 0 x 762 [591]
+ CRUSH rule 0 x 763 [908]
+ CRUSH rule 0 x 764 [787]
+ CRUSH rule 0 x 765 [327]
+ CRUSH rule 0 x 766 [84]
+ CRUSH rule 0 x 767 [370]
+ CRUSH rule 0 x 768 [826]
+ CRUSH rule 0 x 769 [67]
+ CRUSH rule 0 x 770 [593]
+ CRUSH rule 0 x 771 [309]
+ CRUSH rule 0 x 772 [12]
+ CRUSH rule 0 x 773 [253]
+ CRUSH rule 0 x 774 [164]
+ CRUSH rule 0 x 775 [703]
+ CRUSH rule 0 x 776 [728]
+ CRUSH rule 0 x 777 [981]
+ CRUSH rule 0 x 778 [411]
+ CRUSH rule 0 x 779 [346]
+ CRUSH rule 0 x 780 [476]
+ CRUSH rule 0 x 781 [10]
+ CRUSH rule 0 x 782 [462]
+ CRUSH rule 0 x 783 [580]
+ CRUSH rule 0 x 784 [413]
+ CRUSH rule 0 x 785 [341]
+ CRUSH rule 0 x 786 [411]
+ CRUSH rule 0 x 787 [605]
+ CRUSH rule 0 x 788 [226]
+ CRUSH rule 0 x 789 [545]
+ CRUSH rule 0 x 790 [414]
+ CRUSH rule 0 x 791 [660]
+ CRUSH rule 0 x 792 [287]
+ CRUSH rule 0 x 793 [631]
+ CRUSH rule 0 x 794 [931]
+ CRUSH rule 0 x 795 [551]
+ CRUSH rule 0 x 796 [814]
+ CRUSH rule 0 x 797 [64]
+ CRUSH rule 0 x 798 [422]
+ CRUSH rule 0 x 799 [824]
+ CRUSH rule 0 x 800 [862]
+ CRUSH rule 0 x 801 [145]
+ CRUSH rule 0 x 802 [570]
+ CRUSH rule 0 x 803 [151]
+ CRUSH rule 0 x 804 [467]
+ CRUSH rule 0 x 805 [621]
+ CRUSH rule 0 x 806 [898]
+ CRUSH rule 0 x 807 [354]
+ CRUSH rule 0 x 808 [7]
+ CRUSH rule 0 x 809 [70]
+ CRUSH rule 0 x 810 [701]
+ CRUSH rule 0 x 811 [248]
+ CRUSH rule 0 x 812 [230]
+ CRUSH rule 0 x 813 [805]
+ CRUSH rule 0 x 814 [54]
+ CRUSH rule 0 x 815 [679]
+ CRUSH rule 0 x 816 [919]
+ CRUSH rule 0 x 817 [765]
+ CRUSH rule 0 x 818 [415]
+ CRUSH rule 0 x 819 [721]
+ CRUSH rule 0 x 820 [218]
+ CRUSH rule 0 x 821 [185]
+ CRUSH rule 0 x 822 [356]
+ CRUSH rule 0 x 823 [220]
+ CRUSH rule 0 x 824 [292]
+ CRUSH rule 0 x 825 [949]
+ CRUSH rule 0 x 826 [767]
+ CRUSH rule 0 x 827 [631]
+ CRUSH rule 0 x 828 [288]
+ CRUSH rule 0 x 829 [990]
+ CRUSH rule 0 x 830 [152]
+ CRUSH rule 0 x 831 [814]
+ CRUSH rule 0 x 832 [235]
+ CRUSH rule 0 x 833 [657]
+ CRUSH rule 0 x 834 [907]
+ CRUSH rule 0 x 835 [784]
+ CRUSH rule 0 x 836 [951]
+ CRUSH rule 0 x 837 [556]
+ CRUSH rule 0 x 838 [329]
+ CRUSH rule 0 x 839 [568]
+ CRUSH rule 0 x 840 [45]
+ CRUSH rule 0 x 841 [652]
+ CRUSH rule 0 x 842 [629]
+ CRUSH rule 0 x 843 [799]
+ CRUSH rule 0 x 844 [694]
+ CRUSH rule 0 x 845 [332]
+ CRUSH rule 0 x 846 [452]
+ CRUSH rule 0 x 847 [399]
+ CRUSH rule 0 x 848 [303]
+ CRUSH rule 0 x 849 [666]
+ CRUSH rule 0 x 850 [644]
+ CRUSH rule 0 x 851 [527]
+ CRUSH rule 0 x 852 [31]
+ CRUSH rule 0 x 853 [483]
+ CRUSH rule 0 x 854 [697]
+ CRUSH rule 0 x 855 [837]
+ CRUSH rule 0 x 856 [712]
+ CRUSH rule 0 x 857 [77]
+ CRUSH rule 0 x 858 [412]
+ CRUSH rule 0 x 859 [173]
+ CRUSH rule 0 x 860 [776]
+ CRUSH rule 0 x 861 [705]
+ CRUSH rule 0 x 862 [809]
+ CRUSH rule 0 x 863 [349]
+ CRUSH rule 0 x 864 [717]
+ CRUSH rule 0 x 865 [857]
+ CRUSH rule 0 x 866 [394]
+ CRUSH rule 0 x 867 [640]
+ CRUSH rule 0 x 868 [613]
+ CRUSH rule 0 x 869 [973]
+ CRUSH rule 0 x 870 [505]
+ CRUSH rule 0 x 871 [239]
+ CRUSH rule 0 x 872 [21]
+ CRUSH rule 0 x 873 [954]
+ CRUSH rule 0 x 874 [54]
+ CRUSH rule 0 x 875 [809]
+ CRUSH rule 0 x 876 [483]
+ CRUSH rule 0 x 877 [542]
+ CRUSH rule 0 x 878 [217]
+ CRUSH rule 0 x 879 [999]
+ CRUSH rule 0 x 880 [678]
+ CRUSH rule 0 x 881 [394]
+ CRUSH rule 0 x 882 [467]
+ CRUSH rule 0 x 883 [802]
+ CRUSH rule 0 x 884 [653]
+ CRUSH rule 0 x 885 [898]
+ CRUSH rule 0 x 886 [434]
+ CRUSH rule 0 x 887 [297]
+ CRUSH rule 0 x 888 [863]
+ CRUSH rule 0 x 889 [105]
+ CRUSH rule 0 x 890 [550]
+ CRUSH rule 0 x 891 [575]
+ CRUSH rule 0 x 892 [259]
+ CRUSH rule 0 x 893 [902]
+ CRUSH rule 0 x 894 [180]
+ CRUSH rule 0 x 895 [725]
+ CRUSH rule 0 x 896 [951]
+ CRUSH rule 0 x 897 [810]
+ CRUSH rule 0 x 898 [979]
+ CRUSH rule 0 x 899 [685]
+ CRUSH rule 0 x 900 [530]
+ CRUSH rule 0 x 901 [740]
+ CRUSH rule 0 x 902 [800]
+ CRUSH rule 0 x 903 [230]
+ CRUSH rule 0 x 904 [346]
+ CRUSH rule 0 x 905 [530]
+ CRUSH rule 0 x 906 [80]
+ CRUSH rule 0 x 907 [365]
+ CRUSH rule 0 x 908 [204]
+ CRUSH rule 0 x 909 [883]
+ CRUSH rule 0 x 910 [549]
+ CRUSH rule 0 x 911 [325]
+ CRUSH rule 0 x 912 [874]
+ CRUSH rule 0 x 913 [331]
+ CRUSH rule 0 x 914 [836]
+ CRUSH rule 0 x 915 [245]
+ CRUSH rule 0 x 916 [77]
+ CRUSH rule 0 x 917 [239]
+ CRUSH rule 0 x 918 [988]
+ CRUSH rule 0 x 919 [783]
+ CRUSH rule 0 x 920 [623]
+ CRUSH rule 0 x 921 [105]
+ CRUSH rule 0 x 922 [887]
+ CRUSH rule 0 x 923 [223]
+ CRUSH rule 0 x 924 [25]
+ CRUSH rule 0 x 925 [912]
+ CRUSH rule 0 x 926 [968]
+ CRUSH rule 0 x 927 [277]
+ CRUSH rule 0 x 928 [554]
+ CRUSH rule 0 x 929 [761]
+ CRUSH rule 0 x 930 [814]
+ CRUSH rule 0 x 931 [29]
+ CRUSH rule 0 x 932 [446]
+ CRUSH rule 0 x 933 [352]
+ CRUSH rule 0 x 934 [730]
+ CRUSH rule 0 x 935 [731]
+ CRUSH rule 0 x 936 [322]
+ CRUSH rule 0 x 937 [822]
+ CRUSH rule 0 x 938 [557]
+ CRUSH rule 0 x 939 [150]
+ CRUSH rule 0 x 940 [638]
+ CRUSH rule 0 x 941 [730]
+ CRUSH rule 0 x 942 [62]
+ CRUSH rule 0 x 943 [165]
+ CRUSH rule 0 x 944 [199]
+ CRUSH rule 0 x 945 [946]
+ CRUSH rule 0 x 946 [595]
+ CRUSH rule 0 x 947 [800]
+ CRUSH rule 0 x 948 [132]
+ CRUSH rule 0 x 949 [792]
+ CRUSH rule 0 x 950 [111]
+ CRUSH rule 0 x 951 [414]
+ CRUSH rule 0 x 952 [775]
+ CRUSH rule 0 x 953 [349]
+ CRUSH rule 0 x 954 [570]
+ CRUSH rule 0 x 955 [729]
+ CRUSH rule 0 x 956 [519]
+ CRUSH rule 0 x 957 [242]
+ CRUSH rule 0 x 958 [84]
+ CRUSH rule 0 x 959 [270]
+ CRUSH rule 0 x 960 [458]
+ CRUSH rule 0 x 961 [981]
+ CRUSH rule 0 x 962 [623]
+ CRUSH rule 0 x 963 [291]
+ CRUSH rule 0 x 964 [28]
+ CRUSH rule 0 x 965 [675]
+ CRUSH rule 0 x 966 [836]
+ CRUSH rule 0 x 967 [966]
+ CRUSH rule 0 x 968 [864]
+ CRUSH rule 0 x 969 [729]
+ CRUSH rule 0 x 970 [800]
+ CRUSH rule 0 x 971 [737]
+ CRUSH rule 0 x 972 [952]
+ CRUSH rule 0 x 973 [356]
+ CRUSH rule 0 x 974 [545]
+ CRUSH rule 0 x 975 [336]
+ CRUSH rule 0 x 976 [446]
+ CRUSH rule 0 x 977 [202]
+ CRUSH rule 0 x 978 [612]
+ CRUSH rule 0 x 979 [843]
+ CRUSH rule 0 x 980 [60]
+ CRUSH rule 0 x 981 [702]
+ CRUSH rule 0 x 982 [298]
+ CRUSH rule 0 x 983 [723]
+ CRUSH rule 0 x 984 [723]
+ CRUSH rule 0 x 985 [945]
+ CRUSH rule 0 x 986 [772]
+ CRUSH rule 0 x 987 [88]
+ CRUSH rule 0 x 988 [522]
+ CRUSH rule 0 x 989 [578]
+ CRUSH rule 0 x 990 [638]
+ CRUSH rule 0 x 991 [530]
+ CRUSH rule 0 x 992 [925]
+ CRUSH rule 0 x 993 [991]
+ CRUSH rule 0 x 994 [276]
+ CRUSH rule 0 x 995 [288]
+ CRUSH rule 0 x 996 [887]
+ CRUSH rule 0 x 997 [110]
+ CRUSH rule 0 x 998 [435]
+ CRUSH rule 0 x 999 [876]
+ CRUSH rule 0 x 1000 [178]
+ CRUSH rule 0 x 1001 [99]
+ CRUSH rule 0 x 1002 [515]
+ CRUSH rule 0 x 1003 [104]
+ CRUSH rule 0 x 1004 [269]
+ CRUSH rule 0 x 1005 [369]
+ CRUSH rule 0 x 1006 [40]
+ CRUSH rule 0 x 1007 [978]
+ CRUSH rule 0 x 1008 [965]
+ CRUSH rule 0 x 1009 [598]
+ CRUSH rule 0 x 1010 [767]
+ CRUSH rule 0 x 1011 [289]
+ CRUSH rule 0 x 1012 [128]
+ CRUSH rule 0 x 1013 [979]
+ CRUSH rule 0 x 1014 [979]
+ CRUSH rule 0 x 1015 [277]
+ CRUSH rule 0 x 1016 [262]
+ CRUSH rule 0 x 1017 [150]
+ CRUSH rule 0 x 1018 [555]
+ CRUSH rule 0 x 1019 [513]
+ CRUSH rule 0 x 1020 [158]
+ CRUSH rule 0 x 1021 [915]
+ CRUSH rule 0 x 1022 [967]
+ CRUSH rule 0 x 1023 [488]
+ rule 0 (data) num_rep 1 result size == 1:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705]
+ CRUSH rule 0 x 1 [876,250]
+ CRUSH rule 0 x 2 [292,832]
+ CRUSH rule 0 x 3 [623,387]
+ CRUSH rule 0 x 4 [61,334]
+ CRUSH rule 0 x 5 [946,557]
+ CRUSH rule 0 x 6 [576,668]
+ CRUSH rule 0 x 7 [645,753]
+ CRUSH rule 0 x 8 [243,6]
+ CRUSH rule 0 x 9 [22,578]
+ CRUSH rule 0 x 10 [758,828]
+ CRUSH rule 0 x 11 [769,120]
+ CRUSH rule 0 x 12 [780,364]
+ CRUSH rule 0 x 13 [557,18]
+ CRUSH rule 0 x 14 [59,561]
+ CRUSH rule 0 x 15 [718,928]
+ CRUSH rule 0 x 16 [673,632]
+ CRUSH rule 0 x 17 [648,43]
+ CRUSH rule 0 x 18 [654,219]
+ CRUSH rule 0 x 19 [850,545]
+ CRUSH rule 0 x 20 [717,785]
+ CRUSH rule 0 x 21 [420,57]
+ CRUSH rule 0 x 22 [503,998]
+ CRUSH rule 0 x 23 [411,663]
+ CRUSH rule 0 x 24 [266,861]
+ CRUSH rule 0 x 25 [760,483]
+ CRUSH rule 0 x 26 [903,24]
+ CRUSH rule 0 x 27 [946,188]
+ CRUSH rule 0 x 28 [69,312]
+ CRUSH rule 0 x 29 [844,883]
+ CRUSH rule 0 x 30 [621,18]
+ CRUSH rule 0 x 31 [784,943]
+ CRUSH rule 0 x 32 [173,374]
+ CRUSH rule 0 x 33 [698,336]
+ CRUSH rule 0 x 34 [168,836]
+ CRUSH rule 0 x 35 [274,509]
+ CRUSH rule 0 x 36 [318,215]
+ CRUSH rule 0 x 37 [173,604]
+ CRUSH rule 0 x 38 [708,444]
+ CRUSH rule 0 x 39 [662,198]
+ CRUSH rule 0 x 40 [620,801]
+ CRUSH rule 0 x 41 [811,264]
+ CRUSH rule 0 x 42 [863,179]
+ CRUSH rule 0 x 43 [686,822]
+ CRUSH rule 0 x 44 [396,222]
+ CRUSH rule 0 x 45 [991,694]
+ CRUSH rule 0 x 46 [420,909]
+ CRUSH rule 0 x 47 [467,211]
+ CRUSH rule 0 x 48 [955,329]
+ CRUSH rule 0 x 49 [974,891]
+ CRUSH rule 0 x 50 [870,441]
+ CRUSH rule 0 x 51 [182,930]
+ CRUSH rule 0 x 52 [704,812]
+ CRUSH rule 0 x 53 [185,713]
+ CRUSH rule 0 x 54 [270,441]
+ CRUSH rule 0 x 55 [895,734]
+ CRUSH rule 0 x 56 [564,963]
+ CRUSH rule 0 x 57 [738,130]
+ CRUSH rule 0 x 58 [524,113]
+ CRUSH rule 0 x 59 [408,337]
+ CRUSH rule 0 x 60 [228,790]
+ CRUSH rule 0 x 61 [154,843]
+ CRUSH rule 0 x 62 [594,811]
+ CRUSH rule 0 x 63 [646,67]
+ CRUSH rule 0 x 64 [175,542]
+ CRUSH rule 0 x 65 [745,619]
+ CRUSH rule 0 x 66 [275,468]
+ CRUSH rule 0 x 67 [246,958]
+ CRUSH rule 0 x 68 [711,473]
+ CRUSH rule 0 x 69 [493,924]
+ CRUSH rule 0 x 70 [30,499]
+ CRUSH rule 0 x 71 [984,883]
+ CRUSH rule 0 x 72 [71,286]
+ CRUSH rule 0 x 73 [922,618]
+ CRUSH rule 0 x 74 [629,414]
+ CRUSH rule 0 x 75 [222,20]
+ CRUSH rule 0 x 76 [262,366]
+ CRUSH rule 0 x 77 [638,469]
+ CRUSH rule 0 x 78 [324,511]
+ CRUSH rule 0 x 79 [577,990]
+ CRUSH rule 0 x 80 [501,95]
+ CRUSH rule 0 x 81 [506,812]
+ CRUSH rule 0 x 82 [222,145]
+ CRUSH rule 0 x 83 [71,634]
+ CRUSH rule 0 x 84 [49,761]
+ CRUSH rule 0 x 85 [985,896]
+ CRUSH rule 0 x 86 [537,745]
+ CRUSH rule 0 x 87 [997,317]
+ CRUSH rule 0 x 88 [957,350]
+ CRUSH rule 0 x 89 [399,730]
+ CRUSH rule 0 x 90 [943,706]
+ CRUSH rule 0 x 91 [22,368]
+ CRUSH rule 0 x 92 [532,424]
+ CRUSH rule 0 x 93 [218,489]
+ CRUSH rule 0 x 94 [181,96]
+ CRUSH rule 0 x 95 [343,957]
+ CRUSH rule 0 x 96 [861,270]
+ CRUSH rule 0 x 97 [459,706]
+ CRUSH rule 0 x 98 [327,867]
+ CRUSH rule 0 x 99 [974,133]
+ CRUSH rule 0 x 100 [32,445]
+ CRUSH rule 0 x 101 [142,90]
+ CRUSH rule 0 x 102 [172,129]
+ CRUSH rule 0 x 103 [630,47]
+ CRUSH rule 0 x 104 [758,133]
+ CRUSH rule 0 x 105 [843,604]
+ CRUSH rule 0 x 106 [28,681]
+ CRUSH rule 0 x 107 [74,320]
+ CRUSH rule 0 x 108 [875,593]
+ CRUSH rule 0 x 109 [411,985]
+ CRUSH rule 0 x 110 [440,774]
+ CRUSH rule 0 x 111 [405,742]
+ CRUSH rule 0 x 112 [143,181]
+ CRUSH rule 0 x 113 [153,846]
+ CRUSH rule 0 x 114 [804,892]
+ CRUSH rule 0 x 115 [588,508]
+ CRUSH rule 0 x 116 [327,148]
+ CRUSH rule 0 x 117 [95,594]
+ CRUSH rule 0 x 118 [80,957]
+ CRUSH rule 0 x 119 [386,932]
+ CRUSH rule 0 x 120 [366,312]
+ CRUSH rule 0 x 121 [129,154]
+ CRUSH rule 0 x 122 [873,1]
+ CRUSH rule 0 x 123 [533,415]
+ CRUSH rule 0 x 124 [461,691]
+ CRUSH rule 0 x 125 [342,599]
+ CRUSH rule 0 x 126 [819,781]
+ CRUSH rule 0 x 127 [437,893]
+ CRUSH rule 0 x 128 [679,994]
+ CRUSH rule 0 x 129 [380,685]
+ CRUSH rule 0 x 130 [992,52]
+ CRUSH rule 0 x 131 [469,90]
+ CRUSH rule 0 x 132 [571,250]
+ CRUSH rule 0 x 133 [964,728]
+ CRUSH rule 0 x 134 [999,19]
+ CRUSH rule 0 x 135 [634,101]
+ CRUSH rule 0 x 136 [114,889]
+ CRUSH rule 0 x 137 [839,8]
+ CRUSH rule 0 x 138 [967,949]
+ CRUSH rule 0 x 139 [308,711]
+ CRUSH rule 0 x 140 [764,936]
+ CRUSH rule 0 x 141 [423,302]
+ CRUSH rule 0 x 142 [252,821]
+ CRUSH rule 0 x 143 [33,808]
+ CRUSH rule 0 x 144 [472,88]
+ CRUSH rule 0 x 145 [242,208]
+ CRUSH rule 0 x 146 [290,70]
+ CRUSH rule 0 x 147 [447,352]
+ CRUSH rule 0 x 148 [212,644]
+ CRUSH rule 0 x 149 [9,775]
+ CRUSH rule 0 x 150 [166,456]
+ CRUSH rule 0 x 151 [811,875]
+ CRUSH rule 0 x 152 [449,617]
+ CRUSH rule 0 x 153 [523,537]
+ CRUSH rule 0 x 154 [208,559]
+ CRUSH rule 0 x 155 [569,325]
+ CRUSH rule 0 x 156 [488,121]
+ CRUSH rule 0 x 157 [140,723]
+ CRUSH rule 0 x 158 [786,451]
+ CRUSH rule 0 x 159 [134,664]
+ CRUSH rule 0 x 160 [690,112]
+ CRUSH rule 0 x 161 [324,912]
+ CRUSH rule 0 x 162 [748,567]
+ CRUSH rule 0 x 163 [575,499]
+ CRUSH rule 0 x 164 [314,489]
+ CRUSH rule 0 x 165 [116,209]
+ CRUSH rule 0 x 166 [352,706]
+ CRUSH rule 0 x 167 [27,743]
+ CRUSH rule 0 x 168 [953,898]
+ CRUSH rule 0 x 169 [912,147]
+ CRUSH rule 0 x 170 [421,515]
+ CRUSH rule 0 x 171 [488,584]
+ CRUSH rule 0 x 172 [366,443]
+ CRUSH rule 0 x 173 [863,291]
+ CRUSH rule 0 x 174 [263,555]
+ CRUSH rule 0 x 175 [875,961]
+ CRUSH rule 0 x 176 [745,83]
+ CRUSH rule 0 x 177 [128,244]
+ CRUSH rule 0 x 178 [155,41]
+ CRUSH rule 0 x 179 [593,833]
+ CRUSH rule 0 x 180 [154,734]
+ CRUSH rule 0 x 181 [289,675]
+ CRUSH rule 0 x 182 [730,931]
+ CRUSH rule 0 x 183 [639,237]
+ CRUSH rule 0 x 184 [704,312]
+ CRUSH rule 0 x 185 [97,100]
+ CRUSH rule 0 x 186 [26,665]
+ CRUSH rule 0 x 187 [649,14]
+ CRUSH rule 0 x 188 [682,695]
+ CRUSH rule 0 x 189 [325,693]
+ CRUSH rule 0 x 190 [399,933]
+ CRUSH rule 0 x 191 [629,533]
+ CRUSH rule 0 x 192 [503,578]
+ CRUSH rule 0 x 193 [546,333]
+ CRUSH rule 0 x 194 [242,473]
+ CRUSH rule 0 x 195 [625,719]
+ CRUSH rule 0 x 196 [357,114]
+ CRUSH rule 0 x 197 [306,954]
+ CRUSH rule 0 x 198 [863,791]
+ CRUSH rule 0 x 199 [935,906]
+ CRUSH rule 0 x 200 [373,774]
+ CRUSH rule 0 x 201 [659,320]
+ CRUSH rule 0 x 202 [260,433]
+ CRUSH rule 0 x 203 [36,239]
+ CRUSH rule 0 x 204 [92,516]
+ CRUSH rule 0 x 205 [68,395]
+ CRUSH rule 0 x 206 [570,530]
+ CRUSH rule 0 x 207 [834,457]
+ CRUSH rule 0 x 208 [927,484]
+ CRUSH rule 0 x 209 [878,66]
+ CRUSH rule 0 x 210 [572,981]
+ CRUSH rule 0 x 211 [107,597]
+ CRUSH rule 0 x 212 [389,107]
+ CRUSH rule 0 x 213 [497,717]
+ CRUSH rule 0 x 214 [798,65]
+ CRUSH rule 0 x 215 [233,419]
+ CRUSH rule 0 x 216 [494,464]
+ CRUSH rule 0 x 217 [352,396]
+ CRUSH rule 0 x 218 [895,864]
+ CRUSH rule 0 x 219 [222,534]
+ CRUSH rule 0 x 220 [281,19]
+ CRUSH rule 0 x 221 [64,928]
+ CRUSH rule 0 x 222 [40,544]
+ CRUSH rule 0 x 223 [645,556]
+ CRUSH rule 0 x 224 [647,165]
+ CRUSH rule 0 x 225 [219,714]
+ CRUSH rule 0 x 226 [372,511]
+ CRUSH rule 0 x 227 [925,156]
+ CRUSH rule 0 x 228 [682,404]
+ CRUSH rule 0 x 229 [880,838]
+ CRUSH rule 0 x 230 [328,659]
+ CRUSH rule 0 x 231 [320,383]
+ CRUSH rule 0 x 232 [924,846]
+ CRUSH rule 0 x 233 [948,652]
+ CRUSH rule 0 x 234 [484,943]
+ CRUSH rule 0 x 235 [750,65]
+ CRUSH rule 0 x 236 [551,787]
+ CRUSH rule 0 x 237 [390,157]
+ CRUSH rule 0 x 238 [570,6]
+ CRUSH rule 0 x 239 [729,959]
+ CRUSH rule 0 x 240 [981,241]
+ CRUSH rule 0 x 241 [310,816]
+ CRUSH rule 0 x 242 [161,63]
+ CRUSH rule 0 x 243 [180,394]
+ CRUSH rule 0 x 244 [52,174]
+ CRUSH rule 0 x 245 [523,121]
+ CRUSH rule 0 x 246 [362,893]
+ CRUSH rule 0 x 247 [382,184]
+ CRUSH rule 0 x 248 [129,114]
+ CRUSH rule 0 x 249 [159,683]
+ CRUSH rule 0 x 250 [404,945]
+ CRUSH rule 0 x 251 [661,225]
+ CRUSH rule 0 x 252 [961,226]
+ CRUSH rule 0 x 253 [651,97]
+ CRUSH rule 0 x 254 [123,33]
+ CRUSH rule 0 x 255 [314,649]
+ CRUSH rule 0 x 256 [315,215]
+ CRUSH rule 0 x 257 [825,264]
+ CRUSH rule 0 x 258 [624,789]
+ CRUSH rule 0 x 259 [602,542]
+ CRUSH rule 0 x 260 [717,878]
+ CRUSH rule 0 x 261 [145,517]
+ CRUSH rule 0 x 262 [223,1]
+ CRUSH rule 0 x 263 [462,211]
+ CRUSH rule 0 x 264 [654,471]
+ CRUSH rule 0 x 265 [302,794]
+ CRUSH rule 0 x 266 [202,132]
+ CRUSH rule 0 x 267 [282,938]
+ CRUSH rule 0 x 268 [338,309]
+ CRUSH rule 0 x 269 [738,122]
+ CRUSH rule 0 x 270 [707,982]
+ CRUSH rule 0 x 271 [705,432]
+ CRUSH rule 0 x 272 [756,545]
+ CRUSH rule 0 x 273 [197,502]
+ CRUSH rule 0 x 274 [992,44]
+ CRUSH rule 0 x 275 [544,789]
+ CRUSH rule 0 x 276 [658,467]
+ CRUSH rule 0 x 277 [143,490]
+ CRUSH rule 0 x 278 [492,647]
+ CRUSH rule 0 x 279 [517,792]
+ CRUSH rule 0 x 280 [825,740]
+ CRUSH rule 0 x 281 [224,629]
+ CRUSH rule 0 x 282 [298,661]
+ CRUSH rule 0 x 283 [311,606]
+ CRUSH rule 0 x 284 [771,466]
+ CRUSH rule 0 x 285 [693,362]
+ CRUSH rule 0 x 286 [364,477]
+ CRUSH rule 0 x 287 [591,611]
+ CRUSH rule 0 x 288 [965,541]
+ CRUSH rule 0 x 289 [225,551]
+ CRUSH rule 0 x 290 [577,762]
+ CRUSH rule 0 x 291 [160,903]
+ CRUSH rule 0 x 292 [873,598]
+ CRUSH rule 0 x 293 [100,234]
+ CRUSH rule 0 x 294 [285,943]
+ CRUSH rule 0 x 295 [938,262]
+ CRUSH rule 0 x 296 [850,327]
+ CRUSH rule 0 x 297 [951,53]
+ CRUSH rule 0 x 298 [173,336]
+ CRUSH rule 0 x 299 [598,591]
+ CRUSH rule 0 x 300 [531,957]
+ CRUSH rule 0 x 301 [823,628]
+ CRUSH rule 0 x 302 [184,80]
+ CRUSH rule 0 x 303 [521,766]
+ CRUSH rule 0 x 304 [980,127]
+ CRUSH rule 0 x 305 [153,816]
+ CRUSH rule 0 x 306 [423,739]
+ CRUSH rule 0 x 307 [997,557]
+ CRUSH rule 0 x 308 [991,874]
+ CRUSH rule 0 x 309 [860,394]
+ CRUSH rule 0 x 310 [589,818]
+ CRUSH rule 0 x 311 [477,774]
+ CRUSH rule 0 x 312 [887,853]
+ CRUSH rule 0 x 313 [802,646]
+ CRUSH rule 0 x 314 [654,974]
+ CRUSH rule 0 x 315 [767,227]
+ CRUSH rule 0 x 316 [778,83]
+ CRUSH rule 0 x 317 [184,418]
+ CRUSH rule 0 x 318 [525,410]
+ CRUSH rule 0 x 319 [476,724]
+ CRUSH rule 0 x 320 [149,610]
+ CRUSH rule 0 x 321 [710,79]
+ CRUSH rule 0 x 322 [175,275]
+ CRUSH rule 0 x 323 [819,604]
+ CRUSH rule 0 x 324 [16,745]
+ CRUSH rule 0 x 325 [486,400]
+ CRUSH rule 0 x 326 [613,765]
+ CRUSH rule 0 x 327 [125,289]
+ CRUSH rule 0 x 328 [807,383]
+ CRUSH rule 0 x 329 [588,938]
+ CRUSH rule 0 x 330 [932,644]
+ CRUSH rule 0 x 331 [341,953]
+ CRUSH rule 0 x 332 [153,726]
+ CRUSH rule 0 x 333 [745,845]
+ CRUSH rule 0 x 334 [614,751]
+ CRUSH rule 0 x 335 [518,721]
+ CRUSH rule 0 x 336 [389,424]
+ CRUSH rule 0 x 337 [753,508]
+ CRUSH rule 0 x 338 [128,810]
+ CRUSH rule 0 x 339 [430,308]
+ CRUSH rule 0 x 340 [541,44]
+ CRUSH rule 0 x 341 [402,26]
+ CRUSH rule 0 x 342 [982,57]
+ CRUSH rule 0 x 343 [833,412]
+ CRUSH rule 0 x 344 [784,533]
+ CRUSH rule 0 x 345 [546,300]
+ CRUSH rule 0 x 346 [302,420]
+ CRUSH rule 0 x 347 [488,778]
+ CRUSH rule 0 x 348 [903,744]
+ CRUSH rule 0 x 349 [471,547]
+ CRUSH rule 0 x 350 [348,221]
+ CRUSH rule 0 x 351 [961,582]
+ CRUSH rule 0 x 352 [728,137]
+ CRUSH rule 0 x 353 [904,202]
+ CRUSH rule 0 x 354 [345,226]
+ CRUSH rule 0 x 355 [50,430]
+ CRUSH rule 0 x 356 [87,185]
+ CRUSH rule 0 x 357 [762,459]
+ CRUSH rule 0 x 358 [908,25]
+ CRUSH rule 0 x 359 [484,15]
+ CRUSH rule 0 x 360 [173,378]
+ CRUSH rule 0 x 361 [404,577]
+ CRUSH rule 0 x 362 [403,1]
+ CRUSH rule 0 x 363 [639,911]
+ CRUSH rule 0 x 364 [752,689]
+ CRUSH rule 0 x 365 [956,999]
+ CRUSH rule 0 x 366 [860,925]
+ CRUSH rule 0 x 367 [205,609]
+ CRUSH rule 0 x 368 [301,284]
+ CRUSH rule 0 x 369 [452,658]
+ CRUSH rule 0 x 370 [11,467]
+ CRUSH rule 0 x 371 [124,487]
+ CRUSH rule 0 x 372 [253,48]
+ CRUSH rule 0 x 373 [715,605]
+ CRUSH rule 0 x 374 [191,887]
+ CRUSH rule 0 x 375 [711,385]
+ CRUSH rule 0 x 376 [597,818]
+ CRUSH rule 0 x 377 [294,256]
+ CRUSH rule 0 x 378 [34,151]
+ CRUSH rule 0 x 379 [869,136]
+ CRUSH rule 0 x 380 [294,97]
+ CRUSH rule 0 x 381 [119,710]
+ CRUSH rule 0 x 382 [69,631]
+ CRUSH rule 0 x 383 [922,588]
+ CRUSH rule 0 x 384 [221,945]
+ CRUSH rule 0 x 385 [561,737]
+ CRUSH rule 0 x 386 [335,442]
+ CRUSH rule 0 x 387 [514,43]
+ CRUSH rule 0 x 388 [587,89]
+ CRUSH rule 0 x 389 [109,641]
+ CRUSH rule 0 x 390 [925,149]
+ CRUSH rule 0 x 391 [267,87]
+ CRUSH rule 0 x 392 [382,485]
+ CRUSH rule 0 x 393 [425,721]
+ CRUSH rule 0 x 394 [898,18]
+ CRUSH rule 0 x 395 [806,876]
+ CRUSH rule 0 x 396 [790,970]
+ CRUSH rule 0 x 397 [136,363]
+ CRUSH rule 0 x 398 [914,116]
+ CRUSH rule 0 x 399 [261,94]
+ CRUSH rule 0 x 400 [661,197]
+ CRUSH rule 0 x 401 [953,979]
+ CRUSH rule 0 x 402 [738,819]
+ CRUSH rule 0 x 403 [573,238]
+ CRUSH rule 0 x 404 [526,848]
+ CRUSH rule 0 x 405 [582,505]
+ CRUSH rule 0 x 406 [768,324]
+ CRUSH rule 0 x 407 [260,951]
+ CRUSH rule 0 x 408 [657,81]
+ CRUSH rule 0 x 409 [498,89]
+ CRUSH rule 0 x 410 [28,793]
+ CRUSH rule 0 x 411 [684,992]
+ CRUSH rule 0 x 412 [261,958]
+ CRUSH rule 0 x 413 [891,835]
+ CRUSH rule 0 x 414 [127,459]
+ CRUSH rule 0 x 415 [272,540]
+ CRUSH rule 0 x 416 [739,617]
+ CRUSH rule 0 x 417 [106,209]
+ CRUSH rule 0 x 418 [525,441]
+ CRUSH rule 0 x 419 [603,673]
+ CRUSH rule 0 x 420 [988,213]
+ CRUSH rule 0 x 421 [761,521]
+ CRUSH rule 0 x 422 [317,160]
+ CRUSH rule 0 x 423 [137,807]
+ CRUSH rule 0 x 424 [920,37]
+ CRUSH rule 0 x 425 [277,693]
+ CRUSH rule 0 x 426 [485,936]
+ CRUSH rule 0 x 427 [242,515]
+ CRUSH rule 0 x 428 [632,635]
+ CRUSH rule 0 x 429 [641,73]
+ CRUSH rule 0 x 430 [626,585]
+ CRUSH rule 0 x 431 [697,76]
+ CRUSH rule 0 x 432 [590,526]
+ CRUSH rule 0 x 433 [284,387]
+ CRUSH rule 0 x 434 [538,985]
+ CRUSH rule 0 x 435 [30,318]
+ CRUSH rule 0 x 436 [164,919]
+ CRUSH rule 0 x 437 [322,212]
+ CRUSH rule 0 x 438 [142,392]
+ CRUSH rule 0 x 439 [119,370]
+ CRUSH rule 0 x 440 [333,403]
+ CRUSH rule 0 x 441 [477,727]
+ CRUSH rule 0 x 442 [274,590]
+ CRUSH rule 0 x 443 [983,748]
+ CRUSH rule 0 x 444 [536,509]
+ CRUSH rule 0 x 445 [485,528]
+ CRUSH rule 0 x 446 [345,634]
+ CRUSH rule 0 x 447 [61,845]
+ CRUSH rule 0 x 448 [333,232]
+ CRUSH rule 0 x 449 [680,16]
+ CRUSH rule 0 x 450 [235,214]
+ CRUSH rule 0 x 451 [961,468]
+ CRUSH rule 0 x 452 [525,479]
+ CRUSH rule 0 x 453 [138,466]
+ CRUSH rule 0 x 454 [137,625]
+ CRUSH rule 0 x 455 [173,150]
+ CRUSH rule 0 x 456 [235,226]
+ CRUSH rule 0 x 457 [450,577]
+ CRUSH rule 0 x 458 [195,537]
+ CRUSH rule 0 x 459 [381,555]
+ CRUSH rule 0 x 460 [972,730]
+ CRUSH rule 0 x 461 [506,279]
+ CRUSH rule 0 x 462 [692,959]
+ CRUSH rule 0 x 463 [788,667]
+ CRUSH rule 0 x 464 [133,122]
+ CRUSH rule 0 x 465 [971,190]
+ CRUSH rule 0 x 466 [394,576]
+ CRUSH rule 0 x 467 [517,28]
+ CRUSH rule 0 x 468 [829,143]
+ CRUSH rule 0 x 469 [987,936]
+ CRUSH rule 0 x 470 [107,982]
+ CRUSH rule 0 x 471 [181,897]
+ CRUSH rule 0 x 472 [547,512]
+ CRUSH rule 0 x 473 [760,997]
+ CRUSH rule 0 x 474 [787,418]
+ CRUSH rule 0 x 475 [662,312]
+ CRUSH rule 0 x 476 [110,495]
+ CRUSH rule 0 x 477 [393,954]
+ CRUSH rule 0 x 478 [246,483]
+ CRUSH rule 0 x 479 [70,929]
+ CRUSH rule 0 x 480 [753,119]
+ CRUSH rule 0 x 481 [470,429]
+ CRUSH rule 0 x 482 [451,566]
+ CRUSH rule 0 x 483 [816,72]
+ CRUSH rule 0 x 484 [540,454]
+ CRUSH rule 0 x 485 [74,582]
+ CRUSH rule 0 x 486 [958,595]
+ CRUSH rule 0 x 487 [228,302]
+ CRUSH rule 0 x 488 [180,529]
+ CRUSH rule 0 x 489 [47,617]
+ CRUSH rule 0 x 490 [905,822]
+ CRUSH rule 0 x 491 [892,370]
+ CRUSH rule 0 x 492 [588,959]
+ CRUSH rule 0 x 493 [353,461]
+ CRUSH rule 0 x 494 [378,848]
+ CRUSH rule 0 x 495 [845,653]
+ CRUSH rule 0 x 496 [13,988]
+ CRUSH rule 0 x 497 [796,877]
+ CRUSH rule 0 x 498 [412,337]
+ CRUSH rule 0 x 499 [330,695]
+ CRUSH rule 0 x 500 [820,272]
+ CRUSH rule 0 x 501 [110,44]
+ CRUSH rule 0 x 502 [336,595]
+ CRUSH rule 0 x 503 [922,211]
+ CRUSH rule 0 x 504 [483,52]
+ CRUSH rule 0 x 505 [482,598]
+ CRUSH rule 0 x 506 [493,123]
+ CRUSH rule 0 x 507 [12,598]
+ CRUSH rule 0 x 508 [227,157]
+ CRUSH rule 0 x 509 [807,242]
+ CRUSH rule 0 x 510 [134,437]
+ CRUSH rule 0 x 511 [212,54]
+ CRUSH rule 0 x 512 [236,630]
+ CRUSH rule 0 x 513 [994,693]
+ CRUSH rule 0 x 514 [45,508]
+ CRUSH rule 0 x 515 [504,138]
+ CRUSH rule 0 x 516 [285,409]
+ CRUSH rule 0 x 517 [300,232]
+ CRUSH rule 0 x 518 [397,674]
+ CRUSH rule 0 x 519 [86,750]
+ CRUSH rule 0 x 520 [900,833]
+ CRUSH rule 0 x 521 [31,47]
+ CRUSH rule 0 x 522 [390,16]
+ CRUSH rule 0 x 523 [618,308]
+ CRUSH rule 0 x 524 [635,189]
+ CRUSH rule 0 x 525 [311,916]
+ CRUSH rule 0 x 526 [48,738]
+ CRUSH rule 0 x 527 [202,851]
+ CRUSH rule 0 x 528 [565,827]
+ CRUSH rule 0 x 529 [934,864]
+ CRUSH rule 0 x 530 [502,934]
+ CRUSH rule 0 x 531 [681,627]
+ CRUSH rule 0 x 532 [422,6]
+ CRUSH rule 0 x 533 [863,68]
+ CRUSH rule 0 x 534 [962,931]
+ CRUSH rule 0 x 535 [89,565]
+ CRUSH rule 0 x 536 [499,351]
+ CRUSH rule 0 x 537 [676,547]
+ CRUSH rule 0 x 538 [58,644]
+ CRUSH rule 0 x 539 [837,953]
+ CRUSH rule 0 x 540 [831,50]
+ CRUSH rule 0 x 541 [582,757]
+ CRUSH rule 0 x 542 [472,132]
+ CRUSH rule 0 x 543 [382,272]
+ CRUSH rule 0 x 544 [947,930]
+ CRUSH rule 0 x 545 [425,570]
+ CRUSH rule 0 x 546 [18,65]
+ CRUSH rule 0 x 547 [445,715]
+ CRUSH rule 0 x 548 [367,569]
+ CRUSH rule 0 x 549 [125,715]
+ CRUSH rule 0 x 550 [425,599]
+ CRUSH rule 0 x 551 [44,1]
+ CRUSH rule 0 x 552 [246,104]
+ CRUSH rule 0 x 553 [71,703]
+ CRUSH rule 0 x 554 [207,124]
+ CRUSH rule 0 x 555 [570,28]
+ CRUSH rule 0 x 556 [674,152]
+ CRUSH rule 0 x 557 [347,817]
+ CRUSH rule 0 x 558 [627,426]
+ CRUSH rule 0 x 559 [940,630]
+ CRUSH rule 0 x 560 [295,903]
+ CRUSH rule 0 x 561 [506,682]
+ CRUSH rule 0 x 562 [718,529]
+ CRUSH rule 0 x 563 [552,332]
+ CRUSH rule 0 x 564 [835,769]
+ CRUSH rule 0 x 565 [8,167]
+ CRUSH rule 0 x 566 [600,481]
+ CRUSH rule 0 x 567 [999,994]
+ CRUSH rule 0 x 568 [252,431]
+ CRUSH rule 0 x 569 [643,218]
+ CRUSH rule 0 x 570 [617,635]
+ CRUSH rule 0 x 571 [757,80]
+ CRUSH rule 0 x 572 [299,348]
+ CRUSH rule 0 x 573 [25,505]
+ CRUSH rule 0 x 574 [215,431]
+ CRUSH rule 0 x 575 [225,252]
+ CRUSH rule 0 x 576 [627,94]
+ CRUSH rule 0 x 577 [237,809]
+ CRUSH rule 0 x 578 [885,313]
+ CRUSH rule 0 x 579 [924,575]
+ CRUSH rule 0 x 580 [718,51]
+ CRUSH rule 0 x 581 [219,807]
+ CRUSH rule 0 x 582 [893,701]
+ CRUSH rule 0 x 583 [246,930]
+ CRUSH rule 0 x 584 [336,432]
+ CRUSH rule 0 x 585 [324,999]
+ CRUSH rule 0 x 586 [558,230]
+ CRUSH rule 0 x 587 [985,830]
+ CRUSH rule 0 x 588 [211,544]
+ CRUSH rule 0 x 589 [129,21]
+ CRUSH rule 0 x 590 [467,969]
+ CRUSH rule 0 x 591 [758,514]
+ CRUSH rule 0 x 592 [525,253]
+ CRUSH rule 0 x 593 [601,885]
+ CRUSH rule 0 x 594 [227,60]
+ CRUSH rule 0 x 595 [720,854]
+ CRUSH rule 0 x 596 [751,195]
+ CRUSH rule 0 x 597 [129,574]
+ CRUSH rule 0 x 598 [679,207]
+ CRUSH rule 0 x 599 [668,315]
+ CRUSH rule 0 x 600 [143,396]
+ CRUSH rule 0 x 601 [326,573]
+ CRUSH rule 0 x 602 [860,281]
+ CRUSH rule 0 x 603 [709,328]
+ CRUSH rule 0 x 604 [571,62]
+ CRUSH rule 0 x 605 [252,739]
+ CRUSH rule 0 x 606 [339,236]
+ CRUSH rule 0 x 607 [590,248]
+ CRUSH rule 0 x 608 [145,635]
+ CRUSH rule 0 x 609 [973,547]
+ CRUSH rule 0 x 610 [435,816]
+ CRUSH rule 0 x 611 [559,283]
+ CRUSH rule 0 x 612 [273,149]
+ CRUSH rule 0 x 613 [828,614]
+ CRUSH rule 0 x 614 [478,748]
+ CRUSH rule 0 x 615 [392,155]
+ CRUSH rule 0 x 616 [778,637]
+ CRUSH rule 0 x 617 [622,713]
+ CRUSH rule 0 x 618 [149,877]
+ CRUSH rule 0 x 619 [604,163]
+ CRUSH rule 0 x 620 [181,23]
+ CRUSH rule 0 x 621 [735,902]
+ CRUSH rule 0 x 622 [661,824]
+ CRUSH rule 0 x 623 [142,121]
+ CRUSH rule 0 x 624 [360,716]
+ CRUSH rule 0 x 625 [541,167]
+ CRUSH rule 0 x 626 [364,431]
+ CRUSH rule 0 x 627 [458,137]
+ CRUSH rule 0 x 628 [250,350]
+ CRUSH rule 0 x 629 [928,160]
+ CRUSH rule 0 x 630 [243,19]
+ CRUSH rule 0 x 631 [438,221]
+ CRUSH rule 0 x 632 [797,368]
+ CRUSH rule 0 x 633 [993,749]
+ CRUSH rule 0 x 634 [239,351]
+ CRUSH rule 0 x 635 [640,965]
+ CRUSH rule 0 x 636 [173,290]
+ CRUSH rule 0 x 637 [0,918]
+ CRUSH rule 0 x 638 [702,235]
+ CRUSH rule 0 x 639 [475,687]
+ CRUSH rule 0 x 640 [31,664]
+ CRUSH rule 0 x 641 [296,473]
+ CRUSH rule 0 x 642 [894,273]
+ CRUSH rule 0 x 643 [117,111]
+ CRUSH rule 0 x 644 [438,336]
+ CRUSH rule 0 x 645 [982,702]
+ CRUSH rule 0 x 646 [334,804]
+ CRUSH rule 0 x 647 [933,787]
+ CRUSH rule 0 x 648 [22,444]
+ CRUSH rule 0 x 649 [503,229]
+ CRUSH rule 0 x 650 [328,659]
+ CRUSH rule 0 x 651 [3,880]
+ CRUSH rule 0 x 652 [495,977]
+ CRUSH rule 0 x 653 [185,718]
+ CRUSH rule 0 x 654 [130,528]
+ CRUSH rule 0 x 655 [560,872]
+ CRUSH rule 0 x 656 [219,885]
+ CRUSH rule 0 x 657 [233,684]
+ CRUSH rule 0 x 658 [778,6]
+ CRUSH rule 0 x 659 [240,663]
+ CRUSH rule 0 x 660 [244,855]
+ CRUSH rule 0 x 661 [184,270]
+ CRUSH rule 0 x 662 [65,883]
+ CRUSH rule 0 x 663 [323,721]
+ CRUSH rule 0 x 664 [865,113]
+ CRUSH rule 0 x 665 [420,850]
+ CRUSH rule 0 x 666 [319,767]
+ CRUSH rule 0 x 667 [875,39]
+ CRUSH rule 0 x 668 [331,122]
+ CRUSH rule 0 x 669 [915,521]
+ CRUSH rule 0 x 670 [845,659]
+ CRUSH rule 0 x 671 [108,634]
+ CRUSH rule 0 x 672 [578,216]
+ CRUSH rule 0 x 673 [442,74]
+ CRUSH rule 0 x 674 [588,364]
+ CRUSH rule 0 x 675 [489,698]
+ CRUSH rule 0 x 676 [928,911]
+ CRUSH rule 0 x 677 [399,269]
+ CRUSH rule 0 x 678 [546,752]
+ CRUSH rule 0 x 679 [988,25]
+ CRUSH rule 0 x 680 [335,963]
+ CRUSH rule 0 x 681 [690,462]
+ CRUSH rule 0 x 682 [196,588]
+ CRUSH rule 0 x 683 [627,25]
+ CRUSH rule 0 x 684 [38,804]
+ CRUSH rule 0 x 685 [841,368]
+ CRUSH rule 0 x 686 [336,287]
+ CRUSH rule 0 x 687 [20,682]
+ CRUSH rule 0 x 688 [463,371]
+ CRUSH rule 0 x 689 [569,250]
+ CRUSH rule 0 x 690 [551,144]
+ CRUSH rule 0 x 691 [766,464]
+ CRUSH rule 0 x 692 [739,634]
+ CRUSH rule 0 x 693 [339,297]
+ CRUSH rule 0 x 694 [405,26]
+ CRUSH rule 0 x 695 [622,576]
+ CRUSH rule 0 x 696 [558,902]
+ CRUSH rule 0 x 697 [818,222]
+ CRUSH rule 0 x 698 [178,48]
+ CRUSH rule 0 x 699 [450,244]
+ CRUSH rule 0 x 700 [502,771]
+ CRUSH rule 0 x 701 [4,612]
+ CRUSH rule 0 x 702 [177,630]
+ CRUSH rule 0 x 703 [354,178]
+ CRUSH rule 0 x 704 [646,601]
+ CRUSH rule 0 x 705 [921,401]
+ CRUSH rule 0 x 706 [652,877]
+ CRUSH rule 0 x 707 [345,745]
+ CRUSH rule 0 x 708 [333,607]
+ CRUSH rule 0 x 709 [45,187]
+ CRUSH rule 0 x 710 [94,855]
+ CRUSH rule 0 x 711 [227,653]
+ CRUSH rule 0 x 712 [398,953]
+ CRUSH rule 0 x 713 [116,800]
+ CRUSH rule 0 x 714 [111,629]
+ CRUSH rule 0 x 715 [531,291]
+ CRUSH rule 0 x 716 [169,541]
+ CRUSH rule 0 x 717 [417,446]
+ CRUSH rule 0 x 718 [992,383]
+ CRUSH rule 0 x 719 [936,674]
+ CRUSH rule 0 x 720 [370,188]
+ CRUSH rule 0 x 721 [320,859]
+ CRUSH rule 0 x 722 [7,2]
+ CRUSH rule 0 x 723 [270,553]
+ CRUSH rule 0 x 724 [666,822]
+ CRUSH rule 0 x 725 [794,406]
+ CRUSH rule 0 x 726 [420,556]
+ CRUSH rule 0 x 727 [561,461]
+ CRUSH rule 0 x 728 [951,330]
+ CRUSH rule 0 x 729 [656,644]
+ CRUSH rule 0 x 730 [3,558]
+ CRUSH rule 0 x 731 [852,89]
+ CRUSH rule 0 x 732 [983,840]
+ CRUSH rule 0 x 733 [285,396]
+ CRUSH rule 0 x 734 [125,510]
+ CRUSH rule 0 x 735 [417,773]
+ CRUSH rule 0 x 736 [749,396]
+ CRUSH rule 0 x 737 [644,991]
+ CRUSH rule 0 x 738 [449,683]
+ CRUSH rule 0 x 739 [341,220]
+ CRUSH rule 0 x 740 [874,524]
+ CRUSH rule 0 x 741 [189,472]
+ CRUSH rule 0 x 742 [912,581]
+ CRUSH rule 0 x 743 [654,914]
+ CRUSH rule 0 x 744 [725,295]
+ CRUSH rule 0 x 745 [787,858]
+ CRUSH rule 0 x 746 [757,848]
+ CRUSH rule 0 x 747 [700,81]
+ CRUSH rule 0 x 748 [557,436]
+ CRUSH rule 0 x 749 [772,622]
+ CRUSH rule 0 x 750 [946,97]
+ CRUSH rule 0 x 751 [996,618]
+ CRUSH rule 0 x 752 [746,887]
+ CRUSH rule 0 x 753 [741,14]
+ CRUSH rule 0 x 754 [648,349]
+ CRUSH rule 0 x 755 [157,460]
+ CRUSH rule 0 x 756 [416,97]
+ CRUSH rule 0 x 757 [599,839]
+ CRUSH rule 0 x 758 [994,218]
+ CRUSH rule 0 x 759 [959,682]
+ CRUSH rule 0 x 760 [518,943]
+ CRUSH rule 0 x 761 [285,849]
+ CRUSH rule 0 x 762 [591,313]
+ CRUSH rule 0 x 763 [908,411]
+ CRUSH rule 0 x 764 [787,234]
+ CRUSH rule 0 x 765 [327,921]
+ CRUSH rule 0 x 766 [84,161]
+ CRUSH rule 0 x 767 [370,895]
+ CRUSH rule 0 x 768 [826,760]
+ CRUSH rule 0 x 769 [67,768]
+ CRUSH rule 0 x 770 [593,909]
+ CRUSH rule 0 x 771 [309,935]
+ CRUSH rule 0 x 772 [12,125]
+ CRUSH rule 0 x 773 [253,466]
+ CRUSH rule 0 x 774 [164,390]
+ CRUSH rule 0 x 775 [703,47]
+ CRUSH rule 0 x 776 [728,231]
+ CRUSH rule 0 x 777 [981,621]
+ CRUSH rule 0 x 778 [411,456]
+ CRUSH rule 0 x 779 [346,121]
+ CRUSH rule 0 x 780 [476,39]
+ CRUSH rule 0 x 781 [10,130]
+ CRUSH rule 0 x 782 [462,246]
+ CRUSH rule 0 x 783 [580,373]
+ CRUSH rule 0 x 784 [413,113]
+ CRUSH rule 0 x 785 [341,856]
+ CRUSH rule 0 x 786 [411,140]
+ CRUSH rule 0 x 787 [605,522]
+ CRUSH rule 0 x 788 [226,545]
+ CRUSH rule 0 x 789 [545,320]
+ CRUSH rule 0 x 790 [414,748]
+ CRUSH rule 0 x 791 [660,906]
+ CRUSH rule 0 x 792 [287,392]
+ CRUSH rule 0 x 793 [631,133]
+ CRUSH rule 0 x 794 [931,517]
+ CRUSH rule 0 x 795 [551,962]
+ CRUSH rule 0 x 796 [814,4]
+ CRUSH rule 0 x 797 [64,201]
+ CRUSH rule 0 x 798 [422,530]
+ CRUSH rule 0 x 799 [824,32]
+ CRUSH rule 0 x 800 [862,623]
+ CRUSH rule 0 x 801 [145,550]
+ CRUSH rule 0 x 802 [570,19]
+ CRUSH rule 0 x 803 [151,812]
+ CRUSH rule 0 x 804 [467,93]
+ CRUSH rule 0 x 805 [621,223]
+ CRUSH rule 0 x 806 [898,957]
+ CRUSH rule 0 x 807 [354,531]
+ CRUSH rule 0 x 808 [7,96]
+ CRUSH rule 0 x 809 [70,734]
+ CRUSH rule 0 x 810 [701,18]
+ CRUSH rule 0 x 811 [248,547]
+ CRUSH rule 0 x 812 [230,576]
+ CRUSH rule 0 x 813 [805,114]
+ CRUSH rule 0 x 814 [54,619]
+ CRUSH rule 0 x 815 [679,412]
+ CRUSH rule 0 x 816 [919,448]
+ CRUSH rule 0 x 817 [765,830]
+ CRUSH rule 0 x 818 [415,566]
+ CRUSH rule 0 x 819 [721,319]
+ CRUSH rule 0 x 820 [218,301]
+ CRUSH rule 0 x 821 [185,795]
+ CRUSH rule 0 x 822 [356,261]
+ CRUSH rule 0 x 823 [220,281]
+ CRUSH rule 0 x 824 [292,809]
+ CRUSH rule 0 x 825 [949,778]
+ CRUSH rule 0 x 826 [767,818]
+ CRUSH rule 0 x 827 [631,83]
+ CRUSH rule 0 x 828 [288,986]
+ CRUSH rule 0 x 829 [990,667]
+ CRUSH rule 0 x 830 [152,571]
+ CRUSH rule 0 x 831 [814,563]
+ CRUSH rule 0 x 832 [235,641]
+ CRUSH rule 0 x 833 [657,565]
+ CRUSH rule 0 x 834 [907,231]
+ CRUSH rule 0 x 835 [784,262]
+ CRUSH rule 0 x 836 [951,158]
+ CRUSH rule 0 x 837 [556,498]
+ CRUSH rule 0 x 838 [329,274]
+ CRUSH rule 0 x 839 [568,209]
+ CRUSH rule 0 x 840 [45,579]
+ CRUSH rule 0 x 841 [652,702]
+ CRUSH rule 0 x 842 [629,984]
+ CRUSH rule 0 x 843 [799,690]
+ CRUSH rule 0 x 844 [694,600]
+ CRUSH rule 0 x 845 [332,30]
+ CRUSH rule 0 x 846 [452,251]
+ CRUSH rule 0 x 847 [399,681]
+ CRUSH rule 0 x 848 [303,138]
+ CRUSH rule 0 x 849 [666,346]
+ CRUSH rule 0 x 850 [644,511]
+ CRUSH rule 0 x 851 [527,546]
+ CRUSH rule 0 x 852 [31,809]
+ CRUSH rule 0 x 853 [483,330]
+ CRUSH rule 0 x 854 [697,953]
+ CRUSH rule 0 x 855 [837,996]
+ CRUSH rule 0 x 856 [712,40]
+ CRUSH rule 0 x 857 [77,984]
+ CRUSH rule 0 x 858 [412,384]
+ CRUSH rule 0 x 859 [173,760]
+ CRUSH rule 0 x 860 [776,429]
+ CRUSH rule 0 x 861 [705,405]
+ CRUSH rule 0 x 862 [809,44]
+ CRUSH rule 0 x 863 [349,496]
+ CRUSH rule 0 x 864 [717,858]
+ CRUSH rule 0 x 865 [857,603]
+ CRUSH rule 0 x 866 [394,304]
+ CRUSH rule 0 x 867 [640,773]
+ CRUSH rule 0 x 868 [613,950]
+ CRUSH rule 0 x 869 [973,889]
+ CRUSH rule 0 x 870 [505,35]
+ CRUSH rule 0 x 871 [239,264]
+ CRUSH rule 0 x 872 [21,767]
+ CRUSH rule 0 x 873 [954,666]
+ CRUSH rule 0 x 874 [54,510]
+ CRUSH rule 0 x 875 [809,418]
+ CRUSH rule 0 x 876 [483,457]
+ CRUSH rule 0 x 877 [542,531]
+ CRUSH rule 0 x 878 [217,674]
+ CRUSH rule 0 x 879 [999,475]
+ CRUSH rule 0 x 880 [678,573]
+ CRUSH rule 0 x 881 [394,835]
+ CRUSH rule 0 x 882 [467,382]
+ CRUSH rule 0 x 883 [802,744]
+ CRUSH rule 0 x 884 [653,660]
+ CRUSH rule 0 x 885 [898,704]
+ CRUSH rule 0 x 886 [434,357]
+ CRUSH rule 0 x 887 [297,226]
+ CRUSH rule 0 x 888 [863,324]
+ CRUSH rule 0 x 889 [105,102]
+ CRUSH rule 0 x 890 [550,248]
+ CRUSH rule 0 x 891 [575,928]
+ CRUSH rule 0 x 892 [259,862]
+ CRUSH rule 0 x 893 [902,880]
+ CRUSH rule 0 x 894 [180,169]
+ CRUSH rule 0 x 895 [725,849]
+ CRUSH rule 0 x 896 [951,34]
+ CRUSH rule 0 x 897 [810,352]
+ CRUSH rule 0 x 898 [979,433]
+ CRUSH rule 0 x 899 [685,668]
+ CRUSH rule 0 x 900 [530,978]
+ CRUSH rule 0 x 901 [740,107]
+ CRUSH rule 0 x 902 [800,743]
+ CRUSH rule 0 x 903 [230,267]
+ CRUSH rule 0 x 904 [346,949]
+ CRUSH rule 0 x 905 [530,397]
+ CRUSH rule 0 x 906 [80,426]
+ CRUSH rule 0 x 907 [365,968]
+ CRUSH rule 0 x 908 [204,832]
+ CRUSH rule 0 x 909 [883,989]
+ CRUSH rule 0 x 910 [549,593]
+ CRUSH rule 0 x 911 [325,847]
+ CRUSH rule 0 x 912 [874,888]
+ CRUSH rule 0 x 913 [331,463]
+ CRUSH rule 0 x 914 [836,468]
+ CRUSH rule 0 x 915 [245,228]
+ CRUSH rule 0 x 916 [77,967]
+ CRUSH rule 0 x 917 [239,60]
+ CRUSH rule 0 x 918 [988,115]
+ CRUSH rule 0 x 919 [783,139]
+ CRUSH rule 0 x 920 [623,408]
+ CRUSH rule 0 x 921 [105,799]
+ CRUSH rule 0 x 922 [887,505]
+ CRUSH rule 0 x 923 [223,318]
+ CRUSH rule 0 x 924 [25,778]
+ CRUSH rule 0 x 925 [912,601]
+ CRUSH rule 0 x 926 [968,133]
+ CRUSH rule 0 x 927 [277,724]
+ CRUSH rule 0 x 928 [554,203]
+ CRUSH rule 0 x 929 [761,802]
+ CRUSH rule 0 x 930 [814,61]
+ CRUSH rule 0 x 931 [29,193]
+ CRUSH rule 0 x 932 [446,198]
+ CRUSH rule 0 x 933 [352,742]
+ CRUSH rule 0 x 934 [730,2]
+ CRUSH rule 0 x 935 [731,23]
+ CRUSH rule 0 x 936 [322,975]
+ CRUSH rule 0 x 937 [822,221]
+ CRUSH rule 0 x 938 [557,850]
+ CRUSH rule 0 x 939 [150,11]
+ CRUSH rule 0 x 940 [638,398]
+ CRUSH rule 0 x 941 [730,342]
+ CRUSH rule 0 x 942 [62,292]
+ CRUSH rule 0 x 943 [165,314]
+ CRUSH rule 0 x 944 [199,625]
+ CRUSH rule 0 x 945 [946,999]
+ CRUSH rule 0 x 946 [595,93]
+ CRUSH rule 0 x 947 [800,582]
+ CRUSH rule 0 x 948 [132,551]
+ CRUSH rule 0 x 949 [792,920]
+ CRUSH rule 0 x 950 [111,345]
+ CRUSH rule 0 x 951 [414,619]
+ CRUSH rule 0 x 952 [775,469]
+ CRUSH rule 0 x 953 [349,1]
+ CRUSH rule 0 x 954 [570,940]
+ CRUSH rule 0 x 955 [729,774]
+ CRUSH rule 0 x 956 [519,141]
+ CRUSH rule 0 x 957 [242,709]
+ CRUSH rule 0 x 958 [84,217]
+ CRUSH rule 0 x 959 [270,413]
+ CRUSH rule 0 x 960 [458,192]
+ CRUSH rule 0 x 961 [981,388]
+ CRUSH rule 0 x 962 [623,834]
+ CRUSH rule 0 x 963 [291,167]
+ CRUSH rule 0 x 964 [28,156]
+ CRUSH rule 0 x 965 [675,557]
+ CRUSH rule 0 x 966 [836,306]
+ CRUSH rule 0 x 967 [966,386]
+ CRUSH rule 0 x 968 [864,756]
+ CRUSH rule 0 x 969 [729,625]
+ CRUSH rule 0 x 970 [800,362]
+ CRUSH rule 0 x 971 [737,381]
+ CRUSH rule 0 x 972 [952,245]
+ CRUSH rule 0 x 973 [356,455]
+ CRUSH rule 0 x 974 [545,758]
+ CRUSH rule 0 x 975 [336,191]
+ CRUSH rule 0 x 976 [446,208]
+ CRUSH rule 0 x 977 [202,896]
+ CRUSH rule 0 x 978 [612,324]
+ CRUSH rule 0 x 979 [843,457]
+ CRUSH rule 0 x 980 [60,914]
+ CRUSH rule 0 x 981 [702,749]
+ CRUSH rule 0 x 982 [298,928]
+ CRUSH rule 0 x 983 [723,572]
+ CRUSH rule 0 x 984 [723,864]
+ CRUSH rule 0 x 985 [945,459]
+ CRUSH rule 0 x 986 [772,664]
+ CRUSH rule 0 x 987 [88,324]
+ CRUSH rule 0 x 988 [522,927]
+ CRUSH rule 0 x 989 [578,332]
+ CRUSH rule 0 x 990 [638,228]
+ CRUSH rule 0 x 991 [530,221]
+ CRUSH rule 0 x 992 [925,705]
+ CRUSH rule 0 x 993 [991,301]
+ CRUSH rule 0 x 994 [276,51]
+ CRUSH rule 0 x 995 [288,836]
+ CRUSH rule 0 x 996 [887,983]
+ CRUSH rule 0 x 997 [110,924]
+ CRUSH rule 0 x 998 [435,830]
+ CRUSH rule 0 x 999 [876,738]
+ CRUSH rule 0 x 1000 [178,963]
+ CRUSH rule 0 x 1001 [99,519]
+ CRUSH rule 0 x 1002 [515,534]
+ CRUSH rule 0 x 1003 [104,611]
+ CRUSH rule 0 x 1004 [269,638]
+ CRUSH rule 0 x 1005 [369,223]
+ CRUSH rule 0 x 1006 [40,107]
+ CRUSH rule 0 x 1007 [978,111]
+ CRUSH rule 0 x 1008 [965,956]
+ CRUSH rule 0 x 1009 [598,476]
+ CRUSH rule 0 x 1010 [767,523]
+ CRUSH rule 0 x 1011 [289,871]
+ CRUSH rule 0 x 1012 [128,28]
+ CRUSH rule 0 x 1013 [979,765]
+ CRUSH rule 0 x 1014 [979,948]
+ CRUSH rule 0 x 1015 [277,790]
+ CRUSH rule 0 x 1016 [262,73]
+ CRUSH rule 0 x 1017 [150,269]
+ CRUSH rule 0 x 1018 [555,829]
+ CRUSH rule 0 x 1019 [513,356]
+ CRUSH rule 0 x 1020 [158,161]
+ CRUSH rule 0 x 1021 [915,998]
+ CRUSH rule 0 x 1022 [967,829]
+ CRUSH rule 0 x 1023 [488,257]
+ rule 0 (data) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536]
+ CRUSH rule 0 x 1 [876,250,334]
+ CRUSH rule 0 x 2 [292,832,53]
+ CRUSH rule 0 x 3 [623,387,124]
+ CRUSH rule 0 x 4 [61,334,710]
+ CRUSH rule 0 x 5 [946,557,713]
+ CRUSH rule 0 x 6 [576,668,212]
+ CRUSH rule 0 x 7 [645,753,906]
+ CRUSH rule 0 x 8 [243,6,863]
+ CRUSH rule 0 x 9 [22,578,251]
+ CRUSH rule 0 x 10 [758,828,360]
+ CRUSH rule 0 x 11 [769,120,124]
+ CRUSH rule 0 x 12 [780,364,689]
+ CRUSH rule 0 x 13 [557,18,351]
+ CRUSH rule 0 x 14 [59,561,249]
+ CRUSH rule 0 x 15 [718,928,993]
+ CRUSH rule 0 x 16 [673,632,841]
+ CRUSH rule 0 x 17 [648,43,560]
+ CRUSH rule 0 x 18 [654,219,181]
+ CRUSH rule 0 x 19 [850,545,377]
+ CRUSH rule 0 x 20 [717,785,974]
+ CRUSH rule 0 x 21 [420,57,519]
+ CRUSH rule 0 x 22 [503,998,193]
+ CRUSH rule 0 x 23 [411,663,168]
+ CRUSH rule 0 x 24 [266,861,353]
+ CRUSH rule 0 x 25 [760,483,818]
+ CRUSH rule 0 x 26 [903,24,573]
+ CRUSH rule 0 x 27 [946,188,289]
+ CRUSH rule 0 x 28 [69,312,73]
+ CRUSH rule 0 x 29 [844,883,337]
+ CRUSH rule 0 x 30 [621,18,613]
+ CRUSH rule 0 x 31 [784,943,814]
+ CRUSH rule 0 x 32 [173,374,369]
+ CRUSH rule 0 x 33 [698,336,357]
+ CRUSH rule 0 x 34 [168,836,210]
+ CRUSH rule 0 x 35 [274,509,534]
+ CRUSH rule 0 x 36 [318,215,153]
+ CRUSH rule 0 x 37 [173,604,109]
+ CRUSH rule 0 x 38 [708,444,683]
+ CRUSH rule 0 x 39 [662,198,417]
+ CRUSH rule 0 x 40 [620,801,414]
+ CRUSH rule 0 x 41 [811,264,177]
+ CRUSH rule 0 x 42 [863,179,527]
+ CRUSH rule 0 x 43 [686,822,988]
+ CRUSH rule 0 x 44 [396,222,46]
+ CRUSH rule 0 x 45 [991,694,253]
+ CRUSH rule 0 x 46 [420,909,184]
+ CRUSH rule 0 x 47 [467,211,605]
+ CRUSH rule 0 x 48 [955,329,368]
+ CRUSH rule 0 x 49 [974,891,931]
+ CRUSH rule 0 x 50 [870,441,691]
+ CRUSH rule 0 x 51 [182,930,25]
+ CRUSH rule 0 x 52 [704,812,894]
+ CRUSH rule 0 x 53 [185,713,631]
+ CRUSH rule 0 x 54 [270,441,100]
+ CRUSH rule 0 x 55 [895,734,958]
+ CRUSH rule 0 x 56 [564,963,683]
+ CRUSH rule 0 x 57 [738,130,208]
+ CRUSH rule 0 x 58 [524,113,806]
+ CRUSH rule 0 x 59 [408,337,668]
+ CRUSH rule 0 x 60 [228,790,857]
+ CRUSH rule 0 x 61 [154,843,717]
+ CRUSH rule 0 x 62 [594,811,549]
+ CRUSH rule 0 x 63 [646,67,884]
+ CRUSH rule 0 x 64 [175,542,155]
+ CRUSH rule 0 x 65 [745,619,131]
+ CRUSH rule 0 x 66 [275,468,23]
+ CRUSH rule 0 x 67 [246,958,524]
+ CRUSH rule 0 x 68 [711,473,403]
+ CRUSH rule 0 x 69 [493,924,850]
+ CRUSH rule 0 x 70 [30,499,644]
+ CRUSH rule 0 x 71 [984,883,574]
+ CRUSH rule 0 x 72 [71,286,942]
+ CRUSH rule 0 x 73 [922,618,3]
+ CRUSH rule 0 x 74 [629,414,185]
+ CRUSH rule 0 x 75 [222,20,174]
+ CRUSH rule 0 x 76 [262,366,339]
+ CRUSH rule 0 x 77 [638,469,992]
+ CRUSH rule 0 x 78 [324,511,788]
+ CRUSH rule 0 x 79 [577,990,64]
+ CRUSH rule 0 x 80 [501,95,278]
+ CRUSH rule 0 x 81 [506,812,9]
+ CRUSH rule 0 x 82 [222,145,80]
+ CRUSH rule 0 x 83 [71,634,61]
+ CRUSH rule 0 x 84 [49,761,773]
+ CRUSH rule 0 x 85 [985,896,708]
+ CRUSH rule 0 x 86 [537,745,93]
+ CRUSH rule 0 x 87 [997,317,463]
+ CRUSH rule 0 x 88 [957,350,890]
+ CRUSH rule 0 x 89 [399,730,148]
+ CRUSH rule 0 x 90 [943,706,683]
+ CRUSH rule 0 x 91 [22,368,149]
+ CRUSH rule 0 x 92 [532,424,426]
+ CRUSH rule 0 x 93 [218,489,405]
+ CRUSH rule 0 x 94 [181,96,102]
+ CRUSH rule 0 x 95 [343,957,820]
+ CRUSH rule 0 x 96 [861,270,87]
+ CRUSH rule 0 x 97 [459,706,45]
+ CRUSH rule 0 x 98 [327,867,353]
+ CRUSH rule 0 x 99 [974,133,468]
+ CRUSH rule 0 x 100 [32,445,547]
+ CRUSH rule 0 x 101 [142,90,337]
+ CRUSH rule 0 x 102 [172,129,139]
+ CRUSH rule 0 x 103 [630,47,161]
+ CRUSH rule 0 x 104 [758,133,278]
+ CRUSH rule 0 x 105 [843,604,47]
+ CRUSH rule 0 x 106 [28,681,193]
+ CRUSH rule 0 x 107 [74,320,85]
+ CRUSH rule 0 x 108 [875,593,575]
+ CRUSH rule 0 x 109 [411,985,811]
+ CRUSH rule 0 x 110 [440,774,799]
+ CRUSH rule 0 x 111 [405,742,276]
+ CRUSH rule 0 x 112 [143,181,922]
+ CRUSH rule 0 x 113 [153,846,160]
+ CRUSH rule 0 x 114 [804,892,939]
+ CRUSH rule 0 x 115 [588,508,958]
+ CRUSH rule 0 x 116 [327,148,637]
+ CRUSH rule 0 x 117 [95,594,989]
+ CRUSH rule 0 x 118 [80,957,897]
+ CRUSH rule 0 x 119 [386,932,951]
+ CRUSH rule 0 x 120 [366,312,653]
+ CRUSH rule 0 x 121 [129,154,847]
+ CRUSH rule 0 x 122 [873,1,110]
+ CRUSH rule 0 x 123 [533,415,789]
+ CRUSH rule 0 x 124 [461,691,898]
+ CRUSH rule 0 x 125 [342,599,830]
+ CRUSH rule 0 x 126 [819,781,822]
+ CRUSH rule 0 x 127 [437,893,585]
+ CRUSH rule 0 x 128 [679,994,982]
+ CRUSH rule 0 x 129 [380,685,947]
+ CRUSH rule 0 x 130 [992,52,466]
+ CRUSH rule 0 x 131 [469,90,208]
+ CRUSH rule 0 x 132 [571,250,316]
+ CRUSH rule 0 x 133 [964,728,329]
+ CRUSH rule 0 x 134 [999,19,716]
+ CRUSH rule 0 x 135 [634,101,52]
+ CRUSH rule 0 x 136 [114,889,692]
+ CRUSH rule 0 x 137 [839,8,959]
+ CRUSH rule 0 x 138 [967,949,138]
+ CRUSH rule 0 x 139 [308,711,736]
+ CRUSH rule 0 x 140 [764,936,926]
+ CRUSH rule 0 x 141 [423,302,112]
+ CRUSH rule 0 x 142 [252,821,715]
+ CRUSH rule 0 x 143 [33,808,518]
+ CRUSH rule 0 x 144 [472,88,969]
+ CRUSH rule 0 x 145 [242,208,252]
+ CRUSH rule 0 x 146 [290,70,570]
+ CRUSH rule 0 x 147 [447,352,657]
+ CRUSH rule 0 x 148 [212,644,432]
+ CRUSH rule 0 x 149 [9,775,87]
+ CRUSH rule 0 x 150 [166,456,582]
+ CRUSH rule 0 x 151 [811,875,307]
+ CRUSH rule 0 x 152 [449,617,223]
+ CRUSH rule 0 x 153 [523,537,695]
+ CRUSH rule 0 x 154 [208,559,874]
+ CRUSH rule 0 x 155 [569,325,192]
+ CRUSH rule 0 x 156 [488,121,521]
+ CRUSH rule 0 x 157 [140,723,633]
+ CRUSH rule 0 x 158 [786,451,320]
+ CRUSH rule 0 x 159 [134,664,517]
+ CRUSH rule 0 x 160 [690,112,414]
+ CRUSH rule 0 x 161 [324,912,397]
+ CRUSH rule 0 x 162 [748,567,284]
+ CRUSH rule 0 x 163 [575,499,31]
+ CRUSH rule 0 x 164 [314,489,308]
+ CRUSH rule 0 x 165 [116,209,750]
+ CRUSH rule 0 x 166 [352,706,701]
+ CRUSH rule 0 x 167 [27,743,174]
+ CRUSH rule 0 x 168 [953,898,880]
+ CRUSH rule 0 x 169 [912,147,266]
+ CRUSH rule 0 x 170 [421,515,828]
+ CRUSH rule 0 x 171 [488,584,880]
+ CRUSH rule 0 x 172 [366,443,957]
+ CRUSH rule 0 x 173 [863,291,625]
+ CRUSH rule 0 x 174 [263,555,650]
+ CRUSH rule 0 x 175 [875,961,361]
+ CRUSH rule 0 x 176 [745,83,701]
+ CRUSH rule 0 x 177 [128,244,41]
+ CRUSH rule 0 x 178 [155,41,264]
+ CRUSH rule 0 x 179 [593,833,202]
+ CRUSH rule 0 x 180 [154,734,17]
+ CRUSH rule 0 x 181 [289,675,723]
+ CRUSH rule 0 x 182 [730,931,560]
+ CRUSH rule 0 x 183 [639,237,794]
+ CRUSH rule 0 x 184 [704,312,685]
+ CRUSH rule 0 x 185 [97,100,762]
+ CRUSH rule 0 x 186 [26,665,554]
+ CRUSH rule 0 x 187 [649,14,740]
+ CRUSH rule 0 x 188 [682,695,590]
+ CRUSH rule 0 x 189 [325,693,726]
+ CRUSH rule 0 x 190 [399,933,136]
+ CRUSH rule 0 x 191 [629,533,17]
+ CRUSH rule 0 x 192 [503,578,38]
+ CRUSH rule 0 x 193 [546,333,651]
+ CRUSH rule 0 x 194 [242,473,58]
+ CRUSH rule 0 x 195 [625,719,135]
+ CRUSH rule 0 x 196 [357,114,125]
+ CRUSH rule 0 x 197 [306,954,453]
+ CRUSH rule 0 x 198 [863,791,311]
+ CRUSH rule 0 x 199 [935,906,929]
+ CRUSH rule 0 x 200 [373,774,229]
+ CRUSH rule 0 x 201 [659,320,477]
+ CRUSH rule 0 x 202 [260,433,524]
+ CRUSH rule 0 x 203 [36,239,675]
+ CRUSH rule 0 x 204 [92,516,993]
+ CRUSH rule 0 x 205 [68,395,473]
+ CRUSH rule 0 x 206 [570,530,642]
+ CRUSH rule 0 x 207 [834,457,850]
+ CRUSH rule 0 x 208 [927,484,640]
+ CRUSH rule 0 x 209 [878,66,58]
+ CRUSH rule 0 x 210 [572,981,484]
+ CRUSH rule 0 x 211 [107,597,780]
+ CRUSH rule 0 x 212 [389,107,838]
+ CRUSH rule 0 x 213 [497,717,567]
+ CRUSH rule 0 x 214 [798,65,254]
+ CRUSH rule 0 x 215 [233,419,283]
+ CRUSH rule 0 x 216 [494,464,742]
+ CRUSH rule 0 x 217 [352,396,309]
+ CRUSH rule 0 x 218 [895,864,988]
+ CRUSH rule 0 x 219 [222,534,277]
+ CRUSH rule 0 x 220 [281,19,584]
+ CRUSH rule 0 x 221 [64,928,963]
+ CRUSH rule 0 x 222 [40,544,161]
+ CRUSH rule 0 x 223 [645,556,159]
+ CRUSH rule 0 x 224 [647,165,957]
+ CRUSH rule 0 x 225 [219,714,858]
+ CRUSH rule 0 x 226 [372,511,181]
+ CRUSH rule 0 x 227 [925,156,714]
+ CRUSH rule 0 x 228 [682,404,839]
+ CRUSH rule 0 x 229 [880,838,770]
+ CRUSH rule 0 x 230 [328,659,916]
+ CRUSH rule 0 x 231 [320,383,669]
+ CRUSH rule 0 x 232 [924,846,394]
+ CRUSH rule 0 x 233 [948,652,575]
+ CRUSH rule 0 x 234 [484,943,42]
+ CRUSH rule 0 x 235 [750,65,590]
+ CRUSH rule 0 x 236 [551,787,490]
+ CRUSH rule 0 x 237 [390,157,166]
+ CRUSH rule 0 x 238 [570,6,989]
+ CRUSH rule 0 x 239 [729,959,376]
+ CRUSH rule 0 x 240 [981,241,156]
+ CRUSH rule 0 x 241 [310,816,641]
+ CRUSH rule 0 x 242 [161,63,642]
+ CRUSH rule 0 x 243 [180,394,33]
+ CRUSH rule 0 x 244 [52,174,685]
+ CRUSH rule 0 x 245 [523,121,915]
+ CRUSH rule 0 x 246 [362,893,390]
+ CRUSH rule 0 x 247 [382,184,116]
+ CRUSH rule 0 x 248 [129,114,852]
+ CRUSH rule 0 x 249 [159,683,91]
+ CRUSH rule 0 x 250 [404,945,569]
+ CRUSH rule 0 x 251 [661,225,738]
+ CRUSH rule 0 x 252 [961,226,542]
+ CRUSH rule 0 x 253 [651,97,225]
+ CRUSH rule 0 x 254 [123,33,741]
+ CRUSH rule 0 x 255 [314,649,891]
+ CRUSH rule 0 x 256 [315,215,651]
+ CRUSH rule 0 x 257 [825,264,867]
+ CRUSH rule 0 x 258 [624,789,370]
+ CRUSH rule 0 x 259 [602,542,70]
+ CRUSH rule 0 x 260 [717,878,43]
+ CRUSH rule 0 x 261 [145,517,20]
+ CRUSH rule 0 x 262 [223,1,561]
+ CRUSH rule 0 x 263 [462,211,405]
+ CRUSH rule 0 x 264 [654,471,266]
+ CRUSH rule 0 x 265 [302,794,704]
+ CRUSH rule 0 x 266 [202,132,884]
+ CRUSH rule 0 x 267 [282,938,657]
+ CRUSH rule 0 x 268 [338,309,356]
+ CRUSH rule 0 x 269 [738,122,266]
+ CRUSH rule 0 x 270 [707,982,946]
+ CRUSH rule 0 x 271 [705,432,364]
+ CRUSH rule 0 x 272 [756,545,942]
+ CRUSH rule 0 x 273 [197,502,527]
+ CRUSH rule 0 x 274 [992,44,653]
+ CRUSH rule 0 x 275 [544,789,170]
+ CRUSH rule 0 x 276 [658,467,577]
+ CRUSH rule 0 x 277 [143,490,880]
+ CRUSH rule 0 x 278 [492,647,355]
+ CRUSH rule 0 x 279 [517,792,604]
+ CRUSH rule 0 x 280 [825,740,27]
+ CRUSH rule 0 x 281 [224,629,120]
+ CRUSH rule 0 x 282 [298,661,380]
+ CRUSH rule 0 x 283 [311,606,208]
+ CRUSH rule 0 x 284 [771,466,371]
+ CRUSH rule 0 x 285 [693,362,404]
+ CRUSH rule 0 x 286 [364,477,285]
+ CRUSH rule 0 x 287 [591,611,828]
+ CRUSH rule 0 x 288 [965,541,848]
+ CRUSH rule 0 x 289 [225,551,948]
+ CRUSH rule 0 x 290 [577,762,777]
+ CRUSH rule 0 x 291 [160,903,477]
+ CRUSH rule 0 x 292 [873,598,216]
+ CRUSH rule 0 x 293 [100,234,874]
+ CRUSH rule 0 x 294 [285,943,379]
+ CRUSH rule 0 x 295 [938,262,880]
+ CRUSH rule 0 x 296 [850,327,86]
+ CRUSH rule 0 x 297 [951,53,99]
+ CRUSH rule 0 x 298 [173,336,85]
+ CRUSH rule 0 x 299 [598,591,315]
+ CRUSH rule 0 x 300 [531,957,62]
+ CRUSH rule 0 x 301 [823,628,23]
+ CRUSH rule 0 x 302 [184,80,780]
+ CRUSH rule 0 x 303 [521,766,222]
+ CRUSH rule 0 x 304 [980,127,807]
+ CRUSH rule 0 x 305 [153,816,22]
+ CRUSH rule 0 x 306 [423,739,664]
+ CRUSH rule 0 x 307 [997,557,682]
+ CRUSH rule 0 x 308 [991,874,534]
+ CRUSH rule 0 x 309 [860,394,724]
+ CRUSH rule 0 x 310 [589,818,546]
+ CRUSH rule 0 x 311 [477,774,225]
+ CRUSH rule 0 x 312 [887,853,950]
+ CRUSH rule 0 x 313 [802,646,447]
+ CRUSH rule 0 x 314 [654,974,229]
+ CRUSH rule 0 x 315 [767,227,28]
+ CRUSH rule 0 x 316 [778,83,733]
+ CRUSH rule 0 x 317 [184,418,642]
+ CRUSH rule 0 x 318 [525,410,500]
+ CRUSH rule 0 x 319 [476,724,569]
+ CRUSH rule 0 x 320 [149,610,697]
+ CRUSH rule 0 x 321 [710,79,667]
+ CRUSH rule 0 x 322 [175,275,323]
+ CRUSH rule 0 x 323 [819,604,638]
+ CRUSH rule 0 x 324 [16,745,511]
+ CRUSH rule 0 x 325 [486,400,872]
+ CRUSH rule 0 x 326 [613,765,207]
+ CRUSH rule 0 x 327 [125,289,738]
+ CRUSH rule 0 x 328 [807,383,476]
+ CRUSH rule 0 x 329 [588,938,599]
+ CRUSH rule 0 x 330 [932,644,41]
+ CRUSH rule 0 x 331 [341,953,950]
+ CRUSH rule 0 x 332 [153,726,459]
+ CRUSH rule 0 x 333 [745,845,853]
+ CRUSH rule 0 x 334 [614,751,807]
+ CRUSH rule 0 x 335 [518,721,221]
+ CRUSH rule 0 x 336 [389,424,77]
+ CRUSH rule 0 x 337 [753,508,765]
+ CRUSH rule 0 x 338 [128,810,490]
+ CRUSH rule 0 x 339 [430,308,58]
+ CRUSH rule 0 x 340 [541,44,630]
+ CRUSH rule 0 x 341 [402,26,631]
+ CRUSH rule 0 x 342 [982,57,992]
+ CRUSH rule 0 x 343 [833,412,572]
+ CRUSH rule 0 x 344 [784,533,792]
+ CRUSH rule 0 x 345 [546,300,304]
+ CRUSH rule 0 x 346 [302,420,428]
+ CRUSH rule 0 x 347 [488,778,101]
+ CRUSH rule 0 x 348 [903,744,937]
+ CRUSH rule 0 x 349 [471,547,582]
+ CRUSH rule 0 x 350 [348,221,823]
+ CRUSH rule 0 x 351 [961,582,705]
+ CRUSH rule 0 x 352 [728,137,461]
+ CRUSH rule 0 x 353 [904,202,184]
+ CRUSH rule 0 x 354 [345,226,319]
+ CRUSH rule 0 x 355 [50,430,175]
+ CRUSH rule 0 x 356 [87,185,55]
+ CRUSH rule 0 x 357 [762,459,921]
+ CRUSH rule 0 x 358 [908,25,280]
+ CRUSH rule 0 x 359 [484,15,132]
+ CRUSH rule 0 x 360 [173,378,337]
+ CRUSH rule 0 x 361 [404,577,115]
+ CRUSH rule 0 x 362 [403,1,422]
+ CRUSH rule 0 x 363 [639,911,510]
+ CRUSH rule 0 x 364 [752,689,610]
+ CRUSH rule 0 x 365 [956,999,212]
+ CRUSH rule 0 x 366 [860,925,924]
+ CRUSH rule 0 x 367 [205,609,647]
+ CRUSH rule 0 x 368 [301,284,810]
+ CRUSH rule 0 x 369 [452,658,339]
+ CRUSH rule 0 x 370 [11,467,695]
+ CRUSH rule 0 x 371 [124,487,55]
+ CRUSH rule 0 x 372 [253,48,979]
+ CRUSH rule 0 x 373 [715,605,775]
+ CRUSH rule 0 x 374 [191,887,920]
+ CRUSH rule 0 x 375 [711,385,651]
+ CRUSH rule 0 x 376 [597,818,49]
+ CRUSH rule 0 x 377 [294,256,933]
+ CRUSH rule 0 x 378 [34,151,681]
+ CRUSH rule 0 x 379 [869,136,315]
+ CRUSH rule 0 x 380 [294,97,575]
+ CRUSH rule 0 x 381 [119,710,219]
+ CRUSH rule 0 x 382 [69,631,508]
+ CRUSH rule 0 x 383 [922,588,589]
+ CRUSH rule 0 x 384 [221,945,671]
+ CRUSH rule 0 x 385 [561,737,953]
+ CRUSH rule 0 x 386 [335,442,788]
+ CRUSH rule 0 x 387 [514,43,353]
+ CRUSH rule 0 x 388 [587,89,157]
+ CRUSH rule 0 x 389 [109,641,255]
+ CRUSH rule 0 x 390 [925,149,421]
+ CRUSH rule 0 x 391 [267,87,387]
+ CRUSH rule 0 x 392 [382,485,370]
+ CRUSH rule 0 x 393 [425,721,221]
+ CRUSH rule 0 x 394 [898,18,38]
+ CRUSH rule 0 x 395 [806,876,269]
+ CRUSH rule 0 x 396 [790,970,437]
+ CRUSH rule 0 x 397 [136,363,507]
+ CRUSH rule 0 x 398 [914,116,558]
+ CRUSH rule 0 x 399 [261,94,299]
+ CRUSH rule 0 x 400 [661,197,338]
+ CRUSH rule 0 x 401 [953,979,287]
+ CRUSH rule 0 x 402 [738,819,618]
+ CRUSH rule 0 x 403 [573,238,425]
+ CRUSH rule 0 x 404 [526,848,790]
+ CRUSH rule 0 x 405 [582,505,330]
+ CRUSH rule 0 x 406 [768,324,493]
+ CRUSH rule 0 x 407 [260,951,437]
+ CRUSH rule 0 x 408 [657,81,770]
+ CRUSH rule 0 x 409 [498,89,182]
+ CRUSH rule 0 x 410 [28,793,737]
+ CRUSH rule 0 x 411 [684,992,60]
+ CRUSH rule 0 x 412 [261,958,699]
+ CRUSH rule 0 x 413 [891,835,297]
+ CRUSH rule 0 x 414 [127,459,119]
+ CRUSH rule 0 x 415 [272,540,631]
+ CRUSH rule 0 x 416 [739,617,115]
+ CRUSH rule 0 x 417 [106,209,157]
+ CRUSH rule 0 x 418 [525,441,147]
+ CRUSH rule 0 x 419 [603,673,615]
+ CRUSH rule 0 x 420 [988,213,251]
+ CRUSH rule 0 x 421 [761,521,748]
+ CRUSH rule 0 x 422 [317,160,924]
+ CRUSH rule 0 x 423 [137,807,168]
+ CRUSH rule 0 x 424 [920,37,146]
+ CRUSH rule 0 x 425 [277,693,285]
+ CRUSH rule 0 x 426 [485,936,407]
+ CRUSH rule 0 x 427 [242,515,9]
+ CRUSH rule 0 x 428 [632,635,26]
+ CRUSH rule 0 x 429 [641,73,465]
+ CRUSH rule 0 x 430 [626,585,6]
+ CRUSH rule 0 x 431 [697,76,753]
+ CRUSH rule 0 x 432 [590,526,306]
+ CRUSH rule 0 x 433 [284,387,149]
+ CRUSH rule 0 x 434 [538,985,79]
+ CRUSH rule 0 x 435 [30,318,593]
+ CRUSH rule 0 x 436 [164,919,851]
+ CRUSH rule 0 x 437 [322,212,163]
+ CRUSH rule 0 x 438 [142,392,85]
+ CRUSH rule 0 x 439 [119,370,68]
+ CRUSH rule 0 x 440 [333,403,187]
+ CRUSH rule 0 x 441 [477,727,906]
+ CRUSH rule 0 x 442 [274,590,933]
+ CRUSH rule 0 x 443 [983,748,574]
+ CRUSH rule 0 x 444 [536,509,431]
+ CRUSH rule 0 x 445 [485,528,209]
+ CRUSH rule 0 x 446 [345,634,42]
+ CRUSH rule 0 x 447 [61,845,767]
+ CRUSH rule 0 x 448 [333,232,292]
+ CRUSH rule 0 x 449 [680,16,484]
+ CRUSH rule 0 x 450 [235,214,79]
+ CRUSH rule 0 x 451 [961,468,333]
+ CRUSH rule 0 x 452 [525,479,153]
+ CRUSH rule 0 x 453 [138,466,302]
+ CRUSH rule 0 x 454 [137,625,215]
+ CRUSH rule 0 x 455 [173,150,997]
+ CRUSH rule 0 x 456 [235,226,238]
+ CRUSH rule 0 x 457 [450,577,253]
+ CRUSH rule 0 x 458 [195,537,91]
+ CRUSH rule 0 x 459 [381,555,312]
+ CRUSH rule 0 x 460 [972,730,534]
+ CRUSH rule 0 x 461 [506,279,142]
+ CRUSH rule 0 x 462 [692,959,578]
+ CRUSH rule 0 x 463 [788,667,949]
+ CRUSH rule 0 x 464 [133,122,588]
+ CRUSH rule 0 x 465 [971,190,230]
+ CRUSH rule 0 x 466 [394,576,148]
+ CRUSH rule 0 x 467 [517,28,366]
+ CRUSH rule 0 x 468 [829,143,874]
+ CRUSH rule 0 x 469 [987,936,106]
+ CRUSH rule 0 x 470 [107,982,56]
+ CRUSH rule 0 x 471 [181,897,629]
+ CRUSH rule 0 x 472 [547,512,172]
+ CRUSH rule 0 x 473 [760,997,824]
+ CRUSH rule 0 x 474 [787,418,743]
+ CRUSH rule 0 x 475 [662,312,253]
+ CRUSH rule 0 x 476 [110,495,185]
+ CRUSH rule 0 x 477 [393,954,834]
+ CRUSH rule 0 x 478 [246,483,480]
+ CRUSH rule 0 x 479 [70,929,697]
+ CRUSH rule 0 x 480 [753,119,961]
+ CRUSH rule 0 x 481 [470,429,677]
+ CRUSH rule 0 x 482 [451,566,961]
+ CRUSH rule 0 x 483 [816,72,371]
+ CRUSH rule 0 x 484 [540,454,389]
+ CRUSH rule 0 x 485 [74,582,624]
+ CRUSH rule 0 x 486 [958,595,199]
+ CRUSH rule 0 x 487 [228,302,804]
+ CRUSH rule 0 x 488 [180,529,722]
+ CRUSH rule 0 x 489 [47,617,812]
+ CRUSH rule 0 x 490 [905,822,479]
+ CRUSH rule 0 x 491 [892,370,609]
+ CRUSH rule 0 x 492 [588,959,127]
+ CRUSH rule 0 x 493 [353,461,593]
+ CRUSH rule 0 x 494 [378,848,443]
+ CRUSH rule 0 x 495 [845,653,768]
+ CRUSH rule 0 x 496 [13,988,0]
+ CRUSH rule 0 x 497 [796,877,788]
+ CRUSH rule 0 x 498 [412,337,270]
+ CRUSH rule 0 x 499 [330,695,8]
+ CRUSH rule 0 x 500 [820,272,547]
+ CRUSH rule 0 x 501 [110,44,132]
+ CRUSH rule 0 x 502 [336,595,650]
+ CRUSH rule 0 x 503 [922,211,157]
+ CRUSH rule 0 x 504 [483,52,122]
+ CRUSH rule 0 x 505 [482,598,224]
+ CRUSH rule 0 x 506 [493,123,43]
+ CRUSH rule 0 x 507 [12,598,264]
+ CRUSH rule 0 x 508 [227,157,611]
+ CRUSH rule 0 x 509 [807,242,363]
+ CRUSH rule 0 x 510 [134,437,227]
+ CRUSH rule 0 x 511 [212,54,83]
+ CRUSH rule 0 x 512 [236,630,758]
+ CRUSH rule 0 x 513 [994,693,644]
+ CRUSH rule 0 x 514 [45,508,831]
+ CRUSH rule 0 x 515 [504,138,480]
+ CRUSH rule 0 x 516 [285,409,136]
+ CRUSH rule 0 x 517 [300,232,23]
+ CRUSH rule 0 x 518 [397,674,98]
+ CRUSH rule 0 x 519 [86,750,772]
+ CRUSH rule 0 x 520 [900,833,614]
+ CRUSH rule 0 x 521 [31,47,236]
+ CRUSH rule 0 x 522 [390,16,280]
+ CRUSH rule 0 x 523 [618,308,424]
+ CRUSH rule 0 x 524 [635,189,687]
+ CRUSH rule 0 x 525 [311,916,699]
+ CRUSH rule 0 x 526 [48,738,227]
+ CRUSH rule 0 x 527 [202,851,889]
+ CRUSH rule 0 x 528 [565,827,590]
+ CRUSH rule 0 x 529 [934,864,241]
+ CRUSH rule 0 x 530 [502,934,298]
+ CRUSH rule 0 x 531 [681,627,942]
+ CRUSH rule 0 x 532 [422,6,147]
+ CRUSH rule 0 x 533 [863,68,364]
+ CRUSH rule 0 x 534 [962,931,775]
+ CRUSH rule 0 x 535 [89,565,397]
+ CRUSH rule 0 x 536 [499,351,760]
+ CRUSH rule 0 x 537 [676,547,787]
+ CRUSH rule 0 x 538 [58,644,571]
+ CRUSH rule 0 x 539 [837,953,457]
+ CRUSH rule 0 x 540 [831,50,132]
+ CRUSH rule 0 x 541 [582,757,121]
+ CRUSH rule 0 x 542 [472,132,790]
+ CRUSH rule 0 x 543 [382,272,797]
+ CRUSH rule 0 x 544 [947,930,496]
+ CRUSH rule 0 x 545 [425,570,305]
+ CRUSH rule 0 x 546 [18,65,529]
+ CRUSH rule 0 x 547 [445,715,600]
+ CRUSH rule 0 x 548 [367,569,980]
+ CRUSH rule 0 x 549 [125,715,671]
+ CRUSH rule 0 x 550 [425,599,744]
+ CRUSH rule 0 x 551 [44,1,528]
+ CRUSH rule 0 x 552 [246,104,68]
+ CRUSH rule 0 x 553 [71,703,615]
+ CRUSH rule 0 x 554 [207,124,217]
+ CRUSH rule 0 x 555 [570,28,317]
+ CRUSH rule 0 x 556 [674,152,421]
+ CRUSH rule 0 x 557 [347,817,191]
+ CRUSH rule 0 x 558 [627,426,369]
+ CRUSH rule 0 x 559 [940,630,924]
+ CRUSH rule 0 x 560 [295,903,541]
+ CRUSH rule 0 x 561 [506,682,384]
+ CRUSH rule 0 x 562 [718,529,87]
+ CRUSH rule 0 x 563 [552,332,747]
+ CRUSH rule 0 x 564 [835,769,736]
+ CRUSH rule 0 x 565 [8,167,539]
+ CRUSH rule 0 x 566 [600,481,301]
+ CRUSH rule 0 x 567 [999,994,509]
+ CRUSH rule 0 x 568 [252,431,157]
+ CRUSH rule 0 x 569 [643,218,943]
+ CRUSH rule 0 x 570 [617,635,765]
+ CRUSH rule 0 x 571 [757,80,59]
+ CRUSH rule 0 x 572 [299,348,575]
+ CRUSH rule 0 x 573 [25,505,270]
+ CRUSH rule 0 x 574 [215,431,624]
+ CRUSH rule 0 x 575 [225,252,611]
+ CRUSH rule 0 x 576 [627,94,159]
+ CRUSH rule 0 x 577 [237,809,778]
+ CRUSH rule 0 x 578 [885,313,120]
+ CRUSH rule 0 x 579 [924,575,787]
+ CRUSH rule 0 x 580 [718,51,766]
+ CRUSH rule 0 x 581 [219,807,129]
+ CRUSH rule 0 x 582 [893,701,598]
+ CRUSH rule 0 x 583 [246,930,964]
+ CRUSH rule 0 x 584 [336,432,680]
+ CRUSH rule 0 x 585 [324,999,397]
+ CRUSH rule 0 x 586 [558,230,976]
+ CRUSH rule 0 x 587 [985,830,597]
+ CRUSH rule 0 x 588 [211,544,57]
+ CRUSH rule 0 x 589 [129,21,112]
+ CRUSH rule 0 x 590 [467,969,652]
+ CRUSH rule 0 x 591 [758,514,316]
+ CRUSH rule 0 x 592 [525,253,190]
+ CRUSH rule 0 x 593 [601,885,339]
+ CRUSH rule 0 x 594 [227,60,450]
+ CRUSH rule 0 x 595 [720,854,496]
+ CRUSH rule 0 x 596 [751,195,997]
+ CRUSH rule 0 x 597 [129,574,714]
+ CRUSH rule 0 x 598 [679,207,604]
+ CRUSH rule 0 x 599 [668,315,683]
+ CRUSH rule 0 x 600 [143,396,464]
+ CRUSH rule 0 x 601 [326,573,873]
+ CRUSH rule 0 x 602 [860,281,875]
+ CRUSH rule 0 x 603 [709,328,445]
+ CRUSH rule 0 x 604 [571,62,814]
+ CRUSH rule 0 x 605 [252,739,860]
+ CRUSH rule 0 x 606 [339,236,759]
+ CRUSH rule 0 x 607 [590,248,759]
+ CRUSH rule 0 x 608 [145,635,309]
+ CRUSH rule 0 x 609 [973,547,223]
+ CRUSH rule 0 x 610 [435,816,961]
+ CRUSH rule 0 x 611 [559,283,422]
+ CRUSH rule 0 x 612 [273,149,123]
+ CRUSH rule 0 x 613 [828,614,642]
+ CRUSH rule 0 x 614 [478,748,393]
+ CRUSH rule 0 x 615 [392,155,144]
+ CRUSH rule 0 x 616 [778,637,452]
+ CRUSH rule 0 x 617 [622,713,996]
+ CRUSH rule 0 x 618 [149,877,270]
+ CRUSH rule 0 x 619 [604,163,656]
+ CRUSH rule 0 x 620 [181,23,409]
+ CRUSH rule 0 x 621 [735,902,386]
+ CRUSH rule 0 x 622 [661,824,717]
+ CRUSH rule 0 x 623 [142,121,643]
+ CRUSH rule 0 x 624 [360,716,420]
+ CRUSH rule 0 x 625 [541,167,385]
+ CRUSH rule 0 x 626 [364,431,610]
+ CRUSH rule 0 x 627 [458,137,557]
+ CRUSH rule 0 x 628 [250,350,556]
+ CRUSH rule 0 x 629 [928,160,710]
+ CRUSH rule 0 x 630 [243,19,918]
+ CRUSH rule 0 x 631 [438,221,574]
+ CRUSH rule 0 x 632 [797,368,247]
+ CRUSH rule 0 x 633 [993,749,525]
+ CRUSH rule 0 x 634 [239,351,633]
+ CRUSH rule 0 x 635 [640,965,25]
+ CRUSH rule 0 x 636 [173,290,297]
+ CRUSH rule 0 x 637 [0,918,98]
+ CRUSH rule 0 x 638 [702,235,424]
+ CRUSH rule 0 x 639 [475,687,31]
+ CRUSH rule 0 x 640 [31,664,399]
+ CRUSH rule 0 x 641 [296,473,108]
+ CRUSH rule 0 x 642 [894,273,427]
+ CRUSH rule 0 x 643 [117,111,732]
+ CRUSH rule 0 x 644 [438,336,327]
+ CRUSH rule 0 x 645 [982,702,351]
+ CRUSH rule 0 x 646 [334,804,146]
+ CRUSH rule 0 x 647 [933,787,185]
+ CRUSH rule 0 x 648 [22,444,400]
+ CRUSH rule 0 x 649 [503,229,213]
+ CRUSH rule 0 x 650 [328,659,420]
+ CRUSH rule 0 x 651 [3,880,823]
+ CRUSH rule 0 x 652 [495,977,563]
+ CRUSH rule 0 x 653 [185,718,804]
+ CRUSH rule 0 x 654 [130,528,380]
+ CRUSH rule 0 x 655 [560,872,454]
+ CRUSH rule 0 x 656 [219,885,178]
+ CRUSH rule 0 x 657 [233,684,813]
+ CRUSH rule 0 x 658 [778,6,756]
+ CRUSH rule 0 x 659 [240,663,306]
+ CRUSH rule 0 x 660 [244,855,196]
+ CRUSH rule 0 x 661 [184,270,128]
+ CRUSH rule 0 x 662 [65,883,921]
+ CRUSH rule 0 x 663 [323,721,594]
+ CRUSH rule 0 x 664 [865,113,512]
+ CRUSH rule 0 x 665 [420,850,591]
+ CRUSH rule 0 x 666 [319,767,246]
+ CRUSH rule 0 x 667 [875,39,343]
+ CRUSH rule 0 x 668 [331,122,263]
+ CRUSH rule 0 x 669 [915,521,402]
+ CRUSH rule 0 x 670 [845,659,943]
+ CRUSH rule 0 x 671 [108,634,527]
+ CRUSH rule 0 x 672 [578,216,110]
+ CRUSH rule 0 x 673 [442,74,579]
+ CRUSH rule 0 x 674 [588,364,281]
+ CRUSH rule 0 x 675 [489,698,744]
+ CRUSH rule 0 x 676 [928,911,40]
+ CRUSH rule 0 x 677 [399,269,692]
+ CRUSH rule 0 x 678 [546,752,544]
+ CRUSH rule 0 x 679 [988,25,275]
+ CRUSH rule 0 x 680 [335,963,382]
+ CRUSH rule 0 x 681 [690,462,623]
+ CRUSH rule 0 x 682 [196,588,154]
+ CRUSH rule 0 x 683 [627,25,421]
+ CRUSH rule 0 x 684 [38,804,592]
+ CRUSH rule 0 x 685 [841,368,548]
+ CRUSH rule 0 x 686 [336,287,525]
+ CRUSH rule 0 x 687 [20,682,924]
+ CRUSH rule 0 x 688 [463,371,780]
+ CRUSH rule 0 x 689 [569,250,78]
+ CRUSH rule 0 x 690 [551,144,587]
+ CRUSH rule 0 x 691 [766,464,446]
+ CRUSH rule 0 x 692 [739,634,18]
+ CRUSH rule 0 x 693 [339,297,118]
+ CRUSH rule 0 x 694 [405,26,830]
+ CRUSH rule 0 x 695 [622,576,597]
+ CRUSH rule 0 x 696 [558,902,689]
+ CRUSH rule 0 x 697 [818,222,406]
+ CRUSH rule 0 x 698 [178,48,402]
+ CRUSH rule 0 x 699 [450,244,180]
+ CRUSH rule 0 x 700 [502,771,987]
+ CRUSH rule 0 x 701 [4,612,782]
+ CRUSH rule 0 x 702 [177,630,232]
+ CRUSH rule 0 x 703 [354,178,389]
+ CRUSH rule 0 x 704 [646,601,156]
+ CRUSH rule 0 x 705 [921,401,890]
+ CRUSH rule 0 x 706 [652,877,562]
+ CRUSH rule 0 x 707 [345,745,67]
+ CRUSH rule 0 x 708 [333,607,180]
+ CRUSH rule 0 x 709 [45,187,302]
+ CRUSH rule 0 x 710 [94,855,43]
+ CRUSH rule 0 x 711 [227,653,731]
+ CRUSH rule 0 x 712 [398,953,136]
+ CRUSH rule 0 x 713 [116,800,503]
+ CRUSH rule 0 x 714 [111,629,866]
+ CRUSH rule 0 x 715 [531,291,486]
+ CRUSH rule 0 x 716 [169,541,291]
+ CRUSH rule 0 x 717 [417,446,994]
+ CRUSH rule 0 x 718 [992,383,298]
+ CRUSH rule 0 x 719 [936,674,324]
+ CRUSH rule 0 x 720 [370,188,174]
+ CRUSH rule 0 x 721 [320,859,278]
+ CRUSH rule 0 x 722 [7,2,673]
+ CRUSH rule 0 x 723 [270,553,831]
+ CRUSH rule 0 x 724 [666,822,708]
+ CRUSH rule 0 x 725 [794,406,875]
+ CRUSH rule 0 x 726 [420,556,341]
+ CRUSH rule 0 x 727 [561,461,129]
+ CRUSH rule 0 x 728 [951,330,196]
+ CRUSH rule 0 x 729 [656,644,436]
+ CRUSH rule 0 x 730 [3,558,629]
+ CRUSH rule 0 x 731 [852,89,75]
+ CRUSH rule 0 x 732 [983,840,869]
+ CRUSH rule 0 x 733 [285,396,388]
+ CRUSH rule 0 x 734 [125,510,402]
+ CRUSH rule 0 x 735 [417,773,686]
+ CRUSH rule 0 x 736 [749,396,632]
+ CRUSH rule 0 x 737 [644,991,946]
+ CRUSH rule 0 x 738 [449,683,290]
+ CRUSH rule 0 x 739 [341,220,641]
+ CRUSH rule 0 x 740 [874,524,674]
+ CRUSH rule 0 x 741 [189,472,712]
+ CRUSH rule 0 x 742 [912,581,114]
+ CRUSH rule 0 x 743 [654,914,425]
+ CRUSH rule 0 x 744 [725,295,579]
+ CRUSH rule 0 x 745 [787,858,850]
+ CRUSH rule 0 x 746 [757,848,704]
+ CRUSH rule 0 x 747 [700,81,867]
+ CRUSH rule 0 x 748 [557,436,238]
+ CRUSH rule 0 x 749 [772,622,337]
+ CRUSH rule 0 x 750 [946,97,376]
+ CRUSH rule 0 x 751 [996,618,343]
+ CRUSH rule 0 x 752 [746,887,695]
+ CRUSH rule 0 x 753 [741,14,463]
+ CRUSH rule 0 x 754 [648,349,333]
+ CRUSH rule 0 x 755 [157,460,466]
+ CRUSH rule 0 x 756 [416,97,197]
+ CRUSH rule 0 x 757 [599,839,776]
+ CRUSH rule 0 x 758 [994,218,620]
+ CRUSH rule 0 x 759 [959,682,514]
+ CRUSH rule 0 x 760 [518,943,215]
+ CRUSH rule 0 x 761 [285,849,420]
+ CRUSH rule 0 x 762 [591,313,41]
+ CRUSH rule 0 x 763 [908,411,200]
+ CRUSH rule 0 x 764 [787,234,894]
+ CRUSH rule 0 x 765 [327,921,882]
+ CRUSH rule 0 x 766 [84,161,878]
+ CRUSH rule 0 x 767 [370,895,702]
+ CRUSH rule 0 x 768 [826,760,879]
+ CRUSH rule 0 x 769 [67,768,663]
+ CRUSH rule 0 x 770 [593,909,482]
+ CRUSH rule 0 x 771 [309,935,121]
+ CRUSH rule 0 x 772 [12,125,797]
+ CRUSH rule 0 x 773 [253,466,820]
+ CRUSH rule 0 x 774 [164,390,705]
+ CRUSH rule 0 x 775 [703,47,43]
+ CRUSH rule 0 x 776 [728,231,80]
+ CRUSH rule 0 x 777 [981,621,568]
+ CRUSH rule 0 x 778 [411,456,544]
+ CRUSH rule 0 x 779 [346,121,519]
+ CRUSH rule 0 x 780 [476,39,288]
+ CRUSH rule 0 x 781 [10,130,585]
+ CRUSH rule 0 x 782 [462,246,581]
+ CRUSH rule 0 x 783 [580,373,153]
+ CRUSH rule 0 x 784 [413,113,978]
+ CRUSH rule 0 x 785 [341,856,332]
+ CRUSH rule 0 x 786 [411,140,313]
+ CRUSH rule 0 x 787 [605,522,211]
+ CRUSH rule 0 x 788 [226,545,35]
+ CRUSH rule 0 x 789 [545,320,414]
+ CRUSH rule 0 x 790 [414,748,816]
+ CRUSH rule 0 x 791 [660,906,406]
+ CRUSH rule 0 x 792 [287,392,514]
+ CRUSH rule 0 x 793 [631,133,850]
+ CRUSH rule 0 x 794 [931,517,543]
+ CRUSH rule 0 x 795 [551,962,477]
+ CRUSH rule 0 x 796 [814,4,95]
+ CRUSH rule 0 x 797 [64,201,299]
+ CRUSH rule 0 x 798 [422,530,114]
+ CRUSH rule 0 x 799 [824,32,679]
+ CRUSH rule 0 x 800 [862,623,489]
+ CRUSH rule 0 x 801 [145,550,329]
+ CRUSH rule 0 x 802 [570,19,847]
+ CRUSH rule 0 x 803 [151,812,662]
+ CRUSH rule 0 x 804 [467,93,264]
+ CRUSH rule 0 x 805 [621,223,938]
+ CRUSH rule 0 x 806 [898,957,805]
+ CRUSH rule 0 x 807 [354,531,422]
+ CRUSH rule 0 x 808 [7,96,76]
+ CRUSH rule 0 x 809 [70,734,719]
+ CRUSH rule 0 x 810 [701,18,972]
+ CRUSH rule 0 x 811 [248,547,103]
+ CRUSH rule 0 x 812 [230,576,821]
+ CRUSH rule 0 x 813 [805,114,683]
+ CRUSH rule 0 x 814 [54,619,973]
+ CRUSH rule 0 x 815 [679,412,613]
+ CRUSH rule 0 x 816 [919,448,826]
+ CRUSH rule 0 x 817 [765,830,436]
+ CRUSH rule 0 x 818 [415,566,644]
+ CRUSH rule 0 x 819 [721,319,865]
+ CRUSH rule 0 x 820 [218,301,333]
+ CRUSH rule 0 x 821 [185,795,680]
+ CRUSH rule 0 x 822 [356,261,54]
+ CRUSH rule 0 x 823 [220,281,549]
+ CRUSH rule 0 x 824 [292,809,887]
+ CRUSH rule 0 x 825 [949,778,101]
+ CRUSH rule 0 x 826 [767,818,833]
+ CRUSH rule 0 x 827 [631,83,406]
+ CRUSH rule 0 x 828 [288,986,445]
+ CRUSH rule 0 x 829 [990,667,915]
+ CRUSH rule 0 x 830 [152,571,778]
+ CRUSH rule 0 x 831 [814,563,630]
+ CRUSH rule 0 x 832 [235,641,616]
+ CRUSH rule 0 x 833 [657,565,922]
+ CRUSH rule 0 x 834 [907,231,644]
+ CRUSH rule 0 x 835 [784,262,771]
+ CRUSH rule 0 x 836 [951,158,366]
+ CRUSH rule 0 x 837 [556,498,334]
+ CRUSH rule 0 x 838 [329,274,964]
+ CRUSH rule 0 x 839 [568,209,939]
+ CRUSH rule 0 x 840 [45,579,842]
+ CRUSH rule 0 x 841 [652,702,24]
+ CRUSH rule 0 x 842 [629,984,314]
+ CRUSH rule 0 x 843 [799,690,688]
+ CRUSH rule 0 x 844 [694,600,534]
+ CRUSH rule 0 x 845 [332,30,179]
+ CRUSH rule 0 x 846 [452,251,712]
+ CRUSH rule 0 x 847 [399,681,847]
+ CRUSH rule 0 x 848 [303,138,440]
+ CRUSH rule 0 x 849 [666,346,708]
+ CRUSH rule 0 x 850 [644,511,345]
+ CRUSH rule 0 x 851 [527,546,737]
+ CRUSH rule 0 x 852 [31,809,94]
+ CRUSH rule 0 x 853 [483,330,869]
+ CRUSH rule 0 x 854 [697,953,968]
+ CRUSH rule 0 x 855 [837,996,239]
+ CRUSH rule 0 x 856 [712,40,547]
+ CRUSH rule 0 x 857 [77,984,576]
+ CRUSH rule 0 x 858 [412,384,841]
+ CRUSH rule 0 x 859 [173,760,26]
+ CRUSH rule 0 x 860 [776,429,328]
+ CRUSH rule 0 x 861 [705,405,477]
+ CRUSH rule 0 x 862 [809,44,788]
+ CRUSH rule 0 x 863 [349,496,963]
+ CRUSH rule 0 x 864 [717,858,101]
+ CRUSH rule 0 x 865 [857,603,586]
+ CRUSH rule 0 x 866 [394,304,71]
+ CRUSH rule 0 x 867 [640,773,663]
+ CRUSH rule 0 x 868 [613,950,712]
+ CRUSH rule 0 x 869 [973,889,524]
+ CRUSH rule 0 x 870 [505,35,386]
+ CRUSH rule 0 x 871 [239,264,262]
+ CRUSH rule 0 x 872 [21,767,456]
+ CRUSH rule 0 x 873 [954,666,980]
+ CRUSH rule 0 x 874 [54,510,947]
+ CRUSH rule 0 x 875 [809,418,452]
+ CRUSH rule 0 x 876 [483,457,61]
+ CRUSH rule 0 x 877 [542,531,952]
+ CRUSH rule 0 x 878 [217,674,857]
+ CRUSH rule 0 x 879 [999,475,134]
+ CRUSH rule 0 x 880 [678,573,935]
+ CRUSH rule 0 x 881 [394,835,789]
+ CRUSH rule 0 x 882 [467,382,353]
+ CRUSH rule 0 x 883 [802,744,237]
+ CRUSH rule 0 x 884 [653,660,638]
+ CRUSH rule 0 x 885 [898,704,307]
+ CRUSH rule 0 x 886 [434,357,938]
+ CRUSH rule 0 x 887 [297,226,711]
+ CRUSH rule 0 x 888 [863,324,443]
+ CRUSH rule 0 x 889 [105,102,308]
+ CRUSH rule 0 x 890 [550,248,606]
+ CRUSH rule 0 x 891 [575,928,880]
+ CRUSH rule 0 x 892 [259,862,133]
+ CRUSH rule 0 x 893 [902,880,543]
+ CRUSH rule 0 x 894 [180,169,916]
+ CRUSH rule 0 x 895 [725,849,182]
+ CRUSH rule 0 x 896 [951,34,874]
+ CRUSH rule 0 x 897 [810,352,73]
+ CRUSH rule 0 x 898 [979,433,719]
+ CRUSH rule 0 x 899 [685,668,534]
+ CRUSH rule 0 x 900 [530,978,41]
+ CRUSH rule 0 x 901 [740,107,336]
+ CRUSH rule 0 x 902 [800,743,693]
+ CRUSH rule 0 x 903 [230,267,842]
+ CRUSH rule 0 x 904 [346,949,460]
+ CRUSH rule 0 x 905 [530,397,619]
+ CRUSH rule 0 x 906 [80,426,138]
+ CRUSH rule 0 x 907 [365,968,475]
+ CRUSH rule 0 x 908 [204,832,742]
+ CRUSH rule 0 x 909 [883,989,146]
+ CRUSH rule 0 x 910 [549,593,249]
+ CRUSH rule 0 x 911 [325,847,352]
+ CRUSH rule 0 x 912 [874,888,582]
+ CRUSH rule 0 x 913 [331,463,342]
+ CRUSH rule 0 x 914 [836,468,601]
+ CRUSH rule 0 x 915 [245,228,100]
+ CRUSH rule 0 x 916 [77,967,364]
+ CRUSH rule 0 x 917 [239,60,866]
+ CRUSH rule 0 x 918 [988,115,922]
+ CRUSH rule 0 x 919 [783,139,696]
+ CRUSH rule 0 x 920 [623,408,685]
+ CRUSH rule 0 x 921 [105,799,144]
+ CRUSH rule 0 x 922 [887,505,652]
+ CRUSH rule 0 x 923 [223,318,552]
+ CRUSH rule 0 x 924 [25,778,366]
+ CRUSH rule 0 x 925 [912,601,297]
+ CRUSH rule 0 x 926 [968,133,144]
+ CRUSH rule 0 x 927 [277,724,214]
+ CRUSH rule 0 x 928 [554,203,658]
+ CRUSH rule 0 x 929 [761,802,367]
+ CRUSH rule 0 x 930 [814,61,788]
+ CRUSH rule 0 x 931 [29,193,61]
+ CRUSH rule 0 x 932 [446,198,862]
+ CRUSH rule 0 x 933 [352,742,216]
+ CRUSH rule 0 x 934 [730,2,332]
+ CRUSH rule 0 x 935 [731,23,736]
+ CRUSH rule 0 x 936 [322,975,20]
+ CRUSH rule 0 x 937 [822,221,841]
+ CRUSH rule 0 x 938 [557,850,66]
+ CRUSH rule 0 x 939 [150,11,971]
+ CRUSH rule 0 x 940 [638,398,169]
+ CRUSH rule 0 x 941 [730,342,929]
+ CRUSH rule 0 x 942 [62,292,166]
+ CRUSH rule 0 x 943 [165,314,519]
+ CRUSH rule 0 x 944 [199,625,766]
+ CRUSH rule 0 x 945 [946,999,699]
+ CRUSH rule 0 x 946 [595,93,852]
+ CRUSH rule 0 x 947 [800,582,356]
+ CRUSH rule 0 x 948 [132,551,139]
+ CRUSH rule 0 x 949 [792,920,466]
+ CRUSH rule 0 x 950 [111,345,176]
+ CRUSH rule 0 x 951 [414,619,648]
+ CRUSH rule 0 x 952 [775,469,500]
+ CRUSH rule 0 x 953 [349,1,5]
+ CRUSH rule 0 x 954 [570,940,410]
+ CRUSH rule 0 x 955 [729,774,823]
+ CRUSH rule 0 x 956 [519,141,575]
+ CRUSH rule 0 x 957 [242,709,611]
+ CRUSH rule 0 x 958 [84,217,227]
+ CRUSH rule 0 x 959 [270,413,918]
+ CRUSH rule 0 x 960 [458,192,307]
+ CRUSH rule 0 x 961 [981,388,777]
+ CRUSH rule 0 x 962 [623,834,277]
+ CRUSH rule 0 x 963 [291,167,714]
+ CRUSH rule 0 x 964 [28,156,788]
+ CRUSH rule 0 x 965 [675,557,290]
+ CRUSH rule 0 x 966 [836,306,946]
+ CRUSH rule 0 x 967 [966,386,735]
+ CRUSH rule 0 x 968 [864,756,690]
+ CRUSH rule 0 x 969 [729,625,480]
+ CRUSH rule 0 x 970 [800,362,646]
+ CRUSH rule 0 x 971 [737,381,153]
+ CRUSH rule 0 x 972 [952,245,720]
+ CRUSH rule 0 x 973 [356,455,579]
+ CRUSH rule 0 x 974 [545,758,586]
+ CRUSH rule 0 x 975 [336,191,202]
+ CRUSH rule 0 x 976 [446,208,757]
+ CRUSH rule 0 x 977 [202,896,196]
+ CRUSH rule 0 x 978 [612,324,996]
+ CRUSH rule 0 x 979 [843,457,675]
+ CRUSH rule 0 x 980 [60,914,881]
+ CRUSH rule 0 x 981 [702,749,937]
+ CRUSH rule 0 x 982 [298,928,738]
+ CRUSH rule 0 x 983 [723,572,395]
+ CRUSH rule 0 x 984 [723,864,804]
+ CRUSH rule 0 x 985 [945,459,868]
+ CRUSH rule 0 x 986 [772,664,535]
+ CRUSH rule 0 x 987 [88,324,312]
+ CRUSH rule 0 x 988 [522,927,131]
+ CRUSH rule 0 x 989 [578,332,208]
+ CRUSH rule 0 x 990 [638,228,414]
+ CRUSH rule 0 x 991 [530,221,451]
+ CRUSH rule 0 x 992 [925,705,275]
+ CRUSH rule 0 x 993 [991,301,43]
+ CRUSH rule 0 x 994 [276,51,868]
+ CRUSH rule 0 x 995 [288,836,753]
+ CRUSH rule 0 x 996 [887,983,252]
+ CRUSH rule 0 x 997 [110,924,386]
+ CRUSH rule 0 x 998 [435,830,485]
+ CRUSH rule 0 x 999 [876,738,357]
+ CRUSH rule 0 x 1000 [178,963,638]
+ CRUSH rule 0 x 1001 [99,519,66]
+ CRUSH rule 0 x 1002 [515,534,468]
+ CRUSH rule 0 x 1003 [104,611,937]
+ CRUSH rule 0 x 1004 [269,638,724]
+ CRUSH rule 0 x 1005 [369,223,309]
+ CRUSH rule 0 x 1006 [40,107,69]
+ CRUSH rule 0 x 1007 [978,111,416]
+ CRUSH rule 0 x 1008 [965,956,624]
+ CRUSH rule 0 x 1009 [598,476,356]
+ CRUSH rule 0 x 1010 [767,523,239]
+ CRUSH rule 0 x 1011 [289,871,207]
+ CRUSH rule 0 x 1012 [128,28,370]
+ CRUSH rule 0 x 1013 [979,765,660]
+ CRUSH rule 0 x 1014 [979,948,513]
+ CRUSH rule 0 x 1015 [277,790,396]
+ CRUSH rule 0 x 1016 [262,73,128]
+ CRUSH rule 0 x 1017 [150,269,61]
+ CRUSH rule 0 x 1018 [555,829,554]
+ CRUSH rule 0 x 1019 [513,356,265]
+ CRUSH rule 0 x 1020 [158,161,877]
+ CRUSH rule 0 x 1021 [915,998,957]
+ CRUSH rule 0 x 1022 [967,829,973]
+ CRUSH rule 0 x 1023 [488,257,614]
+ rule 0 (data) num_rep 3 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450]
+ CRUSH rule 0 x 1 [876,250,334,633]
+ CRUSH rule 0 x 2 [292,832,53,392]
+ CRUSH rule 0 x 3 [623,387,124,998]
+ CRUSH rule 0 x 4 [61,334,710,4]
+ CRUSH rule 0 x 5 [946,557,713,664]
+ CRUSH rule 0 x 6 [576,668,212,163]
+ CRUSH rule 0 x 7 [645,753,906,393]
+ CRUSH rule 0 x 8 [243,6,863,781]
+ CRUSH rule 0 x 9 [22,578,251,410]
+ CRUSH rule 0 x 10 [758,828,360,477]
+ CRUSH rule 0 x 11 [769,120,124,527]
+ CRUSH rule 0 x 12 [780,364,689,755]
+ CRUSH rule 0 x 13 [557,18,351,719]
+ CRUSH rule 0 x 14 [59,561,249,461]
+ CRUSH rule 0 x 15 [718,928,993,21]
+ CRUSH rule 0 x 16 [673,632,841,954]
+ CRUSH rule 0 x 17 [648,43,560,514]
+ CRUSH rule 0 x 18 [654,219,181,568]
+ CRUSH rule 0 x 19 [850,545,377,848]
+ CRUSH rule 0 x 20 [717,785,974,5]
+ CRUSH rule 0 x 21 [420,57,519,306]
+ CRUSH rule 0 x 22 [503,998,193,821]
+ CRUSH rule 0 x 23 [411,663,168,110]
+ CRUSH rule 0 x 24 [266,861,353,1]
+ CRUSH rule 0 x 25 [760,483,818,600]
+ CRUSH rule 0 x 26 [903,24,573,718]
+ CRUSH rule 0 x 27 [946,188,289,510]
+ CRUSH rule 0 x 28 [69,312,73,198]
+ CRUSH rule 0 x 29 [844,883,337,628]
+ CRUSH rule 0 x 30 [621,18,613,794]
+ CRUSH rule 0 x 31 [784,943,814,539]
+ CRUSH rule 0 x 32 [173,374,369,972]
+ CRUSH rule 0 x 33 [698,336,357,966]
+ CRUSH rule 0 x 34 [168,836,210,798]
+ CRUSH rule 0 x 35 [274,509,534,818]
+ CRUSH rule 0 x 36 [318,215,153,628]
+ CRUSH rule 0 x 37 [173,604,109,935]
+ CRUSH rule 0 x 38 [708,444,683,604]
+ CRUSH rule 0 x 39 [662,198,417,680]
+ CRUSH rule 0 x 40 [620,801,414,78]
+ CRUSH rule 0 x 41 [811,264,177,127]
+ CRUSH rule 0 x 42 [863,179,527,660]
+ CRUSH rule 0 x 43 [686,822,988,228]
+ CRUSH rule 0 x 44 [396,222,46,841]
+ CRUSH rule 0 x 45 [991,694,253,142]
+ CRUSH rule 0 x 46 [420,909,184,285]
+ CRUSH rule 0 x 47 [467,211,605,207]
+ CRUSH rule 0 x 48 [955,329,368,168]
+ CRUSH rule 0 x 49 [974,891,931,29]
+ CRUSH rule 0 x 50 [870,441,691,823]
+ CRUSH rule 0 x 51 [182,930,25,936]
+ CRUSH rule 0 x 52 [704,812,894,794]
+ CRUSH rule 0 x 53 [185,713,631,280]
+ CRUSH rule 0 x 54 [270,441,100,82]
+ CRUSH rule 0 x 55 [895,734,958,793]
+ CRUSH rule 0 x 56 [564,963,683,324]
+ CRUSH rule 0 x 57 [738,130,208,973]
+ CRUSH rule 0 x 58 [524,113,806,903]
+ CRUSH rule 0 x 59 [408,337,668,529]
+ CRUSH rule 0 x 60 [228,790,857,309]
+ CRUSH rule 0 x 61 [154,843,717,467]
+ CRUSH rule 0 x 62 [594,811,549,276]
+ CRUSH rule 0 x 63 [646,67,884,925]
+ CRUSH rule 0 x 64 [175,542,155,837]
+ CRUSH rule 0 x 65 [745,619,131,867]
+ CRUSH rule 0 x 66 [275,468,23,35]
+ CRUSH rule 0 x 67 [246,958,524,493]
+ CRUSH rule 0 x 68 [711,473,403,228]
+ CRUSH rule 0 x 69 [493,924,850,939]
+ CRUSH rule 0 x 70 [30,499,644,33]
+ CRUSH rule 0 x 71 [984,883,574,716]
+ CRUSH rule 0 x 72 [71,286,942,363]
+ CRUSH rule 0 x 73 [922,618,3,371]
+ CRUSH rule 0 x 74 [629,414,185,573]
+ CRUSH rule 0 x 75 [222,20,174,820]
+ CRUSH rule 0 x 76 [262,366,339,290]
+ CRUSH rule 0 x 77 [638,469,992,280]
+ CRUSH rule 0 x 78 [324,511,788,7]
+ CRUSH rule 0 x 79 [577,990,64,94]
+ CRUSH rule 0 x 80 [501,95,278,903]
+ CRUSH rule 0 x 81 [506,812,9,698]
+ CRUSH rule 0 x 82 [222,145,80,785]
+ CRUSH rule 0 x 83 [71,634,61,91]
+ CRUSH rule 0 x 84 [49,761,773,368]
+ CRUSH rule 0 x 85 [985,896,708,861]
+ CRUSH rule 0 x 86 [537,745,93,524]
+ CRUSH rule 0 x 87 [997,317,463,626]
+ CRUSH rule 0 x 88 [957,350,890,857]
+ CRUSH rule 0 x 89 [399,730,148,314]
+ CRUSH rule 0 x 90 [943,706,683,267]
+ CRUSH rule 0 x 91 [22,368,149,928]
+ CRUSH rule 0 x 92 [532,424,426,773]
+ CRUSH rule 0 x 93 [218,489,405,681]
+ CRUSH rule 0 x 94 [181,96,102,515]
+ CRUSH rule 0 x 95 [343,957,820,139]
+ CRUSH rule 0 x 96 [861,270,87,797]
+ CRUSH rule 0 x 97 [459,706,45,328]
+ CRUSH rule 0 x 98 [327,867,353,948]
+ CRUSH rule 0 x 99 [974,133,468,906]
+ CRUSH rule 0 x 100 [32,445,547,371]
+ CRUSH rule 0 x 101 [142,90,337,950]
+ CRUSH rule 0 x 102 [172,129,139,22]
+ CRUSH rule 0 x 103 [630,47,161,356]
+ CRUSH rule 0 x 104 [758,133,278,11]
+ CRUSH rule 0 x 105 [843,604,47,33]
+ CRUSH rule 0 x 106 [28,681,193,679]
+ CRUSH rule 0 x 107 [74,320,85,819]
+ CRUSH rule 0 x 108 [875,593,575,517]
+ CRUSH rule 0 x 109 [411,985,811,720]
+ CRUSH rule 0 x 110 [440,774,799,660]
+ CRUSH rule 0 x 111 [405,742,276,359]
+ CRUSH rule 0 x 112 [143,181,922,545]
+ CRUSH rule 0 x 113 [153,846,160,903]
+ CRUSH rule 0 x 114 [804,892,939,20]
+ CRUSH rule 0 x 115 [588,508,958,580]
+ CRUSH rule 0 x 116 [327,148,637,486]
+ CRUSH rule 0 x 117 [95,594,989,131]
+ CRUSH rule 0 x 118 [80,957,897,239]
+ CRUSH rule 0 x 119 [386,932,951,768]
+ CRUSH rule 0 x 120 [366,312,653,936]
+ CRUSH rule 0 x 121 [129,154,847,16]
+ CRUSH rule 0 x 122 [873,1,110,939]
+ CRUSH rule 0 x 123 [533,415,789,600]
+ CRUSH rule 0 x 124 [461,691,898,723]
+ CRUSH rule 0 x 125 [342,599,830,402]
+ CRUSH rule 0 x 126 [819,781,822,548]
+ CRUSH rule 0 x 127 [437,893,585,707]
+ CRUSH rule 0 x 128 [679,994,982,550]
+ CRUSH rule 0 x 129 [380,685,947,302]
+ CRUSH rule 0 x 130 [992,52,466,867]
+ CRUSH rule 0 x 131 [469,90,208,599]
+ CRUSH rule 0 x 132 [571,250,316,535]
+ CRUSH rule 0 x 133 [964,728,329,902]
+ CRUSH rule 0 x 134 [999,19,716,963]
+ CRUSH rule 0 x 135 [634,101,52,938]
+ CRUSH rule 0 x 136 [114,889,692,768]
+ CRUSH rule 0 x 137 [839,8,959,280]
+ CRUSH rule 0 x 138 [967,949,138,451]
+ CRUSH rule 0 x 139 [308,711,736,247]
+ CRUSH rule 0 x 140 [764,936,926,55]
+ CRUSH rule 0 x 141 [423,302,112,216]
+ CRUSH rule 0 x 142 [252,821,715,340]
+ CRUSH rule 0 x 143 [33,808,518,477]
+ CRUSH rule 0 x 144 [472,88,969,162]
+ CRUSH rule 0 x 145 [242,208,252,604]
+ CRUSH rule 0 x 146 [290,70,570,384]
+ CRUSH rule 0 x 147 [447,352,657,493]
+ CRUSH rule 0 x 148 [212,644,432,658]
+ CRUSH rule 0 x 149 [9,775,87,35]
+ CRUSH rule 0 x 150 [166,456,582,144]
+ CRUSH rule 0 x 151 [811,875,307,20]
+ CRUSH rule 0 x 152 [449,617,223,9]
+ CRUSH rule 0 x 153 [523,537,695,627]
+ CRUSH rule 0 x 154 [208,559,874,597]
+ CRUSH rule 0 x 155 [569,325,192,296]
+ CRUSH rule 0 x 156 [488,121,521,213]
+ CRUSH rule 0 x 157 [140,723,633,260]
+ CRUSH rule 0 x 158 [786,451,320,239]
+ CRUSH rule 0 x 159 [134,664,517,821]
+ CRUSH rule 0 x 160 [690,112,414,990]
+ CRUSH rule 0 x 161 [324,912,397,423]
+ CRUSH rule 0 x 162 [748,567,284,183]
+ CRUSH rule 0 x 163 [575,499,31,816]
+ CRUSH rule 0 x 164 [314,489,308,326]
+ CRUSH rule 0 x 165 [116,209,750,53]
+ CRUSH rule 0 x 166 [352,706,701,810]
+ CRUSH rule 0 x 167 [27,743,174,142]
+ CRUSH rule 0 x 168 [953,898,880,660]
+ CRUSH rule 0 x 169 [912,147,266,547]
+ CRUSH rule 0 x 170 [421,515,828,844]
+ CRUSH rule 0 x 171 [488,584,880,964]
+ CRUSH rule 0 x 172 [366,443,957,66]
+ CRUSH rule 0 x 173 [863,291,625,287]
+ CRUSH rule 0 x 174 [263,555,650,410]
+ CRUSH rule 0 x 175 [875,961,361,575]
+ CRUSH rule 0 x 176 [745,83,701,680]
+ CRUSH rule 0 x 177 [128,244,41,123]
+ CRUSH rule 0 x 178 [155,41,264,777]
+ CRUSH rule 0 x 179 [593,833,202,183]
+ CRUSH rule 0 x 180 [154,734,17,831]
+ CRUSH rule 0 x 181 [289,675,723,800]
+ CRUSH rule 0 x 182 [730,931,560,209]
+ CRUSH rule 0 x 183 [639,237,794,815]
+ CRUSH rule 0 x 184 [704,312,685,645]
+ CRUSH rule 0 x 185 [97,100,762,82]
+ CRUSH rule 0 x 186 [26,665,554,215]
+ CRUSH rule 0 x 187 [649,14,740,494]
+ CRUSH rule 0 x 188 [682,695,590,743]
+ CRUSH rule 0 x 189 [325,693,726,51]
+ CRUSH rule 0 x 190 [399,933,136,955]
+ CRUSH rule 0 x 191 [629,533,17,126]
+ CRUSH rule 0 x 192 [503,578,38,492]
+ CRUSH rule 0 x 193 [546,333,651,678]
+ CRUSH rule 0 x 194 [242,473,58,655]
+ CRUSH rule 0 x 195 [625,719,135,81]
+ CRUSH rule 0 x 196 [357,114,125,867]
+ CRUSH rule 0 x 197 [306,954,453,873]
+ CRUSH rule 0 x 198 [863,791,311,911]
+ CRUSH rule 0 x 199 [935,906,929,252]
+ CRUSH rule 0 x 200 [373,774,229,454]
+ CRUSH rule 0 x 201 [659,320,477,313]
+ CRUSH rule 0 x 202 [260,433,524,880]
+ CRUSH rule 0 x 203 [36,239,675,971]
+ CRUSH rule 0 x 204 [92,516,993,728]
+ CRUSH rule 0 x 205 [68,395,473,45]
+ CRUSH rule 0 x 206 [570,530,642,380]
+ CRUSH rule 0 x 207 [834,457,850,917]
+ CRUSH rule 0 x 208 [927,484,640,976]
+ CRUSH rule 0 x 209 [878,66,58,940]
+ CRUSH rule 0 x 210 [572,981,484,29]
+ CRUSH rule 0 x 211 [107,597,780,857]
+ CRUSH rule 0 x 212 [389,107,838,624]
+ CRUSH rule 0 x 213 [497,717,567,728]
+ CRUSH rule 0 x 214 [798,65,254,572]
+ CRUSH rule 0 x 215 [233,419,283,638]
+ CRUSH rule 0 x 216 [494,464,742,523]
+ CRUSH rule 0 x 217 [352,396,309,938]
+ CRUSH rule 0 x 218 [895,864,988,650]
+ CRUSH rule 0 x 219 [222,534,277,242]
+ CRUSH rule 0 x 220 [281,19,584,563]
+ CRUSH rule 0 x 221 [64,928,963,130]
+ CRUSH rule 0 x 222 [40,544,161,199]
+ CRUSH rule 0 x 223 [645,556,159,417]
+ CRUSH rule 0 x 224 [647,165,957,263]
+ CRUSH rule 0 x 225 [219,714,858,747]
+ CRUSH rule 0 x 226 [372,511,181,277]
+ CRUSH rule 0 x 227 [925,156,714,863]
+ CRUSH rule 0 x 228 [682,404,839,263]
+ CRUSH rule 0 x 229 [880,838,770,891]
+ CRUSH rule 0 x 230 [328,659,916,468]
+ CRUSH rule 0 x 231 [320,383,669,109]
+ CRUSH rule 0 x 232 [924,846,394,319]
+ CRUSH rule 0 x 233 [948,652,575,838]
+ CRUSH rule 0 x 234 [484,943,42,575]
+ CRUSH rule 0 x 235 [750,65,590,168]
+ CRUSH rule 0 x 236 [551,787,490,136]
+ CRUSH rule 0 x 237 [390,157,166,251]
+ CRUSH rule 0 x 238 [570,6,989,707]
+ CRUSH rule 0 x 239 [729,959,376,975]
+ CRUSH rule 0 x 240 [981,241,156,767]
+ CRUSH rule 0 x 241 [310,816,641,177]
+ CRUSH rule 0 x 242 [161,63,642,837]
+ CRUSH rule 0 x 243 [180,394,33,683]
+ CRUSH rule 0 x 244 [52,174,685,189]
+ CRUSH rule 0 x 245 [523,121,915,84]
+ CRUSH rule 0 x 246 [362,893,390,487]
+ CRUSH rule 0 x 247 [382,184,116,34]
+ CRUSH rule 0 x 248 [129,114,852,469]
+ CRUSH rule 0 x 249 [159,683,91,856]
+ CRUSH rule 0 x 250 [404,945,569,955]
+ CRUSH rule 0 x 251 [661,225,738,757]
+ CRUSH rule 0 x 252 [961,226,542,103]
+ CRUSH rule 0 x 253 [651,97,225,364]
+ CRUSH rule 0 x 254 [123,33,741,692]
+ CRUSH rule 0 x 255 [314,649,891,855]
+ CRUSH rule 0 x 256 [315,215,651,126]
+ CRUSH rule 0 x 257 [825,264,867,529]
+ CRUSH rule 0 x 258 [624,789,370,723]
+ CRUSH rule 0 x 259 [602,542,70,563]
+ CRUSH rule 0 x 260 [717,878,43,56]
+ CRUSH rule 0 x 261 [145,517,20,903]
+ CRUSH rule 0 x 262 [223,1,561,420]
+ CRUSH rule 0 x 263 [462,211,405,508]
+ CRUSH rule 0 x 264 [654,471,266,662]
+ CRUSH rule 0 x 265 [302,794,704,798]
+ CRUSH rule 0 x 266 [202,132,884,209]
+ CRUSH rule 0 x 267 [282,938,657,113]
+ CRUSH rule 0 x 268 [338,309,356,278]
+ CRUSH rule 0 x 269 [738,122,266,200]
+ CRUSH rule 0 x 270 [707,982,946,196]
+ CRUSH rule 0 x 271 [705,432,364,735]
+ CRUSH rule 0 x 272 [756,545,942,56]
+ CRUSH rule 0 x 273 [197,502,527,721]
+ CRUSH rule 0 x 274 [992,44,653,573]
+ CRUSH rule 0 x 275 [544,789,170,434]
+ CRUSH rule 0 x 276 [658,467,577,268]
+ CRUSH rule 0 x 277 [143,490,880,483]
+ CRUSH rule 0 x 278 [492,647,355,282]
+ CRUSH rule 0 x 279 [517,792,604,987]
+ CRUSH rule 0 x 280 [825,740,27,848]
+ CRUSH rule 0 x 281 [224,629,120,562]
+ CRUSH rule 0 x 282 [298,661,380,416]
+ CRUSH rule 0 x 283 [311,606,208,50]
+ CRUSH rule 0 x 284 [771,466,371,743]
+ CRUSH rule 0 x 285 [693,362,404,676]
+ CRUSH rule 0 x 286 [364,477,285,167]
+ CRUSH rule 0 x 287 [591,611,828,995]
+ CRUSH rule 0 x 288 [965,541,848,796]
+ CRUSH rule 0 x 289 [225,551,948,877]
+ CRUSH rule 0 x 290 [577,762,777,751]
+ CRUSH rule 0 x 291 [160,903,477,381]
+ CRUSH rule 0 x 292 [873,598,216,666]
+ CRUSH rule 0 x 293 [100,234,874,47]
+ CRUSH rule 0 x 294 [285,943,379,520]
+ CRUSH rule 0 x 295 [938,262,880,327]
+ CRUSH rule 0 x 296 [850,327,86,472]
+ CRUSH rule 0 x 297 [951,53,99,558]
+ CRUSH rule 0 x 298 [173,336,85,766]
+ CRUSH rule 0 x 299 [598,591,315,386]
+ CRUSH rule 0 x 300 [531,957,62,459]
+ CRUSH rule 0 x 301 [823,628,23,858]
+ CRUSH rule 0 x 302 [184,80,780,871]
+ CRUSH rule 0 x 303 [521,766,222,830]
+ CRUSH rule 0 x 304 [980,127,807,507]
+ CRUSH rule 0 x 305 [153,816,22,927]
+ CRUSH rule 0 x 306 [423,739,664,753]
+ CRUSH rule 0 x 307 [997,557,682,456]
+ CRUSH rule 0 x 308 [991,874,534,465]
+ CRUSH rule 0 x 309 [860,394,724,858]
+ CRUSH rule 0 x 310 [589,818,546,201]
+ CRUSH rule 0 x 311 [477,774,225,590]
+ CRUSH rule 0 x 312 [887,853,950,354]
+ CRUSH rule 0 x 313 [802,646,447,416]
+ CRUSH rule 0 x 314 [654,974,229,511]
+ CRUSH rule 0 x 315 [767,227,28,740]
+ CRUSH rule 0 x 316 [778,83,733,359]
+ CRUSH rule 0 x 317 [184,418,642,986]
+ CRUSH rule 0 x 318 [525,410,500,543]
+ CRUSH rule 0 x 319 [476,724,569,382]
+ CRUSH rule 0 x 320 [149,610,697,296]
+ CRUSH rule 0 x 321 [710,79,667,671]
+ CRUSH rule 0 x 322 [175,275,323,333]
+ CRUSH rule 0 x 323 [819,604,638,792]
+ CRUSH rule 0 x 324 [16,745,511,439]
+ CRUSH rule 0 x 325 [486,400,872,873]
+ CRUSH rule 0 x 326 [613,765,207,19]
+ CRUSH rule 0 x 327 [125,289,738,408]
+ CRUSH rule 0 x 328 [807,383,476,583]
+ CRUSH rule 0 x 329 [588,938,599,432]
+ CRUSH rule 0 x 330 [932,644,41,611]
+ CRUSH rule 0 x 331 [341,953,950,537]
+ CRUSH rule 0 x 332 [153,726,459,950]
+ CRUSH rule 0 x 333 [745,845,853,860]
+ CRUSH rule 0 x 334 [614,751,807,58]
+ CRUSH rule 0 x 335 [518,721,221,283]
+ CRUSH rule 0 x 336 [389,424,77,309]
+ CRUSH rule 0 x 337 [753,508,765,720]
+ CRUSH rule 0 x 338 [128,810,490,753]
+ CRUSH rule 0 x 339 [430,308,58,751]
+ CRUSH rule 0 x 340 [541,44,630,231]
+ CRUSH rule 0 x 341 [402,26,631,439]
+ CRUSH rule 0 x 342 [982,57,992,461]
+ CRUSH rule 0 x 343 [833,412,572,732]
+ CRUSH rule 0 x 344 [784,533,792,41]
+ CRUSH rule 0 x 345 [546,300,304,691]
+ CRUSH rule 0 x 346 [302,420,428,891]
+ CRUSH rule 0 x 347 [488,778,101,217]
+ CRUSH rule 0 x 348 [903,744,937,718]
+ CRUSH rule 0 x 349 [471,547,582,306]
+ CRUSH rule 0 x 350 [348,221,823,335]
+ CRUSH rule 0 x 351 [961,582,705,346]
+ CRUSH rule 0 x 352 [728,137,461,298]
+ CRUSH rule 0 x 353 [904,202,184,447]
+ CRUSH rule 0 x 354 [345,226,319,256]
+ CRUSH rule 0 x 355 [50,430,175,43]
+ CRUSH rule 0 x 356 [87,185,55,423]
+ CRUSH rule 0 x 357 [762,459,921,473]
+ CRUSH rule 0 x 358 [908,25,280,6]
+ CRUSH rule 0 x 359 [484,15,132,121]
+ CRUSH rule 0 x 360 [173,378,337,702]
+ CRUSH rule 0 x 361 [404,577,115,25]
+ CRUSH rule 0 x 362 [403,1,422,945]
+ CRUSH rule 0 x 363 [639,911,510,162]
+ CRUSH rule 0 x 364 [752,689,610,990]
+ CRUSH rule 0 x 365 [956,999,212,230]
+ CRUSH rule 0 x 366 [860,925,924,763]
+ CRUSH rule 0 x 367 [205,609,647,665]
+ CRUSH rule 0 x 368 [301,284,810,169]
+ CRUSH rule 0 x 369 [452,658,339,217]
+ CRUSH rule 0 x 370 [11,467,695,989]
+ CRUSH rule 0 x 371 [124,487,55,514]
+ CRUSH rule 0 x 372 [253,48,979,846]
+ CRUSH rule 0 x 373 [715,605,775,748]
+ CRUSH rule 0 x 374 [191,887,920,223]
+ CRUSH rule 0 x 375 [711,385,651,665]
+ CRUSH rule 0 x 376 [597,818,49,458]
+ CRUSH rule 0 x 377 [294,256,933,771]
+ CRUSH rule 0 x 378 [34,151,681,707]
+ CRUSH rule 0 x 379 [869,136,315,378]
+ CRUSH rule 0 x 380 [294,97,575,791]
+ CRUSH rule 0 x 381 [119,710,219,827]
+ CRUSH rule 0 x 382 [69,631,508,706]
+ CRUSH rule 0 x 383 [922,588,589,925]
+ CRUSH rule 0 x 384 [221,945,671,117]
+ CRUSH rule 0 x 385 [561,737,953,723]
+ CRUSH rule 0 x 386 [335,442,788,696]
+ CRUSH rule 0 x 387 [514,43,353,88]
+ CRUSH rule 0 x 388 [587,89,157,996]
+ CRUSH rule 0 x 389 [109,641,255,466]
+ CRUSH rule 0 x 390 [925,149,421,489]
+ CRUSH rule 0 x 391 [267,87,387,527]
+ CRUSH rule 0 x 392 [382,485,370,849]
+ CRUSH rule 0 x 393 [425,721,221,753]
+ CRUSH rule 0 x 394 [898,18,38,793]
+ CRUSH rule 0 x 395 [806,876,269,679]
+ CRUSH rule 0 x 396 [790,970,437,449]
+ CRUSH rule 0 x 397 [136,363,507,613]
+ CRUSH rule 0 x 398 [914,116,558,258]
+ CRUSH rule 0 x 399 [261,94,299,202]
+ CRUSH rule 0 x 400 [661,197,338,461]
+ CRUSH rule 0 x 401 [953,979,287,803]
+ CRUSH rule 0 x 402 [738,819,618,522]
+ CRUSH rule 0 x 403 [573,238,425,546]
+ CRUSH rule 0 x 404 [526,848,790,253]
+ CRUSH rule 0 x 405 [582,505,330,334]
+ CRUSH rule 0 x 406 [768,324,493,60]
+ CRUSH rule 0 x 407 [260,951,437,587]
+ CRUSH rule 0 x 408 [657,81,770,734]
+ CRUSH rule 0 x 409 [498,89,182,423]
+ CRUSH rule 0 x 410 [28,793,737,352]
+ CRUSH rule 0 x 411 [684,992,60,659]
+ CRUSH rule 0 x 412 [261,958,699,950]
+ CRUSH rule 0 x 413 [891,835,297,441]
+ CRUSH rule 0 x 414 [127,459,119,965]
+ CRUSH rule 0 x 415 [272,540,631,328]
+ CRUSH rule 0 x 416 [739,617,115,530]
+ CRUSH rule 0 x 417 [106,209,157,878]
+ CRUSH rule 0 x 418 [525,441,147,390]
+ CRUSH rule 0 x 419 [603,673,615,465]
+ CRUSH rule 0 x 420 [988,213,251,226]
+ CRUSH rule 0 x 421 [761,521,748,368]
+ CRUSH rule 0 x 422 [317,160,924,548]
+ CRUSH rule 0 x 423 [137,807,168,472]
+ CRUSH rule 0 x 424 [920,37,146,263]
+ CRUSH rule 0 x 425 [277,693,285,221]
+ CRUSH rule 0 x 426 [485,936,407,854]
+ CRUSH rule 0 x 427 [242,515,9,564]
+ CRUSH rule 0 x 428 [632,635,26,473]
+ CRUSH rule 0 x 429 [641,73,465,127]
+ CRUSH rule 0 x 430 [626,585,6,387]
+ CRUSH rule 0 x 431 [697,76,753,570]
+ CRUSH rule 0 x 432 [590,526,306,283]
+ CRUSH rule 0 x 433 [284,387,149,817]
+ CRUSH rule 0 x 434 [538,985,79,953]
+ CRUSH rule 0 x 435 [30,318,593,635]
+ CRUSH rule 0 x 436 [164,919,851,693]
+ CRUSH rule 0 x 437 [322,212,163,606]
+ CRUSH rule 0 x 438 [142,392,85,594]
+ CRUSH rule 0 x 439 [119,370,68,443]
+ CRUSH rule 0 x 440 [333,403,187,863]
+ CRUSH rule 0 x 441 [477,727,906,145]
+ CRUSH rule 0 x 442 [274,590,933,244]
+ CRUSH rule 0 x 443 [983,748,574,718]
+ CRUSH rule 0 x 444 [536,509,431,146]
+ CRUSH rule 0 x 445 [485,528,209,964]
+ CRUSH rule 0 x 446 [345,634,42,294]
+ CRUSH rule 0 x 447 [61,845,767,600]
+ CRUSH rule 0 x 448 [333,232,292,846]
+ CRUSH rule 0 x 449 [680,16,484,670]
+ CRUSH rule 0 x 450 [235,214,79,423]
+ CRUSH rule 0 x 451 [961,468,333,640]
+ CRUSH rule 0 x 452 [525,479,153,528]
+ CRUSH rule 0 x 453 [138,466,302,86]
+ CRUSH rule 0 x 454 [137,625,215,402]
+ CRUSH rule 0 x 455 [173,150,997,16]
+ CRUSH rule 0 x 456 [235,226,238,258]
+ CRUSH rule 0 x 457 [450,577,253,413]
+ CRUSH rule 0 x 458 [195,537,91,814]
+ CRUSH rule 0 x 459 [381,555,312,573]
+ CRUSH rule 0 x 460 [972,730,534,678]
+ CRUSH rule 0 x 461 [506,279,142,830]
+ CRUSH rule 0 x 462 [692,959,578,57]
+ CRUSH rule 0 x 463 [788,667,949,550]
+ CRUSH rule 0 x 464 [133,122,588,999]
+ CRUSH rule 0 x 465 [971,190,230,777]
+ CRUSH rule 0 x 466 [394,576,148,157]
+ CRUSH rule 0 x 467 [517,28,366,362]
+ CRUSH rule 0 x 468 [829,143,874,225]
+ CRUSH rule 0 x 469 [987,936,106,725]
+ CRUSH rule 0 x 470 [107,982,56,889]
+ CRUSH rule 0 x 471 [181,897,629,860]
+ CRUSH rule 0 x 472 [547,512,172,24]
+ CRUSH rule 0 x 473 [760,997,824,905]
+ CRUSH rule 0 x 474 [787,418,743,628]
+ CRUSH rule 0 x 475 [662,312,253,617]
+ CRUSH rule 0 x 476 [110,495,185,508]
+ CRUSH rule 0 x 477 [393,954,834,132]
+ CRUSH rule 0 x 478 [246,483,480,644]
+ CRUSH rule 0 x 479 [70,929,697,931]
+ CRUSH rule 0 x 480 [753,119,961,607]
+ CRUSH rule 0 x 481 [470,429,677,242]
+ CRUSH rule 0 x 482 [451,566,961,675]
+ CRUSH rule 0 x 483 [816,72,371,278]
+ CRUSH rule 0 x 484 [540,454,389,31]
+ CRUSH rule 0 x 485 [74,582,624,684]
+ CRUSH rule 0 x 486 [958,595,199,763]
+ CRUSH rule 0 x 487 [228,302,804,833]
+ CRUSH rule 0 x 488 [180,529,722,956]
+ CRUSH rule 0 x 489 [47,617,812,187]
+ CRUSH rule 0 x 490 [905,822,479,124]
+ CRUSH rule 0 x 491 [892,370,609,998]
+ CRUSH rule 0 x 492 [588,959,127,948]
+ CRUSH rule 0 x 493 [353,461,593,291]
+ CRUSH rule 0 x 494 [378,848,443,368]
+ CRUSH rule 0 x 495 [845,653,768,234]
+ CRUSH rule 0 x 496 [13,988,0,691]
+ CRUSH rule 0 x 497 [796,877,788,394]
+ CRUSH rule 0 x 498 [412,337,270,705]
+ CRUSH rule 0 x 499 [330,695,8,74]
+ CRUSH rule 0 x 500 [820,272,547,765]
+ CRUSH rule 0 x 501 [110,44,132,442]
+ CRUSH rule 0 x 502 [336,595,650,274]
+ CRUSH rule 0 x 503 [922,211,157,722]
+ CRUSH rule 0 x 504 [483,52,122,432]
+ CRUSH rule 0 x 505 [482,598,224,279]
+ CRUSH rule 0 x 506 [493,123,43,856]
+ CRUSH rule 0 x 507 [12,598,264,422]
+ CRUSH rule 0 x 508 [227,157,611,301]
+ CRUSH rule 0 x 509 [807,242,363,122]
+ CRUSH rule 0 x 510 [134,437,227,75]
+ CRUSH rule 0 x 511 [212,54,83,799]
+ CRUSH rule 0 x 512 [236,630,758,752]
+ CRUSH rule 0 x 513 [994,693,644,938]
+ CRUSH rule 0 x 514 [45,508,831,19]
+ CRUSH rule 0 x 515 [504,138,480,272]
+ CRUSH rule 0 x 516 [285,409,136,570]
+ CRUSH rule 0 x 517 [300,232,23,906]
+ CRUSH rule 0 x 518 [397,674,98,898]
+ CRUSH rule 0 x 519 [86,750,772,913]
+ CRUSH rule 0 x 520 [900,833,614,130]
+ CRUSH rule 0 x 521 [31,47,236,751]
+ CRUSH rule 0 x 522 [390,16,280,144]
+ CRUSH rule 0 x 523 [618,308,424,590]
+ CRUSH rule 0 x 524 [635,189,687,963]
+ CRUSH rule 0 x 525 [311,916,699,262]
+ CRUSH rule 0 x 526 [48,738,227,718]
+ CRUSH rule 0 x 527 [202,851,889,216]
+ CRUSH rule 0 x 528 [565,827,590,273]
+ CRUSH rule 0 x 529 [934,864,241,43]
+ CRUSH rule 0 x 530 [502,934,298,670]
+ CRUSH rule 0 x 531 [681,627,942,487]
+ CRUSH rule 0 x 532 [422,6,147,205]
+ CRUSH rule 0 x 533 [863,68,364,983]
+ CRUSH rule 0 x 534 [962,931,775,172]
+ CRUSH rule 0 x 535 [89,565,397,693]
+ CRUSH rule 0 x 536 [499,351,760,458]
+ CRUSH rule 0 x 537 [676,547,787,311]
+ CRUSH rule 0 x 538 [58,644,571,649]
+ CRUSH rule 0 x 539 [837,953,457,711]
+ CRUSH rule 0 x 540 [831,50,132,213]
+ CRUSH rule 0 x 541 [582,757,121,525]
+ CRUSH rule 0 x 542 [472,132,790,997]
+ CRUSH rule 0 x 543 [382,272,797,330]
+ CRUSH rule 0 x 544 [947,930,496,883]
+ CRUSH rule 0 x 545 [425,570,305,77]
+ CRUSH rule 0 x 546 [18,65,529,437]
+ CRUSH rule 0 x 547 [445,715,600,472]
+ CRUSH rule 0 x 548 [367,569,980,167]
+ CRUSH rule 0 x 549 [125,715,671,817]
+ CRUSH rule 0 x 550 [425,599,744,199]
+ CRUSH rule 0 x 551 [44,1,528,922]
+ CRUSH rule 0 x 552 [246,104,68,239]
+ CRUSH rule 0 x 553 [71,703,615,28]
+ CRUSH rule 0 x 554 [207,124,217,166]
+ CRUSH rule 0 x 555 [570,28,317,420]
+ CRUSH rule 0 x 556 [674,152,421,79]
+ CRUSH rule 0 x 557 [347,817,191,391]
+ CRUSH rule 0 x 558 [627,426,369,692]
+ CRUSH rule 0 x 559 [940,630,924,242]
+ CRUSH rule 0 x 560 [295,903,541,29]
+ CRUSH rule 0 x 561 [506,682,384,637]
+ CRUSH rule 0 x 562 [718,529,87,729]
+ CRUSH rule 0 x 563 [552,332,747,206]
+ CRUSH rule 0 x 564 [835,769,736,486]
+ CRUSH rule 0 x 565 [8,167,539,182]
+ CRUSH rule 0 x 566 [600,481,301,263]
+ CRUSH rule 0 x 567 [999,994,509,899]
+ CRUSH rule 0 x 568 [252,431,157,62]
+ CRUSH rule 0 x 569 [643,218,943,455]
+ CRUSH rule 0 x 570 [617,635,765,422]
+ CRUSH rule 0 x 571 [757,80,59,98]
+ CRUSH rule 0 x 572 [299,348,575,889]
+ CRUSH rule 0 x 573 [25,505,270,167]
+ CRUSH rule 0 x 574 [215,431,624,177]
+ CRUSH rule 0 x 575 [225,252,611,546]
+ CRUSH rule 0 x 576 [627,94,159,857]
+ CRUSH rule 0 x 577 [237,809,778,636]
+ CRUSH rule 0 x 578 [885,313,120,344]
+ CRUSH rule 0 x 579 [924,575,787,831]
+ CRUSH rule 0 x 580 [718,51,766,121]
+ CRUSH rule 0 x 581 [219,807,129,571]
+ CRUSH rule 0 x 582 [893,701,598,863]
+ CRUSH rule 0 x 583 [246,930,964,170]
+ CRUSH rule 0 x 584 [336,432,680,175]
+ CRUSH rule 0 x 585 [324,999,397,485]
+ CRUSH rule 0 x 586 [558,230,976,541]
+ CRUSH rule 0 x 587 [985,830,597,21]
+ CRUSH rule 0 x 588 [211,544,57,134]
+ CRUSH rule 0 x 589 [129,21,112,190]
+ CRUSH rule 0 x 590 [467,969,652,593]
+ CRUSH rule 0 x 591 [758,514,316,164]
+ CRUSH rule 0 x 592 [525,253,190,443]
+ CRUSH rule 0 x 593 [601,885,339,152]
+ CRUSH rule 0 x 594 [227,60,450,30]
+ CRUSH rule 0 x 595 [720,854,496,912]
+ CRUSH rule 0 x 596 [751,195,997,77]
+ CRUSH rule 0 x 597 [129,574,714,8]
+ CRUSH rule 0 x 598 [679,207,604,396]
+ CRUSH rule 0 x 599 [668,315,683,349]
+ CRUSH rule 0 x 600 [143,396,464,444]
+ CRUSH rule 0 x 601 [326,573,873,902]
+ CRUSH rule 0 x 602 [860,281,875,535]
+ CRUSH rule 0 x 603 [709,328,445,349]
+ CRUSH rule 0 x 604 [571,62,814,95]
+ CRUSH rule 0 x 605 [252,739,860,27]
+ CRUSH rule 0 x 606 [339,236,759,842]
+ CRUSH rule 0 x 607 [590,248,759,868]
+ CRUSH rule 0 x 608 [145,635,309,467]
+ CRUSH rule 0 x 609 [973,547,223,79]
+ CRUSH rule 0 x 610 [435,816,961,983]
+ CRUSH rule 0 x 611 [559,283,422,584]
+ CRUSH rule 0 x 612 [273,149,123,576]
+ CRUSH rule 0 x 613 [828,614,642,674]
+ CRUSH rule 0 x 614 [478,748,393,34]
+ CRUSH rule 0 x 615 [392,155,144,326]
+ CRUSH rule 0 x 616 [778,637,452,248]
+ CRUSH rule 0 x 617 [622,713,996,833]
+ CRUSH rule 0 x 618 [149,877,270,329]
+ CRUSH rule 0 x 619 [604,163,656,409]
+ CRUSH rule 0 x 620 [181,23,409,198]
+ CRUSH rule 0 x 621 [735,902,386,237]
+ CRUSH rule 0 x 622 [661,824,717,568]
+ CRUSH rule 0 x 623 [142,121,643,61]
+ CRUSH rule 0 x 624 [360,716,420,398]
+ CRUSH rule 0 x 625 [541,167,385,1]
+ CRUSH rule 0 x 626 [364,431,610,363]
+ CRUSH rule 0 x 627 [458,137,557,410]
+ CRUSH rule 0 x 628 [250,350,556,497]
+ CRUSH rule 0 x 629 [928,160,710,572]
+ CRUSH rule 0 x 630 [243,19,918,556]
+ CRUSH rule 0 x 631 [438,221,574,676]
+ CRUSH rule 0 x 632 [797,368,247,5]
+ CRUSH rule 0 x 633 [993,749,525,485]
+ CRUSH rule 0 x 634 [239,351,633,299]
+ CRUSH rule 0 x 635 [640,965,25,961]
+ CRUSH rule 0 x 636 [173,290,297,991]
+ CRUSH rule 0 x 637 [0,918,98,108]
+ CRUSH rule 0 x 638 [702,235,424,900]
+ CRUSH rule 0 x 639 [475,687,31,785]
+ CRUSH rule 0 x 640 [31,664,399,677]
+ CRUSH rule 0 x 641 [296,473,108,963]
+ CRUSH rule 0 x 642 [894,273,427,606]
+ CRUSH rule 0 x 643 [117,111,732,191]
+ CRUSH rule 0 x 644 [438,336,327,512]
+ CRUSH rule 0 x 645 [982,702,351,573]
+ CRUSH rule 0 x 646 [334,804,146,842]
+ CRUSH rule 0 x 647 [933,787,185,334]
+ CRUSH rule 0 x 648 [22,444,400,862]
+ CRUSH rule 0 x 649 [503,229,213,460]
+ CRUSH rule 0 x 650 [328,659,420,443]
+ CRUSH rule 0 x 651 [3,880,823,123]
+ CRUSH rule 0 x 652 [495,977,563,733]
+ CRUSH rule 0 x 653 [185,718,804,280]
+ CRUSH rule 0 x 654 [130,528,380,81]
+ CRUSH rule 0 x 655 [560,872,454,504]
+ CRUSH rule 0 x 656 [219,885,178,981]
+ CRUSH rule 0 x 657 [233,684,813,490]
+ CRUSH rule 0 x 658 [778,6,756,380]
+ CRUSH rule 0 x 659 [240,663,306,540]
+ CRUSH rule 0 x 660 [244,855,196,147]
+ CRUSH rule 0 x 661 [184,270,128,398]
+ CRUSH rule 0 x 662 [65,883,921,438]
+ CRUSH rule 0 x 663 [323,721,594,812]
+ CRUSH rule 0 x 664 [865,113,512,51]
+ CRUSH rule 0 x 665 [420,850,591,475]
+ CRUSH rule 0 x 666 [319,767,246,3]
+ CRUSH rule 0 x 667 [875,39,343,100]
+ CRUSH rule 0 x 668 [331,122,263,599]
+ CRUSH rule 0 x 669 [915,521,402,747]
+ CRUSH rule 0 x 670 [845,659,943,447]
+ CRUSH rule 0 x 671 [108,634,527,363]
+ CRUSH rule 0 x 672 [578,216,110,589]
+ CRUSH rule 0 x 673 [442,74,579,797]
+ CRUSH rule 0 x 674 [588,364,281,308]
+ CRUSH rule 0 x 675 [489,698,744,671]
+ CRUSH rule 0 x 676 [928,911,40,180]
+ CRUSH rule 0 x 677 [399,269,692,131]
+ CRUSH rule 0 x 678 [546,752,544,155]
+ CRUSH rule 0 x 679 [988,25,275,433]
+ CRUSH rule 0 x 680 [335,963,382,486]
+ CRUSH rule 0 x 681 [690,462,623,466]
+ CRUSH rule 0 x 682 [196,588,154,257]
+ CRUSH rule 0 x 683 [627,25,421,160]
+ CRUSH rule 0 x 684 [38,804,592,158]
+ CRUSH rule 0 x 685 [841,368,548,362]
+ CRUSH rule 0 x 686 [336,287,525,440]
+ CRUSH rule 0 x 687 [20,682,924,653]
+ CRUSH rule 0 x 688 [463,371,780,556]
+ CRUSH rule 0 x 689 [569,250,78,816]
+ CRUSH rule 0 x 690 [551,144,587,263]
+ CRUSH rule 0 x 691 [766,464,446,533]
+ CRUSH rule 0 x 692 [739,634,18,245]
+ CRUSH rule 0 x 693 [339,297,118,330]
+ CRUSH rule 0 x 694 [405,26,830,181]
+ CRUSH rule 0 x 695 [622,576,597,535]
+ CRUSH rule 0 x 696 [558,902,689,13]
+ CRUSH rule 0 x 697 [818,222,406,691]
+ CRUSH rule 0 x 698 [178,48,402,233]
+ CRUSH rule 0 x 699 [450,244,180,919]
+ CRUSH rule 0 x 700 [502,771,987,706]
+ CRUSH rule 0 x 701 [4,612,782,216]
+ CRUSH rule 0 x 702 [177,630,232,923]
+ CRUSH rule 0 x 703 [354,178,389,393]
+ CRUSH rule 0 x 704 [646,601,156,171]
+ CRUSH rule 0 x 705 [921,401,890,265]
+ CRUSH rule 0 x 706 [652,877,562,452]
+ CRUSH rule 0 x 707 [345,745,67,716]
+ CRUSH rule 0 x 708 [333,607,180,469]
+ CRUSH rule 0 x 709 [45,187,302,115]
+ CRUSH rule 0 x 710 [94,855,43,199]
+ CRUSH rule 0 x 711 [227,653,731,150]
+ CRUSH rule 0 x 712 [398,953,136,870]
+ CRUSH rule 0 x 713 [116,800,503,662]
+ CRUSH rule 0 x 714 [111,629,866,709]
+ CRUSH rule 0 x 715 [531,291,486,382]
+ CRUSH rule 0 x 716 [169,541,291,42]
+ CRUSH rule 0 x 717 [417,446,994,894]
+ CRUSH rule 0 x 718 [992,383,298,844]
+ CRUSH rule 0 x 719 [936,674,324,759]
+ CRUSH rule 0 x 720 [370,188,174,464]
+ CRUSH rule 0 x 721 [320,859,278,259]
+ CRUSH rule 0 x 722 [7,2,673,129]
+ CRUSH rule 0 x 723 [270,553,831,662]
+ CRUSH rule 0 x 724 [666,822,708,895]
+ CRUSH rule 0 x 725 [794,406,875,459]
+ CRUSH rule 0 x 726 [420,556,341,292]
+ CRUSH rule 0 x 727 [561,461,129,635]
+ CRUSH rule 0 x 728 [951,330,196,756]
+ CRUSH rule 0 x 729 [656,644,436,591]
+ CRUSH rule 0 x 730 [3,558,629,184]
+ CRUSH rule 0 x 731 [852,89,75,735]
+ CRUSH rule 0 x 732 [983,840,869,976]
+ CRUSH rule 0 x 733 [285,396,388,122]
+ CRUSH rule 0 x 734 [125,510,402,640]
+ CRUSH rule 0 x 735 [417,773,686,504]
+ CRUSH rule 0 x 736 [749,396,632,550]
+ CRUSH rule 0 x 737 [644,991,946,135]
+ CRUSH rule 0 x 738 [449,683,290,220]
+ CRUSH rule 0 x 739 [341,220,641,454]
+ CRUSH rule 0 x 740 [874,524,674,650]
+ CRUSH rule 0 x 741 [189,472,712,798]
+ CRUSH rule 0 x 742 [912,581,114,730]
+ CRUSH rule 0 x 743 [654,914,425,441]
+ CRUSH rule 0 x 744 [725,295,579,377]
+ CRUSH rule 0 x 745 [787,858,850,506]
+ CRUSH rule 0 x 746 [757,848,704,30]
+ CRUSH rule 0 x 747 [700,81,867,681]
+ CRUSH rule 0 x 748 [557,436,238,664]
+ CRUSH rule 0 x 749 [772,622,337,42]
+ CRUSH rule 0 x 750 [946,97,376,677]
+ CRUSH rule 0 x 751 [996,618,343,911]
+ CRUSH rule 0 x 752 [746,887,695,868]
+ CRUSH rule 0 x 753 [741,14,463,479]
+ CRUSH rule 0 x 754 [648,349,333,355]
+ CRUSH rule 0 x 755 [157,460,466,187]
+ CRUSH rule 0 x 756 [416,97,197,497]
+ CRUSH rule 0 x 757 [599,839,776,410]
+ CRUSH rule 0 x 758 [994,218,620,256]
+ CRUSH rule 0 x 759 [959,682,514,745]
+ CRUSH rule 0 x 760 [518,943,215,83]
+ CRUSH rule 0 x 761 [285,849,420,324]
+ CRUSH rule 0 x 762 [591,313,41,335]
+ CRUSH rule 0 x 763 [908,411,200,740]
+ CRUSH rule 0 x 764 [787,234,894,485]
+ CRUSH rule 0 x 765 [327,921,882,393]
+ CRUSH rule 0 x 766 [84,161,878,704]
+ CRUSH rule 0 x 767 [370,895,702,701]
+ CRUSH rule 0 x 768 [826,760,879,864]
+ CRUSH rule 0 x 769 [67,768,663,735]
+ CRUSH rule 0 x 770 [593,909,482,259]
+ CRUSH rule 0 x 771 [309,935,121,578]
+ CRUSH rule 0 x 772 [12,125,797,301]
+ CRUSH rule 0 x 773 [253,466,820,549]
+ CRUSH rule 0 x 774 [164,390,705,109]
+ CRUSH rule 0 x 775 [703,47,43,973]
+ CRUSH rule 0 x 776 [728,231,80,916]
+ CRUSH rule 0 x 777 [981,621,568,729]
+ CRUSH rule 0 x 778 [411,456,544,597]
+ CRUSH rule 0 x 779 [346,121,519,921]
+ CRUSH rule 0 x 780 [476,39,288,381]
+ CRUSH rule 0 x 781 [10,130,585,844]
+ CRUSH rule 0 x 782 [462,246,581,902]
+ CRUSH rule 0 x 783 [580,373,153,775]
+ CRUSH rule 0 x 784 [413,113,978,990]
+ CRUSH rule 0 x 785 [341,856,332,354]
+ CRUSH rule 0 x 786 [411,140,313,393]
+ CRUSH rule 0 x 787 [605,522,211,813]
+ CRUSH rule 0 x 788 [226,545,35,142]
+ CRUSH rule 0 x 789 [545,320,414,702]
+ CRUSH rule 0 x 790 [414,748,816,327]
+ CRUSH rule 0 x 791 [660,906,406,697]
+ CRUSH rule 0 x 792 [287,392,514,204]
+ CRUSH rule 0 x 793 [631,133,850,713]
+ CRUSH rule 0 x 794 [931,517,543,210]
+ CRUSH rule 0 x 795 [551,962,477,948]
+ CRUSH rule 0 x 796 [814,4,95,27]
+ CRUSH rule 0 x 797 [64,201,299,734]
+ CRUSH rule 0 x 798 [422,530,114,431]
+ CRUSH rule 0 x 799 [824,32,679,562]
+ CRUSH rule 0 x 800 [862,623,489,637]
+ CRUSH rule 0 x 801 [145,550,329,324]
+ CRUSH rule 0 x 802 [570,19,847,308]
+ CRUSH rule 0 x 803 [151,812,662,358]
+ CRUSH rule 0 x 804 [467,93,264,863]
+ CRUSH rule 0 x 805 [621,223,938,809]
+ CRUSH rule 0 x 806 [898,957,805,430]
+ CRUSH rule 0 x 807 [354,531,422,159]
+ CRUSH rule 0 x 808 [7,96,76,897]
+ CRUSH rule 0 x 809 [70,734,719,56]
+ CRUSH rule 0 x 810 [701,18,972,327]
+ CRUSH rule 0 x 811 [248,547,103,728]
+ CRUSH rule 0 x 812 [230,576,821,566]
+ CRUSH rule 0 x 813 [805,114,683,629]
+ CRUSH rule 0 x 814 [54,619,973,741]
+ CRUSH rule 0 x 815 [679,412,613,132]
+ CRUSH rule 0 x 816 [919,448,826,414]
+ CRUSH rule 0 x 817 [765,830,436,521]
+ CRUSH rule 0 x 818 [415,566,644,687]
+ CRUSH rule 0 x 819 [721,319,865,750]
+ CRUSH rule 0 x 820 [218,301,333,190]
+ CRUSH rule 0 x 821 [185,795,680,953]
+ CRUSH rule 0 x 822 [356,261,54,522]
+ CRUSH rule 0 x 823 [220,281,549,456]
+ CRUSH rule 0 x 824 [292,809,887,74]
+ CRUSH rule 0 x 825 [949,778,101,311]
+ CRUSH rule 0 x 826 [767,818,833,927]
+ CRUSH rule 0 x 827 [631,83,406,635]
+ CRUSH rule 0 x 828 [288,986,445,26]
+ CRUSH rule 0 x 829 [990,667,915,694]
+ CRUSH rule 0 x 830 [152,571,778,505]
+ CRUSH rule 0 x 831 [814,563,630,97]
+ CRUSH rule 0 x 832 [235,641,616,110]
+ CRUSH rule 0 x 833 [657,565,922,140]
+ CRUSH rule 0 x 834 [907,231,644,13]
+ CRUSH rule 0 x 835 [784,262,771,264]
+ CRUSH rule 0 x 836 [951,158,366,710]
+ CRUSH rule 0 x 837 [556,498,334,633]
+ CRUSH rule 0 x 838 [329,274,964,547]
+ CRUSH rule 0 x 839 [568,209,939,364]
+ CRUSH rule 0 x 840 [45,579,842,70]
+ CRUSH rule 0 x 841 [652,702,24,605]
+ CRUSH rule 0 x 842 [629,984,314,895]
+ CRUSH rule 0 x 843 [799,690,688,648]
+ CRUSH rule 0 x 844 [694,600,534,700]
+ CRUSH rule 0 x 845 [332,30,179,93]
+ CRUSH rule 0 x 846 [452,251,712,719]
+ CRUSH rule 0 x 847 [399,681,847,739]
+ CRUSH rule 0 x 848 [303,138,440,346]
+ CRUSH rule 0 x 849 [666,346,708,873]
+ CRUSH rule 0 x 850 [644,511,345,844]
+ CRUSH rule 0 x 851 [527,546,737,425]
+ CRUSH rule 0 x 852 [31,809,94,618]
+ CRUSH rule 0 x 853 [483,330,869,184]
+ CRUSH rule 0 x 854 [697,953,968,143]
+ CRUSH rule 0 x 855 [837,996,239,621]
+ CRUSH rule 0 x 856 [712,40,547,430]
+ CRUSH rule 0 x 857 [77,984,576,551]
+ CRUSH rule 0 x 858 [412,384,841,465]
+ CRUSH rule 0 x 859 [173,760,26,300]
+ CRUSH rule 0 x 860 [776,429,328,917]
+ CRUSH rule 0 x 861 [705,405,477,50]
+ CRUSH rule 0 x 862 [809,44,788,938]
+ CRUSH rule 0 x 863 [349,496,963,178]
+ CRUSH rule 0 x 864 [717,858,101,239]
+ CRUSH rule 0 x 865 [857,603,586,262]
+ CRUSH rule 0 x 866 [394,304,71,96]
+ CRUSH rule 0 x 867 [640,773,663,974]
+ CRUSH rule 0 x 868 [613,950,712,663]
+ CRUSH rule 0 x 869 [973,889,524,22]
+ CRUSH rule 0 x 870 [505,35,386,498]
+ CRUSH rule 0 x 871 [239,264,262,773]
+ CRUSH rule 0 x 872 [21,767,456,748]
+ CRUSH rule 0 x 873 [954,666,980,264]
+ CRUSH rule 0 x 874 [54,510,947,1]
+ CRUSH rule 0 x 875 [809,418,452,462]
+ CRUSH rule 0 x 876 [483,457,61,248]
+ CRUSH rule 0 x 877 [542,531,952,939]
+ CRUSH rule 0 x 878 [217,674,857,644]
+ CRUSH rule 0 x 879 [999,475,134,250]
+ CRUSH rule 0 x 880 [678,573,935,385]
+ CRUSH rule 0 x 881 [394,835,789,802]
+ CRUSH rule 0 x 882 [467,382,353,56]
+ CRUSH rule 0 x 883 [802,744,237,337]
+ CRUSH rule 0 x 884 [653,660,638,700]
+ CRUSH rule 0 x 885 [898,704,307,445]
+ CRUSH rule 0 x 886 [434,357,938,641]
+ CRUSH rule 0 x 887 [297,226,711,428]
+ CRUSH rule 0 x 888 [863,324,443,213]
+ CRUSH rule 0 x 889 [105,102,308,163]
+ CRUSH rule 0 x 890 [550,248,606,704]
+ CRUSH rule 0 x 891 [575,928,880,891]
+ CRUSH rule 0 x 892 [259,862,133,271]
+ CRUSH rule 0 x 893 [902,880,543,542]
+ CRUSH rule 0 x 894 [180,169,916,43]
+ CRUSH rule 0 x 895 [725,849,182,129]
+ CRUSH rule 0 x 896 [951,34,874,537]
+ CRUSH rule 0 x 897 [810,352,73,939]
+ CRUSH rule 0 x 898 [979,433,719,411]
+ CRUSH rule 0 x 899 [685,668,534,932]
+ CRUSH rule 0 x 900 [530,978,41,894]
+ CRUSH rule 0 x 901 [740,107,336,175]
+ CRUSH rule 0 x 902 [800,743,693,310]
+ CRUSH rule 0 x 903 [230,267,842,266]
+ CRUSH rule 0 x 904 [346,949,460,973]
+ CRUSH rule 0 x 905 [530,397,619,958]
+ CRUSH rule 0 x 906 [80,426,138,672]
+ CRUSH rule 0 x 907 [365,968,475,297]
+ CRUSH rule 0 x 908 [204,832,742,809]
+ CRUSH rule 0 x 909 [883,989,146,959]
+ CRUSH rule 0 x 910 [549,593,249,853]
+ CRUSH rule 0 x 911 [325,847,352,214]
+ CRUSH rule 0 x 912 [874,888,582,796]
+ CRUSH rule 0 x 913 [331,463,342,574]
+ CRUSH rule 0 x 914 [836,468,601,732]
+ CRUSH rule 0 x 915 [245,228,100,661]
+ CRUSH rule 0 x 916 [77,967,364,435]
+ CRUSH rule 0 x 917 [239,60,866,221]
+ CRUSH rule 0 x 918 [988,115,922,80]
+ CRUSH rule 0 x 919 [783,139,696,1]
+ CRUSH rule 0 x 920 [623,408,685,953]
+ CRUSH rule 0 x 921 [105,799,144,90]
+ CRUSH rule 0 x 922 [887,505,652,348]
+ CRUSH rule 0 x 923 [223,318,552,458]
+ CRUSH rule 0 x 924 [25,778,366,333]
+ CRUSH rule 0 x 925 [912,601,297,682]
+ CRUSH rule 0 x 926 [968,133,144,814]
+ CRUSH rule 0 x 927 [277,724,214,988]
+ CRUSH rule 0 x 928 [554,203,658,789]
+ CRUSH rule 0 x 929 [761,802,367,528]
+ CRUSH rule 0 x 930 [814,61,788,736]
+ CRUSH rule 0 x 931 [29,193,61,41]
+ CRUSH rule 0 x 932 [446,198,862,534]
+ CRUSH rule 0 x 933 [352,742,216,321]
+ CRUSH rule 0 x 934 [730,2,332,631]
+ CRUSH rule 0 x 935 [731,23,736,79]
+ CRUSH rule 0 x 936 [322,975,20,904]
+ CRUSH rule 0 x 937 [822,221,841,161]
+ CRUSH rule 0 x 938 [557,850,66,630]
+ CRUSH rule 0 x 939 [150,11,971,371]
+ CRUSH rule 0 x 940 [638,398,169,616]
+ CRUSH rule 0 x 941 [730,342,929,577]
+ CRUSH rule 0 x 942 [62,292,166,814]
+ CRUSH rule 0 x 943 [165,314,519,548]
+ CRUSH rule 0 x 944 [199,625,766,176]
+ CRUSH rule 0 x 945 [946,999,699,303]
+ CRUSH rule 0 x 946 [595,93,852,142]
+ CRUSH rule 0 x 947 [800,582,356,93]
+ CRUSH rule 0 x 948 [132,551,139,920]
+ CRUSH rule 0 x 949 [792,920,466,380]
+ CRUSH rule 0 x 950 [111,345,176,543]
+ CRUSH rule 0 x 951 [414,619,648,655]
+ CRUSH rule 0 x 952 [775,469,500,356]
+ CRUSH rule 0 x 953 [349,1,5,251]
+ CRUSH rule 0 x 954 [570,940,410,249]
+ CRUSH rule 0 x 955 [729,774,823,800]
+ CRUSH rule 0 x 956 [519,141,575,625]
+ CRUSH rule 0 x 957 [242,709,611,97]
+ CRUSH rule 0 x 958 [84,217,227,253]
+ CRUSH rule 0 x 959 [270,413,918,789]
+ CRUSH rule 0 x 960 [458,192,307,279]
+ CRUSH rule 0 x 961 [981,388,777,546]
+ CRUSH rule 0 x 962 [623,834,277,134]
+ CRUSH rule 0 x 963 [291,167,714,468]
+ CRUSH rule 0 x 964 [28,156,788,127]
+ CRUSH rule 0 x 965 [675,557,290,517]
+ CRUSH rule 0 x 966 [836,306,946,283]
+ CRUSH rule 0 x 967 [966,386,735,837]
+ CRUSH rule 0 x 968 [864,756,690,121]
+ CRUSH rule 0 x 969 [729,625,480,769]
+ CRUSH rule 0 x 970 [800,362,646,582]
+ CRUSH rule 0 x 971 [737,381,153,684]
+ CRUSH rule 0 x 972 [952,245,720,884]
+ CRUSH rule 0 x 973 [356,455,579,857]
+ CRUSH rule 0 x 974 [545,758,586,596]
+ CRUSH rule 0 x 975 [336,191,202,146]
+ CRUSH rule 0 x 976 [446,208,757,620]
+ CRUSH rule 0 x 977 [202,896,196,956]
+ CRUSH rule 0 x 978 [612,324,996,225]
+ CRUSH rule 0 x 979 [843,457,675,650]
+ CRUSH rule 0 x 980 [60,914,881,626]
+ CRUSH rule 0 x 981 [702,749,937,153]
+ CRUSH rule 0 x 982 [298,928,738,167]
+ CRUSH rule 0 x 983 [723,572,395,358]
+ CRUSH rule 0 x 984 [723,864,804,935]
+ CRUSH rule 0 x 985 [945,459,868,211]
+ CRUSH rule 0 x 986 [772,664,535,169]
+ CRUSH rule 0 x 987 [88,324,312,843]
+ CRUSH rule 0 x 988 [522,927,131,996]
+ CRUSH rule 0 x 989 [578,332,208,605]
+ CRUSH rule 0 x 990 [638,228,414,311]
+ CRUSH rule 0 x 991 [530,221,451,422]
+ CRUSH rule 0 x 992 [925,705,275,81]
+ CRUSH rule 0 x 993 [991,301,43,469]
+ CRUSH rule 0 x 994 [276,51,868,683]
+ CRUSH rule 0 x 995 [288,836,753,790]
+ CRUSH rule 0 x 996 [887,983,252,686]
+ CRUSH rule 0 x 997 [110,924,386,79]
+ CRUSH rule 0 x 998 [435,830,485,853]
+ CRUSH rule 0 x 999 [876,738,357,913]
+ CRUSH rule 0 x 1000 [178,963,638,430]
+ CRUSH rule 0 x 1001 [99,519,66,759]
+ CRUSH rule 0 x 1002 [515,534,468,866]
+ CRUSH rule 0 x 1003 [104,611,937,698]
+ CRUSH rule 0 x 1004 [269,638,724,375]
+ CRUSH rule 0 x 1005 [369,223,309,409]
+ CRUSH rule 0 x 1006 [40,107,69,275]
+ CRUSH rule 0 x 1007 [978,111,416,758]
+ CRUSH rule 0 x 1008 [965,956,624,832]
+ CRUSH rule 0 x 1009 [598,476,356,695]
+ CRUSH rule 0 x 1010 [767,523,239,517]
+ CRUSH rule 0 x 1011 [289,871,207,576]
+ CRUSH rule 0 x 1012 [128,28,370,31]
+ CRUSH rule 0 x 1013 [979,765,660,812]
+ CRUSH rule 0 x 1014 [979,948,513,88]
+ CRUSH rule 0 x 1015 [277,790,396,672]
+ CRUSH rule 0 x 1016 [262,73,128,886]
+ CRUSH rule 0 x 1017 [150,269,61,499]
+ CRUSH rule 0 x 1018 [555,829,554,944]
+ CRUSH rule 0 x 1019 [513,356,265,446]
+ CRUSH rule 0 x 1020 [158,161,877,704]
+ CRUSH rule 0 x 1021 [915,998,957,285]
+ CRUSH rule 0 x 1022 [967,829,973,640]
+ CRUSH rule 0 x 1023 [488,257,614,859]
+ rule 0 (data) num_rep 4 result size == 4:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604]
+ CRUSH rule 0 x 1 [876,250,334,633,744]
+ CRUSH rule 0 x 2 [292,832,53,392,386]
+ CRUSH rule 0 x 3 [623,387,124,998,749]
+ CRUSH rule 0 x 4 [61,334,710,4,994]
+ CRUSH rule 0 x 5 [946,557,713,664,141]
+ CRUSH rule 0 x 6 [576,668,212,163,732]
+ CRUSH rule 0 x 7 [645,753,906,393,341]
+ CRUSH rule 0 x 8 [243,6,863,781,211]
+ CRUSH rule 0 x 9 [22,578,251,410,297]
+ CRUSH rule 0 x 10 [758,828,360,477,821]
+ CRUSH rule 0 x 11 [769,120,124,527,119]
+ CRUSH rule 0 x 12 [780,364,689,755,675]
+ CRUSH rule 0 x 13 [557,18,351,719,742]
+ CRUSH rule 0 x 14 [59,561,249,461,971]
+ CRUSH rule 0 x 15 [718,928,993,21,76]
+ CRUSH rule 0 x 16 [673,632,841,954,788]
+ CRUSH rule 0 x 17 [648,43,560,514,142]
+ CRUSH rule 0 x 18 [654,219,181,568,381]
+ CRUSH rule 0 x 19 [850,545,377,848,863]
+ CRUSH rule 0 x 20 [717,785,974,5,225]
+ CRUSH rule 0 x 21 [420,57,519,306,312]
+ CRUSH rule 0 x 22 [503,998,193,821,634]
+ CRUSH rule 0 x 23 [411,663,168,110,899]
+ CRUSH rule 0 x 24 [266,861,353,1,456]
+ CRUSH rule 0 x 25 [760,483,818,600,509]
+ CRUSH rule 0 x 26 [903,24,573,718,112]
+ CRUSH rule 0 x 27 [946,188,289,510,687]
+ CRUSH rule 0 x 28 [69,312,73,198,256]
+ CRUSH rule 0 x 29 [844,883,337,628,496]
+ CRUSH rule 0 x 30 [621,18,613,794,910]
+ CRUSH rule 0 x 31 [784,943,814,539,962]
+ CRUSH rule 0 x 32 [173,374,369,972,315]
+ CRUSH rule 0 x 33 [698,336,357,966,582]
+ CRUSH rule 0 x 34 [168,836,210,798,904]
+ CRUSH rule 0 x 35 [274,509,534,818,912]
+ CRUSH rule 0 x 36 [318,215,153,628,87]
+ CRUSH rule 0 x 37 [173,604,109,935,203]
+ CRUSH rule 0 x 38 [708,444,683,604,722]
+ CRUSH rule 0 x 39 [662,198,417,680,226]
+ CRUSH rule 0 x 40 [620,801,414,78,560]
+ CRUSH rule 0 x 41 [811,264,177,127,148]
+ CRUSH rule 0 x 42 [863,179,527,660,133]
+ CRUSH rule 0 x 43 [686,822,988,228,791]
+ CRUSH rule 0 x 44 [396,222,46,841,536]
+ CRUSH rule 0 x 45 [991,694,253,142,54]
+ CRUSH rule 0 x 46 [420,909,184,285,508]
+ CRUSH rule 0 x 47 [467,211,605,207,241]
+ CRUSH rule 0 x 48 [955,329,368,168,698]
+ CRUSH rule 0 x 49 [974,891,931,29,813]
+ CRUSH rule 0 x 50 [870,441,691,823,761]
+ CRUSH rule 0 x 51 [182,930,25,936,97]
+ CRUSH rule 0 x 52 [704,812,894,794,481]
+ CRUSH rule 0 x 53 [185,713,631,280,345]
+ CRUSH rule 0 x 54 [270,441,100,82,983]
+ CRUSH rule 0 x 55 [895,734,958,793,651]
+ CRUSH rule 0 x 56 [564,963,683,324,40]
+ CRUSH rule 0 x 57 [738,130,208,973,498]
+ CRUSH rule 0 x 58 [524,113,806,903,531]
+ CRUSH rule 0 x 59 [408,337,668,529,34]
+ CRUSH rule 0 x 60 [228,790,857,309,616]
+ CRUSH rule 0 x 61 [154,843,717,467,883]
+ CRUSH rule 0 x 62 [594,811,549,276,693]
+ CRUSH rule 0 x 63 [646,67,884,925,941]
+ CRUSH rule 0 x 64 [175,542,155,837,594]
+ CRUSH rule 0 x 65 [745,619,131,867,269]
+ CRUSH rule 0 x 66 [275,468,23,35,328]
+ CRUSH rule 0 x 67 [246,958,524,493,636]
+ CRUSH rule 0 x 68 [711,473,403,228,835]
+ CRUSH rule 0 x 69 [493,924,850,939,950]
+ CRUSH rule 0 x 70 [30,499,644,33,804]
+ CRUSH rule 0 x 71 [984,883,574,716,575]
+ CRUSH rule 0 x 72 [71,286,942,363,628]
+ CRUSH rule 0 x 73 [922,618,3,371,464]
+ CRUSH rule 0 x 74 [629,414,185,573,678]
+ CRUSH rule 0 x 75 [222,20,174,820,312]
+ CRUSH rule 0 x 76 [262,366,339,290,718]
+ CRUSH rule 0 x 77 [638,469,992,280,773]
+ CRUSH rule 0 x 78 [324,511,788,7,308]
+ CRUSH rule 0 x 79 [577,990,64,94,447]
+ CRUSH rule 0 x 80 [501,95,278,903,631]
+ CRUSH rule 0 x 81 [506,812,9,698,173]
+ CRUSH rule 0 x 82 [222,145,80,785,835]
+ CRUSH rule 0 x 83 [71,634,61,91,856]
+ CRUSH rule 0 x 84 [49,761,773,368,318]
+ CRUSH rule 0 x 85 [985,896,708,861,325]
+ CRUSH rule 0 x 86 [537,745,93,524,466]
+ CRUSH rule 0 x 87 [997,317,463,626,685]
+ CRUSH rule 0 x 88 [957,350,890,857,375]
+ CRUSH rule 0 x 89 [399,730,148,314,159]
+ CRUSH rule 0 x 90 [943,706,683,267,579]
+ CRUSH rule 0 x 91 [22,368,149,928,140]
+ CRUSH rule 0 x 92 [532,424,426,773,623]
+ CRUSH rule 0 x 93 [218,489,405,681,549]
+ CRUSH rule 0 x 94 [181,96,102,515,776]
+ CRUSH rule 0 x 95 [343,957,820,139,334]
+ CRUSH rule 0 x 96 [861,270,87,797,0]
+ CRUSH rule 0 x 97 [459,706,45,328,274]
+ CRUSH rule 0 x 98 [327,867,353,948,728]
+ CRUSH rule 0 x 99 [974,133,468,906,235]
+ CRUSH rule 0 x 100 [32,445,547,371,960]
+ CRUSH rule 0 x 101 [142,90,337,950,970]
+ CRUSH rule 0 x 102 [172,129,139,22,403]
+ CRUSH rule 0 x 103 [630,47,161,356,911]
+ CRUSH rule 0 x 104 [758,133,278,11,947]
+ CRUSH rule 0 x 105 [843,604,47,33,401]
+ CRUSH rule 0 x 106 [28,681,193,679,990]
+ CRUSH rule 0 x 107 [74,320,85,819,315]
+ CRUSH rule 0 x 108 [875,593,575,517,107]
+ CRUSH rule 0 x 109 [411,985,811,720,198]
+ CRUSH rule 0 x 110 [440,774,799,660,715]
+ CRUSH rule 0 x 111 [405,742,276,359,936]
+ CRUSH rule 0 x 112 [143,181,922,545,185]
+ CRUSH rule 0 x 113 [153,846,160,903,789]
+ CRUSH rule 0 x 114 [804,892,939,20,312]
+ CRUSH rule 0 x 115 [588,508,958,580,232]
+ CRUSH rule 0 x 116 [327,148,637,486,712]
+ CRUSH rule 0 x 117 [95,594,989,131,714]
+ CRUSH rule 0 x 118 [80,957,897,239,359]
+ CRUSH rule 0 x 119 [386,932,951,768,679]
+ CRUSH rule 0 x 120 [366,312,653,936,71]
+ CRUSH rule 0 x 121 [129,154,847,16,471]
+ CRUSH rule 0 x 122 [873,1,110,939,90]
+ CRUSH rule 0 x 123 [533,415,789,600,713]
+ CRUSH rule 0 x 124 [461,691,898,723,957]
+ CRUSH rule 0 x 125 [342,599,830,402,615]
+ CRUSH rule 0 x 126 [819,781,822,548,279]
+ CRUSH rule 0 x 127 [437,893,585,707,353]
+ CRUSH rule 0 x 128 [679,994,982,550,991]
+ CRUSH rule 0 x 129 [380,685,947,302,698]
+ CRUSH rule 0 x 130 [992,52,466,867,998]
+ CRUSH rule 0 x 131 [469,90,208,599,829]
+ CRUSH rule 0 x 132 [571,250,316,535,54]
+ CRUSH rule 0 x 133 [964,728,329,902,108]
+ CRUSH rule 0 x 134 [999,19,716,963,323]
+ CRUSH rule 0 x 135 [634,101,52,938,413]
+ CRUSH rule 0 x 136 [114,889,692,768,694]
+ CRUSH rule 0 x 137 [839,8,959,280,922]
+ CRUSH rule 0 x 138 [967,949,138,451,292]
+ CRUSH rule 0 x 139 [308,711,736,247,632]
+ CRUSH rule 0 x 140 [764,936,926,55,331]
+ CRUSH rule 0 x 141 [423,302,112,216,603]
+ CRUSH rule 0 x 142 [252,821,715,340,635]
+ CRUSH rule 0 x 143 [33,808,518,477,325]
+ CRUSH rule 0 x 144 [472,88,969,162,401]
+ CRUSH rule 0 x 145 [242,208,252,604,266]
+ CRUSH rule 0 x 146 [290,70,570,384,934]
+ CRUSH rule 0 x 147 [447,352,657,493,467]
+ CRUSH rule 0 x 148 [212,644,432,658,109]
+ CRUSH rule 0 x 149 [9,775,87,35,260]
+ CRUSH rule 0 x 150 [166,456,582,144,324]
+ CRUSH rule 0 x 151 [811,875,307,20,782]
+ CRUSH rule 0 x 152 [449,617,223,9,182]
+ CRUSH rule 0 x 153 [523,537,695,627,959]
+ CRUSH rule 0 x 154 [208,559,874,597,243]
+ CRUSH rule 0 x 155 [569,325,192,296,367]
+ CRUSH rule 0 x 156 [488,121,521,213,595]
+ CRUSH rule 0 x 157 [140,723,633,260,487]
+ CRUSH rule 0 x 158 [786,451,320,239,667]
+ CRUSH rule 0 x 159 [134,664,517,821,667]
+ CRUSH rule 0 x 160 [690,112,414,990,183]
+ CRUSH rule 0 x 161 [324,912,397,423,991]
+ CRUSH rule 0 x 162 [748,567,284,183,463]
+ CRUSH rule 0 x 163 [575,499,31,816,749]
+ CRUSH rule 0 x 164 [314,489,308,326,51]
+ CRUSH rule 0 x 165 [116,209,750,53,813]
+ CRUSH rule 0 x 166 [352,706,701,810,718]
+ CRUSH rule 0 x 167 [27,743,174,142,551]
+ CRUSH rule 0 x 168 [953,898,880,660,500]
+ CRUSH rule 0 x 169 [912,147,266,547,331]
+ CRUSH rule 0 x 170 [421,515,828,844,151]
+ CRUSH rule 0 x 171 [488,584,880,964,936]
+ CRUSH rule 0 x 172 [366,443,957,66,162]
+ CRUSH rule 0 x 173 [863,291,625,287,158]
+ CRUSH rule 0 x 174 [263,555,650,410,339]
+ CRUSH rule 0 x 175 [875,961,361,575,33]
+ CRUSH rule 0 x 176 [745,83,701,680,250]
+ CRUSH rule 0 x 177 [128,244,41,123,422]
+ CRUSH rule 0 x 178 [155,41,264,777,314]
+ CRUSH rule 0 x 179 [593,833,202,183,971]
+ CRUSH rule 0 x 180 [154,734,17,831,824]
+ CRUSH rule 0 x 181 [289,675,723,800,166]
+ CRUSH rule 0 x 182 [730,931,560,209,943]
+ CRUSH rule 0 x 183 [639,237,794,815,827]
+ CRUSH rule 0 x 184 [704,312,685,645,691]
+ CRUSH rule 0 x 185 [97,100,762,82,999]
+ CRUSH rule 0 x 186 [26,665,554,215,280]
+ CRUSH rule 0 x 187 [649,14,740,494,402]
+ CRUSH rule 0 x 188 [682,695,590,743,927]
+ CRUSH rule 0 x 189 [325,693,726,51,448]
+ CRUSH rule 0 x 190 [399,933,136,955,57]
+ CRUSH rule 0 x 191 [629,533,17,126,60]
+ CRUSH rule 0 x 192 [503,578,38,492,222]
+ CRUSH rule 0 x 193 [546,333,651,678,823]
+ CRUSH rule 0 x 194 [242,473,58,655,277]
+ CRUSH rule 0 x 195 [625,719,135,81,636]
+ CRUSH rule 0 x 196 [357,114,125,867,250]
+ CRUSH rule 0 x 197 [306,954,453,873,211]
+ CRUSH rule 0 x 198 [863,791,311,911,206]
+ CRUSH rule 0 x 199 [935,906,929,252,893]
+ CRUSH rule 0 x 200 [373,774,229,454,909]
+ CRUSH rule 0 x 201 [659,320,477,313,779]
+ CRUSH rule 0 x 202 [260,433,524,880,223]
+ CRUSH rule 0 x 203 [36,239,675,971,703]
+ CRUSH rule 0 x 204 [92,516,993,728,279]
+ CRUSH rule 0 x 205 [68,395,473,45,683]
+ CRUSH rule 0 x 206 [570,530,642,380,311]
+ CRUSH rule 0 x 207 [834,457,850,917,456]
+ CRUSH rule 0 x 208 [927,484,640,976,803]
+ CRUSH rule 0 x 209 [878,66,58,940,48]
+ CRUSH rule 0 x 210 [572,981,484,29,0]
+ CRUSH rule 0 x 211 [107,597,780,857,895]
+ CRUSH rule 0 x 212 [389,107,838,624,698]
+ CRUSH rule 0 x 213 [497,717,567,728,905]
+ CRUSH rule 0 x 214 [798,65,254,572,32]
+ CRUSH rule 0 x 215 [233,419,283,638,520]
+ CRUSH rule 0 x 216 [494,464,742,523,459]
+ CRUSH rule 0 x 217 [352,396,309,938,66]
+ CRUSH rule 0 x 218 [895,864,988,650,593]
+ CRUSH rule 0 x 219 [222,534,277,242,658]
+ CRUSH rule 0 x 220 [281,19,584,563,858]
+ CRUSH rule 0 x 221 [64,928,963,130,312]
+ CRUSH rule 0 x 222 [40,544,161,199,861]
+ CRUSH rule 0 x 223 [645,556,159,417,46]
+ CRUSH rule 0 x 224 [647,165,957,263,961]
+ CRUSH rule 0 x 225 [219,714,858,747,461]
+ CRUSH rule 0 x 226 [372,511,181,277,695]
+ CRUSH rule 0 x 227 [925,156,714,863,257]
+ CRUSH rule 0 x 228 [682,404,839,263,521]
+ CRUSH rule 0 x 229 [880,838,770,891,236]
+ CRUSH rule 0 x 230 [328,659,916,468,646]
+ CRUSH rule 0 x 231 [320,383,669,109,627]
+ CRUSH rule 0 x 232 [924,846,394,319,43]
+ CRUSH rule 0 x 233 [948,652,575,838,498]
+ CRUSH rule 0 x 234 [484,943,42,575,936]
+ CRUSH rule 0 x 235 [750,65,590,168,870]
+ CRUSH rule 0 x 236 [551,787,490,136,370]
+ CRUSH rule 0 x 237 [390,157,166,251,752]
+ CRUSH rule 0 x 238 [570,6,989,707,514]
+ CRUSH rule 0 x 239 [729,959,376,975,496]
+ CRUSH rule 0 x 240 [981,241,156,767,631]
+ CRUSH rule 0 x 241 [310,816,641,177,996]
+ CRUSH rule 0 x 242 [161,63,642,837,763]
+ CRUSH rule 0 x 243 [180,394,33,683,189]
+ CRUSH rule 0 x 244 [52,174,685,189,78]
+ CRUSH rule 0 x 245 [523,121,915,84,386]
+ CRUSH rule 0 x 246 [362,893,390,487,817]
+ CRUSH rule 0 x 247 [382,184,116,34,143]
+ CRUSH rule 0 x 248 [129,114,852,469,359]
+ CRUSH rule 0 x 249 [159,683,91,856,475]
+ CRUSH rule 0 x 250 [404,945,569,955,228]
+ CRUSH rule 0 x 251 [661,225,738,757,37]
+ CRUSH rule 0 x 252 [961,226,542,103,945]
+ CRUSH rule 0 x 253 [651,97,225,364,189]
+ CRUSH rule 0 x 254 [123,33,741,692,599]
+ CRUSH rule 0 x 255 [314,649,891,855,517]
+ CRUSH rule 0 x 256 [315,215,651,126,470]
+ CRUSH rule 0 x 257 [825,264,867,529,409]
+ CRUSH rule 0 x 258 [624,789,370,723,131]
+ CRUSH rule 0 x 259 [602,542,70,563,947]
+ CRUSH rule 0 x 260 [717,878,43,56,377]
+ CRUSH rule 0 x 261 [145,517,20,903,786]
+ CRUSH rule 0 x 262 [223,1,561,420,16]
+ CRUSH rule 0 x 263 [462,211,405,508,787]
+ CRUSH rule 0 x 264 [654,471,266,662,135]
+ CRUSH rule 0 x 265 [302,794,704,798,659]
+ CRUSH rule 0 x 266 [202,132,884,209,551]
+ CRUSH rule 0 x 267 [282,938,657,113,672]
+ CRUSH rule 0 x 268 [338,309,356,278,928]
+ CRUSH rule 0 x 269 [738,122,266,200,894]
+ CRUSH rule 0 x 270 [707,982,946,196,407]
+ CRUSH rule 0 x 271 [705,432,364,735,512]
+ CRUSH rule 0 x 272 [756,545,942,56,542]
+ CRUSH rule 0 x 273 [197,502,527,721,239]
+ CRUSH rule 0 x 274 [992,44,653,573,527]
+ CRUSH rule 0 x 275 [544,789,170,434,23]
+ CRUSH rule 0 x 276 [658,467,577,268,336]
+ CRUSH rule 0 x 277 [143,490,880,483,928]
+ CRUSH rule 0 x 278 [492,647,355,282,834]
+ CRUSH rule 0 x 279 [517,792,604,987,527]
+ CRUSH rule 0 x 280 [825,740,27,848,514]
+ CRUSH rule 0 x 281 [224,629,120,562,616]
+ CRUSH rule 0 x 282 [298,661,380,416,35]
+ CRUSH rule 0 x 283 [311,606,208,50,913]
+ CRUSH rule 0 x 284 [771,466,371,743,672]
+ CRUSH rule 0 x 285 [693,362,404,676,797]
+ CRUSH rule 0 x 286 [364,477,285,167,270]
+ CRUSH rule 0 x 287 [591,611,828,995,170]
+ CRUSH rule 0 x 288 [965,541,848,796,251]
+ CRUSH rule 0 x 289 [225,551,948,877,219]
+ CRUSH rule 0 x 290 [577,762,777,751,291]
+ CRUSH rule 0 x 291 [160,903,477,381,490]
+ CRUSH rule 0 x 292 [873,598,216,666,222]
+ CRUSH rule 0 x 293 [100,234,874,47,28]
+ CRUSH rule 0 x 294 [285,943,379,520,725]
+ CRUSH rule 0 x 295 [938,262,880,327,687]
+ CRUSH rule 0 x 296 [850,327,86,472,1]
+ CRUSH rule 0 x 297 [951,53,99,558,753]
+ CRUSH rule 0 x 298 [173,336,85,766,910]
+ CRUSH rule 0 x 299 [598,591,315,386,895]
+ CRUSH rule 0 x 300 [531,957,62,459,156]
+ CRUSH rule 0 x 301 [823,628,23,858,629]
+ CRUSH rule 0 x 302 [184,80,780,871,531]
+ CRUSH rule 0 x 303 [521,766,222,830,988]
+ CRUSH rule 0 x 304 [980,127,807,507,555]
+ CRUSH rule 0 x 305 [153,816,22,927,696]
+ CRUSH rule 0 x 306 [423,739,664,753,178]
+ CRUSH rule 0 x 307 [997,557,682,456,479]
+ CRUSH rule 0 x 308 [991,874,534,465,330]
+ CRUSH rule 0 x 309 [860,394,724,858,246]
+ CRUSH rule 0 x 310 [589,818,546,201,94]
+ CRUSH rule 0 x 311 [477,774,225,590,830]
+ CRUSH rule 0 x 312 [887,853,950,354,58]
+ CRUSH rule 0 x 313 [802,646,447,416,557]
+ CRUSH rule 0 x 314 [654,974,229,511,562]
+ CRUSH rule 0 x 315 [767,227,28,740,828]
+ CRUSH rule 0 x 316 [778,83,733,359,858]
+ CRUSH rule 0 x 317 [184,418,642,986,939]
+ CRUSH rule 0 x 318 [525,410,500,543,212]
+ CRUSH rule 0 x 319 [476,724,569,382,409]
+ CRUSH rule 0 x 320 [149,610,697,296,818]
+ CRUSH rule 0 x 321 [710,79,667,671,234]
+ CRUSH rule 0 x 322 [175,275,323,333,744]
+ CRUSH rule 0 x 323 [819,604,638,792,316]
+ CRUSH rule 0 x 324 [16,745,511,439,272]
+ CRUSH rule 0 x 325 [486,400,872,873,251]
+ CRUSH rule 0 x 326 [613,765,207,19,359]
+ CRUSH rule 0 x 327 [125,289,738,408,456]
+ CRUSH rule 0 x 328 [807,383,476,583,645]
+ CRUSH rule 0 x 329 [588,938,599,432,446]
+ CRUSH rule 0 x 330 [932,644,41,611,209]
+ CRUSH rule 0 x 331 [341,953,950,537,578]
+ CRUSH rule 0 x 332 [153,726,459,950,466]
+ CRUSH rule 0 x 333 [745,845,853,860,52]
+ CRUSH rule 0 x 334 [614,751,807,58,396]
+ CRUSH rule 0 x 335 [518,721,221,283,454]
+ CRUSH rule 0 x 336 [389,424,77,309,5]
+ CRUSH rule 0 x 337 [753,508,765,720,221]
+ CRUSH rule 0 x 338 [128,810,490,753,406]
+ CRUSH rule 0 x 339 [430,308,58,751,856]
+ CRUSH rule 0 x 340 [541,44,630,231,289]
+ CRUSH rule 0 x 341 [402,26,631,439,165]
+ CRUSH rule 0 x 342 [982,57,992,461,131]
+ CRUSH rule 0 x 343 [833,412,572,732,107]
+ CRUSH rule 0 x 344 [784,533,792,41,642]
+ CRUSH rule 0 x 345 [546,300,304,691,763]
+ CRUSH rule 0 x 346 [302,420,428,891,357]
+ CRUSH rule 0 x 347 [488,778,101,217,366]
+ CRUSH rule 0 x 348 [903,744,937,718,85]
+ CRUSH rule 0 x 349 [471,547,582,306,600]
+ CRUSH rule 0 x 350 [348,221,823,335,383]
+ CRUSH rule 0 x 351 [961,582,705,346,361]
+ CRUSH rule 0 x 352 [728,137,461,298,36]
+ CRUSH rule 0 x 353 [904,202,184,447,58]
+ CRUSH rule 0 x 354 [345,226,319,256,544]
+ CRUSH rule 0 x 355 [50,430,175,43,187]
+ CRUSH rule 0 x 356 [87,185,55,423,829]
+ CRUSH rule 0 x 357 [762,459,921,473,182]
+ CRUSH rule 0 x 358 [908,25,280,6,808]
+ CRUSH rule 0 x 359 [484,15,132,121,394]
+ CRUSH rule 0 x 360 [173,378,337,702,145]
+ CRUSH rule 0 x 361 [404,577,115,25,56]
+ CRUSH rule 0 x 362 [403,1,422,945,132]
+ CRUSH rule 0 x 363 [639,911,510,162,418]
+ CRUSH rule 0 x 364 [752,689,610,990,665]
+ CRUSH rule 0 x 365 [956,999,212,230,624]
+ CRUSH rule 0 x 366 [860,925,924,763,687]
+ CRUSH rule 0 x 367 [205,609,647,665,969]
+ CRUSH rule 0 x 368 [301,284,810,169,78]
+ CRUSH rule 0 x 369 [452,658,339,217,674]
+ CRUSH rule 0 x 370 [11,467,695,989,394]
+ CRUSH rule 0 x 371 [124,487,55,514,313]
+ CRUSH rule 0 x 372 [253,48,979,846,207]
+ CRUSH rule 0 x 373 [715,605,775,748,227]
+ CRUSH rule 0 x 374 [191,887,920,223,714]
+ CRUSH rule 0 x 375 [711,385,651,665,15]
+ CRUSH rule 0 x 376 [597,818,49,458,415]
+ CRUSH rule 0 x 377 [294,256,933,771,184]
+ CRUSH rule 0 x 378 [34,151,681,707,552]
+ CRUSH rule 0 x 379 [869,136,315,378,813]
+ CRUSH rule 0 x 380 [294,97,575,791,690]
+ CRUSH rule 0 x 381 [119,710,219,827,328]
+ CRUSH rule 0 x 382 [69,631,508,706,697]
+ CRUSH rule 0 x 383 [922,588,589,925,471]
+ CRUSH rule 0 x 384 [221,945,671,117,857]
+ CRUSH rule 0 x 385 [561,737,953,723,658]
+ CRUSH rule 0 x 386 [335,442,788,696,507]
+ CRUSH rule 0 x 387 [514,43,353,88,100]
+ CRUSH rule 0 x 388 [587,89,157,996,915]
+ CRUSH rule 0 x 389 [109,641,255,466,372]
+ CRUSH rule 0 x 390 [925,149,421,489,599]
+ CRUSH rule 0 x 391 [267,87,387,527,768]
+ CRUSH rule 0 x 392 [382,485,370,849,936]
+ CRUSH rule 0 x 393 [425,721,221,753,268]
+ CRUSH rule 0 x 394 [898,18,38,793,173]
+ CRUSH rule 0 x 395 [806,876,269,679,32]
+ CRUSH rule 0 x 396 [790,970,437,449,875]
+ CRUSH rule 0 x 397 [136,363,507,613,11]
+ CRUSH rule 0 x 398 [914,116,558,258,722]
+ CRUSH rule 0 x 399 [261,94,299,202,174]
+ CRUSH rule 0 x 400 [661,197,338,461,977]
+ CRUSH rule 0 x 401 [953,979,287,803,41]
+ CRUSH rule 0 x 402 [738,819,618,522,667]
+ CRUSH rule 0 x 403 [573,238,425,546,130]
+ CRUSH rule 0 x 404 [526,848,790,253,922]
+ CRUSH rule 0 x 405 [582,505,330,334,201]
+ CRUSH rule 0 x 406 [768,324,493,60,186]
+ CRUSH rule 0 x 407 [260,951,437,587,692]
+ CRUSH rule 0 x 408 [657,81,770,734,830]
+ CRUSH rule 0 x 409 [498,89,182,423,672]
+ CRUSH rule 0 x 410 [28,793,737,352,166]
+ CRUSH rule 0 x 411 [684,992,60,659,769]
+ CRUSH rule 0 x 412 [261,958,699,950,165]
+ CRUSH rule 0 x 413 [891,835,297,441,384]
+ CRUSH rule 0 x 414 [127,459,119,965,662]
+ CRUSH rule 0 x 415 [272,540,631,328,609]
+ CRUSH rule 0 x 416 [739,617,115,530,339]
+ CRUSH rule 0 x 417 [106,209,157,878,117]
+ CRUSH rule 0 x 418 [525,441,147,390,320]
+ CRUSH rule 0 x 419 [603,673,615,465,266]
+ CRUSH rule 0 x 420 [988,213,251,226,209]
+ CRUSH rule 0 x 421 [761,521,748,368,923]
+ CRUSH rule 0 x 422 [317,160,924,548,198]
+ CRUSH rule 0 x 423 [137,807,168,472,619]
+ CRUSH rule 0 x 424 [920,37,146,263,598]
+ CRUSH rule 0 x 425 [277,693,285,221,478]
+ CRUSH rule 0 x 426 [485,936,407,854,726]
+ CRUSH rule 0 x 427 [242,515,9,564,174]
+ CRUSH rule 0 x 428 [632,635,26,473,494]
+ CRUSH rule 0 x 429 [641,73,465,127,171]
+ CRUSH rule 0 x 430 [626,585,6,387,881]
+ CRUSH rule 0 x 431 [697,76,753,570,964]
+ CRUSH rule 0 x 432 [590,526,306,283,656]
+ CRUSH rule 0 x 433 [284,387,149,817,886]
+ CRUSH rule 0 x 434 [538,985,79,953,770]
+ CRUSH rule 0 x 435 [30,318,593,635,975]
+ CRUSH rule 0 x 436 [164,919,851,693,0]
+ CRUSH rule 0 x 437 [322,212,163,606,302]
+ CRUSH rule 0 x 438 [142,392,85,594,376]
+ CRUSH rule 0 x 439 [119,370,68,443,997]
+ CRUSH rule 0 x 440 [333,403,187,863,475]
+ CRUSH rule 0 x 441 [477,727,906,145,429]
+ CRUSH rule 0 x 442 [274,590,933,244,434]
+ CRUSH rule 0 x 443 [983,748,574,718,700]
+ CRUSH rule 0 x 444 [536,509,431,146,170]
+ CRUSH rule 0 x 445 [485,528,209,964,753]
+ CRUSH rule 0 x 446 [345,634,42,294,711]
+ CRUSH rule 0 x 447 [61,845,767,600,321]
+ CRUSH rule 0 x 448 [333,232,292,846,364]
+ CRUSH rule 0 x 449 [680,16,484,670,851]
+ CRUSH rule 0 x 450 [235,214,79,423,96]
+ CRUSH rule 0 x 451 [961,468,333,640,823]
+ CRUSH rule 0 x 452 [525,479,153,528,570]
+ CRUSH rule 0 x 453 [138,466,302,86,249]
+ CRUSH rule 0 x 454 [137,625,215,402,389]
+ CRUSH rule 0 x 455 [173,150,997,16,846]
+ CRUSH rule 0 x 456 [235,226,238,258,347]
+ CRUSH rule 0 x 457 [450,577,253,413,717]
+ CRUSH rule 0 x 458 [195,537,91,814,351]
+ CRUSH rule 0 x 459 [381,555,312,573,915]
+ CRUSH rule 0 x 460 [972,730,534,678,756]
+ CRUSH rule 0 x 461 [506,279,142,830,784]
+ CRUSH rule 0 x 462 [692,959,578,57,983]
+ CRUSH rule 0 x 463 [788,667,949,550,685]
+ CRUSH rule 0 x 464 [133,122,588,999,270]
+ CRUSH rule 0 x 465 [971,190,230,777,452]
+ CRUSH rule 0 x 466 [394,576,148,157,103]
+ CRUSH rule 0 x 467 [517,28,366,362,984]
+ CRUSH rule 0 x 468 [829,143,874,225,162]
+ CRUSH rule 0 x 469 [987,936,106,725,633]
+ CRUSH rule 0 x 470 [107,982,56,889,67]
+ CRUSH rule 0 x 471 [181,897,629,860,307]
+ CRUSH rule 0 x 472 [547,512,172,24,705]
+ CRUSH rule 0 x 473 [760,997,824,905,888]
+ CRUSH rule 0 x 474 [787,418,743,628,272]
+ CRUSH rule 0 x 475 [662,312,253,617,105]
+ CRUSH rule 0 x 476 [110,495,185,508,961]
+ CRUSH rule 0 x 477 [393,954,834,132,841]
+ CRUSH rule 0 x 478 [246,483,480,644,985]
+ CRUSH rule 0 x 479 [70,929,697,931,744]
+ CRUSH rule 0 x 480 [753,119,961,607,317]
+ CRUSH rule 0 x 481 [470,429,677,242,574]
+ CRUSH rule 0 x 482 [451,566,961,675,354]
+ CRUSH rule 0 x 483 [816,72,371,278,635]
+ CRUSH rule 0 x 484 [540,454,389,31,654]
+ CRUSH rule 0 x 485 [74,582,624,684,566]
+ CRUSH rule 0 x 486 [958,595,199,763,715]
+ CRUSH rule 0 x 487 [228,302,804,833,876]
+ CRUSH rule 0 x 488 [180,529,722,956,353]
+ CRUSH rule 0 x 489 [47,617,812,187,291]
+ CRUSH rule 0 x 490 [905,822,479,124,750]
+ CRUSH rule 0 x 491 [892,370,609,998,433]
+ CRUSH rule 0 x 492 [588,959,127,948,505]
+ CRUSH rule 0 x 493 [353,461,593,291,301]
+ CRUSH rule 0 x 494 [378,848,443,368,507]
+ CRUSH rule 0 x 495 [845,653,768,234,405]
+ CRUSH rule 0 x 496 [13,988,0,691,389]
+ CRUSH rule 0 x 497 [796,877,788,394,648]
+ CRUSH rule 0 x 498 [412,337,270,705,511]
+ CRUSH rule 0 x 499 [330,695,8,74,618]
+ CRUSH rule 0 x 500 [820,272,547,765,755]
+ CRUSH rule 0 x 501 [110,44,132,442,294]
+ CRUSH rule 0 x 502 [336,595,650,274,993]
+ CRUSH rule 0 x 503 [922,211,157,722,502]
+ CRUSH rule 0 x 504 [483,52,122,432,778]
+ CRUSH rule 0 x 505 [482,598,224,279,480]
+ CRUSH rule 0 x 506 [493,123,43,856,936]
+ CRUSH rule 0 x 507 [12,598,264,422,416]
+ CRUSH rule 0 x 508 [227,157,611,301,223]
+ CRUSH rule 0 x 509 [807,242,363,122,582]
+ CRUSH rule 0 x 510 [134,437,227,75,313]
+ CRUSH rule 0 x 511 [212,54,83,799,457]
+ CRUSH rule 0 x 512 [236,630,758,752,361]
+ CRUSH rule 0 x 513 [994,693,644,938,846]
+ CRUSH rule 0 x 514 [45,508,831,19,817]
+ CRUSH rule 0 x 515 [504,138,480,272,530]
+ CRUSH rule 0 x 516 [285,409,136,570,841]
+ CRUSH rule 0 x 517 [300,232,23,906,438]
+ CRUSH rule 0 x 518 [397,674,98,898,967]
+ CRUSH rule 0 x 519 [86,750,772,913,101]
+ CRUSH rule 0 x 520 [900,833,614,130,261]
+ CRUSH rule 0 x 521 [31,47,236,751,911]
+ CRUSH rule 0 x 522 [390,16,280,144,291]
+ CRUSH rule 0 x 523 [618,308,424,590,300]
+ CRUSH rule 0 x 524 [635,189,687,963,601]
+ CRUSH rule 0 x 525 [311,916,699,262,775]
+ CRUSH rule 0 x 526 [48,738,227,718,244]
+ CRUSH rule 0 x 527 [202,851,889,216,763]
+ CRUSH rule 0 x 528 [565,827,590,273,918]
+ CRUSH rule 0 x 529 [934,864,241,43,466]
+ CRUSH rule 0 x 530 [502,934,298,670,986]
+ CRUSH rule 0 x 531 [681,627,942,487,288]
+ CRUSH rule 0 x 532 [422,6,147,205,861]
+ CRUSH rule 0 x 533 [863,68,364,983,247]
+ CRUSH rule 0 x 534 [962,931,775,172,663]
+ CRUSH rule 0 x 535 [89,565,397,693,839]
+ CRUSH rule 0 x 536 [499,351,760,458,918]
+ CRUSH rule 0 x 537 [676,547,787,311,867]
+ CRUSH rule 0 x 538 [58,644,571,649,941]
+ CRUSH rule 0 x 539 [837,953,457,711,458]
+ CRUSH rule 0 x 540 [831,50,132,213,197]
+ CRUSH rule 0 x 541 [582,757,121,525,532]
+ CRUSH rule 0 x 542 [472,132,790,997,948]
+ CRUSH rule 0 x 543 [382,272,797,330,315]
+ CRUSH rule 0 x 544 [947,930,496,883,509]
+ CRUSH rule 0 x 545 [425,570,305,77,821]
+ CRUSH rule 0 x 546 [18,65,529,437,343]
+ CRUSH rule 0 x 547 [445,715,600,472,213]
+ CRUSH rule 0 x 548 [367,569,980,167,627]
+ CRUSH rule 0 x 549 [125,715,671,817,285]
+ CRUSH rule 0 x 550 [425,599,744,199,923]
+ CRUSH rule 0 x 551 [44,1,528,922,944]
+ CRUSH rule 0 x 552 [246,104,68,239,123]
+ CRUSH rule 0 x 553 [71,703,615,28,593]
+ CRUSH rule 0 x 554 [207,124,217,166,525]
+ CRUSH rule 0 x 555 [570,28,317,420,931]
+ CRUSH rule 0 x 556 [674,152,421,79,215]
+ CRUSH rule 0 x 557 [347,817,191,391,741]
+ CRUSH rule 0 x 558 [627,426,369,692,815]
+ CRUSH rule 0 x 559 [940,630,924,242,224]
+ CRUSH rule 0 x 560 [295,903,541,29,245]
+ CRUSH rule 0 x 561 [506,682,384,637,878]
+ CRUSH rule 0 x 562 [718,529,87,729,842]
+ CRUSH rule 0 x 563 [552,332,747,206,274]
+ CRUSH rule 0 x 564 [835,769,736,486,630]
+ CRUSH rule 0 x 565 [8,167,539,182,607]
+ CRUSH rule 0 x 566 [600,481,301,263,90]
+ CRUSH rule 0 x 567 [999,994,509,899,947]
+ CRUSH rule 0 x 568 [252,431,157,62,601]
+ CRUSH rule 0 x 569 [643,218,943,455,83]
+ CRUSH rule 0 x 570 [617,635,765,422,250]
+ CRUSH rule 0 x 571 [757,80,59,98,328]
+ CRUSH rule 0 x 572 [299,348,575,889,943]
+ CRUSH rule 0 x 573 [25,505,270,167,58]
+ CRUSH rule 0 x 574 [215,431,624,177,628]
+ CRUSH rule 0 x 575 [225,252,611,546,32]
+ CRUSH rule 0 x 576 [627,94,159,857,430]
+ CRUSH rule 0 x 577 [237,809,778,636,61]
+ CRUSH rule 0 x 578 [885,313,120,344,771]
+ CRUSH rule 0 x 579 [924,575,787,831,47]
+ CRUSH rule 0 x 580 [718,51,766,121,118]
+ CRUSH rule 0 x 581 [219,807,129,571,856]
+ CRUSH rule 0 x 582 [893,701,598,863,285]
+ CRUSH rule 0 x 583 [246,930,964,170,993]
+ CRUSH rule 0 x 584 [336,432,680,175,495]
+ CRUSH rule 0 x 585 [324,999,397,485,457]
+ CRUSH rule 0 x 586 [558,230,976,541,816]
+ CRUSH rule 0 x 587 [985,830,597,21,308]
+ CRUSH rule 0 x 588 [211,544,57,134,162]
+ CRUSH rule 0 x 589 [129,21,112,190,885]
+ CRUSH rule 0 x 590 [467,969,652,593,287]
+ CRUSH rule 0 x 591 [758,514,316,164,35]
+ CRUSH rule 0 x 592 [525,253,190,443,315]
+ CRUSH rule 0 x 593 [601,885,339,152,297]
+ CRUSH rule 0 x 594 [227,60,450,30,717]
+ CRUSH rule 0 x 595 [720,854,496,912,80]
+ CRUSH rule 0 x 596 [751,195,997,77,261]
+ CRUSH rule 0 x 597 [129,574,714,8,789]
+ CRUSH rule 0 x 598 [679,207,604,396,841]
+ CRUSH rule 0 x 599 [668,315,683,349,681]
+ CRUSH rule 0 x 600 [143,396,464,444,59]
+ CRUSH rule 0 x 601 [326,573,873,902,136]
+ CRUSH rule 0 x 602 [860,281,875,535,672]
+ CRUSH rule 0 x 603 [709,328,445,349,190]
+ CRUSH rule 0 x 604 [571,62,814,95,866]
+ CRUSH rule 0 x 605 [252,739,860,27,313]
+ CRUSH rule 0 x 606 [339,236,759,842,67]
+ CRUSH rule 0 x 607 [590,248,759,868,433]
+ CRUSH rule 0 x 608 [145,635,309,467,875]
+ CRUSH rule 0 x 609 [973,547,223,79,762]
+ CRUSH rule 0 x 610 [435,816,961,983,255]
+ CRUSH rule 0 x 611 [559,283,422,584,176]
+ CRUSH rule 0 x 612 [273,149,123,576,911]
+ CRUSH rule 0 x 613 [828,614,642,674,33]
+ CRUSH rule 0 x 614 [478,748,393,34,171]
+ CRUSH rule 0 x 615 [392,155,144,326,626]
+ CRUSH rule 0 x 616 [778,637,452,248,15]
+ CRUSH rule 0 x 617 [622,713,996,833,611]
+ CRUSH rule 0 x 618 [149,877,270,329,180]
+ CRUSH rule 0 x 619 [604,163,656,409,322]
+ CRUSH rule 0 x 620 [181,23,409,198,64]
+ CRUSH rule 0 x 621 [735,902,386,237,939]
+ CRUSH rule 0 x 622 [661,824,717,568,858]
+ CRUSH rule 0 x 623 [142,121,643,61,695]
+ CRUSH rule 0 x 624 [360,716,420,398,49]
+ CRUSH rule 0 x 625 [541,167,385,1,601]
+ CRUSH rule 0 x 626 [364,431,610,363,535]
+ CRUSH rule 0 x 627 [458,137,557,410,287]
+ CRUSH rule 0 x 628 [250,350,556,497,821]
+ CRUSH rule 0 x 629 [928,160,710,572,365]
+ CRUSH rule 0 x 630 [243,19,918,556,601]
+ CRUSH rule 0 x 631 [438,221,574,676,797]
+ CRUSH rule 0 x 632 [797,368,247,5,32]
+ CRUSH rule 0 x 633 [993,749,525,485,27]
+ CRUSH rule 0 x 634 [239,351,633,299,651]
+ CRUSH rule 0 x 635 [640,965,25,961,306]
+ CRUSH rule 0 x 636 [173,290,297,991,937]
+ CRUSH rule 0 x 637 [0,918,98,108,111]
+ CRUSH rule 0 x 638 [702,235,424,900,983]
+ CRUSH rule 0 x 639 [475,687,31,785,918]
+ CRUSH rule 0 x 640 [31,664,399,677,123]
+ CRUSH rule 0 x 641 [296,473,108,963,341]
+ CRUSH rule 0 x 642 [894,273,427,606,677]
+ CRUSH rule 0 x 643 [117,111,732,191,114]
+ CRUSH rule 0 x 644 [438,336,327,512,599]
+ CRUSH rule 0 x 645 [982,702,351,573,907]
+ CRUSH rule 0 x 646 [334,804,146,842,697]
+ CRUSH rule 0 x 647 [933,787,185,334,752]
+ CRUSH rule 0 x 648 [22,444,400,862,207]
+ CRUSH rule 0 x 649 [503,229,213,460,639]
+ CRUSH rule 0 x 650 [328,659,420,443,739]
+ CRUSH rule 0 x 651 [3,880,823,123,378]
+ CRUSH rule 0 x 652 [495,977,563,733,92]
+ CRUSH rule 0 x 653 [185,718,804,280,975]
+ CRUSH rule 0 x 654 [130,528,380,81,906]
+ CRUSH rule 0 x 655 [560,872,454,504,319]
+ CRUSH rule 0 x 656 [219,885,178,981,863]
+ CRUSH rule 0 x 657 [233,684,813,490,208]
+ CRUSH rule 0 x 658 [778,6,756,380,750]
+ CRUSH rule 0 x 659 [240,663,306,540,789]
+ CRUSH rule 0 x 660 [244,855,196,147,678]
+ CRUSH rule 0 x 661 [184,270,128,398,910]
+ CRUSH rule 0 x 662 [65,883,921,438,79]
+ CRUSH rule 0 x 663 [323,721,594,812,43]
+ CRUSH rule 0 x 664 [865,113,512,51,427]
+ CRUSH rule 0 x 665 [420,850,591,475,202]
+ CRUSH rule 0 x 666 [319,767,246,3,369]
+ CRUSH rule 0 x 667 [875,39,343,100,829]
+ CRUSH rule 0 x 668 [331,122,263,599,355]
+ CRUSH rule 0 x 669 [915,521,402,747,673]
+ CRUSH rule 0 x 670 [845,659,943,447,401]
+ CRUSH rule 0 x 671 [108,634,527,363,856]
+ CRUSH rule 0 x 672 [578,216,110,589,302]
+ CRUSH rule 0 x 673 [442,74,579,797,622]
+ CRUSH rule 0 x 674 [588,364,281,308,645]
+ CRUSH rule 0 x 675 [489,698,744,671,870]
+ CRUSH rule 0 x 676 [928,911,40,180,722]
+ CRUSH rule 0 x 677 [399,269,692,131,615]
+ CRUSH rule 0 x 678 [546,752,544,155,5]
+ CRUSH rule 0 x 679 [988,25,275,433,628]
+ CRUSH rule 0 x 680 [335,963,382,486,749]
+ CRUSH rule 0 x 681 [690,462,623,466,49]
+ CRUSH rule 0 x 682 [196,588,154,257,807]
+ CRUSH rule 0 x 683 [627,25,421,160,873]
+ CRUSH rule 0 x 684 [38,804,592,158,991]
+ CRUSH rule 0 x 685 [841,368,548,362,166]
+ CRUSH rule 0 x 686 [336,287,525,440,166]
+ CRUSH rule 0 x 687 [20,682,924,653,356]
+ CRUSH rule 0 x 688 [463,371,780,556,385]
+ CRUSH rule 0 x 689 [569,250,78,816,847]
+ CRUSH rule 0 x 690 [551,144,587,263,378]
+ CRUSH rule 0 x 691 [766,464,446,533,449]
+ CRUSH rule 0 x 692 [739,634,18,245,624]
+ CRUSH rule 0 x 693 [339,297,118,330,817]
+ CRUSH rule 0 x 694 [405,26,830,181,533]
+ CRUSH rule 0 x 695 [622,576,597,535,600]
+ CRUSH rule 0 x 696 [558,902,689,13,715]
+ CRUSH rule 0 x 697 [818,222,406,691,427]
+ CRUSH rule 0 x 698 [178,48,402,233,841]
+ CRUSH rule 0 x 699 [450,244,180,919,332]
+ CRUSH rule 0 x 700 [502,771,987,706,416]
+ CRUSH rule 0 x 701 [4,612,782,216,853]
+ CRUSH rule 0 x 702 [177,630,232,923,281]
+ CRUSH rule 0 x 703 [354,178,389,393,778]
+ CRUSH rule 0 x 704 [646,601,156,171,603]
+ CRUSH rule 0 x 705 [921,401,890,265,244]
+ CRUSH rule 0 x 706 [652,877,562,452,26]
+ CRUSH rule 0 x 707 [345,745,67,716,789]
+ CRUSH rule 0 x 708 [333,607,180,469,170]
+ CRUSH rule 0 x 709 [45,187,302,115,896]
+ CRUSH rule 0 x 710 [94,855,43,199,18]
+ CRUSH rule 0 x 711 [227,653,731,150,842]
+ CRUSH rule 0 x 712 [398,953,136,870,181]
+ CRUSH rule 0 x 713 [116,800,503,662,635]
+ CRUSH rule 0 x 714 [111,629,866,709,902]
+ CRUSH rule 0 x 715 [531,291,486,382,192]
+ CRUSH rule 0 x 716 [169,541,291,42,343]
+ CRUSH rule 0 x 717 [417,446,994,894,239]
+ CRUSH rule 0 x 718 [992,383,298,844,377]
+ CRUSH rule 0 x 719 [936,674,324,759,194]
+ CRUSH rule 0 x 720 [370,188,174,464,644]
+ CRUSH rule 0 x 721 [320,859,278,259,170]
+ CRUSH rule 0 x 722 [7,2,673,129,96]
+ CRUSH rule 0 x 723 [270,553,831,662,38]
+ CRUSH rule 0 x 724 [666,822,708,895,633]
+ CRUSH rule 0 x 725 [794,406,875,459,981]
+ CRUSH rule 0 x 726 [420,556,341,292,240]
+ CRUSH rule 0 x 727 [561,461,129,635,965]
+ CRUSH rule 0 x 728 [951,330,196,756,589]
+ CRUSH rule 0 x 729 [656,644,436,591,27]
+ CRUSH rule 0 x 730 [3,558,629,184,50]
+ CRUSH rule 0 x 731 [852,89,75,735,713]
+ CRUSH rule 0 x 732 [983,840,869,976,697]
+ CRUSH rule 0 x 733 [285,396,388,122,387]
+ CRUSH rule 0 x 734 [125,510,402,640,676]
+ CRUSH rule 0 x 735 [417,773,686,504,459]
+ CRUSH rule 0 x 736 [749,396,632,550,779]
+ CRUSH rule 0 x 737 [644,991,946,135,448]
+ CRUSH rule 0 x 738 [449,683,290,220,245]
+ CRUSH rule 0 x 739 [341,220,641,454,740]
+ CRUSH rule 0 x 740 [874,524,674,650,472]
+ CRUSH rule 0 x 741 [189,472,712,798,715]
+ CRUSH rule 0 x 742 [912,581,114,730,21]
+ CRUSH rule 0 x 743 [654,914,425,441,763]
+ CRUSH rule 0 x 744 [725,295,579,377,162]
+ CRUSH rule 0 x 745 [787,858,850,506,612]
+ CRUSH rule 0 x 746 [757,848,704,30,47]
+ CRUSH rule 0 x 747 [700,81,867,681,801]
+ CRUSH rule 0 x 748 [557,436,238,664,293]
+ CRUSH rule 0 x 749 [772,622,337,42,156]
+ CRUSH rule 0 x 750 [946,97,376,677,316]
+ CRUSH rule 0 x 751 [996,618,343,911,83]
+ CRUSH rule 0 x 752 [746,887,695,868,610]
+ CRUSH rule 0 x 753 [741,14,463,479,172]
+ CRUSH rule 0 x 754 [648,349,333,355,65]
+ CRUSH rule 0 x 755 [157,460,466,187,959]
+ CRUSH rule 0 x 756 [416,97,197,497,227]
+ CRUSH rule 0 x 757 [599,839,776,410,256]
+ CRUSH rule 0 x 758 [994,218,620,256,361]
+ CRUSH rule 0 x 759 [959,682,514,745,100]
+ CRUSH rule 0 x 760 [518,943,215,83,706]
+ CRUSH rule 0 x 761 [285,849,420,324,987]
+ CRUSH rule 0 x 762 [591,313,41,335,110]
+ CRUSH rule 0 x 763 [908,411,200,740,292]
+ CRUSH rule 0 x 764 [787,234,894,485,883]
+ CRUSH rule 0 x 765 [327,921,882,393,444]
+ CRUSH rule 0 x 766 [84,161,878,704,416]
+ CRUSH rule 0 x 767 [370,895,702,701,890]
+ CRUSH rule 0 x 768 [826,760,879,864,460]
+ CRUSH rule 0 x 769 [67,768,663,735,814]
+ CRUSH rule 0 x 770 [593,909,482,259,5]
+ CRUSH rule 0 x 771 [309,935,121,578,937]
+ CRUSH rule 0 x 772 [12,125,797,301,348]
+ CRUSH rule 0 x 773 [253,466,820,549,591]
+ CRUSH rule 0 x 774 [164,390,705,109,881]
+ CRUSH rule 0 x 775 [703,47,43,973,643]
+ CRUSH rule 0 x 776 [728,231,80,916,2]
+ CRUSH rule 0 x 777 [981,621,568,729,869]
+ CRUSH rule 0 x 778 [411,456,544,597,789]
+ CRUSH rule 0 x 779 [346,121,519,921,587]
+ CRUSH rule 0 x 780 [476,39,288,381,303]
+ CRUSH rule 0 x 781 [10,130,585,844,729]
+ CRUSH rule 0 x 782 [462,246,581,902,623]
+ CRUSH rule 0 x 783 [580,373,153,775,668]
+ CRUSH rule 0 x 784 [413,113,978,990,994]
+ CRUSH rule 0 x 785 [341,856,332,354,59]
+ CRUSH rule 0 x 786 [411,140,313,393,215]
+ CRUSH rule 0 x 787 [605,522,211,813,636]
+ CRUSH rule 0 x 788 [226,545,35,142,726]
+ CRUSH rule 0 x 789 [545,320,414,702,731]
+ CRUSH rule 0 x 790 [414,748,816,327,130]
+ CRUSH rule 0 x 791 [660,906,406,697,916]
+ CRUSH rule 0 x 792 [287,392,514,204,75]
+ CRUSH rule 0 x 793 [631,133,850,713,720]
+ CRUSH rule 0 x 794 [931,517,543,210,963]
+ CRUSH rule 0 x 795 [551,962,477,948,425]
+ CRUSH rule 0 x 796 [814,4,95,27,368]
+ CRUSH rule 0 x 797 [64,201,299,734,605]
+ CRUSH rule 0 x 798 [422,530,114,431,565]
+ CRUSH rule 0 x 799 [824,32,679,562,266]
+ CRUSH rule 0 x 800 [862,623,489,637,861]
+ CRUSH rule 0 x 801 [145,550,329,324,734]
+ CRUSH rule 0 x 802 [570,19,847,308,387]
+ CRUSH rule 0 x 803 [151,812,662,358,880]
+ CRUSH rule 0 x 804 [467,93,264,863,176]
+ CRUSH rule 0 x 805 [621,223,938,809,591]
+ CRUSH rule 0 x 806 [898,957,805,430,499]
+ CRUSH rule 0 x 807 [354,531,422,159,921]
+ CRUSH rule 0 x 808 [7,96,76,897,446]
+ CRUSH rule 0 x 809 [70,734,719,56,687]
+ CRUSH rule 0 x 810 [701,18,972,327,771]
+ CRUSH rule 0 x 811 [248,547,103,728,901]
+ CRUSH rule 0 x 812 [230,576,821,566,993]
+ CRUSH rule 0 x 813 [805,114,683,629,462]
+ CRUSH rule 0 x 814 [54,619,973,741,497]
+ CRUSH rule 0 x 815 [679,412,613,132,969]
+ CRUSH rule 0 x 816 [919,448,826,414,36]
+ CRUSH rule 0 x 817 [765,830,436,521,332]
+ CRUSH rule 0 x 818 [415,566,644,687,692]
+ CRUSH rule 0 x 819 [721,319,865,750,546]
+ CRUSH rule 0 x 820 [218,301,333,190,686]
+ CRUSH rule 0 x 821 [185,795,680,953,329]
+ CRUSH rule 0 x 822 [356,261,54,522,900]
+ CRUSH rule 0 x 823 [220,281,549,456,64]
+ CRUSH rule 0 x 824 [292,809,887,74,776]
+ CRUSH rule 0 x 825 [949,778,101,311,110]
+ CRUSH rule 0 x 826 [767,818,833,927,356]
+ CRUSH rule 0 x 827 [631,83,406,635,657]
+ CRUSH rule 0 x 828 [288,986,445,26,414]
+ CRUSH rule 0 x 829 [990,667,915,694,974]
+ CRUSH rule 0 x 830 [152,571,778,505,685]
+ CRUSH rule 0 x 831 [814,563,630,97,582]
+ CRUSH rule 0 x 832 [235,641,616,110,979]
+ CRUSH rule 0 x 833 [657,565,922,140,825]
+ CRUSH rule 0 x 834 [907,231,644,13,617]
+ CRUSH rule 0 x 835 [784,262,771,264,612]
+ CRUSH rule 0 x 836 [951,158,366,710,43]
+ CRUSH rule 0 x 837 [556,498,334,633,895]
+ CRUSH rule 0 x 838 [329,274,964,547,119]
+ CRUSH rule 0 x 839 [568,209,939,364,658]
+ CRUSH rule 0 x 840 [45,579,842,70,655]
+ CRUSH rule 0 x 841 [652,702,24,605,152]
+ CRUSH rule 0 x 842 [629,984,314,895,408]
+ CRUSH rule 0 x 843 [799,690,688,648,151]
+ CRUSH rule 0 x 844 [694,600,534,700,569]
+ CRUSH rule 0 x 845 [332,30,179,93,951]
+ CRUSH rule 0 x 846 [452,251,712,719,404]
+ CRUSH rule 0 x 847 [399,681,847,739,13]
+ CRUSH rule 0 x 848 [303,138,440,346,547]
+ CRUSH rule 0 x 849 [666,346,708,873,64]
+ CRUSH rule 0 x 850 [644,511,345,844,545]
+ CRUSH rule 0 x 851 [527,546,737,425,100]
+ CRUSH rule 0 x 852 [31,809,94,618,156]
+ CRUSH rule 0 x 853 [483,330,869,184,46]
+ CRUSH rule 0 x 854 [697,953,968,143,502]
+ CRUSH rule 0 x 855 [837,996,239,621,32]
+ CRUSH rule 0 x 856 [712,40,547,430,195]
+ CRUSH rule 0 x 857 [77,984,576,551,568]
+ CRUSH rule 0 x 858 [412,384,841,465,572]
+ CRUSH rule 0 x 859 [173,760,26,300,87]
+ CRUSH rule 0 x 860 [776,429,328,917,658]
+ CRUSH rule 0 x 861 [705,405,477,50,73]
+ CRUSH rule 0 x 862 [809,44,788,938,964]
+ CRUSH rule 0 x 863 [349,496,963,178,675]
+ CRUSH rule 0 x 864 [717,858,101,239,992]
+ CRUSH rule 0 x 865 [857,603,586,262,550]
+ CRUSH rule 0 x 866 [394,304,71,96,642]
+ CRUSH rule 0 x 867 [640,773,663,974,261]
+ CRUSH rule 0 x 868 [613,950,712,663,460]
+ CRUSH rule 0 x 869 [973,889,524,22,671]
+ CRUSH rule 0 x 870 [505,35,386,498,348]
+ CRUSH rule 0 x 871 [239,264,262,773,781]
+ CRUSH rule 0 x 872 [21,767,456,748,783]
+ CRUSH rule 0 x 873 [954,666,980,264,435]
+ CRUSH rule 0 x 874 [54,510,947,1,500]
+ CRUSH rule 0 x 875 [809,418,452,462,88]
+ CRUSH rule 0 x 876 [483,457,61,248,523]
+ CRUSH rule 0 x 877 [542,531,952,939,710]
+ CRUSH rule 0 x 878 [217,674,857,644,678]
+ CRUSH rule 0 x 879 [999,475,134,250,319]
+ CRUSH rule 0 x 880 [678,573,935,385,570]
+ CRUSH rule 0 x 881 [394,835,789,802,587]
+ CRUSH rule 0 x 882 [467,382,353,56,979]
+ CRUSH rule 0 x 883 [802,744,237,337,50]
+ CRUSH rule 0 x 884 [653,660,638,700,31]
+ CRUSH rule 0 x 885 [898,704,307,445,879]
+ CRUSH rule 0 x 886 [434,357,938,641,737]
+ CRUSH rule 0 x 887 [297,226,711,428,370]
+ CRUSH rule 0 x 888 [863,324,443,213,902]
+ CRUSH rule 0 x 889 [105,102,308,163,947]
+ CRUSH rule 0 x 890 [550,248,606,704,615]
+ CRUSH rule 0 x 891 [575,928,880,891,826]
+ CRUSH rule 0 x 892 [259,862,133,271,292]
+ CRUSH rule 0 x 893 [902,880,543,542,37]
+ CRUSH rule 0 x 894 [180,169,916,43,945]
+ CRUSH rule 0 x 895 [725,849,182,129,177]
+ CRUSH rule 0 x 896 [951,34,874,537,969]
+ CRUSH rule 0 x 897 [810,352,73,939,943]
+ CRUSH rule 0 x 898 [979,433,719,411,787]
+ CRUSH rule 0 x 899 [685,668,534,932,399]
+ CRUSH rule 0 x 900 [530,978,41,894,941]
+ CRUSH rule 0 x 901 [740,107,336,175,574]
+ CRUSH rule 0 x 902 [800,743,693,310,67]
+ CRUSH rule 0 x 903 [230,267,842,266,550]
+ CRUSH rule 0 x 904 [346,949,460,973,696]
+ CRUSH rule 0 x 905 [530,397,619,958,576]
+ CRUSH rule 0 x 906 [80,426,138,672,73]
+ CRUSH rule 0 x 907 [365,968,475,297,296]
+ CRUSH rule 0 x 908 [204,832,742,809,862]
+ CRUSH rule 0 x 909 [883,989,146,959,366]
+ CRUSH rule 0 x 910 [549,593,249,853,792]
+ CRUSH rule 0 x 911 [325,847,352,214,851]
+ CRUSH rule 0 x 912 [874,888,582,796,557]
+ CRUSH rule 0 x 913 [331,463,342,574,989]
+ CRUSH rule 0 x 914 [836,468,601,732,607]
+ CRUSH rule 0 x 915 [245,228,100,661,799]
+ CRUSH rule 0 x 916 [77,967,364,435,27]
+ CRUSH rule 0 x 917 [239,60,866,221,772]
+ CRUSH rule 0 x 918 [988,115,922,80,201]
+ CRUSH rule 0 x 919 [783,139,696,1,848]
+ CRUSH rule 0 x 920 [623,408,685,953,974]
+ CRUSH rule 0 x 921 [105,799,144,90,399]
+ CRUSH rule 0 x 922 [887,505,652,348,514]
+ CRUSH rule 0 x 923 [223,318,552,458,743]
+ CRUSH rule 0 x 924 [25,778,366,333,163]
+ CRUSH rule 0 x 925 [912,601,297,682,770]
+ CRUSH rule 0 x 926 [968,133,144,814,155]
+ CRUSH rule 0 x 927 [277,724,214,988,690]
+ CRUSH rule 0 x 928 [554,203,658,789,298]
+ CRUSH rule 0 x 929 [761,802,367,528,758]
+ CRUSH rule 0 x 930 [814,61,788,736,660]
+ CRUSH rule 0 x 931 [29,193,61,41,343]
+ CRUSH rule 0 x 932 [446,198,862,534,168]
+ CRUSH rule 0 x 933 [352,742,216,321,525]
+ CRUSH rule 0 x 934 [730,2,332,631,613]
+ CRUSH rule 0 x 935 [731,23,736,79,361]
+ CRUSH rule 0 x 936 [322,975,20,904,827]
+ CRUSH rule 0 x 937 [822,221,841,161,723]
+ CRUSH rule 0 x 938 [557,850,66,630,499]
+ CRUSH rule 0 x 939 [150,11,971,371,124]
+ CRUSH rule 0 x 940 [638,398,169,616,333]
+ CRUSH rule 0 x 941 [730,342,929,577,451]
+ CRUSH rule 0 x 942 [62,292,166,814,587]
+ CRUSH rule 0 x 943 [165,314,519,548,41]
+ CRUSH rule 0 x 944 [199,625,766,176,194]
+ CRUSH rule 0 x 945 [946,999,699,303,38]
+ CRUSH rule 0 x 946 [595,93,852,142,503]
+ CRUSH rule 0 x 947 [800,582,356,93,716]
+ CRUSH rule 0 x 948 [132,551,139,920,87]
+ CRUSH rule 0 x 949 [792,920,466,380,97]
+ CRUSH rule 0 x 950 [111,345,176,543,879]
+ CRUSH rule 0 x 951 [414,619,648,655,364]
+ CRUSH rule 0 x 952 [775,469,500,356,287]
+ CRUSH rule 0 x 953 [349,1,5,251,168]
+ CRUSH rule 0 x 954 [570,940,410,249,929]
+ CRUSH rule 0 x 955 [729,774,823,800,7]
+ CRUSH rule 0 x 956 [519,141,575,625,738]
+ CRUSH rule 0 x 957 [242,709,611,97,760]
+ CRUSH rule 0 x 958 [84,217,227,253,246]
+ CRUSH rule 0 x 959 [270,413,918,789,703]
+ CRUSH rule 0 x 960 [458,192,307,279,920]
+ CRUSH rule 0 x 961 [981,388,777,546,359]
+ CRUSH rule 0 x 962 [623,834,277,134,729]
+ CRUSH rule 0 x 963 [291,167,714,468,109]
+ CRUSH rule 0 x 964 [28,156,788,127,598]
+ CRUSH rule 0 x 965 [675,557,290,517,840]
+ CRUSH rule 0 x 966 [836,306,946,283,642]
+ CRUSH rule 0 x 967 [966,386,735,837,392]
+ CRUSH rule 0 x 968 [864,756,690,121,328]
+ CRUSH rule 0 x 969 [729,625,480,769,512]
+ CRUSH rule 0 x 970 [800,362,646,582,309]
+ CRUSH rule 0 x 971 [737,381,153,684,298]
+ CRUSH rule 0 x 972 [952,245,720,884,334]
+ CRUSH rule 0 x 973 [356,455,579,857,832]
+ CRUSH rule 0 x 974 [545,758,586,596,790]
+ CRUSH rule 0 x 975 [336,191,202,146,720]
+ CRUSH rule 0 x 976 [446,208,757,620,252]
+ CRUSH rule 0 x 977 [202,896,196,956,763]
+ CRUSH rule 0 x 978 [612,324,996,225,418]
+ CRUSH rule 0 x 979 [843,457,675,650,958]
+ CRUSH rule 0 x 980 [60,914,881,626,850]
+ CRUSH rule 0 x 981 [702,749,937,153,724]
+ CRUSH rule 0 x 982 [298,928,738,167,99]
+ CRUSH rule 0 x 983 [723,572,395,358,900]
+ CRUSH rule 0 x 984 [723,864,804,935,846]
+ CRUSH rule 0 x 985 [945,459,868,211,524]
+ CRUSH rule 0 x 986 [772,664,535,169,297]
+ CRUSH rule 0 x 987 [88,324,312,843,661]
+ CRUSH rule 0 x 988 [522,927,131,996,351]
+ CRUSH rule 0 x 989 [578,332,208,605,975]
+ CRUSH rule 0 x 990 [638,228,414,311,738]
+ CRUSH rule 0 x 991 [530,221,451,422,879]
+ CRUSH rule 0 x 992 [925,705,275,81,234]
+ CRUSH rule 0 x 993 [991,301,43,469,830]
+ CRUSH rule 0 x 994 [276,51,868,683,843]
+ CRUSH rule 0 x 995 [288,836,753,790,758]
+ CRUSH rule 0 x 996 [887,983,252,686,470]
+ CRUSH rule 0 x 997 [110,924,386,79,705]
+ CRUSH rule 0 x 998 [435,830,485,853,926]
+ CRUSH rule 0 x 999 [876,738,357,913,723]
+ CRUSH rule 0 x 1000 [178,963,638,430,845]
+ CRUSH rule 0 x 1001 [99,519,66,759,583]
+ CRUSH rule 0 x 1002 [515,534,468,866,878]
+ CRUSH rule 0 x 1003 [104,611,937,698,94]
+ CRUSH rule 0 x 1004 [269,638,724,375,491]
+ CRUSH rule 0 x 1005 [369,223,309,409,822]
+ CRUSH rule 0 x 1006 [40,107,69,275,79]
+ CRUSH rule 0 x 1007 [978,111,416,758,454]
+ CRUSH rule 0 x 1008 [965,956,624,832,421]
+ CRUSH rule 0 x 1009 [598,476,356,695,919]
+ CRUSH rule 0 x 1010 [767,523,239,517,29]
+ CRUSH rule 0 x 1011 [289,871,207,576,347]
+ CRUSH rule 0 x 1012 [128,28,370,31,341]
+ CRUSH rule 0 x 1013 [979,765,660,812,666]
+ CRUSH rule 0 x 1014 [979,948,513,88,47]
+ CRUSH rule 0 x 1015 [277,790,396,672,542]
+ CRUSH rule 0 x 1016 [262,73,128,886,839]
+ CRUSH rule 0 x 1017 [150,269,61,499,832]
+ CRUSH rule 0 x 1018 [555,829,554,944,406]
+ CRUSH rule 0 x 1019 [513,356,265,446,65]
+ CRUSH rule 0 x 1020 [158,161,877,704,948]
+ CRUSH rule 0 x 1021 [915,998,957,285,546]
+ CRUSH rule 0 x 1022 [967,829,973,640,703]
+ CRUSH rule 0 x 1023 [488,257,614,859,325]
+ rule 0 (data) num_rep 5 result size == 5:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356]
+ CRUSH rule 0 x 87 [997,317,463,626,685,909]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652]
+ CRUSH rule 0 x 194 [242,473,58,655,277,792]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849]
+ CRUSH rule 0 x 257 [825,264,867,529,409,291]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939]
+ CRUSH rule 0 x 262 [223,1,561,420,16,88]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544]
+ CRUSH rule 0 x 324 [16,745,511,439,272,668]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493]
+ CRUSH rule 0 x 374 [191,887,920,223,714,961]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149]
+ CRUSH rule 0 x 445 [485,528,209,964,753,554]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604]
+ CRUSH rule 0 x 699 [450,244,180,919,332,747]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948]
+ CRUSH rule 0 x 711 [227,653,731,150,842,534]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757]
+ CRUSH rule 0 x 742 [912,581,114,730,21,687]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762]
+ CRUSH rule 0 x 813 [805,114,683,629,462,285]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296]
+ CRUSH rule 0 x 868 [613,950,712,663,460,643]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173]
+ CRUSH rule 0 x 926 [968,133,144,814,155,709]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596]
+ CRUSH rule 0 x 974 [545,758,586,596,790,116]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419]
+ rule 0 (data) num_rep 6 result size == 6:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38]
+ CRUSH rule 0 x 87 [997,317,463,626,685,909,49]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359]
+ CRUSH rule 0 x 194 [242,473,58,655,277,792,887]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189]
+ CRUSH rule 0 x 257 [825,264,867,529,409,291,732]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516]
+ CRUSH rule 0 x 262 [223,1,561,420,16,88,534]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236]
+ CRUSH rule 0 x 324 [16,745,511,439,272,668,959]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128]
+ CRUSH rule 0 x 374 [191,887,920,223,714,961,760]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,136]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182]
+ CRUSH rule 0 x 445 [485,528,209,964,753,554,931]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,506]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468]
+ CRUSH rule 0 x 699 [450,244,180,919,332,747,453]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449]
+ CRUSH rule 0 x 711 [227,653,731,150,842,534,110]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863]
+ CRUSH rule 0 x 742 [912,581,114,730,21,687,81]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675]
+ CRUSH rule 0 x 813 [805,114,683,629,462,285,450]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988]
+ CRUSH rule 0 x 868 [613,950,712,663,460,643,547]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969]
+ CRUSH rule 0 x 926 [968,133,144,814,155,709,158]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,16]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549]
+ CRUSH rule 0 x 974 [545,758,586,596,790,116,993]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50]
+ rule 0 (data) num_rep 7 result size == 7:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966,750]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672,820]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527,901]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481,169]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847,220]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964,872]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884,726]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578,14]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462,207]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3,569]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811,484]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380,821]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117,393]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78,170]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855,76]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437,680]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786,969]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935,605]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883,394]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51,834]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975,636]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263,267]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557,633]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477,468]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800,309]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248,908]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501,909]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676,560]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770,569]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719,581]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426,522]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813,217]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428,63]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618,288]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663,877]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75,580]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676,524]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311,758]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929,910]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856,248]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980,503]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930,74]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456,713]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514,40]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160,527]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658,876]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45,390]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959,800]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738,47]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822,628]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83,344]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406,281]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304,899]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882,503]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339,902]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508,763]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77,500]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670,67]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8,762]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643,511]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194,277]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812,14]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45,723]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705,268]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451,891]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862,221]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334,656]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783,593]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705,114]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871,361]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684,411]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587,264]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642,529]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835,705]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633,560]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366,258]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735,953]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197,690]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183,917]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339,24]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51,766]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247,963]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580,51]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66,197]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681,618]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567,908]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38,326]
+ CRUSH rule 0 x 87 [997,317,463,626,685,909,49,28]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99,737]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320,921]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412,184]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495,299]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167,634]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343,949]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82,422]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648,661]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204,750]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83,542]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270,511]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37,138]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9,168]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12,369]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923,106]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933,231]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401,85]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434,121]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878,493]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589,614]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631,996]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856,296]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510,472]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18,949]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725,413]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738,253]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598,418]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421,39]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9,448]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725,142]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766,210]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570,278]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49,126]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424,868]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551,43]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877,248]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482,254]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736,737]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689,209]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909,809]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666,691]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149,904]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270,425]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203,667]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922,597]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14,444]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893,281]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712,649]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846,890]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363,323]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400,885]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384,58]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178,532]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193,258]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424,751]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266,70]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697,610]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577,348]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929,196]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514,546]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352,820]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406,556]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484,553]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671,883]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807,50]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942,864]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443,98]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58,641]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271,229]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384,446]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899,902]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209,641]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242,999]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909,642]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148,88]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587,854]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110,329]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524,389]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548,676]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935,266]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667,463]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601,909]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835,840]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100,910]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36,356]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471,529]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780,932]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51,211]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240,316]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756,647]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856,992]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724,923]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736,846]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168,224]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485,571]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109,903]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74,45]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485,511]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369,270]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566,378]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833,650]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37,1]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527,237]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999,754]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123,759]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359,721]
+ CRUSH rule 0 x 194 [242,473,58,655,277,792,887,561]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755,471]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413,834]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666,316]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355,574]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960,369]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132,271]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495,76]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153,272]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669,676]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697,881]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776,463]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230,367]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76,708]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96,841]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522,185]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14,921]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922,372]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857,894]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687,903]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579,79]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982,826]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973,898]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264,6]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34,497]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697,805]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686,982]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61,559]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597,904]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465,429]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329,320]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606,465]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876,984]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966,217]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261,389]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262,884]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93,880]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50,182]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106,877]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796,835]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103,95]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471,753]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573,128]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327,509]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894,884]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426,427]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450,677]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413,136]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234,756]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799,21]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785,107]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605,837]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989,999]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590,840]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713,237]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886,650]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270,619]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58,354]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838,131]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797,675]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605,453]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607,95]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189,627]
+ CRUSH rule 0 x 257 [825,264,867,529,409,291,732,224]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863,427]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77,191]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533,646]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516,136]
+ CRUSH rule 0 x 262 [223,1,561,420,16,88,534,289]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773,979]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715,916]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833,987]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7,557]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972,645]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715,536]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146,14]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476,571]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263,138]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710,779]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982,735]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370,990]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992,823]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634,98]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783,648]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350,600]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952,250]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895,914]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443,604]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939,879]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369,544]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60,546]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582,975]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699,627]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137,890]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195,538]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795,377]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473,209]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557,86]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806,911]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775,636]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459,833]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440,73]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266,82]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232,343]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213,286]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924,106]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904,838]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220,432]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400,365]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561,905]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214,944]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685,838]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761,648]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459,250]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976,551]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857,153]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90,855]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256,798]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497,929]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24,81]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952,599]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749,841]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761,725]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892,86]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290,97]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800,868]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523,366]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868,841]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187,380]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236,969]
+ CRUSH rule 0 x 324 [16,745,511,439,272,668,959,845]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462,268]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461,509]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750,669]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33,806]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516,713]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420,520]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624,649]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644,821]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243,633]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408,175]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635,367]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698,533]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956,907]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69,11]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607,953]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707,328]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720,503]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516,661]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660,655]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142,114]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127,732]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419,962]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783,661]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862,513]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795,143]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841,164]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766,775]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899,665]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279,616]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612,33]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985,412]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629,228]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891,656]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874,643]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397,52]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29,529]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643,286]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265,35]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444,613]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203,17]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113,373]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59,914]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685,641]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616,93]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284,184]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850,419]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797,547]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212,241]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128,207]
+ CRUSH rule 0 x 374 [191,887,920,223,714,961,760,571]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934,619]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446,897]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654,487]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728,860]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115,557]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255,806]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773,496]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276,56]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29,197]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488,435]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910,329]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232,692]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164,934]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474,267]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340,222]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852,196]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,136,818]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901,82]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652,543]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15,591]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126,179]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726,935]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996,558]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349,672]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749,410]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536,592]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79,32]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658,449]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202,650]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299,577]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776,296]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718,578]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72,345]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246,695]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213,806]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949,507]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313,351]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560,155]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618,907]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97,124]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694,332]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889,344]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138,374]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848,972]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823,884]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506,670]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764,274]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839,547]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905,588]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785,395]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80,236]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791,565]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334,588]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225,94]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857,562]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859,699]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194,366]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513,591]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52,897]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644,646]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371,731]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10,976]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443,23]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755,841]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414,152]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800,174]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205,236]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864,799]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774,350]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182,145]
+ CRUSH rule 0 x 445 [485,528,209,964,753,554,931,638]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314,714]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58,531]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807,688]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258,548]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721,31]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878,33]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604,49]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514,5]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106,103]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295,967]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504,96]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762,975]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399,558]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147,483]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841,512]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385,797]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240,911]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538,111]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789,0]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137,466]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659,35]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187,640]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201,249]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681,551]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558,71]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256,978]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809,56]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756,663]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446,333]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237,764]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984,226]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753,794]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941,843]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158,489]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371,807]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135,375]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731,233]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448,437]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283,170]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866,661]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621,955]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857,782]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924,965]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154,478]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566,779]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188,563]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591,423]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231,474]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389,819]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823,789]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129,763]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542,745]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949,173]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440,509]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930,573]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880,279]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490,852]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262,926]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758,104]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764,558]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898,161]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591,702]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313,282]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798,808]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786,152]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600,968]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899,451]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52,185]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374,985]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481,820]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453,660]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519,737]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625,434]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375,328]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558,956]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495,354]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753,624]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834,212]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8,550]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45,478]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853,643]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270,35]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651,368]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278,926]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577,509]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925,474]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949,374]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54,931]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206,682]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859,30]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148,668]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152,797]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37,485]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528,722]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95,789]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738,277]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137,934]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324,134]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250,362]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117,172]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699,610]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428,267]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517,684]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37,639]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915,570]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161,901]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57,217]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218,916]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693,953]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623,659]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830,762]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593,267]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124,107]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185,356]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887,376]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700,339]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62,817]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903,900]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641,751]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738,873]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184,127]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267,639]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398,521]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494,624]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533,674]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329,848]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33,312]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878,978]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333,841]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389,486]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177,545]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700,521]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487,976]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557,630]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608,755]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874,902]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984,622]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469,193]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642,226]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73,628]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794,682]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952,421]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195,581]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753,180]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811,413]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54,796]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667,318]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269,455]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994,16]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917,525]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180,482]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725,991]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286,280]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599,364]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243,264]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633,596]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697,763]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924,667]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983,281]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857,899]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954,94]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578,386]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148,33]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249,41]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160,888]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570,43]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296,735]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958,580]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92,12]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149,401]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74,307]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364,8]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222,749]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519,967]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35,620]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725,118]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446,798]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485,478]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137,140]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308,111]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225,841]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467,432]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205,580]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538,46]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920,830]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219,211]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416,45]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275,599]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296,337]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849,357]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236,318]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887,57]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701,887]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27,214]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858,138]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897,449]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610,665]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500,631]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660,857]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279,317]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720,135]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372,890]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453,732]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722,748]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869,150]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715,221]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119,818]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198,291]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,506,546]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605,214]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708,6]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858,16]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547,850]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170,954]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63,859]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402,205]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464,902]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170,65]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585,260]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798,658]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796,56]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795,783]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943,554]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938,600]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168,302]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755,330]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954,315]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371,402]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229,506]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528,875]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673,569]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103,763]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666,352]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247,620]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795,347]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774,192]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367,718]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345,599]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652,821]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154,121]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911,638]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917,622]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115,248]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333,161]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970,639]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451,290]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268,525]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828,276]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488,804]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300,989]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664,489]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153,922]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468,180]
+ CRUSH rule 0 x 699 [450,244,180,919,332,747,453,519]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68,641]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585,513]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466,687]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796,607]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655,595]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372,253]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923,770]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2,133]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939,331]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733,607]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449,28]
+ CRUSH rule 0 x 711 [227,653,731,150,842,534,110,639]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895,459]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53,839]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875,649]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322,417]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138,197]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237,62]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544,891]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828,975]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214,76]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177,264]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823,833]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985,846]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616,879]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359,720]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966,535]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105,31]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753,760]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572,933]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760,800]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528,890]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368,271]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880,343]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535,627]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690,59]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845,278]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482,564]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429,397]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146,17]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214,494]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863,571]
+ CRUSH rule 0 x 742 [912,581,114,730,21,687,81,145]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451,631]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843,699]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926,314]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450,651]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879,857]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304,999]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383,506]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169,171]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388,17]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88,315]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481,702]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336,724]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192,279]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850,191]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121,690]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165,686]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15,347]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345,69]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373,361]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664,350]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387,775]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70,202]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402,123]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357,310]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251,951]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645,975]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213,527]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961,324]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933,571]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891,959]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783,951]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890,425]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885,976]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396,76]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563,860]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65,954]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772,645]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17,336]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714,954]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812,516]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626,961]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481,198]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632,151]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490,481]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600,528]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194,216]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237,916]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788,164]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124,742]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406,858]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376,812]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811,459]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268,94]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646,451]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596,196]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473,250]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859,994]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941,643]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219,662]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846,53]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834,881]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663,949]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121,157]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640,607]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802,136]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166,929]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23,145]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620,648]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948,202]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675,28]
+ CRUSH rule 0 x 813 [805,114,683,629,462,285,450,948]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401,266]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314,670]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44,822]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260,172]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769,826]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523,770]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535,787]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621,815]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883,112]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282,641]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559,886]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161,998]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910,63]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212,916]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937,595]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669,330]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448,55]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142,157]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656,135]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764,766]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83,483]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537,937]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351,961]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903,29]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983,998]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47,859]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815,109]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226,46]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575,1]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486,199]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899,382]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611,512]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606,237]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363,893]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700,249]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847,463]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358,35]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95,337]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469,511]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774,679]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441,302]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686,702]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224,810]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12,763]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688,61]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463,903]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699,907]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901,487]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490,409]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172,980]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43,15]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850,40]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255,481]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988,730]
+ CRUSH rule 0 x 868 [613,950,712,663,460,643,547,734]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718,431]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54,992]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387,515]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180,800]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199,358]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93,915]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634,435]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322,141]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181,460]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329,591]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145,750]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319,630]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570,109]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974,483]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202,148]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389,381]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174,972]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56,582]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472,947]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806,53]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399,382]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996,561]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706,701]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53,333]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672,320]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648,685]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599,829]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210,529]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12,481]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342,37]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124,653]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380,419]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157,292]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178,624]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66,738]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957,801]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685,6]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30,169]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664,331]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484,391]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686,965]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824,552]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789,255]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226,889]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925,746]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70,280]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126,79]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255,133]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725,707]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583,923]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888,980]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532,124]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633,290]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952,474]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964,384]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584,31]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969,168]
+ CRUSH rule 0 x 926 [968,133,144,814,155,709,158,96]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465,775]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847,752]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744,171]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832,654]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487,839]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530,462]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568,61]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533,116]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772,49]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138,802]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630,308]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286,395]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408,49]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25,883]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964,28]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,16,440]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759,851]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678,915]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952,885]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933,267]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922,868]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81,220]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799,961]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355,220]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829,408]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16,746]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141,619]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129,696]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536,766]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169,751]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393,281]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346,377]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543,519]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855,49]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455,708]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856,477]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485,701]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361,255]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59,229]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929,773]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19,674]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433,520]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518,956]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576,411]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344,520]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754,540]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549,524]
+ CRUSH rule 0 x 974 [545,758,586,596,790,116,993,644]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330,308]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397,58]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783,828]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514,169]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677,173]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398,943]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536,212]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395,198]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927,597]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950,840]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911,208]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864,555]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76,894]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865,47]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155,380]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340,526]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754,928]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117,546]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382,428]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557,378]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158,265]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459,764]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210,698]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786,762]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15,585]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317,102]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739,922]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729,370]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614,783]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891,113]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597,969]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234,945]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5,444]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975,723]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234,383]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23,241]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48,570]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268,647]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808,351]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969,81]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145,11]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456,560]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637,731]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463,926]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768,245]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495,865]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676,322]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871,828]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50,560]
+ rule 0 (data) num_rep 8 result size == 8:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966,750,695]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672,820,782]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527,901,106]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481,169,816]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847,220,87]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964,872,66]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884,726,456]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578,14,543]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462,207,759]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3,569,603]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811,484,296]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380,821,470]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117,393,435]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78,170,333]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855,76,269]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437,680,761]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786,969,378]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935,605,228]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883,394,188]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51,834,690]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975,636,387]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263,267,128]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557,633,812]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477,468,303]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800,309,622]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248,908,624]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501,909,219]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676,560,753]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770,569,359]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719,581,816]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426,522,208]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813,217,750]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428,63,801]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618,288,846]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663,877,177]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75,580,568]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676,524,510]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311,758,201]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929,910,698]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856,248,279]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980,503,287]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930,74,844]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456,713,348]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514,40,261]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160,527,250]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658,876,201]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45,390,546]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959,800,743]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738,47,812]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822,628,696]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83,344,713]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406,281,991]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304,899,629]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882,503,327]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339,902,81]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508,763,108]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77,500,553]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670,67,114]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8,762,842]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643,511,370]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194,277,985]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812,14,55]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45,723,926]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705,268,140]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451,891,654]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862,221,66]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334,656,719]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783,593,814]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705,114,981]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871,361,533]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684,411,114]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587,264,446]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642,529,966]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835,705,745]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633,560,565]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366,258,711]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735,953,188]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197,690,426]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183,917,464]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339,24,581]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51,766,822]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247,963,0]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580,51,939]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66,197,698]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681,618,723]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567,908,514]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38,326,385]
+ CRUSH rule 0 x 87 [997,317,463,626,685,909,49,28,698]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99,737,942]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320,921,812]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412,184,529]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495,299,812]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167,634,781]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343,949,51]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82,422,738]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648,661,46]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204,750,322]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83,542,131]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270,511,586]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37,138,326]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9,168,590]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12,369,9]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923,106,653]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933,231,520]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401,85,139]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434,121,488]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878,493,550]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589,614,814]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631,996,630]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856,296,122]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510,472,270]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18,949,341]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725,413,187]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738,253,213]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598,418,641]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421,39,241]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9,448,816]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725,142,304]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766,210,528]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570,278,867]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49,126,410]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424,868,469]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551,43,590]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877,248,753]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482,254,158]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736,737,508]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689,209,99]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909,809,553]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666,691,899]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149,904,70]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270,425,864]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203,667,528]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922,597,680]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14,444,709]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893,281,226]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712,649,27]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846,890,151]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363,323,153]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400,885,907]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384,58,373]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178,532,883]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193,258,445]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424,751,746]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266,70,210]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697,610,203]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577,348,1]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929,196,880]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514,546,861]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352,820,857]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406,556,532]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484,553,315]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671,883,204]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807,50,206]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942,864,388]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443,98,27]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58,641,186]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271,229,961]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384,446,836]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899,902,956]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209,641,228]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242,999,974]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909,642,188]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148,88,764]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587,854,482]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110,329,361]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524,389,185]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548,676,448]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935,266,883]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667,463,818]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601,909,60]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835,840,548]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100,910,446]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36,356,274]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471,529,359]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780,932,573]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51,211,409]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240,316,337]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756,647,45]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856,992,696]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724,923,450]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736,846,926]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168,224,705]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485,571,796]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109,903,96]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74,45,438]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485,511,14]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369,270,16]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566,378,816]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833,650,761]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37,1,939]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527,237,295]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999,754,339]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123,759,147]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359,721,996]
+ CRUSH rule 0 x 194 [242,473,58,655,277,792,887,561,449]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755,471,744]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413,834,832]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666,316,243]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355,574,781]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960,369,584]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132,271,128]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495,76,598]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153,272,944]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669,676,762]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697,881,64]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776,463,327]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230,367,890]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76,708,101]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96,841,811]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522,185,949]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14,921,544]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922,372,581]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857,894,60]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687,903,620]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579,79,258]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982,826,488]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973,898,556]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264,6,603]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34,497,108]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697,805,976]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686,982,0]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61,559,846]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597,904,897]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465,429,614]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329,320,645]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606,465,354]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876,984,491]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966,217,501]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261,389,281]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262,884,215]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93,880,959]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50,182,541]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106,877,130]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796,835,714]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103,95,634]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471,753,350]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573,128,154]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327,509,325]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894,884,824]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426,427,736]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450,677,659]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413,136,411]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234,756,496]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799,21,13]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785,107,816]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605,837,1]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989,999,138]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590,840,586]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713,237,468]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886,650,827]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270,619,450]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58,354,16]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838,131,387]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797,675,452]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605,453,987]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607,95,121]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189,627,592]
+ CRUSH rule 0 x 257 [825,264,867,529,409,291,732,224,841]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863,427,873]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77,191,669]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533,646,475]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516,136,87]
+ CRUSH rule 0 x 262 [223,1,561,420,16,88,534,289,498]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773,979,719]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715,916,633]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833,987,445]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7,557,76]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972,645,882]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715,536,983]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146,14,414]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476,571,314]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263,138,526]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710,779,161]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982,735,58]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370,990,320]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992,823,321]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634,98,457]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783,648,927]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350,600,283]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952,250,206]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895,914,892]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443,604,638]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939,879,338]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369,544,721]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60,546,39]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582,975,810]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699,627,725]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137,890,487]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195,538,356]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795,377,825]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473,209,59]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557,86,89]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806,911,738]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775,636,232]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459,833,503]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440,73,29]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266,82,671]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232,343,831]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213,286,61]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924,106,63]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904,838,458]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220,432,393]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400,365,697]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561,905,522]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214,944,845]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685,838,3]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761,648,867]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459,250,415]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976,551,126]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857,153,970]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90,855,441]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256,798,743]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497,929,92]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24,81,215]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952,599,763]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749,841,969]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761,725,923]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892,86,214]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290,97,529]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800,868,364]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523,366,891]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868,841,563]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187,380,947]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236,969,232]
+ CRUSH rule 0 x 324 [16,745,511,439,272,668,959,845,759]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462,268,124]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461,509,75]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750,669,296]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33,806,181]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516,713,223]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420,520,395]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624,649,626]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644,821,238]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243,633,309]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408,175,189]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635,367,997]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698,533,683]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956,907,464]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69,11,624]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607,953,125]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707,328,325]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720,503,209]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516,661,985]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660,655,149]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142,114,108]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127,732,290]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419,962,304]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783,661,622]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862,513,112]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795,143,529]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841,164,765]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766,775,518]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899,665,802]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279,616,892]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612,33,122]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985,412,599]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629,228,150]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891,656,196]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874,643,550]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397,52,702]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29,529,156]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643,286,552]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265,35,662]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444,613,499]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203,17,743]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113,373,426]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59,914,521]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685,641,894]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616,93,283]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284,184,718]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850,419,307]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797,547,778]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212,241,346]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128,207,88]
+ CRUSH rule 0 x 374 [191,887,920,223,714,961,760,571,549]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934,619,527]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446,897,460]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654,487,891]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728,860,968]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115,557,165]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255,806,429]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773,496,433]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276,56,278]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29,197,822]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488,435,223]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910,329,396]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232,692,742]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164,934,297]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474,267,640]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340,222,74]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852,196,469]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,136,818,516]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901,82,695]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652,543,10]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15,591,420]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126,179,607]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726,935,278]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996,558,602]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349,672,826]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749,410,815]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536,592,886]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79,32,343]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658,449,886]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202,650,501]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299,577,563]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776,296,19]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718,578,580]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72,345,709]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246,695,76]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213,806,168]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949,507,361]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313,351,497]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560,155,661]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618,907,9]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97,124,229]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694,332,572]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889,344,838]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138,374,470]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848,972,781]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823,884,832]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506,670,285]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764,274,623]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839,547,599]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905,588,312]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785,395,884]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80,236,988]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791,565,352]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334,588,571]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225,94,303]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857,562,976]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859,699,91]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194,366,279]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513,591,599]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52,897,705]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644,646,747]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371,731,906]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10,976,284]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443,23,696]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755,841,94]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414,152,331]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800,174,117]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205,236,86]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864,799,762]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774,350,37]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182,145,347]
+ CRUSH rule 0 x 445 [485,528,209,964,753,554,931,638,892]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314,714,212]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58,531,827]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807,688,21]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258,548,905]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721,31,312]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878,33,3]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604,49,922]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514,5,494]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106,103,511]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295,967,132]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504,96,890]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762,975,485]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399,558,15]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147,483,517]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841,512,70]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385,797,917]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240,911,375]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538,111,232]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789,0,653]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137,466,531]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659,35,797]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187,640,601]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201,249,555]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681,551,768]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558,71,676]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256,978,409]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809,56,476]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756,663,167]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446,333,245]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237,764,682]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984,226,333]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753,794,237]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941,843,751]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158,489,515]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371,807,687]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135,375,613]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731,233,640]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448,437,219]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283,170,278]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866,661,581]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621,955,400]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857,782,24]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924,965,25]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154,478,512]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566,779,507]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188,563,490]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591,423,668]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231,474,946]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389,819,956]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823,789,217]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129,763,39]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542,745,131]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949,173,398]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440,509,295]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930,573,357]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880,279,616]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490,852,962]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262,926,316]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758,104,831]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764,558,891]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898,161,78]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591,702,346]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313,282,207]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798,808,139]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786,152,921]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600,968,355]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899,451,415]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52,185,197]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374,985,944]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481,820,517]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453,660,93]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519,737,20]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625,434,527]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375,328,3]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558,956,664]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495,354,665]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753,624,769]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834,212,906]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8,550,769]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45,478,911]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853,643,625]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270,35,809]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651,368,118]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278,926,280]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577,509,195]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925,474,669]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949,374,988]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54,931,4]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206,682,627]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859,30,61]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148,668,436]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152,797,492]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37,485,88]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528,722,59]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95,789,348]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738,277,225]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137,934,547]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324,134,839]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250,362,614]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117,172,764]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699,610,785]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428,267,229]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517,684,154]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37,639,934]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915,570,546]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161,901,342]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57,217,21]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218,916,561]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693,953,606]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623,659,403]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830,762,691]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593,267,17]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124,107,766]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185,356,87]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887,376,658]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700,339,687]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62,817,766]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903,900,812]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641,751,930]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738,873,47]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184,127,448]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267,639,646]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398,521,59]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494,624,352]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533,674,23]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329,848,235]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33,312,202]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878,978,1]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333,841,193]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389,486,10]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177,545,839]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700,521,825]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487,976,977]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557,630,468]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608,755,326]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874,902,958]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984,622,175]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469,193,737]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642,226,122]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73,628,884]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794,682,127]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952,421,875]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195,581,649]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753,180,160]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811,413,436]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54,796,369]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667,318,496]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269,455,168]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994,16,777]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917,525,945]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180,482,449]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725,991,955]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286,280,507]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599,364,546]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243,264,31]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633,596,988]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697,763,442]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924,667,356]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983,281,292]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857,899,349]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954,94,88]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578,386,226]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148,33,420]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249,41,778]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160,888,767]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570,43,362]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296,735,245]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958,580,197]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92,12,62]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149,401,14]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74,307,976]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364,8,342]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222,749,697]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519,967,737]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35,620,268]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725,118,875]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446,798,869]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485,478,185]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137,140,488]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308,111,207]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225,841,868]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467,432,944]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205,580,972]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538,46,300]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920,830,171]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219,211,157]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416,45,624]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275,599,219]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296,337,676]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849,357,317]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236,318,228]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887,57,16]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701,887,355]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27,214,226]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858,138,726]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897,449,42]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610,665,299]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500,631,833]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660,857,123]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279,317,414]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720,135,369]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372,890,30]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453,732,262]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722,748,599]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869,150,743]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715,221,31]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119,818,459]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198,291,71]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,506,546,266]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605,214,833]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708,6,746]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858,16,128]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547,850,499]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170,954,22]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63,859,215]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402,205,609]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464,902,276]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170,65,906]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585,260,254]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798,658,28]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796,56,736]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795,783,386]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943,554,395]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938,600,517]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168,302,681]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755,330,584]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954,315,735]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371,402,725]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229,506,565]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528,875,982]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673,569,701]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103,763,527]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666,352,576]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247,620,437]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795,347,831]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774,192,454]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367,718,345]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345,599,30]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652,821,641]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154,121,804]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911,638,690]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917,622,156]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115,248,566]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333,161,74]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970,639,835]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451,290,789]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268,525,425]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828,276,264]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488,804,501]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300,989,804]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664,489,598]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153,922,986]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468,180,783]
+ CRUSH rule 0 x 699 [450,244,180,919,332,747,453,519,100]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68,641,109]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585,513,907]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466,687,742]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796,607,894]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655,595,888]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372,253,807]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923,770,516]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2,133,256]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939,331,41]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733,607,763]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449,28,731]
+ CRUSH rule 0 x 711 [227,653,731,150,842,534,110,639,452]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895,459,341]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53,839,56]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875,649,23]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322,417,973]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138,197,32]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237,62,327]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544,891,210]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828,975,119]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214,76,870]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177,264,867]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823,833,1]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985,846,77]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616,879,480]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359,720,128]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966,535,669]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105,31,506]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753,760,254]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572,933,434]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760,800,945]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528,890,625]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368,271,778]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880,343,590]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535,627,224]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690,59,294]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845,278,559]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482,564,259]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429,397,872]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146,17,314]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214,494,593]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863,571,876]
+ CRUSH rule 0 x 742 [912,581,114,730,21,687,81,145,695]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451,631,911]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843,699,24]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926,314,771]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450,651,105]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879,857,727]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304,999,685]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383,506,570]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169,171,9]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388,17,892]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88,315,728]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481,702,431]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336,724,262]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192,279,371]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850,191,991]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121,690,544]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165,686,449]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15,347,311]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345,69,39]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373,361,684]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664,350,339]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387,775,797]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70,202,557]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402,123,902]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357,310,890]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251,951,675]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645,975,947]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213,527,546]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961,324,309]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933,571,822]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891,959,487]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783,951,982]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890,425,599]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885,976,936]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396,76,680]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563,860,388]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65,954,125]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772,645,254]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17,336,147]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714,954,271]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812,516,774]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626,961,576]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481,198,171]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632,151,586]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490,481,627]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600,528,966]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194,216,486]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237,916,374]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788,164,691]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124,742,990]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406,858,694]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376,812,886]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811,459,344]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268,94,648]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646,451,67]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596,196,93]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473,250,839]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859,994,831]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941,643,398]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219,662,142]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846,53,783]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834,881,23]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663,949,380]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121,157,934]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640,607,790]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802,136,305]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166,929,234]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23,145,184]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620,648,433]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948,202,521]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675,28,263]
+ CRUSH rule 0 x 813 [805,114,683,629,462,285,450,948,742]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401,266,905]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314,670,928]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44,822,332]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260,172,193]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769,826,519]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523,770,56]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535,787,267]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621,815,313]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883,112,601]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282,641,216]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559,886,753]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161,998,370]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910,63,288]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212,916,692]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937,595,935]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669,330,822]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448,55,965]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142,157,957]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656,135,341]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764,766,853]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83,483,811]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537,937,101]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351,961,52]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903,29,454]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983,998,320]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47,859,402]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815,109,762]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226,46,918]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575,1,312]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486,199,966]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899,382,851]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611,512,855]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606,237,414]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363,893,592]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700,249,214]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847,463,995]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358,35,913]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95,337,677]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469,511,999]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774,679,616]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441,302,437]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686,702,919]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224,810,404]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12,763,594]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688,61,545]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463,903,272]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699,907,532]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901,487,725]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490,409,15]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172,980,772]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43,15,29]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850,40,170]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255,481,435]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988,730,753]
+ CRUSH rule 0 x 868 [613,950,712,663,460,643,547,734,16]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718,431,968]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54,992,726]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387,515,98]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180,800,521]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199,358,805]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93,915,801]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634,435,778]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322,141,82]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181,460,459]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329,591,59]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145,750,54]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319,630,888]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570,109,896]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974,483,412]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202,148,129]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389,381,347]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174,972,544]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56,582,915]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472,947,35]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806,53,385]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399,382,761]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996,561,485]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706,701,501]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53,333,458]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672,320,394]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648,685,895]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599,829,809]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210,529,491]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12,481,539]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342,37,303]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124,653,574]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380,419,667]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157,292,724]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178,624,733]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66,738,419]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957,801,74]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685,6,689]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30,169,506]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664,331,184]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484,391,841]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686,965,515]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824,552,717]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789,255,896]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226,889,69]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925,746,664]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70,280,837]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126,79,652]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255,133,892]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725,707,47]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583,923,863]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888,980,33]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532,124,911]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633,290,155]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952,474,67]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964,384,454]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584,31,151]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969,168,500]
+ CRUSH rule 0 x 926 [968,133,144,814,155,709,158,96,739]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465,775,725]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847,752,780]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744,171,144]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832,654,567]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487,839,776]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530,462,202]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568,61,945]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533,116,254]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772,49,567]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138,802,885]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630,308,973]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286,395,927]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408,49,977]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25,883,867]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964,28,633]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,16,440,31]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759,851,617]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678,915,619]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952,885,987]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933,267,846]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922,868,413]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81,220,725]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799,961,564]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355,220,528]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829,408,568]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16,746,835]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141,619,234]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129,696,115]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536,766,579]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169,751,667]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393,281,227]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346,377,425]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543,519,496]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855,49,548]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455,708,649]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856,477,895]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485,701,76]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361,255,507]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59,229,819]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929,773,928]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19,674,395]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433,520,916]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518,956,398]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576,411,416]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344,520,546]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754,540,79]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549,524,109]
+ CRUSH rule 0 x 974 [545,758,586,596,790,116,993,644,405]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330,308,744]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397,58,57]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783,828,409]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514,169,99]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677,173,903]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398,943,764]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536,212,247]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395,198,100]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927,597,103]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950,840,427]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911,208,91]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864,555,687]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76,894,480]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865,47,116]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155,380,797]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340,526,728]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754,928,288]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117,546,798]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382,428,451]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557,378,936]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158,265,110]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459,764,859]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210,698,273]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786,762,444]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15,585,898]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317,102,200]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739,922,343]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729,370,326]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614,783,865]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891,113,424]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597,969,911]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234,945,598]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5,444,795]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975,723,909]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234,383,604]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23,241,838]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48,570,639]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268,647,669]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808,351,572]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969,81,586]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145,11,965]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456,560,935]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637,731,738]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463,926,475]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768,245,337]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495,865,698]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676,322,671]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871,828,440]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50,560,595]
+ rule 0 (data) num_rep 9 result size == 9:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966,750,695,503]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672,820,782,802]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527,901,106,273]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481,169,816,732]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847,220,87,254]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964,872,66,161]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884,726,456,796]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578,14,543,287]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462,207,759,701]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3,569,603,47]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811,484,296,320]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380,821,470,230]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117,393,435,514]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78,170,333,295]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855,76,269,673]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437,680,761,483]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786,969,378,246]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935,605,228,737]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883,394,188,459]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51,834,690,375]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975,636,387,600]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263,267,128,828]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557,633,812,521]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477,468,303,367]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800,309,622,673]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248,908,624,643]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501,909,219,686]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676,560,753,77]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770,569,359,733]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719,581,816,349]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426,522,208,699]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813,217,750,155]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428,63,801,735]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618,288,846,659]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663,877,177,567]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75,580,568,523]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676,524,510,480]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311,758,201,999]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929,910,698,386]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856,248,279,416]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980,503,287,564]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930,74,844,943]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456,713,348,311]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514,40,261,223]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160,527,250,247]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658,876,201,45]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45,390,546,908]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959,800,743,161]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738,47,812,166]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822,628,696,407]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83,344,713,857]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406,281,991,336]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304,899,629,701]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882,503,327,402]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339,902,81,239]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508,763,108,185]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77,500,553,417]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670,67,114,685]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8,762,842,884]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643,511,370,336]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194,277,985,554]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812,14,55,752]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45,723,926,180]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705,268,140,942]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451,891,654,294]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862,221,66,354]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334,656,719,810]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783,593,814,970]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705,114,981,267]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871,361,533,433]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684,411,114,42]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587,264,446,572]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642,529,966,919]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835,705,745,986]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633,560,565,410]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366,258,711,657]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735,953,188,516]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197,690,426,681]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183,917,464,518]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339,24,581,969]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51,766,822,687]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247,963,0,732]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580,51,939,278]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66,197,698,318]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681,618,723,516]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567,908,514,355]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38,326,385,899]
+ CRUSH rule 0 x 87 [997,317,463,626,685,909,49,28,698,706]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99,737,942,647]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320,921,812,908]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412,184,529,127]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495,299,812,743]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167,634,781,242]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343,949,51,732]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82,422,738,933]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648,661,46,112]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204,750,322,75]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83,542,131,240]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270,511,586,230]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37,138,326,603]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9,168,590,873]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12,369,9,872]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923,106,653,999]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933,231,520,303]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401,85,139,855]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434,121,488,465]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878,493,550,484]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589,614,814,970]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631,996,630,597]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856,296,122,477]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510,472,270,753]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18,949,341,837]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725,413,187,840]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738,253,213,541]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598,418,641,891]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421,39,241,881]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9,448,816,609]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725,142,304,591]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766,210,528,252]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570,278,867,489]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49,126,410,33]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424,868,469,183]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551,43,590,51]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877,248,753,395]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482,254,158,641]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736,737,508,150]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689,209,99,479]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909,809,553,785]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666,691,899,665]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149,904,70,566]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270,425,864,38]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203,667,528,387]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922,597,680,25]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14,444,709,592]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893,281,226,739]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712,649,27,274]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846,890,151,872]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363,323,153,238]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400,885,907,214]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384,58,373,121]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178,532,883,380]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193,258,445,451]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424,751,746,854]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266,70,210,61]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697,610,203,382]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577,348,1,323]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929,196,880,458]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514,546,861,796]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352,820,857,282]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406,556,532,750]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484,553,315,504]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671,883,204,12]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807,50,206,368]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942,864,388,639]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443,98,27,120]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58,641,186,553]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271,229,961,163]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384,446,836,917]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899,902,956,424]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209,641,228,213]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242,999,974,652]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909,642,188,143]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148,88,764,40]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587,854,482,522]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110,329,361,743]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524,389,185,893]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548,676,448,991]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935,266,883,77]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667,463,818,819]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601,909,60,255]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835,840,548,588]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100,910,446,541]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36,356,274,976]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471,529,359,571]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780,932,573,814]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51,211,409,865]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240,316,337,361]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756,647,45,752]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856,992,696,784]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724,923,450,340]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736,846,926,129]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168,224,705,185]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485,571,796,587]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109,903,96,526]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74,45,438,26]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485,511,14,329]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369,270,16,920]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566,378,816,553]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833,650,761,468]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37,1,939,463]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527,237,295,716]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999,754,339,271]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123,759,147,99]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359,721,996,318]
+ CRUSH rule 0 x 194 [242,473,58,655,277,792,887,561,449,911]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755,471,744,910]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413,834,832,368]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666,316,243,320]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355,574,781,550]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960,369,584,612]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132,271,128,632]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495,76,598,301]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153,272,944,741]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669,676,762,200]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697,881,64,107]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776,463,327,721]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230,367,890,953]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76,708,101,928]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96,841,811,979]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522,185,949,590]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14,921,544,106]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922,372,581,629]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857,894,60,426]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687,903,620,572]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579,79,258,42]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982,826,488,314]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973,898,556,293]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264,6,603,317]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34,497,108,180]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697,805,976,758]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686,982,0,32]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61,559,846,994]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597,904,897,376]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465,429,614,711]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329,320,645,829]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606,465,354,404]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876,984,491,784]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966,217,501,536]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261,389,281,467]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262,884,215,687]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93,880,959,111]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50,182,541,483]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106,877,130,387]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796,835,714,751]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103,95,634,844]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471,753,350,224]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573,128,154,326]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327,509,325,245]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894,884,824,343]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426,427,736,836]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450,677,659,183]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413,136,411,549]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234,756,496,779]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799,21,13,874]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785,107,816,89]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605,837,1,141]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989,999,138,674]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590,840,586,594]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713,237,468,340]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886,650,827,663]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270,619,450,707]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58,354,16,905]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838,131,387,664]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797,675,452,129]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605,453,987,316]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607,95,121,784]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189,627,592,241]
+ CRUSH rule 0 x 257 [825,264,867,529,409,291,732,224,841,681]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863,427,873,223]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77,191,669,61]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533,646,475,686]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516,136,87,410]
+ CRUSH rule 0 x 262 [223,1,561,420,16,88,534,289,498,357]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773,979,719,421]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715,916,633,121]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833,987,445,23]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7,557,76,987]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972,645,882,451]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715,536,983,688]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146,14,414,236]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476,571,314,538]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263,138,526,607]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710,779,161,222]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982,735,58,1]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370,990,320,52]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992,823,321,784]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634,98,457,487]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783,648,927,285]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350,600,283,422]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952,250,206,714]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895,914,892,149]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443,604,638,571]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939,879,338,786]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369,544,721,267]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60,546,39,71]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582,975,810,703]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699,627,725,389]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137,890,487,621]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195,538,356,523]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795,377,825,874]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473,209,59,346]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557,86,89,417]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806,911,738,969]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775,636,232,518]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459,833,503,207]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440,73,29,38]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266,82,671,320]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232,343,831,540]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213,286,61,961]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924,106,63,457]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904,838,458,828]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220,432,393,433]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400,365,697,497]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561,905,522,342]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214,944,845,895]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685,838,3,983]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761,648,867,488]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459,250,415,194]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976,551,126,307]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857,153,970,99]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90,855,441,736]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256,798,743,645]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497,929,92,639]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24,81,215,850]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952,599,763,183]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749,841,969,314]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761,725,923,461]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892,86,214,189]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290,97,529,220]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800,868,364,427]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523,366,891,998]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868,841,563,961]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187,380,947,952]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236,969,232,741]
+ CRUSH rule 0 x 324 [16,745,511,439,272,668,959,845,759,659]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462,268,124,431]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461,509,75,767]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750,669,296,314]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33,806,181,597]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516,713,223,395]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420,520,395,665]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624,649,626,928]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644,821,238,85]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243,633,309,616]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408,175,189,500]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635,367,997,819]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698,533,683,851]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956,907,464,39]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69,11,624,272]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607,953,125,899]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707,328,325,81]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720,503,209,748]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516,661,985,860]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660,655,149,994]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142,114,108,961]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127,732,290,494]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419,962,304,12]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783,661,622,426]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862,513,112,334]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795,143,529,765]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841,164,765,563]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766,775,518,155]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899,665,802,620]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279,616,892,262]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612,33,122,190]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985,412,599,375]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629,228,150,889]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891,656,196,232]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874,643,550,633]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397,52,702,981]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29,529,156,595]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643,286,552,985]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265,35,662,708]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444,613,499,389]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203,17,743,570]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113,373,426,941]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59,914,521,629]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685,641,894,813]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616,93,283,353]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284,184,718,684]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850,419,307,965]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797,547,778,958]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212,241,346,153]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128,207,88,641]
+ CRUSH rule 0 x 374 [191,887,920,223,714,961,760,571,549,723]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934,619,527,735]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446,897,460,869]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654,487,891,733]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728,860,968,475]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115,557,165,292]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255,806,429,306]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773,496,433,750]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276,56,278,772]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29,197,822,218]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488,435,223,783]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910,329,396,482]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232,692,742,939]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164,934,297,902]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474,267,640,53]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340,222,74,503]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852,196,469,672]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,136,818,516,19]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901,82,695,640]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652,543,10,287]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15,591,420,525]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126,179,607,623]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726,935,278,138]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996,558,602,528]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349,672,826,569]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749,410,815,214]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536,592,886,981]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79,32,343,468]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658,449,886,260]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202,650,501,161]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299,577,563,37]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776,296,19,972]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718,578,580,249]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72,345,709,89]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246,695,76,647]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213,806,168,907]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949,507,361,615]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313,351,497,571]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560,155,661,678]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618,907,9,291]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97,124,229,641]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694,332,572,681]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889,344,838,541]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138,374,470,59]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848,972,781,361]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823,884,832,361]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506,670,285,2]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764,274,623,613]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839,547,599,779]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905,588,312,114]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785,395,884,360]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80,236,988,682]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791,565,352,949]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334,588,571,428]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225,94,303,757]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857,562,976,977]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859,699,91,148]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194,366,279,30]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513,591,599,474]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52,897,705,680]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644,646,747,123]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371,731,906,721]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10,976,284,126]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443,23,696,245]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755,841,94,52]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414,152,331,985]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800,174,117,518]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205,236,86,929]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864,799,762,611]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774,350,37,929]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182,145,347,172]
+ CRUSH rule 0 x 445 [485,528,209,964,753,554,931,638,892,46]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314,714,212,646]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58,531,827,968]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807,688,21,841]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258,548,905,357]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721,31,312,491]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878,33,3,917]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604,49,922,414]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514,5,494,960]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106,103,511,624]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295,967,132,319]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504,96,890,230]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762,975,485,228]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399,558,15,441]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147,483,517,733]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841,512,70,914]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385,797,917,561]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240,911,375,412]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538,111,232,539]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789,0,653,566]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137,466,531,493]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659,35,797,235]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187,640,601,622]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201,249,555,646]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681,551,768,522]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558,71,676,655]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256,978,409,691]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809,56,476,137]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756,663,167,196]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446,333,245,689]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237,764,682,318]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984,226,333,916]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753,794,237,996]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941,843,751,451]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158,489,515,496]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371,807,687,932]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135,375,613,657]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731,233,640,492]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448,437,219,982]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283,170,278,77]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866,661,581,943]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621,955,400,261]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857,782,24,970]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924,965,25,925]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154,478,512,528]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566,779,507,133]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188,563,490,369]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591,423,668,365]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231,474,946,897]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389,819,956,597]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823,789,217,720]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129,763,39,651]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542,745,131,753]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949,173,398,586]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440,509,295,921]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930,573,357,491]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880,279,616,919]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490,852,962,387]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262,926,316,527]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758,104,831,710]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764,558,891,406]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898,161,78,414]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591,702,346,619]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313,282,207,626]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798,808,139,377]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786,152,921,884]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600,968,355,109]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899,451,415,920]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52,185,197,986]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374,985,944,101]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481,820,517,850]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453,660,93,134]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519,737,20,892]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625,434,527,630]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375,328,3,688]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558,956,664,468]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495,354,665,945]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753,624,769,853]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834,212,906,305]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8,550,769,975]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45,478,911,233]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853,643,625,43]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270,35,809,509]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651,368,118,1]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278,926,280,321]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577,509,195,722]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925,474,669,212]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949,374,988,367]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54,931,4,279]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206,682,627,827]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859,30,61,75]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148,668,436,192]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152,797,492,432]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37,485,88,273]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528,722,59,237]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95,789,348,342]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738,277,225,142]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137,934,547,351]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324,134,839,685]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250,362,614,123]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117,172,764,372]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699,610,785,811]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428,267,229,379]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517,684,154,108]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37,639,934,330]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915,570,546,724]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161,901,342,941]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57,217,21,70]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218,916,561,416]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693,953,606,894]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623,659,403,573]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830,762,691,951]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593,267,17,386]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124,107,766,260]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185,356,87,113]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887,376,658,366]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700,339,687,507]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62,817,766,376]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903,900,812,290]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641,751,930,856]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738,873,47,84]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184,127,448,327]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267,639,646,85]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398,521,59,250]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494,624,352,562]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533,674,23,683]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329,848,235,502]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33,312,202,355]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878,978,1,291]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333,841,193,146]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389,486,10,402]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177,545,839,722]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700,521,825,444]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487,976,977,58]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557,630,468,348]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608,755,326,604]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874,902,958,415]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984,622,175,804]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469,193,737,681]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642,226,122,703]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73,628,884,255]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794,682,127,372]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952,421,875,65]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195,581,649,488]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753,180,160,465]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811,413,436,162]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54,796,369,958]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667,318,496,74]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269,455,168,635]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994,16,777,901]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917,525,945,715]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180,482,449,647]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725,991,955,316]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286,280,507,912]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599,364,546,849]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243,264,31,897]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633,596,988,727]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697,763,442,542]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924,667,356,316]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983,281,292,953]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857,899,349,926]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954,94,88,617]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578,386,226,269]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148,33,420,669]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249,41,778,929]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160,888,767,727]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570,43,362,401]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296,735,245,714]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958,580,197,897]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92,12,62,719]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149,401,14,59]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74,307,976,613]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364,8,342,512]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222,749,697,853]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519,967,737,892]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35,620,268,902]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725,118,875,359]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446,798,869,586]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485,478,185,854]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137,140,488,725]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308,111,207,48]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225,841,868,249]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467,432,944,781]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205,580,972,427]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538,46,300,112]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920,830,171,759]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219,211,157,614]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416,45,624,253]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275,599,219,357]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296,337,676,416]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849,357,317,599]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236,318,228,575]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887,57,16,319]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701,887,355,632]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27,214,226,515]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858,138,726,1]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897,449,42,193]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610,665,299,852]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500,631,833,439]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660,857,123,910]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279,317,414,917]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720,135,369,711]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372,890,30,747]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453,732,262,803]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722,748,599,556]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869,150,743,438]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715,221,31,92]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119,818,459,782]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198,291,71,792]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,506,546,266,489]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605,214,833,862]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708,6,746,734]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858,16,128,144]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547,850,499,125]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170,954,22,394]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63,859,215,171]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402,205,609,831]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464,902,276,289]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170,65,906,943]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585,260,254,209]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798,658,28,334]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796,56,736,0]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795,783,386,956]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943,554,395,713]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938,600,517,49]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168,302,681,978]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755,330,584,525]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954,315,735,751]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371,402,725,870]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229,506,565,362]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528,875,982,782]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673,569,701,403]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103,763,527,83]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666,352,576,959]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247,620,437,29]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795,347,831,761]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774,192,454,380]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367,718,345,677]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345,599,30,892]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652,821,641,757]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154,121,804,85]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911,638,690,393]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917,622,156,826]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115,248,566,11]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333,161,74,907]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970,639,835,238]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451,290,789,853]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268,525,425,499]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828,276,264,237]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488,804,501,885]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300,989,804,72]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664,489,598,261]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153,922,986,480]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468,180,783,915]
+ CRUSH rule 0 x 699 [450,244,180,919,332,747,453,519,100,401]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68,641,109,182]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585,513,907,414]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466,687,742,170]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796,607,894,1]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655,595,888,354]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372,253,807,28]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923,770,516,982]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2,133,256,374]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939,331,41,175]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733,607,763,845]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449,28,731,573]
+ CRUSH rule 0 x 711 [227,653,731,150,842,534,110,639,452,502]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895,459,341,833]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53,839,56,829]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875,649,23,79]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322,417,973,582]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138,197,32,415]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237,62,327,958]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544,891,210,370]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828,975,119,87]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214,76,870,779]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177,264,867,327]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823,833,1,774]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985,846,77,467]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616,879,480,309]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359,720,128,627]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966,535,669,74]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105,31,506,430]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753,760,254,379]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572,933,434,816]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760,800,945,743]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528,890,625,535]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368,271,778,172]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880,343,590,539]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535,627,224,790]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690,59,294,569]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845,278,559,613]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482,564,259,896]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429,397,872,716]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146,17,314,156]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214,494,593,155]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863,571,876,528]
+ CRUSH rule 0 x 742 [912,581,114,730,21,687,81,145,695,245]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451,631,911,829]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843,699,24,714]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926,314,771,910]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450,651,105,921]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879,857,727,565]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304,999,685,843]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383,506,570,828]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169,171,9,58]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388,17,892,537]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88,315,728,669]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481,702,431,675]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336,724,262,61]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192,279,371,970]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850,191,991,63]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121,690,544,28]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165,686,449,831]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15,347,311,552]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345,69,39,199]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373,361,684,654]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664,350,339,980]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387,775,797,990]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70,202,557,471]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402,123,902,592]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357,310,890,850]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251,951,675,322]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645,975,947,199]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213,527,546,42]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961,324,309,772]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933,571,822,256]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891,959,487,355]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783,951,982,160]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890,425,599,485]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885,976,936,221]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396,76,680,108]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563,860,388,456]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65,954,125,358]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772,645,254,759]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17,336,147,829]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714,954,271,58]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812,516,774,985]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626,961,576,119]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481,198,171,944]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632,151,586,360]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490,481,627,740]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600,528,966,556]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194,216,486,782]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237,916,374,670]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788,164,691,329]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124,742,990,317]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406,858,694,351]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376,812,886,264]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811,459,344,719]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268,94,648,402]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646,451,67,738]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596,196,93,636]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473,250,839,895]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859,994,831,60]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941,643,398,325]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219,662,142,28]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846,53,783,511]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834,881,23,229]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663,949,380,39]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121,157,934,660]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640,607,790,832]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802,136,305,983]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166,929,234,460]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23,145,184,465]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620,648,433,997]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948,202,521,278]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675,28,263,410]
+ CRUSH rule 0 x 813 [805,114,683,629,462,285,450,948,742,605]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401,266,905,320]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314,670,928,727]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44,822,332,959]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260,172,193,516]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769,826,519,277]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523,770,56,437]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535,787,267,46]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621,815,313,916]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883,112,601,15]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282,641,216,929]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559,886,753,749]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161,998,370,10]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910,63,288,836]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212,916,692,653]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937,595,935,672]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669,330,822,36]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448,55,965,851]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142,157,957,920]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656,135,341,922]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764,766,853,890]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83,483,811,98]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537,937,101,507]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351,961,52,44]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903,29,454,647]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983,998,320,935]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47,859,402,947]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815,109,762,642]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226,46,918,220]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575,1,312,542]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486,199,966,501]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899,382,851,472]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611,512,855,760]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606,237,414,844]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363,893,592,634]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700,249,214,100]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847,463,995,314]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358,35,913,310]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95,337,677,275]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469,511,999,340]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774,679,616,492]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441,302,437,53]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686,702,919,971]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224,810,404,126]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12,763,594,668]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688,61,545,491]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463,903,272,8]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699,907,532,627]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901,487,725,204]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490,409,15,842]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172,980,772,115]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43,15,29,974]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850,40,170,31]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255,481,435,119]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988,730,753,888]
+ CRUSH rule 0 x 868 [613,950,712,663,460,643,547,734,16,649]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718,431,968,472]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54,992,726,783]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387,515,98,232]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180,800,521,270]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199,358,805,255]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93,915,801,43]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634,435,778,884]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322,141,82,412]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181,460,459,527]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329,591,59,4]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145,750,54,997]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319,630,888,970]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570,109,896,826]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974,483,412,547]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202,148,129,72]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389,381,347,314]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174,972,544,894]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56,582,915,541]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472,947,35,528]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806,53,385,387]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399,382,761,907]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996,561,485,482]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706,701,501,680]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53,333,458,77]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672,320,394,373]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648,685,895,735]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599,829,809,713]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210,529,491,289]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12,481,539,562]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342,37,303,70]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124,653,574,384]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380,419,667,56]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157,292,724,805]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178,624,733,498]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66,738,419,199]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957,801,74,934]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685,6,689,387]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30,169,506,497]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664,331,184,461]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484,391,841,967]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686,965,515,421]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824,552,717,159]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789,255,896,868]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226,889,69,237]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925,746,664,533]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70,280,837,367]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126,79,652,793]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255,133,892,524]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725,707,47,216]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583,923,863,232]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888,980,33,261]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532,124,911,206]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633,290,155,137]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952,474,67,938]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964,384,454,448]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584,31,151,178]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969,168,500,68]
+ CRUSH rule 0 x 926 [968,133,144,814,155,709,158,96,739,175]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465,775,725,414]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847,752,780,738]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744,171,144,704]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832,654,567,160]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487,839,776,117]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530,462,202,11]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568,61,945,154]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533,116,254,569]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772,49,567,47]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138,802,885,447]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630,308,973,934]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286,395,927,611]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408,49,977,243]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25,883,867,270]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964,28,633,960]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,16,440,31,906]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759,851,617,420]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678,915,619,69]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952,885,987,775]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933,267,846,866]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922,868,413,545]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81,220,725,211]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799,961,564,71]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355,220,528,747]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829,408,568,734]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16,746,835,529]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141,619,234,517]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129,696,115,984]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536,766,579,398]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169,751,667,381]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393,281,227,412]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346,377,425,533]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543,519,496,956]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855,49,548,861]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455,708,649,93]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856,477,895,89]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485,701,76,55]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361,255,507,540]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59,229,819,610]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929,773,928,579]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19,674,395,483]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433,520,916,41]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518,956,398,269]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576,411,416,523]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344,520,546,612]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754,540,79,174]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549,524,109,364]
+ CRUSH rule 0 x 974 [545,758,586,596,790,116,993,644,405,869]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330,308,744,843]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397,58,57,603]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783,828,409,529]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514,169,99,878]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677,173,903,781]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398,943,764,867]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536,212,247,523]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395,198,100,580]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927,597,103,461]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950,840,427,469]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911,208,91,999]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864,555,687,212]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76,894,480,323]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865,47,116,230]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155,380,797,177]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340,526,728,595]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754,928,288,668]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117,546,798,777]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382,428,451,216]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557,378,936,192]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158,265,110,171]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459,764,859,776]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210,698,273,955]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786,762,444,561]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15,585,898,902]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317,102,200,662]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739,922,343,574]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729,370,326,640]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614,783,865,245]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891,113,424,320]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597,969,911,241]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234,945,598,498]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5,444,795,150]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975,723,909,93]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234,383,604,903]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23,241,838,865]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48,570,639,230]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268,647,669,90]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808,351,572,403]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969,81,586,62]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145,11,965,669]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456,560,935,733]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637,731,738,154]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463,926,475,316]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768,245,337,197]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495,865,698,164]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676,322,671,622]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871,828,440,449]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50,560,595,554]
+ rule 0 (data) num_rep 10 result size == 10:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-firefly-tunables.t b/src/test/cli/crushtool/test-map-firefly-tunables.t
new file mode 100644
index 000000000..a17b15703
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-firefly-tunables.t
@@ -0,0 +1,10259 @@
+ $ crushtool -i "$TESTDIR/test-map-vary-r.crushmap" --test --show-mappings --show-statistics --rule 0 --set-choose-local-tries 0 --set-choose-local-fallback-tries 0 --set-choose-total-tries 50 --set-chooseleaf-descend-once 1 --set-chooseleaf-vary-r 1 --weight 12 0 --weight 20 0 --weight 30 0 --min-rep 1 --max-rep 10
+ rule 0 (data), x = 0..1023, numrep = 1..10
+ CRUSH rule 0 x 0 [101]
+ CRUSH rule 0 x 1 [80]
+ CRUSH rule 0 x 2 [91]
+ CRUSH rule 0 x 3 [51]
+ CRUSH rule 0 x 4 [50]
+ CRUSH rule 0 x 5 [89]
+ CRUSH rule 0 x 6 [91]
+ CRUSH rule 0 x 7 [104]
+ CRUSH rule 0 x 8 [78]
+ CRUSH rule 0 x 9 [101]
+ CRUSH rule 0 x 10 [61]
+ CRUSH rule 0 x 11 [13]
+ CRUSH rule 0 x 12 [83]
+ CRUSH rule 0 x 13 [108]
+ CRUSH rule 0 x 14 [105]
+ CRUSH rule 0 x 15 [18]
+ CRUSH rule 0 x 16 [103]
+ CRUSH rule 0 x 17 [85]
+ CRUSH rule 0 x 18 [11]
+ CRUSH rule 0 x 19 [75]
+ CRUSH rule 0 x 20 [79]
+ CRUSH rule 0 x 21 [84]
+ CRUSH rule 0 x 22 [23]
+ CRUSH rule 0 x 23 [118]
+ CRUSH rule 0 x 24 [83]
+ CRUSH rule 0 x 25 [81]
+ CRUSH rule 0 x 26 [38]
+ CRUSH rule 0 x 27 [76]
+ CRUSH rule 0 x 28 [76]
+ CRUSH rule 0 x 29 [24]
+ CRUSH rule 0 x 30 [94]
+ CRUSH rule 0 x 31 [76]
+ CRUSH rule 0 x 32 [72]
+ CRUSH rule 0 x 33 [77]
+ CRUSH rule 0 x 34 [7]
+ CRUSH rule 0 x 35 [22]
+ CRUSH rule 0 x 36 [104]
+ CRUSH rule 0 x 37 [61]
+ CRUSH rule 0 x 38 [72]
+ CRUSH rule 0 x 39 [68]
+ CRUSH rule 0 x 40 [103]
+ CRUSH rule 0 x 41 [85]
+ CRUSH rule 0 x 42 [106]
+ CRUSH rule 0 x 43 [10]
+ CRUSH rule 0 x 44 [101]
+ CRUSH rule 0 x 45 [83]
+ CRUSH rule 0 x 46 [65]
+ CRUSH rule 0 x 47 [106]
+ CRUSH rule 0 x 48 [34]
+ CRUSH rule 0 x 49 [0]
+ CRUSH rule 0 x 50 [42]
+ CRUSH rule 0 x 51 [104]
+ CRUSH rule 0 x 52 [83]
+ CRUSH rule 0 x 53 [32]
+ CRUSH rule 0 x 54 [9]
+ CRUSH rule 0 x 55 [14]
+ CRUSH rule 0 x 56 [21]
+ CRUSH rule 0 x 57 [93]
+ CRUSH rule 0 x 58 [45]
+ CRUSH rule 0 x 59 [80]
+ CRUSH rule 0 x 60 [90]
+ CRUSH rule 0 x 61 [88]
+ CRUSH rule 0 x 62 [81]
+ CRUSH rule 0 x 63 [79]
+ CRUSH rule 0 x 64 [1]
+ CRUSH rule 0 x 65 [32]
+ CRUSH rule 0 x 66 [48]
+ CRUSH rule 0 x 67 [94]
+ CRUSH rule 0 x 68 [102]
+ CRUSH rule 0 x 69 [62]
+ CRUSH rule 0 x 70 [84]
+ CRUSH rule 0 x 71 [9]
+ CRUSH rule 0 x 72 [97]
+ CRUSH rule 0 x 73 [64]
+ CRUSH rule 0 x 74 [29]
+ CRUSH rule 0 x 75 [29]
+ CRUSH rule 0 x 76 [55]
+ CRUSH rule 0 x 77 [107]
+ CRUSH rule 0 x 78 [11]
+ CRUSH rule 0 x 79 [64]
+ CRUSH rule 0 x 80 [0]
+ CRUSH rule 0 x 81 [71]
+ CRUSH rule 0 x 82 [37]
+ CRUSH rule 0 x 83 [92]
+ CRUSH rule 0 x 84 [49]
+ CRUSH rule 0 x 85 [54]
+ CRUSH rule 0 x 86 [37]
+ CRUSH rule 0 x 87 [116]
+ CRUSH rule 0 x 88 [38]
+ CRUSH rule 0 x 89 [76]
+ CRUSH rule 0 x 90 [14]
+ CRUSH rule 0 x 91 [68]
+ CRUSH rule 0 x 92 [86]
+ CRUSH rule 0 x 93 [44]
+ CRUSH rule 0 x 94 [61]
+ CRUSH rule 0 x 95 [93]
+ CRUSH rule 0 x 96 [66]
+ CRUSH rule 0 x 97 [111]
+ CRUSH rule 0 x 98 [93]
+ CRUSH rule 0 x 99 [78]
+ CRUSH rule 0 x 100 [6]
+ CRUSH rule 0 x 101 [84]
+ CRUSH rule 0 x 102 [82]
+ CRUSH rule 0 x 103 [66]
+ CRUSH rule 0 x 104 [14]
+ CRUSH rule 0 x 105 [87]
+ CRUSH rule 0 x 106 [69]
+ CRUSH rule 0 x 107 [1]
+ CRUSH rule 0 x 108 [94]
+ CRUSH rule 0 x 109 [112]
+ CRUSH rule 0 x 110 [54]
+ CRUSH rule 0 x 111 [10]
+ CRUSH rule 0 x 112 [89]
+ CRUSH rule 0 x 113 [69]
+ CRUSH rule 0 x 114 [79]
+ CRUSH rule 0 x 115 [50]
+ CRUSH rule 0 x 116 [96]
+ CRUSH rule 0 x 117 [87]
+ CRUSH rule 0 x 118 [23]
+ CRUSH rule 0 x 119 [104]
+ CRUSH rule 0 x 120 [57]
+ CRUSH rule 0 x 121 [105]
+ CRUSH rule 0 x 122 [45]
+ CRUSH rule 0 x 123 [112]
+ CRUSH rule 0 x 124 [110]
+ CRUSH rule 0 x 125 [66]
+ CRUSH rule 0 x 126 [51]
+ CRUSH rule 0 x 127 [70]
+ CRUSH rule 0 x 128 [90]
+ CRUSH rule 0 x 129 [103]
+ CRUSH rule 0 x 130 [50]
+ CRUSH rule 0 x 131 [23]
+ CRUSH rule 0 x 132 [69]
+ CRUSH rule 0 x 133 [52]
+ CRUSH rule 0 x 134 [78]
+ CRUSH rule 0 x 135 [78]
+ CRUSH rule 0 x 136 [32]
+ CRUSH rule 0 x 137 [11]
+ CRUSH rule 0 x 138 [17]
+ CRUSH rule 0 x 139 [89]
+ CRUSH rule 0 x 140 [39]
+ CRUSH rule 0 x 141 [89]
+ CRUSH rule 0 x 142 [70]
+ CRUSH rule 0 x 143 [51]
+ CRUSH rule 0 x 144 [13]
+ CRUSH rule 0 x 145 [77]
+ CRUSH rule 0 x 146 [8]
+ CRUSH rule 0 x 147 [22]
+ CRUSH rule 0 x 148 [74]
+ CRUSH rule 0 x 149 [76]
+ CRUSH rule 0 x 150 [14]
+ CRUSH rule 0 x 151 [90]
+ CRUSH rule 0 x 152 [49]
+ CRUSH rule 0 x 153 [71]
+ CRUSH rule 0 x 154 [94]
+ CRUSH rule 0 x 155 [75]
+ CRUSH rule 0 x 156 [94]
+ CRUSH rule 0 x 157 [112]
+ CRUSH rule 0 x 158 [26]
+ CRUSH rule 0 x 159 [52]
+ CRUSH rule 0 x 160 [41]
+ CRUSH rule 0 x 161 [19]
+ CRUSH rule 0 x 162 [55]
+ CRUSH rule 0 x 163 [54]
+ CRUSH rule 0 x 164 [45]
+ CRUSH rule 0 x 165 [25]
+ CRUSH rule 0 x 166 [73]
+ CRUSH rule 0 x 167 [89]
+ CRUSH rule 0 x 168 [47]
+ CRUSH rule 0 x 169 [51]
+ CRUSH rule 0 x 170 [68]
+ CRUSH rule 0 x 171 [73]
+ CRUSH rule 0 x 172 [33]
+ CRUSH rule 0 x 173 [102]
+ CRUSH rule 0 x 174 [116]
+ CRUSH rule 0 x 175 [3]
+ CRUSH rule 0 x 176 [94]
+ CRUSH rule 0 x 177 [52]
+ CRUSH rule 0 x 178 [39]
+ CRUSH rule 0 x 179 [72]
+ CRUSH rule 0 x 180 [60]
+ CRUSH rule 0 x 181 [18]
+ CRUSH rule 0 x 182 [22]
+ CRUSH rule 0 x 183 [11]
+ CRUSH rule 0 x 184 [92]
+ CRUSH rule 0 x 185 [97]
+ CRUSH rule 0 x 186 [67]
+ CRUSH rule 0 x 187 [116]
+ CRUSH rule 0 x 188 [69]
+ CRUSH rule 0 x 189 [47]
+ CRUSH rule 0 x 190 [90]
+ CRUSH rule 0 x 191 [49]
+ CRUSH rule 0 x 192 [68]
+ CRUSH rule 0 x 193 [0]
+ CRUSH rule 0 x 194 [17]
+ CRUSH rule 0 x 195 [119]
+ CRUSH rule 0 x 196 [72]
+ CRUSH rule 0 x 197 [106]
+ CRUSH rule 0 x 198 [114]
+ CRUSH rule 0 x 199 [0]
+ CRUSH rule 0 x 200 [35]
+ CRUSH rule 0 x 201 [14]
+ CRUSH rule 0 x 202 [98]
+ CRUSH rule 0 x 203 [36]
+ CRUSH rule 0 x 204 [10]
+ CRUSH rule 0 x 205 [22]
+ CRUSH rule 0 x 206 [49]
+ CRUSH rule 0 x 207 [80]
+ CRUSH rule 0 x 208 [63]
+ CRUSH rule 0 x 209 [85]
+ CRUSH rule 0 x 210 [79]
+ CRUSH rule 0 x 211 [26]
+ CRUSH rule 0 x 212 [28]
+ CRUSH rule 0 x 213 [91]
+ CRUSH rule 0 x 214 [78]
+ CRUSH rule 0 x 215 [61]
+ CRUSH rule 0 x 216 [99]
+ CRUSH rule 0 x 217 [86]
+ CRUSH rule 0 x 218 [93]
+ CRUSH rule 0 x 219 [28]
+ CRUSH rule 0 x 220 [56]
+ CRUSH rule 0 x 221 [0]
+ CRUSH rule 0 x 222 [50]
+ CRUSH rule 0 x 223 [29]
+ CRUSH rule 0 x 224 [52]
+ CRUSH rule 0 x 225 [61]
+ CRUSH rule 0 x 226 [44]
+ CRUSH rule 0 x 227 [42]
+ CRUSH rule 0 x 228 [117]
+ CRUSH rule 0 x 229 [100]
+ CRUSH rule 0 x 230 [41]
+ CRUSH rule 0 x 231 [56]
+ CRUSH rule 0 x 232 [23]
+ CRUSH rule 0 x 233 [88]
+ CRUSH rule 0 x 234 [4]
+ CRUSH rule 0 x 235 [26]
+ CRUSH rule 0 x 236 [32]
+ CRUSH rule 0 x 237 [92]
+ CRUSH rule 0 x 238 [10]
+ CRUSH rule 0 x 239 [15]
+ CRUSH rule 0 x 240 [109]
+ CRUSH rule 0 x 241 [47]
+ CRUSH rule 0 x 242 [24]
+ CRUSH rule 0 x 243 [76]
+ CRUSH rule 0 x 244 [96]
+ CRUSH rule 0 x 245 [27]
+ CRUSH rule 0 x 246 [35]
+ CRUSH rule 0 x 247 [99]
+ CRUSH rule 0 x 248 [8]
+ CRUSH rule 0 x 249 [85]
+ CRUSH rule 0 x 250 [79]
+ CRUSH rule 0 x 251 [28]
+ CRUSH rule 0 x 252 [95]
+ CRUSH rule 0 x 253 [109]
+ CRUSH rule 0 x 254 [80]
+ CRUSH rule 0 x 255 [112]
+ CRUSH rule 0 x 256 [37]
+ CRUSH rule 0 x 257 [69]
+ CRUSH rule 0 x 258 [34]
+ CRUSH rule 0 x 259 [70]
+ CRUSH rule 0 x 260 [98]
+ CRUSH rule 0 x 261 [94]
+ CRUSH rule 0 x 262 [42]
+ CRUSH rule 0 x 263 [65]
+ CRUSH rule 0 x 264 [36]
+ CRUSH rule 0 x 265 [66]
+ CRUSH rule 0 x 266 [75]
+ CRUSH rule 0 x 267 [58]
+ CRUSH rule 0 x 268 [38]
+ CRUSH rule 0 x 269 [43]
+ CRUSH rule 0 x 270 [58]
+ CRUSH rule 0 x 271 [19]
+ CRUSH rule 0 x 272 [73]
+ CRUSH rule 0 x 273 [108]
+ CRUSH rule 0 x 274 [47]
+ CRUSH rule 0 x 275 [92]
+ CRUSH rule 0 x 276 [7]
+ CRUSH rule 0 x 277 [19]
+ CRUSH rule 0 x 278 [116]
+ CRUSH rule 0 x 279 [101]
+ CRUSH rule 0 x 280 [113]
+ CRUSH rule 0 x 281 [14]
+ CRUSH rule 0 x 282 [106]
+ CRUSH rule 0 x 283 [8]
+ CRUSH rule 0 x 284 [10]
+ CRUSH rule 0 x 285 [88]
+ CRUSH rule 0 x 286 [27]
+ CRUSH rule 0 x 287 [84]
+ CRUSH rule 0 x 288 [103]
+ CRUSH rule 0 x 289 [9]
+ CRUSH rule 0 x 290 [115]
+ CRUSH rule 0 x 291 [48]
+ CRUSH rule 0 x 292 [52]
+ CRUSH rule 0 x 293 [27]
+ CRUSH rule 0 x 294 [79]
+ CRUSH rule 0 x 295 [37]
+ CRUSH rule 0 x 296 [56]
+ CRUSH rule 0 x 297 [35]
+ CRUSH rule 0 x 298 [71]
+ CRUSH rule 0 x 299 [79]
+ CRUSH rule 0 x 300 [67]
+ CRUSH rule 0 x 301 [51]
+ CRUSH rule 0 x 302 [78]
+ CRUSH rule 0 x 303 [19]
+ CRUSH rule 0 x 304 [101]
+ CRUSH rule 0 x 305 [81]
+ CRUSH rule 0 x 306 [0]
+ CRUSH rule 0 x 307 [44]
+ CRUSH rule 0 x 308 [91]
+ CRUSH rule 0 x 309 [15]
+ CRUSH rule 0 x 310 [26]
+ CRUSH rule 0 x 311 [36]
+ CRUSH rule 0 x 312 [33]
+ CRUSH rule 0 x 313 [104]
+ CRUSH rule 0 x 314 [28]
+ CRUSH rule 0 x 315 [16]
+ CRUSH rule 0 x 316 [4]
+ CRUSH rule 0 x 317 [118]
+ CRUSH rule 0 x 318 [32]
+ CRUSH rule 0 x 319 [24]
+ CRUSH rule 0 x 320 [36]
+ CRUSH rule 0 x 321 [26]
+ CRUSH rule 0 x 322 [87]
+ CRUSH rule 0 x 323 [73]
+ CRUSH rule 0 x 324 [64]
+ CRUSH rule 0 x 325 [52]
+ CRUSH rule 0 x 326 [111]
+ CRUSH rule 0 x 327 [62]
+ CRUSH rule 0 x 328 [7]
+ CRUSH rule 0 x 329 [93]
+ CRUSH rule 0 x 330 [24]
+ CRUSH rule 0 x 331 [41]
+ CRUSH rule 0 x 332 [61]
+ CRUSH rule 0 x 333 [16]
+ CRUSH rule 0 x 334 [94]
+ CRUSH rule 0 x 335 [71]
+ CRUSH rule 0 x 336 [16]
+ CRUSH rule 0 x 337 [37]
+ CRUSH rule 0 x 338 [109]
+ CRUSH rule 0 x 339 [13]
+ CRUSH rule 0 x 340 [119]
+ CRUSH rule 0 x 341 [63]
+ CRUSH rule 0 x 342 [92]
+ CRUSH rule 0 x 343 [49]
+ CRUSH rule 0 x 344 [103]
+ CRUSH rule 0 x 345 [56]
+ CRUSH rule 0 x 346 [3]
+ CRUSH rule 0 x 347 [106]
+ CRUSH rule 0 x 348 [10]
+ CRUSH rule 0 x 349 [96]
+ CRUSH rule 0 x 350 [63]
+ CRUSH rule 0 x 351 [60]
+ CRUSH rule 0 x 352 [103]
+ CRUSH rule 0 x 353 [10]
+ CRUSH rule 0 x 354 [55]
+ CRUSH rule 0 x 355 [73]
+ CRUSH rule 0 x 356 [114]
+ CRUSH rule 0 x 357 [70]
+ CRUSH rule 0 x 358 [97]
+ CRUSH rule 0 x 359 [4]
+ CRUSH rule 0 x 360 [106]
+ CRUSH rule 0 x 361 [27]
+ CRUSH rule 0 x 362 [28]
+ CRUSH rule 0 x 363 [45]
+ CRUSH rule 0 x 364 [23]
+ CRUSH rule 0 x 365 [57]
+ CRUSH rule 0 x 366 [14]
+ CRUSH rule 0 x 367 [108]
+ CRUSH rule 0 x 368 [103]
+ CRUSH rule 0 x 369 [11]
+ CRUSH rule 0 x 370 [11]
+ CRUSH rule 0 x 371 [34]
+ CRUSH rule 0 x 372 [58]
+ CRUSH rule 0 x 373 [6]
+ CRUSH rule 0 x 374 [110]
+ CRUSH rule 0 x 375 [19]
+ CRUSH rule 0 x 376 [22]
+ CRUSH rule 0 x 377 [93]
+ CRUSH rule 0 x 378 [67]
+ CRUSH rule 0 x 379 [77]
+ CRUSH rule 0 x 380 [3]
+ CRUSH rule 0 x 381 [55]
+ CRUSH rule 0 x 382 [26]
+ CRUSH rule 0 x 383 [48]
+ CRUSH rule 0 x 384 [15]
+ CRUSH rule 0 x 385 [82]
+ CRUSH rule 0 x 386 [108]
+ CRUSH rule 0 x 387 [70]
+ CRUSH rule 0 x 388 [5]
+ CRUSH rule 0 x 389 [14]
+ CRUSH rule 0 x 390 [68]
+ CRUSH rule 0 x 391 [113]
+ CRUSH rule 0 x 392 [72]
+ CRUSH rule 0 x 393 [115]
+ CRUSH rule 0 x 394 [38]
+ CRUSH rule 0 x 395 [0]
+ CRUSH rule 0 x 396 [59]
+ CRUSH rule 0 x 397 [87]
+ CRUSH rule 0 x 398 [44]
+ CRUSH rule 0 x 399 [9]
+ CRUSH rule 0 x 400 [19]
+ CRUSH rule 0 x 401 [79]
+ CRUSH rule 0 x 402 [107]
+ CRUSH rule 0 x 403 [23]
+ CRUSH rule 0 x 404 [76]
+ CRUSH rule 0 x 405 [10]
+ CRUSH rule 0 x 406 [38]
+ CRUSH rule 0 x 407 [70]
+ CRUSH rule 0 x 408 [55]
+ CRUSH rule 0 x 409 [102]
+ CRUSH rule 0 x 410 [59]
+ CRUSH rule 0 x 411 [34]
+ CRUSH rule 0 x 412 [108]
+ CRUSH rule 0 x 413 [54]
+ CRUSH rule 0 x 414 [70]
+ CRUSH rule 0 x 415 [107]
+ CRUSH rule 0 x 416 [21]
+ CRUSH rule 0 x 417 [8]
+ CRUSH rule 0 x 418 [51]
+ CRUSH rule 0 x 419 [8]
+ CRUSH rule 0 x 420 [109]
+ CRUSH rule 0 x 421 [114]
+ CRUSH rule 0 x 422 [109]
+ CRUSH rule 0 x 423 [59]
+ CRUSH rule 0 x 424 [71]
+ CRUSH rule 0 x 425 [101]
+ CRUSH rule 0 x 426 [47]
+ CRUSH rule 0 x 427 [8]
+ CRUSH rule 0 x 428 [68]
+ CRUSH rule 0 x 429 [76]
+ CRUSH rule 0 x 430 [69]
+ CRUSH rule 0 x 431 [70]
+ CRUSH rule 0 x 432 [46]
+ CRUSH rule 0 x 433 [6]
+ CRUSH rule 0 x 434 [64]
+ CRUSH rule 0 x 435 [16]
+ CRUSH rule 0 x 436 [89]
+ CRUSH rule 0 x 437 [29]
+ CRUSH rule 0 x 438 [105]
+ CRUSH rule 0 x 439 [29]
+ CRUSH rule 0 x 440 [38]
+ CRUSH rule 0 x 441 [112]
+ CRUSH rule 0 x 442 [55]
+ CRUSH rule 0 x 443 [44]
+ CRUSH rule 0 x 444 [72]
+ CRUSH rule 0 x 445 [19]
+ CRUSH rule 0 x 446 [40]
+ CRUSH rule 0 x 447 [13]
+ CRUSH rule 0 x 448 [7]
+ CRUSH rule 0 x 449 [67]
+ CRUSH rule 0 x 450 [117]
+ CRUSH rule 0 x 451 [93]
+ CRUSH rule 0 x 452 [70]
+ CRUSH rule 0 x 453 [82]
+ CRUSH rule 0 x 454 [53]
+ CRUSH rule 0 x 455 [91]
+ CRUSH rule 0 x 456 [101]
+ CRUSH rule 0 x 457 [113]
+ CRUSH rule 0 x 458 [53]
+ CRUSH rule 0 x 459 [25]
+ CRUSH rule 0 x 460 [105]
+ CRUSH rule 0 x 461 [102]
+ CRUSH rule 0 x 462 [98]
+ CRUSH rule 0 x 463 [108]
+ CRUSH rule 0 x 464 [19]
+ CRUSH rule 0 x 465 [29]
+ CRUSH rule 0 x 466 [66]
+ CRUSH rule 0 x 467 [6]
+ CRUSH rule 0 x 468 [97]
+ CRUSH rule 0 x 469 [98]
+ CRUSH rule 0 x 470 [50]
+ CRUSH rule 0 x 471 [40]
+ CRUSH rule 0 x 472 [74]
+ CRUSH rule 0 x 473 [95]
+ CRUSH rule 0 x 474 [51]
+ CRUSH rule 0 x 475 [49]
+ CRUSH rule 0 x 476 [110]
+ CRUSH rule 0 x 477 [25]
+ CRUSH rule 0 x 478 [47]
+ CRUSH rule 0 x 479 [70]
+ CRUSH rule 0 x 480 [62]
+ CRUSH rule 0 x 481 [26]
+ CRUSH rule 0 x 482 [84]
+ CRUSH rule 0 x 483 [15]
+ CRUSH rule 0 x 484 [37]
+ CRUSH rule 0 x 485 [47]
+ CRUSH rule 0 x 486 [92]
+ CRUSH rule 0 x 487 [106]
+ CRUSH rule 0 x 488 [42]
+ CRUSH rule 0 x 489 [76]
+ CRUSH rule 0 x 490 [68]
+ CRUSH rule 0 x 491 [80]
+ CRUSH rule 0 x 492 [21]
+ CRUSH rule 0 x 493 [99]
+ CRUSH rule 0 x 494 [4]
+ CRUSH rule 0 x 495 [40]
+ CRUSH rule 0 x 496 [93]
+ CRUSH rule 0 x 497 [102]
+ CRUSH rule 0 x 498 [68]
+ CRUSH rule 0 x 499 [10]
+ CRUSH rule 0 x 500 [50]
+ CRUSH rule 0 x 501 [60]
+ CRUSH rule 0 x 502 [11]
+ CRUSH rule 0 x 503 [117]
+ CRUSH rule 0 x 504 [90]
+ CRUSH rule 0 x 505 [91]
+ CRUSH rule 0 x 506 [82]
+ CRUSH rule 0 x 507 [81]
+ CRUSH rule 0 x 508 [34]
+ CRUSH rule 0 x 509 [88]
+ CRUSH rule 0 x 510 [11]
+ CRUSH rule 0 x 511 [72]
+ CRUSH rule 0 x 512 [118]
+ CRUSH rule 0 x 513 [22]
+ CRUSH rule 0 x 514 [82]
+ CRUSH rule 0 x 515 [27]
+ CRUSH rule 0 x 516 [66]
+ CRUSH rule 0 x 517 [83]
+ CRUSH rule 0 x 518 [18]
+ CRUSH rule 0 x 519 [67]
+ CRUSH rule 0 x 520 [15]
+ CRUSH rule 0 x 521 [63]
+ CRUSH rule 0 x 522 [56]
+ CRUSH rule 0 x 523 [36]
+ CRUSH rule 0 x 524 [33]
+ CRUSH rule 0 x 525 [3]
+ CRUSH rule 0 x 526 [83]
+ CRUSH rule 0 x 527 [37]
+ CRUSH rule 0 x 528 [108]
+ CRUSH rule 0 x 529 [107]
+ CRUSH rule 0 x 530 [49]
+ CRUSH rule 0 x 531 [27]
+ CRUSH rule 0 x 532 [68]
+ CRUSH rule 0 x 533 [5]
+ CRUSH rule 0 x 534 [97]
+ CRUSH rule 0 x 535 [8]
+ CRUSH rule 0 x 536 [3]
+ CRUSH rule 0 x 537 [116]
+ CRUSH rule 0 x 538 [85]
+ CRUSH rule 0 x 539 [10]
+ CRUSH rule 0 x 540 [100]
+ CRUSH rule 0 x 541 [111]
+ CRUSH rule 0 x 542 [50]
+ CRUSH rule 0 x 543 [45]
+ CRUSH rule 0 x 544 [106]
+ CRUSH rule 0 x 545 [43]
+ CRUSH rule 0 x 546 [108]
+ CRUSH rule 0 x 547 [67]
+ CRUSH rule 0 x 548 [58]
+ CRUSH rule 0 x 549 [60]
+ CRUSH rule 0 x 550 [47]
+ CRUSH rule 0 x 551 [14]
+ CRUSH rule 0 x 552 [70]
+ CRUSH rule 0 x 553 [96]
+ CRUSH rule 0 x 554 [61]
+ CRUSH rule 0 x 555 [76]
+ CRUSH rule 0 x 556 [106]
+ CRUSH rule 0 x 557 [39]
+ CRUSH rule 0 x 558 [70]
+ CRUSH rule 0 x 559 [106]
+ CRUSH rule 0 x 560 [94]
+ CRUSH rule 0 x 561 [27]
+ CRUSH rule 0 x 562 [97]
+ CRUSH rule 0 x 563 [64]
+ CRUSH rule 0 x 564 [96]
+ CRUSH rule 0 x 565 [66]
+ CRUSH rule 0 x 566 [27]
+ CRUSH rule 0 x 567 [88]
+ CRUSH rule 0 x 568 [106]
+ CRUSH rule 0 x 569 [102]
+ CRUSH rule 0 x 570 [98]
+ CRUSH rule 0 x 571 [95]
+ CRUSH rule 0 x 572 [62]
+ CRUSH rule 0 x 573 [51]
+ CRUSH rule 0 x 574 [89]
+ CRUSH rule 0 x 575 [87]
+ CRUSH rule 0 x 576 [112]
+ CRUSH rule 0 x 577 [8]
+ CRUSH rule 0 x 578 [64]
+ CRUSH rule 0 x 579 [78]
+ CRUSH rule 0 x 580 [68]
+ CRUSH rule 0 x 581 [55]
+ CRUSH rule 0 x 582 [15]
+ CRUSH rule 0 x 583 [74]
+ CRUSH rule 0 x 584 [22]
+ CRUSH rule 0 x 585 [35]
+ CRUSH rule 0 x 586 [33]
+ CRUSH rule 0 x 587 [106]
+ CRUSH rule 0 x 588 [0]
+ CRUSH rule 0 x 589 [7]
+ CRUSH rule 0 x 590 [40]
+ CRUSH rule 0 x 591 [42]
+ CRUSH rule 0 x 592 [45]
+ CRUSH rule 0 x 593 [89]
+ CRUSH rule 0 x 594 [27]
+ CRUSH rule 0 x 595 [7]
+ CRUSH rule 0 x 596 [82]
+ CRUSH rule 0 x 597 [72]
+ CRUSH rule 0 x 598 [34]
+ CRUSH rule 0 x 599 [119]
+ CRUSH rule 0 x 600 [24]
+ CRUSH rule 0 x 601 [104]
+ CRUSH rule 0 x 602 [48]
+ CRUSH rule 0 x 603 [24]
+ CRUSH rule 0 x 604 [89]
+ CRUSH rule 0 x 605 [104]
+ CRUSH rule 0 x 606 [49]
+ CRUSH rule 0 x 607 [95]
+ CRUSH rule 0 x 608 [112]
+ CRUSH rule 0 x 609 [61]
+ CRUSH rule 0 x 610 [106]
+ CRUSH rule 0 x 611 [66]
+ CRUSH rule 0 x 612 [103]
+ CRUSH rule 0 x 613 [13]
+ CRUSH rule 0 x 614 [81]
+ CRUSH rule 0 x 615 [61]
+ CRUSH rule 0 x 616 [41]
+ CRUSH rule 0 x 617 [111]
+ CRUSH rule 0 x 618 [26]
+ CRUSH rule 0 x 619 [92]
+ CRUSH rule 0 x 620 [108]
+ CRUSH rule 0 x 621 [106]
+ CRUSH rule 0 x 622 [67]
+ CRUSH rule 0 x 623 [94]
+ CRUSH rule 0 x 624 [115]
+ CRUSH rule 0 x 625 [111]
+ CRUSH rule 0 x 626 [3]
+ CRUSH rule 0 x 627 [19]
+ CRUSH rule 0 x 628 [65]
+ CRUSH rule 0 x 629 [6]
+ CRUSH rule 0 x 630 [22]
+ CRUSH rule 0 x 631 [35]
+ CRUSH rule 0 x 632 [81]
+ CRUSH rule 0 x 633 [65]
+ CRUSH rule 0 x 634 [87]
+ CRUSH rule 0 x 635 [40]
+ CRUSH rule 0 x 636 [23]
+ CRUSH rule 0 x 637 [102]
+ CRUSH rule 0 x 638 [43]
+ CRUSH rule 0 x 639 [31]
+ CRUSH rule 0 x 640 [113]
+ CRUSH rule 0 x 641 [45]
+ CRUSH rule 0 x 642 [47]
+ CRUSH rule 0 x 643 [64]
+ CRUSH rule 0 x 644 [31]
+ CRUSH rule 0 x 645 [76]
+ CRUSH rule 0 x 646 [37]
+ CRUSH rule 0 x 647 [58]
+ CRUSH rule 0 x 648 [31]
+ CRUSH rule 0 x 649 [88]
+ CRUSH rule 0 x 650 [116]
+ CRUSH rule 0 x 651 [97]
+ CRUSH rule 0 x 652 [57]
+ CRUSH rule 0 x 653 [8]
+ CRUSH rule 0 x 654 [49]
+ CRUSH rule 0 x 655 [89]
+ CRUSH rule 0 x 656 [0]
+ CRUSH rule 0 x 657 [47]
+ CRUSH rule 0 x 658 [75]
+ CRUSH rule 0 x 659 [26]
+ CRUSH rule 0 x 660 [65]
+ CRUSH rule 0 x 661 [91]
+ CRUSH rule 0 x 662 [111]
+ CRUSH rule 0 x 663 [88]
+ CRUSH rule 0 x 664 [59]
+ CRUSH rule 0 x 665 [78]
+ CRUSH rule 0 x 666 [112]
+ CRUSH rule 0 x 667 [97]
+ CRUSH rule 0 x 668 [97]
+ CRUSH rule 0 x 669 [85]
+ CRUSH rule 0 x 670 [41]
+ CRUSH rule 0 x 671 [116]
+ CRUSH rule 0 x 672 [44]
+ CRUSH rule 0 x 673 [83]
+ CRUSH rule 0 x 674 [36]
+ CRUSH rule 0 x 675 [88]
+ CRUSH rule 0 x 676 [62]
+ CRUSH rule 0 x 677 [88]
+ CRUSH rule 0 x 678 [98]
+ CRUSH rule 0 x 679 [33]
+ CRUSH rule 0 x 680 [55]
+ CRUSH rule 0 x 681 [115]
+ CRUSH rule 0 x 682 [27]
+ CRUSH rule 0 x 683 [57]
+ CRUSH rule 0 x 684 [22]
+ CRUSH rule 0 x 685 [106]
+ CRUSH rule 0 x 686 [86]
+ CRUSH rule 0 x 687 [32]
+ CRUSH rule 0 x 688 [80]
+ CRUSH rule 0 x 689 [6]
+ CRUSH rule 0 x 690 [43]
+ CRUSH rule 0 x 691 [34]
+ CRUSH rule 0 x 692 [40]
+ CRUSH rule 0 x 693 [29]
+ CRUSH rule 0 x 694 [6]
+ CRUSH rule 0 x 695 [19]
+ CRUSH rule 0 x 696 [36]
+ CRUSH rule 0 x 697 [96]
+ CRUSH rule 0 x 698 [61]
+ CRUSH rule 0 x 699 [47]
+ CRUSH rule 0 x 700 [99]
+ CRUSH rule 0 x 701 [42]
+ CRUSH rule 0 x 702 [0]
+ CRUSH rule 0 x 703 [92]
+ CRUSH rule 0 x 704 [10]
+ CRUSH rule 0 x 705 [105]
+ CRUSH rule 0 x 706 [74]
+ CRUSH rule 0 x 707 [0]
+ CRUSH rule 0 x 708 [84]
+ CRUSH rule 0 x 709 [114]
+ CRUSH rule 0 x 710 [94]
+ CRUSH rule 0 x 711 [68]
+ CRUSH rule 0 x 712 [34]
+ CRUSH rule 0 x 713 [29]
+ CRUSH rule 0 x 714 [81]
+ CRUSH rule 0 x 715 [71]
+ CRUSH rule 0 x 716 [40]
+ CRUSH rule 0 x 717 [61]
+ CRUSH rule 0 x 718 [40]
+ CRUSH rule 0 x 719 [59]
+ CRUSH rule 0 x 720 [69]
+ CRUSH rule 0 x 721 [62]
+ CRUSH rule 0 x 722 [115]
+ CRUSH rule 0 x 723 [117]
+ CRUSH rule 0 x 724 [45]
+ CRUSH rule 0 x 725 [53]
+ CRUSH rule 0 x 726 [84]
+ CRUSH rule 0 x 727 [109]
+ CRUSH rule 0 x 728 [76]
+ CRUSH rule 0 x 729 [108]
+ CRUSH rule 0 x 730 [28]
+ CRUSH rule 0 x 731 [78]
+ CRUSH rule 0 x 732 [55]
+ CRUSH rule 0 x 733 [84]
+ CRUSH rule 0 x 734 [27]
+ CRUSH rule 0 x 735 [83]
+ CRUSH rule 0 x 736 [70]
+ CRUSH rule 0 x 737 [117]
+ CRUSH rule 0 x 738 [118]
+ CRUSH rule 0 x 739 [87]
+ CRUSH rule 0 x 740 [29]
+ CRUSH rule 0 x 741 [96]
+ CRUSH rule 0 x 742 [106]
+ CRUSH rule 0 x 743 [105]
+ CRUSH rule 0 x 744 [23]
+ CRUSH rule 0 x 745 [28]
+ CRUSH rule 0 x 746 [56]
+ CRUSH rule 0 x 747 [65]
+ CRUSH rule 0 x 748 [48]
+ CRUSH rule 0 x 749 [102]
+ CRUSH rule 0 x 750 [50]
+ CRUSH rule 0 x 751 [36]
+ CRUSH rule 0 x 752 [69]
+ CRUSH rule 0 x 753 [116]
+ CRUSH rule 0 x 754 [9]
+ CRUSH rule 0 x 755 [98]
+ CRUSH rule 0 x 756 [113]
+ CRUSH rule 0 x 757 [47]
+ CRUSH rule 0 x 758 [57]
+ CRUSH rule 0 x 759 [74]
+ CRUSH rule 0 x 760 [53]
+ CRUSH rule 0 x 761 [78]
+ CRUSH rule 0 x 762 [87]
+ CRUSH rule 0 x 763 [13]
+ CRUSH rule 0 x 764 [106]
+ CRUSH rule 0 x 765 [109]
+ CRUSH rule 0 x 766 [76]
+ CRUSH rule 0 x 767 [41]
+ CRUSH rule 0 x 768 [13]
+ CRUSH rule 0 x 769 [91]
+ CRUSH rule 0 x 770 [105]
+ CRUSH rule 0 x 771 [10]
+ CRUSH rule 0 x 772 [118]
+ CRUSH rule 0 x 773 [116]
+ CRUSH rule 0 x 774 [100]
+ CRUSH rule 0 x 775 [102]
+ CRUSH rule 0 x 776 [69]
+ CRUSH rule 0 x 777 [76]
+ CRUSH rule 0 x 778 [38]
+ CRUSH rule 0 x 779 [46]
+ CRUSH rule 0 x 780 [63]
+ CRUSH rule 0 x 781 [105]
+ CRUSH rule 0 x 782 [117]
+ CRUSH rule 0 x 783 [60]
+ CRUSH rule 0 x 784 [82]
+ CRUSH rule 0 x 785 [27]
+ CRUSH rule 0 x 786 [41]
+ CRUSH rule 0 x 787 [13]
+ CRUSH rule 0 x 788 [4]
+ CRUSH rule 0 x 789 [50]
+ CRUSH rule 0 x 790 [58]
+ CRUSH rule 0 x 791 [96]
+ CRUSH rule 0 x 792 [80]
+ CRUSH rule 0 x 793 [6]
+ CRUSH rule 0 x 794 [14]
+ CRUSH rule 0 x 795 [51]
+ CRUSH rule 0 x 796 [114]
+ CRUSH rule 0 x 797 [79]
+ CRUSH rule 0 x 798 [42]
+ CRUSH rule 0 x 799 [48]
+ CRUSH rule 0 x 800 [91]
+ CRUSH rule 0 x 801 [2]
+ CRUSH rule 0 x 802 [116]
+ CRUSH rule 0 x 803 [37]
+ CRUSH rule 0 x 804 [33]
+ CRUSH rule 0 x 805 [96]
+ CRUSH rule 0 x 806 [67]
+ CRUSH rule 0 x 807 [47]
+ CRUSH rule 0 x 808 [76]
+ CRUSH rule 0 x 809 [27]
+ CRUSH rule 0 x 810 [119]
+ CRUSH rule 0 x 811 [75]
+ CRUSH rule 0 x 812 [25]
+ CRUSH rule 0 x 813 [64]
+ CRUSH rule 0 x 814 [110]
+ CRUSH rule 0 x 815 [84]
+ CRUSH rule 0 x 816 [25]
+ CRUSH rule 0 x 817 [40]
+ CRUSH rule 0 x 818 [34]
+ CRUSH rule 0 x 819 [88]
+ CRUSH rule 0 x 820 [104]
+ CRUSH rule 0 x 821 [58]
+ CRUSH rule 0 x 822 [29]
+ CRUSH rule 0 x 823 [100]
+ CRUSH rule 0 x 824 [102]
+ CRUSH rule 0 x 825 [47]
+ CRUSH rule 0 x 826 [45]
+ CRUSH rule 0 x 827 [101]
+ CRUSH rule 0 x 828 [60]
+ CRUSH rule 0 x 829 [45]
+ CRUSH rule 0 x 830 [51]
+ CRUSH rule 0 x 831 [6]
+ CRUSH rule 0 x 832 [57]
+ CRUSH rule 0 x 833 [34]
+ CRUSH rule 0 x 834 [90]
+ CRUSH rule 0 x 835 [14]
+ CRUSH rule 0 x 836 [38]
+ CRUSH rule 0 x 837 [51]
+ CRUSH rule 0 x 838 [6]
+ CRUSH rule 0 x 839 [106]
+ CRUSH rule 0 x 840 [33]
+ CRUSH rule 0 x 841 [110]
+ CRUSH rule 0 x 842 [66]
+ CRUSH rule 0 x 843 [11]
+ CRUSH rule 0 x 844 [74]
+ CRUSH rule 0 x 845 [74]
+ CRUSH rule 0 x 846 [98]
+ CRUSH rule 0 x 847 [10]
+ CRUSH rule 0 x 848 [89]
+ CRUSH rule 0 x 849 [42]
+ CRUSH rule 0 x 850 [40]
+ CRUSH rule 0 x 851 [65]
+ CRUSH rule 0 x 852 [31]
+ CRUSH rule 0 x 853 [49]
+ CRUSH rule 0 x 854 [90]
+ CRUSH rule 0 x 855 [2]
+ CRUSH rule 0 x 856 [40]
+ CRUSH rule 0 x 857 [15]
+ CRUSH rule 0 x 858 [10]
+ CRUSH rule 0 x 859 [29]
+ CRUSH rule 0 x 860 [114]
+ CRUSH rule 0 x 861 [22]
+ CRUSH rule 0 x 862 [22]
+ CRUSH rule 0 x 863 [79]
+ CRUSH rule 0 x 864 [68]
+ CRUSH rule 0 x 865 [25]
+ CRUSH rule 0 x 866 [18]
+ CRUSH rule 0 x 867 [3]
+ CRUSH rule 0 x 868 [81]
+ CRUSH rule 0 x 869 [22]
+ CRUSH rule 0 x 870 [73]
+ CRUSH rule 0 x 871 [25]
+ CRUSH rule 0 x 872 [39]
+ CRUSH rule 0 x 873 [92]
+ CRUSH rule 0 x 874 [21]
+ CRUSH rule 0 x 875 [27]
+ CRUSH rule 0 x 876 [98]
+ CRUSH rule 0 x 877 [73]
+ CRUSH rule 0 x 878 [64]
+ CRUSH rule 0 x 879 [29]
+ CRUSH rule 0 x 880 [56]
+ CRUSH rule 0 x 881 [109]
+ CRUSH rule 0 x 882 [60]
+ CRUSH rule 0 x 883 [93]
+ CRUSH rule 0 x 884 [67]
+ CRUSH rule 0 x 885 [31]
+ CRUSH rule 0 x 886 [2]
+ CRUSH rule 0 x 887 [5]
+ CRUSH rule 0 x 888 [16]
+ CRUSH rule 0 x 889 [3]
+ CRUSH rule 0 x 890 [48]
+ CRUSH rule 0 x 891 [86]
+ CRUSH rule 0 x 892 [64]
+ CRUSH rule 0 x 893 [118]
+ CRUSH rule 0 x 894 [16]
+ CRUSH rule 0 x 895 [40]
+ CRUSH rule 0 x 896 [97]
+ CRUSH rule 0 x 897 [60]
+ CRUSH rule 0 x 898 [10]
+ CRUSH rule 0 x 899 [75]
+ CRUSH rule 0 x 900 [102]
+ CRUSH rule 0 x 901 [66]
+ CRUSH rule 0 x 902 [102]
+ CRUSH rule 0 x 903 [5]
+ CRUSH rule 0 x 904 [50]
+ CRUSH rule 0 x 905 [19]
+ CRUSH rule 0 x 906 [75]
+ CRUSH rule 0 x 907 [47]
+ CRUSH rule 0 x 908 [96]
+ CRUSH rule 0 x 909 [94]
+ CRUSH rule 0 x 910 [88]
+ CRUSH rule 0 x 911 [102]
+ CRUSH rule 0 x 912 [91]
+ CRUSH rule 0 x 913 [29]
+ CRUSH rule 0 x 914 [84]
+ CRUSH rule 0 x 915 [70]
+ CRUSH rule 0 x 916 [32]
+ CRUSH rule 0 x 917 [43]
+ CRUSH rule 0 x 918 [91]
+ CRUSH rule 0 x 919 [13]
+ CRUSH rule 0 x 920 [18]
+ CRUSH rule 0 x 921 [104]
+ CRUSH rule 0 x 922 [33]
+ CRUSH rule 0 x 923 [28]
+ CRUSH rule 0 x 924 [69]
+ CRUSH rule 0 x 925 [71]
+ CRUSH rule 0 x 926 [64]
+ CRUSH rule 0 x 927 [99]
+ CRUSH rule 0 x 928 [13]
+ CRUSH rule 0 x 929 [117]
+ CRUSH rule 0 x 930 [31]
+ CRUSH rule 0 x 931 [46]
+ CRUSH rule 0 x 932 [60]
+ CRUSH rule 0 x 933 [88]
+ CRUSH rule 0 x 934 [68]
+ CRUSH rule 0 x 935 [31]
+ CRUSH rule 0 x 936 [104]
+ CRUSH rule 0 x 937 [110]
+ CRUSH rule 0 x 938 [29]
+ CRUSH rule 0 x 939 [77]
+ CRUSH rule 0 x 940 [76]
+ CRUSH rule 0 x 941 [66]
+ CRUSH rule 0 x 942 [83]
+ CRUSH rule 0 x 943 [4]
+ CRUSH rule 0 x 944 [113]
+ CRUSH rule 0 x 945 [17]
+ CRUSH rule 0 x 946 [37]
+ CRUSH rule 0 x 947 [107]
+ CRUSH rule 0 x 948 [55]
+ CRUSH rule 0 x 949 [45]
+ CRUSH rule 0 x 950 [96]
+ CRUSH rule 0 x 951 [40]
+ CRUSH rule 0 x 952 [93]
+ CRUSH rule 0 x 953 [55]
+ CRUSH rule 0 x 954 [84]
+ CRUSH rule 0 x 955 [31]
+ CRUSH rule 0 x 956 [72]
+ CRUSH rule 0 x 957 [3]
+ CRUSH rule 0 x 958 [8]
+ CRUSH rule 0 x 959 [42]
+ CRUSH rule 0 x 960 [113]
+ CRUSH rule 0 x 961 [116]
+ CRUSH rule 0 x 962 [13]
+ CRUSH rule 0 x 963 [0]
+ CRUSH rule 0 x 964 [59]
+ CRUSH rule 0 x 965 [47]
+ CRUSH rule 0 x 966 [88]
+ CRUSH rule 0 x 967 [71]
+ CRUSH rule 0 x 968 [73]
+ CRUSH rule 0 x 969 [53]
+ CRUSH rule 0 x 970 [3]
+ CRUSH rule 0 x 971 [87]
+ CRUSH rule 0 x 972 [3]
+ CRUSH rule 0 x 973 [113]
+ CRUSH rule 0 x 974 [114]
+ CRUSH rule 0 x 975 [40]
+ CRUSH rule 0 x 976 [81]
+ CRUSH rule 0 x 977 [95]
+ CRUSH rule 0 x 978 [35]
+ CRUSH rule 0 x 979 [98]
+ CRUSH rule 0 x 980 [52]
+ CRUSH rule 0 x 981 [89]
+ CRUSH rule 0 x 982 [1]
+ CRUSH rule 0 x 983 [34]
+ CRUSH rule 0 x 984 [78]
+ CRUSH rule 0 x 985 [99]
+ CRUSH rule 0 x 986 [4]
+ CRUSH rule 0 x 987 [78]
+ CRUSH rule 0 x 988 [79]
+ CRUSH rule 0 x 989 [87]
+ CRUSH rule 0 x 990 [47]
+ CRUSH rule 0 x 991 [61]
+ CRUSH rule 0 x 992 [83]
+ CRUSH rule 0 x 993 [75]
+ CRUSH rule 0 x 994 [74]
+ CRUSH rule 0 x 995 [100]
+ CRUSH rule 0 x 996 [41]
+ CRUSH rule 0 x 997 [89]
+ CRUSH rule 0 x 998 [92]
+ CRUSH rule 0 x 999 [101]
+ CRUSH rule 0 x 1000 [9]
+ CRUSH rule 0 x 1001 [49]
+ CRUSH rule 0 x 1002 [99]
+ CRUSH rule 0 x 1003 [43]
+ CRUSH rule 0 x 1004 [89]
+ CRUSH rule 0 x 1005 [105]
+ CRUSH rule 0 x 1006 [45]
+ CRUSH rule 0 x 1007 [19]
+ CRUSH rule 0 x 1008 [31]
+ CRUSH rule 0 x 1009 [19]
+ CRUSH rule 0 x 1010 [42]
+ CRUSH rule 0 x 1011 [25]
+ CRUSH rule 0 x 1012 [68]
+ CRUSH rule 0 x 1013 [5]
+ CRUSH rule 0 x 1014 [33]
+ CRUSH rule 0 x 1015 [106]
+ CRUSH rule 0 x 1016 [88]
+ CRUSH rule 0 x 1017 [0]
+ CRUSH rule 0 x 1018 [63]
+ CRUSH rule 0 x 1019 [104]
+ CRUSH rule 0 x 1020 [96]
+ CRUSH rule 0 x 1021 [117]
+ CRUSH rule 0 x 1022 [73]
+ CRUSH rule 0 x 1023 [0]
+ rule 0 (data) num_rep 1 result size == 1:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114]
+ CRUSH rule 0 x 1 [80,79]
+ CRUSH rule 0 x 2 [91,96]
+ CRUSH rule 0 x 3 [51,4]
+ CRUSH rule 0 x 4 [50,89]
+ CRUSH rule 0 x 5 [89,94]
+ CRUSH rule 0 x 6 [91,76]
+ CRUSH rule 0 x 7 [104,25]
+ CRUSH rule 0 x 8 [78,57]
+ CRUSH rule 0 x 9 [101,102]
+ CRUSH rule 0 x 10 [61,58]
+ CRUSH rule 0 x 11 [13,31]
+ CRUSH rule 0 x 12 [83,46]
+ CRUSH rule 0 x 13 [108,85]
+ CRUSH rule 0 x 14 [105,72]
+ CRUSH rule 0 x 15 [18,7]
+ CRUSH rule 0 x 16 [103,3]
+ CRUSH rule 0 x 17 [85,110]
+ CRUSH rule 0 x 18 [11,65]
+ CRUSH rule 0 x 19 [75,50]
+ CRUSH rule 0 x 20 [79,70]
+ CRUSH rule 0 x 21 [84,49]
+ CRUSH rule 0 x 22 [23,104]
+ CRUSH rule 0 x 23 [118,63]
+ CRUSH rule 0 x 24 [83,38]
+ CRUSH rule 0 x 25 [81,64]
+ CRUSH rule 0 x 26 [38,99]
+ CRUSH rule 0 x 27 [76,107]
+ CRUSH rule 0 x 28 [76,71]
+ CRUSH rule 0 x 29 [24,71]
+ CRUSH rule 0 x 30 [94,87]
+ CRUSH rule 0 x 31 [76,95]
+ CRUSH rule 0 x 32 [72,95]
+ CRUSH rule 0 x 33 [77,86]
+ CRUSH rule 0 x 34 [7,108]
+ CRUSH rule 0 x 35 [22,88]
+ CRUSH rule 0 x 36 [104,65]
+ CRUSH rule 0 x 37 [61,109]
+ CRUSH rule 0 x 38 [72,85]
+ CRUSH rule 0 x 39 [68,103]
+ CRUSH rule 0 x 40 [103,78]
+ CRUSH rule 0 x 41 [85,11]
+ CRUSH rule 0 x 42 [106,33]
+ CRUSH rule 0 x 43 [10,68]
+ CRUSH rule 0 x 44 [101,4]
+ CRUSH rule 0 x 45 [83,15]
+ CRUSH rule 0 x 46 [65,1]
+ CRUSH rule 0 x 47 [106,53]
+ CRUSH rule 0 x 48 [34,33]
+ CRUSH rule 0 x 49 [0,81]
+ CRUSH rule 0 x 50 [42,6]
+ CRUSH rule 0 x 51 [104,75]
+ CRUSH rule 0 x 52 [83,19]
+ CRUSH rule 0 x 53 [32,69]
+ CRUSH rule 0 x 54 [9,79]
+ CRUSH rule 0 x 55 [14,5]
+ CRUSH rule 0 x 56 [21,72]
+ CRUSH rule 0 x 57 [93,84]
+ CRUSH rule 0 x 58 [45,106]
+ CRUSH rule 0 x 59 [80,41]
+ CRUSH rule 0 x 60 [90,57]
+ CRUSH rule 0 x 61 [88,37]
+ CRUSH rule 0 x 62 [81,1]
+ CRUSH rule 0 x 63 [79,113]
+ CRUSH rule 0 x 64 [1,35]
+ CRUSH rule 0 x 65 [32,103]
+ CRUSH rule 0 x 66 [48,99]
+ CRUSH rule 0 x 67 [94,103]
+ CRUSH rule 0 x 68 [102,91]
+ CRUSH rule 0 x 69 [62,77]
+ CRUSH rule 0 x 70 [84,105]
+ CRUSH rule 0 x 71 [9,33]
+ CRUSH rule 0 x 72 [97,42]
+ CRUSH rule 0 x 73 [64,83]
+ CRUSH rule 0 x 74 [29,50]
+ CRUSH rule 0 x 75 [29,28]
+ CRUSH rule 0 x 76 [55,0]
+ CRUSH rule 0 x 77 [107,21]
+ CRUSH rule 0 x 78 [11,89]
+ CRUSH rule 0 x 79 [64,51]
+ CRUSH rule 0 x 80 [0,31]
+ CRUSH rule 0 x 81 [71,109]
+ CRUSH rule 0 x 82 [37,21]
+ CRUSH rule 0 x 83 [92,103]
+ CRUSH rule 0 x 84 [49,115]
+ CRUSH rule 0 x 85 [54,101]
+ CRUSH rule 0 x 86 [37,7]
+ CRUSH rule 0 x 87 [116,4]
+ CRUSH rule 0 x 88 [38,27]
+ CRUSH rule 0 x 89 [76,77]
+ CRUSH rule 0 x 90 [14,50]
+ CRUSH rule 0 x 91 [68,19]
+ CRUSH rule 0 x 92 [86,9]
+ CRUSH rule 0 x 93 [44,65]
+ CRUSH rule 0 x 94 [61,102]
+ CRUSH rule 0 x 95 [93,86]
+ CRUSH rule 0 x 96 [66,87]
+ CRUSH rule 0 x 97 [111,9]
+ CRUSH rule 0 x 98 [93,102]
+ CRUSH rule 0 x 99 [78,3]
+ CRUSH rule 0 x 100 [6,63]
+ CRUSH rule 0 x 101 [84,16]
+ CRUSH rule 0 x 102 [82,105]
+ CRUSH rule 0 x 103 [66,6]
+ CRUSH rule 0 x 104 [14,95]
+ CRUSH rule 0 x 105 [87,1]
+ CRUSH rule 0 x 106 [69,116]
+ CRUSH rule 0 x 107 [1,55]
+ CRUSH rule 0 x 108 [94,53]
+ CRUSH rule 0 x 109 [112,13]
+ CRUSH rule 0 x 110 [54,61]
+ CRUSH rule 0 x 111 [10,78]
+ CRUSH rule 0 x 112 [89,9]
+ CRUSH rule 0 x 113 [69,2]
+ CRUSH rule 0 x 114 [79,110]
+ CRUSH rule 0 x 115 [50,85]
+ CRUSH rule 0 x 116 [96,16]
+ CRUSH rule 0 x 117 [87,42]
+ CRUSH rule 0 x 118 [23,56]
+ CRUSH rule 0 x 119 [104,11]
+ CRUSH rule 0 x 120 [57,5]
+ CRUSH rule 0 x 121 [105,9]
+ CRUSH rule 0 x 122 [45,110]
+ CRUSH rule 0 x 123 [112,35]
+ CRUSH rule 0 x 124 [110,49]
+ CRUSH rule 0 x 125 [66,105]
+ CRUSH rule 0 x 126 [51,28]
+ CRUSH rule 0 x 127 [70,6]
+ CRUSH rule 0 x 128 [90,16]
+ CRUSH rule 0 x 129 [103,110]
+ CRUSH rule 0 x 130 [50,11]
+ CRUSH rule 0 x 131 [23,60]
+ CRUSH rule 0 x 132 [69,70]
+ CRUSH rule 0 x 133 [52,25]
+ CRUSH rule 0 x 134 [78,29]
+ CRUSH rule 0 x 135 [78,3]
+ CRUSH rule 0 x 136 [32,29]
+ CRUSH rule 0 x 137 [11,78]
+ CRUSH rule 0 x 138 [17,94]
+ CRUSH rule 0 x 139 [89,60]
+ CRUSH rule 0 x 140 [39,62]
+ CRUSH rule 0 x 141 [89,98]
+ CRUSH rule 0 x 142 [70,61]
+ CRUSH rule 0 x 143 [51,28]
+ CRUSH rule 0 x 144 [13,81]
+ CRUSH rule 0 x 145 [77,119]
+ CRUSH rule 0 x 146 [8,64]
+ CRUSH rule 0 x 147 [22,37]
+ CRUSH rule 0 x 148 [74,69]
+ CRUSH rule 0 x 149 [76,13]
+ CRUSH rule 0 x 150 [14,47]
+ CRUSH rule 0 x 151 [90,4]
+ CRUSH rule 0 x 152 [49,18]
+ CRUSH rule 0 x 153 [71,44]
+ CRUSH rule 0 x 154 [94,81]
+ CRUSH rule 0 x 155 [75,6]
+ CRUSH rule 0 x 156 [94,85]
+ CRUSH rule 0 x 157 [112,43]
+ CRUSH rule 0 x 158 [26,17]
+ CRUSH rule 0 x 159 [52,29]
+ CRUSH rule 0 x 160 [41,0]
+ CRUSH rule 0 x 161 [19,78]
+ CRUSH rule 0 x 162 [55,2]
+ CRUSH rule 0 x 163 [54,31]
+ CRUSH rule 0 x 164 [45,5]
+ CRUSH rule 0 x 165 [25,72]
+ CRUSH rule 0 x 166 [73,36]
+ CRUSH rule 0 x 167 [89,58]
+ CRUSH rule 0 x 168 [47,40]
+ CRUSH rule 0 x 169 [51,21]
+ CRUSH rule 0 x 170 [68,91]
+ CRUSH rule 0 x 171 [73,90]
+ CRUSH rule 0 x 172 [33,15]
+ CRUSH rule 0 x 173 [102,59]
+ CRUSH rule 0 x 174 [116,25]
+ CRUSH rule 0 x 175 [3,41]
+ CRUSH rule 0 x 176 [94,91]
+ CRUSH rule 0 x 177 [52,85]
+ CRUSH rule 0 x 178 [39,2]
+ CRUSH rule 0 x 179 [72,97]
+ CRUSH rule 0 x 180 [60,7]
+ CRUSH rule 0 x 181 [18,59]
+ CRUSH rule 0 x 182 [22,90]
+ CRUSH rule 0 x 183 [11,74]
+ CRUSH rule 0 x 184 [92,101]
+ CRUSH rule 0 x 185 [97,8]
+ CRUSH rule 0 x 186 [67,116]
+ CRUSH rule 0 x 187 [116,11]
+ CRUSH rule 0 x 188 [69,92]
+ CRUSH rule 0 x 189 [47,84]
+ CRUSH rule 0 x 190 [90,13]
+ CRUSH rule 0 x 191 [49,17]
+ CRUSH rule 0 x 192 [68,93]
+ CRUSH rule 0 x 193 [0,33]
+ CRUSH rule 0 x 194 [17,58]
+ CRUSH rule 0 x 195 [119,41]
+ CRUSH rule 0 x 196 [72,27]
+ CRUSH rule 0 x 197 [106,83]
+ CRUSH rule 0 x 198 [114,95]
+ CRUSH rule 0 x 199 [0,83]
+ CRUSH rule 0 x 200 [35,86]
+ CRUSH rule 0 x 201 [14,29]
+ CRUSH rule 0 x 202 [98,33]
+ CRUSH rule 0 x 203 [36,22]
+ CRUSH rule 0 x 204 [10,98]
+ CRUSH rule 0 x 205 [22,61]
+ CRUSH rule 0 x 206 [49,112]
+ CRUSH rule 0 x 207 [80,39]
+ CRUSH rule 0 x 208 [63,26]
+ CRUSH rule 0 x 209 [85,111]
+ CRUSH rule 0 x 210 [79,18]
+ CRUSH rule 0 x 211 [26,10]
+ CRUSH rule 0 x 212 [28,103]
+ CRUSH rule 0 x 213 [91,0]
+ CRUSH rule 0 x 214 [78,47]
+ CRUSH rule 0 x 215 [61,22]
+ CRUSH rule 0 x 216 [99,3]
+ CRUSH rule 0 x 217 [86,89]
+ CRUSH rule 0 x 218 [93,96]
+ CRUSH rule 0 x 219 [28,59]
+ CRUSH rule 0 x 220 [56,8]
+ CRUSH rule 0 x 221 [0,9]
+ CRUSH rule 0 x 222 [50,63]
+ CRUSH rule 0 x 223 [29,1]
+ CRUSH rule 0 x 224 [52,10]
+ CRUSH rule 0 x 225 [61,11]
+ CRUSH rule 0 x 226 [44,22]
+ CRUSH rule 0 x 227 [42,3]
+ CRUSH rule 0 x 228 [117,49]
+ CRUSH rule 0 x 229 [100,79]
+ CRUSH rule 0 x 230 [41,114]
+ CRUSH rule 0 x 231 [56,95]
+ CRUSH rule 0 x 232 [23,44]
+ CRUSH rule 0 x 233 [88,103]
+ CRUSH rule 0 x 234 [4,101]
+ CRUSH rule 0 x 235 [26,10]
+ CRUSH rule 0 x 236 [32,37]
+ CRUSH rule 0 x 237 [92,3]
+ CRUSH rule 0 x 238 [10,26]
+ CRUSH rule 0 x 239 [15,105]
+ CRUSH rule 0 x 240 [109,85]
+ CRUSH rule 0 x 241 [47,108]
+ CRUSH rule 0 x 242 [24,99]
+ CRUSH rule 0 x 243 [76,8]
+ CRUSH rule 0 x 244 [96,19]
+ CRUSH rule 0 x 245 [27,28]
+ CRUSH rule 0 x 246 [35,82]
+ CRUSH rule 0 x 247 [99,102]
+ CRUSH rule 0 x 248 [8,29]
+ CRUSH rule 0 x 249 [85,1]
+ CRUSH rule 0 x 250 [79,102]
+ CRUSH rule 0 x 251 [28,103]
+ CRUSH rule 0 x 252 [95,22]
+ CRUSH rule 0 x 253 [109,27]
+ CRUSH rule 0 x 254 [80,103]
+ CRUSH rule 0 x 255 [112,22]
+ CRUSH rule 0 x 256 [37,38]
+ CRUSH rule 0 x 257 [69,117]
+ CRUSH rule 0 x 258 [34,55]
+ CRUSH rule 0 x 259 [70,17]
+ CRUSH rule 0 x 260 [98,29]
+ CRUSH rule 0 x 261 [94,83]
+ CRUSH rule 0 x 262 [42,49]
+ CRUSH rule 0 x 263 [65,42]
+ CRUSH rule 0 x 264 [36,17]
+ CRUSH rule 0 x 265 [66,63]
+ CRUSH rule 0 x 266 [75,92]
+ CRUSH rule 0 x 267 [58,35]
+ CRUSH rule 0 x 268 [38,9]
+ CRUSH rule 0 x 269 [43,104]
+ CRUSH rule 0 x 270 [58,37]
+ CRUSH rule 0 x 271 [19,33]
+ CRUSH rule 0 x 272 [73,9]
+ CRUSH rule 0 x 273 [108,29]
+ CRUSH rule 0 x 274 [47,64]
+ CRUSH rule 0 x 275 [92,19]
+ CRUSH rule 0 x 276 [7,79]
+ CRUSH rule 0 x 277 [19,68]
+ CRUSH rule 0 x 278 [116,95]
+ CRUSH rule 0 x 279 [101,3]
+ CRUSH rule 0 x 280 [113,69]
+ CRUSH rule 0 x 281 [14,93]
+ CRUSH rule 0 x 282 [106,7]
+ CRUSH rule 0 x 283 [8,118]
+ CRUSH rule 0 x 284 [10,110]
+ CRUSH rule 0 x 285 [88,63]
+ CRUSH rule 0 x 286 [27,4]
+ CRUSH rule 0 x 287 [84,65]
+ CRUSH rule 0 x 288 [103,8]
+ CRUSH rule 0 x 289 [9,104]
+ CRUSH rule 0 x 290 [115,7]
+ CRUSH rule 0 x 291 [48,45]
+ CRUSH rule 0 x 292 [52,16]
+ CRUSH rule 0 x 293 [27,24]
+ CRUSH rule 0 x 294 [79,36]
+ CRUSH rule 0 x 295 [37,116]
+ CRUSH rule 0 x 296 [56,61]
+ CRUSH rule 0 x 297 [35,40]
+ CRUSH rule 0 x 298 [71,118]
+ CRUSH rule 0 x 299 [79,1]
+ CRUSH rule 0 x 300 [67,5]
+ CRUSH rule 0 x 301 [51,110]
+ CRUSH rule 0 x 302 [78,67]
+ CRUSH rule 0 x 303 [19,94]
+ CRUSH rule 0 x 304 [101,66]
+ CRUSH rule 0 x 305 [81,62]
+ CRUSH rule 0 x 306 [0,23]
+ CRUSH rule 0 x 307 [44,15]
+ CRUSH rule 0 x 308 [91,98]
+ CRUSH rule 0 x 309 [15,18]
+ CRUSH rule 0 x 310 [26,89]
+ CRUSH rule 0 x 311 [36,41]
+ CRUSH rule 0 x 312 [33,22]
+ CRUSH rule 0 x 313 [104,16]
+ CRUSH rule 0 x 314 [28,4]
+ CRUSH rule 0 x 315 [16,8]
+ CRUSH rule 0 x 316 [4,1]
+ CRUSH rule 0 x 317 [118,8]
+ CRUSH rule 0 x 318 [32,47]
+ CRUSH rule 0 x 319 [24,83]
+ CRUSH rule 0 x 320 [36,97]
+ CRUSH rule 0 x 321 [26,85]
+ CRUSH rule 0 x 322 [87,42]
+ CRUSH rule 0 x 323 [73,0]
+ CRUSH rule 0 x 324 [64,37]
+ CRUSH rule 0 x 325 [52,16]
+ CRUSH rule 0 x 326 [111,93]
+ CRUSH rule 0 x 327 [62,16]
+ CRUSH rule 0 x 328 [7,42]
+ CRUSH rule 0 x 329 [93,34]
+ CRUSH rule 0 x 330 [24,4]
+ CRUSH rule 0 x 331 [41,21]
+ CRUSH rule 0 x 332 [61,110]
+ CRUSH rule 0 x 333 [16,8]
+ CRUSH rule 0 x 334 [94,35]
+ CRUSH rule 0 x 335 [71,74]
+ CRUSH rule 0 x 336 [16,19]
+ CRUSH rule 0 x 337 [37,11]
+ CRUSH rule 0 x 338 [109,69]
+ CRUSH rule 0 x 339 [13,64]
+ CRUSH rule 0 x 340 [119,15]
+ CRUSH rule 0 x 341 [63,114]
+ CRUSH rule 0 x 342 [92,25]
+ CRUSH rule 0 x 343 [49,26]
+ CRUSH rule 0 x 344 [103,26]
+ CRUSH rule 0 x 345 [56,25]
+ CRUSH rule 0 x 346 [3,79]
+ CRUSH rule 0 x 347 [106,27]
+ CRUSH rule 0 x 348 [10,117]
+ CRUSH rule 0 x 349 [96,37]
+ CRUSH rule 0 x 350 [63,32]
+ CRUSH rule 0 x 351 [60,85]
+ CRUSH rule 0 x 352 [103,84]
+ CRUSH rule 0 x 353 [10,113]
+ CRUSH rule 0 x 354 [55,52]
+ CRUSH rule 0 x 355 [73,68]
+ CRUSH rule 0 x 356 [114,41]
+ CRUSH rule 0 x 357 [70,13]
+ CRUSH rule 0 x 358 [97,13]
+ CRUSH rule 0 x 359 [4,117]
+ CRUSH rule 0 x 360 [106,69]
+ CRUSH rule 0 x 361 [27,46]
+ CRUSH rule 0 x 362 [28,33]
+ CRUSH rule 0 x 363 [45,26]
+ CRUSH rule 0 x 364 [23,50]
+ CRUSH rule 0 x 365 [57,114]
+ CRUSH rule 0 x 366 [14,58]
+ CRUSH rule 0 x 367 [108,65]
+ CRUSH rule 0 x 368 [103,32]
+ CRUSH rule 0 x 369 [11,57]
+ CRUSH rule 0 x 370 [11,89]
+ CRUSH rule 0 x 371 [34,55]
+ CRUSH rule 0 x 372 [58,10]
+ CRUSH rule 0 x 373 [6,42]
+ CRUSH rule 0 x 374 [110,95]
+ CRUSH rule 0 x 375 [19,92]
+ CRUSH rule 0 x 376 [22,86]
+ CRUSH rule 0 x 377 [93,113]
+ CRUSH rule 0 x 378 [67,36]
+ CRUSH rule 0 x 379 [77,115]
+ CRUSH rule 0 x 380 [3,108]
+ CRUSH rule 0 x 381 [55,1]
+ CRUSH rule 0 x 382 [26,51]
+ CRUSH rule 0 x 383 [48,25]
+ CRUSH rule 0 x 384 [15,100]
+ CRUSH rule 0 x 385 [82,4]
+ CRUSH rule 0 x 386 [108,63]
+ CRUSH rule 0 x 387 [70,41]
+ CRUSH rule 0 x 388 [5,67]
+ CRUSH rule 0 x 389 [14,1]
+ CRUSH rule 0 x 390 [68,10]
+ CRUSH rule 0 x 391 [113,14]
+ CRUSH rule 0 x 392 [72,14]
+ CRUSH rule 0 x 393 [115,6]
+ CRUSH rule 0 x 394 [38,21]
+ CRUSH rule 0 x 395 [0,27]
+ CRUSH rule 0 x 396 [59,92]
+ CRUSH rule 0 x 397 [87,1]
+ CRUSH rule 0 x 398 [44,75]
+ CRUSH rule 0 x 399 [9,2]
+ CRUSH rule 0 x 400 [19,63]
+ CRUSH rule 0 x 401 [79,34]
+ CRUSH rule 0 x 402 [107,98]
+ CRUSH rule 0 x 403 [23,82]
+ CRUSH rule 0 x 404 [76,75]
+ CRUSH rule 0 x 405 [10,32]
+ CRUSH rule 0 x 406 [38,16]
+ CRUSH rule 0 x 407 [70,85]
+ CRUSH rule 0 x 408 [55,72]
+ CRUSH rule 0 x 409 [102,15]
+ CRUSH rule 0 x 410 [59,13]
+ CRUSH rule 0 x 411 [34,29]
+ CRUSH rule 0 x 412 [108,99]
+ CRUSH rule 0 x 413 [54,107]
+ CRUSH rule 0 x 414 [70,4]
+ CRUSH rule 0 x 415 [107,36]
+ CRUSH rule 0 x 416 [21,68]
+ CRUSH rule 0 x 417 [8,70]
+ CRUSH rule 0 x 418 [51,46]
+ CRUSH rule 0 x 419 [8,66]
+ CRUSH rule 0 x 420 [109,105]
+ CRUSH rule 0 x 421 [114,17]
+ CRUSH rule 0 x 422 [109,87]
+ CRUSH rule 0 x 423 [59,98]
+ CRUSH rule 0 x 424 [71,5]
+ CRUSH rule 0 x 425 [101,111]
+ CRUSH rule 0 x 426 [47,46]
+ CRUSH rule 0 x 427 [8,115]
+ CRUSH rule 0 x 428 [68,103]
+ CRUSH rule 0 x 429 [76,6]
+ CRUSH rule 0 x 430 [69,86]
+ CRUSH rule 0 x 431 [70,83]
+ CRUSH rule 0 x 432 [46,37]
+ CRUSH rule 0 x 433 [6,101]
+ CRUSH rule 0 x 434 [64,69]
+ CRUSH rule 0 x 435 [16,50]
+ CRUSH rule 0 x 436 [89,102]
+ CRUSH rule 0 x 437 [29,114]
+ CRUSH rule 0 x 438 [105,98]
+ CRUSH rule 0 x 439 [29,119]
+ CRUSH rule 0 x 440 [38,7]
+ CRUSH rule 0 x 441 [112,105]
+ CRUSH rule 0 x 442 [55,108]
+ CRUSH rule 0 x 443 [44,57]
+ CRUSH rule 0 x 444 [72,27]
+ CRUSH rule 0 x 445 [19,5]
+ CRUSH rule 0 x 446 [40,47]
+ CRUSH rule 0 x 447 [13,61]
+ CRUSH rule 0 x 448 [7,68]
+ CRUSH rule 0 x 449 [67,19]
+ CRUSH rule 0 x 450 [117,79]
+ CRUSH rule 0 x 451 [93,108]
+ CRUSH rule 0 x 452 [70,49]
+ CRUSH rule 0 x 453 [82,22]
+ CRUSH rule 0 x 454 [53,18]
+ CRUSH rule 0 x 455 [91,92]
+ CRUSH rule 0 x 456 [101,104]
+ CRUSH rule 0 x 457 [113,51]
+ CRUSH rule 0 x 458 [53,34]
+ CRUSH rule 0 x 459 [25,115]
+ CRUSH rule 0 x 460 [105,9]
+ CRUSH rule 0 x 461 [102,35]
+ CRUSH rule 0 x 462 [98,107]
+ CRUSH rule 0 x 463 [108,105]
+ CRUSH rule 0 x 464 [19,109]
+ CRUSH rule 0 x 465 [29,86]
+ CRUSH rule 0 x 466 [66,7]
+ CRUSH rule 0 x 467 [6,57]
+ CRUSH rule 0 x 468 [97,26]
+ CRUSH rule 0 x 469 [98,75]
+ CRUSH rule 0 x 470 [50,3]
+ CRUSH rule 0 x 471 [40,79]
+ CRUSH rule 0 x 472 [74,79]
+ CRUSH rule 0 x 473 [95,21]
+ CRUSH rule 0 x 474 [51,32]
+ CRUSH rule 0 x 475 [49,110]
+ CRUSH rule 0 x 476 [110,31]
+ CRUSH rule 0 x 477 [25,106]
+ CRUSH rule 0 x 478 [47,46]
+ CRUSH rule 0 x 479 [70,37]
+ CRUSH rule 0 x 480 [62,57]
+ CRUSH rule 0 x 481 [26,19]
+ CRUSH rule 0 x 482 [84,85]
+ CRUSH rule 0 x 483 [15,116]
+ CRUSH rule 0 x 484 [37,36]
+ CRUSH rule 0 x 485 [47,117]
+ CRUSH rule 0 x 486 [92,10]
+ CRUSH rule 0 x 487 [106,51]
+ CRUSH rule 0 x 488 [42,9]
+ CRUSH rule 0 x 489 [76,16]
+ CRUSH rule 0 x 490 [68,17]
+ CRUSH rule 0 x 491 [80,71]
+ CRUSH rule 0 x 492 [21,57]
+ CRUSH rule 0 x 493 [99,78]
+ CRUSH rule 0 x 494 [4,87]
+ CRUSH rule 0 x 495 [40,43]
+ CRUSH rule 0 x 496 [93,38]
+ CRUSH rule 0 x 497 [102,71]
+ CRUSH rule 0 x 498 [68,83]
+ CRUSH rule 0 x 499 [10,26]
+ CRUSH rule 0 x 500 [50,6]
+ CRUSH rule 0 x 501 [60,9]
+ CRUSH rule 0 x 502 [11,64]
+ CRUSH rule 0 x 503 [117,25]
+ CRUSH rule 0 x 504 [90,41]
+ CRUSH rule 0 x 505 [91,100]
+ CRUSH rule 0 x 506 [82,103]
+ CRUSH rule 0 x 507 [81,54]
+ CRUSH rule 0 x 508 [34,87]
+ CRUSH rule 0 x 509 [88,63]
+ CRUSH rule 0 x 510 [11,73]
+ CRUSH rule 0 x 511 [72,27]
+ CRUSH rule 0 x 512 [118,73]
+ CRUSH rule 0 x 513 [22,76]
+ CRUSH rule 0 x 514 [82,11]
+ CRUSH rule 0 x 515 [27,0]
+ CRUSH rule 0 x 516 [66,13]
+ CRUSH rule 0 x 517 [83,60]
+ CRUSH rule 0 x 518 [18,3]
+ CRUSH rule 0 x 519 [67,119]
+ CRUSH rule 0 x 520 [15,88]
+ CRUSH rule 0 x 521 [63,113]
+ CRUSH rule 0 x 522 [56,73]
+ CRUSH rule 0 x 523 [36,35]
+ CRUSH rule 0 x 524 [33,38]
+ CRUSH rule 0 x 525 [3,119]
+ CRUSH rule 0 x 526 [83,50]
+ CRUSH rule 0 x 527 [37,0]
+ CRUSH rule 0 x 528 [108,87]
+ CRUSH rule 0 x 529 [107,60]
+ CRUSH rule 0 x 530 [49,3]
+ CRUSH rule 0 x 531 [27,104]
+ CRUSH rule 0 x 532 [68,14]
+ CRUSH rule 0 x 533 [5,85]
+ CRUSH rule 0 x 534 [97,24]
+ CRUSH rule 0 x 535 [8,75]
+ CRUSH rule 0 x 536 [3,37]
+ CRUSH rule 0 x 537 [116,7]
+ CRUSH rule 0 x 538 [85,56]
+ CRUSH rule 0 x 539 [10,9]
+ CRUSH rule 0 x 540 [100,101]
+ CRUSH rule 0 x 541 [111,77]
+ CRUSH rule 0 x 542 [50,27]
+ CRUSH rule 0 x 543 [45,21]
+ CRUSH rule 0 x 544 [106,65]
+ CRUSH rule 0 x 545 [43,114]
+ CRUSH rule 0 x 546 [108,79]
+ CRUSH rule 0 x 547 [67,50]
+ CRUSH rule 0 x 548 [58,61]
+ CRUSH rule 0 x 549 [60,22]
+ CRUSH rule 0 x 550 [47,68]
+ CRUSH rule 0 x 551 [14,88]
+ CRUSH rule 0 x 552 [70,65]
+ CRUSH rule 0 x 553 [96,105]
+ CRUSH rule 0 x 554 [61,94]
+ CRUSH rule 0 x 555 [76,37]
+ CRUSH rule 0 x 556 [106,89]
+ CRUSH rule 0 x 557 [39,113]
+ CRUSH rule 0 x 558 [70,79]
+ CRUSH rule 0 x 559 [106,69]
+ CRUSH rule 0 x 560 [94,97]
+ CRUSH rule 0 x 561 [27,76]
+ CRUSH rule 0 x 562 [97,62]
+ CRUSH rule 0 x 563 [64,103]
+ CRUSH rule 0 x 564 [96,41]
+ CRUSH rule 0 x 565 [66,71]
+ CRUSH rule 0 x 566 [27,38]
+ CRUSH rule 0 x 567 [88,8]
+ CRUSH rule 0 x 568 [106,17]
+ CRUSH rule 0 x 569 [102,63]
+ CRUSH rule 0 x 570 [98,27]
+ CRUSH rule 0 x 571 [95,98]
+ CRUSH rule 0 x 572 [62,83]
+ CRUSH rule 0 x 573 [51,118]
+ CRUSH rule 0 x 574 [89,78]
+ CRUSH rule 0 x 575 [87,19]
+ CRUSH rule 0 x 576 [112,73]
+ CRUSH rule 0 x 577 [8,84]
+ CRUSH rule 0 x 578 [64,99]
+ CRUSH rule 0 x 579 [78,77]
+ CRUSH rule 0 x 580 [68,95]
+ CRUSH rule 0 x 581 [55,52]
+ CRUSH rule 0 x 582 [15,113]
+ CRUSH rule 0 x 583 [74,105]
+ CRUSH rule 0 x 584 [22,92]
+ CRUSH rule 0 x 585 [35,1]
+ CRUSH rule 0 x 586 [33,1]
+ CRUSH rule 0 x 587 [106,99]
+ CRUSH rule 0 x 588 [0,83]
+ CRUSH rule 0 x 589 [7,95]
+ CRUSH rule 0 x 590 [40,69]
+ CRUSH rule 0 x 591 [42,23]
+ CRUSH rule 0 x 592 [45,22]
+ CRUSH rule 0 x 593 [89,14]
+ CRUSH rule 0 x 594 [27,76]
+ CRUSH rule 0 x 595 [7,10]
+ CRUSH rule 0 x 596 [82,59]
+ CRUSH rule 0 x 597 [72,83]
+ CRUSH rule 0 x 598 [34,19]
+ CRUSH rule 0 x 599 [119,61]
+ CRUSH rule 0 x 600 [24,27]
+ CRUSH rule 0 x 601 [104,15]
+ CRUSH rule 0 x 602 [48,45]
+ CRUSH rule 0 x 603 [24,13]
+ CRUSH rule 0 x 604 [89,0]
+ CRUSH rule 0 x 605 [104,87]
+ CRUSH rule 0 x 606 [49,34]
+ CRUSH rule 0 x 607 [95,40]
+ CRUSH rule 0 x 608 [112,91]
+ CRUSH rule 0 x 609 [61,66]
+ CRUSH rule 0 x 610 [106,16]
+ CRUSH rule 0 x 611 [66,87]
+ CRUSH rule 0 x 612 [103,8]
+ CRUSH rule 0 x 613 [13,91]
+ CRUSH rule 0 x 614 [81,88]
+ CRUSH rule 0 x 615 [61,19]
+ CRUSH rule 0 x 616 [41,15]
+ CRUSH rule 0 x 617 [111,69]
+ CRUSH rule 0 x 618 [26,99]
+ CRUSH rule 0 x 619 [92,27]
+ CRUSH rule 0 x 620 [108,103]
+ CRUSH rule 0 x 621 [106,99]
+ CRUSH rule 0 x 622 [67,48]
+ CRUSH rule 0 x 623 [94,61]
+ CRUSH rule 0 x 624 [115,59]
+ CRUSH rule 0 x 625 [111,27]
+ CRUSH rule 0 x 626 [3,55]
+ CRUSH rule 0 x 627 [19,29]
+ CRUSH rule 0 x 628 [65,88]
+ CRUSH rule 0 x 629 [6,46]
+ CRUSH rule 0 x 630 [22,72]
+ CRUSH rule 0 x 631 [35,22]
+ CRUSH rule 0 x 632 [81,0]
+ CRUSH rule 0 x 633 [65,68]
+ CRUSH rule 0 x 634 [87,50]
+ CRUSH rule 0 x 635 [40,73]
+ CRUSH rule 0 x 636 [23,70]
+ CRUSH rule 0 x 637 [102,45]
+ CRUSH rule 0 x 638 [43,114]
+ CRUSH rule 0 x 639 [31,78]
+ CRUSH rule 0 x 640 [113,73]
+ CRUSH rule 0 x 641 [45,96]
+ CRUSH rule 0 x 642 [47,66]
+ CRUSH rule 0 x 643 [64,47]
+ CRUSH rule 0 x 644 [31,21]
+ CRUSH rule 0 x 645 [76,43]
+ CRUSH rule 0 x 646 [37,54]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21]
+ CRUSH rule 0 x 649 [88,45]
+ CRUSH rule 0 x 650 [116,7]
+ CRUSH rule 0 x 651 [97,106]
+ CRUSH rule 0 x 652 [57,112]
+ CRUSH rule 0 x 653 [8,116]
+ CRUSH rule 0 x 654 [49,32]
+ CRUSH rule 0 x 655 [89,62]
+ CRUSH rule 0 x 656 [0,49]
+ CRUSH rule 0 x 657 [47,17]
+ CRUSH rule 0 x 658 [75,82]
+ CRUSH rule 0 x 659 [26,83]
+ CRUSH rule 0 x 660 [65,112]
+ CRUSH rule 0 x 661 [91,48]
+ CRUSH rule 0 x 662 [111,99]
+ CRUSH rule 0 x 663 [88,35]
+ CRUSH rule 0 x 664 [59,78]
+ CRUSH rule 0 x 665 [78,15]
+ CRUSH rule 0 x 666 [112,4]
+ CRUSH rule 0 x 667 [97,46]
+ CRUSH rule 0 x 668 [97,8]
+ CRUSH rule 0 x 669 [85,66]
+ CRUSH rule 0 x 670 [41,48]
+ CRUSH rule 0 x 671 [116,97]
+ CRUSH rule 0 x 672 [44,55]
+ CRUSH rule 0 x 673 [83,50]
+ CRUSH rule 0 x 674 [36,8]
+ CRUSH rule 0 x 675 [88,14]
+ CRUSH rule 0 x 676 [62,8]
+ CRUSH rule 0 x 677 [88,67]
+ CRUSH rule 0 x 678 [98,83]
+ CRUSH rule 0 x 679 [33,78]
+ CRUSH rule 0 x 680 [55,94]
+ CRUSH rule 0 x 681 [115,95]
+ CRUSH rule 0 x 682 [27,94]
+ CRUSH rule 0 x 683 [57,80]
+ CRUSH rule 0 x 684 [22,65]
+ CRUSH rule 0 x 685 [106,55]
+ CRUSH rule 0 x 686 [86,95]
+ CRUSH rule 0 x 687 [32,57]
+ CRUSH rule 0 x 688 [80,22]
+ CRUSH rule 0 x 689 [6,48]
+ CRUSH rule 0 x 690 [43,70]
+ CRUSH rule 0 x 691 [34,105]
+ CRUSH rule 0 x 692 [40,97]
+ CRUSH rule 0 x 693 [29,84]
+ CRUSH rule 0 x 694 [6,84]
+ CRUSH rule 0 x 695 [19,69]
+ CRUSH rule 0 x 696 [36,75]
+ CRUSH rule 0 x 697 [96,99]
+ CRUSH rule 0 x 698 [61,11]
+ CRUSH rule 0 x 699 [47,62]
+ CRUSH rule 0 x 700 [99,82]
+ CRUSH rule 0 x 701 [42,11]
+ CRUSH rule 0 x 702 [0,71]
+ CRUSH rule 0 x 703 [92,3]
+ CRUSH rule 0 x 704 [10,19]
+ CRUSH rule 0 x 705 [105,21]
+ CRUSH rule 0 x 706 [74,105]
+ CRUSH rule 0 x 707 [0,77]
+ CRUSH rule 0 x 708 [84,8]
+ CRUSH rule 0 x 709 [114,97]
+ CRUSH rule 0 x 710 [94,7]
+ CRUSH rule 0 x 711 [68,49]
+ CRUSH rule 0 x 712 [34,75]
+ CRUSH rule 0 x 713 [29,0]
+ CRUSH rule 0 x 714 [81,115]
+ CRUSH rule 0 x 715 [71,84]
+ CRUSH rule 0 x 716 [40,17]
+ CRUSH rule 0 x 717 [61,62]
+ CRUSH rule 0 x 718 [40,85]
+ CRUSH rule 0 x 719 [59,42]
+ CRUSH rule 0 x 720 [69,72]
+ CRUSH rule 0 x 721 [62,21]
+ CRUSH rule 0 x 722 [115,8]
+ CRUSH rule 0 x 723 [117,41]
+ CRUSH rule 0 x 724 [45,102]
+ CRUSH rule 0 x 725 [53,113]
+ CRUSH rule 0 x 726 [84,19]
+ CRUSH rule 0 x 727 [109,14]
+ CRUSH rule 0 x 728 [76,16]
+ CRUSH rule 0 x 729 [108,47]
+ CRUSH rule 0 x 730 [28,47]
+ CRUSH rule 0 x 731 [78,37]
+ CRUSH rule 0 x 732 [55,90]
+ CRUSH rule 0 x 733 [84,3]
+ CRUSH rule 0 x 734 [27,117]
+ CRUSH rule 0 x 735 [83,4]
+ CRUSH rule 0 x 736 [70,67]
+ CRUSH rule 0 x 737 [117,15]
+ CRUSH rule 0 x 738 [118,22]
+ CRUSH rule 0 x 739 [87,38]
+ CRUSH rule 0 x 740 [29,38]
+ CRUSH rule 0 x 741 [96,73]
+ CRUSH rule 0 x 742 [106,83]
+ CRUSH rule 0 x 743 [105,94]
+ CRUSH rule 0 x 744 [23,14]
+ CRUSH rule 0 x 745 [28,6]
+ CRUSH rule 0 x 746 [56,47]
+ CRUSH rule 0 x 747 [65,70]
+ CRUSH rule 0 x 748 [48,89]
+ CRUSH rule 0 x 749 [102,51]
+ CRUSH rule 0 x 750 [50,3]
+ CRUSH rule 0 x 751 [36,25]
+ CRUSH rule 0 x 752 [69,52]
+ CRUSH rule 0 x 753 [116,65]
+ CRUSH rule 0 x 754 [9,57]
+ CRUSH rule 0 x 755 [98,81]
+ CRUSH rule 0 x 756 [113,8]
+ CRUSH rule 0 x 757 [47,66]
+ CRUSH rule 0 x 758 [57,88]
+ CRUSH rule 0 x 759 [74,97]
+ CRUSH rule 0 x 760 [53,90]
+ CRUSH rule 0 x 761 [78,97]
+ CRUSH rule 0 x 762 [87,104]
+ CRUSH rule 0 x 763 [13,45]
+ CRUSH rule 0 x 764 [106,81]
+ CRUSH rule 0 x 765 [109,91]
+ CRUSH rule 0 x 766 [76,97]
+ CRUSH rule 0 x 767 [41,116]
+ CRUSH rule 0 x 768 [13,114]
+ CRUSH rule 0 x 769 [91,96]
+ CRUSH rule 0 x 770 [105,19]
+ CRUSH rule 0 x 771 [10,76]
+ CRUSH rule 0 x 772 [118,17]
+ CRUSH rule 0 x 773 [116,75]
+ CRUSH rule 0 x 774 [100,43]
+ CRUSH rule 0 x 775 [102,43]
+ CRUSH rule 0 x 776 [69,38]
+ CRUSH rule 0 x 777 [76,49]
+ CRUSH rule 0 x 778 [38,13]
+ CRUSH rule 0 x 779 [46,21]
+ CRUSH rule 0 x 780 [63,102]
+ CRUSH rule 0 x 781 [105,92]
+ CRUSH rule 0 x 782 [117,31]
+ CRUSH rule 0 x 783 [60,93]
+ CRUSH rule 0 x 784 [82,81]
+ CRUSH rule 0 x 785 [27,84]
+ CRUSH rule 0 x 786 [41,80]
+ CRUSH rule 0 x 787 [13,54]
+ CRUSH rule 0 x 788 [4,100]
+ CRUSH rule 0 x 789 [50,37]
+ CRUSH rule 0 x 790 [58,16]
+ CRUSH rule 0 x 791 [96,14]
+ CRUSH rule 0 x 792 [80,4]
+ CRUSH rule 0 x 793 [6,71]
+ CRUSH rule 0 x 794 [14,89]
+ CRUSH rule 0 x 795 [51,3]
+ CRUSH rule 0 x 796 [114,77]
+ CRUSH rule 0 x 797 [79,100]
+ CRUSH rule 0 x 798 [42,10]
+ CRUSH rule 0 x 799 [48,11]
+ CRUSH rule 0 x 800 [91,7]
+ CRUSH rule 0 x 801 [2,6]
+ CRUSH rule 0 x 802 [116,89]
+ CRUSH rule 0 x 803 [37,32]
+ CRUSH rule 0 x 804 [33,4]
+ CRUSH rule 0 x 805 [96,22]
+ CRUSH rule 0 x 806 [67,90]
+ CRUSH rule 0 x 807 [47,42]
+ CRUSH rule 0 x 808 [76,79]
+ CRUSH rule 0 x 809 [27,26]
+ CRUSH rule 0 x 810 [119,61]
+ CRUSH rule 0 x 811 [75,72]
+ CRUSH rule 0 x 812 [25,52]
+ CRUSH rule 0 x 813 [64,13]
+ CRUSH rule 0 x 814 [110,53]
+ CRUSH rule 0 x 815 [84,61]
+ CRUSH rule 0 x 816 [25,22]
+ CRUSH rule 0 x 817 [40,73]
+ CRUSH rule 0 x 818 [34,13]
+ CRUSH rule 0 x 819 [88,19]
+ CRUSH rule 0 x 820 [104,49]
+ CRUSH rule 0 x 821 [58,69]
+ CRUSH rule 0 x 822 [29,72]
+ CRUSH rule 0 x 823 [100,103]
+ CRUSH rule 0 x 824 [102,81]
+ CRUSH rule 0 x 825 [47,17]
+ CRUSH rule 0 x 826 [45,34]
+ CRUSH rule 0 x 827 [101,11]
+ CRUSH rule 0 x 828 [60,27]
+ CRUSH rule 0 x 829 [45,90]
+ CRUSH rule 0 x 830 [51,96]
+ CRUSH rule 0 x 831 [6,64]
+ CRUSH rule 0 x 832 [57,78]
+ CRUSH rule 0 x 833 [34,97]
+ CRUSH rule 0 x 834 [90,33]
+ CRUSH rule 0 x 835 [14,46]
+ CRUSH rule 0 x 836 [38,43]
+ CRUSH rule 0 x 837 [51,74]
+ CRUSH rule 0 x 838 [6,32]
+ CRUSH rule 0 x 839 [106,8]
+ CRUSH rule 0 x 840 [33,109]
+ CRUSH rule 0 x 841 [110,15]
+ CRUSH rule 0 x 842 [66,67]
+ CRUSH rule 0 x 843 [11,63]
+ CRUSH rule 0 x 844 [74,13]
+ CRUSH rule 0 x 845 [74,43]
+ CRUSH rule 0 x 846 [98,107]
+ CRUSH rule 0 x 847 [10,3]
+ CRUSH rule 0 x 848 [89,17]
+ CRUSH rule 0 x 849 [42,59]
+ CRUSH rule 0 x 850 [40,73]
+ CRUSH rule 0 x 851 [65,94]
+ CRUSH rule 0 x 852 [31,94]
+ CRUSH rule 0 x 853 [49,11]
+ CRUSH rule 0 x 854 [90,31]
+ CRUSH rule 0 x 855 [2,19]
+ CRUSH rule 0 x 856 [40,22]
+ CRUSH rule 0 x 857 [15,82]
+ CRUSH rule 0 x 858 [10,80]
+ CRUSH rule 0 x 859 [29,48]
+ CRUSH rule 0 x 860 [114,75]
+ CRUSH rule 0 x 861 [22,33]
+ CRUSH rule 0 x 862 [22,25]
+ CRUSH rule 0 x 863 [79,50]
+ CRUSH rule 0 x 864 [68,6]
+ CRUSH rule 0 x 865 [25,92]
+ CRUSH rule 0 x 866 [18,89]
+ CRUSH rule 0 x 867 [3,78]
+ CRUSH rule 0 x 868 [81,98]
+ CRUSH rule 0 x 869 [22,104]
+ CRUSH rule 0 x 870 [73,98]
+ CRUSH rule 0 x 871 [25,54]
+ CRUSH rule 0 x 872 [39,48]
+ CRUSH rule 0 x 873 [92,9]
+ CRUSH rule 0 x 874 [21,43]
+ CRUSH rule 0 x 875 [27,108]
+ CRUSH rule 0 x 876 [98,75]
+ CRUSH rule 0 x 877 [73,5]
+ CRUSH rule 0 x 878 [64,45]
+ CRUSH rule 0 x 879 [29,18]
+ CRUSH rule 0 x 880 [56,91]
+ CRUSH rule 0 x 881 [109,69]
+ CRUSH rule 0 x 882 [60,33]
+ CRUSH rule 0 x 883 [93,96]
+ CRUSH rule 0 x 884 [67,58]
+ CRUSH rule 0 x 885 [31,8]
+ CRUSH rule 0 x 886 [2,107]
+ CRUSH rule 0 x 887 [5,93]
+ CRUSH rule 0 x 888 [16,13]
+ CRUSH rule 0 x 889 [3,76]
+ CRUSH rule 0 x 890 [48,63]
+ CRUSH rule 0 x 891 [86,79]
+ CRUSH rule 0 x 892 [64,9]
+ CRUSH rule 0 x 893 [118,33]
+ CRUSH rule 0 x 894 [16,111]
+ CRUSH rule 0 x 895 [40,107]
+ CRUSH rule 0 x 896 [97,96]
+ CRUSH rule 0 x 897 [60,67]
+ CRUSH rule 0 x 898 [10,2]
+ CRUSH rule 0 x 899 [75,80]
+ CRUSH rule 0 x 900 [102,81]
+ CRUSH rule 0 x 901 [66,87]
+ CRUSH rule 0 x 902 [102,49]
+ CRUSH rule 0 x 903 [5,14]
+ CRUSH rule 0 x 904 [50,16]
+ CRUSH rule 0 x 905 [19,51]
+ CRUSH rule 0 x 906 [75,119]
+ CRUSH rule 0 x 907 [47,5]
+ CRUSH rule 0 x 908 [96,9]
+ CRUSH rule 0 x 909 [94,75]
+ CRUSH rule 0 x 910 [88,63]
+ CRUSH rule 0 x 911 [102,23]
+ CRUSH rule 0 x 912 [91,60]
+ CRUSH rule 0 x 913 [29,17]
+ CRUSH rule 0 x 914 [84,29]
+ CRUSH rule 0 x 915 [70,22]
+ CRUSH rule 0 x 916 [32,9]
+ CRUSH rule 0 x 917 [43,26]
+ CRUSH rule 0 x 918 [91,98]
+ CRUSH rule 0 x 919 [13,69]
+ CRUSH rule 0 x 920 [18,87]
+ CRUSH rule 0 x 921 [104,33]
+ CRUSH rule 0 x 922 [33,19]
+ CRUSH rule 0 x 923 [28,8]
+ CRUSH rule 0 x 924 [69,88]
+ CRUSH rule 0 x 925 [71,32]
+ CRUSH rule 0 x 926 [64,69]
+ CRUSH rule 0 x 927 [99,106]
+ CRUSH rule 0 x 928 [13,113]
+ CRUSH rule 0 x 929 [117,61]
+ CRUSH rule 0 x 930 [31,82]
+ CRUSH rule 0 x 931 [46,79]
+ CRUSH rule 0 x 932 [60,13]
+ CRUSH rule 0 x 933 [88,31]
+ CRUSH rule 0 x 934 [68,4]
+ CRUSH rule 0 x 935 [31,18]
+ CRUSH rule 0 x 936 [104,57]
+ CRUSH rule 0 x 937 [110,22]
+ CRUSH rule 0 x 938 [29,106]
+ CRUSH rule 0 x 939 [77,13]
+ CRUSH rule 0 x 940 [76,33]
+ CRUSH rule 0 x 941 [66,37]
+ CRUSH rule 0 x 942 [83,94]
+ CRUSH rule 0 x 943 [4,74]
+ CRUSH rule 0 x 944 [113,53]
+ CRUSH rule 0 x 945 [17,52]
+ CRUSH rule 0 x 946 [37,111]
+ CRUSH rule 0 x 947 [107,74]
+ CRUSH rule 0 x 948 [55,98]
+ CRUSH rule 0 x 949 [45,72]
+ CRUSH rule 0 x 950 [96,23]
+ CRUSH rule 0 x 951 [40,93]
+ CRUSH rule 0 x 952 [93,46]
+ CRUSH rule 0 x 953 [55,92]
+ CRUSH rule 0 x 954 [84,57]
+ CRUSH rule 0 x 955 [31,117]
+ CRUSH rule 0 x 956 [72,11]
+ CRUSH rule 0 x 957 [3,74]
+ CRUSH rule 0 x 958 [8,106]
+ CRUSH rule 0 x 959 [42,59]
+ CRUSH rule 0 x 960 [113,107]
+ CRUSH rule 0 x 961 [116,8]
+ CRUSH rule 0 x 962 [13,62]
+ CRUSH rule 0 x 963 [0,99]
+ CRUSH rule 0 x 964 [59,21]
+ CRUSH rule 0 x 965 [47,115]
+ CRUSH rule 0 x 966 [88,63]
+ CRUSH rule 0 x 967 [71,108]
+ CRUSH rule 0 x 968 [73,7]
+ CRUSH rule 0 x 969 [53,6]
+ CRUSH rule 0 x 970 [3,40]
+ CRUSH rule 0 x 971 [87,38]
+ CRUSH rule 0 x 972 [3,37]
+ CRUSH rule 0 x 973 [113,27]
+ CRUSH rule 0 x 974 [114,23]
+ CRUSH rule 0 x 975 [40,59]
+ CRUSH rule 0 x 976 [81,38]
+ CRUSH rule 0 x 977 [95,102]
+ CRUSH rule 0 x 978 [35,56]
+ CRUSH rule 0 x 979 [98,6]
+ CRUSH rule 0 x 980 [52,69]
+ CRUSH rule 0 x 981 [89,117]
+ CRUSH rule 0 x 982 [1,47]
+ CRUSH rule 0 x 983 [34,61]
+ CRUSH rule 0 x 984 [78,25]
+ CRUSH rule 0 x 985 [99,52]
+ CRUSH rule 0 x 986 [4,59]
+ CRUSH rule 0 x 987 [78,21]
+ CRUSH rule 0 x 988 [79,2]
+ CRUSH rule 0 x 989 [87,17]
+ CRUSH rule 0 x 990 [47,118]
+ CRUSH rule 0 x 991 [61,18]
+ CRUSH rule 0 x 992 [83,66]
+ CRUSH rule 0 x 993 [75,62]
+ CRUSH rule 0 x 994 [74,57]
+ CRUSH rule 0 x 995 [100,97]
+ CRUSH rule 0 x 996 [41,6]
+ CRUSH rule 0 x 997 [89,76]
+ CRUSH rule 0 x 998 [92,47]
+ CRUSH rule 0 x 999 [101,11]
+ CRUSH rule 0 x 1000 [9,119]
+ CRUSH rule 0 x 1001 [49,32]
+ CRUSH rule 0 x 1002 [99,113]
+ CRUSH rule 0 x 1003 [43,18]
+ CRUSH rule 0 x 1004 [89,54]
+ CRUSH rule 0 x 1005 [105,84]
+ CRUSH rule 0 x 1006 [45,111]
+ CRUSH rule 0 x 1007 [19,57]
+ CRUSH rule 0 x 1008 [31,24]
+ CRUSH rule 0 x 1009 [19,111]
+ CRUSH rule 0 x 1010 [42,89]
+ CRUSH rule 0 x 1011 [25,114]
+ CRUSH rule 0 x 1012 [68,71]
+ CRUSH rule 0 x 1013 [5,65]
+ CRUSH rule 0 x 1014 [33,4]
+ CRUSH rule 0 x 1015 [106,45]
+ CRUSH rule 0 x 1016 [88,39]
+ CRUSH rule 0 x 1017 [0,89]
+ CRUSH rule 0 x 1018 [63,5]
+ CRUSH rule 0 x 1019 [104,97]
+ CRUSH rule 0 x 1020 [96,9]
+ CRUSH rule 0 x 1021 [117,6]
+ CRUSH rule 0 x 1022 [73,21]
+ CRUSH rule 0 x 1023 [0,16]
+ rule 0 (data) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 3 result size == 2:\t2/1024 (esc)
+ rule 0 (data) num_rep 3 result size == 3:\t1022/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 4 result size == 2:\t2/1024 (esc)
+ rule 0 (data) num_rep 4 result size == 3:\t1022/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 5 result size == 2:\t2/1024 (esc)
+ rule 0 (data) num_rep 5 result size == 3:\t1022/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76,9]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 6 result size == 2:\t1/1024 (esc)
+ rule 0 (data) num_rep 6 result size == 3:\t1023/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76,9]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 7 result size == 2:\t1/1024 (esc)
+ rule 0 (data) num_rep 7 result size == 3:\t1023/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76,9]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 8 result size == 2:\t1/1024 (esc)
+ rule 0 (data) num_rep 8 result size == 3:\t1023/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76,9]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 9 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,17]
+ CRUSH rule 0 x 2 [91,96,4]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,57,8]
+ CRUSH rule 0 x 9 [101,102,4]
+ CRUSH rule 0 x 10 [61,58,22]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,17]
+ CRUSH rule 0 x 14 [105,72,13]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,52]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,49,9]
+ CRUSH rule 0 x 22 [23,104,21]
+ CRUSH rule 0 x 23 [118,63,6]
+ CRUSH rule 0 x 24 [83,38,8]
+ CRUSH rule 0 x 25 [81,64,3]
+ CRUSH rule 0 x 26 [38,99,3]
+ CRUSH rule 0 x 27 [76,107,17]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [24,71,19]
+ CRUSH rule 0 x 30 [94,87,19]
+ CRUSH rule 0 x 31 [76,95,22]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,3]
+ CRUSH rule 0 x 34 [7,108,83]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [61,109,11]
+ CRUSH rule 0 x 38 [72,85,3]
+ CRUSH rule 0 x 39 [68,103,8]
+ CRUSH rule 0 x 40 [103,78,3]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,33,9]
+ CRUSH rule 0 x 43 [10,68,11]
+ CRUSH rule 0 x 44 [101,4,109]
+ CRUSH rule 0 x 45 [83,15,24]
+ CRUSH rule 0 x 46 [65,1,7]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,33,14]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,101]
+ CRUSH rule 0 x 51 [104,75,9]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,69,7]
+ CRUSH rule 0 x 54 [9,79,104]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,63]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,3]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,9]
+ CRUSH rule 0 x 64 [1,35,9]
+ CRUSH rule 0 x 65 [32,103,15]
+ CRUSH rule 0 x 66 [48,99,9]
+ CRUSH rule 0 x 67 [94,103,15]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,11]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [9,33,38]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,6]
+ CRUSH rule 0 x 74 [29,50,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,7]
+ CRUSH rule 0 x 77 [107,21,0]
+ CRUSH rule 0 x 78 [11,89,102]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,14]
+ CRUSH rule 0 x 81 [71,109,19]
+ CRUSH rule 0 x 82 [37,21,74]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,7]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,27,17]
+ CRUSH rule 0 x 89 [76,77,19]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [68,19,105]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,22]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,17]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [93,102,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [6,63,104]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,7]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,7]
+ CRUSH rule 0 x 106 [69,116,4]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,13,25]
+ CRUSH rule 0 x 110 [54,61,13]
+ CRUSH rule 0 x 111 [10,78,3]
+ CRUSH rule 0 x 112 [89,9,109]
+ CRUSH rule 0 x 113 [69,2,9]
+ CRUSH rule 0 x 114 [79,110,9]
+ CRUSH rule 0 x 115 [50,85,6]
+ CRUSH rule 0 x 116 [96,16,4]
+ CRUSH rule 0 x 117 [87,42,13]
+ CRUSH rule 0 x 118 [23,56,13]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,9,114]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,35,14]
+ CRUSH rule 0 x 124 [110,49,17]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,8]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,9]
+ CRUSH rule 0 x 132 [69,70,19]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,29,8]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,17]
+ CRUSH rule 0 x 137 [11,78,75]
+ CRUSH rule 0 x 138 [17,94,85]
+ CRUSH rule 0 x 139 [89,60,8]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,4]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [8,64,53]
+ CRUSH rule 0 x 147 [22,37,94]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [14,47,110]
+ CRUSH rule 0 x 151 [90,4,65]
+ CRUSH rule 0 x 152 [49,18,15]
+ CRUSH rule 0 x 153 [71,44,9]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,6,70]
+ CRUSH rule 0 x 156 [94,85,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,29,3]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [19,78,95]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,31,9]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,7]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,15]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,17]
+ CRUSH rule 0 x 171 [73,90,13]
+ CRUSH rule 0 x 172 [33,15,102]
+ CRUSH rule 0 x 173 [102,59,19]
+ CRUSH rule 0 x 174 [116,25,15]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,3]
+ CRUSH rule 0 x 177 [52,85,8]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,7,99]
+ CRUSH rule 0 x 181 [18,59,15]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,101,6]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,4]
+ CRUSH rule 0 x 187 [116,11,31]
+ CRUSH rule 0 x 188 [69,92,9]
+ CRUSH rule 0 x 189 [47,84,3]
+ CRUSH rule 0 x 190 [90,13,23]
+ CRUSH rule 0 x 191 [49,17,60]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [17,58,61]
+ CRUSH rule 0 x 195 [119,41,9]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,13]
+ CRUSH rule 0 x 198 [114,95,14]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,14]
+ CRUSH rule 0 x 201 [14,29,109]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,22,101]
+ CRUSH rule 0 x 204 [10,98,17]
+ CRUSH rule 0 x 205 [22,61,72]
+ CRUSH rule 0 x 206 [49,112,15]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,11]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [78,47,13]
+ CRUSH rule 0 x 215 [61,22,102]
+ CRUSH rule 0 x 216 [99,3,104]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [93,96,4]
+ CRUSH rule 0 x 219 [28,59,6]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,15]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [61,11,64]
+ CRUSH rule 0 x 226 [44,22,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,22]
+ CRUSH rule 0 x 229 [100,79,9]
+ CRUSH rule 0 x 230 [41,114,11]
+ CRUSH rule 0 x 231 [56,95,8]
+ CRUSH rule 0 x 232 [23,44,11]
+ CRUSH rule 0 x 233 [88,103,21]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,11]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,85,14]
+ CRUSH rule 0 x 241 [47,108,15]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [96,19,105]
+ CRUSH rule 0 x 245 [27,28,19]
+ CRUSH rule 0 x 246 [35,82,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,13]
+ CRUSH rule 0 x 250 [79,102,13]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,103,3]
+ CRUSH rule 0 x 255 [112,22,85]
+ CRUSH rule 0 x 256 [37,38,11]
+ CRUSH rule 0 x 257 [69,117,9]
+ CRUSH rule 0 x 258 [34,55,19]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,22]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,17,107]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,7]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [43,104,7]
+ CRUSH rule 0 x 270 [58,37,4]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,22]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,43]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,95,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,4]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,7,47]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,15]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,104,45]
+ CRUSH rule 0 x 290 [115,7,101]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [52,16,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,7]
+ CRUSH rule 0 x 297 [35,40,9]
+ CRUSH rule 0 x 298 [71,118,8]
+ CRUSH rule 0 x 299 [79,1,19]
+ CRUSH rule 0 x 300 [67,5,9]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,13]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,95]
+ CRUSH rule 0 x 308 [91,98,21]
+ CRUSH rule 0 x 309 [15,18,99]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,41,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,3]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,8,96]
+ CRUSH rule 0 x 316 [4,1,79]
+ CRUSH rule 0 x 317 [118,8,31]
+ CRUSH rule 0 x 318 [32,47,7]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,97,17]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,42,21]
+ CRUSH rule 0 x 323 [73,0,13]
+ CRUSH rule 0 x 324 [64,37,21]
+ CRUSH rule 0 x 325 [52,16,3]
+ CRUSH rule 0 x 326 [111,93,13]
+ CRUSH rule 0 x 327 [62,16,19]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,21,111]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [94,35,15]
+ CRUSH rule 0 x 335 [71,74,7]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,11,52]
+ CRUSH rule 0 x 338 [109,69,13]
+ CRUSH rule 0 x 339 [13,64,93]
+ CRUSH rule 0 x 340 [119,15,107]
+ CRUSH rule 0 x 341 [63,114,14]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,17]
+ CRUSH rule 0 x 344 [103,26,7]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [10,113,13]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,14]
+ CRUSH rule 0 x 356 [114,41,14]
+ CRUSH rule 0 x 357 [70,13,75]
+ CRUSH rule 0 x 358 [97,13,42]
+ CRUSH rule 0 x 359 [4,117,87]
+ CRUSH rule 0 x 360 [106,69,15]
+ CRUSH rule 0 x 361 [27,46,6]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [57,114,19]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [108,65,8]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [11,57,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [6,42,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,103]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [93,113,11]
+ CRUSH rule 0 x 378 [67,36,15]
+ CRUSH rule 0 x 379 [77,115,7]
+ CRUSH rule 0 x 380 [3,108,83]
+ CRUSH rule 0 x 381 [55,1,14]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,4,67]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,41,21]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,1,45]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,14,27]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,7]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [19,63,98]
+ CRUSH rule 0 x 401 [79,34,11]
+ CRUSH rule 0 x 402 [107,98,8]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,7]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,14]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,21]
+ CRUSH rule 0 x 412 [108,99,9]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [21,68,57]
+ CRUSH rule 0 x 417 [8,70,61]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [8,66,79]
+ CRUSH rule 0 x 420 [109,105,7]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,87,17]
+ CRUSH rule 0 x 423 [59,98,9]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [8,115,65]
+ CRUSH rule 0 x 428 [68,103,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [69,86,13]
+ CRUSH rule 0 x 431 [70,83,17]
+ CRUSH rule 0 x 432 [46,37,19]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,6]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,9]
+ CRUSH rule 0 x 438 [105,98,6]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,7,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,9]
+ CRUSH rule 0 x 444 [72,27,9]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,7]
+ CRUSH rule 0 x 447 [13,61,90]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,79,17]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,11]
+ CRUSH rule 0 x 453 [82,22,59]
+ CRUSH rule 0 x 454 [53,18,21]
+ CRUSH rule 0 x 455 [91,92,3]
+ CRUSH rule 0 x 456 [101,104,9]
+ CRUSH rule 0 x 457 [113,51,4]
+ CRUSH rule 0 x 458 [53,34,21]
+ CRUSH rule 0 x 459 [25,115,11]
+ CRUSH rule 0 x 460 [105,9,74]
+ CRUSH rule 0 x 461 [102,35,13]
+ CRUSH rule 0 x 462 [98,107,8]
+ CRUSH rule 0 x 463 [108,105,11]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,21]
+ CRUSH rule 0 x 466 [66,7,16]
+ CRUSH rule 0 x 467 [6,57,44]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,9]
+ CRUSH rule 0 x 470 [50,3,45]
+ CRUSH rule 0 x 471 [40,79,17]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,21,36]
+ CRUSH rule 0 x 474 [51,32,15]
+ CRUSH rule 0 x 475 [49,110,22]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [47,46,6]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,85,11]
+ CRUSH rule 0 x 483 [15,116,63]
+ CRUSH rule 0 x 484 [37,36,8]
+ CRUSH rule 0 x 485 [47,117,17]
+ CRUSH rule 0 x 486 [92,10,6]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,9,87]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,17,101]
+ CRUSH rule 0 x 491 [80,71,8]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,14]
+ CRUSH rule 0 x 494 [4,87,114]
+ CRUSH rule 0 x 495 [40,43,17]
+ CRUSH rule 0 x 496 [93,38,3]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,3]
+ CRUSH rule 0 x 499 [10,26,7]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,9,103]
+ CRUSH rule 0 x 502 [11,64,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,9]
+ CRUSH rule 0 x 505 [91,100,21]
+ CRUSH rule 0 x 506 [82,103,14]
+ CRUSH rule 0 x 507 [81,54,6]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,106]
+ CRUSH rule 0 x 511 [72,27,21]
+ CRUSH rule 0 x 512 [118,73,13]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,11,29]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,13,43]
+ CRUSH rule 0 x 517 [83,60,8]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,88,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [56,73,19]
+ CRUSH rule 0 x 523 [36,35,3]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [3,119,45]
+ CRUSH rule 0 x 526 [83,50,3]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,87,15]
+ CRUSH rule 0 x 529 [107,60,4]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,104,21]
+ CRUSH rule 0 x 532 [68,14,107]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [8,75,88]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,56,17]
+ CRUSH rule 0 x 539 [10,9,117]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,11]
+ CRUSH rule 0 x 542 [50,27,13]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,65,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [67,50,4]
+ CRUSH rule 0 x 548 [58,61,6]
+ CRUSH rule 0 x 549 [60,22,89]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,59]
+ CRUSH rule 0 x 552 [70,65,22]
+ CRUSH rule 0 x 553 [96,105,9]
+ CRUSH rule 0 x 554 [61,94,22]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,97,8]
+ CRUSH rule 0 x 561 [27,76,9]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,15]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,19]
+ CRUSH rule 0 x 566 [27,38,11]
+ CRUSH rule 0 x 567 [88,8,25]
+ CRUSH rule 0 x 568 [106,17,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [98,27,19]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,83,7]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [87,19,38]
+ CRUSH rule 0 x 576 [112,73,19]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,99,7]
+ CRUSH rule 0 x 579 [78,77,17]
+ CRUSH rule 0 x 580 [68,95,7]
+ CRUSH rule 0 x 581 [55,52,7]
+ CRUSH rule 0 x 582 [15,113,77]
+ CRUSH rule 0 x 583 [74,105,15]
+ CRUSH rule 0 x 584 [22,92,87]
+ CRUSH rule 0 x 585 [35,1,15]
+ CRUSH rule 0 x 586 [33,1,13]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [40,69,4]
+ CRUSH rule 0 x 591 [42,23,11]
+ CRUSH rule 0 x 592 [45,22,108]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,34]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,9]
+ CRUSH rule 0 x 598 [34,19,69]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [24,27,21]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,3]
+ CRUSH rule 0 x 603 [24,13,41]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,13]
+ CRUSH rule 0 x 606 [49,34,13]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [112,91,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,14]
+ CRUSH rule 0 x 611 [66,87,3]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [13,91,96]
+ CRUSH rule 0 x 614 [81,88,11]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [26,99,9]
+ CRUSH rule 0 x 619 [92,27,19]
+ CRUSH rule 0 x 620 [108,103,15]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,14]
+ CRUSH rule 0 x 623 [94,61,15]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,19]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,90]
+ CRUSH rule 0 x 628 [65,88,7]
+ CRUSH rule 0 x 629 [6,46,87]
+ CRUSH rule 0 x 630 [22,72,55]
+ CRUSH rule 0 x 631 [35,22,94]
+ CRUSH rule 0 x 632 [81,0,14]
+ CRUSH rule 0 x 633 [65,68,13]
+ CRUSH rule 0 x 634 [87,50,7]
+ CRUSH rule 0 x 635 [40,73,13]
+ CRUSH rule 0 x 636 [23,70,3]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,11]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,3]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,43,6]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,14]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,3]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [8,116,97]
+ CRUSH rule 0 x 654 [49,32,7]
+ CRUSH rule 0 x 655 [89,62,17]
+ CRUSH rule 0 x 656 [0,49,22]
+ CRUSH rule 0 x 657 [47,17,58]
+ CRUSH rule 0 x 658 [75,82,17]
+ CRUSH rule 0 x 659 [26,83,8]
+ CRUSH rule 0 x 660 [65,112,13]
+ CRUSH rule 0 x 661 [91,48,3]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,8,56]
+ CRUSH rule 0 x 669 [85,66,3]
+ CRUSH rule 0 x 670 [41,48,14]
+ CRUSH rule 0 x 671 [116,97,13]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [36,8,65]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,8]
+ CRUSH rule 0 x 678 [98,83,3]
+ CRUSH rule 0 x 679 [33,78,3]
+ CRUSH rule 0 x 680 [55,94,17]
+ CRUSH rule 0 x 681 [115,95,3]
+ CRUSH rule 0 x 682 [27,94,15]
+ CRUSH rule 0 x 683 [57,80,9]
+ CRUSH rule 0 x 684 [22,65,44]
+ CRUSH rule 0 x 685 [106,55,8]
+ CRUSH rule 0 x 686 [86,95,4]
+ CRUSH rule 0 x 687 [32,57,13]
+ CRUSH rule 0 x 688 [80,22,49]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,4]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,21]
+ CRUSH rule 0 x 694 [6,84,57]
+ CRUSH rule 0 x 695 [19,69,112]
+ CRUSH rule 0 x 696 [36,75,11]
+ CRUSH rule 0 x 697 [96,99,14]
+ CRUSH rule 0 x 698 [61,11,84]
+ CRUSH rule 0 x 699 [47,62,15]
+ CRUSH rule 0 x 700 [99,82,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,3,89]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,21,2]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,15]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,19]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,8]
+ CRUSH rule 0 x 712 [34,75,11]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,3]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,13]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,21,35]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,13]
+ CRUSH rule 0 x 724 [45,102,4]
+ CRUSH rule 0 x 725 [53,113,13]
+ CRUSH rule 0 x 726 [84,19,103]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,47,11]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,37,14]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,4]
+ CRUSH rule 0 x 735 [83,4,54]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,22,65]
+ CRUSH rule 0 x 739 [87,38,11]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,9]
+ CRUSH rule 0 x 744 [23,14,78]
+ CRUSH rule 0 x 745 [28,6,87]
+ CRUSH rule 0 x 746 [56,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,6]
+ CRUSH rule 0 x 750 [50,3,59]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [116,65,21]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,8,43]
+ CRUSH rule 0 x 757 [47,66,14]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,97,6]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,7]
+ CRUSH rule 0 x 762 [87,104,8]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,6]
+ CRUSH rule 0 x 766 [76,97,7]
+ CRUSH rule 0 x 767 [41,116,6]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,96,13]
+ CRUSH rule 0 x 770 [105,19,104]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [118,17,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,19]
+ CRUSH rule 0 x 775 [102,43,13]
+ CRUSH rule 0 x 776 [69,38,14]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,89]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [105,92,22]
+ CRUSH rule 0 x 782 [117,31,13]
+ CRUSH rule 0 x 783 [60,93,13]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,43]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,14,105]
+ CRUSH rule 0 x 792 [80,4,35]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,3,78]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,10,7]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,89,7]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [33,4,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,17]
+ CRUSH rule 0 x 808 [76,79,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,8]
+ CRUSH rule 0 x 811 [75,72,15]
+ CRUSH rule 0 x 812 [25,52,13]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,61,4]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,73,13]
+ CRUSH rule 0 x 818 [34,13,45]
+ CRUSH rule 0 x 819 [88,19,85]
+ CRUSH rule 0 x 820 [104,49,11]
+ CRUSH rule 0 x 821 [58,69,14]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,4]
+ CRUSH rule 0 x 825 [47,17,94]
+ CRUSH rule 0 x 826 [45,34,22]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,64,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,3]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [14,46,25]
+ CRUSH rule 0 x 836 [38,43,7]
+ CRUSH rule 0 x 837 [51,74,15]
+ CRUSH rule 0 x 838 [6,32,107]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,3]
+ CRUSH rule 0 x 841 [110,15,71]
+ CRUSH rule 0 x 842 [66,67,13]
+ CRUSH rule 0 x 843 [11,63,48]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,19]
+ CRUSH rule 0 x 847 [10,3,88]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,14]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,94,11]
+ CRUSH rule 0 x 852 [31,94,7]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [90,31,21]
+ CRUSH rule 0 x 855 [2,19,81]
+ CRUSH rule 0 x 856 [40,22,61]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,19]
+ CRUSH rule 0 x 859 [29,48,4]
+ CRUSH rule 0 x 860 [114,75,21]
+ CRUSH rule 0 x 861 [22,33,98]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,50,11]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,22]
+ CRUSH rule 0 x 867 [3,78,41]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [22,104,89]
+ CRUSH rule 0 x 870 [73,98,3]
+ CRUSH rule 0 x 871 [25,54,19]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,9,75]
+ CRUSH rule 0 x 874 [21,43,66]
+ CRUSH rule 0 x 875 [27,108,7]
+ CRUSH rule 0 x 876 [98,75,13]
+ CRUSH rule 0 x 877 [73,5,4]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [29,18,9]
+ CRUSH rule 0 x 880 [56,91,13]
+ CRUSH rule 0 x 881 [109,69,4]
+ CRUSH rule 0 x 882 [60,33,11]
+ CRUSH rule 0 x 883 [93,96,11]
+ CRUSH rule 0 x 884 [67,58,4]
+ CRUSH rule 0 x 885 [31,8,104]
+ CRUSH rule 0 x 886 [2,107,9]
+ CRUSH rule 0 x 887 [5,93,19]
+ CRUSH rule 0 x 888 [16,13,26]
+ CRUSH rule 0 x 889 [3,76,93]
+ CRUSH rule 0 x 890 [48,63,4]
+ CRUSH rule 0 x 891 [86,79,22]
+ CRUSH rule 0 x 892 [64,9,10]
+ CRUSH rule 0 x 893 [118,33,22]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,107,4]
+ CRUSH rule 0 x 896 [97,96,14]
+ CRUSH rule 0 x 897 [60,67,22]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,80,4]
+ CRUSH rule 0 x 900 [102,81,8]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,8]
+ CRUSH rule 0 x 903 [5,14,33]
+ CRUSH rule 0 x 904 [50,16,4]
+ CRUSH rule 0 x 905 [19,51,110]
+ CRUSH rule 0 x 906 [75,119,13]
+ CRUSH rule 0 x 907 [47,5,7]
+ CRUSH rule 0 x 908 [96,9,29]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,29,17]
+ CRUSH rule 0 x 915 [70,22,107]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,26,3]
+ CRUSH rule 0 x 918 [91,98,6]
+ CRUSH rule 0 x 919 [13,69,56]
+ CRUSH rule 0 x 920 [18,87,11]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,19,117]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,9]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,15]
+ CRUSH rule 0 x 927 [99,106,13]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,3]
+ CRUSH rule 0 x 931 [46,79,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [88,31,6]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [104,57,6]
+ CRUSH rule 0 x 937 [110,22,95]
+ CRUSH rule 0 x 938 [29,106,13]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,33,7]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [4,74,89]
+ CRUSH rule 0 x 944 [113,53,21]
+ CRUSH rule 0 x 945 [17,52,16]
+ CRUSH rule 0 x 946 [37,111,11]
+ CRUSH rule 0 x 947 [107,74,7]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [45,72,21]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,6]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [8,106,43]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,14]
+ CRUSH rule 0 x 964 [59,21,32]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,13]
+ CRUSH rule 0 x 967 [71,108,14]
+ CRUSH rule 0 x 968 [73,7,54]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [3,40,65]
+ CRUSH rule 0 x 971 [87,38,9]
+ CRUSH rule 0 x 972 [3,37,109]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [114,23,13]
+ CRUSH rule 0 x 975 [40,59,8]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,11]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,3]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,22]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,8]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,9]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [75,62,8]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,7]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,7]
+ CRUSH rule 0 x 998 [92,47,13]
+ CRUSH rule 0 x 999 [101,11,66]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,15]
+ CRUSH rule 0 x 1005 [105,84,8]
+ CRUSH rule 0 x 1006 [45,111,6]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,24,13]
+ CRUSH rule 0 x 1009 [19,111,61]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,6]
+ CRUSH rule 0 x 1012 [68,71,21]
+ CRUSH rule 0 x 1013 [5,65,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [106,45,9]
+ CRUSH rule 0 x 1016 [88,39,4]
+ CRUSH rule 0 x 1017 [0,89,7]
+ CRUSH rule 0 x 1018 [63,5,7]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,9,91]
+ CRUSH rule 0 x 1021 [117,6,43]
+ CRUSH rule 0 x 1022 [73,21,36]
+ CRUSH rule 0 x 1023 [0,16,3]
+ rule 0 (data) num_rep 10 result size == 3:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-firstn-indep.t b/src/test/cli/crushtool/test-map-firstn-indep.t
new file mode 100644
index 000000000..4b67285d1
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-firstn-indep.t
@@ -0,0 +1,14 @@
+ $ crushtool -c "$TESTDIR/test-map-firstn-indep.txt" -o "$TESTDIR/test-map-firstn-indep.crushmap"
+ $ crushtool -i "$TESTDIR/test-map-firstn-indep.crushmap" --test --rule 0 --x 1 --show-bad-mappings --min-rep 1 --max-rep 10
+ bad mapping rule 0 x 1 num_rep 9 result [93,80,88,87,56,50,53,72]
+ bad mapping rule 0 x 1 num_rep 10 result [93,80,88,87,56,50,53,72]
+ $ crushtool -i "$TESTDIR/test-map-firstn-indep.crushmap" --test --rule 1 --x 1 --show-bad-mappings --min-rep 1 --max-rep 10
+ bad mapping rule 1 x 1 num_rep 3 result [93,56]
+ bad mapping rule 1 x 1 num_rep 4 result [93,56]
+ bad mapping rule 1 x 1 num_rep 5 result [93,56]
+ bad mapping rule 1 x 1 num_rep 6 result [93,56]
+ bad mapping rule 1 x 1 num_rep 7 result [93,56]
+ bad mapping rule 1 x 1 num_rep 8 result [93,56]
+ bad mapping rule 1 x 1 num_rep 9 result [93,56]
+ bad mapping rule 1 x 1 num_rep 10 result [93,56]
+ $ rm -f "$TESTDIR/test-map-firstn-indep.crushmap"
diff --git a/src/test/cli/crushtool/test-map-firstn-indep.txt b/src/test/cli/crushtool/test-map-firstn-indep.txt
new file mode 100644
index 000000000..bac9e392d
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-firstn-indep.txt
@@ -0,0 +1,439 @@
+# begin crush map
+tunable choose_local_tries 0
+tunable choose_local_fallback_tries 0
+tunable choose_total_tries 50
+tunable chooseleaf_descend_once 1
+
+# devices
+device 0 device0
+device 1 device1
+device 2 device2
+device 3 device3
+device 4 device4
+device 5 device5
+device 6 device6
+device 7 device7
+device 8 device8
+device 9 device9
+device 10 device10
+device 11 device11
+device 12 device12
+device 13 device13
+device 14 device14
+device 15 device15
+device 16 device16
+device 17 device17
+device 18 device18
+device 19 device19
+device 20 device20
+device 21 device21
+device 22 device22
+device 23 device23
+device 24 device24
+device 25 device25
+device 26 device26
+device 27 device27
+device 28 device28
+device 29 device29
+device 30 device30
+device 31 device31
+device 32 device32
+device 33 device33
+device 34 device34
+device 35 device35
+device 36 device36
+device 37 device37
+device 38 device38
+device 39 device39
+device 40 device40
+device 41 device41
+device 42 device42
+device 43 device43
+device 44 device44
+device 45 device45
+device 46 device46
+device 47 device47
+device 48 device48
+device 49 device49
+device 50 device50
+device 51 device51
+device 52 device52
+device 53 device53
+device 54 device54
+device 55 device55
+device 56 device56
+device 57 device57
+device 58 device58
+device 59 device59
+device 60 device60
+device 61 device61
+device 62 device62
+device 63 device63
+device 64 device64
+device 65 device65
+device 66 device66
+device 67 device67
+device 68 device68
+device 69 device69
+device 70 device70
+device 71 device71
+device 72 device72
+device 73 device73
+device 74 device74
+device 75 device75
+device 76 device76
+device 77 device77
+device 78 device78
+device 79 device79
+device 80 device80
+device 81 device81
+device 82 device82
+device 83 device83
+device 84 device84
+device 85 device85
+device 86 device86
+device 87 device87
+device 88 device88
+device 89 device89
+device 90 device90
+device 91 device91
+device 92 device92
+device 93 device93
+device 94 device94
+device 95 device95
+device 96 device96
+device 97 device97
+device 98 device98
+device 99 device99
+
+# types
+type 0 device
+type 1 host
+type 2 rack
+type 3 default
+
+# buckets
+host host0 {
+ id -1 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device0 weight 1.00000
+ item device1 weight 1.00000
+ item device2 weight 1.00000
+ item device3 weight 1.00000
+}
+host host1 {
+ id -2 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device4 weight 1.00000
+ item device5 weight 1.00000
+ item device6 weight 1.00000
+ item device7 weight 1.00000
+}
+host host2 {
+ id -3 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device8 weight 1.00000
+ item device9 weight 1.00000
+ item device10 weight 1.00000
+ item device11 weight 1.00000
+}
+host host3 {
+ id -4 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device12 weight 1.00000
+ item device13 weight 1.00000
+ item device14 weight 1.00000
+ item device15 weight 1.00000
+}
+host host4 {
+ id -5 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device16 weight 1.00000
+ item device17 weight 1.00000
+ item device18 weight 1.00000
+ item device19 weight 1.00000
+}
+host host5 {
+ id -6 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device20 weight 1.00000
+ item device21 weight 1.00000
+ item device22 weight 1.00000
+ item device23 weight 1.00000
+}
+host host6 {
+ id -7 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device24 weight 1.00000
+ item device25 weight 1.00000
+ item device26 weight 1.00000
+ item device27 weight 1.00000
+}
+host host7 {
+ id -8 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device28 weight 1.00000
+ item device29 weight 1.00000
+ item device30 weight 1.00000
+ item device31 weight 1.00000
+}
+host host8 {
+ id -9 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device32 weight 1.00000
+ item device33 weight 1.00000
+ item device34 weight 1.00000
+ item device35 weight 1.00000
+}
+host host9 {
+ id -10 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device36 weight 1.00000
+ item device37 weight 1.00000
+ item device38 weight 1.00000
+ item device39 weight 1.00000
+}
+host host10 {
+ id -11 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device40 weight 1.00000
+ item device41 weight 1.00000
+ item device42 weight 1.00000
+ item device43 weight 1.00000
+}
+host host11 {
+ id -12 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device44 weight 1.00000
+ item device45 weight 1.00000
+ item device46 weight 1.00000
+ item device47 weight 1.00000
+}
+host host12 {
+ id -13 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device48 weight 1.00000
+ item device49 weight 1.00000
+ item device50 weight 1.00000
+ item device51 weight 1.00000
+}
+host host13 {
+ id -14 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device52 weight 1.00000
+ item device53 weight 1.00000
+ item device54 weight 1.00000
+ item device55 weight 1.00000
+}
+host host14 {
+ id -15 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device56 weight 1.00000
+ item device57 weight 1.00000
+ item device58 weight 1.00000
+ item device59 weight 1.00000
+}
+host host15 {
+ id -16 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device60 weight 1.00000
+ item device61 weight 1.00000
+ item device62 weight 1.00000
+ item device63 weight 1.00000
+}
+host host16 {
+ id -17 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device64 weight 1.00000
+ item device65 weight 1.00000
+ item device66 weight 1.00000
+ item device67 weight 1.00000
+}
+host host17 {
+ id -18 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device68 weight 1.00000
+ item device69 weight 1.00000
+ item device70 weight 1.00000
+ item device71 weight 1.00000
+}
+host host18 {
+ id -19 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device72 weight 1.00000
+ item device73 weight 1.00000
+ item device74 weight 1.00000
+ item device75 weight 1.00000
+}
+host host19 {
+ id -20 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device76 weight 1.00000
+ item device77 weight 1.00000
+ item device78 weight 1.00000
+ item device79 weight 1.00000
+}
+host host20 {
+ id -21 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device80 weight 1.00000
+ item device81 weight 1.00000
+ item device82 weight 1.00000
+ item device83 weight 1.00000
+}
+host host21 {
+ id -22 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device84 weight 1.00000
+ item device85 weight 1.00000
+ item device86 weight 1.00000
+ item device87 weight 1.00000
+}
+host host22 {
+ id -23 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device88 weight 1.00000
+ item device89 weight 1.00000
+ item device90 weight 1.00000
+ item device91 weight 1.00000
+}
+host host23 {
+ id -24 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device92 weight 1.00000
+ item device93 weight 1.00000
+ item device94 weight 1.00000
+ item device95 weight 1.00000
+}
+host host24 {
+ id -25 # do not change unnecessarily
+ # weight 4.00000
+ alg straw
+ hash 0 # rjenkins1
+ item device96 weight 1.00000
+ item device97 weight 1.00000
+ item device98 weight 1.00000
+ item device99 weight 1.00000
+}
+rack rack0 {
+ id -26 # do not change unnecessarily
+ # weight 40.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host0 weight 4.00000
+ item host1 weight 4.00000
+ item host2 weight 4.00000
+ item host3 weight 4.00000
+ item host4 weight 4.00000
+ item host5 weight 4.00000
+ item host6 weight 4.00000
+ item host7 weight 4.00000
+ item host8 weight 4.00000
+ item host9 weight 4.00000
+}
+rack rack1 {
+ id -27 # do not change unnecessarily
+ # weight 40.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host10 weight 4.00000
+ item host11 weight 4.00000
+ item host12 weight 4.00000
+ item host13 weight 4.00000
+ item host14 weight 4.00000
+ item host15 weight 4.00000
+ item host16 weight 4.00000
+ item host17 weight 4.00000
+ item host18 weight 4.00000
+ item host19 weight 4.00000
+}
+rack rack2 {
+ id -28 # do not change unnecessarily
+ # weight 20.00000
+ alg straw
+ hash 0 # rjenkins1
+ item host20 weight 4.00000
+ item host21 weight 4.00000
+ item host22 weight 4.00000
+ item host23 weight 4.00000
+ item host24 weight 4.00000
+}
+
+default root {
+ id -31 # do not change unnecessarily
+ # weight 100.00000
+ alg straw
+ hash 0 # rjenkins1
+ item rack1 weight 40.00000
+ item rack1 weight 40.00000
+ item rack2 weight 20.00000
+}
+
+# rules
+rule myrule {
+ id 0
+ type replicated
+ step take root
+ step choose firstn 2 type rack
+ step chooseleaf indep 4 type host
+ step emit
+}
+
+rule myrule1 {
+ id 1
+ type replicated
+ step take root
+ step choose firstn 2 type rack
+ step chooseleaf indep 1 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/crushtool/test-map-hammer-tunables.crushmap b/src/test/cli/crushtool/test-map-hammer-tunables.crushmap
new file mode 100644
index 000000000..34a518d34
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-hammer-tunables.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/test-map-hammer-tunables.t b/src/test/cli/crushtool/test-map-hammer-tunables.t
new file mode 100644
index 000000000..a5b720fe9
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-hammer-tunables.t
@@ -0,0 +1,10252 @@
+ $ crushtool -i "$TESTDIR/test-map-hammer-tunables.crushmap" --test --show-mappings --show-statistics --rule 0 --weight 12 0 --weight 20 0 --weight 30 0 --min-rep 1 --max-rep 10
+ rule 0 (data), x = 0..1023, numrep = 1..10
+ CRUSH rule 0 x 0 [101]
+ CRUSH rule 0 x 1 [80]
+ CRUSH rule 0 x 2 [91]
+ CRUSH rule 0 x 3 [51]
+ CRUSH rule 0 x 4 [50]
+ CRUSH rule 0 x 5 [89]
+ CRUSH rule 0 x 6 [91]
+ CRUSH rule 0 x 7 [104]
+ CRUSH rule 0 x 8 [78]
+ CRUSH rule 0 x 9 [101]
+ CRUSH rule 0 x 10 [61]
+ CRUSH rule 0 x 11 [13]
+ CRUSH rule 0 x 12 [83]
+ CRUSH rule 0 x 13 [108]
+ CRUSH rule 0 x 14 [105]
+ CRUSH rule 0 x 15 [18]
+ CRUSH rule 0 x 16 [103]
+ CRUSH rule 0 x 17 [85]
+ CRUSH rule 0 x 18 [11]
+ CRUSH rule 0 x 19 [75]
+ CRUSH rule 0 x 20 [79]
+ CRUSH rule 0 x 21 [84]
+ CRUSH rule 0 x 22 [23]
+ CRUSH rule 0 x 23 [118]
+ CRUSH rule 0 x 24 [83]
+ CRUSH rule 0 x 25 [81]
+ CRUSH rule 0 x 26 [38]
+ CRUSH rule 0 x 27 [76]
+ CRUSH rule 0 x 28 [76]
+ CRUSH rule 0 x 29 [8]
+ CRUSH rule 0 x 30 [94]
+ CRUSH rule 0 x 31 [76]
+ CRUSH rule 0 x 32 [72]
+ CRUSH rule 0 x 33 [77]
+ CRUSH rule 0 x 34 [74]
+ CRUSH rule 0 x 35 [22]
+ CRUSH rule 0 x 36 [104]
+ CRUSH rule 0 x 37 [38]
+ CRUSH rule 0 x 38 [72]
+ CRUSH rule 0 x 39 [68]
+ CRUSH rule 0 x 40 [103]
+ CRUSH rule 0 x 41 [85]
+ CRUSH rule 0 x 42 [106]
+ CRUSH rule 0 x 43 [10]
+ CRUSH rule 0 x 44 [101]
+ CRUSH rule 0 x 45 [8]
+ CRUSH rule 0 x 46 [65]
+ CRUSH rule 0 x 47 [106]
+ CRUSH rule 0 x 48 [34]
+ CRUSH rule 0 x 49 [0]
+ CRUSH rule 0 x 50 [42]
+ CRUSH rule 0 x 51 [104]
+ CRUSH rule 0 x 52 [83]
+ CRUSH rule 0 x 53 [32]
+ CRUSH rule 0 x 54 [28]
+ CRUSH rule 0 x 55 [14]
+ CRUSH rule 0 x 56 [21]
+ CRUSH rule 0 x 57 [93]
+ CRUSH rule 0 x 58 [45]
+ CRUSH rule 0 x 59 [80]
+ CRUSH rule 0 x 60 [90]
+ CRUSH rule 0 x 61 [88]
+ CRUSH rule 0 x 62 [81]
+ CRUSH rule 0 x 63 [79]
+ CRUSH rule 0 x 64 [1]
+ CRUSH rule 0 x 65 [13]
+ CRUSH rule 0 x 66 [48]
+ CRUSH rule 0 x 67 [94]
+ CRUSH rule 0 x 68 [102]
+ CRUSH rule 0 x 69 [62]
+ CRUSH rule 0 x 70 [84]
+ CRUSH rule 0 x 71 [55]
+ CRUSH rule 0 x 72 [97]
+ CRUSH rule 0 x 73 [64]
+ CRUSH rule 0 x 74 [96]
+ CRUSH rule 0 x 75 [29]
+ CRUSH rule 0 x 76 [55]
+ CRUSH rule 0 x 77 [107]
+ CRUSH rule 0 x 78 [31]
+ CRUSH rule 0 x 79 [64]
+ CRUSH rule 0 x 80 [0]
+ CRUSH rule 0 x 81 [71]
+ CRUSH rule 0 x 82 [37]
+ CRUSH rule 0 x 83 [92]
+ CRUSH rule 0 x 84 [49]
+ CRUSH rule 0 x 85 [54]
+ CRUSH rule 0 x 86 [37]
+ CRUSH rule 0 x 87 [116]
+ CRUSH rule 0 x 88 [38]
+ CRUSH rule 0 x 89 [76]
+ CRUSH rule 0 x 90 [14]
+ CRUSH rule 0 x 91 [93]
+ CRUSH rule 0 x 92 [86]
+ CRUSH rule 0 x 93 [44]
+ CRUSH rule 0 x 94 [61]
+ CRUSH rule 0 x 95 [93]
+ CRUSH rule 0 x 96 [66]
+ CRUSH rule 0 x 97 [111]
+ CRUSH rule 0 x 98 [66]
+ CRUSH rule 0 x 99 [78]
+ CRUSH rule 0 x 100 [28]
+ CRUSH rule 0 x 101 [84]
+ CRUSH rule 0 x 102 [82]
+ CRUSH rule 0 x 103 [66]
+ CRUSH rule 0 x 104 [14]
+ CRUSH rule 0 x 105 [87]
+ CRUSH rule 0 x 106 [69]
+ CRUSH rule 0 x 107 [1]
+ CRUSH rule 0 x 108 [94]
+ CRUSH rule 0 x 109 [112]
+ CRUSH rule 0 x 110 [54]
+ CRUSH rule 0 x 111 [10]
+ CRUSH rule 0 x 112 [89]
+ CRUSH rule 0 x 113 [69]
+ CRUSH rule 0 x 114 [79]
+ CRUSH rule 0 x 115 [50]
+ CRUSH rule 0 x 116 [96]
+ CRUSH rule 0 x 117 [87]
+ CRUSH rule 0 x 118 [23]
+ CRUSH rule 0 x 119 [104]
+ CRUSH rule 0 x 120 [57]
+ CRUSH rule 0 x 121 [105]
+ CRUSH rule 0 x 122 [45]
+ CRUSH rule 0 x 123 [112]
+ CRUSH rule 0 x 124 [110]
+ CRUSH rule 0 x 125 [66]
+ CRUSH rule 0 x 126 [51]
+ CRUSH rule 0 x 127 [70]
+ CRUSH rule 0 x 128 [90]
+ CRUSH rule 0 x 129 [103]
+ CRUSH rule 0 x 130 [50]
+ CRUSH rule 0 x 131 [23]
+ CRUSH rule 0 x 132 [69]
+ CRUSH rule 0 x 133 [52]
+ CRUSH rule 0 x 134 [78]
+ CRUSH rule 0 x 135 [78]
+ CRUSH rule 0 x 136 [32]
+ CRUSH rule 0 x 137 [92]
+ CRUSH rule 0 x 138 [17]
+ CRUSH rule 0 x 139 [89]
+ CRUSH rule 0 x 140 [39]
+ CRUSH rule 0 x 141 [89]
+ CRUSH rule 0 x 142 [70]
+ CRUSH rule 0 x 143 [51]
+ CRUSH rule 0 x 144 [13]
+ CRUSH rule 0 x 145 [77]
+ CRUSH rule 0 x 146 [96]
+ CRUSH rule 0 x 147 [2]
+ CRUSH rule 0 x 148 [74]
+ CRUSH rule 0 x 149 [76]
+ CRUSH rule 0 x 150 [38]
+ CRUSH rule 0 x 151 [90]
+ CRUSH rule 0 x 152 [49]
+ CRUSH rule 0 x 153 [71]
+ CRUSH rule 0 x 154 [94]
+ CRUSH rule 0 x 155 [75]
+ CRUSH rule 0 x 156 [107]
+ CRUSH rule 0 x 157 [112]
+ CRUSH rule 0 x 158 [26]
+ CRUSH rule 0 x 159 [52]
+ CRUSH rule 0 x 160 [41]
+ CRUSH rule 0 x 161 [84]
+ CRUSH rule 0 x 162 [55]
+ CRUSH rule 0 x 163 [54]
+ CRUSH rule 0 x 164 [45]
+ CRUSH rule 0 x 165 [25]
+ CRUSH rule 0 x 166 [73]
+ CRUSH rule 0 x 167 [89]
+ CRUSH rule 0 x 168 [47]
+ CRUSH rule 0 x 169 [51]
+ CRUSH rule 0 x 170 [68]
+ CRUSH rule 0 x 171 [73]
+ CRUSH rule 0 x 172 [117]
+ CRUSH rule 0 x 173 [13]
+ CRUSH rule 0 x 174 [116]
+ CRUSH rule 0 x 175 [3]
+ CRUSH rule 0 x 176 [94]
+ CRUSH rule 0 x 177 [52]
+ CRUSH rule 0 x 178 [39]
+ CRUSH rule 0 x 179 [72]
+ CRUSH rule 0 x 180 [60]
+ CRUSH rule 0 x 181 [18]
+ CRUSH rule 0 x 182 [22]
+ CRUSH rule 0 x 183 [11]
+ CRUSH rule 0 x 184 [92]
+ CRUSH rule 0 x 185 [97]
+ CRUSH rule 0 x 186 [67]
+ CRUSH rule 0 x 187 [116]
+ CRUSH rule 0 x 188 [69]
+ CRUSH rule 0 x 189 [47]
+ CRUSH rule 0 x 190 [65]
+ CRUSH rule 0 x 191 [49]
+ CRUSH rule 0 x 192 [68]
+ CRUSH rule 0 x 193 [0]
+ CRUSH rule 0 x 194 [62]
+ CRUSH rule 0 x 195 [119]
+ CRUSH rule 0 x 196 [72]
+ CRUSH rule 0 x 197 [106]
+ CRUSH rule 0 x 198 [114]
+ CRUSH rule 0 x 199 [0]
+ CRUSH rule 0 x 200 [35]
+ CRUSH rule 0 x 201 [27]
+ CRUSH rule 0 x 202 [98]
+ CRUSH rule 0 x 203 [36]
+ CRUSH rule 0 x 204 [10]
+ CRUSH rule 0 x 205 [81]
+ CRUSH rule 0 x 206 [49]
+ CRUSH rule 0 x 207 [80]
+ CRUSH rule 0 x 208 [63]
+ CRUSH rule 0 x 209 [85]
+ CRUSH rule 0 x 210 [79]
+ CRUSH rule 0 x 211 [26]
+ CRUSH rule 0 x 212 [28]
+ CRUSH rule 0 x 213 [91]
+ CRUSH rule 0 x 214 [91]
+ CRUSH rule 0 x 215 [61]
+ CRUSH rule 0 x 216 [99]
+ CRUSH rule 0 x 217 [86]
+ CRUSH rule 0 x 218 [70]
+ CRUSH rule 0 x 219 [28]
+ CRUSH rule 0 x 220 [56]
+ CRUSH rule 0 x 221 [0]
+ CRUSH rule 0 x 222 [50]
+ CRUSH rule 0 x 223 [29]
+ CRUSH rule 0 x 224 [52]
+ CRUSH rule 0 x 225 [15]
+ CRUSH rule 0 x 226 [44]
+ CRUSH rule 0 x 227 [42]
+ CRUSH rule 0 x 228 [117]
+ CRUSH rule 0 x 229 [100]
+ CRUSH rule 0 x 230 [41]
+ CRUSH rule 0 x 231 [56]
+ CRUSH rule 0 x 232 [23]
+ CRUSH rule 0 x 233 [88]
+ CRUSH rule 0 x 234 [4]
+ CRUSH rule 0 x 235 [26]
+ CRUSH rule 0 x 236 [32]
+ CRUSH rule 0 x 237 [92]
+ CRUSH rule 0 x 238 [10]
+ CRUSH rule 0 x 239 [15]
+ CRUSH rule 0 x 240 [109]
+ CRUSH rule 0 x 241 [47]
+ CRUSH rule 0 x 242 [24]
+ CRUSH rule 0 x 243 [76]
+ CRUSH rule 0 x 244 [103]
+ CRUSH rule 0 x 245 [27]
+ CRUSH rule 0 x 246 [5]
+ CRUSH rule 0 x 247 [99]
+ CRUSH rule 0 x 248 [8]
+ CRUSH rule 0 x 249 [85]
+ CRUSH rule 0 x 250 [93]
+ CRUSH rule 0 x 251 [28]
+ CRUSH rule 0 x 252 [95]
+ CRUSH rule 0 x 253 [109]
+ CRUSH rule 0 x 254 [80]
+ CRUSH rule 0 x 255 [103]
+ CRUSH rule 0 x 256 [37]
+ CRUSH rule 0 x 257 [69]
+ CRUSH rule 0 x 258 [34]
+ CRUSH rule 0 x 259 [70]
+ CRUSH rule 0 x 260 [98]
+ CRUSH rule 0 x 261 [94]
+ CRUSH rule 0 x 262 [42]
+ CRUSH rule 0 x 263 [65]
+ CRUSH rule 0 x 264 [36]
+ CRUSH rule 0 x 265 [66]
+ CRUSH rule 0 x 266 [75]
+ CRUSH rule 0 x 267 [58]
+ CRUSH rule 0 x 268 [38]
+ CRUSH rule 0 x 269 [86]
+ CRUSH rule 0 x 270 [58]
+ CRUSH rule 0 x 271 [19]
+ CRUSH rule 0 x 272 [73]
+ CRUSH rule 0 x 273 [108]
+ CRUSH rule 0 x 274 [47]
+ CRUSH rule 0 x 275 [92]
+ CRUSH rule 0 x 276 [7]
+ CRUSH rule 0 x 277 [19]
+ CRUSH rule 0 x 278 [116]
+ CRUSH rule 0 x 279 [101]
+ CRUSH rule 0 x 280 [113]
+ CRUSH rule 0 x 281 [14]
+ CRUSH rule 0 x 282 [106]
+ CRUSH rule 0 x 283 [8]
+ CRUSH rule 0 x 284 [10]
+ CRUSH rule 0 x 285 [88]
+ CRUSH rule 0 x 286 [27]
+ CRUSH rule 0 x 287 [84]
+ CRUSH rule 0 x 288 [103]
+ CRUSH rule 0 x 289 [9]
+ CRUSH rule 0 x 290 [115]
+ CRUSH rule 0 x 291 [48]
+ CRUSH rule 0 x 292 [89]
+ CRUSH rule 0 x 293 [27]
+ CRUSH rule 0 x 294 [79]
+ CRUSH rule 0 x 295 [37]
+ CRUSH rule 0 x 296 [56]
+ CRUSH rule 0 x 297 [35]
+ CRUSH rule 0 x 298 [71]
+ CRUSH rule 0 x 299 [116]
+ CRUSH rule 0 x 300 [67]
+ CRUSH rule 0 x 301 [51]
+ CRUSH rule 0 x 302 [78]
+ CRUSH rule 0 x 303 [19]
+ CRUSH rule 0 x 304 [101]
+ CRUSH rule 0 x 305 [81]
+ CRUSH rule 0 x 306 [0]
+ CRUSH rule 0 x 307 [44]
+ CRUSH rule 0 x 308 [91]
+ CRUSH rule 0 x 309 [38]
+ CRUSH rule 0 x 310 [26]
+ CRUSH rule 0 x 311 [36]
+ CRUSH rule 0 x 312 [33]
+ CRUSH rule 0 x 313 [104]
+ CRUSH rule 0 x 314 [28]
+ CRUSH rule 0 x 315 [16]
+ CRUSH rule 0 x 316 [4]
+ CRUSH rule 0 x 317 [118]
+ CRUSH rule 0 x 318 [17]
+ CRUSH rule 0 x 319 [24]
+ CRUSH rule 0 x 320 [36]
+ CRUSH rule 0 x 321 [26]
+ CRUSH rule 0 x 322 [87]
+ CRUSH rule 0 x 323 [73]
+ CRUSH rule 0 x 324 [21]
+ CRUSH rule 0 x 325 [52]
+ CRUSH rule 0 x 326 [111]
+ CRUSH rule 0 x 327 [62]
+ CRUSH rule 0 x 328 [7]
+ CRUSH rule 0 x 329 [93]
+ CRUSH rule 0 x 330 [24]
+ CRUSH rule 0 x 331 [41]
+ CRUSH rule 0 x 332 [61]
+ CRUSH rule 0 x 333 [16]
+ CRUSH rule 0 x 334 [3]
+ CRUSH rule 0 x 335 [71]
+ CRUSH rule 0 x 336 [16]
+ CRUSH rule 0 x 337 [37]
+ CRUSH rule 0 x 338 [109]
+ CRUSH rule 0 x 339 [37]
+ CRUSH rule 0 x 340 [119]
+ CRUSH rule 0 x 341 [63]
+ CRUSH rule 0 x 342 [92]
+ CRUSH rule 0 x 343 [49]
+ CRUSH rule 0 x 344 [103]
+ CRUSH rule 0 x 345 [56]
+ CRUSH rule 0 x 346 [3]
+ CRUSH rule 0 x 347 [106]
+ CRUSH rule 0 x 348 [10]
+ CRUSH rule 0 x 349 [96]
+ CRUSH rule 0 x 350 [63]
+ CRUSH rule 0 x 351 [60]
+ CRUSH rule 0 x 352 [103]
+ CRUSH rule 0 x 353 [49]
+ CRUSH rule 0 x 354 [55]
+ CRUSH rule 0 x 355 [73]
+ CRUSH rule 0 x 356 [114]
+ CRUSH rule 0 x 357 [14]
+ CRUSH rule 0 x 358 [97]
+ CRUSH rule 0 x 359 [4]
+ CRUSH rule 0 x 360 [106]
+ CRUSH rule 0 x 361 [27]
+ CRUSH rule 0 x 362 [28]
+ CRUSH rule 0 x 363 [45]
+ CRUSH rule 0 x 364 [23]
+ CRUSH rule 0 x 365 [24]
+ CRUSH rule 0 x 366 [14]
+ CRUSH rule 0 x 367 [103]
+ CRUSH rule 0 x 368 [103]
+ CRUSH rule 0 x 369 [37]
+ CRUSH rule 0 x 370 [11]
+ CRUSH rule 0 x 371 [34]
+ CRUSH rule 0 x 372 [58]
+ CRUSH rule 0 x 373 [98]
+ CRUSH rule 0 x 374 [110]
+ CRUSH rule 0 x 375 [19]
+ CRUSH rule 0 x 376 [22]
+ CRUSH rule 0 x 377 [98]
+ CRUSH rule 0 x 378 [67]
+ CRUSH rule 0 x 379 [77]
+ CRUSH rule 0 x 380 [69]
+ CRUSH rule 0 x 381 [55]
+ CRUSH rule 0 x 382 [26]
+ CRUSH rule 0 x 383 [48]
+ CRUSH rule 0 x 384 [15]
+ CRUSH rule 0 x 385 [82]
+ CRUSH rule 0 x 386 [108]
+ CRUSH rule 0 x 387 [70]
+ CRUSH rule 0 x 388 [5]
+ CRUSH rule 0 x 389 [14]
+ CRUSH rule 0 x 390 [68]
+ CRUSH rule 0 x 391 [113]
+ CRUSH rule 0 x 392 [72]
+ CRUSH rule 0 x 393 [115]
+ CRUSH rule 0 x 394 [38]
+ CRUSH rule 0 x 395 [0]
+ CRUSH rule 0 x 396 [59]
+ CRUSH rule 0 x 397 [87]
+ CRUSH rule 0 x 398 [44]
+ CRUSH rule 0 x 399 [9]
+ CRUSH rule 0 x 400 [101]
+ CRUSH rule 0 x 401 [79]
+ CRUSH rule 0 x 402 [107]
+ CRUSH rule 0 x 403 [23]
+ CRUSH rule 0 x 404 [76]
+ CRUSH rule 0 x 405 [10]
+ CRUSH rule 0 x 406 [38]
+ CRUSH rule 0 x 407 [70]
+ CRUSH rule 0 x 408 [55]
+ CRUSH rule 0 x 409 [102]
+ CRUSH rule 0 x 410 [59]
+ CRUSH rule 0 x 411 [34]
+ CRUSH rule 0 x 412 [108]
+ CRUSH rule 0 x 413 [54]
+ CRUSH rule 0 x 414 [70]
+ CRUSH rule 0 x 415 [107]
+ CRUSH rule 0 x 416 [79]
+ CRUSH rule 0 x 417 [8]
+ CRUSH rule 0 x 418 [51]
+ CRUSH rule 0 x 419 [117]
+ CRUSH rule 0 x 420 [109]
+ CRUSH rule 0 x 421 [114]
+ CRUSH rule 0 x 422 [109]
+ CRUSH rule 0 x 423 [59]
+ CRUSH rule 0 x 424 [71]
+ CRUSH rule 0 x 425 [101]
+ CRUSH rule 0 x 426 [47]
+ CRUSH rule 0 x 427 [86]
+ CRUSH rule 0 x 428 [68]
+ CRUSH rule 0 x 429 [76]
+ CRUSH rule 0 x 430 [9]
+ CRUSH rule 0 x 431 [105]
+ CRUSH rule 0 x 432 [46]
+ CRUSH rule 0 x 433 [6]
+ CRUSH rule 0 x 434 [64]
+ CRUSH rule 0 x 435 [16]
+ CRUSH rule 0 x 436 [89]
+ CRUSH rule 0 x 437 [29]
+ CRUSH rule 0 x 438 [105]
+ CRUSH rule 0 x 439 [29]
+ CRUSH rule 0 x 440 [38]
+ CRUSH rule 0 x 441 [112]
+ CRUSH rule 0 x 442 [55]
+ CRUSH rule 0 x 443 [44]
+ CRUSH rule 0 x 444 [11]
+ CRUSH rule 0 x 445 [19]
+ CRUSH rule 0 x 446 [40]
+ CRUSH rule 0 x 447 [100]
+ CRUSH rule 0 x 448 [7]
+ CRUSH rule 0 x 449 [67]
+ CRUSH rule 0 x 450 [117]
+ CRUSH rule 0 x 451 [93]
+ CRUSH rule 0 x 452 [70]
+ CRUSH rule 0 x 453 [82]
+ CRUSH rule 0 x 454 [53]
+ CRUSH rule 0 x 455 [91]
+ CRUSH rule 0 x 456 [17]
+ CRUSH rule 0 x 457 [113]
+ CRUSH rule 0 x 458 [119]
+ CRUSH rule 0 x 459 [25]
+ CRUSH rule 0 x 460 [11]
+ CRUSH rule 0 x 461 [21]
+ CRUSH rule 0 x 462 [25]
+ CRUSH rule 0 x 463 [6]
+ CRUSH rule 0 x 464 [19]
+ CRUSH rule 0 x 465 [29]
+ CRUSH rule 0 x 466 [66]
+ CRUSH rule 0 x 467 [27]
+ CRUSH rule 0 x 468 [97]
+ CRUSH rule 0 x 469 [98]
+ CRUSH rule 0 x 470 [50]
+ CRUSH rule 0 x 471 [40]
+ CRUSH rule 0 x 472 [74]
+ CRUSH rule 0 x 473 [95]
+ CRUSH rule 0 x 474 [51]
+ CRUSH rule 0 x 475 [3]
+ CRUSH rule 0 x 476 [110]
+ CRUSH rule 0 x 477 [25]
+ CRUSH rule 0 x 478 [19]
+ CRUSH rule 0 x 479 [70]
+ CRUSH rule 0 x 480 [62]
+ CRUSH rule 0 x 481 [26]
+ CRUSH rule 0 x 482 [84]
+ CRUSH rule 0 x 483 [36]
+ CRUSH rule 0 x 484 [37]
+ CRUSH rule 0 x 485 [84]
+ CRUSH rule 0 x 486 [92]
+ CRUSH rule 0 x 487 [106]
+ CRUSH rule 0 x 488 [42]
+ CRUSH rule 0 x 489 [76]
+ CRUSH rule 0 x 490 [68]
+ CRUSH rule 0 x 491 [80]
+ CRUSH rule 0 x 492 [21]
+ CRUSH rule 0 x 493 [99]
+ CRUSH rule 0 x 494 [4]
+ CRUSH rule 0 x 495 [40]
+ CRUSH rule 0 x 496 [13]
+ CRUSH rule 0 x 497 [102]
+ CRUSH rule 0 x 498 [68]
+ CRUSH rule 0 x 499 [22]
+ CRUSH rule 0 x 500 [50]
+ CRUSH rule 0 x 501 [60]
+ CRUSH rule 0 x 502 [11]
+ CRUSH rule 0 x 503 [117]
+ CRUSH rule 0 x 504 [90]
+ CRUSH rule 0 x 505 [91]
+ CRUSH rule 0 x 506 [82]
+ CRUSH rule 0 x 507 [6]
+ CRUSH rule 0 x 508 [34]
+ CRUSH rule 0 x 509 [88]
+ CRUSH rule 0 x 510 [11]
+ CRUSH rule 0 x 511 [72]
+ CRUSH rule 0 x 512 [118]
+ CRUSH rule 0 x 513 [22]
+ CRUSH rule 0 x 514 [82]
+ CRUSH rule 0 x 515 [27]
+ CRUSH rule 0 x 516 [66]
+ CRUSH rule 0 x 517 [83]
+ CRUSH rule 0 x 518 [18]
+ CRUSH rule 0 x 519 [67]
+ CRUSH rule 0 x 520 [15]
+ CRUSH rule 0 x 521 [63]
+ CRUSH rule 0 x 522 [4]
+ CRUSH rule 0 x 523 [36]
+ CRUSH rule 0 x 524 [33]
+ CRUSH rule 0 x 525 [63]
+ CRUSH rule 0 x 526 [83]
+ CRUSH rule 0 x 527 [37]
+ CRUSH rule 0 x 528 [108]
+ CRUSH rule 0 x 529 [107]
+ CRUSH rule 0 x 530 [49]
+ CRUSH rule 0 x 531 [27]
+ CRUSH rule 0 x 532 [68]
+ CRUSH rule 0 x 533 [5]
+ CRUSH rule 0 x 534 [97]
+ CRUSH rule 0 x 535 [48]
+ CRUSH rule 0 x 536 [3]
+ CRUSH rule 0 x 537 [116]
+ CRUSH rule 0 x 538 [85]
+ CRUSH rule 0 x 539 [10]
+ CRUSH rule 0 x 540 [100]
+ CRUSH rule 0 x 541 [111]
+ CRUSH rule 0 x 542 [50]
+ CRUSH rule 0 x 543 [45]
+ CRUSH rule 0 x 544 [106]
+ CRUSH rule 0 x 545 [43]
+ CRUSH rule 0 x 546 [108]
+ CRUSH rule 0 x 547 [27]
+ CRUSH rule 0 x 548 [53]
+ CRUSH rule 0 x 549 [60]
+ CRUSH rule 0 x 550 [47]
+ CRUSH rule 0 x 551 [14]
+ CRUSH rule 0 x 552 [70]
+ CRUSH rule 0 x 553 [96]
+ CRUSH rule 0 x 554 [61]
+ CRUSH rule 0 x 555 [76]
+ CRUSH rule 0 x 556 [106]
+ CRUSH rule 0 x 557 [39]
+ CRUSH rule 0 x 558 [70]
+ CRUSH rule 0 x 559 [106]
+ CRUSH rule 0 x 560 [94]
+ CRUSH rule 0 x 561 [27]
+ CRUSH rule 0 x 562 [97]
+ CRUSH rule 0 x 563 [64]
+ CRUSH rule 0 x 564 [96]
+ CRUSH rule 0 x 565 [66]
+ CRUSH rule 0 x 566 [27]
+ CRUSH rule 0 x 567 [88]
+ CRUSH rule 0 x 568 [17]
+ CRUSH rule 0 x 569 [102]
+ CRUSH rule 0 x 570 [7]
+ CRUSH rule 0 x 571 [95]
+ CRUSH rule 0 x 572 [62]
+ CRUSH rule 0 x 573 [51]
+ CRUSH rule 0 x 574 [89]
+ CRUSH rule 0 x 575 [19]
+ CRUSH rule 0 x 576 [112]
+ CRUSH rule 0 x 577 [8]
+ CRUSH rule 0 x 578 [64]
+ CRUSH rule 0 x 579 [78]
+ CRUSH rule 0 x 580 [68]
+ CRUSH rule 0 x 581 [55]
+ CRUSH rule 0 x 582 [27]
+ CRUSH rule 0 x 583 [74]
+ CRUSH rule 0 x 584 [72]
+ CRUSH rule 0 x 585 [88]
+ CRUSH rule 0 x 586 [33]
+ CRUSH rule 0 x 587 [106]
+ CRUSH rule 0 x 588 [0]
+ CRUSH rule 0 x 589 [7]
+ CRUSH rule 0 x 590 [59]
+ CRUSH rule 0 x 591 [42]
+ CRUSH rule 0 x 592 [45]
+ CRUSH rule 0 x 593 [89]
+ CRUSH rule 0 x 594 [27]
+ CRUSH rule 0 x 595 [7]
+ CRUSH rule 0 x 596 [82]
+ CRUSH rule 0 x 597 [72]
+ CRUSH rule 0 x 598 [34]
+ CRUSH rule 0 x 599 [119]
+ CRUSH rule 0 x 600 [9]
+ CRUSH rule 0 x 601 [104]
+ CRUSH rule 0 x 602 [48]
+ CRUSH rule 0 x 603 [24]
+ CRUSH rule 0 x 604 [89]
+ CRUSH rule 0 x 605 [104]
+ CRUSH rule 0 x 606 [49]
+ CRUSH rule 0 x 607 [95]
+ CRUSH rule 0 x 608 [49]
+ CRUSH rule 0 x 609 [61]
+ CRUSH rule 0 x 610 [106]
+ CRUSH rule 0 x 611 [66]
+ CRUSH rule 0 x 612 [103]
+ CRUSH rule 0 x 613 [84]
+ CRUSH rule 0 x 614 [81]
+ CRUSH rule 0 x 615 [61]
+ CRUSH rule 0 x 616 [41]
+ CRUSH rule 0 x 617 [111]
+ CRUSH rule 0 x 618 [3]
+ CRUSH rule 0 x 619 [92]
+ CRUSH rule 0 x 620 [108]
+ CRUSH rule 0 x 621 [106]
+ CRUSH rule 0 x 622 [67]
+ CRUSH rule 0 x 623 [94]
+ CRUSH rule 0 x 624 [115]
+ CRUSH rule 0 x 625 [111]
+ CRUSH rule 0 x 626 [3]
+ CRUSH rule 0 x 627 [19]
+ CRUSH rule 0 x 628 [65]
+ CRUSH rule 0 x 629 [119]
+ CRUSH rule 0 x 630 [109]
+ CRUSH rule 0 x 631 [48]
+ CRUSH rule 0 x 632 [81]
+ CRUSH rule 0 x 633 [65]
+ CRUSH rule 0 x 634 [87]
+ CRUSH rule 0 x 635 [107]
+ CRUSH rule 0 x 636 [23]
+ CRUSH rule 0 x 637 [102]
+ CRUSH rule 0 x 638 [43]
+ CRUSH rule 0 x 639 [31]
+ CRUSH rule 0 x 640 [113]
+ CRUSH rule 0 x 641 [45]
+ CRUSH rule 0 x 642 [47]
+ CRUSH rule 0 x 643 [64]
+ CRUSH rule 0 x 644 [31]
+ CRUSH rule 0 x 645 [76]
+ CRUSH rule 0 x 646 [37]
+ CRUSH rule 0 x 647 [58]
+ CRUSH rule 0 x 648 [31]
+ CRUSH rule 0 x 649 [88]
+ CRUSH rule 0 x 650 [116]
+ CRUSH rule 0 x 651 [97]
+ CRUSH rule 0 x 652 [57]
+ CRUSH rule 0 x 653 [38]
+ CRUSH rule 0 x 654 [49]
+ CRUSH rule 0 x 655 [89]
+ CRUSH rule 0 x 656 [0]
+ CRUSH rule 0 x 657 [47]
+ CRUSH rule 0 x 658 [75]
+ CRUSH rule 0 x 659 [26]
+ CRUSH rule 0 x 660 [65]
+ CRUSH rule 0 x 661 [91]
+ CRUSH rule 0 x 662 [111]
+ CRUSH rule 0 x 663 [88]
+ CRUSH rule 0 x 664 [59]
+ CRUSH rule 0 x 665 [78]
+ CRUSH rule 0 x 666 [112]
+ CRUSH rule 0 x 667 [97]
+ CRUSH rule 0 x 668 [97]
+ CRUSH rule 0 x 669 [85]
+ CRUSH rule 0 x 670 [41]
+ CRUSH rule 0 x 671 [116]
+ CRUSH rule 0 x 672 [44]
+ CRUSH rule 0 x 673 [83]
+ CRUSH rule 0 x 674 [59]
+ CRUSH rule 0 x 675 [88]
+ CRUSH rule 0 x 676 [62]
+ CRUSH rule 0 x 677 [88]
+ CRUSH rule 0 x 678 [98]
+ CRUSH rule 0 x 679 [70]
+ CRUSH rule 0 x 680 [55]
+ CRUSH rule 0 x 681 [53]
+ CRUSH rule 0 x 682 [27]
+ CRUSH rule 0 x 683 [57]
+ CRUSH rule 0 x 684 [98]
+ CRUSH rule 0 x 685 [106]
+ CRUSH rule 0 x 686 [86]
+ CRUSH rule 0 x 687 [49]
+ CRUSH rule 0 x 688 [16]
+ CRUSH rule 0 x 689 [6]
+ CRUSH rule 0 x 690 [43]
+ CRUSH rule 0 x 691 [34]
+ CRUSH rule 0 x 692 [40]
+ CRUSH rule 0 x 693 [29]
+ CRUSH rule 0 x 694 [6]
+ CRUSH rule 0 x 695 [31]
+ CRUSH rule 0 x 696 [36]
+ CRUSH rule 0 x 697 [96]
+ CRUSH rule 0 x 698 [61]
+ CRUSH rule 0 x 699 [47]
+ CRUSH rule 0 x 700 [0]
+ CRUSH rule 0 x 701 [42]
+ CRUSH rule 0 x 702 [0]
+ CRUSH rule 0 x 703 [92]
+ CRUSH rule 0 x 704 [10]
+ CRUSH rule 0 x 705 [105]
+ CRUSH rule 0 x 706 [74]
+ CRUSH rule 0 x 707 [0]
+ CRUSH rule 0 x 708 [84]
+ CRUSH rule 0 x 709 [114]
+ CRUSH rule 0 x 710 [94]
+ CRUSH rule 0 x 711 [68]
+ CRUSH rule 0 x 712 [34]
+ CRUSH rule 0 x 713 [29]
+ CRUSH rule 0 x 714 [81]
+ CRUSH rule 0 x 715 [71]
+ CRUSH rule 0 x 716 [40]
+ CRUSH rule 0 x 717 [61]
+ CRUSH rule 0 x 718 [40]
+ CRUSH rule 0 x 719 [59]
+ CRUSH rule 0 x 720 [69]
+ CRUSH rule 0 x 721 [62]
+ CRUSH rule 0 x 722 [115]
+ CRUSH rule 0 x 723 [117]
+ CRUSH rule 0 x 724 [45]
+ CRUSH rule 0 x 725 [53]
+ CRUSH rule 0 x 726 [84]
+ CRUSH rule 0 x 727 [109]
+ CRUSH rule 0 x 728 [76]
+ CRUSH rule 0 x 729 [108]
+ CRUSH rule 0 x 730 [28]
+ CRUSH rule 0 x 731 [78]
+ CRUSH rule 0 x 732 [55]
+ CRUSH rule 0 x 733 [84]
+ CRUSH rule 0 x 734 [27]
+ CRUSH rule 0 x 735 [83]
+ CRUSH rule 0 x 736 [70]
+ CRUSH rule 0 x 737 [117]
+ CRUSH rule 0 x 738 [118]
+ CRUSH rule 0 x 739 [87]
+ CRUSH rule 0 x 740 [29]
+ CRUSH rule 0 x 741 [96]
+ CRUSH rule 0 x 742 [106]
+ CRUSH rule 0 x 743 [105]
+ CRUSH rule 0 x 744 [23]
+ CRUSH rule 0 x 745 [28]
+ CRUSH rule 0 x 746 [18]
+ CRUSH rule 0 x 747 [65]
+ CRUSH rule 0 x 748 [48]
+ CRUSH rule 0 x 749 [102]
+ CRUSH rule 0 x 750 [50]
+ CRUSH rule 0 x 751 [36]
+ CRUSH rule 0 x 752 [69]
+ CRUSH rule 0 x 753 [9]
+ CRUSH rule 0 x 754 [9]
+ CRUSH rule 0 x 755 [98]
+ CRUSH rule 0 x 756 [113]
+ CRUSH rule 0 x 757 [47]
+ CRUSH rule 0 x 758 [57]
+ CRUSH rule 0 x 759 [74]
+ CRUSH rule 0 x 760 [53]
+ CRUSH rule 0 x 761 [78]
+ CRUSH rule 0 x 762 [87]
+ CRUSH rule 0 x 763 [13]
+ CRUSH rule 0 x 764 [106]
+ CRUSH rule 0 x 765 [109]
+ CRUSH rule 0 x 766 [76]
+ CRUSH rule 0 x 767 [41]
+ CRUSH rule 0 x 768 [13]
+ CRUSH rule 0 x 769 [91]
+ CRUSH rule 0 x 770 [105]
+ CRUSH rule 0 x 771 [10]
+ CRUSH rule 0 x 772 [8]
+ CRUSH rule 0 x 773 [116]
+ CRUSH rule 0 x 774 [100]
+ CRUSH rule 0 x 775 [15]
+ CRUSH rule 0 x 776 [69]
+ CRUSH rule 0 x 777 [76]
+ CRUSH rule 0 x 778 [38]
+ CRUSH rule 0 x 779 [46]
+ CRUSH rule 0 x 780 [63]
+ CRUSH rule 0 x 781 [19]
+ CRUSH rule 0 x 782 [117]
+ CRUSH rule 0 x 783 [60]
+ CRUSH rule 0 x 784 [82]
+ CRUSH rule 0 x 785 [27]
+ CRUSH rule 0 x 786 [41]
+ CRUSH rule 0 x 787 [13]
+ CRUSH rule 0 x 788 [4]
+ CRUSH rule 0 x 789 [50]
+ CRUSH rule 0 x 790 [58]
+ CRUSH rule 0 x 791 [96]
+ CRUSH rule 0 x 792 [45]
+ CRUSH rule 0 x 793 [6]
+ CRUSH rule 0 x 794 [14]
+ CRUSH rule 0 x 795 [51]
+ CRUSH rule 0 x 796 [114]
+ CRUSH rule 0 x 797 [79]
+ CRUSH rule 0 x 798 [42]
+ CRUSH rule 0 x 799 [48]
+ CRUSH rule 0 x 800 [91]
+ CRUSH rule 0 x 801 [2]
+ CRUSH rule 0 x 802 [116]
+ CRUSH rule 0 x 803 [37]
+ CRUSH rule 0 x 804 [6]
+ CRUSH rule 0 x 805 [96]
+ CRUSH rule 0 x 806 [67]
+ CRUSH rule 0 x 807 [47]
+ CRUSH rule 0 x 808 [76]
+ CRUSH rule 0 x 809 [27]
+ CRUSH rule 0 x 810 [119]
+ CRUSH rule 0 x 811 [75]
+ CRUSH rule 0 x 812 [25]
+ CRUSH rule 0 x 813 [64]
+ CRUSH rule 0 x 814 [110]
+ CRUSH rule 0 x 815 [84]
+ CRUSH rule 0 x 816 [25]
+ CRUSH rule 0 x 817 [40]
+ CRUSH rule 0 x 818 [34]
+ CRUSH rule 0 x 819 [88]
+ CRUSH rule 0 x 820 [104]
+ CRUSH rule 0 x 821 [58]
+ CRUSH rule 0 x 822 [29]
+ CRUSH rule 0 x 823 [100]
+ CRUSH rule 0 x 824 [102]
+ CRUSH rule 0 x 825 [47]
+ CRUSH rule 0 x 826 [45]
+ CRUSH rule 0 x 827 [101]
+ CRUSH rule 0 x 828 [60]
+ CRUSH rule 0 x 829 [45]
+ CRUSH rule 0 x 830 [51]
+ CRUSH rule 0 x 831 [6]
+ CRUSH rule 0 x 832 [57]
+ CRUSH rule 0 x 833 [34]
+ CRUSH rule 0 x 834 [90]
+ CRUSH rule 0 x 835 [55]
+ CRUSH rule 0 x 836 [38]
+ CRUSH rule 0 x 837 [51]
+ CRUSH rule 0 x 838 [6]
+ CRUSH rule 0 x 839 [106]
+ CRUSH rule 0 x 840 [33]
+ CRUSH rule 0 x 841 [110]
+ CRUSH rule 0 x 842 [66]
+ CRUSH rule 0 x 843 [62]
+ CRUSH rule 0 x 844 [74]
+ CRUSH rule 0 x 845 [74]
+ CRUSH rule 0 x 846 [98]
+ CRUSH rule 0 x 847 [10]
+ CRUSH rule 0 x 848 [89]
+ CRUSH rule 0 x 849 [42]
+ CRUSH rule 0 x 850 [40]
+ CRUSH rule 0 x 851 [65]
+ CRUSH rule 0 x 852 [31]
+ CRUSH rule 0 x 853 [49]
+ CRUSH rule 0 x 854 [83]
+ CRUSH rule 0 x 855 [2]
+ CRUSH rule 0 x 856 [6]
+ CRUSH rule 0 x 857 [15]
+ CRUSH rule 0 x 858 [10]
+ CRUSH rule 0 x 859 [14]
+ CRUSH rule 0 x 860 [114]
+ CRUSH rule 0 x 861 [1]
+ CRUSH rule 0 x 862 [22]
+ CRUSH rule 0 x 863 [79]
+ CRUSH rule 0 x 864 [68]
+ CRUSH rule 0 x 865 [25]
+ CRUSH rule 0 x 866 [18]
+ CRUSH rule 0 x 867 [53]
+ CRUSH rule 0 x 868 [81]
+ CRUSH rule 0 x 869 [111]
+ CRUSH rule 0 x 870 [73]
+ CRUSH rule 0 x 871 [25]
+ CRUSH rule 0 x 872 [39]
+ CRUSH rule 0 x 873 [92]
+ CRUSH rule 0 x 874 [96]
+ CRUSH rule 0 x 875 [115]
+ CRUSH rule 0 x 876 [98]
+ CRUSH rule 0 x 877 [73]
+ CRUSH rule 0 x 878 [64]
+ CRUSH rule 0 x 879 [15]
+ CRUSH rule 0 x 880 [56]
+ CRUSH rule 0 x 881 [109]
+ CRUSH rule 0 x 882 [60]
+ CRUSH rule 0 x 883 [93]
+ CRUSH rule 0 x 884 [67]
+ CRUSH rule 0 x 885 [31]
+ CRUSH rule 0 x 886 [2]
+ CRUSH rule 0 x 887 [5]
+ CRUSH rule 0 x 888 [16]
+ CRUSH rule 0 x 889 [27]
+ CRUSH rule 0 x 890 [48]
+ CRUSH rule 0 x 891 [86]
+ CRUSH rule 0 x 892 [64]
+ CRUSH rule 0 x 893 [118]
+ CRUSH rule 0 x 894 [16]
+ CRUSH rule 0 x 895 [40]
+ CRUSH rule 0 x 896 [97]
+ CRUSH rule 0 x 897 [107]
+ CRUSH rule 0 x 898 [10]
+ CRUSH rule 0 x 899 [75]
+ CRUSH rule 0 x 900 [102]
+ CRUSH rule 0 x 901 [66]
+ CRUSH rule 0 x 902 [102]
+ CRUSH rule 0 x 903 [5]
+ CRUSH rule 0 x 904 [50]
+ CRUSH rule 0 x 905 [99]
+ CRUSH rule 0 x 906 [75]
+ CRUSH rule 0 x 907 [47]
+ CRUSH rule 0 x 908 [96]
+ CRUSH rule 0 x 909 [94]
+ CRUSH rule 0 x 910 [88]
+ CRUSH rule 0 x 911 [102]
+ CRUSH rule 0 x 912 [91]
+ CRUSH rule 0 x 913 [29]
+ CRUSH rule 0 x 914 [84]
+ CRUSH rule 0 x 915 [70]
+ CRUSH rule 0 x 916 [32]
+ CRUSH rule 0 x 917 [43]
+ CRUSH rule 0 x 918 [91]
+ CRUSH rule 0 x 919 [13]
+ CRUSH rule 0 x 920 [18]
+ CRUSH rule 0 x 921 [104]
+ CRUSH rule 0 x 922 [33]
+ CRUSH rule 0 x 923 [28]
+ CRUSH rule 0 x 924 [69]
+ CRUSH rule 0 x 925 [71]
+ CRUSH rule 0 x 926 [64]
+ CRUSH rule 0 x 927 [99]
+ CRUSH rule 0 x 928 [13]
+ CRUSH rule 0 x 929 [117]
+ CRUSH rule 0 x 930 [31]
+ CRUSH rule 0 x 931 [83]
+ CRUSH rule 0 x 932 [60]
+ CRUSH rule 0 x 933 [63]
+ CRUSH rule 0 x 934 [68]
+ CRUSH rule 0 x 935 [31]
+ CRUSH rule 0 x 936 [65]
+ CRUSH rule 0 x 937 [110]
+ CRUSH rule 0 x 938 [29]
+ CRUSH rule 0 x 939 [77]
+ CRUSH rule 0 x 940 [76]
+ CRUSH rule 0 x 941 [66]
+ CRUSH rule 0 x 942 [83]
+ CRUSH rule 0 x 943 [32]
+ CRUSH rule 0 x 944 [113]
+ CRUSH rule 0 x 945 [71]
+ CRUSH rule 0 x 946 [37]
+ CRUSH rule 0 x 947 [107]
+ CRUSH rule 0 x 948 [55]
+ CRUSH rule 0 x 949 [11]
+ CRUSH rule 0 x 950 [96]
+ CRUSH rule 0 x 951 [40]
+ CRUSH rule 0 x 952 [93]
+ CRUSH rule 0 x 953 [55]
+ CRUSH rule 0 x 954 [84]
+ CRUSH rule 0 x 955 [31]
+ CRUSH rule 0 x 956 [72]
+ CRUSH rule 0 x 957 [3]
+ CRUSH rule 0 x 958 [23]
+ CRUSH rule 0 x 959 [42]
+ CRUSH rule 0 x 960 [113]
+ CRUSH rule 0 x 961 [116]
+ CRUSH rule 0 x 962 [13]
+ CRUSH rule 0 x 963 [0]
+ CRUSH rule 0 x 964 [59]
+ CRUSH rule 0 x 965 [47]
+ CRUSH rule 0 x 966 [88]
+ CRUSH rule 0 x 967 [71]
+ CRUSH rule 0 x 968 [73]
+ CRUSH rule 0 x 969 [53]
+ CRUSH rule 0 x 970 [111]
+ CRUSH rule 0 x 971 [87]
+ CRUSH rule 0 x 972 [5]
+ CRUSH rule 0 x 973 [113]
+ CRUSH rule 0 x 974 [49]
+ CRUSH rule 0 x 975 [83]
+ CRUSH rule 0 x 976 [81]
+ CRUSH rule 0 x 977 [95]
+ CRUSH rule 0 x 978 [35]
+ CRUSH rule 0 x 979 [98]
+ CRUSH rule 0 x 980 [52]
+ CRUSH rule 0 x 981 [89]
+ CRUSH rule 0 x 982 [1]
+ CRUSH rule 0 x 983 [34]
+ CRUSH rule 0 x 984 [78]
+ CRUSH rule 0 x 985 [99]
+ CRUSH rule 0 x 986 [4]
+ CRUSH rule 0 x 987 [78]
+ CRUSH rule 0 x 988 [79]
+ CRUSH rule 0 x 989 [87]
+ CRUSH rule 0 x 990 [47]
+ CRUSH rule 0 x 991 [61]
+ CRUSH rule 0 x 992 [83]
+ CRUSH rule 0 x 993 [74]
+ CRUSH rule 0 x 994 [74]
+ CRUSH rule 0 x 995 [100]
+ CRUSH rule 0 x 996 [41]
+ CRUSH rule 0 x 997 [89]
+ CRUSH rule 0 x 998 [92]
+ CRUSH rule 0 x 999 [117]
+ CRUSH rule 0 x 1000 [9]
+ CRUSH rule 0 x 1001 [49]
+ CRUSH rule 0 x 1002 [99]
+ CRUSH rule 0 x 1003 [43]
+ CRUSH rule 0 x 1004 [89]
+ CRUSH rule 0 x 1005 [105]
+ CRUSH rule 0 x 1006 [45]
+ CRUSH rule 0 x 1007 [19]
+ CRUSH rule 0 x 1008 [31]
+ CRUSH rule 0 x 1009 [19]
+ CRUSH rule 0 x 1010 [42]
+ CRUSH rule 0 x 1011 [25]
+ CRUSH rule 0 x 1012 [68]
+ CRUSH rule 0 x 1013 [5]
+ CRUSH rule 0 x 1014 [33]
+ CRUSH rule 0 x 1015 [14]
+ CRUSH rule 0 x 1016 [88]
+ CRUSH rule 0 x 1017 [0]
+ CRUSH rule 0 x 1018 [63]
+ CRUSH rule 0 x 1019 [104]
+ CRUSH rule 0 x 1020 [96]
+ CRUSH rule 0 x 1021 [117]
+ CRUSH rule 0 x 1022 [73]
+ CRUSH rule 0 x 1023 [0]
+ rule 0 (data) num_rep 1 result size == 1:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114]
+ CRUSH rule 0 x 1 [80,79]
+ CRUSH rule 0 x 2 [91,19]
+ CRUSH rule 0 x 3 [51,4]
+ CRUSH rule 0 x 4 [50,89]
+ CRUSH rule 0 x 5 [89,94]
+ CRUSH rule 0 x 6 [91,76]
+ CRUSH rule 0 x 7 [104,25]
+ CRUSH rule 0 x 8 [78,14]
+ CRUSH rule 0 x 9 [101,102]
+ CRUSH rule 0 x 10 [61,6]
+ CRUSH rule 0 x 11 [13,31]
+ CRUSH rule 0 x 12 [83,46]
+ CRUSH rule 0 x 13 [108,85]
+ CRUSH rule 0 x 14 [105,3]
+ CRUSH rule 0 x 15 [18,7]
+ CRUSH rule 0 x 16 [103,3]
+ CRUSH rule 0 x 17 [85,110]
+ CRUSH rule 0 x 18 [11,65]
+ CRUSH rule 0 x 19 [75,50]
+ CRUSH rule 0 x 20 [79,70]
+ CRUSH rule 0 x 21 [84,9]
+ CRUSH rule 0 x 22 [23,104]
+ CRUSH rule 0 x 23 [118,17]
+ CRUSH rule 0 x 24 [83,110]
+ CRUSH rule 0 x 25 [81,64]
+ CRUSH rule 0 x 26 [38,99]
+ CRUSH rule 0 x 27 [76,107]
+ CRUSH rule 0 x 28 [76,71]
+ CRUSH rule 0 x 29 [8,119]
+ CRUSH rule 0 x 30 [94,87]
+ CRUSH rule 0 x 31 [76,95]
+ CRUSH rule 0 x 32 [72,95]
+ CRUSH rule 0 x 33 [77,86]
+ CRUSH rule 0 x 34 [74,73]
+ CRUSH rule 0 x 35 [22,88]
+ CRUSH rule 0 x 36 [104,65]
+ CRUSH rule 0 x 37 [38,81]
+ CRUSH rule 0 x 38 [72,11]
+ CRUSH rule 0 x 39 [68,103]
+ CRUSH rule 0 x 40 [103,78]
+ CRUSH rule 0 x 41 [85,11]
+ CRUSH rule 0 x 42 [106,75]
+ CRUSH rule 0 x 43 [10,68]
+ CRUSH rule 0 x 44 [101,116]
+ CRUSH rule 0 x 45 [8,64]
+ CRUSH rule 0 x 46 [65,1]
+ CRUSH rule 0 x 47 [106,53]
+ CRUSH rule 0 x 48 [34,6]
+ CRUSH rule 0 x 49 [0,81]
+ CRUSH rule 0 x 50 [42,6]
+ CRUSH rule 0 x 51 [104,75]
+ CRUSH rule 0 x 52 [83,19]
+ CRUSH rule 0 x 53 [32,75]
+ CRUSH rule 0 x 54 [28,79]
+ CRUSH rule 0 x 55 [14,5]
+ CRUSH rule 0 x 56 [21,72]
+ CRUSH rule 0 x 57 [93,84]
+ CRUSH rule 0 x 58 [45,106]
+ CRUSH rule 0 x 59 [80,41]
+ CRUSH rule 0 x 60 [90,57]
+ CRUSH rule 0 x 61 [88,37]
+ CRUSH rule 0 x 62 [81,1]
+ CRUSH rule 0 x 63 [79,113]
+ CRUSH rule 0 x 64 [1,89]
+ CRUSH rule 0 x 65 [13,0]
+ CRUSH rule 0 x 66 [48,49]
+ CRUSH rule 0 x 67 [94,103]
+ CRUSH rule 0 x 68 [102,91]
+ CRUSH rule 0 x 69 [62,77]
+ CRUSH rule 0 x 70 [84,105]
+ CRUSH rule 0 x 71 [55,19]
+ CRUSH rule 0 x 72 [97,42]
+ CRUSH rule 0 x 73 [64,83]
+ CRUSH rule 0 x 74 [96,59]
+ CRUSH rule 0 x 75 [29,28]
+ CRUSH rule 0 x 76 [55,0]
+ CRUSH rule 0 x 77 [107,21]
+ CRUSH rule 0 x 78 [31,94]
+ CRUSH rule 0 x 79 [64,51]
+ CRUSH rule 0 x 80 [0,31]
+ CRUSH rule 0 x 81 [71,109]
+ CRUSH rule 0 x 82 [37,40]
+ CRUSH rule 0 x 83 [92,103]
+ CRUSH rule 0 x 84 [49,115]
+ CRUSH rule 0 x 85 [54,101]
+ CRUSH rule 0 x 86 [37,7]
+ CRUSH rule 0 x 87 [116,4]
+ CRUSH rule 0 x 88 [38,55]
+ CRUSH rule 0 x 89 [76,77]
+ CRUSH rule 0 x 90 [14,50]
+ CRUSH rule 0 x 91 [93,34]
+ CRUSH rule 0 x 92 [86,9]
+ CRUSH rule 0 x 93 [44,65]
+ CRUSH rule 0 x 94 [61,102]
+ CRUSH rule 0 x 95 [93,86]
+ CRUSH rule 0 x 96 [66,87]
+ CRUSH rule 0 x 97 [111,9]
+ CRUSH rule 0 x 98 [66,91]
+ CRUSH rule 0 x 99 [78,3]
+ CRUSH rule 0 x 100 [28,8]
+ CRUSH rule 0 x 101 [84,16]
+ CRUSH rule 0 x 102 [82,105]
+ CRUSH rule 0 x 103 [66,6]
+ CRUSH rule 0 x 104 [14,95]
+ CRUSH rule 0 x 105 [87,1]
+ CRUSH rule 0 x 106 [69,116]
+ CRUSH rule 0 x 107 [1,55]
+ CRUSH rule 0 x 108 [94,53]
+ CRUSH rule 0 x 109 [112,63]
+ CRUSH rule 0 x 110 [54,61]
+ CRUSH rule 0 x 111 [10,58]
+ CRUSH rule 0 x 112 [89,9]
+ CRUSH rule 0 x 113 [69,2]
+ CRUSH rule 0 x 114 [79,17]
+ CRUSH rule 0 x 115 [50,85]
+ CRUSH rule 0 x 116 [96,16]
+ CRUSH rule 0 x 117 [87,56]
+ CRUSH rule 0 x 118 [23,56]
+ CRUSH rule 0 x 119 [104,11]
+ CRUSH rule 0 x 120 [57,5]
+ CRUSH rule 0 x 121 [105,117]
+ CRUSH rule 0 x 122 [45,110]
+ CRUSH rule 0 x 123 [112,22]
+ CRUSH rule 0 x 124 [110,11]
+ CRUSH rule 0 x 125 [66,105]
+ CRUSH rule 0 x 126 [51,28]
+ CRUSH rule 0 x 127 [70,6]
+ CRUSH rule 0 x 128 [90,16]
+ CRUSH rule 0 x 129 [103,110]
+ CRUSH rule 0 x 130 [50,11]
+ CRUSH rule 0 x 131 [23,60]
+ CRUSH rule 0 x 132 [69,70]
+ CRUSH rule 0 x 133 [52,25]
+ CRUSH rule 0 x 134 [78,6]
+ CRUSH rule 0 x 135 [78,3]
+ CRUSH rule 0 x 136 [32,29]
+ CRUSH rule 0 x 137 [92,41]
+ CRUSH rule 0 x 138 [17,118]
+ CRUSH rule 0 x 139 [89,60]
+ CRUSH rule 0 x 140 [39,62]
+ CRUSH rule 0 x 141 [89,98]
+ CRUSH rule 0 x 142 [70,61]
+ CRUSH rule 0 x 143 [51,28]
+ CRUSH rule 0 x 144 [13,81]
+ CRUSH rule 0 x 145 [77,119]
+ CRUSH rule 0 x 146 [96,69]
+ CRUSH rule 0 x 147 [2,95]
+ CRUSH rule 0 x 148 [74,69]
+ CRUSH rule 0 x 149 [76,13]
+ CRUSH rule 0 x 150 [38,47]
+ CRUSH rule 0 x 151 [90,67]
+ CRUSH rule 0 x 152 [49,18]
+ CRUSH rule 0 x 153 [71,44]
+ CRUSH rule 0 x 154 [94,81]
+ CRUSH rule 0 x 155 [75,112]
+ CRUSH rule 0 x 156 [107,66]
+ CRUSH rule 0 x 157 [112,43]
+ CRUSH rule 0 x 158 [26,17]
+ CRUSH rule 0 x 159 [52,9]
+ CRUSH rule 0 x 160 [41,0]
+ CRUSH rule 0 x 161 [84,45]
+ CRUSH rule 0 x 162 [55,2]
+ CRUSH rule 0 x 163 [54,8]
+ CRUSH rule 0 x 164 [45,5]
+ CRUSH rule 0 x 165 [25,72]
+ CRUSH rule 0 x 166 [73,36]
+ CRUSH rule 0 x 167 [89,58]
+ CRUSH rule 0 x 168 [47,40]
+ CRUSH rule 0 x 169 [51,21]
+ CRUSH rule 0 x 170 [68,91]
+ CRUSH rule 0 x 171 [73,90]
+ CRUSH rule 0 x 172 [117,41]
+ CRUSH rule 0 x 173 [13,34]
+ CRUSH rule 0 x 174 [116,25]
+ CRUSH rule 0 x 175 [3,41]
+ CRUSH rule 0 x 176 [94,91]
+ CRUSH rule 0 x 177 [52,85]
+ CRUSH rule 0 x 178 [39,2]
+ CRUSH rule 0 x 179 [72,97]
+ CRUSH rule 0 x 180 [60,61]
+ CRUSH rule 0 x 181 [18,59]
+ CRUSH rule 0 x 182 [22,90]
+ CRUSH rule 0 x 183 [11,74]
+ CRUSH rule 0 x 184 [92,8]
+ CRUSH rule 0 x 185 [97,8]
+ CRUSH rule 0 x 186 [67,116]
+ CRUSH rule 0 x 187 [116,11]
+ CRUSH rule 0 x 188 [69,110]
+ CRUSH rule 0 x 189 [47,84]
+ CRUSH rule 0 x 190 [65,82]
+ CRUSH rule 0 x 191 [49,38]
+ CRUSH rule 0 x 192 [68,93]
+ CRUSH rule 0 x 193 [0,33]
+ CRUSH rule 0 x 194 [62,99]
+ CRUSH rule 0 x 195 [119,4]
+ CRUSH rule 0 x 196 [72,27]
+ CRUSH rule 0 x 197 [106,83]
+ CRUSH rule 0 x 198 [114,21]
+ CRUSH rule 0 x 199 [0,83]
+ CRUSH rule 0 x 200 [35,86]
+ CRUSH rule 0 x 201 [27,26]
+ CRUSH rule 0 x 202 [98,33]
+ CRUSH rule 0 x 203 [36,91]
+ CRUSH rule 0 x 204 [10,98]
+ CRUSH rule 0 x 205 [81,17]
+ CRUSH rule 0 x 206 [49,112]
+ CRUSH rule 0 x 207 [80,39]
+ CRUSH rule 0 x 208 [63,26]
+ CRUSH rule 0 x 209 [85,111]
+ CRUSH rule 0 x 210 [79,18]
+ CRUSH rule 0 x 211 [26,10]
+ CRUSH rule 0 x 212 [28,103]
+ CRUSH rule 0 x 213 [91,0]
+ CRUSH rule 0 x 214 [91,38]
+ CRUSH rule 0 x 215 [61,86]
+ CRUSH rule 0 x 216 [99,94]
+ CRUSH rule 0 x 217 [86,89]
+ CRUSH rule 0 x 218 [70,4]
+ CRUSH rule 0 x 219 [28,59]
+ CRUSH rule 0 x 220 [56,8]
+ CRUSH rule 0 x 221 [0,9]
+ CRUSH rule 0 x 222 [50,63]
+ CRUSH rule 0 x 223 [29,1]
+ CRUSH rule 0 x 224 [52,10]
+ CRUSH rule 0 x 225 [15,35]
+ CRUSH rule 0 x 226 [44,7]
+ CRUSH rule 0 x 227 [42,3]
+ CRUSH rule 0 x 228 [117,49]
+ CRUSH rule 0 x 229 [100,79]
+ CRUSH rule 0 x 230 [41,114]
+ CRUSH rule 0 x 231 [56,95]
+ CRUSH rule 0 x 232 [23,8]
+ CRUSH rule 0 x 233 [88,103]
+ CRUSH rule 0 x 234 [4,101]
+ CRUSH rule 0 x 235 [26,10]
+ CRUSH rule 0 x 236 [32,37]
+ CRUSH rule 0 x 237 [92,3]
+ CRUSH rule 0 x 238 [10,26]
+ CRUSH rule 0 x 239 [15,105]
+ CRUSH rule 0 x 240 [109,14]
+ CRUSH rule 0 x 241 [47,108]
+ CRUSH rule 0 x 242 [24,99]
+ CRUSH rule 0 x 243 [76,8]
+ CRUSH rule 0 x 244 [103,13]
+ CRUSH rule 0 x 245 [27,82]
+ CRUSH rule 0 x 246 [5,43]
+ CRUSH rule 0 x 247 [99,102]
+ CRUSH rule 0 x 248 [8,29]
+ CRUSH rule 0 x 249 [85,1]
+ CRUSH rule 0 x 250 [93,102]
+ CRUSH rule 0 x 251 [28,103]
+ CRUSH rule 0 x 252 [95,22]
+ CRUSH rule 0 x 253 [109,27]
+ CRUSH rule 0 x 254 [80,13]
+ CRUSH rule 0 x 255 [103,13]
+ CRUSH rule 0 x 256 [37,38]
+ CRUSH rule 0 x 257 [69,117]
+ CRUSH rule 0 x 258 [34,55]
+ CRUSH rule 0 x 259 [70,17]
+ CRUSH rule 0 x 260 [98,29]
+ CRUSH rule 0 x 261 [94,83]
+ CRUSH rule 0 x 262 [42,49]
+ CRUSH rule 0 x 263 [65,42]
+ CRUSH rule 0 x 264 [36,49]
+ CRUSH rule 0 x 265 [66,63]
+ CRUSH rule 0 x 266 [75,92]
+ CRUSH rule 0 x 267 [58,35]
+ CRUSH rule 0 x 268 [38,9]
+ CRUSH rule 0 x 269 [86,59]
+ CRUSH rule 0 x 270 [58,37]
+ CRUSH rule 0 x 271 [19,33]
+ CRUSH rule 0 x 272 [73,9]
+ CRUSH rule 0 x 273 [108,29]
+ CRUSH rule 0 x 274 [47,64]
+ CRUSH rule 0 x 275 [92,19]
+ CRUSH rule 0 x 276 [7,79]
+ CRUSH rule 0 x 277 [19,68]
+ CRUSH rule 0 x 278 [116,105]
+ CRUSH rule 0 x 279 [101,3]
+ CRUSH rule 0 x 280 [113,69]
+ CRUSH rule 0 x 281 [14,93]
+ CRUSH rule 0 x 282 [106,61]
+ CRUSH rule 0 x 283 [8,118]
+ CRUSH rule 0 x 284 [10,110]
+ CRUSH rule 0 x 285 [88,63]
+ CRUSH rule 0 x 286 [27,4]
+ CRUSH rule 0 x 287 [84,65]
+ CRUSH rule 0 x 288 [103,8]
+ CRUSH rule 0 x 289 [9,64]
+ CRUSH rule 0 x 290 [115,17]
+ CRUSH rule 0 x 291 [48,45]
+ CRUSH rule 0 x 292 [89,109]
+ CRUSH rule 0 x 293 [27,24]
+ CRUSH rule 0 x 294 [79,36]
+ CRUSH rule 0 x 295 [37,116]
+ CRUSH rule 0 x 296 [56,61]
+ CRUSH rule 0 x 297 [35,40]
+ CRUSH rule 0 x 298 [71,118]
+ CRUSH rule 0 x 299 [116,61]
+ CRUSH rule 0 x 300 [67,5]
+ CRUSH rule 0 x 301 [51,110]
+ CRUSH rule 0 x 302 [78,67]
+ CRUSH rule 0 x 303 [19,94]
+ CRUSH rule 0 x 304 [101,66]
+ CRUSH rule 0 x 305 [81,62]
+ CRUSH rule 0 x 306 [0,23]
+ CRUSH rule 0 x 307 [44,15]
+ CRUSH rule 0 x 308 [91,98]
+ CRUSH rule 0 x 309 [38,63]
+ CRUSH rule 0 x 310 [26,89]
+ CRUSH rule 0 x 311 [36,83]
+ CRUSH rule 0 x 312 [33,22]
+ CRUSH rule 0 x 313 [104,16]
+ CRUSH rule 0 x 314 [28,4]
+ CRUSH rule 0 x 315 [16,117]
+ CRUSH rule 0 x 316 [4,1]
+ CRUSH rule 0 x 317 [118,8]
+ CRUSH rule 0 x 318 [17,47]
+ CRUSH rule 0 x 319 [24,83]
+ CRUSH rule 0 x 320 [36,65]
+ CRUSH rule 0 x 321 [26,85]
+ CRUSH rule 0 x 322 [87,116]
+ CRUSH rule 0 x 323 [73,0]
+ CRUSH rule 0 x 324 [21,37]
+ CRUSH rule 0 x 325 [52,16]
+ CRUSH rule 0 x 326 [111,93]
+ CRUSH rule 0 x 327 [62,8]
+ CRUSH rule 0 x 328 [7,42]
+ CRUSH rule 0 x 329 [93,34]
+ CRUSH rule 0 x 330 [24,4]
+ CRUSH rule 0 x 331 [41,117]
+ CRUSH rule 0 x 332 [61,110]
+ CRUSH rule 0 x 333 [16,8]
+ CRUSH rule 0 x 334 [3,35]
+ CRUSH rule 0 x 335 [71,74]
+ CRUSH rule 0 x 336 [16,19]
+ CRUSH rule 0 x 337 [37,40]
+ CRUSH rule 0 x 338 [109,13]
+ CRUSH rule 0 x 339 [37,21]
+ CRUSH rule 0 x 340 [119,67]
+ CRUSH rule 0 x 341 [63,8]
+ CRUSH rule 0 x 342 [92,25]
+ CRUSH rule 0 x 343 [49,26]
+ CRUSH rule 0 x 344 [103,26]
+ CRUSH rule 0 x 345 [56,25]
+ CRUSH rule 0 x 346 [3,79]
+ CRUSH rule 0 x 347 [106,27]
+ CRUSH rule 0 x 348 [10,117]
+ CRUSH rule 0 x 349 [96,37]
+ CRUSH rule 0 x 350 [63,32]
+ CRUSH rule 0 x 351 [60,85]
+ CRUSH rule 0 x 352 [103,84]
+ CRUSH rule 0 x 353 [49,113]
+ CRUSH rule 0 x 354 [55,52]
+ CRUSH rule 0 x 355 [73,68]
+ CRUSH rule 0 x 356 [114,41]
+ CRUSH rule 0 x 357 [14,96]
+ CRUSH rule 0 x 358 [97,62]
+ CRUSH rule 0 x 359 [4,105]
+ CRUSH rule 0 x 360 [106,69]
+ CRUSH rule 0 x 361 [27,46]
+ CRUSH rule 0 x 362 [28,33]
+ CRUSH rule 0 x 363 [45,26]
+ CRUSH rule 0 x 364 [23,50]
+ CRUSH rule 0 x 365 [24,22]
+ CRUSH rule 0 x 366 [14,58]
+ CRUSH rule 0 x 367 [103,50]
+ CRUSH rule 0 x 368 [103,32]
+ CRUSH rule 0 x 369 [37,4]
+ CRUSH rule 0 x 370 [11,89]
+ CRUSH rule 0 x 371 [34,55]
+ CRUSH rule 0 x 372 [58,10]
+ CRUSH rule 0 x 373 [98,8]
+ CRUSH rule 0 x 374 [110,95]
+ CRUSH rule 0 x 375 [19,92]
+ CRUSH rule 0 x 376 [22,86]
+ CRUSH rule 0 x 377 [98,105]
+ CRUSH rule 0 x 378 [67,36]
+ CRUSH rule 0 x 379 [77,8]
+ CRUSH rule 0 x 380 [69,104]
+ CRUSH rule 0 x 381 [55,1]
+ CRUSH rule 0 x 382 [26,51]
+ CRUSH rule 0 x 383 [48,25]
+ CRUSH rule 0 x 384 [15,100]
+ CRUSH rule 0 x 385 [82,61]
+ CRUSH rule 0 x 386 [108,63]
+ CRUSH rule 0 x 387 [70,15]
+ CRUSH rule 0 x 388 [5,67]
+ CRUSH rule 0 x 389 [14,29]
+ CRUSH rule 0 x 390 [68,10]
+ CRUSH rule 0 x 391 [113,69]
+ CRUSH rule 0 x 392 [72,14]
+ CRUSH rule 0 x 393 [115,6]
+ CRUSH rule 0 x 394 [38,21]
+ CRUSH rule 0 x 395 [0,27]
+ CRUSH rule 0 x 396 [59,92]
+ CRUSH rule 0 x 397 [87,1]
+ CRUSH rule 0 x 398 [44,75]
+ CRUSH rule 0 x 399 [9,2]
+ CRUSH rule 0 x 400 [101,102]
+ CRUSH rule 0 x 401 [79,34]
+ CRUSH rule 0 x 402 [107,98]
+ CRUSH rule 0 x 403 [23,82]
+ CRUSH rule 0 x 404 [76,75]
+ CRUSH rule 0 x 405 [10,32]
+ CRUSH rule 0 x 406 [38,16]
+ CRUSH rule 0 x 407 [70,85]
+ CRUSH rule 0 x 408 [55,72]
+ CRUSH rule 0 x 409 [102,15]
+ CRUSH rule 0 x 410 [59,13]
+ CRUSH rule 0 x 411 [34,29]
+ CRUSH rule 0 x 412 [108,99]
+ CRUSH rule 0 x 413 [54,107]
+ CRUSH rule 0 x 414 [70,4]
+ CRUSH rule 0 x 415 [107,36]
+ CRUSH rule 0 x 416 [79,68]
+ CRUSH rule 0 x 417 [8,79]
+ CRUSH rule 0 x 418 [51,46]
+ CRUSH rule 0 x 419 [117,16]
+ CRUSH rule 0 x 420 [109,105]
+ CRUSH rule 0 x 421 [114,17]
+ CRUSH rule 0 x 422 [109,8]
+ CRUSH rule 0 x 423 [59,98]
+ CRUSH rule 0 x 424 [71,5]
+ CRUSH rule 0 x 425 [101,111]
+ CRUSH rule 0 x 426 [47,46]
+ CRUSH rule 0 x 427 [86,87]
+ CRUSH rule 0 x 428 [68,35]
+ CRUSH rule 0 x 429 [76,6]
+ CRUSH rule 0 x 430 [9,86]
+ CRUSH rule 0 x 431 [105,119]
+ CRUSH rule 0 x 432 [46,37]
+ CRUSH rule 0 x 433 [6,101]
+ CRUSH rule 0 x 434 [64,69]
+ CRUSH rule 0 x 435 [16,50]
+ CRUSH rule 0 x 436 [89,102]
+ CRUSH rule 0 x 437 [29,114]
+ CRUSH rule 0 x 438 [105,98]
+ CRUSH rule 0 x 439 [29,119]
+ CRUSH rule 0 x 440 [38,13]
+ CRUSH rule 0 x 441 [112,105]
+ CRUSH rule 0 x 442 [55,108]
+ CRUSH rule 0 x 443 [44,57]
+ CRUSH rule 0 x 444 [11,27]
+ CRUSH rule 0 x 445 [19,5]
+ CRUSH rule 0 x 446 [40,47]
+ CRUSH rule 0 x 447 [100,61]
+ CRUSH rule 0 x 448 [7,68]
+ CRUSH rule 0 x 449 [67,19]
+ CRUSH rule 0 x 450 [117,101]
+ CRUSH rule 0 x 451 [93,108]
+ CRUSH rule 0 x 452 [70,49]
+ CRUSH rule 0 x 453 [82,51]
+ CRUSH rule 0 x 454 [53,18]
+ CRUSH rule 0 x 455 [91,92]
+ CRUSH rule 0 x 456 [17,16]
+ CRUSH rule 0 x 457 [113,31]
+ CRUSH rule 0 x 458 [119,43]
+ CRUSH rule 0 x 459 [25,115]
+ CRUSH rule 0 x 460 [11,97]
+ CRUSH rule 0 x 461 [21,111]
+ CRUSH rule 0 x 462 [25,62]
+ CRUSH rule 0 x 463 [6,105]
+ CRUSH rule 0 x 464 [19,109]
+ CRUSH rule 0 x 465 [29,86]
+ CRUSH rule 0 x 466 [66,91]
+ CRUSH rule 0 x 467 [27,68]
+ CRUSH rule 0 x 468 [97,26]
+ CRUSH rule 0 x 469 [98,75]
+ CRUSH rule 0 x 470 [50,67]
+ CRUSH rule 0 x 471 [40,79]
+ CRUSH rule 0 x 472 [74,79]
+ CRUSH rule 0 x 473 [95,36]
+ CRUSH rule 0 x 474 [51,14]
+ CRUSH rule 0 x 475 [3,79]
+ CRUSH rule 0 x 476 [110,31]
+ CRUSH rule 0 x 477 [25,106]
+ CRUSH rule 0 x 478 [19,105]
+ CRUSH rule 0 x 479 [70,37]
+ CRUSH rule 0 x 480 [62,57]
+ CRUSH rule 0 x 481 [26,19]
+ CRUSH rule 0 x 482 [84,14]
+ CRUSH rule 0 x 483 [36,53]
+ CRUSH rule 0 x 484 [37,36]
+ CRUSH rule 0 x 485 [84,15]
+ CRUSH rule 0 x 486 [92,10]
+ CRUSH rule 0 x 487 [106,51]
+ CRUSH rule 0 x 488 [42,43]
+ CRUSH rule 0 x 489 [76,16]
+ CRUSH rule 0 x 490 [68,87]
+ CRUSH rule 0 x 491 [80,71]
+ CRUSH rule 0 x 492 [21,57]
+ CRUSH rule 0 x 493 [99,78]
+ CRUSH rule 0 x 494 [4,87]
+ CRUSH rule 0 x 495 [40,43]
+ CRUSH rule 0 x 496 [13,38]
+ CRUSH rule 0 x 497 [102,71]
+ CRUSH rule 0 x 498 [68,83]
+ CRUSH rule 0 x 499 [22,26]
+ CRUSH rule 0 x 500 [50,6]
+ CRUSH rule 0 x 501 [60,79]
+ CRUSH rule 0 x 502 [11,28]
+ CRUSH rule 0 x 503 [117,25]
+ CRUSH rule 0 x 504 [90,41]
+ CRUSH rule 0 x 505 [91,100]
+ CRUSH rule 0 x 506 [82,103]
+ CRUSH rule 0 x 507 [6,103]
+ CRUSH rule 0 x 508 [34,87]
+ CRUSH rule 0 x 509 [88,63]
+ CRUSH rule 0 x 510 [11,73]
+ CRUSH rule 0 x 511 [72,27]
+ CRUSH rule 0 x 512 [118,73]
+ CRUSH rule 0 x 513 [22,76]
+ CRUSH rule 0 x 514 [82,15]
+ CRUSH rule 0 x 515 [27,0]
+ CRUSH rule 0 x 516 [66,85]
+ CRUSH rule 0 x 517 [83,4]
+ CRUSH rule 0 x 518 [18,3]
+ CRUSH rule 0 x 519 [67,119]
+ CRUSH rule 0 x 520 [15,114]
+ CRUSH rule 0 x 521 [63,113]
+ CRUSH rule 0 x 522 [4,73]
+ CRUSH rule 0 x 523 [36,35]
+ CRUSH rule 0 x 524 [33,38]
+ CRUSH rule 0 x 525 [63,119]
+ CRUSH rule 0 x 526 [83,50]
+ CRUSH rule 0 x 527 [37,0]
+ CRUSH rule 0 x 528 [108,35]
+ CRUSH rule 0 x 529 [107,15]
+ CRUSH rule 0 x 530 [49,3]
+ CRUSH rule 0 x 531 [27,7]
+ CRUSH rule 0 x 532 [68,71]
+ CRUSH rule 0 x 533 [5,85]
+ CRUSH rule 0 x 534 [97,24]
+ CRUSH rule 0 x 535 [48,75]
+ CRUSH rule 0 x 536 [3,37]
+ CRUSH rule 0 x 537 [116,7]
+ CRUSH rule 0 x 538 [85,8]
+ CRUSH rule 0 x 539 [10,9]
+ CRUSH rule 0 x 540 [100,101]
+ CRUSH rule 0 x 541 [111,77]
+ CRUSH rule 0 x 542 [50,27]
+ CRUSH rule 0 x 543 [45,21]
+ CRUSH rule 0 x 544 [106,93]
+ CRUSH rule 0 x 545 [43,114]
+ CRUSH rule 0 x 546 [108,79]
+ CRUSH rule 0 x 547 [27,50]
+ CRUSH rule 0 x 548 [53,82]
+ CRUSH rule 0 x 549 [60,37]
+ CRUSH rule 0 x 550 [47,68]
+ CRUSH rule 0 x 551 [14,88]
+ CRUSH rule 0 x 552 [70,73]
+ CRUSH rule 0 x 553 [96,105]
+ CRUSH rule 0 x 554 [61,0]
+ CRUSH rule 0 x 555 [76,37]
+ CRUSH rule 0 x 556 [106,89]
+ CRUSH rule 0 x 557 [39,113]
+ CRUSH rule 0 x 558 [70,79]
+ CRUSH rule 0 x 559 [106,69]
+ CRUSH rule 0 x 560 [94,43]
+ CRUSH rule 0 x 561 [27,76]
+ CRUSH rule 0 x 562 [97,62]
+ CRUSH rule 0 x 563 [64,103]
+ CRUSH rule 0 x 564 [96,41]
+ CRUSH rule 0 x 565 [66,71]
+ CRUSH rule 0 x 566 [27,38]
+ CRUSH rule 0 x 567 [88,8]
+ CRUSH rule 0 x 568 [17,96]
+ CRUSH rule 0 x 569 [102,63]
+ CRUSH rule 0 x 570 [7,27]
+ CRUSH rule 0 x 571 [95,98]
+ CRUSH rule 0 x 572 [62,89]
+ CRUSH rule 0 x 573 [51,118]
+ CRUSH rule 0 x 574 [89,78]
+ CRUSH rule 0 x 575 [19,101]
+ CRUSH rule 0 x 576 [112,73]
+ CRUSH rule 0 x 577 [8,84]
+ CRUSH rule 0 x 578 [64,8]
+ CRUSH rule 0 x 579 [78,77]
+ CRUSH rule 0 x 580 [68,95]
+ CRUSH rule 0 x 581 [55,52]
+ CRUSH rule 0 x 582 [27,13]
+ CRUSH rule 0 x 583 [74,105]
+ CRUSH rule 0 x 584 [72,13]
+ CRUSH rule 0 x 585 [88,99]
+ CRUSH rule 0 x 586 [33,108]
+ CRUSH rule 0 x 587 [106,99]
+ CRUSH rule 0 x 588 [0,83]
+ CRUSH rule 0 x 589 [7,95]
+ CRUSH rule 0 x 590 [59,112]
+ CRUSH rule 0 x 591 [42,23]
+ CRUSH rule 0 x 592 [45,72]
+ CRUSH rule 0 x 593 [89,14]
+ CRUSH rule 0 x 594 [27,76]
+ CRUSH rule 0 x 595 [7,10]
+ CRUSH rule 0 x 596 [82,59]
+ CRUSH rule 0 x 597 [72,83]
+ CRUSH rule 0 x 598 [34,15]
+ CRUSH rule 0 x 599 [119,61]
+ CRUSH rule 0 x 600 [9,84]
+ CRUSH rule 0 x 601 [104,15]
+ CRUSH rule 0 x 602 [48,45]
+ CRUSH rule 0 x 603 [24,9]
+ CRUSH rule 0 x 604 [89,0]
+ CRUSH rule 0 x 605 [104,87]
+ CRUSH rule 0 x 606 [49,34]
+ CRUSH rule 0 x 607 [95,40]
+ CRUSH rule 0 x 608 [49,80]
+ CRUSH rule 0 x 609 [61,66]
+ CRUSH rule 0 x 610 [106,16]
+ CRUSH rule 0 x 611 [66,87]
+ CRUSH rule 0 x 612 [103,8]
+ CRUSH rule 0 x 613 [84,91]
+ CRUSH rule 0 x 614 [81,7]
+ CRUSH rule 0 x 615 [61,19]
+ CRUSH rule 0 x 616 [41,15]
+ CRUSH rule 0 x 617 [111,69]
+ CRUSH rule 0 x 618 [3,99]
+ CRUSH rule 0 x 619 [92,27]
+ CRUSH rule 0 x 620 [108,103]
+ CRUSH rule 0 x 621 [106,99]
+ CRUSH rule 0 x 622 [67,48]
+ CRUSH rule 0 x 623 [94,3]
+ CRUSH rule 0 x 624 [115,59]
+ CRUSH rule 0 x 625 [111,27]
+ CRUSH rule 0 x 626 [3,55]
+ CRUSH rule 0 x 627 [19,29]
+ CRUSH rule 0 x 628 [65,88]
+ CRUSH rule 0 x 629 [119,7]
+ CRUSH rule 0 x 630 [109,19]
+ CRUSH rule 0 x 631 [48,75]
+ CRUSH rule 0 x 632 [81,0]
+ CRUSH rule 0 x 633 [65,68]
+ CRUSH rule 0 x 634 [87,50]
+ CRUSH rule 0 x 635 [107,9]
+ CRUSH rule 0 x 636 [23,78]
+ CRUSH rule 0 x 637 [102,45]
+ CRUSH rule 0 x 638 [43,114]
+ CRUSH rule 0 x 639 [31,78]
+ CRUSH rule 0 x 640 [113,73]
+ CRUSH rule 0 x 641 [45,96]
+ CRUSH rule 0 x 642 [47,66]
+ CRUSH rule 0 x 643 [64,47]
+ CRUSH rule 0 x 644 [31,21]
+ CRUSH rule 0 x 645 [76,19]
+ CRUSH rule 0 x 646 [37,54]
+ CRUSH rule 0 x 647 [58,87]
+ CRUSH rule 0 x 648 [31,21]
+ CRUSH rule 0 x 649 [88,45]
+ CRUSH rule 0 x 650 [116,7]
+ CRUSH rule 0 x 651 [97,106]
+ CRUSH rule 0 x 652 [57,112]
+ CRUSH rule 0 x 653 [38,39]
+ CRUSH rule 0 x 654 [49,32]
+ CRUSH rule 0 x 655 [89,62]
+ CRUSH rule 0 x 656 [0,49]
+ CRUSH rule 0 x 657 [47,32]
+ CRUSH rule 0 x 658 [75,82]
+ CRUSH rule 0 x 659 [26,83]
+ CRUSH rule 0 x 660 [65,110]
+ CRUSH rule 0 x 661 [91,48]
+ CRUSH rule 0 x 662 [111,99]
+ CRUSH rule 0 x 663 [88,35]
+ CRUSH rule 0 x 664 [59,78]
+ CRUSH rule 0 x 665 [78,15]
+ CRUSH rule 0 x 666 [112,4]
+ CRUSH rule 0 x 667 [97,46]
+ CRUSH rule 0 x 668 [97,15]
+ CRUSH rule 0 x 669 [85,66]
+ CRUSH rule 0 x 670 [41,38]
+ CRUSH rule 0 x 671 [116,97]
+ CRUSH rule 0 x 672 [44,55]
+ CRUSH rule 0 x 673 [83,50]
+ CRUSH rule 0 x 674 [59,78]
+ CRUSH rule 0 x 675 [88,14]
+ CRUSH rule 0 x 676 [62,8]
+ CRUSH rule 0 x 677 [88,67]
+ CRUSH rule 0 x 678 [98,83]
+ CRUSH rule 0 x 679 [70,59]
+ CRUSH rule 0 x 680 [55,96]
+ CRUSH rule 0 x 681 [53,68]
+ CRUSH rule 0 x 682 [27,58]
+ CRUSH rule 0 x 683 [57,80]
+ CRUSH rule 0 x 684 [98,65]
+ CRUSH rule 0 x 685 [106,55]
+ CRUSH rule 0 x 686 [86,95]
+ CRUSH rule 0 x 687 [49,72]
+ CRUSH rule 0 x 688 [16,114]
+ CRUSH rule 0 x 689 [6,48]
+ CRUSH rule 0 x 690 [43,70]
+ CRUSH rule 0 x 691 [34,105]
+ CRUSH rule 0 x 692 [40,97]
+ CRUSH rule 0 x 693 [29,84]
+ CRUSH rule 0 x 694 [6,41]
+ CRUSH rule 0 x 695 [31,60]
+ CRUSH rule 0 x 696 [36,3]
+ CRUSH rule 0 x 697 [96,77]
+ CRUSH rule 0 x 698 [61,68]
+ CRUSH rule 0 x 699 [47,62]
+ CRUSH rule 0 x 700 [0,55]
+ CRUSH rule 0 x 701 [42,11]
+ CRUSH rule 0 x 702 [0,71]
+ CRUSH rule 0 x 703 [92,67]
+ CRUSH rule 0 x 704 [10,19]
+ CRUSH rule 0 x 705 [105,82]
+ CRUSH rule 0 x 706 [74,105]
+ CRUSH rule 0 x 707 [0,77]
+ CRUSH rule 0 x 708 [84,8]
+ CRUSH rule 0 x 709 [114,97]
+ CRUSH rule 0 x 710 [94,7]
+ CRUSH rule 0 x 711 [68,49]
+ CRUSH rule 0 x 712 [34,75]
+ CRUSH rule 0 x 713 [29,0]
+ CRUSH rule 0 x 714 [81,115]
+ CRUSH rule 0 x 715 [71,84]
+ CRUSH rule 0 x 716 [40,17]
+ CRUSH rule 0 x 717 [61,62]
+ CRUSH rule 0 x 718 [40,85]
+ CRUSH rule 0 x 719 [59,42]
+ CRUSH rule 0 x 720 [69,72]
+ CRUSH rule 0 x 721 [62,41]
+ CRUSH rule 0 x 722 [115,8]
+ CRUSH rule 0 x 723 [117,41]
+ CRUSH rule 0 x 724 [45,21]
+ CRUSH rule 0 x 725 [53,113]
+ CRUSH rule 0 x 726 [84,23]
+ CRUSH rule 0 x 727 [109,14]
+ CRUSH rule 0 x 728 [76,16]
+ CRUSH rule 0 x 729 [108,6]
+ CRUSH rule 0 x 730 [28,47]
+ CRUSH rule 0 x 731 [78,27]
+ CRUSH rule 0 x 732 [55,90]
+ CRUSH rule 0 x 733 [84,3]
+ CRUSH rule 0 x 734 [27,117]
+ CRUSH rule 0 x 735 [83,28]
+ CRUSH rule 0 x 736 [70,67]
+ CRUSH rule 0 x 737 [117,15]
+ CRUSH rule 0 x 738 [118,33]
+ CRUSH rule 0 x 739 [87,38]
+ CRUSH rule 0 x 740 [29,38]
+ CRUSH rule 0 x 741 [96,73]
+ CRUSH rule 0 x 742 [106,83]
+ CRUSH rule 0 x 743 [105,94]
+ CRUSH rule 0 x 744 [23,42]
+ CRUSH rule 0 x 745 [28,59]
+ CRUSH rule 0 x 746 [18,47]
+ CRUSH rule 0 x 747 [65,70]
+ CRUSH rule 0 x 748 [48,89]
+ CRUSH rule 0 x 749 [102,51]
+ CRUSH rule 0 x 750 [50,27]
+ CRUSH rule 0 x 751 [36,25]
+ CRUSH rule 0 x 752 [69,52]
+ CRUSH rule 0 x 753 [9,32]
+ CRUSH rule 0 x 754 [9,57]
+ CRUSH rule 0 x 755 [98,81]
+ CRUSH rule 0 x 756 [113,87]
+ CRUSH rule 0 x 757 [47,66]
+ CRUSH rule 0 x 758 [57,88]
+ CRUSH rule 0 x 759 [74,27]
+ CRUSH rule 0 x 760 [53,90]
+ CRUSH rule 0 x 761 [78,97]
+ CRUSH rule 0 x 762 [87,8]
+ CRUSH rule 0 x 763 [13,45]
+ CRUSH rule 0 x 764 [106,81]
+ CRUSH rule 0 x 765 [109,91]
+ CRUSH rule 0 x 766 [76,97]
+ CRUSH rule 0 x 767 [41,116]
+ CRUSH rule 0 x 768 [13,114]
+ CRUSH rule 0 x 769 [91,104]
+ CRUSH rule 0 x 770 [105,96]
+ CRUSH rule 0 x 771 [10,76]
+ CRUSH rule 0 x 772 [8,111]
+ CRUSH rule 0 x 773 [116,75]
+ CRUSH rule 0 x 774 [100,43]
+ CRUSH rule 0 x 775 [15,34]
+ CRUSH rule 0 x 776 [69,38]
+ CRUSH rule 0 x 777 [76,49]
+ CRUSH rule 0 x 778 [38,13]
+ CRUSH rule 0 x 779 [46,21]
+ CRUSH rule 0 x 780 [63,102]
+ CRUSH rule 0 x 781 [19,85]
+ CRUSH rule 0 x 782 [117,31]
+ CRUSH rule 0 x 783 [60,93]
+ CRUSH rule 0 x 784 [82,81]
+ CRUSH rule 0 x 785 [27,84]
+ CRUSH rule 0 x 786 [41,80]
+ CRUSH rule 0 x 787 [13,54]
+ CRUSH rule 0 x 788 [4,100]
+ CRUSH rule 0 x 789 [50,37]
+ CRUSH rule 0 x 790 [58,16]
+ CRUSH rule 0 x 791 [96,97]
+ CRUSH rule 0 x 792 [45,4]
+ CRUSH rule 0 x 793 [6,71]
+ CRUSH rule 0 x 794 [14,89]
+ CRUSH rule 0 x 795 [51,108]
+ CRUSH rule 0 x 796 [114,77]
+ CRUSH rule 0 x 797 [79,100]
+ CRUSH rule 0 x 798 [42,8]
+ CRUSH rule 0 x 799 [48,11]
+ CRUSH rule 0 x 800 [91,7]
+ CRUSH rule 0 x 801 [2,6]
+ CRUSH rule 0 x 802 [116,14]
+ CRUSH rule 0 x 803 [37,32]
+ CRUSH rule 0 x 804 [6,73]
+ CRUSH rule 0 x 805 [96,22]
+ CRUSH rule 0 x 806 [67,90]
+ CRUSH rule 0 x 807 [47,42]
+ CRUSH rule 0 x 808 [76,31]
+ CRUSH rule 0 x 809 [27,26]
+ CRUSH rule 0 x 810 [119,61]
+ CRUSH rule 0 x 811 [75,72]
+ CRUSH rule 0 x 812 [25,52]
+ CRUSH rule 0 x 813 [64,13]
+ CRUSH rule 0 x 814 [110,53]
+ CRUSH rule 0 x 815 [84,67]
+ CRUSH rule 0 x 816 [25,22]
+ CRUSH rule 0 x 817 [40,29]
+ CRUSH rule 0 x 818 [34,85]
+ CRUSH rule 0 x 819 [88,17]
+ CRUSH rule 0 x 820 [104,49]
+ CRUSH rule 0 x 821 [58,69]
+ CRUSH rule 0 x 822 [29,72]
+ CRUSH rule 0 x 823 [100,103]
+ CRUSH rule 0 x 824 [102,81]
+ CRUSH rule 0 x 825 [47,17]
+ CRUSH rule 0 x 826 [45,11]
+ CRUSH rule 0 x 827 [101,11]
+ CRUSH rule 0 x 828 [60,27]
+ CRUSH rule 0 x 829 [45,90]
+ CRUSH rule 0 x 830 [51,96]
+ CRUSH rule 0 x 831 [6,102]
+ CRUSH rule 0 x 832 [57,78]
+ CRUSH rule 0 x 833 [34,97]
+ CRUSH rule 0 x 834 [90,33]
+ CRUSH rule 0 x 835 [55,46]
+ CRUSH rule 0 x 836 [38,43]
+ CRUSH rule 0 x 837 [51,74]
+ CRUSH rule 0 x 838 [6,32]
+ CRUSH rule 0 x 839 [106,8]
+ CRUSH rule 0 x 840 [33,109]
+ CRUSH rule 0 x 841 [110,15]
+ CRUSH rule 0 x 842 [66,67]
+ CRUSH rule 0 x 843 [62,63]
+ CRUSH rule 0 x 844 [74,13]
+ CRUSH rule 0 x 845 [74,43]
+ CRUSH rule 0 x 846 [98,107]
+ CRUSH rule 0 x 847 [10,94]
+ CRUSH rule 0 x 848 [89,17]
+ CRUSH rule 0 x 849 [42,59]
+ CRUSH rule 0 x 850 [40,73]
+ CRUSH rule 0 x 851 [65,4]
+ CRUSH rule 0 x 852 [31,94]
+ CRUSH rule 0 x 853 [49,11]
+ CRUSH rule 0 x 854 [83,54]
+ CRUSH rule 0 x 855 [2,19]
+ CRUSH rule 0 x 856 [6,107]
+ CRUSH rule 0 x 857 [15,82]
+ CRUSH rule 0 x 858 [10,80]
+ CRUSH rule 0 x 859 [14,43]
+ CRUSH rule 0 x 860 [114,75]
+ CRUSH rule 0 x 861 [1,33]
+ CRUSH rule 0 x 862 [22,25]
+ CRUSH rule 0 x 863 [79,0]
+ CRUSH rule 0 x 864 [68,6]
+ CRUSH rule 0 x 865 [25,92]
+ CRUSH rule 0 x 866 [18,89]
+ CRUSH rule 0 x 867 [53,78]
+ CRUSH rule 0 x 868 [81,98]
+ CRUSH rule 0 x 869 [111,11]
+ CRUSH rule 0 x 870 [73,19]
+ CRUSH rule 0 x 871 [25,54]
+ CRUSH rule 0 x 872 [39,48]
+ CRUSH rule 0 x 873 [92,8]
+ CRUSH rule 0 x 874 [96,11]
+ CRUSH rule 0 x 875 [115,59]
+ CRUSH rule 0 x 876 [98,75]
+ CRUSH rule 0 x 877 [73,5]
+ CRUSH rule 0 x 878 [64,45]
+ CRUSH rule 0 x 879 [15,18]
+ CRUSH rule 0 x 880 [56,91]
+ CRUSH rule 0 x 881 [109,69]
+ CRUSH rule 0 x 882 [60,33]
+ CRUSH rule 0 x 883 [93,96]
+ CRUSH rule 0 x 884 [67,38]
+ CRUSH rule 0 x 885 [31,115]
+ CRUSH rule 0 x 886 [2,9]
+ CRUSH rule 0 x 887 [5,7]
+ CRUSH rule 0 x 888 [16,13]
+ CRUSH rule 0 x 889 [27,76]
+ CRUSH rule 0 x 890 [48,63]
+ CRUSH rule 0 x 891 [86,79]
+ CRUSH rule 0 x 892 [64,107]
+ CRUSH rule 0 x 893 [118,22]
+ CRUSH rule 0 x 894 [16,111]
+ CRUSH rule 0 x 895 [40,3]
+ CRUSH rule 0 x 896 [97,96]
+ CRUSH rule 0 x 897 [107,78]
+ CRUSH rule 0 x 898 [10,2]
+ CRUSH rule 0 x 899 [75,15]
+ CRUSH rule 0 x 900 [102,81]
+ CRUSH rule 0 x 901 [66,87]
+ CRUSH rule 0 x 902 [102,49]
+ CRUSH rule 0 x 903 [5,69]
+ CRUSH rule 0 x 904 [50,16]
+ CRUSH rule 0 x 905 [99,76]
+ CRUSH rule 0 x 906 [75,119]
+ CRUSH rule 0 x 907 [47,5]
+ CRUSH rule 0 x 908 [96,37]
+ CRUSH rule 0 x 909 [94,75]
+ CRUSH rule 0 x 910 [88,63]
+ CRUSH rule 0 x 911 [102,23]
+ CRUSH rule 0 x 912 [91,60]
+ CRUSH rule 0 x 913 [29,17]
+ CRUSH rule 0 x 914 [84,14]
+ CRUSH rule 0 x 915 [70,27]
+ CRUSH rule 0 x 916 [32,9]
+ CRUSH rule 0 x 917 [43,108]
+ CRUSH rule 0 x 918 [91,98]
+ CRUSH rule 0 x 919 [13,69]
+ CRUSH rule 0 x 920 [18,9]
+ CRUSH rule 0 x 921 [104,33]
+ CRUSH rule 0 x 922 [33,32]
+ CRUSH rule 0 x 923 [28,8]
+ CRUSH rule 0 x 924 [69,88]
+ CRUSH rule 0 x 925 [71,32]
+ CRUSH rule 0 x 926 [64,69]
+ CRUSH rule 0 x 927 [99,17]
+ CRUSH rule 0 x 928 [13,113]
+ CRUSH rule 0 x 929 [117,61]
+ CRUSH rule 0 x 930 [31,82]
+ CRUSH rule 0 x 931 [83,66]
+ CRUSH rule 0 x 932 [60,13]
+ CRUSH rule 0 x 933 [63,82]
+ CRUSH rule 0 x 934 [68,4]
+ CRUSH rule 0 x 935 [31,18]
+ CRUSH rule 0 x 936 [65,32]
+ CRUSH rule 0 x 937 [110,79]
+ CRUSH rule 0 x 938 [29,106]
+ CRUSH rule 0 x 939 [77,13]
+ CRUSH rule 0 x 940 [76,15]
+ CRUSH rule 0 x 941 [66,37]
+ CRUSH rule 0 x 942 [83,94]
+ CRUSH rule 0 x 943 [32,4]
+ CRUSH rule 0 x 944 [113,4]
+ CRUSH rule 0 x 945 [71,52]
+ CRUSH rule 0 x 946 [37,70]
+ CRUSH rule 0 x 947 [107,74]
+ CRUSH rule 0 x 948 [55,98]
+ CRUSH rule 0 x 949 [11,72]
+ CRUSH rule 0 x 950 [96,23]
+ CRUSH rule 0 x 951 [40,93]
+ CRUSH rule 0 x 952 [93,46]
+ CRUSH rule 0 x 953 [55,92]
+ CRUSH rule 0 x 954 [84,57]
+ CRUSH rule 0 x 955 [31,117]
+ CRUSH rule 0 x 956 [72,11]
+ CRUSH rule 0 x 957 [3,74]
+ CRUSH rule 0 x 958 [23,106]
+ CRUSH rule 0 x 959 [42,59]
+ CRUSH rule 0 x 960 [113,107]
+ CRUSH rule 0 x 961 [116,8]
+ CRUSH rule 0 x 962 [13,62]
+ CRUSH rule 0 x 963 [0,99]
+ CRUSH rule 0 x 964 [59,56]
+ CRUSH rule 0 x 965 [47,115]
+ CRUSH rule 0 x 966 [88,63]
+ CRUSH rule 0 x 967 [71,15]
+ CRUSH rule 0 x 968 [73,7]
+ CRUSH rule 0 x 969 [53,6]
+ CRUSH rule 0 x 970 [111,75]
+ CRUSH rule 0 x 971 [87,22]
+ CRUSH rule 0 x 972 [5,37]
+ CRUSH rule 0 x 973 [113,27]
+ CRUSH rule 0 x 974 [49,112]
+ CRUSH rule 0 x 975 [83,58]
+ CRUSH rule 0 x 976 [81,38]
+ CRUSH rule 0 x 977 [95,102]
+ CRUSH rule 0 x 978 [35,56]
+ CRUSH rule 0 x 979 [98,6]
+ CRUSH rule 0 x 980 [52,69]
+ CRUSH rule 0 x 981 [89,117]
+ CRUSH rule 0 x 982 [1,47]
+ CRUSH rule 0 x 983 [34,61]
+ CRUSH rule 0 x 984 [78,25]
+ CRUSH rule 0 x 985 [99,52]
+ CRUSH rule 0 x 986 [4,59]
+ CRUSH rule 0 x 987 [78,21]
+ CRUSH rule 0 x 988 [79,2]
+ CRUSH rule 0 x 989 [87,17]
+ CRUSH rule 0 x 990 [47,118]
+ CRUSH rule 0 x 991 [61,18]
+ CRUSH rule 0 x 992 [83,66]
+ CRUSH rule 0 x 993 [74,53]
+ CRUSH rule 0 x 994 [74,57]
+ CRUSH rule 0 x 995 [100,97]
+ CRUSH rule 0 x 996 [41,6]
+ CRUSH rule 0 x 997 [89,76]
+ CRUSH rule 0 x 998 [92,47]
+ CRUSH rule 0 x 999 [117,11]
+ CRUSH rule 0 x 1000 [9,119]
+ CRUSH rule 0 x 1001 [49,32]
+ CRUSH rule 0 x 1002 [99,113]
+ CRUSH rule 0 x 1003 [43,18]
+ CRUSH rule 0 x 1004 [89,54]
+ CRUSH rule 0 x 1005 [105,84]
+ CRUSH rule 0 x 1006 [45,111]
+ CRUSH rule 0 x 1007 [19,57]
+ CRUSH rule 0 x 1008 [31,14]
+ CRUSH rule 0 x 1009 [19,111]
+ CRUSH rule 0 x 1010 [42,89]
+ CRUSH rule 0 x 1011 [25,114]
+ CRUSH rule 0 x 1012 [68,71]
+ CRUSH rule 0 x 1013 [5,45]
+ CRUSH rule 0 x 1014 [33,4]
+ CRUSH rule 0 x 1015 [14,45]
+ CRUSH rule 0 x 1016 [88,19]
+ CRUSH rule 0 x 1017 [0,89]
+ CRUSH rule 0 x 1018 [63,5]
+ CRUSH rule 0 x 1019 [104,97]
+ CRUSH rule 0 x 1020 [96,99]
+ CRUSH rule 0 x 1021 [117,41]
+ CRUSH rule 0 x 1022 [73,22]
+ CRUSH rule 0 x 1023 [0,16]
+ rule 0 (data) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 3 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 4 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 5 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 6 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 7 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 8 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 9 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,114,14]
+ CRUSH rule 0 x 1 [80,79,6]
+ CRUSH rule 0 x 2 [91,19,42]
+ CRUSH rule 0 x 3 [51,4,109]
+ CRUSH rule 0 x 4 [50,89,8]
+ CRUSH rule 0 x 5 [89,94,11]
+ CRUSH rule 0 x 6 [91,76,7]
+ CRUSH rule 0 x 7 [104,25,17]
+ CRUSH rule 0 x 8 [78,14,53]
+ CRUSH rule 0 x 9 [101,102,8]
+ CRUSH rule 0 x 10 [61,6,88]
+ CRUSH rule 0 x 11 [13,31,26]
+ CRUSH rule 0 x 12 [83,46,13]
+ CRUSH rule 0 x 13 [108,85,7]
+ CRUSH rule 0 x 14 [105,3,40]
+ CRUSH rule 0 x 15 [18,7,29]
+ CRUSH rule 0 x 16 [103,3,50]
+ CRUSH rule 0 x 17 [85,110,9]
+ CRUSH rule 0 x 18 [11,65,119]
+ CRUSH rule 0 x 19 [75,50,22]
+ CRUSH rule 0 x 20 [79,70,15]
+ CRUSH rule 0 x 21 [84,9,61]
+ CRUSH rule 0 x 22 [23,104,17]
+ CRUSH rule 0 x 23 [118,17,47]
+ CRUSH rule 0 x 24 [83,110,8]
+ CRUSH rule 0 x 25 [81,64,11]
+ CRUSH rule 0 x 26 [38,99,6]
+ CRUSH rule 0 x 27 [76,107,4]
+ CRUSH rule 0 x 28 [76,71,15]
+ CRUSH rule 0 x 29 [8,119,63]
+ CRUSH rule 0 x 30 [94,87,6]
+ CRUSH rule 0 x 31 [76,95,11]
+ CRUSH rule 0 x 32 [72,95,19]
+ CRUSH rule 0 x 33 [77,86,17]
+ CRUSH rule 0 x 34 [74,73,14]
+ CRUSH rule 0 x 35 [22,88,83]
+ CRUSH rule 0 x 36 [104,65,15]
+ CRUSH rule 0 x 37 [38,81,15]
+ CRUSH rule 0 x 38 [72,11,79]
+ CRUSH rule 0 x 39 [68,103,13]
+ CRUSH rule 0 x 40 [103,78,4]
+ CRUSH rule 0 x 41 [85,11,110]
+ CRUSH rule 0 x 42 [106,75,6]
+ CRUSH rule 0 x 43 [10,68,21]
+ CRUSH rule 0 x 44 [101,116,9]
+ CRUSH rule 0 x 45 [8,64,61]
+ CRUSH rule 0 x 46 [65,1,14]
+ CRUSH rule 0 x 47 [106,53,7]
+ CRUSH rule 0 x 48 [34,6,77]
+ CRUSH rule 0 x 49 [0,81,4]
+ CRUSH rule 0 x 50 [42,6,89]
+ CRUSH rule 0 x 51 [104,75,21]
+ CRUSH rule 0 x 52 [83,19,58]
+ CRUSH rule 0 x 53 [32,75,7]
+ CRUSH rule 0 x 54 [28,79,21]
+ CRUSH rule 0 x 55 [14,5,37]
+ CRUSH rule 0 x 56 [21,72,77]
+ CRUSH rule 0 x 57 [93,84,3]
+ CRUSH rule 0 x 58 [45,106,13]
+ CRUSH rule 0 x 59 [80,41,15]
+ CRUSH rule 0 x 60 [90,57,15]
+ CRUSH rule 0 x 61 [88,37,6]
+ CRUSH rule 0 x 62 [81,1,9]
+ CRUSH rule 0 x 63 [79,113,19]
+ CRUSH rule 0 x 64 [1,89,11]
+ CRUSH rule 0 x 65 [13,0,67]
+ CRUSH rule 0 x 66 [48,49,9]
+ CRUSH rule 0 x 67 [94,103,11]
+ CRUSH rule 0 x 68 [102,91,6]
+ CRUSH rule 0 x 69 [62,77,19]
+ CRUSH rule 0 x 70 [84,105,4]
+ CRUSH rule 0 x 71 [55,19,62]
+ CRUSH rule 0 x 72 [97,42,22]
+ CRUSH rule 0 x 73 [64,83,17]
+ CRUSH rule 0 x 74 [96,59,11]
+ CRUSH rule 0 x 75 [29,28,4]
+ CRUSH rule 0 x 76 [55,0,21]
+ CRUSH rule 0 x 77 [107,21,48]
+ CRUSH rule 0 x 78 [31,94,22]
+ CRUSH rule 0 x 79 [64,51,7]
+ CRUSH rule 0 x 80 [0,31,19]
+ CRUSH rule 0 x 81 [71,109,15]
+ CRUSH rule 0 x 82 [37,40,21]
+ CRUSH rule 0 x 83 [92,103,3]
+ CRUSH rule 0 x 84 [49,115,17]
+ CRUSH rule 0 x 85 [54,101,19]
+ CRUSH rule 0 x 86 [37,7,109]
+ CRUSH rule 0 x 87 [116,4,33]
+ CRUSH rule 0 x 88 [38,55,14]
+ CRUSH rule 0 x 89 [76,77,9]
+ CRUSH rule 0 x 90 [14,50,39]
+ CRUSH rule 0 x 91 [93,34,19]
+ CRUSH rule 0 x 92 [86,9,73]
+ CRUSH rule 0 x 93 [44,65,19]
+ CRUSH rule 0 x 94 [61,102,21]
+ CRUSH rule 0 x 95 [93,86,21]
+ CRUSH rule 0 x 96 [66,87,21]
+ CRUSH rule 0 x 97 [111,9,89]
+ CRUSH rule 0 x 98 [66,91,6]
+ CRUSH rule 0 x 99 [78,3,81]
+ CRUSH rule 0 x 100 [28,8,87]
+ CRUSH rule 0 x 101 [84,16,17]
+ CRUSH rule 0 x 102 [82,105,19]
+ CRUSH rule 0 x 103 [66,6,49]
+ CRUSH rule 0 x 104 [14,95,50]
+ CRUSH rule 0 x 105 [87,1,15]
+ CRUSH rule 0 x 106 [69,116,15]
+ CRUSH rule 0 x 107 [1,55,4]
+ CRUSH rule 0 x 108 [94,53,4]
+ CRUSH rule 0 x 109 [112,63,13]
+ CRUSH rule 0 x 110 [54,61,9]
+ CRUSH rule 0 x 111 [10,58,7]
+ CRUSH rule 0 x 112 [89,9,98]
+ CRUSH rule 0 x 113 [69,2,21]
+ CRUSH rule 0 x 114 [79,17,117]
+ CRUSH rule 0 x 115 [50,85,19]
+ CRUSH rule 0 x 116 [96,16,14]
+ CRUSH rule 0 x 117 [87,56,22]
+ CRUSH rule 0 x 118 [23,56,21]
+ CRUSH rule 0 x 119 [104,11,71]
+ CRUSH rule 0 x 120 [57,5,22]
+ CRUSH rule 0 x 121 [105,117,19]
+ CRUSH rule 0 x 122 [45,110,4]
+ CRUSH rule 0 x 123 [112,22,61]
+ CRUSH rule 0 x 124 [110,11,81]
+ CRUSH rule 0 x 125 [66,105,13]
+ CRUSH rule 0 x 126 [51,28,4]
+ CRUSH rule 0 x 127 [70,6,65]
+ CRUSH rule 0 x 128 [90,16,8]
+ CRUSH rule 0 x 129 [103,110,21]
+ CRUSH rule 0 x 130 [50,11,63]
+ CRUSH rule 0 x 131 [23,60,7]
+ CRUSH rule 0 x 132 [69,70,13]
+ CRUSH rule 0 x 133 [52,25,6]
+ CRUSH rule 0 x 134 [78,6,99]
+ CRUSH rule 0 x 135 [78,3,29]
+ CRUSH rule 0 x 136 [32,29,7]
+ CRUSH rule 0 x 137 [92,41,15]
+ CRUSH rule 0 x 138 [17,118,85]
+ CRUSH rule 0 x 139 [89,60,22]
+ CRUSH rule 0 x 140 [39,62,13]
+ CRUSH rule 0 x 141 [89,98,3]
+ CRUSH rule 0 x 142 [70,61,19]
+ CRUSH rule 0 x 143 [51,28,7]
+ CRUSH rule 0 x 144 [13,81,60]
+ CRUSH rule 0 x 145 [77,119,17]
+ CRUSH rule 0 x 146 [96,69,3]
+ CRUSH rule 0 x 147 [2,95,22]
+ CRUSH rule 0 x 148 [74,69,11]
+ CRUSH rule 0 x 149 [76,13,81]
+ CRUSH rule 0 x 150 [38,47,14]
+ CRUSH rule 0 x 151 [90,67,9]
+ CRUSH rule 0 x 152 [49,18,22]
+ CRUSH rule 0 x 153 [71,44,21]
+ CRUSH rule 0 x 154 [94,81,13]
+ CRUSH rule 0 x 155 [75,112,11]
+ CRUSH rule 0 x 156 [107,66,7]
+ CRUSH rule 0 x 157 [112,43,3]
+ CRUSH rule 0 x 158 [26,17,99]
+ CRUSH rule 0 x 159 [52,9,47]
+ CRUSH rule 0 x 160 [41,0,7]
+ CRUSH rule 0 x 161 [84,45,3]
+ CRUSH rule 0 x 162 [55,2,9]
+ CRUSH rule 0 x 163 [54,8,55]
+ CRUSH rule 0 x 164 [45,5,14]
+ CRUSH rule 0 x 165 [25,72,9]
+ CRUSH rule 0 x 166 [73,36,7]
+ CRUSH rule 0 x 167 [89,58,14]
+ CRUSH rule 0 x 168 [47,40,13]
+ CRUSH rule 0 x 169 [51,21,0]
+ CRUSH rule 0 x 170 [68,91,14]
+ CRUSH rule 0 x 171 [73,90,19]
+ CRUSH rule 0 x 172 [117,41,4]
+ CRUSH rule 0 x 173 [13,34,99]
+ CRUSH rule 0 x 174 [116,25,7]
+ CRUSH rule 0 x 175 [3,41,102]
+ CRUSH rule 0 x 176 [94,91,13]
+ CRUSH rule 0 x 177 [52,85,14]
+ CRUSH rule 0 x 178 [39,2,15]
+ CRUSH rule 0 x 179 [72,97,15]
+ CRUSH rule 0 x 180 [60,61,15]
+ CRUSH rule 0 x 181 [18,59,19]
+ CRUSH rule 0 x 182 [22,90,25]
+ CRUSH rule 0 x 183 [11,74,103]
+ CRUSH rule 0 x 184 [92,8,55]
+ CRUSH rule 0 x 185 [97,8,24]
+ CRUSH rule 0 x 186 [67,116,21]
+ CRUSH rule 0 x 187 [116,11,61]
+ CRUSH rule 0 x 188 [69,110,8]
+ CRUSH rule 0 x 189 [47,84,21]
+ CRUSH rule 0 x 190 [65,82,6]
+ CRUSH rule 0 x 191 [49,38,22]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,33,6]
+ CRUSH rule 0 x 194 [62,99,8]
+ CRUSH rule 0 x 195 [119,4,33]
+ CRUSH rule 0 x 196 [72,27,22]
+ CRUSH rule 0 x 197 [106,83,22]
+ CRUSH rule 0 x 198 [114,21,77]
+ CRUSH rule 0 x 199 [0,83,11]
+ CRUSH rule 0 x 200 [35,86,4]
+ CRUSH rule 0 x 201 [27,26,8]
+ CRUSH rule 0 x 202 [98,33,17]
+ CRUSH rule 0 x 203 [36,91,11]
+ CRUSH rule 0 x 204 [10,98,15]
+ CRUSH rule 0 x 205 [81,17,72]
+ CRUSH rule 0 x 206 [49,112,13]
+ CRUSH rule 0 x 207 [80,39,14]
+ CRUSH rule 0 x 208 [63,26,7]
+ CRUSH rule 0 x 209 [85,111,8]
+ CRUSH rule 0 x 210 [79,18,4]
+ CRUSH rule 0 x 211 [26,10,19]
+ CRUSH rule 0 x 212 [28,103,15]
+ CRUSH rule 0 x 213 [91,0,8]
+ CRUSH rule 0 x 214 [91,38,13]
+ CRUSH rule 0 x 215 [61,86,22]
+ CRUSH rule 0 x 216 [99,94,4]
+ CRUSH rule 0 x 217 [86,89,15]
+ CRUSH rule 0 x 218 [70,4,79]
+ CRUSH rule 0 x 219 [28,59,9]
+ CRUSH rule 0 x 220 [56,8,83]
+ CRUSH rule 0 x 221 [0,9,71]
+ CRUSH rule 0 x 222 [50,63,21]
+ CRUSH rule 0 x 223 [29,1,3]
+ CRUSH rule 0 x 224 [52,10,19]
+ CRUSH rule 0 x 225 [15,35,64]
+ CRUSH rule 0 x 226 [44,7,93]
+ CRUSH rule 0 x 227 [42,3,81]
+ CRUSH rule 0 x 228 [117,49,4]
+ CRUSH rule 0 x 229 [100,79,4]
+ CRUSH rule 0 x 230 [41,114,13]
+ CRUSH rule 0 x 231 [56,95,13]
+ CRUSH rule 0 x 232 [23,8,1]
+ CRUSH rule 0 x 233 [88,103,6]
+ CRUSH rule 0 x 234 [4,101,18]
+ CRUSH rule 0 x 235 [26,10,7]
+ CRUSH rule 0 x 236 [32,37,3]
+ CRUSH rule 0 x 237 [92,3,61]
+ CRUSH rule 0 x 238 [10,26,22]
+ CRUSH rule 0 x 239 [15,105,2]
+ CRUSH rule 0 x 240 [109,14,41]
+ CRUSH rule 0 x 241 [47,108,11]
+ CRUSH rule 0 x 242 [24,99,9]
+ CRUSH rule 0 x 243 [76,8,99]
+ CRUSH rule 0 x 244 [103,13,78]
+ CRUSH rule 0 x 245 [27,82,19]
+ CRUSH rule 0 x 246 [5,43,19]
+ CRUSH rule 0 x 247 [99,102,4]
+ CRUSH rule 0 x 248 [8,29,42]
+ CRUSH rule 0 x 249 [85,1,14]
+ CRUSH rule 0 x 250 [93,102,4]
+ CRUSH rule 0 x 251 [28,103,19]
+ CRUSH rule 0 x 252 [95,22,92]
+ CRUSH rule 0 x 253 [109,27,17]
+ CRUSH rule 0 x 254 [80,13,23]
+ CRUSH rule 0 x 255 [103,13,119]
+ CRUSH rule 0 x 256 [37,38,14]
+ CRUSH rule 0 x 257 [69,117,15]
+ CRUSH rule 0 x 258 [34,55,17]
+ CRUSH rule 0 x 259 [70,17,91]
+ CRUSH rule 0 x 260 [98,29,4]
+ CRUSH rule 0 x 261 [94,83,7]
+ CRUSH rule 0 x 262 [42,49,14]
+ CRUSH rule 0 x 263 [65,42,14]
+ CRUSH rule 0 x 264 [36,49,11]
+ CRUSH rule 0 x 265 [66,63,4]
+ CRUSH rule 0 x 266 [75,92,15]
+ CRUSH rule 0 x 267 [58,35,6]
+ CRUSH rule 0 x 268 [38,9,63]
+ CRUSH rule 0 x 269 [86,59,7]
+ CRUSH rule 0 x 270 [58,37,8]
+ CRUSH rule 0 x 271 [19,33,114]
+ CRUSH rule 0 x 272 [73,9,100]
+ CRUSH rule 0 x 273 [108,29,7]
+ CRUSH rule 0 x 274 [47,64,22]
+ CRUSH rule 0 x 275 [92,19,59]
+ CRUSH rule 0 x 276 [7,79,118]
+ CRUSH rule 0 x 277 [19,68,10]
+ CRUSH rule 0 x 278 [116,105,19]
+ CRUSH rule 0 x 279 [101,3,76]
+ CRUSH rule 0 x 280 [113,69,14]
+ CRUSH rule 0 x 281 [14,93,96]
+ CRUSH rule 0 x 282 [106,61,13]
+ CRUSH rule 0 x 283 [8,118,101]
+ CRUSH rule 0 x 284 [10,110,22]
+ CRUSH rule 0 x 285 [88,63,8]
+ CRUSH rule 0 x 286 [27,4,18]
+ CRUSH rule 0 x 287 [84,65,4]
+ CRUSH rule 0 x 288 [103,8,70]
+ CRUSH rule 0 x 289 [9,64,75]
+ CRUSH rule 0 x 290 [115,17,77]
+ CRUSH rule 0 x 291 [48,45,13]
+ CRUSH rule 0 x 292 [89,109,14]
+ CRUSH rule 0 x 293 [27,24,17]
+ CRUSH rule 0 x 294 [79,36,13]
+ CRUSH rule 0 x 295 [37,116,7]
+ CRUSH rule 0 x 296 [56,61,8]
+ CRUSH rule 0 x 297 [35,40,15]
+ CRUSH rule 0 x 298 [71,118,21]
+ CRUSH rule 0 x 299 [116,61,21]
+ CRUSH rule 0 x 300 [67,5,14]
+ CRUSH rule 0 x 301 [51,110,8]
+ CRUSH rule 0 x 302 [78,67,19]
+ CRUSH rule 0 x 303 [19,94,31]
+ CRUSH rule 0 x 304 [101,66,3]
+ CRUSH rule 0 x 305 [81,62,6]
+ CRUSH rule 0 x 306 [0,23,9]
+ CRUSH rule 0 x 307 [44,15,87]
+ CRUSH rule 0 x 308 [91,98,17]
+ CRUSH rule 0 x 309 [38,63,22]
+ CRUSH rule 0 x 310 [26,89,11]
+ CRUSH rule 0 x 311 [36,83,9]
+ CRUSH rule 0 x 312 [33,22,113]
+ CRUSH rule 0 x 313 [104,16,11]
+ CRUSH rule 0 x 314 [28,4,23]
+ CRUSH rule 0 x 315 [16,117,17]
+ CRUSH rule 0 x 316 [4,1,81]
+ CRUSH rule 0 x 317 [118,8,103]
+ CRUSH rule 0 x 318 [17,47,50]
+ CRUSH rule 0 x 319 [24,83,4]
+ CRUSH rule 0 x 320 [36,65,19]
+ CRUSH rule 0 x 321 [26,85,11]
+ CRUSH rule 0 x 322 [87,116,21]
+ CRUSH rule 0 x 323 [73,0,9]
+ CRUSH rule 0 x 324 [21,37,64]
+ CRUSH rule 0 x 325 [52,16,19]
+ CRUSH rule 0 x 326 [111,93,14]
+ CRUSH rule 0 x 327 [62,8,63]
+ CRUSH rule 0 x 328 [7,42,67]
+ CRUSH rule 0 x 329 [93,34,11]
+ CRUSH rule 0 x 330 [24,4,63]
+ CRUSH rule 0 x 331 [41,117,9]
+ CRUSH rule 0 x 332 [61,110,3]
+ CRUSH rule 0 x 333 [16,8,116]
+ CRUSH rule 0 x 334 [3,35,86]
+ CRUSH rule 0 x 335 [71,74,17]
+ CRUSH rule 0 x 336 [16,19,66]
+ CRUSH rule 0 x 337 [37,40,11]
+ CRUSH rule 0 x 338 [109,13,45]
+ CRUSH rule 0 x 339 [37,21,56]
+ CRUSH rule 0 x 340 [119,67,7]
+ CRUSH rule 0 x 341 [63,8,60]
+ CRUSH rule 0 x 342 [92,25,17]
+ CRUSH rule 0 x 343 [49,26,13]
+ CRUSH rule 0 x 344 [103,26,3]
+ CRUSH rule 0 x 345 [56,25,8]
+ CRUSH rule 0 x 346 [3,79,24]
+ CRUSH rule 0 x 347 [106,27,21]
+ CRUSH rule 0 x 348 [10,117,19]
+ CRUSH rule 0 x 349 [96,37,8]
+ CRUSH rule 0 x 350 [63,32,9]
+ CRUSH rule 0 x 351 [60,85,22]
+ CRUSH rule 0 x 352 [103,84,17]
+ CRUSH rule 0 x 353 [49,113,11]
+ CRUSH rule 0 x 354 [55,52,11]
+ CRUSH rule 0 x 355 [73,68,17]
+ CRUSH rule 0 x 356 [114,41,15]
+ CRUSH rule 0 x 357 [14,96,75]
+ CRUSH rule 0 x 358 [97,62,6]
+ CRUSH rule 0 x 359 [4,105,86]
+ CRUSH rule 0 x 360 [106,69,4]
+ CRUSH rule 0 x 361 [27,46,17]
+ CRUSH rule 0 x 362 [28,33,17]
+ CRUSH rule 0 x 363 [45,26,6]
+ CRUSH rule 0 x 364 [23,50,4]
+ CRUSH rule 0 x 365 [24,22,93]
+ CRUSH rule 0 x 366 [14,58,16]
+ CRUSH rule 0 x 367 [103,50,22]
+ CRUSH rule 0 x 368 [103,32,3]
+ CRUSH rule 0 x 369 [37,4,110]
+ CRUSH rule 0 x 370 [11,89,66]
+ CRUSH rule 0 x 371 [34,55,19]
+ CRUSH rule 0 x 372 [58,10,9]
+ CRUSH rule 0 x 373 [98,8,27]
+ CRUSH rule 0 x 374 [110,95,4]
+ CRUSH rule 0 x 375 [19,92,99]
+ CRUSH rule 0 x 376 [22,86,91]
+ CRUSH rule 0 x 377 [98,105,8]
+ CRUSH rule 0 x 378 [67,36,19]
+ CRUSH rule 0 x 379 [77,8,68]
+ CRUSH rule 0 x 380 [69,104,3]
+ CRUSH rule 0 x 381 [55,1,22]
+ CRUSH rule 0 x 382 [26,51,17]
+ CRUSH rule 0 x 383 [48,25,13]
+ CRUSH rule 0 x 384 [15,100,81]
+ CRUSH rule 0 x 385 [82,61,13]
+ CRUSH rule 0 x 386 [108,63,11]
+ CRUSH rule 0 x 387 [70,15,83]
+ CRUSH rule 0 x 388 [5,67,19]
+ CRUSH rule 0 x 389 [14,29,98]
+ CRUSH rule 0 x 390 [68,10,13]
+ CRUSH rule 0 x 391 [113,69,7]
+ CRUSH rule 0 x 392 [72,14,77]
+ CRUSH rule 0 x 393 [115,6,81]
+ CRUSH rule 0 x 394 [38,21,16]
+ CRUSH rule 0 x 395 [0,27,13]
+ CRUSH rule 0 x 396 [59,92,11]
+ CRUSH rule 0 x 397 [87,1,3]
+ CRUSH rule 0 x 398 [44,75,14]
+ CRUSH rule 0 x 399 [9,2,95]
+ CRUSH rule 0 x 400 [101,102,15]
+ CRUSH rule 0 x 401 [79,34,13]
+ CRUSH rule 0 x 402 [107,98,14]
+ CRUSH rule 0 x 403 [23,82,13]
+ CRUSH rule 0 x 404 [76,75,7]
+ CRUSH rule 0 x 405 [10,32,15]
+ CRUSH rule 0 x 406 [38,16,11]
+ CRUSH rule 0 x 407 [70,85,9]
+ CRUSH rule 0 x 408 [55,72,9]
+ CRUSH rule 0 x 409 [102,15,73]
+ CRUSH rule 0 x 410 [59,13,118]
+ CRUSH rule 0 x 411 [34,29,6]
+ CRUSH rule 0 x 412 [108,99,11]
+ CRUSH rule 0 x 413 [54,107,8]
+ CRUSH rule 0 x 414 [70,4,73]
+ CRUSH rule 0 x 415 [107,36,13]
+ CRUSH rule 0 x 416 [79,68,15]
+ CRUSH rule 0 x 417 [8,79,34]
+ CRUSH rule 0 x 418 [51,46,3]
+ CRUSH rule 0 x 419 [117,16,22]
+ CRUSH rule 0 x 420 [109,105,3]
+ CRUSH rule 0 x 421 [114,17,67]
+ CRUSH rule 0 x 422 [109,8,31]
+ CRUSH rule 0 x 423 [59,98,7]
+ CRUSH rule 0 x 424 [71,5,17]
+ CRUSH rule 0 x 425 [101,111,15]
+ CRUSH rule 0 x 426 [47,46,19]
+ CRUSH rule 0 x 427 [86,87,7]
+ CRUSH rule 0 x 428 [68,35,21]
+ CRUSH rule 0 x 429 [76,6,75]
+ CRUSH rule 0 x 430 [9,86,83]
+ CRUSH rule 0 x 431 [105,119,15]
+ CRUSH rule 0 x 432 [46,37,6]
+ CRUSH rule 0 x 433 [6,101,68]
+ CRUSH rule 0 x 434 [64,69,4]
+ CRUSH rule 0 x 435 [16,50,14]
+ CRUSH rule 0 x 436 [89,102,21]
+ CRUSH rule 0 x 437 [29,114,14]
+ CRUSH rule 0 x 438 [105,98,13]
+ CRUSH rule 0 x 439 [29,119,7]
+ CRUSH rule 0 x 440 [38,13,87]
+ CRUSH rule 0 x 441 [112,105,13]
+ CRUSH rule 0 x 442 [55,108,21]
+ CRUSH rule 0 x 443 [44,57,7]
+ CRUSH rule 0 x 444 [11,27,118]
+ CRUSH rule 0 x 445 [19,5,39]
+ CRUSH rule 0 x 446 [40,47,22]
+ CRUSH rule 0 x 447 [100,61,7]
+ CRUSH rule 0 x 448 [7,68,55]
+ CRUSH rule 0 x 449 [67,19,66]
+ CRUSH rule 0 x 450 [117,101,8]
+ CRUSH rule 0 x 451 [93,108,8]
+ CRUSH rule 0 x 452 [70,49,7]
+ CRUSH rule 0 x 453 [82,51,21]
+ CRUSH rule 0 x 454 [53,18,3]
+ CRUSH rule 0 x 455 [91,92,11]
+ CRUSH rule 0 x 456 [17,16,0]
+ CRUSH rule 0 x 457 [113,31,11]
+ CRUSH rule 0 x 458 [119,43,8]
+ CRUSH rule 0 x 459 [25,115,22]
+ CRUSH rule 0 x 460 [11,97,117]
+ CRUSH rule 0 x 461 [21,111,63]
+ CRUSH rule 0 x 462 [25,62,22]
+ CRUSH rule 0 x 463 [6,105,94]
+ CRUSH rule 0 x 464 [19,109,105]
+ CRUSH rule 0 x 465 [29,86,6]
+ CRUSH rule 0 x 466 [66,91,21]
+ CRUSH rule 0 x 467 [27,68,3]
+ CRUSH rule 0 x 468 [97,26,7]
+ CRUSH rule 0 x 469 [98,75,11]
+ CRUSH rule 0 x 470 [50,67,4]
+ CRUSH rule 0 x 471 [40,79,8]
+ CRUSH rule 0 x 472 [74,79,6]
+ CRUSH rule 0 x 473 [95,36,8]
+ CRUSH rule 0 x 474 [51,14,118]
+ CRUSH rule 0 x 475 [3,79,62]
+ CRUSH rule 0 x 476 [110,31,11]
+ CRUSH rule 0 x 477 [25,106,7]
+ CRUSH rule 0 x 478 [19,105,68]
+ CRUSH rule 0 x 479 [70,37,6]
+ CRUSH rule 0 x 480 [62,57,6]
+ CRUSH rule 0 x 481 [26,19,49]
+ CRUSH rule 0 x 482 [84,14,107]
+ CRUSH rule 0 x 483 [36,53,13]
+ CRUSH rule 0 x 484 [37,36,9]
+ CRUSH rule 0 x 485 [84,15,51]
+ CRUSH rule 0 x 486 [92,10,14]
+ CRUSH rule 0 x 487 [106,51,11]
+ CRUSH rule 0 x 488 [42,43,8]
+ CRUSH rule 0 x 489 [76,16,21]
+ CRUSH rule 0 x 490 [68,87,14]
+ CRUSH rule 0 x 491 [80,71,6]
+ CRUSH rule 0 x 492 [21,57,86]
+ CRUSH rule 0 x 493 [99,78,6]
+ CRUSH rule 0 x 494 [4,87,86]
+ CRUSH rule 0 x 495 [40,43,9]
+ CRUSH rule 0 x 496 [13,38,89]
+ CRUSH rule 0 x 497 [102,71,6]
+ CRUSH rule 0 x 498 [68,83,11]
+ CRUSH rule 0 x 499 [22,26,39]
+ CRUSH rule 0 x 500 [50,6,95]
+ CRUSH rule 0 x 501 [60,79,9]
+ CRUSH rule 0 x 502 [11,28,53]
+ CRUSH rule 0 x 503 [117,25,14]
+ CRUSH rule 0 x 504 [90,41,19]
+ CRUSH rule 0 x 505 [91,100,15]
+ CRUSH rule 0 x 506 [82,103,22]
+ CRUSH rule 0 x 507 [6,103,108]
+ CRUSH rule 0 x 508 [34,87,19]
+ CRUSH rule 0 x 509 [88,63,8]
+ CRUSH rule 0 x 510 [11,73,42]
+ CRUSH rule 0 x 511 [72,27,6]
+ CRUSH rule 0 x 512 [118,73,15]
+ CRUSH rule 0 x 513 [22,76,77]
+ CRUSH rule 0 x 514 [82,15,37]
+ CRUSH rule 0 x 515 [27,0,22]
+ CRUSH rule 0 x 516 [66,85,6]
+ CRUSH rule 0 x 517 [83,4,32]
+ CRUSH rule 0 x 518 [18,3,83]
+ CRUSH rule 0 x 519 [67,119,14]
+ CRUSH rule 0 x 520 [15,114,53]
+ CRUSH rule 0 x 521 [63,113,7]
+ CRUSH rule 0 x 522 [4,73,110]
+ CRUSH rule 0 x 523 [36,35,19]
+ CRUSH rule 0 x 524 [33,38,13]
+ CRUSH rule 0 x 525 [63,119,11]
+ CRUSH rule 0 x 526 [83,50,9]
+ CRUSH rule 0 x 527 [37,0,11]
+ CRUSH rule 0 x 528 [108,35,15]
+ CRUSH rule 0 x 529 [107,15,1]
+ CRUSH rule 0 x 530 [49,3,56]
+ CRUSH rule 0 x 531 [27,7,94]
+ CRUSH rule 0 x 532 [68,71,8]
+ CRUSH rule 0 x 533 [5,85,3]
+ CRUSH rule 0 x 534 [97,24,19]
+ CRUSH rule 0 x 535 [48,75,3]
+ CRUSH rule 0 x 536 [3,37,86]
+ CRUSH rule 0 x 537 [116,7,59]
+ CRUSH rule 0 x 538 [85,8,74]
+ CRUSH rule 0 x 539 [10,9,76]
+ CRUSH rule 0 x 540 [100,101,14]
+ CRUSH rule 0 x 541 [111,77,21]
+ CRUSH rule 0 x 542 [50,27,8]
+ CRUSH rule 0 x 543 [45,21,109]
+ CRUSH rule 0 x 544 [106,93,21]
+ CRUSH rule 0 x 545 [43,114,17]
+ CRUSH rule 0 x 546 [108,79,17]
+ CRUSH rule 0 x 547 [27,50,4]
+ CRUSH rule 0 x 548 [53,82,6]
+ CRUSH rule 0 x 549 [60,37,9]
+ CRUSH rule 0 x 550 [47,68,21]
+ CRUSH rule 0 x 551 [14,88,27]
+ CRUSH rule 0 x 552 [70,73,3]
+ CRUSH rule 0 x 553 [96,105,11]
+ CRUSH rule 0 x 554 [61,0,11]
+ CRUSH rule 0 x 555 [76,37,9]
+ CRUSH rule 0 x 556 [106,89,9]
+ CRUSH rule 0 x 557 [39,113,17]
+ CRUSH rule 0 x 558 [70,79,8]
+ CRUSH rule 0 x 559 [106,69,14]
+ CRUSH rule 0 x 560 [94,43,8]
+ CRUSH rule 0 x 561 [27,76,14]
+ CRUSH rule 0 x 562 [97,62,7]
+ CRUSH rule 0 x 563 [64,103,4]
+ CRUSH rule 0 x 564 [96,41,14]
+ CRUSH rule 0 x 565 [66,71,14]
+ CRUSH rule 0 x 566 [27,38,7]
+ CRUSH rule 0 x 567 [88,8,73]
+ CRUSH rule 0 x 568 [17,96,33]
+ CRUSH rule 0 x 569 [102,63,17]
+ CRUSH rule 0 x 570 [7,27,108]
+ CRUSH rule 0 x 571 [95,98,4]
+ CRUSH rule 0 x 572 [62,89,8]
+ CRUSH rule 0 x 573 [51,118,4]
+ CRUSH rule 0 x 574 [89,78,13]
+ CRUSH rule 0 x 575 [19,101,38]
+ CRUSH rule 0 x 576 [112,73,9]
+ CRUSH rule 0 x 577 [8,84,41]
+ CRUSH rule 0 x 578 [64,8,41]
+ CRUSH rule 0 x 579 [78,77,8]
+ CRUSH rule 0 x 580 [68,95,6]
+ CRUSH rule 0 x 581 [55,52,14]
+ CRUSH rule 0 x 582 [27,13,40]
+ CRUSH rule 0 x 583 [74,105,7]
+ CRUSH rule 0 x 584 [72,13,99]
+ CRUSH rule 0 x 585 [88,99,7]
+ CRUSH rule 0 x 586 [33,108,4]
+ CRUSH rule 0 x 587 [106,99,22]
+ CRUSH rule 0 x 588 [0,83,7]
+ CRUSH rule 0 x 589 [7,95,90]
+ CRUSH rule 0 x 590 [59,112,17]
+ CRUSH rule 0 x 591 [42,23,3]
+ CRUSH rule 0 x 592 [45,72,22]
+ CRUSH rule 0 x 593 [89,14,42]
+ CRUSH rule 0 x 594 [27,76,9]
+ CRUSH rule 0 x 595 [7,10,110]
+ CRUSH rule 0 x 596 [82,59,19]
+ CRUSH rule 0 x 597 [72,83,14]
+ CRUSH rule 0 x 598 [34,15,57]
+ CRUSH rule 0 x 599 [119,61,7]
+ CRUSH rule 0 x 600 [9,84,49]
+ CRUSH rule 0 x 601 [104,15,49]
+ CRUSH rule 0 x 602 [48,45,6]
+ CRUSH rule 0 x 603 [24,9,89]
+ CRUSH rule 0 x 604 [89,0,14]
+ CRUSH rule 0 x 605 [104,87,14]
+ CRUSH rule 0 x 606 [49,34,8]
+ CRUSH rule 0 x 607 [95,40,15]
+ CRUSH rule 0 x 608 [49,80,6]
+ CRUSH rule 0 x 609 [61,66,11]
+ CRUSH rule 0 x 610 [106,16,6]
+ CRUSH rule 0 x 611 [66,87,15]
+ CRUSH rule 0 x 612 [103,8,44]
+ CRUSH rule 0 x 613 [84,91,8]
+ CRUSH rule 0 x 614 [81,7,108]
+ CRUSH rule 0 x 615 [61,19,64]
+ CRUSH rule 0 x 616 [41,15,106]
+ CRUSH rule 0 x 617 [111,69,15]
+ CRUSH rule 0 x 618 [3,99,26]
+ CRUSH rule 0 x 619 [92,27,13]
+ CRUSH rule 0 x 620 [108,103,19]
+ CRUSH rule 0 x 621 [106,99,3]
+ CRUSH rule 0 x 622 [67,48,13]
+ CRUSH rule 0 x 623 [94,3,73]
+ CRUSH rule 0 x 624 [115,59,15]
+ CRUSH rule 0 x 625 [111,27,7]
+ CRUSH rule 0 x 626 [3,55,80]
+ CRUSH rule 0 x 627 [19,29,102]
+ CRUSH rule 0 x 628 [65,88,14]
+ CRUSH rule 0 x 629 [119,7,87]
+ CRUSH rule 0 x 630 [109,19,55]
+ CRUSH rule 0 x 631 [48,75,15]
+ CRUSH rule 0 x 632 [81,0,3]
+ CRUSH rule 0 x 633 [65,68,15]
+ CRUSH rule 0 x 634 [87,50,9]
+ CRUSH rule 0 x 635 [107,9,109]
+ CRUSH rule 0 x 636 [23,78,9]
+ CRUSH rule 0 x 637 [102,45,3]
+ CRUSH rule 0 x 638 [43,114,19]
+ CRUSH rule 0 x 639 [31,78,14]
+ CRUSH rule 0 x 640 [113,73,22]
+ CRUSH rule 0 x 641 [45,96,14]
+ CRUSH rule 0 x 642 [47,66,3]
+ CRUSH rule 0 x 643 [64,47,21]
+ CRUSH rule 0 x 644 [31,21,119]
+ CRUSH rule 0 x 645 [76,19,53]
+ CRUSH rule 0 x 646 [37,54,8]
+ CRUSH rule 0 x 647 [58,87,19]
+ CRUSH rule 0 x 648 [31,21,102]
+ CRUSH rule 0 x 649 [88,45,21]
+ CRUSH rule 0 x 650 [116,7,107]
+ CRUSH rule 0 x 651 [97,106,14]
+ CRUSH rule 0 x 652 [57,112,9]
+ CRUSH rule 0 x 653 [38,39,21]
+ CRUSH rule 0 x 654 [49,32,21]
+ CRUSH rule 0 x 655 [89,62,21]
+ CRUSH rule 0 x 656 [0,49,19]
+ CRUSH rule 0 x 657 [47,32,22]
+ CRUSH rule 0 x 658 [75,82,4]
+ CRUSH rule 0 x 659 [26,83,9]
+ CRUSH rule 0 x 660 [65,110,13]
+ CRUSH rule 0 x 661 [91,48,15]
+ CRUSH rule 0 x 662 [111,99,17]
+ CRUSH rule 0 x 663 [88,35,3]
+ CRUSH rule 0 x 664 [59,78,8]
+ CRUSH rule 0 x 665 [78,15,67]
+ CRUSH rule 0 x 666 [112,4,61]
+ CRUSH rule 0 x 667 [97,46,8]
+ CRUSH rule 0 x 668 [97,15,56]
+ CRUSH rule 0 x 669 [85,66,22]
+ CRUSH rule 0 x 670 [41,38,14]
+ CRUSH rule 0 x 671 [116,97,4]
+ CRUSH rule 0 x 672 [44,55,17]
+ CRUSH rule 0 x 673 [83,50,14]
+ CRUSH rule 0 x 674 [59,78,7]
+ CRUSH rule 0 x 675 [88,14,43]
+ CRUSH rule 0 x 676 [62,8,99]
+ CRUSH rule 0 x 677 [88,67,17]
+ CRUSH rule 0 x 678 [98,83,13]
+ CRUSH rule 0 x 679 [70,59,15]
+ CRUSH rule 0 x 680 [55,96,17]
+ CRUSH rule 0 x 681 [53,68,17]
+ CRUSH rule 0 x 682 [27,58,13]
+ CRUSH rule 0 x 683 [57,80,19]
+ CRUSH rule 0 x 684 [98,65,3]
+ CRUSH rule 0 x 685 [106,55,13]
+ CRUSH rule 0 x 686 [86,95,15]
+ CRUSH rule 0 x 687 [49,72,3]
+ CRUSH rule 0 x 688 [16,114,22]
+ CRUSH rule 0 x 689 [6,48,71]
+ CRUSH rule 0 x 690 [43,70,14]
+ CRUSH rule 0 x 691 [34,105,8]
+ CRUSH rule 0 x 692 [40,97,13]
+ CRUSH rule 0 x 693 [29,84,8]
+ CRUSH rule 0 x 694 [6,41,56]
+ CRUSH rule 0 x 695 [31,60,14]
+ CRUSH rule 0 x 696 [36,3,43]
+ CRUSH rule 0 x 697 [96,77,3]
+ CRUSH rule 0 x 698 [61,68,7]
+ CRUSH rule 0 x 699 [47,62,3]
+ CRUSH rule 0 x 700 [0,55,22]
+ CRUSH rule 0 x 701 [42,11,91]
+ CRUSH rule 0 x 702 [0,71,22]
+ CRUSH rule 0 x 703 [92,67,17]
+ CRUSH rule 0 x 704 [10,19,88]
+ CRUSH rule 0 x 705 [105,82,14]
+ CRUSH rule 0 x 706 [74,105,13]
+ CRUSH rule 0 x 707 [0,77,22]
+ CRUSH rule 0 x 708 [84,8,39]
+ CRUSH rule 0 x 709 [114,97,4]
+ CRUSH rule 0 x 710 [94,7,33]
+ CRUSH rule 0 x 711 [68,49,22]
+ CRUSH rule 0 x 712 [34,75,7]
+ CRUSH rule 0 x 713 [29,0,21]
+ CRUSH rule 0 x 714 [81,115,21]
+ CRUSH rule 0 x 715 [71,84,6]
+ CRUSH rule 0 x 716 [40,17,69]
+ CRUSH rule 0 x 717 [61,62,14]
+ CRUSH rule 0 x 718 [40,85,19]
+ CRUSH rule 0 x 719 [59,42,3]
+ CRUSH rule 0 x 720 [69,72,14]
+ CRUSH rule 0 x 721 [62,41,21]
+ CRUSH rule 0 x 722 [115,8,43]
+ CRUSH rule 0 x 723 [117,41,14]
+ CRUSH rule 0 x 724 [45,21,111]
+ CRUSH rule 0 x 725 [53,113,3]
+ CRUSH rule 0 x 726 [84,23,8]
+ CRUSH rule 0 x 727 [109,14,31]
+ CRUSH rule 0 x 728 [76,16,11]
+ CRUSH rule 0 x 729 [108,6,77]
+ CRUSH rule 0 x 730 [28,47,21]
+ CRUSH rule 0 x 731 [78,27,3]
+ CRUSH rule 0 x 732 [55,90,4]
+ CRUSH rule 0 x 733 [84,3,99]
+ CRUSH rule 0 x 734 [27,117,6]
+ CRUSH rule 0 x 735 [83,28,17]
+ CRUSH rule 0 x 736 [70,67,21]
+ CRUSH rule 0 x 737 [117,15,101]
+ CRUSH rule 0 x 738 [118,33,13]
+ CRUSH rule 0 x 739 [87,38,19]
+ CRUSH rule 0 x 740 [29,38,19]
+ CRUSH rule 0 x 741 [96,73,4]
+ CRUSH rule 0 x 742 [106,83,8]
+ CRUSH rule 0 x 743 [105,94,14]
+ CRUSH rule 0 x 744 [23,42,17]
+ CRUSH rule 0 x 745 [28,59,19]
+ CRUSH rule 0 x 746 [18,47,13]
+ CRUSH rule 0 x 747 [65,70,19]
+ CRUSH rule 0 x 748 [48,89,17]
+ CRUSH rule 0 x 749 [102,51,8]
+ CRUSH rule 0 x 750 [50,27,11]
+ CRUSH rule 0 x 751 [36,25,9]
+ CRUSH rule 0 x 752 [69,52,15]
+ CRUSH rule 0 x 753 [9,32,51]
+ CRUSH rule 0 x 754 [9,57,40]
+ CRUSH rule 0 x 755 [98,81,4]
+ CRUSH rule 0 x 756 [113,87,7]
+ CRUSH rule 0 x 757 [47,66,7]
+ CRUSH rule 0 x 758 [57,88,4]
+ CRUSH rule 0 x 759 [74,27,21]
+ CRUSH rule 0 x 760 [53,90,8]
+ CRUSH rule 0 x 761 [78,97,13]
+ CRUSH rule 0 x 762 [87,8,110]
+ CRUSH rule 0 x 763 [13,45,92]
+ CRUSH rule 0 x 764 [106,81,22]
+ CRUSH rule 0 x 765 [109,91,14]
+ CRUSH rule 0 x 766 [76,97,6]
+ CRUSH rule 0 x 767 [41,116,14]
+ CRUSH rule 0 x 768 [13,114,57]
+ CRUSH rule 0 x 769 [91,104,7]
+ CRUSH rule 0 x 770 [105,96,22]
+ CRUSH rule 0 x 771 [10,76,17]
+ CRUSH rule 0 x 772 [8,111,69]
+ CRUSH rule 0 x 773 [116,75,6]
+ CRUSH rule 0 x 774 [100,43,22]
+ CRUSH rule 0 x 775 [15,34,73]
+ CRUSH rule 0 x 776 [69,38,11]
+ CRUSH rule 0 x 777 [76,49,17]
+ CRUSH rule 0 x 778 [38,13,81]
+ CRUSH rule 0 x 779 [46,21,29]
+ CRUSH rule 0 x 780 [63,102,6]
+ CRUSH rule 0 x 781 [19,85,96]
+ CRUSH rule 0 x 782 [117,31,21]
+ CRUSH rule 0 x 783 [60,93,17]
+ CRUSH rule 0 x 784 [82,81,15]
+ CRUSH rule 0 x 785 [27,84,8]
+ CRUSH rule 0 x 786 [41,80,19]
+ CRUSH rule 0 x 787 [13,54,41]
+ CRUSH rule 0 x 788 [4,100,41]
+ CRUSH rule 0 x 789 [50,37,14]
+ CRUSH rule 0 x 790 [58,16,15]
+ CRUSH rule 0 x 791 [96,97,14]
+ CRUSH rule 0 x 792 [45,4,117]
+ CRUSH rule 0 x 793 [6,71,82]
+ CRUSH rule 0 x 794 [14,89,52]
+ CRUSH rule 0 x 795 [51,108,9]
+ CRUSH rule 0 x 796 [114,77,19]
+ CRUSH rule 0 x 797 [79,100,15]
+ CRUSH rule 0 x 798 [42,8,107]
+ CRUSH rule 0 x 799 [48,11,101]
+ CRUSH rule 0 x 800 [91,7,18]
+ CRUSH rule 0 x 801 [2,6,73]
+ CRUSH rule 0 x 802 [116,14,67]
+ CRUSH rule 0 x 803 [37,32,7]
+ CRUSH rule 0 x 804 [6,73,106]
+ CRUSH rule 0 x 805 [96,22,41]
+ CRUSH rule 0 x 806 [67,90,9]
+ CRUSH rule 0 x 807 [47,42,14]
+ CRUSH rule 0 x 808 [76,31,14]
+ CRUSH rule 0 x 809 [27,26,3]
+ CRUSH rule 0 x 810 [119,61,17]
+ CRUSH rule 0 x 811 [75,72,8]
+ CRUSH rule 0 x 812 [25,52,22]
+ CRUSH rule 0 x 813 [64,13,77]
+ CRUSH rule 0 x 814 [110,53,3]
+ CRUSH rule 0 x 815 [84,67,3]
+ CRUSH rule 0 x 816 [25,22,84]
+ CRUSH rule 0 x 817 [40,29,17]
+ CRUSH rule 0 x 818 [34,85,22]
+ CRUSH rule 0 x 819 [88,17,85]
+ CRUSH rule 0 x 820 [104,49,13]
+ CRUSH rule 0 x 821 [58,69,15]
+ CRUSH rule 0 x 822 [29,72,6]
+ CRUSH rule 0 x 823 [100,103,17]
+ CRUSH rule 0 x 824 [102,81,21]
+ CRUSH rule 0 x 825 [47,17,32]
+ CRUSH rule 0 x 826 [45,11,100]
+ CRUSH rule 0 x 827 [101,11,66]
+ CRUSH rule 0 x 828 [60,27,19]
+ CRUSH rule 0 x 829 [45,90,9]
+ CRUSH rule 0 x 830 [51,96,17]
+ CRUSH rule 0 x 831 [6,102,73]
+ CRUSH rule 0 x 832 [57,78,13]
+ CRUSH rule 0 x 833 [34,97,14]
+ CRUSH rule 0 x 834 [90,33,6]
+ CRUSH rule 0 x 835 [55,46,7]
+ CRUSH rule 0 x 836 [38,43,3]
+ CRUSH rule 0 x 837 [51,74,8]
+ CRUSH rule 0 x 838 [6,32,51]
+ CRUSH rule 0 x 839 [106,8,39]
+ CRUSH rule 0 x 840 [33,109,4]
+ CRUSH rule 0 x 841 [110,15,49]
+ CRUSH rule 0 x 842 [66,67,8]
+ CRUSH rule 0 x 843 [62,63,6]
+ CRUSH rule 0 x 844 [74,13,59]
+ CRUSH rule 0 x 845 [74,43,22]
+ CRUSH rule 0 x 846 [98,107,8]
+ CRUSH rule 0 x 847 [10,94,3]
+ CRUSH rule 0 x 848 [89,17,111]
+ CRUSH rule 0 x 849 [42,59,6]
+ CRUSH rule 0 x 850 [40,73,13]
+ CRUSH rule 0 x 851 [65,4,5]
+ CRUSH rule 0 x 852 [31,94,13]
+ CRUSH rule 0 x 853 [49,11,114]
+ CRUSH rule 0 x 854 [83,54,6]
+ CRUSH rule 0 x 855 [2,19,59]
+ CRUSH rule 0 x 856 [6,107,116]
+ CRUSH rule 0 x 857 [15,82,91]
+ CRUSH rule 0 x 858 [10,80,7]
+ CRUSH rule 0 x 859 [14,43,38]
+ CRUSH rule 0 x 860 [114,75,19]
+ CRUSH rule 0 x 861 [1,33,13]
+ CRUSH rule 0 x 862 [22,25,76]
+ CRUSH rule 0 x 863 [79,0,19]
+ CRUSH rule 0 x 864 [68,6,41]
+ CRUSH rule 0 x 865 [25,92,14]
+ CRUSH rule 0 x 866 [18,89,7]
+ CRUSH rule 0 x 867 [53,78,22]
+ CRUSH rule 0 x 868 [81,98,11]
+ CRUSH rule 0 x 869 [111,11,89]
+ CRUSH rule 0 x 870 [73,19,114]
+ CRUSH rule 0 x 871 [25,54,6]
+ CRUSH rule 0 x 872 [39,48,11]
+ CRUSH rule 0 x 873 [92,8,75]
+ CRUSH rule 0 x 874 [96,11,23]
+ CRUSH rule 0 x 875 [115,59,14]
+ CRUSH rule 0 x 876 [98,75,21]
+ CRUSH rule 0 x 877 [73,5,17]
+ CRUSH rule 0 x 878 [64,45,22]
+ CRUSH rule 0 x 879 [15,18,53]
+ CRUSH rule 0 x 880 [56,91,11]
+ CRUSH rule 0 x 881 [109,69,14]
+ CRUSH rule 0 x 882 [60,33,21]
+ CRUSH rule 0 x 883 [93,96,6]
+ CRUSH rule 0 x 884 [67,38,4]
+ CRUSH rule 0 x 885 [31,115,17]
+ CRUSH rule 0 x 886 [2,9,57]
+ CRUSH rule 0 x 887 [5,7,79]
+ CRUSH rule 0 x 888 [16,13,62]
+ CRUSH rule 0 x 889 [27,76,14]
+ CRUSH rule 0 x 890 [48,63,17]
+ CRUSH rule 0 x 891 [86,79,14]
+ CRUSH rule 0 x 892 [64,107,8]
+ CRUSH rule 0 x 893 [118,22,37]
+ CRUSH rule 0 x 894 [16,111,11]
+ CRUSH rule 0 x 895 [40,3,77]
+ CRUSH rule 0 x 896 [97,96,9]
+ CRUSH rule 0 x 897 [107,78,19]
+ CRUSH rule 0 x 898 [10,2,21]
+ CRUSH rule 0 x 899 [75,15,56]
+ CRUSH rule 0 x 900 [102,81,3]
+ CRUSH rule 0 x 901 [66,87,14]
+ CRUSH rule 0 x 902 [102,49,14]
+ CRUSH rule 0 x 903 [5,69,6]
+ CRUSH rule 0 x 904 [50,16,21]
+ CRUSH rule 0 x 905 [99,76,11]
+ CRUSH rule 0 x 906 [75,119,6]
+ CRUSH rule 0 x 907 [47,5,6]
+ CRUSH rule 0 x 908 [96,37,17]
+ CRUSH rule 0 x 909 [94,75,19]
+ CRUSH rule 0 x 910 [88,63,15]
+ CRUSH rule 0 x 911 [102,23,3]
+ CRUSH rule 0 x 912 [91,60,13]
+ CRUSH rule 0 x 913 [29,17,96]
+ CRUSH rule 0 x 914 [84,14,69]
+ CRUSH rule 0 x 915 [70,27,13]
+ CRUSH rule 0 x 916 [32,9,57]
+ CRUSH rule 0 x 917 [43,108,19]
+ CRUSH rule 0 x 918 [91,98,11]
+ CRUSH rule 0 x 919 [13,69,24]
+ CRUSH rule 0 x 920 [18,9,39]
+ CRUSH rule 0 x 921 [104,33,14]
+ CRUSH rule 0 x 922 [33,32,3]
+ CRUSH rule 0 x 923 [28,8,101]
+ CRUSH rule 0 x 924 [69,88,19]
+ CRUSH rule 0 x 925 [71,32,17]
+ CRUSH rule 0 x 926 [64,69,11]
+ CRUSH rule 0 x 927 [99,17,62]
+ CRUSH rule 0 x 928 [13,113,95]
+ CRUSH rule 0 x 929 [117,61,21]
+ CRUSH rule 0 x 930 [31,82,14]
+ CRUSH rule 0 x 931 [83,66,22]
+ CRUSH rule 0 x 932 [60,13,103]
+ CRUSH rule 0 x 933 [63,82,4]
+ CRUSH rule 0 x 934 [68,4,99]
+ CRUSH rule 0 x 935 [31,18,4]
+ CRUSH rule 0 x 936 [65,32,6]
+ CRUSH rule 0 x 937 [110,79,14]
+ CRUSH rule 0 x 938 [29,106,15]
+ CRUSH rule 0 x 939 [77,13,52]
+ CRUSH rule 0 x 940 [76,15,63]
+ CRUSH rule 0 x 941 [66,37,8]
+ CRUSH rule 0 x 942 [83,94,9]
+ CRUSH rule 0 x 943 [32,4,89]
+ CRUSH rule 0 x 944 [113,4,16]
+ CRUSH rule 0 x 945 [71,52,8]
+ CRUSH rule 0 x 946 [37,70,15]
+ CRUSH rule 0 x 947 [107,74,9]
+ CRUSH rule 0 x 948 [55,98,9]
+ CRUSH rule 0 x 949 [11,72,65]
+ CRUSH rule 0 x 950 [96,23,3]
+ CRUSH rule 0 x 951 [40,93,7]
+ CRUSH rule 0 x 952 [93,46,3]
+ CRUSH rule 0 x 953 [55,92,6]
+ CRUSH rule 0 x 954 [84,57,7]
+ CRUSH rule 0 x 955 [31,117,13]
+ CRUSH rule 0 x 956 [72,11,55]
+ CRUSH rule 0 x 957 [3,74,87]
+ CRUSH rule 0 x 958 [23,106,17]
+ CRUSH rule 0 x 959 [42,59,22]
+ CRUSH rule 0 x 960 [113,107,11]
+ CRUSH rule 0 x 961 [116,8,53]
+ CRUSH rule 0 x 962 [13,62,79]
+ CRUSH rule 0 x 963 [0,99,17]
+ CRUSH rule 0 x 964 [59,56,21]
+ CRUSH rule 0 x 965 [47,115,9]
+ CRUSH rule 0 x 966 [88,63,6]
+ CRUSH rule 0 x 967 [71,15,70]
+ CRUSH rule 0 x 968 [73,7,68]
+ CRUSH rule 0 x 969 [53,6,2]
+ CRUSH rule 0 x 970 [111,75,15]
+ CRUSH rule 0 x 971 [87,22,84]
+ CRUSH rule 0 x 972 [5,37,3]
+ CRUSH rule 0 x 973 [113,27,4]
+ CRUSH rule 0 x 974 [49,112,13]
+ CRUSH rule 0 x 975 [83,58,22]
+ CRUSH rule 0 x 976 [81,38,19]
+ CRUSH rule 0 x 977 [95,102,22]
+ CRUSH rule 0 x 978 [35,56,15]
+ CRUSH rule 0 x 979 [98,6,45]
+ CRUSH rule 0 x 980 [52,69,15]
+ CRUSH rule 0 x 981 [89,117,15]
+ CRUSH rule 0 x 982 [1,47,4]
+ CRUSH rule 0 x 983 [34,61,13]
+ CRUSH rule 0 x 984 [78,25,3]
+ CRUSH rule 0 x 985 [99,52,6]
+ CRUSH rule 0 x 986 [4,59,84]
+ CRUSH rule 0 x 987 [78,21,27]
+ CRUSH rule 0 x 988 [79,2,11]
+ CRUSH rule 0 x 989 [87,17,32]
+ CRUSH rule 0 x 990 [47,118,19]
+ CRUSH rule 0 x 991 [61,18,6]
+ CRUSH rule 0 x 992 [83,66,17]
+ CRUSH rule 0 x 993 [74,53,6]
+ CRUSH rule 0 x 994 [74,57,9]
+ CRUSH rule 0 x 995 [100,97,21]
+ CRUSH rule 0 x 996 [41,6,58]
+ CRUSH rule 0 x 997 [89,76,22]
+ CRUSH rule 0 x 998 [92,47,11]
+ CRUSH rule 0 x 999 [117,11,91]
+ CRUSH rule 0 x 1000 [9,119,37]
+ CRUSH rule 0 x 1001 [49,32,7]
+ CRUSH rule 0 x 1002 [99,113,7]
+ CRUSH rule 0 x 1003 [43,18,6]
+ CRUSH rule 0 x 1004 [89,54,14]
+ CRUSH rule 0 x 1005 [105,84,6]
+ CRUSH rule 0 x 1006 [45,111,7]
+ CRUSH rule 0 x 1007 [19,57,5]
+ CRUSH rule 0 x 1008 [31,14,50]
+ CRUSH rule 0 x 1009 [19,111,91]
+ CRUSH rule 0 x 1010 [42,89,13]
+ CRUSH rule 0 x 1011 [25,114,11]
+ CRUSH rule 0 x 1012 [68,71,6]
+ CRUSH rule 0 x 1013 [5,45,3]
+ CRUSH rule 0 x 1014 [33,4,109]
+ CRUSH rule 0 x 1015 [14,45,74]
+ CRUSH rule 0 x 1016 [88,19,105]
+ CRUSH rule 0 x 1017 [0,89,22]
+ CRUSH rule 0 x 1018 [63,5,22]
+ CRUSH rule 0 x 1019 [104,97,4]
+ CRUSH rule 0 x 1020 [96,99,8]
+ CRUSH rule 0 x 1021 [117,41,15]
+ CRUSH rule 0 x 1022 [73,22,36]
+ CRUSH rule 0 x 1023 [0,16,14]
+ rule 0 (data) num_rep 10 result size == 3:\t1024/1024 (esc)
diff --git a/src/test/cli/crushtool/test-map-indep.crushmap b/src/test/cli/crushtool/test-map-indep.crushmap
new file mode 100644
index 000000000..b3fd3c21f
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-indep.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/test-map-indep.t b/src/test/cli/crushtool/test-map-indep.t
new file mode 100644
index 000000000..886332175
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-indep.t
@@ -0,0 +1,10253 @@
+ $ crushtool -i "$TESTDIR/test-map-indep.crushmap" --test --show-mappings --show-statistics --rule 1 --set-choose-local-tries 0 --set-choose-local-fallback-tries 0 --set-choose-total-tries 50 --set-chooseleaf-descend-once 2 --min-rep 1 --max-rep 10
+ rule 1 (metadata), x = 0..1023, numrep = 1..10
+ CRUSH rule 1 x 0 [36]
+ CRUSH rule 1 x 1 [876]
+ CRUSH rule 1 x 2 [292]
+ CRUSH rule 1 x 3 [623]
+ CRUSH rule 1 x 4 [61]
+ CRUSH rule 1 x 5 [946]
+ CRUSH rule 1 x 6 [576]
+ CRUSH rule 1 x 7 [645]
+ CRUSH rule 1 x 8 [243]
+ CRUSH rule 1 x 9 [22]
+ CRUSH rule 1 x 10 [758]
+ CRUSH rule 1 x 11 [769]
+ CRUSH rule 1 x 12 [780]
+ CRUSH rule 1 x 13 [557]
+ CRUSH rule 1 x 14 [59]
+ CRUSH rule 1 x 15 [718]
+ CRUSH rule 1 x 16 [673]
+ CRUSH rule 1 x 17 [648]
+ CRUSH rule 1 x 18 [654]
+ CRUSH rule 1 x 19 [850]
+ CRUSH rule 1 x 20 [717]
+ CRUSH rule 1 x 21 [420]
+ CRUSH rule 1 x 22 [503]
+ CRUSH rule 1 x 23 [411]
+ CRUSH rule 1 x 24 [266]
+ CRUSH rule 1 x 25 [760]
+ CRUSH rule 1 x 26 [903]
+ CRUSH rule 1 x 27 [946]
+ CRUSH rule 1 x 28 [69]
+ CRUSH rule 1 x 29 [844]
+ CRUSH rule 1 x 30 [621]
+ CRUSH rule 1 x 31 [784]
+ CRUSH rule 1 x 32 [173]
+ CRUSH rule 1 x 33 [698]
+ CRUSH rule 1 x 34 [168]
+ CRUSH rule 1 x 35 [274]
+ CRUSH rule 1 x 36 [318]
+ CRUSH rule 1 x 37 [173]
+ CRUSH rule 1 x 38 [708]
+ CRUSH rule 1 x 39 [662]
+ CRUSH rule 1 x 40 [620]
+ CRUSH rule 1 x 41 [811]
+ CRUSH rule 1 x 42 [863]
+ CRUSH rule 1 x 43 [686]
+ CRUSH rule 1 x 44 [396]
+ CRUSH rule 1 x 45 [991]
+ CRUSH rule 1 x 46 [420]
+ CRUSH rule 1 x 47 [467]
+ CRUSH rule 1 x 48 [955]
+ CRUSH rule 1 x 49 [974]
+ CRUSH rule 1 x 50 [870]
+ CRUSH rule 1 x 51 [182]
+ CRUSH rule 1 x 52 [704]
+ CRUSH rule 1 x 53 [185]
+ CRUSH rule 1 x 54 [270]
+ CRUSH rule 1 x 55 [895]
+ CRUSH rule 1 x 56 [564]
+ CRUSH rule 1 x 57 [738]
+ CRUSH rule 1 x 58 [524]
+ CRUSH rule 1 x 59 [408]
+ CRUSH rule 1 x 60 [228]
+ CRUSH rule 1 x 61 [154]
+ CRUSH rule 1 x 62 [594]
+ CRUSH rule 1 x 63 [646]
+ CRUSH rule 1 x 64 [175]
+ CRUSH rule 1 x 65 [745]
+ CRUSH rule 1 x 66 [275]
+ CRUSH rule 1 x 67 [246]
+ CRUSH rule 1 x 68 [711]
+ CRUSH rule 1 x 69 [493]
+ CRUSH rule 1 x 70 [30]
+ CRUSH rule 1 x 71 [984]
+ CRUSH rule 1 x 72 [71]
+ CRUSH rule 1 x 73 [922]
+ CRUSH rule 1 x 74 [629]
+ CRUSH rule 1 x 75 [222]
+ CRUSH rule 1 x 76 [262]
+ CRUSH rule 1 x 77 [638]
+ CRUSH rule 1 x 78 [324]
+ CRUSH rule 1 x 79 [577]
+ CRUSH rule 1 x 80 [501]
+ CRUSH rule 1 x 81 [506]
+ CRUSH rule 1 x 82 [222]
+ CRUSH rule 1 x 83 [71]
+ CRUSH rule 1 x 84 [49]
+ CRUSH rule 1 x 85 [985]
+ CRUSH rule 1 x 86 [537]
+ CRUSH rule 1 x 87 [997]
+ CRUSH rule 1 x 88 [957]
+ CRUSH rule 1 x 89 [399]
+ CRUSH rule 1 x 90 [943]
+ CRUSH rule 1 x 91 [22]
+ CRUSH rule 1 x 92 [532]
+ CRUSH rule 1 x 93 [218]
+ CRUSH rule 1 x 94 [181]
+ CRUSH rule 1 x 95 [343]
+ CRUSH rule 1 x 96 [861]
+ CRUSH rule 1 x 97 [459]
+ CRUSH rule 1 x 98 [327]
+ CRUSH rule 1 x 99 [974]
+ CRUSH rule 1 x 100 [32]
+ CRUSH rule 1 x 101 [142]
+ CRUSH rule 1 x 102 [172]
+ CRUSH rule 1 x 103 [630]
+ CRUSH rule 1 x 104 [758]
+ CRUSH rule 1 x 105 [843]
+ CRUSH rule 1 x 106 [28]
+ CRUSH rule 1 x 107 [74]
+ CRUSH rule 1 x 108 [875]
+ CRUSH rule 1 x 109 [411]
+ CRUSH rule 1 x 110 [440]
+ CRUSH rule 1 x 111 [405]
+ CRUSH rule 1 x 112 [143]
+ CRUSH rule 1 x 113 [153]
+ CRUSH rule 1 x 114 [804]
+ CRUSH rule 1 x 115 [588]
+ CRUSH rule 1 x 116 [327]
+ CRUSH rule 1 x 117 [95]
+ CRUSH rule 1 x 118 [80]
+ CRUSH rule 1 x 119 [386]
+ CRUSH rule 1 x 120 [366]
+ CRUSH rule 1 x 121 [129]
+ CRUSH rule 1 x 122 [873]
+ CRUSH rule 1 x 123 [533]
+ CRUSH rule 1 x 124 [461]
+ CRUSH rule 1 x 125 [342]
+ CRUSH rule 1 x 126 [819]
+ CRUSH rule 1 x 127 [437]
+ CRUSH rule 1 x 128 [679]
+ CRUSH rule 1 x 129 [380]
+ CRUSH rule 1 x 130 [992]
+ CRUSH rule 1 x 131 [469]
+ CRUSH rule 1 x 132 [571]
+ CRUSH rule 1 x 133 [964]
+ CRUSH rule 1 x 134 [999]
+ CRUSH rule 1 x 135 [634]
+ CRUSH rule 1 x 136 [114]
+ CRUSH rule 1 x 137 [839]
+ CRUSH rule 1 x 138 [967]
+ CRUSH rule 1 x 139 [308]
+ CRUSH rule 1 x 140 [764]
+ CRUSH rule 1 x 141 [423]
+ CRUSH rule 1 x 142 [252]
+ CRUSH rule 1 x 143 [33]
+ CRUSH rule 1 x 144 [472]
+ CRUSH rule 1 x 145 [242]
+ CRUSH rule 1 x 146 [290]
+ CRUSH rule 1 x 147 [447]
+ CRUSH rule 1 x 148 [212]
+ CRUSH rule 1 x 149 [9]
+ CRUSH rule 1 x 150 [166]
+ CRUSH rule 1 x 151 [811]
+ CRUSH rule 1 x 152 [449]
+ CRUSH rule 1 x 153 [523]
+ CRUSH rule 1 x 154 [208]
+ CRUSH rule 1 x 155 [569]
+ CRUSH rule 1 x 156 [488]
+ CRUSH rule 1 x 157 [140]
+ CRUSH rule 1 x 158 [786]
+ CRUSH rule 1 x 159 [134]
+ CRUSH rule 1 x 160 [690]
+ CRUSH rule 1 x 161 [324]
+ CRUSH rule 1 x 162 [748]
+ CRUSH rule 1 x 163 [575]
+ CRUSH rule 1 x 164 [314]
+ CRUSH rule 1 x 165 [116]
+ CRUSH rule 1 x 166 [352]
+ CRUSH rule 1 x 167 [27]
+ CRUSH rule 1 x 168 [953]
+ CRUSH rule 1 x 169 [912]
+ CRUSH rule 1 x 170 [421]
+ CRUSH rule 1 x 171 [488]
+ CRUSH rule 1 x 172 [366]
+ CRUSH rule 1 x 173 [863]
+ CRUSH rule 1 x 174 [263]
+ CRUSH rule 1 x 175 [875]
+ CRUSH rule 1 x 176 [745]
+ CRUSH rule 1 x 177 [128]
+ CRUSH rule 1 x 178 [155]
+ CRUSH rule 1 x 179 [593]
+ CRUSH rule 1 x 180 [154]
+ CRUSH rule 1 x 181 [289]
+ CRUSH rule 1 x 182 [730]
+ CRUSH rule 1 x 183 [639]
+ CRUSH rule 1 x 184 [704]
+ CRUSH rule 1 x 185 [97]
+ CRUSH rule 1 x 186 [26]
+ CRUSH rule 1 x 187 [649]
+ CRUSH rule 1 x 188 [682]
+ CRUSH rule 1 x 189 [325]
+ CRUSH rule 1 x 190 [399]
+ CRUSH rule 1 x 191 [629]
+ CRUSH rule 1 x 192 [503]
+ CRUSH rule 1 x 193 [546]
+ CRUSH rule 1 x 194 [242]
+ CRUSH rule 1 x 195 [625]
+ CRUSH rule 1 x 196 [357]
+ CRUSH rule 1 x 197 [306]
+ CRUSH rule 1 x 198 [863]
+ CRUSH rule 1 x 199 [935]
+ CRUSH rule 1 x 200 [373]
+ CRUSH rule 1 x 201 [659]
+ CRUSH rule 1 x 202 [260]
+ CRUSH rule 1 x 203 [36]
+ CRUSH rule 1 x 204 [92]
+ CRUSH rule 1 x 205 [68]
+ CRUSH rule 1 x 206 [570]
+ CRUSH rule 1 x 207 [834]
+ CRUSH rule 1 x 208 [927]
+ CRUSH rule 1 x 209 [878]
+ CRUSH rule 1 x 210 [572]
+ CRUSH rule 1 x 211 [107]
+ CRUSH rule 1 x 212 [389]
+ CRUSH rule 1 x 213 [497]
+ CRUSH rule 1 x 214 [798]
+ CRUSH rule 1 x 215 [233]
+ CRUSH rule 1 x 216 [494]
+ CRUSH rule 1 x 217 [352]
+ CRUSH rule 1 x 218 [895]
+ CRUSH rule 1 x 219 [222]
+ CRUSH rule 1 x 220 [281]
+ CRUSH rule 1 x 221 [64]
+ CRUSH rule 1 x 222 [40]
+ CRUSH rule 1 x 223 [645]
+ CRUSH rule 1 x 224 [647]
+ CRUSH rule 1 x 225 [219]
+ CRUSH rule 1 x 226 [372]
+ CRUSH rule 1 x 227 [925]
+ CRUSH rule 1 x 228 [682]
+ CRUSH rule 1 x 229 [880]
+ CRUSH rule 1 x 230 [328]
+ CRUSH rule 1 x 231 [320]
+ CRUSH rule 1 x 232 [924]
+ CRUSH rule 1 x 233 [948]
+ CRUSH rule 1 x 234 [484]
+ CRUSH rule 1 x 235 [750]
+ CRUSH rule 1 x 236 [551]
+ CRUSH rule 1 x 237 [390]
+ CRUSH rule 1 x 238 [570]
+ CRUSH rule 1 x 239 [729]
+ CRUSH rule 1 x 240 [981]
+ CRUSH rule 1 x 241 [310]
+ CRUSH rule 1 x 242 [161]
+ CRUSH rule 1 x 243 [180]
+ CRUSH rule 1 x 244 [52]
+ CRUSH rule 1 x 245 [523]
+ CRUSH rule 1 x 246 [362]
+ CRUSH rule 1 x 247 [382]
+ CRUSH rule 1 x 248 [129]
+ CRUSH rule 1 x 249 [159]
+ CRUSH rule 1 x 250 [404]
+ CRUSH rule 1 x 251 [661]
+ CRUSH rule 1 x 252 [961]
+ CRUSH rule 1 x 253 [651]
+ CRUSH rule 1 x 254 [123]
+ CRUSH rule 1 x 255 [314]
+ CRUSH rule 1 x 256 [315]
+ CRUSH rule 1 x 257 [825]
+ CRUSH rule 1 x 258 [624]
+ CRUSH rule 1 x 259 [602]
+ CRUSH rule 1 x 260 [717]
+ CRUSH rule 1 x 261 [145]
+ CRUSH rule 1 x 262 [223]
+ CRUSH rule 1 x 263 [462]
+ CRUSH rule 1 x 264 [654]
+ CRUSH rule 1 x 265 [302]
+ CRUSH rule 1 x 266 [202]
+ CRUSH rule 1 x 267 [282]
+ CRUSH rule 1 x 268 [338]
+ CRUSH rule 1 x 269 [738]
+ CRUSH rule 1 x 270 [707]
+ CRUSH rule 1 x 271 [705]
+ CRUSH rule 1 x 272 [756]
+ CRUSH rule 1 x 273 [197]
+ CRUSH rule 1 x 274 [992]
+ CRUSH rule 1 x 275 [544]
+ CRUSH rule 1 x 276 [658]
+ CRUSH rule 1 x 277 [143]
+ CRUSH rule 1 x 278 [492]
+ CRUSH rule 1 x 279 [517]
+ CRUSH rule 1 x 280 [825]
+ CRUSH rule 1 x 281 [224]
+ CRUSH rule 1 x 282 [298]
+ CRUSH rule 1 x 283 [311]
+ CRUSH rule 1 x 284 [771]
+ CRUSH rule 1 x 285 [693]
+ CRUSH rule 1 x 286 [364]
+ CRUSH rule 1 x 287 [591]
+ CRUSH rule 1 x 288 [965]
+ CRUSH rule 1 x 289 [225]
+ CRUSH rule 1 x 290 [577]
+ CRUSH rule 1 x 291 [160]
+ CRUSH rule 1 x 292 [873]
+ CRUSH rule 1 x 293 [100]
+ CRUSH rule 1 x 294 [285]
+ CRUSH rule 1 x 295 [938]
+ CRUSH rule 1 x 296 [850]
+ CRUSH rule 1 x 297 [951]
+ CRUSH rule 1 x 298 [173]
+ CRUSH rule 1 x 299 [598]
+ CRUSH rule 1 x 300 [531]
+ CRUSH rule 1 x 301 [823]
+ CRUSH rule 1 x 302 [184]
+ CRUSH rule 1 x 303 [521]
+ CRUSH rule 1 x 304 [980]
+ CRUSH rule 1 x 305 [153]
+ CRUSH rule 1 x 306 [423]
+ CRUSH rule 1 x 307 [997]
+ CRUSH rule 1 x 308 [991]
+ CRUSH rule 1 x 309 [860]
+ CRUSH rule 1 x 310 [589]
+ CRUSH rule 1 x 311 [477]
+ CRUSH rule 1 x 312 [887]
+ CRUSH rule 1 x 313 [802]
+ CRUSH rule 1 x 314 [654]
+ CRUSH rule 1 x 315 [767]
+ CRUSH rule 1 x 316 [778]
+ CRUSH rule 1 x 317 [184]
+ CRUSH rule 1 x 318 [525]
+ CRUSH rule 1 x 319 [476]
+ CRUSH rule 1 x 320 [149]
+ CRUSH rule 1 x 321 [710]
+ CRUSH rule 1 x 322 [175]
+ CRUSH rule 1 x 323 [819]
+ CRUSH rule 1 x 324 [16]
+ CRUSH rule 1 x 325 [486]
+ CRUSH rule 1 x 326 [613]
+ CRUSH rule 1 x 327 [125]
+ CRUSH rule 1 x 328 [807]
+ CRUSH rule 1 x 329 [588]
+ CRUSH rule 1 x 330 [932]
+ CRUSH rule 1 x 331 [341]
+ CRUSH rule 1 x 332 [153]
+ CRUSH rule 1 x 333 [745]
+ CRUSH rule 1 x 334 [614]
+ CRUSH rule 1 x 335 [518]
+ CRUSH rule 1 x 336 [389]
+ CRUSH rule 1 x 337 [753]
+ CRUSH rule 1 x 338 [128]
+ CRUSH rule 1 x 339 [430]
+ CRUSH rule 1 x 340 [541]
+ CRUSH rule 1 x 341 [402]
+ CRUSH rule 1 x 342 [982]
+ CRUSH rule 1 x 343 [833]
+ CRUSH rule 1 x 344 [784]
+ CRUSH rule 1 x 345 [546]
+ CRUSH rule 1 x 346 [302]
+ CRUSH rule 1 x 347 [488]
+ CRUSH rule 1 x 348 [903]
+ CRUSH rule 1 x 349 [471]
+ CRUSH rule 1 x 350 [348]
+ CRUSH rule 1 x 351 [961]
+ CRUSH rule 1 x 352 [728]
+ CRUSH rule 1 x 353 [904]
+ CRUSH rule 1 x 354 [345]
+ CRUSH rule 1 x 355 [50]
+ CRUSH rule 1 x 356 [87]
+ CRUSH rule 1 x 357 [762]
+ CRUSH rule 1 x 358 [908]
+ CRUSH rule 1 x 359 [484]
+ CRUSH rule 1 x 360 [173]
+ CRUSH rule 1 x 361 [404]
+ CRUSH rule 1 x 362 [403]
+ CRUSH rule 1 x 363 [639]
+ CRUSH rule 1 x 364 [752]
+ CRUSH rule 1 x 365 [956]
+ CRUSH rule 1 x 366 [860]
+ CRUSH rule 1 x 367 [205]
+ CRUSH rule 1 x 368 [301]
+ CRUSH rule 1 x 369 [452]
+ CRUSH rule 1 x 370 [11]
+ CRUSH rule 1 x 371 [124]
+ CRUSH rule 1 x 372 [253]
+ CRUSH rule 1 x 373 [715]
+ CRUSH rule 1 x 374 [191]
+ CRUSH rule 1 x 375 [711]
+ CRUSH rule 1 x 376 [597]
+ CRUSH rule 1 x 377 [294]
+ CRUSH rule 1 x 378 [34]
+ CRUSH rule 1 x 379 [869]
+ CRUSH rule 1 x 380 [294]
+ CRUSH rule 1 x 381 [119]
+ CRUSH rule 1 x 382 [69]
+ CRUSH rule 1 x 383 [922]
+ CRUSH rule 1 x 384 [221]
+ CRUSH rule 1 x 385 [561]
+ CRUSH rule 1 x 386 [335]
+ CRUSH rule 1 x 387 [514]
+ CRUSH rule 1 x 388 [587]
+ CRUSH rule 1 x 389 [109]
+ CRUSH rule 1 x 390 [925]
+ CRUSH rule 1 x 391 [267]
+ CRUSH rule 1 x 392 [382]
+ CRUSH rule 1 x 393 [425]
+ CRUSH rule 1 x 394 [898]
+ CRUSH rule 1 x 395 [806]
+ CRUSH rule 1 x 396 [790]
+ CRUSH rule 1 x 397 [136]
+ CRUSH rule 1 x 398 [914]
+ CRUSH rule 1 x 399 [261]
+ CRUSH rule 1 x 400 [661]
+ CRUSH rule 1 x 401 [953]
+ CRUSH rule 1 x 402 [738]
+ CRUSH rule 1 x 403 [573]
+ CRUSH rule 1 x 404 [526]
+ CRUSH rule 1 x 405 [582]
+ CRUSH rule 1 x 406 [768]
+ CRUSH rule 1 x 407 [260]
+ CRUSH rule 1 x 408 [657]
+ CRUSH rule 1 x 409 [498]
+ CRUSH rule 1 x 410 [28]
+ CRUSH rule 1 x 411 [684]
+ CRUSH rule 1 x 412 [261]
+ CRUSH rule 1 x 413 [891]
+ CRUSH rule 1 x 414 [127]
+ CRUSH rule 1 x 415 [272]
+ CRUSH rule 1 x 416 [739]
+ CRUSH rule 1 x 417 [106]
+ CRUSH rule 1 x 418 [525]
+ CRUSH rule 1 x 419 [603]
+ CRUSH rule 1 x 420 [988]
+ CRUSH rule 1 x 421 [761]
+ CRUSH rule 1 x 422 [317]
+ CRUSH rule 1 x 423 [137]
+ CRUSH rule 1 x 424 [920]
+ CRUSH rule 1 x 425 [277]
+ CRUSH rule 1 x 426 [485]
+ CRUSH rule 1 x 427 [242]
+ CRUSH rule 1 x 428 [632]
+ CRUSH rule 1 x 429 [641]
+ CRUSH rule 1 x 430 [626]
+ CRUSH rule 1 x 431 [697]
+ CRUSH rule 1 x 432 [590]
+ CRUSH rule 1 x 433 [284]
+ CRUSH rule 1 x 434 [538]
+ CRUSH rule 1 x 435 [30]
+ CRUSH rule 1 x 436 [164]
+ CRUSH rule 1 x 437 [322]
+ CRUSH rule 1 x 438 [142]
+ CRUSH rule 1 x 439 [119]
+ CRUSH rule 1 x 440 [333]
+ CRUSH rule 1 x 441 [477]
+ CRUSH rule 1 x 442 [274]
+ CRUSH rule 1 x 443 [983]
+ CRUSH rule 1 x 444 [536]
+ CRUSH rule 1 x 445 [485]
+ CRUSH rule 1 x 446 [345]
+ CRUSH rule 1 x 447 [61]
+ CRUSH rule 1 x 448 [333]
+ CRUSH rule 1 x 449 [680]
+ CRUSH rule 1 x 450 [235]
+ CRUSH rule 1 x 451 [961]
+ CRUSH rule 1 x 452 [525]
+ CRUSH rule 1 x 453 [138]
+ CRUSH rule 1 x 454 [137]
+ CRUSH rule 1 x 455 [173]
+ CRUSH rule 1 x 456 [235]
+ CRUSH rule 1 x 457 [450]
+ CRUSH rule 1 x 458 [195]
+ CRUSH rule 1 x 459 [381]
+ CRUSH rule 1 x 460 [972]
+ CRUSH rule 1 x 461 [506]
+ CRUSH rule 1 x 462 [692]
+ CRUSH rule 1 x 463 [788]
+ CRUSH rule 1 x 464 [133]
+ CRUSH rule 1 x 465 [971]
+ CRUSH rule 1 x 466 [394]
+ CRUSH rule 1 x 467 [517]
+ CRUSH rule 1 x 468 [829]
+ CRUSH rule 1 x 469 [987]
+ CRUSH rule 1 x 470 [107]
+ CRUSH rule 1 x 471 [181]
+ CRUSH rule 1 x 472 [547]
+ CRUSH rule 1 x 473 [760]
+ CRUSH rule 1 x 474 [787]
+ CRUSH rule 1 x 475 [662]
+ CRUSH rule 1 x 476 [110]
+ CRUSH rule 1 x 477 [393]
+ CRUSH rule 1 x 478 [246]
+ CRUSH rule 1 x 479 [70]
+ CRUSH rule 1 x 480 [753]
+ CRUSH rule 1 x 481 [470]
+ CRUSH rule 1 x 482 [451]
+ CRUSH rule 1 x 483 [816]
+ CRUSH rule 1 x 484 [540]
+ CRUSH rule 1 x 485 [74]
+ CRUSH rule 1 x 486 [958]
+ CRUSH rule 1 x 487 [228]
+ CRUSH rule 1 x 488 [180]
+ CRUSH rule 1 x 489 [47]
+ CRUSH rule 1 x 490 [905]
+ CRUSH rule 1 x 491 [892]
+ CRUSH rule 1 x 492 [588]
+ CRUSH rule 1 x 493 [353]
+ CRUSH rule 1 x 494 [378]
+ CRUSH rule 1 x 495 [845]
+ CRUSH rule 1 x 496 [13]
+ CRUSH rule 1 x 497 [796]
+ CRUSH rule 1 x 498 [412]
+ CRUSH rule 1 x 499 [330]
+ CRUSH rule 1 x 500 [820]
+ CRUSH rule 1 x 501 [110]
+ CRUSH rule 1 x 502 [336]
+ CRUSH rule 1 x 503 [922]
+ CRUSH rule 1 x 504 [483]
+ CRUSH rule 1 x 505 [482]
+ CRUSH rule 1 x 506 [493]
+ CRUSH rule 1 x 507 [12]
+ CRUSH rule 1 x 508 [227]
+ CRUSH rule 1 x 509 [807]
+ CRUSH rule 1 x 510 [134]
+ CRUSH rule 1 x 511 [212]
+ CRUSH rule 1 x 512 [236]
+ CRUSH rule 1 x 513 [994]
+ CRUSH rule 1 x 514 [45]
+ CRUSH rule 1 x 515 [504]
+ CRUSH rule 1 x 516 [285]
+ CRUSH rule 1 x 517 [300]
+ CRUSH rule 1 x 518 [397]
+ CRUSH rule 1 x 519 [86]
+ CRUSH rule 1 x 520 [900]
+ CRUSH rule 1 x 521 [31]
+ CRUSH rule 1 x 522 [390]
+ CRUSH rule 1 x 523 [618]
+ CRUSH rule 1 x 524 [635]
+ CRUSH rule 1 x 525 [311]
+ CRUSH rule 1 x 526 [48]
+ CRUSH rule 1 x 527 [202]
+ CRUSH rule 1 x 528 [565]
+ CRUSH rule 1 x 529 [934]
+ CRUSH rule 1 x 530 [502]
+ CRUSH rule 1 x 531 [681]
+ CRUSH rule 1 x 532 [422]
+ CRUSH rule 1 x 533 [863]
+ CRUSH rule 1 x 534 [962]
+ CRUSH rule 1 x 535 [89]
+ CRUSH rule 1 x 536 [499]
+ CRUSH rule 1 x 537 [676]
+ CRUSH rule 1 x 538 [58]
+ CRUSH rule 1 x 539 [837]
+ CRUSH rule 1 x 540 [831]
+ CRUSH rule 1 x 541 [582]
+ CRUSH rule 1 x 542 [472]
+ CRUSH rule 1 x 543 [382]
+ CRUSH rule 1 x 544 [947]
+ CRUSH rule 1 x 545 [425]
+ CRUSH rule 1 x 546 [18]
+ CRUSH rule 1 x 547 [445]
+ CRUSH rule 1 x 548 [367]
+ CRUSH rule 1 x 549 [125]
+ CRUSH rule 1 x 550 [425]
+ CRUSH rule 1 x 551 [44]
+ CRUSH rule 1 x 552 [246]
+ CRUSH rule 1 x 553 [71]
+ CRUSH rule 1 x 554 [207]
+ CRUSH rule 1 x 555 [570]
+ CRUSH rule 1 x 556 [674]
+ CRUSH rule 1 x 557 [347]
+ CRUSH rule 1 x 558 [627]
+ CRUSH rule 1 x 559 [940]
+ CRUSH rule 1 x 560 [295]
+ CRUSH rule 1 x 561 [506]
+ CRUSH rule 1 x 562 [718]
+ CRUSH rule 1 x 563 [552]
+ CRUSH rule 1 x 564 [835]
+ CRUSH rule 1 x 565 [8]
+ CRUSH rule 1 x 566 [600]
+ CRUSH rule 1 x 567 [999]
+ CRUSH rule 1 x 568 [252]
+ CRUSH rule 1 x 569 [643]
+ CRUSH rule 1 x 570 [617]
+ CRUSH rule 1 x 571 [757]
+ CRUSH rule 1 x 572 [299]
+ CRUSH rule 1 x 573 [25]
+ CRUSH rule 1 x 574 [215]
+ CRUSH rule 1 x 575 [225]
+ CRUSH rule 1 x 576 [627]
+ CRUSH rule 1 x 577 [237]
+ CRUSH rule 1 x 578 [885]
+ CRUSH rule 1 x 579 [924]
+ CRUSH rule 1 x 580 [718]
+ CRUSH rule 1 x 581 [219]
+ CRUSH rule 1 x 582 [893]
+ CRUSH rule 1 x 583 [246]
+ CRUSH rule 1 x 584 [336]
+ CRUSH rule 1 x 585 [324]
+ CRUSH rule 1 x 586 [558]
+ CRUSH rule 1 x 587 [985]
+ CRUSH rule 1 x 588 [211]
+ CRUSH rule 1 x 589 [129]
+ CRUSH rule 1 x 590 [467]
+ CRUSH rule 1 x 591 [758]
+ CRUSH rule 1 x 592 [525]
+ CRUSH rule 1 x 593 [601]
+ CRUSH rule 1 x 594 [227]
+ CRUSH rule 1 x 595 [720]
+ CRUSH rule 1 x 596 [751]
+ CRUSH rule 1 x 597 [129]
+ CRUSH rule 1 x 598 [679]
+ CRUSH rule 1 x 599 [668]
+ CRUSH rule 1 x 600 [143]
+ CRUSH rule 1 x 601 [326]
+ CRUSH rule 1 x 602 [860]
+ CRUSH rule 1 x 603 [709]
+ CRUSH rule 1 x 604 [571]
+ CRUSH rule 1 x 605 [252]
+ CRUSH rule 1 x 606 [339]
+ CRUSH rule 1 x 607 [590]
+ CRUSH rule 1 x 608 [145]
+ CRUSH rule 1 x 609 [973]
+ CRUSH rule 1 x 610 [435]
+ CRUSH rule 1 x 611 [559]
+ CRUSH rule 1 x 612 [273]
+ CRUSH rule 1 x 613 [828]
+ CRUSH rule 1 x 614 [478]
+ CRUSH rule 1 x 615 [392]
+ CRUSH rule 1 x 616 [778]
+ CRUSH rule 1 x 617 [622]
+ CRUSH rule 1 x 618 [149]
+ CRUSH rule 1 x 619 [604]
+ CRUSH rule 1 x 620 [181]
+ CRUSH rule 1 x 621 [735]
+ CRUSH rule 1 x 622 [661]
+ CRUSH rule 1 x 623 [142]
+ CRUSH rule 1 x 624 [360]
+ CRUSH rule 1 x 625 [541]
+ CRUSH rule 1 x 626 [364]
+ CRUSH rule 1 x 627 [458]
+ CRUSH rule 1 x 628 [250]
+ CRUSH rule 1 x 629 [928]
+ CRUSH rule 1 x 630 [243]
+ CRUSH rule 1 x 631 [438]
+ CRUSH rule 1 x 632 [797]
+ CRUSH rule 1 x 633 [993]
+ CRUSH rule 1 x 634 [239]
+ CRUSH rule 1 x 635 [640]
+ CRUSH rule 1 x 636 [173]
+ CRUSH rule 1 x 637 [0]
+ CRUSH rule 1 x 638 [702]
+ CRUSH rule 1 x 639 [475]
+ CRUSH rule 1 x 640 [31]
+ CRUSH rule 1 x 641 [296]
+ CRUSH rule 1 x 642 [894]
+ CRUSH rule 1 x 643 [117]
+ CRUSH rule 1 x 644 [438]
+ CRUSH rule 1 x 645 [982]
+ CRUSH rule 1 x 646 [334]
+ CRUSH rule 1 x 647 [933]
+ CRUSH rule 1 x 648 [22]
+ CRUSH rule 1 x 649 [503]
+ CRUSH rule 1 x 650 [328]
+ CRUSH rule 1 x 651 [3]
+ CRUSH rule 1 x 652 [495]
+ CRUSH rule 1 x 653 [185]
+ CRUSH rule 1 x 654 [130]
+ CRUSH rule 1 x 655 [560]
+ CRUSH rule 1 x 656 [219]
+ CRUSH rule 1 x 657 [233]
+ CRUSH rule 1 x 658 [778]
+ CRUSH rule 1 x 659 [240]
+ CRUSH rule 1 x 660 [244]
+ CRUSH rule 1 x 661 [184]
+ CRUSH rule 1 x 662 [65]
+ CRUSH rule 1 x 663 [323]
+ CRUSH rule 1 x 664 [865]
+ CRUSH rule 1 x 665 [420]
+ CRUSH rule 1 x 666 [319]
+ CRUSH rule 1 x 667 [875]
+ CRUSH rule 1 x 668 [331]
+ CRUSH rule 1 x 669 [915]
+ CRUSH rule 1 x 670 [845]
+ CRUSH rule 1 x 671 [108]
+ CRUSH rule 1 x 672 [578]
+ CRUSH rule 1 x 673 [442]
+ CRUSH rule 1 x 674 [588]
+ CRUSH rule 1 x 675 [489]
+ CRUSH rule 1 x 676 [928]
+ CRUSH rule 1 x 677 [399]
+ CRUSH rule 1 x 678 [546]
+ CRUSH rule 1 x 679 [988]
+ CRUSH rule 1 x 680 [335]
+ CRUSH rule 1 x 681 [690]
+ CRUSH rule 1 x 682 [196]
+ CRUSH rule 1 x 683 [627]
+ CRUSH rule 1 x 684 [38]
+ CRUSH rule 1 x 685 [841]
+ CRUSH rule 1 x 686 [336]
+ CRUSH rule 1 x 687 [20]
+ CRUSH rule 1 x 688 [463]
+ CRUSH rule 1 x 689 [569]
+ CRUSH rule 1 x 690 [551]
+ CRUSH rule 1 x 691 [766]
+ CRUSH rule 1 x 692 [739]
+ CRUSH rule 1 x 693 [339]
+ CRUSH rule 1 x 694 [405]
+ CRUSH rule 1 x 695 [622]
+ CRUSH rule 1 x 696 [558]
+ CRUSH rule 1 x 697 [818]
+ CRUSH rule 1 x 698 [178]
+ CRUSH rule 1 x 699 [450]
+ CRUSH rule 1 x 700 [502]
+ CRUSH rule 1 x 701 [4]
+ CRUSH rule 1 x 702 [177]
+ CRUSH rule 1 x 703 [354]
+ CRUSH rule 1 x 704 [646]
+ CRUSH rule 1 x 705 [921]
+ CRUSH rule 1 x 706 [652]
+ CRUSH rule 1 x 707 [345]
+ CRUSH rule 1 x 708 [333]
+ CRUSH rule 1 x 709 [45]
+ CRUSH rule 1 x 710 [94]
+ CRUSH rule 1 x 711 [227]
+ CRUSH rule 1 x 712 [398]
+ CRUSH rule 1 x 713 [116]
+ CRUSH rule 1 x 714 [111]
+ CRUSH rule 1 x 715 [531]
+ CRUSH rule 1 x 716 [169]
+ CRUSH rule 1 x 717 [417]
+ CRUSH rule 1 x 718 [992]
+ CRUSH rule 1 x 719 [936]
+ CRUSH rule 1 x 720 [370]
+ CRUSH rule 1 x 721 [320]
+ CRUSH rule 1 x 722 [7]
+ CRUSH rule 1 x 723 [270]
+ CRUSH rule 1 x 724 [666]
+ CRUSH rule 1 x 725 [794]
+ CRUSH rule 1 x 726 [420]
+ CRUSH rule 1 x 727 [561]
+ CRUSH rule 1 x 728 [951]
+ CRUSH rule 1 x 729 [656]
+ CRUSH rule 1 x 730 [3]
+ CRUSH rule 1 x 731 [852]
+ CRUSH rule 1 x 732 [983]
+ CRUSH rule 1 x 733 [285]
+ CRUSH rule 1 x 734 [125]
+ CRUSH rule 1 x 735 [417]
+ CRUSH rule 1 x 736 [749]
+ CRUSH rule 1 x 737 [644]
+ CRUSH rule 1 x 738 [449]
+ CRUSH rule 1 x 739 [341]
+ CRUSH rule 1 x 740 [874]
+ CRUSH rule 1 x 741 [189]
+ CRUSH rule 1 x 742 [912]
+ CRUSH rule 1 x 743 [654]
+ CRUSH rule 1 x 744 [725]
+ CRUSH rule 1 x 745 [787]
+ CRUSH rule 1 x 746 [757]
+ CRUSH rule 1 x 747 [700]
+ CRUSH rule 1 x 748 [557]
+ CRUSH rule 1 x 749 [772]
+ CRUSH rule 1 x 750 [946]
+ CRUSH rule 1 x 751 [996]
+ CRUSH rule 1 x 752 [746]
+ CRUSH rule 1 x 753 [741]
+ CRUSH rule 1 x 754 [648]
+ CRUSH rule 1 x 755 [157]
+ CRUSH rule 1 x 756 [416]
+ CRUSH rule 1 x 757 [599]
+ CRUSH rule 1 x 758 [994]
+ CRUSH rule 1 x 759 [959]
+ CRUSH rule 1 x 760 [518]
+ CRUSH rule 1 x 761 [285]
+ CRUSH rule 1 x 762 [591]
+ CRUSH rule 1 x 763 [908]
+ CRUSH rule 1 x 764 [787]
+ CRUSH rule 1 x 765 [327]
+ CRUSH rule 1 x 766 [84]
+ CRUSH rule 1 x 767 [370]
+ CRUSH rule 1 x 768 [826]
+ CRUSH rule 1 x 769 [67]
+ CRUSH rule 1 x 770 [593]
+ CRUSH rule 1 x 771 [309]
+ CRUSH rule 1 x 772 [12]
+ CRUSH rule 1 x 773 [253]
+ CRUSH rule 1 x 774 [164]
+ CRUSH rule 1 x 775 [703]
+ CRUSH rule 1 x 776 [728]
+ CRUSH rule 1 x 777 [981]
+ CRUSH rule 1 x 778 [411]
+ CRUSH rule 1 x 779 [346]
+ CRUSH rule 1 x 780 [476]
+ CRUSH rule 1 x 781 [10]
+ CRUSH rule 1 x 782 [462]
+ CRUSH rule 1 x 783 [580]
+ CRUSH rule 1 x 784 [413]
+ CRUSH rule 1 x 785 [341]
+ CRUSH rule 1 x 786 [411]
+ CRUSH rule 1 x 787 [605]
+ CRUSH rule 1 x 788 [226]
+ CRUSH rule 1 x 789 [545]
+ CRUSH rule 1 x 790 [414]
+ CRUSH rule 1 x 791 [660]
+ CRUSH rule 1 x 792 [287]
+ CRUSH rule 1 x 793 [631]
+ CRUSH rule 1 x 794 [931]
+ CRUSH rule 1 x 795 [551]
+ CRUSH rule 1 x 796 [814]
+ CRUSH rule 1 x 797 [64]
+ CRUSH rule 1 x 798 [422]
+ CRUSH rule 1 x 799 [824]
+ CRUSH rule 1 x 800 [862]
+ CRUSH rule 1 x 801 [145]
+ CRUSH rule 1 x 802 [570]
+ CRUSH rule 1 x 803 [151]
+ CRUSH rule 1 x 804 [467]
+ CRUSH rule 1 x 805 [621]
+ CRUSH rule 1 x 806 [898]
+ CRUSH rule 1 x 807 [354]
+ CRUSH rule 1 x 808 [7]
+ CRUSH rule 1 x 809 [70]
+ CRUSH rule 1 x 810 [701]
+ CRUSH rule 1 x 811 [248]
+ CRUSH rule 1 x 812 [230]
+ CRUSH rule 1 x 813 [805]
+ CRUSH rule 1 x 814 [54]
+ CRUSH rule 1 x 815 [679]
+ CRUSH rule 1 x 816 [919]
+ CRUSH rule 1 x 817 [765]
+ CRUSH rule 1 x 818 [415]
+ CRUSH rule 1 x 819 [721]
+ CRUSH rule 1 x 820 [218]
+ CRUSH rule 1 x 821 [185]
+ CRUSH rule 1 x 822 [356]
+ CRUSH rule 1 x 823 [220]
+ CRUSH rule 1 x 824 [292]
+ CRUSH rule 1 x 825 [949]
+ CRUSH rule 1 x 826 [767]
+ CRUSH rule 1 x 827 [631]
+ CRUSH rule 1 x 828 [288]
+ CRUSH rule 1 x 829 [990]
+ CRUSH rule 1 x 830 [152]
+ CRUSH rule 1 x 831 [814]
+ CRUSH rule 1 x 832 [235]
+ CRUSH rule 1 x 833 [657]
+ CRUSH rule 1 x 834 [907]
+ CRUSH rule 1 x 835 [784]
+ CRUSH rule 1 x 836 [951]
+ CRUSH rule 1 x 837 [556]
+ CRUSH rule 1 x 838 [329]
+ CRUSH rule 1 x 839 [568]
+ CRUSH rule 1 x 840 [45]
+ CRUSH rule 1 x 841 [652]
+ CRUSH rule 1 x 842 [629]
+ CRUSH rule 1 x 843 [799]
+ CRUSH rule 1 x 844 [694]
+ CRUSH rule 1 x 845 [332]
+ CRUSH rule 1 x 846 [452]
+ CRUSH rule 1 x 847 [399]
+ CRUSH rule 1 x 848 [303]
+ CRUSH rule 1 x 849 [666]
+ CRUSH rule 1 x 850 [644]
+ CRUSH rule 1 x 851 [527]
+ CRUSH rule 1 x 852 [31]
+ CRUSH rule 1 x 853 [483]
+ CRUSH rule 1 x 854 [697]
+ CRUSH rule 1 x 855 [837]
+ CRUSH rule 1 x 856 [712]
+ CRUSH rule 1 x 857 [77]
+ CRUSH rule 1 x 858 [412]
+ CRUSH rule 1 x 859 [173]
+ CRUSH rule 1 x 860 [776]
+ CRUSH rule 1 x 861 [705]
+ CRUSH rule 1 x 862 [809]
+ CRUSH rule 1 x 863 [349]
+ CRUSH rule 1 x 864 [717]
+ CRUSH rule 1 x 865 [857]
+ CRUSH rule 1 x 866 [394]
+ CRUSH rule 1 x 867 [640]
+ CRUSH rule 1 x 868 [613]
+ CRUSH rule 1 x 869 [973]
+ CRUSH rule 1 x 870 [505]
+ CRUSH rule 1 x 871 [239]
+ CRUSH rule 1 x 872 [21]
+ CRUSH rule 1 x 873 [954]
+ CRUSH rule 1 x 874 [54]
+ CRUSH rule 1 x 875 [809]
+ CRUSH rule 1 x 876 [483]
+ CRUSH rule 1 x 877 [542]
+ CRUSH rule 1 x 878 [217]
+ CRUSH rule 1 x 879 [999]
+ CRUSH rule 1 x 880 [678]
+ CRUSH rule 1 x 881 [394]
+ CRUSH rule 1 x 882 [467]
+ CRUSH rule 1 x 883 [802]
+ CRUSH rule 1 x 884 [653]
+ CRUSH rule 1 x 885 [898]
+ CRUSH rule 1 x 886 [434]
+ CRUSH rule 1 x 887 [297]
+ CRUSH rule 1 x 888 [863]
+ CRUSH rule 1 x 889 [105]
+ CRUSH rule 1 x 890 [550]
+ CRUSH rule 1 x 891 [575]
+ CRUSH rule 1 x 892 [259]
+ CRUSH rule 1 x 893 [902]
+ CRUSH rule 1 x 894 [180]
+ CRUSH rule 1 x 895 [725]
+ CRUSH rule 1 x 896 [951]
+ CRUSH rule 1 x 897 [810]
+ CRUSH rule 1 x 898 [979]
+ CRUSH rule 1 x 899 [685]
+ CRUSH rule 1 x 900 [530]
+ CRUSH rule 1 x 901 [740]
+ CRUSH rule 1 x 902 [800]
+ CRUSH rule 1 x 903 [230]
+ CRUSH rule 1 x 904 [346]
+ CRUSH rule 1 x 905 [530]
+ CRUSH rule 1 x 906 [80]
+ CRUSH rule 1 x 907 [365]
+ CRUSH rule 1 x 908 [204]
+ CRUSH rule 1 x 909 [883]
+ CRUSH rule 1 x 910 [549]
+ CRUSH rule 1 x 911 [325]
+ CRUSH rule 1 x 912 [874]
+ CRUSH rule 1 x 913 [331]
+ CRUSH rule 1 x 914 [836]
+ CRUSH rule 1 x 915 [245]
+ CRUSH rule 1 x 916 [77]
+ CRUSH rule 1 x 917 [239]
+ CRUSH rule 1 x 918 [988]
+ CRUSH rule 1 x 919 [783]
+ CRUSH rule 1 x 920 [623]
+ CRUSH rule 1 x 921 [105]
+ CRUSH rule 1 x 922 [887]
+ CRUSH rule 1 x 923 [223]
+ CRUSH rule 1 x 924 [25]
+ CRUSH rule 1 x 925 [912]
+ CRUSH rule 1 x 926 [968]
+ CRUSH rule 1 x 927 [277]
+ CRUSH rule 1 x 928 [554]
+ CRUSH rule 1 x 929 [761]
+ CRUSH rule 1 x 930 [814]
+ CRUSH rule 1 x 931 [29]
+ CRUSH rule 1 x 932 [446]
+ CRUSH rule 1 x 933 [352]
+ CRUSH rule 1 x 934 [730]
+ CRUSH rule 1 x 935 [731]
+ CRUSH rule 1 x 936 [322]
+ CRUSH rule 1 x 937 [822]
+ CRUSH rule 1 x 938 [557]
+ CRUSH rule 1 x 939 [150]
+ CRUSH rule 1 x 940 [638]
+ CRUSH rule 1 x 941 [730]
+ CRUSH rule 1 x 942 [62]
+ CRUSH rule 1 x 943 [165]
+ CRUSH rule 1 x 944 [199]
+ CRUSH rule 1 x 945 [946]
+ CRUSH rule 1 x 946 [595]
+ CRUSH rule 1 x 947 [800]
+ CRUSH rule 1 x 948 [132]
+ CRUSH rule 1 x 949 [792]
+ CRUSH rule 1 x 950 [111]
+ CRUSH rule 1 x 951 [414]
+ CRUSH rule 1 x 952 [775]
+ CRUSH rule 1 x 953 [349]
+ CRUSH rule 1 x 954 [570]
+ CRUSH rule 1 x 955 [729]
+ CRUSH rule 1 x 956 [519]
+ CRUSH rule 1 x 957 [242]
+ CRUSH rule 1 x 958 [84]
+ CRUSH rule 1 x 959 [270]
+ CRUSH rule 1 x 960 [458]
+ CRUSH rule 1 x 961 [981]
+ CRUSH rule 1 x 962 [623]
+ CRUSH rule 1 x 963 [291]
+ CRUSH rule 1 x 964 [28]
+ CRUSH rule 1 x 965 [675]
+ CRUSH rule 1 x 966 [836]
+ CRUSH rule 1 x 967 [966]
+ CRUSH rule 1 x 968 [864]
+ CRUSH rule 1 x 969 [729]
+ CRUSH rule 1 x 970 [800]
+ CRUSH rule 1 x 971 [737]
+ CRUSH rule 1 x 972 [952]
+ CRUSH rule 1 x 973 [356]
+ CRUSH rule 1 x 974 [545]
+ CRUSH rule 1 x 975 [336]
+ CRUSH rule 1 x 976 [446]
+ CRUSH rule 1 x 977 [202]
+ CRUSH rule 1 x 978 [612]
+ CRUSH rule 1 x 979 [843]
+ CRUSH rule 1 x 980 [60]
+ CRUSH rule 1 x 981 [702]
+ CRUSH rule 1 x 982 [298]
+ CRUSH rule 1 x 983 [723]
+ CRUSH rule 1 x 984 [723]
+ CRUSH rule 1 x 985 [945]
+ CRUSH rule 1 x 986 [772]
+ CRUSH rule 1 x 987 [88]
+ CRUSH rule 1 x 988 [522]
+ CRUSH rule 1 x 989 [578]
+ CRUSH rule 1 x 990 [638]
+ CRUSH rule 1 x 991 [530]
+ CRUSH rule 1 x 992 [925]
+ CRUSH rule 1 x 993 [991]
+ CRUSH rule 1 x 994 [276]
+ CRUSH rule 1 x 995 [288]
+ CRUSH rule 1 x 996 [887]
+ CRUSH rule 1 x 997 [110]
+ CRUSH rule 1 x 998 [435]
+ CRUSH rule 1 x 999 [876]
+ CRUSH rule 1 x 1000 [178]
+ CRUSH rule 1 x 1001 [99]
+ CRUSH rule 1 x 1002 [515]
+ CRUSH rule 1 x 1003 [104]
+ CRUSH rule 1 x 1004 [269]
+ CRUSH rule 1 x 1005 [369]
+ CRUSH rule 1 x 1006 [40]
+ CRUSH rule 1 x 1007 [978]
+ CRUSH rule 1 x 1008 [965]
+ CRUSH rule 1 x 1009 [598]
+ CRUSH rule 1 x 1010 [767]
+ CRUSH rule 1 x 1011 [289]
+ CRUSH rule 1 x 1012 [128]
+ CRUSH rule 1 x 1013 [979]
+ CRUSH rule 1 x 1014 [979]
+ CRUSH rule 1 x 1015 [277]
+ CRUSH rule 1 x 1016 [262]
+ CRUSH rule 1 x 1017 [150]
+ CRUSH rule 1 x 1018 [555]
+ CRUSH rule 1 x 1019 [513]
+ CRUSH rule 1 x 1020 [158]
+ CRUSH rule 1 x 1021 [915]
+ CRUSH rule 1 x 1022 [967]
+ CRUSH rule 1 x 1023 [488]
+ rule 1 (metadata) num_rep 1 result size == 1:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705]
+ CRUSH rule 1 x 1 [876,250]
+ CRUSH rule 1 x 2 [292,832]
+ CRUSH rule 1 x 3 [623,387]
+ CRUSH rule 1 x 4 [61,334]
+ CRUSH rule 1 x 5 [946,557]
+ CRUSH rule 1 x 6 [576,668]
+ CRUSH rule 1 x 7 [645,753]
+ CRUSH rule 1 x 8 [243,6]
+ CRUSH rule 1 x 9 [22,578]
+ CRUSH rule 1 x 10 [758,828]
+ CRUSH rule 1 x 11 [769,120]
+ CRUSH rule 1 x 12 [780,364]
+ CRUSH rule 1 x 13 [557,18]
+ CRUSH rule 1 x 14 [59,561]
+ CRUSH rule 1 x 15 [718,928]
+ CRUSH rule 1 x 16 [673,632]
+ CRUSH rule 1 x 17 [648,43]
+ CRUSH rule 1 x 18 [654,219]
+ CRUSH rule 1 x 19 [850,545]
+ CRUSH rule 1 x 20 [717,785]
+ CRUSH rule 1 x 21 [420,57]
+ CRUSH rule 1 x 22 [503,998]
+ CRUSH rule 1 x 23 [411,663]
+ CRUSH rule 1 x 24 [266,861]
+ CRUSH rule 1 x 25 [760,483]
+ CRUSH rule 1 x 26 [903,24]
+ CRUSH rule 1 x 27 [946,188]
+ CRUSH rule 1 x 28 [69,312]
+ CRUSH rule 1 x 29 [844,883]
+ CRUSH rule 1 x 30 [621,18]
+ CRUSH rule 1 x 31 [784,943]
+ CRUSH rule 1 x 32 [173,374]
+ CRUSH rule 1 x 33 [698,336]
+ CRUSH rule 1 x 34 [168,836]
+ CRUSH rule 1 x 35 [274,509]
+ CRUSH rule 1 x 36 [318,215]
+ CRUSH rule 1 x 37 [173,604]
+ CRUSH rule 1 x 38 [708,444]
+ CRUSH rule 1 x 39 [662,198]
+ CRUSH rule 1 x 40 [620,801]
+ CRUSH rule 1 x 41 [811,264]
+ CRUSH rule 1 x 42 [863,179]
+ CRUSH rule 1 x 43 [686,822]
+ CRUSH rule 1 x 44 [396,222]
+ CRUSH rule 1 x 45 [991,694]
+ CRUSH rule 1 x 46 [420,909]
+ CRUSH rule 1 x 47 [467,211]
+ CRUSH rule 1 x 48 [955,329]
+ CRUSH rule 1 x 49 [974,891]
+ CRUSH rule 1 x 50 [870,441]
+ CRUSH rule 1 x 51 [182,930]
+ CRUSH rule 1 x 52 [704,812]
+ CRUSH rule 1 x 53 [185,713]
+ CRUSH rule 1 x 54 [270,441]
+ CRUSH rule 1 x 55 [895,734]
+ CRUSH rule 1 x 56 [564,963]
+ CRUSH rule 1 x 57 [738,130]
+ CRUSH rule 1 x 58 [524,113]
+ CRUSH rule 1 x 59 [408,337]
+ CRUSH rule 1 x 60 [228,790]
+ CRUSH rule 1 x 61 [154,843]
+ CRUSH rule 1 x 62 [594,811]
+ CRUSH rule 1 x 63 [646,67]
+ CRUSH rule 1 x 64 [175,542]
+ CRUSH rule 1 x 65 [745,619]
+ CRUSH rule 1 x 66 [275,468]
+ CRUSH rule 1 x 67 [246,958]
+ CRUSH rule 1 x 68 [711,473]
+ CRUSH rule 1 x 69 [493,924]
+ CRUSH rule 1 x 70 [30,499]
+ CRUSH rule 1 x 71 [984,883]
+ CRUSH rule 1 x 72 [71,286]
+ CRUSH rule 1 x 73 [922,618]
+ CRUSH rule 1 x 74 [629,414]
+ CRUSH rule 1 x 75 [222,20]
+ CRUSH rule 1 x 76 [262,366]
+ CRUSH rule 1 x 77 [638,469]
+ CRUSH rule 1 x 78 [324,511]
+ CRUSH rule 1 x 79 [577,990]
+ CRUSH rule 1 x 80 [501,95]
+ CRUSH rule 1 x 81 [506,812]
+ CRUSH rule 1 x 82 [222,145]
+ CRUSH rule 1 x 83 [71,634]
+ CRUSH rule 1 x 84 [49,761]
+ CRUSH rule 1 x 85 [985,896]
+ CRUSH rule 1 x 86 [537,745]
+ CRUSH rule 1 x 87 [997,317]
+ CRUSH rule 1 x 88 [957,350]
+ CRUSH rule 1 x 89 [399,730]
+ CRUSH rule 1 x 90 [943,706]
+ CRUSH rule 1 x 91 [22,368]
+ CRUSH rule 1 x 92 [532,424]
+ CRUSH rule 1 x 93 [218,489]
+ CRUSH rule 1 x 94 [181,96]
+ CRUSH rule 1 x 95 [343,957]
+ CRUSH rule 1 x 96 [861,270]
+ CRUSH rule 1 x 97 [459,706]
+ CRUSH rule 1 x 98 [327,867]
+ CRUSH rule 1 x 99 [974,133]
+ CRUSH rule 1 x 100 [32,445]
+ CRUSH rule 1 x 101 [142,90]
+ CRUSH rule 1 x 102 [172,129]
+ CRUSH rule 1 x 103 [630,47]
+ CRUSH rule 1 x 104 [758,133]
+ CRUSH rule 1 x 105 [843,604]
+ CRUSH rule 1 x 106 [28,681]
+ CRUSH rule 1 x 107 [74,320]
+ CRUSH rule 1 x 108 [875,593]
+ CRUSH rule 1 x 109 [411,985]
+ CRUSH rule 1 x 110 [440,774]
+ CRUSH rule 1 x 111 [405,742]
+ CRUSH rule 1 x 112 [143,181]
+ CRUSH rule 1 x 113 [153,846]
+ CRUSH rule 1 x 114 [804,892]
+ CRUSH rule 1 x 115 [588,508]
+ CRUSH rule 1 x 116 [327,148]
+ CRUSH rule 1 x 117 [95,594]
+ CRUSH rule 1 x 118 [80,957]
+ CRUSH rule 1 x 119 [386,932]
+ CRUSH rule 1 x 120 [366,312]
+ CRUSH rule 1 x 121 [129,154]
+ CRUSH rule 1 x 122 [873,1]
+ CRUSH rule 1 x 123 [533,415]
+ CRUSH rule 1 x 124 [461,691]
+ CRUSH rule 1 x 125 [342,599]
+ CRUSH rule 1 x 126 [819,781]
+ CRUSH rule 1 x 127 [437,893]
+ CRUSH rule 1 x 128 [679,994]
+ CRUSH rule 1 x 129 [380,685]
+ CRUSH rule 1 x 130 [992,52]
+ CRUSH rule 1 x 131 [469,90]
+ CRUSH rule 1 x 132 [571,250]
+ CRUSH rule 1 x 133 [964,728]
+ CRUSH rule 1 x 134 [999,19]
+ CRUSH rule 1 x 135 [634,101]
+ CRUSH rule 1 x 136 [114,889]
+ CRUSH rule 1 x 137 [839,8]
+ CRUSH rule 1 x 138 [967,949]
+ CRUSH rule 1 x 139 [308,711]
+ CRUSH rule 1 x 140 [764,936]
+ CRUSH rule 1 x 141 [423,302]
+ CRUSH rule 1 x 142 [252,821]
+ CRUSH rule 1 x 143 [33,808]
+ CRUSH rule 1 x 144 [472,88]
+ CRUSH rule 1 x 145 [242,208]
+ CRUSH rule 1 x 146 [290,70]
+ CRUSH rule 1 x 147 [447,352]
+ CRUSH rule 1 x 148 [212,644]
+ CRUSH rule 1 x 149 [9,775]
+ CRUSH rule 1 x 150 [166,456]
+ CRUSH rule 1 x 151 [811,875]
+ CRUSH rule 1 x 152 [449,617]
+ CRUSH rule 1 x 153 [523,537]
+ CRUSH rule 1 x 154 [208,559]
+ CRUSH rule 1 x 155 [569,325]
+ CRUSH rule 1 x 156 [488,121]
+ CRUSH rule 1 x 157 [140,723]
+ CRUSH rule 1 x 158 [786,451]
+ CRUSH rule 1 x 159 [134,664]
+ CRUSH rule 1 x 160 [690,112]
+ CRUSH rule 1 x 161 [324,912]
+ CRUSH rule 1 x 162 [748,567]
+ CRUSH rule 1 x 163 [575,499]
+ CRUSH rule 1 x 164 [314,489]
+ CRUSH rule 1 x 165 [116,209]
+ CRUSH rule 1 x 166 [352,706]
+ CRUSH rule 1 x 167 [27,743]
+ CRUSH rule 1 x 168 [953,898]
+ CRUSH rule 1 x 169 [912,147]
+ CRUSH rule 1 x 170 [421,515]
+ CRUSH rule 1 x 171 [488,584]
+ CRUSH rule 1 x 172 [366,443]
+ CRUSH rule 1 x 173 [863,291]
+ CRUSH rule 1 x 174 [263,555]
+ CRUSH rule 1 x 175 [875,961]
+ CRUSH rule 1 x 176 [745,83]
+ CRUSH rule 1 x 177 [128,244]
+ CRUSH rule 1 x 178 [155,41]
+ CRUSH rule 1 x 179 [593,833]
+ CRUSH rule 1 x 180 [154,734]
+ CRUSH rule 1 x 181 [289,675]
+ CRUSH rule 1 x 182 [730,931]
+ CRUSH rule 1 x 183 [639,237]
+ CRUSH rule 1 x 184 [704,312]
+ CRUSH rule 1 x 185 [97,100]
+ CRUSH rule 1 x 186 [26,665]
+ CRUSH rule 1 x 187 [649,14]
+ CRUSH rule 1 x 188 [682,695]
+ CRUSH rule 1 x 189 [325,693]
+ CRUSH rule 1 x 190 [399,933]
+ CRUSH rule 1 x 191 [629,533]
+ CRUSH rule 1 x 192 [503,578]
+ CRUSH rule 1 x 193 [546,333]
+ CRUSH rule 1 x 194 [242,473]
+ CRUSH rule 1 x 195 [625,719]
+ CRUSH rule 1 x 196 [357,114]
+ CRUSH rule 1 x 197 [306,954]
+ CRUSH rule 1 x 198 [863,791]
+ CRUSH rule 1 x 199 [935,906]
+ CRUSH rule 1 x 200 [373,774]
+ CRUSH rule 1 x 201 [659,320]
+ CRUSH rule 1 x 202 [260,433]
+ CRUSH rule 1 x 203 [36,239]
+ CRUSH rule 1 x 204 [92,516]
+ CRUSH rule 1 x 205 [68,395]
+ CRUSH rule 1 x 206 [570,530]
+ CRUSH rule 1 x 207 [834,457]
+ CRUSH rule 1 x 208 [927,484]
+ CRUSH rule 1 x 209 [878,66]
+ CRUSH rule 1 x 210 [572,981]
+ CRUSH rule 1 x 211 [107,597]
+ CRUSH rule 1 x 212 [389,107]
+ CRUSH rule 1 x 213 [497,717]
+ CRUSH rule 1 x 214 [798,65]
+ CRUSH rule 1 x 215 [233,419]
+ CRUSH rule 1 x 216 [494,464]
+ CRUSH rule 1 x 217 [352,396]
+ CRUSH rule 1 x 218 [895,864]
+ CRUSH rule 1 x 219 [222,534]
+ CRUSH rule 1 x 220 [281,19]
+ CRUSH rule 1 x 221 [64,928]
+ CRUSH rule 1 x 222 [40,544]
+ CRUSH rule 1 x 223 [645,556]
+ CRUSH rule 1 x 224 [647,165]
+ CRUSH rule 1 x 225 [219,714]
+ CRUSH rule 1 x 226 [372,511]
+ CRUSH rule 1 x 227 [925,156]
+ CRUSH rule 1 x 228 [682,404]
+ CRUSH rule 1 x 229 [880,838]
+ CRUSH rule 1 x 230 [328,659]
+ CRUSH rule 1 x 231 [320,383]
+ CRUSH rule 1 x 232 [924,846]
+ CRUSH rule 1 x 233 [948,652]
+ CRUSH rule 1 x 234 [484,943]
+ CRUSH rule 1 x 235 [750,65]
+ CRUSH rule 1 x 236 [551,787]
+ CRUSH rule 1 x 237 [390,157]
+ CRUSH rule 1 x 238 [570,6]
+ CRUSH rule 1 x 239 [729,959]
+ CRUSH rule 1 x 240 [981,241]
+ CRUSH rule 1 x 241 [310,816]
+ CRUSH rule 1 x 242 [161,63]
+ CRUSH rule 1 x 243 [180,394]
+ CRUSH rule 1 x 244 [52,174]
+ CRUSH rule 1 x 245 [523,121]
+ CRUSH rule 1 x 246 [362,893]
+ CRUSH rule 1 x 247 [382,184]
+ CRUSH rule 1 x 248 [129,114]
+ CRUSH rule 1 x 249 [159,683]
+ CRUSH rule 1 x 250 [404,945]
+ CRUSH rule 1 x 251 [661,225]
+ CRUSH rule 1 x 252 [961,226]
+ CRUSH rule 1 x 253 [651,97]
+ CRUSH rule 1 x 254 [123,33]
+ CRUSH rule 1 x 255 [314,649]
+ CRUSH rule 1 x 256 [315,215]
+ CRUSH rule 1 x 257 [825,264]
+ CRUSH rule 1 x 258 [624,789]
+ CRUSH rule 1 x 259 [602,542]
+ CRUSH rule 1 x 260 [717,878]
+ CRUSH rule 1 x 261 [145,517]
+ CRUSH rule 1 x 262 [223,1]
+ CRUSH rule 1 x 263 [462,211]
+ CRUSH rule 1 x 264 [654,471]
+ CRUSH rule 1 x 265 [302,794]
+ CRUSH rule 1 x 266 [202,132]
+ CRUSH rule 1 x 267 [282,938]
+ CRUSH rule 1 x 268 [338,309]
+ CRUSH rule 1 x 269 [738,122]
+ CRUSH rule 1 x 270 [707,982]
+ CRUSH rule 1 x 271 [705,432]
+ CRUSH rule 1 x 272 [756,545]
+ CRUSH rule 1 x 273 [197,502]
+ CRUSH rule 1 x 274 [992,44]
+ CRUSH rule 1 x 275 [544,789]
+ CRUSH rule 1 x 276 [658,467]
+ CRUSH rule 1 x 277 [143,490]
+ CRUSH rule 1 x 278 [492,647]
+ CRUSH rule 1 x 279 [517,792]
+ CRUSH rule 1 x 280 [825,740]
+ CRUSH rule 1 x 281 [224,629]
+ CRUSH rule 1 x 282 [298,661]
+ CRUSH rule 1 x 283 [311,606]
+ CRUSH rule 1 x 284 [771,466]
+ CRUSH rule 1 x 285 [693,362]
+ CRUSH rule 1 x 286 [364,477]
+ CRUSH rule 1 x 287 [591,611]
+ CRUSH rule 1 x 288 [965,541]
+ CRUSH rule 1 x 289 [225,551]
+ CRUSH rule 1 x 290 [577,762]
+ CRUSH rule 1 x 291 [160,903]
+ CRUSH rule 1 x 292 [873,598]
+ CRUSH rule 1 x 293 [100,234]
+ CRUSH rule 1 x 294 [285,943]
+ CRUSH rule 1 x 295 [938,262]
+ CRUSH rule 1 x 296 [850,327]
+ CRUSH rule 1 x 297 [951,53]
+ CRUSH rule 1 x 298 [173,336]
+ CRUSH rule 1 x 299 [598,591]
+ CRUSH rule 1 x 300 [531,957]
+ CRUSH rule 1 x 301 [823,628]
+ CRUSH rule 1 x 302 [184,80]
+ CRUSH rule 1 x 303 [521,766]
+ CRUSH rule 1 x 304 [980,127]
+ CRUSH rule 1 x 305 [153,816]
+ CRUSH rule 1 x 306 [423,739]
+ CRUSH rule 1 x 307 [997,557]
+ CRUSH rule 1 x 308 [991,874]
+ CRUSH rule 1 x 309 [860,394]
+ CRUSH rule 1 x 310 [589,818]
+ CRUSH rule 1 x 311 [477,774]
+ CRUSH rule 1 x 312 [887,853]
+ CRUSH rule 1 x 313 [802,646]
+ CRUSH rule 1 x 314 [654,974]
+ CRUSH rule 1 x 315 [767,227]
+ CRUSH rule 1 x 316 [778,83]
+ CRUSH rule 1 x 317 [184,418]
+ CRUSH rule 1 x 318 [525,410]
+ CRUSH rule 1 x 319 [476,724]
+ CRUSH rule 1 x 320 [149,610]
+ CRUSH rule 1 x 321 [710,79]
+ CRUSH rule 1 x 322 [175,275]
+ CRUSH rule 1 x 323 [819,604]
+ CRUSH rule 1 x 324 [16,745]
+ CRUSH rule 1 x 325 [486,400]
+ CRUSH rule 1 x 326 [613,765]
+ CRUSH rule 1 x 327 [125,289]
+ CRUSH rule 1 x 328 [807,383]
+ CRUSH rule 1 x 329 [588,938]
+ CRUSH rule 1 x 330 [932,644]
+ CRUSH rule 1 x 331 [341,953]
+ CRUSH rule 1 x 332 [153,726]
+ CRUSH rule 1 x 333 [745,845]
+ CRUSH rule 1 x 334 [614,751]
+ CRUSH rule 1 x 335 [518,721]
+ CRUSH rule 1 x 336 [389,424]
+ CRUSH rule 1 x 337 [753,508]
+ CRUSH rule 1 x 338 [128,810]
+ CRUSH rule 1 x 339 [430,308]
+ CRUSH rule 1 x 340 [541,44]
+ CRUSH rule 1 x 341 [402,26]
+ CRUSH rule 1 x 342 [982,57]
+ CRUSH rule 1 x 343 [833,412]
+ CRUSH rule 1 x 344 [784,533]
+ CRUSH rule 1 x 345 [546,300]
+ CRUSH rule 1 x 346 [302,420]
+ CRUSH rule 1 x 347 [488,778]
+ CRUSH rule 1 x 348 [903,744]
+ CRUSH rule 1 x 349 [471,547]
+ CRUSH rule 1 x 350 [348,221]
+ CRUSH rule 1 x 351 [961,582]
+ CRUSH rule 1 x 352 [728,137]
+ CRUSH rule 1 x 353 [904,202]
+ CRUSH rule 1 x 354 [345,226]
+ CRUSH rule 1 x 355 [50,430]
+ CRUSH rule 1 x 356 [87,185]
+ CRUSH rule 1 x 357 [762,459]
+ CRUSH rule 1 x 358 [908,25]
+ CRUSH rule 1 x 359 [484,15]
+ CRUSH rule 1 x 360 [173,378]
+ CRUSH rule 1 x 361 [404,577]
+ CRUSH rule 1 x 362 [403,1]
+ CRUSH rule 1 x 363 [639,911]
+ CRUSH rule 1 x 364 [752,689]
+ CRUSH rule 1 x 365 [956,999]
+ CRUSH rule 1 x 366 [860,925]
+ CRUSH rule 1 x 367 [205,609]
+ CRUSH rule 1 x 368 [301,284]
+ CRUSH rule 1 x 369 [452,658]
+ CRUSH rule 1 x 370 [11,467]
+ CRUSH rule 1 x 371 [124,487]
+ CRUSH rule 1 x 372 [253,48]
+ CRUSH rule 1 x 373 [715,605]
+ CRUSH rule 1 x 374 [191,887]
+ CRUSH rule 1 x 375 [711,385]
+ CRUSH rule 1 x 376 [597,818]
+ CRUSH rule 1 x 377 [294,256]
+ CRUSH rule 1 x 378 [34,151]
+ CRUSH rule 1 x 379 [869,136]
+ CRUSH rule 1 x 380 [294,97]
+ CRUSH rule 1 x 381 [119,710]
+ CRUSH rule 1 x 382 [69,631]
+ CRUSH rule 1 x 383 [922,588]
+ CRUSH rule 1 x 384 [221,945]
+ CRUSH rule 1 x 385 [561,737]
+ CRUSH rule 1 x 386 [335,442]
+ CRUSH rule 1 x 387 [514,43]
+ CRUSH rule 1 x 388 [587,89]
+ CRUSH rule 1 x 389 [109,641]
+ CRUSH rule 1 x 390 [925,149]
+ CRUSH rule 1 x 391 [267,87]
+ CRUSH rule 1 x 392 [382,485]
+ CRUSH rule 1 x 393 [425,721]
+ CRUSH rule 1 x 394 [898,18]
+ CRUSH rule 1 x 395 [806,876]
+ CRUSH rule 1 x 396 [790,970]
+ CRUSH rule 1 x 397 [136,363]
+ CRUSH rule 1 x 398 [914,116]
+ CRUSH rule 1 x 399 [261,94]
+ CRUSH rule 1 x 400 [661,197]
+ CRUSH rule 1 x 401 [953,979]
+ CRUSH rule 1 x 402 [738,819]
+ CRUSH rule 1 x 403 [573,238]
+ CRUSH rule 1 x 404 [526,848]
+ CRUSH rule 1 x 405 [582,505]
+ CRUSH rule 1 x 406 [768,324]
+ CRUSH rule 1 x 407 [260,951]
+ CRUSH rule 1 x 408 [657,81]
+ CRUSH rule 1 x 409 [498,89]
+ CRUSH rule 1 x 410 [28,793]
+ CRUSH rule 1 x 411 [684,992]
+ CRUSH rule 1 x 412 [261,958]
+ CRUSH rule 1 x 413 [891,835]
+ CRUSH rule 1 x 414 [127,459]
+ CRUSH rule 1 x 415 [272,540]
+ CRUSH rule 1 x 416 [739,617]
+ CRUSH rule 1 x 417 [106,209]
+ CRUSH rule 1 x 418 [525,441]
+ CRUSH rule 1 x 419 [603,673]
+ CRUSH rule 1 x 420 [988,213]
+ CRUSH rule 1 x 421 [761,521]
+ CRUSH rule 1 x 422 [317,160]
+ CRUSH rule 1 x 423 [137,807]
+ CRUSH rule 1 x 424 [920,37]
+ CRUSH rule 1 x 425 [277,693]
+ CRUSH rule 1 x 426 [485,936]
+ CRUSH rule 1 x 427 [242,515]
+ CRUSH rule 1 x 428 [632,635]
+ CRUSH rule 1 x 429 [641,73]
+ CRUSH rule 1 x 430 [626,585]
+ CRUSH rule 1 x 431 [697,76]
+ CRUSH rule 1 x 432 [590,526]
+ CRUSH rule 1 x 433 [284,387]
+ CRUSH rule 1 x 434 [538,985]
+ CRUSH rule 1 x 435 [30,318]
+ CRUSH rule 1 x 436 [164,919]
+ CRUSH rule 1 x 437 [322,212]
+ CRUSH rule 1 x 438 [142,392]
+ CRUSH rule 1 x 439 [119,370]
+ CRUSH rule 1 x 440 [333,403]
+ CRUSH rule 1 x 441 [477,727]
+ CRUSH rule 1 x 442 [274,590]
+ CRUSH rule 1 x 443 [983,748]
+ CRUSH rule 1 x 444 [536,509]
+ CRUSH rule 1 x 445 [485,209]
+ CRUSH rule 1 x 446 [345,634]
+ CRUSH rule 1 x 447 [61,845]
+ CRUSH rule 1 x 448 [333,232]
+ CRUSH rule 1 x 449 [680,16]
+ CRUSH rule 1 x 450 [235,214]
+ CRUSH rule 1 x 451 [961,468]
+ CRUSH rule 1 x 452 [525,479]
+ CRUSH rule 1 x 453 [138,466]
+ CRUSH rule 1 x 454 [137,625]
+ CRUSH rule 1 x 455 [173,150]
+ CRUSH rule 1 x 456 [235,226]
+ CRUSH rule 1 x 457 [450,577]
+ CRUSH rule 1 x 458 [195,537]
+ CRUSH rule 1 x 459 [381,555]
+ CRUSH rule 1 x 460 [972,730]
+ CRUSH rule 1 x 461 [506,279]
+ CRUSH rule 1 x 462 [692,959]
+ CRUSH rule 1 x 463 [788,667]
+ CRUSH rule 1 x 464 [133,122]
+ CRUSH rule 1 x 465 [971,190]
+ CRUSH rule 1 x 466 [394,576]
+ CRUSH rule 1 x 467 [517,28]
+ CRUSH rule 1 x 468 [829,143]
+ CRUSH rule 1 x 469 [987,936]
+ CRUSH rule 1 x 470 [107,982]
+ CRUSH rule 1 x 471 [181,897]
+ CRUSH rule 1 x 472 [547,512]
+ CRUSH rule 1 x 473 [760,997]
+ CRUSH rule 1 x 474 [787,418]
+ CRUSH rule 1 x 475 [662,312]
+ CRUSH rule 1 x 476 [110,495]
+ CRUSH rule 1 x 477 [393,954]
+ CRUSH rule 1 x 478 [246,483]
+ CRUSH rule 1 x 479 [70,929]
+ CRUSH rule 1 x 480 [753,119]
+ CRUSH rule 1 x 481 [470,429]
+ CRUSH rule 1 x 482 [451,566]
+ CRUSH rule 1 x 483 [816,72]
+ CRUSH rule 1 x 484 [540,454]
+ CRUSH rule 1 x 485 [74,582]
+ CRUSH rule 1 x 486 [958,595]
+ CRUSH rule 1 x 487 [228,302]
+ CRUSH rule 1 x 488 [180,529]
+ CRUSH rule 1 x 489 [47,617]
+ CRUSH rule 1 x 490 [905,822]
+ CRUSH rule 1 x 491 [892,370]
+ CRUSH rule 1 x 492 [588,959]
+ CRUSH rule 1 x 493 [353,461]
+ CRUSH rule 1 x 494 [378,848]
+ CRUSH rule 1 x 495 [845,653]
+ CRUSH rule 1 x 496 [13,988]
+ CRUSH rule 1 x 497 [796,877]
+ CRUSH rule 1 x 498 [412,337]
+ CRUSH rule 1 x 499 [330,695]
+ CRUSH rule 1 x 500 [820,272]
+ CRUSH rule 1 x 501 [110,44]
+ CRUSH rule 1 x 502 [336,595]
+ CRUSH rule 1 x 503 [922,211]
+ CRUSH rule 1 x 504 [483,52]
+ CRUSH rule 1 x 505 [482,598]
+ CRUSH rule 1 x 506 [493,123]
+ CRUSH rule 1 x 507 [12,598]
+ CRUSH rule 1 x 508 [227,157]
+ CRUSH rule 1 x 509 [807,242]
+ CRUSH rule 1 x 510 [134,437]
+ CRUSH rule 1 x 511 [212,54]
+ CRUSH rule 1 x 512 [236,630]
+ CRUSH rule 1 x 513 [994,693]
+ CRUSH rule 1 x 514 [45,508]
+ CRUSH rule 1 x 515 [504,138]
+ CRUSH rule 1 x 516 [285,409]
+ CRUSH rule 1 x 517 [300,232]
+ CRUSH rule 1 x 518 [397,674]
+ CRUSH rule 1 x 519 [86,750]
+ CRUSH rule 1 x 520 [900,833]
+ CRUSH rule 1 x 521 [31,47]
+ CRUSH rule 1 x 522 [390,16]
+ CRUSH rule 1 x 523 [618,308]
+ CRUSH rule 1 x 524 [635,189]
+ CRUSH rule 1 x 525 [311,916]
+ CRUSH rule 1 x 526 [48,738]
+ CRUSH rule 1 x 527 [202,851]
+ CRUSH rule 1 x 528 [565,827]
+ CRUSH rule 1 x 529 [934,864]
+ CRUSH rule 1 x 530 [502,934]
+ CRUSH rule 1 x 531 [681,627]
+ CRUSH rule 1 x 532 [422,6]
+ CRUSH rule 1 x 533 [863,68]
+ CRUSH rule 1 x 534 [962,931]
+ CRUSH rule 1 x 535 [89,565]
+ CRUSH rule 1 x 536 [499,351]
+ CRUSH rule 1 x 537 [676,547]
+ CRUSH rule 1 x 538 [58,644]
+ CRUSH rule 1 x 539 [837,953]
+ CRUSH rule 1 x 540 [831,50]
+ CRUSH rule 1 x 541 [582,757]
+ CRUSH rule 1 x 542 [472,132]
+ CRUSH rule 1 x 543 [382,272]
+ CRUSH rule 1 x 544 [947,930]
+ CRUSH rule 1 x 545 [425,570]
+ CRUSH rule 1 x 546 [18,65]
+ CRUSH rule 1 x 547 [445,715]
+ CRUSH rule 1 x 548 [367,569]
+ CRUSH rule 1 x 549 [125,715]
+ CRUSH rule 1 x 550 [425,599]
+ CRUSH rule 1 x 551 [44,1]
+ CRUSH rule 1 x 552 [246,104]
+ CRUSH rule 1 x 553 [71,703]
+ CRUSH rule 1 x 554 [207,124]
+ CRUSH rule 1 x 555 [570,28]
+ CRUSH rule 1 x 556 [674,152]
+ CRUSH rule 1 x 557 [347,817]
+ CRUSH rule 1 x 558 [627,426]
+ CRUSH rule 1 x 559 [940,630]
+ CRUSH rule 1 x 560 [295,903]
+ CRUSH rule 1 x 561 [506,682]
+ CRUSH rule 1 x 562 [718,529]
+ CRUSH rule 1 x 563 [552,332]
+ CRUSH rule 1 x 564 [835,769]
+ CRUSH rule 1 x 565 [8,167]
+ CRUSH rule 1 x 566 [600,481]
+ CRUSH rule 1 x 567 [999,994]
+ CRUSH rule 1 x 568 [252,431]
+ CRUSH rule 1 x 569 [643,218]
+ CRUSH rule 1 x 570 [617,635]
+ CRUSH rule 1 x 571 [757,80]
+ CRUSH rule 1 x 572 [299,348]
+ CRUSH rule 1 x 573 [25,505]
+ CRUSH rule 1 x 574 [215,431]
+ CRUSH rule 1 x 575 [225,252]
+ CRUSH rule 1 x 576 [627,94]
+ CRUSH rule 1 x 577 [237,809]
+ CRUSH rule 1 x 578 [885,313]
+ CRUSH rule 1 x 579 [924,575]
+ CRUSH rule 1 x 580 [718,51]
+ CRUSH rule 1 x 581 [219,807]
+ CRUSH rule 1 x 582 [893,701]
+ CRUSH rule 1 x 583 [246,930]
+ CRUSH rule 1 x 584 [336,432]
+ CRUSH rule 1 x 585 [324,999]
+ CRUSH rule 1 x 586 [558,230]
+ CRUSH rule 1 x 587 [985,830]
+ CRUSH rule 1 x 588 [211,544]
+ CRUSH rule 1 x 589 [129,21]
+ CRUSH rule 1 x 590 [467,969]
+ CRUSH rule 1 x 591 [758,514]
+ CRUSH rule 1 x 592 [525,253]
+ CRUSH rule 1 x 593 [601,885]
+ CRUSH rule 1 x 594 [227,60]
+ CRUSH rule 1 x 595 [720,854]
+ CRUSH rule 1 x 596 [751,195]
+ CRUSH rule 1 x 597 [129,574]
+ CRUSH rule 1 x 598 [679,207]
+ CRUSH rule 1 x 599 [668,315]
+ CRUSH rule 1 x 600 [143,396]
+ CRUSH rule 1 x 601 [326,573]
+ CRUSH rule 1 x 602 [860,281]
+ CRUSH rule 1 x 603 [709,328]
+ CRUSH rule 1 x 604 [571,62]
+ CRUSH rule 1 x 605 [252,739]
+ CRUSH rule 1 x 606 [339,236]
+ CRUSH rule 1 x 607 [590,248]
+ CRUSH rule 1 x 608 [145,635]
+ CRUSH rule 1 x 609 [973,547]
+ CRUSH rule 1 x 610 [435,816]
+ CRUSH rule 1 x 611 [559,283]
+ CRUSH rule 1 x 612 [273,149]
+ CRUSH rule 1 x 613 [828,614]
+ CRUSH rule 1 x 614 [478,748]
+ CRUSH rule 1 x 615 [392,155]
+ CRUSH rule 1 x 616 [778,637]
+ CRUSH rule 1 x 617 [622,713]
+ CRUSH rule 1 x 618 [149,877]
+ CRUSH rule 1 x 619 [604,163]
+ CRUSH rule 1 x 620 [181,23]
+ CRUSH rule 1 x 621 [735,902]
+ CRUSH rule 1 x 622 [661,824]
+ CRUSH rule 1 x 623 [142,121]
+ CRUSH rule 1 x 624 [360,716]
+ CRUSH rule 1 x 625 [541,167]
+ CRUSH rule 1 x 626 [364,431]
+ CRUSH rule 1 x 627 [458,137]
+ CRUSH rule 1 x 628 [250,350]
+ CRUSH rule 1 x 629 [928,160]
+ CRUSH rule 1 x 630 [243,19]
+ CRUSH rule 1 x 631 [438,221]
+ CRUSH rule 1 x 632 [797,368]
+ CRUSH rule 1 x 633 [993,749]
+ CRUSH rule 1 x 634 [239,351]
+ CRUSH rule 1 x 635 [640,965]
+ CRUSH rule 1 x 636 [173,290]
+ CRUSH rule 1 x 637 [0,918]
+ CRUSH rule 1 x 638 [702,235]
+ CRUSH rule 1 x 639 [475,687]
+ CRUSH rule 1 x 640 [31,664]
+ CRUSH rule 1 x 641 [296,473]
+ CRUSH rule 1 x 642 [894,273]
+ CRUSH rule 1 x 643 [117,111]
+ CRUSH rule 1 x 644 [438,336]
+ CRUSH rule 1 x 645 [982,702]
+ CRUSH rule 1 x 646 [334,804]
+ CRUSH rule 1 x 647 [933,787]
+ CRUSH rule 1 x 648 [22,444]
+ CRUSH rule 1 x 649 [503,229]
+ CRUSH rule 1 x 650 [328,659]
+ CRUSH rule 1 x 651 [3,880]
+ CRUSH rule 1 x 652 [495,977]
+ CRUSH rule 1 x 653 [185,718]
+ CRUSH rule 1 x 654 [130,528]
+ CRUSH rule 1 x 655 [560,872]
+ CRUSH rule 1 x 656 [219,885]
+ CRUSH rule 1 x 657 [233,684]
+ CRUSH rule 1 x 658 [778,6]
+ CRUSH rule 1 x 659 [240,663]
+ CRUSH rule 1 x 660 [244,855]
+ CRUSH rule 1 x 661 [184,270]
+ CRUSH rule 1 x 662 [65,883]
+ CRUSH rule 1 x 663 [323,721]
+ CRUSH rule 1 x 664 [865,113]
+ CRUSH rule 1 x 665 [420,850]
+ CRUSH rule 1 x 666 [319,767]
+ CRUSH rule 1 x 667 [875,39]
+ CRUSH rule 1 x 668 [331,122]
+ CRUSH rule 1 x 669 [915,521]
+ CRUSH rule 1 x 670 [845,659]
+ CRUSH rule 1 x 671 [108,634]
+ CRUSH rule 1 x 672 [578,216]
+ CRUSH rule 1 x 673 [442,74]
+ CRUSH rule 1 x 674 [588,364]
+ CRUSH rule 1 x 675 [489,698]
+ CRUSH rule 1 x 676 [928,911]
+ CRUSH rule 1 x 677 [399,269]
+ CRUSH rule 1 x 678 [546,752]
+ CRUSH rule 1 x 679 [988,25]
+ CRUSH rule 1 x 680 [335,963]
+ CRUSH rule 1 x 681 [690,462]
+ CRUSH rule 1 x 682 [196,588]
+ CRUSH rule 1 x 683 [627,25]
+ CRUSH rule 1 x 684 [38,804]
+ CRUSH rule 1 x 685 [841,368]
+ CRUSH rule 1 x 686 [336,287]
+ CRUSH rule 1 x 687 [20,682]
+ CRUSH rule 1 x 688 [463,371]
+ CRUSH rule 1 x 689 [569,250]
+ CRUSH rule 1 x 690 [551,144]
+ CRUSH rule 1 x 691 [766,464]
+ CRUSH rule 1 x 692 [739,634]
+ CRUSH rule 1 x 693 [339,297]
+ CRUSH rule 1 x 694 [405,26]
+ CRUSH rule 1 x 695 [622,576]
+ CRUSH rule 1 x 696 [558,902]
+ CRUSH rule 1 x 697 [818,222]
+ CRUSH rule 1 x 698 [178,48]
+ CRUSH rule 1 x 699 [450,244]
+ CRUSH rule 1 x 700 [502,771]
+ CRUSH rule 1 x 701 [4,612]
+ CRUSH rule 1 x 702 [177,630]
+ CRUSH rule 1 x 703 [354,178]
+ CRUSH rule 1 x 704 [646,601]
+ CRUSH rule 1 x 705 [921,401]
+ CRUSH rule 1 x 706 [652,877]
+ CRUSH rule 1 x 707 [345,745]
+ CRUSH rule 1 x 708 [333,607]
+ CRUSH rule 1 x 709 [45,187]
+ CRUSH rule 1 x 710 [94,855]
+ CRUSH rule 1 x 711 [227,653]
+ CRUSH rule 1 x 712 [398,953]
+ CRUSH rule 1 x 713 [116,800]
+ CRUSH rule 1 x 714 [111,629]
+ CRUSH rule 1 x 715 [531,291]
+ CRUSH rule 1 x 716 [169,541]
+ CRUSH rule 1 x 717 [417,446]
+ CRUSH rule 1 x 718 [992,383]
+ CRUSH rule 1 x 719 [936,674]
+ CRUSH rule 1 x 720 [370,188]
+ CRUSH rule 1 x 721 [320,859]
+ CRUSH rule 1 x 722 [7,2]
+ CRUSH rule 1 x 723 [270,553]
+ CRUSH rule 1 x 724 [666,822]
+ CRUSH rule 1 x 725 [794,406]
+ CRUSH rule 1 x 726 [420,556]
+ CRUSH rule 1 x 727 [561,461]
+ CRUSH rule 1 x 728 [951,330]
+ CRUSH rule 1 x 729 [656,644]
+ CRUSH rule 1 x 730 [3,558]
+ CRUSH rule 1 x 731 [852,89]
+ CRUSH rule 1 x 732 [983,840]
+ CRUSH rule 1 x 733 [285,396]
+ CRUSH rule 1 x 734 [125,510]
+ CRUSH rule 1 x 735 [417,773]
+ CRUSH rule 1 x 736 [749,396]
+ CRUSH rule 1 x 737 [644,991]
+ CRUSH rule 1 x 738 [449,683]
+ CRUSH rule 1 x 739 [341,220]
+ CRUSH rule 1 x 740 [874,524]
+ CRUSH rule 1 x 741 [189,472]
+ CRUSH rule 1 x 742 [912,581]
+ CRUSH rule 1 x 743 [654,914]
+ CRUSH rule 1 x 744 [725,295]
+ CRUSH rule 1 x 745 [787,858]
+ CRUSH rule 1 x 746 [757,848]
+ CRUSH rule 1 x 747 [700,81]
+ CRUSH rule 1 x 748 [557,436]
+ CRUSH rule 1 x 749 [772,622]
+ CRUSH rule 1 x 750 [946,97]
+ CRUSH rule 1 x 751 [996,618]
+ CRUSH rule 1 x 752 [746,887]
+ CRUSH rule 1 x 753 [741,14]
+ CRUSH rule 1 x 754 [648,349]
+ CRUSH rule 1 x 755 [157,460]
+ CRUSH rule 1 x 756 [416,97]
+ CRUSH rule 1 x 757 [599,839]
+ CRUSH rule 1 x 758 [994,218]
+ CRUSH rule 1 x 759 [959,682]
+ CRUSH rule 1 x 760 [518,943]
+ CRUSH rule 1 x 761 [285,849]
+ CRUSH rule 1 x 762 [591,313]
+ CRUSH rule 1 x 763 [908,411]
+ CRUSH rule 1 x 764 [787,234]
+ CRUSH rule 1 x 765 [327,921]
+ CRUSH rule 1 x 766 [84,161]
+ CRUSH rule 1 x 767 [370,895]
+ CRUSH rule 1 x 768 [826,760]
+ CRUSH rule 1 x 769 [67,768]
+ CRUSH rule 1 x 770 [593,909]
+ CRUSH rule 1 x 771 [309,935]
+ CRUSH rule 1 x 772 [12,125]
+ CRUSH rule 1 x 773 [253,466]
+ CRUSH rule 1 x 774 [164,390]
+ CRUSH rule 1 x 775 [703,47]
+ CRUSH rule 1 x 776 [728,231]
+ CRUSH rule 1 x 777 [981,621]
+ CRUSH rule 1 x 778 [411,456]
+ CRUSH rule 1 x 779 [346,121]
+ CRUSH rule 1 x 780 [476,39]
+ CRUSH rule 1 x 781 [10,130]
+ CRUSH rule 1 x 782 [462,246]
+ CRUSH rule 1 x 783 [580,373]
+ CRUSH rule 1 x 784 [413,113]
+ CRUSH rule 1 x 785 [341,856]
+ CRUSH rule 1 x 786 [411,140]
+ CRUSH rule 1 x 787 [605,522]
+ CRUSH rule 1 x 788 [226,545]
+ CRUSH rule 1 x 789 [545,320]
+ CRUSH rule 1 x 790 [414,748]
+ CRUSH rule 1 x 791 [660,906]
+ CRUSH rule 1 x 792 [287,392]
+ CRUSH rule 1 x 793 [631,133]
+ CRUSH rule 1 x 794 [931,517]
+ CRUSH rule 1 x 795 [551,962]
+ CRUSH rule 1 x 796 [814,4]
+ CRUSH rule 1 x 797 [64,201]
+ CRUSH rule 1 x 798 [422,530]
+ CRUSH rule 1 x 799 [824,32]
+ CRUSH rule 1 x 800 [862,623]
+ CRUSH rule 1 x 801 [145,550]
+ CRUSH rule 1 x 802 [570,19]
+ CRUSH rule 1 x 803 [151,812]
+ CRUSH rule 1 x 804 [467,93]
+ CRUSH rule 1 x 805 [621,223]
+ CRUSH rule 1 x 806 [898,957]
+ CRUSH rule 1 x 807 [354,531]
+ CRUSH rule 1 x 808 [7,96]
+ CRUSH rule 1 x 809 [70,734]
+ CRUSH rule 1 x 810 [701,18]
+ CRUSH rule 1 x 811 [248,547]
+ CRUSH rule 1 x 812 [230,576]
+ CRUSH rule 1 x 813 [805,114]
+ CRUSH rule 1 x 814 [54,619]
+ CRUSH rule 1 x 815 [679,412]
+ CRUSH rule 1 x 816 [919,448]
+ CRUSH rule 1 x 817 [765,830]
+ CRUSH rule 1 x 818 [415,566]
+ CRUSH rule 1 x 819 [721,319]
+ CRUSH rule 1 x 820 [218,301]
+ CRUSH rule 1 x 821 [185,795]
+ CRUSH rule 1 x 822 [356,261]
+ CRUSH rule 1 x 823 [220,281]
+ CRUSH rule 1 x 824 [292,809]
+ CRUSH rule 1 x 825 [949,778]
+ CRUSH rule 1 x 826 [767,818]
+ CRUSH rule 1 x 827 [631,83]
+ CRUSH rule 1 x 828 [288,986]
+ CRUSH rule 1 x 829 [990,667]
+ CRUSH rule 1 x 830 [152,571]
+ CRUSH rule 1 x 831 [814,563]
+ CRUSH rule 1 x 832 [235,641]
+ CRUSH rule 1 x 833 [657,565]
+ CRUSH rule 1 x 834 [907,231]
+ CRUSH rule 1 x 835 [784,262]
+ CRUSH rule 1 x 836 [951,158]
+ CRUSH rule 1 x 837 [556,498]
+ CRUSH rule 1 x 838 [329,274]
+ CRUSH rule 1 x 839 [568,209]
+ CRUSH rule 1 x 840 [45,579]
+ CRUSH rule 1 x 841 [652,702]
+ CRUSH rule 1 x 842 [629,984]
+ CRUSH rule 1 x 843 [799,690]
+ CRUSH rule 1 x 844 [694,600]
+ CRUSH rule 1 x 845 [332,30]
+ CRUSH rule 1 x 846 [452,251]
+ CRUSH rule 1 x 847 [399,681]
+ CRUSH rule 1 x 848 [303,138]
+ CRUSH rule 1 x 849 [666,346]
+ CRUSH rule 1 x 850 [644,511]
+ CRUSH rule 1 x 851 [527,546]
+ CRUSH rule 1 x 852 [31,809]
+ CRUSH rule 1 x 853 [483,330]
+ CRUSH rule 1 x 854 [697,953]
+ CRUSH rule 1 x 855 [837,996]
+ CRUSH rule 1 x 856 [712,40]
+ CRUSH rule 1 x 857 [77,984]
+ CRUSH rule 1 x 858 [412,384]
+ CRUSH rule 1 x 859 [173,760]
+ CRUSH rule 1 x 860 [776,429]
+ CRUSH rule 1 x 861 [705,405]
+ CRUSH rule 1 x 862 [809,44]
+ CRUSH rule 1 x 863 [349,496]
+ CRUSH rule 1 x 864 [717,858]
+ CRUSH rule 1 x 865 [857,603]
+ CRUSH rule 1 x 866 [394,304]
+ CRUSH rule 1 x 867 [640,773]
+ CRUSH rule 1 x 868 [613,950]
+ CRUSH rule 1 x 869 [973,889]
+ CRUSH rule 1 x 870 [505,35]
+ CRUSH rule 1 x 871 [239,264]
+ CRUSH rule 1 x 872 [21,767]
+ CRUSH rule 1 x 873 [954,666]
+ CRUSH rule 1 x 874 [54,510]
+ CRUSH rule 1 x 875 [809,418]
+ CRUSH rule 1 x 876 [483,457]
+ CRUSH rule 1 x 877 [542,531]
+ CRUSH rule 1 x 878 [217,674]
+ CRUSH rule 1 x 879 [999,475]
+ CRUSH rule 1 x 880 [678,573]
+ CRUSH rule 1 x 881 [394,835]
+ CRUSH rule 1 x 882 [467,382]
+ CRUSH rule 1 x 883 [802,744]
+ CRUSH rule 1 x 884 [653,660]
+ CRUSH rule 1 x 885 [898,704]
+ CRUSH rule 1 x 886 [434,357]
+ CRUSH rule 1 x 887 [297,226]
+ CRUSH rule 1 x 888 [863,324]
+ CRUSH rule 1 x 889 [105,102]
+ CRUSH rule 1 x 890 [550,248]
+ CRUSH rule 1 x 891 [575,928]
+ CRUSH rule 1 x 892 [259,862]
+ CRUSH rule 1 x 893 [902,880]
+ CRUSH rule 1 x 894 [180,169]
+ CRUSH rule 1 x 895 [725,849]
+ CRUSH rule 1 x 896 [951,34]
+ CRUSH rule 1 x 897 [810,352]
+ CRUSH rule 1 x 898 [979,433]
+ CRUSH rule 1 x 899 [685,668]
+ CRUSH rule 1 x 900 [530,978]
+ CRUSH rule 1 x 901 [740,107]
+ CRUSH rule 1 x 902 [800,743]
+ CRUSH rule 1 x 903 [230,267]
+ CRUSH rule 1 x 904 [346,949]
+ CRUSH rule 1 x 905 [530,397]
+ CRUSH rule 1 x 906 [80,426]
+ CRUSH rule 1 x 907 [365,968]
+ CRUSH rule 1 x 908 [204,832]
+ CRUSH rule 1 x 909 [883,989]
+ CRUSH rule 1 x 910 [549,593]
+ CRUSH rule 1 x 911 [325,847]
+ CRUSH rule 1 x 912 [874,888]
+ CRUSH rule 1 x 913 [331,463]
+ CRUSH rule 1 x 914 [836,468]
+ CRUSH rule 1 x 915 [245,228]
+ CRUSH rule 1 x 916 [77,967]
+ CRUSH rule 1 x 917 [239,60]
+ CRUSH rule 1 x 918 [988,115]
+ CRUSH rule 1 x 919 [783,139]
+ CRUSH rule 1 x 920 [623,408]
+ CRUSH rule 1 x 921 [105,799]
+ CRUSH rule 1 x 922 [887,505]
+ CRUSH rule 1 x 923 [223,318]
+ CRUSH rule 1 x 924 [25,778]
+ CRUSH rule 1 x 925 [912,601]
+ CRUSH rule 1 x 926 [968,133]
+ CRUSH rule 1 x 927 [277,724]
+ CRUSH rule 1 x 928 [554,203]
+ CRUSH rule 1 x 929 [761,802]
+ CRUSH rule 1 x 930 [814,61]
+ CRUSH rule 1 x 931 [29,193]
+ CRUSH rule 1 x 932 [446,198]
+ CRUSH rule 1 x 933 [352,742]
+ CRUSH rule 1 x 934 [730,2]
+ CRUSH rule 1 x 935 [731,23]
+ CRUSH rule 1 x 936 [322,975]
+ CRUSH rule 1 x 937 [822,221]
+ CRUSH rule 1 x 938 [557,850]
+ CRUSH rule 1 x 939 [150,11]
+ CRUSH rule 1 x 940 [638,398]
+ CRUSH rule 1 x 941 [730,342]
+ CRUSH rule 1 x 942 [62,292]
+ CRUSH rule 1 x 943 [165,314]
+ CRUSH rule 1 x 944 [199,625]
+ CRUSH rule 1 x 945 [946,999]
+ CRUSH rule 1 x 946 [595,93]
+ CRUSH rule 1 x 947 [800,582]
+ CRUSH rule 1 x 948 [132,551]
+ CRUSH rule 1 x 949 [792,920]
+ CRUSH rule 1 x 950 [111,345]
+ CRUSH rule 1 x 951 [414,619]
+ CRUSH rule 1 x 952 [775,469]
+ CRUSH rule 1 x 953 [349,1]
+ CRUSH rule 1 x 954 [570,940]
+ CRUSH rule 1 x 955 [729,774]
+ CRUSH rule 1 x 956 [519,141]
+ CRUSH rule 1 x 957 [242,709]
+ CRUSH rule 1 x 958 [84,217]
+ CRUSH rule 1 x 959 [270,413]
+ CRUSH rule 1 x 960 [458,192]
+ CRUSH rule 1 x 961 [981,388]
+ CRUSH rule 1 x 962 [623,834]
+ CRUSH rule 1 x 963 [291,167]
+ CRUSH rule 1 x 964 [28,156]
+ CRUSH rule 1 x 965 [675,557]
+ CRUSH rule 1 x 966 [836,306]
+ CRUSH rule 1 x 967 [966,386]
+ CRUSH rule 1 x 968 [864,756]
+ CRUSH rule 1 x 969 [729,625]
+ CRUSH rule 1 x 970 [800,362]
+ CRUSH rule 1 x 971 [737,381]
+ CRUSH rule 1 x 972 [952,245]
+ CRUSH rule 1 x 973 [356,455]
+ CRUSH rule 1 x 974 [545,758]
+ CRUSH rule 1 x 975 [336,191]
+ CRUSH rule 1 x 976 [446,208]
+ CRUSH rule 1 x 977 [202,896]
+ CRUSH rule 1 x 978 [612,324]
+ CRUSH rule 1 x 979 [843,457]
+ CRUSH rule 1 x 980 [60,914]
+ CRUSH rule 1 x 981 [702,749]
+ CRUSH rule 1 x 982 [298,928]
+ CRUSH rule 1 x 983 [723,572]
+ CRUSH rule 1 x 984 [723,864]
+ CRUSH rule 1 x 985 [945,459]
+ CRUSH rule 1 x 986 [772,664]
+ CRUSH rule 1 x 987 [88,324]
+ CRUSH rule 1 x 988 [522,927]
+ CRUSH rule 1 x 989 [578,332]
+ CRUSH rule 1 x 990 [638,228]
+ CRUSH rule 1 x 991 [530,221]
+ CRUSH rule 1 x 992 [925,705]
+ CRUSH rule 1 x 993 [991,301]
+ CRUSH rule 1 x 994 [276,51]
+ CRUSH rule 1 x 995 [288,836]
+ CRUSH rule 1 x 996 [887,983]
+ CRUSH rule 1 x 997 [110,924]
+ CRUSH rule 1 x 998 [435,830]
+ CRUSH rule 1 x 999 [876,738]
+ CRUSH rule 1 x 1000 [178,963]
+ CRUSH rule 1 x 1001 [99,519]
+ CRUSH rule 1 x 1002 [515,534]
+ CRUSH rule 1 x 1003 [104,611]
+ CRUSH rule 1 x 1004 [269,638]
+ CRUSH rule 1 x 1005 [369,223]
+ CRUSH rule 1 x 1006 [40,107]
+ CRUSH rule 1 x 1007 [978,111]
+ CRUSH rule 1 x 1008 [965,956]
+ CRUSH rule 1 x 1009 [598,476]
+ CRUSH rule 1 x 1010 [767,523]
+ CRUSH rule 1 x 1011 [289,871]
+ CRUSH rule 1 x 1012 [128,28]
+ CRUSH rule 1 x 1013 [979,765]
+ CRUSH rule 1 x 1014 [979,948]
+ CRUSH rule 1 x 1015 [277,790]
+ CRUSH rule 1 x 1016 [262,73]
+ CRUSH rule 1 x 1017 [150,269]
+ CRUSH rule 1 x 1018 [555,829]
+ CRUSH rule 1 x 1019 [513,356]
+ CRUSH rule 1 x 1020 [158,161]
+ CRUSH rule 1 x 1021 [915,998]
+ CRUSH rule 1 x 1022 [967,829]
+ CRUSH rule 1 x 1023 [488,257]
+ rule 1 (metadata) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536]
+ CRUSH rule 1 x 1 [876,250,334]
+ CRUSH rule 1 x 2 [292,832,53]
+ CRUSH rule 1 x 3 [623,387,124]
+ CRUSH rule 1 x 4 [61,334,710]
+ CRUSH rule 1 x 5 [946,557,713]
+ CRUSH rule 1 x 6 [576,668,212]
+ CRUSH rule 1 x 7 [645,753,906]
+ CRUSH rule 1 x 8 [243,6,863]
+ CRUSH rule 1 x 9 [22,578,251]
+ CRUSH rule 1 x 10 [758,828,360]
+ CRUSH rule 1 x 11 [769,120,124]
+ CRUSH rule 1 x 12 [780,364,689]
+ CRUSH rule 1 x 13 [557,18,351]
+ CRUSH rule 1 x 14 [59,561,249]
+ CRUSH rule 1 x 15 [718,928,993]
+ CRUSH rule 1 x 16 [673,632,841]
+ CRUSH rule 1 x 17 [648,43,560]
+ CRUSH rule 1 x 18 [654,219,181]
+ CRUSH rule 1 x 19 [850,545,377]
+ CRUSH rule 1 x 20 [717,785,974]
+ CRUSH rule 1 x 21 [420,57,519]
+ CRUSH rule 1 x 22 [503,998,193]
+ CRUSH rule 1 x 23 [411,663,168]
+ CRUSH rule 1 x 24 [266,861,353]
+ CRUSH rule 1 x 25 [760,483,818]
+ CRUSH rule 1 x 26 [903,24,573]
+ CRUSH rule 1 x 27 [946,188,289]
+ CRUSH rule 1 x 28 [69,312,73]
+ CRUSH rule 1 x 29 [844,883,337]
+ CRUSH rule 1 x 30 [621,18,613]
+ CRUSH rule 1 x 31 [784,943,814]
+ CRUSH rule 1 x 32 [173,374,369]
+ CRUSH rule 1 x 33 [698,336,357]
+ CRUSH rule 1 x 34 [168,836,210]
+ CRUSH rule 1 x 35 [274,509,534]
+ CRUSH rule 1 x 36 [318,215,153]
+ CRUSH rule 1 x 37 [173,604,109]
+ CRUSH rule 1 x 38 [708,444,683]
+ CRUSH rule 1 x 39 [662,198,417]
+ CRUSH rule 1 x 40 [620,801,414]
+ CRUSH rule 1 x 41 [811,264,177]
+ CRUSH rule 1 x 42 [863,179,527]
+ CRUSH rule 1 x 43 [686,822,988]
+ CRUSH rule 1 x 44 [396,222,46]
+ CRUSH rule 1 x 45 [991,694,253]
+ CRUSH rule 1 x 46 [420,909,184]
+ CRUSH rule 1 x 47 [467,211,605]
+ CRUSH rule 1 x 48 [955,329,368]
+ CRUSH rule 1 x 49 [974,891,931]
+ CRUSH rule 1 x 50 [870,441,691]
+ CRUSH rule 1 x 51 [182,930,25]
+ CRUSH rule 1 x 52 [704,812,894]
+ CRUSH rule 1 x 53 [185,713,631]
+ CRUSH rule 1 x 54 [270,441,100]
+ CRUSH rule 1 x 55 [895,734,958]
+ CRUSH rule 1 x 56 [564,963,683]
+ CRUSH rule 1 x 57 [738,130,208]
+ CRUSH rule 1 x 58 [524,113,806]
+ CRUSH rule 1 x 59 [408,337,668]
+ CRUSH rule 1 x 60 [228,790,857]
+ CRUSH rule 1 x 61 [154,843,717]
+ CRUSH rule 1 x 62 [594,811,549]
+ CRUSH rule 1 x 63 [646,67,884]
+ CRUSH rule 1 x 64 [175,542,155]
+ CRUSH rule 1 x 65 [745,619,131]
+ CRUSH rule 1 x 66 [275,468,23]
+ CRUSH rule 1 x 67 [246,958,524]
+ CRUSH rule 1 x 68 [711,473,403]
+ CRUSH rule 1 x 69 [493,924,850]
+ CRUSH rule 1 x 70 [30,499,644]
+ CRUSH rule 1 x 71 [984,883,574]
+ CRUSH rule 1 x 72 [71,286,942]
+ CRUSH rule 1 x 73 [922,618,3]
+ CRUSH rule 1 x 74 [629,414,185]
+ CRUSH rule 1 x 75 [222,20,174]
+ CRUSH rule 1 x 76 [262,366,339]
+ CRUSH rule 1 x 77 [638,469,992]
+ CRUSH rule 1 x 78 [324,511,788]
+ CRUSH rule 1 x 79 [577,990,64]
+ CRUSH rule 1 x 80 [501,95,278]
+ CRUSH rule 1 x 81 [506,812,9]
+ CRUSH rule 1 x 82 [222,145,80]
+ CRUSH rule 1 x 83 [71,634,61]
+ CRUSH rule 1 x 84 [49,761,773]
+ CRUSH rule 1 x 85 [985,896,708]
+ CRUSH rule 1 x 86 [537,745,93]
+ CRUSH rule 1 x 87 [997,317,463]
+ CRUSH rule 1 x 88 [957,350,890]
+ CRUSH rule 1 x 89 [399,730,148]
+ CRUSH rule 1 x 90 [943,706,683]
+ CRUSH rule 1 x 91 [22,368,149]
+ CRUSH rule 1 x 92 [532,424,426]
+ CRUSH rule 1 x 93 [218,489,405]
+ CRUSH rule 1 x 94 [181,96,102]
+ CRUSH rule 1 x 95 [343,957,820]
+ CRUSH rule 1 x 96 [861,270,87]
+ CRUSH rule 1 x 97 [459,706,45]
+ CRUSH rule 1 x 98 [327,867,353]
+ CRUSH rule 1 x 99 [974,133,468]
+ CRUSH rule 1 x 100 [32,445,547]
+ CRUSH rule 1 x 101 [142,90,337]
+ CRUSH rule 1 x 102 [172,129,139]
+ CRUSH rule 1 x 103 [630,47,161]
+ CRUSH rule 1 x 104 [758,133,278]
+ CRUSH rule 1 x 105 [843,604,47]
+ CRUSH rule 1 x 106 [28,681,193]
+ CRUSH rule 1 x 107 [74,320,85]
+ CRUSH rule 1 x 108 [875,593,575]
+ CRUSH rule 1 x 109 [411,985,811]
+ CRUSH rule 1 x 110 [440,774,799]
+ CRUSH rule 1 x 111 [405,742,276]
+ CRUSH rule 1 x 112 [143,181,922]
+ CRUSH rule 1 x 113 [153,846,160]
+ CRUSH rule 1 x 114 [804,892,939]
+ CRUSH rule 1 x 115 [588,508,958]
+ CRUSH rule 1 x 116 [327,148,637]
+ CRUSH rule 1 x 117 [95,594,989]
+ CRUSH rule 1 x 118 [80,957,897]
+ CRUSH rule 1 x 119 [386,932,951]
+ CRUSH rule 1 x 120 [366,312,653]
+ CRUSH rule 1 x 121 [129,154,847]
+ CRUSH rule 1 x 122 [873,1,110]
+ CRUSH rule 1 x 123 [533,415,789]
+ CRUSH rule 1 x 124 [461,691,898]
+ CRUSH rule 1 x 125 [342,599,830]
+ CRUSH rule 1 x 126 [819,781,822]
+ CRUSH rule 1 x 127 [437,893,585]
+ CRUSH rule 1 x 128 [679,994,982]
+ CRUSH rule 1 x 129 [380,685,947]
+ CRUSH rule 1 x 130 [992,52,466]
+ CRUSH rule 1 x 131 [469,90,208]
+ CRUSH rule 1 x 132 [571,250,316]
+ CRUSH rule 1 x 133 [964,728,329]
+ CRUSH rule 1 x 134 [999,19,716]
+ CRUSH rule 1 x 135 [634,101,52]
+ CRUSH rule 1 x 136 [114,889,692]
+ CRUSH rule 1 x 137 [839,8,959]
+ CRUSH rule 1 x 138 [967,949,138]
+ CRUSH rule 1 x 139 [308,711,736]
+ CRUSH rule 1 x 140 [764,936,926]
+ CRUSH rule 1 x 141 [423,302,112]
+ CRUSH rule 1 x 142 [252,821,715]
+ CRUSH rule 1 x 143 [33,808,518]
+ CRUSH rule 1 x 144 [472,88,969]
+ CRUSH rule 1 x 145 [242,208,252]
+ CRUSH rule 1 x 146 [290,70,570]
+ CRUSH rule 1 x 147 [447,352,657]
+ CRUSH rule 1 x 148 [212,644,432]
+ CRUSH rule 1 x 149 [9,775,87]
+ CRUSH rule 1 x 150 [166,456,582]
+ CRUSH rule 1 x 151 [811,875,307]
+ CRUSH rule 1 x 152 [449,617,223]
+ CRUSH rule 1 x 153 [523,537,695]
+ CRUSH rule 1 x 154 [208,559,874]
+ CRUSH rule 1 x 155 [569,325,192]
+ CRUSH rule 1 x 156 [488,121,521]
+ CRUSH rule 1 x 157 [140,723,633]
+ CRUSH rule 1 x 158 [786,451,320]
+ CRUSH rule 1 x 159 [134,664,517]
+ CRUSH rule 1 x 160 [690,112,414]
+ CRUSH rule 1 x 161 [324,912,397]
+ CRUSH rule 1 x 162 [748,567,284]
+ CRUSH rule 1 x 163 [575,499,31]
+ CRUSH rule 1 x 164 [314,489,308]
+ CRUSH rule 1 x 165 [116,209,750]
+ CRUSH rule 1 x 166 [352,706,701]
+ CRUSH rule 1 x 167 [27,743,174]
+ CRUSH rule 1 x 168 [953,898,880]
+ CRUSH rule 1 x 169 [912,147,266]
+ CRUSH rule 1 x 170 [421,515,828]
+ CRUSH rule 1 x 171 [488,584,880]
+ CRUSH rule 1 x 172 [366,443,957]
+ CRUSH rule 1 x 173 [863,291,625]
+ CRUSH rule 1 x 174 [263,555,650]
+ CRUSH rule 1 x 175 [875,961,361]
+ CRUSH rule 1 x 176 [745,83,701]
+ CRUSH rule 1 x 177 [128,244,41]
+ CRUSH rule 1 x 178 [155,41,264]
+ CRUSH rule 1 x 179 [593,833,202]
+ CRUSH rule 1 x 180 [154,734,17]
+ CRUSH rule 1 x 181 [289,675,723]
+ CRUSH rule 1 x 182 [730,931,560]
+ CRUSH rule 1 x 183 [639,237,794]
+ CRUSH rule 1 x 184 [704,312,685]
+ CRUSH rule 1 x 185 [97,100,762]
+ CRUSH rule 1 x 186 [26,665,554]
+ CRUSH rule 1 x 187 [649,14,740]
+ CRUSH rule 1 x 188 [682,695,590]
+ CRUSH rule 1 x 189 [325,693,726]
+ CRUSH rule 1 x 190 [399,933,136]
+ CRUSH rule 1 x 191 [629,533,17]
+ CRUSH rule 1 x 192 [503,578,38]
+ CRUSH rule 1 x 193 [546,333,651]
+ CRUSH rule 1 x 194 [242,473,58]
+ CRUSH rule 1 x 195 [625,719,135]
+ CRUSH rule 1 x 196 [357,114,125]
+ CRUSH rule 1 x 197 [306,954,453]
+ CRUSH rule 1 x 198 [863,791,311]
+ CRUSH rule 1 x 199 [935,906,929]
+ CRUSH rule 1 x 200 [373,774,229]
+ CRUSH rule 1 x 201 [659,320,477]
+ CRUSH rule 1 x 202 [260,433,524]
+ CRUSH rule 1 x 203 [36,239,675]
+ CRUSH rule 1 x 204 [92,516,993]
+ CRUSH rule 1 x 205 [68,395,473]
+ CRUSH rule 1 x 206 [570,530,642]
+ CRUSH rule 1 x 207 [834,457,850]
+ CRUSH rule 1 x 208 [927,484,640]
+ CRUSH rule 1 x 209 [878,66,58]
+ CRUSH rule 1 x 210 [572,981,484]
+ CRUSH rule 1 x 211 [107,597,780]
+ CRUSH rule 1 x 212 [389,107,838]
+ CRUSH rule 1 x 213 [497,717,567]
+ CRUSH rule 1 x 214 [798,65,254]
+ CRUSH rule 1 x 215 [233,419,283]
+ CRUSH rule 1 x 216 [494,464,742]
+ CRUSH rule 1 x 217 [352,396,309]
+ CRUSH rule 1 x 218 [895,864,988]
+ CRUSH rule 1 x 219 [222,534,277]
+ CRUSH rule 1 x 220 [281,19,584]
+ CRUSH rule 1 x 221 [64,928,963]
+ CRUSH rule 1 x 222 [40,544,161]
+ CRUSH rule 1 x 223 [645,556,159]
+ CRUSH rule 1 x 224 [647,165,957]
+ CRUSH rule 1 x 225 [219,714,858]
+ CRUSH rule 1 x 226 [372,511,181]
+ CRUSH rule 1 x 227 [925,156,714]
+ CRUSH rule 1 x 228 [682,404,839]
+ CRUSH rule 1 x 229 [880,838,770]
+ CRUSH rule 1 x 230 [328,659,916]
+ CRUSH rule 1 x 231 [320,383,669]
+ CRUSH rule 1 x 232 [924,846,394]
+ CRUSH rule 1 x 233 [948,652,575]
+ CRUSH rule 1 x 234 [484,943,42]
+ CRUSH rule 1 x 235 [750,65,590]
+ CRUSH rule 1 x 236 [551,787,490]
+ CRUSH rule 1 x 237 [390,157,166]
+ CRUSH rule 1 x 238 [570,6,989]
+ CRUSH rule 1 x 239 [729,959,376]
+ CRUSH rule 1 x 240 [981,241,156]
+ CRUSH rule 1 x 241 [310,816,641]
+ CRUSH rule 1 x 242 [161,63,642]
+ CRUSH rule 1 x 243 [180,394,33]
+ CRUSH rule 1 x 244 [52,174,685]
+ CRUSH rule 1 x 245 [523,121,915]
+ CRUSH rule 1 x 246 [362,893,390]
+ CRUSH rule 1 x 247 [382,184,116]
+ CRUSH rule 1 x 248 [129,114,852]
+ CRUSH rule 1 x 249 [159,683,91]
+ CRUSH rule 1 x 250 [404,945,569]
+ CRUSH rule 1 x 251 [661,225,738]
+ CRUSH rule 1 x 252 [961,226,542]
+ CRUSH rule 1 x 253 [651,97,225]
+ CRUSH rule 1 x 254 [123,33,741]
+ CRUSH rule 1 x 255 [314,649,891]
+ CRUSH rule 1 x 256 [315,215,651]
+ CRUSH rule 1 x 257 [825,264,867]
+ CRUSH rule 1 x 258 [624,789,370]
+ CRUSH rule 1 x 259 [602,542,70]
+ CRUSH rule 1 x 260 [717,878,43]
+ CRUSH rule 1 x 261 [145,517,20]
+ CRUSH rule 1 x 262 [223,1,561]
+ CRUSH rule 1 x 263 [462,211,405]
+ CRUSH rule 1 x 264 [654,471,266]
+ CRUSH rule 1 x 265 [302,794,704]
+ CRUSH rule 1 x 266 [202,132,884]
+ CRUSH rule 1 x 267 [282,938,657]
+ CRUSH rule 1 x 268 [338,309,356]
+ CRUSH rule 1 x 269 [738,122,266]
+ CRUSH rule 1 x 270 [707,982,946]
+ CRUSH rule 1 x 271 [705,432,364]
+ CRUSH rule 1 x 272 [756,545,942]
+ CRUSH rule 1 x 273 [197,502,527]
+ CRUSH rule 1 x 274 [992,44,653]
+ CRUSH rule 1 x 275 [544,789,170]
+ CRUSH rule 1 x 276 [658,467,577]
+ CRUSH rule 1 x 277 [143,490,880]
+ CRUSH rule 1 x 278 [492,647,355]
+ CRUSH rule 1 x 279 [517,792,604]
+ CRUSH rule 1 x 280 [825,740,27]
+ CRUSH rule 1 x 281 [224,629,120]
+ CRUSH rule 1 x 282 [298,661,380]
+ CRUSH rule 1 x 283 [311,606,208]
+ CRUSH rule 1 x 284 [771,466,371]
+ CRUSH rule 1 x 285 [693,362,404]
+ CRUSH rule 1 x 286 [364,477,285]
+ CRUSH rule 1 x 287 [591,611,828]
+ CRUSH rule 1 x 288 [965,541,848]
+ CRUSH rule 1 x 289 [225,551,948]
+ CRUSH rule 1 x 290 [577,762,777]
+ CRUSH rule 1 x 291 [160,903,477]
+ CRUSH rule 1 x 292 [873,598,216]
+ CRUSH rule 1 x 293 [100,234,874]
+ CRUSH rule 1 x 294 [285,943,379]
+ CRUSH rule 1 x 295 [938,262,880]
+ CRUSH rule 1 x 296 [850,327,86]
+ CRUSH rule 1 x 297 [951,53,99]
+ CRUSH rule 1 x 298 [173,336,85]
+ CRUSH rule 1 x 299 [598,591,315]
+ CRUSH rule 1 x 300 [531,957,62]
+ CRUSH rule 1 x 301 [823,628,23]
+ CRUSH rule 1 x 302 [184,80,780]
+ CRUSH rule 1 x 303 [521,766,222]
+ CRUSH rule 1 x 304 [980,127,807]
+ CRUSH rule 1 x 305 [153,816,22]
+ CRUSH rule 1 x 306 [423,739,664]
+ CRUSH rule 1 x 307 [997,557,682]
+ CRUSH rule 1 x 308 [991,874,534]
+ CRUSH rule 1 x 309 [860,394,724]
+ CRUSH rule 1 x 310 [589,818,546]
+ CRUSH rule 1 x 311 [477,774,225]
+ CRUSH rule 1 x 312 [887,853,950]
+ CRUSH rule 1 x 313 [802,646,447]
+ CRUSH rule 1 x 314 [654,974,229]
+ CRUSH rule 1 x 315 [767,227,28]
+ CRUSH rule 1 x 316 [778,83,733]
+ CRUSH rule 1 x 317 [184,418,642]
+ CRUSH rule 1 x 318 [525,410,500]
+ CRUSH rule 1 x 319 [476,724,569]
+ CRUSH rule 1 x 320 [149,610,697]
+ CRUSH rule 1 x 321 [710,79,667]
+ CRUSH rule 1 x 322 [175,275,323]
+ CRUSH rule 1 x 323 [819,604,638]
+ CRUSH rule 1 x 324 [16,745,511]
+ CRUSH rule 1 x 325 [486,400,872]
+ CRUSH rule 1 x 326 [613,765,207]
+ CRUSH rule 1 x 327 [125,289,738]
+ CRUSH rule 1 x 328 [807,383,476]
+ CRUSH rule 1 x 329 [588,938,599]
+ CRUSH rule 1 x 330 [932,644,41]
+ CRUSH rule 1 x 331 [341,953,950]
+ CRUSH rule 1 x 332 [153,726,459]
+ CRUSH rule 1 x 333 [745,845,853]
+ CRUSH rule 1 x 334 [614,751,807]
+ CRUSH rule 1 x 335 [518,721,221]
+ CRUSH rule 1 x 336 [389,424,77]
+ CRUSH rule 1 x 337 [753,508,765]
+ CRUSH rule 1 x 338 [128,810,490]
+ CRUSH rule 1 x 339 [430,308,58]
+ CRUSH rule 1 x 340 [541,44,630]
+ CRUSH rule 1 x 341 [402,26,631]
+ CRUSH rule 1 x 342 [982,57,992]
+ CRUSH rule 1 x 343 [833,412,572]
+ CRUSH rule 1 x 344 [784,533,792]
+ CRUSH rule 1 x 345 [546,300,304]
+ CRUSH rule 1 x 346 [302,420,428]
+ CRUSH rule 1 x 347 [488,778,101]
+ CRUSH rule 1 x 348 [903,744,937]
+ CRUSH rule 1 x 349 [471,547,582]
+ CRUSH rule 1 x 350 [348,221,823]
+ CRUSH rule 1 x 351 [961,582,705]
+ CRUSH rule 1 x 352 [728,137,461]
+ CRUSH rule 1 x 353 [904,202,184]
+ CRUSH rule 1 x 354 [345,226,319]
+ CRUSH rule 1 x 355 [50,430,175]
+ CRUSH rule 1 x 356 [87,185,55]
+ CRUSH rule 1 x 357 [762,459,921]
+ CRUSH rule 1 x 358 [908,25,280]
+ CRUSH rule 1 x 359 [484,15,132]
+ CRUSH rule 1 x 360 [173,378,337]
+ CRUSH rule 1 x 361 [404,577,115]
+ CRUSH rule 1 x 362 [403,1,422]
+ CRUSH rule 1 x 363 [639,911,510]
+ CRUSH rule 1 x 364 [752,689,610]
+ CRUSH rule 1 x 365 [956,999,212]
+ CRUSH rule 1 x 366 [860,925,924]
+ CRUSH rule 1 x 367 [205,609,647]
+ CRUSH rule 1 x 368 [301,284,810]
+ CRUSH rule 1 x 369 [452,658,339]
+ CRUSH rule 1 x 370 [11,467,695]
+ CRUSH rule 1 x 371 [124,487,55]
+ CRUSH rule 1 x 372 [253,48,979]
+ CRUSH rule 1 x 373 [715,605,775]
+ CRUSH rule 1 x 374 [191,887,920]
+ CRUSH rule 1 x 375 [711,385,651]
+ CRUSH rule 1 x 376 [597,818,49]
+ CRUSH rule 1 x 377 [294,256,933]
+ CRUSH rule 1 x 378 [34,151,681]
+ CRUSH rule 1 x 379 [869,136,315]
+ CRUSH rule 1 x 380 [294,97,575]
+ CRUSH rule 1 x 381 [119,710,219]
+ CRUSH rule 1 x 382 [69,631,508]
+ CRUSH rule 1 x 383 [922,588,589]
+ CRUSH rule 1 x 384 [221,945,671]
+ CRUSH rule 1 x 385 [561,737,953]
+ CRUSH rule 1 x 386 [335,442,788]
+ CRUSH rule 1 x 387 [514,43,353]
+ CRUSH rule 1 x 388 [587,89,157]
+ CRUSH rule 1 x 389 [109,641,255]
+ CRUSH rule 1 x 390 [925,149,421]
+ CRUSH rule 1 x 391 [267,87,387]
+ CRUSH rule 1 x 392 [382,485,370]
+ CRUSH rule 1 x 393 [425,721,221]
+ CRUSH rule 1 x 394 [898,18,38]
+ CRUSH rule 1 x 395 [806,876,269]
+ CRUSH rule 1 x 396 [790,970,437]
+ CRUSH rule 1 x 397 [136,363,507]
+ CRUSH rule 1 x 398 [914,116,558]
+ CRUSH rule 1 x 399 [261,94,299]
+ CRUSH rule 1 x 400 [661,197,338]
+ CRUSH rule 1 x 401 [953,979,287]
+ CRUSH rule 1 x 402 [738,819,618]
+ CRUSH rule 1 x 403 [573,238,425]
+ CRUSH rule 1 x 404 [526,848,790]
+ CRUSH rule 1 x 405 [582,505,330]
+ CRUSH rule 1 x 406 [768,324,493]
+ CRUSH rule 1 x 407 [260,951,437]
+ CRUSH rule 1 x 408 [657,81,770]
+ CRUSH rule 1 x 409 [498,89,182]
+ CRUSH rule 1 x 410 [28,793,737]
+ CRUSH rule 1 x 411 [684,992,60]
+ CRUSH rule 1 x 412 [261,958,699]
+ CRUSH rule 1 x 413 [891,835,297]
+ CRUSH rule 1 x 414 [127,459,119]
+ CRUSH rule 1 x 415 [272,540,631]
+ CRUSH rule 1 x 416 [739,617,115]
+ CRUSH rule 1 x 417 [106,209,157]
+ CRUSH rule 1 x 418 [525,441,147]
+ CRUSH rule 1 x 419 [603,673,615]
+ CRUSH rule 1 x 420 [988,213,251]
+ CRUSH rule 1 x 421 [761,521,748]
+ CRUSH rule 1 x 422 [317,160,924]
+ CRUSH rule 1 x 423 [137,807,168]
+ CRUSH rule 1 x 424 [920,37,146]
+ CRUSH rule 1 x 425 [277,693,285]
+ CRUSH rule 1 x 426 [485,936,407]
+ CRUSH rule 1 x 427 [242,515,9]
+ CRUSH rule 1 x 428 [632,635,26]
+ CRUSH rule 1 x 429 [641,73,465]
+ CRUSH rule 1 x 430 [626,585,6]
+ CRUSH rule 1 x 431 [697,76,753]
+ CRUSH rule 1 x 432 [590,526,306]
+ CRUSH rule 1 x 433 [284,387,149]
+ CRUSH rule 1 x 434 [538,985,79]
+ CRUSH rule 1 x 435 [30,318,593]
+ CRUSH rule 1 x 436 [164,919,851]
+ CRUSH rule 1 x 437 [322,212,163]
+ CRUSH rule 1 x 438 [142,392,85]
+ CRUSH rule 1 x 439 [119,370,68]
+ CRUSH rule 1 x 440 [333,403,187]
+ CRUSH rule 1 x 441 [477,727,906]
+ CRUSH rule 1 x 442 [274,590,933]
+ CRUSH rule 1 x 443 [983,748,574]
+ CRUSH rule 1 x 444 [536,509,431]
+ CRUSH rule 1 x 445 [485,964,528]
+ CRUSH rule 1 x 446 [345,634,42]
+ CRUSH rule 1 x 447 [61,845,767]
+ CRUSH rule 1 x 448 [333,232,292]
+ CRUSH rule 1 x 449 [680,16,484]
+ CRUSH rule 1 x 450 [235,214,79]
+ CRUSH rule 1 x 451 [961,468,333]
+ CRUSH rule 1 x 452 [525,479,153]
+ CRUSH rule 1 x 453 [138,466,302]
+ CRUSH rule 1 x 454 [137,625,215]
+ CRUSH rule 1 x 455 [173,150,997]
+ CRUSH rule 1 x 456 [235,226,238]
+ CRUSH rule 1 x 457 [450,577,253]
+ CRUSH rule 1 x 458 [195,537,91]
+ CRUSH rule 1 x 459 [381,555,312]
+ CRUSH rule 1 x 460 [972,730,534]
+ CRUSH rule 1 x 461 [506,279,142]
+ CRUSH rule 1 x 462 [692,959,578]
+ CRUSH rule 1 x 463 [788,667,949]
+ CRUSH rule 1 x 464 [133,122,588]
+ CRUSH rule 1 x 465 [971,190,230]
+ CRUSH rule 1 x 466 [394,576,148]
+ CRUSH rule 1 x 467 [517,28,366]
+ CRUSH rule 1 x 468 [829,143,874]
+ CRUSH rule 1 x 469 [987,936,106]
+ CRUSH rule 1 x 470 [107,982,56]
+ CRUSH rule 1 x 471 [181,897,629]
+ CRUSH rule 1 x 472 [547,512,172]
+ CRUSH rule 1 x 473 [760,997,824]
+ CRUSH rule 1 x 474 [787,418,743]
+ CRUSH rule 1 x 475 [662,312,253]
+ CRUSH rule 1 x 476 [110,495,185]
+ CRUSH rule 1 x 477 [393,954,834]
+ CRUSH rule 1 x 478 [246,483,480]
+ CRUSH rule 1 x 479 [70,929,697]
+ CRUSH rule 1 x 480 [753,119,961]
+ CRUSH rule 1 x 481 [470,429,677]
+ CRUSH rule 1 x 482 [451,566,961]
+ CRUSH rule 1 x 483 [816,72,371]
+ CRUSH rule 1 x 484 [540,454,389]
+ CRUSH rule 1 x 485 [74,582,624]
+ CRUSH rule 1 x 486 [958,595,199]
+ CRUSH rule 1 x 487 [228,302,804]
+ CRUSH rule 1 x 488 [180,529,722]
+ CRUSH rule 1 x 489 [47,617,812]
+ CRUSH rule 1 x 490 [905,822,479]
+ CRUSH rule 1 x 491 [892,370,609]
+ CRUSH rule 1 x 492 [588,959,127]
+ CRUSH rule 1 x 493 [353,461,593]
+ CRUSH rule 1 x 494 [378,848,443]
+ CRUSH rule 1 x 495 [845,653,768]
+ CRUSH rule 1 x 496 [13,988,0]
+ CRUSH rule 1 x 497 [796,877,788]
+ CRUSH rule 1 x 498 [412,337,270]
+ CRUSH rule 1 x 499 [330,695,8]
+ CRUSH rule 1 x 500 [820,272,547]
+ CRUSH rule 1 x 501 [110,44,132]
+ CRUSH rule 1 x 502 [336,595,650]
+ CRUSH rule 1 x 503 [922,211,157]
+ CRUSH rule 1 x 504 [483,52,122]
+ CRUSH rule 1 x 505 [482,598,224]
+ CRUSH rule 1 x 506 [493,123,43]
+ CRUSH rule 1 x 507 [12,598,264]
+ CRUSH rule 1 x 508 [227,157,611]
+ CRUSH rule 1 x 509 [807,242,363]
+ CRUSH rule 1 x 510 [134,437,227]
+ CRUSH rule 1 x 511 [212,54,83]
+ CRUSH rule 1 x 512 [236,630,758]
+ CRUSH rule 1 x 513 [994,693,644]
+ CRUSH rule 1 x 514 [45,508,831]
+ CRUSH rule 1 x 515 [504,138,480]
+ CRUSH rule 1 x 516 [285,409,136]
+ CRUSH rule 1 x 517 [300,232,23]
+ CRUSH rule 1 x 518 [397,674,98]
+ CRUSH rule 1 x 519 [86,750,772]
+ CRUSH rule 1 x 520 [900,833,614]
+ CRUSH rule 1 x 521 [31,47,236]
+ CRUSH rule 1 x 522 [390,16,280]
+ CRUSH rule 1 x 523 [618,308,424]
+ CRUSH rule 1 x 524 [635,189,687]
+ CRUSH rule 1 x 525 [311,916,699]
+ CRUSH rule 1 x 526 [48,738,227]
+ CRUSH rule 1 x 527 [202,851,889]
+ CRUSH rule 1 x 528 [565,827,590]
+ CRUSH rule 1 x 529 [934,864,241]
+ CRUSH rule 1 x 530 [502,934,298]
+ CRUSH rule 1 x 531 [681,627,942]
+ CRUSH rule 1 x 532 [422,6,147]
+ CRUSH rule 1 x 533 [863,68,364]
+ CRUSH rule 1 x 534 [962,931,775]
+ CRUSH rule 1 x 535 [89,565,397]
+ CRUSH rule 1 x 536 [499,351,760]
+ CRUSH rule 1 x 537 [676,547,787]
+ CRUSH rule 1 x 538 [58,644,571]
+ CRUSH rule 1 x 539 [837,953,457]
+ CRUSH rule 1 x 540 [831,50,132]
+ CRUSH rule 1 x 541 [582,757,121]
+ CRUSH rule 1 x 542 [472,132,790]
+ CRUSH rule 1 x 543 [382,272,797]
+ CRUSH rule 1 x 544 [947,930,496]
+ CRUSH rule 1 x 545 [425,570,305]
+ CRUSH rule 1 x 546 [18,65,529]
+ CRUSH rule 1 x 547 [445,715,600]
+ CRUSH rule 1 x 548 [367,569,980]
+ CRUSH rule 1 x 549 [125,715,671]
+ CRUSH rule 1 x 550 [425,599,744]
+ CRUSH rule 1 x 551 [44,1,528]
+ CRUSH rule 1 x 552 [246,104,68]
+ CRUSH rule 1 x 553 [71,703,615]
+ CRUSH rule 1 x 554 [207,124,217]
+ CRUSH rule 1 x 555 [570,28,317]
+ CRUSH rule 1 x 556 [674,152,421]
+ CRUSH rule 1 x 557 [347,817,191]
+ CRUSH rule 1 x 558 [627,426,369]
+ CRUSH rule 1 x 559 [940,630,924]
+ CRUSH rule 1 x 560 [295,903,541]
+ CRUSH rule 1 x 561 [506,682,384]
+ CRUSH rule 1 x 562 [718,529,87]
+ CRUSH rule 1 x 563 [552,332,747]
+ CRUSH rule 1 x 564 [835,769,736]
+ CRUSH rule 1 x 565 [8,167,539]
+ CRUSH rule 1 x 566 [600,481,301]
+ CRUSH rule 1 x 567 [999,994,509]
+ CRUSH rule 1 x 568 [252,431,157]
+ CRUSH rule 1 x 569 [643,218,943]
+ CRUSH rule 1 x 570 [617,635,765]
+ CRUSH rule 1 x 571 [757,80,59]
+ CRUSH rule 1 x 572 [299,348,575]
+ CRUSH rule 1 x 573 [25,505,270]
+ CRUSH rule 1 x 574 [215,431,624]
+ CRUSH rule 1 x 575 [225,252,611]
+ CRUSH rule 1 x 576 [627,94,159]
+ CRUSH rule 1 x 577 [237,809,778]
+ CRUSH rule 1 x 578 [885,313,120]
+ CRUSH rule 1 x 579 [924,575,787]
+ CRUSH rule 1 x 580 [718,51,766]
+ CRUSH rule 1 x 581 [219,807,129]
+ CRUSH rule 1 x 582 [893,701,598]
+ CRUSH rule 1 x 583 [246,930,964]
+ CRUSH rule 1 x 584 [336,432,680]
+ CRUSH rule 1 x 585 [324,999,397]
+ CRUSH rule 1 x 586 [558,230,976]
+ CRUSH rule 1 x 587 [985,830,597]
+ CRUSH rule 1 x 588 [211,544,57]
+ CRUSH rule 1 x 589 [129,21,112]
+ CRUSH rule 1 x 590 [467,969,652]
+ CRUSH rule 1 x 591 [758,514,316]
+ CRUSH rule 1 x 592 [525,253,190]
+ CRUSH rule 1 x 593 [601,885,339]
+ CRUSH rule 1 x 594 [227,60,450]
+ CRUSH rule 1 x 595 [720,854,496]
+ CRUSH rule 1 x 596 [751,195,997]
+ CRUSH rule 1 x 597 [129,574,714]
+ CRUSH rule 1 x 598 [679,207,604]
+ CRUSH rule 1 x 599 [668,315,683]
+ CRUSH rule 1 x 600 [143,396,464]
+ CRUSH rule 1 x 601 [326,573,873]
+ CRUSH rule 1 x 602 [860,281,875]
+ CRUSH rule 1 x 603 [709,328,445]
+ CRUSH rule 1 x 604 [571,62,814]
+ CRUSH rule 1 x 605 [252,739,860]
+ CRUSH rule 1 x 606 [339,236,759]
+ CRUSH rule 1 x 607 [590,248,759]
+ CRUSH rule 1 x 608 [145,635,309]
+ CRUSH rule 1 x 609 [973,547,223]
+ CRUSH rule 1 x 610 [435,816,961]
+ CRUSH rule 1 x 611 [559,283,422]
+ CRUSH rule 1 x 612 [273,149,123]
+ CRUSH rule 1 x 613 [828,614,642]
+ CRUSH rule 1 x 614 [478,748,393]
+ CRUSH rule 1 x 615 [392,155,144]
+ CRUSH rule 1 x 616 [778,637,452]
+ CRUSH rule 1 x 617 [622,713,996]
+ CRUSH rule 1 x 618 [149,877,270]
+ CRUSH rule 1 x 619 [604,163,656]
+ CRUSH rule 1 x 620 [181,23,409]
+ CRUSH rule 1 x 621 [735,902,386]
+ CRUSH rule 1 x 622 [661,824,717]
+ CRUSH rule 1 x 623 [142,121,643]
+ CRUSH rule 1 x 624 [360,716,420]
+ CRUSH rule 1 x 625 [541,167,385]
+ CRUSH rule 1 x 626 [364,431,610]
+ CRUSH rule 1 x 627 [458,137,557]
+ CRUSH rule 1 x 628 [250,350,556]
+ CRUSH rule 1 x 629 [928,160,710]
+ CRUSH rule 1 x 630 [243,19,918]
+ CRUSH rule 1 x 631 [438,221,574]
+ CRUSH rule 1 x 632 [797,368,247]
+ CRUSH rule 1 x 633 [993,749,525]
+ CRUSH rule 1 x 634 [239,351,633]
+ CRUSH rule 1 x 635 [640,965,25]
+ CRUSH rule 1 x 636 [173,290,297]
+ CRUSH rule 1 x 637 [0,918,98]
+ CRUSH rule 1 x 638 [702,235,424]
+ CRUSH rule 1 x 639 [475,687,31]
+ CRUSH rule 1 x 640 [31,664,399]
+ CRUSH rule 1 x 641 [296,473,108]
+ CRUSH rule 1 x 642 [894,273,427]
+ CRUSH rule 1 x 643 [117,111,732]
+ CRUSH rule 1 x 644 [438,336,327]
+ CRUSH rule 1 x 645 [982,702,351]
+ CRUSH rule 1 x 646 [334,804,146]
+ CRUSH rule 1 x 647 [933,787,185]
+ CRUSH rule 1 x 648 [22,444,400]
+ CRUSH rule 1 x 649 [503,229,213]
+ CRUSH rule 1 x 650 [328,659,420]
+ CRUSH rule 1 x 651 [3,880,823]
+ CRUSH rule 1 x 652 [495,977,563]
+ CRUSH rule 1 x 653 [185,718,804]
+ CRUSH rule 1 x 654 [130,528,380]
+ CRUSH rule 1 x 655 [560,872,454]
+ CRUSH rule 1 x 656 [219,885,178]
+ CRUSH rule 1 x 657 [233,684,813]
+ CRUSH rule 1 x 658 [778,6,756]
+ CRUSH rule 1 x 659 [240,663,306]
+ CRUSH rule 1 x 660 [244,855,196]
+ CRUSH rule 1 x 661 [184,270,128]
+ CRUSH rule 1 x 662 [65,883,921]
+ CRUSH rule 1 x 663 [323,721,594]
+ CRUSH rule 1 x 664 [865,113,512]
+ CRUSH rule 1 x 665 [420,850,591]
+ CRUSH rule 1 x 666 [319,767,246]
+ CRUSH rule 1 x 667 [875,39,343]
+ CRUSH rule 1 x 668 [331,122,263]
+ CRUSH rule 1 x 669 [915,521,402]
+ CRUSH rule 1 x 670 [845,659,943]
+ CRUSH rule 1 x 671 [108,634,527]
+ CRUSH rule 1 x 672 [578,216,110]
+ CRUSH rule 1 x 673 [442,74,579]
+ CRUSH rule 1 x 674 [588,364,281]
+ CRUSH rule 1 x 675 [489,698,744]
+ CRUSH rule 1 x 676 [928,911,40]
+ CRUSH rule 1 x 677 [399,269,692]
+ CRUSH rule 1 x 678 [546,752,544]
+ CRUSH rule 1 x 679 [988,25,275]
+ CRUSH rule 1 x 680 [335,963,382]
+ CRUSH rule 1 x 681 [690,462,623]
+ CRUSH rule 1 x 682 [196,588,154]
+ CRUSH rule 1 x 683 [627,25,421]
+ CRUSH rule 1 x 684 [38,804,592]
+ CRUSH rule 1 x 685 [841,368,548]
+ CRUSH rule 1 x 686 [336,287,525]
+ CRUSH rule 1 x 687 [20,682,924]
+ CRUSH rule 1 x 688 [463,371,780]
+ CRUSH rule 1 x 689 [569,250,78]
+ CRUSH rule 1 x 690 [551,144,587]
+ CRUSH rule 1 x 691 [766,464,446]
+ CRUSH rule 1 x 692 [739,634,18]
+ CRUSH rule 1 x 693 [339,297,118]
+ CRUSH rule 1 x 694 [405,26,830]
+ CRUSH rule 1 x 695 [622,576,597]
+ CRUSH rule 1 x 696 [558,902,689]
+ CRUSH rule 1 x 697 [818,222,406]
+ CRUSH rule 1 x 698 [178,48,402]
+ CRUSH rule 1 x 699 [450,244,180]
+ CRUSH rule 1 x 700 [502,771,987]
+ CRUSH rule 1 x 701 [4,612,782]
+ CRUSH rule 1 x 702 [177,630,232]
+ CRUSH rule 1 x 703 [354,178,389]
+ CRUSH rule 1 x 704 [646,601,156]
+ CRUSH rule 1 x 705 [921,401,890]
+ CRUSH rule 1 x 706 [652,877,562]
+ CRUSH rule 1 x 707 [345,745,67]
+ CRUSH rule 1 x 708 [333,607,180]
+ CRUSH rule 1 x 709 [45,187,302]
+ CRUSH rule 1 x 710 [94,855,43]
+ CRUSH rule 1 x 711 [227,653,731]
+ CRUSH rule 1 x 712 [398,953,136]
+ CRUSH rule 1 x 713 [116,800,503]
+ CRUSH rule 1 x 714 [111,629,866]
+ CRUSH rule 1 x 715 [531,291,486]
+ CRUSH rule 1 x 716 [169,541,291]
+ CRUSH rule 1 x 717 [417,446,994]
+ CRUSH rule 1 x 718 [992,383,298]
+ CRUSH rule 1 x 719 [936,674,324]
+ CRUSH rule 1 x 720 [370,188,174]
+ CRUSH rule 1 x 721 [320,859,278]
+ CRUSH rule 1 x 722 [7,2,673]
+ CRUSH rule 1 x 723 [270,553,831]
+ CRUSH rule 1 x 724 [666,822,708]
+ CRUSH rule 1 x 725 [794,406,875]
+ CRUSH rule 1 x 726 [420,556,341]
+ CRUSH rule 1 x 727 [561,461,129]
+ CRUSH rule 1 x 728 [951,330,196]
+ CRUSH rule 1 x 729 [656,644,436]
+ CRUSH rule 1 x 730 [3,558,629]
+ CRUSH rule 1 x 731 [852,89,75]
+ CRUSH rule 1 x 732 [983,840,869]
+ CRUSH rule 1 x 733 [285,396,388]
+ CRUSH rule 1 x 734 [125,510,402]
+ CRUSH rule 1 x 735 [417,773,686]
+ CRUSH rule 1 x 736 [749,396,632]
+ CRUSH rule 1 x 737 [644,991,946]
+ CRUSH rule 1 x 738 [449,683,290]
+ CRUSH rule 1 x 739 [341,220,641]
+ CRUSH rule 1 x 740 [874,524,674]
+ CRUSH rule 1 x 741 [189,472,712]
+ CRUSH rule 1 x 742 [912,581,114]
+ CRUSH rule 1 x 743 [654,914,425]
+ CRUSH rule 1 x 744 [725,295,579]
+ CRUSH rule 1 x 745 [787,858,850]
+ CRUSH rule 1 x 746 [757,848,704]
+ CRUSH rule 1 x 747 [700,81,867]
+ CRUSH rule 1 x 748 [557,436,238]
+ CRUSH rule 1 x 749 [772,622,337]
+ CRUSH rule 1 x 750 [946,97,376]
+ CRUSH rule 1 x 751 [996,618,343]
+ CRUSH rule 1 x 752 [746,887,695]
+ CRUSH rule 1 x 753 [741,14,463]
+ CRUSH rule 1 x 754 [648,349,333]
+ CRUSH rule 1 x 755 [157,460,466]
+ CRUSH rule 1 x 756 [416,97,197]
+ CRUSH rule 1 x 757 [599,839,776]
+ CRUSH rule 1 x 758 [994,218,620]
+ CRUSH rule 1 x 759 [959,682,514]
+ CRUSH rule 1 x 760 [518,943,215]
+ CRUSH rule 1 x 761 [285,849,420]
+ CRUSH rule 1 x 762 [591,313,41]
+ CRUSH rule 1 x 763 [908,411,200]
+ CRUSH rule 1 x 764 [787,234,894]
+ CRUSH rule 1 x 765 [327,921,882]
+ CRUSH rule 1 x 766 [84,161,878]
+ CRUSH rule 1 x 767 [370,895,702]
+ CRUSH rule 1 x 768 [826,760,879]
+ CRUSH rule 1 x 769 [67,768,663]
+ CRUSH rule 1 x 770 [593,909,482]
+ CRUSH rule 1 x 771 [309,935,121]
+ CRUSH rule 1 x 772 [12,125,797]
+ CRUSH rule 1 x 773 [253,466,820]
+ CRUSH rule 1 x 774 [164,390,705]
+ CRUSH rule 1 x 775 [703,47,43]
+ CRUSH rule 1 x 776 [728,231,80]
+ CRUSH rule 1 x 777 [981,621,568]
+ CRUSH rule 1 x 778 [411,456,544]
+ CRUSH rule 1 x 779 [346,121,519]
+ CRUSH rule 1 x 780 [476,39,288]
+ CRUSH rule 1 x 781 [10,130,585]
+ CRUSH rule 1 x 782 [462,246,581]
+ CRUSH rule 1 x 783 [580,373,153]
+ CRUSH rule 1 x 784 [413,113,978]
+ CRUSH rule 1 x 785 [341,856,332]
+ CRUSH rule 1 x 786 [411,140,313]
+ CRUSH rule 1 x 787 [605,522,211]
+ CRUSH rule 1 x 788 [226,545,35]
+ CRUSH rule 1 x 789 [545,320,414]
+ CRUSH rule 1 x 790 [414,748,816]
+ CRUSH rule 1 x 791 [660,906,406]
+ CRUSH rule 1 x 792 [287,392,514]
+ CRUSH rule 1 x 793 [631,133,850]
+ CRUSH rule 1 x 794 [931,517,543]
+ CRUSH rule 1 x 795 [551,962,477]
+ CRUSH rule 1 x 796 [814,4,95]
+ CRUSH rule 1 x 797 [64,201,299]
+ CRUSH rule 1 x 798 [422,530,114]
+ CRUSH rule 1 x 799 [824,32,679]
+ CRUSH rule 1 x 800 [862,623,489]
+ CRUSH rule 1 x 801 [145,550,329]
+ CRUSH rule 1 x 802 [570,19,847]
+ CRUSH rule 1 x 803 [151,812,662]
+ CRUSH rule 1 x 804 [467,93,264]
+ CRUSH rule 1 x 805 [621,223,938]
+ CRUSH rule 1 x 806 [898,957,805]
+ CRUSH rule 1 x 807 [354,531,422]
+ CRUSH rule 1 x 808 [7,96,76]
+ CRUSH rule 1 x 809 [70,734,719]
+ CRUSH rule 1 x 810 [701,18,972]
+ CRUSH rule 1 x 811 [248,547,103]
+ CRUSH rule 1 x 812 [230,576,821]
+ CRUSH rule 1 x 813 [805,114,683]
+ CRUSH rule 1 x 814 [54,619,973]
+ CRUSH rule 1 x 815 [679,412,613]
+ CRUSH rule 1 x 816 [919,448,826]
+ CRUSH rule 1 x 817 [765,830,436]
+ CRUSH rule 1 x 818 [415,566,644]
+ CRUSH rule 1 x 819 [721,319,865]
+ CRUSH rule 1 x 820 [218,301,333]
+ CRUSH rule 1 x 821 [185,795,680]
+ CRUSH rule 1 x 822 [356,261,54]
+ CRUSH rule 1 x 823 [220,281,549]
+ CRUSH rule 1 x 824 [292,809,887]
+ CRUSH rule 1 x 825 [949,778,101]
+ CRUSH rule 1 x 826 [767,818,833]
+ CRUSH rule 1 x 827 [631,83,406]
+ CRUSH rule 1 x 828 [288,986,445]
+ CRUSH rule 1 x 829 [990,667,915]
+ CRUSH rule 1 x 830 [152,571,778]
+ CRUSH rule 1 x 831 [814,563,630]
+ CRUSH rule 1 x 832 [235,641,616]
+ CRUSH rule 1 x 833 [657,565,922]
+ CRUSH rule 1 x 834 [907,231,644]
+ CRUSH rule 1 x 835 [784,262,771]
+ CRUSH rule 1 x 836 [951,158,366]
+ CRUSH rule 1 x 837 [556,498,334]
+ CRUSH rule 1 x 838 [329,274,964]
+ CRUSH rule 1 x 839 [568,209,939]
+ CRUSH rule 1 x 840 [45,579,842]
+ CRUSH rule 1 x 841 [652,702,24]
+ CRUSH rule 1 x 842 [629,984,314]
+ CRUSH rule 1 x 843 [799,690,688]
+ CRUSH rule 1 x 844 [694,600,534]
+ CRUSH rule 1 x 845 [332,30,179]
+ CRUSH rule 1 x 846 [452,251,712]
+ CRUSH rule 1 x 847 [399,681,847]
+ CRUSH rule 1 x 848 [303,138,440]
+ CRUSH rule 1 x 849 [666,346,708]
+ CRUSH rule 1 x 850 [644,511,345]
+ CRUSH rule 1 x 851 [527,546,737]
+ CRUSH rule 1 x 852 [31,809,94]
+ CRUSH rule 1 x 853 [483,330,869]
+ CRUSH rule 1 x 854 [697,953,968]
+ CRUSH rule 1 x 855 [837,996,239]
+ CRUSH rule 1 x 856 [712,40,547]
+ CRUSH rule 1 x 857 [77,984,576]
+ CRUSH rule 1 x 858 [412,384,841]
+ CRUSH rule 1 x 859 [173,760,26]
+ CRUSH rule 1 x 860 [776,429,328]
+ CRUSH rule 1 x 861 [705,405,477]
+ CRUSH rule 1 x 862 [809,44,788]
+ CRUSH rule 1 x 863 [349,496,963]
+ CRUSH rule 1 x 864 [717,858,101]
+ CRUSH rule 1 x 865 [857,603,586]
+ CRUSH rule 1 x 866 [394,304,71]
+ CRUSH rule 1 x 867 [640,773,663]
+ CRUSH rule 1 x 868 [613,950,712]
+ CRUSH rule 1 x 869 [973,889,524]
+ CRUSH rule 1 x 870 [505,35,386]
+ CRUSH rule 1 x 871 [239,264,262]
+ CRUSH rule 1 x 872 [21,767,456]
+ CRUSH rule 1 x 873 [954,666,980]
+ CRUSH rule 1 x 874 [54,510,947]
+ CRUSH rule 1 x 875 [809,418,452]
+ CRUSH rule 1 x 876 [483,457,61]
+ CRUSH rule 1 x 877 [542,531,952]
+ CRUSH rule 1 x 878 [217,674,857]
+ CRUSH rule 1 x 879 [999,475,134]
+ CRUSH rule 1 x 880 [678,573,935]
+ CRUSH rule 1 x 881 [394,835,789]
+ CRUSH rule 1 x 882 [467,382,353]
+ CRUSH rule 1 x 883 [802,744,237]
+ CRUSH rule 1 x 884 [653,660,638]
+ CRUSH rule 1 x 885 [898,704,307]
+ CRUSH rule 1 x 886 [434,357,938]
+ CRUSH rule 1 x 887 [297,226,711]
+ CRUSH rule 1 x 888 [863,324,443]
+ CRUSH rule 1 x 889 [105,102,308]
+ CRUSH rule 1 x 890 [550,248,606]
+ CRUSH rule 1 x 891 [575,928,880]
+ CRUSH rule 1 x 892 [259,862,133]
+ CRUSH rule 1 x 893 [902,880,543]
+ CRUSH rule 1 x 894 [180,169,916]
+ CRUSH rule 1 x 895 [725,849,182]
+ CRUSH rule 1 x 896 [951,34,874]
+ CRUSH rule 1 x 897 [810,352,73]
+ CRUSH rule 1 x 898 [979,433,719]
+ CRUSH rule 1 x 899 [685,668,534]
+ CRUSH rule 1 x 900 [530,978,41]
+ CRUSH rule 1 x 901 [740,107,336]
+ CRUSH rule 1 x 902 [800,743,693]
+ CRUSH rule 1 x 903 [230,267,842]
+ CRUSH rule 1 x 904 [346,949,460]
+ CRUSH rule 1 x 905 [530,397,619]
+ CRUSH rule 1 x 906 [80,426,138]
+ CRUSH rule 1 x 907 [365,968,475]
+ CRUSH rule 1 x 908 [204,832,742]
+ CRUSH rule 1 x 909 [883,989,146]
+ CRUSH rule 1 x 910 [549,593,249]
+ CRUSH rule 1 x 911 [325,847,352]
+ CRUSH rule 1 x 912 [874,888,582]
+ CRUSH rule 1 x 913 [331,463,342]
+ CRUSH rule 1 x 914 [836,468,601]
+ CRUSH rule 1 x 915 [245,228,100]
+ CRUSH rule 1 x 916 [77,967,364]
+ CRUSH rule 1 x 917 [239,60,866]
+ CRUSH rule 1 x 918 [988,115,922]
+ CRUSH rule 1 x 919 [783,139,696]
+ CRUSH rule 1 x 920 [623,408,685]
+ CRUSH rule 1 x 921 [105,799,144]
+ CRUSH rule 1 x 922 [887,505,652]
+ CRUSH rule 1 x 923 [223,318,552]
+ CRUSH rule 1 x 924 [25,778,366]
+ CRUSH rule 1 x 925 [912,601,297]
+ CRUSH rule 1 x 926 [968,133,155]
+ CRUSH rule 1 x 927 [277,724,214]
+ CRUSH rule 1 x 928 [554,203,658]
+ CRUSH rule 1 x 929 [761,802,367]
+ CRUSH rule 1 x 930 [814,61,788]
+ CRUSH rule 1 x 931 [29,193,61]
+ CRUSH rule 1 x 932 [446,198,862]
+ CRUSH rule 1 x 933 [352,742,216]
+ CRUSH rule 1 x 934 [730,2,332]
+ CRUSH rule 1 x 935 [731,23,736]
+ CRUSH rule 1 x 936 [322,975,20]
+ CRUSH rule 1 x 937 [822,221,841]
+ CRUSH rule 1 x 938 [557,850,66]
+ CRUSH rule 1 x 939 [150,11,971]
+ CRUSH rule 1 x 940 [638,398,169]
+ CRUSH rule 1 x 941 [730,342,929]
+ CRUSH rule 1 x 942 [62,292,166]
+ CRUSH rule 1 x 943 [165,314,519]
+ CRUSH rule 1 x 944 [199,625,766]
+ CRUSH rule 1 x 945 [946,999,699]
+ CRUSH rule 1 x 946 [595,93,852]
+ CRUSH rule 1 x 947 [800,582,356]
+ CRUSH rule 1 x 948 [132,551,139]
+ CRUSH rule 1 x 949 [792,920,466]
+ CRUSH rule 1 x 950 [111,345,176]
+ CRUSH rule 1 x 951 [414,619,648]
+ CRUSH rule 1 x 952 [775,469,500]
+ CRUSH rule 1 x 953 [349,1,5]
+ CRUSH rule 1 x 954 [570,940,410]
+ CRUSH rule 1 x 955 [729,774,823]
+ CRUSH rule 1 x 956 [519,141,575]
+ CRUSH rule 1 x 957 [242,709,611]
+ CRUSH rule 1 x 958 [84,217,227]
+ CRUSH rule 1 x 959 [270,413,918]
+ CRUSH rule 1 x 960 [458,192,307]
+ CRUSH rule 1 x 961 [981,388,777]
+ CRUSH rule 1 x 962 [623,834,277]
+ CRUSH rule 1 x 963 [291,167,714]
+ CRUSH rule 1 x 964 [28,156,788]
+ CRUSH rule 1 x 965 [675,557,290]
+ CRUSH rule 1 x 966 [836,306,946]
+ CRUSH rule 1 x 967 [966,386,735]
+ CRUSH rule 1 x 968 [864,756,690]
+ CRUSH rule 1 x 969 [729,625,480]
+ CRUSH rule 1 x 970 [800,362,646]
+ CRUSH rule 1 x 971 [737,381,153]
+ CRUSH rule 1 x 972 [952,245,720]
+ CRUSH rule 1 x 973 [356,455,579]
+ CRUSH rule 1 x 974 [545,758,586]
+ CRUSH rule 1 x 975 [336,191,202]
+ CRUSH rule 1 x 976 [446,208,757]
+ CRUSH rule 1 x 977 [202,896,196]
+ CRUSH rule 1 x 978 [612,324,996]
+ CRUSH rule 1 x 979 [843,457,675]
+ CRUSH rule 1 x 980 [60,914,881]
+ CRUSH rule 1 x 981 [702,749,937]
+ CRUSH rule 1 x 982 [298,928,738]
+ CRUSH rule 1 x 983 [723,572,395]
+ CRUSH rule 1 x 984 [723,864,804]
+ CRUSH rule 1 x 985 [945,459,868]
+ CRUSH rule 1 x 986 [772,664,535]
+ CRUSH rule 1 x 987 [88,324,312]
+ CRUSH rule 1 x 988 [522,927,131]
+ CRUSH rule 1 x 989 [578,332,208]
+ CRUSH rule 1 x 990 [638,228,414]
+ CRUSH rule 1 x 991 [530,221,451]
+ CRUSH rule 1 x 992 [925,705,275]
+ CRUSH rule 1 x 993 [991,301,43]
+ CRUSH rule 1 x 994 [276,51,868]
+ CRUSH rule 1 x 995 [288,836,753]
+ CRUSH rule 1 x 996 [887,983,252]
+ CRUSH rule 1 x 997 [110,924,386]
+ CRUSH rule 1 x 998 [435,830,485]
+ CRUSH rule 1 x 999 [876,738,357]
+ CRUSH rule 1 x 1000 [178,963,638]
+ CRUSH rule 1 x 1001 [99,519,66]
+ CRUSH rule 1 x 1002 [515,534,468]
+ CRUSH rule 1 x 1003 [104,611,937]
+ CRUSH rule 1 x 1004 [269,638,724]
+ CRUSH rule 1 x 1005 [369,223,309]
+ CRUSH rule 1 x 1006 [40,107,69]
+ CRUSH rule 1 x 1007 [978,111,416]
+ CRUSH rule 1 x 1008 [965,956,624]
+ CRUSH rule 1 x 1009 [598,476,356]
+ CRUSH rule 1 x 1010 [767,523,239]
+ CRUSH rule 1 x 1011 [289,871,207]
+ CRUSH rule 1 x 1012 [128,28,370]
+ CRUSH rule 1 x 1013 [979,765,660]
+ CRUSH rule 1 x 1014 [979,948,513]
+ CRUSH rule 1 x 1015 [277,790,396]
+ CRUSH rule 1 x 1016 [262,73,128]
+ CRUSH rule 1 x 1017 [150,269,61]
+ CRUSH rule 1 x 1018 [555,829,554]
+ CRUSH rule 1 x 1019 [513,356,265]
+ CRUSH rule 1 x 1020 [158,161,877]
+ CRUSH rule 1 x 1021 [915,998,957]
+ CRUSH rule 1 x 1022 [967,829,973]
+ CRUSH rule 1 x 1023 [488,257,614]
+ rule 1 (metadata) num_rep 3 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536,450]
+ CRUSH rule 1 x 1 [876,250,334,633]
+ CRUSH rule 1 x 2 [292,832,53,392]
+ CRUSH rule 1 x 3 [623,387,124,998]
+ CRUSH rule 1 x 4 [61,334,710,4]
+ CRUSH rule 1 x 5 [946,557,713,664]
+ CRUSH rule 1 x 6 [576,668,212,163]
+ CRUSH rule 1 x 7 [645,753,906,393]
+ CRUSH rule 1 x 8 [243,6,863,781]
+ CRUSH rule 1 x 9 [22,578,251,410]
+ CRUSH rule 1 x 10 [758,828,360,477]
+ CRUSH rule 1 x 11 [769,120,124,527]
+ CRUSH rule 1 x 12 [780,364,689,755]
+ CRUSH rule 1 x 13 [557,18,351,719]
+ CRUSH rule 1 x 14 [59,561,249,461]
+ CRUSH rule 1 x 15 [718,928,993,21]
+ CRUSH rule 1 x 16 [673,632,841,954]
+ CRUSH rule 1 x 17 [648,43,560,514]
+ CRUSH rule 1 x 18 [654,219,181,568]
+ CRUSH rule 1 x 19 [850,545,377,848]
+ CRUSH rule 1 x 20 [717,785,974,5]
+ CRUSH rule 1 x 21 [420,57,519,306]
+ CRUSH rule 1 x 22 [503,998,193,821]
+ CRUSH rule 1 x 23 [411,663,168,110]
+ CRUSH rule 1 x 24 [266,861,353,1]
+ CRUSH rule 1 x 25 [760,483,818,600]
+ CRUSH rule 1 x 26 [903,24,573,718]
+ CRUSH rule 1 x 27 [946,188,289,510]
+ CRUSH rule 1 x 28 [69,312,73,198]
+ CRUSH rule 1 x 29 [844,883,337,628]
+ CRUSH rule 1 x 30 [621,18,613,794]
+ CRUSH rule 1 x 31 [784,943,814,539]
+ CRUSH rule 1 x 32 [173,374,369,972]
+ CRUSH rule 1 x 33 [698,336,357,966]
+ CRUSH rule 1 x 34 [168,836,210,798]
+ CRUSH rule 1 x 35 [274,509,534,818]
+ CRUSH rule 1 x 36 [318,215,153,628]
+ CRUSH rule 1 x 37 [173,604,109,935]
+ CRUSH rule 1 x 38 [708,444,683,604]
+ CRUSH rule 1 x 39 [662,198,417,680]
+ CRUSH rule 1 x 40 [620,801,414,78]
+ CRUSH rule 1 x 41 [811,264,177,127]
+ CRUSH rule 1 x 42 [863,179,527,660]
+ CRUSH rule 1 x 43 [686,822,988,228]
+ CRUSH rule 1 x 44 [396,222,46,841]
+ CRUSH rule 1 x 45 [991,694,253,142]
+ CRUSH rule 1 x 46 [420,909,184,285]
+ CRUSH rule 1 x 47 [467,211,605,207]
+ CRUSH rule 1 x 48 [955,329,368,168]
+ CRUSH rule 1 x 49 [974,891,931,29]
+ CRUSH rule 1 x 50 [870,441,691,823]
+ CRUSH rule 1 x 51 [182,930,25,936]
+ CRUSH rule 1 x 52 [704,812,894,794]
+ CRUSH rule 1 x 53 [185,713,631,280]
+ CRUSH rule 1 x 54 [270,441,100,82]
+ CRUSH rule 1 x 55 [895,734,958,793]
+ CRUSH rule 1 x 56 [564,963,683,324]
+ CRUSH rule 1 x 57 [738,130,208,973]
+ CRUSH rule 1 x 58 [524,113,806,903]
+ CRUSH rule 1 x 59 [408,337,668,529]
+ CRUSH rule 1 x 60 [228,790,857,309]
+ CRUSH rule 1 x 61 [154,843,717,467]
+ CRUSH rule 1 x 62 [594,811,549,276]
+ CRUSH rule 1 x 63 [646,67,884,925]
+ CRUSH rule 1 x 64 [175,542,155,837]
+ CRUSH rule 1 x 65 [745,619,131,867]
+ CRUSH rule 1 x 66 [275,468,23,35]
+ CRUSH rule 1 x 67 [246,958,524,493]
+ CRUSH rule 1 x 68 [711,473,403,228]
+ CRUSH rule 1 x 69 [493,924,850,939]
+ CRUSH rule 1 x 70 [30,499,644,33]
+ CRUSH rule 1 x 71 [984,883,574,716]
+ CRUSH rule 1 x 72 [71,286,942,363]
+ CRUSH rule 1 x 73 [922,618,3,371]
+ CRUSH rule 1 x 74 [629,414,185,573]
+ CRUSH rule 1 x 75 [222,20,174,820]
+ CRUSH rule 1 x 76 [262,366,339,290]
+ CRUSH rule 1 x 77 [638,469,992,280]
+ CRUSH rule 1 x 78 [324,511,788,7]
+ CRUSH rule 1 x 79 [577,990,64,94]
+ CRUSH rule 1 x 80 [501,95,278,903]
+ CRUSH rule 1 x 81 [506,812,9,698]
+ CRUSH rule 1 x 82 [222,145,80,785]
+ CRUSH rule 1 x 83 [71,634,61,91]
+ CRUSH rule 1 x 84 [49,761,773,368]
+ CRUSH rule 1 x 85 [985,896,708,861]
+ CRUSH rule 1 x 86 [537,745,93,524]
+ CRUSH rule 1 x 87 [997,317,463,626]
+ CRUSH rule 1 x 88 [957,350,890,857]
+ CRUSH rule 1 x 89 [399,730,148,314]
+ CRUSH rule 1 x 90 [943,706,683,267]
+ CRUSH rule 1 x 91 [22,368,149,928]
+ CRUSH rule 1 x 92 [532,424,426,773]
+ CRUSH rule 1 x 93 [218,489,405,681]
+ CRUSH rule 1 x 94 [181,96,102,515]
+ CRUSH rule 1 x 95 [343,957,820,139]
+ CRUSH rule 1 x 96 [861,270,87,797]
+ CRUSH rule 1 x 97 [459,706,45,328]
+ CRUSH rule 1 x 98 [327,867,353,948]
+ CRUSH rule 1 x 99 [974,133,468,906]
+ CRUSH rule 1 x 100 [32,445,547,371]
+ CRUSH rule 1 x 101 [142,90,337,950]
+ CRUSH rule 1 x 102 [172,129,139,22]
+ CRUSH rule 1 x 103 [630,47,161,356]
+ CRUSH rule 1 x 104 [758,133,278,11]
+ CRUSH rule 1 x 105 [843,604,47,33]
+ CRUSH rule 1 x 106 [28,681,193,679]
+ CRUSH rule 1 x 107 [74,320,85,819]
+ CRUSH rule 1 x 108 [875,593,575,517]
+ CRUSH rule 1 x 109 [411,985,811,720]
+ CRUSH rule 1 x 110 [440,774,799,660]
+ CRUSH rule 1 x 111 [405,742,276,359]
+ CRUSH rule 1 x 112 [143,181,922,545]
+ CRUSH rule 1 x 113 [153,846,160,903]
+ CRUSH rule 1 x 114 [804,892,939,20]
+ CRUSH rule 1 x 115 [588,508,958,580]
+ CRUSH rule 1 x 116 [327,148,637,486]
+ CRUSH rule 1 x 117 [95,594,989,131]
+ CRUSH rule 1 x 118 [80,957,897,239]
+ CRUSH rule 1 x 119 [386,932,951,768]
+ CRUSH rule 1 x 120 [366,312,653,936]
+ CRUSH rule 1 x 121 [129,154,847,16]
+ CRUSH rule 1 x 122 [873,1,110,939]
+ CRUSH rule 1 x 123 [533,415,789,600]
+ CRUSH rule 1 x 124 [461,691,898,723]
+ CRUSH rule 1 x 125 [342,599,830,402]
+ CRUSH rule 1 x 126 [819,781,822,548]
+ CRUSH rule 1 x 127 [437,893,585,707]
+ CRUSH rule 1 x 128 [679,994,982,550]
+ CRUSH rule 1 x 129 [380,685,947,302]
+ CRUSH rule 1 x 130 [992,52,466,867]
+ CRUSH rule 1 x 131 [469,90,208,599]
+ CRUSH rule 1 x 132 [571,250,316,535]
+ CRUSH rule 1 x 133 [964,728,329,902]
+ CRUSH rule 1 x 134 [999,19,716,963]
+ CRUSH rule 1 x 135 [634,101,52,938]
+ CRUSH rule 1 x 136 [114,889,692,768]
+ CRUSH rule 1 x 137 [839,8,959,280]
+ CRUSH rule 1 x 138 [967,949,138,451]
+ CRUSH rule 1 x 139 [308,711,736,247]
+ CRUSH rule 1 x 140 [764,936,926,55]
+ CRUSH rule 1 x 141 [423,302,112,216]
+ CRUSH rule 1 x 142 [252,821,715,340]
+ CRUSH rule 1 x 143 [33,808,518,477]
+ CRUSH rule 1 x 144 [472,88,969,162]
+ CRUSH rule 1 x 145 [242,208,252,604]
+ CRUSH rule 1 x 146 [290,70,570,384]
+ CRUSH rule 1 x 147 [447,352,657,493]
+ CRUSH rule 1 x 148 [212,644,432,658]
+ CRUSH rule 1 x 149 [9,775,87,35]
+ CRUSH rule 1 x 150 [166,456,582,144]
+ CRUSH rule 1 x 151 [811,875,307,20]
+ CRUSH rule 1 x 152 [449,617,223,9]
+ CRUSH rule 1 x 153 [523,537,695,627]
+ CRUSH rule 1 x 154 [208,559,874,597]
+ CRUSH rule 1 x 155 [569,325,192,296]
+ CRUSH rule 1 x 156 [488,121,521,213]
+ CRUSH rule 1 x 157 [140,723,633,260]
+ CRUSH rule 1 x 158 [786,451,320,239]
+ CRUSH rule 1 x 159 [134,664,517,821]
+ CRUSH rule 1 x 160 [690,112,414,990]
+ CRUSH rule 1 x 161 [324,912,397,423]
+ CRUSH rule 1 x 162 [748,567,284,183]
+ CRUSH rule 1 x 163 [575,499,31,816]
+ CRUSH rule 1 x 164 [314,489,308,326]
+ CRUSH rule 1 x 165 [116,209,750,53]
+ CRUSH rule 1 x 166 [352,706,701,810]
+ CRUSH rule 1 x 167 [27,743,174,142]
+ CRUSH rule 1 x 168 [953,898,880,660]
+ CRUSH rule 1 x 169 [912,147,266,547]
+ CRUSH rule 1 x 170 [421,515,828,844]
+ CRUSH rule 1 x 171 [488,584,880,964]
+ CRUSH rule 1 x 172 [366,443,957,66]
+ CRUSH rule 1 x 173 [863,291,625,287]
+ CRUSH rule 1 x 174 [263,555,650,410]
+ CRUSH rule 1 x 175 [875,961,361,575]
+ CRUSH rule 1 x 176 [745,83,701,680]
+ CRUSH rule 1 x 177 [128,244,41,123]
+ CRUSH rule 1 x 178 [155,41,264,777]
+ CRUSH rule 1 x 179 [593,833,202,183]
+ CRUSH rule 1 x 180 [154,734,17,831]
+ CRUSH rule 1 x 181 [289,675,723,800]
+ CRUSH rule 1 x 182 [730,931,560,209]
+ CRUSH rule 1 x 183 [639,237,794,815]
+ CRUSH rule 1 x 184 [704,312,685,645]
+ CRUSH rule 1 x 185 [97,100,762,82]
+ CRUSH rule 1 x 186 [26,665,554,215]
+ CRUSH rule 1 x 187 [649,14,740,494]
+ CRUSH rule 1 x 188 [682,695,590,743]
+ CRUSH rule 1 x 189 [325,693,726,51]
+ CRUSH rule 1 x 190 [399,933,136,955]
+ CRUSH rule 1 x 191 [629,533,17,126]
+ CRUSH rule 1 x 192 [503,578,38,492]
+ CRUSH rule 1 x 193 [546,333,651,678]
+ CRUSH rule 1 x 194 [242,473,58,655]
+ CRUSH rule 1 x 195 [625,719,135,81]
+ CRUSH rule 1 x 196 [357,114,125,867]
+ CRUSH rule 1 x 197 [306,954,453,873]
+ CRUSH rule 1 x 198 [863,791,311,911]
+ CRUSH rule 1 x 199 [935,906,929,252]
+ CRUSH rule 1 x 200 [373,774,229,454]
+ CRUSH rule 1 x 201 [659,320,477,313]
+ CRUSH rule 1 x 202 [260,433,524,880]
+ CRUSH rule 1 x 203 [36,239,675,971]
+ CRUSH rule 1 x 204 [92,516,993,728]
+ CRUSH rule 1 x 205 [68,395,473,45]
+ CRUSH rule 1 x 206 [570,530,642,380]
+ CRUSH rule 1 x 207 [834,457,850,917]
+ CRUSH rule 1 x 208 [927,484,640,976]
+ CRUSH rule 1 x 209 [878,66,58,940]
+ CRUSH rule 1 x 210 [572,981,484,29]
+ CRUSH rule 1 x 211 [107,597,780,857]
+ CRUSH rule 1 x 212 [389,107,838,624]
+ CRUSH rule 1 x 213 [497,717,567,728]
+ CRUSH rule 1 x 214 [798,65,254,572]
+ CRUSH rule 1 x 215 [233,419,283,638]
+ CRUSH rule 1 x 216 [494,464,742,523]
+ CRUSH rule 1 x 217 [352,396,309,938]
+ CRUSH rule 1 x 218 [895,864,988,650]
+ CRUSH rule 1 x 219 [222,534,277,242]
+ CRUSH rule 1 x 220 [281,19,584,563]
+ CRUSH rule 1 x 221 [64,928,963,130]
+ CRUSH rule 1 x 222 [40,544,161,199]
+ CRUSH rule 1 x 223 [645,556,159,417]
+ CRUSH rule 1 x 224 [647,165,957,263]
+ CRUSH rule 1 x 225 [219,714,858,747]
+ CRUSH rule 1 x 226 [372,511,181,277]
+ CRUSH rule 1 x 227 [925,156,714,863]
+ CRUSH rule 1 x 228 [682,404,839,263]
+ CRUSH rule 1 x 229 [880,838,770,891]
+ CRUSH rule 1 x 230 [328,659,916,468]
+ CRUSH rule 1 x 231 [320,383,669,109]
+ CRUSH rule 1 x 232 [924,846,394,319]
+ CRUSH rule 1 x 233 [948,652,575,838]
+ CRUSH rule 1 x 234 [484,943,42,575]
+ CRUSH rule 1 x 235 [750,65,590,168]
+ CRUSH rule 1 x 236 [551,787,490,136]
+ CRUSH rule 1 x 237 [390,157,166,251]
+ CRUSH rule 1 x 238 [570,6,989,707]
+ CRUSH rule 1 x 239 [729,959,376,975]
+ CRUSH rule 1 x 240 [981,241,156,767]
+ CRUSH rule 1 x 241 [310,816,641,177]
+ CRUSH rule 1 x 242 [161,63,642,837]
+ CRUSH rule 1 x 243 [180,394,33,683]
+ CRUSH rule 1 x 244 [52,174,685,189]
+ CRUSH rule 1 x 245 [523,121,915,84]
+ CRUSH rule 1 x 246 [362,893,390,487]
+ CRUSH rule 1 x 247 [382,184,116,34]
+ CRUSH rule 1 x 248 [129,114,852,469]
+ CRUSH rule 1 x 249 [159,683,91,856]
+ CRUSH rule 1 x 250 [404,945,569,955]
+ CRUSH rule 1 x 251 [661,225,738,757]
+ CRUSH rule 1 x 252 [961,226,542,103]
+ CRUSH rule 1 x 253 [651,97,225,364]
+ CRUSH rule 1 x 254 [123,33,741,692]
+ CRUSH rule 1 x 255 [314,649,891,855]
+ CRUSH rule 1 x 256 [315,215,651,126]
+ CRUSH rule 1 x 257 [825,264,867,732]
+ CRUSH rule 1 x 258 [624,789,370,723]
+ CRUSH rule 1 x 259 [602,542,70,563]
+ CRUSH rule 1 x 260 [717,878,43,56]
+ CRUSH rule 1 x 261 [145,517,20,903]
+ CRUSH rule 1 x 262 [223,1,561,420]
+ CRUSH rule 1 x 263 [462,211,405,508]
+ CRUSH rule 1 x 264 [654,471,266,662]
+ CRUSH rule 1 x 265 [302,794,704,798]
+ CRUSH rule 1 x 266 [202,132,884,209]
+ CRUSH rule 1 x 267 [282,938,657,113]
+ CRUSH rule 1 x 268 [338,309,356,278]
+ CRUSH rule 1 x 269 [738,122,266,200]
+ CRUSH rule 1 x 270 [707,982,946,196]
+ CRUSH rule 1 x 271 [705,432,364,735]
+ CRUSH rule 1 x 272 [756,545,942,56]
+ CRUSH rule 1 x 273 [197,502,527,721]
+ CRUSH rule 1 x 274 [992,44,653,573]
+ CRUSH rule 1 x 275 [544,789,170,434]
+ CRUSH rule 1 x 276 [658,467,577,268]
+ CRUSH rule 1 x 277 [143,490,880,483]
+ CRUSH rule 1 x 278 [492,647,355,282]
+ CRUSH rule 1 x 279 [517,792,604,987]
+ CRUSH rule 1 x 280 [825,740,27,848]
+ CRUSH rule 1 x 281 [224,629,120,562]
+ CRUSH rule 1 x 282 [298,661,380,416]
+ CRUSH rule 1 x 283 [311,606,208,50]
+ CRUSH rule 1 x 284 [771,466,371,743]
+ CRUSH rule 1 x 285 [693,362,404,676]
+ CRUSH rule 1 x 286 [364,477,285,167]
+ CRUSH rule 1 x 287 [591,611,828,995]
+ CRUSH rule 1 x 288 [965,541,848,796]
+ CRUSH rule 1 x 289 [225,551,948,877]
+ CRUSH rule 1 x 290 [577,762,777,751]
+ CRUSH rule 1 x 291 [160,903,477,381]
+ CRUSH rule 1 x 292 [873,598,216,666]
+ CRUSH rule 1 x 293 [100,234,874,47]
+ CRUSH rule 1 x 294 [285,943,379,520]
+ CRUSH rule 1 x 295 [938,262,880,327]
+ CRUSH rule 1 x 296 [850,327,86,472]
+ CRUSH rule 1 x 297 [951,53,99,558]
+ CRUSH rule 1 x 298 [173,336,85,766]
+ CRUSH rule 1 x 299 [598,591,315,386]
+ CRUSH rule 1 x 300 [531,957,62,459]
+ CRUSH rule 1 x 301 [823,628,23,858]
+ CRUSH rule 1 x 302 [184,80,780,871]
+ CRUSH rule 1 x 303 [521,766,222,830]
+ CRUSH rule 1 x 304 [980,127,807,507]
+ CRUSH rule 1 x 305 [153,816,22,927]
+ CRUSH rule 1 x 306 [423,739,664,753]
+ CRUSH rule 1 x 307 [997,557,682,456]
+ CRUSH rule 1 x 308 [991,874,534,465]
+ CRUSH rule 1 x 309 [860,394,724,858]
+ CRUSH rule 1 x 310 [589,818,546,201]
+ CRUSH rule 1 x 311 [477,774,225,590]
+ CRUSH rule 1 x 312 [887,853,950,354]
+ CRUSH rule 1 x 313 [802,646,447,416]
+ CRUSH rule 1 x 314 [654,974,229,511]
+ CRUSH rule 1 x 315 [767,227,28,740]
+ CRUSH rule 1 x 316 [778,83,733,359]
+ CRUSH rule 1 x 317 [184,418,642,986]
+ CRUSH rule 1 x 318 [525,410,500,543]
+ CRUSH rule 1 x 319 [476,724,569,382]
+ CRUSH rule 1 x 320 [149,610,697,296]
+ CRUSH rule 1 x 321 [710,79,667,671]
+ CRUSH rule 1 x 322 [175,275,323,333]
+ CRUSH rule 1 x 323 [819,604,638,792]
+ CRUSH rule 1 x 324 [16,745,511,439]
+ CRUSH rule 1 x 325 [486,400,872,873]
+ CRUSH rule 1 x 326 [613,765,207,19]
+ CRUSH rule 1 x 327 [125,289,738,408]
+ CRUSH rule 1 x 328 [807,383,476,583]
+ CRUSH rule 1 x 329 [588,938,599,432]
+ CRUSH rule 1 x 330 [932,644,41,611]
+ CRUSH rule 1 x 331 [341,953,950,537]
+ CRUSH rule 1 x 332 [153,726,459,950]
+ CRUSH rule 1 x 333 [745,845,853,860]
+ CRUSH rule 1 x 334 [614,751,807,58]
+ CRUSH rule 1 x 335 [518,721,221,283]
+ CRUSH rule 1 x 336 [389,424,77,309]
+ CRUSH rule 1 x 337 [753,508,765,720]
+ CRUSH rule 1 x 338 [128,810,490,753]
+ CRUSH rule 1 x 339 [430,308,58,751]
+ CRUSH rule 1 x 340 [541,44,630,231]
+ CRUSH rule 1 x 341 [402,26,631,439]
+ CRUSH rule 1 x 342 [982,57,992,461]
+ CRUSH rule 1 x 343 [833,412,572,732]
+ CRUSH rule 1 x 344 [784,533,792,41]
+ CRUSH rule 1 x 345 [546,300,304,691]
+ CRUSH rule 1 x 346 [302,420,428,891]
+ CRUSH rule 1 x 347 [488,778,101,217]
+ CRUSH rule 1 x 348 [903,744,937,718]
+ CRUSH rule 1 x 349 [471,547,582,306]
+ CRUSH rule 1 x 350 [348,221,823,335]
+ CRUSH rule 1 x 351 [961,582,705,346]
+ CRUSH rule 1 x 352 [728,137,461,298]
+ CRUSH rule 1 x 353 [904,202,184,447]
+ CRUSH rule 1 x 354 [345,226,319,256]
+ CRUSH rule 1 x 355 [50,430,175,43]
+ CRUSH rule 1 x 356 [87,185,55,423]
+ CRUSH rule 1 x 357 [762,459,921,473]
+ CRUSH rule 1 x 358 [908,25,280,6]
+ CRUSH rule 1 x 359 [484,15,132,121]
+ CRUSH rule 1 x 360 [173,378,337,702]
+ CRUSH rule 1 x 361 [404,577,115,25]
+ CRUSH rule 1 x 362 [403,1,422,945]
+ CRUSH rule 1 x 363 [639,911,510,162]
+ CRUSH rule 1 x 364 [752,689,610,990]
+ CRUSH rule 1 x 365 [956,999,212,230]
+ CRUSH rule 1 x 366 [860,925,924,763]
+ CRUSH rule 1 x 367 [205,609,647,665]
+ CRUSH rule 1 x 368 [301,284,810,169]
+ CRUSH rule 1 x 369 [452,658,339,217]
+ CRUSH rule 1 x 370 [11,467,695,989]
+ CRUSH rule 1 x 371 [124,487,55,514]
+ CRUSH rule 1 x 372 [253,48,979,846]
+ CRUSH rule 1 x 373 [715,605,775,748]
+ CRUSH rule 1 x 374 [191,887,920,760]
+ CRUSH rule 1 x 375 [711,385,651,665]
+ CRUSH rule 1 x 376 [597,818,49,458]
+ CRUSH rule 1 x 377 [294,256,933,771]
+ CRUSH rule 1 x 378 [34,151,681,707]
+ CRUSH rule 1 x 379 [869,136,315,378]
+ CRUSH rule 1 x 380 [294,97,575,791]
+ CRUSH rule 1 x 381 [119,710,219,827]
+ CRUSH rule 1 x 382 [69,631,508,706]
+ CRUSH rule 1 x 383 [922,588,589,925]
+ CRUSH rule 1 x 384 [221,945,671,117]
+ CRUSH rule 1 x 385 [561,737,953,723]
+ CRUSH rule 1 x 386 [335,442,788,696]
+ CRUSH rule 1 x 387 [514,43,353,88]
+ CRUSH rule 1 x 388 [587,89,157,996]
+ CRUSH rule 1 x 389 [109,641,255,466]
+ CRUSH rule 1 x 390 [925,149,421,489]
+ CRUSH rule 1 x 391 [267,87,387,527]
+ CRUSH rule 1 x 392 [382,485,370,849]
+ CRUSH rule 1 x 393 [425,721,221,753]
+ CRUSH rule 1 x 394 [898,18,38,793]
+ CRUSH rule 1 x 395 [806,876,269,679]
+ CRUSH rule 1 x 396 [790,970,437,449]
+ CRUSH rule 1 x 397 [136,363,507,613]
+ CRUSH rule 1 x 398 [914,116,558,258]
+ CRUSH rule 1 x 399 [261,94,299,202]
+ CRUSH rule 1 x 400 [661,197,338,461]
+ CRUSH rule 1 x 401 [953,979,287,803]
+ CRUSH rule 1 x 402 [738,819,618,522]
+ CRUSH rule 1 x 403 [573,238,425,546]
+ CRUSH rule 1 x 404 [526,848,790,253]
+ CRUSH rule 1 x 405 [582,505,330,334]
+ CRUSH rule 1 x 406 [768,324,493,60]
+ CRUSH rule 1 x 407 [260,951,437,587]
+ CRUSH rule 1 x 408 [657,81,770,734]
+ CRUSH rule 1 x 409 [498,89,182,423]
+ CRUSH rule 1 x 410 [28,793,737,352]
+ CRUSH rule 1 x 411 [684,992,60,659]
+ CRUSH rule 1 x 412 [261,958,699,950]
+ CRUSH rule 1 x 413 [891,835,297,441]
+ CRUSH rule 1 x 414 [127,459,119,965]
+ CRUSH rule 1 x 415 [272,540,631,328]
+ CRUSH rule 1 x 416 [739,617,115,530]
+ CRUSH rule 1 x 417 [106,209,157,878]
+ CRUSH rule 1 x 418 [525,441,147,390]
+ CRUSH rule 1 x 419 [603,673,615,465]
+ CRUSH rule 1 x 420 [988,213,251,226]
+ CRUSH rule 1 x 421 [761,521,748,368]
+ CRUSH rule 1 x 422 [317,160,924,548]
+ CRUSH rule 1 x 423 [137,807,168,472]
+ CRUSH rule 1 x 424 [920,37,146,263]
+ CRUSH rule 1 x 425 [277,693,285,221]
+ CRUSH rule 1 x 426 [485,936,407,854]
+ CRUSH rule 1 x 427 [242,515,9,564]
+ CRUSH rule 1 x 428 [632,635,26,473]
+ CRUSH rule 1 x 429 [641,73,465,127]
+ CRUSH rule 1 x 430 [626,585,6,387]
+ CRUSH rule 1 x 431 [697,76,753,570]
+ CRUSH rule 1 x 432 [590,526,306,283]
+ CRUSH rule 1 x 433 [284,387,149,817]
+ CRUSH rule 1 x 434 [538,985,79,953]
+ CRUSH rule 1 x 435 [30,318,593,635]
+ CRUSH rule 1 x 436 [164,919,851,693]
+ CRUSH rule 1 x 437 [322,212,163,606]
+ CRUSH rule 1 x 438 [142,392,85,594]
+ CRUSH rule 1 x 439 [119,370,68,443]
+ CRUSH rule 1 x 440 [333,403,187,863]
+ CRUSH rule 1 x 441 [477,727,906,145]
+ CRUSH rule 1 x 442 [274,590,933,244]
+ CRUSH rule 1 x 443 [983,748,574,718]
+ CRUSH rule 1 x 444 [536,509,431,146]
+ CRUSH rule 1 x 445 [485,753,528,209]
+ CRUSH rule 1 x 446 [345,634,42,294]
+ CRUSH rule 1 x 447 [61,845,767,600]
+ CRUSH rule 1 x 448 [333,232,292,846]
+ CRUSH rule 1 x 449 [680,16,484,670]
+ CRUSH rule 1 x 450 [235,214,79,423]
+ CRUSH rule 1 x 451 [961,468,333,640]
+ CRUSH rule 1 x 452 [525,479,153,528]
+ CRUSH rule 1 x 453 [138,466,302,86]
+ CRUSH rule 1 x 454 [137,625,215,402]
+ CRUSH rule 1 x 455 [173,150,997,16]
+ CRUSH rule 1 x 456 [235,226,238,258]
+ CRUSH rule 1 x 457 [450,577,253,413]
+ CRUSH rule 1 x 458 [195,537,91,814]
+ CRUSH rule 1 x 459 [381,555,312,573]
+ CRUSH rule 1 x 460 [972,730,534,678]
+ CRUSH rule 1 x 461 [506,279,142,830]
+ CRUSH rule 1 x 462 [692,959,578,57]
+ CRUSH rule 1 x 463 [788,667,949,550]
+ CRUSH rule 1 x 464 [133,122,588,999]
+ CRUSH rule 1 x 465 [971,190,230,777]
+ CRUSH rule 1 x 466 [394,576,148,157]
+ CRUSH rule 1 x 467 [517,28,366,362]
+ CRUSH rule 1 x 468 [829,143,874,225]
+ CRUSH rule 1 x 469 [987,936,106,725]
+ CRUSH rule 1 x 470 [107,982,56,889]
+ CRUSH rule 1 x 471 [181,897,629,860]
+ CRUSH rule 1 x 472 [547,512,172,24]
+ CRUSH rule 1 x 473 [760,997,824,905]
+ CRUSH rule 1 x 474 [787,418,743,628]
+ CRUSH rule 1 x 475 [662,312,253,617]
+ CRUSH rule 1 x 476 [110,495,185,508]
+ CRUSH rule 1 x 477 [393,954,834,132]
+ CRUSH rule 1 x 478 [246,483,480,644]
+ CRUSH rule 1 x 479 [70,929,697,931]
+ CRUSH rule 1 x 480 [753,119,961,607]
+ CRUSH rule 1 x 481 [470,429,677,242]
+ CRUSH rule 1 x 482 [451,566,961,675]
+ CRUSH rule 1 x 483 [816,72,371,278]
+ CRUSH rule 1 x 484 [540,454,389,31]
+ CRUSH rule 1 x 485 [74,582,624,684]
+ CRUSH rule 1 x 486 [958,595,199,763]
+ CRUSH rule 1 x 487 [228,302,804,833]
+ CRUSH rule 1 x 488 [180,529,722,956]
+ CRUSH rule 1 x 489 [47,617,812,187]
+ CRUSH rule 1 x 490 [905,822,479,124]
+ CRUSH rule 1 x 491 [892,370,609,998]
+ CRUSH rule 1 x 492 [588,959,127,948]
+ CRUSH rule 1 x 493 [353,461,593,291]
+ CRUSH rule 1 x 494 [378,848,443,368]
+ CRUSH rule 1 x 495 [845,653,768,234]
+ CRUSH rule 1 x 496 [13,988,0,691]
+ CRUSH rule 1 x 497 [796,877,788,394]
+ CRUSH rule 1 x 498 [412,337,270,705]
+ CRUSH rule 1 x 499 [330,695,8,74]
+ CRUSH rule 1 x 500 [820,272,547,765]
+ CRUSH rule 1 x 501 [110,44,132,442]
+ CRUSH rule 1 x 502 [336,595,650,274]
+ CRUSH rule 1 x 503 [922,211,157,722]
+ CRUSH rule 1 x 504 [483,52,122,432]
+ CRUSH rule 1 x 505 [482,598,224,279]
+ CRUSH rule 1 x 506 [493,123,43,856]
+ CRUSH rule 1 x 507 [12,598,264,422]
+ CRUSH rule 1 x 508 [227,157,611,301]
+ CRUSH rule 1 x 509 [807,242,363,122]
+ CRUSH rule 1 x 510 [134,437,227,75]
+ CRUSH rule 1 x 511 [212,54,83,799]
+ CRUSH rule 1 x 512 [236,630,758,752]
+ CRUSH rule 1 x 513 [994,693,644,938]
+ CRUSH rule 1 x 514 [45,508,831,19]
+ CRUSH rule 1 x 515 [504,138,480,272]
+ CRUSH rule 1 x 516 [285,409,136,570]
+ CRUSH rule 1 x 517 [300,232,23,906]
+ CRUSH rule 1 x 518 [397,674,98,898]
+ CRUSH rule 1 x 519 [86,750,772,913]
+ CRUSH rule 1 x 520 [900,833,614,130]
+ CRUSH rule 1 x 521 [31,47,236,751]
+ CRUSH rule 1 x 522 [390,16,280,144]
+ CRUSH rule 1 x 523 [618,308,424,590]
+ CRUSH rule 1 x 524 [635,189,687,963]
+ CRUSH rule 1 x 525 [311,916,699,262]
+ CRUSH rule 1 x 526 [48,738,227,718]
+ CRUSH rule 1 x 527 [202,851,889,216]
+ CRUSH rule 1 x 528 [565,827,590,273]
+ CRUSH rule 1 x 529 [934,864,241,43]
+ CRUSH rule 1 x 530 [502,934,298,670]
+ CRUSH rule 1 x 531 [681,627,942,487]
+ CRUSH rule 1 x 532 [422,6,147,205]
+ CRUSH rule 1 x 533 [863,68,364,983]
+ CRUSH rule 1 x 534 [962,931,775,172]
+ CRUSH rule 1 x 535 [89,565,397,693]
+ CRUSH rule 1 x 536 [499,351,760,458]
+ CRUSH rule 1 x 537 [676,547,787,311]
+ CRUSH rule 1 x 538 [58,644,571,649]
+ CRUSH rule 1 x 539 [837,953,457,711]
+ CRUSH rule 1 x 540 [831,50,132,213]
+ CRUSH rule 1 x 541 [582,757,121,525]
+ CRUSH rule 1 x 542 [472,132,790,997]
+ CRUSH rule 1 x 543 [382,272,797,330]
+ CRUSH rule 1 x 544 [947,930,496,883]
+ CRUSH rule 1 x 545 [425,570,305,77]
+ CRUSH rule 1 x 546 [18,65,529,437]
+ CRUSH rule 1 x 547 [445,715,600,472]
+ CRUSH rule 1 x 548 [367,569,980,167]
+ CRUSH rule 1 x 549 [125,715,671,817]
+ CRUSH rule 1 x 550 [425,599,744,199]
+ CRUSH rule 1 x 551 [44,1,528,922]
+ CRUSH rule 1 x 552 [246,104,68,239]
+ CRUSH rule 1 x 553 [71,703,615,28]
+ CRUSH rule 1 x 554 [207,124,217,166]
+ CRUSH rule 1 x 555 [570,28,317,420]
+ CRUSH rule 1 x 556 [674,152,421,79]
+ CRUSH rule 1 x 557 [347,817,191,391]
+ CRUSH rule 1 x 558 [627,426,369,692]
+ CRUSH rule 1 x 559 [940,630,924,242]
+ CRUSH rule 1 x 560 [295,903,541,29]
+ CRUSH rule 1 x 561 [506,682,384,637]
+ CRUSH rule 1 x 562 [718,529,87,729]
+ CRUSH rule 1 x 563 [552,332,747,206]
+ CRUSH rule 1 x 564 [835,769,736,486]
+ CRUSH rule 1 x 565 [8,167,539,182]
+ CRUSH rule 1 x 566 [600,481,301,263]
+ CRUSH rule 1 x 567 [999,994,509,899]
+ CRUSH rule 1 x 568 [252,431,157,62]
+ CRUSH rule 1 x 569 [643,218,943,455]
+ CRUSH rule 1 x 570 [617,635,765,422]
+ CRUSH rule 1 x 571 [757,80,59,98]
+ CRUSH rule 1 x 572 [299,348,575,889]
+ CRUSH rule 1 x 573 [25,505,270,167]
+ CRUSH rule 1 x 574 [215,431,624,177]
+ CRUSH rule 1 x 575 [225,252,611,546]
+ CRUSH rule 1 x 576 [627,94,159,857]
+ CRUSH rule 1 x 577 [237,809,778,636]
+ CRUSH rule 1 x 578 [885,313,120,344]
+ CRUSH rule 1 x 579 [924,575,787,831]
+ CRUSH rule 1 x 580 [718,51,766,121]
+ CRUSH rule 1 x 581 [219,807,129,571]
+ CRUSH rule 1 x 582 [893,701,598,863]
+ CRUSH rule 1 x 583 [246,930,964,170]
+ CRUSH rule 1 x 584 [336,432,680,175]
+ CRUSH rule 1 x 585 [324,999,397,485]
+ CRUSH rule 1 x 586 [558,230,976,541]
+ CRUSH rule 1 x 587 [985,830,597,21]
+ CRUSH rule 1 x 588 [211,544,57,134]
+ CRUSH rule 1 x 589 [129,21,112,190]
+ CRUSH rule 1 x 590 [467,969,652,593]
+ CRUSH rule 1 x 591 [758,514,316,164]
+ CRUSH rule 1 x 592 [525,253,190,443]
+ CRUSH rule 1 x 593 [601,885,339,152]
+ CRUSH rule 1 x 594 [227,60,450,30]
+ CRUSH rule 1 x 595 [720,854,496,912]
+ CRUSH rule 1 x 596 [751,195,997,77]
+ CRUSH rule 1 x 597 [129,574,714,8]
+ CRUSH rule 1 x 598 [679,207,604,396]
+ CRUSH rule 1 x 599 [668,315,683,349]
+ CRUSH rule 1 x 600 [143,396,464,444]
+ CRUSH rule 1 x 601 [326,573,873,902]
+ CRUSH rule 1 x 602 [860,281,875,535]
+ CRUSH rule 1 x 603 [709,328,445,349]
+ CRUSH rule 1 x 604 [571,62,814,95]
+ CRUSH rule 1 x 605 [252,739,860,27]
+ CRUSH rule 1 x 606 [339,236,759,842]
+ CRUSH rule 1 x 607 [590,248,759,868]
+ CRUSH rule 1 x 608 [145,635,309,467]
+ CRUSH rule 1 x 609 [973,547,223,79]
+ CRUSH rule 1 x 610 [435,816,961,983]
+ CRUSH rule 1 x 611 [559,283,422,584]
+ CRUSH rule 1 x 612 [273,149,123,576]
+ CRUSH rule 1 x 613 [828,614,642,674]
+ CRUSH rule 1 x 614 [478,748,393,34]
+ CRUSH rule 1 x 615 [392,155,144,326]
+ CRUSH rule 1 x 616 [778,637,452,248]
+ CRUSH rule 1 x 617 [622,713,996,833]
+ CRUSH rule 1 x 618 [149,877,270,329]
+ CRUSH rule 1 x 619 [604,163,656,409]
+ CRUSH rule 1 x 620 [181,23,409,198]
+ CRUSH rule 1 x 621 [735,902,386,237]
+ CRUSH rule 1 x 622 [661,824,717,568]
+ CRUSH rule 1 x 623 [142,121,643,61]
+ CRUSH rule 1 x 624 [360,716,420,398]
+ CRUSH rule 1 x 625 [541,167,385,1]
+ CRUSH rule 1 x 626 [364,431,610,363]
+ CRUSH rule 1 x 627 [458,137,557,410]
+ CRUSH rule 1 x 628 [250,350,556,497]
+ CRUSH rule 1 x 629 [928,160,710,572]
+ CRUSH rule 1 x 630 [243,19,918,556]
+ CRUSH rule 1 x 631 [438,221,574,676]
+ CRUSH rule 1 x 632 [797,368,247,5]
+ CRUSH rule 1 x 633 [993,749,525,485]
+ CRUSH rule 1 x 634 [239,351,633,299]
+ CRUSH rule 1 x 635 [640,965,25,961]
+ CRUSH rule 1 x 636 [173,290,297,991]
+ CRUSH rule 1 x 637 [0,918,98,108]
+ CRUSH rule 1 x 638 [702,235,424,900]
+ CRUSH rule 1 x 639 [475,687,31,785]
+ CRUSH rule 1 x 640 [31,664,399,677]
+ CRUSH rule 1 x 641 [296,473,108,963]
+ CRUSH rule 1 x 642 [894,273,427,606]
+ CRUSH rule 1 x 643 [117,111,732,191]
+ CRUSH rule 1 x 644 [438,336,327,512]
+ CRUSH rule 1 x 645 [982,702,351,573]
+ CRUSH rule 1 x 646 [334,804,146,842]
+ CRUSH rule 1 x 647 [933,787,185,334]
+ CRUSH rule 1 x 648 [22,444,400,862]
+ CRUSH rule 1 x 649 [503,229,213,460]
+ CRUSH rule 1 x 650 [328,659,420,443]
+ CRUSH rule 1 x 651 [3,880,823,123]
+ CRUSH rule 1 x 652 [495,977,563,733]
+ CRUSH rule 1 x 653 [185,718,804,280]
+ CRUSH rule 1 x 654 [130,528,380,81]
+ CRUSH rule 1 x 655 [560,872,454,504]
+ CRUSH rule 1 x 656 [219,885,178,981]
+ CRUSH rule 1 x 657 [233,684,813,490]
+ CRUSH rule 1 x 658 [778,6,756,380]
+ CRUSH rule 1 x 659 [240,663,306,540]
+ CRUSH rule 1 x 660 [244,855,196,147]
+ CRUSH rule 1 x 661 [184,270,128,398]
+ CRUSH rule 1 x 662 [65,883,921,438]
+ CRUSH rule 1 x 663 [323,721,594,812]
+ CRUSH rule 1 x 664 [865,113,512,51]
+ CRUSH rule 1 x 665 [420,850,591,475]
+ CRUSH rule 1 x 666 [319,767,246,3]
+ CRUSH rule 1 x 667 [875,39,343,100]
+ CRUSH rule 1 x 668 [331,122,263,599]
+ CRUSH rule 1 x 669 [915,521,402,747]
+ CRUSH rule 1 x 670 [845,659,943,447]
+ CRUSH rule 1 x 671 [108,634,527,363]
+ CRUSH rule 1 x 672 [578,216,110,589]
+ CRUSH rule 1 x 673 [442,74,579,797]
+ CRUSH rule 1 x 674 [588,364,281,308]
+ CRUSH rule 1 x 675 [489,698,744,671]
+ CRUSH rule 1 x 676 [928,911,40,180]
+ CRUSH rule 1 x 677 [399,269,692,131]
+ CRUSH rule 1 x 678 [546,752,544,155]
+ CRUSH rule 1 x 679 [988,25,275,433]
+ CRUSH rule 1 x 680 [335,963,382,486]
+ CRUSH rule 1 x 681 [690,462,623,466]
+ CRUSH rule 1 x 682 [196,588,154,257]
+ CRUSH rule 1 x 683 [627,25,421,160]
+ CRUSH rule 1 x 684 [38,804,592,158]
+ CRUSH rule 1 x 685 [841,368,548,362]
+ CRUSH rule 1 x 686 [336,287,525,440]
+ CRUSH rule 1 x 687 [20,682,924,653]
+ CRUSH rule 1 x 688 [463,371,780,556]
+ CRUSH rule 1 x 689 [569,250,78,816]
+ CRUSH rule 1 x 690 [551,144,587,263]
+ CRUSH rule 1 x 691 [766,464,446,533]
+ CRUSH rule 1 x 692 [739,634,18,245]
+ CRUSH rule 1 x 693 [339,297,118,330]
+ CRUSH rule 1 x 694 [405,26,830,181]
+ CRUSH rule 1 x 695 [622,576,597,535]
+ CRUSH rule 1 x 696 [558,902,689,13]
+ CRUSH rule 1 x 697 [818,222,406,691]
+ CRUSH rule 1 x 698 [178,48,402,233]
+ CRUSH rule 1 x 699 [450,244,180,919]
+ CRUSH rule 1 x 700 [502,771,987,706]
+ CRUSH rule 1 x 701 [4,612,782,216]
+ CRUSH rule 1 x 702 [177,630,232,923]
+ CRUSH rule 1 x 703 [354,178,389,393]
+ CRUSH rule 1 x 704 [646,601,156,171]
+ CRUSH rule 1 x 705 [921,401,890,265]
+ CRUSH rule 1 x 706 [652,877,562,452]
+ CRUSH rule 1 x 707 [345,745,67,716]
+ CRUSH rule 1 x 708 [333,607,180,469]
+ CRUSH rule 1 x 709 [45,187,302,115]
+ CRUSH rule 1 x 710 [94,855,43,199]
+ CRUSH rule 1 x 711 [227,653,731,150]
+ CRUSH rule 1 x 712 [398,953,136,870]
+ CRUSH rule 1 x 713 [116,800,503,662]
+ CRUSH rule 1 x 714 [111,629,866,709]
+ CRUSH rule 1 x 715 [531,291,486,382]
+ CRUSH rule 1 x 716 [169,541,291,42]
+ CRUSH rule 1 x 717 [417,446,994,894]
+ CRUSH rule 1 x 718 [992,383,298,844]
+ CRUSH rule 1 x 719 [936,674,324,759]
+ CRUSH rule 1 x 720 [370,188,174,464]
+ CRUSH rule 1 x 721 [320,859,278,259]
+ CRUSH rule 1 x 722 [7,2,673,129]
+ CRUSH rule 1 x 723 [270,553,831,662]
+ CRUSH rule 1 x 724 [666,822,708,895]
+ CRUSH rule 1 x 725 [794,406,875,459]
+ CRUSH rule 1 x 726 [420,556,341,292]
+ CRUSH rule 1 x 727 [561,461,129,635]
+ CRUSH rule 1 x 728 [951,330,196,756]
+ CRUSH rule 1 x 729 [656,644,436,591]
+ CRUSH rule 1 x 730 [3,558,629,184]
+ CRUSH rule 1 x 731 [852,89,75,735]
+ CRUSH rule 1 x 732 [983,840,869,976]
+ CRUSH rule 1 x 733 [285,396,388,122]
+ CRUSH rule 1 x 734 [125,510,402,640]
+ CRUSH rule 1 x 735 [417,773,686,504]
+ CRUSH rule 1 x 736 [749,396,632,550]
+ CRUSH rule 1 x 737 [644,991,946,135]
+ CRUSH rule 1 x 738 [449,683,290,220]
+ CRUSH rule 1 x 739 [341,220,641,454]
+ CRUSH rule 1 x 740 [874,524,674,650]
+ CRUSH rule 1 x 741 [189,472,712,798]
+ CRUSH rule 1 x 742 [912,581,114,81]
+ CRUSH rule 1 x 743 [654,914,425,441]
+ CRUSH rule 1 x 744 [725,295,579,377]
+ CRUSH rule 1 x 745 [787,858,850,506]
+ CRUSH rule 1 x 746 [757,848,704,30]
+ CRUSH rule 1 x 747 [700,81,867,681]
+ CRUSH rule 1 x 748 [557,436,238,664]
+ CRUSH rule 1 x 749 [772,622,337,42]
+ CRUSH rule 1 x 750 [946,97,376,677]
+ CRUSH rule 1 x 751 [996,618,343,911]
+ CRUSH rule 1 x 752 [746,887,695,868]
+ CRUSH rule 1 x 753 [741,14,463,479]
+ CRUSH rule 1 x 754 [648,349,333,355]
+ CRUSH rule 1 x 755 [157,460,466,187]
+ CRUSH rule 1 x 756 [416,97,197,497]
+ CRUSH rule 1 x 757 [599,839,776,410]
+ CRUSH rule 1 x 758 [994,218,620,256]
+ CRUSH rule 1 x 759 [959,682,514,745]
+ CRUSH rule 1 x 760 [518,943,215,83]
+ CRUSH rule 1 x 761 [285,849,420,324]
+ CRUSH rule 1 x 762 [591,313,41,335]
+ CRUSH rule 1 x 763 [908,411,200,740]
+ CRUSH rule 1 x 764 [787,234,894,485]
+ CRUSH rule 1 x 765 [327,921,882,393]
+ CRUSH rule 1 x 766 [84,161,878,704]
+ CRUSH rule 1 x 767 [370,895,702,701]
+ CRUSH rule 1 x 768 [826,760,879,864]
+ CRUSH rule 1 x 769 [67,768,663,735]
+ CRUSH rule 1 x 770 [593,909,482,259]
+ CRUSH rule 1 x 771 [309,935,121,578]
+ CRUSH rule 1 x 772 [12,125,797,301]
+ CRUSH rule 1 x 773 [253,466,820,549]
+ CRUSH rule 1 x 774 [164,390,705,109]
+ CRUSH rule 1 x 775 [703,47,43,973]
+ CRUSH rule 1 x 776 [728,231,80,916]
+ CRUSH rule 1 x 777 [981,621,568,729]
+ CRUSH rule 1 x 778 [411,456,544,597]
+ CRUSH rule 1 x 779 [346,121,519,921]
+ CRUSH rule 1 x 780 [476,39,288,381]
+ CRUSH rule 1 x 781 [10,130,585,844]
+ CRUSH rule 1 x 782 [462,246,581,902]
+ CRUSH rule 1 x 783 [580,373,153,775]
+ CRUSH rule 1 x 784 [413,113,978,990]
+ CRUSH rule 1 x 785 [341,856,332,354]
+ CRUSH rule 1 x 786 [411,140,313,393]
+ CRUSH rule 1 x 787 [605,522,211,813]
+ CRUSH rule 1 x 788 [226,545,35,142]
+ CRUSH rule 1 x 789 [545,320,414,702]
+ CRUSH rule 1 x 790 [414,748,816,327]
+ CRUSH rule 1 x 791 [660,906,406,697]
+ CRUSH rule 1 x 792 [287,392,514,204]
+ CRUSH rule 1 x 793 [631,133,850,713]
+ CRUSH rule 1 x 794 [931,517,543,210]
+ CRUSH rule 1 x 795 [551,962,477,948]
+ CRUSH rule 1 x 796 [814,4,95,27]
+ CRUSH rule 1 x 797 [64,201,299,734]
+ CRUSH rule 1 x 798 [422,530,114,431]
+ CRUSH rule 1 x 799 [824,32,679,562]
+ CRUSH rule 1 x 800 [862,623,489,637]
+ CRUSH rule 1 x 801 [145,550,329,324]
+ CRUSH rule 1 x 802 [570,19,847,308]
+ CRUSH rule 1 x 803 [151,812,662,358]
+ CRUSH rule 1 x 804 [467,93,264,863]
+ CRUSH rule 1 x 805 [621,223,938,809]
+ CRUSH rule 1 x 806 [898,957,805,430]
+ CRUSH rule 1 x 807 [354,531,422,159]
+ CRUSH rule 1 x 808 [7,96,76,897]
+ CRUSH rule 1 x 809 [70,734,719,56]
+ CRUSH rule 1 x 810 [701,18,972,327]
+ CRUSH rule 1 x 811 [248,547,103,728]
+ CRUSH rule 1 x 812 [230,576,821,566]
+ CRUSH rule 1 x 813 [805,114,683,629]
+ CRUSH rule 1 x 814 [54,619,973,741]
+ CRUSH rule 1 x 815 [679,412,613,132]
+ CRUSH rule 1 x 816 [919,448,826,414]
+ CRUSH rule 1 x 817 [765,830,436,521]
+ CRUSH rule 1 x 818 [415,566,644,687]
+ CRUSH rule 1 x 819 [721,319,865,750]
+ CRUSH rule 1 x 820 [218,301,333,190]
+ CRUSH rule 1 x 821 [185,795,680,953]
+ CRUSH rule 1 x 822 [356,261,54,522]
+ CRUSH rule 1 x 823 [220,281,549,456]
+ CRUSH rule 1 x 824 [292,809,887,74]
+ CRUSH rule 1 x 825 [949,778,101,311]
+ CRUSH rule 1 x 826 [767,818,833,927]
+ CRUSH rule 1 x 827 [631,83,406,635]
+ CRUSH rule 1 x 828 [288,986,445,26]
+ CRUSH rule 1 x 829 [990,667,915,694]
+ CRUSH rule 1 x 830 [152,571,778,505]
+ CRUSH rule 1 x 831 [814,563,630,97]
+ CRUSH rule 1 x 832 [235,641,616,110]
+ CRUSH rule 1 x 833 [657,565,922,140]
+ CRUSH rule 1 x 834 [907,231,644,13]
+ CRUSH rule 1 x 835 [784,262,771,264]
+ CRUSH rule 1 x 836 [951,158,366,710]
+ CRUSH rule 1 x 837 [556,498,334,633]
+ CRUSH rule 1 x 838 [329,274,964,547]
+ CRUSH rule 1 x 839 [568,209,939,364]
+ CRUSH rule 1 x 840 [45,579,842,70]
+ CRUSH rule 1 x 841 [652,702,24,605]
+ CRUSH rule 1 x 842 [629,984,314,895]
+ CRUSH rule 1 x 843 [799,690,688,648]
+ CRUSH rule 1 x 844 [694,600,534,700]
+ CRUSH rule 1 x 845 [332,30,179,93]
+ CRUSH rule 1 x 846 [452,251,712,719]
+ CRUSH rule 1 x 847 [399,681,847,739]
+ CRUSH rule 1 x 848 [303,138,440,346]
+ CRUSH rule 1 x 849 [666,346,708,873]
+ CRUSH rule 1 x 850 [644,511,345,844]
+ CRUSH rule 1 x 851 [527,546,737,425]
+ CRUSH rule 1 x 852 [31,809,94,618]
+ CRUSH rule 1 x 853 [483,330,869,184]
+ CRUSH rule 1 x 854 [697,953,968,143]
+ CRUSH rule 1 x 855 [837,996,239,621]
+ CRUSH rule 1 x 856 [712,40,547,430]
+ CRUSH rule 1 x 857 [77,984,576,551]
+ CRUSH rule 1 x 858 [412,384,841,465]
+ CRUSH rule 1 x 859 [173,760,26,300]
+ CRUSH rule 1 x 860 [776,429,328,917]
+ CRUSH rule 1 x 861 [705,405,477,50]
+ CRUSH rule 1 x 862 [809,44,788,938]
+ CRUSH rule 1 x 863 [349,496,963,178]
+ CRUSH rule 1 x 864 [717,858,101,239]
+ CRUSH rule 1 x 865 [857,603,586,262]
+ CRUSH rule 1 x 866 [394,304,71,96]
+ CRUSH rule 1 x 867 [640,773,663,974]
+ CRUSH rule 1 x 868 [613,950,712,663]
+ CRUSH rule 1 x 869 [973,889,524,22]
+ CRUSH rule 1 x 870 [505,35,386,498]
+ CRUSH rule 1 x 871 [239,264,262,773]
+ CRUSH rule 1 x 872 [21,767,456,748]
+ CRUSH rule 1 x 873 [954,666,980,264]
+ CRUSH rule 1 x 874 [54,510,947,1]
+ CRUSH rule 1 x 875 [809,418,452,462]
+ CRUSH rule 1 x 876 [483,457,61,248]
+ CRUSH rule 1 x 877 [542,531,952,939]
+ CRUSH rule 1 x 878 [217,674,857,644]
+ CRUSH rule 1 x 879 [999,475,134,250]
+ CRUSH rule 1 x 880 [678,573,935,385]
+ CRUSH rule 1 x 881 [394,835,789,802]
+ CRUSH rule 1 x 882 [467,382,353,56]
+ CRUSH rule 1 x 883 [802,744,237,337]
+ CRUSH rule 1 x 884 [653,660,638,700]
+ CRUSH rule 1 x 885 [898,704,307,445]
+ CRUSH rule 1 x 886 [434,357,938,641]
+ CRUSH rule 1 x 887 [297,226,711,428]
+ CRUSH rule 1 x 888 [863,324,443,213]
+ CRUSH rule 1 x 889 [105,102,308,163]
+ CRUSH rule 1 x 890 [550,248,606,704]
+ CRUSH rule 1 x 891 [575,928,880,891]
+ CRUSH rule 1 x 892 [259,862,133,271]
+ CRUSH rule 1 x 893 [902,880,543,542]
+ CRUSH rule 1 x 894 [180,169,916,43]
+ CRUSH rule 1 x 895 [725,849,182,129]
+ CRUSH rule 1 x 896 [951,34,874,537]
+ CRUSH rule 1 x 897 [810,352,73,939]
+ CRUSH rule 1 x 898 [979,433,719,411]
+ CRUSH rule 1 x 899 [685,668,534,932]
+ CRUSH rule 1 x 900 [530,978,41,894]
+ CRUSH rule 1 x 901 [740,107,336,175]
+ CRUSH rule 1 x 902 [800,743,693,310]
+ CRUSH rule 1 x 903 [230,267,842,266]
+ CRUSH rule 1 x 904 [346,949,460,973]
+ CRUSH rule 1 x 905 [530,397,619,958]
+ CRUSH rule 1 x 906 [80,426,138,672]
+ CRUSH rule 1 x 907 [365,968,475,297]
+ CRUSH rule 1 x 908 [204,832,742,809]
+ CRUSH rule 1 x 909 [883,989,146,959]
+ CRUSH rule 1 x 910 [549,593,249,853]
+ CRUSH rule 1 x 911 [325,847,352,214]
+ CRUSH rule 1 x 912 [874,888,582,796]
+ CRUSH rule 1 x 913 [331,463,342,574]
+ CRUSH rule 1 x 914 [836,468,601,732]
+ CRUSH rule 1 x 915 [245,228,100,661]
+ CRUSH rule 1 x 916 [77,967,364,435]
+ CRUSH rule 1 x 917 [239,60,866,221]
+ CRUSH rule 1 x 918 [988,115,922,80]
+ CRUSH rule 1 x 919 [783,139,696,1]
+ CRUSH rule 1 x 920 [623,408,685,953]
+ CRUSH rule 1 x 921 [105,799,144,90]
+ CRUSH rule 1 x 922 [887,505,652,348]
+ CRUSH rule 1 x 923 [223,318,552,458]
+ CRUSH rule 1 x 924 [25,778,366,333]
+ CRUSH rule 1 x 925 [912,601,297,682]
+ CRUSH rule 1 x 926 [968,133,709,144]
+ CRUSH rule 1 x 927 [277,724,214,988]
+ CRUSH rule 1 x 928 [554,203,658,789]
+ CRUSH rule 1 x 929 [761,802,367,528]
+ CRUSH rule 1 x 930 [814,61,788,736]
+ CRUSH rule 1 x 931 [29,193,61,41]
+ CRUSH rule 1 x 932 [446,198,862,534]
+ CRUSH rule 1 x 933 [352,742,216,321]
+ CRUSH rule 1 x 934 [730,2,332,631]
+ CRUSH rule 1 x 935 [731,23,736,79]
+ CRUSH rule 1 x 936 [322,975,20,904]
+ CRUSH rule 1 x 937 [822,221,841,161]
+ CRUSH rule 1 x 938 [557,850,66,630]
+ CRUSH rule 1 x 939 [150,11,971,371]
+ CRUSH rule 1 x 940 [638,398,169,616]
+ CRUSH rule 1 x 941 [730,342,929,577]
+ CRUSH rule 1 x 942 [62,292,166,814]
+ CRUSH rule 1 x 943 [165,314,519,548]
+ CRUSH rule 1 x 944 [199,625,766,176]
+ CRUSH rule 1 x 945 [946,999,699,303]
+ CRUSH rule 1 x 946 [595,93,852,142]
+ CRUSH rule 1 x 947 [800,582,356,93]
+ CRUSH rule 1 x 948 [132,551,139,920]
+ CRUSH rule 1 x 949 [792,920,466,380]
+ CRUSH rule 1 x 950 [111,345,176,543]
+ CRUSH rule 1 x 951 [414,619,648,655]
+ CRUSH rule 1 x 952 [775,469,500,356]
+ CRUSH rule 1 x 953 [349,1,5,251]
+ CRUSH rule 1 x 954 [570,940,410,249]
+ CRUSH rule 1 x 955 [729,774,823,800]
+ CRUSH rule 1 x 956 [519,141,575,625]
+ CRUSH rule 1 x 957 [242,709,611,97]
+ CRUSH rule 1 x 958 [84,217,227,253]
+ CRUSH rule 1 x 959 [270,413,918,789]
+ CRUSH rule 1 x 960 [458,192,307,279]
+ CRUSH rule 1 x 961 [981,388,777,546]
+ CRUSH rule 1 x 962 [623,834,277,134]
+ CRUSH rule 1 x 963 [291,167,714,468]
+ CRUSH rule 1 x 964 [28,156,788,127]
+ CRUSH rule 1 x 965 [675,557,290,517]
+ CRUSH rule 1 x 966 [836,306,946,283]
+ CRUSH rule 1 x 967 [966,386,735,837]
+ CRUSH rule 1 x 968 [864,756,690,121]
+ CRUSH rule 1 x 969 [729,625,480,769]
+ CRUSH rule 1 x 970 [800,362,646,582]
+ CRUSH rule 1 x 971 [737,381,153,684]
+ CRUSH rule 1 x 972 [952,245,720,884]
+ CRUSH rule 1 x 973 [356,455,579,857]
+ CRUSH rule 1 x 974 [545,758,586,596]
+ CRUSH rule 1 x 975 [336,191,202,146]
+ CRUSH rule 1 x 976 [446,208,757,620]
+ CRUSH rule 1 x 977 [202,896,196,956]
+ CRUSH rule 1 x 978 [612,324,996,225]
+ CRUSH rule 1 x 979 [843,457,675,650]
+ CRUSH rule 1 x 980 [60,914,881,626]
+ CRUSH rule 1 x 981 [702,749,937,153]
+ CRUSH rule 1 x 982 [298,928,738,167]
+ CRUSH rule 1 x 983 [723,572,395,358]
+ CRUSH rule 1 x 984 [723,864,804,935]
+ CRUSH rule 1 x 985 [945,459,868,211]
+ CRUSH rule 1 x 986 [772,664,535,169]
+ CRUSH rule 1 x 987 [88,324,312,843]
+ CRUSH rule 1 x 988 [522,927,131,996]
+ CRUSH rule 1 x 989 [578,332,208,605]
+ CRUSH rule 1 x 990 [638,228,414,311]
+ CRUSH rule 1 x 991 [530,221,451,422]
+ CRUSH rule 1 x 992 [925,705,275,81]
+ CRUSH rule 1 x 993 [991,301,43,469]
+ CRUSH rule 1 x 994 [276,51,868,683]
+ CRUSH rule 1 x 995 [288,836,753,790]
+ CRUSH rule 1 x 996 [887,983,252,686]
+ CRUSH rule 1 x 997 [110,924,386,79]
+ CRUSH rule 1 x 998 [435,830,485,853]
+ CRUSH rule 1 x 999 [876,738,357,913]
+ CRUSH rule 1 x 1000 [178,963,638,430]
+ CRUSH rule 1 x 1001 [99,519,66,759]
+ CRUSH rule 1 x 1002 [515,534,468,866]
+ CRUSH rule 1 x 1003 [104,611,937,698]
+ CRUSH rule 1 x 1004 [269,638,724,375]
+ CRUSH rule 1 x 1005 [369,223,309,409]
+ CRUSH rule 1 x 1006 [40,107,69,275]
+ CRUSH rule 1 x 1007 [978,111,416,758]
+ CRUSH rule 1 x 1008 [965,956,624,832]
+ CRUSH rule 1 x 1009 [598,476,356,695]
+ CRUSH rule 1 x 1010 [767,523,239,517]
+ CRUSH rule 1 x 1011 [289,871,207,576]
+ CRUSH rule 1 x 1012 [128,28,370,31]
+ CRUSH rule 1 x 1013 [979,765,660,812]
+ CRUSH rule 1 x 1014 [979,948,513,88]
+ CRUSH rule 1 x 1015 [277,790,396,672]
+ CRUSH rule 1 x 1016 [262,73,128,886]
+ CRUSH rule 1 x 1017 [150,269,61,499]
+ CRUSH rule 1 x 1018 [555,829,554,944]
+ CRUSH rule 1 x 1019 [513,356,265,446]
+ CRUSH rule 1 x 1020 [158,161,877,704]
+ CRUSH rule 1 x 1021 [915,998,957,285]
+ CRUSH rule 1 x 1022 [967,829,973,640]
+ CRUSH rule 1 x 1023 [488,257,614,859]
+ rule 1 (metadata) num_rep 4 result size == 4:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536,450,604]
+ CRUSH rule 1 x 1 [876,250,334,633,744]
+ CRUSH rule 1 x 2 [292,832,53,392,386]
+ CRUSH rule 1 x 3 [623,387,124,998,749]
+ CRUSH rule 1 x 4 [61,334,710,4,994]
+ CRUSH rule 1 x 5 [946,557,713,664,141]
+ CRUSH rule 1 x 6 [576,668,212,163,732]
+ CRUSH rule 1 x 7 [645,753,906,393,341]
+ CRUSH rule 1 x 8 [243,6,863,781,211]
+ CRUSH rule 1 x 9 [22,578,251,410,297]
+ CRUSH rule 1 x 10 [758,828,360,477,821]
+ CRUSH rule 1 x 11 [769,120,124,527,119]
+ CRUSH rule 1 x 12 [780,364,689,755,675]
+ CRUSH rule 1 x 13 [557,18,351,719,742]
+ CRUSH rule 1 x 14 [59,561,249,461,971]
+ CRUSH rule 1 x 15 [718,928,993,21,76]
+ CRUSH rule 1 x 16 [673,632,841,954,788]
+ CRUSH rule 1 x 17 [648,43,560,514,142]
+ CRUSH rule 1 x 18 [654,219,181,568,381]
+ CRUSH rule 1 x 19 [850,545,377,848,863]
+ CRUSH rule 1 x 20 [717,785,974,5,225]
+ CRUSH rule 1 x 21 [420,57,519,306,312]
+ CRUSH rule 1 x 22 [503,998,193,821,634]
+ CRUSH rule 1 x 23 [411,663,168,110,899]
+ CRUSH rule 1 x 24 [266,861,353,1,456]
+ CRUSH rule 1 x 25 [760,483,818,600,509]
+ CRUSH rule 1 x 26 [903,24,573,718,112]
+ CRUSH rule 1 x 27 [946,188,289,510,687]
+ CRUSH rule 1 x 28 [69,312,73,198,256]
+ CRUSH rule 1 x 29 [844,883,337,628,496]
+ CRUSH rule 1 x 30 [621,18,613,794,910]
+ CRUSH rule 1 x 31 [784,943,814,539,962]
+ CRUSH rule 1 x 32 [173,374,369,972,315]
+ CRUSH rule 1 x 33 [698,336,357,966,582]
+ CRUSH rule 1 x 34 [168,836,210,798,904]
+ CRUSH rule 1 x 35 [274,509,534,818,912]
+ CRUSH rule 1 x 36 [318,215,153,628,87]
+ CRUSH rule 1 x 37 [173,604,109,935,203]
+ CRUSH rule 1 x 38 [708,444,683,604,722]
+ CRUSH rule 1 x 39 [662,198,417,680,226]
+ CRUSH rule 1 x 40 [620,801,414,78,560]
+ CRUSH rule 1 x 41 [811,264,177,127,148]
+ CRUSH rule 1 x 42 [863,179,527,660,133]
+ CRUSH rule 1 x 43 [686,822,988,228,791]
+ CRUSH rule 1 x 44 [396,222,46,841,536]
+ CRUSH rule 1 x 45 [991,694,253,142,54]
+ CRUSH rule 1 x 46 [420,909,184,285,508]
+ CRUSH rule 1 x 47 [467,211,605,207,241]
+ CRUSH rule 1 x 48 [955,329,368,168,698]
+ CRUSH rule 1 x 49 [974,891,931,29,813]
+ CRUSH rule 1 x 50 [870,441,691,823,761]
+ CRUSH rule 1 x 51 [182,930,25,936,97]
+ CRUSH rule 1 x 52 [704,812,894,794,481]
+ CRUSH rule 1 x 53 [185,713,631,280,345]
+ CRUSH rule 1 x 54 [270,441,100,82,983]
+ CRUSH rule 1 x 55 [895,734,958,793,651]
+ CRUSH rule 1 x 56 [564,963,683,324,40]
+ CRUSH rule 1 x 57 [738,130,208,973,498]
+ CRUSH rule 1 x 58 [524,113,806,903,531]
+ CRUSH rule 1 x 59 [408,337,668,529,34]
+ CRUSH rule 1 x 60 [228,790,857,309,616]
+ CRUSH rule 1 x 61 [154,843,717,467,883]
+ CRUSH rule 1 x 62 [594,811,549,276,693]
+ CRUSH rule 1 x 63 [646,67,884,925,941]
+ CRUSH rule 1 x 64 [175,542,155,837,594]
+ CRUSH rule 1 x 65 [745,619,131,867,269]
+ CRUSH rule 1 x 66 [275,468,23,35,328]
+ CRUSH rule 1 x 67 [246,958,524,493,636]
+ CRUSH rule 1 x 68 [711,473,403,228,835]
+ CRUSH rule 1 x 69 [493,924,850,939,950]
+ CRUSH rule 1 x 70 [30,499,644,33,804]
+ CRUSH rule 1 x 71 [984,883,574,716,575]
+ CRUSH rule 1 x 72 [71,286,942,363,628]
+ CRUSH rule 1 x 73 [922,618,3,371,464]
+ CRUSH rule 1 x 74 [629,414,185,573,678]
+ CRUSH rule 1 x 75 [222,20,174,820,312]
+ CRUSH rule 1 x 76 [262,366,339,290,718]
+ CRUSH rule 1 x 77 [638,469,992,280,773]
+ CRUSH rule 1 x 78 [324,511,788,7,308]
+ CRUSH rule 1 x 79 [577,990,64,94,447]
+ CRUSH rule 1 x 80 [501,95,278,903,631]
+ CRUSH rule 1 x 81 [506,812,9,698,173]
+ CRUSH rule 1 x 82 [222,145,80,785,835]
+ CRUSH rule 1 x 83 [71,634,61,91,856]
+ CRUSH rule 1 x 84 [49,761,773,368,318]
+ CRUSH rule 1 x 85 [985,896,708,861,325]
+ CRUSH rule 1 x 86 [537,745,93,524,466]
+ CRUSH rule 1 x 87 [997,317,463,626,685]
+ CRUSH rule 1 x 88 [957,350,890,857,375]
+ CRUSH rule 1 x 89 [399,730,148,314,159]
+ CRUSH rule 1 x 90 [943,706,683,267,579]
+ CRUSH rule 1 x 91 [22,368,149,928,140]
+ CRUSH rule 1 x 92 [532,424,426,773,623]
+ CRUSH rule 1 x 93 [218,489,405,681,549]
+ CRUSH rule 1 x 94 [181,96,102,515,776]
+ CRUSH rule 1 x 95 [343,957,820,139,334]
+ CRUSH rule 1 x 96 [861,270,87,797,0]
+ CRUSH rule 1 x 97 [459,706,45,328,274]
+ CRUSH rule 1 x 98 [327,867,353,948,728]
+ CRUSH rule 1 x 99 [974,133,468,906,235]
+ CRUSH rule 1 x 100 [32,445,547,371,960]
+ CRUSH rule 1 x 101 [142,90,337,950,970]
+ CRUSH rule 1 x 102 [172,129,139,22,403]
+ CRUSH rule 1 x 103 [630,47,161,356,911]
+ CRUSH rule 1 x 104 [758,133,278,11,947]
+ CRUSH rule 1 x 105 [843,604,47,33,401]
+ CRUSH rule 1 x 106 [28,681,193,679,990]
+ CRUSH rule 1 x 107 [74,320,85,819,315]
+ CRUSH rule 1 x 108 [875,593,575,517,107]
+ CRUSH rule 1 x 109 [411,985,811,720,198]
+ CRUSH rule 1 x 110 [440,774,799,660,715]
+ CRUSH rule 1 x 111 [405,742,276,359,936]
+ CRUSH rule 1 x 112 [143,181,922,545,185]
+ CRUSH rule 1 x 113 [153,846,160,903,789]
+ CRUSH rule 1 x 114 [804,892,939,20,312]
+ CRUSH rule 1 x 115 [588,508,958,580,232]
+ CRUSH rule 1 x 116 [327,148,637,486,712]
+ CRUSH rule 1 x 117 [95,594,989,131,714]
+ CRUSH rule 1 x 118 [80,957,897,239,359]
+ CRUSH rule 1 x 119 [386,932,951,768,679]
+ CRUSH rule 1 x 120 [366,312,653,936,71]
+ CRUSH rule 1 x 121 [129,154,847,16,471]
+ CRUSH rule 1 x 122 [873,1,110,939,90]
+ CRUSH rule 1 x 123 [533,415,789,600,713]
+ CRUSH rule 1 x 124 [461,691,898,723,957]
+ CRUSH rule 1 x 125 [342,599,830,402,615]
+ CRUSH rule 1 x 126 [819,781,822,548,279]
+ CRUSH rule 1 x 127 [437,893,585,707,353]
+ CRUSH rule 1 x 128 [679,994,982,550,991]
+ CRUSH rule 1 x 129 [380,685,947,302,698]
+ CRUSH rule 1 x 130 [992,52,466,867,998]
+ CRUSH rule 1 x 131 [469,90,208,599,829]
+ CRUSH rule 1 x 132 [571,250,316,535,54]
+ CRUSH rule 1 x 133 [964,728,329,902,108]
+ CRUSH rule 1 x 134 [999,19,716,963,323]
+ CRUSH rule 1 x 135 [634,101,52,938,413]
+ CRUSH rule 1 x 136 [114,889,692,768,694]
+ CRUSH rule 1 x 137 [839,8,959,280,922]
+ CRUSH rule 1 x 138 [967,949,138,451,292]
+ CRUSH rule 1 x 139 [308,711,736,247,632]
+ CRUSH rule 1 x 140 [764,936,926,55,331]
+ CRUSH rule 1 x 141 [423,302,112,216,603]
+ CRUSH rule 1 x 142 [252,821,715,340,635]
+ CRUSH rule 1 x 143 [33,808,518,477,325]
+ CRUSH rule 1 x 144 [472,88,969,162,401]
+ CRUSH rule 1 x 145 [242,208,252,604,266]
+ CRUSH rule 1 x 146 [290,70,570,384,934]
+ CRUSH rule 1 x 147 [447,352,657,493,467]
+ CRUSH rule 1 x 148 [212,644,432,658,109]
+ CRUSH rule 1 x 149 [9,775,87,35,260]
+ CRUSH rule 1 x 150 [166,456,582,144,324]
+ CRUSH rule 1 x 151 [811,875,307,20,782]
+ CRUSH rule 1 x 152 [449,617,223,9,182]
+ CRUSH rule 1 x 153 [523,537,695,627,959]
+ CRUSH rule 1 x 154 [208,559,874,597,243]
+ CRUSH rule 1 x 155 [569,325,192,296,367]
+ CRUSH rule 1 x 156 [488,121,521,213,595]
+ CRUSH rule 1 x 157 [140,723,633,260,487]
+ CRUSH rule 1 x 158 [786,451,320,239,667]
+ CRUSH rule 1 x 159 [134,664,517,821,667]
+ CRUSH rule 1 x 160 [690,112,414,990,183]
+ CRUSH rule 1 x 161 [324,912,397,423,991]
+ CRUSH rule 1 x 162 [748,567,284,183,463]
+ CRUSH rule 1 x 163 [575,499,31,816,749]
+ CRUSH rule 1 x 164 [314,489,308,326,51]
+ CRUSH rule 1 x 165 [116,209,750,53,813]
+ CRUSH rule 1 x 166 [352,706,701,810,718]
+ CRUSH rule 1 x 167 [27,743,174,142,551]
+ CRUSH rule 1 x 168 [953,898,880,660,500]
+ CRUSH rule 1 x 169 [912,147,266,547,331]
+ CRUSH rule 1 x 170 [421,515,828,844,151]
+ CRUSH rule 1 x 171 [488,584,880,964,936]
+ CRUSH rule 1 x 172 [366,443,957,66,162]
+ CRUSH rule 1 x 173 [863,291,625,287,158]
+ CRUSH rule 1 x 174 [263,555,650,410,339]
+ CRUSH rule 1 x 175 [875,961,361,575,33]
+ CRUSH rule 1 x 176 [745,83,701,680,250]
+ CRUSH rule 1 x 177 [128,244,41,123,422]
+ CRUSH rule 1 x 178 [155,41,264,777,314]
+ CRUSH rule 1 x 179 [593,833,202,183,971]
+ CRUSH rule 1 x 180 [154,734,17,831,824]
+ CRUSH rule 1 x 181 [289,675,723,800,166]
+ CRUSH rule 1 x 182 [730,931,560,209,943]
+ CRUSH rule 1 x 183 [639,237,794,815,827]
+ CRUSH rule 1 x 184 [704,312,685,645,691]
+ CRUSH rule 1 x 185 [97,100,762,82,999]
+ CRUSH rule 1 x 186 [26,665,554,215,280]
+ CRUSH rule 1 x 187 [649,14,740,494,402]
+ CRUSH rule 1 x 188 [682,695,590,743,927]
+ CRUSH rule 1 x 189 [325,693,726,51,448]
+ CRUSH rule 1 x 190 [399,933,136,955,57]
+ CRUSH rule 1 x 191 [629,533,17,126,60]
+ CRUSH rule 1 x 192 [503,578,38,492,222]
+ CRUSH rule 1 x 193 [546,333,651,678,823]
+ CRUSH rule 1 x 194 [242,473,58,655,449]
+ CRUSH rule 1 x 195 [625,719,135,81,636]
+ CRUSH rule 1 x 196 [357,114,125,867,250]
+ CRUSH rule 1 x 197 [306,954,453,873,211]
+ CRUSH rule 1 x 198 [863,791,311,911,206]
+ CRUSH rule 1 x 199 [935,906,929,252,893]
+ CRUSH rule 1 x 200 [373,774,229,454,909]
+ CRUSH rule 1 x 201 [659,320,477,313,779]
+ CRUSH rule 1 x 202 [260,433,524,880,223]
+ CRUSH rule 1 x 203 [36,239,675,971,703]
+ CRUSH rule 1 x 204 [92,516,993,728,279]
+ CRUSH rule 1 x 205 [68,395,473,45,683]
+ CRUSH rule 1 x 206 [570,530,642,380,311]
+ CRUSH rule 1 x 207 [834,457,850,917,456]
+ CRUSH rule 1 x 208 [927,484,640,976,803]
+ CRUSH rule 1 x 209 [878,66,58,940,48]
+ CRUSH rule 1 x 210 [572,981,484,29,0]
+ CRUSH rule 1 x 211 [107,597,780,857,895]
+ CRUSH rule 1 x 212 [389,107,838,624,698]
+ CRUSH rule 1 x 213 [497,717,567,728,905]
+ CRUSH rule 1 x 214 [798,65,254,572,32]
+ CRUSH rule 1 x 215 [233,419,283,638,520]
+ CRUSH rule 1 x 216 [494,464,742,523,459]
+ CRUSH rule 1 x 217 [352,396,309,938,66]
+ CRUSH rule 1 x 218 [895,864,988,650,593]
+ CRUSH rule 1 x 219 [222,534,277,242,658]
+ CRUSH rule 1 x 220 [281,19,584,563,858]
+ CRUSH rule 1 x 221 [64,928,963,130,312]
+ CRUSH rule 1 x 222 [40,544,161,199,861]
+ CRUSH rule 1 x 223 [645,556,159,417,46]
+ CRUSH rule 1 x 224 [647,165,957,263,961]
+ CRUSH rule 1 x 225 [219,714,858,747,461]
+ CRUSH rule 1 x 226 [372,511,181,277,695]
+ CRUSH rule 1 x 227 [925,156,714,863,257]
+ CRUSH rule 1 x 228 [682,404,839,263,521]
+ CRUSH rule 1 x 229 [880,838,770,891,236]
+ CRUSH rule 1 x 230 [328,659,916,468,646]
+ CRUSH rule 1 x 231 [320,383,669,109,627]
+ CRUSH rule 1 x 232 [924,846,394,319,43]
+ CRUSH rule 1 x 233 [948,652,575,838,498]
+ CRUSH rule 1 x 234 [484,943,42,575,936]
+ CRUSH rule 1 x 235 [750,65,590,168,870]
+ CRUSH rule 1 x 236 [551,787,490,136,370]
+ CRUSH rule 1 x 237 [390,157,166,251,752]
+ CRUSH rule 1 x 238 [570,6,989,707,514]
+ CRUSH rule 1 x 239 [729,959,376,975,496]
+ CRUSH rule 1 x 240 [981,241,156,767,631]
+ CRUSH rule 1 x 241 [310,816,641,177,996]
+ CRUSH rule 1 x 242 [161,63,642,837,763]
+ CRUSH rule 1 x 243 [180,394,33,683,189]
+ CRUSH rule 1 x 244 [52,174,685,189,78]
+ CRUSH rule 1 x 245 [523,121,915,84,386]
+ CRUSH rule 1 x 246 [362,893,390,487,817]
+ CRUSH rule 1 x 247 [382,184,116,34,143]
+ CRUSH rule 1 x 248 [129,114,852,469,359]
+ CRUSH rule 1 x 249 [159,683,91,856,475]
+ CRUSH rule 1 x 250 [404,945,569,955,228]
+ CRUSH rule 1 x 251 [661,225,738,757,37]
+ CRUSH rule 1 x 252 [961,226,542,103,945]
+ CRUSH rule 1 x 253 [651,97,225,364,189]
+ CRUSH rule 1 x 254 [123,33,741,692,599]
+ CRUSH rule 1 x 255 [314,649,891,855,517]
+ CRUSH rule 1 x 256 [315,215,651,126,470]
+ CRUSH rule 1 x 257 [825,264,867,224,529]
+ CRUSH rule 1 x 258 [624,789,370,723,131]
+ CRUSH rule 1 x 259 [602,542,70,563,947]
+ CRUSH rule 1 x 260 [717,878,43,56,377]
+ CRUSH rule 1 x 261 [145,517,20,903,786]
+ CRUSH rule 1 x 262 [223,1,561,420,498]
+ CRUSH rule 1 x 263 [462,211,405,508,787]
+ CRUSH rule 1 x 264 [654,471,266,662,135]
+ CRUSH rule 1 x 265 [302,794,704,798,659]
+ CRUSH rule 1 x 266 [202,132,884,209,551]
+ CRUSH rule 1 x 267 [282,938,657,113,672]
+ CRUSH rule 1 x 268 [338,309,356,278,928]
+ CRUSH rule 1 x 269 [738,122,266,200,894]
+ CRUSH rule 1 x 270 [707,982,946,196,407]
+ CRUSH rule 1 x 271 [705,432,364,735,512]
+ CRUSH rule 1 x 272 [756,545,942,56,542]
+ CRUSH rule 1 x 273 [197,502,527,721,239]
+ CRUSH rule 1 x 274 [992,44,653,573,527]
+ CRUSH rule 1 x 275 [544,789,170,434,23]
+ CRUSH rule 1 x 276 [658,467,577,268,336]
+ CRUSH rule 1 x 277 [143,490,880,483,928]
+ CRUSH rule 1 x 278 [492,647,355,282,834]
+ CRUSH rule 1 x 279 [517,792,604,987,527]
+ CRUSH rule 1 x 280 [825,740,27,848,514]
+ CRUSH rule 1 x 281 [224,629,120,562,616]
+ CRUSH rule 1 x 282 [298,661,380,416,35]
+ CRUSH rule 1 x 283 [311,606,208,50,913]
+ CRUSH rule 1 x 284 [771,466,371,743,672]
+ CRUSH rule 1 x 285 [693,362,404,676,797]
+ CRUSH rule 1 x 286 [364,477,285,167,270]
+ CRUSH rule 1 x 287 [591,611,828,995,170]
+ CRUSH rule 1 x 288 [965,541,848,796,251]
+ CRUSH rule 1 x 289 [225,551,948,877,219]
+ CRUSH rule 1 x 290 [577,762,777,751,291]
+ CRUSH rule 1 x 291 [160,903,477,381,490]
+ CRUSH rule 1 x 292 [873,598,216,666,222]
+ CRUSH rule 1 x 293 [100,234,874,47,28]
+ CRUSH rule 1 x 294 [285,943,379,520,725]
+ CRUSH rule 1 x 295 [938,262,880,327,687]
+ CRUSH rule 1 x 296 [850,327,86,472,1]
+ CRUSH rule 1 x 297 [951,53,99,558,753]
+ CRUSH rule 1 x 298 [173,336,85,766,910]
+ CRUSH rule 1 x 299 [598,591,315,386,895]
+ CRUSH rule 1 x 300 [531,957,62,459,156]
+ CRUSH rule 1 x 301 [823,628,23,858,629]
+ CRUSH rule 1 x 302 [184,80,780,871,531]
+ CRUSH rule 1 x 303 [521,766,222,830,988]
+ CRUSH rule 1 x 304 [980,127,807,507,555]
+ CRUSH rule 1 x 305 [153,816,22,927,696]
+ CRUSH rule 1 x 306 [423,739,664,753,178]
+ CRUSH rule 1 x 307 [997,557,682,456,479]
+ CRUSH rule 1 x 308 [991,874,534,465,330]
+ CRUSH rule 1 x 309 [860,394,724,858,246]
+ CRUSH rule 1 x 310 [589,818,546,201,94]
+ CRUSH rule 1 x 311 [477,774,225,590,830]
+ CRUSH rule 1 x 312 [887,853,950,354,58]
+ CRUSH rule 1 x 313 [802,646,447,416,557]
+ CRUSH rule 1 x 314 [654,974,229,511,562]
+ CRUSH rule 1 x 315 [767,227,28,740,828]
+ CRUSH rule 1 x 316 [778,83,733,359,858]
+ CRUSH rule 1 x 317 [184,418,642,986,939]
+ CRUSH rule 1 x 318 [525,410,500,543,212]
+ CRUSH rule 1 x 319 [476,724,569,382,409]
+ CRUSH rule 1 x 320 [149,610,697,296,818]
+ CRUSH rule 1 x 321 [710,79,667,671,234]
+ CRUSH rule 1 x 322 [175,275,323,333,744]
+ CRUSH rule 1 x 323 [819,604,638,792,316]
+ CRUSH rule 1 x 324 [16,745,511,439,272]
+ CRUSH rule 1 x 325 [486,400,872,873,251]
+ CRUSH rule 1 x 326 [613,765,207,19,359]
+ CRUSH rule 1 x 327 [125,289,738,408,456]
+ CRUSH rule 1 x 328 [807,383,476,583,645]
+ CRUSH rule 1 x 329 [588,938,599,432,446]
+ CRUSH rule 1 x 330 [932,644,41,611,209]
+ CRUSH rule 1 x 331 [341,953,950,537,578]
+ CRUSH rule 1 x 332 [153,726,459,950,466]
+ CRUSH rule 1 x 333 [745,845,853,860,52]
+ CRUSH rule 1 x 334 [614,751,807,58,396]
+ CRUSH rule 1 x 335 [518,721,221,283,454]
+ CRUSH rule 1 x 336 [389,424,77,309,5]
+ CRUSH rule 1 x 337 [753,508,765,720,221]
+ CRUSH rule 1 x 338 [128,810,490,753,406]
+ CRUSH rule 1 x 339 [430,308,58,751,856]
+ CRUSH rule 1 x 340 [541,44,630,231,289]
+ CRUSH rule 1 x 341 [402,26,631,439,165]
+ CRUSH rule 1 x 342 [982,57,992,461,131]
+ CRUSH rule 1 x 343 [833,412,572,732,107]
+ CRUSH rule 1 x 344 [784,533,792,41,642]
+ CRUSH rule 1 x 345 [546,300,304,691,763]
+ CRUSH rule 1 x 346 [302,420,428,891,357]
+ CRUSH rule 1 x 347 [488,778,101,217,366]
+ CRUSH rule 1 x 348 [903,744,937,718,85]
+ CRUSH rule 1 x 349 [471,547,582,306,600]
+ CRUSH rule 1 x 350 [348,221,823,335,383]
+ CRUSH rule 1 x 351 [961,582,705,346,361]
+ CRUSH rule 1 x 352 [728,137,461,298,36]
+ CRUSH rule 1 x 353 [904,202,184,447,58]
+ CRUSH rule 1 x 354 [345,226,319,256,544]
+ CRUSH rule 1 x 355 [50,430,175,43,187]
+ CRUSH rule 1 x 356 [87,185,55,423,829]
+ CRUSH rule 1 x 357 [762,459,921,473,182]
+ CRUSH rule 1 x 358 [908,25,280,6,808]
+ CRUSH rule 1 x 359 [484,15,132,121,394]
+ CRUSH rule 1 x 360 [173,378,337,702,145]
+ CRUSH rule 1 x 361 [404,577,115,25,56]
+ CRUSH rule 1 x 362 [403,1,422,945,132]
+ CRUSH rule 1 x 363 [639,911,510,162,418]
+ CRUSH rule 1 x 364 [752,689,610,990,665]
+ CRUSH rule 1 x 365 [956,999,212,230,624]
+ CRUSH rule 1 x 366 [860,925,924,763,687]
+ CRUSH rule 1 x 367 [205,609,647,665,969]
+ CRUSH rule 1 x 368 [301,284,810,169,78]
+ CRUSH rule 1 x 369 [452,658,339,217,674]
+ CRUSH rule 1 x 370 [11,467,695,989,394]
+ CRUSH rule 1 x 371 [124,487,55,514,313]
+ CRUSH rule 1 x 372 [253,48,979,846,207]
+ CRUSH rule 1 x 373 [715,605,775,748,227]
+ CRUSH rule 1 x 374 [191,887,920,571,223]
+ CRUSH rule 1 x 375 [711,385,651,665,15]
+ CRUSH rule 1 x 376 [597,818,49,458,415]
+ CRUSH rule 1 x 377 [294,256,933,771,184]
+ CRUSH rule 1 x 378 [34,151,681,707,552]
+ CRUSH rule 1 x 379 [869,136,315,378,813]
+ CRUSH rule 1 x 380 [294,97,575,791,690]
+ CRUSH rule 1 x 381 [119,710,219,827,328]
+ CRUSH rule 1 x 382 [69,631,508,706,697]
+ CRUSH rule 1 x 383 [922,588,589,925,471]
+ CRUSH rule 1 x 384 [221,945,671,117,857]
+ CRUSH rule 1 x 385 [561,737,953,723,658]
+ CRUSH rule 1 x 386 [335,442,788,696,507]
+ CRUSH rule 1 x 387 [514,43,353,88,100]
+ CRUSH rule 1 x 388 [587,89,157,996,915]
+ CRUSH rule 1 x 389 [109,641,255,466,372]
+ CRUSH rule 1 x 390 [925,149,421,489,599]
+ CRUSH rule 1 x 391 [267,87,387,527,768]
+ CRUSH rule 1 x 392 [382,485,370,849,936]
+ CRUSH rule 1 x 393 [425,721,221,753,268]
+ CRUSH rule 1 x 394 [898,18,38,793,173]
+ CRUSH rule 1 x 395 [806,876,269,679,32]
+ CRUSH rule 1 x 396 [790,970,437,449,875]
+ CRUSH rule 1 x 397 [136,363,507,613,11]
+ CRUSH rule 1 x 398 [914,116,558,258,722]
+ CRUSH rule 1 x 399 [261,94,299,202,174]
+ CRUSH rule 1 x 400 [661,197,338,461,977]
+ CRUSH rule 1 x 401 [953,979,287,803,41]
+ CRUSH rule 1 x 402 [738,819,618,522,667]
+ CRUSH rule 1 x 403 [573,238,425,546,130]
+ CRUSH rule 1 x 404 [526,848,790,253,922]
+ CRUSH rule 1 x 405 [582,505,330,334,201]
+ CRUSH rule 1 x 406 [768,324,493,60,186]
+ CRUSH rule 1 x 407 [260,951,437,587,692]
+ CRUSH rule 1 x 408 [657,81,770,734,830]
+ CRUSH rule 1 x 409 [498,89,182,423,672]
+ CRUSH rule 1 x 410 [28,793,737,352,166]
+ CRUSH rule 1 x 411 [684,992,60,659,769]
+ CRUSH rule 1 x 412 [261,958,699,950,165]
+ CRUSH rule 1 x 413 [891,835,297,441,384]
+ CRUSH rule 1 x 414 [127,459,119,965,662]
+ CRUSH rule 1 x 415 [272,540,631,328,609]
+ CRUSH rule 1 x 416 [739,617,115,530,339]
+ CRUSH rule 1 x 417 [106,209,157,878,117]
+ CRUSH rule 1 x 418 [525,441,147,390,320]
+ CRUSH rule 1 x 419 [603,673,615,465,266]
+ CRUSH rule 1 x 420 [988,213,251,226,209]
+ CRUSH rule 1 x 421 [761,521,748,368,923]
+ CRUSH rule 1 x 422 [317,160,924,548,198]
+ CRUSH rule 1 x 423 [137,807,168,472,619]
+ CRUSH rule 1 x 424 [920,37,146,263,598]
+ CRUSH rule 1 x 425 [277,693,285,221,478]
+ CRUSH rule 1 x 426 [485,936,407,854,726]
+ CRUSH rule 1 x 427 [242,515,9,564,174]
+ CRUSH rule 1 x 428 [632,635,26,473,494]
+ CRUSH rule 1 x 429 [641,73,465,127,171]
+ CRUSH rule 1 x 430 [626,585,6,387,881]
+ CRUSH rule 1 x 431 [697,76,753,570,964]
+ CRUSH rule 1 x 432 [590,526,306,283,656]
+ CRUSH rule 1 x 433 [284,387,149,817,886]
+ CRUSH rule 1 x 434 [538,985,79,953,770]
+ CRUSH rule 1 x 435 [30,318,593,635,975]
+ CRUSH rule 1 x 436 [164,919,851,693,0]
+ CRUSH rule 1 x 437 [322,212,163,606,302]
+ CRUSH rule 1 x 438 [142,392,85,594,376]
+ CRUSH rule 1 x 439 [119,370,68,443,997]
+ CRUSH rule 1 x 440 [333,403,187,863,475]
+ CRUSH rule 1 x 441 [477,727,906,145,429]
+ CRUSH rule 1 x 442 [274,590,933,244,434]
+ CRUSH rule 1 x 443 [983,748,574,718,700]
+ CRUSH rule 1 x 444 [536,509,431,146,170]
+ CRUSH rule 1 x 445 [485,554,528,209,964]
+ CRUSH rule 1 x 446 [345,634,42,294,711]
+ CRUSH rule 1 x 447 [61,845,767,600,321]
+ CRUSH rule 1 x 448 [333,232,292,846,364]
+ CRUSH rule 1 x 449 [680,16,484,670,851]
+ CRUSH rule 1 x 450 [235,214,79,423,96]
+ CRUSH rule 1 x 451 [961,468,333,640,823]
+ CRUSH rule 1 x 452 [525,479,153,528,570]
+ CRUSH rule 1 x 453 [138,466,302,86,249]
+ CRUSH rule 1 x 454 [137,625,215,402,389]
+ CRUSH rule 1 x 455 [173,150,997,16,846]
+ CRUSH rule 1 x 456 [235,226,238,258,347]
+ CRUSH rule 1 x 457 [450,577,253,413,717]
+ CRUSH rule 1 x 458 [195,537,91,814,351]
+ CRUSH rule 1 x 459 [381,555,312,573,915]
+ CRUSH rule 1 x 460 [972,730,534,678,756]
+ CRUSH rule 1 x 461 [506,279,142,830,784]
+ CRUSH rule 1 x 462 [692,959,578,57,983]
+ CRUSH rule 1 x 463 [788,667,949,550,685]
+ CRUSH rule 1 x 464 [133,122,588,999,270]
+ CRUSH rule 1 x 465 [971,190,230,777,452]
+ CRUSH rule 1 x 466 [394,576,148,157,103]
+ CRUSH rule 1 x 467 [517,28,366,362,984]
+ CRUSH rule 1 x 468 [829,143,874,225,162]
+ CRUSH rule 1 x 469 [987,936,106,725,633]
+ CRUSH rule 1 x 470 [107,982,56,889,67]
+ CRUSH rule 1 x 471 [181,897,629,860,307]
+ CRUSH rule 1 x 472 [547,512,172,24,705]
+ CRUSH rule 1 x 473 [760,997,824,905,888]
+ CRUSH rule 1 x 474 [787,418,743,628,272]
+ CRUSH rule 1 x 475 [662,312,253,617,105]
+ CRUSH rule 1 x 476 [110,495,185,508,961]
+ CRUSH rule 1 x 477 [393,954,834,132,841]
+ CRUSH rule 1 x 478 [246,483,480,644,985]
+ CRUSH rule 1 x 479 [70,929,697,931,744]
+ CRUSH rule 1 x 480 [753,119,961,607,317]
+ CRUSH rule 1 x 481 [470,429,677,242,574]
+ CRUSH rule 1 x 482 [451,566,961,675,354]
+ CRUSH rule 1 x 483 [816,72,371,278,635]
+ CRUSH rule 1 x 484 [540,454,389,31,654]
+ CRUSH rule 1 x 485 [74,582,624,684,566]
+ CRUSH rule 1 x 486 [958,595,199,763,715]
+ CRUSH rule 1 x 487 [228,302,804,833,876]
+ CRUSH rule 1 x 488 [180,529,722,956,353]
+ CRUSH rule 1 x 489 [47,617,812,187,291]
+ CRUSH rule 1 x 490 [905,822,479,124,750]
+ CRUSH rule 1 x 491 [892,370,609,998,433]
+ CRUSH rule 1 x 492 [588,959,127,948,505]
+ CRUSH rule 1 x 493 [353,461,593,291,301]
+ CRUSH rule 1 x 494 [378,848,443,368,507]
+ CRUSH rule 1 x 495 [845,653,768,234,405]
+ CRUSH rule 1 x 496 [13,988,0,691,389]
+ CRUSH rule 1 x 497 [796,877,788,394,648]
+ CRUSH rule 1 x 498 [412,337,270,705,511]
+ CRUSH rule 1 x 499 [330,695,8,74,618]
+ CRUSH rule 1 x 500 [820,272,547,765,755]
+ CRUSH rule 1 x 501 [110,44,132,442,294]
+ CRUSH rule 1 x 502 [336,595,650,274,993]
+ CRUSH rule 1 x 503 [922,211,157,722,502]
+ CRUSH rule 1 x 504 [483,52,122,432,778]
+ CRUSH rule 1 x 505 [482,598,224,279,480]
+ CRUSH rule 1 x 506 [493,123,43,856,936]
+ CRUSH rule 1 x 507 [12,598,264,422,416]
+ CRUSH rule 1 x 508 [227,157,611,301,223]
+ CRUSH rule 1 x 509 [807,242,363,122,582]
+ CRUSH rule 1 x 510 [134,437,227,75,313]
+ CRUSH rule 1 x 511 [212,54,83,799,457]
+ CRUSH rule 1 x 512 [236,630,758,752,361]
+ CRUSH rule 1 x 513 [994,693,644,938,846]
+ CRUSH rule 1 x 514 [45,508,831,19,817]
+ CRUSH rule 1 x 515 [504,138,480,272,530]
+ CRUSH rule 1 x 516 [285,409,136,570,841]
+ CRUSH rule 1 x 517 [300,232,23,906,438]
+ CRUSH rule 1 x 518 [397,674,98,898,967]
+ CRUSH rule 1 x 519 [86,750,772,913,101]
+ CRUSH rule 1 x 520 [900,833,614,130,261]
+ CRUSH rule 1 x 521 [31,47,236,751,911]
+ CRUSH rule 1 x 522 [390,16,280,144,291]
+ CRUSH rule 1 x 523 [618,308,424,590,300]
+ CRUSH rule 1 x 524 [635,189,687,963,601]
+ CRUSH rule 1 x 525 [311,916,699,262,775]
+ CRUSH rule 1 x 526 [48,738,227,718,244]
+ CRUSH rule 1 x 527 [202,851,889,216,763]
+ CRUSH rule 1 x 528 [565,827,590,273,918]
+ CRUSH rule 1 x 529 [934,864,241,43,466]
+ CRUSH rule 1 x 530 [502,934,298,670,986]
+ CRUSH rule 1 x 531 [681,627,942,487,288]
+ CRUSH rule 1 x 532 [422,6,147,205,861]
+ CRUSH rule 1 x 533 [863,68,364,983,247]
+ CRUSH rule 1 x 534 [962,931,775,172,663]
+ CRUSH rule 1 x 535 [89,565,397,693,839]
+ CRUSH rule 1 x 536 [499,351,760,458,918]
+ CRUSH rule 1 x 537 [676,547,787,311,867]
+ CRUSH rule 1 x 538 [58,644,571,649,941]
+ CRUSH rule 1 x 539 [837,953,457,711,458]
+ CRUSH rule 1 x 540 [831,50,132,213,197]
+ CRUSH rule 1 x 541 [582,757,121,525,532]
+ CRUSH rule 1 x 542 [472,132,790,997,948]
+ CRUSH rule 1 x 543 [382,272,797,330,315]
+ CRUSH rule 1 x 544 [947,930,496,883,509]
+ CRUSH rule 1 x 545 [425,570,305,77,821]
+ CRUSH rule 1 x 546 [18,65,529,437,343]
+ CRUSH rule 1 x 547 [445,715,600,472,213]
+ CRUSH rule 1 x 548 [367,569,980,167,627]
+ CRUSH rule 1 x 549 [125,715,671,817,285]
+ CRUSH rule 1 x 550 [425,599,744,199,923]
+ CRUSH rule 1 x 551 [44,1,528,922,944]
+ CRUSH rule 1 x 552 [246,104,68,239,123]
+ CRUSH rule 1 x 553 [71,703,615,28,593]
+ CRUSH rule 1 x 554 [207,124,217,166,525]
+ CRUSH rule 1 x 555 [570,28,317,420,931]
+ CRUSH rule 1 x 556 [674,152,421,79,215]
+ CRUSH rule 1 x 557 [347,817,191,391,741]
+ CRUSH rule 1 x 558 [627,426,369,692,815]
+ CRUSH rule 1 x 559 [940,630,924,242,224]
+ CRUSH rule 1 x 560 [295,903,541,29,245]
+ CRUSH rule 1 x 561 [506,682,384,637,878]
+ CRUSH rule 1 x 562 [718,529,87,729,842]
+ CRUSH rule 1 x 563 [552,332,747,206,274]
+ CRUSH rule 1 x 564 [835,769,736,486,630]
+ CRUSH rule 1 x 565 [8,167,539,182,607]
+ CRUSH rule 1 x 566 [600,481,301,263,90]
+ CRUSH rule 1 x 567 [999,994,509,899,947]
+ CRUSH rule 1 x 568 [252,431,157,62,601]
+ CRUSH rule 1 x 569 [643,218,943,455,83]
+ CRUSH rule 1 x 570 [617,635,765,422,250]
+ CRUSH rule 1 x 571 [757,80,59,98,328]
+ CRUSH rule 1 x 572 [299,348,575,889,943]
+ CRUSH rule 1 x 573 [25,505,270,167,58]
+ CRUSH rule 1 x 574 [215,431,624,177,628]
+ CRUSH rule 1 x 575 [225,252,611,546,32]
+ CRUSH rule 1 x 576 [627,94,159,857,430]
+ CRUSH rule 1 x 577 [237,809,778,636,61]
+ CRUSH rule 1 x 578 [885,313,120,344,771]
+ CRUSH rule 1 x 579 [924,575,787,831,47]
+ CRUSH rule 1 x 580 [718,51,766,121,118]
+ CRUSH rule 1 x 581 [219,807,129,571,856]
+ CRUSH rule 1 x 582 [893,701,598,863,285]
+ CRUSH rule 1 x 583 [246,930,964,170,993]
+ CRUSH rule 1 x 584 [336,432,680,175,495]
+ CRUSH rule 1 x 585 [324,999,397,485,457]
+ CRUSH rule 1 x 586 [558,230,976,541,816]
+ CRUSH rule 1 x 587 [985,830,597,21,308]
+ CRUSH rule 1 x 588 [211,544,57,134,162]
+ CRUSH rule 1 x 589 [129,21,112,190,885]
+ CRUSH rule 1 x 590 [467,969,652,593,287]
+ CRUSH rule 1 x 591 [758,514,316,164,35]
+ CRUSH rule 1 x 592 [525,253,190,443,315]
+ CRUSH rule 1 x 593 [601,885,339,152,297]
+ CRUSH rule 1 x 594 [227,60,450,30,717]
+ CRUSH rule 1 x 595 [720,854,496,912,80]
+ CRUSH rule 1 x 596 [751,195,997,77,261]
+ CRUSH rule 1 x 597 [129,574,714,8,789]
+ CRUSH rule 1 x 598 [679,207,604,396,841]
+ CRUSH rule 1 x 599 [668,315,683,349,681]
+ CRUSH rule 1 x 600 [143,396,464,444,59]
+ CRUSH rule 1 x 601 [326,573,873,902,136]
+ CRUSH rule 1 x 602 [860,281,875,535,672]
+ CRUSH rule 1 x 603 [709,328,445,349,190]
+ CRUSH rule 1 x 604 [571,62,814,95,866]
+ CRUSH rule 1 x 605 [252,739,860,27,313]
+ CRUSH rule 1 x 606 [339,236,759,842,67]
+ CRUSH rule 1 x 607 [590,248,759,868,433]
+ CRUSH rule 1 x 608 [145,635,309,467,875]
+ CRUSH rule 1 x 609 [973,547,223,79,762]
+ CRUSH rule 1 x 610 [435,816,961,983,255]
+ CRUSH rule 1 x 611 [559,283,422,584,176]
+ CRUSH rule 1 x 612 [273,149,123,576,911]
+ CRUSH rule 1 x 613 [828,614,642,674,33]
+ CRUSH rule 1 x 614 [478,748,393,34,171]
+ CRUSH rule 1 x 615 [392,155,144,326,626]
+ CRUSH rule 1 x 616 [778,637,452,248,15]
+ CRUSH rule 1 x 617 [622,713,996,833,611]
+ CRUSH rule 1 x 618 [149,877,270,329,180]
+ CRUSH rule 1 x 619 [604,163,656,409,322]
+ CRUSH rule 1 x 620 [181,23,409,198,64]
+ CRUSH rule 1 x 621 [735,902,386,237,939]
+ CRUSH rule 1 x 622 [661,824,717,568,858]
+ CRUSH rule 1 x 623 [142,121,643,61,695]
+ CRUSH rule 1 x 624 [360,716,420,398,49]
+ CRUSH rule 1 x 625 [541,167,385,1,601]
+ CRUSH rule 1 x 626 [364,431,610,363,535]
+ CRUSH rule 1 x 627 [458,137,557,410,287]
+ CRUSH rule 1 x 628 [250,350,556,497,821]
+ CRUSH rule 1 x 629 [928,160,710,572,365]
+ CRUSH rule 1 x 630 [243,19,918,556,601]
+ CRUSH rule 1 x 631 [438,221,574,676,797]
+ CRUSH rule 1 x 632 [797,368,247,5,32]
+ CRUSH rule 1 x 633 [993,749,525,485,27]
+ CRUSH rule 1 x 634 [239,351,633,299,651]
+ CRUSH rule 1 x 635 [640,965,25,961,306]
+ CRUSH rule 1 x 636 [173,290,297,991,937]
+ CRUSH rule 1 x 637 [0,918,98,108,111]
+ CRUSH rule 1 x 638 [702,235,424,900,983]
+ CRUSH rule 1 x 639 [475,687,31,785,918]
+ CRUSH rule 1 x 640 [31,664,399,677,123]
+ CRUSH rule 1 x 641 [296,473,108,963,341]
+ CRUSH rule 1 x 642 [894,273,427,606,677]
+ CRUSH rule 1 x 643 [117,111,732,191,114]
+ CRUSH rule 1 x 644 [438,336,327,512,599]
+ CRUSH rule 1 x 645 [982,702,351,573,907]
+ CRUSH rule 1 x 646 [334,804,146,842,697]
+ CRUSH rule 1 x 647 [933,787,185,334,752]
+ CRUSH rule 1 x 648 [22,444,400,862,207]
+ CRUSH rule 1 x 649 [503,229,213,460,639]
+ CRUSH rule 1 x 650 [328,659,420,443,739]
+ CRUSH rule 1 x 651 [3,880,823,123,378]
+ CRUSH rule 1 x 652 [495,977,563,733,92]
+ CRUSH rule 1 x 653 [185,718,804,280,975]
+ CRUSH rule 1 x 654 [130,528,380,81,906]
+ CRUSH rule 1 x 655 [560,872,454,504,319]
+ CRUSH rule 1 x 656 [219,885,178,981,863]
+ CRUSH rule 1 x 657 [233,684,813,490,208]
+ CRUSH rule 1 x 658 [778,6,756,380,750]
+ CRUSH rule 1 x 659 [240,663,306,540,789]
+ CRUSH rule 1 x 660 [244,855,196,147,678]
+ CRUSH rule 1 x 661 [184,270,128,398,910]
+ CRUSH rule 1 x 662 [65,883,921,438,79]
+ CRUSH rule 1 x 663 [323,721,594,812,43]
+ CRUSH rule 1 x 664 [865,113,512,51,427]
+ CRUSH rule 1 x 665 [420,850,591,475,202]
+ CRUSH rule 1 x 666 [319,767,246,3,369]
+ CRUSH rule 1 x 667 [875,39,343,100,829]
+ CRUSH rule 1 x 668 [331,122,263,599,355]
+ CRUSH rule 1 x 669 [915,521,402,747,673]
+ CRUSH rule 1 x 670 [845,659,943,447,401]
+ CRUSH rule 1 x 671 [108,634,527,363,856]
+ CRUSH rule 1 x 672 [578,216,110,589,302]
+ CRUSH rule 1 x 673 [442,74,579,797,622]
+ CRUSH rule 1 x 674 [588,364,281,308,645]
+ CRUSH rule 1 x 675 [489,698,744,671,870]
+ CRUSH rule 1 x 676 [928,911,40,180,722]
+ CRUSH rule 1 x 677 [399,269,692,131,615]
+ CRUSH rule 1 x 678 [546,752,544,155,5]
+ CRUSH rule 1 x 679 [988,25,275,433,628]
+ CRUSH rule 1 x 680 [335,963,382,486,749]
+ CRUSH rule 1 x 681 [690,462,623,466,49]
+ CRUSH rule 1 x 682 [196,588,154,257,807]
+ CRUSH rule 1 x 683 [627,25,421,160,873]
+ CRUSH rule 1 x 684 [38,804,592,158,991]
+ CRUSH rule 1 x 685 [841,368,548,362,166]
+ CRUSH rule 1 x 686 [336,287,525,440,166]
+ CRUSH rule 1 x 687 [20,682,924,653,356]
+ CRUSH rule 1 x 688 [463,371,780,556,385]
+ CRUSH rule 1 x 689 [569,250,78,816,847]
+ CRUSH rule 1 x 690 [551,144,587,263,378]
+ CRUSH rule 1 x 691 [766,464,446,533,449]
+ CRUSH rule 1 x 692 [739,634,18,245,624]
+ CRUSH rule 1 x 693 [339,297,118,330,817]
+ CRUSH rule 1 x 694 [405,26,830,181,533]
+ CRUSH rule 1 x 695 [622,576,597,535,600]
+ CRUSH rule 1 x 696 [558,902,689,13,715]
+ CRUSH rule 1 x 697 [818,222,406,691,427]
+ CRUSH rule 1 x 698 [178,48,402,233,841]
+ CRUSH rule 1 x 699 [450,244,180,919,100]
+ CRUSH rule 1 x 700 [502,771,987,706,416]
+ CRUSH rule 1 x 701 [4,612,782,216,853]
+ CRUSH rule 1 x 702 [177,630,232,923,281]
+ CRUSH rule 1 x 703 [354,178,389,393,778]
+ CRUSH rule 1 x 704 [646,601,156,171,603]
+ CRUSH rule 1 x 705 [921,401,890,265,244]
+ CRUSH rule 1 x 706 [652,877,562,452,26]
+ CRUSH rule 1 x 707 [345,745,67,716,789]
+ CRUSH rule 1 x 708 [333,607,180,469,170]
+ CRUSH rule 1 x 709 [45,187,302,115,896]
+ CRUSH rule 1 x 710 [94,855,43,199,18]
+ CRUSH rule 1 x 711 [227,653,731,150,452]
+ CRUSH rule 1 x 712 [398,953,136,870,181]
+ CRUSH rule 1 x 713 [116,800,503,662,635]
+ CRUSH rule 1 x 714 [111,629,866,709,902]
+ CRUSH rule 1 x 715 [531,291,486,382,192]
+ CRUSH rule 1 x 716 [169,541,291,42,343]
+ CRUSH rule 1 x 717 [417,446,994,894,239]
+ CRUSH rule 1 x 718 [992,383,298,844,377]
+ CRUSH rule 1 x 719 [936,674,324,759,194]
+ CRUSH rule 1 x 720 [370,188,174,464,644]
+ CRUSH rule 1 x 721 [320,859,278,259,170]
+ CRUSH rule 1 x 722 [7,2,673,129,96]
+ CRUSH rule 1 x 723 [270,553,831,662,38]
+ CRUSH rule 1 x 724 [666,822,708,895,633]
+ CRUSH rule 1 x 725 [794,406,875,459,981]
+ CRUSH rule 1 x 726 [420,556,341,292,240]
+ CRUSH rule 1 x 727 [561,461,129,635,965]
+ CRUSH rule 1 x 728 [951,330,196,756,589]
+ CRUSH rule 1 x 729 [656,644,436,591,27]
+ CRUSH rule 1 x 730 [3,558,629,184,50]
+ CRUSH rule 1 x 731 [852,89,75,735,713]
+ CRUSH rule 1 x 732 [983,840,869,976,697]
+ CRUSH rule 1 x 733 [285,396,388,122,387]
+ CRUSH rule 1 x 734 [125,510,402,640,676]
+ CRUSH rule 1 x 735 [417,773,686,504,459]
+ CRUSH rule 1 x 736 [749,396,632,550,779]
+ CRUSH rule 1 x 737 [644,991,946,135,448]
+ CRUSH rule 1 x 738 [449,683,290,220,245]
+ CRUSH rule 1 x 739 [341,220,641,454,740]
+ CRUSH rule 1 x 740 [874,524,674,650,472]
+ CRUSH rule 1 x 741 [189,472,712,798,715]
+ CRUSH rule 1 x 742 [912,581,114,145,730]
+ CRUSH rule 1 x 743 [654,914,425,441,763]
+ CRUSH rule 1 x 744 [725,295,579,377,162]
+ CRUSH rule 1 x 745 [787,858,850,506,612]
+ CRUSH rule 1 x 746 [757,848,704,30,47]
+ CRUSH rule 1 x 747 [700,81,867,681,801]
+ CRUSH rule 1 x 748 [557,436,238,664,293]
+ CRUSH rule 1 x 749 [772,622,337,42,156]
+ CRUSH rule 1 x 750 [946,97,376,677,316]
+ CRUSH rule 1 x 751 [996,618,343,911,83]
+ CRUSH rule 1 x 752 [746,887,695,868,610]
+ CRUSH rule 1 x 753 [741,14,463,479,172]
+ CRUSH rule 1 x 754 [648,349,333,355,65]
+ CRUSH rule 1 x 755 [157,460,466,187,959]
+ CRUSH rule 1 x 756 [416,97,197,497,227]
+ CRUSH rule 1 x 757 [599,839,776,410,256]
+ CRUSH rule 1 x 758 [994,218,620,256,361]
+ CRUSH rule 1 x 759 [959,682,514,745,100]
+ CRUSH rule 1 x 760 [518,943,215,83,706]
+ CRUSH rule 1 x 761 [285,849,420,324,987]
+ CRUSH rule 1 x 762 [591,313,41,335,110]
+ CRUSH rule 1 x 763 [908,411,200,740,292]
+ CRUSH rule 1 x 764 [787,234,894,485,883]
+ CRUSH rule 1 x 765 [327,921,882,393,444]
+ CRUSH rule 1 x 766 [84,161,878,704,416]
+ CRUSH rule 1 x 767 [370,895,702,701,890]
+ CRUSH rule 1 x 768 [826,760,879,864,460]
+ CRUSH rule 1 x 769 [67,768,663,735,814]
+ CRUSH rule 1 x 770 [593,909,482,259,5]
+ CRUSH rule 1 x 771 [309,935,121,578,937]
+ CRUSH rule 1 x 772 [12,125,797,301,348]
+ CRUSH rule 1 x 773 [253,466,820,549,591]
+ CRUSH rule 1 x 774 [164,390,705,109,881]
+ CRUSH rule 1 x 775 [703,47,43,973,643]
+ CRUSH rule 1 x 776 [728,231,80,916,2]
+ CRUSH rule 1 x 777 [981,621,568,729,869]
+ CRUSH rule 1 x 778 [411,456,544,597,789]
+ CRUSH rule 1 x 779 [346,121,519,921,587]
+ CRUSH rule 1 x 780 [476,39,288,381,303]
+ CRUSH rule 1 x 781 [10,130,585,844,729]
+ CRUSH rule 1 x 782 [462,246,581,902,623]
+ CRUSH rule 1 x 783 [580,373,153,775,668]
+ CRUSH rule 1 x 784 [413,113,978,990,994]
+ CRUSH rule 1 x 785 [341,856,332,354,59]
+ CRUSH rule 1 x 786 [411,140,313,393,215]
+ CRUSH rule 1 x 787 [605,522,211,813,636]
+ CRUSH rule 1 x 788 [226,545,35,142,726]
+ CRUSH rule 1 x 789 [545,320,414,702,731]
+ CRUSH rule 1 x 790 [414,748,816,327,130]
+ CRUSH rule 1 x 791 [660,906,406,697,916]
+ CRUSH rule 1 x 792 [287,392,514,204,75]
+ CRUSH rule 1 x 793 [631,133,850,713,720]
+ CRUSH rule 1 x 794 [931,517,543,210,963]
+ CRUSH rule 1 x 795 [551,962,477,948,425]
+ CRUSH rule 1 x 796 [814,4,95,27,368]
+ CRUSH rule 1 x 797 [64,201,299,734,605]
+ CRUSH rule 1 x 798 [422,530,114,431,565]
+ CRUSH rule 1 x 799 [824,32,679,562,266]
+ CRUSH rule 1 x 800 [862,623,489,637,861]
+ CRUSH rule 1 x 801 [145,550,329,324,734]
+ CRUSH rule 1 x 802 [570,19,847,308,387]
+ CRUSH rule 1 x 803 [151,812,662,358,880]
+ CRUSH rule 1 x 804 [467,93,264,863,176]
+ CRUSH rule 1 x 805 [621,223,938,809,591]
+ CRUSH rule 1 x 806 [898,957,805,430,499]
+ CRUSH rule 1 x 807 [354,531,422,159,921]
+ CRUSH rule 1 x 808 [7,96,76,897,446]
+ CRUSH rule 1 x 809 [70,734,719,56,687]
+ CRUSH rule 1 x 810 [701,18,972,327,771]
+ CRUSH rule 1 x 811 [248,547,103,728,901]
+ CRUSH rule 1 x 812 [230,576,821,566,993]
+ CRUSH rule 1 x 813 [805,114,683,629,742]
+ CRUSH rule 1 x 814 [54,619,973,741,497]
+ CRUSH rule 1 x 815 [679,412,613,132,969]
+ CRUSH rule 1 x 816 [919,448,826,414,36]
+ CRUSH rule 1 x 817 [765,830,436,521,332]
+ CRUSH rule 1 x 818 [415,566,644,687,692]
+ CRUSH rule 1 x 819 [721,319,865,750,546]
+ CRUSH rule 1 x 820 [218,301,333,190,686]
+ CRUSH rule 1 x 821 [185,795,680,953,329]
+ CRUSH rule 1 x 822 [356,261,54,522,900]
+ CRUSH rule 1 x 823 [220,281,549,456,64]
+ CRUSH rule 1 x 824 [292,809,887,74,776]
+ CRUSH rule 1 x 825 [949,778,101,311,110]
+ CRUSH rule 1 x 826 [767,818,833,927,356]
+ CRUSH rule 1 x 827 [631,83,406,635,657]
+ CRUSH rule 1 x 828 [288,986,445,26,414]
+ CRUSH rule 1 x 829 [990,667,915,694,974]
+ CRUSH rule 1 x 830 [152,571,778,505,685]
+ CRUSH rule 1 x 831 [814,563,630,97,582]
+ CRUSH rule 1 x 832 [235,641,616,110,979]
+ CRUSH rule 1 x 833 [657,565,922,140,825]
+ CRUSH rule 1 x 834 [907,231,644,13,617]
+ CRUSH rule 1 x 835 [784,262,771,264,612]
+ CRUSH rule 1 x 836 [951,158,366,710,43]
+ CRUSH rule 1 x 837 [556,498,334,633,895]
+ CRUSH rule 1 x 838 [329,274,964,547,119]
+ CRUSH rule 1 x 839 [568,209,939,364,658]
+ CRUSH rule 1 x 840 [45,579,842,70,655]
+ CRUSH rule 1 x 841 [652,702,24,605,152]
+ CRUSH rule 1 x 842 [629,984,314,895,408]
+ CRUSH rule 1 x 843 [799,690,688,648,151]
+ CRUSH rule 1 x 844 [694,600,534,700,569]
+ CRUSH rule 1 x 845 [332,30,179,93,951]
+ CRUSH rule 1 x 846 [452,251,712,719,404]
+ CRUSH rule 1 x 847 [399,681,847,739,13]
+ CRUSH rule 1 x 848 [303,138,440,346,547]
+ CRUSH rule 1 x 849 [666,346,708,873,64]
+ CRUSH rule 1 x 850 [644,511,345,844,545]
+ CRUSH rule 1 x 851 [527,546,737,425,100]
+ CRUSH rule 1 x 852 [31,809,94,618,156]
+ CRUSH rule 1 x 853 [483,330,869,184,46]
+ CRUSH rule 1 x 854 [697,953,968,143,502]
+ CRUSH rule 1 x 855 [837,996,239,621,32]
+ CRUSH rule 1 x 856 [712,40,547,430,195]
+ CRUSH rule 1 x 857 [77,984,576,551,568]
+ CRUSH rule 1 x 858 [412,384,841,465,572]
+ CRUSH rule 1 x 859 [173,760,26,300,87]
+ CRUSH rule 1 x 860 [776,429,328,917,658]
+ CRUSH rule 1 x 861 [705,405,477,50,73]
+ CRUSH rule 1 x 862 [809,44,788,938,964]
+ CRUSH rule 1 x 863 [349,496,963,178,675]
+ CRUSH rule 1 x 864 [717,858,101,239,992]
+ CRUSH rule 1 x 865 [857,603,586,262,550]
+ CRUSH rule 1 x 866 [394,304,71,96,642]
+ CRUSH rule 1 x 867 [640,773,663,974,261]
+ CRUSH rule 1 x 868 [613,950,712,663,16]
+ CRUSH rule 1 x 869 [973,889,524,22,671]
+ CRUSH rule 1 x 870 [505,35,386,498,348]
+ CRUSH rule 1 x 871 [239,264,262,773,781]
+ CRUSH rule 1 x 872 [21,767,456,748,783]
+ CRUSH rule 1 x 873 [954,666,980,264,435]
+ CRUSH rule 1 x 874 [54,510,947,1,500]
+ CRUSH rule 1 x 875 [809,418,452,462,88]
+ CRUSH rule 1 x 876 [483,457,61,248,523]
+ CRUSH rule 1 x 877 [542,531,952,939,710]
+ CRUSH rule 1 x 878 [217,674,857,644,678]
+ CRUSH rule 1 x 879 [999,475,134,250,319]
+ CRUSH rule 1 x 880 [678,573,935,385,570]
+ CRUSH rule 1 x 881 [394,835,789,802,587]
+ CRUSH rule 1 x 882 [467,382,353,56,979]
+ CRUSH rule 1 x 883 [802,744,237,337,50]
+ CRUSH rule 1 x 884 [653,660,638,700,31]
+ CRUSH rule 1 x 885 [898,704,307,445,879]
+ CRUSH rule 1 x 886 [434,357,938,641,737]
+ CRUSH rule 1 x 887 [297,226,711,428,370]
+ CRUSH rule 1 x 888 [863,324,443,213,902]
+ CRUSH rule 1 x 889 [105,102,308,163,947]
+ CRUSH rule 1 x 890 [550,248,606,704,615]
+ CRUSH rule 1 x 891 [575,928,880,891,826]
+ CRUSH rule 1 x 892 [259,862,133,271,292]
+ CRUSH rule 1 x 893 [902,880,543,542,37]
+ CRUSH rule 1 x 894 [180,169,916,43,945]
+ CRUSH rule 1 x 895 [725,849,182,129,177]
+ CRUSH rule 1 x 896 [951,34,874,537,969]
+ CRUSH rule 1 x 897 [810,352,73,939,943]
+ CRUSH rule 1 x 898 [979,433,719,411,787]
+ CRUSH rule 1 x 899 [685,668,534,932,399]
+ CRUSH rule 1 x 900 [530,978,41,894,941]
+ CRUSH rule 1 x 901 [740,107,336,175,574]
+ CRUSH rule 1 x 902 [800,743,693,310,67]
+ CRUSH rule 1 x 903 [230,267,842,266,550]
+ CRUSH rule 1 x 904 [346,949,460,973,696]
+ CRUSH rule 1 x 905 [530,397,619,958,576]
+ CRUSH rule 1 x 906 [80,426,138,672,73]
+ CRUSH rule 1 x 907 [365,968,475,297,296]
+ CRUSH rule 1 x 908 [204,832,742,809,862]
+ CRUSH rule 1 x 909 [883,989,146,959,366]
+ CRUSH rule 1 x 910 [549,593,249,853,792]
+ CRUSH rule 1 x 911 [325,847,352,214,851]
+ CRUSH rule 1 x 912 [874,888,582,796,557]
+ CRUSH rule 1 x 913 [331,463,342,574,989]
+ CRUSH rule 1 x 914 [836,468,601,732,607]
+ CRUSH rule 1 x 915 [245,228,100,661,799]
+ CRUSH rule 1 x 916 [77,967,364,435,27]
+ CRUSH rule 1 x 917 [239,60,866,221,772]
+ CRUSH rule 1 x 918 [988,115,922,80,201]
+ CRUSH rule 1 x 919 [783,139,696,1,848]
+ CRUSH rule 1 x 920 [623,408,685,953,974]
+ CRUSH rule 1 x 921 [105,799,144,90,399]
+ CRUSH rule 1 x 922 [887,505,652,348,514]
+ CRUSH rule 1 x 923 [223,318,552,458,743]
+ CRUSH rule 1 x 924 [25,778,366,333,163]
+ CRUSH rule 1 x 925 [912,601,297,682,770]
+ CRUSH rule 1 x 926 [968,133,158,144,814]
+ CRUSH rule 1 x 927 [277,724,214,988,690]
+ CRUSH rule 1 x 928 [554,203,658,789,298]
+ CRUSH rule 1 x 929 [761,802,367,528,758]
+ CRUSH rule 1 x 930 [814,61,788,736,660]
+ CRUSH rule 1 x 931 [29,193,61,41,343]
+ CRUSH rule 1 x 932 [446,198,862,534,168]
+ CRUSH rule 1 x 933 [352,742,216,321,525]
+ CRUSH rule 1 x 934 [730,2,332,631,613]
+ CRUSH rule 1 x 935 [731,23,736,79,361]
+ CRUSH rule 1 x 936 [322,975,20,904,827]
+ CRUSH rule 1 x 937 [822,221,841,161,723]
+ CRUSH rule 1 x 938 [557,850,66,630,499]
+ CRUSH rule 1 x 939 [150,11,971,371,124]
+ CRUSH rule 1 x 940 [638,398,169,616,333]
+ CRUSH rule 1 x 941 [730,342,929,577,451]
+ CRUSH rule 1 x 942 [62,292,166,814,587]
+ CRUSH rule 1 x 943 [165,314,519,548,41]
+ CRUSH rule 1 x 944 [199,625,766,176,194]
+ CRUSH rule 1 x 945 [946,999,699,303,38]
+ CRUSH rule 1 x 946 [595,93,852,142,503]
+ CRUSH rule 1 x 947 [800,582,356,93,716]
+ CRUSH rule 1 x 948 [132,551,139,920,87]
+ CRUSH rule 1 x 949 [792,920,466,380,97]
+ CRUSH rule 1 x 950 [111,345,176,543,879]
+ CRUSH rule 1 x 951 [414,619,648,655,364]
+ CRUSH rule 1 x 952 [775,469,500,356,287]
+ CRUSH rule 1 x 953 [349,1,5,251,168]
+ CRUSH rule 1 x 954 [570,940,410,249,929]
+ CRUSH rule 1 x 955 [729,774,823,800,7]
+ CRUSH rule 1 x 956 [519,141,575,625,738]
+ CRUSH rule 1 x 957 [242,709,611,97,760]
+ CRUSH rule 1 x 958 [84,217,227,253,246]
+ CRUSH rule 1 x 959 [270,413,918,789,703]
+ CRUSH rule 1 x 960 [458,192,307,279,920]
+ CRUSH rule 1 x 961 [981,388,777,546,359]
+ CRUSH rule 1 x 962 [623,834,277,134,729]
+ CRUSH rule 1 x 963 [291,167,714,468,109]
+ CRUSH rule 1 x 964 [28,156,788,127,598]
+ CRUSH rule 1 x 965 [675,557,290,517,840]
+ CRUSH rule 1 x 966 [836,306,946,283,642]
+ CRUSH rule 1 x 967 [966,386,735,837,392]
+ CRUSH rule 1 x 968 [864,756,690,121,328]
+ CRUSH rule 1 x 969 [729,625,480,769,512]
+ CRUSH rule 1 x 970 [800,362,646,582,309]
+ CRUSH rule 1 x 971 [737,381,153,684,298]
+ CRUSH rule 1 x 972 [952,245,720,884,334]
+ CRUSH rule 1 x 973 [356,455,579,857,832]
+ CRUSH rule 1 x 974 [545,758,586,596,405]
+ CRUSH rule 1 x 975 [336,191,202,146,720]
+ CRUSH rule 1 x 976 [446,208,757,620,252]
+ CRUSH rule 1 x 977 [202,896,196,956,763]
+ CRUSH rule 1 x 978 [612,324,996,225,418]
+ CRUSH rule 1 x 979 [843,457,675,650,958]
+ CRUSH rule 1 x 980 [60,914,881,626,850]
+ CRUSH rule 1 x 981 [702,749,937,153,724]
+ CRUSH rule 1 x 982 [298,928,738,167,99]
+ CRUSH rule 1 x 983 [723,572,395,358,900]
+ CRUSH rule 1 x 984 [723,864,804,935,846]
+ CRUSH rule 1 x 985 [945,459,868,211,524]
+ CRUSH rule 1 x 986 [772,664,535,169,297]
+ CRUSH rule 1 x 987 [88,324,312,843,661]
+ CRUSH rule 1 x 988 [522,927,131,996,351]
+ CRUSH rule 1 x 989 [578,332,208,605,975]
+ CRUSH rule 1 x 990 [638,228,414,311,738]
+ CRUSH rule 1 x 991 [530,221,451,422,879]
+ CRUSH rule 1 x 992 [925,705,275,81,234]
+ CRUSH rule 1 x 993 [991,301,43,469,830]
+ CRUSH rule 1 x 994 [276,51,868,683,843]
+ CRUSH rule 1 x 995 [288,836,753,790,758]
+ CRUSH rule 1 x 996 [887,983,252,686,470]
+ CRUSH rule 1 x 997 [110,924,386,79,705]
+ CRUSH rule 1 x 998 [435,830,485,853,926]
+ CRUSH rule 1 x 999 [876,738,357,913,723]
+ CRUSH rule 1 x 1000 [178,963,638,430,845]
+ CRUSH rule 1 x 1001 [99,519,66,759,583]
+ CRUSH rule 1 x 1002 [515,534,468,866,878]
+ CRUSH rule 1 x 1003 [104,611,937,698,94]
+ CRUSH rule 1 x 1004 [269,638,724,375,491]
+ CRUSH rule 1 x 1005 [369,223,309,409,822]
+ CRUSH rule 1 x 1006 [40,107,69,275,79]
+ CRUSH rule 1 x 1007 [978,111,416,758,454]
+ CRUSH rule 1 x 1008 [965,956,624,832,421]
+ CRUSH rule 1 x 1009 [598,476,356,695,919]
+ CRUSH rule 1 x 1010 [767,523,239,517,29]
+ CRUSH rule 1 x 1011 [289,871,207,576,347]
+ CRUSH rule 1 x 1012 [128,28,370,31,341]
+ CRUSH rule 1 x 1013 [979,765,660,812,666]
+ CRUSH rule 1 x 1014 [979,948,513,88,47]
+ CRUSH rule 1 x 1015 [277,790,396,672,542]
+ CRUSH rule 1 x 1016 [262,73,128,886,839]
+ CRUSH rule 1 x 1017 [150,269,61,499,832]
+ CRUSH rule 1 x 1018 [555,829,554,944,406]
+ CRUSH rule 1 x 1019 [513,356,265,446,65]
+ CRUSH rule 1 x 1020 [158,161,877,704,948]
+ CRUSH rule 1 x 1021 [915,998,957,285,546]
+ CRUSH rule 1 x 1022 [967,829,973,640,703]
+ CRUSH rule 1 x 1023 [488,257,614,859,325]
+ rule 1 (metadata) num_rep 5 result size == 5:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536,450,604,380]
+ CRUSH rule 1 x 1 [876,250,334,633,744,843]
+ CRUSH rule 1 x 2 [292,832,53,392,386,787]
+ CRUSH rule 1 x 3 [623,387,124,998,749,211]
+ CRUSH rule 1 x 4 [61,334,710,4,994,982]
+ CRUSH rule 1 x 5 [946,557,713,664,141,817]
+ CRUSH rule 1 x 6 [576,668,212,163,732,381]
+ CRUSH rule 1 x 7 [645,753,906,393,341,44]
+ CRUSH rule 1 x 8 [243,6,863,781,211,100]
+ CRUSH rule 1 x 9 [22,578,251,410,297,430]
+ CRUSH rule 1 x 10 [758,828,360,477,821,801]
+ CRUSH rule 1 x 11 [769,120,124,527,119,504]
+ CRUSH rule 1 x 12 [780,364,689,755,675,199]
+ CRUSH rule 1 x 13 [557,18,351,719,742,780]
+ CRUSH rule 1 x 14 [59,561,249,461,971,835]
+ CRUSH rule 1 x 15 [718,928,993,21,76,313]
+ CRUSH rule 1 x 16 [673,632,841,954,788,90]
+ CRUSH rule 1 x 17 [648,43,560,514,142,289]
+ CRUSH rule 1 x 18 [654,219,181,568,381,253]
+ CRUSH rule 1 x 19 [850,545,377,848,863,543]
+ CRUSH rule 1 x 20 [717,785,974,5,225,552]
+ CRUSH rule 1 x 21 [420,57,519,306,312,983]
+ CRUSH rule 1 x 22 [503,998,193,821,634,684]
+ CRUSH rule 1 x 23 [411,663,168,110,899,488]
+ CRUSH rule 1 x 24 [266,861,353,1,456,128]
+ CRUSH rule 1 x 25 [760,483,818,600,509,951]
+ CRUSH rule 1 x 26 [903,24,573,718,112,694]
+ CRUSH rule 1 x 27 [946,188,289,510,687,827]
+ CRUSH rule 1 x 28 [69,312,73,198,256,629]
+ CRUSH rule 1 x 29 [844,883,337,628,496,405]
+ CRUSH rule 1 x 30 [621,18,613,794,910,936]
+ CRUSH rule 1 x 31 [784,943,814,539,962,392]
+ CRUSH rule 1 x 32 [173,374,369,972,315,83]
+ CRUSH rule 1 x 33 [698,336,357,966,582,407]
+ CRUSH rule 1 x 34 [168,836,210,798,904,190]
+ CRUSH rule 1 x 35 [274,509,534,818,912,671]
+ CRUSH rule 1 x 36 [318,215,153,628,87,407]
+ CRUSH rule 1 x 37 [173,604,109,935,203,401]
+ CRUSH rule 1 x 38 [708,444,683,604,722,900]
+ CRUSH rule 1 x 39 [662,198,417,680,226,342]
+ CRUSH rule 1 x 40 [620,801,414,78,560,766]
+ CRUSH rule 1 x 41 [811,264,177,127,148,791]
+ CRUSH rule 1 x 42 [863,179,527,660,133,529]
+ CRUSH rule 1 x 43 [686,822,988,228,791,549]
+ CRUSH rule 1 x 44 [396,222,46,841,536,140]
+ CRUSH rule 1 x 45 [991,694,253,142,54,422]
+ CRUSH rule 1 x 46 [420,909,184,285,508,458]
+ CRUSH rule 1 x 47 [467,211,605,207,241,881]
+ CRUSH rule 1 x 48 [955,329,368,168,698,787]
+ CRUSH rule 1 x 49 [974,891,931,29,813,506]
+ CRUSH rule 1 x 50 [870,441,691,823,761,6]
+ CRUSH rule 1 x 51 [182,930,25,936,97,260]
+ CRUSH rule 1 x 52 [704,812,894,794,481,37]
+ CRUSH rule 1 x 53 [185,713,631,280,345,558]
+ CRUSH rule 1 x 54 [270,441,100,82,983,930]
+ CRUSH rule 1 x 55 [895,734,958,793,651,572]
+ CRUSH rule 1 x 56 [564,963,683,324,40,189]
+ CRUSH rule 1 x 57 [738,130,208,973,498,861]
+ CRUSH rule 1 x 58 [524,113,806,903,531,334]
+ CRUSH rule 1 x 59 [408,337,668,529,34,384]
+ CRUSH rule 1 x 60 [228,790,857,309,616,895]
+ CRUSH rule 1 x 61 [154,843,717,467,883,536]
+ CRUSH rule 1 x 62 [594,811,549,276,693,917]
+ CRUSH rule 1 x 63 [646,67,884,925,941,434]
+ CRUSH rule 1 x 64 [175,542,155,837,594,197]
+ CRUSH rule 1 x 65 [745,619,131,867,269,62]
+ CRUSH rule 1 x 66 [275,468,23,35,328,432]
+ CRUSH rule 1 x 67 [246,958,524,493,636,227]
+ CRUSH rule 1 x 68 [711,473,403,228,835,126]
+ CRUSH rule 1 x 69 [493,924,850,939,950,105]
+ CRUSH rule 1 x 70 [30,499,644,33,804,654]
+ CRUSH rule 1 x 71 [984,883,574,716,575,391]
+ CRUSH rule 1 x 72 [71,286,942,363,628,632]
+ CRUSH rule 1 x 73 [922,618,3,371,464,442]
+ CRUSH rule 1 x 74 [629,414,185,573,678,338]
+ CRUSH rule 1 x 75 [222,20,174,820,312,361]
+ CRUSH rule 1 x 76 [262,366,339,290,718,143]
+ CRUSH rule 1 x 77 [638,469,992,280,773,892]
+ CRUSH rule 1 x 78 [324,511,788,7,308,228]
+ CRUSH rule 1 x 79 [577,990,64,94,447,924]
+ CRUSH rule 1 x 80 [501,95,278,903,631,842]
+ CRUSH rule 1 x 81 [506,812,9,698,173,664]
+ CRUSH rule 1 x 82 [222,145,80,785,835,745]
+ CRUSH rule 1 x 83 [71,634,61,91,856,529]
+ CRUSH rule 1 x 84 [49,761,773,368,318,708]
+ CRUSH rule 1 x 85 [985,896,708,861,325,307]
+ CRUSH rule 1 x 86 [537,745,93,524,466,356]
+ CRUSH rule 1 x 87 [997,317,463,626,685,429]
+ CRUSH rule 1 x 88 [957,350,890,857,375,176]
+ CRUSH rule 1 x 89 [399,730,148,314,159,982]
+ CRUSH rule 1 x 90 [943,706,683,267,579,141]
+ CRUSH rule 1 x 91 [22,368,149,928,140,529]
+ CRUSH rule 1 x 92 [532,424,426,773,623,197]
+ CRUSH rule 1 x 93 [218,489,405,681,549,201]
+ CRUSH rule 1 x 94 [181,96,102,515,776,365]
+ CRUSH rule 1 x 95 [343,957,820,139,334,37]
+ CRUSH rule 1 x 96 [861,270,87,797,0,245]
+ CRUSH rule 1 x 97 [459,706,45,328,274,605]
+ CRUSH rule 1 x 98 [327,867,353,948,728,280]
+ CRUSH rule 1 x 99 [974,133,468,906,235,988]
+ CRUSH rule 1 x 100 [32,445,547,371,960,885]
+ CRUSH rule 1 x 101 [142,90,337,950,970,570]
+ CRUSH rule 1 x 102 [172,129,139,22,403,867]
+ CRUSH rule 1 x 103 [630,47,161,356,911,421]
+ CRUSH rule 1 x 104 [758,133,278,11,947,799]
+ CRUSH rule 1 x 105 [843,604,47,33,401,632]
+ CRUSH rule 1 x 106 [28,681,193,679,990,343]
+ CRUSH rule 1 x 107 [74,320,85,819,315,253]
+ CRUSH rule 1 x 108 [875,593,575,517,107,153]
+ CRUSH rule 1 x 109 [411,985,811,720,198,666]
+ CRUSH rule 1 x 110 [440,774,799,660,715,167]
+ CRUSH rule 1 x 111 [405,742,276,359,936,360]
+ CRUSH rule 1 x 112 [143,181,922,545,185,303]
+ CRUSH rule 1 x 113 [153,846,160,903,789,897]
+ CRUSH rule 1 x 114 [804,892,939,20,312,692]
+ CRUSH rule 1 x 115 [588,508,958,580,232,722]
+ CRUSH rule 1 x 116 [327,148,637,486,712,464]
+ CRUSH rule 1 x 117 [95,594,989,131,714,275]
+ CRUSH rule 1 x 118 [80,957,897,239,359,432]
+ CRUSH rule 1 x 119 [386,932,951,768,679,300]
+ CRUSH rule 1 x 120 [366,312,653,936,71,241]
+ CRUSH rule 1 x 121 [129,154,847,16,471,481]
+ CRUSH rule 1 x 122 [873,1,110,939,90,412]
+ CRUSH rule 1 x 123 [533,415,789,600,713,800]
+ CRUSH rule 1 x 124 [461,691,898,723,957,759]
+ CRUSH rule 1 x 125 [342,599,830,402,615,994]
+ CRUSH rule 1 x 126 [819,781,822,548,279,255]
+ CRUSH rule 1 x 127 [437,893,585,707,353,189]
+ CRUSH rule 1 x 128 [679,994,982,550,991,324]
+ CRUSH rule 1 x 129 [380,685,947,302,698,144]
+ CRUSH rule 1 x 130 [992,52,466,867,998,777]
+ CRUSH rule 1 x 131 [469,90,208,599,829,656]
+ CRUSH rule 1 x 132 [571,250,316,535,54,418]
+ CRUSH rule 1 x 133 [964,728,329,902,108,118]
+ CRUSH rule 1 x 134 [999,19,716,963,323,559]
+ CRUSH rule 1 x 135 [634,101,52,938,413,573]
+ CRUSH rule 1 x 136 [114,889,692,768,694,279]
+ CRUSH rule 1 x 137 [839,8,959,280,922,870]
+ CRUSH rule 1 x 138 [967,949,138,451,292,548]
+ CRUSH rule 1 x 139 [308,711,736,247,632,126]
+ CRUSH rule 1 x 140 [764,936,926,55,331,115]
+ CRUSH rule 1 x 141 [423,302,112,216,603,873]
+ CRUSH rule 1 x 142 [252,821,715,340,635,668]
+ CRUSH rule 1 x 143 [33,808,518,477,325,316]
+ CRUSH rule 1 x 144 [472,88,969,162,401,771]
+ CRUSH rule 1 x 145 [242,208,252,604,266,743]
+ CRUSH rule 1 x 146 [290,70,570,384,934,856]
+ CRUSH rule 1 x 147 [447,352,657,493,467,918]
+ CRUSH rule 1 x 148 [212,644,432,658,109,275]
+ CRUSH rule 1 x 149 [9,775,87,35,260,646]
+ CRUSH rule 1 x 150 [166,456,582,144,324,340]
+ CRUSH rule 1 x 151 [811,875,307,20,782,229]
+ CRUSH rule 1 x 152 [449,617,223,9,182,407]
+ CRUSH rule 1 x 153 [523,537,695,627,959,613]
+ CRUSH rule 1 x 154 [208,559,874,597,243,706]
+ CRUSH rule 1 x 155 [569,325,192,296,367,848]
+ CRUSH rule 1 x 156 [488,121,521,213,595,837]
+ CRUSH rule 1 x 157 [140,723,633,260,487,856]
+ CRUSH rule 1 x 158 [786,451,320,239,667,632]
+ CRUSH rule 1 x 159 [134,664,517,821,667,944]
+ CRUSH rule 1 x 160 [690,112,414,990,183,590]
+ CRUSH rule 1 x 161 [324,912,397,423,991,284]
+ CRUSH rule 1 x 162 [748,567,284,183,463,336]
+ CRUSH rule 1 x 163 [575,499,31,816,749,737]
+ CRUSH rule 1 x 164 [314,489,308,326,51,568]
+ CRUSH rule 1 x 165 [116,209,750,53,813,640]
+ CRUSH rule 1 x 166 [352,706,701,810,718,527]
+ CRUSH rule 1 x 167 [27,743,174,142,551,1]
+ CRUSH rule 1 x 168 [953,898,880,660,500,799]
+ CRUSH rule 1 x 169 [912,147,266,547,331,770]
+ CRUSH rule 1 x 170 [421,515,828,844,151,981]
+ CRUSH rule 1 x 171 [488,584,880,964,936,196]
+ CRUSH rule 1 x 172 [366,443,957,66,162,693]
+ CRUSH rule 1 x 173 [863,291,625,287,158,496]
+ CRUSH rule 1 x 174 [263,555,650,410,339,616]
+ CRUSH rule 1 x 175 [875,961,361,575,33,109]
+ CRUSH rule 1 x 176 [745,83,701,680,250,420]
+ CRUSH rule 1 x 177 [128,244,41,123,422,902]
+ CRUSH rule 1 x 178 [155,41,264,777,314,564]
+ CRUSH rule 1 x 179 [593,833,202,183,971,38]
+ CRUSH rule 1 x 180 [154,734,17,831,824,522]
+ CRUSH rule 1 x 181 [289,675,723,800,166,712]
+ CRUSH rule 1 x 182 [730,931,560,209,943,261]
+ CRUSH rule 1 x 183 [639,237,794,815,827,400]
+ CRUSH rule 1 x 184 [704,312,685,645,691,778]
+ CRUSH rule 1 x 185 [97,100,762,82,999,542]
+ CRUSH rule 1 x 186 [26,665,554,215,280,421]
+ CRUSH rule 1 x 187 [649,14,740,494,402,684]
+ CRUSH rule 1 x 188 [682,695,590,743,927,945]
+ CRUSH rule 1 x 189 [325,693,726,51,448,169]
+ CRUSH rule 1 x 190 [399,933,136,955,57,504]
+ CRUSH rule 1 x 191 [629,533,17,126,60,146]
+ CRUSH rule 1 x 192 [503,578,38,492,222,251]
+ CRUSH rule 1 x 193 [546,333,651,678,823,652]
+ CRUSH rule 1 x 194 [242,473,58,655,911,277]
+ CRUSH rule 1 x 195 [625,719,135,81,636,513]
+ CRUSH rule 1 x 196 [357,114,125,867,250,522]
+ CRUSH rule 1 x 197 [306,954,453,873,211,334]
+ CRUSH rule 1 x 198 [863,791,311,911,206,61]
+ CRUSH rule 1 x 199 [935,906,929,252,893,75]
+ CRUSH rule 1 x 200 [373,774,229,454,909,611]
+ CRUSH rule 1 x 201 [659,320,477,313,779,16]
+ CRUSH rule 1 x 202 [260,433,524,880,223,818]
+ CRUSH rule 1 x 203 [36,239,675,971,703,209]
+ CRUSH rule 1 x 204 [92,516,993,728,279,478]
+ CRUSH rule 1 x 205 [68,395,473,45,683,662]
+ CRUSH rule 1 x 206 [570,530,642,380,311,398]
+ CRUSH rule 1 x 207 [834,457,850,917,456,296]
+ CRUSH rule 1 x 208 [927,484,640,976,803,626]
+ CRUSH rule 1 x 209 [878,66,58,940,48,233]
+ CRUSH rule 1 x 210 [572,981,484,29,0,426]
+ CRUSH rule 1 x 211 [107,597,780,857,895,57]
+ CRUSH rule 1 x 212 [389,107,838,624,698,562]
+ CRUSH rule 1 x 213 [497,717,567,728,905,134]
+ CRUSH rule 1 x 214 [798,65,254,572,32,393]
+ CRUSH rule 1 x 215 [233,419,283,638,520,891]
+ CRUSH rule 1 x 216 [494,464,742,523,459,174]
+ CRUSH rule 1 x 217 [352,396,309,938,66,41]
+ CRUSH rule 1 x 218 [895,864,988,650,593,740]
+ CRUSH rule 1 x 219 [222,534,277,242,658,482]
+ CRUSH rule 1 x 220 [281,19,584,563,858,965]
+ CRUSH rule 1 x 221 [64,928,963,130,312,394]
+ CRUSH rule 1 x 222 [40,544,161,199,861,644]
+ CRUSH rule 1 x 223 [645,556,159,417,46,135]
+ CRUSH rule 1 x 224 [647,165,957,263,961,576]
+ CRUSH rule 1 x 225 [219,714,858,747,461,175]
+ CRUSH rule 1 x 226 [372,511,181,277,695,404]
+ CRUSH rule 1 x 227 [925,156,714,863,257,74]
+ CRUSH rule 1 x 228 [682,404,839,263,521,195]
+ CRUSH rule 1 x 229 [880,838,770,891,236,542]
+ CRUSH rule 1 x 230 [328,659,916,468,646,572]
+ CRUSH rule 1 x 231 [320,383,669,109,627,621]
+ CRUSH rule 1 x 232 [924,846,394,319,43,519]
+ CRUSH rule 1 x 233 [948,652,575,838,498,395]
+ CRUSH rule 1 x 234 [484,943,42,575,936,180]
+ CRUSH rule 1 x 235 [750,65,590,168,870,308]
+ CRUSH rule 1 x 236 [551,787,490,136,370,833]
+ CRUSH rule 1 x 237 [390,157,166,251,752,75]
+ CRUSH rule 1 x 238 [570,6,989,707,514,905]
+ CRUSH rule 1 x 239 [729,959,376,975,496,49]
+ CRUSH rule 1 x 240 [981,241,156,767,631,576]
+ CRUSH rule 1 x 241 [310,816,641,177,996,454]
+ CRUSH rule 1 x 242 [161,63,642,837,763,458]
+ CRUSH rule 1 x 243 [180,394,33,683,189,419]
+ CRUSH rule 1 x 244 [52,174,685,189,78,310]
+ CRUSH rule 1 x 245 [523,121,915,84,386,409]
+ CRUSH rule 1 x 246 [362,893,390,487,817,88]
+ CRUSH rule 1 x 247 [382,184,116,34,143,15]
+ CRUSH rule 1 x 248 [129,114,852,469,359,291]
+ CRUSH rule 1 x 249 [159,683,91,856,475,369]
+ CRUSH rule 1 x 250 [404,945,569,955,228,910]
+ CRUSH rule 1 x 251 [661,225,738,757,37,642]
+ CRUSH rule 1 x 252 [961,226,542,103,945,885]
+ CRUSH rule 1 x 253 [651,97,225,364,189,248]
+ CRUSH rule 1 x 254 [123,33,741,692,599,11]
+ CRUSH rule 1 x 255 [314,649,891,855,517,344]
+ CRUSH rule 1 x 256 [315,215,651,126,470,849]
+ CRUSH rule 1 x 257 [825,264,867,841,529,409]
+ CRUSH rule 1 x 258 [624,789,370,723,131,982]
+ CRUSH rule 1 x 259 [602,542,70,563,947,723]
+ CRUSH rule 1 x 260 [717,878,43,56,377,481]
+ CRUSH rule 1 x 261 [145,517,20,903,786,939]
+ CRUSH rule 1 x 262 [223,1,561,420,357,16]
+ CRUSH rule 1 x 263 [462,211,405,508,787,669]
+ CRUSH rule 1 x 264 [654,471,266,662,135,564]
+ CRUSH rule 1 x 265 [302,794,704,798,659,487]
+ CRUSH rule 1 x 266 [202,132,884,209,551,984]
+ CRUSH rule 1 x 267 [282,938,657,113,672,993]
+ CRUSH rule 1 x 268 [338,309,356,278,928,797]
+ CRUSH rule 1 x 269 [738,122,266,200,894,118]
+ CRUSH rule 1 x 270 [707,982,946,196,407,804]
+ CRUSH rule 1 x 271 [705,432,364,735,512,595]
+ CRUSH rule 1 x 272 [756,545,942,56,542,449]
+ CRUSH rule 1 x 273 [197,502,527,721,239,648]
+ CRUSH rule 1 x 274 [992,44,653,573,527,702]
+ CRUSH rule 1 x 275 [544,789,170,434,23,926]
+ CRUSH rule 1 x 276 [658,467,577,268,336,5]
+ CRUSH rule 1 x 277 [143,490,880,483,928,272]
+ CRUSH rule 1 x 278 [492,647,355,282,834,64]
+ CRUSH rule 1 x 279 [517,792,604,987,527,894]
+ CRUSH rule 1 x 280 [825,740,27,848,514,750]
+ CRUSH rule 1 x 281 [224,629,120,562,616,200]
+ CRUSH rule 1 x 282 [298,661,380,416,35,585]
+ CRUSH rule 1 x 283 [311,606,208,50,913,678]
+ CRUSH rule 1 x 284 [771,466,371,743,672,119]
+ CRUSH rule 1 x 285 [693,362,404,676,797,531]
+ CRUSH rule 1 x 286 [364,477,285,167,270,617]
+ CRUSH rule 1 x 287 [591,611,828,995,170,987]
+ CRUSH rule 1 x 288 [965,541,848,796,251,668]
+ CRUSH rule 1 x 289 [225,551,948,877,219,167]
+ CRUSH rule 1 x 290 [577,762,777,751,291,349]
+ CRUSH rule 1 x 291 [160,903,477,381,490,559]
+ CRUSH rule 1 x 292 [873,598,216,666,222,228]
+ CRUSH rule 1 x 293 [100,234,874,47,28,452]
+ CRUSH rule 1 x 294 [285,943,379,520,725,547]
+ CRUSH rule 1 x 295 [938,262,880,327,687,3]
+ CRUSH rule 1 x 296 [850,327,86,472,1,776]
+ CRUSH rule 1 x 297 [951,53,99,558,753,228]
+ CRUSH rule 1 x 298 [173,336,85,766,910,657]
+ CRUSH rule 1 x 299 [598,591,315,386,895,296]
+ CRUSH rule 1 x 300 [531,957,62,459,156,538]
+ CRUSH rule 1 x 301 [823,628,23,858,629,808]
+ CRUSH rule 1 x 302 [184,80,780,871,531,211]
+ CRUSH rule 1 x 303 [521,766,222,830,988,275]
+ CRUSH rule 1 x 304 [980,127,807,507,555,245]
+ CRUSH rule 1 x 305 [153,816,22,927,696,911]
+ CRUSH rule 1 x 306 [423,739,664,753,178,431]
+ CRUSH rule 1 x 307 [997,557,682,456,479,631]
+ CRUSH rule 1 x 308 [991,874,534,465,330,284]
+ CRUSH rule 1 x 309 [860,394,724,858,246,866]
+ CRUSH rule 1 x 310 [589,818,546,201,94,653]
+ CRUSH rule 1 x 311 [477,774,225,590,830,559]
+ CRUSH rule 1 x 312 [887,853,950,354,58,23]
+ CRUSH rule 1 x 313 [802,646,447,416,557,118]
+ CRUSH rule 1 x 314 [654,974,229,511,562,916]
+ CRUSH rule 1 x 315 [767,227,28,740,828,156]
+ CRUSH rule 1 x 316 [778,83,733,359,858,319]
+ CRUSH rule 1 x 317 [184,418,642,986,939,675]
+ CRUSH rule 1 x 318 [525,410,500,543,212,95]
+ CRUSH rule 1 x 319 [476,724,569,382,409,521]
+ CRUSH rule 1 x 320 [149,610,697,296,818,955]
+ CRUSH rule 1 x 321 [710,79,667,671,234,4]
+ CRUSH rule 1 x 322 [175,275,323,333,744,718]
+ CRUSH rule 1 x 323 [819,604,638,792,316,544]
+ CRUSH rule 1 x 324 [16,745,511,439,272,205]
+ CRUSH rule 1 x 325 [486,400,872,873,251,68]
+ CRUSH rule 1 x 326 [613,765,207,19,359,370]
+ CRUSH rule 1 x 327 [125,289,738,408,456,784]
+ CRUSH rule 1 x 328 [807,383,476,583,645,141]
+ CRUSH rule 1 x 329 [588,938,599,432,446,840]
+ CRUSH rule 1 x 330 [932,644,41,611,209,406]
+ CRUSH rule 1 x 331 [341,953,950,537,578,862]
+ CRUSH rule 1 x 332 [153,726,459,950,466,804]
+ CRUSH rule 1 x 333 [745,845,853,860,52,615]
+ CRUSH rule 1 x 334 [614,751,807,58,396,159]
+ CRUSH rule 1 x 335 [518,721,221,283,454,187]
+ CRUSH rule 1 x 336 [389,424,77,309,5,898]
+ CRUSH rule 1 x 337 [753,508,765,720,221,807]
+ CRUSH rule 1 x 338 [128,810,490,753,406,760]
+ CRUSH rule 1 x 339 [430,308,58,751,856,823]
+ CRUSH rule 1 x 340 [541,44,630,231,289,966]
+ CRUSH rule 1 x 341 [402,26,631,439,165,928]
+ CRUSH rule 1 x 342 [982,57,992,461,131,32]
+ CRUSH rule 1 x 343 [833,412,572,732,107,805]
+ CRUSH rule 1 x 344 [784,533,792,41,642,869]
+ CRUSH rule 1 x 345 [546,300,304,691,763,556]
+ CRUSH rule 1 x 346 [302,420,428,891,357,124]
+ CRUSH rule 1 x 347 [488,778,101,217,366,442]
+ CRUSH rule 1 x 348 [903,744,937,718,85,314]
+ CRUSH rule 1 x 349 [471,547,582,306,600,486]
+ CRUSH rule 1 x 350 [348,221,823,335,383,708]
+ CRUSH rule 1 x 351 [961,582,705,346,361,32]
+ CRUSH rule 1 x 352 [728,137,461,298,36,903]
+ CRUSH rule 1 x 353 [904,202,184,447,58,294]
+ CRUSH rule 1 x 354 [345,226,319,256,544,311]
+ CRUSH rule 1 x 355 [50,430,175,43,187,458]
+ CRUSH rule 1 x 356 [87,185,55,423,829,1]
+ CRUSH rule 1 x 357 [762,459,921,473,182,231]
+ CRUSH rule 1 x 358 [908,25,280,6,808,676]
+ CRUSH rule 1 x 359 [484,15,132,121,394,423]
+ CRUSH rule 1 x 360 [173,378,337,702,145,499]
+ CRUSH rule 1 x 361 [404,577,115,25,56,914]
+ CRUSH rule 1 x 362 [403,1,422,945,132,685]
+ CRUSH rule 1 x 363 [639,911,510,162,418,294]
+ CRUSH rule 1 x 364 [752,689,610,990,665,222]
+ CRUSH rule 1 x 365 [956,999,212,230,624,84]
+ CRUSH rule 1 x 366 [860,925,924,763,687,851]
+ CRUSH rule 1 x 367 [205,609,647,665,969,720]
+ CRUSH rule 1 x 368 [301,284,810,169,78,340]
+ CRUSH rule 1 x 369 [452,658,339,217,674,210]
+ CRUSH rule 1 x 370 [11,467,695,989,394,576]
+ CRUSH rule 1 x 371 [124,487,55,514,313,411]
+ CRUSH rule 1 x 372 [253,48,979,846,207,631]
+ CRUSH rule 1 x 373 [715,605,775,748,227,493]
+ CRUSH rule 1 x 374 [191,887,920,549,223,714]
+ CRUSH rule 1 x 375 [711,385,651,665,15,71]
+ CRUSH rule 1 x 376 [597,818,49,458,415,755]
+ CRUSH rule 1 x 377 [294,256,933,771,184,861]
+ CRUSH rule 1 x 378 [34,151,681,707,552,127]
+ CRUSH rule 1 x 379 [869,136,315,378,813,153]
+ CRUSH rule 1 x 380 [294,97,575,791,690,482]
+ CRUSH rule 1 x 381 [119,710,219,827,328,886]
+ CRUSH rule 1 x 382 [69,631,508,706,697,168]
+ CRUSH rule 1 x 383 [922,588,589,925,471,601]
+ CRUSH rule 1 x 384 [221,945,671,117,857,655]
+ CRUSH rule 1 x 385 [561,737,953,723,658,368]
+ CRUSH rule 1 x 386 [335,442,788,696,507,716]
+ CRUSH rule 1 x 387 [514,43,353,88,100,842]
+ CRUSH rule 1 x 388 [587,89,157,996,915,927]
+ CRUSH rule 1 x 389 [109,641,255,466,372,563]
+ CRUSH rule 1 x 390 [925,149,421,489,599,810]
+ CRUSH rule 1 x 391 [267,87,387,527,768,873]
+ CRUSH rule 1 x 392 [382,485,370,849,936,636]
+ CRUSH rule 1 x 393 [425,721,221,753,268,463]
+ CRUSH rule 1 x 394 [898,18,38,793,173,738]
+ CRUSH rule 1 x 395 [806,876,269,679,32,744]
+ CRUSH rule 1 x 396 [790,970,437,449,875,395]
+ CRUSH rule 1 x 397 [136,363,507,613,11,30]
+ CRUSH rule 1 x 398 [914,116,558,258,722,904]
+ CRUSH rule 1 x 399 [261,94,299,202,174,622]
+ CRUSH rule 1 x 400 [661,197,338,461,977,848]
+ CRUSH rule 1 x 401 [953,979,287,803,41,349]
+ CRUSH rule 1 x 402 [738,819,618,522,667,334]
+ CRUSH rule 1 x 403 [573,238,425,546,130,68]
+ CRUSH rule 1 x 404 [526,848,790,253,922,820]
+ CRUSH rule 1 x 405 [582,505,330,334,201,110]
+ CRUSH rule 1 x 406 [768,324,493,60,186,165]
+ CRUSH rule 1 x 407 [260,951,437,587,692,648]
+ CRUSH rule 1 x 408 [657,81,770,734,830,821]
+ CRUSH rule 1 x 409 [498,89,182,423,672,152]
+ CRUSH rule 1 x 410 [28,793,737,352,166,645]
+ CRUSH rule 1 x 411 [684,992,60,659,769,267]
+ CRUSH rule 1 x 412 [261,958,699,950,165,14]
+ CRUSH rule 1 x 413 [891,835,297,441,384,979]
+ CRUSH rule 1 x 414 [127,459,119,965,662,594]
+ CRUSH rule 1 x 415 [272,540,631,328,609,568]
+ CRUSH rule 1 x 416 [739,617,115,530,339,371]
+ CRUSH rule 1 x 417 [106,209,157,878,117,128]
+ CRUSH rule 1 x 418 [525,441,147,390,320,300]
+ CRUSH rule 1 x 419 [603,673,615,465,266,855]
+ CRUSH rule 1 x 420 [988,213,251,226,209,245]
+ CRUSH rule 1 x 421 [761,521,748,368,923,992]
+ CRUSH rule 1 x 422 [317,160,924,548,198,709]
+ CRUSH rule 1 x 423 [137,807,168,472,619,443]
+ CRUSH rule 1 x 424 [920,37,146,263,598,748]
+ CRUSH rule 1 x 425 [277,693,285,221,478,165]
+ CRUSH rule 1 x 426 [485,936,407,854,726,524]
+ CRUSH rule 1 x 427 [242,515,9,564,174,453]
+ CRUSH rule 1 x 428 [632,635,26,473,494,478]
+ CRUSH rule 1 x 429 [641,73,465,127,171,397]
+ CRUSH rule 1 x 430 [626,585,6,387,881,583]
+ CRUSH rule 1 x 431 [697,76,753,570,964,339]
+ CRUSH rule 1 x 432 [590,526,306,283,656,728]
+ CRUSH rule 1 x 433 [284,387,149,817,886,714]
+ CRUSH rule 1 x 434 [538,985,79,953,770,468]
+ CRUSH rule 1 x 435 [30,318,593,635,975,833]
+ CRUSH rule 1 x 436 [164,919,851,693,0,874]
+ CRUSH rule 1 x 437 [322,212,163,606,302,282]
+ CRUSH rule 1 x 438 [142,392,85,594,376,419]
+ CRUSH rule 1 x 439 [119,370,68,443,997,837]
+ CRUSH rule 1 x 440 [333,403,187,863,475,844]
+ CRUSH rule 1 x 441 [477,727,906,145,429,91]
+ CRUSH rule 1 x 442 [274,590,933,244,434,49]
+ CRUSH rule 1 x 443 [983,748,574,718,700,442]
+ CRUSH rule 1 x 444 [536,509,431,146,170,149]
+ CRUSH rule 1 x 445 [485,931,528,209,964,753]
+ CRUSH rule 1 x 446 [345,634,42,294,711,376]
+ CRUSH rule 1 x 447 [61,845,767,600,321,716]
+ CRUSH rule 1 x 448 [333,232,292,846,364,951]
+ CRUSH rule 1 x 449 [680,16,484,670,851,500]
+ CRUSH rule 1 x 450 [235,214,79,423,96,822]
+ CRUSH rule 1 x 451 [961,468,333,640,823,151]
+ CRUSH rule 1 x 452 [525,479,153,528,570,806]
+ CRUSH rule 1 x 453 [138,466,302,86,249,154]
+ CRUSH rule 1 x 454 [137,625,215,402,389,914]
+ CRUSH rule 1 x 455 [173,150,997,16,846,888]
+ CRUSH rule 1 x 456 [235,226,238,258,347,784]
+ CRUSH rule 1 x 457 [450,577,253,413,717,609]
+ CRUSH rule 1 x 458 [195,537,91,814,351,90]
+ CRUSH rule 1 x 459 [381,555,312,573,915,623]
+ CRUSH rule 1 x 460 [972,730,534,678,756,692]
+ CRUSH rule 1 x 461 [506,279,142,830,784,124]
+ CRUSH rule 1 x 462 [692,959,578,57,983,299]
+ CRUSH rule 1 x 463 [788,667,949,550,685,702]
+ CRUSH rule 1 x 464 [133,122,588,999,270,880]
+ CRUSH rule 1 x 465 [971,190,230,777,452,914]
+ CRUSH rule 1 x 466 [394,576,148,157,103,822]
+ CRUSH rule 1 x 467 [517,28,366,362,984,521]
+ CRUSH rule 1 x 468 [829,143,874,225,162,413]
+ CRUSH rule 1 x 469 [987,936,106,725,633,238]
+ CRUSH rule 1 x 470 [107,982,56,889,67,65]
+ CRUSH rule 1 x 471 [181,897,629,860,307,116]
+ CRUSH rule 1 x 472 [547,512,172,24,705,837]
+ CRUSH rule 1 x 473 [760,997,824,905,888,755]
+ CRUSH rule 1 x 474 [787,418,743,628,272,341]
+ CRUSH rule 1 x 475 [662,312,253,617,105,58]
+ CRUSH rule 1 x 476 [110,495,185,508,961,837]
+ CRUSH rule 1 x 477 [393,954,834,132,841,367]
+ CRUSH rule 1 x 478 [246,483,480,644,985,420]
+ CRUSH rule 1 x 479 [70,929,697,931,744,487]
+ CRUSH rule 1 x 480 [753,119,961,607,317,717]
+ CRUSH rule 1 x 481 [470,429,677,242,574,757]
+ CRUSH rule 1 x 482 [451,566,961,675,354,746]
+ CRUSH rule 1 x 483 [816,72,371,278,635,30]
+ CRUSH rule 1 x 484 [540,454,389,31,654,494]
+ CRUSH rule 1 x 485 [74,582,624,684,566,677]
+ CRUSH rule 1 x 486 [958,595,199,763,715,973]
+ CRUSH rule 1 x 487 [228,302,804,833,876,647]
+ CRUSH rule 1 x 488 [180,529,722,956,353,890]
+ CRUSH rule 1 x 489 [47,617,812,187,291,828]
+ CRUSH rule 1 x 490 [905,822,479,124,750,843]
+ CRUSH rule 1 x 491 [892,370,609,998,433,957]
+ CRUSH rule 1 x 492 [588,959,127,948,505,936]
+ CRUSH rule 1 x 493 [353,461,593,291,301,830]
+ CRUSH rule 1 x 494 [378,848,443,368,507,423]
+ CRUSH rule 1 x 495 [845,653,768,234,405,367]
+ CRUSH rule 1 x 496 [13,988,0,691,389,757]
+ CRUSH rule 1 x 497 [796,877,788,394,648,829]
+ CRUSH rule 1 x 498 [412,337,270,705,511,227]
+ CRUSH rule 1 x 499 [330,695,8,74,618,101]
+ CRUSH rule 1 x 500 [820,272,547,765,755,96]
+ CRUSH rule 1 x 501 [110,44,132,442,294,423]
+ CRUSH rule 1 x 502 [336,595,650,274,993,312]
+ CRUSH rule 1 x 503 [922,211,157,722,502,971]
+ CRUSH rule 1 x 504 [483,52,122,432,778,461]
+ CRUSH rule 1 x 505 [482,598,224,279,480,310]
+ CRUSH rule 1 x 506 [493,123,43,856,936,622]
+ CRUSH rule 1 x 507 [12,598,264,422,416,947]
+ CRUSH rule 1 x 508 [227,157,611,301,223,746]
+ CRUSH rule 1 x 509 [807,242,363,122,582,530]
+ CRUSH rule 1 x 510 [134,437,227,75,313,351]
+ CRUSH rule 1 x 511 [212,54,83,799,457,218]
+ CRUSH rule 1 x 512 [236,630,758,752,361,249]
+ CRUSH rule 1 x 513 [994,693,644,938,846,685]
+ CRUSH rule 1 x 514 [45,508,831,19,817,52]
+ CRUSH rule 1 x 515 [504,138,480,272,530,377]
+ CRUSH rule 1 x 516 [285,409,136,570,841,610]
+ CRUSH rule 1 x 517 [300,232,23,906,438,236]
+ CRUSH rule 1 x 518 [397,674,98,898,967,113]
+ CRUSH rule 1 x 519 [86,750,772,913,101,864]
+ CRUSH rule 1 x 520 [900,833,614,130,261,885]
+ CRUSH rule 1 x 521 [31,47,236,751,911,599]
+ CRUSH rule 1 x 522 [390,16,280,144,291,175]
+ CRUSH rule 1 x 523 [618,308,424,590,300,206]
+ CRUSH rule 1 x 524 [635,189,687,963,601,518]
+ CRUSH rule 1 x 525 [311,916,699,262,775,32]
+ CRUSH rule 1 x 526 [48,738,227,718,244,942]
+ CRUSH rule 1 x 527 [202,851,889,216,763,351]
+ CRUSH rule 1 x 528 [565,827,590,273,918,106]
+ CRUSH rule 1 x 529 [934,864,241,43,466,924]
+ CRUSH rule 1 x 530 [502,934,298,670,986,360]
+ CRUSH rule 1 x 531 [681,627,942,487,288,561]
+ CRUSH rule 1 x 532 [422,6,147,205,861,141]
+ CRUSH rule 1 x 533 [863,68,364,983,247,199]
+ CRUSH rule 1 x 534 [962,931,775,172,663,119]
+ CRUSH rule 1 x 535 [89,565,397,693,839,632]
+ CRUSH rule 1 x 536 [499,351,760,458,918,86]
+ CRUSH rule 1 x 537 [676,547,787,311,867,748]
+ CRUSH rule 1 x 538 [58,644,571,649,941,7]
+ CRUSH rule 1 x 539 [837,953,457,711,458,621]
+ CRUSH rule 1 x 540 [831,50,132,213,197,709]
+ CRUSH rule 1 x 541 [582,757,121,525,532,963]
+ CRUSH rule 1 x 542 [472,132,790,997,948,269]
+ CRUSH rule 1 x 543 [382,272,797,330,315,748]
+ CRUSH rule 1 x 544 [947,930,496,883,509,219]
+ CRUSH rule 1 x 545 [425,570,305,77,821,422]
+ CRUSH rule 1 x 546 [18,65,529,437,343,547]
+ CRUSH rule 1 x 547 [445,715,600,472,213,851]
+ CRUSH rule 1 x 548 [367,569,980,167,627,442]
+ CRUSH rule 1 x 549 [125,715,671,817,285,420]
+ CRUSH rule 1 x 550 [425,599,744,199,923,222]
+ CRUSH rule 1 x 551 [44,1,528,922,944,115]
+ CRUSH rule 1 x 552 [246,104,68,239,123,427]
+ CRUSH rule 1 x 553 [71,703,615,28,593,724]
+ CRUSH rule 1 x 554 [207,124,217,166,525,226]
+ CRUSH rule 1 x 555 [570,28,317,420,931,413]
+ CRUSH rule 1 x 556 [674,152,421,79,215,347]
+ CRUSH rule 1 x 557 [347,817,191,391,741,571]
+ CRUSH rule 1 x 558 [627,426,369,692,815,371]
+ CRUSH rule 1 x 559 [940,630,924,242,224,912]
+ CRUSH rule 1 x 560 [295,903,541,29,245,753]
+ CRUSH rule 1 x 561 [506,682,384,637,878,991]
+ CRUSH rule 1 x 562 [718,529,87,729,842,341]
+ CRUSH rule 1 x 563 [552,332,747,206,274,871]
+ CRUSH rule 1 x 564 [835,769,736,486,630,209]
+ CRUSH rule 1 x 565 [8,167,539,182,607,62]
+ CRUSH rule 1 x 566 [600,481,301,263,90,450]
+ CRUSH rule 1 x 567 [999,994,509,899,947,24]
+ CRUSH rule 1 x 568 [252,431,157,62,601,863]
+ CRUSH rule 1 x 569 [643,218,943,455,83,969]
+ CRUSH rule 1 x 570 [617,635,765,422,250,156]
+ CRUSH rule 1 x 571 [757,80,59,98,328,700]
+ CRUSH rule 1 x 572 [299,348,575,889,943,675]
+ CRUSH rule 1 x 573 [25,505,270,167,58,901]
+ CRUSH rule 1 x 574 [215,431,624,177,628,814]
+ CRUSH rule 1 x 575 [225,252,611,546,32,815]
+ CRUSH rule 1 x 576 [627,94,159,857,430,691]
+ CRUSH rule 1 x 577 [237,809,778,636,61,167]
+ CRUSH rule 1 x 578 [885,313,120,344,771,614]
+ CRUSH rule 1 x 579 [924,575,787,831,47,996]
+ CRUSH rule 1 x 580 [718,51,766,121,118,471]
+ CRUSH rule 1 x 581 [219,807,129,571,856,179]
+ CRUSH rule 1 x 582 [893,701,598,863,285,829]
+ CRUSH rule 1 x 583 [246,930,964,170,993,409]
+ CRUSH rule 1 x 584 [336,432,680,175,495,839]
+ CRUSH rule 1 x 585 [324,999,397,485,457,527]
+ CRUSH rule 1 x 586 [558,230,976,541,816,72]
+ CRUSH rule 1 x 587 [985,830,597,21,308,890]
+ CRUSH rule 1 x 588 [211,544,57,134,162,496]
+ CRUSH rule 1 x 589 [129,21,112,190,885,844]
+ CRUSH rule 1 x 590 [467,969,652,593,287,76]
+ CRUSH rule 1 x 591 [758,514,316,164,35,110]
+ CRUSH rule 1 x 592 [525,253,190,443,315,603]
+ CRUSH rule 1 x 593 [601,885,339,152,297,223]
+ CRUSH rule 1 x 594 [227,60,450,30,717,840]
+ CRUSH rule 1 x 595 [720,854,496,912,80,655]
+ CRUSH rule 1 x 596 [751,195,997,77,261,490]
+ CRUSH rule 1 x 597 [129,574,714,8,789,847]
+ CRUSH rule 1 x 598 [679,207,604,396,841,284]
+ CRUSH rule 1 x 599 [668,315,683,349,681,253]
+ CRUSH rule 1 x 600 [143,396,464,444,59,57]
+ CRUSH rule 1 x 601 [326,573,873,902,136,921]
+ CRUSH rule 1 x 602 [860,281,875,535,672,474]
+ CRUSH rule 1 x 603 [709,328,445,349,190,455]
+ CRUSH rule 1 x 604 [571,62,814,95,866,978]
+ CRUSH rule 1 x 605 [252,739,860,27,313,362]
+ CRUSH rule 1 x 606 [339,236,759,842,67,644]
+ CRUSH rule 1 x 607 [590,248,759,868,433,398]
+ CRUSH rule 1 x 608 [145,635,309,467,875,115]
+ CRUSH rule 1 x 609 [973,547,223,79,762,863]
+ CRUSH rule 1 x 610 [435,816,961,983,255,886]
+ CRUSH rule 1 x 611 [559,283,422,584,176,429]
+ CRUSH rule 1 x 612 [273,149,123,576,911,270]
+ CRUSH rule 1 x 613 [828,614,642,674,33,361]
+ CRUSH rule 1 x 614 [478,748,393,34,171,80]
+ CRUSH rule 1 x 615 [392,155,144,326,626,134]
+ CRUSH rule 1 x 616 [778,637,452,248,15,888]
+ CRUSH rule 1 x 617 [622,713,996,833,611,407]
+ CRUSH rule 1 x 618 [149,877,270,329,180,327]
+ CRUSH rule 1 x 619 [604,163,656,409,322,848]
+ CRUSH rule 1 x 620 [181,23,409,198,64,898]
+ CRUSH rule 1 x 621 [735,902,386,237,939,475]
+ CRUSH rule 1 x 622 [661,824,717,568,858,583]
+ CRUSH rule 1 x 623 [142,121,643,61,695,852]
+ CRUSH rule 1 x 624 [360,716,420,398,49,717]
+ CRUSH rule 1 x 625 [541,167,385,1,601,481]
+ CRUSH rule 1 x 626 [364,431,610,363,535,747]
+ CRUSH rule 1 x 627 [458,137,557,410,287,749]
+ CRUSH rule 1 x 628 [250,350,556,497,821,65]
+ CRUSH rule 1 x 629 [928,160,710,572,365,772]
+ CRUSH rule 1 x 630 [243,19,918,556,601,16]
+ CRUSH rule 1 x 631 [438,221,574,676,797,580]
+ CRUSH rule 1 x 632 [797,368,247,5,32,102]
+ CRUSH rule 1 x 633 [993,749,525,485,27,330]
+ CRUSH rule 1 x 634 [239,351,633,299,651,678]
+ CRUSH rule 1 x 635 [640,965,25,961,306,172]
+ CRUSH rule 1 x 636 [173,290,297,991,937,823]
+ CRUSH rule 1 x 637 [0,918,98,108,111,495]
+ CRUSH rule 1 x 638 [702,235,424,900,983,754]
+ CRUSH rule 1 x 639 [475,687,31,785,918,611]
+ CRUSH rule 1 x 640 [31,664,399,677,123,609]
+ CRUSH rule 1 x 641 [296,473,108,963,341,876]
+ CRUSH rule 1 x 642 [894,273,427,606,677,670]
+ CRUSH rule 1 x 643 [117,111,732,191,114,153]
+ CRUSH rule 1 x 644 [438,336,327,512,599,862]
+ CRUSH rule 1 x 645 [982,702,351,573,907,915]
+ CRUSH rule 1 x 646 [334,804,146,842,697,638]
+ CRUSH rule 1 x 647 [933,787,185,334,752,285]
+ CRUSH rule 1 x 648 [22,444,400,862,207,842]
+ CRUSH rule 1 x 649 [503,229,213,460,639,760]
+ CRUSH rule 1 x 650 [328,659,420,443,739,950]
+ CRUSH rule 1 x 651 [3,880,823,123,378,585]
+ CRUSH rule 1 x 652 [495,977,563,733,92,997]
+ CRUSH rule 1 x 653 [185,718,804,280,975,912]
+ CRUSH rule 1 x 654 [130,528,380,81,906,511]
+ CRUSH rule 1 x 655 [560,872,454,504,319,284]
+ CRUSH rule 1 x 656 [219,885,178,981,863,508]
+ CRUSH rule 1 x 657 [233,684,813,490,208,941]
+ CRUSH rule 1 x 658 [778,6,756,380,750,836]
+ CRUSH rule 1 x 659 [240,663,306,540,789,902]
+ CRUSH rule 1 x 660 [244,855,196,147,678,323]
+ CRUSH rule 1 x 661 [184,270,128,398,910,230]
+ CRUSH rule 1 x 662 [65,883,921,438,79,957]
+ CRUSH rule 1 x 663 [323,721,594,812,43,992]
+ CRUSH rule 1 x 664 [865,113,512,51,427,123]
+ CRUSH rule 1 x 665 [420,850,591,475,202,733]
+ CRUSH rule 1 x 666 [319,767,246,3,369,493]
+ CRUSH rule 1 x 667 [875,39,343,100,829,2]
+ CRUSH rule 1 x 668 [331,122,263,599,355,484]
+ CRUSH rule 1 x 669 [915,521,402,747,673,445]
+ CRUSH rule 1 x 670 [845,659,943,447,401,322]
+ CRUSH rule 1 x 671 [108,634,527,363,856,238]
+ CRUSH rule 1 x 672 [578,216,110,589,302,137]
+ CRUSH rule 1 x 673 [442,74,579,797,622,950]
+ CRUSH rule 1 x 674 [588,364,281,308,645,631]
+ CRUSH rule 1 x 675 [489,698,744,671,870,174]
+ CRUSH rule 1 x 676 [928,911,40,180,722,729]
+ CRUSH rule 1 x 677 [399,269,692,131,615,136]
+ CRUSH rule 1 x 678 [546,752,544,155,5,463]
+ CRUSH rule 1 x 679 [988,25,275,433,628,57]
+ CRUSH rule 1 x 680 [335,963,382,486,749,257]
+ CRUSH rule 1 x 681 [690,462,623,466,49,471]
+ CRUSH rule 1 x 682 [196,588,154,257,807,776]
+ CRUSH rule 1 x 683 [627,25,421,160,873,102]
+ CRUSH rule 1 x 684 [38,804,592,158,991,264]
+ CRUSH rule 1 x 685 [841,368,548,362,166,211]
+ CRUSH rule 1 x 686 [336,287,525,440,166,993]
+ CRUSH rule 1 x 687 [20,682,924,653,356,16]
+ CRUSH rule 1 x 688 [463,371,780,556,385,883]
+ CRUSH rule 1 x 689 [569,250,78,816,847,775]
+ CRUSH rule 1 x 690 [551,144,587,263,378,394]
+ CRUSH rule 1 x 691 [766,464,446,533,449,541]
+ CRUSH rule 1 x 692 [739,634,18,245,624,35]
+ CRUSH rule 1 x 693 [339,297,118,330,817,91]
+ CRUSH rule 1 x 694 [405,26,830,181,533,166]
+ CRUSH rule 1 x 695 [622,576,597,535,600,593]
+ CRUSH rule 1 x 696 [558,902,689,13,715,28]
+ CRUSH rule 1 x 697 [818,222,406,691,427,863]
+ CRUSH rule 1 x 698 [178,48,402,233,841,604]
+ CRUSH rule 1 x 699 [450,244,180,919,401,332]
+ CRUSH rule 1 x 700 [502,771,987,706,416,240]
+ CRUSH rule 1 x 701 [4,612,782,216,853,303]
+ CRUSH rule 1 x 702 [177,630,232,923,281,708]
+ CRUSH rule 1 x 703 [354,178,389,393,778,803]
+ CRUSH rule 1 x 704 [646,601,156,171,603,116]
+ CRUSH rule 1 x 705 [921,401,890,265,244,690]
+ CRUSH rule 1 x 706 [652,877,562,452,26,323]
+ CRUSH rule 1 x 707 [345,745,67,716,789,576]
+ CRUSH rule 1 x 708 [333,607,180,469,170,555]
+ CRUSH rule 1 x 709 [45,187,302,115,896,579]
+ CRUSH rule 1 x 710 [94,855,43,199,18,948]
+ CRUSH rule 1 x 711 [227,653,731,150,502,842]
+ CRUSH rule 1 x 712 [398,953,136,870,181,408]
+ CRUSH rule 1 x 713 [116,800,503,662,635,579]
+ CRUSH rule 1 x 714 [111,629,866,709,902,557]
+ CRUSH rule 1 x 715 [531,291,486,382,192,807]
+ CRUSH rule 1 x 716 [169,541,291,42,343,724]
+ CRUSH rule 1 x 717 [417,446,994,894,239,494]
+ CRUSH rule 1 x 718 [992,383,298,844,377,463]
+ CRUSH rule 1 x 719 [936,674,324,759,194,409]
+ CRUSH rule 1 x 720 [370,188,174,464,644,218]
+ CRUSH rule 1 x 721 [320,859,278,259,170,957]
+ CRUSH rule 1 x 722 [7,2,673,129,96,445]
+ CRUSH rule 1 x 723 [270,553,831,662,38,101]
+ CRUSH rule 1 x 724 [666,822,708,895,633,800]
+ CRUSH rule 1 x 725 [794,406,875,459,981,751]
+ CRUSH rule 1 x 726 [420,556,341,292,240,68]
+ CRUSH rule 1 x 727 [561,461,129,635,965,610]
+ CRUSH rule 1 x 728 [951,330,196,756,589,849]
+ CRUSH rule 1 x 729 [656,644,436,591,27,119]
+ CRUSH rule 1 x 730 [3,558,629,184,50,765]
+ CRUSH rule 1 x 731 [852,89,75,735,713,113]
+ CRUSH rule 1 x 732 [983,840,869,976,697,307]
+ CRUSH rule 1 x 733 [285,396,388,122,387,364]
+ CRUSH rule 1 x 734 [125,510,402,640,676,501]
+ CRUSH rule 1 x 735 [417,773,686,504,459,912]
+ CRUSH rule 1 x 736 [749,396,632,550,779,109]
+ CRUSH rule 1 x 737 [644,991,946,135,448,903]
+ CRUSH rule 1 x 738 [449,683,290,220,245,525]
+ CRUSH rule 1 x 739 [341,220,641,454,740,661]
+ CRUSH rule 1 x 740 [874,524,674,650,472,282]
+ CRUSH rule 1 x 741 [189,472,712,798,715,757]
+ CRUSH rule 1 x 742 [912,581,114,695,730,21]
+ CRUSH rule 1 x 743 [654,914,425,441,763,39]
+ CRUSH rule 1 x 744 [725,295,579,377,162,447]
+ CRUSH rule 1 x 745 [787,858,850,506,612,735]
+ CRUSH rule 1 x 746 [757,848,704,30,47,940]
+ CRUSH rule 1 x 747 [700,81,867,681,801,64]
+ CRUSH rule 1 x 748 [557,436,238,664,293,865]
+ CRUSH rule 1 x 749 [772,622,337,42,156,302]
+ CRUSH rule 1 x 750 [946,97,376,677,316,670]
+ CRUSH rule 1 x 751 [996,618,343,911,83,22]
+ CRUSH rule 1 x 752 [746,887,695,868,610,950]
+ CRUSH rule 1 x 753 [741,14,463,479,172,192]
+ CRUSH rule 1 x 754 [648,349,333,355,65,63]
+ CRUSH rule 1 x 755 [157,460,466,187,959,674]
+ CRUSH rule 1 x 756 [416,97,197,497,227,3]
+ CRUSH rule 1 x 757 [599,839,776,410,256,823]
+ CRUSH rule 1 x 758 [994,218,620,256,361,749]
+ CRUSH rule 1 x 759 [959,682,514,745,100,519]
+ CRUSH rule 1 x 760 [518,943,215,83,706,137]
+ CRUSH rule 1 x 761 [285,849,420,324,987,338]
+ CRUSH rule 1 x 762 [591,313,41,335,110,696]
+ CRUSH rule 1 x 763 [908,411,200,740,292,295]
+ CRUSH rule 1 x 764 [787,234,894,485,883,711]
+ CRUSH rule 1 x 765 [327,921,882,393,444,792]
+ CRUSH rule 1 x 766 [84,161,878,704,416,144]
+ CRUSH rule 1 x 767 [370,895,702,701,890,2]
+ CRUSH rule 1 x 768 [826,760,879,864,460,474]
+ CRUSH rule 1 x 769 [67,768,663,735,814,66]
+ CRUSH rule 1 x 770 [593,909,482,259,5,550]
+ CRUSH rule 1 x 771 [309,935,121,578,937,685]
+ CRUSH rule 1 x 772 [12,125,797,301,348,419]
+ CRUSH rule 1 x 773 [253,466,820,549,591,193]
+ CRUSH rule 1 x 774 [164,390,705,109,881,505]
+ CRUSH rule 1 x 775 [703,47,43,973,643,406]
+ CRUSH rule 1 x 776 [728,231,80,916,2,850]
+ CRUSH rule 1 x 777 [981,621,568,729,869,952]
+ CRUSH rule 1 x 778 [411,456,544,597,789,784]
+ CRUSH rule 1 x 779 [346,121,519,921,587,48]
+ CRUSH rule 1 x 780 [476,39,288,381,303,29]
+ CRUSH rule 1 x 781 [10,130,585,844,729,705]
+ CRUSH rule 1 x 782 [462,246,581,902,623,877]
+ CRUSH rule 1 x 783 [580,373,153,775,668,661]
+ CRUSH rule 1 x 784 [413,113,978,990,994,56]
+ CRUSH rule 1 x 785 [341,856,332,354,59,581]
+ CRUSH rule 1 x 786 [411,140,313,393,215,618]
+ CRUSH rule 1 x 787 [605,522,211,813,636,224]
+ CRUSH rule 1 x 788 [226,545,35,142,726,851]
+ CRUSH rule 1 x 789 [545,320,414,702,731,277]
+ CRUSH rule 1 x 790 [414,748,816,327,130,115]
+ CRUSH rule 1 x 791 [660,906,406,697,916,322]
+ CRUSH rule 1 x 792 [287,392,514,204,75,789]
+ CRUSH rule 1 x 793 [631,133,850,713,720,487]
+ CRUSH rule 1 x 794 [931,517,543,210,963,898]
+ CRUSH rule 1 x 795 [551,962,477,948,425,434]
+ CRUSH rule 1 x 796 [814,4,95,27,368,300]
+ CRUSH rule 1 x 797 [64,201,299,734,605,864]
+ CRUSH rule 1 x 798 [422,530,114,431,565,716]
+ CRUSH rule 1 x 799 [824,32,679,562,266,549]
+ CRUSH rule 1 x 800 [862,623,489,637,861,196]
+ CRUSH rule 1 x 801 [145,550,329,324,734,160]
+ CRUSH rule 1 x 802 [570,19,847,308,387,518]
+ CRUSH rule 1 x 803 [151,812,662,358,880,349]
+ CRUSH rule 1 x 804 [467,93,264,863,176,842]
+ CRUSH rule 1 x 805 [621,223,938,809,591,686]
+ CRUSH rule 1 x 806 [898,957,805,430,499,584]
+ CRUSH rule 1 x 807 [354,531,422,159,921,431]
+ CRUSH rule 1 x 808 [7,96,76,897,446,2]
+ CRUSH rule 1 x 809 [70,734,719,56,687,21]
+ CRUSH rule 1 x 810 [701,18,972,327,771,649]
+ CRUSH rule 1 x 811 [248,547,103,728,901,264]
+ CRUSH rule 1 x 812 [230,576,821,566,993,762]
+ CRUSH rule 1 x 813 [805,114,683,629,605,462]
+ CRUSH rule 1 x 814 [54,619,973,741,497,894]
+ CRUSH rule 1 x 815 [679,412,613,132,969,411]
+ CRUSH rule 1 x 816 [919,448,826,414,36,289]
+ CRUSH rule 1 x 817 [765,830,436,521,332,458]
+ CRUSH rule 1 x 818 [415,566,644,687,692,414]
+ CRUSH rule 1 x 819 [721,319,865,750,546,859]
+ CRUSH rule 1 x 820 [218,301,333,190,686,179]
+ CRUSH rule 1 x 821 [185,795,680,953,329,750]
+ CRUSH rule 1 x 822 [356,261,54,522,900,103]
+ CRUSH rule 1 x 823 [220,281,549,456,64,306]
+ CRUSH rule 1 x 824 [292,809,887,74,776,788]
+ CRUSH rule 1 x 825 [949,778,101,311,110,480]
+ CRUSH rule 1 x 826 [767,818,833,927,356,954]
+ CRUSH rule 1 x 827 [631,83,406,635,657,713]
+ CRUSH rule 1 x 828 [288,986,445,26,414,607]
+ CRUSH rule 1 x 829 [990,667,915,694,974,453]
+ CRUSH rule 1 x 830 [152,571,778,505,685,209]
+ CRUSH rule 1 x 831 [814,563,630,97,582,107]
+ CRUSH rule 1 x 832 [235,641,616,110,979,844]
+ CRUSH rule 1 x 833 [657,565,922,140,825,457]
+ CRUSH rule 1 x 834 [907,231,644,13,617,130]
+ CRUSH rule 1 x 835 [784,262,771,264,612,238]
+ CRUSH rule 1 x 836 [951,158,366,710,43,427]
+ CRUSH rule 1 x 837 [556,498,334,633,895,627]
+ CRUSH rule 1 x 838 [329,274,964,547,119,342]
+ CRUSH rule 1 x 839 [568,209,939,364,658,747]
+ CRUSH rule 1 x 840 [45,579,842,70,655,862]
+ CRUSH rule 1 x 841 [652,702,24,605,152,93]
+ CRUSH rule 1 x 842 [629,984,314,895,408,897]
+ CRUSH rule 1 x 843 [799,690,688,648,151,812]
+ CRUSH rule 1 x 844 [694,600,534,700,569,11]
+ CRUSH rule 1 x 845 [332,30,179,93,951,324]
+ CRUSH rule 1 x 846 [452,251,712,719,404,739]
+ CRUSH rule 1 x 847 [399,681,847,739,13,555]
+ CRUSH rule 1 x 848 [303,138,440,346,547,216]
+ CRUSH rule 1 x 849 [666,346,708,873,64,694]
+ CRUSH rule 1 x 850 [644,511,345,844,545,337]
+ CRUSH rule 1 x 851 [527,546,737,425,100,331]
+ CRUSH rule 1 x 852 [31,809,94,618,156,853]
+ CRUSH rule 1 x 853 [483,330,869,184,46,942]
+ CRUSH rule 1 x 854 [697,953,968,143,502,955]
+ CRUSH rule 1 x 855 [837,996,239,621,32,191]
+ CRUSH rule 1 x 856 [712,40,547,430,195,857]
+ CRUSH rule 1 x 857 [77,984,576,551,568,96]
+ CRUSH rule 1 x 858 [412,384,841,465,572,576]
+ CRUSH rule 1 x 859 [173,760,26,300,87,567]
+ CRUSH rule 1 x 860 [776,429,328,917,658,783]
+ CRUSH rule 1 x 861 [705,405,477,50,73,714]
+ CRUSH rule 1 x 862 [809,44,788,938,964,177]
+ CRUSH rule 1 x 863 [349,496,963,178,675,853]
+ CRUSH rule 1 x 864 [717,858,101,239,992,244]
+ CRUSH rule 1 x 865 [857,603,586,262,550,289]
+ CRUSH rule 1 x 866 [394,304,71,96,642,155]
+ CRUSH rule 1 x 867 [640,773,663,974,261,296]
+ CRUSH rule 1 x 868 [613,950,712,663,649,460]
+ CRUSH rule 1 x 869 [973,889,524,22,671,477]
+ CRUSH rule 1 x 870 [505,35,386,498,348,503]
+ CRUSH rule 1 x 871 [239,264,262,773,781,734]
+ CRUSH rule 1 x 872 [21,767,456,748,783,797]
+ CRUSH rule 1 x 873 [954,666,980,264,435,233]
+ CRUSH rule 1 x 874 [54,510,947,1,500,119]
+ CRUSH rule 1 x 875 [809,418,452,462,88,673]
+ CRUSH rule 1 x 876 [483,457,61,248,523,277]
+ CRUSH rule 1 x 877 [542,531,952,939,710,179]
+ CRUSH rule 1 x 878 [217,674,857,644,678,809]
+ CRUSH rule 1 x 879 [999,475,134,250,319,357]
+ CRUSH rule 1 x 880 [678,573,935,385,570,651]
+ CRUSH rule 1 x 881 [394,835,789,802,587,155]
+ CRUSH rule 1 x 882 [467,382,353,56,979,674]
+ CRUSH rule 1 x 883 [802,744,237,337,50,96]
+ CRUSH rule 1 x 884 [653,660,638,700,31,558]
+ CRUSH rule 1 x 885 [898,704,307,445,879,872]
+ CRUSH rule 1 x 886 [434,357,938,641,737,8]
+ CRUSH rule 1 x 887 [297,226,711,428,370,318]
+ CRUSH rule 1 x 888 [863,324,443,213,902,25]
+ CRUSH rule 1 x 889 [105,102,308,163,947,548]
+ CRUSH rule 1 x 890 [550,248,606,704,615,708]
+ CRUSH rule 1 x 891 [575,928,880,891,826,763]
+ CRUSH rule 1 x 892 [259,862,133,271,292,162]
+ CRUSH rule 1 x 893 [902,880,543,542,37,942]
+ CRUSH rule 1 x 894 [180,169,916,43,945,713]
+ CRUSH rule 1 x 895 [725,849,182,129,177,272]
+ CRUSH rule 1 x 896 [951,34,874,537,969,123]
+ CRUSH rule 1 x 897 [810,352,73,939,943,895]
+ CRUSH rule 1 x 898 [979,433,719,411,787,359]
+ CRUSH rule 1 x 899 [685,668,534,932,399,156]
+ CRUSH rule 1 x 900 [530,978,41,894,941,681]
+ CRUSH rule 1 x 901 [740,107,336,175,574,706]
+ CRUSH rule 1 x 902 [800,743,693,310,67,111]
+ CRUSH rule 1 x 903 [230,267,842,266,550,769]
+ CRUSH rule 1 x 904 [346,949,460,973,696,91]
+ CRUSH rule 1 x 905 [530,397,619,958,576,973]
+ CRUSH rule 1 x 906 [80,426,138,672,73,776]
+ CRUSH rule 1 x 907 [365,968,475,297,296,724]
+ CRUSH rule 1 x 908 [204,832,742,809,862,745]
+ CRUSH rule 1 x 909 [883,989,146,959,366,59]
+ CRUSH rule 1 x 910 [549,593,249,853,792,769]
+ CRUSH rule 1 x 911 [325,847,352,214,851,732]
+ CRUSH rule 1 x 912 [874,888,582,796,557,601]
+ CRUSH rule 1 x 913 [331,463,342,574,989,362]
+ CRUSH rule 1 x 914 [836,468,601,732,607,275]
+ CRUSH rule 1 x 915 [245,228,100,661,799,13]
+ CRUSH rule 1 x 916 [77,967,364,435,27,474]
+ CRUSH rule 1 x 917 [239,60,866,221,772,967]
+ CRUSH rule 1 x 918 [988,115,922,80,201,544]
+ CRUSH rule 1 x 919 [783,139,696,1,848,169]
+ CRUSH rule 1 x 920 [623,408,685,953,974,696]
+ CRUSH rule 1 x 921 [105,799,144,90,399,373]
+ CRUSH rule 1 x 922 [887,505,652,348,514,806]
+ CRUSH rule 1 x 923 [223,318,552,458,743,871]
+ CRUSH rule 1 x 924 [25,778,366,333,163,801]
+ CRUSH rule 1 x 925 [912,601,297,682,770,173]
+ CRUSH rule 1 x 926 [968,133,96,144,814,155]
+ CRUSH rule 1 x 927 [277,724,214,988,690,342]
+ CRUSH rule 1 x 928 [554,203,658,789,298,299]
+ CRUSH rule 1 x 929 [761,802,367,528,758,522]
+ CRUSH rule 1 x 930 [814,61,788,736,660,491]
+ CRUSH rule 1 x 931 [29,193,61,41,343,664]
+ CRUSH rule 1 x 932 [446,198,862,534,168,35]
+ CRUSH rule 1 x 933 [352,742,216,321,525,44]
+ CRUSH rule 1 x 934 [730,2,332,631,613,249]
+ CRUSH rule 1 x 935 [731,23,736,79,361,992]
+ CRUSH rule 1 x 936 [322,975,20,904,827,603]
+ CRUSH rule 1 x 937 [822,221,841,161,723,137]
+ CRUSH rule 1 x 938 [557,850,66,630,499,404]
+ CRUSH rule 1 x 939 [150,11,971,371,124,785]
+ CRUSH rule 1 x 940 [638,398,169,616,333,751]
+ CRUSH rule 1 x 941 [730,342,929,577,451,838]
+ CRUSH rule 1 x 942 [62,292,166,814,587,172]
+ CRUSH rule 1 x 943 [165,314,519,548,41,726]
+ CRUSH rule 1 x 944 [199,625,766,176,194,297]
+ CRUSH rule 1 x 945 [946,999,699,303,38,81]
+ CRUSH rule 1 x 946 [595,93,852,142,503,647]
+ CRUSH rule 1 x 947 [800,582,356,93,716,117]
+ CRUSH rule 1 x 948 [132,551,139,920,87,46]
+ CRUSH rule 1 x 949 [792,920,466,380,97,568]
+ CRUSH rule 1 x 950 [111,345,176,543,879,954]
+ CRUSH rule 1 x 951 [414,619,648,655,364,971]
+ CRUSH rule 1 x 952 [775,469,500,356,287,4]
+ CRUSH rule 1 x 953 [349,1,5,251,168,680]
+ CRUSH rule 1 x 954 [570,940,410,249,929,394]
+ CRUSH rule 1 x 955 [729,774,823,800,7,127]
+ CRUSH rule 1 x 956 [519,141,575,625,738,475]
+ CRUSH rule 1 x 957 [242,709,611,97,760,309]
+ CRUSH rule 1 x 958 [84,217,227,253,246,604]
+ CRUSH rule 1 x 959 [270,413,918,789,703,608]
+ CRUSH rule 1 x 960 [458,192,307,279,920,139]
+ CRUSH rule 1 x 961 [981,388,777,546,359,660]
+ CRUSH rule 1 x 962 [623,834,277,134,729,246]
+ CRUSH rule 1 x 963 [291,167,714,468,109,373]
+ CRUSH rule 1 x 964 [28,156,788,127,598,215]
+ CRUSH rule 1 x 965 [675,557,290,517,840,510]
+ CRUSH rule 1 x 966 [836,306,946,283,642,606]
+ CRUSH rule 1 x 967 [966,386,735,837,392,116]
+ CRUSH rule 1 x 968 [864,756,690,121,328,122]
+ CRUSH rule 1 x 969 [729,625,480,769,512,882]
+ CRUSH rule 1 x 970 [800,362,646,582,309,102]
+ CRUSH rule 1 x 971 [737,381,153,684,298,166]
+ CRUSH rule 1 x 972 [952,245,720,884,334,311]
+ CRUSH rule 1 x 973 [356,455,579,857,832,596]
+ CRUSH rule 1 x 974 [545,758,586,596,869,790]
+ CRUSH rule 1 x 975 [336,191,202,146,720,897]
+ CRUSH rule 1 x 976 [446,208,757,620,252,846]
+ CRUSH rule 1 x 977 [202,896,196,956,763,126]
+ CRUSH rule 1 x 978 [612,324,996,225,418,583]
+ CRUSH rule 1 x 979 [843,457,675,650,958,657]
+ CRUSH rule 1 x 980 [60,914,881,626,850,759]
+ CRUSH rule 1 x 981 [702,749,937,153,724,514]
+ CRUSH rule 1 x 982 [298,928,738,167,99,668]
+ CRUSH rule 1 x 983 [723,572,395,358,900,37]
+ CRUSH rule 1 x 984 [723,864,804,935,846,993]
+ CRUSH rule 1 x 985 [945,459,868,211,524,954]
+ CRUSH rule 1 x 986 [772,664,535,169,297,996]
+ CRUSH rule 1 x 987 [88,324,312,843,661,580]
+ CRUSH rule 1 x 988 [522,927,131,996,351,685]
+ CRUSH rule 1 x 989 [578,332,208,605,975,207]
+ CRUSH rule 1 x 990 [638,228,414,311,738,698]
+ CRUSH rule 1 x 991 [530,221,451,422,879,916]
+ CRUSH rule 1 x 992 [925,705,275,81,234,310]
+ CRUSH rule 1 x 993 [991,301,43,469,830,242]
+ CRUSH rule 1 x 994 [276,51,868,683,843,815]
+ CRUSH rule 1 x 995 [288,836,753,790,758,120]
+ CRUSH rule 1 x 996 [887,983,252,686,470,345]
+ CRUSH rule 1 x 997 [110,924,386,79,705,697]
+ CRUSH rule 1 x 998 [435,830,485,853,926,730]
+ CRUSH rule 1 x 999 [876,738,357,913,723,51]
+ CRUSH rule 1 x 1000 [178,963,638,430,845,586]
+ CRUSH rule 1 x 1001 [99,519,66,759,583,944]
+ CRUSH rule 1 x 1002 [515,534,468,866,878,717]
+ CRUSH rule 1 x 1003 [104,611,937,698,94,67]
+ CRUSH rule 1 x 1004 [269,638,724,375,491,121]
+ CRUSH rule 1 x 1005 [369,223,309,409,822,39]
+ CRUSH rule 1 x 1006 [40,107,69,275,79,429]
+ CRUSH rule 1 x 1007 [978,111,416,758,454,640]
+ CRUSH rule 1 x 1008 [965,956,624,832,421,96]
+ CRUSH rule 1 x 1009 [598,476,356,695,919,566]
+ CRUSH rule 1 x 1010 [767,523,239,517,29,77]
+ CRUSH rule 1 x 1011 [289,871,207,576,347,698]
+ CRUSH rule 1 x 1012 [128,28,370,31,341,755]
+ CRUSH rule 1 x 1013 [979,765,660,812,666,187]
+ CRUSH rule 1 x 1014 [979,948,513,88,47,825]
+ CRUSH rule 1 x 1015 [277,790,396,672,542,647]
+ CRUSH rule 1 x 1016 [262,73,128,886,839,685]
+ CRUSH rule 1 x 1017 [150,269,61,499,832,591]
+ CRUSH rule 1 x 1018 [555,829,554,944,406,576]
+ CRUSH rule 1 x 1019 [513,356,265,446,65,288]
+ CRUSH rule 1 x 1020 [158,161,877,704,948,570]
+ CRUSH rule 1 x 1021 [915,998,957,285,546,202]
+ CRUSH rule 1 x 1022 [967,829,973,640,703,470]
+ CRUSH rule 1 x 1023 [488,257,614,859,325,419]
+ rule 1 (metadata) num_rep 6 result size == 6:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536,450,604,380,966]
+ CRUSH rule 1 x 1 [876,250,334,633,744,843,672]
+ CRUSH rule 1 x 2 [292,832,53,392,386,787,527]
+ CRUSH rule 1 x 3 [623,387,124,998,749,211,481]
+ CRUSH rule 1 x 4 [61,334,710,4,994,982,847]
+ CRUSH rule 1 x 5 [946,557,713,664,141,817,964]
+ CRUSH rule 1 x 6 [576,668,212,163,732,381,884]
+ CRUSH rule 1 x 7 [645,753,906,393,341,44,578]
+ CRUSH rule 1 x 8 [243,6,863,781,211,100,462]
+ CRUSH rule 1 x 9 [22,578,251,410,297,430,3]
+ CRUSH rule 1 x 10 [758,828,360,477,821,801,811]
+ CRUSH rule 1 x 11 [769,120,124,527,119,504,380]
+ CRUSH rule 1 x 12 [780,364,689,755,675,199,117]
+ CRUSH rule 1 x 13 [557,18,351,719,742,780,78]
+ CRUSH rule 1 x 14 [59,561,249,461,971,835,855]
+ CRUSH rule 1 x 15 [718,928,993,21,76,313,437]
+ CRUSH rule 1 x 16 [673,632,841,954,788,90,786]
+ CRUSH rule 1 x 17 [648,43,560,514,142,289,935]
+ CRUSH rule 1 x 18 [654,219,181,568,381,253,883]
+ CRUSH rule 1 x 19 [850,545,377,848,863,543,51]
+ CRUSH rule 1 x 20 [717,785,974,5,225,552,975]
+ CRUSH rule 1 x 21 [420,57,519,306,312,983,263]
+ CRUSH rule 1 x 22 [503,998,193,821,634,684,557]
+ CRUSH rule 1 x 23 [411,663,168,110,899,488,477]
+ CRUSH rule 1 x 24 [266,861,353,1,456,128,800]
+ CRUSH rule 1 x 25 [760,483,818,600,509,951,248]
+ CRUSH rule 1 x 26 [903,24,573,718,112,694,501]
+ CRUSH rule 1 x 27 [946,188,289,510,687,827,676]
+ CRUSH rule 1 x 28 [69,312,73,198,256,629,770]
+ CRUSH rule 1 x 29 [844,883,337,628,496,405,719]
+ CRUSH rule 1 x 30 [621,18,613,794,910,936,426]
+ CRUSH rule 1 x 31 [784,943,814,539,962,392,813]
+ CRUSH rule 1 x 32 [173,374,369,972,315,83,428]
+ CRUSH rule 1 x 33 [698,336,357,966,582,407,618]
+ CRUSH rule 1 x 34 [168,836,210,798,904,190,663]
+ CRUSH rule 1 x 35 [274,509,534,818,912,671,75]
+ CRUSH rule 1 x 36 [318,215,153,628,87,407,676]
+ CRUSH rule 1 x 37 [173,604,109,935,203,401,311]
+ CRUSH rule 1 x 38 [708,444,683,604,722,900,929]
+ CRUSH rule 1 x 39 [662,198,417,680,226,342,856]
+ CRUSH rule 1 x 40 [620,801,414,78,560,766,980]
+ CRUSH rule 1 x 41 [811,264,177,127,148,791,930]
+ CRUSH rule 1 x 42 [863,179,527,660,133,529,456]
+ CRUSH rule 1 x 43 [686,822,988,228,791,549,514]
+ CRUSH rule 1 x 44 [396,222,46,841,536,140,160]
+ CRUSH rule 1 x 45 [991,694,253,142,54,422,658]
+ CRUSH rule 1 x 46 [420,909,184,285,508,458,45]
+ CRUSH rule 1 x 47 [467,211,605,207,241,881,959]
+ CRUSH rule 1 x 48 [955,329,368,168,698,787,738]
+ CRUSH rule 1 x 49 [974,891,931,29,813,506,822]
+ CRUSH rule 1 x 50 [870,441,691,823,761,6,83]
+ CRUSH rule 1 x 51 [182,930,25,936,97,260,406]
+ CRUSH rule 1 x 52 [704,812,894,794,481,37,304]
+ CRUSH rule 1 x 53 [185,713,631,280,345,558,882]
+ CRUSH rule 1 x 54 [270,441,100,82,983,930,339]
+ CRUSH rule 1 x 55 [895,734,958,793,651,572,508]
+ CRUSH rule 1 x 56 [564,963,683,324,40,189,77]
+ CRUSH rule 1 x 57 [738,130,208,973,498,861,670]
+ CRUSH rule 1 x 58 [524,113,806,903,531,334,8]
+ CRUSH rule 1 x 59 [408,337,668,529,34,384,643]
+ CRUSH rule 1 x 60 [228,790,857,309,616,895,194]
+ CRUSH rule 1 x 61 [154,843,717,467,883,536,812]
+ CRUSH rule 1 x 62 [594,811,549,276,693,917,45]
+ CRUSH rule 1 x 63 [646,67,884,925,941,434,705]
+ CRUSH rule 1 x 64 [175,542,155,837,594,197,451]
+ CRUSH rule 1 x 65 [745,619,131,867,269,62,862]
+ CRUSH rule 1 x 66 [275,468,23,35,328,432,334]
+ CRUSH rule 1 x 67 [246,958,524,493,636,227,783]
+ CRUSH rule 1 x 68 [711,473,403,228,835,126,705]
+ CRUSH rule 1 x 69 [493,924,850,939,950,105,871]
+ CRUSH rule 1 x 70 [30,499,644,33,804,654,684]
+ CRUSH rule 1 x 71 [984,883,574,716,575,391,587]
+ CRUSH rule 1 x 72 [71,286,942,363,628,632,642]
+ CRUSH rule 1 x 73 [922,618,3,371,464,442,835]
+ CRUSH rule 1 x 74 [629,414,185,573,678,338,633]
+ CRUSH rule 1 x 75 [222,20,174,820,312,361,366]
+ CRUSH rule 1 x 76 [262,366,339,290,718,143,735]
+ CRUSH rule 1 x 77 [638,469,992,280,773,892,197]
+ CRUSH rule 1 x 78 [324,511,788,7,308,228,183]
+ CRUSH rule 1 x 79 [577,990,64,94,447,924,339]
+ CRUSH rule 1 x 80 [501,95,278,903,631,842,51]
+ CRUSH rule 1 x 81 [506,812,9,698,173,664,247]
+ CRUSH rule 1 x 82 [222,145,80,785,835,745,580]
+ CRUSH rule 1 x 83 [71,634,61,91,856,529,66]
+ CRUSH rule 1 x 84 [49,761,773,368,318,708,681]
+ CRUSH rule 1 x 85 [985,896,708,861,325,307,567]
+ CRUSH rule 1 x 86 [537,745,93,524,466,356,38]
+ CRUSH rule 1 x 87 [997,317,463,626,685,594,909]
+ CRUSH rule 1 x 88 [957,350,890,857,375,176,99]
+ CRUSH rule 1 x 89 [399,730,148,314,159,982,320]
+ CRUSH rule 1 x 90 [943,706,683,267,579,141,412]
+ CRUSH rule 1 x 91 [22,368,149,928,140,529,495]
+ CRUSH rule 1 x 92 [532,424,426,773,623,197,167]
+ CRUSH rule 1 x 93 [218,489,405,681,549,201,343]
+ CRUSH rule 1 x 94 [181,96,102,515,776,365,82]
+ CRUSH rule 1 x 95 [343,957,820,139,334,37,648]
+ CRUSH rule 1 x 96 [861,270,87,797,0,245,204]
+ CRUSH rule 1 x 97 [459,706,45,328,274,605,83]
+ CRUSH rule 1 x 98 [327,867,353,948,728,280,270]
+ CRUSH rule 1 x 99 [974,133,468,906,235,988,37]
+ CRUSH rule 1 x 100 [32,445,547,371,960,885,9]
+ CRUSH rule 1 x 101 [142,90,337,950,970,570,12]
+ CRUSH rule 1 x 102 [172,129,139,22,403,867,923]
+ CRUSH rule 1 x 103 [630,47,161,356,911,421,933]
+ CRUSH rule 1 x 104 [758,133,278,11,947,799,401]
+ CRUSH rule 1 x 105 [843,604,47,33,401,632,434]
+ CRUSH rule 1 x 106 [28,681,193,679,990,343,878]
+ CRUSH rule 1 x 107 [74,320,85,819,315,253,589]
+ CRUSH rule 1 x 108 [875,593,575,517,107,153,631]
+ CRUSH rule 1 x 109 [411,985,811,720,198,666,856]
+ CRUSH rule 1 x 110 [440,774,799,660,715,167,510]
+ CRUSH rule 1 x 111 [405,742,276,359,936,360,18]
+ CRUSH rule 1 x 112 [143,181,922,545,185,303,725]
+ CRUSH rule 1 x 113 [153,846,160,903,789,897,738]
+ CRUSH rule 1 x 114 [804,892,939,20,312,692,598]
+ CRUSH rule 1 x 115 [588,508,958,580,232,722,421]
+ CRUSH rule 1 x 116 [327,148,637,486,712,464,9]
+ CRUSH rule 1 x 117 [95,594,989,131,714,275,725]
+ CRUSH rule 1 x 118 [80,957,897,239,359,432,766]
+ CRUSH rule 1 x 119 [386,932,951,768,679,300,570]
+ CRUSH rule 1 x 120 [366,312,653,936,71,241,49]
+ CRUSH rule 1 x 121 [129,154,847,16,471,481,424]
+ CRUSH rule 1 x 122 [873,1,110,939,90,412,551]
+ CRUSH rule 1 x 123 [533,415,789,600,713,800,877]
+ CRUSH rule 1 x 124 [461,691,898,723,957,759,482]
+ CRUSH rule 1 x 125 [342,599,830,402,615,994,736]
+ CRUSH rule 1 x 126 [819,781,822,548,279,255,689]
+ CRUSH rule 1 x 127 [437,893,585,707,353,189,909]
+ CRUSH rule 1 x 128 [679,994,982,550,991,324,666]
+ CRUSH rule 1 x 129 [380,685,947,302,698,144,149]
+ CRUSH rule 1 x 130 [992,52,466,867,998,777,270]
+ CRUSH rule 1 x 131 [469,90,208,599,829,656,203]
+ CRUSH rule 1 x 132 [571,250,316,535,54,418,922]
+ CRUSH rule 1 x 133 [964,728,329,902,108,118,14]
+ CRUSH rule 1 x 134 [999,19,716,963,323,559,893]
+ CRUSH rule 1 x 135 [634,101,52,938,413,573,712]
+ CRUSH rule 1 x 136 [114,889,692,768,694,279,846]
+ CRUSH rule 1 x 137 [839,8,959,280,922,870,363]
+ CRUSH rule 1 x 138 [967,949,138,451,292,548,400]
+ CRUSH rule 1 x 139 [308,711,736,247,632,126,384]
+ CRUSH rule 1 x 140 [764,936,926,55,331,115,178]
+ CRUSH rule 1 x 141 [423,302,112,216,603,873,193]
+ CRUSH rule 1 x 142 [252,821,715,340,635,668,424]
+ CRUSH rule 1 x 143 [33,808,518,477,325,316,266]
+ CRUSH rule 1 x 144 [472,88,969,162,401,771,697]
+ CRUSH rule 1 x 145 [242,208,252,604,266,743,577]
+ CRUSH rule 1 x 146 [290,70,570,384,934,856,929]
+ CRUSH rule 1 x 147 [447,352,657,493,467,918,514]
+ CRUSH rule 1 x 148 [212,644,432,658,109,275,352]
+ CRUSH rule 1 x 149 [9,775,87,35,260,646,406]
+ CRUSH rule 1 x 150 [166,456,582,144,324,340,484]
+ CRUSH rule 1 x 151 [811,875,307,20,782,229,671]
+ CRUSH rule 1 x 152 [449,617,223,9,182,407,807]
+ CRUSH rule 1 x 153 [523,537,695,627,959,613,942]
+ CRUSH rule 1 x 154 [208,559,874,597,243,706,443]
+ CRUSH rule 1 x 155 [569,325,192,296,367,848,58]
+ CRUSH rule 1 x 156 [488,121,521,213,595,837,271]
+ CRUSH rule 1 x 157 [140,723,633,260,487,856,384]
+ CRUSH rule 1 x 158 [786,451,320,239,667,632,899]
+ CRUSH rule 1 x 159 [134,664,517,821,667,944,209]
+ CRUSH rule 1 x 160 [690,112,414,990,183,590,242]
+ CRUSH rule 1 x 161 [324,912,397,423,991,284,909]
+ CRUSH rule 1 x 162 [748,567,284,183,463,336,148]
+ CRUSH rule 1 x 163 [575,499,31,816,749,737,587]
+ CRUSH rule 1 x 164 [314,489,308,326,51,568,110]
+ CRUSH rule 1 x 165 [116,209,750,53,813,640,524]
+ CRUSH rule 1 x 166 [352,706,701,810,718,527,548]
+ CRUSH rule 1 x 167 [27,743,174,142,551,1,935]
+ CRUSH rule 1 x 168 [953,898,880,660,500,799,667]
+ CRUSH rule 1 x 169 [912,147,266,547,331,770,601]
+ CRUSH rule 1 x 170 [421,515,828,844,151,981,835]
+ CRUSH rule 1 x 171 [488,584,880,964,936,196,100]
+ CRUSH rule 1 x 172 [366,443,957,66,162,693,36]
+ CRUSH rule 1 x 173 [863,291,625,287,158,496,471]
+ CRUSH rule 1 x 174 [263,555,650,410,339,616,780]
+ CRUSH rule 1 x 175 [875,961,361,575,33,109,51]
+ CRUSH rule 1 x 176 [745,83,701,680,250,420,240]
+ CRUSH rule 1 x 177 [128,244,41,123,422,902,756]
+ CRUSH rule 1 x 178 [155,41,264,777,314,564,856]
+ CRUSH rule 1 x 179 [593,833,202,183,971,38,724]
+ CRUSH rule 1 x 180 [154,734,17,831,824,522,736]
+ CRUSH rule 1 x 181 [289,675,723,800,166,712,168]
+ CRUSH rule 1 x 182 [730,931,560,209,943,261,485]
+ CRUSH rule 1 x 183 [639,237,794,815,827,400,109]
+ CRUSH rule 1 x 184 [704,312,685,645,691,778,74]
+ CRUSH rule 1 x 185 [97,100,762,82,999,542,485]
+ CRUSH rule 1 x 186 [26,665,554,215,280,421,369]
+ CRUSH rule 1 x 187 [649,14,740,494,402,684,566]
+ CRUSH rule 1 x 188 [682,695,590,743,927,945,833]
+ CRUSH rule 1 x 189 [325,693,726,51,448,169,37]
+ CRUSH rule 1 x 190 [399,933,136,955,57,504,527]
+ CRUSH rule 1 x 191 [629,533,17,126,60,146,999]
+ CRUSH rule 1 x 192 [503,578,38,492,222,251,123]
+ CRUSH rule 1 x 193 [546,333,651,678,823,652,359]
+ CRUSH rule 1 x 194 [242,473,58,655,888,277,792]
+ CRUSH rule 1 x 195 [625,719,135,81,636,513,755]
+ CRUSH rule 1 x 196 [357,114,125,867,250,522,413]
+ CRUSH rule 1 x 197 [306,954,453,873,211,334,666]
+ CRUSH rule 1 x 198 [863,791,311,911,206,61,355]
+ CRUSH rule 1 x 199 [935,906,929,252,893,75,960]
+ CRUSH rule 1 x 200 [373,774,229,454,909,611,132]
+ CRUSH rule 1 x 201 [659,320,477,313,779,16,495]
+ CRUSH rule 1 x 202 [260,433,524,880,223,818,153]
+ CRUSH rule 1 x 203 [36,239,675,971,703,209,669]
+ CRUSH rule 1 x 204 [92,516,993,728,279,478,697]
+ CRUSH rule 1 x 205 [68,395,473,45,683,662,776]
+ CRUSH rule 1 x 206 [570,530,642,380,311,398,230]
+ CRUSH rule 1 x 207 [834,457,850,917,456,296,76]
+ CRUSH rule 1 x 208 [927,484,640,976,803,626,96]
+ CRUSH rule 1 x 209 [878,66,58,940,48,233,522]
+ CRUSH rule 1 x 210 [572,981,484,29,0,426,14]
+ CRUSH rule 1 x 211 [107,597,780,857,895,57,922]
+ CRUSH rule 1 x 212 [389,107,838,624,698,562,857]
+ CRUSH rule 1 x 213 [497,717,567,728,905,134,687]
+ CRUSH rule 1 x 214 [798,65,254,572,32,393,579]
+ CRUSH rule 1 x 215 [233,419,283,638,520,891,982]
+ CRUSH rule 1 x 216 [494,464,742,523,459,174,973]
+ CRUSH rule 1 x 217 [352,396,309,938,66,41,264]
+ CRUSH rule 1 x 218 [895,864,988,650,593,740,34]
+ CRUSH rule 1 x 219 [222,534,277,242,658,482,697]
+ CRUSH rule 1 x 220 [281,19,584,563,858,965,686]
+ CRUSH rule 1 x 221 [64,928,963,130,312,394,61]
+ CRUSH rule 1 x 222 [40,544,161,199,861,644,597]
+ CRUSH rule 1 x 223 [645,556,159,417,46,135,465]
+ CRUSH rule 1 x 224 [647,165,957,263,961,576,329]
+ CRUSH rule 1 x 225 [219,714,858,747,461,175,606]
+ CRUSH rule 1 x 226 [372,511,181,277,695,404,876]
+ CRUSH rule 1 x 227 [925,156,714,863,257,74,966]
+ CRUSH rule 1 x 228 [682,404,839,263,521,195,261]
+ CRUSH rule 1 x 229 [880,838,770,891,236,542,262]
+ CRUSH rule 1 x 230 [328,659,916,468,646,572,93]
+ CRUSH rule 1 x 231 [320,383,669,109,627,621,50]
+ CRUSH rule 1 x 232 [924,846,394,319,43,519,106]
+ CRUSH rule 1 x 233 [948,652,575,838,498,395,796]
+ CRUSH rule 1 x 234 [484,943,42,575,936,180,103]
+ CRUSH rule 1 x 235 [750,65,590,168,870,308,471]
+ CRUSH rule 1 x 236 [551,787,490,136,370,833,573]
+ CRUSH rule 1 x 237 [390,157,166,251,752,75,327]
+ CRUSH rule 1 x 238 [570,6,989,707,514,905,894]
+ CRUSH rule 1 x 239 [729,959,376,975,496,49,426]
+ CRUSH rule 1 x 240 [981,241,156,767,631,576,450]
+ CRUSH rule 1 x 241 [310,816,641,177,996,454,413]
+ CRUSH rule 1 x 242 [161,63,642,837,763,458,234]
+ CRUSH rule 1 x 243 [180,394,33,683,189,419,799]
+ CRUSH rule 1 x 244 [52,174,685,189,78,310,785]
+ CRUSH rule 1 x 245 [523,121,915,84,386,409,605]
+ CRUSH rule 1 x 246 [362,893,390,487,817,88,989]
+ CRUSH rule 1 x 247 [382,184,116,34,143,15,590]
+ CRUSH rule 1 x 248 [129,114,852,469,359,291,713]
+ CRUSH rule 1 x 249 [159,683,91,856,475,369,886]
+ CRUSH rule 1 x 250 [404,945,569,955,228,910,270]
+ CRUSH rule 1 x 251 [661,225,738,757,37,642,58]
+ CRUSH rule 1 x 252 [961,226,542,103,945,885,838]
+ CRUSH rule 1 x 253 [651,97,225,364,189,248,797]
+ CRUSH rule 1 x 254 [123,33,741,692,599,11,605]
+ CRUSH rule 1 x 255 [314,649,891,855,517,344,607]
+ CRUSH rule 1 x 256 [315,215,651,126,470,849,189]
+ CRUSH rule 1 x 257 [825,264,867,681,529,409,291]
+ CRUSH rule 1 x 258 [624,789,370,723,131,982,863]
+ CRUSH rule 1 x 259 [602,542,70,563,947,723,77]
+ CRUSH rule 1 x 260 [717,878,43,56,377,481,533]
+ CRUSH rule 1 x 261 [145,517,20,903,786,939,516]
+ CRUSH rule 1 x 262 [223,1,561,420,209,16,88]
+ CRUSH rule 1 x 263 [462,211,405,508,787,669,773]
+ CRUSH rule 1 x 264 [654,471,266,662,135,564,715]
+ CRUSH rule 1 x 265 [302,794,704,798,659,487,833]
+ CRUSH rule 1 x 266 [202,132,884,209,551,984,7]
+ CRUSH rule 1 x 267 [282,938,657,113,672,993,972]
+ CRUSH rule 1 x 268 [338,309,356,278,928,797,715]
+ CRUSH rule 1 x 269 [738,122,266,200,894,118,146]
+ CRUSH rule 1 x 270 [707,982,946,196,407,804,476]
+ CRUSH rule 1 x 271 [705,432,364,735,512,595,263]
+ CRUSH rule 1 x 272 [756,545,942,56,542,449,710]
+ CRUSH rule 1 x 273 [197,502,527,721,239,648,982]
+ CRUSH rule 1 x 274 [992,44,653,573,527,702,370]
+ CRUSH rule 1 x 275 [544,789,170,434,23,926,992]
+ CRUSH rule 1 x 276 [658,467,577,268,336,5,634]
+ CRUSH rule 1 x 277 [143,490,880,483,928,272,783]
+ CRUSH rule 1 x 278 [492,647,355,282,834,64,350]
+ CRUSH rule 1 x 279 [517,792,604,987,527,894,952]
+ CRUSH rule 1 x 280 [825,740,27,848,514,750,895]
+ CRUSH rule 1 x 281 [224,629,120,562,616,200,443]
+ CRUSH rule 1 x 282 [298,661,380,416,35,585,939]
+ CRUSH rule 1 x 283 [311,606,208,50,913,678,369]
+ CRUSH rule 1 x 284 [771,466,371,743,672,119,60]
+ CRUSH rule 1 x 285 [693,362,404,676,797,531,582]
+ CRUSH rule 1 x 286 [364,477,285,167,270,617,699]
+ CRUSH rule 1 x 287 [591,611,828,995,170,987,137]
+ CRUSH rule 1 x 288 [965,541,848,796,251,668,195]
+ CRUSH rule 1 x 289 [225,551,948,877,219,167,795]
+ CRUSH rule 1 x 290 [577,762,777,751,291,349,473]
+ CRUSH rule 1 x 291 [160,903,477,381,490,559,557]
+ CRUSH rule 1 x 292 [873,598,216,666,222,228,806]
+ CRUSH rule 1 x 293 [100,234,874,47,28,452,775]
+ CRUSH rule 1 x 294 [285,943,379,520,725,547,459]
+ CRUSH rule 1 x 295 [938,262,880,327,687,3,440]
+ CRUSH rule 1 x 296 [850,327,86,472,1,776,266]
+ CRUSH rule 1 x 297 [951,53,99,558,753,228,232]
+ CRUSH rule 1 x 298 [173,336,85,766,910,657,213]
+ CRUSH rule 1 x 299 [598,591,315,386,895,296,924]
+ CRUSH rule 1 x 300 [531,957,62,459,156,538,904]
+ CRUSH rule 1 x 301 [823,628,23,858,629,808,220]
+ CRUSH rule 1 x 302 [184,80,780,871,531,211,400]
+ CRUSH rule 1 x 303 [521,766,222,830,988,275,561]
+ CRUSH rule 1 x 304 [980,127,807,507,555,245,214]
+ CRUSH rule 1 x 305 [153,816,22,927,696,911,685]
+ CRUSH rule 1 x 306 [423,739,664,753,178,431,761]
+ CRUSH rule 1 x 307 [997,557,682,456,479,631,459]
+ CRUSH rule 1 x 308 [991,874,534,465,330,284,976]
+ CRUSH rule 1 x 309 [860,394,724,858,246,866,857]
+ CRUSH rule 1 x 310 [589,818,546,201,94,653,90]
+ CRUSH rule 1 x 311 [477,774,225,590,830,559,256]
+ CRUSH rule 1 x 312 [887,853,950,354,58,23,497]
+ CRUSH rule 1 x 313 [802,646,447,416,557,118,24]
+ CRUSH rule 1 x 314 [654,974,229,511,562,916,952]
+ CRUSH rule 1 x 315 [767,227,28,740,828,156,749]
+ CRUSH rule 1 x 316 [778,83,733,359,858,319,761]
+ CRUSH rule 1 x 317 [184,418,642,986,939,675,892]
+ CRUSH rule 1 x 318 [525,410,500,543,212,95,290]
+ CRUSH rule 1 x 319 [476,724,569,382,409,521,800]
+ CRUSH rule 1 x 320 [149,610,697,296,818,955,523]
+ CRUSH rule 1 x 321 [710,79,667,671,234,4,868]
+ CRUSH rule 1 x 322 [175,275,323,333,744,718,187]
+ CRUSH rule 1 x 323 [819,604,638,792,316,544,236]
+ CRUSH rule 1 x 324 [16,745,511,439,272,294,668]
+ CRUSH rule 1 x 325 [486,400,872,873,251,68,462]
+ CRUSH rule 1 x 326 [613,765,207,19,359,370,461]
+ CRUSH rule 1 x 327 [125,289,738,408,456,784,750]
+ CRUSH rule 1 x 328 [807,383,476,583,645,141,33]
+ CRUSH rule 1 x 329 [588,938,599,432,446,840,516]
+ CRUSH rule 1 x 330 [932,644,41,611,209,406,420]
+ CRUSH rule 1 x 331 [341,953,950,537,578,862,624]
+ CRUSH rule 1 x 332 [153,726,459,950,466,804,644]
+ CRUSH rule 1 x 333 [745,845,853,860,52,615,243]
+ CRUSH rule 1 x 334 [614,751,807,58,396,159,408]
+ CRUSH rule 1 x 335 [518,721,221,283,454,187,635]
+ CRUSH rule 1 x 336 [389,424,77,309,5,898,698]
+ CRUSH rule 1 x 337 [753,508,765,720,221,807,956]
+ CRUSH rule 1 x 338 [128,810,490,753,406,760,69]
+ CRUSH rule 1 x 339 [430,308,58,751,856,823,607]
+ CRUSH rule 1 x 340 [541,44,630,231,289,966,707]
+ CRUSH rule 1 x 341 [402,26,631,439,165,928,720]
+ CRUSH rule 1 x 342 [982,57,992,461,131,32,516]
+ CRUSH rule 1 x 343 [833,412,572,732,107,805,660]
+ CRUSH rule 1 x 344 [784,533,792,41,642,869,142]
+ CRUSH rule 1 x 345 [546,300,304,691,763,556,127]
+ CRUSH rule 1 x 346 [302,420,428,891,357,124,419]
+ CRUSH rule 1 x 347 [488,778,101,217,366,442,783]
+ CRUSH rule 1 x 348 [903,744,937,718,85,314,862]
+ CRUSH rule 1 x 349 [471,547,582,306,600,486,795]
+ CRUSH rule 1 x 350 [348,221,823,335,383,708,841]
+ CRUSH rule 1 x 351 [961,582,705,346,361,32,766]
+ CRUSH rule 1 x 352 [728,137,461,298,36,903,899]
+ CRUSH rule 1 x 353 [904,202,184,447,58,294,279]
+ CRUSH rule 1 x 354 [345,226,319,256,544,311,612]
+ CRUSH rule 1 x 355 [50,430,175,43,187,458,985]
+ CRUSH rule 1 x 356 [87,185,55,423,829,1,629]
+ CRUSH rule 1 x 357 [762,459,921,473,182,231,891]
+ CRUSH rule 1 x 358 [908,25,280,6,808,676,874]
+ CRUSH rule 1 x 359 [484,15,132,121,394,423,397]
+ CRUSH rule 1 x 360 [173,378,337,702,145,499,29]
+ CRUSH rule 1 x 361 [404,577,115,25,56,914,643]
+ CRUSH rule 1 x 362 [403,1,422,945,132,685,265]
+ CRUSH rule 1 x 363 [639,911,510,162,418,294,444]
+ CRUSH rule 1 x 364 [752,689,610,990,665,222,203]
+ CRUSH rule 1 x 365 [956,999,212,230,624,84,113]
+ CRUSH rule 1 x 366 [860,925,924,763,687,851,59]
+ CRUSH rule 1 x 367 [205,609,647,665,969,720,685]
+ CRUSH rule 1 x 368 [301,284,810,169,78,340,616]
+ CRUSH rule 1 x 369 [452,658,339,217,674,210,284]
+ CRUSH rule 1 x 370 [11,467,695,989,394,576,850]
+ CRUSH rule 1 x 371 [124,487,55,514,313,411,797]
+ CRUSH rule 1 x 372 [253,48,979,846,207,631,212]
+ CRUSH rule 1 x 373 [715,605,775,748,227,493,128]
+ CRUSH rule 1 x 374 [191,887,920,723,223,714,961]
+ CRUSH rule 1 x 375 [711,385,651,665,15,71,934]
+ CRUSH rule 1 x 376 [597,818,49,458,415,755,446]
+ CRUSH rule 1 x 377 [294,256,933,771,184,861,654]
+ CRUSH rule 1 x 378 [34,151,681,707,552,127,728]
+ CRUSH rule 1 x 379 [869,136,315,378,813,153,115]
+ CRUSH rule 1 x 380 [294,97,575,791,690,482,255]
+ CRUSH rule 1 x 381 [119,710,219,827,328,886,773]
+ CRUSH rule 1 x 382 [69,631,508,706,697,168,276]
+ CRUSH rule 1 x 383 [922,588,589,925,471,601,29]
+ CRUSH rule 1 x 384 [221,945,671,117,857,655,488]
+ CRUSH rule 1 x 385 [561,737,953,723,658,368,910]
+ CRUSH rule 1 x 386 [335,442,788,696,507,716,232]
+ CRUSH rule 1 x 387 [514,43,353,88,100,842,164]
+ CRUSH rule 1 x 388 [587,89,157,996,915,927,474]
+ CRUSH rule 1 x 389 [109,641,255,466,372,563,340]
+ CRUSH rule 1 x 390 [925,149,421,489,599,810,852]
+ CRUSH rule 1 x 391 [267,87,387,527,768,873,735]
+ CRUSH rule 1 x 392 [382,485,370,849,936,636,901]
+ CRUSH rule 1 x 393 [425,721,221,753,268,463,652]
+ CRUSH rule 1 x 394 [898,18,38,793,173,738,15]
+ CRUSH rule 1 x 395 [806,876,269,679,32,744,126]
+ CRUSH rule 1 x 396 [790,970,437,449,875,395,726]
+ CRUSH rule 1 x 397 [136,363,507,613,11,30,996]
+ CRUSH rule 1 x 398 [914,116,558,258,722,904,349]
+ CRUSH rule 1 x 399 [261,94,299,202,174,622,749]
+ CRUSH rule 1 x 400 [661,197,338,461,977,848,536]
+ CRUSH rule 1 x 401 [953,979,287,803,41,349,79]
+ CRUSH rule 1 x 402 [738,819,618,522,667,334,658]
+ CRUSH rule 1 x 403 [573,238,425,546,130,68,202]
+ CRUSH rule 1 x 404 [526,848,790,253,922,820,299]
+ CRUSH rule 1 x 405 [582,505,330,334,201,110,776]
+ CRUSH rule 1 x 406 [768,324,493,60,186,165,718]
+ CRUSH rule 1 x 407 [260,951,437,587,692,648,72]
+ CRUSH rule 1 x 408 [657,81,770,734,830,821,246]
+ CRUSH rule 1 x 409 [498,89,182,423,672,152,213]
+ CRUSH rule 1 x 410 [28,793,737,352,166,645,949]
+ CRUSH rule 1 x 411 [684,992,60,659,769,267,313]
+ CRUSH rule 1 x 412 [261,958,699,950,165,14,560]
+ CRUSH rule 1 x 413 [891,835,297,441,384,979,618]
+ CRUSH rule 1 x 414 [127,459,119,965,662,594,97]
+ CRUSH rule 1 x 415 [272,540,631,328,609,568,694]
+ CRUSH rule 1 x 416 [739,617,115,530,339,371,889]
+ CRUSH rule 1 x 417 [106,209,157,878,117,128,138]
+ CRUSH rule 1 x 418 [525,441,147,390,320,300,848]
+ CRUSH rule 1 x 419 [603,673,615,465,266,855,823]
+ CRUSH rule 1 x 420 [988,213,251,226,209,245,506]
+ CRUSH rule 1 x 421 [761,521,748,368,923,992,764]
+ CRUSH rule 1 x 422 [317,160,924,548,198,709,839]
+ CRUSH rule 1 x 423 [137,807,168,472,619,443,905]
+ CRUSH rule 1 x 424 [920,37,146,263,598,748,785]
+ CRUSH rule 1 x 425 [277,693,285,221,478,165,80]
+ CRUSH rule 1 x 426 [485,936,407,854,726,524,791]
+ CRUSH rule 1 x 427 [242,515,9,564,174,453,334]
+ CRUSH rule 1 x 428 [632,635,26,473,494,478,225]
+ CRUSH rule 1 x 429 [641,73,465,127,171,397,857]
+ CRUSH rule 1 x 430 [626,585,6,387,881,583,859]
+ CRUSH rule 1 x 431 [697,76,753,570,964,339,194]
+ CRUSH rule 1 x 432 [590,526,306,283,656,728,513]
+ CRUSH rule 1 x 433 [284,387,149,817,886,714,52]
+ CRUSH rule 1 x 434 [538,985,79,953,770,468,644]
+ CRUSH rule 1 x 435 [30,318,593,635,975,833,371]
+ CRUSH rule 1 x 436 [164,919,851,693,0,874,10]
+ CRUSH rule 1 x 437 [322,212,163,606,302,282,443]
+ CRUSH rule 1 x 438 [142,392,85,594,376,419,755]
+ CRUSH rule 1 x 439 [119,370,68,443,997,837,414]
+ CRUSH rule 1 x 440 [333,403,187,863,475,844,800]
+ CRUSH rule 1 x 441 [477,727,906,145,429,91,205]
+ CRUSH rule 1 x 442 [274,590,933,244,434,49,864]
+ CRUSH rule 1 x 443 [983,748,574,718,700,442,774]
+ CRUSH rule 1 x 444 [536,509,431,146,170,149,182]
+ CRUSH rule 1 x 445 [485,638,528,209,964,753,554]
+ CRUSH rule 1 x 446 [345,634,42,294,711,376,314]
+ CRUSH rule 1 x 447 [61,845,767,600,321,716,58]
+ CRUSH rule 1 x 448 [333,232,292,846,364,951,807]
+ CRUSH rule 1 x 449 [680,16,484,670,851,500,258]
+ CRUSH rule 1 x 450 [235,214,79,423,96,822,721]
+ CRUSH rule 1 x 451 [961,468,333,640,823,151,878]
+ CRUSH rule 1 x 452 [525,479,153,528,570,806,604]
+ CRUSH rule 1 x 453 [138,466,302,86,249,154,514]
+ CRUSH rule 1 x 454 [137,625,215,402,389,914,106]
+ CRUSH rule 1 x 455 [173,150,997,16,846,888,295]
+ CRUSH rule 1 x 456 [235,226,238,258,347,784,504]
+ CRUSH rule 1 x 457 [450,577,253,413,717,609,762]
+ CRUSH rule 1 x 458 [195,537,91,814,351,90,399]
+ CRUSH rule 1 x 459 [381,555,312,573,915,623,147]
+ CRUSH rule 1 x 460 [972,730,534,678,756,692,841]
+ CRUSH rule 1 x 461 [506,279,142,830,784,124,385]
+ CRUSH rule 1 x 462 [692,959,578,57,983,299,240]
+ CRUSH rule 1 x 463 [788,667,949,550,685,702,538]
+ CRUSH rule 1 x 464 [133,122,588,999,270,880,789]
+ CRUSH rule 1 x 465 [971,190,230,777,452,914,137]
+ CRUSH rule 1 x 466 [394,576,148,157,103,822,659]
+ CRUSH rule 1 x 467 [517,28,366,362,984,521,187]
+ CRUSH rule 1 x 468 [829,143,874,225,162,413,201]
+ CRUSH rule 1 x 469 [987,936,106,725,633,238,681]
+ CRUSH rule 1 x 470 [107,982,56,889,67,65,558]
+ CRUSH rule 1 x 471 [181,897,629,860,307,116,256]
+ CRUSH rule 1 x 472 [547,512,172,24,705,837,809]
+ CRUSH rule 1 x 473 [760,997,824,905,888,755,756]
+ CRUSH rule 1 x 474 [787,418,743,628,272,341,446]
+ CRUSH rule 1 x 475 [662,312,253,617,105,58,237]
+ CRUSH rule 1 x 476 [110,495,185,508,961,837,984]
+ CRUSH rule 1 x 477 [393,954,834,132,841,367,753]
+ CRUSH rule 1 x 478 [246,483,480,644,985,420,941]
+ CRUSH rule 1 x 479 [70,929,697,931,744,487,158]
+ CRUSH rule 1 x 480 [753,119,961,607,317,717,371]
+ CRUSH rule 1 x 481 [470,429,677,242,574,757,135]
+ CRUSH rule 1 x 482 [451,566,961,675,354,746,731]
+ CRUSH rule 1 x 483 [816,72,371,278,635,30,448]
+ CRUSH rule 1 x 484 [540,454,389,31,654,494,283]
+ CRUSH rule 1 x 485 [74,582,624,684,566,677,866]
+ CRUSH rule 1 x 486 [958,595,199,763,715,973,621]
+ CRUSH rule 1 x 487 [228,302,804,833,876,647,857]
+ CRUSH rule 1 x 488 [180,529,722,956,353,890,924]
+ CRUSH rule 1 x 489 [47,617,812,187,291,828,154]
+ CRUSH rule 1 x 490 [905,822,479,124,750,843,566]
+ CRUSH rule 1 x 491 [892,370,609,998,433,957,188]
+ CRUSH rule 1 x 492 [588,959,127,948,505,936,591]
+ CRUSH rule 1 x 493 [353,461,593,291,301,830,231]
+ CRUSH rule 1 x 494 [378,848,443,368,507,423,389]
+ CRUSH rule 1 x 495 [845,653,768,234,405,367,823]
+ CRUSH rule 1 x 496 [13,988,0,691,389,757,129]
+ CRUSH rule 1 x 497 [796,877,788,394,648,829,542]
+ CRUSH rule 1 x 498 [412,337,270,705,511,227,949]
+ CRUSH rule 1 x 499 [330,695,8,74,618,101,440]
+ CRUSH rule 1 x 500 [820,272,547,765,755,96,930]
+ CRUSH rule 1 x 501 [110,44,132,442,294,423,880]
+ CRUSH rule 1 x 502 [336,595,650,274,993,312,490]
+ CRUSH rule 1 x 503 [922,211,157,722,502,971,262]
+ CRUSH rule 1 x 504 [483,52,122,432,778,461,758]
+ CRUSH rule 1 x 505 [482,598,224,279,480,310,764]
+ CRUSH rule 1 x 506 [493,123,43,856,936,622,898]
+ CRUSH rule 1 x 507 [12,598,264,422,416,947,591]
+ CRUSH rule 1 x 508 [227,157,611,301,223,746,313]
+ CRUSH rule 1 x 509 [807,242,363,122,582,530,798]
+ CRUSH rule 1 x 510 [134,437,227,75,313,351,786]
+ CRUSH rule 1 x 511 [212,54,83,799,457,218,600]
+ CRUSH rule 1 x 512 [236,630,758,752,361,249,899]
+ CRUSH rule 1 x 513 [994,693,644,938,846,685,52]
+ CRUSH rule 1 x 514 [45,508,831,19,817,52,374]
+ CRUSH rule 1 x 515 [504,138,480,272,530,377,481]
+ CRUSH rule 1 x 516 [285,409,136,570,841,610,453]
+ CRUSH rule 1 x 517 [300,232,23,906,438,236,519]
+ CRUSH rule 1 x 518 [397,674,98,898,967,113,625]
+ CRUSH rule 1 x 519 [86,750,772,913,101,864,375]
+ CRUSH rule 1 x 520 [900,833,614,130,261,885,558]
+ CRUSH rule 1 x 521 [31,47,236,751,911,599,495]
+ CRUSH rule 1 x 522 [390,16,280,144,291,175,753]
+ CRUSH rule 1 x 523 [618,308,424,590,300,206,834]
+ CRUSH rule 1 x 524 [635,189,687,963,601,518,8]
+ CRUSH rule 1 x 525 [311,916,699,262,775,32,45]
+ CRUSH rule 1 x 526 [48,738,227,718,244,942,853]
+ CRUSH rule 1 x 527 [202,851,889,216,763,351,270]
+ CRUSH rule 1 x 528 [565,827,590,273,918,106,651]
+ CRUSH rule 1 x 529 [934,864,241,43,466,924,278]
+ CRUSH rule 1 x 530 [502,934,298,670,986,360,577]
+ CRUSH rule 1 x 531 [681,627,942,487,288,561,925]
+ CRUSH rule 1 x 532 [422,6,147,205,861,141,949]
+ CRUSH rule 1 x 533 [863,68,364,983,247,199,54]
+ CRUSH rule 1 x 534 [962,931,775,172,663,119,206]
+ CRUSH rule 1 x 535 [89,565,397,693,839,632,859]
+ CRUSH rule 1 x 536 [499,351,760,458,918,86,148]
+ CRUSH rule 1 x 537 [676,547,787,311,867,748,152]
+ CRUSH rule 1 x 538 [58,644,571,649,941,7,37]
+ CRUSH rule 1 x 539 [837,953,457,711,458,621,528]
+ CRUSH rule 1 x 540 [831,50,132,213,197,709,95]
+ CRUSH rule 1 x 541 [582,757,121,525,532,963,738]
+ CRUSH rule 1 x 542 [472,132,790,997,948,269,137]
+ CRUSH rule 1 x 543 [382,272,797,330,315,748,324]
+ CRUSH rule 1 x 544 [947,930,496,883,509,219,250]
+ CRUSH rule 1 x 545 [425,570,305,77,821,422,117]
+ CRUSH rule 1 x 546 [18,65,529,437,343,547,699]
+ CRUSH rule 1 x 547 [445,715,600,472,213,851,428]
+ CRUSH rule 1 x 548 [367,569,980,167,627,442,517]
+ CRUSH rule 1 x 549 [125,715,671,817,285,420,37]
+ CRUSH rule 1 x 550 [425,599,744,199,923,222,915]
+ CRUSH rule 1 x 551 [44,1,528,922,944,115,161]
+ CRUSH rule 1 x 552 [246,104,68,239,123,427,57]
+ CRUSH rule 1 x 553 [71,703,615,28,593,724,218]
+ CRUSH rule 1 x 554 [207,124,217,166,525,226,693]
+ CRUSH rule 1 x 555 [570,28,317,420,931,413,623]
+ CRUSH rule 1 x 556 [674,152,421,79,215,347,830]
+ CRUSH rule 1 x 557 [347,817,191,391,741,571,593]
+ CRUSH rule 1 x 558 [627,426,369,692,815,371,124]
+ CRUSH rule 1 x 559 [940,630,924,242,224,912,185]
+ CRUSH rule 1 x 560 [295,903,541,29,245,753,887]
+ CRUSH rule 1 x 561 [506,682,384,637,878,991,700]
+ CRUSH rule 1 x 562 [718,529,87,729,842,341,62]
+ CRUSH rule 1 x 563 [552,332,747,206,274,871,903]
+ CRUSH rule 1 x 564 [835,769,736,486,630,209,641]
+ CRUSH rule 1 x 565 [8,167,539,182,607,62,738]
+ CRUSH rule 1 x 566 [600,481,301,263,90,450,184]
+ CRUSH rule 1 x 567 [999,994,509,899,947,24,267]
+ CRUSH rule 1 x 568 [252,431,157,62,601,863,398]
+ CRUSH rule 1 x 569 [643,218,943,455,83,969,494]
+ CRUSH rule 1 x 570 [617,635,765,422,250,156,533]
+ CRUSH rule 1 x 571 [757,80,59,98,328,700,329]
+ CRUSH rule 1 x 572 [299,348,575,889,943,675,33]
+ CRUSH rule 1 x 573 [25,505,270,167,58,901,878]
+ CRUSH rule 1 x 574 [215,431,624,177,628,814,333]
+ CRUSH rule 1 x 575 [225,252,611,546,32,815,389]
+ CRUSH rule 1 x 576 [627,94,159,857,430,691,177]
+ CRUSH rule 1 x 577 [237,809,778,636,61,167,700]
+ CRUSH rule 1 x 578 [885,313,120,344,771,614,487]
+ CRUSH rule 1 x 579 [924,575,787,831,47,996,557]
+ CRUSH rule 1 x 580 [718,51,766,121,118,471,608]
+ CRUSH rule 1 x 581 [219,807,129,571,856,179,874]
+ CRUSH rule 1 x 582 [893,701,598,863,285,829,984]
+ CRUSH rule 1 x 583 [246,930,964,170,993,409,469]
+ CRUSH rule 1 x 584 [336,432,680,175,495,839,642]
+ CRUSH rule 1 x 585 [324,999,397,485,457,527,73]
+ CRUSH rule 1 x 586 [558,230,976,541,816,72,794]
+ CRUSH rule 1 x 587 [985,830,597,21,308,890,952]
+ CRUSH rule 1 x 588 [211,544,57,134,162,496,195]
+ CRUSH rule 1 x 589 [129,21,112,190,885,844,753]
+ CRUSH rule 1 x 590 [467,969,652,593,287,76,811]
+ CRUSH rule 1 x 591 [758,514,316,164,35,110,54]
+ CRUSH rule 1 x 592 [525,253,190,443,315,603,667]
+ CRUSH rule 1 x 593 [601,885,339,152,297,223,269]
+ CRUSH rule 1 x 594 [227,60,450,30,717,840,994]
+ CRUSH rule 1 x 595 [720,854,496,912,80,655,917]
+ CRUSH rule 1 x 596 [751,195,997,77,261,490,180]
+ CRUSH rule 1 x 597 [129,574,714,8,789,847,725]
+ CRUSH rule 1 x 598 [679,207,604,396,841,284,286]
+ CRUSH rule 1 x 599 [668,315,683,349,681,253,599]
+ CRUSH rule 1 x 600 [143,396,464,444,59,57,243]
+ CRUSH rule 1 x 601 [326,573,873,902,136,921,633]
+ CRUSH rule 1 x 602 [860,281,875,535,672,474,697]
+ CRUSH rule 1 x 603 [709,328,445,349,190,455,924]
+ CRUSH rule 1 x 604 [571,62,814,95,866,978,983]
+ CRUSH rule 1 x 605 [252,739,860,27,313,362,857]
+ CRUSH rule 1 x 606 [339,236,759,842,67,644,954]
+ CRUSH rule 1 x 607 [590,248,759,868,433,398,578]
+ CRUSH rule 1 x 608 [145,635,309,467,875,115,148]
+ CRUSH rule 1 x 609 [973,547,223,79,762,863,249]
+ CRUSH rule 1 x 610 [435,816,961,983,255,886,160]
+ CRUSH rule 1 x 611 [559,283,422,584,176,429,570]
+ CRUSH rule 1 x 612 [273,149,123,576,911,270,296]
+ CRUSH rule 1 x 613 [828,614,642,674,33,361,958]
+ CRUSH rule 1 x 614 [478,748,393,34,171,80,92]
+ CRUSH rule 1 x 615 [392,155,144,326,626,134,149]
+ CRUSH rule 1 x 616 [778,637,452,248,15,888,74]
+ CRUSH rule 1 x 617 [622,713,996,833,611,407,364]
+ CRUSH rule 1 x 618 [149,877,270,329,180,327,222]
+ CRUSH rule 1 x 619 [604,163,656,409,322,848,519]
+ CRUSH rule 1 x 620 [181,23,409,198,64,898,35]
+ CRUSH rule 1 x 621 [735,902,386,237,939,475,725]
+ CRUSH rule 1 x 622 [661,824,717,568,858,583,446]
+ CRUSH rule 1 x 623 [142,121,643,61,695,852,485]
+ CRUSH rule 1 x 624 [360,716,420,398,49,717,137]
+ CRUSH rule 1 x 625 [541,167,385,1,601,481,308]
+ CRUSH rule 1 x 626 [364,431,610,363,535,747,225]
+ CRUSH rule 1 x 627 [458,137,557,410,287,749,467]
+ CRUSH rule 1 x 628 [250,350,556,497,821,65,205]
+ CRUSH rule 1 x 629 [928,160,710,572,365,772,538]
+ CRUSH rule 1 x 630 [243,19,918,556,601,16,920]
+ CRUSH rule 1 x 631 [438,221,574,676,797,580,219]
+ CRUSH rule 1 x 632 [797,368,247,5,32,102,416]
+ CRUSH rule 1 x 633 [993,749,525,485,27,330,275]
+ CRUSH rule 1 x 634 [239,351,633,299,651,678,296]
+ CRUSH rule 1 x 635 [640,965,25,961,306,172,849]
+ CRUSH rule 1 x 636 [173,290,297,991,937,823,236]
+ CRUSH rule 1 x 637 [0,918,98,108,111,495,887]
+ CRUSH rule 1 x 638 [702,235,424,900,983,754,701]
+ CRUSH rule 1 x 639 [475,687,31,785,918,611,27]
+ CRUSH rule 1 x 640 [31,664,399,677,123,609,858]
+ CRUSH rule 1 x 641 [296,473,108,963,341,876,897]
+ CRUSH rule 1 x 642 [894,273,427,606,677,670,610]
+ CRUSH rule 1 x 643 [117,111,732,191,114,153,500]
+ CRUSH rule 1 x 644 [438,336,327,512,599,862,660]
+ CRUSH rule 1 x 645 [982,702,351,573,907,915,279]
+ CRUSH rule 1 x 646 [334,804,146,842,697,638,720]
+ CRUSH rule 1 x 647 [933,787,185,334,752,285,372]
+ CRUSH rule 1 x 648 [22,444,400,862,207,842,453]
+ CRUSH rule 1 x 649 [503,229,213,460,639,760,722]
+ CRUSH rule 1 x 650 [328,659,420,443,739,950,869]
+ CRUSH rule 1 x 651 [3,880,823,123,378,585,715]
+ CRUSH rule 1 x 652 [495,977,563,733,92,997,119]
+ CRUSH rule 1 x 653 [185,718,804,280,975,912,198]
+ CRUSH rule 1 x 654 [130,528,380,81,906,511,904]
+ CRUSH rule 1 x 655 [560,872,454,504,319,284,605]
+ CRUSH rule 1 x 656 [219,885,178,981,863,508,708]
+ CRUSH rule 1 x 657 [233,684,813,490,208,941,858]
+ CRUSH rule 1 x 658 [778,6,756,380,750,836,547]
+ CRUSH rule 1 x 659 [240,663,306,540,789,902,170]
+ CRUSH rule 1 x 660 [244,855,196,147,678,323,63]
+ CRUSH rule 1 x 661 [184,270,128,398,910,230,402]
+ CRUSH rule 1 x 662 [65,883,921,438,79,957,464]
+ CRUSH rule 1 x 663 [323,721,594,812,43,992,170]
+ CRUSH rule 1 x 664 [865,113,512,51,427,123,585]
+ CRUSH rule 1 x 665 [420,850,591,475,202,733,798]
+ CRUSH rule 1 x 666 [319,767,246,3,369,493,796]
+ CRUSH rule 1 x 667 [875,39,343,100,829,2,795]
+ CRUSH rule 1 x 668 [331,122,263,599,355,484,943]
+ CRUSH rule 1 x 669 [915,521,402,747,673,445,938]
+ CRUSH rule 1 x 670 [845,659,943,447,401,322,168]
+ CRUSH rule 1 x 671 [108,634,527,363,856,238,755]
+ CRUSH rule 1 x 672 [578,216,110,589,302,137,954]
+ CRUSH rule 1 x 673 [442,74,579,797,622,950,371]
+ CRUSH rule 1 x 674 [588,364,281,308,645,631,229]
+ CRUSH rule 1 x 675 [489,698,744,671,870,174,528]
+ CRUSH rule 1 x 676 [928,911,40,180,722,729,673]
+ CRUSH rule 1 x 677 [399,269,692,131,615,136,103]
+ CRUSH rule 1 x 678 [546,752,544,155,5,463,666]
+ CRUSH rule 1 x 679 [988,25,275,433,628,57,247]
+ CRUSH rule 1 x 680 [335,963,382,486,749,257,795]
+ CRUSH rule 1 x 681 [690,462,623,466,49,471,774]
+ CRUSH rule 1 x 682 [196,588,154,257,807,776,367]
+ CRUSH rule 1 x 683 [627,25,421,160,873,102,345]
+ CRUSH rule 1 x 684 [38,804,592,158,991,264,652]
+ CRUSH rule 1 x 685 [841,368,548,362,166,211,154]
+ CRUSH rule 1 x 686 [336,287,525,440,166,993,911]
+ CRUSH rule 1 x 687 [20,682,924,653,356,16,917]
+ CRUSH rule 1 x 688 [463,371,780,556,385,883,115]
+ CRUSH rule 1 x 689 [569,250,78,816,847,775,333]
+ CRUSH rule 1 x 690 [551,144,587,263,378,394,970]
+ CRUSH rule 1 x 691 [766,464,446,533,449,541,451]
+ CRUSH rule 1 x 692 [739,634,18,245,624,35,268]
+ CRUSH rule 1 x 693 [339,297,118,330,817,91,828]
+ CRUSH rule 1 x 694 [405,26,830,181,533,166,488]
+ CRUSH rule 1 x 695 [622,576,597,535,600,593,300]
+ CRUSH rule 1 x 696 [558,902,689,13,715,28,664]
+ CRUSH rule 1 x 697 [818,222,406,691,427,863,153]
+ CRUSH rule 1 x 698 [178,48,402,233,841,604,468]
+ CRUSH rule 1 x 699 [450,244,180,919,335,332,747]
+ CRUSH rule 1 x 700 [502,771,987,706,416,240,68]
+ CRUSH rule 1 x 701 [4,612,782,216,853,303,585]
+ CRUSH rule 1 x 702 [177,630,232,923,281,708,466]
+ CRUSH rule 1 x 703 [354,178,389,393,778,803,796]
+ CRUSH rule 1 x 704 [646,601,156,171,603,116,655]
+ CRUSH rule 1 x 705 [921,401,890,265,244,690,372]
+ CRUSH rule 1 x 706 [652,877,562,452,26,323,923]
+ CRUSH rule 1 x 707 [345,745,67,716,789,576,2]
+ CRUSH rule 1 x 708 [333,607,180,469,170,555,939]
+ CRUSH rule 1 x 709 [45,187,302,115,896,579,733]
+ CRUSH rule 1 x 710 [94,855,43,199,18,948,449]
+ CRUSH rule 1 x 711 [227,653,731,150,356,842,534]
+ CRUSH rule 1 x 712 [398,953,136,870,181,408,895]
+ CRUSH rule 1 x 713 [116,800,503,662,635,579,53]
+ CRUSH rule 1 x 714 [111,629,866,709,902,557,875]
+ CRUSH rule 1 x 715 [531,291,486,382,192,807,322]
+ CRUSH rule 1 x 716 [169,541,291,42,343,724,138]
+ CRUSH rule 1 x 717 [417,446,994,894,239,494,237]
+ CRUSH rule 1 x 718 [992,383,298,844,377,463,544]
+ CRUSH rule 1 x 719 [936,674,324,759,194,409,828]
+ CRUSH rule 1 x 720 [370,188,174,464,644,218,214]
+ CRUSH rule 1 x 721 [320,859,278,259,170,957,177]
+ CRUSH rule 1 x 722 [7,2,673,129,96,445,823]
+ CRUSH rule 1 x 723 [270,553,831,662,38,101,985]
+ CRUSH rule 1 x 724 [666,822,708,895,633,800,616]
+ CRUSH rule 1 x 725 [794,406,875,459,981,751,359]
+ CRUSH rule 1 x 726 [420,556,341,292,240,68,966]
+ CRUSH rule 1 x 727 [561,461,129,635,965,610,105]
+ CRUSH rule 1 x 728 [951,330,196,756,589,849,753]
+ CRUSH rule 1 x 729 [656,644,436,591,27,119,572]
+ CRUSH rule 1 x 730 [3,558,629,184,50,765,760]
+ CRUSH rule 1 x 731 [852,89,75,735,713,113,528]
+ CRUSH rule 1 x 732 [983,840,869,976,697,307,368]
+ CRUSH rule 1 x 733 [285,396,388,122,387,364,880]
+ CRUSH rule 1 x 734 [125,510,402,640,676,501,535]
+ CRUSH rule 1 x 735 [417,773,686,504,459,912,690]
+ CRUSH rule 1 x 736 [749,396,632,550,779,109,845]
+ CRUSH rule 1 x 737 [644,991,946,135,448,903,482]
+ CRUSH rule 1 x 738 [449,683,290,220,245,525,429]
+ CRUSH rule 1 x 739 [341,220,641,454,740,661,146]
+ CRUSH rule 1 x 740 [874,524,674,650,472,282,214]
+ CRUSH rule 1 x 741 [189,472,712,798,715,757,863]
+ CRUSH rule 1 x 742 [912,581,114,245,730,21,687]
+ CRUSH rule 1 x 743 [654,914,425,441,763,39,451]
+ CRUSH rule 1 x 744 [725,295,579,377,162,447,843]
+ CRUSH rule 1 x 745 [787,858,850,506,612,735,926]
+ CRUSH rule 1 x 746 [757,848,704,30,47,940,450]
+ CRUSH rule 1 x 747 [700,81,867,681,801,64,879]
+ CRUSH rule 1 x 748 [557,436,238,664,293,865,304]
+ CRUSH rule 1 x 749 [772,622,337,42,156,302,383]
+ CRUSH rule 1 x 750 [946,97,376,677,316,670,169]
+ CRUSH rule 1 x 751 [996,618,343,911,83,22,388]
+ CRUSH rule 1 x 752 [746,887,695,868,610,950,88]
+ CRUSH rule 1 x 753 [741,14,463,479,172,192,481]
+ CRUSH rule 1 x 754 [648,349,333,355,65,63,336]
+ CRUSH rule 1 x 755 [157,460,466,187,959,674,192]
+ CRUSH rule 1 x 756 [416,97,197,497,227,3,850]
+ CRUSH rule 1 x 757 [599,839,776,410,256,823,121]
+ CRUSH rule 1 x 758 [994,218,620,256,361,749,165]
+ CRUSH rule 1 x 759 [959,682,514,745,100,519,15]
+ CRUSH rule 1 x 760 [518,943,215,83,706,137,345]
+ CRUSH rule 1 x 761 [285,849,420,324,987,338,373]
+ CRUSH rule 1 x 762 [591,313,41,335,110,696,664]
+ CRUSH rule 1 x 763 [908,411,200,740,292,295,387]
+ CRUSH rule 1 x 764 [787,234,894,485,883,711,70]
+ CRUSH rule 1 x 765 [327,921,882,393,444,792,402]
+ CRUSH rule 1 x 766 [84,161,878,704,416,144,357]
+ CRUSH rule 1 x 767 [370,895,702,701,890,2,251]
+ CRUSH rule 1 x 768 [826,760,879,864,460,474,645]
+ CRUSH rule 1 x 769 [67,768,663,735,814,66,213]
+ CRUSH rule 1 x 770 [593,909,482,259,5,550,961]
+ CRUSH rule 1 x 771 [309,935,121,578,937,685,933]
+ CRUSH rule 1 x 772 [12,125,797,301,348,419,891]
+ CRUSH rule 1 x 773 [253,466,820,549,591,193,783]
+ CRUSH rule 1 x 774 [164,390,705,109,881,505,890]
+ CRUSH rule 1 x 775 [703,47,43,973,643,406,885]
+ CRUSH rule 1 x 776 [728,231,80,916,2,850,396]
+ CRUSH rule 1 x 777 [981,621,568,729,869,952,563]
+ CRUSH rule 1 x 778 [411,456,544,597,789,784,65]
+ CRUSH rule 1 x 779 [346,121,519,921,587,48,772]
+ CRUSH rule 1 x 780 [476,39,288,381,303,29,17]
+ CRUSH rule 1 x 781 [10,130,585,844,729,705,714]
+ CRUSH rule 1 x 782 [462,246,581,902,623,877,812]
+ CRUSH rule 1 x 783 [580,373,153,775,668,661,626]
+ CRUSH rule 1 x 784 [413,113,978,990,994,56,481]
+ CRUSH rule 1 x 785 [341,856,332,354,59,581,632]
+ CRUSH rule 1 x 786 [411,140,313,393,215,618,490]
+ CRUSH rule 1 x 787 [605,522,211,813,636,224,600]
+ CRUSH rule 1 x 788 [226,545,35,142,726,851,194]
+ CRUSH rule 1 x 789 [545,320,414,702,731,277,237]
+ CRUSH rule 1 x 790 [414,748,816,327,130,115,788]
+ CRUSH rule 1 x 791 [660,906,406,697,916,322,124]
+ CRUSH rule 1 x 792 [287,392,514,204,75,789,406]
+ CRUSH rule 1 x 793 [631,133,850,713,720,487,376]
+ CRUSH rule 1 x 794 [931,517,543,210,963,898,811]
+ CRUSH rule 1 x 795 [551,962,477,948,425,434,268]
+ CRUSH rule 1 x 796 [814,4,95,27,368,300,646]
+ CRUSH rule 1 x 797 [64,201,299,734,605,864,596]
+ CRUSH rule 1 x 798 [422,530,114,431,565,716,473]
+ CRUSH rule 1 x 799 [824,32,679,562,266,549,859]
+ CRUSH rule 1 x 800 [862,623,489,637,861,196,941]
+ CRUSH rule 1 x 801 [145,550,329,324,734,160,219]
+ CRUSH rule 1 x 802 [570,19,847,308,387,518,846]
+ CRUSH rule 1 x 803 [151,812,662,358,880,349,834]
+ CRUSH rule 1 x 804 [467,93,264,863,176,842,663]
+ CRUSH rule 1 x 805 [621,223,938,809,591,686,121]
+ CRUSH rule 1 x 806 [898,957,805,430,499,584,640]
+ CRUSH rule 1 x 807 [354,531,422,159,921,431,802]
+ CRUSH rule 1 x 808 [7,96,76,897,446,2,166]
+ CRUSH rule 1 x 809 [70,734,719,56,687,21,23]
+ CRUSH rule 1 x 810 [701,18,972,327,771,649,620]
+ CRUSH rule 1 x 811 [248,547,103,728,901,264,948]
+ CRUSH rule 1 x 812 [230,576,821,566,993,762,675]
+ CRUSH rule 1 x 813 [805,114,683,629,845,462,285]
+ CRUSH rule 1 x 814 [54,619,973,741,497,894,401]
+ CRUSH rule 1 x 815 [679,412,613,132,969,411,314]
+ CRUSH rule 1 x 816 [919,448,826,414,36,289,44]
+ CRUSH rule 1 x 817 [765,830,436,521,332,458,260]
+ CRUSH rule 1 x 818 [415,566,644,687,692,414,769]
+ CRUSH rule 1 x 819 [721,319,865,750,546,859,523]
+ CRUSH rule 1 x 820 [218,301,333,190,686,179,535]
+ CRUSH rule 1 x 821 [185,795,680,953,329,750,621]
+ CRUSH rule 1 x 822 [356,261,54,522,900,103,883]
+ CRUSH rule 1 x 823 [220,281,549,456,64,306,282]
+ CRUSH rule 1 x 824 [292,809,887,74,776,788,559]
+ CRUSH rule 1 x 825 [949,778,101,311,110,480,161]
+ CRUSH rule 1 x 826 [767,818,833,927,356,954,910]
+ CRUSH rule 1 x 827 [631,83,406,635,657,713,212]
+ CRUSH rule 1 x 828 [288,986,445,26,414,607,937]
+ CRUSH rule 1 x 829 [990,667,915,694,974,453,669]
+ CRUSH rule 1 x 830 [152,571,778,505,685,209,448]
+ CRUSH rule 1 x 831 [814,563,630,97,582,107,142]
+ CRUSH rule 1 x 832 [235,641,616,110,979,844,656]
+ CRUSH rule 1 x 833 [657,565,922,140,825,457,764]
+ CRUSH rule 1 x 834 [907,231,644,13,617,130,83]
+ CRUSH rule 1 x 835 [784,262,771,264,612,238,537]
+ CRUSH rule 1 x 836 [951,158,366,710,43,427,351]
+ CRUSH rule 1 x 837 [556,498,334,633,895,627,903]
+ CRUSH rule 1 x 838 [329,274,964,547,119,342,983]
+ CRUSH rule 1 x 839 [568,209,939,364,658,747,47]
+ CRUSH rule 1 x 840 [45,579,842,70,655,862,815]
+ CRUSH rule 1 x 841 [652,702,24,605,152,93,226]
+ CRUSH rule 1 x 842 [629,984,314,895,408,897,575]
+ CRUSH rule 1 x 843 [799,690,688,648,151,812,486]
+ CRUSH rule 1 x 844 [694,600,534,700,569,11,899]
+ CRUSH rule 1 x 845 [332,30,179,93,951,324,611]
+ CRUSH rule 1 x 846 [452,251,712,719,404,739,606]
+ CRUSH rule 1 x 847 [399,681,847,739,13,555,363]
+ CRUSH rule 1 x 848 [303,138,440,346,547,216,700]
+ CRUSH rule 1 x 849 [666,346,708,873,64,694,847]
+ CRUSH rule 1 x 850 [644,511,345,844,545,337,358]
+ CRUSH rule 1 x 851 [527,546,737,425,100,331,95]
+ CRUSH rule 1 x 852 [31,809,94,618,156,853,469]
+ CRUSH rule 1 x 853 [483,330,869,184,46,942,774]
+ CRUSH rule 1 x 854 [697,953,968,143,502,955,441]
+ CRUSH rule 1 x 855 [837,996,239,621,32,191,686]
+ CRUSH rule 1 x 856 [712,40,547,430,195,857,224]
+ CRUSH rule 1 x 857 [77,984,576,551,568,96,12]
+ CRUSH rule 1 x 858 [412,384,841,465,572,576,688]
+ CRUSH rule 1 x 859 [173,760,26,300,87,567,463]
+ CRUSH rule 1 x 860 [776,429,328,917,658,783,699]
+ CRUSH rule 1 x 861 [705,405,477,50,73,714,901]
+ CRUSH rule 1 x 862 [809,44,788,938,964,177,490]
+ CRUSH rule 1 x 863 [349,496,963,178,675,853,172]
+ CRUSH rule 1 x 864 [717,858,101,239,992,244,43]
+ CRUSH rule 1 x 865 [857,603,586,262,550,289,850]
+ CRUSH rule 1 x 866 [394,304,71,96,642,155,255]
+ CRUSH rule 1 x 867 [640,773,663,974,261,296,988]
+ CRUSH rule 1 x 868 [613,950,712,663,528,460,643]
+ CRUSH rule 1 x 869 [973,889,524,22,671,477,718]
+ CRUSH rule 1 x 870 [505,35,386,498,348,503,54]
+ CRUSH rule 1 x 871 [239,264,262,773,781,734,387]
+ CRUSH rule 1 x 872 [21,767,456,748,783,797,180]
+ CRUSH rule 1 x 873 [954,666,980,264,435,233,199]
+ CRUSH rule 1 x 874 [54,510,947,1,500,119,93]
+ CRUSH rule 1 x 875 [809,418,452,462,88,673,634]
+ CRUSH rule 1 x 876 [483,457,61,248,523,277,322]
+ CRUSH rule 1 x 877 [542,531,952,939,710,179,181]
+ CRUSH rule 1 x 878 [217,674,857,644,678,809,329]
+ CRUSH rule 1 x 879 [999,475,134,250,319,357,145]
+ CRUSH rule 1 x 880 [678,573,935,385,570,651,319]
+ CRUSH rule 1 x 881 [394,835,789,802,587,155,570]
+ CRUSH rule 1 x 882 [467,382,353,56,979,674,974]
+ CRUSH rule 1 x 883 [802,744,237,337,50,96,202]
+ CRUSH rule 1 x 884 [653,660,638,700,31,558,389]
+ CRUSH rule 1 x 885 [898,704,307,445,879,872,174]
+ CRUSH rule 1 x 886 [434,357,938,641,737,8,56]
+ CRUSH rule 1 x 887 [297,226,711,428,370,318,472]
+ CRUSH rule 1 x 888 [863,324,443,213,902,25,806]
+ CRUSH rule 1 x 889 [105,102,308,163,947,548,399]
+ CRUSH rule 1 x 890 [550,248,606,704,615,708,996]
+ CRUSH rule 1 x 891 [575,928,880,891,826,763,706]
+ CRUSH rule 1 x 892 [259,862,133,271,292,162,53]
+ CRUSH rule 1 x 893 [902,880,543,542,37,942,672]
+ CRUSH rule 1 x 894 [180,169,916,43,945,713,648]
+ CRUSH rule 1 x 895 [725,849,182,129,177,272,599]
+ CRUSH rule 1 x 896 [951,34,874,537,969,123,210]
+ CRUSH rule 1 x 897 [810,352,73,939,943,895,12]
+ CRUSH rule 1 x 898 [979,433,719,411,787,359,342]
+ CRUSH rule 1 x 899 [685,668,534,932,399,156,124]
+ CRUSH rule 1 x 900 [530,978,41,894,941,681,380]
+ CRUSH rule 1 x 901 [740,107,336,175,574,706,157]
+ CRUSH rule 1 x 902 [800,743,693,310,67,111,178]
+ CRUSH rule 1 x 903 [230,267,842,266,550,769,66]
+ CRUSH rule 1 x 904 [346,949,460,973,696,91,957]
+ CRUSH rule 1 x 905 [530,397,619,958,576,973,685]
+ CRUSH rule 1 x 906 [80,426,138,672,73,776,30]
+ CRUSH rule 1 x 907 [365,968,475,297,296,724,664]
+ CRUSH rule 1 x 908 [204,832,742,809,862,745,484]
+ CRUSH rule 1 x 909 [883,989,146,959,366,59,686]
+ CRUSH rule 1 x 910 [549,593,249,853,792,769,824]
+ CRUSH rule 1 x 911 [325,847,352,214,851,732,789]
+ CRUSH rule 1 x 912 [874,888,582,796,557,601,226]
+ CRUSH rule 1 x 913 [331,463,342,574,989,362,925]
+ CRUSH rule 1 x 914 [836,468,601,732,607,275,70]
+ CRUSH rule 1 x 915 [245,228,100,661,799,13,126]
+ CRUSH rule 1 x 916 [77,967,364,435,27,474,255]
+ CRUSH rule 1 x 917 [239,60,866,221,772,967,725]
+ CRUSH rule 1 x 918 [988,115,922,80,201,544,583]
+ CRUSH rule 1 x 919 [783,139,696,1,848,169,888]
+ CRUSH rule 1 x 920 [623,408,685,953,974,696,532]
+ CRUSH rule 1 x 921 [105,799,144,90,399,373,633]
+ CRUSH rule 1 x 922 [887,505,652,348,514,806,952]
+ CRUSH rule 1 x 923 [223,318,552,458,743,871,964]
+ CRUSH rule 1 x 924 [25,778,366,333,163,801,584]
+ CRUSH rule 1 x 925 [912,601,297,682,770,173,969]
+ CRUSH rule 1 x 926 [968,133,739,144,814,155,709]
+ CRUSH rule 1 x 927 [277,724,214,988,690,342,465]
+ CRUSH rule 1 x 928 [554,203,658,789,298,299,847]
+ CRUSH rule 1 x 929 [761,802,367,528,758,522,744]
+ CRUSH rule 1 x 930 [814,61,788,736,660,491,832]
+ CRUSH rule 1 x 931 [29,193,61,41,343,664,487]
+ CRUSH rule 1 x 932 [446,198,862,534,168,35,530]
+ CRUSH rule 1 x 933 [352,742,216,321,525,44,568]
+ CRUSH rule 1 x 934 [730,2,332,631,613,249,533]
+ CRUSH rule 1 x 935 [731,23,736,79,361,992,772]
+ CRUSH rule 1 x 936 [322,975,20,904,827,603,138]
+ CRUSH rule 1 x 937 [822,221,841,161,723,137,630]
+ CRUSH rule 1 x 938 [557,850,66,630,499,404,286]
+ CRUSH rule 1 x 939 [150,11,971,371,124,785,408]
+ CRUSH rule 1 x 940 [638,398,169,616,333,751,25]
+ CRUSH rule 1 x 941 [730,342,929,577,451,838,964]
+ CRUSH rule 1 x 942 [62,292,166,814,587,172,237]
+ CRUSH rule 1 x 943 [165,314,519,548,41,726,759]
+ CRUSH rule 1 x 944 [199,625,766,176,194,297,678]
+ CRUSH rule 1 x 945 [946,999,699,303,38,81,952]
+ CRUSH rule 1 x 946 [595,93,852,142,503,647,933]
+ CRUSH rule 1 x 947 [800,582,356,93,716,117,922]
+ CRUSH rule 1 x 948 [132,551,139,920,87,46,81]
+ CRUSH rule 1 x 949 [792,920,466,380,97,568,799]
+ CRUSH rule 1 x 950 [111,345,176,543,879,954,355]
+ CRUSH rule 1 x 951 [414,619,648,655,364,971,829]
+ CRUSH rule 1 x 952 [775,469,500,356,287,4,16]
+ CRUSH rule 1 x 953 [349,1,5,251,168,680,141]
+ CRUSH rule 1 x 954 [570,940,410,249,929,394,129]
+ CRUSH rule 1 x 955 [729,774,823,800,7,127,536]
+ CRUSH rule 1 x 956 [519,141,575,625,738,475,169]
+ CRUSH rule 1 x 957 [242,709,611,97,760,309,393]
+ CRUSH rule 1 x 958 [84,217,227,253,246,604,346]
+ CRUSH rule 1 x 959 [270,413,918,789,703,608,543]
+ CRUSH rule 1 x 960 [458,192,307,279,920,139,855]
+ CRUSH rule 1 x 961 [981,388,777,546,359,660,455]
+ CRUSH rule 1 x 962 [623,834,277,134,729,246,856]
+ CRUSH rule 1 x 963 [291,167,714,468,109,373,485]
+ CRUSH rule 1 x 964 [28,156,788,127,598,215,361]
+ CRUSH rule 1 x 965 [675,557,290,517,840,510,59]
+ CRUSH rule 1 x 966 [836,306,946,283,642,606,929]
+ CRUSH rule 1 x 967 [966,386,735,837,392,116,19]
+ CRUSH rule 1 x 968 [864,756,690,121,328,122,433]
+ CRUSH rule 1 x 969 [729,625,480,769,512,882,518]
+ CRUSH rule 1 x 970 [800,362,646,582,309,102,576]
+ CRUSH rule 1 x 971 [737,381,153,684,298,166,344]
+ CRUSH rule 1 x 972 [952,245,720,884,334,311,754]
+ CRUSH rule 1 x 973 [356,455,579,857,832,596,549]
+ CRUSH rule 1 x 974 [545,758,586,596,269,790,116]
+ CRUSH rule 1 x 975 [336,191,202,146,720,897,330]
+ CRUSH rule 1 x 976 [446,208,757,620,252,846,397]
+ CRUSH rule 1 x 977 [202,896,196,956,763,126,783]
+ CRUSH rule 1 x 978 [612,324,996,225,418,583,514]
+ CRUSH rule 1 x 979 [843,457,675,650,958,657,677]
+ CRUSH rule 1 x 980 [60,914,881,626,850,759,398]
+ CRUSH rule 1 x 981 [702,749,937,153,724,514,536]
+ CRUSH rule 1 x 982 [298,928,738,167,99,668,395]
+ CRUSH rule 1 x 983 [723,572,395,358,900,37,927]
+ CRUSH rule 1 x 984 [723,864,804,935,846,993,950]
+ CRUSH rule 1 x 985 [945,459,868,211,524,954,911]
+ CRUSH rule 1 x 986 [772,664,535,169,297,996,864]
+ CRUSH rule 1 x 987 [88,324,312,843,661,580,76]
+ CRUSH rule 1 x 988 [522,927,131,996,351,685,865]
+ CRUSH rule 1 x 989 [578,332,208,605,975,207,155]
+ CRUSH rule 1 x 990 [638,228,414,311,738,698,340]
+ CRUSH rule 1 x 991 [530,221,451,422,879,916,754]
+ CRUSH rule 1 x 992 [925,705,275,81,234,310,117]
+ CRUSH rule 1 x 993 [991,301,43,469,830,242,382]
+ CRUSH rule 1 x 994 [276,51,868,683,843,815,557]
+ CRUSH rule 1 x 995 [288,836,753,790,758,120,158]
+ CRUSH rule 1 x 996 [887,983,252,686,470,345,459]
+ CRUSH rule 1 x 997 [110,924,386,79,705,697,210]
+ CRUSH rule 1 x 998 [435,830,485,853,926,730,786]
+ CRUSH rule 1 x 999 [876,738,357,913,723,51,15]
+ CRUSH rule 1 x 1000 [178,963,638,430,845,586,317]
+ CRUSH rule 1 x 1001 [99,519,66,759,583,944,739]
+ CRUSH rule 1 x 1002 [515,534,468,866,878,717,729]
+ CRUSH rule 1 x 1003 [104,611,937,698,94,67,614]
+ CRUSH rule 1 x 1004 [269,638,724,375,491,121,891]
+ CRUSH rule 1 x 1005 [369,223,309,409,822,39,597]
+ CRUSH rule 1 x 1006 [40,107,69,275,79,429,234]
+ CRUSH rule 1 x 1007 [978,111,416,758,454,640,5]
+ CRUSH rule 1 x 1008 [965,956,624,832,421,96,975]
+ CRUSH rule 1 x 1009 [598,476,356,695,919,566,234]
+ CRUSH rule 1 x 1010 [767,523,239,517,29,77,23]
+ CRUSH rule 1 x 1011 [289,871,207,576,347,698,48]
+ CRUSH rule 1 x 1012 [128,28,370,31,341,755,268]
+ CRUSH rule 1 x 1013 [979,765,660,812,666,187,808]
+ CRUSH rule 1 x 1014 [979,948,513,88,47,825,969]
+ CRUSH rule 1 x 1015 [277,790,396,672,542,647,145]
+ CRUSH rule 1 x 1016 [262,73,128,886,839,685,456]
+ CRUSH rule 1 x 1017 [150,269,61,499,832,591,637]
+ CRUSH rule 1 x 1018 [555,829,554,944,406,576,463]
+ CRUSH rule 1 x 1019 [513,356,265,446,65,288,768]
+ CRUSH rule 1 x 1020 [158,161,877,704,948,570,495]
+ CRUSH rule 1 x 1021 [915,998,957,285,546,202,676]
+ CRUSH rule 1 x 1022 [967,829,973,640,703,470,871]
+ CRUSH rule 1 x 1023 [488,257,614,859,325,419,50]
+ rule 1 (metadata) num_rep 7 result size == 7:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536,450,604,380,966,750]
+ CRUSH rule 1 x 1 [876,250,334,633,744,843,672,820]
+ CRUSH rule 1 x 2 [292,832,53,392,386,787,527,901]
+ CRUSH rule 1 x 3 [623,387,124,998,749,211,481,169]
+ CRUSH rule 1 x 4 [61,334,710,4,994,982,847,220]
+ CRUSH rule 1 x 5 [946,557,713,664,141,817,964,872]
+ CRUSH rule 1 x 6 [576,668,212,163,732,381,884,726]
+ CRUSH rule 1 x 7 [645,753,906,393,341,44,578,14]
+ CRUSH rule 1 x 8 [243,6,863,781,211,100,462,207]
+ CRUSH rule 1 x 9 [22,578,251,410,297,430,3,569]
+ CRUSH rule 1 x 10 [758,828,360,477,821,801,811,484]
+ CRUSH rule 1 x 11 [769,120,124,527,119,504,380,821]
+ CRUSH rule 1 x 12 [780,364,689,755,675,199,117,393]
+ CRUSH rule 1 x 13 [557,18,351,719,742,780,78,170]
+ CRUSH rule 1 x 14 [59,561,249,461,971,835,855,76]
+ CRUSH rule 1 x 15 [718,928,993,21,76,313,437,664]
+ CRUSH rule 1 x 16 [673,632,841,954,788,90,786,969]
+ CRUSH rule 1 x 17 [648,43,560,514,142,289,935,605]
+ CRUSH rule 1 x 18 [654,219,181,568,381,253,883,394]
+ CRUSH rule 1 x 19 [850,545,377,848,863,543,51,834]
+ CRUSH rule 1 x 20 [717,785,974,5,225,552,975,636]
+ CRUSH rule 1 x 21 [420,57,519,306,312,983,263,267]
+ CRUSH rule 1 x 22 [503,998,193,821,634,684,557,633]
+ CRUSH rule 1 x 23 [411,663,168,110,899,488,477,468]
+ CRUSH rule 1 x 24 [266,861,353,1,456,128,800,309]
+ CRUSH rule 1 x 25 [760,483,818,600,509,951,248,908]
+ CRUSH rule 1 x 26 [903,24,573,718,112,694,501,909]
+ CRUSH rule 1 x 27 [946,188,289,510,687,827,676,560]
+ CRUSH rule 1 x 28 [69,312,73,198,256,629,770,569]
+ CRUSH rule 1 x 29 [844,883,337,628,496,405,719,581]
+ CRUSH rule 1 x 30 [621,18,613,794,910,936,426,522]
+ CRUSH rule 1 x 31 [784,943,814,539,962,392,813,217]
+ CRUSH rule 1 x 32 [173,374,369,972,315,83,428,63]
+ CRUSH rule 1 x 33 [698,336,357,966,582,407,618,288]
+ CRUSH rule 1 x 34 [168,836,210,798,904,190,663,877]
+ CRUSH rule 1 x 35 [274,509,534,818,912,671,75,580]
+ CRUSH rule 1 x 36 [318,215,153,628,87,407,676,524]
+ CRUSH rule 1 x 37 [173,604,109,935,203,401,311,758]
+ CRUSH rule 1 x 38 [708,444,683,604,722,900,929,910]
+ CRUSH rule 1 x 39 [662,198,417,680,226,342,856,248]
+ CRUSH rule 1 x 40 [620,801,414,78,560,766,980,503]
+ CRUSH rule 1 x 41 [811,264,177,127,148,791,930,74]
+ CRUSH rule 1 x 42 [863,179,527,660,133,529,456,713]
+ CRUSH rule 1 x 43 [686,822,988,228,791,549,514,40]
+ CRUSH rule 1 x 44 [396,222,46,841,536,140,160,527]
+ CRUSH rule 1 x 45 [991,694,253,142,54,422,658,876]
+ CRUSH rule 1 x 46 [420,909,184,285,508,458,45,390]
+ CRUSH rule 1 x 47 [467,211,605,207,241,881,959,800]
+ CRUSH rule 1 x 48 [955,329,368,168,698,787,738,47]
+ CRUSH rule 1 x 49 [974,891,931,29,813,506,822,628]
+ CRUSH rule 1 x 50 [870,441,691,823,761,6,83,344]
+ CRUSH rule 1 x 51 [182,930,25,936,97,260,406,281]
+ CRUSH rule 1 x 52 [704,812,894,794,481,37,304,899]
+ CRUSH rule 1 x 53 [185,713,631,280,345,558,882,503]
+ CRUSH rule 1 x 54 [270,441,100,82,983,930,339,902]
+ CRUSH rule 1 x 55 [895,734,958,793,651,572,508,763]
+ CRUSH rule 1 x 56 [564,963,683,324,40,189,77,500]
+ CRUSH rule 1 x 57 [738,130,208,973,498,861,670,67]
+ CRUSH rule 1 x 58 [524,113,806,903,531,334,8,762]
+ CRUSH rule 1 x 59 [408,337,668,529,34,384,643,511]
+ CRUSH rule 1 x 60 [228,790,857,309,616,895,194,277]
+ CRUSH rule 1 x 61 [154,843,717,467,883,536,812,14]
+ CRUSH rule 1 x 62 [594,811,549,276,693,917,45,723]
+ CRUSH rule 1 x 63 [646,67,884,925,941,434,705,268]
+ CRUSH rule 1 x 64 [175,542,155,837,594,197,451,891]
+ CRUSH rule 1 x 65 [745,619,131,867,269,62,862,221]
+ CRUSH rule 1 x 66 [275,468,23,35,328,432,334,656]
+ CRUSH rule 1 x 67 [246,958,524,493,636,227,783,593]
+ CRUSH rule 1 x 68 [711,473,403,228,835,126,705,114]
+ CRUSH rule 1 x 69 [493,924,850,939,950,105,871,361]
+ CRUSH rule 1 x 70 [30,499,644,33,804,654,684,411]
+ CRUSH rule 1 x 71 [984,883,574,716,575,391,587,264]
+ CRUSH rule 1 x 72 [71,286,942,363,628,632,642,529]
+ CRUSH rule 1 x 73 [922,618,3,371,464,442,835,705]
+ CRUSH rule 1 x 74 [629,414,185,573,678,338,633,560]
+ CRUSH rule 1 x 75 [222,20,174,820,312,361,366,258]
+ CRUSH rule 1 x 76 [262,366,339,290,718,143,735,953]
+ CRUSH rule 1 x 77 [638,469,992,280,773,892,197,690]
+ CRUSH rule 1 x 78 [324,511,788,7,308,228,183,917]
+ CRUSH rule 1 x 79 [577,990,64,94,447,924,339,24]
+ CRUSH rule 1 x 80 [501,95,278,903,631,842,51,766]
+ CRUSH rule 1 x 81 [506,812,9,698,173,664,247,963]
+ CRUSH rule 1 x 82 [222,145,80,785,835,745,580,51]
+ CRUSH rule 1 x 83 [71,634,61,91,856,529,66,197]
+ CRUSH rule 1 x 84 [49,761,773,368,318,708,681,618]
+ CRUSH rule 1 x 85 [985,896,708,861,325,307,567,908]
+ CRUSH rule 1 x 86 [537,745,93,524,466,356,38,326]
+ CRUSH rule 1 x 87 [997,317,463,626,685,458,909,49]
+ CRUSH rule 1 x 88 [957,350,890,857,375,176,99,737]
+ CRUSH rule 1 x 89 [399,730,148,314,159,982,320,921]
+ CRUSH rule 1 x 90 [943,706,683,267,579,141,412,184]
+ CRUSH rule 1 x 91 [22,368,149,928,140,529,495,299]
+ CRUSH rule 1 x 92 [532,424,426,773,623,197,167,634]
+ CRUSH rule 1 x 93 [218,489,405,681,549,201,343,949]
+ CRUSH rule 1 x 94 [181,96,102,515,776,365,82,422]
+ CRUSH rule 1 x 95 [343,957,820,139,334,37,648,661]
+ CRUSH rule 1 x 96 [861,270,87,797,0,245,204,750]
+ CRUSH rule 1 x 97 [459,706,45,328,274,605,83,542]
+ CRUSH rule 1 x 98 [327,867,353,948,728,280,270,511]
+ CRUSH rule 1 x 99 [974,133,468,906,235,988,37,138]
+ CRUSH rule 1 x 100 [32,445,547,371,960,885,9,168]
+ CRUSH rule 1 x 101 [142,90,337,950,970,570,12,369]
+ CRUSH rule 1 x 102 [172,129,139,22,403,867,923,106]
+ CRUSH rule 1 x 103 [630,47,161,356,911,421,933,231]
+ CRUSH rule 1 x 104 [758,133,278,11,947,799,401,85]
+ CRUSH rule 1 x 105 [843,604,47,33,401,632,434,121]
+ CRUSH rule 1 x 106 [28,681,193,679,990,343,878,493]
+ CRUSH rule 1 x 107 [74,320,85,819,315,253,589,614]
+ CRUSH rule 1 x 108 [875,593,575,517,107,153,631,996]
+ CRUSH rule 1 x 109 [411,985,811,720,198,666,856,296]
+ CRUSH rule 1 x 110 [440,774,799,660,715,167,510,472]
+ CRUSH rule 1 x 111 [405,742,276,359,936,360,18,949]
+ CRUSH rule 1 x 112 [143,181,922,545,185,303,725,413]
+ CRUSH rule 1 x 113 [153,846,160,903,789,897,738,253]
+ CRUSH rule 1 x 114 [804,892,939,20,312,692,598,418]
+ CRUSH rule 1 x 115 [588,508,958,580,232,722,421,39]
+ CRUSH rule 1 x 116 [327,148,637,486,712,464,9,448]
+ CRUSH rule 1 x 117 [95,594,989,131,714,275,725,142]
+ CRUSH rule 1 x 118 [80,957,897,239,359,432,766,210]
+ CRUSH rule 1 x 119 [386,932,951,768,679,300,570,278]
+ CRUSH rule 1 x 120 [366,312,653,936,71,241,49,126]
+ CRUSH rule 1 x 121 [129,154,847,16,471,481,424,868]
+ CRUSH rule 1 x 122 [873,1,110,939,90,412,551,43]
+ CRUSH rule 1 x 123 [533,415,789,600,713,800,877,248]
+ CRUSH rule 1 x 124 [461,691,898,723,957,759,482,254]
+ CRUSH rule 1 x 125 [342,599,830,402,615,994,736,737]
+ CRUSH rule 1 x 126 [819,781,822,548,279,255,689,209]
+ CRUSH rule 1 x 127 [437,893,585,707,353,189,909,809]
+ CRUSH rule 1 x 128 [679,994,982,550,991,324,666,691]
+ CRUSH rule 1 x 129 [380,685,947,302,698,144,149,51]
+ CRUSH rule 1 x 130 [992,52,466,867,998,777,270,425]
+ CRUSH rule 1 x 131 [469,90,208,599,829,656,203,667]
+ CRUSH rule 1 x 132 [571,250,316,535,54,418,922,597]
+ CRUSH rule 1 x 133 [964,728,329,902,108,118,14,444]
+ CRUSH rule 1 x 134 [999,19,716,963,323,559,893,281]
+ CRUSH rule 1 x 135 [634,101,52,938,413,573,712,649]
+ CRUSH rule 1 x 136 [114,889,692,768,694,279,846,890]
+ CRUSH rule 1 x 137 [839,8,959,280,922,870,363,323]
+ CRUSH rule 1 x 138 [967,949,138,451,292,548,400,885]
+ CRUSH rule 1 x 139 [308,711,736,247,632,126,384,58]
+ CRUSH rule 1 x 140 [764,936,926,55,331,115,178,532]
+ CRUSH rule 1 x 141 [423,302,112,216,603,873,193,258]
+ CRUSH rule 1 x 142 [252,821,715,340,635,668,424,87]
+ CRUSH rule 1 x 143 [33,808,518,477,325,316,266,70]
+ CRUSH rule 1 x 144 [472,88,969,162,401,771,697,610]
+ CRUSH rule 1 x 145 [242,208,252,604,266,743,577,348]
+ CRUSH rule 1 x 146 [290,70,570,384,934,856,929,196]
+ CRUSH rule 1 x 147 [447,352,657,493,467,918,514,546]
+ CRUSH rule 1 x 148 [212,644,432,658,109,275,352,820]
+ CRUSH rule 1 x 149 [9,775,87,35,260,646,406,556]
+ CRUSH rule 1 x 150 [166,456,582,144,324,340,484,553]
+ CRUSH rule 1 x 151 [811,875,307,20,782,229,671,883]
+ CRUSH rule 1 x 152 [449,617,223,9,182,407,807,50]
+ CRUSH rule 1 x 153 [523,537,695,627,959,613,942,864]
+ CRUSH rule 1 x 154 [208,559,874,597,243,706,443,98]
+ CRUSH rule 1 x 155 [569,325,192,296,367,848,58,641]
+ CRUSH rule 1 x 156 [488,121,521,213,595,837,271,229]
+ CRUSH rule 1 x 157 [140,723,633,260,487,856,384,446]
+ CRUSH rule 1 x 158 [786,451,320,239,667,632,899,902]
+ CRUSH rule 1 x 159 [134,664,517,821,667,944,209,641]
+ CRUSH rule 1 x 160 [690,112,414,990,183,590,242,999]
+ CRUSH rule 1 x 161 [324,912,397,423,991,284,909,642]
+ CRUSH rule 1 x 162 [748,567,284,183,463,336,148,88]
+ CRUSH rule 1 x 163 [575,499,31,816,749,737,587,854]
+ CRUSH rule 1 x 164 [314,489,308,326,51,568,110,329]
+ CRUSH rule 1 x 165 [116,209,750,53,813,640,524,389]
+ CRUSH rule 1 x 166 [352,706,701,810,718,527,548,676]
+ CRUSH rule 1 x 167 [27,743,174,142,551,1,935,266]
+ CRUSH rule 1 x 168 [953,898,880,660,500,799,667,463]
+ CRUSH rule 1 x 169 [912,147,266,547,331,770,601,909]
+ CRUSH rule 1 x 170 [421,515,828,844,151,981,835,840]
+ CRUSH rule 1 x 171 [488,584,880,964,936,196,100,910]
+ CRUSH rule 1 x 172 [366,443,957,66,162,693,36,356]
+ CRUSH rule 1 x 173 [863,291,625,287,158,496,471,529]
+ CRUSH rule 1 x 174 [263,555,650,410,339,616,780,932]
+ CRUSH rule 1 x 175 [875,961,361,575,33,109,51,211]
+ CRUSH rule 1 x 176 [745,83,701,680,250,420,240,316]
+ CRUSH rule 1 x 177 [128,244,41,123,422,902,756,647]
+ CRUSH rule 1 x 178 [155,41,264,777,314,564,856,992]
+ CRUSH rule 1 x 179 [593,833,202,183,971,38,724,923]
+ CRUSH rule 1 x 180 [154,734,17,831,824,522,736,846]
+ CRUSH rule 1 x 181 [289,675,723,800,166,712,168,224]
+ CRUSH rule 1 x 182 [730,931,560,209,943,261,485,571]
+ CRUSH rule 1 x 183 [639,237,794,815,827,400,109,903]
+ CRUSH rule 1 x 184 [704,312,685,645,691,778,74,45]
+ CRUSH rule 1 x 185 [97,100,762,82,999,542,485,511]
+ CRUSH rule 1 x 186 [26,665,554,215,280,421,369,270]
+ CRUSH rule 1 x 187 [649,14,740,494,402,684,566,378]
+ CRUSH rule 1 x 188 [682,695,590,743,927,945,833,650]
+ CRUSH rule 1 x 189 [325,693,726,51,448,169,37,1]
+ CRUSH rule 1 x 190 [399,933,136,955,57,504,527,237]
+ CRUSH rule 1 x 191 [629,533,17,126,60,146,999,754]
+ CRUSH rule 1 x 192 [503,578,38,492,222,251,123,759]
+ CRUSH rule 1 x 193 [546,333,651,678,823,652,359,721]
+ CRUSH rule 1 x 194 [242,473,58,655,311,277,792,887]
+ CRUSH rule 1 x 195 [625,719,135,81,636,513,755,471]
+ CRUSH rule 1 x 196 [357,114,125,867,250,522,413,834]
+ CRUSH rule 1 x 197 [306,954,453,873,211,334,666,316]
+ CRUSH rule 1 x 198 [863,791,311,911,206,61,355,574]
+ CRUSH rule 1 x 199 [935,906,929,252,893,75,960,369]
+ CRUSH rule 1 x 200 [373,774,229,454,909,611,132,271]
+ CRUSH rule 1 x 201 [659,320,477,313,779,16,495,76]
+ CRUSH rule 1 x 202 [260,433,524,880,223,818,153,272]
+ CRUSH rule 1 x 203 [36,239,675,971,703,209,669,676]
+ CRUSH rule 1 x 204 [92,516,993,728,279,478,697,881]
+ CRUSH rule 1 x 205 [68,395,473,45,683,662,776,463]
+ CRUSH rule 1 x 206 [570,530,642,380,311,398,230,367]
+ CRUSH rule 1 x 207 [834,457,850,917,456,296,76,708]
+ CRUSH rule 1 x 208 [927,484,640,976,803,626,96,841]
+ CRUSH rule 1 x 209 [878,66,58,940,48,233,522,185]
+ CRUSH rule 1 x 210 [572,981,484,29,0,426,14,921]
+ CRUSH rule 1 x 211 [107,597,780,857,895,57,922,372]
+ CRUSH rule 1 x 212 [389,107,838,624,698,562,857,894]
+ CRUSH rule 1 x 213 [497,717,567,728,905,134,687,903]
+ CRUSH rule 1 x 214 [798,65,254,572,32,393,579,79]
+ CRUSH rule 1 x 215 [233,419,283,638,520,891,982,826]
+ CRUSH rule 1 x 216 [494,464,742,523,459,174,973,898]
+ CRUSH rule 1 x 217 [352,396,309,938,66,41,264,6]
+ CRUSH rule 1 x 218 [895,864,988,650,593,740,34,497]
+ CRUSH rule 1 x 219 [222,534,277,242,658,482,697,805]
+ CRUSH rule 1 x 220 [281,19,584,563,858,965,686,982]
+ CRUSH rule 1 x 221 [64,928,963,130,312,394,61,559]
+ CRUSH rule 1 x 222 [40,544,161,199,861,644,597,904]
+ CRUSH rule 1 x 223 [645,556,159,417,46,135,465,429]
+ CRUSH rule 1 x 224 [647,165,957,263,961,576,329,320]
+ CRUSH rule 1 x 225 [219,714,858,747,461,175,606,465]
+ CRUSH rule 1 x 226 [372,511,181,277,695,404,876,984]
+ CRUSH rule 1 x 227 [925,156,714,863,257,74,966,217]
+ CRUSH rule 1 x 228 [682,404,839,263,521,195,261,389]
+ CRUSH rule 1 x 229 [880,838,770,891,236,542,262,884]
+ CRUSH rule 1 x 230 [328,659,916,468,646,572,93,880]
+ CRUSH rule 1 x 231 [320,383,669,109,627,621,50,182]
+ CRUSH rule 1 x 232 [924,846,394,319,43,519,106,877]
+ CRUSH rule 1 x 233 [948,652,575,838,498,395,796,835]
+ CRUSH rule 1 x 234 [484,943,42,575,936,180,103,95]
+ CRUSH rule 1 x 235 [750,65,590,168,870,308,471,753]
+ CRUSH rule 1 x 236 [551,787,490,136,370,833,573,128]
+ CRUSH rule 1 x 237 [390,157,166,251,752,75,327,509]
+ CRUSH rule 1 x 238 [570,6,989,707,514,905,894,884]
+ CRUSH rule 1 x 239 [729,959,376,975,496,49,426,427]
+ CRUSH rule 1 x 240 [981,241,156,767,631,576,450,677]
+ CRUSH rule 1 x 241 [310,816,641,177,996,454,413,136]
+ CRUSH rule 1 x 242 [161,63,642,837,763,458,234,756]
+ CRUSH rule 1 x 243 [180,394,33,683,189,419,799,21]
+ CRUSH rule 1 x 244 [52,174,685,189,78,310,785,107]
+ CRUSH rule 1 x 245 [523,121,915,84,386,409,605,837]
+ CRUSH rule 1 x 246 [362,893,390,487,817,88,989,999]
+ CRUSH rule 1 x 247 [382,184,116,34,143,15,590,840]
+ CRUSH rule 1 x 248 [129,114,852,469,359,291,713,237]
+ CRUSH rule 1 x 249 [159,683,91,856,475,369,886,650]
+ CRUSH rule 1 x 250 [404,945,569,955,228,910,270,619]
+ CRUSH rule 1 x 251 [661,225,738,757,37,642,58,354]
+ CRUSH rule 1 x 252 [961,226,542,103,945,885,838,131]
+ CRUSH rule 1 x 253 [651,97,225,364,189,248,797,675]
+ CRUSH rule 1 x 254 [123,33,741,692,599,11,605,453]
+ CRUSH rule 1 x 255 [314,649,891,855,517,344,607,95]
+ CRUSH rule 1 x 256 [315,215,651,126,470,849,189,627]
+ CRUSH rule 1 x 257 [825,264,867,141,529,409,291,732]
+ CRUSH rule 1 x 258 [624,789,370,723,131,982,863,427]
+ CRUSH rule 1 x 259 [602,542,70,563,947,723,77,191]
+ CRUSH rule 1 x 260 [717,878,43,56,377,481,533,646]
+ CRUSH rule 1 x 261 [145,517,20,903,786,939,516,136]
+ CRUSH rule 1 x 262 [223,1,561,420,680,16,88,534]
+ CRUSH rule 1 x 263 [462,211,405,508,787,669,773,979]
+ CRUSH rule 1 x 264 [654,471,266,662,135,564,715,916]
+ CRUSH rule 1 x 265 [302,794,704,798,659,487,833,987]
+ CRUSH rule 1 x 266 [202,132,884,209,551,984,7,557]
+ CRUSH rule 1 x 267 [282,938,657,113,672,993,972,645]
+ CRUSH rule 1 x 268 [338,309,356,278,928,797,715,536]
+ CRUSH rule 1 x 269 [738,122,266,200,894,118,146,14]
+ CRUSH rule 1 x 270 [707,982,946,196,407,804,476,571]
+ CRUSH rule 1 x 271 [705,432,364,735,512,595,263,138]
+ CRUSH rule 1 x 272 [756,545,942,56,542,449,710,779]
+ CRUSH rule 1 x 273 [197,502,527,721,239,648,982,735]
+ CRUSH rule 1 x 274 [992,44,653,573,527,702,370,990]
+ CRUSH rule 1 x 275 [544,789,170,434,23,926,992,823]
+ CRUSH rule 1 x 276 [658,467,577,268,336,5,634,98]
+ CRUSH rule 1 x 277 [143,490,880,483,928,272,783,648]
+ CRUSH rule 1 x 278 [492,647,355,282,834,64,350,600]
+ CRUSH rule 1 x 279 [517,792,604,987,527,894,952,250]
+ CRUSH rule 1 x 280 [825,740,27,848,514,750,895,914]
+ CRUSH rule 1 x 281 [224,629,120,562,616,200,443,604]
+ CRUSH rule 1 x 282 [298,661,380,416,35,585,939,879]
+ CRUSH rule 1 x 283 [311,606,208,50,913,678,369,544]
+ CRUSH rule 1 x 284 [771,466,371,743,672,119,60,546]
+ CRUSH rule 1 x 285 [693,362,404,676,797,531,582,975]
+ CRUSH rule 1 x 286 [364,477,285,167,270,617,699,627]
+ CRUSH rule 1 x 287 [591,611,828,995,170,987,137,890]
+ CRUSH rule 1 x 288 [965,541,848,796,251,668,195,538]
+ CRUSH rule 1 x 289 [225,551,948,877,219,167,795,377]
+ CRUSH rule 1 x 290 [577,762,777,751,291,349,473,209]
+ CRUSH rule 1 x 291 [160,903,477,381,490,559,557,86]
+ CRUSH rule 1 x 292 [873,598,216,666,222,228,806,911]
+ CRUSH rule 1 x 293 [100,234,874,47,28,452,775,636]
+ CRUSH rule 1 x 294 [285,943,379,520,725,547,459,833]
+ CRUSH rule 1 x 295 [938,262,880,327,687,3,440,73]
+ CRUSH rule 1 x 296 [850,327,86,472,1,776,266,82]
+ CRUSH rule 1 x 297 [951,53,99,558,753,228,232,343]
+ CRUSH rule 1 x 298 [173,336,85,766,910,657,213,286]
+ CRUSH rule 1 x 299 [598,591,315,386,895,296,924,106]
+ CRUSH rule 1 x 300 [531,957,62,459,156,538,904,838]
+ CRUSH rule 1 x 301 [823,628,23,858,629,808,220,432]
+ CRUSH rule 1 x 302 [184,80,780,871,531,211,400,365]
+ CRUSH rule 1 x 303 [521,766,222,830,988,275,561,905]
+ CRUSH rule 1 x 304 [980,127,807,507,555,245,214,944]
+ CRUSH rule 1 x 305 [153,816,22,927,696,911,685,838]
+ CRUSH rule 1 x 306 [423,739,664,753,178,431,761,648]
+ CRUSH rule 1 x 307 [997,557,682,456,479,631,459,250]
+ CRUSH rule 1 x 308 [991,874,534,465,330,284,976,551]
+ CRUSH rule 1 x 309 [860,394,724,858,246,866,857,153]
+ CRUSH rule 1 x 310 [589,818,546,201,94,653,90,855]
+ CRUSH rule 1 x 311 [477,774,225,590,830,559,256,798]
+ CRUSH rule 1 x 312 [887,853,950,354,58,23,497,929]
+ CRUSH rule 1 x 313 [802,646,447,416,557,118,24,81]
+ CRUSH rule 1 x 314 [654,974,229,511,562,916,952,599]
+ CRUSH rule 1 x 315 [767,227,28,740,828,156,749,841]
+ CRUSH rule 1 x 316 [778,83,733,359,858,319,761,725]
+ CRUSH rule 1 x 317 [184,418,642,986,939,675,892,86]
+ CRUSH rule 1 x 318 [525,410,500,543,212,95,290,97]
+ CRUSH rule 1 x 319 [476,724,569,382,409,521,800,868]
+ CRUSH rule 1 x 320 [149,610,697,296,818,955,523,366]
+ CRUSH rule 1 x 321 [710,79,667,671,234,4,868,841]
+ CRUSH rule 1 x 322 [175,275,323,333,744,718,187,380]
+ CRUSH rule 1 x 323 [819,604,638,792,316,544,236,404]
+ CRUSH rule 1 x 324 [16,745,511,439,272,95,668,959]
+ CRUSH rule 1 x 325 [486,400,872,873,251,68,462,268]
+ CRUSH rule 1 x 326 [613,765,207,19,359,370,461,509]
+ CRUSH rule 1 x 327 [125,289,738,408,456,784,750,669]
+ CRUSH rule 1 x 328 [807,383,476,583,645,141,33,806]
+ CRUSH rule 1 x 329 [588,938,599,432,446,840,516,713]
+ CRUSH rule 1 x 330 [932,644,41,611,209,406,420,520]
+ CRUSH rule 1 x 331 [341,953,950,537,578,862,624,649]
+ CRUSH rule 1 x 332 [153,726,459,950,466,804,644,821]
+ CRUSH rule 1 x 333 [745,845,853,860,52,615,243,633]
+ CRUSH rule 1 x 334 [614,751,807,58,396,159,408,175]
+ CRUSH rule 1 x 335 [518,721,221,283,454,187,635,367]
+ CRUSH rule 1 x 336 [389,424,77,309,5,898,698,533]
+ CRUSH rule 1 x 337 [753,508,765,720,221,807,956,907]
+ CRUSH rule 1 x 338 [128,810,490,753,406,760,69,11]
+ CRUSH rule 1 x 339 [430,308,58,751,856,823,607,953]
+ CRUSH rule 1 x 340 [541,44,630,231,289,966,707,328]
+ CRUSH rule 1 x 341 [402,26,631,439,165,928,720,503]
+ CRUSH rule 1 x 342 [982,57,992,461,131,32,516,661]
+ CRUSH rule 1 x 343 [833,412,572,732,107,805,660,655]
+ CRUSH rule 1 x 344 [784,533,792,41,642,869,142,114]
+ CRUSH rule 1 x 345 [546,300,304,691,763,556,127,732]
+ CRUSH rule 1 x 346 [302,420,428,891,357,124,419,962]
+ CRUSH rule 1 x 347 [488,778,101,217,366,442,783,661]
+ CRUSH rule 1 x 348 [903,744,937,718,85,314,862,513]
+ CRUSH rule 1 x 349 [471,547,582,306,600,486,795,143]
+ CRUSH rule 1 x 350 [348,221,823,335,383,708,841,164]
+ CRUSH rule 1 x 351 [961,582,705,346,361,32,766,775]
+ CRUSH rule 1 x 352 [728,137,461,298,36,903,899,665]
+ CRUSH rule 1 x 353 [904,202,184,447,58,294,279,616]
+ CRUSH rule 1 x 354 [345,226,319,256,544,311,612,33]
+ CRUSH rule 1 x 355 [50,430,175,43,187,458,985,412]
+ CRUSH rule 1 x 356 [87,185,55,423,829,1,629,228]
+ CRUSH rule 1 x 357 [762,459,921,473,182,231,891,656]
+ CRUSH rule 1 x 358 [908,25,280,6,808,676,874,643]
+ CRUSH rule 1 x 359 [484,15,132,121,394,423,397,52]
+ CRUSH rule 1 x 360 [173,378,337,702,145,499,29,529]
+ CRUSH rule 1 x 361 [404,577,115,25,56,914,643,286]
+ CRUSH rule 1 x 362 [403,1,422,945,132,685,265,35]
+ CRUSH rule 1 x 363 [639,911,510,162,418,294,444,613]
+ CRUSH rule 1 x 364 [752,689,610,990,665,222,203,17]
+ CRUSH rule 1 x 365 [956,999,212,230,624,84,113,373]
+ CRUSH rule 1 x 366 [860,925,924,763,687,851,59,914]
+ CRUSH rule 1 x 367 [205,609,647,665,969,720,685,641]
+ CRUSH rule 1 x 368 [301,284,810,169,78,340,616,93]
+ CRUSH rule 1 x 369 [452,658,339,217,674,210,284,184]
+ CRUSH rule 1 x 370 [11,467,695,989,394,576,850,419]
+ CRUSH rule 1 x 371 [124,487,55,514,313,411,797,547]
+ CRUSH rule 1 x 372 [253,48,979,846,207,631,212,241]
+ CRUSH rule 1 x 373 [715,605,775,748,227,493,128,207]
+ CRUSH rule 1 x 374 [191,887,920,764,223,714,961,760]
+ CRUSH rule 1 x 375 [711,385,651,665,15,71,934,619]
+ CRUSH rule 1 x 376 [597,818,49,458,415,755,446,897]
+ CRUSH rule 1 x 377 [294,256,933,771,184,861,654,487]
+ CRUSH rule 1 x 378 [34,151,681,707,552,127,728,860]
+ CRUSH rule 1 x 379 [869,136,315,378,813,153,115,557]
+ CRUSH rule 1 x 380 [294,97,575,791,690,482,255,806]
+ CRUSH rule 1 x 381 [119,710,219,827,328,886,773,496]
+ CRUSH rule 1 x 382 [69,631,508,706,697,168,276,56]
+ CRUSH rule 1 x 383 [922,588,589,925,471,601,29,197]
+ CRUSH rule 1 x 384 [221,945,671,117,857,655,488,435]
+ CRUSH rule 1 x 385 [561,737,953,723,658,368,910,329]
+ CRUSH rule 1 x 386 [335,442,788,696,507,716,232,692]
+ CRUSH rule 1 x 387 [514,43,353,88,100,842,164,934]
+ CRUSH rule 1 x 388 [587,89,157,996,915,927,474,267]
+ CRUSH rule 1 x 389 [109,641,255,466,372,563,340,222]
+ CRUSH rule 1 x 390 [925,149,421,489,599,810,852,196]
+ CRUSH rule 1 x 391 [267,87,387,527,768,873,481,136]
+ CRUSH rule 1 x 392 [382,485,370,849,936,636,901,82]
+ CRUSH rule 1 x 393 [425,721,221,753,268,463,652,543]
+ CRUSH rule 1 x 394 [898,18,38,793,173,738,15,591]
+ CRUSH rule 1 x 395 [806,876,269,679,32,744,126,179]
+ CRUSH rule 1 x 396 [790,970,437,449,875,395,726,935]
+ CRUSH rule 1 x 397 [136,363,507,613,11,30,996,558]
+ CRUSH rule 1 x 398 [914,116,558,258,722,904,349,672]
+ CRUSH rule 1 x 399 [261,94,299,202,174,622,749,410]
+ CRUSH rule 1 x 400 [661,197,338,461,977,848,536,592]
+ CRUSH rule 1 x 401 [953,979,287,803,41,349,79,32]
+ CRUSH rule 1 x 402 [738,819,618,522,667,334,658,449]
+ CRUSH rule 1 x 403 [573,238,425,546,130,68,202,650]
+ CRUSH rule 1 x 404 [526,848,790,253,922,820,299,577]
+ CRUSH rule 1 x 405 [582,505,330,334,201,110,776,296]
+ CRUSH rule 1 x 406 [768,324,493,60,186,165,718,578]
+ CRUSH rule 1 x 407 [260,951,437,587,692,648,72,345]
+ CRUSH rule 1 x 408 [657,81,770,734,830,821,246,695]
+ CRUSH rule 1 x 409 [498,89,182,423,672,152,213,806]
+ CRUSH rule 1 x 410 [28,793,737,352,166,645,949,507]
+ CRUSH rule 1 x 411 [684,992,60,659,769,267,313,351]
+ CRUSH rule 1 x 412 [261,958,699,950,165,14,560,155]
+ CRUSH rule 1 x 413 [891,835,297,441,384,979,618,907]
+ CRUSH rule 1 x 414 [127,459,119,965,662,594,97,124]
+ CRUSH rule 1 x 415 [272,540,631,328,609,568,694,332]
+ CRUSH rule 1 x 416 [739,617,115,530,339,371,889,344]
+ CRUSH rule 1 x 417 [106,209,157,878,117,128,138,374]
+ CRUSH rule 1 x 418 [525,441,147,390,320,300,848,972]
+ CRUSH rule 1 x 419 [603,673,615,465,266,855,823,884]
+ CRUSH rule 1 x 420 [988,213,251,226,209,245,506,670]
+ CRUSH rule 1 x 421 [761,521,748,368,923,992,764,274]
+ CRUSH rule 1 x 422 [317,160,924,548,198,709,839,547]
+ CRUSH rule 1 x 423 [137,807,168,472,619,443,905,588]
+ CRUSH rule 1 x 424 [920,37,146,263,598,748,785,395]
+ CRUSH rule 1 x 425 [277,693,285,221,478,165,80,236]
+ CRUSH rule 1 x 426 [485,936,407,854,726,524,791,565]
+ CRUSH rule 1 x 427 [242,515,9,564,174,453,334,588]
+ CRUSH rule 1 x 428 [632,635,26,473,494,478,225,94]
+ CRUSH rule 1 x 429 [641,73,465,127,171,397,857,562]
+ CRUSH rule 1 x 430 [626,585,6,387,881,583,859,699]
+ CRUSH rule 1 x 431 [697,76,753,570,964,339,194,366]
+ CRUSH rule 1 x 432 [590,526,306,283,656,728,513,591]
+ CRUSH rule 1 x 433 [284,387,149,817,886,714,52,897]
+ CRUSH rule 1 x 434 [538,985,79,953,770,468,644,646]
+ CRUSH rule 1 x 435 [30,318,593,635,975,833,371,731]
+ CRUSH rule 1 x 436 [164,919,851,693,0,874,10,976]
+ CRUSH rule 1 x 437 [322,212,163,606,302,282,443,23]
+ CRUSH rule 1 x 438 [142,392,85,594,376,419,755,841]
+ CRUSH rule 1 x 439 [119,370,68,443,997,837,414,152]
+ CRUSH rule 1 x 440 [333,403,187,863,475,844,800,174]
+ CRUSH rule 1 x 441 [477,727,906,145,429,91,205,236]
+ CRUSH rule 1 x 442 [274,590,933,244,434,49,864,799]
+ CRUSH rule 1 x 443 [983,748,574,718,700,442,774,350]
+ CRUSH rule 1 x 444 [536,509,431,146,170,149,182,145]
+ CRUSH rule 1 x 445 [485,892,528,209,964,753,554,931]
+ CRUSH rule 1 x 446 [345,634,42,294,711,376,314,714]
+ CRUSH rule 1 x 447 [61,845,767,600,321,716,58,531]
+ CRUSH rule 1 x 448 [333,232,292,846,364,951,807,688]
+ CRUSH rule 1 x 449 [680,16,484,670,851,500,258,548]
+ CRUSH rule 1 x 450 [235,214,79,423,96,822,721,31]
+ CRUSH rule 1 x 451 [961,468,333,640,823,151,878,33]
+ CRUSH rule 1 x 452 [525,479,153,528,570,806,604,49]
+ CRUSH rule 1 x 453 [138,466,302,86,249,154,514,5]
+ CRUSH rule 1 x 454 [137,625,215,402,389,914,106,103]
+ CRUSH rule 1 x 455 [173,150,997,16,846,888,295,967]
+ CRUSH rule 1 x 456 [235,226,238,258,347,784,504,96]
+ CRUSH rule 1 x 457 [450,577,253,413,717,609,762,975]
+ CRUSH rule 1 x 458 [195,537,91,814,351,90,399,558]
+ CRUSH rule 1 x 459 [381,555,312,573,915,623,147,483]
+ CRUSH rule 1 x 460 [972,730,534,678,756,692,841,512]
+ CRUSH rule 1 x 461 [506,279,142,830,784,124,385,797]
+ CRUSH rule 1 x 462 [692,959,578,57,983,299,240,911]
+ CRUSH rule 1 x 463 [788,667,949,550,685,702,538,111]
+ CRUSH rule 1 x 464 [133,122,588,999,270,880,789,0]
+ CRUSH rule 1 x 465 [971,190,230,777,452,914,137,466]
+ CRUSH rule 1 x 466 [394,576,148,157,103,822,659,35]
+ CRUSH rule 1 x 467 [517,28,366,362,984,521,187,640]
+ CRUSH rule 1 x 468 [829,143,874,225,162,413,201,249]
+ CRUSH rule 1 x 469 [987,936,106,725,633,238,681,159]
+ CRUSH rule 1 x 470 [107,982,56,889,67,65,558,71]
+ CRUSH rule 1 x 471 [181,897,629,860,307,116,256,978]
+ CRUSH rule 1 x 472 [547,512,172,24,705,837,809,56]
+ CRUSH rule 1 x 473 [760,997,824,905,888,755,756,663]
+ CRUSH rule 1 x 474 [787,418,743,628,272,341,446,333]
+ CRUSH rule 1 x 475 [662,312,253,617,105,58,237,764]
+ CRUSH rule 1 x 476 [110,495,185,508,961,837,984,226]
+ CRUSH rule 1 x 477 [393,954,834,132,841,367,753,794]
+ CRUSH rule 1 x 478 [246,483,480,644,985,420,941,843]
+ CRUSH rule 1 x 479 [70,929,697,931,744,487,158,489]
+ CRUSH rule 1 x 480 [753,119,961,607,317,717,371,807]
+ CRUSH rule 1 x 481 [470,429,677,242,574,757,135,375]
+ CRUSH rule 1 x 482 [451,566,961,675,354,746,731,233]
+ CRUSH rule 1 x 483 [816,72,371,278,635,30,448,437]
+ CRUSH rule 1 x 484 [540,454,389,31,654,494,283,170]
+ CRUSH rule 1 x 485 [74,582,624,684,566,677,866,661]
+ CRUSH rule 1 x 486 [958,595,199,763,715,973,621,955]
+ CRUSH rule 1 x 487 [228,302,804,833,876,647,857,782]
+ CRUSH rule 1 x 488 [180,529,722,956,353,890,924,965]
+ CRUSH rule 1 x 489 [47,617,812,187,291,828,154,478]
+ CRUSH rule 1 x 490 [905,822,479,124,750,843,566,779]
+ CRUSH rule 1 x 491 [892,370,609,998,433,957,188,563]
+ CRUSH rule 1 x 492 [588,959,127,948,505,936,591,423]
+ CRUSH rule 1 x 493 [353,461,593,291,301,830,231,580]
+ CRUSH rule 1 x 494 [378,848,443,368,507,423,389,819]
+ CRUSH rule 1 x 495 [845,653,768,234,405,367,823,789]
+ CRUSH rule 1 x 496 [13,988,0,691,389,757,129,763]
+ CRUSH rule 1 x 497 [796,877,788,394,648,829,542,745]
+ CRUSH rule 1 x 498 [412,337,270,705,511,227,949,173]
+ CRUSH rule 1 x 499 [330,695,8,74,618,101,440,509]
+ CRUSH rule 1 x 500 [820,272,547,765,755,96,930,573]
+ CRUSH rule 1 x 501 [110,44,132,442,294,423,880,279]
+ CRUSH rule 1 x 502 [336,595,650,274,993,312,490,852]
+ CRUSH rule 1 x 503 [922,211,157,722,502,971,262,926]
+ CRUSH rule 1 x 504 [483,52,122,432,778,461,758,104]
+ CRUSH rule 1 x 505 [482,598,224,279,480,310,764,558]
+ CRUSH rule 1 x 506 [493,123,43,856,936,622,898,161]
+ CRUSH rule 1 x 507 [12,598,264,422,416,947,591,702]
+ CRUSH rule 1 x 508 [227,157,611,301,223,746,313,282]
+ CRUSH rule 1 x 509 [807,242,363,122,582,530,798,808]
+ CRUSH rule 1 x 510 [134,437,227,75,313,351,786,152]
+ CRUSH rule 1 x 511 [212,54,83,799,457,218,600,968]
+ CRUSH rule 1 x 512 [236,630,758,752,361,249,899,451]
+ CRUSH rule 1 x 513 [994,693,644,938,846,685,52,185]
+ CRUSH rule 1 x 514 [45,508,831,19,817,52,374,985]
+ CRUSH rule 1 x 515 [504,138,480,272,530,377,481,820]
+ CRUSH rule 1 x 516 [285,409,136,570,841,610,453,660]
+ CRUSH rule 1 x 517 [300,232,23,906,438,236,519,737]
+ CRUSH rule 1 x 518 [397,674,98,898,967,113,625,434]
+ CRUSH rule 1 x 519 [86,750,772,913,101,864,375,328]
+ CRUSH rule 1 x 520 [900,833,614,130,261,885,558,956]
+ CRUSH rule 1 x 521 [31,47,236,751,911,599,495,354]
+ CRUSH rule 1 x 522 [390,16,280,144,291,175,753,624]
+ CRUSH rule 1 x 523 [618,308,424,590,300,206,834,212]
+ CRUSH rule 1 x 524 [635,189,687,963,601,518,8,550]
+ CRUSH rule 1 x 525 [311,916,699,262,775,32,45,478]
+ CRUSH rule 1 x 526 [48,738,227,718,244,942,853,643]
+ CRUSH rule 1 x 527 [202,851,889,216,763,351,270,35]
+ CRUSH rule 1 x 528 [565,827,590,273,918,106,651,368]
+ CRUSH rule 1 x 529 [934,864,241,43,466,924,278,926]
+ CRUSH rule 1 x 530 [502,934,298,670,986,360,577,509]
+ CRUSH rule 1 x 531 [681,627,942,487,288,561,925,474]
+ CRUSH rule 1 x 532 [422,6,147,205,861,141,949,374]
+ CRUSH rule 1 x 533 [863,68,364,983,247,199,54,931]
+ CRUSH rule 1 x 534 [962,931,775,172,663,119,206,682]
+ CRUSH rule 1 x 535 [89,565,397,693,839,632,859,30]
+ CRUSH rule 1 x 536 [499,351,760,458,918,86,148,668]
+ CRUSH rule 1 x 537 [676,547,787,311,867,748,152,797]
+ CRUSH rule 1 x 538 [58,644,571,649,941,7,37,485]
+ CRUSH rule 1 x 539 [837,953,457,711,458,621,528,722]
+ CRUSH rule 1 x 540 [831,50,132,213,197,709,95,789]
+ CRUSH rule 1 x 541 [582,757,121,525,532,963,738,277]
+ CRUSH rule 1 x 542 [472,132,790,997,948,269,137,934]
+ CRUSH rule 1 x 543 [382,272,797,330,315,748,324,134]
+ CRUSH rule 1 x 544 [947,930,496,883,509,219,250,362]
+ CRUSH rule 1 x 545 [425,570,305,77,821,422,117,172]
+ CRUSH rule 1 x 546 [18,65,529,437,343,547,699,610]
+ CRUSH rule 1 x 547 [445,715,600,472,213,851,428,267]
+ CRUSH rule 1 x 548 [367,569,980,167,627,442,517,684]
+ CRUSH rule 1 x 549 [125,715,671,817,285,420,37,639]
+ CRUSH rule 1 x 550 [425,599,744,199,923,222,915,570]
+ CRUSH rule 1 x 551 [44,1,528,922,944,115,161,901]
+ CRUSH rule 1 x 552 [246,104,68,239,123,427,57,217]
+ CRUSH rule 1 x 553 [71,703,615,28,593,724,218,916]
+ CRUSH rule 1 x 554 [207,124,217,166,525,226,693,953]
+ CRUSH rule 1 x 555 [570,28,317,420,931,413,623,659]
+ CRUSH rule 1 x 556 [674,152,421,79,215,347,830,762]
+ CRUSH rule 1 x 557 [347,817,191,391,741,571,593,267]
+ CRUSH rule 1 x 558 [627,426,369,692,815,371,124,107]
+ CRUSH rule 1 x 559 [940,630,924,242,224,912,185,356]
+ CRUSH rule 1 x 560 [295,903,541,29,245,753,887,376]
+ CRUSH rule 1 x 561 [506,682,384,637,878,991,700,339]
+ CRUSH rule 1 x 562 [718,529,87,729,842,341,62,817]
+ CRUSH rule 1 x 563 [552,332,747,206,274,871,903,900]
+ CRUSH rule 1 x 564 [835,769,736,486,630,209,641,751]
+ CRUSH rule 1 x 565 [8,167,539,182,607,62,738,873]
+ CRUSH rule 1 x 566 [600,481,301,263,90,450,184,127]
+ CRUSH rule 1 x 567 [999,994,509,899,947,24,267,639]
+ CRUSH rule 1 x 568 [252,431,157,62,601,863,398,521]
+ CRUSH rule 1 x 569 [643,218,943,455,83,969,494,624]
+ CRUSH rule 1 x 570 [617,635,765,422,250,156,533,674]
+ CRUSH rule 1 x 571 [757,80,59,98,328,700,329,848]
+ CRUSH rule 1 x 572 [299,348,575,889,943,675,33,312]
+ CRUSH rule 1 x 573 [25,505,270,167,58,901,878,978]
+ CRUSH rule 1 x 574 [215,431,624,177,628,814,333,841]
+ CRUSH rule 1 x 575 [225,252,611,546,32,815,389,486]
+ CRUSH rule 1 x 576 [627,94,159,857,430,691,177,545]
+ CRUSH rule 1 x 577 [237,809,778,636,61,167,700,521]
+ CRUSH rule 1 x 578 [885,313,120,344,771,614,487,976]
+ CRUSH rule 1 x 579 [924,575,787,831,47,996,557,630]
+ CRUSH rule 1 x 580 [718,51,766,121,118,471,608,755]
+ CRUSH rule 1 x 581 [219,807,129,571,856,179,874,902]
+ CRUSH rule 1 x 582 [893,701,598,863,285,829,984,622]
+ CRUSH rule 1 x 583 [246,930,964,170,993,409,469,193]
+ CRUSH rule 1 x 584 [336,432,680,175,495,839,642,226]
+ CRUSH rule 1 x 585 [324,999,397,485,457,527,73,628]
+ CRUSH rule 1 x 586 [558,230,976,541,816,72,794,682]
+ CRUSH rule 1 x 587 [985,830,597,21,308,890,952,421]
+ CRUSH rule 1 x 588 [211,544,57,134,162,496,195,581]
+ CRUSH rule 1 x 589 [129,21,112,190,885,844,753,180]
+ CRUSH rule 1 x 590 [467,969,652,593,287,76,811,413]
+ CRUSH rule 1 x 591 [758,514,316,164,35,110,54,796]
+ CRUSH rule 1 x 592 [525,253,190,443,315,603,667,318]
+ CRUSH rule 1 x 593 [601,885,339,152,297,223,269,455]
+ CRUSH rule 1 x 594 [227,60,450,30,717,840,994,16]
+ CRUSH rule 1 x 595 [720,854,496,912,80,655,917,525]
+ CRUSH rule 1 x 596 [751,195,997,77,261,490,180,482]
+ CRUSH rule 1 x 597 [129,574,714,8,789,847,725,991]
+ CRUSH rule 1 x 598 [679,207,604,396,841,284,286,280]
+ CRUSH rule 1 x 599 [668,315,683,349,681,253,599,364]
+ CRUSH rule 1 x 600 [143,396,464,444,59,57,243,264]
+ CRUSH rule 1 x 601 [326,573,873,902,136,921,633,596]
+ CRUSH rule 1 x 602 [860,281,875,535,672,474,697,763]
+ CRUSH rule 1 x 603 [709,328,445,349,190,455,924,667]
+ CRUSH rule 1 x 604 [571,62,814,95,866,978,983,281]
+ CRUSH rule 1 x 605 [252,739,860,27,313,362,857,899]
+ CRUSH rule 1 x 606 [339,236,759,842,67,644,954,94]
+ CRUSH rule 1 x 607 [590,248,759,868,433,398,578,386]
+ CRUSH rule 1 x 608 [145,635,309,467,875,115,148,33]
+ CRUSH rule 1 x 609 [973,547,223,79,762,863,249,41]
+ CRUSH rule 1 x 610 [435,816,961,983,255,886,160,888]
+ CRUSH rule 1 x 611 [559,283,422,584,176,429,570,43]
+ CRUSH rule 1 x 612 [273,149,123,576,911,270,296,735]
+ CRUSH rule 1 x 613 [828,614,642,674,33,361,958,580]
+ CRUSH rule 1 x 614 [478,748,393,34,171,80,92,12]
+ CRUSH rule 1 x 615 [392,155,144,326,626,134,149,401]
+ CRUSH rule 1 x 616 [778,637,452,248,15,888,74,307]
+ CRUSH rule 1 x 617 [622,713,996,833,611,407,364,8]
+ CRUSH rule 1 x 618 [149,877,270,329,180,327,222,749]
+ CRUSH rule 1 x 619 [604,163,656,409,322,848,519,967]
+ CRUSH rule 1 x 620 [181,23,409,198,64,898,35,620]
+ CRUSH rule 1 x 621 [735,902,386,237,939,475,725,118]
+ CRUSH rule 1 x 622 [661,824,717,568,858,583,446,798]
+ CRUSH rule 1 x 623 [142,121,643,61,695,852,485,478]
+ CRUSH rule 1 x 624 [360,716,420,398,49,717,137,140]
+ CRUSH rule 1 x 625 [541,167,385,1,601,481,308,111]
+ CRUSH rule 1 x 626 [364,431,610,363,535,747,225,841]
+ CRUSH rule 1 x 627 [458,137,557,410,287,749,467,432]
+ CRUSH rule 1 x 628 [250,350,556,497,821,65,205,580]
+ CRUSH rule 1 x 629 [928,160,710,572,365,772,538,46]
+ CRUSH rule 1 x 630 [243,19,918,556,601,16,920,830]
+ CRUSH rule 1 x 631 [438,221,574,676,797,580,219,211]
+ CRUSH rule 1 x 632 [797,368,247,5,32,102,416,45]
+ CRUSH rule 1 x 633 [993,749,525,485,27,330,275,599]
+ CRUSH rule 1 x 634 [239,351,633,299,651,678,296,337]
+ CRUSH rule 1 x 635 [640,965,25,961,306,172,849,357]
+ CRUSH rule 1 x 636 [173,290,297,991,937,823,236,318]
+ CRUSH rule 1 x 637 [0,918,98,108,111,495,887,57]
+ CRUSH rule 1 x 638 [702,235,424,900,983,754,701,887]
+ CRUSH rule 1 x 639 [475,687,31,785,918,611,27,214]
+ CRUSH rule 1 x 640 [31,664,399,677,123,609,858,138]
+ CRUSH rule 1 x 641 [296,473,108,963,341,876,897,449]
+ CRUSH rule 1 x 642 [894,273,427,606,677,670,610,665]
+ CRUSH rule 1 x 643 [117,111,732,191,114,153,500,631]
+ CRUSH rule 1 x 644 [438,336,327,512,599,862,660,857]
+ CRUSH rule 1 x 645 [982,702,351,573,907,915,279,317]
+ CRUSH rule 1 x 646 [334,804,146,842,697,638,720,135]
+ CRUSH rule 1 x 647 [933,787,185,334,752,285,372,890]
+ CRUSH rule 1 x 648 [22,444,400,862,207,842,453,732]
+ CRUSH rule 1 x 649 [503,229,213,460,639,760,722,748]
+ CRUSH rule 1 x 650 [328,659,420,443,739,950,869,150]
+ CRUSH rule 1 x 651 [3,880,823,123,378,585,715,221]
+ CRUSH rule 1 x 652 [495,977,563,733,92,997,119,818]
+ CRUSH rule 1 x 653 [185,718,804,280,975,912,198,291]
+ CRUSH rule 1 x 654 [130,528,380,81,906,511,750,506]
+ CRUSH rule 1 x 655 [560,872,454,504,319,284,605,214]
+ CRUSH rule 1 x 656 [219,885,178,981,863,508,708,6]
+ CRUSH rule 1 x 657 [233,684,813,490,208,941,858,16]
+ CRUSH rule 1 x 658 [778,6,756,380,750,836,547,850]
+ CRUSH rule 1 x 659 [240,663,306,540,789,902,170,954]
+ CRUSH rule 1 x 660 [244,855,196,147,678,323,63,859]
+ CRUSH rule 1 x 661 [184,270,128,398,910,230,402,205]
+ CRUSH rule 1 x 662 [65,883,921,438,79,957,464,902]
+ CRUSH rule 1 x 663 [323,721,594,812,43,992,170,65]
+ CRUSH rule 1 x 664 [865,113,512,51,427,123,585,260]
+ CRUSH rule 1 x 665 [420,850,591,475,202,733,798,658]
+ CRUSH rule 1 x 666 [319,767,246,3,369,493,796,56]
+ CRUSH rule 1 x 667 [875,39,343,100,829,2,795,783]
+ CRUSH rule 1 x 668 [331,122,263,599,355,484,943,554]
+ CRUSH rule 1 x 669 [915,521,402,747,673,445,938,600]
+ CRUSH rule 1 x 670 [845,659,943,447,401,322,168,302]
+ CRUSH rule 1 x 671 [108,634,527,363,856,238,755,330]
+ CRUSH rule 1 x 672 [578,216,110,589,302,137,954,315]
+ CRUSH rule 1 x 673 [442,74,579,797,622,950,371,402]
+ CRUSH rule 1 x 674 [588,364,281,308,645,631,229,506]
+ CRUSH rule 1 x 675 [489,698,744,671,870,174,528,875]
+ CRUSH rule 1 x 676 [928,911,40,180,722,729,673,569]
+ CRUSH rule 1 x 677 [399,269,692,131,615,136,103,763]
+ CRUSH rule 1 x 678 [546,752,544,155,5,463,666,352]
+ CRUSH rule 1 x 679 [988,25,275,433,628,57,247,620]
+ CRUSH rule 1 x 680 [335,963,382,486,749,257,795,347]
+ CRUSH rule 1 x 681 [690,462,623,466,49,471,774,192]
+ CRUSH rule 1 x 682 [196,588,154,257,807,776,367,718]
+ CRUSH rule 1 x 683 [627,25,421,160,873,102,345,599]
+ CRUSH rule 1 x 684 [38,804,592,158,991,264,652,821]
+ CRUSH rule 1 x 685 [841,368,548,362,166,211,154,121]
+ CRUSH rule 1 x 686 [336,287,525,440,166,993,911,638]
+ CRUSH rule 1 x 687 [20,682,924,653,356,16,917,622]
+ CRUSH rule 1 x 688 [463,371,780,556,385,883,115,248]
+ CRUSH rule 1 x 689 [569,250,78,816,847,775,333,161]
+ CRUSH rule 1 x 690 [551,144,587,263,378,394,970,639]
+ CRUSH rule 1 x 691 [766,464,446,533,449,541,451,290]
+ CRUSH rule 1 x 692 [739,634,18,245,624,35,268,525]
+ CRUSH rule 1 x 693 [339,297,118,330,817,91,828,276]
+ CRUSH rule 1 x 694 [405,26,830,181,533,166,488,804]
+ CRUSH rule 1 x 695 [622,576,597,535,600,593,300,989]
+ CRUSH rule 1 x 696 [558,902,689,13,715,28,664,489]
+ CRUSH rule 1 x 697 [818,222,406,691,427,863,153,922]
+ CRUSH rule 1 x 698 [178,48,402,233,841,604,468,180]
+ CRUSH rule 1 x 699 [450,244,180,919,426,332,747,453]
+ CRUSH rule 1 x 700 [502,771,987,706,416,240,68,641]
+ CRUSH rule 1 x 701 [4,612,782,216,853,303,585,513]
+ CRUSH rule 1 x 702 [177,630,232,923,281,708,466,687]
+ CRUSH rule 1 x 703 [354,178,389,393,778,803,796,607]
+ CRUSH rule 1 x 704 [646,601,156,171,603,116,655,595]
+ CRUSH rule 1 x 705 [921,401,890,265,244,690,372,253]
+ CRUSH rule 1 x 706 [652,877,562,452,26,323,923,770]
+ CRUSH rule 1 x 707 [345,745,67,716,789,576,2,133]
+ CRUSH rule 1 x 708 [333,607,180,469,170,555,939,331]
+ CRUSH rule 1 x 709 [45,187,302,115,896,579,733,607]
+ CRUSH rule 1 x 710 [94,855,43,199,18,948,449,28]
+ CRUSH rule 1 x 711 [227,653,731,150,278,842,534,110]
+ CRUSH rule 1 x 712 [398,953,136,870,181,408,895,459]
+ CRUSH rule 1 x 713 [116,800,503,662,635,579,53,839]
+ CRUSH rule 1 x 714 [111,629,866,709,902,557,875,649]
+ CRUSH rule 1 x 715 [531,291,486,382,192,807,322,417]
+ CRUSH rule 1 x 716 [169,541,291,42,343,724,138,197]
+ CRUSH rule 1 x 717 [417,446,994,894,239,494,237,62]
+ CRUSH rule 1 x 718 [992,383,298,844,377,463,544,891]
+ CRUSH rule 1 x 719 [936,674,324,759,194,409,828,975]
+ CRUSH rule 1 x 720 [370,188,174,464,644,218,214,76]
+ CRUSH rule 1 x 721 [320,859,278,259,170,957,177,264]
+ CRUSH rule 1 x 722 [7,2,673,129,96,445,823,833]
+ CRUSH rule 1 x 723 [270,553,831,662,38,101,985,846]
+ CRUSH rule 1 x 724 [666,822,708,895,633,800,616,879]
+ CRUSH rule 1 x 725 [794,406,875,459,981,751,359,926]
+ CRUSH rule 1 x 726 [420,556,341,292,240,68,966,535]
+ CRUSH rule 1 x 727 [561,461,129,635,965,610,105,31]
+ CRUSH rule 1 x 728 [951,330,196,756,589,849,753,760]
+ CRUSH rule 1 x 729 [656,644,436,591,27,119,572,933]
+ CRUSH rule 1 x 730 [3,558,629,184,50,765,760,800]
+ CRUSH rule 1 x 731 [852,89,75,735,713,113,528,890]
+ CRUSH rule 1 x 732 [983,840,869,976,697,307,368,271]
+ CRUSH rule 1 x 733 [285,396,388,122,387,364,880,343]
+ CRUSH rule 1 x 734 [125,510,402,640,676,501,535,627]
+ CRUSH rule 1 x 735 [417,773,686,504,459,912,690,59]
+ CRUSH rule 1 x 736 [749,396,632,550,779,109,845,278]
+ CRUSH rule 1 x 737 [644,991,946,135,448,903,482,564]
+ CRUSH rule 1 x 738 [449,683,290,220,245,525,429,397]
+ CRUSH rule 1 x 739 [341,220,641,454,740,661,146,17]
+ CRUSH rule 1 x 740 [874,524,674,650,472,282,214,494]
+ CRUSH rule 1 x 741 [189,472,712,798,715,757,863,571]
+ CRUSH rule 1 x 742 [912,581,114,377,730,21,687,81]
+ CRUSH rule 1 x 743 [654,914,425,441,763,39,451,631]
+ CRUSH rule 1 x 744 [725,295,579,377,162,447,843,699]
+ CRUSH rule 1 x 745 [787,858,850,506,612,735,926,314]
+ CRUSH rule 1 x 746 [757,848,704,30,47,940,450,651]
+ CRUSH rule 1 x 747 [700,81,867,681,801,64,879,857]
+ CRUSH rule 1 x 748 [557,436,238,664,293,865,304,999]
+ CRUSH rule 1 x 749 [772,622,337,42,156,302,383,506]
+ CRUSH rule 1 x 750 [946,97,376,677,316,670,169,171]
+ CRUSH rule 1 x 751 [996,618,343,911,83,22,388,17]
+ CRUSH rule 1 x 752 [746,887,695,868,610,950,88,315]
+ CRUSH rule 1 x 753 [741,14,463,479,172,192,481,702]
+ CRUSH rule 1 x 754 [648,349,333,355,65,63,336,724]
+ CRUSH rule 1 x 755 [157,460,466,187,959,674,192,279]
+ CRUSH rule 1 x 756 [416,97,197,497,227,3,850,191]
+ CRUSH rule 1 x 757 [599,839,776,410,256,823,121,690]
+ CRUSH rule 1 x 758 [994,218,620,256,361,749,165,686]
+ CRUSH rule 1 x 759 [959,682,514,745,100,519,15,347]
+ CRUSH rule 1 x 760 [518,943,215,83,706,137,345,69]
+ CRUSH rule 1 x 761 [285,849,420,324,987,338,373,361]
+ CRUSH rule 1 x 762 [591,313,41,335,110,696,664,350]
+ CRUSH rule 1 x 763 [908,411,200,740,292,295,387,775]
+ CRUSH rule 1 x 764 [787,234,894,485,883,711,70,202]
+ CRUSH rule 1 x 765 [327,921,882,393,444,792,402,123]
+ CRUSH rule 1 x 766 [84,161,878,704,416,144,357,310]
+ CRUSH rule 1 x 767 [370,895,702,701,890,2,251,951]
+ CRUSH rule 1 x 768 [826,760,879,864,460,474,645,975]
+ CRUSH rule 1 x 769 [67,768,663,735,814,66,213,527]
+ CRUSH rule 1 x 770 [593,909,482,259,5,550,961,324]
+ CRUSH rule 1 x 771 [309,935,121,578,937,685,933,571]
+ CRUSH rule 1 x 772 [12,125,797,301,348,419,891,959]
+ CRUSH rule 1 x 773 [253,466,820,549,591,193,783,951]
+ CRUSH rule 1 x 774 [164,390,705,109,881,505,890,425]
+ CRUSH rule 1 x 775 [703,47,43,973,643,406,885,976]
+ CRUSH rule 1 x 776 [728,231,80,916,2,850,396,76]
+ CRUSH rule 1 x 777 [981,621,568,729,869,952,563,860]
+ CRUSH rule 1 x 778 [411,456,544,597,789,784,65,954]
+ CRUSH rule 1 x 779 [346,121,519,921,587,48,772,645]
+ CRUSH rule 1 x 780 [476,39,288,381,303,29,17,336]
+ CRUSH rule 1 x 781 [10,130,585,844,729,705,714,954]
+ CRUSH rule 1 x 782 [462,246,581,902,623,877,812,516]
+ CRUSH rule 1 x 783 [580,373,153,775,668,661,626,961]
+ CRUSH rule 1 x 784 [413,113,978,990,994,56,481,198]
+ CRUSH rule 1 x 785 [341,856,332,354,59,581,632,151]
+ CRUSH rule 1 x 786 [411,140,313,393,215,618,490,481]
+ CRUSH rule 1 x 787 [605,522,211,813,636,224,600,528]
+ CRUSH rule 1 x 788 [226,545,35,142,726,851,194,216]
+ CRUSH rule 1 x 789 [545,320,414,702,731,277,237,916]
+ CRUSH rule 1 x 790 [414,748,816,327,130,115,788,164]
+ CRUSH rule 1 x 791 [660,906,406,697,916,322,124,711]
+ CRUSH rule 1 x 792 [287,392,514,204,75,789,406,858]
+ CRUSH rule 1 x 793 [631,133,850,713,720,487,376,812]
+ CRUSH rule 1 x 794 [931,517,543,210,963,898,811,459]
+ CRUSH rule 1 x 795 [551,962,477,948,425,434,268,94]
+ CRUSH rule 1 x 796 [814,4,95,27,368,300,646,451]
+ CRUSH rule 1 x 797 [64,201,299,734,605,864,596,196]
+ CRUSH rule 1 x 798 [422,530,114,431,565,716,473,250]
+ CRUSH rule 1 x 799 [824,32,679,562,266,549,859,994]
+ CRUSH rule 1 x 800 [862,623,489,637,861,196,941,643]
+ CRUSH rule 1 x 801 [145,550,329,324,734,160,219,662]
+ CRUSH rule 1 x 802 [570,19,847,308,387,518,846,53]
+ CRUSH rule 1 x 803 [151,812,662,358,880,349,834,881]
+ CRUSH rule 1 x 804 [467,93,264,863,176,842,663,949]
+ CRUSH rule 1 x 805 [621,223,938,809,591,686,121,157]
+ CRUSH rule 1 x 806 [898,957,805,430,499,584,640,607]
+ CRUSH rule 1 x 807 [354,531,422,159,921,431,802,136]
+ CRUSH rule 1 x 808 [7,96,76,897,446,2,166,929]
+ CRUSH rule 1 x 809 [70,734,719,56,687,21,23,145]
+ CRUSH rule 1 x 810 [701,18,972,327,771,649,620,648]
+ CRUSH rule 1 x 811 [248,547,103,728,901,264,948,202]
+ CRUSH rule 1 x 812 [230,576,821,566,993,762,675,28]
+ CRUSH rule 1 x 813 [805,114,683,629,396,462,285,450]
+ CRUSH rule 1 x 814 [54,619,973,741,497,894,401,266]
+ CRUSH rule 1 x 815 [679,412,613,132,969,411,314,670]
+ CRUSH rule 1 x 816 [919,448,826,414,36,289,44,822]
+ CRUSH rule 1 x 817 [765,830,436,521,332,458,260,172]
+ CRUSH rule 1 x 818 [415,566,644,687,692,414,769,826]
+ CRUSH rule 1 x 819 [721,319,865,750,546,859,523,770]
+ CRUSH rule 1 x 820 [218,301,333,190,686,179,535,787]
+ CRUSH rule 1 x 821 [185,795,680,953,329,750,621,815]
+ CRUSH rule 1 x 822 [356,261,54,522,900,103,883,112]
+ CRUSH rule 1 x 823 [220,281,549,456,64,306,282,641]
+ CRUSH rule 1 x 824 [292,809,887,74,776,788,559,886]
+ CRUSH rule 1 x 825 [949,778,101,311,110,480,161,998]
+ CRUSH rule 1 x 826 [767,818,833,927,356,954,910,63]
+ CRUSH rule 1 x 827 [631,83,406,635,657,713,212,916]
+ CRUSH rule 1 x 828 [288,986,445,26,414,607,937,595]
+ CRUSH rule 1 x 829 [990,667,915,694,974,453,669,330]
+ CRUSH rule 1 x 830 [152,571,778,505,685,209,448,55]
+ CRUSH rule 1 x 831 [814,563,630,97,582,107,142,157]
+ CRUSH rule 1 x 832 [235,641,616,110,979,844,656,135]
+ CRUSH rule 1 x 833 [657,565,922,140,825,457,764,766]
+ CRUSH rule 1 x 834 [907,231,644,13,617,130,83,483]
+ CRUSH rule 1 x 835 [784,262,771,264,612,238,537,937]
+ CRUSH rule 1 x 836 [951,158,366,710,43,427,351,961]
+ CRUSH rule 1 x 837 [556,498,334,633,895,627,903,29]
+ CRUSH rule 1 x 838 [329,274,964,547,119,342,983,998]
+ CRUSH rule 1 x 839 [568,209,939,364,658,747,47,859]
+ CRUSH rule 1 x 840 [45,579,842,70,655,862,815,109]
+ CRUSH rule 1 x 841 [652,702,24,605,152,93,226,46]
+ CRUSH rule 1 x 842 [629,984,314,895,408,897,575,1]
+ CRUSH rule 1 x 843 [799,690,688,648,151,812,486,199]
+ CRUSH rule 1 x 844 [694,600,534,700,569,11,899,382]
+ CRUSH rule 1 x 845 [332,30,179,93,951,324,611,512]
+ CRUSH rule 1 x 846 [452,251,712,719,404,739,606,237]
+ CRUSH rule 1 x 847 [399,681,847,739,13,555,363,893]
+ CRUSH rule 1 x 848 [303,138,440,346,547,216,700,249]
+ CRUSH rule 1 x 849 [666,346,708,873,64,694,847,463]
+ CRUSH rule 1 x 850 [644,511,345,844,545,337,358,35]
+ CRUSH rule 1 x 851 [527,546,737,425,100,331,95,337]
+ CRUSH rule 1 x 852 [31,809,94,618,156,853,469,511]
+ CRUSH rule 1 x 853 [483,330,869,184,46,942,774,679]
+ CRUSH rule 1 x 854 [697,953,968,143,502,955,441,302]
+ CRUSH rule 1 x 855 [837,996,239,621,32,191,686,702]
+ CRUSH rule 1 x 856 [712,40,547,430,195,857,224,810]
+ CRUSH rule 1 x 857 [77,984,576,551,568,96,12,763]
+ CRUSH rule 1 x 858 [412,384,841,465,572,576,688,61]
+ CRUSH rule 1 x 859 [173,760,26,300,87,567,463,903]
+ CRUSH rule 1 x 860 [776,429,328,917,658,783,699,907]
+ CRUSH rule 1 x 861 [705,405,477,50,73,714,901,487]
+ CRUSH rule 1 x 862 [809,44,788,938,964,177,490,409]
+ CRUSH rule 1 x 863 [349,496,963,178,675,853,172,980]
+ CRUSH rule 1 x 864 [717,858,101,239,992,244,43,15]
+ CRUSH rule 1 x 865 [857,603,586,262,550,289,850,40]
+ CRUSH rule 1 x 866 [394,304,71,96,642,155,255,481]
+ CRUSH rule 1 x 867 [640,773,663,974,261,296,988,730]
+ CRUSH rule 1 x 868 [613,950,712,663,101,460,643,547]
+ CRUSH rule 1 x 869 [973,889,524,22,671,477,718,431]
+ CRUSH rule 1 x 870 [505,35,386,498,348,503,54,992]
+ CRUSH rule 1 x 871 [239,264,262,773,781,734,387,515]
+ CRUSH rule 1 x 872 [21,767,456,748,783,797,180,800]
+ CRUSH rule 1 x 873 [954,666,980,264,435,233,199,358]
+ CRUSH rule 1 x 874 [54,510,947,1,500,119,93,915]
+ CRUSH rule 1 x 875 [809,418,452,462,88,673,634,435]
+ CRUSH rule 1 x 876 [483,457,61,248,523,277,322,141]
+ CRUSH rule 1 x 877 [542,531,952,939,710,179,181,460]
+ CRUSH rule 1 x 878 [217,674,857,644,678,809,329,591]
+ CRUSH rule 1 x 879 [999,475,134,250,319,357,145,750]
+ CRUSH rule 1 x 880 [678,573,935,385,570,651,319,630]
+ CRUSH rule 1 x 881 [394,835,789,802,587,155,570,109]
+ CRUSH rule 1 x 882 [467,382,353,56,979,674,974,483]
+ CRUSH rule 1 x 883 [802,744,237,337,50,96,202,148]
+ CRUSH rule 1 x 884 [653,660,638,700,31,558,389,381]
+ CRUSH rule 1 x 885 [898,704,307,445,879,872,174,972]
+ CRUSH rule 1 x 886 [434,357,938,641,737,8,56,582]
+ CRUSH rule 1 x 887 [297,226,711,428,370,318,472,947]
+ CRUSH rule 1 x 888 [863,324,443,213,902,25,806,53]
+ CRUSH rule 1 x 889 [105,102,308,163,947,548,399,382]
+ CRUSH rule 1 x 890 [550,248,606,704,615,708,996,561]
+ CRUSH rule 1 x 891 [575,928,880,891,826,763,706,701]
+ CRUSH rule 1 x 892 [259,862,133,271,292,162,53,333]
+ CRUSH rule 1 x 893 [902,880,543,542,37,942,672,320]
+ CRUSH rule 1 x 894 [180,169,916,43,945,713,648,685]
+ CRUSH rule 1 x 895 [725,849,182,129,177,272,599,829]
+ CRUSH rule 1 x 896 [951,34,874,537,969,123,210,529]
+ CRUSH rule 1 x 897 [810,352,73,939,943,895,12,481]
+ CRUSH rule 1 x 898 [979,433,719,411,787,359,342,37]
+ CRUSH rule 1 x 899 [685,668,534,932,399,156,124,653]
+ CRUSH rule 1 x 900 [530,978,41,894,941,681,380,419]
+ CRUSH rule 1 x 901 [740,107,336,175,574,706,157,292]
+ CRUSH rule 1 x 902 [800,743,693,310,67,111,178,624]
+ CRUSH rule 1 x 903 [230,267,842,266,550,769,66,738]
+ CRUSH rule 1 x 904 [346,949,460,973,696,91,957,801]
+ CRUSH rule 1 x 905 [530,397,619,958,576,973,685,6]
+ CRUSH rule 1 x 906 [80,426,138,672,73,776,30,169]
+ CRUSH rule 1 x 907 [365,968,475,297,296,724,664,331]
+ CRUSH rule 1 x 908 [204,832,742,809,862,745,484,391]
+ CRUSH rule 1 x 909 [883,989,146,959,366,59,686,965]
+ CRUSH rule 1 x 910 [549,593,249,853,792,769,824,552]
+ CRUSH rule 1 x 911 [325,847,352,214,851,732,789,255]
+ CRUSH rule 1 x 912 [874,888,582,796,557,601,226,889]
+ CRUSH rule 1 x 913 [331,463,342,574,989,362,925,746]
+ CRUSH rule 1 x 914 [836,468,601,732,607,275,70,280]
+ CRUSH rule 1 x 915 [245,228,100,661,799,13,126,79]
+ CRUSH rule 1 x 916 [77,967,364,435,27,474,255,133]
+ CRUSH rule 1 x 917 [239,60,866,221,772,967,725,707]
+ CRUSH rule 1 x 918 [988,115,922,80,201,544,583,923]
+ CRUSH rule 1 x 919 [783,139,696,1,848,169,888,980]
+ CRUSH rule 1 x 920 [623,408,685,953,974,696,532,124]
+ CRUSH rule 1 x 921 [105,799,144,90,399,373,633,290]
+ CRUSH rule 1 x 922 [887,505,652,348,514,806,952,474]
+ CRUSH rule 1 x 923 [223,318,552,458,743,871,964,384]
+ CRUSH rule 1 x 924 [25,778,366,333,163,801,584,31]
+ CRUSH rule 1 x 925 [912,601,297,682,770,173,969,168]
+ CRUSH rule 1 x 926 [968,133,175,144,814,155,709,158]
+ CRUSH rule 1 x 927 [277,724,214,988,690,342,465,775]
+ CRUSH rule 1 x 928 [554,203,658,789,298,299,847,752]
+ CRUSH rule 1 x 929 [761,802,367,528,758,522,744,171]
+ CRUSH rule 1 x 930 [814,61,788,736,660,491,832,654]
+ CRUSH rule 1 x 931 [29,193,61,41,343,664,487,839]
+ CRUSH rule 1 x 932 [446,198,862,534,168,35,530,462]
+ CRUSH rule 1 x 933 [352,742,216,321,525,44,568,61]
+ CRUSH rule 1 x 934 [730,2,332,631,613,249,533,116]
+ CRUSH rule 1 x 935 [731,23,736,79,361,992,772,49]
+ CRUSH rule 1 x 936 [322,975,20,904,827,603,138,802]
+ CRUSH rule 1 x 937 [822,221,841,161,723,137,630,308]
+ CRUSH rule 1 x 938 [557,850,66,630,499,404,286,395]
+ CRUSH rule 1 x 939 [150,11,971,371,124,785,408,49]
+ CRUSH rule 1 x 940 [638,398,169,616,333,751,25,883]
+ CRUSH rule 1 x 941 [730,342,929,577,451,838,964,28]
+ CRUSH rule 1 x 942 [62,292,166,814,587,172,724,16]
+ CRUSH rule 1 x 943 [165,314,519,548,41,726,759,851]
+ CRUSH rule 1 x 944 [199,625,766,176,194,297,678,915]
+ CRUSH rule 1 x 945 [946,999,699,303,38,81,952,885]
+ CRUSH rule 1 x 946 [595,93,852,142,503,647,933,267]
+ CRUSH rule 1 x 947 [800,582,356,93,716,117,922,868]
+ CRUSH rule 1 x 948 [132,551,139,920,87,46,81,220]
+ CRUSH rule 1 x 949 [792,920,466,380,97,568,799,961]
+ CRUSH rule 1 x 950 [111,345,176,543,879,954,355,220]
+ CRUSH rule 1 x 951 [414,619,648,655,364,971,829,408]
+ CRUSH rule 1 x 952 [775,469,500,356,287,4,16,746]
+ CRUSH rule 1 x 953 [349,1,5,251,168,680,141,619]
+ CRUSH rule 1 x 954 [570,940,410,249,929,394,129,696]
+ CRUSH rule 1 x 955 [729,774,823,800,7,127,536,766]
+ CRUSH rule 1 x 956 [519,141,575,625,738,475,169,751]
+ CRUSH rule 1 x 957 [242,709,611,97,760,309,393,281]
+ CRUSH rule 1 x 958 [84,217,227,253,246,604,346,377]
+ CRUSH rule 1 x 959 [270,413,918,789,703,608,543,519]
+ CRUSH rule 1 x 960 [458,192,307,279,920,139,855,49]
+ CRUSH rule 1 x 961 [981,388,777,546,359,660,455,708]
+ CRUSH rule 1 x 962 [623,834,277,134,729,246,856,477]
+ CRUSH rule 1 x 963 [291,167,714,468,109,373,485,701]
+ CRUSH rule 1 x 964 [28,156,788,127,598,215,361,255]
+ CRUSH rule 1 x 965 [675,557,290,517,840,510,59,229]
+ CRUSH rule 1 x 966 [836,306,946,283,642,606,929,773]
+ CRUSH rule 1 x 967 [966,386,735,837,392,116,19,674]
+ CRUSH rule 1 x 968 [864,756,690,121,328,122,433,520]
+ CRUSH rule 1 x 969 [729,625,480,769,512,882,518,956]
+ CRUSH rule 1 x 970 [800,362,646,582,309,102,576,411]
+ CRUSH rule 1 x 971 [737,381,153,684,298,166,344,520]
+ CRUSH rule 1 x 972 [952,245,720,884,334,311,754,540]
+ CRUSH rule 1 x 973 [356,455,579,857,832,596,549,524]
+ CRUSH rule 1 x 974 [545,758,586,596,46,790,116,993]
+ CRUSH rule 1 x 975 [336,191,202,146,720,897,330,308]
+ CRUSH rule 1 x 976 [446,208,757,620,252,846,397,58]
+ CRUSH rule 1 x 977 [202,896,196,956,763,126,783,828]
+ CRUSH rule 1 x 978 [612,324,996,225,418,583,514,169]
+ CRUSH rule 1 x 979 [843,457,675,650,958,657,677,173]
+ CRUSH rule 1 x 980 [60,914,881,626,850,759,398,943]
+ CRUSH rule 1 x 981 [702,749,937,153,724,514,536,212]
+ CRUSH rule 1 x 982 [298,928,738,167,99,668,395,198]
+ CRUSH rule 1 x 983 [723,572,395,358,900,37,927,597]
+ CRUSH rule 1 x 984 [723,864,804,935,846,993,950,840]
+ CRUSH rule 1 x 985 [945,459,868,211,524,954,911,208]
+ CRUSH rule 1 x 986 [772,664,535,169,297,996,864,555]
+ CRUSH rule 1 x 987 [88,324,312,843,661,580,76,894]
+ CRUSH rule 1 x 988 [522,927,131,996,351,685,865,47]
+ CRUSH rule 1 x 989 [578,332,208,605,975,207,155,380]
+ CRUSH rule 1 x 990 [638,228,414,311,738,698,340,526]
+ CRUSH rule 1 x 991 [530,221,451,422,879,916,754,928]
+ CRUSH rule 1 x 992 [925,705,275,81,234,310,117,546]
+ CRUSH rule 1 x 993 [991,301,43,469,830,242,382,428]
+ CRUSH rule 1 x 994 [276,51,868,683,843,815,557,378]
+ CRUSH rule 1 x 995 [288,836,753,790,758,120,158,265]
+ CRUSH rule 1 x 996 [887,983,252,686,470,345,459,764]
+ CRUSH rule 1 x 997 [110,924,386,79,705,697,210,698]
+ CRUSH rule 1 x 998 [435,830,485,853,926,730,786,762]
+ CRUSH rule 1 x 999 [876,738,357,913,723,51,15,585]
+ CRUSH rule 1 x 1000 [178,963,638,430,845,586,317,102]
+ CRUSH rule 1 x 1001 [99,519,66,759,583,944,739,922]
+ CRUSH rule 1 x 1002 [515,534,468,866,878,717,729,370]
+ CRUSH rule 1 x 1003 [104,611,937,698,94,67,614,783]
+ CRUSH rule 1 x 1004 [269,638,724,375,491,121,891,113]
+ CRUSH rule 1 x 1005 [369,223,309,409,822,39,597,969]
+ CRUSH rule 1 x 1006 [40,107,69,275,79,429,234,945]
+ CRUSH rule 1 x 1007 [978,111,416,758,454,640,5,444]
+ CRUSH rule 1 x 1008 [965,956,624,832,421,96,975,723]
+ CRUSH rule 1 x 1009 [598,476,356,695,919,566,234,383]
+ CRUSH rule 1 x 1010 [767,523,239,517,29,77,23,241]
+ CRUSH rule 1 x 1011 [289,871,207,576,347,698,48,570]
+ CRUSH rule 1 x 1012 [128,28,370,31,341,755,268,647]
+ CRUSH rule 1 x 1013 [979,765,660,812,666,187,808,351]
+ CRUSH rule 1 x 1014 [979,948,513,88,47,825,969,81]
+ CRUSH rule 1 x 1015 [277,790,396,672,542,647,145,11]
+ CRUSH rule 1 x 1016 [262,73,128,886,839,685,456,560]
+ CRUSH rule 1 x 1017 [150,269,61,499,832,591,637,731]
+ CRUSH rule 1 x 1018 [555,829,554,944,406,576,463,926]
+ CRUSH rule 1 x 1019 [513,356,265,446,65,288,768,245]
+ CRUSH rule 1 x 1020 [158,161,877,704,948,570,495,865]
+ CRUSH rule 1 x 1021 [915,998,957,285,546,202,676,322]
+ CRUSH rule 1 x 1022 [967,829,973,640,703,470,871,828]
+ CRUSH rule 1 x 1023 [488,257,614,859,325,419,50,560]
+ rule 1 (metadata) num_rep 8 result size == 8:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536,450,604,380,966,750,695]
+ CRUSH rule 1 x 1 [876,250,334,633,744,843,672,820,782]
+ CRUSH rule 1 x 2 [292,832,53,392,386,787,527,901,106]
+ CRUSH rule 1 x 3 [623,387,124,998,749,211,481,169,816]
+ CRUSH rule 1 x 4 [61,334,710,4,994,982,847,220,87]
+ CRUSH rule 1 x 5 [946,557,713,664,141,817,964,872,66]
+ CRUSH rule 1 x 6 [576,668,212,163,732,381,884,726,456]
+ CRUSH rule 1 x 7 [645,753,906,393,341,44,578,14,543]
+ CRUSH rule 1 x 8 [243,6,863,781,211,100,462,207,759]
+ CRUSH rule 1 x 9 [22,578,251,410,297,430,3,569,603]
+ CRUSH rule 1 x 10 [758,828,360,477,821,801,811,484,296]
+ CRUSH rule 1 x 11 [769,120,124,527,119,504,380,821,470]
+ CRUSH rule 1 x 12 [780,364,689,755,675,199,117,393,435]
+ CRUSH rule 1 x 13 [557,18,351,719,742,780,78,170,333]
+ CRUSH rule 1 x 14 [59,561,249,461,971,835,855,76,269]
+ CRUSH rule 1 x 15 [718,928,993,21,76,313,437,462,680]
+ CRUSH rule 1 x 16 [673,632,841,954,788,90,786,969,378]
+ CRUSH rule 1 x 17 [648,43,560,514,142,289,935,605,228]
+ CRUSH rule 1 x 18 [654,219,181,568,381,253,883,394,188]
+ CRUSH rule 1 x 19 [850,545,377,848,863,543,51,834,690]
+ CRUSH rule 1 x 20 [717,785,974,5,225,552,975,636,387]
+ CRUSH rule 1 x 21 [420,57,519,306,312,983,263,267,128]
+ CRUSH rule 1 x 22 [503,998,193,821,634,684,557,633,812]
+ CRUSH rule 1 x 23 [411,663,168,110,899,488,477,468,303]
+ CRUSH rule 1 x 24 [266,861,353,1,456,128,800,309,622]
+ CRUSH rule 1 x 25 [760,483,818,600,509,951,248,908,624]
+ CRUSH rule 1 x 26 [903,24,573,718,112,694,501,909,219]
+ CRUSH rule 1 x 27 [946,188,289,510,687,827,676,560,753]
+ CRUSH rule 1 x 28 [69,312,73,198,256,629,770,569,359]
+ CRUSH rule 1 x 29 [844,883,337,628,496,405,719,581,816]
+ CRUSH rule 1 x 30 [621,18,613,794,910,936,426,522,208]
+ CRUSH rule 1 x 31 [784,943,814,539,962,392,813,217,750]
+ CRUSH rule 1 x 32 [173,374,369,972,315,83,428,63,801]
+ CRUSH rule 1 x 33 [698,336,357,966,582,407,618,288,846]
+ CRUSH rule 1 x 34 [168,836,210,798,904,190,663,877,177]
+ CRUSH rule 1 x 35 [274,509,534,818,912,671,75,580,568]
+ CRUSH rule 1 x 36 [318,215,153,628,87,407,676,524,510]
+ CRUSH rule 1 x 37 [173,604,109,935,203,401,311,758,201]
+ CRUSH rule 1 x 38 [708,444,683,604,722,900,929,910,698]
+ CRUSH rule 1 x 39 [662,198,417,680,226,342,856,248,279]
+ CRUSH rule 1 x 40 [620,801,414,78,560,766,980,503,287]
+ CRUSH rule 1 x 41 [811,264,177,127,148,791,930,74,844]
+ CRUSH rule 1 x 42 [863,179,527,660,133,529,456,713,348]
+ CRUSH rule 1 x 43 [686,822,988,228,791,549,514,40,261]
+ CRUSH rule 1 x 44 [396,222,46,841,536,140,160,527,250]
+ CRUSH rule 1 x 45 [991,694,253,142,54,422,658,876,201]
+ CRUSH rule 1 x 46 [420,909,184,285,508,458,45,390,546]
+ CRUSH rule 1 x 47 [467,211,605,207,241,881,959,800,743]
+ CRUSH rule 1 x 48 [955,329,368,168,698,787,738,47,812]
+ CRUSH rule 1 x 49 [974,891,931,29,813,506,822,628,696]
+ CRUSH rule 1 x 50 [870,441,691,823,761,6,83,344,713]
+ CRUSH rule 1 x 51 [182,930,25,936,97,260,406,281,991]
+ CRUSH rule 1 x 52 [704,812,894,794,481,37,304,899,629]
+ CRUSH rule 1 x 53 [185,713,631,280,345,558,882,503,327]
+ CRUSH rule 1 x 54 [270,441,100,82,983,930,339,902,81]
+ CRUSH rule 1 x 55 [895,734,958,793,651,572,508,763,108]
+ CRUSH rule 1 x 56 [564,963,683,324,40,189,77,500,553]
+ CRUSH rule 1 x 57 [738,130,208,973,498,861,670,67,114]
+ CRUSH rule 1 x 58 [524,113,806,903,531,334,8,762,842]
+ CRUSH rule 1 x 59 [408,337,668,529,34,384,643,511,370]
+ CRUSH rule 1 x 60 [228,790,857,309,616,895,194,277,985]
+ CRUSH rule 1 x 61 [154,843,717,467,883,536,812,14,55]
+ CRUSH rule 1 x 62 [594,811,549,276,693,917,45,723,926]
+ CRUSH rule 1 x 63 [646,67,884,925,941,434,705,268,140]
+ CRUSH rule 1 x 64 [175,542,155,837,594,197,451,891,654]
+ CRUSH rule 1 x 65 [745,619,131,867,269,62,862,221,66]
+ CRUSH rule 1 x 66 [275,468,23,35,328,432,334,656,719]
+ CRUSH rule 1 x 67 [246,958,524,493,636,227,783,593,814]
+ CRUSH rule 1 x 68 [711,473,403,228,835,126,705,114,981]
+ CRUSH rule 1 x 69 [493,924,850,939,950,105,871,361,533]
+ CRUSH rule 1 x 70 [30,499,644,33,804,654,684,411,114]
+ CRUSH rule 1 x 71 [984,883,574,716,575,391,587,264,446]
+ CRUSH rule 1 x 72 [71,286,942,363,628,632,642,529,966]
+ CRUSH rule 1 x 73 [922,618,3,371,464,442,835,705,260]
+ CRUSH rule 1 x 74 [629,414,185,573,678,338,633,560,565]
+ CRUSH rule 1 x 75 [222,20,174,820,312,361,366,258,711]
+ CRUSH rule 1 x 76 [262,366,339,290,718,143,735,953,188]
+ CRUSH rule 1 x 77 [638,469,992,280,773,892,197,690,426]
+ CRUSH rule 1 x 78 [324,511,788,7,308,228,183,917,464]
+ CRUSH rule 1 x 79 [577,990,64,94,447,924,339,24,581]
+ CRUSH rule 1 x 80 [501,95,278,903,631,842,51,766,822]
+ CRUSH rule 1 x 81 [506,812,9,698,173,664,247,963,0]
+ CRUSH rule 1 x 82 [222,145,80,785,835,745,580,51,939]
+ CRUSH rule 1 x 83 [71,634,61,91,856,529,66,197,698]
+ CRUSH rule 1 x 84 [49,761,773,368,318,708,681,618,723]
+ CRUSH rule 1 x 85 [985,896,708,861,325,307,567,908,514]
+ CRUSH rule 1 x 86 [537,745,93,524,466,356,38,326,385]
+ CRUSH rule 1 x 87 [997,317,463,626,685,162,909,49,28]
+ CRUSH rule 1 x 88 [957,350,890,857,375,176,99,737,942]
+ CRUSH rule 1 x 89 [399,730,148,314,159,982,320,921,812]
+ CRUSH rule 1 x 90 [943,706,683,267,579,141,412,184,529]
+ CRUSH rule 1 x 91 [22,368,149,928,140,529,495,299,812]
+ CRUSH rule 1 x 92 [532,424,426,773,623,197,167,634,781]
+ CRUSH rule 1 x 93 [218,489,405,681,549,201,343,949,51]
+ CRUSH rule 1 x 94 [181,96,102,515,776,365,82,422,738]
+ CRUSH rule 1 x 95 [343,957,820,139,334,37,648,661,46]
+ CRUSH rule 1 x 96 [861,270,87,797,0,245,204,750,322]
+ CRUSH rule 1 x 97 [459,706,45,328,274,605,83,542,131]
+ CRUSH rule 1 x 98 [327,867,353,948,728,280,270,511,586]
+ CRUSH rule 1 x 99 [974,133,468,906,235,988,37,138,326]
+ CRUSH rule 1 x 100 [32,445,547,371,960,885,9,168,590]
+ CRUSH rule 1 x 101 [142,90,337,950,970,570,12,369,9]
+ CRUSH rule 1 x 102 [172,129,139,22,403,867,923,106,653]
+ CRUSH rule 1 x 103 [630,47,161,356,911,421,933,231,520]
+ CRUSH rule 1 x 104 [758,133,278,11,947,799,401,85,139]
+ CRUSH rule 1 x 105 [843,604,47,33,401,632,434,121,488]
+ CRUSH rule 1 x 106 [28,681,193,679,990,343,878,493,550]
+ CRUSH rule 1 x 107 [74,320,85,819,315,253,589,614,814]
+ CRUSH rule 1 x 108 [875,593,575,517,107,153,631,996,630]
+ CRUSH rule 1 x 109 [411,985,811,720,198,666,856,296,122]
+ CRUSH rule 1 x 110 [440,774,799,660,715,167,510,472,270]
+ CRUSH rule 1 x 111 [405,742,276,359,936,360,18,949,341]
+ CRUSH rule 1 x 112 [143,181,922,545,185,303,725,413,187]
+ CRUSH rule 1 x 113 [153,846,160,903,789,897,738,253,213]
+ CRUSH rule 1 x 114 [804,892,939,20,312,692,598,418,641]
+ CRUSH rule 1 x 115 [588,508,958,580,232,722,421,39,241]
+ CRUSH rule 1 x 116 [327,148,637,486,712,464,9,448,816]
+ CRUSH rule 1 x 117 [95,594,989,131,714,275,725,142,304]
+ CRUSH rule 1 x 118 [80,957,897,239,359,432,766,210,528]
+ CRUSH rule 1 x 119 [386,932,951,768,679,300,570,278,867]
+ CRUSH rule 1 x 120 [366,312,653,936,71,241,49,126,410]
+ CRUSH rule 1 x 121 [129,154,847,16,471,481,424,868,469]
+ CRUSH rule 1 x 122 [873,1,110,939,90,412,551,43,590]
+ CRUSH rule 1 x 123 [533,415,789,600,713,800,877,248,753]
+ CRUSH rule 1 x 124 [461,691,898,723,957,759,482,254,158]
+ CRUSH rule 1 x 125 [342,599,830,402,615,994,736,737,508]
+ CRUSH rule 1 x 126 [819,781,822,548,279,255,689,209,99]
+ CRUSH rule 1 x 127 [437,893,585,707,353,189,909,809,553]
+ CRUSH rule 1 x 128 [679,994,982,550,991,324,666,691,899]
+ CRUSH rule 1 x 129 [380,685,947,302,698,144,149,718,904]
+ CRUSH rule 1 x 130 [992,52,466,867,998,777,270,425,864]
+ CRUSH rule 1 x 131 [469,90,208,599,829,656,203,667,528]
+ CRUSH rule 1 x 132 [571,250,316,535,54,418,922,597,680]
+ CRUSH rule 1 x 133 [964,728,329,902,108,118,14,444,709]
+ CRUSH rule 1 x 134 [999,19,716,963,323,559,893,281,226]
+ CRUSH rule 1 x 135 [634,101,52,938,413,573,712,649,27]
+ CRUSH rule 1 x 136 [114,889,692,768,694,279,846,890,151]
+ CRUSH rule 1 x 137 [839,8,959,280,922,870,363,323,153]
+ CRUSH rule 1 x 138 [967,949,138,451,292,548,400,885,907]
+ CRUSH rule 1 x 139 [308,711,736,247,632,126,384,58,373]
+ CRUSH rule 1 x 140 [764,936,926,55,331,115,178,532,883]
+ CRUSH rule 1 x 141 [423,302,112,216,603,873,193,258,445]
+ CRUSH rule 1 x 142 [252,821,715,340,635,668,424,881,751]
+ CRUSH rule 1 x 143 [33,808,518,477,325,316,266,70,210]
+ CRUSH rule 1 x 144 [472,88,969,162,401,771,697,610,203]
+ CRUSH rule 1 x 145 [242,208,252,604,266,743,577,348,1]
+ CRUSH rule 1 x 146 [290,70,570,384,934,856,929,196,880]
+ CRUSH rule 1 x 147 [447,352,657,493,467,918,514,546,861]
+ CRUSH rule 1 x 148 [212,644,432,658,109,275,352,820,857]
+ CRUSH rule 1 x 149 [9,775,87,35,260,646,406,556,532]
+ CRUSH rule 1 x 150 [166,456,582,144,324,340,484,553,315]
+ CRUSH rule 1 x 151 [811,875,307,20,782,229,671,883,204]
+ CRUSH rule 1 x 152 [449,617,223,9,182,407,807,50,206]
+ CRUSH rule 1 x 153 [523,537,695,627,959,613,942,864,388]
+ CRUSH rule 1 x 154 [208,559,874,597,243,706,443,98,27]
+ CRUSH rule 1 x 155 [569,325,192,296,367,848,58,641,186]
+ CRUSH rule 1 x 156 [488,121,521,213,595,837,271,229,961]
+ CRUSH rule 1 x 157 [140,723,633,260,487,856,384,446,836]
+ CRUSH rule 1 x 158 [786,451,320,239,667,632,899,902,956]
+ CRUSH rule 1 x 159 [134,664,517,821,667,944,209,641,228]
+ CRUSH rule 1 x 160 [690,112,414,990,183,590,242,999,974]
+ CRUSH rule 1 x 161 [324,912,397,423,991,284,909,642,188]
+ CRUSH rule 1 x 162 [748,567,284,183,463,336,148,88,764]
+ CRUSH rule 1 x 163 [575,499,31,816,749,737,587,854,482]
+ CRUSH rule 1 x 164 [314,489,308,326,51,568,110,329,361]
+ CRUSH rule 1 x 165 [116,209,750,53,813,640,524,389,185]
+ CRUSH rule 1 x 166 [352,706,701,810,718,527,548,676,448]
+ CRUSH rule 1 x 167 [27,743,174,142,551,1,935,266,883]
+ CRUSH rule 1 x 168 [953,898,880,660,500,799,667,463,818]
+ CRUSH rule 1 x 169 [912,147,266,547,331,770,601,909,60]
+ CRUSH rule 1 x 170 [421,515,828,844,151,981,835,840,548]
+ CRUSH rule 1 x 171 [488,584,880,964,936,196,100,910,446]
+ CRUSH rule 1 x 172 [366,443,957,66,162,693,36,356,274]
+ CRUSH rule 1 x 173 [863,291,625,287,158,496,471,529,359]
+ CRUSH rule 1 x 174 [263,555,650,410,339,616,780,932,573]
+ CRUSH rule 1 x 175 [875,961,361,575,33,109,51,211,409]
+ CRUSH rule 1 x 176 [745,83,701,680,250,420,240,316,337]
+ CRUSH rule 1 x 177 [128,244,41,123,422,902,756,647,45]
+ CRUSH rule 1 x 178 [155,41,264,777,314,564,856,992,696]
+ CRUSH rule 1 x 179 [593,833,202,183,971,38,724,923,450]
+ CRUSH rule 1 x 180 [154,734,17,831,824,522,736,846,926]
+ CRUSH rule 1 x 181 [289,675,723,800,166,712,168,224,705]
+ CRUSH rule 1 x 182 [730,931,560,209,943,261,485,571,796]
+ CRUSH rule 1 x 183 [639,237,794,815,827,400,109,903,96]
+ CRUSH rule 1 x 184 [704,312,685,645,691,778,74,45,438]
+ CRUSH rule 1 x 185 [97,100,762,82,999,542,485,511,14]
+ CRUSH rule 1 x 186 [26,665,554,215,280,421,369,270,16]
+ CRUSH rule 1 x 187 [649,14,740,494,402,684,566,378,816]
+ CRUSH rule 1 x 188 [682,695,590,743,927,945,833,650,761]
+ CRUSH rule 1 x 189 [325,693,726,51,448,169,37,1,939]
+ CRUSH rule 1 x 190 [399,933,136,955,57,504,527,237,295]
+ CRUSH rule 1 x 191 [629,533,17,126,60,146,999,754,339]
+ CRUSH rule 1 x 192 [503,578,38,492,222,251,123,759,147]
+ CRUSH rule 1 x 193 [546,333,651,678,823,652,359,721,996]
+ CRUSH rule 1 x 194 [242,473,58,655,851,277,792,887,561]
+ CRUSH rule 1 x 195 [625,719,135,81,636,513,755,471,629]
+ CRUSH rule 1 x 196 [357,114,125,867,250,522,413,834,832]
+ CRUSH rule 1 x 197 [306,954,453,873,211,334,666,316,243]
+ CRUSH rule 1 x 198 [863,791,311,911,206,61,355,574,781]
+ CRUSH rule 1 x 199 [935,906,929,252,893,75,960,369,584]
+ CRUSH rule 1 x 200 [373,774,229,454,909,611,132,271,128]
+ CRUSH rule 1 x 201 [659,320,477,313,779,16,495,76,598]
+ CRUSH rule 1 x 202 [260,433,524,880,223,818,153,272,944]
+ CRUSH rule 1 x 203 [36,239,675,971,703,209,669,676,762]
+ CRUSH rule 1 x 204 [92,516,993,728,279,478,697,881,64]
+ CRUSH rule 1 x 205 [68,395,473,45,683,662,776,463,327]
+ CRUSH rule 1 x 206 [570,530,642,380,311,398,230,367,890]
+ CRUSH rule 1 x 207 [834,457,850,917,456,296,76,708,101]
+ CRUSH rule 1 x 208 [927,484,640,976,803,626,96,841,811]
+ CRUSH rule 1 x 209 [878,66,58,940,48,233,522,185,949]
+ CRUSH rule 1 x 210 [572,981,484,29,0,426,14,921,544]
+ CRUSH rule 1 x 211 [107,597,780,857,895,57,922,372,581]
+ CRUSH rule 1 x 212 [389,107,838,624,698,562,857,894,60]
+ CRUSH rule 1 x 213 [497,717,567,728,905,134,687,903,620]
+ CRUSH rule 1 x 214 [798,65,254,572,32,393,579,79,258]
+ CRUSH rule 1 x 215 [233,419,283,638,520,891,982,826,488]
+ CRUSH rule 1 x 216 [494,464,742,523,459,174,973,898,556]
+ CRUSH rule 1 x 217 [352,396,309,938,66,41,264,6,603]
+ CRUSH rule 1 x 218 [895,864,988,650,593,740,34,497,108]
+ CRUSH rule 1 x 219 [222,534,277,242,658,482,697,805,976]
+ CRUSH rule 1 x 220 [281,19,584,563,858,965,686,982,0]
+ CRUSH rule 1 x 221 [64,928,963,130,312,394,61,559,846]
+ CRUSH rule 1 x 222 [40,544,161,199,861,644,597,904,897]
+ CRUSH rule 1 x 223 [645,556,159,417,46,135,465,429,614]
+ CRUSH rule 1 x 224 [647,165,957,263,961,576,329,320,645]
+ CRUSH rule 1 x 225 [219,714,858,747,461,175,606,465,354]
+ CRUSH rule 1 x 226 [372,511,181,277,695,404,876,984,491]
+ CRUSH rule 1 x 227 [925,156,714,863,257,74,966,217,501]
+ CRUSH rule 1 x 228 [682,404,839,263,521,195,261,389,281]
+ CRUSH rule 1 x 229 [880,838,770,891,236,542,262,884,215]
+ CRUSH rule 1 x 230 [328,659,916,468,646,572,93,880,959]
+ CRUSH rule 1 x 231 [320,383,669,109,627,621,50,182,541]
+ CRUSH rule 1 x 232 [924,846,394,319,43,519,106,877,130]
+ CRUSH rule 1 x 233 [948,652,575,838,498,395,796,835,714]
+ CRUSH rule 1 x 234 [484,943,42,575,936,180,103,95,634]
+ CRUSH rule 1 x 235 [750,65,590,168,870,308,471,753,350]
+ CRUSH rule 1 x 236 [551,787,490,136,370,833,573,128,154]
+ CRUSH rule 1 x 237 [390,157,166,251,752,75,327,509,325]
+ CRUSH rule 1 x 238 [570,6,989,707,514,905,894,884,824]
+ CRUSH rule 1 x 239 [729,959,376,975,496,49,426,427,736]
+ CRUSH rule 1 x 240 [981,241,156,767,631,576,450,677,659]
+ CRUSH rule 1 x 241 [310,816,641,177,996,454,413,136,411]
+ CRUSH rule 1 x 242 [161,63,642,837,763,458,234,756,496]
+ CRUSH rule 1 x 243 [180,394,33,683,189,419,799,21,13]
+ CRUSH rule 1 x 244 [52,174,685,189,78,310,785,107,816]
+ CRUSH rule 1 x 245 [523,121,915,84,386,409,605,837,1]
+ CRUSH rule 1 x 246 [362,893,390,487,817,88,989,999,138]
+ CRUSH rule 1 x 247 [382,184,116,34,143,15,590,840,586]
+ CRUSH rule 1 x 248 [129,114,852,469,359,291,713,237,468]
+ CRUSH rule 1 x 249 [159,683,91,856,475,369,886,650,827]
+ CRUSH rule 1 x 250 [404,945,569,955,228,910,270,619,450]
+ CRUSH rule 1 x 251 [661,225,738,757,37,642,58,354,16]
+ CRUSH rule 1 x 252 [961,226,542,103,945,885,838,131,387]
+ CRUSH rule 1 x 253 [651,97,225,364,189,248,797,675,452]
+ CRUSH rule 1 x 254 [123,33,741,692,599,11,605,453,987]
+ CRUSH rule 1 x 255 [314,649,891,855,517,344,607,95,121]
+ CRUSH rule 1 x 256 [315,215,651,126,470,849,189,627,592]
+ CRUSH rule 1 x 257 [825,264,867,17,529,409,291,732,224]
+ CRUSH rule 1 x 258 [624,789,370,723,131,982,863,427,873]
+ CRUSH rule 1 x 259 [602,542,70,563,947,723,77,191,669]
+ CRUSH rule 1 x 260 [717,878,43,56,377,481,533,646,475]
+ CRUSH rule 1 x 261 [145,517,20,903,786,939,516,136,87]
+ CRUSH rule 1 x 262 [223,1,561,420,256,16,88,534,289]
+ CRUSH rule 1 x 263 [462,211,405,508,787,669,773,979,719]
+ CRUSH rule 1 x 264 [654,471,266,662,135,564,715,916,633]
+ CRUSH rule 1 x 265 [302,794,704,798,659,487,833,987,445]
+ CRUSH rule 1 x 266 [202,132,884,209,551,984,7,557,76]
+ CRUSH rule 1 x 267 [282,938,657,113,672,993,972,645,882]
+ CRUSH rule 1 x 268 [338,309,356,278,928,797,715,536,983]
+ CRUSH rule 1 x 269 [738,122,266,200,894,118,146,14,414]
+ CRUSH rule 1 x 270 [707,982,946,196,407,804,476,571,314]
+ CRUSH rule 1 x 271 [705,432,364,735,512,595,263,138,526]
+ CRUSH rule 1 x 272 [756,545,942,56,542,449,710,779,161]
+ CRUSH rule 1 x 273 [197,502,527,721,239,648,982,735,58]
+ CRUSH rule 1 x 274 [992,44,653,573,527,702,370,990,320]
+ CRUSH rule 1 x 275 [544,789,170,434,23,926,992,823,321]
+ CRUSH rule 1 x 276 [658,467,577,268,336,5,634,98,457]
+ CRUSH rule 1 x 277 [143,490,880,483,928,272,783,648,927]
+ CRUSH rule 1 x 278 [492,647,355,282,834,64,350,600,283]
+ CRUSH rule 1 x 279 [517,792,604,987,527,894,952,250,206]
+ CRUSH rule 1 x 280 [825,740,27,848,514,750,895,914,892]
+ CRUSH rule 1 x 281 [224,629,120,562,616,200,443,604,859]
+ CRUSH rule 1 x 282 [298,661,380,416,35,585,939,879,338]
+ CRUSH rule 1 x 283 [311,606,208,50,913,678,369,544,721]
+ CRUSH rule 1 x 284 [771,466,371,743,672,119,60,546,39]
+ CRUSH rule 1 x 285 [693,362,404,676,797,531,582,975,810]
+ CRUSH rule 1 x 286 [364,477,285,167,270,617,699,627,725]
+ CRUSH rule 1 x 287 [591,611,828,995,170,987,137,890,487]
+ CRUSH rule 1 x 288 [965,541,848,796,251,668,195,538,356]
+ CRUSH rule 1 x 289 [225,551,948,877,219,167,795,377,825]
+ CRUSH rule 1 x 290 [577,762,777,751,291,349,473,209,59]
+ CRUSH rule 1 x 291 [160,903,477,381,490,559,557,86,89]
+ CRUSH rule 1 x 292 [873,598,216,666,222,228,806,911,738]
+ CRUSH rule 1 x 293 [100,234,874,47,28,452,775,636,232]
+ CRUSH rule 1 x 294 [285,943,379,520,725,547,459,833,503]
+ CRUSH rule 1 x 295 [938,262,880,327,687,3,440,73,29]
+ CRUSH rule 1 x 296 [850,327,86,472,1,776,266,82,671]
+ CRUSH rule 1 x 297 [951,53,99,558,753,228,232,343,831]
+ CRUSH rule 1 x 298 [173,336,85,766,910,657,213,286,61]
+ CRUSH rule 1 x 299 [598,591,315,386,895,296,924,106,63]
+ CRUSH rule 1 x 300 [531,957,62,459,156,538,904,838,458]
+ CRUSH rule 1 x 301 [823,628,23,858,629,808,220,432,393]
+ CRUSH rule 1 x 302 [184,80,780,871,531,211,400,365,697]
+ CRUSH rule 1 x 303 [521,766,222,830,988,275,561,905,522]
+ CRUSH rule 1 x 304 [980,127,807,507,555,245,214,944,845]
+ CRUSH rule 1 x 305 [153,816,22,927,696,911,685,838,3]
+ CRUSH rule 1 x 306 [423,739,664,753,178,431,761,648,867]
+ CRUSH rule 1 x 307 [997,557,682,456,479,631,459,250,415]
+ CRUSH rule 1 x 308 [991,874,534,465,330,284,976,551,126]
+ CRUSH rule 1 x 309 [860,394,724,858,246,866,857,153,970]
+ CRUSH rule 1 x 310 [589,818,546,201,94,653,90,855,441]
+ CRUSH rule 1 x 311 [477,774,225,590,830,559,256,798,743]
+ CRUSH rule 1 x 312 [887,853,950,354,58,23,497,929,92]
+ CRUSH rule 1 x 313 [802,646,447,416,557,118,24,81,215]
+ CRUSH rule 1 x 314 [654,974,229,511,562,916,952,599,398]
+ CRUSH rule 1 x 315 [767,227,28,740,828,156,749,841,969]
+ CRUSH rule 1 x 316 [778,83,733,359,858,319,761,725,923]
+ CRUSH rule 1 x 317 [184,418,642,986,939,675,892,86,214]
+ CRUSH rule 1 x 318 [525,410,500,543,212,95,290,97,529]
+ CRUSH rule 1 x 319 [476,724,569,382,409,521,800,868,364]
+ CRUSH rule 1 x 320 [149,610,697,296,818,955,523,366,891]
+ CRUSH rule 1 x 321 [710,79,667,671,234,4,868,841,563]
+ CRUSH rule 1 x 322 [175,275,323,333,744,718,187,380,947]
+ CRUSH rule 1 x 323 [819,604,638,792,316,544,236,597,969]
+ CRUSH rule 1 x 324 [16,745,511,439,272,417,668,959,845]
+ CRUSH rule 1 x 325 [486,400,872,873,251,68,462,268,124]
+ CRUSH rule 1 x 326 [613,765,207,19,359,370,461,509,75]
+ CRUSH rule 1 x 327 [125,289,738,408,456,784,750,669,296]
+ CRUSH rule 1 x 328 [807,383,476,583,645,141,33,806,181]
+ CRUSH rule 1 x 329 [588,938,599,432,446,840,516,713,223]
+ CRUSH rule 1 x 330 [932,644,41,611,209,406,420,520,395]
+ CRUSH rule 1 x 331 [341,953,950,537,578,862,624,649,626]
+ CRUSH rule 1 x 332 [153,726,459,950,466,804,644,821,238]
+ CRUSH rule 1 x 333 [745,845,853,860,52,615,243,633,309]
+ CRUSH rule 1 x 334 [614,751,807,58,396,159,408,175,189]
+ CRUSH rule 1 x 335 [518,721,221,283,454,187,635,367,997]
+ CRUSH rule 1 x 336 [389,424,77,309,5,898,698,533,683]
+ CRUSH rule 1 x 337 [753,508,765,720,221,807,956,907,464]
+ CRUSH rule 1 x 338 [128,810,490,753,406,760,69,11,624]
+ CRUSH rule 1 x 339 [430,308,58,751,856,823,607,953,125]
+ CRUSH rule 1 x 340 [541,44,630,231,289,966,707,328,325]
+ CRUSH rule 1 x 341 [402,26,631,439,165,928,720,503,209]
+ CRUSH rule 1 x 342 [982,57,992,461,131,32,516,661,985]
+ CRUSH rule 1 x 343 [833,412,572,732,107,805,660,655,149]
+ CRUSH rule 1 x 344 [784,533,792,41,642,869,142,114,108]
+ CRUSH rule 1 x 345 [546,300,304,691,763,556,127,732,290]
+ CRUSH rule 1 x 346 [302,420,428,891,357,124,419,962,304]
+ CRUSH rule 1 x 347 [488,778,101,217,366,442,783,661,622]
+ CRUSH rule 1 x 348 [903,744,937,718,85,314,862,513,112]
+ CRUSH rule 1 x 349 [471,547,582,306,600,486,795,143,529]
+ CRUSH rule 1 x 350 [348,221,823,335,383,708,841,164,765]
+ CRUSH rule 1 x 351 [961,582,705,346,361,32,766,775,518]
+ CRUSH rule 1 x 352 [728,137,461,298,36,903,899,665,802]
+ CRUSH rule 1 x 353 [904,202,184,447,58,294,279,616,892]
+ CRUSH rule 1 x 354 [345,226,319,256,544,311,612,33,122]
+ CRUSH rule 1 x 355 [50,430,175,43,187,458,985,412,599]
+ CRUSH rule 1 x 356 [87,185,55,423,829,1,629,228,150]
+ CRUSH rule 1 x 357 [762,459,921,473,182,231,891,656,196]
+ CRUSH rule 1 x 358 [908,25,280,6,808,676,874,643,550]
+ CRUSH rule 1 x 359 [484,15,132,121,394,423,397,52,702]
+ CRUSH rule 1 x 360 [173,378,337,702,145,499,29,529,156]
+ CRUSH rule 1 x 361 [404,577,115,25,56,914,643,286,552]
+ CRUSH rule 1 x 362 [403,1,422,945,132,685,265,35,662]
+ CRUSH rule 1 x 363 [639,911,510,162,418,294,444,613,813]
+ CRUSH rule 1 x 364 [752,689,610,990,665,222,203,17,743]
+ CRUSH rule 1 x 365 [956,999,212,230,624,84,113,373,426]
+ CRUSH rule 1 x 366 [860,925,924,763,687,851,59,914,521]
+ CRUSH rule 1 x 367 [205,609,647,665,969,720,685,641,894]
+ CRUSH rule 1 x 368 [301,284,810,169,78,340,616,93,283]
+ CRUSH rule 1 x 369 [452,658,339,217,674,210,284,184,718]
+ CRUSH rule 1 x 370 [11,467,695,989,394,576,850,419,307]
+ CRUSH rule 1 x 371 [124,487,55,514,313,411,797,547,778]
+ CRUSH rule 1 x 372 [253,48,979,846,207,631,212,241,346]
+ CRUSH rule 1 x 373 [715,605,775,748,227,493,128,207,88]
+ CRUSH rule 1 x 374 [191,887,920,291,223,714,961,760,571]
+ CRUSH rule 1 x 375 [711,385,651,665,15,71,934,619,527]
+ CRUSH rule 1 x 376 [597,818,49,458,415,755,446,897,460]
+ CRUSH rule 1 x 377 [294,256,933,771,184,861,654,487,891]
+ CRUSH rule 1 x 378 [34,151,681,707,552,127,728,860,968]
+ CRUSH rule 1 x 379 [869,136,315,378,813,153,115,557,165]
+ CRUSH rule 1 x 380 [294,97,575,791,690,482,255,806,429]
+ CRUSH rule 1 x 381 [119,710,219,827,328,886,773,496,433]
+ CRUSH rule 1 x 382 [69,631,508,706,697,168,276,56,278]
+ CRUSH rule 1 x 383 [922,588,589,925,471,601,29,197,822]
+ CRUSH rule 1 x 384 [221,945,671,117,857,655,488,435,223]
+ CRUSH rule 1 x 385 [561,737,953,723,658,368,910,329,396]
+ CRUSH rule 1 x 386 [335,442,788,696,507,716,232,692,742]
+ CRUSH rule 1 x 387 [514,43,353,88,100,842,164,934,297]
+ CRUSH rule 1 x 388 [587,89,157,996,915,927,474,267,640]
+ CRUSH rule 1 x 389 [109,641,255,466,372,563,340,222,74]
+ CRUSH rule 1 x 390 [925,149,421,489,599,810,852,196,469]
+ CRUSH rule 1 x 391 [267,87,387,527,768,873,41,136,818]
+ CRUSH rule 1 x 392 [382,485,370,849,936,636,901,82,695]
+ CRUSH rule 1 x 393 [425,721,221,753,268,463,652,543,10]
+ CRUSH rule 1 x 394 [898,18,38,793,173,738,15,591,420]
+ CRUSH rule 1 x 395 [806,876,269,679,32,744,126,179,607]
+ CRUSH rule 1 x 396 [790,970,437,449,875,395,726,935,278]
+ CRUSH rule 1 x 397 [136,363,507,613,11,30,996,558,602]
+ CRUSH rule 1 x 398 [914,116,558,258,722,904,349,672,826]
+ CRUSH rule 1 x 399 [261,94,299,202,174,622,749,410,815]
+ CRUSH rule 1 x 400 [661,197,338,461,977,848,536,592,886]
+ CRUSH rule 1 x 401 [953,979,287,803,41,349,79,32,343]
+ CRUSH rule 1 x 402 [738,819,618,522,667,334,658,449,886]
+ CRUSH rule 1 x 403 [573,238,425,546,130,68,202,650,501]
+ CRUSH rule 1 x 404 [526,848,790,253,922,820,299,577,563]
+ CRUSH rule 1 x 405 [582,505,330,334,201,110,776,296,19]
+ CRUSH rule 1 x 406 [768,324,493,60,186,165,718,578,580]
+ CRUSH rule 1 x 407 [260,951,437,587,692,648,72,345,709]
+ CRUSH rule 1 x 408 [657,81,770,734,830,821,246,695,76]
+ CRUSH rule 1 x 409 [498,89,182,423,672,152,213,806,168]
+ CRUSH rule 1 x 410 [28,793,737,352,166,645,949,507,361]
+ CRUSH rule 1 x 411 [684,992,60,659,769,267,313,351,497]
+ CRUSH rule 1 x 412 [261,958,699,950,165,14,560,155,661]
+ CRUSH rule 1 x 413 [891,835,297,441,384,979,618,907,9]
+ CRUSH rule 1 x 414 [127,459,119,965,662,594,97,124,229]
+ CRUSH rule 1 x 415 [272,540,631,328,609,568,694,332,572]
+ CRUSH rule 1 x 416 [739,617,115,530,339,371,889,344,838]
+ CRUSH rule 1 x 417 [106,209,157,878,117,128,138,374,470]
+ CRUSH rule 1 x 418 [525,441,147,390,320,300,848,972,781]
+ CRUSH rule 1 x 419 [603,673,615,465,266,855,823,884,832]
+ CRUSH rule 1 x 420 [988,213,251,226,209,245,506,670,285]
+ CRUSH rule 1 x 421 [761,521,748,368,923,992,764,274,623]
+ CRUSH rule 1 x 422 [317,160,924,548,198,709,839,547,599]
+ CRUSH rule 1 x 423 [137,807,168,472,619,443,905,588,312]
+ CRUSH rule 1 x 424 [920,37,146,263,598,748,785,395,884]
+ CRUSH rule 1 x 425 [277,693,285,221,478,165,80,236,988]
+ CRUSH rule 1 x 426 [485,936,407,854,726,524,791,565,352]
+ CRUSH rule 1 x 427 [242,515,9,564,174,453,334,588,571]
+ CRUSH rule 1 x 428 [632,635,26,473,494,478,225,94,303]
+ CRUSH rule 1 x 429 [641,73,465,127,171,397,857,562,976]
+ CRUSH rule 1 x 430 [626,585,6,387,881,583,859,699,91]
+ CRUSH rule 1 x 431 [697,76,753,570,964,339,194,366,279]
+ CRUSH rule 1 x 432 [590,526,306,283,656,728,513,591,599]
+ CRUSH rule 1 x 433 [284,387,149,817,886,714,52,897,705]
+ CRUSH rule 1 x 434 [538,985,79,953,770,468,644,646,747]
+ CRUSH rule 1 x 435 [30,318,593,635,975,833,371,731,906]
+ CRUSH rule 1 x 436 [164,919,851,693,0,874,10,976,284]
+ CRUSH rule 1 x 437 [322,212,163,606,302,282,443,23,696]
+ CRUSH rule 1 x 438 [142,392,85,594,376,419,755,841,94]
+ CRUSH rule 1 x 439 [119,370,68,443,997,837,414,152,331]
+ CRUSH rule 1 x 440 [333,403,187,863,475,844,800,174,117]
+ CRUSH rule 1 x 441 [477,727,906,145,429,91,205,236,86]
+ CRUSH rule 1 x 442 [274,590,933,244,434,49,864,799,762]
+ CRUSH rule 1 x 443 [983,748,574,718,700,442,774,350,37]
+ CRUSH rule 1 x 444 [536,509,431,146,170,149,182,145,347]
+ CRUSH rule 1 x 445 [485,46,528,209,964,753,554,931,638]
+ CRUSH rule 1 x 446 [345,634,42,294,711,376,314,714,212]
+ CRUSH rule 1 x 447 [61,845,767,600,321,716,58,531,827]
+ CRUSH rule 1 x 448 [333,232,292,846,364,951,807,688,21]
+ CRUSH rule 1 x 449 [680,16,484,670,851,500,258,548,905]
+ CRUSH rule 1 x 450 [235,214,79,423,96,822,721,31,312]
+ CRUSH rule 1 x 451 [961,468,333,640,823,151,878,33,3]
+ CRUSH rule 1 x 452 [525,479,153,528,570,806,604,49,922]
+ CRUSH rule 1 x 453 [138,466,302,86,249,154,514,5,494]
+ CRUSH rule 1 x 454 [137,625,215,402,389,914,106,103,511]
+ CRUSH rule 1 x 455 [173,150,997,16,846,888,295,967,132]
+ CRUSH rule 1 x 456 [235,226,238,258,347,784,504,96,890]
+ CRUSH rule 1 x 457 [450,577,253,413,717,609,762,975,485]
+ CRUSH rule 1 x 458 [195,537,91,814,351,90,399,558,15]
+ CRUSH rule 1 x 459 [381,555,312,573,915,623,147,483,517]
+ CRUSH rule 1 x 460 [972,730,534,678,756,692,841,512,70]
+ CRUSH rule 1 x 461 [506,279,142,830,784,124,385,797,917]
+ CRUSH rule 1 x 462 [692,959,578,57,983,299,240,911,375]
+ CRUSH rule 1 x 463 [788,667,949,550,685,702,538,111,232]
+ CRUSH rule 1 x 464 [133,122,588,999,270,880,789,0,653]
+ CRUSH rule 1 x 465 [971,190,230,777,452,914,137,466,531]
+ CRUSH rule 1 x 466 [394,576,148,157,103,822,659,35,797]
+ CRUSH rule 1 x 467 [517,28,366,362,984,521,187,640,601]
+ CRUSH rule 1 x 468 [829,143,874,225,162,413,201,249,555]
+ CRUSH rule 1 x 469 [987,936,106,725,633,238,681,463,551]
+ CRUSH rule 1 x 470 [107,982,56,889,67,65,558,71,676]
+ CRUSH rule 1 x 471 [181,897,629,860,307,116,256,978,409]
+ CRUSH rule 1 x 472 [547,512,172,24,705,837,809,56,476]
+ CRUSH rule 1 x 473 [760,997,824,905,888,755,756,663,167]
+ CRUSH rule 1 x 474 [787,418,743,628,272,341,446,333,245]
+ CRUSH rule 1 x 475 [662,312,253,617,105,58,237,764,682]
+ CRUSH rule 1 x 476 [110,495,185,508,961,837,984,226,333]
+ CRUSH rule 1 x 477 [393,954,834,132,841,367,753,794,237]
+ CRUSH rule 1 x 478 [246,483,480,644,985,420,941,843,751]
+ CRUSH rule 1 x 479 [70,929,697,931,744,487,158,489,515]
+ CRUSH rule 1 x 480 [753,119,961,607,317,717,371,807,687]
+ CRUSH rule 1 x 481 [470,429,677,242,574,757,135,375,613]
+ CRUSH rule 1 x 482 [451,566,961,675,354,746,731,233,640]
+ CRUSH rule 1 x 483 [816,72,371,278,635,30,448,437,219]
+ CRUSH rule 1 x 484 [540,454,389,31,654,494,283,170,278]
+ CRUSH rule 1 x 485 [74,582,624,684,566,677,866,661,581]
+ CRUSH rule 1 x 486 [958,595,199,763,715,973,621,955,400]
+ CRUSH rule 1 x 487 [228,302,804,833,876,647,857,782,24]
+ CRUSH rule 1 x 488 [180,529,722,956,353,890,924,965,25]
+ CRUSH rule 1 x 489 [47,617,812,187,291,828,154,478,512]
+ CRUSH rule 1 x 490 [905,822,479,124,750,843,566,779,963]
+ CRUSH rule 1 x 491 [892,370,609,998,433,957,188,563,490]
+ CRUSH rule 1 x 492 [588,959,127,948,505,936,591,423,668]
+ CRUSH rule 1 x 493 [353,461,593,291,301,830,231,442,474]
+ CRUSH rule 1 x 494 [378,848,443,368,507,423,389,819,956]
+ CRUSH rule 1 x 495 [845,653,768,234,405,367,823,789,217]
+ CRUSH rule 1 x 496 [13,988,0,691,389,757,129,763,39]
+ CRUSH rule 1 x 497 [796,877,788,394,648,829,542,745,131]
+ CRUSH rule 1 x 498 [412,337,270,705,511,227,949,173,398]
+ CRUSH rule 1 x 499 [330,695,8,74,618,101,440,509,295]
+ CRUSH rule 1 x 500 [820,272,547,765,755,96,930,573,357]
+ CRUSH rule 1 x 501 [110,44,132,442,294,423,880,279,616]
+ CRUSH rule 1 x 502 [336,595,650,274,993,312,490,852,962]
+ CRUSH rule 1 x 503 [922,211,157,722,502,971,262,926,316]
+ CRUSH rule 1 x 504 [483,52,122,432,778,461,758,104,831]
+ CRUSH rule 1 x 505 [482,598,224,279,480,310,764,558,891]
+ CRUSH rule 1 x 506 [493,123,43,856,936,622,898,161,78]
+ CRUSH rule 1 x 507 [12,598,264,422,416,947,591,702,346]
+ CRUSH rule 1 x 508 [227,157,611,301,223,746,313,282,207]
+ CRUSH rule 1 x 509 [807,242,363,122,582,530,798,808,139]
+ CRUSH rule 1 x 510 [134,437,227,75,313,351,786,152,921]
+ CRUSH rule 1 x 511 [212,54,83,799,457,218,600,968,355]
+ CRUSH rule 1 x 512 [236,630,758,752,361,249,899,451,415]
+ CRUSH rule 1 x 513 [994,693,644,938,846,685,52,185,197]
+ CRUSH rule 1 x 514 [45,508,831,19,817,52,374,985,944]
+ CRUSH rule 1 x 515 [504,138,480,272,530,377,481,820,517]
+ CRUSH rule 1 x 516 [285,409,136,570,841,610,453,660,93]
+ CRUSH rule 1 x 517 [300,232,23,906,438,236,519,737,20]
+ CRUSH rule 1 x 518 [397,674,98,898,967,113,625,434,527]
+ CRUSH rule 1 x 519 [86,750,772,913,101,864,375,328,3]
+ CRUSH rule 1 x 520 [900,833,614,130,261,885,558,956,664]
+ CRUSH rule 1 x 521 [31,47,236,751,911,599,495,354,665]
+ CRUSH rule 1 x 522 [390,16,280,144,291,175,753,624,769]
+ CRUSH rule 1 x 523 [618,308,424,590,300,206,834,212,906]
+ CRUSH rule 1 x 524 [635,189,687,963,601,518,8,550,769]
+ CRUSH rule 1 x 525 [311,916,699,262,775,32,45,478,911]
+ CRUSH rule 1 x 526 [48,738,227,718,244,942,853,643,625]
+ CRUSH rule 1 x 527 [202,851,889,216,763,351,270,35,809]
+ CRUSH rule 1 x 528 [565,827,590,273,918,106,651,368,118]
+ CRUSH rule 1 x 529 [934,864,241,43,466,924,278,926,280]
+ CRUSH rule 1 x 530 [502,934,298,670,986,360,577,509,195]
+ CRUSH rule 1 x 531 [681,627,942,487,288,561,925,474,669]
+ CRUSH rule 1 x 532 [422,6,147,205,861,141,949,374,988]
+ CRUSH rule 1 x 533 [863,68,364,983,247,199,54,931,4]
+ CRUSH rule 1 x 534 [962,931,775,172,663,119,206,682,627]
+ CRUSH rule 1 x 535 [89,565,397,693,839,632,859,30,61]
+ CRUSH rule 1 x 536 [499,351,760,458,918,86,148,668,436]
+ CRUSH rule 1 x 537 [676,547,787,311,867,748,152,797,492]
+ CRUSH rule 1 x 538 [58,644,571,649,941,7,37,485,88]
+ CRUSH rule 1 x 539 [837,953,457,711,458,621,528,722,59]
+ CRUSH rule 1 x 540 [831,50,132,213,197,709,95,789,348]
+ CRUSH rule 1 x 541 [582,757,121,525,532,963,738,277,225]
+ CRUSH rule 1 x 542 [472,132,790,997,948,269,137,934,547]
+ CRUSH rule 1 x 543 [382,272,797,330,315,748,324,134,839]
+ CRUSH rule 1 x 544 [947,930,496,883,509,219,250,362,614]
+ CRUSH rule 1 x 545 [425,570,305,77,821,422,117,172,764]
+ CRUSH rule 1 x 546 [18,65,529,437,343,547,699,610,785]
+ CRUSH rule 1 x 547 [445,715,600,472,213,851,428,267,229]
+ CRUSH rule 1 x 548 [367,569,980,167,627,442,517,684,154]
+ CRUSH rule 1 x 549 [125,715,671,817,285,420,37,639,934]
+ CRUSH rule 1 x 550 [425,599,744,199,923,222,915,570,546]
+ CRUSH rule 1 x 551 [44,1,528,922,944,115,161,901,342]
+ CRUSH rule 1 x 552 [246,104,68,239,123,427,57,217,21]
+ CRUSH rule 1 x 553 [71,703,615,28,593,724,218,916,561]
+ CRUSH rule 1 x 554 [207,124,217,166,525,226,693,953,606]
+ CRUSH rule 1 x 555 [570,28,317,420,931,413,623,659,403]
+ CRUSH rule 1 x 556 [674,152,421,79,215,347,830,762,691]
+ CRUSH rule 1 x 557 [347,817,191,391,741,571,593,267,17]
+ CRUSH rule 1 x 558 [627,426,369,692,815,371,124,107,766]
+ CRUSH rule 1 x 559 [940,630,924,242,224,912,185,356,87]
+ CRUSH rule 1 x 560 [295,903,541,29,245,753,887,376,658]
+ CRUSH rule 1 x 561 [506,682,384,637,878,991,700,339,687]
+ CRUSH rule 1 x 562 [718,529,87,729,842,341,62,817,766]
+ CRUSH rule 1 x 563 [552,332,747,206,274,871,903,900,812]
+ CRUSH rule 1 x 564 [835,769,736,486,630,209,641,751,930]
+ CRUSH rule 1 x 565 [8,167,539,182,607,62,738,873,47]
+ CRUSH rule 1 x 566 [600,481,301,263,90,450,184,127,448]
+ CRUSH rule 1 x 567 [999,994,509,899,947,24,267,639,646]
+ CRUSH rule 1 x 568 [252,431,157,62,601,863,398,521,59]
+ CRUSH rule 1 x 569 [643,218,943,455,83,969,494,624,352]
+ CRUSH rule 1 x 570 [617,635,765,422,250,156,533,674,23]
+ CRUSH rule 1 x 571 [757,80,59,98,328,700,329,848,235]
+ CRUSH rule 1 x 572 [299,348,575,889,943,675,33,312,202]
+ CRUSH rule 1 x 573 [25,505,270,167,58,901,878,978,1]
+ CRUSH rule 1 x 574 [215,431,624,177,628,814,333,841,193]
+ CRUSH rule 1 x 575 [225,252,611,546,32,815,389,486,10]
+ CRUSH rule 1 x 576 [627,94,159,857,430,691,177,545,839]
+ CRUSH rule 1 x 577 [237,809,778,636,61,167,700,521,825]
+ CRUSH rule 1 x 578 [885,313,120,344,771,614,487,976,977]
+ CRUSH rule 1 x 579 [924,575,787,831,47,996,557,630,468]
+ CRUSH rule 1 x 580 [718,51,766,121,118,471,608,755,326]
+ CRUSH rule 1 x 581 [219,807,129,571,856,179,874,902,958]
+ CRUSH rule 1 x 582 [893,701,598,863,285,829,984,622,175]
+ CRUSH rule 1 x 583 [246,930,964,170,993,409,469,193,737]
+ CRUSH rule 1 x 584 [336,432,680,175,495,839,642,226,122]
+ CRUSH rule 1 x 585 [324,999,397,485,457,527,73,628,884]
+ CRUSH rule 1 x 586 [558,230,976,541,816,72,794,682,127]
+ CRUSH rule 1 x 587 [985,830,597,21,308,890,952,421,875]
+ CRUSH rule 1 x 588 [211,544,57,134,162,496,195,581,649]
+ CRUSH rule 1 x 589 [129,21,112,190,885,844,753,180,160]
+ CRUSH rule 1 x 590 [467,969,652,593,287,76,811,413,436]
+ CRUSH rule 1 x 591 [758,514,316,164,35,110,54,796,369]
+ CRUSH rule 1 x 592 [525,253,190,443,315,603,667,318,496]
+ CRUSH rule 1 x 593 [601,885,339,152,297,223,269,455,168]
+ CRUSH rule 1 x 594 [227,60,450,30,717,840,994,16,777]
+ CRUSH rule 1 x 595 [720,854,496,912,80,655,917,525,945]
+ CRUSH rule 1 x 596 [751,195,997,77,261,490,180,482,449]
+ CRUSH rule 1 x 597 [129,574,714,8,789,847,725,991,955]
+ CRUSH rule 1 x 598 [679,207,604,396,841,284,286,280,507]
+ CRUSH rule 1 x 599 [668,315,683,349,681,253,599,364,546]
+ CRUSH rule 1 x 600 [143,396,464,444,59,57,243,264,31]
+ CRUSH rule 1 x 601 [326,573,873,902,136,921,633,596,988]
+ CRUSH rule 1 x 602 [860,281,875,535,672,474,697,763,442]
+ CRUSH rule 1 x 603 [709,328,445,349,190,455,924,667,356]
+ CRUSH rule 1 x 604 [571,62,814,95,866,978,983,281,292]
+ CRUSH rule 1 x 605 [252,739,860,27,313,362,857,899,349]
+ CRUSH rule 1 x 606 [339,236,759,842,67,644,954,94,88]
+ CRUSH rule 1 x 607 [590,248,759,868,433,398,578,386,226]
+ CRUSH rule 1 x 608 [145,635,309,467,875,115,148,33,420]
+ CRUSH rule 1 x 609 [973,547,223,79,762,863,249,41,778]
+ CRUSH rule 1 x 610 [435,816,961,983,255,886,160,888,685]
+ CRUSH rule 1 x 611 [559,283,422,584,176,429,570,43,362]
+ CRUSH rule 1 x 612 [273,149,123,576,911,270,296,735,245]
+ CRUSH rule 1 x 613 [828,614,642,674,33,361,958,580,197]
+ CRUSH rule 1 x 614 [478,748,393,34,171,80,92,12,62]
+ CRUSH rule 1 x 615 [392,155,144,326,626,134,149,401,14]
+ CRUSH rule 1 x 616 [778,637,452,248,15,888,74,307,976]
+ CRUSH rule 1 x 617 [622,713,996,833,611,407,364,8,342]
+ CRUSH rule 1 x 618 [149,877,270,329,180,327,222,749,697]
+ CRUSH rule 1 x 619 [604,163,656,409,322,848,519,967,737]
+ CRUSH rule 1 x 620 [181,23,409,198,64,898,35,620,268]
+ CRUSH rule 1 x 621 [735,902,386,237,939,475,725,118,875]
+ CRUSH rule 1 x 622 [661,824,717,568,858,583,446,798,869]
+ CRUSH rule 1 x 623 [142,121,643,61,695,852,485,478,185]
+ CRUSH rule 1 x 624 [360,716,420,398,49,717,137,140,488]
+ CRUSH rule 1 x 625 [541,167,385,1,601,481,308,111,207]
+ CRUSH rule 1 x 626 [364,431,610,363,535,747,225,841,868]
+ CRUSH rule 1 x 627 [458,137,557,410,287,749,467,432,944]
+ CRUSH rule 1 x 628 [250,350,556,497,821,65,205,580,972]
+ CRUSH rule 1 x 629 [928,160,710,572,365,772,538,46,300]
+ CRUSH rule 1 x 630 [243,19,918,556,601,16,920,830,171]
+ CRUSH rule 1 x 631 [438,221,574,676,797,580,219,211,157]
+ CRUSH rule 1 x 632 [797,368,247,5,32,102,416,45,624]
+ CRUSH rule 1 x 633 [993,749,525,485,27,330,275,599,219]
+ CRUSH rule 1 x 634 [239,351,633,299,651,678,296,337,676]
+ CRUSH rule 1 x 635 [640,965,25,961,306,172,849,357,317]
+ CRUSH rule 1 x 636 [173,290,297,991,937,823,236,318,228]
+ CRUSH rule 1 x 637 [0,918,98,108,111,495,887,57,16]
+ CRUSH rule 1 x 638 [702,235,424,900,983,754,701,887,355]
+ CRUSH rule 1 x 639 [475,687,31,785,918,611,27,214,226]
+ CRUSH rule 1 x 640 [31,664,399,677,123,609,858,138,726]
+ CRUSH rule 1 x 641 [296,473,108,963,341,876,897,449,42]
+ CRUSH rule 1 x 642 [894,273,427,606,677,670,610,665,299]
+ CRUSH rule 1 x 643 [117,111,732,191,114,153,500,631,833]
+ CRUSH rule 1 x 644 [438,336,327,512,599,862,660,857,123]
+ CRUSH rule 1 x 645 [982,702,351,573,907,915,279,317,414]
+ CRUSH rule 1 x 646 [334,804,146,842,697,638,720,135,369]
+ CRUSH rule 1 x 647 [933,787,185,334,752,285,372,890,30]
+ CRUSH rule 1 x 648 [22,444,400,862,207,842,453,732,262]
+ CRUSH rule 1 x 649 [503,229,213,460,639,760,722,748,599]
+ CRUSH rule 1 x 650 [328,659,420,443,739,950,869,150,743]
+ CRUSH rule 1 x 651 [3,880,823,123,378,585,715,221,31]
+ CRUSH rule 1 x 652 [495,977,563,733,92,997,119,818,459]
+ CRUSH rule 1 x 653 [185,718,804,280,975,912,198,291,71]
+ CRUSH rule 1 x 654 [130,528,380,81,906,511,431,506,546]
+ CRUSH rule 1 x 655 [560,872,454,504,319,284,605,214,833]
+ CRUSH rule 1 x 656 [219,885,178,981,863,508,708,6,746]
+ CRUSH rule 1 x 657 [233,684,813,490,208,941,858,16,128]
+ CRUSH rule 1 x 658 [778,6,756,380,750,836,547,850,499]
+ CRUSH rule 1 x 659 [240,663,306,540,789,902,170,954,22]
+ CRUSH rule 1 x 660 [244,855,196,147,678,323,63,859,215]
+ CRUSH rule 1 x 661 [184,270,128,398,910,230,402,205,609]
+ CRUSH rule 1 x 662 [65,883,921,438,79,957,464,902,276]
+ CRUSH rule 1 x 663 [323,721,594,812,43,992,170,65,906]
+ CRUSH rule 1 x 664 [865,113,512,51,427,123,585,260,254]
+ CRUSH rule 1 x 665 [420,850,591,475,202,733,798,658,28]
+ CRUSH rule 1 x 666 [319,767,246,3,369,493,796,56,736]
+ CRUSH rule 1 x 667 [875,39,343,100,829,2,795,783,386]
+ CRUSH rule 1 x 668 [331,122,263,599,355,484,943,554,395]
+ CRUSH rule 1 x 669 [915,521,402,747,673,445,938,600,517]
+ CRUSH rule 1 x 670 [845,659,943,447,401,322,168,302,681]
+ CRUSH rule 1 x 671 [108,634,527,363,856,238,755,330,584]
+ CRUSH rule 1 x 672 [578,216,110,589,302,137,954,315,735]
+ CRUSH rule 1 x 673 [442,74,579,797,622,950,371,402,725]
+ CRUSH rule 1 x 674 [588,364,281,308,645,631,229,506,565]
+ CRUSH rule 1 x 675 [489,698,744,671,870,174,528,875,982]
+ CRUSH rule 1 x 676 [928,911,40,180,722,729,673,569,701]
+ CRUSH rule 1 x 677 [399,269,692,131,615,136,103,763,527]
+ CRUSH rule 1 x 678 [546,752,544,155,5,463,666,352,576]
+ CRUSH rule 1 x 679 [988,25,275,433,628,57,247,620,437]
+ CRUSH rule 1 x 680 [335,963,382,486,749,257,795,347,831]
+ CRUSH rule 1 x 681 [690,462,623,466,49,471,774,192,454]
+ CRUSH rule 1 x 682 [196,588,154,257,807,776,367,718,345]
+ CRUSH rule 1 x 683 [627,25,421,160,873,102,345,599,30]
+ CRUSH rule 1 x 684 [38,804,592,158,991,264,652,821,641]
+ CRUSH rule 1 x 685 [841,368,548,362,166,211,154,121,51]
+ CRUSH rule 1 x 686 [336,287,525,440,166,993,911,638,690]
+ CRUSH rule 1 x 687 [20,682,924,653,356,16,917,622,156]
+ CRUSH rule 1 x 688 [463,371,780,556,385,883,115,248,566]
+ CRUSH rule 1 x 689 [569,250,78,816,847,775,333,161,74]
+ CRUSH rule 1 x 690 [551,144,587,263,378,394,970,639,835]
+ CRUSH rule 1 x 691 [766,464,446,533,449,541,451,290,789]
+ CRUSH rule 1 x 692 [739,634,18,245,624,35,268,525,425]
+ CRUSH rule 1 x 693 [339,297,118,330,817,91,828,276,264]
+ CRUSH rule 1 x 694 [405,26,830,181,533,166,488,804,501]
+ CRUSH rule 1 x 695 [622,576,597,535,600,593,300,989,804]
+ CRUSH rule 1 x 696 [558,902,689,13,715,28,664,489,598]
+ CRUSH rule 1 x 697 [818,222,406,691,427,863,153,922,986]
+ CRUSH rule 1 x 698 [178,48,402,233,841,604,468,180,783]
+ CRUSH rule 1 x 699 [450,244,180,919,711,332,747,453,519]
+ CRUSH rule 1 x 700 [502,771,987,706,416,240,68,641,109]
+ CRUSH rule 1 x 701 [4,612,782,216,853,303,585,513,907]
+ CRUSH rule 1 x 702 [177,630,232,923,281,708,466,687,742]
+ CRUSH rule 1 x 703 [354,178,389,393,778,803,796,607,894]
+ CRUSH rule 1 x 704 [646,601,156,171,603,116,655,595,888]
+ CRUSH rule 1 x 705 [921,401,890,265,244,690,372,253,807]
+ CRUSH rule 1 x 706 [652,877,562,452,26,323,923,770,516]
+ CRUSH rule 1 x 707 [345,745,67,716,789,576,2,133,256]
+ CRUSH rule 1 x 708 [333,607,180,469,170,555,939,331,41]
+ CRUSH rule 1 x 709 [45,187,302,115,896,579,733,607,763]
+ CRUSH rule 1 x 710 [94,855,43,199,18,948,449,28,731]
+ CRUSH rule 1 x 711 [227,653,731,150,693,842,534,110,639]
+ CRUSH rule 1 x 712 [398,953,136,870,181,408,895,459,341]
+ CRUSH rule 1 x 713 [116,800,503,662,635,579,53,839,56]
+ CRUSH rule 1 x 714 [111,629,866,709,902,557,875,649,23]
+ CRUSH rule 1 x 715 [531,291,486,382,192,807,322,417,973]
+ CRUSH rule 1 x 716 [169,541,291,42,343,724,138,197,32]
+ CRUSH rule 1 x 717 [417,446,994,894,239,494,237,62,327]
+ CRUSH rule 1 x 718 [992,383,298,844,377,463,544,891,210]
+ CRUSH rule 1 x 719 [936,674,324,759,194,409,828,975,119]
+ CRUSH rule 1 x 720 [370,188,174,464,644,218,214,76,870]
+ CRUSH rule 1 x 721 [320,859,278,259,170,957,177,264,867]
+ CRUSH rule 1 x 722 [7,2,673,129,96,445,823,833,1]
+ CRUSH rule 1 x 723 [270,553,831,662,38,101,985,846,77]
+ CRUSH rule 1 x 724 [666,822,708,895,633,800,616,879,480]
+ CRUSH rule 1 x 725 [794,406,875,459,981,751,359,34,720]
+ CRUSH rule 1 x 726 [420,556,341,292,240,68,966,535,669]
+ CRUSH rule 1 x 727 [561,461,129,635,965,610,105,31,506]
+ CRUSH rule 1 x 728 [951,330,196,756,589,849,753,760,254]
+ CRUSH rule 1 x 729 [656,644,436,591,27,119,572,933,434]
+ CRUSH rule 1 x 730 [3,558,629,184,50,765,760,800,945]
+ CRUSH rule 1 x 731 [852,89,75,735,713,113,528,890,625]
+ CRUSH rule 1 x 732 [983,840,869,976,697,307,368,271,778]
+ CRUSH rule 1 x 733 [285,396,388,122,387,364,880,343,590]
+ CRUSH rule 1 x 734 [125,510,402,640,676,501,535,627,224]
+ CRUSH rule 1 x 735 [417,773,686,504,459,912,690,59,294]
+ CRUSH rule 1 x 736 [749,396,632,550,779,109,845,278,559]
+ CRUSH rule 1 x 737 [644,991,946,135,448,903,482,564,259]
+ CRUSH rule 1 x 738 [449,683,290,220,245,525,429,397,872]
+ CRUSH rule 1 x 739 [341,220,641,454,740,661,146,17,314]
+ CRUSH rule 1 x 740 [874,524,674,650,472,282,214,494,593]
+ CRUSH rule 1 x 741 [189,472,712,798,715,757,863,571,876]
+ CRUSH rule 1 x 742 [912,581,114,759,730,21,687,81,145]
+ CRUSH rule 1 x 743 [654,914,425,441,763,39,451,631,911]
+ CRUSH rule 1 x 744 [725,295,579,377,162,447,843,699,24]
+ CRUSH rule 1 x 745 [787,858,850,506,612,735,926,314,771]
+ CRUSH rule 1 x 746 [757,848,704,30,47,940,450,651,105]
+ CRUSH rule 1 x 747 [700,81,867,681,801,64,879,857,727]
+ CRUSH rule 1 x 748 [557,436,238,664,293,865,304,999,685]
+ CRUSH rule 1 x 749 [772,622,337,42,156,302,383,506,570]
+ CRUSH rule 1 x 750 [946,97,376,677,316,670,169,171,9]
+ CRUSH rule 1 x 751 [996,618,343,911,83,22,388,17,892]
+ CRUSH rule 1 x 752 [746,887,695,868,610,950,88,315,728]
+ CRUSH rule 1 x 753 [741,14,463,479,172,192,481,702,431]
+ CRUSH rule 1 x 754 [648,349,333,355,65,63,336,724,262]
+ CRUSH rule 1 x 755 [157,460,466,187,959,674,192,279,371]
+ CRUSH rule 1 x 756 [416,97,197,497,227,3,850,191,991]
+ CRUSH rule 1 x 757 [599,839,776,410,256,823,121,690,544]
+ CRUSH rule 1 x 758 [994,218,620,256,361,749,165,686,449]
+ CRUSH rule 1 x 759 [959,682,514,745,100,519,15,347,311]
+ CRUSH rule 1 x 760 [518,943,215,83,706,137,345,69,39]
+ CRUSH rule 1 x 761 [285,849,420,324,987,338,373,361,684]
+ CRUSH rule 1 x 762 [591,313,41,335,110,696,664,350,339]
+ CRUSH rule 1 x 763 [908,411,200,740,292,295,387,775,797]
+ CRUSH rule 1 x 764 [787,234,894,485,883,711,70,202,557]
+ CRUSH rule 1 x 765 [327,921,882,393,444,792,402,123,902]
+ CRUSH rule 1 x 766 [84,161,878,704,416,144,357,310,890]
+ CRUSH rule 1 x 767 [370,895,702,701,890,2,251,951,675]
+ CRUSH rule 1 x 768 [826,760,879,864,460,474,645,975,947]
+ CRUSH rule 1 x 769 [67,768,663,735,814,66,213,527,546]
+ CRUSH rule 1 x 770 [593,909,482,259,5,550,961,324,309]
+ CRUSH rule 1 x 771 [309,935,121,578,937,685,933,571,822]
+ CRUSH rule 1 x 772 [12,125,797,301,348,419,891,959,487]
+ CRUSH rule 1 x 773 [253,466,820,549,591,193,783,951,982]
+ CRUSH rule 1 x 774 [164,390,705,109,881,505,890,425,599]
+ CRUSH rule 1 x 775 [703,47,43,973,643,406,885,976,936]
+ CRUSH rule 1 x 776 [728,231,80,916,2,850,396,76,680]
+ CRUSH rule 1 x 777 [981,621,568,729,869,952,563,860,388]
+ CRUSH rule 1 x 778 [411,456,544,597,789,784,65,954,125]
+ CRUSH rule 1 x 779 [346,121,519,921,587,48,772,645,254]
+ CRUSH rule 1 x 780 [476,39,288,381,303,29,17,336,147]
+ CRUSH rule 1 x 781 [10,130,585,844,729,705,714,954,271]
+ CRUSH rule 1 x 782 [462,246,581,902,623,877,812,516,774]
+ CRUSH rule 1 x 783 [580,373,153,775,668,661,626,961,576]
+ CRUSH rule 1 x 784 [413,113,978,990,994,56,481,198,171]
+ CRUSH rule 1 x 785 [341,856,332,354,59,581,632,151,586]
+ CRUSH rule 1 x 786 [411,140,313,393,215,618,490,481,627]
+ CRUSH rule 1 x 787 [605,522,211,813,636,224,600,528,966]
+ CRUSH rule 1 x 788 [226,545,35,142,726,851,194,216,486]
+ CRUSH rule 1 x 789 [545,320,414,702,731,277,237,916,374]
+ CRUSH rule 1 x 790 [414,748,816,327,130,115,788,164,691]
+ CRUSH rule 1 x 791 [660,906,406,697,916,322,124,267,742]
+ CRUSH rule 1 x 792 [287,392,514,204,75,789,406,858,694]
+ CRUSH rule 1 x 793 [631,133,850,713,720,487,376,812,886]
+ CRUSH rule 1 x 794 [931,517,543,210,963,898,811,459,344]
+ CRUSH rule 1 x 795 [551,962,477,948,425,434,268,94,648]
+ CRUSH rule 1 x 796 [814,4,95,27,368,300,646,451,67]
+ CRUSH rule 1 x 797 [64,201,299,734,605,864,596,196,93]
+ CRUSH rule 1 x 798 [422,530,114,431,565,716,473,250,839]
+ CRUSH rule 1 x 799 [824,32,679,562,266,549,859,994,831]
+ CRUSH rule 1 x 800 [862,623,489,637,861,196,941,643,398]
+ CRUSH rule 1 x 801 [145,550,329,324,734,160,219,662,142]
+ CRUSH rule 1 x 802 [570,19,847,308,387,518,846,53,783]
+ CRUSH rule 1 x 803 [151,812,662,358,880,349,834,881,23]
+ CRUSH rule 1 x 804 [467,93,264,863,176,842,663,949,380]
+ CRUSH rule 1 x 805 [621,223,938,809,591,686,121,157,934]
+ CRUSH rule 1 x 806 [898,957,805,430,499,584,640,607,790]
+ CRUSH rule 1 x 807 [354,531,422,159,921,431,802,136,305]
+ CRUSH rule 1 x 808 [7,96,76,897,446,2,166,929,234]
+ CRUSH rule 1 x 809 [70,734,719,56,687,21,23,145,184]
+ CRUSH rule 1 x 810 [701,18,972,327,771,649,620,648,433]
+ CRUSH rule 1 x 811 [248,547,103,728,901,264,948,202,521]
+ CRUSH rule 1 x 812 [230,576,821,566,993,762,675,28,263]
+ CRUSH rule 1 x 813 [805,114,683,629,288,462,285,450,948]
+ CRUSH rule 1 x 814 [54,619,973,741,497,894,401,266,905]
+ CRUSH rule 1 x 815 [679,412,613,132,969,411,314,670,928]
+ CRUSH rule 1 x 816 [919,448,826,414,36,289,44,822,332]
+ CRUSH rule 1 x 817 [765,830,436,521,332,458,260,172,193]
+ CRUSH rule 1 x 818 [415,566,644,687,692,414,769,826,519]
+ CRUSH rule 1 x 819 [721,319,865,750,546,859,523,770,56]
+ CRUSH rule 1 x 820 [218,301,333,190,686,179,535,787,267]
+ CRUSH rule 1 x 821 [185,795,680,953,329,750,621,815,313]
+ CRUSH rule 1 x 822 [356,261,54,522,900,103,883,112,601]
+ CRUSH rule 1 x 823 [220,281,549,456,64,306,282,641,216]
+ CRUSH rule 1 x 824 [292,809,887,74,776,788,559,886,753]
+ CRUSH rule 1 x 825 [949,778,101,311,110,480,161,998,370]
+ CRUSH rule 1 x 826 [767,818,833,927,356,954,910,63,288]
+ CRUSH rule 1 x 827 [631,83,406,635,657,713,212,916,692]
+ CRUSH rule 1 x 828 [288,986,445,26,414,607,937,595,935]
+ CRUSH rule 1 x 829 [990,667,915,694,974,453,669,330,822]
+ CRUSH rule 1 x 830 [152,571,778,505,685,209,448,55,965]
+ CRUSH rule 1 x 831 [814,563,630,97,582,107,142,157,957]
+ CRUSH rule 1 x 832 [235,641,616,110,979,844,656,135,341]
+ CRUSH rule 1 x 833 [657,565,922,140,825,457,764,766,853]
+ CRUSH rule 1 x 834 [907,231,644,13,617,130,83,483,811]
+ CRUSH rule 1 x 835 [784,262,771,264,612,238,537,937,101]
+ CRUSH rule 1 x 836 [951,158,366,710,43,427,351,961,52]
+ CRUSH rule 1 x 837 [556,498,334,633,895,627,903,29,454]
+ CRUSH rule 1 x 838 [329,274,964,547,119,342,983,998,320]
+ CRUSH rule 1 x 839 [568,209,939,364,658,747,47,859,402]
+ CRUSH rule 1 x 840 [45,579,842,70,655,862,815,109,762]
+ CRUSH rule 1 x 841 [652,702,24,605,152,93,226,46,918]
+ CRUSH rule 1 x 842 [629,984,314,895,408,897,575,1,312]
+ CRUSH rule 1 x 843 [799,690,688,648,151,812,486,199,966]
+ CRUSH rule 1 x 844 [694,600,534,700,569,11,899,382,851]
+ CRUSH rule 1 x 845 [332,30,179,93,951,324,611,512,855]
+ CRUSH rule 1 x 846 [452,251,712,719,404,739,606,237,414]
+ CRUSH rule 1 x 847 [399,681,847,739,13,555,363,893,592]
+ CRUSH rule 1 x 848 [303,138,440,346,547,216,700,249,214]
+ CRUSH rule 1 x 849 [666,346,708,873,64,694,847,463,995]
+ CRUSH rule 1 x 850 [644,511,345,844,545,337,358,35,913]
+ CRUSH rule 1 x 851 [527,546,737,425,100,331,95,337,677]
+ CRUSH rule 1 x 852 [31,809,94,618,156,853,469,511,999]
+ CRUSH rule 1 x 853 [483,330,869,184,46,942,774,679,616]
+ CRUSH rule 1 x 854 [697,953,968,143,502,955,441,302,437]
+ CRUSH rule 1 x 855 [837,996,239,621,32,191,686,702,919]
+ CRUSH rule 1 x 856 [712,40,547,430,195,857,224,810,404]
+ CRUSH rule 1 x 857 [77,984,576,551,568,96,12,763,594]
+ CRUSH rule 1 x 858 [412,384,841,465,572,576,688,61,545]
+ CRUSH rule 1 x 859 [173,760,26,300,87,567,463,903,272]
+ CRUSH rule 1 x 860 [776,429,328,917,658,783,699,907,532]
+ CRUSH rule 1 x 861 [705,405,477,50,73,714,901,487,725]
+ CRUSH rule 1 x 862 [809,44,788,938,964,177,490,409,15]
+ CRUSH rule 1 x 863 [349,496,963,178,675,853,172,980,772]
+ CRUSH rule 1 x 864 [717,858,101,239,992,244,43,15,29]
+ CRUSH rule 1 x 865 [857,603,586,262,550,289,850,40,170]
+ CRUSH rule 1 x 866 [394,304,71,96,642,155,255,481,435]
+ CRUSH rule 1 x 867 [640,773,663,974,261,296,988,730,753]
+ CRUSH rule 1 x 868 [613,950,712,663,761,460,643,547,734]
+ CRUSH rule 1 x 869 [973,889,524,22,671,477,718,431,968]
+ CRUSH rule 1 x 870 [505,35,386,498,348,503,54,992,726]
+ CRUSH rule 1 x 871 [239,264,262,773,781,734,387,515,98]
+ CRUSH rule 1 x 872 [21,767,456,748,783,797,180,800,521]
+ CRUSH rule 1 x 873 [954,666,980,264,435,233,199,358,805]
+ CRUSH rule 1 x 874 [54,510,947,1,500,119,93,915,801]
+ CRUSH rule 1 x 875 [809,418,452,462,88,673,634,435,778]
+ CRUSH rule 1 x 876 [483,457,61,248,523,277,322,141,82]
+ CRUSH rule 1 x 877 [542,531,952,939,710,179,181,460,459]
+ CRUSH rule 1 x 878 [217,674,857,644,678,809,329,591,59]
+ CRUSH rule 1 x 879 [999,475,134,250,319,357,145,750,54]
+ CRUSH rule 1 x 880 [678,573,935,385,570,651,319,630,888]
+ CRUSH rule 1 x 881 [394,835,789,802,587,155,570,109,896]
+ CRUSH rule 1 x 882 [467,382,353,56,979,674,974,483,412]
+ CRUSH rule 1 x 883 [802,744,237,337,50,96,202,148,129]
+ CRUSH rule 1 x 884 [653,660,638,700,31,558,389,381,347]
+ CRUSH rule 1 x 885 [898,704,307,445,879,872,174,972,544]
+ CRUSH rule 1 x 886 [434,357,938,641,737,8,56,582,915]
+ CRUSH rule 1 x 887 [297,226,711,428,370,318,472,947,35]
+ CRUSH rule 1 x 888 [863,324,443,213,902,25,806,53,385]
+ CRUSH rule 1 x 889 [105,102,308,163,947,548,399,382,761]
+ CRUSH rule 1 x 890 [550,248,606,704,615,708,996,561,485]
+ CRUSH rule 1 x 891 [575,928,880,891,826,763,706,701,501]
+ CRUSH rule 1 x 892 [259,862,133,271,292,162,53,333,458]
+ CRUSH rule 1 x 893 [902,880,543,542,37,942,672,320,394]
+ CRUSH rule 1 x 894 [180,169,916,43,945,713,648,685,895]
+ CRUSH rule 1 x 895 [725,849,182,129,177,272,599,829,809]
+ CRUSH rule 1 x 896 [951,34,874,537,969,123,210,529,491]
+ CRUSH rule 1 x 897 [810,352,73,939,943,895,12,481,539]
+ CRUSH rule 1 x 898 [979,433,719,411,787,359,342,37,303]
+ CRUSH rule 1 x 899 [685,668,534,932,399,156,124,653,574]
+ CRUSH rule 1 x 900 [530,978,41,894,941,681,380,419,667]
+ CRUSH rule 1 x 901 [740,107,336,175,574,706,157,292,724]
+ CRUSH rule 1 x 902 [800,743,693,310,67,111,178,624,733]
+ CRUSH rule 1 x 903 [230,267,842,266,550,769,66,738,419]
+ CRUSH rule 1 x 904 [346,949,460,973,696,91,957,801,74]
+ CRUSH rule 1 x 905 [530,397,619,958,576,973,685,6,689]
+ CRUSH rule 1 x 906 [80,426,138,672,73,776,30,169,506]
+ CRUSH rule 1 x 907 [365,968,475,297,296,724,664,331,184]
+ CRUSH rule 1 x 908 [204,832,742,809,862,745,484,391,841]
+ CRUSH rule 1 x 909 [883,989,146,959,366,59,686,965,515]
+ CRUSH rule 1 x 910 [549,593,249,853,792,769,824,552,717]
+ CRUSH rule 1 x 911 [325,847,352,214,851,732,789,255,896]
+ CRUSH rule 1 x 912 [874,888,582,796,557,601,226,889,69]
+ CRUSH rule 1 x 913 [331,463,342,574,989,362,925,746,664]
+ CRUSH rule 1 x 914 [836,468,601,732,607,275,70,280,837]
+ CRUSH rule 1 x 915 [245,228,100,661,799,13,126,79,652]
+ CRUSH rule 1 x 916 [77,967,364,435,27,474,255,133,892]
+ CRUSH rule 1 x 917 [239,60,866,221,772,967,725,707,47]
+ CRUSH rule 1 x 918 [988,115,922,80,201,544,583,923,863]
+ CRUSH rule 1 x 919 [783,139,696,1,848,169,888,980,33]
+ CRUSH rule 1 x 920 [623,408,685,953,974,696,532,124,911]
+ CRUSH rule 1 x 921 [105,799,144,90,399,373,633,290,155]
+ CRUSH rule 1 x 922 [887,505,652,348,514,806,952,474,67]
+ CRUSH rule 1 x 923 [223,318,552,458,743,871,964,384,454]
+ CRUSH rule 1 x 924 [25,778,366,333,163,801,584,31,151]
+ CRUSH rule 1 x 925 [912,601,297,682,770,173,969,168,500]
+ CRUSH rule 1 x 926 [968,133,135,144,814,155,709,158,96]
+ CRUSH rule 1 x 927 [277,724,214,988,690,342,465,775,725]
+ CRUSH rule 1 x 928 [554,203,658,789,298,299,847,752,780]
+ CRUSH rule 1 x 929 [761,802,367,528,758,522,744,171,144]
+ CRUSH rule 1 x 930 [814,61,788,736,660,491,832,654,567]
+ CRUSH rule 1 x 931 [29,193,61,41,343,664,487,839,776]
+ CRUSH rule 1 x 932 [446,198,862,534,168,35,530,462,202]
+ CRUSH rule 1 x 933 [352,742,216,321,525,44,568,61,945]
+ CRUSH rule 1 x 934 [730,2,332,631,613,249,533,116,254]
+ CRUSH rule 1 x 935 [731,23,736,79,361,992,772,49,567]
+ CRUSH rule 1 x 936 [322,975,20,904,827,603,138,802,885]
+ CRUSH rule 1 x 937 [822,221,841,161,723,137,630,308,973]
+ CRUSH rule 1 x 938 [557,850,66,630,499,404,286,395,927]
+ CRUSH rule 1 x 939 [150,11,971,371,124,785,408,49,977]
+ CRUSH rule 1 x 940 [638,398,169,616,333,751,25,883,867]
+ CRUSH rule 1 x 941 [730,342,929,577,451,838,964,28,633]
+ CRUSH rule 1 x 942 [62,292,166,814,587,172,854,16,440]
+ CRUSH rule 1 x 943 [165,314,519,548,41,726,759,851,617]
+ CRUSH rule 1 x 944 [199,625,766,176,194,297,678,915,619]
+ CRUSH rule 1 x 945 [946,999,699,303,38,81,952,885,987]
+ CRUSH rule 1 x 946 [595,93,852,142,503,647,933,267,846]
+ CRUSH rule 1 x 947 [800,582,356,93,716,117,922,868,413]
+ CRUSH rule 1 x 948 [132,551,139,920,87,46,81,220,725]
+ CRUSH rule 1 x 949 [792,920,466,380,97,568,799,961,564]
+ CRUSH rule 1 x 950 [111,345,176,543,879,954,355,220,528]
+ CRUSH rule 1 x 951 [414,619,648,655,364,971,829,408,568]
+ CRUSH rule 1 x 952 [775,469,500,356,287,4,16,746,835]
+ CRUSH rule 1 x 953 [349,1,5,251,168,680,141,619,234]
+ CRUSH rule 1 x 954 [570,940,410,249,929,394,129,696,115]
+ CRUSH rule 1 x 955 [729,774,823,800,7,127,536,766,579]
+ CRUSH rule 1 x 956 [519,141,575,625,738,475,169,751,667]
+ CRUSH rule 1 x 957 [242,709,611,97,760,309,393,281,227]
+ CRUSH rule 1 x 958 [84,217,227,253,246,604,346,377,425]
+ CRUSH rule 1 x 959 [270,413,918,789,703,608,543,519,496]
+ CRUSH rule 1 x 960 [458,192,307,279,920,139,855,49,548]
+ CRUSH rule 1 x 961 [981,388,777,546,359,660,455,708,649]
+ CRUSH rule 1 x 962 [623,834,277,134,729,246,856,477,895]
+ CRUSH rule 1 x 963 [291,167,714,468,109,373,485,701,76]
+ CRUSH rule 1 x 964 [28,156,788,127,598,215,361,255,507]
+ CRUSH rule 1 x 965 [675,557,290,517,840,510,59,229,819]
+ CRUSH rule 1 x 966 [836,306,946,283,642,606,929,773,928]
+ CRUSH rule 1 x 967 [966,386,735,837,392,116,19,674,395]
+ CRUSH rule 1 x 968 [864,756,690,121,328,122,433,520,916]
+ CRUSH rule 1 x 969 [729,625,480,769,512,882,518,956,398]
+ CRUSH rule 1 x 970 [800,362,646,582,309,102,576,411,416]
+ CRUSH rule 1 x 971 [737,381,153,684,298,166,344,520,546]
+ CRUSH rule 1 x 972 [952,245,720,884,334,311,754,540,79]
+ CRUSH rule 1 x 973 [356,455,579,857,832,596,549,524,109]
+ CRUSH rule 1 x 974 [545,758,586,596,539,790,116,993,644]
+ CRUSH rule 1 x 975 [336,191,202,146,720,897,330,308,744]
+ CRUSH rule 1 x 976 [446,208,757,620,252,846,397,58,57]
+ CRUSH rule 1 x 977 [202,896,196,956,763,126,783,828,409]
+ CRUSH rule 1 x 978 [612,324,996,225,418,583,514,169,99]
+ CRUSH rule 1 x 979 [843,457,675,650,958,657,677,173,903]
+ CRUSH rule 1 x 980 [60,914,881,626,850,759,398,943,764]
+ CRUSH rule 1 x 981 [702,749,937,153,724,514,536,212,247]
+ CRUSH rule 1 x 982 [298,928,738,167,99,668,395,198,100]
+ CRUSH rule 1 x 983 [723,572,395,358,900,37,927,597,103]
+ CRUSH rule 1 x 984 [723,864,804,935,846,993,950,840,427]
+ CRUSH rule 1 x 985 [945,459,868,211,524,954,911,208,91]
+ CRUSH rule 1 x 986 [772,664,535,169,297,996,864,555,687]
+ CRUSH rule 1 x 987 [88,324,312,843,661,580,76,894,480]
+ CRUSH rule 1 x 988 [522,927,131,996,351,685,865,47,116]
+ CRUSH rule 1 x 989 [578,332,208,605,975,207,155,380,797]
+ CRUSH rule 1 x 990 [638,228,414,311,738,698,340,526,728]
+ CRUSH rule 1 x 991 [530,221,451,422,879,916,754,928,288]
+ CRUSH rule 1 x 992 [925,705,275,81,234,310,117,546,798]
+ CRUSH rule 1 x 993 [991,301,43,469,830,242,382,428,451]
+ CRUSH rule 1 x 994 [276,51,868,683,843,815,557,378,936]
+ CRUSH rule 1 x 995 [288,836,753,790,758,120,158,265,110]
+ CRUSH rule 1 x 996 [887,983,252,686,470,345,459,764,859]
+ CRUSH rule 1 x 997 [110,924,386,79,705,697,210,698,273]
+ CRUSH rule 1 x 998 [435,830,485,853,926,730,786,762,444]
+ CRUSH rule 1 x 999 [876,738,357,913,723,51,15,585,898]
+ CRUSH rule 1 x 1000 [178,963,638,430,845,586,317,102,200]
+ CRUSH rule 1 x 1001 [99,519,66,759,583,944,739,922,343]
+ CRUSH rule 1 x 1002 [515,534,468,866,878,717,729,370,326]
+ CRUSH rule 1 x 1003 [104,611,937,698,94,67,614,783,865]
+ CRUSH rule 1 x 1004 [269,638,724,375,491,121,891,113,424]
+ CRUSH rule 1 x 1005 [369,223,309,409,822,39,597,969,911]
+ CRUSH rule 1 x 1006 [40,107,69,275,79,429,234,945,598]
+ CRUSH rule 1 x 1007 [978,111,416,758,454,640,5,444,795]
+ CRUSH rule 1 x 1008 [965,956,624,832,421,96,975,723,909]
+ CRUSH rule 1 x 1009 [598,476,356,695,919,566,234,383,604]
+ CRUSH rule 1 x 1010 [767,523,239,517,29,77,23,241,838]
+ CRUSH rule 1 x 1011 [289,871,207,576,347,698,48,570,639]
+ CRUSH rule 1 x 1012 [128,28,370,31,341,755,268,647,669]
+ CRUSH rule 1 x 1013 [979,765,660,812,666,187,808,351,572]
+ CRUSH rule 1 x 1014 [979,948,513,88,47,825,969,81,586]
+ CRUSH rule 1 x 1015 [277,790,396,672,542,647,145,11,965]
+ CRUSH rule 1 x 1016 [262,73,128,886,839,685,456,560,935]
+ CRUSH rule 1 x 1017 [150,269,61,499,832,591,637,731,738]
+ CRUSH rule 1 x 1018 [555,829,554,944,406,576,463,926,475]
+ CRUSH rule 1 x 1019 [513,356,265,446,65,288,768,245,337]
+ CRUSH rule 1 x 1020 [158,161,877,704,948,570,495,865,698]
+ CRUSH rule 1 x 1021 [915,998,957,285,546,202,676,322,671]
+ CRUSH rule 1 x 1022 [967,829,973,640,703,470,871,828,440]
+ CRUSH rule 1 x 1023 [488,257,614,859,325,419,50,560,595]
+ rule 1 (metadata) num_rep 9 result size == 9:\t1024/1024 (esc)
+ CRUSH rule 1 x 0 [36,705,536,450,604,380,966,750,695,503]
+ CRUSH rule 1 x 1 [876,250,334,633,744,843,672,820,782,802]
+ CRUSH rule 1 x 2 [292,832,53,392,386,787,527,901,106,273]
+ CRUSH rule 1 x 3 [623,387,124,998,749,211,481,169,816,732]
+ CRUSH rule 1 x 4 [61,334,710,4,994,982,847,220,87,254]
+ CRUSH rule 1 x 5 [946,557,713,664,141,817,964,872,66,161]
+ CRUSH rule 1 x 6 [576,668,212,163,732,381,884,726,456,796]
+ CRUSH rule 1 x 7 [645,753,906,393,341,44,578,14,543,287]
+ CRUSH rule 1 x 8 [243,6,863,781,211,100,462,207,759,701]
+ CRUSH rule 1 x 9 [22,578,251,410,297,430,3,569,603,47]
+ CRUSH rule 1 x 10 [758,828,360,477,821,801,811,484,296,320]
+ CRUSH rule 1 x 11 [769,120,124,527,119,504,380,821,470,230]
+ CRUSH rule 1 x 12 [780,364,689,755,675,199,117,393,435,514]
+ CRUSH rule 1 x 13 [557,18,351,719,742,780,78,170,333,295]
+ CRUSH rule 1 x 14 [59,561,249,461,971,835,855,76,269,673]
+ CRUSH rule 1 x 15 [718,928,993,21,76,313,437,797,680,761]
+ CRUSH rule 1 x 16 [673,632,841,954,788,90,786,969,378,246]
+ CRUSH rule 1 x 17 [648,43,560,514,142,289,935,605,228,737]
+ CRUSH rule 1 x 18 [654,219,181,568,381,253,883,394,188,459]
+ CRUSH rule 1 x 19 [850,545,377,848,863,543,51,834,690,375]
+ CRUSH rule 1 x 20 [717,785,974,5,225,552,975,636,387,600]
+ CRUSH rule 1 x 21 [420,57,519,306,312,983,263,267,128,828]
+ CRUSH rule 1 x 22 [503,998,193,821,634,684,557,633,812,521]
+ CRUSH rule 1 x 23 [411,663,168,110,899,488,477,468,303,367]
+ CRUSH rule 1 x 24 [266,861,353,1,456,128,800,309,622,673]
+ CRUSH rule 1 x 25 [760,483,818,600,509,951,248,908,624,643]
+ CRUSH rule 1 x 26 [903,24,573,718,112,694,501,909,219,686]
+ CRUSH rule 1 x 27 [946,188,289,510,687,827,676,560,753,77]
+ CRUSH rule 1 x 28 [69,312,73,198,256,629,770,569,359,733]
+ CRUSH rule 1 x 29 [844,883,337,628,496,405,719,581,816,349]
+ CRUSH rule 1 x 30 [621,18,613,794,910,936,426,522,208,699]
+ CRUSH rule 1 x 31 [784,943,814,539,962,392,813,217,750,155]
+ CRUSH rule 1 x 32 [173,374,369,972,315,83,428,63,801,735]
+ CRUSH rule 1 x 33 [698,336,357,966,582,407,618,288,846,659]
+ CRUSH rule 1 x 34 [168,836,210,798,904,190,663,877,177,567]
+ CRUSH rule 1 x 35 [274,509,534,818,912,671,75,580,568,523]
+ CRUSH rule 1 x 36 [318,215,153,628,87,407,676,524,510,480]
+ CRUSH rule 1 x 37 [173,604,109,935,203,401,311,758,201,999]
+ CRUSH rule 1 x 38 [708,444,683,604,722,900,929,910,698,386]
+ CRUSH rule 1 x 39 [662,198,417,680,226,342,856,248,279,416]
+ CRUSH rule 1 x 40 [620,801,414,78,560,766,980,503,287,564]
+ CRUSH rule 1 x 41 [811,264,177,127,148,791,930,74,844,943]
+ CRUSH rule 1 x 42 [863,179,527,660,133,529,456,713,348,311]
+ CRUSH rule 1 x 43 [686,822,988,228,791,549,514,40,261,223]
+ CRUSH rule 1 x 44 [396,222,46,841,536,140,160,527,250,247]
+ CRUSH rule 1 x 45 [991,694,253,142,54,422,658,876,201,45]
+ CRUSH rule 1 x 46 [420,909,184,285,508,458,45,390,546,908]
+ CRUSH rule 1 x 47 [467,211,605,207,241,881,959,800,743,161]
+ CRUSH rule 1 x 48 [955,329,368,168,698,787,738,47,812,166]
+ CRUSH rule 1 x 49 [974,891,931,29,813,506,822,628,696,407]
+ CRUSH rule 1 x 50 [870,441,691,823,761,6,83,344,713,857]
+ CRUSH rule 1 x 51 [182,930,25,936,97,260,406,281,991,336]
+ CRUSH rule 1 x 52 [704,812,894,794,481,37,304,899,629,701]
+ CRUSH rule 1 x 53 [185,713,631,280,345,558,882,503,327,402]
+ CRUSH rule 1 x 54 [270,441,100,82,983,930,339,902,81,239]
+ CRUSH rule 1 x 55 [895,734,958,793,651,572,508,763,108,185]
+ CRUSH rule 1 x 56 [564,963,683,324,40,189,77,500,553,417]
+ CRUSH rule 1 x 57 [738,130,208,973,498,861,670,67,114,685]
+ CRUSH rule 1 x 58 [524,113,806,903,531,334,8,762,842,884]
+ CRUSH rule 1 x 59 [408,337,668,529,34,384,643,511,370,336]
+ CRUSH rule 1 x 60 [228,790,857,309,616,895,194,277,985,554]
+ CRUSH rule 1 x 61 [154,843,717,467,883,536,812,14,55,752]
+ CRUSH rule 1 x 62 [594,811,549,276,693,917,45,723,926,180]
+ CRUSH rule 1 x 63 [646,67,884,925,941,434,705,268,140,942]
+ CRUSH rule 1 x 64 [175,542,155,837,594,197,451,891,654,294]
+ CRUSH rule 1 x 65 [745,619,131,867,269,62,862,221,66,354]
+ CRUSH rule 1 x 66 [275,468,23,35,328,432,334,656,719,810]
+ CRUSH rule 1 x 67 [246,958,524,493,636,227,783,593,814,970]
+ CRUSH rule 1 x 68 [711,473,403,228,835,126,705,114,981,267]
+ CRUSH rule 1 x 69 [493,924,850,939,950,105,871,361,533,433]
+ CRUSH rule 1 x 70 [30,499,644,33,804,654,684,411,114,42]
+ CRUSH rule 1 x 71 [984,883,574,716,575,391,587,264,446,572]
+ CRUSH rule 1 x 72 [71,286,942,363,628,632,642,529,966,919]
+ CRUSH rule 1 x 73 [922,618,3,371,464,442,835,705,61,745]
+ CRUSH rule 1 x 74 [629,414,185,573,678,338,633,560,565,410]
+ CRUSH rule 1 x 75 [222,20,174,820,312,361,366,258,711,657]
+ CRUSH rule 1 x 76 [262,366,339,290,718,143,735,953,188,516]
+ CRUSH rule 1 x 77 [638,469,992,280,773,892,197,690,426,681]
+ CRUSH rule 1 x 78 [324,511,788,7,308,228,183,917,464,518]
+ CRUSH rule 1 x 79 [577,990,64,94,447,924,339,24,581,969]
+ CRUSH rule 1 x 80 [501,95,278,903,631,842,51,766,822,687]
+ CRUSH rule 1 x 81 [506,812,9,698,173,664,247,963,0,732]
+ CRUSH rule 1 x 82 [222,145,80,785,835,745,580,51,939,278]
+ CRUSH rule 1 x 83 [71,634,61,91,856,529,66,197,698,318]
+ CRUSH rule 1 x 84 [49,761,773,368,318,708,681,618,723,516]
+ CRUSH rule 1 x 85 [985,896,708,861,325,307,567,908,514,355]
+ CRUSH rule 1 x 86 [537,745,93,524,466,356,38,326,385,899]
+ CRUSH rule 1 x 87 [997,317,463,626,685,845,909,49,28,698]
+ CRUSH rule 1 x 88 [957,350,890,857,375,176,99,737,942,647]
+ CRUSH rule 1 x 89 [399,730,148,314,159,982,320,921,812,908]
+ CRUSH rule 1 x 90 [943,706,683,267,579,141,412,184,529,127]
+ CRUSH rule 1 x 91 [22,368,149,928,140,529,495,299,812,743]
+ CRUSH rule 1 x 92 [532,424,426,773,623,197,167,634,781,242]
+ CRUSH rule 1 x 93 [218,489,405,681,549,201,343,949,51,732]
+ CRUSH rule 1 x 94 [181,96,102,515,776,365,82,422,738,933]
+ CRUSH rule 1 x 95 [343,957,820,139,334,37,648,661,46,112]
+ CRUSH rule 1 x 96 [861,270,87,797,0,245,204,750,322,75]
+ CRUSH rule 1 x 97 [459,706,45,328,274,605,83,542,131,240]
+ CRUSH rule 1 x 98 [327,867,353,948,728,280,270,511,586,230]
+ CRUSH rule 1 x 99 [974,133,468,906,235,988,37,138,326,603]
+ CRUSH rule 1 x 100 [32,445,547,371,960,885,9,168,590,873]
+ CRUSH rule 1 x 101 [142,90,337,950,970,570,12,369,9,872]
+ CRUSH rule 1 x 102 [172,129,139,22,403,867,923,106,653,999]
+ CRUSH rule 1 x 103 [630,47,161,356,911,421,933,231,520,303]
+ CRUSH rule 1 x 104 [758,133,278,11,947,799,401,85,139,855]
+ CRUSH rule 1 x 105 [843,604,47,33,401,632,434,121,488,644]
+ CRUSH rule 1 x 106 [28,681,193,679,990,343,878,493,550,484]
+ CRUSH rule 1 x 107 [74,320,85,819,315,253,589,614,814,970]
+ CRUSH rule 1 x 108 [875,593,575,517,107,153,631,996,630,597]
+ CRUSH rule 1 x 109 [411,985,811,720,198,666,856,296,122,477]
+ CRUSH rule 1 x 110 [440,774,799,660,715,167,510,472,270,753]
+ CRUSH rule 1 x 111 [405,742,276,359,936,360,18,949,341,837]
+ CRUSH rule 1 x 112 [143,181,922,545,185,303,725,413,187,840]
+ CRUSH rule 1 x 113 [153,846,160,903,789,897,738,253,213,541]
+ CRUSH rule 1 x 114 [804,892,939,20,312,692,598,418,641,891]
+ CRUSH rule 1 x 115 [588,508,958,580,232,722,421,39,241,881]
+ CRUSH rule 1 x 116 [327,148,637,486,712,464,9,448,816,609]
+ CRUSH rule 1 x 117 [95,594,989,131,714,275,725,142,304,591]
+ CRUSH rule 1 x 118 [80,957,897,239,359,432,766,210,528,252]
+ CRUSH rule 1 x 119 [386,932,951,768,679,300,570,278,867,489]
+ CRUSH rule 1 x 120 [366,312,653,936,71,241,49,126,410,33]
+ CRUSH rule 1 x 121 [129,154,847,16,471,481,424,868,469,183]
+ CRUSH rule 1 x 122 [873,1,110,939,90,412,551,43,590,51]
+ CRUSH rule 1 x 123 [533,415,789,600,713,800,877,248,753,395]
+ CRUSH rule 1 x 124 [461,691,898,723,957,759,482,254,158,641]
+ CRUSH rule 1 x 125 [342,599,830,402,615,994,736,737,508,150]
+ CRUSH rule 1 x 126 [819,781,822,548,279,255,689,209,99,479]
+ CRUSH rule 1 x 127 [437,893,585,707,353,189,909,809,553,785]
+ CRUSH rule 1 x 128 [679,994,982,550,991,324,666,691,899,665]
+ CRUSH rule 1 x 129 [380,685,947,302,698,144,149,983,904,70]
+ CRUSH rule 1 x 130 [992,52,466,867,998,777,270,425,864,38]
+ CRUSH rule 1 x 131 [469,90,208,599,829,656,203,667,528,387]
+ CRUSH rule 1 x 132 [571,250,316,535,54,418,922,597,680,25]
+ CRUSH rule 1 x 133 [964,728,329,902,108,118,14,444,709,592]
+ CRUSH rule 1 x 134 [999,19,716,963,323,559,893,281,226,739]
+ CRUSH rule 1 x 135 [634,101,52,938,413,573,712,649,27,274]
+ CRUSH rule 1 x 136 [114,889,692,768,694,279,846,890,151,872]
+ CRUSH rule 1 x 137 [839,8,959,280,922,870,363,323,153,238]
+ CRUSH rule 1 x 138 [967,949,138,451,292,548,400,885,907,214]
+ CRUSH rule 1 x 139 [308,711,736,247,632,126,384,58,373,121]
+ CRUSH rule 1 x 140 [764,936,926,55,331,115,178,532,883,380]
+ CRUSH rule 1 x 141 [423,302,112,216,603,873,193,258,445,451]
+ CRUSH rule 1 x 142 [252,821,715,340,635,668,424,515,751,746]
+ CRUSH rule 1 x 143 [33,808,518,477,325,316,266,70,210,61]
+ CRUSH rule 1 x 144 [472,88,969,162,401,771,697,610,203,382]
+ CRUSH rule 1 x 145 [242,208,252,604,266,743,577,348,1,323]
+ CRUSH rule 1 x 146 [290,70,570,384,934,856,929,196,880,458]
+ CRUSH rule 1 x 147 [447,352,657,493,467,918,514,546,861,796]
+ CRUSH rule 1 x 148 [212,644,432,658,109,275,352,820,857,282]
+ CRUSH rule 1 x 149 [9,775,87,35,260,646,406,556,532,750]
+ CRUSH rule 1 x 150 [166,456,582,144,324,340,484,553,315,504]
+ CRUSH rule 1 x 151 [811,875,307,20,782,229,671,883,204,12]
+ CRUSH rule 1 x 152 [449,617,223,9,182,407,807,50,206,368]
+ CRUSH rule 1 x 153 [523,537,695,627,959,613,942,864,388,639]
+ CRUSH rule 1 x 154 [208,559,874,597,243,706,443,98,27,120]
+ CRUSH rule 1 x 155 [569,325,192,296,367,848,58,641,186,553]
+ CRUSH rule 1 x 156 [488,121,521,213,595,837,271,229,961,163]
+ CRUSH rule 1 x 157 [140,723,633,260,487,856,384,446,836,917]
+ CRUSH rule 1 x 158 [786,451,320,239,667,632,899,902,956,424]
+ CRUSH rule 1 x 159 [134,664,517,821,667,944,209,641,228,213]
+ CRUSH rule 1 x 160 [690,112,414,990,183,590,242,999,974,652]
+ CRUSH rule 1 x 161 [324,912,397,423,991,284,909,642,188,143]
+ CRUSH rule 1 x 162 [748,567,284,183,463,336,148,88,764,40]
+ CRUSH rule 1 x 163 [575,499,31,816,749,737,587,854,482,522]
+ CRUSH rule 1 x 164 [314,489,308,326,51,568,110,329,361,743]
+ CRUSH rule 1 x 165 [116,209,750,53,813,640,524,389,185,893]
+ CRUSH rule 1 x 166 [352,706,701,810,718,527,548,676,448,991]
+ CRUSH rule 1 x 167 [27,743,174,142,551,1,935,266,883,77]
+ CRUSH rule 1 x 168 [953,898,880,660,500,799,667,463,818,819]
+ CRUSH rule 1 x 169 [912,147,266,547,331,770,601,909,60,255]
+ CRUSH rule 1 x 170 [421,515,828,844,151,981,835,840,548,588]
+ CRUSH rule 1 x 171 [488,584,880,964,936,196,100,910,446,541]
+ CRUSH rule 1 x 172 [366,443,957,66,162,693,36,356,274,976]
+ CRUSH rule 1 x 173 [863,291,625,287,158,496,471,529,359,571]
+ CRUSH rule 1 x 174 [263,555,650,410,339,616,780,932,573,814]
+ CRUSH rule 1 x 175 [875,961,361,575,33,109,51,211,409,865]
+ CRUSH rule 1 x 176 [745,83,701,680,250,420,240,316,337,361]
+ CRUSH rule 1 x 177 [128,244,41,123,422,902,756,647,45,752]
+ CRUSH rule 1 x 178 [155,41,264,777,314,564,856,992,696,784]
+ CRUSH rule 1 x 179 [593,833,202,183,971,38,724,923,450,340]
+ CRUSH rule 1 x 180 [154,734,17,831,824,522,736,846,926,129]
+ CRUSH rule 1 x 181 [289,675,723,800,166,712,168,224,705,185]
+ CRUSH rule 1 x 182 [730,931,560,209,943,261,485,571,796,587]
+ CRUSH rule 1 x 183 [639,237,794,815,827,400,109,903,96,526]
+ CRUSH rule 1 x 184 [704,312,685,645,691,778,74,45,438,26]
+ CRUSH rule 1 x 185 [97,100,762,82,999,542,485,511,14,329]
+ CRUSH rule 1 x 186 [26,665,554,215,280,421,369,270,16,920]
+ CRUSH rule 1 x 187 [649,14,740,494,402,684,566,378,816,553]
+ CRUSH rule 1 x 188 [682,695,590,743,927,945,833,650,761,468]
+ CRUSH rule 1 x 189 [325,693,726,51,448,169,37,1,939,463]
+ CRUSH rule 1 x 190 [399,933,136,955,57,504,527,237,295,716]
+ CRUSH rule 1 x 191 [629,533,17,126,60,146,999,754,339,271]
+ CRUSH rule 1 x 192 [503,578,38,492,222,251,123,759,147,99]
+ CRUSH rule 1 x 193 [546,333,651,678,823,652,359,721,996,318]
+ CRUSH rule 1 x 194 [242,473,58,655,907,277,792,887,561,449]
+ CRUSH rule 1 x 195 [625,719,135,81,636,513,755,471,658,744]
+ CRUSH rule 1 x 196 [357,114,125,867,250,522,413,834,832,368]
+ CRUSH rule 1 x 197 [306,954,453,873,211,334,666,316,243,320]
+ CRUSH rule 1 x 198 [863,791,311,911,206,61,355,574,781,550]
+ CRUSH rule 1 x 199 [935,906,929,252,893,75,960,369,584,612]
+ CRUSH rule 1 x 200 [373,774,229,454,909,611,132,271,128,632]
+ CRUSH rule 1 x 201 [659,320,477,313,779,16,495,76,598,301]
+ CRUSH rule 1 x 202 [260,433,524,880,223,818,153,272,944,741]
+ CRUSH rule 1 x 203 [36,239,675,971,703,209,669,676,762,200]
+ CRUSH rule 1 x 204 [92,516,993,728,279,478,697,881,64,107]
+ CRUSH rule 1 x 205 [68,395,473,45,683,662,776,463,327,721]
+ CRUSH rule 1 x 206 [570,530,642,380,311,398,230,367,890,953]
+ CRUSH rule 1 x 207 [834,457,850,917,456,296,76,708,101,928]
+ CRUSH rule 1 x 208 [927,484,640,976,803,626,96,841,811,979]
+ CRUSH rule 1 x 209 [878,66,58,940,48,233,522,185,949,590]
+ CRUSH rule 1 x 210 [572,981,484,29,0,426,14,921,544,334]
+ CRUSH rule 1 x 211 [107,597,780,857,895,57,922,372,581,629]
+ CRUSH rule 1 x 212 [389,107,838,624,698,562,857,894,60,426]
+ CRUSH rule 1 x 213 [497,717,567,728,905,134,687,903,620,572]
+ CRUSH rule 1 x 214 [798,65,254,572,32,393,579,79,258,42]
+ CRUSH rule 1 x 215 [233,419,283,638,520,891,982,826,488,314]
+ CRUSH rule 1 x 216 [494,464,742,523,459,174,973,898,556,293]
+ CRUSH rule 1 x 217 [352,396,309,938,66,41,264,6,603,317]
+ CRUSH rule 1 x 218 [895,864,988,650,593,740,34,497,108,180]
+ CRUSH rule 1 x 219 [222,534,277,242,658,482,697,805,976,758]
+ CRUSH rule 1 x 220 [281,19,584,563,858,965,686,982,0,32]
+ CRUSH rule 1 x 221 [64,928,963,130,312,394,61,559,846,994]
+ CRUSH rule 1 x 222 [40,544,161,199,861,644,597,904,897,376]
+ CRUSH rule 1 x 223 [645,556,159,417,46,135,465,429,614,711]
+ CRUSH rule 1 x 224 [647,165,957,263,961,576,329,320,645,829]
+ CRUSH rule 1 x 225 [219,714,858,747,461,175,606,465,354,404]
+ CRUSH rule 1 x 226 [372,511,181,277,695,404,876,984,491,784]
+ CRUSH rule 1 x 227 [925,156,714,863,257,74,966,217,501,536]
+ CRUSH rule 1 x 228 [682,404,839,263,521,195,261,389,281,467]
+ CRUSH rule 1 x 229 [880,838,770,891,236,542,262,884,215,687]
+ CRUSH rule 1 x 230 [328,659,916,468,646,572,93,880,959,111]
+ CRUSH rule 1 x 231 [320,383,669,109,627,621,50,182,541,483]
+ CRUSH rule 1 x 232 [924,846,394,319,43,519,106,877,130,387]
+ CRUSH rule 1 x 233 [948,652,575,838,498,395,796,835,714,751]
+ CRUSH rule 1 x 234 [484,943,42,575,936,180,103,95,634,844]
+ CRUSH rule 1 x 235 [750,65,590,168,870,308,471,753,350,224]
+ CRUSH rule 1 x 236 [551,787,490,136,370,833,573,128,154,326]
+ CRUSH rule 1 x 237 [390,157,166,251,752,75,327,509,325,245]
+ CRUSH rule 1 x 238 [570,6,989,707,514,905,894,884,824,343]
+ CRUSH rule 1 x 239 [729,959,376,975,496,49,426,427,736,836]
+ CRUSH rule 1 x 240 [981,241,156,767,631,576,450,677,659,183]
+ CRUSH rule 1 x 241 [310,816,641,177,996,454,413,136,411,549]
+ CRUSH rule 1 x 242 [161,63,642,837,763,458,234,756,496,779]
+ CRUSH rule 1 x 243 [180,394,33,683,189,419,799,21,13,874]
+ CRUSH rule 1 x 244 [52,174,685,189,78,310,785,107,816,89]
+ CRUSH rule 1 x 245 [523,121,915,84,386,409,605,837,1,141]
+ CRUSH rule 1 x 246 [362,893,390,487,817,88,989,999,138,674]
+ CRUSH rule 1 x 247 [382,184,116,34,143,15,590,840,586,594]
+ CRUSH rule 1 x 248 [129,114,852,469,359,291,713,237,468,340]
+ CRUSH rule 1 x 249 [159,683,91,856,475,369,886,650,827,663]
+ CRUSH rule 1 x 250 [404,945,569,955,228,910,270,619,450,707]
+ CRUSH rule 1 x 251 [661,225,738,757,37,642,58,354,16,905]
+ CRUSH rule 1 x 252 [961,226,542,103,945,885,838,131,387,664]
+ CRUSH rule 1 x 253 [651,97,225,364,189,248,797,675,452,129]
+ CRUSH rule 1 x 254 [123,33,741,692,599,11,605,453,987,316]
+ CRUSH rule 1 x 255 [314,649,891,855,517,344,607,95,121,784]
+ CRUSH rule 1 x 256 [315,215,651,126,470,849,189,627,592,241]
+ CRUSH rule 1 x 257 [825,264,867,301,529,409,291,732,224,841]
+ CRUSH rule 1 x 258 [624,789,370,723,131,982,863,427,873,223]
+ CRUSH rule 1 x 259 [602,542,70,563,947,723,77,191,669,61]
+ CRUSH rule 1 x 260 [717,878,43,56,377,481,533,646,475,686]
+ CRUSH rule 1 x 261 [145,517,20,903,786,939,516,136,87,410]
+ CRUSH rule 1 x 262 [223,1,561,420,805,16,88,534,289,498]
+ CRUSH rule 1 x 263 [462,211,405,508,787,669,773,979,719,421]
+ CRUSH rule 1 x 264 [654,471,266,662,135,564,715,916,633,121]
+ CRUSH rule 1 x 265 [302,794,704,798,659,487,833,987,445,23]
+ CRUSH rule 1 x 266 [202,132,884,209,551,984,7,557,76,987]
+ CRUSH rule 1 x 267 [282,938,657,113,672,993,972,645,882,451]
+ CRUSH rule 1 x 268 [338,309,356,278,928,797,715,536,983,688]
+ CRUSH rule 1 x 269 [738,122,266,200,894,118,146,14,414,236]
+ CRUSH rule 1 x 270 [707,982,946,196,407,804,476,571,314,538]
+ CRUSH rule 1 x 271 [705,432,364,735,512,595,263,138,526,607]
+ CRUSH rule 1 x 272 [756,545,942,56,542,449,710,779,161,222]
+ CRUSH rule 1 x 273 [197,502,527,721,239,648,982,735,58,1]
+ CRUSH rule 1 x 274 [992,44,653,573,527,702,370,990,320,52]
+ CRUSH rule 1 x 275 [544,789,170,434,23,926,992,823,321,784]
+ CRUSH rule 1 x 276 [658,467,577,268,336,5,634,98,457,487]
+ CRUSH rule 1 x 277 [143,490,880,483,928,272,783,648,927,285]
+ CRUSH rule 1 x 278 [492,647,355,282,834,64,350,600,283,422]
+ CRUSH rule 1 x 279 [517,792,604,987,527,894,952,250,206,714]
+ CRUSH rule 1 x 280 [825,740,27,848,514,750,895,914,892,149]
+ CRUSH rule 1 x 281 [224,629,120,562,616,200,443,604,52,638]
+ CRUSH rule 1 x 282 [298,661,380,416,35,585,939,879,338,786]
+ CRUSH rule 1 x 283 [311,606,208,50,913,678,369,544,721,267]
+ CRUSH rule 1 x 284 [771,466,371,743,672,119,60,546,39,71]
+ CRUSH rule 1 x 285 [693,362,404,676,797,531,582,975,810,703]
+ CRUSH rule 1 x 286 [364,477,285,167,270,617,699,627,725,389]
+ CRUSH rule 1 x 287 [591,611,828,995,170,987,137,890,487,621]
+ CRUSH rule 1 x 288 [965,541,848,796,251,668,195,538,356,523]
+ CRUSH rule 1 x 289 [225,551,948,877,219,167,795,377,825,874]
+ CRUSH rule 1 x 290 [577,762,777,751,291,349,473,209,59,346]
+ CRUSH rule 1 x 291 [160,903,477,381,490,559,557,86,89,417]
+ CRUSH rule 1 x 292 [873,598,216,666,222,228,806,911,738,969]
+ CRUSH rule 1 x 293 [100,234,874,47,28,452,775,636,232,518]
+ CRUSH rule 1 x 294 [285,943,379,520,725,547,459,833,503,207]
+ CRUSH rule 1 x 295 [938,262,880,327,687,3,440,73,29,38]
+ CRUSH rule 1 x 296 [850,327,86,472,1,776,266,82,671,320]
+ CRUSH rule 1 x 297 [951,53,99,558,753,228,232,343,831,540]
+ CRUSH rule 1 x 298 [173,336,85,766,910,657,213,286,61,961]
+ CRUSH rule 1 x 299 [598,591,315,386,895,296,924,106,63,457]
+ CRUSH rule 1 x 300 [531,957,62,459,156,538,904,838,458,828]
+ CRUSH rule 1 x 301 [823,628,23,858,629,808,220,432,393,433]
+ CRUSH rule 1 x 302 [184,80,780,871,531,211,400,365,697,497]
+ CRUSH rule 1 x 303 [521,766,222,830,988,275,561,905,522,342]
+ CRUSH rule 1 x 304 [980,127,807,507,555,245,214,944,845,895]
+ CRUSH rule 1 x 305 [153,816,22,927,696,911,685,838,3,983]
+ CRUSH rule 1 x 306 [423,739,664,753,178,431,761,648,867,488]
+ CRUSH rule 1 x 307 [997,557,682,456,479,631,459,250,415,194]
+ CRUSH rule 1 x 308 [991,874,534,465,330,284,976,551,126,307]
+ CRUSH rule 1 x 309 [860,394,724,858,246,866,857,153,970,99]
+ CRUSH rule 1 x 310 [589,818,546,201,94,653,90,855,441,736]
+ CRUSH rule 1 x 311 [477,774,225,590,830,559,256,798,743,645]
+ CRUSH rule 1 x 312 [887,853,950,354,58,23,497,929,92,639]
+ CRUSH rule 1 x 313 [802,646,447,416,557,118,24,81,215,850]
+ CRUSH rule 1 x 314 [654,974,229,511,562,916,952,599,201,763]
+ CRUSH rule 1 x 315 [767,227,28,740,828,156,749,841,969,314]
+ CRUSH rule 1 x 316 [778,83,733,359,858,319,761,725,923,461]
+ CRUSH rule 1 x 317 [184,418,642,986,939,675,892,86,214,189]
+ CRUSH rule 1 x 318 [525,410,500,543,212,95,290,97,529,220]
+ CRUSH rule 1 x 319 [476,724,569,382,409,521,800,868,364,427]
+ CRUSH rule 1 x 320 [149,610,697,296,818,955,523,366,891,998]
+ CRUSH rule 1 x 321 [710,79,667,671,234,4,868,841,563,961]
+ CRUSH rule 1 x 322 [175,275,323,333,744,718,187,380,947,952]
+ CRUSH rule 1 x 323 [819,604,638,792,316,544,236,307,969,232]
+ CRUSH rule 1 x 324 [16,745,511,439,272,932,668,959,845,759]
+ CRUSH rule 1 x 325 [486,400,872,873,251,68,462,268,124,431]
+ CRUSH rule 1 x 326 [613,765,207,19,359,370,461,509,75,767]
+ CRUSH rule 1 x 327 [125,289,738,408,456,784,750,669,296,314]
+ CRUSH rule 1 x 328 [807,383,476,583,645,141,33,806,181,597]
+ CRUSH rule 1 x 329 [588,938,599,432,446,840,516,713,223,395]
+ CRUSH rule 1 x 330 [932,644,41,611,209,406,420,520,395,665]
+ CRUSH rule 1 x 331 [341,953,950,537,578,862,624,649,626,928]
+ CRUSH rule 1 x 332 [153,726,459,950,466,804,644,821,238,85]
+ CRUSH rule 1 x 333 [745,845,853,860,52,615,243,633,309,616]
+ CRUSH rule 1 x 334 [614,751,807,58,396,159,408,175,189,500]
+ CRUSH rule 1 x 335 [518,721,221,283,454,187,635,367,997,819]
+ CRUSH rule 1 x 336 [389,424,77,309,5,898,698,533,683,851]
+ CRUSH rule 1 x 337 [753,508,765,720,221,807,956,907,464,39]
+ CRUSH rule 1 x 338 [128,810,490,753,406,760,69,11,624,272]
+ CRUSH rule 1 x 339 [430,308,58,751,856,823,607,953,125,899]
+ CRUSH rule 1 x 340 [541,44,630,231,289,966,707,328,325,81]
+ CRUSH rule 1 x 341 [402,26,631,439,165,928,720,503,209,748]
+ CRUSH rule 1 x 342 [982,57,992,461,131,32,516,661,985,860]
+ CRUSH rule 1 x 343 [833,412,572,732,107,805,660,655,149,994]
+ CRUSH rule 1 x 344 [784,533,792,41,642,869,142,114,108,961]
+ CRUSH rule 1 x 345 [546,300,304,691,763,556,127,732,290,494]
+ CRUSH rule 1 x 346 [302,420,428,891,357,124,419,962,304,12]
+ CRUSH rule 1 x 347 [488,778,101,217,366,442,783,661,622,426]
+ CRUSH rule 1 x 348 [903,744,937,718,85,314,862,513,112,334]
+ CRUSH rule 1 x 349 [471,547,582,306,600,486,795,143,529,765]
+ CRUSH rule 1 x 350 [348,221,823,335,383,708,841,164,765,563]
+ CRUSH rule 1 x 351 [961,582,705,346,361,32,766,775,518,155]
+ CRUSH rule 1 x 352 [728,137,461,298,36,903,899,665,802,620]
+ CRUSH rule 1 x 353 [904,202,184,447,58,294,279,616,892,262]
+ CRUSH rule 1 x 354 [345,226,319,256,544,311,612,33,122,190]
+ CRUSH rule 1 x 355 [50,430,175,43,187,458,985,412,599,375]
+ CRUSH rule 1 x 356 [87,185,55,423,829,1,629,228,150,889]
+ CRUSH rule 1 x 357 [762,459,921,473,182,231,891,656,196,232]
+ CRUSH rule 1 x 358 [908,25,280,6,808,676,874,643,550,633]
+ CRUSH rule 1 x 359 [484,15,132,121,394,423,397,52,702,981]
+ CRUSH rule 1 x 360 [173,378,337,702,145,499,29,529,156,595]
+ CRUSH rule 1 x 361 [404,577,115,25,56,914,643,286,552,985]
+ CRUSH rule 1 x 362 [403,1,422,945,132,685,265,35,662,708]
+ CRUSH rule 1 x 363 [639,911,510,162,418,294,444,613,466,499]
+ CRUSH rule 1 x 364 [752,689,610,990,665,222,203,17,743,570]
+ CRUSH rule 1 x 365 [956,999,212,230,624,84,113,373,426,941]
+ CRUSH rule 1 x 366 [860,925,924,763,687,851,59,914,521,629]
+ CRUSH rule 1 x 367 [205,609,647,665,969,720,685,641,894,813]
+ CRUSH rule 1 x 368 [301,284,810,169,78,340,616,93,283,353]
+ CRUSH rule 1 x 369 [452,658,339,217,674,210,284,184,718,684]
+ CRUSH rule 1 x 370 [11,467,695,989,394,576,850,419,307,965]
+ CRUSH rule 1 x 371 [124,487,55,514,313,411,797,547,778,958]
+ CRUSH rule 1 x 372 [253,48,979,846,207,631,212,241,346,153]
+ CRUSH rule 1 x 373 [715,605,775,748,227,493,128,207,88,641]
+ CRUSH rule 1 x 374 [191,887,920,340,223,714,961,760,571,549]
+ CRUSH rule 1 x 375 [711,385,651,665,15,71,934,619,527,735]
+ CRUSH rule 1 x 376 [597,818,49,458,415,755,446,897,460,869]
+ CRUSH rule 1 x 377 [294,256,933,771,184,861,654,487,891,733]
+ CRUSH rule 1 x 378 [34,151,681,707,552,127,728,860,968,475]
+ CRUSH rule 1 x 379 [869,136,315,378,813,153,115,557,165,292]
+ CRUSH rule 1 x 380 [294,97,575,791,690,482,255,806,429,306]
+ CRUSH rule 1 x 381 [119,710,219,827,328,886,773,496,433,750]
+ CRUSH rule 1 x 382 [69,631,508,706,697,168,276,56,278,772]
+ CRUSH rule 1 x 383 [922,588,589,925,471,601,29,197,822,218]
+ CRUSH rule 1 x 384 [221,945,671,117,857,655,488,435,223,783]
+ CRUSH rule 1 x 385 [561,737,953,723,658,368,910,329,396,482]
+ CRUSH rule 1 x 386 [335,442,788,696,507,716,232,692,742,939]
+ CRUSH rule 1 x 387 [514,43,353,88,100,842,164,934,297,902]
+ CRUSH rule 1 x 388 [587,89,157,996,915,927,474,267,640,53]
+ CRUSH rule 1 x 389 [109,641,255,466,372,563,340,222,74,503]
+ CRUSH rule 1 x 390 [925,149,421,489,599,810,852,196,469,672]
+ CRUSH rule 1 x 391 [267,87,387,527,768,873,886,136,818,516]
+ CRUSH rule 1 x 392 [382,485,370,849,936,636,901,82,695,640]
+ CRUSH rule 1 x 393 [425,721,221,753,268,463,652,543,10,287]
+ CRUSH rule 1 x 394 [898,18,38,793,173,738,15,591,420,525]
+ CRUSH rule 1 x 395 [806,876,269,679,32,744,126,179,607,623]
+ CRUSH rule 1 x 396 [790,970,437,449,875,395,726,935,278,138]
+ CRUSH rule 1 x 397 [136,363,507,613,11,30,996,558,602,528]
+ CRUSH rule 1 x 398 [914,116,558,258,722,904,349,672,826,569]
+ CRUSH rule 1 x 399 [261,94,299,202,174,622,749,410,815,214]
+ CRUSH rule 1 x 400 [661,197,338,461,977,848,536,592,886,981]
+ CRUSH rule 1 x 401 [953,979,287,803,41,349,79,32,343,468]
+ CRUSH rule 1 x 402 [738,819,618,522,667,334,658,449,886,260]
+ CRUSH rule 1 x 403 [573,238,425,546,130,68,202,650,501,628]
+ CRUSH rule 1 x 404 [526,848,790,253,922,820,299,577,563,37]
+ CRUSH rule 1 x 405 [582,505,330,334,201,110,776,296,19,972]
+ CRUSH rule 1 x 406 [768,324,493,60,186,165,718,578,580,249]
+ CRUSH rule 1 x 407 [260,951,437,587,692,648,72,345,709,89]
+ CRUSH rule 1 x 408 [657,81,770,734,830,821,246,695,76,647]
+ CRUSH rule 1 x 409 [498,89,182,423,672,152,213,806,168,907]
+ CRUSH rule 1 x 410 [28,793,737,352,166,645,949,507,361,615]
+ CRUSH rule 1 x 411 [684,992,60,659,769,267,313,351,497,571]
+ CRUSH rule 1 x 412 [261,958,699,950,165,14,560,155,661,678]
+ CRUSH rule 1 x 413 [891,835,297,441,384,979,618,907,9,291]
+ CRUSH rule 1 x 414 [127,459,119,965,662,594,97,124,229,641]
+ CRUSH rule 1 x 415 [272,540,631,328,609,568,694,332,572,681]
+ CRUSH rule 1 x 416 [739,617,115,530,339,371,889,344,838,541]
+ CRUSH rule 1 x 417 [106,209,157,878,117,128,138,374,470,59]
+ CRUSH rule 1 x 418 [525,441,147,390,320,300,848,972,781,361]
+ CRUSH rule 1 x 419 [603,673,615,465,266,855,823,884,832,361]
+ CRUSH rule 1 x 420 [988,213,251,226,209,245,506,670,285,2]
+ CRUSH rule 1 x 421 [761,521,748,368,923,992,764,274,623,613]
+ CRUSH rule 1 x 422 [317,160,924,548,198,709,839,547,599,779]
+ CRUSH rule 1 x 423 [137,807,168,472,619,443,905,588,312,114]
+ CRUSH rule 1 x 424 [920,37,146,263,598,748,785,395,884,360]
+ CRUSH rule 1 x 425 [277,693,285,221,478,165,80,236,988,682]
+ CRUSH rule 1 x 426 [485,936,407,854,726,524,791,565,352,949]
+ CRUSH rule 1 x 427 [242,515,9,564,174,453,334,588,571,428]
+ CRUSH rule 1 x 428 [632,635,26,473,494,478,225,94,303,757]
+ CRUSH rule 1 x 429 [641,73,465,127,171,397,857,562,976,977]
+ CRUSH rule 1 x 430 [626,585,6,387,881,583,859,699,91,148]
+ CRUSH rule 1 x 431 [697,76,753,570,964,339,194,366,279,30]
+ CRUSH rule 1 x 432 [590,526,306,283,656,728,513,591,599,474]
+ CRUSH rule 1 x 433 [284,387,149,817,886,714,52,897,705,256]
+ CRUSH rule 1 x 434 [538,985,79,953,770,468,644,646,747,123]
+ CRUSH rule 1 x 435 [30,318,593,635,975,833,371,731,906,721]
+ CRUSH rule 1 x 436 [164,919,851,693,0,874,10,976,284,126]
+ CRUSH rule 1 x 437 [322,212,163,606,302,282,443,23,696,245]
+ CRUSH rule 1 x 438 [142,392,85,594,376,419,755,841,94,52]
+ CRUSH rule 1 x 439 [119,370,68,443,997,837,414,152,331,985]
+ CRUSH rule 1 x 440 [333,403,187,863,475,844,800,174,117,518]
+ CRUSH rule 1 x 441 [477,727,906,145,429,91,205,236,86,929]
+ CRUSH rule 1 x 442 [274,590,933,244,434,49,864,799,762,611]
+ CRUSH rule 1 x 443 [983,748,574,718,700,442,774,350,37,929]
+ CRUSH rule 1 x 444 [536,509,431,146,170,149,182,145,347,172]
+ CRUSH rule 1 x 445 [485,311,528,209,964,753,554,931,638,892]
+ CRUSH rule 1 x 446 [345,634,42,294,711,376,314,714,212,646]
+ CRUSH rule 1 x 447 [61,845,767,600,321,716,58,531,827,968]
+ CRUSH rule 1 x 448 [333,232,292,846,364,951,807,688,21,841]
+ CRUSH rule 1 x 449 [680,16,484,670,851,500,258,548,905,988]
+ CRUSH rule 1 x 450 [235,214,79,423,96,822,721,31,312,491]
+ CRUSH rule 1 x 451 [961,468,333,640,823,151,878,33,3,917]
+ CRUSH rule 1 x 452 [525,479,153,528,570,806,604,49,922,414]
+ CRUSH rule 1 x 453 [138,466,302,86,249,154,514,5,494,960]
+ CRUSH rule 1 x 454 [137,625,215,402,389,914,106,103,511,624]
+ CRUSH rule 1 x 455 [173,150,997,16,846,888,295,967,132,319]
+ CRUSH rule 1 x 456 [235,226,238,258,347,784,504,96,890,230]
+ CRUSH rule 1 x 457 [450,577,253,413,717,609,762,975,485,228]
+ CRUSH rule 1 x 458 [195,537,91,814,351,90,399,558,15,441]
+ CRUSH rule 1 x 459 [381,555,312,573,915,623,147,483,517,733]
+ CRUSH rule 1 x 460 [972,730,534,678,756,692,841,512,70,914]
+ CRUSH rule 1 x 461 [506,279,142,830,784,124,385,797,917,561]
+ CRUSH rule 1 x 462 [692,959,578,57,983,299,240,911,375,412]
+ CRUSH rule 1 x 463 [788,667,949,550,685,702,538,111,232,539]
+ CRUSH rule 1 x 464 [133,122,588,999,270,880,789,0,653,566]
+ CRUSH rule 1 x 465 [971,190,230,777,452,914,137,466,531,493]
+ CRUSH rule 1 x 466 [394,576,148,157,103,822,659,35,797,235]
+ CRUSH rule 1 x 467 [517,28,366,362,984,521,187,640,601,622]
+ CRUSH rule 1 x 468 [829,143,874,225,162,413,201,249,555,646]
+ CRUSH rule 1 x 469 [987,936,106,725,633,238,681,851,551,768]
+ CRUSH rule 1 x 470 [107,982,56,889,67,65,558,71,676,655]
+ CRUSH rule 1 x 471 [181,897,629,860,307,116,256,978,409,691]
+ CRUSH rule 1 x 472 [547,512,172,24,705,837,809,56,476,137]
+ CRUSH rule 1 x 473 [760,997,824,905,888,755,756,663,167,196]
+ CRUSH rule 1 x 474 [787,418,743,628,272,341,446,333,245,689]
+ CRUSH rule 1 x 475 [662,312,253,617,105,58,237,764,682,318]
+ CRUSH rule 1 x 476 [110,495,185,508,961,837,984,226,333,916]
+ CRUSH rule 1 x 477 [393,954,834,132,841,367,753,794,237,996]
+ CRUSH rule 1 x 478 [246,483,480,644,985,420,941,843,751,451]
+ CRUSH rule 1 x 479 [70,929,697,931,744,487,158,489,515,496]
+ CRUSH rule 1 x 480 [753,119,961,607,317,717,371,807,687,932]
+ CRUSH rule 1 x 481 [470,429,677,242,574,757,135,375,613,657]
+ CRUSH rule 1 x 482 [451,566,961,675,354,746,731,233,640,492]
+ CRUSH rule 1 x 483 [816,72,371,278,635,30,448,437,219,982]
+ CRUSH rule 1 x 484 [540,454,389,31,654,494,283,170,278,77]
+ CRUSH rule 1 x 485 [74,582,624,684,566,677,866,661,581,943]
+ CRUSH rule 1 x 486 [958,595,199,763,715,973,621,955,400,261]
+ CRUSH rule 1 x 487 [228,302,804,833,876,647,857,782,24,970]
+ CRUSH rule 1 x 488 [180,529,722,956,353,890,924,965,25,925]
+ CRUSH rule 1 x 489 [47,617,812,187,291,828,154,478,512,528]
+ CRUSH rule 1 x 490 [905,822,479,124,750,843,566,779,936,507]
+ CRUSH rule 1 x 491 [892,370,609,998,433,957,188,563,490,369]
+ CRUSH rule 1 x 492 [588,959,127,948,505,936,591,423,668,365]
+ CRUSH rule 1 x 493 [353,461,593,291,301,830,231,893,474,946]
+ CRUSH rule 1 x 494 [378,848,443,368,507,423,389,819,956,597]
+ CRUSH rule 1 x 495 [845,653,768,234,405,367,823,789,217,720]
+ CRUSH rule 1 x 496 [13,988,0,691,389,757,129,763,39,651]
+ CRUSH rule 1 x 497 [796,877,788,394,648,829,542,745,131,753]
+ CRUSH rule 1 x 498 [412,337,270,705,511,227,949,173,398,586]
+ CRUSH rule 1 x 499 [330,695,8,74,618,101,440,509,295,921]
+ CRUSH rule 1 x 500 [820,272,547,765,755,96,930,573,357,491]
+ CRUSH rule 1 x 501 [110,44,132,442,294,423,880,279,616,919]
+ CRUSH rule 1 x 502 [336,595,650,274,993,312,490,852,962,387]
+ CRUSH rule 1 x 503 [922,211,157,722,502,971,262,926,316,527]
+ CRUSH rule 1 x 504 [483,52,122,432,778,461,758,104,831,710]
+ CRUSH rule 1 x 505 [482,598,224,279,480,310,764,558,891,406]
+ CRUSH rule 1 x 506 [493,123,43,856,936,622,898,161,78,414]
+ CRUSH rule 1 x 507 [12,598,264,422,416,947,591,702,346,619]
+ CRUSH rule 1 x 508 [227,157,611,301,223,746,313,282,207,626]
+ CRUSH rule 1 x 509 [807,242,363,122,582,530,798,808,139,377]
+ CRUSH rule 1 x 510 [134,437,227,75,313,351,786,152,921,884]
+ CRUSH rule 1 x 511 [212,54,83,799,457,218,600,968,355,109]
+ CRUSH rule 1 x 512 [236,630,758,752,361,249,899,451,415,920]
+ CRUSH rule 1 x 513 [994,693,644,938,846,685,52,185,197,986]
+ CRUSH rule 1 x 514 [45,508,831,19,817,52,374,985,944,101]
+ CRUSH rule 1 x 515 [504,138,480,272,530,377,481,820,517,850]
+ CRUSH rule 1 x 516 [285,409,136,570,841,610,453,660,93,134]
+ CRUSH rule 1 x 517 [300,232,23,906,438,236,519,737,20,892]
+ CRUSH rule 1 x 518 [397,674,98,898,967,113,625,434,527,630]
+ CRUSH rule 1 x 519 [86,750,772,913,101,864,375,328,3,688]
+ CRUSH rule 1 x 520 [900,833,614,130,261,885,558,956,664,468]
+ CRUSH rule 1 x 521 [31,47,236,751,911,599,495,354,665,945]
+ CRUSH rule 1 x 522 [390,16,280,144,291,175,753,624,769,853]
+ CRUSH rule 1 x 523 [618,308,424,590,300,206,834,212,906,305]
+ CRUSH rule 1 x 524 [635,189,687,963,601,518,8,550,769,975]
+ CRUSH rule 1 x 525 [311,916,699,262,775,32,45,478,911,233]
+ CRUSH rule 1 x 526 [48,738,227,718,244,942,853,643,625,43]
+ CRUSH rule 1 x 527 [202,851,889,216,763,351,270,35,809,509]
+ CRUSH rule 1 x 528 [565,827,590,273,918,106,651,368,118,1]
+ CRUSH rule 1 x 529 [934,864,241,43,466,924,278,926,280,321]
+ CRUSH rule 1 x 530 [502,934,298,670,986,360,577,509,195,722]
+ CRUSH rule 1 x 531 [681,627,942,487,288,561,925,474,669,212]
+ CRUSH rule 1 x 532 [422,6,147,205,861,141,949,374,988,367]
+ CRUSH rule 1 x 533 [863,68,364,983,247,199,54,931,4,279]
+ CRUSH rule 1 x 534 [962,931,775,172,663,119,206,682,627,827]
+ CRUSH rule 1 x 535 [89,565,397,693,839,632,859,30,61,75]
+ CRUSH rule 1 x 536 [499,351,760,458,918,86,148,668,436,192]
+ CRUSH rule 1 x 537 [676,547,787,311,867,748,152,797,492,926]
+ CRUSH rule 1 x 538 [58,644,571,649,941,7,37,485,88,273]
+ CRUSH rule 1 x 539 [837,953,457,711,458,621,528,722,59,237]
+ CRUSH rule 1 x 540 [831,50,132,213,197,709,95,789,348,342]
+ CRUSH rule 1 x 541 [582,757,121,525,532,963,738,277,225,142]
+ CRUSH rule 1 x 542 [472,132,790,997,948,269,137,934,547,351]
+ CRUSH rule 1 x 543 [382,272,797,330,315,748,324,134,839,685]
+ CRUSH rule 1 x 544 [947,930,496,883,509,219,250,362,614,123]
+ CRUSH rule 1 x 545 [425,570,305,77,821,422,117,172,764,372]
+ CRUSH rule 1 x 546 [18,65,529,437,343,547,699,610,785,811]
+ CRUSH rule 1 x 547 [445,715,600,472,213,851,428,267,229,379]
+ CRUSH rule 1 x 548 [367,569,980,167,627,442,517,684,154,108]
+ CRUSH rule 1 x 549 [125,715,671,817,285,420,37,639,934,330]
+ CRUSH rule 1 x 550 [425,599,744,199,923,222,915,570,546,724]
+ CRUSH rule 1 x 551 [44,1,528,922,944,115,161,901,342,941]
+ CRUSH rule 1 x 552 [246,104,68,239,123,427,57,217,21,70]
+ CRUSH rule 1 x 553 [71,703,615,28,593,724,218,916,561,416]
+ CRUSH rule 1 x 554 [207,124,217,166,525,226,693,953,606,894]
+ CRUSH rule 1 x 555 [570,28,317,420,931,413,623,659,403,573]
+ CRUSH rule 1 x 556 [674,152,421,79,215,347,830,762,691,951]
+ CRUSH rule 1 x 557 [347,817,191,391,741,571,593,267,17,386]
+ CRUSH rule 1 x 558 [627,426,369,692,815,371,124,107,766,260]
+ CRUSH rule 1 x 559 [940,630,924,242,224,912,185,356,87,113]
+ CRUSH rule 1 x 560 [295,903,541,29,245,753,887,376,658,366]
+ CRUSH rule 1 x 561 [506,682,384,637,878,991,700,339,687,507]
+ CRUSH rule 1 x 562 [718,529,87,729,842,341,62,817,766,376]
+ CRUSH rule 1 x 563 [552,332,747,206,274,871,903,900,812,290]
+ CRUSH rule 1 x 564 [835,769,736,486,630,209,641,751,930,856]
+ CRUSH rule 1 x 565 [8,167,539,182,607,62,738,873,47,84]
+ CRUSH rule 1 x 566 [600,481,301,263,90,450,184,127,448,327]
+ CRUSH rule 1 x 567 [999,994,509,899,947,24,267,639,646,85]
+ CRUSH rule 1 x 568 [252,431,157,62,601,863,398,521,59,250]
+ CRUSH rule 1 x 569 [643,218,943,455,83,969,494,624,352,562]
+ CRUSH rule 1 x 570 [617,635,765,422,250,156,533,674,23,683]
+ CRUSH rule 1 x 571 [757,80,59,98,328,700,329,848,235,502]
+ CRUSH rule 1 x 572 [299,348,575,889,943,675,33,312,202,355]
+ CRUSH rule 1 x 573 [25,505,270,167,58,901,878,978,1,291]
+ CRUSH rule 1 x 574 [215,431,624,177,628,814,333,841,193,146]
+ CRUSH rule 1 x 575 [225,252,611,546,32,815,389,486,10,402]
+ CRUSH rule 1 x 576 [627,94,159,857,430,691,177,545,839,722]
+ CRUSH rule 1 x 577 [237,809,778,636,61,167,700,521,825,444]
+ CRUSH rule 1 x 578 [885,313,120,344,771,614,487,976,977,58]
+ CRUSH rule 1 x 579 [924,575,787,831,47,996,557,630,468,348]
+ CRUSH rule 1 x 580 [718,51,766,121,118,471,608,755,326,604]
+ CRUSH rule 1 x 581 [219,807,129,571,856,179,874,902,958,415]
+ CRUSH rule 1 x 582 [893,701,598,863,285,829,984,622,175,804]
+ CRUSH rule 1 x 583 [246,930,964,170,993,409,469,193,737,681]
+ CRUSH rule 1 x 584 [336,432,680,175,495,839,642,226,122,703]
+ CRUSH rule 1 x 585 [324,999,397,485,457,527,73,628,884,255]
+ CRUSH rule 1 x 586 [558,230,976,541,816,72,794,682,127,372]
+ CRUSH rule 1 x 587 [985,830,597,21,308,890,952,421,875,65]
+ CRUSH rule 1 x 588 [211,544,57,134,162,496,195,581,649,488]
+ CRUSH rule 1 x 589 [129,21,112,190,885,844,753,180,160,465]
+ CRUSH rule 1 x 590 [467,969,652,593,287,76,811,413,436,162]
+ CRUSH rule 1 x 591 [758,514,316,164,35,110,54,796,369,958]
+ CRUSH rule 1 x 592 [525,253,190,443,315,603,667,318,496,74]
+ CRUSH rule 1 x 593 [601,885,339,152,297,223,269,455,168,635]
+ CRUSH rule 1 x 594 [227,60,450,30,717,840,994,16,777,901]
+ CRUSH rule 1 x 595 [720,854,496,912,80,655,917,525,945,715]
+ CRUSH rule 1 x 596 [751,195,997,77,261,490,180,482,449,647]
+ CRUSH rule 1 x 597 [129,574,714,8,789,847,725,991,955,316]
+ CRUSH rule 1 x 598 [679,207,604,396,841,284,286,280,507,912]
+ CRUSH rule 1 x 599 [668,315,683,349,681,253,599,364,546,849]
+ CRUSH rule 1 x 600 [143,396,464,444,59,57,243,264,31,897]
+ CRUSH rule 1 x 601 [326,573,873,902,136,921,633,596,988,727]
+ CRUSH rule 1 x 602 [860,281,875,535,672,474,697,763,442,542]
+ CRUSH rule 1 x 603 [709,328,445,349,190,455,924,667,356,316]
+ CRUSH rule 1 x 604 [571,62,814,95,866,978,983,281,292,953]
+ CRUSH rule 1 x 605 [252,739,860,27,313,362,857,899,349,926]
+ CRUSH rule 1 x 606 [339,236,759,842,67,644,954,94,88,617]
+ CRUSH rule 1 x 607 [590,248,759,868,433,398,578,386,226,269]
+ CRUSH rule 1 x 608 [145,635,309,467,875,115,148,33,420,669]
+ CRUSH rule 1 x 609 [973,547,223,79,762,863,249,41,778,929]
+ CRUSH rule 1 x 610 [435,816,961,983,255,886,160,888,597,767]
+ CRUSH rule 1 x 611 [559,283,422,584,176,429,570,43,362,401]
+ CRUSH rule 1 x 612 [273,149,123,576,911,270,296,735,245,714]
+ CRUSH rule 1 x 613 [828,614,642,674,33,361,958,580,197,897]
+ CRUSH rule 1 x 614 [478,748,393,34,171,80,92,12,62,719]
+ CRUSH rule 1 x 615 [392,155,144,326,626,134,149,401,14,59]
+ CRUSH rule 1 x 616 [778,637,452,248,15,888,74,307,976,613]
+ CRUSH rule 1 x 617 [622,713,996,833,611,407,364,8,342,512]
+ CRUSH rule 1 x 618 [149,877,270,329,180,327,222,749,697,853]
+ CRUSH rule 1 x 619 [604,163,656,409,322,848,519,967,737,892]
+ CRUSH rule 1 x 620 [181,23,409,198,64,898,35,620,268,902]
+ CRUSH rule 1 x 621 [735,902,386,237,939,475,725,118,875,359]
+ CRUSH rule 1 x 622 [661,824,717,568,858,583,446,798,869,586]
+ CRUSH rule 1 x 623 [142,121,643,61,695,852,485,478,185,854]
+ CRUSH rule 1 x 624 [360,716,420,398,49,717,137,140,488,725]
+ CRUSH rule 1 x 625 [541,167,385,1,601,481,308,111,207,48]
+ CRUSH rule 1 x 626 [364,431,610,363,535,747,225,841,868,249]
+ CRUSH rule 1 x 627 [458,137,557,410,287,749,467,432,944,781]
+ CRUSH rule 1 x 628 [250,350,556,497,821,65,205,580,972,427]
+ CRUSH rule 1 x 629 [928,160,710,572,365,772,538,46,300,112]
+ CRUSH rule 1 x 630 [243,19,918,556,601,16,920,830,171,759]
+ CRUSH rule 1 x 631 [438,221,574,676,797,580,219,211,157,614]
+ CRUSH rule 1 x 632 [797,368,247,5,32,102,416,45,624,253]
+ CRUSH rule 1 x 633 [993,749,525,485,27,330,275,599,219,357]
+ CRUSH rule 1 x 634 [239,351,633,299,651,678,296,337,676,416]
+ CRUSH rule 1 x 635 [640,965,25,961,306,172,849,357,317,599]
+ CRUSH rule 1 x 636 [173,290,297,991,937,823,236,318,228,575]
+ CRUSH rule 1 x 637 [0,918,98,108,111,495,887,57,16,319]
+ CRUSH rule 1 x 638 [702,235,424,900,983,754,701,887,355,632]
+ CRUSH rule 1 x 639 [475,687,31,785,918,611,27,214,226,515]
+ CRUSH rule 1 x 640 [31,664,399,677,123,609,858,138,726,1]
+ CRUSH rule 1 x 641 [296,473,108,963,341,876,897,449,42,193]
+ CRUSH rule 1 x 642 [894,273,427,606,677,670,610,665,299,852]
+ CRUSH rule 1 x 643 [117,111,732,191,114,153,500,631,833,439]
+ CRUSH rule 1 x 644 [438,336,327,512,599,862,660,857,123,910]
+ CRUSH rule 1 x 645 [982,702,351,573,907,915,279,317,414,917]
+ CRUSH rule 1 x 646 [334,804,146,842,697,638,720,135,369,711]
+ CRUSH rule 1 x 647 [933,787,185,334,752,285,372,890,30,747]
+ CRUSH rule 1 x 648 [22,444,400,862,207,842,453,732,262,803]
+ CRUSH rule 1 x 649 [503,229,213,460,639,760,722,748,599,556]
+ CRUSH rule 1 x 650 [328,659,420,443,739,950,869,150,743,438]
+ CRUSH rule 1 x 651 [3,880,823,123,378,585,715,221,31,92]
+ CRUSH rule 1 x 652 [495,977,563,733,92,997,119,818,459,782]
+ CRUSH rule 1 x 653 [185,718,804,280,975,912,198,291,71,792]
+ CRUSH rule 1 x 654 [130,528,380,81,906,511,773,506,546,266]
+ CRUSH rule 1 x 655 [560,872,454,504,319,284,605,214,833,862]
+ CRUSH rule 1 x 656 [219,885,178,981,863,508,708,6,746,734]
+ CRUSH rule 1 x 657 [233,684,813,490,208,941,858,16,128,144]
+ CRUSH rule 1 x 658 [778,6,756,380,750,836,547,850,499,125]
+ CRUSH rule 1 x 659 [240,663,306,540,789,902,170,954,22,394]
+ CRUSH rule 1 x 660 [244,855,196,147,678,323,63,859,215,171]
+ CRUSH rule 1 x 661 [184,270,128,398,910,230,402,205,609,831]
+ CRUSH rule 1 x 662 [65,883,921,438,79,957,464,902,276,289]
+ CRUSH rule 1 x 663 [323,721,594,812,43,992,170,65,906,943]
+ CRUSH rule 1 x 664 [865,113,512,51,427,123,585,260,254,209]
+ CRUSH rule 1 x 665 [420,850,591,475,202,733,798,658,28,334]
+ CRUSH rule 1 x 666 [319,767,246,3,369,493,796,56,736,0]
+ CRUSH rule 1 x 667 [875,39,343,100,829,2,795,783,386,956]
+ CRUSH rule 1 x 668 [331,122,263,599,355,484,943,554,395,713]
+ CRUSH rule 1 x 669 [915,521,402,747,673,445,938,600,517,49]
+ CRUSH rule 1 x 670 [845,659,943,447,401,322,168,302,681,978]
+ CRUSH rule 1 x 671 [108,634,527,363,856,238,755,330,584,525]
+ CRUSH rule 1 x 672 [578,216,110,589,302,137,954,315,735,751]
+ CRUSH rule 1 x 673 [442,74,579,797,622,950,371,402,725,870]
+ CRUSH rule 1 x 674 [588,364,281,308,645,631,229,506,565,362]
+ CRUSH rule 1 x 675 [489,698,744,671,870,174,528,875,982,782]
+ CRUSH rule 1 x 676 [928,911,40,180,722,729,673,569,701,403]
+ CRUSH rule 1 x 677 [399,269,692,131,615,136,103,763,527,83]
+ CRUSH rule 1 x 678 [546,752,544,155,5,463,666,352,576,959]
+ CRUSH rule 1 x 679 [988,25,275,433,628,57,247,620,437,29]
+ CRUSH rule 1 x 680 [335,963,382,486,749,257,795,347,831,761]
+ CRUSH rule 1 x 681 [690,462,623,466,49,471,774,192,454,380]
+ CRUSH rule 1 x 682 [196,588,154,257,807,776,367,718,345,677]
+ CRUSH rule 1 x 683 [627,25,421,160,873,102,345,599,30,892]
+ CRUSH rule 1 x 684 [38,804,592,158,991,264,652,821,641,757]
+ CRUSH rule 1 x 685 [841,368,548,362,166,211,154,121,937,804]
+ CRUSH rule 1 x 686 [336,287,525,440,166,993,911,638,690,393]
+ CRUSH rule 1 x 687 [20,682,924,653,356,16,917,622,156,826]
+ CRUSH rule 1 x 688 [463,371,780,556,385,883,115,248,566,11]
+ CRUSH rule 1 x 689 [569,250,78,816,847,775,333,161,74,907]
+ CRUSH rule 1 x 690 [551,144,587,263,378,394,970,639,835,238]
+ CRUSH rule 1 x 691 [766,464,446,533,449,541,451,290,789,853]
+ CRUSH rule 1 x 692 [739,634,18,245,624,35,268,525,425,499]
+ CRUSH rule 1 x 693 [339,297,118,330,817,91,828,276,264,237]
+ CRUSH rule 1 x 694 [405,26,830,181,533,166,488,804,501,885]
+ CRUSH rule 1 x 695 [622,576,597,535,600,593,300,989,804,72]
+ CRUSH rule 1 x 696 [558,902,689,13,715,28,664,489,598,261]
+ CRUSH rule 1 x 697 [818,222,406,691,427,863,153,922,986,480]
+ CRUSH rule 1 x 698 [178,48,402,233,841,604,468,180,783,915]
+ CRUSH rule 1 x 699 [450,244,180,919,276,332,747,453,519,100]
+ CRUSH rule 1 x 700 [502,771,987,706,416,240,68,641,109,182]
+ CRUSH rule 1 x 701 [4,612,782,216,853,303,585,513,907,414]
+ CRUSH rule 1 x 702 [177,630,232,923,281,708,466,687,742,170]
+ CRUSH rule 1 x 703 [354,178,389,393,778,803,796,607,894,1]
+ CRUSH rule 1 x 704 [646,601,156,171,603,116,655,595,888,354]
+ CRUSH rule 1 x 705 [921,401,890,265,244,690,372,253,807,28]
+ CRUSH rule 1 x 706 [652,877,562,452,26,323,923,770,516,982]
+ CRUSH rule 1 x 707 [345,745,67,716,789,576,2,133,256,374]
+ CRUSH rule 1 x 708 [333,607,180,469,170,555,939,331,41,175]
+ CRUSH rule 1 x 709 [45,187,302,115,896,579,733,607,763,845]
+ CRUSH rule 1 x 710 [94,855,43,199,18,948,449,28,731,573]
+ CRUSH rule 1 x 711 [227,653,731,150,380,842,534,110,639,452]
+ CRUSH rule 1 x 712 [398,953,136,870,181,408,895,459,341,833]
+ CRUSH rule 1 x 713 [116,800,503,662,635,579,53,839,56,829]
+ CRUSH rule 1 x 714 [111,629,866,709,902,557,875,649,23,79]
+ CRUSH rule 1 x 715 [531,291,486,382,192,807,322,417,973,582]
+ CRUSH rule 1 x 716 [169,541,291,42,343,724,138,197,32,415]
+ CRUSH rule 1 x 717 [417,446,994,894,239,494,237,62,327,958]
+ CRUSH rule 1 x 718 [992,383,298,844,377,463,544,891,210,370]
+ CRUSH rule 1 x 719 [936,674,324,759,194,409,828,975,119,87]
+ CRUSH rule 1 x 720 [370,188,174,464,644,218,214,76,870,779]
+ CRUSH rule 1 x 721 [320,859,278,259,170,957,177,264,867,327]
+ CRUSH rule 1 x 722 [7,2,673,129,96,445,823,833,1,774]
+ CRUSH rule 1 x 723 [270,553,831,662,38,101,985,846,77,467]
+ CRUSH rule 1 x 724 [666,822,708,895,633,800,616,879,480,309]
+ CRUSH rule 1 x 725 [794,406,875,459,981,751,359,687,720,128]
+ CRUSH rule 1 x 726 [420,556,341,292,240,68,966,535,669,74]
+ CRUSH rule 1 x 727 [561,461,129,635,965,610,105,31,506,430]
+ CRUSH rule 1 x 728 [951,330,196,756,589,849,753,760,254,379]
+ CRUSH rule 1 x 729 [656,644,436,591,27,119,572,933,434,816]
+ CRUSH rule 1 x 730 [3,558,629,184,50,765,760,800,945,743]
+ CRUSH rule 1 x 731 [852,89,75,735,713,113,528,890,625,535]
+ CRUSH rule 1 x 732 [983,840,869,976,697,307,368,271,778,172]
+ CRUSH rule 1 x 733 [285,396,388,122,387,364,880,343,590,539]
+ CRUSH rule 1 x 734 [125,510,402,640,676,501,535,627,224,790]
+ CRUSH rule 1 x 735 [417,773,686,504,459,912,690,59,294,569]
+ CRUSH rule 1 x 736 [749,396,632,550,779,109,845,278,559,613]
+ CRUSH rule 1 x 737 [644,991,946,135,448,903,482,564,259,896]
+ CRUSH rule 1 x 738 [449,683,290,220,245,525,429,397,872,716]
+ CRUSH rule 1 x 739 [341,220,641,454,740,661,146,17,314,156]
+ CRUSH rule 1 x 740 [874,524,674,650,472,282,214,494,593,155]
+ CRUSH rule 1 x 741 [189,472,712,798,715,757,863,571,876,528]
+ CRUSH rule 1 x 742 [912,581,114,447,730,21,687,81,145,695]
+ CRUSH rule 1 x 743 [654,914,425,441,763,39,451,631,911,829]
+ CRUSH rule 1 x 744 [725,295,579,377,162,447,843,699,24,714]
+ CRUSH rule 1 x 745 [787,858,850,506,612,735,926,314,771,910]
+ CRUSH rule 1 x 746 [757,848,704,30,47,940,450,651,105,921]
+ CRUSH rule 1 x 747 [700,81,867,681,801,64,879,857,727,565]
+ CRUSH rule 1 x 748 [557,436,238,664,293,865,304,999,685,843]
+ CRUSH rule 1 x 749 [772,622,337,42,156,302,383,506,570,828]
+ CRUSH rule 1 x 750 [946,97,376,677,316,670,169,171,9,58]
+ CRUSH rule 1 x 751 [996,618,343,911,83,22,388,17,892,537]
+ CRUSH rule 1 x 752 [746,887,695,868,610,950,88,315,728,669]
+ CRUSH rule 1 x 753 [741,14,463,479,172,192,481,702,431,675]
+ CRUSH rule 1 x 754 [648,349,333,355,65,63,336,724,262,61]
+ CRUSH rule 1 x 755 [157,460,466,187,959,674,192,279,371,970]
+ CRUSH rule 1 x 756 [416,97,197,497,227,3,850,191,991,63]
+ CRUSH rule 1 x 757 [599,839,776,410,256,823,121,690,544,28]
+ CRUSH rule 1 x 758 [994,218,620,256,361,749,165,686,449,831]
+ CRUSH rule 1 x 759 [959,682,514,745,100,519,15,347,311,752]
+ CRUSH rule 1 x 760 [518,943,215,83,706,137,345,69,39,199]
+ CRUSH rule 1 x 761 [285,849,420,324,987,338,373,361,684,654]
+ CRUSH rule 1 x 762 [591,313,41,335,110,696,664,350,339,980]
+ CRUSH rule 1 x 763 [908,411,200,740,292,295,387,775,797,990]
+ CRUSH rule 1 x 764 [787,234,894,485,883,711,70,202,557,471]
+ CRUSH rule 1 x 765 [327,921,882,393,444,792,402,123,902,592]
+ CRUSH rule 1 x 766 [84,161,878,704,416,144,357,310,890,850]
+ CRUSH rule 1 x 767 [370,895,702,701,890,2,251,951,675,322]
+ CRUSH rule 1 x 768 [826,760,879,864,460,474,645,975,947,199]
+ CRUSH rule 1 x 769 [67,768,663,735,814,66,213,527,546,42]
+ CRUSH rule 1 x 770 [593,909,482,259,5,550,961,324,309,772]
+ CRUSH rule 1 x 771 [309,935,121,578,937,685,933,571,822,256]
+ CRUSH rule 1 x 772 [12,125,797,301,348,419,891,959,487,355]
+ CRUSH rule 1 x 773 [253,466,820,549,591,193,783,951,982,160]
+ CRUSH rule 1 x 774 [164,390,705,109,881,505,890,425,599,485]
+ CRUSH rule 1 x 775 [703,47,43,973,643,406,885,976,936,221]
+ CRUSH rule 1 x 776 [728,231,80,916,2,850,396,76,680,108]
+ CRUSH rule 1 x 777 [981,621,568,729,869,952,563,860,388,456]
+ CRUSH rule 1 x 778 [411,456,544,597,789,784,65,954,125,358]
+ CRUSH rule 1 x 779 [346,121,519,921,587,48,772,645,254,759]
+ CRUSH rule 1 x 780 [476,39,288,381,303,29,17,336,147,829]
+ CRUSH rule 1 x 781 [10,130,585,844,729,705,714,954,271,58]
+ CRUSH rule 1 x 782 [462,246,581,902,623,877,812,516,774,985]
+ CRUSH rule 1 x 783 [580,373,153,775,668,661,626,961,576,119]
+ CRUSH rule 1 x 784 [413,113,978,990,994,56,481,198,171,944]
+ CRUSH rule 1 x 785 [341,856,332,354,59,581,632,151,586,360]
+ CRUSH rule 1 x 786 [411,140,313,393,215,618,490,481,627,740]
+ CRUSH rule 1 x 787 [605,522,211,813,636,224,600,528,966,556]
+ CRUSH rule 1 x 788 [226,545,35,142,726,851,194,216,486,782]
+ CRUSH rule 1 x 789 [545,320,414,702,731,277,237,916,374,670]
+ CRUSH rule 1 x 790 [414,748,816,327,130,115,788,164,691,329]
+ CRUSH rule 1 x 791 [660,906,406,697,916,322,124,401,742,990]
+ CRUSH rule 1 x 792 [287,392,514,204,75,789,406,858,694,351]
+ CRUSH rule 1 x 793 [631,133,850,713,720,487,376,812,886,264]
+ CRUSH rule 1 x 794 [931,517,543,210,963,898,811,459,344,719]
+ CRUSH rule 1 x 795 [551,962,477,948,425,434,268,94,648,402]
+ CRUSH rule 1 x 796 [814,4,95,27,368,300,646,451,67,738]
+ CRUSH rule 1 x 797 [64,201,299,734,605,864,596,196,93,636]
+ CRUSH rule 1 x 798 [422,530,114,431,565,716,473,250,839,895]
+ CRUSH rule 1 x 799 [824,32,679,562,266,549,859,994,831,60]
+ CRUSH rule 1 x 800 [862,623,489,637,861,196,941,643,398,325]
+ CRUSH rule 1 x 801 [145,550,329,324,734,160,219,662,142,28]
+ CRUSH rule 1 x 802 [570,19,847,308,387,518,846,53,783,511]
+ CRUSH rule 1 x 803 [151,812,662,358,880,349,834,881,23,229]
+ CRUSH rule 1 x 804 [467,93,264,863,176,842,663,949,380,39]
+ CRUSH rule 1 x 805 [621,223,938,809,591,686,121,157,934,660]
+ CRUSH rule 1 x 806 [898,957,805,430,499,584,640,607,790,832]
+ CRUSH rule 1 x 807 [354,531,422,159,921,431,802,136,305,983]
+ CRUSH rule 1 x 808 [7,96,76,897,446,2,166,929,234,460]
+ CRUSH rule 1 x 809 [70,734,719,56,687,21,23,145,184,465]
+ CRUSH rule 1 x 810 [701,18,972,327,771,649,620,648,433,997]
+ CRUSH rule 1 x 811 [248,547,103,728,901,264,948,202,521,278]
+ CRUSH rule 1 x 812 [230,576,821,566,993,762,675,28,263,410]
+ CRUSH rule 1 x 813 [805,114,683,629,99,462,285,450,948,742]
+ CRUSH rule 1 x 814 [54,619,973,741,497,894,401,266,905,320]
+ CRUSH rule 1 x 815 [679,412,613,132,969,411,314,670,928,727]
+ CRUSH rule 1 x 816 [919,448,826,414,36,289,44,822,332,959]
+ CRUSH rule 1 x 817 [765,830,436,521,332,458,260,172,193,516]
+ CRUSH rule 1 x 818 [415,566,644,687,692,414,769,826,519,277]
+ CRUSH rule 1 x 819 [721,319,865,750,546,859,523,770,56,437]
+ CRUSH rule 1 x 820 [218,301,333,190,686,179,535,787,267,46]
+ CRUSH rule 1 x 821 [185,795,680,953,329,750,621,815,313,916]
+ CRUSH rule 1 x 822 [356,261,54,522,900,103,883,112,601,15]
+ CRUSH rule 1 x 823 [220,281,549,456,64,306,282,641,216,929]
+ CRUSH rule 1 x 824 [292,809,887,74,776,788,559,886,753,749]
+ CRUSH rule 1 x 825 [949,778,101,311,110,480,161,998,370,10]
+ CRUSH rule 1 x 826 [767,818,833,927,356,954,910,63,288,836]
+ CRUSH rule 1 x 827 [631,83,406,635,657,713,212,916,692,653]
+ CRUSH rule 1 x 828 [288,986,445,26,414,607,937,595,935,672]
+ CRUSH rule 1 x 829 [990,667,915,694,974,453,669,330,822,36]
+ CRUSH rule 1 x 830 [152,571,778,505,685,209,448,55,965,851]
+ CRUSH rule 1 x 831 [814,563,630,97,582,107,142,157,957,330]
+ CRUSH rule 1 x 832 [235,641,616,110,979,844,656,135,341,922]
+ CRUSH rule 1 x 833 [657,565,922,140,825,457,764,766,853,890]
+ CRUSH rule 1 x 834 [907,231,644,13,617,130,83,483,811,98]
+ CRUSH rule 1 x 835 [784,262,771,264,612,238,537,937,101,507]
+ CRUSH rule 1 x 836 [951,158,366,710,43,427,351,961,52,44]
+ CRUSH rule 1 x 837 [556,498,334,633,895,627,903,29,454,647]
+ CRUSH rule 1 x 838 [329,274,964,547,119,342,983,998,320,935]
+ CRUSH rule 1 x 839 [568,209,939,364,658,747,47,859,402,947]
+ CRUSH rule 1 x 840 [45,579,842,70,655,862,815,109,762,642]
+ CRUSH rule 1 x 841 [652,702,24,605,152,93,226,46,918,220]
+ CRUSH rule 1 x 842 [629,984,314,895,408,897,575,1,312,542]
+ CRUSH rule 1 x 843 [799,690,688,648,151,812,486,199,966,501]
+ CRUSH rule 1 x 844 [694,600,534,700,569,11,899,382,851,472]
+ CRUSH rule 1 x 845 [332,30,179,93,951,324,611,512,855,760]
+ CRUSH rule 1 x 846 [452,251,712,719,404,739,606,237,414,844]
+ CRUSH rule 1 x 847 [399,681,847,739,13,555,363,893,592,634]
+ CRUSH rule 1 x 848 [303,138,440,346,547,216,700,249,214,100]
+ CRUSH rule 1 x 849 [666,346,708,873,64,694,847,463,995,314]
+ CRUSH rule 1 x 850 [644,511,345,844,545,337,358,35,913,310]
+ CRUSH rule 1 x 851 [527,546,737,425,100,331,95,337,677,275]
+ CRUSH rule 1 x 852 [31,809,94,618,156,853,469,511,999,340]
+ CRUSH rule 1 x 853 [483,330,869,184,46,942,774,679,616,492]
+ CRUSH rule 1 x 854 [697,953,968,143,502,955,441,302,437,53]
+ CRUSH rule 1 x 855 [837,996,239,621,32,191,686,702,919,971]
+ CRUSH rule 1 x 856 [712,40,547,430,195,857,224,810,404,126]
+ CRUSH rule 1 x 857 [77,984,576,551,568,96,12,763,594,668]
+ CRUSH rule 1 x 858 [412,384,841,465,572,576,688,61,545,491]
+ CRUSH rule 1 x 859 [173,760,26,300,87,567,463,903,272,8]
+ CRUSH rule 1 x 860 [776,429,328,917,658,783,699,907,532,627]
+ CRUSH rule 1 x 861 [705,405,477,50,73,714,901,487,725,204]
+ CRUSH rule 1 x 862 [809,44,788,938,964,177,490,409,15,842]
+ CRUSH rule 1 x 863 [349,496,963,178,675,853,172,980,772,115]
+ CRUSH rule 1 x 864 [717,858,101,239,992,244,43,15,29,974]
+ CRUSH rule 1 x 865 [857,603,586,262,550,289,850,40,170,31]
+ CRUSH rule 1 x 866 [394,304,71,96,642,155,255,481,435,119]
+ CRUSH rule 1 x 867 [640,773,663,974,261,296,988,730,753,888]
+ CRUSH rule 1 x 868 [613,950,712,663,448,460,643,547,734,16]
+ CRUSH rule 1 x 869 [973,889,524,22,671,477,718,431,968,472]
+ CRUSH rule 1 x 870 [505,35,386,498,348,503,54,992,726,783]
+ CRUSH rule 1 x 871 [239,264,262,773,781,734,387,515,98,232]
+ CRUSH rule 1 x 872 [21,767,456,748,783,797,180,800,521,270]
+ CRUSH rule 1 x 873 [954,666,980,264,435,233,199,358,805,255]
+ CRUSH rule 1 x 874 [54,510,947,1,500,119,93,915,801,43]
+ CRUSH rule 1 x 875 [809,418,452,462,88,673,634,435,778,884]
+ CRUSH rule 1 x 876 [483,457,61,248,523,277,322,141,82,412]
+ CRUSH rule 1 x 877 [542,531,952,939,710,179,181,460,459,527]
+ CRUSH rule 1 x 878 [217,674,857,644,678,809,329,591,59,4]
+ CRUSH rule 1 x 879 [999,475,134,250,319,357,145,750,54,997]
+ CRUSH rule 1 x 880 [678,573,935,385,570,651,319,630,888,970]
+ CRUSH rule 1 x 881 [394,835,789,802,587,155,570,109,896,826]
+ CRUSH rule 1 x 882 [467,382,353,56,979,674,974,483,412,547]
+ CRUSH rule 1 x 883 [802,744,237,337,50,96,202,148,129,72]
+ CRUSH rule 1 x 884 [653,660,638,700,31,558,389,381,347,314]
+ CRUSH rule 1 x 885 [898,704,307,445,879,872,174,972,544,894]
+ CRUSH rule 1 x 886 [434,357,938,641,737,8,56,582,915,541]
+ CRUSH rule 1 x 887 [297,226,711,428,370,318,472,947,35,528]
+ CRUSH rule 1 x 888 [863,324,443,213,902,25,806,53,385,387]
+ CRUSH rule 1 x 889 [105,102,308,163,947,548,399,382,761,907]
+ CRUSH rule 1 x 890 [550,248,606,704,615,708,996,561,485,482]
+ CRUSH rule 1 x 891 [575,928,880,891,826,763,706,701,501,680]
+ CRUSH rule 1 x 892 [259,862,133,271,292,162,53,333,458,77]
+ CRUSH rule 1 x 893 [902,880,543,542,37,942,672,320,394,373]
+ CRUSH rule 1 x 894 [180,169,916,43,945,713,648,685,895,735]
+ CRUSH rule 1 x 895 [725,849,182,129,177,272,599,829,809,713]
+ CRUSH rule 1 x 896 [951,34,874,537,969,123,210,529,491,289]
+ CRUSH rule 1 x 897 [810,352,73,939,943,895,12,481,539,562]
+ CRUSH rule 1 x 898 [979,433,719,411,787,359,342,37,303,70]
+ CRUSH rule 1 x 899 [685,668,534,932,399,156,124,653,574,384]
+ CRUSH rule 1 x 900 [530,978,41,894,941,681,380,419,667,56]
+ CRUSH rule 1 x 901 [740,107,336,175,574,706,157,292,724,805]
+ CRUSH rule 1 x 902 [800,743,693,310,67,111,178,624,733,498]
+ CRUSH rule 1 x 903 [230,267,842,266,550,769,66,738,419,199]
+ CRUSH rule 1 x 904 [346,949,460,973,696,91,957,801,74,934]
+ CRUSH rule 1 x 905 [530,397,619,958,576,973,685,6,689,387]
+ CRUSH rule 1 x 906 [80,426,138,672,73,776,30,169,506,497]
+ CRUSH rule 1 x 907 [365,968,475,297,296,724,664,331,184,461]
+ CRUSH rule 1 x 908 [204,832,742,809,862,745,484,391,841,967]
+ CRUSH rule 1 x 909 [883,989,146,959,366,59,686,965,515,421]
+ CRUSH rule 1 x 910 [549,593,249,853,792,769,824,552,717,159]
+ CRUSH rule 1 x 911 [325,847,352,214,851,732,789,255,896,868]
+ CRUSH rule 1 x 912 [874,888,582,796,557,601,226,889,69,237]
+ CRUSH rule 1 x 913 [331,463,342,574,989,362,925,746,664,533]
+ CRUSH rule 1 x 914 [836,468,601,732,607,275,70,280,837,367]
+ CRUSH rule 1 x 915 [245,228,100,661,799,13,126,79,652,793]
+ CRUSH rule 1 x 916 [77,967,364,435,27,474,255,133,892,524]
+ CRUSH rule 1 x 917 [239,60,866,221,772,967,725,707,47,216]
+ CRUSH rule 1 x 918 [988,115,922,80,201,544,583,923,863,232]
+ CRUSH rule 1 x 919 [783,139,696,1,848,169,888,980,33,261]
+ CRUSH rule 1 x 920 [623,408,685,953,974,696,532,124,911,206]
+ CRUSH rule 1 x 921 [105,799,144,90,399,373,633,290,155,137]
+ CRUSH rule 1 x 922 [887,505,652,348,514,806,952,474,67,938]
+ CRUSH rule 1 x 923 [223,318,552,458,743,871,964,384,454,448]
+ CRUSH rule 1 x 924 [25,778,366,333,163,801,584,31,151,178]
+ CRUSH rule 1 x 925 [912,601,297,682,770,173,969,168,500,68]
+ CRUSH rule 1 x 926 [968,133,690,144,814,155,709,158,96,739]
+ CRUSH rule 1 x 927 [277,724,214,988,690,342,465,775,725,414]
+ CRUSH rule 1 x 928 [554,203,658,789,298,299,847,752,780,738]
+ CRUSH rule 1 x 929 [761,802,367,528,758,522,744,171,144,704]
+ CRUSH rule 1 x 930 [814,61,788,736,660,491,832,654,567,160]
+ CRUSH rule 1 x 931 [29,193,61,41,343,664,487,839,776,117]
+ CRUSH rule 1 x 932 [446,198,862,534,168,35,530,462,202,11]
+ CRUSH rule 1 x 933 [352,742,216,321,525,44,568,61,945,154]
+ CRUSH rule 1 x 934 [730,2,332,631,613,249,533,116,254,569]
+ CRUSH rule 1 x 935 [731,23,736,79,361,992,772,49,567,47]
+ CRUSH rule 1 x 936 [322,975,20,904,827,603,138,802,885,447]
+ CRUSH rule 1 x 937 [822,221,841,161,723,137,630,308,973,934]
+ CRUSH rule 1 x 938 [557,850,66,630,499,404,286,395,927,611]
+ CRUSH rule 1 x 939 [150,11,971,371,124,785,408,49,977,243]
+ CRUSH rule 1 x 940 [638,398,169,616,333,751,25,883,867,270]
+ CRUSH rule 1 x 941 [730,342,929,577,451,838,964,28,633,960]
+ CRUSH rule 1 x 942 [62,292,166,814,587,172,553,16,440,31]
+ CRUSH rule 1 x 943 [165,314,519,548,41,726,759,851,617,420]
+ CRUSH rule 1 x 944 [199,625,766,176,194,297,678,915,619,69]
+ CRUSH rule 1 x 945 [946,999,699,303,38,81,952,885,987,775]
+ CRUSH rule 1 x 946 [595,93,852,142,503,647,933,267,846,866]
+ CRUSH rule 1 x 947 [800,582,356,93,716,117,922,868,413,545]
+ CRUSH rule 1 x 948 [132,551,139,920,87,46,81,220,725,211]
+ CRUSH rule 1 x 949 [792,920,466,380,97,568,799,961,564,71]
+ CRUSH rule 1 x 950 [111,345,176,543,879,954,355,220,528,747]
+ CRUSH rule 1 x 951 [414,619,648,655,364,971,829,408,568,734]
+ CRUSH rule 1 x 952 [775,469,500,356,287,4,16,746,835,529]
+ CRUSH rule 1 x 953 [349,1,5,251,168,680,141,619,234,517]
+ CRUSH rule 1 x 954 [570,940,410,249,929,394,129,696,115,984]
+ CRUSH rule 1 x 955 [729,774,823,800,7,127,536,766,579,398]
+ CRUSH rule 1 x 956 [519,141,575,625,738,475,169,751,667,381]
+ CRUSH rule 1 x 957 [242,709,611,97,760,309,393,281,227,412]
+ CRUSH rule 1 x 958 [84,217,227,253,246,604,346,377,425,533]
+ CRUSH rule 1 x 959 [270,413,918,789,703,608,543,519,496,956]
+ CRUSH rule 1 x 960 [458,192,307,279,920,139,855,49,548,367]
+ CRUSH rule 1 x 961 [981,388,777,546,359,660,455,708,649,93]
+ CRUSH rule 1 x 962 [623,834,277,134,729,246,856,477,895,89]
+ CRUSH rule 1 x 963 [291,167,714,468,109,373,485,701,76,55]
+ CRUSH rule 1 x 964 [28,156,788,127,598,215,361,255,507,540]
+ CRUSH rule 1 x 965 [675,557,290,517,840,510,59,229,819,610]
+ CRUSH rule 1 x 966 [836,306,946,283,642,606,929,773,928,579]
+ CRUSH rule 1 x 967 [966,386,735,837,392,116,19,674,395,483]
+ CRUSH rule 1 x 968 [864,756,690,121,328,122,433,520,916,41]
+ CRUSH rule 1 x 969 [729,625,480,769,512,882,518,956,398,269]
+ CRUSH rule 1 x 970 [800,362,646,582,309,102,576,411,416,523]
+ CRUSH rule 1 x 971 [737,381,153,684,298,166,344,520,546,612]
+ CRUSH rule 1 x 972 [952,245,720,884,334,311,754,540,79,174]
+ CRUSH rule 1 x 973 [356,455,579,857,832,596,549,524,109,364]
+ CRUSH rule 1 x 974 [545,758,586,596,300,790,116,993,644,405]
+ CRUSH rule 1 x 975 [336,191,202,146,720,897,330,308,744,843]
+ CRUSH rule 1 x 976 [446,208,757,620,252,846,397,58,57,603]
+ CRUSH rule 1 x 977 [202,896,196,956,763,126,783,828,409,529]
+ CRUSH rule 1 x 978 [612,324,996,225,418,583,514,169,99,878]
+ CRUSH rule 1 x 979 [843,457,675,650,958,657,677,173,903,781]
+ CRUSH rule 1 x 980 [60,914,881,626,850,759,398,943,764,867]
+ CRUSH rule 1 x 981 [702,749,937,153,724,514,536,212,247,523]
+ CRUSH rule 1 x 982 [298,928,738,167,99,668,395,198,100,580]
+ CRUSH rule 1 x 983 [723,572,395,358,900,37,927,597,103,461]
+ CRUSH rule 1 x 984 [723,864,804,935,846,993,950,840,427,469]
+ CRUSH rule 1 x 985 [945,459,868,211,524,954,911,208,91,999]
+ CRUSH rule 1 x 986 [772,664,535,169,297,996,864,555,687,212]
+ CRUSH rule 1 x 987 [88,324,312,843,661,580,76,894,480,323]
+ CRUSH rule 1 x 988 [522,927,131,996,351,685,865,47,116,230]
+ CRUSH rule 1 x 989 [578,332,208,605,975,207,155,380,797,177]
+ CRUSH rule 1 x 990 [638,228,414,311,738,698,340,526,728,595]
+ CRUSH rule 1 x 991 [530,221,451,422,879,916,754,928,288,668]
+ CRUSH rule 1 x 992 [925,705,275,81,234,310,117,546,798,777]
+ CRUSH rule 1 x 993 [991,301,43,469,830,242,382,428,451,216]
+ CRUSH rule 1 x 994 [276,51,868,683,843,815,557,378,936,192]
+ CRUSH rule 1 x 995 [288,836,753,790,758,120,158,265,110,171]
+ CRUSH rule 1 x 996 [887,983,252,686,470,345,459,764,859,776]
+ CRUSH rule 1 x 997 [110,924,386,79,705,697,210,698,273,955]
+ CRUSH rule 1 x 998 [435,830,485,853,926,730,786,762,444,561]
+ CRUSH rule 1 x 999 [876,738,357,913,723,51,15,585,898,902]
+ CRUSH rule 1 x 1000 [178,963,638,430,845,586,317,102,200,662]
+ CRUSH rule 1 x 1001 [99,519,66,759,583,944,739,922,343,574]
+ CRUSH rule 1 x 1002 [515,534,468,866,878,717,729,370,326,640]
+ CRUSH rule 1 x 1003 [104,611,937,698,94,67,614,783,865,245]
+ CRUSH rule 1 x 1004 [269,638,724,375,491,121,891,113,424,320]
+ CRUSH rule 1 x 1005 [369,223,309,409,822,39,597,969,911,241]
+ CRUSH rule 1 x 1006 [40,107,69,275,79,429,234,945,598,498]
+ CRUSH rule 1 x 1007 [978,111,416,758,454,640,5,444,795,150]
+ CRUSH rule 1 x 1008 [965,956,624,832,421,96,975,723,909,93]
+ CRUSH rule 1 x 1009 [598,476,356,695,919,566,234,383,604,903]
+ CRUSH rule 1 x 1010 [767,523,239,517,29,77,23,241,838,865]
+ CRUSH rule 1 x 1011 [289,871,207,576,347,698,48,570,639,230]
+ CRUSH rule 1 x 1012 [128,28,370,31,341,755,268,647,669,90]
+ CRUSH rule 1 x 1013 [979,765,660,812,666,187,808,351,572,403]
+ CRUSH rule 1 x 1014 [979,948,513,88,47,825,969,81,586,62]
+ CRUSH rule 1 x 1015 [277,790,396,672,542,647,145,11,965,669]
+ CRUSH rule 1 x 1016 [262,73,128,886,839,685,456,560,935,733]
+ CRUSH rule 1 x 1017 [150,269,61,499,832,591,637,731,738,154]
+ CRUSH rule 1 x 1018 [555,829,554,944,406,576,463,926,475,316]
+ CRUSH rule 1 x 1019 [513,356,265,446,65,288,768,245,337,197]
+ CRUSH rule 1 x 1020 [158,161,877,704,948,570,495,865,698,835]
+ CRUSH rule 1 x 1021 [915,998,957,285,546,202,676,322,671,622]
+ CRUSH rule 1 x 1022 [967,829,973,640,703,470,871,828,440,449]
+ CRUSH rule 1 x 1023 [488,257,614,859,325,419,50,560,595,554]
+ rule 1 (metadata) num_rep 10 result size == 10:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-jewel-tunables.crushmap b/src/test/cli/crushtool/test-map-jewel-tunables.crushmap
new file mode 100644
index 000000000..d4d3f28aa
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-jewel-tunables.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/test-map-jewel-tunables.t b/src/test/cli/crushtool/test-map-jewel-tunables.t
new file mode 100644
index 000000000..47acb51a4
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-jewel-tunables.t
@@ -0,0 +1,10252 @@
+ $ crushtool -i "$TESTDIR/test-map-jewel-tunables.crushmap" --test --show-mappings --show-statistics --rule 0 --weight 12 0 --weight 20 0 --weight 30 0 --min-rep 1 --max-rep 10
+ rule 0 (data), x = 0..1023, numrep = 1..10
+ CRUSH rule 0 x 0 [101]
+ CRUSH rule 0 x 1 [80]
+ CRUSH rule 0 x 2 [91]
+ CRUSH rule 0 x 3 [51]
+ CRUSH rule 0 x 4 [50]
+ CRUSH rule 0 x 5 [89]
+ CRUSH rule 0 x 6 [91]
+ CRUSH rule 0 x 7 [104]
+ CRUSH rule 0 x 8 [78]
+ CRUSH rule 0 x 9 [101]
+ CRUSH rule 0 x 10 [61]
+ CRUSH rule 0 x 11 [13]
+ CRUSH rule 0 x 12 [83]
+ CRUSH rule 0 x 13 [108]
+ CRUSH rule 0 x 14 [105]
+ CRUSH rule 0 x 15 [18]
+ CRUSH rule 0 x 16 [103]
+ CRUSH rule 0 x 17 [85]
+ CRUSH rule 0 x 18 [11]
+ CRUSH rule 0 x 19 [75]
+ CRUSH rule 0 x 20 [79]
+ CRUSH rule 0 x 21 [84]
+ CRUSH rule 0 x 22 [23]
+ CRUSH rule 0 x 23 [118]
+ CRUSH rule 0 x 24 [83]
+ CRUSH rule 0 x 25 [81]
+ CRUSH rule 0 x 26 [38]
+ CRUSH rule 0 x 27 [76]
+ CRUSH rule 0 x 28 [76]
+ CRUSH rule 0 x 29 [8]
+ CRUSH rule 0 x 30 [94]
+ CRUSH rule 0 x 31 [76]
+ CRUSH rule 0 x 32 [72]
+ CRUSH rule 0 x 33 [77]
+ CRUSH rule 0 x 34 [74]
+ CRUSH rule 0 x 35 [22]
+ CRUSH rule 0 x 36 [104]
+ CRUSH rule 0 x 37 [38]
+ CRUSH rule 0 x 38 [72]
+ CRUSH rule 0 x 39 [68]
+ CRUSH rule 0 x 40 [103]
+ CRUSH rule 0 x 41 [85]
+ CRUSH rule 0 x 42 [106]
+ CRUSH rule 0 x 43 [10]
+ CRUSH rule 0 x 44 [101]
+ CRUSH rule 0 x 45 [8]
+ CRUSH rule 0 x 46 [65]
+ CRUSH rule 0 x 47 [106]
+ CRUSH rule 0 x 48 [34]
+ CRUSH rule 0 x 49 [0]
+ CRUSH rule 0 x 50 [42]
+ CRUSH rule 0 x 51 [104]
+ CRUSH rule 0 x 52 [83]
+ CRUSH rule 0 x 53 [32]
+ CRUSH rule 0 x 54 [28]
+ CRUSH rule 0 x 55 [14]
+ CRUSH rule 0 x 56 [21]
+ CRUSH rule 0 x 57 [93]
+ CRUSH rule 0 x 58 [45]
+ CRUSH rule 0 x 59 [80]
+ CRUSH rule 0 x 60 [90]
+ CRUSH rule 0 x 61 [88]
+ CRUSH rule 0 x 62 [81]
+ CRUSH rule 0 x 63 [79]
+ CRUSH rule 0 x 64 [1]
+ CRUSH rule 0 x 65 [13]
+ CRUSH rule 0 x 66 [48]
+ CRUSH rule 0 x 67 [94]
+ CRUSH rule 0 x 68 [102]
+ CRUSH rule 0 x 69 [62]
+ CRUSH rule 0 x 70 [84]
+ CRUSH rule 0 x 71 [55]
+ CRUSH rule 0 x 72 [97]
+ CRUSH rule 0 x 73 [64]
+ CRUSH rule 0 x 74 [96]
+ CRUSH rule 0 x 75 [29]
+ CRUSH rule 0 x 76 [55]
+ CRUSH rule 0 x 77 [107]
+ CRUSH rule 0 x 78 [31]
+ CRUSH rule 0 x 79 [64]
+ CRUSH rule 0 x 80 [0]
+ CRUSH rule 0 x 81 [71]
+ CRUSH rule 0 x 82 [37]
+ CRUSH rule 0 x 83 [92]
+ CRUSH rule 0 x 84 [49]
+ CRUSH rule 0 x 85 [54]
+ CRUSH rule 0 x 86 [37]
+ CRUSH rule 0 x 87 [116]
+ CRUSH rule 0 x 88 [38]
+ CRUSH rule 0 x 89 [76]
+ CRUSH rule 0 x 90 [14]
+ CRUSH rule 0 x 91 [93]
+ CRUSH rule 0 x 92 [86]
+ CRUSH rule 0 x 93 [44]
+ CRUSH rule 0 x 94 [61]
+ CRUSH rule 0 x 95 [93]
+ CRUSH rule 0 x 96 [66]
+ CRUSH rule 0 x 97 [111]
+ CRUSH rule 0 x 98 [66]
+ CRUSH rule 0 x 99 [78]
+ CRUSH rule 0 x 100 [28]
+ CRUSH rule 0 x 101 [84]
+ CRUSH rule 0 x 102 [82]
+ CRUSH rule 0 x 103 [66]
+ CRUSH rule 0 x 104 [14]
+ CRUSH rule 0 x 105 [87]
+ CRUSH rule 0 x 106 [69]
+ CRUSH rule 0 x 107 [1]
+ CRUSH rule 0 x 108 [94]
+ CRUSH rule 0 x 109 [112]
+ CRUSH rule 0 x 110 [54]
+ CRUSH rule 0 x 111 [10]
+ CRUSH rule 0 x 112 [89]
+ CRUSH rule 0 x 113 [69]
+ CRUSH rule 0 x 114 [79]
+ CRUSH rule 0 x 115 [50]
+ CRUSH rule 0 x 116 [96]
+ CRUSH rule 0 x 117 [87]
+ CRUSH rule 0 x 118 [23]
+ CRUSH rule 0 x 119 [104]
+ CRUSH rule 0 x 120 [57]
+ CRUSH rule 0 x 121 [105]
+ CRUSH rule 0 x 122 [45]
+ CRUSH rule 0 x 123 [112]
+ CRUSH rule 0 x 124 [110]
+ CRUSH rule 0 x 125 [66]
+ CRUSH rule 0 x 126 [51]
+ CRUSH rule 0 x 127 [70]
+ CRUSH rule 0 x 128 [90]
+ CRUSH rule 0 x 129 [103]
+ CRUSH rule 0 x 130 [50]
+ CRUSH rule 0 x 131 [23]
+ CRUSH rule 0 x 132 [69]
+ CRUSH rule 0 x 133 [52]
+ CRUSH rule 0 x 134 [78]
+ CRUSH rule 0 x 135 [78]
+ CRUSH rule 0 x 136 [32]
+ CRUSH rule 0 x 137 [92]
+ CRUSH rule 0 x 138 [17]
+ CRUSH rule 0 x 139 [89]
+ CRUSH rule 0 x 140 [39]
+ CRUSH rule 0 x 141 [89]
+ CRUSH rule 0 x 142 [70]
+ CRUSH rule 0 x 143 [51]
+ CRUSH rule 0 x 144 [13]
+ CRUSH rule 0 x 145 [77]
+ CRUSH rule 0 x 146 [96]
+ CRUSH rule 0 x 147 [2]
+ CRUSH rule 0 x 148 [74]
+ CRUSH rule 0 x 149 [76]
+ CRUSH rule 0 x 150 [38]
+ CRUSH rule 0 x 151 [90]
+ CRUSH rule 0 x 152 [49]
+ CRUSH rule 0 x 153 [71]
+ CRUSH rule 0 x 154 [94]
+ CRUSH rule 0 x 155 [75]
+ CRUSH rule 0 x 156 [107]
+ CRUSH rule 0 x 157 [112]
+ CRUSH rule 0 x 158 [26]
+ CRUSH rule 0 x 159 [52]
+ CRUSH rule 0 x 160 [41]
+ CRUSH rule 0 x 161 [84]
+ CRUSH rule 0 x 162 [55]
+ CRUSH rule 0 x 163 [54]
+ CRUSH rule 0 x 164 [45]
+ CRUSH rule 0 x 165 [25]
+ CRUSH rule 0 x 166 [73]
+ CRUSH rule 0 x 167 [89]
+ CRUSH rule 0 x 168 [47]
+ CRUSH rule 0 x 169 [51]
+ CRUSH rule 0 x 170 [68]
+ CRUSH rule 0 x 171 [73]
+ CRUSH rule 0 x 172 [117]
+ CRUSH rule 0 x 173 [13]
+ CRUSH rule 0 x 174 [116]
+ CRUSH rule 0 x 175 [3]
+ CRUSH rule 0 x 176 [94]
+ CRUSH rule 0 x 177 [52]
+ CRUSH rule 0 x 178 [39]
+ CRUSH rule 0 x 179 [72]
+ CRUSH rule 0 x 180 [60]
+ CRUSH rule 0 x 181 [18]
+ CRUSH rule 0 x 182 [22]
+ CRUSH rule 0 x 183 [11]
+ CRUSH rule 0 x 184 [92]
+ CRUSH rule 0 x 185 [97]
+ CRUSH rule 0 x 186 [67]
+ CRUSH rule 0 x 187 [116]
+ CRUSH rule 0 x 188 [69]
+ CRUSH rule 0 x 189 [47]
+ CRUSH rule 0 x 190 [65]
+ CRUSH rule 0 x 191 [49]
+ CRUSH rule 0 x 192 [68]
+ CRUSH rule 0 x 193 [0]
+ CRUSH rule 0 x 194 [62]
+ CRUSH rule 0 x 195 [119]
+ CRUSH rule 0 x 196 [72]
+ CRUSH rule 0 x 197 [106]
+ CRUSH rule 0 x 198 [114]
+ CRUSH rule 0 x 199 [0]
+ CRUSH rule 0 x 200 [35]
+ CRUSH rule 0 x 201 [27]
+ CRUSH rule 0 x 202 [98]
+ CRUSH rule 0 x 203 [36]
+ CRUSH rule 0 x 204 [10]
+ CRUSH rule 0 x 205 [81]
+ CRUSH rule 0 x 206 [49]
+ CRUSH rule 0 x 207 [80]
+ CRUSH rule 0 x 208 [63]
+ CRUSH rule 0 x 209 [85]
+ CRUSH rule 0 x 210 [79]
+ CRUSH rule 0 x 211 [26]
+ CRUSH rule 0 x 212 [28]
+ CRUSH rule 0 x 213 [91]
+ CRUSH rule 0 x 214 [91]
+ CRUSH rule 0 x 215 [61]
+ CRUSH rule 0 x 216 [99]
+ CRUSH rule 0 x 217 [86]
+ CRUSH rule 0 x 218 [70]
+ CRUSH rule 0 x 219 [28]
+ CRUSH rule 0 x 220 [56]
+ CRUSH rule 0 x 221 [0]
+ CRUSH rule 0 x 222 [50]
+ CRUSH rule 0 x 223 [29]
+ CRUSH rule 0 x 224 [52]
+ CRUSH rule 0 x 225 [15]
+ CRUSH rule 0 x 226 [44]
+ CRUSH rule 0 x 227 [42]
+ CRUSH rule 0 x 228 [117]
+ CRUSH rule 0 x 229 [100]
+ CRUSH rule 0 x 230 [41]
+ CRUSH rule 0 x 231 [56]
+ CRUSH rule 0 x 232 [23]
+ CRUSH rule 0 x 233 [88]
+ CRUSH rule 0 x 234 [4]
+ CRUSH rule 0 x 235 [26]
+ CRUSH rule 0 x 236 [32]
+ CRUSH rule 0 x 237 [92]
+ CRUSH rule 0 x 238 [10]
+ CRUSH rule 0 x 239 [15]
+ CRUSH rule 0 x 240 [109]
+ CRUSH rule 0 x 241 [47]
+ CRUSH rule 0 x 242 [24]
+ CRUSH rule 0 x 243 [76]
+ CRUSH rule 0 x 244 [103]
+ CRUSH rule 0 x 245 [27]
+ CRUSH rule 0 x 246 [5]
+ CRUSH rule 0 x 247 [99]
+ CRUSH rule 0 x 248 [8]
+ CRUSH rule 0 x 249 [85]
+ CRUSH rule 0 x 250 [93]
+ CRUSH rule 0 x 251 [28]
+ CRUSH rule 0 x 252 [95]
+ CRUSH rule 0 x 253 [109]
+ CRUSH rule 0 x 254 [80]
+ CRUSH rule 0 x 255 [103]
+ CRUSH rule 0 x 256 [37]
+ CRUSH rule 0 x 257 [69]
+ CRUSH rule 0 x 258 [34]
+ CRUSH rule 0 x 259 [70]
+ CRUSH rule 0 x 260 [98]
+ CRUSH rule 0 x 261 [94]
+ CRUSH rule 0 x 262 [42]
+ CRUSH rule 0 x 263 [65]
+ CRUSH rule 0 x 264 [36]
+ CRUSH rule 0 x 265 [66]
+ CRUSH rule 0 x 266 [75]
+ CRUSH rule 0 x 267 [58]
+ CRUSH rule 0 x 268 [38]
+ CRUSH rule 0 x 269 [86]
+ CRUSH rule 0 x 270 [58]
+ CRUSH rule 0 x 271 [19]
+ CRUSH rule 0 x 272 [73]
+ CRUSH rule 0 x 273 [108]
+ CRUSH rule 0 x 274 [47]
+ CRUSH rule 0 x 275 [92]
+ CRUSH rule 0 x 276 [7]
+ CRUSH rule 0 x 277 [19]
+ CRUSH rule 0 x 278 [116]
+ CRUSH rule 0 x 279 [101]
+ CRUSH rule 0 x 280 [113]
+ CRUSH rule 0 x 281 [14]
+ CRUSH rule 0 x 282 [106]
+ CRUSH rule 0 x 283 [8]
+ CRUSH rule 0 x 284 [10]
+ CRUSH rule 0 x 285 [88]
+ CRUSH rule 0 x 286 [27]
+ CRUSH rule 0 x 287 [84]
+ CRUSH rule 0 x 288 [103]
+ CRUSH rule 0 x 289 [9]
+ CRUSH rule 0 x 290 [115]
+ CRUSH rule 0 x 291 [48]
+ CRUSH rule 0 x 292 [89]
+ CRUSH rule 0 x 293 [27]
+ CRUSH rule 0 x 294 [79]
+ CRUSH rule 0 x 295 [37]
+ CRUSH rule 0 x 296 [56]
+ CRUSH rule 0 x 297 [35]
+ CRUSH rule 0 x 298 [71]
+ CRUSH rule 0 x 299 [116]
+ CRUSH rule 0 x 300 [67]
+ CRUSH rule 0 x 301 [51]
+ CRUSH rule 0 x 302 [78]
+ CRUSH rule 0 x 303 [19]
+ CRUSH rule 0 x 304 [101]
+ CRUSH rule 0 x 305 [81]
+ CRUSH rule 0 x 306 [0]
+ CRUSH rule 0 x 307 [44]
+ CRUSH rule 0 x 308 [91]
+ CRUSH rule 0 x 309 [38]
+ CRUSH rule 0 x 310 [26]
+ CRUSH rule 0 x 311 [36]
+ CRUSH rule 0 x 312 [33]
+ CRUSH rule 0 x 313 [104]
+ CRUSH rule 0 x 314 [28]
+ CRUSH rule 0 x 315 [16]
+ CRUSH rule 0 x 316 [4]
+ CRUSH rule 0 x 317 [118]
+ CRUSH rule 0 x 318 [17]
+ CRUSH rule 0 x 319 [24]
+ CRUSH rule 0 x 320 [36]
+ CRUSH rule 0 x 321 [26]
+ CRUSH rule 0 x 322 [87]
+ CRUSH rule 0 x 323 [73]
+ CRUSH rule 0 x 324 [21]
+ CRUSH rule 0 x 325 [52]
+ CRUSH rule 0 x 326 [111]
+ CRUSH rule 0 x 327 [62]
+ CRUSH rule 0 x 328 [7]
+ CRUSH rule 0 x 329 [93]
+ CRUSH rule 0 x 330 [24]
+ CRUSH rule 0 x 331 [41]
+ CRUSH rule 0 x 332 [61]
+ CRUSH rule 0 x 333 [16]
+ CRUSH rule 0 x 334 [3]
+ CRUSH rule 0 x 335 [71]
+ CRUSH rule 0 x 336 [16]
+ CRUSH rule 0 x 337 [37]
+ CRUSH rule 0 x 338 [109]
+ CRUSH rule 0 x 339 [37]
+ CRUSH rule 0 x 340 [119]
+ CRUSH rule 0 x 341 [63]
+ CRUSH rule 0 x 342 [92]
+ CRUSH rule 0 x 343 [49]
+ CRUSH rule 0 x 344 [103]
+ CRUSH rule 0 x 345 [56]
+ CRUSH rule 0 x 346 [3]
+ CRUSH rule 0 x 347 [106]
+ CRUSH rule 0 x 348 [10]
+ CRUSH rule 0 x 349 [96]
+ CRUSH rule 0 x 350 [63]
+ CRUSH rule 0 x 351 [60]
+ CRUSH rule 0 x 352 [103]
+ CRUSH rule 0 x 353 [49]
+ CRUSH rule 0 x 354 [55]
+ CRUSH rule 0 x 355 [73]
+ CRUSH rule 0 x 356 [114]
+ CRUSH rule 0 x 357 [14]
+ CRUSH rule 0 x 358 [97]
+ CRUSH rule 0 x 359 [4]
+ CRUSH rule 0 x 360 [106]
+ CRUSH rule 0 x 361 [27]
+ CRUSH rule 0 x 362 [28]
+ CRUSH rule 0 x 363 [45]
+ CRUSH rule 0 x 364 [23]
+ CRUSH rule 0 x 365 [24]
+ CRUSH rule 0 x 366 [14]
+ CRUSH rule 0 x 367 [103]
+ CRUSH rule 0 x 368 [103]
+ CRUSH rule 0 x 369 [37]
+ CRUSH rule 0 x 370 [11]
+ CRUSH rule 0 x 371 [34]
+ CRUSH rule 0 x 372 [58]
+ CRUSH rule 0 x 373 [98]
+ CRUSH rule 0 x 374 [110]
+ CRUSH rule 0 x 375 [19]
+ CRUSH rule 0 x 376 [22]
+ CRUSH rule 0 x 377 [98]
+ CRUSH rule 0 x 378 [67]
+ CRUSH rule 0 x 379 [77]
+ CRUSH rule 0 x 380 [69]
+ CRUSH rule 0 x 381 [55]
+ CRUSH rule 0 x 382 [26]
+ CRUSH rule 0 x 383 [48]
+ CRUSH rule 0 x 384 [15]
+ CRUSH rule 0 x 385 [82]
+ CRUSH rule 0 x 386 [108]
+ CRUSH rule 0 x 387 [70]
+ CRUSH rule 0 x 388 [5]
+ CRUSH rule 0 x 389 [14]
+ CRUSH rule 0 x 390 [68]
+ CRUSH rule 0 x 391 [113]
+ CRUSH rule 0 x 392 [72]
+ CRUSH rule 0 x 393 [115]
+ CRUSH rule 0 x 394 [38]
+ CRUSH rule 0 x 395 [0]
+ CRUSH rule 0 x 396 [59]
+ CRUSH rule 0 x 397 [87]
+ CRUSH rule 0 x 398 [44]
+ CRUSH rule 0 x 399 [9]
+ CRUSH rule 0 x 400 [101]
+ CRUSH rule 0 x 401 [79]
+ CRUSH rule 0 x 402 [107]
+ CRUSH rule 0 x 403 [23]
+ CRUSH rule 0 x 404 [76]
+ CRUSH rule 0 x 405 [10]
+ CRUSH rule 0 x 406 [38]
+ CRUSH rule 0 x 407 [70]
+ CRUSH rule 0 x 408 [55]
+ CRUSH rule 0 x 409 [102]
+ CRUSH rule 0 x 410 [59]
+ CRUSH rule 0 x 411 [34]
+ CRUSH rule 0 x 412 [108]
+ CRUSH rule 0 x 413 [54]
+ CRUSH rule 0 x 414 [70]
+ CRUSH rule 0 x 415 [107]
+ CRUSH rule 0 x 416 [79]
+ CRUSH rule 0 x 417 [8]
+ CRUSH rule 0 x 418 [51]
+ CRUSH rule 0 x 419 [117]
+ CRUSH rule 0 x 420 [109]
+ CRUSH rule 0 x 421 [114]
+ CRUSH rule 0 x 422 [109]
+ CRUSH rule 0 x 423 [59]
+ CRUSH rule 0 x 424 [71]
+ CRUSH rule 0 x 425 [101]
+ CRUSH rule 0 x 426 [47]
+ CRUSH rule 0 x 427 [86]
+ CRUSH rule 0 x 428 [68]
+ CRUSH rule 0 x 429 [76]
+ CRUSH rule 0 x 430 [9]
+ CRUSH rule 0 x 431 [105]
+ CRUSH rule 0 x 432 [46]
+ CRUSH rule 0 x 433 [6]
+ CRUSH rule 0 x 434 [64]
+ CRUSH rule 0 x 435 [16]
+ CRUSH rule 0 x 436 [89]
+ CRUSH rule 0 x 437 [29]
+ CRUSH rule 0 x 438 [105]
+ CRUSH rule 0 x 439 [29]
+ CRUSH rule 0 x 440 [38]
+ CRUSH rule 0 x 441 [112]
+ CRUSH rule 0 x 442 [55]
+ CRUSH rule 0 x 443 [44]
+ CRUSH rule 0 x 444 [11]
+ CRUSH rule 0 x 445 [19]
+ CRUSH rule 0 x 446 [40]
+ CRUSH rule 0 x 447 [100]
+ CRUSH rule 0 x 448 [7]
+ CRUSH rule 0 x 449 [67]
+ CRUSH rule 0 x 450 [117]
+ CRUSH rule 0 x 451 [93]
+ CRUSH rule 0 x 452 [70]
+ CRUSH rule 0 x 453 [82]
+ CRUSH rule 0 x 454 [53]
+ CRUSH rule 0 x 455 [91]
+ CRUSH rule 0 x 456 [17]
+ CRUSH rule 0 x 457 [113]
+ CRUSH rule 0 x 458 [119]
+ CRUSH rule 0 x 459 [25]
+ CRUSH rule 0 x 460 [11]
+ CRUSH rule 0 x 461 [21]
+ CRUSH rule 0 x 462 [25]
+ CRUSH rule 0 x 463 [6]
+ CRUSH rule 0 x 464 [19]
+ CRUSH rule 0 x 465 [29]
+ CRUSH rule 0 x 466 [66]
+ CRUSH rule 0 x 467 [27]
+ CRUSH rule 0 x 468 [97]
+ CRUSH rule 0 x 469 [98]
+ CRUSH rule 0 x 470 [50]
+ CRUSH rule 0 x 471 [40]
+ CRUSH rule 0 x 472 [74]
+ CRUSH rule 0 x 473 [95]
+ CRUSH rule 0 x 474 [51]
+ CRUSH rule 0 x 475 [3]
+ CRUSH rule 0 x 476 [110]
+ CRUSH rule 0 x 477 [25]
+ CRUSH rule 0 x 478 [19]
+ CRUSH rule 0 x 479 [70]
+ CRUSH rule 0 x 480 [62]
+ CRUSH rule 0 x 481 [26]
+ CRUSH rule 0 x 482 [84]
+ CRUSH rule 0 x 483 [36]
+ CRUSH rule 0 x 484 [37]
+ CRUSH rule 0 x 485 [84]
+ CRUSH rule 0 x 486 [92]
+ CRUSH rule 0 x 487 [106]
+ CRUSH rule 0 x 488 [42]
+ CRUSH rule 0 x 489 [76]
+ CRUSH rule 0 x 490 [68]
+ CRUSH rule 0 x 491 [80]
+ CRUSH rule 0 x 492 [21]
+ CRUSH rule 0 x 493 [99]
+ CRUSH rule 0 x 494 [4]
+ CRUSH rule 0 x 495 [40]
+ CRUSH rule 0 x 496 [13]
+ CRUSH rule 0 x 497 [102]
+ CRUSH rule 0 x 498 [68]
+ CRUSH rule 0 x 499 [22]
+ CRUSH rule 0 x 500 [50]
+ CRUSH rule 0 x 501 [60]
+ CRUSH rule 0 x 502 [11]
+ CRUSH rule 0 x 503 [117]
+ CRUSH rule 0 x 504 [90]
+ CRUSH rule 0 x 505 [91]
+ CRUSH rule 0 x 506 [82]
+ CRUSH rule 0 x 507 [6]
+ CRUSH rule 0 x 508 [34]
+ CRUSH rule 0 x 509 [88]
+ CRUSH rule 0 x 510 [11]
+ CRUSH rule 0 x 511 [72]
+ CRUSH rule 0 x 512 [118]
+ CRUSH rule 0 x 513 [22]
+ CRUSH rule 0 x 514 [82]
+ CRUSH rule 0 x 515 [27]
+ CRUSH rule 0 x 516 [66]
+ CRUSH rule 0 x 517 [83]
+ CRUSH rule 0 x 518 [18]
+ CRUSH rule 0 x 519 [67]
+ CRUSH rule 0 x 520 [15]
+ CRUSH rule 0 x 521 [63]
+ CRUSH rule 0 x 522 [4]
+ CRUSH rule 0 x 523 [36]
+ CRUSH rule 0 x 524 [33]
+ CRUSH rule 0 x 525 [63]
+ CRUSH rule 0 x 526 [83]
+ CRUSH rule 0 x 527 [37]
+ CRUSH rule 0 x 528 [108]
+ CRUSH rule 0 x 529 [107]
+ CRUSH rule 0 x 530 [49]
+ CRUSH rule 0 x 531 [27]
+ CRUSH rule 0 x 532 [68]
+ CRUSH rule 0 x 533 [5]
+ CRUSH rule 0 x 534 [97]
+ CRUSH rule 0 x 535 [48]
+ CRUSH rule 0 x 536 [3]
+ CRUSH rule 0 x 537 [116]
+ CRUSH rule 0 x 538 [85]
+ CRUSH rule 0 x 539 [10]
+ CRUSH rule 0 x 540 [100]
+ CRUSH rule 0 x 541 [111]
+ CRUSH rule 0 x 542 [50]
+ CRUSH rule 0 x 543 [45]
+ CRUSH rule 0 x 544 [106]
+ CRUSH rule 0 x 545 [43]
+ CRUSH rule 0 x 546 [108]
+ CRUSH rule 0 x 547 [27]
+ CRUSH rule 0 x 548 [53]
+ CRUSH rule 0 x 549 [60]
+ CRUSH rule 0 x 550 [47]
+ CRUSH rule 0 x 551 [14]
+ CRUSH rule 0 x 552 [70]
+ CRUSH rule 0 x 553 [96]
+ CRUSH rule 0 x 554 [61]
+ CRUSH rule 0 x 555 [76]
+ CRUSH rule 0 x 556 [106]
+ CRUSH rule 0 x 557 [39]
+ CRUSH rule 0 x 558 [70]
+ CRUSH rule 0 x 559 [106]
+ CRUSH rule 0 x 560 [94]
+ CRUSH rule 0 x 561 [27]
+ CRUSH rule 0 x 562 [97]
+ CRUSH rule 0 x 563 [64]
+ CRUSH rule 0 x 564 [96]
+ CRUSH rule 0 x 565 [66]
+ CRUSH rule 0 x 566 [27]
+ CRUSH rule 0 x 567 [88]
+ CRUSH rule 0 x 568 [17]
+ CRUSH rule 0 x 569 [102]
+ CRUSH rule 0 x 570 [7]
+ CRUSH rule 0 x 571 [95]
+ CRUSH rule 0 x 572 [62]
+ CRUSH rule 0 x 573 [51]
+ CRUSH rule 0 x 574 [89]
+ CRUSH rule 0 x 575 [19]
+ CRUSH rule 0 x 576 [112]
+ CRUSH rule 0 x 577 [8]
+ CRUSH rule 0 x 578 [64]
+ CRUSH rule 0 x 579 [78]
+ CRUSH rule 0 x 580 [68]
+ CRUSH rule 0 x 581 [55]
+ CRUSH rule 0 x 582 [27]
+ CRUSH rule 0 x 583 [74]
+ CRUSH rule 0 x 584 [72]
+ CRUSH rule 0 x 585 [88]
+ CRUSH rule 0 x 586 [33]
+ CRUSH rule 0 x 587 [106]
+ CRUSH rule 0 x 588 [0]
+ CRUSH rule 0 x 589 [7]
+ CRUSH rule 0 x 590 [59]
+ CRUSH rule 0 x 591 [42]
+ CRUSH rule 0 x 592 [45]
+ CRUSH rule 0 x 593 [89]
+ CRUSH rule 0 x 594 [27]
+ CRUSH rule 0 x 595 [7]
+ CRUSH rule 0 x 596 [82]
+ CRUSH rule 0 x 597 [72]
+ CRUSH rule 0 x 598 [34]
+ CRUSH rule 0 x 599 [119]
+ CRUSH rule 0 x 600 [9]
+ CRUSH rule 0 x 601 [104]
+ CRUSH rule 0 x 602 [48]
+ CRUSH rule 0 x 603 [24]
+ CRUSH rule 0 x 604 [89]
+ CRUSH rule 0 x 605 [104]
+ CRUSH rule 0 x 606 [49]
+ CRUSH rule 0 x 607 [95]
+ CRUSH rule 0 x 608 [49]
+ CRUSH rule 0 x 609 [61]
+ CRUSH rule 0 x 610 [106]
+ CRUSH rule 0 x 611 [66]
+ CRUSH rule 0 x 612 [103]
+ CRUSH rule 0 x 613 [84]
+ CRUSH rule 0 x 614 [81]
+ CRUSH rule 0 x 615 [61]
+ CRUSH rule 0 x 616 [41]
+ CRUSH rule 0 x 617 [111]
+ CRUSH rule 0 x 618 [3]
+ CRUSH rule 0 x 619 [92]
+ CRUSH rule 0 x 620 [108]
+ CRUSH rule 0 x 621 [106]
+ CRUSH rule 0 x 622 [67]
+ CRUSH rule 0 x 623 [94]
+ CRUSH rule 0 x 624 [115]
+ CRUSH rule 0 x 625 [111]
+ CRUSH rule 0 x 626 [3]
+ CRUSH rule 0 x 627 [19]
+ CRUSH rule 0 x 628 [65]
+ CRUSH rule 0 x 629 [119]
+ CRUSH rule 0 x 630 [109]
+ CRUSH rule 0 x 631 [48]
+ CRUSH rule 0 x 632 [81]
+ CRUSH rule 0 x 633 [65]
+ CRUSH rule 0 x 634 [87]
+ CRUSH rule 0 x 635 [107]
+ CRUSH rule 0 x 636 [23]
+ CRUSH rule 0 x 637 [102]
+ CRUSH rule 0 x 638 [43]
+ CRUSH rule 0 x 639 [31]
+ CRUSH rule 0 x 640 [113]
+ CRUSH rule 0 x 641 [45]
+ CRUSH rule 0 x 642 [47]
+ CRUSH rule 0 x 643 [64]
+ CRUSH rule 0 x 644 [31]
+ CRUSH rule 0 x 645 [76]
+ CRUSH rule 0 x 646 [37]
+ CRUSH rule 0 x 647 [58]
+ CRUSH rule 0 x 648 [31]
+ CRUSH rule 0 x 649 [88]
+ CRUSH rule 0 x 650 [116]
+ CRUSH rule 0 x 651 [97]
+ CRUSH rule 0 x 652 [57]
+ CRUSH rule 0 x 653 [38]
+ CRUSH rule 0 x 654 [49]
+ CRUSH rule 0 x 655 [89]
+ CRUSH rule 0 x 656 [0]
+ CRUSH rule 0 x 657 [47]
+ CRUSH rule 0 x 658 [75]
+ CRUSH rule 0 x 659 [26]
+ CRUSH rule 0 x 660 [65]
+ CRUSH rule 0 x 661 [91]
+ CRUSH rule 0 x 662 [111]
+ CRUSH rule 0 x 663 [88]
+ CRUSH rule 0 x 664 [59]
+ CRUSH rule 0 x 665 [78]
+ CRUSH rule 0 x 666 [112]
+ CRUSH rule 0 x 667 [97]
+ CRUSH rule 0 x 668 [97]
+ CRUSH rule 0 x 669 [85]
+ CRUSH rule 0 x 670 [41]
+ CRUSH rule 0 x 671 [116]
+ CRUSH rule 0 x 672 [44]
+ CRUSH rule 0 x 673 [83]
+ CRUSH rule 0 x 674 [59]
+ CRUSH rule 0 x 675 [88]
+ CRUSH rule 0 x 676 [62]
+ CRUSH rule 0 x 677 [88]
+ CRUSH rule 0 x 678 [98]
+ CRUSH rule 0 x 679 [70]
+ CRUSH rule 0 x 680 [55]
+ CRUSH rule 0 x 681 [53]
+ CRUSH rule 0 x 682 [27]
+ CRUSH rule 0 x 683 [57]
+ CRUSH rule 0 x 684 [98]
+ CRUSH rule 0 x 685 [106]
+ CRUSH rule 0 x 686 [86]
+ CRUSH rule 0 x 687 [49]
+ CRUSH rule 0 x 688 [16]
+ CRUSH rule 0 x 689 [6]
+ CRUSH rule 0 x 690 [43]
+ CRUSH rule 0 x 691 [34]
+ CRUSH rule 0 x 692 [40]
+ CRUSH rule 0 x 693 [29]
+ CRUSH rule 0 x 694 [6]
+ CRUSH rule 0 x 695 [31]
+ CRUSH rule 0 x 696 [36]
+ CRUSH rule 0 x 697 [96]
+ CRUSH rule 0 x 698 [61]
+ CRUSH rule 0 x 699 [47]
+ CRUSH rule 0 x 700 [0]
+ CRUSH rule 0 x 701 [42]
+ CRUSH rule 0 x 702 [0]
+ CRUSH rule 0 x 703 [92]
+ CRUSH rule 0 x 704 [10]
+ CRUSH rule 0 x 705 [105]
+ CRUSH rule 0 x 706 [74]
+ CRUSH rule 0 x 707 [0]
+ CRUSH rule 0 x 708 [84]
+ CRUSH rule 0 x 709 [114]
+ CRUSH rule 0 x 710 [94]
+ CRUSH rule 0 x 711 [68]
+ CRUSH rule 0 x 712 [34]
+ CRUSH rule 0 x 713 [29]
+ CRUSH rule 0 x 714 [81]
+ CRUSH rule 0 x 715 [71]
+ CRUSH rule 0 x 716 [40]
+ CRUSH rule 0 x 717 [61]
+ CRUSH rule 0 x 718 [40]
+ CRUSH rule 0 x 719 [59]
+ CRUSH rule 0 x 720 [69]
+ CRUSH rule 0 x 721 [62]
+ CRUSH rule 0 x 722 [115]
+ CRUSH rule 0 x 723 [117]
+ CRUSH rule 0 x 724 [45]
+ CRUSH rule 0 x 725 [53]
+ CRUSH rule 0 x 726 [84]
+ CRUSH rule 0 x 727 [109]
+ CRUSH rule 0 x 728 [76]
+ CRUSH rule 0 x 729 [108]
+ CRUSH rule 0 x 730 [28]
+ CRUSH rule 0 x 731 [78]
+ CRUSH rule 0 x 732 [55]
+ CRUSH rule 0 x 733 [84]
+ CRUSH rule 0 x 734 [27]
+ CRUSH rule 0 x 735 [83]
+ CRUSH rule 0 x 736 [70]
+ CRUSH rule 0 x 737 [117]
+ CRUSH rule 0 x 738 [118]
+ CRUSH rule 0 x 739 [87]
+ CRUSH rule 0 x 740 [29]
+ CRUSH rule 0 x 741 [96]
+ CRUSH rule 0 x 742 [106]
+ CRUSH rule 0 x 743 [105]
+ CRUSH rule 0 x 744 [23]
+ CRUSH rule 0 x 745 [28]
+ CRUSH rule 0 x 746 [18]
+ CRUSH rule 0 x 747 [65]
+ CRUSH rule 0 x 748 [48]
+ CRUSH rule 0 x 749 [102]
+ CRUSH rule 0 x 750 [50]
+ CRUSH rule 0 x 751 [36]
+ CRUSH rule 0 x 752 [69]
+ CRUSH rule 0 x 753 [9]
+ CRUSH rule 0 x 754 [9]
+ CRUSH rule 0 x 755 [98]
+ CRUSH rule 0 x 756 [113]
+ CRUSH rule 0 x 757 [47]
+ CRUSH rule 0 x 758 [57]
+ CRUSH rule 0 x 759 [74]
+ CRUSH rule 0 x 760 [53]
+ CRUSH rule 0 x 761 [78]
+ CRUSH rule 0 x 762 [87]
+ CRUSH rule 0 x 763 [13]
+ CRUSH rule 0 x 764 [106]
+ CRUSH rule 0 x 765 [109]
+ CRUSH rule 0 x 766 [76]
+ CRUSH rule 0 x 767 [41]
+ CRUSH rule 0 x 768 [13]
+ CRUSH rule 0 x 769 [91]
+ CRUSH rule 0 x 770 [105]
+ CRUSH rule 0 x 771 [10]
+ CRUSH rule 0 x 772 [8]
+ CRUSH rule 0 x 773 [116]
+ CRUSH rule 0 x 774 [100]
+ CRUSH rule 0 x 775 [15]
+ CRUSH rule 0 x 776 [69]
+ CRUSH rule 0 x 777 [76]
+ CRUSH rule 0 x 778 [38]
+ CRUSH rule 0 x 779 [46]
+ CRUSH rule 0 x 780 [63]
+ CRUSH rule 0 x 781 [19]
+ CRUSH rule 0 x 782 [117]
+ CRUSH rule 0 x 783 [60]
+ CRUSH rule 0 x 784 [82]
+ CRUSH rule 0 x 785 [27]
+ CRUSH rule 0 x 786 [41]
+ CRUSH rule 0 x 787 [13]
+ CRUSH rule 0 x 788 [4]
+ CRUSH rule 0 x 789 [50]
+ CRUSH rule 0 x 790 [58]
+ CRUSH rule 0 x 791 [96]
+ CRUSH rule 0 x 792 [45]
+ CRUSH rule 0 x 793 [6]
+ CRUSH rule 0 x 794 [14]
+ CRUSH rule 0 x 795 [51]
+ CRUSH rule 0 x 796 [114]
+ CRUSH rule 0 x 797 [79]
+ CRUSH rule 0 x 798 [42]
+ CRUSH rule 0 x 799 [48]
+ CRUSH rule 0 x 800 [91]
+ CRUSH rule 0 x 801 [2]
+ CRUSH rule 0 x 802 [116]
+ CRUSH rule 0 x 803 [37]
+ CRUSH rule 0 x 804 [6]
+ CRUSH rule 0 x 805 [96]
+ CRUSH rule 0 x 806 [67]
+ CRUSH rule 0 x 807 [47]
+ CRUSH rule 0 x 808 [76]
+ CRUSH rule 0 x 809 [27]
+ CRUSH rule 0 x 810 [119]
+ CRUSH rule 0 x 811 [75]
+ CRUSH rule 0 x 812 [25]
+ CRUSH rule 0 x 813 [64]
+ CRUSH rule 0 x 814 [110]
+ CRUSH rule 0 x 815 [84]
+ CRUSH rule 0 x 816 [25]
+ CRUSH rule 0 x 817 [40]
+ CRUSH rule 0 x 818 [34]
+ CRUSH rule 0 x 819 [88]
+ CRUSH rule 0 x 820 [104]
+ CRUSH rule 0 x 821 [58]
+ CRUSH rule 0 x 822 [29]
+ CRUSH rule 0 x 823 [100]
+ CRUSH rule 0 x 824 [102]
+ CRUSH rule 0 x 825 [47]
+ CRUSH rule 0 x 826 [45]
+ CRUSH rule 0 x 827 [101]
+ CRUSH rule 0 x 828 [60]
+ CRUSH rule 0 x 829 [45]
+ CRUSH rule 0 x 830 [51]
+ CRUSH rule 0 x 831 [6]
+ CRUSH rule 0 x 832 [57]
+ CRUSH rule 0 x 833 [34]
+ CRUSH rule 0 x 834 [90]
+ CRUSH rule 0 x 835 [55]
+ CRUSH rule 0 x 836 [38]
+ CRUSH rule 0 x 837 [51]
+ CRUSH rule 0 x 838 [6]
+ CRUSH rule 0 x 839 [106]
+ CRUSH rule 0 x 840 [33]
+ CRUSH rule 0 x 841 [110]
+ CRUSH rule 0 x 842 [66]
+ CRUSH rule 0 x 843 [62]
+ CRUSH rule 0 x 844 [74]
+ CRUSH rule 0 x 845 [74]
+ CRUSH rule 0 x 846 [98]
+ CRUSH rule 0 x 847 [10]
+ CRUSH rule 0 x 848 [89]
+ CRUSH rule 0 x 849 [42]
+ CRUSH rule 0 x 850 [40]
+ CRUSH rule 0 x 851 [65]
+ CRUSH rule 0 x 852 [31]
+ CRUSH rule 0 x 853 [49]
+ CRUSH rule 0 x 854 [83]
+ CRUSH rule 0 x 855 [2]
+ CRUSH rule 0 x 856 [6]
+ CRUSH rule 0 x 857 [15]
+ CRUSH rule 0 x 858 [10]
+ CRUSH rule 0 x 859 [14]
+ CRUSH rule 0 x 860 [114]
+ CRUSH rule 0 x 861 [1]
+ CRUSH rule 0 x 862 [22]
+ CRUSH rule 0 x 863 [79]
+ CRUSH rule 0 x 864 [68]
+ CRUSH rule 0 x 865 [25]
+ CRUSH rule 0 x 866 [18]
+ CRUSH rule 0 x 867 [53]
+ CRUSH rule 0 x 868 [81]
+ CRUSH rule 0 x 869 [111]
+ CRUSH rule 0 x 870 [73]
+ CRUSH rule 0 x 871 [25]
+ CRUSH rule 0 x 872 [39]
+ CRUSH rule 0 x 873 [92]
+ CRUSH rule 0 x 874 [96]
+ CRUSH rule 0 x 875 [115]
+ CRUSH rule 0 x 876 [98]
+ CRUSH rule 0 x 877 [73]
+ CRUSH rule 0 x 878 [64]
+ CRUSH rule 0 x 879 [15]
+ CRUSH rule 0 x 880 [56]
+ CRUSH rule 0 x 881 [109]
+ CRUSH rule 0 x 882 [60]
+ CRUSH rule 0 x 883 [93]
+ CRUSH rule 0 x 884 [67]
+ CRUSH rule 0 x 885 [31]
+ CRUSH rule 0 x 886 [2]
+ CRUSH rule 0 x 887 [5]
+ CRUSH rule 0 x 888 [16]
+ CRUSH rule 0 x 889 [27]
+ CRUSH rule 0 x 890 [48]
+ CRUSH rule 0 x 891 [86]
+ CRUSH rule 0 x 892 [64]
+ CRUSH rule 0 x 893 [118]
+ CRUSH rule 0 x 894 [16]
+ CRUSH rule 0 x 895 [40]
+ CRUSH rule 0 x 896 [97]
+ CRUSH rule 0 x 897 [107]
+ CRUSH rule 0 x 898 [10]
+ CRUSH rule 0 x 899 [75]
+ CRUSH rule 0 x 900 [102]
+ CRUSH rule 0 x 901 [66]
+ CRUSH rule 0 x 902 [102]
+ CRUSH rule 0 x 903 [5]
+ CRUSH rule 0 x 904 [50]
+ CRUSH rule 0 x 905 [99]
+ CRUSH rule 0 x 906 [75]
+ CRUSH rule 0 x 907 [47]
+ CRUSH rule 0 x 908 [96]
+ CRUSH rule 0 x 909 [94]
+ CRUSH rule 0 x 910 [88]
+ CRUSH rule 0 x 911 [102]
+ CRUSH rule 0 x 912 [91]
+ CRUSH rule 0 x 913 [29]
+ CRUSH rule 0 x 914 [84]
+ CRUSH rule 0 x 915 [70]
+ CRUSH rule 0 x 916 [32]
+ CRUSH rule 0 x 917 [43]
+ CRUSH rule 0 x 918 [91]
+ CRUSH rule 0 x 919 [13]
+ CRUSH rule 0 x 920 [18]
+ CRUSH rule 0 x 921 [104]
+ CRUSH rule 0 x 922 [33]
+ CRUSH rule 0 x 923 [28]
+ CRUSH rule 0 x 924 [69]
+ CRUSH rule 0 x 925 [71]
+ CRUSH rule 0 x 926 [64]
+ CRUSH rule 0 x 927 [99]
+ CRUSH rule 0 x 928 [13]
+ CRUSH rule 0 x 929 [117]
+ CRUSH rule 0 x 930 [31]
+ CRUSH rule 0 x 931 [83]
+ CRUSH rule 0 x 932 [60]
+ CRUSH rule 0 x 933 [63]
+ CRUSH rule 0 x 934 [68]
+ CRUSH rule 0 x 935 [31]
+ CRUSH rule 0 x 936 [65]
+ CRUSH rule 0 x 937 [110]
+ CRUSH rule 0 x 938 [29]
+ CRUSH rule 0 x 939 [77]
+ CRUSH rule 0 x 940 [76]
+ CRUSH rule 0 x 941 [66]
+ CRUSH rule 0 x 942 [83]
+ CRUSH rule 0 x 943 [32]
+ CRUSH rule 0 x 944 [113]
+ CRUSH rule 0 x 945 [71]
+ CRUSH rule 0 x 946 [37]
+ CRUSH rule 0 x 947 [107]
+ CRUSH rule 0 x 948 [55]
+ CRUSH rule 0 x 949 [11]
+ CRUSH rule 0 x 950 [96]
+ CRUSH rule 0 x 951 [40]
+ CRUSH rule 0 x 952 [93]
+ CRUSH rule 0 x 953 [55]
+ CRUSH rule 0 x 954 [84]
+ CRUSH rule 0 x 955 [31]
+ CRUSH rule 0 x 956 [72]
+ CRUSH rule 0 x 957 [3]
+ CRUSH rule 0 x 958 [23]
+ CRUSH rule 0 x 959 [42]
+ CRUSH rule 0 x 960 [113]
+ CRUSH rule 0 x 961 [116]
+ CRUSH rule 0 x 962 [13]
+ CRUSH rule 0 x 963 [0]
+ CRUSH rule 0 x 964 [59]
+ CRUSH rule 0 x 965 [47]
+ CRUSH rule 0 x 966 [88]
+ CRUSH rule 0 x 967 [71]
+ CRUSH rule 0 x 968 [73]
+ CRUSH rule 0 x 969 [53]
+ CRUSH rule 0 x 970 [111]
+ CRUSH rule 0 x 971 [87]
+ CRUSH rule 0 x 972 [5]
+ CRUSH rule 0 x 973 [113]
+ CRUSH rule 0 x 974 [49]
+ CRUSH rule 0 x 975 [83]
+ CRUSH rule 0 x 976 [81]
+ CRUSH rule 0 x 977 [95]
+ CRUSH rule 0 x 978 [35]
+ CRUSH rule 0 x 979 [98]
+ CRUSH rule 0 x 980 [52]
+ CRUSH rule 0 x 981 [89]
+ CRUSH rule 0 x 982 [1]
+ CRUSH rule 0 x 983 [34]
+ CRUSH rule 0 x 984 [78]
+ CRUSH rule 0 x 985 [99]
+ CRUSH rule 0 x 986 [4]
+ CRUSH rule 0 x 987 [78]
+ CRUSH rule 0 x 988 [79]
+ CRUSH rule 0 x 989 [87]
+ CRUSH rule 0 x 990 [47]
+ CRUSH rule 0 x 991 [61]
+ CRUSH rule 0 x 992 [83]
+ CRUSH rule 0 x 993 [74]
+ CRUSH rule 0 x 994 [74]
+ CRUSH rule 0 x 995 [100]
+ CRUSH rule 0 x 996 [41]
+ CRUSH rule 0 x 997 [89]
+ CRUSH rule 0 x 998 [92]
+ CRUSH rule 0 x 999 [117]
+ CRUSH rule 0 x 1000 [9]
+ CRUSH rule 0 x 1001 [49]
+ CRUSH rule 0 x 1002 [99]
+ CRUSH rule 0 x 1003 [43]
+ CRUSH rule 0 x 1004 [89]
+ CRUSH rule 0 x 1005 [105]
+ CRUSH rule 0 x 1006 [45]
+ CRUSH rule 0 x 1007 [19]
+ CRUSH rule 0 x 1008 [31]
+ CRUSH rule 0 x 1009 [19]
+ CRUSH rule 0 x 1010 [42]
+ CRUSH rule 0 x 1011 [25]
+ CRUSH rule 0 x 1012 [68]
+ CRUSH rule 0 x 1013 [5]
+ CRUSH rule 0 x 1014 [33]
+ CRUSH rule 0 x 1015 [14]
+ CRUSH rule 0 x 1016 [88]
+ CRUSH rule 0 x 1017 [0]
+ CRUSH rule 0 x 1018 [63]
+ CRUSH rule 0 x 1019 [104]
+ CRUSH rule 0 x 1020 [96]
+ CRUSH rule 0 x 1021 [117]
+ CRUSH rule 0 x 1022 [73]
+ CRUSH rule 0 x 1023 [0]
+ rule 0 (data) num_rep 1 result size == 1:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28]
+ CRUSH rule 0 x 1 [80,13]
+ CRUSH rule 0 x 2 [91,11]
+ CRUSH rule 0 x 3 [51,13]
+ CRUSH rule 0 x 4 [50,101]
+ CRUSH rule 0 x 5 [89,113]
+ CRUSH rule 0 x 6 [91,109]
+ CRUSH rule 0 x 7 [104,51]
+ CRUSH rule 0 x 8 [78,75]
+ CRUSH rule 0 x 9 [101,80]
+ CRUSH rule 0 x 10 [61,4]
+ CRUSH rule 0 x 11 [13,43]
+ CRUSH rule 0 x 12 [83,0]
+ CRUSH rule 0 x 13 [108,22]
+ CRUSH rule 0 x 14 [105,9]
+ CRUSH rule 0 x 15 [18,7]
+ CRUSH rule 0 x 16 [103,4]
+ CRUSH rule 0 x 17 [85,80]
+ CRUSH rule 0 x 18 [11,71]
+ CRUSH rule 0 x 19 [75,114]
+ CRUSH rule 0 x 20 [79,64]
+ CRUSH rule 0 x 21 [84,7]
+ CRUSH rule 0 x 22 [23,66]
+ CRUSH rule 0 x 23 [118,6]
+ CRUSH rule 0 x 24 [83,111]
+ CRUSH rule 0 x 25 [81,116]
+ CRUSH rule 0 x 26 [38,69]
+ CRUSH rule 0 x 27 [76,103]
+ CRUSH rule 0 x 28 [76,103]
+ CRUSH rule 0 x 29 [8,46]
+ CRUSH rule 0 x 30 [94,7]
+ CRUSH rule 0 x 31 [76,35]
+ CRUSH rule 0 x 32 [72,35]
+ CRUSH rule 0 x 33 [77,104]
+ CRUSH rule 0 x 34 [74,67]
+ CRUSH rule 0 x 35 [22,115]
+ CRUSH rule 0 x 36 [104,33]
+ CRUSH rule 0 x 37 [38,57]
+ CRUSH rule 0 x 38 [72,11]
+ CRUSH rule 0 x 39 [68,73]
+ CRUSH rule 0 x 40 [103,76]
+ CRUSH rule 0 x 41 [85,4]
+ CRUSH rule 0 x 42 [106,39]
+ CRUSH rule 0 x 43 [10,115]
+ CRUSH rule 0 x 44 [101,66]
+ CRUSH rule 0 x 45 [8,80]
+ CRUSH rule 0 x 46 [65,66]
+ CRUSH rule 0 x 47 [106,41]
+ CRUSH rule 0 x 48 [34,4]
+ CRUSH rule 0 x 49 [0,27]
+ CRUSH rule 0 x 50 [42,14]
+ CRUSH rule 0 x 51 [104,59]
+ CRUSH rule 0 x 52 [83,14]
+ CRUSH rule 0 x 53 [32,93]
+ CRUSH rule 0 x 54 [28,77]
+ CRUSH rule 0 x 55 [14,94]
+ CRUSH rule 0 x 56 [21,112]
+ CRUSH rule 0 x 57 [93,88]
+ CRUSH rule 0 x 58 [45,1]
+ CRUSH rule 0 x 59 [80,31]
+ CRUSH rule 0 x 60 [90,33]
+ CRUSH rule 0 x 61 [88,39]
+ CRUSH rule 0 x 62 [81,0]
+ CRUSH rule 0 x 63 [79,96]
+ CRUSH rule 0 x 64 [1,8]
+ CRUSH rule 0 x 65 [13,92]
+ CRUSH rule 0 x 66 [48,79]
+ CRUSH rule 0 x 67 [94,91]
+ CRUSH rule 0 x 68 [102,105]
+ CRUSH rule 0 x 69 [62,4]
+ CRUSH rule 0 x 70 [84,27]
+ CRUSH rule 0 x 71 [55,108]
+ CRUSH rule 0 x 72 [97,42]
+ CRUSH rule 0 x 73 [64,81]
+ CRUSH rule 0 x 74 [96,41]
+ CRUSH rule 0 x 75 [29,98]
+ CRUSH rule 0 x 76 [55,111]
+ CRUSH rule 0 x 77 [107,21]
+ CRUSH rule 0 x 78 [31,100]
+ CRUSH rule 0 x 79 [64,75]
+ CRUSH rule 0 x 80 [0,67]
+ CRUSH rule 0 x 81 [71,52]
+ CRUSH rule 0 x 82 [37,0]
+ CRUSH rule 0 x 83 [92,75]
+ CRUSH rule 0 x 84 [49,40]
+ CRUSH rule 0 x 85 [54,71]
+ CRUSH rule 0 x 86 [37,14]
+ CRUSH rule 0 x 87 [116,3]
+ CRUSH rule 0 x 88 [38,95]
+ CRUSH rule 0 x 89 [76,41]
+ CRUSH rule 0 x 90 [14,98]
+ CRUSH rule 0 x 91 [93,114]
+ CRUSH rule 0 x 92 [86,13]
+ CRUSH rule 0 x 93 [44,41]
+ CRUSH rule 0 x 94 [61,18]
+ CRUSH rule 0 x 95 [93,98]
+ CRUSH rule 0 x 96 [66,25]
+ CRUSH rule 0 x 97 [111,4]
+ CRUSH rule 0 x 98 [66,16]
+ CRUSH rule 0 x 99 [78,22]
+ CRUSH rule 0 x 100 [28,4]
+ CRUSH rule 0 x 101 [84,51]
+ CRUSH rule 0 x 102 [82,93]
+ CRUSH rule 0 x 103 [66,4]
+ CRUSH rule 0 x 104 [14,10]
+ CRUSH rule 0 x 105 [87,100]
+ CRUSH rule 0 x 106 [69,66]
+ CRUSH rule 0 x 107 [1,41]
+ CRUSH rule 0 x 108 [94,75]
+ CRUSH rule 0 x 109 [112,87]
+ CRUSH rule 0 x 110 [54,10]
+ CRUSH rule 0 x 111 [10,112]
+ CRUSH rule 0 x 112 [89,11]
+ CRUSH rule 0 x 113 [69,26]
+ CRUSH rule 0 x 114 [79,22]
+ CRUSH rule 0 x 115 [50,65]
+ CRUSH rule 0 x 116 [96,53]
+ CRUSH rule 0 x 117 [87,86]
+ CRUSH rule 0 x 118 [23,106]
+ CRUSH rule 0 x 119 [104,14]
+ CRUSH rule 0 x 120 [57,42]
+ CRUSH rule 0 x 121 [105,50]
+ CRUSH rule 0 x 122 [45,68]
+ CRUSH rule 0 x 123 [112,15]
+ CRUSH rule 0 x 124 [110,19]
+ CRUSH rule 0 x 125 [66,71]
+ CRUSH rule 0 x 126 [51,64]
+ CRUSH rule 0 x 127 [70,13]
+ CRUSH rule 0 x 128 [90,47]
+ CRUSH rule 0 x 129 [103,108]
+ CRUSH rule 0 x 130 [50,17]
+ CRUSH rule 0 x 131 [23,60]
+ CRUSH rule 0 x 132 [69,58]
+ CRUSH rule 0 x 133 [52,85]
+ CRUSH rule 0 x 134 [78,81]
+ CRUSH rule 0 x 135 [78,6]
+ CRUSH rule 0 x 136 [32,83]
+ CRUSH rule 0 x 137 [92,87]
+ CRUSH rule 0 x 138 [17,74]
+ CRUSH rule 0 x 139 [89,92]
+ CRUSH rule 0 x 140 [39,1]
+ CRUSH rule 0 x 141 [89,96]
+ CRUSH rule 0 x 142 [70,73]
+ CRUSH rule 0 x 143 [51,26]
+ CRUSH rule 0 x 144 [13,55]
+ CRUSH rule 0 x 145 [77,100]
+ CRUSH rule 0 x 146 [96,73]
+ CRUSH rule 0 x 147 [2,89]
+ CRUSH rule 0 x 148 [74,91]
+ CRUSH rule 0 x 149 [76,19]
+ CRUSH rule 0 x 150 [38,105]
+ CRUSH rule 0 x 151 [90,85]
+ CRUSH rule 0 x 152 [49,84]
+ CRUSH rule 0 x 153 [71,42]
+ CRUSH rule 0 x 154 [94,23]
+ CRUSH rule 0 x 155 [75,119]
+ CRUSH rule 0 x 156 [107,18]
+ CRUSH rule 0 x 157 [112,57]
+ CRUSH rule 0 x 158 [26,3]
+ CRUSH rule 0 x 159 [52,17]
+ CRUSH rule 0 x 160 [41,119]
+ CRUSH rule 0 x 161 [84,51]
+ CRUSH rule 0 x 162 [55,2]
+ CRUSH rule 0 x 163 [54,21]
+ CRUSH rule 0 x 164 [45,44]
+ CRUSH rule 0 x 165 [25,116]
+ CRUSH rule 0 x 166 [73,38]
+ CRUSH rule 0 x 167 [89,119]
+ CRUSH rule 0 x 168 [47,90]
+ CRUSH rule 0 x 169 [51,22]
+ CRUSH rule 0 x 170 [68,53]
+ CRUSH rule 0 x 171 [73,28]
+ CRUSH rule 0 x 172 [117,23]
+ CRUSH rule 0 x 173 [13,40]
+ CRUSH rule 0 x 174 [116,85]
+ CRUSH rule 0 x 175 [3,85]
+ CRUSH rule 0 x 176 [94,83]
+ CRUSH rule 0 x 177 [52,29]
+ CRUSH rule 0 x 178 [39,42]
+ CRUSH rule 0 x 179 [72,89]
+ CRUSH rule 0 x 180 [60,67]
+ CRUSH rule 0 x 181 [18,16]
+ CRUSH rule 0 x 182 [22,5]
+ CRUSH rule 0 x 183 [11,110]
+ CRUSH rule 0 x 184 [92,15]
+ CRUSH rule 0 x 185 [97,117]
+ CRUSH rule 0 x 186 [67,96]
+ CRUSH rule 0 x 187 [116,14]
+ CRUSH rule 0 x 188 [69,54]
+ CRUSH rule 0 x 189 [47,113]
+ CRUSH rule 0 x 190 [65,90]
+ CRUSH rule 0 x 191 [49,113]
+ CRUSH rule 0 x 192 [68,93]
+ CRUSH rule 0 x 193 [0,37]
+ CRUSH rule 0 x 194 [62,63]
+ CRUSH rule 0 x 195 [119,11]
+ CRUSH rule 0 x 196 [72,59]
+ CRUSH rule 0 x 197 [106,49]
+ CRUSH rule 0 x 198 [114,21]
+ CRUSH rule 0 x 199 [0,99]
+ CRUSH rule 0 x 200 [35,102]
+ CRUSH rule 0 x 201 [27,104]
+ CRUSH rule 0 x 202 [98,59]
+ CRUSH rule 0 x 203 [36,91]
+ CRUSH rule 0 x 204 [10,113]
+ CRUSH rule 0 x 205 [81,22]
+ CRUSH rule 0 x 206 [49,92]
+ CRUSH rule 0 x 207 [80,19]
+ CRUSH rule 0 x 208 [63,92]
+ CRUSH rule 0 x 209 [85,78]
+ CRUSH rule 0 x 210 [79,76]
+ CRUSH rule 0 x 211 [26,89]
+ CRUSH rule 0 x 212 [28,33]
+ CRUSH rule 0 x 213 [91,102]
+ CRUSH rule 0 x 214 [91,118]
+ CRUSH rule 0 x 215 [61,58]
+ CRUSH rule 0 x 216 [99,108]
+ CRUSH rule 0 x 217 [86,97]
+ CRUSH rule 0 x 218 [70,15]
+ CRUSH rule 0 x 219 [28,91]
+ CRUSH rule 0 x 220 [56,9]
+ CRUSH rule 0 x 221 [0,21]
+ CRUSH rule 0 x 222 [50,65]
+ CRUSH rule 0 x 223 [29,46]
+ CRUSH rule 0 x 224 [52,71]
+ CRUSH rule 0 x 225 [15,87]
+ CRUSH rule 0 x 226 [44,13]
+ CRUSH rule 0 x 227 [42,21]
+ CRUSH rule 0 x 228 [117,55]
+ CRUSH rule 0 x 229 [100,67]
+ CRUSH rule 0 x 230 [41,52]
+ CRUSH rule 0 x 231 [56,61]
+ CRUSH rule 0 x 232 [23,11]
+ CRUSH rule 0 x 233 [88,35]
+ CRUSH rule 0 x 234 [4,55]
+ CRUSH rule 0 x 235 [26,16]
+ CRUSH rule 0 x 236 [32,39]
+ CRUSH rule 0 x 237 [92,4]
+ CRUSH rule 0 x 238 [10,117]
+ CRUSH rule 0 x 239 [15,10]
+ CRUSH rule 0 x 240 [109,3]
+ CRUSH rule 0 x 241 [47,44]
+ CRUSH rule 0 x 242 [24,61]
+ CRUSH rule 0 x 243 [76,9]
+ CRUSH rule 0 x 244 [103,17]
+ CRUSH rule 0 x 245 [27,34]
+ CRUSH rule 0 x 246 [5,35]
+ CRUSH rule 0 x 247 [99,38]
+ CRUSH rule 0 x 248 [8,45]
+ CRUSH rule 0 x 249 [85,38]
+ CRUSH rule 0 x 250 [93,78]
+ CRUSH rule 0 x 251 [28,41]
+ CRUSH rule 0 x 252 [95,3]
+ CRUSH rule 0 x 253 [109,97]
+ CRUSH rule 0 x 254 [80,11]
+ CRUSH rule 0 x 255 [103,22]
+ CRUSH rule 0 x 256 [37,82]
+ CRUSH rule 0 x 257 [69,104]
+ CRUSH rule 0 x 258 [34,63]
+ CRUSH rule 0 x 259 [70,9]
+ CRUSH rule 0 x 260 [98,43]
+ CRUSH rule 0 x 261 [94,77]
+ CRUSH rule 0 x 262 [42,45]
+ CRUSH rule 0 x 263 [65,68]
+ CRUSH rule 0 x 264 [36,45]
+ CRUSH rule 0 x 265 [66,97]
+ CRUSH rule 0 x 266 [75,64]
+ CRUSH rule 0 x 267 [58,39]
+ CRUSH rule 0 x 268 [38,3]
+ CRUSH rule 0 x 269 [86,91]
+ CRUSH rule 0 x 270 [58,43]
+ CRUSH rule 0 x 271 [19,43]
+ CRUSH rule 0 x 272 [73,8]
+ CRUSH rule 0 x 273 [108,16]
+ CRUSH rule 0 x 274 [47,88]
+ CRUSH rule 0 x 275 [92,21]
+ CRUSH rule 0 x 276 [7,57]
+ CRUSH rule 0 x 277 [19,117]
+ CRUSH rule 0 x 278 [116,63]
+ CRUSH rule 0 x 279 [101,102]
+ CRUSH rule 0 x 280 [113,75]
+ CRUSH rule 0 x 281 [14,97]
+ CRUSH rule 0 x 282 [106,53]
+ CRUSH rule 0 x 283 [8,36]
+ CRUSH rule 0 x 284 [10,32]
+ CRUSH rule 0 x 285 [88,63]
+ CRUSH rule 0 x 286 [27,6]
+ CRUSH rule 0 x 287 [84,101]
+ CRUSH rule 0 x 288 [103,22]
+ CRUSH rule 0 x 289 [9,26]
+ CRUSH rule 0 x 290 [115,9]
+ CRUSH rule 0 x 291 [48,47]
+ CRUSH rule 0 x 292 [89,108]
+ CRUSH rule 0 x 293 [27,118]
+ CRUSH rule 0 x 294 [79,111]
+ CRUSH rule 0 x 295 [37,18]
+ CRUSH rule 0 x 296 [56,27]
+ CRUSH rule 0 x 297 [35,28]
+ CRUSH rule 0 x 298 [71,2]
+ CRUSH rule 0 x 299 [116,85]
+ CRUSH rule 0 x 300 [67,26]
+ CRUSH rule 0 x 301 [51,32]
+ CRUSH rule 0 x 302 [78,105]
+ CRUSH rule 0 x 303 [19,82]
+ CRUSH rule 0 x 304 [101,50]
+ CRUSH rule 0 x 305 [81,68]
+ CRUSH rule 0 x 306 [0,97]
+ CRUSH rule 0 x 307 [44,21]
+ CRUSH rule 0 x 308 [91,2]
+ CRUSH rule 0 x 309 [38,39]
+ CRUSH rule 0 x 310 [26,15]
+ CRUSH rule 0 x 311 [36,75]
+ CRUSH rule 0 x 312 [33,15]
+ CRUSH rule 0 x 313 [104,65]
+ CRUSH rule 0 x 314 [28,9]
+ CRUSH rule 0 x 315 [16,72]
+ CRUSH rule 0 x 316 [4,76]
+ CRUSH rule 0 x 317 [118,13]
+ CRUSH rule 0 x 318 [17,77]
+ CRUSH rule 0 x 319 [24,93]
+ CRUSH rule 0 x 320 [36,41]
+ CRUSH rule 0 x 321 [26,81]
+ CRUSH rule 0 x 322 [87,24]
+ CRUSH rule 0 x 323 [73,76]
+ CRUSH rule 0 x 324 [21,75]
+ CRUSH rule 0 x 325 [52,43]
+ CRUSH rule 0 x 326 [111,105]
+ CRUSH rule 0 x 327 [62,17]
+ CRUSH rule 0 x 328 [7,0]
+ CRUSH rule 0 x 329 [93,14]
+ CRUSH rule 0 x 330 [24,15]
+ CRUSH rule 0 x 331 [41,109]
+ CRUSH rule 0 x 332 [61,111]
+ CRUSH rule 0 x 333 [16,6]
+ CRUSH rule 0 x 334 [3,29]
+ CRUSH rule 0 x 335 [71,66]
+ CRUSH rule 0 x 336 [16,11]
+ CRUSH rule 0 x 337 [37,113]
+ CRUSH rule 0 x 338 [109,6]
+ CRUSH rule 0 x 339 [37,22]
+ CRUSH rule 0 x 340 [119,101]
+ CRUSH rule 0 x 341 [63,14]
+ CRUSH rule 0 x 342 [92,71]
+ CRUSH rule 0 x 343 [49,56]
+ CRUSH rule 0 x 344 [103,113]
+ CRUSH rule 0 x 345 [56,35]
+ CRUSH rule 0 x 346 [3,25]
+ CRUSH rule 0 x 347 [106,85]
+ CRUSH rule 0 x 348 [10,114]
+ CRUSH rule 0 x 349 [96,103]
+ CRUSH rule 0 x 350 [63,32]
+ CRUSH rule 0 x 351 [60,73]
+ CRUSH rule 0 x 352 [103,68]
+ CRUSH rule 0 x 353 [49,113]
+ CRUSH rule 0 x 354 [55,74]
+ CRUSH rule 0 x 355 [73,80]
+ CRUSH rule 0 x 356 [114,65]
+ CRUSH rule 0 x 357 [14,110]
+ CRUSH rule 0 x 358 [97,56]
+ CRUSH rule 0 x 359 [4,89]
+ CRUSH rule 0 x 360 [106,31]
+ CRUSH rule 0 x 361 [27,56]
+ CRUSH rule 0 x 362 [28,55]
+ CRUSH rule 0 x 363 [45,60]
+ CRUSH rule 0 x 364 [23,2]
+ CRUSH rule 0 x 365 [24,21]
+ CRUSH rule 0 x 366 [14,100]
+ CRUSH rule 0 x 367 [103,82]
+ CRUSH rule 0 x 368 [103,17]
+ CRUSH rule 0 x 369 [37,11]
+ CRUSH rule 0 x 370 [11,65]
+ CRUSH rule 0 x 371 [34,65]
+ CRUSH rule 0 x 372 [58,23]
+ CRUSH rule 0 x 373 [98,22]
+ CRUSH rule 0 x 374 [110,89]
+ CRUSH rule 0 x 375 [19,76]
+ CRUSH rule 0 x 376 [22,98]
+ CRUSH rule 0 x 377 [98,87]
+ CRUSH rule 0 x 378 [67,58]
+ CRUSH rule 0 x 379 [77,94]
+ CRUSH rule 0 x 380 [69,108]
+ CRUSH rule 0 x 381 [55,106]
+ CRUSH rule 0 x 382 [26,83]
+ CRUSH rule 0 x 383 [48,93]
+ CRUSH rule 0 x 384 [15,0]
+ CRUSH rule 0 x 385 [82,27]
+ CRUSH rule 0 x 386 [108,25]
+ CRUSH rule 0 x 387 [70,14]
+ CRUSH rule 0 x 388 [5,37]
+ CRUSH rule 0 x 389 [14,67]
+ CRUSH rule 0 x 390 [68,77]
+ CRUSH rule 0 x 391 [113,105]
+ CRUSH rule 0 x 392 [72,13]
+ CRUSH rule 0 x 393 [115,21]
+ CRUSH rule 0 x 394 [38,17]
+ CRUSH rule 0 x 395 [0,65]
+ CRUSH rule 0 x 396 [59,116]
+ CRUSH rule 0 x 397 [87,90]
+ CRUSH rule 0 x 398 [44,51]
+ CRUSH rule 0 x 399 [9,113]
+ CRUSH rule 0 x 400 [101,100]
+ CRUSH rule 0 x 401 [79,52]
+ CRUSH rule 0 x 402 [107,110]
+ CRUSH rule 0 x 403 [23,92]
+ CRUSH rule 0 x 404 [76,31]
+ CRUSH rule 0 x 405 [10,48]
+ CRUSH rule 0 x 406 [38,29]
+ CRUSH rule 0 x 407 [70,25]
+ CRUSH rule 0 x 408 [55,104]
+ CRUSH rule 0 x 409 [102,6]
+ CRUSH rule 0 x 410 [59,8]
+ CRUSH rule 0 x 411 [34,49]
+ CRUSH rule 0 x 412 [108,105]
+ CRUSH rule 0 x 413 [54,37]
+ CRUSH rule 0 x 414 [70,3]
+ CRUSH rule 0 x 415 [107,0]
+ CRUSH rule 0 x 416 [79,24]
+ CRUSH rule 0 x 417 [8,23]
+ CRUSH rule 0 x 418 [51,114]
+ CRUSH rule 0 x 419 [117,55]
+ CRUSH rule 0 x 420 [109,71]
+ CRUSH rule 0 x 421 [114,17]
+ CRUSH rule 0 x 422 [109,14]
+ CRUSH rule 0 x 423 [59,0]
+ CRUSH rule 0 x 424 [71,84]
+ CRUSH rule 0 x 425 [101,50]
+ CRUSH rule 0 x 426 [47,88]
+ CRUSH rule 0 x 427 [86,45]
+ CRUSH rule 0 x 428 [68,31]
+ CRUSH rule 0 x 429 [76,13]
+ CRUSH rule 0 x 430 [9,117]
+ CRUSH rule 0 x 431 [105,66]
+ CRUSH rule 0 x 432 [46,91]
+ CRUSH rule 0 x 433 [6,77]
+ CRUSH rule 0 x 434 [64,59]
+ CRUSH rule 0 x 435 [16,2]
+ CRUSH rule 0 x 436 [89,102]
+ CRUSH rule 0 x 437 [29,78]
+ CRUSH rule 0 x 438 [105,56]
+ CRUSH rule 0 x 439 [29,68]
+ CRUSH rule 0 x 440 [38,7]
+ CRUSH rule 0 x 441 [112,57]
+ CRUSH rule 0 x 442 [55,18]
+ CRUSH rule 0 x 443 [44,37]
+ CRUSH rule 0 x 444 [11,49]
+ CRUSH rule 0 x 445 [19,114]
+ CRUSH rule 0 x 446 [40,43]
+ CRUSH rule 0 x 447 [100,43]
+ CRUSH rule 0 x 448 [7,26]
+ CRUSH rule 0 x 449 [67,13]
+ CRUSH rule 0 x 450 [117,97]
+ CRUSH rule 0 x 451 [93,118]
+ CRUSH rule 0 x 452 [70,37]
+ CRUSH rule 0 x 453 [82,55]
+ CRUSH rule 0 x 454 [53,28]
+ CRUSH rule 0 x 455 [91,34]
+ CRUSH rule 0 x 456 [17,55]
+ CRUSH rule 0 x 457 [113,103]
+ CRUSH rule 0 x 458 [119,41]
+ CRUSH rule 0 x 459 [25,104]
+ CRUSH rule 0 x 460 [11,55]
+ CRUSH rule 0 x 461 [21,5]
+ CRUSH rule 0 x 462 [25,72]
+ CRUSH rule 0 x 463 [6,57]
+ CRUSH rule 0 x 464 [19,50]
+ CRUSH rule 0 x 465 [29,7]
+ CRUSH rule 0 x 466 [66,89]
+ CRUSH rule 0 x 467 [27,32]
+ CRUSH rule 0 x 468 [97,118]
+ CRUSH rule 0 x 469 [98,71]
+ CRUSH rule 0 x 470 [50,29]
+ CRUSH rule 0 x 471 [40,31]
+ CRUSH rule 0 x 472 [74,61]
+ CRUSH rule 0 x 473 [95,98]
+ CRUSH rule 0 x 474 [51,8]
+ CRUSH rule 0 x 475 [3,25]
+ CRUSH rule 0 x 476 [110,55]
+ CRUSH rule 0 x 477 [25,74]
+ CRUSH rule 0 x 478 [19,57]
+ CRUSH rule 0 x 479 [70,91]
+ CRUSH rule 0 x 480 [62,33]
+ CRUSH rule 0 x 481 [26,3]
+ CRUSH rule 0 x 482 [84,6]
+ CRUSH rule 0 x 483 [36,55]
+ CRUSH rule 0 x 484 [37,28]
+ CRUSH rule 0 x 485 [84,14]
+ CRUSH rule 0 x 486 [92,61]
+ CRUSH rule 0 x 487 [106,53]
+ CRUSH rule 0 x 488 [42,7]
+ CRUSH rule 0 x 489 [76,31]
+ CRUSH rule 0 x 490 [68,107]
+ CRUSH rule 0 x 491 [80,57]
+ CRUSH rule 0 x 492 [21,71]
+ CRUSH rule 0 x 493 [99,44]
+ CRUSH rule 0 x 494 [4,59]
+ CRUSH rule 0 x 495 [40,87]
+ CRUSH rule 0 x 496 [13,106]
+ CRUSH rule 0 x 497 [102,81]
+ CRUSH rule 0 x 498 [68,73]
+ CRUSH rule 0 x 499 [22,28]
+ CRUSH rule 0 x 500 [50,6]
+ CRUSH rule 0 x 501 [60,103]
+ CRUSH rule 0 x 502 [11,1]
+ CRUSH rule 0 x 503 [117,85]
+ CRUSH rule 0 x 504 [90,55]
+ CRUSH rule 0 x 505 [91,94]
+ CRUSH rule 0 x 506 [82,89]
+ CRUSH rule 0 x 507 [6,77]
+ CRUSH rule 0 x 508 [34,77]
+ CRUSH rule 0 x 509 [88,43]
+ CRUSH rule 0 x 510 [11,69]
+ CRUSH rule 0 x 511 [72,47]
+ CRUSH rule 0 x 512 [118,101]
+ CRUSH rule 0 x 513 [22,80]
+ CRUSH rule 0 x 514 [82,21]
+ CRUSH rule 0 x 515 [27,38]
+ CRUSH rule 0 x 516 [66,61]
+ CRUSH rule 0 x 517 [83,4]
+ CRUSH rule 0 x 518 [18,13]
+ CRUSH rule 0 x 519 [67,52]
+ CRUSH rule 0 x 520 [15,88]
+ CRUSH rule 0 x 521 [63,62]
+ CRUSH rule 0 x 522 [4,51]
+ CRUSH rule 0 x 523 [36,23]
+ CRUSH rule 0 x 524 [33,94]
+ CRUSH rule 0 x 525 [63,104]
+ CRUSH rule 0 x 526 [83,118]
+ CRUSH rule 0 x 527 [37,5]
+ CRUSH rule 0 x 528 [108,43]
+ CRUSH rule 0 x 529 [107,7]
+ CRUSH rule 0 x 530 [49,11]
+ CRUSH rule 0 x 531 [27,82]
+ CRUSH rule 0 x 532 [68,89]
+ CRUSH rule 0 x 533 [5,73]
+ CRUSH rule 0 x 534 [97,104]
+ CRUSH rule 0 x 535 [48,41]
+ CRUSH rule 0 x 536 [3,71]
+ CRUSH rule 0 x 537 [116,7]
+ CRUSH rule 0 x 538 [85,3]
+ CRUSH rule 0 x 539 [10,82]
+ CRUSH rule 0 x 540 [100,31]
+ CRUSH rule 0 x 541 [111,67]
+ CRUSH rule 0 x 542 [50,103]
+ CRUSH rule 0 x 543 [45,21]
+ CRUSH rule 0 x 544 [106,67]
+ CRUSH rule 0 x 545 [43,86]
+ CRUSH rule 0 x 546 [108,49]
+ CRUSH rule 0 x 547 [27,18]
+ CRUSH rule 0 x 548 [53,66]
+ CRUSH rule 0 x 549 [60,89]
+ CRUSH rule 0 x 550 [47,62]
+ CRUSH rule 0 x 551 [14,52]
+ CRUSH rule 0 x 552 [70,10]
+ CRUSH rule 0 x 553 [96,73]
+ CRUSH rule 0 x 554 [61,70]
+ CRUSH rule 0 x 555 [76,69]
+ CRUSH rule 0 x 556 [106,10]
+ CRUSH rule 0 x 557 [39,58]
+ CRUSH rule 0 x 558 [70,93]
+ CRUSH rule 0 x 559 [106,23]
+ CRUSH rule 0 x 560 [94,16]
+ CRUSH rule 0 x 561 [27,68]
+ CRUSH rule 0 x 562 [97,112]
+ CRUSH rule 0 x 563 [64,61]
+ CRUSH rule 0 x 564 [96,59]
+ CRUSH rule 0 x 565 [66,69]
+ CRUSH rule 0 x 566 [27,86]
+ CRUSH rule 0 x 567 [88,4]
+ CRUSH rule 0 x 568 [17,96]
+ CRUSH rule 0 x 569 [102,29]
+ CRUSH rule 0 x 570 [7,103]
+ CRUSH rule 0 x 571 [95,110]
+ CRUSH rule 0 x 572 [62,33]
+ CRUSH rule 0 x 573 [51,46]
+ CRUSH rule 0 x 574 [89,64]
+ CRUSH rule 0 x 575 [19,53]
+ CRUSH rule 0 x 576 [112,87]
+ CRUSH rule 0 x 577 [8,113]
+ CRUSH rule 0 x 578 [64,3]
+ CRUSH rule 0 x 579 [78,37]
+ CRUSH rule 0 x 580 [68,35]
+ CRUSH rule 0 x 581 [55,113]
+ CRUSH rule 0 x 582 [27,19]
+ CRUSH rule 0 x 583 [74,99]
+ CRUSH rule 0 x 584 [72,53]
+ CRUSH rule 0 x 585 [88,79]
+ CRUSH rule 0 x 586 [33,1]
+ CRUSH rule 0 x 587 [106,53]
+ CRUSH rule 0 x 588 [0,45]
+ CRUSH rule 0 x 589 [7,85]
+ CRUSH rule 0 x 590 [59,40]
+ CRUSH rule 0 x 591 [42,43]
+ CRUSH rule 0 x 592 [45,110]
+ CRUSH rule 0 x 593 [89,14]
+ CRUSH rule 0 x 594 [27,76]
+ CRUSH rule 0 x 595 [7,10]
+ CRUSH rule 0 x 596 [82,41]
+ CRUSH rule 0 x 597 [72,97]
+ CRUSH rule 0 x 598 [34,17]
+ CRUSH rule 0 x 599 [119,53]
+ CRUSH rule 0 x 600 [9,36]
+ CRUSH rule 0 x 601 [104,21]
+ CRUSH rule 0 x 602 [48,39]
+ CRUSH rule 0 x 603 [24,11]
+ CRUSH rule 0 x 604 [89,82]
+ CRUSH rule 0 x 605 [104,63]
+ CRUSH rule 0 x 606 [49,58]
+ CRUSH rule 0 x 607 [95,72]
+ CRUSH rule 0 x 608 [49,48]
+ CRUSH rule 0 x 609 [61,70]
+ CRUSH rule 0 x 610 [106,73]
+ CRUSH rule 0 x 611 [66,37]
+ CRUSH rule 0 x 612 [103,84]
+ CRUSH rule 0 x 613 [84,57]
+ CRUSH rule 0 x 614 [81,9]
+ CRUSH rule 0 x 615 [61,9]
+ CRUSH rule 0 x 616 [41,8]
+ CRUSH rule 0 x 617 [111,81]
+ CRUSH rule 0 x 618 [3,39]
+ CRUSH rule 0 x 619 [92,31]
+ CRUSH rule 0 x 620 [108,31]
+ CRUSH rule 0 x 621 [106,57]
+ CRUSH rule 0 x 622 [67,102]
+ CRUSH rule 0 x 623 [94,7]
+ CRUSH rule 0 x 624 [115,29]
+ CRUSH rule 0 x 625 [111,67]
+ CRUSH rule 0 x 626 [3,25]
+ CRUSH rule 0 x 627 [19,105]
+ CRUSH rule 0 x 628 [65,100]
+ CRUSH rule 0 x 629 [119,15]
+ CRUSH rule 0 x 630 [109,4]
+ CRUSH rule 0 x 631 [48,33]
+ CRUSH rule 0 x 632 [81,60]
+ CRUSH rule 0 x 633 [65,110]
+ CRUSH rule 0 x 634 [87,50]
+ CRUSH rule 0 x 635 [107,9]
+ CRUSH rule 0 x 636 [23,66]
+ CRUSH rule 0 x 637 [102,29]
+ CRUSH rule 0 x 638 [43,4]
+ CRUSH rule 0 x 639 [31,76]
+ CRUSH rule 0 x 640 [113,87]
+ CRUSH rule 0 x 641 [45,58]
+ CRUSH rule 0 x 642 [47,17]
+ CRUSH rule 0 x 643 [64,97]
+ CRUSH rule 0 x 644 [31,4]
+ CRUSH rule 0 x 645 [76,13]
+ CRUSH rule 0 x 646 [37,86]
+ CRUSH rule 0 x 647 [58,101]
+ CRUSH rule 0 x 648 [31,9]
+ CRUSH rule 0 x 649 [88,39]
+ CRUSH rule 0 x 650 [116,19]
+ CRUSH rule 0 x 651 [97,116]
+ CRUSH rule 0 x 652 [57,28]
+ CRUSH rule 0 x 653 [38,95]
+ CRUSH rule 0 x 654 [49,92]
+ CRUSH rule 0 x 655 [89,54]
+ CRUSH rule 0 x 656 [0,89]
+ CRUSH rule 0 x 657 [47,18]
+ CRUSH rule 0 x 658 [75,32]
+ CRUSH rule 0 x 659 [26,33]
+ CRUSH rule 0 x 660 [65,82]
+ CRUSH rule 0 x 661 [91,76]
+ CRUSH rule 0 x 662 [111,73]
+ CRUSH rule 0 x 663 [88,67]
+ CRUSH rule 0 x 664 [59,52]
+ CRUSH rule 0 x 665 [78,7]
+ CRUSH rule 0 x 666 [112,8]
+ CRUSH rule 0 x 667 [97,80]
+ CRUSH rule 0 x 668 [97,22]
+ CRUSH rule 0 x 669 [85,0]
+ CRUSH rule 0 x 670 [41,62]
+ CRUSH rule 0 x 671 [116,37]
+ CRUSH rule 0 x 672 [44,67]
+ CRUSH rule 0 x 673 [83,116]
+ CRUSH rule 0 x 674 [59,98]
+ CRUSH rule 0 x 675 [88,17]
+ CRUSH rule 0 x 676 [62,4]
+ CRUSH rule 0 x 677 [88,105]
+ CRUSH rule 0 x 678 [98,57]
+ CRUSH rule 0 x 679 [70,61]
+ CRUSH rule 0 x 680 [55,5]
+ CRUSH rule 0 x 681 [53,68]
+ CRUSH rule 0 x 682 [27,78]
+ CRUSH rule 0 x 683 [57,118]
+ CRUSH rule 0 x 684 [98,45]
+ CRUSH rule 0 x 685 [106,25]
+ CRUSH rule 0 x 686 [86,45]
+ CRUSH rule 0 x 687 [49,102]
+ CRUSH rule 0 x 688 [16,52]
+ CRUSH rule 0 x 689 [6,112]
+ CRUSH rule 0 x 690 [43,17]
+ CRUSH rule 0 x 691 [34,99]
+ CRUSH rule 0 x 692 [40,67]
+ CRUSH rule 0 x 693 [29,118]
+ CRUSH rule 0 x 694 [6,75]
+ CRUSH rule 0 x 695 [31,72]
+ CRUSH rule 0 x 696 [36,3]
+ CRUSH rule 0 x 697 [96,99]
+ CRUSH rule 0 x 698 [61,100]
+ CRUSH rule 0 x 699 [47,60]
+ CRUSH rule 0 x 700 [0,93]
+ CRUSH rule 0 x 701 [42,21]
+ CRUSH rule 0 x 702 [0,105]
+ CRUSH rule 0 x 703 [92,29]
+ CRUSH rule 0 x 704 [10,8]
+ CRUSH rule 0 x 705 [105,0]
+ CRUSH rule 0 x 706 [74,10]
+ CRUSH rule 0 x 707 [0,91]
+ CRUSH rule 0 x 708 [84,21]
+ CRUSH rule 0 x 709 [114,23]
+ CRUSH rule 0 x 710 [94,19]
+ CRUSH rule 0 x 711 [68,41]
+ CRUSH rule 0 x 712 [34,71]
+ CRUSH rule 0 x 713 [29,2]
+ CRUSH rule 0 x 714 [81,80]
+ CRUSH rule 0 x 715 [71,62]
+ CRUSH rule 0 x 716 [40,6]
+ CRUSH rule 0 x 717 [61,60]
+ CRUSH rule 0 x 718 [40,69]
+ CRUSH rule 0 x 719 [59,74]
+ CRUSH rule 0 x 720 [69,2]
+ CRUSH rule 0 x 721 [62,75]
+ CRUSH rule 0 x 722 [115,19]
+ CRUSH rule 0 x 723 [117,25]
+ CRUSH rule 0 x 724 [45,3]
+ CRUSH rule 0 x 725 [53,110]
+ CRUSH rule 0 x 726 [84,107]
+ CRUSH rule 0 x 727 [109,19]
+ CRUSH rule 0 x 728 [76,65]
+ CRUSH rule 0 x 729 [108,7]
+ CRUSH rule 0 x 730 [28,37]
+ CRUSH rule 0 x 731 [78,41]
+ CRUSH rule 0 x 732 [55,112]
+ CRUSH rule 0 x 733 [84,7]
+ CRUSH rule 0 x 734 [27,110]
+ CRUSH rule 0 x 735 [83,62]
+ CRUSH rule 0 x 736 [70,67]
+ CRUSH rule 0 x 737 [117,11]
+ CRUSH rule 0 x 738 [118,95]
+ CRUSH rule 0 x 739 [87,1]
+ CRUSH rule 0 x 740 [29,92]
+ CRUSH rule 0 x 741 [96,49]
+ CRUSH rule 0 x 742 [106,31]
+ CRUSH rule 0 x 743 [105,5]
+ CRUSH rule 0 x 744 [23,64]
+ CRUSH rule 0 x 745 [28,85]
+ CRUSH rule 0 x 746 [18,47]
+ CRUSH rule 0 x 747 [65,108]
+ CRUSH rule 0 x 748 [48,25]
+ CRUSH rule 0 x 749 [102,71]
+ CRUSH rule 0 x 750 [50,77]
+ CRUSH rule 0 x 751 [36,29]
+ CRUSH rule 0 x 752 [69,119]
+ CRUSH rule 0 x 753 [9,34]
+ CRUSH rule 0 x 754 [9,39]
+ CRUSH rule 0 x 755 [98,45]
+ CRUSH rule 0 x 756 [113,83]
+ CRUSH rule 0 x 757 [47,112]
+ CRUSH rule 0 x 758 [57,84]
+ CRUSH rule 0 x 759 [74,65]
+ CRUSH rule 0 x 760 [53,34]
+ CRUSH rule 0 x 761 [78,105]
+ CRUSH rule 0 x 762 [87,13]
+ CRUSH rule 0 x 763 [13,16]
+ CRUSH rule 0 x 764 [106,27]
+ CRUSH rule 0 x 765 [109,77]
+ CRUSH rule 0 x 766 [76,105]
+ CRUSH rule 0 x 767 [41,80]
+ CRUSH rule 0 x 768 [13,50]
+ CRUSH rule 0 x 769 [91,96]
+ CRUSH rule 0 x 770 [105,62]
+ CRUSH rule 0 x 771 [10,28]
+ CRUSH rule 0 x 772 [8,102]
+ CRUSH rule 0 x 773 [116,91]
+ CRUSH rule 0 x 774 [100,105]
+ CRUSH rule 0 x 775 [15,61]
+ CRUSH rule 0 x 776 [69,44]
+ CRUSH rule 0 x 777 [76,23]
+ CRUSH rule 0 x 778 [38,9]
+ CRUSH rule 0 x 779 [46,17]
+ CRUSH rule 0 x 780 [63,70]
+ CRUSH rule 0 x 781 [19,16]
+ CRUSH rule 0 x 782 [117,59]
+ CRUSH rule 0 x 783 [60,25]
+ CRUSH rule 0 x 784 [82,81]
+ CRUSH rule 0 x 785 [27,50]
+ CRUSH rule 0 x 786 [41,90]
+ CRUSH rule 0 x 787 [13,34]
+ CRUSH rule 0 x 788 [4,113]
+ CRUSH rule 0 x 789 [50,51]
+ CRUSH rule 0 x 790 [58,95]
+ CRUSH rule 0 x 791 [96,37]
+ CRUSH rule 0 x 792 [45,13]
+ CRUSH rule 0 x 793 [6,103]
+ CRUSH rule 0 x 794 [14,61]
+ CRUSH rule 0 x 795 [51,26]
+ CRUSH rule 0 x 796 [114,43]
+ CRUSH rule 0 x 797 [79,115]
+ CRUSH rule 0 x 798 [42,19]
+ CRUSH rule 0 x 799 [48,41]
+ CRUSH rule 0 x 800 [91,22]
+ CRUSH rule 0 x 801 [2,11]
+ CRUSH rule 0 x 802 [116,19]
+ CRUSH rule 0 x 803 [37,46]
+ CRUSH rule 0 x 804 [6,93]
+ CRUSH rule 0 x 805 [96,3]
+ CRUSH rule 0 x 806 [67,110]
+ CRUSH rule 0 x 807 [47,92]
+ CRUSH rule 0 x 808 [76,31]
+ CRUSH rule 0 x 809 [27,90]
+ CRUSH rule 0 x 810 [119,35]
+ CRUSH rule 0 x 811 [75,84]
+ CRUSH rule 0 x 812 [25,94]
+ CRUSH rule 0 x 813 [64,27]
+ CRUSH rule 0 x 814 [110,29]
+ CRUSH rule 0 x 815 [84,39]
+ CRUSH rule 0 x 816 [25,3]
+ CRUSH rule 0 x 817 [40,57]
+ CRUSH rule 0 x 818 [34,16]
+ CRUSH rule 0 x 819 [88,15]
+ CRUSH rule 0 x 820 [104,29]
+ CRUSH rule 0 x 821 [58,16]
+ CRUSH rule 0 x 822 [29,98]
+ CRUSH rule 0 x 823 [100,37]
+ CRUSH rule 0 x 824 [102,95]
+ CRUSH rule 0 x 825 [47,14]
+ CRUSH rule 0 x 826 [45,8]
+ CRUSH rule 0 x 827 [101,19]
+ CRUSH rule 0 x 828 [60,27]
+ CRUSH rule 0 x 829 [45,102]
+ CRUSH rule 0 x 830 [51,0]
+ CRUSH rule 0 x 831 [6,64]
+ CRUSH rule 0 x 832 [57,116]
+ CRUSH rule 0 x 833 [34,105]
+ CRUSH rule 0 x 834 [90,77]
+ CRUSH rule 0 x 835 [55,50]
+ CRUSH rule 0 x 836 [38,51]
+ CRUSH rule 0 x 837 [51,78]
+ CRUSH rule 0 x 838 [6,98]
+ CRUSH rule 0 x 839 [106,15]
+ CRUSH rule 0 x 840 [33,117]
+ CRUSH rule 0 x 841 [110,13]
+ CRUSH rule 0 x 842 [66,83]
+ CRUSH rule 0 x 843 [62,107]
+ CRUSH rule 0 x 844 [74,22]
+ CRUSH rule 0 x 845 [74,63]
+ CRUSH rule 0 x 846 [98,41]
+ CRUSH rule 0 x 847 [10,90]
+ CRUSH rule 0 x 848 [89,19]
+ CRUSH rule 0 x 849 [42,61]
+ CRUSH rule 0 x 850 [40,87]
+ CRUSH rule 0 x 851 [65,11]
+ CRUSH rule 0 x 852 [31,100]
+ CRUSH rule 0 x 853 [49,11]
+ CRUSH rule 0 x 854 [83,92]
+ CRUSH rule 0 x 855 [2,22]
+ CRUSH rule 0 x 856 [6,41]
+ CRUSH rule 0 x 857 [15,110]
+ CRUSH rule 0 x 858 [10,114]
+ CRUSH rule 0 x 859 [14,41]
+ CRUSH rule 0 x 860 [114,93]
+ CRUSH rule 0 x 861 [1,105]
+ CRUSH rule 0 x 862 [22,27]
+ CRUSH rule 0 x 863 [79,50]
+ CRUSH rule 0 x 864 [68,19]
+ CRUSH rule 0 x 865 [25,68]
+ CRUSH rule 0 x 866 [18,85]
+ CRUSH rule 0 x 867 [53,58]
+ CRUSH rule 0 x 868 [81,0]
+ CRUSH rule 0 x 869 [111,22]
+ CRUSH rule 0 x 870 [73,94]
+ CRUSH rule 0 x 871 [25,64]
+ CRUSH rule 0 x 872 [39,2]
+ CRUSH rule 0 x 873 [92,6]
+ CRUSH rule 0 x 874 [96,17]
+ CRUSH rule 0 x 875 [115,27]
+ CRUSH rule 0 x 876 [98,16]
+ CRUSH rule 0 x 877 [73,46]
+ CRUSH rule 0 x 878 [64,45]
+ CRUSH rule 0 x 879 [15,1]
+ CRUSH rule 0 x 880 [56,105]
+ CRUSH rule 0 x 881 [109,97]
+ CRUSH rule 0 x 882 [60,79]
+ CRUSH rule 0 x 883 [93,17]
+ CRUSH rule 0 x 884 [67,36]
+ CRUSH rule 0 x 885 [31,104]
+ CRUSH rule 0 x 886 [2,7]
+ CRUSH rule 0 x 887 [5,9]
+ CRUSH rule 0 x 888 [16,22]
+ CRUSH rule 0 x 889 [27,2]
+ CRUSH rule 0 x 890 [48,47]
+ CRUSH rule 0 x 891 [86,59]
+ CRUSH rule 0 x 892 [64,91]
+ CRUSH rule 0 x 893 [118,7]
+ CRUSH rule 0 x 894 [16,94]
+ CRUSH rule 0 x 895 [40,101]
+ CRUSH rule 0 x 896 [97,119]
+ CRUSH rule 0 x 897 [107,80]
+ CRUSH rule 0 x 898 [10,88]
+ CRUSH rule 0 x 899 [75,1]
+ CRUSH rule 0 x 900 [102,55]
+ CRUSH rule 0 x 901 [66,61]
+ CRUSH rule 0 x 902 [102,10]
+ CRUSH rule 0 x 903 [5,33]
+ CRUSH rule 0 x 904 [50,10]
+ CRUSH rule 0 x 905 [99,5]
+ CRUSH rule 0 x 906 [75,119]
+ CRUSH rule 0 x 907 [47,34]
+ CRUSH rule 0 x 908 [96,73]
+ CRUSH rule 0 x 909 [94,87]
+ CRUSH rule 0 x 910 [88,57]
+ CRUSH rule 0 x 911 [102,43]
+ CRUSH rule 0 x 912 [91,111]
+ CRUSH rule 0 x 913 [29,21]
+ CRUSH rule 0 x 914 [84,19]
+ CRUSH rule 0 x 915 [70,43]
+ CRUSH rule 0 x 916 [32,7]
+ CRUSH rule 0 x 917 [43,102]
+ CRUSH rule 0 x 918 [91,26]
+ CRUSH rule 0 x 919 [13,51]
+ CRUSH rule 0 x 920 [18,13]
+ CRUSH rule 0 x 921 [104,8]
+ CRUSH rule 0 x 922 [33,96]
+ CRUSH rule 0 x 923 [28,15]
+ CRUSH rule 0 x 924 [69,76]
+ CRUSH rule 0 x 925 [71,104]
+ CRUSH rule 0 x 926 [64,65]
+ CRUSH rule 0 x 927 [99,6]
+ CRUSH rule 0 x 928 [13,94]
+ CRUSH rule 0 x 929 [117,95]
+ CRUSH rule 0 x 930 [31,111]
+ CRUSH rule 0 x 931 [83,64]
+ CRUSH rule 0 x 932 [60,21]
+ CRUSH rule 0 x 933 [63,113]
+ CRUSH rule 0 x 934 [68,21]
+ CRUSH rule 0 x 935 [31,46]
+ CRUSH rule 0 x 936 [65,116]
+ CRUSH rule 0 x 937 [110,65]
+ CRUSH rule 0 x 938 [29,98]
+ CRUSH rule 0 x 939 [77,11]
+ CRUSH rule 0 x 940 [76,19]
+ CRUSH rule 0 x 941 [66,10]
+ CRUSH rule 0 x 942 [83,32]
+ CRUSH rule 0 x 943 [32,9]
+ CRUSH rule 0 x 944 [113,7]
+ CRUSH rule 0 x 945 [71,111]
+ CRUSH rule 0 x 946 [37,42]
+ CRUSH rule 0 x 947 [107,48]
+ CRUSH rule 0 x 948 [55,24]
+ CRUSH rule 0 x 949 [11,109]
+ CRUSH rule 0 x 950 [96,33]
+ CRUSH rule 0 x 951 [40,63]
+ CRUSH rule 0 x 952 [93,5]
+ CRUSH rule 0 x 953 [55,28]
+ CRUSH rule 0 x 954 [84,83]
+ CRUSH rule 0 x 955 [31,90]
+ CRUSH rule 0 x 956 [72,6]
+ CRUSH rule 0 x 957 [3,88]
+ CRUSH rule 0 x 958 [23,74]
+ CRUSH rule 0 x 959 [42,93]
+ CRUSH rule 0 x 960 [113,91]
+ CRUSH rule 0 x 961 [116,4]
+ CRUSH rule 0 x 962 [13,52]
+ CRUSH rule 0 x 963 [0,83]
+ CRUSH rule 0 x 964 [59,44]
+ CRUSH rule 0 x 965 [47,102]
+ CRUSH rule 0 x 966 [88,69]
+ CRUSH rule 0 x 967 [71,17]
+ CRUSH rule 0 x 968 [73,9]
+ CRUSH rule 0 x 969 [53,21]
+ CRUSH rule 0 x 970 [111,85]
+ CRUSH rule 0 x 971 [87,19]
+ CRUSH rule 0 x 972 [5,33]
+ CRUSH rule 0 x 973 [113,81]
+ CRUSH rule 0 x 974 [49,86]
+ CRUSH rule 0 x 975 [83,96]
+ CRUSH rule 0 x 976 [81,100]
+ CRUSH rule 0 x 977 [95,76]
+ CRUSH rule 0 x 978 [35,4]
+ CRUSH rule 0 x 979 [98,13]
+ CRUSH rule 0 x 980 [52,93]
+ CRUSH rule 0 x 981 [89,46]
+ CRUSH rule 0 x 982 [1,95]
+ CRUSH rule 0 x 983 [34,37]
+ CRUSH rule 0 x 984 [78,23]
+ CRUSH rule 0 x 985 [99,24]
+ CRUSH rule 0 x 986 [4,33]
+ CRUSH rule 0 x 987 [78,22]
+ CRUSH rule 0 x 988 [79,84]
+ CRUSH rule 0 x 989 [87,6]
+ CRUSH rule 0 x 990 [47,46]
+ CRUSH rule 0 x 991 [61,18]
+ CRUSH rule 0 x 992 [83,111]
+ CRUSH rule 0 x 993 [74,27]
+ CRUSH rule 0 x 994 [74,105]
+ CRUSH rule 0 x 995 [100,45]
+ CRUSH rule 0 x 996 [41,22]
+ CRUSH rule 0 x 997 [89,32]
+ CRUSH rule 0 x 998 [92,65]
+ CRUSH rule 0 x 999 [117,13]
+ CRUSH rule 0 x 1000 [9,48]
+ CRUSH rule 0 x 1001 [49,109]
+ CRUSH rule 0 x 1002 [99,106]
+ CRUSH rule 0 x 1003 [43,22]
+ CRUSH rule 0 x 1004 [89,106]
+ CRUSH rule 0 x 1005 [105,44]
+ CRUSH rule 0 x 1006 [45,5]
+ CRUSH rule 0 x 1007 [19,67]
+ CRUSH rule 0 x 1008 [31,3]
+ CRUSH rule 0 x 1009 [19,108]
+ CRUSH rule 0 x 1010 [42,67]
+ CRUSH rule 0 x 1011 [25,113]
+ CRUSH rule 0 x 1012 [68,81]
+ CRUSH rule 0 x 1013 [5,93]
+ CRUSH rule 0 x 1014 [33,8]
+ CRUSH rule 0 x 1015 [14,99]
+ CRUSH rule 0 x 1016 [88,6]
+ CRUSH rule 0 x 1017 [0,61]
+ CRUSH rule 0 x 1018 [63,26]
+ CRUSH rule 0 x 1019 [104,61]
+ CRUSH rule 0 x 1020 [96,83]
+ CRUSH rule 0 x 1021 [117,35]
+ CRUSH rule 0 x 1022 [73,6]
+ CRUSH rule 0 x 1023 [0,83]
+ rule 0 (data) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 3 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 4 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 5 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 6 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 7 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 8 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 9 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [101,28,17]
+ CRUSH rule 0 x 1 [80,13,75]
+ CRUSH rule 0 x 2 [91,11,68]
+ CRUSH rule 0 x 3 [51,13,112]
+ CRUSH rule 0 x 4 [50,101,3]
+ CRUSH rule 0 x 5 [89,113,11]
+ CRUSH rule 0 x 6 [91,109,13]
+ CRUSH rule 0 x 7 [104,51,14]
+ CRUSH rule 0 x 8 [78,75,11]
+ CRUSH rule 0 x 9 [101,80,7]
+ CRUSH rule 0 x 10 [61,4,111]
+ CRUSH rule 0 x 11 [13,43,40]
+ CRUSH rule 0 x 12 [83,0,17]
+ CRUSH rule 0 x 13 [108,22,93]
+ CRUSH rule 0 x 14 [105,9,104]
+ CRUSH rule 0 x 15 [18,7,16]
+ CRUSH rule 0 x 16 [103,4,60]
+ CRUSH rule 0 x 17 [85,80,14]
+ CRUSH rule 0 x 18 [11,71,48]
+ CRUSH rule 0 x 19 [75,114,3]
+ CRUSH rule 0 x 20 [79,64,7]
+ CRUSH rule 0 x 21 [84,7,61]
+ CRUSH rule 0 x 22 [23,66,21]
+ CRUSH rule 0 x 23 [118,6,10]
+ CRUSH rule 0 x 24 [83,111,19]
+ CRUSH rule 0 x 25 [81,116,13]
+ CRUSH rule 0 x 26 [38,69,13]
+ CRUSH rule 0 x 27 [76,103,8]
+ CRUSH rule 0 x 28 [76,103,4]
+ CRUSH rule 0 x 29 [8,46,59]
+ CRUSH rule 0 x 30 [94,7,103]
+ CRUSH rule 0 x 31 [76,35,3]
+ CRUSH rule 0 x 32 [72,35,4]
+ CRUSH rule 0 x 33 [77,104,14]
+ CRUSH rule 0 x 34 [74,67,11]
+ CRUSH rule 0 x 35 [22,115,57]
+ CRUSH rule 0 x 36 [104,33,15]
+ CRUSH rule 0 x 37 [38,57,22]
+ CRUSH rule 0 x 38 [72,11,81]
+ CRUSH rule 0 x 39 [68,73,13]
+ CRUSH rule 0 x 40 [103,76,4]
+ CRUSH rule 0 x 41 [85,4,78]
+ CRUSH rule 0 x 42 [106,39,15]
+ CRUSH rule 0 x 43 [10,115,22]
+ CRUSH rule 0 x 44 [101,66,4]
+ CRUSH rule 0 x 45 [8,80,71]
+ CRUSH rule 0 x 46 [65,66,17]
+ CRUSH rule 0 x 47 [106,41,19]
+ CRUSH rule 0 x 48 [34,4,41]
+ CRUSH rule 0 x 49 [0,27,15]
+ CRUSH rule 0 x 50 [42,14,55]
+ CRUSH rule 0 x 51 [104,59,15]
+ CRUSH rule 0 x 52 [83,14,80]
+ CRUSH rule 0 x 53 [32,93,9]
+ CRUSH rule 0 x 54 [28,77,4]
+ CRUSH rule 0 x 55 [14,94,75]
+ CRUSH rule 0 x 56 [21,112,63]
+ CRUSH rule 0 x 57 [93,88,3]
+ CRUSH rule 0 x 58 [45,1,14]
+ CRUSH rule 0 x 59 [80,31,6]
+ CRUSH rule 0 x 60 [90,33,4]
+ CRUSH rule 0 x 61 [88,39,19]
+ CRUSH rule 0 x 62 [81,0,4]
+ CRUSH rule 0 x 63 [79,96,3]
+ CRUSH rule 0 x 64 [1,8,35]
+ CRUSH rule 0 x 65 [13,92,61]
+ CRUSH rule 0 x 66 [48,79,11]
+ CRUSH rule 0 x 67 [94,91,11]
+ CRUSH rule 0 x 68 [102,105,4]
+ CRUSH rule 0 x 69 [62,4,53]
+ CRUSH rule 0 x 70 [84,27,4]
+ CRUSH rule 0 x 71 [55,108,8]
+ CRUSH rule 0 x 72 [97,42,13]
+ CRUSH rule 0 x 73 [64,81,14]
+ CRUSH rule 0 x 74 [96,41,13]
+ CRUSH rule 0 x 75 [29,98,15]
+ CRUSH rule 0 x 76 [55,111,22]
+ CRUSH rule 0 x 77 [107,21,72]
+ CRUSH rule 0 x 78 [31,100,9]
+ CRUSH rule 0 x 79 [64,75,8]
+ CRUSH rule 0 x 80 [0,67,17]
+ CRUSH rule 0 x 81 [71,52,15]
+ CRUSH rule 0 x 82 [37,0,11]
+ CRUSH rule 0 x 83 [92,75,9]
+ CRUSH rule 0 x 84 [49,40,7]
+ CRUSH rule 0 x 85 [54,71,11]
+ CRUSH rule 0 x 86 [37,14,111]
+ CRUSH rule 0 x 87 [116,3,93]
+ CRUSH rule 0 x 88 [38,95,3]
+ CRUSH rule 0 x 89 [76,41,19]
+ CRUSH rule 0 x 90 [14,98,75]
+ CRUSH rule 0 x 91 [93,114,21]
+ CRUSH rule 0 x 92 [86,13,23]
+ CRUSH rule 0 x 93 [44,41,15]
+ CRUSH rule 0 x 94 [61,18,11]
+ CRUSH rule 0 x 95 [93,98,8]
+ CRUSH rule 0 x 96 [66,25,8]
+ CRUSH rule 0 x 97 [111,4,33]
+ CRUSH rule 0 x 98 [66,16,17]
+ CRUSH rule 0 x 99 [78,22,87]
+ CRUSH rule 0 x 100 [28,4,61]
+ CRUSH rule 0 x 101 [84,51,8]
+ CRUSH rule 0 x 102 [82,93,7]
+ CRUSH rule 0 x 103 [66,4,105]
+ CRUSH rule 0 x 104 [14,10,48]
+ CRUSH rule 0 x 105 [87,100,7]
+ CRUSH rule 0 x 106 [69,66,3]
+ CRUSH rule 0 x 107 [1,41,15]
+ CRUSH rule 0 x 108 [94,75,19]
+ CRUSH rule 0 x 109 [112,87,21]
+ CRUSH rule 0 x 110 [54,10,17]
+ CRUSH rule 0 x 111 [10,112,8]
+ CRUSH rule 0 x 112 [89,11,102]
+ CRUSH rule 0 x 113 [69,26,14]
+ CRUSH rule 0 x 114 [79,22,110]
+ CRUSH rule 0 x 115 [50,65,22]
+ CRUSH rule 0 x 116 [96,53,22]
+ CRUSH rule 0 x 117 [87,86,15]
+ CRUSH rule 0 x 118 [23,106,3]
+ CRUSH rule 0 x 119 [104,14,31]
+ CRUSH rule 0 x 120 [57,42,21]
+ CRUSH rule 0 x 121 [105,50,9]
+ CRUSH rule 0 x 122 [45,68,22]
+ CRUSH rule 0 x 123 [112,15,43]
+ CRUSH rule 0 x 124 [110,19,69]
+ CRUSH rule 0 x 125 [66,71,22]
+ CRUSH rule 0 x 126 [51,64,17]
+ CRUSH rule 0 x 127 [70,13,59]
+ CRUSH rule 0 x 128 [90,47,14]
+ CRUSH rule 0 x 129 [103,108,7]
+ CRUSH rule 0 x 130 [50,17,55]
+ CRUSH rule 0 x 131 [23,60,15]
+ CRUSH rule 0 x 132 [69,58,13]
+ CRUSH rule 0 x 133 [52,85,14]
+ CRUSH rule 0 x 134 [78,81,8]
+ CRUSH rule 0 x 135 [78,6,53]
+ CRUSH rule 0 x 136 [32,83,11]
+ CRUSH rule 0 x 137 [92,87,3]
+ CRUSH rule 0 x 138 [17,74,41]
+ CRUSH rule 0 x 139 [89,92,8]
+ CRUSH rule 0 x 140 [39,1,13]
+ CRUSH rule 0 x 141 [89,96,8]
+ CRUSH rule 0 x 142 [70,73,13]
+ CRUSH rule 0 x 143 [51,26,22]
+ CRUSH rule 0 x 144 [13,55,1]
+ CRUSH rule 0 x 145 [77,100,6]
+ CRUSH rule 0 x 146 [96,73,22]
+ CRUSH rule 0 x 147 [2,89,9]
+ CRUSH rule 0 x 148 [74,91,8]
+ CRUSH rule 0 x 149 [76,19,45]
+ CRUSH rule 0 x 150 [38,105,8]
+ CRUSH rule 0 x 151 [90,85,7]
+ CRUSH rule 0 x 152 [49,84,21]
+ CRUSH rule 0 x 153 [71,42,9]
+ CRUSH rule 0 x 154 [94,23,4]
+ CRUSH rule 0 x 155 [75,119,3]
+ CRUSH rule 0 x 156 [107,18,19]
+ CRUSH rule 0 x 157 [112,57,8]
+ CRUSH rule 0 x 158 [26,3,103]
+ CRUSH rule 0 x 159 [52,17,41]
+ CRUSH rule 0 x 160 [41,119,7]
+ CRUSH rule 0 x 161 [84,51,4]
+ CRUSH rule 0 x 162 [55,2,22]
+ CRUSH rule 0 x 163 [54,21,31]
+ CRUSH rule 0 x 164 [45,44,6]
+ CRUSH rule 0 x 165 [25,116,14]
+ CRUSH rule 0 x 166 [73,38,7]
+ CRUSH rule 0 x 167 [89,119,21]
+ CRUSH rule 0 x 168 [47,90,6]
+ CRUSH rule 0 x 169 [51,22,24]
+ CRUSH rule 0 x 170 [68,53,9]
+ CRUSH rule 0 x 171 [73,28,13]
+ CRUSH rule 0 x 172 [117,23,17]
+ CRUSH rule 0 x 173 [13,40,25]
+ CRUSH rule 0 x 174 [116,85,3]
+ CRUSH rule 0 x 175 [3,85,1]
+ CRUSH rule 0 x 176 [94,83,22]
+ CRUSH rule 0 x 177 [52,29,7]
+ CRUSH rule 0 x 178 [39,42,9]
+ CRUSH rule 0 x 179 [72,89,4]
+ CRUSH rule 0 x 180 [60,67,7]
+ CRUSH rule 0 x 181 [18,16,15]
+ CRUSH rule 0 x 182 [22,5,71]
+ CRUSH rule 0 x 183 [11,110,25]
+ CRUSH rule 0 x 184 [92,15,91]
+ CRUSH rule 0 x 185 [97,117,4]
+ CRUSH rule 0 x 186 [67,96,21]
+ CRUSH rule 0 x 187 [116,14,31]
+ CRUSH rule 0 x 188 [69,54,14]
+ CRUSH rule 0 x 189 [47,113,11]
+ CRUSH rule 0 x 190 [65,90,8]
+ CRUSH rule 0 x 191 [49,113,17]
+ CRUSH rule 0 x 192 [68,93,7]
+ CRUSH rule 0 x 193 [0,37,15]
+ CRUSH rule 0 x 194 [62,63,19]
+ CRUSH rule 0 x 195 [119,11,67]
+ CRUSH rule 0 x 196 [72,59,7]
+ CRUSH rule 0 x 197 [106,49,8]
+ CRUSH rule 0 x 198 [114,21,39]
+ CRUSH rule 0 x 199 [0,99,17]
+ CRUSH rule 0 x 200 [35,102,13]
+ CRUSH rule 0 x 201 [27,104,11]
+ CRUSH rule 0 x 202 [98,59,7]
+ CRUSH rule 0 x 203 [36,91,22]
+ CRUSH rule 0 x 204 [10,113,9]
+ CRUSH rule 0 x 205 [81,22,52]
+ CRUSH rule 0 x 206 [49,92,19]
+ CRUSH rule 0 x 207 [80,19,25]
+ CRUSH rule 0 x 208 [63,92,21]
+ CRUSH rule 0 x 209 [85,78,13]
+ CRUSH rule 0 x 210 [79,76,15]
+ CRUSH rule 0 x 211 [26,89,6]
+ CRUSH rule 0 x 212 [28,33,11]
+ CRUSH rule 0 x 213 [91,102,3]
+ CRUSH rule 0 x 214 [91,118,6]
+ CRUSH rule 0 x 215 [61,58,22]
+ CRUSH rule 0 x 216 [99,108,9]
+ CRUSH rule 0 x 217 [86,97,14]
+ CRUSH rule 0 x 218 [70,15,97]
+ CRUSH rule 0 x 219 [28,91,19]
+ CRUSH rule 0 x 220 [56,9,23]
+ CRUSH rule 0 x 221 [0,21,45]
+ CRUSH rule 0 x 222 [50,65,13]
+ CRUSH rule 0 x 223 [29,46,4]
+ CRUSH rule 0 x 224 [52,71,17]
+ CRUSH rule 0 x 225 [15,87,112]
+ CRUSH rule 0 x 226 [44,13,65]
+ CRUSH rule 0 x 227 [42,21,35]
+ CRUSH rule 0 x 228 [117,55,17]
+ CRUSH rule 0 x 229 [100,67,21]
+ CRUSH rule 0 x 230 [41,52,17]
+ CRUSH rule 0 x 231 [56,61,22]
+ CRUSH rule 0 x 232 [23,11,44]
+ CRUSH rule 0 x 233 [88,35,9]
+ CRUSH rule 0 x 234 [4,55,94]
+ CRUSH rule 0 x 235 [26,16,11]
+ CRUSH rule 0 x 236 [32,39,15]
+ CRUSH rule 0 x 237 [92,4,97]
+ CRUSH rule 0 x 238 [10,117,21]
+ CRUSH rule 0 x 239 [15,10,96]
+ CRUSH rule 0 x 240 [109,3,99]
+ CRUSH rule 0 x 241 [47,44,14]
+ CRUSH rule 0 x 242 [24,61,8]
+ CRUSH rule 0 x 243 [76,9,101]
+ CRUSH rule 0 x 244 [103,17,78]
+ CRUSH rule 0 x 245 [27,34,14]
+ CRUSH rule 0 x 246 [5,35,11]
+ CRUSH rule 0 x 247 [99,38,4]
+ CRUSH rule 0 x 248 [8,45,42]
+ CRUSH rule 0 x 249 [85,38,3]
+ CRUSH rule 0 x 250 [93,78,3]
+ CRUSH rule 0 x 251 [28,41,15]
+ CRUSH rule 0 x 252 [95,3,56]
+ CRUSH rule 0 x 253 [109,97,19]
+ CRUSH rule 0 x 254 [80,11,41]
+ CRUSH rule 0 x 255 [103,22,110]
+ CRUSH rule 0 x 256 [37,82,14]
+ CRUSH rule 0 x 257 [69,104,6]
+ CRUSH rule 0 x 258 [34,63,3]
+ CRUSH rule 0 x 259 [70,9,75]
+ CRUSH rule 0 x 260 [98,43,7]
+ CRUSH rule 0 x 261 [94,77,22]
+ CRUSH rule 0 x 262 [42,45,9]
+ CRUSH rule 0 x 263 [65,68,21]
+ CRUSH rule 0 x 264 [36,45,22]
+ CRUSH rule 0 x 265 [66,97,14]
+ CRUSH rule 0 x 266 [75,64,17]
+ CRUSH rule 0 x 267 [58,39,8]
+ CRUSH rule 0 x 268 [38,3,47]
+ CRUSH rule 0 x 269 [86,91,3]
+ CRUSH rule 0 x 270 [58,43,7]
+ CRUSH rule 0 x 271 [19,43,88]
+ CRUSH rule 0 x 272 [73,8,52]
+ CRUSH rule 0 x 273 [108,16,9]
+ CRUSH rule 0 x 274 [47,88,8]
+ CRUSH rule 0 x 275 [92,21,99]
+ CRUSH rule 0 x 276 [7,57,100]
+ CRUSH rule 0 x 277 [19,117,87]
+ CRUSH rule 0 x 278 [116,63,13]
+ CRUSH rule 0 x 279 [101,102,15]
+ CRUSH rule 0 x 280 [113,75,17]
+ CRUSH rule 0 x 281 [14,97,56]
+ CRUSH rule 0 x 282 [106,53,11]
+ CRUSH rule 0 x 283 [8,36,41]
+ CRUSH rule 0 x 284 [10,32,15]
+ CRUSH rule 0 x 285 [88,63,9]
+ CRUSH rule 0 x 286 [27,6,48]
+ CRUSH rule 0 x 287 [84,101,4]
+ CRUSH rule 0 x 288 [103,22,100]
+ CRUSH rule 0 x 289 [9,26,45]
+ CRUSH rule 0 x 290 [115,9,31]
+ CRUSH rule 0 x 291 [48,47,13]
+ CRUSH rule 0 x 292 [89,108,15]
+ CRUSH rule 0 x 293 [27,118,11]
+ CRUSH rule 0 x 294 [79,111,21]
+ CRUSH rule 0 x 295 [37,18,11]
+ CRUSH rule 0 x 296 [56,27,7]
+ CRUSH rule 0 x 297 [35,28,19]
+ CRUSH rule 0 x 298 [71,2,6]
+ CRUSH rule 0 x 299 [116,85,6]
+ CRUSH rule 0 x 300 [67,26,21]
+ CRUSH rule 0 x 301 [51,32,13]
+ CRUSH rule 0 x 302 [78,105,13]
+ CRUSH rule 0 x 303 [19,82,67]
+ CRUSH rule 0 x 304 [101,50,21]
+ CRUSH rule 0 x 305 [81,68,21]
+ CRUSH rule 0 x 306 [0,97,17]
+ CRUSH rule 0 x 307 [44,21,63]
+ CRUSH rule 0 x 308 [91,2,9]
+ CRUSH rule 0 x 309 [38,39,19]
+ CRUSH rule 0 x 310 [26,15,75]
+ CRUSH rule 0 x 311 [36,75,3]
+ CRUSH rule 0 x 312 [33,15,58]
+ CRUSH rule 0 x 313 [104,65,17]
+ CRUSH rule 0 x 314 [28,9,61]
+ CRUSH rule 0 x 315 [16,72,14]
+ CRUSH rule 0 x 316 [4,76,23]
+ CRUSH rule 0 x 317 [118,13,55]
+ CRUSH rule 0 x 318 [17,77,92]
+ CRUSH rule 0 x 319 [24,93,3]
+ CRUSH rule 0 x 320 [36,41,13]
+ CRUSH rule 0 x 321 [26,81,3]
+ CRUSH rule 0 x 322 [87,24,8]
+ CRUSH rule 0 x 323 [73,76,19]
+ CRUSH rule 0 x 324 [21,75,110]
+ CRUSH rule 0 x 325 [52,43,3]
+ CRUSH rule 0 x 326 [111,105,4]
+ CRUSH rule 0 x 327 [62,17,16]
+ CRUSH rule 0 x 328 [7,0,99]
+ CRUSH rule 0 x 329 [93,14,32]
+ CRUSH rule 0 x 330 [24,15,37]
+ CRUSH rule 0 x 331 [41,109,4]
+ CRUSH rule 0 x 332 [61,111,11]
+ CRUSH rule 0 x 333 [16,6,5]
+ CRUSH rule 0 x 334 [3,29,36]
+ CRUSH rule 0 x 335 [71,66,22]
+ CRUSH rule 0 x 336 [16,11,5]
+ CRUSH rule 0 x 337 [37,113,9]
+ CRUSH rule 0 x 338 [109,6,41]
+ CRUSH rule 0 x 339 [37,22,1]
+ CRUSH rule 0 x 340 [119,101,19]
+ CRUSH rule 0 x 341 [63,14,114]
+ CRUSH rule 0 x 342 [92,71,8]
+ CRUSH rule 0 x 343 [49,56,7]
+ CRUSH rule 0 x 344 [103,113,17]
+ CRUSH rule 0 x 345 [56,35,22]
+ CRUSH rule 0 x 346 [3,25,40]
+ CRUSH rule 0 x 347 [106,85,21]
+ CRUSH rule 0 x 348 [10,114,6]
+ CRUSH rule 0 x 349 [96,103,6]
+ CRUSH rule 0 x 350 [63,32,22]
+ CRUSH rule 0 x 351 [60,73,13]
+ CRUSH rule 0 x 352 [103,68,9]
+ CRUSH rule 0 x 353 [49,113,17]
+ CRUSH rule 0 x 354 [55,74,8]
+ CRUSH rule 0 x 355 [73,80,11]
+ CRUSH rule 0 x 356 [114,65,11]
+ CRUSH rule 0 x 357 [14,110,41]
+ CRUSH rule 0 x 358 [97,56,11]
+ CRUSH rule 0 x 359 [4,89,106]
+ CRUSH rule 0 x 360 [106,31,4]
+ CRUSH rule 0 x 361 [27,56,21]
+ CRUSH rule 0 x 362 [28,55,15]
+ CRUSH rule 0 x 363 [45,60,19]
+ CRUSH rule 0 x 364 [23,2,17]
+ CRUSH rule 0 x 365 [24,21,35]
+ CRUSH rule 0 x 366 [14,100,33]
+ CRUSH rule 0 x 367 [103,82,13]
+ CRUSH rule 0 x 368 [103,17,44]
+ CRUSH rule 0 x 369 [37,11,94]
+ CRUSH rule 0 x 370 [11,65,76]
+ CRUSH rule 0 x 371 [34,65,15]
+ CRUSH rule 0 x 372 [58,23,9]
+ CRUSH rule 0 x 373 [98,22,47]
+ CRUSH rule 0 x 374 [110,89,13]
+ CRUSH rule 0 x 375 [19,76,49]
+ CRUSH rule 0 x 376 [22,98,63]
+ CRUSH rule 0 x 377 [98,87,21]
+ CRUSH rule 0 x 378 [67,58,14]
+ CRUSH rule 0 x 379 [77,94,7]
+ CRUSH rule 0 x 380 [69,108,14]
+ CRUSH rule 0 x 381 [55,106,13]
+ CRUSH rule 0 x 382 [26,83,13]
+ CRUSH rule 0 x 383 [48,93,22]
+ CRUSH rule 0 x 384 [15,0,59]
+ CRUSH rule 0 x 385 [82,27,15]
+ CRUSH rule 0 x 386 [108,25,15]
+ CRUSH rule 0 x 387 [70,14,91]
+ CRUSH rule 0 x 388 [5,37,11]
+ CRUSH rule 0 x 389 [14,67,1]
+ CRUSH rule 0 x 390 [68,77,8]
+ CRUSH rule 0 x 391 [113,105,19]
+ CRUSH rule 0 x 392 [72,13,39]
+ CRUSH rule 0 x 393 [115,21,97]
+ CRUSH rule 0 x 394 [38,17,49]
+ CRUSH rule 0 x 395 [0,65,3]
+ CRUSH rule 0 x 396 [59,116,4]
+ CRUSH rule 0 x 397 [87,90,11]
+ CRUSH rule 0 x 398 [44,51,7]
+ CRUSH rule 0 x 399 [9,113,65]
+ CRUSH rule 0 x 400 [101,100,11]
+ CRUSH rule 0 x 401 [79,52,8]
+ CRUSH rule 0 x 402 [107,110,8]
+ CRUSH rule 0 x 403 [23,92,13]
+ CRUSH rule 0 x 404 [76,31,13]
+ CRUSH rule 0 x 405 [10,48,8]
+ CRUSH rule 0 x 406 [38,29,4]
+ CRUSH rule 0 x 407 [70,25,11]
+ CRUSH rule 0 x 408 [55,104,22]
+ CRUSH rule 0 x 409 [102,6,23]
+ CRUSH rule 0 x 410 [59,8,92]
+ CRUSH rule 0 x 411 [34,49,15]
+ CRUSH rule 0 x 412 [108,105,7]
+ CRUSH rule 0 x 413 [54,37,13]
+ CRUSH rule 0 x 414 [70,3,10]
+ CRUSH rule 0 x 415 [107,0,6]
+ CRUSH rule 0 x 416 [79,24,22]
+ CRUSH rule 0 x 417 [8,23,36]
+ CRUSH rule 0 x 418 [51,114,9]
+ CRUSH rule 0 x 419 [117,55,8]
+ CRUSH rule 0 x 420 [109,71,17]
+ CRUSH rule 0 x 421 [114,17,75]
+ CRUSH rule 0 x 422 [109,14,55]
+ CRUSH rule 0 x 423 [59,0,9]
+ CRUSH rule 0 x 424 [71,84,3]
+ CRUSH rule 0 x 425 [101,50,14]
+ CRUSH rule 0 x 426 [47,88,7]
+ CRUSH rule 0 x 427 [86,45,17]
+ CRUSH rule 0 x 428 [68,31,6]
+ CRUSH rule 0 x 429 [76,13,59]
+ CRUSH rule 0 x 430 [9,117,97]
+ CRUSH rule 0 x 431 [105,66,17]
+ CRUSH rule 0 x 432 [46,91,13]
+ CRUSH rule 0 x 433 [6,77,1]
+ CRUSH rule 0 x 434 [64,59,7]
+ CRUSH rule 0 x 435 [16,2,15]
+ CRUSH rule 0 x 436 [89,102,3]
+ CRUSH rule 0 x 437 [29,78,14]
+ CRUSH rule 0 x 438 [105,56,7]
+ CRUSH rule 0 x 439 [29,68,22]
+ CRUSH rule 0 x 440 [38,7,63]
+ CRUSH rule 0 x 441 [112,57,6]
+ CRUSH rule 0 x 442 [55,18,22]
+ CRUSH rule 0 x 443 [44,37,3]
+ CRUSH rule 0 x 444 [11,49,60]
+ CRUSH rule 0 x 445 [19,114,59]
+ CRUSH rule 0 x 446 [40,43,22]
+ CRUSH rule 0 x 447 [100,43,17]
+ CRUSH rule 0 x 448 [7,26,55]
+ CRUSH rule 0 x 449 [67,13,66]
+ CRUSH rule 0 x 450 [117,97,17]
+ CRUSH rule 0 x 451 [93,118,11]
+ CRUSH rule 0 x 452 [70,37,8]
+ CRUSH rule 0 x 453 [82,55,8]
+ CRUSH rule 0 x 454 [53,28,22]
+ CRUSH rule 0 x 455 [91,34,19]
+ CRUSH rule 0 x 456 [17,55,104]
+ CRUSH rule 0 x 457 [113,103,13]
+ CRUSH rule 0 x 458 [119,41,9]
+ CRUSH rule 0 x 459 [25,104,8]
+ CRUSH rule 0 x 460 [11,55,119]
+ CRUSH rule 0 x 461 [21,5,39]
+ CRUSH rule 0 x 462 [25,72,8]
+ CRUSH rule 0 x 463 [6,57,80]
+ CRUSH rule 0 x 464 [19,50,91]
+ CRUSH rule 0 x 465 [29,7,5]
+ CRUSH rule 0 x 466 [66,89,9]
+ CRUSH rule 0 x 467 [27,32,15]
+ CRUSH rule 0 x 468 [97,118,3]
+ CRUSH rule 0 x 469 [98,71,22]
+ CRUSH rule 0 x 470 [50,29,3]
+ CRUSH rule 0 x 471 [40,31,13]
+ CRUSH rule 0 x 472 [74,61,19]
+ CRUSH rule 0 x 473 [95,98,14]
+ CRUSH rule 0 x 474 [51,8,32]
+ CRUSH rule 0 x 475 [3,25,117]
+ CRUSH rule 0 x 476 [110,55,15]
+ CRUSH rule 0 x 477 [25,74,14]
+ CRUSH rule 0 x 478 [19,57,38]
+ CRUSH rule 0 x 479 [70,91,8]
+ CRUSH rule 0 x 480 [62,33,3]
+ CRUSH rule 0 x 481 [26,3,75]
+ CRUSH rule 0 x 482 [84,6,29]
+ CRUSH rule 0 x 483 [36,55,7]
+ CRUSH rule 0 x 484 [37,28,7]
+ CRUSH rule 0 x 485 [84,14,47]
+ CRUSH rule 0 x 486 [92,61,11]
+ CRUSH rule 0 x 487 [106,53,17]
+ CRUSH rule 0 x 488 [42,7,55]
+ CRUSH rule 0 x 489 [76,31,13]
+ CRUSH rule 0 x 490 [68,107,22]
+ CRUSH rule 0 x 491 [80,57,3]
+ CRUSH rule 0 x 492 [21,71,113]
+ CRUSH rule 0 x 493 [99,44,6]
+ CRUSH rule 0 x 494 [4,59,98]
+ CRUSH rule 0 x 495 [40,87,17]
+ CRUSH rule 0 x 496 [13,106,71]
+ CRUSH rule 0 x 497 [102,81,9]
+ CRUSH rule 0 x 498 [68,73,21]
+ CRUSH rule 0 x 499 [22,28,107]
+ CRUSH rule 0 x 500 [50,6,81]
+ CRUSH rule 0 x 501 [60,103,19]
+ CRUSH rule 0 x 502 [11,1,45]
+ CRUSH rule 0 x 503 [117,85,4]
+ CRUSH rule 0 x 504 [90,55,9]
+ CRUSH rule 0 x 505 [91,94,3]
+ CRUSH rule 0 x 506 [82,89,21]
+ CRUSH rule 0 x 507 [6,77,54]
+ CRUSH rule 0 x 508 [34,77,13]
+ CRUSH rule 0 x 509 [88,43,3]
+ CRUSH rule 0 x 510 [11,69,100]
+ CRUSH rule 0 x 511 [72,47,11]
+ CRUSH rule 0 x 512 [118,101,4]
+ CRUSH rule 0 x 513 [22,80,10]
+ CRUSH rule 0 x 514 [82,21,69]
+ CRUSH rule 0 x 515 [27,38,21]
+ CRUSH rule 0 x 516 [66,61,19]
+ CRUSH rule 0 x 517 [83,4,44]
+ CRUSH rule 0 x 518 [18,13,107]
+ CRUSH rule 0 x 519 [67,52,7]
+ CRUSH rule 0 x 520 [15,88,27]
+ CRUSH rule 0 x 521 [63,62,22]
+ CRUSH rule 0 x 522 [4,51,118]
+ CRUSH rule 0 x 523 [36,23,3]
+ CRUSH rule 0 x 524 [33,94,4]
+ CRUSH rule 0 x 525 [63,104,7]
+ CRUSH rule 0 x 526 [83,118,3]
+ CRUSH rule 0 x 527 [37,5,9]
+ CRUSH rule 0 x 528 [108,43,15]
+ CRUSH rule 0 x 529 [107,7,60]
+ CRUSH rule 0 x 530 [49,11,80]
+ CRUSH rule 0 x 531 [27,82,22]
+ CRUSH rule 0 x 532 [68,89,21]
+ CRUSH rule 0 x 533 [5,73,15]
+ CRUSH rule 0 x 534 [97,104,3]
+ CRUSH rule 0 x 535 [48,41,14]
+ CRUSH rule 0 x 536 [3,71,52]
+ CRUSH rule 0 x 537 [116,7,83]
+ CRUSH rule 0 x 538 [85,3,56]
+ CRUSH rule 0 x 539 [10,82,4]
+ CRUSH rule 0 x 540 [100,31,4]
+ CRUSH rule 0 x 541 [111,67,21]
+ CRUSH rule 0 x 542 [50,103,9]
+ CRUSH rule 0 x 543 [45,21,113]
+ CRUSH rule 0 x 544 [106,67,14]
+ CRUSH rule 0 x 545 [43,86,8]
+ CRUSH rule 0 x 546 [108,49,3]
+ CRUSH rule 0 x 547 [27,18,6]
+ CRUSH rule 0 x 548 [53,66,4]
+ CRUSH rule 0 x 549 [60,89,6]
+ CRUSH rule 0 x 550 [47,62,21]
+ CRUSH rule 0 x 551 [14,52,71]
+ CRUSH rule 0 x 552 [70,10,17]
+ CRUSH rule 0 x 553 [96,73,8]
+ CRUSH rule 0 x 554 [61,70,7]
+ CRUSH rule 0 x 555 [76,69,9]
+ CRUSH rule 0 x 556 [106,10,22]
+ CRUSH rule 0 x 557 [39,58,11]
+ CRUSH rule 0 x 558 [70,93,14]
+ CRUSH rule 0 x 559 [106,23,21]
+ CRUSH rule 0 x 560 [94,16,8]
+ CRUSH rule 0 x 561 [27,68,6]
+ CRUSH rule 0 x 562 [97,112,15]
+ CRUSH rule 0 x 563 [64,61,21]
+ CRUSH rule 0 x 564 [96,59,8]
+ CRUSH rule 0 x 565 [66,69,3]
+ CRUSH rule 0 x 566 [27,86,11]
+ CRUSH rule 0 x 567 [88,4,25]
+ CRUSH rule 0 x 568 [17,96,69]
+ CRUSH rule 0 x 569 [102,29,11]
+ CRUSH rule 0 x 570 [7,103,5]
+ CRUSH rule 0 x 571 [95,110,11]
+ CRUSH rule 0 x 572 [62,33,3]
+ CRUSH rule 0 x 573 [51,46,6]
+ CRUSH rule 0 x 574 [89,64,17]
+ CRUSH rule 0 x 575 [19,53,113]
+ CRUSH rule 0 x 576 [112,87,14]
+ CRUSH rule 0 x 577 [8,113,63]
+ CRUSH rule 0 x 578 [64,3,35]
+ CRUSH rule 0 x 579 [78,37,3]
+ CRUSH rule 0 x 580 [68,35,8]
+ CRUSH rule 0 x 581 [55,113,7]
+ CRUSH rule 0 x 582 [27,19,38]
+ CRUSH rule 0 x 583 [74,99,22]
+ CRUSH rule 0 x 584 [72,53,21]
+ CRUSH rule 0 x 585 [88,79,22]
+ CRUSH rule 0 x 586 [33,1,4]
+ CRUSH rule 0 x 587 [106,53,14]
+ CRUSH rule 0 x 588 [0,45,17]
+ CRUSH rule 0 x 589 [7,85,112]
+ CRUSH rule 0 x 590 [59,40,11]
+ CRUSH rule 0 x 591 [42,43,14]
+ CRUSH rule 0 x 592 [45,110,17]
+ CRUSH rule 0 x 593 [89,14,114]
+ CRUSH rule 0 x 594 [27,76,22]
+ CRUSH rule 0 x 595 [7,10,117]
+ CRUSH rule 0 x 596 [82,41,13]
+ CRUSH rule 0 x 597 [72,97,6]
+ CRUSH rule 0 x 598 [34,17,65]
+ CRUSH rule 0 x 599 [119,53,15]
+ CRUSH rule 0 x 600 [9,36,69]
+ CRUSH rule 0 x 601 [104,21,87]
+ CRUSH rule 0 x 602 [48,39,7]
+ CRUSH rule 0 x 603 [24,11,89]
+ CRUSH rule 0 x 604 [89,82,7]
+ CRUSH rule 0 x 605 [104,63,9]
+ CRUSH rule 0 x 606 [49,58,4]
+ CRUSH rule 0 x 607 [95,72,19]
+ CRUSH rule 0 x 608 [49,48,19]
+ CRUSH rule 0 x 609 [61,70,3]
+ CRUSH rule 0 x 610 [106,73,11]
+ CRUSH rule 0 x 611 [66,37,17]
+ CRUSH rule 0 x 612 [103,84,3]
+ CRUSH rule 0 x 613 [84,57,9]
+ CRUSH rule 0 x 614 [81,9,88]
+ CRUSH rule 0 x 615 [61,9,109]
+ CRUSH rule 0 x 616 [41,8,119]
+ CRUSH rule 0 x 617 [111,81,4]
+ CRUSH rule 0 x 618 [3,39,104]
+ CRUSH rule 0 x 619 [92,31,11]
+ CRUSH rule 0 x 620 [108,31,11]
+ CRUSH rule 0 x 621 [106,57,3]
+ CRUSH rule 0 x 622 [67,102,7]
+ CRUSH rule 0 x 623 [94,7,93]
+ CRUSH rule 0 x 624 [115,29,13]
+ CRUSH rule 0 x 625 [111,67,21]
+ CRUSH rule 0 x 626 [3,25,40]
+ CRUSH rule 0 x 627 [19,105,56]
+ CRUSH rule 0 x 628 [65,100,9]
+ CRUSH rule 0 x 629 [119,15,65]
+ CRUSH rule 0 x 630 [109,4,91]
+ CRUSH rule 0 x 631 [48,33,17]
+ CRUSH rule 0 x 632 [81,60,14]
+ CRUSH rule 0 x 633 [65,110,9]
+ CRUSH rule 0 x 634 [87,50,14]
+ CRUSH rule 0 x 635 [107,9,104]
+ CRUSH rule 0 x 636 [23,66,9]
+ CRUSH rule 0 x 637 [102,29,4]
+ CRUSH rule 0 x 638 [43,4,109]
+ CRUSH rule 0 x 639 [31,76,9]
+ CRUSH rule 0 x 640 [113,87,7]
+ CRUSH rule 0 x 641 [45,58,7]
+ CRUSH rule 0 x 642 [47,17,102]
+ CRUSH rule 0 x 643 [64,97,7]
+ CRUSH rule 0 x 644 [31,4,94]
+ CRUSH rule 0 x 645 [76,13,31]
+ CRUSH rule 0 x 646 [37,86,15]
+ CRUSH rule 0 x 647 [58,101,21]
+ CRUSH rule 0 x 648 [31,9,56]
+ CRUSH rule 0 x 649 [88,39,15]
+ CRUSH rule 0 x 650 [116,19,71]
+ CRUSH rule 0 x 651 [97,116,22]
+ CRUSH rule 0 x 652 [57,28,19]
+ CRUSH rule 0 x 653 [38,95,21]
+ CRUSH rule 0 x 654 [49,92,9]
+ CRUSH rule 0 x 655 [89,54,11]
+ CRUSH rule 0 x 656 [0,89,4]
+ CRUSH rule 0 x 657 [47,18,19]
+ CRUSH rule 0 x 658 [75,32,17]
+ CRUSH rule 0 x 659 [26,33,14]
+ CRUSH rule 0 x 660 [65,82,21]
+ CRUSH rule 0 x 661 [91,76,17]
+ CRUSH rule 0 x 662 [111,73,6]
+ CRUSH rule 0 x 663 [88,67,3]
+ CRUSH rule 0 x 664 [59,52,15]
+ CRUSH rule 0 x 665 [78,7,59]
+ CRUSH rule 0 x 666 [112,8,31]
+ CRUSH rule 0 x 667 [97,80,6]
+ CRUSH rule 0 x 668 [97,22,92]
+ CRUSH rule 0 x 669 [85,0,6]
+ CRUSH rule 0 x 670 [41,62,7]
+ CRUSH rule 0 x 671 [116,37,7]
+ CRUSH rule 0 x 672 [44,67,22]
+ CRUSH rule 0 x 673 [83,116,9]
+ CRUSH rule 0 x 674 [59,98,14]
+ CRUSH rule 0 x 675 [88,17,83]
+ CRUSH rule 0 x 676 [62,4,75]
+ CRUSH rule 0 x 677 [88,105,3]
+ CRUSH rule 0 x 678 [98,57,3]
+ CRUSH rule 0 x 679 [70,61,9]
+ CRUSH rule 0 x 680 [55,5,14]
+ CRUSH rule 0 x 681 [53,68,3]
+ CRUSH rule 0 x 682 [27,78,7]
+ CRUSH rule 0 x 683 [57,118,11]
+ CRUSH rule 0 x 684 [98,45,22]
+ CRUSH rule 0 x 685 [106,25,3]
+ CRUSH rule 0 x 686 [86,45,6]
+ CRUSH rule 0 x 687 [49,102,15]
+ CRUSH rule 0 x 688 [16,52,7]
+ CRUSH rule 0 x 689 [6,112,59]
+ CRUSH rule 0 x 690 [43,17,48]
+ CRUSH rule 0 x 691 [34,99,8]
+ CRUSH rule 0 x 692 [40,67,8]
+ CRUSH rule 0 x 693 [29,118,22]
+ CRUSH rule 0 x 694 [6,75,84]
+ CRUSH rule 0 x 695 [31,72,7]
+ CRUSH rule 0 x 696 [36,3,16]
+ CRUSH rule 0 x 697 [96,99,11]
+ CRUSH rule 0 x 698 [61,100,4]
+ CRUSH rule 0 x 699 [47,60,15]
+ CRUSH rule 0 x 700 [0,93,15]
+ CRUSH rule 0 x 701 [42,21,105]
+ CRUSH rule 0 x 702 [0,105,21]
+ CRUSH rule 0 x 703 [92,29,13]
+ CRUSH rule 0 x 704 [10,8,109]
+ CRUSH rule 0 x 705 [105,0,6]
+ CRUSH rule 0 x 706 [74,10,13]
+ CRUSH rule 0 x 707 [0,91,14]
+ CRUSH rule 0 x 708 [84,21,89]
+ CRUSH rule 0 x 709 [114,23,8]
+ CRUSH rule 0 x 710 [94,19,35]
+ CRUSH rule 0 x 711 [68,41,6]
+ CRUSH rule 0 x 712 [34,71,11]
+ CRUSH rule 0 x 713 [29,2,19]
+ CRUSH rule 0 x 714 [81,80,17]
+ CRUSH rule 0 x 715 [71,62,13]
+ CRUSH rule 0 x 716 [40,6,37]
+ CRUSH rule 0 x 717 [61,60,9]
+ CRUSH rule 0 x 718 [40,69,15]
+ CRUSH rule 0 x 719 [59,74,21]
+ CRUSH rule 0 x 720 [69,2,22]
+ CRUSH rule 0 x 721 [62,75,6]
+ CRUSH rule 0 x 722 [115,19,95]
+ CRUSH rule 0 x 723 [117,25,21]
+ CRUSH rule 0 x 724 [45,3,26]
+ CRUSH rule 0 x 725 [53,110,19]
+ CRUSH rule 0 x 726 [84,107,8]
+ CRUSH rule 0 x 727 [109,19,107]
+ CRUSH rule 0 x 728 [76,65,6]
+ CRUSH rule 0 x 729 [108,7,47]
+ CRUSH rule 0 x 730 [28,37,21]
+ CRUSH rule 0 x 731 [78,41,6]
+ CRUSH rule 0 x 732 [55,112,11]
+ CRUSH rule 0 x 733 [84,7,67]
+ CRUSH rule 0 x 734 [27,110,8]
+ CRUSH rule 0 x 735 [83,62,17]
+ CRUSH rule 0 x 736 [70,67,14]
+ CRUSH rule 0 x 737 [117,11,99]
+ CRUSH rule 0 x 738 [118,95,17]
+ CRUSH rule 0 x 739 [87,1,17]
+ CRUSH rule 0 x 740 [29,92,13]
+ CRUSH rule 0 x 741 [96,49,19]
+ CRUSH rule 0 x 742 [106,31,14]
+ CRUSH rule 0 x 743 [105,5,9]
+ CRUSH rule 0 x 744 [23,64,6]
+ CRUSH rule 0 x 745 [28,85,21]
+ CRUSH rule 0 x 746 [18,47,6]
+ CRUSH rule 0 x 747 [65,108,14]
+ CRUSH rule 0 x 748 [48,25,21]
+ CRUSH rule 0 x 749 [102,71,19]
+ CRUSH rule 0 x 750 [50,77,13]
+ CRUSH rule 0 x 751 [36,29,11]
+ CRUSH rule 0 x 752 [69,119,9]
+ CRUSH rule 0 x 753 [9,34,83]
+ CRUSH rule 0 x 754 [9,39,52]
+ CRUSH rule 0 x 755 [98,45,17]
+ CRUSH rule 0 x 756 [113,83,4]
+ CRUSH rule 0 x 757 [47,112,21]
+ CRUSH rule 0 x 758 [57,84,17]
+ CRUSH rule 0 x 759 [74,65,9]
+ CRUSH rule 0 x 760 [53,34,9]
+ CRUSH rule 0 x 761 [78,105,19]
+ CRUSH rule 0 x 762 [87,13,94]
+ CRUSH rule 0 x 763 [13,16,98]
+ CRUSH rule 0 x 764 [106,27,22]
+ CRUSH rule 0 x 765 [109,77,17]
+ CRUSH rule 0 x 766 [76,105,13]
+ CRUSH rule 0 x 767 [41,80,11]
+ CRUSH rule 0 x 768 [13,50,71]
+ CRUSH rule 0 x 769 [91,96,9]
+ CRUSH rule 0 x 770 [105,62,17]
+ CRUSH rule 0 x 771 [10,28,4]
+ CRUSH rule 0 x 772 [8,102,31]
+ CRUSH rule 0 x 773 [116,91,7]
+ CRUSH rule 0 x 774 [100,105,22]
+ CRUSH rule 0 x 775 [15,61,18]
+ CRUSH rule 0 x 776 [69,44,15]
+ CRUSH rule 0 x 777 [76,23,4]
+ CRUSH rule 0 x 778 [38,9,16]
+ CRUSH rule 0 x 779 [46,17,79]
+ CRUSH rule 0 x 780 [63,70,8]
+ CRUSH rule 0 x 781 [19,16,108]
+ CRUSH rule 0 x 782 [117,59,21]
+ CRUSH rule 0 x 783 [60,25,7]
+ CRUSH rule 0 x 784 [82,81,3]
+ CRUSH rule 0 x 785 [27,50,11]
+ CRUSH rule 0 x 786 [41,90,15]
+ CRUSH rule 0 x 787 [13,34,95]
+ CRUSH rule 0 x 788 [4,113,103]
+ CRUSH rule 0 x 789 [50,51,4]
+ CRUSH rule 0 x 790 [58,95,7]
+ CRUSH rule 0 x 791 [96,37,14]
+ CRUSH rule 0 x 792 [45,13,82]
+ CRUSH rule 0 x 793 [6,103,26]
+ CRUSH rule 0 x 794 [14,61,108]
+ CRUSH rule 0 x 795 [51,26,14]
+ CRUSH rule 0 x 796 [114,43,6]
+ CRUSH rule 0 x 797 [79,115,3]
+ CRUSH rule 0 x 798 [42,19,69]
+ CRUSH rule 0 x 799 [48,41,6]
+ CRUSH rule 0 x 800 [91,22,38]
+ CRUSH rule 0 x 801 [2,11,57]
+ CRUSH rule 0 x 802 [116,19,71]
+ CRUSH rule 0 x 803 [37,46,15]
+ CRUSH rule 0 x 804 [6,93,40]
+ CRUSH rule 0 x 805 [96,3,49]
+ CRUSH rule 0 x 806 [67,110,22]
+ CRUSH rule 0 x 807 [47,92,4]
+ CRUSH rule 0 x 808 [76,31,9]
+ CRUSH rule 0 x 809 [27,90,13]
+ CRUSH rule 0 x 810 [119,35,9]
+ CRUSH rule 0 x 811 [75,84,14]
+ CRUSH rule 0 x 812 [25,94,4]
+ CRUSH rule 0 x 813 [64,27,13]
+ CRUSH rule 0 x 814 [110,29,13]
+ CRUSH rule 0 x 815 [84,39,4]
+ CRUSH rule 0 x 816 [25,3,38]
+ CRUSH rule 0 x 817 [40,57,22]
+ CRUSH rule 0 x 818 [34,16,13]
+ CRUSH rule 0 x 819 [88,15,75]
+ CRUSH rule 0 x 820 [104,29,9]
+ CRUSH rule 0 x 821 [58,16,11]
+ CRUSH rule 0 x 822 [29,98,8]
+ CRUSH rule 0 x 823 [100,37,17]
+ CRUSH rule 0 x 824 [102,95,22]
+ CRUSH rule 0 x 825 [47,14,26]
+ CRUSH rule 0 x 826 [45,8,34]
+ CRUSH rule 0 x 827 [101,19,70]
+ CRUSH rule 0 x 828 [60,27,14]
+ CRUSH rule 0 x 829 [45,102,17]
+ CRUSH rule 0 x 830 [51,0,21]
+ CRUSH rule 0 x 831 [6,64,53]
+ CRUSH rule 0 x 832 [57,116,19]
+ CRUSH rule 0 x 833 [34,105,9]
+ CRUSH rule 0 x 834 [90,77,13]
+ CRUSH rule 0 x 835 [55,50,11]
+ CRUSH rule 0 x 836 [38,51,3]
+ CRUSH rule 0 x 837 [51,78,14]
+ CRUSH rule 0 x 838 [6,98,35]
+ CRUSH rule 0 x 839 [106,15,31]
+ CRUSH rule 0 x 840 [33,117,13]
+ CRUSH rule 0 x 841 [110,13,55]
+ CRUSH rule 0 x 842 [66,83,17]
+ CRUSH rule 0 x 843 [62,107,22]
+ CRUSH rule 0 x 844 [74,22,57]
+ CRUSH rule 0 x 845 [74,63,22]
+ CRUSH rule 0 x 846 [98,41,19]
+ CRUSH rule 0 x 847 [10,90,13]
+ CRUSH rule 0 x 848 [89,19,52]
+ CRUSH rule 0 x 849 [42,61,17]
+ CRUSH rule 0 x 850 [40,87,6]
+ CRUSH rule 0 x 851 [65,11,86]
+ CRUSH rule 0 x 852 [31,100,9]
+ CRUSH rule 0 x 853 [49,11,80]
+ CRUSH rule 0 x 854 [83,92,21]
+ CRUSH rule 0 x 855 [2,22,101]
+ CRUSH rule 0 x 856 [6,41,86]
+ CRUSH rule 0 x 857 [15,110,99]
+ CRUSH rule 0 x 858 [10,114,19]
+ CRUSH rule 0 x 859 [14,41,88]
+ CRUSH rule 0 x 860 [114,93,8]
+ CRUSH rule 0 x 861 [1,105,14]
+ CRUSH rule 0 x 862 [22,27,86]
+ CRUSH rule 0 x 863 [79,50,19]
+ CRUSH rule 0 x 864 [68,19,57]
+ CRUSH rule 0 x 865 [25,68,14]
+ CRUSH rule 0 x 866 [18,85,11]
+ CRUSH rule 0 x 867 [53,58,13]
+ CRUSH rule 0 x 868 [81,0,11]
+ CRUSH rule 0 x 869 [111,22,73]
+ CRUSH rule 0 x 870 [73,94,9]
+ CRUSH rule 0 x 871 [25,64,7]
+ CRUSH rule 0 x 872 [39,2,11]
+ CRUSH rule 0 x 873 [92,6,41]
+ CRUSH rule 0 x 874 [96,17,31]
+ CRUSH rule 0 x 875 [115,27,15]
+ CRUSH rule 0 x 876 [98,16,8]
+ CRUSH rule 0 x 877 [73,46,9]
+ CRUSH rule 0 x 878 [64,45,8]
+ CRUSH rule 0 x 879 [15,1,59]
+ CRUSH rule 0 x 880 [56,105,15]
+ CRUSH rule 0 x 881 [109,97,11]
+ CRUSH rule 0 x 882 [60,79,15]
+ CRUSH rule 0 x 883 [93,17,82]
+ CRUSH rule 0 x 884 [67,36,19]
+ CRUSH rule 0 x 885 [31,104,22]
+ CRUSH rule 0 x 886 [2,7,27]
+ CRUSH rule 0 x 887 [5,9,45]
+ CRUSH rule 0 x 888 [16,22,70]
+ CRUSH rule 0 x 889 [27,2,7]
+ CRUSH rule 0 x 890 [48,47,15]
+ CRUSH rule 0 x 891 [86,59,8]
+ CRUSH rule 0 x 892 [64,91,4]
+ CRUSH rule 0 x 893 [118,7,33]
+ CRUSH rule 0 x 894 [16,94,8]
+ CRUSH rule 0 x 895 [40,101,3]
+ CRUSH rule 0 x 896 [97,119,17]
+ CRUSH rule 0 x 897 [107,80,19]
+ CRUSH rule 0 x 898 [10,88,15]
+ CRUSH rule 0 x 899 [75,1,7]
+ CRUSH rule 0 x 900 [102,55,19]
+ CRUSH rule 0 x 901 [66,61,9]
+ CRUSH rule 0 x 902 [102,10,7]
+ CRUSH rule 0 x 903 [5,33,7]
+ CRUSH rule 0 x 904 [50,10,22]
+ CRUSH rule 0 x 905 [99,5,13]
+ CRUSH rule 0 x 906 [75,119,22]
+ CRUSH rule 0 x 907 [47,34,9]
+ CRUSH rule 0 x 908 [96,73,19]
+ CRUSH rule 0 x 909 [94,87,13]
+ CRUSH rule 0 x 910 [88,57,4]
+ CRUSH rule 0 x 911 [102,43,21]
+ CRUSH rule 0 x 912 [91,111,9]
+ CRUSH rule 0 x 913 [29,21,34]
+ CRUSH rule 0 x 914 [84,19,29]
+ CRUSH rule 0 x 915 [70,43,14]
+ CRUSH rule 0 x 916 [32,7,81]
+ CRUSH rule 0 x 917 [43,102,13]
+ CRUSH rule 0 x 918 [91,26,11]
+ CRUSH rule 0 x 919 [13,51,28]
+ CRUSH rule 0 x 920 [18,13,10]
+ CRUSH rule 0 x 921 [104,8,65]
+ CRUSH rule 0 x 922 [33,96,11]
+ CRUSH rule 0 x 923 [28,15,27]
+ CRUSH rule 0 x 924 [69,76,3]
+ CRUSH rule 0 x 925 [71,104,15]
+ CRUSH rule 0 x 926 [64,65,11]
+ CRUSH rule 0 x 927 [99,6,76]
+ CRUSH rule 0 x 928 [13,94,65]
+ CRUSH rule 0 x 929 [117,95,6]
+ CRUSH rule 0 x 930 [31,111,4]
+ CRUSH rule 0 x 931 [83,64,6]
+ CRUSH rule 0 x 932 [60,21,35]
+ CRUSH rule 0 x 933 [63,113,13]
+ CRUSH rule 0 x 934 [68,21,51]
+ CRUSH rule 0 x 935 [31,46,13]
+ CRUSH rule 0 x 936 [65,116,21]
+ CRUSH rule 0 x 937 [110,65,6]
+ CRUSH rule 0 x 938 [29,98,4]
+ CRUSH rule 0 x 939 [77,11,42]
+ CRUSH rule 0 x 940 [76,19,49]
+ CRUSH rule 0 x 941 [66,10,22]
+ CRUSH rule 0 x 942 [83,32,8]
+ CRUSH rule 0 x 943 [32,9,75]
+ CRUSH rule 0 x 944 [113,7,47]
+ CRUSH rule 0 x 945 [71,111,22]
+ CRUSH rule 0 x 946 [37,42,17]
+ CRUSH rule 0 x 947 [107,48,7]
+ CRUSH rule 0 x 948 [55,24,13]
+ CRUSH rule 0 x 949 [11,109,75]
+ CRUSH rule 0 x 950 [96,33,14]
+ CRUSH rule 0 x 951 [40,63,7]
+ CRUSH rule 0 x 952 [93,5,21]
+ CRUSH rule 0 x 953 [55,28,7]
+ CRUSH rule 0 x 954 [84,83,4]
+ CRUSH rule 0 x 955 [31,90,9]
+ CRUSH rule 0 x 956 [72,6,91]
+ CRUSH rule 0 x 957 [3,88,16]
+ CRUSH rule 0 x 958 [23,74,14]
+ CRUSH rule 0 x 959 [42,93,15]
+ CRUSH rule 0 x 960 [113,91,19]
+ CRUSH rule 0 x 961 [116,4,89]
+ CRUSH rule 0 x 962 [13,52,10]
+ CRUSH rule 0 x 963 [0,83,13]
+ CRUSH rule 0 x 964 [59,44,15]
+ CRUSH rule 0 x 965 [47,102,22]
+ CRUSH rule 0 x 966 [88,69,22]
+ CRUSH rule 0 x 967 [71,17,108]
+ CRUSH rule 0 x 968 [73,9,108]
+ CRUSH rule 0 x 969 [53,21,111]
+ CRUSH rule 0 x 970 [111,85,17]
+ CRUSH rule 0 x 971 [87,19,38]
+ CRUSH rule 0 x 972 [5,33,19]
+ CRUSH rule 0 x 973 [113,81,7]
+ CRUSH rule 0 x 974 [49,86,6]
+ CRUSH rule 0 x 975 [83,96,17]
+ CRUSH rule 0 x 976 [81,100,8]
+ CRUSH rule 0 x 977 [95,76,22]
+ CRUSH rule 0 x 978 [35,4,94]
+ CRUSH rule 0 x 979 [98,13,41]
+ CRUSH rule 0 x 980 [52,93,21]
+ CRUSH rule 0 x 981 [89,46,14]
+ CRUSH rule 0 x 982 [1,95,9]
+ CRUSH rule 0 x 983 [34,37,9]
+ CRUSH rule 0 x 984 [78,23,8]
+ CRUSH rule 0 x 985 [99,24,15]
+ CRUSH rule 0 x 986 [4,33,76]
+ CRUSH rule 0 x 987 [78,22,53]
+ CRUSH rule 0 x 988 [79,84,17]
+ CRUSH rule 0 x 989 [87,6,86]
+ CRUSH rule 0 x 990 [47,46,22]
+ CRUSH rule 0 x 991 [61,18,15]
+ CRUSH rule 0 x 992 [83,111,9]
+ CRUSH rule 0 x 993 [74,27,22]
+ CRUSH rule 0 x 994 [74,105,17]
+ CRUSH rule 0 x 995 [100,45,21]
+ CRUSH rule 0 x 996 [41,22,58]
+ CRUSH rule 0 x 997 [89,32,6]
+ CRUSH rule 0 x 998 [92,65,7]
+ CRUSH rule 0 x 999 [117,13,10]
+ CRUSH rule 0 x 1000 [9,48,85]
+ CRUSH rule 0 x 1001 [49,109,11]
+ CRUSH rule 0 x 1002 [99,106,17]
+ CRUSH rule 0 x 1003 [43,22,88]
+ CRUSH rule 0 x 1004 [89,106,9]
+ CRUSH rule 0 x 1005 [105,44,14]
+ CRUSH rule 0 x 1006 [45,5,14]
+ CRUSH rule 0 x 1007 [19,67,66]
+ CRUSH rule 0 x 1008 [31,3,76]
+ CRUSH rule 0 x 1009 [19,108,65]
+ CRUSH rule 0 x 1010 [42,67,19]
+ CRUSH rule 0 x 1011 [25,113,19]
+ CRUSH rule 0 x 1012 [68,81,13]
+ CRUSH rule 0 x 1013 [5,93,21]
+ CRUSH rule 0 x 1014 [33,8,88]
+ CRUSH rule 0 x 1015 [14,99,50]
+ CRUSH rule 0 x 1016 [88,6,25]
+ CRUSH rule 0 x 1017 [0,61,22]
+ CRUSH rule 0 x 1018 [63,26,9]
+ CRUSH rule 0 x 1019 [104,61,15]
+ CRUSH rule 0 x 1020 [96,83,14]
+ CRUSH rule 0 x 1021 [117,35,6]
+ CRUSH rule 0 x 1022 [73,6,36]
+ CRUSH rule 0 x 1023 [0,83,7]
+ rule 0 (data) num_rep 10 result size == 3:\t1024/1024 (esc)
diff --git a/src/test/cli/crushtool/test-map-legacy-tunables.t b/src/test/cli/crushtool/test-map-legacy-tunables.t
new file mode 100644
index 000000000..bdfa004ac
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-legacy-tunables.t
@@ -0,0 +1,10252 @@
+ $ crushtool -i "$TESTDIR/test-map-a.crushmap" --test --show-mappings --show-statistics --rule 0 --min-rep 1 --max-rep 10
+ rule 0 (data), x = 0..1023, numrep = 1..10
+ CRUSH rule 0 x 0 [36]
+ CRUSH rule 0 x 1 [876]
+ CRUSH rule 0 x 2 [292]
+ CRUSH rule 0 x 3 [623]
+ CRUSH rule 0 x 4 [61]
+ CRUSH rule 0 x 5 [946]
+ CRUSH rule 0 x 6 [576]
+ CRUSH rule 0 x 7 [645]
+ CRUSH rule 0 x 8 [243]
+ CRUSH rule 0 x 9 [22]
+ CRUSH rule 0 x 10 [758]
+ CRUSH rule 0 x 11 [769]
+ CRUSH rule 0 x 12 [780]
+ CRUSH rule 0 x 13 [557]
+ CRUSH rule 0 x 14 [59]
+ CRUSH rule 0 x 15 [718]
+ CRUSH rule 0 x 16 [673]
+ CRUSH rule 0 x 17 [648]
+ CRUSH rule 0 x 18 [654]
+ CRUSH rule 0 x 19 [850]
+ CRUSH rule 0 x 20 [717]
+ CRUSH rule 0 x 21 [420]
+ CRUSH rule 0 x 22 [503]
+ CRUSH rule 0 x 23 [411]
+ CRUSH rule 0 x 24 [266]
+ CRUSH rule 0 x 25 [760]
+ CRUSH rule 0 x 26 [903]
+ CRUSH rule 0 x 27 [946]
+ CRUSH rule 0 x 28 [69]
+ CRUSH rule 0 x 29 [844]
+ CRUSH rule 0 x 30 [621]
+ CRUSH rule 0 x 31 [784]
+ CRUSH rule 0 x 32 [173]
+ CRUSH rule 0 x 33 [698]
+ CRUSH rule 0 x 34 [168]
+ CRUSH rule 0 x 35 [274]
+ CRUSH rule 0 x 36 [318]
+ CRUSH rule 0 x 37 [173]
+ CRUSH rule 0 x 38 [708]
+ CRUSH rule 0 x 39 [662]
+ CRUSH rule 0 x 40 [620]
+ CRUSH rule 0 x 41 [811]
+ CRUSH rule 0 x 42 [863]
+ CRUSH rule 0 x 43 [686]
+ CRUSH rule 0 x 44 [396]
+ CRUSH rule 0 x 45 [991]
+ CRUSH rule 0 x 46 [420]
+ CRUSH rule 0 x 47 [467]
+ CRUSH rule 0 x 48 [955]
+ CRUSH rule 0 x 49 [974]
+ CRUSH rule 0 x 50 [870]
+ CRUSH rule 0 x 51 [182]
+ CRUSH rule 0 x 52 [704]
+ CRUSH rule 0 x 53 [185]
+ CRUSH rule 0 x 54 [270]
+ CRUSH rule 0 x 55 [895]
+ CRUSH rule 0 x 56 [564]
+ CRUSH rule 0 x 57 [738]
+ CRUSH rule 0 x 58 [524]
+ CRUSH rule 0 x 59 [408]
+ CRUSH rule 0 x 60 [228]
+ CRUSH rule 0 x 61 [154]
+ CRUSH rule 0 x 62 [594]
+ CRUSH rule 0 x 63 [646]
+ CRUSH rule 0 x 64 [175]
+ CRUSH rule 0 x 65 [745]
+ CRUSH rule 0 x 66 [275]
+ CRUSH rule 0 x 67 [246]
+ CRUSH rule 0 x 68 [711]
+ CRUSH rule 0 x 69 [493]
+ CRUSH rule 0 x 70 [30]
+ CRUSH rule 0 x 71 [984]
+ CRUSH rule 0 x 72 [71]
+ CRUSH rule 0 x 73 [922]
+ CRUSH rule 0 x 74 [629]
+ CRUSH rule 0 x 75 [222]
+ CRUSH rule 0 x 76 [262]
+ CRUSH rule 0 x 77 [638]
+ CRUSH rule 0 x 78 [324]
+ CRUSH rule 0 x 79 [577]
+ CRUSH rule 0 x 80 [501]
+ CRUSH rule 0 x 81 [506]
+ CRUSH rule 0 x 82 [222]
+ CRUSH rule 0 x 83 [71]
+ CRUSH rule 0 x 84 [49]
+ CRUSH rule 0 x 85 [985]
+ CRUSH rule 0 x 86 [537]
+ CRUSH rule 0 x 87 [997]
+ CRUSH rule 0 x 88 [957]
+ CRUSH rule 0 x 89 [399]
+ CRUSH rule 0 x 90 [943]
+ CRUSH rule 0 x 91 [22]
+ CRUSH rule 0 x 92 [532]
+ CRUSH rule 0 x 93 [218]
+ CRUSH rule 0 x 94 [181]
+ CRUSH rule 0 x 95 [343]
+ CRUSH rule 0 x 96 [861]
+ CRUSH rule 0 x 97 [459]
+ CRUSH rule 0 x 98 [327]
+ CRUSH rule 0 x 99 [974]
+ CRUSH rule 0 x 100 [32]
+ CRUSH rule 0 x 101 [142]
+ CRUSH rule 0 x 102 [172]
+ CRUSH rule 0 x 103 [630]
+ CRUSH rule 0 x 104 [758]
+ CRUSH rule 0 x 105 [843]
+ CRUSH rule 0 x 106 [28]
+ CRUSH rule 0 x 107 [74]
+ CRUSH rule 0 x 108 [875]
+ CRUSH rule 0 x 109 [411]
+ CRUSH rule 0 x 110 [440]
+ CRUSH rule 0 x 111 [405]
+ CRUSH rule 0 x 112 [143]
+ CRUSH rule 0 x 113 [153]
+ CRUSH rule 0 x 114 [804]
+ CRUSH rule 0 x 115 [588]
+ CRUSH rule 0 x 116 [327]
+ CRUSH rule 0 x 117 [95]
+ CRUSH rule 0 x 118 [80]
+ CRUSH rule 0 x 119 [386]
+ CRUSH rule 0 x 120 [366]
+ CRUSH rule 0 x 121 [129]
+ CRUSH rule 0 x 122 [873]
+ CRUSH rule 0 x 123 [533]
+ CRUSH rule 0 x 124 [461]
+ CRUSH rule 0 x 125 [342]
+ CRUSH rule 0 x 126 [819]
+ CRUSH rule 0 x 127 [437]
+ CRUSH rule 0 x 128 [679]
+ CRUSH rule 0 x 129 [380]
+ CRUSH rule 0 x 130 [992]
+ CRUSH rule 0 x 131 [469]
+ CRUSH rule 0 x 132 [571]
+ CRUSH rule 0 x 133 [964]
+ CRUSH rule 0 x 134 [999]
+ CRUSH rule 0 x 135 [634]
+ CRUSH rule 0 x 136 [114]
+ CRUSH rule 0 x 137 [839]
+ CRUSH rule 0 x 138 [967]
+ CRUSH rule 0 x 139 [308]
+ CRUSH rule 0 x 140 [764]
+ CRUSH rule 0 x 141 [423]
+ CRUSH rule 0 x 142 [252]
+ CRUSH rule 0 x 143 [33]
+ CRUSH rule 0 x 144 [472]
+ CRUSH rule 0 x 145 [242]
+ CRUSH rule 0 x 146 [290]
+ CRUSH rule 0 x 147 [447]
+ CRUSH rule 0 x 148 [212]
+ CRUSH rule 0 x 149 [9]
+ CRUSH rule 0 x 150 [166]
+ CRUSH rule 0 x 151 [811]
+ CRUSH rule 0 x 152 [449]
+ CRUSH rule 0 x 153 [523]
+ CRUSH rule 0 x 154 [208]
+ CRUSH rule 0 x 155 [569]
+ CRUSH rule 0 x 156 [488]
+ CRUSH rule 0 x 157 [140]
+ CRUSH rule 0 x 158 [786]
+ CRUSH rule 0 x 159 [134]
+ CRUSH rule 0 x 160 [690]
+ CRUSH rule 0 x 161 [324]
+ CRUSH rule 0 x 162 [748]
+ CRUSH rule 0 x 163 [575]
+ CRUSH rule 0 x 164 [314]
+ CRUSH rule 0 x 165 [116]
+ CRUSH rule 0 x 166 [352]
+ CRUSH rule 0 x 167 [27]
+ CRUSH rule 0 x 168 [953]
+ CRUSH rule 0 x 169 [912]
+ CRUSH rule 0 x 170 [421]
+ CRUSH rule 0 x 171 [488]
+ CRUSH rule 0 x 172 [366]
+ CRUSH rule 0 x 173 [863]
+ CRUSH rule 0 x 174 [263]
+ CRUSH rule 0 x 175 [875]
+ CRUSH rule 0 x 176 [745]
+ CRUSH rule 0 x 177 [128]
+ CRUSH rule 0 x 178 [155]
+ CRUSH rule 0 x 179 [593]
+ CRUSH rule 0 x 180 [154]
+ CRUSH rule 0 x 181 [289]
+ CRUSH rule 0 x 182 [730]
+ CRUSH rule 0 x 183 [639]
+ CRUSH rule 0 x 184 [704]
+ CRUSH rule 0 x 185 [97]
+ CRUSH rule 0 x 186 [26]
+ CRUSH rule 0 x 187 [649]
+ CRUSH rule 0 x 188 [682]
+ CRUSH rule 0 x 189 [325]
+ CRUSH rule 0 x 190 [399]
+ CRUSH rule 0 x 191 [629]
+ CRUSH rule 0 x 192 [503]
+ CRUSH rule 0 x 193 [546]
+ CRUSH rule 0 x 194 [242]
+ CRUSH rule 0 x 195 [625]
+ CRUSH rule 0 x 196 [357]
+ CRUSH rule 0 x 197 [306]
+ CRUSH rule 0 x 198 [863]
+ CRUSH rule 0 x 199 [935]
+ CRUSH rule 0 x 200 [373]
+ CRUSH rule 0 x 201 [659]
+ CRUSH rule 0 x 202 [260]
+ CRUSH rule 0 x 203 [36]
+ CRUSH rule 0 x 204 [92]
+ CRUSH rule 0 x 205 [68]
+ CRUSH rule 0 x 206 [570]
+ CRUSH rule 0 x 207 [834]
+ CRUSH rule 0 x 208 [927]
+ CRUSH rule 0 x 209 [878]
+ CRUSH rule 0 x 210 [572]
+ CRUSH rule 0 x 211 [107]
+ CRUSH rule 0 x 212 [389]
+ CRUSH rule 0 x 213 [497]
+ CRUSH rule 0 x 214 [798]
+ CRUSH rule 0 x 215 [233]
+ CRUSH rule 0 x 216 [494]
+ CRUSH rule 0 x 217 [352]
+ CRUSH rule 0 x 218 [895]
+ CRUSH rule 0 x 219 [222]
+ CRUSH rule 0 x 220 [281]
+ CRUSH rule 0 x 221 [64]
+ CRUSH rule 0 x 222 [40]
+ CRUSH rule 0 x 223 [645]
+ CRUSH rule 0 x 224 [647]
+ CRUSH rule 0 x 225 [219]
+ CRUSH rule 0 x 226 [372]
+ CRUSH rule 0 x 227 [925]
+ CRUSH rule 0 x 228 [682]
+ CRUSH rule 0 x 229 [880]
+ CRUSH rule 0 x 230 [328]
+ CRUSH rule 0 x 231 [320]
+ CRUSH rule 0 x 232 [924]
+ CRUSH rule 0 x 233 [948]
+ CRUSH rule 0 x 234 [484]
+ CRUSH rule 0 x 235 [750]
+ CRUSH rule 0 x 236 [551]
+ CRUSH rule 0 x 237 [390]
+ CRUSH rule 0 x 238 [570]
+ CRUSH rule 0 x 239 [729]
+ CRUSH rule 0 x 240 [981]
+ CRUSH rule 0 x 241 [310]
+ CRUSH rule 0 x 242 [161]
+ CRUSH rule 0 x 243 [180]
+ CRUSH rule 0 x 244 [52]
+ CRUSH rule 0 x 245 [523]
+ CRUSH rule 0 x 246 [362]
+ CRUSH rule 0 x 247 [382]
+ CRUSH rule 0 x 248 [129]
+ CRUSH rule 0 x 249 [159]
+ CRUSH rule 0 x 250 [404]
+ CRUSH rule 0 x 251 [661]
+ CRUSH rule 0 x 252 [961]
+ CRUSH rule 0 x 253 [651]
+ CRUSH rule 0 x 254 [123]
+ CRUSH rule 0 x 255 [314]
+ CRUSH rule 0 x 256 [315]
+ CRUSH rule 0 x 257 [825]
+ CRUSH rule 0 x 258 [624]
+ CRUSH rule 0 x 259 [602]
+ CRUSH rule 0 x 260 [717]
+ CRUSH rule 0 x 261 [145]
+ CRUSH rule 0 x 262 [223]
+ CRUSH rule 0 x 263 [462]
+ CRUSH rule 0 x 264 [654]
+ CRUSH rule 0 x 265 [302]
+ CRUSH rule 0 x 266 [202]
+ CRUSH rule 0 x 267 [282]
+ CRUSH rule 0 x 268 [338]
+ CRUSH rule 0 x 269 [738]
+ CRUSH rule 0 x 270 [707]
+ CRUSH rule 0 x 271 [705]
+ CRUSH rule 0 x 272 [756]
+ CRUSH rule 0 x 273 [197]
+ CRUSH rule 0 x 274 [992]
+ CRUSH rule 0 x 275 [544]
+ CRUSH rule 0 x 276 [658]
+ CRUSH rule 0 x 277 [143]
+ CRUSH rule 0 x 278 [492]
+ CRUSH rule 0 x 279 [517]
+ CRUSH rule 0 x 280 [825]
+ CRUSH rule 0 x 281 [224]
+ CRUSH rule 0 x 282 [298]
+ CRUSH rule 0 x 283 [311]
+ CRUSH rule 0 x 284 [771]
+ CRUSH rule 0 x 285 [693]
+ CRUSH rule 0 x 286 [364]
+ CRUSH rule 0 x 287 [591]
+ CRUSH rule 0 x 288 [965]
+ CRUSH rule 0 x 289 [225]
+ CRUSH rule 0 x 290 [577]
+ CRUSH rule 0 x 291 [160]
+ CRUSH rule 0 x 292 [873]
+ CRUSH rule 0 x 293 [100]
+ CRUSH rule 0 x 294 [285]
+ CRUSH rule 0 x 295 [938]
+ CRUSH rule 0 x 296 [850]
+ CRUSH rule 0 x 297 [951]
+ CRUSH rule 0 x 298 [173]
+ CRUSH rule 0 x 299 [598]
+ CRUSH rule 0 x 300 [531]
+ CRUSH rule 0 x 301 [823]
+ CRUSH rule 0 x 302 [184]
+ CRUSH rule 0 x 303 [521]
+ CRUSH rule 0 x 304 [980]
+ CRUSH rule 0 x 305 [153]
+ CRUSH rule 0 x 306 [423]
+ CRUSH rule 0 x 307 [997]
+ CRUSH rule 0 x 308 [991]
+ CRUSH rule 0 x 309 [860]
+ CRUSH rule 0 x 310 [589]
+ CRUSH rule 0 x 311 [477]
+ CRUSH rule 0 x 312 [887]
+ CRUSH rule 0 x 313 [802]
+ CRUSH rule 0 x 314 [654]
+ CRUSH rule 0 x 315 [767]
+ CRUSH rule 0 x 316 [778]
+ CRUSH rule 0 x 317 [184]
+ CRUSH rule 0 x 318 [525]
+ CRUSH rule 0 x 319 [476]
+ CRUSH rule 0 x 320 [149]
+ CRUSH rule 0 x 321 [710]
+ CRUSH rule 0 x 322 [175]
+ CRUSH rule 0 x 323 [819]
+ CRUSH rule 0 x 324 [16]
+ CRUSH rule 0 x 325 [486]
+ CRUSH rule 0 x 326 [613]
+ CRUSH rule 0 x 327 [125]
+ CRUSH rule 0 x 328 [807]
+ CRUSH rule 0 x 329 [588]
+ CRUSH rule 0 x 330 [932]
+ CRUSH rule 0 x 331 [341]
+ CRUSH rule 0 x 332 [153]
+ CRUSH rule 0 x 333 [745]
+ CRUSH rule 0 x 334 [614]
+ CRUSH rule 0 x 335 [518]
+ CRUSH rule 0 x 336 [389]
+ CRUSH rule 0 x 337 [753]
+ CRUSH rule 0 x 338 [128]
+ CRUSH rule 0 x 339 [430]
+ CRUSH rule 0 x 340 [541]
+ CRUSH rule 0 x 341 [402]
+ CRUSH rule 0 x 342 [982]
+ CRUSH rule 0 x 343 [833]
+ CRUSH rule 0 x 344 [784]
+ CRUSH rule 0 x 345 [546]
+ CRUSH rule 0 x 346 [302]
+ CRUSH rule 0 x 347 [488]
+ CRUSH rule 0 x 348 [903]
+ CRUSH rule 0 x 349 [471]
+ CRUSH rule 0 x 350 [348]
+ CRUSH rule 0 x 351 [961]
+ CRUSH rule 0 x 352 [728]
+ CRUSH rule 0 x 353 [904]
+ CRUSH rule 0 x 354 [345]
+ CRUSH rule 0 x 355 [50]
+ CRUSH rule 0 x 356 [87]
+ CRUSH rule 0 x 357 [762]
+ CRUSH rule 0 x 358 [908]
+ CRUSH rule 0 x 359 [484]
+ CRUSH rule 0 x 360 [173]
+ CRUSH rule 0 x 361 [404]
+ CRUSH rule 0 x 362 [403]
+ CRUSH rule 0 x 363 [639]
+ CRUSH rule 0 x 364 [752]
+ CRUSH rule 0 x 365 [956]
+ CRUSH rule 0 x 366 [860]
+ CRUSH rule 0 x 367 [205]
+ CRUSH rule 0 x 368 [301]
+ CRUSH rule 0 x 369 [452]
+ CRUSH rule 0 x 370 [11]
+ CRUSH rule 0 x 371 [124]
+ CRUSH rule 0 x 372 [253]
+ CRUSH rule 0 x 373 [715]
+ CRUSH rule 0 x 374 [191]
+ CRUSH rule 0 x 375 [711]
+ CRUSH rule 0 x 376 [597]
+ CRUSH rule 0 x 377 [294]
+ CRUSH rule 0 x 378 [34]
+ CRUSH rule 0 x 379 [869]
+ CRUSH rule 0 x 380 [294]
+ CRUSH rule 0 x 381 [119]
+ CRUSH rule 0 x 382 [69]
+ CRUSH rule 0 x 383 [922]
+ CRUSH rule 0 x 384 [221]
+ CRUSH rule 0 x 385 [561]
+ CRUSH rule 0 x 386 [335]
+ CRUSH rule 0 x 387 [514]
+ CRUSH rule 0 x 388 [587]
+ CRUSH rule 0 x 389 [109]
+ CRUSH rule 0 x 390 [925]
+ CRUSH rule 0 x 391 [267]
+ CRUSH rule 0 x 392 [382]
+ CRUSH rule 0 x 393 [425]
+ CRUSH rule 0 x 394 [898]
+ CRUSH rule 0 x 395 [806]
+ CRUSH rule 0 x 396 [790]
+ CRUSH rule 0 x 397 [136]
+ CRUSH rule 0 x 398 [914]
+ CRUSH rule 0 x 399 [261]
+ CRUSH rule 0 x 400 [661]
+ CRUSH rule 0 x 401 [953]
+ CRUSH rule 0 x 402 [738]
+ CRUSH rule 0 x 403 [573]
+ CRUSH rule 0 x 404 [526]
+ CRUSH rule 0 x 405 [582]
+ CRUSH rule 0 x 406 [768]
+ CRUSH rule 0 x 407 [260]
+ CRUSH rule 0 x 408 [657]
+ CRUSH rule 0 x 409 [498]
+ CRUSH rule 0 x 410 [28]
+ CRUSH rule 0 x 411 [684]
+ CRUSH rule 0 x 412 [261]
+ CRUSH rule 0 x 413 [891]
+ CRUSH rule 0 x 414 [127]
+ CRUSH rule 0 x 415 [272]
+ CRUSH rule 0 x 416 [739]
+ CRUSH rule 0 x 417 [106]
+ CRUSH rule 0 x 418 [525]
+ CRUSH rule 0 x 419 [603]
+ CRUSH rule 0 x 420 [988]
+ CRUSH rule 0 x 421 [761]
+ CRUSH rule 0 x 422 [317]
+ CRUSH rule 0 x 423 [137]
+ CRUSH rule 0 x 424 [920]
+ CRUSH rule 0 x 425 [277]
+ CRUSH rule 0 x 426 [485]
+ CRUSH rule 0 x 427 [242]
+ CRUSH rule 0 x 428 [632]
+ CRUSH rule 0 x 429 [641]
+ CRUSH rule 0 x 430 [626]
+ CRUSH rule 0 x 431 [697]
+ CRUSH rule 0 x 432 [590]
+ CRUSH rule 0 x 433 [284]
+ CRUSH rule 0 x 434 [538]
+ CRUSH rule 0 x 435 [30]
+ CRUSH rule 0 x 436 [164]
+ CRUSH rule 0 x 437 [322]
+ CRUSH rule 0 x 438 [142]
+ CRUSH rule 0 x 439 [119]
+ CRUSH rule 0 x 440 [333]
+ CRUSH rule 0 x 441 [477]
+ CRUSH rule 0 x 442 [274]
+ CRUSH rule 0 x 443 [983]
+ CRUSH rule 0 x 444 [536]
+ CRUSH rule 0 x 445 [485]
+ CRUSH rule 0 x 446 [345]
+ CRUSH rule 0 x 447 [61]
+ CRUSH rule 0 x 448 [333]
+ CRUSH rule 0 x 449 [680]
+ CRUSH rule 0 x 450 [235]
+ CRUSH rule 0 x 451 [961]
+ CRUSH rule 0 x 452 [525]
+ CRUSH rule 0 x 453 [138]
+ CRUSH rule 0 x 454 [137]
+ CRUSH rule 0 x 455 [173]
+ CRUSH rule 0 x 456 [235]
+ CRUSH rule 0 x 457 [450]
+ CRUSH rule 0 x 458 [195]
+ CRUSH rule 0 x 459 [381]
+ CRUSH rule 0 x 460 [972]
+ CRUSH rule 0 x 461 [506]
+ CRUSH rule 0 x 462 [692]
+ CRUSH rule 0 x 463 [788]
+ CRUSH rule 0 x 464 [133]
+ CRUSH rule 0 x 465 [971]
+ CRUSH rule 0 x 466 [394]
+ CRUSH rule 0 x 467 [517]
+ CRUSH rule 0 x 468 [829]
+ CRUSH rule 0 x 469 [987]
+ CRUSH rule 0 x 470 [107]
+ CRUSH rule 0 x 471 [181]
+ CRUSH rule 0 x 472 [547]
+ CRUSH rule 0 x 473 [760]
+ CRUSH rule 0 x 474 [787]
+ CRUSH rule 0 x 475 [662]
+ CRUSH rule 0 x 476 [110]
+ CRUSH rule 0 x 477 [393]
+ CRUSH rule 0 x 478 [246]
+ CRUSH rule 0 x 479 [70]
+ CRUSH rule 0 x 480 [753]
+ CRUSH rule 0 x 481 [470]
+ CRUSH rule 0 x 482 [451]
+ CRUSH rule 0 x 483 [816]
+ CRUSH rule 0 x 484 [540]
+ CRUSH rule 0 x 485 [74]
+ CRUSH rule 0 x 486 [958]
+ CRUSH rule 0 x 487 [228]
+ CRUSH rule 0 x 488 [180]
+ CRUSH rule 0 x 489 [47]
+ CRUSH rule 0 x 490 [905]
+ CRUSH rule 0 x 491 [892]
+ CRUSH rule 0 x 492 [588]
+ CRUSH rule 0 x 493 [353]
+ CRUSH rule 0 x 494 [378]
+ CRUSH rule 0 x 495 [845]
+ CRUSH rule 0 x 496 [13]
+ CRUSH rule 0 x 497 [796]
+ CRUSH rule 0 x 498 [412]
+ CRUSH rule 0 x 499 [330]
+ CRUSH rule 0 x 500 [820]
+ CRUSH rule 0 x 501 [110]
+ CRUSH rule 0 x 502 [336]
+ CRUSH rule 0 x 503 [922]
+ CRUSH rule 0 x 504 [483]
+ CRUSH rule 0 x 505 [482]
+ CRUSH rule 0 x 506 [493]
+ CRUSH rule 0 x 507 [12]
+ CRUSH rule 0 x 508 [227]
+ CRUSH rule 0 x 509 [807]
+ CRUSH rule 0 x 510 [134]
+ CRUSH rule 0 x 511 [212]
+ CRUSH rule 0 x 512 [236]
+ CRUSH rule 0 x 513 [994]
+ CRUSH rule 0 x 514 [45]
+ CRUSH rule 0 x 515 [504]
+ CRUSH rule 0 x 516 [285]
+ CRUSH rule 0 x 517 [300]
+ CRUSH rule 0 x 518 [397]
+ CRUSH rule 0 x 519 [86]
+ CRUSH rule 0 x 520 [900]
+ CRUSH rule 0 x 521 [31]
+ CRUSH rule 0 x 522 [390]
+ CRUSH rule 0 x 523 [618]
+ CRUSH rule 0 x 524 [635]
+ CRUSH rule 0 x 525 [311]
+ CRUSH rule 0 x 526 [48]
+ CRUSH rule 0 x 527 [202]
+ CRUSH rule 0 x 528 [565]
+ CRUSH rule 0 x 529 [934]
+ CRUSH rule 0 x 530 [502]
+ CRUSH rule 0 x 531 [681]
+ CRUSH rule 0 x 532 [422]
+ CRUSH rule 0 x 533 [863]
+ CRUSH rule 0 x 534 [962]
+ CRUSH rule 0 x 535 [89]
+ CRUSH rule 0 x 536 [499]
+ CRUSH rule 0 x 537 [676]
+ CRUSH rule 0 x 538 [58]
+ CRUSH rule 0 x 539 [837]
+ CRUSH rule 0 x 540 [831]
+ CRUSH rule 0 x 541 [582]
+ CRUSH rule 0 x 542 [472]
+ CRUSH rule 0 x 543 [382]
+ CRUSH rule 0 x 544 [947]
+ CRUSH rule 0 x 545 [425]
+ CRUSH rule 0 x 546 [18]
+ CRUSH rule 0 x 547 [445]
+ CRUSH rule 0 x 548 [367]
+ CRUSH rule 0 x 549 [125]
+ CRUSH rule 0 x 550 [425]
+ CRUSH rule 0 x 551 [44]
+ CRUSH rule 0 x 552 [246]
+ CRUSH rule 0 x 553 [71]
+ CRUSH rule 0 x 554 [207]
+ CRUSH rule 0 x 555 [570]
+ CRUSH rule 0 x 556 [674]
+ CRUSH rule 0 x 557 [347]
+ CRUSH rule 0 x 558 [627]
+ CRUSH rule 0 x 559 [940]
+ CRUSH rule 0 x 560 [295]
+ CRUSH rule 0 x 561 [506]
+ CRUSH rule 0 x 562 [718]
+ CRUSH rule 0 x 563 [552]
+ CRUSH rule 0 x 564 [835]
+ CRUSH rule 0 x 565 [8]
+ CRUSH rule 0 x 566 [600]
+ CRUSH rule 0 x 567 [999]
+ CRUSH rule 0 x 568 [252]
+ CRUSH rule 0 x 569 [643]
+ CRUSH rule 0 x 570 [617]
+ CRUSH rule 0 x 571 [757]
+ CRUSH rule 0 x 572 [299]
+ CRUSH rule 0 x 573 [25]
+ CRUSH rule 0 x 574 [215]
+ CRUSH rule 0 x 575 [225]
+ CRUSH rule 0 x 576 [627]
+ CRUSH rule 0 x 577 [237]
+ CRUSH rule 0 x 578 [885]
+ CRUSH rule 0 x 579 [924]
+ CRUSH rule 0 x 580 [718]
+ CRUSH rule 0 x 581 [219]
+ CRUSH rule 0 x 582 [893]
+ CRUSH rule 0 x 583 [246]
+ CRUSH rule 0 x 584 [336]
+ CRUSH rule 0 x 585 [324]
+ CRUSH rule 0 x 586 [558]
+ CRUSH rule 0 x 587 [985]
+ CRUSH rule 0 x 588 [211]
+ CRUSH rule 0 x 589 [129]
+ CRUSH rule 0 x 590 [467]
+ CRUSH rule 0 x 591 [758]
+ CRUSH rule 0 x 592 [525]
+ CRUSH rule 0 x 593 [601]
+ CRUSH rule 0 x 594 [227]
+ CRUSH rule 0 x 595 [720]
+ CRUSH rule 0 x 596 [751]
+ CRUSH rule 0 x 597 [129]
+ CRUSH rule 0 x 598 [679]
+ CRUSH rule 0 x 599 [668]
+ CRUSH rule 0 x 600 [143]
+ CRUSH rule 0 x 601 [326]
+ CRUSH rule 0 x 602 [860]
+ CRUSH rule 0 x 603 [709]
+ CRUSH rule 0 x 604 [571]
+ CRUSH rule 0 x 605 [252]
+ CRUSH rule 0 x 606 [339]
+ CRUSH rule 0 x 607 [590]
+ CRUSH rule 0 x 608 [145]
+ CRUSH rule 0 x 609 [973]
+ CRUSH rule 0 x 610 [435]
+ CRUSH rule 0 x 611 [559]
+ CRUSH rule 0 x 612 [273]
+ CRUSH rule 0 x 613 [828]
+ CRUSH rule 0 x 614 [478]
+ CRUSH rule 0 x 615 [392]
+ CRUSH rule 0 x 616 [778]
+ CRUSH rule 0 x 617 [622]
+ CRUSH rule 0 x 618 [149]
+ CRUSH rule 0 x 619 [604]
+ CRUSH rule 0 x 620 [181]
+ CRUSH rule 0 x 621 [735]
+ CRUSH rule 0 x 622 [661]
+ CRUSH rule 0 x 623 [142]
+ CRUSH rule 0 x 624 [360]
+ CRUSH rule 0 x 625 [541]
+ CRUSH rule 0 x 626 [364]
+ CRUSH rule 0 x 627 [458]
+ CRUSH rule 0 x 628 [250]
+ CRUSH rule 0 x 629 [928]
+ CRUSH rule 0 x 630 [243]
+ CRUSH rule 0 x 631 [438]
+ CRUSH rule 0 x 632 [797]
+ CRUSH rule 0 x 633 [993]
+ CRUSH rule 0 x 634 [239]
+ CRUSH rule 0 x 635 [640]
+ CRUSH rule 0 x 636 [173]
+ CRUSH rule 0 x 637 [0]
+ CRUSH rule 0 x 638 [702]
+ CRUSH rule 0 x 639 [475]
+ CRUSH rule 0 x 640 [31]
+ CRUSH rule 0 x 641 [296]
+ CRUSH rule 0 x 642 [894]
+ CRUSH rule 0 x 643 [117]
+ CRUSH rule 0 x 644 [438]
+ CRUSH rule 0 x 645 [982]
+ CRUSH rule 0 x 646 [334]
+ CRUSH rule 0 x 647 [933]
+ CRUSH rule 0 x 648 [22]
+ CRUSH rule 0 x 649 [503]
+ CRUSH rule 0 x 650 [328]
+ CRUSH rule 0 x 651 [3]
+ CRUSH rule 0 x 652 [495]
+ CRUSH rule 0 x 653 [185]
+ CRUSH rule 0 x 654 [130]
+ CRUSH rule 0 x 655 [560]
+ CRUSH rule 0 x 656 [219]
+ CRUSH rule 0 x 657 [233]
+ CRUSH rule 0 x 658 [778]
+ CRUSH rule 0 x 659 [240]
+ CRUSH rule 0 x 660 [244]
+ CRUSH rule 0 x 661 [184]
+ CRUSH rule 0 x 662 [65]
+ CRUSH rule 0 x 663 [323]
+ CRUSH rule 0 x 664 [865]
+ CRUSH rule 0 x 665 [420]
+ CRUSH rule 0 x 666 [319]
+ CRUSH rule 0 x 667 [875]
+ CRUSH rule 0 x 668 [331]
+ CRUSH rule 0 x 669 [915]
+ CRUSH rule 0 x 670 [845]
+ CRUSH rule 0 x 671 [108]
+ CRUSH rule 0 x 672 [578]
+ CRUSH rule 0 x 673 [442]
+ CRUSH rule 0 x 674 [588]
+ CRUSH rule 0 x 675 [489]
+ CRUSH rule 0 x 676 [928]
+ CRUSH rule 0 x 677 [399]
+ CRUSH rule 0 x 678 [546]
+ CRUSH rule 0 x 679 [988]
+ CRUSH rule 0 x 680 [335]
+ CRUSH rule 0 x 681 [690]
+ CRUSH rule 0 x 682 [196]
+ CRUSH rule 0 x 683 [627]
+ CRUSH rule 0 x 684 [38]
+ CRUSH rule 0 x 685 [841]
+ CRUSH rule 0 x 686 [336]
+ CRUSH rule 0 x 687 [20]
+ CRUSH rule 0 x 688 [463]
+ CRUSH rule 0 x 689 [569]
+ CRUSH rule 0 x 690 [551]
+ CRUSH rule 0 x 691 [766]
+ CRUSH rule 0 x 692 [739]
+ CRUSH rule 0 x 693 [339]
+ CRUSH rule 0 x 694 [405]
+ CRUSH rule 0 x 695 [622]
+ CRUSH rule 0 x 696 [558]
+ CRUSH rule 0 x 697 [818]
+ CRUSH rule 0 x 698 [178]
+ CRUSH rule 0 x 699 [450]
+ CRUSH rule 0 x 700 [502]
+ CRUSH rule 0 x 701 [4]
+ CRUSH rule 0 x 702 [177]
+ CRUSH rule 0 x 703 [354]
+ CRUSH rule 0 x 704 [646]
+ CRUSH rule 0 x 705 [921]
+ CRUSH rule 0 x 706 [652]
+ CRUSH rule 0 x 707 [345]
+ CRUSH rule 0 x 708 [333]
+ CRUSH rule 0 x 709 [45]
+ CRUSH rule 0 x 710 [94]
+ CRUSH rule 0 x 711 [227]
+ CRUSH rule 0 x 712 [398]
+ CRUSH rule 0 x 713 [116]
+ CRUSH rule 0 x 714 [111]
+ CRUSH rule 0 x 715 [531]
+ CRUSH rule 0 x 716 [169]
+ CRUSH rule 0 x 717 [417]
+ CRUSH rule 0 x 718 [992]
+ CRUSH rule 0 x 719 [936]
+ CRUSH rule 0 x 720 [370]
+ CRUSH rule 0 x 721 [320]
+ CRUSH rule 0 x 722 [7]
+ CRUSH rule 0 x 723 [270]
+ CRUSH rule 0 x 724 [666]
+ CRUSH rule 0 x 725 [794]
+ CRUSH rule 0 x 726 [420]
+ CRUSH rule 0 x 727 [561]
+ CRUSH rule 0 x 728 [951]
+ CRUSH rule 0 x 729 [656]
+ CRUSH rule 0 x 730 [3]
+ CRUSH rule 0 x 731 [852]
+ CRUSH rule 0 x 732 [983]
+ CRUSH rule 0 x 733 [285]
+ CRUSH rule 0 x 734 [125]
+ CRUSH rule 0 x 735 [417]
+ CRUSH rule 0 x 736 [749]
+ CRUSH rule 0 x 737 [644]
+ CRUSH rule 0 x 738 [449]
+ CRUSH rule 0 x 739 [341]
+ CRUSH rule 0 x 740 [874]
+ CRUSH rule 0 x 741 [189]
+ CRUSH rule 0 x 742 [912]
+ CRUSH rule 0 x 743 [654]
+ CRUSH rule 0 x 744 [725]
+ CRUSH rule 0 x 745 [787]
+ CRUSH rule 0 x 746 [757]
+ CRUSH rule 0 x 747 [700]
+ CRUSH rule 0 x 748 [557]
+ CRUSH rule 0 x 749 [772]
+ CRUSH rule 0 x 750 [946]
+ CRUSH rule 0 x 751 [996]
+ CRUSH rule 0 x 752 [746]
+ CRUSH rule 0 x 753 [741]
+ CRUSH rule 0 x 754 [648]
+ CRUSH rule 0 x 755 [157]
+ CRUSH rule 0 x 756 [416]
+ CRUSH rule 0 x 757 [599]
+ CRUSH rule 0 x 758 [994]
+ CRUSH rule 0 x 759 [959]
+ CRUSH rule 0 x 760 [518]
+ CRUSH rule 0 x 761 [285]
+ CRUSH rule 0 x 762 [591]
+ CRUSH rule 0 x 763 [908]
+ CRUSH rule 0 x 764 [787]
+ CRUSH rule 0 x 765 [327]
+ CRUSH rule 0 x 766 [84]
+ CRUSH rule 0 x 767 [370]
+ CRUSH rule 0 x 768 [826]
+ CRUSH rule 0 x 769 [67]
+ CRUSH rule 0 x 770 [593]
+ CRUSH rule 0 x 771 [309]
+ CRUSH rule 0 x 772 [12]
+ CRUSH rule 0 x 773 [253]
+ CRUSH rule 0 x 774 [164]
+ CRUSH rule 0 x 775 [703]
+ CRUSH rule 0 x 776 [728]
+ CRUSH rule 0 x 777 [981]
+ CRUSH rule 0 x 778 [411]
+ CRUSH rule 0 x 779 [346]
+ CRUSH rule 0 x 780 [476]
+ CRUSH rule 0 x 781 [10]
+ CRUSH rule 0 x 782 [462]
+ CRUSH rule 0 x 783 [580]
+ CRUSH rule 0 x 784 [413]
+ CRUSH rule 0 x 785 [341]
+ CRUSH rule 0 x 786 [411]
+ CRUSH rule 0 x 787 [605]
+ CRUSH rule 0 x 788 [226]
+ CRUSH rule 0 x 789 [545]
+ CRUSH rule 0 x 790 [414]
+ CRUSH rule 0 x 791 [660]
+ CRUSH rule 0 x 792 [287]
+ CRUSH rule 0 x 793 [631]
+ CRUSH rule 0 x 794 [931]
+ CRUSH rule 0 x 795 [551]
+ CRUSH rule 0 x 796 [814]
+ CRUSH rule 0 x 797 [64]
+ CRUSH rule 0 x 798 [422]
+ CRUSH rule 0 x 799 [824]
+ CRUSH rule 0 x 800 [862]
+ CRUSH rule 0 x 801 [145]
+ CRUSH rule 0 x 802 [570]
+ CRUSH rule 0 x 803 [151]
+ CRUSH rule 0 x 804 [467]
+ CRUSH rule 0 x 805 [621]
+ CRUSH rule 0 x 806 [898]
+ CRUSH rule 0 x 807 [354]
+ CRUSH rule 0 x 808 [7]
+ CRUSH rule 0 x 809 [70]
+ CRUSH rule 0 x 810 [701]
+ CRUSH rule 0 x 811 [248]
+ CRUSH rule 0 x 812 [230]
+ CRUSH rule 0 x 813 [805]
+ CRUSH rule 0 x 814 [54]
+ CRUSH rule 0 x 815 [679]
+ CRUSH rule 0 x 816 [919]
+ CRUSH rule 0 x 817 [765]
+ CRUSH rule 0 x 818 [415]
+ CRUSH rule 0 x 819 [721]
+ CRUSH rule 0 x 820 [218]
+ CRUSH rule 0 x 821 [185]
+ CRUSH rule 0 x 822 [356]
+ CRUSH rule 0 x 823 [220]
+ CRUSH rule 0 x 824 [292]
+ CRUSH rule 0 x 825 [949]
+ CRUSH rule 0 x 826 [767]
+ CRUSH rule 0 x 827 [631]
+ CRUSH rule 0 x 828 [288]
+ CRUSH rule 0 x 829 [990]
+ CRUSH rule 0 x 830 [152]
+ CRUSH rule 0 x 831 [814]
+ CRUSH rule 0 x 832 [235]
+ CRUSH rule 0 x 833 [657]
+ CRUSH rule 0 x 834 [907]
+ CRUSH rule 0 x 835 [784]
+ CRUSH rule 0 x 836 [951]
+ CRUSH rule 0 x 837 [556]
+ CRUSH rule 0 x 838 [329]
+ CRUSH rule 0 x 839 [568]
+ CRUSH rule 0 x 840 [45]
+ CRUSH rule 0 x 841 [652]
+ CRUSH rule 0 x 842 [629]
+ CRUSH rule 0 x 843 [799]
+ CRUSH rule 0 x 844 [694]
+ CRUSH rule 0 x 845 [332]
+ CRUSH rule 0 x 846 [452]
+ CRUSH rule 0 x 847 [399]
+ CRUSH rule 0 x 848 [303]
+ CRUSH rule 0 x 849 [666]
+ CRUSH rule 0 x 850 [644]
+ CRUSH rule 0 x 851 [527]
+ CRUSH rule 0 x 852 [31]
+ CRUSH rule 0 x 853 [483]
+ CRUSH rule 0 x 854 [697]
+ CRUSH rule 0 x 855 [837]
+ CRUSH rule 0 x 856 [712]
+ CRUSH rule 0 x 857 [77]
+ CRUSH rule 0 x 858 [412]
+ CRUSH rule 0 x 859 [173]
+ CRUSH rule 0 x 860 [776]
+ CRUSH rule 0 x 861 [705]
+ CRUSH rule 0 x 862 [809]
+ CRUSH rule 0 x 863 [349]
+ CRUSH rule 0 x 864 [717]
+ CRUSH rule 0 x 865 [857]
+ CRUSH rule 0 x 866 [394]
+ CRUSH rule 0 x 867 [640]
+ CRUSH rule 0 x 868 [613]
+ CRUSH rule 0 x 869 [973]
+ CRUSH rule 0 x 870 [505]
+ CRUSH rule 0 x 871 [239]
+ CRUSH rule 0 x 872 [21]
+ CRUSH rule 0 x 873 [954]
+ CRUSH rule 0 x 874 [54]
+ CRUSH rule 0 x 875 [809]
+ CRUSH rule 0 x 876 [483]
+ CRUSH rule 0 x 877 [542]
+ CRUSH rule 0 x 878 [217]
+ CRUSH rule 0 x 879 [999]
+ CRUSH rule 0 x 880 [678]
+ CRUSH rule 0 x 881 [394]
+ CRUSH rule 0 x 882 [467]
+ CRUSH rule 0 x 883 [802]
+ CRUSH rule 0 x 884 [653]
+ CRUSH rule 0 x 885 [898]
+ CRUSH rule 0 x 886 [434]
+ CRUSH rule 0 x 887 [297]
+ CRUSH rule 0 x 888 [863]
+ CRUSH rule 0 x 889 [105]
+ CRUSH rule 0 x 890 [550]
+ CRUSH rule 0 x 891 [575]
+ CRUSH rule 0 x 892 [259]
+ CRUSH rule 0 x 893 [902]
+ CRUSH rule 0 x 894 [180]
+ CRUSH rule 0 x 895 [725]
+ CRUSH rule 0 x 896 [951]
+ CRUSH rule 0 x 897 [810]
+ CRUSH rule 0 x 898 [979]
+ CRUSH rule 0 x 899 [685]
+ CRUSH rule 0 x 900 [530]
+ CRUSH rule 0 x 901 [740]
+ CRUSH rule 0 x 902 [800]
+ CRUSH rule 0 x 903 [230]
+ CRUSH rule 0 x 904 [346]
+ CRUSH rule 0 x 905 [530]
+ CRUSH rule 0 x 906 [80]
+ CRUSH rule 0 x 907 [365]
+ CRUSH rule 0 x 908 [204]
+ CRUSH rule 0 x 909 [883]
+ CRUSH rule 0 x 910 [549]
+ CRUSH rule 0 x 911 [325]
+ CRUSH rule 0 x 912 [874]
+ CRUSH rule 0 x 913 [331]
+ CRUSH rule 0 x 914 [836]
+ CRUSH rule 0 x 915 [245]
+ CRUSH rule 0 x 916 [77]
+ CRUSH rule 0 x 917 [239]
+ CRUSH rule 0 x 918 [988]
+ CRUSH rule 0 x 919 [783]
+ CRUSH rule 0 x 920 [623]
+ CRUSH rule 0 x 921 [105]
+ CRUSH rule 0 x 922 [887]
+ CRUSH rule 0 x 923 [223]
+ CRUSH rule 0 x 924 [25]
+ CRUSH rule 0 x 925 [912]
+ CRUSH rule 0 x 926 [968]
+ CRUSH rule 0 x 927 [277]
+ CRUSH rule 0 x 928 [554]
+ CRUSH rule 0 x 929 [761]
+ CRUSH rule 0 x 930 [814]
+ CRUSH rule 0 x 931 [29]
+ CRUSH rule 0 x 932 [446]
+ CRUSH rule 0 x 933 [352]
+ CRUSH rule 0 x 934 [730]
+ CRUSH rule 0 x 935 [731]
+ CRUSH rule 0 x 936 [322]
+ CRUSH rule 0 x 937 [822]
+ CRUSH rule 0 x 938 [557]
+ CRUSH rule 0 x 939 [150]
+ CRUSH rule 0 x 940 [638]
+ CRUSH rule 0 x 941 [730]
+ CRUSH rule 0 x 942 [62]
+ CRUSH rule 0 x 943 [165]
+ CRUSH rule 0 x 944 [199]
+ CRUSH rule 0 x 945 [946]
+ CRUSH rule 0 x 946 [595]
+ CRUSH rule 0 x 947 [800]
+ CRUSH rule 0 x 948 [132]
+ CRUSH rule 0 x 949 [792]
+ CRUSH rule 0 x 950 [111]
+ CRUSH rule 0 x 951 [414]
+ CRUSH rule 0 x 952 [775]
+ CRUSH rule 0 x 953 [349]
+ CRUSH rule 0 x 954 [570]
+ CRUSH rule 0 x 955 [729]
+ CRUSH rule 0 x 956 [519]
+ CRUSH rule 0 x 957 [242]
+ CRUSH rule 0 x 958 [84]
+ CRUSH rule 0 x 959 [270]
+ CRUSH rule 0 x 960 [458]
+ CRUSH rule 0 x 961 [981]
+ CRUSH rule 0 x 962 [623]
+ CRUSH rule 0 x 963 [291]
+ CRUSH rule 0 x 964 [28]
+ CRUSH rule 0 x 965 [675]
+ CRUSH rule 0 x 966 [836]
+ CRUSH rule 0 x 967 [966]
+ CRUSH rule 0 x 968 [864]
+ CRUSH rule 0 x 969 [729]
+ CRUSH rule 0 x 970 [800]
+ CRUSH rule 0 x 971 [737]
+ CRUSH rule 0 x 972 [952]
+ CRUSH rule 0 x 973 [356]
+ CRUSH rule 0 x 974 [545]
+ CRUSH rule 0 x 975 [336]
+ CRUSH rule 0 x 976 [446]
+ CRUSH rule 0 x 977 [202]
+ CRUSH rule 0 x 978 [612]
+ CRUSH rule 0 x 979 [843]
+ CRUSH rule 0 x 980 [60]
+ CRUSH rule 0 x 981 [702]
+ CRUSH rule 0 x 982 [298]
+ CRUSH rule 0 x 983 [723]
+ CRUSH rule 0 x 984 [723]
+ CRUSH rule 0 x 985 [945]
+ CRUSH rule 0 x 986 [772]
+ CRUSH rule 0 x 987 [88]
+ CRUSH rule 0 x 988 [522]
+ CRUSH rule 0 x 989 [578]
+ CRUSH rule 0 x 990 [638]
+ CRUSH rule 0 x 991 [530]
+ CRUSH rule 0 x 992 [925]
+ CRUSH rule 0 x 993 [991]
+ CRUSH rule 0 x 994 [276]
+ CRUSH rule 0 x 995 [288]
+ CRUSH rule 0 x 996 [887]
+ CRUSH rule 0 x 997 [110]
+ CRUSH rule 0 x 998 [435]
+ CRUSH rule 0 x 999 [876]
+ CRUSH rule 0 x 1000 [178]
+ CRUSH rule 0 x 1001 [99]
+ CRUSH rule 0 x 1002 [515]
+ CRUSH rule 0 x 1003 [104]
+ CRUSH rule 0 x 1004 [269]
+ CRUSH rule 0 x 1005 [369]
+ CRUSH rule 0 x 1006 [40]
+ CRUSH rule 0 x 1007 [978]
+ CRUSH rule 0 x 1008 [965]
+ CRUSH rule 0 x 1009 [598]
+ CRUSH rule 0 x 1010 [767]
+ CRUSH rule 0 x 1011 [289]
+ CRUSH rule 0 x 1012 [128]
+ CRUSH rule 0 x 1013 [979]
+ CRUSH rule 0 x 1014 [979]
+ CRUSH rule 0 x 1015 [277]
+ CRUSH rule 0 x 1016 [262]
+ CRUSH rule 0 x 1017 [150]
+ CRUSH rule 0 x 1018 [555]
+ CRUSH rule 0 x 1019 [513]
+ CRUSH rule 0 x 1020 [158]
+ CRUSH rule 0 x 1021 [915]
+ CRUSH rule 0 x 1022 [967]
+ CRUSH rule 0 x 1023 [488]
+ rule 0 (data) num_rep 1 result size == 1:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705]
+ CRUSH rule 0 x 1 [876,250]
+ CRUSH rule 0 x 2 [292,832]
+ CRUSH rule 0 x 3 [623,387]
+ CRUSH rule 0 x 4 [61,334]
+ CRUSH rule 0 x 5 [946,557]
+ CRUSH rule 0 x 6 [576,668]
+ CRUSH rule 0 x 7 [645,753]
+ CRUSH rule 0 x 8 [243,6]
+ CRUSH rule 0 x 9 [22,578]
+ CRUSH rule 0 x 10 [758,828]
+ CRUSH rule 0 x 11 [769,120]
+ CRUSH rule 0 x 12 [780,364]
+ CRUSH rule 0 x 13 [557,18]
+ CRUSH rule 0 x 14 [59,561]
+ CRUSH rule 0 x 15 [718,928]
+ CRUSH rule 0 x 16 [673,632]
+ CRUSH rule 0 x 17 [648,43]
+ CRUSH rule 0 x 18 [654,219]
+ CRUSH rule 0 x 19 [850,545]
+ CRUSH rule 0 x 20 [717,785]
+ CRUSH rule 0 x 21 [420,57]
+ CRUSH rule 0 x 22 [503,998]
+ CRUSH rule 0 x 23 [411,663]
+ CRUSH rule 0 x 24 [266,861]
+ CRUSH rule 0 x 25 [760,483]
+ CRUSH rule 0 x 26 [903,24]
+ CRUSH rule 0 x 27 [946,188]
+ CRUSH rule 0 x 28 [69,312]
+ CRUSH rule 0 x 29 [844,883]
+ CRUSH rule 0 x 30 [621,18]
+ CRUSH rule 0 x 31 [784,943]
+ CRUSH rule 0 x 32 [173,374]
+ CRUSH rule 0 x 33 [698,336]
+ CRUSH rule 0 x 34 [168,836]
+ CRUSH rule 0 x 35 [274,509]
+ CRUSH rule 0 x 36 [318,215]
+ CRUSH rule 0 x 37 [173,604]
+ CRUSH rule 0 x 38 [708,444]
+ CRUSH rule 0 x 39 [662,198]
+ CRUSH rule 0 x 40 [620,801]
+ CRUSH rule 0 x 41 [811,264]
+ CRUSH rule 0 x 42 [863,179]
+ CRUSH rule 0 x 43 [686,822]
+ CRUSH rule 0 x 44 [396,222]
+ CRUSH rule 0 x 45 [991,694]
+ CRUSH rule 0 x 46 [420,909]
+ CRUSH rule 0 x 47 [467,211]
+ CRUSH rule 0 x 48 [955,329]
+ CRUSH rule 0 x 49 [974,891]
+ CRUSH rule 0 x 50 [870,441]
+ CRUSH rule 0 x 51 [182,930]
+ CRUSH rule 0 x 52 [704,812]
+ CRUSH rule 0 x 53 [185,713]
+ CRUSH rule 0 x 54 [270,441]
+ CRUSH rule 0 x 55 [895,734]
+ CRUSH rule 0 x 56 [564,963]
+ CRUSH rule 0 x 57 [738,130]
+ CRUSH rule 0 x 58 [524,113]
+ CRUSH rule 0 x 59 [408,337]
+ CRUSH rule 0 x 60 [228,790]
+ CRUSH rule 0 x 61 [154,843]
+ CRUSH rule 0 x 62 [594,811]
+ CRUSH rule 0 x 63 [646,67]
+ CRUSH rule 0 x 64 [175,542]
+ CRUSH rule 0 x 65 [745,619]
+ CRUSH rule 0 x 66 [275,468]
+ CRUSH rule 0 x 67 [246,958]
+ CRUSH rule 0 x 68 [711,473]
+ CRUSH rule 0 x 69 [493,924]
+ CRUSH rule 0 x 70 [30,499]
+ CRUSH rule 0 x 71 [984,883]
+ CRUSH rule 0 x 72 [71,286]
+ CRUSH rule 0 x 73 [922,618]
+ CRUSH rule 0 x 74 [629,414]
+ CRUSH rule 0 x 75 [222,20]
+ CRUSH rule 0 x 76 [262,366]
+ CRUSH rule 0 x 77 [638,469]
+ CRUSH rule 0 x 78 [324,511]
+ CRUSH rule 0 x 79 [577,990]
+ CRUSH rule 0 x 80 [501,95]
+ CRUSH rule 0 x 81 [506,812]
+ CRUSH rule 0 x 82 [222,145]
+ CRUSH rule 0 x 83 [71,634]
+ CRUSH rule 0 x 84 [49,761]
+ CRUSH rule 0 x 85 [985,896]
+ CRUSH rule 0 x 86 [537,745]
+ CRUSH rule 0 x 87 [997,317]
+ CRUSH rule 0 x 88 [957,350]
+ CRUSH rule 0 x 89 [399,730]
+ CRUSH rule 0 x 90 [943,706]
+ CRUSH rule 0 x 91 [22,368]
+ CRUSH rule 0 x 92 [532,424]
+ CRUSH rule 0 x 93 [218,489]
+ CRUSH rule 0 x 94 [181,96]
+ CRUSH rule 0 x 95 [343,957]
+ CRUSH rule 0 x 96 [861,270]
+ CRUSH rule 0 x 97 [459,706]
+ CRUSH rule 0 x 98 [327,867]
+ CRUSH rule 0 x 99 [974,133]
+ CRUSH rule 0 x 100 [32,445]
+ CRUSH rule 0 x 101 [142,90]
+ CRUSH rule 0 x 102 [172,129]
+ CRUSH rule 0 x 103 [630,47]
+ CRUSH rule 0 x 104 [758,133]
+ CRUSH rule 0 x 105 [843,604]
+ CRUSH rule 0 x 106 [28,681]
+ CRUSH rule 0 x 107 [74,320]
+ CRUSH rule 0 x 108 [875,593]
+ CRUSH rule 0 x 109 [411,985]
+ CRUSH rule 0 x 110 [440,774]
+ CRUSH rule 0 x 111 [405,742]
+ CRUSH rule 0 x 112 [143,181]
+ CRUSH rule 0 x 113 [153,846]
+ CRUSH rule 0 x 114 [804,892]
+ CRUSH rule 0 x 115 [588,508]
+ CRUSH rule 0 x 116 [327,148]
+ CRUSH rule 0 x 117 [95,594]
+ CRUSH rule 0 x 118 [80,957]
+ CRUSH rule 0 x 119 [386,932]
+ CRUSH rule 0 x 120 [366,312]
+ CRUSH rule 0 x 121 [129,154]
+ CRUSH rule 0 x 122 [873,1]
+ CRUSH rule 0 x 123 [533,415]
+ CRUSH rule 0 x 124 [461,691]
+ CRUSH rule 0 x 125 [342,599]
+ CRUSH rule 0 x 126 [819,781]
+ CRUSH rule 0 x 127 [437,893]
+ CRUSH rule 0 x 128 [679,994]
+ CRUSH rule 0 x 129 [380,685]
+ CRUSH rule 0 x 130 [992,52]
+ CRUSH rule 0 x 131 [469,90]
+ CRUSH rule 0 x 132 [571,250]
+ CRUSH rule 0 x 133 [964,728]
+ CRUSH rule 0 x 134 [999,19]
+ CRUSH rule 0 x 135 [634,101]
+ CRUSH rule 0 x 136 [114,889]
+ CRUSH rule 0 x 137 [839,8]
+ CRUSH rule 0 x 138 [967,949]
+ CRUSH rule 0 x 139 [308,711]
+ CRUSH rule 0 x 140 [764,936]
+ CRUSH rule 0 x 141 [423,302]
+ CRUSH rule 0 x 142 [252,821]
+ CRUSH rule 0 x 143 [33,808]
+ CRUSH rule 0 x 144 [472,88]
+ CRUSH rule 0 x 145 [242,208]
+ CRUSH rule 0 x 146 [290,70]
+ CRUSH rule 0 x 147 [447,352]
+ CRUSH rule 0 x 148 [212,644]
+ CRUSH rule 0 x 149 [9,775]
+ CRUSH rule 0 x 150 [166,456]
+ CRUSH rule 0 x 151 [811,875]
+ CRUSH rule 0 x 152 [449,617]
+ CRUSH rule 0 x 153 [523,537]
+ CRUSH rule 0 x 154 [208,559]
+ CRUSH rule 0 x 155 [569,325]
+ CRUSH rule 0 x 156 [488,121]
+ CRUSH rule 0 x 157 [140,723]
+ CRUSH rule 0 x 158 [786,451]
+ CRUSH rule 0 x 159 [134,664]
+ CRUSH rule 0 x 160 [690,112]
+ CRUSH rule 0 x 161 [324,912]
+ CRUSH rule 0 x 162 [748,567]
+ CRUSH rule 0 x 163 [575,499]
+ CRUSH rule 0 x 164 [314,489]
+ CRUSH rule 0 x 165 [116,209]
+ CRUSH rule 0 x 166 [352,706]
+ CRUSH rule 0 x 167 [27,743]
+ CRUSH rule 0 x 168 [953,898]
+ CRUSH rule 0 x 169 [912,147]
+ CRUSH rule 0 x 170 [421,515]
+ CRUSH rule 0 x 171 [488,584]
+ CRUSH rule 0 x 172 [366,443]
+ CRUSH rule 0 x 173 [863,291]
+ CRUSH rule 0 x 174 [263,555]
+ CRUSH rule 0 x 175 [875,961]
+ CRUSH rule 0 x 176 [745,83]
+ CRUSH rule 0 x 177 [128,244]
+ CRUSH rule 0 x 178 [155,41]
+ CRUSH rule 0 x 179 [593,833]
+ CRUSH rule 0 x 180 [154,734]
+ CRUSH rule 0 x 181 [289,675]
+ CRUSH rule 0 x 182 [730,931]
+ CRUSH rule 0 x 183 [639,237]
+ CRUSH rule 0 x 184 [704,312]
+ CRUSH rule 0 x 185 [97,100]
+ CRUSH rule 0 x 186 [26,665]
+ CRUSH rule 0 x 187 [649,14]
+ CRUSH rule 0 x 188 [682,695]
+ CRUSH rule 0 x 189 [325,693]
+ CRUSH rule 0 x 190 [399,933]
+ CRUSH rule 0 x 191 [629,533]
+ CRUSH rule 0 x 192 [503,578]
+ CRUSH rule 0 x 193 [546,333]
+ CRUSH rule 0 x 194 [242,473]
+ CRUSH rule 0 x 195 [625,719]
+ CRUSH rule 0 x 196 [357,114]
+ CRUSH rule 0 x 197 [306,954]
+ CRUSH rule 0 x 198 [863,791]
+ CRUSH rule 0 x 199 [935,906]
+ CRUSH rule 0 x 200 [373,774]
+ CRUSH rule 0 x 201 [659,320]
+ CRUSH rule 0 x 202 [260,433]
+ CRUSH rule 0 x 203 [36,239]
+ CRUSH rule 0 x 204 [92,516]
+ CRUSH rule 0 x 205 [68,395]
+ CRUSH rule 0 x 206 [570,530]
+ CRUSH rule 0 x 207 [834,457]
+ CRUSH rule 0 x 208 [927,484]
+ CRUSH rule 0 x 209 [878,66]
+ CRUSH rule 0 x 210 [572,981]
+ CRUSH rule 0 x 211 [107,597]
+ CRUSH rule 0 x 212 [389,107]
+ CRUSH rule 0 x 213 [497,717]
+ CRUSH rule 0 x 214 [798,65]
+ CRUSH rule 0 x 215 [233,419]
+ CRUSH rule 0 x 216 [494,464]
+ CRUSH rule 0 x 217 [352,396]
+ CRUSH rule 0 x 218 [895,864]
+ CRUSH rule 0 x 219 [222,534]
+ CRUSH rule 0 x 220 [281,19]
+ CRUSH rule 0 x 221 [64,928]
+ CRUSH rule 0 x 222 [40,544]
+ CRUSH rule 0 x 223 [645,556]
+ CRUSH rule 0 x 224 [647,165]
+ CRUSH rule 0 x 225 [219,714]
+ CRUSH rule 0 x 226 [372,511]
+ CRUSH rule 0 x 227 [925,156]
+ CRUSH rule 0 x 228 [682,404]
+ CRUSH rule 0 x 229 [880,838]
+ CRUSH rule 0 x 230 [328,659]
+ CRUSH rule 0 x 231 [320,383]
+ CRUSH rule 0 x 232 [924,846]
+ CRUSH rule 0 x 233 [948,652]
+ CRUSH rule 0 x 234 [484,943]
+ CRUSH rule 0 x 235 [750,65]
+ CRUSH rule 0 x 236 [551,787]
+ CRUSH rule 0 x 237 [390,157]
+ CRUSH rule 0 x 238 [570,6]
+ CRUSH rule 0 x 239 [729,959]
+ CRUSH rule 0 x 240 [981,241]
+ CRUSH rule 0 x 241 [310,816]
+ CRUSH rule 0 x 242 [161,63]
+ CRUSH rule 0 x 243 [180,394]
+ CRUSH rule 0 x 244 [52,174]
+ CRUSH rule 0 x 245 [523,121]
+ CRUSH rule 0 x 246 [362,893]
+ CRUSH rule 0 x 247 [382,184]
+ CRUSH rule 0 x 248 [129,114]
+ CRUSH rule 0 x 249 [159,683]
+ CRUSH rule 0 x 250 [404,945]
+ CRUSH rule 0 x 251 [661,225]
+ CRUSH rule 0 x 252 [961,226]
+ CRUSH rule 0 x 253 [651,97]
+ CRUSH rule 0 x 254 [123,33]
+ CRUSH rule 0 x 255 [314,649]
+ CRUSH rule 0 x 256 [315,215]
+ CRUSH rule 0 x 257 [825,264]
+ CRUSH rule 0 x 258 [624,789]
+ CRUSH rule 0 x 259 [602,542]
+ CRUSH rule 0 x 260 [717,878]
+ CRUSH rule 0 x 261 [145,517]
+ CRUSH rule 0 x 262 [223,1]
+ CRUSH rule 0 x 263 [462,211]
+ CRUSH rule 0 x 264 [654,471]
+ CRUSH rule 0 x 265 [302,794]
+ CRUSH rule 0 x 266 [202,132]
+ CRUSH rule 0 x 267 [282,938]
+ CRUSH rule 0 x 268 [338,309]
+ CRUSH rule 0 x 269 [738,122]
+ CRUSH rule 0 x 270 [707,982]
+ CRUSH rule 0 x 271 [705,432]
+ CRUSH rule 0 x 272 [756,545]
+ CRUSH rule 0 x 273 [197,502]
+ CRUSH rule 0 x 274 [992,44]
+ CRUSH rule 0 x 275 [544,789]
+ CRUSH rule 0 x 276 [658,467]
+ CRUSH rule 0 x 277 [143,490]
+ CRUSH rule 0 x 278 [492,647]
+ CRUSH rule 0 x 279 [517,792]
+ CRUSH rule 0 x 280 [825,740]
+ CRUSH rule 0 x 281 [224,629]
+ CRUSH rule 0 x 282 [298,661]
+ CRUSH rule 0 x 283 [311,606]
+ CRUSH rule 0 x 284 [771,466]
+ CRUSH rule 0 x 285 [693,362]
+ CRUSH rule 0 x 286 [364,477]
+ CRUSH rule 0 x 287 [591,611]
+ CRUSH rule 0 x 288 [965,541]
+ CRUSH rule 0 x 289 [225,551]
+ CRUSH rule 0 x 290 [577,762]
+ CRUSH rule 0 x 291 [160,903]
+ CRUSH rule 0 x 292 [873,598]
+ CRUSH rule 0 x 293 [100,234]
+ CRUSH rule 0 x 294 [285,943]
+ CRUSH rule 0 x 295 [938,262]
+ CRUSH rule 0 x 296 [850,327]
+ CRUSH rule 0 x 297 [951,53]
+ CRUSH rule 0 x 298 [173,336]
+ CRUSH rule 0 x 299 [598,591]
+ CRUSH rule 0 x 300 [531,957]
+ CRUSH rule 0 x 301 [823,628]
+ CRUSH rule 0 x 302 [184,80]
+ CRUSH rule 0 x 303 [521,766]
+ CRUSH rule 0 x 304 [980,127]
+ CRUSH rule 0 x 305 [153,816]
+ CRUSH rule 0 x 306 [423,739]
+ CRUSH rule 0 x 307 [997,557]
+ CRUSH rule 0 x 308 [991,874]
+ CRUSH rule 0 x 309 [860,394]
+ CRUSH rule 0 x 310 [589,818]
+ CRUSH rule 0 x 311 [477,774]
+ CRUSH rule 0 x 312 [887,853]
+ CRUSH rule 0 x 313 [802,646]
+ CRUSH rule 0 x 314 [654,974]
+ CRUSH rule 0 x 315 [767,227]
+ CRUSH rule 0 x 316 [778,83]
+ CRUSH rule 0 x 317 [184,418]
+ CRUSH rule 0 x 318 [525,410]
+ CRUSH rule 0 x 319 [476,724]
+ CRUSH rule 0 x 320 [149,610]
+ CRUSH rule 0 x 321 [710,79]
+ CRUSH rule 0 x 322 [175,275]
+ CRUSH rule 0 x 323 [819,604]
+ CRUSH rule 0 x 324 [16,745]
+ CRUSH rule 0 x 325 [486,400]
+ CRUSH rule 0 x 326 [613,765]
+ CRUSH rule 0 x 327 [125,289]
+ CRUSH rule 0 x 328 [807,383]
+ CRUSH rule 0 x 329 [588,938]
+ CRUSH rule 0 x 330 [932,644]
+ CRUSH rule 0 x 331 [341,953]
+ CRUSH rule 0 x 332 [153,726]
+ CRUSH rule 0 x 333 [745,845]
+ CRUSH rule 0 x 334 [614,751]
+ CRUSH rule 0 x 335 [518,721]
+ CRUSH rule 0 x 336 [389,424]
+ CRUSH rule 0 x 337 [753,508]
+ CRUSH rule 0 x 338 [128,810]
+ CRUSH rule 0 x 339 [430,308]
+ CRUSH rule 0 x 340 [541,44]
+ CRUSH rule 0 x 341 [402,26]
+ CRUSH rule 0 x 342 [982,57]
+ CRUSH rule 0 x 343 [833,412]
+ CRUSH rule 0 x 344 [784,533]
+ CRUSH rule 0 x 345 [546,300]
+ CRUSH rule 0 x 346 [302,420]
+ CRUSH rule 0 x 347 [488,778]
+ CRUSH rule 0 x 348 [903,744]
+ CRUSH rule 0 x 349 [471,547]
+ CRUSH rule 0 x 350 [348,221]
+ CRUSH rule 0 x 351 [961,582]
+ CRUSH rule 0 x 352 [728,137]
+ CRUSH rule 0 x 353 [904,202]
+ CRUSH rule 0 x 354 [345,226]
+ CRUSH rule 0 x 355 [50,430]
+ CRUSH rule 0 x 356 [87,185]
+ CRUSH rule 0 x 357 [762,459]
+ CRUSH rule 0 x 358 [908,25]
+ CRUSH rule 0 x 359 [484,15]
+ CRUSH rule 0 x 360 [173,378]
+ CRUSH rule 0 x 361 [404,577]
+ CRUSH rule 0 x 362 [403,1]
+ CRUSH rule 0 x 363 [639,911]
+ CRUSH rule 0 x 364 [752,689]
+ CRUSH rule 0 x 365 [956,999]
+ CRUSH rule 0 x 366 [860,925]
+ CRUSH rule 0 x 367 [205,609]
+ CRUSH rule 0 x 368 [301,284]
+ CRUSH rule 0 x 369 [452,658]
+ CRUSH rule 0 x 370 [11,467]
+ CRUSH rule 0 x 371 [124,487]
+ CRUSH rule 0 x 372 [253,48]
+ CRUSH rule 0 x 373 [715,605]
+ CRUSH rule 0 x 374 [191,887]
+ CRUSH rule 0 x 375 [711,385]
+ CRUSH rule 0 x 376 [597,818]
+ CRUSH rule 0 x 377 [294,256]
+ CRUSH rule 0 x 378 [34,151]
+ CRUSH rule 0 x 379 [869,136]
+ CRUSH rule 0 x 380 [294,97]
+ CRUSH rule 0 x 381 [119,710]
+ CRUSH rule 0 x 382 [69,631]
+ CRUSH rule 0 x 383 [922,588]
+ CRUSH rule 0 x 384 [221,945]
+ CRUSH rule 0 x 385 [561,737]
+ CRUSH rule 0 x 386 [335,442]
+ CRUSH rule 0 x 387 [514,43]
+ CRUSH rule 0 x 388 [587,89]
+ CRUSH rule 0 x 389 [109,641]
+ CRUSH rule 0 x 390 [925,149]
+ CRUSH rule 0 x 391 [267,87]
+ CRUSH rule 0 x 392 [382,485]
+ CRUSH rule 0 x 393 [425,721]
+ CRUSH rule 0 x 394 [898,18]
+ CRUSH rule 0 x 395 [806,876]
+ CRUSH rule 0 x 396 [790,970]
+ CRUSH rule 0 x 397 [136,363]
+ CRUSH rule 0 x 398 [914,116]
+ CRUSH rule 0 x 399 [261,94]
+ CRUSH rule 0 x 400 [661,197]
+ CRUSH rule 0 x 401 [953,979]
+ CRUSH rule 0 x 402 [738,819]
+ CRUSH rule 0 x 403 [573,238]
+ CRUSH rule 0 x 404 [526,848]
+ CRUSH rule 0 x 405 [582,505]
+ CRUSH rule 0 x 406 [768,324]
+ CRUSH rule 0 x 407 [260,951]
+ CRUSH rule 0 x 408 [657,81]
+ CRUSH rule 0 x 409 [498,89]
+ CRUSH rule 0 x 410 [28,793]
+ CRUSH rule 0 x 411 [684,992]
+ CRUSH rule 0 x 412 [261,958]
+ CRUSH rule 0 x 413 [891,835]
+ CRUSH rule 0 x 414 [127,459]
+ CRUSH rule 0 x 415 [272,540]
+ CRUSH rule 0 x 416 [739,617]
+ CRUSH rule 0 x 417 [106,209]
+ CRUSH rule 0 x 418 [525,441]
+ CRUSH rule 0 x 419 [603,673]
+ CRUSH rule 0 x 420 [988,213]
+ CRUSH rule 0 x 421 [761,521]
+ CRUSH rule 0 x 422 [317,160]
+ CRUSH rule 0 x 423 [137,807]
+ CRUSH rule 0 x 424 [920,37]
+ CRUSH rule 0 x 425 [277,693]
+ CRUSH rule 0 x 426 [485,936]
+ CRUSH rule 0 x 427 [242,515]
+ CRUSH rule 0 x 428 [632,635]
+ CRUSH rule 0 x 429 [641,73]
+ CRUSH rule 0 x 430 [626,585]
+ CRUSH rule 0 x 431 [697,76]
+ CRUSH rule 0 x 432 [590,526]
+ CRUSH rule 0 x 433 [284,387]
+ CRUSH rule 0 x 434 [538,985]
+ CRUSH rule 0 x 435 [30,318]
+ CRUSH rule 0 x 436 [164,919]
+ CRUSH rule 0 x 437 [322,212]
+ CRUSH rule 0 x 438 [142,392]
+ CRUSH rule 0 x 439 [119,370]
+ CRUSH rule 0 x 440 [333,403]
+ CRUSH rule 0 x 441 [477,727]
+ CRUSH rule 0 x 442 [274,590]
+ CRUSH rule 0 x 443 [983,748]
+ CRUSH rule 0 x 444 [536,509]
+ CRUSH rule 0 x 445 [485,482]
+ CRUSH rule 0 x 446 [345,634]
+ CRUSH rule 0 x 447 [61,845]
+ CRUSH rule 0 x 448 [333,232]
+ CRUSH rule 0 x 449 [680,16]
+ CRUSH rule 0 x 450 [235,214]
+ CRUSH rule 0 x 451 [961,468]
+ CRUSH rule 0 x 452 [525,479]
+ CRUSH rule 0 x 453 [138,466]
+ CRUSH rule 0 x 454 [137,625]
+ CRUSH rule 0 x 455 [173,150]
+ CRUSH rule 0 x 456 [235,226]
+ CRUSH rule 0 x 457 [450,577]
+ CRUSH rule 0 x 458 [195,537]
+ CRUSH rule 0 x 459 [381,555]
+ CRUSH rule 0 x 460 [972,730]
+ CRUSH rule 0 x 461 [506,279]
+ CRUSH rule 0 x 462 [692,959]
+ CRUSH rule 0 x 463 [788,667]
+ CRUSH rule 0 x 464 [133,122]
+ CRUSH rule 0 x 465 [971,190]
+ CRUSH rule 0 x 466 [394,576]
+ CRUSH rule 0 x 467 [517,28]
+ CRUSH rule 0 x 468 [829,143]
+ CRUSH rule 0 x 469 [987,936]
+ CRUSH rule 0 x 470 [107,982]
+ CRUSH rule 0 x 471 [181,897]
+ CRUSH rule 0 x 472 [547,512]
+ CRUSH rule 0 x 473 [760,997]
+ CRUSH rule 0 x 474 [787,418]
+ CRUSH rule 0 x 475 [662,312]
+ CRUSH rule 0 x 476 [110,495]
+ CRUSH rule 0 x 477 [393,954]
+ CRUSH rule 0 x 478 [246,483]
+ CRUSH rule 0 x 479 [70,929]
+ CRUSH rule 0 x 480 [753,119]
+ CRUSH rule 0 x 481 [470,429]
+ CRUSH rule 0 x 482 [451,566]
+ CRUSH rule 0 x 483 [816,72]
+ CRUSH rule 0 x 484 [540,454]
+ CRUSH rule 0 x 485 [74,582]
+ CRUSH rule 0 x 486 [958,595]
+ CRUSH rule 0 x 487 [228,302]
+ CRUSH rule 0 x 488 [180,529]
+ CRUSH rule 0 x 489 [47,617]
+ CRUSH rule 0 x 490 [905,822]
+ CRUSH rule 0 x 491 [892,370]
+ CRUSH rule 0 x 492 [588,959]
+ CRUSH rule 0 x 493 [353,461]
+ CRUSH rule 0 x 494 [378,848]
+ CRUSH rule 0 x 495 [845,653]
+ CRUSH rule 0 x 496 [13,988]
+ CRUSH rule 0 x 497 [796,877]
+ CRUSH rule 0 x 498 [412,337]
+ CRUSH rule 0 x 499 [330,695]
+ CRUSH rule 0 x 500 [820,272]
+ CRUSH rule 0 x 501 [110,44]
+ CRUSH rule 0 x 502 [336,595]
+ CRUSH rule 0 x 503 [922,211]
+ CRUSH rule 0 x 504 [483,52]
+ CRUSH rule 0 x 505 [482,598]
+ CRUSH rule 0 x 506 [493,123]
+ CRUSH rule 0 x 507 [12,598]
+ CRUSH rule 0 x 508 [227,157]
+ CRUSH rule 0 x 509 [807,242]
+ CRUSH rule 0 x 510 [134,437]
+ CRUSH rule 0 x 511 [212,54]
+ CRUSH rule 0 x 512 [236,630]
+ CRUSH rule 0 x 513 [994,693]
+ CRUSH rule 0 x 514 [45,508]
+ CRUSH rule 0 x 515 [504,138]
+ CRUSH rule 0 x 516 [285,409]
+ CRUSH rule 0 x 517 [300,232]
+ CRUSH rule 0 x 518 [397,674]
+ CRUSH rule 0 x 519 [86,750]
+ CRUSH rule 0 x 520 [900,833]
+ CRUSH rule 0 x 521 [31,47]
+ CRUSH rule 0 x 522 [390,16]
+ CRUSH rule 0 x 523 [618,308]
+ CRUSH rule 0 x 524 [635,189]
+ CRUSH rule 0 x 525 [311,916]
+ CRUSH rule 0 x 526 [48,738]
+ CRUSH rule 0 x 527 [202,851]
+ CRUSH rule 0 x 528 [565,827]
+ CRUSH rule 0 x 529 [934,864]
+ CRUSH rule 0 x 530 [502,934]
+ CRUSH rule 0 x 531 [681,627]
+ CRUSH rule 0 x 532 [422,6]
+ CRUSH rule 0 x 533 [863,68]
+ CRUSH rule 0 x 534 [962,931]
+ CRUSH rule 0 x 535 [89,565]
+ CRUSH rule 0 x 536 [499,351]
+ CRUSH rule 0 x 537 [676,547]
+ CRUSH rule 0 x 538 [58,644]
+ CRUSH rule 0 x 539 [837,953]
+ CRUSH rule 0 x 540 [831,50]
+ CRUSH rule 0 x 541 [582,757]
+ CRUSH rule 0 x 542 [472,132]
+ CRUSH rule 0 x 543 [382,272]
+ CRUSH rule 0 x 544 [947,930]
+ CRUSH rule 0 x 545 [425,570]
+ CRUSH rule 0 x 546 [18,65]
+ CRUSH rule 0 x 547 [445,715]
+ CRUSH rule 0 x 548 [367,569]
+ CRUSH rule 0 x 549 [125,715]
+ CRUSH rule 0 x 550 [425,599]
+ CRUSH rule 0 x 551 [44,1]
+ CRUSH rule 0 x 552 [246,104]
+ CRUSH rule 0 x 553 [71,703]
+ CRUSH rule 0 x 554 [207,124]
+ CRUSH rule 0 x 555 [570,28]
+ CRUSH rule 0 x 556 [674,152]
+ CRUSH rule 0 x 557 [347,817]
+ CRUSH rule 0 x 558 [627,426]
+ CRUSH rule 0 x 559 [940,630]
+ CRUSH rule 0 x 560 [295,903]
+ CRUSH rule 0 x 561 [506,682]
+ CRUSH rule 0 x 562 [718,529]
+ CRUSH rule 0 x 563 [552,332]
+ CRUSH rule 0 x 564 [835,769]
+ CRUSH rule 0 x 565 [8,167]
+ CRUSH rule 0 x 566 [600,481]
+ CRUSH rule 0 x 567 [999,994]
+ CRUSH rule 0 x 568 [252,431]
+ CRUSH rule 0 x 569 [643,218]
+ CRUSH rule 0 x 570 [617,635]
+ CRUSH rule 0 x 571 [757,80]
+ CRUSH rule 0 x 572 [299,348]
+ CRUSH rule 0 x 573 [25,505]
+ CRUSH rule 0 x 574 [215,431]
+ CRUSH rule 0 x 575 [225,252]
+ CRUSH rule 0 x 576 [627,94]
+ CRUSH rule 0 x 577 [237,809]
+ CRUSH rule 0 x 578 [885,313]
+ CRUSH rule 0 x 579 [924,575]
+ CRUSH rule 0 x 580 [718,51]
+ CRUSH rule 0 x 581 [219,807]
+ CRUSH rule 0 x 582 [893,701]
+ CRUSH rule 0 x 583 [246,930]
+ CRUSH rule 0 x 584 [336,432]
+ CRUSH rule 0 x 585 [324,999]
+ CRUSH rule 0 x 586 [558,230]
+ CRUSH rule 0 x 587 [985,830]
+ CRUSH rule 0 x 588 [211,544]
+ CRUSH rule 0 x 589 [129,21]
+ CRUSH rule 0 x 590 [467,969]
+ CRUSH rule 0 x 591 [758,514]
+ CRUSH rule 0 x 592 [525,253]
+ CRUSH rule 0 x 593 [601,885]
+ CRUSH rule 0 x 594 [227,60]
+ CRUSH rule 0 x 595 [720,854]
+ CRUSH rule 0 x 596 [751,195]
+ CRUSH rule 0 x 597 [129,574]
+ CRUSH rule 0 x 598 [679,207]
+ CRUSH rule 0 x 599 [668,315]
+ CRUSH rule 0 x 600 [143,396]
+ CRUSH rule 0 x 601 [326,573]
+ CRUSH rule 0 x 602 [860,281]
+ CRUSH rule 0 x 603 [709,328]
+ CRUSH rule 0 x 604 [571,62]
+ CRUSH rule 0 x 605 [252,739]
+ CRUSH rule 0 x 606 [339,236]
+ CRUSH rule 0 x 607 [590,248]
+ CRUSH rule 0 x 608 [145,635]
+ CRUSH rule 0 x 609 [973,547]
+ CRUSH rule 0 x 610 [435,816]
+ CRUSH rule 0 x 611 [559,283]
+ CRUSH rule 0 x 612 [273,149]
+ CRUSH rule 0 x 613 [828,614]
+ CRUSH rule 0 x 614 [478,748]
+ CRUSH rule 0 x 615 [392,155]
+ CRUSH rule 0 x 616 [778,637]
+ CRUSH rule 0 x 617 [622,713]
+ CRUSH rule 0 x 618 [149,877]
+ CRUSH rule 0 x 619 [604,163]
+ CRUSH rule 0 x 620 [181,23]
+ CRUSH rule 0 x 621 [735,902]
+ CRUSH rule 0 x 622 [661,824]
+ CRUSH rule 0 x 623 [142,121]
+ CRUSH rule 0 x 624 [360,716]
+ CRUSH rule 0 x 625 [541,167]
+ CRUSH rule 0 x 626 [364,431]
+ CRUSH rule 0 x 627 [458,137]
+ CRUSH rule 0 x 628 [250,350]
+ CRUSH rule 0 x 629 [928,160]
+ CRUSH rule 0 x 630 [243,19]
+ CRUSH rule 0 x 631 [438,221]
+ CRUSH rule 0 x 632 [797,368]
+ CRUSH rule 0 x 633 [993,749]
+ CRUSH rule 0 x 634 [239,351]
+ CRUSH rule 0 x 635 [640,965]
+ CRUSH rule 0 x 636 [173,290]
+ CRUSH rule 0 x 637 [0,918]
+ CRUSH rule 0 x 638 [702,235]
+ CRUSH rule 0 x 639 [475,687]
+ CRUSH rule 0 x 640 [31,664]
+ CRUSH rule 0 x 641 [296,473]
+ CRUSH rule 0 x 642 [894,273]
+ CRUSH rule 0 x 643 [117,111]
+ CRUSH rule 0 x 644 [438,336]
+ CRUSH rule 0 x 645 [982,702]
+ CRUSH rule 0 x 646 [334,804]
+ CRUSH rule 0 x 647 [933,787]
+ CRUSH rule 0 x 648 [22,444]
+ CRUSH rule 0 x 649 [503,229]
+ CRUSH rule 0 x 650 [328,659]
+ CRUSH rule 0 x 651 [3,880]
+ CRUSH rule 0 x 652 [495,977]
+ CRUSH rule 0 x 653 [185,718]
+ CRUSH rule 0 x 654 [130,528]
+ CRUSH rule 0 x 655 [560,872]
+ CRUSH rule 0 x 656 [219,885]
+ CRUSH rule 0 x 657 [233,684]
+ CRUSH rule 0 x 658 [778,6]
+ CRUSH rule 0 x 659 [240,663]
+ CRUSH rule 0 x 660 [244,855]
+ CRUSH rule 0 x 661 [184,270]
+ CRUSH rule 0 x 662 [65,883]
+ CRUSH rule 0 x 663 [323,721]
+ CRUSH rule 0 x 664 [865,113]
+ CRUSH rule 0 x 665 [420,850]
+ CRUSH rule 0 x 666 [319,767]
+ CRUSH rule 0 x 667 [875,39]
+ CRUSH rule 0 x 668 [331,122]
+ CRUSH rule 0 x 669 [915,521]
+ CRUSH rule 0 x 670 [845,659]
+ CRUSH rule 0 x 671 [108,634]
+ CRUSH rule 0 x 672 [578,216]
+ CRUSH rule 0 x 673 [442,74]
+ CRUSH rule 0 x 674 [588,364]
+ CRUSH rule 0 x 675 [489,698]
+ CRUSH rule 0 x 676 [928,911]
+ CRUSH rule 0 x 677 [399,269]
+ CRUSH rule 0 x 678 [546,752]
+ CRUSH rule 0 x 679 [988,25]
+ CRUSH rule 0 x 680 [335,963]
+ CRUSH rule 0 x 681 [690,462]
+ CRUSH rule 0 x 682 [196,588]
+ CRUSH rule 0 x 683 [627,25]
+ CRUSH rule 0 x 684 [38,804]
+ CRUSH rule 0 x 685 [841,368]
+ CRUSH rule 0 x 686 [336,287]
+ CRUSH rule 0 x 687 [20,682]
+ CRUSH rule 0 x 688 [463,371]
+ CRUSH rule 0 x 689 [569,250]
+ CRUSH rule 0 x 690 [551,144]
+ CRUSH rule 0 x 691 [766,464]
+ CRUSH rule 0 x 692 [739,634]
+ CRUSH rule 0 x 693 [339,297]
+ CRUSH rule 0 x 694 [405,26]
+ CRUSH rule 0 x 695 [622,576]
+ CRUSH rule 0 x 696 [558,902]
+ CRUSH rule 0 x 697 [818,222]
+ CRUSH rule 0 x 698 [178,48]
+ CRUSH rule 0 x 699 [450,244]
+ CRUSH rule 0 x 700 [502,771]
+ CRUSH rule 0 x 701 [4,612]
+ CRUSH rule 0 x 702 [177,630]
+ CRUSH rule 0 x 703 [354,178]
+ CRUSH rule 0 x 704 [646,601]
+ CRUSH rule 0 x 705 [921,401]
+ CRUSH rule 0 x 706 [652,877]
+ CRUSH rule 0 x 707 [345,745]
+ CRUSH rule 0 x 708 [333,607]
+ CRUSH rule 0 x 709 [45,187]
+ CRUSH rule 0 x 710 [94,855]
+ CRUSH rule 0 x 711 [227,653]
+ CRUSH rule 0 x 712 [398,953]
+ CRUSH rule 0 x 713 [116,800]
+ CRUSH rule 0 x 714 [111,629]
+ CRUSH rule 0 x 715 [531,291]
+ CRUSH rule 0 x 716 [169,541]
+ CRUSH rule 0 x 717 [417,446]
+ CRUSH rule 0 x 718 [992,383]
+ CRUSH rule 0 x 719 [936,674]
+ CRUSH rule 0 x 720 [370,188]
+ CRUSH rule 0 x 721 [320,859]
+ CRUSH rule 0 x 722 [7,2]
+ CRUSH rule 0 x 723 [270,553]
+ CRUSH rule 0 x 724 [666,822]
+ CRUSH rule 0 x 725 [794,406]
+ CRUSH rule 0 x 726 [420,556]
+ CRUSH rule 0 x 727 [561,461]
+ CRUSH rule 0 x 728 [951,330]
+ CRUSH rule 0 x 729 [656,644]
+ CRUSH rule 0 x 730 [3,558]
+ CRUSH rule 0 x 731 [852,89]
+ CRUSH rule 0 x 732 [983,840]
+ CRUSH rule 0 x 733 [285,396]
+ CRUSH rule 0 x 734 [125,510]
+ CRUSH rule 0 x 735 [417,773]
+ CRUSH rule 0 x 736 [749,396]
+ CRUSH rule 0 x 737 [644,991]
+ CRUSH rule 0 x 738 [449,683]
+ CRUSH rule 0 x 739 [341,220]
+ CRUSH rule 0 x 740 [874,524]
+ CRUSH rule 0 x 741 [189,472]
+ CRUSH rule 0 x 742 [912,581]
+ CRUSH rule 0 x 743 [654,914]
+ CRUSH rule 0 x 744 [725,295]
+ CRUSH rule 0 x 745 [787,858]
+ CRUSH rule 0 x 746 [757,848]
+ CRUSH rule 0 x 747 [700,81]
+ CRUSH rule 0 x 748 [557,436]
+ CRUSH rule 0 x 749 [772,622]
+ CRUSH rule 0 x 750 [946,97]
+ CRUSH rule 0 x 751 [996,618]
+ CRUSH rule 0 x 752 [746,887]
+ CRUSH rule 0 x 753 [741,14]
+ CRUSH rule 0 x 754 [648,349]
+ CRUSH rule 0 x 755 [157,460]
+ CRUSH rule 0 x 756 [416,97]
+ CRUSH rule 0 x 757 [599,839]
+ CRUSH rule 0 x 758 [994,218]
+ CRUSH rule 0 x 759 [959,682]
+ CRUSH rule 0 x 760 [518,943]
+ CRUSH rule 0 x 761 [285,849]
+ CRUSH rule 0 x 762 [591,313]
+ CRUSH rule 0 x 763 [908,411]
+ CRUSH rule 0 x 764 [787,234]
+ CRUSH rule 0 x 765 [327,921]
+ CRUSH rule 0 x 766 [84,161]
+ CRUSH rule 0 x 767 [370,895]
+ CRUSH rule 0 x 768 [826,760]
+ CRUSH rule 0 x 769 [67,768]
+ CRUSH rule 0 x 770 [593,909]
+ CRUSH rule 0 x 771 [309,935]
+ CRUSH rule 0 x 772 [12,125]
+ CRUSH rule 0 x 773 [253,466]
+ CRUSH rule 0 x 774 [164,390]
+ CRUSH rule 0 x 775 [703,47]
+ CRUSH rule 0 x 776 [728,231]
+ CRUSH rule 0 x 777 [981,621]
+ CRUSH rule 0 x 778 [411,456]
+ CRUSH rule 0 x 779 [346,121]
+ CRUSH rule 0 x 780 [476,39]
+ CRUSH rule 0 x 781 [10,130]
+ CRUSH rule 0 x 782 [462,246]
+ CRUSH rule 0 x 783 [580,373]
+ CRUSH rule 0 x 784 [413,113]
+ CRUSH rule 0 x 785 [341,856]
+ CRUSH rule 0 x 786 [411,140]
+ CRUSH rule 0 x 787 [605,522]
+ CRUSH rule 0 x 788 [226,545]
+ CRUSH rule 0 x 789 [545,320]
+ CRUSH rule 0 x 790 [414,748]
+ CRUSH rule 0 x 791 [660,906]
+ CRUSH rule 0 x 792 [287,392]
+ CRUSH rule 0 x 793 [631,133]
+ CRUSH rule 0 x 794 [931,517]
+ CRUSH rule 0 x 795 [551,962]
+ CRUSH rule 0 x 796 [814,4]
+ CRUSH rule 0 x 797 [64,201]
+ CRUSH rule 0 x 798 [422,530]
+ CRUSH rule 0 x 799 [824,32]
+ CRUSH rule 0 x 800 [862,623]
+ CRUSH rule 0 x 801 [145,550]
+ CRUSH rule 0 x 802 [570,19]
+ CRUSH rule 0 x 803 [151,812]
+ CRUSH rule 0 x 804 [467,93]
+ CRUSH rule 0 x 805 [621,223]
+ CRUSH rule 0 x 806 [898,957]
+ CRUSH rule 0 x 807 [354,531]
+ CRUSH rule 0 x 808 [7,96]
+ CRUSH rule 0 x 809 [70,734]
+ CRUSH rule 0 x 810 [701,18]
+ CRUSH rule 0 x 811 [248,547]
+ CRUSH rule 0 x 812 [230,576]
+ CRUSH rule 0 x 813 [805,114]
+ CRUSH rule 0 x 814 [54,619]
+ CRUSH rule 0 x 815 [679,412]
+ CRUSH rule 0 x 816 [919,448]
+ CRUSH rule 0 x 817 [765,830]
+ CRUSH rule 0 x 818 [415,566]
+ CRUSH rule 0 x 819 [721,319]
+ CRUSH rule 0 x 820 [218,301]
+ CRUSH rule 0 x 821 [185,795]
+ CRUSH rule 0 x 822 [356,261]
+ CRUSH rule 0 x 823 [220,281]
+ CRUSH rule 0 x 824 [292,809]
+ CRUSH rule 0 x 825 [949,778]
+ CRUSH rule 0 x 826 [767,818]
+ CRUSH rule 0 x 827 [631,83]
+ CRUSH rule 0 x 828 [288,986]
+ CRUSH rule 0 x 829 [990,667]
+ CRUSH rule 0 x 830 [152,571]
+ CRUSH rule 0 x 831 [814,563]
+ CRUSH rule 0 x 832 [235,641]
+ CRUSH rule 0 x 833 [657,565]
+ CRUSH rule 0 x 834 [907,231]
+ CRUSH rule 0 x 835 [784,262]
+ CRUSH rule 0 x 836 [951,158]
+ CRUSH rule 0 x 837 [556,498]
+ CRUSH rule 0 x 838 [329,274]
+ CRUSH rule 0 x 839 [568,209]
+ CRUSH rule 0 x 840 [45,579]
+ CRUSH rule 0 x 841 [652,702]
+ CRUSH rule 0 x 842 [629,984]
+ CRUSH rule 0 x 843 [799,690]
+ CRUSH rule 0 x 844 [694,600]
+ CRUSH rule 0 x 845 [332,30]
+ CRUSH rule 0 x 846 [452,251]
+ CRUSH rule 0 x 847 [399,681]
+ CRUSH rule 0 x 848 [303,138]
+ CRUSH rule 0 x 849 [666,346]
+ CRUSH rule 0 x 850 [644,511]
+ CRUSH rule 0 x 851 [527,546]
+ CRUSH rule 0 x 852 [31,809]
+ CRUSH rule 0 x 853 [483,330]
+ CRUSH rule 0 x 854 [697,953]
+ CRUSH rule 0 x 855 [837,996]
+ CRUSH rule 0 x 856 [712,40]
+ CRUSH rule 0 x 857 [77,984]
+ CRUSH rule 0 x 858 [412,384]
+ CRUSH rule 0 x 859 [173,760]
+ CRUSH rule 0 x 860 [776,429]
+ CRUSH rule 0 x 861 [705,405]
+ CRUSH rule 0 x 862 [809,44]
+ CRUSH rule 0 x 863 [349,496]
+ CRUSH rule 0 x 864 [717,858]
+ CRUSH rule 0 x 865 [857,603]
+ CRUSH rule 0 x 866 [394,304]
+ CRUSH rule 0 x 867 [640,773]
+ CRUSH rule 0 x 868 [613,950]
+ CRUSH rule 0 x 869 [973,889]
+ CRUSH rule 0 x 870 [505,35]
+ CRUSH rule 0 x 871 [239,264]
+ CRUSH rule 0 x 872 [21,767]
+ CRUSH rule 0 x 873 [954,666]
+ CRUSH rule 0 x 874 [54,510]
+ CRUSH rule 0 x 875 [809,418]
+ CRUSH rule 0 x 876 [483,457]
+ CRUSH rule 0 x 877 [542,531]
+ CRUSH rule 0 x 878 [217,674]
+ CRUSH rule 0 x 879 [999,475]
+ CRUSH rule 0 x 880 [678,573]
+ CRUSH rule 0 x 881 [394,835]
+ CRUSH rule 0 x 882 [467,382]
+ CRUSH rule 0 x 883 [802,744]
+ CRUSH rule 0 x 884 [653,660]
+ CRUSH rule 0 x 885 [898,704]
+ CRUSH rule 0 x 886 [434,357]
+ CRUSH rule 0 x 887 [297,226]
+ CRUSH rule 0 x 888 [863,324]
+ CRUSH rule 0 x 889 [105,102]
+ CRUSH rule 0 x 890 [550,248]
+ CRUSH rule 0 x 891 [575,928]
+ CRUSH rule 0 x 892 [259,862]
+ CRUSH rule 0 x 893 [902,880]
+ CRUSH rule 0 x 894 [180,169]
+ CRUSH rule 0 x 895 [725,849]
+ CRUSH rule 0 x 896 [951,34]
+ CRUSH rule 0 x 897 [810,352]
+ CRUSH rule 0 x 898 [979,433]
+ CRUSH rule 0 x 899 [685,668]
+ CRUSH rule 0 x 900 [530,978]
+ CRUSH rule 0 x 901 [740,107]
+ CRUSH rule 0 x 902 [800,743]
+ CRUSH rule 0 x 903 [230,267]
+ CRUSH rule 0 x 904 [346,949]
+ CRUSH rule 0 x 905 [530,397]
+ CRUSH rule 0 x 906 [80,426]
+ CRUSH rule 0 x 907 [365,968]
+ CRUSH rule 0 x 908 [204,832]
+ CRUSH rule 0 x 909 [883,989]
+ CRUSH rule 0 x 910 [549,593]
+ CRUSH rule 0 x 911 [325,847]
+ CRUSH rule 0 x 912 [874,888]
+ CRUSH rule 0 x 913 [331,463]
+ CRUSH rule 0 x 914 [836,468]
+ CRUSH rule 0 x 915 [245,228]
+ CRUSH rule 0 x 916 [77,967]
+ CRUSH rule 0 x 917 [239,60]
+ CRUSH rule 0 x 918 [988,115]
+ CRUSH rule 0 x 919 [783,139]
+ CRUSH rule 0 x 920 [623,408]
+ CRUSH rule 0 x 921 [105,799]
+ CRUSH rule 0 x 922 [887,505]
+ CRUSH rule 0 x 923 [223,318]
+ CRUSH rule 0 x 924 [25,778]
+ CRUSH rule 0 x 925 [912,601]
+ CRUSH rule 0 x 926 [968,133]
+ CRUSH rule 0 x 927 [277,724]
+ CRUSH rule 0 x 928 [554,203]
+ CRUSH rule 0 x 929 [761,802]
+ CRUSH rule 0 x 930 [814,61]
+ CRUSH rule 0 x 931 [29,193]
+ CRUSH rule 0 x 932 [446,198]
+ CRUSH rule 0 x 933 [352,742]
+ CRUSH rule 0 x 934 [730,2]
+ CRUSH rule 0 x 935 [731,23]
+ CRUSH rule 0 x 936 [322,975]
+ CRUSH rule 0 x 937 [822,221]
+ CRUSH rule 0 x 938 [557,850]
+ CRUSH rule 0 x 939 [150,11]
+ CRUSH rule 0 x 940 [638,398]
+ CRUSH rule 0 x 941 [730,342]
+ CRUSH rule 0 x 942 [62,292]
+ CRUSH rule 0 x 943 [165,314]
+ CRUSH rule 0 x 944 [199,625]
+ CRUSH rule 0 x 945 [946,999]
+ CRUSH rule 0 x 946 [595,93]
+ CRUSH rule 0 x 947 [800,582]
+ CRUSH rule 0 x 948 [132,551]
+ CRUSH rule 0 x 949 [792,920]
+ CRUSH rule 0 x 950 [111,345]
+ CRUSH rule 0 x 951 [414,619]
+ CRUSH rule 0 x 952 [775,469]
+ CRUSH rule 0 x 953 [349,1]
+ CRUSH rule 0 x 954 [570,940]
+ CRUSH rule 0 x 955 [729,774]
+ CRUSH rule 0 x 956 [519,141]
+ CRUSH rule 0 x 957 [242,709]
+ CRUSH rule 0 x 958 [84,217]
+ CRUSH rule 0 x 959 [270,413]
+ CRUSH rule 0 x 960 [458,192]
+ CRUSH rule 0 x 961 [981,388]
+ CRUSH rule 0 x 962 [623,834]
+ CRUSH rule 0 x 963 [291,167]
+ CRUSH rule 0 x 964 [28,156]
+ CRUSH rule 0 x 965 [675,557]
+ CRUSH rule 0 x 966 [836,306]
+ CRUSH rule 0 x 967 [966,386]
+ CRUSH rule 0 x 968 [864,756]
+ CRUSH rule 0 x 969 [729,625]
+ CRUSH rule 0 x 970 [800,362]
+ CRUSH rule 0 x 971 [737,381]
+ CRUSH rule 0 x 972 [952,245]
+ CRUSH rule 0 x 973 [356,455]
+ CRUSH rule 0 x 974 [545,758]
+ CRUSH rule 0 x 975 [336,191]
+ CRUSH rule 0 x 976 [446,208]
+ CRUSH rule 0 x 977 [202,896]
+ CRUSH rule 0 x 978 [612,324]
+ CRUSH rule 0 x 979 [843,457]
+ CRUSH rule 0 x 980 [60,914]
+ CRUSH rule 0 x 981 [702,749]
+ CRUSH rule 0 x 982 [298,928]
+ CRUSH rule 0 x 983 [723,572]
+ CRUSH rule 0 x 984 [723,864]
+ CRUSH rule 0 x 985 [945,459]
+ CRUSH rule 0 x 986 [772,664]
+ CRUSH rule 0 x 987 [88,324]
+ CRUSH rule 0 x 988 [522,927]
+ CRUSH rule 0 x 989 [578,332]
+ CRUSH rule 0 x 990 [638,228]
+ CRUSH rule 0 x 991 [530,221]
+ CRUSH rule 0 x 992 [925,705]
+ CRUSH rule 0 x 993 [991,301]
+ CRUSH rule 0 x 994 [276,51]
+ CRUSH rule 0 x 995 [288,836]
+ CRUSH rule 0 x 996 [887,983]
+ CRUSH rule 0 x 997 [110,924]
+ CRUSH rule 0 x 998 [435,830]
+ CRUSH rule 0 x 999 [876,738]
+ CRUSH rule 0 x 1000 [178,963]
+ CRUSH rule 0 x 1001 [99,519]
+ CRUSH rule 0 x 1002 [515,534]
+ CRUSH rule 0 x 1003 [104,611]
+ CRUSH rule 0 x 1004 [269,638]
+ CRUSH rule 0 x 1005 [369,223]
+ CRUSH rule 0 x 1006 [40,107]
+ CRUSH rule 0 x 1007 [978,111]
+ CRUSH rule 0 x 1008 [965,956]
+ CRUSH rule 0 x 1009 [598,476]
+ CRUSH rule 0 x 1010 [767,523]
+ CRUSH rule 0 x 1011 [289,871]
+ CRUSH rule 0 x 1012 [128,28]
+ CRUSH rule 0 x 1013 [979,765]
+ CRUSH rule 0 x 1014 [979,948]
+ CRUSH rule 0 x 1015 [277,790]
+ CRUSH rule 0 x 1016 [262,73]
+ CRUSH rule 0 x 1017 [150,269]
+ CRUSH rule 0 x 1018 [555,829]
+ CRUSH rule 0 x 1019 [513,356]
+ CRUSH rule 0 x 1020 [158,161]
+ CRUSH rule 0 x 1021 [915,998]
+ CRUSH rule 0 x 1022 [967,829]
+ CRUSH rule 0 x 1023 [488,257]
+ rule 0 (data) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536]
+ CRUSH rule 0 x 1 [876,250,334]
+ CRUSH rule 0 x 2 [292,832,53]
+ CRUSH rule 0 x 3 [623,387,124]
+ CRUSH rule 0 x 4 [61,334,710]
+ CRUSH rule 0 x 5 [946,557,713]
+ CRUSH rule 0 x 6 [576,668,212]
+ CRUSH rule 0 x 7 [645,753,906]
+ CRUSH rule 0 x 8 [243,6,863]
+ CRUSH rule 0 x 9 [22,578,251]
+ CRUSH rule 0 x 10 [758,828,360]
+ CRUSH rule 0 x 11 [769,120,124]
+ CRUSH rule 0 x 12 [780,364,689]
+ CRUSH rule 0 x 13 [557,18,351]
+ CRUSH rule 0 x 14 [59,561,249]
+ CRUSH rule 0 x 15 [718,928,993]
+ CRUSH rule 0 x 16 [673,632,841]
+ CRUSH rule 0 x 17 [648,43,560]
+ CRUSH rule 0 x 18 [654,219,181]
+ CRUSH rule 0 x 19 [850,545,377]
+ CRUSH rule 0 x 20 [717,785,974]
+ CRUSH rule 0 x 21 [420,57,519]
+ CRUSH rule 0 x 22 [503,998,193]
+ CRUSH rule 0 x 23 [411,663,168]
+ CRUSH rule 0 x 24 [266,861,353]
+ CRUSH rule 0 x 25 [760,483,818]
+ CRUSH rule 0 x 26 [903,24,573]
+ CRUSH rule 0 x 27 [946,188,289]
+ CRUSH rule 0 x 28 [69,312,73]
+ CRUSH rule 0 x 29 [844,883,337]
+ CRUSH rule 0 x 30 [621,18,613]
+ CRUSH rule 0 x 31 [784,943,814]
+ CRUSH rule 0 x 32 [173,374,369]
+ CRUSH rule 0 x 33 [698,336,357]
+ CRUSH rule 0 x 34 [168,836,210]
+ CRUSH rule 0 x 35 [274,509,534]
+ CRUSH rule 0 x 36 [318,215,153]
+ CRUSH rule 0 x 37 [173,604,109]
+ CRUSH rule 0 x 38 [708,444,683]
+ CRUSH rule 0 x 39 [662,198,417]
+ CRUSH rule 0 x 40 [620,801,414]
+ CRUSH rule 0 x 41 [811,264,177]
+ CRUSH rule 0 x 42 [863,179,527]
+ CRUSH rule 0 x 43 [686,822,988]
+ CRUSH rule 0 x 44 [396,222,46]
+ CRUSH rule 0 x 45 [991,694,253]
+ CRUSH rule 0 x 46 [420,909,184]
+ CRUSH rule 0 x 47 [467,211,605]
+ CRUSH rule 0 x 48 [955,329,368]
+ CRUSH rule 0 x 49 [974,891,931]
+ CRUSH rule 0 x 50 [870,441,691]
+ CRUSH rule 0 x 51 [182,930,25]
+ CRUSH rule 0 x 52 [704,812,894]
+ CRUSH rule 0 x 53 [185,713,631]
+ CRUSH rule 0 x 54 [270,441,100]
+ CRUSH rule 0 x 55 [895,734,958]
+ CRUSH rule 0 x 56 [564,963,683]
+ CRUSH rule 0 x 57 [738,130,208]
+ CRUSH rule 0 x 58 [524,113,806]
+ CRUSH rule 0 x 59 [408,337,668]
+ CRUSH rule 0 x 60 [228,790,857]
+ CRUSH rule 0 x 61 [154,843,717]
+ CRUSH rule 0 x 62 [594,811,549]
+ CRUSH rule 0 x 63 [646,67,884]
+ CRUSH rule 0 x 64 [175,542,155]
+ CRUSH rule 0 x 65 [745,619,131]
+ CRUSH rule 0 x 66 [275,468,23]
+ CRUSH rule 0 x 67 [246,958,524]
+ CRUSH rule 0 x 68 [711,473,403]
+ CRUSH rule 0 x 69 [493,924,850]
+ CRUSH rule 0 x 70 [30,499,644]
+ CRUSH rule 0 x 71 [984,883,574]
+ CRUSH rule 0 x 72 [71,286,942]
+ CRUSH rule 0 x 73 [922,618,3]
+ CRUSH rule 0 x 74 [629,414,185]
+ CRUSH rule 0 x 75 [222,20,174]
+ CRUSH rule 0 x 76 [262,366,339]
+ CRUSH rule 0 x 77 [638,469,992]
+ CRUSH rule 0 x 78 [324,511,788]
+ CRUSH rule 0 x 79 [577,990,64]
+ CRUSH rule 0 x 80 [501,95,278]
+ CRUSH rule 0 x 81 [506,812,9]
+ CRUSH rule 0 x 82 [222,145,80]
+ CRUSH rule 0 x 83 [71,634,61]
+ CRUSH rule 0 x 84 [49,761,773]
+ CRUSH rule 0 x 85 [985,896,708]
+ CRUSH rule 0 x 86 [537,745,93]
+ CRUSH rule 0 x 87 [997,317,463]
+ CRUSH rule 0 x 88 [957,350,890]
+ CRUSH rule 0 x 89 [399,730,148]
+ CRUSH rule 0 x 90 [943,706,683]
+ CRUSH rule 0 x 91 [22,368,149]
+ CRUSH rule 0 x 92 [532,424,426]
+ CRUSH rule 0 x 93 [218,489,405]
+ CRUSH rule 0 x 94 [181,96,102]
+ CRUSH rule 0 x 95 [343,957,820]
+ CRUSH rule 0 x 96 [861,270,87]
+ CRUSH rule 0 x 97 [459,706,45]
+ CRUSH rule 0 x 98 [327,867,353]
+ CRUSH rule 0 x 99 [974,133,468]
+ CRUSH rule 0 x 100 [32,445,547]
+ CRUSH rule 0 x 101 [142,90,337]
+ CRUSH rule 0 x 102 [172,129,139]
+ CRUSH rule 0 x 103 [630,47,161]
+ CRUSH rule 0 x 104 [758,133,278]
+ CRUSH rule 0 x 105 [843,604,47]
+ CRUSH rule 0 x 106 [28,681,193]
+ CRUSH rule 0 x 107 [74,320,85]
+ CRUSH rule 0 x 108 [875,593,575]
+ CRUSH rule 0 x 109 [411,985,811]
+ CRUSH rule 0 x 110 [440,774,799]
+ CRUSH rule 0 x 111 [405,742,276]
+ CRUSH rule 0 x 112 [143,181,922]
+ CRUSH rule 0 x 113 [153,846,160]
+ CRUSH rule 0 x 114 [804,892,939]
+ CRUSH rule 0 x 115 [588,508,958]
+ CRUSH rule 0 x 116 [327,148,637]
+ CRUSH rule 0 x 117 [95,594,989]
+ CRUSH rule 0 x 118 [80,957,897]
+ CRUSH rule 0 x 119 [386,932,951]
+ CRUSH rule 0 x 120 [366,312,653]
+ CRUSH rule 0 x 121 [129,154,847]
+ CRUSH rule 0 x 122 [873,1,110]
+ CRUSH rule 0 x 123 [533,415,789]
+ CRUSH rule 0 x 124 [461,691,898]
+ CRUSH rule 0 x 125 [342,599,830]
+ CRUSH rule 0 x 126 [819,781,822]
+ CRUSH rule 0 x 127 [437,893,585]
+ CRUSH rule 0 x 128 [679,994,982]
+ CRUSH rule 0 x 129 [380,685,947]
+ CRUSH rule 0 x 130 [992,52,466]
+ CRUSH rule 0 x 131 [469,90,208]
+ CRUSH rule 0 x 132 [571,250,316]
+ CRUSH rule 0 x 133 [964,728,329]
+ CRUSH rule 0 x 134 [999,19,716]
+ CRUSH rule 0 x 135 [634,101,52]
+ CRUSH rule 0 x 136 [114,889,692]
+ CRUSH rule 0 x 137 [839,8,959]
+ CRUSH rule 0 x 138 [967,949,138]
+ CRUSH rule 0 x 139 [308,711,736]
+ CRUSH rule 0 x 140 [764,936,926]
+ CRUSH rule 0 x 141 [423,302,112]
+ CRUSH rule 0 x 142 [252,821,715]
+ CRUSH rule 0 x 143 [33,808,518]
+ CRUSH rule 0 x 144 [472,88,969]
+ CRUSH rule 0 x 145 [242,208,252]
+ CRUSH rule 0 x 146 [290,70,570]
+ CRUSH rule 0 x 147 [447,352,657]
+ CRUSH rule 0 x 148 [212,644,432]
+ CRUSH rule 0 x 149 [9,775,87]
+ CRUSH rule 0 x 150 [166,456,582]
+ CRUSH rule 0 x 151 [811,875,307]
+ CRUSH rule 0 x 152 [449,617,223]
+ CRUSH rule 0 x 153 [523,537,695]
+ CRUSH rule 0 x 154 [208,559,874]
+ CRUSH rule 0 x 155 [569,325,192]
+ CRUSH rule 0 x 156 [488,121,521]
+ CRUSH rule 0 x 157 [140,723,633]
+ CRUSH rule 0 x 158 [786,451,320]
+ CRUSH rule 0 x 159 [134,664,517]
+ CRUSH rule 0 x 160 [690,112,414]
+ CRUSH rule 0 x 161 [324,912,397]
+ CRUSH rule 0 x 162 [748,567,284]
+ CRUSH rule 0 x 163 [575,499,31]
+ CRUSH rule 0 x 164 [314,489,308]
+ CRUSH rule 0 x 165 [116,209,750]
+ CRUSH rule 0 x 166 [352,706,701]
+ CRUSH rule 0 x 167 [27,743,174]
+ CRUSH rule 0 x 168 [953,898,880]
+ CRUSH rule 0 x 169 [912,147,266]
+ CRUSH rule 0 x 170 [421,515,828]
+ CRUSH rule 0 x 171 [488,584,880]
+ CRUSH rule 0 x 172 [366,443,957]
+ CRUSH rule 0 x 173 [863,291,625]
+ CRUSH rule 0 x 174 [263,555,650]
+ CRUSH rule 0 x 175 [875,961,361]
+ CRUSH rule 0 x 176 [745,83,701]
+ CRUSH rule 0 x 177 [128,244,41]
+ CRUSH rule 0 x 178 [155,41,264]
+ CRUSH rule 0 x 179 [593,833,202]
+ CRUSH rule 0 x 180 [154,734,17]
+ CRUSH rule 0 x 181 [289,675,723]
+ CRUSH rule 0 x 182 [730,931,560]
+ CRUSH rule 0 x 183 [639,237,794]
+ CRUSH rule 0 x 184 [704,312,685]
+ CRUSH rule 0 x 185 [97,100,762]
+ CRUSH rule 0 x 186 [26,665,554]
+ CRUSH rule 0 x 187 [649,14,740]
+ CRUSH rule 0 x 188 [682,695,590]
+ CRUSH rule 0 x 189 [325,693,726]
+ CRUSH rule 0 x 190 [399,933,136]
+ CRUSH rule 0 x 191 [629,533,17]
+ CRUSH rule 0 x 192 [503,578,38]
+ CRUSH rule 0 x 193 [546,333,651]
+ CRUSH rule 0 x 194 [242,473,58]
+ CRUSH rule 0 x 195 [625,719,135]
+ CRUSH rule 0 x 196 [357,114,125]
+ CRUSH rule 0 x 197 [306,954,453]
+ CRUSH rule 0 x 198 [863,791,311]
+ CRUSH rule 0 x 199 [935,906,929]
+ CRUSH rule 0 x 200 [373,774,229]
+ CRUSH rule 0 x 201 [659,320,477]
+ CRUSH rule 0 x 202 [260,433,524]
+ CRUSH rule 0 x 203 [36,239,675]
+ CRUSH rule 0 x 204 [92,516,993]
+ CRUSH rule 0 x 205 [68,395,473]
+ CRUSH rule 0 x 206 [570,530,642]
+ CRUSH rule 0 x 207 [834,457,850]
+ CRUSH rule 0 x 208 [927,484,640]
+ CRUSH rule 0 x 209 [878,66,58]
+ CRUSH rule 0 x 210 [572,981,484]
+ CRUSH rule 0 x 211 [107,597,780]
+ CRUSH rule 0 x 212 [389,107,838]
+ CRUSH rule 0 x 213 [497,717,567]
+ CRUSH rule 0 x 214 [798,65,254]
+ CRUSH rule 0 x 215 [233,419,283]
+ CRUSH rule 0 x 216 [494,464,742]
+ CRUSH rule 0 x 217 [352,396,309]
+ CRUSH rule 0 x 218 [895,864,988]
+ CRUSH rule 0 x 219 [222,534,277]
+ CRUSH rule 0 x 220 [281,19,584]
+ CRUSH rule 0 x 221 [64,928,963]
+ CRUSH rule 0 x 222 [40,544,161]
+ CRUSH rule 0 x 223 [645,556,159]
+ CRUSH rule 0 x 224 [647,165,957]
+ CRUSH rule 0 x 225 [219,714,858]
+ CRUSH rule 0 x 226 [372,511,181]
+ CRUSH rule 0 x 227 [925,156,714]
+ CRUSH rule 0 x 228 [682,404,839]
+ CRUSH rule 0 x 229 [880,838,770]
+ CRUSH rule 0 x 230 [328,659,916]
+ CRUSH rule 0 x 231 [320,383,669]
+ CRUSH rule 0 x 232 [924,846,394]
+ CRUSH rule 0 x 233 [948,652,575]
+ CRUSH rule 0 x 234 [484,943,42]
+ CRUSH rule 0 x 235 [750,65,590]
+ CRUSH rule 0 x 236 [551,787,490]
+ CRUSH rule 0 x 237 [390,157,166]
+ CRUSH rule 0 x 238 [570,6,989]
+ CRUSH rule 0 x 239 [729,959,376]
+ CRUSH rule 0 x 240 [981,241,156]
+ CRUSH rule 0 x 241 [310,816,641]
+ CRUSH rule 0 x 242 [161,63,642]
+ CRUSH rule 0 x 243 [180,394,33]
+ CRUSH rule 0 x 244 [52,174,685]
+ CRUSH rule 0 x 245 [523,121,915]
+ CRUSH rule 0 x 246 [362,893,390]
+ CRUSH rule 0 x 247 [382,184,116]
+ CRUSH rule 0 x 248 [129,114,852]
+ CRUSH rule 0 x 249 [159,683,91]
+ CRUSH rule 0 x 250 [404,945,569]
+ CRUSH rule 0 x 251 [661,225,738]
+ CRUSH rule 0 x 252 [961,226,542]
+ CRUSH rule 0 x 253 [651,97,225]
+ CRUSH rule 0 x 254 [123,33,741]
+ CRUSH rule 0 x 255 [314,649,891]
+ CRUSH rule 0 x 256 [315,215,651]
+ CRUSH rule 0 x 257 [825,264,867]
+ CRUSH rule 0 x 258 [624,789,370]
+ CRUSH rule 0 x 259 [602,542,70]
+ CRUSH rule 0 x 260 [717,878,43]
+ CRUSH rule 0 x 261 [145,517,20]
+ CRUSH rule 0 x 262 [223,1,561]
+ CRUSH rule 0 x 263 [462,211,405]
+ CRUSH rule 0 x 264 [654,471,266]
+ CRUSH rule 0 x 265 [302,794,704]
+ CRUSH rule 0 x 266 [202,132,884]
+ CRUSH rule 0 x 267 [282,938,657]
+ CRUSH rule 0 x 268 [338,309,356]
+ CRUSH rule 0 x 269 [738,122,266]
+ CRUSH rule 0 x 270 [707,982,946]
+ CRUSH rule 0 x 271 [705,432,364]
+ CRUSH rule 0 x 272 [756,545,942]
+ CRUSH rule 0 x 273 [197,502,527]
+ CRUSH rule 0 x 274 [992,44,653]
+ CRUSH rule 0 x 275 [544,789,170]
+ CRUSH rule 0 x 276 [658,467,577]
+ CRUSH rule 0 x 277 [143,490,880]
+ CRUSH rule 0 x 278 [492,647,355]
+ CRUSH rule 0 x 279 [517,792,604]
+ CRUSH rule 0 x 280 [825,740,27]
+ CRUSH rule 0 x 281 [224,629,120]
+ CRUSH rule 0 x 282 [298,661,380]
+ CRUSH rule 0 x 283 [311,606,208]
+ CRUSH rule 0 x 284 [771,466,371]
+ CRUSH rule 0 x 285 [693,362,404]
+ CRUSH rule 0 x 286 [364,477,285]
+ CRUSH rule 0 x 287 [591,611,828]
+ CRUSH rule 0 x 288 [965,541,848]
+ CRUSH rule 0 x 289 [225,551,948]
+ CRUSH rule 0 x 290 [577,762,777]
+ CRUSH rule 0 x 291 [160,903,477]
+ CRUSH rule 0 x 292 [873,598,216]
+ CRUSH rule 0 x 293 [100,234,874]
+ CRUSH rule 0 x 294 [285,943,379]
+ CRUSH rule 0 x 295 [938,262,880]
+ CRUSH rule 0 x 296 [850,327,86]
+ CRUSH rule 0 x 297 [951,53,99]
+ CRUSH rule 0 x 298 [173,336,85]
+ CRUSH rule 0 x 299 [598,591,315]
+ CRUSH rule 0 x 300 [531,957,62]
+ CRUSH rule 0 x 301 [823,628,23]
+ CRUSH rule 0 x 302 [184,80,780]
+ CRUSH rule 0 x 303 [521,766,222]
+ CRUSH rule 0 x 304 [980,127,807]
+ CRUSH rule 0 x 305 [153,816,22]
+ CRUSH rule 0 x 306 [423,739,664]
+ CRUSH rule 0 x 307 [997,557,682]
+ CRUSH rule 0 x 308 [991,874,534]
+ CRUSH rule 0 x 309 [860,394,724]
+ CRUSH rule 0 x 310 [589,818,546]
+ CRUSH rule 0 x 311 [477,774,225]
+ CRUSH rule 0 x 312 [887,853,950]
+ CRUSH rule 0 x 313 [802,646,447]
+ CRUSH rule 0 x 314 [654,974,229]
+ CRUSH rule 0 x 315 [767,227,28]
+ CRUSH rule 0 x 316 [778,83,733]
+ CRUSH rule 0 x 317 [184,418,642]
+ CRUSH rule 0 x 318 [525,410,500]
+ CRUSH rule 0 x 319 [476,724,569]
+ CRUSH rule 0 x 320 [149,610,697]
+ CRUSH rule 0 x 321 [710,79,667]
+ CRUSH rule 0 x 322 [175,275,323]
+ CRUSH rule 0 x 323 [819,604,638]
+ CRUSH rule 0 x 324 [16,745,511]
+ CRUSH rule 0 x 325 [486,400,872]
+ CRUSH rule 0 x 326 [613,765,207]
+ CRUSH rule 0 x 327 [125,289,738]
+ CRUSH rule 0 x 328 [807,383,476]
+ CRUSH rule 0 x 329 [588,938,599]
+ CRUSH rule 0 x 330 [932,644,41]
+ CRUSH rule 0 x 331 [341,953,950]
+ CRUSH rule 0 x 332 [153,726,459]
+ CRUSH rule 0 x 333 [745,845,853]
+ CRUSH rule 0 x 334 [614,751,807]
+ CRUSH rule 0 x 335 [518,721,221]
+ CRUSH rule 0 x 336 [389,424,77]
+ CRUSH rule 0 x 337 [753,508,765]
+ CRUSH rule 0 x 338 [128,810,490]
+ CRUSH rule 0 x 339 [430,308,58]
+ CRUSH rule 0 x 340 [541,44,630]
+ CRUSH rule 0 x 341 [402,26,631]
+ CRUSH rule 0 x 342 [982,57,992]
+ CRUSH rule 0 x 343 [833,412,572]
+ CRUSH rule 0 x 344 [784,533,792]
+ CRUSH rule 0 x 345 [546,300,304]
+ CRUSH rule 0 x 346 [302,420,428]
+ CRUSH rule 0 x 347 [488,778,101]
+ CRUSH rule 0 x 348 [903,744,937]
+ CRUSH rule 0 x 349 [471,547,582]
+ CRUSH rule 0 x 350 [348,221,823]
+ CRUSH rule 0 x 351 [961,582,705]
+ CRUSH rule 0 x 352 [728,137,461]
+ CRUSH rule 0 x 353 [904,202,184]
+ CRUSH rule 0 x 354 [345,226,319]
+ CRUSH rule 0 x 355 [50,430,175]
+ CRUSH rule 0 x 356 [87,185,55]
+ CRUSH rule 0 x 357 [762,459,921]
+ CRUSH rule 0 x 358 [908,25,280]
+ CRUSH rule 0 x 359 [484,15,132]
+ CRUSH rule 0 x 360 [173,378,337]
+ CRUSH rule 0 x 361 [404,577,115]
+ CRUSH rule 0 x 362 [403,1,422]
+ CRUSH rule 0 x 363 [639,911,510]
+ CRUSH rule 0 x 364 [752,689,610]
+ CRUSH rule 0 x 365 [956,999,212]
+ CRUSH rule 0 x 366 [860,925,924]
+ CRUSH rule 0 x 367 [205,609,647]
+ CRUSH rule 0 x 368 [301,284,810]
+ CRUSH rule 0 x 369 [452,658,339]
+ CRUSH rule 0 x 370 [11,467,695]
+ CRUSH rule 0 x 371 [124,487,55]
+ CRUSH rule 0 x 372 [253,48,979]
+ CRUSH rule 0 x 373 [715,605,775]
+ CRUSH rule 0 x 374 [191,887,920]
+ CRUSH rule 0 x 375 [711,385,651]
+ CRUSH rule 0 x 376 [597,818,49]
+ CRUSH rule 0 x 377 [294,256,933]
+ CRUSH rule 0 x 378 [34,151,681]
+ CRUSH rule 0 x 379 [869,136,315]
+ CRUSH rule 0 x 380 [294,97,575]
+ CRUSH rule 0 x 381 [119,710,219]
+ CRUSH rule 0 x 382 [69,631,508]
+ CRUSH rule 0 x 383 [922,588,589]
+ CRUSH rule 0 x 384 [221,945,671]
+ CRUSH rule 0 x 385 [561,737,953]
+ CRUSH rule 0 x 386 [335,442,788]
+ CRUSH rule 0 x 387 [514,43,353]
+ CRUSH rule 0 x 388 [587,89,157]
+ CRUSH rule 0 x 389 [109,641,255]
+ CRUSH rule 0 x 390 [925,149,421]
+ CRUSH rule 0 x 391 [267,87,387]
+ CRUSH rule 0 x 392 [382,485,370]
+ CRUSH rule 0 x 393 [425,721,221]
+ CRUSH rule 0 x 394 [898,18,38]
+ CRUSH rule 0 x 395 [806,876,269]
+ CRUSH rule 0 x 396 [790,970,437]
+ CRUSH rule 0 x 397 [136,363,507]
+ CRUSH rule 0 x 398 [914,116,558]
+ CRUSH rule 0 x 399 [261,94,299]
+ CRUSH rule 0 x 400 [661,197,338]
+ CRUSH rule 0 x 401 [953,979,287]
+ CRUSH rule 0 x 402 [738,819,618]
+ CRUSH rule 0 x 403 [573,238,425]
+ CRUSH rule 0 x 404 [526,848,790]
+ CRUSH rule 0 x 405 [582,505,330]
+ CRUSH rule 0 x 406 [768,324,493]
+ CRUSH rule 0 x 407 [260,951,437]
+ CRUSH rule 0 x 408 [657,81,770]
+ CRUSH rule 0 x 409 [498,89,182]
+ CRUSH rule 0 x 410 [28,793,737]
+ CRUSH rule 0 x 411 [684,992,60]
+ CRUSH rule 0 x 412 [261,958,699]
+ CRUSH rule 0 x 413 [891,835,297]
+ CRUSH rule 0 x 414 [127,459,119]
+ CRUSH rule 0 x 415 [272,540,631]
+ CRUSH rule 0 x 416 [739,617,115]
+ CRUSH rule 0 x 417 [106,209,157]
+ CRUSH rule 0 x 418 [525,441,147]
+ CRUSH rule 0 x 419 [603,673,615]
+ CRUSH rule 0 x 420 [988,213,251]
+ CRUSH rule 0 x 421 [761,521,748]
+ CRUSH rule 0 x 422 [317,160,924]
+ CRUSH rule 0 x 423 [137,807,168]
+ CRUSH rule 0 x 424 [920,37,146]
+ CRUSH rule 0 x 425 [277,693,285]
+ CRUSH rule 0 x 426 [485,936,407]
+ CRUSH rule 0 x 427 [242,515,9]
+ CRUSH rule 0 x 428 [632,635,26]
+ CRUSH rule 0 x 429 [641,73,465]
+ CRUSH rule 0 x 430 [626,585,6]
+ CRUSH rule 0 x 431 [697,76,753]
+ CRUSH rule 0 x 432 [590,526,306]
+ CRUSH rule 0 x 433 [284,387,149]
+ CRUSH rule 0 x 434 [538,985,79]
+ CRUSH rule 0 x 435 [30,318,593]
+ CRUSH rule 0 x 436 [164,919,851]
+ CRUSH rule 0 x 437 [322,212,163]
+ CRUSH rule 0 x 438 [142,392,85]
+ CRUSH rule 0 x 439 [119,370,68]
+ CRUSH rule 0 x 440 [333,403,187]
+ CRUSH rule 0 x 441 [477,727,906]
+ CRUSH rule 0 x 442 [274,590,933]
+ CRUSH rule 0 x 443 [983,748,574]
+ CRUSH rule 0 x 444 [536,509,431]
+ CRUSH rule 0 x 445 [485,482,528]
+ CRUSH rule 0 x 446 [345,634,42]
+ CRUSH rule 0 x 447 [61,845,767]
+ CRUSH rule 0 x 448 [333,232,292]
+ CRUSH rule 0 x 449 [680,16,484]
+ CRUSH rule 0 x 450 [235,214,79]
+ CRUSH rule 0 x 451 [961,468,333]
+ CRUSH rule 0 x 452 [525,479,153]
+ CRUSH rule 0 x 453 [138,466,302]
+ CRUSH rule 0 x 454 [137,625,215]
+ CRUSH rule 0 x 455 [173,150,997]
+ CRUSH rule 0 x 456 [235,226,238]
+ CRUSH rule 0 x 457 [450,577,253]
+ CRUSH rule 0 x 458 [195,537,91]
+ CRUSH rule 0 x 459 [381,555,312]
+ CRUSH rule 0 x 460 [972,730,534]
+ CRUSH rule 0 x 461 [506,279,142]
+ CRUSH rule 0 x 462 [692,959,578]
+ CRUSH rule 0 x 463 [788,667,949]
+ CRUSH rule 0 x 464 [133,122,588]
+ CRUSH rule 0 x 465 [971,190,230]
+ CRUSH rule 0 x 466 [394,576,148]
+ CRUSH rule 0 x 467 [517,28,366]
+ CRUSH rule 0 x 468 [829,143,874]
+ CRUSH rule 0 x 469 [987,936,106]
+ CRUSH rule 0 x 470 [107,982,56]
+ CRUSH rule 0 x 471 [181,897,629]
+ CRUSH rule 0 x 472 [547,512,172]
+ CRUSH rule 0 x 473 [760,997,824]
+ CRUSH rule 0 x 474 [787,418,743]
+ CRUSH rule 0 x 475 [662,312,253]
+ CRUSH rule 0 x 476 [110,495,185]
+ CRUSH rule 0 x 477 [393,954,834]
+ CRUSH rule 0 x 478 [246,483,480]
+ CRUSH rule 0 x 479 [70,929,697]
+ CRUSH rule 0 x 480 [753,119,961]
+ CRUSH rule 0 x 481 [470,429,677]
+ CRUSH rule 0 x 482 [451,566,961]
+ CRUSH rule 0 x 483 [816,72,371]
+ CRUSH rule 0 x 484 [540,454,389]
+ CRUSH rule 0 x 485 [74,582,624]
+ CRUSH rule 0 x 486 [958,595,199]
+ CRUSH rule 0 x 487 [228,302,804]
+ CRUSH rule 0 x 488 [180,529,722]
+ CRUSH rule 0 x 489 [47,617,812]
+ CRUSH rule 0 x 490 [905,822,479]
+ CRUSH rule 0 x 491 [892,370,609]
+ CRUSH rule 0 x 492 [588,959,127]
+ CRUSH rule 0 x 493 [353,461,593]
+ CRUSH rule 0 x 494 [378,848,443]
+ CRUSH rule 0 x 495 [845,653,768]
+ CRUSH rule 0 x 496 [13,988,0]
+ CRUSH rule 0 x 497 [796,877,788]
+ CRUSH rule 0 x 498 [412,337,270]
+ CRUSH rule 0 x 499 [330,695,8]
+ CRUSH rule 0 x 500 [820,272,547]
+ CRUSH rule 0 x 501 [110,44,132]
+ CRUSH rule 0 x 502 [336,595,650]
+ CRUSH rule 0 x 503 [922,211,157]
+ CRUSH rule 0 x 504 [483,52,122]
+ CRUSH rule 0 x 505 [482,598,224]
+ CRUSH rule 0 x 506 [493,123,43]
+ CRUSH rule 0 x 507 [12,598,264]
+ CRUSH rule 0 x 508 [227,157,611]
+ CRUSH rule 0 x 509 [807,242,363]
+ CRUSH rule 0 x 510 [134,437,227]
+ CRUSH rule 0 x 511 [212,54,83]
+ CRUSH rule 0 x 512 [236,630,758]
+ CRUSH rule 0 x 513 [994,693,644]
+ CRUSH rule 0 x 514 [45,508,831]
+ CRUSH rule 0 x 515 [504,138,480]
+ CRUSH rule 0 x 516 [285,409,136]
+ CRUSH rule 0 x 517 [300,232,23]
+ CRUSH rule 0 x 518 [397,674,98]
+ CRUSH rule 0 x 519 [86,750,772]
+ CRUSH rule 0 x 520 [900,833,614]
+ CRUSH rule 0 x 521 [31,47,236]
+ CRUSH rule 0 x 522 [390,16,280]
+ CRUSH rule 0 x 523 [618,308,424]
+ CRUSH rule 0 x 524 [635,189,687]
+ CRUSH rule 0 x 525 [311,916,699]
+ CRUSH rule 0 x 526 [48,738,227]
+ CRUSH rule 0 x 527 [202,851,889]
+ CRUSH rule 0 x 528 [565,827,590]
+ CRUSH rule 0 x 529 [934,864,241]
+ CRUSH rule 0 x 530 [502,934,298]
+ CRUSH rule 0 x 531 [681,627,942]
+ CRUSH rule 0 x 532 [422,6,147]
+ CRUSH rule 0 x 533 [863,68,364]
+ CRUSH rule 0 x 534 [962,931,775]
+ CRUSH rule 0 x 535 [89,565,397]
+ CRUSH rule 0 x 536 [499,351,760]
+ CRUSH rule 0 x 537 [676,547,787]
+ CRUSH rule 0 x 538 [58,644,571]
+ CRUSH rule 0 x 539 [837,953,457]
+ CRUSH rule 0 x 540 [831,50,132]
+ CRUSH rule 0 x 541 [582,757,121]
+ CRUSH rule 0 x 542 [472,132,790]
+ CRUSH rule 0 x 543 [382,272,797]
+ CRUSH rule 0 x 544 [947,930,496]
+ CRUSH rule 0 x 545 [425,570,305]
+ CRUSH rule 0 x 546 [18,65,529]
+ CRUSH rule 0 x 547 [445,715,600]
+ CRUSH rule 0 x 548 [367,569,980]
+ CRUSH rule 0 x 549 [125,715,671]
+ CRUSH rule 0 x 550 [425,599,744]
+ CRUSH rule 0 x 551 [44,1,528]
+ CRUSH rule 0 x 552 [246,104,68]
+ CRUSH rule 0 x 553 [71,703,615]
+ CRUSH rule 0 x 554 [207,124,217]
+ CRUSH rule 0 x 555 [570,28,317]
+ CRUSH rule 0 x 556 [674,152,421]
+ CRUSH rule 0 x 557 [347,817,191]
+ CRUSH rule 0 x 558 [627,426,369]
+ CRUSH rule 0 x 559 [940,630,924]
+ CRUSH rule 0 x 560 [295,903,541]
+ CRUSH rule 0 x 561 [506,682,384]
+ CRUSH rule 0 x 562 [718,529,87]
+ CRUSH rule 0 x 563 [552,332,747]
+ CRUSH rule 0 x 564 [835,769,736]
+ CRUSH rule 0 x 565 [8,167,539]
+ CRUSH rule 0 x 566 [600,481,301]
+ CRUSH rule 0 x 567 [999,994,509]
+ CRUSH rule 0 x 568 [252,431,157]
+ CRUSH rule 0 x 569 [643,218,943]
+ CRUSH rule 0 x 570 [617,635,765]
+ CRUSH rule 0 x 571 [757,80,59]
+ CRUSH rule 0 x 572 [299,348,575]
+ CRUSH rule 0 x 573 [25,505,270]
+ CRUSH rule 0 x 574 [215,431,624]
+ CRUSH rule 0 x 575 [225,252,611]
+ CRUSH rule 0 x 576 [627,94,159]
+ CRUSH rule 0 x 577 [237,809,778]
+ CRUSH rule 0 x 578 [885,313,120]
+ CRUSH rule 0 x 579 [924,575,787]
+ CRUSH rule 0 x 580 [718,51,766]
+ CRUSH rule 0 x 581 [219,807,129]
+ CRUSH rule 0 x 582 [893,701,598]
+ CRUSH rule 0 x 583 [246,930,964]
+ CRUSH rule 0 x 584 [336,432,680]
+ CRUSH rule 0 x 585 [324,999,397]
+ CRUSH rule 0 x 586 [558,230,976]
+ CRUSH rule 0 x 587 [985,830,597]
+ CRUSH rule 0 x 588 [211,544,57]
+ CRUSH rule 0 x 589 [129,21,112]
+ CRUSH rule 0 x 590 [467,969,652]
+ CRUSH rule 0 x 591 [758,514,316]
+ CRUSH rule 0 x 592 [525,253,190]
+ CRUSH rule 0 x 593 [601,885,339]
+ CRUSH rule 0 x 594 [227,60,450]
+ CRUSH rule 0 x 595 [720,854,496]
+ CRUSH rule 0 x 596 [751,195,997]
+ CRUSH rule 0 x 597 [129,574,714]
+ CRUSH rule 0 x 598 [679,207,604]
+ CRUSH rule 0 x 599 [668,315,683]
+ CRUSH rule 0 x 600 [143,396,464]
+ CRUSH rule 0 x 601 [326,573,873]
+ CRUSH rule 0 x 602 [860,281,875]
+ CRUSH rule 0 x 603 [709,328,445]
+ CRUSH rule 0 x 604 [571,62,814]
+ CRUSH rule 0 x 605 [252,739,860]
+ CRUSH rule 0 x 606 [339,236,759]
+ CRUSH rule 0 x 607 [590,248,759]
+ CRUSH rule 0 x 608 [145,635,309]
+ CRUSH rule 0 x 609 [973,547,223]
+ CRUSH rule 0 x 610 [435,816,961]
+ CRUSH rule 0 x 611 [559,283,422]
+ CRUSH rule 0 x 612 [273,149,123]
+ CRUSH rule 0 x 613 [828,614,642]
+ CRUSH rule 0 x 614 [478,748,393]
+ CRUSH rule 0 x 615 [392,155,144]
+ CRUSH rule 0 x 616 [778,637,452]
+ CRUSH rule 0 x 617 [622,713,996]
+ CRUSH rule 0 x 618 [149,877,270]
+ CRUSH rule 0 x 619 [604,163,656]
+ CRUSH rule 0 x 620 [181,23,409]
+ CRUSH rule 0 x 621 [735,902,386]
+ CRUSH rule 0 x 622 [661,824,717]
+ CRUSH rule 0 x 623 [142,121,643]
+ CRUSH rule 0 x 624 [360,716,420]
+ CRUSH rule 0 x 625 [541,167,385]
+ CRUSH rule 0 x 626 [364,431,610]
+ CRUSH rule 0 x 627 [458,137,557]
+ CRUSH rule 0 x 628 [250,350,556]
+ CRUSH rule 0 x 629 [928,160,710]
+ CRUSH rule 0 x 630 [243,19,918]
+ CRUSH rule 0 x 631 [438,221,574]
+ CRUSH rule 0 x 632 [797,368,247]
+ CRUSH rule 0 x 633 [993,749,525]
+ CRUSH rule 0 x 634 [239,351,633]
+ CRUSH rule 0 x 635 [640,965,25]
+ CRUSH rule 0 x 636 [173,290,297]
+ CRUSH rule 0 x 637 [0,918,98]
+ CRUSH rule 0 x 638 [702,235,424]
+ CRUSH rule 0 x 639 [475,687,31]
+ CRUSH rule 0 x 640 [31,664,399]
+ CRUSH rule 0 x 641 [296,473,108]
+ CRUSH rule 0 x 642 [894,273,427]
+ CRUSH rule 0 x 643 [117,111,732]
+ CRUSH rule 0 x 644 [438,336,327]
+ CRUSH rule 0 x 645 [982,702,351]
+ CRUSH rule 0 x 646 [334,804,146]
+ CRUSH rule 0 x 647 [933,787,185]
+ CRUSH rule 0 x 648 [22,444,400]
+ CRUSH rule 0 x 649 [503,229,213]
+ CRUSH rule 0 x 650 [328,659,420]
+ CRUSH rule 0 x 651 [3,880,823]
+ CRUSH rule 0 x 652 [495,977,563]
+ CRUSH rule 0 x 653 [185,718,804]
+ CRUSH rule 0 x 654 [130,528,380]
+ CRUSH rule 0 x 655 [560,872,454]
+ CRUSH rule 0 x 656 [219,885,178]
+ CRUSH rule 0 x 657 [233,684,813]
+ CRUSH rule 0 x 658 [778,6,756]
+ CRUSH rule 0 x 659 [240,663,306]
+ CRUSH rule 0 x 660 [244,855,196]
+ CRUSH rule 0 x 661 [184,270,128]
+ CRUSH rule 0 x 662 [65,883,921]
+ CRUSH rule 0 x 663 [323,721,594]
+ CRUSH rule 0 x 664 [865,113,512]
+ CRUSH rule 0 x 665 [420,850,591]
+ CRUSH rule 0 x 666 [319,767,246]
+ CRUSH rule 0 x 667 [875,39,343]
+ CRUSH rule 0 x 668 [331,122,263]
+ CRUSH rule 0 x 669 [915,521,402]
+ CRUSH rule 0 x 670 [845,659,943]
+ CRUSH rule 0 x 671 [108,634,527]
+ CRUSH rule 0 x 672 [578,216,110]
+ CRUSH rule 0 x 673 [442,74,579]
+ CRUSH rule 0 x 674 [588,364,281]
+ CRUSH rule 0 x 675 [489,698,744]
+ CRUSH rule 0 x 676 [928,911,40]
+ CRUSH rule 0 x 677 [399,269,692]
+ CRUSH rule 0 x 678 [546,752,544]
+ CRUSH rule 0 x 679 [988,25,275]
+ CRUSH rule 0 x 680 [335,963,382]
+ CRUSH rule 0 x 681 [690,462,623]
+ CRUSH rule 0 x 682 [196,588,154]
+ CRUSH rule 0 x 683 [627,25,421]
+ CRUSH rule 0 x 684 [38,804,592]
+ CRUSH rule 0 x 685 [841,368,548]
+ CRUSH rule 0 x 686 [336,287,525]
+ CRUSH rule 0 x 687 [20,682,924]
+ CRUSH rule 0 x 688 [463,371,780]
+ CRUSH rule 0 x 689 [569,250,78]
+ CRUSH rule 0 x 690 [551,144,587]
+ CRUSH rule 0 x 691 [766,464,446]
+ CRUSH rule 0 x 692 [739,634,18]
+ CRUSH rule 0 x 693 [339,297,118]
+ CRUSH rule 0 x 694 [405,26,830]
+ CRUSH rule 0 x 695 [622,576,597]
+ CRUSH rule 0 x 696 [558,902,689]
+ CRUSH rule 0 x 697 [818,222,406]
+ CRUSH rule 0 x 698 [178,48,402]
+ CRUSH rule 0 x 699 [450,244,180]
+ CRUSH rule 0 x 700 [502,771,987]
+ CRUSH rule 0 x 701 [4,612,782]
+ CRUSH rule 0 x 702 [177,630,232]
+ CRUSH rule 0 x 703 [354,178,389]
+ CRUSH rule 0 x 704 [646,601,156]
+ CRUSH rule 0 x 705 [921,401,890]
+ CRUSH rule 0 x 706 [652,877,562]
+ CRUSH rule 0 x 707 [345,745,67]
+ CRUSH rule 0 x 708 [333,607,180]
+ CRUSH rule 0 x 709 [45,187,302]
+ CRUSH rule 0 x 710 [94,855,43]
+ CRUSH rule 0 x 711 [227,653,731]
+ CRUSH rule 0 x 712 [398,953,136]
+ CRUSH rule 0 x 713 [116,800,503]
+ CRUSH rule 0 x 714 [111,629,866]
+ CRUSH rule 0 x 715 [531,291,486]
+ CRUSH rule 0 x 716 [169,541,291]
+ CRUSH rule 0 x 717 [417,446,994]
+ CRUSH rule 0 x 718 [992,383,298]
+ CRUSH rule 0 x 719 [936,674,324]
+ CRUSH rule 0 x 720 [370,188,174]
+ CRUSH rule 0 x 721 [320,859,278]
+ CRUSH rule 0 x 722 [7,2,673]
+ CRUSH rule 0 x 723 [270,553,831]
+ CRUSH rule 0 x 724 [666,822,708]
+ CRUSH rule 0 x 725 [794,406,875]
+ CRUSH rule 0 x 726 [420,556,341]
+ CRUSH rule 0 x 727 [561,461,129]
+ CRUSH rule 0 x 728 [951,330,196]
+ CRUSH rule 0 x 729 [656,644,436]
+ CRUSH rule 0 x 730 [3,558,629]
+ CRUSH rule 0 x 731 [852,89,75]
+ CRUSH rule 0 x 732 [983,840,869]
+ CRUSH rule 0 x 733 [285,396,388]
+ CRUSH rule 0 x 734 [125,510,402]
+ CRUSH rule 0 x 735 [417,773,686]
+ CRUSH rule 0 x 736 [749,396,632]
+ CRUSH rule 0 x 737 [644,991,946]
+ CRUSH rule 0 x 738 [449,683,290]
+ CRUSH rule 0 x 739 [341,220,641]
+ CRUSH rule 0 x 740 [874,524,674]
+ CRUSH rule 0 x 741 [189,472,712]
+ CRUSH rule 0 x 742 [912,581,114]
+ CRUSH rule 0 x 743 [654,914,425]
+ CRUSH rule 0 x 744 [725,295,579]
+ CRUSH rule 0 x 745 [787,858,850]
+ CRUSH rule 0 x 746 [757,848,704]
+ CRUSH rule 0 x 747 [700,81,867]
+ CRUSH rule 0 x 748 [557,436,238]
+ CRUSH rule 0 x 749 [772,622,337]
+ CRUSH rule 0 x 750 [946,97,376]
+ CRUSH rule 0 x 751 [996,618,343]
+ CRUSH rule 0 x 752 [746,887,695]
+ CRUSH rule 0 x 753 [741,14,463]
+ CRUSH rule 0 x 754 [648,349,333]
+ CRUSH rule 0 x 755 [157,460,466]
+ CRUSH rule 0 x 756 [416,97,197]
+ CRUSH rule 0 x 757 [599,839,776]
+ CRUSH rule 0 x 758 [994,218,620]
+ CRUSH rule 0 x 759 [959,682,514]
+ CRUSH rule 0 x 760 [518,943,215]
+ CRUSH rule 0 x 761 [285,849,420]
+ CRUSH rule 0 x 762 [591,313,41]
+ CRUSH rule 0 x 763 [908,411,200]
+ CRUSH rule 0 x 764 [787,234,894]
+ CRUSH rule 0 x 765 [327,921,882]
+ CRUSH rule 0 x 766 [84,161,878]
+ CRUSH rule 0 x 767 [370,895,702]
+ CRUSH rule 0 x 768 [826,760,879]
+ CRUSH rule 0 x 769 [67,768,663]
+ CRUSH rule 0 x 770 [593,909,482]
+ CRUSH rule 0 x 771 [309,935,121]
+ CRUSH rule 0 x 772 [12,125,797]
+ CRUSH rule 0 x 773 [253,466,820]
+ CRUSH rule 0 x 774 [164,390,705]
+ CRUSH rule 0 x 775 [703,47,43]
+ CRUSH rule 0 x 776 [728,231,80]
+ CRUSH rule 0 x 777 [981,621,568]
+ CRUSH rule 0 x 778 [411,456,544]
+ CRUSH rule 0 x 779 [346,121,519]
+ CRUSH rule 0 x 780 [476,39,288]
+ CRUSH rule 0 x 781 [10,130,585]
+ CRUSH rule 0 x 782 [462,246,581]
+ CRUSH rule 0 x 783 [580,373,153]
+ CRUSH rule 0 x 784 [413,113,978]
+ CRUSH rule 0 x 785 [341,856,332]
+ CRUSH rule 0 x 786 [411,140,313]
+ CRUSH rule 0 x 787 [605,522,211]
+ CRUSH rule 0 x 788 [226,545,35]
+ CRUSH rule 0 x 789 [545,320,414]
+ CRUSH rule 0 x 790 [414,748,816]
+ CRUSH rule 0 x 791 [660,906,406]
+ CRUSH rule 0 x 792 [287,392,514]
+ CRUSH rule 0 x 793 [631,133,850]
+ CRUSH rule 0 x 794 [931,517,543]
+ CRUSH rule 0 x 795 [551,962,477]
+ CRUSH rule 0 x 796 [814,4,95]
+ CRUSH rule 0 x 797 [64,201,299]
+ CRUSH rule 0 x 798 [422,530,114]
+ CRUSH rule 0 x 799 [824,32,679]
+ CRUSH rule 0 x 800 [862,623,489]
+ CRUSH rule 0 x 801 [145,550,329]
+ CRUSH rule 0 x 802 [570,19,847]
+ CRUSH rule 0 x 803 [151,812,662]
+ CRUSH rule 0 x 804 [467,93,264]
+ CRUSH rule 0 x 805 [621,223,938]
+ CRUSH rule 0 x 806 [898,957,805]
+ CRUSH rule 0 x 807 [354,531,422]
+ CRUSH rule 0 x 808 [7,96,76]
+ CRUSH rule 0 x 809 [70,734,719]
+ CRUSH rule 0 x 810 [701,18,972]
+ CRUSH rule 0 x 811 [248,547,103]
+ CRUSH rule 0 x 812 [230,576,821]
+ CRUSH rule 0 x 813 [805,114,683]
+ CRUSH rule 0 x 814 [54,619,973]
+ CRUSH rule 0 x 815 [679,412,613]
+ CRUSH rule 0 x 816 [919,448,826]
+ CRUSH rule 0 x 817 [765,830,436]
+ CRUSH rule 0 x 818 [415,566,644]
+ CRUSH rule 0 x 819 [721,319,865]
+ CRUSH rule 0 x 820 [218,301,333]
+ CRUSH rule 0 x 821 [185,795,680]
+ CRUSH rule 0 x 822 [356,261,54]
+ CRUSH rule 0 x 823 [220,281,549]
+ CRUSH rule 0 x 824 [292,809,887]
+ CRUSH rule 0 x 825 [949,778,101]
+ CRUSH rule 0 x 826 [767,818,833]
+ CRUSH rule 0 x 827 [631,83,406]
+ CRUSH rule 0 x 828 [288,986,445]
+ CRUSH rule 0 x 829 [990,667,915]
+ CRUSH rule 0 x 830 [152,571,778]
+ CRUSH rule 0 x 831 [814,563,630]
+ CRUSH rule 0 x 832 [235,641,616]
+ CRUSH rule 0 x 833 [657,565,922]
+ CRUSH rule 0 x 834 [907,231,644]
+ CRUSH rule 0 x 835 [784,262,771]
+ CRUSH rule 0 x 836 [951,158,366]
+ CRUSH rule 0 x 837 [556,498,334]
+ CRUSH rule 0 x 838 [329,274,964]
+ CRUSH rule 0 x 839 [568,209,939]
+ CRUSH rule 0 x 840 [45,579,842]
+ CRUSH rule 0 x 841 [652,702,24]
+ CRUSH rule 0 x 842 [629,984,314]
+ CRUSH rule 0 x 843 [799,690,688]
+ CRUSH rule 0 x 844 [694,600,534]
+ CRUSH rule 0 x 845 [332,30,179]
+ CRUSH rule 0 x 846 [452,251,712]
+ CRUSH rule 0 x 847 [399,681,847]
+ CRUSH rule 0 x 848 [303,138,440]
+ CRUSH rule 0 x 849 [666,346,708]
+ CRUSH rule 0 x 850 [644,511,345]
+ CRUSH rule 0 x 851 [527,546,737]
+ CRUSH rule 0 x 852 [31,809,94]
+ CRUSH rule 0 x 853 [483,330,869]
+ CRUSH rule 0 x 854 [697,953,968]
+ CRUSH rule 0 x 855 [837,996,239]
+ CRUSH rule 0 x 856 [712,40,547]
+ CRUSH rule 0 x 857 [77,984,576]
+ CRUSH rule 0 x 858 [412,384,841]
+ CRUSH rule 0 x 859 [173,760,26]
+ CRUSH rule 0 x 860 [776,429,328]
+ CRUSH rule 0 x 861 [705,405,477]
+ CRUSH rule 0 x 862 [809,44,788]
+ CRUSH rule 0 x 863 [349,496,963]
+ CRUSH rule 0 x 864 [717,858,101]
+ CRUSH rule 0 x 865 [857,603,586]
+ CRUSH rule 0 x 866 [394,304,71]
+ CRUSH rule 0 x 867 [640,773,663]
+ CRUSH rule 0 x 868 [613,950,712]
+ CRUSH rule 0 x 869 [973,889,524]
+ CRUSH rule 0 x 870 [505,35,386]
+ CRUSH rule 0 x 871 [239,264,262]
+ CRUSH rule 0 x 872 [21,767,456]
+ CRUSH rule 0 x 873 [954,666,980]
+ CRUSH rule 0 x 874 [54,510,947]
+ CRUSH rule 0 x 875 [809,418,452]
+ CRUSH rule 0 x 876 [483,457,61]
+ CRUSH rule 0 x 877 [542,531,952]
+ CRUSH rule 0 x 878 [217,674,857]
+ CRUSH rule 0 x 879 [999,475,134]
+ CRUSH rule 0 x 880 [678,573,935]
+ CRUSH rule 0 x 881 [394,835,789]
+ CRUSH rule 0 x 882 [467,382,353]
+ CRUSH rule 0 x 883 [802,744,237]
+ CRUSH rule 0 x 884 [653,660,638]
+ CRUSH rule 0 x 885 [898,704,307]
+ CRUSH rule 0 x 886 [434,357,938]
+ CRUSH rule 0 x 887 [297,226,711]
+ CRUSH rule 0 x 888 [863,324,443]
+ CRUSH rule 0 x 889 [105,102,308]
+ CRUSH rule 0 x 890 [550,248,606]
+ CRUSH rule 0 x 891 [575,928,880]
+ CRUSH rule 0 x 892 [259,862,133]
+ CRUSH rule 0 x 893 [902,880,543]
+ CRUSH rule 0 x 894 [180,169,916]
+ CRUSH rule 0 x 895 [725,849,182]
+ CRUSH rule 0 x 896 [951,34,874]
+ CRUSH rule 0 x 897 [810,352,73]
+ CRUSH rule 0 x 898 [979,433,719]
+ CRUSH rule 0 x 899 [685,668,534]
+ CRUSH rule 0 x 900 [530,978,41]
+ CRUSH rule 0 x 901 [740,107,336]
+ CRUSH rule 0 x 902 [800,743,693]
+ CRUSH rule 0 x 903 [230,267,842]
+ CRUSH rule 0 x 904 [346,949,460]
+ CRUSH rule 0 x 905 [530,397,619]
+ CRUSH rule 0 x 906 [80,426,138]
+ CRUSH rule 0 x 907 [365,968,475]
+ CRUSH rule 0 x 908 [204,832,742]
+ CRUSH rule 0 x 909 [883,989,146]
+ CRUSH rule 0 x 910 [549,593,249]
+ CRUSH rule 0 x 911 [325,847,352]
+ CRUSH rule 0 x 912 [874,888,582]
+ CRUSH rule 0 x 913 [331,463,342]
+ CRUSH rule 0 x 914 [836,468,601]
+ CRUSH rule 0 x 915 [245,228,100]
+ CRUSH rule 0 x 916 [77,967,364]
+ CRUSH rule 0 x 917 [239,60,866]
+ CRUSH rule 0 x 918 [988,115,922]
+ CRUSH rule 0 x 919 [783,139,696]
+ CRUSH rule 0 x 920 [623,408,685]
+ CRUSH rule 0 x 921 [105,799,144]
+ CRUSH rule 0 x 922 [887,505,652]
+ CRUSH rule 0 x 923 [223,318,552]
+ CRUSH rule 0 x 924 [25,778,366]
+ CRUSH rule 0 x 925 [912,601,297]
+ CRUSH rule 0 x 926 [968,133,132]
+ CRUSH rule 0 x 927 [277,724,214]
+ CRUSH rule 0 x 928 [554,203,658]
+ CRUSH rule 0 x 929 [761,802,367]
+ CRUSH rule 0 x 930 [814,61,788]
+ CRUSH rule 0 x 931 [29,193,61]
+ CRUSH rule 0 x 932 [446,198,862]
+ CRUSH rule 0 x 933 [352,742,216]
+ CRUSH rule 0 x 934 [730,2,332]
+ CRUSH rule 0 x 935 [731,23,736]
+ CRUSH rule 0 x 936 [322,975,20]
+ CRUSH rule 0 x 937 [822,221,841]
+ CRUSH rule 0 x 938 [557,850,66]
+ CRUSH rule 0 x 939 [150,11,971]
+ CRUSH rule 0 x 940 [638,398,169]
+ CRUSH rule 0 x 941 [730,342,929]
+ CRUSH rule 0 x 942 [62,292,166]
+ CRUSH rule 0 x 943 [165,314,519]
+ CRUSH rule 0 x 944 [199,625,766]
+ CRUSH rule 0 x 945 [946,999,699]
+ CRUSH rule 0 x 946 [595,93,852]
+ CRUSH rule 0 x 947 [800,582,356]
+ CRUSH rule 0 x 948 [132,551,139]
+ CRUSH rule 0 x 949 [792,920,466]
+ CRUSH rule 0 x 950 [111,345,176]
+ CRUSH rule 0 x 951 [414,619,648]
+ CRUSH rule 0 x 952 [775,469,500]
+ CRUSH rule 0 x 953 [349,1,5]
+ CRUSH rule 0 x 954 [570,940,410]
+ CRUSH rule 0 x 955 [729,774,823]
+ CRUSH rule 0 x 956 [519,141,575]
+ CRUSH rule 0 x 957 [242,709,611]
+ CRUSH rule 0 x 958 [84,217,227]
+ CRUSH rule 0 x 959 [270,413,918]
+ CRUSH rule 0 x 960 [458,192,307]
+ CRUSH rule 0 x 961 [981,388,777]
+ CRUSH rule 0 x 962 [623,834,277]
+ CRUSH rule 0 x 963 [291,167,714]
+ CRUSH rule 0 x 964 [28,156,788]
+ CRUSH rule 0 x 965 [675,557,290]
+ CRUSH rule 0 x 966 [836,306,946]
+ CRUSH rule 0 x 967 [966,386,735]
+ CRUSH rule 0 x 968 [864,756,690]
+ CRUSH rule 0 x 969 [729,625,480]
+ CRUSH rule 0 x 970 [800,362,646]
+ CRUSH rule 0 x 971 [737,381,153]
+ CRUSH rule 0 x 972 [952,245,720]
+ CRUSH rule 0 x 973 [356,455,579]
+ CRUSH rule 0 x 974 [545,758,586]
+ CRUSH rule 0 x 975 [336,191,202]
+ CRUSH rule 0 x 976 [446,208,757]
+ CRUSH rule 0 x 977 [202,896,196]
+ CRUSH rule 0 x 978 [612,324,996]
+ CRUSH rule 0 x 979 [843,457,675]
+ CRUSH rule 0 x 980 [60,914,881]
+ CRUSH rule 0 x 981 [702,749,937]
+ CRUSH rule 0 x 982 [298,928,738]
+ CRUSH rule 0 x 983 [723,572,395]
+ CRUSH rule 0 x 984 [723,864,804]
+ CRUSH rule 0 x 985 [945,459,868]
+ CRUSH rule 0 x 986 [772,664,535]
+ CRUSH rule 0 x 987 [88,324,312]
+ CRUSH rule 0 x 988 [522,927,131]
+ CRUSH rule 0 x 989 [578,332,208]
+ CRUSH rule 0 x 990 [638,228,414]
+ CRUSH rule 0 x 991 [530,221,451]
+ CRUSH rule 0 x 992 [925,705,275]
+ CRUSH rule 0 x 993 [991,301,43]
+ CRUSH rule 0 x 994 [276,51,868]
+ CRUSH rule 0 x 995 [288,836,753]
+ CRUSH rule 0 x 996 [887,983,252]
+ CRUSH rule 0 x 997 [110,924,386]
+ CRUSH rule 0 x 998 [435,830,485]
+ CRUSH rule 0 x 999 [876,738,357]
+ CRUSH rule 0 x 1000 [178,963,638]
+ CRUSH rule 0 x 1001 [99,519,66]
+ CRUSH rule 0 x 1002 [515,534,468]
+ CRUSH rule 0 x 1003 [104,611,937]
+ CRUSH rule 0 x 1004 [269,638,724]
+ CRUSH rule 0 x 1005 [369,223,309]
+ CRUSH rule 0 x 1006 [40,107,69]
+ CRUSH rule 0 x 1007 [978,111,416]
+ CRUSH rule 0 x 1008 [965,956,624]
+ CRUSH rule 0 x 1009 [598,476,356]
+ CRUSH rule 0 x 1010 [767,523,239]
+ CRUSH rule 0 x 1011 [289,871,207]
+ CRUSH rule 0 x 1012 [128,28,370]
+ CRUSH rule 0 x 1013 [979,765,660]
+ CRUSH rule 0 x 1014 [979,948,513]
+ CRUSH rule 0 x 1015 [277,790,396]
+ CRUSH rule 0 x 1016 [262,73,128]
+ CRUSH rule 0 x 1017 [150,269,61]
+ CRUSH rule 0 x 1018 [555,829,554]
+ CRUSH rule 0 x 1019 [513,356,265]
+ CRUSH rule 0 x 1020 [158,161,877]
+ CRUSH rule 0 x 1021 [915,998,957]
+ CRUSH rule 0 x 1022 [967,829,973]
+ CRUSH rule 0 x 1023 [488,257,614]
+ rule 0 (data) num_rep 3 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450]
+ CRUSH rule 0 x 1 [876,250,334,633]
+ CRUSH rule 0 x 2 [292,832,53,392]
+ CRUSH rule 0 x 3 [623,387,124,998]
+ CRUSH rule 0 x 4 [61,334,710,4]
+ CRUSH rule 0 x 5 [946,557,713,664]
+ CRUSH rule 0 x 6 [576,668,212,163]
+ CRUSH rule 0 x 7 [645,753,906,393]
+ CRUSH rule 0 x 8 [243,6,863,781]
+ CRUSH rule 0 x 9 [22,578,251,410]
+ CRUSH rule 0 x 10 [758,828,360,477]
+ CRUSH rule 0 x 11 [769,120,124,527]
+ CRUSH rule 0 x 12 [780,364,689,755]
+ CRUSH rule 0 x 13 [557,18,351,719]
+ CRUSH rule 0 x 14 [59,561,249,461]
+ CRUSH rule 0 x 15 [718,928,993,21]
+ CRUSH rule 0 x 16 [673,632,841,954]
+ CRUSH rule 0 x 17 [648,43,560,514]
+ CRUSH rule 0 x 18 [654,219,181,568]
+ CRUSH rule 0 x 19 [850,545,377,848]
+ CRUSH rule 0 x 20 [717,785,974,5]
+ CRUSH rule 0 x 21 [420,57,519,306]
+ CRUSH rule 0 x 22 [503,998,193,821]
+ CRUSH rule 0 x 23 [411,663,168,110]
+ CRUSH rule 0 x 24 [266,861,353,1]
+ CRUSH rule 0 x 25 [760,483,818,600]
+ CRUSH rule 0 x 26 [903,24,573,718]
+ CRUSH rule 0 x 27 [946,188,289,510]
+ CRUSH rule 0 x 28 [69,312,73,198]
+ CRUSH rule 0 x 29 [844,883,337,628]
+ CRUSH rule 0 x 30 [621,18,613,794]
+ CRUSH rule 0 x 31 [784,943,814,539]
+ CRUSH rule 0 x 32 [173,374,369,972]
+ CRUSH rule 0 x 33 [698,336,357,966]
+ CRUSH rule 0 x 34 [168,836,210,798]
+ CRUSH rule 0 x 35 [274,509,534,818]
+ CRUSH rule 0 x 36 [318,215,153,628]
+ CRUSH rule 0 x 37 [173,604,109,935]
+ CRUSH rule 0 x 38 [708,444,683,604]
+ CRUSH rule 0 x 39 [662,198,417,680]
+ CRUSH rule 0 x 40 [620,801,414,78]
+ CRUSH rule 0 x 41 [811,264,177,127]
+ CRUSH rule 0 x 42 [863,179,527,660]
+ CRUSH rule 0 x 43 [686,822,988,228]
+ CRUSH rule 0 x 44 [396,222,46,841]
+ CRUSH rule 0 x 45 [991,694,253,142]
+ CRUSH rule 0 x 46 [420,909,184,285]
+ CRUSH rule 0 x 47 [467,211,605,207]
+ CRUSH rule 0 x 48 [955,329,368,168]
+ CRUSH rule 0 x 49 [974,891,931,29]
+ CRUSH rule 0 x 50 [870,441,691,823]
+ CRUSH rule 0 x 51 [182,930,25,936]
+ CRUSH rule 0 x 52 [704,812,894,794]
+ CRUSH rule 0 x 53 [185,713,631,280]
+ CRUSH rule 0 x 54 [270,441,100,82]
+ CRUSH rule 0 x 55 [895,734,958,793]
+ CRUSH rule 0 x 56 [564,963,683,324]
+ CRUSH rule 0 x 57 [738,130,208,973]
+ CRUSH rule 0 x 58 [524,113,806,903]
+ CRUSH rule 0 x 59 [408,337,668,529]
+ CRUSH rule 0 x 60 [228,790,857,309]
+ CRUSH rule 0 x 61 [154,843,717,467]
+ CRUSH rule 0 x 62 [594,811,549,276]
+ CRUSH rule 0 x 63 [646,67,884,925]
+ CRUSH rule 0 x 64 [175,542,155,837]
+ CRUSH rule 0 x 65 [745,619,131,867]
+ CRUSH rule 0 x 66 [275,468,23,35]
+ CRUSH rule 0 x 67 [246,958,524,493]
+ CRUSH rule 0 x 68 [711,473,403,228]
+ CRUSH rule 0 x 69 [493,924,850,939]
+ CRUSH rule 0 x 70 [30,499,644,33]
+ CRUSH rule 0 x 71 [984,883,574,716]
+ CRUSH rule 0 x 72 [71,286,942,363]
+ CRUSH rule 0 x 73 [922,618,3,371]
+ CRUSH rule 0 x 74 [629,414,185,573]
+ CRUSH rule 0 x 75 [222,20,174,820]
+ CRUSH rule 0 x 76 [262,366,339,290]
+ CRUSH rule 0 x 77 [638,469,992,280]
+ CRUSH rule 0 x 78 [324,511,788,7]
+ CRUSH rule 0 x 79 [577,990,64,94]
+ CRUSH rule 0 x 80 [501,95,278,903]
+ CRUSH rule 0 x 81 [506,812,9,698]
+ CRUSH rule 0 x 82 [222,145,80,785]
+ CRUSH rule 0 x 83 [71,634,61,91]
+ CRUSH rule 0 x 84 [49,761,773,368]
+ CRUSH rule 0 x 85 [985,896,708,861]
+ CRUSH rule 0 x 86 [537,745,93,524]
+ CRUSH rule 0 x 87 [997,317,463,626]
+ CRUSH rule 0 x 88 [957,350,890,857]
+ CRUSH rule 0 x 89 [399,730,148,314]
+ CRUSH rule 0 x 90 [943,706,683,267]
+ CRUSH rule 0 x 91 [22,368,149,928]
+ CRUSH rule 0 x 92 [532,424,426,773]
+ CRUSH rule 0 x 93 [218,489,405,681]
+ CRUSH rule 0 x 94 [181,96,102,515]
+ CRUSH rule 0 x 95 [343,957,820,139]
+ CRUSH rule 0 x 96 [861,270,87,797]
+ CRUSH rule 0 x 97 [459,706,45,328]
+ CRUSH rule 0 x 98 [327,867,353,948]
+ CRUSH rule 0 x 99 [974,133,468,906]
+ CRUSH rule 0 x 100 [32,445,547,371]
+ CRUSH rule 0 x 101 [142,90,337,950]
+ CRUSH rule 0 x 102 [172,129,139,22]
+ CRUSH rule 0 x 103 [630,47,161,356]
+ CRUSH rule 0 x 104 [758,133,278,11]
+ CRUSH rule 0 x 105 [843,604,47,33]
+ CRUSH rule 0 x 106 [28,681,193,679]
+ CRUSH rule 0 x 107 [74,320,85,819]
+ CRUSH rule 0 x 108 [875,593,575,517]
+ CRUSH rule 0 x 109 [411,985,811,720]
+ CRUSH rule 0 x 110 [440,774,799,660]
+ CRUSH rule 0 x 111 [405,742,276,359]
+ CRUSH rule 0 x 112 [143,181,922,545]
+ CRUSH rule 0 x 113 [153,846,160,903]
+ CRUSH rule 0 x 114 [804,892,939,20]
+ CRUSH rule 0 x 115 [588,508,958,580]
+ CRUSH rule 0 x 116 [327,148,637,486]
+ CRUSH rule 0 x 117 [95,594,989,131]
+ CRUSH rule 0 x 118 [80,957,897,239]
+ CRUSH rule 0 x 119 [386,932,951,768]
+ CRUSH rule 0 x 120 [366,312,653,936]
+ CRUSH rule 0 x 121 [129,154,847,16]
+ CRUSH rule 0 x 122 [873,1,110,939]
+ CRUSH rule 0 x 123 [533,415,789,600]
+ CRUSH rule 0 x 124 [461,691,898,723]
+ CRUSH rule 0 x 125 [342,599,830,402]
+ CRUSH rule 0 x 126 [819,781,822,548]
+ CRUSH rule 0 x 127 [437,893,585,707]
+ CRUSH rule 0 x 128 [679,994,982,550]
+ CRUSH rule 0 x 129 [380,685,947,302]
+ CRUSH rule 0 x 130 [992,52,466,867]
+ CRUSH rule 0 x 131 [469,90,208,599]
+ CRUSH rule 0 x 132 [571,250,316,535]
+ CRUSH rule 0 x 133 [964,728,329,902]
+ CRUSH rule 0 x 134 [999,19,716,963]
+ CRUSH rule 0 x 135 [634,101,52,938]
+ CRUSH rule 0 x 136 [114,889,692,768]
+ CRUSH rule 0 x 137 [839,8,959,280]
+ CRUSH rule 0 x 138 [967,949,138,451]
+ CRUSH rule 0 x 139 [308,711,736,247]
+ CRUSH rule 0 x 140 [764,936,926,55]
+ CRUSH rule 0 x 141 [423,302,112,216]
+ CRUSH rule 0 x 142 [252,821,715,340]
+ CRUSH rule 0 x 143 [33,808,518,477]
+ CRUSH rule 0 x 144 [472,88,969,162]
+ CRUSH rule 0 x 145 [242,208,252,604]
+ CRUSH rule 0 x 146 [290,70,570,384]
+ CRUSH rule 0 x 147 [447,352,657,493]
+ CRUSH rule 0 x 148 [212,644,432,658]
+ CRUSH rule 0 x 149 [9,775,87,35]
+ CRUSH rule 0 x 150 [166,456,582,144]
+ CRUSH rule 0 x 151 [811,875,307,20]
+ CRUSH rule 0 x 152 [449,617,223,9]
+ CRUSH rule 0 x 153 [523,537,695,627]
+ CRUSH rule 0 x 154 [208,559,874,597]
+ CRUSH rule 0 x 155 [569,325,192,296]
+ CRUSH rule 0 x 156 [488,121,521,213]
+ CRUSH rule 0 x 157 [140,723,633,260]
+ CRUSH rule 0 x 158 [786,451,320,239]
+ CRUSH rule 0 x 159 [134,664,517,821]
+ CRUSH rule 0 x 160 [690,112,414,990]
+ CRUSH rule 0 x 161 [324,912,397,423]
+ CRUSH rule 0 x 162 [748,567,284,183]
+ CRUSH rule 0 x 163 [575,499,31,816]
+ CRUSH rule 0 x 164 [314,489,308,326]
+ CRUSH rule 0 x 165 [116,209,750,53]
+ CRUSH rule 0 x 166 [352,706,701,810]
+ CRUSH rule 0 x 167 [27,743,174,142]
+ CRUSH rule 0 x 168 [953,898,880,660]
+ CRUSH rule 0 x 169 [912,147,266,547]
+ CRUSH rule 0 x 170 [421,515,828,844]
+ CRUSH rule 0 x 171 [488,584,880,964]
+ CRUSH rule 0 x 172 [366,443,957,66]
+ CRUSH rule 0 x 173 [863,291,625,287]
+ CRUSH rule 0 x 174 [263,555,650,410]
+ CRUSH rule 0 x 175 [875,961,361,575]
+ CRUSH rule 0 x 176 [745,83,701,680]
+ CRUSH rule 0 x 177 [128,244,41,123]
+ CRUSH rule 0 x 178 [155,41,264,777]
+ CRUSH rule 0 x 179 [593,833,202,183]
+ CRUSH rule 0 x 180 [154,734,17,831]
+ CRUSH rule 0 x 181 [289,675,723,800]
+ CRUSH rule 0 x 182 [730,931,560,209]
+ CRUSH rule 0 x 183 [639,237,794,815]
+ CRUSH rule 0 x 184 [704,312,685,645]
+ CRUSH rule 0 x 185 [97,100,762,82]
+ CRUSH rule 0 x 186 [26,665,554,215]
+ CRUSH rule 0 x 187 [649,14,740,494]
+ CRUSH rule 0 x 188 [682,695,590,743]
+ CRUSH rule 0 x 189 [325,693,726,51]
+ CRUSH rule 0 x 190 [399,933,136,955]
+ CRUSH rule 0 x 191 [629,533,17,126]
+ CRUSH rule 0 x 192 [503,578,38,492]
+ CRUSH rule 0 x 193 [546,333,651,678]
+ CRUSH rule 0 x 194 [242,473,58,655]
+ CRUSH rule 0 x 195 [625,719,135,81]
+ CRUSH rule 0 x 196 [357,114,125,867]
+ CRUSH rule 0 x 197 [306,954,453,873]
+ CRUSH rule 0 x 198 [863,791,311,911]
+ CRUSH rule 0 x 199 [935,906,929,252]
+ CRUSH rule 0 x 200 [373,774,229,454]
+ CRUSH rule 0 x 201 [659,320,477,313]
+ CRUSH rule 0 x 202 [260,433,524,880]
+ CRUSH rule 0 x 203 [36,239,675,971]
+ CRUSH rule 0 x 204 [92,516,993,728]
+ CRUSH rule 0 x 205 [68,395,473,45]
+ CRUSH rule 0 x 206 [570,530,642,380]
+ CRUSH rule 0 x 207 [834,457,850,917]
+ CRUSH rule 0 x 208 [927,484,640,976]
+ CRUSH rule 0 x 209 [878,66,58,940]
+ CRUSH rule 0 x 210 [572,981,484,29]
+ CRUSH rule 0 x 211 [107,597,780,857]
+ CRUSH rule 0 x 212 [389,107,838,624]
+ CRUSH rule 0 x 213 [497,717,567,728]
+ CRUSH rule 0 x 214 [798,65,254,572]
+ CRUSH rule 0 x 215 [233,419,283,638]
+ CRUSH rule 0 x 216 [494,464,742,523]
+ CRUSH rule 0 x 217 [352,396,309,938]
+ CRUSH rule 0 x 218 [895,864,988,650]
+ CRUSH rule 0 x 219 [222,534,277,242]
+ CRUSH rule 0 x 220 [281,19,584,563]
+ CRUSH rule 0 x 221 [64,928,963,130]
+ CRUSH rule 0 x 222 [40,544,161,199]
+ CRUSH rule 0 x 223 [645,556,159,417]
+ CRUSH rule 0 x 224 [647,165,957,263]
+ CRUSH rule 0 x 225 [219,714,858,747]
+ CRUSH rule 0 x 226 [372,511,181,277]
+ CRUSH rule 0 x 227 [925,156,714,863]
+ CRUSH rule 0 x 228 [682,404,839,263]
+ CRUSH rule 0 x 229 [880,838,770,891]
+ CRUSH rule 0 x 230 [328,659,916,468]
+ CRUSH rule 0 x 231 [320,383,669,109]
+ CRUSH rule 0 x 232 [924,846,394,319]
+ CRUSH rule 0 x 233 [948,652,575,838]
+ CRUSH rule 0 x 234 [484,943,42,575]
+ CRUSH rule 0 x 235 [750,65,590,168]
+ CRUSH rule 0 x 236 [551,787,490,136]
+ CRUSH rule 0 x 237 [390,157,166,251]
+ CRUSH rule 0 x 238 [570,6,989,707]
+ CRUSH rule 0 x 239 [729,959,376,975]
+ CRUSH rule 0 x 240 [981,241,156,767]
+ CRUSH rule 0 x 241 [310,816,641,177]
+ CRUSH rule 0 x 242 [161,63,642,837]
+ CRUSH rule 0 x 243 [180,394,33,683]
+ CRUSH rule 0 x 244 [52,174,685,189]
+ CRUSH rule 0 x 245 [523,121,915,84]
+ CRUSH rule 0 x 246 [362,893,390,487]
+ CRUSH rule 0 x 247 [382,184,116,34]
+ CRUSH rule 0 x 248 [129,114,852,469]
+ CRUSH rule 0 x 249 [159,683,91,856]
+ CRUSH rule 0 x 250 [404,945,569,955]
+ CRUSH rule 0 x 251 [661,225,738,757]
+ CRUSH rule 0 x 252 [961,226,542,103]
+ CRUSH rule 0 x 253 [651,97,225,364]
+ CRUSH rule 0 x 254 [123,33,741,692]
+ CRUSH rule 0 x 255 [314,649,891,855]
+ CRUSH rule 0 x 256 [315,215,651,126]
+ CRUSH rule 0 x 257 [825,264,867,869]
+ CRUSH rule 0 x 258 [624,789,370,723]
+ CRUSH rule 0 x 259 [602,542,70,563]
+ CRUSH rule 0 x 260 [717,878,43,56]
+ CRUSH rule 0 x 261 [145,517,20,903]
+ CRUSH rule 0 x 262 [223,1,561,420]
+ CRUSH rule 0 x 263 [462,211,405,508]
+ CRUSH rule 0 x 264 [654,471,266,662]
+ CRUSH rule 0 x 265 [302,794,704,798]
+ CRUSH rule 0 x 266 [202,132,884,209]
+ CRUSH rule 0 x 267 [282,938,657,113]
+ CRUSH rule 0 x 268 [338,309,356,278]
+ CRUSH rule 0 x 269 [738,122,266,200]
+ CRUSH rule 0 x 270 [707,982,946,196]
+ CRUSH rule 0 x 271 [705,432,364,735]
+ CRUSH rule 0 x 272 [756,545,942,56]
+ CRUSH rule 0 x 273 [197,502,527,721]
+ CRUSH rule 0 x 274 [992,44,653,573]
+ CRUSH rule 0 x 275 [544,789,170,434]
+ CRUSH rule 0 x 276 [658,467,577,268]
+ CRUSH rule 0 x 277 [143,490,880,483]
+ CRUSH rule 0 x 278 [492,647,355,282]
+ CRUSH rule 0 x 279 [517,792,604,987]
+ CRUSH rule 0 x 280 [825,740,27,848]
+ CRUSH rule 0 x 281 [224,629,120,562]
+ CRUSH rule 0 x 282 [298,661,380,416]
+ CRUSH rule 0 x 283 [311,606,208,50]
+ CRUSH rule 0 x 284 [771,466,371,743]
+ CRUSH rule 0 x 285 [693,362,404,676]
+ CRUSH rule 0 x 286 [364,477,285,167]
+ CRUSH rule 0 x 287 [591,611,828,995]
+ CRUSH rule 0 x 288 [965,541,848,796]
+ CRUSH rule 0 x 289 [225,551,948,877]
+ CRUSH rule 0 x 290 [577,762,777,751]
+ CRUSH rule 0 x 291 [160,903,477,381]
+ CRUSH rule 0 x 292 [873,598,216,666]
+ CRUSH rule 0 x 293 [100,234,874,47]
+ CRUSH rule 0 x 294 [285,943,379,520]
+ CRUSH rule 0 x 295 [938,262,880,327]
+ CRUSH rule 0 x 296 [850,327,86,472]
+ CRUSH rule 0 x 297 [951,53,99,558]
+ CRUSH rule 0 x 298 [173,336,85,766]
+ CRUSH rule 0 x 299 [598,591,315,386]
+ CRUSH rule 0 x 300 [531,957,62,459]
+ CRUSH rule 0 x 301 [823,628,23,858]
+ CRUSH rule 0 x 302 [184,80,780,871]
+ CRUSH rule 0 x 303 [521,766,222,830]
+ CRUSH rule 0 x 304 [980,127,807,507]
+ CRUSH rule 0 x 305 [153,816,22,927]
+ CRUSH rule 0 x 306 [423,739,664,753]
+ CRUSH rule 0 x 307 [997,557,682,456]
+ CRUSH rule 0 x 308 [991,874,534,465]
+ CRUSH rule 0 x 309 [860,394,724,858]
+ CRUSH rule 0 x 310 [589,818,546,201]
+ CRUSH rule 0 x 311 [477,774,225,590]
+ CRUSH rule 0 x 312 [887,853,950,354]
+ CRUSH rule 0 x 313 [802,646,447,416]
+ CRUSH rule 0 x 314 [654,974,229,511]
+ CRUSH rule 0 x 315 [767,227,28,740]
+ CRUSH rule 0 x 316 [778,83,733,359]
+ CRUSH rule 0 x 317 [184,418,642,986]
+ CRUSH rule 0 x 318 [525,410,500,543]
+ CRUSH rule 0 x 319 [476,724,569,382]
+ CRUSH rule 0 x 320 [149,610,697,296]
+ CRUSH rule 0 x 321 [710,79,667,671]
+ CRUSH rule 0 x 322 [175,275,323,333]
+ CRUSH rule 0 x 323 [819,604,638,792]
+ CRUSH rule 0 x 324 [16,745,511,439]
+ CRUSH rule 0 x 325 [486,400,872,873]
+ CRUSH rule 0 x 326 [613,765,207,19]
+ CRUSH rule 0 x 327 [125,289,738,408]
+ CRUSH rule 0 x 328 [807,383,476,583]
+ CRUSH rule 0 x 329 [588,938,599,432]
+ CRUSH rule 0 x 330 [932,644,41,611]
+ CRUSH rule 0 x 331 [341,953,950,537]
+ CRUSH rule 0 x 332 [153,726,459,950]
+ CRUSH rule 0 x 333 [745,845,853,860]
+ CRUSH rule 0 x 334 [614,751,807,58]
+ CRUSH rule 0 x 335 [518,721,221,283]
+ CRUSH rule 0 x 336 [389,424,77,309]
+ CRUSH rule 0 x 337 [753,508,765,720]
+ CRUSH rule 0 x 338 [128,810,490,753]
+ CRUSH rule 0 x 339 [430,308,58,751]
+ CRUSH rule 0 x 340 [541,44,630,231]
+ CRUSH rule 0 x 341 [402,26,631,439]
+ CRUSH rule 0 x 342 [982,57,992,461]
+ CRUSH rule 0 x 343 [833,412,572,732]
+ CRUSH rule 0 x 344 [784,533,792,41]
+ CRUSH rule 0 x 345 [546,300,304,691]
+ CRUSH rule 0 x 346 [302,420,428,891]
+ CRUSH rule 0 x 347 [488,778,101,217]
+ CRUSH rule 0 x 348 [903,744,937,718]
+ CRUSH rule 0 x 349 [471,547,582,306]
+ CRUSH rule 0 x 350 [348,221,823,335]
+ CRUSH rule 0 x 351 [961,582,705,346]
+ CRUSH rule 0 x 352 [728,137,461,298]
+ CRUSH rule 0 x 353 [904,202,184,447]
+ CRUSH rule 0 x 354 [345,226,319,256]
+ CRUSH rule 0 x 355 [50,430,175,43]
+ CRUSH rule 0 x 356 [87,185,55,423]
+ CRUSH rule 0 x 357 [762,459,921,473]
+ CRUSH rule 0 x 358 [908,25,280,6]
+ CRUSH rule 0 x 359 [484,15,132,121]
+ CRUSH rule 0 x 360 [173,378,337,702]
+ CRUSH rule 0 x 361 [404,577,115,25]
+ CRUSH rule 0 x 362 [403,1,422,945]
+ CRUSH rule 0 x 363 [639,911,510,162]
+ CRUSH rule 0 x 364 [752,689,610,990]
+ CRUSH rule 0 x 365 [956,999,212,230]
+ CRUSH rule 0 x 366 [860,925,924,763]
+ CRUSH rule 0 x 367 [205,609,647,665]
+ CRUSH rule 0 x 368 [301,284,810,169]
+ CRUSH rule 0 x 369 [452,658,339,217]
+ CRUSH rule 0 x 370 [11,467,695,989]
+ CRUSH rule 0 x 371 [124,487,55,514]
+ CRUSH rule 0 x 372 [253,48,979,846]
+ CRUSH rule 0 x 373 [715,605,775,748]
+ CRUSH rule 0 x 374 [191,887,920,928]
+ CRUSH rule 0 x 375 [711,385,651,665]
+ CRUSH rule 0 x 376 [597,818,49,458]
+ CRUSH rule 0 x 377 [294,256,933,771]
+ CRUSH rule 0 x 378 [34,151,681,707]
+ CRUSH rule 0 x 379 [869,136,315,378]
+ CRUSH rule 0 x 380 [294,97,575,791]
+ CRUSH rule 0 x 381 [119,710,219,827]
+ CRUSH rule 0 x 382 [69,631,508,706]
+ CRUSH rule 0 x 383 [922,588,589,925]
+ CRUSH rule 0 x 384 [221,945,671,117]
+ CRUSH rule 0 x 385 [561,737,953,723]
+ CRUSH rule 0 x 386 [335,442,788,696]
+ CRUSH rule 0 x 387 [514,43,353,88]
+ CRUSH rule 0 x 388 [587,89,157,996]
+ CRUSH rule 0 x 389 [109,641,255,466]
+ CRUSH rule 0 x 390 [925,149,421,489]
+ CRUSH rule 0 x 391 [267,87,387,527]
+ CRUSH rule 0 x 392 [382,485,370,849]
+ CRUSH rule 0 x 393 [425,721,221,753]
+ CRUSH rule 0 x 394 [898,18,38,793]
+ CRUSH rule 0 x 395 [806,876,269,679]
+ CRUSH rule 0 x 396 [790,970,437,449]
+ CRUSH rule 0 x 397 [136,363,507,613]
+ CRUSH rule 0 x 398 [914,116,558,258]
+ CRUSH rule 0 x 399 [261,94,299,202]
+ CRUSH rule 0 x 400 [661,197,338,461]
+ CRUSH rule 0 x 401 [953,979,287,803]
+ CRUSH rule 0 x 402 [738,819,618,522]
+ CRUSH rule 0 x 403 [573,238,425,546]
+ CRUSH rule 0 x 404 [526,848,790,253]
+ CRUSH rule 0 x 405 [582,505,330,334]
+ CRUSH rule 0 x 406 [768,324,493,60]
+ CRUSH rule 0 x 407 [260,951,437,587]
+ CRUSH rule 0 x 408 [657,81,770,734]
+ CRUSH rule 0 x 409 [498,89,182,423]
+ CRUSH rule 0 x 410 [28,793,737,352]
+ CRUSH rule 0 x 411 [684,992,60,659]
+ CRUSH rule 0 x 412 [261,958,699,950]
+ CRUSH rule 0 x 413 [891,835,297,441]
+ CRUSH rule 0 x 414 [127,459,119,965]
+ CRUSH rule 0 x 415 [272,540,631,328]
+ CRUSH rule 0 x 416 [739,617,115,530]
+ CRUSH rule 0 x 417 [106,209,157,878]
+ CRUSH rule 0 x 418 [525,441,147,390]
+ CRUSH rule 0 x 419 [603,673,615,465]
+ CRUSH rule 0 x 420 [988,213,251,226]
+ CRUSH rule 0 x 421 [761,521,748,368]
+ CRUSH rule 0 x 422 [317,160,924,548]
+ CRUSH rule 0 x 423 [137,807,168,472]
+ CRUSH rule 0 x 424 [920,37,146,263]
+ CRUSH rule 0 x 425 [277,693,285,221]
+ CRUSH rule 0 x 426 [485,936,407,854]
+ CRUSH rule 0 x 427 [242,515,9,564]
+ CRUSH rule 0 x 428 [632,635,26,473]
+ CRUSH rule 0 x 429 [641,73,465,127]
+ CRUSH rule 0 x 430 [626,585,6,387]
+ CRUSH rule 0 x 431 [697,76,753,570]
+ CRUSH rule 0 x 432 [590,526,306,283]
+ CRUSH rule 0 x 433 [284,387,149,817]
+ CRUSH rule 0 x 434 [538,985,79,953]
+ CRUSH rule 0 x 435 [30,318,593,635]
+ CRUSH rule 0 x 436 [164,919,851,693]
+ CRUSH rule 0 x 437 [322,212,163,606]
+ CRUSH rule 0 x 438 [142,392,85,594]
+ CRUSH rule 0 x 439 [119,370,68,443]
+ CRUSH rule 0 x 440 [333,403,187,863]
+ CRUSH rule 0 x 441 [477,727,906,145]
+ CRUSH rule 0 x 442 [274,590,933,244]
+ CRUSH rule 0 x 443 [983,748,574,718]
+ CRUSH rule 0 x 444 [536,509,431,146]
+ CRUSH rule 0 x 445 [485,482,528,209]
+ CRUSH rule 0 x 446 [345,634,42,294]
+ CRUSH rule 0 x 447 [61,845,767,600]
+ CRUSH rule 0 x 448 [333,232,292,846]
+ CRUSH rule 0 x 449 [680,16,484,670]
+ CRUSH rule 0 x 450 [235,214,79,423]
+ CRUSH rule 0 x 451 [961,468,333,640]
+ CRUSH rule 0 x 452 [525,479,153,528]
+ CRUSH rule 0 x 453 [138,466,302,86]
+ CRUSH rule 0 x 454 [137,625,215,402]
+ CRUSH rule 0 x 455 [173,150,997,16]
+ CRUSH rule 0 x 456 [235,226,238,258]
+ CRUSH rule 0 x 457 [450,577,253,413]
+ CRUSH rule 0 x 458 [195,537,91,814]
+ CRUSH rule 0 x 459 [381,555,312,573]
+ CRUSH rule 0 x 460 [972,730,534,678]
+ CRUSH rule 0 x 461 [506,279,142,830]
+ CRUSH rule 0 x 462 [692,959,578,57]
+ CRUSH rule 0 x 463 [788,667,949,550]
+ CRUSH rule 0 x 464 [133,122,588,999]
+ CRUSH rule 0 x 465 [971,190,230,777]
+ CRUSH rule 0 x 466 [394,576,148,157]
+ CRUSH rule 0 x 467 [517,28,366,362]
+ CRUSH rule 0 x 468 [829,143,874,225]
+ CRUSH rule 0 x 469 [987,936,106,725]
+ CRUSH rule 0 x 470 [107,982,56,889]
+ CRUSH rule 0 x 471 [181,897,629,860]
+ CRUSH rule 0 x 472 [547,512,172,24]
+ CRUSH rule 0 x 473 [760,997,824,905]
+ CRUSH rule 0 x 474 [787,418,743,628]
+ CRUSH rule 0 x 475 [662,312,253,617]
+ CRUSH rule 0 x 476 [110,495,185,508]
+ CRUSH rule 0 x 477 [393,954,834,132]
+ CRUSH rule 0 x 478 [246,483,480,644]
+ CRUSH rule 0 x 479 [70,929,697,931]
+ CRUSH rule 0 x 480 [753,119,961,607]
+ CRUSH rule 0 x 481 [470,429,677,242]
+ CRUSH rule 0 x 482 [451,566,961,675]
+ CRUSH rule 0 x 483 [816,72,371,278]
+ CRUSH rule 0 x 484 [540,454,389,31]
+ CRUSH rule 0 x 485 [74,582,624,684]
+ CRUSH rule 0 x 486 [958,595,199,763]
+ CRUSH rule 0 x 487 [228,302,804,833]
+ CRUSH rule 0 x 488 [180,529,722,956]
+ CRUSH rule 0 x 489 [47,617,812,187]
+ CRUSH rule 0 x 490 [905,822,479,124]
+ CRUSH rule 0 x 491 [892,370,609,998]
+ CRUSH rule 0 x 492 [588,959,127,948]
+ CRUSH rule 0 x 493 [353,461,593,291]
+ CRUSH rule 0 x 494 [378,848,443,368]
+ CRUSH rule 0 x 495 [845,653,768,234]
+ CRUSH rule 0 x 496 [13,988,0,691]
+ CRUSH rule 0 x 497 [796,877,788,394]
+ CRUSH rule 0 x 498 [412,337,270,705]
+ CRUSH rule 0 x 499 [330,695,8,74]
+ CRUSH rule 0 x 500 [820,272,547,765]
+ CRUSH rule 0 x 501 [110,44,132,442]
+ CRUSH rule 0 x 502 [336,595,650,274]
+ CRUSH rule 0 x 503 [922,211,157,722]
+ CRUSH rule 0 x 504 [483,52,122,432]
+ CRUSH rule 0 x 505 [482,598,224,279]
+ CRUSH rule 0 x 506 [493,123,43,856]
+ CRUSH rule 0 x 507 [12,598,264,422]
+ CRUSH rule 0 x 508 [227,157,611,301]
+ CRUSH rule 0 x 509 [807,242,363,122]
+ CRUSH rule 0 x 510 [134,437,227,75]
+ CRUSH rule 0 x 511 [212,54,83,799]
+ CRUSH rule 0 x 512 [236,630,758,752]
+ CRUSH rule 0 x 513 [994,693,644,938]
+ CRUSH rule 0 x 514 [45,508,831,19]
+ CRUSH rule 0 x 515 [504,138,480,272]
+ CRUSH rule 0 x 516 [285,409,136,570]
+ CRUSH rule 0 x 517 [300,232,23,906]
+ CRUSH rule 0 x 518 [397,674,98,898]
+ CRUSH rule 0 x 519 [86,750,772,913]
+ CRUSH rule 0 x 520 [900,833,614,130]
+ CRUSH rule 0 x 521 [31,47,236,751]
+ CRUSH rule 0 x 522 [390,16,280,144]
+ CRUSH rule 0 x 523 [618,308,424,590]
+ CRUSH rule 0 x 524 [635,189,687,963]
+ CRUSH rule 0 x 525 [311,916,699,262]
+ CRUSH rule 0 x 526 [48,738,227,718]
+ CRUSH rule 0 x 527 [202,851,889,216]
+ CRUSH rule 0 x 528 [565,827,590,273]
+ CRUSH rule 0 x 529 [934,864,241,43]
+ CRUSH rule 0 x 530 [502,934,298,670]
+ CRUSH rule 0 x 531 [681,627,942,487]
+ CRUSH rule 0 x 532 [422,6,147,205]
+ CRUSH rule 0 x 533 [863,68,364,983]
+ CRUSH rule 0 x 534 [962,931,775,172]
+ CRUSH rule 0 x 535 [89,565,397,693]
+ CRUSH rule 0 x 536 [499,351,760,458]
+ CRUSH rule 0 x 537 [676,547,787,311]
+ CRUSH rule 0 x 538 [58,644,571,649]
+ CRUSH rule 0 x 539 [837,953,457,711]
+ CRUSH rule 0 x 540 [831,50,132,213]
+ CRUSH rule 0 x 541 [582,757,121,525]
+ CRUSH rule 0 x 542 [472,132,790,997]
+ CRUSH rule 0 x 543 [382,272,797,330]
+ CRUSH rule 0 x 544 [947,930,496,883]
+ CRUSH rule 0 x 545 [425,570,305,77]
+ CRUSH rule 0 x 546 [18,65,529,437]
+ CRUSH rule 0 x 547 [445,715,600,472]
+ CRUSH rule 0 x 548 [367,569,980,167]
+ CRUSH rule 0 x 549 [125,715,671,817]
+ CRUSH rule 0 x 550 [425,599,744,199]
+ CRUSH rule 0 x 551 [44,1,528,922]
+ CRUSH rule 0 x 552 [246,104,68,239]
+ CRUSH rule 0 x 553 [71,703,615,28]
+ CRUSH rule 0 x 554 [207,124,217,166]
+ CRUSH rule 0 x 555 [570,28,317,420]
+ CRUSH rule 0 x 556 [674,152,421,79]
+ CRUSH rule 0 x 557 [347,817,191,391]
+ CRUSH rule 0 x 558 [627,426,369,692]
+ CRUSH rule 0 x 559 [940,630,924,242]
+ CRUSH rule 0 x 560 [295,903,541,29]
+ CRUSH rule 0 x 561 [506,682,384,637]
+ CRUSH rule 0 x 562 [718,529,87,729]
+ CRUSH rule 0 x 563 [552,332,747,206]
+ CRUSH rule 0 x 564 [835,769,736,486]
+ CRUSH rule 0 x 565 [8,167,539,182]
+ CRUSH rule 0 x 566 [600,481,301,263]
+ CRUSH rule 0 x 567 [999,994,509,899]
+ CRUSH rule 0 x 568 [252,431,157,62]
+ CRUSH rule 0 x 569 [643,218,943,455]
+ CRUSH rule 0 x 570 [617,635,765,422]
+ CRUSH rule 0 x 571 [757,80,59,98]
+ CRUSH rule 0 x 572 [299,348,575,889]
+ CRUSH rule 0 x 573 [25,505,270,167]
+ CRUSH rule 0 x 574 [215,431,624,177]
+ CRUSH rule 0 x 575 [225,252,611,546]
+ CRUSH rule 0 x 576 [627,94,159,857]
+ CRUSH rule 0 x 577 [237,809,778,636]
+ CRUSH rule 0 x 578 [885,313,120,344]
+ CRUSH rule 0 x 579 [924,575,787,831]
+ CRUSH rule 0 x 580 [718,51,766,121]
+ CRUSH rule 0 x 581 [219,807,129,571]
+ CRUSH rule 0 x 582 [893,701,598,863]
+ CRUSH rule 0 x 583 [246,930,964,170]
+ CRUSH rule 0 x 584 [336,432,680,175]
+ CRUSH rule 0 x 585 [324,999,397,485]
+ CRUSH rule 0 x 586 [558,230,976,541]
+ CRUSH rule 0 x 587 [985,830,597,21]
+ CRUSH rule 0 x 588 [211,544,57,134]
+ CRUSH rule 0 x 589 [129,21,112,190]
+ CRUSH rule 0 x 590 [467,969,652,593]
+ CRUSH rule 0 x 591 [758,514,316,164]
+ CRUSH rule 0 x 592 [525,253,190,443]
+ CRUSH rule 0 x 593 [601,885,339,152]
+ CRUSH rule 0 x 594 [227,60,450,30]
+ CRUSH rule 0 x 595 [720,854,496,912]
+ CRUSH rule 0 x 596 [751,195,997,77]
+ CRUSH rule 0 x 597 [129,574,714,8]
+ CRUSH rule 0 x 598 [679,207,604,396]
+ CRUSH rule 0 x 599 [668,315,683,349]
+ CRUSH rule 0 x 600 [143,396,464,444]
+ CRUSH rule 0 x 601 [326,573,873,902]
+ CRUSH rule 0 x 602 [860,281,875,535]
+ CRUSH rule 0 x 603 [709,328,445,349]
+ CRUSH rule 0 x 604 [571,62,814,95]
+ CRUSH rule 0 x 605 [252,739,860,27]
+ CRUSH rule 0 x 606 [339,236,759,842]
+ CRUSH rule 0 x 607 [590,248,759,868]
+ CRUSH rule 0 x 608 [145,635,309,467]
+ CRUSH rule 0 x 609 [973,547,223,79]
+ CRUSH rule 0 x 610 [435,816,961,983]
+ CRUSH rule 0 x 611 [559,283,422,584]
+ CRUSH rule 0 x 612 [273,149,123,576]
+ CRUSH rule 0 x 613 [828,614,642,674]
+ CRUSH rule 0 x 614 [478,748,393,34]
+ CRUSH rule 0 x 615 [392,155,144,326]
+ CRUSH rule 0 x 616 [778,637,452,248]
+ CRUSH rule 0 x 617 [622,713,996,833]
+ CRUSH rule 0 x 618 [149,877,270,329]
+ CRUSH rule 0 x 619 [604,163,656,409]
+ CRUSH rule 0 x 620 [181,23,409,198]
+ CRUSH rule 0 x 621 [735,902,386,237]
+ CRUSH rule 0 x 622 [661,824,717,568]
+ CRUSH rule 0 x 623 [142,121,643,61]
+ CRUSH rule 0 x 624 [360,716,420,398]
+ CRUSH rule 0 x 625 [541,167,385,1]
+ CRUSH rule 0 x 626 [364,431,610,363]
+ CRUSH rule 0 x 627 [458,137,557,410]
+ CRUSH rule 0 x 628 [250,350,556,497]
+ CRUSH rule 0 x 629 [928,160,710,572]
+ CRUSH rule 0 x 630 [243,19,918,556]
+ CRUSH rule 0 x 631 [438,221,574,676]
+ CRUSH rule 0 x 632 [797,368,247,5]
+ CRUSH rule 0 x 633 [993,749,525,485]
+ CRUSH rule 0 x 634 [239,351,633,299]
+ CRUSH rule 0 x 635 [640,965,25,961]
+ CRUSH rule 0 x 636 [173,290,297,991]
+ CRUSH rule 0 x 637 [0,918,98,108]
+ CRUSH rule 0 x 638 [702,235,424,900]
+ CRUSH rule 0 x 639 [475,687,31,785]
+ CRUSH rule 0 x 640 [31,664,399,677]
+ CRUSH rule 0 x 641 [296,473,108,963]
+ CRUSH rule 0 x 642 [894,273,427,606]
+ CRUSH rule 0 x 643 [117,111,732,191]
+ CRUSH rule 0 x 644 [438,336,327,512]
+ CRUSH rule 0 x 645 [982,702,351,573]
+ CRUSH rule 0 x 646 [334,804,146,842]
+ CRUSH rule 0 x 647 [933,787,185,334]
+ CRUSH rule 0 x 648 [22,444,400,862]
+ CRUSH rule 0 x 649 [503,229,213,460]
+ CRUSH rule 0 x 650 [328,659,420,443]
+ CRUSH rule 0 x 651 [3,880,823,123]
+ CRUSH rule 0 x 652 [495,977,563,733]
+ CRUSH rule 0 x 653 [185,718,804,280]
+ CRUSH rule 0 x 654 [130,528,380,81]
+ CRUSH rule 0 x 655 [560,872,454,504]
+ CRUSH rule 0 x 656 [219,885,178,981]
+ CRUSH rule 0 x 657 [233,684,813,490]
+ CRUSH rule 0 x 658 [778,6,756,380]
+ CRUSH rule 0 x 659 [240,663,306,540]
+ CRUSH rule 0 x 660 [244,855,196,147]
+ CRUSH rule 0 x 661 [184,270,128,398]
+ CRUSH rule 0 x 662 [65,883,921,438]
+ CRUSH rule 0 x 663 [323,721,594,812]
+ CRUSH rule 0 x 664 [865,113,512,51]
+ CRUSH rule 0 x 665 [420,850,591,475]
+ CRUSH rule 0 x 666 [319,767,246,3]
+ CRUSH rule 0 x 667 [875,39,343,100]
+ CRUSH rule 0 x 668 [331,122,263,599]
+ CRUSH rule 0 x 669 [915,521,402,747]
+ CRUSH rule 0 x 670 [845,659,943,447]
+ CRUSH rule 0 x 671 [108,634,527,363]
+ CRUSH rule 0 x 672 [578,216,110,589]
+ CRUSH rule 0 x 673 [442,74,579,797]
+ CRUSH rule 0 x 674 [588,364,281,308]
+ CRUSH rule 0 x 675 [489,698,744,671]
+ CRUSH rule 0 x 676 [928,911,40,180]
+ CRUSH rule 0 x 677 [399,269,692,131]
+ CRUSH rule 0 x 678 [546,752,544,155]
+ CRUSH rule 0 x 679 [988,25,275,433]
+ CRUSH rule 0 x 680 [335,963,382,486]
+ CRUSH rule 0 x 681 [690,462,623,466]
+ CRUSH rule 0 x 682 [196,588,154,257]
+ CRUSH rule 0 x 683 [627,25,421,160]
+ CRUSH rule 0 x 684 [38,804,592,158]
+ CRUSH rule 0 x 685 [841,368,548,362]
+ CRUSH rule 0 x 686 [336,287,525,440]
+ CRUSH rule 0 x 687 [20,682,924,653]
+ CRUSH rule 0 x 688 [463,371,780,556]
+ CRUSH rule 0 x 689 [569,250,78,816]
+ CRUSH rule 0 x 690 [551,144,587,263]
+ CRUSH rule 0 x 691 [766,464,446,533]
+ CRUSH rule 0 x 692 [739,634,18,245]
+ CRUSH rule 0 x 693 [339,297,118,330]
+ CRUSH rule 0 x 694 [405,26,830,181]
+ CRUSH rule 0 x 695 [622,576,597,535]
+ CRUSH rule 0 x 696 [558,902,689,13]
+ CRUSH rule 0 x 697 [818,222,406,691]
+ CRUSH rule 0 x 698 [178,48,402,233]
+ CRUSH rule 0 x 699 [450,244,180,919]
+ CRUSH rule 0 x 700 [502,771,987,706]
+ CRUSH rule 0 x 701 [4,612,782,216]
+ CRUSH rule 0 x 702 [177,630,232,923]
+ CRUSH rule 0 x 703 [354,178,389,393]
+ CRUSH rule 0 x 704 [646,601,156,171]
+ CRUSH rule 0 x 705 [921,401,890,265]
+ CRUSH rule 0 x 706 [652,877,562,452]
+ CRUSH rule 0 x 707 [345,745,67,716]
+ CRUSH rule 0 x 708 [333,607,180,469]
+ CRUSH rule 0 x 709 [45,187,302,115]
+ CRUSH rule 0 x 710 [94,855,43,199]
+ CRUSH rule 0 x 711 [227,653,731,150]
+ CRUSH rule 0 x 712 [398,953,136,870]
+ CRUSH rule 0 x 713 [116,800,503,662]
+ CRUSH rule 0 x 714 [111,629,866,709]
+ CRUSH rule 0 x 715 [531,291,486,382]
+ CRUSH rule 0 x 716 [169,541,291,42]
+ CRUSH rule 0 x 717 [417,446,994,894]
+ CRUSH rule 0 x 718 [992,383,298,844]
+ CRUSH rule 0 x 719 [936,674,324,759]
+ CRUSH rule 0 x 720 [370,188,174,464]
+ CRUSH rule 0 x 721 [320,859,278,259]
+ CRUSH rule 0 x 722 [7,2,673,129]
+ CRUSH rule 0 x 723 [270,553,831,662]
+ CRUSH rule 0 x 724 [666,822,708,895]
+ CRUSH rule 0 x 725 [794,406,875,459]
+ CRUSH rule 0 x 726 [420,556,341,292]
+ CRUSH rule 0 x 727 [561,461,129,635]
+ CRUSH rule 0 x 728 [951,330,196,756]
+ CRUSH rule 0 x 729 [656,644,436,591]
+ CRUSH rule 0 x 730 [3,558,629,184]
+ CRUSH rule 0 x 731 [852,89,75,735]
+ CRUSH rule 0 x 732 [983,840,869,976]
+ CRUSH rule 0 x 733 [285,396,388,122]
+ CRUSH rule 0 x 734 [125,510,402,640]
+ CRUSH rule 0 x 735 [417,773,686,504]
+ CRUSH rule 0 x 736 [749,396,632,550]
+ CRUSH rule 0 x 737 [644,991,946,135]
+ CRUSH rule 0 x 738 [449,683,290,220]
+ CRUSH rule 0 x 739 [341,220,641,454]
+ CRUSH rule 0 x 740 [874,524,674,650]
+ CRUSH rule 0 x 741 [189,472,712,798]
+ CRUSH rule 0 x 742 [912,581,114,117]
+ CRUSH rule 0 x 743 [654,914,425,441]
+ CRUSH rule 0 x 744 [725,295,579,377]
+ CRUSH rule 0 x 745 [787,858,850,506]
+ CRUSH rule 0 x 746 [757,848,704,30]
+ CRUSH rule 0 x 747 [700,81,867,681]
+ CRUSH rule 0 x 748 [557,436,238,664]
+ CRUSH rule 0 x 749 [772,622,337,42]
+ CRUSH rule 0 x 750 [946,97,376,677]
+ CRUSH rule 0 x 751 [996,618,343,911]
+ CRUSH rule 0 x 752 [746,887,695,868]
+ CRUSH rule 0 x 753 [741,14,463,479]
+ CRUSH rule 0 x 754 [648,349,333,355]
+ CRUSH rule 0 x 755 [157,460,466,187]
+ CRUSH rule 0 x 756 [416,97,197,497]
+ CRUSH rule 0 x 757 [599,839,776,410]
+ CRUSH rule 0 x 758 [994,218,620,256]
+ CRUSH rule 0 x 759 [959,682,514,745]
+ CRUSH rule 0 x 760 [518,943,215,83]
+ CRUSH rule 0 x 761 [285,849,420,324]
+ CRUSH rule 0 x 762 [591,313,41,335]
+ CRUSH rule 0 x 763 [908,411,200,740]
+ CRUSH rule 0 x 764 [787,234,894,485]
+ CRUSH rule 0 x 765 [327,921,882,393]
+ CRUSH rule 0 x 766 [84,161,878,704]
+ CRUSH rule 0 x 767 [370,895,702,701]
+ CRUSH rule 0 x 768 [826,760,879,864]
+ CRUSH rule 0 x 769 [67,768,663,735]
+ CRUSH rule 0 x 770 [593,909,482,259]
+ CRUSH rule 0 x 771 [309,935,121,578]
+ CRUSH rule 0 x 772 [12,125,797,301]
+ CRUSH rule 0 x 773 [253,466,820,549]
+ CRUSH rule 0 x 774 [164,390,705,109]
+ CRUSH rule 0 x 775 [703,47,43,973]
+ CRUSH rule 0 x 776 [728,231,80,916]
+ CRUSH rule 0 x 777 [981,621,568,729]
+ CRUSH rule 0 x 778 [411,456,544,597]
+ CRUSH rule 0 x 779 [346,121,519,921]
+ CRUSH rule 0 x 780 [476,39,288,381]
+ CRUSH rule 0 x 781 [10,130,585,844]
+ CRUSH rule 0 x 782 [462,246,581,902]
+ CRUSH rule 0 x 783 [580,373,153,775]
+ CRUSH rule 0 x 784 [413,113,978,990]
+ CRUSH rule 0 x 785 [341,856,332,354]
+ CRUSH rule 0 x 786 [411,140,313,393]
+ CRUSH rule 0 x 787 [605,522,211,813]
+ CRUSH rule 0 x 788 [226,545,35,142]
+ CRUSH rule 0 x 789 [545,320,414,702]
+ CRUSH rule 0 x 790 [414,748,816,327]
+ CRUSH rule 0 x 791 [660,906,406,697]
+ CRUSH rule 0 x 792 [287,392,514,204]
+ CRUSH rule 0 x 793 [631,133,850,713]
+ CRUSH rule 0 x 794 [931,517,543,210]
+ CRUSH rule 0 x 795 [551,962,477,948]
+ CRUSH rule 0 x 796 [814,4,95,27]
+ CRUSH rule 0 x 797 [64,201,299,734]
+ CRUSH rule 0 x 798 [422,530,114,431]
+ CRUSH rule 0 x 799 [824,32,679,562]
+ CRUSH rule 0 x 800 [862,623,489,637]
+ CRUSH rule 0 x 801 [145,550,329,324]
+ CRUSH rule 0 x 802 [570,19,847,308]
+ CRUSH rule 0 x 803 [151,812,662,358]
+ CRUSH rule 0 x 804 [467,93,264,863]
+ CRUSH rule 0 x 805 [621,223,938,809]
+ CRUSH rule 0 x 806 [898,957,805,430]
+ CRUSH rule 0 x 807 [354,531,422,159]
+ CRUSH rule 0 x 808 [7,96,76,897]
+ CRUSH rule 0 x 809 [70,734,719,56]
+ CRUSH rule 0 x 810 [701,18,972,327]
+ CRUSH rule 0 x 811 [248,547,103,728]
+ CRUSH rule 0 x 812 [230,576,821,566]
+ CRUSH rule 0 x 813 [805,114,683,629]
+ CRUSH rule 0 x 814 [54,619,973,741]
+ CRUSH rule 0 x 815 [679,412,613,132]
+ CRUSH rule 0 x 816 [919,448,826,414]
+ CRUSH rule 0 x 817 [765,830,436,521]
+ CRUSH rule 0 x 818 [415,566,644,687]
+ CRUSH rule 0 x 819 [721,319,865,750]
+ CRUSH rule 0 x 820 [218,301,333,190]
+ CRUSH rule 0 x 821 [185,795,680,953]
+ CRUSH rule 0 x 822 [356,261,54,522]
+ CRUSH rule 0 x 823 [220,281,549,456]
+ CRUSH rule 0 x 824 [292,809,887,74]
+ CRUSH rule 0 x 825 [949,778,101,311]
+ CRUSH rule 0 x 826 [767,818,833,927]
+ CRUSH rule 0 x 827 [631,83,406,635]
+ CRUSH rule 0 x 828 [288,986,445,26]
+ CRUSH rule 0 x 829 [990,667,915,694]
+ CRUSH rule 0 x 830 [152,571,778,505]
+ CRUSH rule 0 x 831 [814,563,630,97]
+ CRUSH rule 0 x 832 [235,641,616,110]
+ CRUSH rule 0 x 833 [657,565,922,140]
+ CRUSH rule 0 x 834 [907,231,644,13]
+ CRUSH rule 0 x 835 [784,262,771,264]
+ CRUSH rule 0 x 836 [951,158,366,710]
+ CRUSH rule 0 x 837 [556,498,334,633]
+ CRUSH rule 0 x 838 [329,274,964,547]
+ CRUSH rule 0 x 839 [568,209,939,364]
+ CRUSH rule 0 x 840 [45,579,842,70]
+ CRUSH rule 0 x 841 [652,702,24,605]
+ CRUSH rule 0 x 842 [629,984,314,895]
+ CRUSH rule 0 x 843 [799,690,688,648]
+ CRUSH rule 0 x 844 [694,600,534,700]
+ CRUSH rule 0 x 845 [332,30,179,93]
+ CRUSH rule 0 x 846 [452,251,712,719]
+ CRUSH rule 0 x 847 [399,681,847,739]
+ CRUSH rule 0 x 848 [303,138,440,346]
+ CRUSH rule 0 x 849 [666,346,708,873]
+ CRUSH rule 0 x 850 [644,511,345,844]
+ CRUSH rule 0 x 851 [527,546,737,425]
+ CRUSH rule 0 x 852 [31,809,94,618]
+ CRUSH rule 0 x 853 [483,330,869,184]
+ CRUSH rule 0 x 854 [697,953,968,143]
+ CRUSH rule 0 x 855 [837,996,239,621]
+ CRUSH rule 0 x 856 [712,40,547,430]
+ CRUSH rule 0 x 857 [77,984,576,551]
+ CRUSH rule 0 x 858 [412,384,841,465]
+ CRUSH rule 0 x 859 [173,760,26,300]
+ CRUSH rule 0 x 860 [776,429,328,917]
+ CRUSH rule 0 x 861 [705,405,477,50]
+ CRUSH rule 0 x 862 [809,44,788,938]
+ CRUSH rule 0 x 863 [349,496,963,178]
+ CRUSH rule 0 x 864 [717,858,101,239]
+ CRUSH rule 0 x 865 [857,603,586,262]
+ CRUSH rule 0 x 866 [394,304,71,96]
+ CRUSH rule 0 x 867 [640,773,663,974]
+ CRUSH rule 0 x 868 [613,950,712,663]
+ CRUSH rule 0 x 869 [973,889,524,22]
+ CRUSH rule 0 x 870 [505,35,386,498]
+ CRUSH rule 0 x 871 [239,264,262,773]
+ CRUSH rule 0 x 872 [21,767,456,748]
+ CRUSH rule 0 x 873 [954,666,980,264]
+ CRUSH rule 0 x 874 [54,510,947,1]
+ CRUSH rule 0 x 875 [809,418,452,462]
+ CRUSH rule 0 x 876 [483,457,61,248]
+ CRUSH rule 0 x 877 [542,531,952,939]
+ CRUSH rule 0 x 878 [217,674,857,644]
+ CRUSH rule 0 x 879 [999,475,134,250]
+ CRUSH rule 0 x 880 [678,573,935,385]
+ CRUSH rule 0 x 881 [394,835,789,802]
+ CRUSH rule 0 x 882 [467,382,353,56]
+ CRUSH rule 0 x 883 [802,744,237,337]
+ CRUSH rule 0 x 884 [653,660,638,700]
+ CRUSH rule 0 x 885 [898,704,307,445]
+ CRUSH rule 0 x 886 [434,357,938,641]
+ CRUSH rule 0 x 887 [297,226,711,428]
+ CRUSH rule 0 x 888 [863,324,443,213]
+ CRUSH rule 0 x 889 [105,102,308,163]
+ CRUSH rule 0 x 890 [550,248,606,704]
+ CRUSH rule 0 x 891 [575,928,880,891]
+ CRUSH rule 0 x 892 [259,862,133,271]
+ CRUSH rule 0 x 893 [902,880,543,542]
+ CRUSH rule 0 x 894 [180,169,916,43]
+ CRUSH rule 0 x 895 [725,849,182,129]
+ CRUSH rule 0 x 896 [951,34,874,537]
+ CRUSH rule 0 x 897 [810,352,73,939]
+ CRUSH rule 0 x 898 [979,433,719,411]
+ CRUSH rule 0 x 899 [685,668,534,932]
+ CRUSH rule 0 x 900 [530,978,41,894]
+ CRUSH rule 0 x 901 [740,107,336,175]
+ CRUSH rule 0 x 902 [800,743,693,310]
+ CRUSH rule 0 x 903 [230,267,842,266]
+ CRUSH rule 0 x 904 [346,949,460,973]
+ CRUSH rule 0 x 905 [530,397,619,958]
+ CRUSH rule 0 x 906 [80,426,138,672]
+ CRUSH rule 0 x 907 [365,968,475,297]
+ CRUSH rule 0 x 908 [204,832,742,809]
+ CRUSH rule 0 x 909 [883,989,146,959]
+ CRUSH rule 0 x 910 [549,593,249,853]
+ CRUSH rule 0 x 911 [325,847,352,214]
+ CRUSH rule 0 x 912 [874,888,582,796]
+ CRUSH rule 0 x 913 [331,463,342,574]
+ CRUSH rule 0 x 914 [836,468,601,732]
+ CRUSH rule 0 x 915 [245,228,100,661]
+ CRUSH rule 0 x 916 [77,967,364,435]
+ CRUSH rule 0 x 917 [239,60,866,221]
+ CRUSH rule 0 x 918 [988,115,922,80]
+ CRUSH rule 0 x 919 [783,139,696,1]
+ CRUSH rule 0 x 920 [623,408,685,953]
+ CRUSH rule 0 x 921 [105,799,144,90]
+ CRUSH rule 0 x 922 [887,505,652,348]
+ CRUSH rule 0 x 923 [223,318,552,458]
+ CRUSH rule 0 x 924 [25,778,366,333]
+ CRUSH rule 0 x 925 [912,601,297,682]
+ CRUSH rule 0 x 926 [968,133,132,144]
+ CRUSH rule 0 x 927 [277,724,214,988]
+ CRUSH rule 0 x 928 [554,203,658,789]
+ CRUSH rule 0 x 929 [761,802,367,528]
+ CRUSH rule 0 x 930 [814,61,788,736]
+ CRUSH rule 0 x 931 [29,193,61,41]
+ CRUSH rule 0 x 932 [446,198,862,534]
+ CRUSH rule 0 x 933 [352,742,216,321]
+ CRUSH rule 0 x 934 [730,2,332,631]
+ CRUSH rule 0 x 935 [731,23,736,79]
+ CRUSH rule 0 x 936 [322,975,20,904]
+ CRUSH rule 0 x 937 [822,221,841,161]
+ CRUSH rule 0 x 938 [557,850,66,630]
+ CRUSH rule 0 x 939 [150,11,971,371]
+ CRUSH rule 0 x 940 [638,398,169,616]
+ CRUSH rule 0 x 941 [730,342,929,577]
+ CRUSH rule 0 x 942 [62,292,166,814]
+ CRUSH rule 0 x 943 [165,314,519,548]
+ CRUSH rule 0 x 944 [199,625,766,176]
+ CRUSH rule 0 x 945 [946,999,699,303]
+ CRUSH rule 0 x 946 [595,93,852,142]
+ CRUSH rule 0 x 947 [800,582,356,93]
+ CRUSH rule 0 x 948 [132,551,139,920]
+ CRUSH rule 0 x 949 [792,920,466,380]
+ CRUSH rule 0 x 950 [111,345,176,543]
+ CRUSH rule 0 x 951 [414,619,648,655]
+ CRUSH rule 0 x 952 [775,469,500,356]
+ CRUSH rule 0 x 953 [349,1,5,251]
+ CRUSH rule 0 x 954 [570,940,410,249]
+ CRUSH rule 0 x 955 [729,774,823,800]
+ CRUSH rule 0 x 956 [519,141,575,625]
+ CRUSH rule 0 x 957 [242,709,611,97]
+ CRUSH rule 0 x 958 [84,217,227,253]
+ CRUSH rule 0 x 959 [270,413,918,789]
+ CRUSH rule 0 x 960 [458,192,307,279]
+ CRUSH rule 0 x 961 [981,388,777,546]
+ CRUSH rule 0 x 962 [623,834,277,134]
+ CRUSH rule 0 x 963 [291,167,714,468]
+ CRUSH rule 0 x 964 [28,156,788,127]
+ CRUSH rule 0 x 965 [675,557,290,517]
+ CRUSH rule 0 x 966 [836,306,946,283]
+ CRUSH rule 0 x 967 [966,386,735,837]
+ CRUSH rule 0 x 968 [864,756,690,121]
+ CRUSH rule 0 x 969 [729,625,480,769]
+ CRUSH rule 0 x 970 [800,362,646,582]
+ CRUSH rule 0 x 971 [737,381,153,684]
+ CRUSH rule 0 x 972 [952,245,720,884]
+ CRUSH rule 0 x 973 [356,455,579,857]
+ CRUSH rule 0 x 974 [545,758,586,596]
+ CRUSH rule 0 x 975 [336,191,202,146]
+ CRUSH rule 0 x 976 [446,208,757,620]
+ CRUSH rule 0 x 977 [202,896,196,956]
+ CRUSH rule 0 x 978 [612,324,996,225]
+ CRUSH rule 0 x 979 [843,457,675,650]
+ CRUSH rule 0 x 980 [60,914,881,626]
+ CRUSH rule 0 x 981 [702,749,937,153]
+ CRUSH rule 0 x 982 [298,928,738,167]
+ CRUSH rule 0 x 983 [723,572,395,358]
+ CRUSH rule 0 x 984 [723,864,804,935]
+ CRUSH rule 0 x 985 [945,459,868,211]
+ CRUSH rule 0 x 986 [772,664,535,169]
+ CRUSH rule 0 x 987 [88,324,312,843]
+ CRUSH rule 0 x 988 [522,927,131,996]
+ CRUSH rule 0 x 989 [578,332,208,605]
+ CRUSH rule 0 x 990 [638,228,414,311]
+ CRUSH rule 0 x 991 [530,221,451,422]
+ CRUSH rule 0 x 992 [925,705,275,81]
+ CRUSH rule 0 x 993 [991,301,43,469]
+ CRUSH rule 0 x 994 [276,51,868,683]
+ CRUSH rule 0 x 995 [288,836,753,790]
+ CRUSH rule 0 x 996 [887,983,252,686]
+ CRUSH rule 0 x 997 [110,924,386,79]
+ CRUSH rule 0 x 998 [435,830,485,853]
+ CRUSH rule 0 x 999 [876,738,357,913]
+ CRUSH rule 0 x 1000 [178,963,638,430]
+ CRUSH rule 0 x 1001 [99,519,66,759]
+ CRUSH rule 0 x 1002 [515,534,468,866]
+ CRUSH rule 0 x 1003 [104,611,937,698]
+ CRUSH rule 0 x 1004 [269,638,724,375]
+ CRUSH rule 0 x 1005 [369,223,309,409]
+ CRUSH rule 0 x 1006 [40,107,69,275]
+ CRUSH rule 0 x 1007 [978,111,416,758]
+ CRUSH rule 0 x 1008 [965,956,624,832]
+ CRUSH rule 0 x 1009 [598,476,356,695]
+ CRUSH rule 0 x 1010 [767,523,239,517]
+ CRUSH rule 0 x 1011 [289,871,207,576]
+ CRUSH rule 0 x 1012 [128,28,370,31]
+ CRUSH rule 0 x 1013 [979,765,660,812]
+ CRUSH rule 0 x 1014 [979,948,513,88]
+ CRUSH rule 0 x 1015 [277,790,396,672]
+ CRUSH rule 0 x 1016 [262,73,128,886]
+ CRUSH rule 0 x 1017 [150,269,61,499]
+ CRUSH rule 0 x 1018 [555,829,554,944]
+ CRUSH rule 0 x 1019 [513,356,265,446]
+ CRUSH rule 0 x 1020 [158,161,877,704]
+ CRUSH rule 0 x 1021 [915,998,957,285]
+ CRUSH rule 0 x 1022 [967,829,973,640]
+ CRUSH rule 0 x 1023 [488,257,614,859]
+ rule 0 (data) num_rep 4 result size == 4:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604]
+ CRUSH rule 0 x 1 [876,250,334,633,744]
+ CRUSH rule 0 x 2 [292,832,53,392,386]
+ CRUSH rule 0 x 3 [623,387,124,998,749]
+ CRUSH rule 0 x 4 [61,334,710,4,994]
+ CRUSH rule 0 x 5 [946,557,713,664,141]
+ CRUSH rule 0 x 6 [576,668,212,163,732]
+ CRUSH rule 0 x 7 [645,753,906,393,341]
+ CRUSH rule 0 x 8 [243,6,863,781,211]
+ CRUSH rule 0 x 9 [22,578,251,410,297]
+ CRUSH rule 0 x 10 [758,828,360,477,821]
+ CRUSH rule 0 x 11 [769,120,124,527,119]
+ CRUSH rule 0 x 12 [780,364,689,755,675]
+ CRUSH rule 0 x 13 [557,18,351,719,742]
+ CRUSH rule 0 x 14 [59,561,249,461,971]
+ CRUSH rule 0 x 15 [718,928,993,21,76]
+ CRUSH rule 0 x 16 [673,632,841,954,788]
+ CRUSH rule 0 x 17 [648,43,560,514,142]
+ CRUSH rule 0 x 18 [654,219,181,568,381]
+ CRUSH rule 0 x 19 [850,545,377,848,863]
+ CRUSH rule 0 x 20 [717,785,974,5,225]
+ CRUSH rule 0 x 21 [420,57,519,306,312]
+ CRUSH rule 0 x 22 [503,998,193,821,634]
+ CRUSH rule 0 x 23 [411,663,168,110,899]
+ CRUSH rule 0 x 24 [266,861,353,1,456]
+ CRUSH rule 0 x 25 [760,483,818,600,509]
+ CRUSH rule 0 x 26 [903,24,573,718,112]
+ CRUSH rule 0 x 27 [946,188,289,510,687]
+ CRUSH rule 0 x 28 [69,312,73,198,256]
+ CRUSH rule 0 x 29 [844,883,337,628,496]
+ CRUSH rule 0 x 30 [621,18,613,794,910]
+ CRUSH rule 0 x 31 [784,943,814,539,962]
+ CRUSH rule 0 x 32 [173,374,369,972,315]
+ CRUSH rule 0 x 33 [698,336,357,966,582]
+ CRUSH rule 0 x 34 [168,836,210,798,904]
+ CRUSH rule 0 x 35 [274,509,534,818,912]
+ CRUSH rule 0 x 36 [318,215,153,628,87]
+ CRUSH rule 0 x 37 [173,604,109,935,203]
+ CRUSH rule 0 x 38 [708,444,683,604,722]
+ CRUSH rule 0 x 39 [662,198,417,680,226]
+ CRUSH rule 0 x 40 [620,801,414,78,560]
+ CRUSH rule 0 x 41 [811,264,177,127,148]
+ CRUSH rule 0 x 42 [863,179,527,660,133]
+ CRUSH rule 0 x 43 [686,822,988,228,791]
+ CRUSH rule 0 x 44 [396,222,46,841,536]
+ CRUSH rule 0 x 45 [991,694,253,142,54]
+ CRUSH rule 0 x 46 [420,909,184,285,508]
+ CRUSH rule 0 x 47 [467,211,605,207,241]
+ CRUSH rule 0 x 48 [955,329,368,168,698]
+ CRUSH rule 0 x 49 [974,891,931,29,813]
+ CRUSH rule 0 x 50 [870,441,691,823,761]
+ CRUSH rule 0 x 51 [182,930,25,936,97]
+ CRUSH rule 0 x 52 [704,812,894,794,481]
+ CRUSH rule 0 x 53 [185,713,631,280,345]
+ CRUSH rule 0 x 54 [270,441,100,82,983]
+ CRUSH rule 0 x 55 [895,734,958,793,651]
+ CRUSH rule 0 x 56 [564,963,683,324,40]
+ CRUSH rule 0 x 57 [738,130,208,973,498]
+ CRUSH rule 0 x 58 [524,113,806,903,531]
+ CRUSH rule 0 x 59 [408,337,668,529,34]
+ CRUSH rule 0 x 60 [228,790,857,309,616]
+ CRUSH rule 0 x 61 [154,843,717,467,883]
+ CRUSH rule 0 x 62 [594,811,549,276,693]
+ CRUSH rule 0 x 63 [646,67,884,925,941]
+ CRUSH rule 0 x 64 [175,542,155,837,594]
+ CRUSH rule 0 x 65 [745,619,131,867,269]
+ CRUSH rule 0 x 66 [275,468,23,35,328]
+ CRUSH rule 0 x 67 [246,958,524,493,636]
+ CRUSH rule 0 x 68 [711,473,403,228,835]
+ CRUSH rule 0 x 69 [493,924,850,939,950]
+ CRUSH rule 0 x 70 [30,499,644,33,804]
+ CRUSH rule 0 x 71 [984,883,574,716,575]
+ CRUSH rule 0 x 72 [71,286,942,363,628]
+ CRUSH rule 0 x 73 [922,618,3,371,464]
+ CRUSH rule 0 x 74 [629,414,185,573,678]
+ CRUSH rule 0 x 75 [222,20,174,820,312]
+ CRUSH rule 0 x 76 [262,366,339,290,718]
+ CRUSH rule 0 x 77 [638,469,992,280,773]
+ CRUSH rule 0 x 78 [324,511,788,7,308]
+ CRUSH rule 0 x 79 [577,990,64,94,447]
+ CRUSH rule 0 x 80 [501,95,278,903,631]
+ CRUSH rule 0 x 81 [506,812,9,698,173]
+ CRUSH rule 0 x 82 [222,145,80,785,835]
+ CRUSH rule 0 x 83 [71,634,61,91,856]
+ CRUSH rule 0 x 84 [49,761,773,368,318]
+ CRUSH rule 0 x 85 [985,896,708,861,325]
+ CRUSH rule 0 x 86 [537,745,93,524,466]
+ CRUSH rule 0 x 87 [997,317,463,626,685]
+ CRUSH rule 0 x 88 [957,350,890,857,375]
+ CRUSH rule 0 x 89 [399,730,148,314,159]
+ CRUSH rule 0 x 90 [943,706,683,267,579]
+ CRUSH rule 0 x 91 [22,368,149,928,140]
+ CRUSH rule 0 x 92 [532,424,426,773,623]
+ CRUSH rule 0 x 93 [218,489,405,681,549]
+ CRUSH rule 0 x 94 [181,96,102,515,776]
+ CRUSH rule 0 x 95 [343,957,820,139,334]
+ CRUSH rule 0 x 96 [861,270,87,797,0]
+ CRUSH rule 0 x 97 [459,706,45,328,274]
+ CRUSH rule 0 x 98 [327,867,353,948,728]
+ CRUSH rule 0 x 99 [974,133,468,906,235]
+ CRUSH rule 0 x 100 [32,445,547,371,960]
+ CRUSH rule 0 x 101 [142,90,337,950,970]
+ CRUSH rule 0 x 102 [172,129,139,22,403]
+ CRUSH rule 0 x 103 [630,47,161,356,911]
+ CRUSH rule 0 x 104 [758,133,278,11,947]
+ CRUSH rule 0 x 105 [843,604,47,33,401]
+ CRUSH rule 0 x 106 [28,681,193,679,990]
+ CRUSH rule 0 x 107 [74,320,85,819,315]
+ CRUSH rule 0 x 108 [875,593,575,517,107]
+ CRUSH rule 0 x 109 [411,985,811,720,198]
+ CRUSH rule 0 x 110 [440,774,799,660,715]
+ CRUSH rule 0 x 111 [405,742,276,359,936]
+ CRUSH rule 0 x 112 [143,181,922,545,185]
+ CRUSH rule 0 x 113 [153,846,160,903,789]
+ CRUSH rule 0 x 114 [804,892,939,20,312]
+ CRUSH rule 0 x 115 [588,508,958,580,232]
+ CRUSH rule 0 x 116 [327,148,637,486,712]
+ CRUSH rule 0 x 117 [95,594,989,131,714]
+ CRUSH rule 0 x 118 [80,957,897,239,359]
+ CRUSH rule 0 x 119 [386,932,951,768,679]
+ CRUSH rule 0 x 120 [366,312,653,936,71]
+ CRUSH rule 0 x 121 [129,154,847,16,471]
+ CRUSH rule 0 x 122 [873,1,110,939,90]
+ CRUSH rule 0 x 123 [533,415,789,600,713]
+ CRUSH rule 0 x 124 [461,691,898,723,957]
+ CRUSH rule 0 x 125 [342,599,830,402,615]
+ CRUSH rule 0 x 126 [819,781,822,548,279]
+ CRUSH rule 0 x 127 [437,893,585,707,353]
+ CRUSH rule 0 x 128 [679,994,982,550,991]
+ CRUSH rule 0 x 129 [380,685,947,302,698]
+ CRUSH rule 0 x 130 [992,52,466,867,998]
+ CRUSH rule 0 x 131 [469,90,208,599,829]
+ CRUSH rule 0 x 132 [571,250,316,535,54]
+ CRUSH rule 0 x 133 [964,728,329,902,108]
+ CRUSH rule 0 x 134 [999,19,716,963,323]
+ CRUSH rule 0 x 135 [634,101,52,938,413]
+ CRUSH rule 0 x 136 [114,889,692,768,694]
+ CRUSH rule 0 x 137 [839,8,959,280,922]
+ CRUSH rule 0 x 138 [967,949,138,451,292]
+ CRUSH rule 0 x 139 [308,711,736,247,632]
+ CRUSH rule 0 x 140 [764,936,926,55,331]
+ CRUSH rule 0 x 141 [423,302,112,216,603]
+ CRUSH rule 0 x 142 [252,821,715,340,635]
+ CRUSH rule 0 x 143 [33,808,518,477,325]
+ CRUSH rule 0 x 144 [472,88,969,162,401]
+ CRUSH rule 0 x 145 [242,208,252,604,266]
+ CRUSH rule 0 x 146 [290,70,570,384,934]
+ CRUSH rule 0 x 147 [447,352,657,493,467]
+ CRUSH rule 0 x 148 [212,644,432,658,109]
+ CRUSH rule 0 x 149 [9,775,87,35,260]
+ CRUSH rule 0 x 150 [166,456,582,144,324]
+ CRUSH rule 0 x 151 [811,875,307,20,782]
+ CRUSH rule 0 x 152 [449,617,223,9,182]
+ CRUSH rule 0 x 153 [523,537,695,627,959]
+ CRUSH rule 0 x 154 [208,559,874,597,243]
+ CRUSH rule 0 x 155 [569,325,192,296,367]
+ CRUSH rule 0 x 156 [488,121,521,213,595]
+ CRUSH rule 0 x 157 [140,723,633,260,487]
+ CRUSH rule 0 x 158 [786,451,320,239,667]
+ CRUSH rule 0 x 159 [134,664,517,821,667]
+ CRUSH rule 0 x 160 [690,112,414,990,183]
+ CRUSH rule 0 x 161 [324,912,397,423,991]
+ CRUSH rule 0 x 162 [748,567,284,183,463]
+ CRUSH rule 0 x 163 [575,499,31,816,749]
+ CRUSH rule 0 x 164 [314,489,308,326,51]
+ CRUSH rule 0 x 165 [116,209,750,53,813]
+ CRUSH rule 0 x 166 [352,706,701,810,718]
+ CRUSH rule 0 x 167 [27,743,174,142,551]
+ CRUSH rule 0 x 168 [953,898,880,660,500]
+ CRUSH rule 0 x 169 [912,147,266,547,331]
+ CRUSH rule 0 x 170 [421,515,828,844,151]
+ CRUSH rule 0 x 171 [488,584,880,964,936]
+ CRUSH rule 0 x 172 [366,443,957,66,162]
+ CRUSH rule 0 x 173 [863,291,625,287,158]
+ CRUSH rule 0 x 174 [263,555,650,410,339]
+ CRUSH rule 0 x 175 [875,961,361,575,33]
+ CRUSH rule 0 x 176 [745,83,701,680,250]
+ CRUSH rule 0 x 177 [128,244,41,123,422]
+ CRUSH rule 0 x 178 [155,41,264,777,314]
+ CRUSH rule 0 x 179 [593,833,202,183,971]
+ CRUSH rule 0 x 180 [154,734,17,831,824]
+ CRUSH rule 0 x 181 [289,675,723,800,166]
+ CRUSH rule 0 x 182 [730,931,560,209,943]
+ CRUSH rule 0 x 183 [639,237,794,815,827]
+ CRUSH rule 0 x 184 [704,312,685,645,691]
+ CRUSH rule 0 x 185 [97,100,762,82,999]
+ CRUSH rule 0 x 186 [26,665,554,215,280]
+ CRUSH rule 0 x 187 [649,14,740,494,402]
+ CRUSH rule 0 x 188 [682,695,590,743,927]
+ CRUSH rule 0 x 189 [325,693,726,51,448]
+ CRUSH rule 0 x 190 [399,933,136,955,57]
+ CRUSH rule 0 x 191 [629,533,17,126,60]
+ CRUSH rule 0 x 192 [503,578,38,492,222]
+ CRUSH rule 0 x 193 [546,333,651,678,823]
+ CRUSH rule 0 x 194 [242,473,58,655,653]
+ CRUSH rule 0 x 195 [625,719,135,81,636]
+ CRUSH rule 0 x 196 [357,114,125,867,250]
+ CRUSH rule 0 x 197 [306,954,453,873,211]
+ CRUSH rule 0 x 198 [863,791,311,911,206]
+ CRUSH rule 0 x 199 [935,906,929,252,893]
+ CRUSH rule 0 x 200 [373,774,229,454,909]
+ CRUSH rule 0 x 201 [659,320,477,313,779]
+ CRUSH rule 0 x 202 [260,433,524,880,223]
+ CRUSH rule 0 x 203 [36,239,675,971,703]
+ CRUSH rule 0 x 204 [92,516,993,728,279]
+ CRUSH rule 0 x 205 [68,395,473,45,683]
+ CRUSH rule 0 x 206 [570,530,642,380,311]
+ CRUSH rule 0 x 207 [834,457,850,917,456]
+ CRUSH rule 0 x 208 [927,484,640,976,803]
+ CRUSH rule 0 x 209 [878,66,58,940,48]
+ CRUSH rule 0 x 210 [572,981,484,29,0]
+ CRUSH rule 0 x 211 [107,597,780,857,895]
+ CRUSH rule 0 x 212 [389,107,838,624,698]
+ CRUSH rule 0 x 213 [497,717,567,728,905]
+ CRUSH rule 0 x 214 [798,65,254,572,32]
+ CRUSH rule 0 x 215 [233,419,283,638,520]
+ CRUSH rule 0 x 216 [494,464,742,523,459]
+ CRUSH rule 0 x 217 [352,396,309,938,66]
+ CRUSH rule 0 x 218 [895,864,988,650,593]
+ CRUSH rule 0 x 219 [222,534,277,242,658]
+ CRUSH rule 0 x 220 [281,19,584,563,858]
+ CRUSH rule 0 x 221 [64,928,963,130,312]
+ CRUSH rule 0 x 222 [40,544,161,199,861]
+ CRUSH rule 0 x 223 [645,556,159,417,46]
+ CRUSH rule 0 x 224 [647,165,957,263,961]
+ CRUSH rule 0 x 225 [219,714,858,747,461]
+ CRUSH rule 0 x 226 [372,511,181,277,695]
+ CRUSH rule 0 x 227 [925,156,714,863,257]
+ CRUSH rule 0 x 228 [682,404,839,263,521]
+ CRUSH rule 0 x 229 [880,838,770,891,236]
+ CRUSH rule 0 x 230 [328,659,916,468,646]
+ CRUSH rule 0 x 231 [320,383,669,109,627]
+ CRUSH rule 0 x 232 [924,846,394,319,43]
+ CRUSH rule 0 x 233 [948,652,575,838,498]
+ CRUSH rule 0 x 234 [484,943,42,575,936]
+ CRUSH rule 0 x 235 [750,65,590,168,870]
+ CRUSH rule 0 x 236 [551,787,490,136,370]
+ CRUSH rule 0 x 237 [390,157,166,251,752]
+ CRUSH rule 0 x 238 [570,6,989,707,514]
+ CRUSH rule 0 x 239 [729,959,376,975,496]
+ CRUSH rule 0 x 240 [981,241,156,767,631]
+ CRUSH rule 0 x 241 [310,816,641,177,996]
+ CRUSH rule 0 x 242 [161,63,642,837,763]
+ CRUSH rule 0 x 243 [180,394,33,683,189]
+ CRUSH rule 0 x 244 [52,174,685,189,78]
+ CRUSH rule 0 x 245 [523,121,915,84,386]
+ CRUSH rule 0 x 246 [362,893,390,487,817]
+ CRUSH rule 0 x 247 [382,184,116,34,143]
+ CRUSH rule 0 x 248 [129,114,852,469,359]
+ CRUSH rule 0 x 249 [159,683,91,856,475]
+ CRUSH rule 0 x 250 [404,945,569,955,228]
+ CRUSH rule 0 x 251 [661,225,738,757,37]
+ CRUSH rule 0 x 252 [961,226,542,103,945]
+ CRUSH rule 0 x 253 [651,97,225,364,189]
+ CRUSH rule 0 x 254 [123,33,741,692,599]
+ CRUSH rule 0 x 255 [314,649,891,855,517]
+ CRUSH rule 0 x 256 [315,215,651,126,470]
+ CRUSH rule 0 x 257 [825,264,867,869,529]
+ CRUSH rule 0 x 258 [624,789,370,723,131]
+ CRUSH rule 0 x 259 [602,542,70,563,947]
+ CRUSH rule 0 x 260 [717,878,43,56,377]
+ CRUSH rule 0 x 261 [145,517,20,903,786]
+ CRUSH rule 0 x 262 [223,1,561,420,229]
+ CRUSH rule 0 x 263 [462,211,405,508,787]
+ CRUSH rule 0 x 264 [654,471,266,662,135]
+ CRUSH rule 0 x 265 [302,794,704,798,659]
+ CRUSH rule 0 x 266 [202,132,884,209,551]
+ CRUSH rule 0 x 267 [282,938,657,113,672]
+ CRUSH rule 0 x 268 [338,309,356,278,928]
+ CRUSH rule 0 x 269 [738,122,266,200,894]
+ CRUSH rule 0 x 270 [707,982,946,196,407]
+ CRUSH rule 0 x 271 [705,432,364,735,512]
+ CRUSH rule 0 x 272 [756,545,942,56,542]
+ CRUSH rule 0 x 273 [197,502,527,721,239]
+ CRUSH rule 0 x 274 [992,44,653,573,527]
+ CRUSH rule 0 x 275 [544,789,170,434,23]
+ CRUSH rule 0 x 276 [658,467,577,268,336]
+ CRUSH rule 0 x 277 [143,490,880,483,928]
+ CRUSH rule 0 x 278 [492,647,355,282,834]
+ CRUSH rule 0 x 279 [517,792,604,987,527]
+ CRUSH rule 0 x 280 [825,740,27,848,514]
+ CRUSH rule 0 x 281 [224,629,120,562,616]
+ CRUSH rule 0 x 282 [298,661,380,416,35]
+ CRUSH rule 0 x 283 [311,606,208,50,913]
+ CRUSH rule 0 x 284 [771,466,371,743,672]
+ CRUSH rule 0 x 285 [693,362,404,676,797]
+ CRUSH rule 0 x 286 [364,477,285,167,270]
+ CRUSH rule 0 x 287 [591,611,828,995,170]
+ CRUSH rule 0 x 288 [965,541,848,796,251]
+ CRUSH rule 0 x 289 [225,551,948,877,219]
+ CRUSH rule 0 x 290 [577,762,777,751,291]
+ CRUSH rule 0 x 291 [160,903,477,381,490]
+ CRUSH rule 0 x 292 [873,598,216,666,222]
+ CRUSH rule 0 x 293 [100,234,874,47,28]
+ CRUSH rule 0 x 294 [285,943,379,520,725]
+ CRUSH rule 0 x 295 [938,262,880,327,687]
+ CRUSH rule 0 x 296 [850,327,86,472,1]
+ CRUSH rule 0 x 297 [951,53,99,558,753]
+ CRUSH rule 0 x 298 [173,336,85,766,910]
+ CRUSH rule 0 x 299 [598,591,315,386,895]
+ CRUSH rule 0 x 300 [531,957,62,459,156]
+ CRUSH rule 0 x 301 [823,628,23,858,629]
+ CRUSH rule 0 x 302 [184,80,780,871,531]
+ CRUSH rule 0 x 303 [521,766,222,830,988]
+ CRUSH rule 0 x 304 [980,127,807,507,555]
+ CRUSH rule 0 x 305 [153,816,22,927,696]
+ CRUSH rule 0 x 306 [423,739,664,753,178]
+ CRUSH rule 0 x 307 [997,557,682,456,479]
+ CRUSH rule 0 x 308 [991,874,534,465,330]
+ CRUSH rule 0 x 309 [860,394,724,858,246]
+ CRUSH rule 0 x 310 [589,818,546,201,94]
+ CRUSH rule 0 x 311 [477,774,225,590,830]
+ CRUSH rule 0 x 312 [887,853,950,354,58]
+ CRUSH rule 0 x 313 [802,646,447,416,557]
+ CRUSH rule 0 x 314 [654,974,229,511,562]
+ CRUSH rule 0 x 315 [767,227,28,740,828]
+ CRUSH rule 0 x 316 [778,83,733,359,858]
+ CRUSH rule 0 x 317 [184,418,642,986,939]
+ CRUSH rule 0 x 318 [525,410,500,543,212]
+ CRUSH rule 0 x 319 [476,724,569,382,409]
+ CRUSH rule 0 x 320 [149,610,697,296,818]
+ CRUSH rule 0 x 321 [710,79,667,671,234]
+ CRUSH rule 0 x 322 [175,275,323,333,744]
+ CRUSH rule 0 x 323 [819,604,638,792,316]
+ CRUSH rule 0 x 324 [16,745,511,439,272]
+ CRUSH rule 0 x 325 [486,400,872,873,251]
+ CRUSH rule 0 x 326 [613,765,207,19,359]
+ CRUSH rule 0 x 327 [125,289,738,408,456]
+ CRUSH rule 0 x 328 [807,383,476,583,645]
+ CRUSH rule 0 x 329 [588,938,599,432,446]
+ CRUSH rule 0 x 330 [932,644,41,611,209]
+ CRUSH rule 0 x 331 [341,953,950,537,578]
+ CRUSH rule 0 x 332 [153,726,459,950,466]
+ CRUSH rule 0 x 333 [745,845,853,860,52]
+ CRUSH rule 0 x 334 [614,751,807,58,396]
+ CRUSH rule 0 x 335 [518,721,221,283,454]
+ CRUSH rule 0 x 336 [389,424,77,309,5]
+ CRUSH rule 0 x 337 [753,508,765,720,221]
+ CRUSH rule 0 x 338 [128,810,490,753,406]
+ CRUSH rule 0 x 339 [430,308,58,751,856]
+ CRUSH rule 0 x 340 [541,44,630,231,289]
+ CRUSH rule 0 x 341 [402,26,631,439,165]
+ CRUSH rule 0 x 342 [982,57,992,461,131]
+ CRUSH rule 0 x 343 [833,412,572,732,107]
+ CRUSH rule 0 x 344 [784,533,792,41,642]
+ CRUSH rule 0 x 345 [546,300,304,691,763]
+ CRUSH rule 0 x 346 [302,420,428,891,357]
+ CRUSH rule 0 x 347 [488,778,101,217,366]
+ CRUSH rule 0 x 348 [903,744,937,718,85]
+ CRUSH rule 0 x 349 [471,547,582,306,600]
+ CRUSH rule 0 x 350 [348,221,823,335,383]
+ CRUSH rule 0 x 351 [961,582,705,346,361]
+ CRUSH rule 0 x 352 [728,137,461,298,36]
+ CRUSH rule 0 x 353 [904,202,184,447,58]
+ CRUSH rule 0 x 354 [345,226,319,256,544]
+ CRUSH rule 0 x 355 [50,430,175,43,187]
+ CRUSH rule 0 x 356 [87,185,55,423,829]
+ CRUSH rule 0 x 357 [762,459,921,473,182]
+ CRUSH rule 0 x 358 [908,25,280,6,808]
+ CRUSH rule 0 x 359 [484,15,132,121,394]
+ CRUSH rule 0 x 360 [173,378,337,702,145]
+ CRUSH rule 0 x 361 [404,577,115,25,56]
+ CRUSH rule 0 x 362 [403,1,422,945,132]
+ CRUSH rule 0 x 363 [639,911,510,162,418]
+ CRUSH rule 0 x 364 [752,689,610,990,665]
+ CRUSH rule 0 x 365 [956,999,212,230,624]
+ CRUSH rule 0 x 366 [860,925,924,763,687]
+ CRUSH rule 0 x 367 [205,609,647,665,969]
+ CRUSH rule 0 x 368 [301,284,810,169,78]
+ CRUSH rule 0 x 369 [452,658,339,217,674]
+ CRUSH rule 0 x 370 [11,467,695,989,394]
+ CRUSH rule 0 x 371 [124,487,55,514,313]
+ CRUSH rule 0 x 372 [253,48,979,846,207]
+ CRUSH rule 0 x 373 [715,605,775,748,227]
+ CRUSH rule 0 x 374 [191,887,920,928,223]
+ CRUSH rule 0 x 375 [711,385,651,665,15]
+ CRUSH rule 0 x 376 [597,818,49,458,415]
+ CRUSH rule 0 x 377 [294,256,933,771,184]
+ CRUSH rule 0 x 378 [34,151,681,707,552]
+ CRUSH rule 0 x 379 [869,136,315,378,813]
+ CRUSH rule 0 x 380 [294,97,575,791,690]
+ CRUSH rule 0 x 381 [119,710,219,827,328]
+ CRUSH rule 0 x 382 [69,631,508,706,697]
+ CRUSH rule 0 x 383 [922,588,589,925,471]
+ CRUSH rule 0 x 384 [221,945,671,117,857]
+ CRUSH rule 0 x 385 [561,737,953,723,658]
+ CRUSH rule 0 x 386 [335,442,788,696,507]
+ CRUSH rule 0 x 387 [514,43,353,88,100]
+ CRUSH rule 0 x 388 [587,89,157,996,915]
+ CRUSH rule 0 x 389 [109,641,255,466,372]
+ CRUSH rule 0 x 390 [925,149,421,489,599]
+ CRUSH rule 0 x 391 [267,87,387,527,768]
+ CRUSH rule 0 x 392 [382,485,370,849,936]
+ CRUSH rule 0 x 393 [425,721,221,753,268]
+ CRUSH rule 0 x 394 [898,18,38,793,173]
+ CRUSH rule 0 x 395 [806,876,269,679,32]
+ CRUSH rule 0 x 396 [790,970,437,449,875]
+ CRUSH rule 0 x 397 [136,363,507,613,11]
+ CRUSH rule 0 x 398 [914,116,558,258,722]
+ CRUSH rule 0 x 399 [261,94,299,202,174]
+ CRUSH rule 0 x 400 [661,197,338,461,977]
+ CRUSH rule 0 x 401 [953,979,287,803,41]
+ CRUSH rule 0 x 402 [738,819,618,522,667]
+ CRUSH rule 0 x 403 [573,238,425,546,130]
+ CRUSH rule 0 x 404 [526,848,790,253,922]
+ CRUSH rule 0 x 405 [582,505,330,334,201]
+ CRUSH rule 0 x 406 [768,324,493,60,186]
+ CRUSH rule 0 x 407 [260,951,437,587,692]
+ CRUSH rule 0 x 408 [657,81,770,734,830]
+ CRUSH rule 0 x 409 [498,89,182,423,672]
+ CRUSH rule 0 x 410 [28,793,737,352,166]
+ CRUSH rule 0 x 411 [684,992,60,659,769]
+ CRUSH rule 0 x 412 [261,958,699,950,165]
+ CRUSH rule 0 x 413 [891,835,297,441,384]
+ CRUSH rule 0 x 414 [127,459,119,965,662]
+ CRUSH rule 0 x 415 [272,540,631,328,609]
+ CRUSH rule 0 x 416 [739,617,115,530,339]
+ CRUSH rule 0 x 417 [106,209,157,878,117]
+ CRUSH rule 0 x 418 [525,441,147,390,320]
+ CRUSH rule 0 x 419 [603,673,615,465,266]
+ CRUSH rule 0 x 420 [988,213,251,226,209]
+ CRUSH rule 0 x 421 [761,521,748,368,923]
+ CRUSH rule 0 x 422 [317,160,924,548,198]
+ CRUSH rule 0 x 423 [137,807,168,472,619]
+ CRUSH rule 0 x 424 [920,37,146,263,598]
+ CRUSH rule 0 x 425 [277,693,285,221,478]
+ CRUSH rule 0 x 426 [485,936,407,854,726]
+ CRUSH rule 0 x 427 [242,515,9,564,174]
+ CRUSH rule 0 x 428 [632,635,26,473,494]
+ CRUSH rule 0 x 429 [641,73,465,127,171]
+ CRUSH rule 0 x 430 [626,585,6,387,881]
+ CRUSH rule 0 x 431 [697,76,753,570,964]
+ CRUSH rule 0 x 432 [590,526,306,283,656]
+ CRUSH rule 0 x 433 [284,387,149,817,886]
+ CRUSH rule 0 x 434 [538,985,79,953,770]
+ CRUSH rule 0 x 435 [30,318,593,635,975]
+ CRUSH rule 0 x 436 [164,919,851,693,0]
+ CRUSH rule 0 x 437 [322,212,163,606,302]
+ CRUSH rule 0 x 438 [142,392,85,594,376]
+ CRUSH rule 0 x 439 [119,370,68,443,997]
+ CRUSH rule 0 x 440 [333,403,187,863,475]
+ CRUSH rule 0 x 441 [477,727,906,145,429]
+ CRUSH rule 0 x 442 [274,590,933,244,434]
+ CRUSH rule 0 x 443 [983,748,574,718,700]
+ CRUSH rule 0 x 444 [536,509,431,146,170]
+ CRUSH rule 0 x 445 [485,482,528,209,964]
+ CRUSH rule 0 x 446 [345,634,42,294,711]
+ CRUSH rule 0 x 447 [61,845,767,600,321]
+ CRUSH rule 0 x 448 [333,232,292,846,364]
+ CRUSH rule 0 x 449 [680,16,484,670,851]
+ CRUSH rule 0 x 450 [235,214,79,423,96]
+ CRUSH rule 0 x 451 [961,468,333,640,823]
+ CRUSH rule 0 x 452 [525,479,153,528,570]
+ CRUSH rule 0 x 453 [138,466,302,86,249]
+ CRUSH rule 0 x 454 [137,625,215,402,389]
+ CRUSH rule 0 x 455 [173,150,997,16,846]
+ CRUSH rule 0 x 456 [235,226,238,258,347]
+ CRUSH rule 0 x 457 [450,577,253,413,717]
+ CRUSH rule 0 x 458 [195,537,91,814,351]
+ CRUSH rule 0 x 459 [381,555,312,573,915]
+ CRUSH rule 0 x 460 [972,730,534,678,756]
+ CRUSH rule 0 x 461 [506,279,142,830,784]
+ CRUSH rule 0 x 462 [692,959,578,57,983]
+ CRUSH rule 0 x 463 [788,667,949,550,685]
+ CRUSH rule 0 x 464 [133,122,588,999,270]
+ CRUSH rule 0 x 465 [971,190,230,777,452]
+ CRUSH rule 0 x 466 [394,576,148,157,103]
+ CRUSH rule 0 x 467 [517,28,366,362,984]
+ CRUSH rule 0 x 468 [829,143,874,225,162]
+ CRUSH rule 0 x 469 [987,936,106,725,633]
+ CRUSH rule 0 x 470 [107,982,56,889,67]
+ CRUSH rule 0 x 471 [181,897,629,860,307]
+ CRUSH rule 0 x 472 [547,512,172,24,705]
+ CRUSH rule 0 x 473 [760,997,824,905,888]
+ CRUSH rule 0 x 474 [787,418,743,628,272]
+ CRUSH rule 0 x 475 [662,312,253,617,105]
+ CRUSH rule 0 x 476 [110,495,185,508,961]
+ CRUSH rule 0 x 477 [393,954,834,132,841]
+ CRUSH rule 0 x 478 [246,483,480,644,985]
+ CRUSH rule 0 x 479 [70,929,697,931,744]
+ CRUSH rule 0 x 480 [753,119,961,607,317]
+ CRUSH rule 0 x 481 [470,429,677,242,574]
+ CRUSH rule 0 x 482 [451,566,961,675,354]
+ CRUSH rule 0 x 483 [816,72,371,278,635]
+ CRUSH rule 0 x 484 [540,454,389,31,654]
+ CRUSH rule 0 x 485 [74,582,624,684,566]
+ CRUSH rule 0 x 486 [958,595,199,763,715]
+ CRUSH rule 0 x 487 [228,302,804,833,876]
+ CRUSH rule 0 x 488 [180,529,722,956,353]
+ CRUSH rule 0 x 489 [47,617,812,187,291]
+ CRUSH rule 0 x 490 [905,822,479,124,750]
+ CRUSH rule 0 x 491 [892,370,609,998,433]
+ CRUSH rule 0 x 492 [588,959,127,948,505]
+ CRUSH rule 0 x 493 [353,461,593,291,301]
+ CRUSH rule 0 x 494 [378,848,443,368,507]
+ CRUSH rule 0 x 495 [845,653,768,234,405]
+ CRUSH rule 0 x 496 [13,988,0,691,389]
+ CRUSH rule 0 x 497 [796,877,788,394,648]
+ CRUSH rule 0 x 498 [412,337,270,705,511]
+ CRUSH rule 0 x 499 [330,695,8,74,618]
+ CRUSH rule 0 x 500 [820,272,547,765,755]
+ CRUSH rule 0 x 501 [110,44,132,442,294]
+ CRUSH rule 0 x 502 [336,595,650,274,993]
+ CRUSH rule 0 x 503 [922,211,157,722,502]
+ CRUSH rule 0 x 504 [483,52,122,432,778]
+ CRUSH rule 0 x 505 [482,598,224,279,480]
+ CRUSH rule 0 x 506 [493,123,43,856,936]
+ CRUSH rule 0 x 507 [12,598,264,422,416]
+ CRUSH rule 0 x 508 [227,157,611,301,223]
+ CRUSH rule 0 x 509 [807,242,363,122,582]
+ CRUSH rule 0 x 510 [134,437,227,75,313]
+ CRUSH rule 0 x 511 [212,54,83,799,457]
+ CRUSH rule 0 x 512 [236,630,758,752,361]
+ CRUSH rule 0 x 513 [994,693,644,938,846]
+ CRUSH rule 0 x 514 [45,508,831,19,817]
+ CRUSH rule 0 x 515 [504,138,480,272,530]
+ CRUSH rule 0 x 516 [285,409,136,570,841]
+ CRUSH rule 0 x 517 [300,232,23,906,438]
+ CRUSH rule 0 x 518 [397,674,98,898,967]
+ CRUSH rule 0 x 519 [86,750,772,913,101]
+ CRUSH rule 0 x 520 [900,833,614,130,261]
+ CRUSH rule 0 x 521 [31,47,236,751,911]
+ CRUSH rule 0 x 522 [390,16,280,144,291]
+ CRUSH rule 0 x 523 [618,308,424,590,300]
+ CRUSH rule 0 x 524 [635,189,687,963,601]
+ CRUSH rule 0 x 525 [311,916,699,262,775]
+ CRUSH rule 0 x 526 [48,738,227,718,244]
+ CRUSH rule 0 x 527 [202,851,889,216,763]
+ CRUSH rule 0 x 528 [565,827,590,273,918]
+ CRUSH rule 0 x 529 [934,864,241,43,466]
+ CRUSH rule 0 x 530 [502,934,298,670,986]
+ CRUSH rule 0 x 531 [681,627,942,487,288]
+ CRUSH rule 0 x 532 [422,6,147,205,861]
+ CRUSH rule 0 x 533 [863,68,364,983,247]
+ CRUSH rule 0 x 534 [962,931,775,172,663]
+ CRUSH rule 0 x 535 [89,565,397,693,839]
+ CRUSH rule 0 x 536 [499,351,760,458,918]
+ CRUSH rule 0 x 537 [676,547,787,311,867]
+ CRUSH rule 0 x 538 [58,644,571,649,941]
+ CRUSH rule 0 x 539 [837,953,457,711,458]
+ CRUSH rule 0 x 540 [831,50,132,213,197]
+ CRUSH rule 0 x 541 [582,757,121,525,532]
+ CRUSH rule 0 x 542 [472,132,790,997,948]
+ CRUSH rule 0 x 543 [382,272,797,330,315]
+ CRUSH rule 0 x 544 [947,930,496,883,509]
+ CRUSH rule 0 x 545 [425,570,305,77,821]
+ CRUSH rule 0 x 546 [18,65,529,437,343]
+ CRUSH rule 0 x 547 [445,715,600,472,213]
+ CRUSH rule 0 x 548 [367,569,980,167,627]
+ CRUSH rule 0 x 549 [125,715,671,817,285]
+ CRUSH rule 0 x 550 [425,599,744,199,923]
+ CRUSH rule 0 x 551 [44,1,528,922,944]
+ CRUSH rule 0 x 552 [246,104,68,239,123]
+ CRUSH rule 0 x 553 [71,703,615,28,593]
+ CRUSH rule 0 x 554 [207,124,217,166,525]
+ CRUSH rule 0 x 555 [570,28,317,420,931]
+ CRUSH rule 0 x 556 [674,152,421,79,215]
+ CRUSH rule 0 x 557 [347,817,191,391,741]
+ CRUSH rule 0 x 558 [627,426,369,692,815]
+ CRUSH rule 0 x 559 [940,630,924,242,224]
+ CRUSH rule 0 x 560 [295,903,541,29,245]
+ CRUSH rule 0 x 561 [506,682,384,637,878]
+ CRUSH rule 0 x 562 [718,529,87,729,842]
+ CRUSH rule 0 x 563 [552,332,747,206,274]
+ CRUSH rule 0 x 564 [835,769,736,486,630]
+ CRUSH rule 0 x 565 [8,167,539,182,607]
+ CRUSH rule 0 x 566 [600,481,301,263,90]
+ CRUSH rule 0 x 567 [999,994,509,899,947]
+ CRUSH rule 0 x 568 [252,431,157,62,601]
+ CRUSH rule 0 x 569 [643,218,943,455,83]
+ CRUSH rule 0 x 570 [617,635,765,422,250]
+ CRUSH rule 0 x 571 [757,80,59,98,328]
+ CRUSH rule 0 x 572 [299,348,575,889,943]
+ CRUSH rule 0 x 573 [25,505,270,167,58]
+ CRUSH rule 0 x 574 [215,431,624,177,628]
+ CRUSH rule 0 x 575 [225,252,611,546,32]
+ CRUSH rule 0 x 576 [627,94,159,857,430]
+ CRUSH rule 0 x 577 [237,809,778,636,61]
+ CRUSH rule 0 x 578 [885,313,120,344,771]
+ CRUSH rule 0 x 579 [924,575,787,831,47]
+ CRUSH rule 0 x 580 [718,51,766,121,118]
+ CRUSH rule 0 x 581 [219,807,129,571,856]
+ CRUSH rule 0 x 582 [893,701,598,863,285]
+ CRUSH rule 0 x 583 [246,930,964,170,993]
+ CRUSH rule 0 x 584 [336,432,680,175,495]
+ CRUSH rule 0 x 585 [324,999,397,485,457]
+ CRUSH rule 0 x 586 [558,230,976,541,816]
+ CRUSH rule 0 x 587 [985,830,597,21,308]
+ CRUSH rule 0 x 588 [211,544,57,134,162]
+ CRUSH rule 0 x 589 [129,21,112,190,885]
+ CRUSH rule 0 x 590 [467,969,652,593,287]
+ CRUSH rule 0 x 591 [758,514,316,164,35]
+ CRUSH rule 0 x 592 [525,253,190,443,315]
+ CRUSH rule 0 x 593 [601,885,339,152,297]
+ CRUSH rule 0 x 594 [227,60,450,30,717]
+ CRUSH rule 0 x 595 [720,854,496,912,80]
+ CRUSH rule 0 x 596 [751,195,997,77,261]
+ CRUSH rule 0 x 597 [129,574,714,8,789]
+ CRUSH rule 0 x 598 [679,207,604,396,841]
+ CRUSH rule 0 x 599 [668,315,683,349,681]
+ CRUSH rule 0 x 600 [143,396,464,444,59]
+ CRUSH rule 0 x 601 [326,573,873,902,136]
+ CRUSH rule 0 x 602 [860,281,875,535,672]
+ CRUSH rule 0 x 603 [709,328,445,349,190]
+ CRUSH rule 0 x 604 [571,62,814,95,866]
+ CRUSH rule 0 x 605 [252,739,860,27,313]
+ CRUSH rule 0 x 606 [339,236,759,842,67]
+ CRUSH rule 0 x 607 [590,248,759,868,433]
+ CRUSH rule 0 x 608 [145,635,309,467,875]
+ CRUSH rule 0 x 609 [973,547,223,79,762]
+ CRUSH rule 0 x 610 [435,816,961,983,255]
+ CRUSH rule 0 x 611 [559,283,422,584,176]
+ CRUSH rule 0 x 612 [273,149,123,576,911]
+ CRUSH rule 0 x 613 [828,614,642,674,33]
+ CRUSH rule 0 x 614 [478,748,393,34,171]
+ CRUSH rule 0 x 615 [392,155,144,326,626]
+ CRUSH rule 0 x 616 [778,637,452,248,15]
+ CRUSH rule 0 x 617 [622,713,996,833,611]
+ CRUSH rule 0 x 618 [149,877,270,329,180]
+ CRUSH rule 0 x 619 [604,163,656,409,322]
+ CRUSH rule 0 x 620 [181,23,409,198,64]
+ CRUSH rule 0 x 621 [735,902,386,237,939]
+ CRUSH rule 0 x 622 [661,824,717,568,858]
+ CRUSH rule 0 x 623 [142,121,643,61,695]
+ CRUSH rule 0 x 624 [360,716,420,398,49]
+ CRUSH rule 0 x 625 [541,167,385,1,601]
+ CRUSH rule 0 x 626 [364,431,610,363,535]
+ CRUSH rule 0 x 627 [458,137,557,410,287]
+ CRUSH rule 0 x 628 [250,350,556,497,821]
+ CRUSH rule 0 x 629 [928,160,710,572,365]
+ CRUSH rule 0 x 630 [243,19,918,556,601]
+ CRUSH rule 0 x 631 [438,221,574,676,797]
+ CRUSH rule 0 x 632 [797,368,247,5,32]
+ CRUSH rule 0 x 633 [993,749,525,485,27]
+ CRUSH rule 0 x 634 [239,351,633,299,651]
+ CRUSH rule 0 x 635 [640,965,25,961,306]
+ CRUSH rule 0 x 636 [173,290,297,991,937]
+ CRUSH rule 0 x 637 [0,918,98,108,111]
+ CRUSH rule 0 x 638 [702,235,424,900,983]
+ CRUSH rule 0 x 639 [475,687,31,785,918]
+ CRUSH rule 0 x 640 [31,664,399,677,123]
+ CRUSH rule 0 x 641 [296,473,108,963,341]
+ CRUSH rule 0 x 642 [894,273,427,606,677]
+ CRUSH rule 0 x 643 [117,111,732,191,114]
+ CRUSH rule 0 x 644 [438,336,327,512,599]
+ CRUSH rule 0 x 645 [982,702,351,573,907]
+ CRUSH rule 0 x 646 [334,804,146,842,697]
+ CRUSH rule 0 x 647 [933,787,185,334,752]
+ CRUSH rule 0 x 648 [22,444,400,862,207]
+ CRUSH rule 0 x 649 [503,229,213,460,639]
+ CRUSH rule 0 x 650 [328,659,420,443,739]
+ CRUSH rule 0 x 651 [3,880,823,123,378]
+ CRUSH rule 0 x 652 [495,977,563,733,92]
+ CRUSH rule 0 x 653 [185,718,804,280,975]
+ CRUSH rule 0 x 654 [130,528,380,81,906]
+ CRUSH rule 0 x 655 [560,872,454,504,319]
+ CRUSH rule 0 x 656 [219,885,178,981,863]
+ CRUSH rule 0 x 657 [233,684,813,490,208]
+ CRUSH rule 0 x 658 [778,6,756,380,750]
+ CRUSH rule 0 x 659 [240,663,306,540,789]
+ CRUSH rule 0 x 660 [244,855,196,147,678]
+ CRUSH rule 0 x 661 [184,270,128,398,910]
+ CRUSH rule 0 x 662 [65,883,921,438,79]
+ CRUSH rule 0 x 663 [323,721,594,812,43]
+ CRUSH rule 0 x 664 [865,113,512,51,427]
+ CRUSH rule 0 x 665 [420,850,591,475,202]
+ CRUSH rule 0 x 666 [319,767,246,3,369]
+ CRUSH rule 0 x 667 [875,39,343,100,829]
+ CRUSH rule 0 x 668 [331,122,263,599,355]
+ CRUSH rule 0 x 669 [915,521,402,747,673]
+ CRUSH rule 0 x 670 [845,659,943,447,401]
+ CRUSH rule 0 x 671 [108,634,527,363,856]
+ CRUSH rule 0 x 672 [578,216,110,589,302]
+ CRUSH rule 0 x 673 [442,74,579,797,622]
+ CRUSH rule 0 x 674 [588,364,281,308,645]
+ CRUSH rule 0 x 675 [489,698,744,671,870]
+ CRUSH rule 0 x 676 [928,911,40,180,722]
+ CRUSH rule 0 x 677 [399,269,692,131,615]
+ CRUSH rule 0 x 678 [546,752,544,155,5]
+ CRUSH rule 0 x 679 [988,25,275,433,628]
+ CRUSH rule 0 x 680 [335,963,382,486,749]
+ CRUSH rule 0 x 681 [690,462,623,466,49]
+ CRUSH rule 0 x 682 [196,588,154,257,807]
+ CRUSH rule 0 x 683 [627,25,421,160,873]
+ CRUSH rule 0 x 684 [38,804,592,158,991]
+ CRUSH rule 0 x 685 [841,368,548,362,166]
+ CRUSH rule 0 x 686 [336,287,525,440,166]
+ CRUSH rule 0 x 687 [20,682,924,653,356]
+ CRUSH rule 0 x 688 [463,371,780,556,385]
+ CRUSH rule 0 x 689 [569,250,78,816,847]
+ CRUSH rule 0 x 690 [551,144,587,263,378]
+ CRUSH rule 0 x 691 [766,464,446,533,449]
+ CRUSH rule 0 x 692 [739,634,18,245,624]
+ CRUSH rule 0 x 693 [339,297,118,330,817]
+ CRUSH rule 0 x 694 [405,26,830,181,533]
+ CRUSH rule 0 x 695 [622,576,597,535,600]
+ CRUSH rule 0 x 696 [558,902,689,13,715]
+ CRUSH rule 0 x 697 [818,222,406,691,427]
+ CRUSH rule 0 x 698 [178,48,402,233,841]
+ CRUSH rule 0 x 699 [450,244,180,919,183]
+ CRUSH rule 0 x 700 [502,771,987,706,416]
+ CRUSH rule 0 x 701 [4,612,782,216,853]
+ CRUSH rule 0 x 702 [177,630,232,923,281]
+ CRUSH rule 0 x 703 [354,178,389,393,778]
+ CRUSH rule 0 x 704 [646,601,156,171,603]
+ CRUSH rule 0 x 705 [921,401,890,265,244]
+ CRUSH rule 0 x 706 [652,877,562,452,26]
+ CRUSH rule 0 x 707 [345,745,67,716,789]
+ CRUSH rule 0 x 708 [333,607,180,469,170]
+ CRUSH rule 0 x 709 [45,187,302,115,896]
+ CRUSH rule 0 x 710 [94,855,43,199,18]
+ CRUSH rule 0 x 711 [227,653,731,150,156]
+ CRUSH rule 0 x 712 [398,953,136,870,181]
+ CRUSH rule 0 x 713 [116,800,503,662,635]
+ CRUSH rule 0 x 714 [111,629,866,709,902]
+ CRUSH rule 0 x 715 [531,291,486,382,192]
+ CRUSH rule 0 x 716 [169,541,291,42,343]
+ CRUSH rule 0 x 717 [417,446,994,894,239]
+ CRUSH rule 0 x 718 [992,383,298,844,377]
+ CRUSH rule 0 x 719 [936,674,324,759,194]
+ CRUSH rule 0 x 720 [370,188,174,464,644]
+ CRUSH rule 0 x 721 [320,859,278,259,170]
+ CRUSH rule 0 x 722 [7,2,673,129,96]
+ CRUSH rule 0 x 723 [270,553,831,662,38]
+ CRUSH rule 0 x 724 [666,822,708,895,633]
+ CRUSH rule 0 x 725 [794,406,875,459,981]
+ CRUSH rule 0 x 726 [420,556,341,292,240]
+ CRUSH rule 0 x 727 [561,461,129,635,965]
+ CRUSH rule 0 x 728 [951,330,196,756,589]
+ CRUSH rule 0 x 729 [656,644,436,591,27]
+ CRUSH rule 0 x 730 [3,558,629,184,50]
+ CRUSH rule 0 x 731 [852,89,75,735,713]
+ CRUSH rule 0 x 732 [983,840,869,976,697]
+ CRUSH rule 0 x 733 [285,396,388,122,387]
+ CRUSH rule 0 x 734 [125,510,402,640,676]
+ CRUSH rule 0 x 735 [417,773,686,504,459]
+ CRUSH rule 0 x 736 [749,396,632,550,779]
+ CRUSH rule 0 x 737 [644,991,946,135,448]
+ CRUSH rule 0 x 738 [449,683,290,220,245]
+ CRUSH rule 0 x 739 [341,220,641,454,740]
+ CRUSH rule 0 x 740 [874,524,674,650,472]
+ CRUSH rule 0 x 741 [189,472,712,798,715]
+ CRUSH rule 0 x 742 [912,581,114,117,730]
+ CRUSH rule 0 x 743 [654,914,425,441,763]
+ CRUSH rule 0 x 744 [725,295,579,377,162]
+ CRUSH rule 0 x 745 [787,858,850,506,612]
+ CRUSH rule 0 x 746 [757,848,704,30,47]
+ CRUSH rule 0 x 747 [700,81,867,681,801]
+ CRUSH rule 0 x 748 [557,436,238,664,293]
+ CRUSH rule 0 x 749 [772,622,337,42,156]
+ CRUSH rule 0 x 750 [946,97,376,677,316]
+ CRUSH rule 0 x 751 [996,618,343,911,83]
+ CRUSH rule 0 x 752 [746,887,695,868,610]
+ CRUSH rule 0 x 753 [741,14,463,479,172]
+ CRUSH rule 0 x 754 [648,349,333,355,65]
+ CRUSH rule 0 x 755 [157,460,466,187,959]
+ CRUSH rule 0 x 756 [416,97,197,497,227]
+ CRUSH rule 0 x 757 [599,839,776,410,256]
+ CRUSH rule 0 x 758 [994,218,620,256,361]
+ CRUSH rule 0 x 759 [959,682,514,745,100]
+ CRUSH rule 0 x 760 [518,943,215,83,706]
+ CRUSH rule 0 x 761 [285,849,420,324,987]
+ CRUSH rule 0 x 762 [591,313,41,335,110]
+ CRUSH rule 0 x 763 [908,411,200,740,292]
+ CRUSH rule 0 x 764 [787,234,894,485,883]
+ CRUSH rule 0 x 765 [327,921,882,393,444]
+ CRUSH rule 0 x 766 [84,161,878,704,416]
+ CRUSH rule 0 x 767 [370,895,702,701,890]
+ CRUSH rule 0 x 768 [826,760,879,864,460]
+ CRUSH rule 0 x 769 [67,768,663,735,814]
+ CRUSH rule 0 x 770 [593,909,482,259,5]
+ CRUSH rule 0 x 771 [309,935,121,578,937]
+ CRUSH rule 0 x 772 [12,125,797,301,348]
+ CRUSH rule 0 x 773 [253,466,820,549,591]
+ CRUSH rule 0 x 774 [164,390,705,109,881]
+ CRUSH rule 0 x 775 [703,47,43,973,643]
+ CRUSH rule 0 x 776 [728,231,80,916,2]
+ CRUSH rule 0 x 777 [981,621,568,729,869]
+ CRUSH rule 0 x 778 [411,456,544,597,789]
+ CRUSH rule 0 x 779 [346,121,519,921,587]
+ CRUSH rule 0 x 780 [476,39,288,381,303]
+ CRUSH rule 0 x 781 [10,130,585,844,729]
+ CRUSH rule 0 x 782 [462,246,581,902,623]
+ CRUSH rule 0 x 783 [580,373,153,775,668]
+ CRUSH rule 0 x 784 [413,113,978,990,994]
+ CRUSH rule 0 x 785 [341,856,332,354,59]
+ CRUSH rule 0 x 786 [411,140,313,393,215]
+ CRUSH rule 0 x 787 [605,522,211,813,636]
+ CRUSH rule 0 x 788 [226,545,35,142,726]
+ CRUSH rule 0 x 789 [545,320,414,702,731]
+ CRUSH rule 0 x 790 [414,748,816,327,130]
+ CRUSH rule 0 x 791 [660,906,406,697,916]
+ CRUSH rule 0 x 792 [287,392,514,204,75]
+ CRUSH rule 0 x 793 [631,133,850,713,720]
+ CRUSH rule 0 x 794 [931,517,543,210,963]
+ CRUSH rule 0 x 795 [551,962,477,948,425]
+ CRUSH rule 0 x 796 [814,4,95,27,368]
+ CRUSH rule 0 x 797 [64,201,299,734,605]
+ CRUSH rule 0 x 798 [422,530,114,431,565]
+ CRUSH rule 0 x 799 [824,32,679,562,266]
+ CRUSH rule 0 x 800 [862,623,489,637,861]
+ CRUSH rule 0 x 801 [145,550,329,324,734]
+ CRUSH rule 0 x 802 [570,19,847,308,387]
+ CRUSH rule 0 x 803 [151,812,662,358,880]
+ CRUSH rule 0 x 804 [467,93,264,863,176]
+ CRUSH rule 0 x 805 [621,223,938,809,591]
+ CRUSH rule 0 x 806 [898,957,805,430,499]
+ CRUSH rule 0 x 807 [354,531,422,159,921]
+ CRUSH rule 0 x 808 [7,96,76,897,446]
+ CRUSH rule 0 x 809 [70,734,719,56,687]
+ CRUSH rule 0 x 810 [701,18,972,327,771]
+ CRUSH rule 0 x 811 [248,547,103,728,901]
+ CRUSH rule 0 x 812 [230,576,821,566,993]
+ CRUSH rule 0 x 813 [805,114,683,629,801]
+ CRUSH rule 0 x 814 [54,619,973,741,497]
+ CRUSH rule 0 x 815 [679,412,613,132,969]
+ CRUSH rule 0 x 816 [919,448,826,414,36]
+ CRUSH rule 0 x 817 [765,830,436,521,332]
+ CRUSH rule 0 x 818 [415,566,644,687,692]
+ CRUSH rule 0 x 819 [721,319,865,750,546]
+ CRUSH rule 0 x 820 [218,301,333,190,686]
+ CRUSH rule 0 x 821 [185,795,680,953,329]
+ CRUSH rule 0 x 822 [356,261,54,522,900]
+ CRUSH rule 0 x 823 [220,281,549,456,64]
+ CRUSH rule 0 x 824 [292,809,887,74,776]
+ CRUSH rule 0 x 825 [949,778,101,311,110]
+ CRUSH rule 0 x 826 [767,818,833,927,356]
+ CRUSH rule 0 x 827 [631,83,406,635,657]
+ CRUSH rule 0 x 828 [288,986,445,26,414]
+ CRUSH rule 0 x 829 [990,667,915,694,974]
+ CRUSH rule 0 x 830 [152,571,778,505,685]
+ CRUSH rule 0 x 831 [814,563,630,97,582]
+ CRUSH rule 0 x 832 [235,641,616,110,979]
+ CRUSH rule 0 x 833 [657,565,922,140,825]
+ CRUSH rule 0 x 834 [907,231,644,13,617]
+ CRUSH rule 0 x 835 [784,262,771,264,612]
+ CRUSH rule 0 x 836 [951,158,366,710,43]
+ CRUSH rule 0 x 837 [556,498,334,633,895]
+ CRUSH rule 0 x 838 [329,274,964,547,119]
+ CRUSH rule 0 x 839 [568,209,939,364,658]
+ CRUSH rule 0 x 840 [45,579,842,70,655]
+ CRUSH rule 0 x 841 [652,702,24,605,152]
+ CRUSH rule 0 x 842 [629,984,314,895,408]
+ CRUSH rule 0 x 843 [799,690,688,648,151]
+ CRUSH rule 0 x 844 [694,600,534,700,569]
+ CRUSH rule 0 x 845 [332,30,179,93,951]
+ CRUSH rule 0 x 846 [452,251,712,719,404]
+ CRUSH rule 0 x 847 [399,681,847,739,13]
+ CRUSH rule 0 x 848 [303,138,440,346,547]
+ CRUSH rule 0 x 849 [666,346,708,873,64]
+ CRUSH rule 0 x 850 [644,511,345,844,545]
+ CRUSH rule 0 x 851 [527,546,737,425,100]
+ CRUSH rule 0 x 852 [31,809,94,618,156]
+ CRUSH rule 0 x 853 [483,330,869,184,46]
+ CRUSH rule 0 x 854 [697,953,968,143,502]
+ CRUSH rule 0 x 855 [837,996,239,621,32]
+ CRUSH rule 0 x 856 [712,40,547,430,195]
+ CRUSH rule 0 x 857 [77,984,576,551,568]
+ CRUSH rule 0 x 858 [412,384,841,465,572]
+ CRUSH rule 0 x 859 [173,760,26,300,87]
+ CRUSH rule 0 x 860 [776,429,328,917,658]
+ CRUSH rule 0 x 861 [705,405,477,50,73]
+ CRUSH rule 0 x 862 [809,44,788,938,964]
+ CRUSH rule 0 x 863 [349,496,963,178,675]
+ CRUSH rule 0 x 864 [717,858,101,239,992]
+ CRUSH rule 0 x 865 [857,603,586,262,550]
+ CRUSH rule 0 x 866 [394,304,71,96,642]
+ CRUSH rule 0 x 867 [640,773,663,974,261]
+ CRUSH rule 0 x 868 [613,950,712,663,666]
+ CRUSH rule 0 x 869 [973,889,524,22,671]
+ CRUSH rule 0 x 870 [505,35,386,498,348]
+ CRUSH rule 0 x 871 [239,264,262,773,781]
+ CRUSH rule 0 x 872 [21,767,456,748,783]
+ CRUSH rule 0 x 873 [954,666,980,264,435]
+ CRUSH rule 0 x 874 [54,510,947,1,500]
+ CRUSH rule 0 x 875 [809,418,452,462,88]
+ CRUSH rule 0 x 876 [483,457,61,248,523]
+ CRUSH rule 0 x 877 [542,531,952,939,710]
+ CRUSH rule 0 x 878 [217,674,857,644,678]
+ CRUSH rule 0 x 879 [999,475,134,250,319]
+ CRUSH rule 0 x 880 [678,573,935,385,570]
+ CRUSH rule 0 x 881 [394,835,789,802,587]
+ CRUSH rule 0 x 882 [467,382,353,56,979]
+ CRUSH rule 0 x 883 [802,744,237,337,50]
+ CRUSH rule 0 x 884 [653,660,638,700,31]
+ CRUSH rule 0 x 885 [898,704,307,445,879]
+ CRUSH rule 0 x 886 [434,357,938,641,737]
+ CRUSH rule 0 x 887 [297,226,711,428,370]
+ CRUSH rule 0 x 888 [863,324,443,213,902]
+ CRUSH rule 0 x 889 [105,102,308,163,947]
+ CRUSH rule 0 x 890 [550,248,606,704,615]
+ CRUSH rule 0 x 891 [575,928,880,891,826]
+ CRUSH rule 0 x 892 [259,862,133,271,292]
+ CRUSH rule 0 x 893 [902,880,543,542,37]
+ CRUSH rule 0 x 894 [180,169,916,43,945]
+ CRUSH rule 0 x 895 [725,849,182,129,177]
+ CRUSH rule 0 x 896 [951,34,874,537,969]
+ CRUSH rule 0 x 897 [810,352,73,939,943]
+ CRUSH rule 0 x 898 [979,433,719,411,787]
+ CRUSH rule 0 x 899 [685,668,534,932,399]
+ CRUSH rule 0 x 900 [530,978,41,894,941]
+ CRUSH rule 0 x 901 [740,107,336,175,574]
+ CRUSH rule 0 x 902 [800,743,693,310,67]
+ CRUSH rule 0 x 903 [230,267,842,266,550]
+ CRUSH rule 0 x 904 [346,949,460,973,696]
+ CRUSH rule 0 x 905 [530,397,619,958,576]
+ CRUSH rule 0 x 906 [80,426,138,672,73]
+ CRUSH rule 0 x 907 [365,968,475,297,296]
+ CRUSH rule 0 x 908 [204,832,742,809,862]
+ CRUSH rule 0 x 909 [883,989,146,959,366]
+ CRUSH rule 0 x 910 [549,593,249,853,792]
+ CRUSH rule 0 x 911 [325,847,352,214,851]
+ CRUSH rule 0 x 912 [874,888,582,796,557]
+ CRUSH rule 0 x 913 [331,463,342,574,989]
+ CRUSH rule 0 x 914 [836,468,601,732,607]
+ CRUSH rule 0 x 915 [245,228,100,661,799]
+ CRUSH rule 0 x 916 [77,967,364,435,27]
+ CRUSH rule 0 x 917 [239,60,866,221,772]
+ CRUSH rule 0 x 918 [988,115,922,80,201]
+ CRUSH rule 0 x 919 [783,139,696,1,848]
+ CRUSH rule 0 x 920 [623,408,685,953,974]
+ CRUSH rule 0 x 921 [105,799,144,90,399]
+ CRUSH rule 0 x 922 [887,505,652,348,514]
+ CRUSH rule 0 x 923 [223,318,552,458,743]
+ CRUSH rule 0 x 924 [25,778,366,333,163]
+ CRUSH rule 0 x 925 [912,601,297,682,770]
+ CRUSH rule 0 x 926 [968,133,132,144,814]
+ CRUSH rule 0 x 927 [277,724,214,988,690]
+ CRUSH rule 0 x 928 [554,203,658,789,298]
+ CRUSH rule 0 x 929 [761,802,367,528,758]
+ CRUSH rule 0 x 930 [814,61,788,736,660]
+ CRUSH rule 0 x 931 [29,193,61,41,343]
+ CRUSH rule 0 x 932 [446,198,862,534,168]
+ CRUSH rule 0 x 933 [352,742,216,321,525]
+ CRUSH rule 0 x 934 [730,2,332,631,613]
+ CRUSH rule 0 x 935 [731,23,736,79,361]
+ CRUSH rule 0 x 936 [322,975,20,904,827]
+ CRUSH rule 0 x 937 [822,221,841,161,723]
+ CRUSH rule 0 x 938 [557,850,66,630,499]
+ CRUSH rule 0 x 939 [150,11,971,371,124]
+ CRUSH rule 0 x 940 [638,398,169,616,333]
+ CRUSH rule 0 x 941 [730,342,929,577,451]
+ CRUSH rule 0 x 942 [62,292,166,814,587]
+ CRUSH rule 0 x 943 [165,314,519,548,41]
+ CRUSH rule 0 x 944 [199,625,766,176,194]
+ CRUSH rule 0 x 945 [946,999,699,303,38]
+ CRUSH rule 0 x 946 [595,93,852,142,503]
+ CRUSH rule 0 x 947 [800,582,356,93,716]
+ CRUSH rule 0 x 948 [132,551,139,920,87]
+ CRUSH rule 0 x 949 [792,920,466,380,97]
+ CRUSH rule 0 x 950 [111,345,176,543,879]
+ CRUSH rule 0 x 951 [414,619,648,655,364]
+ CRUSH rule 0 x 952 [775,469,500,356,287]
+ CRUSH rule 0 x 953 [349,1,5,251,168]
+ CRUSH rule 0 x 954 [570,940,410,249,929]
+ CRUSH rule 0 x 955 [729,774,823,800,7]
+ CRUSH rule 0 x 956 [519,141,575,625,738]
+ CRUSH rule 0 x 957 [242,709,611,97,760]
+ CRUSH rule 0 x 958 [84,217,227,253,246]
+ CRUSH rule 0 x 959 [270,413,918,789,703]
+ CRUSH rule 0 x 960 [458,192,307,279,920]
+ CRUSH rule 0 x 961 [981,388,777,546,359]
+ CRUSH rule 0 x 962 [623,834,277,134,729]
+ CRUSH rule 0 x 963 [291,167,714,468,109]
+ CRUSH rule 0 x 964 [28,156,788,127,598]
+ CRUSH rule 0 x 965 [675,557,290,517,840]
+ CRUSH rule 0 x 966 [836,306,946,283,642]
+ CRUSH rule 0 x 967 [966,386,735,837,392]
+ CRUSH rule 0 x 968 [864,756,690,121,328]
+ CRUSH rule 0 x 969 [729,625,480,769,512]
+ CRUSH rule 0 x 970 [800,362,646,582,309]
+ CRUSH rule 0 x 971 [737,381,153,684,298]
+ CRUSH rule 0 x 972 [952,245,720,884,334]
+ CRUSH rule 0 x 973 [356,455,579,857,832]
+ CRUSH rule 0 x 974 [545,758,586,596,756]
+ CRUSH rule 0 x 975 [336,191,202,146,720]
+ CRUSH rule 0 x 976 [446,208,757,620,252]
+ CRUSH rule 0 x 977 [202,896,196,956,763]
+ CRUSH rule 0 x 978 [612,324,996,225,418]
+ CRUSH rule 0 x 979 [843,457,675,650,958]
+ CRUSH rule 0 x 980 [60,914,881,626,850]
+ CRUSH rule 0 x 981 [702,749,937,153,724]
+ CRUSH rule 0 x 982 [298,928,738,167,99]
+ CRUSH rule 0 x 983 [723,572,395,358,900]
+ CRUSH rule 0 x 984 [723,864,804,935,846]
+ CRUSH rule 0 x 985 [945,459,868,211,524]
+ CRUSH rule 0 x 986 [772,664,535,169,297]
+ CRUSH rule 0 x 987 [88,324,312,843,661]
+ CRUSH rule 0 x 988 [522,927,131,996,351]
+ CRUSH rule 0 x 989 [578,332,208,605,975]
+ CRUSH rule 0 x 990 [638,228,414,311,738]
+ CRUSH rule 0 x 991 [530,221,451,422,879]
+ CRUSH rule 0 x 992 [925,705,275,81,234]
+ CRUSH rule 0 x 993 [991,301,43,469,830]
+ CRUSH rule 0 x 994 [276,51,868,683,843]
+ CRUSH rule 0 x 995 [288,836,753,790,758]
+ CRUSH rule 0 x 996 [887,983,252,686,470]
+ CRUSH rule 0 x 997 [110,924,386,79,705]
+ CRUSH rule 0 x 998 [435,830,485,853,926]
+ CRUSH rule 0 x 999 [876,738,357,913,723]
+ CRUSH rule 0 x 1000 [178,963,638,430,845]
+ CRUSH rule 0 x 1001 [99,519,66,759,583]
+ CRUSH rule 0 x 1002 [515,534,468,866,878]
+ CRUSH rule 0 x 1003 [104,611,937,698,94]
+ CRUSH rule 0 x 1004 [269,638,724,375,491]
+ CRUSH rule 0 x 1005 [369,223,309,409,822]
+ CRUSH rule 0 x 1006 [40,107,69,275,79]
+ CRUSH rule 0 x 1007 [978,111,416,758,454]
+ CRUSH rule 0 x 1008 [965,956,624,832,421]
+ CRUSH rule 0 x 1009 [598,476,356,695,919]
+ CRUSH rule 0 x 1010 [767,523,239,517,29]
+ CRUSH rule 0 x 1011 [289,871,207,576,347]
+ CRUSH rule 0 x 1012 [128,28,370,31,341]
+ CRUSH rule 0 x 1013 [979,765,660,812,666]
+ CRUSH rule 0 x 1014 [979,948,513,88,47]
+ CRUSH rule 0 x 1015 [277,790,396,672,542]
+ CRUSH rule 0 x 1016 [262,73,128,886,839]
+ CRUSH rule 0 x 1017 [150,269,61,499,832]
+ CRUSH rule 0 x 1018 [555,829,554,944,406]
+ CRUSH rule 0 x 1019 [513,356,265,446,65]
+ CRUSH rule 0 x 1020 [158,161,877,704,948]
+ CRUSH rule 0 x 1021 [915,998,957,285,546]
+ CRUSH rule 0 x 1022 [967,829,973,640,703]
+ CRUSH rule 0 x 1023 [488,257,614,859,325]
+ rule 0 (data) num_rep 5 result size == 5:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356]
+ CRUSH rule 0 x 87 [997,317,463,626,685,683]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652]
+ CRUSH rule 0 x 194 [242,473,58,655,653,277]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849]
+ CRUSH rule 0 x 257 [825,264,867,869,529,409]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939]
+ CRUSH rule 0 x 262 [223,1,561,420,229,16]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544]
+ CRUSH rule 0 x 324 [16,745,511,439,272,513]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493]
+ CRUSH rule 0 x 374 [191,887,920,928,223,714]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149]
+ CRUSH rule 0 x 445 [485,482,528,209,964,753]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604]
+ CRUSH rule 0 x 699 [450,244,180,919,183,332]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948]
+ CRUSH rule 0 x 711 [227,653,731,150,156,842]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757]
+ CRUSH rule 0 x 742 [912,581,114,117,730,21]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762]
+ CRUSH rule 0 x 813 [805,114,683,629,801,462]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296]
+ CRUSH rule 0 x 868 [613,950,712,663,666,460]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173]
+ CRUSH rule 0 x 926 [968,133,132,144,814,155]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596]
+ CRUSH rule 0 x 974 [545,758,586,596,756,790]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419]
+ rule 0 (data) num_rep 6 result size == 6:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38]
+ CRUSH rule 0 x 87 [997,317,463,626,685,683,909]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359]
+ CRUSH rule 0 x 194 [242,473,58,655,653,277,792]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189]
+ CRUSH rule 0 x 257 [825,264,867,869,529,409,291]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516]
+ CRUSH rule 0 x 262 [223,1,561,420,229,16,88]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236]
+ CRUSH rule 0 x 324 [16,745,511,439,272,513,668]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128]
+ CRUSH rule 0 x 374 [191,887,920,928,223,714,961]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,879]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182]
+ CRUSH rule 0 x 445 [485,482,528,209,964,753,554]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,385]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468]
+ CRUSH rule 0 x 699 [450,244,180,919,183,332,747]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449]
+ CRUSH rule 0 x 711 [227,653,731,150,156,842,534]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863]
+ CRUSH rule 0 x 742 [912,581,114,117,730,21,687]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675]
+ CRUSH rule 0 x 813 [805,114,683,629,801,462,285]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988]
+ CRUSH rule 0 x 868 [613,950,712,663,666,460,643]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969]
+ CRUSH rule 0 x 926 [968,133,132,144,814,155,709]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,171]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549]
+ CRUSH rule 0 x 974 [545,758,586,596,756,790,116]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50]
+ rule 0 (data) num_rep 7 result size == 7:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966,750]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672,820]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527,901]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481,169]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847,220]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964,872]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884,726]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578,14]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462,207]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3,569]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811,484]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380,821]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117,393]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78,170]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855,76]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437,72]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786,969]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935,605]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883,394]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51,834]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975,636]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263,267]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557,633]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477,468]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800,309]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248,908]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501,909]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676,560]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770,569]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719,581]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426,522]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813,217]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428,63]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618,288]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663,877]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75,580]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676,524]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311,758]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929,910]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856,248]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980,503]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930,74]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456,713]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514,40]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160,527]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658,876]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45,390]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959,800]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738,47]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822,628]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83,344]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406,281]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304,899]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882,503]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339,902]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508,763]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77,500]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670,67]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8,762]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643,511]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194,277]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812,14]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45,723]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705,268]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451,891]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862,221]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334,656]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783,593]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705,114]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871,361]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684,411]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587,264]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642,529]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835,705]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633,560]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366,258]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735,953]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197,690]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183,917]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339,24]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51,766]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247,963]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580,51]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66,197]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681,618]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567,908]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38,326]
+ CRUSH rule 0 x 87 [997,317,463,626,685,683,909,49]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99,737]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320,921]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412,184]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495,299]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167,634]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343,949]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82,422]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648,661]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204,750]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83,542]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270,511]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37,138]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9,168]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12,369]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923,106]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933,231]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401,85]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434,121]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878,493]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589,614]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631,996]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856,296]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510,472]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18,949]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725,413]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738,253]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598,418]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421,39]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9,448]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725,142]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766,210]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570,278]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49,126]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424,868]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551,43]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877,248]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482,254]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736,737]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689,209]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909,809]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666,691]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149,146]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270,425]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203,667]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922,597]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14,444]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893,281]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712,649]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846,890]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363,323]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400,885]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384,58]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178,532]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193,258]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424,820]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266,70]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697,610]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577,348]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929,196]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514,546]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352,820]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406,556]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484,553]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671,883]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807,50]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942,864]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443,98]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58,641]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271,229]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384,446]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899,902]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209,641]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242,999]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909,642]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148,88]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587,854]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110,329]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524,389]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548,676]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935,266]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667,463]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601,909]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835,840]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100,910]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36,356]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471,529]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780,932]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51,211]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240,316]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756,647]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856,992]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724,923]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736,846]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168,224]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485,571]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109,903]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74,45]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485,511]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369,270]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566,378]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833,650]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37,1]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527,237]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999,754]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123,759]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359,721]
+ CRUSH rule 0 x 194 [242,473,58,655,653,277,792,887]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755,471]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413,834]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666,316]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355,574]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960,369]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132,271]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495,76]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153,272]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669,676]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697,881]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776,463]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230,367]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76,708]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96,841]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522,185]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14,921]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922,372]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857,894]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687,903]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579,79]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982,826]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973,898]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264,6]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34,497]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697,805]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686,982]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61,559]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597,904]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465,429]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329,320]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606,465]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876,984]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966,217]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261,389]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262,884]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93,880]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50,182]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106,877]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796,835]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103,95]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471,753]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573,128]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327,509]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894,884]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426,427]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450,677]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413,136]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234,756]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799,21]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785,107]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605,837]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989,999]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590,840]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713,237]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886,650]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270,619]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58,354]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838,131]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797,675]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605,453]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607,95]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189,627]
+ CRUSH rule 0 x 257 [825,264,867,869,529,409,291,732]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863,427]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77,191]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533,646]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516,136]
+ CRUSH rule 0 x 262 [223,1,561,420,229,16,88,534]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773,979]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715,916]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833,987]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7,557]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972,645]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715,536]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146,14]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476,571]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263,138]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710,779]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982,735]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370,990]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992,823]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634,98]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783,648]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350,600]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952,250]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895,914]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443,604]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939,879]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369,544]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60,546]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582,975]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699,627]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137,890]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195,538]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795,377]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473,209]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557,86]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806,911]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775,636]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459,833]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440,73]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266,82]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232,343]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213,286]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924,106]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904,838]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220,432]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400,365]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561,905]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214,944]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685,838]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761,648]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459,250]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976,551]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857,153]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90,855]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256,798]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497,929]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24,81]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952,599]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749,841]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761,725]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892,86]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290,97]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800,868]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523,366]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868,841]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187,380]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236,810]
+ CRUSH rule 0 x 324 [16,745,511,439,272,513,668,959]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462,268]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461,509]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750,669]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33,806]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516,713]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420,520]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624,649]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644,821]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243,633]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408,175]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635,367]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698,533]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956,907]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69,11]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607,953]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707,328]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720,503]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516,661]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660,655]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142,114]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127,732]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419,962]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783,661]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862,513]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795,143]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841,164]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766,775]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899,665]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279,616]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612,33]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985,412]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629,228]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891,656]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874,643]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397,52]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29,529]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643,286]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265,35]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444,613]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203,17]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113,373]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59,914]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685,641]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616,93]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284,184]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850,419]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797,547]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212,241]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128,207]
+ CRUSH rule 0 x 374 [191,887,920,928,223,714,961,760]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934,619]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446,897]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654,487]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728,860]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115,557]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255,806]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773,496]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276,56]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29,197]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488,435]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910,329]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232,692]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164,934]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474,267]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340,222]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852,196]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,879,136]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901,82]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652,543]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15,591]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126,179]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726,935]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996,558]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349,672]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749,410]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536,592]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79,32]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658,449]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202,650]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299,577]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776,296]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718,578]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72,345]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246,695]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213,806]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949,507]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313,351]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560,155]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618,907]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97,124]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694,332]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889,344]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138,374]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848,972]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823,884]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506,670]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764,274]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839,547]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905,588]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785,395]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80,236]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791,565]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334,588]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225,94]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857,562]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859,699]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194,366]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513,591]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52,897]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644,646]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371,731]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10,976]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443,23]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755,841]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414,152]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800,174]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205,236]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864,799]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774,350]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182,145]
+ CRUSH rule 0 x 445 [485,482,528,209,964,753,554,931]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314,714]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58,531]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807,688]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258,548]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721,31]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878,33]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604,49]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514,5]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106,103]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295,967]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504,96]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762,975]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399,558]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147,483]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841,512]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385,797]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240,911]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538,111]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789,0]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137,466]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659,35]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187,640]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201,249]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681,683]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558,71]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256,978]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809,56]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756,663]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446,333]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237,764]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984,226]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753,794]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941,843]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158,489]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371,807]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135,375]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731,233]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448,437]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283,170]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866,661]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621,955]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857,782]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924,965]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154,478]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566,779]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188,563]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591,423]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231,308]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389,819]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823,789]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129,763]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542,745]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949,173]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440,509]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930,573]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880,279]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490,852]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262,926]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758,104]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764,558]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898,161]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591,702]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313,282]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798,808]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786,152]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600,968]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899,451]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52,185]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374,985]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481,820]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453,660]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519,737]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625,434]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375,328]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558,956]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495,354]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753,624]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834,212]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8,550]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45,478]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853,643]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270,35]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651,368]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278,926]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577,509]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925,474]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949,374]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54,931]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206,682]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859,30]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148,668]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152,797]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37,485]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528,722]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95,789]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738,277]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137,934]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324,134]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250,362]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117,172]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699,610]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428,267]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517,684]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37,639]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915,570]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161,901]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57,217]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218,916]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693,953]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623,659]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830,762]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593,267]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124,107]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185,356]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887,376]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700,339]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62,817]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903,900]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641,751]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738,873]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184,127]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267,639]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398,521]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494,624]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533,674]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329,848]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33,312]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878,978]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333,841]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389,486]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177,545]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700,521]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487,976]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557,630]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608,755]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874,902]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984,622]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469,193]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642,226]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73,628]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794,682]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952,421]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195,581]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753,180]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811,413]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54,796]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667,318]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269,455]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994,16]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917,525]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180,482]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725,991]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286,280]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599,364]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243,264]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633,596]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697,763]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924,667]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983,281]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857,899]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954,94]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578,386]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148,33]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249,41]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160,888]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570,43]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296,735]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958,580]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92,12]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149,401]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74,307]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364,8]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222,749]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519,967]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35,620]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725,118]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446,798]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485,478]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137,140]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308,111]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225,841]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467,432]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205,580]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538,46]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920,830]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219,211]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416,45]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275,599]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296,337]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849,357]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236,318]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887,57]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701,887]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27,214]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858,138]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897,449]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610,665]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500,631]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660,857]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279,317]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720,135]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372,890]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453,732]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722,748]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869,150]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715,221]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119,818]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198,291]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,385,506]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605,214]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708,6]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858,16]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547,850]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170,954]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63,859]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402,205]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464,902]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170,65]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585,260]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798,658]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796,56]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795,783]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943,554]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938,600]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168,302]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755,330]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954,315]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371,402]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229,506]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528,875]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673,569]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103,763]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666,352]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247,620]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795,347]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774,192]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367,718]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345,599]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652,821]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154,121]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911,638]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917,622]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115,248]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333,161]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970,639]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451,290]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268,525]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828,276]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488,804]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300,989]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664,489]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153,922]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468,180]
+ CRUSH rule 0 x 699 [450,244,180,919,183,332,747,453]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68,641]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585,513]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466,687]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796,607]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655,595]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372,253]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923,770]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2,133]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939,331]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733,607]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449,28]
+ CRUSH rule 0 x 711 [227,653,731,150,156,842,534,110]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895,459]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53,839]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875,649]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322,417]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138,197]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237,62]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544,891]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828,975]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214,76]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177,264]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823,833]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985,846]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616,879]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359,983]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966,535]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105,31]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753,760]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572,933]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760,800]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528,890]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368,271]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880,343]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535,627]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690,59]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845,278]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482,564]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429,397]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146,17]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214,494]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863,571]
+ CRUSH rule 0 x 742 [912,581,114,117,730,21,687,81]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451,631]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843,699]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926,314]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450,651]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879,857]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304,999]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383,506]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169,171]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388,17]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88,315]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481,702]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336,724]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192,279]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850,191]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121,690]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165,686]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15,347]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345,69]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373,361]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664,350]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387,775]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70,202]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402,123]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357,310]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251,951]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645,975]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213,527]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961,324]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933,571]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891,959]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783,951]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890,425]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885,976]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396,76]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563,860]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65,954]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772,645]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17,336]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714,954]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812,516]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626,961]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481,198]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632,151]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490,481]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600,528]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194,216]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237,916]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788,164]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124,128]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406,858]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376,812]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811,459]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268,94]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646,451]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596,196]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473,250]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859,994]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941,643]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219,662]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846,53]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834,881]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663,949]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121,157]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640,607]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802,136]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166,929]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23,145]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620,648]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948,202]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675,28]
+ CRUSH rule 0 x 813 [805,114,683,629,801,462,285,450]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401,266]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314,670]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44,822]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260,172]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769,826]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523,770]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535,787]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621,815]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883,112]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282,641]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559,886]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161,998]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910,63]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212,916]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937,595]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669,330]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448,55]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142,157]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656,135]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764,766]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83,483]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537,937]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351,961]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903,29]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983,998]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47,859]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815,109]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226,46]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575,1]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486,199]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899,382]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611,512]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606,237]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363,893]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700,249]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847,463]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358,35]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95,337]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469,511]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774,679]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441,302]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686,702]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224,810]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12,763]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688,61]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463,903]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699,907]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901,487]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490,409]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172,980]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43,15]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850,40]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255,481]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988,730]
+ CRUSH rule 0 x 868 [613,950,712,663,666,460,643,547]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718,431]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54,992]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387,515]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180,800]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199,358]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93,915]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634,435]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322,141]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181,460]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329,591]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145,750]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319,630]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570,109]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974,483]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202,148]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389,381]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174,972]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56,582]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472,947]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806,53]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399,382]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996,561]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706,701]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53,333]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672,320]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648,685]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599,829]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210,529]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12,481]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342,37]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124,653]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380,419]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157,292]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178,624]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66,738]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957,801]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685,6]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30,169]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664,331]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484,391]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686,965]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824,552]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789,255]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226,889]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925,746]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70,280]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126,79]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255,133]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725,707]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583,923]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888,980]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532,124]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633,290]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952,474]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964,384]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584,31]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969,168]
+ CRUSH rule 0 x 926 [968,133,132,144,814,155,709,158]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465,775]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847,752]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744,171]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832,654]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487,839]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530,462]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568,61]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533,116]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772,49]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138,802]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630,308]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286,395]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408,49]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25,883]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964,28]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,171,16]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759,851]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678,915]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952,885]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933,267]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922,868]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81,220]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799,961]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355,220]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829,408]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16,746]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141,619]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129,696]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536,766]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169,751]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393,281]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346,377]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543,519]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855,49]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455,708]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856,477]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485,701]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361,255]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59,229]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929,773]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19,674]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433,520]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518,956]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576,411]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344,520]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754,540]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549,524]
+ CRUSH rule 0 x 974 [545,758,586,596,756,790,116,993]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330,308]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397,58]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783,828]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514,169]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677,173]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398,943]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536,212]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395,198]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927,597]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950,840]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911,208]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864,555]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76,894]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865,47]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155,380]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340,526]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754,928]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117,546]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382,428]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557,378]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158,265]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459,764]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210,698]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786,762]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15,585]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317,102]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739,922]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729,370]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614,783]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891,113]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597,969]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234,945]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5,444]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975,723]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234,383]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23,241]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48,570]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268,647]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808,351]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969,81]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145,11]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456,560]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637,731]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463,926]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768,245]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495,865]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676,322]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871,828]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50,560]
+ rule 0 (data) num_rep 8 result size == 8:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966,750,695]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672,820,782]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527,901,106]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481,169,816]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847,220,87]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964,872,66]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884,726,456]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578,14,543]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462,207,759]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3,569,603]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811,484,296]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380,821,470]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117,393,435]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78,170,333]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855,76,269]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437,72,680]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786,969,378]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935,605,228]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883,394,188]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51,834,690]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975,636,387]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263,267,128]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557,633,812]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477,468,303]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800,309,622]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248,908,624]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501,909,219]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676,560,753]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770,569,359]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719,581,816]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426,522,208]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813,217,750]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428,63,801]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618,288,846]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663,877,177]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75,580,568]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676,524,510]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311,758,201]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929,910,698]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856,248,279]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980,503,287]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930,74,844]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456,713,348]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514,40,261]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160,527,250]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658,876,201]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45,390,546]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959,800,743]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738,47,812]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822,628,696]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83,344,713]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406,281,991]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304,899,629]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882,503,327]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339,902,81]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508,763,108]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77,500,553]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670,67,114]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8,762,842]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643,511,370]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194,277,985]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812,14,55]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45,723,926]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705,268,140]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451,891,654]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862,221,66]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334,656,719]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783,593,814]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705,114,981]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871,361,533]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684,411,114]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587,264,446]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642,529,966]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835,705,839]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633,560,565]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366,258,711]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735,953,188]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197,690,426]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183,917,464]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339,24,581]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51,766,822]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247,963,0]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580,51,939]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66,197,698]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681,618,723]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567,908,514]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38,326,385]
+ CRUSH rule 0 x 87 [997,317,463,626,685,683,909,49,28]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99,737,942]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320,921,812]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412,184,529]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495,299,812]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167,634,781]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343,949,51]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82,422,738]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648,661,46]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204,750,322]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83,542,131]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270,511,586]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37,138,326]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9,168,590]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12,369,9]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923,106,653]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933,231,520]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401,85,139]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434,121,488]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878,493,550]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589,614,814]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631,996,630]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856,296,122]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510,472,270]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18,949,341]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725,413,187]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738,253,213]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598,418,641]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421,39,241]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9,448,816]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725,142,304]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766,210,528]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570,278,867]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49,126,410]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424,868,469]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551,43,590]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877,248,753]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482,254,158]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736,737,508]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689,209,99]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909,809,553]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666,691,899]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149,146,904]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270,425,864]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203,667,528]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922,597,680]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14,444,709]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893,281,226]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712,649,27]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846,890,151]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363,323,153]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400,885,907]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384,58,373]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178,532,883]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193,258,445]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424,820,751]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266,70,210]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697,610,203]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577,348,1]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929,196,880]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514,546,861]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352,820,857]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406,556,532]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484,553,315]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671,883,204]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807,50,206]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942,864,388]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443,98,27]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58,641,186]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271,229,961]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384,446,836]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899,902,956]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209,641,228]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242,999,974]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909,642,188]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148,88,764]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587,854,482]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110,329,361]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524,389,185]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548,676,448]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935,266,883]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667,463,818]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601,909,60]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835,840,548]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100,910,446]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36,356,274]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471,529,359]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780,932,573]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51,211,409]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240,316,337]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756,647,45]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856,992,696]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724,923,450]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736,846,926]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168,224,705]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485,571,796]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109,903,96]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74,45,438]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485,511,14]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369,270,16]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566,378,816]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833,650,761]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37,1,939]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527,237,295]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999,754,339]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123,759,147]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359,721,996]
+ CRUSH rule 0 x 194 [242,473,58,655,653,277,792,887,561]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755,471,134]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413,834,832]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666,316,243]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355,574,781]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960,369,584]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132,271,128]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495,76,598]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153,272,944]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669,676,762]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697,881,64]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776,463,327]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230,367,890]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76,708,101]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96,841,811]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522,185,949]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14,921,544]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922,372,581]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857,894,60]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687,903,620]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579,79,258]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982,826,488]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973,898,556]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264,6,603]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34,497,108]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697,805,976]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686,982,0]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61,559,846]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597,904,897]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465,429,614]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329,320,645]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606,465,354]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876,984,491]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966,217,501]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261,389,281]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262,884,215]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93,880,959]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50,182,541]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106,877,130]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796,835,714]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103,95,634]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471,753,350]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573,128,154]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327,509,325]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894,884,824]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426,427,736]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450,677,659]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413,136,411]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234,756,496]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799,21,13]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785,107,816]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605,837,1]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989,999,138]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590,840,586]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713,237,468]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886,650,827]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270,619,450]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58,354,16]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838,131,387]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797,675,452]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605,453,987]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607,95,121]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189,627,592]
+ CRUSH rule 0 x 257 [825,264,867,869,529,409,291,732,224]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863,427,873]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77,191,669]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533,646,475]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516,136,87]
+ CRUSH rule 0 x 262 [223,1,561,420,229,16,88,534,289]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773,979,719]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715,916,633]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833,987,445]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7,557,76]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972,645,882]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715,536,983]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146,14,414]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476,571,314]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263,138,526]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710,779,161]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982,735,58]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370,990,320]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992,823,321]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634,98,457]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783,648,927]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350,600,283]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952,250,206]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895,914,892]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443,604,619]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939,879,338]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369,544,721]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60,546,39]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582,975,810]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699,627,725]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137,890,487]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195,538,356]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795,377,825]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473,209,59]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557,86,89]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806,911,738]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775,636,232]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459,833,503]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440,73,29]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266,82,671]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232,343,831]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213,286,61]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924,106,63]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904,838,458]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220,432,393]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400,365,697]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561,905,522]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214,944,845]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685,838,3]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761,648,867]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459,250,415]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976,551,126]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857,153,970]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90,855,441]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256,798,743]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497,929,92]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24,81,215]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952,599,222]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749,841,969]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761,725,923]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892,86,214]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290,97,529]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800,868,364]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523,366,891]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868,841,563]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187,380,947]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236,810,969]
+ CRUSH rule 0 x 324 [16,745,511,439,272,513,668,959,845]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462,268,124]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461,509,75]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750,669,296]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33,806,181]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516,713,223]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420,520,395]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624,649,626]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644,821,238]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243,633,309]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408,175,189]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635,367,997]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698,533,683]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956,907,464]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69,11,624]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607,953,125]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707,328,325]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720,503,209]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516,661,985]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660,655,149]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142,114,108]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127,732,290]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419,962,304]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783,661,622]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862,513,112]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795,143,529]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841,164,765]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766,775,518]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899,665,802]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279,616,892]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612,33,122]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985,412,599]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629,228,150]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891,656,196]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874,643,550]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397,52,702]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29,529,156]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643,286,552]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265,35,662]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444,613,919]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203,17,743]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113,373,426]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59,914,521]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685,641,894]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616,93,283]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284,184,718]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850,419,307]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797,547,778]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212,241,346]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128,207,88]
+ CRUSH rule 0 x 374 [191,887,920,928,223,714,961,760,571]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934,619,527]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446,897,460]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654,487,891]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728,860,968]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115,557,165]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255,806,429]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773,496,433]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276,56,278]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29,197,822]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488,435,223]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910,329,396]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232,692,742]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164,934,297]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474,267,640]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340,222,74]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852,196,469]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,879,136,818]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901,82,695]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652,543,10]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15,591,420]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126,179,607]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726,935,278]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996,558,602]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349,672,826]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749,410,815]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536,592,886]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79,32,343]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658,449,886]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202,650,501]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299,577,563]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776,296,19]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718,578,580]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72,345,709]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246,695,76]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213,806,168]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949,507,361]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313,351,497]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560,155,661]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618,907,9]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97,124,229]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694,332,572]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889,344,838]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138,374,470]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848,972,781]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823,884,832]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506,670,285]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764,274,623]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839,547,599]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905,588,312]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785,395,884]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80,236,988]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791,565,352]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334,588,571]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225,94,303]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857,562,976]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859,699,91]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194,366,279]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513,591,599]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52,897,705]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644,646,747]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371,731,906]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10,976,284]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443,23,696]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755,841,94]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414,152,331]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800,174,117]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205,236,86]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864,799,762]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774,350,37]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182,145,347]
+ CRUSH rule 0 x 445 [485,482,528,209,964,753,554,931,638]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314,714,212]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58,531,827]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807,688,21]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258,548,905]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721,31,312]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878,33,3]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604,49,922]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514,5,494]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106,103,511]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295,967,132]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504,96,890]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762,975,485]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399,558,15]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147,483,517]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841,512,70]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385,797,917]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240,911,375]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538,111,232]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789,0,653]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137,466,531]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659,35,797]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187,640,601]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201,249,555]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681,683,551]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558,71,676]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256,978,409]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809,56,476]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756,663,167]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446,333,245]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237,764,682]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984,226,333]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753,794,237]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941,843,751]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158,489,515]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371,807,687]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135,375,613]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731,233,640]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448,437,219]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283,170,278]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866,661,581]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621,955,400]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857,782,24]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924,965,25]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154,478,512]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566,779,845]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188,563,490]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591,423,668]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231,308,474]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389,819,956]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823,789,217]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129,763,39]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542,745,131]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949,173,398]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440,509,295]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930,573,357]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880,279,616]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490,852,962]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262,926,316]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758,104,831]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764,558,891]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898,161,78]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591,702,346]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313,282,207]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798,808,139]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786,152,921]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600,968,355]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899,451,415]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52,185,197]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374,985,944]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481,820,517]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453,660,93]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519,737,20]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625,434,527]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375,328,3]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558,956,664]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495,354,665]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753,624,769]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834,212,906]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8,550,769]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45,478,911]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853,643,625]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270,35,809]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651,368,118]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278,926,280]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577,509,195]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925,474,669]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949,374,988]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54,931,4]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206,682,627]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859,30,61]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148,668,436]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152,797,492]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37,485,88]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528,722,59]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95,789,348]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738,277,225]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137,934,547]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324,134,839]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250,362,614]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117,172,764]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699,610,785]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428,267,229]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517,684,154]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37,639,934]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915,570,546]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161,901,342]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57,217,21]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218,916,561]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693,953,606]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623,659,403]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830,762,691]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593,267,17]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124,107,766]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185,356,87]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887,376,658]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700,339,687]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62,817,766]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903,900,812]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641,751,930]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738,873,47]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184,127,448]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267,639,646]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398,521,59]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494,624,352]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533,674,23]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329,848,235]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33,312,202]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878,978,1]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333,841,193]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389,486,10]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177,545,839]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700,521,825]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487,976,977]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557,630,468]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608,755,326]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874,902,958]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984,622,175]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469,193,737]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642,226,122]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73,628,884]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794,682,127]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952,421,875]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195,581,649]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753,180,160]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811,413,436]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54,796,369]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667,318,496]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269,455,168]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994,16,777]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917,525,945]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180,482,449]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725,991,955]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286,280,507]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599,364,546]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243,264,31]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633,596,988]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697,763,442]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924,667,356]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983,281,292]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857,899,349]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954,94,88]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578,386,226]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148,33,420]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249,41,778]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160,888,889]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570,43,362]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296,735,245]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958,580,197]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92,12,62]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149,401,14]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74,307,976]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364,8,342]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222,749,697]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519,967,737]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35,620,268]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725,118,875]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446,798,869]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485,478,185]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137,140,488]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308,111,207]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225,841,868]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467,432,944]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205,580,972]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538,46,300]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920,830,171]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219,211,157]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416,45,624]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275,599,219]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296,337,676]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849,357,317]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236,318,228]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887,57,16]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701,887,355]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27,214,226]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858,138,726]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897,449,42]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610,665,299]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500,631,833]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660,857,123]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279,317,414]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720,135,369]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372,890,30]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453,732,262]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722,748,599]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869,150,743]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715,221,31]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119,818,459]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198,291,71]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,385,506,546]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605,214,833]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708,6,746]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858,16,128]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547,850,499]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170,954,22]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63,859,215]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402,205,609]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464,902,276]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170,65,906]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585,260,254]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798,658,28]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796,56,736]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795,783,386]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943,554,395]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938,600,517]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168,302,681]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755,330,584]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954,315,735]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371,402,725]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229,506,565]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528,875,982]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673,569,701]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103,763,527]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666,352,576]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247,620,437]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795,347,831]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774,192,454]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367,718,345]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345,599,30]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652,821,641]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154,121,843]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911,638,690]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917,622,156]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115,248,566]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333,161,74]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970,639,835]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451,290,789]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268,525,425]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828,276,264]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488,804,501]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300,989,804]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664,489,598]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153,922,986]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468,180,783]
+ CRUSH rule 0 x 699 [450,244,180,919,183,332,747,453,519]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68,641,109]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585,513,907]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466,687,742]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796,607,894]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655,595,888]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372,253,807]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923,770,516]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2,133,256]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939,331,41]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733,607,763]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449,28,731]
+ CRUSH rule 0 x 711 [227,653,731,150,156,842,534,110,639]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895,459,341]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53,839,56]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875,649,23]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322,417,973]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138,197,32]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237,62,327]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544,891,210]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828,975,119]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214,76,870]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177,264,867]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823,833,1]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985,846,77]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616,879,480]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359,983,720]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966,535,669]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105,31,506]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753,760,254]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572,933,434]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760,800,945]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528,890,625]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368,271,778]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880,343,590]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535,627,224]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690,59,294]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845,278,559]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482,564,259]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429,397,872]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146,17,314]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214,494,593]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863,571,876]
+ CRUSH rule 0 x 742 [912,581,114,117,730,21,687,81,145]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451,631,911]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843,699,24]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926,314,771]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450,651,105]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879,857,727]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304,999,685]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383,506,570]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169,171,9]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388,17,892]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88,315,728]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481,702,431]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336,724,262]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192,279,371]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850,191,991]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121,690,544]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165,686,449]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15,347,311]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345,69,39]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373,361,684]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664,350,339]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387,775,797]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70,202,557]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402,123,902]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357,310,890]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251,951,675]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645,975,947]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213,527,546]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961,324,309]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933,571,822]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891,959,487]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783,951,982]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890,425,599]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885,976,936]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396,76,680]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563,860,388]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65,954,125]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772,645,254]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17,336,147]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714,954,271]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812,516,774]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626,961,576]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481,198,171]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632,151,586]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490,481,627]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600,528,966]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194,216,486]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237,916,374]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788,164,691]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124,128,742]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406,858,694]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376,812,886]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811,459,344]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268,94,648]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646,451,67]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596,196,93]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473,250,839]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859,994,831]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941,643,398]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219,662,142]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846,53,783]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834,881,23]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663,949,380]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121,157,934]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640,607,790]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802,136,305]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166,929,234]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23,145,184]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620,648,433]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948,202,521]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675,28,263]
+ CRUSH rule 0 x 813 [805,114,683,629,801,462,285,450,948]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401,266,905]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314,670,928]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44,822,332]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260,172,193]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769,826,519]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523,770,56]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535,787,267]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621,815,313]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883,112,601]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282,641,216]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559,886,753]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161,998,370]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910,63,288]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212,916,692]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937,595,935]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669,330,822]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448,55,965]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142,157,957]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656,135,341]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764,766,853]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83,483,811]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537,937,101]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351,961,52]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903,29,454]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983,998,320]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47,859,402]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815,109,762]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226,46,918]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575,1,312]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486,199,966]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899,382,851]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611,512,855]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606,237,414]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363,893,592]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700,249,214]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847,463,995]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358,35,913]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95,337,677]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469,511,999]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774,679,616]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441,302,437]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686,702,919]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224,810,404]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12,763,594]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688,61,545]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463,903,272]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699,907,532]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901,487,725]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490,409,15]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172,980,772]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43,15,29]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850,40,170]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255,481,435]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988,730,753]
+ CRUSH rule 0 x 868 [613,950,712,663,666,460,643,547,734]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718,431,968]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54,992,726]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387,515,98]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180,800,521]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199,358,805]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93,915,801]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634,435,778]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322,141,82]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181,460,459]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329,591,59]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145,750,54]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319,630,888]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570,109,896]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974,483,412]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202,148,129]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389,381,347]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174,972,544]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56,582,915]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472,947,35]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806,53,385]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399,382,761]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996,561,485]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706,701,501]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53,333,458]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672,320,394]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648,685,895]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599,829,809]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210,529,491]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12,481,539]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342,37,303]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124,653,574]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380,419,667]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157,292,724]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178,624,733]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66,738,419]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957,801,74]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685,6,689]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30,169,506]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664,331,184]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484,391,841]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686,965,515]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824,552,717]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789,255,896]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226,889,69]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925,746,664]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70,280,837]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126,79,652]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255,133,892]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725,707,47]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583,923,863]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888,980,33]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532,124,911]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633,290,155]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952,474,67]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964,384,454]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584,31,151]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969,168,500]
+ CRUSH rule 0 x 926 [968,133,132,144,814,155,709,158,96]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465,775,725]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847,752,780]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744,171,144]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832,654,567]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487,839,776]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530,462,202]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568,61,945]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533,116,254]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772,49,567]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138,802,885]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630,308,973]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286,395,927]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408,49,977]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25,883,867]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964,28,633]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,171,16,440]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759,851,617]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678,915,619]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952,885,987]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933,267,846]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922,868,413]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81,220,725]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799,961,564]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355,220,528]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829,408,568]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16,746,835]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141,619,234]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129,696,115]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536,766,579]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169,751,667]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393,281,227]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346,377,425]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543,519,496]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855,49,548]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455,708,649]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856,477,895]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485,701,76]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361,255,507]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59,229,819]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929,773,928]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19,674,395]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433,520,916]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518,956,398]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576,411,416]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344,520,546]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754,540,79]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549,524,109]
+ CRUSH rule 0 x 974 [545,758,586,596,756,790,116,993,644]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330,308,744]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397,58,57]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783,828,409]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514,169,99]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677,173,903]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398,943,764]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536,212,247]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395,198,100]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927,597,103]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950,840,427]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911,208,91]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864,555,687]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76,894,480]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865,47,116]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155,380,797]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340,526,728]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754,928,288]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117,546,798]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382,428,451]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557,378,936]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158,265,110]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459,764,859]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210,698,273]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786,762,444]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15,585,898]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317,102,200]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739,922,343]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729,370,326]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614,783,865]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891,113,424]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597,969,911]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234,945,598]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5,444,795]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975,723,909]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234,383,604]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23,241,838]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48,570,639]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268,647,669]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808,351,572]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969,81,586]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145,11,965]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456,560,935]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637,731,738]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463,926,475]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768,245,337]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495,865,698]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676,322,671]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871,828,440]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50,560,595]
+ rule 0 (data) num_rep 9 result size == 9:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [36,705,536,450,604,380,966,750,695,503]
+ CRUSH rule 0 x 1 [876,250,334,633,744,843,672,820,782,802]
+ CRUSH rule 0 x 2 [292,832,53,392,386,787,527,901,106,273]
+ CRUSH rule 0 x 3 [623,387,124,998,749,211,481,169,816,732]
+ CRUSH rule 0 x 4 [61,334,710,4,994,982,847,220,87,254]
+ CRUSH rule 0 x 5 [946,557,713,664,141,817,964,872,66,161]
+ CRUSH rule 0 x 6 [576,668,212,163,732,381,884,726,456,796]
+ CRUSH rule 0 x 7 [645,753,906,393,341,44,578,14,543,287]
+ CRUSH rule 0 x 8 [243,6,863,781,211,100,462,207,759,701]
+ CRUSH rule 0 x 9 [22,578,251,410,297,430,3,569,603,47]
+ CRUSH rule 0 x 10 [758,828,360,477,821,801,811,484,296,320]
+ CRUSH rule 0 x 11 [769,120,124,527,119,504,380,821,470,230]
+ CRUSH rule 0 x 12 [780,364,689,755,675,199,117,393,435,514]
+ CRUSH rule 0 x 13 [557,18,351,719,742,780,78,170,333,295]
+ CRUSH rule 0 x 14 [59,561,249,461,971,835,855,76,269,673]
+ CRUSH rule 0 x 15 [718,928,993,21,76,313,437,72,680,761]
+ CRUSH rule 0 x 16 [673,632,841,954,788,90,786,969,378,246]
+ CRUSH rule 0 x 17 [648,43,560,514,142,289,935,605,228,737]
+ CRUSH rule 0 x 18 [654,219,181,568,381,253,883,394,188,459]
+ CRUSH rule 0 x 19 [850,545,377,848,863,543,51,834,690,375]
+ CRUSH rule 0 x 20 [717,785,974,5,225,552,975,636,387,600]
+ CRUSH rule 0 x 21 [420,57,519,306,312,983,263,267,128,828]
+ CRUSH rule 0 x 22 [503,998,193,821,634,684,557,633,812,521]
+ CRUSH rule 0 x 23 [411,663,168,110,899,488,477,468,303,367]
+ CRUSH rule 0 x 24 [266,861,353,1,456,128,800,309,622,673]
+ CRUSH rule 0 x 25 [760,483,818,600,509,951,248,908,624,643]
+ CRUSH rule 0 x 26 [903,24,573,718,112,694,501,909,219,686]
+ CRUSH rule 0 x 27 [946,188,289,510,687,827,676,560,753,77]
+ CRUSH rule 0 x 28 [69,312,73,198,256,629,770,569,359,733]
+ CRUSH rule 0 x 29 [844,883,337,628,496,405,719,581,816,349]
+ CRUSH rule 0 x 30 [621,18,613,794,910,936,426,522,208,699]
+ CRUSH rule 0 x 31 [784,943,814,539,962,392,813,217,750,155]
+ CRUSH rule 0 x 32 [173,374,369,972,315,83,428,63,801,735]
+ CRUSH rule 0 x 33 [698,336,357,966,582,407,618,288,846,659]
+ CRUSH rule 0 x 34 [168,836,210,798,904,190,663,877,177,567]
+ CRUSH rule 0 x 35 [274,509,534,818,912,671,75,580,568,523]
+ CRUSH rule 0 x 36 [318,215,153,628,87,407,676,524,510,480]
+ CRUSH rule 0 x 37 [173,604,109,935,203,401,311,758,201,999]
+ CRUSH rule 0 x 38 [708,444,683,604,722,900,929,910,698,386]
+ CRUSH rule 0 x 39 [662,198,417,680,226,342,856,248,279,416]
+ CRUSH rule 0 x 40 [620,801,414,78,560,766,980,503,287,564]
+ CRUSH rule 0 x 41 [811,264,177,127,148,791,930,74,844,943]
+ CRUSH rule 0 x 42 [863,179,527,660,133,529,456,713,348,311]
+ CRUSH rule 0 x 43 [686,822,988,228,791,549,514,40,261,223]
+ CRUSH rule 0 x 44 [396,222,46,841,536,140,160,527,250,247]
+ CRUSH rule 0 x 45 [991,694,253,142,54,422,658,876,201,45]
+ CRUSH rule 0 x 46 [420,909,184,285,508,458,45,390,546,908]
+ CRUSH rule 0 x 47 [467,211,605,207,241,881,959,800,743,161]
+ CRUSH rule 0 x 48 [955,329,368,168,698,787,738,47,812,166]
+ CRUSH rule 0 x 49 [974,891,931,29,813,506,822,628,696,407]
+ CRUSH rule 0 x 50 [870,441,691,823,761,6,83,344,713,857]
+ CRUSH rule 0 x 51 [182,930,25,936,97,260,406,281,991,336]
+ CRUSH rule 0 x 52 [704,812,894,794,481,37,304,899,629,701]
+ CRUSH rule 0 x 53 [185,713,631,280,345,558,882,503,327,402]
+ CRUSH rule 0 x 54 [270,441,100,82,983,930,339,902,81,239]
+ CRUSH rule 0 x 55 [895,734,958,793,651,572,508,763,108,185]
+ CRUSH rule 0 x 56 [564,963,683,324,40,189,77,500,553,417]
+ CRUSH rule 0 x 57 [738,130,208,973,498,861,670,67,114,685]
+ CRUSH rule 0 x 58 [524,113,806,903,531,334,8,762,842,884]
+ CRUSH rule 0 x 59 [408,337,668,529,34,384,643,511,370,336]
+ CRUSH rule 0 x 60 [228,790,857,309,616,895,194,277,985,554]
+ CRUSH rule 0 x 61 [154,843,717,467,883,536,812,14,55,752]
+ CRUSH rule 0 x 62 [594,811,549,276,693,917,45,723,926,180]
+ CRUSH rule 0 x 63 [646,67,884,925,941,434,705,268,140,942]
+ CRUSH rule 0 x 64 [175,542,155,837,594,197,451,891,654,294]
+ CRUSH rule 0 x 65 [745,619,131,867,269,62,862,221,66,354]
+ CRUSH rule 0 x 66 [275,468,23,35,328,432,334,656,719,810]
+ CRUSH rule 0 x 67 [246,958,524,493,636,227,783,593,814,970]
+ CRUSH rule 0 x 68 [711,473,403,228,835,126,705,114,981,267]
+ CRUSH rule 0 x 69 [493,924,850,939,950,105,871,361,533,433]
+ CRUSH rule 0 x 70 [30,499,644,33,804,654,684,411,114,42]
+ CRUSH rule 0 x 71 [984,883,574,716,575,391,587,264,446,572]
+ CRUSH rule 0 x 72 [71,286,942,363,628,632,642,529,966,919]
+ CRUSH rule 0 x 73 [922,618,3,371,464,442,835,705,839,745]
+ CRUSH rule 0 x 74 [629,414,185,573,678,338,633,560,565,410]
+ CRUSH rule 0 x 75 [222,20,174,820,312,361,366,258,711,657]
+ CRUSH rule 0 x 76 [262,366,339,290,718,143,735,953,188,516]
+ CRUSH rule 0 x 77 [638,469,992,280,773,892,197,690,426,681]
+ CRUSH rule 0 x 78 [324,511,788,7,308,228,183,917,464,518]
+ CRUSH rule 0 x 79 [577,990,64,94,447,924,339,24,581,969]
+ CRUSH rule 0 x 80 [501,95,278,903,631,842,51,766,822,687]
+ CRUSH rule 0 x 81 [506,812,9,698,173,664,247,963,0,732]
+ CRUSH rule 0 x 82 [222,145,80,785,835,745,580,51,939,278]
+ CRUSH rule 0 x 83 [71,634,61,91,856,529,66,197,698,318]
+ CRUSH rule 0 x 84 [49,761,773,368,318,708,681,618,723,516]
+ CRUSH rule 0 x 85 [985,896,708,861,325,307,567,908,514,355]
+ CRUSH rule 0 x 86 [537,745,93,524,466,356,38,326,385,899]
+ CRUSH rule 0 x 87 [997,317,463,626,685,683,909,49,28,698]
+ CRUSH rule 0 x 88 [957,350,890,857,375,176,99,737,942,647]
+ CRUSH rule 0 x 89 [399,730,148,314,159,982,320,921,812,908]
+ CRUSH rule 0 x 90 [943,706,683,267,579,141,412,184,529,127]
+ CRUSH rule 0 x 91 [22,368,149,928,140,529,495,299,812,743]
+ CRUSH rule 0 x 92 [532,424,426,773,623,197,167,634,781,242]
+ CRUSH rule 0 x 93 [218,489,405,681,549,201,343,949,51,732]
+ CRUSH rule 0 x 94 [181,96,102,515,776,365,82,422,738,933]
+ CRUSH rule 0 x 95 [343,957,820,139,334,37,648,661,46,112]
+ CRUSH rule 0 x 96 [861,270,87,797,0,245,204,750,322,75]
+ CRUSH rule 0 x 97 [459,706,45,328,274,605,83,542,131,240]
+ CRUSH rule 0 x 98 [327,867,353,948,728,280,270,511,586,230]
+ CRUSH rule 0 x 99 [974,133,468,906,235,988,37,138,326,603]
+ CRUSH rule 0 x 100 [32,445,547,371,960,885,9,168,590,873]
+ CRUSH rule 0 x 101 [142,90,337,950,970,570,12,369,9,872]
+ CRUSH rule 0 x 102 [172,129,139,22,403,867,923,106,653,999]
+ CRUSH rule 0 x 103 [630,47,161,356,911,421,933,231,520,303]
+ CRUSH rule 0 x 104 [758,133,278,11,947,799,401,85,139,855]
+ CRUSH rule 0 x 105 [843,604,47,33,401,632,434,121,488,840]
+ CRUSH rule 0 x 106 [28,681,193,679,990,343,878,493,550,484]
+ CRUSH rule 0 x 107 [74,320,85,819,315,253,589,614,814,970]
+ CRUSH rule 0 x 108 [875,593,575,517,107,153,631,996,630,597]
+ CRUSH rule 0 x 109 [411,985,811,720,198,666,856,296,122,477]
+ CRUSH rule 0 x 110 [440,774,799,660,715,167,510,472,270,753]
+ CRUSH rule 0 x 111 [405,742,276,359,936,360,18,949,341,837]
+ CRUSH rule 0 x 112 [143,181,922,545,185,303,725,413,187,840]
+ CRUSH rule 0 x 113 [153,846,160,903,789,897,738,253,213,541]
+ CRUSH rule 0 x 114 [804,892,939,20,312,692,598,418,641,891]
+ CRUSH rule 0 x 115 [588,508,958,580,232,722,421,39,241,881]
+ CRUSH rule 0 x 116 [327,148,637,486,712,464,9,448,816,609]
+ CRUSH rule 0 x 117 [95,594,989,131,714,275,725,142,304,591]
+ CRUSH rule 0 x 118 [80,957,897,239,359,432,766,210,528,252]
+ CRUSH rule 0 x 119 [386,932,951,768,679,300,570,278,867,489]
+ CRUSH rule 0 x 120 [366,312,653,936,71,241,49,126,410,33]
+ CRUSH rule 0 x 121 [129,154,847,16,471,481,424,868,469,183]
+ CRUSH rule 0 x 122 [873,1,110,939,90,412,551,43,590,51]
+ CRUSH rule 0 x 123 [533,415,789,600,713,800,877,248,753,395]
+ CRUSH rule 0 x 124 [461,691,898,723,957,759,482,254,158,641]
+ CRUSH rule 0 x 125 [342,599,830,402,615,994,736,737,508,150]
+ CRUSH rule 0 x 126 [819,781,822,548,279,255,689,209,99,479]
+ CRUSH rule 0 x 127 [437,893,585,707,353,189,909,809,553,785]
+ CRUSH rule 0 x 128 [679,994,982,550,991,324,666,691,899,665]
+ CRUSH rule 0 x 129 [380,685,947,302,698,144,149,146,904,70]
+ CRUSH rule 0 x 130 [992,52,466,867,998,777,270,425,864,38]
+ CRUSH rule 0 x 131 [469,90,208,599,829,656,203,667,528,387]
+ CRUSH rule 0 x 132 [571,250,316,535,54,418,922,597,680,25]
+ CRUSH rule 0 x 133 [964,728,329,902,108,118,14,444,709,592]
+ CRUSH rule 0 x 134 [999,19,716,963,323,559,893,281,226,739]
+ CRUSH rule 0 x 135 [634,101,52,938,413,573,712,649,27,274]
+ CRUSH rule 0 x 136 [114,889,692,768,694,279,846,890,151,872]
+ CRUSH rule 0 x 137 [839,8,959,280,922,870,363,323,153,238]
+ CRUSH rule 0 x 138 [967,949,138,451,292,548,400,885,907,214]
+ CRUSH rule 0 x 139 [308,711,736,247,632,126,384,58,373,121]
+ CRUSH rule 0 x 140 [764,936,926,55,331,115,178,532,883,380]
+ CRUSH rule 0 x 141 [423,302,112,216,603,873,193,258,445,451]
+ CRUSH rule 0 x 142 [252,821,715,340,635,668,424,820,751,746]
+ CRUSH rule 0 x 143 [33,808,518,477,325,316,266,70,210,61]
+ CRUSH rule 0 x 144 [472,88,969,162,401,771,697,610,203,382]
+ CRUSH rule 0 x 145 [242,208,252,604,266,743,577,348,1,323]
+ CRUSH rule 0 x 146 [290,70,570,384,934,856,929,196,880,458]
+ CRUSH rule 0 x 147 [447,352,657,493,467,918,514,546,861,796]
+ CRUSH rule 0 x 148 [212,644,432,658,109,275,352,820,857,282]
+ CRUSH rule 0 x 149 [9,775,87,35,260,646,406,556,532,750]
+ CRUSH rule 0 x 150 [166,456,582,144,324,340,484,553,315,504]
+ CRUSH rule 0 x 151 [811,875,307,20,782,229,671,883,204,12]
+ CRUSH rule 0 x 152 [449,617,223,9,182,407,807,50,206,368]
+ CRUSH rule 0 x 153 [523,537,695,627,959,613,942,864,388,639]
+ CRUSH rule 0 x 154 [208,559,874,597,243,706,443,98,27,120]
+ CRUSH rule 0 x 155 [569,325,192,296,367,848,58,641,186,553]
+ CRUSH rule 0 x 156 [488,121,521,213,595,837,271,229,961,163]
+ CRUSH rule 0 x 157 [140,723,633,260,487,856,384,446,836,917]
+ CRUSH rule 0 x 158 [786,451,320,239,667,632,899,902,956,424]
+ CRUSH rule 0 x 159 [134,664,517,821,667,944,209,641,228,213]
+ CRUSH rule 0 x 160 [690,112,414,990,183,590,242,999,974,652]
+ CRUSH rule 0 x 161 [324,912,397,423,991,284,909,642,188,143]
+ CRUSH rule 0 x 162 [748,567,284,183,463,336,148,88,764,40]
+ CRUSH rule 0 x 163 [575,499,31,816,749,737,587,854,482,522]
+ CRUSH rule 0 x 164 [314,489,308,326,51,568,110,329,361,743]
+ CRUSH rule 0 x 165 [116,209,750,53,813,640,524,389,185,893]
+ CRUSH rule 0 x 166 [352,706,701,810,718,527,548,676,448,991]
+ CRUSH rule 0 x 167 [27,743,174,142,551,1,935,266,883,77]
+ CRUSH rule 0 x 168 [953,898,880,660,500,799,667,463,818,819]
+ CRUSH rule 0 x 169 [912,147,266,547,331,770,601,909,60,255]
+ CRUSH rule 0 x 170 [421,515,828,844,151,981,835,840,548,588]
+ CRUSH rule 0 x 171 [488,584,880,964,936,196,100,910,446,541]
+ CRUSH rule 0 x 172 [366,443,957,66,162,693,36,356,274,976]
+ CRUSH rule 0 x 173 [863,291,625,287,158,496,471,529,359,571]
+ CRUSH rule 0 x 174 [263,555,650,410,339,616,780,932,573,814]
+ CRUSH rule 0 x 175 [875,961,361,575,33,109,51,211,409,865]
+ CRUSH rule 0 x 176 [745,83,701,680,250,420,240,316,337,361]
+ CRUSH rule 0 x 177 [128,244,41,123,422,902,756,647,45,752]
+ CRUSH rule 0 x 178 [155,41,264,777,314,564,856,992,696,784]
+ CRUSH rule 0 x 179 [593,833,202,183,971,38,724,923,450,340]
+ CRUSH rule 0 x 180 [154,734,17,831,824,522,736,846,926,129]
+ CRUSH rule 0 x 181 [289,675,723,800,166,712,168,224,705,185]
+ CRUSH rule 0 x 182 [730,931,560,209,943,261,485,571,796,587]
+ CRUSH rule 0 x 183 [639,237,794,815,827,400,109,903,96,526]
+ CRUSH rule 0 x 184 [704,312,685,645,691,778,74,45,438,26]
+ CRUSH rule 0 x 185 [97,100,762,82,999,542,485,511,14,329]
+ CRUSH rule 0 x 186 [26,665,554,215,280,421,369,270,16,920]
+ CRUSH rule 0 x 187 [649,14,740,494,402,684,566,378,816,553]
+ CRUSH rule 0 x 188 [682,695,590,743,927,945,833,650,761,468]
+ CRUSH rule 0 x 189 [325,693,726,51,448,169,37,1,939,463]
+ CRUSH rule 0 x 190 [399,933,136,955,57,504,527,237,295,716]
+ CRUSH rule 0 x 191 [629,533,17,126,60,146,999,754,339,271]
+ CRUSH rule 0 x 192 [503,578,38,492,222,251,123,759,147,99]
+ CRUSH rule 0 x 193 [546,333,651,678,823,652,359,721,996,318]
+ CRUSH rule 0 x 194 [242,473,58,655,653,277,792,887,561,449]
+ CRUSH rule 0 x 195 [625,719,135,81,636,513,755,471,134,744]
+ CRUSH rule 0 x 196 [357,114,125,867,250,522,413,834,832,368]
+ CRUSH rule 0 x 197 [306,954,453,873,211,334,666,316,243,320]
+ CRUSH rule 0 x 198 [863,791,311,911,206,61,355,574,781,550]
+ CRUSH rule 0 x 199 [935,906,929,252,893,75,960,369,584,612]
+ CRUSH rule 0 x 200 [373,774,229,454,909,611,132,271,128,632]
+ CRUSH rule 0 x 201 [659,320,477,313,779,16,495,76,598,301]
+ CRUSH rule 0 x 202 [260,433,524,880,223,818,153,272,944,741]
+ CRUSH rule 0 x 203 [36,239,675,971,703,209,669,676,762,200]
+ CRUSH rule 0 x 204 [92,516,993,728,279,478,697,881,64,107]
+ CRUSH rule 0 x 205 [68,395,473,45,683,662,776,463,327,721]
+ CRUSH rule 0 x 206 [570,530,642,380,311,398,230,367,890,953]
+ CRUSH rule 0 x 207 [834,457,850,917,456,296,76,708,101,928]
+ CRUSH rule 0 x 208 [927,484,640,976,803,626,96,841,811,979]
+ CRUSH rule 0 x 209 [878,66,58,940,48,233,522,185,949,590]
+ CRUSH rule 0 x 210 [572,981,484,29,0,426,14,921,544,4]
+ CRUSH rule 0 x 211 [107,597,780,857,895,57,922,372,581,629]
+ CRUSH rule 0 x 212 [389,107,838,624,698,562,857,894,60,426]
+ CRUSH rule 0 x 213 [497,717,567,728,905,134,687,903,620,572]
+ CRUSH rule 0 x 214 [798,65,254,572,32,393,579,79,258,42]
+ CRUSH rule 0 x 215 [233,419,283,638,520,891,982,826,488,314]
+ CRUSH rule 0 x 216 [494,464,742,523,459,174,973,898,556,293]
+ CRUSH rule 0 x 217 [352,396,309,938,66,41,264,6,603,317]
+ CRUSH rule 0 x 218 [895,864,988,650,593,740,34,497,108,180]
+ CRUSH rule 0 x 219 [222,534,277,242,658,482,697,805,976,758]
+ CRUSH rule 0 x 220 [281,19,584,563,858,965,686,982,0,32]
+ CRUSH rule 0 x 221 [64,928,963,130,312,394,61,559,846,994]
+ CRUSH rule 0 x 222 [40,544,161,199,861,644,597,904,897,376]
+ CRUSH rule 0 x 223 [645,556,159,417,46,135,465,429,614,711]
+ CRUSH rule 0 x 224 [647,165,957,263,961,576,329,320,645,829]
+ CRUSH rule 0 x 225 [219,714,858,747,461,175,606,465,354,404]
+ CRUSH rule 0 x 226 [372,511,181,277,695,404,876,984,491,784]
+ CRUSH rule 0 x 227 [925,156,714,863,257,74,966,217,501,536]
+ CRUSH rule 0 x 228 [682,404,839,263,521,195,261,389,281,467]
+ CRUSH rule 0 x 229 [880,838,770,891,236,542,262,884,215,687]
+ CRUSH rule 0 x 230 [328,659,916,468,646,572,93,880,959,111]
+ CRUSH rule 0 x 231 [320,383,669,109,627,621,50,182,541,483]
+ CRUSH rule 0 x 232 [924,846,394,319,43,519,106,877,130,387]
+ CRUSH rule 0 x 233 [948,652,575,838,498,395,796,835,714,751]
+ CRUSH rule 0 x 234 [484,943,42,575,936,180,103,95,634,844]
+ CRUSH rule 0 x 235 [750,65,590,168,870,308,471,753,350,224]
+ CRUSH rule 0 x 236 [551,787,490,136,370,833,573,128,154,326]
+ CRUSH rule 0 x 237 [390,157,166,251,752,75,327,509,325,245]
+ CRUSH rule 0 x 238 [570,6,989,707,514,905,894,884,824,343]
+ CRUSH rule 0 x 239 [729,959,376,975,496,49,426,427,736,836]
+ CRUSH rule 0 x 240 [981,241,156,767,631,576,450,677,659,183]
+ CRUSH rule 0 x 241 [310,816,641,177,996,454,413,136,411,549]
+ CRUSH rule 0 x 242 [161,63,642,837,763,458,234,756,496,779]
+ CRUSH rule 0 x 243 [180,394,33,683,189,419,799,21,13,874]
+ CRUSH rule 0 x 244 [52,174,685,189,78,310,785,107,816,89]
+ CRUSH rule 0 x 245 [523,121,915,84,386,409,605,837,1,141]
+ CRUSH rule 0 x 246 [362,893,390,487,817,88,989,999,138,674]
+ CRUSH rule 0 x 247 [382,184,116,34,143,15,590,840,586,594]
+ CRUSH rule 0 x 248 [129,114,852,469,359,291,713,237,468,340]
+ CRUSH rule 0 x 249 [159,683,91,856,475,369,886,650,827,663]
+ CRUSH rule 0 x 250 [404,945,569,955,228,910,270,619,450,707]
+ CRUSH rule 0 x 251 [661,225,738,757,37,642,58,354,16,905]
+ CRUSH rule 0 x 252 [961,226,542,103,945,885,838,131,387,664]
+ CRUSH rule 0 x 253 [651,97,225,364,189,248,797,675,452,129]
+ CRUSH rule 0 x 254 [123,33,741,692,599,11,605,453,987,316]
+ CRUSH rule 0 x 255 [314,649,891,855,517,344,607,95,121,784]
+ CRUSH rule 0 x 256 [315,215,651,126,470,849,189,627,592,241]
+ CRUSH rule 0 x 257 [825,264,867,869,529,409,291,732,224,841]
+ CRUSH rule 0 x 258 [624,789,370,723,131,982,863,427,873,223]
+ CRUSH rule 0 x 259 [602,542,70,563,947,723,77,191,669,61]
+ CRUSH rule 0 x 260 [717,878,43,56,377,481,533,646,475,686]
+ CRUSH rule 0 x 261 [145,517,20,903,786,939,516,136,87,410]
+ CRUSH rule 0 x 262 [223,1,561,420,229,16,88,534,289,498]
+ CRUSH rule 0 x 263 [462,211,405,508,787,669,773,979,719,421]
+ CRUSH rule 0 x 264 [654,471,266,662,135,564,715,916,633,121]
+ CRUSH rule 0 x 265 [302,794,704,798,659,487,833,987,445,23]
+ CRUSH rule 0 x 266 [202,132,884,209,551,984,7,557,76,987]
+ CRUSH rule 0 x 267 [282,938,657,113,672,993,972,645,882,451]
+ CRUSH rule 0 x 268 [338,309,356,278,928,797,715,536,983,688]
+ CRUSH rule 0 x 269 [738,122,266,200,894,118,146,14,414,236]
+ CRUSH rule 0 x 270 [707,982,946,196,407,804,476,571,314,538]
+ CRUSH rule 0 x 271 [705,432,364,735,512,595,263,138,526,607]
+ CRUSH rule 0 x 272 [756,545,942,56,542,449,710,779,161,222]
+ CRUSH rule 0 x 273 [197,502,527,721,239,648,982,735,58,1]
+ CRUSH rule 0 x 274 [992,44,653,573,527,702,370,990,320,52]
+ CRUSH rule 0 x 275 [544,789,170,434,23,926,992,823,321,784]
+ CRUSH rule 0 x 276 [658,467,577,268,336,5,634,98,457,487]
+ CRUSH rule 0 x 277 [143,490,880,483,928,272,783,648,927,285]
+ CRUSH rule 0 x 278 [492,647,355,282,834,64,350,600,283,422]
+ CRUSH rule 0 x 279 [517,792,604,987,527,894,952,250,206,714]
+ CRUSH rule 0 x 280 [825,740,27,848,514,750,895,914,892,149]
+ CRUSH rule 0 x 281 [224,629,120,562,616,200,443,604,619,638]
+ CRUSH rule 0 x 282 [298,661,380,416,35,585,939,879,338,786]
+ CRUSH rule 0 x 283 [311,606,208,50,913,678,369,544,721,267]
+ CRUSH rule 0 x 284 [771,466,371,743,672,119,60,546,39,71]
+ CRUSH rule 0 x 285 [693,362,404,676,797,531,582,975,810,703]
+ CRUSH rule 0 x 286 [364,477,285,167,270,617,699,627,725,389]
+ CRUSH rule 0 x 287 [591,611,828,995,170,987,137,890,487,621]
+ CRUSH rule 0 x 288 [965,541,848,796,251,668,195,538,356,523]
+ CRUSH rule 0 x 289 [225,551,948,877,219,167,795,377,825,874]
+ CRUSH rule 0 x 290 [577,762,777,751,291,349,473,209,59,346]
+ CRUSH rule 0 x 291 [160,903,477,381,490,559,557,86,89,417]
+ CRUSH rule 0 x 292 [873,598,216,666,222,228,806,911,738,969]
+ CRUSH rule 0 x 293 [100,234,874,47,28,452,775,636,232,518]
+ CRUSH rule 0 x 294 [285,943,379,520,725,547,459,833,503,207]
+ CRUSH rule 0 x 295 [938,262,880,327,687,3,440,73,29,38]
+ CRUSH rule 0 x 296 [850,327,86,472,1,776,266,82,671,320]
+ CRUSH rule 0 x 297 [951,53,99,558,753,228,232,343,831,540]
+ CRUSH rule 0 x 298 [173,336,85,766,910,657,213,286,61,961]
+ CRUSH rule 0 x 299 [598,591,315,386,895,296,924,106,63,457]
+ CRUSH rule 0 x 300 [531,957,62,459,156,538,904,838,458,828]
+ CRUSH rule 0 x 301 [823,628,23,858,629,808,220,432,393,433]
+ CRUSH rule 0 x 302 [184,80,780,871,531,211,400,365,697,497]
+ CRUSH rule 0 x 303 [521,766,222,830,988,275,561,905,522,342]
+ CRUSH rule 0 x 304 [980,127,807,507,555,245,214,944,845,895]
+ CRUSH rule 0 x 305 [153,816,22,927,696,911,685,838,3,983]
+ CRUSH rule 0 x 306 [423,739,664,753,178,431,761,648,867,488]
+ CRUSH rule 0 x 307 [997,557,682,456,479,631,459,250,415,194]
+ CRUSH rule 0 x 308 [991,874,534,465,330,284,976,551,126,307]
+ CRUSH rule 0 x 309 [860,394,724,858,246,866,857,153,970,99]
+ CRUSH rule 0 x 310 [589,818,546,201,94,653,90,855,441,736]
+ CRUSH rule 0 x 311 [477,774,225,590,830,559,256,798,743,645]
+ CRUSH rule 0 x 312 [887,853,950,354,58,23,497,929,92,639]
+ CRUSH rule 0 x 313 [802,646,447,416,557,118,24,81,215,850]
+ CRUSH rule 0 x 314 [654,974,229,511,562,916,952,599,222,763]
+ CRUSH rule 0 x 315 [767,227,28,740,828,156,749,841,969,314]
+ CRUSH rule 0 x 316 [778,83,733,359,858,319,761,725,923,461]
+ CRUSH rule 0 x 317 [184,418,642,986,939,675,892,86,214,189]
+ CRUSH rule 0 x 318 [525,410,500,543,212,95,290,97,529,220]
+ CRUSH rule 0 x 319 [476,724,569,382,409,521,800,868,364,427]
+ CRUSH rule 0 x 320 [149,610,697,296,818,955,523,366,891,998]
+ CRUSH rule 0 x 321 [710,79,667,671,234,4,868,841,563,961]
+ CRUSH rule 0 x 322 [175,275,323,333,744,718,187,380,947,952]
+ CRUSH rule 0 x 323 [819,604,638,792,316,544,236,810,969,232]
+ CRUSH rule 0 x 324 [16,745,511,439,272,513,668,959,845,759]
+ CRUSH rule 0 x 325 [486,400,872,873,251,68,462,268,124,431]
+ CRUSH rule 0 x 326 [613,765,207,19,359,370,461,509,75,767]
+ CRUSH rule 0 x 327 [125,289,738,408,456,784,750,669,296,314]
+ CRUSH rule 0 x 328 [807,383,476,583,645,141,33,806,181,597]
+ CRUSH rule 0 x 329 [588,938,599,432,446,840,516,713,223,395]
+ CRUSH rule 0 x 330 [932,644,41,611,209,406,420,520,395,665]
+ CRUSH rule 0 x 331 [341,953,950,537,578,862,624,649,626,928]
+ CRUSH rule 0 x 332 [153,726,459,950,466,804,644,821,238,85]
+ CRUSH rule 0 x 333 [745,845,853,860,52,615,243,633,309,616]
+ CRUSH rule 0 x 334 [614,751,807,58,396,159,408,175,189,500]
+ CRUSH rule 0 x 335 [518,721,221,283,454,187,635,367,997,819]
+ CRUSH rule 0 x 336 [389,424,77,309,5,898,698,533,683,851]
+ CRUSH rule 0 x 337 [753,508,765,720,221,807,956,907,464,39]
+ CRUSH rule 0 x 338 [128,810,490,753,406,760,69,11,624,272]
+ CRUSH rule 0 x 339 [430,308,58,751,856,823,607,953,125,899]
+ CRUSH rule 0 x 340 [541,44,630,231,289,966,707,328,325,81]
+ CRUSH rule 0 x 341 [402,26,631,439,165,928,720,503,209,748]
+ CRUSH rule 0 x 342 [982,57,992,461,131,32,516,661,985,860]
+ CRUSH rule 0 x 343 [833,412,572,732,107,805,660,655,149,994]
+ CRUSH rule 0 x 344 [784,533,792,41,642,869,142,114,108,961]
+ CRUSH rule 0 x 345 [546,300,304,691,763,556,127,732,290,494]
+ CRUSH rule 0 x 346 [302,420,428,891,357,124,419,962,304,12]
+ CRUSH rule 0 x 347 [488,778,101,217,366,442,783,661,622,426]
+ CRUSH rule 0 x 348 [903,744,937,718,85,314,862,513,112,334]
+ CRUSH rule 0 x 349 [471,547,582,306,600,486,795,143,529,765]
+ CRUSH rule 0 x 350 [348,221,823,335,383,708,841,164,765,563]
+ CRUSH rule 0 x 351 [961,582,705,346,361,32,766,775,518,155]
+ CRUSH rule 0 x 352 [728,137,461,298,36,903,899,665,802,620]
+ CRUSH rule 0 x 353 [904,202,184,447,58,294,279,616,892,262]
+ CRUSH rule 0 x 354 [345,226,319,256,544,311,612,33,122,190]
+ CRUSH rule 0 x 355 [50,430,175,43,187,458,985,412,599,375]
+ CRUSH rule 0 x 356 [87,185,55,423,829,1,629,228,150,889]
+ CRUSH rule 0 x 357 [762,459,921,473,182,231,891,656,196,232]
+ CRUSH rule 0 x 358 [908,25,280,6,808,676,874,643,550,633]
+ CRUSH rule 0 x 359 [484,15,132,121,394,423,397,52,702,981]
+ CRUSH rule 0 x 360 [173,378,337,702,145,499,29,529,156,595]
+ CRUSH rule 0 x 361 [404,577,115,25,56,914,643,286,552,985]
+ CRUSH rule 0 x 362 [403,1,422,945,132,685,265,35,662,708]
+ CRUSH rule 0 x 363 [639,911,510,162,418,294,444,613,919,499]
+ CRUSH rule 0 x 364 [752,689,610,990,665,222,203,17,743,570]
+ CRUSH rule 0 x 365 [956,999,212,230,624,84,113,373,426,941]
+ CRUSH rule 0 x 366 [860,925,924,763,687,851,59,914,521,629]
+ CRUSH rule 0 x 367 [205,609,647,665,969,720,685,641,894,813]
+ CRUSH rule 0 x 368 [301,284,810,169,78,340,616,93,283,353]
+ CRUSH rule 0 x 369 [452,658,339,217,674,210,284,184,718,684]
+ CRUSH rule 0 x 370 [11,467,695,989,394,576,850,419,307,965]
+ CRUSH rule 0 x 371 [124,487,55,514,313,411,797,547,778,958]
+ CRUSH rule 0 x 372 [253,48,979,846,207,631,212,241,346,153]
+ CRUSH rule 0 x 373 [715,605,775,748,227,493,128,207,88,641]
+ CRUSH rule 0 x 374 [191,887,920,928,223,714,961,760,571,549]
+ CRUSH rule 0 x 375 [711,385,651,665,15,71,934,619,527,735]
+ CRUSH rule 0 x 376 [597,818,49,458,415,755,446,897,460,869]
+ CRUSH rule 0 x 377 [294,256,933,771,184,861,654,487,891,733]
+ CRUSH rule 0 x 378 [34,151,681,707,552,127,728,860,968,475]
+ CRUSH rule 0 x 379 [869,136,315,378,813,153,115,557,165,292]
+ CRUSH rule 0 x 380 [294,97,575,791,690,482,255,806,429,306]
+ CRUSH rule 0 x 381 [119,710,219,827,328,886,773,496,433,750]
+ CRUSH rule 0 x 382 [69,631,508,706,697,168,276,56,278,772]
+ CRUSH rule 0 x 383 [922,588,589,925,471,601,29,197,822,218]
+ CRUSH rule 0 x 384 [221,945,671,117,857,655,488,435,223,783]
+ CRUSH rule 0 x 385 [561,737,953,723,658,368,910,329,396,482]
+ CRUSH rule 0 x 386 [335,442,788,696,507,716,232,692,742,939]
+ CRUSH rule 0 x 387 [514,43,353,88,100,842,164,934,297,902]
+ CRUSH rule 0 x 388 [587,89,157,996,915,927,474,267,640,53]
+ CRUSH rule 0 x 389 [109,641,255,466,372,563,340,222,74,503]
+ CRUSH rule 0 x 390 [925,149,421,489,599,810,852,196,469,672]
+ CRUSH rule 0 x 391 [267,87,387,527,768,873,879,136,818,516]
+ CRUSH rule 0 x 392 [382,485,370,849,936,636,901,82,695,640]
+ CRUSH rule 0 x 393 [425,721,221,753,268,463,652,543,10,287]
+ CRUSH rule 0 x 394 [898,18,38,793,173,738,15,591,420,525]
+ CRUSH rule 0 x 395 [806,876,269,679,32,744,126,179,607,623]
+ CRUSH rule 0 x 396 [790,970,437,449,875,395,726,935,278,138]
+ CRUSH rule 0 x 397 [136,363,507,613,11,30,996,558,602,528]
+ CRUSH rule 0 x 398 [914,116,558,258,722,904,349,672,826,569]
+ CRUSH rule 0 x 399 [261,94,299,202,174,622,749,410,815,214]
+ CRUSH rule 0 x 400 [661,197,338,461,977,848,536,592,886,981]
+ CRUSH rule 0 x 401 [953,979,287,803,41,349,79,32,343,468]
+ CRUSH rule 0 x 402 [738,819,618,522,667,334,658,449,886,260]
+ CRUSH rule 0 x 403 [573,238,425,546,130,68,202,650,501,578]
+ CRUSH rule 0 x 404 [526,848,790,253,922,820,299,577,563,37]
+ CRUSH rule 0 x 405 [582,505,330,334,201,110,776,296,19,972]
+ CRUSH rule 0 x 406 [768,324,493,60,186,165,718,578,580,249]
+ CRUSH rule 0 x 407 [260,951,437,587,692,648,72,345,709,89]
+ CRUSH rule 0 x 408 [657,81,770,734,830,821,246,695,76,647]
+ CRUSH rule 0 x 409 [498,89,182,423,672,152,213,806,168,907]
+ CRUSH rule 0 x 410 [28,793,737,352,166,645,949,507,361,615]
+ CRUSH rule 0 x 411 [684,992,60,659,769,267,313,351,497,571]
+ CRUSH rule 0 x 412 [261,958,699,950,165,14,560,155,661,678]
+ CRUSH rule 0 x 413 [891,835,297,441,384,979,618,907,9,291]
+ CRUSH rule 0 x 414 [127,459,119,965,662,594,97,124,229,641]
+ CRUSH rule 0 x 415 [272,540,631,328,609,568,694,332,572,681]
+ CRUSH rule 0 x 416 [739,617,115,530,339,371,889,344,838,541]
+ CRUSH rule 0 x 417 [106,209,157,878,117,128,138,374,470,59]
+ CRUSH rule 0 x 418 [525,441,147,390,320,300,848,972,781,361]
+ CRUSH rule 0 x 419 [603,673,615,465,266,855,823,884,832,361]
+ CRUSH rule 0 x 420 [988,213,251,226,209,245,506,670,285,2]
+ CRUSH rule 0 x 421 [761,521,748,368,923,992,764,274,623,613]
+ CRUSH rule 0 x 422 [317,160,924,548,198,709,839,547,599,779]
+ CRUSH rule 0 x 423 [137,807,168,472,619,443,905,588,312,114]
+ CRUSH rule 0 x 424 [920,37,146,263,598,748,785,395,884,360]
+ CRUSH rule 0 x 425 [277,693,285,221,478,165,80,236,988,682]
+ CRUSH rule 0 x 426 [485,936,407,854,726,524,791,565,352,949]
+ CRUSH rule 0 x 427 [242,515,9,564,174,453,334,588,571,428]
+ CRUSH rule 0 x 428 [632,635,26,473,494,478,225,94,303,757]
+ CRUSH rule 0 x 429 [641,73,465,127,171,397,857,562,976,977]
+ CRUSH rule 0 x 430 [626,585,6,387,881,583,859,699,91,148]
+ CRUSH rule 0 x 431 [697,76,753,570,964,339,194,366,279,30]
+ CRUSH rule 0 x 432 [590,526,306,283,656,728,513,591,599,474]
+ CRUSH rule 0 x 433 [284,387,149,817,886,714,52,897,705,707]
+ CRUSH rule 0 x 434 [538,985,79,953,770,468,644,646,747,123]
+ CRUSH rule 0 x 435 [30,318,593,635,975,833,371,731,906,721]
+ CRUSH rule 0 x 436 [164,919,851,693,0,874,10,976,284,126]
+ CRUSH rule 0 x 437 [322,212,163,606,302,282,443,23,696,245]
+ CRUSH rule 0 x 438 [142,392,85,594,376,419,755,841,94,52]
+ CRUSH rule 0 x 439 [119,370,68,443,997,837,414,152,331,985]
+ CRUSH rule 0 x 440 [333,403,187,863,475,844,800,174,117,518]
+ CRUSH rule 0 x 441 [477,727,906,145,429,91,205,236,86,929]
+ CRUSH rule 0 x 442 [274,590,933,244,434,49,864,799,762,611]
+ CRUSH rule 0 x 443 [983,748,574,718,700,442,774,350,37,929]
+ CRUSH rule 0 x 444 [536,509,431,146,170,149,182,145,347,172]
+ CRUSH rule 0 x 445 [485,482,528,209,964,753,554,931,638,892]
+ CRUSH rule 0 x 446 [345,634,42,294,711,376,314,714,212,646]
+ CRUSH rule 0 x 447 [61,845,767,600,321,716,58,531,827,968]
+ CRUSH rule 0 x 448 [333,232,292,846,364,951,807,688,21,841]
+ CRUSH rule 0 x 449 [680,16,484,670,851,500,258,548,905,682]
+ CRUSH rule 0 x 450 [235,214,79,423,96,822,721,31,312,491]
+ CRUSH rule 0 x 451 [961,468,333,640,823,151,878,33,3,917]
+ CRUSH rule 0 x 452 [525,479,153,528,570,806,604,49,922,414]
+ CRUSH rule 0 x 453 [138,466,302,86,249,154,514,5,494,960]
+ CRUSH rule 0 x 454 [137,625,215,402,389,914,106,103,511,624]
+ CRUSH rule 0 x 455 [173,150,997,16,846,888,295,967,132,319]
+ CRUSH rule 0 x 456 [235,226,238,258,347,784,504,96,890,230]
+ CRUSH rule 0 x 457 [450,577,253,413,717,609,762,975,485,228]
+ CRUSH rule 0 x 458 [195,537,91,814,351,90,399,558,15,441]
+ CRUSH rule 0 x 459 [381,555,312,573,915,623,147,483,517,733]
+ CRUSH rule 0 x 460 [972,730,534,678,756,692,841,512,70,914]
+ CRUSH rule 0 x 461 [506,279,142,830,784,124,385,797,917,561]
+ CRUSH rule 0 x 462 [692,959,578,57,983,299,240,911,375,412]
+ CRUSH rule 0 x 463 [788,667,949,550,685,702,538,111,232,539]
+ CRUSH rule 0 x 464 [133,122,588,999,270,880,789,0,653,566]
+ CRUSH rule 0 x 465 [971,190,230,777,452,914,137,466,531,493]
+ CRUSH rule 0 x 466 [394,576,148,157,103,822,659,35,797,235]
+ CRUSH rule 0 x 467 [517,28,366,362,984,521,187,640,601,622]
+ CRUSH rule 0 x 468 [829,143,874,225,162,413,201,249,555,646]
+ CRUSH rule 0 x 469 [987,936,106,725,633,238,681,683,551,768]
+ CRUSH rule 0 x 470 [107,982,56,889,67,65,558,71,676,655]
+ CRUSH rule 0 x 471 [181,897,629,860,307,116,256,978,409,691]
+ CRUSH rule 0 x 472 [547,512,172,24,705,837,809,56,476,137]
+ CRUSH rule 0 x 473 [760,997,824,905,888,755,756,663,167,196]
+ CRUSH rule 0 x 474 [787,418,743,628,272,341,446,333,245,689]
+ CRUSH rule 0 x 475 [662,312,253,617,105,58,237,764,682,318]
+ CRUSH rule 0 x 476 [110,495,185,508,961,837,984,226,333,916]
+ CRUSH rule 0 x 477 [393,954,834,132,841,367,753,794,237,996]
+ CRUSH rule 0 x 478 [246,483,480,644,985,420,941,843,751,451]
+ CRUSH rule 0 x 479 [70,929,697,931,744,487,158,489,515,496]
+ CRUSH rule 0 x 480 [753,119,961,607,317,717,371,807,687,932]
+ CRUSH rule 0 x 481 [470,429,677,242,574,757,135,375,613,657]
+ CRUSH rule 0 x 482 [451,566,961,675,354,746,731,233,640,492]
+ CRUSH rule 0 x 483 [816,72,371,278,635,30,448,437,219,982]
+ CRUSH rule 0 x 484 [540,454,389,31,654,494,283,170,278,77]
+ CRUSH rule 0 x 485 [74,582,624,684,566,677,866,661,581,943]
+ CRUSH rule 0 x 486 [958,595,199,763,715,973,621,955,400,261]
+ CRUSH rule 0 x 487 [228,302,804,833,876,647,857,782,24,970]
+ CRUSH rule 0 x 488 [180,529,722,956,353,890,924,965,25,925]
+ CRUSH rule 0 x 489 [47,617,812,187,291,828,154,478,512,528]
+ CRUSH rule 0 x 490 [905,822,479,124,750,843,566,779,845,507]
+ CRUSH rule 0 x 491 [892,370,609,998,433,957,188,563,490,369]
+ CRUSH rule 0 x 492 [588,959,127,948,505,936,591,423,668,365]
+ CRUSH rule 0 x 493 [353,461,593,291,301,830,231,308,474,946]
+ CRUSH rule 0 x 494 [378,848,443,368,507,423,389,819,956,597]
+ CRUSH rule 0 x 495 [845,653,768,234,405,367,823,789,217,720]
+ CRUSH rule 0 x 496 [13,988,0,691,389,757,129,763,39,651]
+ CRUSH rule 0 x 497 [796,877,788,394,648,829,542,745,131,753]
+ CRUSH rule 0 x 498 [412,337,270,705,511,227,949,173,398,586]
+ CRUSH rule 0 x 499 [330,695,8,74,618,101,440,509,295,921]
+ CRUSH rule 0 x 500 [820,272,547,765,755,96,930,573,357,491]
+ CRUSH rule 0 x 501 [110,44,132,442,294,423,880,279,616,919]
+ CRUSH rule 0 x 502 [336,595,650,274,993,312,490,852,962,387]
+ CRUSH rule 0 x 503 [922,211,157,722,502,971,262,926,316,527]
+ CRUSH rule 0 x 504 [483,52,122,432,778,461,758,104,831,710]
+ CRUSH rule 0 x 505 [482,598,224,279,480,310,764,558,891,406]
+ CRUSH rule 0 x 506 [493,123,43,856,936,622,898,161,78,414]
+ CRUSH rule 0 x 507 [12,598,264,422,416,947,591,702,346,619]
+ CRUSH rule 0 x 508 [227,157,611,301,223,746,313,282,207,626]
+ CRUSH rule 0 x 509 [807,242,363,122,582,530,798,808,139,377]
+ CRUSH rule 0 x 510 [134,437,227,75,313,351,786,152,921,884]
+ CRUSH rule 0 x 511 [212,54,83,799,457,218,600,968,355,109]
+ CRUSH rule 0 x 512 [236,630,758,752,361,249,899,451,415,920]
+ CRUSH rule 0 x 513 [994,693,644,938,846,685,52,185,197,986]
+ CRUSH rule 0 x 514 [45,508,831,19,817,52,374,985,944,101]
+ CRUSH rule 0 x 515 [504,138,480,272,530,377,481,820,517,850]
+ CRUSH rule 0 x 516 [285,409,136,570,841,610,453,660,93,134]
+ CRUSH rule 0 x 517 [300,232,23,906,438,236,519,737,20,892]
+ CRUSH rule 0 x 518 [397,674,98,898,967,113,625,434,527,630]
+ CRUSH rule 0 x 519 [86,750,772,913,101,864,375,328,3,688]
+ CRUSH rule 0 x 520 [900,833,614,130,261,885,558,956,664,468]
+ CRUSH rule 0 x 521 [31,47,236,751,911,599,495,354,665,945]
+ CRUSH rule 0 x 522 [390,16,280,144,291,175,753,624,769,853]
+ CRUSH rule 0 x 523 [618,308,424,590,300,206,834,212,906,305]
+ CRUSH rule 0 x 524 [635,189,687,963,601,518,8,550,769,975]
+ CRUSH rule 0 x 525 [311,916,699,262,775,32,45,478,911,233]
+ CRUSH rule 0 x 526 [48,738,227,718,244,942,853,643,625,43]
+ CRUSH rule 0 x 527 [202,851,889,216,763,351,270,35,809,509]
+ CRUSH rule 0 x 528 [565,827,590,273,918,106,651,368,118,1]
+ CRUSH rule 0 x 529 [934,864,241,43,466,924,278,926,280,321]
+ CRUSH rule 0 x 530 [502,934,298,670,986,360,577,509,195,722]
+ CRUSH rule 0 x 531 [681,627,942,487,288,561,925,474,669,212]
+ CRUSH rule 0 x 532 [422,6,147,205,861,141,949,374,988,367]
+ CRUSH rule 0 x 533 [863,68,364,983,247,199,54,931,4,279]
+ CRUSH rule 0 x 534 [962,931,775,172,663,119,206,682,627,827]
+ CRUSH rule 0 x 535 [89,565,397,693,839,632,859,30,61,75]
+ CRUSH rule 0 x 536 [499,351,760,458,918,86,148,668,436,192]
+ CRUSH rule 0 x 537 [676,547,787,311,867,748,152,797,492,862]
+ CRUSH rule 0 x 538 [58,644,571,649,941,7,37,485,88,273]
+ CRUSH rule 0 x 539 [837,953,457,711,458,621,528,722,59,237]
+ CRUSH rule 0 x 540 [831,50,132,213,197,709,95,789,348,342]
+ CRUSH rule 0 x 541 [582,757,121,525,532,963,738,277,225,142]
+ CRUSH rule 0 x 542 [472,132,790,997,948,269,137,934,547,351]
+ CRUSH rule 0 x 543 [382,272,797,330,315,748,324,134,839,685]
+ CRUSH rule 0 x 544 [947,930,496,883,509,219,250,362,614,123]
+ CRUSH rule 0 x 545 [425,570,305,77,821,422,117,172,764,372]
+ CRUSH rule 0 x 546 [18,65,529,437,343,547,699,610,785,811]
+ CRUSH rule 0 x 547 [445,715,600,472,213,851,428,267,229,379]
+ CRUSH rule 0 x 548 [367,569,980,167,627,442,517,684,154,108]
+ CRUSH rule 0 x 549 [125,715,671,817,285,420,37,639,934,330]
+ CRUSH rule 0 x 550 [425,599,744,199,923,222,915,570,546,724]
+ CRUSH rule 0 x 551 [44,1,528,922,944,115,161,901,342,941]
+ CRUSH rule 0 x 552 [246,104,68,239,123,427,57,217,21,70]
+ CRUSH rule 0 x 553 [71,703,615,28,593,724,218,916,561,416]
+ CRUSH rule 0 x 554 [207,124,217,166,525,226,693,953,606,894]
+ CRUSH rule 0 x 555 [570,28,317,420,931,413,623,659,403,573]
+ CRUSH rule 0 x 556 [674,152,421,79,215,347,830,762,691,951]
+ CRUSH rule 0 x 557 [347,817,191,391,741,571,593,267,17,386]
+ CRUSH rule 0 x 558 [627,426,369,692,815,371,124,107,766,260]
+ CRUSH rule 0 x 559 [940,630,924,242,224,912,185,356,87,113]
+ CRUSH rule 0 x 560 [295,903,541,29,245,753,887,376,658,366]
+ CRUSH rule 0 x 561 [506,682,384,637,878,991,700,339,687,507]
+ CRUSH rule 0 x 562 [718,529,87,729,842,341,62,817,766,376]
+ CRUSH rule 0 x 563 [552,332,747,206,274,871,903,900,812,290]
+ CRUSH rule 0 x 564 [835,769,736,486,630,209,641,751,930,856]
+ CRUSH rule 0 x 565 [8,167,539,182,607,62,738,873,47,84]
+ CRUSH rule 0 x 566 [600,481,301,263,90,450,184,127,448,327]
+ CRUSH rule 0 x 567 [999,994,509,899,947,24,267,639,646,85]
+ CRUSH rule 0 x 568 [252,431,157,62,601,863,398,521,59,250]
+ CRUSH rule 0 x 569 [643,218,943,455,83,969,494,624,352,562]
+ CRUSH rule 0 x 570 [617,635,765,422,250,156,533,674,23,683]
+ CRUSH rule 0 x 571 [757,80,59,98,328,700,329,848,235,502]
+ CRUSH rule 0 x 572 [299,348,575,889,943,675,33,312,202,355]
+ CRUSH rule 0 x 573 [25,505,270,167,58,901,878,978,1,291]
+ CRUSH rule 0 x 574 [215,431,624,177,628,814,333,841,193,146]
+ CRUSH rule 0 x 575 [225,252,611,546,32,815,389,486,10,402]
+ CRUSH rule 0 x 576 [627,94,159,857,430,691,177,545,839,722]
+ CRUSH rule 0 x 577 [237,809,778,636,61,167,700,521,825,444]
+ CRUSH rule 0 x 578 [885,313,120,344,771,614,487,976,977,58]
+ CRUSH rule 0 x 579 [924,575,787,831,47,996,557,630,468,348]
+ CRUSH rule 0 x 580 [718,51,766,121,118,471,608,755,326,604]
+ CRUSH rule 0 x 581 [219,807,129,571,856,179,874,902,958,415]
+ CRUSH rule 0 x 582 [893,701,598,863,285,829,984,622,175,804]
+ CRUSH rule 0 x 583 [246,930,964,170,993,409,469,193,737,681]
+ CRUSH rule 0 x 584 [336,432,680,175,495,839,642,226,122,703]
+ CRUSH rule 0 x 585 [324,999,397,485,457,527,73,628,884,255]
+ CRUSH rule 0 x 586 [558,230,976,541,816,72,794,682,127,372]
+ CRUSH rule 0 x 587 [985,830,597,21,308,890,952,421,875,65]
+ CRUSH rule 0 x 588 [211,544,57,134,162,496,195,581,649,488]
+ CRUSH rule 0 x 589 [129,21,112,190,885,844,753,180,160,465]
+ CRUSH rule 0 x 590 [467,969,652,593,287,76,811,413,436,162]
+ CRUSH rule 0 x 591 [758,514,316,164,35,110,54,796,369,958]
+ CRUSH rule 0 x 592 [525,253,190,443,315,603,667,318,496,74]
+ CRUSH rule 0 x 593 [601,885,339,152,297,223,269,455,168,635]
+ CRUSH rule 0 x 594 [227,60,450,30,717,840,994,16,777,901]
+ CRUSH rule 0 x 595 [720,854,496,912,80,655,917,525,945,715]
+ CRUSH rule 0 x 596 [751,195,997,77,261,490,180,482,449,647]
+ CRUSH rule 0 x 597 [129,574,714,8,789,847,725,991,955,316]
+ CRUSH rule 0 x 598 [679,207,604,396,841,284,286,280,507,912]
+ CRUSH rule 0 x 599 [668,315,683,349,681,253,599,364,546,849]
+ CRUSH rule 0 x 600 [143,396,464,444,59,57,243,264,31,897]
+ CRUSH rule 0 x 601 [326,573,873,902,136,921,633,596,988,727]
+ CRUSH rule 0 x 602 [860,281,875,535,672,474,697,763,442,542]
+ CRUSH rule 0 x 603 [709,328,445,349,190,455,924,667,356,316]
+ CRUSH rule 0 x 604 [571,62,814,95,866,978,983,281,292,953]
+ CRUSH rule 0 x 605 [252,739,860,27,313,362,857,899,349,926]
+ CRUSH rule 0 x 606 [339,236,759,842,67,644,954,94,88,617]
+ CRUSH rule 0 x 607 [590,248,759,868,433,398,578,386,226,269]
+ CRUSH rule 0 x 608 [145,635,309,467,875,115,148,33,420,669]
+ CRUSH rule 0 x 609 [973,547,223,79,762,863,249,41,778,929]
+ CRUSH rule 0 x 610 [435,816,961,983,255,886,160,888,889,767]
+ CRUSH rule 0 x 611 [559,283,422,584,176,429,570,43,362,401]
+ CRUSH rule 0 x 612 [273,149,123,576,911,270,296,735,245,714]
+ CRUSH rule 0 x 613 [828,614,642,674,33,361,958,580,197,897]
+ CRUSH rule 0 x 614 [478,748,393,34,171,80,92,12,62,719]
+ CRUSH rule 0 x 615 [392,155,144,326,626,134,149,401,14,59]
+ CRUSH rule 0 x 616 [778,637,452,248,15,888,74,307,976,613]
+ CRUSH rule 0 x 617 [622,713,996,833,611,407,364,8,342,512]
+ CRUSH rule 0 x 618 [149,877,270,329,180,327,222,749,697,853]
+ CRUSH rule 0 x 619 [604,163,656,409,322,848,519,967,737,892]
+ CRUSH rule 0 x 620 [181,23,409,198,64,898,35,620,268,902]
+ CRUSH rule 0 x 621 [735,902,386,237,939,475,725,118,875,359]
+ CRUSH rule 0 x 622 [661,824,717,568,858,583,446,798,869,586]
+ CRUSH rule 0 x 623 [142,121,643,61,695,852,485,478,185,854]
+ CRUSH rule 0 x 624 [360,716,420,398,49,717,137,140,488,725]
+ CRUSH rule 0 x 625 [541,167,385,1,601,481,308,111,207,48]
+ CRUSH rule 0 x 626 [364,431,610,363,535,747,225,841,868,249]
+ CRUSH rule 0 x 627 [458,137,557,410,287,749,467,432,944,781]
+ CRUSH rule 0 x 628 [250,350,556,497,821,65,205,580,972,427]
+ CRUSH rule 0 x 629 [928,160,710,572,365,772,538,46,300,112]
+ CRUSH rule 0 x 630 [243,19,918,556,601,16,920,830,171,759]
+ CRUSH rule 0 x 631 [438,221,574,676,797,580,219,211,157,614]
+ CRUSH rule 0 x 632 [797,368,247,5,32,102,416,45,624,253]
+ CRUSH rule 0 x 633 [993,749,525,485,27,330,275,599,219,357]
+ CRUSH rule 0 x 634 [239,351,633,299,651,678,296,337,676,416]
+ CRUSH rule 0 x 635 [640,965,25,961,306,172,849,357,317,599]
+ CRUSH rule 0 x 636 [173,290,297,991,937,823,236,318,228,575]
+ CRUSH rule 0 x 637 [0,918,98,108,111,495,887,57,16,319]
+ CRUSH rule 0 x 638 [702,235,424,900,983,754,701,887,355,632]
+ CRUSH rule 0 x 639 [475,687,31,785,918,611,27,214,226,515]
+ CRUSH rule 0 x 640 [31,664,399,677,123,609,858,138,726,1]
+ CRUSH rule 0 x 641 [296,473,108,963,341,876,897,449,42,193]
+ CRUSH rule 0 x 642 [894,273,427,606,677,670,610,665,299,852]
+ CRUSH rule 0 x 643 [117,111,732,191,114,153,500,631,833,439]
+ CRUSH rule 0 x 644 [438,336,327,512,599,862,660,857,123,910]
+ CRUSH rule 0 x 645 [982,702,351,573,907,915,279,317,414,917]
+ CRUSH rule 0 x 646 [334,804,146,842,697,638,720,135,369,711]
+ CRUSH rule 0 x 647 [933,787,185,334,752,285,372,890,30,747]
+ CRUSH rule 0 x 648 [22,444,400,862,207,842,453,732,262,803]
+ CRUSH rule 0 x 649 [503,229,213,460,639,760,722,748,599,556]
+ CRUSH rule 0 x 650 [328,659,420,443,739,950,869,150,743,438]
+ CRUSH rule 0 x 651 [3,880,823,123,378,585,715,221,31,92]
+ CRUSH rule 0 x 652 [495,977,563,733,92,997,119,818,459,782]
+ CRUSH rule 0 x 653 [185,718,804,280,975,912,198,291,71,792]
+ CRUSH rule 0 x 654 [130,528,380,81,906,511,385,506,546,266]
+ CRUSH rule 0 x 655 [560,872,454,504,319,284,605,214,833,862]
+ CRUSH rule 0 x 656 [219,885,178,981,863,508,708,6,746,734]
+ CRUSH rule 0 x 657 [233,684,813,490,208,941,858,16,128,144]
+ CRUSH rule 0 x 658 [778,6,756,380,750,836,547,850,499,125]
+ CRUSH rule 0 x 659 [240,663,306,540,789,902,170,954,22,394]
+ CRUSH rule 0 x 660 [244,855,196,147,678,323,63,859,215,171]
+ CRUSH rule 0 x 661 [184,270,128,398,910,230,402,205,609,831]
+ CRUSH rule 0 x 662 [65,883,921,438,79,957,464,902,276,289]
+ CRUSH rule 0 x 663 [323,721,594,812,43,992,170,65,906,943]
+ CRUSH rule 0 x 664 [865,113,512,51,427,123,585,260,254,209]
+ CRUSH rule 0 x 665 [420,850,591,475,202,733,798,658,28,334]
+ CRUSH rule 0 x 666 [319,767,246,3,369,493,796,56,736,0]
+ CRUSH rule 0 x 667 [875,39,343,100,829,2,795,783,386,956]
+ CRUSH rule 0 x 668 [331,122,263,599,355,484,943,554,395,713]
+ CRUSH rule 0 x 669 [915,521,402,747,673,445,938,600,517,49]
+ CRUSH rule 0 x 670 [845,659,943,447,401,322,168,302,681,978]
+ CRUSH rule 0 x 671 [108,634,527,363,856,238,755,330,584,525]
+ CRUSH rule 0 x 672 [578,216,110,589,302,137,954,315,735,751]
+ CRUSH rule 0 x 673 [442,74,579,797,622,950,371,402,725,870]
+ CRUSH rule 0 x 674 [588,364,281,308,645,631,229,506,565,362]
+ CRUSH rule 0 x 675 [489,698,744,671,870,174,528,875,982,782]
+ CRUSH rule 0 x 676 [928,911,40,180,722,729,673,569,701,403]
+ CRUSH rule 0 x 677 [399,269,692,131,615,136,103,763,527,83]
+ CRUSH rule 0 x 678 [546,752,544,155,5,463,666,352,576,959]
+ CRUSH rule 0 x 679 [988,25,275,433,628,57,247,620,437,29]
+ CRUSH rule 0 x 680 [335,963,382,486,749,257,795,347,831,761]
+ CRUSH rule 0 x 681 [690,462,623,466,49,471,774,192,454,380]
+ CRUSH rule 0 x 682 [196,588,154,257,807,776,367,718,345,677]
+ CRUSH rule 0 x 683 [627,25,421,160,873,102,345,599,30,892]
+ CRUSH rule 0 x 684 [38,804,592,158,991,264,652,821,641,757]
+ CRUSH rule 0 x 685 [841,368,548,362,166,211,154,121,843,804]
+ CRUSH rule 0 x 686 [336,287,525,440,166,993,911,638,690,393]
+ CRUSH rule 0 x 687 [20,682,924,653,356,16,917,622,156,826]
+ CRUSH rule 0 x 688 [463,371,780,556,385,883,115,248,566,11]
+ CRUSH rule 0 x 689 [569,250,78,816,847,775,333,161,74,907]
+ CRUSH rule 0 x 690 [551,144,587,263,378,394,970,639,835,238]
+ CRUSH rule 0 x 691 [766,464,446,533,449,541,451,290,789,853]
+ CRUSH rule 0 x 692 [739,634,18,245,624,35,268,525,425,499]
+ CRUSH rule 0 x 693 [339,297,118,330,817,91,828,276,264,237]
+ CRUSH rule 0 x 694 [405,26,830,181,533,166,488,804,501,885]
+ CRUSH rule 0 x 695 [622,576,597,535,600,593,300,989,804,72]
+ CRUSH rule 0 x 696 [558,902,689,13,715,28,664,489,598,261]
+ CRUSH rule 0 x 697 [818,222,406,691,427,863,153,922,986,480]
+ CRUSH rule 0 x 698 [178,48,402,233,841,604,468,180,783,915]
+ CRUSH rule 0 x 699 [450,244,180,919,183,332,747,453,519,100]
+ CRUSH rule 0 x 700 [502,771,987,706,416,240,68,641,109,182]
+ CRUSH rule 0 x 701 [4,612,782,216,853,303,585,513,907,414]
+ CRUSH rule 0 x 702 [177,630,232,923,281,708,466,687,742,170]
+ CRUSH rule 0 x 703 [354,178,389,393,778,803,796,607,894,1]
+ CRUSH rule 0 x 704 [646,601,156,171,603,116,655,595,888,354]
+ CRUSH rule 0 x 705 [921,401,890,265,244,690,372,253,807,28]
+ CRUSH rule 0 x 706 [652,877,562,452,26,323,923,770,516,982]
+ CRUSH rule 0 x 707 [345,745,67,716,789,576,2,133,256,374]
+ CRUSH rule 0 x 708 [333,607,180,469,170,555,939,331,41,175]
+ CRUSH rule 0 x 709 [45,187,302,115,896,579,733,607,763,845]
+ CRUSH rule 0 x 710 [94,855,43,199,18,948,449,28,731,573]
+ CRUSH rule 0 x 711 [227,653,731,150,156,842,534,110,639,452]
+ CRUSH rule 0 x 712 [398,953,136,870,181,408,895,459,341,833]
+ CRUSH rule 0 x 713 [116,800,503,662,635,579,53,839,56,829]
+ CRUSH rule 0 x 714 [111,629,866,709,902,557,875,649,23,79]
+ CRUSH rule 0 x 715 [531,291,486,382,192,807,322,417,973,582]
+ CRUSH rule 0 x 716 [169,541,291,42,343,724,138,197,32,415]
+ CRUSH rule 0 x 717 [417,446,994,894,239,494,237,62,327,958]
+ CRUSH rule 0 x 718 [992,383,298,844,377,463,544,891,210,370]
+ CRUSH rule 0 x 719 [936,674,324,759,194,409,828,975,119,87]
+ CRUSH rule 0 x 720 [370,188,174,464,644,218,214,76,870,779]
+ CRUSH rule 0 x 721 [320,859,278,259,170,957,177,264,867,327]
+ CRUSH rule 0 x 722 [7,2,673,129,96,445,823,833,1,774]
+ CRUSH rule 0 x 723 [270,553,831,662,38,101,985,846,77,467]
+ CRUSH rule 0 x 724 [666,822,708,895,633,800,616,879,480,309]
+ CRUSH rule 0 x 725 [794,406,875,459,981,751,359,983,720,128]
+ CRUSH rule 0 x 726 [420,556,341,292,240,68,966,535,669,74]
+ CRUSH rule 0 x 727 [561,461,129,635,965,610,105,31,506,430]
+ CRUSH rule 0 x 728 [951,330,196,756,589,849,753,760,254,379]
+ CRUSH rule 0 x 729 [656,644,436,591,27,119,572,933,434,816]
+ CRUSH rule 0 x 730 [3,558,629,184,50,765,760,800,945,743]
+ CRUSH rule 0 x 731 [852,89,75,735,713,113,528,890,625,535]
+ CRUSH rule 0 x 732 [983,840,869,976,697,307,368,271,778,172]
+ CRUSH rule 0 x 733 [285,396,388,122,387,364,880,343,590,539]
+ CRUSH rule 0 x 734 [125,510,402,640,676,501,535,627,224,790]
+ CRUSH rule 0 x 735 [417,773,686,504,459,912,690,59,294,569]
+ CRUSH rule 0 x 736 [749,396,632,550,779,109,845,278,559,613]
+ CRUSH rule 0 x 737 [644,991,946,135,448,903,482,564,259,896]
+ CRUSH rule 0 x 738 [449,683,290,220,245,525,429,397,872,716]
+ CRUSH rule 0 x 739 [341,220,641,454,740,661,146,17,314,156]
+ CRUSH rule 0 x 740 [874,524,674,650,472,282,214,494,593,155]
+ CRUSH rule 0 x 741 [189,472,712,798,715,757,863,571,876,528]
+ CRUSH rule 0 x 742 [912,581,114,117,730,21,687,81,145,695]
+ CRUSH rule 0 x 743 [654,914,425,441,763,39,451,631,911,829]
+ CRUSH rule 0 x 744 [725,295,579,377,162,447,843,699,24,714]
+ CRUSH rule 0 x 745 [787,858,850,506,612,735,926,314,771,910]
+ CRUSH rule 0 x 746 [757,848,704,30,47,940,450,651,105,921]
+ CRUSH rule 0 x 747 [700,81,867,681,801,64,879,857,727,565]
+ CRUSH rule 0 x 748 [557,436,238,664,293,865,304,999,685,843]
+ CRUSH rule 0 x 749 [772,622,337,42,156,302,383,506,570,828]
+ CRUSH rule 0 x 750 [946,97,376,677,316,670,169,171,9,58]
+ CRUSH rule 0 x 751 [996,618,343,911,83,22,388,17,892,537]
+ CRUSH rule 0 x 752 [746,887,695,868,610,950,88,315,728,669]
+ CRUSH rule 0 x 753 [741,14,463,479,172,192,481,702,431,675]
+ CRUSH rule 0 x 754 [648,349,333,355,65,63,336,724,262,61]
+ CRUSH rule 0 x 755 [157,460,466,187,959,674,192,279,371,970]
+ CRUSH rule 0 x 756 [416,97,197,497,227,3,850,191,991,63]
+ CRUSH rule 0 x 757 [599,839,776,410,256,823,121,690,544,28]
+ CRUSH rule 0 x 758 [994,218,620,256,361,749,165,686,449,831]
+ CRUSH rule 0 x 759 [959,682,514,745,100,519,15,347,311,512]
+ CRUSH rule 0 x 760 [518,943,215,83,706,137,345,69,39,199]
+ CRUSH rule 0 x 761 [285,849,420,324,987,338,373,361,684,654]
+ CRUSH rule 0 x 762 [591,313,41,335,110,696,664,350,339,980]
+ CRUSH rule 0 x 763 [908,411,200,740,292,295,387,775,797,990]
+ CRUSH rule 0 x 764 [787,234,894,485,883,711,70,202,557,471]
+ CRUSH rule 0 x 765 [327,921,882,393,444,792,402,123,902,592]
+ CRUSH rule 0 x 766 [84,161,878,704,416,144,357,310,890,850]
+ CRUSH rule 0 x 767 [370,895,702,701,890,2,251,951,675,322]
+ CRUSH rule 0 x 768 [826,760,879,864,460,474,645,975,947,199]
+ CRUSH rule 0 x 769 [67,768,663,735,814,66,213,527,546,42]
+ CRUSH rule 0 x 770 [593,909,482,259,5,550,961,324,309,772]
+ CRUSH rule 0 x 771 [309,935,121,578,937,685,933,571,822,256]
+ CRUSH rule 0 x 772 [12,125,797,301,348,419,891,959,487,355]
+ CRUSH rule 0 x 773 [253,466,820,549,591,193,783,951,982,160]
+ CRUSH rule 0 x 774 [164,390,705,109,881,505,890,425,599,485]
+ CRUSH rule 0 x 775 [703,47,43,973,643,406,885,976,936,221]
+ CRUSH rule 0 x 776 [728,231,80,916,2,850,396,76,680,108]
+ CRUSH rule 0 x 777 [981,621,568,729,869,952,563,860,388,456]
+ CRUSH rule 0 x 778 [411,456,544,597,789,784,65,954,125,358]
+ CRUSH rule 0 x 779 [346,121,519,921,587,48,772,645,254,759]
+ CRUSH rule 0 x 780 [476,39,288,381,303,29,17,336,147,829]
+ CRUSH rule 0 x 781 [10,130,585,844,729,705,714,954,271,58]
+ CRUSH rule 0 x 782 [462,246,581,902,623,877,812,516,774,985]
+ CRUSH rule 0 x 783 [580,373,153,775,668,661,626,961,576,119]
+ CRUSH rule 0 x 784 [413,113,978,990,994,56,481,198,171,944]
+ CRUSH rule 0 x 785 [341,856,332,354,59,581,632,151,586,360]
+ CRUSH rule 0 x 786 [411,140,313,393,215,618,490,481,627,740]
+ CRUSH rule 0 x 787 [605,522,211,813,636,224,600,528,966,556]
+ CRUSH rule 0 x 788 [226,545,35,142,726,851,194,216,486,782]
+ CRUSH rule 0 x 789 [545,320,414,702,731,277,237,916,374,670]
+ CRUSH rule 0 x 790 [414,748,816,327,130,115,788,164,691,329]
+ CRUSH rule 0 x 791 [660,906,406,697,916,322,124,128,742,990]
+ CRUSH rule 0 x 792 [287,392,514,204,75,789,406,858,694,351]
+ CRUSH rule 0 x 793 [631,133,850,713,720,487,376,812,886,264]
+ CRUSH rule 0 x 794 [931,517,543,210,963,898,811,459,344,719]
+ CRUSH rule 0 x 795 [551,962,477,948,425,434,268,94,648,402]
+ CRUSH rule 0 x 796 [814,4,95,27,368,300,646,451,67,738]
+ CRUSH rule 0 x 797 [64,201,299,734,605,864,596,196,93,636]
+ CRUSH rule 0 x 798 [422,530,114,431,565,716,473,250,839,895]
+ CRUSH rule 0 x 799 [824,32,679,562,266,549,859,994,831,60]
+ CRUSH rule 0 x 800 [862,623,489,637,861,196,941,643,398,325]
+ CRUSH rule 0 x 801 [145,550,329,324,734,160,219,662,142,28]
+ CRUSH rule 0 x 802 [570,19,847,308,387,518,846,53,783,511]
+ CRUSH rule 0 x 803 [151,812,662,358,880,349,834,881,23,229]
+ CRUSH rule 0 x 804 [467,93,264,863,176,842,663,949,380,39]
+ CRUSH rule 0 x 805 [621,223,938,809,591,686,121,157,934,660]
+ CRUSH rule 0 x 806 [898,957,805,430,499,584,640,607,790,832]
+ CRUSH rule 0 x 807 [354,531,422,159,921,431,802,136,305,983]
+ CRUSH rule 0 x 808 [7,96,76,897,446,2,166,929,234,460]
+ CRUSH rule 0 x 809 [70,734,719,56,687,21,23,145,184,465]
+ CRUSH rule 0 x 810 [701,18,972,327,771,649,620,648,433,997]
+ CRUSH rule 0 x 811 [248,547,103,728,901,264,948,202,521,278]
+ CRUSH rule 0 x 812 [230,576,821,566,993,762,675,28,263,410]
+ CRUSH rule 0 x 813 [805,114,683,629,801,462,285,450,948,742]
+ CRUSH rule 0 x 814 [54,619,973,741,497,894,401,266,905,320]
+ CRUSH rule 0 x 815 [679,412,613,132,969,411,314,670,928,727]
+ CRUSH rule 0 x 816 [919,448,826,414,36,289,44,822,332,959]
+ CRUSH rule 0 x 817 [765,830,436,521,332,458,260,172,193,516]
+ CRUSH rule 0 x 818 [415,566,644,687,692,414,769,826,519,277]
+ CRUSH rule 0 x 819 [721,319,865,750,546,859,523,770,56,437]
+ CRUSH rule 0 x 820 [218,301,333,190,686,179,535,787,267,46]
+ CRUSH rule 0 x 821 [185,795,680,953,329,750,621,815,313,916]
+ CRUSH rule 0 x 822 [356,261,54,522,900,103,883,112,601,15]
+ CRUSH rule 0 x 823 [220,281,549,456,64,306,282,641,216,929]
+ CRUSH rule 0 x 824 [292,809,887,74,776,788,559,886,753,749]
+ CRUSH rule 0 x 825 [949,778,101,311,110,480,161,998,370,10]
+ CRUSH rule 0 x 826 [767,818,833,927,356,954,910,63,288,836]
+ CRUSH rule 0 x 827 [631,83,406,635,657,713,212,916,692,653]
+ CRUSH rule 0 x 828 [288,986,445,26,414,607,937,595,935,672]
+ CRUSH rule 0 x 829 [990,667,915,694,974,453,669,330,822,36]
+ CRUSH rule 0 x 830 [152,571,778,505,685,209,448,55,965,851]
+ CRUSH rule 0 x 831 [814,563,630,97,582,107,142,157,957,105]
+ CRUSH rule 0 x 832 [235,641,616,110,979,844,656,135,341,922]
+ CRUSH rule 0 x 833 [657,565,922,140,825,457,764,766,853,890]
+ CRUSH rule 0 x 834 [907,231,644,13,617,130,83,483,811,98]
+ CRUSH rule 0 x 835 [784,262,771,264,612,238,537,937,101,507]
+ CRUSH rule 0 x 836 [951,158,366,710,43,427,351,961,52,44]
+ CRUSH rule 0 x 837 [556,498,334,633,895,627,903,29,454,647]
+ CRUSH rule 0 x 838 [329,274,964,547,119,342,983,998,320,935]
+ CRUSH rule 0 x 839 [568,209,939,364,658,747,47,859,402,947]
+ CRUSH rule 0 x 840 [45,579,842,70,655,862,815,109,762,642]
+ CRUSH rule 0 x 841 [652,702,24,605,152,93,226,46,918,220]
+ CRUSH rule 0 x 842 [629,984,314,895,408,897,575,1,312,542]
+ CRUSH rule 0 x 843 [799,690,688,648,151,812,486,199,966,501]
+ CRUSH rule 0 x 844 [694,600,534,700,569,11,899,382,851,472]
+ CRUSH rule 0 x 845 [332,30,179,93,951,324,611,512,855,760]
+ CRUSH rule 0 x 846 [452,251,712,719,404,739,606,237,414,844]
+ CRUSH rule 0 x 847 [399,681,847,739,13,555,363,893,592,634]
+ CRUSH rule 0 x 848 [303,138,440,346,547,216,700,249,214,100]
+ CRUSH rule 0 x 849 [666,346,708,873,64,694,847,463,995,314]
+ CRUSH rule 0 x 850 [644,511,345,844,545,337,358,35,913,310]
+ CRUSH rule 0 x 851 [527,546,737,425,100,331,95,337,677,275]
+ CRUSH rule 0 x 852 [31,809,94,618,156,853,469,511,999,340]
+ CRUSH rule 0 x 853 [483,330,869,184,46,942,774,679,616,492]
+ CRUSH rule 0 x 854 [697,953,968,143,502,955,441,302,437,53]
+ CRUSH rule 0 x 855 [837,996,239,621,32,191,686,702,919,971]
+ CRUSH rule 0 x 856 [712,40,547,430,195,857,224,810,404,126]
+ CRUSH rule 0 x 857 [77,984,576,551,568,96,12,763,594,668]
+ CRUSH rule 0 x 858 [412,384,841,465,572,576,688,61,545,491]
+ CRUSH rule 0 x 859 [173,760,26,300,87,567,463,903,272,8]
+ CRUSH rule 0 x 860 [776,429,328,917,658,783,699,907,532,627]
+ CRUSH rule 0 x 861 [705,405,477,50,73,714,901,487,725,204]
+ CRUSH rule 0 x 862 [809,44,788,938,964,177,490,409,15,842]
+ CRUSH rule 0 x 863 [349,496,963,178,675,853,172,980,772,115]
+ CRUSH rule 0 x 864 [717,858,101,239,992,244,43,15,29,974]
+ CRUSH rule 0 x 865 [857,603,586,262,550,289,850,40,170,31]
+ CRUSH rule 0 x 866 [394,304,71,96,642,155,255,481,435,119]
+ CRUSH rule 0 x 867 [640,773,663,974,261,296,988,730,753,888]
+ CRUSH rule 0 x 868 [613,950,712,663,666,460,643,547,734,16]
+ CRUSH rule 0 x 869 [973,889,524,22,671,477,718,431,968,472]
+ CRUSH rule 0 x 870 [505,35,386,498,348,503,54,992,726,783]
+ CRUSH rule 0 x 871 [239,264,262,773,781,734,387,515,98,232]
+ CRUSH rule 0 x 872 [21,767,456,748,783,797,180,800,521,270]
+ CRUSH rule 0 x 873 [954,666,980,264,435,233,199,358,805,255]
+ CRUSH rule 0 x 874 [54,510,947,1,500,119,93,915,801,43]
+ CRUSH rule 0 x 875 [809,418,452,462,88,673,634,435,778,884]
+ CRUSH rule 0 x 876 [483,457,61,248,523,277,322,141,82,412]
+ CRUSH rule 0 x 877 [542,531,952,939,710,179,181,460,459,527]
+ CRUSH rule 0 x 878 [217,674,857,644,678,809,329,591,59,4]
+ CRUSH rule 0 x 879 [999,475,134,250,319,357,145,750,54,997]
+ CRUSH rule 0 x 880 [678,573,935,385,570,651,319,630,888,970]
+ CRUSH rule 0 x 881 [394,835,789,802,587,155,570,109,896,826]
+ CRUSH rule 0 x 882 [467,382,353,56,979,674,974,483,412,547]
+ CRUSH rule 0 x 883 [802,744,237,337,50,96,202,148,129,72]
+ CRUSH rule 0 x 884 [653,660,638,700,31,558,389,381,347,314]
+ CRUSH rule 0 x 885 [898,704,307,445,879,872,174,972,544,894]
+ CRUSH rule 0 x 886 [434,357,938,641,737,8,56,582,915,541]
+ CRUSH rule 0 x 887 [297,226,711,428,370,318,472,947,35,528]
+ CRUSH rule 0 x 888 [863,324,443,213,902,25,806,53,385,387]
+ CRUSH rule 0 x 889 [105,102,308,163,947,548,399,382,761,907]
+ CRUSH rule 0 x 890 [550,248,606,704,615,708,996,561,485,482]
+ CRUSH rule 0 x 891 [575,928,880,891,826,763,706,701,501,680]
+ CRUSH rule 0 x 892 [259,862,133,271,292,162,53,333,458,77]
+ CRUSH rule 0 x 893 [902,880,543,542,37,942,672,320,394,373]
+ CRUSH rule 0 x 894 [180,169,916,43,945,713,648,685,895,735]
+ CRUSH rule 0 x 895 [725,849,182,129,177,272,599,829,809,713]
+ CRUSH rule 0 x 896 [951,34,874,537,969,123,210,529,491,289]
+ CRUSH rule 0 x 897 [810,352,73,939,943,895,12,481,539,562]
+ CRUSH rule 0 x 898 [979,433,719,411,787,359,342,37,303,70]
+ CRUSH rule 0 x 899 [685,668,534,932,399,156,124,653,574,384]
+ CRUSH rule 0 x 900 [530,978,41,894,941,681,380,419,667,56]
+ CRUSH rule 0 x 901 [740,107,336,175,574,706,157,292,724,805]
+ CRUSH rule 0 x 902 [800,743,693,310,67,111,178,624,733,498]
+ CRUSH rule 0 x 903 [230,267,842,266,550,769,66,738,419,199]
+ CRUSH rule 0 x 904 [346,949,460,973,696,91,957,801,74,934]
+ CRUSH rule 0 x 905 [530,397,619,958,576,973,685,6,689,387]
+ CRUSH rule 0 x 906 [80,426,138,672,73,776,30,169,506,497]
+ CRUSH rule 0 x 907 [365,968,475,297,296,724,664,331,184,461]
+ CRUSH rule 0 x 908 [204,832,742,809,862,745,484,391,841,967]
+ CRUSH rule 0 x 909 [883,989,146,959,366,59,686,965,515,421]
+ CRUSH rule 0 x 910 [549,593,249,853,792,769,824,552,717,159]
+ CRUSH rule 0 x 911 [325,847,352,214,851,732,789,255,896,868]
+ CRUSH rule 0 x 912 [874,888,582,796,557,601,226,889,69,237]
+ CRUSH rule 0 x 913 [331,463,342,574,989,362,925,746,664,533]
+ CRUSH rule 0 x 914 [836,468,601,732,607,275,70,280,837,367]
+ CRUSH rule 0 x 915 [245,228,100,661,799,13,126,79,652,793]
+ CRUSH rule 0 x 916 [77,967,364,435,27,474,255,133,892,524]
+ CRUSH rule 0 x 917 [239,60,866,221,772,967,725,707,47,216]
+ CRUSH rule 0 x 918 [988,115,922,80,201,544,583,923,863,232]
+ CRUSH rule 0 x 919 [783,139,696,1,848,169,888,980,33,261]
+ CRUSH rule 0 x 920 [623,408,685,953,974,696,532,124,911,206]
+ CRUSH rule 0 x 921 [105,799,144,90,399,373,633,290,155,137]
+ CRUSH rule 0 x 922 [887,505,652,348,514,806,952,474,67,938]
+ CRUSH rule 0 x 923 [223,318,552,458,743,871,964,384,454,448]
+ CRUSH rule 0 x 924 [25,778,366,333,163,801,584,31,151,178]
+ CRUSH rule 0 x 925 [912,601,297,682,770,173,969,168,500,68]
+ CRUSH rule 0 x 926 [968,133,132,144,814,155,709,158,96,739]
+ CRUSH rule 0 x 927 [277,724,214,988,690,342,465,775,725,414]
+ CRUSH rule 0 x 928 [554,203,658,789,298,299,847,752,780,738]
+ CRUSH rule 0 x 929 [761,802,367,528,758,522,744,171,144,704]
+ CRUSH rule 0 x 930 [814,61,788,736,660,491,832,654,567,160]
+ CRUSH rule 0 x 931 [29,193,61,41,343,664,487,839,776,117]
+ CRUSH rule 0 x 932 [446,198,862,534,168,35,530,462,202,11]
+ CRUSH rule 0 x 933 [352,742,216,321,525,44,568,61,945,154]
+ CRUSH rule 0 x 934 [730,2,332,631,613,249,533,116,254,569]
+ CRUSH rule 0 x 935 [731,23,736,79,361,992,772,49,567,47]
+ CRUSH rule 0 x 936 [322,975,20,904,827,603,138,802,885,447]
+ CRUSH rule 0 x 937 [822,221,841,161,723,137,630,308,973,934]
+ CRUSH rule 0 x 938 [557,850,66,630,499,404,286,395,927,611]
+ CRUSH rule 0 x 939 [150,11,971,371,124,785,408,49,977,243]
+ CRUSH rule 0 x 940 [638,398,169,616,333,751,25,883,867,270]
+ CRUSH rule 0 x 941 [730,342,929,577,451,838,964,28,633,960]
+ CRUSH rule 0 x 942 [62,292,166,814,587,172,171,16,440,31]
+ CRUSH rule 0 x 943 [165,314,519,548,41,726,759,851,617,420]
+ CRUSH rule 0 x 944 [199,625,766,176,194,297,678,915,619,69]
+ CRUSH rule 0 x 945 [946,999,699,303,38,81,952,885,987,775]
+ CRUSH rule 0 x 946 [595,93,852,142,503,647,933,267,846,866]
+ CRUSH rule 0 x 947 [800,582,356,93,716,117,922,868,413,545]
+ CRUSH rule 0 x 948 [132,551,139,920,87,46,81,220,725,211]
+ CRUSH rule 0 x 949 [792,920,466,380,97,568,799,961,564,71]
+ CRUSH rule 0 x 950 [111,345,176,543,879,954,355,220,528,747]
+ CRUSH rule 0 x 951 [414,619,648,655,364,971,829,408,568,734]
+ CRUSH rule 0 x 952 [775,469,500,356,287,4,16,746,835,529]
+ CRUSH rule 0 x 953 [349,1,5,251,168,680,141,619,234,517]
+ CRUSH rule 0 x 954 [570,940,410,249,929,394,129,696,115,984]
+ CRUSH rule 0 x 955 [729,774,823,800,7,127,536,766,579,398]
+ CRUSH rule 0 x 956 [519,141,575,625,738,475,169,751,667,381]
+ CRUSH rule 0 x 957 [242,709,611,97,760,309,393,281,227,412]
+ CRUSH rule 0 x 958 [84,217,227,253,246,604,346,377,425,533]
+ CRUSH rule 0 x 959 [270,413,918,789,703,608,543,519,496,956]
+ CRUSH rule 0 x 960 [458,192,307,279,920,139,855,49,548,304]
+ CRUSH rule 0 x 961 [981,388,777,546,359,660,455,708,649,93]
+ CRUSH rule 0 x 962 [623,834,277,134,729,246,856,477,895,89]
+ CRUSH rule 0 x 963 [291,167,714,468,109,373,485,701,76,55]
+ CRUSH rule 0 x 964 [28,156,788,127,598,215,361,255,507,540]
+ CRUSH rule 0 x 965 [675,557,290,517,840,510,59,229,819,610]
+ CRUSH rule 0 x 966 [836,306,946,283,642,606,929,773,928,579]
+ CRUSH rule 0 x 967 [966,386,735,837,392,116,19,674,395,483]
+ CRUSH rule 0 x 968 [864,756,690,121,328,122,433,520,916,41]
+ CRUSH rule 0 x 969 [729,625,480,769,512,882,518,956,398,269]
+ CRUSH rule 0 x 970 [800,362,646,582,309,102,576,411,416,523]
+ CRUSH rule 0 x 971 [737,381,153,684,298,166,344,520,546,612]
+ CRUSH rule 0 x 972 [952,245,720,884,334,311,754,540,79,174]
+ CRUSH rule 0 x 973 [356,455,579,857,832,596,549,524,109,364]
+ CRUSH rule 0 x 974 [545,758,586,596,756,790,116,993,644,405]
+ CRUSH rule 0 x 975 [336,191,202,146,720,897,330,308,744,843]
+ CRUSH rule 0 x 976 [446,208,757,620,252,846,397,58,57,603]
+ CRUSH rule 0 x 977 [202,896,196,956,763,126,783,828,409,529]
+ CRUSH rule 0 x 978 [612,324,996,225,418,583,514,169,99,878]
+ CRUSH rule 0 x 979 [843,457,675,650,958,657,677,173,903,781]
+ CRUSH rule 0 x 980 [60,914,881,626,850,759,398,943,764,867]
+ CRUSH rule 0 x 981 [702,749,937,153,724,514,536,212,247,523]
+ CRUSH rule 0 x 982 [298,928,738,167,99,668,395,198,100,580]
+ CRUSH rule 0 x 983 [723,572,395,358,900,37,927,597,103,461]
+ CRUSH rule 0 x 984 [723,864,804,935,846,993,950,840,427,469]
+ CRUSH rule 0 x 985 [945,459,868,211,524,954,911,208,91,999]
+ CRUSH rule 0 x 986 [772,664,535,169,297,996,864,555,687,212]
+ CRUSH rule 0 x 987 [88,324,312,843,661,580,76,894,480,323]
+ CRUSH rule 0 x 988 [522,927,131,996,351,685,865,47,116,230]
+ CRUSH rule 0 x 989 [578,332,208,605,975,207,155,380,797,177]
+ CRUSH rule 0 x 990 [638,228,414,311,738,698,340,526,728,595]
+ CRUSH rule 0 x 991 [530,221,451,422,879,916,754,928,288,668]
+ CRUSH rule 0 x 992 [925,705,275,81,234,310,117,546,798,777]
+ CRUSH rule 0 x 993 [991,301,43,469,830,242,382,428,451,216]
+ CRUSH rule 0 x 994 [276,51,868,683,843,815,557,378,936,192]
+ CRUSH rule 0 x 995 [288,836,753,790,758,120,158,265,110,171]
+ CRUSH rule 0 x 996 [887,983,252,686,470,345,459,764,859,776]
+ CRUSH rule 0 x 997 [110,924,386,79,705,697,210,698,273,955]
+ CRUSH rule 0 x 998 [435,830,485,853,926,730,786,762,444,561]
+ CRUSH rule 0 x 999 [876,738,357,913,723,51,15,585,898,902]
+ CRUSH rule 0 x 1000 [178,963,638,430,845,586,317,102,200,662]
+ CRUSH rule 0 x 1001 [99,519,66,759,583,944,739,922,343,574]
+ CRUSH rule 0 x 1002 [515,534,468,866,878,717,729,370,326,640]
+ CRUSH rule 0 x 1003 [104,611,937,698,94,67,614,783,865,245]
+ CRUSH rule 0 x 1004 [269,638,724,375,491,121,891,113,424,320]
+ CRUSH rule 0 x 1005 [369,223,309,409,822,39,597,969,911,241]
+ CRUSH rule 0 x 1006 [40,107,69,275,79,429,234,945,598,498]
+ CRUSH rule 0 x 1007 [978,111,416,758,454,640,5,444,795,150]
+ CRUSH rule 0 x 1008 [965,956,624,832,421,96,975,723,909,93]
+ CRUSH rule 0 x 1009 [598,476,356,695,919,566,234,383,604,903]
+ CRUSH rule 0 x 1010 [767,523,239,517,29,77,23,241,838,865]
+ CRUSH rule 0 x 1011 [289,871,207,576,347,698,48,570,639,230]
+ CRUSH rule 0 x 1012 [128,28,370,31,341,755,268,647,669,90]
+ CRUSH rule 0 x 1013 [979,765,660,812,666,187,808,351,572,403]
+ CRUSH rule 0 x 1014 [979,948,513,88,47,825,969,81,586,62]
+ CRUSH rule 0 x 1015 [277,790,396,672,542,647,145,11,965,669]
+ CRUSH rule 0 x 1016 [262,73,128,886,839,685,456,560,935,733]
+ CRUSH rule 0 x 1017 [150,269,61,499,832,591,637,731,738,154]
+ CRUSH rule 0 x 1018 [555,829,554,944,406,576,463,926,475,316]
+ CRUSH rule 0 x 1019 [513,356,265,446,65,288,768,245,337,197]
+ CRUSH rule 0 x 1020 [158,161,877,704,948,570,495,865,698,872]
+ CRUSH rule 0 x 1021 [915,998,957,285,546,202,676,322,671,622]
+ CRUSH rule 0 x 1022 [967,829,973,640,703,470,871,828,440,449]
+ CRUSH rule 0 x 1023 [488,257,614,859,325,419,50,560,595,554]
+ rule 0 (data) num_rep 10 result size == 10:\t1024/1024 (esc)
diff --git a/src/test/cli/crushtool/test-map-tries-vs-retries.crushmap b/src/test/cli/crushtool/test-map-tries-vs-retries.crushmap
new file mode 100644
index 000000000..e36c0eb9f
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-tries-vs-retries.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/test-map-tries-vs-retries.t b/src/test/cli/crushtool/test-map-tries-vs-retries.t
new file mode 100644
index 000000000..f5ac22044
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-tries-vs-retries.t
@@ -0,0 +1,10259 @@
+ $ crushtool -i "$TESTDIR/test-map-tries-vs-retries.crushmap" --test --show-mappings --show-statistics --weight 0 0 --weight 8 0 --min-rep 1 --max-rep 10
+ rule 0 (replicated_ruleset), x = 0..1023, numrep = 1..10
+ CRUSH rule 0 x 0 [7]
+ CRUSH rule 0 x 1 [10]
+ CRUSH rule 0 x 2 [1]
+ CRUSH rule 0 x 3 [15]
+ CRUSH rule 0 x 4 [14]
+ CRUSH rule 0 x 5 [7]
+ CRUSH rule 0 x 6 [12]
+ CRUSH rule 0 x 7 [9]
+ CRUSH rule 0 x 8 [10]
+ CRUSH rule 0 x 9 [7]
+ CRUSH rule 0 x 10 [10]
+ CRUSH rule 0 x 11 [13]
+ CRUSH rule 0 x 12 [7]
+ CRUSH rule 0 x 13 [3]
+ CRUSH rule 0 x 14 [13]
+ CRUSH rule 0 x 15 [15]
+ CRUSH rule 0 x 16 [7]
+ CRUSH rule 0 x 17 [10]
+ CRUSH rule 0 x 18 [1]
+ CRUSH rule 0 x 19 [7]
+ CRUSH rule 0 x 20 [14]
+ CRUSH rule 0 x 21 [3]
+ CRUSH rule 0 x 22 [6]
+ CRUSH rule 0 x 23 [10]
+ CRUSH rule 0 x 24 [12]
+ CRUSH rule 0 x 25 [7]
+ CRUSH rule 0 x 26 [1]
+ CRUSH rule 0 x 27 [3]
+ CRUSH rule 0 x 28 [14]
+ CRUSH rule 0 x 29 [5]
+ CRUSH rule 0 x 30 [2]
+ CRUSH rule 0 x 31 [5]
+ CRUSH rule 0 x 32 [9]
+ CRUSH rule 0 x 33 [13]
+ CRUSH rule 0 x 34 [13]
+ CRUSH rule 0 x 35 [4]
+ CRUSH rule 0 x 36 [3]
+ CRUSH rule 0 x 37 [9]
+ CRUSH rule 0 x 38 [3]
+ CRUSH rule 0 x 39 [12]
+ CRUSH rule 0 x 40 [10]
+ CRUSH rule 0 x 41 [4]
+ CRUSH rule 0 x 42 [3]
+ CRUSH rule 0 x 43 [10]
+ CRUSH rule 0 x 44 [11]
+ CRUSH rule 0 x 45 [11]
+ CRUSH rule 0 x 46 [6]
+ CRUSH rule 0 x 47 [3]
+ CRUSH rule 0 x 48 [4]
+ CRUSH rule 0 x 49 [9]
+ CRUSH rule 0 x 50 [14]
+ CRUSH rule 0 x 51 [10]
+ CRUSH rule 0 x 52 [12]
+ CRUSH rule 0 x 53 [3]
+ CRUSH rule 0 x 54 [4]
+ CRUSH rule 0 x 55 [4]
+ CRUSH rule 0 x 56 [5]
+ CRUSH rule 0 x 57 [6]
+ CRUSH rule 0 x 58 [7]
+ CRUSH rule 0 x 59 [2]
+ CRUSH rule 0 x 60 [3]
+ CRUSH rule 0 x 61 [3]
+ CRUSH rule 0 x 62 [15]
+ CRUSH rule 0 x 63 [10]
+ CRUSH rule 0 x 64 [3]
+ CRUSH rule 0 x 65 [4]
+ CRUSH rule 0 x 66 [15]
+ CRUSH rule 0 x 67 [2]
+ CRUSH rule 0 x 68 [15]
+ CRUSH rule 0 x 69 [2]
+ CRUSH rule 0 x 70 [9]
+ CRUSH rule 0 x 71 [15]
+ CRUSH rule 0 x 72 [9]
+ CRUSH rule 0 x 73 [5]
+ CRUSH rule 0 x 74 [11]
+ CRUSH rule 0 x 75 [9]
+ CRUSH rule 0 x 76 [6]
+ CRUSH rule 0 x 77 [7]
+ CRUSH rule 0 x 78 [9]
+ CRUSH rule 0 x 79 [13]
+ CRUSH rule 0 x 80 [15]
+ CRUSH rule 0 x 81 [15]
+ CRUSH rule 0 x 82 [14]
+ CRUSH rule 0 x 83 [4]
+ CRUSH rule 0 x 84 [10]
+ CRUSH rule 0 x 85 [3]
+ CRUSH rule 0 x 86 [10]
+ CRUSH rule 0 x 87 [15]
+ CRUSH rule 0 x 88 [4]
+ CRUSH rule 0 x 89 [3]
+ CRUSH rule 0 x 90 [4]
+ CRUSH rule 0 x 91 [6]
+ CRUSH rule 0 x 92 [1]
+ CRUSH rule 0 x 93 [9]
+ CRUSH rule 0 x 94 [9]
+ CRUSH rule 0 x 95 [7]
+ CRUSH rule 0 x 96 [2]
+ CRUSH rule 0 x 97 [4]
+ CRUSH rule 0 x 98 [11]
+ CRUSH rule 0 x 99 [12]
+ CRUSH rule 0 x 100 [9]
+ CRUSH rule 0 x 101 [15]
+ CRUSH rule 0 x 102 [3]
+ CRUSH rule 0 x 103 [13]
+ CRUSH rule 0 x 104 [14]
+ CRUSH rule 0 x 105 [14]
+ CRUSH rule 0 x 106 [6]
+ CRUSH rule 0 x 107 [3]
+ CRUSH rule 0 x 108 [5]
+ CRUSH rule 0 x 109 [9]
+ CRUSH rule 0 x 110 [5]
+ CRUSH rule 0 x 111 [10]
+ CRUSH rule 0 x 112 [1]
+ CRUSH rule 0 x 113 [6]
+ CRUSH rule 0 x 114 [5]
+ CRUSH rule 0 x 115 [10]
+ CRUSH rule 0 x 116 [1]
+ CRUSH rule 0 x 117 [5]
+ CRUSH rule 0 x 118 [10]
+ CRUSH rule 0 x 119 [14]
+ CRUSH rule 0 x 120 [11]
+ CRUSH rule 0 x 121 [9]
+ CRUSH rule 0 x 122 [4]
+ CRUSH rule 0 x 123 [3]
+ CRUSH rule 0 x 124 [12]
+ CRUSH rule 0 x 125 [9]
+ CRUSH rule 0 x 126 [7]
+ CRUSH rule 0 x 127 [4]
+ CRUSH rule 0 x 128 [3]
+ CRUSH rule 0 x 129 [11]
+ CRUSH rule 0 x 130 [3]
+ CRUSH rule 0 x 131 [12]
+ CRUSH rule 0 x 132 [11]
+ CRUSH rule 0 x 133 [3]
+ CRUSH rule 0 x 134 [12]
+ CRUSH rule 0 x 135 [3]
+ CRUSH rule 0 x 136 [15]
+ CRUSH rule 0 x 137 [14]
+ CRUSH rule 0 x 138 [13]
+ CRUSH rule 0 x 139 [11]
+ CRUSH rule 0 x 140 [11]
+ CRUSH rule 0 x 141 [6]
+ CRUSH rule 0 x 142 [3]
+ CRUSH rule 0 x 143 [9]
+ CRUSH rule 0 x 144 [13]
+ CRUSH rule 0 x 145 [12]
+ CRUSH rule 0 x 146 [1]
+ CRUSH rule 0 x 147 [1]
+ CRUSH rule 0 x 148 [12]
+ CRUSH rule 0 x 149 [2]
+ CRUSH rule 0 x 150 [1]
+ CRUSH rule 0 x 151 [2]
+ CRUSH rule 0 x 152 [5]
+ CRUSH rule 0 x 153 [6]
+ CRUSH rule 0 x 154 [3]
+ CRUSH rule 0 x 155 [14]
+ CRUSH rule 0 x 156 [7]
+ CRUSH rule 0 x 157 [15]
+ CRUSH rule 0 x 158 [15]
+ CRUSH rule 0 x 159 [4]
+ CRUSH rule 0 x 160 [5]
+ CRUSH rule 0 x 161 [1]
+ CRUSH rule 0 x 162 [10]
+ CRUSH rule 0 x 163 [15]
+ CRUSH rule 0 x 164 [9]
+ CRUSH rule 0 x 165 [11]
+ CRUSH rule 0 x 166 [1]
+ CRUSH rule 0 x 167 [9]
+ CRUSH rule 0 x 168 [13]
+ CRUSH rule 0 x 169 [1]
+ CRUSH rule 0 x 170 [1]
+ CRUSH rule 0 x 171 [9]
+ CRUSH rule 0 x 172 [14]
+ CRUSH rule 0 x 173 [5]
+ CRUSH rule 0 x 174 [15]
+ CRUSH rule 0 x 175 [5]
+ CRUSH rule 0 x 176 [9]
+ CRUSH rule 0 x 177 [2]
+ CRUSH rule 0 x 178 [12]
+ CRUSH rule 0 x 179 [2]
+ CRUSH rule 0 x 180 [3]
+ CRUSH rule 0 x 181 [9]
+ CRUSH rule 0 x 182 [5]
+ CRUSH rule 0 x 183 [5]
+ CRUSH rule 0 x 184 [2]
+ CRUSH rule 0 x 185 [13]
+ CRUSH rule 0 x 186 [6]
+ CRUSH rule 0 x 187 [1]
+ CRUSH rule 0 x 188 [9]
+ CRUSH rule 0 x 189 [6]
+ CRUSH rule 0 x 190 [9]
+ CRUSH rule 0 x 191 [7]
+ CRUSH rule 0 x 192 [2]
+ CRUSH rule 0 x 193 [3]
+ CRUSH rule 0 x 194 [3]
+ CRUSH rule 0 x 195 [5]
+ CRUSH rule 0 x 196 [4]
+ CRUSH rule 0 x 197 [14]
+ CRUSH rule 0 x 198 [2]
+ CRUSH rule 0 x 199 [2]
+ CRUSH rule 0 x 200 [7]
+ CRUSH rule 0 x 201 [9]
+ CRUSH rule 0 x 202 [14]
+ CRUSH rule 0 x 203 [12]
+ CRUSH rule 0 x 204 [6]
+ CRUSH rule 0 x 205 [15]
+ CRUSH rule 0 x 206 [13]
+ CRUSH rule 0 x 207 [2]
+ CRUSH rule 0 x 208 [13]
+ CRUSH rule 0 x 209 [6]
+ CRUSH rule 0 x 210 [13]
+ CRUSH rule 0 x 211 [2]
+ CRUSH rule 0 x 212 [10]
+ CRUSH rule 0 x 213 [3]
+ CRUSH rule 0 x 214 [7]
+ CRUSH rule 0 x 215 [6]
+ CRUSH rule 0 x 216 [12]
+ CRUSH rule 0 x 217 [12]
+ CRUSH rule 0 x 218 [12]
+ CRUSH rule 0 x 219 [3]
+ CRUSH rule 0 x 220 [14]
+ CRUSH rule 0 x 221 [15]
+ CRUSH rule 0 x 222 [10]
+ CRUSH rule 0 x 223 [9]
+ CRUSH rule 0 x 224 [1]
+ CRUSH rule 0 x 225 [10]
+ CRUSH rule 0 x 226 [4]
+ CRUSH rule 0 x 227 [7]
+ CRUSH rule 0 x 228 [2]
+ CRUSH rule 0 x 229 [9]
+ CRUSH rule 0 x 230 [10]
+ CRUSH rule 0 x 231 [2]
+ CRUSH rule 0 x 232 [10]
+ CRUSH rule 0 x 233 [6]
+ CRUSH rule 0 x 234 [10]
+ CRUSH rule 0 x 235 [13]
+ CRUSH rule 0 x 236 [2]
+ CRUSH rule 0 x 237 [3]
+ CRUSH rule 0 x 238 [2]
+ CRUSH rule 0 x 239 [4]
+ CRUSH rule 0 x 240 [15]
+ CRUSH rule 0 x 241 [7]
+ CRUSH rule 0 x 242 [14]
+ CRUSH rule 0 x 243 [2]
+ CRUSH rule 0 x 244 [13]
+ CRUSH rule 0 x 245 [12]
+ CRUSH rule 0 x 246 [15]
+ CRUSH rule 0 x 247 [6]
+ CRUSH rule 0 x 248 [5]
+ CRUSH rule 0 x 249 [10]
+ CRUSH rule 0 x 250 [12]
+ CRUSH rule 0 x 251 [13]
+ CRUSH rule 0 x 252 [7]
+ CRUSH rule 0 x 253 [3]
+ CRUSH rule 0 x 254 [2]
+ CRUSH rule 0 x 255 [1]
+ CRUSH rule 0 x 256 [6]
+ CRUSH rule 0 x 257 [15]
+ CRUSH rule 0 x 258 [12]
+ CRUSH rule 0 x 259 [9]
+ CRUSH rule 0 x 260 [10]
+ CRUSH rule 0 x 261 [13]
+ CRUSH rule 0 x 262 [15]
+ CRUSH rule 0 x 263 [12]
+ CRUSH rule 0 x 264 [13]
+ CRUSH rule 0 x 265 [12]
+ CRUSH rule 0 x 266 [14]
+ CRUSH rule 0 x 267 [12]
+ CRUSH rule 0 x 268 [4]
+ CRUSH rule 0 x 269 [11]
+ CRUSH rule 0 x 270 [7]
+ CRUSH rule 0 x 271 [4]
+ CRUSH rule 0 x 272 [15]
+ CRUSH rule 0 x 273 [2]
+ CRUSH rule 0 x 274 [10]
+ CRUSH rule 0 x 275 [10]
+ CRUSH rule 0 x 276 [5]
+ CRUSH rule 0 x 277 [14]
+ CRUSH rule 0 x 278 [5]
+ CRUSH rule 0 x 279 [6]
+ CRUSH rule 0 x 280 [7]
+ CRUSH rule 0 x 281 [5]
+ CRUSH rule 0 x 282 [2]
+ CRUSH rule 0 x 283 [4]
+ CRUSH rule 0 x 284 [5]
+ CRUSH rule 0 x 285 [15]
+ CRUSH rule 0 x 286 [10]
+ CRUSH rule 0 x 287 [12]
+ CRUSH rule 0 x 288 [4]
+ CRUSH rule 0 x 289 [2]
+ CRUSH rule 0 x 290 [12]
+ CRUSH rule 0 x 291 [7]
+ CRUSH rule 0 x 292 [4]
+ CRUSH rule 0 x 293 [6]
+ CRUSH rule 0 x 294 [9]
+ CRUSH rule 0 x 295 [6]
+ CRUSH rule 0 x 296 [3]
+ CRUSH rule 0 x 297 [6]
+ CRUSH rule 0 x 298 [14]
+ CRUSH rule 0 x 299 [14]
+ CRUSH rule 0 x 300 [15]
+ CRUSH rule 0 x 301 [9]
+ CRUSH rule 0 x 302 [9]
+ CRUSH rule 0 x 303 [4]
+ CRUSH rule 0 x 304 [6]
+ CRUSH rule 0 x 305 [13]
+ CRUSH rule 0 x 306 [10]
+ CRUSH rule 0 x 307 [11]
+ CRUSH rule 0 x 308 [12]
+ CRUSH rule 0 x 309 [9]
+ CRUSH rule 0 x 310 [3]
+ CRUSH rule 0 x 311 [3]
+ CRUSH rule 0 x 312 [15]
+ CRUSH rule 0 x 313 [9]
+ CRUSH rule 0 x 314 [2]
+ CRUSH rule 0 x 315 [15]
+ CRUSH rule 0 x 316 [4]
+ CRUSH rule 0 x 317 [1]
+ CRUSH rule 0 x 318 [4]
+ CRUSH rule 0 x 319 [2]
+ CRUSH rule 0 x 320 [5]
+ CRUSH rule 0 x 321 [1]
+ CRUSH rule 0 x 322 [13]
+ CRUSH rule 0 x 323 [7]
+ CRUSH rule 0 x 324 [5]
+ CRUSH rule 0 x 325 [9]
+ CRUSH rule 0 x 326 [11]
+ CRUSH rule 0 x 327 [12]
+ CRUSH rule 0 x 328 [5]
+ CRUSH rule 0 x 329 [2]
+ CRUSH rule 0 x 330 [3]
+ CRUSH rule 0 x 331 [12]
+ CRUSH rule 0 x 332 [10]
+ CRUSH rule 0 x 333 [6]
+ CRUSH rule 0 x 334 [4]
+ CRUSH rule 0 x 335 [11]
+ CRUSH rule 0 x 336 [6]
+ CRUSH rule 0 x 337 [15]
+ CRUSH rule 0 x 338 [10]
+ CRUSH rule 0 x 339 [11]
+ CRUSH rule 0 x 340 [11]
+ CRUSH rule 0 x 341 [7]
+ CRUSH rule 0 x 342 [12]
+ CRUSH rule 0 x 343 [12]
+ CRUSH rule 0 x 344 [9]
+ CRUSH rule 0 x 345 [14]
+ CRUSH rule 0 x 346 [5]
+ CRUSH rule 0 x 347 [10]
+ CRUSH rule 0 x 348 [7]
+ CRUSH rule 0 x 349 [9]
+ CRUSH rule 0 x 350 [13]
+ CRUSH rule 0 x 351 [13]
+ CRUSH rule 0 x 352 [1]
+ CRUSH rule 0 x 353 [10]
+ CRUSH rule 0 x 354 [6]
+ CRUSH rule 0 x 355 [13]
+ CRUSH rule 0 x 356 [15]
+ CRUSH rule 0 x 357 [4]
+ CRUSH rule 0 x 358 [12]
+ CRUSH rule 0 x 359 [5]
+ CRUSH rule 0 x 360 [13]
+ CRUSH rule 0 x 361 [5]
+ CRUSH rule 0 x 362 [2]
+ CRUSH rule 0 x 363 [7]
+ CRUSH rule 0 x 364 [2]
+ CRUSH rule 0 x 365 [13]
+ CRUSH rule 0 x 366 [12]
+ CRUSH rule 0 x 367 [7]
+ CRUSH rule 0 x 368 [7]
+ CRUSH rule 0 x 369 [7]
+ CRUSH rule 0 x 370 [4]
+ CRUSH rule 0 x 371 [1]
+ CRUSH rule 0 x 372 [10]
+ CRUSH rule 0 x 373 [15]
+ CRUSH rule 0 x 374 [3]
+ CRUSH rule 0 x 375 [5]
+ CRUSH rule 0 x 376 [5]
+ CRUSH rule 0 x 377 [1]
+ CRUSH rule 0 x 378 [9]
+ CRUSH rule 0 x 379 [11]
+ CRUSH rule 0 x 380 [6]
+ CRUSH rule 0 x 381 [15]
+ CRUSH rule 0 x 382 [14]
+ CRUSH rule 0 x 383 [3]
+ CRUSH rule 0 x 384 [4]
+ CRUSH rule 0 x 385 [4]
+ CRUSH rule 0 x 386 [14]
+ CRUSH rule 0 x 387 [1]
+ CRUSH rule 0 x 388 [2]
+ CRUSH rule 0 x 389 [12]
+ CRUSH rule 0 x 390 [2]
+ CRUSH rule 0 x 391 [3]
+ CRUSH rule 0 x 392 [11]
+ CRUSH rule 0 x 393 [2]
+ CRUSH rule 0 x 394 [4]
+ CRUSH rule 0 x 395 [10]
+ CRUSH rule 0 x 396 [2]
+ CRUSH rule 0 x 397 [1]
+ CRUSH rule 0 x 398 [9]
+ CRUSH rule 0 x 399 [5]
+ CRUSH rule 0 x 400 [10]
+ CRUSH rule 0 x 401 [6]
+ CRUSH rule 0 x 402 [4]
+ CRUSH rule 0 x 403 [7]
+ CRUSH rule 0 x 404 [14]
+ CRUSH rule 0 x 405 [9]
+ CRUSH rule 0 x 406 [12]
+ CRUSH rule 0 x 407 [9]
+ CRUSH rule 0 x 408 [7]
+ CRUSH rule 0 x 409 [11]
+ CRUSH rule 0 x 410 [6]
+ CRUSH rule 0 x 411 [13]
+ CRUSH rule 0 x 412 [5]
+ CRUSH rule 0 x 413 [13]
+ CRUSH rule 0 x 414 [3]
+ CRUSH rule 0 x 415 [6]
+ CRUSH rule 0 x 416 [13]
+ CRUSH rule 0 x 417 [4]
+ CRUSH rule 0 x 418 [14]
+ CRUSH rule 0 x 419 [5]
+ CRUSH rule 0 x 420 [2]
+ CRUSH rule 0 x 421 [15]
+ CRUSH rule 0 x 422 [4]
+ CRUSH rule 0 x 423 [3]
+ CRUSH rule 0 x 424 [6]
+ CRUSH rule 0 x 425 [11]
+ CRUSH rule 0 x 426 [12]
+ CRUSH rule 0 x 427 [14]
+ CRUSH rule 0 x 428 [12]
+ CRUSH rule 0 x 429 [3]
+ CRUSH rule 0 x 430 [3]
+ CRUSH rule 0 x 431 [9]
+ CRUSH rule 0 x 432 [4]
+ CRUSH rule 0 x 433 [4]
+ CRUSH rule 0 x 434 [2]
+ CRUSH rule 0 x 435 [13]
+ CRUSH rule 0 x 436 [9]
+ CRUSH rule 0 x 437 [9]
+ CRUSH rule 0 x 438 [7]
+ CRUSH rule 0 x 439 [7]
+ CRUSH rule 0 x 440 [14]
+ CRUSH rule 0 x 441 [2]
+ CRUSH rule 0 x 442 [10]
+ CRUSH rule 0 x 443 [12]
+ CRUSH rule 0 x 444 [4]
+ CRUSH rule 0 x 445 [4]
+ CRUSH rule 0 x 446 [12]
+ CRUSH rule 0 x 447 [15]
+ CRUSH rule 0 x 448 [5]
+ CRUSH rule 0 x 449 [14]
+ CRUSH rule 0 x 450 [2]
+ CRUSH rule 0 x 451 [6]
+ CRUSH rule 0 x 452 [14]
+ CRUSH rule 0 x 453 [5]
+ CRUSH rule 0 x 454 [10]
+ CRUSH rule 0 x 455 [6]
+ CRUSH rule 0 x 456 [5]
+ CRUSH rule 0 x 457 [9]
+ CRUSH rule 0 x 458 [9]
+ CRUSH rule 0 x 459 [13]
+ CRUSH rule 0 x 460 [5]
+ CRUSH rule 0 x 461 [4]
+ CRUSH rule 0 x 462 [4]
+ CRUSH rule 0 x 463 [4]
+ CRUSH rule 0 x 464 [4]
+ CRUSH rule 0 x 465 [5]
+ CRUSH rule 0 x 466 [13]
+ CRUSH rule 0 x 467 [13]
+ CRUSH rule 0 x 468 [10]
+ CRUSH rule 0 x 469 [4]
+ CRUSH rule 0 x 470 [3]
+ CRUSH rule 0 x 471 [6]
+ CRUSH rule 0 x 472 [2]
+ CRUSH rule 0 x 473 [15]
+ CRUSH rule 0 x 474 [15]
+ CRUSH rule 0 x 475 [10]
+ CRUSH rule 0 x 476 [3]
+ CRUSH rule 0 x 477 [6]
+ CRUSH rule 0 x 478 [4]
+ CRUSH rule 0 x 479 [13]
+ CRUSH rule 0 x 480 [1]
+ CRUSH rule 0 x 481 [15]
+ CRUSH rule 0 x 482 [2]
+ CRUSH rule 0 x 483 [10]
+ CRUSH rule 0 x 484 [1]
+ CRUSH rule 0 x 485 [9]
+ CRUSH rule 0 x 486 [3]
+ CRUSH rule 0 x 487 [12]
+ CRUSH rule 0 x 488 [14]
+ CRUSH rule 0 x 489 [11]
+ CRUSH rule 0 x 490 [4]
+ CRUSH rule 0 x 491 [1]
+ CRUSH rule 0 x 492 [5]
+ CRUSH rule 0 x 493 [12]
+ CRUSH rule 0 x 494 [1]
+ CRUSH rule 0 x 495 [3]
+ CRUSH rule 0 x 496 [5]
+ CRUSH rule 0 x 497 [13]
+ CRUSH rule 0 x 498 [10]
+ CRUSH rule 0 x 499 [14]
+ CRUSH rule 0 x 500 [15]
+ CRUSH rule 0 x 501 [10]
+ CRUSH rule 0 x 502 [5]
+ CRUSH rule 0 x 503 [15]
+ CRUSH rule 0 x 504 [13]
+ CRUSH rule 0 x 505 [12]
+ CRUSH rule 0 x 506 [11]
+ CRUSH rule 0 x 507 [4]
+ CRUSH rule 0 x 508 [12]
+ CRUSH rule 0 x 509 [4]
+ CRUSH rule 0 x 510 [5]
+ CRUSH rule 0 x 511 [2]
+ CRUSH rule 0 x 512 [15]
+ CRUSH rule 0 x 513 [4]
+ CRUSH rule 0 x 514 [11]
+ CRUSH rule 0 x 515 [12]
+ CRUSH rule 0 x 516 [14]
+ CRUSH rule 0 x 517 [11]
+ CRUSH rule 0 x 518 [3]
+ CRUSH rule 0 x 519 [12]
+ CRUSH rule 0 x 520 [12]
+ CRUSH rule 0 x 521 [11]
+ CRUSH rule 0 x 522 [4]
+ CRUSH rule 0 x 523 [3]
+ CRUSH rule 0 x 524 [15]
+ CRUSH rule 0 x 525 [3]
+ CRUSH rule 0 x 526 [10]
+ CRUSH rule 0 x 527 [3]
+ CRUSH rule 0 x 528 [12]
+ CRUSH rule 0 x 529 [6]
+ CRUSH rule 0 x 530 [11]
+ CRUSH rule 0 x 531 [9]
+ CRUSH rule 0 x 532 [5]
+ CRUSH rule 0 x 533 [12]
+ CRUSH rule 0 x 534 [11]
+ CRUSH rule 0 x 535 [11]
+ CRUSH rule 0 x 536 [9]
+ CRUSH rule 0 x 537 [15]
+ CRUSH rule 0 x 538 [13]
+ CRUSH rule 0 x 539 [10]
+ CRUSH rule 0 x 540 [12]
+ CRUSH rule 0 x 541 [2]
+ CRUSH rule 0 x 542 [3]
+ CRUSH rule 0 x 543 [4]
+ CRUSH rule 0 x 544 [3]
+ CRUSH rule 0 x 545 [14]
+ CRUSH rule 0 x 546 [5]
+ CRUSH rule 0 x 547 [5]
+ CRUSH rule 0 x 548 [11]
+ CRUSH rule 0 x 549 [14]
+ CRUSH rule 0 x 550 [9]
+ CRUSH rule 0 x 551 [11]
+ CRUSH rule 0 x 552 [2]
+ CRUSH rule 0 x 553 [11]
+ CRUSH rule 0 x 554 [11]
+ CRUSH rule 0 x 555 [6]
+ CRUSH rule 0 x 556 [15]
+ CRUSH rule 0 x 557 [12]
+ CRUSH rule 0 x 558 [12]
+ CRUSH rule 0 x 559 [2]
+ CRUSH rule 0 x 560 [4]
+ CRUSH rule 0 x 561 [12]
+ CRUSH rule 0 x 562 [7]
+ CRUSH rule 0 x 563 [15]
+ CRUSH rule 0 x 564 [2]
+ CRUSH rule 0 x 565 [3]
+ CRUSH rule 0 x 566 [6]
+ CRUSH rule 0 x 567 [15]
+ CRUSH rule 0 x 568 [4]
+ CRUSH rule 0 x 569 [11]
+ CRUSH rule 0 x 570 [1]
+ CRUSH rule 0 x 571 [10]
+ CRUSH rule 0 x 572 [12]
+ CRUSH rule 0 x 573 [7]
+ CRUSH rule 0 x 574 [11]
+ CRUSH rule 0 x 575 [5]
+ CRUSH rule 0 x 576 [3]
+ CRUSH rule 0 x 577 [13]
+ CRUSH rule 0 x 578 [4]
+ CRUSH rule 0 x 579 [13]
+ CRUSH rule 0 x 580 [3]
+ CRUSH rule 0 x 581 [7]
+ CRUSH rule 0 x 582 [10]
+ CRUSH rule 0 x 583 [4]
+ CRUSH rule 0 x 584 [10]
+ CRUSH rule 0 x 585 [5]
+ CRUSH rule 0 x 586 [7]
+ CRUSH rule 0 x 587 [11]
+ CRUSH rule 0 x 588 [3]
+ CRUSH rule 0 x 589 [9]
+ CRUSH rule 0 x 590 [12]
+ CRUSH rule 0 x 591 [2]
+ CRUSH rule 0 x 592 [15]
+ CRUSH rule 0 x 593 [13]
+ CRUSH rule 0 x 594 [12]
+ CRUSH rule 0 x 595 [12]
+ CRUSH rule 0 x 596 [2]
+ CRUSH rule 0 x 597 [15]
+ CRUSH rule 0 x 598 [11]
+ CRUSH rule 0 x 599 [13]
+ CRUSH rule 0 x 600 [4]
+ CRUSH rule 0 x 601 [13]
+ CRUSH rule 0 x 602 [3]
+ CRUSH rule 0 x 603 [3]
+ CRUSH rule 0 x 604 [14]
+ CRUSH rule 0 x 605 [2]
+ CRUSH rule 0 x 606 [12]
+ CRUSH rule 0 x 607 [3]
+ CRUSH rule 0 x 608 [13]
+ CRUSH rule 0 x 609 [14]
+ CRUSH rule 0 x 610 [7]
+ CRUSH rule 0 x 611 [13]
+ CRUSH rule 0 x 612 [7]
+ CRUSH rule 0 x 613 [10]
+ CRUSH rule 0 x 614 [9]
+ CRUSH rule 0 x 615 [9]
+ CRUSH rule 0 x 616 [10]
+ CRUSH rule 0 x 617 [15]
+ CRUSH rule 0 x 618 [4]
+ CRUSH rule 0 x 619 [15]
+ CRUSH rule 0 x 620 [3]
+ CRUSH rule 0 x 621 [3]
+ CRUSH rule 0 x 622 [10]
+ CRUSH rule 0 x 623 [4]
+ CRUSH rule 0 x 624 [3]
+ CRUSH rule 0 x 625 [11]
+ CRUSH rule 0 x 626 [10]
+ CRUSH rule 0 x 627 [1]
+ CRUSH rule 0 x 628 [15]
+ CRUSH rule 0 x 629 [5]
+ CRUSH rule 0 x 630 [1]
+ CRUSH rule 0 x 631 [5]
+ CRUSH rule 0 x 632 [12]
+ CRUSH rule 0 x 633 [14]
+ CRUSH rule 0 x 634 [6]
+ CRUSH rule 0 x 635 [6]
+ CRUSH rule 0 x 636 [13]
+ CRUSH rule 0 x 637 [3]
+ CRUSH rule 0 x 638 [10]
+ CRUSH rule 0 x 639 [6]
+ CRUSH rule 0 x 640 [9]
+ CRUSH rule 0 x 641 [10]
+ CRUSH rule 0 x 642 [1]
+ CRUSH rule 0 x 643 [3]
+ CRUSH rule 0 x 644 [15]
+ CRUSH rule 0 x 645 [14]
+ CRUSH rule 0 x 646 [5]
+ CRUSH rule 0 x 647 [10]
+ CRUSH rule 0 x 648 [6]
+ CRUSH rule 0 x 649 [3]
+ CRUSH rule 0 x 650 [10]
+ CRUSH rule 0 x 651 [3]
+ CRUSH rule 0 x 652 [15]
+ CRUSH rule 0 x 653 [11]
+ CRUSH rule 0 x 654 [13]
+ CRUSH rule 0 x 655 [6]
+ CRUSH rule 0 x 656 [3]
+ CRUSH rule 0 x 657 [11]
+ CRUSH rule 0 x 658 [7]
+ CRUSH rule 0 x 659 [2]
+ CRUSH rule 0 x 660 [13]
+ CRUSH rule 0 x 661 [7]
+ CRUSH rule 0 x 662 [15]
+ CRUSH rule 0 x 663 [14]
+ CRUSH rule 0 x 664 [6]
+ CRUSH rule 0 x 665 [2]
+ CRUSH rule 0 x 666 [12]
+ CRUSH rule 0 x 667 [1]
+ CRUSH rule 0 x 668 [9]
+ CRUSH rule 0 x 669 [9]
+ CRUSH rule 0 x 670 [6]
+ CRUSH rule 0 x 671 [6]
+ CRUSH rule 0 x 672 [2]
+ CRUSH rule 0 x 673 [7]
+ CRUSH rule 0 x 674 [7]
+ CRUSH rule 0 x 675 [9]
+ CRUSH rule 0 x 676 [10]
+ CRUSH rule 0 x 677 [2]
+ CRUSH rule 0 x 678 [1]
+ CRUSH rule 0 x 679 [5]
+ CRUSH rule 0 x 680 [7]
+ CRUSH rule 0 x 681 [6]
+ CRUSH rule 0 x 682 [6]
+ CRUSH rule 0 x 683 [6]
+ CRUSH rule 0 x 684 [9]
+ CRUSH rule 0 x 685 [5]
+ CRUSH rule 0 x 686 [1]
+ CRUSH rule 0 x 687 [7]
+ CRUSH rule 0 x 688 [11]
+ CRUSH rule 0 x 689 [5]
+ CRUSH rule 0 x 690 [9]
+ CRUSH rule 0 x 691 [11]
+ CRUSH rule 0 x 692 [15]
+ CRUSH rule 0 x 693 [5]
+ CRUSH rule 0 x 694 [4]
+ CRUSH rule 0 x 695 [6]
+ CRUSH rule 0 x 696 [1]
+ CRUSH rule 0 x 697 [13]
+ CRUSH rule 0 x 698 [11]
+ CRUSH rule 0 x 699 [7]
+ CRUSH rule 0 x 700 [12]
+ CRUSH rule 0 x 701 [3]
+ CRUSH rule 0 x 702 [3]
+ CRUSH rule 0 x 703 [15]
+ CRUSH rule 0 x 704 [6]
+ CRUSH rule 0 x 705 [14]
+ CRUSH rule 0 x 706 [1]
+ CRUSH rule 0 x 707 [4]
+ CRUSH rule 0 x 708 [3]
+ CRUSH rule 0 x 709 [11]
+ CRUSH rule 0 x 710 [14]
+ CRUSH rule 0 x 711 [14]
+ CRUSH rule 0 x 712 [12]
+ CRUSH rule 0 x 713 [11]
+ CRUSH rule 0 x 714 [12]
+ CRUSH rule 0 x 715 [6]
+ CRUSH rule 0 x 716 [11]
+ CRUSH rule 0 x 717 [12]
+ CRUSH rule 0 x 718 [7]
+ CRUSH rule 0 x 719 [5]
+ CRUSH rule 0 x 720 [4]
+ CRUSH rule 0 x 721 [11]
+ CRUSH rule 0 x 722 [2]
+ CRUSH rule 0 x 723 [2]
+ CRUSH rule 0 x 724 [7]
+ CRUSH rule 0 x 725 [11]
+ CRUSH rule 0 x 726 [7]
+ CRUSH rule 0 x 727 [2]
+ CRUSH rule 0 x 728 [13]
+ CRUSH rule 0 x 729 [15]
+ CRUSH rule 0 x 730 [3]
+ CRUSH rule 0 x 731 [9]
+ CRUSH rule 0 x 732 [1]
+ CRUSH rule 0 x 733 [11]
+ CRUSH rule 0 x 734 [14]
+ CRUSH rule 0 x 735 [6]
+ CRUSH rule 0 x 736 [3]
+ CRUSH rule 0 x 737 [1]
+ CRUSH rule 0 x 738 [11]
+ CRUSH rule 0 x 739 [11]
+ CRUSH rule 0 x 740 [7]
+ CRUSH rule 0 x 741 [12]
+ CRUSH rule 0 x 742 [9]
+ CRUSH rule 0 x 743 [5]
+ CRUSH rule 0 x 744 [6]
+ CRUSH rule 0 x 745 [3]
+ CRUSH rule 0 x 746 [3]
+ CRUSH rule 0 x 747 [15]
+ CRUSH rule 0 x 748 [6]
+ CRUSH rule 0 x 749 [14]
+ CRUSH rule 0 x 750 [1]
+ CRUSH rule 0 x 751 [15]
+ CRUSH rule 0 x 752 [13]
+ CRUSH rule 0 x 753 [4]
+ CRUSH rule 0 x 754 [14]
+ CRUSH rule 0 x 755 [13]
+ CRUSH rule 0 x 756 [3]
+ CRUSH rule 0 x 757 [10]
+ CRUSH rule 0 x 758 [6]
+ CRUSH rule 0 x 759 [5]
+ CRUSH rule 0 x 760 [1]
+ CRUSH rule 0 x 761 [2]
+ CRUSH rule 0 x 762 [1]
+ CRUSH rule 0 x 763 [4]
+ CRUSH rule 0 x 764 [1]
+ CRUSH rule 0 x 765 [9]
+ CRUSH rule 0 x 766 [11]
+ CRUSH rule 0 x 767 [6]
+ CRUSH rule 0 x 768 [2]
+ CRUSH rule 0 x 769 [15]
+ CRUSH rule 0 x 770 [15]
+ CRUSH rule 0 x 771 [9]
+ CRUSH rule 0 x 772 [4]
+ CRUSH rule 0 x 773 [3]
+ CRUSH rule 0 x 774 [12]
+ CRUSH rule 0 x 775 [5]
+ CRUSH rule 0 x 776 [10]
+ CRUSH rule 0 x 777 [11]
+ CRUSH rule 0 x 778 [13]
+ CRUSH rule 0 x 779 [5]
+ CRUSH rule 0 x 780 [13]
+ CRUSH rule 0 x 781 [5]
+ CRUSH rule 0 x 782 [2]
+ CRUSH rule 0 x 783 [12]
+ CRUSH rule 0 x 784 [14]
+ CRUSH rule 0 x 785 [6]
+ CRUSH rule 0 x 786 [10]
+ CRUSH rule 0 x 787 [1]
+ CRUSH rule 0 x 788 [4]
+ CRUSH rule 0 x 789 [9]
+ CRUSH rule 0 x 790 [15]
+ CRUSH rule 0 x 791 [9]
+ CRUSH rule 0 x 792 [6]
+ CRUSH rule 0 x 793 [15]
+ CRUSH rule 0 x 794 [5]
+ CRUSH rule 0 x 795 [6]
+ CRUSH rule 0 x 796 [11]
+ CRUSH rule 0 x 797 [14]
+ CRUSH rule 0 x 798 [5]
+ CRUSH rule 0 x 799 [2]
+ CRUSH rule 0 x 800 [6]
+ CRUSH rule 0 x 801 [2]
+ CRUSH rule 0 x 802 [1]
+ CRUSH rule 0 x 803 [7]
+ CRUSH rule 0 x 804 [5]
+ CRUSH rule 0 x 805 [13]
+ CRUSH rule 0 x 806 [6]
+ CRUSH rule 0 x 807 [14]
+ CRUSH rule 0 x 808 [2]
+ CRUSH rule 0 x 809 [1]
+ CRUSH rule 0 x 810 [2]
+ CRUSH rule 0 x 811 [15]
+ CRUSH rule 0 x 812 [7]
+ CRUSH rule 0 x 813 [4]
+ CRUSH rule 0 x 814 [13]
+ CRUSH rule 0 x 815 [15]
+ CRUSH rule 0 x 816 [14]
+ CRUSH rule 0 x 817 [10]
+ CRUSH rule 0 x 818 [15]
+ CRUSH rule 0 x 819 [5]
+ CRUSH rule 0 x 820 [3]
+ CRUSH rule 0 x 821 [15]
+ CRUSH rule 0 x 822 [10]
+ CRUSH rule 0 x 823 [2]
+ CRUSH rule 0 x 824 [3]
+ CRUSH rule 0 x 825 [10]
+ CRUSH rule 0 x 826 [5]
+ CRUSH rule 0 x 827 [13]
+ CRUSH rule 0 x 828 [12]
+ CRUSH rule 0 x 829 [13]
+ CRUSH rule 0 x 830 [15]
+ CRUSH rule 0 x 831 [1]
+ CRUSH rule 0 x 832 [14]
+ CRUSH rule 0 x 833 [9]
+ CRUSH rule 0 x 834 [9]
+ CRUSH rule 0 x 835 [14]
+ CRUSH rule 0 x 836 [3]
+ CRUSH rule 0 x 837 [15]
+ CRUSH rule 0 x 838 [12]
+ CRUSH rule 0 x 839 [3]
+ CRUSH rule 0 x 840 [10]
+ CRUSH rule 0 x 841 [3]
+ CRUSH rule 0 x 842 [9]
+ CRUSH rule 0 x 843 [14]
+ CRUSH rule 0 x 844 [7]
+ CRUSH rule 0 x 845 [13]
+ CRUSH rule 0 x 846 [3]
+ CRUSH rule 0 x 847 [12]
+ CRUSH rule 0 x 848 [11]
+ CRUSH rule 0 x 849 [3]
+ CRUSH rule 0 x 850 [1]
+ CRUSH rule 0 x 851 [14]
+ CRUSH rule 0 x 852 [9]
+ CRUSH rule 0 x 853 [13]
+ CRUSH rule 0 x 854 [7]
+ CRUSH rule 0 x 855 [14]
+ CRUSH rule 0 x 856 [5]
+ CRUSH rule 0 x 857 [4]
+ CRUSH rule 0 x 858 [5]
+ CRUSH rule 0 x 859 [5]
+ CRUSH rule 0 x 860 [11]
+ CRUSH rule 0 x 861 [13]
+ CRUSH rule 0 x 862 [5]
+ CRUSH rule 0 x 863 [11]
+ CRUSH rule 0 x 864 [6]
+ CRUSH rule 0 x 865 [4]
+ CRUSH rule 0 x 866 [2]
+ CRUSH rule 0 x 867 [12]
+ CRUSH rule 0 x 868 [14]
+ CRUSH rule 0 x 869 [10]
+ CRUSH rule 0 x 870 [14]
+ CRUSH rule 0 x 871 [6]
+ CRUSH rule 0 x 872 [6]
+ CRUSH rule 0 x 873 [2]
+ CRUSH rule 0 x 874 [12]
+ CRUSH rule 0 x 875 [10]
+ CRUSH rule 0 x 876 [14]
+ CRUSH rule 0 x 877 [15]
+ CRUSH rule 0 x 878 [7]
+ CRUSH rule 0 x 879 [12]
+ CRUSH rule 0 x 880 [2]
+ CRUSH rule 0 x 881 [6]
+ CRUSH rule 0 x 882 [11]
+ CRUSH rule 0 x 883 [13]
+ CRUSH rule 0 x 884 [6]
+ CRUSH rule 0 x 885 [14]
+ CRUSH rule 0 x 886 [13]
+ CRUSH rule 0 x 887 [14]
+ CRUSH rule 0 x 888 [10]
+ CRUSH rule 0 x 889 [15]
+ CRUSH rule 0 x 890 [10]
+ CRUSH rule 0 x 891 [9]
+ CRUSH rule 0 x 892 [12]
+ CRUSH rule 0 x 893 [1]
+ CRUSH rule 0 x 894 [7]
+ CRUSH rule 0 x 895 [2]
+ CRUSH rule 0 x 896 [9]
+ CRUSH rule 0 x 897 [7]
+ CRUSH rule 0 x 898 [10]
+ CRUSH rule 0 x 899 [1]
+ CRUSH rule 0 x 900 [2]
+ CRUSH rule 0 x 901 [9]
+ CRUSH rule 0 x 902 [4]
+ CRUSH rule 0 x 903 [14]
+ CRUSH rule 0 x 904 [15]
+ CRUSH rule 0 x 905 [12]
+ CRUSH rule 0 x 906 [14]
+ CRUSH rule 0 x 907 [7]
+ CRUSH rule 0 x 908 [2]
+ CRUSH rule 0 x 909 [10]
+ CRUSH rule 0 x 910 [12]
+ CRUSH rule 0 x 911 [11]
+ CRUSH rule 0 x 912 [6]
+ CRUSH rule 0 x 913 [4]
+ CRUSH rule 0 x 914 [4]
+ CRUSH rule 0 x 915 [12]
+ CRUSH rule 0 x 916 [3]
+ CRUSH rule 0 x 917 [1]
+ CRUSH rule 0 x 918 [7]
+ CRUSH rule 0 x 919 [10]
+ CRUSH rule 0 x 920 [4]
+ CRUSH rule 0 x 921 [1]
+ CRUSH rule 0 x 922 [6]
+ CRUSH rule 0 x 923 [12]
+ CRUSH rule 0 x 924 [6]
+ CRUSH rule 0 x 925 [12]
+ CRUSH rule 0 x 926 [3]
+ CRUSH rule 0 x 927 [6]
+ CRUSH rule 0 x 928 [13]
+ CRUSH rule 0 x 929 [10]
+ CRUSH rule 0 x 930 [7]
+ CRUSH rule 0 x 931 [6]
+ CRUSH rule 0 x 932 [13]
+ CRUSH rule 0 x 933 [12]
+ CRUSH rule 0 x 934 [12]
+ CRUSH rule 0 x 935 [6]
+ CRUSH rule 0 x 936 [9]
+ CRUSH rule 0 x 937 [14]
+ CRUSH rule 0 x 938 [14]
+ CRUSH rule 0 x 939 [6]
+ CRUSH rule 0 x 940 [13]
+ CRUSH rule 0 x 941 [3]
+ CRUSH rule 0 x 942 [15]
+ CRUSH rule 0 x 943 [10]
+ CRUSH rule 0 x 944 [2]
+ CRUSH rule 0 x 945 [10]
+ CRUSH rule 0 x 946 [11]
+ CRUSH rule 0 x 947 [11]
+ CRUSH rule 0 x 948 [7]
+ CRUSH rule 0 x 949 [9]
+ CRUSH rule 0 x 950 [9]
+ CRUSH rule 0 x 951 [2]
+ CRUSH rule 0 x 952 [9]
+ CRUSH rule 0 x 953 [1]
+ CRUSH rule 0 x 954 [10]
+ CRUSH rule 0 x 955 [7]
+ CRUSH rule 0 x 956 [1]
+ CRUSH rule 0 x 957 [14]
+ CRUSH rule 0 x 958 [15]
+ CRUSH rule 0 x 959 [2]
+ CRUSH rule 0 x 960 [2]
+ CRUSH rule 0 x 961 [3]
+ CRUSH rule 0 x 962 [5]
+ CRUSH rule 0 x 963 [13]
+ CRUSH rule 0 x 964 [7]
+ CRUSH rule 0 x 965 [12]
+ CRUSH rule 0 x 966 [12]
+ CRUSH rule 0 x 967 [7]
+ CRUSH rule 0 x 968 [12]
+ CRUSH rule 0 x 969 [11]
+ CRUSH rule 0 x 970 [5]
+ CRUSH rule 0 x 971 [1]
+ CRUSH rule 0 x 972 [12]
+ CRUSH rule 0 x 973 [1]
+ CRUSH rule 0 x 974 [7]
+ CRUSH rule 0 x 975 [7]
+ CRUSH rule 0 x 976 [7]
+ CRUSH rule 0 x 977 [14]
+ CRUSH rule 0 x 978 [12]
+ CRUSH rule 0 x 979 [5]
+ CRUSH rule 0 x 980 [15]
+ CRUSH rule 0 x 981 [5]
+ CRUSH rule 0 x 982 [2]
+ CRUSH rule 0 x 983 [3]
+ CRUSH rule 0 x 984 [15]
+ CRUSH rule 0 x 985 [11]
+ CRUSH rule 0 x 986 [6]
+ CRUSH rule 0 x 987 [13]
+ CRUSH rule 0 x 988 [12]
+ CRUSH rule 0 x 989 [7]
+ CRUSH rule 0 x 990 [1]
+ CRUSH rule 0 x 991 [7]
+ CRUSH rule 0 x 992 [9]
+ CRUSH rule 0 x 993 [6]
+ CRUSH rule 0 x 994 [3]
+ CRUSH rule 0 x 995 [15]
+ CRUSH rule 0 x 996 [15]
+ CRUSH rule 0 x 997 [15]
+ CRUSH rule 0 x 998 [6]
+ CRUSH rule 0 x 999 [9]
+ CRUSH rule 0 x 1000 [14]
+ CRUSH rule 0 x 1001 [11]
+ CRUSH rule 0 x 1002 [1]
+ CRUSH rule 0 x 1003 [10]
+ CRUSH rule 0 x 1004 [15]
+ CRUSH rule 0 x 1005 [6]
+ CRUSH rule 0 x 1006 [10]
+ CRUSH rule 0 x 1007 [1]
+ CRUSH rule 0 x 1008 [7]
+ CRUSH rule 0 x 1009 [5]
+ CRUSH rule 0 x 1010 [10]
+ CRUSH rule 0 x 1011 [6]
+ CRUSH rule 0 x 1012 [12]
+ CRUSH rule 0 x 1013 [2]
+ CRUSH rule 0 x 1014 [1]
+ CRUSH rule 0 x 1015 [12]
+ CRUSH rule 0 x 1016 [10]
+ CRUSH rule 0 x 1017 [5]
+ CRUSH rule 0 x 1018 [13]
+ CRUSH rule 0 x 1019 [10]
+ CRUSH rule 0 x 1020 [3]
+ CRUSH rule 0 x 1021 [2]
+ CRUSH rule 0 x 1022 [15]
+ CRUSH rule 0 x 1023 [15]
+ rule 0 (replicated_ruleset) num_rep 1 result size == 1:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [7,10]
+ CRUSH rule 0 x 1 [10,15]
+ CRUSH rule 0 x 2 [1,12]
+ CRUSH rule 0 x 3 [15,4]
+ CRUSH rule 0 x 4 [14,2]
+ CRUSH rule 0 x 5 [7,4]
+ CRUSH rule 0 x 6 [12,6]
+ CRUSH rule 0 x 7 [9,2]
+ CRUSH rule 0 x 8 [10,2]
+ CRUSH rule 0 x 9 [7,1]
+ CRUSH rule 0 x 10 [10,14]
+ CRUSH rule 0 x 11 [13,9]
+ CRUSH rule 0 x 12 [7,1]
+ CRUSH rule 0 x 13 [3,5]
+ CRUSH rule 0 x 14 [13,5]
+ CRUSH rule 0 x 15 [15,1]
+ CRUSH rule 0 x 16 [7,11]
+ CRUSH rule 0 x 17 [10,1]
+ CRUSH rule 0 x 18 [1,7]
+ CRUSH rule 0 x 19 [7,12]
+ CRUSH rule 0 x 20 [14,12]
+ CRUSH rule 0 x 21 [3,12]
+ CRUSH rule 0 x 22 [6,3]
+ CRUSH rule 0 x 23 [10,5]
+ CRUSH rule 0 x 24 [12,11]
+ CRUSH rule 0 x 25 [7,12]
+ CRUSH rule 0 x 26 [1,7]
+ CRUSH rule 0 x 27 [3,6]
+ CRUSH rule 0 x 28 [14,4]
+ CRUSH rule 0 x 29 [5,14]
+ CRUSH rule 0 x 30 [2,5]
+ CRUSH rule 0 x 31 [5,15]
+ CRUSH rule 0 x 32 [9,10]
+ CRUSH rule 0 x 33 [13,4]
+ CRUSH rule 0 x 34 [13,15]
+ CRUSH rule 0 x 35 [4,14]
+ CRUSH rule 0 x 36 [3,12]
+ CRUSH rule 0 x 37 [9,2]
+ CRUSH rule 0 x 38 [3,4]
+ CRUSH rule 0 x 39 [12,7]
+ CRUSH rule 0 x 40 [10,1]
+ CRUSH rule 0 x 41 [4,9]
+ CRUSH rule 0 x 42 [3,6]
+ CRUSH rule 0 x 43 [10,5]
+ CRUSH rule 0 x 44 [11,4]
+ CRUSH rule 0 x 45 [11,12]
+ CRUSH rule 0 x 46 [6,9]
+ CRUSH rule 0 x 47 [3,9]
+ CRUSH rule 0 x 48 [4,6]
+ CRUSH rule 0 x 49 [9,15]
+ CRUSH rule 0 x 50 [14,12]
+ CRUSH rule 0 x 51 [10,6]
+ CRUSH rule 0 x 52 [12,1]
+ CRUSH rule 0 x 53 [3,6]
+ CRUSH rule 0 x 54 [4,13]
+ CRUSH rule 0 x 55 [4,11]
+ CRUSH rule 0 x 56 [5,9]
+ CRUSH rule 0 x 57 [6,2]
+ CRUSH rule 0 x 58 [7,1]
+ CRUSH rule 0 x 59 [2,13]
+ CRUSH rule 0 x 60 [3,6]
+ CRUSH rule 0 x 61 [3,15]
+ CRUSH rule 0 x 62 [15,11]
+ CRUSH rule 0 x 63 [10,14]
+ CRUSH rule 0 x 64 [3,9]
+ CRUSH rule 0 x 65 [4,12]
+ CRUSH rule 0 x 66 [15,11]
+ CRUSH rule 0 x 67 [2,6]
+ CRUSH rule 0 x 68 [15,7]
+ CRUSH rule 0 x 69 [2,1]
+ CRUSH rule 0 x 70 [9,6]
+ CRUSH rule 0 x 71 [15,5]
+ CRUSH rule 0 x 72 [9,10]
+ CRUSH rule 0 x 73 [5,3]
+ CRUSH rule 0 x 74 [11,7]
+ CRUSH rule 0 x 75 [9,7]
+ CRUSH rule 0 x 76 [6,1]
+ CRUSH rule 0 x 77 [7,4]
+ CRUSH rule 0 x 78 [9,3]
+ CRUSH rule 0 x 79 [13,2]
+ CRUSH rule 0 x 80 [15,2]
+ CRUSH rule 0 x 81 [15,2]
+ CRUSH rule 0 x 82 [14,13]
+ CRUSH rule 0 x 83 [4,15]
+ CRUSH rule 0 x 84 [10,7]
+ CRUSH rule 0 x 85 [3,15]
+ CRUSH rule 0 x 86 [10,9]
+ CRUSH rule 0 x 87 [15,10]
+ CRUSH rule 0 x 88 [4,13]
+ CRUSH rule 0 x 89 [3,9]
+ CRUSH rule 0 x 90 [4,9]
+ CRUSH rule 0 x 91 [6,11]
+ CRUSH rule 0 x 92 [1,5]
+ CRUSH rule 0 x 93 [9,3]
+ CRUSH rule 0 x 94 [9,2]
+ CRUSH rule 0 x 95 [7,15]
+ CRUSH rule 0 x 96 [2,15]
+ CRUSH rule 0 x 97 [4,11]
+ CRUSH rule 0 x 98 [11,13]
+ CRUSH rule 0 x 99 [12,4]
+ CRUSH rule 0 x 100 [9,4]
+ CRUSH rule 0 x 101 [15,7]
+ CRUSH rule 0 x 102 [3,11]
+ CRUSH rule 0 x 103 [13,11]
+ CRUSH rule 0 x 104 [14,6]
+ CRUSH rule 0 x 105 [14,10]
+ CRUSH rule 0 x 106 [6,5]
+ CRUSH rule 0 x 107 [3,1]
+ CRUSH rule 0 x 108 [5,10]
+ CRUSH rule 0 x 109 [9,1]
+ CRUSH rule 0 x 110 [5,1]
+ CRUSH rule 0 x 111 [10,1]
+ CRUSH rule 0 x 112 [1,10]
+ CRUSH rule 0 x 113 [6,10]
+ CRUSH rule 0 x 114 [5,13]
+ CRUSH rule 0 x 115 [10,13]
+ CRUSH rule 0 x 116 [1,14]
+ CRUSH rule 0 x 117 [5,6]
+ CRUSH rule 0 x 118 [10,4]
+ CRUSH rule 0 x 119 [14,12]
+ CRUSH rule 0 x 120 [11,3]
+ CRUSH rule 0 x 121 [9,5]
+ CRUSH rule 0 x 122 [4,3]
+ CRUSH rule 0 x 123 [3,10]
+ CRUSH rule 0 x 124 [12,2]
+ CRUSH rule 0 x 125 [9,12]
+ CRUSH rule 0 x 126 [7,15]
+ CRUSH rule 0 x 127 [4,14]
+ CRUSH rule 0 x 128 [3,12]
+ CRUSH rule 0 x 129 [11,13]
+ CRUSH rule 0 x 130 [3,13]
+ CRUSH rule 0 x 131 [12,1]
+ CRUSH rule 0 x 132 [11,15]
+ CRUSH rule 0 x 133 [3,6]
+ CRUSH rule 0 x 134 [12,5]
+ CRUSH rule 0 x 135 [3,14]
+ CRUSH rule 0 x 136 [15,6]
+ CRUSH rule 0 x 137 [14,3]
+ CRUSH rule 0 x 138 [13,15]
+ CRUSH rule 0 x 139 [11,2]
+ CRUSH rule 0 x 140 [11,4]
+ CRUSH rule 0 x 141 [6,12]
+ CRUSH rule 0 x 142 [3,14]
+ CRUSH rule 0 x 143 [9,6]
+ CRUSH rule 0 x 144 [13,7]
+ CRUSH rule 0 x 145 [12,2]
+ CRUSH rule 0 x 146 [1,5]
+ CRUSH rule 0 x 147 [1,4]
+ CRUSH rule 0 x 148 [12,7]
+ CRUSH rule 0 x 149 [2,5]
+ CRUSH rule 0 x 150 [1,15]
+ CRUSH rule 0 x 151 [2,9]
+ CRUSH rule 0 x 152 [5,9]
+ CRUSH rule 0 x 153 [6,9]
+ CRUSH rule 0 x 154 [3,11]
+ CRUSH rule 0 x 155 [14,12]
+ CRUSH rule 0 x 156 [7,13]
+ CRUSH rule 0 x 157 [15,1]
+ CRUSH rule 0 x 158 [15,1]
+ CRUSH rule 0 x 159 [4,14]
+ CRUSH rule 0 x 160 [5,7]
+ CRUSH rule 0 x 161 [1,2]
+ CRUSH rule 0 x 162 [10,6]
+ CRUSH rule 0 x 163 [15,1]
+ CRUSH rule 0 x 164 [9,14]
+ CRUSH rule 0 x 165 [11,7]
+ CRUSH rule 0 x 166 [1,2]
+ CRUSH rule 0 x 167 [9,7]
+ CRUSH rule 0 x 168 [13,2]
+ CRUSH rule 0 x 169 [1,4]
+ CRUSH rule 0 x 170 [1,15]
+ CRUSH rule 0 x 171 [9,2]
+ CRUSH rule 0 x 172 [14,4]
+ CRUSH rule 0 x 173 [5,10]
+ CRUSH rule 0 x 174 [15,6]
+ CRUSH rule 0 x 175 [5,7]
+ CRUSH rule 0 x 176 [9,6]
+ CRUSH rule 0 x 177 [2,9]
+ CRUSH rule 0 x 178 [12,11]
+ CRUSH rule 0 x 179 [2,10]
+ CRUSH rule 0 x 180 [3,11]
+ CRUSH rule 0 x 181 [9,12]
+ CRUSH rule 0 x 182 [5,13]
+ CRUSH rule 0 x 183 [5,7]
+ CRUSH rule 0 x 184 [2,5]
+ CRUSH rule 0 x 185 [13,5]
+ CRUSH rule 0 x 186 [6,14]
+ CRUSH rule 0 x 187 [1,4]
+ CRUSH rule 0 x 188 [9,13]
+ CRUSH rule 0 x 189 [6,12]
+ CRUSH rule 0 x 190 [9,13]
+ CRUSH rule 0 x 191 [7,11]
+ CRUSH rule 0 x 192 [2,11]
+ CRUSH rule 0 x 193 [3,13]
+ CRUSH rule 0 x 194 [3,13]
+ CRUSH rule 0 x 195 [5,7]
+ CRUSH rule 0 x 196 [4,15]
+ CRUSH rule 0 x 197 [14,10]
+ CRUSH rule 0 x 198 [2,5]
+ CRUSH rule 0 x 199 [2,10]
+ CRUSH rule 0 x 200 [7,14]
+ CRUSH rule 0 x 201 [9,14]
+ CRUSH rule 0 x 202 [14,11]
+ CRUSH rule 0 x 203 [12,5]
+ CRUSH rule 0 x 204 [6,11]
+ CRUSH rule 0 x 205 [15,4]
+ CRUSH rule 0 x 206 [13,11]
+ CRUSH rule 0 x 207 [2,11]
+ CRUSH rule 0 x 208 [13,1]
+ CRUSH rule 0 x 209 [6,15]
+ CRUSH rule 0 x 210 [13,11]
+ CRUSH rule 0 x 211 [2,14]
+ CRUSH rule 0 x 212 [10,1]
+ CRUSH rule 0 x 213 [3,9]
+ CRUSH rule 0 x 214 [7,15]
+ CRUSH rule 0 x 215 [6,1]
+ CRUSH rule 0 x 216 [12,9]
+ CRUSH rule 0 x 217 [12,11]
+ CRUSH rule 0 x 218 [12,10]
+ CRUSH rule 0 x 219 [3,11]
+ CRUSH rule 0 x 220 [14,4]
+ CRUSH rule 0 x 221 [15,5]
+ CRUSH rule 0 x 222 [10,4]
+ CRUSH rule 0 x 223 [9,7]
+ CRUSH rule 0 x 224 [1,7]
+ CRUSH rule 0 x 225 [10,5]
+ CRUSH rule 0 x 226 [4,1]
+ CRUSH rule 0 x 227 [7,2]
+ CRUSH rule 0 x 228 [2,15]
+ CRUSH rule 0 x 229 [9,3]
+ CRUSH rule 0 x 230 [10,5]
+ CRUSH rule 0 x 231 [2,7]
+ CRUSH rule 0 x 232 [10,5]
+ CRUSH rule 0 x 233 [6,12]
+ CRUSH rule 0 x 234 [10,1]
+ CRUSH rule 0 x 235 [13,14]
+ CRUSH rule 0 x 236 [2,15]
+ CRUSH rule 0 x 237 [3,12]
+ CRUSH rule 0 x 238 [2,10]
+ CRUSH rule 0 x 239 [4,15]
+ CRUSH rule 0 x 240 [15,5]
+ CRUSH rule 0 x 241 [7,9]
+ CRUSH rule 0 x 242 [14,2]
+ CRUSH rule 0 x 243 [2,11]
+ CRUSH rule 0 x 244 [13,9]
+ CRUSH rule 0 x 245 [12,9]
+ CRUSH rule 0 x 246 [15,3]
+ CRUSH rule 0 x 247 [6,4]
+ CRUSH rule 0 x 248 [5,13]
+ CRUSH rule 0 x 249 [10,14]
+ CRUSH rule 0 x 250 [12,15]
+ CRUSH rule 0 x 251 [13,2]
+ CRUSH rule 0 x 252 [7,5]
+ CRUSH rule 0 x 253 [3,13]
+ CRUSH rule 0 x 254 [2,9]
+ CRUSH rule 0 x 255 [1,9]
+ CRUSH rule 0 x 256 [6,9]
+ CRUSH rule 0 x 257 [15,12]
+ CRUSH rule 0 x 258 [12,5]
+ CRUSH rule 0 x 259 [9,10]
+ CRUSH rule 0 x 260 [10,12]
+ CRUSH rule 0 x 261 [13,7]
+ CRUSH rule 0 x 262 [15,3]
+ CRUSH rule 0 x 263 [12,6]
+ CRUSH rule 0 x 264 [13,14]
+ CRUSH rule 0 x 265 [12,10]
+ CRUSH rule 0 x 266 [14,7]
+ CRUSH rule 0 x 267 [12,11]
+ CRUSH rule 0 x 268 [4,1]
+ CRUSH rule 0 x 269 [11,1]
+ CRUSH rule 0 x 270 [7,11]
+ CRUSH rule 0 x 271 [4,7]
+ CRUSH rule 0 x 272 [15,5]
+ CRUSH rule 0 x 273 [2,10]
+ CRUSH rule 0 x 274 [10,2]
+ CRUSH rule 0 x 275 [10,3]
+ CRUSH rule 0 x 276 [5,12]
+ CRUSH rule 0 x 277 [14,3]
+ CRUSH rule 0 x 278 [5,6]
+ CRUSH rule 0 x 279 [6,10]
+ CRUSH rule 0 x 280 [7,3]
+ CRUSH rule 0 x 281 [5,11]
+ CRUSH rule 0 x 282 [2,1]
+ CRUSH rule 0 x 283 [4,1]
+ CRUSH rule 0 x 284 [5,11]
+ CRUSH rule 0 x 285 [15,5]
+ CRUSH rule 0 x 286 [10,4]
+ CRUSH rule 0 x 287 [12,4]
+ CRUSH rule 0 x 288 [4,12]
+ CRUSH rule 0 x 289 [2,5]
+ CRUSH rule 0 x 290 [12,2]
+ CRUSH rule 0 x 291 [7,11]
+ CRUSH rule 0 x 292 [4,10]
+ CRUSH rule 0 x 293 [6,5]
+ CRUSH rule 0 x 294 [9,12]
+ CRUSH rule 0 x 295 [6,10]
+ CRUSH rule 0 x 296 [3,1]
+ CRUSH rule 0 x 297 [6,13]
+ CRUSH rule 0 x 298 [14,9]
+ CRUSH rule 0 x 299 [14,12]
+ CRUSH rule 0 x 300 [15,7]
+ CRUSH rule 0 x 301 [9,11]
+ CRUSH rule 0 x 302 [9,7]
+ CRUSH rule 0 x 303 [4,13]
+ CRUSH rule 0 x 304 [6,9]
+ CRUSH rule 0 x 305 [13,7]
+ CRUSH rule 0 x 306 [10,12]
+ CRUSH rule 0 x 307 [11,12]
+ CRUSH rule 0 x 308 [12,14]
+ CRUSH rule 0 x 309 [9,3]
+ CRUSH rule 0 x 310 [3,1]
+ CRUSH rule 0 x 311 [3,9]
+ CRUSH rule 0 x 312 [15,13]
+ CRUSH rule 0 x 313 [9,15]
+ CRUSH rule 0 x 314 [2,15]
+ CRUSH rule 0 x 315 [15,2]
+ CRUSH rule 0 x 316 [4,9]
+ CRUSH rule 0 x 317 [1,5]
+ CRUSH rule 0 x 318 [4,1]
+ CRUSH rule 0 x 319 [2,15]
+ CRUSH rule 0 x 320 [5,7]
+ CRUSH rule 0 x 321 [1,6]
+ CRUSH rule 0 x 322 [13,7]
+ CRUSH rule 0 x 323 [7,4]
+ CRUSH rule 0 x 324 [5,6]
+ CRUSH rule 0 x 325 [9,10]
+ CRUSH rule 0 x 326 [11,7]
+ CRUSH rule 0 x 327 [12,5]
+ CRUSH rule 0 x 328 [5,2]
+ CRUSH rule 0 x 329 [2,6]
+ CRUSH rule 0 x 330 [3,9]
+ CRUSH rule 0 x 331 [12,14]
+ CRUSH rule 0 x 332 [10,12]
+ CRUSH rule 0 x 333 [6,5]
+ CRUSH rule 0 x 334 [4,9]
+ CRUSH rule 0 x 335 [11,7]
+ CRUSH rule 0 x 336 [6,14]
+ CRUSH rule 0 x 337 [15,11]
+ CRUSH rule 0 x 338 [10,5]
+ CRUSH rule 0 x 339 [11,14]
+ CRUSH rule 0 x 340 [11,6]
+ CRUSH rule 0 x 341 [7,5]
+ CRUSH rule 0 x 342 [12,14]
+ CRUSH rule 0 x 343 [12,14]
+ CRUSH rule 0 x 344 [9,11]
+ CRUSH rule 0 x 345 [14,2]
+ CRUSH rule 0 x 346 [5,3]
+ CRUSH rule 0 x 347 [10,2]
+ CRUSH rule 0 x 348 [7,9]
+ CRUSH rule 0 x 349 [9,6]
+ CRUSH rule 0 x 350 [13,9]
+ CRUSH rule 0 x 351 [13,5]
+ CRUSH rule 0 x 352 [1,12]
+ CRUSH rule 0 x 353 [10,14]
+ CRUSH rule 0 x 354 [6,3]
+ CRUSH rule 0 x 355 [13,14]
+ CRUSH rule 0 x 356 [15,13]
+ CRUSH rule 0 x 357 [4,11]
+ CRUSH rule 0 x 358 [12,7]
+ CRUSH rule 0 x 359 [5,15]
+ CRUSH rule 0 x 360 [13,10]
+ CRUSH rule 0 x 361 [5,3]
+ CRUSH rule 0 x 362 [2,9]
+ CRUSH rule 0 x 363 [7,12]
+ CRUSH rule 0 x 364 [2,12]
+ CRUSH rule 0 x 365 [13,5]
+ CRUSH rule 0 x 366 [12,7]
+ CRUSH rule 0 x 367 [7,13]
+ CRUSH rule 0 x 368 [7,9]
+ CRUSH rule 0 x 369 [7,5]
+ CRUSH rule 0 x 370 [4,7]
+ CRUSH rule 0 x 371 [1,7]
+ CRUSH rule 0 x 372 [10,4]
+ CRUSH rule 0 x 373 [15,5]
+ CRUSH rule 0 x 374 [3,15]
+ CRUSH rule 0 x 375 [5,2]
+ CRUSH rule 0 x 376 [5,14]
+ CRUSH rule 0 x 377 [1,15]
+ CRUSH rule 0 x 378 [9,12]
+ CRUSH rule 0 x 379 [11,2]
+ CRUSH rule 0 x 380 [6,1]
+ CRUSH rule 0 x 381 [15,13]
+ CRUSH rule 0 x 382 [14,3]
+ CRUSH rule 0 x 383 [3,6]
+ CRUSH rule 0 x 384 [4,13]
+ CRUSH rule 0 x 385 [4,6]
+ CRUSH rule 0 x 386 [14,3]
+ CRUSH rule 0 x 387 [1,11]
+ CRUSH rule 0 x 388 [2,6]
+ CRUSH rule 0 x 389 [12,7]
+ CRUSH rule 0 x 390 [2,11]
+ CRUSH rule 0 x 391 [3,4]
+ CRUSH rule 0 x 392 [11,5]
+ CRUSH rule 0 x 393 [2,14]
+ CRUSH rule 0 x 394 [4,9]
+ CRUSH rule 0 x 395 [10,13]
+ CRUSH rule 0 x 396 [2,12]
+ CRUSH rule 0 x 397 [1,14]
+ CRUSH rule 0 x 398 [9,2]
+ CRUSH rule 0 x 399 [5,9]
+ CRUSH rule 0 x 400 [10,6]
+ CRUSH rule 0 x 401 [6,9]
+ CRUSH rule 0 x 402 [4,7]
+ CRUSH rule 0 x 403 [7,15]
+ CRUSH rule 0 x 404 [14,12]
+ CRUSH rule 0 x 405 [9,15]
+ CRUSH rule 0 x 406 [12,14]
+ CRUSH rule 0 x 407 [9,5]
+ CRUSH rule 0 x 408 [7,1]
+ CRUSH rule 0 x 409 [11,2]
+ CRUSH rule 0 x 410 [6,4]
+ CRUSH rule 0 x 411 [13,11]
+ CRUSH rule 0 x 412 [5,9]
+ CRUSH rule 0 x 413 [13,5]
+ CRUSH rule 0 x 414 [3,11]
+ CRUSH rule 0 x 415 [6,10]
+ CRUSH rule 0 x 416 [13,1]
+ CRUSH rule 0 x 417 [4,12]
+ CRUSH rule 0 x 418 [14,5]
+ CRUSH rule 0 x 419 [5,14]
+ CRUSH rule 0 x 420 [2,4]
+ CRUSH rule 0 x 421 [15,4]
+ CRUSH rule 0 x 422 [4,11]
+ CRUSH rule 0 x 423 [3,15]
+ CRUSH rule 0 x 424 [6,10]
+ CRUSH rule 0 x 425 [11,15]
+ CRUSH rule 0 x 426 [12,4]
+ CRUSH rule 0 x 427 [14,10]
+ CRUSH rule 0 x 428 [12,7]
+ CRUSH rule 0 x 429 [3,4]
+ CRUSH rule 0 x 430 [3,5]
+ CRUSH rule 0 x 431 [9,3]
+ CRUSH rule 0 x 432 [4,1]
+ CRUSH rule 0 x 433 [4,11]
+ CRUSH rule 0 x 434 [2,14]
+ CRUSH rule 0 x 435 [13,11]
+ CRUSH rule 0 x 436 [9,15]
+ CRUSH rule 0 x 437 [9,6]
+ CRUSH rule 0 x 438 [7,2]
+ CRUSH rule 0 x 439 [7,14]
+ CRUSH rule 0 x 440 [14,11]
+ CRUSH rule 0 x 441 [2,4]
+ CRUSH rule 0 x 442 [10,13]
+ CRUSH rule 0 x 443 [12,15]
+ CRUSH rule 0 x 444 [4,13]
+ CRUSH rule 0 x 445 [4,2]
+ CRUSH rule 0 x 446 [12,10]
+ CRUSH rule 0 x 447 [15,7]
+ CRUSH rule 0 x 448 [5,2]
+ CRUSH rule 0 x 449 [14,5]
+ CRUSH rule 0 x 450 [2,4]
+ CRUSH rule 0 x 451 [6,14]
+ CRUSH rule 0 x 452 [14,9]
+ CRUSH rule 0 x 453 [5,15]
+ CRUSH rule 0 x 454 [10,4]
+ CRUSH rule 0 x 455 [6,13]
+ CRUSH rule 0 x 456 [5,7]
+ CRUSH rule 0 x 457 [9,1]
+ CRUSH rule 0 x 458 [9,11]
+ CRUSH rule 0 x 459 [13,15]
+ CRUSH rule 0 x 460 [5,12]
+ CRUSH rule 0 x 461 [4,3]
+ CRUSH rule 0 x 462 [4,7]
+ CRUSH rule 0 x 463 [4,12]
+ CRUSH rule 0 x 464 [4,2]
+ CRUSH rule 0 x 465 [5,10]
+ CRUSH rule 0 x 466 [13,5]
+ CRUSH rule 0 x 467 [13,6]
+ CRUSH rule 0 x 468 [10,7]
+ CRUSH rule 0 x 469 [4,9]
+ CRUSH rule 0 x 470 [3,9]
+ CRUSH rule 0 x 471 [6,1]
+ CRUSH rule 0 x 472 [2,14]
+ CRUSH rule 0 x 473 [15,10]
+ CRUSH rule 0 x 474 [15,10]
+ CRUSH rule 0 x 475 [10,5]
+ CRUSH rule 0 x 476 [3,6]
+ CRUSH rule 0 x 477 [6,13]
+ CRUSH rule 0 x 478 [4,15]
+ CRUSH rule 0 x 479 [13,11]
+ CRUSH rule 0 x 480 [1,13]
+ CRUSH rule 0 x 481 [15,12]
+ CRUSH rule 0 x 482 [2,12]
+ CRUSH rule 0 x 483 [10,1]
+ CRUSH rule 0 x 484 [1,4]
+ CRUSH rule 0 x 485 [9,4]
+ CRUSH rule 0 x 486 [3,10]
+ CRUSH rule 0 x 487 [12,11]
+ CRUSH rule 0 x 488 [14,4]
+ CRUSH rule 0 x 489 [11,4]
+ CRUSH rule 0 x 490 [4,9]
+ CRUSH rule 0 x 491 [1,12]
+ CRUSH rule 0 x 492 [5,7]
+ CRUSH rule 0 x 493 [12,1]
+ CRUSH rule 0 x 494 [1,7]
+ CRUSH rule 0 x 495 [3,15]
+ CRUSH rule 0 x 496 [5,3]
+ CRUSH rule 0 x 497 [13,10]
+ CRUSH rule 0 x 498 [10,6]
+ CRUSH rule 0 x 499 [14,3]
+ CRUSH rule 0 x 500 [15,9]
+ CRUSH rule 0 x 501 [10,13]
+ CRUSH rule 0 x 502 [5,1]
+ CRUSH rule 0 x 503 [15,10]
+ CRUSH rule 0 x 504 [13,2]
+ CRUSH rule 0 x 505 [12,7]
+ CRUSH rule 0 x 506 [11,7]
+ CRUSH rule 0 x 507 [4,14]
+ CRUSH rule 0 x 508 [12,1]
+ CRUSH rule 0 x 509 [4,2]
+ CRUSH rule 0 x 510 [5,3]
+ CRUSH rule 0 x 511 [2,12]
+ CRUSH rule 0 x 512 [15,11]
+ CRUSH rule 0 x 513 [4,9]
+ CRUSH rule 0 x 514 [11,9]
+ CRUSH rule 0 x 515 [12,14]
+ CRUSH rule 0 x 516 [14,11]
+ CRUSH rule 0 x 517 [11,5]
+ CRUSH rule 0 x 518 [3,5]
+ CRUSH rule 0 x 519 [12,14]
+ CRUSH rule 0 x 520 [12,4]
+ CRUSH rule 0 x 521 [11,5]
+ CRUSH rule 0 x 522 [4,12]
+ CRUSH rule 0 x 523 [3,1]
+ CRUSH rule 0 x 524 [15,9]
+ CRUSH rule 0 x 525 [3,15]
+ CRUSH rule 0 x 526 [10,2]
+ CRUSH rule 0 x 527 [3,13]
+ CRUSH rule 0 x 528 [12,7]
+ CRUSH rule 0 x 529 [6,4]
+ CRUSH rule 0 x 530 [11,9]
+ CRUSH rule 0 x 531 [9,15]
+ CRUSH rule 0 x 532 [5,3]
+ CRUSH rule 0 x 533 [12,15]
+ CRUSH rule 0 x 534 [11,9]
+ CRUSH rule 0 x 535 [11,1]
+ CRUSH rule 0 x 536 [9,1]
+ CRUSH rule 0 x 537 [15,5]
+ CRUSH rule 0 x 538 [13,5]
+ CRUSH rule 0 x 539 [10,12]
+ CRUSH rule 0 x 540 [12,15]
+ CRUSH rule 0 x 541 [2,1]
+ CRUSH rule 0 x 542 [3,9]
+ CRUSH rule 0 x 543 [4,10]
+ CRUSH rule 0 x 544 [3,15]
+ CRUSH rule 0 x 545 [14,10]
+ CRUSH rule 0 x 546 [5,15]
+ CRUSH rule 0 x 547 [5,13]
+ CRUSH rule 0 x 548 [11,7]
+ CRUSH rule 0 x 549 [14,1]
+ CRUSH rule 0 x 550 [9,15]
+ CRUSH rule 0 x 551 [11,2]
+ CRUSH rule 0 x 552 [2,11]
+ CRUSH rule 0 x 553 [11,9]
+ CRUSH rule 0 x 554 [11,14]
+ CRUSH rule 0 x 555 [6,5]
+ CRUSH rule 0 x 556 [15,6]
+ CRUSH rule 0 x 557 [12,2]
+ CRUSH rule 0 x 558 [12,1]
+ CRUSH rule 0 x 559 [2,13]
+ CRUSH rule 0 x 560 [4,9]
+ CRUSH rule 0 x 561 [12,7]
+ CRUSH rule 0 x 562 [7,13]
+ CRUSH rule 0 x 563 [15,4]
+ CRUSH rule 0 x 564 [2,13]
+ CRUSH rule 0 x 565 [3,12]
+ CRUSH rule 0 x 566 [6,14]
+ CRUSH rule 0 x 567 [15,4]
+ CRUSH rule 0 x 568 [4,14]
+ CRUSH rule 0 x 569 [11,3]
+ CRUSH rule 0 x 570 [1,10]
+ CRUSH rule 0 x 571 [10,12]
+ CRUSH rule 0 x 572 [12,14]
+ CRUSH rule 0 x 573 [7,15]
+ CRUSH rule 0 x 574 [11,14]
+ CRUSH rule 0 x 575 [5,13]
+ CRUSH rule 0 x 576 [3,15]
+ CRUSH rule 0 x 577 [13,9]
+ CRUSH rule 0 x 578 [4,10]
+ CRUSH rule 0 x 579 [13,1]
+ CRUSH rule 0 x 580 [3,12]
+ CRUSH rule 0 x 581 [7,14]
+ CRUSH rule 0 x 582 [10,5]
+ CRUSH rule 0 x 583 [4,15]
+ CRUSH rule 0 x 584 [10,1]
+ CRUSH rule 0 x 585 [5,3]
+ CRUSH rule 0 x 586 [7,10]
+ CRUSH rule 0 x 587 [11,6]
+ CRUSH rule 0 x 588 [3,12]
+ CRUSH rule 0 x 589 [9,7]
+ CRUSH rule 0 x 590 [12,1]
+ CRUSH rule 0 x 591 [2,6]
+ CRUSH rule 0 x 592 [15,12]
+ CRUSH rule 0 x 593 [13,14]
+ CRUSH rule 0 x 594 [12,14]
+ CRUSH rule 0 x 595 [12,7]
+ CRUSH rule 0 x 596 [2,7]
+ CRUSH rule 0 x 597 [15,1]
+ CRUSH rule 0 x 598 [11,5]
+ CRUSH rule 0 x 599 [13,11]
+ CRUSH rule 0 x 600 [4,12]
+ CRUSH rule 0 x 601 [13,5]
+ CRUSH rule 0 x 602 [3,11]
+ CRUSH rule 0 x 603 [3,1]
+ CRUSH rule 0 x 604 [14,2]
+ CRUSH rule 0 x 605 [2,7]
+ CRUSH rule 0 x 606 [12,15]
+ CRUSH rule 0 x 607 [3,9]
+ CRUSH rule 0 x 608 [13,10]
+ CRUSH rule 0 x 609 [14,3]
+ CRUSH rule 0 x 610 [7,10]
+ CRUSH rule 0 x 611 [13,1]
+ CRUSH rule 0 x 612 [7,1]
+ CRUSH rule 0 x 613 [10,7]
+ CRUSH rule 0 x 614 [9,4]
+ CRUSH rule 0 x 615 [9,4]
+ CRUSH rule 0 x 616 [10,14]
+ CRUSH rule 0 x 617 [15,7]
+ CRUSH rule 0 x 618 [4,2]
+ CRUSH rule 0 x 619 [15,4]
+ CRUSH rule 0 x 620 [3,7]
+ CRUSH rule 0 x 621 [3,6]
+ CRUSH rule 0 x 622 [10,2]
+ CRUSH rule 0 x 623 [4,9]
+ CRUSH rule 0 x 624 [3,9]
+ CRUSH rule 0 x 625 [11,7]
+ CRUSH rule 0 x 626 [10,12]
+ CRUSH rule 0 x 627 [1,12]
+ CRUSH rule 0 x 628 [15,13]
+ CRUSH rule 0 x 629 [5,6]
+ CRUSH rule 0 x 630 [1,4]
+ CRUSH rule 0 x 631 [5,7]
+ CRUSH rule 0 x 632 [12,3]
+ CRUSH rule 0 x 633 [14,4]
+ CRUSH rule 0 x 634 [6,9]
+ CRUSH rule 0 x 635 [6,5]
+ CRUSH rule 0 x 636 [13,6]
+ CRUSH rule 0 x 637 [3,1]
+ CRUSH rule 0 x 638 [10,15]
+ CRUSH rule 0 x 639 [6,9]
+ CRUSH rule 0 x 640 [9,6]
+ CRUSH rule 0 x 641 [10,6]
+ CRUSH rule 0 x 642 [1,15]
+ CRUSH rule 0 x 643 [3,7]
+ CRUSH rule 0 x 644 [15,13]
+ CRUSH rule 0 x 645 [14,2]
+ CRUSH rule 0 x 646 [5,13]
+ CRUSH rule 0 x 647 [10,1]
+ CRUSH rule 0 x 648 [6,5]
+ CRUSH rule 0 x 649 [3,9]
+ CRUSH rule 0 x 650 [10,9]
+ CRUSH rule 0 x 651 [3,9]
+ CRUSH rule 0 x 652 [15,9]
+ CRUSH rule 0 x 653 [11,14]
+ CRUSH rule 0 x 654 [13,6]
+ CRUSH rule 0 x 655 [6,3]
+ CRUSH rule 0 x 656 [3,15]
+ CRUSH rule 0 x 657 [11,15]
+ CRUSH rule 0 x 658 [7,2]
+ CRUSH rule 0 x 659 [2,5]
+ CRUSH rule 0 x 660 [13,14]
+ CRUSH rule 0 x 661 [7,15]
+ CRUSH rule 0 x 662 [15,2]
+ CRUSH rule 0 x 663 [14,9]
+ CRUSH rule 0 x 664 [6,10]
+ CRUSH rule 0 x 665 [2,9]
+ CRUSH rule 0 x 666 [12,3]
+ CRUSH rule 0 x 667 [1,9]
+ CRUSH rule 0 x 668 [9,5]
+ CRUSH rule 0 x 669 [9,7]
+ CRUSH rule 0 x 670 [6,10]
+ CRUSH rule 0 x 671 [6,15]
+ CRUSH rule 0 x 672 [2,9]
+ CRUSH rule 0 x 673 [7,10]
+ CRUSH rule 0 x 674 [7,12]
+ CRUSH rule 0 x 675 [9,5]
+ CRUSH rule 0 x 676 [10,12]
+ CRUSH rule 0 x 677 [2,12]
+ CRUSH rule 0 x 678 [1,2]
+ CRUSH rule 0 x 679 [5,6]
+ CRUSH rule 0 x 680 [7,11]
+ CRUSH rule 0 x 681 [6,4]
+ CRUSH rule 0 x 682 [6,1]
+ CRUSH rule 0 x 683 [6,13]
+ CRUSH rule 0 x 684 [9,11]
+ CRUSH rule 0 x 685 [5,1]
+ CRUSH rule 0 x 686 [1,9]
+ CRUSH rule 0 x 687 [7,13]
+ CRUSH rule 0 x 688 [11,9]
+ CRUSH rule 0 x 689 [5,2]
+ CRUSH rule 0 x 690 [9,7]
+ CRUSH rule 0 x 691 [11,15]
+ CRUSH rule 0 x 692 [15,5]
+ CRUSH rule 0 x 693 [5,6]
+ CRUSH rule 0 x 694 [4,7]
+ CRUSH rule 0 x 695 [6,13]
+ CRUSH rule 0 x 696 [1,2]
+ CRUSH rule 0 x 697 [13,11]
+ CRUSH rule 0 x 698 [11,13]
+ CRUSH rule 0 x 699 [7,14]
+ CRUSH rule 0 x 700 [12,14]
+ CRUSH rule 0 x 701 [3,13]
+ CRUSH rule 0 x 702 [3,12]
+ CRUSH rule 0 x 703 [15,11]
+ CRUSH rule 0 x 704 [6,4]
+ CRUSH rule 0 x 705 [14,6]
+ CRUSH rule 0 x 706 [1,12]
+ CRUSH rule 0 x 707 [4,7]
+ CRUSH rule 0 x 708 [3,10]
+ CRUSH rule 0 x 709 [11,12]
+ CRUSH rule 0 x 710 [14,2]
+ CRUSH rule 0 x 711 [14,3]
+ CRUSH rule 0 x 712 [12,3]
+ CRUSH rule 0 x 713 [11,9]
+ CRUSH rule 0 x 714 [12,1]
+ CRUSH rule 0 x 715 [6,1]
+ CRUSH rule 0 x 716 [11,13]
+ CRUSH rule 0 x 717 [12,4]
+ CRUSH rule 0 x 718 [7,15]
+ CRUSH rule 0 x 719 [5,15]
+ CRUSH rule 0 x 720 [4,13]
+ CRUSH rule 0 x 721 [11,3]
+ CRUSH rule 0 x 722 [2,4]
+ CRUSH rule 0 x 723 [2,1]
+ CRUSH rule 0 x 724 [7,1]
+ CRUSH rule 0 x 725 [11,12]
+ CRUSH rule 0 x 726 [7,14]
+ CRUSH rule 0 x 727 [2,5]
+ CRUSH rule 0 x 728 [13,11]
+ CRUSH rule 0 x 729 [15,11]
+ CRUSH rule 0 x 730 [3,7]
+ CRUSH rule 0 x 731 [9,1]
+ CRUSH rule 0 x 732 [1,2]
+ CRUSH rule 0 x 733 [11,3]
+ CRUSH rule 0 x 734 [14,3]
+ CRUSH rule 0 x 735 [6,9]
+ CRUSH rule 0 x 736 [3,9]
+ CRUSH rule 0 x 737 [1,4]
+ CRUSH rule 0 x 738 [11,15]
+ CRUSH rule 0 x 739 [11,12]
+ CRUSH rule 0 x 740 [7,9]
+ CRUSH rule 0 x 741 [12,11]
+ CRUSH rule 0 x 742 [9,7]
+ CRUSH rule 0 x 743 [5,13]
+ CRUSH rule 0 x 744 [6,2]
+ CRUSH rule 0 x 745 [3,6]
+ CRUSH rule 0 x 746 [3,7]
+ CRUSH rule 0 x 747 [15,11]
+ CRUSH rule 0 x 748 [6,10]
+ CRUSH rule 0 x 749 [14,9]
+ CRUSH rule 0 x 750 [1,14]
+ CRUSH rule 0 x 751 [15,1]
+ CRUSH rule 0 x 752 [13,1]
+ CRUSH rule 0 x 753 [4,11]
+ CRUSH rule 0 x 754 [14,12]
+ CRUSH rule 0 x 755 [13,6]
+ CRUSH rule 0 x 756 [3,4]
+ CRUSH rule 0 x 757 [10,6]
+ CRUSH rule 0 x 758 [6,3]
+ CRUSH rule 0 x 759 [5,7]
+ CRUSH rule 0 x 760 [1,15]
+ CRUSH rule 0 x 761 [2,12]
+ CRUSH rule 0 x 762 [1,4]
+ CRUSH rule 0 x 763 [4,13]
+ CRUSH rule 0 x 764 [1,14]
+ CRUSH rule 0 x 765 [9,15]
+ CRUSH rule 0 x 766 [11,2]
+ CRUSH rule 0 x 767 [6,11]
+ CRUSH rule 0 x 768 [2,12]
+ CRUSH rule 0 x 769 [15,1]
+ CRUSH rule 0 x 770 [15,13]
+ CRUSH rule 0 x 771 [9,2]
+ CRUSH rule 0 x 772 [4,3]
+ CRUSH rule 0 x 773 [3,7]
+ CRUSH rule 0 x 774 [12,6]
+ CRUSH rule 0 x 775 [5,10]
+ CRUSH rule 0 x 776 [10,15]
+ CRUSH rule 0 x 777 [11,13]
+ CRUSH rule 0 x 778 [13,1]
+ CRUSH rule 0 x 779 [5,11]
+ CRUSH rule 0 x 780 [13,9]
+ CRUSH rule 0 x 781 [5,7]
+ CRUSH rule 0 x 782 [2,15]
+ CRUSH rule 0 x 783 [12,7]
+ CRUSH rule 0 x 784 [14,1]
+ CRUSH rule 0 x 785 [6,12]
+ CRUSH rule 0 x 786 [10,5]
+ CRUSH rule 0 x 787 [1,12]
+ CRUSH rule 0 x 788 [4,2]
+ CRUSH rule 0 x 789 [9,2]
+ CRUSH rule 0 x 790 [15,2]
+ CRUSH rule 0 x 791 [9,4]
+ CRUSH rule 0 x 792 [6,4]
+ CRUSH rule 0 x 793 [15,9]
+ CRUSH rule 0 x 794 [5,12]
+ CRUSH rule 0 x 795 [6,14]
+ CRUSH rule 0 x 796 [11,2]
+ CRUSH rule 0 x 797 [14,3]
+ CRUSH rule 0 x 798 [5,11]
+ CRUSH rule 0 x 799 [2,9]
+ CRUSH rule 0 x 800 [6,3]
+ CRUSH rule 0 x 801 [2,5]
+ CRUSH rule 0 x 802 [1,4]
+ CRUSH rule 0 x 803 [7,2]
+ CRUSH rule 0 x 804 [5,14]
+ CRUSH rule 0 x 805 [13,4]
+ CRUSH rule 0 x 806 [6,2]
+ CRUSH rule 0 x 807 [14,2]
+ CRUSH rule 0 x 808 [2,15]
+ CRUSH rule 0 x 809 [1,11]
+ CRUSH rule 0 x 810 [2,5]
+ CRUSH rule 0 x 811 [15,6]
+ CRUSH rule 0 x 812 [7,11]
+ CRUSH rule 0 x 813 [4,10]
+ CRUSH rule 0 x 814 [13,4]
+ CRUSH rule 0 x 815 [15,12]
+ CRUSH rule 0 x 816 [14,10]
+ CRUSH rule 0 x 817 [10,7]
+ CRUSH rule 0 x 818 [15,2]
+ CRUSH rule 0 x 819 [5,12]
+ CRUSH rule 0 x 820 [3,6]
+ CRUSH rule 0 x 821 [15,10]
+ CRUSH rule 0 x 822 [10,13]
+ CRUSH rule 0 x 823 [2,6]
+ CRUSH rule 0 x 824 [3,7]
+ CRUSH rule 0 x 825 [10,5]
+ CRUSH rule 0 x 826 [5,2]
+ CRUSH rule 0 x 827 [13,5]
+ CRUSH rule 0 x 828 [12,6]
+ CRUSH rule 0 x 829 [13,6]
+ CRUSH rule 0 x 830 [15,13]
+ CRUSH rule 0 x 831 [1,4]
+ CRUSH rule 0 x 832 [14,11]
+ CRUSH rule 0 x 833 [9,13]
+ CRUSH rule 0 x 834 [9,7]
+ CRUSH rule 0 x 835 [14,3]
+ CRUSH rule 0 x 836 [3,9]
+ CRUSH rule 0 x 837 [15,12]
+ CRUSH rule 0 x 838 [12,14]
+ CRUSH rule 0 x 839 [3,4]
+ CRUSH rule 0 x 840 [10,15]
+ CRUSH rule 0 x 841 [3,5]
+ CRUSH rule 0 x 842 [9,13]
+ CRUSH rule 0 x 843 [14,7]
+ CRUSH rule 0 x 844 [7,1]
+ CRUSH rule 0 x 845 [13,6]
+ CRUSH rule 0 x 846 [3,7]
+ CRUSH rule 0 x 847 [12,15]
+ CRUSH rule 0 x 848 [11,13]
+ CRUSH rule 0 x 849 [3,15]
+ CRUSH rule 0 x 850 [1,3]
+ CRUSH rule 0 x 851 [14,4]
+ CRUSH rule 0 x 852 [9,12]
+ CRUSH rule 0 x 853 [13,14]
+ CRUSH rule 0 x 854 [7,11]
+ CRUSH rule 0 x 855 [14,4]
+ CRUSH rule 0 x 856 [5,10]
+ CRUSH rule 0 x 857 [4,3]
+ CRUSH rule 0 x 858 [5,15]
+ CRUSH rule 0 x 859 [5,15]
+ CRUSH rule 0 x 860 [11,14]
+ CRUSH rule 0 x 861 [13,7]
+ CRUSH rule 0 x 862 [5,10]
+ CRUSH rule 0 x 863 [11,6]
+ CRUSH rule 0 x 864 [6,13]
+ CRUSH rule 0 x 865 [4,1]
+ CRUSH rule 0 x 866 [2,13]
+ CRUSH rule 0 x 867 [12,2]
+ CRUSH rule 0 x 868 [14,11]
+ CRUSH rule 0 x 869 [10,13]
+ CRUSH rule 0 x 870 [14,9]
+ CRUSH rule 0 x 871 [6,2]
+ CRUSH rule 0 x 872 [6,1]
+ CRUSH rule 0 x 873 [2,5]
+ CRUSH rule 0 x 874 [12,4]
+ CRUSH rule 0 x 875 [10,6]
+ CRUSH rule 0 x 876 [14,7]
+ CRUSH rule 0 x 877 [15,11]
+ CRUSH rule 0 x 878 [7,14]
+ CRUSH rule 0 x 879 [12,2]
+ CRUSH rule 0 x 880 [2,12]
+ CRUSH rule 0 x 881 [6,3]
+ CRUSH rule 0 x 882 [11,13]
+ CRUSH rule 0 x 883 [13,1]
+ CRUSH rule 0 x 884 [6,15]
+ CRUSH rule 0 x 885 [14,7]
+ CRUSH rule 0 x 886 [13,11]
+ CRUSH rule 0 x 887 [14,4]
+ CRUSH rule 0 x 888 [10,12]
+ CRUSH rule 0 x 889 [15,13]
+ CRUSH rule 0 x 890 [10,12]
+ CRUSH rule 0 x 891 [9,5]
+ CRUSH rule 0 x 892 [12,15]
+ CRUSH rule 0 x 893 [1,3]
+ CRUSH rule 0 x 894 [7,2]
+ CRUSH rule 0 x 895 [2,1]
+ CRUSH rule 0 x 896 [9,1]
+ CRUSH rule 0 x 897 [7,5]
+ CRUSH rule 0 x 898 [10,6]
+ CRUSH rule 0 x 899 [1,11]
+ CRUSH rule 0 x 900 [2,9]
+ CRUSH rule 0 x 901 [9,12]
+ CRUSH rule 0 x 902 [4,2]
+ CRUSH rule 0 x 903 [14,10]
+ CRUSH rule 0 x 904 [15,12]
+ CRUSH rule 0 x 905 [12,6]
+ CRUSH rule 0 x 906 [14,11]
+ CRUSH rule 0 x 907 [7,12]
+ CRUSH rule 0 x 908 [2,15]
+ CRUSH rule 0 x 909 [10,14]
+ CRUSH rule 0 x 910 [12,7]
+ CRUSH rule 0 x 911 [11,15]
+ CRUSH rule 0 x 912 [6,4]
+ CRUSH rule 0 x 913 [4,6]
+ CRUSH rule 0 x 914 [4,15]
+ CRUSH rule 0 x 915 [12,14]
+ CRUSH rule 0 x 916 [3,1]
+ CRUSH rule 0 x 917 [1,15]
+ CRUSH rule 0 x 918 [7,14]
+ CRUSH rule 0 x 919 [10,7]
+ CRUSH rule 0 x 920 [4,2]
+ CRUSH rule 0 x 921 [1,11]
+ CRUSH rule 0 x 922 [6,4]
+ CRUSH rule 0 x 923 [12,2]
+ CRUSH rule 0 x 924 [6,2]
+ CRUSH rule 0 x 925 [12,15]
+ CRUSH rule 0 x 926 [3,13]
+ CRUSH rule 0 x 927 [6,5]
+ CRUSH rule 0 x 928 [13,1]
+ CRUSH rule 0 x 929 [10,7]
+ CRUSH rule 0 x 930 [7,15]
+ CRUSH rule 0 x 931 [6,15]
+ CRUSH rule 0 x 932 [13,2]
+ CRUSH rule 0 x 933 [12,7]
+ CRUSH rule 0 x 934 [12,2]
+ CRUSH rule 0 x 935 [6,11]
+ CRUSH rule 0 x 936 [9,12]
+ CRUSH rule 0 x 937 [14,2]
+ CRUSH rule 0 x 938 [14,3]
+ CRUSH rule 0 x 939 [6,4]
+ CRUSH rule 0 x 940 [13,11]
+ CRUSH rule 0 x 941 [3,12]
+ CRUSH rule 0 x 942 [15,12]
+ CRUSH rule 0 x 943 [10,2]
+ CRUSH rule 0 x 944 [2,9]
+ CRUSH rule 0 x 945 [10,15]
+ CRUSH rule 0 x 946 [11,15]
+ CRUSH rule 0 x 947 [11,3]
+ CRUSH rule 0 x 948 [7,13]
+ CRUSH rule 0 x 949 [9,1]
+ CRUSH rule 0 x 950 [9,15]
+ CRUSH rule 0 x 951 [2,6]
+ CRUSH rule 0 x 952 [9,7]
+ CRUSH rule 0 x 953 [1,3]
+ CRUSH rule 0 x 954 [10,2]
+ CRUSH rule 0 x 955 [7,14]
+ CRUSH rule 0 x 956 [1,6]
+ CRUSH rule 0 x 957 [14,11]
+ CRUSH rule 0 x 958 [15,4]
+ CRUSH rule 0 x 959 [2,1]
+ CRUSH rule 0 x 960 [2,6]
+ CRUSH rule 0 x 961 [3,13]
+ CRUSH rule 0 x 962 [5,11]
+ CRUSH rule 0 x 963 [13,10]
+ CRUSH rule 0 x 964 [7,11]
+ CRUSH rule 0 x 965 [12,2]
+ CRUSH rule 0 x 966 [12,14]
+ CRUSH rule 0 x 967 [7,5]
+ CRUSH rule 0 x 968 [12,15]
+ CRUSH rule 0 x 969 [11,4]
+ CRUSH rule 0 x 970 [5,12]
+ CRUSH rule 0 x 971 [1,9]
+ CRUSH rule 0 x 972 [12,3]
+ CRUSH rule 0 x 973 [1,10]
+ CRUSH rule 0 x 974 [7,11]
+ CRUSH rule 0 x 975 [7,9]
+ CRUSH rule 0 x 976 [7,3]
+ CRUSH rule 0 x 977 [14,3]
+ CRUSH rule 0 x 978 [12,5]
+ CRUSH rule 0 x 979 [5,1]
+ CRUSH rule 0 x 980 [15,11]
+ CRUSH rule 0 x 981 [5,11]
+ CRUSH rule 0 x 982 [2,6]
+ CRUSH rule 0 x 983 [3,12]
+ CRUSH rule 0 x 984 [15,13]
+ CRUSH rule 0 x 985 [11,2]
+ CRUSH rule 0 x 986 [6,13]
+ CRUSH rule 0 x 987 [13,14]
+ CRUSH rule 0 x 988 [12,9]
+ CRUSH rule 0 x 989 [7,4]
+ CRUSH rule 0 x 990 [1,10]
+ CRUSH rule 0 x 991 [7,11]
+ CRUSH rule 0 x 992 [9,10]
+ CRUSH rule 0 x 993 [6,10]
+ CRUSH rule 0 x 994 [3,13]
+ CRUSH rule 0 x 995 [15,6]
+ CRUSH rule 0 x 996 [15,10]
+ CRUSH rule 0 x 997 [15,2]
+ CRUSH rule 0 x 998 [6,1]
+ CRUSH rule 0 x 999 [9,10]
+ CRUSH rule 0 x 1000 [14,2]
+ CRUSH rule 0 x 1001 [11,14]
+ CRUSH rule 0 x 1002 [1,10]
+ CRUSH rule 0 x 1003 [10,7]
+ CRUSH rule 0 x 1004 [15,1]
+ CRUSH rule 0 x 1005 [6,12]
+ CRUSH rule 0 x 1006 [10,12]
+ CRUSH rule 0 x 1007 [1,7]
+ CRUSH rule 0 x 1008 [7,4]
+ CRUSH rule 0 x 1009 [5,2]
+ CRUSH rule 0 x 1010 [10,2]
+ CRUSH rule 0 x 1011 [6,3]
+ CRUSH rule 0 x 1012 [12,6]
+ CRUSH rule 0 x 1013 [2,14]
+ CRUSH rule 0 x 1014 [1,13]
+ CRUSH rule 0 x 1015 [12,6]
+ CRUSH rule 0 x 1016 [10,13]
+ CRUSH rule 0 x 1017 [5,11]
+ CRUSH rule 0 x 1018 [13,11]
+ CRUSH rule 0 x 1019 [10,13]
+ CRUSH rule 0 x 1020 [3,1]
+ CRUSH rule 0 x 1021 [2,11]
+ CRUSH rule 0 x 1022 [15,5]
+ CRUSH rule 0 x 1023 [15,2]
+ rule 0 (replicated_ruleset) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3]
+ CRUSH rule 0 x 1 [10,15,1]
+ CRUSH rule 0 x 2 [1,12,2]
+ CRUSH rule 0 x 3 [15,4,10]
+ CRUSH rule 0 x 4 [14,2,10]
+ CRUSH rule 0 x 5 [7,4,11]
+ CRUSH rule 0 x 6 [12,6,10]
+ CRUSH rule 0 x 7 [9,2,6]
+ CRUSH rule 0 x 8 [10,2,15]
+ CRUSH rule 0 x 9 [7,1,14]
+ CRUSH rule 0 x 10 [10,14,4]
+ CRUSH rule 0 x 11 [13,9,14]
+ CRUSH rule 0 x 12 [7,1,2]
+ CRUSH rule 0 x 13 [3,5,12]
+ CRUSH rule 0 x 14 [13,5,2]
+ CRUSH rule 0 x 15 [15,1,9]
+ CRUSH rule 0 x 16 [7,11,14]
+ CRUSH rule 0 x 17 [10,1,13]
+ CRUSH rule 0 x 18 [1,7,3]
+ CRUSH rule 0 x 19 [7,12,2]
+ CRUSH rule 0 x 20 [14,12,3]
+ CRUSH rule 0 x 21 [3,12,1]
+ CRUSH rule 0 x 22 [6,3,13]
+ CRUSH rule 0 x 23 [10,5,13]
+ CRUSH rule 0 x 24 [12,11,3]
+ CRUSH rule 0 x 25 [7,12,15]
+ CRUSH rule 0 x 26 [1,7,13]
+ CRUSH rule 0 x 27 [3,6,15]
+ CRUSH rule 0 x 28 [14,4,3]
+ CRUSH rule 0 x 29 [5,14,12]
+ CRUSH rule 0 x 30 [2,5,6]
+ CRUSH rule 0 x 31 [5,15,10]
+ CRUSH rule 0 x 32 [9,10,2]
+ CRUSH rule 0 x 33 [13,4,9]
+ CRUSH rule 0 x 34 [13,15,2]
+ CRUSH rule 0 x 35 [4,14,3]
+ CRUSH rule 0 x 36 [3,12,9]
+ CRUSH rule 0 x 37 [9,2,6]
+ CRUSH rule 0 x 38 [3,4,13]
+ CRUSH rule 0 x 39 [12,7,14]
+ CRUSH rule 0 x 40 [10,1,9]
+ CRUSH rule 0 x 41 [4,9,11]
+ CRUSH rule 0 x 42 [3,6,14]
+ CRUSH rule 0 x 43 [10,5,15]
+ CRUSH rule 0 x 44 [11,4,13]
+ CRUSH rule 0 x 45 [11,12,15]
+ CRUSH rule 0 x 46 [6,9,2]
+ CRUSH rule 0 x 47 [3,9,6]
+ CRUSH rule 0 x 48 [4,6,2]
+ CRUSH rule 0 x 49 [9,15,10]
+ CRUSH rule 0 x 50 [14,12,1]
+ CRUSH rule 0 x 51 [10,6,5]
+ CRUSH rule 0 x 52 [12,1,9]
+ CRUSH rule 0 x 53 [3,6,13]
+ CRUSH rule 0 x 54 [4,13,9]
+ CRUSH rule 0 x 55 [4,11,2]
+ CRUSH rule 0 x 56 [5,9,10]
+ CRUSH rule 0 x 57 [6,2,1]
+ CRUSH rule 0 x 58 [7,1,11]
+ CRUSH rule 0 x 59 [2,13,1]
+ CRUSH rule 0 x 60 [3,6,11]
+ CRUSH rule 0 x 61 [3,15,13]
+ CRUSH rule 0 x 62 [15,11,7]
+ CRUSH rule 0 x 63 [10,14,12]
+ CRUSH rule 0 x 64 [3,9,1]
+ CRUSH rule 0 x 65 [4,12,11]
+ CRUSH rule 0 x 66 [15,11,6]
+ CRUSH rule 0 x 67 [2,6,4]
+ CRUSH rule 0 x 68 [15,7,4]
+ CRUSH rule 0 x 69 [2,1,15]
+ CRUSH rule 0 x 70 [9,6,1]
+ CRUSH rule 0 x 71 [15,5,1]
+ CRUSH rule 0 x 72 [9,10,3]
+ CRUSH rule 0 x 73 [5,3,11]
+ CRUSH rule 0 x 74 [11,7,9]
+ CRUSH rule 0 x 75 [9,7,11]
+ CRUSH rule 0 x 76 [6,1,3]
+ CRUSH rule 0 x 77 [7,4,2]
+ CRUSH rule 0 x 78 [9,3,1]
+ CRUSH rule 0 x 79 [13,2,15]
+ CRUSH rule 0 x 80 [15,2,6]
+ CRUSH rule 0 x 81 [15,2,1]
+ CRUSH rule 0 x 82 [14,13,5]
+ CRUSH rule 0 x 83 [4,15,3]
+ CRUSH rule 0 x 84 [10,7,9]
+ CRUSH rule 0 x 85 [3,15,9]
+ CRUSH rule 0 x 86 [10,9,14]
+ CRUSH rule 0 x 87 [15,10,7]
+ CRUSH rule 0 x 88 [4,13,3]
+ CRUSH rule 0 x 89 [3,9,7]
+ CRUSH rule 0 x 90 [4,9,7]
+ CRUSH rule 0 x 91 [6,11,9]
+ CRUSH rule 0 x 92 [1,5,10]
+ CRUSH rule 0 x 93 [9,3,15]
+ CRUSH rule 0 x 94 [9,2,12]
+ CRUSH rule 0 x 95 [7,15,4]
+ CRUSH rule 0 x 96 [2,15,11]
+ CRUSH rule 0 x 97 [4,11,2]
+ CRUSH rule 0 x 98 [11,13,9]
+ CRUSH rule 0 x 99 [12,4,11]
+ CRUSH rule 0 x 100 [9,4,10]
+ CRUSH rule 0 x 101 [15,7,1]
+ CRUSH rule 0 x 102 [3,11,14]
+ CRUSH rule 0 x 103 [13,11,6]
+ CRUSH rule 0 x 104 [14,6,3]
+ CRUSH rule 0 x 105 [14,10,1]
+ CRUSH rule 0 x 106 [6,5,13]
+ CRUSH rule 0 x 107 [3,1,10]
+ CRUSH rule 0 x 108 [5,10,7]
+ CRUSH rule 0 x 109 [9,1,13]
+ CRUSH rule 0 x 110 [5,1,11]
+ CRUSH rule 0 x 111 [10,1,9]
+ CRUSH rule 0 x 112 [1,10,4]
+ CRUSH rule 0 x 113 [6,10,13]
+ CRUSH rule 0 x 114 [5,13,6]
+ CRUSH rule 0 x 115 [10,13,14]
+ CRUSH rule 0 x 116 [1,14,13]
+ CRUSH rule 0 x 117 [5,6,1]
+ CRUSH rule 0 x 118 [10,4,13]
+ CRUSH rule 0 x 119 [14,12,11]
+ CRUSH rule 0 x 120 [11,3,14]
+ CRUSH rule 0 x 121 [9,5,1]
+ CRUSH rule 0 x 122 [4,3,14]
+ CRUSH rule 0 x 123 [3,10,5]
+ CRUSH rule 0 x 124 [12,2,1]
+ CRUSH rule 0 x 125 [9,12,15]
+ CRUSH rule 0 x 126 [7,15,10]
+ CRUSH rule 0 x 127 [4,14,9]
+ CRUSH rule 0 x 128 [3,12,1]
+ CRUSH rule 0 x 129 [11,13,14]
+ CRUSH rule 0 x 130 [3,13,5]
+ CRUSH rule 0 x 131 [12,1,6]
+ CRUSH rule 0 x 132 [11,15,13]
+ CRUSH rule 0 x 133 [3,6,9]
+ CRUSH rule 0 x 134 [12,5,6]
+ CRUSH rule 0 x 135 [3,14,12]
+ CRUSH rule 0 x 136 [15,6,9]
+ CRUSH rule 0 x 137 [14,3,6]
+ CRUSH rule 0 x 138 [13,15,4]
+ CRUSH rule 0 x 139 [11,2,13]
+ CRUSH rule 0 x 140 [11,4,12]
+ CRUSH rule 0 x 141 [6,12,15]
+ CRUSH rule 0 x 142 [3,14,7]
+ CRUSH rule 0 x 143 [9,6,4]
+ CRUSH rule 0 x 144 [13,7,11]
+ CRUSH rule 0 x 145 [12,2,6]
+ CRUSH rule 0 x 146 [1,5,9]
+ CRUSH rule 0 x 147 [1,4,9]
+ CRUSH rule 0 x 148 [12,7,9]
+ CRUSH rule 0 x 149 [2,5,9]
+ CRUSH rule 0 x 150 [1,15,2]
+ CRUSH rule 0 x 151 [2,9,14]
+ CRUSH rule 0 x 152 [5,9,2]
+ CRUSH rule 0 x 153 [6,9,4]
+ CRUSH rule 0 x 154 [3,11,7]
+ CRUSH rule 0 x 155 [14,12,7]
+ CRUSH rule 0 x 156 [7,13,3]
+ CRUSH rule 0 x 157 [15,1,6]
+ CRUSH rule 0 x 158 [15,1,10]
+ CRUSH rule 0 x 159 [4,14,3]
+ CRUSH rule 0 x 160 [5,7,3]
+ CRUSH rule 0 x 161 [1,2,11]
+ CRUSH rule 0 x 162 [10,6,1]
+ CRUSH rule 0 x 163 [15,1,10]
+ CRUSH rule 0 x 164 [9,14,10]
+ CRUSH rule 0 x 165 [11,7,2]
+ CRUSH rule 0 x 166 [1,2,12]
+ CRUSH rule 0 x 167 [9,7,3]
+ CRUSH rule 0 x 168 [13,2,4]
+ CRUSH rule 0 x 169 [1,4,9]
+ CRUSH rule 0 x 170 [1,15,7]
+ CRUSH rule 0 x 171 [9,2,10]
+ CRUSH rule 0 x 172 [14,4,10]
+ CRUSH rule 0 x 173 [5,10,12]
+ CRUSH rule 0 x 174 [15,6,4]
+ CRUSH rule 0 x 175 [5,7,9]
+ CRUSH rule 0 x 176 [9,6,3]
+ CRUSH rule 0 x 177 [2,9,10]
+ CRUSH rule 0 x 178 [12,11,7]
+ CRUSH rule 0 x 179 [2,10,13]
+ CRUSH rule 0 x 180 [3,11,5]
+ CRUSH rule 0 x 181 [9,12,6]
+ CRUSH rule 0 x 182 [5,13,11]
+ CRUSH rule 0 x 183 [5,7,10]
+ CRUSH rule 0 x 184 [2,5,11]
+ CRUSH rule 0 x 185 [13,5,7]
+ CRUSH rule 0 x 186 [6,14,13]
+ CRUSH rule 0 x 187 [1,4,11]
+ CRUSH rule 0 x 188 [9,13,5]
+ CRUSH rule 0 x 189 [6,12,4]
+ CRUSH rule 0 x 190 [9,13,15]
+ CRUSH rule 0 x 191 [7,11,4]
+ CRUSH rule 0 x 192 [2,11,5]
+ CRUSH rule 0 x 193 [3,13,6]
+ CRUSH rule 0 x 194 [3,13,4]
+ CRUSH rule 0 x 195 [5,7,10]
+ CRUSH rule 0 x 196 [4,15,1]
+ CRUSH rule 0 x 197 [14,10,13]
+ CRUSH rule 0 x 198 [2,5,6]
+ CRUSH rule 0 x 199 [2,10,4]
+ CRUSH rule 0 x 200 [7,14,11]
+ CRUSH rule 0 x 201 [9,14,1]
+ CRUSH rule 0 x 202 [14,11,7]
+ CRUSH rule 0 x 203 [12,5,7]
+ CRUSH rule 0 x 204 [6,11,3]
+ CRUSH rule 0 x 205 [15,4,6]
+ CRUSH rule 0 x 206 [13,11,2]
+ CRUSH rule 0 x 207 [2,11,7]
+ CRUSH rule 0 x 208 [13,1,6]
+ CRUSH rule 0 x 209 [6,15,13]
+ CRUSH rule 0 x 210 [13,11,2]
+ CRUSH rule 0 x 211 [2,14,1]
+ CRUSH rule 0 x 212 [10,1,12]
+ CRUSH rule 0 x 213 [3,9,6]
+ CRUSH rule 0 x 214 [7,15,4]
+ CRUSH rule 0 x 215 [6,1,4]
+ CRUSH rule 0 x 216 [12,9,6]
+ CRUSH rule 0 x 217 [12,11,1]
+ CRUSH rule 0 x 218 [12,10,15]
+ CRUSH rule 0 x 219 [3,11,14]
+ CRUSH rule 0 x 220 [14,4,3]
+ CRUSH rule 0 x 221 [15,5,2]
+ CRUSH rule 0 x 222 [10,4,3]
+ CRUSH rule 0 x 223 [9,7,11]
+ CRUSH rule 0 x 224 [1,7,10]
+ CRUSH rule 0 x 225 [10,5,2]
+ CRUSH rule 0 x 226 [4,1,9]
+ CRUSH rule 0 x 227 [7,2,12]
+ CRUSH rule 0 x 228 [2,15,11]
+ CRUSH rule 0 x 229 [9,3,7]
+ CRUSH rule 0 x 230 [10,5,7]
+ CRUSH rule 0 x 231 [2,7,5]
+ CRUSH rule 0 x 232 [10,5,13]
+ CRUSH rule 0 x 233 [6,12,11]
+ CRUSH rule 0 x 234 [10,1,2]
+ CRUSH rule 0 x 235 [13,14,7]
+ CRUSH rule 0 x 236 [2,15,9]
+ CRUSH rule 0 x 237 [3,12,9]
+ CRUSH rule 0 x 238 [2,10,4]
+ CRUSH rule 0 x 239 [4,15,10]
+ CRUSH rule 0 x 240 [15,5,13]
+ CRUSH rule 0 x 241 [7,9,15]
+ CRUSH rule 0 x 242 [14,2,6]
+ CRUSH rule 0 x 243 [2,11,5]
+ CRUSH rule 0 x 244 [13,9,15]
+ CRUSH rule 0 x 245 [12,9,15]
+ CRUSH rule 0 x 246 [15,3,5]
+ CRUSH rule 0 x 247 [6,4,9]
+ CRUSH rule 0 x 248 [5,13,7]
+ CRUSH rule 0 x 249 [10,14,7]
+ CRUSH rule 0 x 250 [12,15,1]
+ CRUSH rule 0 x 251 [13,2,15]
+ CRUSH rule 0 x 252 [7,5,13]
+ CRUSH rule 0 x 253 [3,13,15]
+ CRUSH rule 0 x 254 [2,9,13]
+ CRUSH rule 0 x 255 [1,9,13]
+ CRUSH rule 0 x 256 [6,9,13]
+ CRUSH rule 0 x 257 [15,12,3]
+ CRUSH rule 0 x 258 [12,5,6]
+ CRUSH rule 0 x 259 [9,10,4]
+ CRUSH rule 0 x 260 [10,12,6]
+ CRUSH rule 0 x 261 [13,7,2]
+ CRUSH rule 0 x 262 [15,3,12]
+ CRUSH rule 0 x 263 [12,6,10]
+ CRUSH rule 0 x 264 [13,14,11]
+ CRUSH rule 0 x 265 [12,10,14]
+ CRUSH rule 0 x 266 [14,7,11]
+ CRUSH rule 0 x 267 [12,11,6]
+ CRUSH rule 0 x 268 [4,1,15]
+ CRUSH rule 0 x 269 [11,1,15]
+ CRUSH rule 0 x 270 [7,11,12]
+ CRUSH rule 0 x 271 [4,7,3]
+ CRUSH rule 0 x 272 [15,5,13]
+ CRUSH rule 0 x 273 [2,10,7]
+ CRUSH rule 0 x 274 [10,2,5]
+ CRUSH rule 0 x 275 [10,3,4]
+ CRUSH rule 0 x 276 [5,12,9]
+ CRUSH rule 0 x 277 [14,3,13]
+ CRUSH rule 0 x 278 [5,6,14]
+ CRUSH rule 0 x 279 [6,10,13]
+ CRUSH rule 0 x 280 [7,3,14]
+ CRUSH rule 0 x 281 [5,11,14]
+ CRUSH rule 0 x 282 [2,1,13]
+ CRUSH rule 0 x 283 [4,1,12]
+ CRUSH rule 0 x 284 [5,11,7]
+ CRUSH rule 0 x 285 [15,5,3]
+ CRUSH rule 0 x 286 [10,4,3]
+ CRUSH rule 0 x 287 [12,4,9]
+ CRUSH rule 0 x 288 [4,12,10]
+ CRUSH rule 0 x 289 [2,5,14]
+ CRUSH rule 0 x 290 [12,2,5]
+ CRUSH rule 0 x 291 [7,11,1]
+ CRUSH rule 0 x 292 [4,10,6]
+ CRUSH rule 0 x 293 [6,5,11]
+ CRUSH rule 0 x 294 [9,12,3]
+ CRUSH rule 0 x 295 [6,10,3]
+ CRUSH rule 0 x 296 [3,1,13]
+ CRUSH rule 0 x 297 [6,13,4]
+ CRUSH rule 0 x 298 [14,9,13]
+ CRUSH rule 0 x 299 [14,12,11]
+ CRUSH rule 0 x 300 [15,7,10]
+ CRUSH rule 0 x 301 [9,11,7]
+ CRUSH rule 0 x 302 [9,7,1]
+ CRUSH rule 0 x 303 [4,13,3]
+ CRUSH rule 0 x 304 [6,9,2]
+ CRUSH rule 0 x 305 [13,7,5]
+ CRUSH rule 0 x 306 [10,12,4]
+ CRUSH rule 0 x 307 [11,12,15]
+ CRUSH rule 0 x 308 [12,14,10]
+ CRUSH rule 0 x 309 [9,3,12]
+ CRUSH rule 0 x 310 [3,1,5]
+ CRUSH rule 0 x 311 [3,9,7]
+ CRUSH rule 0 x 312 [15,13,9]
+ CRUSH rule 0 x 313 [9,15,3]
+ CRUSH rule 0 x 314 [2,15,9]
+ CRUSH rule 0 x 315 [15,2,13]
+ CRUSH rule 0 x 316 [4,9,11]
+ CRUSH rule 0 x 317 [1,5,3]
+ CRUSH rule 0 x 318 [4,1,15]
+ CRUSH rule 0 x 319 [2,15,4]
+ CRUSH rule 0 x 320 [5,7,13]
+ CRUSH rule 0 x 321 [1,6,11]
+ CRUSH rule 0 x 322 [13,7,5]
+ CRUSH rule 0 x 323 [7,4,10]
+ CRUSH rule 0 x 324 [5,6,10]
+ CRUSH rule 0 x 325 [9,10,14]
+ CRUSH rule 0 x 326 [11,7,13]
+ CRUSH rule 0 x 327 [12,5,10]
+ CRUSH rule 0 x 328 [5,2,6]
+ CRUSH rule 0 x 329 [2,6,15]
+ CRUSH rule 0 x 330 [3,9,11]
+ CRUSH rule 0 x 331 [12,14,6]
+ CRUSH rule 0 x 332 [10,12,6]
+ CRUSH rule 0 x 333 [6,5,3]
+ CRUSH rule 0 x 334 [4,9,2]
+ CRUSH rule 0 x 335 [11,7,1]
+ CRUSH rule 0 x 336 [6,14,13]
+ CRUSH rule 0 x 337 [15,11,3]
+ CRUSH rule 0 x 338 [10,5,3]
+ CRUSH rule 0 x 339 [11,14,13]
+ CRUSH rule 0 x 340 [11,6,12]
+ CRUSH rule 0 x 341 [7,5,2]
+ CRUSH rule 0 x 342 [12,14,1]
+ CRUSH rule 0 x 343 [12,14,9]
+ CRUSH rule 0 x 344 [9,11,5]
+ CRUSH rule 0 x 345 [14,2,11]
+ CRUSH rule 0 x 346 [5,3,14]
+ CRUSH rule 0 x 347 [10,2,12]
+ CRUSH rule 0 x 348 [7,9,10]
+ CRUSH rule 0 x 349 [9,6,10]
+ CRUSH rule 0 x 350 [13,9,15]
+ CRUSH rule 0 x 351 [13,5,15]
+ CRUSH rule 0 x 352 [1,12,11]
+ CRUSH rule 0 x 353 [10,14,12]
+ CRUSH rule 0 x 354 [6,3,15]
+ CRUSH rule 0 x 355 [13,14,6]
+ CRUSH rule 0 x 356 [15,13,2]
+ CRUSH rule 0 x 357 [4,11,1]
+ CRUSH rule 0 x 358 [12,7,2]
+ CRUSH rule 0 x 359 [5,15,7]
+ CRUSH rule 0 x 360 [13,10,1]
+ CRUSH rule 0 x 361 [5,3,13]
+ CRUSH rule 0 x 362 [2,9,11]
+ CRUSH rule 0 x 363 [7,12,3]
+ CRUSH rule 0 x 364 [2,12,6]
+ CRUSH rule 0 x 365 [13,5,11]
+ CRUSH rule 0 x 366 [12,7,3]
+ CRUSH rule 0 x 367 [7,13,3]
+ CRUSH rule 0 x 368 [7,9,10]
+ CRUSH rule 0 x 369 [7,5,3]
+ CRUSH rule 0 x 370 [4,7,14]
+ CRUSH rule 0 x 371 [1,7,12]
+ CRUSH rule 0 x 372 [10,4,3]
+ CRUSH rule 0 x 373 [15,5,2]
+ CRUSH rule 0 x 374 [3,15,12]
+ CRUSH rule 0 x 375 [5,2,14]
+ CRUSH rule 0 x 376 [5,14,10]
+ CRUSH rule 0 x 377 [1,15,2]
+ CRUSH rule 0 x 378 [9,12,2]
+ CRUSH rule 0 x 379 [11,2,15]
+ CRUSH rule 0 x 380 [6,1,12]
+ CRUSH rule 0 x 381 [15,13,7]
+ CRUSH rule 0 x 382 [14,3,1]
+ CRUSH rule 0 x 383 [3,6,11]
+ CRUSH rule 0 x 384 [4,13,6]
+ CRUSH rule 0 x 385 [4,6,15]
+ CRUSH rule 0 x 386 [14,3,11]
+ CRUSH rule 0 x 387 [1,11,5]
+ CRUSH rule 0 x 388 [2,6,11]
+ CRUSH rule 0 x 389 [12,7,2]
+ CRUSH rule 0 x 390 [2,11,13]
+ CRUSH rule 0 x 391 [3,4,9]
+ CRUSH rule 0 x 392 [11,5,14]
+ CRUSH rule 0 x 393 [2,14,5]
+ CRUSH rule 0 x 394 [4,9,3]
+ CRUSH rule 0 x 395 [10,13,5]
+ CRUSH rule 0 x 396 [2,12,15]
+ CRUSH rule 0 x 397 [1,14,9]
+ CRUSH rule 0 x 398 [9,2,1]
+ CRUSH rule 0 x 399 [5,9,14]
+ CRUSH rule 0 x 400 [10,6,2]
+ CRUSH rule 0 x 401 [6,9,11]
+ CRUSH rule 0 x 402 [4,7,9]
+ CRUSH rule 0 x 403 [7,15,13]
+ CRUSH rule 0 x 404 [14,12,7]
+ CRUSH rule 0 x 405 [9,15,11]
+ CRUSH rule 0 x 406 [12,14,9]
+ CRUSH rule 0 x 407 [9,5,12]
+ CRUSH rule 0 x 408 [7,1,5]
+ CRUSH rule 0 x 409 [11,2,4]
+ CRUSH rule 0 x 410 [6,4,14]
+ CRUSH rule 0 x 411 [13,11,15]
+ CRUSH rule 0 x 412 [5,9,6]
+ CRUSH rule 0 x 413 [13,5,3]
+ CRUSH rule 0 x 414 [3,11,9]
+ CRUSH rule 0 x 415 [6,10,14]
+ CRUSH rule 0 x 416 [13,1,4]
+ CRUSH rule 0 x 417 [4,12,1]
+ CRUSH rule 0 x 418 [14,5,10]
+ CRUSH rule 0 x 419 [5,14,10]
+ CRUSH rule 0 x 420 [2,4,9]
+ CRUSH rule 0 x 421 [15,4,10]
+ CRUSH rule 0 x 422 [4,11,2]
+ CRUSH rule 0 x 423 [3,15,12]
+ CRUSH rule 0 x 424 [6,10,12]
+ CRUSH rule 0 x 425 [11,15,2]
+ CRUSH rule 0 x 426 [12,4,7]
+ CRUSH rule 0 x 427 [14,10,3]
+ CRUSH rule 0 x 428 [12,7,9]
+ CRUSH rule 0 x 429 [3,4,9]
+ CRUSH rule 0 x 430 [3,5,10]
+ CRUSH rule 0 x 431 [9,3,7]
+ CRUSH rule 0 x 432 [4,1,12]
+ CRUSH rule 0 x 433 [4,11,12]
+ CRUSH rule 0 x 434 [2,14,9]
+ CRUSH rule 0 x 435 [13,11,5]
+ CRUSH rule 0 x 436 [9,15,10]
+ CRUSH rule 0 x 437 [9,6,3]
+ CRUSH rule 0 x 438 [7,2,13]
+ CRUSH rule 0 x 439 [7,14,4]
+ CRUSH rule 0 x 440 [14,11,9]
+ CRUSH rule 0 x 441 [2,4,11]
+ CRUSH rule 0 x 442 [10,13,9]
+ CRUSH rule 0 x 443 [12,15,10]
+ CRUSH rule 0 x 444 [4,13,7]
+ CRUSH rule 0 x 445 [4,2,15]
+ CRUSH rule 0 x 446 [12,10,6]
+ CRUSH rule 0 x 447 [15,7,13]
+ CRUSH rule 0 x 448 [5,2,13]
+ CRUSH rule 0 x 449 [14,5,3]
+ CRUSH rule 0 x 450 [2,4,6]
+ CRUSH rule 0 x 451 [6,14,11]
+ CRUSH rule 0 x 452 [14,9,10]
+ CRUSH rule 0 x 453 [5,15,13]
+ CRUSH rule 0 x 454 [10,4,2]
+ CRUSH rule 0 x 455 [6,13,2]
+ CRUSH rule 0 x 456 [5,7,13]
+ CRUSH rule 0 x 457 [9,1,5]
+ CRUSH rule 0 x 458 [9,11,15]
+ CRUSH rule 0 x 459 [13,15,11]
+ CRUSH rule 0 x 460 [5,12,10]
+ CRUSH rule 0 x 461 [4,3,9]
+ CRUSH rule 0 x 462 [4,7,12]
+ CRUSH rule 0 x 463 [4,12,14]
+ CRUSH rule 0 x 464 [4,2,15]
+ CRUSH rule 0 x 465 [5,10,9]
+ CRUSH rule 0 x 466 [13,5,2]
+ CRUSH rule 0 x 467 [13,6,14]
+ CRUSH rule 0 x 468 [10,7,12]
+ CRUSH rule 0 x 469 [4,9,6]
+ CRUSH rule 0 x 470 [3,9,12]
+ CRUSH rule 0 x 471 [6,1,5]
+ CRUSH rule 0 x 472 [2,14,7]
+ CRUSH rule 0 x 473 [15,10,6]
+ CRUSH rule 0 x 474 [15,10,4]
+ CRUSH rule 0 x 475 [10,5,12]
+ CRUSH rule 0 x 476 [3,6,10]
+ CRUSH rule 0 x 477 [6,13,5]
+ CRUSH rule 0 x 478 [4,15,1]
+ CRUSH rule 0 x 479 [13,11,1]
+ CRUSH rule 0 x 480 [1,13,6]
+ CRUSH rule 0 x 481 [15,12,7]
+ CRUSH rule 0 x 482 [2,12,9]
+ CRUSH rule 0 x 483 [10,1,4]
+ CRUSH rule 0 x 484 [1,4,10]
+ CRUSH rule 0 x 485 [9,4,3]
+ CRUSH rule 0 x 486 [3,10,15]
+ CRUSH rule 0 x 487 [12,11,4]
+ CRUSH rule 0 x 488 [14,4,1]
+ CRUSH rule 0 x 489 [11,4,2]
+ CRUSH rule 0 x 490 [4,9,1]
+ CRUSH rule 0 x 491 [1,12,5]
+ CRUSH rule 0 x 492 [5,7,11]
+ CRUSH rule 0 x 493 [12,1,4]
+ CRUSH rule 0 x 494 [1,7,13]
+ CRUSH rule 0 x 495 [3,15,7]
+ CRUSH rule 0 x 496 [5,3,7]
+ CRUSH rule 0 x 497 [13,10,3]
+ CRUSH rule 0 x 498 [10,6,1]
+ CRUSH rule 0 x 499 [14,3,12]
+ CRUSH rule 0 x 500 [15,9,6]
+ CRUSH rule 0 x 501 [10,13,1]
+ CRUSH rule 0 x 502 [5,1,14]
+ CRUSH rule 0 x 503 [15,10,7]
+ CRUSH rule 0 x 504 [13,2,7]
+ CRUSH rule 0 x 505 [12,7,5]
+ CRUSH rule 0 x 506 [11,7,9]
+ CRUSH rule 0 x 507 [4,14,13]
+ CRUSH rule 0 x 508 [12,1,4]
+ CRUSH rule 0 x 509 [4,2,6]
+ CRUSH rule 0 x 510 [5,3,1]
+ CRUSH rule 0 x 511 [2,12,10]
+ CRUSH rule 0 x 512 [15,11,3]
+ CRUSH rule 0 x 513 [4,9,11]
+ CRUSH rule 0 x 514 [11,9,3]
+ CRUSH rule 0 x 515 [12,14,6]
+ CRUSH rule 0 x 516 [14,11,1]
+ CRUSH rule 0 x 517 [11,5,6]
+ CRUSH rule 0 x 518 [3,5,7]
+ CRUSH rule 0 x 519 [12,14,2]
+ CRUSH rule 0 x 520 [12,4,2]
+ CRUSH rule 0 x 521 [11,5,9]
+ CRUSH rule 0 x 522 [4,12,11]
+ CRUSH rule 0 x 523 [3,1,5]
+ CRUSH rule 0 x 524 [15,9,3]
+ CRUSH rule 0 x 525 [3,15,11]
+ CRUSH rule 0 x 526 [10,2,5]
+ CRUSH rule 0 x 527 [3,13,4]
+ CRUSH rule 0 x 528 [12,7,15]
+ CRUSH rule 0 x 529 [6,4,10]
+ CRUSH rule 0 x 530 [11,9,12]
+ CRUSH rule 0 x 531 [9,15,4]
+ CRUSH rule 0 x 532 [5,3,13]
+ CRUSH rule 0 x 533 [12,15,1]
+ CRUSH rule 0 x 534 [11,9,3]
+ CRUSH rule 0 x 535 [11,1,3]
+ CRUSH rule 0 x 536 [9,1,14]
+ CRUSH rule 0 x 537 [15,5,13]
+ CRUSH rule 0 x 538 [13,5,11]
+ CRUSH rule 0 x 539 [10,12,6]
+ CRUSH rule 0 x 540 [12,15,7]
+ CRUSH rule 0 x 541 [2,1,6]
+ CRUSH rule 0 x 542 [3,9,15]
+ CRUSH rule 0 x 543 [4,10,9]
+ CRUSH rule 0 x 544 [3,15,9]
+ CRUSH rule 0 x 545 [14,10,7]
+ CRUSH rule 0 x 546 [5,15,13]
+ CRUSH rule 0 x 547 [5,13,7]
+ CRUSH rule 0 x 548 [11,7,12]
+ CRUSH rule 0 x 549 [14,1,4]
+ CRUSH rule 0 x 550 [9,15,3]
+ CRUSH rule 0 x 551 [11,2,15]
+ CRUSH rule 0 x 552 [2,11,14]
+ CRUSH rule 0 x 553 [11,9,14]
+ CRUSH rule 0 x 554 [11,14,6]
+ CRUSH rule 0 x 555 [6,5,10]
+ CRUSH rule 0 x 556 [15,6,3]
+ CRUSH rule 0 x 557 [12,2,5]
+ CRUSH rule 0 x 558 [12,1,6]
+ CRUSH rule 0 x 559 [2,13,5]
+ CRUSH rule 0 x 560 [4,9,12]
+ CRUSH rule 0 x 561 [12,7,1]
+ CRUSH rule 0 x 562 [7,13,9]
+ CRUSH rule 0 x 563 [15,4,3]
+ CRUSH rule 0 x 564 [2,13,7]
+ CRUSH rule 0 x 565 [3,12,4]
+ CRUSH rule 0 x 566 [6,14,4]
+ CRUSH rule 0 x 567 [15,4,11]
+ CRUSH rule 0 x 568 [4,14,1]
+ CRUSH rule 0 x 569 [11,3,15]
+ CRUSH rule 0 x 570 [1,10,13]
+ CRUSH rule 0 x 571 [10,12,14]
+ CRUSH rule 0 x 572 [12,14,3]
+ CRUSH rule 0 x 573 [7,15,11]
+ CRUSH rule 0 x 574 [11,14,13]
+ CRUSH rule 0 x 575 [5,13,15]
+ CRUSH rule 0 x 576 [3,15,11]
+ CRUSH rule 0 x 577 [13,9,6]
+ CRUSH rule 0 x 578 [4,10,1]
+ CRUSH rule 0 x 579 [13,1,15]
+ CRUSH rule 0 x 580 [3,12,4]
+ CRUSH rule 0 x 581 [7,14,12]
+ CRUSH rule 0 x 582 [10,5,13]
+ CRUSH rule 0 x 583 [4,15,1]
+ CRUSH rule 0 x 584 [10,1,5]
+ CRUSH rule 0 x 585 [5,3,6]
+ CRUSH rule 0 x 586 [7,10,14]
+ CRUSH rule 0 x 587 [11,6,9]
+ CRUSH rule 0 x 588 [3,12,7]
+ CRUSH rule 0 x 589 [9,7,12]
+ CRUSH rule 0 x 590 [12,1,3]
+ CRUSH rule 0 x 591 [2,6,14]
+ CRUSH rule 0 x 592 [15,12,9]
+ CRUSH rule 0 x 593 [13,14,5]
+ CRUSH rule 0 x 594 [12,14,2]
+ CRUSH rule 0 x 595 [12,7,10]
+ CRUSH rule 0 x 596 [2,7,12]
+ CRUSH rule 0 x 597 [15,1,2]
+ CRUSH rule 0 x 598 [11,5,9]
+ CRUSH rule 0 x 599 [13,11,1]
+ CRUSH rule 0 x 600 [4,12,3]
+ CRUSH rule 0 x 601 [13,5,15]
+ CRUSH rule 0 x 602 [3,11,7]
+ CRUSH rule 0 x 603 [3,1,4]
+ CRUSH rule 0 x 604 [14,2,6]
+ CRUSH rule 0 x 605 [2,7,12]
+ CRUSH rule 0 x 606 [12,15,1]
+ CRUSH rule 0 x 607 [3,9,10]
+ CRUSH rule 0 x 608 [13,10,1]
+ CRUSH rule 0 x 609 [14,3,7]
+ CRUSH rule 0 x 610 [7,10,5]
+ CRUSH rule 0 x 611 [13,1,5]
+ CRUSH rule 0 x 612 [7,1,2]
+ CRUSH rule 0 x 613 [10,7,14]
+ CRUSH rule 0 x 614 [9,4,15]
+ CRUSH rule 0 x 615 [9,4,11]
+ CRUSH rule 0 x 616 [10,14,1]
+ CRUSH rule 0 x 617 [15,7,2]
+ CRUSH rule 0 x 618 [4,2,10]
+ CRUSH rule 0 x 619 [15,4,3]
+ CRUSH rule 0 x 620 [3,7,11]
+ CRUSH rule 0 x 621 [3,6,4]
+ CRUSH rule 0 x 622 [10,2,13]
+ CRUSH rule 0 x 623 [4,9,14]
+ CRUSH rule 0 x 624 [3,9,15]
+ CRUSH rule 0 x 625 [11,7,3]
+ CRUSH rule 0 x 626 [10,12,2]
+ CRUSH rule 0 x 627 [1,12,10]
+ CRUSH rule 0 x 628 [15,13,11]
+ CRUSH rule 0 x 629 [5,6,15]
+ CRUSH rule 0 x 630 [1,4,12]
+ CRUSH rule 0 x 631 [5,7,1]
+ CRUSH rule 0 x 632 [12,3,11]
+ CRUSH rule 0 x 633 [14,4,3]
+ CRUSH rule 0 x 634 [6,9,5]
+ CRUSH rule 0 x 635 [6,5,2]
+ CRUSH rule 0 x 636 [13,6,11]
+ CRUSH rule 0 x 637 [3,1,10]
+ CRUSH rule 0 x 638 [10,15,3]
+ CRUSH rule 0 x 639 [6,9,14]
+ CRUSH rule 0 x 640 [9,6,1]
+ CRUSH rule 0 x 641 [10,6,5]
+ CRUSH rule 0 x 642 [1,15,4]
+ CRUSH rule 0 x 643 [3,7,5]
+ CRUSH rule 0 x 644 [15,13,6]
+ CRUSH rule 0 x 645 [14,2,4]
+ CRUSH rule 0 x 646 [5,13,14]
+ CRUSH rule 0 x 647 [10,1,9]
+ CRUSH rule 0 x 648 [6,5,2]
+ CRUSH rule 0 x 649 [3,9,13]
+ CRUSH rule 0 x 650 [10,9,4]
+ CRUSH rule 0 x 651 [3,9,5]
+ CRUSH rule 0 x 652 [15,9,4]
+ CRUSH rule 0 x 653 [11,14,1]
+ CRUSH rule 0 x 654 [13,6,2]
+ CRUSH rule 0 x 655 [6,3,4]
+ CRUSH rule 0 x 656 [3,15,1]
+ CRUSH rule 0 x 657 [11,15,3]
+ CRUSH rule 0 x 658 [7,2,10]
+ CRUSH rule 0 x 659 [2,5,14]
+ CRUSH rule 0 x 660 [13,14,10]
+ CRUSH rule 0 x 661 [7,15,3]
+ CRUSH rule 0 x 662 [15,2,12]
+ CRUSH rule 0 x 663 [14,9,13]
+ CRUSH rule 0 x 664 [6,10,12]
+ CRUSH rule 0 x 665 [2,9,12]
+ CRUSH rule 0 x 666 [12,3,6]
+ CRUSH rule 0 x 667 [1,9,12]
+ CRUSH rule 0 x 668 [9,5,1]
+ CRUSH rule 0 x 669 [9,7,14]
+ CRUSH rule 0 x 670 [6,10,9]
+ CRUSH rule 0 x 671 [6,15,5]
+ CRUSH rule 0 x 672 [2,9,13]
+ CRUSH rule 0 x 673 [7,10,5]
+ CRUSH rule 0 x 674 [7,12,10]
+ CRUSH rule 0 x 675 [9,5,1]
+ CRUSH rule 0 x 676 [10,12,2]
+ CRUSH rule 0 x 677 [2,12,1]
+ CRUSH rule 0 x 678 [1,2,4]
+ CRUSH rule 0 x 679 [5,6,12]
+ CRUSH rule 0 x 680 [7,11,3]
+ CRUSH rule 0 x 681 [6,4,3]
+ CRUSH rule 0 x 682 [6,1,11]
+ CRUSH rule 0 x 683 [6,13,2]
+ CRUSH rule 0 x 684 [9,11,3]
+ CRUSH rule 0 x 685 [5,1,15]
+ CRUSH rule 0 x 686 [1,9,11]
+ CRUSH rule 0 x 687 [7,13,3]
+ CRUSH rule 0 x 688 [11,9,1]
+ CRUSH rule 0 x 689 [5,2,9]
+ CRUSH rule 0 x 690 [9,7,10]
+ CRUSH rule 0 x 691 [11,15,9]
+ CRUSH rule 0 x 692 [15,5,1]
+ CRUSH rule 0 x 693 [5,6,12]
+ CRUSH rule 0 x 694 [4,7,1]
+ CRUSH rule 0 x 695 [6,13,14]
+ CRUSH rule 0 x 696 [1,2,4]
+ CRUSH rule 0 x 697 [13,11,3]
+ CRUSH rule 0 x 698 [11,13,4]
+ CRUSH rule 0 x 699 [7,14,12]
+ CRUSH rule 0 x 700 [12,14,11]
+ CRUSH rule 0 x 701 [3,13,1]
+ CRUSH rule 0 x 702 [3,12,15]
+ CRUSH rule 0 x 703 [15,11,13]
+ CRUSH rule 0 x 704 [6,4,2]
+ CRUSH rule 0 x 705 [14,6,11]
+ CRUSH rule 0 x 706 [1,12,3]
+ CRUSH rule 0 x 707 [4,7,14]
+ CRUSH rule 0 x 708 [3,10,5]
+ CRUSH rule 0 x 709 [11,12,3]
+ CRUSH rule 0 x 710 [14,2,11]
+ CRUSH rule 0 x 711 [14,3,9]
+ CRUSH rule 0 x 712 [12,3,11]
+ CRUSH rule 0 x 713 [11,9,3]
+ CRUSH rule 0 x 714 [12,1,9]
+ CRUSH rule 0 x 715 [6,1,14]
+ CRUSH rule 0 x 716 [11,13,9]
+ CRUSH rule 0 x 717 [12,4,10]
+ CRUSH rule 0 x 718 [7,15,5]
+ CRUSH rule 0 x 719 [5,15,13]
+ CRUSH rule 0 x 720 [4,13,10]
+ CRUSH rule 0 x 721 [11,3,14]
+ CRUSH rule 0 x 722 [2,4,6]
+ CRUSH rule 0 x 723 [2,1,12]
+ CRUSH rule 0 x 724 [7,1,9]
+ CRUSH rule 0 x 725 [11,12,7]
+ CRUSH rule 0 x 726 [7,14,4]
+ CRUSH rule 0 x 727 [2,5,1]
+ CRUSH rule 0 x 728 [13,11,4]
+ CRUSH rule 0 x 729 [15,11,4]
+ CRUSH rule 0 x 730 [3,7,1]
+ CRUSH rule 0 x 731 [9,1,6]
+ CRUSH rule 0 x 732 [1,2,10]
+ CRUSH rule 0 x 733 [11,3,5]
+ CRUSH rule 0 x 734 [14,3,11]
+ CRUSH rule 0 x 735 [6,9,2]
+ CRUSH rule 0 x 736 [3,9,1]
+ CRUSH rule 0 x 737 [1,4,2]
+ CRUSH rule 0 x 738 [11,15,7]
+ CRUSH rule 0 x 739 [11,12,6]
+ CRUSH rule 0 x 740 [7,9,10]
+ CRUSH rule 0 x 741 [12,11,7]
+ CRUSH rule 0 x 742 [9,7,4]
+ CRUSH rule 0 x 743 [5,13,9]
+ CRUSH rule 0 x 744 [6,2,13]
+ CRUSH rule 0 x 745 [3,6,1]
+ CRUSH rule 0 x 746 [3,7,9]
+ CRUSH rule 0 x 747 [15,11,5]
+ CRUSH rule 0 x 748 [6,10,13]
+ CRUSH rule 0 x 749 [14,9,10]
+ CRUSH rule 0 x 750 [1,14,6]
+ CRUSH rule 0 x 751 [15,1,6]
+ CRUSH rule 0 x 752 [13,1,7]
+ CRUSH rule 0 x 753 [4,11,1]
+ CRUSH rule 0 x 754 [14,12,11]
+ CRUSH rule 0 x 755 [13,6,1]
+ CRUSH rule 0 x 756 [3,4,14]
+ CRUSH rule 0 x 757 [10,6,1]
+ CRUSH rule 0 x 758 [6,3,4]
+ CRUSH rule 0 x 759 [5,7,3]
+ CRUSH rule 0 x 760 [1,15,10]
+ CRUSH rule 0 x 761 [2,12,1]
+ CRUSH rule 0 x 762 [1,4,10]
+ CRUSH rule 0 x 763 [4,13,1]
+ CRUSH rule 0 x 764 [1,14,6]
+ CRUSH rule 0 x 765 [9,15,2]
+ CRUSH rule 0 x 766 [11,2,7]
+ CRUSH rule 0 x 767 [6,11,4]
+ CRUSH rule 0 x 768 [2,12,15]
+ CRUSH rule 0 x 769 [15,1,9]
+ CRUSH rule 0 x 770 [15,13,4]
+ CRUSH rule 0 x 771 [9,2,12]
+ CRUSH rule 0 x 772 [4,3,13]
+ CRUSH rule 0 x 773 [3,7,4]
+ CRUSH rule 0 x 774 [12,6,3]
+ CRUSH rule 0 x 775 [5,10,14]
+ CRUSH rule 0 x 776 [10,15,3]
+ CRUSH rule 0 x 777 [11,13,4]
+ CRUSH rule 0 x 778 [13,1,9]
+ CRUSH rule 0 x 779 [5,11,1]
+ CRUSH rule 0 x 780 [13,9,3]
+ CRUSH rule 0 x 781 [5,7,14]
+ CRUSH rule 0 x 782 [2,15,9]
+ CRUSH rule 0 x 783 [12,7,5]
+ CRUSH rule 0 x 784 [14,1,10]
+ CRUSH rule 0 x 785 [6,12,1]
+ CRUSH rule 0 x 786 [10,5,2]
+ CRUSH rule 0 x 787 [1,12,10]
+ CRUSH rule 0 x 788 [4,2,9]
+ CRUSH rule 0 x 789 [9,2,14]
+ CRUSH rule 0 x 790 [15,2,7]
+ CRUSH rule 0 x 791 [9,4,7]
+ CRUSH rule 0 x 792 [6,4,15]
+ CRUSH rule 0 x 793 [15,9,6]
+ CRUSH rule 0 x 794 [5,12,2]
+ CRUSH rule 0 x 795 [6,14,12]
+ CRUSH rule 0 x 796 [11,2,12]
+ CRUSH rule 0 x 797 [14,3,7]
+ CRUSH rule 0 x 798 [5,11,6]
+ CRUSH rule 0 x 799 [2,9,14]
+ CRUSH rule 0 x 800 [6,3,4]
+ CRUSH rule 0 x 801 [2,5,6]
+ CRUSH rule 0 x 802 [1,4,12]
+ CRUSH rule 0 x 803 [7,2,4]
+ CRUSH rule 0 x 804 [5,14,9]
+ CRUSH rule 0 x 805 [13,4,3]
+ CRUSH rule 0 x 806 [6,2,13]
+ CRUSH rule 0 x 807 [14,2,7]
+ CRUSH rule 0 x 808 [2,15,12]
+ CRUSH rule 0 x 809 [1,11,7]
+ CRUSH rule 0 x 810 [2,5,9]
+ CRUSH rule 0 x 811 [15,6,3]
+ CRUSH rule 0 x 812 [7,11,2]
+ CRUSH rule 0 x 813 [4,10,13]
+ CRUSH rule 0 x 814 [13,4,9]
+ CRUSH rule 0 x 815 [15,12,9]
+ CRUSH rule 0 x 816 [14,10,13]
+ CRUSH rule 0 x 817 [10,7,2]
+ CRUSH rule 0 x 818 [15,2,11]
+ CRUSH rule 0 x 819 [5,12,10]
+ CRUSH rule 0 x 820 [3,6,9]
+ CRUSH rule 0 x 821 [15,10,9]
+ CRUSH rule 0 x 822 [10,13,2]
+ CRUSH rule 0 x 823 [2,6,12]
+ CRUSH rule 0 x 824 [3,7,9]
+ CRUSH rule 0 x 825 [10,5,14]
+ CRUSH rule 0 x 826 [5,2,11]
+ CRUSH rule 0 x 827 [13,5,1]
+ CRUSH rule 0 x 828 [12,6,10]
+ CRUSH rule 0 x 829 [13,6,15]
+ CRUSH rule 0 x 830 [15,13,2]
+ CRUSH rule 0 x 831 [1,4,11]
+ CRUSH rule 0 x 832 [14,11,13]
+ CRUSH rule 0 x 833 [9,13,3]
+ CRUSH rule 0 x 834 [9,7,5]
+ CRUSH rule 0 x 835 [14,3,13]
+ CRUSH rule 0 x 836 [3,9,10]
+ CRUSH rule 0 x 837 [15,12,11]
+ CRUSH rule 0 x 838 [12,14,9]
+ CRUSH rule 0 x 839 [3,4,6]
+ CRUSH rule 0 x 840 [10,15,12]
+ CRUSH rule 0 x 841 [3,5,7]
+ CRUSH rule 0 x 842 [9,13,2]
+ CRUSH rule 0 x 843 [14,7,4]
+ CRUSH rule 0 x 844 [7,1,4]
+ CRUSH rule 0 x 845 [13,6,1]
+ CRUSH rule 0 x 846 [3,7,15]
+ CRUSH rule 0 x 847 [12,15,11]
+ CRUSH rule 0 x 848 [11,13,1]
+ CRUSH rule 0 x 849 [3,15,11]
+ CRUSH rule 0 x 850 [1,3,10]
+ CRUSH rule 0 x 851 [14,4,3]
+ CRUSH rule 0 x 852 [9,12,4]
+ CRUSH rule 0 x 853 [13,14,6]
+ CRUSH rule 0 x 854 [7,11,12]
+ CRUSH rule 0 x 855 [14,4,12]
+ CRUSH rule 0 x 856 [5,10,7]
+ CRUSH rule 0 x 857 [4,3,13]
+ CRUSH rule 0 x 858 [5,15,6]
+ CRUSH rule 0 x 859 [5,15,6]
+ CRUSH rule 0 x 860 [11,14,1]
+ CRUSH rule 0 x 861 [13,7,4]
+ CRUSH rule 0 x 862 [5,10,9]
+ CRUSH rule 0 x 863 [11,6,3]
+ CRUSH rule 0 x 864 [6,13,4]
+ CRUSH rule 0 x 865 [4,1,14]
+ CRUSH rule 0 x 866 [2,13,4]
+ CRUSH rule 0 x 867 [12,2,9]
+ CRUSH rule 0 x 868 [14,11,7]
+ CRUSH rule 0 x 869 [10,13,7]
+ CRUSH rule 0 x 870 [14,9,11]
+ CRUSH rule 0 x 871 [6,2,1]
+ CRUSH rule 0 x 872 [6,1,15]
+ CRUSH rule 0 x 873 [2,5,12]
+ CRUSH rule 0 x 874 [12,4,7]
+ CRUSH rule 0 x 875 [10,6,14]
+ CRUSH rule 0 x 876 [14,7,13]
+ CRUSH rule 0 x 877 [15,11,13]
+ CRUSH rule 0 x 878 [7,14,3]
+ CRUSH rule 0 x 879 [12,2,7]
+ CRUSH rule 0 x 880 [2,12,10]
+ CRUSH rule 0 x 881 [6,3,1]
+ CRUSH rule 0 x 882 [11,13,7]
+ CRUSH rule 0 x 883 [13,1,3]
+ CRUSH rule 0 x 884 [6,15,4]
+ CRUSH rule 0 x 885 [14,7,9]
+ CRUSH rule 0 x 886 [13,11,4]
+ CRUSH rule 0 x 887 [14,4,12]
+ CRUSH rule 0 x 888 [10,12,7]
+ CRUSH rule 0 x 889 [15,13,4]
+ CRUSH rule 0 x 890 [10,12,14]
+ CRUSH rule 0 x 891 [9,5,11]
+ CRUSH rule 0 x 892 [12,15,2]
+ CRUSH rule 0 x 893 [1,3,5]
+ CRUSH rule 0 x 894 [7,2,11]
+ CRUSH rule 0 x 895 [2,1,11]
+ CRUSH rule 0 x 896 [9,1,14]
+ CRUSH rule 0 x 897 [7,5,14]
+ CRUSH rule 0 x 898 [10,6,12]
+ CRUSH rule 0 x 899 [1,11,5]
+ CRUSH rule 0 x 900 [2,9,10]
+ CRUSH rule 0 x 901 [9,12,11]
+ CRUSH rule 0 x 902 [4,2,6]
+ CRUSH rule 0 x 903 [14,10,3]
+ CRUSH rule 0 x 904 [15,12,4]
+ CRUSH rule 0 x 905 [12,6,11]
+ CRUSH rule 0 x 906 [14,11,12]
+ CRUSH rule 0 x 907 [7,12,3]
+ CRUSH rule 0 x 908 [2,15,9]
+ CRUSH rule 0 x 909 [10,14,1]
+ CRUSH rule 0 x 910 [12,7,4]
+ CRUSH rule 0 x 911 [11,15,2]
+ CRUSH rule 0 x 912 [6,4,14]
+ CRUSH rule 0 x 913 [4,6,10]
+ CRUSH rule 0 x 914 [4,15,2]
+ CRUSH rule 0 x 915 [12,14,1]
+ CRUSH rule 0 x 916 [3,1,11]
+ CRUSH rule 0 x 917 [1,15,6]
+ CRUSH rule 0 x 918 [7,14,11]
+ CRUSH rule 0 x 919 [10,7,3]
+ CRUSH rule 0 x 920 [4,2,10]
+ CRUSH rule 0 x 921 [1,11,6]
+ CRUSH rule 0 x 922 [6,4,14]
+ CRUSH rule 0 x 923 [12,2,5]
+ CRUSH rule 0 x 924 [6,2,14]
+ CRUSH rule 0 x 925 [12,15,2]
+ CRUSH rule 0 x 926 [3,13,10]
+ CRUSH rule 0 x 927 [6,5,1]
+ CRUSH rule 0 x 928 [13,1,3]
+ CRUSH rule 0 x 929 [10,7,1]
+ CRUSH rule 0 x 930 [7,15,10]
+ CRUSH rule 0 x 931 [6,15,11]
+ CRUSH rule 0 x 932 [13,2,5]
+ CRUSH rule 0 x 933 [12,7,14]
+ CRUSH rule 0 x 934 [12,2,5]
+ CRUSH rule 0 x 935 [6,11,1]
+ CRUSH rule 0 x 936 [9,12,7]
+ CRUSH rule 0 x 937 [14,2,11]
+ CRUSH rule 0 x 938 [14,3,5]
+ CRUSH rule 0 x 939 [6,4,14]
+ CRUSH rule 0 x 940 [13,11,4]
+ CRUSH rule 0 x 941 [3,12,4]
+ CRUSH rule 0 x 942 [15,12,10]
+ CRUSH rule 0 x 943 [10,2,4]
+ CRUSH rule 0 x 944 [2,9,4]
+ CRUSH rule 0 x 945 [10,15,2]
+ CRUSH rule 0 x 946 [11,15,7]
+ CRUSH rule 0 x 947 [11,3,14]
+ CRUSH rule 0 x 948 [7,13,11]
+ CRUSH rule 0 x 949 [9,1,12]
+ CRUSH rule 0 x 950 [9,15,13]
+ CRUSH rule 0 x 951 [2,6,12]
+ CRUSH rule 0 x 952 [9,7,15]
+ CRUSH rule 0 x 953 [1,3,6]
+ CRUSH rule 0 x 954 [10,2,14]
+ CRUSH rule 0 x 955 [7,14,3]
+ CRUSH rule 0 x 956 [1,6,11]
+ CRUSH rule 0 x 957 [14,11,1]
+ CRUSH rule 0 x 958 [15,4,3]
+ CRUSH rule 0 x 959 [2,1,12]
+ CRUSH rule 0 x 960 [2,6,11]
+ CRUSH rule 0 x 961 [3,13,11]
+ CRUSH rule 0 x 962 [5,11,3]
+ CRUSH rule 0 x 963 [13,10,15]
+ CRUSH rule 0 x 964 [7,11,4]
+ CRUSH rule 0 x 965 [12,2,9]
+ CRUSH rule 0 x 966 [12,14,9]
+ CRUSH rule 0 x 967 [7,5,3]
+ CRUSH rule 0 x 968 [12,15,4]
+ CRUSH rule 0 x 969 [11,4,7]
+ CRUSH rule 0 x 970 [5,12,10]
+ CRUSH rule 0 x 971 [1,9,4]
+ CRUSH rule 0 x 972 [12,3,14]
+ CRUSH rule 0 x 973 [1,10,4]
+ CRUSH rule 0 x 974 [7,11,1]
+ CRUSH rule 0 x 975 [7,9,15]
+ CRUSH rule 0 x 976 [7,3,15]
+ CRUSH rule 0 x 977 [14,3,6]
+ CRUSH rule 0 x 978 [12,5,11]
+ CRUSH rule 0 x 979 [5,1,13]
+ CRUSH rule 0 x 980 [15,11,5]
+ CRUSH rule 0 x 981 [5,11,15]
+ CRUSH rule 0 x 982 [2,6,14]
+ CRUSH rule 0 x 983 [3,12,10]
+ CRUSH rule 0 x 984 [15,13,1]
+ CRUSH rule 0 x 985 [11,2,15]
+ CRUSH rule 0 x 986 [6,13,9]
+ CRUSH rule 0 x 987 [13,14,5]
+ CRUSH rule 0 x 988 [12,9,10]
+ CRUSH rule 0 x 989 [7,4,3]
+ CRUSH rule 0 x 990 [1,10,9]
+ CRUSH rule 0 x 991 [7,11,1]
+ CRUSH rule 0 x 992 [9,10,2]
+ CRUSH rule 0 x 993 [6,10,14]
+ CRUSH rule 0 x 994 [3,13,15]
+ CRUSH rule 0 x 995 [15,6,12]
+ CRUSH rule 0 x 996 [15,10,5]
+ CRUSH rule 0 x 997 [15,2,1]
+ CRUSH rule 0 x 998 [6,1,9]
+ CRUSH rule 0 x 999 [9,10,15]
+ CRUSH rule 0 x 1000 [14,2,9]
+ CRUSH rule 0 x 1001 [11,14,4]
+ CRUSH rule 0 x 1002 [1,10,14]
+ CRUSH rule 0 x 1003 [10,7,5]
+ CRUSH rule 0 x 1004 [15,1,4]
+ CRUSH rule 0 x 1005 [6,12,2]
+ CRUSH rule 0 x 1006 [10,12,15]
+ CRUSH rule 0 x 1007 [1,7,13]
+ CRUSH rule 0 x 1008 [7,4,9]
+ CRUSH rule 0 x 1009 [5,2,11]
+ CRUSH rule 0 x 1010 [10,2,15]
+ CRUSH rule 0 x 1011 [6,3,12]
+ CRUSH rule 0 x 1012 [12,6,9]
+ CRUSH rule 0 x 1013 [2,14,12]
+ CRUSH rule 0 x 1014 [1,13,7]
+ CRUSH rule 0 x 1015 [12,6,10]
+ CRUSH rule 0 x 1016 [10,13,14]
+ CRUSH rule 0 x 1017 [5,11,14]
+ CRUSH rule 0 x 1018 [13,11,14]
+ CRUSH rule 0 x 1019 [10,13,14]
+ CRUSH rule 0 x 1020 [3,1,13]
+ CRUSH rule 0 x 1021 [2,11,14]
+ CRUSH rule 0 x 1022 [15,5,7]
+ CRUSH rule 0 x 1023 [15,2,9]
+ rule 0 (replicated_ruleset) num_rep 3 result size == 3:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3,15]
+ CRUSH rule 0 x 1 [10,15,1,2]
+ CRUSH rule 0 x 2 [1,12,2,6]
+ CRUSH rule 0 x 3 [15,4,10,2]
+ CRUSH rule 0 x 4 [14,2,10,1]
+ CRUSH rule 0 x 5 [7,4,11,2]
+ CRUSH rule 0 x 6 [12,6,10,9]
+ CRUSH rule 0 x 7 [9,2,6,12]
+ CRUSH rule 0 x 8 [10,2,15,1]
+ CRUSH rule 0 x 9 [7,1,14,2]
+ CRUSH rule 0 x 10 [10,14,4,1]
+ CRUSH rule 0 x 11 [13,9,14,7]
+ CRUSH rule 0 x 12 [7,1,2,5]
+ CRUSH rule 0 x 13 [3,5,12,7]
+ CRUSH rule 0 x 14 [13,5,2,7]
+ CRUSH rule 0 x 15 [15,1,9,6]
+ CRUSH rule 0 x 16 [7,11,14,2]
+ CRUSH rule 0 x 17 [10,1,13,2]
+ CRUSH rule 0 x 18 [1,7,3,10]
+ CRUSH rule 0 x 19 [7,12,2,4]
+ CRUSH rule 0 x 20 [14,12,3,10]
+ CRUSH rule 0 x 21 [3,12,1,10]
+ CRUSH rule 0 x 22 [6,3,13,11]
+ CRUSH rule 0 x 23 [10,5,13,9]
+ CRUSH rule 0 x 24 [12,11,3,1]
+ CRUSH rule 0 x 25 [7,12,15,1]
+ CRUSH rule 0 x 26 [1,7,13,2]
+ CRUSH rule 0 x 27 [3,6,15,4]
+ CRUSH rule 0 x 28 [14,4,3,9]
+ CRUSH rule 0 x 29 [5,14,12,11]
+ CRUSH rule 0 x 30 [2,5,6,9]
+ CRUSH rule 0 x 31 [5,15,10,1]
+ CRUSH rule 0 x 32 [9,10,2,1]
+ CRUSH rule 0 x 33 [13,4,9,2]
+ CRUSH rule 0 x 34 [13,15,2,4]
+ CRUSH rule 0 x 35 [4,14,3,13]
+ CRUSH rule 0 x 36 [3,12,9,7]
+ CRUSH rule 0 x 37 [9,2,6,14]
+ CRUSH rule 0 x 38 [3,4,13,10]
+ CRUSH rule 0 x 39 [12,7,14,11]
+ CRUSH rule 0 x 40 [10,1,9,5]
+ CRUSH rule 0 x 41 [4,9,11,1]
+ CRUSH rule 0 x 42 [3,6,14,10]
+ CRUSH rule 0 x 43 [10,5,15,7]
+ CRUSH rule 0 x 44 [11,4,13,3]
+ CRUSH rule 0 x 45 [11,12,15,9]
+ CRUSH rule 0 x 46 [6,9,2,14]
+ CRUSH rule 0 x 47 [3,9,6,4]
+ CRUSH rule 0 x 48 [4,6,2,1]
+ CRUSH rule 0 x 49 [9,15,10,7]
+ CRUSH rule 0 x 50 [14,12,1,4]
+ CRUSH rule 0 x 51 [10,6,5,12]
+ CRUSH rule 0 x 52 [12,1,9,11]
+ CRUSH rule 0 x 53 [3,6,13,9]
+ CRUSH rule 0 x 54 [4,13,9,2]
+ CRUSH rule 0 x 55 [4,11,2,7]
+ CRUSH rule 0 x 56 [5,9,10,1]
+ CRUSH rule 0 x 57 [6,2,1,15]
+ CRUSH rule 0 x 58 [7,1,11,4]
+ CRUSH rule 0 x 59 [2,13,1,10]
+ CRUSH rule 0 x 60 [3,6,11,1]
+ CRUSH rule 0 x 61 [3,15,13,7]
+ CRUSH rule 0 x 62 [15,11,7,12]
+ CRUSH rule 0 x 63 [10,14,12,1]
+ CRUSH rule 0 x 64 [3,9,1,4]
+ CRUSH rule 0 x 65 [4,12,11,7]
+ CRUSH rule 0 x 66 [15,11,6,9]
+ CRUSH rule 0 x 67 [2,6,4,14]
+ CRUSH rule 0 x 68 [15,7,4,2]
+ CRUSH rule 0 x 69 [2,1,15,10]
+ CRUSH rule 0 x 70 [9,6,1,3]
+ CRUSH rule 0 x 71 [15,5,1,3]
+ CRUSH rule 0 x 72 [9,10,3,5]
+ CRUSH rule 0 x 73 [5,3,11,1]
+ CRUSH rule 0 x 74 [11,7,9,5]
+ CRUSH rule 0 x 75 [9,7,11,14]
+ CRUSH rule 0 x 76 [6,1,3,5]
+ CRUSH rule 0 x 77 [7,4,2,13]
+ CRUSH rule 0 x 78 [9,3,1,5]
+ CRUSH rule 0 x 79 [13,2,15,5]
+ CRUSH rule 0 x 80 [15,2,6,4]
+ CRUSH rule 0 x 81 [15,2,1,11]
+ CRUSH rule 0 x 82 [14,13,5,11]
+ CRUSH rule 0 x 83 [4,15,3,9]
+ CRUSH rule 0 x 84 [10,7,9,15]
+ CRUSH rule 0 x 85 [3,15,9,7]
+ CRUSH rule 0 x 86 [10,9,14,1]
+ CRUSH rule 0 x 87 [15,10,7,12]
+ CRUSH rule 0 x 88 [4,13,3,1]
+ CRUSH rule 0 x 89 [3,9,7,4]
+ CRUSH rule 0 x 90 [4,9,7,12]
+ CRUSH rule 0 x 91 [6,11,9,1]
+ CRUSH rule 0 x 92 [1,5,10,9]
+ CRUSH rule 0 x 93 [9,3,15,13]
+ CRUSH rule 0 x 94 [9,2,12,5]
+ CRUSH rule 0 x 95 [7,15,4,10]
+ CRUSH rule 0 x 96 [2,15,11,7]
+ CRUSH rule 0 x 97 [4,11,2,13]
+ CRUSH rule 0 x 98 [11,13,9,3]
+ CRUSH rule 0 x 99 [12,4,11,7]
+ CRUSH rule 0 x 100 [9,4,10,15]
+ CRUSH rule 0 x 101 [15,7,1,9]
+ CRUSH rule 0 x 102 [3,11,14,6]
+ CRUSH rule 0 x 103 [13,11,6,14]
+ CRUSH rule 0 x 104 [14,6,3,5]
+ CRUSH rule 0 x 105 [14,10,1,9]
+ CRUSH rule 0 x 106 [6,5,13,2]
+ CRUSH rule 0 x 107 [3,1,10,14]
+ CRUSH rule 0 x 108 [5,10,7,2]
+ CRUSH rule 0 x 109 [9,1,13,7]
+ CRUSH rule 0 x 110 [5,1,11,3]
+ CRUSH rule 0 x 111 [10,1,9,7]
+ CRUSH rule 0 x 112 [1,10,4,14]
+ CRUSH rule 0 x 113 [6,10,13,9]
+ CRUSH rule 0 x 114 [5,13,6,2]
+ CRUSH rule 0 x 115 [10,13,14,3]
+ CRUSH rule 0 x 116 [1,14,13,2]
+ CRUSH rule 0 x 117 [5,6,1,12]
+ CRUSH rule 0 x 118 [10,4,13,15]
+ CRUSH rule 0 x 119 [14,12,11,4]
+ CRUSH rule 0 x 120 [11,3,14,13]
+ CRUSH rule 0 x 121 [9,5,1,11]
+ CRUSH rule 0 x 122 [4,3,14,1]
+ CRUSH rule 0 x 123 [3,10,5,6]
+ CRUSH rule 0 x 124 [12,2,1,5]
+ CRUSH rule 0 x 125 [9,12,15,1]
+ CRUSH rule 0 x 126 [7,15,10,9]
+ CRUSH rule 0 x 127 [4,14,9,13]
+ CRUSH rule 0 x 128 [3,12,1,10]
+ CRUSH rule 0 x 129 [11,13,14,2]
+ CRUSH rule 0 x 130 [3,13,5,14]
+ CRUSH rule 0 x 131 [12,1,6,15]
+ CRUSH rule 0 x 132 [11,15,13,9]
+ CRUSH rule 0 x 133 [3,6,9,11]
+ CRUSH rule 0 x 134 [12,5,6,15]
+ CRUSH rule 0 x 135 [3,14,12,4]
+ CRUSH rule 0 x 136 [15,6,9,4]
+ CRUSH rule 0 x 137 [14,3,6,11]
+ CRUSH rule 0 x 138 [13,15,4,10]
+ CRUSH rule 0 x 139 [11,2,13,9]
+ CRUSH rule 0 x 140 [11,4,12,15]
+ CRUSH rule 0 x 141 [6,12,15,11]
+ CRUSH rule 0 x 142 [3,14,7,9]
+ CRUSH rule 0 x 143 [9,6,4,2]
+ CRUSH rule 0 x 144 [13,7,11,2]
+ CRUSH rule 0 x 145 [12,2,6,10]
+ CRUSH rule 0 x 146 [1,5,9,2]
+ CRUSH rule 0 x 147 [1,4,9,11]
+ CRUSH rule 0 x 148 [12,7,9,2]
+ CRUSH rule 0 x 149 [2,5,9,12]
+ CRUSH rule 0 x 150 [1,15,2,10]
+ CRUSH rule 0 x 151 [2,9,14,7]
+ CRUSH rule 0 x 152 [5,9,2,6]
+ CRUSH rule 0 x 153 [6,9,4,15]
+ CRUSH rule 0 x 154 [3,11,7,1]
+ CRUSH rule 0 x 155 [14,12,7,3]
+ CRUSH rule 0 x 156 [7,13,3,10]
+ CRUSH rule 0 x 157 [15,1,6,4]
+ CRUSH rule 0 x 158 [15,1,10,6]
+ CRUSH rule 0 x 159 [4,14,3,12]
+ CRUSH rule 0 x 160 [5,7,3,14]
+ CRUSH rule 0 x 161 [1,2,11,4]
+ CRUSH rule 0 x 162 [10,6,1,12]
+ CRUSH rule 0 x 163 [15,1,10,2]
+ CRUSH rule 0 x 164 [9,14,10,7]
+ CRUSH rule 0 x 165 [11,7,2,13]
+ CRUSH rule 0 x 166 [1,2,12,14]
+ CRUSH rule 0 x 167 [9,7,3,4]
+ CRUSH rule 0 x 168 [13,2,4,1]
+ CRUSH rule 0 x 169 [1,4,9,14]
+ CRUSH rule 0 x 170 [1,15,7,9]
+ CRUSH rule 0 x 171 [9,2,10,7]
+ CRUSH rule 0 x 172 [14,4,10,12]
+ CRUSH rule 0 x 173 [5,10,12,15]
+ CRUSH rule 0 x 174 [15,6,4,12]
+ CRUSH rule 0 x 175 [5,7,9,3]
+ CRUSH rule 0 x 176 [9,6,3,14]
+ CRUSH rule 0 x 177 [2,9,10,13]
+ CRUSH rule 0 x 178 [12,11,7,14]
+ CRUSH rule 0 x 179 [2,10,13,9]
+ CRUSH rule 0 x 180 [3,11,5,15]
+ CRUSH rule 0 x 181 [9,12,6,5]
+ CRUSH rule 0 x 182 [5,13,11,2]
+ CRUSH rule 0 x 183 [5,7,10,13]
+ CRUSH rule 0 x 184 [2,5,11,12]
+ CRUSH rule 0 x 185 [13,5,7,11]
+ CRUSH rule 0 x 186 [6,14,13,5]
+ CRUSH rule 0 x 187 [1,4,11,13]
+ CRUSH rule 0 x 188 [9,13,5,14]
+ CRUSH rule 0 x 189 [6,12,4,9]
+ CRUSH rule 0 x 190 [9,13,15,10]
+ CRUSH rule 0 x 191 [7,11,4,1]
+ CRUSH rule 0 x 192 [2,11,5,15]
+ CRUSH rule 0 x 193 [3,13,6,10]
+ CRUSH rule 0 x 194 [3,13,4,14]
+ CRUSH rule 0 x 195 [5,7,10,12]
+ CRUSH rule 0 x 196 [4,15,1,10]
+ CRUSH rule 0 x 197 [14,10,13,4]
+ CRUSH rule 0 x 198 [2,5,6,15]
+ CRUSH rule 0 x 199 [2,10,4,15]
+ CRUSH rule 0 x 200 [7,14,11,4]
+ CRUSH rule 0 x 201 [9,14,1,7]
+ CRUSH rule 0 x 202 [14,11,7,3]
+ CRUSH rule 0 x 203 [12,5,7,15]
+ CRUSH rule 0 x 204 [6,11,3,12]
+ CRUSH rule 0 x 205 [15,4,6,10]
+ CRUSH rule 0 x 206 [13,11,2,15]
+ CRUSH rule 0 x 207 [2,11,7,4]
+ CRUSH rule 0 x 208 [13,1,6,14]
+ CRUSH rule 0 x 209 [6,15,13,1]
+ CRUSH rule 0 x 210 [13,11,2,7]
+ CRUSH rule 0 x 211 [2,14,1,13]
+ CRUSH rule 0 x 212 [10,1,12,15]
+ CRUSH rule 0 x 213 [3,9,6,5]
+ CRUSH rule 0 x 214 [7,15,4,1]
+ CRUSH rule 0 x 215 [6,1,4,13]
+ CRUSH rule 0 x 216 [12,9,6,2]
+ CRUSH rule 0 x 217 [12,11,1,14]
+ CRUSH rule 0 x 218 [12,10,15,6]
+ CRUSH rule 0 x 219 [3,11,14,6]
+ CRUSH rule 0 x 220 [14,4,3,12]
+ CRUSH rule 0 x 221 [15,5,2,6]
+ CRUSH rule 0 x 222 [10,4,3,15]
+ CRUSH rule 0 x 223 [9,7,11,1]
+ CRUSH rule 0 x 224 [1,7,10,2]
+ CRUSH rule 0 x 225 [10,5,2,6]
+ CRUSH rule 0 x 226 [4,1,9,3]
+ CRUSH rule 0 x 227 [7,2,12,15]
+ CRUSH rule 0 x 228 [2,15,11,1]
+ CRUSH rule 0 x 229 [9,3,7,14]
+ CRUSH rule 0 x 230 [10,5,7,2]
+ CRUSH rule 0 x 231 [2,7,5,13]
+ CRUSH rule 0 x 232 [10,5,13,1]
+ CRUSH rule 0 x 233 [6,12,11,4]
+ CRUSH rule 0 x 234 [10,1,2,12]
+ CRUSH rule 0 x 235 [13,14,7,10]
+ CRUSH rule 0 x 236 [2,15,9,12]
+ CRUSH rule 0 x 237 [3,12,9,10]
+ CRUSH rule 0 x 238 [2,10,4,15]
+ CRUSH rule 0 x 239 [4,15,10,7]
+ CRUSH rule 0 x 240 [15,5,13,7]
+ CRUSH rule 0 x 241 [7,9,15,12]
+ CRUSH rule 0 x 242 [14,2,6,9]
+ CRUSH rule 0 x 243 [2,11,5,1]
+ CRUSH rule 0 x 244 [13,9,15,3]
+ CRUSH rule 0 x 245 [12,9,15,3]
+ CRUSH rule 0 x 246 [15,3,5,11]
+ CRUSH rule 0 x 247 [6,4,9,12]
+ CRUSH rule 0 x 248 [5,13,7,11]
+ CRUSH rule 0 x 249 [10,14,7,3]
+ CRUSH rule 0 x 250 [12,15,1,10]
+ CRUSH rule 0 x 251 [13,2,15,5]
+ CRUSH rule 0 x 252 [7,5,13,9]
+ CRUSH rule 0 x 253 [3,13,15,10]
+ CRUSH rule 0 x 254 [2,9,13,14]
+ CRUSH rule 0 x 255 [1,9,13,2]
+ CRUSH rule 0 x 256 [6,9,13,1]
+ CRUSH rule 0 x 257 [15,12,3,9]
+ CRUSH rule 0 x 258 [12,5,6,10]
+ CRUSH rule 0 x 259 [9,10,4,3]
+ CRUSH rule 0 x 260 [10,12,6,9]
+ CRUSH rule 0 x 261 [13,7,2,1]
+ CRUSH rule 0 x 262 [15,3,12,7]
+ CRUSH rule 0 x 263 [12,6,10,9]
+ CRUSH rule 0 x 264 [13,14,11,3]
+ CRUSH rule 0 x 265 [12,10,14,5]
+ CRUSH rule 0 x 266 [14,7,11,1]
+ CRUSH rule 0 x 267 [12,11,6,5]
+ CRUSH rule 0 x 268 [4,1,15,12]
+ CRUSH rule 0 x 269 [11,1,15,5]
+ CRUSH rule 0 x 270 [7,11,12,3]
+ CRUSH rule 0 x 271 [4,7,3,13]
+ CRUSH rule 0 x 272 [15,5,13,10]
+ CRUSH rule 0 x 273 [2,10,7,12]
+ CRUSH rule 0 x 274 [10,2,5,6]
+ CRUSH rule 0 x 275 [10,3,4,7]
+ CRUSH rule 0 x 276 [5,12,9,2]
+ CRUSH rule 0 x 277 [14,3,13,4]
+ CRUSH rule 0 x 278 [5,6,14,3]
+ CRUSH rule 0 x 279 [6,10,13,3]
+ CRUSH rule 0 x 280 [7,3,14,9]
+ CRUSH rule 0 x 281 [5,11,14,7]
+ CRUSH rule 0 x 282 [2,1,13,14]
+ CRUSH rule 0 x 283 [4,1,12,3]
+ CRUSH rule 0 x 284 [5,11,7,15]
+ CRUSH rule 0 x 285 [15,5,3,1]
+ CRUSH rule 0 x 286 [10,4,3,6]
+ CRUSH rule 0 x 287 [12,4,9,1]
+ CRUSH rule 0 x 288 [4,12,10,7]
+ CRUSH rule 0 x 289 [2,5,14,9]
+ CRUSH rule 0 x 290 [12,2,5,6]
+ CRUSH rule 0 x 291 [7,11,1,14]
+ CRUSH rule 0 x 292 [4,10,6,3]
+ CRUSH rule 0 x 293 [6,5,11,1]
+ CRUSH rule 0 x 294 [9,12,3,14]
+ CRUSH rule 0 x 295 [6,10,3,14]
+ CRUSH rule 0 x 296 [3,1,13,7]
+ CRUSH rule 0 x 297 [6,13,4,14]
+ CRUSH rule 0 x 298 [14,9,13,1]
+ CRUSH rule 0 x 299 [14,12,11,6]
+ CRUSH rule 0 x 300 [15,7,10,5]
+ CRUSH rule 0 x 301 [9,11,7,1]
+ CRUSH rule 0 x 302 [9,7,1,13]
+ CRUSH rule 0 x 303 [4,13,3,7]
+ CRUSH rule 0 x 304 [6,9,2,11]
+ CRUSH rule 0 x 305 [13,7,5,11]
+ CRUSH rule 0 x 306 [10,12,4,6]
+ CRUSH rule 0 x 307 [11,12,15,5]
+ CRUSH rule 0 x 308 [12,14,10,9]
+ CRUSH rule 0 x 309 [9,3,12,5]
+ CRUSH rule 0 x 310 [3,1,5,10]
+ CRUSH rule 0 x 311 [3,9,7,1]
+ CRUSH rule 0 x 312 [15,13,9,7]
+ CRUSH rule 0 x 313 [9,15,3,7]
+ CRUSH rule 0 x 314 [2,15,9,5]
+ CRUSH rule 0 x 315 [15,2,13,1]
+ CRUSH rule 0 x 316 [4,9,11,2]
+ CRUSH rule 0 x 317 [1,5,3,13]
+ CRUSH rule 0 x 318 [4,1,15,11]
+ CRUSH rule 0 x 319 [2,15,4,1]
+ CRUSH rule 0 x 320 [5,7,13,9]
+ CRUSH rule 0 x 321 [1,6,11,15]
+ CRUSH rule 0 x 322 [13,7,5,3]
+ CRUSH rule 0 x 323 [7,4,10,1]
+ CRUSH rule 0 x 324 [5,6,10,15]
+ CRUSH rule 0 x 325 [9,10,14,5]
+ CRUSH rule 0 x 326 [11,7,13,4]
+ CRUSH rule 0 x 327 [12,5,10,14]
+ CRUSH rule 0 x 328 [5,2,6,14]
+ CRUSH rule 0 x 329 [2,6,15,5]
+ CRUSH rule 0 x 330 [3,9,11,13]
+ CRUSH rule 0 x 331 [12,14,6,3]
+ CRUSH rule 0 x 332 [10,12,6,15]
+ CRUSH rule 0 x 333 [6,5,3,12]
+ CRUSH rule 0 x 334 [4,9,2,12]
+ CRUSH rule 0 x 335 [11,7,1,5]
+ CRUSH rule 0 x 336 [6,14,13,2]
+ CRUSH rule 0 x 337 [15,11,3,7]
+ CRUSH rule 0 x 338 [10,5,3,6]
+ CRUSH rule 0 x 339 [11,14,13,5]
+ CRUSH rule 0 x 340 [11,6,12,4]
+ CRUSH rule 0 x 341 [7,5,2,10]
+ CRUSH rule 0 x 342 [12,14,1,9]
+ CRUSH rule 0 x 343 [12,14,9,6]
+ CRUSH rule 0 x 344 [9,11,5,2]
+ CRUSH rule 0 x 345 [14,2,11,9]
+ CRUSH rule 0 x 346 [5,3,14,10]
+ CRUSH rule 0 x 347 [10,2,12,6]
+ CRUSH rule 0 x 348 [7,9,10,1]
+ CRUSH rule 0 x 349 [9,6,10,12]
+ CRUSH rule 0 x 350 [13,9,15,4]
+ CRUSH rule 0 x 351 [13,5,15,3]
+ CRUSH rule 0 x 352 [1,12,11,9]
+ CRUSH rule 0 x 353 [10,14,12,2]
+ CRUSH rule 0 x 354 [6,3,15,10]
+ CRUSH rule 0 x 355 [13,14,6,10]
+ CRUSH rule 0 x 356 [15,13,2,9]
+ CRUSH rule 0 x 357 [4,11,1,13]
+ CRUSH rule 0 x 358 [12,7,2,9]
+ CRUSH rule 0 x 359 [5,15,7,11]
+ CRUSH rule 0 x 360 [13,10,1,2]
+ CRUSH rule 0 x 361 [5,3,13,6]
+ CRUSH rule 0 x 362 [2,9,11,13]
+ CRUSH rule 0 x 363 [7,12,3,9]
+ CRUSH rule 0 x 364 [2,12,6,9]
+ CRUSH rule 0 x 365 [13,5,11,15]
+ CRUSH rule 0 x 366 [12,7,3,14]
+ CRUSH rule 0 x 367 [7,13,3,1]
+ CRUSH rule 0 x 368 [7,9,10,15]
+ CRUSH rule 0 x 369 [7,5,3,13]
+ CRUSH rule 0 x 370 [4,7,14,1]
+ CRUSH rule 0 x 371 [1,7,12,3]
+ CRUSH rule 0 x 372 [10,4,3,14]
+ CRUSH rule 0 x 373 [15,5,2,6]
+ CRUSH rule 0 x 374 [3,15,12,5]
+ CRUSH rule 0 x 375 [5,2,14,1]
+ CRUSH rule 0 x 376 [5,14,10,13]
+ CRUSH rule 0 x 377 [1,15,2,4]
+ CRUSH rule 0 x 378 [9,12,2,15]
+ CRUSH rule 0 x 379 [11,2,15,5]
+ CRUSH rule 0 x 380 [6,1,12,11]
+ CRUSH rule 0 x 381 [15,13,7,5]
+ CRUSH rule 0 x 382 [14,3,1,4]
+ CRUSH rule 0 x 383 [3,6,11,4]
+ CRUSH rule 0 x 384 [4,13,6,3]
+ CRUSH rule 0 x 385 [4,6,15,3]
+ CRUSH rule 0 x 386 [14,3,11,13]
+ CRUSH rule 0 x 387 [1,11,5,7]
+ CRUSH rule 0 x 388 [2,6,11,9]
+ CRUSH rule 0 x 389 [12,7,2,4]
+ CRUSH rule 0 x 390 [2,11,13,7]
+ CRUSH rule 0 x 391 [3,4,9,13]
+ CRUSH rule 0 x 392 [11,5,14,7]
+ CRUSH rule 0 x 393 [2,14,5,9]
+ CRUSH rule 0 x 394 [4,9,3,15]
+ CRUSH rule 0 x 395 [10,13,5,15]
+ CRUSH rule 0 x 396 [2,12,15,9]
+ CRUSH rule 0 x 397 [1,14,9,4]
+ CRUSH rule 0 x 398 [9,2,1,5]
+ CRUSH rule 0 x 399 [5,9,14,3]
+ CRUSH rule 0 x 400 [10,6,2,4]
+ CRUSH rule 0 x 401 [6,9,11,12]
+ CRUSH rule 0 x 402 [4,7,9,2]
+ CRUSH rule 0 x 403 [7,15,13,3]
+ CRUSH rule 0 x 404 [14,12,7,9]
+ CRUSH rule 0 x 405 [9,15,11,2]
+ CRUSH rule 0 x 406 [12,14,9,2]
+ CRUSH rule 0 x 407 [9,5,12,10]
+ CRUSH rule 0 x 408 [7,1,5,2]
+ CRUSH rule 0 x 409 [11,2,4,13]
+ CRUSH rule 0 x 410 [6,4,14,2]
+ CRUSH rule 0 x 411 [13,11,15,6]
+ CRUSH rule 0 x 412 [5,9,6,11]
+ CRUSH rule 0 x 413 [13,5,3,11]
+ CRUSH rule 0 x 414 [3,11,9,13]
+ CRUSH rule 0 x 415 [6,10,14,5]
+ CRUSH rule 0 x 416 [13,1,4,7]
+ CRUSH rule 0 x 417 [4,12,1,15]
+ CRUSH rule 0 x 418 [14,5,10,2]
+ CRUSH rule 0 x 419 [5,14,10,9]
+ CRUSH rule 0 x 420 [2,4,9,11]
+ CRUSH rule 0 x 421 [15,4,10,3]
+ CRUSH rule 0 x 422 [4,11,2,7]
+ CRUSH rule 0 x 423 [3,15,12,6]
+ CRUSH rule 0 x 424 [6,10,12,2]
+ CRUSH rule 0 x 425 [11,15,2,13]
+ CRUSH rule 0 x 426 [12,4,7,1]
+ CRUSH rule 0 x 427 [14,10,3,1]
+ CRUSH rule 0 x 428 [12,7,9,4]
+ CRUSH rule 0 x 429 [3,4,9,7]
+ CRUSH rule 0 x 430 [3,5,10,13]
+ CRUSH rule 0 x 431 [9,3,7,1]
+ CRUSH rule 0 x 432 [4,1,12,7]
+ CRUSH rule 0 x 433 [4,11,12,15]
+ CRUSH rule 0 x 434 [2,14,9,1]
+ CRUSH rule 0 x 435 [13,11,5,6]
+ CRUSH rule 0 x 436 [9,15,10,2]
+ CRUSH rule 0 x 437 [9,6,3,14]
+ CRUSH rule 0 x 438 [7,2,13,4]
+ CRUSH rule 0 x 439 [7,14,4,3]
+ CRUSH rule 0 x 440 [14,11,9,2]
+ CRUSH rule 0 x 441 [2,4,11,9]
+ CRUSH rule 0 x 442 [10,13,9,7]
+ CRUSH rule 0 x 443 [12,15,10,9]
+ CRUSH rule 0 x 444 [4,13,7,14]
+ CRUSH rule 0 x 445 [4,2,15,7]
+ CRUSH rule 0 x 446 [12,10,6,9]
+ CRUSH rule 0 x 447 [15,7,13,1]
+ CRUSH rule 0 x 448 [5,2,13,7]
+ CRUSH rule 0 x 449 [14,5,3,12]
+ CRUSH rule 0 x 450 [2,4,6,9]
+ CRUSH rule 0 x 451 [6,14,11,3]
+ CRUSH rule 0 x 452 [14,9,10,4]
+ CRUSH rule 0 x 453 [5,15,13,2]
+ CRUSH rule 0 x 454 [10,4,2,6]
+ CRUSH rule 0 x 455 [6,13,2,4]
+ CRUSH rule 0 x 456 [5,7,13,1]
+ CRUSH rule 0 x 457 [9,1,5,7]
+ CRUSH rule 0 x 458 [9,11,15,4]
+ CRUSH rule 0 x 459 [13,15,11,1]
+ CRUSH rule 0 x 460 [5,12,10,15]
+ CRUSH rule 0 x 461 [4,3,9,13]
+ CRUSH rule 0 x 462 [4,7,12,14]
+ CRUSH rule 0 x 463 [4,12,14,11]
+ CRUSH rule 0 x 464 [4,2,15,10]
+ CRUSH rule 0 x 465 [5,10,9,7]
+ CRUSH rule 0 x 466 [13,5,2,15]
+ CRUSH rule 0 x 467 [13,6,14,3]
+ CRUSH rule 0 x 468 [10,7,12,14]
+ CRUSH rule 0 x 469 [4,9,6,14]
+ CRUSH rule 0 x 470 [3,9,12,15]
+ CRUSH rule 0 x 471 [6,1,5,14]
+ CRUSH rule 0 x 472 [2,14,7,5]
+ CRUSH rule 0 x 473 [15,10,6,9]
+ CRUSH rule 0 x 474 [15,10,4,12]
+ CRUSH rule 0 x 475 [10,5,12,9]
+ CRUSH rule 0 x 476 [3,6,10,12]
+ CRUSH rule 0 x 477 [6,13,5,15]
+ CRUSH rule 0 x 478 [4,15,1,3]
+ CRUSH rule 0 x 479 [13,11,1,6]
+ CRUSH rule 0 x 480 [1,13,6,4]
+ CRUSH rule 0 x 481 [15,12,7,9]
+ CRUSH rule 0 x 482 [2,12,9,1]
+ CRUSH rule 0 x 483 [10,1,4,15]
+ CRUSH rule 0 x 484 [1,4,10,13]
+ CRUSH rule 0 x 485 [9,4,3,1]
+ CRUSH rule 0 x 486 [3,10,15,9]
+ CRUSH rule 0 x 487 [12,11,4,14]
+ CRUSH rule 0 x 488 [14,4,1,9]
+ CRUSH rule 0 x 489 [11,4,2,13]
+ CRUSH rule 0 x 490 [4,9,1,3]
+ CRUSH rule 0 x 491 [1,12,5,2]
+ CRUSH rule 0 x 492 [5,7,11,3]
+ CRUSH rule 0 x 493 [12,1,4,15]
+ CRUSH rule 0 x 494 [1,7,13,4]
+ CRUSH rule 0 x 495 [3,15,7,1]
+ CRUSH rule 0 x 496 [5,3,7,13]
+ CRUSH rule 0 x 497 [13,10,3,6]
+ CRUSH rule 0 x 498 [10,6,1,5]
+ CRUSH rule 0 x 499 [14,3,12,5]
+ CRUSH rule 0 x 500 [15,9,6,12]
+ CRUSH rule 0 x 501 [10,13,1,9]
+ CRUSH rule 0 x 502 [5,1,14,11]
+ CRUSH rule 0 x 503 [15,10,7,9]
+ CRUSH rule 0 x 504 [13,2,7,1]
+ CRUSH rule 0 x 505 [12,7,5,2]
+ CRUSH rule 0 x 506 [11,7,9,14]
+ CRUSH rule 0 x 507 [4,14,13,3]
+ CRUSH rule 0 x 508 [12,1,4,9]
+ CRUSH rule 0 x 509 [4,2,6,9]
+ CRUSH rule 0 x 510 [5,3,1,12]
+ CRUSH rule 0 x 511 [2,12,10,6]
+ CRUSH rule 0 x 512 [15,11,3,5]
+ CRUSH rule 0 x 513 [4,9,11,3]
+ CRUSH rule 0 x 514 [11,9,3,4]
+ CRUSH rule 0 x 515 [12,14,6,5]
+ CRUSH rule 0 x 516 [14,11,1,12]
+ CRUSH rule 0 x 517 [11,5,6,13]
+ CRUSH rule 0 x 518 [3,5,7,12]
+ CRUSH rule 0 x 519 [12,14,2,1]
+ CRUSH rule 0 x 520 [12,4,2,10]
+ CRUSH rule 0 x 521 [11,5,9,6]
+ CRUSH rule 0 x 522 [4,12,11,1]
+ CRUSH rule 0 x 523 [3,1,5,9]
+ CRUSH rule 0 x 524 [15,9,3,11]
+ CRUSH rule 0 x 525 [3,15,11,6]
+ CRUSH rule 0 x 526 [10,2,5,13]
+ CRUSH rule 0 x 527 [3,13,4,1]
+ CRUSH rule 0 x 528 [12,7,15,10]
+ CRUSH rule 0 x 529 [6,4,10,12]
+ CRUSH rule 0 x 530 [11,9,12,7]
+ CRUSH rule 0 x 531 [9,15,4,7]
+ CRUSH rule 0 x 532 [5,3,13,7]
+ CRUSH rule 0 x 533 [12,15,1,2]
+ CRUSH rule 0 x 534 [11,9,3,7]
+ CRUSH rule 0 x 535 [11,1,3,5]
+ CRUSH rule 0 x 536 [9,1,14,13]
+ CRUSH rule 0 x 537 [15,5,13,2]
+ CRUSH rule 0 x 538 [13,5,11,2]
+ CRUSH rule 0 x 539 [10,12,6,14]
+ CRUSH rule 0 x 540 [12,15,7,3]
+ CRUSH rule 0 x 541 [2,1,6,11]
+ CRUSH rule 0 x 542 [3,9,15,5]
+ CRUSH rule 0 x 543 [4,10,9,3]
+ CRUSH rule 0 x 544 [3,15,9,11]
+ CRUSH rule 0 x 545 [14,10,7,12]
+ CRUSH rule 0 x 546 [5,15,13,7]
+ CRUSH rule 0 x 547 [5,13,7,9]
+ CRUSH rule 0 x 548 [11,7,12,15]
+ CRUSH rule 0 x 549 [14,1,4,9]
+ CRUSH rule 0 x 550 [9,15,3,13]
+ CRUSH rule 0 x 551 [11,2,15,6]
+ CRUSH rule 0 x 552 [2,11,14,1]
+ CRUSH rule 0 x 553 [11,9,14,6]
+ CRUSH rule 0 x 554 [11,14,6,4]
+ CRUSH rule 0 x 555 [6,5,10,9]
+ CRUSH rule 0 x 556 [15,6,3,13]
+ CRUSH rule 0 x 557 [12,2,5,14]
+ CRUSH rule 0 x 558 [12,1,6,15]
+ CRUSH rule 0 x 559 [2,13,5,10]
+ CRUSH rule 0 x 560 [4,9,12,6]
+ CRUSH rule 0 x 561 [12,7,1,2]
+ CRUSH rule 0 x 562 [7,13,9,14]
+ CRUSH rule 0 x 563 [15,4,3,10]
+ CRUSH rule 0 x 564 [2,13,7,1]
+ CRUSH rule 0 x 565 [3,12,4,1]
+ CRUSH rule 0 x 566 [6,14,4,2]
+ CRUSH rule 0 x 567 [15,4,11,6]
+ CRUSH rule 0 x 568 [4,14,1,6]
+ CRUSH rule 0 x 569 [11,3,15,13]
+ CRUSH rule 0 x 570 [1,10,13,4]
+ CRUSH rule 0 x 571 [10,12,14,9]
+ CRUSH rule 0 x 572 [12,14,3,10]
+ CRUSH rule 0 x 573 [7,15,11,2]
+ CRUSH rule 0 x 574 [11,14,13,1]
+ CRUSH rule 0 x 575 [5,13,15,9]
+ CRUSH rule 0 x 576 [3,15,11,9]
+ CRUSH rule 0 x 577 [13,9,6,15]
+ CRUSH rule 0 x 578 [4,10,1,2]
+ CRUSH rule 0 x 579 [13,1,15,2]
+ CRUSH rule 0 x 580 [3,12,4,1]
+ CRUSH rule 0 x 581 [7,14,12,10]
+ CRUSH rule 0 x 582 [10,5,13,14]
+ CRUSH rule 0 x 583 [4,15,1,9]
+ CRUSH rule 0 x 584 [10,1,5,13]
+ CRUSH rule 0 x 585 [5,3,6,1]
+ CRUSH rule 0 x 586 [7,10,14,12]
+ CRUSH rule 0 x 587 [11,6,9,4]
+ CRUSH rule 0 x 588 [3,12,7,15]
+ CRUSH rule 0 x 589 [9,7,12,1]
+ CRUSH rule 0 x 590 [12,1,3,9]
+ CRUSH rule 0 x 591 [2,6,14,13]
+ CRUSH rule 0 x 592 [15,12,9,7]
+ CRUSH rule 0 x 593 [13,14,5,11]
+ CRUSH rule 0 x 594 [12,14,2,9]
+ CRUSH rule 0 x 595 [12,7,10,3]
+ CRUSH rule 0 x 596 [2,7,12,11]
+ CRUSH rule 0 x 597 [15,1,2,10]
+ CRUSH rule 0 x 598 [11,5,9,14]
+ CRUSH rule 0 x 599 [13,11,1,5]
+ CRUSH rule 0 x 600 [4,12,3,10]
+ CRUSH rule 0 x 601 [13,5,15,2]
+ CRUSH rule 0 x 602 [3,11,7,1]
+ CRUSH rule 0 x 603 [3,1,4,14]
+ CRUSH rule 0 x 604 [14,2,6,1]
+ CRUSH rule 0 x 605 [2,7,12,5]
+ CRUSH rule 0 x 606 [12,15,1,5]
+ CRUSH rule 0 x 607 [3,9,10,14]
+ CRUSH rule 0 x 608 [13,10,1,7]
+ CRUSH rule 0 x 609 [14,3,7,9]
+ CRUSH rule 0 x 610 [7,10,5,1]
+ CRUSH rule 0 x 611 [13,1,5,3]
+ CRUSH rule 0 x 612 [7,1,2,13]
+ CRUSH rule 0 x 613 [10,7,14,9]
+ CRUSH rule 0 x 614 [9,4,15,3]
+ CRUSH rule 0 x 615 [9,4,11,2]
+ CRUSH rule 0 x 616 [10,14,1,5]
+ CRUSH rule 0 x 617 [15,7,2,11]
+ CRUSH rule 0 x 618 [4,2,10,6]
+ CRUSH rule 0 x 619 [15,4,3,9]
+ CRUSH rule 0 x 620 [3,7,11,14]
+ CRUSH rule 0 x 621 [3,6,4,14]
+ CRUSH rule 0 x 622 [10,2,13,5]
+ CRUSH rule 0 x 623 [4,9,14,7]
+ CRUSH rule 0 x 624 [3,9,15,6]
+ CRUSH rule 0 x 625 [11,7,3,5]
+ CRUSH rule 0 x 626 [10,12,2,1]
+ CRUSH rule 0 x 627 [1,12,10,14]
+ CRUSH rule 0 x 628 [15,13,11,4]
+ CRUSH rule 0 x 629 [5,6,15,12]
+ CRUSH rule 0 x 630 [1,4,12,9]
+ CRUSH rule 0 x 631 [5,7,1,15]
+ CRUSH rule 0 x 632 [12,3,11,9]
+ CRUSH rule 0 x 633 [14,4,3,7]
+ CRUSH rule 0 x 634 [6,9,5,3]
+ CRUSH rule 0 x 635 [6,5,2,15]
+ CRUSH rule 0 x 636 [13,6,11,3]
+ CRUSH rule 0 x 637 [3,1,10,6]
+ CRUSH rule 0 x 638 [10,15,3,5]
+ CRUSH rule 0 x 639 [6,9,14,4]
+ CRUSH rule 0 x 640 [9,6,1,11]
+ CRUSH rule 0 x 641 [10,6,5,14]
+ CRUSH rule 0 x 642 [1,15,4,6]
+ CRUSH rule 0 x 643 [3,7,5,1]
+ CRUSH rule 0 x 644 [15,13,6,9]
+ CRUSH rule 0 x 645 [14,2,4,9]
+ CRUSH rule 0 x 646 [5,13,14,1]
+ CRUSH rule 0 x 647 [10,1,9,13]
+ CRUSH rule 0 x 648 [6,5,2,14]
+ CRUSH rule 0 x 649 [3,9,13,11]
+ CRUSH rule 0 x 650 [10,9,4,15]
+ CRUSH rule 0 x 651 [3,9,5,7]
+ CRUSH rule 0 x 652 [15,9,4,6]
+ CRUSH rule 0 x 653 [11,14,1,3]
+ CRUSH rule 0 x 654 [13,6,2,10]
+ CRUSH rule 0 x 655 [6,3,4,15]
+ CRUSH rule 0 x 656 [3,15,1,4]
+ CRUSH rule 0 x 657 [11,15,3,5]
+ CRUSH rule 0 x 658 [7,2,10,12]
+ CRUSH rule 0 x 659 [2,5,14,6]
+ CRUSH rule 0 x 660 [13,14,10,6]
+ CRUSH rule 0 x 661 [7,15,3,12]
+ CRUSH rule 0 x 662 [15,2,12,5]
+ CRUSH rule 0 x 663 [14,9,13,10]
+ CRUSH rule 0 x 664 [6,10,12,4]
+ CRUSH rule 0 x 665 [2,9,12,1]
+ CRUSH rule 0 x 666 [12,3,6,1]
+ CRUSH rule 0 x 667 [1,9,12,10]
+ CRUSH rule 0 x 668 [9,5,1,2]
+ CRUSH rule 0 x 669 [9,7,14,5]
+ CRUSH rule 0 x 670 [6,10,9,13]
+ CRUSH rule 0 x 671 [6,15,5,10]
+ CRUSH rule 0 x 672 [2,9,13,1]
+ CRUSH rule 0 x 673 [7,10,5,9]
+ CRUSH rule 0 x 674 [7,12,10,1]
+ CRUSH rule 0 x 675 [9,5,1,10]
+ CRUSH rule 0 x 676 [10,12,2,1]
+ CRUSH rule 0 x 677 [2,12,1,4]
+ CRUSH rule 0 x 678 [1,2,4,10]
+ CRUSH rule 0 x 679 [5,6,12,15]
+ CRUSH rule 0 x 680 [7,11,3,1]
+ CRUSH rule 0 x 681 [6,4,3,11]
+ CRUSH rule 0 x 682 [6,1,11,15]
+ CRUSH rule 0 x 683 [6,13,2,4]
+ CRUSH rule 0 x 684 [9,11,3,7]
+ CRUSH rule 0 x 685 [5,1,15,7]
+ CRUSH rule 0 x 686 [1,9,11,14]
+ CRUSH rule 0 x 687 [7,13,3,5]
+ CRUSH rule 0 x 688 [11,9,1,14]
+ CRUSH rule 0 x 689 [5,2,9,12]
+ CRUSH rule 0 x 690 [9,7,10,3]
+ CRUSH rule 0 x 691 [11,15,9,5]
+ CRUSH rule 0 x 692 [15,5,1,2]
+ CRUSH rule 0 x 693 [5,6,12,15]
+ CRUSH rule 0 x 694 [4,7,1,10]
+ CRUSH rule 0 x 695 [6,13,14,10]
+ CRUSH rule 0 x 696 [1,2,4,14]
+ CRUSH rule 0 x 697 [13,11,3,6]
+ CRUSH rule 0 x 698 [11,13,4,2]
+ CRUSH rule 0 x 699 [7,14,12,4]
+ CRUSH rule 0 x 700 [12,14,11,9]
+ CRUSH rule 0 x 701 [3,13,1,14]
+ CRUSH rule 0 x 702 [3,12,15,6]
+ CRUSH rule 0 x 703 [15,11,13,3]
+ CRUSH rule 0 x 704 [6,4,2,15]
+ CRUSH rule 0 x 705 [14,6,11,5]
+ CRUSH rule 0 x 706 [1,12,3,6]
+ CRUSH rule 0 x 707 [4,7,14,3]
+ CRUSH rule 0 x 708 [3,10,5,1]
+ CRUSH rule 0 x 709 [11,12,3,7]
+ CRUSH rule 0 x 710 [14,2,11,9]
+ CRUSH rule 0 x 711 [14,3,9,10]
+ CRUSH rule 0 x 712 [12,3,11,15]
+ CRUSH rule 0 x 713 [11,9,3,15]
+ CRUSH rule 0 x 714 [12,1,9,7]
+ CRUSH rule 0 x 715 [6,1,14,4]
+ CRUSH rule 0 x 716 [11,13,9,14]
+ CRUSH rule 0 x 717 [12,4,10,9]
+ CRUSH rule 0 x 718 [7,15,5,2]
+ CRUSH rule 0 x 719 [5,15,13,3]
+ CRUSH rule 0 x 720 [4,13,10,2]
+ CRUSH rule 0 x 721 [11,3,14,9]
+ CRUSH rule 0 x 722 [2,4,6,1]
+ CRUSH rule 0 x 723 [2,1,12,15]
+ CRUSH rule 0 x 724 [7,1,9,10]
+ CRUSH rule 0 x 725 [11,12,7,15]
+ CRUSH rule 0 x 726 [7,14,4,3]
+ CRUSH rule 0 x 727 [2,5,1,11]
+ CRUSH rule 0 x 728 [13,11,4,6]
+ CRUSH rule 0 x 729 [15,11,4,6]
+ CRUSH rule 0 x 730 [3,7,1,13]
+ CRUSH rule 0 x 731 [9,1,6,5]
+ CRUSH rule 0 x 732 [1,2,10,13]
+ CRUSH rule 0 x 733 [11,3,5,6]
+ CRUSH rule 0 x 734 [14,3,11,7]
+ CRUSH rule 0 x 735 [6,9,2,10]
+ CRUSH rule 0 x 736 [3,9,1,11]
+ CRUSH rule 0 x 737 [1,4,2,12]
+ CRUSH rule 0 x 738 [11,15,7,4]
+ CRUSH rule 0 x 739 [11,12,6,2]
+ CRUSH rule 0 x 740 [7,9,10,13]
+ CRUSH rule 0 x 741 [12,11,7,15]
+ CRUSH rule 0 x 742 [9,7,4,11]
+ CRUSH rule 0 x 743 [5,13,9,15]
+ CRUSH rule 0 x 744 [6,2,13,1]
+ CRUSH rule 0 x 745 [3,6,1,4]
+ CRUSH rule 0 x 746 [3,7,9,10]
+ CRUSH rule 0 x 747 [15,11,5,2]
+ CRUSH rule 0 x 748 [6,10,13,2]
+ CRUSH rule 0 x 749 [14,9,10,7]
+ CRUSH rule 0 x 750 [1,14,6,5]
+ CRUSH rule 0 x 751 [15,1,6,9]
+ CRUSH rule 0 x 752 [13,1,7,3]
+ CRUSH rule 0 x 753 [4,11,1,3]
+ CRUSH rule 0 x 754 [14,12,11,4]
+ CRUSH rule 0 x 755 [13,6,1,10]
+ CRUSH rule 0 x 756 [3,4,14,6]
+ CRUSH rule 0 x 757 [10,6,1,4]
+ CRUSH rule 0 x 758 [6,3,4,10]
+ CRUSH rule 0 x 759 [5,7,3,14]
+ CRUSH rule 0 x 760 [1,15,10,12]
+ CRUSH rule 0 x 761 [2,12,1,14]
+ CRUSH rule 0 x 762 [1,4,10,9]
+ CRUSH rule 0 x 763 [4,13,1,14]
+ CRUSH rule 0 x 764 [1,14,6,13]
+ CRUSH rule 0 x 765 [9,15,2,13]
+ CRUSH rule 0 x 766 [11,2,7,15]
+ CRUSH rule 0 x 767 [6,11,4,3]
+ CRUSH rule 0 x 768 [2,12,15,7]
+ CRUSH rule 0 x 769 [15,1,9,2]
+ CRUSH rule 0 x 770 [15,13,4,6]
+ CRUSH rule 0 x 771 [9,2,12,11]
+ CRUSH rule 0 x 772 [4,3,13,11]
+ CRUSH rule 0 x 773 [3,7,4,15]
+ CRUSH rule 0 x 774 [12,6,3,15]
+ CRUSH rule 0 x 775 [5,10,14,2]
+ CRUSH rule 0 x 776 [10,15,3,9]
+ CRUSH rule 0 x 777 [11,13,4,7]
+ CRUSH rule 0 x 778 [13,1,9,11]
+ CRUSH rule 0 x 779 [5,11,1,14]
+ CRUSH rule 0 x 780 [13,9,3,6]
+ CRUSH rule 0 x 781 [5,7,14,3]
+ CRUSH rule 0 x 782 [2,15,9,7]
+ CRUSH rule 0 x 783 [12,7,5,14]
+ CRUSH rule 0 x 784 [14,1,10,13]
+ CRUSH rule 0 x 785 [6,12,1,2]
+ CRUSH rule 0 x 786 [10,5,2,15]
+ CRUSH rule 0 x 787 [1,12,10,2]
+ CRUSH rule 0 x 788 [4,2,9,13]
+ CRUSH rule 0 x 789 [9,2,14,7]
+ CRUSH rule 0 x 790 [15,2,7,4]
+ CRUSH rule 0 x 791 [9,4,7,13]
+ CRUSH rule 0 x 792 [6,4,15,10]
+ CRUSH rule 0 x 793 [15,9,6,2]
+ CRUSH rule 0 x 794 [5,12,2,14]
+ CRUSH rule 0 x 795 [6,14,12,4]
+ CRUSH rule 0 x 796 [11,2,12,6]
+ CRUSH rule 0 x 797 [14,3,7,1]
+ CRUSH rule 0 x 798 [5,11,6,13]
+ CRUSH rule 0 x 799 [2,9,14,4]
+ CRUSH rule 0 x 800 [6,3,4,11]
+ CRUSH rule 0 x 801 [2,5,6,13]
+ CRUSH rule 0 x 802 [1,4,12,7]
+ CRUSH rule 0 x 803 [7,2,4,1]
+ CRUSH rule 0 x 804 [5,14,9,7]
+ CRUSH rule 0 x 805 [13,4,3,1]
+ CRUSH rule 0 x 806 [6,2,13,4]
+ CRUSH rule 0 x 807 [14,2,7,4]
+ CRUSH rule 0 x 808 [2,15,12,7]
+ CRUSH rule 0 x 809 [1,11,7,12]
+ CRUSH rule 0 x 810 [2,5,9,12]
+ CRUSH rule 0 x 811 [15,6,3,10]
+ CRUSH rule 0 x 812 [7,11,2,14]
+ CRUSH rule 0 x 813 [4,10,13,14]
+ CRUSH rule 0 x 814 [13,4,9,3]
+ CRUSH rule 0 x 815 [15,12,9,4]
+ CRUSH rule 0 x 816 [14,10,13,7]
+ CRUSH rule 0 x 817 [10,7,2,15]
+ CRUSH rule 0 x 818 [15,2,11,4]
+ CRUSH rule 0 x 819 [5,12,10,6]
+ CRUSH rule 0 x 820 [3,6,9,12]
+ CRUSH rule 0 x 821 [15,10,9,13]
+ CRUSH rule 0 x 822 [10,13,2,9]
+ CRUSH rule 0 x 823 [2,6,12,10]
+ CRUSH rule 0 x 824 [3,7,9,13]
+ CRUSH rule 0 x 825 [10,5,14,6]
+ CRUSH rule 0 x 826 [5,2,11,15]
+ CRUSH rule 0 x 827 [13,5,1,3]
+ CRUSH rule 0 x 828 [12,6,10,5]
+ CRUSH rule 0 x 829 [13,6,15,10]
+ CRUSH rule 0 x 830 [15,13,2,9]
+ CRUSH rule 0 x 831 [1,4,11,12]
+ CRUSH rule 0 x 832 [14,11,13,2]
+ CRUSH rule 0 x 833 [9,13,3,11]
+ CRUSH rule 0 x 834 [9,7,5,1]
+ CRUSH rule 0 x 835 [14,3,13,6]
+ CRUSH rule 0 x 836 [3,9,10,13]
+ CRUSH rule 0 x 837 [15,12,11,2]
+ CRUSH rule 0 x 838 [12,14,9,2]
+ CRUSH rule 0 x 839 [3,4,6,10]
+ CRUSH rule 0 x 840 [10,15,12,4]
+ CRUSH rule 0 x 841 [3,5,7,12]
+ CRUSH rule 0 x 842 [9,13,2,6]
+ CRUSH rule 0 x 843 [14,7,4,9]
+ CRUSH rule 0 x 844 [7,1,4,15]
+ CRUSH rule 0 x 845 [13,6,1,15]
+ CRUSH rule 0 x 846 [3,7,15,13]
+ CRUSH rule 0 x 847 [12,15,11,5]
+ CRUSH rule 0 x 848 [11,13,1,14]
+ CRUSH rule 0 x 849 [3,15,11,9]
+ CRUSH rule 0 x 850 [1,3,10,6]
+ CRUSH rule 0 x 851 [14,4,3,6]
+ CRUSH rule 0 x 852 [9,12,4,7]
+ CRUSH rule 0 x 853 [13,14,6,11]
+ CRUSH rule 0 x 854 [7,11,12,1]
+ CRUSH rule 0 x 855 [14,4,12,6]
+ CRUSH rule 0 x 856 [5,10,7,3]
+ CRUSH rule 0 x 857 [4,3,13,11]
+ CRUSH rule 0 x 858 [5,15,6,3]
+ CRUSH rule 0 x 859 [5,15,6,2]
+ CRUSH rule 0 x 860 [11,14,1,12]
+ CRUSH rule 0 x 861 [13,7,4,10]
+ CRUSH rule 0 x 862 [5,10,9,7]
+ CRUSH rule 0 x 863 [11,6,3,9]
+ CRUSH rule 0 x 864 [6,13,4,2]
+ CRUSH rule 0 x 865 [4,1,14,11]
+ CRUSH rule 0 x 866 [2,13,4,15]
+ CRUSH rule 0 x 867 [12,2,9,10]
+ CRUSH rule 0 x 868 [14,11,7,2]
+ CRUSH rule 0 x 869 [10,13,7,14]
+ CRUSH rule 0 x 870 [14,9,11,4]
+ CRUSH rule 0 x 871 [6,2,1,4]
+ CRUSH rule 0 x 872 [6,1,15,3]
+ CRUSH rule 0 x 873 [2,5,12,10]
+ CRUSH rule 0 x 874 [12,4,7,2]
+ CRUSH rule 0 x 875 [10,6,14,1]
+ CRUSH rule 0 x 876 [14,7,13,3]
+ CRUSH rule 0 x 877 [15,11,13,9]
+ CRUSH rule 0 x 878 [7,14,3,13]
+ CRUSH rule 0 x 879 [12,2,7,4]
+ CRUSH rule 0 x 880 [2,12,10,7]
+ CRUSH rule 0 x 881 [6,3,1,11]
+ CRUSH rule 0 x 882 [11,13,7,1]
+ CRUSH rule 0 x 883 [13,1,3,10]
+ CRUSH rule 0 x 884 [6,15,4,9]
+ CRUSH rule 0 x 885 [14,7,9,4]
+ CRUSH rule 0 x 886 [13,11,4,2]
+ CRUSH rule 0 x 887 [14,4,12,11]
+ CRUSH rule 0 x 888 [10,12,7,15]
+ CRUSH rule 0 x 889 [15,13,4,1]
+ CRUSH rule 0 x 890 [10,12,14,2]
+ CRUSH rule 0 x 891 [9,5,11,6]
+ CRUSH rule 0 x 892 [12,15,2,4]
+ CRUSH rule 0 x 893 [1,3,5,9]
+ CRUSH rule 0 x 894 [7,2,11,13]
+ CRUSH rule 0 x 895 [2,1,11,5]
+ CRUSH rule 0 x 896 [9,1,14,10]
+ CRUSH rule 0 x 897 [7,5,14,3]
+ CRUSH rule 0 x 898 [10,6,12,9]
+ CRUSH rule 0 x 899 [1,11,5,3]
+ CRUSH rule 0 x 900 [2,9,10,7]
+ CRUSH rule 0 x 901 [9,12,11,3]
+ CRUSH rule 0 x 902 [4,2,6,15]
+ CRUSH rule 0 x 903 [14,10,3,1]
+ CRUSH rule 0 x 904 [15,12,4,9]
+ CRUSH rule 0 x 905 [12,6,11,3]
+ CRUSH rule 0 x 906 [14,11,12,2]
+ CRUSH rule 0 x 907 [7,12,3,9]
+ CRUSH rule 0 x 908 [2,15,9,6]
+ CRUSH rule 0 x 909 [10,14,1,13]
+ CRUSH rule 0 x 910 [12,7,4,15]
+ CRUSH rule 0 x 911 [11,15,2,4]
+ CRUSH rule 0 x 912 [6,4,14,13]
+ CRUSH rule 0 x 913 [4,6,10,1]
+ CRUSH rule 0 x 914 [4,15,2,10]
+ CRUSH rule 0 x 915 [12,14,1,9]
+ CRUSH rule 0 x 916 [3,1,11,5]
+ CRUSH rule 0 x 917 [1,15,6,5]
+ CRUSH rule 0 x 918 [7,14,11,4]
+ CRUSH rule 0 x 919 [10,7,3,13]
+ CRUSH rule 0 x 920 [4,2,10,15]
+ CRUSH rule 0 x 921 [1,11,6,13]
+ CRUSH rule 0 x 922 [6,4,14,13]
+ CRUSH rule 0 x 923 [12,2,5,14]
+ CRUSH rule 0 x 924 [6,2,14,13]
+ CRUSH rule 0 x 925 [12,15,2,10]
+ CRUSH rule 0 x 926 [3,13,10,1]
+ CRUSH rule 0 x 927 [6,5,1,11]
+ CRUSH rule 0 x 928 [13,1,3,9]
+ CRUSH rule 0 x 929 [10,7,1,5]
+ CRUSH rule 0 x 930 [7,15,10,5]
+ CRUSH rule 0 x 931 [6,15,11,9]
+ CRUSH rule 0 x 932 [13,2,5,11]
+ CRUSH rule 0 x 933 [12,7,14,10]
+ CRUSH rule 0 x 934 [12,2,5,7]
+ CRUSH rule 0 x 935 [6,11,1,14]
+ CRUSH rule 0 x 936 [9,12,7,5]
+ CRUSH rule 0 x 937 [14,2,11,1]
+ CRUSH rule 0 x 938 [14,3,5,11]
+ CRUSH rule 0 x 939 [6,4,14,9]
+ CRUSH rule 0 x 940 [13,11,4,2]
+ CRUSH rule 0 x 941 [3,12,4,7]
+ CRUSH rule 0 x 942 [15,12,10,4]
+ CRUSH rule 0 x 943 [10,2,4,9]
+ CRUSH rule 0 x 944 [2,9,4,7]
+ CRUSH rule 0 x 945 [10,15,2,9]
+ CRUSH rule 0 x 946 [11,15,7,12]
+ CRUSH rule 0 x 947 [11,3,14,1]
+ CRUSH rule 0 x 948 [7,13,11,5]
+ CRUSH rule 0 x 949 [9,1,12,5]
+ CRUSH rule 0 x 950 [9,15,13,6]
+ CRUSH rule 0 x 951 [2,6,12,9]
+ CRUSH rule 0 x 952 [9,7,15,3]
+ CRUSH rule 0 x 953 [1,3,6,10]
+ CRUSH rule 0 x 954 [10,2,14,9]
+ CRUSH rule 0 x 955 [7,14,3,1]
+ CRUSH rule 0 x 956 [1,6,11,5]
+ CRUSH rule 0 x 957 [14,11,1,12]
+ CRUSH rule 0 x 958 [15,4,3,11]
+ CRUSH rule 0 x 959 [2,1,12,15]
+ CRUSH rule 0 x 960 [2,6,11,13]
+ CRUSH rule 0 x 961 [3,13,11,9]
+ CRUSH rule 0 x 962 [5,11,3,14]
+ CRUSH rule 0 x 963 [13,10,15,4]
+ CRUSH rule 0 x 964 [7,11,4,9]
+ CRUSH rule 0 x 965 [12,2,9,7]
+ CRUSH rule 0 x 966 [12,14,9,4]
+ CRUSH rule 0 x 967 [7,5,3,10]
+ CRUSH rule 0 x 968 [12,15,4,9]
+ CRUSH rule 0 x 969 [11,4,7,1]
+ CRUSH rule 0 x 970 [5,12,10,1]
+ CRUSH rule 0 x 971 [1,9,4,12]
+ CRUSH rule 0 x 972 [12,3,14,5]
+ CRUSH rule 0 x 973 [1,10,4,12]
+ CRUSH rule 0 x 974 [7,11,1,2]
+ CRUSH rule 0 x 975 [7,9,15,12]
+ CRUSH rule 0 x 976 [7,3,15,5]
+ CRUSH rule 0 x 977 [14,3,6,10]
+ CRUSH rule 0 x 978 [12,5,11,1]
+ CRUSH rule 0 x 979 [5,1,13,6]
+ CRUSH rule 0 x 980 [15,11,5,6]
+ CRUSH rule 0 x 981 [5,11,15,12]
+ CRUSH rule 0 x 982 [2,6,14,11]
+ CRUSH rule 0 x 983 [3,12,10,9]
+ CRUSH rule 0 x 984 [15,13,1,10]
+ CRUSH rule 0 x 985 [11,2,15,1]
+ CRUSH rule 0 x 986 [6,13,9,1]
+ CRUSH rule 0 x 987 [13,14,5,10]
+ CRUSH rule 0 x 988 [12,9,10,14]
+ CRUSH rule 0 x 989 [7,4,3,15]
+ CRUSH rule 0 x 990 [1,10,9,13]
+ CRUSH rule 0 x 991 [7,11,1,14]
+ CRUSH rule 0 x 992 [9,10,2,13]
+ CRUSH rule 0 x 993 [6,10,14,12]
+ CRUSH rule 0 x 994 [3,13,15,4]
+ CRUSH rule 0 x 995 [15,6,12,2]
+ CRUSH rule 0 x 996 [15,10,5,3]
+ CRUSH rule 0 x 997 [15,2,1,12]
+ CRUSH rule 0 x 998 [6,1,9,5]
+ CRUSH rule 0 x 999 [9,10,15,5]
+ CRUSH rule 0 x 1000 [14,2,9,4]
+ CRUSH rule 0 x 1001 [11,14,4,2]
+ CRUSH rule 0 x 1002 [1,10,14,2]
+ CRUSH rule 0 x 1003 [10,7,5,14]
+ CRUSH rule 0 x 1004 [15,1,4,6]
+ CRUSH rule 0 x 1005 [6,12,2,10]
+ CRUSH rule 0 x 1006 [10,12,15,1]
+ CRUSH rule 0 x 1007 [1,7,13,14]
+ CRUSH rule 0 x 1008 [7,4,9,11]
+ CRUSH rule 0 x 1009 [5,2,11,7]
+ CRUSH rule 0 x 1010 [10,2,15,6]
+ CRUSH rule 0 x 1011 [6,3,12,1]
+ CRUSH rule 0 x 1012 [12,6,9,15]
+ CRUSH rule 0 x 1013 [2,14,12,4]
+ CRUSH rule 0 x 1014 [1,13,7,2]
+ CRUSH rule 0 x 1015 [12,6,10,1]
+ CRUSH rule 0 x 1016 [10,13,14,3]
+ CRUSH rule 0 x 1017 [5,11,14,7]
+ CRUSH rule 0 x 1018 [13,11,14,1]
+ CRUSH rule 0 x 1019 [10,13,14,7]
+ CRUSH rule 0 x 1020 [3,1,13,4]
+ CRUSH rule 0 x 1021 [2,11,14,9]
+ CRUSH rule 0 x 1022 [15,5,7,2]
+ CRUSH rule 0 x 1023 [15,2,9,12]
+ rule 0 (replicated_ruleset) num_rep 4 result size == 4:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3,15,12]
+ CRUSH rule 0 x 1 [10,15,1,2,13]
+ CRUSH rule 0 x 2 [1,12,2,6,5]
+ CRUSH rule 0 x 3 [15,4,10,2,9]
+ CRUSH rule 0 x 4 [14,2,10,1,9]
+ CRUSH rule 0 x 5 [7,4,11,2,13]
+ CRUSH rule 0 x 6 [12,6,10,9,3]
+ CRUSH rule 0 x 7 [9,2,6,12,11]
+ CRUSH rule 0 x 8 [10,2,15,1,4]
+ CRUSH rule 0 x 9 [7,1,14,2,11]
+ CRUSH rule 0 x 10 [10,14,4,1,2]
+ CRUSH rule 0 x 11 [13,9,14,7,5]
+ CRUSH rule 0 x 12 [7,1,2,5,13]
+ CRUSH rule 0 x 13 [3,5,12,7,9]
+ CRUSH rule 0 x 14 [13,5,2,7,10]
+ CRUSH rule 0 x 15 [15,1,9,6,13]
+ CRUSH rule 0 x 16 [7,11,14,2,13]
+ CRUSH rule 0 x 17 [10,1,13,2,4]
+ CRUSH rule 0 x 18 [1,7,3,10,5]
+ CRUSH rule 0 x 19 [7,12,2,4,15]
+ CRUSH rule 0 x 20 [14,12,3,10,9]
+ CRUSH rule 0 x 21 [3,12,1,10,4]
+ CRUSH rule 0 x 22 [6,3,13,11,4]
+ CRUSH rule 0 x 23 [10,5,13,9,3]
+ CRUSH rule 0 x 24 [12,11,3,1,9]
+ CRUSH rule 0 x 25 [7,12,15,1,3]
+ CRUSH rule 0 x 26 [1,7,13,2,14]
+ CRUSH rule 0 x 27 [3,6,15,4,13]
+ CRUSH rule 0 x 28 [14,4,3,9,6]
+ CRUSH rule 0 x 29 [5,14,12,11,6]
+ CRUSH rule 0 x 30 [2,5,6,9,1]
+ CRUSH rule 0 x 31 [5,15,10,1,9]
+ CRUSH rule 0 x 32 [9,10,2,1,13]
+ CRUSH rule 0 x 33 [13,4,9,2,7]
+ CRUSH rule 0 x 34 [13,15,2,4,1]
+ CRUSH rule 0 x 35 [4,14,3,13,10]
+ CRUSH rule 0 x 36 [3,12,9,7,5]
+ CRUSH rule 0 x 37 [9,2,6,14,11]
+ CRUSH rule 0 x 38 [3,4,13,10,9]
+ CRUSH rule 0 x 39 [12,7,14,11,1]
+ CRUSH rule 0 x 40 [10,1,9,5,15]
+ CRUSH rule 0 x 41 [4,9,11,1,14]
+ CRUSH rule 0 x 42 [3,6,14,10,12]
+ CRUSH rule 0 x 43 [10,5,15,7,2]
+ CRUSH rule 0 x 44 [11,4,13,3,7]
+ CRUSH rule 0 x 45 [11,12,15,9,1]
+ CRUSH rule 0 x 46 [6,9,2,14,11]
+ CRUSH rule 0 x 47 [3,9,6,4,13]
+ CRUSH rule 0 x 48 [4,6,2,1,10]
+ CRUSH rule 0 x 49 [9,15,10,7,4]
+ CRUSH rule 0 x 50 [14,12,1,4,2]
+ CRUSH rule 0 x 51 [10,6,5,12,15]
+ CRUSH rule 0 x 52 [12,1,9,11,7]
+ CRUSH rule 0 x 53 [3,6,13,9,5]
+ CRUSH rule 0 x 54 [4,13,9,2,14]
+ CRUSH rule 0 x 55 [4,11,2,7,1]
+ CRUSH rule 0 x 56 [5,9,10,1,3]
+ CRUSH rule 0 x 57 [6,2,1,15,10]
+ CRUSH rule 0 x 58 [7,1,11,4,3]
+ CRUSH rule 0 x 59 [2,13,1,10,9]
+ CRUSH rule 0 x 60 [3,6,11,1,4]
+ CRUSH rule 0 x 61 [3,15,13,7,4]
+ CRUSH rule 0 x 62 [15,11,7,12,5]
+ CRUSH rule 0 x 63 [10,14,12,1,7]
+ CRUSH rule 0 x 64 [3,9,1,4,7]
+ CRUSH rule 0 x 65 [4,12,11,7,14]
+ CRUSH rule 0 x 66 [15,11,6,9,4]
+ CRUSH rule 0 x 67 [2,6,4,14,1]
+ CRUSH rule 0 x 68 [15,7,4,2,9]
+ CRUSH rule 0 x 69 [2,1,15,10,4]
+ CRUSH rule 0 x 70 [9,6,1,3,13]
+ CRUSH rule 0 x 71 [15,5,1,3,13]
+ CRUSH rule 0 x 72 [9,10,3,5,7]
+ CRUSH rule 0 x 73 [5,3,11,1,7]
+ CRUSH rule 0 x 74 [11,7,9,5,1]
+ CRUSH rule 0 x 75 [9,7,11,14,12]
+ CRUSH rule 0 x 76 [6,1,3,5,14]
+ CRUSH rule 0 x 77 [7,4,2,13,9]
+ CRUSH rule 0 x 78 [9,3,1,5,6]
+ CRUSH rule 0 x 79 [13,2,15,5,7]
+ CRUSH rule 0 x 80 [15,2,6,4,13]
+ CRUSH rule 0 x 81 [15,2,1,11,4]
+ CRUSH rule 0 x 82 [14,13,5,11,6]
+ CRUSH rule 0 x 83 [4,15,3,9,10]
+ CRUSH rule 0 x 84 [10,7,9,15,3]
+ CRUSH rule 0 x 85 [3,15,9,7,4]
+ CRUSH rule 0 x 86 [10,9,14,1,13]
+ CRUSH rule 0 x 87 [15,10,7,12,5]
+ CRUSH rule 0 x 88 [4,13,3,1,9]
+ CRUSH rule 0 x 89 [3,9,7,4,1]
+ CRUSH rule 0 x 90 [4,9,7,12,11]
+ CRUSH rule 0 x 91 [6,11,9,1,2]
+ CRUSH rule 0 x 92 [1,5,10,9,13]
+ CRUSH rule 0 x 93 [9,3,15,13,7]
+ CRUSH rule 0 x 94 [9,2,12,5,6]
+ CRUSH rule 0 x 95 [7,15,4,10,9]
+ CRUSH rule 0 x 96 [2,15,11,7,5]
+ CRUSH rule 0 x 97 [4,11,2,13,1]
+ CRUSH rule 0 x 98 [11,13,9,3,15]
+ CRUSH rule 0 x 99 [12,4,11,7,3]
+ CRUSH rule 0 x 100 [9,4,10,15,7]
+ CRUSH rule 0 x 101 [15,7,1,9,10]
+ CRUSH rule 0 x 102 [3,11,14,6,13]
+ CRUSH rule 0 x 103 [13,11,6,14,4]
+ CRUSH rule 0 x 104 [14,6,3,5,9]
+ CRUSH rule 0 x 105 [14,10,1,9,3]
+ CRUSH rule 0 x 106 [6,5,13,2,14]
+ CRUSH rule 0 x 107 [3,1,10,14,13]
+ CRUSH rule 0 x 108 [5,10,7,2,15]
+ CRUSH rule 0 x 109 [9,1,13,7,15]
+ CRUSH rule 0 x 110 [5,1,11,3,7]
+ CRUSH rule 0 x 111 [10,1,9,7,5]
+ CRUSH rule 0 x 112 [1,10,4,14,2]
+ CRUSH rule 0 x 113 [6,10,13,9,1]
+ CRUSH rule 0 x 114 [5,13,6,2,1]
+ CRUSH rule 0 x 115 [10,13,14,3,9]
+ CRUSH rule 0 x 116 [1,14,13,2,11]
+ CRUSH rule 0 x 117 [5,6,1,12,15]
+ CRUSH rule 0 x 118 [10,4,13,15,9]
+ CRUSH rule 0 x 119 [14,12,11,4,6]
+ CRUSH rule 0 x 120 [11,3,14,13,4]
+ CRUSH rule 0 x 121 [9,5,1,11,7]
+ CRUSH rule 0 x 122 [4,3,14,1,11]
+ CRUSH rule 0 x 123 [3,10,5,6,9]
+ CRUSH rule 0 x 124 [12,2,1,5,14]
+ CRUSH rule 0 x 125 [9,12,15,1,6]
+ CRUSH rule 0 x 126 [7,15,10,9,2]
+ CRUSH rule 0 x 127 [4,14,9,13,1]
+ CRUSH rule 0 x 128 [3,12,1,10,4]
+ CRUSH rule 0 x 129 [11,13,14,2,9]
+ CRUSH rule 0 x 130 [3,13,5,14,10]
+ CRUSH rule 0 x 131 [12,1,6,15,4]
+ CRUSH rule 0 x 132 [11,15,13,9,2]
+ CRUSH rule 0 x 133 [3,6,9,11,15]
+ CRUSH rule 0 x 134 [12,5,6,15,3]
+ CRUSH rule 0 x 135 [3,14,12,4,6]
+ CRUSH rule 0 x 136 [15,6,9,4,10]
+ CRUSH rule 0 x 137 [14,3,6,11,1]
+ CRUSH rule 0 x 138 [13,15,4,10,2]
+ CRUSH rule 0 x 139 [11,2,13,9,1]
+ CRUSH rule 0 x 140 [11,4,12,15,2]
+ CRUSH rule 0 x 141 [6,12,15,11,3]
+ CRUSH rule 0 x 142 [3,14,7,9,11]
+ CRUSH rule 0 x 143 [9,6,4,2,14]
+ CRUSH rule 0 x 144 [13,7,11,2,14]
+ CRUSH rule 0 x 145 [12,2,6,10,9]
+ CRUSH rule 0 x 146 [1,5,9,2,6]
+ CRUSH rule 0 x 147 [1,4,9,11,2]
+ CRUSH rule 0 x 148 [12,7,9,2,14]
+ CRUSH rule 0 x 149 [2,5,9,12,11]
+ CRUSH rule 0 x 150 [1,15,2,10,7]
+ CRUSH rule 0 x 151 [2,9,14,7,1]
+ CRUSH rule 0 x 152 [5,9,2,6,10]
+ CRUSH rule 0 x 153 [6,9,4,15,2]
+ CRUSH rule 0 x 154 [3,11,7,1,4]
+ CRUSH rule 0 x 155 [14,12,7,3,5]
+ CRUSH rule 0 x 156 [7,13,3,10,15]
+ CRUSH rule 0 x 157 [15,1,6,4,3]
+ CRUSH rule 0 x 158 [15,1,10,6,12]
+ CRUSH rule 0 x 159 [4,14,3,12,10]
+ CRUSH rule 0 x 160 [5,7,3,14,11]
+ CRUSH rule 0 x 161 [1,2,11,4,6]
+ CRUSH rule 0 x 162 [10,6,1,12,2]
+ CRUSH rule 0 x 163 [15,1,10,2,6]
+ CRUSH rule 0 x 164 [9,14,10,7,12]
+ CRUSH rule 0 x 165 [11,7,2,13,9]
+ CRUSH rule 0 x 166 [1,2,12,14,4]
+ CRUSH rule 0 x 167 [9,7,3,4,11]
+ CRUSH rule 0 x 168 [13,2,4,1,6]
+ CRUSH rule 0 x 169 [1,4,9,14,13]
+ CRUSH rule 0 x 170 [1,15,7,9,12]
+ CRUSH rule 0 x 171 [9,2,10,7,1]
+ CRUSH rule 0 x 172 [14,4,10,12,9]
+ CRUSH rule 0 x 173 [5,10,12,15,6]
+ CRUSH rule 0 x 174 [15,6,4,12,1]
+ CRUSH rule 0 x 175 [5,7,9,3,10]
+ CRUSH rule 0 x 176 [9,6,3,14,13]
+ CRUSH rule 0 x 177 [2,9,10,13,4]
+ CRUSH rule 0 x 178 [12,11,7,14,3]
+ CRUSH rule 0 x 179 [2,10,13,9,5]
+ CRUSH rule 0 x 180 [3,11,5,15,7]
+ CRUSH rule 0 x 181 [9,12,6,5,1]
+ CRUSH rule 0 x 182 [5,13,11,2,1]
+ CRUSH rule 0 x 183 [5,7,10,13,3]
+ CRUSH rule 0 x 184 [2,5,11,12,7]
+ CRUSH rule 0 x 185 [13,5,7,11,2]
+ CRUSH rule 0 x 186 [6,14,13,5,10]
+ CRUSH rule 0 x 187 [1,4,11,13,6]
+ CRUSH rule 0 x 188 [9,13,5,14,10]
+ CRUSH rule 0 x 189 [6,12,4,9,2]
+ CRUSH rule 0 x 190 [9,13,15,10,3]
+ CRUSH rule 0 x 191 [7,11,4,1,15]
+ CRUSH rule 0 x 192 [2,11,5,15,6]
+ CRUSH rule 0 x 193 [3,13,6,10,4]
+ CRUSH rule 0 x 194 [3,13,4,14,6]
+ CRUSH rule 0 x 195 [5,7,10,12,1]
+ CRUSH rule 0 x 196 [4,15,1,10,9]
+ CRUSH rule 0 x 197 [14,10,13,4,6]
+ CRUSH rule 0 x 198 [2,5,6,15,9]
+ CRUSH rule 0 x 199 [2,10,4,15,1]
+ CRUSH rule 0 x 200 [7,14,11,4,1]
+ CRUSH rule 0 x 201 [9,14,1,7,4]
+ CRUSH rule 0 x 202 [14,11,7,3,5]
+ CRUSH rule 0 x 203 [12,5,7,15,1]
+ CRUSH rule 0 x 204 [6,11,3,12,14]
+ CRUSH rule 0 x 205 [15,4,6,10,13]
+ CRUSH rule 0 x 206 [13,11,2,15,7]
+ CRUSH rule 0 x 207 [2,11,7,4,14]
+ CRUSH rule 0 x 208 [13,1,6,14,9]
+ CRUSH rule 0 x 209 [6,15,13,1,11]
+ CRUSH rule 0 x 210 [13,11,2,7,5]
+ CRUSH rule 0 x 211 [2,14,1,13,11]
+ CRUSH rule 0 x 212 [10,1,12,15,5]
+ CRUSH rule 0 x 213 [3,9,6,5,15]
+ CRUSH rule 0 x 214 [7,15,4,1,10]
+ CRUSH rule 0 x 215 [6,1,4,13,3]
+ CRUSH rule 0 x 216 [12,9,6,2,1]
+ CRUSH rule 0 x 217 [12,11,1,14,2]
+ CRUSH rule 0 x 218 [12,10,15,6,1]
+ CRUSH rule 0 x 219 [3,11,14,6,4]
+ CRUSH rule 0 x 220 [14,4,3,12,10]
+ CRUSH rule 0 x 221 [15,5,2,6,12]
+ CRUSH rule 0 x 222 [10,4,3,15,7]
+ CRUSH rule 0 x 223 [9,7,11,1,4]
+ CRUSH rule 0 x 224 [1,7,10,2,12]
+ CRUSH rule 0 x 225 [10,5,2,6,1]
+ CRUSH rule 0 x 226 [4,1,9,3,13]
+ CRUSH rule 0 x 227 [7,2,12,15,5]
+ CRUSH rule 0 x 228 [2,15,11,1,6]
+ CRUSH rule 0 x 229 [9,3,7,14,1]
+ CRUSH rule 0 x 230 [10,5,7,2,15]
+ CRUSH rule 0 x 231 [2,7,5,13,9]
+ CRUSH rule 0 x 232 [10,5,13,1,9]
+ CRUSH rule 0 x 233 [6,12,11,4,9]
+ CRUSH rule 0 x 234 [10,1,2,12,5]
+ CRUSH rule 0 x 235 [13,14,7,10,1]
+ CRUSH rule 0 x 236 [2,15,9,12,1]
+ CRUSH rule 0 x 237 [3,12,9,10,4]
+ CRUSH rule 0 x 238 [2,10,4,15,6]
+ CRUSH rule 0 x 239 [4,15,10,7,9]
+ CRUSH rule 0 x 240 [15,5,13,7,2]
+ CRUSH rule 0 x 241 [7,9,15,12,1]
+ CRUSH rule 0 x 242 [14,2,6,9,10]
+ CRUSH rule 0 x 243 [2,11,5,1,15]
+ CRUSH rule 0 x 244 [13,9,15,3,11]
+ CRUSH rule 0 x 245 [12,9,15,3,1]
+ CRUSH rule 0 x 246 [15,3,5,11,7]
+ CRUSH rule 0 x 247 [6,4,9,12,1]
+ CRUSH rule 0 x 248 [5,13,7,11,9]
+ CRUSH rule 0 x 249 [10,14,7,3,9]
+ CRUSH rule 0 x 250 [12,15,1,10,5]
+ CRUSH rule 0 x 251 [13,2,15,5,6]
+ CRUSH rule 0 x 252 [7,5,13,9,3]
+ CRUSH rule 0 x 253 [3,13,15,10,7]
+ CRUSH rule 0 x 254 [2,9,13,14,4]
+ CRUSH rule 0 x 255 [1,9,13,2,6]
+ CRUSH rule 0 x 256 [6,9,13,1,3]
+ CRUSH rule 0 x 257 [15,12,3,9,6]
+ CRUSH rule 0 x 258 [12,5,6,10,2]
+ CRUSH rule 0 x 259 [9,10,4,3,14]
+ CRUSH rule 0 x 260 [10,12,6,9,3]
+ CRUSH rule 0 x 261 [13,7,2,1,15]
+ CRUSH rule 0 x 262 [15,3,12,7,4]
+ CRUSH rule 0 x 263 [12,6,10,9,5]
+ CRUSH rule 0 x 264 [13,14,11,3,1]
+ CRUSH rule 0 x 265 [12,10,14,5,7]
+ CRUSH rule 0 x 266 [14,7,11,1,2]
+ CRUSH rule 0 x 267 [12,11,6,5,1]
+ CRUSH rule 0 x 268 [4,1,15,12,6]
+ CRUSH rule 0 x 269 [11,1,15,5,13]
+ CRUSH rule 0 x 270 [7,11,12,3,1]
+ CRUSH rule 0 x 271 [4,7,3,13,15]
+ CRUSH rule 0 x 272 [15,5,13,10,6]
+ CRUSH rule 0 x 273 [2,10,7,12,1]
+ CRUSH rule 0 x 274 [10,2,5,6,13]
+ CRUSH rule 0 x 275 [10,3,4,7,14]
+ CRUSH rule 0 x 276 [5,12,9,2,11]
+ CRUSH rule 0 x 277 [14,3,13,4,1]
+ CRUSH rule 0 x 278 [5,6,14,3,1]
+ CRUSH rule 0 x 279 [6,10,13,3,9]
+ CRUSH rule 0 x 280 [7,3,14,9,1]
+ CRUSH rule 0 x 281 [5,11,14,7,9]
+ CRUSH rule 0 x 282 [2,1,13,14,9]
+ CRUSH rule 0 x 283 [4,1,12,3,10]
+ CRUSH rule 0 x 284 [5,11,7,15,3]
+ CRUSH rule 0 x 285 [15,5,3,1,6]
+ CRUSH rule 0 x 286 [10,4,3,6,12]
+ CRUSH rule 0 x 287 [12,4,9,1,3]
+ CRUSH rule 0 x 288 [4,12,10,7,1]
+ CRUSH rule 0 x 289 [2,5,14,9,13]
+ CRUSH rule 0 x 290 [12,2,5,6,15]
+ CRUSH rule 0 x 291 [7,11,1,14,5]
+ CRUSH rule 0 x 292 [4,10,6,3,14]
+ CRUSH rule 0 x 293 [6,5,11,1,2]
+ CRUSH rule 0 x 294 [9,12,3,14,6]
+ CRUSH rule 0 x 295 [6,10,3,14,9]
+ CRUSH rule 0 x 296 [3,1,13,7,14]
+ CRUSH rule 0 x 297 [6,13,4,14,10]
+ CRUSH rule 0 x 298 [14,9,13,1,4]
+ CRUSH rule 0 x 299 [14,12,11,6,4]
+ CRUSH rule 0 x 300 [15,7,10,5,1]
+ CRUSH rule 0 x 301 [9,11,7,1,13]
+ CRUSH rule 0 x 302 [9,7,1,13,5]
+ CRUSH rule 0 x 303 [4,13,3,7,10]
+ CRUSH rule 0 x 304 [6,9,2,11,15]
+ CRUSH rule 0 x 305 [13,7,5,11,2]
+ CRUSH rule 0 x 306 [10,12,4,6,9]
+ CRUSH rule 0 x 307 [11,12,15,5,6]
+ CRUSH rule 0 x 308 [12,14,10,9,1]
+ CRUSH rule 0 x 309 [9,3,12,5,11]
+ CRUSH rule 0 x 310 [3,1,5,10,14]
+ CRUSH rule 0 x 311 [3,9,7,1,14]
+ CRUSH rule 0 x 312 [15,13,9,7,5]
+ CRUSH rule 0 x 313 [9,15,3,7,5]
+ CRUSH rule 0 x 314 [2,15,9,5,6]
+ CRUSH rule 0 x 315 [15,2,13,1,11]
+ CRUSH rule 0 x 316 [4,9,11,2,12]
+ CRUSH rule 0 x 317 [1,5,3,13,15]
+ CRUSH rule 0 x 318 [4,1,15,11,9]
+ CRUSH rule 0 x 319 [2,15,4,1,11]
+ CRUSH rule 0 x 320 [5,7,13,9,11]
+ CRUSH rule 0 x 321 [1,6,11,15,5]
+ CRUSH rule 0 x 322 [13,7,5,3,14]
+ CRUSH rule 0 x 323 [7,4,10,1,2]
+ CRUSH rule 0 x 324 [5,6,10,15,2]
+ CRUSH rule 0 x 325 [9,10,14,5,1]
+ CRUSH rule 0 x 326 [11,7,13,4,2]
+ CRUSH rule 0 x 327 [12,5,10,14,3]
+ CRUSH rule 0 x 328 [5,2,6,14,1]
+ CRUSH rule 0 x 329 [2,6,15,5,9]
+ CRUSH rule 0 x 330 [3,9,11,13,1]
+ CRUSH rule 0 x 331 [12,14,6,3,1]
+ CRUSH rule 0 x 332 [10,12,6,15,9]
+ CRUSH rule 0 x 333 [6,5,3,12,14]
+ CRUSH rule 0 x 334 [4,9,2,12,7]
+ CRUSH rule 0 x 335 [11,7,1,5,13]
+ CRUSH rule 0 x 336 [6,14,13,2,5]
+ CRUSH rule 0 x 337 [15,11,3,7,12]
+ CRUSH rule 0 x 338 [10,5,3,6,15]
+ CRUSH rule 0 x 339 [11,14,13,5,3]
+ CRUSH rule 0 x 340 [11,6,12,4,9]
+ CRUSH rule 0 x 341 [7,5,2,10,14]
+ CRUSH rule 0 x 342 [12,14,1,9,2]
+ CRUSH rule 0 x 343 [12,14,9,6,10]
+ CRUSH rule 0 x 344 [9,11,5,2,14]
+ CRUSH rule 0 x 345 [14,2,11,9,6]
+ CRUSH rule 0 x 346 [5,3,14,10,7]
+ CRUSH rule 0 x 347 [10,2,12,6,9]
+ CRUSH rule 0 x 348 [7,9,10,1,14]
+ CRUSH rule 0 x 349 [9,6,10,12,1]
+ CRUSH rule 0 x 350 [13,9,15,4,10]
+ CRUSH rule 0 x 351 [13,5,15,3,1]
+ CRUSH rule 0 x 352 [1,12,11,9,4]
+ CRUSH rule 0 x 353 [10,14,12,2,9]
+ CRUSH rule 0 x 354 [6,3,15,10,9]
+ CRUSH rule 0 x 355 [13,14,6,10,2]
+ CRUSH rule 0 x 356 [15,13,2,9,6]
+ CRUSH rule 0 x 357 [4,11,1,13,3]
+ CRUSH rule 0 x 358 [12,7,2,9,1]
+ CRUSH rule 0 x 359 [5,15,7,11,3]
+ CRUSH rule 0 x 360 [13,10,1,2,6]
+ CRUSH rule 0 x 361 [5,3,13,6,1]
+ CRUSH rule 0 x 362 [2,9,11,13,1]
+ CRUSH rule 0 x 363 [7,12,3,9,15]
+ CRUSH rule 0 x 364 [2,12,6,9,5]
+ CRUSH rule 0 x 365 [13,5,11,15,6]
+ CRUSH rule 0 x 366 [12,7,3,14,5]
+ CRUSH rule 0 x 367 [7,13,3,1,5]
+ CRUSH rule 0 x 368 [7,9,10,15,3]
+ CRUSH rule 0 x 369 [7,5,3,13,14]
+ CRUSH rule 0 x 370 [4,7,14,1,2]
+ CRUSH rule 0 x 371 [1,7,12,3,4]
+ CRUSH rule 0 x 372 [10,4,3,14,6]
+ CRUSH rule 0 x 373 [15,5,2,6,13]
+ CRUSH rule 0 x 374 [3,15,12,5,1]
+ CRUSH rule 0 x 375 [5,2,14,1,6]
+ CRUSH rule 0 x 376 [5,14,10,13,3]
+ CRUSH rule 0 x 377 [1,15,2,4,9]
+ CRUSH rule 0 x 378 [9,12,2,15,1]
+ CRUSH rule 0 x 379 [11,2,15,5,7]
+ CRUSH rule 0 x 380 [6,1,12,11,2]
+ CRUSH rule 0 x 381 [15,13,7,5,10]
+ CRUSH rule 0 x 382 [14,3,1,4,13]
+ CRUSH rule 0 x 383 [3,6,11,4,13]
+ CRUSH rule 0 x 384 [4,13,6,3,15]
+ CRUSH rule 0 x 385 [4,6,15,3,10]
+ CRUSH rule 0 x 386 [14,3,11,13,5]
+ CRUSH rule 0 x 387 [1,11,5,7,9]
+ CRUSH rule 0 x 388 [2,6,11,9,15]
+ CRUSH rule 0 x 389 [12,7,2,4,15]
+ CRUSH rule 0 x 390 [2,11,13,7,5]
+ CRUSH rule 0 x 391 [3,4,9,13,7]
+ CRUSH rule 0 x 392 [11,5,14,7,1]
+ CRUSH rule 0 x 393 [2,14,5,9,7]
+ CRUSH rule 0 x 394 [4,9,3,15,13]
+ CRUSH rule 0 x 395 [10,13,5,15,6]
+ CRUSH rule 0 x 396 [2,12,15,9,4]
+ CRUSH rule 0 x 397 [1,14,9,4,12]
+ CRUSH rule 0 x 398 [9,2,1,5,12]
+ CRUSH rule 0 x 399 [5,9,14,3,1]
+ CRUSH rule 0 x 400 [10,6,2,4,15]
+ CRUSH rule 0 x 401 [6,9,11,12,4]
+ CRUSH rule 0 x 402 [4,7,9,2,13]
+ CRUSH rule 0 x 403 [7,15,13,3,5]
+ CRUSH rule 0 x 404 [14,12,7,9,2]
+ CRUSH rule 0 x 405 [9,15,11,2,4]
+ CRUSH rule 0 x 406 [12,14,9,2,7]
+ CRUSH rule 0 x 407 [9,5,12,10,15]
+ CRUSH rule 0 x 408 [7,1,5,2,10]
+ CRUSH rule 0 x 409 [11,2,4,13,1]
+ CRUSH rule 0 x 410 [6,4,14,2,12]
+ CRUSH rule 0 x 411 [13,11,15,6,4]
+ CRUSH rule 0 x 412 [5,9,6,11,14]
+ CRUSH rule 0 x 413 [13,5,3,11,6]
+ CRUSH rule 0 x 414 [3,11,9,13,4]
+ CRUSH rule 0 x 415 [6,10,14,5,1]
+ CRUSH rule 0 x 416 [13,1,4,7,2]
+ CRUSH rule 0 x 417 [4,12,1,15,2]
+ CRUSH rule 0 x 418 [14,5,10,2,6]
+ CRUSH rule 0 x 419 [5,14,10,9,2]
+ CRUSH rule 0 x 420 [2,4,9,11,6]
+ CRUSH rule 0 x 421 [15,4,10,3,9]
+ CRUSH rule 0 x 422 [4,11,2,7,13]
+ CRUSH rule 0 x 423 [3,15,12,6,5]
+ CRUSH rule 0 x 424 [6,10,12,2,5]
+ CRUSH rule 0 x 425 [11,15,2,13,5]
+ CRUSH rule 0 x 426 [12,4,7,1,9]
+ CRUSH rule 0 x 427 [14,10,3,1,9]
+ CRUSH rule 0 x 428 [12,7,9,4,2]
+ CRUSH rule 0 x 429 [3,4,9,7,11]
+ CRUSH rule 0 x 430 [3,5,10,13,1]
+ CRUSH rule 0 x 431 [9,3,7,1,12]
+ CRUSH rule 0 x 432 [4,1,12,7,15]
+ CRUSH rule 0 x 433 [4,11,12,15,7]
+ CRUSH rule 0 x 434 [2,14,9,1,5]
+ CRUSH rule 0 x 435 [13,11,5,6,9]
+ CRUSH rule 0 x 436 [9,15,10,2,4]
+ CRUSH rule 0 x 437 [9,6,3,14,10]
+ CRUSH rule 0 x 438 [7,2,13,4,11]
+ CRUSH rule 0 x 439 [7,14,4,3,12]
+ CRUSH rule 0 x 440 [14,11,9,2,7]
+ CRUSH rule 0 x 441 [2,4,11,9,13]
+ CRUSH rule 0 x 442 [10,13,9,7,15]
+ CRUSH rule 0 x 443 [12,15,10,9,2]
+ CRUSH rule 0 x 444 [4,13,7,14,3]
+ CRUSH rule 0 x 445 [4,2,15,7,1]
+ CRUSH rule 0 x 446 [12,10,6,9,4]
+ CRUSH rule 0 x 447 [15,7,13,1,4]
+ CRUSH rule 0 x 448 [5,2,13,7,15]
+ CRUSH rule 0 x 449 [14,5,3,12,10]
+ CRUSH rule 0 x 450 [2,4,6,9,15]
+ CRUSH rule 0 x 451 [6,14,11,3,9]
+ CRUSH rule 0 x 452 [14,9,10,4,2]
+ CRUSH rule 0 x 453 [5,15,13,2,6]
+ CRUSH rule 0 x 454 [10,4,2,6,15]
+ CRUSH rule 0 x 455 [6,13,2,4,10]
+ CRUSH rule 0 x 456 [5,7,13,1,11]
+ CRUSH rule 0 x 457 [9,1,5,7,11]
+ CRUSH rule 0 x 458 [9,11,15,4,7]
+ CRUSH rule 0 x 459 [13,15,11,1,5]
+ CRUSH rule 0 x 460 [5,12,10,15,7]
+ CRUSH rule 0 x 461 [4,3,9,13,15]
+ CRUSH rule 0 x 462 [4,7,12,14,11]
+ CRUSH rule 0 x 463 [4,12,14,11,2]
+ CRUSH rule 0 x 464 [4,2,15,10,1]
+ CRUSH rule 0 x 465 [5,10,9,7,13]
+ CRUSH rule 0 x 466 [13,5,2,15,9]
+ CRUSH rule 0 x 467 [13,6,14,3,9]
+ CRUSH rule 0 x 468 [10,7,12,14,4]
+ CRUSH rule 0 x 469 [4,9,6,14,12]
+ CRUSH rule 0 x 470 [3,9,12,15,5]
+ CRUSH rule 0 x 471 [6,1,5,14,13]
+ CRUSH rule 0 x 472 [2,14,7,5,13]
+ CRUSH rule 0 x 473 [15,10,6,9,4]
+ CRUSH rule 0 x 474 [15,10,4,12,6]
+ CRUSH rule 0 x 475 [10,5,12,9,14]
+ CRUSH rule 0 x 476 [3,6,10,12,1]
+ CRUSH rule 0 x 477 [6,13,5,15,11]
+ CRUSH rule 0 x 478 [4,15,1,3,7]
+ CRUSH rule 0 x 479 [13,11,1,6,14]
+ CRUSH rule 0 x 480 [1,13,6,4,9]
+ CRUSH rule 0 x 481 [15,12,7,9,1]
+ CRUSH rule 0 x 482 [2,12,9,1,7]
+ CRUSH rule 0 x 483 [10,1,4,15,9]
+ CRUSH rule 0 x 484 [1,4,10,13,7]
+ CRUSH rule 0 x 485 [9,4,3,1,14]
+ CRUSH rule 0 x 486 [3,10,15,9,7]
+ CRUSH rule 0 x 487 [12,11,4,14,7]
+ CRUSH rule 0 x 488 [14,4,1,9,2]
+ CRUSH rule 0 x 489 [11,4,2,13,15]
+ CRUSH rule 0 x 490 [4,9,1,3,13]
+ CRUSH rule 0 x 491 [1,12,5,2,14]
+ CRUSH rule 0 x 492 [5,7,11,3,14]
+ CRUSH rule 0 x 493 [12,1,4,15,3]
+ CRUSH rule 0 x 494 [1,7,13,4,15]
+ CRUSH rule 0 x 495 [3,15,7,1,9]
+ CRUSH rule 0 x 496 [5,3,7,13,9]
+ CRUSH rule 0 x 497 [13,10,3,6,5]
+ CRUSH rule 0 x 498 [10,6,1,5,9]
+ CRUSH rule 0 x 499 [14,3,12,5,1]
+ CRUSH rule 0 x 500 [15,9,6,12,11]
+ CRUSH rule 0 x 501 [10,13,1,9,3]
+ CRUSH rule 0 x 502 [5,1,14,11,7]
+ CRUSH rule 0 x 503 [15,10,7,9,1]
+ CRUSH rule 0 x 504 [13,2,7,1,14]
+ CRUSH rule 0 x 505 [12,7,5,2,14]
+ CRUSH rule 0 x 506 [11,7,9,14,12]
+ CRUSH rule 0 x 507 [4,14,13,3,9]
+ CRUSH rule 0 x 508 [12,1,4,9,2]
+ CRUSH rule 0 x 509 [4,2,6,9,14]
+ CRUSH rule 0 x 510 [5,3,1,12,11]
+ CRUSH rule 0 x 511 [2,12,10,6,14]
+ CRUSH rule 0 x 512 [15,11,3,5,7]
+ CRUSH rule 0 x 513 [4,9,11,3,13]
+ CRUSH rule 0 x 514 [11,9,3,4,12]
+ CRUSH rule 0 x 515 [12,14,6,5,3]
+ CRUSH rule 0 x 516 [14,11,1,12,3]
+ CRUSH rule 0 x 517 [11,5,6,13,9]
+ CRUSH rule 0 x 518 [3,5,7,12,15]
+ CRUSH rule 0 x 519 [12,14,2,1,4]
+ CRUSH rule 0 x 520 [12,4,2,10,6]
+ CRUSH rule 0 x 521 [11,5,9,6,15]
+ CRUSH rule 0 x 522 [4,12,11,1,15]
+ CRUSH rule 0 x 523 [3,1,5,9,15]
+ CRUSH rule 0 x 524 [15,9,3,11,13]
+ CRUSH rule 0 x 525 [3,15,11,6,9]
+ CRUSH rule 0 x 526 [10,2,5,13,6]
+ CRUSH rule 0 x 527 [3,13,4,1,9]
+ CRUSH rule 0 x 528 [12,7,15,10,2]
+ CRUSH rule 0 x 529 [6,4,10,12,2]
+ CRUSH rule 0 x 530 [11,9,12,7,5]
+ CRUSH rule 0 x 531 [9,15,4,7,2]
+ CRUSH rule 0 x 532 [5,3,13,7,9]
+ CRUSH rule 0 x 533 [12,15,1,2,7]
+ CRUSH rule 0 x 534 [11,9,3,7,15]
+ CRUSH rule 0 x 535 [11,1,3,5,14]
+ CRUSH rule 0 x 536 [9,1,14,13,4]
+ CRUSH rule 0 x 537 [15,5,13,2,7]
+ CRUSH rule 0 x 538 [13,5,11,2,6]
+ CRUSH rule 0 x 539 [10,12,6,14,1]
+ CRUSH rule 0 x 540 [12,15,7,3,9]
+ CRUSH rule 0 x 541 [2,1,6,11,14]
+ CRUSH rule 0 x 542 [3,9,15,5,11]
+ CRUSH rule 0 x 543 [4,10,9,3,6]
+ CRUSH rule 0 x 544 [3,15,9,11,7]
+ CRUSH rule 0 x 545 [14,10,7,12,4]
+ CRUSH rule 0 x 546 [5,15,13,7,1]
+ CRUSH rule 0 x 547 [5,13,7,9,3]
+ CRUSH rule 0 x 548 [11,7,12,15,4]
+ CRUSH rule 0 x 549 [14,1,4,9,13]
+ CRUSH rule 0 x 550 [9,15,3,13,1]
+ CRUSH rule 0 x 551 [11,2,15,6,13]
+ CRUSH rule 0 x 552 [2,11,14,1,9]
+ CRUSH rule 0 x 553 [11,9,14,6,4]
+ CRUSH rule 0 x 554 [11,14,6,4,13]
+ CRUSH rule 0 x 555 [6,5,10,9,14]
+ CRUSH rule 0 x 556 [15,6,3,13,11]
+ CRUSH rule 0 x 557 [12,2,5,14,10]
+ CRUSH rule 0 x 558 [12,1,6,15,5]
+ CRUSH rule 0 x 559 [2,13,5,10,14]
+ CRUSH rule 0 x 560 [4,9,12,6,3]
+ CRUSH rule 0 x 561 [12,7,1,2,5]
+ CRUSH rule 0 x 562 [7,13,9,14,2]
+ CRUSH rule 0 x 563 [15,4,3,10,13]
+ CRUSH rule 0 x 564 [2,13,7,1,15]
+ CRUSH rule 0 x 565 [3,12,4,1,14]
+ CRUSH rule 0 x 566 [6,14,4,2,13]
+ CRUSH rule 0 x 567 [15,4,11,6,3]
+ CRUSH rule 0 x 568 [4,14,1,6,10]
+ CRUSH rule 0 x 569 [11,3,15,13,5]
+ CRUSH rule 0 x 570 [1,10,13,4,7]
+ CRUSH rule 0 x 571 [10,12,14,9,4]
+ CRUSH rule 0 x 572 [12,14,3,10,6]
+ CRUSH rule 0 x 573 [7,15,11,2,12]
+ CRUSH rule 0 x 574 [11,14,13,1,3]
+ CRUSH rule 0 x 575 [5,13,15,9,6]
+ CRUSH rule 0 x 576 [3,15,11,9,1]
+ CRUSH rule 0 x 577 [13,9,6,15,3]
+ CRUSH rule 0 x 578 [4,10,1,2,7]
+ CRUSH rule 0 x 579 [13,1,15,2,10]
+ CRUSH rule 0 x 580 [3,12,4,1,10]
+ CRUSH rule 0 x 581 [7,14,12,10,1]
+ CRUSH rule 0 x 582 [10,5,13,14,1]
+ CRUSH rule 0 x 583 [4,15,1,9,10]
+ CRUSH rule 0 x 584 [10,1,5,13,6]
+ CRUSH rule 0 x 585 [5,3,6,1,11]
+ CRUSH rule 0 x 586 [7,10,14,12,9]
+ CRUSH rule 0 x 587 [11,6,9,4,1]
+ CRUSH rule 0 x 588 [3,12,7,15,4]
+ CRUSH rule 0 x 589 [9,7,12,1,10]
+ CRUSH rule 0 x 590 [12,1,3,9,10]
+ CRUSH rule 0 x 591 [2,6,14,13,9]
+ CRUSH rule 0 x 592 [15,12,9,7,5]
+ CRUSH rule 0 x 593 [13,14,5,11,9]
+ CRUSH rule 0 x 594 [12,14,2,9,7]
+ CRUSH rule 0 x 595 [12,7,10,3,1]
+ CRUSH rule 0 x 596 [2,7,12,11,1]
+ CRUSH rule 0 x 597 [15,1,2,10,7]
+ CRUSH rule 0 x 598 [11,5,9,14,12]
+ CRUSH rule 0 x 599 [13,11,1,5,6]
+ CRUSH rule 0 x 600 [4,12,3,10,9]
+ CRUSH rule 0 x 601 [13,5,15,2,1]
+ CRUSH rule 0 x 602 [3,11,7,1,13]
+ CRUSH rule 0 x 603 [3,1,4,14,10]
+ CRUSH rule 0 x 604 [14,2,6,1,11]
+ CRUSH rule 0 x 605 [2,7,12,5,14]
+ CRUSH rule 0 x 606 [12,15,1,5,7]
+ CRUSH rule 0 x 607 [3,9,10,14,7]
+ CRUSH rule 0 x 608 [13,10,1,7,9]
+ CRUSH rule 0 x 609 [14,3,7,9,11]
+ CRUSH rule 0 x 610 [7,10,5,1,12]
+ CRUSH rule 0 x 611 [13,1,5,3,10]
+ CRUSH rule 0 x 612 [7,1,2,13,9]
+ CRUSH rule 0 x 613 [10,7,14,9,5]
+ CRUSH rule 0 x 614 [9,4,15,3,1]
+ CRUSH rule 0 x 615 [9,4,11,2,1]
+ CRUSH rule 0 x 616 [10,14,1,5,3]
+ CRUSH rule 0 x 617 [15,7,2,11,12]
+ CRUSH rule 0 x 618 [4,2,10,6,14]
+ CRUSH rule 0 x 619 [15,4,3,9,6]
+ CRUSH rule 0 x 620 [3,7,11,14,13]
+ CRUSH rule 0 x 621 [3,6,4,14,1]
+ CRUSH rule 0 x 622 [10,2,13,5,15]
+ CRUSH rule 0 x 623 [4,9,14,7,3]
+ CRUSH rule 0 x 624 [3,9,15,6,10]
+ CRUSH rule 0 x 625 [11,7,3,5,13]
+ CRUSH rule 0 x 626 [10,12,2,1,9]
+ CRUSH rule 0 x 627 [1,12,10,14,3]
+ CRUSH rule 0 x 628 [15,13,11,4,2]
+ CRUSH rule 0 x 629 [5,6,15,12,1]
+ CRUSH rule 0 x 630 [1,4,12,9,3]
+ CRUSH rule 0 x 631 [5,7,1,15,12]
+ CRUSH rule 0 x 632 [12,3,11,9,6]
+ CRUSH rule 0 x 633 [14,4,3,7,10]
+ CRUSH rule 0 x 634 [6,9,5,3,13]
+ CRUSH rule 0 x 635 [6,5,2,15,9]
+ CRUSH rule 0 x 636 [13,6,11,3,15]
+ CRUSH rule 0 x 637 [3,1,10,6,9]
+ CRUSH rule 0 x 638 [10,15,3,5,13]
+ CRUSH rule 0 x 639 [6,9,14,4,3]
+ CRUSH rule 0 x 640 [9,6,1,11,14]
+ CRUSH rule 0 x 641 [10,6,5,14,1]
+ CRUSH rule 0 x 642 [1,15,4,6,2]
+ CRUSH rule 0 x 643 [3,7,5,1,10]
+ CRUSH rule 0 x 644 [15,13,6,9,3]
+ CRUSH rule 0 x 645 [14,2,4,9,10]
+ CRUSH rule 0 x 646 [5,13,14,1,6]
+ CRUSH rule 0 x 647 [10,1,9,13,6]
+ CRUSH rule 0 x 648 [6,5,2,14,11]
+ CRUSH rule 0 x 649 [3,9,13,11,4]
+ CRUSH rule 0 x 650 [10,9,4,15,12]
+ CRUSH rule 0 x 651 [3,9,5,7,14]
+ CRUSH rule 0 x 652 [15,9,4,6,13]
+ CRUSH rule 0 x 653 [11,14,1,3,6]
+ CRUSH rule 0 x 654 [13,6,2,10,15]
+ CRUSH rule 0 x 655 [6,3,4,15,12]
+ CRUSH rule 0 x 656 [3,15,1,4,6]
+ CRUSH rule 0 x 657 [11,15,3,5,7]
+ CRUSH rule 0 x 658 [7,2,10,12,1]
+ CRUSH rule 0 x 659 [2,5,14,6,10]
+ CRUSH rule 0 x 660 [13,14,10,6,4]
+ CRUSH rule 0 x 661 [7,15,3,12,11]
+ CRUSH rule 0 x 662 [15,2,12,5,1]
+ CRUSH rule 0 x 663 [14,9,13,10,5]
+ CRUSH rule 0 x 664 [6,10,12,4,9]
+ CRUSH rule 0 x 665 [2,9,12,1,7]
+ CRUSH rule 0 x 666 [12,3,6,1,15]
+ CRUSH rule 0 x 667 [1,9,12,10,2]
+ CRUSH rule 0 x 668 [9,5,1,2,6]
+ CRUSH rule 0 x 669 [9,7,14,5,11]
+ CRUSH rule 0 x 670 [6,10,9,13,1]
+ CRUSH rule 0 x 671 [6,15,5,10,13]
+ CRUSH rule 0 x 672 [2,9,13,1,4]
+ CRUSH rule 0 x 673 [7,10,5,9,15]
+ CRUSH rule 0 x 674 [7,12,10,1,14]
+ CRUSH rule 0 x 675 [9,5,1,10,6]
+ CRUSH rule 0 x 676 [10,12,2,1,4]
+ CRUSH rule 0 x 677 [2,12,1,4,10]
+ CRUSH rule 0 x 678 [1,2,4,10,12]
+ CRUSH rule 0 x 679 [5,6,12,15,9]
+ CRUSH rule 0 x 680 [7,11,3,1,15]
+ CRUSH rule 0 x 681 [6,4,3,11,14]
+ CRUSH rule 0 x 682 [6,1,11,15,12]
+ CRUSH rule 0 x 683 [6,13,2,4,9]
+ CRUSH rule 0 x 684 [9,11,3,7,15]
+ CRUSH rule 0 x 685 [5,1,15,7,9]
+ CRUSH rule 0 x 686 [1,9,11,14,6]
+ CRUSH rule 0 x 687 [7,13,3,5,11]
+ CRUSH rule 0 x 688 [11,9,1,14,3]
+ CRUSH rule 0 x 689 [5,2,9,12,1]
+ CRUSH rule 0 x 690 [9,7,10,3,13]
+ CRUSH rule 0 x 691 [11,15,9,5,7]
+ CRUSH rule 0 x 692 [15,5,1,2,9]
+ CRUSH rule 0 x 693 [5,6,12,15,2]
+ CRUSH rule 0 x 694 [4,7,1,10,12]
+ CRUSH rule 0 x 695 [6,13,14,10,9]
+ CRUSH rule 0 x 696 [1,2,4,14,7]
+ CRUSH rule 0 x 697 [13,11,3,6,4]
+ CRUSH rule 0 x 698 [11,13,4,2,6]
+ CRUSH rule 0 x 699 [7,14,12,4,2]
+ CRUSH rule 0 x 700 [12,14,11,9,4]
+ CRUSH rule 0 x 701 [3,13,1,14,4]
+ CRUSH rule 0 x 702 [3,12,15,6,5]
+ CRUSH rule 0 x 703 [15,11,13,3,4]
+ CRUSH rule 0 x 704 [6,4,2,15,11]
+ CRUSH rule 0 x 705 [14,6,11,5,1]
+ CRUSH rule 0 x 706 [1,12,3,6,4]
+ CRUSH rule 0 x 707 [4,7,14,3,10]
+ CRUSH rule 0 x 708 [3,10,5,1,15]
+ CRUSH rule 0 x 709 [11,12,3,7,5]
+ CRUSH rule 0 x 710 [14,2,11,9,5]
+ CRUSH rule 0 x 711 [14,3,9,10,12]
+ CRUSH rule 0 x 712 [12,3,11,15,9]
+ CRUSH rule 0 x 713 [11,9,3,15,13]
+ CRUSH rule 0 x 714 [12,1,9,7,2]
+ CRUSH rule 0 x 715 [6,1,14,4,11]
+ CRUSH rule 0 x 716 [11,13,9,14,5]
+ CRUSH rule 0 x 717 [12,4,10,9,15]
+ CRUSH rule 0 x 718 [7,15,5,2,11]
+ CRUSH rule 0 x 719 [5,15,13,3,1]
+ CRUSH rule 0 x 720 [4,13,10,2,7]
+ CRUSH rule 0 x 721 [11,3,14,9,1]
+ CRUSH rule 0 x 722 [2,4,6,1,9]
+ CRUSH rule 0 x 723 [2,1,12,15,11]
+ CRUSH rule 0 x 724 [7,1,9,10,5]
+ CRUSH rule 0 x 725 [11,12,7,15,4]
+ CRUSH rule 0 x 726 [7,14,4,3,11]
+ CRUSH rule 0 x 727 [2,5,1,11,15]
+ CRUSH rule 0 x 728 [13,11,4,6,15]
+ CRUSH rule 0 x 729 [15,11,4,6,2]
+ CRUSH rule 0 x 730 [3,7,1,13,11]
+ CRUSH rule 0 x 731 [9,1,6,5,2]
+ CRUSH rule 0 x 732 [1,2,10,13,9]
+ CRUSH rule 0 x 733 [11,3,5,6,1]
+ CRUSH rule 0 x 734 [14,3,11,7,12]
+ CRUSH rule 0 x 735 [6,9,2,10,13]
+ CRUSH rule 0 x 736 [3,9,1,11,7]
+ CRUSH rule 0 x 737 [1,4,2,12,9]
+ CRUSH rule 0 x 738 [11,15,7,4,9]
+ CRUSH rule 0 x 739 [11,12,6,2,4]
+ CRUSH rule 0 x 740 [7,9,10,13,1]
+ CRUSH rule 0 x 741 [12,11,7,15,2]
+ CRUSH rule 0 x 742 [9,7,4,11,12]
+ CRUSH rule 0 x 743 [5,13,9,15,10]
+ CRUSH rule 0 x 744 [6,2,13,1,14]
+ CRUSH rule 0 x 745 [3,6,1,4,11]
+ CRUSH rule 0 x 746 [3,7,9,10,14]
+ CRUSH rule 0 x 747 [15,11,5,2,13]
+ CRUSH rule 0 x 748 [6,10,13,2,14]
+ CRUSH rule 0 x 749 [14,9,10,7,5]
+ CRUSH rule 0 x 750 [1,14,6,5,11]
+ CRUSH rule 0 x 751 [15,1,6,9,5]
+ CRUSH rule 0 x 752 [13,1,7,3,11]
+ CRUSH rule 0 x 753 [4,11,1,3,15]
+ CRUSH rule 0 x 754 [14,12,11,4,2]
+ CRUSH rule 0 x 755 [13,6,1,10,4]
+ CRUSH rule 0 x 756 [3,4,14,6,1]
+ CRUSH rule 0 x 757 [10,6,1,4,13]
+ CRUSH rule 0 x 758 [6,3,4,10,15]
+ CRUSH rule 0 x 759 [5,7,3,14,11]
+ CRUSH rule 0 x 760 [1,15,10,12,4]
+ CRUSH rule 0 x 761 [2,12,1,14,5]
+ CRUSH rule 0 x 762 [1,4,10,9,3]
+ CRUSH rule 0 x 763 [4,13,1,14,7]
+ CRUSH rule 0 x 764 [1,14,6,13,9]
+ CRUSH rule 0 x 765 [9,15,2,13,4]
+ CRUSH rule 0 x 766 [11,2,7,15,9]
+ CRUSH rule 0 x 767 [6,11,4,3,12]
+ CRUSH rule 0 x 768 [2,12,15,7,1]
+ CRUSH rule 0 x 769 [15,1,9,2,11]
+ CRUSH rule 0 x 770 [15,13,4,6,3]
+ CRUSH rule 0 x 771 [9,2,12,11,6]
+ CRUSH rule 0 x 772 [4,3,13,11,14]
+ CRUSH rule 0 x 773 [3,7,4,15,1]
+ CRUSH rule 0 x 774 [12,6,3,15,5]
+ CRUSH rule 0 x 775 [5,10,14,2,6]
+ CRUSH rule 0 x 776 [10,15,3,9,6]
+ CRUSH rule 0 x 777 [11,13,4,7,1]
+ CRUSH rule 0 x 778 [13,1,9,11,15]
+ CRUSH rule 0 x 779 [5,11,1,14,2]
+ CRUSH rule 0 x 780 [13,9,3,6,4]
+ CRUSH rule 0 x 781 [5,7,14,3,1]
+ CRUSH rule 0 x 782 [2,15,9,7,11]
+ CRUSH rule 0 x 783 [12,7,5,14,9]
+ CRUSH rule 0 x 784 [14,1,10,13,3]
+ CRUSH rule 0 x 785 [6,12,1,2,4]
+ CRUSH rule 0 x 786 [10,5,2,15,1]
+ CRUSH rule 0 x 787 [1,12,10,2,9]
+ CRUSH rule 0 x 788 [4,2,9,13,6]
+ CRUSH rule 0 x 789 [9,2,14,7,4]
+ CRUSH rule 0 x 790 [15,2,7,4,1]
+ CRUSH rule 0 x 791 [9,4,7,13,14]
+ CRUSH rule 0 x 792 [6,4,15,10,12]
+ CRUSH rule 0 x 793 [15,9,6,2,13]
+ CRUSH rule 0 x 794 [5,12,2,14,9]
+ CRUSH rule 0 x 795 [6,14,12,4,10]
+ CRUSH rule 0 x 796 [11,2,12,6,15]
+ CRUSH rule 0 x 797 [14,3,7,1,5]
+ CRUSH rule 0 x 798 [5,11,6,13,1]
+ CRUSH rule 0 x 799 [2,9,14,4,13]
+ CRUSH rule 0 x 800 [6,3,4,11,15]
+ CRUSH rule 0 x 801 [2,5,6,13,9]
+ CRUSH rule 0 x 802 [1,4,12,7,3]
+ CRUSH rule 0 x 803 [7,2,4,1,11]
+ CRUSH rule 0 x 804 [5,14,9,7,3]
+ CRUSH rule 0 x 805 [13,4,3,1,10]
+ CRUSH rule 0 x 806 [6,2,13,4,15]
+ CRUSH rule 0 x 807 [14,2,7,4,9]
+ CRUSH rule 0 x 808 [2,15,12,7,9]
+ CRUSH rule 0 x 809 [1,11,7,12,4]
+ CRUSH rule 0 x 810 [2,5,9,12,15]
+ CRUSH rule 0 x 811 [15,6,3,10,1]
+ CRUSH rule 0 x 812 [7,11,2,14,9]
+ CRUSH rule 0 x 813 [4,10,13,14,2]
+ CRUSH rule 0 x 814 [13,4,9,3,10]
+ CRUSH rule 0 x 815 [15,12,9,4,10]
+ CRUSH rule 0 x 816 [14,10,13,7,3]
+ CRUSH rule 0 x 817 [10,7,2,15,13]
+ CRUSH rule 0 x 818 [15,2,11,4,1]
+ CRUSH rule 0 x 819 [5,12,10,6,1]
+ CRUSH rule 0 x 820 [3,6,9,12,11]
+ CRUSH rule 0 x 821 [15,10,9,13,3]
+ CRUSH rule 0 x 822 [10,13,2,9,7]
+ CRUSH rule 0 x 823 [2,6,12,10,15]
+ CRUSH rule 0 x 824 [3,7,9,13,15]
+ CRUSH rule 0 x 825 [10,5,14,6,12]
+ CRUSH rule 0 x 826 [5,2,11,15,1]
+ CRUSH rule 0 x 827 [13,5,1,3,7]
+ CRUSH rule 0 x 828 [12,6,10,5,1]
+ CRUSH rule 0 x 829 [13,6,15,10,5]
+ CRUSH rule 0 x 830 [15,13,2,9,7]
+ CRUSH rule 0 x 831 [1,4,11,12,6]
+ CRUSH rule 0 x 832 [14,11,13,2,9]
+ CRUSH rule 0 x 833 [9,13,3,11,7]
+ CRUSH rule 0 x 834 [9,7,5,1,11]
+ CRUSH rule 0 x 835 [14,3,13,6,4]
+ CRUSH rule 0 x 836 [3,9,10,13,1]
+ CRUSH rule 0 x 837 [15,12,11,2,7]
+ CRUSH rule 0 x 838 [12,14,9,2,5]
+ CRUSH rule 0 x 839 [3,4,6,10,15]
+ CRUSH rule 0 x 840 [10,15,12,4,7]
+ CRUSH rule 0 x 841 [3,5,7,12,11]
+ CRUSH rule 0 x 842 [9,13,2,6,5]
+ CRUSH rule 0 x 843 [14,7,4,9,3]
+ CRUSH rule 0 x 844 [7,1,4,15,9]
+ CRUSH rule 0 x 845 [13,6,1,15,4]
+ CRUSH rule 0 x 846 [3,7,15,13,1]
+ CRUSH rule 0 x 847 [12,15,11,5,2]
+ CRUSH rule 0 x 848 [11,13,1,14,5]
+ CRUSH rule 0 x 849 [3,15,11,9,6]
+ CRUSH rule 0 x 850 [1,3,10,6,14]
+ CRUSH rule 0 x 851 [14,4,3,6,11]
+ CRUSH rule 0 x 852 [9,12,4,7,15]
+ CRUSH rule 0 x 853 [13,14,6,11,2]
+ CRUSH rule 0 x 854 [7,11,12,1,4]
+ CRUSH rule 0 x 855 [14,4,12,6,3]
+ CRUSH rule 0 x 856 [5,10,7,3,15]
+ CRUSH rule 0 x 857 [4,3,13,11,9]
+ CRUSH rule 0 x 858 [5,15,6,3,9]
+ CRUSH rule 0 x 859 [5,15,6,2,1]
+ CRUSH rule 0 x 860 [11,14,1,12,6]
+ CRUSH rule 0 x 861 [13,7,4,10,1]
+ CRUSH rule 0 x 862 [5,10,9,7,3]
+ CRUSH rule 0 x 863 [11,6,3,9,4]
+ CRUSH rule 0 x 864 [6,13,4,2,10]
+ CRUSH rule 0 x 865 [4,1,14,11,6]
+ CRUSH rule 0 x 866 [2,13,4,15,9]
+ CRUSH rule 0 x 867 [12,2,9,10,4]
+ CRUSH rule 0 x 868 [14,11,7,2,1]
+ CRUSH rule 0 x 869 [10,13,7,14,3]
+ CRUSH rule 0 x 870 [14,9,11,4,3]
+ CRUSH rule 0 x 871 [6,2,1,4,15]
+ CRUSH rule 0 x 872 [6,1,15,3,10]
+ CRUSH rule 0 x 873 [2,5,12,10,1]
+ CRUSH rule 0 x 874 [12,4,7,2,15]
+ CRUSH rule 0 x 875 [10,6,14,1,12]
+ CRUSH rule 0 x 876 [14,7,13,3,9]
+ CRUSH rule 0 x 877 [15,11,13,9,5]
+ CRUSH rule 0 x 878 [7,14,3,13,9]
+ CRUSH rule 0 x 879 [12,2,7,4,10]
+ CRUSH rule 0 x 880 [2,12,10,7,1]
+ CRUSH rule 0 x 881 [6,3,1,11,4]
+ CRUSH rule 0 x 882 [11,13,7,1,2]
+ CRUSH rule 0 x 883 [13,1,3,10,6]
+ CRUSH rule 0 x 884 [6,15,4,9,3]
+ CRUSH rule 0 x 885 [14,7,9,4,2]
+ CRUSH rule 0 x 886 [13,11,4,2,1]
+ CRUSH rule 0 x 887 [14,4,12,11,2]
+ CRUSH rule 0 x 888 [10,12,7,15,9]
+ CRUSH rule 0 x 889 [15,13,4,1,6]
+ CRUSH rule 0 x 890 [10,12,14,2,9]
+ CRUSH rule 0 x 891 [9,5,11,6,3]
+ CRUSH rule 0 x 892 [12,15,2,4,7]
+ CRUSH rule 0 x 893 [1,3,5,9,6]
+ CRUSH rule 0 x 894 [7,2,11,13,4]
+ CRUSH rule 0 x 895 [2,1,11,5,7]
+ CRUSH rule 0 x 896 [9,1,14,10,4]
+ CRUSH rule 0 x 897 [7,5,14,3,1]
+ CRUSH rule 0 x 898 [10,6,12,9,15]
+ CRUSH rule 0 x 899 [1,11,5,3,13]
+ CRUSH rule 0 x 900 [2,9,10,7,13]
+ CRUSH rule 0 x 901 [9,12,11,3,14]
+ CRUSH rule 0 x 902 [4,2,6,15,12]
+ CRUSH rule 0 x 903 [14,10,3,1,12]
+ CRUSH rule 0 x 904 [15,12,4,9,6]
+ CRUSH rule 0 x 905 [12,6,11,3,9]
+ CRUSH rule 0 x 906 [14,11,12,2,4]
+ CRUSH rule 0 x 907 [7,12,3,9,10]
+ CRUSH rule 0 x 908 [2,15,9,6,10]
+ CRUSH rule 0 x 909 [10,14,1,13,2]
+ CRUSH rule 0 x 910 [12,7,4,15,10]
+ CRUSH rule 0 x 911 [11,15,2,4,9]
+ CRUSH rule 0 x 912 [6,4,14,13,3]
+ CRUSH rule 0 x 913 [4,6,10,1,12]
+ CRUSH rule 0 x 914 [4,15,2,10,1]
+ CRUSH rule 0 x 915 [12,14,1,9,4]
+ CRUSH rule 0 x 916 [3,1,11,5,6]
+ CRUSH rule 0 x 917 [1,15,6,5,10]
+ CRUSH rule 0 x 918 [7,14,11,4,9]
+ CRUSH rule 0 x 919 [10,7,3,13,15]
+ CRUSH rule 0 x 920 [4,2,10,15,1]
+ CRUSH rule 0 x 921 [1,11,6,13,4]
+ CRUSH rule 0 x 922 [6,4,14,13,3]
+ CRUSH rule 0 x 923 [12,2,5,14,10]
+ CRUSH rule 0 x 924 [6,2,14,13,9]
+ CRUSH rule 0 x 925 [12,15,2,10,1]
+ CRUSH rule 0 x 926 [3,13,10,1,14]
+ CRUSH rule 0 x 927 [6,5,1,11,14]
+ CRUSH rule 0 x 928 [13,1,3,9,6]
+ CRUSH rule 0 x 929 [10,7,1,5,2]
+ CRUSH rule 0 x 930 [7,15,10,5,1]
+ CRUSH rule 0 x 931 [6,15,11,9,5]
+ CRUSH rule 0 x 932 [13,2,5,11,9]
+ CRUSH rule 0 x 933 [12,7,14,10,4]
+ CRUSH rule 0 x 934 [12,2,5,7,9]
+ CRUSH rule 0 x 935 [6,11,1,14,5]
+ CRUSH rule 0 x 936 [9,12,7,5,1]
+ CRUSH rule 0 x 937 [14,2,11,1,13]
+ CRUSH rule 0 x 938 [14,3,5,11,7]
+ CRUSH rule 0 x 939 [6,4,14,9,12]
+ CRUSH rule 0 x 940 [13,11,4,2,1]
+ CRUSH rule 0 x 941 [3,12,4,7,14]
+ CRUSH rule 0 x 942 [15,12,10,4,1]
+ CRUSH rule 0 x 943 [10,2,4,9,6]
+ CRUSH rule 0 x 944 [2,9,4,7,1]
+ CRUSH rule 0 x 945 [10,15,2,9,5]
+ CRUSH rule 0 x 946 [11,15,7,12,5]
+ CRUSH rule 0 x 947 [11,3,14,1,12]
+ CRUSH rule 0 x 948 [7,13,11,5,14]
+ CRUSH rule 0 x 949 [9,1,12,5,15]
+ CRUSH rule 0 x 950 [9,15,13,6,4]
+ CRUSH rule 0 x 951 [2,6,12,9,10]
+ CRUSH rule 0 x 952 [9,7,15,3,5]
+ CRUSH rule 0 x 953 [1,3,6,10,12]
+ CRUSH rule 0 x 954 [10,2,14,9,4]
+ CRUSH rule 0 x 955 [7,14,3,1,10]
+ CRUSH rule 0 x 956 [1,6,11,5,14]
+ CRUSH rule 0 x 957 [14,11,1,12,6]
+ CRUSH rule 0 x 958 [15,4,3,11,1]
+ CRUSH rule 0 x 959 [2,1,12,15,10]
+ CRUSH rule 0 x 960 [2,6,11,13,15]
+ CRUSH rule 0 x 961 [3,13,11,9,6]
+ CRUSH rule 0 x 962 [5,11,3,14,1]
+ CRUSH rule 0 x 963 [13,10,15,4,6]
+ CRUSH rule 0 x 964 [7,11,4,9,2]
+ CRUSH rule 0 x 965 [12,2,9,7,4]
+ CRUSH rule 0 x 966 [12,14,9,4,1]
+ CRUSH rule 0 x 967 [7,5,3,10,12]
+ CRUSH rule 0 x 968 [12,15,4,9,11]
+ CRUSH rule 0 x 969 [11,4,7,1,9]
+ CRUSH rule 0 x 970 [5,12,10,1,3]
+ CRUSH rule 0 x 971 [1,9,4,12,7]
+ CRUSH rule 0 x 972 [12,3,14,5,1]
+ CRUSH rule 0 x 973 [1,10,4,12,2]
+ CRUSH rule 0 x 974 [7,11,1,2,15]
+ CRUSH rule 0 x 975 [7,9,15,12,2]
+ CRUSH rule 0 x 976 [7,3,15,5,12]
+ CRUSH rule 0 x 977 [14,3,6,10,4]
+ CRUSH rule 0 x 978 [12,5,11,1,15]
+ CRUSH rule 0 x 979 [5,1,13,6,15]
+ CRUSH rule 0 x 980 [15,11,5,6,1]
+ CRUSH rule 0 x 981 [5,11,15,12,7]
+ CRUSH rule 0 x 982 [2,6,14,11,12]
+ CRUSH rule 0 x 983 [3,12,10,9,14]
+ CRUSH rule 0 x 984 [15,13,1,10,2]
+ CRUSH rule 0 x 985 [11,2,15,1,4]
+ CRUSH rule 0 x 986 [6,13,9,1,15]
+ CRUSH rule 0 x 987 [13,14,5,10,6]
+ CRUSH rule 0 x 988 [12,9,10,14,3]
+ CRUSH rule 0 x 989 [7,4,3,15,9]
+ CRUSH rule 0 x 990 [1,10,9,13,3]
+ CRUSH rule 0 x 991 [7,11,1,14,2]
+ CRUSH rule 0 x 992 [9,10,2,13,7]
+ CRUSH rule 0 x 993 [6,10,14,12,4]
+ CRUSH rule 0 x 994 [3,13,15,4,11]
+ CRUSH rule 0 x 995 [15,6,12,2,5]
+ CRUSH rule 0 x 996 [15,10,5,3,13]
+ CRUSH rule 0 x 997 [15,2,1,12,7]
+ CRUSH rule 0 x 998 [6,1,9,5,12]
+ CRUSH rule 0 x 999 [9,10,15,5,13]
+ CRUSH rule 0 x 1000 [14,2,9,4,12]
+ CRUSH rule 0 x 1001 [11,14,4,2,6]
+ CRUSH rule 0 x 1002 [1,10,14,2,9]
+ CRUSH rule 0 x 1003 [10,7,5,14,2]
+ CRUSH rule 0 x 1004 [15,1,4,6,10]
+ CRUSH rule 0 x 1005 [6,12,2,10,9]
+ CRUSH rule 0 x 1006 [10,12,15,1,2]
+ CRUSH rule 0 x 1007 [1,7,13,14,3]
+ CRUSH rule 0 x 1008 [7,4,9,11,3]
+ CRUSH rule 0 x 1009 [5,2,11,7,15]
+ CRUSH rule 0 x 1010 [10,2,15,6,9]
+ CRUSH rule 0 x 1011 [6,3,12,1,10]
+ CRUSH rule 0 x 1012 [12,6,9,15,3]
+ CRUSH rule 0 x 1013 [2,14,12,4,9]
+ CRUSH rule 0 x 1014 [1,13,7,2,10]
+ CRUSH rule 0 x 1015 [12,6,10,1,4]
+ CRUSH rule 0 x 1016 [10,13,14,3,5]
+ CRUSH rule 0 x 1017 [5,11,14,7,13]
+ CRUSH rule 0 x 1018 [13,11,14,1,9]
+ CRUSH rule 0 x 1019 [10,13,14,7,5]
+ CRUSH rule 0 x 1020 [3,1,13,4,10]
+ CRUSH rule 0 x 1021 [2,11,14,9,4]
+ CRUSH rule 0 x 1022 [15,5,7,2,12]
+ CRUSH rule 0 x 1023 [15,2,9,12,1]
+ rule 0 (replicated_ruleset) num_rep 5 result size == 5:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3,15,12,1]
+ CRUSH rule 0 x 1 [10,15,1,2,13,4]
+ CRUSH rule 0 x 2 [1,12,2,6,5,10]
+ CRUSH rule 0 x 3 [15,4,10,2,9,6]
+ CRUSH rule 0 x 4 [14,2,10,1,9,4]
+ CRUSH rule 0 x 5 [7,4,11,2,13,15]
+ CRUSH rule 0 x 6 [12,6,10,9,3,4]
+ CRUSH rule 0 x 7 [9,2,6,12,11,4]
+ CRUSH rule 0 x 8 [10,2,15,1,4,13]
+ CRUSH rule 0 x 9 [7,1,14,2,11,9]
+ CRUSH rule 0 x 10 [10,14,4,1,2,7]
+ CRUSH rule 0 x 11 [13,9,14,7,5,11]
+ CRUSH rule 0 x 12 [7,1,2,5,13,15]
+ CRUSH rule 0 x 13 [3,5,12,7,9,1]
+ CRUSH rule 0 x 14 [13,5,2,7,10,15]
+ CRUSH rule 0 x 15 [15,1,9,6,13,3]
+ CRUSH rule 0 x 16 [7,11,14,2,13,1]
+ CRUSH rule 0 x 17 [10,1,13,2,4,6]
+ CRUSH rule 0 x 18 [1,7,3,10,5,12]
+ CRUSH rule 0 x 19 [7,12,2,4,15,10]
+ CRUSH rule 0 x 20 [14,12,3,10,9,4]
+ CRUSH rule 0 x 21 [3,12,1,10,4,15]
+ CRUSH rule 0 x 22 [6,3,13,11,4,1]
+ CRUSH rule 0 x 23 [10,5,13,9,3,15]
+ CRUSH rule 0 x 24 [12,11,3,1,9,4]
+ CRUSH rule 0 x 25 [7,12,15,1,3,10]
+ CRUSH rule 0 x 26 [1,7,13,2,14,5]
+ CRUSH rule 0 x 27 [3,6,15,4,13,9]
+ CRUSH rule 0 x 28 [14,4,3,9,6,11]
+ CRUSH rule 0 x 29 [5,14,12,11,6,3]
+ CRUSH rule 0 x 30 [2,5,6,9,1,11]
+ CRUSH rule 0 x 31 [5,15,10,1,9,13]
+ CRUSH rule 0 x 32 [9,10,2,1,13,14]
+ CRUSH rule 0 x 33 [13,4,9,2,7,1]
+ CRUSH rule 0 x 34 [13,15,2,4,1,10]
+ CRUSH rule 0 x 35 [4,14,3,13,10,9]
+ CRUSH rule 0 x 36 [3,12,9,7,5,10]
+ CRUSH rule 0 x 37 [9,2,6,14,11,1]
+ CRUSH rule 0 x 38 [3,4,13,10,9,1]
+ CRUSH rule 0 x 39 [12,7,14,11,1,9]
+ CRUSH rule 0 x 40 [10,1,9,5,15,2]
+ CRUSH rule 0 x 41 [4,9,11,1,14,13]
+ CRUSH rule 0 x 42 [3,6,14,10,12,5]
+ CRUSH rule 0 x 43 [10,5,15,7,2,9]
+ CRUSH rule 0 x 44 [11,4,13,3,7,14]
+ CRUSH rule 0 x 45 [11,12,15,9,1,5]
+ CRUSH rule 0 x 46 [6,9,2,14,11,13]
+ CRUSH rule 0 x 47 [3,9,6,4,13,1]
+ CRUSH rule 0 x 48 [4,6,2,1,10,14]
+ CRUSH rule 0 x 49 [9,15,10,7,4,3]
+ CRUSH rule 0 x 50 [14,12,1,4,2,11]
+ CRUSH rule 0 x 51 [10,6,5,12,15,2]
+ CRUSH rule 0 x 52 [12,1,9,11,7,3]
+ CRUSH rule 0 x 53 [3,6,13,9,5,1]
+ CRUSH rule 0 x 54 [4,13,9,2,14,10]
+ CRUSH rule 0 x 55 [4,11,2,7,1,13]
+ CRUSH rule 0 x 56 [5,9,10,1,3,13]
+ CRUSH rule 0 x 57 [6,2,1,15,10,12]
+ CRUSH rule 0 x 58 [7,1,11,4,3,14]
+ CRUSH rule 0 x 59 [2,13,1,10,9,5]
+ CRUSH rule 0 x 60 [3,6,11,1,4,9]
+ CRUSH rule 0 x 61 [3,15,13,7,4,1]
+ CRUSH rule 0 x 62 [15,11,7,12,5,9]
+ CRUSH rule 0 x 63 [10,14,12,1,7,3]
+ CRUSH rule 0 x 64 [3,9,1,4,7,12]
+ CRUSH rule 0 x 65 [4,12,11,7,14,3]
+ CRUSH rule 0 x 66 [15,11,6,9,4,1]
+ CRUSH rule 0 x 67 [2,6,4,14,1,11]
+ CRUSH rule 0 x 68 [15,7,4,2,9,12]
+ CRUSH rule 0 x 69 [2,1,15,10,4,9]
+ CRUSH rule 0 x 70 [9,6,1,3,13,15]
+ CRUSH rule 0 x 71 [15,5,1,3,13,10]
+ CRUSH rule 0 x 72 [9,10,3,5,7,12]
+ CRUSH rule 0 x 73 [5,3,11,1,7,12]
+ CRUSH rule 0 x 74 [11,7,9,5,1,15]
+ CRUSH rule 0 x 75 [9,7,11,14,12,1]
+ CRUSH rule 0 x 76 [6,1,3,5,14,10]
+ CRUSH rule 0 x 77 [7,4,2,13,9,1]
+ CRUSH rule 0 x 78 [9,3,1,5,6,13]
+ CRUSH rule 0 x 79 [13,2,15,5,7,9]
+ CRUSH rule 0 x 80 [15,2,6,4,13,10]
+ CRUSH rule 0 x 81 [15,2,1,11,4,6]
+ CRUSH rule 0 x 82 [14,13,5,11,6,2]
+ CRUSH rule 0 x 83 [4,15,3,9,10,13]
+ CRUSH rule 0 x 84 [10,7,9,15,3,4]
+ CRUSH rule 0 x 85 [3,15,9,7,4,11]
+ CRUSH rule 0 x 86 [10,9,14,1,13,4]
+ CRUSH rule 0 x 87 [15,10,7,12,5,3]
+ CRUSH rule 0 x 88 [4,13,3,1,9,15]
+ CRUSH rule 0 x 89 [3,9,7,4,1,14]
+ CRUSH rule 0 x 90 [4,9,7,12,11,14]
+ CRUSH rule 0 x 91 [6,11,9,1,2,4]
+ CRUSH rule 0 x 92 [1,5,10,9,13,15]
+ CRUSH rule 0 x 93 [9,3,15,13,7,5]
+ CRUSH rule 0 x 94 [9,2,12,5,6,11]
+ CRUSH rule 0 x 95 [7,15,4,10,9,13]
+ CRUSH rule 0 x 96 [2,15,11,7,5,1]
+ CRUSH rule 0 x 97 [4,11,2,13,1,7]
+ CRUSH rule 0 x 98 [11,13,9,3,15,1]
+ CRUSH rule 0 x 99 [12,4,11,7,3,14]
+ CRUSH rule 0 x 100 [9,4,10,15,7,3]
+ CRUSH rule 0 x 101 [15,7,1,9,10,5]
+ CRUSH rule 0 x 102 [3,11,14,6,13,4]
+ CRUSH rule 0 x 103 [13,11,6,14,4,3]
+ CRUSH rule 0 x 104 [14,6,3,5,9,1]
+ CRUSH rule 0 x 105 [14,10,1,9,3,5]
+ CRUSH rule 0 x 106 [6,5,13,2,14,11]
+ CRUSH rule 0 x 107 [3,1,10,14,13,5]
+ CRUSH rule 0 x 108 [5,10,7,2,15,9]
+ CRUSH rule 0 x 109 [9,1,13,7,15,5]
+ CRUSH rule 0 x 110 [5,1,11,3,7,14]
+ CRUSH rule 0 x 111 [10,1,9,7,5,2]
+ CRUSH rule 0 x 112 [1,10,4,14,2,12]
+ CRUSH rule 0 x 113 [6,10,13,9,1,5]
+ CRUSH rule 0 x 114 [5,13,6,2,1,14]
+ CRUSH rule 0 x 115 [10,13,14,3,9,1]
+ CRUSH rule 0 x 116 [1,14,13,2,11,5]
+ CRUSH rule 0 x 117 [5,6,1,12,15,9]
+ CRUSH rule 0 x 118 [10,4,13,15,9,3]
+ CRUSH rule 0 x 119 [14,12,11,4,6,9]
+ CRUSH rule 0 x 120 [11,3,14,13,4,7]
+ CRUSH rule 0 x 121 [9,5,1,11,7,3]
+ CRUSH rule 0 x 122 [4,3,14,1,11,13]
+ CRUSH rule 0 x 123 [3,10,5,6,9,1]
+ CRUSH rule 0 x 124 [12,2,1,5,14,7]
+ CRUSH rule 0 x 125 [9,12,15,1,6,5]
+ CRUSH rule 0 x 126 [7,15,10,9,2,12]
+ CRUSH rule 0 x 127 [4,14,9,13,1,3]
+ CRUSH rule 0 x 128 [3,12,1,10,4,9]
+ CRUSH rule 0 x 129 [11,13,14,2,9,4]
+ CRUSH rule 0 x 130 [3,13,5,14,10,1]
+ CRUSH rule 0 x 131 [12,1,6,15,4,2]
+ CRUSH rule 0 x 132 [11,15,13,9,2,5]
+ CRUSH rule 0 x 133 [3,6,9,11,15,12]
+ CRUSH rule 0 x 134 [12,5,6,15,3,9]
+ CRUSH rule 0 x 135 [3,14,12,4,6,11]
+ CRUSH rule 0 x 136 [15,6,9,4,10,3]
+ CRUSH rule 0 x 137 [14,3,6,11,1,9]
+ CRUSH rule 0 x 138 [13,15,4,10,2,7]
+ CRUSH rule 0 x 139 [11,2,13,9,1,15]
+ CRUSH rule 0 x 140 [11,4,12,15,2,6]
+ CRUSH rule 0 x 141 [6,12,15,11,3,5]
+ CRUSH rule 0 x 142 [3,14,7,9,11,1]
+ CRUSH rule 0 x 143 [9,6,4,2,14,10]
+ CRUSH rule 0 x 144 [13,7,11,2,14,4]
+ CRUSH rule 0 x 145 [12,2,6,10,9,4]
+ CRUSH rule 0 x 146 [1,5,9,2,6,13]
+ CRUSH rule 0 x 147 [1,4,9,11,2,7]
+ CRUSH rule 0 x 148 [12,7,9,2,14,11]
+ CRUSH rule 0 x 149 [2,5,9,12,11,1]
+ CRUSH rule 0 x 150 [1,15,2,10,7,9]
+ CRUSH rule 0 x 151 [2,9,14,7,1,10]
+ CRUSH rule 0 x 152 [5,9,2,6,10,13]
+ CRUSH rule 0 x 153 [6,9,4,15,2,1]
+ CRUSH rule 0 x 154 [3,11,7,1,4,12]
+ CRUSH rule 0 x 155 [14,12,7,3,5,1]
+ CRUSH rule 0 x 156 [7,13,3,10,15,5]
+ CRUSH rule 0 x 157 [15,1,6,4,3,10]
+ CRUSH rule 0 x 158 [15,1,10,6,12,2]
+ CRUSH rule 0 x 159 [4,14,3,12,10,6]
+ CRUSH rule 0 x 160 [5,7,3,14,11,1]
+ CRUSH rule 0 x 161 [1,2,11,4,6,13]
+ CRUSH rule 0 x 162 [10,6,1,12,2,4]
+ CRUSH rule 0 x 163 [15,1,10,2,6,4]
+ CRUSH rule 0 x 164 [9,14,10,7,12,2]
+ CRUSH rule 0 x 165 [11,7,2,13,9,15]
+ CRUSH rule 0 x 166 [1,2,12,14,4,11]
+ CRUSH rule 0 x 167 [9,7,3,4,11,13]
+ CRUSH rule 0 x 168 [13,2,4,1,6,15]
+ CRUSH rule 0 x 169 [1,4,9,14,13,10]
+ CRUSH rule 0 x 170 [1,15,7,9,12,10]
+ CRUSH rule 0 x 171 [9,2,10,7,1,5]
+ CRUSH rule 0 x 172 [14,4,10,12,9,3]
+ CRUSH rule 0 x 173 [5,10,12,15,6,1]
+ CRUSH rule 0 x 174 [15,6,4,12,1,11]
+ CRUSH rule 0 x 175 [5,7,9,3,10,1]
+ CRUSH rule 0 x 176 [9,6,3,14,13,10]
+ CRUSH rule 0 x 177 [2,9,10,13,4,1]
+ CRUSH rule 0 x 178 [12,11,7,14,3,4]
+ CRUSH rule 0 x 179 [2,10,13,9,5,1]
+ CRUSH rule 0 x 180 [3,11,5,15,7,12]
+ CRUSH rule 0 x 181 [9,12,6,5,1,10]
+ CRUSH rule 0 x 182 [5,13,11,2,1,6]
+ CRUSH rule 0 x 183 [5,7,10,13,3,9]
+ CRUSH rule 0 x 184 [2,5,11,12,7,1]
+ CRUSH rule 0 x 185 [13,5,7,11,2,14]
+ CRUSH rule 0 x 186 [6,14,13,5,10,1]
+ CRUSH rule 0 x 187 [1,4,11,13,6,14]
+ CRUSH rule 0 x 188 [9,13,5,14,10,6]
+ CRUSH rule 0 x 189 [6,12,4,9,2,1]
+ CRUSH rule 0 x 190 [9,13,15,10,3,1]
+ CRUSH rule 0 x 191 [7,11,4,1,15,12]
+ CRUSH rule 0 x 192 [2,11,5,15,6,1]
+ CRUSH rule 0 x 193 [3,13,6,10,4,1]
+ CRUSH rule 0 x 194 [3,13,4,14,6,9]
+ CRUSH rule 0 x 195 [5,7,10,12,1,3]
+ CRUSH rule 0 x 196 [4,15,1,10,9,2]
+ CRUSH rule 0 x 197 [14,10,13,4,6,3]
+ CRUSH rule 0 x 198 [2,5,6,15,9,13]
+ CRUSH rule 0 x 199 [2,10,4,15,1,9]
+ CRUSH rule 0 x 200 [7,14,11,4,1,3]
+ CRUSH rule 0 x 201 [9,14,1,7,4,3]
+ CRUSH rule 0 x 202 [14,11,7,3,5,1]
+ CRUSH rule 0 x 203 [12,5,7,15,1,2]
+ CRUSH rule 0 x 204 [6,11,3,12,14,1]
+ CRUSH rule 0 x 205 [15,4,6,10,13,9]
+ CRUSH rule 0 x 206 [13,11,2,15,7,1]
+ CRUSH rule 0 x 207 [2,11,7,4,14,1]
+ CRUSH rule 0 x 208 [13,1,6,14,9,11]
+ CRUSH rule 0 x 209 [6,15,13,1,11,4]
+ CRUSH rule 0 x 210 [13,11,2,7,5,14]
+ CRUSH rule 0 x 211 [2,14,1,13,11,7]
+ CRUSH rule 0 x 212 [10,1,12,15,5,6]
+ CRUSH rule 0 x 213 [3,9,6,5,15,13]
+ CRUSH rule 0 x 214 [7,15,4,1,10,2]
+ CRUSH rule 0 x 215 [6,1,4,13,3,11]
+ CRUSH rule 0 x 216 [12,9,6,2,1,11]
+ CRUSH rule 0 x 217 [12,11,1,14,2,4]
+ CRUSH rule 0 x 218 [12,10,15,6,1,4]
+ CRUSH rule 0 x 219 [3,11,14,6,4,1]
+ CRUSH rule 0 x 220 [14,4,3,12,10,9]
+ CRUSH rule 0 x 221 [15,5,2,6,12,11]
+ CRUSH rule 0 x 222 [10,4,3,15,7,12]
+ CRUSH rule 0 x 223 [9,7,11,1,4,14]
+ CRUSH rule 0 x 224 [1,7,10,2,12,9]
+ CRUSH rule 0 x 225 [10,5,2,6,1,13]
+ CRUSH rule 0 x 226 [4,1,9,3,13,10]
+ CRUSH rule 0 x 227 [7,2,12,15,5,11]
+ CRUSH rule 0 x 228 [2,15,11,1,6,13]
+ CRUSH rule 0 x 229 [9,3,7,14,1,12]
+ CRUSH rule 0 x 230 [10,5,7,2,15,1]
+ CRUSH rule 0 x 231 [2,7,5,13,9,15]
+ CRUSH rule 0 x 232 [10,5,13,1,9,2]
+ CRUSH rule 0 x 233 [6,12,11,4,9,14]
+ CRUSH rule 0 x 234 [10,1,2,12,5,9]
+ CRUSH rule 0 x 235 [13,14,7,10,1,9]
+ CRUSH rule 0 x 236 [2,15,9,12,1,7]
+ CRUSH rule 0 x 237 [3,12,9,10,4,7]
+ CRUSH rule 0 x 238 [2,10,4,15,6,12]
+ CRUSH rule 0 x 239 [4,15,10,7,9,13]
+ CRUSH rule 0 x 240 [15,5,13,7,2,9]
+ CRUSH rule 0 x 241 [7,9,15,12,1,5]
+ CRUSH rule 0 x 242 [14,2,6,9,10,12]
+ CRUSH rule 0 x 243 [2,11,5,1,15,6]
+ CRUSH rule 0 x 244 [13,9,15,3,11,7]
+ CRUSH rule 0 x 245 [12,9,15,3,1,5]
+ CRUSH rule 0 x 246 [15,3,5,11,7,1]
+ CRUSH rule 0 x 247 [6,4,9,12,1,2]
+ CRUSH rule 0 x 248 [5,13,7,11,9,15]
+ CRUSH rule 0 x 249 [10,14,7,3,9,13]
+ CRUSH rule 0 x 250 [12,15,1,10,5,6]
+ CRUSH rule 0 x 251 [13,2,15,5,6,1]
+ CRUSH rule 0 x 252 [7,5,13,9,3,10]
+ CRUSH rule 0 x 253 [3,13,15,10,7,4]
+ CRUSH rule 0 x 254 [2,9,13,14,4,6]
+ CRUSH rule 0 x 255 [1,9,13,2,6,10]
+ CRUSH rule 0 x 256 [6,9,13,1,3,14]
+ CRUSH rule 0 x 257 [15,12,3,9,6,4]
+ CRUSH rule 0 x 258 [12,5,6,10,2,1]
+ CRUSH rule 0 x 259 [9,10,4,3,14,13]
+ CRUSH rule 0 x 260 [10,12,6,9,3,15]
+ CRUSH rule 0 x 261 [13,7,2,1,15,5]
+ CRUSH rule 0 x 262 [15,3,12,7,4,9]
+ CRUSH rule 0 x 263 [12,6,10,9,5,15]
+ CRUSH rule 0 x 264 [13,14,11,3,1,4]
+ CRUSH rule 0 x 265 [12,10,14,5,7,1]
+ CRUSH rule 0 x 266 [14,7,11,1,2,9]
+ CRUSH rule 0 x 267 [12,11,6,5,1,2]
+ CRUSH rule 0 x 268 [4,1,15,12,6,11]
+ CRUSH rule 0 x 269 [11,1,15,5,13,9]
+ CRUSH rule 0 x 270 [7,11,12,3,1,14]
+ CRUSH rule 0 x 271 [4,7,3,13,15,10]
+ CRUSH rule 0 x 272 [15,5,13,10,6,2]
+ CRUSH rule 0 x 273 [2,10,7,12,1,15]
+ CRUSH rule 0 x 274 [10,2,5,6,13,9]
+ CRUSH rule 0 x 275 [10,3,4,7,14,13]
+ CRUSH rule 0 x 276 [5,12,9,2,11,7]
+ CRUSH rule 0 x 277 [14,3,13,4,1,9]
+ CRUSH rule 0 x 278 [5,6,14,3,1,11]
+ CRUSH rule 0 x 279 [6,10,13,3,9,4]
+ CRUSH rule 0 x 280 [7,3,14,9,1,11]
+ CRUSH rule 0 x 281 [5,11,14,7,9,13]
+ CRUSH rule 0 x 282 [2,1,13,14,9,7]
+ CRUSH rule 0 x 283 [4,1,12,3,10,7]
+ CRUSH rule 0 x 284 [5,11,7,15,3,13]
+ CRUSH rule 0 x 285 [15,5,3,1,6,13]
+ CRUSH rule 0 x 286 [10,4,3,6,12,15]
+ CRUSH rule 0 x 287 [12,4,9,1,3,11]
+ CRUSH rule 0 x 288 [4,12,10,7,1,3]
+ CRUSH rule 0 x 289 [2,5,14,9,13,6]
+ CRUSH rule 0 x 290 [12,2,5,6,15,9]
+ CRUSH rule 0 x 291 [7,11,1,14,5,9]
+ CRUSH rule 0 x 292 [4,10,6,3,14,9]
+ CRUSH rule 0 x 293 [6,5,11,1,2,14]
+ CRUSH rule 0 x 294 [9,12,3,14,6,11]
+ CRUSH rule 0 x 295 [6,10,3,14,9,4]
+ CRUSH rule 0 x 296 [3,1,13,7,14,9]
+ CRUSH rule 0 x 297 [6,13,4,14,10,1]
+ CRUSH rule 0 x 298 [14,9,13,1,4,2]
+ CRUSH rule 0 x 299 [14,12,11,6,4,2]
+ CRUSH rule 0 x 300 [15,7,10,5,1,3]
+ CRUSH rule 0 x 301 [9,11,7,1,13,14]
+ CRUSH rule 0 x 302 [9,7,1,13,5,10]
+ CRUSH rule 0 x 303 [4,13,3,7,10,15]
+ CRUSH rule 0 x 304 [6,9,2,11,15,13]
+ CRUSH rule 0 x 305 [13,7,5,11,2,15]
+ CRUSH rule 0 x 306 [10,12,4,6,9,2]
+ CRUSH rule 0 x 307 [11,12,15,5,6,2]
+ CRUSH rule 0 x 308 [12,14,10,9,1,2]
+ CRUSH rule 0 x 309 [9,3,12,5,11,15]
+ CRUSH rule 0 x 310 [3,1,5,10,14,9]
+ CRUSH rule 0 x 311 [3,9,7,1,14,13]
+ CRUSH rule 0 x 312 [15,13,9,7,5,10]
+ CRUSH rule 0 x 313 [9,15,3,7,5,13]
+ CRUSH rule 0 x 314 [2,15,9,5,6,12]
+ CRUSH rule 0 x 315 [15,2,13,1,11,9]
+ CRUSH rule 0 x 316 [4,9,11,2,12,14]
+ CRUSH rule 0 x 317 [1,5,3,13,15,7]
+ CRUSH rule 0 x 318 [4,1,15,11,9,13]
+ CRUSH rule 0 x 319 [2,15,4,1,11,9]
+ CRUSH rule 0 x 320 [5,7,13,9,11,2]
+ CRUSH rule 0 x 321 [1,6,11,15,5,3]
+ CRUSH rule 0 x 322 [13,7,5,3,14,11]
+ CRUSH rule 0 x 323 [7,4,10,1,2,13]
+ CRUSH rule 0 x 324 [5,6,10,15,2,13]
+ CRUSH rule 0 x 325 [9,10,14,5,1,6]
+ CRUSH rule 0 x 326 [11,7,13,4,2,15]
+ CRUSH rule 0 x 327 [12,5,10,14,3,7]
+ CRUSH rule 0 x 328 [5,2,6,14,1,11]
+ CRUSH rule 0 x 329 [2,6,15,5,9,10]
+ CRUSH rule 0 x 330 [3,9,11,13,1,6]
+ CRUSH rule 0 x 331 [12,14,6,3,1,4]
+ CRUSH rule 0 x 332 [10,12,6,15,9,2]
+ CRUSH rule 0 x 333 [6,5,3,12,14,10]
+ CRUSH rule 0 x 334 [4,9,2,12,7,11]
+ CRUSH rule 0 x 335 [11,7,1,5,13,2]
+ CRUSH rule 0 x 336 [6,14,13,2,5,9]
+ CRUSH rule 0 x 337 [15,11,3,7,12,5]
+ CRUSH rule 0 x 338 [10,5,3,6,15,1]
+ CRUSH rule 0 x 339 [11,14,13,5,3,7]
+ CRUSH rule 0 x 340 [11,6,12,4,9,3]
+ CRUSH rule 0 x 341 [7,5,2,10,14,9]
+ CRUSH rule 0 x 342 [12,14,1,9,2,11]
+ CRUSH rule 0 x 343 [12,14,9,6,10,2]
+ CRUSH rule 0 x 344 [9,11,5,2,14,13]
+ CRUSH rule 0 x 345 [14,2,11,9,6,12]
+ CRUSH rule 0 x 346 [5,3,14,10,7,1]
+ CRUSH rule 0 x 347 [10,2,12,6,9,1]
+ CRUSH rule 0 x 348 [7,9,10,1,14,13]
+ CRUSH rule 0 x 349 [9,6,10,12,1,5]
+ CRUSH rule 0 x 350 [13,9,15,4,10,7]
+ CRUSH rule 0 x 351 [13,5,15,3,1,6]
+ CRUSH rule 0 x 352 [1,12,11,9,4,7]
+ CRUSH rule 0 x 353 [10,14,12,2,9,1]
+ CRUSH rule 0 x 354 [6,3,15,10,9,4]
+ CRUSH rule 0 x 355 [13,14,6,10,2,5]
+ CRUSH rule 0 x 356 [15,13,2,9,6,5]
+ CRUSH rule 0 x 357 [4,11,1,13,3,14]
+ CRUSH rule 0 x 358 [12,7,2,9,1,14]
+ CRUSH rule 0 x 359 [5,15,7,11,3,13]
+ CRUSH rule 0 x 360 [13,10,1,2,6,14]
+ CRUSH rule 0 x 361 [5,3,13,6,1,14]
+ CRUSH rule 0 x 362 [2,9,11,13,1,6]
+ CRUSH rule 0 x 363 [7,12,3,9,15,4]
+ CRUSH rule 0 x 364 [2,12,6,9,5,10]
+ CRUSH rule 0 x 365 [13,5,11,15,6,2]
+ CRUSH rule 0 x 366 [12,7,3,14,5,10]
+ CRUSH rule 0 x 367 [7,13,3,1,5,11]
+ CRUSH rule 0 x 368 [7,9,10,15,3,4]
+ CRUSH rule 0 x 369 [7,5,3,13,14,9]
+ CRUSH rule 0 x 370 [4,7,14,1,2,9]
+ CRUSH rule 0 x 371 [1,7,12,3,4,15]
+ CRUSH rule 0 x 372 [10,4,3,14,6,1]
+ CRUSH rule 0 x 373 [15,5,2,6,13,1]
+ CRUSH rule 0 x 374 [3,15,12,5,1,6]
+ CRUSH rule 0 x 375 [5,2,14,1,6,13]
+ CRUSH rule 0 x 376 [5,14,10,13,3,6]
+ CRUSH rule 0 x 377 [1,15,2,4,9,11]
+ CRUSH rule 0 x 378 [9,12,2,15,1,5]
+ CRUSH rule 0 x 379 [11,2,15,5,7,9]
+ CRUSH rule 0 x 380 [6,1,12,11,2,9]
+ CRUSH rule 0 x 381 [15,13,7,5,10,2]
+ CRUSH rule 0 x 382 [14,3,1,4,13,7]
+ CRUSH rule 0 x 383 [3,6,11,4,13,15]
+ CRUSH rule 0 x 384 [4,13,6,3,15,11]
+ CRUSH rule 0 x 385 [4,6,15,3,10,9]
+ CRUSH rule 0 x 386 [14,3,11,13,5,6]
+ CRUSH rule 0 x 387 [1,11,5,7,9,2]
+ CRUSH rule 0 x 388 [2,6,11,9,15,4]
+ CRUSH rule 0 x 389 [12,7,2,4,15,10]
+ CRUSH rule 0 x 390 [2,11,13,7,5,9]
+ CRUSH rule 0 x 391 [3,4,9,13,7,10]
+ CRUSH rule 0 x 392 [11,5,14,7,1,9]
+ CRUSH rule 0 x 393 [2,14,5,9,7,13]
+ CRUSH rule 0 x 394 [4,9,3,15,13,6]
+ CRUSH rule 0 x 395 [10,13,5,15,6,9]
+ CRUSH rule 0 x 396 [2,12,15,9,4,6]
+ CRUSH rule 0 x 397 [1,14,9,4,12,10]
+ CRUSH rule 0 x 398 [9,2,1,5,12,6]
+ CRUSH rule 0 x 399 [5,9,14,3,1,10]
+ CRUSH rule 0 x 400 [10,6,2,4,15,12]
+ CRUSH rule 0 x 401 [6,9,11,12,4,3]
+ CRUSH rule 0 x 402 [4,7,9,2,13,1]
+ CRUSH rule 0 x 403 [7,15,13,3,5,9]
+ CRUSH rule 0 x 404 [14,12,7,9,2,1]
+ CRUSH rule 0 x 405 [9,15,11,2,4,7]
+ CRUSH rule 0 x 406 [12,14,9,2,7,10]
+ CRUSH rule 0 x 407 [9,5,12,10,15,6]
+ CRUSH rule 0 x 408 [7,1,5,2,10,15]
+ CRUSH rule 0 x 409 [11,2,4,13,1,15]
+ CRUSH rule 0 x 410 [6,4,14,2,12,9]
+ CRUSH rule 0 x 411 [13,11,15,6,4,1]
+ CRUSH rule 0 x 412 [5,9,6,11,14,2]
+ CRUSH rule 0 x 413 [13,5,3,11,6,9]
+ CRUSH rule 0 x 414 [3,11,9,13,4,1]
+ CRUSH rule 0 x 415 [6,10,14,5,1,13]
+ CRUSH rule 0 x 416 [13,1,4,7,2,9]
+ CRUSH rule 0 x 417 [4,12,1,15,2,11]
+ CRUSH rule 0 x 418 [14,5,10,2,6,9]
+ CRUSH rule 0 x 419 [5,14,10,9,2,12]
+ CRUSH rule 0 x 420 [2,4,9,11,6,14]
+ CRUSH rule 0 x 421 [15,4,10,3,9,12]
+ CRUSH rule 0 x 422 [4,11,2,7,13,9]
+ CRUSH rule 0 x 423 [3,15,12,6,5,1]
+ CRUSH rule 0 x 424 [6,10,12,2,5,1]
+ CRUSH rule 0 x 425 [11,15,2,13,5,7]
+ CRUSH rule 0 x 426 [12,4,7,1,9,10]
+ CRUSH rule 0 x 427 [14,10,3,1,9,7]
+ CRUSH rule 0 x 428 [12,7,9,4,2,1]
+ CRUSH rule 0 x 429 [3,4,9,7,11,12]
+ CRUSH rule 0 x 430 [3,5,10,13,1,15]
+ CRUSH rule 0 x 431 [9,3,7,1,12,5]
+ CRUSH rule 0 x 432 [4,1,12,7,15,2]
+ CRUSH rule 0 x 433 [4,11,12,15,7,3]
+ CRUSH rule 0 x 434 [2,14,9,1,5,11]
+ CRUSH rule 0 x 435 [13,11,5,6,9,2]
+ CRUSH rule 0 x 436 [9,15,10,2,4,1]
+ CRUSH rule 0 x 437 [9,6,3,14,10,12]
+ CRUSH rule 0 x 438 [7,2,13,4,11,1]
+ CRUSH rule 0 x 439 [7,14,4,3,12,10]
+ CRUSH rule 0 x 440 [14,11,9,2,7,12]
+ CRUSH rule 0 x 441 [2,4,11,9,13,6]
+ CRUSH rule 0 x 442 [10,13,9,7,15,1]
+ CRUSH rule 0 x 443 [12,15,10,9,2,1]
+ CRUSH rule 0 x 444 [4,13,7,14,3,1]
+ CRUSH rule 0 x 445 [4,2,15,7,1,9]
+ CRUSH rule 0 x 446 [12,10,6,9,4,1]
+ CRUSH rule 0 x 447 [15,7,13,1,4,9]
+ CRUSH rule 0 x 448 [5,2,13,7,15,10]
+ CRUSH rule 0 x 449 [14,5,3,12,10,9]
+ CRUSH rule 0 x 450 [2,4,6,9,15,1]
+ CRUSH rule 0 x 451 [6,14,11,3,9,1]
+ CRUSH rule 0 x 452 [14,9,10,4,2,13]
+ CRUSH rule 0 x 453 [5,15,13,2,6,9]
+ CRUSH rule 0 x 454 [10,4,2,6,15,12]
+ CRUSH rule 0 x 455 [6,13,2,4,10,1]
+ CRUSH rule 0 x 456 [5,7,13,1,11,3]
+ CRUSH rule 0 x 457 [9,1,5,7,11,13]
+ CRUSH rule 0 x 458 [9,11,15,4,7,2]
+ CRUSH rule 0 x 459 [13,15,11,1,5,2]
+ CRUSH rule 0 x 460 [5,12,10,15,7,3]
+ CRUSH rule 0 x 461 [4,3,9,13,15,6]
+ CRUSH rule 0 x 462 [4,7,12,14,11,1]
+ CRUSH rule 0 x 463 [4,12,14,11,2,7]
+ CRUSH rule 0 x 464 [4,2,15,10,1,9]
+ CRUSH rule 0 x 465 [5,10,9,7,13,1]
+ CRUSH rule 0 x 466 [13,5,2,15,9,11]
+ CRUSH rule 0 x 467 [13,6,14,3,9,1]
+ CRUSH rule 0 x 468 [10,7,12,14,4,1]
+ CRUSH rule 0 x 469 [4,9,6,14,12,11]
+ CRUSH rule 0 x 470 [3,9,12,15,5,6]
+ CRUSH rule 0 x 471 [6,1,5,14,13,10]
+ CRUSH rule 0 x 472 [2,14,7,5,13,1]
+ CRUSH rule 0 x 473 [15,10,6,9,4,12]
+ CRUSH rule 0 x 474 [15,10,4,12,6,9]
+ CRUSH rule 0 x 475 [10,5,12,9,14,3]
+ CRUSH rule 0 x 476 [3,6,10,12,1,15]
+ CRUSH rule 0 x 477 [6,13,5,15,11,9]
+ CRUSH rule 0 x 478 [4,15,1,3,7,12]
+ CRUSH rule 0 x 479 [13,11,1,6,14,5]
+ CRUSH rule 0 x 480 [1,13,6,4,9,14]
+ CRUSH rule 0 x 481 [15,12,7,9,1,3]
+ CRUSH rule 0 x 482 [2,12,9,1,7,11]
+ CRUSH rule 0 x 483 [10,1,4,15,9,7]
+ CRUSH rule 0 x 484 [1,4,10,13,7,14]
+ CRUSH rule 0 x 485 [9,4,3,1,14,12]
+ CRUSH rule 0 x 486 [3,10,15,9,7,13]
+ CRUSH rule 0 x 487 [12,11,4,14,7,2]
+ CRUSH rule 0 x 488 [14,4,1,9,2,6]
+ CRUSH rule 0 x 489 [11,4,2,13,15,7]
+ CRUSH rule 0 x 490 [4,9,1,3,13,15]
+ CRUSH rule 0 x 491 [1,12,5,2,14,11]
+ CRUSH rule 0 x 492 [5,7,11,3,14,9]
+ CRUSH rule 0 x 493 [12,1,4,15,3,11]
+ CRUSH rule 0 x 494 [1,7,13,4,15,9]
+ CRUSH rule 0 x 495 [3,15,7,1,9,5]
+ CRUSH rule 0 x 496 [5,3,7,13,9,14]
+ CRUSH rule 0 x 497 [13,10,3,6,5,14]
+ CRUSH rule 0 x 498 [10,6,1,5,9,12]
+ CRUSH rule 0 x 499 [14,3,12,5,1,11]
+ CRUSH rule 0 x 500 [15,9,6,12,11,2]
+ CRUSH rule 0 x 501 [10,13,1,9,3,14]
+ CRUSH rule 0 x 502 [5,1,14,11,7,12]
+ CRUSH rule 0 x 503 [15,10,7,9,1,12]
+ CRUSH rule 0 x 504 [13,2,7,1,14,11]
+ CRUSH rule 0 x 505 [12,7,5,2,14,10]
+ CRUSH rule 0 x 506 [11,7,9,14,12,1]
+ CRUSH rule 0 x 507 [4,14,13,3,9,7]
+ CRUSH rule 0 x 508 [12,1,4,9,2,11]
+ CRUSH rule 0 x 509 [4,2,6,9,14,1]
+ CRUSH rule 0 x 510 [5,3,1,12,11,14]
+ CRUSH rule 0 x 511 [2,12,10,6,14,5]
+ CRUSH rule 0 x 512 [15,11,3,5,7,1]
+ CRUSH rule 0 x 513 [4,9,11,3,13,7]
+ CRUSH rule 0 x 514 [11,9,3,4,12,15]
+ CRUSH rule 0 x 515 [12,14,6,5,3,9]
+ CRUSH rule 0 x 516 [14,11,1,12,3,7]
+ CRUSH rule 0 x 517 [11,5,6,13,9,3]
+ CRUSH rule 0 x 518 [3,5,7,12,15,11]
+ CRUSH rule 0 x 519 [12,14,2,1,4,6]
+ CRUSH rule 0 x 520 [12,4,2,10,6,15]
+ CRUSH rule 0 x 521 [11,5,9,6,15,3]
+ CRUSH rule 0 x 522 [4,12,11,1,15,3]
+ CRUSH rule 0 x 523 [3,1,5,9,15,10]
+ CRUSH rule 0 x 524 [15,9,3,11,13,7]
+ CRUSH rule 0 x 525 [3,15,11,6,9,12]
+ CRUSH rule 0 x 526 [10,2,5,13,6,15]
+ CRUSH rule 0 x 527 [3,13,4,1,9,10]
+ CRUSH rule 0 x 528 [12,7,15,10,2,5]
+ CRUSH rule 0 x 529 [6,4,10,12,2,9]
+ CRUSH rule 0 x 530 [11,9,12,7,5,1]
+ CRUSH rule 0 x 531 [9,15,4,7,2,13]
+ CRUSH rule 0 x 532 [5,3,13,7,9,14]
+ CRUSH rule 0 x 533 [12,15,1,2,7,5]
+ CRUSH rule 0 x 534 [11,9,3,7,15,4]
+ CRUSH rule 0 x 535 [11,1,3,5,14,9]
+ CRUSH rule 0 x 536 [9,1,14,13,4,6]
+ CRUSH rule 0 x 537 [15,5,13,2,7,11]
+ CRUSH rule 0 x 538 [13,5,11,2,6,15]
+ CRUSH rule 0 x 539 [10,12,6,14,1,2]
+ CRUSH rule 0 x 540 [12,15,7,3,9,11]
+ CRUSH rule 0 x 541 [2,1,6,11,14,13]
+ CRUSH rule 0 x 542 [3,9,15,5,11,12]
+ CRUSH rule 0 x 543 [4,10,9,3,6,13]
+ CRUSH rule 0 x 544 [3,15,9,11,7,4]
+ CRUSH rule 0 x 545 [14,10,7,12,4,9]
+ CRUSH rule 0 x 546 [5,15,13,7,1,10]
+ CRUSH rule 0 x 547 [5,13,7,9,3,14]
+ CRUSH rule 0 x 548 [11,7,12,15,4,2]
+ CRUSH rule 0 x 549 [14,1,4,9,13,6]
+ CRUSH rule 0 x 550 [9,15,3,13,1,6]
+ CRUSH rule 0 x 551 [11,2,15,6,13,5]
+ CRUSH rule 0 x 552 [2,11,14,1,9,6]
+ CRUSH rule 0 x 553 [11,9,14,6,4,13]
+ CRUSH rule 0 x 554 [11,14,6,4,13,9]
+ CRUSH rule 0 x 555 [6,5,10,9,14,2]
+ CRUSH rule 0 x 556 [15,6,3,13,11,4]
+ CRUSH rule 0 x 557 [12,2,5,14,10,9]
+ CRUSH rule 0 x 558 [12,1,6,15,5,10]
+ CRUSH rule 0 x 559 [2,13,5,10,14,7]
+ CRUSH rule 0 x 560 [4,9,12,6,3,10]
+ CRUSH rule 0 x 561 [12,7,1,2,5,15]
+ CRUSH rule 0 x 562 [7,13,9,14,2,1]
+ CRUSH rule 0 x 563 [15,4,3,10,13,9]
+ CRUSH rule 0 x 564 [2,13,7,1,15,10]
+ CRUSH rule 0 x 565 [3,12,4,1,14,7]
+ CRUSH rule 0 x 566 [6,14,4,2,13,11]
+ CRUSH rule 0 x 567 [15,4,11,6,3,12]
+ CRUSH rule 0 x 568 [4,14,1,6,10,13]
+ CRUSH rule 0 x 569 [11,3,15,13,5,1]
+ CRUSH rule 0 x 570 [1,10,13,4,7,2]
+ CRUSH rule 0 x 571 [10,12,14,9,4,2]
+ CRUSH rule 0 x 572 [12,14,3,10,6,1]
+ CRUSH rule 0 x 573 [7,15,11,2,12,9]
+ CRUSH rule 0 x 574 [11,14,13,1,3,7]
+ CRUSH rule 0 x 575 [5,13,15,9,6,10]
+ CRUSH rule 0 x 576 [3,15,11,9,1,6]
+ CRUSH rule 0 x 577 [13,9,6,15,3,11]
+ CRUSH rule 0 x 578 [4,10,1,2,7,13]
+ CRUSH rule 0 x 579 [13,1,15,2,10,7]
+ CRUSH rule 0 x 580 [3,12,4,1,10,15]
+ CRUSH rule 0 x 581 [7,14,12,10,1,2]
+ CRUSH rule 0 x 582 [10,5,13,14,1,2]
+ CRUSH rule 0 x 583 [4,15,1,9,10,12]
+ CRUSH rule 0 x 584 [10,1,5,13,6,9]
+ CRUSH rule 0 x 585 [5,3,6,1,11,14]
+ CRUSH rule 0 x 586 [7,10,14,12,9,3]
+ CRUSH rule 0 x 587 [11,6,9,4,1,14]
+ CRUSH rule 0 x 588 [3,12,7,15,4,9]
+ CRUSH rule 0 x 589 [9,7,12,1,10,3]
+ CRUSH rule 0 x 590 [12,1,3,9,10,6]
+ CRUSH rule 0 x 591 [2,6,14,13,9,11]
+ CRUSH rule 0 x 592 [15,12,9,7,5,2]
+ CRUSH rule 0 x 593 [13,14,5,11,9,6]
+ CRUSH rule 0 x 594 [12,14,2,9,7,4]
+ CRUSH rule 0 x 595 [12,7,10,3,1,14]
+ CRUSH rule 0 x 596 [2,7,12,11,1,5]
+ CRUSH rule 0 x 597 [15,1,2,10,7,13]
+ CRUSH rule 0 x 598 [11,5,9,14,12,7]
+ CRUSH rule 0 x 599 [13,11,1,5,6,2]
+ CRUSH rule 0 x 600 [4,12,3,10,9,7]
+ CRUSH rule 0 x 601 [13,5,15,2,1,7]
+ CRUSH rule 0 x 602 [3,11,7,1,13,15]
+ CRUSH rule 0 x 603 [3,1,4,14,10,9]
+ CRUSH rule 0 x 604 [14,2,6,1,11,13]
+ CRUSH rule 0 x 605 [2,7,12,5,14,10]
+ CRUSH rule 0 x 606 [12,15,1,5,7,9]
+ CRUSH rule 0 x 607 [3,9,10,14,7,1]
+ CRUSH rule 0 x 608 [13,10,1,7,9,15]
+ CRUSH rule 0 x 609 [14,3,7,9,11,12]
+ CRUSH rule 0 x 610 [7,10,5,1,12,2]
+ CRUSH rule 0 x 611 [13,1,5,3,10,7]
+ CRUSH rule 0 x 612 [7,1,2,13,9,15]
+ CRUSH rule 0 x 613 [10,7,14,9,5,2]
+ CRUSH rule 0 x 614 [9,4,15,3,1,11]
+ CRUSH rule 0 x 615 [9,4,11,2,1,12]
+ CRUSH rule 0 x 616 [10,14,1,5,3,6]
+ CRUSH rule 0 x 617 [15,7,2,11,12,1]
+ CRUSH rule 0 x 618 [4,2,10,6,14,9]
+ CRUSH rule 0 x 619 [15,4,3,9,6,1]
+ CRUSH rule 0 x 620 [3,7,11,14,13,1]
+ CRUSH rule 0 x 621 [3,6,4,14,1,11]
+ CRUSH rule 0 x 622 [10,2,13,5,15,9]
+ CRUSH rule 0 x 623 [4,9,14,7,3,13]
+ CRUSH rule 0 x 624 [3,9,15,6,10,1]
+ CRUSH rule 0 x 625 [11,7,3,5,13,15]
+ CRUSH rule 0 x 626 [10,12,2,1,9,7]
+ CRUSH rule 0 x 627 [1,12,10,14,3,5]
+ CRUSH rule 0 x 628 [15,13,11,4,2,1]
+ CRUSH rule 0 x 629 [5,6,15,12,1,10]
+ CRUSH rule 0 x 630 [1,4,12,9,3,7]
+ CRUSH rule 0 x 631 [5,7,1,15,12,11]
+ CRUSH rule 0 x 632 [12,3,11,9,6,1]
+ CRUSH rule 0 x 633 [14,4,3,7,10,12]
+ CRUSH rule 0 x 634 [6,9,5,3,13,11]
+ CRUSH rule 0 x 635 [6,5,2,15,9,12]
+ CRUSH rule 0 x 636 [13,6,11,3,15,9]
+ CRUSH rule 0 x 637 [3,1,10,6,9,12]
+ CRUSH rule 0 x 638 [10,15,3,5,13,1]
+ CRUSH rule 0 x 639 [6,9,14,4,3,1]
+ CRUSH rule 0 x 640 [9,6,1,11,14,2]
+ CRUSH rule 0 x 641 [10,6,5,14,1,9]
+ CRUSH rule 0 x 642 [1,15,4,6,2,10]
+ CRUSH rule 0 x 643 [3,7,5,1,10,15]
+ CRUSH rule 0 x 644 [15,13,6,9,3,11]
+ CRUSH rule 0 x 645 [14,2,4,9,10,1]
+ CRUSH rule 0 x 646 [5,13,14,1,6,9]
+ CRUSH rule 0 x 647 [10,1,9,13,6,2]
+ CRUSH rule 0 x 648 [6,5,2,14,11,1]
+ CRUSH rule 0 x 649 [3,9,13,11,4,14]
+ CRUSH rule 0 x 650 [10,9,4,15,12,7]
+ CRUSH rule 0 x 651 [3,9,5,7,14,1]
+ CRUSH rule 0 x 652 [15,9,4,6,13,1]
+ CRUSH rule 0 x 653 [11,14,1,3,6,9]
+ CRUSH rule 0 x 654 [13,6,2,10,15,4]
+ CRUSH rule 0 x 655 [6,3,4,15,12,11]
+ CRUSH rule 0 x 656 [3,15,1,4,6,12]
+ CRUSH rule 0 x 657 [11,15,3,5,7,13]
+ CRUSH rule 0 x 658 [7,2,10,12,1,4]
+ CRUSH rule 0 x 659 [2,5,14,6,10,12]
+ CRUSH rule 0 x 660 [13,14,10,6,4,9]
+ CRUSH rule 0 x 661 [7,15,3,12,11,4]
+ CRUSH rule 0 x 662 [15,2,12,5,1,10]
+ CRUSH rule 0 x 663 [14,9,13,10,5,3]
+ CRUSH rule 0 x 664 [6,10,12,4,9,2]
+ CRUSH rule 0 x 665 [2,9,12,1,7,10]
+ CRUSH rule 0 x 666 [12,3,6,1,15,9]
+ CRUSH rule 0 x 667 [1,9,12,10,2,14]
+ CRUSH rule 0 x 668 [9,5,1,2,6,11]
+ CRUSH rule 0 x 669 [9,7,14,5,11,13]
+ CRUSH rule 0 x 670 [6,10,9,13,1,2]
+ CRUSH rule 0 x 671 [6,15,5,10,13,3]
+ CRUSH rule 0 x 672 [2,9,13,1,4,14]
+ CRUSH rule 0 x 673 [7,10,5,9,15,13]
+ CRUSH rule 0 x 674 [7,12,10,1,14,9]
+ CRUSH rule 0 x 675 [9,5,1,10,6,14]
+ CRUSH rule 0 x 676 [10,12,2,1,4,15]
+ CRUSH rule 0 x 677 [2,12,1,4,10,6]
+ CRUSH rule 0 x 678 [1,2,4,10,12,14]
+ CRUSH rule 0 x 679 [5,6,12,15,9,11]
+ CRUSH rule 0 x 680 [7,11,3,1,15,4]
+ CRUSH rule 0 x 681 [6,4,3,11,14,13]
+ CRUSH rule 0 x 682 [6,1,11,15,12,2]
+ CRUSH rule 0 x 683 [6,13,2,4,9,14]
+ CRUSH rule 0 x 684 [9,11,3,7,15,4]
+ CRUSH rule 0 x 685 [5,1,15,7,9,2]
+ CRUSH rule 0 x 686 [1,9,11,14,6,13]
+ CRUSH rule 0 x 687 [7,13,3,5,11,9]
+ CRUSH rule 0 x 688 [11,9,1,14,3,5]
+ CRUSH rule 0 x 689 [5,2,9,12,1,14]
+ CRUSH rule 0 x 690 [9,7,10,3,13,15]
+ CRUSH rule 0 x 691 [11,15,9,5,7,13]
+ CRUSH rule 0 x 692 [15,5,1,2,9,11]
+ CRUSH rule 0 x 693 [5,6,12,15,2,10]
+ CRUSH rule 0 x 694 [4,7,1,10,12,3]
+ CRUSH rule 0 x 695 [6,13,14,10,9,5]
+ CRUSH rule 0 x 696 [1,2,4,14,7,11]
+ CRUSH rule 0 x 697 [13,11,3,6,4,14]
+ CRUSH rule 0 x 698 [11,13,4,2,6,1]
+ CRUSH rule 0 x 699 [7,14,12,4,2,11]
+ CRUSH rule 0 x 700 [12,14,11,9,4,6]
+ CRUSH rule 0 x 701 [3,13,1,14,4,7]
+ CRUSH rule 0 x 702 [3,12,15,6,5,11]
+ CRUSH rule 0 x 703 [15,11,13,3,4,7]
+ CRUSH rule 0 x 704 [6,4,2,15,11,1]
+ CRUSH rule 0 x 705 [14,6,11,5,1,13]
+ CRUSH rule 0 x 706 [1,12,3,6,4,10]
+ CRUSH rule 0 x 707 [4,7,14,3,10,9]
+ CRUSH rule 0 x 708 [3,10,5,1,15,9]
+ CRUSH rule 0 x 709 [11,12,3,7,5,14]
+ CRUSH rule 0 x 710 [14,2,11,9,5,7]
+ CRUSH rule 0 x 711 [14,3,9,10,12,5]
+ CRUSH rule 0 x 712 [12,3,11,15,9,1]
+ CRUSH rule 0 x 713 [11,9,3,15,13,6]
+ CRUSH rule 0 x 714 [12,1,9,7,2,15]
+ CRUSH rule 0 x 715 [6,1,14,4,11,12]
+ CRUSH rule 0 x 716 [11,13,9,14,5,2]
+ CRUSH rule 0 x 717 [12,4,10,9,15,1]
+ CRUSH rule 0 x 718 [7,15,5,2,11,13]
+ CRUSH rule 0 x 719 [5,15,13,3,1,7]
+ CRUSH rule 0 x 720 [4,13,10,2,7,9]
+ CRUSH rule 0 x 721 [11,3,14,9,1,12]
+ CRUSH rule 0 x 722 [2,4,6,1,9,15]
+ CRUSH rule 0 x 723 [2,1,12,15,11,7]
+ CRUSH rule 0 x 724 [7,1,9,10,5,15]
+ CRUSH rule 0 x 725 [11,12,7,15,4,1]
+ CRUSH rule 0 x 726 [7,14,4,3,11,13]
+ CRUSH rule 0 x 727 [2,5,1,11,15,7]
+ CRUSH rule 0 x 728 [13,11,4,6,15,2]
+ CRUSH rule 0 x 729 [15,11,4,6,2,9]
+ CRUSH rule 0 x 730 [3,7,1,13,11,15]
+ CRUSH rule 0 x 731 [9,1,6,5,2,11]
+ CRUSH rule 0 x 732 [1,2,10,13,9,4]
+ CRUSH rule 0 x 733 [11,3,5,6,1,9]
+ CRUSH rule 0 x 734 [14,3,11,7,12,9]
+ CRUSH rule 0 x 735 [6,9,2,10,13,14]
+ CRUSH rule 0 x 736 [3,9,1,11,7,5]
+ CRUSH rule 0 x 737 [1,4,2,12,9,10]
+ CRUSH rule 0 x 738 [11,15,7,4,9,2]
+ CRUSH rule 0 x 739 [11,12,6,2,4,1]
+ CRUSH rule 0 x 740 [7,9,10,13,1,15]
+ CRUSH rule 0 x 741 [12,11,7,15,2,5]
+ CRUSH rule 0 x 742 [9,7,4,11,12,1]
+ CRUSH rule 0 x 743 [5,13,9,15,10,7]
+ CRUSH rule 0 x 744 [6,2,13,1,14,11]
+ CRUSH rule 0 x 745 [3,6,1,4,11,12]
+ CRUSH rule 0 x 746 [3,7,9,10,14,5]
+ CRUSH rule 0 x 747 [15,11,5,2,13,9]
+ CRUSH rule 0 x 748 [6,10,13,2,14,5]
+ CRUSH rule 0 x 749 [14,9,10,7,5,1]
+ CRUSH rule 0 x 750 [1,14,6,5,11,2]
+ CRUSH rule 0 x 751 [15,1,6,9,5,11]
+ CRUSH rule 0 x 752 [13,1,7,3,11,15]
+ CRUSH rule 0 x 753 [4,11,1,3,15,7]
+ CRUSH rule 0 x 754 [14,12,11,4,2,1]
+ CRUSH rule 0 x 755 [13,6,1,10,4,2]
+ CRUSH rule 0 x 756 [3,4,14,6,1,10]
+ CRUSH rule 0 x 757 [10,6,1,4,13,15]
+ CRUSH rule 0 x 758 [6,3,4,10,15,13]
+ CRUSH rule 0 x 759 [5,7,3,14,11,1]
+ CRUSH rule 0 x 760 [1,15,10,12,4,3]
+ CRUSH rule 0 x 761 [2,12,1,14,5,7]
+ CRUSH rule 0 x 762 [1,4,10,9,3,7]
+ CRUSH rule 0 x 763 [4,13,1,14,7,10]
+ CRUSH rule 0 x 764 [1,14,6,13,9,5]
+ CRUSH rule 0 x 765 [9,15,2,13,4,1]
+ CRUSH rule 0 x 766 [11,2,7,15,9,12]
+ CRUSH rule 0 x 767 [6,11,4,3,12,14]
+ CRUSH rule 0 x 768 [2,12,15,7,1,11]
+ CRUSH rule 0 x 769 [15,1,9,2,11,12]
+ CRUSH rule 0 x 770 [15,13,4,6,3,10]
+ CRUSH rule 0 x 771 [9,2,12,11,6,14]
+ CRUSH rule 0 x 772 [4,3,13,11,14,1]
+ CRUSH rule 0 x 773 [3,7,4,15,1,12]
+ CRUSH rule 0 x 774 [12,6,3,15,5,9]
+ CRUSH rule 0 x 775 [5,10,14,2,6,1]
+ CRUSH rule 0 x 776 [10,15,3,9,6,13]
+ CRUSH rule 0 x 777 [11,13,4,7,1,14]
+ CRUSH rule 0 x 778 [13,1,9,11,15,6]
+ CRUSH rule 0 x 779 [5,11,1,14,2,9]
+ CRUSH rule 0 x 780 [13,9,3,6,4,1]
+ CRUSH rule 0 x 781 [5,7,14,3,1,12]
+ CRUSH rule 0 x 782 [2,15,9,7,11,13]
+ CRUSH rule 0 x 783 [12,7,5,14,9,1]
+ CRUSH rule 0 x 784 [14,1,10,13,3,4]
+ CRUSH rule 0 x 785 [6,12,1,2,4,9]
+ CRUSH rule 0 x 786 [10,5,2,15,1,7]
+ CRUSH rule 0 x 787 [1,12,10,2,9,4]
+ CRUSH rule 0 x 788 [4,2,9,13,6,15]
+ CRUSH rule 0 x 789 [9,2,14,7,4,12]
+ CRUSH rule 0 x 790 [15,2,7,4,1,10]
+ CRUSH rule 0 x 791 [9,4,7,13,14,11]
+ CRUSH rule 0 x 792 [6,4,15,10,12,3]
+ CRUSH rule 0 x 793 [15,9,6,2,13,11]
+ CRUSH rule 0 x 794 [5,12,2,14,9,10]
+ CRUSH rule 0 x 795 [6,14,12,4,10,1]
+ CRUSH rule 0 x 796 [11,2,12,6,15,4]
+ CRUSH rule 0 x 797 [14,3,7,1,5,13]
+ CRUSH rule 0 x 798 [5,11,6,13,1,3]
+ CRUSH rule 0 x 799 [2,9,14,4,13,6]
+ CRUSH rule 0 x 800 [6,3,4,11,15,13]
+ CRUSH rule 0 x 801 [2,5,6,13,9,1]
+ CRUSH rule 0 x 802 [1,4,12,7,3,9]
+ CRUSH rule 0 x 803 [7,2,4,1,11,13]
+ CRUSH rule 0 x 804 [5,14,9,7,3,1]
+ CRUSH rule 0 x 805 [13,4,3,1,10,15]
+ CRUSH rule 0 x 806 [6,2,13,4,15,1]
+ CRUSH rule 0 x 807 [14,2,7,4,9,12]
+ CRUSH rule 0 x 808 [2,15,12,7,9,1]
+ CRUSH rule 0 x 809 [1,11,7,12,4,2]
+ CRUSH rule 0 x 810 [2,5,9,12,15,1]
+ CRUSH rule 0 x 811 [15,6,3,10,1,5]
+ CRUSH rule 0 x 812 [7,11,2,14,9,5]
+ CRUSH rule 0 x 813 [4,10,13,14,2,6]
+ CRUSH rule 0 x 814 [13,4,9,3,10,6]
+ CRUSH rule 0 x 815 [15,12,9,4,10,6]
+ CRUSH rule 0 x 816 [14,10,13,7,3,9]
+ CRUSH rule 0 x 817 [10,7,2,15,13,9]
+ CRUSH rule 0 x 818 [15,2,11,4,1,12]
+ CRUSH rule 0 x 819 [5,12,10,6,1,14]
+ CRUSH rule 0 x 820 [3,6,9,12,11,15]
+ CRUSH rule 0 x 821 [15,10,9,13,3,4]
+ CRUSH rule 0 x 822 [10,13,2,9,7,4]
+ CRUSH rule 0 x 823 [2,6,12,10,15,4]
+ CRUSH rule 0 x 824 [3,7,9,13,15,5]
+ CRUSH rule 0 x 825 [10,5,14,6,12,9]
+ CRUSH rule 0 x 826 [5,2,11,15,1,12]
+ CRUSH rule 0 x 827 [13,5,1,3,7,9]
+ CRUSH rule 0 x 828 [12,6,10,5,1,9]
+ CRUSH rule 0 x 829 [13,6,15,10,5,3]
+ CRUSH rule 0 x 830 [15,13,2,9,7,11]
+ CRUSH rule 0 x 831 [1,4,11,12,6,3]
+ CRUSH rule 0 x 832 [14,11,13,2,9,4]
+ CRUSH rule 0 x 833 [9,13,3,11,7,5]
+ CRUSH rule 0 x 834 [9,7,5,1,11,2]
+ CRUSH rule 0 x 835 [14,3,13,6,4,9]
+ CRUSH rule 0 x 836 [3,9,10,13,1,5]
+ CRUSH rule 0 x 837 [15,12,11,2,7,9]
+ CRUSH rule 0 x 838 [12,14,9,2,5,7]
+ CRUSH rule 0 x 839 [3,4,6,10,15,1]
+ CRUSH rule 0 x 840 [10,15,12,4,7,1]
+ CRUSH rule 0 x 841 [3,5,7,12,11,15]
+ CRUSH rule 0 x 842 [9,13,2,6,5,14]
+ CRUSH rule 0 x 843 [14,7,4,9,3,12]
+ CRUSH rule 0 x 844 [7,1,4,15,9,2]
+ CRUSH rule 0 x 845 [13,6,1,15,4,2]
+ CRUSH rule 0 x 846 [3,7,15,13,1,9]
+ CRUSH rule 0 x 847 [12,15,11,5,2,7]
+ CRUSH rule 0 x 848 [11,13,1,14,5,9]
+ CRUSH rule 0 x 849 [3,15,11,9,6,1]
+ CRUSH rule 0 x 850 [1,3,10,6,14,4]
+ CRUSH rule 0 x 851 [14,4,3,6,11,1]
+ CRUSH rule 0 x 852 [9,12,4,7,15,2]
+ CRUSH rule 0 x 853 [13,14,6,11,2,4]
+ CRUSH rule 0 x 854 [7,11,12,1,4,15]
+ CRUSH rule 0 x 855 [14,4,12,6,3,1]
+ CRUSH rule 0 x 856 [5,10,7,3,15,9]
+ CRUSH rule 0 x 857 [4,3,13,11,9,1]
+ CRUSH rule 0 x 858 [5,15,6,3,9,12]
+ CRUSH rule 0 x 859 [5,15,6,2,1,11]
+ CRUSH rule 0 x 860 [11,14,1,12,6,9]
+ CRUSH rule 0 x 861 [13,7,4,10,1,14]
+ CRUSH rule 0 x 862 [5,10,9,7,3,12]
+ CRUSH rule 0 x 863 [11,6,3,9,4,12]
+ CRUSH rule 0 x 864 [6,13,4,2,10,15]
+ CRUSH rule 0 x 865 [4,1,14,11,6,9]
+ CRUSH rule 0 x 866 [2,13,4,15,9,6]
+ CRUSH rule 0 x 867 [12,2,9,10,4,14]
+ CRUSH rule 0 x 868 [14,11,7,2,1,4]
+ CRUSH rule 0 x 869 [10,13,7,14,3,5]
+ CRUSH rule 0 x 870 [14,9,11,4,3,12]
+ CRUSH rule 0 x 871 [6,2,1,4,15,13]
+ CRUSH rule 0 x 872 [6,1,15,3,10,12]
+ CRUSH rule 0 x 873 [2,5,12,10,1,9]
+ CRUSH rule 0 x 874 [12,4,7,2,15,10]
+ CRUSH rule 0 x 875 [10,6,14,1,12,5]
+ CRUSH rule 0 x 876 [14,7,13,3,9,1]
+ CRUSH rule 0 x 877 [15,11,13,9,5,1]
+ CRUSH rule 0 x 878 [7,14,3,13,9,1]
+ CRUSH rule 0 x 879 [12,2,7,4,10,15]
+ CRUSH rule 0 x 880 [2,12,10,7,1,4]
+ CRUSH rule 0 x 881 [6,3,1,11,4,15]
+ CRUSH rule 0 x 882 [11,13,7,1,2,15]
+ CRUSH rule 0 x 883 [13,1,3,10,6,5]
+ CRUSH rule 0 x 884 [6,15,4,9,3,11]
+ CRUSH rule 0 x 885 [14,7,9,4,2,13]
+ CRUSH rule 0 x 886 [13,11,4,2,1,14]
+ CRUSH rule 0 x 887 [14,4,12,11,2,6]
+ CRUSH rule 0 x 888 [10,12,7,15,9,2]
+ CRUSH rule 0 x 889 [15,13,4,1,6,2]
+ CRUSH rule 0 x 890 [10,12,14,2,9,5]
+ CRUSH rule 0 x 891 [9,5,11,6,3,15]
+ CRUSH rule 0 x 892 [12,15,2,4,7,9]
+ CRUSH rule 0 x 893 [1,3,5,9,6,10]
+ CRUSH rule 0 x 894 [7,2,11,13,4,1]
+ CRUSH rule 0 x 895 [2,1,11,5,7,15]
+ CRUSH rule 0 x 896 [9,1,14,10,4,12]
+ CRUSH rule 0 x 897 [7,5,14,3,1,9]
+ CRUSH rule 0 x 898 [10,6,12,9,15,5]
+ CRUSH rule 0 x 899 [1,11,5,3,13,14]
+ CRUSH rule 0 x 900 [2,9,10,7,13,14]
+ CRUSH rule 0 x 901 [9,12,11,3,14,4]
+ CRUSH rule 0 x 902 [4,2,6,15,12,10]
+ CRUSH rule 0 x 903 [14,10,3,1,12,6]
+ CRUSH rule 0 x 904 [15,12,4,9,6,3]
+ CRUSH rule 0 x 905 [12,6,11,3,9,4]
+ CRUSH rule 0 x 906 [14,11,12,2,4,9]
+ CRUSH rule 0 x 907 [7,12,3,9,10,5]
+ CRUSH rule 0 x 908 [2,15,9,6,10,13]
+ CRUSH rule 0 x 909 [10,14,1,13,2,9]
+ CRUSH rule 0 x 910 [12,7,4,15,10,3]
+ CRUSH rule 0 x 911 [11,15,2,4,9,13]
+ CRUSH rule 0 x 912 [6,4,14,13,3,1]
+ CRUSH rule 0 x 913 [4,6,10,1,12,3]
+ CRUSH rule 0 x 914 [4,15,2,10,1,13]
+ CRUSH rule 0 x 915 [12,14,1,9,4,3]
+ CRUSH rule 0 x 916 [3,1,11,5,6,13]
+ CRUSH rule 0 x 917 [1,15,6,5,10,3]
+ CRUSH rule 0 x 918 [7,14,11,4,9,2]
+ CRUSH rule 0 x 919 [10,7,3,13,15,1]
+ CRUSH rule 0 x 920 [4,2,10,15,1,13]
+ CRUSH rule 0 x 921 [1,11,6,13,4,2]
+ CRUSH rule 0 x 922 [6,4,14,13,3,1]
+ CRUSH rule 0 x 923 [12,2,5,14,10,1]
+ CRUSH rule 0 x 924 [6,2,14,13,9,1]
+ CRUSH rule 0 x 925 [12,15,2,10,1,5]
+ CRUSH rule 0 x 926 [3,13,10,1,14,9]
+ CRUSH rule 0 x 927 [6,5,1,11,14,2]
+ CRUSH rule 0 x 928 [13,1,3,9,6,11]
+ CRUSH rule 0 x 929 [10,7,1,5,2,12]
+ CRUSH rule 0 x 930 [7,15,10,5,1,13]
+ CRUSH rule 0 x 931 [6,15,11,9,5,3]
+ CRUSH rule 0 x 932 [13,2,5,11,9,1]
+ CRUSH rule 0 x 933 [12,7,14,10,4,1]
+ CRUSH rule 0 x 934 [12,2,5,7,9,1]
+ CRUSH rule 0 x 935 [6,11,1,14,5,13]
+ CRUSH rule 0 x 936 [9,12,7,5,1,2]
+ CRUSH rule 0 x 937 [14,2,11,1,13,4]
+ CRUSH rule 0 x 938 [14,3,5,11,7,9]
+ CRUSH rule 0 x 939 [6,4,14,9,12,1]
+ CRUSH rule 0 x 940 [13,11,4,2,1,6]
+ CRUSH rule 0 x 941 [3,12,4,7,14,10]
+ CRUSH rule 0 x 942 [15,12,10,4,1,9]
+ CRUSH rule 0 x 943 [10,2,4,9,6,15]
+ CRUSH rule 0 x 944 [2,9,4,7,1,14]
+ CRUSH rule 0 x 945 [10,15,2,9,5,12]
+ CRUSH rule 0 x 946 [11,15,7,12,5,9]
+ CRUSH rule 0 x 947 [11,3,14,1,12,5]
+ CRUSH rule 0 x 948 [7,13,11,5,14,2]
+ CRUSH rule 0 x 949 [9,1,12,5,15,10]
+ CRUSH rule 0 x 950 [9,15,13,6,4,2]
+ CRUSH rule 0 x 951 [2,6,12,9,10,4]
+ CRUSH rule 0 x 952 [9,7,15,3,5,13]
+ CRUSH rule 0 x 953 [1,3,6,10,12,14]
+ CRUSH rule 0 x 954 [10,2,14,9,4,6]
+ CRUSH rule 0 x 955 [7,14,3,1,10,4]
+ CRUSH rule 0 x 956 [1,6,11,5,14,3]
+ CRUSH rule 0 x 957 [14,11,1,12,6,9]
+ CRUSH rule 0 x 958 [15,4,3,11,1,6]
+ CRUSH rule 0 x 959 [2,1,12,15,10,9]
+ CRUSH rule 0 x 960 [2,6,11,13,15,4]
+ CRUSH rule 0 x 961 [3,13,11,9,6,1]
+ CRUSH rule 0 x 962 [5,11,3,14,1,6]
+ CRUSH rule 0 x 963 [13,10,15,4,6,9]
+ CRUSH rule 0 x 964 [7,11,4,9,2,12]
+ CRUSH rule 0 x 965 [12,2,9,7,4,15]
+ CRUSH rule 0 x 966 [12,14,9,4,1,2]
+ CRUSH rule 0 x 967 [7,5,3,10,12,14]
+ CRUSH rule 0 x 968 [12,15,4,9,11,6]
+ CRUSH rule 0 x 969 [11,4,7,1,9,14]
+ CRUSH rule 0 x 970 [5,12,10,1,3,14]
+ CRUSH rule 0 x 971 [1,9,4,12,7,2]
+ CRUSH rule 0 x 972 [12,3,14,5,1,9]
+ CRUSH rule 0 x 973 [1,10,4,12,2,7]
+ CRUSH rule 0 x 974 [7,11,1,2,15,4]
+ CRUSH rule 0 x 975 [7,9,15,12,2,11]
+ CRUSH rule 0 x 976 [7,3,15,5,12,11]
+ CRUSH rule 0 x 977 [14,3,6,10,4,1]
+ CRUSH rule 0 x 978 [12,5,11,1,15,3]
+ CRUSH rule 0 x 979 [5,1,13,6,15,10]
+ CRUSH rule 0 x 980 [15,11,5,6,1,3]
+ CRUSH rule 0 x 981 [5,11,15,12,7,1]
+ CRUSH rule 0 x 982 [2,6,14,11,12,9]
+ CRUSH rule 0 x 983 [3,12,10,9,14,5]
+ CRUSH rule 0 x 984 [15,13,1,10,2,5]
+ CRUSH rule 0 x 985 [11,2,15,1,4,13]
+ CRUSH rule 0 x 986 [6,13,9,1,15,10]
+ CRUSH rule 0 x 987 [13,14,5,10,6,1]
+ CRUSH rule 0 x 988 [12,9,10,14,3,1]
+ CRUSH rule 0 x 989 [7,4,3,15,9,13]
+ CRUSH rule 0 x 990 [1,10,9,13,3,4]
+ CRUSH rule 0 x 991 [7,11,1,14,2,5]
+ CRUSH rule 0 x 992 [9,10,2,13,7,4]
+ CRUSH rule 0 x 993 [6,10,14,12,4,1]
+ CRUSH rule 0 x 994 [3,13,15,4,11,7]
+ CRUSH rule 0 x 995 [15,6,12,2,5,11]
+ CRUSH rule 0 x 996 [15,10,5,3,13,1]
+ CRUSH rule 0 x 997 [15,2,1,12,7,9]
+ CRUSH rule 0 x 998 [6,1,9,5,12,11]
+ CRUSH rule 0 x 999 [9,10,15,5,13,3]
+ CRUSH rule 0 x 1000 [14,2,9,4,12,1]
+ CRUSH rule 0 x 1001 [11,14,4,2,6,9]
+ CRUSH rule 0 x 1002 [1,10,14,2,9,5]
+ CRUSH rule 0 x 1003 [10,7,5,14,2,1]
+ CRUSH rule 0 x 1004 [15,1,4,6,10,12]
+ CRUSH rule 0 x 1005 [6,12,2,10,9,15]
+ CRUSH rule 0 x 1006 [10,12,15,1,2,6]
+ CRUSH rule 0 x 1007 [1,7,13,14,3,4]
+ CRUSH rule 0 x 1008 [7,4,9,11,3,15]
+ CRUSH rule 0 x 1009 [5,2,11,7,15,9]
+ CRUSH rule 0 x 1010 [10,2,15,6,9,13]
+ CRUSH rule 0 x 1011 [6,3,12,1,10,4]
+ CRUSH rule 0 x 1012 [12,6,9,15,3,1]
+ CRUSH rule 0 x 1013 [2,14,12,4,9,1]
+ CRUSH rule 0 x 1014 [1,13,7,2,10,14]
+ CRUSH rule 0 x 1015 [12,6,10,1,4,15]
+ CRUSH rule 0 x 1016 [10,13,14,3,5,6]
+ CRUSH rule 0 x 1017 [5,11,14,7,13,9]
+ CRUSH rule 0 x 1018 [13,11,14,1,9,3]
+ CRUSH rule 0 x 1019 [10,13,14,7,5,1]
+ CRUSH rule 0 x 1020 [3,1,13,4,10,9]
+ CRUSH rule 0 x 1021 [2,11,14,9,4,6]
+ CRUSH rule 0 x 1022 [15,5,7,2,12,10]
+ CRUSH rule 0 x 1023 [15,2,9,12,1,7]
+ rule 0 (replicated_ruleset) num_rep 6 result size == 6:\t1024/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3,15,12,1,4]
+ CRUSH rule 0 x 1 [10,15,1,2,13,4,7]
+ CRUSH rule 0 x 2 [1,12,2,6,5,10,15]
+ CRUSH rule 0 x 3 [15,4,10,2,9,6,13]
+ CRUSH rule 0 x 4 [14,2,10,1,9,4,7]
+ CRUSH rule 0 x 5 [7,4,11,2,13,15,9]
+ CRUSH rule 0 x 6 [12,6,10,9,3,4,14]
+ CRUSH rule 0 x 7 [9,2,6,12,11,4,1]
+ CRUSH rule 0 x 8 [10,2,15,1,4,13,6]
+ CRUSH rule 0 x 9 [7,1,14,2,11,9,12]
+ CRUSH rule 0 x 10 [10,14,4,1,2,7,13]
+ CRUSH rule 0 x 11 [13,9,14,7,5,11,2]
+ CRUSH rule 0 x 12 [7,1,2,5,13,15,11]
+ CRUSH rule 0 x 13 [3,5,12,7,9,1,14]
+ CRUSH rule 0 x 14 [13,5,2,7,10,15,1]
+ CRUSH rule 0 x 15 [15,1,9,6,13,3,5]
+ CRUSH rule 0 x 16 [7,11,14,2,13,1,9]
+ CRUSH rule 0 x 17 [10,1,13,2,4,6,14]
+ CRUSH rule 0 x 18 [1,7,3,10,5,12,9]
+ CRUSH rule 0 x 19 [7,12,2,4,15,10,1]
+ CRUSH rule 0 x 20 [14,12,3,10,9,4,7]
+ CRUSH rule 0 x 21 [3,12,1,10,4,15,6]
+ CRUSH rule 0 x 22 [6,3,13,11,4,1,15]
+ CRUSH rule 0 x 23 [10,5,13,9,3,15,1]
+ CRUSH rule 0 x 24 [12,11,3,1,9,4,7]
+ CRUSH rule 0 x 25 [7,12,15,1,3,10,4]
+ CRUSH rule 0 x 26 [1,7,13,2,14,5,9]
+ CRUSH rule 0 x 27 [3,6,15,4,13,9,11]
+ CRUSH rule 0 x 28 [14,4,3,9,6,11,13]
+ CRUSH rule 0 x 29 [5,14,12,11,6,3,1]
+ CRUSH rule 0 x 30 [2,5,6,9,1,11,13]
+ CRUSH rule 0 x 31 [5,15,10,1,9,13,6]
+ CRUSH rule 0 x 32 [9,10,2,1,13,14,6]
+ CRUSH rule 0 x 33 [13,4,9,2,7,1,10]
+ CRUSH rule 0 x 34 [13,15,2,4,1,10,9]
+ CRUSH rule 0 x 35 [4,14,3,13,10,9,1]
+ CRUSH rule 0 x 36 [3,12,9,7,5,10,14]
+ CRUSH rule 0 x 37 [9,2,6,14,11,1,4]
+ CRUSH rule 0 x 38 [3,4,13,10,9,1,14]
+ CRUSH rule 0 x 39 [12,7,14,11,1,9,5]
+ CRUSH rule 0 x 40 [10,1,9,5,15,2,6]
+ CRUSH rule 0 x 41 [4,9,11,1,14,13,6]
+ CRUSH rule 0 x 42 [3,6,14,10,12,5,1]
+ CRUSH rule 0 x 43 [10,5,15,7,2,9,12]
+ CRUSH rule 0 x 44 [11,4,13,3,7,14,9]
+ CRUSH rule 0 x 45 [11,12,15,9,1,5,6]
+ CRUSH rule 0 x 46 [6,9,2,14,11,13,1]
+ CRUSH rule 0 x 47 [3,9,6,4,13,1,11]
+ CRUSH rule 0 x 48 [4,6,2,1,10,14,13]
+ CRUSH rule 0 x 49 [9,15,10,7,4,3,13]
+ CRUSH rule 0 x 50 [14,12,1,4,2,11,6]
+ CRUSH rule 0 x 51 [10,6,5,12,15,2,1]
+ CRUSH rule 0 x 52 [12,1,9,11,7,3,14]
+ CRUSH rule 0 x 53 [3,6,13,9,5,1,11]
+ CRUSH rule 0 x 54 [4,13,9,2,14,10,6]
+ CRUSH rule 0 x 55 [4,11,2,7,1,13,9]
+ CRUSH rule 0 x 56 [5,9,10,1,3,13,14]
+ CRUSH rule 0 x 57 [6,2,1,15,10,12,5]
+ CRUSH rule 0 x 58 [7,1,11,4,3,14,12]
+ CRUSH rule 0 x 59 [2,13,1,10,9,5,14]
+ CRUSH rule 0 x 60 [3,6,11,1,4,9,12]
+ CRUSH rule 0 x 61 [3,15,13,7,4,1,10]
+ CRUSH rule 0 x 62 [15,11,7,12,5,9,2]
+ CRUSH rule 0 x 63 [10,14,12,1,7,3]
+ CRUSH rule 0 x 64 [3,9,1,4,7,12,11]
+ CRUSH rule 0 x 65 [4,12,11,7,14,3,1]
+ CRUSH rule 0 x 66 [15,11,6,9,4,1,3]
+ CRUSH rule 0 x 67 [2,6,4,14,1,11,12]
+ CRUSH rule 0 x 68 [15,7,4,2,9,12,11]
+ CRUSH rule 0 x 69 [2,1,15,10,4,9,13]
+ CRUSH rule 0 x 70 [9,6,1,3,13,15,11]
+ CRUSH rule 0 x 71 [15,5,1,3,13,10,7]
+ CRUSH rule 0 x 72 [9,10,3,5,7,12,15]
+ CRUSH rule 0 x 73 [5,3,11,1,7,12,15]
+ CRUSH rule 0 x 74 [11,7,9,5,1,15,3]
+ CRUSH rule 0 x 75 [9,7,11,14,12,1,2]
+ CRUSH rule 0 x 76 [6,1,3,5,14,10,12]
+ CRUSH rule 0 x 77 [7,4,2,13,9,1,11]
+ CRUSH rule 0 x 78 [9,3,1,5,6,13,14]
+ CRUSH rule 0 x 79 [13,2,15,5,7,9,11]
+ CRUSH rule 0 x 80 [15,2,6,4,13,10,1]
+ CRUSH rule 0 x 81 [15,2,1,11,4,6,13]
+ CRUSH rule 0 x 82 [14,13,5,11,6,2,1]
+ CRUSH rule 0 x 83 [4,15,3,9,10,13,6]
+ CRUSH rule 0 x 84 [10,7,9,15,3,4,1]
+ CRUSH rule 0 x 85 [3,15,9,7,4,11,1]
+ CRUSH rule 0 x 86 [10,9,14,1,13,4,2]
+ CRUSH rule 0 x 87 [15,10,7,12,5,3,9]
+ CRUSH rule 0 x 88 [4,13,3,1,9,15,11]
+ CRUSH rule 0 x 89 [3,9,7,4,1,14,10]
+ CRUSH rule 0 x 90 [4,9,7,12,11,14,2]
+ CRUSH rule 0 x 91 [6,11,9,1,2,4,14]
+ CRUSH rule 0 x 92 [1,5,10,9,13,15,6]
+ CRUSH rule 0 x 93 [9,3,15,13,7,5,1]
+ CRUSH rule 0 x 94 [9,2,12,5,6,11,1]
+ CRUSH rule 0 x 95 [7,15,4,10,9,13,2]
+ CRUSH rule 0 x 96 [2,15,11,7,5,1,12]
+ CRUSH rule 0 x 97 [4,11,2,13,1,7,9]
+ CRUSH rule 0 x 98 [11,13,9,3,15,1,5]
+ CRUSH rule 0 x 99 [12,4,11,7,3,14,9]
+ CRUSH rule 0 x 100 [9,4,10,15,7,3,13]
+ CRUSH rule 0 x 101 [15,7,1,9,10,5,2]
+ CRUSH rule 0 x 102 [3,11,14,6,13,4,9]
+ CRUSH rule 0 x 103 [13,11,6,14,4,3,1]
+ CRUSH rule 0 x 104 [14,6,3,5,9,1,10]
+ CRUSH rule 0 x 105 [14,10,1,9,3,5,6]
+ CRUSH rule 0 x 106 [6,5,13,2,14,11,1]
+ CRUSH rule 0 x 107 [3,1,10,14,13,5,9]
+ CRUSH rule 0 x 108 [5,10,7,2,15,9,12]
+ CRUSH rule 0 x 109 [9,1,13,7,15,5,11]
+ CRUSH rule 0 x 110 [5,1,11,3,7,14,13]
+ CRUSH rule 0 x 111 [10,1,9,7,5,2,13]
+ CRUSH rule 0 x 112 [1,10,4,14,2,12,6]
+ CRUSH rule 0 x 113 [6,10,13,9,1,5,2]
+ CRUSH rule 0 x 114 [5,13,6,2,1,14,11]
+ CRUSH rule 0 x 115 [10,13,14,3,9,1,6]
+ CRUSH rule 0 x 116 [1,14,13,2,11,5,7]
+ CRUSH rule 0 x 117 [5,6,1,12,15,9,11]
+ CRUSH rule 0 x 118 [10,4,13,15,9,3,1]
+ CRUSH rule 0 x 119 [14,12,11,4,6,9,3]
+ CRUSH rule 0 x 120 [11,3,14,13,4,7]
+ CRUSH rule 0 x 121 [9,5,1,11,7,3,15]
+ CRUSH rule 0 x 122 [4,3,14,1,11,13,7]
+ CRUSH rule 0 x 123 [3,10,5,6,9,1,12]
+ CRUSH rule 0 x 124 [12,2,1,5,14,7,11]
+ CRUSH rule 0 x 125 [9,12,15,1,6,5,3]
+ CRUSH rule 0 x 126 [7,15,10,9,2,12,5]
+ CRUSH rule 0 x 127 [4,14,9,13,1,3,7]
+ CRUSH rule 0 x 128 [3,12,1,10,4,9,7]
+ CRUSH rule 0 x 129 [11,13,14,2,9,4,6]
+ CRUSH rule 0 x 130 [3,13,5,14,10,1,9]
+ CRUSH rule 0 x 131 [12,1,6,15,4,2,11]
+ CRUSH rule 0 x 132 [11,15,13,9,2,5,7]
+ CRUSH rule 0 x 133 [3,6,9,11,15,12,5]
+ CRUSH rule 0 x 134 [12,5,6,15,3,9,10]
+ CRUSH rule 0 x 135 [3,14,12,4,6,11,9]
+ CRUSH rule 0 x 136 [15,6,9,4,10,3,12]
+ CRUSH rule 0 x 137 [14,3,6,11,1,9,4]
+ CRUSH rule 0 x 138 [13,15,4,10,2,7,1]
+ CRUSH rule 0 x 139 [11,2,13,9,1,15,7]
+ CRUSH rule 0 x 140 [11,4,12,15,2,6,9]
+ CRUSH rule 0 x 141 [6,12,15,11,3,5,1]
+ CRUSH rule 0 x 142 [3,14,7,9,11,1,4]
+ CRUSH rule 0 x 143 [9,6,4,2,14,10,12]
+ CRUSH rule 0 x 144 [13,7,11,2,14,4,1]
+ CRUSH rule 0 x 145 [12,2,6,10,9,4,14]
+ CRUSH rule 0 x 146 [1,5,9,2,6,13,14]
+ CRUSH rule 0 x 147 [1,4,9,11,2,7,15]
+ CRUSH rule 0 x 148 [12,7,9,2,14,11,1]
+ CRUSH rule 0 x 149 [2,5,9,12,11,1,7]
+ CRUSH rule 0 x 150 [1,15,2,10,7,9,5]
+ CRUSH rule 0 x 151 [2,9,14,7,1,10,5]
+ CRUSH rule 0 x 152 [5,9,2,6,10,13,14]
+ CRUSH rule 0 x 153 [6,9,4,15,2,1,10]
+ CRUSH rule 0 x 154 [3,11,7,1,4,12,15]
+ CRUSH rule 0 x 155 [14,12,7,3,5,1,9]
+ CRUSH rule 0 x 156 [7,13,3,10,15,5,1]
+ CRUSH rule 0 x 157 [15,1,6,4,3,10,9]
+ CRUSH rule 0 x 158 [15,1,10,6,12,2,4]
+ CRUSH rule 0 x 159 [4,14,3,12,10,6,1]
+ CRUSH rule 0 x 160 [5,7,3,14,11,1,12]
+ CRUSH rule 0 x 161 [1,2,11,4,6,13,14]
+ CRUSH rule 0 x 162 [10,6,1,12,2,4,14]
+ CRUSH rule 0 x 163 [15,1,10,2,6,4,13]
+ CRUSH rule 0 x 164 [9,14,10,7,12,2,5]
+ CRUSH rule 0 x 165 [11,7,2,13,9,15,1]
+ CRUSH rule 0 x 166 [1,2,12,14,4,11,7]
+ CRUSH rule 0 x 167 [9,7,3,4,11,13,15]
+ CRUSH rule 0 x 168 [13,2,4,1,6,15,10]
+ CRUSH rule 0 x 169 [1,4,9,14,13,10,2]
+ CRUSH rule 0 x 170 [1,15,7,9,12,10,3]
+ CRUSH rule 0 x 171 [9,2,10,7,1,5,15]
+ CRUSH rule 0 x 172 [14,4,10,12,9,3,6]
+ CRUSH rule 0 x 173 [5,10,12,15,6,1,2]
+ CRUSH rule 0 x 174 [15,6,4,12,1,11,9]
+ CRUSH rule 0 x 175 [5,7,9,3,10,1,14]
+ CRUSH rule 0 x 176 [9,6,3,14,13,10,4]
+ CRUSH rule 0 x 177 [2,9,10,13,4,1,14]
+ CRUSH rule 0 x 178 [12,11,7,14,3,4]
+ CRUSH rule 0 x 179 [2,10,13,9,5,1,7]
+ CRUSH rule 0 x 180 [3,11,5,15,7,12]
+ CRUSH rule 0 x 181 [9,12,6,5,1,10,14]
+ CRUSH rule 0 x 182 [5,13,11,2,1,6,14]
+ CRUSH rule 0 x 183 [5,7,10,13,3,9,14]
+ CRUSH rule 0 x 184 [2,5,11,12,7,1,9]
+ CRUSH rule 0 x 185 [13,5,7,11,2,14]
+ CRUSH rule 0 x 186 [6,14,13,5,10,1,3]
+ CRUSH rule 0 x 187 [1,4,11,13,6,14,9]
+ CRUSH rule 0 x 188 [9,13,5,14,10,6,2]
+ CRUSH rule 0 x 189 [6,12,4,9,2,1,11]
+ CRUSH rule 0 x 190 [9,13,15,10,3,1,5]
+ CRUSH rule 0 x 191 [7,11,4,1,15,12,9]
+ CRUSH rule 0 x 192 [2,11,5,15,6,1,13]
+ CRUSH rule 0 x 193 [3,13,6,10,4,1,9]
+ CRUSH rule 0 x 194 [3,13,4,14,6,9,1]
+ CRUSH rule 0 x 195 [5,7,10,12,1,3,15]
+ CRUSH rule 0 x 196 [4,15,1,10,9,2,13]
+ CRUSH rule 0 x 197 [14,10,13,4,6,3,1]
+ CRUSH rule 0 x 198 [2,5,6,15,9,13,10]
+ CRUSH rule 0 x 199 [2,10,4,15,1,9,6]
+ CRUSH rule 0 x 200 [7,14,11,4,1,3,13]
+ CRUSH rule 0 x 201 [9,14,1,7,4,3,10]
+ CRUSH rule 0 x 202 [14,11,7,3,5,1,12]
+ CRUSH rule 0 x 203 [12,5,7,15,1,2,10]
+ CRUSH rule 0 x 204 [6,11,3,12,14,1,9]
+ CRUSH rule 0 x 205 [15,4,6,10,13,9,2]
+ CRUSH rule 0 x 206 [13,11,2,15,7,1,5]
+ CRUSH rule 0 x 207 [2,11,7,4,14,1,12]
+ CRUSH rule 0 x 208 [13,1,6,14,9,11,3]
+ CRUSH rule 0 x 209 [6,15,13,1,11,4,9]
+ CRUSH rule 0 x 210 [13,11,2,7,5,14,9]
+ CRUSH rule 0 x 211 [2,14,1,13,11,7,9]
+ CRUSH rule 0 x 212 [10,1,12,15,5,6,2]
+ CRUSH rule 0 x 213 [3,9,6,5,15,13,1]
+ CRUSH rule 0 x 214 [7,15,4,1,10,2,13]
+ CRUSH rule 0 x 215 [6,1,4,13,3,11,14]
+ CRUSH rule 0 x 216 [12,9,6,2,1,11,5]
+ CRUSH rule 0 x 217 [12,11,1,14,2,4,7]
+ CRUSH rule 0 x 218 [12,10,15,6,1,4,9]
+ CRUSH rule 0 x 219 [3,11,14,6,4,1,13]
+ CRUSH rule 0 x 220 [14,4,3,12,10,9,6]
+ CRUSH rule 0 x 221 [15,5,2,6,12,11,9]
+ CRUSH rule 0 x 222 [10,4,3,15,7,12,1]
+ CRUSH rule 0 x 223 [9,7,11,1,4,14,13]
+ CRUSH rule 0 x 224 [1,7,10,2,12,9,14]
+ CRUSH rule 0 x 225 [10,5,2,6,1,13,9]
+ CRUSH rule 0 x 226 [4,1,9,3,13,10,15]
+ CRUSH rule 0 x 227 [7,2,12,15,5,11]
+ CRUSH rule 0 x 228 [2,15,11,1,6,13,9]
+ CRUSH rule 0 x 229 [9,3,7,14,1,12,4]
+ CRUSH rule 0 x 230 [10,5,7,2,15,1,13]
+ CRUSH rule 0 x 231 [2,7,5,13,9,15,10]
+ CRUSH rule 0 x 232 [10,5,13,1,9,2,7]
+ CRUSH rule 0 x 233 [6,12,11,4,9,14,1]
+ CRUSH rule 0 x 234 [10,1,2,12,5,9,15]
+ CRUSH rule 0 x 235 [13,14,7,10,1,9,5]
+ CRUSH rule 0 x 236 [2,15,9,12,1,7,4]
+ CRUSH rule 0 x 237 [3,12,9,10,4,7,15]
+ CRUSH rule 0 x 238 [2,10,4,15,6,12,9]
+ CRUSH rule 0 x 239 [4,15,10,7,9,13,3]
+ CRUSH rule 0 x 240 [15,5,13,7,2,9,10]
+ CRUSH rule 0 x 241 [7,9,15,12,1,5,2]
+ CRUSH rule 0 x 242 [14,2,6,9,10,12,5]
+ CRUSH rule 0 x 243 [2,11,5,1,15,6,9]
+ CRUSH rule 0 x 244 [13,9,15,3,11,7,5]
+ CRUSH rule 0 x 245 [12,9,15,3,1,5,10]
+ CRUSH rule 0 x 246 [15,3,5,11,7,1,12]
+ CRUSH rule 0 x 247 [6,4,9,12,1,2,10]
+ CRUSH rule 0 x 248 [5,13,7,11,9,15,3]
+ CRUSH rule 0 x 249 [10,14,7,3,9,13,1]
+ CRUSH rule 0 x 250 [12,15,1,10,5,6,3]
+ CRUSH rule 0 x 251 [13,2,15,5,6,1,9]
+ CRUSH rule 0 x 252 [7,5,13,9,3,10,14]
+ CRUSH rule 0 x 253 [3,13,15,10,7,4]
+ CRUSH rule 0 x 254 [2,9,13,14,4,6,10]
+ CRUSH rule 0 x 255 [1,9,13,2,6,10,4]
+ CRUSH rule 0 x 256 [6,9,13,1,3,14,5]
+ CRUSH rule 0 x 257 [15,12,3,9,6,4,11]
+ CRUSH rule 0 x 258 [12,5,6,10,2,1,14]
+ CRUSH rule 0 x 259 [9,10,4,3,14,13,1]
+ CRUSH rule 0 x 260 [10,12,6,9,3,15,1]
+ CRUSH rule 0 x 261 [13,7,2,1,15,5,11]
+ CRUSH rule 0 x 262 [15,3,12,7,4,9,1]
+ CRUSH rule 0 x 263 [12,6,10,9,5,15,3]
+ CRUSH rule 0 x 264 [13,14,11,3,1,4,7]
+ CRUSH rule 0 x 265 [12,10,14,5,7,1,9]
+ CRUSH rule 0 x 266 [14,7,11,1,2,9,4]
+ CRUSH rule 0 x 267 [12,11,6,5,1,2,15]
+ CRUSH rule 0 x 268 [4,1,15,12,6,11,3]
+ CRUSH rule 0 x 269 [11,1,15,5,13,9,7]
+ CRUSH rule 0 x 270 [7,11,12,3,1,14,9]
+ CRUSH rule 0 x 271 [4,7,3,13,15,10,9]
+ CRUSH rule 0 x 272 [15,5,13,10,6,2]
+ CRUSH rule 0 x 273 [2,10,7,12,1,15,5]
+ CRUSH rule 0 x 274 [10,2,5,6,13,9,15]
+ CRUSH rule 0 x 275 [10,3,4,7,14,13]
+ CRUSH rule 0 x 276 [5,12,9,2,11,7,15]
+ CRUSH rule 0 x 277 [14,3,13,4,1,9,11]
+ CRUSH rule 0 x 278 [5,6,14,3,1,11,13]
+ CRUSH rule 0 x 279 [6,10,13,3,9,4,15]
+ CRUSH rule 0 x 280 [7,3,14,9,1,11,4]
+ CRUSH rule 0 x 281 [5,11,14,7,9,13,2]
+ CRUSH rule 0 x 282 [2,1,13,14,9,7,5]
+ CRUSH rule 0 x 283 [4,1,12,3,10,7,15]
+ CRUSH rule 0 x 284 [5,11,7,15,3,13,1]
+ CRUSH rule 0 x 285 [15,5,3,1,6,13,11]
+ CRUSH rule 0 x 286 [10,4,3,6,12,15,1]
+ CRUSH rule 0 x 287 [12,4,9,1,3,11,15]
+ CRUSH rule 0 x 288 [4,12,10,7,1,3,14]
+ CRUSH rule 0 x 289 [2,5,14,9,13,6,10]
+ CRUSH rule 0 x 290 [12,2,5,6,15,9,1]
+ CRUSH rule 0 x 291 [7,11,1,14,5,9,2]
+ CRUSH rule 0 x 292 [4,10,6,3,14,9,12]
+ CRUSH rule 0 x 293 [6,5,11,1,2,14,12]
+ CRUSH rule 0 x 294 [9,12,3,14,6,11,5]
+ CRUSH rule 0 x 295 [6,10,3,14,9,4,13]
+ CRUSH rule 0 x 296 [3,1,13,7,14,9,10]
+ CRUSH rule 0 x 297 [6,13,4,14,10,1,2]
+ CRUSH rule 0 x 298 [14,9,13,1,4,2,7]
+ CRUSH rule 0 x 299 [14,12,11,6,4,2,1]
+ CRUSH rule 0 x 300 [15,7,10,5,1,3,13]
+ CRUSH rule 0 x 301 [9,11,7,1,13,14,4]
+ CRUSH rule 0 x 302 [9,7,1,13,5,10,3]
+ CRUSH rule 0 x 303 [4,13,3,7,10,15,1]
+ CRUSH rule 0 x 304 [6,9,2,11,15,13,4]
+ CRUSH rule 0 x 305 [13,7,5,11,2,15,9]
+ CRUSH rule 0 x 306 [10,12,4,6,9,2,15]
+ CRUSH rule 0 x 307 [11,12,15,5,6,2,1]
+ CRUSH rule 0 x 308 [12,14,10,9,1,2,5]
+ CRUSH rule 0 x 309 [9,3,12,5,11,15,7]
+ CRUSH rule 0 x 310 [3,1,5,10,14,9,7]
+ CRUSH rule 0 x 311 [3,9,7,1,14,13,10]
+ CRUSH rule 0 x 312 [15,13,9,7,5,10,2]
+ CRUSH rule 0 x 313 [9,15,3,7,5,13,1]
+ CRUSH rule 0 x 314 [2,15,9,5,6,12,1]
+ CRUSH rule 0 x 315 [15,2,13,1,11,9,6]
+ CRUSH rule 0 x 316 [4,9,11,2,12,14,6]
+ CRUSH rule 0 x 317 [1,5,3,13,15,7,10]
+ CRUSH rule 0 x 318 [4,1,15,11,9,13,6]
+ CRUSH rule 0 x 319 [2,15,4,1,11,9,7]
+ CRUSH rule 0 x 320 [5,7,13,9,11,2,1]
+ CRUSH rule 0 x 321 [1,6,11,15,5,3,13]
+ CRUSH rule 0 x 322 [13,7,5,3,14,11,1]
+ CRUSH rule 0 x 323 [7,4,10,1,2,13,14]
+ CRUSH rule 0 x 324 [5,6,10,15,2,13]
+ CRUSH rule 0 x 325 [9,10,14,5,1,6,2]
+ CRUSH rule 0 x 326 [11,7,13,4,2,15,1]
+ CRUSH rule 0 x 327 [12,5,10,14,3,7,9]
+ CRUSH rule 0 x 328 [5,2,6,14,1,11,12]
+ CRUSH rule 0 x 329 [2,6,15,5,9,10,13]
+ CRUSH rule 0 x 330 [3,9,11,13,1,6,5]
+ CRUSH rule 0 x 331 [12,14,6,3,1,4,10]
+ CRUSH rule 0 x 332 [10,12,6,15,9,2,5]
+ CRUSH rule 0 x 333 [6,5,3,12,14,10,9]
+ CRUSH rule 0 x 334 [4,9,2,12,7,11,15]
+ CRUSH rule 0 x 335 [11,7,1,5,13,2,9]
+ CRUSH rule 0 x 336 [6,14,13,2,5,9,11]
+ CRUSH rule 0 x 337 [15,11,3,7,12,5]
+ CRUSH rule 0 x 338 [10,5,3,6,15,1,9]
+ CRUSH rule 0 x 339 [11,14,13,5,3,7,1]
+ CRUSH rule 0 x 340 [11,6,12,4,9,3,14]
+ CRUSH rule 0 x 341 [7,5,2,10,14,9,1]
+ CRUSH rule 0 x 342 [12,14,1,9,2,11,4]
+ CRUSH rule 0 x 343 [12,14,9,6,10,2,4]
+ CRUSH rule 0 x 344 [9,11,5,2,14,13,1]
+ CRUSH rule 0 x 345 [14,2,11,9,6,12,4]
+ CRUSH rule 0 x 346 [5,3,14,10,7,1,13]
+ CRUSH rule 0 x 347 [10,2,12,6,9,1,14]
+ CRUSH rule 0 x 348 [7,9,10,1,14,13,3]
+ CRUSH rule 0 x 349 [9,6,10,12,1,5,14]
+ CRUSH rule 0 x 350 [13,9,15,4,10,7,2]
+ CRUSH rule 0 x 351 [13,5,15,3,1,6,11]
+ CRUSH rule 0 x 352 [1,12,11,9,4,7,3]
+ CRUSH rule 0 x 353 [10,14,12,2,9,1,4]
+ CRUSH rule 0 x 354 [6,3,15,10,9,4,13]
+ CRUSH rule 0 x 355 [13,14,6,10,2,5,1]
+ CRUSH rule 0 x 356 [15,13,2,9,6,5,1]
+ CRUSH rule 0 x 357 [4,11,1,13,3,14,6]
+ CRUSH rule 0 x 358 [12,7,2,9,1,14,10]
+ CRUSH rule 0 x 359 [5,15,7,11,3,13]
+ CRUSH rule 0 x 360 [13,10,1,2,6,14,5]
+ CRUSH rule 0 x 361 [5,3,13,6,1,14,11]
+ CRUSH rule 0 x 362 [2,9,11,13,1,6,5]
+ CRUSH rule 0 x 363 [7,12,3,9,15,4,1]
+ CRUSH rule 0 x 364 [2,12,6,9,5,10,15]
+ CRUSH rule 0 x 365 [13,5,11,15,6,2,9]
+ CRUSH rule 0 x 366 [12,7,3,14,5,10,9]
+ CRUSH rule 0 x 367 [7,13,3,1,5,11,15]
+ CRUSH rule 0 x 368 [7,9,10,15,3,4,13]
+ CRUSH rule 0 x 369 [7,5,3,13,14,9,11]
+ CRUSH rule 0 x 370 [4,7,14,1,2,9,11]
+ CRUSH rule 0 x 371 [1,7,12,3,4,15,10]
+ CRUSH rule 0 x 372 [10,4,3,14,6,1,12]
+ CRUSH rule 0 x 373 [15,5,2,6,13,1,9]
+ CRUSH rule 0 x 374 [3,15,12,5,1,6,10]
+ CRUSH rule 0 x 375 [5,2,14,1,6,13,11]
+ CRUSH rule 0 x 376 [5,14,10,13,3,6,1]
+ CRUSH rule 0 x 377 [1,15,2,4,9,11,12]
+ CRUSH rule 0 x 378 [9,12,2,15,1,5,11]
+ CRUSH rule 0 x 379 [11,2,15,5,7,9,13]
+ CRUSH rule 0 x 380 [6,1,12,11,2,9,5]
+ CRUSH rule 0 x 381 [15,13,7,5,10,2,1]
+ CRUSH rule 0 x 382 [14,3,1,4,13,7,10]
+ CRUSH rule 0 x 383 [3,6,11,4,13,15,1]
+ CRUSH rule 0 x 384 [4,13,6,3,15,11,9]
+ CRUSH rule 0 x 385 [4,6,15,3,10,9,1]
+ CRUSH rule 0 x 386 [14,3,11,13,5,6,9]
+ CRUSH rule 0 x 387 [1,11,5,7,9,2,14]
+ CRUSH rule 0 x 388 [2,6,11,9,15,4,12]
+ CRUSH rule 0 x 389 [12,7,2,4,15,10,1]
+ CRUSH rule 0 x 390 [2,11,13,7,5,9,15]
+ CRUSH rule 0 x 391 [3,4,9,13,7,10,1]
+ CRUSH rule 0 x 392 [11,5,14,7,1,9,2]
+ CRUSH rule 0 x 393 [2,14,5,9,7,13,11]
+ CRUSH rule 0 x 394 [4,9,3,15,13,6,1]
+ CRUSH rule 0 x 395 [10,13,5,15,6,9,3]
+ CRUSH rule 0 x 396 [2,12,15,9,4,6,11]
+ CRUSH rule 0 x 397 [1,14,9,4,12,10,3]
+ CRUSH rule 0 x 398 [9,2,1,5,12,6,11]
+ CRUSH rule 0 x 399 [5,9,14,3,1,10,13]
+ CRUSH rule 0 x 400 [10,6,2,4,15,12,1]
+ CRUSH rule 0 x 401 [6,9,11,12,4,3,15]
+ CRUSH rule 0 x 402 [4,7,9,2,13,1,15]
+ CRUSH rule 0 x 403 [7,15,13,3,5,9,10]
+ CRUSH rule 0 x 404 [14,12,7,9,2,1,5]
+ CRUSH rule 0 x 405 [9,15,11,2,4,7,13]
+ CRUSH rule 0 x 406 [12,14,9,2,7,10,4]
+ CRUSH rule 0 x 407 [9,5,12,10,15,6,3]
+ CRUSH rule 0 x 408 [7,1,5,2,10,15,13]
+ CRUSH rule 0 x 409 [11,2,4,13,1,15,7]
+ CRUSH rule 0 x 410 [6,4,14,2,12,9,10]
+ CRUSH rule 0 x 411 [13,11,15,6,4,1,9]
+ CRUSH rule 0 x 412 [5,9,6,11,14,2,12]
+ CRUSH rule 0 x 413 [13,5,3,11,6,9,1]
+ CRUSH rule 0 x 414 [3,11,9,13,4,1,6]
+ CRUSH rule 0 x 415 [6,10,14,5,1,13,3]
+ CRUSH rule 0 x 416 [13,1,4,7,2,9,14]
+ CRUSH rule 0 x 417 [4,12,1,15,2,11,9]
+ CRUSH rule 0 x 418 [14,5,10,2,6,9,13]
+ CRUSH rule 0 x 419 [5,14,10,9,2,12,6]
+ CRUSH rule 0 x 420 [2,4,9,11,6,14,13]
+ CRUSH rule 0 x 421 [15,4,10,3,9,12,7]
+ CRUSH rule 0 x 422 [4,11,2,7,13,9,15]
+ CRUSH rule 0 x 423 [3,15,12,6,5,1,9]
+ CRUSH rule 0 x 424 [6,10,12,2,5,1,14]
+ CRUSH rule 0 x 425 [11,15,2,13,5,7,9]
+ CRUSH rule 0 x 426 [12,4,7,1,9,10,14]
+ CRUSH rule 0 x 427 [14,10,3,1,9,7,5]
+ CRUSH rule 0 x 428 [12,7,9,4,2,1,14]
+ CRUSH rule 0 x 429 [3,4,9,7,11,12,1]
+ CRUSH rule 0 x 430 [3,5,10,13,1,15,6]
+ CRUSH rule 0 x 431 [9,3,7,1,12,5,14]
+ CRUSH rule 0 x 432 [4,1,12,7,15,2,10]
+ CRUSH rule 0 x 433 [4,11,12,15,7,3]
+ CRUSH rule 0 x 434 [2,14,9,1,5,11,7]
+ CRUSH rule 0 x 435 [13,11,5,6,9,2,15]
+ CRUSH rule 0 x 436 [9,15,10,2,4,1,12]
+ CRUSH rule 0 x 437 [9,6,3,14,10,12,5]
+ CRUSH rule 0 x 438 [7,2,13,4,11,1,9]
+ CRUSH rule 0 x 439 [7,14,4,3,12,10]
+ CRUSH rule 0 x 440 [14,11,9,2,7,12,1]
+ CRUSH rule 0 x 441 [2,4,11,9,13,6,1]
+ CRUSH rule 0 x 442 [10,13,9,7,15,1,4]
+ CRUSH rule 0 x 443 [12,15,10,9,2,1,6]
+ CRUSH rule 0 x 444 [4,13,7,14,3,1,9]
+ CRUSH rule 0 x 445 [4,2,15,7,1,9,11]
+ CRUSH rule 0 x 446 [12,10,6,9,4,1,15]
+ CRUSH rule 0 x 447 [15,7,13,1,4,9,3]
+ CRUSH rule 0 x 448 [5,2,13,7,15,10]
+ CRUSH rule 0 x 449 [14,5,3,12,10,9,6]
+ CRUSH rule 0 x 450 [2,4,6,9,15,1,13]
+ CRUSH rule 0 x 451 [6,14,11,3,9,1,12]
+ CRUSH rule 0 x 452 [14,9,10,4,2,13,7]
+ CRUSH rule 0 x 453 [5,15,13,2,6,9,11]
+ CRUSH rule 0 x 454 [10,4,2,6,15,12,9]
+ CRUSH rule 0 x 455 [6,13,2,4,10,1,15]
+ CRUSH rule 0 x 456 [5,7,13,1,11,3,9]
+ CRUSH rule 0 x 457 [9,1,5,7,11,13,15]
+ CRUSH rule 0 x 458 [9,11,15,4,7,2,12]
+ CRUSH rule 0 x 459 [13,15,11,1,5,2,6]
+ CRUSH rule 0 x 460 [5,12,10,15,7,3,9]
+ CRUSH rule 0 x 461 [4,3,9,13,15,6,10]
+ CRUSH rule 0 x 462 [4,7,12,14,11,1,3]
+ CRUSH rule 0 x 463 [4,12,14,11,2,7,1]
+ CRUSH rule 0 x 464 [4,2,15,10,1,9,13]
+ CRUSH rule 0 x 465 [5,10,9,7,13,1,3]
+ CRUSH rule 0 x 466 [13,5,2,15,9,11,6]
+ CRUSH rule 0 x 467 [13,6,14,3,9,1,11]
+ CRUSH rule 0 x 468 [10,7,12,14,4,1,9]
+ CRUSH rule 0 x 469 [4,9,6,14,12,11,3]
+ CRUSH rule 0 x 470 [3,9,12,15,5,6,10]
+ CRUSH rule 0 x 471 [6,1,5,14,13,10,9]
+ CRUSH rule 0 x 472 [2,14,7,5,13,1,11]
+ CRUSH rule 0 x 473 [15,10,6,9,4,12,2]
+ CRUSH rule 0 x 474 [15,10,4,12,6,9,2]
+ CRUSH rule 0 x 475 [10,5,12,9,14,3,6]
+ CRUSH rule 0 x 476 [3,6,10,12,1,15,9]
+ CRUSH rule 0 x 477 [6,13,5,15,11,9,2]
+ CRUSH rule 0 x 478 [4,15,1,3,7,12,10]
+ CRUSH rule 0 x 479 [13,11,1,6,14,5,9]
+ CRUSH rule 0 x 480 [1,13,6,4,9,14,11]
+ CRUSH rule 0 x 481 [15,12,7,9,1,3,10]
+ CRUSH rule 0 x 482 [2,12,9,1,7,11,14]
+ CRUSH rule 0 x 483 [10,1,4,15,9,7,13]
+ CRUSH rule 0 x 484 [1,4,10,13,7,14,2]
+ CRUSH rule 0 x 485 [9,4,3,1,14,12,7]
+ CRUSH rule 0 x 486 [3,10,15,9,7,13,4]
+ CRUSH rule 0 x 487 [12,11,4,14,7,2,1]
+ CRUSH rule 0 x 488 [14,4,1,9,2,6,10]
+ CRUSH rule 0 x 489 [11,4,2,13,15,7]
+ CRUSH rule 0 x 490 [4,9,1,3,13,15,6]
+ CRUSH rule 0 x 491 [1,12,5,2,14,11,6]
+ CRUSH rule 0 x 492 [5,7,11,3,14,9,1]
+ CRUSH rule 0 x 493 [12,1,4,15,3,11,9]
+ CRUSH rule 0 x 494 [1,7,13,4,15,9,10]
+ CRUSH rule 0 x 495 [3,15,7,1,9,5,12]
+ CRUSH rule 0 x 496 [5,3,7,13,9,14,10]
+ CRUSH rule 0 x 497 [13,10,3,6,5,14,1]
+ CRUSH rule 0 x 498 [10,6,1,5,9,12,3]
+ CRUSH rule 0 x 499 [14,3,12,5,1,11,9]
+ CRUSH rule 0 x 500 [15,9,6,12,11,2,1]
+ CRUSH rule 0 x 501 [10,13,1,9,3,14,5]
+ CRUSH rule 0 x 502 [5,1,14,11,7,12,9]
+ CRUSH rule 0 x 503 [15,10,7,9,1,12,4]
+ CRUSH rule 0 x 504 [13,2,7,1,14,11,5]
+ CRUSH rule 0 x 505 [12,7,5,2,14,10,9]
+ CRUSH rule 0 x 506 [11,7,9,14,12,1,2]
+ CRUSH rule 0 x 507 [4,14,13,3,9,7,1]
+ CRUSH rule 0 x 508 [12,1,4,9,2,11,15]
+ CRUSH rule 0 x 509 [4,2,6,9,14,1,10]
+ CRUSH rule 0 x 510 [5,3,1,12,11,14,9]
+ CRUSH rule 0 x 511 [2,12,10,6,14,5]
+ CRUSH rule 0 x 512 [15,11,3,5,7,1,13]
+ CRUSH rule 0 x 513 [4,9,11,3,13,7,1]
+ CRUSH rule 0 x 514 [11,9,3,4,12,15,6]
+ CRUSH rule 0 x 515 [12,14,6,5,3,9,1]
+ CRUSH rule 0 x 516 [14,11,1,12,3,7,4]
+ CRUSH rule 0 x 517 [11,5,6,13,9,3,14]
+ CRUSH rule 0 x 518 [3,5,7,12,15,11,9]
+ CRUSH rule 0 x 519 [12,14,2,1,4,6,9]
+ CRUSH rule 0 x 520 [12,4,2,10,6,15,9]
+ CRUSH rule 0 x 521 [11,5,9,6,15,3,13]
+ CRUSH rule 0 x 522 [4,12,11,1,15,3,9]
+ CRUSH rule 0 x 523 [3,1,5,9,15,10,13]
+ CRUSH rule 0 x 524 [15,9,3,11,13,7,4]
+ CRUSH rule 0 x 525 [3,15,11,6,9,12,4]
+ CRUSH rule 0 x 526 [10,2,5,13,6,15,1]
+ CRUSH rule 0 x 527 [3,13,4,1,9,10,14]
+ CRUSH rule 0 x 528 [12,7,15,10,2,5,9]
+ CRUSH rule 0 x 529 [6,4,10,12,2,9,14]
+ CRUSH rule 0 x 530 [11,9,12,7,5,1,3]
+ CRUSH rule 0 x 531 [9,15,4,7,2,13,1]
+ CRUSH rule 0 x 532 [5,3,13,7,9,14,1]
+ CRUSH rule 0 x 533 [12,15,1,2,7,5,10]
+ CRUSH rule 0 x 534 [11,9,3,7,15,4,1]
+ CRUSH rule 0 x 535 [11,1,3,5,14,9,12]
+ CRUSH rule 0 x 536 [9,1,14,13,4,6,2]
+ CRUSH rule 0 x 537 [15,5,13,2,7,11]
+ CRUSH rule 0 x 538 [13,5,11,2,6,15,9]
+ CRUSH rule 0 x 539 [10,12,6,14,1,2,9]
+ CRUSH rule 0 x 540 [12,15,7,3,9,11,1]
+ CRUSH rule 0 x 541 [2,1,6,11,14,13,4]
+ CRUSH rule 0 x 542 [3,9,15,5,11,12,7]
+ CRUSH rule 0 x 543 [4,10,9,3,6,13,14]
+ CRUSH rule 0 x 544 [3,15,9,11,7,4,12]
+ CRUSH rule 0 x 545 [14,10,7,12,4,9,1]
+ CRUSH rule 0 x 546 [5,15,13,7,1,10,9]
+ CRUSH rule 0 x 547 [5,13,7,9,3,14,10]
+ CRUSH rule 0 x 548 [11,7,12,15,4,2]
+ CRUSH rule 0 x 549 [14,1,4,9,13,6,3]
+ CRUSH rule 0 x 550 [9,15,3,13,1,6,4]
+ CRUSH rule 0 x 551 [11,2,15,6,13,5,1]
+ CRUSH rule 0 x 552 [2,11,14,1,9,6,5]
+ CRUSH rule 0 x 553 [11,9,14,6,4,13,3]
+ CRUSH rule 0 x 554 [11,14,6,4,13,9,3]
+ CRUSH rule 0 x 555 [6,5,10,9,14,2,13]
+ CRUSH rule 0 x 556 [15,6,3,13,11,4,1]
+ CRUSH rule 0 x 557 [12,2,5,14,10,9,6]
+ CRUSH rule 0 x 558 [12,1,6,15,5,10,3]
+ CRUSH rule 0 x 559 [2,13,5,10,14,7,1]
+ CRUSH rule 0 x 560 [4,9,12,6,3,10,1]
+ CRUSH rule 0 x 561 [12,7,1,2,5,15,11]
+ CRUSH rule 0 x 562 [7,13,9,14,2,1,11]
+ CRUSH rule 0 x 563 [15,4,3,10,13,9,7]
+ CRUSH rule 0 x 564 [2,13,7,1,15,10,4]
+ CRUSH rule 0 x 565 [3,12,4,1,14,7,11]
+ CRUSH rule 0 x 566 [6,14,4,2,13,11]
+ CRUSH rule 0 x 567 [15,4,11,6,3,12]
+ CRUSH rule 0 x 568 [4,14,1,6,10,13,3]
+ CRUSH rule 0 x 569 [11,3,15,13,5,1,9]
+ CRUSH rule 0 x 570 [1,10,13,4,7,2,9]
+ CRUSH rule 0 x 571 [10,12,14,9,4,2,1]
+ CRUSH rule 0 x 572 [12,14,3,10,6,1,4]
+ CRUSH rule 0 x 573 [7,15,11,2,12,9,4]
+ CRUSH rule 0 x 574 [11,14,13,1,3,7,4]
+ CRUSH rule 0 x 575 [5,13,15,9,6,10,2]
+ CRUSH rule 0 x 576 [3,15,11,9,1,6,5]
+ CRUSH rule 0 x 577 [13,9,6,15,3,11,4]
+ CRUSH rule 0 x 578 [4,10,1,2,7,13,14]
+ CRUSH rule 0 x 579 [13,1,15,2,10,7,5]
+ CRUSH rule 0 x 580 [3,12,4,1,10,15,7]
+ CRUSH rule 0 x 581 [7,14,12,10,1,2,9]
+ CRUSH rule 0 x 582 [10,5,13,14,1,2,7]
+ CRUSH rule 0 x 583 [4,15,1,9,10,12,2]
+ CRUSH rule 0 x 584 [10,1,5,13,6,9,2]
+ CRUSH rule 0 x 585 [5,3,6,1,11,14,13]
+ CRUSH rule 0 x 586 [7,10,14,12,9,3,5]
+ CRUSH rule 0 x 587 [11,6,9,4,1,14,13]
+ CRUSH rule 0 x 588 [3,12,7,15,4,9,1]
+ CRUSH rule 0 x 589 [9,7,12,1,10,3,4]
+ CRUSH rule 0 x 590 [12,1,3,9,10,6,4]
+ CRUSH rule 0 x 591 [2,6,14,13,9,11,4]
+ CRUSH rule 0 x 592 [15,12,9,7,5,2,11]
+ CRUSH rule 0 x 593 [13,14,5,11,9,6,2]
+ CRUSH rule 0 x 594 [12,14,2,9,7,4,1]
+ CRUSH rule 0 x 595 [12,7,10,3,1,14,9]
+ CRUSH rule 0 x 596 [2,7,12,11,1,5,15]
+ CRUSH rule 0 x 597 [15,1,2,10,7,13,5]
+ CRUSH rule 0 x 598 [11,5,9,14,12,7,3]
+ CRUSH rule 0 x 599 [13,11,1,5,6,2,15]
+ CRUSH rule 0 x 600 [4,12,3,10,9,7,1]
+ CRUSH rule 0 x 601 [13,5,15,2,1,7,9]
+ CRUSH rule 0 x 602 [3,11,7,1,13,15,5]
+ CRUSH rule 0 x 603 [3,1,4,14,10,9,6]
+ CRUSH rule 0 x 604 [14,2,6,1,11,13,9]
+ CRUSH rule 0 x 605 [2,7,12,5,14,10,1]
+ CRUSH rule 0 x 606 [12,15,1,5,7,9,3]
+ CRUSH rule 0 x 607 [3,9,10,14,7,1,4]
+ CRUSH rule 0 x 608 [13,10,1,7,9,15,5]
+ CRUSH rule 0 x 609 [14,3,7,9,11,12,5]
+ CRUSH rule 0 x 610 [7,10,5,1,12,2,15]
+ CRUSH rule 0 x 611 [13,1,5,3,10,7,15]
+ CRUSH rule 0 x 612 [7,1,2,13,9,15,4]
+ CRUSH rule 0 x 613 [10,7,14,9,5,2,13]
+ CRUSH rule 0 x 614 [9,4,15,3,1,11,6]
+ CRUSH rule 0 x 615 [9,4,11,2,1,12,6]
+ CRUSH rule 0 x 616 [10,14,1,5,3,6,12]
+ CRUSH rule 0 x 617 [15,7,2,11,12,1,9]
+ CRUSH rule 0 x 618 [4,2,10,6,14,9,1]
+ CRUSH rule 0 x 619 [15,4,3,9,6,1,13]
+ CRUSH rule 0 x 620 [3,7,11,14,13,1,5]
+ CRUSH rule 0 x 621 [3,6,4,14,1,11,13]
+ CRUSH rule 0 x 622 [10,2,13,5,15,9,1]
+ CRUSH rule 0 x 623 [4,9,14,7,3,13,11]
+ CRUSH rule 0 x 624 [3,9,15,6,10,1,5]
+ CRUSH rule 0 x 625 [11,7,3,5,13,15,9]
+ CRUSH rule 0 x 626 [10,12,2,1,9,7,5]
+ CRUSH rule 0 x 627 [1,12,10,14,3,5,9]
+ CRUSH rule 0 x 628 [15,13,11,4,2,1,7]
+ CRUSH rule 0 x 629 [5,6,15,12,1,10,3]
+ CRUSH rule 0 x 630 [1,4,12,9,3,7,15]
+ CRUSH rule 0 x 631 [5,7,1,15,12,11,3]
+ CRUSH rule 0 x 632 [12,3,11,9,6,1,15]
+ CRUSH rule 0 x 633 [14,4,3,7,10,12,9]
+ CRUSH rule 0 x 634 [6,9,5,3,13,11,14]
+ CRUSH rule 0 x 635 [6,5,2,15,9,12,11]
+ CRUSH rule 0 x 636 [13,6,11,3,15,9,1]
+ CRUSH rule 0 x 637 [3,1,10,6,9,12,4]
+ CRUSH rule 0 x 638 [10,15,3,5,13,1,7]
+ CRUSH rule 0 x 639 [6,9,14,4,3,1,10]
+ CRUSH rule 0 x 640 [9,6,1,11,14,2,4]
+ CRUSH rule 0 x 641 [10,6,5,14,1,9,12]
+ CRUSH rule 0 x 642 [1,15,4,6,2,10,9]
+ CRUSH rule 0 x 643 [3,7,5,1,10,15,13]
+ CRUSH rule 0 x 644 [15,13,6,9,3,11,5]
+ CRUSH rule 0 x 645 [14,2,4,9,10,1,7]
+ CRUSH rule 0 x 646 [5,13,14,1,6,9,2]
+ CRUSH rule 0 x 647 [10,1,9,13,6,2,14]
+ CRUSH rule 0 x 648 [6,5,2,14,11,1,12]
+ CRUSH rule 0 x 649 [3,9,13,11,4,14,1]
+ CRUSH rule 0 x 650 [10,9,4,15,12,7,1]
+ CRUSH rule 0 x 651 [3,9,5,7,14,1,13]
+ CRUSH rule 0 x 652 [15,9,4,6,13,1,2]
+ CRUSH rule 0 x 653 [11,14,1,3,6,9,12]
+ CRUSH rule 0 x 654 [13,6,2,10,15,4,1]
+ CRUSH rule 0 x 655 [6,3,4,15,12,11,1]
+ CRUSH rule 0 x 656 [3,15,1,4,6,12,11]
+ CRUSH rule 0 x 657 [11,15,3,5,7,13,1]
+ CRUSH rule 0 x 658 [7,2,10,12,1,4,9]
+ CRUSH rule 0 x 659 [2,5,14,6,10,12]
+ CRUSH rule 0 x 660 [13,14,10,6,4,9,3]
+ CRUSH rule 0 x 661 [7,15,3,12,11,4,9]
+ CRUSH rule 0 x 662 [15,2,12,5,1,10,9]
+ CRUSH rule 0 x 663 [14,9,13,10,5,3,1]
+ CRUSH rule 0 x 664 [6,10,12,4,9,2,1]
+ CRUSH rule 0 x 665 [2,9,12,1,7,10,4]
+ CRUSH rule 0 x 666 [12,3,6,1,15,9,10]
+ CRUSH rule 0 x 667 [1,9,12,10,2,14,7]
+ CRUSH rule 0 x 668 [9,5,1,2,6,11,13]
+ CRUSH rule 0 x 669 [9,7,14,5,11,13,1]
+ CRUSH rule 0 x 670 [6,10,9,13,1,2,15]
+ CRUSH rule 0 x 671 [6,15,5,10,13,3]
+ CRUSH rule 0 x 672 [2,9,13,1,4,14,6]
+ CRUSH rule 0 x 673 [7,10,5,9,15,13,2]
+ CRUSH rule 0 x 674 [7,12,10,1,14,9,3]
+ CRUSH rule 0 x 675 [9,5,1,10,6,14,12]
+ CRUSH rule 0 x 676 [10,12,2,1,4,15,7]
+ CRUSH rule 0 x 677 [2,12,1,4,10,6,15]
+ CRUSH rule 0 x 678 [1,2,4,10,12,14,9]
+ CRUSH rule 0 x 679 [5,6,12,15,9,11,3]
+ CRUSH rule 0 x 680 [7,11,3,1,15,4,9]
+ CRUSH rule 0 x 681 [6,4,3,11,14,13,1]
+ CRUSH rule 0 x 682 [6,1,11,15,12,2,5]
+ CRUSH rule 0 x 683 [6,13,2,4,9,14,10]
+ CRUSH rule 0 x 684 [9,11,3,7,15,4,13]
+ CRUSH rule 0 x 685 [5,1,15,7,9,2,10]
+ CRUSH rule 0 x 686 [1,9,11,14,6,13,4]
+ CRUSH rule 0 x 687 [7,13,3,5,11,9,15]
+ CRUSH rule 0 x 688 [11,9,1,14,3,5,7]
+ CRUSH rule 0 x 689 [5,2,9,12,1,14,11]
+ CRUSH rule 0 x 690 [9,7,10,3,13,15,5]
+ CRUSH rule 0 x 691 [11,15,9,5,7,13,2]
+ CRUSH rule 0 x 692 [15,5,1,2,9,11,12]
+ CRUSH rule 0 x 693 [5,6,12,15,2,10,9]
+ CRUSH rule 0 x 694 [4,7,1,10,12,3,14]
+ CRUSH rule 0 x 695 [6,13,14,10,9,5,1]
+ CRUSH rule 0 x 696 [1,2,4,14,7,11,13]
+ CRUSH rule 0 x 697 [13,11,3,6,4,14,9]
+ CRUSH rule 0 x 698 [11,13,4,2,6,1,9]
+ CRUSH rule 0 x 699 [7,14,12,4,2,11]
+ CRUSH rule 0 x 700 [12,14,11,9,4,6,3]
+ CRUSH rule 0 x 701 [3,13,1,14,4,7,11]
+ CRUSH rule 0 x 702 [3,12,15,6,5,11,1]
+ CRUSH rule 0 x 703 [15,11,13,3,4,7,1]
+ CRUSH rule 0 x 704 [6,4,2,15,11,1,13]
+ CRUSH rule 0 x 705 [14,6,11,5,1,13,9]
+ CRUSH rule 0 x 706 [1,12,3,6,4,10,15]
+ CRUSH rule 0 x 707 [4,7,14,3,10,9,13]
+ CRUSH rule 0 x 708 [3,10,5,1,15,9,7]
+ CRUSH rule 0 x 709 [11,12,3,7,5,14,1]
+ CRUSH rule 0 x 710 [14,2,11,9,5,7,12]
+ CRUSH rule 0 x 711 [14,3,9,10,12,5,6]
+ CRUSH rule 0 x 712 [12,3,11,15,9,1,6]
+ CRUSH rule 0 x 713 [11,9,3,15,13,6,4]
+ CRUSH rule 0 x 714 [12,1,9,7,2,15,10]
+ CRUSH rule 0 x 715 [6,1,14,4,11,12,3]
+ CRUSH rule 0 x 716 [11,13,9,14,5,2,1]
+ CRUSH rule 0 x 717 [12,4,10,9,15,1,2]
+ CRUSH rule 0 x 718 [7,15,5,2,11,13]
+ CRUSH rule 0 x 719 [5,15,13,3,1,7,11]
+ CRUSH rule 0 x 720 [4,13,10,2,7,9,1]
+ CRUSH rule 0 x 721 [11,3,14,9,1,12,4]
+ CRUSH rule 0 x 722 [2,4,6,1,9,15,13]
+ CRUSH rule 0 x 723 [2,1,12,15,11,7,5]
+ CRUSH rule 0 x 724 [7,1,9,10,5,15,13]
+ CRUSH rule 0 x 725 [11,12,7,15,4,1,2]
+ CRUSH rule 0 x 726 [7,14,4,3,11,13,9]
+ CRUSH rule 0 x 727 [2,5,1,11,15,7,12]
+ CRUSH rule 0 x 728 [13,11,4,6,15,2]
+ CRUSH rule 0 x 729 [15,11,4,6,2,9,1]
+ CRUSH rule 0 x 730 [3,7,1,13,11,15,9]
+ CRUSH rule 0 x 731 [9,1,6,5,2,11,13]
+ CRUSH rule 0 x 732 [1,2,10,13,9,4,7]
+ CRUSH rule 0 x 733 [11,3,5,6,1,9,12]
+ CRUSH rule 0 x 734 [14,3,11,7,12,9,4]
+ CRUSH rule 0 x 735 [6,9,2,10,13,14,5]
+ CRUSH rule 0 x 736 [3,9,1,11,7,5,13]
+ CRUSH rule 0 x 737 [1,4,2,12,9,10,6]
+ CRUSH rule 0 x 738 [11,15,7,4,9,2,12]
+ CRUSH rule 0 x 739 [11,12,6,2,4,1,14]
+ CRUSH rule 0 x 740 [7,9,10,13,1,15,2]
+ CRUSH rule 0 x 741 [12,11,7,15,2,5]
+ CRUSH rule 0 x 742 [9,7,4,11,12,1,14]
+ CRUSH rule 0 x 743 [5,13,9,15,10,7,3]
+ CRUSH rule 0 x 744 [6,2,13,1,14,11,4]
+ CRUSH rule 0 x 745 [3,6,1,4,11,12,14]
+ CRUSH rule 0 x 746 [3,7,9,10,14,5,1]
+ CRUSH rule 0 x 747 [15,11,5,2,13,9,1]
+ CRUSH rule 0 x 748 [6,10,13,2,14,5,9]
+ CRUSH rule 0 x 749 [14,9,10,7,5,1,2]
+ CRUSH rule 0 x 750 [1,14,6,5,11,2,13]
+ CRUSH rule 0 x 751 [15,1,6,9,5,11,12]
+ CRUSH rule 0 x 752 [13,1,7,3,11,15,9]
+ CRUSH rule 0 x 753 [4,11,1,3,15,7,13]
+ CRUSH rule 0 x 754 [14,12,11,4,2,1,9]
+ CRUSH rule 0 x 755 [13,6,1,10,4,2,14]
+ CRUSH rule 0 x 756 [3,4,14,6,1,10,13]
+ CRUSH rule 0 x 757 [10,6,1,4,13,15,2]
+ CRUSH rule 0 x 758 [6,3,4,10,15,13,9]
+ CRUSH rule 0 x 759 [5,7,3,14,11,1,9]
+ CRUSH rule 0 x 760 [1,15,10,12,4,3,9]
+ CRUSH rule 0 x 761 [2,12,1,14,5,7,10]
+ CRUSH rule 0 x 762 [1,4,10,9,3,7,14]
+ CRUSH rule 0 x 763 [4,13,1,14,7,10,2]
+ CRUSH rule 0 x 764 [1,14,6,13,9,5,2]
+ CRUSH rule 0 x 765 [9,15,2,13,4,1,11]
+ CRUSH rule 0 x 766 [11,2,7,15,9,12,4]
+ CRUSH rule 0 x 767 [6,11,4,3,12,14]
+ CRUSH rule 0 x 768 [2,12,15,7,1,11,9]
+ CRUSH rule 0 x 769 [15,1,9,2,11,12,7]
+ CRUSH rule 0 x 770 [15,13,4,6,3,10,1]
+ CRUSH rule 0 x 771 [9,2,12,11,6,14,5]
+ CRUSH rule 0 x 772 [4,3,13,11,14,1,7]
+ CRUSH rule 0 x 773 [3,7,4,15,1,12,11]
+ CRUSH rule 0 x 774 [12,6,3,15,5,9,10]
+ CRUSH rule 0 x 775 [5,10,14,2,6,1,13]
+ CRUSH rule 0 x 776 [10,15,3,9,6,13,1]
+ CRUSH rule 0 x 777 [11,13,4,7,1,14,9]
+ CRUSH rule 0 x 778 [13,1,9,11,15,6,3]
+ CRUSH rule 0 x 779 [5,11,1,14,2,9,13]
+ CRUSH rule 0 x 780 [13,9,3,6,4,1,14]
+ CRUSH rule 0 x 781 [5,7,14,3,1,12,11]
+ CRUSH rule 0 x 782 [2,15,9,7,11,13,4]
+ CRUSH rule 0 x 783 [12,7,5,14,9,1,2]
+ CRUSH rule 0 x 784 [14,1,10,13,3,4,7]
+ CRUSH rule 0 x 785 [6,12,1,2,4,9,15]
+ CRUSH rule 0 x 786 [10,5,2,15,1,7,12]
+ CRUSH rule 0 x 787 [1,12,10,2,9,4,14]
+ CRUSH rule 0 x 788 [4,2,9,13,6,15,11]
+ CRUSH rule 0 x 789 [9,2,14,7,4,12,1]
+ CRUSH rule 0 x 790 [15,2,7,4,1,10,13]
+ CRUSH rule 0 x 791 [9,4,7,13,14,11,1]
+ CRUSH rule 0 x 792 [6,4,15,10,12,3]
+ CRUSH rule 0 x 793 [15,9,6,2,13,11,4]
+ CRUSH rule 0 x 794 [5,12,2,14,9,10,1]
+ CRUSH rule 0 x 795 [6,14,12,4,10,1,2]
+ CRUSH rule 0 x 796 [11,2,12,6,15,4]
+ CRUSH rule 0 x 797 [14,3,7,1,5,13,11]
+ CRUSH rule 0 x 798 [5,11,6,13,1,3,15]
+ CRUSH rule 0 x 799 [2,9,14,4,13,6,11]
+ CRUSH rule 0 x 800 [6,3,4,11,15,13]
+ CRUSH rule 0 x 801 [2,5,6,13,9,1,10]
+ CRUSH rule 0 x 802 [1,4,12,7,3,9,10]
+ CRUSH rule 0 x 803 [7,2,4,1,11,13,9]
+ CRUSH rule 0 x 804 [5,14,9,7,3,1,12]
+ CRUSH rule 0 x 805 [13,4,3,1,10,15,7]
+ CRUSH rule 0 x 806 [6,2,13,4,15,1,10]
+ CRUSH rule 0 x 807 [14,2,7,4,9,12,1]
+ CRUSH rule 0 x 808 [2,15,12,7,9,1,5]
+ CRUSH rule 0 x 809 [1,11,7,12,4,2,15]
+ CRUSH rule 0 x 810 [2,5,9,12,15,1,7]
+ CRUSH rule 0 x 811 [15,6,3,10,1,5,9]
+ CRUSH rule 0 x 812 [7,11,2,14,9,5,12]
+ CRUSH rule 0 x 813 [4,10,13,14,2,6,9]
+ CRUSH rule 0 x 814 [13,4,9,3,10,6,15]
+ CRUSH rule 0 x 815 [15,12,9,4,10,6,1]
+ CRUSH rule 0 x 816 [14,10,13,7,3,9,4]
+ CRUSH rule 0 x 817 [10,7,2,15,13,9,5]
+ CRUSH rule 0 x 818 [15,2,11,4,1,12,6]
+ CRUSH rule 0 x 819 [5,12,10,6,1,14,3]
+ CRUSH rule 0 x 820 [3,6,9,12,11,15,4]
+ CRUSH rule 0 x 821 [15,10,9,13,3,4,7]
+ CRUSH rule 0 x 822 [10,13,2,9,7,4,14]
+ CRUSH rule 0 x 823 [2,6,12,10,15,4,1]
+ CRUSH rule 0 x 824 [3,7,9,13,15,5,10]
+ CRUSH rule 0 x 825 [10,5,14,6,12,9,3]
+ CRUSH rule 0 x 826 [5,2,11,15,1,12,9]
+ CRUSH rule 0 x 827 [13,5,1,3,7,9,11]
+ CRUSH rule 0 x 828 [12,6,10,5,1,9,2]
+ CRUSH rule 0 x 829 [13,6,15,10,5,3,9]
+ CRUSH rule 0 x 830 [15,13,2,9,7,11,1]
+ CRUSH rule 0 x 831 [1,4,11,12,6,3,15]
+ CRUSH rule 0 x 832 [14,11,13,2,9,4,6]
+ CRUSH rule 0 x 833 [9,13,3,11,7,5,15]
+ CRUSH rule 0 x 834 [9,7,5,1,11,2,13]
+ CRUSH rule 0 x 835 [14,3,13,6,4,9,1]
+ CRUSH rule 0 x 836 [3,9,10,13,1,5,14]
+ CRUSH rule 0 x 837 [15,12,11,2,7,9,5]
+ CRUSH rule 0 x 838 [12,14,9,2,5,7,11]
+ CRUSH rule 0 x 839 [3,4,6,10,15,1,13]
+ CRUSH rule 0 x 840 [10,15,12,4,7,1,2]
+ CRUSH rule 0 x 841 [3,5,7,12,11,15,1]
+ CRUSH rule 0 x 842 [9,13,2,6,5,14,10]
+ CRUSH rule 0 x 843 [14,7,4,9,3,12,1]
+ CRUSH rule 0 x 844 [7,1,4,15,9,2,11]
+ CRUSH rule 0 x 845 [13,6,1,15,4,2,11]
+ CRUSH rule 0 x 846 [3,7,15,13,1,9,10]
+ CRUSH rule 0 x 847 [12,15,11,5,2,7,1]
+ CRUSH rule 0 x 848 [11,13,1,14,5,9,2]
+ CRUSH rule 0 x 849 [3,15,11,9,6,1,13]
+ CRUSH rule 0 x 850 [1,3,10,6,14,4,9]
+ CRUSH rule 0 x 851 [14,4,3,6,11,1,13]
+ CRUSH rule 0 x 852 [9,12,4,7,15,2,11]
+ CRUSH rule 0 x 853 [13,14,6,11,2,4,9]
+ CRUSH rule 0 x 854 [7,11,12,1,4,15,3]
+ CRUSH rule 0 x 855 [14,4,12,6,3,1,10]
+ CRUSH rule 0 x 856 [5,10,7,3,15,9,12]
+ CRUSH rule 0 x 857 [4,3,13,11,9,1,7]
+ CRUSH rule 0 x 858 [5,15,6,3,9,12,1]
+ CRUSH rule 0 x 859 [5,15,6,2,1,11,12]
+ CRUSH rule 0 x 860 [11,14,1,12,6,9,2]
+ CRUSH rule 0 x 861 [13,7,4,10,1,14,3]
+ CRUSH rule 0 x 862 [5,10,9,7,3,12,1]
+ CRUSH rule 0 x 863 [11,6,3,9,4,12,15]
+ CRUSH rule 0 x 864 [6,13,4,2,10,15,1]
+ CRUSH rule 0 x 865 [4,1,14,11,6,9,3]
+ CRUSH rule 0 x 866 [2,13,4,15,9,6,11]
+ CRUSH rule 0 x 867 [12,2,9,10,4,14,6]
+ CRUSH rule 0 x 868 [14,11,7,2,1,4,9]
+ CRUSH rule 0 x 869 [10,13,7,14,3,5,1]
+ CRUSH rule 0 x 870 [14,9,11,4,3,12,6]
+ CRUSH rule 0 x 871 [6,2,1,4,15,13,11]
+ CRUSH rule 0 x 872 [6,1,15,3,10,12,5]
+ CRUSH rule 0 x 873 [2,5,12,10,1,9,15]
+ CRUSH rule 0 x 874 [12,4,7,2,15,10,1]
+ CRUSH rule 0 x 875 [10,6,14,1,12,5,9]
+ CRUSH rule 0 x 876 [14,7,13,3,9,1,11]
+ CRUSH rule 0 x 877 [15,11,13,9,5,1,6]
+ CRUSH rule 0 x 878 [7,14,3,13,9,1,11]
+ CRUSH rule 0 x 879 [12,2,7,4,10,15]
+ CRUSH rule 0 x 880 [2,12,10,7,1,4,9]
+ CRUSH rule 0 x 881 [6,3,1,11,4,15,9]
+ CRUSH rule 0 x 882 [11,13,7,1,2,15,4]
+ CRUSH rule 0 x 883 [13,1,3,10,6,5,9]
+ CRUSH rule 0 x 884 [6,15,4,9,3,11,12]
+ CRUSH rule 0 x 885 [14,7,9,4,2,13,11]
+ CRUSH rule 0 x 886 [13,11,4,2,1,14,9]
+ CRUSH rule 0 x 887 [14,4,12,11,2,6,9]
+ CRUSH rule 0 x 888 [10,12,7,15,9,2,1]
+ CRUSH rule 0 x 889 [15,13,4,1,6,2,10]
+ CRUSH rule 0 x 890 [10,12,14,2,9,5,6]
+ CRUSH rule 0 x 891 [9,5,11,6,3,15,12]
+ CRUSH rule 0 x 892 [12,15,2,4,7,9,11]
+ CRUSH rule 0 x 893 [1,3,5,9,6,10,14]
+ CRUSH rule 0 x 894 [7,2,11,13,4,1,14]
+ CRUSH rule 0 x 895 [2,1,11,5,7,15,13]
+ CRUSH rule 0 x 896 [9,1,14,10,4,12,2]
+ CRUSH rule 0 x 897 [7,5,14,3,1,9,11]
+ CRUSH rule 0 x 898 [10,6,12,9,15,5,2]
+ CRUSH rule 0 x 899 [1,11,5,3,13,14,9]
+ CRUSH rule 0 x 900 [2,9,10,7,13,14,5]
+ CRUSH rule 0 x 901 [9,12,11,3,14,4,1]
+ CRUSH rule 0 x 902 [4,2,6,15,12,10,1]
+ CRUSH rule 0 x 903 [14,10,3,1,12,6,5]
+ CRUSH rule 0 x 904 [15,12,4,9,6,3,11]
+ CRUSH rule 0 x 905 [12,6,11,3,9,4,15]
+ CRUSH rule 0 x 906 [14,11,12,2,4,9,6]
+ CRUSH rule 0 x 907 [7,12,3,9,10,5,14]
+ CRUSH rule 0 x 908 [2,15,9,6,10,13,5]
+ CRUSH rule 0 x 909 [10,14,1,13,2,9,7]
+ CRUSH rule 0 x 910 [12,7,4,15,10,3,1]
+ CRUSH rule 0 x 911 [11,15,2,4,9,13,6]
+ CRUSH rule 0 x 912 [6,4,14,13,3,1,11]
+ CRUSH rule 0 x 913 [4,6,10,1,12,3,9]
+ CRUSH rule 0 x 914 [4,15,2,10,1,13,7]
+ CRUSH rule 0 x 915 [12,14,1,9,4,3,11]
+ CRUSH rule 0 x 916 [3,1,11,5,6,13,14]
+ CRUSH rule 0 x 917 [1,15,6,5,10,3,13]
+ CRUSH rule 0 x 918 [7,14,11,4,9,2,13]
+ CRUSH rule 0 x 919 [10,7,3,13,15,1,4]
+ CRUSH rule 0 x 920 [4,2,10,15,1,13,6]
+ CRUSH rule 0 x 921 [1,11,6,13,4,2,9]
+ CRUSH rule 0 x 922 [6,4,14,13,3,1,10]
+ CRUSH rule 0 x 923 [12,2,5,14,10,1,9]
+ CRUSH rule 0 x 924 [6,2,14,13,9,1,11]
+ CRUSH rule 0 x 925 [12,15,2,10,1,5,7]
+ CRUSH rule 0 x 926 [3,13,10,1,14,9,6]
+ CRUSH rule 0 x 927 [6,5,1,11,14,2,13]
+ CRUSH rule 0 x 928 [13,1,3,9,6,11,15]
+ CRUSH rule 0 x 929 [10,7,1,5,2,12,9]
+ CRUSH rule 0 x 930 [7,15,10,5,1,13,2]
+ CRUSH rule 0 x 931 [6,15,11,9,5,3,1]
+ CRUSH rule 0 x 932 [13,2,5,11,9,1,6]
+ CRUSH rule 0 x 933 [12,7,14,10,4,1,2]
+ CRUSH rule 0 x 934 [12,2,5,7,9,1,15]
+ CRUSH rule 0 x 935 [6,11,1,14,5,13,3]
+ CRUSH rule 0 x 936 [9,12,7,5,1,2,14]
+ CRUSH rule 0 x 937 [14,2,11,1,13,4,9]
+ CRUSH rule 0 x 938 [14,3,5,11,7,9,13]
+ CRUSH rule 0 x 939 [6,4,14,9,12,1,11]
+ CRUSH rule 0 x 940 [13,11,4,2,1,6,15]
+ CRUSH rule 0 x 941 [3,12,4,7,14,10]
+ CRUSH rule 0 x 942 [15,12,10,4,1,9,3]
+ CRUSH rule 0 x 943 [10,2,4,9,6,15,12]
+ CRUSH rule 0 x 944 [2,9,4,7,1,14,12]
+ CRUSH rule 0 x 945 [10,15,2,9,5,12,7]
+ CRUSH rule 0 x 946 [11,15,7,12,5,9,2]
+ CRUSH rule 0 x 947 [11,3,14,1,12,5,6]
+ CRUSH rule 0 x 948 [7,13,11,5,14,2,1]
+ CRUSH rule 0 x 949 [9,1,12,5,15,10,2]
+ CRUSH rule 0 x 950 [9,15,13,6,4,2,10]
+ CRUSH rule 0 x 951 [2,6,12,9,10,4,14]
+ CRUSH rule 0 x 952 [9,7,15,3,5,13,11]
+ CRUSH rule 0 x 953 [1,3,6,10,12,14,4]
+ CRUSH rule 0 x 954 [10,2,14,9,4,6,12]
+ CRUSH rule 0 x 955 [7,14,3,1,10,4,9]
+ CRUSH rule 0 x 956 [1,6,11,5,14,3,9]
+ CRUSH rule 0 x 957 [14,11,1,12,6,9,4]
+ CRUSH rule 0 x 958 [15,4,3,11,1,6,12]
+ CRUSH rule 0 x 959 [2,1,12,15,10,9,4]
+ CRUSH rule 0 x 960 [2,6,11,13,15,4,9]
+ CRUSH rule 0 x 961 [3,13,11,9,6,1,4]
+ CRUSH rule 0 x 962 [5,11,3,14,1,6,13]
+ CRUSH rule 0 x 963 [13,10,15,4,6,9,1]
+ CRUSH rule 0 x 964 [7,11,4,9,2,12,1]
+ CRUSH rule 0 x 965 [12,2,9,7,4,15,11]
+ CRUSH rule 0 x 966 [12,14,9,4,1,2,11]
+ CRUSH rule 0 x 967 [7,5,3,10,12,14]
+ CRUSH rule 0 x 968 [12,15,4,9,11,6,3]
+ CRUSH rule 0 x 969 [11,4,7,1,9,14,13]
+ CRUSH rule 0 x 970 [5,12,10,1,3,14,9]
+ CRUSH rule 0 x 971 [1,9,4,12,7,2,10]
+ CRUSH rule 0 x 972 [12,3,14,5,1,9,7]
+ CRUSH rule 0 x 973 [1,10,4,12,2,7,15]
+ CRUSH rule 0 x 974 [7,11,1,2,15,4,12]
+ CRUSH rule 0 x 975 [7,9,15,12,2,11,4]
+ CRUSH rule 0 x 976 [7,3,15,5,12,11,1]
+ CRUSH rule 0 x 977 [14,3,6,10,4,1,12]
+ CRUSH rule 0 x 978 [12,5,11,1,15,3,6]
+ CRUSH rule 0 x 979 [5,1,13,6,15,10,3]
+ CRUSH rule 0 x 980 [15,11,5,6,1,3,13]
+ CRUSH rule 0 x 981 [5,11,15,12,7,1,2]
+ CRUSH rule 0 x 982 [2,6,14,11,12,9,5]
+ CRUSH rule 0 x 983 [3,12,10,9,14,5,6]
+ CRUSH rule 0 x 984 [15,13,1,10,2,5,7]
+ CRUSH rule 0 x 985 [11,2,15,1,4,13,6]
+ CRUSH rule 0 x 986 [6,13,9,1,15,10,5]
+ CRUSH rule 0 x 987 [13,14,5,10,6,1,3]
+ CRUSH rule 0 x 988 [12,9,10,14,3,1,4]
+ CRUSH rule 0 x 989 [7,4,3,15,9,13,10]
+ CRUSH rule 0 x 990 [1,10,9,13,3,4,6]
+ CRUSH rule 0 x 991 [7,11,1,14,2,5,9]
+ CRUSH rule 0 x 992 [9,10,2,13,7,4,1]
+ CRUSH rule 0 x 993 [6,10,14,12,4,1,2]
+ CRUSH rule 0 x 994 [3,13,15,4,11,7,1]
+ CRUSH rule 0 x 995 [15,6,12,2,5,11]
+ CRUSH rule 0 x 996 [15,10,5,3,13,1,9]
+ CRUSH rule 0 x 997 [15,2,1,12,7,9,4]
+ CRUSH rule 0 x 998 [6,1,9,5,12,11,15]
+ CRUSH rule 0 x 999 [9,10,15,5,13,3,7]
+ CRUSH rule 0 x 1000 [14,2,9,4,12,1,6]
+ CRUSH rule 0 x 1001 [11,14,4,2,6,9,1]
+ CRUSH rule 0 x 1002 [1,10,14,2,9,5,13]
+ CRUSH rule 0 x 1003 [10,7,5,14,2,1,9]
+ CRUSH rule 0 x 1004 [15,1,4,6,10,12,9]
+ CRUSH rule 0 x 1005 [6,12,2,10,9,15,5]
+ CRUSH rule 0 x 1006 [10,12,15,1,2,6,5]
+ CRUSH rule 0 x 1007 [1,7,13,14,3,4,10]
+ CRUSH rule 0 x 1008 [7,4,9,11,3,15,1]
+ CRUSH rule 0 x 1009 [5,2,11,7,15,9,1]
+ CRUSH rule 0 x 1010 [10,2,15,6,9,13,4]
+ CRUSH rule 0 x 1011 [6,3,12,1,10,4,9]
+ CRUSH rule 0 x 1012 [12,6,9,15,3,1,5]
+ CRUSH rule 0 x 1013 [2,14,12,4,9,1,6]
+ CRUSH rule 0 x 1014 [1,13,7,2,10,14,5]
+ CRUSH rule 0 x 1015 [12,6,10,1,4,15,9]
+ CRUSH rule 0 x 1016 [10,13,14,3,5,6,1]
+ CRUSH rule 0 x 1017 [5,11,14,7,13,9,2]
+ CRUSH rule 0 x 1018 [13,11,14,1,9,3,5]
+ CRUSH rule 0 x 1019 [10,13,14,7,5,1,2]
+ CRUSH rule 0 x 1020 [3,1,13,4,10,9,14]
+ CRUSH rule 0 x 1021 [2,11,14,9,4,6,1]
+ CRUSH rule 0 x 1022 [15,5,7,2,12,10]
+ CRUSH rule 0 x 1023 [15,2,9,12,1,7,4]
+ rule 0 (replicated_ruleset) num_rep 7 result size == 6:\t36/1024 (esc)
+ rule 0 (replicated_ruleset) num_rep 7 result size == 7:\t988/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3,15,12,1,4,9]
+ CRUSH rule 0 x 1 [10,15,1,2,13,4,7,9]
+ CRUSH rule 0 x 2 [1,12,2,6,5,10,15]
+ CRUSH rule 0 x 3 [15,4,10,2,9,6,13]
+ CRUSH rule 0 x 4 [14,2,10,1,9,4,7,13]
+ CRUSH rule 0 x 5 [7,4,11,2,13,15,9]
+ CRUSH rule 0 x 6 [12,6,10,9,3,4,14]
+ CRUSH rule 0 x 7 [9,2,6,12,11,4,1,14]
+ CRUSH rule 0 x 8 [10,2,15,1,4,13,6,9]
+ CRUSH rule 0 x 9 [7,1,14,2,11,9,12,4]
+ CRUSH rule 0 x 10 [10,14,4,1,2,7,13,9]
+ CRUSH rule 0 x 11 [13,9,14,7,5,11,2,1]
+ CRUSH rule 0 x 12 [7,1,2,5,13,15,11,9]
+ CRUSH rule 0 x 13 [3,5,12,7,9,1,14,11]
+ CRUSH rule 0 x 14 [13,5,2,7,10,15,1,9]
+ CRUSH rule 0 x 15 [15,1,9,6,13,3,5,11]
+ CRUSH rule 0 x 16 [7,11,14,2,13,1,9,4]
+ CRUSH rule 0 x 17 [10,1,13,2,4,6,14,9]
+ CRUSH rule 0 x 18 [1,7,3,10,5,12,9,14]
+ CRUSH rule 0 x 19 [7,12,2,4,15,10,1]
+ CRUSH rule 0 x 20 [14,12,3,10,9,4,7,1]
+ CRUSH rule 0 x 21 [3,12,1,10,4,15,6]
+ CRUSH rule 0 x 22 [6,3,13,11,4,1,15]
+ CRUSH rule 0 x 23 [10,5,13,9,3,15,1,6]
+ CRUSH rule 0 x 24 [12,11,3,1,9,4,7,15]
+ CRUSH rule 0 x 25 [7,12,15,1,3,10,4,9]
+ CRUSH rule 0 x 26 [1,7,13,2,14,5,9,11]
+ CRUSH rule 0 x 27 [3,6,15,4,13,9,11,1]
+ CRUSH rule 0 x 28 [14,4,3,9,6,11,13]
+ CRUSH rule 0 x 29 [5,14,12,11,6,3,1,9]
+ CRUSH rule 0 x 30 [2,5,6,9,1,11,13,14]
+ CRUSH rule 0 x 31 [5,15,10,1,9,13,6,3]
+ CRUSH rule 0 x 32 [9,10,2,1,13,14,6,4]
+ CRUSH rule 0 x 33 [13,4,9,2,7,1,10,14]
+ CRUSH rule 0 x 34 [13,15,2,4,1,10,9,6]
+ CRUSH rule 0 x 35 [4,14,3,13,10,9,1,6]
+ CRUSH rule 0 x 36 [3,12,9,7,5,10,14,1]
+ CRUSH rule 0 x 37 [9,2,6,14,11,1,4,13]
+ CRUSH rule 0 x 38 [3,4,13,10,9,1,14,6]
+ CRUSH rule 0 x 39 [12,7,14,11,1,9,5,2]
+ CRUSH rule 0 x 40 [10,1,9,5,15,2,6,13]
+ CRUSH rule 0 x 41 [4,9,11,1,14,13,6,3]
+ CRUSH rule 0 x 42 [3,6,14,10,12,5,1]
+ CRUSH rule 0 x 43 [10,5,15,7,2,9,12]
+ CRUSH rule 0 x 44 [11,4,13,3,7,14,9]
+ CRUSH rule 0 x 45 [11,12,15,9,1,5,6,3]
+ CRUSH rule 0 x 46 [6,9,2,14,11,13,1,5]
+ CRUSH rule 0 x 47 [3,9,6,4,13,1,11,15]
+ CRUSH rule 0 x 48 [4,6,2,1,10,14,13]
+ CRUSH rule 0 x 49 [9,15,10,7,4,3,13]
+ CRUSH rule 0 x 50 [14,12,1,4,2,11,6,9]
+ CRUSH rule 0 x 51 [10,6,5,12,15,2,1,9]
+ CRUSH rule 0 x 52 [12,1,9,11,7,3,14,4]
+ CRUSH rule 0 x 53 [3,6,13,9,5,1,11,15]
+ CRUSH rule 0 x 54 [4,13,9,2,14,10,6,1]
+ CRUSH rule 0 x 55 [4,11,2,7,1,13,9,15]
+ CRUSH rule 0 x 56 [5,9,10,1,3,13,14,7]
+ CRUSH rule 0 x 57 [6,2,1,15,10,12,5]
+ CRUSH rule 0 x 58 [7,1,11,4,3,14,12,9]
+ CRUSH rule 0 x 59 [2,13,1,10,9,5,14,6]
+ CRUSH rule 0 x 60 [3,6,11,1,4,9,12,15]
+ CRUSH rule 0 x 61 [3,15,13,7,4,1,10,9]
+ CRUSH rule 0 x 62 [15,11,7,12,5,9,2,1]
+ CRUSH rule 0 x 63 [10,14,12,1,7,3]
+ CRUSH rule 0 x 64 [3,9,1,4,7,12,11,15]
+ CRUSH rule 0 x 65 [4,12,11,7,14,3,1]
+ CRUSH rule 0 x 66 [15,11,6,9,4,1,3,12]
+ CRUSH rule 0 x 67 [2,6,4,14,1,11,12]
+ CRUSH rule 0 x 68 [15,7,4,2,9,12,11]
+ CRUSH rule 0 x 69 [2,1,15,10,4,9,13,7]
+ CRUSH rule 0 x 70 [9,6,1,3,13,15,11,5]
+ CRUSH rule 0 x 71 [15,5,1,3,13,10,7]
+ CRUSH rule 0 x 72 [9,10,3,5,7,12,15,1]
+ CRUSH rule 0 x 73 [5,3,11,1,7,12,15,9]
+ CRUSH rule 0 x 74 [11,7,9,5,1,15,3,12]
+ CRUSH rule 0 x 75 [9,7,11,14,12,1,2,5]
+ CRUSH rule 0 x 76 [6,1,3,5,14,10,12,9]
+ CRUSH rule 0 x 77 [7,4,2,13,9,1,11,15]
+ CRUSH rule 0 x 78 [9,3,1,5,6,13,14,11]
+ CRUSH rule 0 x 79 [13,2,15,5,7,9,11,1]
+ CRUSH rule 0 x 80 [15,2,6,4,13,10,1]
+ CRUSH rule 0 x 81 [15,2,1,11,4,6,13]
+ CRUSH rule 0 x 82 [14,13,5,11,6,2,1,9]
+ CRUSH rule 0 x 83 [4,15,3,9,10,13,6,1]
+ CRUSH rule 0 x 84 [10,7,9,15,3,4,1,13]
+ CRUSH rule 0 x 85 [3,15,9,7,4,11,1,12]
+ CRUSH rule 0 x 86 [10,9,14,1,13,4,2,7]
+ CRUSH rule 0 x 87 [15,10,7,12,5,3,9,1]
+ CRUSH rule 0 x 88 [4,13,3,1,9,15,11,7]
+ CRUSH rule 0 x 89 [3,9,7,4,1,14,10,12]
+ CRUSH rule 0 x 90 [4,9,7,12,11,14,2,1]
+ CRUSH rule 0 x 91 [6,11,9,1,2,4,14,13]
+ CRUSH rule 0 x 92 [1,5,10,9,13,15,6,2]
+ CRUSH rule 0 x 93 [9,3,15,13,7,5,1,10]
+ CRUSH rule 0 x 94 [9,2,12,5,6,11,1,14]
+ CRUSH rule 0 x 95 [7,15,4,10,9,13,2,1]
+ CRUSH rule 0 x 96 [2,15,11,7,5,1,12]
+ CRUSH rule 0 x 97 [4,11,2,13,1,7,9,14]
+ CRUSH rule 0 x 98 [11,13,9,3,15,1,5,6]
+ CRUSH rule 0 x 99 [12,4,11,7,3,14,9,1]
+ CRUSH rule 0 x 100 [9,4,10,15,7,3,13]
+ CRUSH rule 0 x 101 [15,7,1,9,10,5,2,12]
+ CRUSH rule 0 x 102 [3,11,14,6,13,4,9,1]
+ CRUSH rule 0 x 103 [13,11,6,14,4,3,1]
+ CRUSH rule 0 x 104 [14,6,3,5,9,1,10,13]
+ CRUSH rule 0 x 105 [14,10,1,9,3,5,6,13]
+ CRUSH rule 0 x 106 [6,5,13,2,14,11,1,9]
+ CRUSH rule 0 x 107 [3,1,10,14,13,5,9,6]
+ CRUSH rule 0 x 108 [5,10,7,2,15,9,12,1]
+ CRUSH rule 0 x 109 [9,1,13,7,15,5,11,3]
+ CRUSH rule 0 x 110 [5,1,11,3,7,14,13,9]
+ CRUSH rule 0 x 111 [10,1,9,7,5,2,13,14]
+ CRUSH rule 0 x 112 [1,10,4,14,2,12,6,9]
+ CRUSH rule 0 x 113 [6,10,13,9,1,5,2,14]
+ CRUSH rule 0 x 114 [5,13,6,2,1,14,11]
+ CRUSH rule 0 x 115 [10,13,14,3,9,1,6,5]
+ CRUSH rule 0 x 116 [1,14,13,2,11,5,7]
+ CRUSH rule 0 x 117 [5,6,1,12,15,9,11,3]
+ CRUSH rule 0 x 118 [10,4,13,15,9,3,1,6]
+ CRUSH rule 0 x 119 [14,12,11,4,6,9,3,1]
+ CRUSH rule 0 x 120 [11,3,14,13,4,7]
+ CRUSH rule 0 x 121 [9,5,1,11,7,3,15,12]
+ CRUSH rule 0 x 122 [4,3,14,1,11,13,7]
+ CRUSH rule 0 x 123 [3,10,5,6,9,1,12,15]
+ CRUSH rule 0 x 124 [12,2,1,5,14,7,11,9]
+ CRUSH rule 0 x 125 [9,12,15,1,6,5,3,10]
+ CRUSH rule 0 x 126 [7,15,10,9,2,12,5,1]
+ CRUSH rule 0 x 127 [4,14,9,13,1,3,7,10]
+ CRUSH rule 0 x 128 [3,12,1,10,4,9,7,14]
+ CRUSH rule 0 x 129 [11,13,14,2,9,4,6,1]
+ CRUSH rule 0 x 130 [3,13,5,14,10,1,9,6]
+ CRUSH rule 0 x 131 [12,1,6,15,4,2,11,9]
+ CRUSH rule 0 x 132 [11,15,13,9,2,5,7]
+ CRUSH rule 0 x 133 [3,6,9,11,15,12,5]
+ CRUSH rule 0 x 134 [12,5,6,15,3,9,10,1]
+ CRUSH rule 0 x 135 [3,14,12,4,6,11,9]
+ CRUSH rule 0 x 136 [15,6,9,4,10,3,12]
+ CRUSH rule 0 x 137 [14,3,6,11,1,9,4,13]
+ CRUSH rule 0 x 138 [13,15,4,10,2,7,1]
+ CRUSH rule 0 x 139 [11,2,13,9,1,15,7,5]
+ CRUSH rule 0 x 140 [11,4,12,15,2,6,9,1]
+ CRUSH rule 0 x 141 [6,12,15,11,3,5,1]
+ CRUSH rule 0 x 142 [3,14,7,9,11,1,4,13]
+ CRUSH rule 0 x 143 [9,6,4,2,14,10,12]
+ CRUSH rule 0 x 144 [13,7,11,2,14,4,1,9]
+ CRUSH rule 0 x 145 [12,2,6,10,9,4,14,1]
+ CRUSH rule 0 x 146 [1,5,9,2,6,13,14,10]
+ CRUSH rule 0 x 147 [1,4,9,11,2,7,15,12]
+ CRUSH rule 0 x 148 [12,7,9,2,14,11,1,4]
+ CRUSH rule 0 x 149 [2,5,9,12,11,1,7,14]
+ CRUSH rule 0 x 150 [1,15,2,10,7,9,5,12]
+ CRUSH rule 0 x 151 [2,9,14,7,1,10,5,13]
+ CRUSH rule 0 x 152 [5,9,2,6,10,13,14]
+ CRUSH rule 0 x 153 [6,9,4,15,2,1,10,12]
+ CRUSH rule 0 x 154 [3,11,7,1,4,12,15,9]
+ CRUSH rule 0 x 155 [14,12,7,3,5,1,9,11]
+ CRUSH rule 0 x 156 [7,13,3,10,15,5,1]
+ CRUSH rule 0 x 157 [15,1,6,4,3,10,9,13]
+ CRUSH rule 0 x 158 [15,1,10,6,12,2,4]
+ CRUSH rule 0 x 159 [4,14,3,12,10,6,1,9]
+ CRUSH rule 0 x 160 [5,7,3,14,11,1,12]
+ CRUSH rule 0 x 161 [1,2,11,4,6,13,14]
+ CRUSH rule 0 x 162 [10,6,1,12,2,4,14,9]
+ CRUSH rule 0 x 163 [15,1,10,2,6,4,13,9]
+ CRUSH rule 0 x 164 [9,14,10,7,12,2,5,1]
+ CRUSH rule 0 x 165 [11,7,2,13,9,15,1,5]
+ CRUSH rule 0 x 166 [1,2,12,14,4,11,7,9]
+ CRUSH rule 0 x 167 [9,7,3,4,11,13,15,1]
+ CRUSH rule 0 x 168 [13,2,4,1,6,15,10,9]
+ CRUSH rule 0 x 169 [1,4,9,14,13,10,2,7]
+ CRUSH rule 0 x 170 [1,15,7,9,12,10,3,5]
+ CRUSH rule 0 x 171 [9,2,10,7,1,5,15,12]
+ CRUSH rule 0 x 172 [14,4,10,12,9,3,6]
+ CRUSH rule 0 x 173 [5,10,12,15,6,1,2,9]
+ CRUSH rule 0 x 174 [15,6,4,12,1,11,9,2]
+ CRUSH rule 0 x 175 [5,7,9,3,10,1,14,13]
+ CRUSH rule 0 x 176 [9,6,3,14,13,10,4]
+ CRUSH rule 0 x 177 [2,9,10,13,4,1,14,7]
+ CRUSH rule 0 x 178 [12,11,7,14,3,4]
+ CRUSH rule 0 x 179 [2,10,13,9,5,1,7,14]
+ CRUSH rule 0 x 180 [3,11,5,15,7,12]
+ CRUSH rule 0 x 181 [9,12,6,5,1,10,14,2]
+ CRUSH rule 0 x 182 [5,13,11,2,1,6,14]
+ CRUSH rule 0 x 183 [5,7,10,13,3,9,14,1]
+ CRUSH rule 0 x 184 [2,5,11,12,7,1,9,15]
+ CRUSH rule 0 x 185 [13,5,7,11,2,14]
+ CRUSH rule 0 x 186 [6,14,13,5,10,1,3,9]
+ CRUSH rule 0 x 187 [1,4,11,13,6,14,9,3]
+ CRUSH rule 0 x 188 [9,13,5,14,10,6,2]
+ CRUSH rule 0 x 189 [6,12,4,9,2,1,11,15]
+ CRUSH rule 0 x 190 [9,13,15,10,3,1,5,6]
+ CRUSH rule 0 x 191 [7,11,4,1,15,12,9,2]
+ CRUSH rule 0 x 192 [2,11,5,15,6,1,13]
+ CRUSH rule 0 x 193 [3,13,6,10,4,1,9,14]
+ CRUSH rule 0 x 194 [3,13,4,14,6,9,1,11]
+ CRUSH rule 0 x 195 [5,7,10,12,1,3,15,9]
+ CRUSH rule 0 x 196 [4,15,1,10,9,2,13,7]
+ CRUSH rule 0 x 197 [14,10,13,4,6,3,1,9]
+ CRUSH rule 0 x 198 [2,5,6,15,9,13,10]
+ CRUSH rule 0 x 199 [2,10,4,15,1,9,6,12]
+ CRUSH rule 0 x 200 [7,14,11,4,1,3,13]
+ CRUSH rule 0 x 201 [9,14,1,7,4,3,10,13]
+ CRUSH rule 0 x 202 [14,11,7,3,5,1,12]
+ CRUSH rule 0 x 203 [12,5,7,15,1,2,10,9]
+ CRUSH rule 0 x 204 [6,11,3,12,14,1,9,4]
+ CRUSH rule 0 x 205 [15,4,6,10,13,9,2,1]
+ CRUSH rule 0 x 206 [13,11,2,15,7,1,5]
+ CRUSH rule 0 x 207 [2,11,7,4,14,1,12,9]
+ CRUSH rule 0 x 208 [13,1,6,14,9,11,3,5]
+ CRUSH rule 0 x 209 [6,15,13,1,11,4,9,2]
+ CRUSH rule 0 x 210 [13,11,2,7,5,14,9]
+ CRUSH rule 0 x 211 [2,14,1,13,11,7,9,5]
+ CRUSH rule 0 x 212 [10,1,12,15,5,6,2,9]
+ CRUSH rule 0 x 213 [3,9,6,5,15,13,1,11]
+ CRUSH rule 0 x 214 [7,15,4,1,10,2,13,9]
+ CRUSH rule 0 x 215 [6,1,4,13,3,11,14]
+ CRUSH rule 0 x 216 [12,9,6,2,1,11,5,15]
+ CRUSH rule 0 x 217 [12,11,1,14,2,4,7]
+ CRUSH rule 0 x 218 [12,10,15,6,1,4,9,2]
+ CRUSH rule 0 x 219 [3,11,14,6,4,1,13]
+ CRUSH rule 0 x 220 [14,4,3,12,10,9,6]
+ CRUSH rule 0 x 221 [15,5,2,6,12,11,9]
+ CRUSH rule 0 x 222 [10,4,3,15,7,12,1]
+ CRUSH rule 0 x 223 [9,7,11,1,4,14,13,3]
+ CRUSH rule 0 x 224 [1,7,10,2,12,9,14,5]
+ CRUSH rule 0 x 225 [10,5,2,6,1,13,9,15]
+ CRUSH rule 0 x 226 [4,1,9,3,13,10,15,7]
+ CRUSH rule 0 x 227 [7,2,12,15,5,11]
+ CRUSH rule 0 x 228 [2,15,11,1,6,13,9,4]
+ CRUSH rule 0 x 229 [9,3,7,14,1,12,4,10]
+ CRUSH rule 0 x 230 [10,5,7,2,15,1,13]
+ CRUSH rule 0 x 231 [2,7,5,13,9,15,10]
+ CRUSH rule 0 x 232 [10,5,13,1,9,2,7,14]
+ CRUSH rule 0 x 233 [6,12,11,4,9,14,1,3]
+ CRUSH rule 0 x 234 [10,1,2,12,5,9,15,7]
+ CRUSH rule 0 x 235 [13,14,7,10,1,9,5,3]
+ CRUSH rule 0 x 236 [2,15,9,12,1,7,4,10]
+ CRUSH rule 0 x 237 [3,12,9,10,4,7,15]
+ CRUSH rule 0 x 238 [2,10,4,15,6,12,9,1]
+ CRUSH rule 0 x 239 [4,15,10,7,9,13,3,1]
+ CRUSH rule 0 x 240 [15,5,13,7,2,9,10]
+ CRUSH rule 0 x 241 [7,9,15,12,1,5,2,10]
+ CRUSH rule 0 x 242 [14,2,6,9,10,12,5]
+ CRUSH rule 0 x 243 [2,11,5,1,15,6,9,13]
+ CRUSH rule 0 x 244 [13,9,15,3,11,7,5]
+ CRUSH rule 0 x 245 [12,9,15,3,1,5,10,7]
+ CRUSH rule 0 x 246 [15,3,5,11,7,1,12,9]
+ CRUSH rule 0 x 247 [6,4,9,12,1,2,10,14]
+ CRUSH rule 0 x 248 [5,13,7,11,9,15,3,1]
+ CRUSH rule 0 x 249 [10,14,7,3,9,13,1,4]
+ CRUSH rule 0 x 250 [12,15,1,10,5,6,3,9]
+ CRUSH rule 0 x 251 [13,2,15,5,6,1,9,10]
+ CRUSH rule 0 x 252 [7,5,13,9,3,10,14,1]
+ CRUSH rule 0 x 253 [3,13,15,10,7,4]
+ CRUSH rule 0 x 254 [2,9,13,14,4,6,10]
+ CRUSH rule 0 x 255 [1,9,13,2,6,10,4,15]
+ CRUSH rule 0 x 256 [6,9,13,1,3,14,5,10]
+ CRUSH rule 0 x 257 [15,12,3,9,6,4,11]
+ CRUSH rule 0 x 258 [12,5,6,10,2,1,14,9]
+ CRUSH rule 0 x 259 [9,10,4,3,14,13,1,7]
+ CRUSH rule 0 x 260 [10,12,6,9,3,15,1,4]
+ CRUSH rule 0 x 261 [13,7,2,1,15,5,11,9]
+ CRUSH rule 0 x 262 [15,3,12,7,4,9,1,11]
+ CRUSH rule 0 x 263 [12,6,10,9,5,15,3,1]
+ CRUSH rule 0 x 264 [13,14,11,3,1,4,7,9]
+ CRUSH rule 0 x 265 [12,10,14,5,7,1,9,3]
+ CRUSH rule 0 x 266 [14,7,11,1,2,9,4,12]
+ CRUSH rule 0 x 267 [12,11,6,5,1,2,15]
+ CRUSH rule 0 x 268 [4,1,15,12,6,11,3,9]
+ CRUSH rule 0 x 269 [11,1,15,5,13,9,7,2]
+ CRUSH rule 0 x 270 [7,11,12,3,1,14,9,4]
+ CRUSH rule 0 x 271 [4,7,3,13,15,10,9,1]
+ CRUSH rule 0 x 272 [15,5,13,10,6,2]
+ CRUSH rule 0 x 273 [2,10,7,12,1,15,5]
+ CRUSH rule 0 x 274 [10,2,5,6,13,9,15,1]
+ CRUSH rule 0 x 275 [10,3,4,7,14,13]
+ CRUSH rule 0 x 276 [5,12,9,2,11,7,15,1]
+ CRUSH rule 0 x 277 [14,3,13,4,1,9,11,7]
+ CRUSH rule 0 x 278 [5,6,14,3,1,11,13,9]
+ CRUSH rule 0 x 279 [6,10,13,3,9,4,15]
+ CRUSH rule 0 x 280 [7,3,14,9,1,11,4,13]
+ CRUSH rule 0 x 281 [5,11,14,7,9,13,2,1]
+ CRUSH rule 0 x 282 [2,1,13,14,9,7,5,10]
+ CRUSH rule 0 x 283 [4,1,12,3,10,7,15]
+ CRUSH rule 0 x 284 [5,11,7,15,3,13,1,9]
+ CRUSH rule 0 x 285 [15,5,3,1,6,13,11,9]
+ CRUSH rule 0 x 286 [10,4,3,6,12,15,1]
+ CRUSH rule 0 x 287 [12,4,9,1,3,11,15,7]
+ CRUSH rule 0 x 288 [4,12,10,7,1,3,14,9]
+ CRUSH rule 0 x 289 [2,5,14,9,13,6,10]
+ CRUSH rule 0 x 290 [12,2,5,6,15,9,1,10]
+ CRUSH rule 0 x 291 [7,11,1,14,5,9,2,12]
+ CRUSH rule 0 x 292 [4,10,6,3,14,9,12,1]
+ CRUSH rule 0 x 293 [6,5,11,1,2,14,12]
+ CRUSH rule 0 x 294 [9,12,3,14,6,11,5,1]
+ CRUSH rule 0 x 295 [6,10,3,14,9,4,13]
+ CRUSH rule 0 x 296 [3,1,13,7,14,9,10,4]
+ CRUSH rule 0 x 297 [6,13,4,14,10,1,2,9]
+ CRUSH rule 0 x 298 [14,9,13,1,4,2,7,10]
+ CRUSH rule 0 x 299 [14,12,11,6,4,2,1,9]
+ CRUSH rule 0 x 300 [15,7,10,5,1,3,13,9]
+ CRUSH rule 0 x 301 [9,11,7,1,13,14,4,2]
+ CRUSH rule 0 x 302 [9,7,1,13,5,10,3,15]
+ CRUSH rule 0 x 303 [4,13,3,7,10,15,1]
+ CRUSH rule 0 x 304 [6,9,2,11,15,13,4]
+ CRUSH rule 0 x 305 [13,7,5,11,2,15,9]
+ CRUSH rule 0 x 306 [10,12,4,6,9,2,15,1]
+ CRUSH rule 0 x 307 [11,12,15,5,6,2,1,9]
+ CRUSH rule 0 x 308 [12,14,10,9,1,2,5,7]
+ CRUSH rule 0 x 309 [9,3,12,5,11,15,7]
+ CRUSH rule 0 x 310 [3,1,5,10,14,9,7,12]
+ CRUSH rule 0 x 311 [3,9,7,1,14,13,10,5]
+ CRUSH rule 0 x 312 [15,13,9,7,5,10,2]
+ CRUSH rule 0 x 313 [9,15,3,7,5,13,1,11]
+ CRUSH rule 0 x 314 [2,15,9,5,6,12,1,11]
+ CRUSH rule 0 x 315 [15,2,13,1,11,9,6,4]
+ CRUSH rule 0 x 316 [4,9,11,2,12,14,6]
+ CRUSH rule 0 x 317 [1,5,3,13,15,7,10]
+ CRUSH rule 0 x 318 [4,1,15,11,9,13,6,2]
+ CRUSH rule 0 x 319 [2,15,4,1,11,9,7,12]
+ CRUSH rule 0 x 320 [5,7,13,9,11,2,1,15]
+ CRUSH rule 0 x 321 [1,6,11,15,5,3,13]
+ CRUSH rule 0 x 322 [13,7,5,3,14,11,1]
+ CRUSH rule 0 x 323 [7,4,10,1,2,13,14]
+ CRUSH rule 0 x 324 [5,6,10,15,2,13]
+ CRUSH rule 0 x 325 [9,10,14,5,1,6,2,13]
+ CRUSH rule 0 x 326 [11,7,13,4,2,15,1]
+ CRUSH rule 0 x 327 [12,5,10,14,3,7,9]
+ CRUSH rule 0 x 328 [5,2,6,14,1,11,12]
+ CRUSH rule 0 x 329 [2,6,15,5,9,10,13,1]
+ CRUSH rule 0 x 330 [3,9,11,13,1,6,5,14]
+ CRUSH rule 0 x 331 [12,14,6,3,1,4,10,9]
+ CRUSH rule 0 x 332 [10,12,6,15,9,2,5]
+ CRUSH rule 0 x 333 [6,5,3,12,14,10,9,1]
+ CRUSH rule 0 x 334 [4,9,2,12,7,11,15,1]
+ CRUSH rule 0 x 335 [11,7,1,5,13,2,9,15]
+ CRUSH rule 0 x 336 [6,14,13,2,5,9,11]
+ CRUSH rule 0 x 337 [15,11,3,7,12,5]
+ CRUSH rule 0 x 338 [10,5,3,6,15,1,9,13]
+ CRUSH rule 0 x 339 [11,14,13,5,3,7,1]
+ CRUSH rule 0 x 340 [11,6,12,4,9,3,14,1]
+ CRUSH rule 0 x 341 [7,5,2,10,14,9,1,12]
+ CRUSH rule 0 x 342 [12,14,1,9,2,11,4,7]
+ CRUSH rule 0 x 343 [12,14,9,6,10,2,4,1]
+ CRUSH rule 0 x 344 [9,11,5,2,14,13,1,7]
+ CRUSH rule 0 x 345 [14,2,11,9,6,12,4]
+ CRUSH rule 0 x 346 [5,3,14,10,7,1,13]
+ CRUSH rule 0 x 347 [10,2,12,6,9,1,14,5]
+ CRUSH rule 0 x 348 [7,9,10,1,14,13,3,4]
+ CRUSH rule 0 x 349 [9,6,10,12,1,5,14]
+ CRUSH rule 0 x 350 [13,9,15,4,10,7,2,1]
+ CRUSH rule 0 x 351 [13,5,15,3,1,6,11]
+ CRUSH rule 0 x 352 [1,12,11,9,4,7,3,15]
+ CRUSH rule 0 x 353 [10,14,12,2,9,1,4,6]
+ CRUSH rule 0 x 354 [6,3,15,10,9,4,13]
+ CRUSH rule 0 x 355 [13,14,6,10,2,5,1,9]
+ CRUSH rule 0 x 356 [15,13,2,9,6,5,1,11]
+ CRUSH rule 0 x 357 [4,11,1,13,3,14,6,9]
+ CRUSH rule 0 x 358 [12,7,2,9,1,14,10,4]
+ CRUSH rule 0 x 359 [5,15,7,11,3,13]
+ CRUSH rule 0 x 360 [13,10,1,2,6,14,5]
+ CRUSH rule 0 x 361 [5,3,13,6,1,14,11,9]
+ CRUSH rule 0 x 362 [2,9,11,13,1,6,5,15]
+ CRUSH rule 0 x 363 [7,12,3,9,15,4,1,10]
+ CRUSH rule 0 x 364 [2,12,6,9,5,10,15]
+ CRUSH rule 0 x 365 [13,5,11,15,6,2,9]
+ CRUSH rule 0 x 366 [12,7,3,14,5,10,9]
+ CRUSH rule 0 x 367 [7,13,3,1,5,11,15,9]
+ CRUSH rule 0 x 368 [7,9,10,15,3,4,13]
+ CRUSH rule 0 x 369 [7,5,3,13,14,9,11,1]
+ CRUSH rule 0 x 370 [4,7,14,1,2,9,11,12]
+ CRUSH rule 0 x 371 [1,7,12,3,4,15,10,9]
+ CRUSH rule 0 x 372 [10,4,3,14,6,1,12,9]
+ CRUSH rule 0 x 373 [15,5,2,6,13,1,9,10]
+ CRUSH rule 0 x 374 [3,15,12,5,1,6,10,9]
+ CRUSH rule 0 x 375 [5,2,14,1,6,13,11,9]
+ CRUSH rule 0 x 376 [5,14,10,13,3,6,1]
+ CRUSH rule 0 x 377 [1,15,2,4,9,11,12,6]
+ CRUSH rule 0 x 378 [9,12,2,15,1,5,11,6]
+ CRUSH rule 0 x 379 [11,2,15,5,7,9,13,1]
+ CRUSH rule 0 x 380 [6,1,12,11,2,9,5,14]
+ CRUSH rule 0 x 381 [15,13,7,5,10,2,1,9]
+ CRUSH rule 0 x 382 [14,3,1,4,13,7,10]
+ CRUSH rule 0 x 383 [3,6,11,4,13,15,1]
+ CRUSH rule 0 x 384 [4,13,6,3,15,11,9]
+ CRUSH rule 0 x 385 [4,6,15,3,10,9,1,13]
+ CRUSH rule 0 x 386 [14,3,11,13,5,6,9,1]
+ CRUSH rule 0 x 387 [1,11,5,7,9,2,14,12]
+ CRUSH rule 0 x 388 [2,6,11,9,15,4,12]
+ CRUSH rule 0 x 389 [12,7,2,4,15,10,1]
+ CRUSH rule 0 x 390 [2,11,13,7,5,9,15]
+ CRUSH rule 0 x 391 [3,4,9,13,7,10,1,14]
+ CRUSH rule 0 x 392 [11,5,14,7,1,9,2,12]
+ CRUSH rule 0 x 393 [2,14,5,9,7,13,11]
+ CRUSH rule 0 x 394 [4,9,3,15,13,6,1,11]
+ CRUSH rule 0 x 395 [10,13,5,15,6,9,3,1]
+ CRUSH rule 0 x 396 [2,12,15,9,4,6,11]
+ CRUSH rule 0 x 397 [1,14,9,4,12,10,3,7]
+ CRUSH rule 0 x 398 [9,2,1,5,12,6,11,15]
+ CRUSH rule 0 x 399 [5,9,14,3,1,10,13,7]
+ CRUSH rule 0 x 400 [10,6,2,4,15,12,1,9]
+ CRUSH rule 0 x 401 [6,9,11,12,4,3,15,1]
+ CRUSH rule 0 x 402 [4,7,9,2,13,1,15,11]
+ CRUSH rule 0 x 403 [7,15,13,3,5,9,10]
+ CRUSH rule 0 x 404 [14,12,7,9,2,1,5,11]
+ CRUSH rule 0 x 405 [9,15,11,2,4,7,13,1]
+ CRUSH rule 0 x 406 [12,14,9,2,7,10,4,1]
+ CRUSH rule 0 x 407 [9,5,12,10,15,6,3]
+ CRUSH rule 0 x 408 [7,1,5,2,10,15,13,9]
+ CRUSH rule 0 x 409 [11,2,4,13,1,15,7,9]
+ CRUSH rule 0 x 410 [6,4,14,2,12,9,10,1]
+ CRUSH rule 0 x 411 [13,11,15,6,4,1,9,2]
+ CRUSH rule 0 x 412 [5,9,6,11,14,2,12]
+ CRUSH rule 0 x 413 [13,5,3,11,6,9,1,14]
+ CRUSH rule 0 x 414 [3,11,9,13,4,1,6,15]
+ CRUSH rule 0 x 415 [6,10,14,5,1,13,3,9]
+ CRUSH rule 0 x 416 [13,1,4,7,2,9,14,11]
+ CRUSH rule 0 x 417 [4,12,1,15,2,11,9,6]
+ CRUSH rule 0 x 418 [14,5,10,2,6,9,13]
+ CRUSH rule 0 x 419 [5,14,10,9,2,12,6,1]
+ CRUSH rule 0 x 420 [2,4,9,11,6,14,13,1]
+ CRUSH rule 0 x 421 [15,4,10,3,9,12,7]
+ CRUSH rule 0 x 422 [4,11,2,7,13,9,15]
+ CRUSH rule 0 x 423 [3,15,12,6,5,1,9,10]
+ CRUSH rule 0 x 424 [6,10,12,2,5,1,14,9]
+ CRUSH rule 0 x 425 [11,15,2,13,5,7,9,1]
+ CRUSH rule 0 x 426 [12,4,7,1,9,10,14,2]
+ CRUSH rule 0 x 427 [14,10,3,1,9,7,5,13]
+ CRUSH rule 0 x 428 [12,7,9,4,2,1,14,10]
+ CRUSH rule 0 x 429 [3,4,9,7,11,12,1,14]
+ CRUSH rule 0 x 430 [3,5,10,13,1,15,6]
+ CRUSH rule 0 x 431 [9,3,7,1,12,5,14,11]
+ CRUSH rule 0 x 432 [4,1,12,7,15,2,10,9]
+ CRUSH rule 0 x 433 [4,11,12,15,7,3]
+ CRUSH rule 0 x 434 [2,14,9,1,5,11,7,13]
+ CRUSH rule 0 x 435 [13,11,5,6,9,2,15]
+ CRUSH rule 0 x 436 [9,15,10,2,4,1,12,7]
+ CRUSH rule 0 x 437 [9,6,3,14,10,12,5,1]
+ CRUSH rule 0 x 438 [7,2,13,4,11,1,9,14]
+ CRUSH rule 0 x 439 [7,14,4,3,12,10]
+ CRUSH rule 0 x 440 [14,11,9,2,7,12,1,5]
+ CRUSH rule 0 x 441 [2,4,11,9,13,6,1,14]
+ CRUSH rule 0 x 442 [10,13,9,7,15,1,4,2]
+ CRUSH rule 0 x 443 [12,15,10,9,2,1,6,4]
+ CRUSH rule 0 x 444 [4,13,7,14,3,1,9,11]
+ CRUSH rule 0 x 445 [4,2,15,7,1,9,11,12]
+ CRUSH rule 0 x 446 [12,10,6,9,4,1,15,3]
+ CRUSH rule 0 x 447 [15,7,13,1,4,9,3,11]
+ CRUSH rule 0 x 448 [5,2,13,7,15,10]
+ CRUSH rule 0 x 449 [14,5,3,12,10,9,6]
+ CRUSH rule 0 x 450 [2,4,6,9,15,1,13,10]
+ CRUSH rule 0 x 451 [6,14,11,3,9,1,12,5]
+ CRUSH rule 0 x 452 [14,9,10,4,2,13,7]
+ CRUSH rule 0 x 453 [5,15,13,2,6,9,11]
+ CRUSH rule 0 x 454 [10,4,2,6,15,12,9,1]
+ CRUSH rule 0 x 455 [6,13,2,4,10,1,15]
+ CRUSH rule 0 x 456 [5,7,13,1,11,3,9,15]
+ CRUSH rule 0 x 457 [9,1,5,7,11,13,15]
+ CRUSH rule 0 x 458 [9,11,15,4,7,2,12,1]
+ CRUSH rule 0 x 459 [13,15,11,1,5,2,6]
+ CRUSH rule 0 x 460 [5,12,10,15,7,3,9]
+ CRUSH rule 0 x 461 [4,3,9,13,15,6,10]
+ CRUSH rule 0 x 462 [4,7,12,14,11,1,3,9]
+ CRUSH rule 0 x 463 [4,12,14,11,2,7,1,9]
+ CRUSH rule 0 x 464 [4,2,15,10,1,9,13,7]
+ CRUSH rule 0 x 465 [5,10,9,7,13,1,3,14]
+ CRUSH rule 0 x 466 [13,5,2,15,9,11,6,1]
+ CRUSH rule 0 x 467 [13,6,14,3,9,1,11,5]
+ CRUSH rule 0 x 468 [10,7,12,14,4,1,9,2]
+ CRUSH rule 0 x 469 [4,9,6,14,12,11,3,1]
+ CRUSH rule 0 x 470 [3,9,12,15,5,6,10]
+ CRUSH rule 0 x 471 [6,1,5,14,13,10,9,3]
+ CRUSH rule 0 x 472 [2,14,7,5,13,1,11,9]
+ CRUSH rule 0 x 473 [15,10,6,9,4,12,2]
+ CRUSH rule 0 x 474 [15,10,4,12,6,9,2,1]
+ CRUSH rule 0 x 475 [10,5,12,9,14,3,6,1]
+ CRUSH rule 0 x 476 [3,6,10,12,1,15,9,4]
+ CRUSH rule 0 x 477 [6,13,5,15,11,9,2,1]
+ CRUSH rule 0 x 478 [4,15,1,3,7,12,10,9]
+ CRUSH rule 0 x 479 [13,11,1,6,14,5,9,3]
+ CRUSH rule 0 x 480 [1,13,6,4,9,14,11,3]
+ CRUSH rule 0 x 481 [15,12,7,9,1,3,10,4]
+ CRUSH rule 0 x 482 [2,12,9,1,7,11,14,4]
+ CRUSH rule 0 x 483 [10,1,4,15,9,7,13,2]
+ CRUSH rule 0 x 484 [1,4,10,13,7,14,2,9]
+ CRUSH rule 0 x 485 [9,4,3,1,14,12,7,10]
+ CRUSH rule 0 x 486 [3,10,15,9,7,13,4,1]
+ CRUSH rule 0 x 487 [12,11,4,14,7,2,1]
+ CRUSH rule 0 x 488 [14,4,1,9,2,6,10,12]
+ CRUSH rule 0 x 489 [11,4,2,13,15,7]
+ CRUSH rule 0 x 490 [4,9,1,3,13,15,6,11]
+ CRUSH rule 0 x 491 [1,12,5,2,14,11,6]
+ CRUSH rule 0 x 492 [5,7,11,3,14,9,1,13]
+ CRUSH rule 0 x 493 [12,1,4,15,3,11,9,6]
+ CRUSH rule 0 x 494 [1,7,13,4,15,9,10,3]
+ CRUSH rule 0 x 495 [3,15,7,1,9,5,12,11]
+ CRUSH rule 0 x 496 [5,3,7,13,9,14,10]
+ CRUSH rule 0 x 497 [13,10,3,6,5,14,1]
+ CRUSH rule 0 x 498 [10,6,1,5,9,12,3,15]
+ CRUSH rule 0 x 499 [14,3,12,5,1,11,9,7]
+ CRUSH rule 0 x 500 [15,9,6,12,11,2,1,5]
+ CRUSH rule 0 x 501 [10,13,1,9,3,14,5,7]
+ CRUSH rule 0 x 502 [5,1,14,11,7,12,9,2]
+ CRUSH rule 0 x 503 [15,10,7,9,1,12,4,2]
+ CRUSH rule 0 x 504 [13,2,7,1,14,11,5]
+ CRUSH rule 0 x 505 [12,7,5,2,14,10,9]
+ CRUSH rule 0 x 506 [11,7,9,14,12,1,2,5]
+ CRUSH rule 0 x 507 [4,14,13,3,9,7,1,10]
+ CRUSH rule 0 x 508 [12,1,4,9,2,11,15,7]
+ CRUSH rule 0 x 509 [4,2,6,9,14,1,10,13]
+ CRUSH rule 0 x 510 [5,3,1,12,11,14,9,7]
+ CRUSH rule 0 x 511 [2,12,10,6,14,5]
+ CRUSH rule 0 x 512 [15,11,3,5,7,1,13]
+ CRUSH rule 0 x 513 [4,9,11,3,13,7,1,14]
+ CRUSH rule 0 x 514 [11,9,3,4,12,15,6,1]
+ CRUSH rule 0 x 515 [12,14,6,5,3,9,1,10]
+ CRUSH rule 0 x 516 [14,11,1,12,3,7,4,9]
+ CRUSH rule 0 x 517 [11,5,6,13,9,3,14]
+ CRUSH rule 0 x 518 [3,5,7,12,15,11,9,1]
+ CRUSH rule 0 x 519 [12,14,2,1,4,6,9,10]
+ CRUSH rule 0 x 520 [12,4,2,10,6,15,9]
+ CRUSH rule 0 x 521 [11,5,9,6,15,3,13]
+ CRUSH rule 0 x 522 [4,12,11,1,15,3,9,6]
+ CRUSH rule 0 x 523 [3,1,5,9,15,10,13,7]
+ CRUSH rule 0 x 524 [15,9,3,11,13,7,4,1]
+ CRUSH rule 0 x 525 [3,15,11,6,9,12,4]
+ CRUSH rule 0 x 526 [10,2,5,13,6,15,1,9]
+ CRUSH rule 0 x 527 [3,13,4,1,9,10,14,7]
+ CRUSH rule 0 x 528 [12,7,15,10,2,5,9]
+ CRUSH rule 0 x 529 [6,4,10,12,2,9,14]
+ CRUSH rule 0 x 530 [11,9,12,7,5,1,3,15]
+ CRUSH rule 0 x 531 [9,15,4,7,2,13,1,11]
+ CRUSH rule 0 x 532 [5,3,13,7,9,14,1,10]
+ CRUSH rule 0 x 533 [12,15,1,2,7,5,10]
+ CRUSH rule 0 x 534 [11,9,3,7,15,4,1,12]
+ CRUSH rule 0 x 535 [11,1,3,5,14,9,12,7]
+ CRUSH rule 0 x 536 [9,1,14,13,4,6,2,11]
+ CRUSH rule 0 x 537 [15,5,13,2,7,11]
+ CRUSH rule 0 x 538 [13,5,11,2,6,15,9]
+ CRUSH rule 0 x 539 [10,12,6,14,1,2,9,5]
+ CRUSH rule 0 x 540 [12,15,7,3,9,11,1,4]
+ CRUSH rule 0 x 541 [2,1,6,11,14,13,4]
+ CRUSH rule 0 x 542 [3,9,15,5,11,12,7,1]
+ CRUSH rule 0 x 543 [4,10,9,3,6,13,14]
+ CRUSH rule 0 x 544 [3,15,9,11,7,4,12,1]
+ CRUSH rule 0 x 545 [14,10,7,12,4,9,1,3]
+ CRUSH rule 0 x 546 [5,15,13,7,1,10,9,2]
+ CRUSH rule 0 x 547 [5,13,7,9,3,14,10]
+ CRUSH rule 0 x 548 [11,7,12,15,4,2]
+ CRUSH rule 0 x 549 [14,1,4,9,13,6,3,10]
+ CRUSH rule 0 x 550 [9,15,3,13,1,6,4,11]
+ CRUSH rule 0 x 551 [11,2,15,6,13,5,1]
+ CRUSH rule 0 x 552 [2,11,14,1,9,6,5,12]
+ CRUSH rule 0 x 553 [11,9,14,6,4,13,3]
+ CRUSH rule 0 x 554 [11,14,6,4,13,9,3,1]
+ CRUSH rule 0 x 555 [6,5,10,9,14,2,13,1]
+ CRUSH rule 0 x 556 [15,6,3,13,11,4,1,9]
+ CRUSH rule 0 x 557 [12,2,5,14,10,9,6]
+ CRUSH rule 0 x 558 [12,1,6,15,5,10,3]
+ CRUSH rule 0 x 559 [2,13,5,10,14,7,1]
+ CRUSH rule 0 x 560 [4,9,12,6,3,10,1,15]
+ CRUSH rule 0 x 561 [12,7,1,2,5,15,11,9]
+ CRUSH rule 0 x 562 [7,13,9,14,2,1,11,4]
+ CRUSH rule 0 x 563 [15,4,3,10,13,9,7]
+ CRUSH rule 0 x 564 [2,13,7,1,15,10,4]
+ CRUSH rule 0 x 565 [3,12,4,1,14,7,11]
+ CRUSH rule 0 x 566 [6,14,4,2,13,11]
+ CRUSH rule 0 x 567 [15,4,11,6,3,12]
+ CRUSH rule 0 x 568 [4,14,1,6,10,13,3,9]
+ CRUSH rule 0 x 569 [11,3,15,13,5,1,9,7]
+ CRUSH rule 0 x 570 [1,10,13,4,7,2,9,14]
+ CRUSH rule 0 x 571 [10,12,14,9,4,2,1,6]
+ CRUSH rule 0 x 572 [12,14,3,10,6,1,4,9]
+ CRUSH rule 0 x 573 [7,15,11,2,12,9,4,1]
+ CRUSH rule 0 x 574 [11,14,13,1,3,7,4,9]
+ CRUSH rule 0 x 575 [5,13,15,9,6,10,2]
+ CRUSH rule 0 x 576 [3,15,11,9,1,6,5,13]
+ CRUSH rule 0 x 577 [13,9,6,15,3,11,4,1]
+ CRUSH rule 0 x 578 [4,10,1,2,7,13,14,9]
+ CRUSH rule 0 x 579 [13,1,15,2,10,7,5,9]
+ CRUSH rule 0 x 580 [3,12,4,1,10,15,7,9]
+ CRUSH rule 0 x 581 [7,14,12,10,1,2,9,5]
+ CRUSH rule 0 x 582 [10,5,13,14,1,2,7]
+ CRUSH rule 0 x 583 [4,15,1,9,10,12,2,6]
+ CRUSH rule 0 x 584 [10,1,5,13,6,9,2,15]
+ CRUSH rule 0 x 585 [5,3,6,1,11,14,13,9]
+ CRUSH rule 0 x 586 [7,10,14,12,9,3,5,1]
+ CRUSH rule 0 x 587 [11,6,9,4,1,14,13,2]
+ CRUSH rule 0 x 588 [3,12,7,15,4,9,1,10]
+ CRUSH rule 0 x 589 [9,7,12,1,10,3,4,15]
+ CRUSH rule 0 x 590 [12,1,3,9,10,6,4,14]
+ CRUSH rule 0 x 591 [2,6,14,13,9,11,4]
+ CRUSH rule 0 x 592 [15,12,9,7,5,2,11,1]
+ CRUSH rule 0 x 593 [13,14,5,11,9,6,2]
+ CRUSH rule 0 x 594 [12,14,2,9,7,4,1,11]
+ CRUSH rule 0 x 595 [12,7,10,3,1,14,9,4]
+ CRUSH rule 0 x 596 [2,7,12,11,1,5,15,9]
+ CRUSH rule 0 x 597 [15,1,2,10,7,13,5,9]
+ CRUSH rule 0 x 598 [11,5,9,14,12,7,3]
+ CRUSH rule 0 x 599 [13,11,1,5,6,2,15,9]
+ CRUSH rule 0 x 600 [4,12,3,10,9,7,1,14]
+ CRUSH rule 0 x 601 [13,5,15,2,1,7,9,10]
+ CRUSH rule 0 x 602 [3,11,7,1,13,15,5,9]
+ CRUSH rule 0 x 603 [3,1,4,14,10,9,6,12]
+ CRUSH rule 0 x 604 [14,2,6,1,11,13,9,4]
+ CRUSH rule 0 x 605 [2,7,12,5,14,10,1,9]
+ CRUSH rule 0 x 606 [12,15,1,5,7,9,3,11]
+ CRUSH rule 0 x 607 [3,9,10,14,7,1,4,12]
+ CRUSH rule 0 x 608 [13,10,1,7,9,15,5,2]
+ CRUSH rule 0 x 609 [14,3,7,9,11,12,5]
+ CRUSH rule 0 x 610 [7,10,5,1,12,2,15]
+ CRUSH rule 0 x 611 [13,1,5,3,10,7,15,9]
+ CRUSH rule 0 x 612 [7,1,2,13,9,15,4,11]
+ CRUSH rule 0 x 613 [10,7,14,9,5,2,13]
+ CRUSH rule 0 x 614 [9,4,15,3,1,11,6,12]
+ CRUSH rule 0 x 615 [9,4,11,2,1,12,6,15]
+ CRUSH rule 0 x 616 [10,14,1,5,3,6,12,9]
+ CRUSH rule 0 x 617 [15,7,2,11,12,1,9,4]
+ CRUSH rule 0 x 618 [4,2,10,6,14,9,1,12]
+ CRUSH rule 0 x 619 [15,4,3,9,6,1,13,11]
+ CRUSH rule 0 x 620 [3,7,11,14,13,1,5]
+ CRUSH rule 0 x 621 [3,6,4,14,1,11,13]
+ CRUSH rule 0 x 622 [10,2,13,5,15,9,1,7]
+ CRUSH rule 0 x 623 [4,9,14,7,3,13,11]
+ CRUSH rule 0 x 624 [3,9,15,6,10,1,5,12]
+ CRUSH rule 0 x 625 [11,7,3,5,13,15,9]
+ CRUSH rule 0 x 626 [10,12,2,1,9,7,5,14]
+ CRUSH rule 0 x 627 [1,12,10,14,3,5,9,7]
+ CRUSH rule 0 x 628 [15,13,11,4,2,1,7,9]
+ CRUSH rule 0 x 629 [5,6,15,12,1,10,3,9]
+ CRUSH rule 0 x 630 [1,4,12,9,3,7,15,11]
+ CRUSH rule 0 x 631 [5,7,1,15,12,11,3,9]
+ CRUSH rule 0 x 632 [12,3,11,9,6,1,15,5]
+ CRUSH rule 0 x 633 [14,4,3,7,10,12,9]
+ CRUSH rule 0 x 634 [6,9,5,3,13,11,14]
+ CRUSH rule 0 x 635 [6,5,2,15,9,12,11]
+ CRUSH rule 0 x 636 [13,6,11,3,15,9,1,4]
+ CRUSH rule 0 x 637 [3,1,10,6,9,12,4,14]
+ CRUSH rule 0 x 638 [10,15,3,5,13,1,7]
+ CRUSH rule 0 x 639 [6,9,14,4,3,1,10,13]
+ CRUSH rule 0 x 640 [9,6,1,11,14,2,4,13]
+ CRUSH rule 0 x 641 [10,6,5,14,1,9,12,2]
+ CRUSH rule 0 x 642 [1,15,4,6,2,10,9,12]
+ CRUSH rule 0 x 643 [3,7,5,1,10,15,13]
+ CRUSH rule 0 x 644 [15,13,6,9,3,11,5]
+ CRUSH rule 0 x 645 [14,2,4,9,10,1,7,13]
+ CRUSH rule 0 x 646 [5,13,14,1,6,9,2,11]
+ CRUSH rule 0 x 647 [10,1,9,13,6,2,14,5]
+ CRUSH rule 0 x 648 [6,5,2,14,11,1,12,9]
+ CRUSH rule 0 x 649 [3,9,13,11,4,14,1,7]
+ CRUSH rule 0 x 650 [10,9,4,15,12,7,1,2]
+ CRUSH rule 0 x 651 [3,9,5,7,14,1,13,11]
+ CRUSH rule 0 x 652 [15,9,4,6,13,1,2,11]
+ CRUSH rule 0 x 653 [11,14,1,3,6,9,12,5]
+ CRUSH rule 0 x 654 [13,6,2,10,15,4,1,9]
+ CRUSH rule 0 x 655 [6,3,4,15,12,11,1]
+ CRUSH rule 0 x 656 [3,15,1,4,6,12,11]
+ CRUSH rule 0 x 657 [11,15,3,5,7,13,1,9]
+ CRUSH rule 0 x 658 [7,2,10,12,1,4,9,14]
+ CRUSH rule 0 x 659 [2,5,14,6,10,12]
+ CRUSH rule 0 x 660 [13,14,10,6,4,9,3]
+ CRUSH rule 0 x 661 [7,15,3,12,11,4,9,1]
+ CRUSH rule 0 x 662 [15,2,12,5,1,10,9,7]
+ CRUSH rule 0 x 663 [14,9,13,10,5,3,1,6]
+ CRUSH rule 0 x 664 [6,10,12,4,9,2,1,15]
+ CRUSH rule 0 x 665 [2,9,12,1,7,10,4,15]
+ CRUSH rule 0 x 666 [12,3,6,1,15,9,10,4]
+ CRUSH rule 0 x 667 [1,9,12,10,2,14,7,4]
+ CRUSH rule 0 x 668 [9,5,1,2,6,11,13,15]
+ CRUSH rule 0 x 669 [9,7,14,5,11,13,1,2]
+ CRUSH rule 0 x 670 [6,10,9,13,1,2,15,4]
+ CRUSH rule 0 x 671 [6,15,5,10,13,3]
+ CRUSH rule 0 x 672 [2,9,13,1,4,14,6,10]
+ CRUSH rule 0 x 673 [7,10,5,9,15,13,2,1]
+ CRUSH rule 0 x 674 [7,12,10,1,14,9,3,4]
+ CRUSH rule 0 x 675 [9,5,1,10,6,14,12,2]
+ CRUSH rule 0 x 676 [10,12,2,1,4,15,7]
+ CRUSH rule 0 x 677 [2,12,1,4,10,6,15,9]
+ CRUSH rule 0 x 678 [1,2,4,10,12,14,9,6]
+ CRUSH rule 0 x 679 [5,6,12,15,9,11,3]
+ CRUSH rule 0 x 680 [7,11,3,1,15,4,9,12]
+ CRUSH rule 0 x 681 [6,4,3,11,14,13,1,9]
+ CRUSH rule 0 x 682 [6,1,11,15,12,2,5,9]
+ CRUSH rule 0 x 683 [6,13,2,4,9,14,10,1]
+ CRUSH rule 0 x 684 [9,11,3,7,15,4,13]
+ CRUSH rule 0 x 685 [5,1,15,7,9,2,10,13]
+ CRUSH rule 0 x 686 [1,9,11,14,6,13,4,3]
+ CRUSH rule 0 x 687 [7,13,3,5,11,9,15,1]
+ CRUSH rule 0 x 688 [11,9,1,14,3,5,7,12]
+ CRUSH rule 0 x 689 [5,2,9,12,1,14,11,7]
+ CRUSH rule 0 x 690 [9,7,10,3,13,15,5,1]
+ CRUSH rule 0 x 691 [11,15,9,5,7,13,2]
+ CRUSH rule 0 x 692 [15,5,1,2,9,11,12,7]
+ CRUSH rule 0 x 693 [5,6,12,15,2,10,9,1]
+ CRUSH rule 0 x 694 [4,7,1,10,12,3,14]
+ CRUSH rule 0 x 695 [6,13,14,10,9,5,1,3]
+ CRUSH rule 0 x 696 [1,2,4,14,7,11,13]
+ CRUSH rule 0 x 697 [13,11,3,6,4,14,9,1]
+ CRUSH rule 0 x 698 [11,13,4,2,6,1,9,15]
+ CRUSH rule 0 x 699 [7,14,12,4,2,11]
+ CRUSH rule 0 x 700 [12,14,11,9,4,6,3,1]
+ CRUSH rule 0 x 701 [3,13,1,14,4,7,11]
+ CRUSH rule 0 x 702 [3,12,15,6,5,11,1,9]
+ CRUSH rule 0 x 703 [15,11,13,3,4,7,1,9]
+ CRUSH rule 0 x 704 [6,4,2,15,11,1,13,9]
+ CRUSH rule 0 x 705 [14,6,11,5,1,13,9,3]
+ CRUSH rule 0 x 706 [1,12,3,6,4,10,15,9]
+ CRUSH rule 0 x 707 [4,7,14,3,10,9,13]
+ CRUSH rule 0 x 708 [3,10,5,1,15,9,7,13]
+ CRUSH rule 0 x 709 [11,12,3,7,5,14,1,9]
+ CRUSH rule 0 x 710 [14,2,11,9,5,7,12,1]
+ CRUSH rule 0 x 711 [14,3,9,10,12,5,6,1]
+ CRUSH rule 0 x 712 [12,3,11,15,9,1,6,4]
+ CRUSH rule 0 x 713 [11,9,3,15,13,6,4,1]
+ CRUSH rule 0 x 714 [12,1,9,7,2,15,10,5]
+ CRUSH rule 0 x 715 [6,1,14,4,11,12,3,9]
+ CRUSH rule 0 x 716 [11,13,9,14,5,2,1,7]
+ CRUSH rule 0 x 717 [12,4,10,9,15,1,2,7]
+ CRUSH rule 0 x 718 [7,15,5,2,11,13]
+ CRUSH rule 0 x 719 [5,15,13,3,1,7,11]
+ CRUSH rule 0 x 720 [4,13,10,2,7,9,1,14]
+ CRUSH rule 0 x 721 [11,3,14,9,1,12,4,6]
+ CRUSH rule 0 x 722 [2,4,6,1,9,15,13,10]
+ CRUSH rule 0 x 723 [2,1,12,15,11,7,5,9]
+ CRUSH rule 0 x 724 [7,1,9,10,5,15,13,2]
+ CRUSH rule 0 x 725 [11,12,7,15,4,1,2]
+ CRUSH rule 0 x 726 [7,14,4,3,11,13,9,1]
+ CRUSH rule 0 x 727 [2,5,1,11,15,7,12]
+ CRUSH rule 0 x 728 [13,11,4,6,15,2]
+ CRUSH rule 0 x 729 [15,11,4,6,2,9,1,13]
+ CRUSH rule 0 x 730 [3,7,1,13,11,15,9,5]
+ CRUSH rule 0 x 731 [9,1,6,5,2,11,13,15]
+ CRUSH rule 0 x 732 [1,2,10,13,9,4,7,15]
+ CRUSH rule 0 x 733 [11,3,5,6,1,9,12,15]
+ CRUSH rule 0 x 734 [14,3,11,7,12,9,4,1]
+ CRUSH rule 0 x 735 [6,9,2,10,13,14,5]
+ CRUSH rule 0 x 736 [3,9,1,11,7,5,13,14]
+ CRUSH rule 0 x 737 [1,4,2,12,9,10,6,15]
+ CRUSH rule 0 x 738 [11,15,7,4,9,2,12]
+ CRUSH rule 0 x 739 [11,12,6,2,4,1,14]
+ CRUSH rule 0 x 740 [7,9,10,13,1,15,2,5]
+ CRUSH rule 0 x 741 [12,11,7,15,2,5]
+ CRUSH rule 0 x 742 [9,7,4,11,12,1,14,3]
+ CRUSH rule 0 x 743 [5,13,9,15,10,7,3]
+ CRUSH rule 0 x 744 [6,2,13,1,14,11,4]
+ CRUSH rule 0 x 745 [3,6,1,4,11,12,14,9]
+ CRUSH rule 0 x 746 [3,7,9,10,14,5,1,13]
+ CRUSH rule 0 x 747 [15,11,5,2,13,9,1,7]
+ CRUSH rule 0 x 748 [6,10,13,2,14,5,9,1]
+ CRUSH rule 0 x 749 [14,9,10,7,5,1,2,12]
+ CRUSH rule 0 x 750 [1,14,6,5,11,2,13]
+ CRUSH rule 0 x 751 [15,1,6,9,5,11,12,3]
+ CRUSH rule 0 x 752 [13,1,7,3,11,15,9,4]
+ CRUSH rule 0 x 753 [4,11,1,3,15,7,13]
+ CRUSH rule 0 x 754 [14,12,11,4,2,1,9,6]
+ CRUSH rule 0 x 755 [13,6,1,10,4,2,14,9]
+ CRUSH rule 0 x 756 [3,4,14,6,1,10,13,9]
+ CRUSH rule 0 x 757 [10,6,1,4,13,15,2]
+ CRUSH rule 0 x 758 [6,3,4,10,15,13,9,1]
+ CRUSH rule 0 x 759 [5,7,3,14,11,1,9,13]
+ CRUSH rule 0 x 760 [1,15,10,12,4,3,9,7]
+ CRUSH rule 0 x 761 [2,12,1,14,5,7,10]
+ CRUSH rule 0 x 762 [1,4,10,9,3,7,14,12]
+ CRUSH rule 0 x 763 [4,13,1,14,7,10,2,9]
+ CRUSH rule 0 x 764 [1,14,6,13,9,5,2,10]
+ CRUSH rule 0 x 765 [9,15,2,13,4,1,11,7]
+ CRUSH rule 0 x 766 [11,2,7,15,9,12,4]
+ CRUSH rule 0 x 767 [6,11,4,3,12,14]
+ CRUSH rule 0 x 768 [2,12,15,7,1,11,9,4]
+ CRUSH rule 0 x 769 [15,1,9,2,11,12,7,4]
+ CRUSH rule 0 x 770 [15,13,4,6,3,10,1,9]
+ CRUSH rule 0 x 771 [9,2,12,11,6,14,5,1]
+ CRUSH rule 0 x 772 [4,3,13,11,14,1,7]
+ CRUSH rule 0 x 773 [3,7,4,15,1,12,11,9]
+ CRUSH rule 0 x 774 [12,6,3,15,5,9,10,1]
+ CRUSH rule 0 x 775 [5,10,14,2,6,1,13]
+ CRUSH rule 0 x 776 [10,15,3,9,6,13,1,5]
+ CRUSH rule 0 x 777 [11,13,4,7,1,14,9,2]
+ CRUSH rule 0 x 778 [13,1,9,11,15,6,3,5]
+ CRUSH rule 0 x 779 [5,11,1,14,2,9,13,6]
+ CRUSH rule 0 x 780 [13,9,3,6,4,1,14,10]
+ CRUSH rule 0 x 781 [5,7,14,3,1,12,11,9]
+ CRUSH rule 0 x 782 [2,15,9,7,11,13,4,1]
+ CRUSH rule 0 x 783 [12,7,5,14,9,1,2,10]
+ CRUSH rule 0 x 784 [14,1,10,13,3,4,7,9]
+ CRUSH rule 0 x 785 [6,12,1,2,4,9,15,10]
+ CRUSH rule 0 x 786 [10,5,2,15,1,7,12,9]
+ CRUSH rule 0 x 787 [1,12,10,2,9,4,14,6]
+ CRUSH rule 0 x 788 [4,2,9,13,6,15,11]
+ CRUSH rule 0 x 789 [9,2,14,7,4,12,1,10]
+ CRUSH rule 0 x 790 [15,2,7,4,1,10,13]
+ CRUSH rule 0 x 791 [9,4,7,13,14,11,1,3]
+ CRUSH rule 0 x 792 [6,4,15,10,12,3]
+ CRUSH rule 0 x 793 [15,9,6,2,13,11,4]
+ CRUSH rule 0 x 794 [5,12,2,14,9,10,1,6]
+ CRUSH rule 0 x 795 [6,14,12,4,10,1,2,9]
+ CRUSH rule 0 x 796 [11,2,12,6,15,4]
+ CRUSH rule 0 x 797 [14,3,7,1,5,13,11]
+ CRUSH rule 0 x 798 [5,11,6,13,1,3,15,9]
+ CRUSH rule 0 x 799 [2,9,14,4,13,6,11]
+ CRUSH rule 0 x 800 [6,3,4,11,15,13]
+ CRUSH rule 0 x 801 [2,5,6,13,9,1,10,15]
+ CRUSH rule 0 x 802 [1,4,12,7,3,9,10,14]
+ CRUSH rule 0 x 803 [7,2,4,1,11,13,9,14]
+ CRUSH rule 0 x 804 [5,14,9,7,3,1,12,10]
+ CRUSH rule 0 x 805 [13,4,3,1,10,15,7]
+ CRUSH rule 0 x 806 [6,2,13,4,15,1,10]
+ CRUSH rule 0 x 807 [14,2,7,4,9,12,1,10]
+ CRUSH rule 0 x 808 [2,15,12,7,9,1,5,10]
+ CRUSH rule 0 x 809 [1,11,7,12,4,2,15,9]
+ CRUSH rule 0 x 810 [2,5,9,12,15,1,7,11]
+ CRUSH rule 0 x 811 [15,6,3,10,1,5,9,12]
+ CRUSH rule 0 x 812 [7,11,2,14,9,5,12]
+ CRUSH rule 0 x 813 [4,10,13,14,2,6,9]
+ CRUSH rule 0 x 814 [13,4,9,3,10,6,15]
+ CRUSH rule 0 x 815 [15,12,9,4,10,6,1,2]
+ CRUSH rule 0 x 816 [14,10,13,7,3,9,4,1]
+ CRUSH rule 0 x 817 [10,7,2,15,13,9,5]
+ CRUSH rule 0 x 818 [15,2,11,4,1,12,6,9]
+ CRUSH rule 0 x 819 [5,12,10,6,1,14,3]
+ CRUSH rule 0 x 820 [3,6,9,12,11,15,4,1]
+ CRUSH rule 0 x 821 [15,10,9,13,3,4,7,1]
+ CRUSH rule 0 x 822 [10,13,2,9,7,4,14,1]
+ CRUSH rule 0 x 823 [2,6,12,10,15,4,1,9]
+ CRUSH rule 0 x 824 [3,7,9,13,15,5,10]
+ CRUSH rule 0 x 825 [10,5,14,6,12,9,3]
+ CRUSH rule 0 x 826 [5,2,11,15,1,12,9,7]
+ CRUSH rule 0 x 827 [13,5,1,3,7,9,11,14]
+ CRUSH rule 0 x 828 [12,6,10,5,1,9,2,15]
+ CRUSH rule 0 x 829 [13,6,15,10,5,3,9]
+ CRUSH rule 0 x 830 [15,13,2,9,7,11,1,5]
+ CRUSH rule 0 x 831 [1,4,11,12,6,3,15]
+ CRUSH rule 0 x 832 [14,11,13,2,9,4,6,1]
+ CRUSH rule 0 x 833 [9,13,3,11,7,5,15,1]
+ CRUSH rule 0 x 834 [9,7,5,1,11,2,13,14]
+ CRUSH rule 0 x 835 [14,3,13,6,4,9,1,10]
+ CRUSH rule 0 x 836 [3,9,10,13,1,5,14,7]
+ CRUSH rule 0 x 837 [15,12,11,2,7,9,5]
+ CRUSH rule 0 x 838 [12,14,9,2,5,7,11]
+ CRUSH rule 0 x 839 [3,4,6,10,15,1,13,9]
+ CRUSH rule 0 x 840 [10,15,12,4,7,1,2,9]
+ CRUSH rule 0 x 841 [3,5,7,12,11,15,1,9]
+ CRUSH rule 0 x 842 [9,13,2,6,5,14,10,1]
+ CRUSH rule 0 x 843 [14,7,4,9,3,12,1,10]
+ CRUSH rule 0 x 844 [7,1,4,15,9,2,11,12]
+ CRUSH rule 0 x 845 [13,6,1,15,4,2,11]
+ CRUSH rule 0 x 846 [3,7,15,13,1,9,10,4]
+ CRUSH rule 0 x 847 [12,15,11,5,2,7,1]
+ CRUSH rule 0 x 848 [11,13,1,14,5,9,2,7]
+ CRUSH rule 0 x 849 [3,15,11,9,6,1,13,5]
+ CRUSH rule 0 x 850 [1,3,10,6,14,4,9,12]
+ CRUSH rule 0 x 851 [14,4,3,6,11,1,13]
+ CRUSH rule 0 x 852 [9,12,4,7,15,2,11,1]
+ CRUSH rule 0 x 853 [13,14,6,11,2,4,9,1]
+ CRUSH rule 0 x 854 [7,11,12,1,4,15,3]
+ CRUSH rule 0 x 855 [14,4,12,6,3,1,10]
+ CRUSH rule 0 x 856 [5,10,7,3,15,9,12,1]
+ CRUSH rule 0 x 857 [4,3,13,11,9,1,7,14]
+ CRUSH rule 0 x 858 [5,15,6,3,9,12,1,10]
+ CRUSH rule 0 x 859 [5,15,6,2,1,11,12,9]
+ CRUSH rule 0 x 860 [11,14,1,12,6,9,2,4]
+ CRUSH rule 0 x 861 [13,7,4,10,1,14,3,9]
+ CRUSH rule 0 x 862 [5,10,9,7,3,12,1,15]
+ CRUSH rule 0 x 863 [11,6,3,9,4,12,15]
+ CRUSH rule 0 x 864 [6,13,4,2,10,15,1,9]
+ CRUSH rule 0 x 865 [4,1,14,11,6,9,3,13]
+ CRUSH rule 0 x 866 [2,13,4,15,9,6,11]
+ CRUSH rule 0 x 867 [12,2,9,10,4,14,6]
+ CRUSH rule 0 x 868 [14,11,7,2,1,4,9,12]
+ CRUSH rule 0 x 869 [10,13,7,14,3,5,1]
+ CRUSH rule 0 x 870 [14,9,11,4,3,12,6,1]
+ CRUSH rule 0 x 871 [6,2,1,4,15,13,11,9]
+ CRUSH rule 0 x 872 [6,1,15,3,10,12,5]
+ CRUSH rule 0 x 873 [2,5,12,10,1,9,15,7]
+ CRUSH rule 0 x 874 [12,4,7,2,15,10,1]
+ CRUSH rule 0 x 875 [10,6,14,1,12,5,9,3]
+ CRUSH rule 0 x 876 [14,7,13,3,9,1,11,4]
+ CRUSH rule 0 x 877 [15,11,13,9,5,1,6,3]
+ CRUSH rule 0 x 878 [7,14,3,13,9,1,11,4]
+ CRUSH rule 0 x 879 [12,2,7,4,10,15]
+ CRUSH rule 0 x 880 [2,12,10,7,1,4,9,14]
+ CRUSH rule 0 x 881 [6,3,1,11,4,15,9,13]
+ CRUSH rule 0 x 882 [11,13,7,1,2,15,4,9]
+ CRUSH rule 0 x 883 [13,1,3,10,6,5,9,15]
+ CRUSH rule 0 x 884 [6,15,4,9,3,11,12,1]
+ CRUSH rule 0 x 885 [14,7,9,4,2,13,11]
+ CRUSH rule 0 x 886 [13,11,4,2,1,14,9,6]
+ CRUSH rule 0 x 887 [14,4,12,11,2,6,9,1]
+ CRUSH rule 0 x 888 [10,12,7,15,9,2,1,5]
+ CRUSH rule 0 x 889 [15,13,4,1,6,2,10,9]
+ CRUSH rule 0 x 890 [10,12,14,2,9,5,6,1]
+ CRUSH rule 0 x 891 [9,5,11,6,3,15,12,1]
+ CRUSH rule 0 x 892 [12,15,2,4,7,9,11,1]
+ CRUSH rule 0 x 893 [1,3,5,9,6,10,14,12]
+ CRUSH rule 0 x 894 [7,2,11,13,4,1,14]
+ CRUSH rule 0 x 895 [2,1,11,5,7,15,13,9]
+ CRUSH rule 0 x 896 [9,1,14,10,4,12,2,7]
+ CRUSH rule 0 x 897 [7,5,14,3,1,9,11,12]
+ CRUSH rule 0 x 898 [10,6,12,9,15,5,2,1]
+ CRUSH rule 0 x 899 [1,11,5,3,13,14,9,6]
+ CRUSH rule 0 x 900 [2,9,10,7,13,14,5,1]
+ CRUSH rule 0 x 901 [9,12,11,3,14,4,1,6]
+ CRUSH rule 0 x 902 [4,2,6,15,12,10,1]
+ CRUSH rule 0 x 903 [14,10,3,1,12,6,5]
+ CRUSH rule 0 x 904 [15,12,4,9,6,3,11]
+ CRUSH rule 0 x 905 [12,6,11,3,9,4,15]
+ CRUSH rule 0 x 906 [14,11,12,2,4,9,6]
+ CRUSH rule 0 x 907 [7,12,3,9,10,5,14,1]
+ CRUSH rule 0 x 908 [2,15,9,6,10,13,5,1]
+ CRUSH rule 0 x 909 [10,14,1,13,2,9,7,4]
+ CRUSH rule 0 x 910 [12,7,4,15,10,3,1,9]
+ CRUSH rule 0 x 911 [11,15,2,4,9,13,6,1]
+ CRUSH rule 0 x 912 [6,4,14,13,3,1,11]
+ CRUSH rule 0 x 913 [4,6,10,1,12,3,9,14]
+ CRUSH rule 0 x 914 [4,15,2,10,1,13,7]
+ CRUSH rule 0 x 915 [12,14,1,9,4,3,11,6]
+ CRUSH rule 0 x 916 [3,1,11,5,6,13,14]
+ CRUSH rule 0 x 917 [1,15,6,5,10,3,13,9]
+ CRUSH rule 0 x 918 [7,14,11,4,9,2,13]
+ CRUSH rule 0 x 919 [10,7,3,13,15,1,4]
+ CRUSH rule 0 x 920 [4,2,10,15,1,13,6]
+ CRUSH rule 0 x 921 [1,11,6,13,4,2,9,14]
+ CRUSH rule 0 x 922 [6,4,14,13,3,1,10,9]
+ CRUSH rule 0 x 923 [12,2,5,14,10,1,9,6]
+ CRUSH rule 0 x 924 [6,2,14,13,9,1,11,5]
+ CRUSH rule 0 x 925 [12,15,2,10,1,5,7]
+ CRUSH rule 0 x 926 [3,13,10,1,14,9,6,5]
+ CRUSH rule 0 x 927 [6,5,1,11,14,2,13,9]
+ CRUSH rule 0 x 928 [13,1,3,9,6,11,15,5]
+ CRUSH rule 0 x 929 [10,7,1,5,2,12,9,14]
+ CRUSH rule 0 x 930 [7,15,10,5,1,13,2]
+ CRUSH rule 0 x 931 [6,15,11,9,5,3,1,13]
+ CRUSH rule 0 x 932 [13,2,5,11,9,1,6,15]
+ CRUSH rule 0 x 933 [12,7,14,10,4,1,2,9]
+ CRUSH rule 0 x 934 [12,2,5,7,9,1,15,11]
+ CRUSH rule 0 x 935 [6,11,1,14,5,13,3,9]
+ CRUSH rule 0 x 936 [9,12,7,5,1,2,14,11]
+ CRUSH rule 0 x 937 [14,2,11,1,13,4,9,6]
+ CRUSH rule 0 x 938 [14,3,5,11,7,9,13]
+ CRUSH rule 0 x 939 [6,4,14,9,12,1,11,2]
+ CRUSH rule 0 x 940 [13,11,4,2,1,6,15]
+ CRUSH rule 0 x 941 [3,12,4,7,14,10]
+ CRUSH rule 0 x 942 [15,12,10,4,1,9,3,7]
+ CRUSH rule 0 x 943 [10,2,4,9,6,15,12]
+ CRUSH rule 0 x 944 [2,9,4,7,1,14,12,11]
+ CRUSH rule 0 x 945 [10,15,2,9,5,12,7]
+ CRUSH rule 0 x 946 [11,15,7,12,5,9,2]
+ CRUSH rule 0 x 947 [11,3,14,1,12,5,6,9]
+ CRUSH rule 0 x 948 [7,13,11,5,14,2,1,9]
+ CRUSH rule 0 x 949 [9,1,12,5,15,10,2,6]
+ CRUSH rule 0 x 950 [9,15,13,6,4,2,10]
+ CRUSH rule 0 x 951 [2,6,12,9,10,4,14]
+ CRUSH rule 0 x 952 [9,7,15,3,5,13,11]
+ CRUSH rule 0 x 953 [1,3,6,10,12,14,4,9]
+ CRUSH rule 0 x 954 [10,2,14,9,4,6,12,1]
+ CRUSH rule 0 x 955 [7,14,3,1,10,4,9,12]
+ CRUSH rule 0 x 956 [1,6,11,5,14,3,9,13]
+ CRUSH rule 0 x 957 [14,11,1,12,6,9,4,3]
+ CRUSH rule 0 x 958 [15,4,3,11,1,6,12,9]
+ CRUSH rule 0 x 959 [2,1,12,15,10,9,4,6]
+ CRUSH rule 0 x 960 [2,6,11,13,15,4,9]
+ CRUSH rule 0 x 961 [3,13,11,9,6,1,4,15]
+ CRUSH rule 0 x 962 [5,11,3,14,1,6,13,9]
+ CRUSH rule 0 x 963 [13,10,15,4,6,9,1,3]
+ CRUSH rule 0 x 964 [7,11,4,9,2,12,1,15]
+ CRUSH rule 0 x 965 [12,2,9,7,4,15,11,1]
+ CRUSH rule 0 x 966 [12,14,9,4,1,2,11,7]
+ CRUSH rule 0 x 967 [7,5,3,10,12,14]
+ CRUSH rule 0 x 968 [12,15,4,9,11,6,3]
+ CRUSH rule 0 x 969 [11,4,7,1,9,14,13,2]
+ CRUSH rule 0 x 970 [5,12,10,1,3,14,9,6]
+ CRUSH rule 0 x 971 [1,9,4,12,7,2,10,15]
+ CRUSH rule 0 x 972 [12,3,14,5,1,9,7,11]
+ CRUSH rule 0 x 973 [1,10,4,12,2,7,15]
+ CRUSH rule 0 x 974 [7,11,1,2,15,4,12,9]
+ CRUSH rule 0 x 975 [7,9,15,12,2,11,4]
+ CRUSH rule 0 x 976 [7,3,15,5,12,11,1]
+ CRUSH rule 0 x 977 [14,3,6,10,4,1,12]
+ CRUSH rule 0 x 978 [12,5,11,1,15,3,6]
+ CRUSH rule 0 x 979 [5,1,13,6,15,10,3,9]
+ CRUSH rule 0 x 980 [15,11,5,6,1,3,13,9]
+ CRUSH rule 0 x 981 [5,11,15,12,7,1,2]
+ CRUSH rule 0 x 982 [2,6,14,11,12,9,5]
+ CRUSH rule 0 x 983 [3,12,10,9,14,5,6]
+ CRUSH rule 0 x 984 [15,13,1,10,2,5,7]
+ CRUSH rule 0 x 985 [11,2,15,1,4,13,6,9]
+ CRUSH rule 0 x 986 [6,13,9,1,15,10,5,2]
+ CRUSH rule 0 x 987 [13,14,5,10,6,1,3,9]
+ CRUSH rule 0 x 988 [12,9,10,14,3,1,4,7]
+ CRUSH rule 0 x 989 [7,4,3,15,9,13,10,1]
+ CRUSH rule 0 x 990 [1,10,9,13,3,4,6,15]
+ CRUSH rule 0 x 991 [7,11,1,14,2,5,9,12]
+ CRUSH rule 0 x 992 [9,10,2,13,7,4,1,15]
+ CRUSH rule 0 x 993 [6,10,14,12,4,1,2]
+ CRUSH rule 0 x 994 [3,13,15,4,11,7,1,9]
+ CRUSH rule 0 x 995 [15,6,12,2,5,11]
+ CRUSH rule 0 x 996 [15,10,5,3,13,1,9,7]
+ CRUSH rule 0 x 997 [15,2,1,12,7,9,4,10]
+ CRUSH rule 0 x 998 [6,1,9,5,12,11,15,2]
+ CRUSH rule 0 x 999 [9,10,15,5,13,3,7]
+ CRUSH rule 0 x 1000 [14,2,9,4,12,1,6,11]
+ CRUSH rule 0 x 1001 [11,14,4,2,6,9,1,13]
+ CRUSH rule 0 x 1002 [1,10,14,2,9,5,13,7]
+ CRUSH rule 0 x 1003 [10,7,5,14,2,1,9,12]
+ CRUSH rule 0 x 1004 [15,1,4,6,10,12,9,3]
+ CRUSH rule 0 x 1005 [6,12,2,10,9,15,5,1]
+ CRUSH rule 0 x 1006 [10,12,15,1,2,6,5]
+ CRUSH rule 0 x 1007 [1,7,13,14,3,4,10]
+ CRUSH rule 0 x 1008 [7,4,9,11,3,15,1,13]
+ CRUSH rule 0 x 1009 [5,2,11,7,15,9,1,12]
+ CRUSH rule 0 x 1010 [10,2,15,6,9,13,4,1]
+ CRUSH rule 0 x 1011 [6,3,12,1,10,4,9,14]
+ CRUSH rule 0 x 1012 [12,6,9,15,3,1,5,11]
+ CRUSH rule 0 x 1013 [2,14,12,4,9,1,6,10]
+ CRUSH rule 0 x 1014 [1,13,7,2,10,14,5]
+ CRUSH rule 0 x 1015 [12,6,10,1,4,15,9,2]
+ CRUSH rule 0 x 1016 [10,13,14,3,5,6,1]
+ CRUSH rule 0 x 1017 [5,11,14,7,13,9,2]
+ CRUSH rule 0 x 1018 [13,11,14,1,9,3,5,7]
+ CRUSH rule 0 x 1019 [10,13,14,7,5,1,2]
+ CRUSH rule 0 x 1020 [3,1,13,4,10,9,14,6]
+ CRUSH rule 0 x 1021 [2,11,14,9,4,6,1,13]
+ CRUSH rule 0 x 1022 [15,5,7,2,12,10]
+ CRUSH rule 0 x 1023 [15,2,9,12,1,7,4,11]
+ rule 0 (replicated_ruleset) num_rep 8 result size == 6:\t36/1024 (esc)
+ rule 0 (replicated_ruleset) num_rep 8 result size == 7:\t264/1024 (esc)
+ rule 0 (replicated_ruleset) num_rep 8 result size == 8:\t724/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3,15,12,1,4,9]
+ CRUSH rule 0 x 1 [10,15,1,2,13,4,7,9]
+ CRUSH rule 0 x 2 [1,12,2,6,5,10,15]
+ CRUSH rule 0 x 3 [15,4,10,2,9,6,13]
+ CRUSH rule 0 x 4 [14,2,10,1,9,4,7,13]
+ CRUSH rule 0 x 5 [7,4,11,2,13,15,9]
+ CRUSH rule 0 x 6 [12,6,10,9,3,4,14]
+ CRUSH rule 0 x 7 [9,2,6,12,11,4,1,14]
+ CRUSH rule 0 x 8 [10,2,15,1,4,13,6,9]
+ CRUSH rule 0 x 9 [7,1,14,2,11,9,12,4]
+ CRUSH rule 0 x 10 [10,14,4,1,2,7,13,9]
+ CRUSH rule 0 x 11 [13,9,14,7,5,11,2,1]
+ CRUSH rule 0 x 12 [7,1,2,5,13,15,11,9]
+ CRUSH rule 0 x 13 [3,5,12,7,9,1,14,11]
+ CRUSH rule 0 x 14 [13,5,2,7,10,15,1,9]
+ CRUSH rule 0 x 15 [15,1,9,6,13,3,5,11]
+ CRUSH rule 0 x 16 [7,11,14,2,13,1,9,4]
+ CRUSH rule 0 x 17 [10,1,13,2,4,6,14,9]
+ CRUSH rule 0 x 18 [1,7,3,10,5,12,9,14]
+ CRUSH rule 0 x 19 [7,12,2,4,15,10,1]
+ CRUSH rule 0 x 20 [14,12,3,10,9,4,7,1]
+ CRUSH rule 0 x 21 [3,12,1,10,4,15,6]
+ CRUSH rule 0 x 22 [6,3,13,11,4,1,15]
+ CRUSH rule 0 x 23 [10,5,13,9,3,15,1,6]
+ CRUSH rule 0 x 24 [12,11,3,1,9,4,7,15]
+ CRUSH rule 0 x 25 [7,12,15,1,3,10,4,9]
+ CRUSH rule 0 x 26 [1,7,13,2,14,5,9,11]
+ CRUSH rule 0 x 27 [3,6,15,4,13,9,11,1]
+ CRUSH rule 0 x 28 [14,4,3,9,6,11,13]
+ CRUSH rule 0 x 29 [5,14,12,11,6,3,1,9]
+ CRUSH rule 0 x 30 [2,5,6,9,1,11,13,14]
+ CRUSH rule 0 x 31 [5,15,10,1,9,13,6,3]
+ CRUSH rule 0 x 32 [9,10,2,1,13,14,6,4]
+ CRUSH rule 0 x 33 [13,4,9,2,7,1,10,14]
+ CRUSH rule 0 x 34 [13,15,2,4,1,10,9,6]
+ CRUSH rule 0 x 35 [4,14,3,13,10,9,1,6]
+ CRUSH rule 0 x 36 [3,12,9,7,5,10,14,1]
+ CRUSH rule 0 x 37 [9,2,6,14,11,1,4,13]
+ CRUSH rule 0 x 38 [3,4,13,10,9,1,14,6]
+ CRUSH rule 0 x 39 [12,7,14,11,1,9,5,2]
+ CRUSH rule 0 x 40 [10,1,9,5,15,2,6,13]
+ CRUSH rule 0 x 41 [4,9,11,1,14,13,6,3]
+ CRUSH rule 0 x 42 [3,6,14,10,12,5,1]
+ CRUSH rule 0 x 43 [10,5,15,7,2,9,12]
+ CRUSH rule 0 x 44 [11,4,13,3,7,14,9]
+ CRUSH rule 0 x 45 [11,12,15,9,1,5,6,3]
+ CRUSH rule 0 x 46 [6,9,2,14,11,13,1,5]
+ CRUSH rule 0 x 47 [3,9,6,4,13,1,11,15]
+ CRUSH rule 0 x 48 [4,6,2,1,10,14,13]
+ CRUSH rule 0 x 49 [9,15,10,7,4,3,13]
+ CRUSH rule 0 x 50 [14,12,1,4,2,11,6,9]
+ CRUSH rule 0 x 51 [10,6,5,12,15,2,1,9]
+ CRUSH rule 0 x 52 [12,1,9,11,7,3,14,4]
+ CRUSH rule 0 x 53 [3,6,13,9,5,1,11,15]
+ CRUSH rule 0 x 54 [4,13,9,2,14,10,6,1]
+ CRUSH rule 0 x 55 [4,11,2,7,1,13,9,15]
+ CRUSH rule 0 x 56 [5,9,10,1,3,13,14,7]
+ CRUSH rule 0 x 57 [6,2,1,15,10,12,5]
+ CRUSH rule 0 x 58 [7,1,11,4,3,14,12,9]
+ CRUSH rule 0 x 59 [2,13,1,10,9,5,14,6]
+ CRUSH rule 0 x 60 [3,6,11,1,4,9,12,15]
+ CRUSH rule 0 x 61 [3,15,13,7,4,1,10,9]
+ CRUSH rule 0 x 62 [15,11,7,12,5,9,2,1]
+ CRUSH rule 0 x 63 [10,14,12,1,7,3]
+ CRUSH rule 0 x 64 [3,9,1,4,7,12,11,15]
+ CRUSH rule 0 x 65 [4,12,11,7,14,3,1]
+ CRUSH rule 0 x 66 [15,11,6,9,4,1,3,12]
+ CRUSH rule 0 x 67 [2,6,4,14,1,11,12]
+ CRUSH rule 0 x 68 [15,7,4,2,9,12,11]
+ CRUSH rule 0 x 69 [2,1,15,10,4,9,13,7]
+ CRUSH rule 0 x 70 [9,6,1,3,13,15,11,5]
+ CRUSH rule 0 x 71 [15,5,1,3,13,10,7]
+ CRUSH rule 0 x 72 [9,10,3,5,7,12,15,1]
+ CRUSH rule 0 x 73 [5,3,11,1,7,12,15,9]
+ CRUSH rule 0 x 74 [11,7,9,5,1,15,3,12]
+ CRUSH rule 0 x 75 [9,7,11,14,12,1,2,5]
+ CRUSH rule 0 x 76 [6,1,3,5,14,10,12,9]
+ CRUSH rule 0 x 77 [7,4,2,13,9,1,11,15]
+ CRUSH rule 0 x 78 [9,3,1,5,6,13,14,11]
+ CRUSH rule 0 x 79 [13,2,15,5,7,9,11,1]
+ CRUSH rule 0 x 80 [15,2,6,4,13,10,1]
+ CRUSH rule 0 x 81 [15,2,1,11,4,6,13]
+ CRUSH rule 0 x 82 [14,13,5,11,6,2,1,9]
+ CRUSH rule 0 x 83 [4,15,3,9,10,13,6,1]
+ CRUSH rule 0 x 84 [10,7,9,15,3,4,1,13]
+ CRUSH rule 0 x 85 [3,15,9,7,4,11,1,12]
+ CRUSH rule 0 x 86 [10,9,14,1,13,4,2,7]
+ CRUSH rule 0 x 87 [15,10,7,12,5,3,9,1]
+ CRUSH rule 0 x 88 [4,13,3,1,9,15,11,7]
+ CRUSH rule 0 x 89 [3,9,7,4,1,14,10,12]
+ CRUSH rule 0 x 90 [4,9,7,12,11,14,2,1]
+ CRUSH rule 0 x 91 [6,11,9,1,2,4,14,13]
+ CRUSH rule 0 x 92 [1,5,10,9,13,15,6,2]
+ CRUSH rule 0 x 93 [9,3,15,13,7,5,1,10]
+ CRUSH rule 0 x 94 [9,2,12,5,6,11,1,14]
+ CRUSH rule 0 x 95 [7,15,4,10,9,13,2,1]
+ CRUSH rule 0 x 96 [2,15,11,7,5,1,12]
+ CRUSH rule 0 x 97 [4,11,2,13,1,7,9,14]
+ CRUSH rule 0 x 98 [11,13,9,3,15,1,5,6]
+ CRUSH rule 0 x 99 [12,4,11,7,3,14,9,1]
+ CRUSH rule 0 x 100 [9,4,10,15,7,3,13]
+ CRUSH rule 0 x 101 [15,7,1,9,10,5,2,12]
+ CRUSH rule 0 x 102 [3,11,14,6,13,4,9,1]
+ CRUSH rule 0 x 103 [13,11,6,14,4,3,1]
+ CRUSH rule 0 x 104 [14,6,3,5,9,1,10,13]
+ CRUSH rule 0 x 105 [14,10,1,9,3,5,6,13]
+ CRUSH rule 0 x 106 [6,5,13,2,14,11,1,9]
+ CRUSH rule 0 x 107 [3,1,10,14,13,5,9,6]
+ CRUSH rule 0 x 108 [5,10,7,2,15,9,12,1]
+ CRUSH rule 0 x 109 [9,1,13,7,15,5,11,3]
+ CRUSH rule 0 x 110 [5,1,11,3,7,14,13,9]
+ CRUSH rule 0 x 111 [10,1,9,7,5,2,13,14]
+ CRUSH rule 0 x 112 [1,10,4,14,2,12,6,9]
+ CRUSH rule 0 x 113 [6,10,13,9,1,5,2,14]
+ CRUSH rule 0 x 114 [5,13,6,2,1,14,11]
+ CRUSH rule 0 x 115 [10,13,14,3,9,1,6,5]
+ CRUSH rule 0 x 116 [1,14,13,2,11,5,7]
+ CRUSH rule 0 x 117 [5,6,1,12,15,9,11,3]
+ CRUSH rule 0 x 118 [10,4,13,15,9,3,1,6]
+ CRUSH rule 0 x 119 [14,12,11,4,6,9,3,1]
+ CRUSH rule 0 x 120 [11,3,14,13,4,7]
+ CRUSH rule 0 x 121 [9,5,1,11,7,3,15,12]
+ CRUSH rule 0 x 122 [4,3,14,1,11,13,7]
+ CRUSH rule 0 x 123 [3,10,5,6,9,1,12,15]
+ CRUSH rule 0 x 124 [12,2,1,5,14,7,11,9]
+ CRUSH rule 0 x 125 [9,12,15,1,6,5,3,10]
+ CRUSH rule 0 x 126 [7,15,10,9,2,12,5,1]
+ CRUSH rule 0 x 127 [4,14,9,13,1,3,7,10]
+ CRUSH rule 0 x 128 [3,12,1,10,4,9,7,14]
+ CRUSH rule 0 x 129 [11,13,14,2,9,4,6,1]
+ CRUSH rule 0 x 130 [3,13,5,14,10,1,9,6]
+ CRUSH rule 0 x 131 [12,1,6,15,4,2,11,9]
+ CRUSH rule 0 x 132 [11,15,13,9,2,5,7]
+ CRUSH rule 0 x 133 [3,6,9,11,15,12,5]
+ CRUSH rule 0 x 134 [12,5,6,15,3,9,10,1]
+ CRUSH rule 0 x 135 [3,14,12,4,6,11,9]
+ CRUSH rule 0 x 136 [15,6,9,4,10,3,12]
+ CRUSH rule 0 x 137 [14,3,6,11,1,9,4,13]
+ CRUSH rule 0 x 138 [13,15,4,10,2,7,1]
+ CRUSH rule 0 x 139 [11,2,13,9,1,15,7,5]
+ CRUSH rule 0 x 140 [11,4,12,15,2,6,9,1]
+ CRUSH rule 0 x 141 [6,12,15,11,3,5,1]
+ CRUSH rule 0 x 142 [3,14,7,9,11,1,4,13]
+ CRUSH rule 0 x 143 [9,6,4,2,14,10,12]
+ CRUSH rule 0 x 144 [13,7,11,2,14,4,1,9]
+ CRUSH rule 0 x 145 [12,2,6,10,9,4,14,1]
+ CRUSH rule 0 x 146 [1,5,9,2,6,13,14,10]
+ CRUSH rule 0 x 147 [1,4,9,11,2,7,15,12]
+ CRUSH rule 0 x 148 [12,7,9,2,14,11,1,4]
+ CRUSH rule 0 x 149 [2,5,9,12,11,1,7,14]
+ CRUSH rule 0 x 150 [1,15,2,10,7,9,5,12]
+ CRUSH rule 0 x 151 [2,9,14,7,1,10,5,13]
+ CRUSH rule 0 x 152 [5,9,2,6,10,13,14]
+ CRUSH rule 0 x 153 [6,9,4,15,2,1,10,12]
+ CRUSH rule 0 x 154 [3,11,7,1,4,12,15,9]
+ CRUSH rule 0 x 155 [14,12,7,3,5,1,9,11]
+ CRUSH rule 0 x 156 [7,13,3,10,15,5,1]
+ CRUSH rule 0 x 157 [15,1,6,4,3,10,9,13]
+ CRUSH rule 0 x 158 [15,1,10,6,12,2,4]
+ CRUSH rule 0 x 159 [4,14,3,12,10,6,1,9]
+ CRUSH rule 0 x 160 [5,7,3,14,11,1,12]
+ CRUSH rule 0 x 161 [1,2,11,4,6,13,14]
+ CRUSH rule 0 x 162 [10,6,1,12,2,4,14,9]
+ CRUSH rule 0 x 163 [15,1,10,2,6,4,13,9]
+ CRUSH rule 0 x 164 [9,14,10,7,12,2,5,1]
+ CRUSH rule 0 x 165 [11,7,2,13,9,15,1,5]
+ CRUSH rule 0 x 166 [1,2,12,14,4,11,7,9]
+ CRUSH rule 0 x 167 [9,7,3,4,11,13,15,1]
+ CRUSH rule 0 x 168 [13,2,4,1,6,15,10,9]
+ CRUSH rule 0 x 169 [1,4,9,14,13,10,2,7]
+ CRUSH rule 0 x 170 [1,15,7,9,12,10,3,5]
+ CRUSH rule 0 x 171 [9,2,10,7,1,5,15,12]
+ CRUSH rule 0 x 172 [14,4,10,12,9,3,6]
+ CRUSH rule 0 x 173 [5,10,12,15,6,1,2,9]
+ CRUSH rule 0 x 174 [15,6,4,12,1,11,9,2]
+ CRUSH rule 0 x 175 [5,7,9,3,10,1,14,13]
+ CRUSH rule 0 x 176 [9,6,3,14,13,10,4]
+ CRUSH rule 0 x 177 [2,9,10,13,4,1,14,7]
+ CRUSH rule 0 x 178 [12,11,7,14,3,4]
+ CRUSH rule 0 x 179 [2,10,13,9,5,1,7,14]
+ CRUSH rule 0 x 180 [3,11,5,15,7,12]
+ CRUSH rule 0 x 181 [9,12,6,5,1,10,14,2]
+ CRUSH rule 0 x 182 [5,13,11,2,1,6,14]
+ CRUSH rule 0 x 183 [5,7,10,13,3,9,14,1]
+ CRUSH rule 0 x 184 [2,5,11,12,7,1,9,15]
+ CRUSH rule 0 x 185 [13,5,7,11,2,14]
+ CRUSH rule 0 x 186 [6,14,13,5,10,1,3,9]
+ CRUSH rule 0 x 187 [1,4,11,13,6,14,9,3]
+ CRUSH rule 0 x 188 [9,13,5,14,10,6,2]
+ CRUSH rule 0 x 189 [6,12,4,9,2,1,11,15]
+ CRUSH rule 0 x 190 [9,13,15,10,3,1,5,6]
+ CRUSH rule 0 x 191 [7,11,4,1,15,12,9,2]
+ CRUSH rule 0 x 192 [2,11,5,15,6,1,13]
+ CRUSH rule 0 x 193 [3,13,6,10,4,1,9,14]
+ CRUSH rule 0 x 194 [3,13,4,14,6,9,1,11]
+ CRUSH rule 0 x 195 [5,7,10,12,1,3,15,9]
+ CRUSH rule 0 x 196 [4,15,1,10,9,2,13,7]
+ CRUSH rule 0 x 197 [14,10,13,4,6,3,1,9]
+ CRUSH rule 0 x 198 [2,5,6,15,9,13,10]
+ CRUSH rule 0 x 199 [2,10,4,15,1,9,6,12]
+ CRUSH rule 0 x 200 [7,14,11,4,1,3,13]
+ CRUSH rule 0 x 201 [9,14,1,7,4,3,10,13]
+ CRUSH rule 0 x 202 [14,11,7,3,5,1,12]
+ CRUSH rule 0 x 203 [12,5,7,15,1,2,10,9]
+ CRUSH rule 0 x 204 [6,11,3,12,14,1,9,4]
+ CRUSH rule 0 x 205 [15,4,6,10,13,9,2,1]
+ CRUSH rule 0 x 206 [13,11,2,15,7,1,5]
+ CRUSH rule 0 x 207 [2,11,7,4,14,1,12,9]
+ CRUSH rule 0 x 208 [13,1,6,14,9,11,3,5]
+ CRUSH rule 0 x 209 [6,15,13,1,11,4,9,2]
+ CRUSH rule 0 x 210 [13,11,2,7,5,14,9]
+ CRUSH rule 0 x 211 [2,14,1,13,11,7,9,5]
+ CRUSH rule 0 x 212 [10,1,12,15,5,6,2,9]
+ CRUSH rule 0 x 213 [3,9,6,5,15,13,1,11]
+ CRUSH rule 0 x 214 [7,15,4,1,10,2,13,9]
+ CRUSH rule 0 x 215 [6,1,4,13,3,11,14]
+ CRUSH rule 0 x 216 [12,9,6,2,1,11,5,15]
+ CRUSH rule 0 x 217 [12,11,1,14,2,4,7]
+ CRUSH rule 0 x 218 [12,10,15,6,1,4,9,2]
+ CRUSH rule 0 x 219 [3,11,14,6,4,1,13]
+ CRUSH rule 0 x 220 [14,4,3,12,10,9,6]
+ CRUSH rule 0 x 221 [15,5,2,6,12,11,9]
+ CRUSH rule 0 x 222 [10,4,3,15,7,12,1]
+ CRUSH rule 0 x 223 [9,7,11,1,4,14,13,3]
+ CRUSH rule 0 x 224 [1,7,10,2,12,9,14,5]
+ CRUSH rule 0 x 225 [10,5,2,6,1,13,9,15]
+ CRUSH rule 0 x 226 [4,1,9,3,13,10,15,7]
+ CRUSH rule 0 x 227 [7,2,12,15,5,11]
+ CRUSH rule 0 x 228 [2,15,11,1,6,13,9,4]
+ CRUSH rule 0 x 229 [9,3,7,14,1,12,4,10]
+ CRUSH rule 0 x 230 [10,5,7,2,15,1,13]
+ CRUSH rule 0 x 231 [2,7,5,13,9,15,10]
+ CRUSH rule 0 x 232 [10,5,13,1,9,2,7,14]
+ CRUSH rule 0 x 233 [6,12,11,4,9,14,1,3]
+ CRUSH rule 0 x 234 [10,1,2,12,5,9,15,7]
+ CRUSH rule 0 x 235 [13,14,7,10,1,9,5,3]
+ CRUSH rule 0 x 236 [2,15,9,12,1,7,4,10]
+ CRUSH rule 0 x 237 [3,12,9,10,4,7,15]
+ CRUSH rule 0 x 238 [2,10,4,15,6,12,9,1]
+ CRUSH rule 0 x 239 [4,15,10,7,9,13,3,1]
+ CRUSH rule 0 x 240 [15,5,13,7,2,9,10]
+ CRUSH rule 0 x 241 [7,9,15,12,1,5,2,10]
+ CRUSH rule 0 x 242 [14,2,6,9,10,12,5]
+ CRUSH rule 0 x 243 [2,11,5,1,15,6,9,13]
+ CRUSH rule 0 x 244 [13,9,15,3,11,7,5]
+ CRUSH rule 0 x 245 [12,9,15,3,1,5,10,7]
+ CRUSH rule 0 x 246 [15,3,5,11,7,1,12,9]
+ CRUSH rule 0 x 247 [6,4,9,12,1,2,10,14]
+ CRUSH rule 0 x 248 [5,13,7,11,9,15,3,1]
+ CRUSH rule 0 x 249 [10,14,7,3,9,13,1,4]
+ CRUSH rule 0 x 250 [12,15,1,10,5,6,3,9]
+ CRUSH rule 0 x 251 [13,2,15,5,6,1,9,10]
+ CRUSH rule 0 x 252 [7,5,13,9,3,10,14,1]
+ CRUSH rule 0 x 253 [3,13,15,10,7,4]
+ CRUSH rule 0 x 254 [2,9,13,14,4,6,10]
+ CRUSH rule 0 x 255 [1,9,13,2,6,10,4,15]
+ CRUSH rule 0 x 256 [6,9,13,1,3,14,5,10]
+ CRUSH rule 0 x 257 [15,12,3,9,6,4,11]
+ CRUSH rule 0 x 258 [12,5,6,10,2,1,14,9]
+ CRUSH rule 0 x 259 [9,10,4,3,14,13,1,7]
+ CRUSH rule 0 x 260 [10,12,6,9,3,15,1,4]
+ CRUSH rule 0 x 261 [13,7,2,1,15,5,11,9]
+ CRUSH rule 0 x 262 [15,3,12,7,4,9,1,11]
+ CRUSH rule 0 x 263 [12,6,10,9,5,15,3,1]
+ CRUSH rule 0 x 264 [13,14,11,3,1,4,7,9]
+ CRUSH rule 0 x 265 [12,10,14,5,7,1,9,3]
+ CRUSH rule 0 x 266 [14,7,11,1,2,9,4,12]
+ CRUSH rule 0 x 267 [12,11,6,5,1,2,15]
+ CRUSH rule 0 x 268 [4,1,15,12,6,11,3,9]
+ CRUSH rule 0 x 269 [11,1,15,5,13,9,7,2]
+ CRUSH rule 0 x 270 [7,11,12,3,1,14,9,4]
+ CRUSH rule 0 x 271 [4,7,3,13,15,10,9,1]
+ CRUSH rule 0 x 272 [15,5,13,10,6,2]
+ CRUSH rule 0 x 273 [2,10,7,12,1,15,5]
+ CRUSH rule 0 x 274 [10,2,5,6,13,9,15,1]
+ CRUSH rule 0 x 275 [10,3,4,7,14,13]
+ CRUSH rule 0 x 276 [5,12,9,2,11,7,15,1]
+ CRUSH rule 0 x 277 [14,3,13,4,1,9,11,7]
+ CRUSH rule 0 x 278 [5,6,14,3,1,11,13,9]
+ CRUSH rule 0 x 279 [6,10,13,3,9,4,15]
+ CRUSH rule 0 x 280 [7,3,14,9,1,11,4,13]
+ CRUSH rule 0 x 281 [5,11,14,7,9,13,2,1]
+ CRUSH rule 0 x 282 [2,1,13,14,9,7,5,10]
+ CRUSH rule 0 x 283 [4,1,12,3,10,7,15]
+ CRUSH rule 0 x 284 [5,11,7,15,3,13,1,9]
+ CRUSH rule 0 x 285 [15,5,3,1,6,13,11,9]
+ CRUSH rule 0 x 286 [10,4,3,6,12,15,1]
+ CRUSH rule 0 x 287 [12,4,9,1,3,11,15,7]
+ CRUSH rule 0 x 288 [4,12,10,7,1,3,14,9]
+ CRUSH rule 0 x 289 [2,5,14,9,13,6,10]
+ CRUSH rule 0 x 290 [12,2,5,6,15,9,1,10]
+ CRUSH rule 0 x 291 [7,11,1,14,5,9,2,12]
+ CRUSH rule 0 x 292 [4,10,6,3,14,9,12,1]
+ CRUSH rule 0 x 293 [6,5,11,1,2,14,12]
+ CRUSH rule 0 x 294 [9,12,3,14,6,11,5,1]
+ CRUSH rule 0 x 295 [6,10,3,14,9,4,13]
+ CRUSH rule 0 x 296 [3,1,13,7,14,9,10,4]
+ CRUSH rule 0 x 297 [6,13,4,14,10,1,2,9]
+ CRUSH rule 0 x 298 [14,9,13,1,4,2,7,10]
+ CRUSH rule 0 x 299 [14,12,11,6,4,2,1,9]
+ CRUSH rule 0 x 300 [15,7,10,5,1,3,13,9]
+ CRUSH rule 0 x 301 [9,11,7,1,13,14,4,2]
+ CRUSH rule 0 x 302 [9,7,1,13,5,10,3,15]
+ CRUSH rule 0 x 303 [4,13,3,7,10,15,1]
+ CRUSH rule 0 x 304 [6,9,2,11,15,13,4]
+ CRUSH rule 0 x 305 [13,7,5,11,2,15,9]
+ CRUSH rule 0 x 306 [10,12,4,6,9,2,15,1]
+ CRUSH rule 0 x 307 [11,12,15,5,6,2,1,9]
+ CRUSH rule 0 x 308 [12,14,10,9,1,2,5,7]
+ CRUSH rule 0 x 309 [9,3,12,5,11,15,7]
+ CRUSH rule 0 x 310 [3,1,5,10,14,9,7,12]
+ CRUSH rule 0 x 311 [3,9,7,1,14,13,10,5]
+ CRUSH rule 0 x 312 [15,13,9,7,5,10,2]
+ CRUSH rule 0 x 313 [9,15,3,7,5,13,1,11]
+ CRUSH rule 0 x 314 [2,15,9,5,6,12,1,11]
+ CRUSH rule 0 x 315 [15,2,13,1,11,9,6,4]
+ CRUSH rule 0 x 316 [4,9,11,2,12,14,6]
+ CRUSH rule 0 x 317 [1,5,3,13,15,7,10]
+ CRUSH rule 0 x 318 [4,1,15,11,9,13,6,2]
+ CRUSH rule 0 x 319 [2,15,4,1,11,9,7,12]
+ CRUSH rule 0 x 320 [5,7,13,9,11,2,1,15]
+ CRUSH rule 0 x 321 [1,6,11,15,5,3,13]
+ CRUSH rule 0 x 322 [13,7,5,3,14,11,1]
+ CRUSH rule 0 x 323 [7,4,10,1,2,13,14]
+ CRUSH rule 0 x 324 [5,6,10,15,2,13]
+ CRUSH rule 0 x 325 [9,10,14,5,1,6,2,13]
+ CRUSH rule 0 x 326 [11,7,13,4,2,15,1]
+ CRUSH rule 0 x 327 [12,5,10,14,3,7,9]
+ CRUSH rule 0 x 328 [5,2,6,14,1,11,12]
+ CRUSH rule 0 x 329 [2,6,15,5,9,10,13,1]
+ CRUSH rule 0 x 330 [3,9,11,13,1,6,5,14]
+ CRUSH rule 0 x 331 [12,14,6,3,1,4,10,9]
+ CRUSH rule 0 x 332 [10,12,6,15,9,2,5]
+ CRUSH rule 0 x 333 [6,5,3,12,14,10,9,1]
+ CRUSH rule 0 x 334 [4,9,2,12,7,11,15,1]
+ CRUSH rule 0 x 335 [11,7,1,5,13,2,9,15]
+ CRUSH rule 0 x 336 [6,14,13,2,5,9,11]
+ CRUSH rule 0 x 337 [15,11,3,7,12,5]
+ CRUSH rule 0 x 338 [10,5,3,6,15,1,9,13]
+ CRUSH rule 0 x 339 [11,14,13,5,3,7,1]
+ CRUSH rule 0 x 340 [11,6,12,4,9,3,14,1]
+ CRUSH rule 0 x 341 [7,5,2,10,14,9,1,12]
+ CRUSH rule 0 x 342 [12,14,1,9,2,11,4,7]
+ CRUSH rule 0 x 343 [12,14,9,6,10,2,4,1]
+ CRUSH rule 0 x 344 [9,11,5,2,14,13,1,7]
+ CRUSH rule 0 x 345 [14,2,11,9,6,12,4]
+ CRUSH rule 0 x 346 [5,3,14,10,7,1,13]
+ CRUSH rule 0 x 347 [10,2,12,6,9,1,14,5]
+ CRUSH rule 0 x 348 [7,9,10,1,14,13,3,4]
+ CRUSH rule 0 x 349 [9,6,10,12,1,5,14]
+ CRUSH rule 0 x 350 [13,9,15,4,10,7,2,1]
+ CRUSH rule 0 x 351 [13,5,15,3,1,6,11]
+ CRUSH rule 0 x 352 [1,12,11,9,4,7,3,15]
+ CRUSH rule 0 x 353 [10,14,12,2,9,1,4,6]
+ CRUSH rule 0 x 354 [6,3,15,10,9,4,13]
+ CRUSH rule 0 x 355 [13,14,6,10,2,5,1,9]
+ CRUSH rule 0 x 356 [15,13,2,9,6,5,1,11]
+ CRUSH rule 0 x 357 [4,11,1,13,3,14,6,9]
+ CRUSH rule 0 x 358 [12,7,2,9,1,14,10,4]
+ CRUSH rule 0 x 359 [5,15,7,11,3,13]
+ CRUSH rule 0 x 360 [13,10,1,2,6,14,5]
+ CRUSH rule 0 x 361 [5,3,13,6,1,14,11,9]
+ CRUSH rule 0 x 362 [2,9,11,13,1,6,5,15]
+ CRUSH rule 0 x 363 [7,12,3,9,15,4,1,10]
+ CRUSH rule 0 x 364 [2,12,6,9,5,10,15]
+ CRUSH rule 0 x 365 [13,5,11,15,6,2,9]
+ CRUSH rule 0 x 366 [12,7,3,14,5,10,9]
+ CRUSH rule 0 x 367 [7,13,3,1,5,11,15,9]
+ CRUSH rule 0 x 368 [7,9,10,15,3,4,13]
+ CRUSH rule 0 x 369 [7,5,3,13,14,9,11,1]
+ CRUSH rule 0 x 370 [4,7,14,1,2,9,11,12]
+ CRUSH rule 0 x 371 [1,7,12,3,4,15,10,9]
+ CRUSH rule 0 x 372 [10,4,3,14,6,1,12,9]
+ CRUSH rule 0 x 373 [15,5,2,6,13,1,9,10]
+ CRUSH rule 0 x 374 [3,15,12,5,1,6,10,9]
+ CRUSH rule 0 x 375 [5,2,14,1,6,13,11,9]
+ CRUSH rule 0 x 376 [5,14,10,13,3,6,1]
+ CRUSH rule 0 x 377 [1,15,2,4,9,11,12,6]
+ CRUSH rule 0 x 378 [9,12,2,15,1,5,11,6]
+ CRUSH rule 0 x 379 [11,2,15,5,7,9,13,1]
+ CRUSH rule 0 x 380 [6,1,12,11,2,9,5,14]
+ CRUSH rule 0 x 381 [15,13,7,5,10,2,1,9]
+ CRUSH rule 0 x 382 [14,3,1,4,13,7,10]
+ CRUSH rule 0 x 383 [3,6,11,4,13,15,1]
+ CRUSH rule 0 x 384 [4,13,6,3,15,11,9]
+ CRUSH rule 0 x 385 [4,6,15,3,10,9,1,13]
+ CRUSH rule 0 x 386 [14,3,11,13,5,6,9,1]
+ CRUSH rule 0 x 387 [1,11,5,7,9,2,14,12]
+ CRUSH rule 0 x 388 [2,6,11,9,15,4,12]
+ CRUSH rule 0 x 389 [12,7,2,4,15,10,1]
+ CRUSH rule 0 x 390 [2,11,13,7,5,9,15]
+ CRUSH rule 0 x 391 [3,4,9,13,7,10,1,14]
+ CRUSH rule 0 x 392 [11,5,14,7,1,9,2,12]
+ CRUSH rule 0 x 393 [2,14,5,9,7,13,11]
+ CRUSH rule 0 x 394 [4,9,3,15,13,6,1,11]
+ CRUSH rule 0 x 395 [10,13,5,15,6,9,3,1]
+ CRUSH rule 0 x 396 [2,12,15,9,4,6,11]
+ CRUSH rule 0 x 397 [1,14,9,4,12,10,3,7]
+ CRUSH rule 0 x 398 [9,2,1,5,12,6,11,15]
+ CRUSH rule 0 x 399 [5,9,14,3,1,10,13,7]
+ CRUSH rule 0 x 400 [10,6,2,4,15,12,1,9]
+ CRUSH rule 0 x 401 [6,9,11,12,4,3,15,1]
+ CRUSH rule 0 x 402 [4,7,9,2,13,1,15,11]
+ CRUSH rule 0 x 403 [7,15,13,3,5,9,10]
+ CRUSH rule 0 x 404 [14,12,7,9,2,1,5,11]
+ CRUSH rule 0 x 405 [9,15,11,2,4,7,13,1]
+ CRUSH rule 0 x 406 [12,14,9,2,7,10,4,1]
+ CRUSH rule 0 x 407 [9,5,12,10,15,6,3]
+ CRUSH rule 0 x 408 [7,1,5,2,10,15,13,9]
+ CRUSH rule 0 x 409 [11,2,4,13,1,15,7,9]
+ CRUSH rule 0 x 410 [6,4,14,2,12,9,10,1]
+ CRUSH rule 0 x 411 [13,11,15,6,4,1,9,2]
+ CRUSH rule 0 x 412 [5,9,6,11,14,2,12]
+ CRUSH rule 0 x 413 [13,5,3,11,6,9,1,14]
+ CRUSH rule 0 x 414 [3,11,9,13,4,1,6,15]
+ CRUSH rule 0 x 415 [6,10,14,5,1,13,3,9]
+ CRUSH rule 0 x 416 [13,1,4,7,2,9,14,11]
+ CRUSH rule 0 x 417 [4,12,1,15,2,11,9,6]
+ CRUSH rule 0 x 418 [14,5,10,2,6,9,13]
+ CRUSH rule 0 x 419 [5,14,10,9,2,12,6,1]
+ CRUSH rule 0 x 420 [2,4,9,11,6,14,13,1]
+ CRUSH rule 0 x 421 [15,4,10,3,9,12,7]
+ CRUSH rule 0 x 422 [4,11,2,7,13,9,15]
+ CRUSH rule 0 x 423 [3,15,12,6,5,1,9,10]
+ CRUSH rule 0 x 424 [6,10,12,2,5,1,14,9]
+ CRUSH rule 0 x 425 [11,15,2,13,5,7,9,1]
+ CRUSH rule 0 x 426 [12,4,7,1,9,10,14,2]
+ CRUSH rule 0 x 427 [14,10,3,1,9,7,5,13]
+ CRUSH rule 0 x 428 [12,7,9,4,2,1,14,10]
+ CRUSH rule 0 x 429 [3,4,9,7,11,12,1,14]
+ CRUSH rule 0 x 430 [3,5,10,13,1,15,6]
+ CRUSH rule 0 x 431 [9,3,7,1,12,5,14,11]
+ CRUSH rule 0 x 432 [4,1,12,7,15,2,10,9]
+ CRUSH rule 0 x 433 [4,11,12,15,7,3]
+ CRUSH rule 0 x 434 [2,14,9,1,5,11,7,13]
+ CRUSH rule 0 x 435 [13,11,5,6,9,2,15]
+ CRUSH rule 0 x 436 [9,15,10,2,4,1,12,7]
+ CRUSH rule 0 x 437 [9,6,3,14,10,12,5,1]
+ CRUSH rule 0 x 438 [7,2,13,4,11,1,9,14]
+ CRUSH rule 0 x 439 [7,14,4,3,12,10]
+ CRUSH rule 0 x 440 [14,11,9,2,7,12,1,5]
+ CRUSH rule 0 x 441 [2,4,11,9,13,6,1,14]
+ CRUSH rule 0 x 442 [10,13,9,7,15,1,4,2]
+ CRUSH rule 0 x 443 [12,15,10,9,2,1,6,4]
+ CRUSH rule 0 x 444 [4,13,7,14,3,1,9,11]
+ CRUSH rule 0 x 445 [4,2,15,7,1,9,11,12]
+ CRUSH rule 0 x 446 [12,10,6,9,4,1,15,3]
+ CRUSH rule 0 x 447 [15,7,13,1,4,9,3,11]
+ CRUSH rule 0 x 448 [5,2,13,7,15,10]
+ CRUSH rule 0 x 449 [14,5,3,12,10,9,6]
+ CRUSH rule 0 x 450 [2,4,6,9,15,1,13,10]
+ CRUSH rule 0 x 451 [6,14,11,3,9,1,12,5]
+ CRUSH rule 0 x 452 [14,9,10,4,2,13,7]
+ CRUSH rule 0 x 453 [5,15,13,2,6,9,11]
+ CRUSH rule 0 x 454 [10,4,2,6,15,12,9,1]
+ CRUSH rule 0 x 455 [6,13,2,4,10,1,15]
+ CRUSH rule 0 x 456 [5,7,13,1,11,3,9,15]
+ CRUSH rule 0 x 457 [9,1,5,7,11,13,15,2]
+ CRUSH rule 0 x 458 [9,11,15,4,7,2,12,1]
+ CRUSH rule 0 x 459 [13,15,11,1,5,2,6]
+ CRUSH rule 0 x 460 [5,12,10,15,7,3,9]
+ CRUSH rule 0 x 461 [4,3,9,13,15,6,10]
+ CRUSH rule 0 x 462 [4,7,12,14,11,1,3,9]
+ CRUSH rule 0 x 463 [4,12,14,11,2,7,1,9]
+ CRUSH rule 0 x 464 [4,2,15,10,1,9,13,7]
+ CRUSH rule 0 x 465 [5,10,9,7,13,1,3,14]
+ CRUSH rule 0 x 466 [13,5,2,15,9,11,6,1]
+ CRUSH rule 0 x 467 [13,6,14,3,9,1,11,5]
+ CRUSH rule 0 x 468 [10,7,12,14,4,1,9,2]
+ CRUSH rule 0 x 469 [4,9,6,14,12,11,3,1]
+ CRUSH rule 0 x 470 [3,9,12,15,5,6,10]
+ CRUSH rule 0 x 471 [6,1,5,14,13,10,9,3]
+ CRUSH rule 0 x 472 [2,14,7,5,13,1,11,9]
+ CRUSH rule 0 x 473 [15,10,6,9,4,12,2]
+ CRUSH rule 0 x 474 [15,10,4,12,6,9,2,1]
+ CRUSH rule 0 x 475 [10,5,12,9,14,3,6,1]
+ CRUSH rule 0 x 476 [3,6,10,12,1,15,9,4]
+ CRUSH rule 0 x 477 [6,13,5,15,11,9,2,1]
+ CRUSH rule 0 x 478 [4,15,1,3,7,12,10,9]
+ CRUSH rule 0 x 479 [13,11,1,6,14,5,9,3]
+ CRUSH rule 0 x 480 [1,13,6,4,9,14,11,3]
+ CRUSH rule 0 x 481 [15,12,7,9,1,3,10,4]
+ CRUSH rule 0 x 482 [2,12,9,1,7,11,14,4]
+ CRUSH rule 0 x 483 [10,1,4,15,9,7,13,2]
+ CRUSH rule 0 x 484 [1,4,10,13,7,14,2,9]
+ CRUSH rule 0 x 485 [9,4,3,1,14,12,7,10]
+ CRUSH rule 0 x 486 [3,10,15,9,7,13,4,1]
+ CRUSH rule 0 x 487 [12,11,4,14,7,2,1]
+ CRUSH rule 0 x 488 [14,4,1,9,2,6,10,12]
+ CRUSH rule 0 x 489 [11,4,2,13,15,7]
+ CRUSH rule 0 x 490 [4,9,1,3,13,15,6,11]
+ CRUSH rule 0 x 491 [1,12,5,2,14,11,6]
+ CRUSH rule 0 x 492 [5,7,11,3,14,9,1,13]
+ CRUSH rule 0 x 493 [12,1,4,15,3,11,9,6]
+ CRUSH rule 0 x 494 [1,7,13,4,15,9,10,3]
+ CRUSH rule 0 x 495 [3,15,7,1,9,5,12,11]
+ CRUSH rule 0 x 496 [5,3,7,13,9,14,10]
+ CRUSH rule 0 x 497 [13,10,3,6,5,14,1]
+ CRUSH rule 0 x 498 [10,6,1,5,9,12,3,15]
+ CRUSH rule 0 x 499 [14,3,12,5,1,11,9,7]
+ CRUSH rule 0 x 500 [15,9,6,12,11,2,1,5]
+ CRUSH rule 0 x 501 [10,13,1,9,3,14,5,7]
+ CRUSH rule 0 x 502 [5,1,14,11,7,12,9,2]
+ CRUSH rule 0 x 503 [15,10,7,9,1,12,4,2]
+ CRUSH rule 0 x 504 [13,2,7,1,14,11,5]
+ CRUSH rule 0 x 505 [12,7,5,2,14,10,9]
+ CRUSH rule 0 x 506 [11,7,9,14,12,1,2,5]
+ CRUSH rule 0 x 507 [4,14,13,3,9,7,1,10]
+ CRUSH rule 0 x 508 [12,1,4,9,2,11,15,7]
+ CRUSH rule 0 x 509 [4,2,6,9,14,1,10,13]
+ CRUSH rule 0 x 510 [5,3,1,12,11,14,9,7]
+ CRUSH rule 0 x 511 [2,12,10,6,14,5]
+ CRUSH rule 0 x 512 [15,11,3,5,7,1,13]
+ CRUSH rule 0 x 513 [4,9,11,3,13,7,1,14]
+ CRUSH rule 0 x 514 [11,9,3,4,12,15,6,1]
+ CRUSH rule 0 x 515 [12,14,6,5,3,9,1,10]
+ CRUSH rule 0 x 516 [14,11,1,12,3,7,4,9]
+ CRUSH rule 0 x 517 [11,5,6,13,9,3,14]
+ CRUSH rule 0 x 518 [3,5,7,12,15,11,9,1]
+ CRUSH rule 0 x 519 [12,14,2,1,4,6,9,10]
+ CRUSH rule 0 x 520 [12,4,2,10,6,15,9]
+ CRUSH rule 0 x 521 [11,5,9,6,15,3,13]
+ CRUSH rule 0 x 522 [4,12,11,1,15,3,9,6]
+ CRUSH rule 0 x 523 [3,1,5,9,15,10,13,7]
+ CRUSH rule 0 x 524 [15,9,3,11,13,7,4,1]
+ CRUSH rule 0 x 525 [3,15,11,6,9,12,4]
+ CRUSH rule 0 x 526 [10,2,5,13,6,15,1,9]
+ CRUSH rule 0 x 527 [3,13,4,1,9,10,14,7]
+ CRUSH rule 0 x 528 [12,7,15,10,2,5,9]
+ CRUSH rule 0 x 529 [6,4,10,12,2,9,14]
+ CRUSH rule 0 x 530 [11,9,12,7,5,1,3,15]
+ CRUSH rule 0 x 531 [9,15,4,7,2,13,1,11]
+ CRUSH rule 0 x 532 [5,3,13,7,9,14,1,10]
+ CRUSH rule 0 x 533 [12,15,1,2,7,5,10]
+ CRUSH rule 0 x 534 [11,9,3,7,15,4,1,12]
+ CRUSH rule 0 x 535 [11,1,3,5,14,9,12,7]
+ CRUSH rule 0 x 536 [9,1,14,13,4,6,2,11]
+ CRUSH rule 0 x 537 [15,5,13,2,7,11]
+ CRUSH rule 0 x 538 [13,5,11,2,6,15,9]
+ CRUSH rule 0 x 539 [10,12,6,14,1,2,9,5]
+ CRUSH rule 0 x 540 [12,15,7,3,9,11,1,4]
+ CRUSH rule 0 x 541 [2,1,6,11,14,13,4]
+ CRUSH rule 0 x 542 [3,9,15,5,11,12,7,1]
+ CRUSH rule 0 x 543 [4,10,9,3,6,13,14]
+ CRUSH rule 0 x 544 [3,15,9,11,7,4,12,1]
+ CRUSH rule 0 x 545 [14,10,7,12,4,9,1,3]
+ CRUSH rule 0 x 546 [5,15,13,7,1,10,9,2]
+ CRUSH rule 0 x 547 [5,13,7,9,3,14,10]
+ CRUSH rule 0 x 548 [11,7,12,15,4,2]
+ CRUSH rule 0 x 549 [14,1,4,9,13,6,3,10]
+ CRUSH rule 0 x 550 [9,15,3,13,1,6,4,11]
+ CRUSH rule 0 x 551 [11,2,15,6,13,5,1]
+ CRUSH rule 0 x 552 [2,11,14,1,9,6,5,12]
+ CRUSH rule 0 x 553 [11,9,14,6,4,13,3]
+ CRUSH rule 0 x 554 [11,14,6,4,13,9,3,1]
+ CRUSH rule 0 x 555 [6,5,10,9,14,2,13,1]
+ CRUSH rule 0 x 556 [15,6,3,13,11,4,1,9]
+ CRUSH rule 0 x 557 [12,2,5,14,10,9,6]
+ CRUSH rule 0 x 558 [12,1,6,15,5,10,3]
+ CRUSH rule 0 x 559 [2,13,5,10,14,7,1]
+ CRUSH rule 0 x 560 [4,9,12,6,3,10,1,15]
+ CRUSH rule 0 x 561 [12,7,1,2,5,15,11,9]
+ CRUSH rule 0 x 562 [7,13,9,14,2,1,11,4]
+ CRUSH rule 0 x 563 [15,4,3,10,13,9,7]
+ CRUSH rule 0 x 564 [2,13,7,1,15,10,4]
+ CRUSH rule 0 x 565 [3,12,4,1,14,7,11]
+ CRUSH rule 0 x 566 [6,14,4,2,13,11]
+ CRUSH rule 0 x 567 [15,4,11,6,3,12]
+ CRUSH rule 0 x 568 [4,14,1,6,10,13,3,9]
+ CRUSH rule 0 x 569 [11,3,15,13,5,1,9,7]
+ CRUSH rule 0 x 570 [1,10,13,4,7,2,9,14]
+ CRUSH rule 0 x 571 [10,12,14,9,4,2,1,6]
+ CRUSH rule 0 x 572 [12,14,3,10,6,1,4,9]
+ CRUSH rule 0 x 573 [7,15,11,2,12,9,4,1]
+ CRUSH rule 0 x 574 [11,14,13,1,3,7,4,9]
+ CRUSH rule 0 x 575 [5,13,15,9,6,10,2]
+ CRUSH rule 0 x 576 [3,15,11,9,1,6,5,13]
+ CRUSH rule 0 x 577 [13,9,6,15,3,11,4,1]
+ CRUSH rule 0 x 578 [4,10,1,2,7,13,14,9]
+ CRUSH rule 0 x 579 [13,1,15,2,10,7,5,9]
+ CRUSH rule 0 x 580 [3,12,4,1,10,15,7,9]
+ CRUSH rule 0 x 581 [7,14,12,10,1,2,9,5]
+ CRUSH rule 0 x 582 [10,5,13,14,1,2,7]
+ CRUSH rule 0 x 583 [4,15,1,9,10,12,2,6]
+ CRUSH rule 0 x 584 [10,1,5,13,6,9,2,15]
+ CRUSH rule 0 x 585 [5,3,6,1,11,14,13,9]
+ CRUSH rule 0 x 586 [7,10,14,12,9,3,5,1]
+ CRUSH rule 0 x 587 [11,6,9,4,1,14,13,2]
+ CRUSH rule 0 x 588 [3,12,7,15,4,9,1,10]
+ CRUSH rule 0 x 589 [9,7,12,1,10,3,4,15]
+ CRUSH rule 0 x 590 [12,1,3,9,10,6,4,14]
+ CRUSH rule 0 x 591 [2,6,14,13,9,11,4]
+ CRUSH rule 0 x 592 [15,12,9,7,5,2,11,1]
+ CRUSH rule 0 x 593 [13,14,5,11,9,6,2]
+ CRUSH rule 0 x 594 [12,14,2,9,7,4,1,11]
+ CRUSH rule 0 x 595 [12,7,10,3,1,14,9,4]
+ CRUSH rule 0 x 596 [2,7,12,11,1,5,15,9]
+ CRUSH rule 0 x 597 [15,1,2,10,7,13,5,9]
+ CRUSH rule 0 x 598 [11,5,9,14,12,7,3]
+ CRUSH rule 0 x 599 [13,11,1,5,6,2,15,9]
+ CRUSH rule 0 x 600 [4,12,3,10,9,7,1,14]
+ CRUSH rule 0 x 601 [13,5,15,2,1,7,9,10]
+ CRUSH rule 0 x 602 [3,11,7,1,13,15,5,9]
+ CRUSH rule 0 x 603 [3,1,4,14,10,9,6,12]
+ CRUSH rule 0 x 604 [14,2,6,1,11,13,9,4]
+ CRUSH rule 0 x 605 [2,7,12,5,14,10,1,9]
+ CRUSH rule 0 x 606 [12,15,1,5,7,9,3,11]
+ CRUSH rule 0 x 607 [3,9,10,14,7,1,4,12]
+ CRUSH rule 0 x 608 [13,10,1,7,9,15,5,2]
+ CRUSH rule 0 x 609 [14,3,7,9,11,12,5]
+ CRUSH rule 0 x 610 [7,10,5,1,12,2,15]
+ CRUSH rule 0 x 611 [13,1,5,3,10,7,15,9]
+ CRUSH rule 0 x 612 [7,1,2,13,9,15,4,11]
+ CRUSH rule 0 x 613 [10,7,14,9,5,2,13]
+ CRUSH rule 0 x 614 [9,4,15,3,1,11,6,12]
+ CRUSH rule 0 x 615 [9,4,11,2,1,12,6,15]
+ CRUSH rule 0 x 616 [10,14,1,5,3,6,12,9]
+ CRUSH rule 0 x 617 [15,7,2,11,12,1,9,4]
+ CRUSH rule 0 x 618 [4,2,10,6,14,9,1,12]
+ CRUSH rule 0 x 619 [15,4,3,9,6,1,13,11]
+ CRUSH rule 0 x 620 [3,7,11,14,13,1,5]
+ CRUSH rule 0 x 621 [3,6,4,14,1,11,13]
+ CRUSH rule 0 x 622 [10,2,13,5,15,9,1,7]
+ CRUSH rule 0 x 623 [4,9,14,7,3,13,11]
+ CRUSH rule 0 x 624 [3,9,15,6,10,1,5,12]
+ CRUSH rule 0 x 625 [11,7,3,5,13,15,9]
+ CRUSH rule 0 x 626 [10,12,2,1,9,7,5,14]
+ CRUSH rule 0 x 627 [1,12,10,14,3,5,9,7]
+ CRUSH rule 0 x 628 [15,13,11,4,2,1,7,9]
+ CRUSH rule 0 x 629 [5,6,15,12,1,10,3,9]
+ CRUSH rule 0 x 630 [1,4,12,9,3,7,15,11]
+ CRUSH rule 0 x 631 [5,7,1,15,12,11,3,9]
+ CRUSH rule 0 x 632 [12,3,11,9,6,1,15,5]
+ CRUSH rule 0 x 633 [14,4,3,7,10,12,9]
+ CRUSH rule 0 x 634 [6,9,5,3,13,11,14]
+ CRUSH rule 0 x 635 [6,5,2,15,9,12,11]
+ CRUSH rule 0 x 636 [13,6,11,3,15,9,1,4]
+ CRUSH rule 0 x 637 [3,1,10,6,9,12,4,14]
+ CRUSH rule 0 x 638 [10,15,3,5,13,1,7]
+ CRUSH rule 0 x 639 [6,9,14,4,3,1,10,13]
+ CRUSH rule 0 x 640 [9,6,1,11,14,2,4,13]
+ CRUSH rule 0 x 641 [10,6,5,14,1,9,12,2]
+ CRUSH rule 0 x 642 [1,15,4,6,2,10,9,12]
+ CRUSH rule 0 x 643 [3,7,5,1,10,15,13]
+ CRUSH rule 0 x 644 [15,13,6,9,3,11,5]
+ CRUSH rule 0 x 645 [14,2,4,9,10,1,7,13]
+ CRUSH rule 0 x 646 [5,13,14,1,6,9,2,11]
+ CRUSH rule 0 x 647 [10,1,9,13,6,2,14,5]
+ CRUSH rule 0 x 648 [6,5,2,14,11,1,12,9]
+ CRUSH rule 0 x 649 [3,9,13,11,4,14,1,7]
+ CRUSH rule 0 x 650 [10,9,4,15,12,7,1,2]
+ CRUSH rule 0 x 651 [3,9,5,7,14,1,13,11]
+ CRUSH rule 0 x 652 [15,9,4,6,13,1,2,11]
+ CRUSH rule 0 x 653 [11,14,1,3,6,9,12,5]
+ CRUSH rule 0 x 654 [13,6,2,10,15,4,1,9]
+ CRUSH rule 0 x 655 [6,3,4,15,12,11,1]
+ CRUSH rule 0 x 656 [3,15,1,4,6,12,11]
+ CRUSH rule 0 x 657 [11,15,3,5,7,13,1,9]
+ CRUSH rule 0 x 658 [7,2,10,12,1,4,9,14]
+ CRUSH rule 0 x 659 [2,5,14,6,10,12]
+ CRUSH rule 0 x 660 [13,14,10,6,4,9,3]
+ CRUSH rule 0 x 661 [7,15,3,12,11,4,9,1]
+ CRUSH rule 0 x 662 [15,2,12,5,1,10,9,7]
+ CRUSH rule 0 x 663 [14,9,13,10,5,3,1,6]
+ CRUSH rule 0 x 664 [6,10,12,4,9,2,1,15]
+ CRUSH rule 0 x 665 [2,9,12,1,7,10,4,15]
+ CRUSH rule 0 x 666 [12,3,6,1,15,9,10,4]
+ CRUSH rule 0 x 667 [1,9,12,10,2,14,7,4]
+ CRUSH rule 0 x 668 [9,5,1,2,6,11,13,15]
+ CRUSH rule 0 x 669 [9,7,14,5,11,13,1,2]
+ CRUSH rule 0 x 670 [6,10,9,13,1,2,15,4]
+ CRUSH rule 0 x 671 [6,15,5,10,13,3]
+ CRUSH rule 0 x 672 [2,9,13,1,4,14,6,10]
+ CRUSH rule 0 x 673 [7,10,5,9,15,13,2,1]
+ CRUSH rule 0 x 674 [7,12,10,1,14,9,3,4]
+ CRUSH rule 0 x 675 [9,5,1,10,6,14,12,2]
+ CRUSH rule 0 x 676 [10,12,2,1,4,15,7]
+ CRUSH rule 0 x 677 [2,12,1,4,10,6,15,9]
+ CRUSH rule 0 x 678 [1,2,4,10,12,14,9,6]
+ CRUSH rule 0 x 679 [5,6,12,15,9,11,3]
+ CRUSH rule 0 x 680 [7,11,3,1,15,4,9,12]
+ CRUSH rule 0 x 681 [6,4,3,11,14,13,1,9]
+ CRUSH rule 0 x 682 [6,1,11,15,12,2,5,9]
+ CRUSH rule 0 x 683 [6,13,2,4,9,14,10,1]
+ CRUSH rule 0 x 684 [9,11,3,7,15,4,13]
+ CRUSH rule 0 x 685 [5,1,15,7,9,2,10,13]
+ CRUSH rule 0 x 686 [1,9,11,14,6,13,4,3]
+ CRUSH rule 0 x 687 [7,13,3,5,11,9,15,1]
+ CRUSH rule 0 x 688 [11,9,1,14,3,5,7,12]
+ CRUSH rule 0 x 689 [5,2,9,12,1,14,11,7]
+ CRUSH rule 0 x 690 [9,7,10,3,13,15,5,1]
+ CRUSH rule 0 x 691 [11,15,9,5,7,13,2]
+ CRUSH rule 0 x 692 [15,5,1,2,9,11,12,7]
+ CRUSH rule 0 x 693 [5,6,12,15,2,10,9,1]
+ CRUSH rule 0 x 694 [4,7,1,10,12,3,14]
+ CRUSH rule 0 x 695 [6,13,14,10,9,5,1,3]
+ CRUSH rule 0 x 696 [1,2,4,14,7,11,13]
+ CRUSH rule 0 x 697 [13,11,3,6,4,14,9,1]
+ CRUSH rule 0 x 698 [11,13,4,2,6,1,9,15]
+ CRUSH rule 0 x 699 [7,14,12,4,2,11]
+ CRUSH rule 0 x 700 [12,14,11,9,4,6,3,1]
+ CRUSH rule 0 x 701 [3,13,1,14,4,7,11]
+ CRUSH rule 0 x 702 [3,12,15,6,5,11,1,9]
+ CRUSH rule 0 x 703 [15,11,13,3,4,7,1,9]
+ CRUSH rule 0 x 704 [6,4,2,15,11,1,13,9]
+ CRUSH rule 0 x 705 [14,6,11,5,1,13,9,3]
+ CRUSH rule 0 x 706 [1,12,3,6,4,10,15,9]
+ CRUSH rule 0 x 707 [4,7,14,3,10,9,13]
+ CRUSH rule 0 x 708 [3,10,5,1,15,9,7,13]
+ CRUSH rule 0 x 709 [11,12,3,7,5,14,1,9]
+ CRUSH rule 0 x 710 [14,2,11,9,5,7,12,1]
+ CRUSH rule 0 x 711 [14,3,9,10,12,5,6,1]
+ CRUSH rule 0 x 712 [12,3,11,15,9,1,6,4]
+ CRUSH rule 0 x 713 [11,9,3,15,13,6,4,1]
+ CRUSH rule 0 x 714 [12,1,9,7,2,15,10,5]
+ CRUSH rule 0 x 715 [6,1,14,4,11,12,3,9]
+ CRUSH rule 0 x 716 [11,13,9,14,5,2,1,7]
+ CRUSH rule 0 x 717 [12,4,10,9,15,1,2,7]
+ CRUSH rule 0 x 718 [7,15,5,2,11,13]
+ CRUSH rule 0 x 719 [5,15,13,3,1,7,11]
+ CRUSH rule 0 x 720 [4,13,10,2,7,9,1,14]
+ CRUSH rule 0 x 721 [11,3,14,9,1,12,4,6]
+ CRUSH rule 0 x 722 [2,4,6,1,9,15,13,10]
+ CRUSH rule 0 x 723 [2,1,12,15,11,7,5,9]
+ CRUSH rule 0 x 724 [7,1,9,10,5,15,13,2]
+ CRUSH rule 0 x 725 [11,12,7,15,4,1,2]
+ CRUSH rule 0 x 726 [7,14,4,3,11,13,9,1]
+ CRUSH rule 0 x 727 [2,5,1,11,15,7,12]
+ CRUSH rule 0 x 728 [13,11,4,6,15,2]
+ CRUSH rule 0 x 729 [15,11,4,6,2,9,1,13]
+ CRUSH rule 0 x 730 [3,7,1,13,11,15,9,5]
+ CRUSH rule 0 x 731 [9,1,6,5,2,11,13,15]
+ CRUSH rule 0 x 732 [1,2,10,13,9,4,7,15]
+ CRUSH rule 0 x 733 [11,3,5,6,1,9,12,15]
+ CRUSH rule 0 x 734 [14,3,11,7,12,9,4,1]
+ CRUSH rule 0 x 735 [6,9,2,10,13,14,5]
+ CRUSH rule 0 x 736 [3,9,1,11,7,5,13,14]
+ CRUSH rule 0 x 737 [1,4,2,12,9,10,6,15]
+ CRUSH rule 0 x 738 [11,15,7,4,9,2,12]
+ CRUSH rule 0 x 739 [11,12,6,2,4,1,14]
+ CRUSH rule 0 x 740 [7,9,10,13,1,15,2,5]
+ CRUSH rule 0 x 741 [12,11,7,15,2,5]
+ CRUSH rule 0 x 742 [9,7,4,11,12,1,14,3]
+ CRUSH rule 0 x 743 [5,13,9,15,10,7,3]
+ CRUSH rule 0 x 744 [6,2,13,1,14,11,4]
+ CRUSH rule 0 x 745 [3,6,1,4,11,12,14,9]
+ CRUSH rule 0 x 746 [3,7,9,10,14,5,1,13]
+ CRUSH rule 0 x 747 [15,11,5,2,13,9,1,7]
+ CRUSH rule 0 x 748 [6,10,13,2,14,5,9,1]
+ CRUSH rule 0 x 749 [14,9,10,7,5,1,2,12]
+ CRUSH rule 0 x 750 [1,14,6,5,11,2,13]
+ CRUSH rule 0 x 751 [15,1,6,9,5,11,12,3]
+ CRUSH rule 0 x 752 [13,1,7,3,11,15,9,4]
+ CRUSH rule 0 x 753 [4,11,1,3,15,7,13]
+ CRUSH rule 0 x 754 [14,12,11,4,2,1,9,6]
+ CRUSH rule 0 x 755 [13,6,1,10,4,2,14,9]
+ CRUSH rule 0 x 756 [3,4,14,6,1,10,13,9]
+ CRUSH rule 0 x 757 [10,6,1,4,13,15,2]
+ CRUSH rule 0 x 758 [6,3,4,10,15,13,9,1]
+ CRUSH rule 0 x 759 [5,7,3,14,11,1,9,13]
+ CRUSH rule 0 x 760 [1,15,10,12,4,3,9,7]
+ CRUSH rule 0 x 761 [2,12,1,14,5,7,10]
+ CRUSH rule 0 x 762 [1,4,10,9,3,7,14,12]
+ CRUSH rule 0 x 763 [4,13,1,14,7,10,2,9]
+ CRUSH rule 0 x 764 [1,14,6,13,9,5,2,10]
+ CRUSH rule 0 x 765 [9,15,2,13,4,1,11,7]
+ CRUSH rule 0 x 766 [11,2,7,15,9,12,4]
+ CRUSH rule 0 x 767 [6,11,4,3,12,14]
+ CRUSH rule 0 x 768 [2,12,15,7,1,11,9,4]
+ CRUSH rule 0 x 769 [15,1,9,2,11,12,7,4]
+ CRUSH rule 0 x 770 [15,13,4,6,3,10,1,9]
+ CRUSH rule 0 x 771 [9,2,12,11,6,14,5,1]
+ CRUSH rule 0 x 772 [4,3,13,11,14,1,7]
+ CRUSH rule 0 x 773 [3,7,4,15,1,12,11,9]
+ CRUSH rule 0 x 774 [12,6,3,15,5,9,10,1]
+ CRUSH rule 0 x 775 [5,10,14,2,6,1,13]
+ CRUSH rule 0 x 776 [10,15,3,9,6,13,1,5]
+ CRUSH rule 0 x 777 [11,13,4,7,1,14,9,2]
+ CRUSH rule 0 x 778 [13,1,9,11,15,6,3,5]
+ CRUSH rule 0 x 779 [5,11,1,14,2,9,13,6]
+ CRUSH rule 0 x 780 [13,9,3,6,4,1,14,10]
+ CRUSH rule 0 x 781 [5,7,14,3,1,12,11,9]
+ CRUSH rule 0 x 782 [2,15,9,7,11,13,4,1]
+ CRUSH rule 0 x 783 [12,7,5,14,9,1,2,10]
+ CRUSH rule 0 x 784 [14,1,10,13,3,4,7,9]
+ CRUSH rule 0 x 785 [6,12,1,2,4,9,15,10]
+ CRUSH rule 0 x 786 [10,5,2,15,1,7,12,9]
+ CRUSH rule 0 x 787 [1,12,10,2,9,4,14,6]
+ CRUSH rule 0 x 788 [4,2,9,13,6,15,11]
+ CRUSH rule 0 x 789 [9,2,14,7,4,12,1,10]
+ CRUSH rule 0 x 790 [15,2,7,4,1,10,13]
+ CRUSH rule 0 x 791 [9,4,7,13,14,11,1,3]
+ CRUSH rule 0 x 792 [6,4,15,10,12,3]
+ CRUSH rule 0 x 793 [15,9,6,2,13,11,4]
+ CRUSH rule 0 x 794 [5,12,2,14,9,10,1,6]
+ CRUSH rule 0 x 795 [6,14,12,4,10,1,2,9]
+ CRUSH rule 0 x 796 [11,2,12,6,15,4]
+ CRUSH rule 0 x 797 [14,3,7,1,5,13,11]
+ CRUSH rule 0 x 798 [5,11,6,13,1,3,15,9]
+ CRUSH rule 0 x 799 [2,9,14,4,13,6,11]
+ CRUSH rule 0 x 800 [6,3,4,11,15,13]
+ CRUSH rule 0 x 801 [2,5,6,13,9,1,10,15]
+ CRUSH rule 0 x 802 [1,4,12,7,3,9,10,14]
+ CRUSH rule 0 x 803 [7,2,4,1,11,13,9,14]
+ CRUSH rule 0 x 804 [5,14,9,7,3,1,12,10]
+ CRUSH rule 0 x 805 [13,4,3,1,10,15,7]
+ CRUSH rule 0 x 806 [6,2,13,4,15,1,10]
+ CRUSH rule 0 x 807 [14,2,7,4,9,12,1,10]
+ CRUSH rule 0 x 808 [2,15,12,7,9,1,5,10]
+ CRUSH rule 0 x 809 [1,11,7,12,4,2,15,9]
+ CRUSH rule 0 x 810 [2,5,9,12,15,1,7,11]
+ CRUSH rule 0 x 811 [15,6,3,10,1,5,9,12]
+ CRUSH rule 0 x 812 [7,11,2,14,9,5,12]
+ CRUSH rule 0 x 813 [4,10,13,14,2,6,9]
+ CRUSH rule 0 x 814 [13,4,9,3,10,6,15]
+ CRUSH rule 0 x 815 [15,12,9,4,10,6,1,2]
+ CRUSH rule 0 x 816 [14,10,13,7,3,9,4,1]
+ CRUSH rule 0 x 817 [10,7,2,15,13,9,5]
+ CRUSH rule 0 x 818 [15,2,11,4,1,12,6,9]
+ CRUSH rule 0 x 819 [5,12,10,6,1,14,3]
+ CRUSH rule 0 x 820 [3,6,9,12,11,15,4,1]
+ CRUSH rule 0 x 821 [15,10,9,13,3,4,7,1]
+ CRUSH rule 0 x 822 [10,13,2,9,7,4,14,1]
+ CRUSH rule 0 x 823 [2,6,12,10,15,4,1,9]
+ CRUSH rule 0 x 824 [3,7,9,13,15,5,10]
+ CRUSH rule 0 x 825 [10,5,14,6,12,9,3]
+ CRUSH rule 0 x 826 [5,2,11,15,1,12,9,7]
+ CRUSH rule 0 x 827 [13,5,1,3,7,9,11,14]
+ CRUSH rule 0 x 828 [12,6,10,5,1,9,2,15]
+ CRUSH rule 0 x 829 [13,6,15,10,5,3,9]
+ CRUSH rule 0 x 830 [15,13,2,9,7,11,1,5]
+ CRUSH rule 0 x 831 [1,4,11,12,6,3,15]
+ CRUSH rule 0 x 832 [14,11,13,2,9,4,6,1]
+ CRUSH rule 0 x 833 [9,13,3,11,7,5,15,1]
+ CRUSH rule 0 x 834 [9,7,5,1,11,2,13,14]
+ CRUSH rule 0 x 835 [14,3,13,6,4,9,1,10]
+ CRUSH rule 0 x 836 [3,9,10,13,1,5,14,7]
+ CRUSH rule 0 x 837 [15,12,11,2,7,9,5]
+ CRUSH rule 0 x 838 [12,14,9,2,5,7,11]
+ CRUSH rule 0 x 839 [3,4,6,10,15,1,13,9]
+ CRUSH rule 0 x 840 [10,15,12,4,7,1,2,9]
+ CRUSH rule 0 x 841 [3,5,7,12,11,15,1,9]
+ CRUSH rule 0 x 842 [9,13,2,6,5,14,10,1]
+ CRUSH rule 0 x 843 [14,7,4,9,3,12,1,10]
+ CRUSH rule 0 x 844 [7,1,4,15,9,2,11,12]
+ CRUSH rule 0 x 845 [13,6,1,15,4,2,11]
+ CRUSH rule 0 x 846 [3,7,15,13,1,9,10,4]
+ CRUSH rule 0 x 847 [12,15,11,5,2,7,1]
+ CRUSH rule 0 x 848 [11,13,1,14,5,9,2,7]
+ CRUSH rule 0 x 849 [3,15,11,9,6,1,13,5]
+ CRUSH rule 0 x 850 [1,3,10,6,14,4,9,12]
+ CRUSH rule 0 x 851 [14,4,3,6,11,1,13]
+ CRUSH rule 0 x 852 [9,12,4,7,15,2,11,1]
+ CRUSH rule 0 x 853 [13,14,6,11,2,4,9,1]
+ CRUSH rule 0 x 854 [7,11,12,1,4,15,3]
+ CRUSH rule 0 x 855 [14,4,12,6,3,1,10]
+ CRUSH rule 0 x 856 [5,10,7,3,15,9,12,1]
+ CRUSH rule 0 x 857 [4,3,13,11,9,1,7,14]
+ CRUSH rule 0 x 858 [5,15,6,3,9,12,1,10]
+ CRUSH rule 0 x 859 [5,15,6,2,1,11,12,9]
+ CRUSH rule 0 x 860 [11,14,1,12,6,9,2,4]
+ CRUSH rule 0 x 861 [13,7,4,10,1,14,3,9]
+ CRUSH rule 0 x 862 [5,10,9,7,3,12,1,15]
+ CRUSH rule 0 x 863 [11,6,3,9,4,12,15]
+ CRUSH rule 0 x 864 [6,13,4,2,10,15,1,9]
+ CRUSH rule 0 x 865 [4,1,14,11,6,9,3,13]
+ CRUSH rule 0 x 866 [2,13,4,15,9,6,11]
+ CRUSH rule 0 x 867 [12,2,9,10,4,14,6]
+ CRUSH rule 0 x 868 [14,11,7,2,1,4,9,12]
+ CRUSH rule 0 x 869 [10,13,7,14,3,5,1]
+ CRUSH rule 0 x 870 [14,9,11,4,3,12,6,1]
+ CRUSH rule 0 x 871 [6,2,1,4,15,13,11,9]
+ CRUSH rule 0 x 872 [6,1,15,3,10,12,5]
+ CRUSH rule 0 x 873 [2,5,12,10,1,9,15,7]
+ CRUSH rule 0 x 874 [12,4,7,2,15,10,1]
+ CRUSH rule 0 x 875 [10,6,14,1,12,5,9,3]
+ CRUSH rule 0 x 876 [14,7,13,3,9,1,11,4]
+ CRUSH rule 0 x 877 [15,11,13,9,5,1,6,3]
+ CRUSH rule 0 x 878 [7,14,3,13,9,1,11,4]
+ CRUSH rule 0 x 879 [12,2,7,4,10,15]
+ CRUSH rule 0 x 880 [2,12,10,7,1,4,9,14]
+ CRUSH rule 0 x 881 [6,3,1,11,4,15,9,13]
+ CRUSH rule 0 x 882 [11,13,7,1,2,15,4,9]
+ CRUSH rule 0 x 883 [13,1,3,10,6,5,9,15]
+ CRUSH rule 0 x 884 [6,15,4,9,3,11,12,1]
+ CRUSH rule 0 x 885 [14,7,9,4,2,13,11]
+ CRUSH rule 0 x 886 [13,11,4,2,1,14,9,6]
+ CRUSH rule 0 x 887 [14,4,12,11,2,6,9,1]
+ CRUSH rule 0 x 888 [10,12,7,15,9,2,1,5]
+ CRUSH rule 0 x 889 [15,13,4,1,6,2,10,9]
+ CRUSH rule 0 x 890 [10,12,14,2,9,5,6,1]
+ CRUSH rule 0 x 891 [9,5,11,6,3,15,12,1]
+ CRUSH rule 0 x 892 [12,15,2,4,7,9,11,1]
+ CRUSH rule 0 x 893 [1,3,5,9,6,10,14,12]
+ CRUSH rule 0 x 894 [7,2,11,13,4,1,14]
+ CRUSH rule 0 x 895 [2,1,11,5,7,15,13,9]
+ CRUSH rule 0 x 896 [9,1,14,10,4,12,2,7]
+ CRUSH rule 0 x 897 [7,5,14,3,1,9,11,12]
+ CRUSH rule 0 x 898 [10,6,12,9,15,5,2,1]
+ CRUSH rule 0 x 899 [1,11,5,3,13,14,9,6]
+ CRUSH rule 0 x 900 [2,9,10,7,13,14,5,1]
+ CRUSH rule 0 x 901 [9,12,11,3,14,4,1,6]
+ CRUSH rule 0 x 902 [4,2,6,15,12,10,1]
+ CRUSH rule 0 x 903 [14,10,3,1,12,6,5]
+ CRUSH rule 0 x 904 [15,12,4,9,6,3,11]
+ CRUSH rule 0 x 905 [12,6,11,3,9,4,15]
+ CRUSH rule 0 x 906 [14,11,12,2,4,9,6]
+ CRUSH rule 0 x 907 [7,12,3,9,10,5,14,1]
+ CRUSH rule 0 x 908 [2,15,9,6,10,13,5,1]
+ CRUSH rule 0 x 909 [10,14,1,13,2,9,7,4]
+ CRUSH rule 0 x 910 [12,7,4,15,10,3,1,9]
+ CRUSH rule 0 x 911 [11,15,2,4,9,13,6,1]
+ CRUSH rule 0 x 912 [6,4,14,13,3,1,11]
+ CRUSH rule 0 x 913 [4,6,10,1,12,3,9,14]
+ CRUSH rule 0 x 914 [4,15,2,10,1,13,7]
+ CRUSH rule 0 x 915 [12,14,1,9,4,3,11,6]
+ CRUSH rule 0 x 916 [3,1,11,5,6,13,14]
+ CRUSH rule 0 x 917 [1,15,6,5,10,3,13,9]
+ CRUSH rule 0 x 918 [7,14,11,4,9,2,13]
+ CRUSH rule 0 x 919 [10,7,3,13,15,1,4]
+ CRUSH rule 0 x 920 [4,2,10,15,1,13,6]
+ CRUSH rule 0 x 921 [1,11,6,13,4,2,9,14]
+ CRUSH rule 0 x 922 [6,4,14,13,3,1,10,9]
+ CRUSH rule 0 x 923 [12,2,5,14,10,1,9,6]
+ CRUSH rule 0 x 924 [6,2,14,13,9,1,11,5]
+ CRUSH rule 0 x 925 [12,15,2,10,1,5,7]
+ CRUSH rule 0 x 926 [3,13,10,1,14,9,6,5]
+ CRUSH rule 0 x 927 [6,5,1,11,14,2,13,9]
+ CRUSH rule 0 x 928 [13,1,3,9,6,11,15,5]
+ CRUSH rule 0 x 929 [10,7,1,5,2,12,9,14]
+ CRUSH rule 0 x 930 [7,15,10,5,1,13,2]
+ CRUSH rule 0 x 931 [6,15,11,9,5,3,1,13]
+ CRUSH rule 0 x 932 [13,2,5,11,9,1,6,15]
+ CRUSH rule 0 x 933 [12,7,14,10,4,1,2,9]
+ CRUSH rule 0 x 934 [12,2,5,7,9,1,15,11]
+ CRUSH rule 0 x 935 [6,11,1,14,5,13,3,9]
+ CRUSH rule 0 x 936 [9,12,7,5,1,2,14,11]
+ CRUSH rule 0 x 937 [14,2,11,1,13,4,9,6]
+ CRUSH rule 0 x 938 [14,3,5,11,7,9,13]
+ CRUSH rule 0 x 939 [6,4,14,9,12,1,11,2]
+ CRUSH rule 0 x 940 [13,11,4,2,1,6,15]
+ CRUSH rule 0 x 941 [3,12,4,7,14,10]
+ CRUSH rule 0 x 942 [15,12,10,4,1,9,3,7]
+ CRUSH rule 0 x 943 [10,2,4,9,6,15,12]
+ CRUSH rule 0 x 944 [2,9,4,7,1,14,12,11]
+ CRUSH rule 0 x 945 [10,15,2,9,5,12,7]
+ CRUSH rule 0 x 946 [11,15,7,12,5,9,2]
+ CRUSH rule 0 x 947 [11,3,14,1,12,5,6,9]
+ CRUSH rule 0 x 948 [7,13,11,5,14,2,1,9]
+ CRUSH rule 0 x 949 [9,1,12,5,15,10,2,6]
+ CRUSH rule 0 x 950 [9,15,13,6,4,2,10]
+ CRUSH rule 0 x 951 [2,6,12,9,10,4,14]
+ CRUSH rule 0 x 952 [9,7,15,3,5,13,11]
+ CRUSH rule 0 x 953 [1,3,6,10,12,14,4,9]
+ CRUSH rule 0 x 954 [10,2,14,9,4,6,12,1]
+ CRUSH rule 0 x 955 [7,14,3,1,10,4,9,12]
+ CRUSH rule 0 x 956 [1,6,11,5,14,3,9,13]
+ CRUSH rule 0 x 957 [14,11,1,12,6,9,4,3]
+ CRUSH rule 0 x 958 [15,4,3,11,1,6,12,9]
+ CRUSH rule 0 x 959 [2,1,12,15,10,9,4,6]
+ CRUSH rule 0 x 960 [2,6,11,13,15,4,9]
+ CRUSH rule 0 x 961 [3,13,11,9,6,1,4,15]
+ CRUSH rule 0 x 962 [5,11,3,14,1,6,13,9]
+ CRUSH rule 0 x 963 [13,10,15,4,6,9,1,3]
+ CRUSH rule 0 x 964 [7,11,4,9,2,12,1,15]
+ CRUSH rule 0 x 965 [12,2,9,7,4,15,11,1]
+ CRUSH rule 0 x 966 [12,14,9,4,1,2,11,7]
+ CRUSH rule 0 x 967 [7,5,3,10,12,14]
+ CRUSH rule 0 x 968 [12,15,4,9,11,6,3]
+ CRUSH rule 0 x 969 [11,4,7,1,9,14,13,2]
+ CRUSH rule 0 x 970 [5,12,10,1,3,14,9,6]
+ CRUSH rule 0 x 971 [1,9,4,12,7,2,10,15]
+ CRUSH rule 0 x 972 [12,3,14,5,1,9,7,11]
+ CRUSH rule 0 x 973 [1,10,4,12,2,7,15]
+ CRUSH rule 0 x 974 [7,11,1,2,15,4,12,9]
+ CRUSH rule 0 x 975 [7,9,15,12,2,11,4]
+ CRUSH rule 0 x 976 [7,3,15,5,12,11,1]
+ CRUSH rule 0 x 977 [14,3,6,10,4,1,12]
+ CRUSH rule 0 x 978 [12,5,11,1,15,3,6]
+ CRUSH rule 0 x 979 [5,1,13,6,15,10,3,9]
+ CRUSH rule 0 x 980 [15,11,5,6,1,3,13,9]
+ CRUSH rule 0 x 981 [5,11,15,12,7,1,2]
+ CRUSH rule 0 x 982 [2,6,14,11,12,9,5]
+ CRUSH rule 0 x 983 [3,12,10,9,14,5,6]
+ CRUSH rule 0 x 984 [15,13,1,10,2,5,7]
+ CRUSH rule 0 x 985 [11,2,15,1,4,13,6,9]
+ CRUSH rule 0 x 986 [6,13,9,1,15,10,5,2]
+ CRUSH rule 0 x 987 [13,14,5,10,6,1,3,9]
+ CRUSH rule 0 x 988 [12,9,10,14,3,1,4,7]
+ CRUSH rule 0 x 989 [7,4,3,15,9,13,10,1]
+ CRUSH rule 0 x 990 [1,10,9,13,3,4,6,15]
+ CRUSH rule 0 x 991 [7,11,1,14,2,5,9,12]
+ CRUSH rule 0 x 992 [9,10,2,13,7,4,1,15]
+ CRUSH rule 0 x 993 [6,10,14,12,4,1,2]
+ CRUSH rule 0 x 994 [3,13,15,4,11,7,1,9]
+ CRUSH rule 0 x 995 [15,6,12,2,5,11]
+ CRUSH rule 0 x 996 [15,10,5,3,13,1,9,7]
+ CRUSH rule 0 x 997 [15,2,1,12,7,9,4,10]
+ CRUSH rule 0 x 998 [6,1,9,5,12,11,15,2]
+ CRUSH rule 0 x 999 [9,10,15,5,13,3,7]
+ CRUSH rule 0 x 1000 [14,2,9,4,12,1,6,11]
+ CRUSH rule 0 x 1001 [11,14,4,2,6,9,1,13]
+ CRUSH rule 0 x 1002 [1,10,14,2,9,5,13,7]
+ CRUSH rule 0 x 1003 [10,7,5,14,2,1,9,12]
+ CRUSH rule 0 x 1004 [15,1,4,6,10,12,9,3]
+ CRUSH rule 0 x 1005 [6,12,2,10,9,15,5,1]
+ CRUSH rule 0 x 1006 [10,12,15,1,2,6,5]
+ CRUSH rule 0 x 1007 [1,7,13,14,3,4,10]
+ CRUSH rule 0 x 1008 [7,4,9,11,3,15,1,13]
+ CRUSH rule 0 x 1009 [5,2,11,7,15,9,1,12]
+ CRUSH rule 0 x 1010 [10,2,15,6,9,13,4,1]
+ CRUSH rule 0 x 1011 [6,3,12,1,10,4,9,14]
+ CRUSH rule 0 x 1012 [12,6,9,15,3,1,5,11]
+ CRUSH rule 0 x 1013 [2,14,12,4,9,1,6,10]
+ CRUSH rule 0 x 1014 [1,13,7,2,10,14,5]
+ CRUSH rule 0 x 1015 [12,6,10,1,4,15,9,2]
+ CRUSH rule 0 x 1016 [10,13,14,3,5,6,1]
+ CRUSH rule 0 x 1017 [5,11,14,7,13,9,2]
+ CRUSH rule 0 x 1018 [13,11,14,1,9,3,5,7]
+ CRUSH rule 0 x 1019 [10,13,14,7,5,1,2]
+ CRUSH rule 0 x 1020 [3,1,13,4,10,9,14,6]
+ CRUSH rule 0 x 1021 [2,11,14,9,4,6,1,13]
+ CRUSH rule 0 x 1022 [15,5,7,2,12,10]
+ CRUSH rule 0 x 1023 [15,2,9,12,1,7,4,11]
+ rule 0 (replicated_ruleset) num_rep 9 result size == 6:\t36/1024 (esc)
+ rule 0 (replicated_ruleset) num_rep 9 result size == 7:\t263/1024 (esc)
+ rule 0 (replicated_ruleset) num_rep 9 result size == 8:\t725/1024 (esc)
+ CRUSH rule 0 x 0 [7,10,3,15,12,1,4,9]
+ CRUSH rule 0 x 1 [10,15,1,2,13,4,7,9]
+ CRUSH rule 0 x 2 [1,12,2,6,5,10,15]
+ CRUSH rule 0 x 3 [15,4,10,2,9,6,13]
+ CRUSH rule 0 x 4 [14,2,10,1,9,4,7,13]
+ CRUSH rule 0 x 5 [7,4,11,2,13,15,9]
+ CRUSH rule 0 x 6 [12,6,10,9,3,4,14]
+ CRUSH rule 0 x 7 [9,2,6,12,11,4,1,14]
+ CRUSH rule 0 x 8 [10,2,15,1,4,13,6,9]
+ CRUSH rule 0 x 9 [7,1,14,2,11,9,12,4]
+ CRUSH rule 0 x 10 [10,14,4,1,2,7,13,9]
+ CRUSH rule 0 x 11 [13,9,14,7,5,11,2,1]
+ CRUSH rule 0 x 12 [7,1,2,5,13,15,11,9]
+ CRUSH rule 0 x 13 [3,5,12,7,9,1,14,11]
+ CRUSH rule 0 x 14 [13,5,2,7,10,15,1,9]
+ CRUSH rule 0 x 15 [15,1,9,6,13,3,5,11]
+ CRUSH rule 0 x 16 [7,11,14,2,13,1,9,4]
+ CRUSH rule 0 x 17 [10,1,13,2,4,6,14,9]
+ CRUSH rule 0 x 18 [1,7,3,10,5,12,9,14]
+ CRUSH rule 0 x 19 [7,12,2,4,15,10,1]
+ CRUSH rule 0 x 20 [14,12,3,10,9,4,7,1]
+ CRUSH rule 0 x 21 [3,12,1,10,4,15,6]
+ CRUSH rule 0 x 22 [6,3,13,11,4,1,15]
+ CRUSH rule 0 x 23 [10,5,13,9,3,15,1,6]
+ CRUSH rule 0 x 24 [12,11,3,1,9,4,7,15]
+ CRUSH rule 0 x 25 [7,12,15,1,3,10,4,9]
+ CRUSH rule 0 x 26 [1,7,13,2,14,5,9,11]
+ CRUSH rule 0 x 27 [3,6,15,4,13,9,11,1]
+ CRUSH rule 0 x 28 [14,4,3,9,6,11,13]
+ CRUSH rule 0 x 29 [5,14,12,11,6,3,1,9]
+ CRUSH rule 0 x 30 [2,5,6,9,1,11,13,14]
+ CRUSH rule 0 x 31 [5,15,10,1,9,13,6,3]
+ CRUSH rule 0 x 32 [9,10,2,1,13,14,6,4]
+ CRUSH rule 0 x 33 [13,4,9,2,7,1,10,14]
+ CRUSH rule 0 x 34 [13,15,2,4,1,10,9,6]
+ CRUSH rule 0 x 35 [4,14,3,13,10,9,1,6]
+ CRUSH rule 0 x 36 [3,12,9,7,5,10,14,1]
+ CRUSH rule 0 x 37 [9,2,6,14,11,1,4,13]
+ CRUSH rule 0 x 38 [3,4,13,10,9,1,14,6]
+ CRUSH rule 0 x 39 [12,7,14,11,1,9,5,2]
+ CRUSH rule 0 x 40 [10,1,9,5,15,2,6,13]
+ CRUSH rule 0 x 41 [4,9,11,1,14,13,6,3]
+ CRUSH rule 0 x 42 [3,6,14,10,12,5,1]
+ CRUSH rule 0 x 43 [10,5,15,7,2,9,12]
+ CRUSH rule 0 x 44 [11,4,13,3,7,14,9]
+ CRUSH rule 0 x 45 [11,12,15,9,1,5,6,3]
+ CRUSH rule 0 x 46 [6,9,2,14,11,13,1,5]
+ CRUSH rule 0 x 47 [3,9,6,4,13,1,11,15]
+ CRUSH rule 0 x 48 [4,6,2,1,10,14,13]
+ CRUSH rule 0 x 49 [9,15,10,7,4,3,13]
+ CRUSH rule 0 x 50 [14,12,1,4,2,11,6,9]
+ CRUSH rule 0 x 51 [10,6,5,12,15,2,1,9]
+ CRUSH rule 0 x 52 [12,1,9,11,7,3,14,4]
+ CRUSH rule 0 x 53 [3,6,13,9,5,1,11,15]
+ CRUSH rule 0 x 54 [4,13,9,2,14,10,6,1]
+ CRUSH rule 0 x 55 [4,11,2,7,1,13,9,15]
+ CRUSH rule 0 x 56 [5,9,10,1,3,13,14,7]
+ CRUSH rule 0 x 57 [6,2,1,15,10,12,5]
+ CRUSH rule 0 x 58 [7,1,11,4,3,14,12,9]
+ CRUSH rule 0 x 59 [2,13,1,10,9,5,14,6]
+ CRUSH rule 0 x 60 [3,6,11,1,4,9,12,15]
+ CRUSH rule 0 x 61 [3,15,13,7,4,1,10,9]
+ CRUSH rule 0 x 62 [15,11,7,12,5,9,2,1]
+ CRUSH rule 0 x 63 [10,14,12,1,7,3]
+ CRUSH rule 0 x 64 [3,9,1,4,7,12,11,15]
+ CRUSH rule 0 x 65 [4,12,11,7,14,3,1]
+ CRUSH rule 0 x 66 [15,11,6,9,4,1,3,12]
+ CRUSH rule 0 x 67 [2,6,4,14,1,11,12]
+ CRUSH rule 0 x 68 [15,7,4,2,9,12,11]
+ CRUSH rule 0 x 69 [2,1,15,10,4,9,13,7]
+ CRUSH rule 0 x 70 [9,6,1,3,13,15,11,5]
+ CRUSH rule 0 x 71 [15,5,1,3,13,10,7]
+ CRUSH rule 0 x 72 [9,10,3,5,7,12,15,1]
+ CRUSH rule 0 x 73 [5,3,11,1,7,12,15,9]
+ CRUSH rule 0 x 74 [11,7,9,5,1,15,3,12]
+ CRUSH rule 0 x 75 [9,7,11,14,12,1,2,5]
+ CRUSH rule 0 x 76 [6,1,3,5,14,10,12,9]
+ CRUSH rule 0 x 77 [7,4,2,13,9,1,11,15]
+ CRUSH rule 0 x 78 [9,3,1,5,6,13,14,11]
+ CRUSH rule 0 x 79 [13,2,15,5,7,9,11,1]
+ CRUSH rule 0 x 80 [15,2,6,4,13,10,1]
+ CRUSH rule 0 x 81 [15,2,1,11,4,6,13]
+ CRUSH rule 0 x 82 [14,13,5,11,6,2,1,9]
+ CRUSH rule 0 x 83 [4,15,3,9,10,13,6,1]
+ CRUSH rule 0 x 84 [10,7,9,15,3,4,1,13]
+ CRUSH rule 0 x 85 [3,15,9,7,4,11,1,12]
+ CRUSH rule 0 x 86 [10,9,14,1,13,4,2,7]
+ CRUSH rule 0 x 87 [15,10,7,12,5,3,9,1]
+ CRUSH rule 0 x 88 [4,13,3,1,9,15,11,7]
+ CRUSH rule 0 x 89 [3,9,7,4,1,14,10,12]
+ CRUSH rule 0 x 90 [4,9,7,12,11,14,2,1]
+ CRUSH rule 0 x 91 [6,11,9,1,2,4,14,13]
+ CRUSH rule 0 x 92 [1,5,10,9,13,15,6,2]
+ CRUSH rule 0 x 93 [9,3,15,13,7,5,1,10]
+ CRUSH rule 0 x 94 [9,2,12,5,6,11,1,14]
+ CRUSH rule 0 x 95 [7,15,4,10,9,13,2,1]
+ CRUSH rule 0 x 96 [2,15,11,7,5,1,12]
+ CRUSH rule 0 x 97 [4,11,2,13,1,7,9,14]
+ CRUSH rule 0 x 98 [11,13,9,3,15,1,5,6]
+ CRUSH rule 0 x 99 [12,4,11,7,3,14,9,1]
+ CRUSH rule 0 x 100 [9,4,10,15,7,3,13]
+ CRUSH rule 0 x 101 [15,7,1,9,10,5,2,12]
+ CRUSH rule 0 x 102 [3,11,14,6,13,4,9,1]
+ CRUSH rule 0 x 103 [13,11,6,14,4,3,1]
+ CRUSH rule 0 x 104 [14,6,3,5,9,1,10,13]
+ CRUSH rule 0 x 105 [14,10,1,9,3,5,6,13]
+ CRUSH rule 0 x 106 [6,5,13,2,14,11,1,9]
+ CRUSH rule 0 x 107 [3,1,10,14,13,5,9,6]
+ CRUSH rule 0 x 108 [5,10,7,2,15,9,12,1]
+ CRUSH rule 0 x 109 [9,1,13,7,15,5,11,3]
+ CRUSH rule 0 x 110 [5,1,11,3,7,14,13,9]
+ CRUSH rule 0 x 111 [10,1,9,7,5,2,13,14]
+ CRUSH rule 0 x 112 [1,10,4,14,2,12,6,9]
+ CRUSH rule 0 x 113 [6,10,13,9,1,5,2,14]
+ CRUSH rule 0 x 114 [5,13,6,2,1,14,11]
+ CRUSH rule 0 x 115 [10,13,14,3,9,1,6,5]
+ CRUSH rule 0 x 116 [1,14,13,2,11,5,7]
+ CRUSH rule 0 x 117 [5,6,1,12,15,9,11,3]
+ CRUSH rule 0 x 118 [10,4,13,15,9,3,1,6]
+ CRUSH rule 0 x 119 [14,12,11,4,6,9,3,1]
+ CRUSH rule 0 x 120 [11,3,14,13,4,7]
+ CRUSH rule 0 x 121 [9,5,1,11,7,3,15,12]
+ CRUSH rule 0 x 122 [4,3,14,1,11,13,7]
+ CRUSH rule 0 x 123 [3,10,5,6,9,1,12,15]
+ CRUSH rule 0 x 124 [12,2,1,5,14,7,11,9]
+ CRUSH rule 0 x 125 [9,12,15,1,6,5,3,10]
+ CRUSH rule 0 x 126 [7,15,10,9,2,12,5,1]
+ CRUSH rule 0 x 127 [4,14,9,13,1,3,7,10]
+ CRUSH rule 0 x 128 [3,12,1,10,4,9,7,14]
+ CRUSH rule 0 x 129 [11,13,14,2,9,4,6,1]
+ CRUSH rule 0 x 130 [3,13,5,14,10,1,9,6]
+ CRUSH rule 0 x 131 [12,1,6,15,4,2,11,9]
+ CRUSH rule 0 x 132 [11,15,13,9,2,5,7]
+ CRUSH rule 0 x 133 [3,6,9,11,15,12,5]
+ CRUSH rule 0 x 134 [12,5,6,15,3,9,10,1]
+ CRUSH rule 0 x 135 [3,14,12,4,6,11,9]
+ CRUSH rule 0 x 136 [15,6,9,4,10,3,12]
+ CRUSH rule 0 x 137 [14,3,6,11,1,9,4,13]
+ CRUSH rule 0 x 138 [13,15,4,10,2,7,1]
+ CRUSH rule 0 x 139 [11,2,13,9,1,15,7,5]
+ CRUSH rule 0 x 140 [11,4,12,15,2,6,9,1]
+ CRUSH rule 0 x 141 [6,12,15,11,3,5,1]
+ CRUSH rule 0 x 142 [3,14,7,9,11,1,4,13]
+ CRUSH rule 0 x 143 [9,6,4,2,14,10,12]
+ CRUSH rule 0 x 144 [13,7,11,2,14,4,1,9]
+ CRUSH rule 0 x 145 [12,2,6,10,9,4,14,1]
+ CRUSH rule 0 x 146 [1,5,9,2,6,13,14,10]
+ CRUSH rule 0 x 147 [1,4,9,11,2,7,15,12]
+ CRUSH rule 0 x 148 [12,7,9,2,14,11,1,4]
+ CRUSH rule 0 x 149 [2,5,9,12,11,1,7,14]
+ CRUSH rule 0 x 150 [1,15,2,10,7,9,5,12]
+ CRUSH rule 0 x 151 [2,9,14,7,1,10,5,13]
+ CRUSH rule 0 x 152 [5,9,2,6,10,13,14]
+ CRUSH rule 0 x 153 [6,9,4,15,2,1,10,12]
+ CRUSH rule 0 x 154 [3,11,7,1,4,12,15,9]
+ CRUSH rule 0 x 155 [14,12,7,3,5,1,9,11]
+ CRUSH rule 0 x 156 [7,13,3,10,15,5,1]
+ CRUSH rule 0 x 157 [15,1,6,4,3,10,9,13]
+ CRUSH rule 0 x 158 [15,1,10,6,12,2,4]
+ CRUSH rule 0 x 159 [4,14,3,12,10,6,1,9]
+ CRUSH rule 0 x 160 [5,7,3,14,11,1,12]
+ CRUSH rule 0 x 161 [1,2,11,4,6,13,14]
+ CRUSH rule 0 x 162 [10,6,1,12,2,4,14,9]
+ CRUSH rule 0 x 163 [15,1,10,2,6,4,13,9]
+ CRUSH rule 0 x 164 [9,14,10,7,12,2,5,1]
+ CRUSH rule 0 x 165 [11,7,2,13,9,15,1,5]
+ CRUSH rule 0 x 166 [1,2,12,14,4,11,7,9]
+ CRUSH rule 0 x 167 [9,7,3,4,11,13,15,1]
+ CRUSH rule 0 x 168 [13,2,4,1,6,15,10,9]
+ CRUSH rule 0 x 169 [1,4,9,14,13,10,2,7]
+ CRUSH rule 0 x 170 [1,15,7,9,12,10,3,5]
+ CRUSH rule 0 x 171 [9,2,10,7,1,5,15,12]
+ CRUSH rule 0 x 172 [14,4,10,12,9,3,6]
+ CRUSH rule 0 x 173 [5,10,12,15,6,1,2,9]
+ CRUSH rule 0 x 174 [15,6,4,12,1,11,9,2]
+ CRUSH rule 0 x 175 [5,7,9,3,10,1,14,13]
+ CRUSH rule 0 x 176 [9,6,3,14,13,10,4]
+ CRUSH rule 0 x 177 [2,9,10,13,4,1,14,7]
+ CRUSH rule 0 x 178 [12,11,7,14,3,4]
+ CRUSH rule 0 x 179 [2,10,13,9,5,1,7,14]
+ CRUSH rule 0 x 180 [3,11,5,15,7,12]
+ CRUSH rule 0 x 181 [9,12,6,5,1,10,14,2]
+ CRUSH rule 0 x 182 [5,13,11,2,1,6,14]
+ CRUSH rule 0 x 183 [5,7,10,13,3,9,14,1]
+ CRUSH rule 0 x 184 [2,5,11,12,7,1,9,15]
+ CRUSH rule 0 x 185 [13,5,7,11,2,14]
+ CRUSH rule 0 x 186 [6,14,13,5,10,1,3,9]
+ CRUSH rule 0 x 187 [1,4,11,13,6,14,9,3]
+ CRUSH rule 0 x 188 [9,13,5,14,10,6,2]
+ CRUSH rule 0 x 189 [6,12,4,9,2,1,11,15]
+ CRUSH rule 0 x 190 [9,13,15,10,3,1,5,6]
+ CRUSH rule 0 x 191 [7,11,4,1,15,12,9,2]
+ CRUSH rule 0 x 192 [2,11,5,15,6,1,13]
+ CRUSH rule 0 x 193 [3,13,6,10,4,1,9,14]
+ CRUSH rule 0 x 194 [3,13,4,14,6,9,1,11]
+ CRUSH rule 0 x 195 [5,7,10,12,1,3,15,9]
+ CRUSH rule 0 x 196 [4,15,1,10,9,2,13,7]
+ CRUSH rule 0 x 197 [14,10,13,4,6,3,1,9]
+ CRUSH rule 0 x 198 [2,5,6,15,9,13,10]
+ CRUSH rule 0 x 199 [2,10,4,15,1,9,6,12]
+ CRUSH rule 0 x 200 [7,14,11,4,1,3,13]
+ CRUSH rule 0 x 201 [9,14,1,7,4,3,10,13]
+ CRUSH rule 0 x 202 [14,11,7,3,5,1,12]
+ CRUSH rule 0 x 203 [12,5,7,15,1,2,10,9]
+ CRUSH rule 0 x 204 [6,11,3,12,14,1,9,4]
+ CRUSH rule 0 x 205 [15,4,6,10,13,9,2,1]
+ CRUSH rule 0 x 206 [13,11,2,15,7,1,5]
+ CRUSH rule 0 x 207 [2,11,7,4,14,1,12,9]
+ CRUSH rule 0 x 208 [13,1,6,14,9,11,3,5]
+ CRUSH rule 0 x 209 [6,15,13,1,11,4,9,2]
+ CRUSH rule 0 x 210 [13,11,2,7,5,14,9]
+ CRUSH rule 0 x 211 [2,14,1,13,11,7,9,5]
+ CRUSH rule 0 x 212 [10,1,12,15,5,6,2,9]
+ CRUSH rule 0 x 213 [3,9,6,5,15,13,1,11]
+ CRUSH rule 0 x 214 [7,15,4,1,10,2,13,9]
+ CRUSH rule 0 x 215 [6,1,4,13,3,11,14]
+ CRUSH rule 0 x 216 [12,9,6,2,1,11,5,15]
+ CRUSH rule 0 x 217 [12,11,1,14,2,4,7]
+ CRUSH rule 0 x 218 [12,10,15,6,1,4,9,2]
+ CRUSH rule 0 x 219 [3,11,14,6,4,1,13]
+ CRUSH rule 0 x 220 [14,4,3,12,10,9,6]
+ CRUSH rule 0 x 221 [15,5,2,6,12,11,9]
+ CRUSH rule 0 x 222 [10,4,3,15,7,12,1]
+ CRUSH rule 0 x 223 [9,7,11,1,4,14,13,3]
+ CRUSH rule 0 x 224 [1,7,10,2,12,9,14,5]
+ CRUSH rule 0 x 225 [10,5,2,6,1,13,9,15]
+ CRUSH rule 0 x 226 [4,1,9,3,13,10,15,7]
+ CRUSH rule 0 x 227 [7,2,12,15,5,11]
+ CRUSH rule 0 x 228 [2,15,11,1,6,13,9,4]
+ CRUSH rule 0 x 229 [9,3,7,14,1,12,4,10]
+ CRUSH rule 0 x 230 [10,5,7,2,15,1,13]
+ CRUSH rule 0 x 231 [2,7,5,13,9,15,10]
+ CRUSH rule 0 x 232 [10,5,13,1,9,2,7,14]
+ CRUSH rule 0 x 233 [6,12,11,4,9,14,1,3]
+ CRUSH rule 0 x 234 [10,1,2,12,5,9,15,7]
+ CRUSH rule 0 x 235 [13,14,7,10,1,9,5,3]
+ CRUSH rule 0 x 236 [2,15,9,12,1,7,4,10]
+ CRUSH rule 0 x 237 [3,12,9,10,4,7,15]
+ CRUSH rule 0 x 238 [2,10,4,15,6,12,9,1]
+ CRUSH rule 0 x 239 [4,15,10,7,9,13,3,1]
+ CRUSH rule 0 x 240 [15,5,13,7,2,9,10]
+ CRUSH rule 0 x 241 [7,9,15,12,1,5,2,10]
+ CRUSH rule 0 x 242 [14,2,6,9,10,12,5]
+ CRUSH rule 0 x 243 [2,11,5,1,15,6,9,13]
+ CRUSH rule 0 x 244 [13,9,15,3,11,7,5]
+ CRUSH rule 0 x 245 [12,9,15,3,1,5,10,7]
+ CRUSH rule 0 x 246 [15,3,5,11,7,1,12,9]
+ CRUSH rule 0 x 247 [6,4,9,12,1,2,10,14]
+ CRUSH rule 0 x 248 [5,13,7,11,9,15,3,1]
+ CRUSH rule 0 x 249 [10,14,7,3,9,13,1,4]
+ CRUSH rule 0 x 250 [12,15,1,10,5,6,3,9]
+ CRUSH rule 0 x 251 [13,2,15,5,6,1,9,10]
+ CRUSH rule 0 x 252 [7,5,13,9,3,10,14,1]
+ CRUSH rule 0 x 253 [3,13,15,10,7,4]
+ CRUSH rule 0 x 254 [2,9,13,14,4,6,10]
+ CRUSH rule 0 x 255 [1,9,13,2,6,10,4,15]
+ CRUSH rule 0 x 256 [6,9,13,1,3,14,5,10]
+ CRUSH rule 0 x 257 [15,12,3,9,6,4,11]
+ CRUSH rule 0 x 258 [12,5,6,10,2,1,14,9]
+ CRUSH rule 0 x 259 [9,10,4,3,14,13,1,7]
+ CRUSH rule 0 x 260 [10,12,6,9,3,15,1,4]
+ CRUSH rule 0 x 261 [13,7,2,1,15,5,11,9]
+ CRUSH rule 0 x 262 [15,3,12,7,4,9,1,11]
+ CRUSH rule 0 x 263 [12,6,10,9,5,15,3,1]
+ CRUSH rule 0 x 264 [13,14,11,3,1,4,7,9]
+ CRUSH rule 0 x 265 [12,10,14,5,7,1,9,3]
+ CRUSH rule 0 x 266 [14,7,11,1,2,9,4,12]
+ CRUSH rule 0 x 267 [12,11,6,5,1,2,15]
+ CRUSH rule 0 x 268 [4,1,15,12,6,11,3,9]
+ CRUSH rule 0 x 269 [11,1,15,5,13,9,7,2]
+ CRUSH rule 0 x 270 [7,11,12,3,1,14,9,4]
+ CRUSH rule 0 x 271 [4,7,3,13,15,10,9,1]
+ CRUSH rule 0 x 272 [15,5,13,10,6,2]
+ CRUSH rule 0 x 273 [2,10,7,12,1,15,5]
+ CRUSH rule 0 x 274 [10,2,5,6,13,9,15,1]
+ CRUSH rule 0 x 275 [10,3,4,7,14,13]
+ CRUSH rule 0 x 276 [5,12,9,2,11,7,15,1]
+ CRUSH rule 0 x 277 [14,3,13,4,1,9,11,7]
+ CRUSH rule 0 x 278 [5,6,14,3,1,11,13,9]
+ CRUSH rule 0 x 279 [6,10,13,3,9,4,15]
+ CRUSH rule 0 x 280 [7,3,14,9,1,11,4,13]
+ CRUSH rule 0 x 281 [5,11,14,7,9,13,2,1]
+ CRUSH rule 0 x 282 [2,1,13,14,9,7,5,10]
+ CRUSH rule 0 x 283 [4,1,12,3,10,7,15]
+ CRUSH rule 0 x 284 [5,11,7,15,3,13,1,9]
+ CRUSH rule 0 x 285 [15,5,3,1,6,13,11,9]
+ CRUSH rule 0 x 286 [10,4,3,6,12,15,1]
+ CRUSH rule 0 x 287 [12,4,9,1,3,11,15,7]
+ CRUSH rule 0 x 288 [4,12,10,7,1,3,14,9]
+ CRUSH rule 0 x 289 [2,5,14,9,13,6,10]
+ CRUSH rule 0 x 290 [12,2,5,6,15,9,1,10]
+ CRUSH rule 0 x 291 [7,11,1,14,5,9,2,12]
+ CRUSH rule 0 x 292 [4,10,6,3,14,9,12,1]
+ CRUSH rule 0 x 293 [6,5,11,1,2,14,12]
+ CRUSH rule 0 x 294 [9,12,3,14,6,11,5,1]
+ CRUSH rule 0 x 295 [6,10,3,14,9,4,13]
+ CRUSH rule 0 x 296 [3,1,13,7,14,9,10,4]
+ CRUSH rule 0 x 297 [6,13,4,14,10,1,2,9]
+ CRUSH rule 0 x 298 [14,9,13,1,4,2,7,10]
+ CRUSH rule 0 x 299 [14,12,11,6,4,2,1,9]
+ CRUSH rule 0 x 300 [15,7,10,5,1,3,13,9]
+ CRUSH rule 0 x 301 [9,11,7,1,13,14,4,2]
+ CRUSH rule 0 x 302 [9,7,1,13,5,10,3,15]
+ CRUSH rule 0 x 303 [4,13,3,7,10,15,1]
+ CRUSH rule 0 x 304 [6,9,2,11,15,13,4]
+ CRUSH rule 0 x 305 [13,7,5,11,2,15,9]
+ CRUSH rule 0 x 306 [10,12,4,6,9,2,15,1]
+ CRUSH rule 0 x 307 [11,12,15,5,6,2,1,9]
+ CRUSH rule 0 x 308 [12,14,10,9,1,2,5,7]
+ CRUSH rule 0 x 309 [9,3,12,5,11,15,7]
+ CRUSH rule 0 x 310 [3,1,5,10,14,9,7,12]
+ CRUSH rule 0 x 311 [3,9,7,1,14,13,10,5]
+ CRUSH rule 0 x 312 [15,13,9,7,5,10,2]
+ CRUSH rule 0 x 313 [9,15,3,7,5,13,1,11]
+ CRUSH rule 0 x 314 [2,15,9,5,6,12,1,11]
+ CRUSH rule 0 x 315 [15,2,13,1,11,9,6,4]
+ CRUSH rule 0 x 316 [4,9,11,2,12,14,6]
+ CRUSH rule 0 x 317 [1,5,3,13,15,7,10]
+ CRUSH rule 0 x 318 [4,1,15,11,9,13,6,2]
+ CRUSH rule 0 x 319 [2,15,4,1,11,9,7,12]
+ CRUSH rule 0 x 320 [5,7,13,9,11,2,1,15]
+ CRUSH rule 0 x 321 [1,6,11,15,5,3,13]
+ CRUSH rule 0 x 322 [13,7,5,3,14,11,1]
+ CRUSH rule 0 x 323 [7,4,10,1,2,13,14]
+ CRUSH rule 0 x 324 [5,6,10,15,2,13]
+ CRUSH rule 0 x 325 [9,10,14,5,1,6,2,13]
+ CRUSH rule 0 x 326 [11,7,13,4,2,15,1]
+ CRUSH rule 0 x 327 [12,5,10,14,3,7,9]
+ CRUSH rule 0 x 328 [5,2,6,14,1,11,12]
+ CRUSH rule 0 x 329 [2,6,15,5,9,10,13,1]
+ CRUSH rule 0 x 330 [3,9,11,13,1,6,5,14]
+ CRUSH rule 0 x 331 [12,14,6,3,1,4,10,9]
+ CRUSH rule 0 x 332 [10,12,6,15,9,2,5]
+ CRUSH rule 0 x 333 [6,5,3,12,14,10,9,1]
+ CRUSH rule 0 x 334 [4,9,2,12,7,11,15,1]
+ CRUSH rule 0 x 335 [11,7,1,5,13,2,9,15]
+ CRUSH rule 0 x 336 [6,14,13,2,5,9,11]
+ CRUSH rule 0 x 337 [15,11,3,7,12,5]
+ CRUSH rule 0 x 338 [10,5,3,6,15,1,9,13]
+ CRUSH rule 0 x 339 [11,14,13,5,3,7,1]
+ CRUSH rule 0 x 340 [11,6,12,4,9,3,14,1]
+ CRUSH rule 0 x 341 [7,5,2,10,14,9,1,12]
+ CRUSH rule 0 x 342 [12,14,1,9,2,11,4,7]
+ CRUSH rule 0 x 343 [12,14,9,6,10,2,4,1]
+ CRUSH rule 0 x 344 [9,11,5,2,14,13,1,7]
+ CRUSH rule 0 x 345 [14,2,11,9,6,12,4]
+ CRUSH rule 0 x 346 [5,3,14,10,7,1,13]
+ CRUSH rule 0 x 347 [10,2,12,6,9,1,14,5]
+ CRUSH rule 0 x 348 [7,9,10,1,14,13,3,4]
+ CRUSH rule 0 x 349 [9,6,10,12,1,5,14]
+ CRUSH rule 0 x 350 [13,9,15,4,10,7,2,1]
+ CRUSH rule 0 x 351 [13,5,15,3,1,6,11]
+ CRUSH rule 0 x 352 [1,12,11,9,4,7,3,15]
+ CRUSH rule 0 x 353 [10,14,12,2,9,1,4,6]
+ CRUSH rule 0 x 354 [6,3,15,10,9,4,13]
+ CRUSH rule 0 x 355 [13,14,6,10,2,5,1,9]
+ CRUSH rule 0 x 356 [15,13,2,9,6,5,1,11]
+ CRUSH rule 0 x 357 [4,11,1,13,3,14,6,9]
+ CRUSH rule 0 x 358 [12,7,2,9,1,14,10,4]
+ CRUSH rule 0 x 359 [5,15,7,11,3,13]
+ CRUSH rule 0 x 360 [13,10,1,2,6,14,5]
+ CRUSH rule 0 x 361 [5,3,13,6,1,14,11,9]
+ CRUSH rule 0 x 362 [2,9,11,13,1,6,5,15]
+ CRUSH rule 0 x 363 [7,12,3,9,15,4,1,10]
+ CRUSH rule 0 x 364 [2,12,6,9,5,10,15]
+ CRUSH rule 0 x 365 [13,5,11,15,6,2,9]
+ CRUSH rule 0 x 366 [12,7,3,14,5,10,9]
+ CRUSH rule 0 x 367 [7,13,3,1,5,11,15,9]
+ CRUSH rule 0 x 368 [7,9,10,15,3,4,13]
+ CRUSH rule 0 x 369 [7,5,3,13,14,9,11,1]
+ CRUSH rule 0 x 370 [4,7,14,1,2,9,11,12]
+ CRUSH rule 0 x 371 [1,7,12,3,4,15,10,9]
+ CRUSH rule 0 x 372 [10,4,3,14,6,1,12,9]
+ CRUSH rule 0 x 373 [15,5,2,6,13,1,9,10]
+ CRUSH rule 0 x 374 [3,15,12,5,1,6,10,9]
+ CRUSH rule 0 x 375 [5,2,14,1,6,13,11,9]
+ CRUSH rule 0 x 376 [5,14,10,13,3,6,1]
+ CRUSH rule 0 x 377 [1,15,2,4,9,11,12,6]
+ CRUSH rule 0 x 378 [9,12,2,15,1,5,11,6]
+ CRUSH rule 0 x 379 [11,2,15,5,7,9,13,1]
+ CRUSH rule 0 x 380 [6,1,12,11,2,9,5,14]
+ CRUSH rule 0 x 381 [15,13,7,5,10,2,1,9]
+ CRUSH rule 0 x 382 [14,3,1,4,13,7,10]
+ CRUSH rule 0 x 383 [3,6,11,4,13,15,1]
+ CRUSH rule 0 x 384 [4,13,6,3,15,11,9]
+ CRUSH rule 0 x 385 [4,6,15,3,10,9,1,13]
+ CRUSH rule 0 x 386 [14,3,11,13,5,6,9,1]
+ CRUSH rule 0 x 387 [1,11,5,7,9,2,14,12]
+ CRUSH rule 0 x 388 [2,6,11,9,15,4,12]
+ CRUSH rule 0 x 389 [12,7,2,4,15,10,1]
+ CRUSH rule 0 x 390 [2,11,13,7,5,9,15]
+ CRUSH rule 0 x 391 [3,4,9,13,7,10,1,14]
+ CRUSH rule 0 x 392 [11,5,14,7,1,9,2,12]
+ CRUSH rule 0 x 393 [2,14,5,9,7,13,11]
+ CRUSH rule 0 x 394 [4,9,3,15,13,6,1,11]
+ CRUSH rule 0 x 395 [10,13,5,15,6,9,3,1]
+ CRUSH rule 0 x 396 [2,12,15,9,4,6,11]
+ CRUSH rule 0 x 397 [1,14,9,4,12,10,3,7]
+ CRUSH rule 0 x 398 [9,2,1,5,12,6,11,15]
+ CRUSH rule 0 x 399 [5,9,14,3,1,10,13,7]
+ CRUSH rule 0 x 400 [10,6,2,4,15,12,1,9]
+ CRUSH rule 0 x 401 [6,9,11,12,4,3,15,1]
+ CRUSH rule 0 x 402 [4,7,9,2,13,1,15,11]
+ CRUSH rule 0 x 403 [7,15,13,3,5,9,10]
+ CRUSH rule 0 x 404 [14,12,7,9,2,1,5,11]
+ CRUSH rule 0 x 405 [9,15,11,2,4,7,13,1]
+ CRUSH rule 0 x 406 [12,14,9,2,7,10,4,1]
+ CRUSH rule 0 x 407 [9,5,12,10,15,6,3]
+ CRUSH rule 0 x 408 [7,1,5,2,10,15,13,9]
+ CRUSH rule 0 x 409 [11,2,4,13,1,15,7,9]
+ CRUSH rule 0 x 410 [6,4,14,2,12,9,10,1]
+ CRUSH rule 0 x 411 [13,11,15,6,4,1,9,2]
+ CRUSH rule 0 x 412 [5,9,6,11,14,2,12]
+ CRUSH rule 0 x 413 [13,5,3,11,6,9,1,14]
+ CRUSH rule 0 x 414 [3,11,9,13,4,1,6,15]
+ CRUSH rule 0 x 415 [6,10,14,5,1,13,3,9]
+ CRUSH rule 0 x 416 [13,1,4,7,2,9,14,11]
+ CRUSH rule 0 x 417 [4,12,1,15,2,11,9,6]
+ CRUSH rule 0 x 418 [14,5,10,2,6,9,13]
+ CRUSH rule 0 x 419 [5,14,10,9,2,12,6,1]
+ CRUSH rule 0 x 420 [2,4,9,11,6,14,13,1]
+ CRUSH rule 0 x 421 [15,4,10,3,9,12,7]
+ CRUSH rule 0 x 422 [4,11,2,7,13,9,15]
+ CRUSH rule 0 x 423 [3,15,12,6,5,1,9,10]
+ CRUSH rule 0 x 424 [6,10,12,2,5,1,14,9]
+ CRUSH rule 0 x 425 [11,15,2,13,5,7,9,1]
+ CRUSH rule 0 x 426 [12,4,7,1,9,10,14,2]
+ CRUSH rule 0 x 427 [14,10,3,1,9,7,5,13]
+ CRUSH rule 0 x 428 [12,7,9,4,2,1,14,10]
+ CRUSH rule 0 x 429 [3,4,9,7,11,12,1,14]
+ CRUSH rule 0 x 430 [3,5,10,13,1,15,6]
+ CRUSH rule 0 x 431 [9,3,7,1,12,5,14,11]
+ CRUSH rule 0 x 432 [4,1,12,7,15,2,10,9]
+ CRUSH rule 0 x 433 [4,11,12,15,7,3]
+ CRUSH rule 0 x 434 [2,14,9,1,5,11,7,13]
+ CRUSH rule 0 x 435 [13,11,5,6,9,2,15]
+ CRUSH rule 0 x 436 [9,15,10,2,4,1,12,7]
+ CRUSH rule 0 x 437 [9,6,3,14,10,12,5,1]
+ CRUSH rule 0 x 438 [7,2,13,4,11,1,9,14]
+ CRUSH rule 0 x 439 [7,14,4,3,12,10]
+ CRUSH rule 0 x 440 [14,11,9,2,7,12,1,5]
+ CRUSH rule 0 x 441 [2,4,11,9,13,6,1,14]
+ CRUSH rule 0 x 442 [10,13,9,7,15,1,4,2]
+ CRUSH rule 0 x 443 [12,15,10,9,2,1,6,4]
+ CRUSH rule 0 x 444 [4,13,7,14,3,1,9,11]
+ CRUSH rule 0 x 445 [4,2,15,7,1,9,11,12]
+ CRUSH rule 0 x 446 [12,10,6,9,4,1,15,3]
+ CRUSH rule 0 x 447 [15,7,13,1,4,9,3,11]
+ CRUSH rule 0 x 448 [5,2,13,7,15,10]
+ CRUSH rule 0 x 449 [14,5,3,12,10,9,6]
+ CRUSH rule 0 x 450 [2,4,6,9,15,1,13,10]
+ CRUSH rule 0 x 451 [6,14,11,3,9,1,12,5]
+ CRUSH rule 0 x 452 [14,9,10,4,2,13,7]
+ CRUSH rule 0 x 453 [5,15,13,2,6,9,11]
+ CRUSH rule 0 x 454 [10,4,2,6,15,12,9,1]
+ CRUSH rule 0 x 455 [6,13,2,4,10,1,15]
+ CRUSH rule 0 x 456 [5,7,13,1,11,3,9,15]
+ CRUSH rule 0 x 457 [9,1,5,7,11,13,15,2]
+ CRUSH rule 0 x 458 [9,11,15,4,7,2,12,1]
+ CRUSH rule 0 x 459 [13,15,11,1,5,2,6]
+ CRUSH rule 0 x 460 [5,12,10,15,7,3,9]
+ CRUSH rule 0 x 461 [4,3,9,13,15,6,10]
+ CRUSH rule 0 x 462 [4,7,12,14,11,1,3,9]
+ CRUSH rule 0 x 463 [4,12,14,11,2,7,1,9]
+ CRUSH rule 0 x 464 [4,2,15,10,1,9,13,7]
+ CRUSH rule 0 x 465 [5,10,9,7,13,1,3,14]
+ CRUSH rule 0 x 466 [13,5,2,15,9,11,6,1]
+ CRUSH rule 0 x 467 [13,6,14,3,9,1,11,5]
+ CRUSH rule 0 x 468 [10,7,12,14,4,1,9,2]
+ CRUSH rule 0 x 469 [4,9,6,14,12,11,3,1]
+ CRUSH rule 0 x 470 [3,9,12,15,5,6,10]
+ CRUSH rule 0 x 471 [6,1,5,14,13,10,9,3]
+ CRUSH rule 0 x 472 [2,14,7,5,13,1,11,9]
+ CRUSH rule 0 x 473 [15,10,6,9,4,12,2]
+ CRUSH rule 0 x 474 [15,10,4,12,6,9,2,1]
+ CRUSH rule 0 x 475 [10,5,12,9,14,3,6,1]
+ CRUSH rule 0 x 476 [3,6,10,12,1,15,9,4]
+ CRUSH rule 0 x 477 [6,13,5,15,11,9,2,1]
+ CRUSH rule 0 x 478 [4,15,1,3,7,12,10,9]
+ CRUSH rule 0 x 479 [13,11,1,6,14,5,9,3]
+ CRUSH rule 0 x 480 [1,13,6,4,9,14,11,3]
+ CRUSH rule 0 x 481 [15,12,7,9,1,3,10,4]
+ CRUSH rule 0 x 482 [2,12,9,1,7,11,14,4]
+ CRUSH rule 0 x 483 [10,1,4,15,9,7,13,2]
+ CRUSH rule 0 x 484 [1,4,10,13,7,14,2,9]
+ CRUSH rule 0 x 485 [9,4,3,1,14,12,7,10]
+ CRUSH rule 0 x 486 [3,10,15,9,7,13,4,1]
+ CRUSH rule 0 x 487 [12,11,4,14,7,2,1]
+ CRUSH rule 0 x 488 [14,4,1,9,2,6,10,12]
+ CRUSH rule 0 x 489 [11,4,2,13,15,7]
+ CRUSH rule 0 x 490 [4,9,1,3,13,15,6,11]
+ CRUSH rule 0 x 491 [1,12,5,2,14,11,6]
+ CRUSH rule 0 x 492 [5,7,11,3,14,9,1,13]
+ CRUSH rule 0 x 493 [12,1,4,15,3,11,9,6]
+ CRUSH rule 0 x 494 [1,7,13,4,15,9,10,3]
+ CRUSH rule 0 x 495 [3,15,7,1,9,5,12,11]
+ CRUSH rule 0 x 496 [5,3,7,13,9,14,10]
+ CRUSH rule 0 x 497 [13,10,3,6,5,14,1]
+ CRUSH rule 0 x 498 [10,6,1,5,9,12,3,15]
+ CRUSH rule 0 x 499 [14,3,12,5,1,11,9,7]
+ CRUSH rule 0 x 500 [15,9,6,12,11,2,1,5]
+ CRUSH rule 0 x 501 [10,13,1,9,3,14,5,7]
+ CRUSH rule 0 x 502 [5,1,14,11,7,12,9,2]
+ CRUSH rule 0 x 503 [15,10,7,9,1,12,4,2]
+ CRUSH rule 0 x 504 [13,2,7,1,14,11,5]
+ CRUSH rule 0 x 505 [12,7,5,2,14,10,9]
+ CRUSH rule 0 x 506 [11,7,9,14,12,1,2,5]
+ CRUSH rule 0 x 507 [4,14,13,3,9,7,1,10]
+ CRUSH rule 0 x 508 [12,1,4,9,2,11,15,7]
+ CRUSH rule 0 x 509 [4,2,6,9,14,1,10,13]
+ CRUSH rule 0 x 510 [5,3,1,12,11,14,9,7]
+ CRUSH rule 0 x 511 [2,12,10,6,14,5]
+ CRUSH rule 0 x 512 [15,11,3,5,7,1,13]
+ CRUSH rule 0 x 513 [4,9,11,3,13,7,1,14]
+ CRUSH rule 0 x 514 [11,9,3,4,12,15,6,1]
+ CRUSH rule 0 x 515 [12,14,6,5,3,9,1,10]
+ CRUSH rule 0 x 516 [14,11,1,12,3,7,4,9]
+ CRUSH rule 0 x 517 [11,5,6,13,9,3,14]
+ CRUSH rule 0 x 518 [3,5,7,12,15,11,9,1]
+ CRUSH rule 0 x 519 [12,14,2,1,4,6,9,10]
+ CRUSH rule 0 x 520 [12,4,2,10,6,15,9]
+ CRUSH rule 0 x 521 [11,5,9,6,15,3,13]
+ CRUSH rule 0 x 522 [4,12,11,1,15,3,9,6]
+ CRUSH rule 0 x 523 [3,1,5,9,15,10,13,7]
+ CRUSH rule 0 x 524 [15,9,3,11,13,7,4,1]
+ CRUSH rule 0 x 525 [3,15,11,6,9,12,4]
+ CRUSH rule 0 x 526 [10,2,5,13,6,15,1,9]
+ CRUSH rule 0 x 527 [3,13,4,1,9,10,14,7]
+ CRUSH rule 0 x 528 [12,7,15,10,2,5,9]
+ CRUSH rule 0 x 529 [6,4,10,12,2,9,14]
+ CRUSH rule 0 x 530 [11,9,12,7,5,1,3,15]
+ CRUSH rule 0 x 531 [9,15,4,7,2,13,1,11]
+ CRUSH rule 0 x 532 [5,3,13,7,9,14,1,10]
+ CRUSH rule 0 x 533 [12,15,1,2,7,5,10]
+ CRUSH rule 0 x 534 [11,9,3,7,15,4,1,12]
+ CRUSH rule 0 x 535 [11,1,3,5,14,9,12,7]
+ CRUSH rule 0 x 536 [9,1,14,13,4,6,2,11]
+ CRUSH rule 0 x 537 [15,5,13,2,7,11]
+ CRUSH rule 0 x 538 [13,5,11,2,6,15,9]
+ CRUSH rule 0 x 539 [10,12,6,14,1,2,9,5]
+ CRUSH rule 0 x 540 [12,15,7,3,9,11,1,4]
+ CRUSH rule 0 x 541 [2,1,6,11,14,13,4]
+ CRUSH rule 0 x 542 [3,9,15,5,11,12,7,1]
+ CRUSH rule 0 x 543 [4,10,9,3,6,13,14]
+ CRUSH rule 0 x 544 [3,15,9,11,7,4,12,1]
+ CRUSH rule 0 x 545 [14,10,7,12,4,9,1,3]
+ CRUSH rule 0 x 546 [5,15,13,7,1,10,9,2]
+ CRUSH rule 0 x 547 [5,13,7,9,3,14,10]
+ CRUSH rule 0 x 548 [11,7,12,15,4,2]
+ CRUSH rule 0 x 549 [14,1,4,9,13,6,3,10]
+ CRUSH rule 0 x 550 [9,15,3,13,1,6,4,11]
+ CRUSH rule 0 x 551 [11,2,15,6,13,5,1]
+ CRUSH rule 0 x 552 [2,11,14,1,9,6,5,12]
+ CRUSH rule 0 x 553 [11,9,14,6,4,13,3]
+ CRUSH rule 0 x 554 [11,14,6,4,13,9,3,1]
+ CRUSH rule 0 x 555 [6,5,10,9,14,2,13,1]
+ CRUSH rule 0 x 556 [15,6,3,13,11,4,1,9]
+ CRUSH rule 0 x 557 [12,2,5,14,10,9,6]
+ CRUSH rule 0 x 558 [12,1,6,15,5,10,3]
+ CRUSH rule 0 x 559 [2,13,5,10,14,7,1]
+ CRUSH rule 0 x 560 [4,9,12,6,3,10,1,15]
+ CRUSH rule 0 x 561 [12,7,1,2,5,15,11,9]
+ CRUSH rule 0 x 562 [7,13,9,14,2,1,11,4]
+ CRUSH rule 0 x 563 [15,4,3,10,13,9,7]
+ CRUSH rule 0 x 564 [2,13,7,1,15,10,4]
+ CRUSH rule 0 x 565 [3,12,4,1,14,7,11]
+ CRUSH rule 0 x 566 [6,14,4,2,13,11]
+ CRUSH rule 0 x 567 [15,4,11,6,3,12]
+ CRUSH rule 0 x 568 [4,14,1,6,10,13,3,9]
+ CRUSH rule 0 x 569 [11,3,15,13,5,1,9,7]
+ CRUSH rule 0 x 570 [1,10,13,4,7,2,9,14]
+ CRUSH rule 0 x 571 [10,12,14,9,4,2,1,6]
+ CRUSH rule 0 x 572 [12,14,3,10,6,1,4,9]
+ CRUSH rule 0 x 573 [7,15,11,2,12,9,4,1]
+ CRUSH rule 0 x 574 [11,14,13,1,3,7,4,9]
+ CRUSH rule 0 x 575 [5,13,15,9,6,10,2]
+ CRUSH rule 0 x 576 [3,15,11,9,1,6,5,13]
+ CRUSH rule 0 x 577 [13,9,6,15,3,11,4,1]
+ CRUSH rule 0 x 578 [4,10,1,2,7,13,14,9]
+ CRUSH rule 0 x 579 [13,1,15,2,10,7,5,9]
+ CRUSH rule 0 x 580 [3,12,4,1,10,15,7,9]
+ CRUSH rule 0 x 581 [7,14,12,10,1,2,9,5]
+ CRUSH rule 0 x 582 [10,5,13,14,1,2,7]
+ CRUSH rule 0 x 583 [4,15,1,9,10,12,2,6]
+ CRUSH rule 0 x 584 [10,1,5,13,6,9,2,15]
+ CRUSH rule 0 x 585 [5,3,6,1,11,14,13,9]
+ CRUSH rule 0 x 586 [7,10,14,12,9,3,5,1]
+ CRUSH rule 0 x 587 [11,6,9,4,1,14,13,2]
+ CRUSH rule 0 x 588 [3,12,7,15,4,9,1,10]
+ CRUSH rule 0 x 589 [9,7,12,1,10,3,4,15]
+ CRUSH rule 0 x 590 [12,1,3,9,10,6,4,14]
+ CRUSH rule 0 x 591 [2,6,14,13,9,11,4]
+ CRUSH rule 0 x 592 [15,12,9,7,5,2,11,1]
+ CRUSH rule 0 x 593 [13,14,5,11,9,6,2]
+ CRUSH rule 0 x 594 [12,14,2,9,7,4,1,11]
+ CRUSH rule 0 x 595 [12,7,10,3,1,14,9,4]
+ CRUSH rule 0 x 596 [2,7,12,11,1,5,15,9]
+ CRUSH rule 0 x 597 [15,1,2,10,7,13,5,9]
+ CRUSH rule 0 x 598 [11,5,9,14,12,7,3]
+ CRUSH rule 0 x 599 [13,11,1,5,6,2,15,9]
+ CRUSH rule 0 x 600 [4,12,3,10,9,7,1,14]
+ CRUSH rule 0 x 601 [13,5,15,2,1,7,9,10]
+ CRUSH rule 0 x 602 [3,11,7,1,13,15,5,9]
+ CRUSH rule 0 x 603 [3,1,4,14,10,9,6,12]
+ CRUSH rule 0 x 604 [14,2,6,1,11,13,9,4]
+ CRUSH rule 0 x 605 [2,7,12,5,14,10,1,9]
+ CRUSH rule 0 x 606 [12,15,1,5,7,9,3,11]
+ CRUSH rule 0 x 607 [3,9,10,14,7,1,4,12]
+ CRUSH rule 0 x 608 [13,10,1,7,9,15,5,2]
+ CRUSH rule 0 x 609 [14,3,7,9,11,12,5]
+ CRUSH rule 0 x 610 [7,10,5,1,12,2,15]
+ CRUSH rule 0 x 611 [13,1,5,3,10,7,15,9]
+ CRUSH rule 0 x 612 [7,1,2,13,9,15,4,11]
+ CRUSH rule 0 x 613 [10,7,14,9,5,2,13]
+ CRUSH rule 0 x 614 [9,4,15,3,1,11,6,12]
+ CRUSH rule 0 x 615 [9,4,11,2,1,12,6,15]
+ CRUSH rule 0 x 616 [10,14,1,5,3,6,12,9]
+ CRUSH rule 0 x 617 [15,7,2,11,12,1,9,4]
+ CRUSH rule 0 x 618 [4,2,10,6,14,9,1,12]
+ CRUSH rule 0 x 619 [15,4,3,9,6,1,13,11]
+ CRUSH rule 0 x 620 [3,7,11,14,13,1,5]
+ CRUSH rule 0 x 621 [3,6,4,14,1,11,13]
+ CRUSH rule 0 x 622 [10,2,13,5,15,9,1,7]
+ CRUSH rule 0 x 623 [4,9,14,7,3,13,11]
+ CRUSH rule 0 x 624 [3,9,15,6,10,1,5,12]
+ CRUSH rule 0 x 625 [11,7,3,5,13,15,9]
+ CRUSH rule 0 x 626 [10,12,2,1,9,7,5,14]
+ CRUSH rule 0 x 627 [1,12,10,14,3,5,9,7]
+ CRUSH rule 0 x 628 [15,13,11,4,2,1,7,9]
+ CRUSH rule 0 x 629 [5,6,15,12,1,10,3,9]
+ CRUSH rule 0 x 630 [1,4,12,9,3,7,15,11]
+ CRUSH rule 0 x 631 [5,7,1,15,12,11,3,9]
+ CRUSH rule 0 x 632 [12,3,11,9,6,1,15,5]
+ CRUSH rule 0 x 633 [14,4,3,7,10,12,9]
+ CRUSH rule 0 x 634 [6,9,5,3,13,11,14]
+ CRUSH rule 0 x 635 [6,5,2,15,9,12,11]
+ CRUSH rule 0 x 636 [13,6,11,3,15,9,1,4]
+ CRUSH rule 0 x 637 [3,1,10,6,9,12,4,14]
+ CRUSH rule 0 x 638 [10,15,3,5,13,1,7]
+ CRUSH rule 0 x 639 [6,9,14,4,3,1,10,13]
+ CRUSH rule 0 x 640 [9,6,1,11,14,2,4,13]
+ CRUSH rule 0 x 641 [10,6,5,14,1,9,12,2]
+ CRUSH rule 0 x 642 [1,15,4,6,2,10,9,12]
+ CRUSH rule 0 x 643 [3,7,5,1,10,15,13]
+ CRUSH rule 0 x 644 [15,13,6,9,3,11,5]
+ CRUSH rule 0 x 645 [14,2,4,9,10,1,7,13]
+ CRUSH rule 0 x 646 [5,13,14,1,6,9,2,11]
+ CRUSH rule 0 x 647 [10,1,9,13,6,2,14,5]
+ CRUSH rule 0 x 648 [6,5,2,14,11,1,12,9]
+ CRUSH rule 0 x 649 [3,9,13,11,4,14,1,7]
+ CRUSH rule 0 x 650 [10,9,4,15,12,7,1,2]
+ CRUSH rule 0 x 651 [3,9,5,7,14,1,13,11]
+ CRUSH rule 0 x 652 [15,9,4,6,13,1,2,11]
+ CRUSH rule 0 x 653 [11,14,1,3,6,9,12,5]
+ CRUSH rule 0 x 654 [13,6,2,10,15,4,1,9]
+ CRUSH rule 0 x 655 [6,3,4,15,12,11,1]
+ CRUSH rule 0 x 656 [3,15,1,4,6,12,11]
+ CRUSH rule 0 x 657 [11,15,3,5,7,13,1,9]
+ CRUSH rule 0 x 658 [7,2,10,12,1,4,9,14]
+ CRUSH rule 0 x 659 [2,5,14,6,10,12]
+ CRUSH rule 0 x 660 [13,14,10,6,4,9,3]
+ CRUSH rule 0 x 661 [7,15,3,12,11,4,9,1]
+ CRUSH rule 0 x 662 [15,2,12,5,1,10,9,7]
+ CRUSH rule 0 x 663 [14,9,13,10,5,3,1,6]
+ CRUSH rule 0 x 664 [6,10,12,4,9,2,1,15]
+ CRUSH rule 0 x 665 [2,9,12,1,7,10,4,15]
+ CRUSH rule 0 x 666 [12,3,6,1,15,9,10,4]
+ CRUSH rule 0 x 667 [1,9,12,10,2,14,7,4]
+ CRUSH rule 0 x 668 [9,5,1,2,6,11,13,15]
+ CRUSH rule 0 x 669 [9,7,14,5,11,13,1,2]
+ CRUSH rule 0 x 670 [6,10,9,13,1,2,15,4]
+ CRUSH rule 0 x 671 [6,15,5,10,13,3]
+ CRUSH rule 0 x 672 [2,9,13,1,4,14,6,10]
+ CRUSH rule 0 x 673 [7,10,5,9,15,13,2,1]
+ CRUSH rule 0 x 674 [7,12,10,1,14,9,3,4]
+ CRUSH rule 0 x 675 [9,5,1,10,6,14,12,2]
+ CRUSH rule 0 x 676 [10,12,2,1,4,15,7]
+ CRUSH rule 0 x 677 [2,12,1,4,10,6,15,9]
+ CRUSH rule 0 x 678 [1,2,4,10,12,14,9,6]
+ CRUSH rule 0 x 679 [5,6,12,15,9,11,3]
+ CRUSH rule 0 x 680 [7,11,3,1,15,4,9,12]
+ CRUSH rule 0 x 681 [6,4,3,11,14,13,1,9]
+ CRUSH rule 0 x 682 [6,1,11,15,12,2,5,9]
+ CRUSH rule 0 x 683 [6,13,2,4,9,14,10,1]
+ CRUSH rule 0 x 684 [9,11,3,7,15,4,13]
+ CRUSH rule 0 x 685 [5,1,15,7,9,2,10,13]
+ CRUSH rule 0 x 686 [1,9,11,14,6,13,4,3]
+ CRUSH rule 0 x 687 [7,13,3,5,11,9,15,1]
+ CRUSH rule 0 x 688 [11,9,1,14,3,5,7,12]
+ CRUSH rule 0 x 689 [5,2,9,12,1,14,11,7]
+ CRUSH rule 0 x 690 [9,7,10,3,13,15,5,1]
+ CRUSH rule 0 x 691 [11,15,9,5,7,13,2]
+ CRUSH rule 0 x 692 [15,5,1,2,9,11,12,7]
+ CRUSH rule 0 x 693 [5,6,12,15,2,10,9,1]
+ CRUSH rule 0 x 694 [4,7,1,10,12,3,14]
+ CRUSH rule 0 x 695 [6,13,14,10,9,5,1,3]
+ CRUSH rule 0 x 696 [1,2,4,14,7,11,13]
+ CRUSH rule 0 x 697 [13,11,3,6,4,14,9,1]
+ CRUSH rule 0 x 698 [11,13,4,2,6,1,9,15]
+ CRUSH rule 0 x 699 [7,14,12,4,2,11]
+ CRUSH rule 0 x 700 [12,14,11,9,4,6,3,1]
+ CRUSH rule 0 x 701 [3,13,1,14,4,7,11]
+ CRUSH rule 0 x 702 [3,12,15,6,5,11,1,9]
+ CRUSH rule 0 x 703 [15,11,13,3,4,7,1,9]
+ CRUSH rule 0 x 704 [6,4,2,15,11,1,13,9]
+ CRUSH rule 0 x 705 [14,6,11,5,1,13,9,3]
+ CRUSH rule 0 x 706 [1,12,3,6,4,10,15,9]
+ CRUSH rule 0 x 707 [4,7,14,3,10,9,13]
+ CRUSH rule 0 x 708 [3,10,5,1,15,9,7,13]
+ CRUSH rule 0 x 709 [11,12,3,7,5,14,1,9]
+ CRUSH rule 0 x 710 [14,2,11,9,5,7,12,1]
+ CRUSH rule 0 x 711 [14,3,9,10,12,5,6,1]
+ CRUSH rule 0 x 712 [12,3,11,15,9,1,6,4]
+ CRUSH rule 0 x 713 [11,9,3,15,13,6,4,1]
+ CRUSH rule 0 x 714 [12,1,9,7,2,15,10,5]
+ CRUSH rule 0 x 715 [6,1,14,4,11,12,3,9]
+ CRUSH rule 0 x 716 [11,13,9,14,5,2,1,7]
+ CRUSH rule 0 x 717 [12,4,10,9,15,1,2,7]
+ CRUSH rule 0 x 718 [7,15,5,2,11,13]
+ CRUSH rule 0 x 719 [5,15,13,3,1,7,11]
+ CRUSH rule 0 x 720 [4,13,10,2,7,9,1,14]
+ CRUSH rule 0 x 721 [11,3,14,9,1,12,4,6]
+ CRUSH rule 0 x 722 [2,4,6,1,9,15,13,10]
+ CRUSH rule 0 x 723 [2,1,12,15,11,7,5,9]
+ CRUSH rule 0 x 724 [7,1,9,10,5,15,13,2]
+ CRUSH rule 0 x 725 [11,12,7,15,4,1,2]
+ CRUSH rule 0 x 726 [7,14,4,3,11,13,9,1]
+ CRUSH rule 0 x 727 [2,5,1,11,15,7,12]
+ CRUSH rule 0 x 728 [13,11,4,6,15,2]
+ CRUSH rule 0 x 729 [15,11,4,6,2,9,1,13]
+ CRUSH rule 0 x 730 [3,7,1,13,11,15,9,5]
+ CRUSH rule 0 x 731 [9,1,6,5,2,11,13,15]
+ CRUSH rule 0 x 732 [1,2,10,13,9,4,7,15]
+ CRUSH rule 0 x 733 [11,3,5,6,1,9,12,15]
+ CRUSH rule 0 x 734 [14,3,11,7,12,9,4,1]
+ CRUSH rule 0 x 735 [6,9,2,10,13,14,5]
+ CRUSH rule 0 x 736 [3,9,1,11,7,5,13,14]
+ CRUSH rule 0 x 737 [1,4,2,12,9,10,6,15]
+ CRUSH rule 0 x 738 [11,15,7,4,9,2,12]
+ CRUSH rule 0 x 739 [11,12,6,2,4,1,14]
+ CRUSH rule 0 x 740 [7,9,10,13,1,15,2,5]
+ CRUSH rule 0 x 741 [12,11,7,15,2,5]
+ CRUSH rule 0 x 742 [9,7,4,11,12,1,14,3]
+ CRUSH rule 0 x 743 [5,13,9,15,10,7,3]
+ CRUSH rule 0 x 744 [6,2,13,1,14,11,4]
+ CRUSH rule 0 x 745 [3,6,1,4,11,12,14,9]
+ CRUSH rule 0 x 746 [3,7,9,10,14,5,1,13]
+ CRUSH rule 0 x 747 [15,11,5,2,13,9,1,7]
+ CRUSH rule 0 x 748 [6,10,13,2,14,5,9,1]
+ CRUSH rule 0 x 749 [14,9,10,7,5,1,2,12]
+ CRUSH rule 0 x 750 [1,14,6,5,11,2,13]
+ CRUSH rule 0 x 751 [15,1,6,9,5,11,12,3]
+ CRUSH rule 0 x 752 [13,1,7,3,11,15,9,4]
+ CRUSH rule 0 x 753 [4,11,1,3,15,7,13]
+ CRUSH rule 0 x 754 [14,12,11,4,2,1,9,6]
+ CRUSH rule 0 x 755 [13,6,1,10,4,2,14,9]
+ CRUSH rule 0 x 756 [3,4,14,6,1,10,13,9]
+ CRUSH rule 0 x 757 [10,6,1,4,13,15,2]
+ CRUSH rule 0 x 758 [6,3,4,10,15,13,9,1]
+ CRUSH rule 0 x 759 [5,7,3,14,11,1,9,13]
+ CRUSH rule 0 x 760 [1,15,10,12,4,3,9,7]
+ CRUSH rule 0 x 761 [2,12,1,14,5,7,10]
+ CRUSH rule 0 x 762 [1,4,10,9,3,7,14,12]
+ CRUSH rule 0 x 763 [4,13,1,14,7,10,2,9]
+ CRUSH rule 0 x 764 [1,14,6,13,9,5,2,10]
+ CRUSH rule 0 x 765 [9,15,2,13,4,1,11,7]
+ CRUSH rule 0 x 766 [11,2,7,15,9,12,4]
+ CRUSH rule 0 x 767 [6,11,4,3,12,14]
+ CRUSH rule 0 x 768 [2,12,15,7,1,11,9,4]
+ CRUSH rule 0 x 769 [15,1,9,2,11,12,7,4]
+ CRUSH rule 0 x 770 [15,13,4,6,3,10,1,9]
+ CRUSH rule 0 x 771 [9,2,12,11,6,14,5,1]
+ CRUSH rule 0 x 772 [4,3,13,11,14,1,7]
+ CRUSH rule 0 x 773 [3,7,4,15,1,12,11,9]
+ CRUSH rule 0 x 774 [12,6,3,15,5,9,10,1]
+ CRUSH rule 0 x 775 [5,10,14,2,6,1,13]
+ CRUSH rule 0 x 776 [10,15,3,9,6,13,1,5]
+ CRUSH rule 0 x 777 [11,13,4,7,1,14,9,2]
+ CRUSH rule 0 x 778 [13,1,9,11,15,6,3,5]
+ CRUSH rule 0 x 779 [5,11,1,14,2,9,13,6]
+ CRUSH rule 0 x 780 [13,9,3,6,4,1,14,10]
+ CRUSH rule 0 x 781 [5,7,14,3,1,12,11,9]
+ CRUSH rule 0 x 782 [2,15,9,7,11,13,4,1]
+ CRUSH rule 0 x 783 [12,7,5,14,9,1,2,10]
+ CRUSH rule 0 x 784 [14,1,10,13,3,4,7,9]
+ CRUSH rule 0 x 785 [6,12,1,2,4,9,15,10]
+ CRUSH rule 0 x 786 [10,5,2,15,1,7,12,9]
+ CRUSH rule 0 x 787 [1,12,10,2,9,4,14,6]
+ CRUSH rule 0 x 788 [4,2,9,13,6,15,11]
+ CRUSH rule 0 x 789 [9,2,14,7,4,12,1,10]
+ CRUSH rule 0 x 790 [15,2,7,4,1,10,13]
+ CRUSH rule 0 x 791 [9,4,7,13,14,11,1,3]
+ CRUSH rule 0 x 792 [6,4,15,10,12,3]
+ CRUSH rule 0 x 793 [15,9,6,2,13,11,4]
+ CRUSH rule 0 x 794 [5,12,2,14,9,10,1,6]
+ CRUSH rule 0 x 795 [6,14,12,4,10,1,2,9]
+ CRUSH rule 0 x 796 [11,2,12,6,15,4]
+ CRUSH rule 0 x 797 [14,3,7,1,5,13,11]
+ CRUSH rule 0 x 798 [5,11,6,13,1,3,15,9]
+ CRUSH rule 0 x 799 [2,9,14,4,13,6,11]
+ CRUSH rule 0 x 800 [6,3,4,11,15,13]
+ CRUSH rule 0 x 801 [2,5,6,13,9,1,10,15]
+ CRUSH rule 0 x 802 [1,4,12,7,3,9,10,14]
+ CRUSH rule 0 x 803 [7,2,4,1,11,13,9,14]
+ CRUSH rule 0 x 804 [5,14,9,7,3,1,12,10]
+ CRUSH rule 0 x 805 [13,4,3,1,10,15,7]
+ CRUSH rule 0 x 806 [6,2,13,4,15,1,10]
+ CRUSH rule 0 x 807 [14,2,7,4,9,12,1,10]
+ CRUSH rule 0 x 808 [2,15,12,7,9,1,5,10]
+ CRUSH rule 0 x 809 [1,11,7,12,4,2,15,9]
+ CRUSH rule 0 x 810 [2,5,9,12,15,1,7,11]
+ CRUSH rule 0 x 811 [15,6,3,10,1,5,9,12]
+ CRUSH rule 0 x 812 [7,11,2,14,9,5,12]
+ CRUSH rule 0 x 813 [4,10,13,14,2,6,9]
+ CRUSH rule 0 x 814 [13,4,9,3,10,6,15]
+ CRUSH rule 0 x 815 [15,12,9,4,10,6,1,2]
+ CRUSH rule 0 x 816 [14,10,13,7,3,9,4,1]
+ CRUSH rule 0 x 817 [10,7,2,15,13,9,5]
+ CRUSH rule 0 x 818 [15,2,11,4,1,12,6,9]
+ CRUSH rule 0 x 819 [5,12,10,6,1,14,3]
+ CRUSH rule 0 x 820 [3,6,9,12,11,15,4,1]
+ CRUSH rule 0 x 821 [15,10,9,13,3,4,7,1]
+ CRUSH rule 0 x 822 [10,13,2,9,7,4,14,1]
+ CRUSH rule 0 x 823 [2,6,12,10,15,4,1,9]
+ CRUSH rule 0 x 824 [3,7,9,13,15,5,10]
+ CRUSH rule 0 x 825 [10,5,14,6,12,9,3]
+ CRUSH rule 0 x 826 [5,2,11,15,1,12,9,7]
+ CRUSH rule 0 x 827 [13,5,1,3,7,9,11,14]
+ CRUSH rule 0 x 828 [12,6,10,5,1,9,2,15]
+ CRUSH rule 0 x 829 [13,6,15,10,5,3,9]
+ CRUSH rule 0 x 830 [15,13,2,9,7,11,1,5]
+ CRUSH rule 0 x 831 [1,4,11,12,6,3,15]
+ CRUSH rule 0 x 832 [14,11,13,2,9,4,6,1]
+ CRUSH rule 0 x 833 [9,13,3,11,7,5,15,1]
+ CRUSH rule 0 x 834 [9,7,5,1,11,2,13,14]
+ CRUSH rule 0 x 835 [14,3,13,6,4,9,1,10]
+ CRUSH rule 0 x 836 [3,9,10,13,1,5,14,7]
+ CRUSH rule 0 x 837 [15,12,11,2,7,9,5]
+ CRUSH rule 0 x 838 [12,14,9,2,5,7,11]
+ CRUSH rule 0 x 839 [3,4,6,10,15,1,13,9]
+ CRUSH rule 0 x 840 [10,15,12,4,7,1,2,9]
+ CRUSH rule 0 x 841 [3,5,7,12,11,15,1,9]
+ CRUSH rule 0 x 842 [9,13,2,6,5,14,10,1]
+ CRUSH rule 0 x 843 [14,7,4,9,3,12,1,10]
+ CRUSH rule 0 x 844 [7,1,4,15,9,2,11,12]
+ CRUSH rule 0 x 845 [13,6,1,15,4,2,11]
+ CRUSH rule 0 x 846 [3,7,15,13,1,9,10,4]
+ CRUSH rule 0 x 847 [12,15,11,5,2,7,1]
+ CRUSH rule 0 x 848 [11,13,1,14,5,9,2,7]
+ CRUSH rule 0 x 849 [3,15,11,9,6,1,13,5]
+ CRUSH rule 0 x 850 [1,3,10,6,14,4,9,12]
+ CRUSH rule 0 x 851 [14,4,3,6,11,1,13]
+ CRUSH rule 0 x 852 [9,12,4,7,15,2,11,1]
+ CRUSH rule 0 x 853 [13,14,6,11,2,4,9,1]
+ CRUSH rule 0 x 854 [7,11,12,1,4,15,3]
+ CRUSH rule 0 x 855 [14,4,12,6,3,1,10]
+ CRUSH rule 0 x 856 [5,10,7,3,15,9,12,1]
+ CRUSH rule 0 x 857 [4,3,13,11,9,1,7,14]
+ CRUSH rule 0 x 858 [5,15,6,3,9,12,1,10]
+ CRUSH rule 0 x 859 [5,15,6,2,1,11,12,9]
+ CRUSH rule 0 x 860 [11,14,1,12,6,9,2,4]
+ CRUSH rule 0 x 861 [13,7,4,10,1,14,3,9]
+ CRUSH rule 0 x 862 [5,10,9,7,3,12,1,15]
+ CRUSH rule 0 x 863 [11,6,3,9,4,12,15]
+ CRUSH rule 0 x 864 [6,13,4,2,10,15,1,9]
+ CRUSH rule 0 x 865 [4,1,14,11,6,9,3,13]
+ CRUSH rule 0 x 866 [2,13,4,15,9,6,11]
+ CRUSH rule 0 x 867 [12,2,9,10,4,14,6]
+ CRUSH rule 0 x 868 [14,11,7,2,1,4,9,12]
+ CRUSH rule 0 x 869 [10,13,7,14,3,5,1]
+ CRUSH rule 0 x 870 [14,9,11,4,3,12,6,1]
+ CRUSH rule 0 x 871 [6,2,1,4,15,13,11,9]
+ CRUSH rule 0 x 872 [6,1,15,3,10,12,5]
+ CRUSH rule 0 x 873 [2,5,12,10,1,9,15,7]
+ CRUSH rule 0 x 874 [12,4,7,2,15,10,1]
+ CRUSH rule 0 x 875 [10,6,14,1,12,5,9,3]
+ CRUSH rule 0 x 876 [14,7,13,3,9,1,11,4]
+ CRUSH rule 0 x 877 [15,11,13,9,5,1,6,3]
+ CRUSH rule 0 x 878 [7,14,3,13,9,1,11,4]
+ CRUSH rule 0 x 879 [12,2,7,4,10,15]
+ CRUSH rule 0 x 880 [2,12,10,7,1,4,9,14]
+ CRUSH rule 0 x 881 [6,3,1,11,4,15,9,13]
+ CRUSH rule 0 x 882 [11,13,7,1,2,15,4,9]
+ CRUSH rule 0 x 883 [13,1,3,10,6,5,9,15]
+ CRUSH rule 0 x 884 [6,15,4,9,3,11,12,1]
+ CRUSH rule 0 x 885 [14,7,9,4,2,13,11]
+ CRUSH rule 0 x 886 [13,11,4,2,1,14,9,6]
+ CRUSH rule 0 x 887 [14,4,12,11,2,6,9,1]
+ CRUSH rule 0 x 888 [10,12,7,15,9,2,1,5]
+ CRUSH rule 0 x 889 [15,13,4,1,6,2,10,9]
+ CRUSH rule 0 x 890 [10,12,14,2,9,5,6,1]
+ CRUSH rule 0 x 891 [9,5,11,6,3,15,12,1]
+ CRUSH rule 0 x 892 [12,15,2,4,7,9,11,1]
+ CRUSH rule 0 x 893 [1,3,5,9,6,10,14,12]
+ CRUSH rule 0 x 894 [7,2,11,13,4,1,14]
+ CRUSH rule 0 x 895 [2,1,11,5,7,15,13,9]
+ CRUSH rule 0 x 896 [9,1,14,10,4,12,2,7]
+ CRUSH rule 0 x 897 [7,5,14,3,1,9,11,12]
+ CRUSH rule 0 x 898 [10,6,12,9,15,5,2,1]
+ CRUSH rule 0 x 899 [1,11,5,3,13,14,9,6]
+ CRUSH rule 0 x 900 [2,9,10,7,13,14,5,1]
+ CRUSH rule 0 x 901 [9,12,11,3,14,4,1,6]
+ CRUSH rule 0 x 902 [4,2,6,15,12,10,1]
+ CRUSH rule 0 x 903 [14,10,3,1,12,6,5]
+ CRUSH rule 0 x 904 [15,12,4,9,6,3,11]
+ CRUSH rule 0 x 905 [12,6,11,3,9,4,15]
+ CRUSH rule 0 x 906 [14,11,12,2,4,9,6]
+ CRUSH rule 0 x 907 [7,12,3,9,10,5,14,1]
+ CRUSH rule 0 x 908 [2,15,9,6,10,13,5,1]
+ CRUSH rule 0 x 909 [10,14,1,13,2,9,7,4]
+ CRUSH rule 0 x 910 [12,7,4,15,10,3,1,9]
+ CRUSH rule 0 x 911 [11,15,2,4,9,13,6,1]
+ CRUSH rule 0 x 912 [6,4,14,13,3,1,11]
+ CRUSH rule 0 x 913 [4,6,10,1,12,3,9,14]
+ CRUSH rule 0 x 914 [4,15,2,10,1,13,7]
+ CRUSH rule 0 x 915 [12,14,1,9,4,3,11,6]
+ CRUSH rule 0 x 916 [3,1,11,5,6,13,14]
+ CRUSH rule 0 x 917 [1,15,6,5,10,3,13,9]
+ CRUSH rule 0 x 918 [7,14,11,4,9,2,13]
+ CRUSH rule 0 x 919 [10,7,3,13,15,1,4]
+ CRUSH rule 0 x 920 [4,2,10,15,1,13,6]
+ CRUSH rule 0 x 921 [1,11,6,13,4,2,9,14]
+ CRUSH rule 0 x 922 [6,4,14,13,3,1,10,9]
+ CRUSH rule 0 x 923 [12,2,5,14,10,1,9,6]
+ CRUSH rule 0 x 924 [6,2,14,13,9,1,11,5]
+ CRUSH rule 0 x 925 [12,15,2,10,1,5,7]
+ CRUSH rule 0 x 926 [3,13,10,1,14,9,6,5]
+ CRUSH rule 0 x 927 [6,5,1,11,14,2,13,9]
+ CRUSH rule 0 x 928 [13,1,3,9,6,11,15,5]
+ CRUSH rule 0 x 929 [10,7,1,5,2,12,9,14]
+ CRUSH rule 0 x 930 [7,15,10,5,1,13,2]
+ CRUSH rule 0 x 931 [6,15,11,9,5,3,1,13]
+ CRUSH rule 0 x 932 [13,2,5,11,9,1,6,15]
+ CRUSH rule 0 x 933 [12,7,14,10,4,1,2,9]
+ CRUSH rule 0 x 934 [12,2,5,7,9,1,15,11]
+ CRUSH rule 0 x 935 [6,11,1,14,5,13,3,9]
+ CRUSH rule 0 x 936 [9,12,7,5,1,2,14,11]
+ CRUSH rule 0 x 937 [14,2,11,1,13,4,9,6]
+ CRUSH rule 0 x 938 [14,3,5,11,7,9,13]
+ CRUSH rule 0 x 939 [6,4,14,9,12,1,11,2]
+ CRUSH rule 0 x 940 [13,11,4,2,1,6,15]
+ CRUSH rule 0 x 941 [3,12,4,7,14,10]
+ CRUSH rule 0 x 942 [15,12,10,4,1,9,3,7]
+ CRUSH rule 0 x 943 [10,2,4,9,6,15,12]
+ CRUSH rule 0 x 944 [2,9,4,7,1,14,12,11]
+ CRUSH rule 0 x 945 [10,15,2,9,5,12,7]
+ CRUSH rule 0 x 946 [11,15,7,12,5,9,2]
+ CRUSH rule 0 x 947 [11,3,14,1,12,5,6,9]
+ CRUSH rule 0 x 948 [7,13,11,5,14,2,1,9]
+ CRUSH rule 0 x 949 [9,1,12,5,15,10,2,6]
+ CRUSH rule 0 x 950 [9,15,13,6,4,2,10]
+ CRUSH rule 0 x 951 [2,6,12,9,10,4,14]
+ CRUSH rule 0 x 952 [9,7,15,3,5,13,11]
+ CRUSH rule 0 x 953 [1,3,6,10,12,14,4,9]
+ CRUSH rule 0 x 954 [10,2,14,9,4,6,12,1]
+ CRUSH rule 0 x 955 [7,14,3,1,10,4,9,12]
+ CRUSH rule 0 x 956 [1,6,11,5,14,3,9,13]
+ CRUSH rule 0 x 957 [14,11,1,12,6,9,4,3]
+ CRUSH rule 0 x 958 [15,4,3,11,1,6,12,9]
+ CRUSH rule 0 x 959 [2,1,12,15,10,9,4,6]
+ CRUSH rule 0 x 960 [2,6,11,13,15,4,9]
+ CRUSH rule 0 x 961 [3,13,11,9,6,1,4,15]
+ CRUSH rule 0 x 962 [5,11,3,14,1,6,13,9]
+ CRUSH rule 0 x 963 [13,10,15,4,6,9,1,3]
+ CRUSH rule 0 x 964 [7,11,4,9,2,12,1,15]
+ CRUSH rule 0 x 965 [12,2,9,7,4,15,11,1]
+ CRUSH rule 0 x 966 [12,14,9,4,1,2,11,7]
+ CRUSH rule 0 x 967 [7,5,3,10,12,14]
+ CRUSH rule 0 x 968 [12,15,4,9,11,6,3]
+ CRUSH rule 0 x 969 [11,4,7,1,9,14,13,2]
+ CRUSH rule 0 x 970 [5,12,10,1,3,14,9,6]
+ CRUSH rule 0 x 971 [1,9,4,12,7,2,10,15]
+ CRUSH rule 0 x 972 [12,3,14,5,1,9,7,11]
+ CRUSH rule 0 x 973 [1,10,4,12,2,7,15]
+ CRUSH rule 0 x 974 [7,11,1,2,15,4,12,9]
+ CRUSH rule 0 x 975 [7,9,15,12,2,11,4]
+ CRUSH rule 0 x 976 [7,3,15,5,12,11,1]
+ CRUSH rule 0 x 977 [14,3,6,10,4,1,12]
+ CRUSH rule 0 x 978 [12,5,11,1,15,3,6]
+ CRUSH rule 0 x 979 [5,1,13,6,15,10,3,9]
+ CRUSH rule 0 x 980 [15,11,5,6,1,3,13,9]
+ CRUSH rule 0 x 981 [5,11,15,12,7,1,2]
+ CRUSH rule 0 x 982 [2,6,14,11,12,9,5]
+ CRUSH rule 0 x 983 [3,12,10,9,14,5,6]
+ CRUSH rule 0 x 984 [15,13,1,10,2,5,7]
+ CRUSH rule 0 x 985 [11,2,15,1,4,13,6,9]
+ CRUSH rule 0 x 986 [6,13,9,1,15,10,5,2]
+ CRUSH rule 0 x 987 [13,14,5,10,6,1,3,9]
+ CRUSH rule 0 x 988 [12,9,10,14,3,1,4,7]
+ CRUSH rule 0 x 989 [7,4,3,15,9,13,10,1]
+ CRUSH rule 0 x 990 [1,10,9,13,3,4,6,15]
+ CRUSH rule 0 x 991 [7,11,1,14,2,5,9,12]
+ CRUSH rule 0 x 992 [9,10,2,13,7,4,1,15]
+ CRUSH rule 0 x 993 [6,10,14,12,4,1,2]
+ CRUSH rule 0 x 994 [3,13,15,4,11,7,1,9]
+ CRUSH rule 0 x 995 [15,6,12,2,5,11]
+ CRUSH rule 0 x 996 [15,10,5,3,13,1,9,7]
+ CRUSH rule 0 x 997 [15,2,1,12,7,9,4,10]
+ CRUSH rule 0 x 998 [6,1,9,5,12,11,15,2]
+ CRUSH rule 0 x 999 [9,10,15,5,13,3,7]
+ CRUSH rule 0 x 1000 [14,2,9,4,12,1,6,11]
+ CRUSH rule 0 x 1001 [11,14,4,2,6,9,1,13]
+ CRUSH rule 0 x 1002 [1,10,14,2,9,5,13,7]
+ CRUSH rule 0 x 1003 [10,7,5,14,2,1,9,12]
+ CRUSH rule 0 x 1004 [15,1,4,6,10,12,9,3]
+ CRUSH rule 0 x 1005 [6,12,2,10,9,15,5,1]
+ CRUSH rule 0 x 1006 [10,12,15,1,2,6,5]
+ CRUSH rule 0 x 1007 [1,7,13,14,3,4,10]
+ CRUSH rule 0 x 1008 [7,4,9,11,3,15,1,13]
+ CRUSH rule 0 x 1009 [5,2,11,7,15,9,1,12]
+ CRUSH rule 0 x 1010 [10,2,15,6,9,13,4,1]
+ CRUSH rule 0 x 1011 [6,3,12,1,10,4,9,14]
+ CRUSH rule 0 x 1012 [12,6,9,15,3,1,5,11]
+ CRUSH rule 0 x 1013 [2,14,12,4,9,1,6,10]
+ CRUSH rule 0 x 1014 [1,13,7,2,10,14,5]
+ CRUSH rule 0 x 1015 [12,6,10,1,4,15,9,2]
+ CRUSH rule 0 x 1016 [10,13,14,3,5,6,1]
+ CRUSH rule 0 x 1017 [5,11,14,7,13,9,2]
+ CRUSH rule 0 x 1018 [13,11,14,1,9,3,5,7]
+ CRUSH rule 0 x 1019 [10,13,14,7,5,1,2]
+ CRUSH rule 0 x 1020 [3,1,13,4,10,9,14,6]
+ CRUSH rule 0 x 1021 [2,11,14,9,4,6,1,13]
+ CRUSH rule 0 x 1022 [15,5,7,2,12,10]
+ CRUSH rule 0 x 1023 [15,2,9,12,1,7,4,11]
+ rule 0 (replicated_ruleset) num_rep 10 result size == 6:\t36/1024 (esc)
+ rule 0 (replicated_ruleset) num_rep 10 result size == 7:\t263/1024 (esc)
+ rule 0 (replicated_ruleset) num_rep 10 result size == 8:\t725/1024 (esc)
diff --git a/src/test/cli/crushtool/test-map-vary-r-0.t b/src/test/cli/crushtool/test-map-vary-r-0.t
new file mode 100644
index 000000000..0a294aa96
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-vary-r-0.t
@@ -0,0 +1,3081 @@
+ $ crushtool -i "$TESTDIR/test-map-vary-r.crushmap" --test --show-mappings --show-statistics --rule 3 --set-chooseleaf-vary-r 0 --weight 0 0 --weight 4 0 --weight 9 0 --min-rep 2 --max-rep 4
+ rule 3 (delltestrule), x = 0..1023, numrep = 2..4
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,12]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,52]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,82]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,40]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,107]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38]
+ CRUSH rule 3 x 38 [72,57]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,2]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,29]
+ CRUSH rule 3 x 54 [28,77]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,105]
+ CRUSH rule 3 x 69 [62]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,88]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,43]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,39]
+ CRUSH rule 3 x 79 [64,65]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,83]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,68]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,26]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,17]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,83]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,100]
+ CRUSH rule 3 x 146 [12,15]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,81]
+ CRUSH rule 3 x 154 [19,56]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,22]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,103]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,100]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,85]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90]
+ CRUSH rule 3 x 191 [49,113]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,66]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,86]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,118]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,32]
+ CRUSH rule 3 x 234 [32,55]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,10]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,87]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,31]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,54]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,75]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,18]
+ CRUSH rule 3 x 291 [35,117]
+ CRUSH rule 3 x 292 [20,74]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,36]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,105]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,119]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,15]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,17]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,116]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,118]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,112]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,32]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,81]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,12]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,113]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,70]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,17]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,44]
+ CRUSH rule 3 x 410 [70,8]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,15]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,41]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,21]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,68]
+ CRUSH rule 3 x 456 [101,60]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,106]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,84]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,61]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99]
+ CRUSH rule 3 x 492 [21,58]
+ CRUSH rule 3 x 493 [94,89]
+ CRUSH rule 3 x 494 [56,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,82]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,6]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,77]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,30]
+ CRUSH rule 3 x 518 [18,37]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,104]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,92]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,19]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,116]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,35]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,96]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,24]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,44]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,50]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,114]
+ CRUSH rule 3 x 638 [43,78]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,99]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,39]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,107]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,102]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,100]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,116]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,26]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36]
+ CRUSH rule 3 x 697 [19,38]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,60]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,113]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,38]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,26]
+ CRUSH rule 3 x 723 [117,75]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,38]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,94]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,56]
+ CRUSH rule 3 x 752 [82,16]
+ CRUSH rule 3 x 753 [116,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,65]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,90]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,53]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,93]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,35]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,94]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,19]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,102]
+ CRUSH rule 3 x 824 [102,95]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,33]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,75]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,39]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,31]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,29]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,66]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,62]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,80]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,25]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,60]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,107]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,10]
+ CRUSH rule 3 x 942 [80,37]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,111]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,16]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,46]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,103]
+ CRUSH rule 3 x 969 [53]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,19]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,100]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,113]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,33]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,22]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,35]
+ CRUSH rule 3 x 1014 [33,48]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,111]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [59,88]
+ rule 3 (delltestrule) num_rep 2 result size == 1:\t27/1024 (esc)
+ rule 3 (delltestrule) num_rep 2 result size == 2:\t997/1024 (esc)
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,12]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,52]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,82]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,40]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,107]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38]
+ CRUSH rule 3 x 38 [72,57]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,2]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,29]
+ CRUSH rule 3 x 54 [28,77]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,105]
+ CRUSH rule 3 x 69 [62]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,88]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,43]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,39]
+ CRUSH rule 3 x 79 [64,65]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,83]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,68]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,26]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,17]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,83]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,100]
+ CRUSH rule 3 x 146 [12,15]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,81]
+ CRUSH rule 3 x 154 [19,56]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,22]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,103]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,100]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,85]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90]
+ CRUSH rule 3 x 191 [49,113]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,66]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,86]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,118]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,32]
+ CRUSH rule 3 x 234 [32,55]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,10]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,87]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,31]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,54]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,75]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,18]
+ CRUSH rule 3 x 291 [35,117]
+ CRUSH rule 3 x 292 [20,74]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,36]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,105]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,119]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,15]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,17]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,116]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,118]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,112]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,32]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,81]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,12]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,113]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,70]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,17]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,44]
+ CRUSH rule 3 x 410 [70,8]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,15]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,41]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,21]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,68]
+ CRUSH rule 3 x 456 [101,60]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,106]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,84]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,61]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99]
+ CRUSH rule 3 x 492 [21,58]
+ CRUSH rule 3 x 493 [94,89]
+ CRUSH rule 3 x 494 [56,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,82]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,6]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,77]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,30]
+ CRUSH rule 3 x 518 [18,37]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,104]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,92]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,19]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,116]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,35]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,96]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,24]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,44]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,50]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,114]
+ CRUSH rule 3 x 638 [43,78]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,99]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,39]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,107]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,102]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,100]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,116]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,26]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36]
+ CRUSH rule 3 x 697 [19,38]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,60]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,113]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,38]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,26]
+ CRUSH rule 3 x 723 [117,75]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,38]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,94]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,56]
+ CRUSH rule 3 x 752 [82,16]
+ CRUSH rule 3 x 753 [116,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,65]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,90]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,53]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,93]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,35]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,94]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,19]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,102]
+ CRUSH rule 3 x 824 [102,95]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,33]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,75]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,39]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,31]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,29]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,66]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,62]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,80]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,25]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,60]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,107]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,10]
+ CRUSH rule 3 x 942 [80,37]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,111]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,16]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,46]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,103]
+ CRUSH rule 3 x 969 [53]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,19]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,100]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,113]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,33]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,22]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,35]
+ CRUSH rule 3 x 1014 [33,48]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,111]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [59,88]
+ rule 3 (delltestrule) num_rep 3 result size == 1:\t27/1024 (esc)
+ rule 3 (delltestrule) num_rep 3 result size == 2:\t997/1024 (esc)
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,12]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,52]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,82]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,40]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,107]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38]
+ CRUSH rule 3 x 38 [72,57]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,2]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,29]
+ CRUSH rule 3 x 54 [28,77]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,105]
+ CRUSH rule 3 x 69 [62]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,88]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,43]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,39]
+ CRUSH rule 3 x 79 [64,65]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,83]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,68]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,26]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,17]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,83]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,100]
+ CRUSH rule 3 x 146 [12,15]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,81]
+ CRUSH rule 3 x 154 [19,56]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,22]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,103]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,100]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,85]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90]
+ CRUSH rule 3 x 191 [49,113]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,66]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,86]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,118]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,32]
+ CRUSH rule 3 x 234 [32,55]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,10]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,87]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,31]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,54]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,75]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,18]
+ CRUSH rule 3 x 291 [35,117]
+ CRUSH rule 3 x 292 [20,74]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,36]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,105]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,119]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,15]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,17]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,116]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,118]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,112]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,32]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,81]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,12]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,113]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,70]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,17]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,44]
+ CRUSH rule 3 x 410 [70,8]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,15]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,41]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,21]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,68]
+ CRUSH rule 3 x 456 [101,60]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,106]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,84]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,61]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99]
+ CRUSH rule 3 x 492 [21,58]
+ CRUSH rule 3 x 493 [94,89]
+ CRUSH rule 3 x 494 [56,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,82]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,6]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,77]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,30]
+ CRUSH rule 3 x 518 [18,37]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,104]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,92]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,19]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,116]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,35]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,96]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,24]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,44]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,50]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,114]
+ CRUSH rule 3 x 638 [43,78]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,99]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,39]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,107]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,102]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,100]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,116]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,26]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36]
+ CRUSH rule 3 x 697 [19,38]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,60]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,113]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,38]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,26]
+ CRUSH rule 3 x 723 [117,75]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,38]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,94]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,56]
+ CRUSH rule 3 x 752 [82,16]
+ CRUSH rule 3 x 753 [116,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,65]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,90]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,53]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,93]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,35]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,94]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,19]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,102]
+ CRUSH rule 3 x 824 [102,95]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,33]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,75]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,39]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,31]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,29]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,66]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,62]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,80]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,25]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,60]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,107]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,10]
+ CRUSH rule 3 x 942 [80,37]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,111]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,16]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,46]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,103]
+ CRUSH rule 3 x 969 [53]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,19]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,100]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,113]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,33]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,22]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,35]
+ CRUSH rule 3 x 1014 [33,48]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,111]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [59,88]
+ rule 3 (delltestrule) num_rep 4 result size == 1:\t27/1024 (esc)
+ rule 3 (delltestrule) num_rep 4 result size == 2:\t997/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-vary-r-1.t b/src/test/cli/crushtool/test-map-vary-r-1.t
new file mode 100644
index 000000000..3b871c129
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-vary-r-1.t
@@ -0,0 +1,3078 @@
+ $ crushtool -i "$TESTDIR/test-map-vary-r.crushmap" --test --show-mappings --show-statistics --rule 3 --set-chooseleaf-vary-r 1 --weight 0 0 --weight 4 0 --weight 9 0 --min-rep 2 --max-rep 4
+ rule 3 (delltestrule), x = 0..1023, numrep = 2..4
+ CRUSH rule 3 x 0 [94,6]
+ CRUSH rule 3 x 1 [73,52]
+ CRUSH rule 3 x 2 [91,48]
+ CRUSH rule 3 x 3 [51,48]
+ CRUSH rule 3 x 4 [45,114]
+ CRUSH rule 3 x 5 [89,94]
+ CRUSH rule 3 x 6 [91,76]
+ CRUSH rule 3 x 7 [104,73]
+ CRUSH rule 3 x 8 [41,98]
+ CRUSH rule 3 x 9 [46,47]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,40]
+ CRUSH rule 3 x 12 [83,26]
+ CRUSH rule 3 x 13 [27,28]
+ CRUSH rule 3 x 14 [105,64]
+ CRUSH rule 3 x 15 [18,7]
+ CRUSH rule 3 x 16 [103,30]
+ CRUSH rule 3 x 17 [85,118]
+ CRUSH rule 3 x 18 [11,106]
+ CRUSH rule 3 x 19 [75,50]
+ CRUSH rule 3 x 20 [111,67]
+ CRUSH rule 3 x 21 [84,61]
+ CRUSH rule 3 x 22 [23,104]
+ CRUSH rule 3 x 23 [19,86]
+ CRUSH rule 3 x 24 [83,60]
+ CRUSH rule 3 x 25 [81,64]
+ CRUSH rule 3 x 26 [17,38]
+ CRUSH rule 3 x 27 [33,84]
+ CRUSH rule 3 x 28 [45,90]
+ CRUSH rule 3 x 29 [8,109]
+ CRUSH rule 3 x 30 [55,42]
+ CRUSH rule 3 x 31 [76,95]
+ CRUSH rule 3 x 32 [72,11]
+ CRUSH rule 3 x 33 [86,53]
+ CRUSH rule 3 x 34 [7,108]
+ CRUSH rule 3 x 35 [108,13]
+ CRUSH rule 3 x 36 [67,66]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,105]
+ CRUSH rule 3 x 39 [68,103]
+ CRUSH rule 3 x 40 [30,85]
+ CRUSH rule 3 x 41 [52,11]
+ CRUSH rule 3 x 42 [106,75]
+ CRUSH rule 3 x 43 [10,104]
+ CRUSH rule 3 x 44 [101,28]
+ CRUSH rule 3 x 45 [83,64]
+ CRUSH rule 3 x 46 [54,31]
+ CRUSH rule 3 x 47 [106,61]
+ CRUSH rule 3 x 48 [34,41]
+ CRUSH rule 3 x 49 [79,110]
+ CRUSH rule 3 x 50 [42,13]
+ CRUSH rule 3 x 51 [6,94]
+ CRUSH rule 3 x 52 [82,19]
+ CRUSH rule 3 x 53 [32,91]
+ CRUSH rule 3 x 54 [108,8]
+ CRUSH rule 3 x 55 [14,94]
+ CRUSH rule 3 x 56 [21,72]
+ CRUSH rule 3 x 57 [69,88]
+ CRUSH rule 3 x 58 [48,87]
+ CRUSH rule 3 x 59 [21,113]
+ CRUSH rule 3 x 60 [90,73]
+ CRUSH rule 3 x 61 [88,63]
+ CRUSH rule 3 x 62 [100,13]
+ CRUSH rule 3 x 63 [79,5]
+ CRUSH rule 3 x 64 [1,89]
+ CRUSH rule 3 x 65 [32,103]
+ CRUSH rule 3 x 66 [48,79]
+ CRUSH rule 3 x 67 [94,11]
+ CRUSH rule 3 x 68 [102,15]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,11]
+ CRUSH rule 3 x 71 [12,33]
+ CRUSH rule 3 x 72 [26,99]
+ CRUSH rule 3 x 73 [29,114]
+ CRUSH rule 3 x 74 [29,1]
+ CRUSH rule 3 x 75 [60,65]
+ CRUSH rule 3 x 76 [55,62]
+ CRUSH rule 3 x 77 [107,100]
+ CRUSH rule 3 x 78 [86,107]
+ CRUSH rule 3 x 79 [64,16]
+ CRUSH rule 3 x 80 [19,100]
+ CRUSH rule 3 x 81 [64,16]
+ CRUSH rule 3 x 82 [37,40]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,115]
+ CRUSH rule 3 x 85 [87,88]
+ CRUSH rule 3 x 86 [37,68]
+ CRUSH rule 3 x 87 [116,77]
+ CRUSH rule 3 x 88 [38,55]
+ CRUSH rule 3 x 89 [76,25]
+ CRUSH rule 3 x 90 [14,50]
+ CRUSH rule 3 x 91 [68,61]
+ CRUSH rule 3 x 92 [86,95]
+ CRUSH rule 3 x 93 [44,35]
+ CRUSH rule 3 x 94 [46,71]
+ CRUSH rule 3 x 95 [108,53]
+ CRUSH rule 3 x 96 [66,87]
+ CRUSH rule 3 x 97 [111,45]
+ CRUSH rule 3 x 98 [93,110]
+ CRUSH rule 3 x 99 [78,43]
+ CRUSH rule 3 x 100 [28,61]
+ CRUSH rule 3 x 101 [91,110]
+ CRUSH rule 3 x 102 [82,7]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,79]
+ CRUSH rule 3 x 105 [34,87]
+ CRUSH rule 3 x 106 [69,12]
+ CRUSH rule 3 x 107 [1,59]
+ CRUSH rule 3 x 108 [7,109]
+ CRUSH rule 3 x 109 [112,67]
+ CRUSH rule 3 x 110 [54,61]
+ CRUSH rule 3 x 111 [10,92]
+ CRUSH rule 3 x 112 [80,11]
+ CRUSH rule 3 x 113 [69,38]
+ CRUSH rule 3 x 114 [79,38]
+ CRUSH rule 3 x 115 [10,48]
+ CRUSH rule 3 x 116 [37,108]
+ CRUSH rule 3 x 117 [87,56]
+ CRUSH rule 3 x 118 [23,56]
+ CRUSH rule 3 x 119 [104,31]
+ CRUSH rule 3 x 120 [44,93]
+ CRUSH rule 3 x 121 [80,16]
+ CRUSH rule 3 x 122 [45,54]
+ CRUSH rule 3 x 123 [22,112]
+ CRUSH rule 3 x 124 [97,50]
+ CRUSH rule 3 x 125 [66,6]
+ CRUSH rule 3 x 126 [70,39]
+ CRUSH rule 3 x 127 [70,75]
+ CRUSH rule 3 x 128 [11,111]
+ CRUSH rule 3 x 129 [103,46]
+ CRUSH rule 3 x 130 [50,73]
+ CRUSH rule 3 x 131 [44,15]
+ CRUSH rule 3 x 132 [69,58]
+ CRUSH rule 3 x 133 [67,115]
+ CRUSH rule 3 x 134 [37,60]
+ CRUSH rule 3 x 135 [78,61]
+ CRUSH rule 3 x 136 [32,29]
+ CRUSH rule 3 x 137 [92,87]
+ CRUSH rule 3 x 138 [54,8]
+ CRUSH rule 3 x 139 [89,60]
+ CRUSH rule 3 x 140 [39,50]
+ CRUSH rule 3 x 141 [89,62]
+ CRUSH rule 3 x 142 [22,86]
+ CRUSH rule 3 x 143 [96,16]
+ CRUSH rule 3 x 144 [13,1]
+ CRUSH rule 3 x 145 [77,54]
+ CRUSH rule 3 x 146 [12,43]
+ CRUSH rule 3 x 147 [2,59]
+ CRUSH rule 3 x 148 [85,50]
+ CRUSH rule 3 x 149 [103,68]
+ CRUSH rule 3 x 150 [14,50]
+ CRUSH rule 3 x 151 [75,56]
+ CRUSH rule 3 x 152 [49,18]
+ CRUSH rule 3 x 153 [92,79]
+ CRUSH rule 3 x 154 [19,26]
+ CRUSH rule 3 x 155 [12,13]
+ CRUSH rule 3 x 156 [107,18]
+ CRUSH rule 3 x 157 [15,78]
+ CRUSH rule 3 x 158 [11,28]
+ CRUSH rule 3 x 159 [33,88]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,78]
+ CRUSH rule 3 x 162 [55,96]
+ CRUSH rule 3 x 163 [54,55]
+ CRUSH rule 3 x 164 [72,99]
+ CRUSH rule 3 x 165 [25,116]
+ CRUSH rule 3 x 166 [2,23]
+ CRUSH rule 3 x 167 [89,40]
+ CRUSH rule 3 x 168 [68,49]
+ CRUSH rule 3 x 169 [51,50]
+ CRUSH rule 3 x 170 [68,91]
+ CRUSH rule 3 x 171 [88,51]
+ CRUSH rule 3 x 172 [117,23]
+ CRUSH rule 3 x 173 [29,1]
+ CRUSH rule 3 x 174 [67,40]
+ CRUSH rule 3 x 175 [48,41]
+ CRUSH rule 3 x 176 [94,91]
+ CRUSH rule 3 x 177 [53,70]
+ CRUSH rule 3 x 178 [39,110]
+ CRUSH rule 3 x 179 [72,89]
+ CRUSH rule 3 x 180 [3,38]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,46]
+ CRUSH rule 3 x 183 [11,78]
+ CRUSH rule 3 x 184 [79,92]
+ CRUSH rule 3 x 185 [97,92]
+ CRUSH rule 3 x 186 [67,116]
+ CRUSH rule 3 x 187 [6,96]
+ CRUSH rule 3 x 188 [76,16]
+ CRUSH rule 3 x 189 [96,71]
+ CRUSH rule 3 x 190 [90,6]
+ CRUSH rule 3 x 191 [49,84]
+ CRUSH rule 3 x 192 [93,114]
+ CRUSH rule 3 x 193 [89,60]
+ CRUSH rule 3 x 194 [62,61]
+ CRUSH rule 3 x 195 [119,95]
+ CRUSH rule 3 x 196 [20,28]
+ CRUSH rule 3 x 197 [6,64]
+ CRUSH rule 3 x 198 [55,112]
+ CRUSH rule 3 x 199 [66,17]
+ CRUSH rule 3 x 200 [12,63]
+ CRUSH rule 3 x 201 [52,23]
+ CRUSH rule 3 x 202 [98,33]
+ CRUSH rule 3 x 203 [36,22]
+ CRUSH rule 3 x 204 [10,100]
+ CRUSH rule 3 x 205 [38,29]
+ CRUSH rule 3 x 206 [38,27]
+ CRUSH rule 3 x 207 [19,108]
+ CRUSH rule 3 x 208 [63,26]
+ CRUSH rule 3 x 209 [70,11]
+ CRUSH rule 3 x 210 [79,58]
+ CRUSH rule 3 x 211 [26,59]
+ CRUSH rule 3 x 212 [107,114]
+ CRUSH rule 3 x 213 [100,3]
+ CRUSH rule 3 x 214 [91,118]
+ CRUSH rule 3 x 215 [92,16]
+ CRUSH rule 3 x 216 [99,94]
+ CRUSH rule 3 x 217 [86,99]
+ CRUSH rule 3 x 218 [70,15]
+ CRUSH rule 3 x 219 [61,58]
+ CRUSH rule 3 x 220 [23,56]
+ CRUSH rule 3 x 221 [21,92]
+ CRUSH rule 3 x 222 [102,29]
+ CRUSH rule 3 x 223 [34,73]
+ CRUSH rule 3 x 224 [107,68]
+ CRUSH rule 3 x 225 [61,98]
+ CRUSH rule 3 x 226 [44,13]
+ CRUSH rule 3 x 227 [55,60]
+ CRUSH rule 3 x 228 [117,55]
+ CRUSH rule 3 x 229 [100,67]
+ CRUSH rule 3 x 230 [41,109]
+ CRUSH rule 3 x 231 [30,71]
+ CRUSH rule 3 x 232 [23,1]
+ CRUSH rule 3 x 233 [47,90]
+ CRUSH rule 3 x 234 [55,62]
+ CRUSH rule 3 x 235 [20,60]
+ CRUSH rule 3 x 236 [95,24]
+ CRUSH rule 3 x 237 [21,106]
+ CRUSH rule 3 x 238 [109,15]
+ CRUSH rule 3 x 239 [40,101]
+ CRUSH rule 3 x 240 [63,60]
+ CRUSH rule 3 x 241 [47,12]
+ CRUSH rule 3 x 242 [73,74]
+ CRUSH rule 3 x 243 [76,8]
+ CRUSH rule 3 x 244 [103,50]
+ CRUSH rule 3 x 245 [106,95]
+ CRUSH rule 3 x 246 [35,82]
+ CRUSH rule 3 x 247 [116,101]
+ CRUSH rule 3 x 248 [8,119]
+ CRUSH rule 3 x 249 [2,17]
+ CRUSH rule 3 x 250 [34,89]
+ CRUSH rule 3 x 251 [28,69]
+ CRUSH rule 3 x 252 [95,80]
+ CRUSH rule 3 x 253 [109,3]
+ CRUSH rule 3 x 254 [99,80]
+ CRUSH rule 3 x 255 [112,85]
+ CRUSH rule 3 x 256 [94,63]
+ CRUSH rule 3 x 257 [100,87]
+ CRUSH rule 3 x 258 [34,63]
+ CRUSH rule 3 x 259 [70,107]
+ CRUSH rule 3 x 260 [89,115]
+ CRUSH rule 3 x 261 [94,83]
+ CRUSH rule 3 x 262 [42,45]
+ CRUSH rule 3 x 263 [113,101]
+ CRUSH rule 3 x 264 [36,81]
+ CRUSH rule 3 x 265 [14,88]
+ CRUSH rule 3 x 266 [75,96]
+ CRUSH rule 3 x 267 [6,5]
+ CRUSH rule 3 x 268 [38,47]
+ CRUSH rule 3 x 269 [86,59]
+ CRUSH rule 3 x 270 [87,70]
+ CRUSH rule 3 x 271 [19,108]
+ CRUSH rule 3 x 272 [73,5]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,64]
+ CRUSH rule 3 x 275 [29,34]
+ CRUSH rule 3 x 276 [7,100]
+ CRUSH rule 3 x 277 [74,6]
+ CRUSH rule 3 x 278 [107,115]
+ CRUSH rule 3 x 279 [112,20]
+ CRUSH rule 3 x 280 [113,15]
+ CRUSH rule 3 x 281 [89,56]
+ CRUSH rule 3 x 282 [20,38]
+ CRUSH rule 3 x 283 [8,114]
+ CRUSH rule 3 x 284 [66,75]
+ CRUSH rule 3 x 285 [99,94]
+ CRUSH rule 3 x 286 [78,6]
+ CRUSH rule 3 x 287 [12,27]
+ CRUSH rule 3 x 288 [24,22]
+ CRUSH rule 3 x 289 [105,64]
+ CRUSH rule 3 x 290 [25,46]
+ CRUSH rule 3 x 291 [35,116]
+ CRUSH rule 3 x 292 [20,109]
+ CRUSH rule 3 x 293 [27,92]
+ CRUSH rule 3 x 294 [60,93]
+ CRUSH rule 3 x 295 [37,2]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,55]
+ CRUSH rule 3 x 298 [70,53]
+ CRUSH rule 3 x 299 [116,10]
+ CRUSH rule 3 x 300 [67,26]
+ CRUSH rule 3 x 301 [117,23]
+ CRUSH rule 3 x 302 [78,67]
+ CRUSH rule 3 x 303 [19,5]
+ CRUSH rule 3 x 304 [101,50]
+ CRUSH rule 3 x 305 [5,59]
+ CRUSH rule 3 x 306 [41,18]
+ CRUSH rule 3 x 307 [65,5]
+ CRUSH rule 3 x 308 [91,2]
+ CRUSH rule 3 x 309 [38,53]
+ CRUSH rule 3 x 310 [26,15]
+ CRUSH rule 3 x 311 [36,95]
+ CRUSH rule 3 x 312 [114,61]
+ CRUSH rule 3 x 313 [104,65]
+ CRUSH rule 3 x 314 [28,6]
+ CRUSH rule 3 x 315 [118,31]
+ CRUSH rule 3 x 316 [98,95]
+ CRUSH rule 3 x 317 [118,13]
+ CRUSH rule 3 x 318 [17,30]
+ CRUSH rule 3 x 319 [53,1]
+ CRUSH rule 3 x 320 [36,41]
+ CRUSH rule 3 x 321 [33,5]
+ CRUSH rule 3 x 322 [68,10]
+ CRUSH rule 3 x 323 [66,29]
+ CRUSH rule 3 x 324 [21,72]
+ CRUSH rule 3 x 325 [52,16]
+ CRUSH rule 3 x 326 [7,109]
+ CRUSH rule 3 x 327 [62,17]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,100]
+ CRUSH rule 3 x 330 [24,11]
+ CRUSH rule 3 x 331 [84,95]
+ CRUSH rule 3 x 332 [61,111]
+ CRUSH rule 3 x 333 [116,25]
+ CRUSH rule 3 x 334 [94,103]
+ CRUSH rule 3 x 335 [71,118]
+ CRUSH rule 3 x 336 [24,99]
+ CRUSH rule 3 x 337 [18,83]
+ CRUSH rule 3 x 338 [43,28]
+ CRUSH rule 3 x 339 [13,64]
+ CRUSH rule 3 x 340 [81,111]
+ CRUSH rule 3 x 341 [46,105]
+ CRUSH rule 3 x 342 [92,23]
+ CRUSH rule 3 x 343 [49,112]
+ CRUSH rule 3 x 344 [1,87]
+ CRUSH rule 3 x 345 [56,35]
+ CRUSH rule 3 x 346 [3,54]
+ CRUSH rule 3 x 347 [106,27]
+ CRUSH rule 3 x 348 [10,117]
+ CRUSH rule 3 x 349 [96,87]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,41]
+ CRUSH rule 3 x 352 [36,13]
+ CRUSH rule 3 x 353 [10,82]
+ CRUSH rule 3 x 354 [55,52]
+ CRUSH rule 3 x 355 [73,94]
+ CRUSH rule 3 x 356 [75,66]
+ CRUSH rule 3 x 357 [70,93]
+ CRUSH rule 3 x 358 [97,56]
+ CRUSH rule 3 x 359 [110,105]
+ CRUSH rule 3 x 360 [106,57]
+ CRUSH rule 3 x 361 [27,42]
+ CRUSH rule 3 x 362 [28,55]
+ CRUSH rule 3 x 363 [68,20]
+ CRUSH rule 3 x 364 [23,50]
+ CRUSH rule 3 x 365 [57,76]
+ CRUSH rule 3 x 366 [42,75]
+ CRUSH rule 3 x 367 [103,82]
+ CRUSH rule 3 x 368 [103,104]
+ CRUSH rule 3 x 369 [12,57]
+ CRUSH rule 3 x 370 [11,26]
+ CRUSH rule 3 x 371 [34,55]
+ CRUSH rule 3 x 372 [58,14]
+ CRUSH rule 3 x 373 [6,42]
+ CRUSH rule 3 x 374 [110,95]
+ CRUSH rule 3 x 375 [5,43]
+ CRUSH rule 3 x 376 [91,86]
+ CRUSH rule 3 x 377 [93,116]
+ CRUSH rule 3 x 378 [68,6]
+ CRUSH rule 3 x 379 [77,44]
+ CRUSH rule 3 x 380 [76,83]
+ CRUSH rule 3 x 381 [36,27]
+ CRUSH rule 3 x 382 [26,77]
+ CRUSH rule 3 x 383 [76,99]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,93]
+ CRUSH rule 3 x 386 [83,92]
+ CRUSH rule 3 x 387 [16,26]
+ CRUSH rule 3 x 388 [29,113]
+ CRUSH rule 3 x 389 [92,29]
+ CRUSH rule 3 x 390 [68,77]
+ CRUSH rule 3 x 391 [15,88]
+ CRUSH rule 3 x 392 [21,32]
+ CRUSH rule 3 x 393 [91,18]
+ CRUSH rule 3 x 394 [38,73]
+ CRUSH rule 3 x 395 [21,119]
+ CRUSH rule 3 x 396 [12,13]
+ CRUSH rule 3 x 397 [40,63]
+ CRUSH rule 3 x 398 [44,3]
+ CRUSH rule 3 x 399 [5,95]
+ CRUSH rule 3 x 400 [19,102]
+ CRUSH rule 3 x 401 [79,52]
+ CRUSH rule 3 x 402 [107,98]
+ CRUSH rule 3 x 403 [23,82]
+ CRUSH rule 3 x 404 [87,68]
+ CRUSH rule 3 x 405 [90,97]
+ CRUSH rule 3 x 406 [15,117]
+ CRUSH rule 3 x 407 [70,35]
+ CRUSH rule 3 x 408 [55,72]
+ CRUSH rule 3 x 409 [73,62]
+ CRUSH rule 3 x 410 [70,73]
+ CRUSH rule 3 x 411 [34,25]
+ CRUSH rule 3 x 412 [105,117]
+ CRUSH rule 3 x 413 [41,110]
+ CRUSH rule 3 x 414 [70,65]
+ CRUSH rule 3 x 415 [107,5]
+ CRUSH rule 3 x 416 [2,22]
+ CRUSH rule 3 x 417 [26,14]
+ CRUSH rule 3 x 418 [51,46]
+ CRUSH rule 3 x 419 [8,82]
+ CRUSH rule 3 x 420 [109,105]
+ CRUSH rule 3 x 421 [114,75]
+ CRUSH rule 3 x 422 [109,87]
+ CRUSH rule 3 x 423 [59,24]
+ CRUSH rule 3 x 424 [92,51]
+ CRUSH rule 3 x 425 [101,111]
+ CRUSH rule 3 x 426 [36,6]
+ CRUSH rule 3 x 427 [8,24]
+ CRUSH rule 3 x 428 [68,35]
+ CRUSH rule 3 x 429 [76,75]
+ CRUSH rule 3 x 430 [67,117]
+ CRUSH rule 3 x 431 [70,25]
+ CRUSH rule 3 x 432 [7,34]
+ CRUSH rule 3 x 433 [49,84]
+ CRUSH rule 3 x 434 [64,31]
+ CRUSH rule 3 x 435 [110,13]
+ CRUSH rule 3 x 436 [106,89]
+ CRUSH rule 3 x 437 [26,65]
+ CRUSH rule 3 x 438 [118,63]
+ CRUSH rule 3 x 439 [40,21]
+ CRUSH rule 3 x 440 [45,119]
+ CRUSH rule 3 x 441 [112,105]
+ CRUSH rule 3 x 442 [55,113]
+ CRUSH rule 3 x 443 [44,33]
+ CRUSH rule 3 x 444 [71,38]
+ CRUSH rule 3 x 445 [58,81]
+ CRUSH rule 3 x 446 [40,10]
+ CRUSH rule 3 x 447 [100,61]
+ CRUSH rule 3 x 448 [111,73]
+ CRUSH rule 3 x 449 [67,66]
+ CRUSH rule 3 x 450 [117,61]
+ CRUSH rule 3 x 451 [66,81]
+ CRUSH rule 3 x 452 [70,8]
+ CRUSH rule 3 x 453 [82,85]
+ CRUSH rule 3 x 454 [53,18]
+ CRUSH rule 3 x 455 [91,42]
+ CRUSH rule 3 x 456 [101,46]
+ CRUSH rule 3 x 457 [113,51]
+ CRUSH rule 3 x 458 [119,25]
+ CRUSH rule 3 x 459 [50,67]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,51]
+ CRUSH rule 3 x 462 [98,107]
+ CRUSH rule 3 x 463 [108,105]
+ CRUSH rule 3 x 464 [19,109]
+ CRUSH rule 3 x 465 [62,23]
+ CRUSH rule 3 x 466 [53,12]
+ CRUSH rule 3 x 467 [40,57]
+ CRUSH rule 3 x 468 [97,44]
+ CRUSH rule 3 x 469 [98,75]
+ CRUSH rule 3 x 470 [50,29]
+ CRUSH rule 3 x 471 [40,13]
+ CRUSH rule 3 x 472 [27,18]
+ CRUSH rule 3 x 473 [48,35]
+ CRUSH rule 3 x 474 [51,32]
+ CRUSH rule 3 x 475 [49,117]
+ CRUSH rule 3 x 476 [110,31]
+ CRUSH rule 3 x 477 [80,97]
+ CRUSH rule 3 x 478 [78,99]
+ CRUSH rule 3 x 479 [31,66]
+ CRUSH rule 3 x 480 [75,88]
+ CRUSH rule 3 x 481 [26,20]
+ CRUSH rule 3 x 482 [84,53]
+ CRUSH rule 3 x 483 [15,116]
+ CRUSH rule 3 x 484 [37,114]
+ CRUSH rule 3 x 485 [84,8]
+ CRUSH rule 3 x 486 [92,10]
+ CRUSH rule 3 x 487 [106,17]
+ CRUSH rule 3 x 488 [42,20]
+ CRUSH rule 3 x 489 [89,2]
+ CRUSH rule 3 x 490 [22,114]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,66]
+ CRUSH rule 3 x 493 [94,14]
+ CRUSH rule 3 x 494 [59,86]
+ CRUSH rule 3 x 495 [95,58]
+ CRUSH rule 3 x 496 [46,41]
+ CRUSH rule 3 x 497 [102,27]
+ CRUSH rule 3 x 498 [21,116]
+ CRUSH rule 3 x 499 [5,49]
+ CRUSH rule 3 x 500 [50,49]
+ CRUSH rule 3 x 501 [60,3]
+ CRUSH rule 3 x 502 [65,110]
+ CRUSH rule 3 x 503 [21,112]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,93]
+ CRUSH rule 3 x 506 [79,64]
+ CRUSH rule 3 x 507 [34,107]
+ CRUSH rule 3 x 508 [45,114]
+ CRUSH rule 3 x 509 [19,88]
+ CRUSH rule 3 x 510 [117,45]
+ CRUSH rule 3 x 511 [14,104]
+ CRUSH rule 3 x 512 [59,26]
+ CRUSH rule 3 x 513 [102,93]
+ CRUSH rule 3 x 514 [75,72]
+ CRUSH rule 3 x 515 [84,41]
+ CRUSH rule 3 x 516 [37,30]
+ CRUSH rule 3 x 517 [83,115]
+ CRUSH rule 3 x 518 [18,83]
+ CRUSH rule 3 x 519 [67,88]
+ CRUSH rule 3 x 520 [15,114]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,51]
+ CRUSH rule 3 x 523 [68,101]
+ CRUSH rule 3 x 524 [33,38]
+ CRUSH rule 3 x 525 [63,115]
+ CRUSH rule 3 x 526 [83,50]
+ CRUSH rule 3 x 527 [37,56]
+ CRUSH rule 3 x 528 [108,81]
+ CRUSH rule 3 x 529 [74,33]
+ CRUSH rule 3 x 530 [49,92]
+ CRUSH rule 3 x 531 [117,105]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,85]
+ CRUSH rule 3 x 534 [97,24]
+ CRUSH rule 3 x 535 [48,75]
+ CRUSH rule 3 x 536 [113,101]
+ CRUSH rule 3 x 537 [116,47]
+ CRUSH rule 3 x 538 [85,74]
+ CRUSH rule 3 x 539 [72,43]
+ CRUSH rule 3 x 540 [39,34]
+ CRUSH rule 3 x 541 [53,84]
+ CRUSH rule 3 x 542 [27,32]
+ CRUSH rule 3 x 543 [45,113]
+ CRUSH rule 3 x 544 [59,42]
+ CRUSH rule 3 x 545 [118,95]
+ CRUSH rule 3 x 546 [18,79]
+ CRUSH rule 3 x 547 [67,30]
+ CRUSH rule 3 x 548 [53,100]
+ CRUSH rule 3 x 549 [60,45]
+ CRUSH rule 3 x 550 [92,101]
+ CRUSH rule 3 x 551 [77,88]
+ CRUSH rule 3 x 552 [61,94]
+ CRUSH rule 3 x 553 [71,78]
+ CRUSH rule 3 x 554 [61,115]
+ CRUSH rule 3 x 555 [76,77]
+ CRUSH rule 3 x 556 [106,55]
+ CRUSH rule 3 x 557 [26,22]
+ CRUSH rule 3 x 558 [41,84]
+ CRUSH rule 3 x 559 [65,24]
+ CRUSH rule 3 x 560 [94,16]
+ CRUSH rule 3 x 561 [27,5]
+ CRUSH rule 3 x 562 [78,59]
+ CRUSH rule 3 x 563 [59,70]
+ CRUSH rule 3 x 564 [96,8]
+ CRUSH rule 3 x 565 [8,48]
+ CRUSH rule 3 x 566 [119,17]
+ CRUSH rule 3 x 567 [7,38]
+ CRUSH rule 3 x 568 [57,94]
+ CRUSH rule 3 x 569 [65,26]
+ CRUSH rule 3 x 570 [98,27]
+ CRUSH rule 3 x 571 [95,30]
+ CRUSH rule 3 x 572 [62,83]
+ CRUSH rule 3 x 573 [1,79]
+ CRUSH rule 3 x 574 [89,42]
+ CRUSH rule 3 x 575 [87,113]
+ CRUSH rule 3 x 576 [21,68]
+ CRUSH rule 3 x 577 [8,84]
+ CRUSH rule 3 x 578 [75,115]
+ CRUSH rule 3 x 579 [105,68]
+ CRUSH rule 3 x 580 [51,28]
+ CRUSH rule 3 x 581 [55,113]
+ CRUSH rule 3 x 582 [27,113]
+ CRUSH rule 3 x 583 [6,78]
+ CRUSH rule 3 x 584 [10,30]
+ CRUSH rule 3 x 585 [20,111]
+ CRUSH rule 3 x 586 [48,27]
+ CRUSH rule 3 x 587 [29,94]
+ CRUSH rule 3 x 588 [103,90]
+ CRUSH rule 3 x 589 [88,95]
+ CRUSH rule 3 x 590 [76,101]
+ CRUSH rule 3 x 591 [42,43]
+ CRUSH rule 3 x 592 [78,51]
+ CRUSH rule 3 x 593 [82,71]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,59]
+ CRUSH rule 3 x 597 [16,36]
+ CRUSH rule 3 x 598 [37,56]
+ CRUSH rule 3 x 599 [10,84]
+ CRUSH rule 3 x 600 [24,69]
+ CRUSH rule 3 x 601 [104,14]
+ CRUSH rule 3 x 602 [48,45]
+ CRUSH rule 3 x 603 [93,32]
+ CRUSH rule 3 x 604 [118,79]
+ CRUSH rule 3 x 605 [104,53]
+ CRUSH rule 3 x 606 [90,83]
+ CRUSH rule 3 x 607 [95,110]
+ CRUSH rule 3 x 608 [112,101]
+ CRUSH rule 3 x 609 [34,99]
+ CRUSH rule 3 x 610 [106,16]
+ CRUSH rule 3 x 611 [66,87]
+ CRUSH rule 3 x 612 [2,81]
+ CRUSH rule 3 x 613 [13,86]
+ CRUSH rule 3 x 614 [50,3]
+ CRUSH rule 3 x 615 [24,73]
+ CRUSH rule 3 x 616 [41,119]
+ CRUSH rule 3 x 617 [81,106]
+ CRUSH rule 3 x 618 [3,104]
+ CRUSH rule 3 x 619 [92,7]
+ CRUSH rule 3 x 620 [108,11]
+ CRUSH rule 3 x 621 [105,115]
+ CRUSH rule 3 x 622 [67,48]
+ CRUSH rule 3 x 623 [69,74]
+ CRUSH rule 3 x 624 [115,49]
+ CRUSH rule 3 x 625 [73,109]
+ CRUSH rule 3 x 626 [52,3]
+ CRUSH rule 3 x 627 [116,3]
+ CRUSH rule 3 x 628 [98,91]
+ CRUSH rule 3 x 629 [6,112]
+ CRUSH rule 3 x 630 [22,72]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,71]
+ CRUSH rule 3 x 633 [65,12]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,46]
+ CRUSH rule 3 x 636 [23,70]
+ CRUSH rule 3 x 637 [99,24]
+ CRUSH rule 3 x 638 [43,114]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,73]
+ CRUSH rule 3 x 641 [45,84]
+ CRUSH rule 3 x 642 [47,66]
+ CRUSH rule 3 x 643 [64,8]
+ CRUSH rule 3 x 644 [31,82]
+ CRUSH rule 3 x 645 [77,64]
+ CRUSH rule 3 x 646 [37,86]
+ CRUSH rule 3 x 647 [65,56]
+ CRUSH rule 3 x 648 [84,13]
+ CRUSH rule 3 x 649 [88,55]
+ CRUSH rule 3 x 650 [21,76]
+ CRUSH rule 3 x 651 [63,116]
+ CRUSH rule 3 x 652 [57,112]
+ CRUSH rule 3 x 653 [38,61]
+ CRUSH rule 3 x 654 [104,67]
+ CRUSH rule 3 x 655 [89,54]
+ CRUSH rule 3 x 656 [84,49]
+ CRUSH rule 3 x 657 [47,32]
+ CRUSH rule 3 x 658 [80,29]
+ CRUSH rule 3 x 659 [11,112]
+ CRUSH rule 3 x 660 [65,111]
+ CRUSH rule 3 x 661 [96,73]
+ CRUSH rule 3 x 662 [111,73]
+ CRUSH rule 3 x 663 [83,60]
+ CRUSH rule 3 x 664 [59,80]
+ CRUSH rule 3 x 665 [31,117]
+ CRUSH rule 3 x 666 [112,101]
+ CRUSH rule 3 x 667 [70,47]
+ CRUSH rule 3 x 668 [96,57]
+ CRUSH rule 3 x 669 [56,39]
+ CRUSH rule 3 x 670 [98,105]
+ CRUSH rule 3 x 671 [57,48]
+ CRUSH rule 3 x 672 [37,36]
+ CRUSH rule 3 x 673 [83,2]
+ CRUSH rule 3 x 674 [36,25]
+ CRUSH rule 3 x 675 [88,14]
+ CRUSH rule 3 x 676 [3,110]
+ CRUSH rule 3 x 677 [88,67]
+ CRUSH rule 3 x 678 [27,44]
+ CRUSH rule 3 x 679 [33,116]
+ CRUSH rule 3 x 680 [111,39]
+ CRUSH rule 3 x 681 [53,12]
+ CRUSH rule 3 x 682 [12,87]
+ CRUSH rule 3 x 683 [24,85]
+ CRUSH rule 3 x 684 [98,65]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,72]
+ CRUSH rule 3 x 688 [16,114]
+ CRUSH rule 3 x 689 [32,31]
+ CRUSH rule 3 x 690 [96,33]
+ CRUSH rule 3 x 691 [34,6]
+ CRUSH rule 3 x 692 [97,84]
+ CRUSH rule 3 x 693 [29,118]
+ CRUSH rule 3 x 694 [6,30]
+ CRUSH rule 3 x 695 [31,72]
+ CRUSH rule 3 x 696 [104,97]
+ CRUSH rule 3 x 697 [19,96]
+ CRUSH rule 3 x 698 [30,69]
+ CRUSH rule 3 x 699 [47,76]
+ CRUSH rule 3 x 700 [82,55]
+ CRUSH rule 3 x 701 [53,80]
+ CRUSH rule 3 x 702 [95,98]
+ CRUSH rule 3 x 703 [92,65]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,1]
+ CRUSH rule 3 x 706 [74,35]
+ CRUSH rule 3 x 707 [91,115]
+ CRUSH rule 3 x 708 [95,112]
+ CRUSH rule 3 x 709 [73,72]
+ CRUSH rule 3 x 710 [94,47]
+ CRUSH rule 3 x 711 [68,41]
+ CRUSH rule 3 x 712 [107,18]
+ CRUSH rule 3 x 713 [29,109]
+ CRUSH rule 3 x 714 [86,61]
+ CRUSH rule 3 x 715 [74,13]
+ CRUSH rule 3 x 716 [101,56]
+ CRUSH rule 3 x 717 [12,29]
+ CRUSH rule 3 x 718 [83,24]
+ CRUSH rule 3 x 719 [26,10]
+ CRUSH rule 3 x 720 [69,2]
+ CRUSH rule 3 x 721 [51,42]
+ CRUSH rule 3 x 722 [15,74]
+ CRUSH rule 3 x 723 [117,14]
+ CRUSH rule 3 x 724 [45,38]
+ CRUSH rule 3 x 725 [53,110]
+ CRUSH rule 3 x 726 [103,68]
+ CRUSH rule 3 x 727 [89,100]
+ CRUSH rule 3 x 728 [76,16]
+ CRUSH rule 3 x 729 [35,90]
+ CRUSH rule 3 x 730 [28,103]
+ CRUSH rule 3 x 731 [78,41]
+ CRUSH rule 3 x 732 [1,27]
+ CRUSH rule 3 x 733 [35,100]
+ CRUSH rule 3 x 734 [119,85]
+ CRUSH rule 3 x 735 [102,43]
+ CRUSH rule 3 x 736 [37,92]
+ CRUSH rule 3 x 737 [117,11]
+ CRUSH rule 3 x 738 [57,32]
+ CRUSH rule 3 x 739 [87,1]
+ CRUSH rule 3 x 740 [29,80]
+ CRUSH rule 3 x 741 [47,111]
+ CRUSH rule 3 x 742 [106,83]
+ CRUSH rule 3 x 743 [105,94]
+ CRUSH rule 3 x 744 [23,64]
+ CRUSH rule 3 x 745 [37,112]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,95]
+ CRUSH rule 3 x 748 [48,14]
+ CRUSH rule 3 x 749 [102,101]
+ CRUSH rule 3 x 750 [83,78]
+ CRUSH rule 3 x 751 [25,104]
+ CRUSH rule 3 x 752 [82,95]
+ CRUSH rule 3 x 753 [14,113]
+ CRUSH rule 3 x 754 [114,51]
+ CRUSH rule 3 x 755 [87,26]
+ CRUSH rule 3 x 756 [113,87]
+ CRUSH rule 3 x 757 [47,66]
+ CRUSH rule 3 x 758 [54,63]
+ CRUSH rule 3 x 759 [74,20]
+ CRUSH rule 3 x 760 [88,22]
+ CRUSH rule 3 x 761 [73,86]
+ CRUSH rule 3 x 762 [34,17]
+ CRUSH rule 3 x 763 [13,78]
+ CRUSH rule 3 x 764 [89,42]
+ CRUSH rule 3 x 765 [109,91]
+ CRUSH rule 3 x 766 [19,66]
+ CRUSH rule 3 x 767 [41,26]
+ CRUSH rule 3 x 768 [106,57]
+ CRUSH rule 3 x 769 [91,104]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,35]
+ CRUSH rule 3 x 772 [97,108]
+ CRUSH rule 3 x 773 [116,47]
+ CRUSH rule 3 x 774 [100,31]
+ CRUSH rule 3 x 775 [102,43]
+ CRUSH rule 3 x 776 [69,38]
+ CRUSH rule 3 x 777 [91,52]
+ CRUSH rule 3 x 778 [83,119]
+ CRUSH rule 3 x 779 [47,60]
+ CRUSH rule 3 x 780 [63,70]
+ CRUSH rule 3 x 781 [105,2]
+ CRUSH rule 3 x 782 [117,59]
+ CRUSH rule 3 x 783 [19,109]
+ CRUSH rule 3 x 784 [63,114]
+ CRUSH rule 3 x 785 [27,84]
+ CRUSH rule 3 x 786 [41,110]
+ CRUSH rule 3 x 787 [108,73]
+ CRUSH rule 3 x 788 [74,103]
+ CRUSH rule 3 x 789 [50,17]
+ CRUSH rule 3 x 790 [20,106]
+ CRUSH rule 3 x 791 [96,87]
+ CRUSH rule 3 x 792 [80,97]
+ CRUSH rule 3 x 793 [6,26]
+ CRUSH rule 3 x 794 [14,42]
+ CRUSH rule 3 x 795 [30,8]
+ CRUSH rule 3 x 796 [87,36]
+ CRUSH rule 3 x 797 [64,61]
+ CRUSH rule 3 x 798 [42,69]
+ CRUSH rule 3 x 799 [19,117]
+ CRUSH rule 3 x 800 [106,8]
+ CRUSH rule 3 x 801 [2,57]
+ CRUSH rule 3 x 802 [63,68]
+ CRUSH rule 3 x 803 [46,35]
+ CRUSH rule 3 x 804 [33,26]
+ CRUSH rule 3 x 805 [96,49]
+ CRUSH rule 3 x 806 [48,25]
+ CRUSH rule 3 x 807 [48,83]
+ CRUSH rule 3 x 808 [76,31]
+ CRUSH rule 3 x 809 [27,48]
+ CRUSH rule 3 x 810 [119,71]
+ CRUSH rule 3 x 811 [111,91]
+ CRUSH rule 3 x 812 [25,111]
+ CRUSH rule 3 x 813 [81,28]
+ CRUSH rule 3 x 814 [95,42]
+ CRUSH rule 3 x 815 [84,61]
+ CRUSH rule 3 x 816 [64,35]
+ CRUSH rule 3 x 817 [63,60]
+ CRUSH rule 3 x 818 [69,46]
+ CRUSH rule 3 x 819 [88,75]
+ CRUSH rule 3 x 820 [104,57]
+ CRUSH rule 3 x 821 [58,21]
+ CRUSH rule 3 x 822 [20,80]
+ CRUSH rule 3 x 823 [63,118]
+ CRUSH rule 3 x 824 [102,13]
+ CRUSH rule 3 x 825 [47,118]
+ CRUSH rule 3 x 826 [44,7]
+ CRUSH rule 3 x 827 [101,88]
+ CRUSH rule 3 x 828 [60,41]
+ CRUSH rule 3 x 829 [45,102]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,75]
+ CRUSH rule 3 x 833 [57,32]
+ CRUSH rule 3 x 834 [90,33]
+ CRUSH rule 3 x 835 [6,1]
+ CRUSH rule 3 x 836 [63,68]
+ CRUSH rule 3 x 837 [76,71]
+ CRUSH rule 3 x 838 [106,20]
+ CRUSH rule 3 x 839 [87,96]
+ CRUSH rule 3 x 840 [33,32]
+ CRUSH rule 3 x 841 [110,55]
+ CRUSH rule 3 x 842 [66,87]
+ CRUSH rule 3 x 843 [11,80]
+ CRUSH rule 3 x 844 [74,103]
+ CRUSH rule 3 x 845 [74,43]
+ CRUSH rule 3 x 846 [43,76]
+ CRUSH rule 3 x 847 [62,20]
+ CRUSH rule 3 x 848 [92,17]
+ CRUSH rule 3 x 849 [93,36]
+ CRUSH rule 3 x 850 [83,82]
+ CRUSH rule 3 x 851 [65,94]
+ CRUSH rule 3 x 852 [60,22]
+ CRUSH rule 3 x 853 [88,29]
+ CRUSH rule 3 x 854 [83,54]
+ CRUSH rule 3 x 855 [2,101]
+ CRUSH rule 3 x 856 [40,41]
+ CRUSH rule 3 x 857 [69,82]
+ CRUSH rule 3 x 858 [98,81]
+ CRUSH rule 3 x 859 [56,43]
+ CRUSH rule 3 x 860 [11,26]
+ CRUSH rule 3 x 861 [22,110]
+ CRUSH rule 3 x 862 [22,70]
+ CRUSH rule 3 x 863 [79,84]
+ CRUSH rule 3 x 864 [77,24]
+ CRUSH rule 3 x 865 [119,17]
+ CRUSH rule 3 x 866 [18,49]
+ CRUSH rule 3 x 867 [3,84]
+ CRUSH rule 3 x 868 [100,107]
+ CRUSH rule 3 x 869 [22,104]
+ CRUSH rule 3 x 870 [73,30]
+ CRUSH rule 3 x 871 [84,105]
+ CRUSH rule 3 x 872 [72,75]
+ CRUSH rule 3 x 873 [81,96]
+ CRUSH rule 3 x 874 [21,72]
+ CRUSH rule 3 x 875 [115,59]
+ CRUSH rule 3 x 876 [98,49]
+ CRUSH rule 3 x 877 [80,79]
+ CRUSH rule 3 x 878 [87,94]
+ CRUSH rule 3 x 879 [29,18]
+ CRUSH rule 3 x 880 [23,40]
+ CRUSH rule 3 x 881 [109,69]
+ CRUSH rule 3 x 882 [31,118]
+ CRUSH rule 3 x 883 [102,8]
+ CRUSH rule 3 x 884 [80,19]
+ CRUSH rule 3 x 885 [46,101]
+ CRUSH rule 3 x 886 [2,65]
+ CRUSH rule 3 x 887 [5,99]
+ CRUSH rule 3 x 888 [16,70]
+ CRUSH rule 3 x 889 [84,93]
+ CRUSH rule 3 x 890 [65,118]
+ CRUSH rule 3 x 891 [86,105]
+ CRUSH rule 3 x 892 [64,10]
+ CRUSH rule 3 x 893 [20,110]
+ CRUSH rule 3 x 894 [32,47]
+ CRUSH rule 3 x 895 [40,21]
+ CRUSH rule 3 x 896 [113,14]
+ CRUSH rule 3 x 897 [107,80]
+ CRUSH rule 3 x 898 [76,71]
+ CRUSH rule 3 x 899 [75,82]
+ CRUSH rule 3 x 900 [83,82]
+ CRUSH rule 3 x 901 [66,61]
+ CRUSH rule 3 x 902 [25,56]
+ CRUSH rule 3 x 903 [53,46]
+ CRUSH rule 3 x 904 [50,101]
+ CRUSH rule 3 x 905 [99,110]
+ CRUSH rule 3 x 906 [68,27]
+ CRUSH rule 3 x 907 [109,47]
+ CRUSH rule 3 x 908 [47,1]
+ CRUSH rule 3 x 909 [73,2]
+ CRUSH rule 3 x 910 [71,74]
+ CRUSH rule 3 x 911 [39,42]
+ CRUSH rule 3 x 912 [90,7]
+ CRUSH rule 3 x 913 [29,96]
+ CRUSH rule 3 x 914 [84,45]
+ CRUSH rule 3 x 915 [49,115]
+ CRUSH rule 3 x 916 [32,77]
+ CRUSH rule 3 x 917 [46,23]
+ CRUSH rule 3 x 918 [82,73]
+ CRUSH rule 3 x 919 [13,28]
+ CRUSH rule 3 x 920 [25,26]
+ CRUSH rule 3 x 921 [55,119]
+ CRUSH rule 3 x 922 [33,32]
+ CRUSH rule 3 x 923 [28,15]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,22]
+ CRUSH rule 3 x 926 [64,69]
+ CRUSH rule 3 x 927 [32,16]
+ CRUSH rule 3 x 928 [13,113]
+ CRUSH rule 3 x 929 [85,115]
+ CRUSH rule 3 x 930 [104,20]
+ CRUSH rule 3 x 931 [46,79]
+ CRUSH rule 3 x 932 [43,52]
+ CRUSH rule 3 x 933 [18,55]
+ CRUSH rule 3 x 934 [68,81]
+ CRUSH rule 3 x 935 [28,57]
+ CRUSH rule 3 x 936 [104,57]
+ CRUSH rule 3 x 937 [110,10]
+ CRUSH rule 3 x 938 [48,23]
+ CRUSH rule 3 x 939 [77,42]
+ CRUSH rule 3 x 940 [76,49]
+ CRUSH rule 3 x 941 [66,101]
+ CRUSH rule 3 x 942 [80,87]
+ CRUSH rule 3 x 943 [75,74]
+ CRUSH rule 3 x 944 [82,53]
+ CRUSH rule 3 x 945 [71,74]
+ CRUSH rule 3 x 946 [37,42]
+ CRUSH rule 3 x 947 [107,30]
+ CRUSH rule 3 x 948 [108,95]
+ CRUSH rule 3 x 949 [46,103]
+ CRUSH rule 3 x 950 [96,101]
+ CRUSH rule 3 x 951 [40,14]
+ CRUSH rule 3 x 952 [114,21]
+ CRUSH rule 3 x 953 [62,23]
+ CRUSH rule 3 x 954 [103,5]
+ CRUSH rule 3 x 955 [42,73]
+ CRUSH rule 3 x 956 [72,103]
+ CRUSH rule 3 x 957 [117,22]
+ CRUSH rule 3 x 958 [23,106]
+ CRUSH rule 3 x 959 [42,93]
+ CRUSH rule 3 x 960 [113,8]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,51]
+ CRUSH rule 3 x 963 [101,106]
+ CRUSH rule 3 x 964 [66,89]
+ CRUSH rule 3 x 965 [47,102]
+ CRUSH rule 3 x 966 [88,63]
+ CRUSH rule 3 x 967 [71,46]
+ CRUSH rule 3 x 968 [74,51]
+ CRUSH rule 3 x 969 [53,78]
+ CRUSH rule 3 x 970 [3,30]
+ CRUSH rule 3 x 971 [66,107]
+ CRUSH rule 3 x 972 [3,66]
+ CRUSH rule 3 x 973 [113,20]
+ CRUSH rule 3 x 974 [114,35]
+ CRUSH rule 3 x 975 [83,58]
+ CRUSH rule 3 x 976 [81,48]
+ CRUSH rule 3 x 977 [95,102]
+ CRUSH rule 3 x 978 [119,41]
+ CRUSH rule 3 x 979 [98,6]
+ CRUSH rule 3 x 980 [39,108]
+ CRUSH rule 3 x 981 [89,84]
+ CRUSH rule 3 x 982 [19,94]
+ CRUSH rule 3 x 983 [34,45]
+ CRUSH rule 3 x 984 [78,63]
+ CRUSH rule 3 x 985 [99,52]
+ CRUSH rule 3 x 986 [44,99]
+ CRUSH rule 3 x 987 [25,32]
+ CRUSH rule 3 x 988 [79,2]
+ CRUSH rule 3 x 989 [87,26]
+ CRUSH rule 3 x 990 [72,69]
+ CRUSH rule 3 x 991 [90,8]
+ CRUSH rule 3 x 992 [30,67]
+ CRUSH rule 3 x 993 [74,49]
+ CRUSH rule 3 x 994 [74,105]
+ CRUSH rule 3 x 995 [100,97]
+ CRUSH rule 3 x 996 [41,58]
+ CRUSH rule 3 x 997 [89,76]
+ CRUSH rule 3 x 998 [92,47]
+ CRUSH rule 3 x 999 [117,16]
+ CRUSH rule 3 x 1000 [50,47]
+ CRUSH rule 3 x 1001 [83,102]
+ CRUSH rule 3 x 1002 [94,37]
+ CRUSH rule 3 x 1003 [43,88]
+ CRUSH rule 3 x 1004 [89,54]
+ CRUSH rule 3 x 1005 [105,84]
+ CRUSH rule 3 x 1006 [45,111]
+ CRUSH rule 3 x 1007 [19,66]
+ CRUSH rule 3 x 1008 [31,76]
+ CRUSH rule 3 x 1009 [1,95]
+ CRUSH rule 3 x 1010 [31,113]
+ CRUSH rule 3 x 1011 [64,81]
+ CRUSH rule 3 x 1012 [68,49]
+ CRUSH rule 3 x 1013 [5,93]
+ CRUSH rule 3 x 1014 [33,66]
+ CRUSH rule 3 x 1015 [106,45]
+ CRUSH rule 3 x 1016 [107,86]
+ CRUSH rule 3 x 1017 [12,61]
+ CRUSH rule 3 x 1018 [61,26]
+ CRUSH rule 3 x 1019 [27,104]
+ CRUSH rule 3 x 1020 [31,86]
+ CRUSH rule 3 x 1021 [22,26]
+ CRUSH rule 3 x 1022 [73,34]
+ CRUSH rule 3 x 1023 [88,79]
+ rule 3 (delltestrule) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,6]
+ CRUSH rule 3 x 1 [73,52]
+ CRUSH rule 3 x 2 [91,48]
+ CRUSH rule 3 x 3 [51,48]
+ CRUSH rule 3 x 4 [45,114]
+ CRUSH rule 3 x 5 [89,94]
+ CRUSH rule 3 x 6 [91,76]
+ CRUSH rule 3 x 7 [104,73]
+ CRUSH rule 3 x 8 [41,98]
+ CRUSH rule 3 x 9 [46,47]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,40]
+ CRUSH rule 3 x 12 [83,26]
+ CRUSH rule 3 x 13 [27,28]
+ CRUSH rule 3 x 14 [105,64]
+ CRUSH rule 3 x 15 [18,7]
+ CRUSH rule 3 x 16 [103,30]
+ CRUSH rule 3 x 17 [85,118]
+ CRUSH rule 3 x 18 [11,106]
+ CRUSH rule 3 x 19 [75,50]
+ CRUSH rule 3 x 20 [111,67]
+ CRUSH rule 3 x 21 [84,61]
+ CRUSH rule 3 x 22 [23,104]
+ CRUSH rule 3 x 23 [19,86]
+ CRUSH rule 3 x 24 [83,60]
+ CRUSH rule 3 x 25 [81,64]
+ CRUSH rule 3 x 26 [17,38]
+ CRUSH rule 3 x 27 [33,84]
+ CRUSH rule 3 x 28 [45,90]
+ CRUSH rule 3 x 29 [8,109]
+ CRUSH rule 3 x 30 [55,42]
+ CRUSH rule 3 x 31 [76,95]
+ CRUSH rule 3 x 32 [72,11]
+ CRUSH rule 3 x 33 [86,53]
+ CRUSH rule 3 x 34 [7,108]
+ CRUSH rule 3 x 35 [108,13]
+ CRUSH rule 3 x 36 [67,66]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,105]
+ CRUSH rule 3 x 39 [68,103]
+ CRUSH rule 3 x 40 [30,85]
+ CRUSH rule 3 x 41 [52,11]
+ CRUSH rule 3 x 42 [106,75]
+ CRUSH rule 3 x 43 [10,104]
+ CRUSH rule 3 x 44 [101,28]
+ CRUSH rule 3 x 45 [83,64]
+ CRUSH rule 3 x 46 [54,31]
+ CRUSH rule 3 x 47 [106,61]
+ CRUSH rule 3 x 48 [34,41]
+ CRUSH rule 3 x 49 [79,110]
+ CRUSH rule 3 x 50 [42,13]
+ CRUSH rule 3 x 51 [6,94]
+ CRUSH rule 3 x 52 [82,19]
+ CRUSH rule 3 x 53 [32,91]
+ CRUSH rule 3 x 54 [108,8]
+ CRUSH rule 3 x 55 [14,94]
+ CRUSH rule 3 x 56 [21,72]
+ CRUSH rule 3 x 57 [69,88]
+ CRUSH rule 3 x 58 [48,87]
+ CRUSH rule 3 x 59 [21,113]
+ CRUSH rule 3 x 60 [90,73]
+ CRUSH rule 3 x 61 [88,63]
+ CRUSH rule 3 x 62 [100,13]
+ CRUSH rule 3 x 63 [79,5]
+ CRUSH rule 3 x 64 [1,89]
+ CRUSH rule 3 x 65 [32,103]
+ CRUSH rule 3 x 66 [48,79]
+ CRUSH rule 3 x 67 [94,11]
+ CRUSH rule 3 x 68 [102,15]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,11]
+ CRUSH rule 3 x 71 [12,33]
+ CRUSH rule 3 x 72 [26,99]
+ CRUSH rule 3 x 73 [29,114]
+ CRUSH rule 3 x 74 [29,1]
+ CRUSH rule 3 x 75 [60,65]
+ CRUSH rule 3 x 76 [55,62]
+ CRUSH rule 3 x 77 [107,100]
+ CRUSH rule 3 x 78 [86,107]
+ CRUSH rule 3 x 79 [64,16]
+ CRUSH rule 3 x 80 [19,100]
+ CRUSH rule 3 x 81 [64,16]
+ CRUSH rule 3 x 82 [37,40]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,115]
+ CRUSH rule 3 x 85 [87,88]
+ CRUSH rule 3 x 86 [37,68]
+ CRUSH rule 3 x 87 [116,77]
+ CRUSH rule 3 x 88 [38,55]
+ CRUSH rule 3 x 89 [76,25]
+ CRUSH rule 3 x 90 [14,50]
+ CRUSH rule 3 x 91 [68,61]
+ CRUSH rule 3 x 92 [86,95]
+ CRUSH rule 3 x 93 [44,35]
+ CRUSH rule 3 x 94 [46,71]
+ CRUSH rule 3 x 95 [108,53]
+ CRUSH rule 3 x 96 [66,87]
+ CRUSH rule 3 x 97 [111,45]
+ CRUSH rule 3 x 98 [93,110]
+ CRUSH rule 3 x 99 [78,43]
+ CRUSH rule 3 x 100 [28,61]
+ CRUSH rule 3 x 101 [91,110]
+ CRUSH rule 3 x 102 [82,7]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,79]
+ CRUSH rule 3 x 105 [34,87]
+ CRUSH rule 3 x 106 [69,12]
+ CRUSH rule 3 x 107 [1,59]
+ CRUSH rule 3 x 108 [7,109]
+ CRUSH rule 3 x 109 [112,67]
+ CRUSH rule 3 x 110 [54,61]
+ CRUSH rule 3 x 111 [10,92]
+ CRUSH rule 3 x 112 [80,11]
+ CRUSH rule 3 x 113 [69,38]
+ CRUSH rule 3 x 114 [79,38]
+ CRUSH rule 3 x 115 [10,48]
+ CRUSH rule 3 x 116 [37,108]
+ CRUSH rule 3 x 117 [87,56]
+ CRUSH rule 3 x 118 [23,56]
+ CRUSH rule 3 x 119 [104,31]
+ CRUSH rule 3 x 120 [44,93]
+ CRUSH rule 3 x 121 [80,16]
+ CRUSH rule 3 x 122 [45,54]
+ CRUSH rule 3 x 123 [22,112]
+ CRUSH rule 3 x 124 [97,50]
+ CRUSH rule 3 x 125 [66,6]
+ CRUSH rule 3 x 126 [70,39]
+ CRUSH rule 3 x 127 [70,75]
+ CRUSH rule 3 x 128 [11,111]
+ CRUSH rule 3 x 129 [103,46]
+ CRUSH rule 3 x 130 [50,73]
+ CRUSH rule 3 x 131 [44,15]
+ CRUSH rule 3 x 132 [69,58]
+ CRUSH rule 3 x 133 [67,115]
+ CRUSH rule 3 x 134 [37,60]
+ CRUSH rule 3 x 135 [78,61]
+ CRUSH rule 3 x 136 [32,29]
+ CRUSH rule 3 x 137 [92,87]
+ CRUSH rule 3 x 138 [54,8]
+ CRUSH rule 3 x 139 [89,60]
+ CRUSH rule 3 x 140 [39,50]
+ CRUSH rule 3 x 141 [89,62]
+ CRUSH rule 3 x 142 [22,86]
+ CRUSH rule 3 x 143 [96,16]
+ CRUSH rule 3 x 144 [13,1]
+ CRUSH rule 3 x 145 [77,54]
+ CRUSH rule 3 x 146 [12,43]
+ CRUSH rule 3 x 147 [2,59]
+ CRUSH rule 3 x 148 [85,50]
+ CRUSH rule 3 x 149 [103,68]
+ CRUSH rule 3 x 150 [14,50]
+ CRUSH rule 3 x 151 [75,56]
+ CRUSH rule 3 x 152 [49,18]
+ CRUSH rule 3 x 153 [92,79]
+ CRUSH rule 3 x 154 [19,26]
+ CRUSH rule 3 x 155 [12,13]
+ CRUSH rule 3 x 156 [107,18]
+ CRUSH rule 3 x 157 [15,78]
+ CRUSH rule 3 x 158 [11,28]
+ CRUSH rule 3 x 159 [33,88]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,78]
+ CRUSH rule 3 x 162 [55,96]
+ CRUSH rule 3 x 163 [54,55]
+ CRUSH rule 3 x 164 [72,99]
+ CRUSH rule 3 x 165 [25,116]
+ CRUSH rule 3 x 166 [2,23]
+ CRUSH rule 3 x 167 [89,40]
+ CRUSH rule 3 x 168 [68,49]
+ CRUSH rule 3 x 169 [51,50]
+ CRUSH rule 3 x 170 [68,91]
+ CRUSH rule 3 x 171 [88,51]
+ CRUSH rule 3 x 172 [117,23]
+ CRUSH rule 3 x 173 [29,1]
+ CRUSH rule 3 x 174 [67,40]
+ CRUSH rule 3 x 175 [48,41]
+ CRUSH rule 3 x 176 [94,91]
+ CRUSH rule 3 x 177 [53,70]
+ CRUSH rule 3 x 178 [39,110]
+ CRUSH rule 3 x 179 [72,89]
+ CRUSH rule 3 x 180 [3,38]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,46]
+ CRUSH rule 3 x 183 [11,78]
+ CRUSH rule 3 x 184 [79,92]
+ CRUSH rule 3 x 185 [97,92]
+ CRUSH rule 3 x 186 [67,116]
+ CRUSH rule 3 x 187 [6,96]
+ CRUSH rule 3 x 188 [76,16]
+ CRUSH rule 3 x 189 [96,71]
+ CRUSH rule 3 x 190 [90,6]
+ CRUSH rule 3 x 191 [49,84]
+ CRUSH rule 3 x 192 [93,114]
+ CRUSH rule 3 x 193 [89,60]
+ CRUSH rule 3 x 194 [62,61]
+ CRUSH rule 3 x 195 [119,95]
+ CRUSH rule 3 x 196 [20,28]
+ CRUSH rule 3 x 197 [6,64]
+ CRUSH rule 3 x 198 [55,112]
+ CRUSH rule 3 x 199 [66,17]
+ CRUSH rule 3 x 200 [12,63]
+ CRUSH rule 3 x 201 [52,23]
+ CRUSH rule 3 x 202 [98,33]
+ CRUSH rule 3 x 203 [36,22]
+ CRUSH rule 3 x 204 [10,100]
+ CRUSH rule 3 x 205 [38,29]
+ CRUSH rule 3 x 206 [38,27]
+ CRUSH rule 3 x 207 [19,108]
+ CRUSH rule 3 x 208 [63,26]
+ CRUSH rule 3 x 209 [70,11]
+ CRUSH rule 3 x 210 [79,58]
+ CRUSH rule 3 x 211 [26,59]
+ CRUSH rule 3 x 212 [107,114]
+ CRUSH rule 3 x 213 [100,3]
+ CRUSH rule 3 x 214 [91,118]
+ CRUSH rule 3 x 215 [92,16]
+ CRUSH rule 3 x 216 [99,94]
+ CRUSH rule 3 x 217 [86,99]
+ CRUSH rule 3 x 218 [70,15]
+ CRUSH rule 3 x 219 [61,58]
+ CRUSH rule 3 x 220 [23,56]
+ CRUSH rule 3 x 221 [21,92]
+ CRUSH rule 3 x 222 [102,29]
+ CRUSH rule 3 x 223 [34,73]
+ CRUSH rule 3 x 224 [107,68]
+ CRUSH rule 3 x 225 [61,98]
+ CRUSH rule 3 x 226 [44,13]
+ CRUSH rule 3 x 227 [55,60]
+ CRUSH rule 3 x 228 [117,55]
+ CRUSH rule 3 x 229 [100,67]
+ CRUSH rule 3 x 230 [41,109]
+ CRUSH rule 3 x 231 [30,71]
+ CRUSH rule 3 x 232 [23,1]
+ CRUSH rule 3 x 233 [47,90]
+ CRUSH rule 3 x 234 [55,62]
+ CRUSH rule 3 x 235 [20,60]
+ CRUSH rule 3 x 236 [95,24]
+ CRUSH rule 3 x 237 [21,106]
+ CRUSH rule 3 x 238 [109,15]
+ CRUSH rule 3 x 239 [40,101]
+ CRUSH rule 3 x 240 [63,60]
+ CRUSH rule 3 x 241 [47,12]
+ CRUSH rule 3 x 242 [73,74]
+ CRUSH rule 3 x 243 [76,8]
+ CRUSH rule 3 x 244 [103,50]
+ CRUSH rule 3 x 245 [106,95]
+ CRUSH rule 3 x 246 [35,82]
+ CRUSH rule 3 x 247 [116,101]
+ CRUSH rule 3 x 248 [8,119]
+ CRUSH rule 3 x 249 [2,17]
+ CRUSH rule 3 x 250 [34,89]
+ CRUSH rule 3 x 251 [28,69]
+ CRUSH rule 3 x 252 [95,80]
+ CRUSH rule 3 x 253 [109,3]
+ CRUSH rule 3 x 254 [99,80]
+ CRUSH rule 3 x 255 [112,85]
+ CRUSH rule 3 x 256 [94,63]
+ CRUSH rule 3 x 257 [100,87]
+ CRUSH rule 3 x 258 [34,63]
+ CRUSH rule 3 x 259 [70,107]
+ CRUSH rule 3 x 260 [89,115]
+ CRUSH rule 3 x 261 [94,83]
+ CRUSH rule 3 x 262 [42,45]
+ CRUSH rule 3 x 263 [113,101]
+ CRUSH rule 3 x 264 [36,81]
+ CRUSH rule 3 x 265 [14,88]
+ CRUSH rule 3 x 266 [75,96]
+ CRUSH rule 3 x 267 [6,5]
+ CRUSH rule 3 x 268 [38,47]
+ CRUSH rule 3 x 269 [86,59]
+ CRUSH rule 3 x 270 [87,70]
+ CRUSH rule 3 x 271 [19,108]
+ CRUSH rule 3 x 272 [73,5]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,64]
+ CRUSH rule 3 x 275 [29,34]
+ CRUSH rule 3 x 276 [7,100]
+ CRUSH rule 3 x 277 [74,6]
+ CRUSH rule 3 x 278 [107,115]
+ CRUSH rule 3 x 279 [112,20]
+ CRUSH rule 3 x 280 [113,15]
+ CRUSH rule 3 x 281 [89,56]
+ CRUSH rule 3 x 282 [20,38]
+ CRUSH rule 3 x 283 [8,114]
+ CRUSH rule 3 x 284 [66,75]
+ CRUSH rule 3 x 285 [99,94]
+ CRUSH rule 3 x 286 [78,6]
+ CRUSH rule 3 x 287 [12,27]
+ CRUSH rule 3 x 288 [24,22]
+ CRUSH rule 3 x 289 [105,64]
+ CRUSH rule 3 x 290 [25,46]
+ CRUSH rule 3 x 291 [35,116]
+ CRUSH rule 3 x 292 [20,109]
+ CRUSH rule 3 x 293 [27,92]
+ CRUSH rule 3 x 294 [60,93]
+ CRUSH rule 3 x 295 [37,2]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,55]
+ CRUSH rule 3 x 298 [70,53]
+ CRUSH rule 3 x 299 [116,10]
+ CRUSH rule 3 x 300 [67,26]
+ CRUSH rule 3 x 301 [117,23]
+ CRUSH rule 3 x 302 [78,67]
+ CRUSH rule 3 x 303 [19,5]
+ CRUSH rule 3 x 304 [101,50]
+ CRUSH rule 3 x 305 [5,59]
+ CRUSH rule 3 x 306 [41,18]
+ CRUSH rule 3 x 307 [65,5]
+ CRUSH rule 3 x 308 [91,2]
+ CRUSH rule 3 x 309 [38,53]
+ CRUSH rule 3 x 310 [26,15]
+ CRUSH rule 3 x 311 [36,95]
+ CRUSH rule 3 x 312 [114,61]
+ CRUSH rule 3 x 313 [104,65]
+ CRUSH rule 3 x 314 [28,6]
+ CRUSH rule 3 x 315 [118,31]
+ CRUSH rule 3 x 316 [98,95]
+ CRUSH rule 3 x 317 [118,13]
+ CRUSH rule 3 x 318 [17,30]
+ CRUSH rule 3 x 319 [53,1]
+ CRUSH rule 3 x 320 [36,41]
+ CRUSH rule 3 x 321 [33,5]
+ CRUSH rule 3 x 322 [68,10]
+ CRUSH rule 3 x 323 [66,29]
+ CRUSH rule 3 x 324 [21,72]
+ CRUSH rule 3 x 325 [52,16]
+ CRUSH rule 3 x 326 [7,109]
+ CRUSH rule 3 x 327 [62,17]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,100]
+ CRUSH rule 3 x 330 [24,11]
+ CRUSH rule 3 x 331 [84,95]
+ CRUSH rule 3 x 332 [61,111]
+ CRUSH rule 3 x 333 [116,25]
+ CRUSH rule 3 x 334 [94,103]
+ CRUSH rule 3 x 335 [71,118]
+ CRUSH rule 3 x 336 [24,99]
+ CRUSH rule 3 x 337 [18,83]
+ CRUSH rule 3 x 338 [43,28]
+ CRUSH rule 3 x 339 [13,64]
+ CRUSH rule 3 x 340 [81,111]
+ CRUSH rule 3 x 341 [46,105]
+ CRUSH rule 3 x 342 [92,23]
+ CRUSH rule 3 x 343 [49,112]
+ CRUSH rule 3 x 344 [1,87]
+ CRUSH rule 3 x 345 [56,35]
+ CRUSH rule 3 x 346 [3,54]
+ CRUSH rule 3 x 347 [106,27]
+ CRUSH rule 3 x 348 [10,117]
+ CRUSH rule 3 x 349 [96,87]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,41]
+ CRUSH rule 3 x 352 [36,13]
+ CRUSH rule 3 x 353 [10,82]
+ CRUSH rule 3 x 354 [55,52]
+ CRUSH rule 3 x 355 [73,94]
+ CRUSH rule 3 x 356 [75,66]
+ CRUSH rule 3 x 357 [70,93]
+ CRUSH rule 3 x 358 [97,56]
+ CRUSH rule 3 x 359 [110,105]
+ CRUSH rule 3 x 360 [106,57]
+ CRUSH rule 3 x 361 [27,42]
+ CRUSH rule 3 x 362 [28,55]
+ CRUSH rule 3 x 363 [68,20]
+ CRUSH rule 3 x 364 [23,50]
+ CRUSH rule 3 x 365 [57,76]
+ CRUSH rule 3 x 366 [42,75]
+ CRUSH rule 3 x 367 [103,82]
+ CRUSH rule 3 x 368 [103,104]
+ CRUSH rule 3 x 369 [12,57]
+ CRUSH rule 3 x 370 [11,26]
+ CRUSH rule 3 x 371 [34,55]
+ CRUSH rule 3 x 372 [58,14]
+ CRUSH rule 3 x 373 [6,42]
+ CRUSH rule 3 x 374 [110,95]
+ CRUSH rule 3 x 375 [5,43]
+ CRUSH rule 3 x 376 [91,86]
+ CRUSH rule 3 x 377 [93,116]
+ CRUSH rule 3 x 378 [68,6]
+ CRUSH rule 3 x 379 [77,44]
+ CRUSH rule 3 x 380 [76,83]
+ CRUSH rule 3 x 381 [36,27]
+ CRUSH rule 3 x 382 [26,77]
+ CRUSH rule 3 x 383 [76,99]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,93]
+ CRUSH rule 3 x 386 [83,92]
+ CRUSH rule 3 x 387 [16,26]
+ CRUSH rule 3 x 388 [29,113]
+ CRUSH rule 3 x 389 [92,29]
+ CRUSH rule 3 x 390 [68,77]
+ CRUSH rule 3 x 391 [15,88]
+ CRUSH rule 3 x 392 [21,32]
+ CRUSH rule 3 x 393 [91,18]
+ CRUSH rule 3 x 394 [38,73]
+ CRUSH rule 3 x 395 [21,119]
+ CRUSH rule 3 x 396 [12,13]
+ CRUSH rule 3 x 397 [40,63]
+ CRUSH rule 3 x 398 [44,3]
+ CRUSH rule 3 x 399 [5,95]
+ CRUSH rule 3 x 400 [19,102]
+ CRUSH rule 3 x 401 [79,52]
+ CRUSH rule 3 x 402 [107,98]
+ CRUSH rule 3 x 403 [23,82]
+ CRUSH rule 3 x 404 [87,68]
+ CRUSH rule 3 x 405 [90,97]
+ CRUSH rule 3 x 406 [15,117]
+ CRUSH rule 3 x 407 [70,35]
+ CRUSH rule 3 x 408 [55,72]
+ CRUSH rule 3 x 409 [73,62]
+ CRUSH rule 3 x 410 [70,73]
+ CRUSH rule 3 x 411 [34,25]
+ CRUSH rule 3 x 412 [105,117]
+ CRUSH rule 3 x 413 [41,110]
+ CRUSH rule 3 x 414 [70,65]
+ CRUSH rule 3 x 415 [107,5]
+ CRUSH rule 3 x 416 [2,22]
+ CRUSH rule 3 x 417 [26,14]
+ CRUSH rule 3 x 418 [51,46]
+ CRUSH rule 3 x 419 [8,82]
+ CRUSH rule 3 x 420 [109,105]
+ CRUSH rule 3 x 421 [114,75]
+ CRUSH rule 3 x 422 [109,87]
+ CRUSH rule 3 x 423 [59,24]
+ CRUSH rule 3 x 424 [92,51]
+ CRUSH rule 3 x 425 [101,111]
+ CRUSH rule 3 x 426 [36,6]
+ CRUSH rule 3 x 427 [8,24]
+ CRUSH rule 3 x 428 [68,35]
+ CRUSH rule 3 x 429 [76,75]
+ CRUSH rule 3 x 430 [67,117]
+ CRUSH rule 3 x 431 [70,25]
+ CRUSH rule 3 x 432 [7,34]
+ CRUSH rule 3 x 433 [49,84]
+ CRUSH rule 3 x 434 [64,31]
+ CRUSH rule 3 x 435 [110,13]
+ CRUSH rule 3 x 436 [106,89]
+ CRUSH rule 3 x 437 [26,65]
+ CRUSH rule 3 x 438 [118,63]
+ CRUSH rule 3 x 439 [40,21]
+ CRUSH rule 3 x 440 [45,119]
+ CRUSH rule 3 x 441 [112,105]
+ CRUSH rule 3 x 442 [55,113]
+ CRUSH rule 3 x 443 [44,33]
+ CRUSH rule 3 x 444 [71,38]
+ CRUSH rule 3 x 445 [58,81]
+ CRUSH rule 3 x 446 [40,10]
+ CRUSH rule 3 x 447 [100,61]
+ CRUSH rule 3 x 448 [111,73]
+ CRUSH rule 3 x 449 [67,66]
+ CRUSH rule 3 x 450 [117,61]
+ CRUSH rule 3 x 451 [66,81]
+ CRUSH rule 3 x 452 [70,8]
+ CRUSH rule 3 x 453 [82,85]
+ CRUSH rule 3 x 454 [53,18]
+ CRUSH rule 3 x 455 [91,42]
+ CRUSH rule 3 x 456 [101,46]
+ CRUSH rule 3 x 457 [113,51]
+ CRUSH rule 3 x 458 [119,25]
+ CRUSH rule 3 x 459 [50,67]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,51]
+ CRUSH rule 3 x 462 [98,107]
+ CRUSH rule 3 x 463 [108,105]
+ CRUSH rule 3 x 464 [19,109]
+ CRUSH rule 3 x 465 [62,23]
+ CRUSH rule 3 x 466 [53,12]
+ CRUSH rule 3 x 467 [40,57]
+ CRUSH rule 3 x 468 [97,44]
+ CRUSH rule 3 x 469 [98,75]
+ CRUSH rule 3 x 470 [50,29]
+ CRUSH rule 3 x 471 [40,13]
+ CRUSH rule 3 x 472 [27,18]
+ CRUSH rule 3 x 473 [48,35]
+ CRUSH rule 3 x 474 [51,32]
+ CRUSH rule 3 x 475 [49,117]
+ CRUSH rule 3 x 476 [110,31]
+ CRUSH rule 3 x 477 [80,97]
+ CRUSH rule 3 x 478 [78,99]
+ CRUSH rule 3 x 479 [31,66]
+ CRUSH rule 3 x 480 [75,88]
+ CRUSH rule 3 x 481 [26,20]
+ CRUSH rule 3 x 482 [84,53]
+ CRUSH rule 3 x 483 [15,116]
+ CRUSH rule 3 x 484 [37,114]
+ CRUSH rule 3 x 485 [84,8]
+ CRUSH rule 3 x 486 [92,10]
+ CRUSH rule 3 x 487 [106,17]
+ CRUSH rule 3 x 488 [42,20]
+ CRUSH rule 3 x 489 [89,2]
+ CRUSH rule 3 x 490 [22,114]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,66]
+ CRUSH rule 3 x 493 [94,14]
+ CRUSH rule 3 x 494 [59,86]
+ CRUSH rule 3 x 495 [95,58]
+ CRUSH rule 3 x 496 [46,41]
+ CRUSH rule 3 x 497 [102,27]
+ CRUSH rule 3 x 498 [21,116]
+ CRUSH rule 3 x 499 [5,49]
+ CRUSH rule 3 x 500 [50,49]
+ CRUSH rule 3 x 501 [60,3]
+ CRUSH rule 3 x 502 [65,110]
+ CRUSH rule 3 x 503 [21,112]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,93]
+ CRUSH rule 3 x 506 [79,64]
+ CRUSH rule 3 x 507 [34,107]
+ CRUSH rule 3 x 508 [45,114]
+ CRUSH rule 3 x 509 [19,88]
+ CRUSH rule 3 x 510 [117,45]
+ CRUSH rule 3 x 511 [14,104]
+ CRUSH rule 3 x 512 [59,26]
+ CRUSH rule 3 x 513 [102,93]
+ CRUSH rule 3 x 514 [75,72]
+ CRUSH rule 3 x 515 [84,41]
+ CRUSH rule 3 x 516 [37,30]
+ CRUSH rule 3 x 517 [83,115]
+ CRUSH rule 3 x 518 [18,83]
+ CRUSH rule 3 x 519 [67,88]
+ CRUSH rule 3 x 520 [15,114]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,51]
+ CRUSH rule 3 x 523 [68,101]
+ CRUSH rule 3 x 524 [33,38]
+ CRUSH rule 3 x 525 [63,115]
+ CRUSH rule 3 x 526 [83,50]
+ CRUSH rule 3 x 527 [37,56]
+ CRUSH rule 3 x 528 [108,81]
+ CRUSH rule 3 x 529 [74,33]
+ CRUSH rule 3 x 530 [49,92]
+ CRUSH rule 3 x 531 [117,105]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,85]
+ CRUSH rule 3 x 534 [97,24]
+ CRUSH rule 3 x 535 [48,75]
+ CRUSH rule 3 x 536 [113,101]
+ CRUSH rule 3 x 537 [116,47]
+ CRUSH rule 3 x 538 [85,74]
+ CRUSH rule 3 x 539 [72,43]
+ CRUSH rule 3 x 540 [39,34]
+ CRUSH rule 3 x 541 [53,84]
+ CRUSH rule 3 x 542 [27,32]
+ CRUSH rule 3 x 543 [45,113]
+ CRUSH rule 3 x 544 [59,42]
+ CRUSH rule 3 x 545 [118,95]
+ CRUSH rule 3 x 546 [18,79]
+ CRUSH rule 3 x 547 [67,30]
+ CRUSH rule 3 x 548 [53,100]
+ CRUSH rule 3 x 549 [60,45]
+ CRUSH rule 3 x 550 [92,101]
+ CRUSH rule 3 x 551 [77,88]
+ CRUSH rule 3 x 552 [61,94]
+ CRUSH rule 3 x 553 [71,78]
+ CRUSH rule 3 x 554 [61,115]
+ CRUSH rule 3 x 555 [76,77]
+ CRUSH rule 3 x 556 [106,55]
+ CRUSH rule 3 x 557 [26,22]
+ CRUSH rule 3 x 558 [41,84]
+ CRUSH rule 3 x 559 [65,24]
+ CRUSH rule 3 x 560 [94,16]
+ CRUSH rule 3 x 561 [27,5]
+ CRUSH rule 3 x 562 [78,59]
+ CRUSH rule 3 x 563 [59,70]
+ CRUSH rule 3 x 564 [96,8]
+ CRUSH rule 3 x 565 [8,48]
+ CRUSH rule 3 x 566 [119,17]
+ CRUSH rule 3 x 567 [7,38]
+ CRUSH rule 3 x 568 [57,94]
+ CRUSH rule 3 x 569 [65,26]
+ CRUSH rule 3 x 570 [98,27]
+ CRUSH rule 3 x 571 [95,30]
+ CRUSH rule 3 x 572 [62,83]
+ CRUSH rule 3 x 573 [1,79]
+ CRUSH rule 3 x 574 [89,42]
+ CRUSH rule 3 x 575 [87,113]
+ CRUSH rule 3 x 576 [21,68]
+ CRUSH rule 3 x 577 [8,84]
+ CRUSH rule 3 x 578 [75,115]
+ CRUSH rule 3 x 579 [105,68]
+ CRUSH rule 3 x 580 [51,28]
+ CRUSH rule 3 x 581 [55,113]
+ CRUSH rule 3 x 582 [27,113]
+ CRUSH rule 3 x 583 [6,78]
+ CRUSH rule 3 x 584 [10,30]
+ CRUSH rule 3 x 585 [20,111]
+ CRUSH rule 3 x 586 [48,27]
+ CRUSH rule 3 x 587 [29,94]
+ CRUSH rule 3 x 588 [103,90]
+ CRUSH rule 3 x 589 [88,95]
+ CRUSH rule 3 x 590 [76,101]
+ CRUSH rule 3 x 591 [42,43]
+ CRUSH rule 3 x 592 [78,51]
+ CRUSH rule 3 x 593 [82,71]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,59]
+ CRUSH rule 3 x 597 [16,36]
+ CRUSH rule 3 x 598 [37,56]
+ CRUSH rule 3 x 599 [10,84]
+ CRUSH rule 3 x 600 [24,69]
+ CRUSH rule 3 x 601 [104,14]
+ CRUSH rule 3 x 602 [48,45]
+ CRUSH rule 3 x 603 [93,32]
+ CRUSH rule 3 x 604 [118,79]
+ CRUSH rule 3 x 605 [104,53]
+ CRUSH rule 3 x 606 [90,83]
+ CRUSH rule 3 x 607 [95,110]
+ CRUSH rule 3 x 608 [112,101]
+ CRUSH rule 3 x 609 [34,99]
+ CRUSH rule 3 x 610 [106,16]
+ CRUSH rule 3 x 611 [66,87]
+ CRUSH rule 3 x 612 [2,81]
+ CRUSH rule 3 x 613 [13,86]
+ CRUSH rule 3 x 614 [50,3]
+ CRUSH rule 3 x 615 [24,73]
+ CRUSH rule 3 x 616 [41,119]
+ CRUSH rule 3 x 617 [81,106]
+ CRUSH rule 3 x 618 [3,104]
+ CRUSH rule 3 x 619 [92,7]
+ CRUSH rule 3 x 620 [108,11]
+ CRUSH rule 3 x 621 [105,115]
+ CRUSH rule 3 x 622 [67,48]
+ CRUSH rule 3 x 623 [69,74]
+ CRUSH rule 3 x 624 [115,49]
+ CRUSH rule 3 x 625 [73,109]
+ CRUSH rule 3 x 626 [52,3]
+ CRUSH rule 3 x 627 [116,3]
+ CRUSH rule 3 x 628 [98,91]
+ CRUSH rule 3 x 629 [6,112]
+ CRUSH rule 3 x 630 [22,72]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,71]
+ CRUSH rule 3 x 633 [65,12]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,46]
+ CRUSH rule 3 x 636 [23,70]
+ CRUSH rule 3 x 637 [99,24]
+ CRUSH rule 3 x 638 [43,114]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,73]
+ CRUSH rule 3 x 641 [45,84]
+ CRUSH rule 3 x 642 [47,66]
+ CRUSH rule 3 x 643 [64,8]
+ CRUSH rule 3 x 644 [31,82]
+ CRUSH rule 3 x 645 [77,64]
+ CRUSH rule 3 x 646 [37,86]
+ CRUSH rule 3 x 647 [65,56]
+ CRUSH rule 3 x 648 [84,13]
+ CRUSH rule 3 x 649 [88,55]
+ CRUSH rule 3 x 650 [21,76]
+ CRUSH rule 3 x 651 [63,116]
+ CRUSH rule 3 x 652 [57,112]
+ CRUSH rule 3 x 653 [38,61]
+ CRUSH rule 3 x 654 [104,67]
+ CRUSH rule 3 x 655 [89,54]
+ CRUSH rule 3 x 656 [84,49]
+ CRUSH rule 3 x 657 [47,32]
+ CRUSH rule 3 x 658 [80,29]
+ CRUSH rule 3 x 659 [11,112]
+ CRUSH rule 3 x 660 [65,111]
+ CRUSH rule 3 x 661 [96,73]
+ CRUSH rule 3 x 662 [111,73]
+ CRUSH rule 3 x 663 [83,60]
+ CRUSH rule 3 x 664 [59,80]
+ CRUSH rule 3 x 665 [31,117]
+ CRUSH rule 3 x 666 [112,101]
+ CRUSH rule 3 x 667 [70,47]
+ CRUSH rule 3 x 668 [96,57]
+ CRUSH rule 3 x 669 [56,39]
+ CRUSH rule 3 x 670 [98,105]
+ CRUSH rule 3 x 671 [57,48]
+ CRUSH rule 3 x 672 [37,36]
+ CRUSH rule 3 x 673 [83,2]
+ CRUSH rule 3 x 674 [36,25]
+ CRUSH rule 3 x 675 [88,14]
+ CRUSH rule 3 x 676 [3,110]
+ CRUSH rule 3 x 677 [88,67]
+ CRUSH rule 3 x 678 [27,44]
+ CRUSH rule 3 x 679 [33,116]
+ CRUSH rule 3 x 680 [111,39]
+ CRUSH rule 3 x 681 [53,12]
+ CRUSH rule 3 x 682 [12,87]
+ CRUSH rule 3 x 683 [24,85]
+ CRUSH rule 3 x 684 [98,65]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,72]
+ CRUSH rule 3 x 688 [16,114]
+ CRUSH rule 3 x 689 [32,31]
+ CRUSH rule 3 x 690 [96,33]
+ CRUSH rule 3 x 691 [34,6]
+ CRUSH rule 3 x 692 [97,84]
+ CRUSH rule 3 x 693 [29,118]
+ CRUSH rule 3 x 694 [6,30]
+ CRUSH rule 3 x 695 [31,72]
+ CRUSH rule 3 x 696 [104,97]
+ CRUSH rule 3 x 697 [19,96]
+ CRUSH rule 3 x 698 [30,69]
+ CRUSH rule 3 x 699 [47,76]
+ CRUSH rule 3 x 700 [82,55]
+ CRUSH rule 3 x 701 [53,80]
+ CRUSH rule 3 x 702 [95,98]
+ CRUSH rule 3 x 703 [92,65]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,1]
+ CRUSH rule 3 x 706 [74,35]
+ CRUSH rule 3 x 707 [91,115]
+ CRUSH rule 3 x 708 [95,112]
+ CRUSH rule 3 x 709 [73,72]
+ CRUSH rule 3 x 710 [94,47]
+ CRUSH rule 3 x 711 [68,41]
+ CRUSH rule 3 x 712 [107,18]
+ CRUSH rule 3 x 713 [29,109]
+ CRUSH rule 3 x 714 [86,61]
+ CRUSH rule 3 x 715 [74,13]
+ CRUSH rule 3 x 716 [101,56]
+ CRUSH rule 3 x 717 [12,29]
+ CRUSH rule 3 x 718 [83,24]
+ CRUSH rule 3 x 719 [26,10]
+ CRUSH rule 3 x 720 [69,2]
+ CRUSH rule 3 x 721 [51,42]
+ CRUSH rule 3 x 722 [15,74]
+ CRUSH rule 3 x 723 [117,14]
+ CRUSH rule 3 x 724 [45,38]
+ CRUSH rule 3 x 725 [53,110]
+ CRUSH rule 3 x 726 [103,68]
+ CRUSH rule 3 x 727 [89,100]
+ CRUSH rule 3 x 728 [76,16]
+ CRUSH rule 3 x 729 [35,90]
+ CRUSH rule 3 x 730 [28,103]
+ CRUSH rule 3 x 731 [78,41]
+ CRUSH rule 3 x 732 [1,27]
+ CRUSH rule 3 x 733 [35,100]
+ CRUSH rule 3 x 734 [119,85]
+ CRUSH rule 3 x 735 [102,43]
+ CRUSH rule 3 x 736 [37,92]
+ CRUSH rule 3 x 737 [117,11]
+ CRUSH rule 3 x 738 [57,32]
+ CRUSH rule 3 x 739 [87,1]
+ CRUSH rule 3 x 740 [29,80]
+ CRUSH rule 3 x 741 [47,111]
+ CRUSH rule 3 x 742 [106,83]
+ CRUSH rule 3 x 743 [105,94]
+ CRUSH rule 3 x 744 [23,64]
+ CRUSH rule 3 x 745 [37,112]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,95]
+ CRUSH rule 3 x 748 [48,14]
+ CRUSH rule 3 x 749 [102,101]
+ CRUSH rule 3 x 750 [83,78]
+ CRUSH rule 3 x 751 [25,104]
+ CRUSH rule 3 x 752 [82,95]
+ CRUSH rule 3 x 753 [14,113]
+ CRUSH rule 3 x 754 [114,51]
+ CRUSH rule 3 x 755 [87,26]
+ CRUSH rule 3 x 756 [113,87]
+ CRUSH rule 3 x 757 [47,66]
+ CRUSH rule 3 x 758 [54,63]
+ CRUSH rule 3 x 759 [74,20]
+ CRUSH rule 3 x 760 [88,22]
+ CRUSH rule 3 x 761 [73,86]
+ CRUSH rule 3 x 762 [34,17]
+ CRUSH rule 3 x 763 [13,78]
+ CRUSH rule 3 x 764 [89,42]
+ CRUSH rule 3 x 765 [109,91]
+ CRUSH rule 3 x 766 [19,66]
+ CRUSH rule 3 x 767 [41,26]
+ CRUSH rule 3 x 768 [106,57]
+ CRUSH rule 3 x 769 [91,104]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,35]
+ CRUSH rule 3 x 772 [97,108]
+ CRUSH rule 3 x 773 [116,47]
+ CRUSH rule 3 x 774 [100,31]
+ CRUSH rule 3 x 775 [102,43]
+ CRUSH rule 3 x 776 [69,38]
+ CRUSH rule 3 x 777 [91,52]
+ CRUSH rule 3 x 778 [83,119]
+ CRUSH rule 3 x 779 [47,60]
+ CRUSH rule 3 x 780 [63,70]
+ CRUSH rule 3 x 781 [105,2]
+ CRUSH rule 3 x 782 [117,59]
+ CRUSH rule 3 x 783 [19,109]
+ CRUSH rule 3 x 784 [63,114]
+ CRUSH rule 3 x 785 [27,84]
+ CRUSH rule 3 x 786 [41,110]
+ CRUSH rule 3 x 787 [108,73]
+ CRUSH rule 3 x 788 [74,103]
+ CRUSH rule 3 x 789 [50,17]
+ CRUSH rule 3 x 790 [20,106]
+ CRUSH rule 3 x 791 [96,87]
+ CRUSH rule 3 x 792 [80,97]
+ CRUSH rule 3 x 793 [6,26]
+ CRUSH rule 3 x 794 [14,42]
+ CRUSH rule 3 x 795 [30,8]
+ CRUSH rule 3 x 796 [87,36]
+ CRUSH rule 3 x 797 [64,61]
+ CRUSH rule 3 x 798 [42,69]
+ CRUSH rule 3 x 799 [19,117]
+ CRUSH rule 3 x 800 [106,8]
+ CRUSH rule 3 x 801 [2,57]
+ CRUSH rule 3 x 802 [63,68]
+ CRUSH rule 3 x 803 [46,35]
+ CRUSH rule 3 x 804 [33,26]
+ CRUSH rule 3 x 805 [96,49]
+ CRUSH rule 3 x 806 [48,25]
+ CRUSH rule 3 x 807 [48,83]
+ CRUSH rule 3 x 808 [76,31]
+ CRUSH rule 3 x 809 [27,48]
+ CRUSH rule 3 x 810 [119,71]
+ CRUSH rule 3 x 811 [111,91]
+ CRUSH rule 3 x 812 [25,111]
+ CRUSH rule 3 x 813 [81,28]
+ CRUSH rule 3 x 814 [95,42]
+ CRUSH rule 3 x 815 [84,61]
+ CRUSH rule 3 x 816 [64,35]
+ CRUSH rule 3 x 817 [63,60]
+ CRUSH rule 3 x 818 [69,46]
+ CRUSH rule 3 x 819 [88,75]
+ CRUSH rule 3 x 820 [104,57]
+ CRUSH rule 3 x 821 [58,21]
+ CRUSH rule 3 x 822 [20,80]
+ CRUSH rule 3 x 823 [63,118]
+ CRUSH rule 3 x 824 [102,13]
+ CRUSH rule 3 x 825 [47,118]
+ CRUSH rule 3 x 826 [44,7]
+ CRUSH rule 3 x 827 [101,88]
+ CRUSH rule 3 x 828 [60,41]
+ CRUSH rule 3 x 829 [45,102]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,75]
+ CRUSH rule 3 x 833 [57,32]
+ CRUSH rule 3 x 834 [90,33]
+ CRUSH rule 3 x 835 [6,1]
+ CRUSH rule 3 x 836 [63,68]
+ CRUSH rule 3 x 837 [76,71]
+ CRUSH rule 3 x 838 [106,20]
+ CRUSH rule 3 x 839 [87,96]
+ CRUSH rule 3 x 840 [33,32]
+ CRUSH rule 3 x 841 [110,55]
+ CRUSH rule 3 x 842 [66,87]
+ CRUSH rule 3 x 843 [11,80]
+ CRUSH rule 3 x 844 [74,103]
+ CRUSH rule 3 x 845 [74,43]
+ CRUSH rule 3 x 846 [43,76]
+ CRUSH rule 3 x 847 [62,20]
+ CRUSH rule 3 x 848 [92,17]
+ CRUSH rule 3 x 849 [93,36]
+ CRUSH rule 3 x 850 [83,82]
+ CRUSH rule 3 x 851 [65,94]
+ CRUSH rule 3 x 852 [60,22]
+ CRUSH rule 3 x 853 [88,29]
+ CRUSH rule 3 x 854 [83,54]
+ CRUSH rule 3 x 855 [2,101]
+ CRUSH rule 3 x 856 [40,41]
+ CRUSH rule 3 x 857 [69,82]
+ CRUSH rule 3 x 858 [98,81]
+ CRUSH rule 3 x 859 [56,43]
+ CRUSH rule 3 x 860 [11,26]
+ CRUSH rule 3 x 861 [22,110]
+ CRUSH rule 3 x 862 [22,70]
+ CRUSH rule 3 x 863 [79,84]
+ CRUSH rule 3 x 864 [77,24]
+ CRUSH rule 3 x 865 [119,17]
+ CRUSH rule 3 x 866 [18,49]
+ CRUSH rule 3 x 867 [3,84]
+ CRUSH rule 3 x 868 [100,107]
+ CRUSH rule 3 x 869 [22,104]
+ CRUSH rule 3 x 870 [73,30]
+ CRUSH rule 3 x 871 [84,105]
+ CRUSH rule 3 x 872 [72,75]
+ CRUSH rule 3 x 873 [81,96]
+ CRUSH rule 3 x 874 [21,72]
+ CRUSH rule 3 x 875 [115,59]
+ CRUSH rule 3 x 876 [98,49]
+ CRUSH rule 3 x 877 [80,79]
+ CRUSH rule 3 x 878 [87,94]
+ CRUSH rule 3 x 879 [29,18]
+ CRUSH rule 3 x 880 [23,40]
+ CRUSH rule 3 x 881 [109,69]
+ CRUSH rule 3 x 882 [31,118]
+ CRUSH rule 3 x 883 [102,8]
+ CRUSH rule 3 x 884 [80,19]
+ CRUSH rule 3 x 885 [46,101]
+ CRUSH rule 3 x 886 [2,65]
+ CRUSH rule 3 x 887 [5,99]
+ CRUSH rule 3 x 888 [16,70]
+ CRUSH rule 3 x 889 [84,93]
+ CRUSH rule 3 x 890 [65,118]
+ CRUSH rule 3 x 891 [86,105]
+ CRUSH rule 3 x 892 [64,10]
+ CRUSH rule 3 x 893 [20,110]
+ CRUSH rule 3 x 894 [32,47]
+ CRUSH rule 3 x 895 [40,21]
+ CRUSH rule 3 x 896 [113,14]
+ CRUSH rule 3 x 897 [107,80]
+ CRUSH rule 3 x 898 [76,71]
+ CRUSH rule 3 x 899 [75,82]
+ CRUSH rule 3 x 900 [83,82]
+ CRUSH rule 3 x 901 [66,61]
+ CRUSH rule 3 x 902 [25,56]
+ CRUSH rule 3 x 903 [53,46]
+ CRUSH rule 3 x 904 [50,101]
+ CRUSH rule 3 x 905 [99,110]
+ CRUSH rule 3 x 906 [68,27]
+ CRUSH rule 3 x 907 [109,47]
+ CRUSH rule 3 x 908 [47,1]
+ CRUSH rule 3 x 909 [73,2]
+ CRUSH rule 3 x 910 [71,74]
+ CRUSH rule 3 x 911 [39,42]
+ CRUSH rule 3 x 912 [90,7]
+ CRUSH rule 3 x 913 [29,96]
+ CRUSH rule 3 x 914 [84,45]
+ CRUSH rule 3 x 915 [49,115]
+ CRUSH rule 3 x 916 [32,77]
+ CRUSH rule 3 x 917 [46,23]
+ CRUSH rule 3 x 918 [82,73]
+ CRUSH rule 3 x 919 [13,28]
+ CRUSH rule 3 x 920 [25,26]
+ CRUSH rule 3 x 921 [55,119]
+ CRUSH rule 3 x 922 [33,32]
+ CRUSH rule 3 x 923 [28,15]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,22]
+ CRUSH rule 3 x 926 [64,69]
+ CRUSH rule 3 x 927 [32,16]
+ CRUSH rule 3 x 928 [13,113]
+ CRUSH rule 3 x 929 [85,115]
+ CRUSH rule 3 x 930 [104,20]
+ CRUSH rule 3 x 931 [46,79]
+ CRUSH rule 3 x 932 [43,52]
+ CRUSH rule 3 x 933 [18,55]
+ CRUSH rule 3 x 934 [68,81]
+ CRUSH rule 3 x 935 [28,57]
+ CRUSH rule 3 x 936 [104,57]
+ CRUSH rule 3 x 937 [110,10]
+ CRUSH rule 3 x 938 [48,23]
+ CRUSH rule 3 x 939 [77,42]
+ CRUSH rule 3 x 940 [76,49]
+ CRUSH rule 3 x 941 [66,101]
+ CRUSH rule 3 x 942 [80,87]
+ CRUSH rule 3 x 943 [75,74]
+ CRUSH rule 3 x 944 [82,53]
+ CRUSH rule 3 x 945 [71,74]
+ CRUSH rule 3 x 946 [37,42]
+ CRUSH rule 3 x 947 [107,30]
+ CRUSH rule 3 x 948 [108,95]
+ CRUSH rule 3 x 949 [46,103]
+ CRUSH rule 3 x 950 [96,101]
+ CRUSH rule 3 x 951 [40,14]
+ CRUSH rule 3 x 952 [114,21]
+ CRUSH rule 3 x 953 [62,23]
+ CRUSH rule 3 x 954 [103,5]
+ CRUSH rule 3 x 955 [42,73]
+ CRUSH rule 3 x 956 [72,103]
+ CRUSH rule 3 x 957 [117,22]
+ CRUSH rule 3 x 958 [23,106]
+ CRUSH rule 3 x 959 [42,93]
+ CRUSH rule 3 x 960 [113,8]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,51]
+ CRUSH rule 3 x 963 [101,106]
+ CRUSH rule 3 x 964 [66,89]
+ CRUSH rule 3 x 965 [47,102]
+ CRUSH rule 3 x 966 [88,63]
+ CRUSH rule 3 x 967 [71,46]
+ CRUSH rule 3 x 968 [74,51]
+ CRUSH rule 3 x 969 [53,78]
+ CRUSH rule 3 x 970 [3,30]
+ CRUSH rule 3 x 971 [66,107]
+ CRUSH rule 3 x 972 [3,66]
+ CRUSH rule 3 x 973 [113,20]
+ CRUSH rule 3 x 974 [114,35]
+ CRUSH rule 3 x 975 [83,58]
+ CRUSH rule 3 x 976 [81,48]
+ CRUSH rule 3 x 977 [95,102]
+ CRUSH rule 3 x 978 [119,41]
+ CRUSH rule 3 x 979 [98,6]
+ CRUSH rule 3 x 980 [39,108]
+ CRUSH rule 3 x 981 [89,84]
+ CRUSH rule 3 x 982 [19,94]
+ CRUSH rule 3 x 983 [34,45]
+ CRUSH rule 3 x 984 [78,63]
+ CRUSH rule 3 x 985 [99,52]
+ CRUSH rule 3 x 986 [44,99]
+ CRUSH rule 3 x 987 [25,32]
+ CRUSH rule 3 x 988 [79,2]
+ CRUSH rule 3 x 989 [87,26]
+ CRUSH rule 3 x 990 [72,69]
+ CRUSH rule 3 x 991 [90,8]
+ CRUSH rule 3 x 992 [30,67]
+ CRUSH rule 3 x 993 [74,49]
+ CRUSH rule 3 x 994 [74,105]
+ CRUSH rule 3 x 995 [100,97]
+ CRUSH rule 3 x 996 [41,58]
+ CRUSH rule 3 x 997 [89,76]
+ CRUSH rule 3 x 998 [92,47]
+ CRUSH rule 3 x 999 [117,16]
+ CRUSH rule 3 x 1000 [50,47]
+ CRUSH rule 3 x 1001 [83,102]
+ CRUSH rule 3 x 1002 [94,37]
+ CRUSH rule 3 x 1003 [43,88]
+ CRUSH rule 3 x 1004 [89,54]
+ CRUSH rule 3 x 1005 [105,84]
+ CRUSH rule 3 x 1006 [45,111]
+ CRUSH rule 3 x 1007 [19,66]
+ CRUSH rule 3 x 1008 [31,76]
+ CRUSH rule 3 x 1009 [1,95]
+ CRUSH rule 3 x 1010 [31,113]
+ CRUSH rule 3 x 1011 [64,81]
+ CRUSH rule 3 x 1012 [68,49]
+ CRUSH rule 3 x 1013 [5,93]
+ CRUSH rule 3 x 1014 [33,66]
+ CRUSH rule 3 x 1015 [106,45]
+ CRUSH rule 3 x 1016 [107,86]
+ CRUSH rule 3 x 1017 [12,61]
+ CRUSH rule 3 x 1018 [61,26]
+ CRUSH rule 3 x 1019 [27,104]
+ CRUSH rule 3 x 1020 [31,86]
+ CRUSH rule 3 x 1021 [22,26]
+ CRUSH rule 3 x 1022 [73,34]
+ CRUSH rule 3 x 1023 [88,79]
+ rule 3 (delltestrule) num_rep 3 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,6]
+ CRUSH rule 3 x 1 [73,52]
+ CRUSH rule 3 x 2 [91,48]
+ CRUSH rule 3 x 3 [51,48]
+ CRUSH rule 3 x 4 [45,114]
+ CRUSH rule 3 x 5 [89,94]
+ CRUSH rule 3 x 6 [91,76]
+ CRUSH rule 3 x 7 [104,73]
+ CRUSH rule 3 x 8 [41,98]
+ CRUSH rule 3 x 9 [46,47]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,40]
+ CRUSH rule 3 x 12 [83,26]
+ CRUSH rule 3 x 13 [27,28]
+ CRUSH rule 3 x 14 [105,64]
+ CRUSH rule 3 x 15 [18,7]
+ CRUSH rule 3 x 16 [103,30]
+ CRUSH rule 3 x 17 [85,118]
+ CRUSH rule 3 x 18 [11,106]
+ CRUSH rule 3 x 19 [75,50]
+ CRUSH rule 3 x 20 [111,67]
+ CRUSH rule 3 x 21 [84,61]
+ CRUSH rule 3 x 22 [23,104]
+ CRUSH rule 3 x 23 [19,86]
+ CRUSH rule 3 x 24 [83,60]
+ CRUSH rule 3 x 25 [81,64]
+ CRUSH rule 3 x 26 [17,38]
+ CRUSH rule 3 x 27 [33,84]
+ CRUSH rule 3 x 28 [45,90]
+ CRUSH rule 3 x 29 [8,109]
+ CRUSH rule 3 x 30 [55,42]
+ CRUSH rule 3 x 31 [76,95]
+ CRUSH rule 3 x 32 [72,11]
+ CRUSH rule 3 x 33 [86,53]
+ CRUSH rule 3 x 34 [7,108]
+ CRUSH rule 3 x 35 [108,13]
+ CRUSH rule 3 x 36 [67,66]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,105]
+ CRUSH rule 3 x 39 [68,103]
+ CRUSH rule 3 x 40 [30,85]
+ CRUSH rule 3 x 41 [52,11]
+ CRUSH rule 3 x 42 [106,75]
+ CRUSH rule 3 x 43 [10,104]
+ CRUSH rule 3 x 44 [101,28]
+ CRUSH rule 3 x 45 [83,64]
+ CRUSH rule 3 x 46 [54,31]
+ CRUSH rule 3 x 47 [106,61]
+ CRUSH rule 3 x 48 [34,41]
+ CRUSH rule 3 x 49 [79,110]
+ CRUSH rule 3 x 50 [42,13]
+ CRUSH rule 3 x 51 [6,94]
+ CRUSH rule 3 x 52 [82,19]
+ CRUSH rule 3 x 53 [32,91]
+ CRUSH rule 3 x 54 [108,8]
+ CRUSH rule 3 x 55 [14,94]
+ CRUSH rule 3 x 56 [21,72]
+ CRUSH rule 3 x 57 [69,88]
+ CRUSH rule 3 x 58 [48,87]
+ CRUSH rule 3 x 59 [21,113]
+ CRUSH rule 3 x 60 [90,73]
+ CRUSH rule 3 x 61 [88,63]
+ CRUSH rule 3 x 62 [100,13]
+ CRUSH rule 3 x 63 [79,5]
+ CRUSH rule 3 x 64 [1,89]
+ CRUSH rule 3 x 65 [32,103]
+ CRUSH rule 3 x 66 [48,79]
+ CRUSH rule 3 x 67 [94,11]
+ CRUSH rule 3 x 68 [102,15]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,11]
+ CRUSH rule 3 x 71 [12,33]
+ CRUSH rule 3 x 72 [26,99]
+ CRUSH rule 3 x 73 [29,114]
+ CRUSH rule 3 x 74 [29,1]
+ CRUSH rule 3 x 75 [60,65]
+ CRUSH rule 3 x 76 [55,62]
+ CRUSH rule 3 x 77 [107,100]
+ CRUSH rule 3 x 78 [86,107]
+ CRUSH rule 3 x 79 [64,16]
+ CRUSH rule 3 x 80 [19,100]
+ CRUSH rule 3 x 81 [64,16]
+ CRUSH rule 3 x 82 [37,40]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,115]
+ CRUSH rule 3 x 85 [87,88]
+ CRUSH rule 3 x 86 [37,68]
+ CRUSH rule 3 x 87 [116,77]
+ CRUSH rule 3 x 88 [38,55]
+ CRUSH rule 3 x 89 [76,25]
+ CRUSH rule 3 x 90 [14,50]
+ CRUSH rule 3 x 91 [68,61]
+ CRUSH rule 3 x 92 [86,95]
+ CRUSH rule 3 x 93 [44,35]
+ CRUSH rule 3 x 94 [46,71]
+ CRUSH rule 3 x 95 [108,53]
+ CRUSH rule 3 x 96 [66,87]
+ CRUSH rule 3 x 97 [111,45]
+ CRUSH rule 3 x 98 [93,110]
+ CRUSH rule 3 x 99 [78,43]
+ CRUSH rule 3 x 100 [28,61]
+ CRUSH rule 3 x 101 [91,110]
+ CRUSH rule 3 x 102 [82,7]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,79]
+ CRUSH rule 3 x 105 [34,87]
+ CRUSH rule 3 x 106 [69,12]
+ CRUSH rule 3 x 107 [1,59]
+ CRUSH rule 3 x 108 [7,109]
+ CRUSH rule 3 x 109 [112,67]
+ CRUSH rule 3 x 110 [54,61]
+ CRUSH rule 3 x 111 [10,92]
+ CRUSH rule 3 x 112 [80,11]
+ CRUSH rule 3 x 113 [69,38]
+ CRUSH rule 3 x 114 [79,38]
+ CRUSH rule 3 x 115 [10,48]
+ CRUSH rule 3 x 116 [37,108]
+ CRUSH rule 3 x 117 [87,56]
+ CRUSH rule 3 x 118 [23,56]
+ CRUSH rule 3 x 119 [104,31]
+ CRUSH rule 3 x 120 [44,93]
+ CRUSH rule 3 x 121 [80,16]
+ CRUSH rule 3 x 122 [45,54]
+ CRUSH rule 3 x 123 [22,112]
+ CRUSH rule 3 x 124 [97,50]
+ CRUSH rule 3 x 125 [66,6]
+ CRUSH rule 3 x 126 [70,39]
+ CRUSH rule 3 x 127 [70,75]
+ CRUSH rule 3 x 128 [11,111]
+ CRUSH rule 3 x 129 [103,46]
+ CRUSH rule 3 x 130 [50,73]
+ CRUSH rule 3 x 131 [44,15]
+ CRUSH rule 3 x 132 [69,58]
+ CRUSH rule 3 x 133 [67,115]
+ CRUSH rule 3 x 134 [37,60]
+ CRUSH rule 3 x 135 [78,61]
+ CRUSH rule 3 x 136 [32,29]
+ CRUSH rule 3 x 137 [92,87]
+ CRUSH rule 3 x 138 [54,8]
+ CRUSH rule 3 x 139 [89,60]
+ CRUSH rule 3 x 140 [39,50]
+ CRUSH rule 3 x 141 [89,62]
+ CRUSH rule 3 x 142 [22,86]
+ CRUSH rule 3 x 143 [96,16]
+ CRUSH rule 3 x 144 [13,1]
+ CRUSH rule 3 x 145 [77,54]
+ CRUSH rule 3 x 146 [12,43]
+ CRUSH rule 3 x 147 [2,59]
+ CRUSH rule 3 x 148 [85,50]
+ CRUSH rule 3 x 149 [103,68]
+ CRUSH rule 3 x 150 [14,50]
+ CRUSH rule 3 x 151 [75,56]
+ CRUSH rule 3 x 152 [49,18]
+ CRUSH rule 3 x 153 [92,79]
+ CRUSH rule 3 x 154 [19,26]
+ CRUSH rule 3 x 155 [12,13]
+ CRUSH rule 3 x 156 [107,18]
+ CRUSH rule 3 x 157 [15,78]
+ CRUSH rule 3 x 158 [11,28]
+ CRUSH rule 3 x 159 [33,88]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,78]
+ CRUSH rule 3 x 162 [55,96]
+ CRUSH rule 3 x 163 [54,55]
+ CRUSH rule 3 x 164 [72,99]
+ CRUSH rule 3 x 165 [25,116]
+ CRUSH rule 3 x 166 [2,23]
+ CRUSH rule 3 x 167 [89,40]
+ CRUSH rule 3 x 168 [68,49]
+ CRUSH rule 3 x 169 [51,50]
+ CRUSH rule 3 x 170 [68,91]
+ CRUSH rule 3 x 171 [88,51]
+ CRUSH rule 3 x 172 [117,23]
+ CRUSH rule 3 x 173 [29,1]
+ CRUSH rule 3 x 174 [67,40]
+ CRUSH rule 3 x 175 [48,41]
+ CRUSH rule 3 x 176 [94,91]
+ CRUSH rule 3 x 177 [53,70]
+ CRUSH rule 3 x 178 [39,110]
+ CRUSH rule 3 x 179 [72,89]
+ CRUSH rule 3 x 180 [3,38]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,46]
+ CRUSH rule 3 x 183 [11,78]
+ CRUSH rule 3 x 184 [79,92]
+ CRUSH rule 3 x 185 [97,92]
+ CRUSH rule 3 x 186 [67,116]
+ CRUSH rule 3 x 187 [6,96]
+ CRUSH rule 3 x 188 [76,16]
+ CRUSH rule 3 x 189 [96,71]
+ CRUSH rule 3 x 190 [90,6]
+ CRUSH rule 3 x 191 [49,84]
+ CRUSH rule 3 x 192 [93,114]
+ CRUSH rule 3 x 193 [89,60]
+ CRUSH rule 3 x 194 [62,61]
+ CRUSH rule 3 x 195 [119,95]
+ CRUSH rule 3 x 196 [20,28]
+ CRUSH rule 3 x 197 [6,64]
+ CRUSH rule 3 x 198 [55,112]
+ CRUSH rule 3 x 199 [66,17]
+ CRUSH rule 3 x 200 [12,63]
+ CRUSH rule 3 x 201 [52,23]
+ CRUSH rule 3 x 202 [98,33]
+ CRUSH rule 3 x 203 [36,22]
+ CRUSH rule 3 x 204 [10,100]
+ CRUSH rule 3 x 205 [38,29]
+ CRUSH rule 3 x 206 [38,27]
+ CRUSH rule 3 x 207 [19,108]
+ CRUSH rule 3 x 208 [63,26]
+ CRUSH rule 3 x 209 [70,11]
+ CRUSH rule 3 x 210 [79,58]
+ CRUSH rule 3 x 211 [26,59]
+ CRUSH rule 3 x 212 [107,114]
+ CRUSH rule 3 x 213 [100,3]
+ CRUSH rule 3 x 214 [91,118]
+ CRUSH rule 3 x 215 [92,16]
+ CRUSH rule 3 x 216 [99,94]
+ CRUSH rule 3 x 217 [86,99]
+ CRUSH rule 3 x 218 [70,15]
+ CRUSH rule 3 x 219 [61,58]
+ CRUSH rule 3 x 220 [23,56]
+ CRUSH rule 3 x 221 [21,92]
+ CRUSH rule 3 x 222 [102,29]
+ CRUSH rule 3 x 223 [34,73]
+ CRUSH rule 3 x 224 [107,68]
+ CRUSH rule 3 x 225 [61,98]
+ CRUSH rule 3 x 226 [44,13]
+ CRUSH rule 3 x 227 [55,60]
+ CRUSH rule 3 x 228 [117,55]
+ CRUSH rule 3 x 229 [100,67]
+ CRUSH rule 3 x 230 [41,109]
+ CRUSH rule 3 x 231 [30,71]
+ CRUSH rule 3 x 232 [23,1]
+ CRUSH rule 3 x 233 [47,90]
+ CRUSH rule 3 x 234 [55,62]
+ CRUSH rule 3 x 235 [20,60]
+ CRUSH rule 3 x 236 [95,24]
+ CRUSH rule 3 x 237 [21,106]
+ CRUSH rule 3 x 238 [109,15]
+ CRUSH rule 3 x 239 [40,101]
+ CRUSH rule 3 x 240 [63,60]
+ CRUSH rule 3 x 241 [47,12]
+ CRUSH rule 3 x 242 [73,74]
+ CRUSH rule 3 x 243 [76,8]
+ CRUSH rule 3 x 244 [103,50]
+ CRUSH rule 3 x 245 [106,95]
+ CRUSH rule 3 x 246 [35,82]
+ CRUSH rule 3 x 247 [116,101]
+ CRUSH rule 3 x 248 [8,119]
+ CRUSH rule 3 x 249 [2,17]
+ CRUSH rule 3 x 250 [34,89]
+ CRUSH rule 3 x 251 [28,69]
+ CRUSH rule 3 x 252 [95,80]
+ CRUSH rule 3 x 253 [109,3]
+ CRUSH rule 3 x 254 [99,80]
+ CRUSH rule 3 x 255 [112,85]
+ CRUSH rule 3 x 256 [94,63]
+ CRUSH rule 3 x 257 [100,87]
+ CRUSH rule 3 x 258 [34,63]
+ CRUSH rule 3 x 259 [70,107]
+ CRUSH rule 3 x 260 [89,115]
+ CRUSH rule 3 x 261 [94,83]
+ CRUSH rule 3 x 262 [42,45]
+ CRUSH rule 3 x 263 [113,101]
+ CRUSH rule 3 x 264 [36,81]
+ CRUSH rule 3 x 265 [14,88]
+ CRUSH rule 3 x 266 [75,96]
+ CRUSH rule 3 x 267 [6,5]
+ CRUSH rule 3 x 268 [38,47]
+ CRUSH rule 3 x 269 [86,59]
+ CRUSH rule 3 x 270 [87,70]
+ CRUSH rule 3 x 271 [19,108]
+ CRUSH rule 3 x 272 [73,5]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,64]
+ CRUSH rule 3 x 275 [29,34]
+ CRUSH rule 3 x 276 [7,100]
+ CRUSH rule 3 x 277 [74,6]
+ CRUSH rule 3 x 278 [107,115]
+ CRUSH rule 3 x 279 [112,20]
+ CRUSH rule 3 x 280 [113,15]
+ CRUSH rule 3 x 281 [89,56]
+ CRUSH rule 3 x 282 [20,38]
+ CRUSH rule 3 x 283 [8,114]
+ CRUSH rule 3 x 284 [66,75]
+ CRUSH rule 3 x 285 [99,94]
+ CRUSH rule 3 x 286 [78,6]
+ CRUSH rule 3 x 287 [12,27]
+ CRUSH rule 3 x 288 [24,22]
+ CRUSH rule 3 x 289 [105,64]
+ CRUSH rule 3 x 290 [25,46]
+ CRUSH rule 3 x 291 [35,116]
+ CRUSH rule 3 x 292 [20,109]
+ CRUSH rule 3 x 293 [27,92]
+ CRUSH rule 3 x 294 [60,93]
+ CRUSH rule 3 x 295 [37,2]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,55]
+ CRUSH rule 3 x 298 [70,53]
+ CRUSH rule 3 x 299 [116,10]
+ CRUSH rule 3 x 300 [67,26]
+ CRUSH rule 3 x 301 [117,23]
+ CRUSH rule 3 x 302 [78,67]
+ CRUSH rule 3 x 303 [19,5]
+ CRUSH rule 3 x 304 [101,50]
+ CRUSH rule 3 x 305 [5,59]
+ CRUSH rule 3 x 306 [41,18]
+ CRUSH rule 3 x 307 [65,5]
+ CRUSH rule 3 x 308 [91,2]
+ CRUSH rule 3 x 309 [38,53]
+ CRUSH rule 3 x 310 [26,15]
+ CRUSH rule 3 x 311 [36,95]
+ CRUSH rule 3 x 312 [114,61]
+ CRUSH rule 3 x 313 [104,65]
+ CRUSH rule 3 x 314 [28,6]
+ CRUSH rule 3 x 315 [118,31]
+ CRUSH rule 3 x 316 [98,95]
+ CRUSH rule 3 x 317 [118,13]
+ CRUSH rule 3 x 318 [17,30]
+ CRUSH rule 3 x 319 [53,1]
+ CRUSH rule 3 x 320 [36,41]
+ CRUSH rule 3 x 321 [33,5]
+ CRUSH rule 3 x 322 [68,10]
+ CRUSH rule 3 x 323 [66,29]
+ CRUSH rule 3 x 324 [21,72]
+ CRUSH rule 3 x 325 [52,16]
+ CRUSH rule 3 x 326 [7,109]
+ CRUSH rule 3 x 327 [62,17]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,100]
+ CRUSH rule 3 x 330 [24,11]
+ CRUSH rule 3 x 331 [84,95]
+ CRUSH rule 3 x 332 [61,111]
+ CRUSH rule 3 x 333 [116,25]
+ CRUSH rule 3 x 334 [94,103]
+ CRUSH rule 3 x 335 [71,118]
+ CRUSH rule 3 x 336 [24,99]
+ CRUSH rule 3 x 337 [18,83]
+ CRUSH rule 3 x 338 [43,28]
+ CRUSH rule 3 x 339 [13,64]
+ CRUSH rule 3 x 340 [81,111]
+ CRUSH rule 3 x 341 [46,105]
+ CRUSH rule 3 x 342 [92,23]
+ CRUSH rule 3 x 343 [49,112]
+ CRUSH rule 3 x 344 [1,87]
+ CRUSH rule 3 x 345 [56,35]
+ CRUSH rule 3 x 346 [3,54]
+ CRUSH rule 3 x 347 [106,27]
+ CRUSH rule 3 x 348 [10,117]
+ CRUSH rule 3 x 349 [96,87]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,41]
+ CRUSH rule 3 x 352 [36,13]
+ CRUSH rule 3 x 353 [10,82]
+ CRUSH rule 3 x 354 [55,52]
+ CRUSH rule 3 x 355 [73,94]
+ CRUSH rule 3 x 356 [75,66]
+ CRUSH rule 3 x 357 [70,93]
+ CRUSH rule 3 x 358 [97,56]
+ CRUSH rule 3 x 359 [110,105]
+ CRUSH rule 3 x 360 [106,57]
+ CRUSH rule 3 x 361 [27,42]
+ CRUSH rule 3 x 362 [28,55]
+ CRUSH rule 3 x 363 [68,20]
+ CRUSH rule 3 x 364 [23,50]
+ CRUSH rule 3 x 365 [57,76]
+ CRUSH rule 3 x 366 [42,75]
+ CRUSH rule 3 x 367 [103,82]
+ CRUSH rule 3 x 368 [103,104]
+ CRUSH rule 3 x 369 [12,57]
+ CRUSH rule 3 x 370 [11,26]
+ CRUSH rule 3 x 371 [34,55]
+ CRUSH rule 3 x 372 [58,14]
+ CRUSH rule 3 x 373 [6,42]
+ CRUSH rule 3 x 374 [110,95]
+ CRUSH rule 3 x 375 [5,43]
+ CRUSH rule 3 x 376 [91,86]
+ CRUSH rule 3 x 377 [93,116]
+ CRUSH rule 3 x 378 [68,6]
+ CRUSH rule 3 x 379 [77,44]
+ CRUSH rule 3 x 380 [76,83]
+ CRUSH rule 3 x 381 [36,27]
+ CRUSH rule 3 x 382 [26,77]
+ CRUSH rule 3 x 383 [76,99]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,93]
+ CRUSH rule 3 x 386 [83,92]
+ CRUSH rule 3 x 387 [16,26]
+ CRUSH rule 3 x 388 [29,113]
+ CRUSH rule 3 x 389 [92,29]
+ CRUSH rule 3 x 390 [68,77]
+ CRUSH rule 3 x 391 [15,88]
+ CRUSH rule 3 x 392 [21,32]
+ CRUSH rule 3 x 393 [91,18]
+ CRUSH rule 3 x 394 [38,73]
+ CRUSH rule 3 x 395 [21,119]
+ CRUSH rule 3 x 396 [12,13]
+ CRUSH rule 3 x 397 [40,63]
+ CRUSH rule 3 x 398 [44,3]
+ CRUSH rule 3 x 399 [5,95]
+ CRUSH rule 3 x 400 [19,102]
+ CRUSH rule 3 x 401 [79,52]
+ CRUSH rule 3 x 402 [107,98]
+ CRUSH rule 3 x 403 [23,82]
+ CRUSH rule 3 x 404 [87,68]
+ CRUSH rule 3 x 405 [90,97]
+ CRUSH rule 3 x 406 [15,117]
+ CRUSH rule 3 x 407 [70,35]
+ CRUSH rule 3 x 408 [55,72]
+ CRUSH rule 3 x 409 [73,62]
+ CRUSH rule 3 x 410 [70,73]
+ CRUSH rule 3 x 411 [34,25]
+ CRUSH rule 3 x 412 [105,117]
+ CRUSH rule 3 x 413 [41,110]
+ CRUSH rule 3 x 414 [70,65]
+ CRUSH rule 3 x 415 [107,5]
+ CRUSH rule 3 x 416 [2,22]
+ CRUSH rule 3 x 417 [26,14]
+ CRUSH rule 3 x 418 [51,46]
+ CRUSH rule 3 x 419 [8,82]
+ CRUSH rule 3 x 420 [109,105]
+ CRUSH rule 3 x 421 [114,75]
+ CRUSH rule 3 x 422 [109,87]
+ CRUSH rule 3 x 423 [59,24]
+ CRUSH rule 3 x 424 [92,51]
+ CRUSH rule 3 x 425 [101,111]
+ CRUSH rule 3 x 426 [36,6]
+ CRUSH rule 3 x 427 [8,24]
+ CRUSH rule 3 x 428 [68,35]
+ CRUSH rule 3 x 429 [76,75]
+ CRUSH rule 3 x 430 [67,117]
+ CRUSH rule 3 x 431 [70,25]
+ CRUSH rule 3 x 432 [7,34]
+ CRUSH rule 3 x 433 [49,84]
+ CRUSH rule 3 x 434 [64,31]
+ CRUSH rule 3 x 435 [110,13]
+ CRUSH rule 3 x 436 [106,89]
+ CRUSH rule 3 x 437 [26,65]
+ CRUSH rule 3 x 438 [118,63]
+ CRUSH rule 3 x 439 [40,21]
+ CRUSH rule 3 x 440 [45,119]
+ CRUSH rule 3 x 441 [112,105]
+ CRUSH rule 3 x 442 [55,113]
+ CRUSH rule 3 x 443 [44,33]
+ CRUSH rule 3 x 444 [71,38]
+ CRUSH rule 3 x 445 [58,81]
+ CRUSH rule 3 x 446 [40,10]
+ CRUSH rule 3 x 447 [100,61]
+ CRUSH rule 3 x 448 [111,73]
+ CRUSH rule 3 x 449 [67,66]
+ CRUSH rule 3 x 450 [117,61]
+ CRUSH rule 3 x 451 [66,81]
+ CRUSH rule 3 x 452 [70,8]
+ CRUSH rule 3 x 453 [82,85]
+ CRUSH rule 3 x 454 [53,18]
+ CRUSH rule 3 x 455 [91,42]
+ CRUSH rule 3 x 456 [101,46]
+ CRUSH rule 3 x 457 [113,51]
+ CRUSH rule 3 x 458 [119,25]
+ CRUSH rule 3 x 459 [50,67]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,51]
+ CRUSH rule 3 x 462 [98,107]
+ CRUSH rule 3 x 463 [108,105]
+ CRUSH rule 3 x 464 [19,109]
+ CRUSH rule 3 x 465 [62,23]
+ CRUSH rule 3 x 466 [53,12]
+ CRUSH rule 3 x 467 [40,57]
+ CRUSH rule 3 x 468 [97,44]
+ CRUSH rule 3 x 469 [98,75]
+ CRUSH rule 3 x 470 [50,29]
+ CRUSH rule 3 x 471 [40,13]
+ CRUSH rule 3 x 472 [27,18]
+ CRUSH rule 3 x 473 [48,35]
+ CRUSH rule 3 x 474 [51,32]
+ CRUSH rule 3 x 475 [49,117]
+ CRUSH rule 3 x 476 [110,31]
+ CRUSH rule 3 x 477 [80,97]
+ CRUSH rule 3 x 478 [78,99]
+ CRUSH rule 3 x 479 [31,66]
+ CRUSH rule 3 x 480 [75,88]
+ CRUSH rule 3 x 481 [26,20]
+ CRUSH rule 3 x 482 [84,53]
+ CRUSH rule 3 x 483 [15,116]
+ CRUSH rule 3 x 484 [37,114]
+ CRUSH rule 3 x 485 [84,8]
+ CRUSH rule 3 x 486 [92,10]
+ CRUSH rule 3 x 487 [106,17]
+ CRUSH rule 3 x 488 [42,20]
+ CRUSH rule 3 x 489 [89,2]
+ CRUSH rule 3 x 490 [22,114]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,66]
+ CRUSH rule 3 x 493 [94,14]
+ CRUSH rule 3 x 494 [59,86]
+ CRUSH rule 3 x 495 [95,58]
+ CRUSH rule 3 x 496 [46,41]
+ CRUSH rule 3 x 497 [102,27]
+ CRUSH rule 3 x 498 [21,116]
+ CRUSH rule 3 x 499 [5,49]
+ CRUSH rule 3 x 500 [50,49]
+ CRUSH rule 3 x 501 [60,3]
+ CRUSH rule 3 x 502 [65,110]
+ CRUSH rule 3 x 503 [21,112]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,93]
+ CRUSH rule 3 x 506 [79,64]
+ CRUSH rule 3 x 507 [34,107]
+ CRUSH rule 3 x 508 [45,114]
+ CRUSH rule 3 x 509 [19,88]
+ CRUSH rule 3 x 510 [117,45]
+ CRUSH rule 3 x 511 [14,104]
+ CRUSH rule 3 x 512 [59,26]
+ CRUSH rule 3 x 513 [102,93]
+ CRUSH rule 3 x 514 [75,72]
+ CRUSH rule 3 x 515 [84,41]
+ CRUSH rule 3 x 516 [37,30]
+ CRUSH rule 3 x 517 [83,115]
+ CRUSH rule 3 x 518 [18,83]
+ CRUSH rule 3 x 519 [67,88]
+ CRUSH rule 3 x 520 [15,114]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,51]
+ CRUSH rule 3 x 523 [68,101]
+ CRUSH rule 3 x 524 [33,38]
+ CRUSH rule 3 x 525 [63,115]
+ CRUSH rule 3 x 526 [83,50]
+ CRUSH rule 3 x 527 [37,56]
+ CRUSH rule 3 x 528 [108,81]
+ CRUSH rule 3 x 529 [74,33]
+ CRUSH rule 3 x 530 [49,92]
+ CRUSH rule 3 x 531 [117,105]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,85]
+ CRUSH rule 3 x 534 [97,24]
+ CRUSH rule 3 x 535 [48,75]
+ CRUSH rule 3 x 536 [113,101]
+ CRUSH rule 3 x 537 [116,47]
+ CRUSH rule 3 x 538 [85,74]
+ CRUSH rule 3 x 539 [72,43]
+ CRUSH rule 3 x 540 [39,34]
+ CRUSH rule 3 x 541 [53,84]
+ CRUSH rule 3 x 542 [27,32]
+ CRUSH rule 3 x 543 [45,113]
+ CRUSH rule 3 x 544 [59,42]
+ CRUSH rule 3 x 545 [118,95]
+ CRUSH rule 3 x 546 [18,79]
+ CRUSH rule 3 x 547 [67,30]
+ CRUSH rule 3 x 548 [53,100]
+ CRUSH rule 3 x 549 [60,45]
+ CRUSH rule 3 x 550 [92,101]
+ CRUSH rule 3 x 551 [77,88]
+ CRUSH rule 3 x 552 [61,94]
+ CRUSH rule 3 x 553 [71,78]
+ CRUSH rule 3 x 554 [61,115]
+ CRUSH rule 3 x 555 [76,77]
+ CRUSH rule 3 x 556 [106,55]
+ CRUSH rule 3 x 557 [26,22]
+ CRUSH rule 3 x 558 [41,84]
+ CRUSH rule 3 x 559 [65,24]
+ CRUSH rule 3 x 560 [94,16]
+ CRUSH rule 3 x 561 [27,5]
+ CRUSH rule 3 x 562 [78,59]
+ CRUSH rule 3 x 563 [59,70]
+ CRUSH rule 3 x 564 [96,8]
+ CRUSH rule 3 x 565 [8,48]
+ CRUSH rule 3 x 566 [119,17]
+ CRUSH rule 3 x 567 [7,38]
+ CRUSH rule 3 x 568 [57,94]
+ CRUSH rule 3 x 569 [65,26]
+ CRUSH rule 3 x 570 [98,27]
+ CRUSH rule 3 x 571 [95,30]
+ CRUSH rule 3 x 572 [62,83]
+ CRUSH rule 3 x 573 [1,79]
+ CRUSH rule 3 x 574 [89,42]
+ CRUSH rule 3 x 575 [87,113]
+ CRUSH rule 3 x 576 [21,68]
+ CRUSH rule 3 x 577 [8,84]
+ CRUSH rule 3 x 578 [75,115]
+ CRUSH rule 3 x 579 [105,68]
+ CRUSH rule 3 x 580 [51,28]
+ CRUSH rule 3 x 581 [55,113]
+ CRUSH rule 3 x 582 [27,113]
+ CRUSH rule 3 x 583 [6,78]
+ CRUSH rule 3 x 584 [10,30]
+ CRUSH rule 3 x 585 [20,111]
+ CRUSH rule 3 x 586 [48,27]
+ CRUSH rule 3 x 587 [29,94]
+ CRUSH rule 3 x 588 [103,90]
+ CRUSH rule 3 x 589 [88,95]
+ CRUSH rule 3 x 590 [76,101]
+ CRUSH rule 3 x 591 [42,43]
+ CRUSH rule 3 x 592 [78,51]
+ CRUSH rule 3 x 593 [82,71]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,59]
+ CRUSH rule 3 x 597 [16,36]
+ CRUSH rule 3 x 598 [37,56]
+ CRUSH rule 3 x 599 [10,84]
+ CRUSH rule 3 x 600 [24,69]
+ CRUSH rule 3 x 601 [104,14]
+ CRUSH rule 3 x 602 [48,45]
+ CRUSH rule 3 x 603 [93,32]
+ CRUSH rule 3 x 604 [118,79]
+ CRUSH rule 3 x 605 [104,53]
+ CRUSH rule 3 x 606 [90,83]
+ CRUSH rule 3 x 607 [95,110]
+ CRUSH rule 3 x 608 [112,101]
+ CRUSH rule 3 x 609 [34,99]
+ CRUSH rule 3 x 610 [106,16]
+ CRUSH rule 3 x 611 [66,87]
+ CRUSH rule 3 x 612 [2,81]
+ CRUSH rule 3 x 613 [13,86]
+ CRUSH rule 3 x 614 [50,3]
+ CRUSH rule 3 x 615 [24,73]
+ CRUSH rule 3 x 616 [41,119]
+ CRUSH rule 3 x 617 [81,106]
+ CRUSH rule 3 x 618 [3,104]
+ CRUSH rule 3 x 619 [92,7]
+ CRUSH rule 3 x 620 [108,11]
+ CRUSH rule 3 x 621 [105,115]
+ CRUSH rule 3 x 622 [67,48]
+ CRUSH rule 3 x 623 [69,74]
+ CRUSH rule 3 x 624 [115,49]
+ CRUSH rule 3 x 625 [73,109]
+ CRUSH rule 3 x 626 [52,3]
+ CRUSH rule 3 x 627 [116,3]
+ CRUSH rule 3 x 628 [98,91]
+ CRUSH rule 3 x 629 [6,112]
+ CRUSH rule 3 x 630 [22,72]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,71]
+ CRUSH rule 3 x 633 [65,12]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,46]
+ CRUSH rule 3 x 636 [23,70]
+ CRUSH rule 3 x 637 [99,24]
+ CRUSH rule 3 x 638 [43,114]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,73]
+ CRUSH rule 3 x 641 [45,84]
+ CRUSH rule 3 x 642 [47,66]
+ CRUSH rule 3 x 643 [64,8]
+ CRUSH rule 3 x 644 [31,82]
+ CRUSH rule 3 x 645 [77,64]
+ CRUSH rule 3 x 646 [37,86]
+ CRUSH rule 3 x 647 [65,56]
+ CRUSH rule 3 x 648 [84,13]
+ CRUSH rule 3 x 649 [88,55]
+ CRUSH rule 3 x 650 [21,76]
+ CRUSH rule 3 x 651 [63,116]
+ CRUSH rule 3 x 652 [57,112]
+ CRUSH rule 3 x 653 [38,61]
+ CRUSH rule 3 x 654 [104,67]
+ CRUSH rule 3 x 655 [89,54]
+ CRUSH rule 3 x 656 [84,49]
+ CRUSH rule 3 x 657 [47,32]
+ CRUSH rule 3 x 658 [80,29]
+ CRUSH rule 3 x 659 [11,112]
+ CRUSH rule 3 x 660 [65,111]
+ CRUSH rule 3 x 661 [96,73]
+ CRUSH rule 3 x 662 [111,73]
+ CRUSH rule 3 x 663 [83,60]
+ CRUSH rule 3 x 664 [59,80]
+ CRUSH rule 3 x 665 [31,117]
+ CRUSH rule 3 x 666 [112,101]
+ CRUSH rule 3 x 667 [70,47]
+ CRUSH rule 3 x 668 [96,57]
+ CRUSH rule 3 x 669 [56,39]
+ CRUSH rule 3 x 670 [98,105]
+ CRUSH rule 3 x 671 [57,48]
+ CRUSH rule 3 x 672 [37,36]
+ CRUSH rule 3 x 673 [83,2]
+ CRUSH rule 3 x 674 [36,25]
+ CRUSH rule 3 x 675 [88,14]
+ CRUSH rule 3 x 676 [3,110]
+ CRUSH rule 3 x 677 [88,67]
+ CRUSH rule 3 x 678 [27,44]
+ CRUSH rule 3 x 679 [33,116]
+ CRUSH rule 3 x 680 [111,39]
+ CRUSH rule 3 x 681 [53,12]
+ CRUSH rule 3 x 682 [12,87]
+ CRUSH rule 3 x 683 [24,85]
+ CRUSH rule 3 x 684 [98,65]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,72]
+ CRUSH rule 3 x 688 [16,114]
+ CRUSH rule 3 x 689 [32,31]
+ CRUSH rule 3 x 690 [96,33]
+ CRUSH rule 3 x 691 [34,6]
+ CRUSH rule 3 x 692 [97,84]
+ CRUSH rule 3 x 693 [29,118]
+ CRUSH rule 3 x 694 [6,30]
+ CRUSH rule 3 x 695 [31,72]
+ CRUSH rule 3 x 696 [104,97]
+ CRUSH rule 3 x 697 [19,96]
+ CRUSH rule 3 x 698 [30,69]
+ CRUSH rule 3 x 699 [47,76]
+ CRUSH rule 3 x 700 [82,55]
+ CRUSH rule 3 x 701 [53,80]
+ CRUSH rule 3 x 702 [95,98]
+ CRUSH rule 3 x 703 [92,65]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,1]
+ CRUSH rule 3 x 706 [74,35]
+ CRUSH rule 3 x 707 [91,115]
+ CRUSH rule 3 x 708 [95,112]
+ CRUSH rule 3 x 709 [73,72]
+ CRUSH rule 3 x 710 [94,47]
+ CRUSH rule 3 x 711 [68,41]
+ CRUSH rule 3 x 712 [107,18]
+ CRUSH rule 3 x 713 [29,109]
+ CRUSH rule 3 x 714 [86,61]
+ CRUSH rule 3 x 715 [74,13]
+ CRUSH rule 3 x 716 [101,56]
+ CRUSH rule 3 x 717 [12,29]
+ CRUSH rule 3 x 718 [83,24]
+ CRUSH rule 3 x 719 [26,10]
+ CRUSH rule 3 x 720 [69,2]
+ CRUSH rule 3 x 721 [51,42]
+ CRUSH rule 3 x 722 [15,74]
+ CRUSH rule 3 x 723 [117,14]
+ CRUSH rule 3 x 724 [45,38]
+ CRUSH rule 3 x 725 [53,110]
+ CRUSH rule 3 x 726 [103,68]
+ CRUSH rule 3 x 727 [89,100]
+ CRUSH rule 3 x 728 [76,16]
+ CRUSH rule 3 x 729 [35,90]
+ CRUSH rule 3 x 730 [28,103]
+ CRUSH rule 3 x 731 [78,41]
+ CRUSH rule 3 x 732 [1,27]
+ CRUSH rule 3 x 733 [35,100]
+ CRUSH rule 3 x 734 [119,85]
+ CRUSH rule 3 x 735 [102,43]
+ CRUSH rule 3 x 736 [37,92]
+ CRUSH rule 3 x 737 [117,11]
+ CRUSH rule 3 x 738 [57,32]
+ CRUSH rule 3 x 739 [87,1]
+ CRUSH rule 3 x 740 [29,80]
+ CRUSH rule 3 x 741 [47,111]
+ CRUSH rule 3 x 742 [106,83]
+ CRUSH rule 3 x 743 [105,94]
+ CRUSH rule 3 x 744 [23,64]
+ CRUSH rule 3 x 745 [37,112]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,95]
+ CRUSH rule 3 x 748 [48,14]
+ CRUSH rule 3 x 749 [102,101]
+ CRUSH rule 3 x 750 [83,78]
+ CRUSH rule 3 x 751 [25,104]
+ CRUSH rule 3 x 752 [82,95]
+ CRUSH rule 3 x 753 [14,113]
+ CRUSH rule 3 x 754 [114,51]
+ CRUSH rule 3 x 755 [87,26]
+ CRUSH rule 3 x 756 [113,87]
+ CRUSH rule 3 x 757 [47,66]
+ CRUSH rule 3 x 758 [54,63]
+ CRUSH rule 3 x 759 [74,20]
+ CRUSH rule 3 x 760 [88,22]
+ CRUSH rule 3 x 761 [73,86]
+ CRUSH rule 3 x 762 [34,17]
+ CRUSH rule 3 x 763 [13,78]
+ CRUSH rule 3 x 764 [89,42]
+ CRUSH rule 3 x 765 [109,91]
+ CRUSH rule 3 x 766 [19,66]
+ CRUSH rule 3 x 767 [41,26]
+ CRUSH rule 3 x 768 [106,57]
+ CRUSH rule 3 x 769 [91,104]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,35]
+ CRUSH rule 3 x 772 [97,108]
+ CRUSH rule 3 x 773 [116,47]
+ CRUSH rule 3 x 774 [100,31]
+ CRUSH rule 3 x 775 [102,43]
+ CRUSH rule 3 x 776 [69,38]
+ CRUSH rule 3 x 777 [91,52]
+ CRUSH rule 3 x 778 [83,119]
+ CRUSH rule 3 x 779 [47,60]
+ CRUSH rule 3 x 780 [63,70]
+ CRUSH rule 3 x 781 [105,2]
+ CRUSH rule 3 x 782 [117,59]
+ CRUSH rule 3 x 783 [19,109]
+ CRUSH rule 3 x 784 [63,114]
+ CRUSH rule 3 x 785 [27,84]
+ CRUSH rule 3 x 786 [41,110]
+ CRUSH rule 3 x 787 [108,73]
+ CRUSH rule 3 x 788 [74,103]
+ CRUSH rule 3 x 789 [50,17]
+ CRUSH rule 3 x 790 [20,106]
+ CRUSH rule 3 x 791 [96,87]
+ CRUSH rule 3 x 792 [80,97]
+ CRUSH rule 3 x 793 [6,26]
+ CRUSH rule 3 x 794 [14,42]
+ CRUSH rule 3 x 795 [30,8]
+ CRUSH rule 3 x 796 [87,36]
+ CRUSH rule 3 x 797 [64,61]
+ CRUSH rule 3 x 798 [42,69]
+ CRUSH rule 3 x 799 [19,117]
+ CRUSH rule 3 x 800 [106,8]
+ CRUSH rule 3 x 801 [2,57]
+ CRUSH rule 3 x 802 [63,68]
+ CRUSH rule 3 x 803 [46,35]
+ CRUSH rule 3 x 804 [33,26]
+ CRUSH rule 3 x 805 [96,49]
+ CRUSH rule 3 x 806 [48,25]
+ CRUSH rule 3 x 807 [48,83]
+ CRUSH rule 3 x 808 [76,31]
+ CRUSH rule 3 x 809 [27,48]
+ CRUSH rule 3 x 810 [119,71]
+ CRUSH rule 3 x 811 [111,91]
+ CRUSH rule 3 x 812 [25,111]
+ CRUSH rule 3 x 813 [81,28]
+ CRUSH rule 3 x 814 [95,42]
+ CRUSH rule 3 x 815 [84,61]
+ CRUSH rule 3 x 816 [64,35]
+ CRUSH rule 3 x 817 [63,60]
+ CRUSH rule 3 x 818 [69,46]
+ CRUSH rule 3 x 819 [88,75]
+ CRUSH rule 3 x 820 [104,57]
+ CRUSH rule 3 x 821 [58,21]
+ CRUSH rule 3 x 822 [20,80]
+ CRUSH rule 3 x 823 [63,118]
+ CRUSH rule 3 x 824 [102,13]
+ CRUSH rule 3 x 825 [47,118]
+ CRUSH rule 3 x 826 [44,7]
+ CRUSH rule 3 x 827 [101,88]
+ CRUSH rule 3 x 828 [60,41]
+ CRUSH rule 3 x 829 [45,102]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,75]
+ CRUSH rule 3 x 833 [57,32]
+ CRUSH rule 3 x 834 [90,33]
+ CRUSH rule 3 x 835 [6,1]
+ CRUSH rule 3 x 836 [63,68]
+ CRUSH rule 3 x 837 [76,71]
+ CRUSH rule 3 x 838 [106,20]
+ CRUSH rule 3 x 839 [87,96]
+ CRUSH rule 3 x 840 [33,32]
+ CRUSH rule 3 x 841 [110,55]
+ CRUSH rule 3 x 842 [66,87]
+ CRUSH rule 3 x 843 [11,80]
+ CRUSH rule 3 x 844 [74,103]
+ CRUSH rule 3 x 845 [74,43]
+ CRUSH rule 3 x 846 [43,76]
+ CRUSH rule 3 x 847 [62,20]
+ CRUSH rule 3 x 848 [92,17]
+ CRUSH rule 3 x 849 [93,36]
+ CRUSH rule 3 x 850 [83,82]
+ CRUSH rule 3 x 851 [65,94]
+ CRUSH rule 3 x 852 [60,22]
+ CRUSH rule 3 x 853 [88,29]
+ CRUSH rule 3 x 854 [83,54]
+ CRUSH rule 3 x 855 [2,101]
+ CRUSH rule 3 x 856 [40,41]
+ CRUSH rule 3 x 857 [69,82]
+ CRUSH rule 3 x 858 [98,81]
+ CRUSH rule 3 x 859 [56,43]
+ CRUSH rule 3 x 860 [11,26]
+ CRUSH rule 3 x 861 [22,110]
+ CRUSH rule 3 x 862 [22,70]
+ CRUSH rule 3 x 863 [79,84]
+ CRUSH rule 3 x 864 [77,24]
+ CRUSH rule 3 x 865 [119,17]
+ CRUSH rule 3 x 866 [18,49]
+ CRUSH rule 3 x 867 [3,84]
+ CRUSH rule 3 x 868 [100,107]
+ CRUSH rule 3 x 869 [22,104]
+ CRUSH rule 3 x 870 [73,30]
+ CRUSH rule 3 x 871 [84,105]
+ CRUSH rule 3 x 872 [72,75]
+ CRUSH rule 3 x 873 [81,96]
+ CRUSH rule 3 x 874 [21,72]
+ CRUSH rule 3 x 875 [115,59]
+ CRUSH rule 3 x 876 [98,49]
+ CRUSH rule 3 x 877 [80,79]
+ CRUSH rule 3 x 878 [87,94]
+ CRUSH rule 3 x 879 [29,18]
+ CRUSH rule 3 x 880 [23,40]
+ CRUSH rule 3 x 881 [109,69]
+ CRUSH rule 3 x 882 [31,118]
+ CRUSH rule 3 x 883 [102,8]
+ CRUSH rule 3 x 884 [80,19]
+ CRUSH rule 3 x 885 [46,101]
+ CRUSH rule 3 x 886 [2,65]
+ CRUSH rule 3 x 887 [5,99]
+ CRUSH rule 3 x 888 [16,70]
+ CRUSH rule 3 x 889 [84,93]
+ CRUSH rule 3 x 890 [65,118]
+ CRUSH rule 3 x 891 [86,105]
+ CRUSH rule 3 x 892 [64,10]
+ CRUSH rule 3 x 893 [20,110]
+ CRUSH rule 3 x 894 [32,47]
+ CRUSH rule 3 x 895 [40,21]
+ CRUSH rule 3 x 896 [113,14]
+ CRUSH rule 3 x 897 [107,80]
+ CRUSH rule 3 x 898 [76,71]
+ CRUSH rule 3 x 899 [75,82]
+ CRUSH rule 3 x 900 [83,82]
+ CRUSH rule 3 x 901 [66,61]
+ CRUSH rule 3 x 902 [25,56]
+ CRUSH rule 3 x 903 [53,46]
+ CRUSH rule 3 x 904 [50,101]
+ CRUSH rule 3 x 905 [99,110]
+ CRUSH rule 3 x 906 [68,27]
+ CRUSH rule 3 x 907 [109,47]
+ CRUSH rule 3 x 908 [47,1]
+ CRUSH rule 3 x 909 [73,2]
+ CRUSH rule 3 x 910 [71,74]
+ CRUSH rule 3 x 911 [39,42]
+ CRUSH rule 3 x 912 [90,7]
+ CRUSH rule 3 x 913 [29,96]
+ CRUSH rule 3 x 914 [84,45]
+ CRUSH rule 3 x 915 [49,115]
+ CRUSH rule 3 x 916 [32,77]
+ CRUSH rule 3 x 917 [46,23]
+ CRUSH rule 3 x 918 [82,73]
+ CRUSH rule 3 x 919 [13,28]
+ CRUSH rule 3 x 920 [25,26]
+ CRUSH rule 3 x 921 [55,119]
+ CRUSH rule 3 x 922 [33,32]
+ CRUSH rule 3 x 923 [28,15]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,22]
+ CRUSH rule 3 x 926 [64,69]
+ CRUSH rule 3 x 927 [32,16]
+ CRUSH rule 3 x 928 [13,113]
+ CRUSH rule 3 x 929 [85,115]
+ CRUSH rule 3 x 930 [104,20]
+ CRUSH rule 3 x 931 [46,79]
+ CRUSH rule 3 x 932 [43,52]
+ CRUSH rule 3 x 933 [18,55]
+ CRUSH rule 3 x 934 [68,81]
+ CRUSH rule 3 x 935 [28,57]
+ CRUSH rule 3 x 936 [104,57]
+ CRUSH rule 3 x 937 [110,10]
+ CRUSH rule 3 x 938 [48,23]
+ CRUSH rule 3 x 939 [77,42]
+ CRUSH rule 3 x 940 [76,49]
+ CRUSH rule 3 x 941 [66,101]
+ CRUSH rule 3 x 942 [80,87]
+ CRUSH rule 3 x 943 [75,74]
+ CRUSH rule 3 x 944 [82,53]
+ CRUSH rule 3 x 945 [71,74]
+ CRUSH rule 3 x 946 [37,42]
+ CRUSH rule 3 x 947 [107,30]
+ CRUSH rule 3 x 948 [108,95]
+ CRUSH rule 3 x 949 [46,103]
+ CRUSH rule 3 x 950 [96,101]
+ CRUSH rule 3 x 951 [40,14]
+ CRUSH rule 3 x 952 [114,21]
+ CRUSH rule 3 x 953 [62,23]
+ CRUSH rule 3 x 954 [103,5]
+ CRUSH rule 3 x 955 [42,73]
+ CRUSH rule 3 x 956 [72,103]
+ CRUSH rule 3 x 957 [117,22]
+ CRUSH rule 3 x 958 [23,106]
+ CRUSH rule 3 x 959 [42,93]
+ CRUSH rule 3 x 960 [113,8]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,51]
+ CRUSH rule 3 x 963 [101,106]
+ CRUSH rule 3 x 964 [66,89]
+ CRUSH rule 3 x 965 [47,102]
+ CRUSH rule 3 x 966 [88,63]
+ CRUSH rule 3 x 967 [71,46]
+ CRUSH rule 3 x 968 [74,51]
+ CRUSH rule 3 x 969 [53,78]
+ CRUSH rule 3 x 970 [3,30]
+ CRUSH rule 3 x 971 [66,107]
+ CRUSH rule 3 x 972 [3,66]
+ CRUSH rule 3 x 973 [113,20]
+ CRUSH rule 3 x 974 [114,35]
+ CRUSH rule 3 x 975 [83,58]
+ CRUSH rule 3 x 976 [81,48]
+ CRUSH rule 3 x 977 [95,102]
+ CRUSH rule 3 x 978 [119,41]
+ CRUSH rule 3 x 979 [98,6]
+ CRUSH rule 3 x 980 [39,108]
+ CRUSH rule 3 x 981 [89,84]
+ CRUSH rule 3 x 982 [19,94]
+ CRUSH rule 3 x 983 [34,45]
+ CRUSH rule 3 x 984 [78,63]
+ CRUSH rule 3 x 985 [99,52]
+ CRUSH rule 3 x 986 [44,99]
+ CRUSH rule 3 x 987 [25,32]
+ CRUSH rule 3 x 988 [79,2]
+ CRUSH rule 3 x 989 [87,26]
+ CRUSH rule 3 x 990 [72,69]
+ CRUSH rule 3 x 991 [90,8]
+ CRUSH rule 3 x 992 [30,67]
+ CRUSH rule 3 x 993 [74,49]
+ CRUSH rule 3 x 994 [74,105]
+ CRUSH rule 3 x 995 [100,97]
+ CRUSH rule 3 x 996 [41,58]
+ CRUSH rule 3 x 997 [89,76]
+ CRUSH rule 3 x 998 [92,47]
+ CRUSH rule 3 x 999 [117,16]
+ CRUSH rule 3 x 1000 [50,47]
+ CRUSH rule 3 x 1001 [83,102]
+ CRUSH rule 3 x 1002 [94,37]
+ CRUSH rule 3 x 1003 [43,88]
+ CRUSH rule 3 x 1004 [89,54]
+ CRUSH rule 3 x 1005 [105,84]
+ CRUSH rule 3 x 1006 [45,111]
+ CRUSH rule 3 x 1007 [19,66]
+ CRUSH rule 3 x 1008 [31,76]
+ CRUSH rule 3 x 1009 [1,95]
+ CRUSH rule 3 x 1010 [31,113]
+ CRUSH rule 3 x 1011 [64,81]
+ CRUSH rule 3 x 1012 [68,49]
+ CRUSH rule 3 x 1013 [5,93]
+ CRUSH rule 3 x 1014 [33,66]
+ CRUSH rule 3 x 1015 [106,45]
+ CRUSH rule 3 x 1016 [107,86]
+ CRUSH rule 3 x 1017 [12,61]
+ CRUSH rule 3 x 1018 [61,26]
+ CRUSH rule 3 x 1019 [27,104]
+ CRUSH rule 3 x 1020 [31,86]
+ CRUSH rule 3 x 1021 [22,26]
+ CRUSH rule 3 x 1022 [73,34]
+ CRUSH rule 3 x 1023 [88,79]
+ rule 3 (delltestrule) num_rep 4 result size == 2:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-vary-r-2.t b/src/test/cli/crushtool/test-map-vary-r-2.t
new file mode 100644
index 000000000..c9d936590
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-vary-r-2.t
@@ -0,0 +1,3078 @@
+ $ crushtool -i "$TESTDIR/test-map-vary-r.crushmap" --test --show-mappings --show-statistics --rule 3 --set-chooseleaf-vary-r 2 --weight 0 0 --weight 4 0 --weight 9 0 --min-rep 2 --max-rep 4
+ rule 3 (delltestrule), x = 0..1023, numrep = 2..4
+ CRUSH rule 3 x 0 [94,45]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,118]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,96]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,70]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,76]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,100]
+ CRUSH rule 3 x 17 [85,110]
+ CRUSH rule 3 x 18 [11,119]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,47]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,90]
+ CRUSH rule 3 x 24 [83,38]
+ CRUSH rule 3 x 25 [81,5]
+ CRUSH rule 3 x 26 [17,100]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,119]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,69]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,52]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,29]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,11]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,68]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,21]
+ CRUSH rule 3 x 47 [106,53]
+ CRUSH rule 3 x 48 [34,33]
+ CRUSH rule 3 x 49 [99,104]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,52]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,75]
+ CRUSH rule 3 x 54 [28,93]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,40]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,33]
+ CRUSH rule 3 x 61 [88,3]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,113]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,61]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,7]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,44]
+ CRUSH rule 3 x 74 [29,74]
+ CRUSH rule 3 x 75 [60,89]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,7]
+ CRUSH rule 3 x 79 [64,41]
+ CRUSH rule 3 x 80 [73,104]
+ CRUSH rule 3 x 81 [64,19]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,68]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,95]
+ CRUSH rule 3 x 89 [76,3]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,65]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,23]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,100]
+ CRUSH rule 3 x 99 [78,22]
+ CRUSH rule 3 x 100 [28,63]
+ CRUSH rule 3 x 101 [91,36]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,95]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,116]
+ CRUSH rule 3 x 107 [1,55]
+ CRUSH rule 3 x 108 [7,72]
+ CRUSH rule 3 x 109 [112,63]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,78]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,54]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,42]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,16]
+ CRUSH rule 3 x 122 [45,110]
+ CRUSH rule 3 x 123 [22,86]
+ CRUSH rule 3 x 124 [97,72]
+ CRUSH rule 3 x 125 [66,71]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,97]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,110]
+ CRUSH rule 3 x 130 [50,63]
+ CRUSH rule 3 x 131 [44,25]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,2]
+ CRUSH rule 3 x 134 [37,94]
+ CRUSH rule 3 x 135 [78,17]
+ CRUSH rule 3 x 136 [32,91]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,62]
+ CRUSH rule 3 x 141 [89,96]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,87]
+ CRUSH rule 3 x 144 [13,86]
+ CRUSH rule 3 x 145 [77,60]
+ CRUSH rule 3 x 146 [12,53]
+ CRUSH rule 3 x 147 [2,59]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,58]
+ CRUSH rule 3 x 150 [14,54]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,33]
+ CRUSH rule 3 x 154 [19,111]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,5]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,58]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,31]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,47]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,107]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,34]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,2]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,1]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,90]
+ CRUSH rule 3 x 183 [11,74]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,113]
+ CRUSH rule 3 x 186 [67,96]
+ CRUSH rule 3 x 187 [6,36]
+ CRUSH rule 3 x 188 [76,20]
+ CRUSH rule 3 x 189 [96,89]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,60]
+ CRUSH rule 3 x 192 [93,64]
+ CRUSH rule 3 x 193 [89,112]
+ CRUSH rule 3 x 194 [62,63]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [97,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,29]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,98]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,35]
+ CRUSH rule 3 x 207 [19,36]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,76]
+ CRUSH rule 3 x 211 [26,89]
+ CRUSH rule 3 x 212 [115,107]
+ CRUSH rule 3 x 213 [100,8]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,89]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,2]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,12]
+ CRUSH rule 3 x 222 [50,63]
+ CRUSH rule 3 x 223 [34,59]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,102]
+ CRUSH rule 3 x 226 [44,105]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,78]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,44]
+ CRUSH rule 3 x 233 [47,113]
+ CRUSH rule 3 x 234 [55,18]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,53]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,38]
+ CRUSH rule 3 x 242 [73,52]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,71]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,81]
+ CRUSH rule 3 x 250 [34,41]
+ CRUSH rule 3 x 251 [28,15]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,77]
+ CRUSH rule 3 x 256 [94,45]
+ CRUSH rule 3 x 257 [100,81]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,80]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,53]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,18]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,86]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,70]
+ CRUSH rule 3 x 271 [19,88]
+ CRUSH rule 3 x 272 [73,52]
+ CRUSH rule 3 x 273 [69,92]
+ CRUSH rule 3 x 274 [47,88]
+ CRUSH rule 3 x 275 [112,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,87]
+ CRUSH rule 3 x 278 [107,44]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,10]
+ CRUSH rule 3 x 281 [89,109]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,118]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,101]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,26]
+ CRUSH rule 3 x 290 [25,12]
+ CRUSH rule 3 x 291 [35,46]
+ CRUSH rule 3 x 292 [20,28]
+ CRUSH rule 3 x 293 [27,24]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,116]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,63]
+ CRUSH rule 3 x 298 [70,87]
+ CRUSH rule 3 x 299 [116,61]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,94]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,53]
+ CRUSH rule 3 x 306 [41,12]
+ CRUSH rule 3 x 307 [65,64]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,69]
+ CRUSH rule 3 x 311 [36,83]
+ CRUSH rule 3 x 312 [114,97]
+ CRUSH rule 3 x 313 [104,31]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,22]
+ CRUSH rule 3 x 316 [98,8]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,48]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,30]
+ CRUSH rule 3 x 330 [24,55]
+ CRUSH rule 3 x 331 [84,91]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,35]
+ CRUSH rule 3 x 335 [71,66]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,74]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,52]
+ CRUSH rule 3 x 341 [46,81]
+ CRUSH rule 3 x 342 [92,6]
+ CRUSH rule 3 x 343 [49,26]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,87]
+ CRUSH rule 3 x 346 [3,70]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,103]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,91]
+ CRUSH rule 3 x 353 [10,118]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,5]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,89]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,46]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,75]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,115]
+ CRUSH rule 3 x 366 [42,33]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,23]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,76]
+ CRUSH rule 3 x 378 [68,10]
+ CRUSH rule 3 x 379 [77,30]
+ CRUSH rule 3 x 380 [76,25]
+ CRUSH rule 3 x 381 [36,55]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,25]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,15]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,118]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,2]
+ CRUSH rule 3 x 393 [91,112]
+ CRUSH rule 3 x 394 [38,13]
+ CRUSH rule 3 x 395 [21,117]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,103]
+ CRUSH rule 3 x 400 [19,100]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,110]
+ CRUSH rule 3 x 403 [23,92]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,52]
+ CRUSH rule 3 x 407 [70,85]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,1]
+ CRUSH rule 3 x 410 [70,107]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,68]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,66]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,17]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,21]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,73]
+ CRUSH rule 3 x 427 [8,115]
+ CRUSH rule 3 x 428 [68,31]
+ CRUSH rule 3 x 429 [76,6]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,1]
+ CRUSH rule 3 x 434 [64,31]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,25]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,85]
+ CRUSH rule 3 x 439 [40,21]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,57]
+ CRUSH rule 3 x 442 [55,108]
+ CRUSH rule 3 x 443 [44,14]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,19]
+ CRUSH rule 3 x 449 [67,66]
+ CRUSH rule 3 x 450 [117,97]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,41]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,92]
+ CRUSH rule 3 x 456 [101,104]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,43]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,64]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,60]
+ CRUSH rule 3 x 469 [98,71]
+ CRUSH rule 3 x 470 [50,27]
+ CRUSH rule 3 x 471 [40,31]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,75]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,90]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,36]
+ CRUSH rule 3 x 485 [84,43]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,51]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,86]
+ CRUSH rule 3 x 493 [94,21]
+ CRUSH rule 3 x 494 [59,98]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,12]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,37]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,28]
+ CRUSH rule 3 x 503 [21,96]
+ CRUSH rule 3 x 504 [67,1]
+ CRUSH rule 3 x 505 [12,10]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,67]
+ CRUSH rule 3 x 508 [45,56]
+ CRUSH rule 3 x 509 [19,70]
+ CRUSH rule 3 x 510 [117,73]
+ CRUSH rule 3 x 511 [14,117]
+ CRUSH rule 3 x 512 [59,26]
+ CRUSH rule 3 x 513 [102,93]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,39]
+ CRUSH rule 3 x 516 [37,12]
+ CRUSH rule 3 x 517 [83,68]
+ CRUSH rule 3 x 518 [18,107]
+ CRUSH rule 3 x 519 [67,119]
+ CRUSH rule 3 x 520 [15,88]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,3]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,113]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,56]
+ CRUSH rule 3 x 528 [108,35]
+ CRUSH rule 3 x 529 [74,15]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,104]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,37]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,56]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,94]
+ CRUSH rule 3 x 541 [53,86]
+ CRUSH rule 3 x 542 [27,114]
+ CRUSH rule 3 x 543 [45,78]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,49]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,82]
+ CRUSH rule 3 x 549 [60,71]
+ CRUSH rule 3 x 550 [92,71]
+ CRUSH rule 3 x 551 [77,88]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,20]
+ CRUSH rule 3 x 556 [106,89]
+ CRUSH rule 3 x 557 [26,45]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,105]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,25]
+ CRUSH rule 3 x 563 [59,72]
+ CRUSH rule 3 x 564 [96,59]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,35]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,38]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,11]
+ CRUSH rule 3 x 574 [89,78]
+ CRUSH rule 3 x 575 [87,50]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,113]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,116]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,66]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,116]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,63]
+ CRUSH rule 3 x 591 [42,77]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,31]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,94]
+ CRUSH rule 3 x 598 [37,60]
+ CRUSH rule 3 x 599 [10,76]
+ CRUSH rule 3 x 600 [24,7]
+ CRUSH rule 3 x 601 [104,87]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,60]
+ CRUSH rule 3 x 604 [118,71]
+ CRUSH rule 3 x 605 [104,87]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,40]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,7]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [81,54]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,27]
+ CRUSH rule 3 x 620 [108,103]
+ CRUSH rule 3 x 621 [105,110]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,64]
+ CRUSH rule 3 x 624 [115,29]
+ CRUSH rule 3 x 625 [73,98]
+ CRUSH rule 3 x 626 [52,55]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,35]
+ CRUSH rule 3 x 629 [6,46]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,80]
+ CRUSH rule 3 x 632 [80,95]
+ CRUSH rule 3 x 633 [65,68]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,72]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,68]
+ CRUSH rule 3 x 638 [43,109]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,96]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,7]
+ CRUSH rule 3 x 644 [31,5]
+ CRUSH rule 3 x 645 [77,1]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,56]
+ CRUSH rule 3 x 648 [56,79]
+ CRUSH rule 3 x 649 [88,103]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,14]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [7,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,2]
+ CRUSH rule 3 x 660 [65,110]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,78]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,101]
+ CRUSH rule 3 x 667 [70,11]
+ CRUSH rule 3 x 668 [96,63]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,99]
+ CRUSH rule 3 x 671 [57,2]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,62]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,17]
+ CRUSH rule 3 x 676 [3,100]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,26]
+ CRUSH rule 3 x 680 [111,43]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,95]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,41]
+ CRUSH rule 3 x 690 [96,103]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,72]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,70]
+ CRUSH rule 3 x 695 [31,62]
+ CRUSH rule 3 x 696 [42,97]
+ CRUSH rule 3 x 697 [19,86]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,106]
+ CRUSH rule 3 x 700 [35,82]
+ CRUSH rule 3 x 701 [53,30]
+ CRUSH rule 3 x 702 [101,32]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,8]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,42]
+ CRUSH rule 3 x 708 [95,84]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,23]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,26]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,21]
+ CRUSH rule 3 x 716 [101,72]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,96]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,58]
+ CRUSH rule 3 x 722 [15,80]
+ CRUSH rule 3 x 723 [117,41]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,116]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,42]
+ CRUSH rule 3 x 730 [28,47]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,89]
+ CRUSH rule 3 x 733 [35,62]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,73]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,90]
+ CRUSH rule 3 x 742 [106,31]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,89]
+ CRUSH rule 3 x 749 [102,71]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,74]
+ CRUSH rule 3 x 752 [82,83]
+ CRUSH rule 3 x 753 [14,32]
+ CRUSH rule 3 x 754 [114,57]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,83]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,29]
+ CRUSH rule 3 x 760 [88,105]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,41]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,116]
+ CRUSH rule 3 x 768 [106,71]
+ CRUSH rule 3 x 769 [91,48]
+ CRUSH rule 3 x 770 [72,43]
+ CRUSH rule 3 x 771 [115,97]
+ CRUSH rule 3 x 772 [97,111]
+ CRUSH rule 3 x 773 [116,75]
+ CRUSH rule 3 x 774 [100,43]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,118]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,40]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,58]
+ CRUSH rule 3 x 787 [108,16]
+ CRUSH rule 3 x 788 [74,6]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,97]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,90]
+ CRUSH rule 3 x 797 [64,63]
+ CRUSH rule 3 x 798 [42,91]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,49]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,5]
+ CRUSH rule 3 x 803 [57,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,63]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,59]
+ CRUSH rule 3 x 809 [27,26]
+ CRUSH rule 3 x 810 [119,107]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,52]
+ CRUSH rule 3 x 813 [81,72]
+ CRUSH rule 3 x 814 [95,32]
+ CRUSH rule 3 x 815 [84,15]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,15]
+ CRUSH rule 3 x 820 [104,49]
+ CRUSH rule 3 x 821 [58,85]
+ CRUSH rule 3 x 822 [20,98]
+ CRUSH rule 3 x 823 [63,90]
+ CRUSH rule 3 x 824 [102,81]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,3]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,3]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,46]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,17]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,109]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,57]
+ CRUSH rule 3 x 845 [74,63]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,117]
+ CRUSH rule 3 x 852 [60,27]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,84]
+ CRUSH rule 3 x 855 [2,105]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,79]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,54]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,68]
+ CRUSH rule 3 x 865 [119,14]
+ CRUSH rule 3 x 866 [18,89]
+ CRUSH rule 3 x 867 [3,78]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,21]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,112]
+ CRUSH rule 3 x 874 [21,44]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,75]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,96]
+ CRUSH rule 3 x 881 [109,69]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,51]
+ CRUSH rule 3 x 884 [80,103]
+ CRUSH rule 3 x 885 [46,7]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,40]
+ CRUSH rule 3 x 889 [84,93]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,105]
+ CRUSH rule 3 x 892 [64,87]
+ CRUSH rule 3 x 893 [20,115]
+ CRUSH rule 3 x 894 [32,3]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,93]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,80]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,104]
+ CRUSH rule 3 x 903 [53,64]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,5]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,86]
+ CRUSH rule 3 x 911 [39,84]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,12]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,54]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,86]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,23]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,33]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,80]
+ CRUSH rule 3 x 930 [104,61]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,42]
+ CRUSH rule 3 x 933 [18,63]
+ CRUSH rule 3 x 934 [68,51]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,11]
+ CRUSH rule 3 x 938 [48,73]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,22]
+ CRUSH rule 3 x 942 [80,8]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,7]
+ CRUSH rule 3 x 945 [71,56]
+ CRUSH rule 3 x 946 [37,114]
+ CRUSH rule 3 x 947 [107,74]
+ CRUSH rule 3 x 948 [108,79]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,87]
+ CRUSH rule 3 x 953 [62,105]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,91]
+ CRUSH rule 3 x 957 [117,16]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,107]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,92]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,108]
+ CRUSH rule 3 x 968 [74,6]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,10]
+ CRUSH rule 3 x 972 [3,58]
+ CRUSH rule 3 x 973 [113,81]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,117]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [119,93]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,26]
+ CRUSH rule 3 x 981 [89,117]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,21]
+ CRUSH rule 3 x 984 [78,15]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,103]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,86]
+ CRUSH rule 3 x 990 [72,35]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,53]
+ CRUSH rule 3 x 994 [74,20]
+ CRUSH rule 3 x 995 [100,17]
+ CRUSH rule 3 x 996 [41,30]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,65]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,36]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,118]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,113]
+ CRUSH rule 3 x 1008 [31,36]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,34]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,71]
+ CRUSH rule 3 x 1013 [5,39]
+ CRUSH rule 3 x 1014 [33,80]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,109]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,109]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,108]
+ CRUSH rule 3 x 1022 [73,106]
+ CRUSH rule 3 x 1023 [88,89]
+ rule 3 (delltestrule) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,45]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,118]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,96]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,70]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,76]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,100]
+ CRUSH rule 3 x 17 [85,110]
+ CRUSH rule 3 x 18 [11,119]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,47]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,90]
+ CRUSH rule 3 x 24 [83,38]
+ CRUSH rule 3 x 25 [81,5]
+ CRUSH rule 3 x 26 [17,100]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,119]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,69]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,52]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,29]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,11]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,68]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,21]
+ CRUSH rule 3 x 47 [106,53]
+ CRUSH rule 3 x 48 [34,33]
+ CRUSH rule 3 x 49 [99,104]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,52]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,75]
+ CRUSH rule 3 x 54 [28,93]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,40]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,33]
+ CRUSH rule 3 x 61 [88,3]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,113]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,61]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,7]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,44]
+ CRUSH rule 3 x 74 [29,74]
+ CRUSH rule 3 x 75 [60,89]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,7]
+ CRUSH rule 3 x 79 [64,41]
+ CRUSH rule 3 x 80 [73,104]
+ CRUSH rule 3 x 81 [64,19]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,68]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,95]
+ CRUSH rule 3 x 89 [76,3]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,65]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,23]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,100]
+ CRUSH rule 3 x 99 [78,22]
+ CRUSH rule 3 x 100 [28,63]
+ CRUSH rule 3 x 101 [91,36]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,95]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,116]
+ CRUSH rule 3 x 107 [1,55]
+ CRUSH rule 3 x 108 [7,72]
+ CRUSH rule 3 x 109 [112,63]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,78]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,54]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,42]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,16]
+ CRUSH rule 3 x 122 [45,110]
+ CRUSH rule 3 x 123 [22,86]
+ CRUSH rule 3 x 124 [97,72]
+ CRUSH rule 3 x 125 [66,71]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,97]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,110]
+ CRUSH rule 3 x 130 [50,63]
+ CRUSH rule 3 x 131 [44,25]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,2]
+ CRUSH rule 3 x 134 [37,94]
+ CRUSH rule 3 x 135 [78,17]
+ CRUSH rule 3 x 136 [32,91]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,62]
+ CRUSH rule 3 x 141 [89,96]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,87]
+ CRUSH rule 3 x 144 [13,86]
+ CRUSH rule 3 x 145 [77,60]
+ CRUSH rule 3 x 146 [12,53]
+ CRUSH rule 3 x 147 [2,59]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,58]
+ CRUSH rule 3 x 150 [14,54]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,33]
+ CRUSH rule 3 x 154 [19,111]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,5]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,58]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,31]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,47]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,107]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,34]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,2]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,1]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,90]
+ CRUSH rule 3 x 183 [11,74]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,113]
+ CRUSH rule 3 x 186 [67,96]
+ CRUSH rule 3 x 187 [6,36]
+ CRUSH rule 3 x 188 [76,20]
+ CRUSH rule 3 x 189 [96,89]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,60]
+ CRUSH rule 3 x 192 [93,64]
+ CRUSH rule 3 x 193 [89,112]
+ CRUSH rule 3 x 194 [62,63]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [97,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,29]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,98]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,35]
+ CRUSH rule 3 x 207 [19,36]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,76]
+ CRUSH rule 3 x 211 [26,89]
+ CRUSH rule 3 x 212 [115,107]
+ CRUSH rule 3 x 213 [100,8]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,89]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,2]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,12]
+ CRUSH rule 3 x 222 [50,63]
+ CRUSH rule 3 x 223 [34,59]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,102]
+ CRUSH rule 3 x 226 [44,105]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,78]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,44]
+ CRUSH rule 3 x 233 [47,113]
+ CRUSH rule 3 x 234 [55,18]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,53]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,38]
+ CRUSH rule 3 x 242 [73,52]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,71]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,81]
+ CRUSH rule 3 x 250 [34,41]
+ CRUSH rule 3 x 251 [28,15]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,77]
+ CRUSH rule 3 x 256 [94,45]
+ CRUSH rule 3 x 257 [100,81]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,80]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,53]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,18]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,86]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,70]
+ CRUSH rule 3 x 271 [19,88]
+ CRUSH rule 3 x 272 [73,52]
+ CRUSH rule 3 x 273 [69,92]
+ CRUSH rule 3 x 274 [47,88]
+ CRUSH rule 3 x 275 [112,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,87]
+ CRUSH rule 3 x 278 [107,44]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,10]
+ CRUSH rule 3 x 281 [89,109]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,118]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,101]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,26]
+ CRUSH rule 3 x 290 [25,12]
+ CRUSH rule 3 x 291 [35,46]
+ CRUSH rule 3 x 292 [20,28]
+ CRUSH rule 3 x 293 [27,24]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,116]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,63]
+ CRUSH rule 3 x 298 [70,87]
+ CRUSH rule 3 x 299 [116,61]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,94]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,53]
+ CRUSH rule 3 x 306 [41,12]
+ CRUSH rule 3 x 307 [65,64]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,69]
+ CRUSH rule 3 x 311 [36,83]
+ CRUSH rule 3 x 312 [114,97]
+ CRUSH rule 3 x 313 [104,31]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,22]
+ CRUSH rule 3 x 316 [98,8]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,48]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,30]
+ CRUSH rule 3 x 330 [24,55]
+ CRUSH rule 3 x 331 [84,91]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,35]
+ CRUSH rule 3 x 335 [71,66]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,74]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,52]
+ CRUSH rule 3 x 341 [46,81]
+ CRUSH rule 3 x 342 [92,6]
+ CRUSH rule 3 x 343 [49,26]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,87]
+ CRUSH rule 3 x 346 [3,70]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,103]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,91]
+ CRUSH rule 3 x 353 [10,118]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,5]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,89]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,46]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,75]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,115]
+ CRUSH rule 3 x 366 [42,33]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,23]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,76]
+ CRUSH rule 3 x 378 [68,10]
+ CRUSH rule 3 x 379 [77,30]
+ CRUSH rule 3 x 380 [76,25]
+ CRUSH rule 3 x 381 [36,55]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,25]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,15]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,118]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,2]
+ CRUSH rule 3 x 393 [91,112]
+ CRUSH rule 3 x 394 [38,13]
+ CRUSH rule 3 x 395 [21,117]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,103]
+ CRUSH rule 3 x 400 [19,100]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,110]
+ CRUSH rule 3 x 403 [23,92]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,52]
+ CRUSH rule 3 x 407 [70,85]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,1]
+ CRUSH rule 3 x 410 [70,107]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,68]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,66]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,17]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,21]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,73]
+ CRUSH rule 3 x 427 [8,115]
+ CRUSH rule 3 x 428 [68,31]
+ CRUSH rule 3 x 429 [76,6]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,1]
+ CRUSH rule 3 x 434 [64,31]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,25]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,85]
+ CRUSH rule 3 x 439 [40,21]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,57]
+ CRUSH rule 3 x 442 [55,108]
+ CRUSH rule 3 x 443 [44,14]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,19]
+ CRUSH rule 3 x 449 [67,66]
+ CRUSH rule 3 x 450 [117,97]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,41]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,92]
+ CRUSH rule 3 x 456 [101,104]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,43]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,64]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,60]
+ CRUSH rule 3 x 469 [98,71]
+ CRUSH rule 3 x 470 [50,27]
+ CRUSH rule 3 x 471 [40,31]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,75]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,90]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,36]
+ CRUSH rule 3 x 485 [84,43]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,51]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,86]
+ CRUSH rule 3 x 493 [94,21]
+ CRUSH rule 3 x 494 [59,98]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,12]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,37]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,28]
+ CRUSH rule 3 x 503 [21,96]
+ CRUSH rule 3 x 504 [67,1]
+ CRUSH rule 3 x 505 [12,10]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,67]
+ CRUSH rule 3 x 508 [45,56]
+ CRUSH rule 3 x 509 [19,70]
+ CRUSH rule 3 x 510 [117,73]
+ CRUSH rule 3 x 511 [14,117]
+ CRUSH rule 3 x 512 [59,26]
+ CRUSH rule 3 x 513 [102,93]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,39]
+ CRUSH rule 3 x 516 [37,12]
+ CRUSH rule 3 x 517 [83,68]
+ CRUSH rule 3 x 518 [18,107]
+ CRUSH rule 3 x 519 [67,119]
+ CRUSH rule 3 x 520 [15,88]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,3]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,113]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,56]
+ CRUSH rule 3 x 528 [108,35]
+ CRUSH rule 3 x 529 [74,15]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,104]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,37]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,56]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,94]
+ CRUSH rule 3 x 541 [53,86]
+ CRUSH rule 3 x 542 [27,114]
+ CRUSH rule 3 x 543 [45,78]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,49]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,82]
+ CRUSH rule 3 x 549 [60,71]
+ CRUSH rule 3 x 550 [92,71]
+ CRUSH rule 3 x 551 [77,88]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,20]
+ CRUSH rule 3 x 556 [106,89]
+ CRUSH rule 3 x 557 [26,45]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,105]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,25]
+ CRUSH rule 3 x 563 [59,72]
+ CRUSH rule 3 x 564 [96,59]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,35]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,38]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,11]
+ CRUSH rule 3 x 574 [89,78]
+ CRUSH rule 3 x 575 [87,50]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,113]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,116]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,66]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,116]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,63]
+ CRUSH rule 3 x 591 [42,77]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,31]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,94]
+ CRUSH rule 3 x 598 [37,60]
+ CRUSH rule 3 x 599 [10,76]
+ CRUSH rule 3 x 600 [24,7]
+ CRUSH rule 3 x 601 [104,87]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,60]
+ CRUSH rule 3 x 604 [118,71]
+ CRUSH rule 3 x 605 [104,87]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,40]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,7]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [81,54]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,27]
+ CRUSH rule 3 x 620 [108,103]
+ CRUSH rule 3 x 621 [105,110]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,64]
+ CRUSH rule 3 x 624 [115,29]
+ CRUSH rule 3 x 625 [73,98]
+ CRUSH rule 3 x 626 [52,55]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,35]
+ CRUSH rule 3 x 629 [6,46]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,80]
+ CRUSH rule 3 x 632 [80,95]
+ CRUSH rule 3 x 633 [65,68]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,72]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,68]
+ CRUSH rule 3 x 638 [43,109]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,96]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,7]
+ CRUSH rule 3 x 644 [31,5]
+ CRUSH rule 3 x 645 [77,1]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,56]
+ CRUSH rule 3 x 648 [56,79]
+ CRUSH rule 3 x 649 [88,103]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,14]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [7,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,2]
+ CRUSH rule 3 x 660 [65,110]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,78]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,101]
+ CRUSH rule 3 x 667 [70,11]
+ CRUSH rule 3 x 668 [96,63]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,99]
+ CRUSH rule 3 x 671 [57,2]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,62]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,17]
+ CRUSH rule 3 x 676 [3,100]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,26]
+ CRUSH rule 3 x 680 [111,43]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,95]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,41]
+ CRUSH rule 3 x 690 [96,103]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,72]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,70]
+ CRUSH rule 3 x 695 [31,62]
+ CRUSH rule 3 x 696 [42,97]
+ CRUSH rule 3 x 697 [19,86]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,106]
+ CRUSH rule 3 x 700 [35,82]
+ CRUSH rule 3 x 701 [53,30]
+ CRUSH rule 3 x 702 [101,32]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,8]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,42]
+ CRUSH rule 3 x 708 [95,84]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,23]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,26]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,21]
+ CRUSH rule 3 x 716 [101,72]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,96]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,58]
+ CRUSH rule 3 x 722 [15,80]
+ CRUSH rule 3 x 723 [117,41]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,116]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,42]
+ CRUSH rule 3 x 730 [28,47]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,89]
+ CRUSH rule 3 x 733 [35,62]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,73]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,90]
+ CRUSH rule 3 x 742 [106,31]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,89]
+ CRUSH rule 3 x 749 [102,71]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,74]
+ CRUSH rule 3 x 752 [82,83]
+ CRUSH rule 3 x 753 [14,32]
+ CRUSH rule 3 x 754 [114,57]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,83]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,29]
+ CRUSH rule 3 x 760 [88,105]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,41]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,116]
+ CRUSH rule 3 x 768 [106,71]
+ CRUSH rule 3 x 769 [91,48]
+ CRUSH rule 3 x 770 [72,43]
+ CRUSH rule 3 x 771 [115,97]
+ CRUSH rule 3 x 772 [97,111]
+ CRUSH rule 3 x 773 [116,75]
+ CRUSH rule 3 x 774 [100,43]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,118]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,40]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,58]
+ CRUSH rule 3 x 787 [108,16]
+ CRUSH rule 3 x 788 [74,6]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,97]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,90]
+ CRUSH rule 3 x 797 [64,63]
+ CRUSH rule 3 x 798 [42,91]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,49]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,5]
+ CRUSH rule 3 x 803 [57,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,63]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,59]
+ CRUSH rule 3 x 809 [27,26]
+ CRUSH rule 3 x 810 [119,107]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,52]
+ CRUSH rule 3 x 813 [81,72]
+ CRUSH rule 3 x 814 [95,32]
+ CRUSH rule 3 x 815 [84,15]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,15]
+ CRUSH rule 3 x 820 [104,49]
+ CRUSH rule 3 x 821 [58,85]
+ CRUSH rule 3 x 822 [20,98]
+ CRUSH rule 3 x 823 [63,90]
+ CRUSH rule 3 x 824 [102,81]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,3]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,3]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,46]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,17]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,109]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,57]
+ CRUSH rule 3 x 845 [74,63]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,117]
+ CRUSH rule 3 x 852 [60,27]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,84]
+ CRUSH rule 3 x 855 [2,105]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,79]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,54]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,68]
+ CRUSH rule 3 x 865 [119,14]
+ CRUSH rule 3 x 866 [18,89]
+ CRUSH rule 3 x 867 [3,78]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,21]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,112]
+ CRUSH rule 3 x 874 [21,44]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,75]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,96]
+ CRUSH rule 3 x 881 [109,69]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,51]
+ CRUSH rule 3 x 884 [80,103]
+ CRUSH rule 3 x 885 [46,7]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,40]
+ CRUSH rule 3 x 889 [84,93]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,105]
+ CRUSH rule 3 x 892 [64,87]
+ CRUSH rule 3 x 893 [20,115]
+ CRUSH rule 3 x 894 [32,3]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,93]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,80]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,104]
+ CRUSH rule 3 x 903 [53,64]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,5]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,86]
+ CRUSH rule 3 x 911 [39,84]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,12]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,54]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,86]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,23]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,33]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,80]
+ CRUSH rule 3 x 930 [104,61]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,42]
+ CRUSH rule 3 x 933 [18,63]
+ CRUSH rule 3 x 934 [68,51]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,11]
+ CRUSH rule 3 x 938 [48,73]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,22]
+ CRUSH rule 3 x 942 [80,8]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,7]
+ CRUSH rule 3 x 945 [71,56]
+ CRUSH rule 3 x 946 [37,114]
+ CRUSH rule 3 x 947 [107,74]
+ CRUSH rule 3 x 948 [108,79]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,87]
+ CRUSH rule 3 x 953 [62,105]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,91]
+ CRUSH rule 3 x 957 [117,16]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,107]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,92]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,108]
+ CRUSH rule 3 x 968 [74,6]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,10]
+ CRUSH rule 3 x 972 [3,58]
+ CRUSH rule 3 x 973 [113,81]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,117]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [119,93]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,26]
+ CRUSH rule 3 x 981 [89,117]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,21]
+ CRUSH rule 3 x 984 [78,15]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,103]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,86]
+ CRUSH rule 3 x 990 [72,35]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,53]
+ CRUSH rule 3 x 994 [74,20]
+ CRUSH rule 3 x 995 [100,17]
+ CRUSH rule 3 x 996 [41,30]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,65]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,36]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,118]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,113]
+ CRUSH rule 3 x 1008 [31,36]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,34]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,71]
+ CRUSH rule 3 x 1013 [5,39]
+ CRUSH rule 3 x 1014 [33,80]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,109]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,109]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,108]
+ CRUSH rule 3 x 1022 [73,106]
+ CRUSH rule 3 x 1023 [88,89]
+ rule 3 (delltestrule) num_rep 3 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,45]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,118]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,96]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,70]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,76]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,100]
+ CRUSH rule 3 x 17 [85,110]
+ CRUSH rule 3 x 18 [11,119]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,47]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,90]
+ CRUSH rule 3 x 24 [83,38]
+ CRUSH rule 3 x 25 [81,5]
+ CRUSH rule 3 x 26 [17,100]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,119]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,69]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,52]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,29]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,11]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,68]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,21]
+ CRUSH rule 3 x 47 [106,53]
+ CRUSH rule 3 x 48 [34,33]
+ CRUSH rule 3 x 49 [99,104]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,52]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,75]
+ CRUSH rule 3 x 54 [28,93]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,40]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,33]
+ CRUSH rule 3 x 61 [88,3]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,113]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,61]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,7]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,44]
+ CRUSH rule 3 x 74 [29,74]
+ CRUSH rule 3 x 75 [60,89]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,7]
+ CRUSH rule 3 x 79 [64,41]
+ CRUSH rule 3 x 80 [73,104]
+ CRUSH rule 3 x 81 [64,19]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,68]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,95]
+ CRUSH rule 3 x 89 [76,3]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,65]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,23]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,100]
+ CRUSH rule 3 x 99 [78,22]
+ CRUSH rule 3 x 100 [28,63]
+ CRUSH rule 3 x 101 [91,36]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,95]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,116]
+ CRUSH rule 3 x 107 [1,55]
+ CRUSH rule 3 x 108 [7,72]
+ CRUSH rule 3 x 109 [112,63]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,78]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,54]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,42]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,16]
+ CRUSH rule 3 x 122 [45,110]
+ CRUSH rule 3 x 123 [22,86]
+ CRUSH rule 3 x 124 [97,72]
+ CRUSH rule 3 x 125 [66,71]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,97]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,110]
+ CRUSH rule 3 x 130 [50,63]
+ CRUSH rule 3 x 131 [44,25]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,2]
+ CRUSH rule 3 x 134 [37,94]
+ CRUSH rule 3 x 135 [78,17]
+ CRUSH rule 3 x 136 [32,91]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,62]
+ CRUSH rule 3 x 141 [89,96]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,87]
+ CRUSH rule 3 x 144 [13,86]
+ CRUSH rule 3 x 145 [77,60]
+ CRUSH rule 3 x 146 [12,53]
+ CRUSH rule 3 x 147 [2,59]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,58]
+ CRUSH rule 3 x 150 [14,54]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,33]
+ CRUSH rule 3 x 154 [19,111]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,5]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,58]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,31]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,47]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,107]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,34]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,2]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,1]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,90]
+ CRUSH rule 3 x 183 [11,74]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,113]
+ CRUSH rule 3 x 186 [67,96]
+ CRUSH rule 3 x 187 [6,36]
+ CRUSH rule 3 x 188 [76,20]
+ CRUSH rule 3 x 189 [96,89]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,60]
+ CRUSH rule 3 x 192 [93,64]
+ CRUSH rule 3 x 193 [89,112]
+ CRUSH rule 3 x 194 [62,63]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [97,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,29]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,98]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,35]
+ CRUSH rule 3 x 207 [19,36]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,76]
+ CRUSH rule 3 x 211 [26,89]
+ CRUSH rule 3 x 212 [115,107]
+ CRUSH rule 3 x 213 [100,8]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,89]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,2]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,12]
+ CRUSH rule 3 x 222 [50,63]
+ CRUSH rule 3 x 223 [34,59]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,102]
+ CRUSH rule 3 x 226 [44,105]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,78]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,44]
+ CRUSH rule 3 x 233 [47,113]
+ CRUSH rule 3 x 234 [55,18]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,53]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,38]
+ CRUSH rule 3 x 242 [73,52]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,71]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,81]
+ CRUSH rule 3 x 250 [34,41]
+ CRUSH rule 3 x 251 [28,15]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,77]
+ CRUSH rule 3 x 256 [94,45]
+ CRUSH rule 3 x 257 [100,81]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,80]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,53]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,18]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,86]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,70]
+ CRUSH rule 3 x 271 [19,88]
+ CRUSH rule 3 x 272 [73,52]
+ CRUSH rule 3 x 273 [69,92]
+ CRUSH rule 3 x 274 [47,88]
+ CRUSH rule 3 x 275 [112,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,87]
+ CRUSH rule 3 x 278 [107,44]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,10]
+ CRUSH rule 3 x 281 [89,109]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,118]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,101]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,26]
+ CRUSH rule 3 x 290 [25,12]
+ CRUSH rule 3 x 291 [35,46]
+ CRUSH rule 3 x 292 [20,28]
+ CRUSH rule 3 x 293 [27,24]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,116]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,63]
+ CRUSH rule 3 x 298 [70,87]
+ CRUSH rule 3 x 299 [116,61]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,94]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,53]
+ CRUSH rule 3 x 306 [41,12]
+ CRUSH rule 3 x 307 [65,64]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,69]
+ CRUSH rule 3 x 311 [36,83]
+ CRUSH rule 3 x 312 [114,97]
+ CRUSH rule 3 x 313 [104,31]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,22]
+ CRUSH rule 3 x 316 [98,8]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,48]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,30]
+ CRUSH rule 3 x 330 [24,55]
+ CRUSH rule 3 x 331 [84,91]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,35]
+ CRUSH rule 3 x 335 [71,66]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,74]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,52]
+ CRUSH rule 3 x 341 [46,81]
+ CRUSH rule 3 x 342 [92,6]
+ CRUSH rule 3 x 343 [49,26]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,87]
+ CRUSH rule 3 x 346 [3,70]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,103]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,91]
+ CRUSH rule 3 x 353 [10,118]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,5]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,89]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,46]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,75]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,115]
+ CRUSH rule 3 x 366 [42,33]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,23]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,76]
+ CRUSH rule 3 x 378 [68,10]
+ CRUSH rule 3 x 379 [77,30]
+ CRUSH rule 3 x 380 [76,25]
+ CRUSH rule 3 x 381 [36,55]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,25]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,15]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,118]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,2]
+ CRUSH rule 3 x 393 [91,112]
+ CRUSH rule 3 x 394 [38,13]
+ CRUSH rule 3 x 395 [21,117]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,103]
+ CRUSH rule 3 x 400 [19,100]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,110]
+ CRUSH rule 3 x 403 [23,92]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,52]
+ CRUSH rule 3 x 407 [70,85]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,1]
+ CRUSH rule 3 x 410 [70,107]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,68]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,66]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,17]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,21]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,73]
+ CRUSH rule 3 x 427 [8,115]
+ CRUSH rule 3 x 428 [68,31]
+ CRUSH rule 3 x 429 [76,6]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,1]
+ CRUSH rule 3 x 434 [64,31]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,25]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,85]
+ CRUSH rule 3 x 439 [40,21]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,57]
+ CRUSH rule 3 x 442 [55,108]
+ CRUSH rule 3 x 443 [44,14]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,19]
+ CRUSH rule 3 x 449 [67,66]
+ CRUSH rule 3 x 450 [117,97]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,41]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,92]
+ CRUSH rule 3 x 456 [101,104]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,43]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,64]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,60]
+ CRUSH rule 3 x 469 [98,71]
+ CRUSH rule 3 x 470 [50,27]
+ CRUSH rule 3 x 471 [40,31]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,75]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,90]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,36]
+ CRUSH rule 3 x 485 [84,43]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,51]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,86]
+ CRUSH rule 3 x 493 [94,21]
+ CRUSH rule 3 x 494 [59,98]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,12]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,37]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,28]
+ CRUSH rule 3 x 503 [21,96]
+ CRUSH rule 3 x 504 [67,1]
+ CRUSH rule 3 x 505 [12,10]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,67]
+ CRUSH rule 3 x 508 [45,56]
+ CRUSH rule 3 x 509 [19,70]
+ CRUSH rule 3 x 510 [117,73]
+ CRUSH rule 3 x 511 [14,117]
+ CRUSH rule 3 x 512 [59,26]
+ CRUSH rule 3 x 513 [102,93]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,39]
+ CRUSH rule 3 x 516 [37,12]
+ CRUSH rule 3 x 517 [83,68]
+ CRUSH rule 3 x 518 [18,107]
+ CRUSH rule 3 x 519 [67,119]
+ CRUSH rule 3 x 520 [15,88]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,3]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,113]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,56]
+ CRUSH rule 3 x 528 [108,35]
+ CRUSH rule 3 x 529 [74,15]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,104]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,37]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,56]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,94]
+ CRUSH rule 3 x 541 [53,86]
+ CRUSH rule 3 x 542 [27,114]
+ CRUSH rule 3 x 543 [45,78]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,49]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,82]
+ CRUSH rule 3 x 549 [60,71]
+ CRUSH rule 3 x 550 [92,71]
+ CRUSH rule 3 x 551 [77,88]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,20]
+ CRUSH rule 3 x 556 [106,89]
+ CRUSH rule 3 x 557 [26,45]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,105]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,25]
+ CRUSH rule 3 x 563 [59,72]
+ CRUSH rule 3 x 564 [96,59]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,35]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,38]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,11]
+ CRUSH rule 3 x 574 [89,78]
+ CRUSH rule 3 x 575 [87,50]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,113]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,116]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,66]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,116]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,63]
+ CRUSH rule 3 x 591 [42,77]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,31]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,94]
+ CRUSH rule 3 x 598 [37,60]
+ CRUSH rule 3 x 599 [10,76]
+ CRUSH rule 3 x 600 [24,7]
+ CRUSH rule 3 x 601 [104,87]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,60]
+ CRUSH rule 3 x 604 [118,71]
+ CRUSH rule 3 x 605 [104,87]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,40]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,7]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [81,54]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,27]
+ CRUSH rule 3 x 620 [108,103]
+ CRUSH rule 3 x 621 [105,110]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,64]
+ CRUSH rule 3 x 624 [115,29]
+ CRUSH rule 3 x 625 [73,98]
+ CRUSH rule 3 x 626 [52,55]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,35]
+ CRUSH rule 3 x 629 [6,46]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,80]
+ CRUSH rule 3 x 632 [80,95]
+ CRUSH rule 3 x 633 [65,68]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,72]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,68]
+ CRUSH rule 3 x 638 [43,109]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,96]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,7]
+ CRUSH rule 3 x 644 [31,5]
+ CRUSH rule 3 x 645 [77,1]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,56]
+ CRUSH rule 3 x 648 [56,79]
+ CRUSH rule 3 x 649 [88,103]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,14]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [7,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,2]
+ CRUSH rule 3 x 660 [65,110]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,78]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,101]
+ CRUSH rule 3 x 667 [70,11]
+ CRUSH rule 3 x 668 [96,63]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,99]
+ CRUSH rule 3 x 671 [57,2]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,62]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,17]
+ CRUSH rule 3 x 676 [3,100]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,26]
+ CRUSH rule 3 x 680 [111,43]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,95]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,41]
+ CRUSH rule 3 x 690 [96,103]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,72]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,70]
+ CRUSH rule 3 x 695 [31,62]
+ CRUSH rule 3 x 696 [42,97]
+ CRUSH rule 3 x 697 [19,86]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,106]
+ CRUSH rule 3 x 700 [35,82]
+ CRUSH rule 3 x 701 [53,30]
+ CRUSH rule 3 x 702 [101,32]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,8]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,42]
+ CRUSH rule 3 x 708 [95,84]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,23]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,26]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,21]
+ CRUSH rule 3 x 716 [101,72]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,96]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,58]
+ CRUSH rule 3 x 722 [15,80]
+ CRUSH rule 3 x 723 [117,41]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,116]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,42]
+ CRUSH rule 3 x 730 [28,47]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,89]
+ CRUSH rule 3 x 733 [35,62]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,73]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,90]
+ CRUSH rule 3 x 742 [106,31]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,89]
+ CRUSH rule 3 x 749 [102,71]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,74]
+ CRUSH rule 3 x 752 [82,83]
+ CRUSH rule 3 x 753 [14,32]
+ CRUSH rule 3 x 754 [114,57]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,83]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,29]
+ CRUSH rule 3 x 760 [88,105]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,41]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,116]
+ CRUSH rule 3 x 768 [106,71]
+ CRUSH rule 3 x 769 [91,48]
+ CRUSH rule 3 x 770 [72,43]
+ CRUSH rule 3 x 771 [115,97]
+ CRUSH rule 3 x 772 [97,111]
+ CRUSH rule 3 x 773 [116,75]
+ CRUSH rule 3 x 774 [100,43]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,118]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,40]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,58]
+ CRUSH rule 3 x 787 [108,16]
+ CRUSH rule 3 x 788 [74,6]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,97]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,90]
+ CRUSH rule 3 x 797 [64,63]
+ CRUSH rule 3 x 798 [42,91]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,49]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,5]
+ CRUSH rule 3 x 803 [57,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,63]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,59]
+ CRUSH rule 3 x 809 [27,26]
+ CRUSH rule 3 x 810 [119,107]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,52]
+ CRUSH rule 3 x 813 [81,72]
+ CRUSH rule 3 x 814 [95,32]
+ CRUSH rule 3 x 815 [84,15]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,15]
+ CRUSH rule 3 x 820 [104,49]
+ CRUSH rule 3 x 821 [58,85]
+ CRUSH rule 3 x 822 [20,98]
+ CRUSH rule 3 x 823 [63,90]
+ CRUSH rule 3 x 824 [102,81]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,3]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,3]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,46]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,17]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,109]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,57]
+ CRUSH rule 3 x 845 [74,63]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,117]
+ CRUSH rule 3 x 852 [60,27]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,84]
+ CRUSH rule 3 x 855 [2,105]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,79]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,54]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,68]
+ CRUSH rule 3 x 865 [119,14]
+ CRUSH rule 3 x 866 [18,89]
+ CRUSH rule 3 x 867 [3,78]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,21]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,112]
+ CRUSH rule 3 x 874 [21,44]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,75]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,96]
+ CRUSH rule 3 x 881 [109,69]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,51]
+ CRUSH rule 3 x 884 [80,103]
+ CRUSH rule 3 x 885 [46,7]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,40]
+ CRUSH rule 3 x 889 [84,93]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,105]
+ CRUSH rule 3 x 892 [64,87]
+ CRUSH rule 3 x 893 [20,115]
+ CRUSH rule 3 x 894 [32,3]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,93]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,80]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,104]
+ CRUSH rule 3 x 903 [53,64]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,5]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,86]
+ CRUSH rule 3 x 911 [39,84]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,12]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,54]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,86]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,23]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,33]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,80]
+ CRUSH rule 3 x 930 [104,61]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,42]
+ CRUSH rule 3 x 933 [18,63]
+ CRUSH rule 3 x 934 [68,51]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,11]
+ CRUSH rule 3 x 938 [48,73]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,22]
+ CRUSH rule 3 x 942 [80,8]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,7]
+ CRUSH rule 3 x 945 [71,56]
+ CRUSH rule 3 x 946 [37,114]
+ CRUSH rule 3 x 947 [107,74]
+ CRUSH rule 3 x 948 [108,79]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,87]
+ CRUSH rule 3 x 953 [62,105]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,91]
+ CRUSH rule 3 x 957 [117,16]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,107]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,92]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,108]
+ CRUSH rule 3 x 968 [74,6]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,10]
+ CRUSH rule 3 x 972 [3,58]
+ CRUSH rule 3 x 973 [113,81]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,117]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [119,93]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,26]
+ CRUSH rule 3 x 981 [89,117]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,21]
+ CRUSH rule 3 x 984 [78,15]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,103]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,86]
+ CRUSH rule 3 x 990 [72,35]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,53]
+ CRUSH rule 3 x 994 [74,20]
+ CRUSH rule 3 x 995 [100,17]
+ CRUSH rule 3 x 996 [41,30]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,65]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,36]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,118]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,113]
+ CRUSH rule 3 x 1008 [31,36]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,34]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,71]
+ CRUSH rule 3 x 1013 [5,39]
+ CRUSH rule 3 x 1014 [33,80]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,109]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,109]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,108]
+ CRUSH rule 3 x 1022 [73,106]
+ CRUSH rule 3 x 1023 [88,89]
+ rule 3 (delltestrule) num_rep 4 result size == 2:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-vary-r-3.t b/src/test/cli/crushtool/test-map-vary-r-3.t
new file mode 100644
index 000000000..554e0bc65
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-vary-r-3.t
@@ -0,0 +1,3078 @@
+ $ crushtool -i "$TESTDIR/test-map-vary-r.crushmap" --test --show-mappings --show-statistics --rule 3 --set-chooseleaf-vary-r 3 --weight 0 0 --weight 4 0 --weight 9 0 --min-rep 2 --max-rep 4
+ rule 3 (delltestrule), x = 0..1023, numrep = 2..4
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,62]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,60]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,48]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,111]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,20]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,85]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,12]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,93]
+ CRUSH rule 3 x 54 [28,3]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,91]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,92]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,16]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,89]
+ CRUSH rule 3 x 79 [64,75]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,57]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,117]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,2]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,55]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,29]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,119]
+ CRUSH rule 3 x 146 [12,73]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,8]
+ CRUSH rule 3 x 154 [19,5]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,10]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,47]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,86]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,17]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,38]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,118]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,32]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,54]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,104]
+ CRUSH rule 3 x 234 [55,94]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,105]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,41]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,22]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,66]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,74]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,69]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,112]
+ CRUSH rule 3 x 291 [35,52]
+ CRUSH rule 3 x 292 [20,60]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,66]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,55]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,80]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,16]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,16]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,92]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,88]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,40]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,113]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,101]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,58]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,94]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,114]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,20]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,92]
+ CRUSH rule 3 x 410 [70,11]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,21]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,15]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,22]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,34]
+ CRUSH rule 3 x 456 [101,119]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,2]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,109]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,13]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,113]
+ CRUSH rule 3 x 493 [94,105]
+ CRUSH rule 3 x 494 [66,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,78]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,81]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,54]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,17]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59,111]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,60]
+ CRUSH rule 3 x 518 [18,13]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,119]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,66]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,21]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,18]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,29]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,108]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,62]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,40]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,64]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,30]
+ CRUSH rule 3 x 638 [43,113]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,97]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,45]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,87]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,82]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,86]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,50]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3,40]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,84]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36,97]
+ CRUSH rule 3 x 697 [19,60]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,62]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,94]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,30]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,56]
+ CRUSH rule 3 x 723 [117,25]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,48]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,44]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,119]
+ CRUSH rule 3 x 752 [82,47]
+ CRUSH rule 3 x 753 [24,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,27]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,80]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,37]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,75]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,29]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,30]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,91]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,88]
+ CRUSH rule 3 x 824 [102,81]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,11]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,35]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,85]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,77]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,16]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,98]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,58]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,12]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,41]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,24]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,21]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,37]
+ CRUSH rule 3 x 942 [80,8]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,52]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,55]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,78]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,37]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,22]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,38]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,98]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,59]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,31]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,10]
+ CRUSH rule 3 x 1014 [33,98]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,44]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [83,88]
+ rule 3 (delltestrule) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,62]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,60]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,48]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,111]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,20]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,85]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,12]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,93]
+ CRUSH rule 3 x 54 [28,3]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,91]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,92]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,16]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,89]
+ CRUSH rule 3 x 79 [64,75]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,57]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,117]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,2]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,55]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,29]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,119]
+ CRUSH rule 3 x 146 [12,73]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,8]
+ CRUSH rule 3 x 154 [19,5]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,10]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,47]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,86]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,17]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,38]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,118]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,32]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,54]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,104]
+ CRUSH rule 3 x 234 [55,94]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,105]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,41]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,22]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,66]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,74]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,69]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,112]
+ CRUSH rule 3 x 291 [35,52]
+ CRUSH rule 3 x 292 [20,60]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,66]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,55]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,80]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,16]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,16]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,92]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,88]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,40]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,113]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,101]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,58]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,94]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,114]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,20]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,92]
+ CRUSH rule 3 x 410 [70,11]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,21]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,15]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,22]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,34]
+ CRUSH rule 3 x 456 [101,119]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,2]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,109]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,13]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,113]
+ CRUSH rule 3 x 493 [94,105]
+ CRUSH rule 3 x 494 [66,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,78]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,81]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,54]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,17]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59,111]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,60]
+ CRUSH rule 3 x 518 [18,13]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,119]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,66]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,21]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,18]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,29]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,108]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,62]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,40]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,64]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,30]
+ CRUSH rule 3 x 638 [43,113]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,97]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,45]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,87]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,82]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,86]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,50]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3,40]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,84]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36,97]
+ CRUSH rule 3 x 697 [19,60]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,62]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,94]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,30]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,56]
+ CRUSH rule 3 x 723 [117,25]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,48]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,44]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,119]
+ CRUSH rule 3 x 752 [82,47]
+ CRUSH rule 3 x 753 [24,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,27]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,80]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,37]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,75]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,29]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,30]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,91]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,88]
+ CRUSH rule 3 x 824 [102,81]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,11]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,35]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,85]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,77]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,16]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,98]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,58]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,12]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,41]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,24]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,21]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,37]
+ CRUSH rule 3 x 942 [80,8]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,52]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,55]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,78]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,37]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,22]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,38]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,98]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,59]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,31]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,10]
+ CRUSH rule 3 x 1014 [33,98]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,44]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [83,88]
+ rule 3 (delltestrule) num_rep 3 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,62]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,60]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,48]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,111]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,20]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,85]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,12]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,93]
+ CRUSH rule 3 x 54 [28,3]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,91]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,92]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,16]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,89]
+ CRUSH rule 3 x 79 [64,75]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,57]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,117]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,2]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,55]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,29]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,119]
+ CRUSH rule 3 x 146 [12,73]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,8]
+ CRUSH rule 3 x 154 [19,5]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,10]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,47]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,86]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,17]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,38]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,118]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,32]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,54]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,104]
+ CRUSH rule 3 x 234 [55,94]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,105]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,41]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,22]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,66]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,74]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,69]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,112]
+ CRUSH rule 3 x 291 [35,52]
+ CRUSH rule 3 x 292 [20,60]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,66]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,55]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,80]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,16]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,16]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,92]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,88]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,40]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,113]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,101]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,58]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,94]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,114]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,20]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,92]
+ CRUSH rule 3 x 410 [70,11]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,21]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,15]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,22]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,34]
+ CRUSH rule 3 x 456 [101,119]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,2]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,109]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,13]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,113]
+ CRUSH rule 3 x 493 [94,105]
+ CRUSH rule 3 x 494 [66,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,78]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,81]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,54]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,17]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59,111]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,60]
+ CRUSH rule 3 x 518 [18,13]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,119]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,66]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,21]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,18]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,29]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,108]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,62]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,40]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,64]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,30]
+ CRUSH rule 3 x 638 [43,113]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,97]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,45]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,87]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,82]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,86]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,50]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3,40]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,84]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36,97]
+ CRUSH rule 3 x 697 [19,60]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,62]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,94]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,30]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,56]
+ CRUSH rule 3 x 723 [117,25]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,48]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,44]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,119]
+ CRUSH rule 3 x 752 [82,47]
+ CRUSH rule 3 x 753 [24,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,27]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,80]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,37]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,75]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,29]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,30]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,91]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,88]
+ CRUSH rule 3 x 824 [102,81]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,11]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,35]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,85]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,77]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,16]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,98]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,58]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,12]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,41]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,24]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,21]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,37]
+ CRUSH rule 3 x 942 [80,8]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,52]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,55]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,78]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,37]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,22]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,38]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,98]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,59]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,31]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,10]
+ CRUSH rule 3 x 1014 [33,98]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,44]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [83,88]
+ rule 3 (delltestrule) num_rep 4 result size == 2:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-vary-r-4.t b/src/test/cli/crushtool/test-map-vary-r-4.t
new file mode 100644
index 000000000..29b41ccf6
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-vary-r-4.t
@@ -0,0 +1,3078 @@
+ $ crushtool -i "$TESTDIR/test-map-vary-r.crushmap" --test --show-mappings --show-statistics --rule 3 --set-chooseleaf-vary-r 4 --weight 0 0 --weight 4 0 --weight 9 0 --min-rep 2 --max-rep 4
+ rule 3 (delltestrule), x = 0..1023, numrep = 2..4
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,12]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,52]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,46]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,40]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,107]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,57]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,114]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,29]
+ CRUSH rule 3 x 54 [28,77]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,105]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,88]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,43]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,39]
+ CRUSH rule 3 x 79 [64,65]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,83]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,68]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,26]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,17]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,83]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,100]
+ CRUSH rule 3 x 146 [12,15]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,81]
+ CRUSH rule 3 x 154 [19,56]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,22]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,103]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,100]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,85]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,113]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,66]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,86]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,102]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,32]
+ CRUSH rule 3 x 234 [55,78]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,10]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,87]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,31]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,54]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,75]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,18]
+ CRUSH rule 3 x 291 [35,42]
+ CRUSH rule 3 x 292 [20,74]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,36]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,105]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,119]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,15]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,17]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,116]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,118]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,112]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,32]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,81]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,12]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,113]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,70]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,21]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,44]
+ CRUSH rule 3 x 410 [70,47]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,15]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,79]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,21]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,68]
+ CRUSH rule 3 x 456 [101,60]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,106]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,106]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,61]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,58]
+ CRUSH rule 3 x 493 [94,89]
+ CRUSH rule 3 x 494 [56,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,82]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,6]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,77]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59,111]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,30]
+ CRUSH rule 3 x 518 [18,37]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,104]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,92]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,19]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,116]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,35]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,32]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,24]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,44]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,50]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,114]
+ CRUSH rule 3 x 638 [43,78]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,99]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,39]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,107]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,102]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,100]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,116]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3,40]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,26]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36,97]
+ CRUSH rule 3 x 697 [19,38]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,60]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,113]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,38]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,26]
+ CRUSH rule 3 x 723 [117,75]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,38]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,94]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,56]
+ CRUSH rule 3 x 752 [82,16]
+ CRUSH rule 3 x 753 [116,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,65]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,90]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,53]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,93]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,61]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,94]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,19]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,102]
+ CRUSH rule 3 x 824 [102,95]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,33]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,75]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,39]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,31]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,29]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,66]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,62]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,80]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,25]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,60]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,107]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,10]
+ CRUSH rule 3 x 942 [80,37]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,111]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,16]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,46]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,75]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,19]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,100]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,113]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,33]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,22]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,35]
+ CRUSH rule 3 x 1014 [33,48]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,111]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [59,88]
+ rule 3 (delltestrule) num_rep 2 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,12]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,52]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,46]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,40]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,107]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,57]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,114]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,29]
+ CRUSH rule 3 x 54 [28,77]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,105]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,88]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,43]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,39]
+ CRUSH rule 3 x 79 [64,65]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,83]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,68]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,26]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,17]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,83]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,100]
+ CRUSH rule 3 x 146 [12,15]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,81]
+ CRUSH rule 3 x 154 [19,56]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,22]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,103]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,100]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,85]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,113]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,66]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,86]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,102]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,32]
+ CRUSH rule 3 x 234 [55,78]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,10]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,87]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,31]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,54]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,75]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,18]
+ CRUSH rule 3 x 291 [35,42]
+ CRUSH rule 3 x 292 [20,74]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,36]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,105]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,119]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,15]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,17]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,116]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,118]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,112]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,32]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,81]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,12]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,113]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,70]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,21]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,44]
+ CRUSH rule 3 x 410 [70,47]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,15]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,79]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,21]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,68]
+ CRUSH rule 3 x 456 [101,60]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,106]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,106]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,61]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,58]
+ CRUSH rule 3 x 493 [94,89]
+ CRUSH rule 3 x 494 [56,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,82]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,6]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,77]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59,111]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,30]
+ CRUSH rule 3 x 518 [18,37]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,104]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,92]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,19]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,116]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,35]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,32]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,24]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,44]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,50]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,114]
+ CRUSH rule 3 x 638 [43,78]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,99]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,39]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,107]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,102]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,100]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,116]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3,40]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,26]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36,97]
+ CRUSH rule 3 x 697 [19,38]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,60]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,113]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,38]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,26]
+ CRUSH rule 3 x 723 [117,75]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,38]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,94]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,56]
+ CRUSH rule 3 x 752 [82,16]
+ CRUSH rule 3 x 753 [116,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,65]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,90]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,53]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,93]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,61]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,94]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,19]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,102]
+ CRUSH rule 3 x 824 [102,95]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,33]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,75]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,39]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,31]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,29]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,66]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,62]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,80]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,25]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,60]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,107]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,10]
+ CRUSH rule 3 x 942 [80,37]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,111]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,16]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,46]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,75]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,19]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,100]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,113]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,33]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,22]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,35]
+ CRUSH rule 3 x 1014 [33,48]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,111]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [59,88]
+ rule 3 (delltestrule) num_rep 3 result size == 2:\t1024/1024 (esc)
+ CRUSH rule 3 x 0 [94,85]
+ CRUSH rule 3 x 1 [73,78]
+ CRUSH rule 3 x 2 [91,104]
+ CRUSH rule 3 x 3 [51,94]
+ CRUSH rule 3 x 4 [45,28]
+ CRUSH rule 3 x 5 [89,113]
+ CRUSH rule 3 x 6 [91,12]
+ CRUSH rule 3 x 7 [104,71]
+ CRUSH rule 3 x 8 [41,12]
+ CRUSH rule 3 x 9 [46,35]
+ CRUSH rule 3 x 10 [61,60]
+ CRUSH rule 3 x 11 [13,74]
+ CRUSH rule 3 x 12 [83,62]
+ CRUSH rule 3 x 13 [27,117]
+ CRUSH rule 3 x 14 [105,115]
+ CRUSH rule 3 x 15 [18,87]
+ CRUSH rule 3 x 16 [103,52]
+ CRUSH rule 3 x 17 [85,80]
+ CRUSH rule 3 x 18 [11,46]
+ CRUSH rule 3 x 19 [75,114]
+ CRUSH rule 3 x 20 [111,27]
+ CRUSH rule 3 x 21 [84,7]
+ CRUSH rule 3 x 22 [23,66]
+ CRUSH rule 3 x 23 [19,84]
+ CRUSH rule 3 x 24 [83,40]
+ CRUSH rule 3 x 25 [81,108]
+ CRUSH rule 3 x 26 [17,117]
+ CRUSH rule 3 x 27 [33,58]
+ CRUSH rule 3 x 28 [45,98]
+ CRUSH rule 3 x 29 [8,46]
+ CRUSH rule 3 x 30 [55,119]
+ CRUSH rule 3 x 31 [76,35]
+ CRUSH rule 3 x 32 [72,13]
+ CRUSH rule 3 x 33 [86,107]
+ CRUSH rule 3 x 34 [7,38]
+ CRUSH rule 3 x 35 [108,31]
+ CRUSH rule 3 x 36 [67,24]
+ CRUSH rule 3 x 37 [38,17]
+ CRUSH rule 3 x 38 [72,57]
+ CRUSH rule 3 x 39 [68,73]
+ CRUSH rule 3 x 40 [30,25]
+ CRUSH rule 3 x 41 [52,91]
+ CRUSH rule 3 x 42 [106,39]
+ CRUSH rule 3 x 43 [10,115]
+ CRUSH rule 3 x 44 [101,115]
+ CRUSH rule 3 x 45 [83,80]
+ CRUSH rule 3 x 46 [54,33]
+ CRUSH rule 3 x 47 [106,41]
+ CRUSH rule 3 x 48 [34,65]
+ CRUSH rule 3 x 49 [99,46]
+ CRUSH rule 3 x 50 [42,85]
+ CRUSH rule 3 x 51 [6,114]
+ CRUSH rule 3 x 52 [82,14]
+ CRUSH rule 3 x 53 [32,29]
+ CRUSH rule 3 x 54 [28,77]
+ CRUSH rule 3 x 55 [14,44]
+ CRUSH rule 3 x 56 [21,112]
+ CRUSH rule 3 x 57 [93,26]
+ CRUSH rule 3 x 58 [48,95]
+ CRUSH rule 3 x 59 [21,104]
+ CRUSH rule 3 x 60 [90,75]
+ CRUSH rule 3 x 61 [88,39]
+ CRUSH rule 3 x 62 [100,8]
+ CRUSH rule 3 x 63 [79,96]
+ CRUSH rule 3 x 64 [1,77]
+ CRUSH rule 3 x 65 [32,25]
+ CRUSH rule 3 x 66 [48,93]
+ CRUSH rule 3 x 67 [94,91]
+ CRUSH rule 3 x 68 [102,105]
+ CRUSH rule 3 x 69 [62,20]
+ CRUSH rule 3 x 70 [84,27]
+ CRUSH rule 3 x 71 [12,99]
+ CRUSH rule 3 x 72 [26,69]
+ CRUSH rule 3 x 73 [29,88]
+ CRUSH rule 3 x 74 [29,60]
+ CRUSH rule 3 x 75 [60,43]
+ CRUSH rule 3 x 76 [55,60]
+ CRUSH rule 3 x 77 [107,78]
+ CRUSH rule 3 x 78 [86,39]
+ CRUSH rule 3 x 79 [64,65]
+ CRUSH rule 3 x 80 [73,26]
+ CRUSH rule 3 x 81 [64,57]
+ CRUSH rule 3 x 82 [37,1]
+ CRUSH rule 3 x 83 [92,22]
+ CRUSH rule 3 x 84 [49,40]
+ CRUSH rule 3 x 85 [87,30]
+ CRUSH rule 3 x 86 [37,119]
+ CRUSH rule 3 x 87 [116,3]
+ CRUSH rule 3 x 88 [38,22]
+ CRUSH rule 3 x 89 [76,41]
+ CRUSH rule 3 x 90 [14,98]
+ CRUSH rule 3 x 91 [68,27]
+ CRUSH rule 3 x 92 [86,13]
+ CRUSH rule 3 x 93 [44,83]
+ CRUSH rule 3 x 94 [46,15]
+ CRUSH rule 3 x 95 [108,6]
+ CRUSH rule 3 x 96 [66,25]
+ CRUSH rule 3 x 97 [111,33]
+ CRUSH rule 3 x 98 [93,36]
+ CRUSH rule 3 x 99 [78,17]
+ CRUSH rule 3 x 100 [28,55]
+ CRUSH rule 3 x 101 [91,34]
+ CRUSH rule 3 x 102 [82,93]
+ CRUSH rule 3 x 103 [66,105]
+ CRUSH rule 3 x 104 [116,10]
+ CRUSH rule 3 x 105 [34,69]
+ CRUSH rule 3 x 106 [69,66]
+ CRUSH rule 3 x 107 [1,41]
+ CRUSH rule 3 x 108 [7,68]
+ CRUSH rule 3 x 109 [112,87]
+ CRUSH rule 3 x 110 [54,10]
+ CRUSH rule 3 x 111 [10,86]
+ CRUSH rule 3 x 112 [80,29]
+ CRUSH rule 3 x 113 [69,26]
+ CRUSH rule 3 x 114 [79,46]
+ CRUSH rule 3 x 115 [10,111]
+ CRUSH rule 3 x 116 [37,86]
+ CRUSH rule 3 x 117 [87,50]
+ CRUSH rule 3 x 118 [23,106]
+ CRUSH rule 3 x 119 [104,14]
+ CRUSH rule 3 x 120 [44,3]
+ CRUSH rule 3 x 121 [80,14]
+ CRUSH rule 3 x 122 [45,68]
+ CRUSH rule 3 x 123 [112,22]
+ CRUSH rule 3 x 124 [97,118]
+ CRUSH rule 3 x 125 [66,7]
+ CRUSH rule 3 x 126 [70,23]
+ CRUSH rule 3 x 127 [70,13]
+ CRUSH rule 3 x 128 [11,119]
+ CRUSH rule 3 x 129 [103,108]
+ CRUSH rule 3 x 130 [50,17]
+ CRUSH rule 3 x 131 [44,55]
+ CRUSH rule 3 x 132 [69,1]
+ CRUSH rule 3 x 133 [67,104]
+ CRUSH rule 3 x 134 [37,66]
+ CRUSH rule 3 x 135 [78,101]
+ CRUSH rule 3 x 136 [32,83]
+ CRUSH rule 3 x 137 [92,81]
+ CRUSH rule 3 x 138 [54,17]
+ CRUSH rule 3 x 139 [89,92]
+ CRUSH rule 3 x 140 [39,1]
+ CRUSH rule 3 x 141 [89,28]
+ CRUSH rule 3 x 142 [22,26]
+ CRUSH rule 3 x 143 [96,77]
+ CRUSH rule 3 x 144 [13,111]
+ CRUSH rule 3 x 145 [77,100]
+ CRUSH rule 3 x 146 [12,15]
+ CRUSH rule 3 x 147 [2,11]
+ CRUSH rule 3 x 148 [85,108]
+ CRUSH rule 3 x 149 [103,62]
+ CRUSH rule 3 x 150 [14,78]
+ CRUSH rule 3 x 151 [75,119]
+ CRUSH rule 3 x 152 [49,84]
+ CRUSH rule 3 x 153 [92,81]
+ CRUSH rule 3 x 154 [19,56]
+ CRUSH rule 3 x 155 [12,75]
+ CRUSH rule 3 x 156 [107,112]
+ CRUSH rule 3 x 157 [15,28]
+ CRUSH rule 3 x 158 [11,113]
+ CRUSH rule 3 x 159 [33,52]
+ CRUSH rule 3 x 160 [86,35]
+ CRUSH rule 3 x 161 [19,117]
+ CRUSH rule 3 x 162 [55,113]
+ CRUSH rule 3 x 163 [54,87]
+ CRUSH rule 3 x 164 [72,8]
+ CRUSH rule 3 x 165 [25,74]
+ CRUSH rule 3 x 166 [2,22]
+ CRUSH rule 3 x 167 [89,56]
+ CRUSH rule 3 x 168 [68,103]
+ CRUSH rule 3 x 169 [51,12]
+ CRUSH rule 3 x 170 [68,53]
+ CRUSH rule 3 x 171 [88,79]
+ CRUSH rule 3 x 172 [117,89]
+ CRUSH rule 3 x 173 [29,40]
+ CRUSH rule 3 x 174 [67,86]
+ CRUSH rule 3 x 175 [48,85]
+ CRUSH rule 3 x 176 [94,83]
+ CRUSH rule 3 x 177 [53,18]
+ CRUSH rule 3 x 178 [39,30]
+ CRUSH rule 3 x 179 [72,17]
+ CRUSH rule 3 x 180 [3,114]
+ CRUSH rule 3 x 181 [18,16]
+ CRUSH rule 3 x 182 [75,5]
+ CRUSH rule 3 x 183 [11,110]
+ CRUSH rule 3 x 184 [79,48]
+ CRUSH rule 3 x 185 [97,100]
+ CRUSH rule 3 x 186 [67,44]
+ CRUSH rule 3 x 187 [6,50]
+ CRUSH rule 3 x 188 [76,85]
+ CRUSH rule 3 x 189 [96,7]
+ CRUSH rule 3 x 190 [90,95]
+ CRUSH rule 3 x 191 [49,113]
+ CRUSH rule 3 x 192 [93,58]
+ CRUSH rule 3 x 193 [89,66]
+ CRUSH rule 3 x 194 [62,3]
+ CRUSH rule 3 x 195 [119,85]
+ CRUSH rule 3 x 196 [20,72]
+ CRUSH rule 3 x 197 [6,116]
+ CRUSH rule 3 x 198 [55,92]
+ CRUSH rule 3 x 199 [77,66]
+ CRUSH rule 3 x 200 [12,81]
+ CRUSH rule 3 x 201 [52,71]
+ CRUSH rule 3 x 202 [98,59]
+ CRUSH rule 3 x 203 [36,19]
+ CRUSH rule 3 x 204 [10,113]
+ CRUSH rule 3 x 205 [38,79]
+ CRUSH rule 3 x 206 [38,105]
+ CRUSH rule 3 x 207 [19,86]
+ CRUSH rule 3 x 208 [63,92]
+ CRUSH rule 3 x 209 [70,99]
+ CRUSH rule 3 x 210 [79,102]
+ CRUSH rule 3 x 211 [26,27]
+ CRUSH rule 3 x 212 [28,107]
+ CRUSH rule 3 x 213 [100,49]
+ CRUSH rule 3 x 214 [91,88]
+ CRUSH rule 3 x 215 [92,7]
+ CRUSH rule 3 x 216 [99,108]
+ CRUSH rule 3 x 217 [86,97]
+ CRUSH rule 3 x 218 [70,10]
+ CRUSH rule 3 x 219 [61,112]
+ CRUSH rule 3 x 220 [23,66]
+ CRUSH rule 3 x 221 [51,66]
+ CRUSH rule 3 x 222 [50,65]
+ CRUSH rule 3 x 223 [34,45]
+ CRUSH rule 3 x 224 [107,44]
+ CRUSH rule 3 x 225 [61,102]
+ CRUSH rule 3 x 226 [44,87]
+ CRUSH rule 3 x 227 [55,66]
+ CRUSH rule 3 x 228 [117,103]
+ CRUSH rule 3 x 229 [100,27]
+ CRUSH rule 3 x 230 [41,32]
+ CRUSH rule 3 x 231 [30,16]
+ CRUSH rule 3 x 232 [23,102]
+ CRUSH rule 3 x 233 [47,32]
+ CRUSH rule 3 x 234 [55,78]
+ CRUSH rule 3 x 235 [20,32]
+ CRUSH rule 3 x 236 [95,118]
+ CRUSH rule 3 x 237 [21,72]
+ CRUSH rule 3 x 238 [109,53]
+ CRUSH rule 3 x 239 [40,10]
+ CRUSH rule 3 x 240 [63,96]
+ CRUSH rule 3 x 241 [47,1]
+ CRUSH rule 3 x 242 [73,24]
+ CRUSH rule 3 x 243 [76,79]
+ CRUSH rule 3 x 244 [103,115]
+ CRUSH rule 3 x 245 [106,29]
+ CRUSH rule 3 x 246 [35,5]
+ CRUSH rule 3 x 247 [116,37]
+ CRUSH rule 3 x 248 [8,34]
+ CRUSH rule 3 x 249 [2,105]
+ CRUSH rule 3 x 250 [34,79]
+ CRUSH rule 3 x 251 [28,87]
+ CRUSH rule 3 x 252 [95,24]
+ CRUSH rule 3 x 253 [109,97]
+ CRUSH rule 3 x 254 [99,56]
+ CRUSH rule 3 x 255 [112,31]
+ CRUSH rule 3 x 256 [94,31]
+ CRUSH rule 3 x 257 [100,39]
+ CRUSH rule 3 x 258 [34,83]
+ CRUSH rule 3 x 259 [70,87]
+ CRUSH rule 3 x 260 [89,24]
+ CRUSH rule 3 x 261 [94,77]
+ CRUSH rule 3 x 262 [42,97]
+ CRUSH rule 3 x 263 [113,37]
+ CRUSH rule 3 x 264 [36,89]
+ CRUSH rule 3 x 265 [14,46]
+ CRUSH rule 3 x 266 [75,48]
+ CRUSH rule 3 x 267 [6,46]
+ CRUSH rule 3 x 268 [38,3]
+ CRUSH rule 3 x 269 [86,91]
+ CRUSH rule 3 x 270 [87,54]
+ CRUSH rule 3 x 271 [19,78]
+ CRUSH rule 3 x 272 [73,110]
+ CRUSH rule 3 x 273 [69,113]
+ CRUSH rule 3 x 274 [47,26]
+ CRUSH rule 3 x 275 [92,29]
+ CRUSH rule 3 x 276 [7,38]
+ CRUSH rule 3 x 277 [74,95]
+ CRUSH rule 3 x 278 [107,62]
+ CRUSH rule 3 x 279 [112,53]
+ CRUSH rule 3 x 280 [113,75]
+ CRUSH rule 3 x 281 [89,40]
+ CRUSH rule 3 x 282 [20,46]
+ CRUSH rule 3 x 283 [8,36]
+ CRUSH rule 3 x 284 [66,85]
+ CRUSH rule 3 x 285 [99,109]
+ CRUSH rule 3 x 286 [78,89]
+ CRUSH rule 3 x 287 [12,79]
+ CRUSH rule 3 x 288 [24,37]
+ CRUSH rule 3 x 289 [105,74]
+ CRUSH rule 3 x 290 [25,18]
+ CRUSH rule 3 x 291 [35,42]
+ CRUSH rule 3 x 292 [20,74]
+ CRUSH rule 3 x 293 [27,118]
+ CRUSH rule 3 x 294 [60,75]
+ CRUSH rule 3 x 295 [37,36]
+ CRUSH rule 3 x 296 [16,28]
+ CRUSH rule 3 x 297 [36,29]
+ CRUSH rule 3 x 298 [70,105]
+ CRUSH rule 3 x 299 [116,85]
+ CRUSH rule 3 x 300 [67,36]
+ CRUSH rule 3 x 301 [117,71]
+ CRUSH rule 3 x 302 [78,105]
+ CRUSH rule 3 x 303 [19,82]
+ CRUSH rule 3 x 304 [101,38]
+ CRUSH rule 3 x 305 [5,49]
+ CRUSH rule 3 x 306 [41,64]
+ CRUSH rule 3 x 307 [65,119]
+ CRUSH rule 3 x 308 [91,115]
+ CRUSH rule 3 x 309 [38,41]
+ CRUSH rule 3 x 310 [26,43]
+ CRUSH rule 3 x 311 [36,75]
+ CRUSH rule 3 x 312 [114,15]
+ CRUSH rule 3 x 313 [104,79]
+ CRUSH rule 3 x 314 [28,43]
+ CRUSH rule 3 x 315 [118,17]
+ CRUSH rule 3 x 316 [98,39]
+ CRUSH rule 3 x 317 [118,21]
+ CRUSH rule 3 x 318 [17,94]
+ CRUSH rule 3 x 319 [53,62]
+ CRUSH rule 3 x 320 [36,3]
+ CRUSH rule 3 x 321 [33,60]
+ CRUSH rule 3 x 322 [68,3]
+ CRUSH rule 3 x 323 [66,95]
+ CRUSH rule 3 x 324 [21,42]
+ CRUSH rule 3 x 325 [52,43]
+ CRUSH rule 3 x 326 [7,90]
+ CRUSH rule 3 x 327 [62,3]
+ CRUSH rule 3 x 328 [61,42]
+ CRUSH rule 3 x 329 [19,115]
+ CRUSH rule 3 x 330 [24,15]
+ CRUSH rule 3 x 331 [84,14]
+ CRUSH rule 3 x 332 [61,72]
+ CRUSH rule 3 x 333 [116,6]
+ CRUSH rule 3 x 334 [94,29]
+ CRUSH rule 3 x 335 [71,116]
+ CRUSH rule 3 x 336 [24,11]
+ CRUSH rule 3 x 337 [18,23]
+ CRUSH rule 3 x 338 [43,118]
+ CRUSH rule 3 x 339 [13,50]
+ CRUSH rule 3 x 340 [81,115]
+ CRUSH rule 3 x 341 [46,65]
+ CRUSH rule 3 x 342 [92,71]
+ CRUSH rule 3 x 343 [49,56]
+ CRUSH rule 3 x 344 [1,25]
+ CRUSH rule 3 x 345 [56,11]
+ CRUSH rule 3 x 346 [3,112]
+ CRUSH rule 3 x 347 [106,85]
+ CRUSH rule 3 x 348 [10,114]
+ CRUSH rule 3 x 349 [96,51]
+ CRUSH rule 3 x 350 [63,32]
+ CRUSH rule 3 x 351 [60,20]
+ CRUSH rule 3 x 352 [36,21]
+ CRUSH rule 3 x 353 [10,32]
+ CRUSH rule 3 x 354 [55,74]
+ CRUSH rule 3 x 355 [73,80]
+ CRUSH rule 3 x 356 [75,96]
+ CRUSH rule 3 x 357 [70,89]
+ CRUSH rule 3 x 358 [97,92]
+ CRUSH rule 3 x 359 [119,20]
+ CRUSH rule 3 x 360 [106,15]
+ CRUSH rule 3 x 361 [27,56]
+ CRUSH rule 3 x 362 [28,22]
+ CRUSH rule 3 x 363 [68,81]
+ CRUSH rule 3 x 364 [23,2]
+ CRUSH rule 3 x 365 [57,12]
+ CRUSH rule 3 x 366 [42,61]
+ CRUSH rule 3 x 367 [103,108]
+ CRUSH rule 3 x 368 [103,119]
+ CRUSH rule 3 x 369 [12,11]
+ CRUSH rule 3 x 370 [11,109]
+ CRUSH rule 3 x 371 [34,65]
+ CRUSH rule 3 x 372 [58,29]
+ CRUSH rule 3 x 373 [6,64]
+ CRUSH rule 3 x 374 [110,89]
+ CRUSH rule 3 x 375 [5,89]
+ CRUSH rule 3 x 376 [91,98]
+ CRUSH rule 3 x 377 [93,113]
+ CRUSH rule 3 x 378 [68,41]
+ CRUSH rule 3 x 379 [77,94]
+ CRUSH rule 3 x 380 [76,107]
+ CRUSH rule 3 x 381 [36,20]
+ CRUSH rule 3 x 382 [26,107]
+ CRUSH rule 3 x 383 [48,93]
+ CRUSH rule 3 x 384 [15,100]
+ CRUSH rule 3 x 385 [82,27]
+ CRUSH rule 3 x 386 [83,24]
+ CRUSH rule 3 x 387 [16,70]
+ CRUSH rule 3 x 388 [29,66]
+ CRUSH rule 3 x 389 [92,67]
+ CRUSH rule 3 x 390 [68,13]
+ CRUSH rule 3 x 391 [15,2]
+ CRUSH rule 3 x 392 [21,110]
+ CRUSH rule 3 x 393 [91,113]
+ CRUSH rule 3 x 394 [38,21]
+ CRUSH rule 3 x 395 [21,92]
+ CRUSH rule 3 x 396 [12,59]
+ CRUSH rule 3 x 397 [40,51]
+ CRUSH rule 3 x 398 [44,21]
+ CRUSH rule 3 x 399 [5,33]
+ CRUSH rule 3 x 400 [19,64]
+ CRUSH rule 3 x 401 [79,109]
+ CRUSH rule 3 x 402 [107,72]
+ CRUSH rule 3 x 403 [23,74]
+ CRUSH rule 3 x 404 [87,78]
+ CRUSH rule 3 x 405 [90,93]
+ CRUSH rule 3 x 406 [15,98]
+ CRUSH rule 3 x 407 [70,25]
+ CRUSH rule 3 x 408 [55,104]
+ CRUSH rule 3 x 409 [73,44]
+ CRUSH rule 3 x 410 [70,47]
+ CRUSH rule 3 x 411 [34,15]
+ CRUSH rule 3 x 412 [105,44]
+ CRUSH rule 3 x 413 [41,86]
+ CRUSH rule 3 x 414 [70,71]
+ CRUSH rule 3 x 415 [107,80]
+ CRUSH rule 3 x 416 [2,23]
+ CRUSH rule 3 x 417 [26,23]
+ CRUSH rule 3 x 418 [51,114]
+ CRUSH rule 3 x 419 [8,94]
+ CRUSH rule 3 x 420 [109,15]
+ CRUSH rule 3 x 421 [114,77]
+ CRUSH rule 3 x 422 [109,39]
+ CRUSH rule 3 x 423 [59,98]
+ CRUSH rule 3 x 424 [92,65]
+ CRUSH rule 3 x 425 [101,50]
+ CRUSH rule 3 x 426 [36,57]
+ CRUSH rule 3 x 427 [8,38]
+ CRUSH rule 3 x 428 [68,63]
+ CRUSH rule 3 x 429 [76,13]
+ CRUSH rule 3 x 430 [67,100]
+ CRUSH rule 3 x 431 [70,53]
+ CRUSH rule 3 x 432 [7,50]
+ CRUSH rule 3 x 433 [49,24]
+ CRUSH rule 3 x 434 [64,59]
+ CRUSH rule 3 x 435 [110,71]
+ CRUSH rule 3 x 436 [106,47]
+ CRUSH rule 3 x 437 [26,29]
+ CRUSH rule 3 x 438 [118,95]
+ CRUSH rule 3 x 439 [40,83]
+ CRUSH rule 3 x 440 [45,68]
+ CRUSH rule 3 x 441 [112,15]
+ CRUSH rule 3 x 442 [55,18]
+ CRUSH rule 3 x 443 [44,37]
+ CRUSH rule 3 x 444 [71,119]
+ CRUSH rule 3 x 445 [58,63]
+ CRUSH rule 3 x 446 [40,20]
+ CRUSH rule 3 x 447 [100,43]
+ CRUSH rule 3 x 448 [111,15]
+ CRUSH rule 3 x 449 [67,102]
+ CRUSH rule 3 x 450 [117,79]
+ CRUSH rule 3 x 451 [66,75]
+ CRUSH rule 3 x 452 [70,33]
+ CRUSH rule 3 x 453 [82,21]
+ CRUSH rule 3 x 454 [53,28]
+ CRUSH rule 3 x 455 [91,68]
+ CRUSH rule 3 x 456 [101,60]
+ CRUSH rule 3 x 457 [113,97]
+ CRUSH rule 3 x 458 [119,41]
+ CRUSH rule 3 x 459 [50,55]
+ CRUSH rule 3 x 460 [105,30]
+ CRUSH rule 3 x 461 [102,45]
+ CRUSH rule 3 x 462 [98,25]
+ CRUSH rule 3 x 463 [108,57]
+ CRUSH rule 3 x 464 [19,50]
+ CRUSH rule 3 x 465 [62,95]
+ CRUSH rule 3 x 466 [53,106]
+ CRUSH rule 3 x 467 [40,95]
+ CRUSH rule 3 x 468 [97,108]
+ CRUSH rule 3 x 469 [98,16]
+ CRUSH rule 3 x 470 [50,3]
+ CRUSH rule 3 x 471 [40,14]
+ CRUSH rule 3 x 472 [27,28]
+ CRUSH rule 3 x 473 [48,17]
+ CRUSH rule 3 x 474 [51,113]
+ CRUSH rule 3 x 475 [49,66]
+ CRUSH rule 3 x 476 [110,55]
+ CRUSH rule 3 x 477 [80,8]
+ CRUSH rule 3 x 478 [78,25]
+ CRUSH rule 3 x 479 [31,106]
+ CRUSH rule 3 x 480 [75,5]
+ CRUSH rule 3 x 481 [26,37]
+ CRUSH rule 3 x 482 [84,87]
+ CRUSH rule 3 x 483 [15,113]
+ CRUSH rule 3 x 484 [37,28]
+ CRUSH rule 3 x 485 [84,61]
+ CRUSH rule 3 x 486 [92,61]
+ CRUSH rule 3 x 487 [106,53]
+ CRUSH rule 3 x 488 [42,7]
+ CRUSH rule 3 x 489 [89,98]
+ CRUSH rule 3 x 490 [22,119]
+ CRUSH rule 3 x 491 [99,5]
+ CRUSH rule 3 x 492 [21,58]
+ CRUSH rule 3 x 493 [94,89]
+ CRUSH rule 3 x 494 [56,59]
+ CRUSH rule 3 x 495 [95,119]
+ CRUSH rule 3 x 496 [46,43]
+ CRUSH rule 3 x 497 [102,89]
+ CRUSH rule 3 x 498 [21,82]
+ CRUSH rule 3 x 499 [5,95]
+ CRUSH rule 3 x 500 [50,6]
+ CRUSH rule 3 x 501 [60,75]
+ CRUSH rule 3 x 502 [65,1]
+ CRUSH rule 3 x 503 [21,115]
+ CRUSH rule 3 x 504 [67,5]
+ CRUSH rule 3 x 505 [12,91]
+ CRUSH rule 3 x 506 [79,110]
+ CRUSH rule 3 x 507 [34,77]
+ CRUSH rule 3 x 508 [34,45]
+ CRUSH rule 3 x 509 [19,74]
+ CRUSH rule 3 x 510 [117,69]
+ CRUSH rule 3 x 511 [14,34]
+ CRUSH rule 3 x 512 [59,111]
+ CRUSH rule 3 x 513 [102,13]
+ CRUSH rule 3 x 514 [75,111]
+ CRUSH rule 3 x 515 [84,83]
+ CRUSH rule 3 x 516 [37,80]
+ CRUSH rule 3 x 517 [83,30]
+ CRUSH rule 3 x 518 [18,37]
+ CRUSH rule 3 x 519 [67,52]
+ CRUSH rule 3 x 520 [15,70]
+ CRUSH rule 3 x 521 [70,22]
+ CRUSH rule 3 x 522 [56,3]
+ CRUSH rule 3 x 523 [36,23]
+ CRUSH rule 3 x 524 [33,94]
+ CRUSH rule 3 x 525 [63,104]
+ CRUSH rule 3 x 526 [83,118]
+ CRUSH rule 3 x 527 [37,5]
+ CRUSH rule 3 x 528 [108,43]
+ CRUSH rule 3 x 529 [74,7]
+ CRUSH rule 3 x 530 [49,12]
+ CRUSH rule 3 x 531 [117,107]
+ CRUSH rule 3 x 532 [31,68]
+ CRUSH rule 3 x 533 [5,73]
+ CRUSH rule 3 x 534 [97,104]
+ CRUSH rule 3 x 535 [48,41]
+ CRUSH rule 3 x 536 [113,71]
+ CRUSH rule 3 x 537 [116,7]
+ CRUSH rule 3 x 538 [85,40]
+ CRUSH rule 3 x 539 [72,85]
+ CRUSH rule 3 x 540 [39,12]
+ CRUSH rule 3 x 541 [53,64]
+ CRUSH rule 3 x 542 [27,54]
+ CRUSH rule 3 x 543 [45,106]
+ CRUSH rule 3 x 544 [59,26]
+ CRUSH rule 3 x 545 [118,15]
+ CRUSH rule 3 x 546 [18,71]
+ CRUSH rule 3 x 547 [67,80]
+ CRUSH rule 3 x 548 [53,92]
+ CRUSH rule 3 x 549 [60,51]
+ CRUSH rule 3 x 550 [92,37]
+ CRUSH rule 3 x 551 [77,52]
+ CRUSH rule 3 x 552 [61,80]
+ CRUSH rule 3 x 553 [71,84]
+ CRUSH rule 3 x 554 [61,52]
+ CRUSH rule 3 x 555 [76,69]
+ CRUSH rule 3 x 556 [106,10]
+ CRUSH rule 3 x 557 [26,35]
+ CRUSH rule 3 x 558 [41,46]
+ CRUSH rule 3 x 559 [65,86]
+ CRUSH rule 3 x 560 [94,91]
+ CRUSH rule 3 x 561 [27,98]
+ CRUSH rule 3 x 562 [78,19]
+ CRUSH rule 3 x 563 [59,82]
+ CRUSH rule 3 x 564 [96,15]
+ CRUSH rule 3 x 565 [8,92]
+ CRUSH rule 3 x 566 [119,81]
+ CRUSH rule 3 x 567 [7,46]
+ CRUSH rule 3 x 568 [57,96]
+ CRUSH rule 3 x 569 [65,100]
+ CRUSH rule 3 x 570 [98,103]
+ CRUSH rule 3 x 571 [95,110]
+ CRUSH rule 3 x 572 [62,75]
+ CRUSH rule 3 x 573 [1,20]
+ CRUSH rule 3 x 574 [89,64]
+ CRUSH rule 3 x 575 [87,54]
+ CRUSH rule 3 x 576 [21,113]
+ CRUSH rule 3 x 577 [8,113]
+ CRUSH rule 3 x 578 [75,116]
+ CRUSH rule 3 x 579 [105,96]
+ CRUSH rule 3 x 580 [51,12]
+ CRUSH rule 3 x 581 [55,40]
+ CRUSH rule 3 x 582 [27,106]
+ CRUSH rule 3 x 583 [6,102]
+ CRUSH rule 3 x 584 [10,90]
+ CRUSH rule 3 x 585 [20,88]
+ CRUSH rule 3 x 586 [48,67]
+ CRUSH rule 3 x 587 [29,5]
+ CRUSH rule 3 x 588 [103,40]
+ CRUSH rule 3 x 589 [88,85]
+ CRUSH rule 3 x 590 [76,11]
+ CRUSH rule 3 x 591 [42,17]
+ CRUSH rule 3 x 592 [78,6]
+ CRUSH rule 3 x 593 [82,35]
+ CRUSH rule 3 x 594 [27,76]
+ CRUSH rule 3 x 595 [52,10]
+ CRUSH rule 3 x 596 [82,99]
+ CRUSH rule 3 x 597 [16,32]
+ CRUSH rule 3 x 598 [37,36]
+ CRUSH rule 3 x 599 [10,24]
+ CRUSH rule 3 x 600 [24,37]
+ CRUSH rule 3 x 601 [104,21]
+ CRUSH rule 3 x 602 [48,39]
+ CRUSH rule 3 x 603 [93,44]
+ CRUSH rule 3 x 604 [118,87]
+ CRUSH rule 3 x 605 [104,63]
+ CRUSH rule 3 x 606 [90,103]
+ CRUSH rule 3 x 607 [95,72]
+ CRUSH rule 3 x 608 [112,71]
+ CRUSH rule 3 x 609 [34,16]
+ CRUSH rule 3 x 610 [106,73]
+ CRUSH rule 3 x 611 [66,37]
+ CRUSH rule 3 x 612 [2,20]
+ CRUSH rule 3 x 613 [13,92]
+ CRUSH rule 3 x 614 [50,65]
+ CRUSH rule 3 x 615 [24,39]
+ CRUSH rule 3 x 616 [41,46]
+ CRUSH rule 3 x 617 [111,81]
+ CRUSH rule 3 x 618 [3,72]
+ CRUSH rule 3 x 619 [92,31]
+ CRUSH rule 3 x 620 [108,31]
+ CRUSH rule 3 x 621 [105,50]
+ CRUSH rule 3 x 622 [67,102]
+ CRUSH rule 3 x 623 [69,117]
+ CRUSH rule 3 x 624 [115,79]
+ CRUSH rule 3 x 625 [73,94]
+ CRUSH rule 3 x 626 [52,25]
+ CRUSH rule 3 x 627 [116,105]
+ CRUSH rule 3 x 628 [98,87]
+ CRUSH rule 3 x 629 [6,116]
+ CRUSH rule 3 x 630 [22,50]
+ CRUSH rule 3 x 631 [35,96]
+ CRUSH rule 3 x 632 [80,53]
+ CRUSH rule 3 x 633 [65,110]
+ CRUSH rule 3 x 634 [87,50]
+ CRUSH rule 3 x 635 [107,111]
+ CRUSH rule 3 x 636 [23,30]
+ CRUSH rule 3 x 637 [99,114]
+ CRUSH rule 3 x 638 [43,78]
+ CRUSH rule 3 x 639 [30,31]
+ CRUSH rule 3 x 640 [113,87]
+ CRUSH rule 3 x 641 [45,58]
+ CRUSH rule 3 x 642 [47,30]
+ CRUSH rule 3 x 643 [64,99]
+ CRUSH rule 3 x 644 [31,119]
+ CRUSH rule 3 x 645 [77,90]
+ CRUSH rule 3 x 646 [37,26]
+ CRUSH rule 3 x 647 [65,112]
+ CRUSH rule 3 x 648 [31,84]
+ CRUSH rule 3 x 649 [88,39]
+ CRUSH rule 3 x 650 [21,44]
+ CRUSH rule 3 x 651 [63,12]
+ CRUSH rule 3 x 652 [57,28]
+ CRUSH rule 3 x 653 [38,63]
+ CRUSH rule 3 x 654 [104,107]
+ CRUSH rule 3 x 655 [89,109]
+ CRUSH rule 3 x 656 [79,84]
+ CRUSH rule 3 x 657 [47,18]
+ CRUSH rule 3 x 658 [80,49]
+ CRUSH rule 3 x 659 [11,104]
+ CRUSH rule 3 x 660 [65,102]
+ CRUSH rule 3 x 661 [96,67]
+ CRUSH rule 3 x 662 [111,43]
+ CRUSH rule 3 x 663 [83,115]
+ CRUSH rule 3 x 664 [59,52]
+ CRUSH rule 3 x 665 [31,86]
+ CRUSH rule 3 x 666 [112,8]
+ CRUSH rule 3 x 667 [70,107]
+ CRUSH rule 3 x 668 [96,43]
+ CRUSH rule 3 x 669 [56,25]
+ CRUSH rule 3 x 670 [98,83]
+ CRUSH rule 3 x 671 [57,100]
+ CRUSH rule 3 x 672 [37,98]
+ CRUSH rule 3 x 673 [83,116]
+ CRUSH rule 3 x 674 [36,95]
+ CRUSH rule 3 x 675 [88,91]
+ CRUSH rule 3 x 676 [3,40]
+ CRUSH rule 3 x 677 [88,105]
+ CRUSH rule 3 x 678 [27,100]
+ CRUSH rule 3 x 679 [33,118]
+ CRUSH rule 3 x 680 [111,81]
+ CRUSH rule 3 x 681 [53,68]
+ CRUSH rule 3 x 682 [12,83]
+ CRUSH rule 3 x 683 [24,67]
+ CRUSH rule 3 x 684 [98,45]
+ CRUSH rule 3 x 685 [106,25]
+ CRUSH rule 3 x 686 [86,45]
+ CRUSH rule 3 x 687 [49,102]
+ CRUSH rule 3 x 688 [16,52]
+ CRUSH rule 3 x 689 [32,101]
+ CRUSH rule 3 x 690 [96,79]
+ CRUSH rule 3 x 691 [34,99]
+ CRUSH rule 3 x 692 [97,68]
+ CRUSH rule 3 x 693 [29,38]
+ CRUSH rule 3 x 694 [6,26]
+ CRUSH rule 3 x 695 [31,112]
+ CRUSH rule 3 x 696 [36,97]
+ CRUSH rule 3 x 697 [19,38]
+ CRUSH rule 3 x 698 [30,103]
+ CRUSH rule 3 x 699 [47,60]
+ CRUSH rule 3 x 700 [99,82]
+ CRUSH rule 3 x 701 [53,72]
+ CRUSH rule 3 x 702 [101,113]
+ CRUSH rule 3 x 703 [92,20]
+ CRUSH rule 3 x 704 [34,47]
+ CRUSH rule 3 x 705 [105,88]
+ CRUSH rule 3 x 706 [74,20]
+ CRUSH rule 3 x 707 [95,40]
+ CRUSH rule 3 x 708 [95,38]
+ CRUSH rule 3 x 709 [73,94]
+ CRUSH rule 3 x 710 [94,7]
+ CRUSH rule 3 x 711 [68,16]
+ CRUSH rule 3 x 712 [107,64]
+ CRUSH rule 3 x 713 [29,2]
+ CRUSH rule 3 x 714 [86,97]
+ CRUSH rule 3 x 715 [74,95]
+ CRUSH rule 3 x 716 [101,74]
+ CRUSH rule 3 x 717 [12,57]
+ CRUSH rule 3 x 718 [83,106]
+ CRUSH rule 3 x 719 [26,39]
+ CRUSH rule 3 x 720 [69,64]
+ CRUSH rule 3 x 721 [51,119]
+ CRUSH rule 3 x 722 [15,26]
+ CRUSH rule 3 x 723 [117,75]
+ CRUSH rule 3 x 724 [45,106]
+ CRUSH rule 3 x 725 [53,66]
+ CRUSH rule 3 x 726 [103,38]
+ CRUSH rule 3 x 727 [89,115]
+ CRUSH rule 3 x 728 [76,65]
+ CRUSH rule 3 x 729 [35,48]
+ CRUSH rule 3 x 730 [28,37]
+ CRUSH rule 3 x 731 [78,6]
+ CRUSH rule 3 x 732 [1,93]
+ CRUSH rule 3 x 733 [35,44]
+ CRUSH rule 3 x 734 [119,93]
+ CRUSH rule 3 x 735 [102,17]
+ CRUSH rule 3 x 736 [37,78]
+ CRUSH rule 3 x 737 [117,35]
+ CRUSH rule 3 x 738 [57,56]
+ CRUSH rule 3 x 739 [87,24]
+ CRUSH rule 3 x 740 [29,34]
+ CRUSH rule 3 x 741 [47,94]
+ CRUSH rule 3 x 742 [106,107]
+ CRUSH rule 3 x 743 [105,5]
+ CRUSH rule 3 x 744 [23,30]
+ CRUSH rule 3 x 745 [37,106]
+ CRUSH rule 3 x 746 [56,47]
+ CRUSH rule 3 x 747 [56,107]
+ CRUSH rule 3 x 748 [48,25]
+ CRUSH rule 3 x 749 [102,93]
+ CRUSH rule 3 x 750 [83,102]
+ CRUSH rule 3 x 751 [25,56]
+ CRUSH rule 3 x 752 [82,16]
+ CRUSH rule 3 x 753 [116,14]
+ CRUSH rule 3 x 754 [114,39]
+ CRUSH rule 3 x 755 [87,60]
+ CRUSH rule 3 x 756 [113,77]
+ CRUSH rule 3 x 757 [47,112]
+ CRUSH rule 3 x 758 [54,107]
+ CRUSH rule 3 x 759 [74,65]
+ CRUSH rule 3 x 760 [88,47]
+ CRUSH rule 3 x 761 [73,98]
+ CRUSH rule 3 x 762 [34,33]
+ CRUSH rule 3 x 763 [13,116]
+ CRUSH rule 3 x 764 [89,2]
+ CRUSH rule 3 x 765 [109,77]
+ CRUSH rule 3 x 766 [19,92]
+ CRUSH rule 3 x 767 [41,80]
+ CRUSH rule 3 x 768 [106,16]
+ CRUSH rule 3 x 769 [91,2]
+ CRUSH rule 3 x 770 [72,19]
+ CRUSH rule 3 x 771 [115,63]
+ CRUSH rule 3 x 772 [97,102]
+ CRUSH rule 3 x 773 [116,91]
+ CRUSH rule 3 x 774 [100,105]
+ CRUSH rule 3 x 775 [102,95]
+ CRUSH rule 3 x 776 [69,44]
+ CRUSH rule 3 x 777 [91,102]
+ CRUSH rule 3 x 778 [83,110]
+ CRUSH rule 3 x 779 [47,80]
+ CRUSH rule 3 x 780 [63,117]
+ CRUSH rule 3 x 781 [105,106]
+ CRUSH rule 3 x 782 [117,107]
+ CRUSH rule 3 x 783 [19,30]
+ CRUSH rule 3 x 784 [63,82]
+ CRUSH rule 3 x 785 [27,50]
+ CRUSH rule 3 x 786 [41,90]
+ CRUSH rule 3 x 787 [108,27]
+ CRUSH rule 3 x 788 [74,75]
+ CRUSH rule 3 x 789 [50,67]
+ CRUSH rule 3 x 790 [20,108]
+ CRUSH rule 3 x 791 [96,53]
+ CRUSH rule 3 x 792 [80,13]
+ CRUSH rule 3 x 793 [6,82]
+ CRUSH rule 3 x 794 [14,90]
+ CRUSH rule 3 x 795 [30,67]
+ CRUSH rule 3 x 796 [87,60]
+ CRUSH rule 3 x 797 [64,93]
+ CRUSH rule 3 x 798 [42,19]
+ CRUSH rule 3 x 799 [19,113]
+ CRUSH rule 3 x 800 [106,22]
+ CRUSH rule 3 x 801 [2,11]
+ CRUSH rule 3 x 802 [63,1]
+ CRUSH rule 3 x 803 [37,46]
+ CRUSH rule 3 x 804 [33,66]
+ CRUSH rule 3 x 805 [96,3]
+ CRUSH rule 3 x 806 [48,57]
+ CRUSH rule 3 x 807 [48,85]
+ CRUSH rule 3 x 808 [76,15]
+ CRUSH rule 3 x 809 [27,90]
+ CRUSH rule 3 x 810 [119,61]
+ CRUSH rule 3 x 811 [111,93]
+ CRUSH rule 3 x 812 [25,94]
+ CRUSH rule 3 x 813 [81,50]
+ CRUSH rule 3 x 814 [95,48]
+ CRUSH rule 3 x 815 [84,6]
+ CRUSH rule 3 x 816 [64,3]
+ CRUSH rule 3 x 817 [63,117]
+ CRUSH rule 3 x 818 [69,52]
+ CRUSH rule 3 x 819 [88,19]
+ CRUSH rule 3 x 820 [104,29]
+ CRUSH rule 3 x 821 [58,107]
+ CRUSH rule 3 x 822 [20,18]
+ CRUSH rule 3 x 823 [63,102]
+ CRUSH rule 3 x 824 [102,95]
+ CRUSH rule 3 x 825 [47,46]
+ CRUSH rule 3 x 826 [44,33]
+ CRUSH rule 3 x 827 [101,115]
+ CRUSH rule 3 x 828 [60,39]
+ CRUSH rule 3 x 829 [45,24]
+ CRUSH rule 3 x 830 [51,96]
+ CRUSH rule 3 x 831 [78,53]
+ CRUSH rule 3 x 832 [28,15]
+ CRUSH rule 3 x 833 [57,72]
+ CRUSH rule 3 x 834 [90,77]
+ CRUSH rule 3 x 835 [14,50]
+ CRUSH rule 3 x 836 [63,100]
+ CRUSH rule 3 x 837 [76,85]
+ CRUSH rule 3 x 838 [106,75]
+ CRUSH rule 3 x 839 [87,12]
+ CRUSH rule 3 x 840 [33,117]
+ CRUSH rule 3 x 841 [110,13]
+ CRUSH rule 3 x 842 [66,97]
+ CRUSH rule 3 x 843 [11,50]
+ CRUSH rule 3 x 844 [74,22]
+ CRUSH rule 3 x 845 [74,20]
+ CRUSH rule 3 x 846 [43,113]
+ CRUSH rule 3 x 847 [62,105]
+ CRUSH rule 3 x 848 [92,19]
+ CRUSH rule 3 x 849 [93,118]
+ CRUSH rule 3 x 850 [83,119]
+ CRUSH rule 3 x 851 [65,56]
+ CRUSH rule 3 x 852 [60,11]
+ CRUSH rule 3 x 853 [88,11]
+ CRUSH rule 3 x 854 [83,52]
+ CRUSH rule 3 x 855 [2,22]
+ CRUSH rule 3 x 856 [40,13]
+ CRUSH rule 3 x 857 [69,110]
+ CRUSH rule 3 x 858 [98,27]
+ CRUSH rule 3 x 859 [56,41]
+ CRUSH rule 3 x 860 [11,30]
+ CRUSH rule 3 x 861 [22,68]
+ CRUSH rule 3 x 862 [22,52]
+ CRUSH rule 3 x 863 [79,32]
+ CRUSH rule 3 x 864 [77,32]
+ CRUSH rule 3 x 865 [119,99]
+ CRUSH rule 3 x 866 [18,39]
+ CRUSH rule 3 x 867 [3,58]
+ CRUSH rule 3 x 868 [100,22]
+ CRUSH rule 3 x 869 [22,86]
+ CRUSH rule 3 x 870 [73,94]
+ CRUSH rule 3 x 871 [84,51]
+ CRUSH rule 3 x 872 [72,91]
+ CRUSH rule 3 x 873 [81,72]
+ CRUSH rule 3 x 874 [21,38]
+ CRUSH rule 3 x 875 [115,27]
+ CRUSH rule 3 x 876 [98,16]
+ CRUSH rule 3 x 877 [80,25]
+ CRUSH rule 3 x 878 [87,114]
+ CRUSH rule 3 x 879 [29,1]
+ CRUSH rule 3 x 880 [23,2]
+ CRUSH rule 3 x 881 [109,97]
+ CRUSH rule 3 x 882 [31,36]
+ CRUSH rule 3 x 883 [102,17]
+ CRUSH rule 3 x 884 [80,23]
+ CRUSH rule 3 x 885 [46,31]
+ CRUSH rule 3 x 886 [2,11]
+ CRUSH rule 3 x 887 [5,85]
+ CRUSH rule 3 x 888 [16,64]
+ CRUSH rule 3 x 889 [84,45]
+ CRUSH rule 3 x 890 [65,50]
+ CRUSH rule 3 x 891 [86,59]
+ CRUSH rule 3 x 892 [64,11]
+ CRUSH rule 3 x 893 [20,118]
+ CRUSH rule 3 x 894 [32,14]
+ CRUSH rule 3 x 895 [40,91]
+ CRUSH rule 3 x 896 [113,29]
+ CRUSH rule 3 x 897 [107,112]
+ CRUSH rule 3 x 898 [76,51]
+ CRUSH rule 3 x 899 [75,66]
+ CRUSH rule 3 x 900 [83,111]
+ CRUSH rule 3 x 901 [66,17]
+ CRUSH rule 3 x 902 [25,5]
+ CRUSH rule 3 x 903 [53,54]
+ CRUSH rule 3 x 904 [50,10]
+ CRUSH rule 3 x 905 [99,106]
+ CRUSH rule 3 x 906 [68,73]
+ CRUSH rule 3 x 907 [109,45]
+ CRUSH rule 3 x 908 [47,24]
+ CRUSH rule 3 x 909 [73,94]
+ CRUSH rule 3 x 910 [71,26]
+ CRUSH rule 3 x 911 [39,62]
+ CRUSH rule 3 x 912 [90,39]
+ CRUSH rule 3 x 913 [29,80]
+ CRUSH rule 3 x 914 [84,99]
+ CRUSH rule 3 x 915 [49,62]
+ CRUSH rule 3 x 916 [32,7]
+ CRUSH rule 3 x 917 [46,91]
+ CRUSH rule 3 x 918 [82,71]
+ CRUSH rule 3 x 919 [13,109]
+ CRUSH rule 3 x 920 [25,100]
+ CRUSH rule 3 x 921 [55,32]
+ CRUSH rule 3 x 922 [33,96]
+ CRUSH rule 3 x 923 [28,79]
+ CRUSH rule 3 x 924 [1,41]
+ CRUSH rule 3 x 925 [113,25]
+ CRUSH rule 3 x 926 [64,65]
+ CRUSH rule 3 x 927 [32,23]
+ CRUSH rule 3 x 928 [13,94]
+ CRUSH rule 3 x 929 [85,60]
+ CRUSH rule 3 x 930 [104,55]
+ CRUSH rule 3 x 931 [46,91]
+ CRUSH rule 3 x 932 [43,54]
+ CRUSH rule 3 x 933 [18,93]
+ CRUSH rule 3 x 934 [68,107]
+ CRUSH rule 3 x 935 [28,23]
+ CRUSH rule 3 x 936 [104,51]
+ CRUSH rule 3 x 937 [110,37]
+ CRUSH rule 3 x 938 [48,69]
+ CRUSH rule 3 x 939 [77,32]
+ CRUSH rule 3 x 940 [76,19]
+ CRUSH rule 3 x 941 [66,10]
+ CRUSH rule 3 x 942 [80,37]
+ CRUSH rule 3 x 943 [75,82]
+ CRUSH rule 3 x 944 [113,15]
+ CRUSH rule 3 x 945 [71,111]
+ CRUSH rule 3 x 946 [37,115]
+ CRUSH rule 3 x 947 [107,48]
+ CRUSH rule 3 x 948 [108,8]
+ CRUSH rule 3 x 949 [46,14]
+ CRUSH rule 3 x 950 [96,13]
+ CRUSH rule 3 x 951 [40,63]
+ CRUSH rule 3 x 952 [114,16]
+ CRUSH rule 3 x 953 [62,53]
+ CRUSH rule 3 x 954 [103,68]
+ CRUSH rule 3 x 955 [42,63]
+ CRUSH rule 3 x 956 [72,6]
+ CRUSH rule 3 x 957 [117,6]
+ CRUSH rule 3 x 958 [23,74]
+ CRUSH rule 3 x 959 [42,87]
+ CRUSH rule 3 x 960 [113,91]
+ CRUSH rule 3 x 961 [116,61]
+ CRUSH rule 3 x 962 [60,41]
+ CRUSH rule 3 x 963 [103,46]
+ CRUSH rule 3 x 964 [66,15]
+ CRUSH rule 3 x 965 [47,108]
+ CRUSH rule 3 x 966 [88,69]
+ CRUSH rule 3 x 967 [71,74]
+ CRUSH rule 3 x 968 [74,75]
+ CRUSH rule 3 x 969 [53,30]
+ CRUSH rule 3 x 970 [3,2]
+ CRUSH rule 3 x 971 [66,19]
+ CRUSH rule 3 x 972 [3,115]
+ CRUSH rule 3 x 973 [113,89]
+ CRUSH rule 3 x 974 [114,73]
+ CRUSH rule 3 x 975 [83,96]
+ CRUSH rule 3 x 976 [81,100]
+ CRUSH rule 3 x 977 [95,76]
+ CRUSH rule 3 x 978 [35,119]
+ CRUSH rule 3 x 979 [98,13]
+ CRUSH rule 3 x 980 [39,113]
+ CRUSH rule 3 x 981 [89,46]
+ CRUSH rule 3 x 982 [19,66]
+ CRUSH rule 3 x 983 [34,107]
+ CRUSH rule 3 x 984 [78,23]
+ CRUSH rule 3 x 985 [99,24]
+ CRUSH rule 3 x 986 [44,33]
+ CRUSH rule 3 x 987 [25,98]
+ CRUSH rule 3 x 988 [79,84]
+ CRUSH rule 3 x 989 [87,60]
+ CRUSH rule 3 x 990 [72,22]
+ CRUSH rule 3 x 991 [90,71]
+ CRUSH rule 3 x 992 [30,75]
+ CRUSH rule 3 x 993 [74,27]
+ CRUSH rule 3 x 994 [74,75]
+ CRUSH rule 3 x 995 [100,45]
+ CRUSH rule 3 x 996 [41,34]
+ CRUSH rule 3 x 997 [89,32]
+ CRUSH rule 3 x 998 [92,41]
+ CRUSH rule 3 x 999 [117,13]
+ CRUSH rule 3 x 1000 [50,31]
+ CRUSH rule 3 x 1001 [83,116]
+ CRUSH rule 3 x 1002 [94,13]
+ CRUSH rule 3 x 1003 [43,54]
+ CRUSH rule 3 x 1004 [89,106]
+ CRUSH rule 3 x 1005 [105,76]
+ CRUSH rule 3 x 1006 [45,5]
+ CRUSH rule 3 x 1007 [19,111]
+ CRUSH rule 3 x 1008 [31,74]
+ CRUSH rule 3 x 1009 [1,51]
+ CRUSH rule 3 x 1010 [31,108]
+ CRUSH rule 3 x 1011 [64,3]
+ CRUSH rule 3 x 1012 [68,81]
+ CRUSH rule 3 x 1013 [5,35]
+ CRUSH rule 3 x 1014 [33,48]
+ CRUSH rule 3 x 1015 [106,99]
+ CRUSH rule 3 x 1016 [107,111]
+ CRUSH rule 3 x 1017 [12,69]
+ CRUSH rule 3 x 1018 [61,60]
+ CRUSH rule 3 x 1019 [27,88]
+ CRUSH rule 3 x 1020 [31,111]
+ CRUSH rule 3 x 1021 [22,36]
+ CRUSH rule 3 x 1022 [73,28]
+ CRUSH rule 3 x 1023 [59,88]
+ rule 3 (delltestrule) num_rep 4 result size == 2:\t1024/1024 (esc)
+ crushtool successfully built or modified map. Use '-o <file>' to write it out.
diff --git a/src/test/cli/crushtool/test-map-vary-r.crushmap b/src/test/cli/crushtool/test-map-vary-r.crushmap
new file mode 100644
index 000000000..41886aefc
--- /dev/null
+++ b/src/test/cli/crushtool/test-map-vary-r.crushmap
Binary files differ
diff --git a/src/test/cli/crushtool/tree.template b/src/test/cli/crushtool/tree.template
new file mode 100644
index 000000000..980857871
--- /dev/null
+++ b/src/test/cli/crushtool/tree.template
Binary files differ
diff --git a/src/test/cli/crushtool/tree.template.final b/src/test/cli/crushtool/tree.template.final
new file mode 100644
index 000000000..c46ba3cbe
--- /dev/null
+++ b/src/test/cli/crushtool/tree.template.final
@@ -0,0 +1,64 @@
+# begin crush map
+
+# devices
+device 0 device0
+device 1 device1
+device 2 device2
+device 3 device3
+device 4 device4
+device 5 device5
+device 6 device6
+device 7 device7
+
+# types
+type 0 device
+type 1 host
+type 2 cluster
+
+# buckets
+host host0 {
+ id -2 # do not change unnecessarily
+ # weight 8.00000
+ alg tree # do not change pos for existing items unnecessarily
+ hash 0 # rjenkins1
+ item device0 weight 1.00000 pos 0
+ item device1 weight 1.00000 pos 1
+ item device2 weight 1.00000 pos 2
+ item device3 weight 1.00000 pos 3
+ item device4 weight 1.00000 pos 4
+ item device5 weight 1.00000 pos 5
+ item device6 weight 1.00000 pos 6
+ item device7 weight 1.00000 pos 7
+}
+cluster cluster0 {
+ id -1 # do not change unnecessarily
+ # weight 8.00000
+ alg tree # do not change pos for existing items unnecessarily
+ hash 0 # rjenkins1
+ item host0 weight 8.00000 pos 0
+}
+
+# rules
+rule data {
+ id 0
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule metadata {
+ id 1
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+rule rbd {
+ id 2
+ type replicated
+ step take cluster0
+ step chooseleaf firstn 0 type host
+ step emit
+}
+
+# end crush map
diff --git a/src/test/cli/monmaptool/add-exists.t b/src/test/cli/monmaptool/add-exists.t
new file mode 100644
index 000000000..c51d9fb80
--- /dev/null
+++ b/src/test/cli/monmaptool/add-exists.t
@@ -0,0 +1,29 @@
+ $ monmaptool --create mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (0 monitors)
+
+ $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+
+ $ monmaptool --add foo 2.3.4.5:6789 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: writing epoch 0 to mymonmap (1 monitors)
+ $ monmaptool --add foo 3.4.5.6:7890 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: map already contains mon.foo
+ monmaptool -h for usage
+ [1]
+
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
+ 0: v1:2.3.4.5:6789/0 mon.foo
+
+ $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+ $ [ "$ORIG_FSID" = "$NEW_FSID" ]
diff --git a/src/test/cli/monmaptool/add-many.t b/src/test/cli/monmaptool/add-many.t
new file mode 100644
index 000000000..4118d46af
--- /dev/null
+++ b/src/test/cli/monmaptool/add-many.t
@@ -0,0 +1,35 @@
+ $ monmaptool --create mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (0 monitors)
+
+ $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+
+ $ monmaptool --add foo 2.3.4.5:6789 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: writing epoch 0 to mymonmap (1 monitors)
+ $ monmaptool --add bar 3.4.5.6:7890 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: writing epoch 0 to mymonmap (2 monitors)
+ $ monmaptool --add baz 4.5.6.7:8901 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: writing epoch 0 to mymonmap (3 monitors)
+ $ monmaptool --addv fiz '[v2:172.21.15.68:6791,v1:172.21.15.68:6792]' mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: writing epoch 0 to mymonmap (4 monitors)
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
+ 0: v1:2.3.4.5:6789/0 mon.foo
+ 1: [v2:172.21.15.68:6791/0,v1:172.21.15.68:6792/0] mon.fiz
+ 2: v2:3.4.5.6:7890/0 mon.bar
+ 3: v2:4.5.6.7:8901/0 mon.baz
+
+ $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+ $ [ "$ORIG_FSID" = "$NEW_FSID" ]
diff --git a/src/test/cli/monmaptool/clobber.t b/src/test/cli/monmaptool/clobber.t
new file mode 100644
index 000000000..2fb45ad61
--- /dev/null
+++ b/src/test/cli/monmaptool/clobber.t
@@ -0,0 +1,44 @@
+ $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (1 monitors)
+
+ $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+
+ $ monmaptool --create mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: mymonmap exists, --clobber to overwrite
+ [255]
+
+# hasn't changed yet
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
+ 0: v1:2.3.4.5:6789/0 mon.foo
+
+ $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+ $ [ "$ORIG_FSID" = "$NEW_FSID" ]
+
+ $ monmaptool --create --clobber mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (0 monitors)
+
+ $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+ $ [ "$ORIG_FSID" != "$NEW_FSID" ]
+
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
diff --git a/src/test/cli/monmaptool/create-print.t b/src/test/cli/monmaptool/create-print.t
new file mode 100644
index 000000000..31cca0242
--- /dev/null
+++ b/src/test/cli/monmaptool/create-print.t
@@ -0,0 +1,23 @@
+ $ monmaptool --create mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (0 monitors)
+
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
+
+ $ monmaptool --print -- mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
diff --git a/src/test/cli/monmaptool/create-with-add.t b/src/test/cli/monmaptool/create-with-add.t
new file mode 100644
index 000000000..b32a7b35c
--- /dev/null
+++ b/src/test/cli/monmaptool/create-with-add.t
@@ -0,0 +1,15 @@
+ $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (1 monitors)
+
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
+ 0: v1:2.3.4.5:6789/0 mon.foo
diff --git a/src/test/cli/monmaptool/feature-set-unset-list.t b/src/test/cli/monmaptool/feature-set-unset-list.t
new file mode 100644
index 000000000..6a0c8a17a
--- /dev/null
+++ b/src/test/cli/monmaptool/feature-set-unset-list.t
@@ -0,0 +1,87 @@
+ $ monmaptool --create --add a 10.10.10.10:1234 /tmp/test.monmap.1234
+ monmaptool: monmap file /tmp/test.monmap.1234
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to /tmp/test.monmap.1234 (1 monitors)
+
+ $ monmaptool --feature-list --feature-list plain --feature-list parseable /tmp/test.monmap.1234
+ monmaptool: monmap file /tmp/test.monmap.1234
+ MONMAP FEATURES:
+ persistent: [none]
+ optional: [none]
+ required: [none]
+
+ AVAILABLE FEATURES:
+ supported: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ persistent: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ MONMAP FEATURES:
+ persistent: [none]
+ optional: [none]
+ required: [none]
+
+ AVAILABLE FEATURES:
+ supported: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ persistent: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ monmap:persistent:[none]
+ monmap:optional:[none]
+ monmap:required:[none]
+ available:supported:[kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ available:persistent:[kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+
+ $ monmaptool --feature-set foo /tmp/test.monmap.1234
+ unknown features name 'foo' or unable to parse value: Expected option value to be integer, got 'foo'
+ monmaptool -h for usage
+ [1]
+
+ $ monmaptool --feature-set kraken --feature-set 4096 --optional --feature-set 32 --persistent /tmp/test.monmap.1234
+ monmaptool: monmap file /tmp/test.monmap.1234
+ monmaptool: writing epoch 0 to /tmp/test.monmap.1234 (1 monitors)
+
+ $ monmaptool --feature-list /tmp/test.monmap.1234
+ monmaptool: monmap file /tmp/test.monmap.1234
+ MONMAP FEATURES:
+ persistent: [kraken(1),octopus(32)]
+ optional: [unknown(4096)]
+ required: [kraken(1),octopus(32),unknown(4096)]
+
+ AVAILABLE FEATURES:
+ supported: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ persistent: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+
+ $ monmaptool --feature-unset 32 --optional --feature-list /tmp/test.monmap.1234
+ monmaptool: monmap file /tmp/test.monmap.1234
+ MONMAP FEATURES:
+ persistent: [kraken(1),octopus(32)]
+ optional: [unknown(4096)]
+ required: [kraken(1),octopus(32),unknown(4096)]
+
+ AVAILABLE FEATURES:
+ supported: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ persistent: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ monmaptool: writing epoch 0 to /tmp/test.monmap.1234 (1 monitors)
+
+ $ monmaptool --feature-unset 32 --persistent --feature-unset 4096 --optional --feature-list /tmp/test.monmap.1234
+ monmaptool: monmap file /tmp/test.monmap.1234
+ MONMAP FEATURES:
+ persistent: [kraken(1)]
+ optional: [none]
+ required: [kraken(1)]
+
+ AVAILABLE FEATURES:
+ supported: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ persistent: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ monmaptool: writing epoch 0 to /tmp/test.monmap.1234 (1 monitors)
+
+ $ monmaptool --feature-unset kraken --feature-list /tmp/test.monmap.1234
+ monmaptool: monmap file /tmp/test.monmap.1234
+ MONMAP FEATURES:
+ persistent: [none]
+ optional: [none]
+ required: [none]
+
+ AVAILABLE FEATURES:
+ supported: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ persistent: [kraken(1),luminous(2),mimic(4),osdmap-prune(8),nautilus(16),octopus(32),pacific(64),elector-pinging(128),quincy(256),reef(512)]
+ monmaptool: writing epoch 0 to /tmp/test.monmap.1234 (1 monitors)
+
+ $ rm /tmp/test.monmap.1234
diff --git a/src/test/cli/monmaptool/help.t b/src/test/cli/monmaptool/help.t
new file mode 100644
index 000000000..f12c1a07d
--- /dev/null
+++ b/src/test/cli/monmaptool/help.t
@@ -0,0 +1,11 @@
+ $ monmaptool --help
+ usage: monmaptool [--print] [--create [--clobber] [--fsid uuid]]
+ [--enable-all-features]
+ [--generate] [--set-initial-members]
+ [--add name 1.2.3.4:567] [--rm name]
+ [--addv name [v2:1.2.4.5:567,v1:1.2.3.4:568]]
+ [--feature-list [plain|parseable]]
+ [--feature-set <value> [--optional|--persistent]]
+ [--feature-unset <value> [--optional|--persistent]]
+ [--set-min-mon-release <release-major-number>]
+ <mapfilename>
diff --git a/src/test/cli/monmaptool/print-empty.t b/src/test/cli/monmaptool/print-empty.t
new file mode 100644
index 000000000..cd67db485
--- /dev/null
+++ b/src/test/cli/monmaptool/print-empty.t
@@ -0,0 +1,5 @@
+ $ touch empty
+ $ monmaptool --print empty
+ monmaptool: monmap file empty
+ monmaptool: unable to read monmap file
+ [255]
diff --git a/src/test/cli/monmaptool/print-nonexistent.t b/src/test/cli/monmaptool/print-nonexistent.t
new file mode 100644
index 000000000..ae366c1ee
--- /dev/null
+++ b/src/test/cli/monmaptool/print-nonexistent.t
@@ -0,0 +1,4 @@
+ $ monmaptool --print nonexistent
+ monmaptool: monmap file nonexistent
+ monmaptool: couldn't open nonexistent: (2) No such file or directory
+ [255]
diff --git a/src/test/cli/monmaptool/rm-nonexistent.t b/src/test/cli/monmaptool/rm-nonexistent.t
new file mode 100644
index 000000000..165dbb398
--- /dev/null
+++ b/src/test/cli/monmaptool/rm-nonexistent.t
@@ -0,0 +1,27 @@
+ $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (1 monitors)
+
+ $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+
+ $ monmaptool --rm doesnotexist mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: removing doesnotexist
+ monmaptool: map does not contain doesnotexist
+ monmaptool -h for usage
+ [1]
+
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
+ 0: v1:2.3.4.5:6789/0 mon.foo
+
+ $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+ $ [ "$ORIG_FSID" = "$NEW_FSID" ]
diff --git a/src/test/cli/monmaptool/rm.t b/src/test/cli/monmaptool/rm.t
new file mode 100644
index 000000000..4a693d59a
--- /dev/null
+++ b/src/test/cli/monmaptool/rm.t
@@ -0,0 +1,24 @@
+ $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ setting min_mon_release = pacific
+ monmaptool: writing epoch 0 to mymonmap (1 monitors)
+
+ $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+
+ $ monmaptool --rm foo mymonmap
+ monmaptool: monmap file mymonmap
+ monmaptool: removing foo
+ monmaptool: writing epoch 0 to mymonmap (0 monitors)
+
+ $ monmaptool --print mymonmap
+ monmaptool: monmap file mymonmap
+ epoch 0
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ min_mon_release 16 (pacific)
+ election_strategy: 1
+
+ $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)"
+ $ [ "$ORIG_FSID" = "$NEW_FSID" ]
diff --git a/src/test/cli/monmaptool/simple.t b/src/test/cli/monmaptool/simple.t
new file mode 100644
index 000000000..48c569117
--- /dev/null
+++ b/src/test/cli/monmaptool/simple.t
@@ -0,0 +1,3 @@
+ $ monmaptool
+ monmaptool: -h or --help for usage
+ [1]
diff --git a/src/test/cli/osdmaptool/ceph.conf.withracks b/src/test/cli/osdmaptool/ceph.conf.withracks
new file mode 100644
index 000000000..09399e955
--- /dev/null
+++ b/src/test/cli/osdmaptool/ceph.conf.withracks
@@ -0,0 +1,1480 @@
+
+[global]
+ auth supported = cephx
+ ms bind ipv6 = true
+
+[mon]
+ mon data = /var/ceph/mon
+ mon clock drift allowed = 0.1
+
+ osd pool default size = 3
+ osd pool default crush rule = 0
+
+ ; don't mark down osds out automatically; wait for an admin!
+ mon osd down out interval = 0
+
+[mon.alpha]
+ host = peon5752
+ mon addr = [2607:f298:4:2243::5752]:6789
+
+[mon.beta]
+ host = peon5753
+ mon addr = [2607:f298:4:2243::5753]:6789
+
+[mon.charlie]
+ host = peon5754
+ mon addr = [2607:f298:4:2243::5754]:6789
+
+[client]
+ rgw socket path = /var/run/ceph/radosgw.$name
+ rgw cache enabled = true
+ rgw dns name = objects.dreamhost.com
+ rgw swift url = https://objects.dreamhost.com
+
+[client.radosgw.peon5751]
+ host = peon5751
+ log file = /var/log/ceph/$name.log
+ debug rgw = 40
+ debug ms = 1
+
+
+[osd]
+ keyring = /mnt/osd.$id/keyring
+ osd data = /mnt/osd.$id
+ osd journal = /dev/disk/by-label/osd.$id.journal
+ osd mkfs type = btrfs
+ osd mount options btrfs = rw,noatime
+ devs = /dev/disk/by-label/osd.$id.data
+; temp sage
+ debug osd = 20
+ debug ms = 1
+ debug filestore = 20
+
+[osd.1]
+ host = cephstore5522
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5522]
+ public addr = [2607:f298:4:2243::5522]
+
+[osd.2]
+ host = cephstore5522
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5522]
+ public addr = [2607:f298:4:2243::5522]
+
+[osd.3]
+ host = cephstore5522
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5522]
+ public addr = [2607:f298:4:2243::5522]
+
+[osd.4]
+ host = cephstore5522
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5522]
+ public addr = [2607:f298:4:2243::5522]
+
+[osd.5]
+ host = cephstore5522
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5522]
+ public addr = [2607:f298:4:2243::5522]
+
+[osd.6]
+ host = cephstore5522
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5522]
+ public addr = [2607:f298:4:2243::5522]
+
+[osd.7]
+ host = cephstore5522
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5522]
+ public addr = [2607:f298:4:2243::5522]
+
+[osd.8]
+ host = cephstore5523
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5523]
+ public addr = [2607:f298:4:2243::5523]
+
+[osd.9]
+ host = cephstore5523
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5523]
+ public addr = [2607:f298:4:2243::5523]
+
+[osd.10]
+ host = cephstore5523
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5523]
+ public addr = [2607:f298:4:2243::5523]
+
+[osd.11]
+ host = cephstore5523
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5523]
+ public addr = [2607:f298:4:2243::5523]
+
+[osd.12]
+ host = cephstore5523
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5523]
+ public addr = [2607:f298:4:2243::5523]
+
+[osd.13]
+ host = cephstore5523
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5523]
+ public addr = [2607:f298:4:2243::5523]
+
+[osd.14]
+ host = cephstore5523
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5523]
+ public addr = [2607:f298:4:2243::5523]
+
+[osd.15]
+ host = cephstore5524
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5524]
+ public addr = [2607:f298:4:2243::5524]
+
+[osd.16]
+ host = cephstore5524
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5524]
+ public addr = [2607:f298:4:2243::5524]
+
+[osd.17]
+ host = cephstore5524
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5524]
+ public addr = [2607:f298:4:2243::5524]
+
+[osd.18]
+ host = cephstore5524
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5524]
+ public addr = [2607:f298:4:2243::5524]
+
+[osd.19]
+ host = cephstore5524
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5524]
+ public addr = [2607:f298:4:2243::5524]
+
+[osd.20]
+ host = cephstore5524
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5524]
+ public addr = [2607:f298:4:2243::5524]
+
+[osd.21]
+ host = cephstore5524
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5524]
+ public addr = [2607:f298:4:2243::5524]
+
+[osd.22]
+ host = cephstore5525
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5525]
+ public addr = [2607:f298:4:2243::5525]
+
+[osd.23]
+ host = cephstore5525
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5525]
+ public addr = [2607:f298:4:2243::5525]
+
+[osd.24]
+ host = cephstore5525
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5525]
+ public addr = [2607:f298:4:2243::5525]
+
+[osd.25]
+ host = cephstore5525
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5525]
+ public addr = [2607:f298:4:2243::5525]
+
+[osd.26]
+ host = cephstore5525
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5525]
+ public addr = [2607:f298:4:2243::5525]
+
+[osd.27]
+ host = cephstore5525
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5525]
+ public addr = [2607:f298:4:2243::5525]
+
+[osd.28]
+ host = cephstore5525
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5525]
+ public addr = [2607:f298:4:2243::5525]
+
+[osd.29]
+ host = cephstore5526
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5526]
+ public addr = [2607:f298:4:2243::5526]
+
+[osd.30]
+ host = cephstore5526
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5526]
+ public addr = [2607:f298:4:2243::5526]
+
+[osd.31]
+ host = cephstore5526
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5526]
+ public addr = [2607:f298:4:2243::5526]
+
+[osd.32]
+ host = cephstore5526
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5526]
+ public addr = [2607:f298:4:2243::5526]
+
+[osd.33]
+ host = cephstore5526
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5526]
+ public addr = [2607:f298:4:2243::5526]
+
+[osd.34]
+ host = cephstore5526
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5526]
+ public addr = [2607:f298:4:2243::5526]
+
+[osd.35]
+ host = cephstore5526
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5526]
+ public addr = [2607:f298:4:2243::5526]
+
+[osd.36]
+ host = cephstore5527
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5527]
+ public addr = [2607:f298:4:2243::5527]
+
+[osd.37]
+ host = cephstore5527
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5527]
+ public addr = [2607:f298:4:2243::5527]
+
+[osd.38]
+ host = cephstore5527
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5527]
+ public addr = [2607:f298:4:2243::5527]
+
+[osd.39]
+ host = cephstore5527
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5527]
+ public addr = [2607:f298:4:2243::5527]
+
+[osd.40]
+ host = cephstore5527
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5527]
+ public addr = [2607:f298:4:2243::5527]
+
+[osd.41]
+ host = cephstore5527
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5527]
+ public addr = [2607:f298:4:2243::5527]
+
+[osd.42]
+ host = cephstore5527
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5527]
+ public addr = [2607:f298:4:2243::5527]
+
+[osd.43]
+ host = cephstore5529
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5529]
+ public addr = [2607:f298:4:2243::5529]
+
+[osd.44]
+ host = cephstore5529
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5529]
+ public addr = [2607:f298:4:2243::5529]
+
+[osd.45]
+ host = cephstore5529
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5529]
+ public addr = [2607:f298:4:2243::5529]
+
+[osd.46]
+ host = cephstore5529
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5529]
+ public addr = [2607:f298:4:2243::5529]
+
+[osd.47]
+ host = cephstore5529
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5529]
+ public addr = [2607:f298:4:2243::5529]
+
+[osd.48]
+ host = cephstore5529
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5529]
+ public addr = [2607:f298:4:2243::5529]
+
+[osd.49]
+ host = cephstore5529
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5529]
+ public addr = [2607:f298:4:2243::5529]
+
+[osd.50]
+ host = cephstore5530
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5530]
+ public addr = [2607:f298:4:2243::5530]
+
+[osd.51]
+ host = cephstore5530
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5530]
+ public addr = [2607:f298:4:2243::5530]
+
+[osd.52]
+ host = cephstore5530
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5530]
+ public addr = [2607:f298:4:2243::5530]
+
+[osd.53]
+ host = cephstore5530
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5530]
+ public addr = [2607:f298:4:2243::5530]
+
+[osd.54]
+ host = cephstore5530
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5530]
+ public addr = [2607:f298:4:2243::5530]
+
+[osd.55]
+ host = cephstore5530
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5530]
+ public addr = [2607:f298:4:2243::5530]
+
+[osd.56]
+ host = cephstore5530
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::5530]
+ public addr = [2607:f298:4:2243::5530]
+
+[osd.57]
+ host = cephstore6230
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6230]
+ public addr = [2607:f298:4:2243::6230]
+
+[osd.58]
+ host = cephstore6230
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6230]
+ public addr = [2607:f298:4:2243::6230]
+
+[osd.59]
+ host = cephstore6230
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6230]
+ public addr = [2607:f298:4:2243::6230]
+
+[osd.60]
+ host = cephstore6230
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6230]
+ public addr = [2607:f298:4:2243::6230]
+
+[osd.61]
+ host = cephstore6230
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6230]
+ public addr = [2607:f298:4:2243::6230]
+
+[osd.62]
+ host = cephstore6230
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6230]
+ public addr = [2607:f298:4:2243::6230]
+
+[osd.63]
+ host = cephstore6230
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6230]
+ public addr = [2607:f298:4:2243::6230]
+
+[osd.64]
+ host = cephstore6231
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6231]
+ public addr = [2607:f298:4:2243::6231]
+
+[osd.65]
+ host = cephstore6231
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6231]
+ public addr = [2607:f298:4:2243::6231]
+
+[osd.66]
+ host = cephstore6231
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6231]
+ public addr = [2607:f298:4:2243::6231]
+
+[osd.67]
+ host = cephstore6231
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6231]
+ public addr = [2607:f298:4:2243::6231]
+
+[osd.68]
+ host = cephstore6231
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6231]
+ public addr = [2607:f298:4:2243::6231]
+
+[osd.69]
+ host = cephstore6231
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6231]
+ public addr = [2607:f298:4:2243::6231]
+
+[osd.70]
+ host = cephstore6231
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6231]
+ public addr = [2607:f298:4:2243::6231]
+
+[osd.71]
+ host = cephstore6232
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6232]
+ public addr = [2607:f298:4:2243::6232]
+
+[osd.72]
+ host = cephstore6232
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6232]
+ public addr = [2607:f298:4:2243::6232]
+
+[osd.73]
+ host = cephstore6232
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6232]
+ public addr = [2607:f298:4:2243::6232]
+
+[osd.74]
+ host = cephstore6232
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6232]
+ public addr = [2607:f298:4:2243::6232]
+
+[osd.75]
+ host = cephstore6232
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6232]
+ public addr = [2607:f298:4:2243::6232]
+
+[osd.76]
+ host = cephstore6232
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6232]
+ public addr = [2607:f298:4:2243::6232]
+
+[osd.77]
+ host = cephstore6232
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6232]
+ public addr = [2607:f298:4:2243::6232]
+
+[osd.78]
+ host = cephstore6233
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6233]
+ public addr = [2607:f298:4:2243::6233]
+
+[osd.79]
+ host = cephstore6233
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6233]
+ public addr = [2607:f298:4:2243::6233]
+
+[osd.80]
+ host = cephstore6233
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6233]
+ public addr = [2607:f298:4:2243::6233]
+
+[osd.81]
+ host = cephstore6233
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6233]
+ public addr = [2607:f298:4:2243::6233]
+
+[osd.82]
+ host = cephstore6233
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6233]
+ public addr = [2607:f298:4:2243::6233]
+
+[osd.83]
+ host = cephstore6233
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6233]
+ public addr = [2607:f298:4:2243::6233]
+
+[osd.84]
+ host = cephstore6233
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6233]
+ public addr = [2607:f298:4:2243::6233]
+
+[osd.85]
+ host = cephstore6234
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6234]
+ public addr = [2607:f298:4:2243::6234]
+
+[osd.86]
+ host = cephstore6234
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6234]
+ public addr = [2607:f298:4:2243::6234]
+
+[osd.87]
+ host = cephstore6234
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6234]
+ public addr = [2607:f298:4:2243::6234]
+
+[osd.88]
+ host = cephstore6234
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6234]
+ public addr = [2607:f298:4:2243::6234]
+
+[osd.89]
+ host = cephstore6234
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6234]
+ public addr = [2607:f298:4:2243::6234]
+
+[osd.90]
+ host = cephstore6234
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6234]
+ public addr = [2607:f298:4:2243::6234]
+
+[osd.91]
+ host = cephstore6234
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6234]
+ public addr = [2607:f298:4:2243::6234]
+
+[osd.92]
+ host = cephstore6235
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6235]
+ public addr = [2607:f298:4:2243::6235]
+
+[osd.93]
+ host = cephstore6235
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6235]
+ public addr = [2607:f298:4:2243::6235]
+
+[osd.94]
+ host = cephstore6235
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6235]
+ public addr = [2607:f298:4:2243::6235]
+
+[osd.95]
+ host = cephstore6235
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6235]
+ public addr = [2607:f298:4:2243::6235]
+
+[osd.96]
+ host = cephstore6235
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6235]
+ public addr = [2607:f298:4:2243::6235]
+
+[osd.97]
+ host = cephstore6235
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6235]
+ public addr = [2607:f298:4:2243::6235]
+
+[osd.98]
+ host = cephstore6235
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6235]
+ public addr = [2607:f298:4:2243::6235]
+
+[osd.99]
+ host = cephstore6236
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6236]
+ public addr = [2607:f298:4:2243::6236]
+
+[osd.100]
+ host = cephstore6236
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6236]
+ public addr = [2607:f298:4:2243::6236]
+
+[osd.101]
+ host = cephstore6236
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6236]
+ public addr = [2607:f298:4:2243::6236]
+
+[osd.102]
+ host = cephstore6236
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6236]
+ public addr = [2607:f298:4:2243::6236]
+
+[osd.103]
+ host = cephstore6236
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6236]
+ public addr = [2607:f298:4:2243::6236]
+
+[osd.104]
+ host = cephstore6236
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6236]
+ public addr = [2607:f298:4:2243::6236]
+
+[osd.105]
+ host = cephstore6236
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6236]
+ public addr = [2607:f298:4:2243::6236]
+
+[osd.106]
+ host = cephstore6237
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6237]
+ public addr = [2607:f298:4:2243::6237]
+
+[osd.107]
+ host = cephstore6237
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6237]
+ public addr = [2607:f298:4:2243::6237]
+
+[osd.108]
+ host = cephstore6237
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6237]
+ public addr = [2607:f298:4:2243::6237]
+
+[osd.109]
+ host = cephstore6237
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6237]
+ public addr = [2607:f298:4:2243::6237]
+
+[osd.110]
+ host = cephstore6237
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6237]
+ public addr = [2607:f298:4:2243::6237]
+
+[osd.111]
+ host = cephstore6237
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6237]
+ public addr = [2607:f298:4:2243::6237]
+
+[osd.112]
+ host = cephstore6237
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6237]
+ public addr = [2607:f298:4:2243::6237]
+
+[osd.113]
+ host = cephstore6238
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6238]
+ public addr = [2607:f298:4:2243::6238]
+
+[osd.114]
+ host = cephstore6238
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6238]
+ public addr = [2607:f298:4:2243::6238]
+
+[osd.115]
+ host = cephstore6238
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6238]
+ public addr = [2607:f298:4:2243::6238]
+
+[osd.116]
+ host = cephstore6238
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6238]
+ public addr = [2607:f298:4:2243::6238]
+
+[osd.117]
+ host = cephstore6238
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6238]
+ public addr = [2607:f298:4:2243::6238]
+
+[osd.118]
+ host = cephstore6238
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6238]
+ public addr = [2607:f298:4:2243::6238]
+
+[osd.119]
+ host = cephstore6238
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6238]
+ public addr = [2607:f298:4:2243::6238]
+
+[osd.120]
+ host = cephstore6239
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6239]
+ public addr = [2607:f298:4:2243::6239]
+
+[osd.121]
+ host = cephstore6239
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6239]
+ public addr = [2607:f298:4:2243::6239]
+
+[osd.122]
+ host = cephstore6239
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6239]
+ public addr = [2607:f298:4:2243::6239]
+
+[osd.123]
+ host = cephstore6239
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6239]
+ public addr = [2607:f298:4:2243::6239]
+
+[osd.124]
+ host = cephstore6239
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6239]
+ public addr = [2607:f298:4:2243::6239]
+
+[osd.125]
+ host = cephstore6239
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6239]
+ public addr = [2607:f298:4:2243::6239]
+
+[osd.126]
+ host = cephstore6239
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6239]
+ public addr = [2607:f298:4:2243::6239]
+
+[osd.127]
+ host = cephstore6240
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6240]
+ public addr = [2607:f298:4:2243::6240]
+
+[osd.128]
+ host = cephstore6240
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6240]
+ public addr = [2607:f298:4:2243::6240]
+
+[osd.129]
+ host = cephstore6240
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6240]
+ public addr = [2607:f298:4:2243::6240]
+
+[osd.130]
+ host = cephstore6240
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6240]
+ public addr = [2607:f298:4:2243::6240]
+
+[osd.131]
+ host = cephstore6240
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6240]
+ public addr = [2607:f298:4:2243::6240]
+
+[osd.132]
+ host = cephstore6240
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6240]
+ public addr = [2607:f298:4:2243::6240]
+
+[osd.133]
+ host = cephstore6240
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6240]
+ public addr = [2607:f298:4:2243::6240]
+
+[osd.134]
+ host = cephstore6241
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6241]
+ public addr = [2607:f298:4:2243::6241]
+
+[osd.135]
+ host = cephstore6241
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6241]
+ public addr = [2607:f298:4:2243::6241]
+
+[osd.136]
+ host = cephstore6241
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6241]
+ public addr = [2607:f298:4:2243::6241]
+
+[osd.137]
+ host = cephstore6241
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6241]
+ public addr = [2607:f298:4:2243::6241]
+
+[osd.138]
+ host = cephstore6241
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6241]
+ public addr = [2607:f298:4:2243::6241]
+
+[osd.139]
+ host = cephstore6241
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6241]
+ public addr = [2607:f298:4:2243::6241]
+
+[osd.140]
+ host = cephstore6241
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6241]
+ public addr = [2607:f298:4:2243::6241]
+
+[osd.141]
+ host = cephstore6242
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6242]
+ public addr = [2607:f298:4:2243::6242]
+
+[osd.142]
+ host = cephstore6242
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6242]
+ public addr = [2607:f298:4:2243::6242]
+
+[osd.143]
+ host = cephstore6242
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6242]
+ public addr = [2607:f298:4:2243::6242]
+
+[osd.144]
+ host = cephstore6242
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6242]
+ public addr = [2607:f298:4:2243::6242]
+
+[osd.145]
+ host = cephstore6242
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6242]
+ public addr = [2607:f298:4:2243::6242]
+
+[osd.146]
+ host = cephstore6242
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6242]
+ public addr = [2607:f298:4:2243::6242]
+
+[osd.147]
+ host = cephstore6242
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6242]
+ public addr = [2607:f298:4:2243::6242]
+
+[osd.148]
+ host = cephstore6243
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6243]
+ public addr = [2607:f298:4:2243::6243]
+
+[osd.149]
+ host = cephstore6243
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6243]
+ public addr = [2607:f298:4:2243::6243]
+
+[osd.150]
+ host = cephstore6243
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6243]
+ public addr = [2607:f298:4:2243::6243]
+
+[osd.151]
+ host = cephstore6243
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6243]
+ public addr = [2607:f298:4:2243::6243]
+
+[osd.152]
+ host = cephstore6243
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6243]
+ public addr = [2607:f298:4:2243::6243]
+
+[osd.153]
+ host = cephstore6243
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6243]
+ public addr = [2607:f298:4:2243::6243]
+
+[osd.154]
+ host = cephstore6243
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6243]
+ public addr = [2607:f298:4:2243::6243]
+
+[osd.155]
+ host = cephstore6244
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6244]
+ public addr = [2607:f298:4:2243::6244]
+
+[osd.156]
+ host = cephstore6244
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6244]
+ public addr = [2607:f298:4:2243::6244]
+
+[osd.157]
+ host = cephstore6244
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6244]
+ public addr = [2607:f298:4:2243::6244]
+
+[osd.158]
+ host = cephstore6244
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6244]
+ public addr = [2607:f298:4:2243::6244]
+
+[osd.159]
+ host = cephstore6244
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6244]
+ public addr = [2607:f298:4:2243::6244]
+
+[osd.160]
+ host = cephstore6244
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6244]
+ public addr = [2607:f298:4:2243::6244]
+
+[osd.161]
+ host = cephstore6244
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6244]
+ public addr = [2607:f298:4:2243::6244]
+
+[osd.162]
+ host = cephstore6245
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6245]
+ public addr = [2607:f298:4:2243::6245]
+
+[osd.163]
+ host = cephstore6245
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6245]
+ public addr = [2607:f298:4:2243::6245]
+
+[osd.164]
+ host = cephstore6245
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6245]
+ public addr = [2607:f298:4:2243::6245]
+
+[osd.165]
+ host = cephstore6245
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6245]
+ public addr = [2607:f298:4:2243::6245]
+
+[osd.166]
+ host = cephstore6245
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6245]
+ public addr = [2607:f298:4:2243::6245]
+
+[osd.167]
+ host = cephstore6245
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6245]
+ public addr = [2607:f298:4:2243::6245]
+
+[osd.168]
+ host = cephstore6245
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6245]
+ public addr = [2607:f298:4:2243::6245]
+
+[osd.169]
+ host = cephstore6246
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6246]
+ public addr = [2607:f298:4:2243::6246]
+
+[osd.170]
+ host = cephstore6246
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6246]
+ public addr = [2607:f298:4:2243::6246]
+
+[osd.171]
+ host = cephstore6246
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6246]
+ public addr = [2607:f298:4:2243::6246]
+
+[osd.172]
+ host = cephstore6246
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6246]
+ public addr = [2607:f298:4:2243::6246]
+
+[osd.173]
+ host = cephstore6246
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6246]
+ public addr = [2607:f298:4:2243::6246]
+
+[osd.174]
+ host = cephstore6246
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6246]
+ public addr = [2607:f298:4:2243::6246]
+
+[osd.175]
+ host = cephstore6246
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6246]
+ public addr = [2607:f298:4:2243::6246]
+
+[osd.176]
+ host = cephstore6336
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6336]
+ public addr = [2607:f298:4:2243::6336]
+
+[osd.177]
+ host = cephstore6336
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6336]
+ public addr = [2607:f298:4:2243::6336]
+
+[osd.178]
+ host = cephstore6336
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6336]
+ public addr = [2607:f298:4:2243::6336]
+
+[osd.179]
+ host = cephstore6336
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6336]
+ public addr = [2607:f298:4:2243::6336]
+
+[osd.180]
+ host = cephstore6336
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6336]
+ public addr = [2607:f298:4:2243::6336]
+
+[osd.181]
+ host = cephstore6336
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6336]
+ public addr = [2607:f298:4:2243::6336]
+
+[osd.182]
+ host = cephstore6336
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6336]
+ public addr = [2607:f298:4:2243::6336]
+
+[osd.183]
+ host = cephstore6337
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6337]
+ public addr = [2607:f298:4:2243::6337]
+
+[osd.184]
+ host = cephstore6337
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6337]
+ public addr = [2607:f298:4:2243::6337]
+
+[osd.185]
+ host = cephstore6337
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6337]
+ public addr = [2607:f298:4:2243::6337]
+
+[osd.186]
+ host = cephstore6337
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6337]
+ public addr = [2607:f298:4:2243::6337]
+
+[osd.187]
+ host = cephstore6337
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6337]
+ public addr = [2607:f298:4:2243::6337]
+
+[osd.188]
+ host = cephstore6337
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6337]
+ public addr = [2607:f298:4:2243::6337]
+
+[osd.189]
+ host = cephstore6337
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6337]
+ public addr = [2607:f298:4:2243::6337]
+
+[osd.190]
+ host = cephstore6338
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6338]
+ public addr = [2607:f298:4:2243::6338]
+
+[osd.191]
+ host = cephstore6338
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6338]
+ public addr = [2607:f298:4:2243::6338]
+
+[osd.192]
+ host = cephstore6338
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6338]
+ public addr = [2607:f298:4:2243::6338]
+
+[osd.193]
+ host = cephstore6338
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6338]
+ public addr = [2607:f298:4:2243::6338]
+
+[osd.194]
+ host = cephstore6338
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6338]
+ public addr = [2607:f298:4:2243::6338]
+
+[osd.195]
+ host = cephstore6338
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6338]
+ public addr = [2607:f298:4:2243::6338]
+
+[osd.196]
+ host = cephstore6338
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6338]
+ public addr = [2607:f298:4:2243::6338]
+
+[osd.197]
+ host = cephstore6339
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6339]
+ public addr = [2607:f298:4:2243::6339]
+
+[osd.198]
+ host = cephstore6339
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6339]
+ public addr = [2607:f298:4:2243::6339]
+
+[osd.199]
+ host = cephstore6339
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6339]
+ public addr = [2607:f298:4:2243::6339]
+
+[osd.200]
+ host = cephstore6339
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6339]
+ public addr = [2607:f298:4:2243::6339]
+
+[osd.201]
+ host = cephstore6339
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6339]
+ public addr = [2607:f298:4:2243::6339]
+
+[osd.202]
+ host = cephstore6339
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6339]
+ public addr = [2607:f298:4:2243::6339]
+
+[osd.203]
+ host = cephstore6339
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6339]
+ public addr = [2607:f298:4:2243::6339]
+
+[osd.204]
+ host = cephstore6340
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6340]
+ public addr = [2607:f298:4:2243::6340]
+
+[osd.205]
+ host = cephstore6340
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6340]
+ public addr = [2607:f298:4:2243::6340]
+
+[osd.206]
+ host = cephstore6340
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6340]
+ public addr = [2607:f298:4:2243::6340]
+
+[osd.207]
+ host = cephstore6340
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6340]
+ public addr = [2607:f298:4:2243::6340]
+
+[osd.208]
+ host = cephstore6340
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6340]
+ public addr = [2607:f298:4:2243::6340]
+
+[osd.209]
+ host = cephstore6340
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6340]
+ public addr = [2607:f298:4:2243::6340]
+
+[osd.210]
+ host = cephstore6340
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6340]
+ public addr = [2607:f298:4:2243::6340]
+
+[osd.211]
+ host = cephstore6341
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6341]
+ public addr = [2607:f298:4:2243::6341]
+
+[osd.212]
+ host = cephstore6341
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6341]
+ public addr = [2607:f298:4:2243::6341]
+
+[osd.213]
+ host = cephstore6341
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6341]
+ public addr = [2607:f298:4:2243::6341]
+
+[osd.214]
+ host = cephstore6341
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6341]
+ public addr = [2607:f298:4:2243::6341]
+
+[osd.215]
+ host = cephstore6341
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6341]
+ public addr = [2607:f298:4:2243::6341]
+
+[osd.216]
+ host = cephstore6341
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6341]
+ public addr = [2607:f298:4:2243::6341]
+
+[osd.217]
+ host = cephstore6341
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6341]
+ public addr = [2607:f298:4:2243::6341]
+
+[osd.218]
+ host = cephstore6342
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6342]
+ public addr = [2607:f298:4:2243::6342]
+
+[osd.219]
+ host = cephstore6342
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6342]
+ public addr = [2607:f298:4:2243::6342]
+
+[osd.220]
+ host = cephstore6342
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6342]
+ public addr = [2607:f298:4:2243::6342]
+
+[osd.221]
+ host = cephstore6342
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6342]
+ public addr = [2607:f298:4:2243::6342]
+
+[osd.222]
+ host = cephstore6342
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6342]
+ public addr = [2607:f298:4:2243::6342]
+
+[osd.223]
+ host = cephstore6342
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6342]
+ public addr = [2607:f298:4:2243::6342]
+
+[osd.224]
+ host = cephstore6342
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6342]
+ public addr = [2607:f298:4:2243::6342]
+
+[osd.225]
+ host = cephstore6343
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6343]
+ public addr = [2607:f298:4:2243::6343]
+
+[osd.226]
+ host = cephstore6343
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6343]
+ public addr = [2607:f298:4:2243::6343]
+
+[osd.227]
+ host = cephstore6343
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6343]
+ public addr = [2607:f298:4:2243::6343]
+
+[osd.228]
+ host = cephstore6343
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6343]
+ public addr = [2607:f298:4:2243::6343]
+
+[osd.229]
+ host = cephstore6343
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6343]
+ public addr = [2607:f298:4:2243::6343]
+
+[osd.230]
+ host = cephstore6343
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6343]
+ public addr = [2607:f298:4:2243::6343]
+
+[osd.231]
+ host = cephstore6343
+ rack = irv-n1
+ cluster addr = [2607:f298:4:3243::6343]
+ public addr = [2607:f298:4:2243::6343]
+
+[osd.232]
+ host = cephstore6345
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6345]
+ public addr = [2607:f298:4:2243::6345]
+
+[osd.233]
+ host = cephstore6345
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6345]
+ public addr = [2607:f298:4:2243::6345]
+
+[osd.234]
+ host = cephstore6345
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6345]
+ public addr = [2607:f298:4:2243::6345]
+
+[osd.235]
+ host = cephstore6345
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6345]
+ public addr = [2607:f298:4:2243::6345]
+
+[osd.236]
+ host = cephstore6345
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6345]
+ public addr = [2607:f298:4:2243::6345]
+
+[osd.237]
+ host = cephstore6345
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6345]
+ public addr = [2607:f298:4:2243::6345]
+
+[osd.238]
+ host = cephstore6345
+ rack = irv-n2
+ cluster addr = [2607:f298:4:3243::6345]
+ public addr = [2607:f298:4:2243::6345]
+
diff --git a/src/test/cli/osdmaptool/clobber.t b/src/test/cli/osdmaptool/clobber.t
new file mode 100644
index 000000000..146960693
--- /dev/null
+++ b/src/test/cli/osdmaptool/clobber.t
@@ -0,0 +1,65 @@
+ $ osdmaptool --createsimple 3 myosdmap --with-default-pool
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: writing epoch 1 to myosdmap
+
+ $ ORIG_FSID="$(osdmaptool --print myosdmap|grep ^fsid)"
+ osdmaptool: osdmap file 'myosdmap'
+
+ $ osdmaptool --createsimple 3 myosdmap --with-default-pool
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: myosdmap exists, --clobber to overwrite
+ [255]
+
+# hasn't changed yet
+#TODO typo
+ $ osdmaptool --print myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ epoch 1
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ flags
+ crush_version 1
+ full_ratio 0
+ backfillfull_ratio 0
+ nearfull_ratio 0
+ min_compat_client jewel
+ stretch_mode_enabled false
+
+ pool 1 'rbd' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 192 pgp_num 192 autoscale_mode on last_change 0 flags hashpspool stripe_width 0 application rbd
+
+ max_osd 3
+
+
+ $ NEW_FSID="$(osdmaptool --print myosdmap|grep ^fsid)"
+ osdmaptool: osdmap file 'myosdmap'
+ $ [ "$ORIG_FSID" = "$NEW_FSID" ]
+
+ $ osdmaptool --createsimple 1 --clobber myosdmap --with-default-pool
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: writing epoch 1 to myosdmap
+
+ $ osdmaptool --print myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ epoch 1
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ flags
+ crush_version 1
+ full_ratio 0
+ backfillfull_ratio 0
+ nearfull_ratio 0
+ min_compat_client jewel
+ stretch_mode_enabled false
+
+ pool 1 'rbd' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 64 pgp_num 64 autoscale_mode on last_change 0 flags hashpspool stripe_width 0 application rbd
+
+ max_osd 1
+
+
+ $ NEW_FSID="$(osdmaptool --print myosdmap|grep ^fsid)"
+ osdmaptool: osdmap file 'myosdmap'
+#TODO --clobber should probably set new fsid, remove the [1]
+ $ [ "$ORIG_FSID" != "$NEW_FSID" ]
+ [1]
diff --git a/src/test/cli/osdmaptool/create-print.t b/src/test/cli/osdmaptool/create-print.t
new file mode 100644
index 000000000..9d745b82f
--- /dev/null
+++ b/src/test/cli/osdmaptool/create-print.t
@@ -0,0 +1,97 @@
+ $ osdmaptool --createsimple 3 myosdmap --with-default-pool
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: writing epoch 1 to myosdmap
+
+ $ osdmaptool --export-crush oc myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: exported crush map to oc
+ $ crushtool --decompile oc
+ # begin crush map
+ tunable choose_local_tries 0
+ tunable choose_local_fallback_tries 0
+ tunable choose_total_tries 50
+ tunable chooseleaf_descend_once 1
+ tunable chooseleaf_vary_r 1
+ tunable chooseleaf_stable 1
+ tunable straw_calc_version 1
+ tunable allowed_bucket_algs 54
+
+ # devices
+ device 0 osd.0
+ device 1 osd.1
+ device 2 osd.2
+
+ # types
+ type 0 osd
+ type 1 host
+ type 2 chassis
+ type 3 rack
+ type 4 row
+ type 5 pdu
+ type 6 pod
+ type 7 room
+ type 8 datacenter
+ type 9 zone
+ type 10 region
+ type 11 root
+
+ # buckets
+ host localhost {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \t# weight 3.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.0 weight 1.00000 (esc)
+ \titem osd.1 weight 1.00000 (esc)
+ \titem osd.2 weight 1.00000 (esc)
+ }
+ rack localrack {
+ \tid -3\t\t# do not change unnecessarily (esc)
+ \t# weight 3.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem localhost weight 3.00000 (esc)
+ }
+ root default {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \t# weight 3.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem localrack weight 3.00000 (esc)
+ }
+
+ # rules
+ rule replicated_rule {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take default (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+ $ osdmaptool --print myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ epoch 1
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ flags
+ crush_version 1
+ full_ratio 0
+ backfillfull_ratio 0
+ nearfull_ratio 0
+ min_compat_client jewel
+ stretch_mode_enabled false
+
+ pool 1 'rbd' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 192 pgp_num 192 autoscale_mode on last_change 0 flags hashpspool stripe_width 0 application rbd
+
+ max_osd 3
+
+ $ osdmaptool --clobber --createsimple 3 --with-default-pool myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: writing epoch 1 to myosdmap
+ $ osdmaptool --print myosdmap | grep 'pool 1'
+ osdmaptool: osdmap file 'myosdmap'
+ pool 1 'rbd' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 192 pgp_num 192 autoscale_mode on last_change 0 flags hashpspool stripe_width 0 application rbd
+ $ rm -f myosdmap
diff --git a/src/test/cli/osdmaptool/create-racks.t b/src/test/cli/osdmaptool/create-racks.t
new file mode 100644
index 000000000..d1e65d7b5
--- /dev/null
+++ b/src/test/cli/osdmaptool/create-racks.t
@@ -0,0 +1,810 @@
+ $ osdmaptool --create-from-conf om -c $TESTDIR/ceph.conf.withracks --with-default-pool
+ osdmaptool: osdmap file 'om'
+ osdmaptool: writing epoch 1 to om
+ $ osdmaptool --export-crush oc om
+ osdmaptool: osdmap file 'om'
+ osdmaptool: exported crush map to oc
+ $ crushtool --decompile oc
+ # begin crush map
+ tunable choose_local_tries 0
+ tunable choose_local_fallback_tries 0
+ tunable choose_total_tries 50
+ tunable chooseleaf_descend_once 1
+ tunable chooseleaf_vary_r 1
+ tunable chooseleaf_stable 1
+ tunable straw_calc_version 1
+ tunable allowed_bucket_algs 54
+
+ # devices
+ device 1 osd.1
+ device 2 osd.2
+ device 3 osd.3
+ device 4 osd.4
+ device 5 osd.5
+ device 6 osd.6
+ device 7 osd.7
+ device 8 osd.8
+ device 9 osd.9
+ device 10 osd.10
+ device 11 osd.11
+ device 12 osd.12
+ device 13 osd.13
+ device 14 osd.14
+ device 15 osd.15
+ device 16 osd.16
+ device 17 osd.17
+ device 18 osd.18
+ device 19 osd.19
+ device 20 osd.20
+ device 21 osd.21
+ device 22 osd.22
+ device 23 osd.23
+ device 24 osd.24
+ device 25 osd.25
+ device 26 osd.26
+ device 27 osd.27
+ device 28 osd.28
+ device 29 osd.29
+ device 30 osd.30
+ device 31 osd.31
+ device 32 osd.32
+ device 33 osd.33
+ device 34 osd.34
+ device 35 osd.35
+ device 36 osd.36
+ device 37 osd.37
+ device 38 osd.38
+ device 39 osd.39
+ device 40 osd.40
+ device 41 osd.41
+ device 42 osd.42
+ device 43 osd.43
+ device 44 osd.44
+ device 45 osd.45
+ device 46 osd.46
+ device 47 osd.47
+ device 48 osd.48
+ device 49 osd.49
+ device 50 osd.50
+ device 51 osd.51
+ device 52 osd.52
+ device 53 osd.53
+ device 54 osd.54
+ device 55 osd.55
+ device 56 osd.56
+ device 57 osd.57
+ device 58 osd.58
+ device 59 osd.59
+ device 60 osd.60
+ device 61 osd.61
+ device 62 osd.62
+ device 63 osd.63
+ device 64 osd.64
+ device 65 osd.65
+ device 66 osd.66
+ device 67 osd.67
+ device 68 osd.68
+ device 69 osd.69
+ device 70 osd.70
+ device 71 osd.71
+ device 72 osd.72
+ device 73 osd.73
+ device 74 osd.74
+ device 75 osd.75
+ device 76 osd.76
+ device 77 osd.77
+ device 78 osd.78
+ device 79 osd.79
+ device 80 osd.80
+ device 81 osd.81
+ device 82 osd.82
+ device 83 osd.83
+ device 84 osd.84
+ device 85 osd.85
+ device 86 osd.86
+ device 87 osd.87
+ device 88 osd.88
+ device 89 osd.89
+ device 90 osd.90
+ device 91 osd.91
+ device 92 osd.92
+ device 93 osd.93
+ device 94 osd.94
+ device 95 osd.95
+ device 96 osd.96
+ device 97 osd.97
+ device 98 osd.98
+ device 99 osd.99
+ device 100 osd.100
+ device 101 osd.101
+ device 102 osd.102
+ device 103 osd.103
+ device 104 osd.104
+ device 105 osd.105
+ device 106 osd.106
+ device 107 osd.107
+ device 108 osd.108
+ device 109 osd.109
+ device 110 osd.110
+ device 111 osd.111
+ device 112 osd.112
+ device 113 osd.113
+ device 114 osd.114
+ device 115 osd.115
+ device 116 osd.116
+ device 117 osd.117
+ device 118 osd.118
+ device 119 osd.119
+ device 120 osd.120
+ device 121 osd.121
+ device 122 osd.122
+ device 123 osd.123
+ device 124 osd.124
+ device 125 osd.125
+ device 126 osd.126
+ device 127 osd.127
+ device 128 osd.128
+ device 129 osd.129
+ device 130 osd.130
+ device 131 osd.131
+ device 132 osd.132
+ device 133 osd.133
+ device 134 osd.134
+ device 135 osd.135
+ device 136 osd.136
+ device 137 osd.137
+ device 138 osd.138
+ device 139 osd.139
+ device 140 osd.140
+ device 141 osd.141
+ device 142 osd.142
+ device 143 osd.143
+ device 144 osd.144
+ device 145 osd.145
+ device 146 osd.146
+ device 147 osd.147
+ device 148 osd.148
+ device 149 osd.149
+ device 150 osd.150
+ device 151 osd.151
+ device 152 osd.152
+ device 153 osd.153
+ device 154 osd.154
+ device 155 osd.155
+ device 156 osd.156
+ device 157 osd.157
+ device 158 osd.158
+ device 159 osd.159
+ device 160 osd.160
+ device 161 osd.161
+ device 162 osd.162
+ device 163 osd.163
+ device 164 osd.164
+ device 165 osd.165
+ device 166 osd.166
+ device 167 osd.167
+ device 168 osd.168
+ device 169 osd.169
+ device 170 osd.170
+ device 171 osd.171
+ device 172 osd.172
+ device 173 osd.173
+ device 174 osd.174
+ device 175 osd.175
+ device 176 osd.176
+ device 177 osd.177
+ device 178 osd.178
+ device 179 osd.179
+ device 180 osd.180
+ device 181 osd.181
+ device 182 osd.182
+ device 183 osd.183
+ device 184 osd.184
+ device 185 osd.185
+ device 186 osd.186
+ device 187 osd.187
+ device 188 osd.188
+ device 189 osd.189
+ device 190 osd.190
+ device 191 osd.191
+ device 192 osd.192
+ device 193 osd.193
+ device 194 osd.194
+ device 195 osd.195
+ device 196 osd.196
+ device 197 osd.197
+ device 198 osd.198
+ device 199 osd.199
+ device 200 osd.200
+ device 201 osd.201
+ device 202 osd.202
+ device 203 osd.203
+ device 204 osd.204
+ device 205 osd.205
+ device 206 osd.206
+ device 207 osd.207
+ device 208 osd.208
+ device 209 osd.209
+ device 210 osd.210
+ device 211 osd.211
+ device 212 osd.212
+ device 213 osd.213
+ device 214 osd.214
+ device 215 osd.215
+ device 216 osd.216
+ device 217 osd.217
+ device 218 osd.218
+ device 219 osd.219
+ device 220 osd.220
+ device 221 osd.221
+ device 222 osd.222
+ device 223 osd.223
+ device 224 osd.224
+ device 225 osd.225
+ device 226 osd.226
+ device 227 osd.227
+ device 228 osd.228
+ device 229 osd.229
+ device 230 osd.230
+ device 231 osd.231
+ device 232 osd.232
+ device 233 osd.233
+ device 234 osd.234
+ device 235 osd.235
+ device 236 osd.236
+ device 237 osd.237
+ device 238 osd.238
+
+ # types
+ type 0 osd
+ type 1 host
+ type 2 chassis
+ type 3 rack
+ type 4 row
+ type 5 pdu
+ type 6 pod
+ type 7 room
+ type 8 datacenter
+ type 9 zone
+ type 10 region
+ type 11 root
+
+ # buckets
+ host cephstore5522 {
+ \tid -2\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.1 weight 1.00000 (esc)
+ \titem osd.2 weight 1.00000 (esc)
+ \titem osd.3 weight 1.00000 (esc)
+ \titem osd.4 weight 1.00000 (esc)
+ \titem osd.5 weight 1.00000 (esc)
+ \titem osd.6 weight 1.00000 (esc)
+ \titem osd.7 weight 1.00000 (esc)
+ }
+ host cephstore5523 {
+ \tid -4\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.10 weight 1.00000 (esc)
+ \titem osd.11 weight 1.00000 (esc)
+ \titem osd.12 weight 1.00000 (esc)
+ \titem osd.13 weight 1.00000 (esc)
+ \titem osd.14 weight 1.00000 (esc)
+ \titem osd.8 weight 1.00000 (esc)
+ \titem osd.9 weight 1.00000 (esc)
+ }
+ host cephstore6238 {
+ \tid -8\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.113 weight 1.00000 (esc)
+ \titem osd.114 weight 1.00000 (esc)
+ \titem osd.115 weight 1.00000 (esc)
+ \titem osd.116 weight 1.00000 (esc)
+ \titem osd.117 weight 1.00000 (esc)
+ \titem osd.118 weight 1.00000 (esc)
+ \titem osd.119 weight 1.00000 (esc)
+ }
+ host cephstore6240 {
+ \tid -10\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.127 weight 1.00000 (esc)
+ \titem osd.128 weight 1.00000 (esc)
+ \titem osd.129 weight 1.00000 (esc)
+ \titem osd.130 weight 1.00000 (esc)
+ \titem osd.131 weight 1.00000 (esc)
+ \titem osd.132 weight 1.00000 (esc)
+ \titem osd.133 weight 1.00000 (esc)
+ }
+ host cephstore6242 {
+ \tid -12\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.141 weight 1.00000 (esc)
+ \titem osd.142 weight 1.00000 (esc)
+ \titem osd.143 weight 1.00000 (esc)
+ \titem osd.144 weight 1.00000 (esc)
+ \titem osd.145 weight 1.00000 (esc)
+ \titem osd.146 weight 1.00000 (esc)
+ \titem osd.147 weight 1.00000 (esc)
+ }
+ host cephstore5524 {
+ \tid -14\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.15 weight 1.00000 (esc)
+ \titem osd.16 weight 1.00000 (esc)
+ \titem osd.17 weight 1.00000 (esc)
+ \titem osd.18 weight 1.00000 (esc)
+ \titem osd.19 weight 1.00000 (esc)
+ \titem osd.20 weight 1.00000 (esc)
+ \titem osd.21 weight 1.00000 (esc)
+ }
+ host cephstore6244 {
+ \tid -15\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.155 weight 1.00000 (esc)
+ \titem osd.156 weight 1.00000 (esc)
+ \titem osd.157 weight 1.00000 (esc)
+ \titem osd.158 weight 1.00000 (esc)
+ \titem osd.159 weight 1.00000 (esc)
+ \titem osd.160 weight 1.00000 (esc)
+ \titem osd.161 weight 1.00000 (esc)
+ }
+ host cephstore6246 {
+ \tid -17\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.169 weight 1.00000 (esc)
+ \titem osd.170 weight 1.00000 (esc)
+ \titem osd.171 weight 1.00000 (esc)
+ \titem osd.172 weight 1.00000 (esc)
+ \titem osd.173 weight 1.00000 (esc)
+ \titem osd.174 weight 1.00000 (esc)
+ \titem osd.175 weight 1.00000 (esc)
+ }
+ host cephstore6337 {
+ \tid -19\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.183 weight 1.00000 (esc)
+ \titem osd.184 weight 1.00000 (esc)
+ \titem osd.185 weight 1.00000 (esc)
+ \titem osd.186 weight 1.00000 (esc)
+ \titem osd.187 weight 1.00000 (esc)
+ \titem osd.188 weight 1.00000 (esc)
+ \titem osd.189 weight 1.00000 (esc)
+ }
+ host cephstore6341 {
+ \tid -23\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.211 weight 1.00000 (esc)
+ \titem osd.212 weight 1.00000 (esc)
+ \titem osd.213 weight 1.00000 (esc)
+ \titem osd.214 weight 1.00000 (esc)
+ \titem osd.215 weight 1.00000 (esc)
+ \titem osd.216 weight 1.00000 (esc)
+ \titem osd.217 weight 1.00000 (esc)
+ }
+ host cephstore6342 {
+ \tid -24\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.218 weight 1.00000 (esc)
+ \titem osd.219 weight 1.00000 (esc)
+ \titem osd.220 weight 1.00000 (esc)
+ \titem osd.221 weight 1.00000 (esc)
+ \titem osd.222 weight 1.00000 (esc)
+ \titem osd.223 weight 1.00000 (esc)
+ \titem osd.224 weight 1.00000 (esc)
+ }
+ host cephstore5525 {
+ \tid -25\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.22 weight 1.00000 (esc)
+ \titem osd.23 weight 1.00000 (esc)
+ \titem osd.24 weight 1.00000 (esc)
+ \titem osd.25 weight 1.00000 (esc)
+ \titem osd.26 weight 1.00000 (esc)
+ \titem osd.27 weight 1.00000 (esc)
+ \titem osd.28 weight 1.00000 (esc)
+ }
+ host cephstore6345 {
+ \tid -27\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.232 weight 1.00000 (esc)
+ \titem osd.233 weight 1.00000 (esc)
+ \titem osd.234 weight 1.00000 (esc)
+ \titem osd.235 weight 1.00000 (esc)
+ \titem osd.236 weight 1.00000 (esc)
+ \titem osd.237 weight 1.00000 (esc)
+ \titem osd.238 weight 1.00000 (esc)
+ }
+ host cephstore5526 {
+ \tid -28\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.29 weight 1.00000 (esc)
+ \titem osd.30 weight 1.00000 (esc)
+ \titem osd.31 weight 1.00000 (esc)
+ \titem osd.32 weight 1.00000 (esc)
+ \titem osd.33 weight 1.00000 (esc)
+ \titem osd.34 weight 1.00000 (esc)
+ \titem osd.35 weight 1.00000 (esc)
+ }
+ host cephstore5527 {
+ \tid -29\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.36 weight 1.00000 (esc)
+ \titem osd.37 weight 1.00000 (esc)
+ \titem osd.38 weight 1.00000 (esc)
+ \titem osd.39 weight 1.00000 (esc)
+ \titem osd.40 weight 1.00000 (esc)
+ \titem osd.41 weight 1.00000 (esc)
+ \titem osd.42 weight 1.00000 (esc)
+ }
+ host cephstore5529 {
+ \tid -30\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.43 weight 1.00000 (esc)
+ \titem osd.44 weight 1.00000 (esc)
+ \titem osd.45 weight 1.00000 (esc)
+ \titem osd.46 weight 1.00000 (esc)
+ \titem osd.47 weight 1.00000 (esc)
+ \titem osd.48 weight 1.00000 (esc)
+ \titem osd.49 weight 1.00000 (esc)
+ }
+ host cephstore5530 {
+ \tid -31\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.50 weight 1.00000 (esc)
+ \titem osd.51 weight 1.00000 (esc)
+ \titem osd.52 weight 1.00000 (esc)
+ \titem osd.53 weight 1.00000 (esc)
+ \titem osd.54 weight 1.00000 (esc)
+ \titem osd.55 weight 1.00000 (esc)
+ \titem osd.56 weight 1.00000 (esc)
+ }
+ rack irv-n2 {
+ \tid -3\t\t# do not change unnecessarily (esc)
+ \t# weight 119.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem cephstore5522 weight 7.00000 (esc)
+ \titem cephstore5523 weight 7.00000 (esc)
+ \titem cephstore6238 weight 7.00000 (esc)
+ \titem cephstore6240 weight 7.00000 (esc)
+ \titem cephstore6242 weight 7.00000 (esc)
+ \titem cephstore5524 weight 7.00000 (esc)
+ \titem cephstore6244 weight 7.00000 (esc)
+ \titem cephstore6246 weight 7.00000 (esc)
+ \titem cephstore6337 weight 7.00000 (esc)
+ \titem cephstore6341 weight 7.00000 (esc)
+ \titem cephstore6342 weight 7.00000 (esc)
+ \titem cephstore5525 weight 7.00000 (esc)
+ \titem cephstore6345 weight 7.00000 (esc)
+ \titem cephstore5526 weight 7.00000 (esc)
+ \titem cephstore5527 weight 7.00000 (esc)
+ \titem cephstore5529 weight 7.00000 (esc)
+ \titem cephstore5530 weight 7.00000 (esc)
+ }
+ host cephstore6236 {
+ \tid -5\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.100 weight 1.00000 (esc)
+ \titem osd.101 weight 1.00000 (esc)
+ \titem osd.102 weight 1.00000 (esc)
+ \titem osd.103 weight 1.00000 (esc)
+ \titem osd.104 weight 1.00000 (esc)
+ \titem osd.105 weight 1.00000 (esc)
+ \titem osd.99 weight 1.00000 (esc)
+ }
+ host cephstore6237 {
+ \tid -7\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.106 weight 1.00000 (esc)
+ \titem osd.107 weight 1.00000 (esc)
+ \titem osd.108 weight 1.00000 (esc)
+ \titem osd.109 weight 1.00000 (esc)
+ \titem osd.110 weight 1.00000 (esc)
+ \titem osd.111 weight 1.00000 (esc)
+ \titem osd.112 weight 1.00000 (esc)
+ }
+ host cephstore6239 {
+ \tid -9\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.120 weight 1.00000 (esc)
+ \titem osd.121 weight 1.00000 (esc)
+ \titem osd.122 weight 1.00000 (esc)
+ \titem osd.123 weight 1.00000 (esc)
+ \titem osd.124 weight 1.00000 (esc)
+ \titem osd.125 weight 1.00000 (esc)
+ \titem osd.126 weight 1.00000 (esc)
+ }
+ host cephstore6241 {
+ \tid -11\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.134 weight 1.00000 (esc)
+ \titem osd.135 weight 1.00000 (esc)
+ \titem osd.136 weight 1.00000 (esc)
+ \titem osd.137 weight 1.00000 (esc)
+ \titem osd.138 weight 1.00000 (esc)
+ \titem osd.139 weight 1.00000 (esc)
+ \titem osd.140 weight 1.00000 (esc)
+ }
+ host cephstore6243 {
+ \tid -13\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.148 weight 1.00000 (esc)
+ \titem osd.149 weight 1.00000 (esc)
+ \titem osd.150 weight 1.00000 (esc)
+ \titem osd.151 weight 1.00000 (esc)
+ \titem osd.152 weight 1.00000 (esc)
+ \titem osd.153 weight 1.00000 (esc)
+ \titem osd.154 weight 1.00000 (esc)
+ }
+ host cephstore6245 {
+ \tid -16\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.162 weight 1.00000 (esc)
+ \titem osd.163 weight 1.00000 (esc)
+ \titem osd.164 weight 1.00000 (esc)
+ \titem osd.165 weight 1.00000 (esc)
+ \titem osd.166 weight 1.00000 (esc)
+ \titem osd.167 weight 1.00000 (esc)
+ \titem osd.168 weight 1.00000 (esc)
+ }
+ host cephstore6336 {
+ \tid -18\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.176 weight 1.00000 (esc)
+ \titem osd.177 weight 1.00000 (esc)
+ \titem osd.178 weight 1.00000 (esc)
+ \titem osd.179 weight 1.00000 (esc)
+ \titem osd.180 weight 1.00000 (esc)
+ \titem osd.181 weight 1.00000 (esc)
+ \titem osd.182 weight 1.00000 (esc)
+ }
+ host cephstore6338 {
+ \tid -20\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.190 weight 1.00000 (esc)
+ \titem osd.191 weight 1.00000 (esc)
+ \titem osd.192 weight 1.00000 (esc)
+ \titem osd.193 weight 1.00000 (esc)
+ \titem osd.194 weight 1.00000 (esc)
+ \titem osd.195 weight 1.00000 (esc)
+ \titem osd.196 weight 1.00000 (esc)
+ }
+ host cephstore6339 {
+ \tid -21\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.197 weight 1.00000 (esc)
+ \titem osd.198 weight 1.00000 (esc)
+ \titem osd.199 weight 1.00000 (esc)
+ \titem osd.200 weight 1.00000 (esc)
+ \titem osd.201 weight 1.00000 (esc)
+ \titem osd.202 weight 1.00000 (esc)
+ \titem osd.203 weight 1.00000 (esc)
+ }
+ host cephstore6340 {
+ \tid -22\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.204 weight 1.00000 (esc)
+ \titem osd.205 weight 1.00000 (esc)
+ \titem osd.206 weight 1.00000 (esc)
+ \titem osd.207 weight 1.00000 (esc)
+ \titem osd.208 weight 1.00000 (esc)
+ \titem osd.209 weight 1.00000 (esc)
+ \titem osd.210 weight 1.00000 (esc)
+ }
+ host cephstore6343 {
+ \tid -26\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.225 weight 1.00000 (esc)
+ \titem osd.226 weight 1.00000 (esc)
+ \titem osd.227 weight 1.00000 (esc)
+ \titem osd.228 weight 1.00000 (esc)
+ \titem osd.229 weight 1.00000 (esc)
+ \titem osd.230 weight 1.00000 (esc)
+ \titem osd.231 weight 1.00000 (esc)
+ }
+ host cephstore6230 {
+ \tid -32\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.57 weight 1.00000 (esc)
+ \titem osd.58 weight 1.00000 (esc)
+ \titem osd.59 weight 1.00000 (esc)
+ \titem osd.60 weight 1.00000 (esc)
+ \titem osd.61 weight 1.00000 (esc)
+ \titem osd.62 weight 1.00000 (esc)
+ \titem osd.63 weight 1.00000 (esc)
+ }
+ host cephstore6231 {
+ \tid -33\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.64 weight 1.00000 (esc)
+ \titem osd.65 weight 1.00000 (esc)
+ \titem osd.66 weight 1.00000 (esc)
+ \titem osd.67 weight 1.00000 (esc)
+ \titem osd.68 weight 1.00000 (esc)
+ \titem osd.69 weight 1.00000 (esc)
+ \titem osd.70 weight 1.00000 (esc)
+ }
+ host cephstore6232 {
+ \tid -34\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.71 weight 1.00000 (esc)
+ \titem osd.72 weight 1.00000 (esc)
+ \titem osd.73 weight 1.00000 (esc)
+ \titem osd.74 weight 1.00000 (esc)
+ \titem osd.75 weight 1.00000 (esc)
+ \titem osd.76 weight 1.00000 (esc)
+ \titem osd.77 weight 1.00000 (esc)
+ }
+ host cephstore6233 {
+ \tid -35\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.78 weight 1.00000 (esc)
+ \titem osd.79 weight 1.00000 (esc)
+ \titem osd.80 weight 1.00000 (esc)
+ \titem osd.81 weight 1.00000 (esc)
+ \titem osd.82 weight 1.00000 (esc)
+ \titem osd.83 weight 1.00000 (esc)
+ \titem osd.84 weight 1.00000 (esc)
+ }
+ host cephstore6234 {
+ \tid -36\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.85 weight 1.00000 (esc)
+ \titem osd.86 weight 1.00000 (esc)
+ \titem osd.87 weight 1.00000 (esc)
+ \titem osd.88 weight 1.00000 (esc)
+ \titem osd.89 weight 1.00000 (esc)
+ \titem osd.90 weight 1.00000 (esc)
+ \titem osd.91 weight 1.00000 (esc)
+ }
+ host cephstore6235 {
+ \tid -37\t\t# do not change unnecessarily (esc)
+ \t# weight 7.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem osd.92 weight 1.00000 (esc)
+ \titem osd.93 weight 1.00000 (esc)
+ \titem osd.94 weight 1.00000 (esc)
+ \titem osd.95 weight 1.00000 (esc)
+ \titem osd.96 weight 1.00000 (esc)
+ \titem osd.97 weight 1.00000 (esc)
+ \titem osd.98 weight 1.00000 (esc)
+ }
+ rack irv-n1 {
+ \tid -6\t\t# do not change unnecessarily (esc)
+ \t# weight 119.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem cephstore6236 weight 7.00000 (esc)
+ \titem cephstore6237 weight 7.00000 (esc)
+ \titem cephstore6239 weight 7.00000 (esc)
+ \titem cephstore6241 weight 7.00000 (esc)
+ \titem cephstore6243 weight 7.00000 (esc)
+ \titem cephstore6245 weight 7.00000 (esc)
+ \titem cephstore6336 weight 7.00000 (esc)
+ \titem cephstore6338 weight 7.00000 (esc)
+ \titem cephstore6339 weight 7.00000 (esc)
+ \titem cephstore6340 weight 7.00000 (esc)
+ \titem cephstore6343 weight 7.00000 (esc)
+ \titem cephstore6230 weight 7.00000 (esc)
+ \titem cephstore6231 weight 7.00000 (esc)
+ \titem cephstore6232 weight 7.00000 (esc)
+ \titem cephstore6233 weight 7.00000 (esc)
+ \titem cephstore6234 weight 7.00000 (esc)
+ \titem cephstore6235 weight 7.00000 (esc)
+ }
+ root default {
+ \tid -1\t\t# do not change unnecessarily (esc)
+ \t# weight 238.00000 (esc)
+ \talg straw2 (esc)
+ \thash 0\t# rjenkins1 (esc)
+ \titem irv-n2 weight 119.00000 (esc)
+ \titem irv-n1 weight 119.00000 (esc)
+ }
+
+ # rules
+ rule replicated_rule {
+ \tid 0 (esc)
+ \ttype replicated (esc)
+ \tstep take default (esc)
+ \tstep chooseleaf firstn 0 type host (esc)
+ \tstep emit (esc)
+ }
+
+ # end crush map
+ $ rm oc
+ $ osdmaptool --test-map-pg 0.0 om
+ osdmaptool: osdmap file 'om'
+ parsed '0.0' -> 0.0
+ 0.0 raw ([], p-1) up ([], p-1) acting ([], p-1)
+ $ osdmaptool --print om
+ osdmaptool: osdmap file 'om'
+ epoch 1
+ fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
+ created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
+ flags
+ crush_version 1
+ full_ratio 0
+ backfillfull_ratio 0
+ nearfull_ratio 0
+ min_compat_client jewel
+ stretch_mode_enabled false
+
+ pool 1 'rbd' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 15296 pgp_num 15296 autoscale_mode on last_change 0 flags hashpspool stripe_width 0 application rbd
+
+ max_osd 239
+
+
+ $ osdmaptool --clobber --create-from-conf --with-default-pool om -c $TESTDIR/ceph.conf.withracks
+ osdmaptool: osdmap file 'om'
+ osdmaptool: writing epoch 1 to om
+ $ osdmaptool --print om | grep 'pool 1'
+ osdmaptool: osdmap file 'om'
+ pool 1 'rbd' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 15296 pgp_num 15296 autoscale_mode on last_change 0 flags hashpspool stripe_width 0 application rbd
+ $ rm -f om
diff --git a/src/test/cli/osdmaptool/crush.t b/src/test/cli/osdmaptool/crush.t
new file mode 100644
index 000000000..520f11e50
--- /dev/null
+++ b/src/test/cli/osdmaptool/crush.t
@@ -0,0 +1,17 @@
+ $ osdmaptool --createsimple 3 myosdmap --with-default-pool
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: writing epoch 1 to myosdmap
+ $ osdmaptool --export-crush oc myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: exported crush map to oc
+ $ osdmaptool --import-crush oc myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: imported 497 byte crush map from oc
+ osdmaptool: writing epoch 3 to myosdmap
+ $ osdmaptool --adjust-crush-weight 0:5 myosdmap
+ osdmaptool: osdmap file 'myosdmap'
+ Adjusted osd.0 CRUSH weight to 5
+ $ osdmaptool --adjust-crush-weight 0:5 myosdmap --save
+ osdmaptool: osdmap file 'myosdmap'
+ Adjusted osd.0 CRUSH weight to 5
+ osdmaptool: writing epoch 5 to myosdmap \ No newline at end of file
diff --git a/src/test/cli/osdmaptool/help.t b/src/test/cli/osdmaptool/help.t
new file mode 100644
index 000000000..624fe9102
--- /dev/null
+++ b/src/test/cli/osdmaptool/help.t
@@ -0,0 +1,42 @@
+# TODO be user-friendly
+ $ osdmaptool --help
+ usage: [--print] <mapfilename>
+ --create-from-conf creates an osd map with default configurations
+ --createsimple <numosd> [--clobber] [--pg-bits <bitsperosd>] [--pgp-bits <bits>] creates a relatively generic OSD map with <numosd> devices
+ --pgp-bits <bits> pgp_num map attribute will be shifted by <bits>
+ --pg-bits <bits> pg_num map attribute will be shifted by <bits>
+ --clobber allows osdmaptool to overwrite <mapfilename> if it already exists
+ --export-crush <file> write osdmap's crush map to <file>
+ --import-crush <file> replace osdmap's crush map with <file>
+ --health dump health checks
+ --test-map-pgs [--pool <poolid>] [--pg_num <pg_num>] [--range-first <first> --range-last <last>] map all pgs
+ --test-map-pgs-dump [--pool <poolid>] [--range-first <first> --range-last <last>] map all pgs
+ --test-map-pgs-dump-all [--pool <poolid>] [--range-first <first> --range-last <last>] map all pgs to osds
+ --mark-up-in mark osds up and in (but do not persist)
+ --mark-out <osdid> mark an osd as out (but do not persist)
+ --mark-up <osdid> mark an osd as up (but do not persist)
+ --mark-in <osdid> mark an osd as in (but do not persist)
+ --with-default-pool include default pool when creating map
+ --clear-temp clear pg_temp and primary_temp
+ --clean-temps clean pg_temps
+ --test-random do random placements
+ --test-map-pg <pgid> map a pgid to osds
+ --test-map-object <objectname> [--pool <poolid>] map an object to osds
+ --upmap-cleanup <file> clean up pg_upmap[_items] entries, writing
+ commands to <file> [default: - for stdout]
+ --upmap <file> calculate pg upmap entries to balance pg layout
+ writing commands to <file> [default: - for stdout]
+ --upmap-max <max-count> set max upmap entries to calculate [default: 10]
+ --upmap-deviation <max-deviation>
+ max deviation from target [default: 5]
+ --upmap-pool <poolname> restrict upmap balancing to 1 or more pools
+ --upmap-active Act like an active balancer, keep applying changes until balanced
+ --dump <format> displays the map in plain text when <format> is 'plain', 'json' if specified format is not supported
+ --tree displays a tree of the map
+ --test-crush [--range-first <first> --range-last <last>] map pgs to acting osds
+ --adjust-crush-weight <osdid:weight>[,<osdid:weight>,<...>] change <osdid> CRUSH <weight> (but do not persist)
+ --save write modified osdmap with upmap or crush-adjust changes
+ --read <file> calculate pg upmap entries to balance pg primaries
+ --read-pool <poolname> specify which pool the read balancer should adjust
+ --vstart prefix upmap and read output with './bin/'
+ [1]
diff --git a/src/test/cli/osdmaptool/missing-argument.t b/src/test/cli/osdmaptool/missing-argument.t
new file mode 100644
index 000000000..de9b80073
--- /dev/null
+++ b/src/test/cli/osdmaptool/missing-argument.t
@@ -0,0 +1,3 @@
+ $ osdmaptool
+ osdmaptool: -h or --help for usage
+ [1]
diff --git a/src/test/cli/osdmaptool/pool.t b/src/test/cli/osdmaptool/pool.t
new file mode 100644
index 000000000..4a967843f
--- /dev/null
+++ b/src/test/cli/osdmaptool/pool.t
@@ -0,0 +1,54 @@
+ $ osdmaptool --createsimple 3 myosdmap --with-default-pool
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: writing epoch 1 to myosdmap
+
+#
+# --test-map-object / --pool
+#
+ $ osdmaptool myosdmap --test-map-object foo --pool
+ Option --pool requires an argument.
+
+ [1]
+
+ $ osdmaptool myosdmap --test-map-object foo --pool bar
+ The option value 'bar' is invalid
+ [1]
+
+ $ osdmaptool myosdmap --test-map-object foo --pool 123
+ osdmaptool: osdmap file 'myosdmap'
+ There is no pool 123
+ [1]
+
+ $ osdmaptool myosdmap --test-map-object foo --pool 1
+ osdmaptool: osdmap file 'myosdmap'
+ object 'foo' \-\> 1\..* (re)
+
+ $ osdmaptool myosdmap --test-map-object foo
+ osdmaptool: osdmap file 'myosdmap'
+ osdmaptool: assuming pool 1 (use --pool to override)
+ object 'foo' \-\> 1\..* (re)
+
+#
+# --test-map-pgs / --pool
+#
+ $ osdmaptool myosdmap --test-map-pgs --pool
+ Option --pool requires an argument.
+
+ [1]
+
+ $ osdmaptool myosdmap --test-map-pgs --pool baz
+ The option value 'baz' is invalid
+ [1]
+
+ $ osdmaptool myosdmap --test-map-pgs --pool 123
+ osdmaptool: osdmap file 'myosdmap'
+ There is no pool 123
+ [1]
+
+ $ osdmaptool myosdmap --mark-up-in --test-map-pgs --pool 1 | grep pool
+ osdmaptool: osdmap file 'myosdmap'
+ pool 1 pg_num .* (re)
+
+ $ osdmaptool myosdmap --mark-up-in --test-map-pgs | grep pool
+ osdmaptool: osdmap file 'myosdmap'
+ pool 1 pg_num .* (re)
diff --git a/src/test/cli/osdmaptool/print-empty.t b/src/test/cli/osdmaptool/print-empty.t
new file mode 100644
index 000000000..a629f7717
--- /dev/null
+++ b/src/test/cli/osdmaptool/print-empty.t
@@ -0,0 +1,5 @@
+ $ touch empty
+ $ osdmaptool --print empty
+ osdmaptool: osdmap file 'empty'
+ osdmaptool: error decoding osdmap 'empty'
+ [255]
diff --git a/src/test/cli/osdmaptool/print-nonexistent.t b/src/test/cli/osdmaptool/print-nonexistent.t
new file mode 100644
index 000000000..88f7e6182
--- /dev/null
+++ b/src/test/cli/osdmaptool/print-nonexistent.t
@@ -0,0 +1,4 @@
+ $ osdmaptool --print nonexistent
+ osdmaptool: osdmap file 'nonexistent'
+ osdmaptool: couldn't open nonexistent: can't open nonexistent: (2) No such file or directory
+ [255]
diff --git a/src/test/cli/osdmaptool/test-map-pgs.t b/src/test/cli/osdmaptool/test-map-pgs.t
new file mode 100644
index 000000000..f9f7897b2
--- /dev/null
+++ b/src/test/cli/osdmaptool/test-map-pgs.t
@@ -0,0 +1,43 @@
+ $ NUM_OSDS=500
+ $ POOL_COUNT=1 # data + metadata + rbd
+ $ SIZE=3
+ $ PG_BITS=4
+#
+# create an osdmap with a few hundred devices and a realistic crushmap
+#
+ $ OSD_MAP="osdmap"
+ $ osdmaptool --osd_pool_default_size $SIZE --pg_bits $PG_BITS --createsimple $NUM_OSDS "$OSD_MAP" > /dev/null --with-default-pool
+ osdmaptool: osdmap file 'osdmap'
+ $ CRUSH_MAP="crushmap"
+ $ CEPH_ARGS="--debug-crush 0" crushtool --outfn "$CRUSH_MAP" --build --num_osds $NUM_OSDS node straw 10 rack straw 10 root straw 0
+ $ osdmaptool --import-crush "$CRUSH_MAP" "$OSD_MAP" > /dev/null
+ osdmaptool: osdmap file 'osdmap'
+ $ OUT="$TESTDIR/out"
+#
+# --test-map-pgs
+#
+ $ osdmaptool --mark-up-in --test-map-pgs "$OSD_MAP" > "$OUT"
+ osdmaptool: osdmap file 'osdmap'
+ $ PG_NUM=$(($NUM_OSDS << $PG_BITS))
+ $ grep "pg_num $PG_NUM" "$OUT" || cat $OUT
+ pool 1 pg_num 8000
+ $ TOTAL=$((POOL_COUNT * $PG_NUM))
+ $ grep -E "size $SIZE[[:space:]]$TOTAL" $OUT || cat $OUT
+ size 3\t8000 (esc)
+ $ STATS_CRUSH=$(grep '^ avg ' "$OUT")
+#
+# --test-map-pgs --test-random is expected to change nothing regarding the totals
+#
+ $ osdmaptool --mark-up-in --test-random --test-map-pgs "$OSD_MAP" > "$OUT"
+ osdmaptool: osdmap file 'osdmap'
+ $ PG_NUM=$(($NUM_OSDS << $PG_BITS))
+ $ grep "pg_num $PG_NUM" "$OUT" || cat $OUT
+ pool 1 pg_num 8000
+ $ TOTAL=$((POOL_COUNT * $PG_NUM))
+ $ grep -E "size $SIZE[[:space:]]$TOTAL" $OUT || cat $OUT
+ size 3\t8000 (esc)
+ $ STATS_RANDOM=$(grep '^ avg ' "$OUT")
+#
+# cleanup
+#
+ $ rm -f "$CRUSH_MAP" "$OSD_MAP" "$OUT"
diff --git a/src/test/cli/osdmaptool/tree.t b/src/test/cli/osdmaptool/tree.t
new file mode 100644
index 000000000..387f564b0
--- /dev/null
+++ b/src/test/cli/osdmaptool/tree.t
@@ -0,0 +1,95 @@
+ $ osdmaptool --createsimple 3 om --with-default-pool
+ osdmaptool: osdmap file 'om'
+ osdmaptool: writing epoch 1 to om
+
+ $ osdmaptool --tree=plain om
+ osdmaptool: osdmap file 'om'
+ ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF
+ -1 3.00000 root default
+ -3 3.00000 rack localrack
+ -2 3.00000 host localhost
+ 0 1.00000 osd.0 DNE 0
+ 1 1.00000 osd.1 DNE 0
+ 2 1.00000 osd.2 DNE 0
+
+ $ osdmaptool --tree=json-pretty om
+ osdmaptool: osdmap file 'om'
+ {
+ "nodes": [
+ {
+ "id": -1,
+ "name": "default",
+ "type": "root",
+ "type_id": 11,
+ "children": [
+ -3
+ ]
+ },
+ {
+ "id": -3,
+ "name": "localrack",
+ "type": "rack",
+ "type_id": 3,
+ "pool_weights": {},
+ "children": [
+ -2
+ ]
+ },
+ {
+ "id": -2,
+ "name": "localhost",
+ "type": "host",
+ "type_id": 1,
+ "pool_weights": {},
+ "children": [
+ 2,
+ 1,
+ 0
+ ]
+ },
+ {
+ "id": 0,
+ "name": "osd.0",
+ "type": "osd",
+ "type_id": 0,
+ "crush_weight": 1,
+ "depth": 3,
+ "pool_weights": {},
+ "exists": 0,
+ "status": "down",
+ "reweight": 0,
+ "primary_affinity": 1
+ },
+ {
+ "id": 1,
+ "name": "osd.1",
+ "type": "osd",
+ "type_id": 0,
+ "crush_weight": 1,
+ "depth": 3,
+ "pool_weights": {},
+ "exists": 0,
+ "status": "down",
+ "reweight": 0,
+ "primary_affinity": 1
+ },
+ {
+ "id": 2,
+ "name": "osd.2",
+ "type": "osd",
+ "type_id": 0,
+ "crush_weight": 1,
+ "depth": 3,
+ "pool_weights": {},
+ "exists": 0,
+ "status": "down",
+ "reweight": 0,
+ "primary_affinity": 1
+ }
+ ],
+ "stray": []
+ }
+
+
+ $ rm -f om
+
diff --git a/src/test/cli/osdmaptool/upmap-out.t b/src/test/cli/osdmaptool/upmap-out.t
new file mode 100644
index 000000000..02b13ec56
--- /dev/null
+++ b/src/test/cli/osdmaptool/upmap-out.t
@@ -0,0 +1,24 @@
+ $ osdmaptool --create-from-conf om -c $TESTDIR/ceph.conf.withracks --with-default-pool
+ osdmaptool: osdmap file 'om'
+ osdmaptool: writing epoch 1 to om
+ $ osdmaptool --osd_calc_pg_upmaps_aggressively=false om --mark-up-in --mark-out 147 --upmap-max 11 --upmap c
+ osdmaptool: osdmap file 'om'
+ marking all OSDs up and in
+ marking OSD@147 as out
+ writing upmap command output to: c
+ checking for upmap cleanups
+ upmap, max-count 11, max deviation 5
+ pools rbd
+ prepared 11/11 changes
+ $ cat c
+ ceph osd pg-upmap-items 1.7 142 145
+ ceph osd pg-upmap-items 1.8 219 223
+ ceph osd pg-upmap-items 1.17 201 202 171 173
+ ceph osd pg-upmap-items 1.1a 201 202
+ ceph osd pg-upmap-items 1.1c 201 202
+ ceph osd pg-upmap-items 1.20 201 202
+ ceph osd pg-upmap-items 1.51 201 202
+ ceph osd pg-upmap-items 1.62 219 223
+ ceph osd pg-upmap-items 1.6f 219 223
+ ceph osd pg-upmap-items 1.82 219 223
+ $ rm -f om c
diff --git a/src/test/cli/osdmaptool/upmap.t b/src/test/cli/osdmaptool/upmap.t
new file mode 100644
index 000000000..b84fea28c
--- /dev/null
+++ b/src/test/cli/osdmaptool/upmap.t
@@ -0,0 +1,37 @@
+ $ osdmaptool --create-from-conf om -c $TESTDIR/ceph.conf.withracks --with-default-pool
+ osdmaptool: osdmap file 'om'
+ osdmaptool: writing epoch 1 to om
+ $ osdmaptool --osd_calc_pg_upmaps_aggressively=false om --mark-up-in --upmap-max 11 --upmap c --save
+ osdmaptool: osdmap file 'om'
+ marking all OSDs up and in
+ writing upmap command output to: c
+ checking for upmap cleanups
+ upmap, max-count 11, max deviation 5
+ pools rbd
+ prepared 11/11 changes
+ osdmaptool: writing epoch 3 to om
+ $ cat c
+ ceph osd pg-upmap-items 1.7 142 147
+ ceph osd pg-upmap-items 1.8 219 223
+ ceph osd pg-upmap-items 1.17 201 202 171 173
+ ceph osd pg-upmap-items 1.1a 201 202
+ ceph osd pg-upmap-items 1.1c 201 202
+ ceph osd pg-upmap-items 1.20 201 202
+ ceph osd pg-upmap-items 1.24 232 233
+ ceph osd pg-upmap-items 1.51 201 202
+ ceph osd pg-upmap-items 1.62 219 223
+ ceph osd pg-upmap-items 1.6f 219 223
+ $ osdmaptool --print om | grep pg_upmap_items
+ osdmaptool: osdmap file 'om'
+ pg_upmap_items 1.7 [142,147]
+ pg_upmap_items 1.8 [219,223]
+ pg_upmap_items 1.17 [201,202,171,173]
+ pg_upmap_items 1.1a [201,202]
+ pg_upmap_items 1.1c [201,202]
+ pg_upmap_items 1.20 [201,202]
+ pg_upmap_items 1.24 [232,233]
+ pg_upmap_items 1.51 [201,202]
+ pg_upmap_items 1.62 [219,223]
+ pg_upmap_items 1.6f [219,223]
+ $ rm -f om c
+
diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t
new file mode 100644
index 000000000..828bebf07
--- /dev/null
+++ b/src/test/cli/radosgw-admin/help.t
@@ -0,0 +1,393 @@
+ $ radosgw-admin --help
+ usage: radosgw-admin <cmd> [options...]
+ commands:
+ user create create a new user
+ user modify modify user
+ user info get user info
+ user rename rename user
+ user rm remove user
+ user suspend suspend a user
+ user enable re-enable user after suspension
+ user check check user info
+ user stats show user stats as accounted by quota subsystem
+ user list list users
+ caps add add user capabilities
+ caps rm remove user capabilities
+ subuser create create a new subuser
+ subuser modify modify subuser
+ subuser rm remove subuser
+ key create create access key
+ key rm remove access key
+ bucket list list buckets (specify --allow-unordered for
+ faster, unsorted listing)
+ bucket limit check show bucket sharding stats
+ bucket link link bucket to specified user
+ bucket unlink unlink bucket from specified user
+ bucket stats returns bucket statistics
+ bucket rm remove bucket
+ bucket check check bucket index by verifying size and object count stats
+ bucket check olh check for olh index entries and objects that are pending removal
+ bucket check unlinked check for object versions that are not visible in a bucket listing
+ bucket chown link bucket to specified user and update its object ACLs
+ bucket reshard reshard bucket
+ bucket rewrite rewrite all objects in the specified bucket
+ bucket sync checkpoint poll a bucket's sync status until it catches up to its remote
+ bucket sync disable disable bucket sync
+ bucket sync enable enable bucket sync
+ bucket radoslist list rados objects backing bucket's objects
+ bi get retrieve bucket index object entries
+ bi put store bucket index object entries
+ bi list list raw bucket index entries
+ bi purge purge bucket index entries
+ object rm remove object
+ object put put object
+ object stat stat an object for its metadata
+ object unlink unlink object from bucket index
+ object rewrite rewrite the specified object
+ object reindex reindex the object(s) indicated by --bucket and either --object or --objects-file
+ objects expire run expired objects cleanup
+ objects expire-stale list list stale expired objects (caused by reshard)
+ objects expire-stale rm remove stale expired objects
+ period rm remove a period
+ period get get period info
+ period get-current get current period info
+ period pull pull a period
+ period push push a period
+ period list list all periods
+ period update update the staging period
+ period commit commit the staging period
+ quota set set quota params
+ quota enable enable quota
+ quota disable disable quota
+ ratelimit get get ratelimit params
+ ratelimit set set ratelimit params
+ ratelimit enable enable ratelimit
+ ratelimit disable disable ratelimit
+ global quota get view global quota params
+ global quota set set global quota params
+ global quota enable enable a global quota
+ global quota disable disable a global quota
+ global ratelimit get view global ratelimit params
+ global ratelimit set set global ratelimit params
+ global ratelimit enable enable a ratelimit quota
+ global ratelimit disable disable a ratelimit quota
+ realm create create a new realm
+ realm rm remove a realm
+ realm get show realm info
+ realm get-default get default realm name
+ realm list list realms
+ realm list-periods list all realm periods
+ realm rename rename a realm
+ realm set set realm info (requires infile)
+ realm default set realm as default
+ realm pull pull a realm and its current period
+ zonegroup add add a zone to a zonegroup
+ zonegroup create create a new zone group info
+ zonegroup default set default zone group
+ zonegroup delete delete a zone group info
+ zonegroup get show zone group info
+ zonegroup modify modify an existing zonegroup
+ zonegroup set set zone group info (requires infile)
+ zonegroup rm remove a zone from a zonegroup
+ zonegroup rename rename a zone group
+ zonegroup list list all zone groups set on this cluster
+ zonegroup placement list list zonegroup's placement targets
+ zonegroup placement get get a placement target of a specific zonegroup
+ zonegroup placement add add a placement target id to a zonegroup
+ zonegroup placement modify modify a placement target of a specific zonegroup
+ zonegroup placement rm remove a placement target from a zonegroup
+ zonegroup placement default set a zonegroup's default placement target
+ zone create create a new zone
+ zone rm remove a zone
+ zone get show zone cluster params
+ zone modify modify an existing zone
+ zone set set zone cluster params (requires infile)
+ zone list list all zones set on this cluster
+ zone rename rename a zone
+ zone placement list list zone's placement targets
+ zone placement get get a zone placement target
+ zone placement add add a zone placement target
+ zone placement modify modify a zone placement target
+ zone placement rm remove a zone placement target
+ metadata sync status get metadata sync status
+ metadata sync init init metadata sync
+ metadata sync run run metadata sync
+ data sync status get data sync status of the specified source zone
+ data sync init init data sync for the specified source zone
+ data sync run run data sync for the specified source zone
+ pool add add an existing pool for data placement
+ pool rm remove an existing pool from data placement set
+ pools list list placement active set
+ policy read bucket/object policy
+ log list list log objects
+ log show dump a log from specific object or (bucket + date
+ + bucket-id)
+ (NOTE: required to specify formatting of date
+ to "YYYY-MM-DD-hh")
+ log rm remove log object
+ usage show show usage (by user, by bucket, date range)
+ usage trim trim usage (by user, by bucket, date range)
+ usage clear reset all the usage stats for the cluster
+ gc list dump expired garbage collection objects (specify
+ --include-all to list all entries, including unexpired)
+ gc process manually process garbage (specify
+ --include-all to process all entries, including unexpired)
+ lc list list all bucket lifecycle progress
+ lc get get a lifecycle bucket configuration
+ lc process manually process lifecycle
+ lc reshard fix fix LC for a resharded bucket
+ metadata get get metadata info
+ metadata put put metadata info
+ metadata rm remove metadata info
+ metadata list list metadata info
+ mdlog list list metadata log
+ mdlog autotrim auto trim metadata log
+ mdlog trim trim metadata log (use marker)
+ mdlog status read metadata log status
+ bilog list list bucket index log
+ bilog trim trim bucket index log (use start-marker, end-marker)
+ bilog status read bucket index log status
+ bilog autotrim auto trim bucket index log
+ datalog list list data log
+ datalog trim trim data log
+ datalog status read data log status
+ datalog type change datalog type to --log_type={fifo,omap}
+ orphans find deprecated -- init and run search for leaked rados objects (use job-id, pool)
+ orphans finish deprecated -- clean up search for leaked rados objects
+ orphans list-jobs deprecated -- list the current job-ids for orphans search
+ * the three 'orphans' sub-commands are now deprecated; consider using the `rgw-orphan-list` tool
+ role create create a AWS role for use with STS
+ role delete remove a role
+ role get get a role
+ role list list roles with specified path prefix
+ role-trust-policy modify modify the assume role policy of an existing role
+ role-policy put add/update permission policy to role
+ role-policy list list policies attached to a role
+ role-policy get get the specified inline policy document embedded with the given role
+ role-policy delete remove policy attached to a role
+ role update update max_session_duration of a role
+ reshard add schedule a resharding of a bucket
+ reshard list list all bucket resharding or scheduled to be resharded
+ reshard status read bucket resharding status
+ reshard process process of scheduled reshard jobs
+ reshard cancel cancel resharding a bucket
+ reshard stale-instances list list stale-instances from bucket resharding
+ reshard stale-instances delete cleanup stale-instances from bucket resharding
+ sync error list list sync error
+ sync error trim trim sync error
+ mfa create create a new MFA TOTP token
+ mfa list list MFA TOTP tokens
+ mfa get show MFA TOTP token
+ mfa remove delete MFA TOTP token
+ mfa check check MFA TOTP token
+ mfa resync re-sync MFA TOTP token
+ topic list list bucket notifications topics
+ topic get get a bucket notifications topic
+ topic rm remove a bucket notifications topic
+ script put upload a lua script to a context
+ script get get the lua script of a context
+ script rm remove the lua scripts of a context
+ script-package add add a lua package to the scripts allowlist
+ script-package rm remove a lua package from the scripts allowlist
+ script-package list get the lua packages allowlist
+ notification list list bucket notifications configuration
+ notification get get a bucket notifications configuration
+ notification rm remove a bucket notifications configuration
+ options:
+ --tenant=<tenant> tenant name
+ --user_ns=<namespace> namespace of user (oidc in case of users authenticated with oidc provider)
+ --uid=<id> user id
+ --new-uid=<id> new user id
+ --subuser=<name> subuser name
+ --access-key=<key> S3 access key
+ --email=<email> user's email address
+ --secret/--secret-key=<key>
+ specify secret key
+ --gen-access-key generate random access key (for S3)
+ --gen-secret generate random secret key
+ --key-type=<type> key type, options are: swift, s3
+ --temp-url-key[-2]=<key> temp url key
+ --access=<access> Set access permissions for sub-user, should be one
+ of read, write, readwrite, full
+ --display-name=<name> user's display name
+ --max-buckets max number of buckets for a user
+ --admin set the admin flag on the user
+ --system set the system flag on the user
+ --op-mask set the op mask on the user
+ --bucket=<bucket> Specify the bucket name. Also used by the quota command.
+ --pool=<pool> Specify the pool name. Also used to scan for leaked rados objects.
+ --object=<object> object name
+ --objects-file=<file> file containing a list of object names to process
+ --object-version=<version> object version
+ --date=<date> date in the format yyyy-mm-dd
+ --start-date=<date> start date in the format yyyy-mm-dd
+ --end-date=<date> end date in the format yyyy-mm-dd
+ --bucket-id=<bucket-id> bucket id
+ --bucket-new-name=<bucket>
+ for bucket link: optional new name
+ --shard-id=<shard-id> optional for:
+ mdlog list
+ data sync status
+ required for:
+ mdlog trim
+ --gen=<gen-id> optional for:
+ bilog list
+ bilog trim
+ bilog status
+ --max-entries=<entries> max entries for listing operations
+ --metadata-key=<key> key to retrieve metadata from with metadata get
+ --remote=<remote> zone or zonegroup id of remote gateway
+ --period=<id> period id
+ --url=<url> url for pushing/pulling period/realm
+ --epoch=<number> period epoch
+ --commit commit the period during 'period update'
+ --staging get staging period info
+ --master set as master
+ --master-zone=<id> master zone id
+ --rgw-realm=<name> realm name
+ --realm-id=<id> realm id
+ --realm-new-name=<name> realm new name
+ --rgw-zonegroup=<name> zonegroup name
+ --zonegroup-id=<id> zonegroup id
+ --zonegroup-new-name=<name>
+ zonegroup new name
+ --rgw-zone=<name> name of zone in which radosgw is running
+ --zone-id=<id> zone id
+ --zone-new-name=<name> zone new name
+ --source-zone specify the source zone (for data sync)
+ --default set entity (realm, zonegroup, zone) as default
+ --read-only set zone as read-only (when adding to zonegroup)
+ --redirect-zone specify zone id to redirect when response is 404 (not found)
+ --placement-id placement id for zonegroup placement commands
+ --storage-class storage class for zonegroup placement commands
+ --tags=<list> list of tags for zonegroup placement add and modify commands
+ --tags-add=<list> list of tags to add for zonegroup placement modify command
+ --tags-rm=<list> list of tags to remove for zonegroup placement modify command
+ --endpoints=<list> zone endpoints
+ --index-pool=<pool> placement target index pool
+ --data-pool=<pool> placement target data pool
+ --data-extra-pool=<pool> placement target data extra (non-ec) pool
+ --placement-index-type=<type>
+ placement target index type (normal, indexless, or #id)
+ --placement-inline-data=<true>
+ set whether the placement target is configured to store a data
+ chunk inline in head objects
+ --compression=<type> placement target compression type (plugin name or empty/none)
+ --tier-type=<type> zone tier type
+ --tier-config=<k>=<v>[,...]
+ set zone tier config keys, values
+ --tier-config-rm=<k>[,...]
+ unset zone tier config keys
+ --sync-from-all[=false] set/reset whether zone syncs from all zonegroup peers
+ --sync-from=[zone-name][,...]
+ set list of zones to sync from
+ --sync-from-rm=[zone-name][,...]
+ remove zones from list of zones to sync from
+ --bucket-index-max-shards override a zone/zonegroup's default bucket index shard count
+ --fix besides checking bucket index, will also fix it
+ --check-objects bucket check: rebuilds bucket index according to
+ actual objects state
+ --format=<format> specify output format for certain operations: xml,
+ json
+ --purge-data when specified, user removal will also purge all the
+ user data
+ --purge-keys when specified, subuser removal will also purge all the
+ subuser keys
+ --purge-objects remove a bucket's objects before deleting it
+ (NOTE: required to delete a non-empty bucket)
+ --sync-stats option to 'user stats', update user stats with current
+ stats reported by user's buckets indexes
+ --reset-stats option to 'user stats', reset stats in accordance with user buckets
+ --show-config show configuration
+ --show-log-entries=<flag> enable/disable dump of log entries on log show
+ --show-log-sum=<flag> enable/disable dump of log summation on log show
+ --skip-zero-entries log show only dumps entries that don't have zero value
+ in one of the numeric field
+ --infile=<file> file to read in when setting data
+ --categories=<list> comma separated list of categories, used in usage show
+ --caps=<caps> list of caps (e.g., "usage=read, write; user=read")
+ --op-mask=<op-mask> permission of user's operations (e.g., "read, write, delete, *")
+ --yes-i-really-mean-it required for certain operations
+ --warnings-only when specified with bucket limit check, list
+ only buckets nearing or over the current max
+ objects per shard value
+ --bypass-gc when specified with bucket deletion, triggers
+ object deletions by not involving GC
+ --inconsistent-index when specified with bucket deletion and bypass-gc set to true,
+ ignores bucket index consistency
+ --min-rewrite-size min object size for bucket rewrite (default 4M)
+ --max-rewrite-size max object size for bucket rewrite (default ULLONG_MAX)
+ --min-rewrite-stripe-size min stripe size for object rewrite (default 0)
+ --trim-delay-ms time interval in msec to limit the frequency of sync error log entries trimming operations,
+ the trimming process will sleep the specified msec for every 1000 entries trimmed
+ --max-concurrent-ios maximum concurrent ios for bucket operations (default: 32)
+ --enable-feature enable a zone/zonegroup feature
+ --disable-feature disable a zone/zonegroup feature
+
+ <date> := "YYYY-MM-DD[ hh:mm:ss]"
+
+ Quota options:
+ --max-objects specify max objects (negative value to disable)
+ --max-size specify max size (in B/K/M/G/T, negative value to disable)
+ --quota-scope scope of quota (bucket, user)
+
+ Rate limiting options:
+ --max-read-ops specify max requests per minute for READ ops per RGW (GET and HEAD request methods), 0 means unlimited
+ --max-read-bytes specify max bytes per minute for READ ops per RGW (GET and HEAD request methods), 0 means unlimited
+ --max-write-ops specify max requests per minute for WRITE ops per RGW (Not GET or HEAD request methods), 0 means unlimited
+ --max-write-bytes specify max bytes per minute for WRITE ops per RGW (Not GET or HEAD request methods), 0 means unlimited
+ --ratelimit-scope scope of rate limiting: bucket, user, anonymous
+ anonymous can be configured only with global rate limit
+
+ Orphans search options:
+ --num-shards num of shards to use for keeping the temporary scan info
+ --orphan-stale-secs num of seconds to wait before declaring an object to be an orphan (default: 86400)
+ --job-id set the job id (for orphans find)
+ --detail detailed mode, log and stat head objects as well
+
+ Orphans list-jobs options:
+ --extra-info provide extra info in job list
+
+ Role options:
+ --role-name name of the role to create
+ --path path to the role
+ --assume-role-policy-doc the trust relationship policy document that grants an entity permission to assume the role
+ --policy-name name of the policy document
+ --policy-doc permission policy document
+ --path-prefix path prefix for filtering roles
+
+ MFA options:
+ --totp-serial a string that represents the ID of a TOTP token
+ --totp-seed the secret seed that is used to calculate the TOTP
+ --totp-seconds the time resolution that is being used for TOTP generation
+ --totp-window the number of TOTP tokens that are checked before and after the current token when validating token
+ --totp-pin the valid value of a TOTP token at a certain time
+
+ Bucket notifications options:
+ --topic bucket notifications topic name
+ --notification-id bucket notifications id
+
+ Script options:
+ --context context in which the script runs. one of: prerequest, postrequest, background, getdata, putdata
+ --package name of the lua package that should be added/removed to/from the allowlist
+ --allow-compilation package is allowed to compile C code as part of its installation
+
+ Bucket check olh/unlinked options:
+ --min-age-hours minimum age of unlinked objects to consider for bucket check unlinked (default: 1)
+ --dump-keys when specified, all keys identified as problematic are printed to stdout
+ --hide-progress when specified, per-shard progress details are not printed to stderr
+
+ radoslist options:
+ --rgw-obj-fs the field separator that will separate the rados
+ object name from the rgw object name;
+ additionally rados objects for incomplete
+ multipart uploads will not be output
+
+ --conf/-c FILE read configuration from the given configuration file
+ --id ID set ID portion of my name
+ --name/-n TYPE.ID set name
+ --cluster NAME set cluster name (default: ceph)
+ --setuser USER set uid to user or uid (and gid to user's gid)
+ --setgroup GROUP set gid to group or gid
+ --version show version and quit
+
diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t
new file mode 100644
index 000000000..8d8d30273
--- /dev/null
+++ b/src/test/cli/rbd/help.t
@@ -0,0 +1,2666 @@
+ $ rbd --help
+ usage: rbd <command> ...
+
+ Command-line interface for managing Ceph RBD images.
+
+ Positional arguments:
+ <command>
+ bench Simple benchmark.
+ children Display children of an image or its
+ snapshot.
+ clone Clone a snapshot into a CoW child image.
+ config global get Get a global-level configuration override.
+ config global list (... ls) List global-level configuration overrides.
+ config global remove (... rm) Remove a global-level configuration
+ override.
+ config global set Set a global-level configuration override.
+ config image get Get an image-level configuration override.
+ config image list (... ls) List image-level configuration overrides.
+ config image remove (... rm) Remove an image-level configuration
+ override.
+ config image set Set an image-level configuration override.
+ config pool get Get a pool-level configuration override.
+ config pool list (... ls) List pool-level configuration overrides.
+ config pool remove (... rm) Remove a pool-level configuration
+ override.
+ config pool set Set a pool-level configuration override.
+ copy (cp) Copy src image to dest.
+ create Create an empty image.
+ deep copy (deep cp) Deep copy (including snapshots) src image
+ to dest.
+ device attach Attach image to device.
+ device detach Detach image from device.
+ device list (showmapped) List mapped rbd images.
+ device map (map) Map an image to a block device.
+ device unmap (unmap) Unmap a rbd device.
+ diff Print extents that differ since a
+ previous snap, or image creation.
+ disk-usage (du) Show disk usage stats for pool, image or
+ snapshot.
+ encryption format Format image to an encrypted format.
+ export Export image to file.
+ export-diff Export incremental diff to file.
+ feature disable Disable the specified image feature.
+ feature enable Enable the specified image feature.
+ flatten Fill clone with parent data (make it
+ independent).
+ group create Create a group.
+ group image add Add an image to a group.
+ group image list (... ls) List images in a group.
+ group image remove (... rm) Remove an image from a group.
+ group list (group ls) List rbd groups.
+ group remove (group rm) Delete a group.
+ group rename Rename a group within pool.
+ group snap create Make a snapshot of a group.
+ group snap list (... ls) List snapshots of a group.
+ group snap remove (... rm) Remove a snapshot from a group.
+ group snap rename Rename group's snapshot.
+ group snap rollback Rollback group to snapshot.
+ image-meta get Image metadata get the value associated
+ with the key.
+ image-meta list (image-meta ls) Image metadata list keys with values.
+ image-meta remove (image-meta rm) Image metadata remove the key and value
+ associated.
+ image-meta set Image metadata set key with value.
+ import Import image from file.
+ import-diff Import an incremental diff.
+ info Show information about image size,
+ striping, etc.
+ journal client disconnect Flag image journal client as disconnected.
+ journal export Export image journal.
+ journal import Import image journal.
+ journal info Show information about image journal.
+ journal inspect Inspect image journal for structural
+ errors.
+ journal reset Reset image journal.
+ journal status Show status of image journal.
+ list (ls) List rbd images.
+ lock add Take a lock on an image.
+ lock list (lock ls) Show locks held on an image.
+ lock remove (lock rm) Release a lock on an image.
+ merge-diff Merge two diff exports together.
+ migration abort Cancel interrupted image migration.
+ migration commit Commit image migration.
+ migration execute Execute image migration.
+ migration prepare Prepare image migration.
+ mirror image demote Demote an image to non-primary for RBD
+ mirroring.
+ mirror image disable Disable RBD mirroring for an image.
+ mirror image enable Enable RBD mirroring for an image.
+ mirror image promote Promote an image to primary for RBD
+ mirroring.
+ mirror image resync Force resync to primary image for RBD
+ mirroring.
+ mirror image snapshot Create RBD mirroring image snapshot.
+ mirror image status Show RBD mirroring status for an image.
+ mirror pool demote Demote all primary images in the pool.
+ mirror pool disable Disable RBD mirroring by default within a
+ pool.
+ mirror pool enable Enable RBD mirroring by default within a
+ pool.
+ mirror pool info Show information about the pool mirroring
+ configuration.
+ mirror pool peer add Add a mirroring peer to a pool.
+ mirror pool peer bootstrap create Create a peer bootstrap token to import
+ in a remote cluster
+ mirror pool peer bootstrap import Import a peer bootstrap token created
+ from a remote cluster
+ mirror pool peer remove Remove a mirroring peer from a pool.
+ mirror pool peer set Update mirroring peer settings.
+ mirror pool promote Promote all non-primary images in the
+ pool.
+ mirror pool status Show status for all mirrored images in
+ the pool.
+ mirror snapshot schedule add Add mirror snapshot schedule.
+ mirror snapshot schedule list (... ls)
+ List mirror snapshot schedule.
+ mirror snapshot schedule remove (... rm)
+ Remove mirror snapshot schedule.
+ mirror snapshot schedule status Show mirror snapshot schedule status.
+ namespace create Create an RBD image namespace.
+ namespace list (namespace ls) List RBD image namespaces.
+ namespace remove (namespace rm) Remove an RBD image namespace.
+ object-map check Verify the object map is correct.
+ object-map rebuild Rebuild an invalid object map.
+ perf image iostat Display image IO statistics.
+ perf image iotop Display a top-like IO monitor.
+ persistent-cache flush Flush persistent cache.
+ persistent-cache invalidate Invalidate (discard) existing / dirty
+ persistent cache.
+ pool init Initialize pool for use by RBD.
+ pool stats Display pool statistics.
+ remove (rm) Delete an image.
+ rename (mv) Rename image within pool.
+ resize Resize (expand or shrink) image.
+ snap create (snap add) Create a snapshot.
+ snap limit clear Remove snapshot limit.
+ snap limit set Limit the number of snapshots.
+ snap list (snap ls) Dump list of image snapshots.
+ snap protect Prevent a snapshot from being deleted.
+ snap purge Delete all unprotected snapshots.
+ snap remove (snap rm) Delete a snapshot.
+ snap rename Rename a snapshot.
+ snap rollback (snap revert) Rollback image to snapshot.
+ snap unprotect Allow a snapshot to be deleted.
+ sparsify Reclaim space for zeroed image extents.
+ status Show the status of this image.
+ trash list (trash ls) List trash images.
+ trash move (trash mv) Move an image to the trash.
+ trash purge Remove all expired images from trash.
+ trash purge schedule add Add trash purge schedule.
+ trash purge schedule list (... ls)
+ List trash purge schedule.
+ trash purge schedule remove (... rm)
+ Remove trash purge schedule.
+ trash purge schedule status Show trash purge schedule status.
+ trash remove (trash rm) Remove an image from trash.
+ trash restore Restore an image from trash.
+ watch Watch events on image.
+
+ Optional arguments:
+ -c [ --conf ] arg path to cluster configuration
+ --cluster arg cluster name
+ --id arg client id (without 'client.' prefix)
+ -n [ --name ] arg client name
+ -m [ --mon_host ] arg monitor host
+ -K [ --keyfile ] arg path to secret key
+ -k [ --keyring ] arg path to keyring
+
+ See 'rbd help <command>' for help on a specific command.
+ $ rbd help | grep '^ [a-z]' | sed 's/^ \([a-z -]*[a-z]\).*/\1/g' | while read -r line; do echo rbd help $line ; rbd help $line; done
+ rbd help bench
+ usage: rbd bench [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--io-size <io-size>] [--io-threads <io-threads>]
+ [--io-total <io-total>] [--io-pattern <io-pattern>]
+ [--rw-mix-read <rw-mix-read>] --io-type <io-type>
+ <image-spec>
+
+ Simple benchmark.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --io-size arg IO size (in B/K/M/G) (< 4G) [default: 4K]
+ --io-threads arg ios in flight [default: 16]
+ --io-total arg total size for IO (in B/K/M/G/T) [default: 1G]
+ --io-pattern arg IO pattern (rand, seq, or full-seq) [default: seq]
+ --rw-mix-read arg read proportion in readwrite (<= 100) [default: 50]
+ --io-type arg IO type (read, write, or readwrite(rw))
+
+ rbd help children
+ usage: rbd children [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>] [--snap-id <snap-id>]
+ [--all] [--descendants] [--format <format>]
+ [--pretty-format]
+ <image-or-snap-spec>
+
+ Display children of an image or its snapshot.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>])
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --snap-id arg snapshot id
+ -a [ --all ] list all children (include trash)
+ --descendants include all descendants
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help clone
+ usage: rbd clone [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>] [--dest <dest>]
+ [--order <order>] [--object-size <object-size>]
+ [--image-feature <image-feature>] [--image-shared]
+ [--stripe-unit <stripe-unit>] [--stripe-count <stripe-count>]
+ [--data-pool <data-pool>]
+ [--mirror-image-mode <mirror-image-mode>]
+ [--journal-splay-width <journal-splay-width>]
+ [--journal-object-size <journal-object-size>]
+ [--journal-pool <journal-pool>]
+ <source-snap-spec> <dest-image-spec>
+
+ Clone a snapshot into a CoW child image.
+
+ Positional arguments
+ <source-snap-spec> source snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>
+ )
+ <dest-image-spec> destination image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --snap arg source snapshot name
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+ --object-size arg object size in B/K/M [4K <= object size <= 32M]
+ --image-feature arg image features
+ [layering(+), exclusive-lock(+*), object-map(+*),
+ deep-flatten(+-), journaling(*)]
+ --image-shared shared image
+ --stripe-unit arg stripe unit in B/K/M
+ --stripe-count arg stripe count
+ --data-pool arg data pool
+ --mirror-image-mode arg mirror image mode [journal or snapshot]
+ --journal-splay-width arg number of active journal objects
+ --journal-object-size arg size of journal objects [4K <= size <= 64M]
+ --journal-pool arg pool for journal objects
+
+ Image Features:
+ (*) supports enabling/disabling on existing images
+ (-) supports disabling-only on existing images
+ (+) enabled by default for new images if features not specified
+
+ rbd help config global get
+ usage: rbd config global get <config-entity> <key>
+
+ Get a global-level configuration override.
+
+ Positional arguments
+ <config-entity> config entity (global, client, client.<id>)
+ <key> config key
+
+ rbd help config global list
+ usage: rbd config global list [--format <format>] [--pretty-format]
+ <config-entity>
+
+ List global-level configuration overrides.
+
+ Positional arguments
+ <config-entity> config entity (global, client, client.<id>)
+
+ Optional arguments
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help config global remove
+ usage: rbd config global remove <config-entity> <key>
+
+ Remove a global-level configuration override.
+
+ Positional arguments
+ <config-entity> config entity (global, client, client.<id>)
+ <key> config key
+
+ rbd help config global set
+ usage: rbd config global set <config-entity> <key> <value>
+
+ Set a global-level configuration override.
+
+ Positional arguments
+ <config-entity> config entity (global, client, client.<id>)
+ <key> config key
+ <value> config value
+
+ rbd help config image get
+ usage: rbd config image get [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <key>
+
+ Get an image-level configuration override.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <key> config key
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help config image list
+ usage: rbd config image list [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--format <format>]
+ [--pretty-format]
+ <image-spec>
+
+ List image-level configuration overrides.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help config image remove
+ usage: rbd config image remove [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <key>
+
+ Remove an image-level configuration override.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <key> config key
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help config image set
+ usage: rbd config image set [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <key> <value>
+
+ Set an image-level configuration override.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <key> config key
+ <value> config value
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help config pool get
+ usage: rbd config pool get <pool-name> <key>
+
+ Get a pool-level configuration override.
+
+ Positional arguments
+ <pool-name> pool name
+ <key> config key
+
+ rbd help config pool list
+ usage: rbd config pool list [--format <format>] [--pretty-format]
+ <pool-name>
+
+ List pool-level configuration overrides.
+
+ Positional arguments
+ <pool-name> pool name
+
+ Optional arguments
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help config pool remove
+ usage: rbd config pool remove <pool-name> <key>
+
+ Remove a pool-level configuration override.
+
+ Positional arguments
+ <pool-name> pool name
+ <key> config key
+
+ rbd help config pool set
+ usage: rbd config pool set <pool-name> <key> <value>
+
+ Set a pool-level configuration override.
+
+ Positional arguments
+ <pool-name> pool name
+ <key> config key
+ <value> config value
+
+ rbd help copy
+ usage: rbd copy [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>] [--dest <dest>]
+ [--order <order>] [--object-size <object-size>]
+ [--image-feature <image-feature>] [--image-shared]
+ [--stripe-unit <stripe-unit>] [--stripe-count <stripe-count>]
+ [--data-pool <data-pool>]
+ [--mirror-image-mode <mirror-image-mode>]
+ [--journal-splay-width <journal-splay-width>]
+ [--journal-object-size <journal-object-size>]
+ [--journal-pool <journal-pool>] [--sparse-size <sparse-size>]
+ [--no-progress]
+ <source-image-or-snap-spec> <dest-image-spec>
+
+ Copy src image to dest.
+
+ Positional arguments
+ <source-image-or-snap-spec> source image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-n
+ ame>])
+ <dest-image-spec> destination image specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --snap arg source snapshot name
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+ --object-size arg object size in B/K/M [4K <= object size <= 32M]
+ --image-feature arg image features
+ [layering(+), exclusive-lock(+*),
+ object-map(+*), deep-flatten(+-), journaling(*)]
+ --image-shared shared image
+ --stripe-unit arg stripe unit in B/K/M
+ --stripe-count arg stripe count
+ --data-pool arg data pool
+ --mirror-image-mode arg mirror image mode [journal or snapshot]
+ --journal-splay-width arg number of active journal objects
+ --journal-object-size arg size of journal objects [4K <= size <= 64M]
+ --journal-pool arg pool for journal objects
+ --sparse-size arg sparse size in B/K/M [default: 4K]
+ --no-progress disable progress output
+
+ Image Features:
+ (*) supports enabling/disabling on existing images
+ (-) supports disabling-only on existing images
+ (+) enabled by default for new images if features not specified
+
+ rbd help create
+ usage: rbd create [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--image-format <image-format>] [--new-format]
+ [--order <order>] [--object-size <object-size>]
+ [--image-feature <image-feature>] [--image-shared]
+ [--stripe-unit <stripe-unit>]
+ [--stripe-count <stripe-count>] [--data-pool <data-pool>]
+ [--mirror-image-mode <mirror-image-mode>]
+ [--journal-splay-width <journal-splay-width>]
+ [--journal-object-size <journal-object-size>]
+ [--journal-pool <journal-pool>]
+ [--thick-provision] --size <size> [--no-progress]
+ <image-spec>
+
+ Create an empty image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --image-format arg image format [default: 2]
+ --object-size arg object size in B/K/M [4K <= object size <= 32M]
+ --image-feature arg image features
+ [layering(+), exclusive-lock(+*), object-map(+*),
+ deep-flatten(+-), journaling(*)]
+ --image-shared shared image
+ --stripe-unit arg stripe unit in B/K/M
+ --stripe-count arg stripe count
+ --data-pool arg data pool
+ --mirror-image-mode arg mirror image mode [journal or snapshot]
+ --journal-splay-width arg number of active journal objects
+ --journal-object-size arg size of journal objects [4K <= size <= 64M]
+ --journal-pool arg pool for journal objects
+ --thick-provision fully allocate storage and zero image
+ -s [ --size ] arg image size (in M/G/T) [default: M]
+ --no-progress disable progress output
+
+ Image Features:
+ (*) supports enabling/disabling on existing images
+ (-) supports disabling-only on existing images
+ (+) enabled by default for new images if features not specified
+
+ rbd help deep copy
+ usage: rbd deep copy [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>]
+ [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>] [--dest <dest>]
+ [--order <order>] [--object-size <object-size>]
+ [--image-feature <image-feature>] [--image-shared]
+ [--stripe-unit <stripe-unit>]
+ [--stripe-count <stripe-count>] [--data-pool <data-pool>]
+ [--mirror-image-mode <mirror-image-mode>]
+ [--journal-splay-width <journal-splay-width>]
+ [--journal-object-size <journal-object-size>]
+ [--journal-pool <journal-pool>] [--flatten]
+ [--no-progress]
+ <source-image-or-snap-spec> <dest-image-spec>
+
+ Deep copy (including snapshots) src image to dest.
+
+ Positional arguments
+ <source-image-or-snap-spec> source image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-n
+ ame>])
+ <dest-image-spec> destination image specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --snap arg source snapshot name
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+ --object-size arg object size in B/K/M [4K <= object size <= 32M]
+ --image-feature arg image features
+ [layering(+), exclusive-lock(+*),
+ object-map(+*), deep-flatten(+-), journaling(*)]
+ --image-shared shared image
+ --stripe-unit arg stripe unit in B/K/M
+ --stripe-count arg stripe count
+ --data-pool arg data pool
+ --mirror-image-mode arg mirror image mode [journal or snapshot]
+ --journal-splay-width arg number of active journal objects
+ --journal-object-size arg size of journal objects [4K <= size <= 64M]
+ --journal-pool arg pool for journal objects
+ --flatten fill clone with parent data (make it independent)
+ --no-progress disable progress output
+
+ Image Features:
+ (*) supports enabling/disabling on existing images
+ (-) supports disabling-only on existing images
+ (+) enabled by default for new images if features not specified
+
+ rbd help device attach
+ usage: rbd device attach [--device-type <device-type>] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] --device <device> [--show-cookie]
+ [--cookie <cookie>] [--read-only] [--force]
+ [--exclusive] [--quiesce]
+ [--quiesce-hook <quiesce-hook>] [--snap-id <snap-id>]
+ [--options <options>]
+ <image-or-snap-spec>
+
+ Attach image to device.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>
+ ])
+
+ Optional arguments
+ -t [ --device-type ] arg device type [ggate, krbd (default), nbd]
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --device arg specify device path
+ --show-cookie show device cookie
+ --cookie arg specify device cookie
+ --read-only attach read-only
+ --force force attach
+ --exclusive disable automatic exclusive lock transitions
+ --quiesce use quiesce hooks
+ --quiesce-hook arg quiesce hook path
+ --snap-id arg snapshot id
+ -o [ --options ] arg device specific options
+
+ rbd help device detach
+ usage: rbd device detach [--device-type <device-type>] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--snap-id <snap-id>]
+ [--options <options>]
+ <image-or-snap-or-device-spec>
+
+ Detach image from device.
+
+ Positional arguments
+ <image-or-snap-or-device-spec> image, snapshot, or device specification
+ [<pool-name>/[<namespace>/]]<image-name>[@<sna
+ p-name>] or <device-path>
+
+ Optional arguments
+ -t [ --device-type ] arg device type [ggate, krbd (default), nbd]
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --snap-id arg snapshot id
+ -o [ --options ] arg device specific options
+
+ rbd help device list
+ usage: rbd device list [--device-type <device-type>] [--format <format>]
+ [--pretty-format]
+
+ List mapped rbd images.
+
+ Optional arguments
+ -t [ --device-type ] arg device type [ggate, krbd (default), nbd]
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help device map
+ usage: rbd device map [--device-type <device-type>] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--show-cookie] [--cookie <cookie>]
+ [--read-only] [--exclusive] [--quiesce]
+ [--quiesce-hook <quiesce-hook>] [--snap-id <snap-id>]
+ [--options <options>]
+ <image-or-snap-spec>
+
+ Map an image to a block device.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>
+ ])
+
+ Optional arguments
+ -t [ --device-type ] arg device type [ggate, krbd (default), nbd]
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --show-cookie show device cookie
+ --cookie arg specify device cookie
+ --read-only map read-only
+ --exclusive disable automatic exclusive lock transitions
+ --quiesce use quiesce hooks
+ --quiesce-hook arg quiesce hook path
+ --snap-id arg snapshot id
+ -o [ --options ] arg device specific options
+
+ rbd help device unmap
+ usage: rbd device unmap [--device-type <device-type>] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--snap-id <snap-id>]
+ [--options <options>]
+ <image-or-snap-or-device-spec>
+
+ Unmap a rbd device.
+
+ Positional arguments
+ <image-or-snap-or-device-spec> image, snapshot, or device specification
+ [<pool-name>/[<namespace>/]]<image-name>[@<sna
+ p-name>] or <device-path>
+
+ Optional arguments
+ -t [ --device-type ] arg device type [ggate, krbd (default), nbd]
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --snap-id arg snapshot id
+ -o [ --options ] arg device specific options
+
+ rbd help diff
+ usage: rbd diff [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--from-snap <from-snap>] [--whole-object]
+ [--format <format>] [--pretty-format]
+ <image-or-snap-spec>
+
+ Print extents that differ since a previous snap, or image creation.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>])
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --from-snap arg snapshot starting point
+ --whole-object compare whole object
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help disk-usage
+ usage: rbd disk-usage [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>] [--format <format>]
+ [--pretty-format] [--from-snap <from-snap>] [--exact]
+ [--merge-snapshots]
+ <image-or-snap-spec>
+
+ Show disk usage stats for pool, image or snapshot.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>])
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+ --from-snap arg snapshot starting point
+ --exact compute exact disk usage (slow)
+ --merge-snapshots merge snapshot sizes with its image
+
+ rbd help encryption format
+ usage: rbd encryption format [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--cipher-alg <cipher-alg>]
+ <image-spec> <format> <passphrase-file>
+
+ Format image to an encrypted format.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <format> encryption format [possible values: luks1, luks2]
+ <passphrase-file> path of file containing passphrase for unlocking the
+ image
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --cipher-alg arg encryption algorithm [possible values: aes-128, aes-256
+ (default)]
+
+ rbd help export
+ usage: rbd export [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--path <path>] [--no-progress]
+ [--export-format <export-format>]
+ <source-image-or-snap-spec> <path-name>
+
+ Export image to file.
+
+ Positional arguments
+ <source-image-or-snap-spec> source image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-n
+ ame>])
+ <path-name> export file (or '-' for stdout)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --snap arg source snapshot name
+ --path arg export file (or '-' for stdout)
+ --no-progress disable progress output
+ --export-format arg format of image file
+
+ rbd help export-diff
+ usage: rbd export-diff [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>] [--path <path>]
+ [--from-snap <from-snap>] [--whole-object]
+ [--no-progress]
+ <source-image-or-snap-spec> <path-name>
+
+ Export incremental diff to file.
+
+ Positional arguments
+ <source-image-or-snap-spec> source image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-n
+ ame>])
+ <path-name> export file (or '-' for stdout)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --snap arg source snapshot name
+ --path arg export file (or '-' for stdout)
+ --from-snap arg snapshot starting point
+ --whole-object compare whole object
+ --no-progress disable progress output
+
+ rbd help feature disable
+ usage: rbd feature disable [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <features> [<features> ...]
+
+ Disable the specified image feature.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <features> image features
+ [exclusive-lock, object-map, journaling]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help feature enable
+ usage: rbd feature enable [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ [--journal-splay-width <journal-splay-width>]
+ [--journal-object-size <journal-object-size>]
+ [--journal-pool <journal-pool>]
+ <image-spec> <features> [<features> ...]
+
+ Enable the specified image feature.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <features> image features
+ [exclusive-lock, object-map, journaling]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --journal-splay-width arg number of active journal objects
+ --journal-object-size arg size of journal objects [4K <= size <= 64M]
+ --journal-pool arg pool for journal objects
+
+ rbd help flatten
+ usage: rbd flatten [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--no-progress] [--encryption-format <encryption-format>]
+ [--encryption-passphrase-file <encryption-passphrase-file>]
+ <image-spec>
+
+ Fill clone with parent data (make it independent).
+
+ Positional arguments
+ <image-spec> image specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --no-progress disable progress output
+ --encryption-format arg encryption format (luks, luks1, luks2)
+ [default: luks]
+ --encryption-passphrase-file arg path to file containing passphrase for
+ unlocking the image
+
+ rbd help group create
+ usage: rbd group create [--pool <pool>] [--namespace <namespace>]
+ [--group <group>]
+ <group-spec>
+
+ Create a group.
+
+ Positional arguments
+ <group-spec> group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+
+ rbd help group image add
+ usage: rbd group image add [--group-pool <group-pool>]
+ [--group-namespace <group-namespace>]
+ [--group <group>] [--image-pool <image-pool>]
+ [--image-namespace <image-namespace>]
+ [--image <image>] [--pool <pool>]
+ <group-spec> <image-spec>
+
+ Add an image to a group.
+
+ Positional arguments
+ <group-spec> group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ --group-pool arg group pool name
+ --group-namespace arg group namespace name
+ --group arg group name
+ --image-pool arg image pool name
+ --image-namespace arg image namespace name
+ --image arg image name
+ -p [ --pool ] arg pool name unless overridden
+
+ rbd help group image list
+ usage: rbd group image list [--format <format>] [--pretty-format]
+ [--pool <pool>] [--namespace <namespace>]
+ [--group <group>]
+ <group-spec>
+
+ List images in a group.
+
+ Positional arguments
+ <group-spec> group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+
+ Optional arguments
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+
+ rbd help group image remove
+ usage: rbd group image remove [--group-pool <group-pool>]
+ [--group-namespace <group-namespace>]
+ [--group <group>] [--image-pool <image-pool>]
+ [--image-namespace <image-namespace>]
+ [--image <image>] [--pool <pool>]
+ [--image-id <image-id>]
+ <group-spec> <image-spec>
+
+ Remove an image from a group.
+
+ Positional arguments
+ <group-spec> group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ --group-pool arg group pool name
+ --group-namespace arg group namespace name
+ --group arg group name
+ --image-pool arg image pool name
+ --image-namespace arg image namespace name
+ --image arg image name
+ -p [ --pool ] arg pool name unless overridden
+ --image-id arg image id
+
+ rbd help group list
+ usage: rbd group list [--pool <pool>] [--namespace <namespace>]
+ [--format <format>] [--pretty-format]
+ <pool-spec>
+
+ List rbd groups.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help group remove
+ usage: rbd group remove [--pool <pool>] [--namespace <namespace>]
+ [--group <group>]
+ <group-spec>
+
+ Delete a group.
+
+ Positional arguments
+ <group-spec> group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+
+ rbd help group rename
+ usage: rbd group rename [--pool <pool>] [--namespace <namespace>]
+ [--group <group>] [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>]
+ [--dest-group <dest-group>]
+ <source-group-spec> <dest-group-spec>
+
+ Rename a group within pool.
+
+ Positional arguments
+ <source-group-spec> source group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+ <dest-group-spec> destination group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --group arg source group name
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest-group arg destination group name
+
+ rbd help group snap create
+ usage: rbd group snap create [--pool <pool>] [--namespace <namespace>]
+ [--group <group>] [--snap <snap>]
+ [--skip-quiesce] [--ignore-quiesce-error]
+ <group-snap-spec>
+
+ Make a snapshot of a group.
+
+ Positional arguments
+ <group-snap-spec> group specification
+ (example:
+ [<pool-name>/[<namespace>/]]<group-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+ --snap arg snapshot name
+ --skip-quiesce do not run quiesce hooks
+ --ignore-quiesce-error ignore quiesce hook error
+
+ rbd help group snap list
+ usage: rbd group snap list [--format <format>] [--pretty-format]
+ [--pool <pool>] [--namespace <namespace>]
+ [--group <group>]
+ <group-spec>
+
+ List snapshots of a group.
+
+ Positional arguments
+ <group-spec> group specification
+ (example: [<pool-name>/[<namespace>/]]<group-name>)
+
+ Optional arguments
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+
+ rbd help group snap remove
+ usage: rbd group snap remove [--pool <pool>] [--namespace <namespace>]
+ [--group <group>] [--snap <snap>]
+ <group-snap-spec>
+
+ Remove a snapshot from a group.
+
+ Positional arguments
+ <group-snap-spec> group specification
+ (example:
+ [<pool-name>/[<namespace>/]]<group-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+ --snap arg snapshot name
+
+ rbd help group snap rename
+ usage: rbd group snap rename [--pool <pool>] [--namespace <namespace>]
+ [--group <group>] [--snap <snap>]
+ [--dest-snap <dest-snap>]
+ <group-snap-spec> <dest-snap>
+
+ Rename group's snapshot.
+
+ Positional arguments
+ <group-snap-spec> group specification
+ (example:
+ [<pool-name>/[<namespace>/]]<group-name>@<snap-name>)
+ <dest-snap> destination snapshot name
+ (example: <snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+ --snap arg snapshot name
+ --dest-snap arg destination snapshot name
+
+ rbd help group snap rollback
+ usage: rbd group snap rollback [--no-progress] [--pool <pool>]
+ [--namespace <namespace>] [--group <group>]
+ [--snap <snap>]
+ <group-snap-spec>
+
+ Rollback group to snapshot.
+
+ Positional arguments
+ <group-snap-spec> group specification
+ (example:
+ [<pool-name>/[<namespace>/]]<group-name>@<snap-name>)
+
+ Optional arguments
+ --no-progress disable progress output
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+ --snap arg snapshot name
+
+ rbd help image-meta get
+ usage: rbd image-meta get [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <key>
+
+ Image metadata get the value associated with the key.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <key> image meta key
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help image-meta list
+ usage: rbd image-meta list [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--format <format>]
+ [--pretty-format]
+ <image-spec>
+
+ Image metadata list keys with values.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help image-meta remove
+ usage: rbd image-meta remove [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <key>
+
+ Image metadata remove the key and value associated.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <key> image meta key
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help image-meta set
+ usage: rbd image-meta set [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <key> <value>
+
+ Image metadata set key with value.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <key> image meta key
+ <value> image meta value
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help import
+ usage: rbd import [--path <path>] [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>] [--dest <dest>]
+ [--image-format <image-format>] [--new-format]
+ [--order <order>] [--object-size <object-size>]
+ [--image-feature <image-feature>] [--image-shared]
+ [--stripe-unit <stripe-unit>]
+ [--stripe-count <stripe-count>] [--data-pool <data-pool>]
+ [--mirror-image-mode <mirror-image-mode>]
+ [--journal-splay-width <journal-splay-width>]
+ [--journal-object-size <journal-object-size>]
+ [--journal-pool <journal-pool>]
+ [--sparse-size <sparse-size>] [--no-progress]
+ [--export-format <export-format>] [--pool <pool>]
+ [--image <image>]
+ <path-name> <dest-image-spec>
+
+ Import image from file.
+
+ Positional arguments
+ <path-name> import file (or '-' for stdin)
+ <dest-image-spec> destination image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ --path arg import file (or '-' for stdin)
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+ --image-format arg image format [default: 2]
+ --object-size arg object size in B/K/M [4K <= object size <= 32M]
+ --image-feature arg image features
+ [layering(+), exclusive-lock(+*), object-map(+*),
+ deep-flatten(+-), journaling(*)]
+ --image-shared shared image
+ --stripe-unit arg stripe unit in B/K/M
+ --stripe-count arg stripe count
+ --data-pool arg data pool
+ --mirror-image-mode arg mirror image mode [journal or snapshot]
+ --journal-splay-width arg number of active journal objects
+ --journal-object-size arg size of journal objects [4K <= size <= 64M]
+ --journal-pool arg pool for journal objects
+ --sparse-size arg sparse size in B/K/M [default: 4K]
+ --no-progress disable progress output
+ --export-format arg format of image file
+
+ Image Features:
+ (*) supports enabling/disabling on existing images
+ (-) supports disabling-only on existing images
+ (+) enabled by default for new images if features not specified
+
+ rbd help import-diff
+ usage: rbd import-diff [--path <path>] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ [--sparse-size <sparse-size>] [--no-progress]
+ <path-name> <image-spec>
+
+ Import an incremental diff.
+
+ Positional arguments
+ <path-name> import file (or '-' for stdin)
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ --path arg import file (or '-' for stdin)
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --sparse-size arg sparse size in B/K/M [default: 4K]
+ --no-progress disable progress output
+
+ rbd help info
+ usage: rbd info [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--image-id <image-id>] [--format <format>]
+ [--pretty-format]
+ <image-or-snap-spec>
+
+ Show information about image size, striping, etc.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>])
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --image-id arg image id
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help journal client disconnect
+ usage: rbd journal client disconnect [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--journal <journal>]
+ [--client-id <client-id>]
+ <journal-spec>
+
+ Flag image journal client as disconnected.
+
+ Positional arguments
+ <journal-spec> journal specification
+ (example: [<pool-name>/[<namespace>/]]<journal-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --journal arg journal name
+ --client-id arg client ID (or leave unspecified to disconnect all)
+
+ rbd help journal export
+ usage: rbd journal export [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--journal <journal>]
+ [--path <path>] [--verbose] [--no-error]
+ <source-journal-spec> <path-name>
+
+ Export image journal.
+
+ Positional arguments
+ <source-journal-spec> source journal specification
+ (example: [<pool-name>/[<namespace>/]]<journal-name>)
+ <path-name> export file (or '-' for stdout)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --journal arg source journal name
+ --path arg export file (or '-' for stdout)
+ --verbose be verbose
+ --no-error continue after error
+
+ rbd help journal import
+ usage: rbd journal import [--path <path>] [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>] [--dest <dest>]
+ [--dest-journal <dest-journal>] [--verbose]
+ [--no-error]
+ <path-name> <dest-journal-spec>
+
+ Import image journal.
+
+ Positional arguments
+ <path-name> import file (or '-' for stdin)
+ <dest-journal-spec> destination journal specification
+ (example: [<pool-name>/[<namespace>/]]<journal-name>)
+
+ Optional arguments
+ --path arg import file (or '-' for stdin)
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+ --dest-journal arg destination journal name
+ --verbose be verbose
+ --no-error continue after error
+
+ rbd help journal info
+ usage: rbd journal info [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--journal <journal>]
+ [--format <format>] [--pretty-format]
+ <journal-spec>
+
+ Show information about image journal.
+
+ Positional arguments
+ <journal-spec> journal specification
+ (example: [<pool-name>/[<namespace>/]]<journal-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --journal arg journal name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help journal inspect
+ usage: rbd journal inspect [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--journal <journal>] [--verbose]
+ <journal-spec>
+
+ Inspect image journal for structural errors.
+
+ Positional arguments
+ <journal-spec> journal specification
+ (example: [<pool-name>/[<namespace>/]]<journal-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --journal arg journal name
+ --verbose be verbose
+
+ rbd help journal reset
+ usage: rbd journal reset [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--journal <journal>]
+ <journal-spec>
+
+ Reset image journal.
+
+ Positional arguments
+ <journal-spec> journal specification
+ (example: [<pool-name>/[<namespace>/]]<journal-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --journal arg journal name
+
+ rbd help journal status
+ usage: rbd journal status [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--journal <journal>]
+ [--format <format>] [--pretty-format]
+ <journal-spec>
+
+ Show status of image journal.
+
+ Positional arguments
+ <journal-spec> journal specification
+ (example: [<pool-name>/[<namespace>/]]<journal-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --journal arg journal name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help list
+ usage: rbd list [--long] [--pool <pool>] [--namespace <namespace>]
+ [--format <format>] [--pretty-format]
+ <pool-spec>
+
+ List rbd images.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -l [ --long ] long listing format
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help lock add
+ usage: rbd lock add [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--shared <shared>]
+ <image-spec> <lock-id>
+
+ Take a lock on an image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <lock-id> unique lock id
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --shared arg shared lock tag
+
+ rbd help lock list
+ usage: rbd lock list [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--format <format>] [--pretty-format]
+ <image-spec>
+
+ Show locks held on an image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help lock remove
+ usage: rbd lock remove [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> <lock-id> <locker>
+
+ Release a lock on an image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <lock-id> unique lock id
+ <locker> locker client
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help merge-diff
+ usage: rbd merge-diff [--path <path>] [--no-progress]
+ <diff1-path> <diff2-path> <path-name>
+
+ Merge two diff exports together.
+
+ Positional arguments
+ <diff1-path> path to first diff (or '-' for stdin)
+ <diff2-path> path to second diff
+ <path-name> path to merged diff (or '-' for stdout)
+
+ Optional arguments
+ --path arg path to merged diff (or '-' for stdout)
+ --no-progress disable progress output
+
+ rbd help migration abort
+ usage: rbd migration abort [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--no-progress]
+ <image-spec>
+
+ Cancel interrupted image migration.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --no-progress disable progress output
+
+ rbd help migration commit
+ usage: rbd migration commit [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--no-progress] [--force]
+ <image-spec>
+
+ Commit image migration.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --no-progress disable progress output
+ --force proceed even if the image has children
+
+ rbd help migration execute
+ usage: rbd migration execute [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--no-progress]
+ <image-spec>
+
+ Execute image migration.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --no-progress disable progress output
+
+ rbd help migration prepare
+ usage: rbd migration prepare [--import-only]
+ [--source-spec-path <source-spec-path>]
+ [--source-spec <source-spec>] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ [--snap <snap>] [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>]
+ [--dest <dest>] [--image-format <image-format>]
+ [--new-format] [--order <order>]
+ [--object-size <object-size>]
+ [--image-feature <image-feature>]
+ [--image-shared] [--stripe-unit <stripe-unit>]
+ [--stripe-count <stripe-count>]
+ [--data-pool <data-pool>]
+ [--mirror-image-mode <mirror-image-mode>]
+ [--journal-splay-width <journal-splay-width>]
+ [--journal-object-size <journal-object-size>]
+ [--journal-pool <journal-pool>] [--flatten]
+ <source-image-or-snap-spec> <dest-image-spec>
+
+ Prepare image migration.
+
+ Positional arguments
+ <source-image-or-snap-spec> source image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-n
+ ame>])
+ <dest-image-spec> destination image specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ --import-only only import data from source
+ --source-spec-path arg source-spec file (or '-' for stdin)
+ --source-spec arg source-spec
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --snap arg source snapshot name
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+ --image-format arg image format [default: 2]
+ --object-size arg object size in B/K/M [4K <= object size <= 32M]
+ --image-feature arg image features
+ [layering(+), exclusive-lock(+*),
+ object-map(+*), deep-flatten(+-), journaling(*)]
+ --image-shared shared image
+ --stripe-unit arg stripe unit in B/K/M
+ --stripe-count arg stripe count
+ --data-pool arg data pool
+ --mirror-image-mode arg mirror image mode [journal or snapshot]
+ --journal-splay-width arg number of active journal objects
+ --journal-object-size arg size of journal objects [4K <= size <= 64M]
+ --journal-pool arg pool for journal objects
+ --flatten fill clone with parent data (make it independent)
+
+ Image Features:
+ (*) supports enabling/disabling on existing images
+ (-) supports disabling-only on existing images
+ (+) enabled by default for new images if features not specified
+
+ rbd help mirror image demote
+ usage: rbd mirror image demote [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec>
+
+ Demote an image to non-primary for RBD mirroring.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help mirror image disable
+ usage: rbd mirror image disable [--force] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ <image-spec>
+
+ Disable RBD mirroring for an image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ --force disable even if not primary
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help mirror image enable
+ usage: rbd mirror image enable [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec> [<mode>]
+
+ Enable RBD mirroring for an image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <mode> mirror image mode (journal or snapshot) [default:
+ journal]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help mirror image promote
+ usage: rbd mirror image promote [--force] [--pool <pool>]
+ [--namespace <namespace>] [--image <image>]
+ <image-spec>
+
+ Promote an image to primary for RBD mirroring.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ --force promote even if not cleanly demoted by remote cluster
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help mirror image resync
+ usage: rbd mirror image resync [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec>
+
+ Force resync to primary image for RBD mirroring.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help mirror image snapshot
+ usage: rbd mirror image snapshot [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--skip-quiesce]
+ [--ignore-quiesce-error]
+ <image-spec>
+
+ Create RBD mirroring image snapshot.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --skip-quiesce do not run quiesce hooks
+ --ignore-quiesce-error ignore quiesce hook error
+
+ rbd help mirror image status
+ usage: rbd mirror image status [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--format <format>]
+ [--pretty-format]
+ <image-spec>
+
+ Show RBD mirroring status for an image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help mirror pool demote
+ usage: rbd mirror pool demote [--pool <pool>] [--namespace <namespace>]
+ <pool-spec>
+
+ Demote all primary images in the pool.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help mirror pool disable
+ usage: rbd mirror pool disable [--pool <pool>] [--namespace <namespace>]
+ <pool-spec>
+
+ Disable RBD mirroring by default within a pool.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help mirror pool enable
+ usage: rbd mirror pool enable [--pool <pool>] [--namespace <namespace>]
+ [--site-name <site-name>]
+ <pool-spec> <mode>
+
+ Enable RBD mirroring by default within a pool.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+ <mode> mirror mode [image or pool]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --site-name arg local site name
+
+ rbd help mirror pool info
+ usage: rbd mirror pool info [--pool <pool>] [--namespace <namespace>]
+ [--format <format>] [--pretty-format] [--all]
+ <pool-spec>
+
+ Show information about the pool mirroring configuration.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+ --all list all attributes
+
+ rbd help mirror pool peer add
+ usage: rbd mirror pool peer add [--pool <pool>]
+ [--remote-client-name <remote-client-name>]
+ [--remote-cluster <remote-cluster>]
+ [--remote-mon-host <remote-mon-host>]
+ [--remote-key-file <remote-key-file>]
+ [--direction <direction>]
+ <pool-name> <remote-cluster-spec>
+
+ Add a mirroring peer to a pool.
+
+ Positional arguments
+ <pool-name> pool name
+ <remote-cluster-spec> remote cluster spec
+ (example: [<client name>@]<cluster name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --remote-client-name arg remote client name
+ --remote-cluster arg remote cluster name
+ --remote-mon-host arg remote mon host(s)
+ --remote-key-file arg path to file containing remote key
+ --direction arg mirroring direction (rx-only, rx-tx)
+ [default: rx-tx]
+
+ rbd help mirror pool peer bootstrap create
+ usage: rbd mirror pool peer bootstrap create
+ [--pool <pool>] [--site-name <site-name>]
+ <pool-name>
+
+ Create a peer bootstrap token to import in a remote cluster
+
+ Positional arguments
+ <pool-name> pool name
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --site-name arg local site name
+
+ rbd help mirror pool peer bootstrap import
+ usage: rbd mirror pool peer bootstrap import
+ [--pool <pool>]
+ [--site-name <site-name>]
+ [--token-path <token-path>]
+ [--direction <direction>]
+ <pool-name> <token-path>
+
+ Import a peer bootstrap token created from a remote cluster
+
+ Positional arguments
+ <pool-name> pool name
+ <token-path> bootstrap token file (or '-' for stdin)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --site-name arg local site name
+ --token-path arg bootstrap token file (or '-' for stdin)
+ --direction arg mirroring direction (rx-only, rx-tx)
+ [default: rx-tx]
+
+ rbd help mirror pool peer remove
+ usage: rbd mirror pool peer remove [--pool <pool>]
+ <pool-name> <uuid>
+
+ Remove a mirroring peer from a pool.
+
+ Positional arguments
+ <pool-name> pool name
+ <uuid> peer uuid
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+
+ rbd help mirror pool peer set
+ usage: rbd mirror pool peer set [--pool <pool>]
+ <pool-name> <uuid> <key> <value>
+
+ Update mirroring peer settings.
+
+ Positional arguments
+ <pool-name> pool name
+ <uuid> peer uuid
+ <key> peer parameter
+ (direction, site-name, client, mon-host, key-file)
+ <value> new value for specified key
+ (rx-only, tx-only, or rx-tx for direction)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+
+ rbd help mirror pool promote
+ usage: rbd mirror pool promote [--force] [--pool <pool>]
+ [--namespace <namespace>]
+ <pool-spec>
+
+ Promote all non-primary images in the pool.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ --force promote even if not cleanly demoted by remote cluster
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help mirror pool status
+ usage: rbd mirror pool status [--pool <pool>] [--namespace <namespace>]
+ [--format <format>] [--pretty-format] [--verbose]
+ <pool-spec>
+
+ Show status for all mirrored images in the pool.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+ --verbose be verbose
+
+ rbd help mirror snapshot schedule add
+ usage: rbd mirror snapshot schedule add
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--image <image>]
+ <interval> [<start-time>]
+
+ Add mirror snapshot schedule.
+
+ Positional arguments
+ <interval> schedule interval
+ <start-time> schedule start time
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help mirror snapshot schedule list
+ usage: rbd mirror snapshot schedule list
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--image <image>] [--recursive]
+ [--format <format>] [--pretty-format]
+
+ List mirror snapshot schedule.
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ -R [ --recursive ] list all schedules
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help mirror snapshot schedule remove
+ usage: rbd mirror snapshot schedule remove
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--image <image>]
+ [<interval>] [<start-time>]
+
+ Remove mirror snapshot schedule.
+
+ Positional arguments
+ <interval> schedule interval
+ <start-time> schedule start time
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help mirror snapshot schedule status
+ usage: rbd mirror snapshot schedule status
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--image <image>] [--format <format>]
+ [--pretty-format]
+
+ Show mirror snapshot schedule status.
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help namespace create
+ usage: rbd namespace create [--pool <pool>] [--namespace <namespace>]
+ <pool-spec>
+
+ Create an RBD image namespace.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help namespace list
+ usage: rbd namespace list [--pool <pool>] [--format <format>] [--pretty-format]
+ <pool-name>
+
+ List RBD image namespaces.
+
+ Positional arguments
+ <pool-name> pool name
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help namespace remove
+ usage: rbd namespace remove [--pool <pool>] [--namespace <namespace>]
+ <pool-spec>
+
+ Remove an RBD image namespace.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help object-map check
+ usage: rbd object-map check [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>] [--no-progress]
+ <image-or-snap-spec>
+
+ Verify the object map is correct.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>])
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --no-progress disable progress output
+
+ rbd help object-map rebuild
+ usage: rbd object-map rebuild [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>] [--no-progress]
+ <image-or-snap-spec>
+
+ Rebuild an invalid object map.
+
+ Positional arguments
+ <image-or-snap-spec> image or snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>[@<snap-name>])
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --no-progress disable progress output
+
+ rbd help perf image iostat
+ usage: rbd perf image iostat [--pool <pool>] [--namespace <namespace>]
+ [--iterations <iterations>] [--sort-by <sort-by>]
+ [--format <format>] [--pretty-format]
+ <pool-spec>
+
+ Display image IO statistics.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --iterations arg iterations of metric collection [> 0]
+ --sort-by arg (=write_ops) sort-by IO metric (write-ops, read-ops,
+ write-bytes, read-bytes, write-latency,
+ read-latency) [default: write-ops]
+ --format arg output format (plain, json, or xml) [default:
+ plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help perf image iotop
+ usage: rbd perf image iotop [--pool <pool>] [--namespace <namespace>]
+ <pool-spec>
+
+ Display a top-like IO monitor.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help persistent-cache flush
+ usage: rbd persistent-cache flush [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--image-id <image-id>]
+ <image-spec>
+
+ Flush persistent cache.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --image-id arg image id
+
+ rbd help persistent-cache invalidate
+ usage: rbd persistent-cache invalidate
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--image <image>] [--image-id <image-id>]
+ <image-spec>
+
+ Invalidate (discard) existing / dirty persistent cache.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --image-id arg image id
+
+ rbd help pool init
+ usage: rbd pool init [--pool <pool>] [--force]
+ <pool-name>
+
+ Initialize pool for use by RBD.
+
+ Positional arguments
+ <pool-name> pool name
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --force force initialize pool for RBD use if registered by
+ another application
+
+ rbd help pool stats
+ usage: rbd pool stats [--pool <pool>] [--namespace <namespace>]
+ [--format <format>] [--pretty-format]
+ <pool-spec>
+
+ Display pool statistics.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ Note: legacy v1 images are not included in stats
+ rbd help remove
+ usage: rbd remove [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--no-progress]
+ <image-spec>
+
+ Delete an image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --no-progress disable progress output
+
+ rbd help rename
+ usage: rbd rename [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>] [--dest <dest>]
+ <source-image-spec> <dest-image-spec>
+
+ Rename image within pool.
+
+ Positional arguments
+ <source-image-spec> source image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+ <dest-image-spec> destination image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+
+ rbd help resize
+ usage: rbd resize [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] --size <size> [--allow-shrink]
+ [--no-progress] [--encryption-format <encryption-format>]
+ [--encryption-passphrase-file <encryption-passphrase-file>]
+ <image-spec>
+
+ Resize (expand or shrink) image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ -s [ --size ] arg image size (in M/G/T) [default: M]
+ --allow-shrink permit shrinking
+ --no-progress disable progress output
+ --encryption-format arg encryption format (luks, luks1, luks2)
+ [default: luks]
+ --encryption-passphrase-file arg path to file containing passphrase for
+ unlocking the image
+
+ rbd help snap create
+ usage: rbd snap create [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>] [--skip-quiesce]
+ [--ignore-quiesce-error] [--no-progress]
+ <snap-spec>
+
+ Create a snapshot.
+
+ Positional arguments
+ <snap-spec> snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --skip-quiesce do not run quiesce hooks
+ --ignore-quiesce-error ignore quiesce hook error
+ --no-progress disable progress output
+
+ rbd help snap limit clear
+ usage: rbd snap limit clear [--pool <pool>] [--namespace <namespace>]
+ [--image <image>]
+ <image-spec>
+
+ Remove snapshot limit.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+ rbd help snap limit set
+ usage: rbd snap limit set [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--limit <limit>]
+ <image-spec>
+
+ Limit the number of snapshots.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --limit arg maximum allowed snapshot count
+
+ rbd help snap list
+ usage: rbd snap list [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--image-id <image-id>]
+ [--format <format>] [--pretty-format] [--all]
+ <image-spec>
+
+ Dump list of image snapshots.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --image-id arg image id
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+ -a [ --all ] list snapshots from all namespaces
+
+ rbd help snap protect
+ usage: rbd snap protect [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>]
+ <snap-spec>
+
+ Prevent a snapshot from being deleted.
+
+ Positional arguments
+ <snap-spec> snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+
+ rbd help snap purge
+ usage: rbd snap purge [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--image-id <image-id>] [--no-progress]
+ <image-spec>
+
+ Delete all unprotected snapshots.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --image-id arg image id
+ --no-progress disable progress output
+
+ rbd help snap remove
+ usage: rbd snap remove [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>]
+ [--image-id <image-id>] [--snap-id <snap-id>]
+ [--no-progress] [--force]
+ <snap-spec>
+
+ Delete a snapshot.
+
+ Positional arguments
+ <snap-spec> snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --image-id arg image id
+ --snap-id arg snapshot id
+ --no-progress disable progress output
+ --force flatten children and unprotect snapshot if needed.
+
+ rbd help snap rename
+ usage: rbd snap rename [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>]
+ [--dest-pool <dest-pool>]
+ [--dest-namespace <dest-namespace>] [--dest <dest>]
+ [--dest-snap <dest-snap>]
+ <source-snap-spec> <dest-snap-spec>
+
+ Rename a snapshot.
+
+ Positional arguments
+ <source-snap-spec> source snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>)
+ <dest-snap-spec> destination snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg source pool name
+ --namespace arg source namespace name
+ --image arg source image name
+ --snap arg source snapshot name
+ --dest-pool arg destination pool name
+ --dest-namespace arg destination namespace name
+ --dest arg destination image name
+ --dest-snap arg destination snapshot name
+
+ rbd help snap rollback
+ usage: rbd snap rollback [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>] [--no-progress]
+ <snap-spec>
+
+ Rollback image to snapshot.
+
+ Positional arguments
+ <snap-spec> snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --no-progress disable progress output
+
+ rbd help snap unprotect
+ usage: rbd snap unprotect [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--snap <snap>]
+ [--image-id <image-id>]
+ <snap-spec>
+
+ Allow a snapshot to be deleted.
+
+ Positional arguments
+ <snap-spec> snapshot specification
+ (example:
+ [<pool-name>/[<namespace>/]]<image-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --snap arg snapshot name
+ --image-id arg image id
+
+ rbd help sparsify
+ usage: rbd sparsify [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--no-progress]
+ [--sparse-size <sparse-size>]
+ <image-spec>
+
+ Reclaim space for zeroed image extents.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --no-progress disable progress output
+ --sparse-size arg sparse size in B/K/M [default: 4K]
+
+ rbd help status
+ usage: rbd status [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ [--format <format>] [--pretty-format]
+ <image-spec>
+
+ Show the status of this image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help trash list
+ usage: rbd trash list [--pool <pool>] [--namespace <namespace>] [--all]
+ [--long] [--format <format>] [--pretty-format]
+ <pool-spec>
+
+ List trash images.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ -a [ --all ] list images from all sources
+ -l [ --long ] long listing format
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help trash move
+ usage: rbd trash move [--pool <pool>] [--namespace <namespace>]
+ [--image <image>] [--expires-at <expires-at>]
+ <image-spec>
+
+ Move an image to the trash.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+ --expires-at arg (=now) set the expiration time of an image so it can be
+ purged when it is stale
+
+ rbd help trash purge
+ usage: rbd trash purge [--pool <pool>] [--namespace <namespace>]
+ [--no-progress] [--expired-before <expired-before>]
+ [--threshold <threshold>]
+ <pool-spec>
+
+ Remove all expired images from trash.
+
+ Positional arguments
+ <pool-spec> pool specification
+ (example: <pool-name>[/<namespace>]
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --no-progress disable progress output
+ --expired-before date purges images that expired before the given date
+ --threshold arg purges images until the current pool data usage is
+ reduced to X%, value range: 0.0-1.0
+
+ rbd help trash purge schedule add
+ usage: rbd trash purge schedule add [--pool <pool>] [--namespace <namespace>]
+ <interval> [<start-time>]
+
+ Add trash purge schedule.
+
+ Positional arguments
+ <interval> schedule interval
+ <start-time> schedule start time
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help trash purge schedule list
+ usage: rbd trash purge schedule list [--pool <pool>] [--namespace <namespace>]
+ [--recursive] [--format <format>]
+ [--pretty-format]
+
+ List trash purge schedule.
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ -R [ --recursive ] list all schedules
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help trash purge schedule remove
+ usage: rbd trash purge schedule remove
+ [--pool <pool>] [--namespace <namespace>]
+ [<interval>] [<start-time>]
+
+ Remove trash purge schedule.
+
+ Positional arguments
+ <interval> schedule interval
+ <start-time> schedule start time
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+
+ rbd help trash purge schedule status
+ usage: rbd trash purge schedule status
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--format <format>] [--pretty-format]
+
+ Show trash purge schedule status.
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help trash remove
+ usage: rbd trash remove [--pool <pool>] [--namespace <namespace>]
+ [--image-id <image-id>] [--no-progress] [--force]
+ <image-id>
+
+ Remove an image from trash.
+
+ Positional arguments
+ <image-id> image id
+ (example: [<pool-name>/[<namespace>/]]<image-id>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image-id arg image id
+ --no-progress disable progress output
+ --force force remove of non-expired delayed images
+
+ rbd help trash restore
+ usage: rbd trash restore [--pool <pool>] [--namespace <namespace>]
+ [--image-id <image-id>] [--image <image>]
+ <image-id>
+
+ Restore an image from trash.
+
+ Positional arguments
+ <image-id> image id
+ (example: [<pool-name>/]<image-id>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image-id arg image id
+ --image arg image name
+
+ rbd help watch
+ usage: rbd watch [--pool <pool>] [--namespace <namespace>] [--image <image>]
+ <image-spec>
+
+ Watch events on image.
+
+ Positional arguments
+ <image-spec> image specification
+ (example: [<pool-name>/[<namespace>/]]<image-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --image arg image name
+
+
diff --git a/src/test/cli/rbd/invalid-snap-usage.t b/src/test/cli/rbd/invalid-snap-usage.t
new file mode 100644
index 000000000..920c3c223
--- /dev/null
+++ b/src/test/cli/rbd/invalid-snap-usage.t
@@ -0,0 +1,115 @@
+ $ rbd create foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd flatten foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd resize foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd rm foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd import-diff /tmp/diff foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd mv foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd mv foo@snap bar
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd mv foo@snap bar@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta list foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta get foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta get foo@snap key
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta set foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta set foo@snap key
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta set foo@snap key val
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta remove foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd image-meta remove foo@snap key
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd snap ls foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd snap purge foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd watch foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd status foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd feature disable foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd feature disable foo@snap layering
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd feature enable foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd feature enable foo@snap layering
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd lock list foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd lock add foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd lock add foo@snap id
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd lock remove foo@snap
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd lock remove foo@snap id
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd lock remove foo@snap id client.1234
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd bench foo@snap --io-type write
+ rbd: snapshot name specified for a command that doesn't use it
+ [22]
+
+ $ rbd clone foo@snap bar@snap
+ rbd: destination snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd import /bin/ls ls@snap
+ rbd: destination snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd cp foo bar@snap
+ rbd: destination snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd cp foo@snap bar@snap
+ rbd: destination snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd deep cp foo bar@snap
+ rbd: destination snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd deep cp foo@snap bar@snap
+ rbd: destination snapshot name specified for a command that doesn't use it
+ [22]
+ $ rbd mv foo bar@snap
+ rbd: destination snapshot name specified for a command that doesn't use it
+ [22]
diff --git a/src/test/cli/rbd/not-enough-args.t b/src/test/cli/rbd/not-enough-args.t
new file mode 100644
index 000000000..3f613e6bb
--- /dev/null
+++ b/src/test/cli/rbd/not-enough-args.t
@@ -0,0 +1,222 @@
+ $ rbd info
+ rbd: image name was not specified
+ [22]
+ $ rbd create
+ rbd: image name was not specified
+ [22]
+ $ rbd clone
+ rbd: image name was not specified
+ [22]
+ $ rbd clone foo
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd clone foo@snap
+ rbd: destination image name was not specified
+ [22]
+ $ rbd clone foo bar
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd clone foo bar@snap
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd children
+ rbd: image name was not specified
+ [22]
+ $ rbd flatten
+ rbd: image name was not specified
+ [22]
+ $ rbd resize
+ rbd: image name was not specified
+ [22]
+ $ rbd rm
+ rbd: image name was not specified
+ [22]
+ $ rbd export
+ rbd: image name was not specified
+ [22]
+ $ rbd import
+ rbd: path was not specified
+ [22]
+ $ rbd diff
+ rbd: image name was not specified
+ [22]
+ $ rbd export-diff
+ rbd: image name was not specified
+ [22]
+ $ rbd export-diff foo
+ rbd: path was not specified
+ [22]
+ $ rbd export-diff foo@snap
+ rbd: path was not specified
+ [22]
+ $ rbd merge-diff
+ rbd: first diff was not specified
+ [22]
+ $ rbd merge-diff /tmp/diff1
+ rbd: second diff was not specified
+ [22]
+ $ rbd merge-diff /tmp/diff1 /tmp/diff2
+ rbd: path was not specified
+ [22]
+ $ rbd import-diff
+ rbd: path was not specified
+ [22]
+ $ rbd import-diff /tmp/diff
+ rbd: image name was not specified
+ [22]
+ $ rbd cp
+ rbd: image name was not specified
+ [22]
+ $ rbd cp foo
+ rbd: destination image name was not specified
+ [22]
+ $ rbd cp foo@snap
+ rbd: destination image name was not specified
+ [22]
+ $ rbd deep cp
+ rbd: image name was not specified
+ [22]
+ $ rbd deep cp foo
+ rbd: destination image name was not specified
+ [22]
+ $ rbd deep cp foo@snap
+ rbd: destination image name was not specified
+ [22]
+ $ rbd mv
+ rbd: image name was not specified
+ [22]
+ $ rbd mv foo
+ rbd: destination image name was not specified
+ [22]
+ $ rbd image-meta list
+ rbd: image name was not specified
+ [22]
+ $ rbd image-meta get
+ rbd: image name was not specified
+ [22]
+ $ rbd image-meta get foo
+ rbd: metadata key was not specified
+ [22]
+ $ rbd image-meta set
+ rbd: image name was not specified
+ [22]
+ $ rbd image-meta set foo
+ rbd: metadata key was not specified
+ [22]
+ $ rbd image-meta set foo key
+ rbd: metadata value was not specified
+ [22]
+ $ rbd image-meta remove
+ rbd: image name was not specified
+ [22]
+ $ rbd image-meta remove foo
+ rbd: metadata key was not specified
+ [22]
+ $ rbd object-map rebuild
+ rbd: image name was not specified
+ [22]
+ $ rbd snap ls
+ rbd: image name was not specified
+ [22]
+ $ rbd snap create
+ rbd: image name was not specified
+ [22]
+ $ rbd snap create foo
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd snap rollback
+ rbd: image name was not specified
+ [22]
+ $ rbd snap rollback foo
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd snap rm
+ rbd: image name was not specified
+ [22]
+ $ rbd snap rm foo
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd snap purge
+ rbd: image name was not specified
+ [22]
+ $ rbd snap protect
+ rbd: image name was not specified
+ [22]
+ $ rbd snap protect foo
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd snap unprotect
+ rbd: image name was not specified
+ [22]
+ $ rbd snap unprotect foo
+ rbd: snapshot name was not specified
+ [22]
+ $ rbd watch
+ rbd: image name was not specified
+ [22]
+ $ rbd status
+ rbd: image name was not specified
+ [22]
+ $ rbd device map
+ rbd: image name was not specified
+ [22]
+ $ rbd device unmap
+ rbd: unmap requires either image name or device path
+ [22]
+ $ rbd feature disable
+ rbd: image name was not specified
+ [22]
+ $ rbd feature disable foo
+ rbd: at least one feature name must be specified
+ [22]
+ $ rbd feature enable
+ rbd: image name was not specified
+ [22]
+ $ rbd feature enable foo
+ rbd: at least one feature name must be specified
+ [22]
+ $ rbd lock list
+ rbd: image name was not specified
+ [22]
+ $ rbd lock add
+ rbd: image name was not specified
+ [22]
+ $ rbd lock add foo
+ rbd: lock id was not specified
+ [22]
+ $ rbd lock remove
+ rbd: image name was not specified
+ [22]
+ $ rbd lock remove foo
+ rbd: lock id was not specified
+ [22]
+ $ rbd lock remove foo id
+ rbd: locker was not specified
+ [22]
+ $ rbd bench --io-type write
+ rbd: image name was not specified
+ [22]
+ $ rbd mirror pool enable rbd
+ rbd: must specify 'image' or 'pool' mode.
+ [22]
+ $ rbd mirror pool peer add rbd
+ rbd: remote cluster was not specified
+ [22]
+ $ rbd mirror pool peer remove rbd
+ rbd: must specify peer uuid
+ [22]
+ $ rbd mirror image demote
+ rbd: image name was not specified
+ [22]
+ $ rbd mirror image disable
+ rbd: image name was not specified
+ [22]
+ $ rbd mirror image enable
+ rbd: image name was not specified
+ [22]
+ $ rbd mirror image promote
+ rbd: image name was not specified
+ [22]
+ $ rbd mirror image resync
+ rbd: image name was not specified
+ [22]
diff --git a/src/test/cli/rbd/too-many-args.t b/src/test/cli/rbd/too-many-args.t
new file mode 100644
index 000000000..cf075fb30
--- /dev/null
+++ b/src/test/cli/rbd/too-many-args.t
@@ -0,0 +1,33 @@
+A command taking no args:
+
+ $ rbd device list junk
+ rbd: too many arguments
+ [1]
+
+A command taking one arg:
+
+ $ rbd info img1 junk
+ rbd: too many arguments
+ [1]
+
+A command taking two args:
+
+ $ rbd copy img1 img2 junk
+ rbd: too many arguments
+ [1]
+
+A command taking three args:
+
+ $ rbd lock remove img1 lock1 locker1 junk
+ rbd: too many arguments
+ [1]
+
+A command taking unlimited args:
+
+ $ rbd feature enable img1 layering striping exclusive-lock object-map fast-diff deep-flatten journaling junk
+ rbd: the argument for option is invalid
+ [1]
+
+ $ rbd feature disable img1 layering striping exclusive-lock object-map fast-diff deep-flatten journaling junk
+ rbd: the argument for option is invalid
+ [1]
diff --git a/src/test/client/CMakeLists.txt b/src/test/client/CMakeLists.txt
new file mode 100644
index 000000000..1937bdd0b
--- /dev/null
+++ b/src/test/client/CMakeLists.txt
@@ -0,0 +1,17 @@
+if(${WITH_CEPHFS})
+ add_executable(ceph_test_client
+ main.cc
+ alternate_name.cc
+ ops.cc
+ )
+ target_link_libraries(ceph_test_client
+ client
+ global
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_client
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(${WITH_CEPHFS})
diff --git a/src/test/client/TestClient.h b/src/test/client/TestClient.h
new file mode 100644
index 000000000..bf3b274af
--- /dev/null
+++ b/src/test/client/TestClient.h
@@ -0,0 +1,150 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2021 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+
+#include "common/async/context_pool.h"
+#include "global/global_context.h"
+
+#include "msg/Messenger.h"
+#include "mon/MonClient.h"
+#include "osdc/ObjectCacher.h"
+#include "client/MetaRequest.h"
+#include "client/Client.h"
+#include "messages/MClientReclaim.h"
+#include "messages/MClientSession.h"
+#include "common/async/blocked_completion.h"
+
+#define dout_subsys ceph_subsys_client
+
+namespace bs = boost::system;
+namespace ca = ceph::async;
+
+class ClientScaffold : public Client {
+public:
+ ClientScaffold(Messenger *m, MonClient *mc, Objecter *objecter_) : Client(m, mc, objecter_) {}
+ virtual ~ClientScaffold()
+ { }
+ int check_dummy_op(const UserPerm& perms){
+ RWRef_t mref_reader(mount_state, CLIENT_MOUNTING);
+ if (!mref_reader.is_state_satisfied()) {
+ return -CEPHFS_ENOTCONN;
+ }
+ std::scoped_lock l(client_lock);
+ MetaRequest *req = new MetaRequest(CEPH_MDS_OP_DUMMY);
+ int res = make_request(req, perms);
+ ldout(cct, 10) << __func__ << " result=" << res << dendl;
+ return res;
+ }
+ int send_unknown_session_op(int op) {
+ RWRef_t mref_reader(mount_state, CLIENT_MOUNTING);
+ if (!mref_reader.is_state_satisfied()) {
+ return -CEPHFS_ENOTCONN;
+ }
+ std::scoped_lock l(client_lock);
+ auto session = _get_or_open_mds_session(0);
+ auto msg = make_message<MClientSession>(op, session->seq);
+ int res = session->con->send_message2(std::move(msg));
+ ldout(cct, 10) << __func__ << " result=" << res << dendl;
+ return res;
+ }
+ bool check_client_blocklisted() {
+ RWRef_t mref_reader(mount_state, CLIENT_MOUNTING);
+ if (!mref_reader.is_state_satisfied()) {
+ return -CEPHFS_ENOTCONN;
+ }
+ std::scoped_lock l(client_lock);
+ bs::error_code ec;
+ ldout(cct, 20) << __func__ << ": waiting for latest osdmap" << dendl;
+ objecter->wait_for_latest_osdmap(ca::use_blocked[ec]);
+ ldout(cct, 20) << __func__ << ": got latest osdmap: " << ec << dendl;
+ const auto myaddrs = messenger->get_myaddrs();
+ return objecter->with_osdmap([&](const OSDMap& o) {return o.is_blocklisted(myaddrs);});
+ }
+ bool check_unknown_reclaim_flag(uint32_t flag) {
+ RWRef_t mref_reader(mount_state, CLIENT_MOUNTING);
+ if (!mref_reader.is_state_satisfied()) {
+ return -CEPHFS_ENOTCONN;
+ }
+ std::scoped_lock l(client_lock);
+ char uuid[256];
+ sprintf(uuid, "unknownreclaimflag:%x", getpid());
+ auto session = _get_or_open_mds_session(0);
+ auto m = make_message<MClientReclaim>(uuid, flag);
+ ceph_assert(session->con->send_message2(std::move(m)) == 0);
+ wait_on_list(waiting_for_reclaim);
+ return session->reclaim_state == MetaSession::RECLAIM_FAIL ? true : false;
+ }
+};
+
+class TestClient : public ::testing::Test {
+public:
+ static void SetUpTestSuite() {
+ icp.start(g_ceph_context->_conf.get_val<std::uint64_t>("client_asio_thread_count"));
+ }
+ static void TearDownTestSuite() {
+ icp.stop();
+ }
+ void SetUp() override {
+ messenger = Messenger::create_client_messenger(g_ceph_context, "client");
+ if (messenger->start() != 0) {
+ throw std::runtime_error("failed to start messenger");
+ }
+
+ mc = new MonClient(g_ceph_context, icp);
+ if (mc->build_initial_monmap() < 0) {
+ throw std::runtime_error("build monmap");
+ }
+ mc->set_messenger(messenger);
+ mc->set_want_keys(CEPH_ENTITY_TYPE_MDS | CEPH_ENTITY_TYPE_OSD);
+ if (mc->init() < 0) {
+ throw std::runtime_error("init monclient");
+ }
+
+ objecter = new Objecter(g_ceph_context, messenger, mc, icp);
+ objecter->set_client_incarnation(0);
+ objecter->init();
+ messenger->add_dispatcher_tail(objecter);
+ objecter->start();
+
+ client = new ClientScaffold(messenger, mc, objecter);
+ client->init();
+ client->mount("/", myperm, true);
+ }
+ void TearDown() override {
+ if (client->is_mounted())
+ client->unmount();
+ client->shutdown();
+ objecter->shutdown();
+ mc->shutdown();
+ messenger->shutdown();
+ messenger->wait();
+
+ delete client;
+ client = nullptr;
+ delete objecter;
+ objecter = nullptr;
+ delete mc;
+ mc = nullptr;
+ delete messenger;
+ messenger = nullptr;
+ }
+protected:
+ static inline ceph::async::io_context_pool icp;
+ static inline UserPerm myperm{0,0};
+ MonClient* mc = nullptr;
+ Messenger* messenger = nullptr;
+ Objecter* objecter = nullptr;
+ ClientScaffold* client = nullptr;
+};
diff --git a/src/test/client/alternate_name.cc b/src/test/client/alternate_name.cc
new file mode 100644
index 000000000..43f428012
--- /dev/null
+++ b/src/test/client/alternate_name.cc
@@ -0,0 +1,197 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2021 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <errno.h>
+
+#include <iostream>
+#include <string>
+
+#include <fmt/format.h>
+
+#include "test/client/TestClient.h"
+
+TEST_F(TestClient, AlternateNameRemount) {
+ auto altname = std::string("foo");
+ auto dir = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ ASSERT_EQ(0, client->mkdir(dir.c_str(), 0777, myperm, altname));
+
+ client->unmount();
+ TearDown();
+ SetUp();
+ client->mount("/", myperm, true);
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(dir.c_str(), &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname);
+ }
+
+ ASSERT_EQ(0, client->rmdir(dir.c_str(), myperm));
+}
+
+
+TEST_F(TestClient, AlternateNameMkdir) {
+ auto dir = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ ASSERT_EQ(0, client->mkdir(dir.c_str(), 0777, myperm, "foo"));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(dir.c_str(), &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, "foo");
+ }
+
+ ASSERT_EQ(0, client->rmdir(dir.c_str(), myperm));
+}
+
+TEST_F(TestClient, AlternateNameLong) {
+ auto altname = std::string(4096+1024, '-');
+ auto dir = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ ASSERT_EQ(0, client->mkdir(dir.c_str(), 0777, myperm, altname));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(dir.c_str(), &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname);
+ }
+
+ ASSERT_EQ(0, client->rmdir(dir.c_str(), myperm));
+}
+
+TEST_F(TestClient, AlternateNameCreat) {
+ auto altname = std::string("foo");
+ auto file = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ int fd = client->open(file.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(file, &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname);
+ }
+}
+
+TEST_F(TestClient, AlternateNameSymlink) {
+ auto altname = std::string("foo");
+ auto file = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ int fd = client->open(file.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ auto file2 = file+"2";
+ auto altname2 = altname+"2";
+ ASSERT_EQ(0, client->symlink(file.c_str(), file2.c_str(), myperm, altname2));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(file2, &wdr, myperm, false));
+ ASSERT_EQ(wdr.alternate_name, altname2);
+ ASSERT_EQ(0, client->walk(file, &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname);
+ }
+}
+
+TEST_F(TestClient, AlternateNameRename) {
+ auto altname = std::string("foo");
+ auto file = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ int fd = client->open(file.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ auto file2 = file+"2";
+ auto altname2 = altname+"2";
+
+ ASSERT_EQ(0, client->rename(file.c_str(), file2.c_str(), myperm, altname2));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(file2, &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname2);
+ }
+}
+
+TEST_F(TestClient, AlternateNameRenameExistMatch) {
+ auto altname = std::string("foo");
+ auto file = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ int fd = client->open(file.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ auto file2 = file+"2";
+ auto altname2 = altname+"2";
+
+ fd = client->open(file2.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname2);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ ASSERT_EQ(0, client->rename(file.c_str(), file2.c_str(), myperm, altname2));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(file2, &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname2);
+ }
+}
+
+TEST_F(TestClient, AlternateNameRenameExistMisMatch) {
+ auto altname = std::string("foo");
+ auto file = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ int fd = client->open(file.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ auto file2 = file+"2";
+ auto altname2 = altname+"2";
+
+ fd = client->open(file2.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname+"mismatch");
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ ASSERT_EQ(-EINVAL, client->rename(file.c_str(), file2.c_str(), myperm, altname2));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(file2, &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname+"mismatch");
+ }
+}
+
+TEST_F(TestClient, AlternateNameLink) {
+ auto altname = std::string("foo");
+ auto file = fmt::format("{}_{}", ::testing::UnitTest::GetInstance()->current_test_info()->name(), getpid());
+ int fd = client->open(file.c_str(), O_CREAT|O_WRONLY, myperm, 0777, altname);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(3, client->write(fd, "baz", 3, 0));
+ ASSERT_EQ(0, client->close(fd));
+
+ auto file2 = file+"2";
+ auto altname2 = altname+"2";
+
+ ASSERT_EQ(0, client->link(file.c_str(), file2.c_str(), myperm, altname2));
+
+ {
+ Client::walk_dentry_result wdr;
+ ASSERT_EQ(0, client->walk(file2, &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname2);
+ ASSERT_EQ(0, client->walk(file, &wdr, myperm));
+ ASSERT_EQ(wdr.alternate_name, altname);
+ }
+}
diff --git a/src/test/client/iozone.sh b/src/test/client/iozone.sh
new file mode 100755
index 000000000..31e955da7
--- /dev/null
+++ b/src/test/client/iozone.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -e
+name=`echo $0 | sed 's/\//_/g'`
+mkdir $name
+cd $name
+
+iozone -c -e -s 1024M -r 16K -t 1 -F f1 -i 0 -i 1
+iozone -c -e -s 1024M -r 1M -t 1 -F f2 -i 0 -i 1
+iozone -c -e -s 10240M -r 1M -t 1 -F f3 -i 0 -i 1
+
+cd ..
diff --git a/src/test/client/kernel_untar_build.sh b/src/test/client/kernel_untar_build.sh
new file mode 100755
index 000000000..1e9409202
--- /dev/null
+++ b/src/test/client/kernel_untar_build.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -e
+name=`echo $0 | sed 's/\//_/g'`
+mkdir $name
+cd $name
+
+tar jxvf /root/linux*
+cd linux*
+make defconfig
+make
+cd ..
+rm -r linux*
diff --git a/src/test/client/main.cc b/src/test/client/main.cc
new file mode 100644
index 000000000..dcd48acbb
--- /dev/null
+++ b/src/test/client/main.cc
@@ -0,0 +1,28 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ * Copyright (C) 2016 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ [[maybe_unused]] auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/client/ops.cc b/src/test/client/ops.cc
new file mode 100644
index 000000000..e6e25135e
--- /dev/null
+++ b/src/test/client/ops.cc
@@ -0,0 +1,45 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <errno.h>
+#include "TestClient.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "gtest/gtest-spi.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-more-matchers.h"
+
+TEST_F(TestClient, CheckDummyOP) {
+ ASSERT_EQ(client->check_dummy_op(myperm), -EOPNOTSUPP);
+}
+
+TEST_F(TestClient, CheckUnknownSessionOp) {
+ ASSERT_EQ(client->send_unknown_session_op(-1), 0);
+ sleep(5);
+ ASSERT_EQ(client->check_client_blocklisted(), true);
+}
+
+TEST_F(TestClient, CheckZeroReclaimFlag) {
+ ASSERT_EQ(client->check_unknown_reclaim_flag(0), true);
+}
+TEST_F(TestClient, CheckUnknownReclaimFlag) {
+ ASSERT_EQ(client->check_unknown_reclaim_flag(2), true);
+}
+TEST_F(TestClient, CheckNegativeReclaimFlagUnmasked) {
+ ASSERT_EQ(client->check_unknown_reclaim_flag(-1 & ~MClientReclaim::FLAG_FINISH), true);
+}
+TEST_F(TestClient, CheckNegativeReclaimFlag) {
+ ASSERT_EQ(client->check_unknown_reclaim_flag(-1), true);
+}
diff --git a/src/test/cls_2pc_queue/CMakeLists.txt b/src/test/cls_2pc_queue/CMakeLists.txt
new file mode 100644
index 000000000..1b5a4eda3
--- /dev/null
+++ b/src/test/cls_2pc_queue/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_executable(ceph_test_cls_2pc_queue
+ test_cls_2pc_queue.cc
+)
+target_link_libraries(ceph_test_cls_2pc_queue
+ cls_2pc_queue_client
+ cls_queue_client
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx)
+
+ install(TARGETS
+ ceph_test_cls_2pc_queue
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
diff --git a/src/test/cls_2pc_queue/test_cls_2pc_queue.cc b/src/test/cls_2pc_queue/test_cls_2pc_queue.cc
new file mode 100644
index 000000000..a0e83aacb
--- /dev/null
+++ b/src/test/cls_2pc_queue/test_cls_2pc_queue.cc
@@ -0,0 +1,968 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+
+#include "cls/2pc_queue/cls_2pc_queue_types.h"
+#include "cls/2pc_queue/cls_2pc_queue_client.h"
+#include "cls/queue/cls_queue_client.h"
+#include "cls/2pc_queue/cls_2pc_queue_types.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+#include "global/global_context.h"
+
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <thread>
+#include <chrono>
+#include <atomic>
+
+using namespace std;
+
+class TestCls2PCQueue : public ::testing::Test {
+protected:
+ librados::Rados rados;
+ std::string pool_name;
+ librados::IoCtx ioctx;
+
+ void SetUp() override {
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+
+ void TearDown() override {
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+ }
+};
+
+TEST_F(TestCls2PCQueue, GetCapacity)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 8*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size;
+
+ const int ret = cls_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(max_size, size);
+}
+
+TEST_F(TestCls2PCQueue, AsyncGetCapacity)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 8*1024;
+ librados::ObjectWriteOperation wop;
+ wop.create(true);
+ cls_2pc_queue_init(wop, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &wop));
+
+ librados::ObjectReadOperation rop;
+ bufferlist bl;
+ int rc;
+ cls_2pc_queue_get_capacity(rop, &bl, &rc);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &rop, nullptr));
+ ASSERT_EQ(0, rc);
+ uint64_t size;
+ ASSERT_EQ(cls_2pc_queue_get_capacity_result(bl, size), 0);
+ ASSERT_EQ(max_size, size);
+}
+
+TEST_F(TestCls2PCQueue, Reserve)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024U*1024U;
+ const auto number_of_ops = 10U;
+ const auto number_of_elements = 23U;
+ const auto size_to_reserve = 250U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_EQ(res_id, i+1);
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), number_of_ops);
+ for (const auto& r : reservations) {
+ ASSERT_NE(r.first, cls_2pc_reservation::NO_ID);
+ ASSERT_GT(r.second.timestamp.time_since_epoch().count(), 0);
+ }
+}
+
+TEST_F(TestCls2PCQueue, AsyncReserve)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024U*1024U;
+ constexpr auto number_of_ops = 10U;
+ constexpr auto number_of_elements = 23U;
+ const auto size_to_reserve = 250U;
+ librados::ObjectWriteOperation wop;
+ wop.create(true);
+ cls_2pc_queue_init(wop, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &wop));
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ bufferlist res_bl;
+ int res_rc;
+ cls_2pc_queue_reserve(wop, size_to_reserve, number_of_elements, &res_bl, &res_rc);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &wop, librados::OPERATION_RETURNVEC));
+ ASSERT_EQ(res_rc, 0);
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(0, cls_2pc_queue_reserve_result(res_bl, res_id));
+ ASSERT_EQ(res_id, i+1);
+ }
+
+ bufferlist bl;
+ int rc;
+ librados::ObjectReadOperation rop;
+ cls_2pc_queue_list_reservations(rop, &bl, &rc);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &rop, nullptr));
+ ASSERT_EQ(0, rc);
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations_result(bl, reservations));
+ ASSERT_EQ(reservations.size(), number_of_ops);
+ for (const auto& r : reservations) {
+ ASSERT_NE(r.first, cls_2pc_reservation::NO_ID);
+ ASSERT_GT(r.second.timestamp.time_since_epoch().count(), 0);
+ }
+}
+
+TEST_F(TestCls2PCQueue, Commit)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024*128;
+ const auto number_of_ops = 200U;
+ const auto number_of_elements = 23U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ auto total_size = 0UL;
+ std::vector<bufferlist> data(number_of_elements);
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix, &total_size] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ total_size += bl.length();
+ return bl;
+ });
+
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, total_size, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ cls_2pc_queue_commit(op, data, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, Abort)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024U*1024U;
+ const auto number_of_ops = 17U;
+ const auto number_of_elements = 23U;
+ const auto size_to_reserve = 250U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ cls_2pc_queue_abort(op, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, ReserveError)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 256U*1024U;
+ const auto number_of_ops = 254U;
+ const auto number_of_elements = 1U;
+ const auto size_to_reserve = 1024U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ cls_2pc_reservation::id_t res_id;
+ for (auto i = 0U; i < number_of_ops-1; ++i) {
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ }
+ res_id = cls_2pc_reservation::NO_ID;
+ // this one is failing because it exceeds the queue size
+ ASSERT_NE(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_EQ(res_id, cls_2pc_reservation::NO_ID);
+
+ // this one is failing because it tries to reserve 0 entries
+ ASSERT_NE(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, 0, res_id), 0);
+ // this one is failing because it tries to reserve 0 bytes
+ ASSERT_NE(cls_2pc_queue_reserve(ioctx, queue_name, 0, number_of_elements, res_id), 0);
+
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), number_of_ops-1);
+ for (const auto& r : reservations) {
+ ASSERT_NE(r.first, cls_2pc_reservation::NO_ID);
+ ASSERT_GT(r.second.timestamp.time_since_epoch().count(), 0);
+ }
+}
+
+TEST_F(TestCls2PCQueue, CommitError)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 17U;
+ const auto number_of_elements = 23U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ const auto invalid_reservation_op = 8;
+ const auto invalid_elements_op = 11;
+ std::vector<bufferlist> invalid_data(number_of_elements+3);
+ // create vector of buffer lists
+ std::generate(invalid_data.begin(), invalid_data.end(), [j = 0] () mutable {
+ bufferlist bl;
+ bl.append("invalid data is larger that regular data" + to_string(j++));
+ return bl;
+ });
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ auto total_size = 0UL;
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix, &total_size] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ total_size += bl.length();
+ return bl;
+ });
+
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, total_size, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ if (i == invalid_reservation_op) {
+ // fail on a commits with invalid reservation id
+ cls_2pc_queue_commit(op, data, res_id+999);
+ ASSERT_NE(0, ioctx.operate(queue_name, &op));
+ } else if (i == invalid_elements_op) {
+ // fail on a commits when data size is larger than the reserved one
+ cls_2pc_queue_commit(op, invalid_data, res_id);
+ ASSERT_NE(0, ioctx.operate(queue_name, &op));
+ } else {
+ cls_2pc_queue_commit(op, data, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ // 2 reservations were not comitted
+ ASSERT_EQ(reservations.size(), 2);
+}
+
+TEST_F(TestCls2PCQueue, AbortError)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 17U;
+ const auto number_of_elements = 23U;
+ const auto size_to_reserve = 250U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ const auto invalid_reservation_op = 8;
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ if (i == invalid_reservation_op) {
+ // aborting a reservation which does not exists
+ // is a no-op, not an error
+ cls_2pc_queue_abort(op, res_id+999);
+ } else {
+ cls_2pc_queue_abort(op, res_id);
+ }
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ // 1 reservation was not aborted
+ ASSERT_EQ(reservations.size(), 1);
+}
+
+TEST_F(TestCls2PCQueue, MultiReserve)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 11U;
+ const auto number_of_elements = 23U;
+ const auto max_producer_count = 10U;
+ const auto size_to_reserve = 250U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ std::vector<std::thread> producers(max_producer_count);
+ for (auto& p : producers) {
+ p = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, 0);
+ }
+ });
+ }
+
+ std::for_each(producers.begin(), producers.end(), [](auto& p) { p.join(); });
+
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), number_of_ops*max_producer_count);
+ auto total_reservations = 0U;
+ for (const auto& r : reservations) {
+ total_reservations += r.second.size;
+ }
+ ASSERT_EQ(total_reservations, number_of_ops*max_producer_count*size_to_reserve);
+}
+
+TEST_F(TestCls2PCQueue, MultiCommit)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 11U;
+ const auto number_of_elements = 23U;
+ const auto max_producer_count = 10U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ std::vector<std::thread> producers(max_producer_count);
+ for (auto& p : producers) {
+ p = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ auto total_size = 0UL;
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix, &total_size] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ total_size += bl.length();
+ return bl;
+ });
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, total_size, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, 0);
+ cls_2pc_queue_commit(op, data, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+ }
+
+ std::for_each(producers.begin(), producers.end(), [](auto& p) { p.join(); });
+
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, MultiAbort)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 11U;
+ const auto number_of_elements = 23U;
+ const auto max_producer_count = 10U;
+ const auto size_to_reserve = 250U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ std::vector<std::thread> producers(max_producer_count);
+ for (auto& p : producers) {
+ p = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, 0);
+ cls_2pc_queue_abort(op, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+ }
+
+ std::for_each(producers.begin(), producers.end(), [](auto& p) { p.join(); });
+
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, ReserveCommit)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 11U;
+ const auto number_of_elements = 23U;
+ const auto max_workers = 10U;
+ const auto size_to_reserve = 512U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ std::vector<std::thread> reservers(max_workers);
+ for (auto& r : reservers) {
+ r = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ }
+ });
+ }
+
+ auto committer = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ int remaining_ops = number_of_ops*max_workers;
+ while (remaining_ops > 0) {
+ const std::string element_prefix("op-" +to_string(remaining_ops) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ return bl;
+ });
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ for (const auto& r : reservations) {
+ cls_2pc_queue_commit(op, data, r.first);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ --remaining_ops;
+ }
+ }
+ });
+
+ std::for_each(reservers.begin(), reservers.end(), [](auto& r) { r.join(); });
+ committer.join();
+
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, ReserveAbort)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 17U;
+ const auto number_of_elements = 23U;
+ const auto max_workers = 10U;
+ const auto size_to_reserve = 250U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ std::vector<std::thread> reservers(max_workers);
+ for (auto& r : reservers) {
+ r = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ }
+ });
+ }
+
+ auto aborter = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ int remaining_ops = number_of_ops*max_workers;
+ while (remaining_ops > 0) {
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ for (const auto& r : reservations) {
+ cls_2pc_queue_abort(op, r.first);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ --remaining_ops;
+ }
+ }
+ });
+
+ std::for_each(reservers.begin(), reservers.end(), [](auto& r) { r.join(); });
+ aborter.join();
+
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, ManualCleanup)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 128*1024*1024;
+ const auto number_of_ops = 17U;
+ const auto number_of_elements = 23U;
+ const auto max_workers = 10U;
+ const auto size_to_reserve = 512U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // anything older than 100ms is cosidered stale
+ ceph::coarse_real_time stale_time = ceph::coarse_real_clock::now() + std::chrono::milliseconds(100);
+
+ std::vector<std::thread> reservers(max_workers);
+ for (auto& r : reservers) {
+ r = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ // wait for 10ms between each reservation to make sure at least some are stale
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ });
+ }
+
+ auto cleaned_reservations = 0U;
+ auto committed_reservations = 0U;
+ auto aborter = std::thread([this, &queue_name, &stale_time, &cleaned_reservations, &committed_reservations] {
+ librados::ObjectWriteOperation op;
+ int remaining_ops = number_of_ops*max_workers;
+ while (remaining_ops > 0) {
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ for (const auto& r : reservations) {
+ if (r.second.timestamp > stale_time) {
+ // abort stale reservations
+ cls_2pc_queue_abort(op, r.first);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ ++cleaned_reservations;
+ } else {
+ // commit good reservations
+ const std::string element_prefix("op-" +to_string(remaining_ops) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ return bl;
+ });
+ cls_2pc_queue_commit(op, data, r.first);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ ++committed_reservations;
+ }
+ --remaining_ops;
+ }
+ }
+ });
+
+
+ std::for_each(reservers.begin(), reservers.end(), [](auto& r) { r.join(); });
+ aborter.join();
+
+ ASSERT_GT(cleaned_reservations, 0);
+ ASSERT_EQ(committed_reservations + cleaned_reservations, number_of_ops*max_workers);
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, Cleanup)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 128*1024*1024;
+ const auto number_of_ops = 15U;
+ const auto number_of_elements = 23U;
+ const auto max_workers = 10U;
+ const auto size_to_reserve = 512U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // anything older than 100ms is cosidered stale
+ ceph::coarse_real_time stale_time = ceph::coarse_real_clock::now() + std::chrono::milliseconds(100);
+
+ std::vector<std::thread> reservers(max_workers);
+ for (auto& r : reservers) {
+ r = std::thread([this, &queue_name] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ // wait for 10ms between each reservation to make sure at least some are stale
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ });
+ }
+
+ std::for_each(reservers.begin(), reservers.end(), [](auto& r) { r.join(); });
+
+ cls_2pc_reservations all_reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, all_reservations));
+ ASSERT_EQ(all_reservations.size(), number_of_ops*max_workers);
+
+ cls_2pc_queue_expire_reservations(op, stale_time);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ cls_2pc_reservations good_reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, good_reservations));
+
+ for (const auto& r : all_reservations) {
+ if (good_reservations.find(r.first) == good_reservations.end()) {
+ // not in the "good" list
+ ASSERT_GE(stale_time.time_since_epoch().count(),
+ r.second.timestamp.time_since_epoch().count());
+ }
+ }
+ for (const auto& r : good_reservations) {
+ ASSERT_LT(stale_time.time_since_epoch().count(),
+ r.second.timestamp.time_since_epoch().count());
+ }
+}
+
+TEST_F(TestCls2PCQueue, MultiProducer)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 128*1024*1024;
+ const auto number_of_ops = 300U;
+ const auto number_of_elements = 23U;
+ const auto max_producer_count = 10U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ auto producer_count = max_producer_count;
+
+ std::vector<std::thread> producers(max_producer_count);
+ for (auto& p : producers) {
+ p = std::thread([this, &queue_name, &producer_count] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ auto total_size = 0UL;
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix, &total_size] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ total_size += bl.length();
+ return bl;
+ });
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, total_size, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, 0);
+ cls_2pc_queue_commit(op, data, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ --producer_count;
+ });
+ }
+
+ auto consume_count = 0U;
+ std::thread consumer([this, &queue_name, &consume_count, &producer_count] {
+ librados::ObjectWriteOperation op;
+ const auto max_elements = 42;
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ while (producer_count > 0 || truncated) {
+ const auto ret = cls_2pc_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ consume_count += entries.size();
+ cls_2pc_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+
+ std::for_each(producers.begin(), producers.end(), [](auto& p) { p.join(); });
+ consumer.join();
+ ASSERT_EQ(consume_count, number_of_ops*number_of_elements*max_producer_count);
+}
+
+TEST_F(TestCls2PCQueue, AsyncConsumer)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ constexpr auto max_size = 128*1024*1024;
+ constexpr auto number_of_ops = 250U;
+ constexpr auto number_of_elements = 23U;
+ librados::ObjectWriteOperation wop;
+ wop.create(true);
+ cls_2pc_queue_init(wop, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &wop));
+
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ auto total_size = 0UL;
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix, &total_size] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ total_size += bl.length();
+ return bl;
+ });
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, total_size, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, 0);
+ cls_2pc_queue_commit(wop, data, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &wop));
+ }
+
+ constexpr auto max_elements = 42;
+ std::string marker;
+ std::string end_marker;
+ librados::ObjectReadOperation rop;
+ auto consume_count = 0U;
+ std::vector<cls_queue_entry> entries;
+ bool truncated = true;
+ while (truncated) {
+ bufferlist bl;
+ int rc;
+ cls_2pc_queue_list_entries(rop, marker, max_elements, &bl, &rc);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &rop, nullptr));
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(cls_2pc_queue_list_entries_result(bl, entries, &truncated, end_marker), 0);
+ consume_count += entries.size();
+ cls_2pc_queue_remove_entries(wop, end_marker);
+ marker = end_marker;
+ }
+
+ ASSERT_EQ(consume_count, number_of_ops*number_of_elements);
+ // execute all delete operations in a batch
+ ASSERT_EQ(0, ioctx.operate(queue_name, &wop));
+ // make sure that queue is empty
+ ASSERT_EQ(cls_2pc_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker), 0);
+ ASSERT_EQ(entries.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, MultiProducerConsumer)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024*1024;
+ const auto number_of_ops = 300U;
+ const auto number_of_elements = 23U;
+ const auto max_workers = 10U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ auto producer_count = max_workers;
+
+ auto retry_happened = false;
+
+ std::vector<std::thread> producers(max_workers);
+ for (auto& p : producers) {
+ p = std::thread([this, &queue_name, &producer_count, &retry_happened] {
+ librados::ObjectWriteOperation op;
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ auto total_size = 0UL;
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix, &total_size] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ total_size += bl.length();
+ return bl;
+ });
+ cls_2pc_reservation::id_t res_id = cls_2pc_reservation::NO_ID;
+ auto rc = cls_2pc_queue_reserve(ioctx, queue_name, total_size, number_of_elements, res_id);
+ while (rc != 0) {
+ // other errors should cause test to fail
+ ASSERT_EQ(rc, -ENOSPC);
+ ASSERT_EQ(res_id, 0);
+ // queue is full, sleep and retry
+ retry_happened = true;
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ rc = cls_2pc_queue_reserve(ioctx, queue_name, total_size, number_of_elements, res_id);
+ };
+ ASSERT_NE(res_id, 0);
+ cls_2pc_queue_commit(op, data, res_id);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ --producer_count;
+ });
+ }
+
+ const auto max_elements = 128;
+ std::vector<std::thread> consumers(max_workers/2);
+ for (auto& c : consumers) {
+ c = std::thread([this, &queue_name, &producer_count] {
+ librados::ObjectWriteOperation op;
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ while (producer_count > 0 || truncated) {
+ const auto ret = cls_2pc_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ if (entries.empty()) {
+ // queue is empty, let it fill
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ } else {
+ cls_2pc_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ }
+ });
+ }
+
+ std::for_each(producers.begin(), producers.end(), [](auto& p) { p.join(); });
+ std::for_each(consumers.begin(), consumers.end(), [](auto& c) { c.join(); });
+ if (!retry_happened) {
+ std::cerr << "Queue was never full - all reservations were sucessfull." <<
+ "Please decrease the amount of consumer threads" << std::endl;
+ }
+ // make sure that queue is empty and no reservations remain
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ ASSERT_EQ(0, cls_2pc_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker));
+ ASSERT_EQ(entries.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, ReserveSpillover)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024U*1024U;
+ const auto number_of_ops = 1024U;
+ const auto number_of_elements = 8U;
+ const auto size_to_reserve = 64U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), number_of_ops);
+ for (const auto& r : reservations) {
+ ASSERT_NE(r.first, cls_2pc_reservation::NO_ID);
+ ASSERT_GT(r.second.timestamp.time_since_epoch().count(), 0);
+ }
+}
+
+TEST_F(TestCls2PCQueue, CommitSpillover)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024U*1024U;
+ const auto number_of_ops = 1024U;
+ const auto number_of_elements = 4U;
+ const auto size_to_reserve = 128U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ for (const auto& r : reservations) {
+ const std::string element_prefix("foo");
+ std::vector<bufferlist> data(number_of_elements);
+ auto total_size = 0UL;
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix, &total_size] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ total_size += bl.length();
+ return bl;
+ });
+ ASSERT_NE(r.first, cls_2pc_reservation::NO_ID);
+ cls_2pc_queue_commit(op, data, r.first);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
+TEST_F(TestCls2PCQueue, AbortSpillover)
+{
+ const std::string queue_name = __PRETTY_FUNCTION__;
+ const auto max_size = 1024U*1024U;
+ const auto number_of_ops = 1024U;
+ const auto number_of_elements = 4U;
+ const auto size_to_reserve = 128U;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_2pc_queue_init(op, queue_name, max_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ for (auto i = 0U; i < number_of_ops; ++i) {
+ cls_2pc_reservation::id_t res_id;
+ ASSERT_EQ(cls_2pc_queue_reserve(ioctx, queue_name, size_to_reserve, number_of_elements, res_id), 0);
+ ASSERT_NE(res_id, cls_2pc_reservation::NO_ID);
+ }
+ cls_2pc_reservations reservations;
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ for (const auto& r : reservations) {
+ ASSERT_NE(r.first, cls_2pc_reservation::NO_ID);
+ cls_2pc_queue_abort(op, r.first);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ ASSERT_EQ(0, cls_2pc_queue_list_reservations(ioctx, queue_name, reservations));
+ ASSERT_EQ(reservations.size(), 0);
+}
+
diff --git a/src/test/cls_cas/CMakeLists.txt b/src/test/cls_cas/CMakeLists.txt
new file mode 100644
index 000000000..16bff8b3b
--- /dev/null
+++ b/src/test/cls_cas/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_executable(ceph_test_cls_cas
+ test_cls_cas.cc)
+target_link_libraries(ceph_test_cls_cas
+ librados
+ cls_cas_client
+ cls_cas_internal
+ global
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ )
+install(TARGETS
+ ceph_test_cls_cas
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_cas/test_cls_cas.cc b/src/test/cls_cas/test_cls_cas.cc
new file mode 100644
index 000000000..636c6e2db
--- /dev/null
+++ b/src/test/cls_cas/test_cls_cas.cc
@@ -0,0 +1,368 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "include/stringify.h"
+#include "cls/cas/cls_cas_client.h"
+#include "cls/cas/cls_cas_internal.h"
+
+#include "include/utime.h"
+#include "common/Clock.h"
+#include "global/global_context.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+/// creates a temporary pool and initializes an IoCtx for each test
+class cls_cas : public ::testing::Test {
+ librados::Rados rados;
+ std::string pool_name;
+ protected:
+ librados::IoCtx ioctx;
+
+ void SetUp() {
+ pool_name = get_temp_pool_name();
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+ void TearDown() {
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+ }
+};
+
+static librados::ObjectWriteOperation *new_op() {
+ return new librados::ObjectWriteOperation();
+}
+
+/*
+static librados::ObjectReadOperation *new_rop() {
+ return new librados::ObjectReadOperation();
+}
+*/
+
+TEST_F(cls_cas, get_put)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1, ref2, ref3;
+ ref1.oid.name = "foo1";
+ ref2.oid.name = "foo2";
+ ref3.oid.name = "foo3";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // get x3
+ {
+ auto op = new_op();
+ cls_cas_chunk_get_ref(*op, ref2);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // get again
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref3, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // 3x puts to remove
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref3);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref2);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+
+ // get
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // put
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+}
+
+TEST_F(cls_cas, wrong_put)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1, ref2;
+ ref1.oid.name = "foo1";
+ ref2.oid.name = "foo2";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // put wrong thing
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref2);
+ ASSERT_EQ(-ENOLINK, ioctx.operate(oid, op));
+ }
+
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+}
+
+TEST_F(cls_cas, dup_get)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1;
+ ref1.oid.name = "foo1";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // duplicated entries are allowed by "by_object". as it tracks refs of the same
+ // hobject_t for snapshot support.
+ int n_refs = 0;
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ n_refs++;
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // dup create_or_get_ref, get_ref will succeed but take no additional ref
+ // only if the chunk_refs' type is not "by_object"
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ n_refs++;
+ }
+ {
+ auto op = new_op();
+ cls_cas_chunk_get_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ n_refs++;
+ }
+
+ for (int i = 0; i < n_refs; i++) {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ if (i < n_refs - 1) {
+ // should not referenced anymore, but by_object is an exception
+ // and by_object is used by default.
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+ } else {
+ // the last reference was removed
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+ }
+ }
+}
+
+TEST_F(cls_cas, dup_put)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1;
+ ref1.oid.name = "foo1";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, op));
+ }
+}
+
+
+TEST_F(cls_cas, get_wrong_data)
+{
+ bufferlist bl, bl2;
+ bl.append("my data");
+ bl2.append("my different data");
+ string oid = "mychunk";
+ hobject_t ref1, ref2;
+ ref1.oid.name = "foo1";
+ ref2.oid.name = "foo2";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // get
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // get w/ different data
+ // with verify
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref2, bl2, true);
+ ASSERT_EQ(-ENOMSG, ioctx.operate(oid, op));
+ }
+ // without verify
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref2, bl2, false);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // put
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref2);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+}
+
+static int count_bits(unsigned long n)
+{
+ // base case
+ if (n == 0)
+ return 0;
+ else
+ return 1 + count_bits(n & (n - 1));
+}
+
+TEST(chunk_refs_t, size)
+{
+ chunk_refs_t r;
+ size_t max = 1048576;
+
+ // mix in pool changes as i gets bigger
+ size_t pool_mask = 0xfff5110;
+
+ // eventually add in a zillion different pools to force us to a raw count
+ size_t pool_cutoff = max/2;
+
+ for (size_t i = 1; i <= max; ++i) {
+ hobject_t h(sobject_t(object_t("foo"s + stringify(i)), i));
+ h.pool = i > pool_cutoff ? i : (i & pool_mask);
+ r.get(h);
+ if (count_bits(i) <= 2) {
+ bufferlist bl;
+ r.dynamic_encode(bl, 512);
+ if (count_bits(i) == 1) {
+ cout << i << "\t" << bl.length()
+ << "\t" << r.describe_encoding()
+ << std::endl;
+ }
+
+ // verify reencoding is correct
+ chunk_refs_t a;
+ auto t = bl.cbegin();
+ decode(a, t);
+ bufferlist bl2;
+ encode(a, bl2);
+ if (!bl.contents_equal(bl2)) {
+ std::unique_ptr<Formatter> f(Formatter::create("json-pretty"));
+ cout << "original:\n";
+ f->dump_object("refs", r);
+ f->flush(cout);
+ cout << "decoded:\n";
+ f->dump_object("refs", a);
+ f->flush(cout);
+ cout << "original encoding:\n";
+ bl.hexdump(cout);
+ cout << "decoded re-encoding:\n";
+ bl2.hexdump(cout);
+ ASSERT_TRUE(bl.contents_equal(bl2));
+ }
+ }
+ }
+ ASSERT_EQ(max, r.count());
+ for (size_t i = 1; i <= max; ++i) {
+ hobject_t h(sobject_t(object_t("foo"s + stringify(i)), 1));
+ h.pool = i > pool_cutoff ? i : (i & pool_mask);
+ bool ret = r.put(h);
+ ASSERT_TRUE(ret);
+ }
+ ASSERT_EQ(0, r.count());
+}
diff --git a/src/test/cls_cmpomap/CMakeLists.txt b/src/test/cls_cmpomap/CMakeLists.txt
new file mode 100644
index 000000000..471b5e7f7
--- /dev/null
+++ b/src/test/cls_cmpomap/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(ceph_test_cls_cmpomap test_cls_cmpomap.cc)
+target_link_libraries(ceph_test_cls_cmpomap cls_cmpomap_client librados radostest-cxx ${UNITTEST_LIBS})
+install(TARGETS ceph_test_cls_cmpomap DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_cmpomap/test_cls_cmpomap.cc b/src/test/cls_cmpomap/test_cls_cmpomap.cc
new file mode 100644
index 000000000..c8abb5266
--- /dev/null
+++ b/src/test/cls_cmpomap/test_cls_cmpomap.cc
@@ -0,0 +1,720 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat, Inc
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include "cls/cmpomap/client.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+#include <optional>
+
+// create/destroy a pool that's shared by all tests in the process
+struct RadosEnv : public ::testing::Environment {
+ static std::optional<std::string> pool_name;
+ public:
+ static librados::Rados rados;
+ static librados::IoCtx ioctx;
+
+ void SetUp() override {
+ // create pool
+ std::string name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(name, rados));
+ pool_name = name;
+ ASSERT_EQ(rados.ioctx_create(name.c_str(), ioctx), 0);
+ }
+ void TearDown() override {
+ ioctx.close();
+ if (pool_name) {
+ ASSERT_EQ(destroy_one_pool_pp(*pool_name, rados), 0);
+ }
+ }
+};
+std::optional<std::string> RadosEnv::pool_name;
+librados::Rados RadosEnv::rados;
+librados::IoCtx RadosEnv::ioctx;
+
+auto *const rados_env = ::testing::AddGlobalTestEnvironment(new RadosEnv);
+
+namespace cls::cmpomap {
+
+// test fixture with helper functions
+class CmpOmap : public ::testing::Test {
+ protected:
+ librados::IoCtx& ioctx = RadosEnv::ioctx;
+
+ int do_cmp_vals(const std::string& oid, Mode mode,
+ Op comparison, ComparisonMap values,
+ std::optional<bufferlist> def = std::nullopt)
+ {
+ librados::ObjectReadOperation op;
+ int ret = cmp_vals(op, mode, comparison,
+ std::move(values), std::move(def));
+ if (ret < 0) {
+ return ret;
+ }
+ return ioctx.operate(oid, &op, nullptr);
+ }
+
+ int do_cmp_set_vals(const std::string& oid, Mode mode,
+ Op comparison, ComparisonMap values,
+ std::optional<bufferlist> def = std::nullopt)
+ {
+ librados::ObjectWriteOperation op;
+ int ret = cmp_set_vals(op, mode, comparison,
+ std::move(values), std::move(def));
+ if (ret < 0) {
+ return ret;
+ }
+ return ioctx.operate(oid, &op);
+ }
+
+ int do_cmp_rm_keys(const std::string& oid, Mode mode,
+ Op comparison, ComparisonMap values)
+ {
+ librados::ObjectWriteOperation op;
+ int ret = cmp_rm_keys(op, mode, comparison, std::move(values));
+ if (ret < 0) {
+ return ret;
+ }
+ return ioctx.operate(oid, &op);
+ }
+
+ int get_vals(const std::string& oid, std::map<std::string, bufferlist>* vals)
+ {
+ std::string marker;
+ bool more = false;
+ do {
+ std::map<std::string, bufferlist> tmp;
+ int r = ioctx.omap_get_vals2(oid, marker, 1000, &tmp, &more);
+ if (r < 0) {
+ return r;
+ }
+ if (!tmp.empty()) {
+ marker = tmp.rbegin()->first;
+ vals->merge(std::move(tmp));
+ }
+ } while (more);
+ return 0;
+ }
+};
+
+TEST_F(CmpOmap, cmp_vals_noexist_str)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+
+ // compare a nonempty value against a missing key with no default
+ const bufferlist input = string_buffer("a");
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::EQ, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::NE, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GT, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GTE, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LT, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LTE, {{key, input}}), -ECANCELED);
+}
+
+TEST_F(CmpOmap, cmp_vals_noexist_str_default)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+
+ // compare a nonempty value against a missing key with nonempty default
+ const bufferlist input = string_buffer("a");
+ const bufferlist def = string_buffer("b");
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::EQ, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::NE, {{key, input}}, def), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GT, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GTE, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LT, {{key, input}}, def), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LTE, {{key, input}}, def), 0);
+}
+
+TEST_F(CmpOmap, cmp_vals_noexist_u64)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+
+ // 0 != nullopt
+ const bufferlist input = u64_buffer(0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, input}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, input}}), -ECANCELED);
+}
+
+TEST_F(CmpOmap, cmp_vals_noexist_u64_default)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+
+ // 1 == noexist
+ const bufferlist input = u64_buffer(1);
+ const bufferlist def = u64_buffer(2);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, input}}, def), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, input}}, def), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, input}}, def), 0);
+}
+
+TEST_F(CmpOmap, cmp_vals_str)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const std::string key = "key";
+ ASSERT_EQ(ioctx.omap_set(oid, {{key, string_buffer("bbb")}}), 0);
+ {
+ // empty < existing
+ const bufferlist empty;
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::EQ, {{key, empty}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::NE, {{key, empty}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GT, {{key, empty}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GTE, {{key, empty}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LT, {{key, empty}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LTE, {{key, empty}}), 0);
+ }
+ {
+ // value < existing
+ const bufferlist value = string_buffer("aaa");
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::EQ, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::NE, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GT, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GTE, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LT, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LTE, {{key, value}}), 0);
+ }
+ {
+ // value > existing
+ const bufferlist value = string_buffer("bbbb");
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::EQ, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::NE, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GT, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::GTE, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LT, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::String, Op::LTE, {{key, value}}), -ECANCELED);
+ }
+}
+
+TEST_F(CmpOmap, cmp_vals_u64)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const std::string key = "key";
+ ASSERT_EQ(ioctx.omap_set(oid, {{key, u64_buffer(42)}}), 0);
+ {
+ // 0 < existing
+ const bufferlist value = u64_buffer(0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, value}}), 0);
+ }
+ {
+ // 42 == existing
+ const bufferlist value = u64_buffer(42);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, value}}), 0);
+ }
+ {
+ // uint64-max > existing
+ uint64_t v = std::numeric_limits<uint64_t>::max();
+ const bufferlist value = u64_buffer(v);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, value}}), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, value}}), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, value}}), -ECANCELED);
+ }
+}
+
+TEST_F(CmpOmap, cmp_vals_u64_invalid_input)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+ const bufferlist empty; // empty buffer can't be decoded as u64
+ const bufferlist def = u64_buffer(0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, empty}}, def), -EINVAL);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, empty}}, def), -EINVAL);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, empty}}, def), -EINVAL);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, empty}}, def), -EINVAL);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, empty}}, def), -EINVAL);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, empty}}, def), -EINVAL);
+}
+
+TEST_F(CmpOmap, cmp_vals_u64_invalid_default)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+ const bufferlist input = u64_buffer(0);
+ const bufferlist def = string_buffer("bbb"); // can't be decoded as u64
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, input}}, def), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, input}}, def), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, input}}, def), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, input}}, def), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, input}}, def), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, input}}, def), -EIO);
+}
+
+TEST_F(CmpOmap, cmp_vals_u64_empty_default)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+ const bufferlist input = u64_buffer(1);
+ const bufferlist def; // empty buffer defaults to 0
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, input}}, def), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, input}}, def), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, input}}, def), 0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, input}}, def), -ECANCELED);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, input}}, def), -ECANCELED);
+}
+
+TEST_F(CmpOmap, cmp_vals_u64_invalid_value)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ ASSERT_EQ(ioctx.create(oid, true), 0);
+ const std::string key = "key";
+ ASSERT_EQ(ioctx.omap_set(oid, {{key, string_buffer("bbb")}}), 0);
+ const bufferlist input = u64_buffer(0);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::EQ, {{key, input}}), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::NE, {{key, input}}), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GT, {{key, input}}), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::GTE, {{key, input}}), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LT, {{key, input}}), -EIO);
+ EXPECT_EQ(do_cmp_vals(oid, Mode::U64, Op::LTE, {{key, input}}), -EIO);
+}
+
+TEST_F(CmpOmap, cmp_vals_at_max_keys)
+{
+ ComparisonMap comparisons;
+ const bufferlist empty;
+ for (uint32_t i = 0; i < max_keys; i++) {
+ comparisons.emplace(std::to_string(i), empty);
+ }
+ librados::ObjectReadOperation op;
+ EXPECT_EQ(cmp_vals(op, Mode::String, Op::EQ, std::move(comparisons), empty), 0);
+}
+
+TEST_F(CmpOmap, cmp_vals_over_max_keys)
+{
+ ComparisonMap comparisons;
+ const bufferlist empty;
+ for (uint32_t i = 0; i < max_keys + 1; i++) {
+ comparisons.emplace(std::to_string(i), empty);
+ }
+ librados::ObjectReadOperation op;
+ EXPECT_EQ(cmp_vals(op, Mode::String, Op::EQ, std::move(comparisons), empty), -E2BIG);
+}
+
+TEST_F(CmpOmap, cmp_set_vals_noexist_str)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value = string_buffer("bbb");
+
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::EQ, {{"eq", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::NE, {{"ne", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::GT, {{"gt", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::GTE, {{"gte", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::LT, {{"lt", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::LTE, {{"lte", value}}), 0);
+
+ std::map<std::string, bufferlist> vals;
+ EXPECT_EQ(get_vals(oid, &vals), -ENOENT); // never got created
+}
+
+TEST_F(CmpOmap, cmp_set_vals_noexist_str_default)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value = string_buffer("bbb");
+ const bufferlist def;
+
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::EQ, {{"eq", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::NE, {{"ne", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::GT, {{"gt", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::GTE, {{"gte", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::LT, {{"lt", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::String, Op::LTE, {{"lte", value}}, def), 0);
+
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ EXPECT_EQ(vals.count("eq"), 0);
+ EXPECT_EQ(vals.count("ne"), 1);
+ EXPECT_EQ(vals.count("gt"), 1);
+ EXPECT_EQ(vals.count("gte"), 1);
+ EXPECT_EQ(vals.count("lt"), 0);
+ EXPECT_EQ(vals.count("lte"), 0);
+}
+
+TEST_F(CmpOmap, cmp_set_vals_noexist_u64)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value = u64_buffer(0);
+
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::EQ, {{"eq", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::NE, {{"ne", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::GT, {{"gt", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::GTE, {{"gte", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::LT, {{"lt", value}}), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::LTE, {{"lte", value}}), 0);
+
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), -ENOENT);
+}
+
+TEST_F(CmpOmap, cmp_set_vals_noexist_u64_default)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value = u64_buffer(0);
+ const bufferlist def = u64_buffer(0);
+
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::EQ, {{"eq", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::NE, {{"ne", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::GT, {{"gt", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::GTE, {{"gte", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::LT, {{"lt", value}}, def), 0);
+ EXPECT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::LTE, {{"lte", value}}, def), 0);
+
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ EXPECT_EQ(vals.count("eq"), 1);
+ EXPECT_EQ(vals.count("ne"), 0);
+ EXPECT_EQ(vals.count("gt"), 0);
+ EXPECT_EQ(vals.count("gte"), 1);
+ EXPECT_EQ(vals.count("lt"), 0);
+ EXPECT_EQ(vals.count("lte"), 1);
+}
+
+TEST_F(CmpOmap, cmp_set_vals_str)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value1 = string_buffer("bbb");
+ const bufferlist value2 = string_buffer("ccc");
+ {
+ std::map<std::string, bufferlist> vals = {
+ {"eq", value1},
+ {"ne", value1},
+ {"gt", value1},
+ {"gte", value1},
+ {"lt", value1},
+ {"lte", value1},
+ };
+ ASSERT_EQ(ioctx.omap_set(oid, vals), 0);
+ }
+
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::String, Op::EQ, {{"eq", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::String, Op::NE, {{"ne", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::String, Op::GT, {{"gt", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::String, Op::GTE, {{"gte", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::String, Op::LT, {{"lt", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::String, Op::LTE, {{"lte", value2}}), 0);
+
+ {
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ ASSERT_EQ(vals.size(), 6);
+ EXPECT_EQ(value1, vals["eq"]);
+ EXPECT_EQ(value2, vals["ne"]);
+ EXPECT_EQ(value2, vals["gt"]);
+ EXPECT_EQ(value2, vals["gte"]);
+ EXPECT_EQ(value1, vals["lt"]);
+ EXPECT_EQ(value1, vals["lte"]);
+ }
+}
+
+TEST_F(CmpOmap, cmp_set_vals_u64)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value1 = u64_buffer(0);
+ const bufferlist value2 = u64_buffer(42);
+ {
+ std::map<std::string, bufferlist> vals = {
+ {"eq", value1},
+ {"ne", value1},
+ {"gt", value1},
+ {"gte", value1},
+ {"lt", value1},
+ {"lte", value1},
+ };
+ ASSERT_EQ(ioctx.omap_set(oid, vals), 0);
+ }
+
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::EQ, {{"eq", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::NE, {{"ne", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::GT, {{"gt", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::GTE, {{"gte", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::LT, {{"lt", value2}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::LTE, {{"lte", value2}}), 0);
+
+ {
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ ASSERT_EQ(vals.size(), 6);
+ EXPECT_EQ(value1, vals["eq"]);
+ EXPECT_EQ(value2, vals["ne"]);
+ EXPECT_EQ(value2, vals["gt"]);
+ EXPECT_EQ(value2, vals["gte"]);
+ EXPECT_EQ(value1, vals["lt"]);
+ EXPECT_EQ(value1, vals["lte"]);
+ }
+}
+
+TEST_F(CmpOmap, cmp_set_vals_u64_einval)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const std::string key = "key";
+ const bufferlist value1 = u64_buffer(0);
+ const bufferlist value2 = string_buffer("ccc");
+ ASSERT_EQ(ioctx.omap_set(oid, {{key, value1}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::EQ, {{key, value2}}), -EINVAL);
+}
+
+TEST_F(CmpOmap, cmp_set_vals_u64_eio)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const std::string key = "key";
+ const bufferlist value1 = string_buffer("ccc");
+ const bufferlist value2 = u64_buffer(0);
+ ASSERT_EQ(ioctx.omap_set(oid, {{key, value1}}), 0);
+ ASSERT_EQ(do_cmp_set_vals(oid, Mode::U64, Op::EQ, {{key, value2}}), 0);
+ {
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ ASSERT_EQ(vals.size(), 1);
+ EXPECT_EQ(value1, vals[key]);
+ }
+}
+
+TEST_F(CmpOmap, cmp_set_vals_at_max_keys)
+{
+ ComparisonMap comparisons;
+ const bufferlist value = u64_buffer(0);
+ for (uint32_t i = 0; i < max_keys; i++) {
+ comparisons.emplace(std::to_string(i), value);
+ }
+ librados::ObjectWriteOperation op;
+ EXPECT_EQ(cmp_set_vals(op, Mode::U64, Op::EQ, std::move(comparisons), std::nullopt), 0);
+}
+
+TEST_F(CmpOmap, cmp_set_vals_over_max_keys)
+{
+ ComparisonMap comparisons;
+ const bufferlist value = u64_buffer(0);
+ for (uint32_t i = 0; i < max_keys + 1; i++) {
+ comparisons.emplace(std::to_string(i), value);
+ }
+ librados::ObjectWriteOperation op;
+ EXPECT_EQ(cmp_set_vals(op, Mode::U64, Op::EQ, std::move(comparisons), std::nullopt), -E2BIG);
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_noexist_str)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist empty;
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::EQ, {{"eq", empty}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::NE, {{"ne", empty}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::GT, {{"gt", empty}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::GTE, {{"gte", empty}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::LT, {{"lt", empty}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::LTE, {{"lte", empty}}), 0);
+
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), -ENOENT);
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_noexist_u64)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value = u64_buffer(0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::EQ, {{"eq", value}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::NE, {{"ne", value}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::GT, {{"gt", value}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::GTE, {{"gte", value}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::LT, {{"lt", value}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::LTE, {{"lte", value}}), 0);
+
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), -ENOENT);
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_str)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value1 = string_buffer("bbb");
+ const bufferlist value2 = string_buffer("ccc");
+ {
+ std::map<std::string, bufferlist> vals = {
+ {"eq", value1},
+ {"ne", value1},
+ {"gt", value1},
+ {"gte", value1},
+ {"lt", value1},
+ {"lte", value1},
+ };
+ ASSERT_EQ(ioctx.omap_set(oid, vals), 0);
+ }
+
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::EQ, {{"eq", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::NE, {{"ne", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::GT, {{"gt", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::GTE, {{"gte", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::LT, {{"lt", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::String, Op::LTE, {{"lte", value2}}), 0);
+
+ {
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ EXPECT_EQ(vals.count("eq"), 1);
+ EXPECT_EQ(vals.count("ne"), 0);
+ EXPECT_EQ(vals.count("gt"), 0);
+ EXPECT_EQ(vals.count("gte"), 0);
+ EXPECT_EQ(vals.count("lt"), 1);
+ EXPECT_EQ(vals.count("lte"), 1);
+ }
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_u64)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value1 = u64_buffer(0);
+ const bufferlist value2 = u64_buffer(42);
+ {
+ std::map<std::string, bufferlist> vals = {
+ {"eq", value1},
+ {"ne", value1},
+ {"gt", value1},
+ {"gte", value1},
+ {"lt", value1},
+ {"lte", value1},
+ };
+ ASSERT_EQ(ioctx.omap_set(oid, vals), 0);
+ }
+
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::EQ, {{"eq", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::NE, {{"ne", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::GT, {{"gt", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::GTE, {{"gte", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::LT, {{"lt", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::LTE, {{"lte", value2}}), 0);
+
+ {
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ EXPECT_EQ(vals.count("eq"), 1);
+ EXPECT_EQ(vals.count("ne"), 0);
+ EXPECT_EQ(vals.count("gt"), 0);
+ EXPECT_EQ(vals.count("gte"), 0);
+ EXPECT_EQ(vals.count("lt"), 1);
+ EXPECT_EQ(vals.count("lte"), 1);
+ }
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_u64_einval)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const std::string key = "key";
+ const bufferlist value1 = u64_buffer(0);
+ const bufferlist value2 = string_buffer("ccc");
+ ASSERT_EQ(ioctx.omap_set(oid, {{key, value1}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::EQ, {{key, value2}}), -EINVAL);
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_u64_eio)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const std::string key = "key";
+ const bufferlist value1 = string_buffer("ccc");
+ const bufferlist value2 = u64_buffer(0);
+ ASSERT_EQ(ioctx.omap_set(oid, {{key, value1}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::EQ, {{key, value2}}), 0);
+ {
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ EXPECT_EQ(vals.count(key), 1);
+ }
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_at_max_keys)
+{
+ ComparisonMap comparisons;
+ const bufferlist value = u64_buffer(0);
+ for (uint32_t i = 0; i < max_keys; i++) {
+ comparisons.emplace(std::to_string(i), value);
+ }
+ librados::ObjectWriteOperation op;
+ EXPECT_EQ(cmp_rm_keys(op, Mode::U64, Op::EQ, std::move(comparisons)), 0);
+}
+
+TEST_F(CmpOmap, cmp_rm_keys_over_max_keys)
+{
+ ComparisonMap comparisons;
+ const bufferlist value = u64_buffer(0);
+ for (uint32_t i = 0; i < max_keys + 1; i++) {
+ comparisons.emplace(std::to_string(i), value);
+ }
+ librados::ObjectWriteOperation op;
+ EXPECT_EQ(cmp_rm_keys(op, Mode::U64, Op::EQ, std::move(comparisons)), -E2BIG);
+}
+
+// test upgrades from empty omap values to u64
+TEST_F(CmpOmap, cmp_rm_keys_u64_empty)
+{
+ const std::string oid = __PRETTY_FUNCTION__;
+ const bufferlist value1; // empty buffer
+ const bufferlist value2 = u64_buffer(42);
+ {
+ std::map<std::string, bufferlist> vals = {
+ {"eq", value1},
+ {"ne", value1},
+ {"gt", value1},
+ {"gte", value1},
+ {"lt", value1},
+ {"lte", value1},
+ };
+ ASSERT_EQ(ioctx.omap_set(oid, vals), 0);
+ }
+
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::EQ, {{"eq", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::NE, {{"ne", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::GT, {{"gt", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::GTE, {{"gte", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::LT, {{"lt", value2}}), 0);
+ ASSERT_EQ(do_cmp_rm_keys(oid, Mode::U64, Op::LTE, {{"lte", value2}}), 0);
+
+ {
+ std::map<std::string, bufferlist> vals;
+ ASSERT_EQ(get_vals(oid, &vals), 0);
+ EXPECT_EQ(vals.count("eq"), 1);
+ EXPECT_EQ(vals.count("ne"), 0);
+ EXPECT_EQ(vals.count("gt"), 0);
+ EXPECT_EQ(vals.count("gte"), 0);
+ EXPECT_EQ(vals.count("lt"), 1);
+ EXPECT_EQ(vals.count("lte"), 1);
+ }
+}
+
+} // namespace cls::cmpomap
diff --git a/src/test/cls_hello/CMakeLists.txt b/src/test/cls_hello/CMakeLists.txt
new file mode 100644
index 000000000..9efb95e03
--- /dev/null
+++ b/src/test/cls_hello/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_executable(ceph_test_cls_hello
+ test_cls_hello.cc
+ )
+target_link_libraries(ceph_test_cls_hello
+ librados
+ global
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ )
+install(TARGETS
+ ceph_test_cls_hello
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_hello/test_cls_hello.cc b/src/test/cls_hello/test_cls_hello.cc
new file mode 100644
index 000000000..cb05bb77b
--- /dev/null
+++ b/src/test/cls_hello/test_cls_hello.cc
@@ -0,0 +1,283 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <errno.h>
+#include <string>
+
+#include "include/rados/librados.hpp"
+#include "include/encoding.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+#include "json_spirit/json_spirit.h"
+
+using namespace librados;
+
+TEST(ClsHello, SayHello) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist in, out;
+ ASSERT_EQ(-ENOENT, ioctx.exec("myobject", "hello", "say_hello", in, out));
+ ASSERT_EQ(0, ioctx.write_full("myobject", in));
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "say_hello", in, out));
+ ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
+
+ out.clear();
+ in.append("Tester");
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "say_hello", in, out));
+ ASSERT_EQ(std::string("Hello, Tester!"), std::string(out.c_str(), out.length()));
+
+ out.clear();
+ in.clear();
+ char buf[4096];
+ memset(buf, 1, sizeof(buf));
+ in.append(buf, sizeof(buf));
+ ASSERT_EQ(-EINVAL, ioctx.exec("myobject", "hello", "say_hello", in, out));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsHello, RecordHello) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist in, out;
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "record_hello", in, out));
+ ASSERT_EQ(-EEXIST, ioctx.exec("myobject", "hello", "record_hello", in, out));
+
+ in.append("Tester");
+ ASSERT_EQ(0, ioctx.exec("myobject2", "hello", "record_hello", in, out));
+ ASSERT_EQ(-EEXIST, ioctx.exec("myobject2", "hello", "record_hello", in, out));
+ ASSERT_EQ(0u, out.length());
+
+ in.clear();
+ out.clear();
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "replay", in, out));
+ ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
+ out.clear();
+ ASSERT_EQ(0, ioctx.exec("myobject2", "hello", "replay", in, out));
+ ASSERT_EQ(std::string("Hello, Tester!"), std::string(out.c_str(), out.length()));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+static std::string _get_required_osd_release(Rados& cluster)
+{
+ bufferlist inbl;
+ std::string cmd = std::string("{\"prefix\": \"osd dump\",\"format\":\"json\"}");
+ bufferlist outbl;
+ int r = cluster.mon_command(cmd, inbl, &outbl, NULL);
+ ceph_assert(r >= 0);
+ std::string outstr(outbl.c_str(), outbl.length());
+ json_spirit::Value v;
+ if (!json_spirit::read(outstr, v)) {
+ std::cerr <<" unable to parse json " << outstr << std::endl;
+ return "";
+ }
+
+ json_spirit::Object& o = v.get_obj();
+ for (json_spirit::Object::size_type i=0; i<o.size(); i++) {
+ json_spirit::Pair& p = o[i];
+ if (p.name_ == "require_osd_release") {
+ std::cout << "require_osd_release = " << p.value_.get_str() << std::endl;
+ return p.value_.get_str();
+ }
+ }
+ std::cerr << "didn't find require_osd_release in " << outstr << std::endl;
+ return "";
+}
+
+TEST(ClsHello, WriteReturnData) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ // skip test if not yet mimic
+ if (_get_required_osd_release(cluster) < "octopus") {
+ std::cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ // this will return nothing -- no flag is set
+ bufferlist in, out;
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "write_return_data", in, out));
+ ASSERT_EQ(std::string(), std::string(out.c_str(), out.length()));
+
+ // this will return an error due to unexpected input.
+ // note that we set the RETURNVEC flag here so that we *reliably* get the
+ // "too much input data!" return data string. do it lots of times so we are
+ // more likely to resend a request and hit the dup op handling path.
+ char buf[4096];
+ memset(buf, 1, sizeof(buf));
+ for (unsigned i=0; i<1000; ++i) {
+ std::cout << i << std::endl;
+ in.clear();
+ in.append(buf, sizeof(buf));
+ int rval;
+ ObjectWriteOperation o;
+ o.exec("hello", "write_return_data", in, &out, &rval);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &o,
+ librados::OPERATION_RETURNVEC));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EINVAL, completion->get_return_value());
+ ASSERT_EQ(-EINVAL, rval);
+ ASSERT_EQ(std::string("too much input data!"), std::string(out.c_str(), out.length()));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.getxattr("myobject2", "foo", out));
+
+ // this *will* return data due to the RETURNVEC flag
+ // using a-sync call
+ {
+ in.clear();
+ out.clear();
+ int rval;
+ ObjectWriteOperation o;
+ o.exec("hello", "write_return_data", in, &out, &rval);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &o,
+ librados::OPERATION_RETURNVEC));
+ completion->wait_for_complete();
+ ASSERT_EQ(42, completion->get_return_value());
+ ASSERT_EQ(42, rval);
+ out.hexdump(std::cout);
+ ASSERT_EQ("you might see this", std::string(out.c_str(), out.length()));
+ }
+ // using sync call
+ {
+ in.clear();
+ out.clear();
+ int rval;
+ ObjectWriteOperation o;
+ o.exec("hello", "write_return_data", in, &out, &rval);
+ ASSERT_EQ(42, ioctx.operate("foo", &o,
+ librados::OPERATION_RETURNVEC));
+ ASSERT_EQ(42, rval);
+ out.hexdump(std::cout);
+ ASSERT_EQ("you might see this", std::string(out.c_str(), out.length()));
+ }
+
+ // this will overflow because the return data is too big
+ {
+ in.clear();
+ out.clear();
+ int rval;
+ ObjectWriteOperation o;
+ o.exec("hello", "write_too_much_return_data", in, &out, &rval);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &o,
+ librados::OPERATION_RETURNVEC));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EOVERFLOW, completion->get_return_value());
+ ASSERT_EQ(-EOVERFLOW, rval);
+ ASSERT_EQ("", std::string(out.c_str(), out.length()));
+ }
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsHello, Loud) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist in, out;
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "record_hello", in, out));
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "replay", in, out));
+ ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
+
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "turn_it_to_11", in, out));
+ ASSERT_EQ(0, ioctx.exec("myobject", "hello", "replay", in, out));
+ ASSERT_EQ(std::string("HELLO, WORLD!"), std::string(out.c_str(), out.length()));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsHello, BadMethods) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist in, out;
+
+ ASSERT_EQ(0, ioctx.write_full("myobject", in));
+ ASSERT_EQ(-EIO, ioctx.exec("myobject", "hello", "bad_reader", in, out));
+ ASSERT_EQ(-EIO, ioctx.exec("myobject", "hello", "bad_writer", in, out));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsHello, Filter) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist obj_content;
+ obj_content.append(buf, sizeof(buf));
+
+ std::string target_str = "content";
+
+ // Write xattr bare, no ::encod'ing
+ bufferlist target_val;
+ target_val.append(target_str);
+ bufferlist nontarget_val;
+ nontarget_val.append("rhubarb");
+
+ ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
+ ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
+ ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
+
+ ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
+ ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
+
+ bufferlist filter_bl;
+ std::string filter_name = "hello.hello";
+ encode(filter_name, filter_bl);
+ encode("_theattr", filter_bl);
+ encode(target_str, filter_bl);
+
+ NObjectIterator iter(ioctx.nobjects_begin(filter_bl));
+ bool foundit = false;
+ int k = 0;
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ // We should only see the object that matches the filter
+ ASSERT_EQ((*iter).get_oid(), "has_xattr");
+ // We should only see it once
+ ASSERT_EQ(k, 0);
+ ++iter;
+ ++k;
+ }
+ ASSERT_TRUE(foundit);
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
diff --git a/src/test/cls_journal/CMakeLists.txt b/src/test/cls_journal/CMakeLists.txt
new file mode 100644
index 000000000..eeb943330
--- /dev/null
+++ b/src/test/cls_journal/CMakeLists.txt
@@ -0,0 +1,16 @@
+# cls_test_cls_journal
+add_executable(ceph_test_cls_journal
+ test_cls_journal.cc
+ $<TARGET_OBJECTS:common_texttable_obj>)
+target_link_libraries(ceph_test_cls_journal
+ cls_journal_client
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ radostest-cxx)
+install(TARGETS
+ ceph_test_cls_journal
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_journal/test_cls_journal.cc b/src/test/cls_journal/test_cls_journal.cc
new file mode 100644
index 000000000..16fc7a7d0
--- /dev/null
+++ b/src/test/cls_journal/test_cls_journal.cc
@@ -0,0 +1,695 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/journal/cls_journal_client.h"
+#include "include/stringify.h"
+#include "common/Cond.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+#include <errno.h>
+#include <set>
+#include <string>
+
+using namespace cls::journal;
+
+static bool is_sparse_read_supported(librados::IoCtx &ioctx,
+ const std::string &oid) {
+ EXPECT_EQ(0, ioctx.create(oid, true));
+ bufferlist inbl;
+ inbl.append(std::string(1, 'X'));
+ EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 1));
+ EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 3));
+
+ std::map<uint64_t, uint64_t> m;
+ bufferlist outbl;
+ int r = ioctx.sparse_read(oid, m, outbl, 4, 0);
+ ioctx.remove(oid);
+
+ int expected_r = 2;
+ std::map<uint64_t, uint64_t> expected_m = {{1, 1}, {3, 1}};
+ bufferlist expected_outbl;
+ expected_outbl.append(std::string(2, 'X'));
+
+ return (r == expected_r && m == expected_m &&
+ outbl.contents_equal(expected_outbl));
+}
+
+class TestClsJournal : public ::testing::Test {
+public:
+
+ static void SetUpTestCase() {
+ _pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(_pool_name, _rados));
+ }
+
+ static void TearDownTestCase() {
+ ASSERT_EQ(0, destroy_one_pool_pp(_pool_name, _rados));
+ }
+
+ std::string get_temp_image_name() {
+ ++_image_number;
+ return "image" + stringify(_image_number);
+ }
+
+ static std::string _pool_name;
+ static librados::Rados _rados;
+ static uint64_t _image_number;
+
+};
+
+std::string TestClsJournal::_pool_name;
+librados::Rados TestClsJournal::_rados;
+uint64_t TestClsJournal::_image_number = 0;
+
+TEST_F(TestClsJournal, Create) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ uint8_t order = 1;
+ uint8_t splay_width = 2;
+ int64_t pool_id = ioctx.get_id();
+ ASSERT_EQ(0, client::create(ioctx, oid, order, splay_width, pool_id));
+
+ uint8_t read_order;
+ uint8_t read_splay_width;
+ int64_t read_pool_id;
+ C_SaferCond cond;
+ client::get_immutable_metadata(ioctx, oid, &read_order, &read_splay_width,
+ &read_pool_id, &cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(order, read_order);
+ ASSERT_EQ(splay_width, read_splay_width);
+ ASSERT_EQ(pool_id, read_pool_id);
+}
+
+TEST_F(TestClsJournal, MinimumSet) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ librados::ObjectWriteOperation op1;
+ client::set_active_set(&op1, 300);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ uint64_t minimum_set = 123;
+ librados::ObjectWriteOperation op2;
+ client::set_minimum_set(&op2, minimum_set);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ C_SaferCond cond;
+ uint64_t read_minimum_set;
+ uint64_t read_active_set;
+ std::set<cls::journal::Client> read_clients;
+ client::get_mutable_metadata(ioctx, oid, &read_minimum_set, &read_active_set,
+ &read_clients, &cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(minimum_set, read_minimum_set);
+}
+
+TEST_F(TestClsJournal, MinimumSetStale) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ librados::ObjectWriteOperation op1;
+ client::set_active_set(&op1, 300);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ uint64_t minimum_set = 123;
+ librados::ObjectWriteOperation op2;
+ client::set_minimum_set(&op2, minimum_set);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ librados::ObjectWriteOperation op3;
+ client::set_minimum_set(&op3, 1);
+ ASSERT_EQ(-ESTALE, ioctx.operate(oid, &op3));
+
+ C_SaferCond cond;
+ uint64_t read_minimum_set;
+ uint64_t read_active_set;
+ std::set<cls::journal::Client> read_clients;
+ client::get_mutable_metadata(ioctx, oid, &read_minimum_set, &read_active_set,
+ &read_clients, &cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(minimum_set, read_minimum_set);
+}
+
+TEST_F(TestClsJournal, MinimumSetOrderConstraint) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ librados::ObjectWriteOperation op1;
+ client::set_minimum_set(&op1, 123);
+ ASSERT_EQ(-EINVAL, ioctx.operate(oid, &op1));
+
+ C_SaferCond cond;
+ uint64_t read_minimum_set;
+ uint64_t read_active_set;
+ std::set<cls::journal::Client> read_clients;
+ client::get_mutable_metadata(ioctx, oid, &read_minimum_set, &read_active_set,
+ &read_clients, &cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(0U, read_minimum_set);
+}
+
+TEST_F(TestClsJournal, ActiveSet) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ uint64_t active_set = 234;
+ librados::ObjectWriteOperation op1;
+ client::set_active_set(&op1, active_set);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ C_SaferCond cond;
+ uint64_t read_minimum_set;
+ uint64_t read_active_set;
+ std::set<cls::journal::Client> read_clients;
+ client::get_mutable_metadata(ioctx, oid, &read_minimum_set, &read_active_set,
+ &read_clients, &cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(active_set, read_active_set);
+}
+
+TEST_F(TestClsJournal, ActiveSetStale) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ librados::ObjectWriteOperation op1;
+ client::set_active_set(&op1, 345);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ client::set_active_set(&op2, 3);
+ ASSERT_EQ(-ESTALE, ioctx.operate(oid, &op2));
+}
+
+TEST_F(TestClsJournal, CreateDuplicate) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+ ASSERT_EQ(-EEXIST, client::create(ioctx, oid, 3, 5, ioctx.get_id()));
+}
+
+TEST_F(TestClsJournal, GetClient) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ Client client;
+ ASSERT_EQ(-ENOENT, client::get_client(ioctx, oid, "id", &client));
+
+ bufferlist data;
+ data.append(std::string(128, '1'));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", data));
+
+ ASSERT_EQ(0, client::get_client(ioctx, oid, "id1", &client));
+ Client expected_client("id1", data);
+ ASSERT_EQ(expected_client, client);
+}
+
+TEST_F(TestClsJournal, ClientRegister) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ std::set<Client> clients;
+ ASSERT_EQ(0, client::client_list(ioctx, oid, &clients));
+
+ std::set<Client> expected_clients = {Client("id1", bufferlist())};
+ ASSERT_EQ(expected_clients, clients);
+}
+
+TEST_F(TestClsJournal, ClientRegisterDuplicate) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+ ASSERT_EQ(-EEXIST, client::client_register(ioctx, oid, "id1", bufferlist()));
+}
+
+TEST_F(TestClsJournal, ClientUpdateData) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ ASSERT_EQ(-ENOENT, client::client_update_data(ioctx, oid, "id1",
+ bufferlist()));
+
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ bufferlist data;
+ data.append(std::string(128, '1'));
+ ASSERT_EQ(0, client::client_update_data(ioctx, oid, "id1", data));
+
+ Client client;
+ ASSERT_EQ(0, client::get_client(ioctx, oid, "id1", &client));
+ Client expected_client("id1", data);
+ ASSERT_EQ(expected_client, client);
+}
+
+TEST_F(TestClsJournal, ClientUpdateState) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ ASSERT_EQ(-ENOENT, client::client_update_state(ioctx, oid, "id1",
+ CLIENT_STATE_DISCONNECTED));
+
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ bufferlist data;
+ data.append(std::string(128, '1'));
+ ASSERT_EQ(0, client::client_update_state(ioctx, oid, "id1",
+ CLIENT_STATE_DISCONNECTED));
+
+ Client client;
+ ASSERT_EQ(0, client::get_client(ioctx, oid, "id1", &client));
+ Client expected_client;
+ expected_client.id = "id1";
+ expected_client.state = CLIENT_STATE_DISCONNECTED;
+ ASSERT_EQ(expected_client, client);
+}
+
+TEST_F(TestClsJournal, ClientUnregister) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+ ASSERT_EQ(0, client::client_unregister(ioctx, oid, "id1"));
+}
+
+TEST_F(TestClsJournal, ClientUnregisterDNE) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 4, ioctx.get_id()));
+
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+ ASSERT_EQ(0, client::client_unregister(ioctx, oid, "id1"));
+ ASSERT_EQ(-ENOENT, client::client_unregister(ioctx, oid, "id1"));
+}
+
+TEST_F(TestClsJournal, ClientUnregisterPruneTags) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 2, ioctx.get_id()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id2", bufferlist()));
+
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 0, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 1, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+
+ for (uint32_t i = 2; i <= 96; ++i) {
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, i, 1, bufferlist()));
+ }
+
+ librados::ObjectWriteOperation op1;
+ client::client_commit(&op1, "id1", {{{1, 32, 120}}});
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ ASSERT_EQ(0, client::client_unregister(ioctx, oid, "id2"));
+
+ std::set<Tag> expected_tags = {{0, 0, {}}};
+ for (uint32_t i = 32; i <= 96; ++i) {
+ expected_tags.insert({i, 1, {}});
+ }
+ std::set<Tag> tags;
+ ASSERT_EQ(0, client::tag_list(ioctx, oid, "id1",
+ boost::optional<uint64_t>(), &tags));
+ ASSERT_EQ(expected_tags, tags);
+}
+
+TEST_F(TestClsJournal, ClientCommit) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 2, ioctx.get_id()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ cls::journal::ObjectPositions object_positions;
+ object_positions = {
+ cls::journal::ObjectPosition(0, 234, 120),
+ cls::journal::ObjectPosition(3, 235, 121)};
+ cls::journal::ObjectSetPosition object_set_position(
+ object_positions);
+
+ librados::ObjectWriteOperation op2;
+ client::client_commit(&op2, "id1", object_set_position);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ std::set<Client> clients;
+ ASSERT_EQ(0, client::client_list(ioctx, oid, &clients));
+
+ std::set<Client> expected_clients = {
+ Client("id1", bufferlist(), object_set_position)};
+ ASSERT_EQ(expected_clients, clients);
+}
+
+TEST_F(TestClsJournal, ClientCommitInvalid) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 2, ioctx.get_id()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ cls::journal::ObjectPositions object_positions;
+ object_positions = {
+ cls::journal::ObjectPosition(0, 234, 120),
+ cls::journal::ObjectPosition(4, 234, 121),
+ cls::journal::ObjectPosition(5, 235, 121)};
+ cls::journal::ObjectSetPosition object_set_position(
+ object_positions);
+
+ librados::ObjectWriteOperation op2;
+ client::client_commit(&op2, "id1", object_set_position);
+ ASSERT_EQ(-EINVAL, ioctx.operate(oid, &op2));
+}
+
+TEST_F(TestClsJournal, ClientCommitDNE) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ cls::journal::ObjectSetPosition object_set_position;
+
+ librados::ObjectWriteOperation op1;
+ client::client_commit(&op1, "id1", object_set_position);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, &op1));
+}
+
+TEST_F(TestClsJournal, ClientList) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 12, 5, ioctx.get_id()));
+
+ std::set<Client> expected_clients;
+ librados::ObjectWriteOperation op1;
+ for (uint32_t i = 0; i < 512; ++i) {
+ std::string id = "id" + stringify(i + 1);
+ expected_clients.insert(Client(id, bufferlist()));
+ client::client_register(&op1, id, bufferlist());
+ }
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ std::set<Client> clients;
+ ASSERT_EQ(0, client::client_list(ioctx, oid, &clients));
+ ASSERT_EQ(expected_clients, clients);
+
+ C_SaferCond cond;
+ uint64_t read_minimum_set;
+ uint64_t read_active_set;
+ std::set<cls::journal::Client> read_clients;
+ client::get_mutable_metadata(ioctx, oid, &read_minimum_set, &read_active_set,
+ &read_clients, &cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(expected_clients, read_clients);
+}
+
+TEST_F(TestClsJournal, GetNextTagTid) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ uint64_t tag_tid;
+ ASSERT_EQ(-ENOENT, client::get_next_tag_tid(ioctx, oid, &tag_tid));
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 2, ioctx.get_id()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ ASSERT_EQ(0, client::get_next_tag_tid(ioctx, oid, &tag_tid));
+ ASSERT_EQ(0U, tag_tid);
+
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 0, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(0, client::get_next_tag_tid(ioctx, oid, &tag_tid));
+ ASSERT_EQ(1U, tag_tid);
+}
+
+TEST_F(TestClsJournal, TagCreate) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(-ENOENT, client::tag_create(ioctx, oid, 0, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 2, ioctx.get_id()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ ASSERT_EQ(-ESTALE, client::tag_create(ioctx, oid, 1, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(-EINVAL, client::tag_create(ioctx, oid, 0, 1, bufferlist()));
+
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 0, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(-EEXIST, client::tag_create(ioctx, oid, 0, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 1, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 2, 1, bufferlist()));
+
+ std::set<Tag> expected_tags = {
+ {0, 0, {}}, {1, 1, {}}, {2, 1, {}}};
+ std::set<Tag> tags;
+ ASSERT_EQ(0, client::tag_list(ioctx, oid, "id1",
+ boost::optional<uint64_t>(), &tags));
+ ASSERT_EQ(expected_tags, tags);
+}
+
+TEST_F(TestClsJournal, TagCreatePrunesTags) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 2, ioctx.get_id()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 0, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 1, Tag::TAG_CLASS_NEW,
+ bufferlist()));
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 2, 1, bufferlist()));
+
+ librados::ObjectWriteOperation op1;
+ client::client_commit(&op1, "id1", {{{1, 2, 120}}});
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, 3, 0, bufferlist()));
+
+ std::set<Tag> expected_tags = {
+ {0, 0, {}}, {2, 1, {}}, {3, 0, {}}};
+ std::set<Tag> tags;
+ ASSERT_EQ(0, client::tag_list(ioctx, oid, "id1",
+ boost::optional<uint64_t>(), &tags));
+ ASSERT_EQ(expected_tags, tags);
+}
+
+TEST_F(TestClsJournal, TagList) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, client::create(ioctx, oid, 2, 2, ioctx.get_id()));
+ ASSERT_EQ(0, client::client_register(ioctx, oid, "id1", bufferlist()));
+
+ std::set<Tag> expected_all_tags;
+ std::set<Tag> expected_filtered_tags;
+ for (uint32_t i = 0; i < 96; ++i) {
+ uint64_t tag_class = Tag::TAG_CLASS_NEW;
+ if (i > 1) {
+ tag_class = i % 2 == 0 ? 0 : 1;
+ }
+
+ Tag tag(i, i % 2 == 0 ? 0 : 1, bufferlist());
+ expected_all_tags.insert(tag);
+ if (i % 2 == 0) {
+ expected_filtered_tags.insert(tag);
+ }
+ ASSERT_EQ(0, client::tag_create(ioctx, oid, i, tag_class,
+ bufferlist()));
+ }
+
+ std::set<Tag> tags;
+ ASSERT_EQ(0, client::tag_list(ioctx, oid, "id1", boost::optional<uint64_t>(),
+ &tags));
+ ASSERT_EQ(expected_all_tags, tags);
+
+ ASSERT_EQ(0, client::tag_list(ioctx, oid, "id1", boost::optional<uint64_t>(0),
+ &tags));
+ ASSERT_EQ(expected_filtered_tags, tags);
+
+ librados::ObjectWriteOperation op1;
+ client::client_commit(&op1, "id1", {{{96, 0, 120}}});
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ ASSERT_EQ(0, client::tag_list(ioctx, oid, "id1", boost::optional<uint64_t>(),
+ &tags));
+ ASSERT_EQ(expected_all_tags, tags);
+}
+
+TEST_F(TestClsJournal, GuardAppend) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ bufferlist bl;
+ bl.append("journal entry!");
+
+ librados::ObjectWriteOperation op1;
+ op1.append(bl);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ client::guard_append(&op2, 1024);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+}
+
+TEST_F(TestClsJournal, GuardAppendDNE) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ librados::ObjectWriteOperation op2;
+ client::guard_append(&op2, 1024);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+}
+
+TEST_F(TestClsJournal, GuardAppendOverflow) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+
+ bufferlist bl;
+ bl.append("journal entry!");
+
+ librados::ObjectWriteOperation op1;
+ op1.append(bl);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ client::guard_append(&op2, 1);
+ ASSERT_EQ(-EOVERFLOW, ioctx.operate(oid, &op2));
+}
+
+TEST_F(TestClsJournal, Append) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ ioctx.remove(oid);
+
+ bool sparse_read_supported = is_sparse_read_supported(ioctx, oid);
+
+ bufferlist bl;
+ bl.append("journal entry!");
+
+ librados::ObjectWriteOperation op1;
+ client::append(&op1, 1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ client::append(&op2, 1, bl);
+ ASSERT_EQ(-EOVERFLOW, ioctx.operate(oid, &op2));
+
+ bufferlist outbl;
+ ASSERT_LE(bl.length(), ioctx.read(oid, outbl, 0, 0));
+
+ bufferlist tmpbl;
+ tmpbl.substr_of(outbl, 0, bl.length());
+ ASSERT_TRUE(bl.contents_equal(tmpbl));
+
+ if (outbl.length() == bl.length()) {
+ std::cout << "padding is not enabled" << std::endl;
+ return;
+ }
+
+ tmpbl.clear();
+ tmpbl.substr_of(outbl, bl.length(), outbl.length() - bl.length());
+ ASSERT_TRUE(tmpbl.is_zero());
+
+ if (!sparse_read_supported) {
+ std::cout << "sparse_read is not supported" << std::endl;
+ return;
+ }
+
+ librados::ObjectWriteOperation op3;
+ client::append(&op3, 1 << 24, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+
+ std::map<uint64_t, uint64_t> m;
+ uint64_t pad_len = outbl.length();
+ outbl.clear();
+ std::map<uint64_t, uint64_t> expected_m =
+ {{0, bl.length()}, {pad_len, bl.length()}};
+ ASSERT_EQ(expected_m.size(), ioctx.sparse_read(oid, m, outbl, 2 * pad_len, 0));
+ ASSERT_EQ(m, expected_m);
+
+ uint64_t buffer_offset = 0;
+ for (auto &it : m) {
+ tmpbl.clear();
+ tmpbl.substr_of(outbl, buffer_offset, it.second);
+ ASSERT_TRUE(bl.contents_equal(tmpbl));
+ buffer_offset += it.second;
+ }
+}
diff --git a/src/test/cls_lock/CMakeLists.txt b/src/test/cls_lock/CMakeLists.txt
new file mode 100644
index 000000000..eef2de40e
--- /dev/null
+++ b/src/test/cls_lock/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_executable(ceph_test_cls_lock
+ test_cls_lock.cc
+ )
+target_link_libraries(ceph_test_cls_lock
+ cls_lock_client
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ radostest-cxx
+ )
+install(TARGETS
+ ceph_test_cls_lock
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_lock/test_cls_lock.cc b/src/test/cls_lock/test_cls_lock.cc
new file mode 100644
index 000000000..862720826
--- /dev/null
+++ b/src/test/cls_lock/test_cls_lock.cc
@@ -0,0 +1,589 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <errno.h>
+
+#include "include/types.h"
+#include "common/Clock.h"
+#include "msg/msg_types.h"
+#include "include/rados/librados.hpp"
+
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+#include "cls/lock/cls_lock_client.h"
+#include "cls/lock/cls_lock_ops.h"
+
+using namespace std;
+using namespace librados;
+using namespace rados::cls::lock;
+
+void lock_info(IoCtx *ioctx, string& oid, string& name, map<locker_id_t, locker_info_t>& lockers,
+ ClsLockType *assert_type, string *assert_tag)
+{
+ ClsLockType lock_type = ClsLockType::NONE;
+ string tag;
+ lockers.clear();
+ ASSERT_EQ(0, get_lock_info(ioctx, oid, name, &lockers, &lock_type, &tag));
+ cout << "lock: " << name << std::endl;
+ cout << " lock_type: " << cls_lock_type_str(lock_type) << std::endl;
+ cout << " tag: " << tag << std::endl;
+ cout << " lockers:" << std::endl;
+
+ if (assert_type) {
+ ASSERT_EQ(*assert_type, lock_type);
+ }
+
+ if (assert_tag) {
+ ASSERT_EQ(*assert_tag, tag);
+ }
+
+ map<locker_id_t, locker_info_t>::iterator liter;
+ for (liter = lockers.begin(); liter != lockers.end(); ++liter) {
+ const locker_id_t& locker = liter->first;
+ cout << " " << locker.locker << " expiration=" << liter->second.expiration
+ << " addr=" << liter->second.addr << " cookie=" << locker.cookie << std::endl;
+ }
+}
+
+void lock_info(IoCtx *ioctx, string& oid, string& name, map<locker_id_t, locker_info_t>& lockers)
+{
+ lock_info(ioctx, oid, name, lockers, NULL, NULL);
+}
+
+bool lock_expired(IoCtx *ioctx, string& oid, string& name)
+{
+ ClsLockType lock_type = ClsLockType::NONE;
+ string tag;
+ map<locker_id_t, locker_info_t> lockers;
+ if (0 == get_lock_info(ioctx, oid, name, &lockers, &lock_type, &tag))
+ return false;
+ utime_t now = ceph_clock_now();
+ map<locker_id_t, locker_info_t>::iterator liter;
+ for (liter = lockers.begin(); liter != lockers.end(); ++liter) {
+ if (liter->second.expiration > now)
+ return false;
+ }
+
+ return true;
+}
+
+TEST(ClsLock, TestMultiLocking) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+ ClsLockType lock_type_shared = ClsLockType::SHARED;
+ ClsLockType lock_type_exclusive = ClsLockType::EXCLUSIVE;
+
+
+ Rados cluster2;
+ IoCtx ioctx2;
+ ASSERT_EQ("", connect_cluster_pp(cluster2));
+ cluster2.ioctx_create(pool_name.c_str(), ioctx2);
+
+ string oid = "foo";
+ bufferlist bl;
+ string lock_name = "mylock";
+
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ Lock l(lock_name);
+ // we set the duration, so the log output contains a locker with a
+ // non-zero expiration time
+ l.set_duration(utime_t(120, 0));
+
+ /* test lock object */
+
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ /* test exclusive lock */
+ ASSERT_EQ(-EEXIST, l.lock_exclusive(&ioctx, oid));
+
+ /* test idempotency */
+ l.set_may_renew(true);
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ l.set_may_renew(false);
+
+ /* test second client */
+ Lock l2(lock_name);
+ ASSERT_EQ(-EBUSY, l2.lock_exclusive(&ioctx2, oid));
+ ASSERT_EQ(-EBUSY, l2.lock_shared(&ioctx2, oid));
+
+ list<string> locks;
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+
+ ASSERT_EQ(1, (int)locks.size());
+ list<string>::iterator iter = locks.begin();
+ map<locker_id_t, locker_info_t> lockers;
+ lock_info(&ioctx, oid, *iter, lockers, &lock_type_exclusive, NULL);
+
+ ASSERT_EQ(1, (int)lockers.size());
+
+ /* test unlock */
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+ locks.clear();
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+
+ /* test shared lock */
+ ASSERT_EQ(0, l2.lock_shared(&ioctx2, oid));
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ locks.clear();
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+ ASSERT_EQ(1, (int)locks.size());
+ iter = locks.begin();
+ lock_info(&ioctx, oid, *iter, lockers, &lock_type_shared, NULL);
+ ASSERT_EQ(2, (int)lockers.size());
+
+ /* test break locks */
+ entity_name_t name = entity_name_t::CLIENT(cluster.get_instance_id());
+ entity_name_t name2 = entity_name_t::CLIENT(cluster2.get_instance_id());
+
+ l2.break_lock(&ioctx2, oid, name);
+ lock_info(&ioctx, oid, *iter, lockers);
+ ASSERT_EQ(1, (int)lockers.size());
+ map<locker_id_t, locker_info_t>::iterator liter = lockers.begin();
+ const locker_id_t& id = liter->first;
+ ASSERT_EQ(name2, id.locker);
+
+ /* test lock tag */
+ Lock l_tag(lock_name);
+ l_tag.set_tag("non-default tag");
+ ASSERT_EQ(-EBUSY, l_tag.lock_shared(&ioctx, oid));
+
+
+ /* test modify description */
+ string description = "new description";
+ l.set_description(description);
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestMeta) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+
+ Rados cluster2;
+ IoCtx ioctx2;
+ ASSERT_EQ("", connect_cluster_pp(cluster2));
+ cluster2.ioctx_create(pool_name.c_str(), ioctx2);
+
+ string oid = "foo";
+ bufferlist bl;
+ string lock_name = "mylock";
+
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ Lock l(lock_name);
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ /* test lock tag */
+ Lock l_tag(lock_name);
+ l_tag.set_tag("non-default tag");
+ ASSERT_EQ(-EBUSY, l_tag.lock_shared(&ioctx2, oid));
+
+
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+
+ /* test description */
+ Lock l2(lock_name);
+ string description = "new description";
+ l2.set_description(description);
+ ASSERT_EQ(0, l2.lock_shared(&ioctx2, oid));
+
+ map<locker_id_t, locker_info_t> lockers;
+ lock_info(&ioctx, oid, lock_name, lockers, NULL, NULL);
+ ASSERT_EQ(1, (int)lockers.size());
+
+ map<locker_id_t, locker_info_t>::iterator iter = lockers.begin();
+ locker_info_t locker = iter->second;
+ ASSERT_EQ("new description", locker.description);
+
+ ASSERT_EQ(0, l2.unlock(&ioctx2, oid));
+
+ /* check new tag */
+ string new_tag = "new_tag";
+ l.set_tag(new_tag);
+ l.set_may_renew(true);
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+ lock_info(&ioctx, oid, lock_name, lockers, NULL, &new_tag);
+ ASSERT_EQ(1, (int)lockers.size());
+ l.set_tag("");
+ ASSERT_EQ(-EBUSY, l.lock_exclusive(&ioctx, oid));
+ l.set_tag(new_tag);
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestCookie) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ string lock_name = "mylock";
+ Lock l(lock_name);
+
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ /* new cookie */
+ string cookie = "new cookie";
+ l.set_cookie(cookie);
+ ASSERT_EQ(-EBUSY, l.lock_exclusive(&ioctx, oid));
+ ASSERT_EQ(-ENOENT, l.unlock(&ioctx, oid));
+ l.set_cookie("");
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+
+ map<locker_id_t, locker_info_t> lockers;
+ lock_info(&ioctx, oid, lock_name, lockers);
+ ASSERT_EQ(0, (int)lockers.size());
+
+ l.set_cookie(cookie);
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+ l.set_cookie("");
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ lock_info(&ioctx, oid, lock_name, lockers);
+ ASSERT_EQ(2, (int)lockers.size());
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestMultipleLocks) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock1");
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ Lock l2("lock2");
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid));
+
+ list<string> locks;
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+
+ ASSERT_EQ(2, (int)locks.size());
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestLockDuration) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock");
+ utime_t dur(5, 0);
+ l.set_duration(dur);
+ utime_t start = ceph_clock_now();
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+ int r = l.lock_exclusive(&ioctx, oid);
+ if (r == 0) {
+ // it's possible to get success if we were just really slow...
+ ASSERT_TRUE(ceph_clock_now() > start + dur);
+ } else {
+ ASSERT_EQ(-EEXIST, r);
+ }
+
+ sleep(dur.sec());
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestAssertLocked) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock1");
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ librados::ObjectWriteOperation op1;
+ l.assert_locked_exclusive(&op1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ l.assert_locked_shared(&op2);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op2));
+
+ l.set_tag("tag");
+ librados::ObjectWriteOperation op3;
+ l.assert_locked_exclusive(&op3);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op3));
+ l.set_tag("");
+
+ l.set_cookie("cookie");
+ librados::ObjectWriteOperation op4;
+ l.assert_locked_exclusive(&op4);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4));
+ l.set_cookie("");
+
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+
+ librados::ObjectWriteOperation op5;
+ l.assert_locked_exclusive(&op5);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestSetCookie) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ string name = "name";
+ string tag = "tag";
+ string cookie = "cookie";
+ string new_cookie = "new cookie";
+ librados::ObjectWriteOperation op1;
+ set_cookie(&op1, name, ClsLockType::SHARED, cookie, tag, new_cookie);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ lock(&op2, name, ClsLockType::SHARED, cookie, tag, "", utime_t{}, 0);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ librados::ObjectWriteOperation op3;
+ lock(&op3, name, ClsLockType::SHARED, "cookie 2", tag, "", utime_t{}, 0);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+
+ librados::ObjectWriteOperation op4;
+ set_cookie(&op4, name, ClsLockType::SHARED, cookie, tag, cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4));
+
+ librados::ObjectWriteOperation op5;
+ set_cookie(&op5, name, ClsLockType::SHARED, cookie, "wrong tag", new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5));
+
+ librados::ObjectWriteOperation op6;
+ set_cookie(&op6, name, ClsLockType::SHARED, "wrong cookie", tag, new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op6));
+
+ librados::ObjectWriteOperation op7;
+ set_cookie(&op7, name, ClsLockType::EXCLUSIVE, cookie, tag, new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op7));
+
+ librados::ObjectWriteOperation op8;
+ set_cookie(&op8, name, ClsLockType::SHARED, cookie, tag, "cookie 2");
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op8));
+
+ librados::ObjectWriteOperation op9;
+ set_cookie(&op9, name, ClsLockType::SHARED, cookie, tag, new_cookie);
+ ASSERT_EQ(0, ioctx.operate(oid, &op9));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestRenew) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string lock_name1 = "mylock1";
+
+ ASSERT_EQ(0, ioctx.write(oid1, bl, bl.length(), 0));
+
+ Lock l1(lock_name1);
+ utime_t lock_duration1(5, 0);
+ l1.set_duration(lock_duration1);
+
+ ASSERT_EQ(0, l1.lock_exclusive(&ioctx, oid1));
+ l1.set_may_renew(true);
+ sleep(2);
+ ASSERT_EQ(0, l1.lock_exclusive(&ioctx, oid1));
+ sleep(7);
+ ASSERT_EQ(0, l1.lock_exclusive(&ioctx, oid1)) <<
+ "when a cls_lock is set to may_renew, a relock after expiration "
+ "should still work";
+ ASSERT_EQ(0, l1.unlock(&ioctx, oid1));
+
+ // ***********************************************
+
+ string oid2 = "foo2";
+ string lock_name2 = "mylock2";
+
+ ASSERT_EQ(0, ioctx.write(oid2, bl, bl.length(), 0));
+
+ Lock l2(lock_name2);
+ utime_t lock_duration2(5, 0);
+ l2.set_duration(lock_duration2);
+
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid2));
+ l2.set_must_renew(true);
+ sleep(2);
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid2));
+ sleep(7);
+ ASSERT_EQ(-ENOENT, l2.lock_exclusive(&ioctx, oid2)) <<
+ "when a cls_lock is set to must_renew, a relock after expiration "
+ "should fail";
+ ASSERT_EQ(-ENOENT, l2.unlock(&ioctx, oid2));
+
+ // ***********************************************
+
+ string oid3 = "foo3";
+ string lock_name3 = "mylock3";
+
+ ASSERT_EQ(0, ioctx.write(oid3, bl, bl.length(), 0));
+
+ Lock l3(lock_name3);
+ l3.set_duration(utime_t(5, 0));
+ l3.set_must_renew(true);
+
+ ASSERT_EQ(-ENOENT, l3.lock_exclusive(&ioctx, oid3)) <<
+ "unable to create a lock with must_renew";
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestExclusiveEphemeralBasic) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string oid2 = "foo2";
+ string lock_name1 = "mylock1";
+ string lock_name2 = "mylock2";
+
+ Lock l1(lock_name1);
+ l1.set_duration(utime_t(5, 0));
+
+ uint64_t size;
+ time_t mod_time;
+
+ l1.set_may_renew(true);
+ ASSERT_EQ(0, l1.lock_exclusive_ephemeral(&ioctx, oid1));
+ ASSERT_EQ(0, ioctx.stat(oid1, &size, &mod_time));
+ sleep(2);
+ int r1 = l1.unlock(&ioctx, oid1);
+ EXPECT_TRUE(r1 == 0 || ((r1 == -ENOENT) && (lock_expired(&ioctx, oid1, lock_name1))))
+ << "unlock should return 0 or -ENOENT return: " << r1;
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid1, &size, &mod_time));
+
+ // ***********************************************
+
+ Lock l2(lock_name2);
+ utime_t lock_duration2(5, 0);
+ l2.set_duration(utime_t(5, 0));
+
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid2));
+ ASSERT_EQ(0, ioctx.stat(oid2, &size, &mod_time));
+ sleep(2);
+ int r2 = l2.unlock(&ioctx, oid2);
+ EXPECT_TRUE(r2 == 0 || ((r2 == -ENOENT) && (lock_expired(&ioctx, oid2, lock_name2))))
+ << "unlock should return 0 or -ENOENT return: " << r2;
+ ASSERT_EQ(0, ioctx.stat(oid2, &size, &mod_time));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+
+TEST(ClsLock, TestExclusiveEphemeralStealEphemeral) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string lock_name1 = "mylock1";
+
+ Lock l1(lock_name1);
+ l1.set_duration(utime_t(3, 0));
+
+ ASSERT_EQ(0, l1.lock_exclusive_ephemeral(&ioctx, oid1));
+ sleep(4);
+
+ // l1 is expired, l2 can take; l2 is also exclusive_ephemeral
+ Lock l2(lock_name1);
+ l2.set_duration(utime_t(3, 0));
+ ASSERT_EQ(0, l2.lock_exclusive_ephemeral(&ioctx, oid1));
+ sleep(1);
+ ASSERT_EQ(0, l2.unlock(&ioctx, oid1));
+
+ // l2 cannot unlock its expired lock
+ ASSERT_EQ(-ENOENT, l1.unlock(&ioctx, oid1));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+
+TEST(ClsLock, TestExclusiveEphemeralStealExclusive) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string lock_name1 = "mylock1";
+
+ Lock l1(lock_name1);
+ l1.set_duration(utime_t(3, 0));
+
+ ASSERT_EQ(0, l1.lock_exclusive_ephemeral(&ioctx, oid1));
+ sleep(4);
+
+ // l1 is expired, l2 can take; l2 is exclusive (but not ephemeral)
+ Lock l2(lock_name1);
+ l2.set_duration(utime_t(3, 0));
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid1));
+ sleep(1);
+ ASSERT_EQ(0, l2.unlock(&ioctx, oid1));
+
+ // l2 cannot unlock its expired lock
+ ASSERT_EQ(-ENOENT, l1.unlock(&ioctx, oid1));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
diff --git a/src/test/cls_log/CMakeLists.txt b/src/test/cls_log/CMakeLists.txt
new file mode 100644
index 000000000..b5a88d47c
--- /dev/null
+++ b/src/test/cls_log/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_executable(ceph_test_cls_log
+ test_cls_log.cc)
+target_link_libraries(ceph_test_cls_log
+ librados
+ cls_log_client
+ global
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ )
+install(TARGETS
+ ceph_test_cls_log
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_log/test_cls_log.cc b/src/test/cls_log/test_cls_log.cc
new file mode 100644
index 000000000..e8777ac5f
--- /dev/null
+++ b/src/test/cls_log/test_cls_log.cc
@@ -0,0 +1,385 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "cls/log/cls_log_types.h"
+#include "cls/log/cls_log_client.h"
+
+#include "include/utime.h"
+#include "common/Clock.h"
+#include "global/global_context.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+/// creates a temporary pool and initializes an IoCtx for each test
+class cls_log : public ::testing::Test {
+ librados::Rados rados;
+ std::string pool_name;
+ protected:
+ librados::IoCtx ioctx;
+
+ void SetUp() {
+ pool_name = get_temp_pool_name();
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+ void TearDown() {
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+ }
+};
+
+static int read_bl(bufferlist& bl, int *i)
+{
+ auto iter = bl.cbegin();
+
+ try {
+ decode(*i, iter);
+ } catch (buffer::error& err) {
+ std::cout << "failed to decode buffer" << std::endl;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void add_log(librados::ObjectWriteOperation *op, utime_t& timestamp, string& section, string&name, int i)
+{
+ bufferlist bl;
+ encode(i, bl);
+
+ cls_log_add(*op, timestamp, section, name, bl);
+}
+
+
+string get_name(int i)
+{
+ string name_prefix = "data-source";
+
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", i);
+ return name_prefix + buf;
+}
+
+void generate_log(librados::IoCtx& ioctx, string& oid, int max, utime_t& start_time, bool modify_time)
+{
+ string section = "global";
+
+ librados::ObjectWriteOperation op;
+
+ int i;
+
+ for (i = 0; i < max; i++) {
+ uint32_t secs = start_time.sec();
+ if (modify_time)
+ secs += i;
+
+ utime_t ts(secs, start_time.nsec());
+ string name = get_name(i);
+
+ add_log(&op, ts, section, name, i);
+ }
+
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+}
+
+utime_t get_time(utime_t& start_time, int i, bool modify_time)
+{
+ uint32_t secs = start_time.sec();
+ if (modify_time)
+ secs += i;
+ return utime_t(secs, start_time.nsec());
+}
+
+void check_entry(cls_log_entry& entry, utime_t& start_time, int i, bool modified_time)
+{
+ string section = "global";
+ string name = get_name(i);
+ utime_t ts = get_time(start_time, i, modified_time);
+
+ ASSERT_EQ(section, entry.section);
+ ASSERT_EQ(name, entry.name);
+ ASSERT_EQ(ts, entry.timestamp);
+}
+
+static int log_list(librados::IoCtx& ioctx, const std::string& oid,
+ utime_t& from, utime_t& to,
+ const string& in_marker, int max_entries,
+ list<cls_log_entry>& entries,
+ string *out_marker, bool *truncated)
+{
+ librados::ObjectReadOperation rop;
+ cls_log_list(rop, from, to, in_marker, max_entries,
+ entries, out_marker, truncated);
+ bufferlist obl;
+ return ioctx.operate(oid, &rop, &obl);
+}
+
+static int log_list(librados::IoCtx& ioctx, const std::string& oid,
+ utime_t& from, utime_t& to, int max_entries,
+ list<cls_log_entry>& entries, bool *truncated)
+{
+ std::string marker;
+ return log_list(ioctx, oid, from, to, marker, max_entries,
+ entries, &marker, truncated);
+}
+
+static int log_list(librados::IoCtx& ioctx, const std::string& oid,
+ list<cls_log_entry>& entries)
+{
+ utime_t from, to;
+ bool truncated{false};
+ return log_list(ioctx, oid, from, to, 0, entries, &truncated);
+}
+
+TEST_F(cls_log, test_log_add_same_time)
+{
+ /* add chains */
+ string oid = "obj";
+
+ /* create object */
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* generate log */
+ utime_t start_time = ceph_clock_now();
+ utime_t to_time = get_time(start_time, 1, true);
+ generate_log(ioctx, oid, 10, start_time, false);
+
+ list<cls_log_entry> entries;
+ bool truncated;
+
+ /* check list */
+ {
+ ASSERT_EQ(0, log_list(ioctx, oid, start_time, to_time, 0,
+ entries, &truncated));
+ ASSERT_EQ(10, (int)entries.size());
+ ASSERT_EQ(0, (int)truncated);
+ }
+ list<cls_log_entry>::iterator iter;
+
+ /* need to sort returned entries, all were using the same time as key */
+ map<int, cls_log_entry> check_ents;
+
+ for (iter = entries.begin(); iter != entries.end(); ++iter) {
+ cls_log_entry& entry = *iter;
+
+ int num;
+ ASSERT_EQ(0, read_bl(entry.data, &num));
+
+ check_ents[num] = entry;
+ }
+
+ ASSERT_EQ(10, (int)check_ents.size());
+
+ map<int, cls_log_entry>::iterator ei;
+
+ /* verify entries are as expected */
+
+ int i;
+
+ for (i = 0, ei = check_ents.begin(); i < 10; i++, ++ei) {
+ cls_log_entry& entry = ei->second;
+
+ ASSERT_EQ(i, ei->first);
+ check_entry(entry, start_time, i, false);
+ }
+
+ /* check list again, now want to be truncated*/
+ {
+ ASSERT_EQ(0, log_list(ioctx, oid, start_time, to_time, 1,
+ entries, &truncated));
+ ASSERT_EQ(1, (int)entries.size());
+ ASSERT_EQ(1, (int)truncated);
+ }
+}
+
+TEST_F(cls_log, test_log_add_different_time)
+{
+ /* add chains */
+ string oid = "obj";
+
+ /* create object */
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* generate log */
+ utime_t start_time = ceph_clock_now();
+ generate_log(ioctx, oid, 10, start_time, true);
+
+ list<cls_log_entry> entries;
+ bool truncated;
+
+ utime_t to_time = utime_t(start_time.sec() + 10, start_time.nsec());
+
+ {
+ /* check list */
+ ASSERT_EQ(0, log_list(ioctx, oid, start_time, to_time, 0,
+ entries, &truncated));
+ ASSERT_EQ(10, (int)entries.size());
+ ASSERT_EQ(0, (int)truncated);
+ }
+
+ list<cls_log_entry>::iterator iter;
+
+ /* returned entries should be sorted by time */
+ map<int, cls_log_entry> check_ents;
+
+ int i;
+
+ for (i = 0, iter = entries.begin(); iter != entries.end(); ++iter, ++i) {
+ cls_log_entry& entry = *iter;
+
+ int num;
+
+ ASSERT_EQ(0, read_bl(entry.data, &num));
+
+ ASSERT_EQ(i, num);
+
+ check_entry(entry, start_time, i, true);
+ }
+
+ /* check list again with shifted time */
+ {
+ utime_t next_time = get_time(start_time, 1, true);
+ ASSERT_EQ(0, log_list(ioctx, oid, next_time, to_time, 0,
+ entries, &truncated));
+ ASSERT_EQ(9u, entries.size());
+ ASSERT_FALSE(truncated);
+ }
+
+ string marker;
+ i = 0;
+ do {
+ string old_marker = std::move(marker);
+ ASSERT_EQ(0, log_list(ioctx, oid, start_time, to_time, old_marker, 1,
+ entries, &marker, &truncated));
+ ASSERT_NE(old_marker, marker);
+ ASSERT_EQ(1, (int)entries.size());
+
+ ++i;
+ ASSERT_GE(10, i);
+ } while (truncated);
+
+ ASSERT_EQ(10, i);
+}
+
+int do_log_trim(librados::IoCtx& ioctx, const std::string& oid,
+ const std::string& from_marker, const std::string& to_marker)
+{
+ librados::ObjectWriteOperation op;
+ cls_log_trim(op, {}, {}, from_marker, to_marker);
+ return ioctx.operate(oid, &op);
+}
+
+int do_log_trim(librados::IoCtx& ioctx, const std::string& oid,
+ const utime_t& from_time, const utime_t& to_time)
+{
+ librados::ObjectWriteOperation op;
+ cls_log_trim(op, from_time, to_time, "", "");
+ return ioctx.operate(oid, &op);
+}
+
+TEST_F(cls_log, trim_by_time)
+{
+ /* add chains */
+ string oid = "obj";
+
+ /* create object */
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* generate log */
+ utime_t start_time = ceph_clock_now();
+ generate_log(ioctx, oid, 10, start_time, true);
+
+ list<cls_log_entry> entries;
+ bool truncated;
+
+ /* check list */
+
+ /* trim */
+ utime_t to_time = get_time(start_time, 10, true);
+
+ for (int i = 0; i < 10; i++) {
+ utime_t trim_time = get_time(start_time, i, true);
+
+ utime_t zero_time;
+
+ ASSERT_EQ(0, do_log_trim(ioctx, oid, zero_time, trim_time));
+ ASSERT_EQ(-ENODATA, do_log_trim(ioctx, oid, zero_time, trim_time));
+
+ ASSERT_EQ(0, log_list(ioctx, oid, start_time, to_time, 0,
+ entries, &truncated));
+ ASSERT_EQ(9u - i, entries.size());
+ ASSERT_FALSE(truncated);
+ }
+}
+
+TEST_F(cls_log, trim_by_marker)
+{
+ string oid = "obj";
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ utime_t start_time = ceph_clock_now();
+ generate_log(ioctx, oid, 10, start_time, true);
+
+ utime_t zero_time;
+ std::vector<cls_log_entry> log1;
+ {
+ list<cls_log_entry> entries;
+ ASSERT_EQ(0, log_list(ioctx, oid, entries));
+ ASSERT_EQ(10u, entries.size());
+
+ log1.assign(std::make_move_iterator(entries.begin()),
+ std::make_move_iterator(entries.end()));
+ }
+ // trim front of log
+ {
+ const std::string from = "";
+ const std::string to = log1[0].id;
+ ASSERT_EQ(0, do_log_trim(ioctx, oid, from, to));
+ list<cls_log_entry> entries;
+ ASSERT_EQ(0, log_list(ioctx, oid, entries));
+ ASSERT_EQ(9u, entries.size());
+ EXPECT_EQ(log1[1].id, entries.begin()->id);
+ ASSERT_EQ(-ENODATA, do_log_trim(ioctx, oid, from, to));
+ }
+ // trim back of log
+ {
+ const std::string from = log1[8].id;
+ const std::string to = "9";
+ ASSERT_EQ(0, do_log_trim(ioctx, oid, from, to));
+ list<cls_log_entry> entries;
+ ASSERT_EQ(0, log_list(ioctx, oid, entries));
+ ASSERT_EQ(8u, entries.size());
+ EXPECT_EQ(log1[8].id, entries.rbegin()->id);
+ ASSERT_EQ(-ENODATA, do_log_trim(ioctx, oid, from, to));
+ }
+ // trim a key from the middle
+ {
+ const std::string from = log1[3].id;
+ const std::string to = log1[4].id;
+ ASSERT_EQ(0, do_log_trim(ioctx, oid, from, to));
+ list<cls_log_entry> entries;
+ ASSERT_EQ(0, log_list(ioctx, oid, entries));
+ ASSERT_EQ(7u, entries.size());
+ ASSERT_EQ(-ENODATA, do_log_trim(ioctx, oid, from, to));
+ }
+ // trim full log
+ {
+ const std::string from = "";
+ const std::string to = "9";
+ ASSERT_EQ(0, do_log_trim(ioctx, oid, from, to));
+ list<cls_log_entry> entries;
+ ASSERT_EQ(0, log_list(ioctx, oid, entries));
+ ASSERT_EQ(0u, entries.size());
+ ASSERT_EQ(-ENODATA, do_log_trim(ioctx, oid, from, to));
+ }
+}
diff --git a/src/test/cls_lua/CMakeLists.txt b/src/test/cls_lua/CMakeLists.txt
new file mode 100644
index 000000000..510bb58a6
--- /dev/null
+++ b/src/test/cls_lua/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_executable(ceph_test_cls_lua
+ test_cls_lua.cc
+)
+target_link_libraries(ceph_test_cls_lua
+ cls_lua_client
+ ${LUA_LIBRARIES}
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx)
+target_include_directories(ceph_test_cls_lua PRIVATE "${LUA_INCLUDE_DIR}")
+install(TARGETS
+ ceph_test_cls_lua
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_lua/test_cls_lua.cc b/src/test/cls_lua/test_cls_lua.cc
new file mode 100644
index 000000000..a7d09aba4
--- /dev/null
+++ b/src/test/cls_lua/test_cls_lua.cc
@@ -0,0 +1,1108 @@
+#include <errno.h>
+#include <lua.hpp>
+#include "include/types.h"
+#include "include/rados/librados.hpp"
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+#include "cls/lua/cls_lua_client.h"
+#include "cls/lua/cls_lua.h"
+
+using namespace std;
+
+/*
+ * JSON script to test JSON I/O protocol with cls_lua
+ */
+const std::string json_test_script = R"jsonscript(
+{
+ "script": "function json_echo(input, output) output:append(input:str()); end objclass.register(json_echo)",
+ "handler": "json_echo",
+ "input": "omg it works",
+}
+)jsonscript";
+
+/*
+ * Lua test script thanks to the magical c++11 string literal syntax
+ */
+const std::string test_script = R"luascript(
+--
+-- This Lua script file contains all of the handlers used in the cls_lua unit
+-- tests (src/test/cls_lua/test_cls_lua.cc). Each section header corresponds
+-- to the ClsLua.XYZ test.
+--
+
+--
+-- Read
+--
+function read(input, output)
+ size = objclass.stat()
+ bl = objclass.read(0, size)
+ output:append(bl:str())
+end
+
+objclass.register(read)
+
+--
+-- Write
+--
+function write(input, output)
+ objclass.write(0, #input, input)
+end
+
+objclass.register(write)
+
+--
+-- MapGetVal
+--
+function map_get_val_foo(input, output)
+ bl = objclass.map_get_val('foo')
+ output:append(bl:str())
+end
+
+function map_get_val_dne()
+ bl = objclass.map_get_val('dne')
+end
+
+objclass.register(map_get_val_foo)
+objclass.register(map_get_val_dne)
+
+--
+-- Stat
+--
+function stat_ret(input, output)
+ size, mtime = objclass.stat()
+ output:append(size .. "," .. mtime)
+end
+
+function stat_sdne()
+ size, mtime = objclass.stat()
+end
+
+function stat_sdne_pcall()
+ ok, ret, size, mtime = pcall(objclass.stat, o)
+ assert(ok == false)
+ assert(ret == -objclass.ENOENT)
+ return ret
+end
+
+objclass.register(stat_ret)
+objclass.register(stat_sdne)
+objclass.register(stat_sdne_pcall)
+
+--
+-- RetVal
+--
+function rv_h() end
+function rv_h1() return 1; end
+function rv_h0() return 0; end
+function rv_hn1() return -1; end
+function rv_hs1() return '1'; end
+function rv_hs0() return '0'; end
+function rv_hsn1() return '-1'; end
+function rv_hnil() return nil; end
+function rv_ht() return {}; end
+function rv_hstr() return 'asdf'; end
+
+objclass.register(rv_h)
+objclass.register(rv_h1)
+objclass.register(rv_h0)
+objclass.register(rv_hn1)
+objclass.register(rv_hs1)
+objclass.register(rv_hs0)
+objclass.register(rv_hsn1)
+objclass.register(rv_hnil)
+objclass.register(rv_ht)
+objclass.register(rv_hstr)
+
+--
+-- Create
+--
+function create_c() objclass.create(true); end
+function create_cne() objclass.create(false); end
+
+objclass.register(create_c)
+objclass.register(create_cne)
+
+--
+-- Pcall
+--
+function pcall_c() objclass.create(true); end
+
+function pcall_pc()
+ ok, ret = pcall(objclass.create, true)
+ assert(ok == false)
+ assert(ret == -objclass.EEXIST)
+end
+
+function pcall_pcr()
+ ok, ret = pcall(objclass.create, true)
+ assert(ok == false)
+ assert(ret == -objclass.EEXIST)
+ return ret
+end
+
+function pcall_pcr2()
+ ok, ret = pcall(objclass.create, true)
+ assert(ok == false)
+ assert(ret == -objclass.EEXIST)
+ ok, ret = pcall(objclass.create, true)
+ assert(ok == false)
+ assert(ret == -objclass.EEXIST)
+ return -9999
+end
+
+objclass.register(pcall_c)
+objclass.register(pcall_pc)
+objclass.register(pcall_pcr)
+objclass.register(pcall_pcr2)
+
+--
+-- Remove
+--
+function remove_c() objclass.create(true); end
+function remove_r() objclass.remove(); end
+
+objclass.register(remove_c)
+objclass.register(remove_r)
+
+--
+-- MapSetVal
+--
+function map_set_val(input, output)
+ objclass.map_set_val('foo', input)
+end
+
+objclass.register(map_set_val)
+
+--
+-- MapClear
+--
+function map_clear()
+ objclass.map_clear()
+end
+
+objclass.register(map_clear)
+
+--
+-- BufferlistEquality
+--
+function bl_eq_empty_equal(input, output)
+ bl1 = bufferlist.new()
+ bl2 = bufferlist.new()
+ assert(bl1 == bl2)
+end
+
+function bl_eq_empty_selfequal()
+ bl1 = bufferlist.new()
+ assert(bl1 == bl1)
+end
+
+function bl_eq_selfequal()
+ bl1 = bufferlist.new()
+ bl1:append('asdf')
+ assert(bl1 == bl1)
+end
+
+function bl_eq_equal()
+ bl1 = bufferlist.new()
+ bl2 = bufferlist.new()
+ bl1:append('abc')
+ bl2:append('abc')
+ assert(bl1 == bl2)
+end
+
+function bl_eq_notequal()
+ bl1 = bufferlist.new()
+ bl2 = bufferlist.new()
+ bl1:append('abc')
+ bl2:append('abcd')
+ assert(bl1 ~= bl2)
+end
+
+objclass.register(bl_eq_empty_equal)
+objclass.register(bl_eq_empty_selfequal)
+objclass.register(bl_eq_selfequal)
+objclass.register(bl_eq_equal)
+objclass.register(bl_eq_notequal)
+
+--
+-- Bufferlist Compare
+--
+function bl_lt()
+ local a = bufferlist.new()
+ local b = bufferlist.new()
+ a:append('A')
+ b:append('B')
+ assert(a < b)
+end
+
+function bl_le()
+ local a = bufferlist.new()
+ local b = bufferlist.new()
+ a:append('A')
+ b:append('B')
+ assert(a <= b)
+end
+
+objclass.register(bl_lt)
+objclass.register(bl_le)
+
+--
+-- Bufferlist concat
+--
+function bl_concat_eq()
+ local a = bufferlist.new()
+ local b = bufferlist.new()
+ local ab = bufferlist.new()
+ a:append('A')
+ b:append('B')
+ ab:append('AB')
+ assert(a .. b == ab)
+end
+
+function bl_concat_ne()
+ local a = bufferlist.new()
+ local b = bufferlist.new()
+ local ab = bufferlist.new()
+ a:append('A')
+ b:append('B')
+ ab:append('AB')
+ assert(b .. a ~= ab)
+end
+
+function bl_concat_immut()
+ local a = bufferlist.new()
+ local b = bufferlist.new()
+ local ab = bufferlist.new()
+ a:append('A')
+ b:append('B')
+ ab:append('AB')
+ x = a .. b
+ assert(x == ab)
+ b:append('C')
+ assert(x == ab)
+ local bc = bufferlist.new()
+ bc:append('BC')
+ assert(b == bc)
+end
+
+objclass.register(bl_concat_eq)
+objclass.register(bl_concat_ne)
+objclass.register(bl_concat_immut)
+
+--
+-- RunError
+--
+function runerr_a()
+ error('error_a')
+end
+
+function runerr_b()
+ runerr_a()
+end
+
+function runerr_c()
+ runerr_b()
+end
+
+-- only runerr_c is called
+objclass.register(runerr_c)
+
+--
+-- GetXattr
+--
+function getxattr(input, output)
+ bl = objclass.getxattr("fooz")
+ output:append(bl:str())
+end
+
+objclass.register(getxattr)
+
+--
+-- SetXattr
+--
+function setxattr(input, output)
+ objclass.setxattr("fooz2", input)
+end
+
+objclass.register(setxattr)
+
+--
+-- WriteFull
+--
+function write_full(input, output)
+ objclass.write_full(input)
+end
+
+objclass.register(write_full)
+
+--
+-- GetXattrs
+--
+function getxattrs(input, output)
+ -- result
+ xattrs = objclass.getxattrs()
+
+ -- sort for determisitic test
+ arr = {}
+ for n in pairs(xattrs) do
+ table.insert(arr, n)
+ end
+ table.sort(arr)
+
+ output_str = ""
+ for i,key in ipairs(arr) do
+ output_str = output_str .. key .. "/" .. xattrs[key]:str() .. "/"
+ end
+ output:append(output_str)
+end
+
+objclass.register(getxattrs)
+
+--
+-- MapGetKeys
+--
+function map_get_keys(input, output)
+ -- result
+ keys = objclass.map_get_keys("", 5)
+
+ -- sort for determisitic test
+ arr = {}
+ for n in pairs(keys) do
+ table.insert(arr, n)
+ end
+ table.sort(arr)
+
+ output_str = ""
+ for i,key in ipairs(arr) do
+ output_str = output_str .. key .. "/"
+ end
+
+ output:append(output_str)
+end
+
+objclass.register(map_get_keys)
+
+--
+-- MapGetVals
+--
+function map_get_vals(input, output)
+ -- result
+ kvs = objclass.map_get_vals("", "", 10)
+
+ -- sort for determisitic test
+ arr = {}
+ for n in pairs(kvs) do
+ table.insert(arr, n)
+ end
+ table.sort(arr)
+
+ output_str = ""
+ for i,key in ipairs(arr) do
+ output_str = output_str .. key .. "/" .. kvs[key]:str() .. "/"
+ end
+ output:append(output_str)
+end
+
+objclass.register(map_get_vals)
+
+--
+-- MapHeader (write)
+--
+function map_write_header(input, output)
+ objclass.map_write_header(input)
+end
+
+objclass.register(map_write_header)
+
+--
+-- MapHeader (read)
+--
+function map_read_header(input, output)
+ hdr = objclass.map_read_header()
+ output:append(hdr:str())
+end
+
+objclass.register(map_read_header)
+
+--
+-- MapSetVals
+--
+function map_set_vals_empty(input, output)
+ record = {}
+ objclass.map_set_vals(record)
+end
+
+function map_set_vals_one(input, output)
+ record = {
+ a = "a_val"
+ }
+ objclass.map_set_vals(record)
+end
+
+function map_set_vals_two(input, output)
+ record = {
+ a = "a_val",
+ b = "b_val"
+ }
+ objclass.map_set_vals(record)
+end
+
+function map_set_vals_three(input, output)
+ record = {
+ a = "a_val",
+ b = "b_val",
+ c = "c_val"
+ }
+ objclass.map_set_vals(record)
+end
+
+function map_set_vals_array(input, output)
+ array = {}
+ array[1] = "1_val"
+ array[2] = "2_val"
+ objclass.map_set_vals(array)
+end
+
+function map_set_vals_mixed(input, output)
+ record = {
+ a = "a_val",
+ b = "b_val"
+ }
+ record[1] = "1_val"
+ record[2] = "2_val"
+ objclass.map_set_vals(record)
+end
+
+function map_set_vals_bad_val(input, output)
+ record = {
+ a = {}
+ }
+ objclass.map_set_vals(record)
+end
+
+objclass.register(map_set_vals_empty)
+objclass.register(map_set_vals_one)
+objclass.register(map_set_vals_two)
+objclass.register(map_set_vals_three)
+objclass.register(map_set_vals_array)
+objclass.register(map_set_vals_mixed)
+objclass.register(map_set_vals_bad_val)
+
+--
+-- MapRemoveKey
+--
+function map_remove_key(input, output)
+ objclass.map_remove_key("a")
+end
+
+objclass.register(map_remove_key)
+
+--
+-- Version/Subop
+--
+function current_version(input, output)
+ ret = objclass.current_version()
+ output:append("" .. ret)
+ objclass.log(0, ret)
+end
+
+function current_subop_num(input, output)
+ ret = objclass.current_subop_num()
+ output:append("" .. ret)
+ objclass.log(0, ret)
+end
+
+function current_subop_version(input, output)
+ ret = objclass.current_subop_version()
+ output:append("" .. ret)
+ objclass.log(0, ret)
+end
+
+objclass.register(current_version)
+objclass.register(current_subop_num)
+objclass.register(current_subop_version)
+
+)luascript";
+
+/*
+ * Test harness uses single pool for the entire test case, and generates
+ * unique object names for each test.
+ */
+class ClsLua : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+
+ static void TearDownTestCase() {
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+ }
+
+ void SetUp() override {
+ /* Grab test names to build unique objects */
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ /* Create unique string using test/testname/pid */
+ std::stringstream ss_oid;
+ ss_oid << test_info->test_case_name() << "_" <<
+ test_info->name() << "_" << getpid();
+
+ /* Unique object for test to use */
+ oid = ss_oid.str();
+ }
+
+ void TearDown() override {
+ }
+
+ /*
+ * Helper function. This functionality should eventually make its way into
+ * a clslua client library of some sort.
+ */
+ int __clslua_exec(const string& oid, const string& script,
+ librados::bufferlist *input = NULL, const string& funcname = "")
+ {
+ bufferlist inbl;
+ if (input)
+ inbl = *input;
+
+ reply_output.clear();
+
+ return cls_lua_client::exec(ioctx, oid, script, funcname, inbl,
+ reply_output);
+ }
+
+ int clslua_exec(const string& script, librados::bufferlist *input = NULL,
+ const string& funcname = "")
+ {
+ return __clslua_exec(oid, script, input, funcname);
+ }
+
+ static librados::Rados rados;
+ static librados::IoCtx ioctx;
+ static string pool_name;
+
+ string oid;
+ bufferlist reply_output;
+};
+
+librados::Rados ClsLua::rados;
+librados::IoCtx ClsLua::ioctx;
+string ClsLua::pool_name;
+
+TEST_F(ClsLua, Write) {
+ /* write some data into object */
+ string written = "Hello World";
+ bufferlist inbl;
+ encode(written, inbl);
+ ASSERT_EQ(0, clslua_exec(test_script, &inbl, "write"));
+
+ /* have Lua read out of the object */
+ uint64_t size;
+ bufferlist outbl;
+ ASSERT_EQ(0, ioctx.stat(oid, &size, NULL));
+ ASSERT_EQ(size, (uint64_t)ioctx.read(oid, outbl, size, 0) );
+
+ /* compare what Lua read to what we wrote */
+ string read;
+ decode(read, outbl);
+ ASSERT_EQ(read, written);
+}
+
+TEST_F(ClsLua, SyntaxError) {
+ ASSERT_EQ(-EIO, clslua_exec("-"));
+}
+
+TEST_F(ClsLua, EmptyScript) {
+ ASSERT_EQ(0, clslua_exec(""));
+}
+
+TEST_F(ClsLua, RetVal) {
+ /* handlers can return numeric values */
+ ASSERT_EQ(1, clslua_exec(test_script, NULL, "rv_h1"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "rv_h0"));
+ ASSERT_EQ(-1, clslua_exec(test_script, NULL, "rv_hn1"));
+ ASSERT_EQ(1, clslua_exec(test_script, NULL, "rv_hs1"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "rv_hs0"));
+ ASSERT_EQ(-1, clslua_exec(test_script, NULL, "rv_hsn1"));
+
+ /* no return value is success */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "rv_h"));
+
+ /* non-numeric return values are errors */
+ ASSERT_EQ(-EIO, clslua_exec(test_script, NULL, "rv_hnil"));
+ ASSERT_EQ(-EIO, clslua_exec(test_script, NULL, "rv_ht"));
+ ASSERT_EQ(-EIO, clslua_exec(test_script, NULL, "rv_hstr"));
+}
+
+TEST_F(ClsLua, Create) {
+ /* create works */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "create_c"));
+
+ /* exclusive works */
+ ASSERT_EQ(-EEXIST, clslua_exec(test_script, NULL, "create_c"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "create_cne"));
+}
+
+TEST_F(ClsLua, Pcall) {
+ /* create and error works */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "pcall_c"));
+ ASSERT_EQ(-EEXIST, clslua_exec(test_script, NULL, "pcall_c"));
+
+ /* pcall masks the error */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "pcall_pc"));
+
+ /* pcall lets us get the failed return value */
+ ASSERT_EQ(-EEXIST, clslua_exec(test_script, NULL, "pcall_pcr"));
+
+ /*
+ * the first call in pcr2 will fail (check ret != 0), and the second pcall
+ * should also fail (we check with a bogus return value to mask real
+ * errors). This is also an important check for our error handling because
+ * we need a case where two functions in the same handler fail to exercise
+ * our internal error book keeping.
+ */
+ ASSERT_EQ(-9999, clslua_exec(test_script, NULL, "pcall_pcr2"));
+}
+
+TEST_F(ClsLua, Remove) {
+ /* object doesn't exist */
+ ASSERT_EQ(-ENOENT, clslua_exec(test_script, NULL, "remove_r"));
+
+ /* can remove */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "remove_c"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "remove_r"));
+ ASSERT_EQ(-ENOENT, clslua_exec(test_script, NULL, "remove_r"));
+}
+
+TEST_F(ClsLua, Stat) {
+ /* build object and stat */
+ char buf[1024] = {};
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write_full(oid, bl));
+ uint64_t size;
+ time_t mtime;
+ ASSERT_EQ(0, ioctx.stat(oid, &size, &mtime));
+
+ /* test stat success */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "stat_ret"));
+
+ // size,mtime from ioctx call
+ std::stringstream s1;
+ s1 << size << "," << mtime;
+
+ // lua constructed size,mtime string
+ std::string s2(reply_output.c_str(), reply_output.length());
+
+ ASSERT_EQ(s1.str(), s2);
+
+ /* test object dne */
+ ASSERT_EQ(-ENOENT, __clslua_exec("dne", test_script, NULL, "stat_sdne"));
+
+ /* can capture error with pcall */
+ ASSERT_EQ(-ENOENT, __clslua_exec("dne", test_script, NULL, "stat_sdne_pcall"));
+}
+
+TEST_F(ClsLua, MapClear) {
+ /* write some data into a key */
+ string msg = "This is a test message";
+ bufferlist val;
+ val.append(msg.c_str(), msg.size());
+ map<string, bufferlist> map;
+ map["foo"] = val;
+ ASSERT_EQ(0, ioctx.omap_set(oid, map));
+
+ /* test we can get it back out */
+ set<string> keys;
+ keys.insert("foo");
+ map.clear();
+ ASSERT_EQ(0, (int)map.count("foo"));
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys(oid, keys, &map));
+ ASSERT_EQ(1, (int)map.count("foo"));
+
+ /* now clear it */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_clear"));
+
+ /* test that the map we get back is empty now */
+ map.clear();
+ ASSERT_EQ(0, (int)map.count("foo"));
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys(oid, keys, &map));
+ ASSERT_EQ(0, (int)map.count("foo"));
+}
+
+TEST_F(ClsLua, MapSetVal) {
+ /* build some input value */
+ bufferlist orig_val;
+ encode("this is the original value yay", orig_val);
+
+ /* have the lua script stuff the data into a map value */
+ ASSERT_EQ(0, clslua_exec(test_script, &orig_val, "map_set_val"));
+
+ /* grap the key now and compare to orig */
+ map<string, bufferlist> out_map;
+ set<string> out_keys;
+ out_keys.insert("foo");
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys(oid, out_keys, &out_map));
+ bufferlist out_bl = out_map["foo"];
+ string out_val;
+ decode(out_val, out_bl);
+ ASSERT_EQ(out_val, "this is the original value yay");
+}
+
+TEST_F(ClsLua, MapGetVal) {
+ /* write some data into a key */
+ string msg = "This is a test message";
+ bufferlist orig_val;
+ orig_val.append(msg.c_str(), msg.size());
+ map<string, bufferlist> orig_map;
+ orig_map["foo"] = orig_val;
+ ASSERT_EQ(0, ioctx.omap_set(oid, orig_map));
+
+ /* now compare to what we put it */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_val_foo"));
+
+ /* check return */
+ string ret_val;
+ ret_val.assign(reply_output.c_str(), reply_output.length());
+ ASSERT_EQ(ret_val, msg);
+
+ /* error case */
+ ASSERT_EQ(-ENOENT, clslua_exec(test_script, NULL, "map_get_val_dne"));
+}
+
+TEST_F(ClsLua, Read) {
+ /* put data into object */
+ string msg = "This is a test message";
+ bufferlist bl;
+ bl.append(msg.c_str(), msg.size());
+ ASSERT_EQ(0, ioctx.write_full(oid, bl));
+
+ /* get lua to read it and send it back */
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "read"));
+
+ /* check return */
+ string ret_val;
+ ret_val.assign(reply_output.c_str(), reply_output.length());
+ ASSERT_EQ(ret_val, msg);
+}
+
+TEST_F(ClsLua, Log) {
+ ASSERT_EQ(0, clslua_exec("objclass.log()"));
+ ASSERT_EQ(0, clslua_exec("s = objclass.log(); objclass.log(s);"));
+ ASSERT_EQ(0, clslua_exec("objclass.log(1)"));
+ ASSERT_EQ(0, clslua_exec("objclass.log(-1)"));
+ ASSERT_EQ(0, clslua_exec("objclass.log('x')"));
+ ASSERT_EQ(0, clslua_exec("objclass.log(0, 0)"));
+ ASSERT_EQ(0, clslua_exec("objclass.log(1, 1)"));
+ ASSERT_EQ(0, clslua_exec("objclass.log(-10, -10)"));
+ ASSERT_EQ(0, clslua_exec("objclass.log('x', 'y')"));
+ ASSERT_EQ(0, clslua_exec("objclass.log(1, 'one')"));
+ ASSERT_EQ(0, clslua_exec("objclass.log(1, 'one', 'two')"));
+ ASSERT_EQ(0, clslua_exec("objclass.log('one', 'two', 'three')"));
+ ASSERT_EQ(0, clslua_exec("s = objclass.log('one', 'two', 'three'); objclass.log(s);"));
+}
+
+TEST_F(ClsLua, BufferlistEquality) {
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_eq_empty_equal"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_eq_empty_selfequal"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_eq_selfequal"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_eq_equal"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_eq_notequal"));
+}
+
+TEST_F(ClsLua, RunError) {
+ ASSERT_EQ(-EIO, clslua_exec(test_script, NULL, "runerr_c"));
+}
+
+TEST_F(ClsLua, HandleNotFunc) {
+ string script = "x = 1;";
+ ASSERT_EQ(-EOPNOTSUPP, clslua_exec(script, NULL, "x"));
+}
+
+TEST_F(ClsLua, Register) {
+ /* normal cases: register and maybe call the handler */
+ string script = "function h() end; objclass.register(h);";
+ ASSERT_EQ(0, clslua_exec(script, NULL, ""));
+ ASSERT_EQ(0, clslua_exec(script, NULL, "h"));
+
+ /* can register and call multiple handlers */
+ script = "function h1() end; function h2() end;"
+ "objclass.register(h1); objclass.register(h2);";
+ ASSERT_EQ(0, clslua_exec(script, NULL, ""));
+ ASSERT_EQ(0, clslua_exec(script, NULL, "h1"));
+ ASSERT_EQ(0, clslua_exec(script, NULL, "h2"));
+
+ /* normal cases: register before function is defined */
+ script = "objclass.register(h); function h() end;";
+ ASSERT_EQ(-EIO, clslua_exec(script, NULL, ""));
+ ASSERT_EQ(-EIO, clslua_exec(script, NULL, "h"));
+
+ /* cannot call handler that isn't registered */
+ script = "function h() end;";
+ ASSERT_EQ(-EIO, clslua_exec(script, NULL, "h"));
+
+ /* handler doesn't exist */
+ script = "objclass.register(lalala);";
+ ASSERT_EQ(-EIO, clslua_exec(script, NULL, ""));
+
+ /* handler isn't a function */
+ script = "objclass.register('some string');";
+ ASSERT_EQ(-EIO, clslua_exec(script, NULL, ""));
+
+ /* cannot register handler multiple times */
+ script = "function h() end; objclass.register(h); objclass.register(h);";
+ ASSERT_EQ(-EIO, clslua_exec(script, NULL, ""));
+}
+
+TEST_F(ClsLua, BufferlistCompare) {
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_lt"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_le"));
+}
+
+TEST_F(ClsLua, BufferlistConcat) {
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_concat_eq"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_concat_ne"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "bl_concat_immut"));
+}
+
+TEST_F(ClsLua, GetXattr) {
+ bufferlist bl;
+ bl.append("blahblahblahblahblah");
+ ASSERT_EQ(0, ioctx.setxattr(oid, "fooz", bl));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "getxattr"));
+ ASSERT_TRUE(reply_output == bl);
+}
+
+TEST_F(ClsLua, SetXattr) {
+ bufferlist inbl;
+ inbl.append("blahblahblahblahblah");
+ ASSERT_EQ(0, clslua_exec(test_script, &inbl, "setxattr"));
+ bufferlist outbl;
+ ASSERT_EQ((int)inbl.length(), ioctx.getxattr(oid, "fooz2", outbl));
+ ASSERT_TRUE(outbl == inbl);
+}
+
+TEST_F(ClsLua, WriteFull) {
+ // write some data
+ char buf[1024] = {};
+ bufferlist blin;
+ blin.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write(oid, blin, blin.length(), 0));
+ bufferlist blout;
+ ASSERT_EQ((int)blin.length(), ioctx.read(oid, blout, 0, 0));
+ ASSERT_EQ(blin, blout);
+
+ // execute write_full from lua
+ blin.clear();
+ char buf2[200];
+ sprintf(buf2, "%s", "data replacing content");
+ blin.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, clslua_exec(test_script, &blin, "write_full"));
+
+ // read it back
+ blout.clear();
+ ASSERT_EQ((int)blin.length(), ioctx.read(oid, blout, 0, 0));
+ ASSERT_EQ(blin, blout);
+}
+
+TEST_F(ClsLua, GetXattrs) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "getxattrs"));
+ ASSERT_EQ(0, (int)reply_output.length());
+
+ string key1str("key1str");
+ bufferlist key1bl;
+ key1bl.append(key1str);
+ ASSERT_EQ(0, ioctx.setxattr(oid, "key1", key1bl));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "getxattrs"));
+ string out1(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out1.c_str(), "key1/key1str/");
+
+ string key2str("key2str");
+ bufferlist key2bl;
+ key2bl.append(key2str);
+ ASSERT_EQ(0, ioctx.setxattr(oid, "key2", key2bl));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "getxattrs"));
+ string out2(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out2.c_str(), "key1/key1str/key2/key2str/");
+
+ string key3str("key3str");
+ bufferlist key3bl;
+ key3bl.append(key3str);
+ ASSERT_EQ(0, ioctx.setxattr(oid, "key3", key3bl));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "getxattrs"));
+ string out3(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out3.c_str(), "key1/key1str/key2/key2str/key3/key3str/");
+}
+
+TEST_F(ClsLua, MapGetKeys) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_keys"));
+ ASSERT_EQ(0, (int)reply_output.length());
+
+ map<string, bufferlist> kvpairs;
+
+ kvpairs["k1"] = bufferlist();
+ ASSERT_EQ(0, ioctx.omap_set(oid, kvpairs));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_keys"));
+ string out1(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out1.c_str(), "k1/");
+
+ kvpairs["k2"] = bufferlist();
+ ASSERT_EQ(0, ioctx.omap_set(oid, kvpairs));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_keys"));
+ string out2(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out2.c_str(), "k1/k2/");
+
+ kvpairs["xxx"] = bufferlist();
+ ASSERT_EQ(0, ioctx.omap_set(oid, kvpairs));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_keys"));
+ string out3(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out3.c_str(), "k1/k2/xxx/");
+}
+
+TEST_F(ClsLua, MapGetVals) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_vals"));
+ ASSERT_EQ(0, (int)reply_output.length());
+
+ map<string, bufferlist> kvpairs;
+
+ kvpairs["key1"] = bufferlist();
+ kvpairs["key1"].append(string("key1str"));
+ ASSERT_EQ(0, ioctx.omap_set(oid, kvpairs));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_vals"));
+ string out1(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out1.c_str(), "key1/key1str/");
+
+ kvpairs["key2"] = bufferlist();
+ kvpairs["key2"].append(string("key2str"));
+ ASSERT_EQ(0, ioctx.omap_set(oid, kvpairs));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_vals"));
+ string out2(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out2.c_str(), "key1/key1str/key2/key2str/");
+
+ kvpairs["key3"] = bufferlist();
+ kvpairs["key3"].append(string("key3str"));
+ ASSERT_EQ(0, ioctx.omap_set(oid, kvpairs));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_get_vals"));
+ string out3(reply_output.c_str(), reply_output.length()); // add the trailing \0
+ ASSERT_STREQ(out3.c_str(), "key1/key1str/key2/key2str/key3/key3str/");
+}
+
+TEST_F(ClsLua, MapHeader) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ bufferlist bl_out;
+ ASSERT_EQ(0, ioctx.omap_get_header(oid, &bl_out));
+ ASSERT_EQ(0, (int)bl_out.length());
+
+ std::string val("this is a value");
+ bufferlist hdr;
+ hdr.append(val);
+
+ ASSERT_EQ(0, clslua_exec(test_script, &hdr, "map_write_header"));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_read_header"));
+
+ ASSERT_EQ(reply_output, hdr);
+}
+
+TEST_F(ClsLua, MapSetVals) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_set_vals_empty"));
+
+ std::map<string, bufferlist> out_vals;
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_set_vals_one"));
+ ASSERT_EQ(0, ioctx.omap_get_vals(oid, "", 100, &out_vals));
+ ASSERT_EQ(1, (int)out_vals.size());
+ ASSERT_STREQ("a_val", std::string(out_vals["a"].c_str(), out_vals["a"].length()).c_str());
+
+ out_vals.clear();
+ ASSERT_EQ(0, ioctx.omap_clear(oid));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_set_vals_two"));
+ ASSERT_EQ(0, ioctx.omap_get_vals(oid, "", 100, &out_vals));
+ ASSERT_EQ(2, (int)out_vals.size());
+ ASSERT_STREQ("a_val", std::string(out_vals["a"].c_str(), out_vals["a"].length()).c_str());
+ ASSERT_STREQ("b_val", std::string(out_vals["b"].c_str(), out_vals["b"].length()).c_str());
+
+ out_vals.clear();
+ ASSERT_EQ(0, ioctx.omap_clear(oid));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_set_vals_three"));
+ ASSERT_EQ(0, ioctx.omap_get_vals(oid, "", 100, &out_vals));
+ ASSERT_EQ(3, (int)out_vals.size());
+ ASSERT_STREQ("a_val", std::string(out_vals["a"].c_str(), out_vals["a"].length()).c_str());
+ ASSERT_STREQ("b_val", std::string(out_vals["b"].c_str(), out_vals["b"].length()).c_str());
+ ASSERT_STREQ("c_val", std::string(out_vals["c"].c_str(), out_vals["c"].length()).c_str());
+
+ out_vals.clear();
+ ASSERT_EQ(0, ioctx.omap_clear(oid));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_set_vals_array"));
+ ASSERT_EQ(0, ioctx.omap_get_vals(oid, "", 100, &out_vals));
+ ASSERT_EQ(2, (int)out_vals.size());
+ ASSERT_STREQ("1_val", std::string(out_vals["1"].c_str(), out_vals["1"].length()).c_str());
+ ASSERT_STREQ("2_val", std::string(out_vals["2"].c_str(), out_vals["2"].length()).c_str());
+
+ out_vals.clear();
+ ASSERT_EQ(0, ioctx.omap_clear(oid));
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_set_vals_mixed"));
+ ASSERT_EQ(0, ioctx.omap_get_vals(oid, "", 100, &out_vals));
+ ASSERT_EQ(4, (int)out_vals.size());
+ ASSERT_STREQ("a_val", std::string(out_vals["a"].c_str(), out_vals["a"].length()).c_str());
+ ASSERT_STREQ("b_val", std::string(out_vals["b"].c_str(), out_vals["b"].length()).c_str());
+ ASSERT_STREQ("1_val", std::string(out_vals["1"].c_str(), out_vals["1"].length()).c_str());
+ ASSERT_STREQ("2_val", std::string(out_vals["2"].c_str(), out_vals["2"].length()).c_str());
+
+ ASSERT_EQ(-EINVAL, clslua_exec(test_script, NULL, "map_set_vals_bad_val"));
+}
+
+TEST_F(ClsLua, MapRemoveKey) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ std::map<string, bufferlist> out_vals;
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_set_vals_two"));
+ ASSERT_EQ(0, ioctx.omap_get_vals(oid, "", 100, &out_vals));
+ ASSERT_EQ(2, (int)out_vals.size());
+ ASSERT_STREQ("a_val", std::string(out_vals["a"].c_str(), out_vals["a"].length()).c_str());
+ ASSERT_STREQ("b_val", std::string(out_vals["b"].c_str(), out_vals["b"].length()).c_str());
+
+ out_vals.clear();
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "map_remove_key"));
+ ASSERT_EQ(0, ioctx.omap_get_vals(oid, "", 100, &out_vals));
+ ASSERT_EQ(1, (int)out_vals.size());
+ ASSERT_STREQ("b_val", std::string(out_vals["b"].c_str(), out_vals["b"].length()).c_str());
+}
+
+TEST_F(ClsLua, VersionSubop) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "current_version"));
+ ASSERT_GT((int)reply_output.length(), 0);
+
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "current_subop_num"));
+ ASSERT_GT((int)reply_output.length(), 0);
+
+ ASSERT_EQ(0, clslua_exec(test_script, NULL, "current_subop_version"));
+ ASSERT_GT((int)reply_output.length(), 0);
+}
+
+TEST_F(ClsLua, Json) {
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ bufferlist inbl, outbl;
+
+ inbl.append(json_test_script);
+
+ int ret = ioctx.exec(oid, "lua", "eval_json", inbl, outbl);
+ ASSERT_EQ(ret, 0);
+
+ std::string out(outbl.c_str(), outbl.length());
+ ASSERT_STREQ(out.c_str(), "omg it works");
+}
diff --git a/src/test/cls_numops/CMakeLists.txt b/src/test/cls_numops/CMakeLists.txt
new file mode 100644
index 000000000..827ff606f
--- /dev/null
+++ b/src/test/cls_numops/CMakeLists.txt
@@ -0,0 +1,16 @@
+# ceph_test_cls_numops
+add_executable(ceph_test_cls_numops
+ test_cls_numops.cc)
+target_link_libraries(ceph_test_cls_numops
+ librados
+ global
+ cls_numops_client
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ )
+install(TARGETS
+ ceph_test_cls_numops
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_numops/test_cls_numops.cc b/src/test/cls_numops/test_cls_numops.cc
new file mode 100644
index 000000000..ebb225620
--- /dev/null
+++ b/src/test/cls_numops/test_cls_numops.cc
@@ -0,0 +1,414 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 CERN
+ *
+ * Author: Joaquim Rocha <joaquim.rocha@cern.ch>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <iostream>
+#include <errno.h>
+#include <set>
+#include <sstream>
+#include <string>
+
+#include "cls/numops/cls_numops_client.h"
+#include "gtest/gtest.h"
+#include "include/rados/librados.hpp"
+#include "test/librados/test_cxx.h"
+
+using namespace librados;
+
+TEST(ClsNumOps, Add) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ // exec numops add method with an empty bufferlist
+
+ bufferlist in, out;
+
+ ASSERT_EQ(-EINVAL, ioctx.exec("myobject", "numops", "add", in, out));
+
+ // add a number to a non-existing key
+
+ std::string key = "my-key";
+ double value_in = 0.5;
+
+ std::stringstream stream;
+ stream << value_in;
+
+ ASSERT_EQ(0, rados::cls::numops::add(&ioctx, "myobject", key, value_in));
+
+ // check that the omap entry was set and the value matches
+
+ std::set<std::string> keys;
+ std::map<std::string, bufferlist> omap;
+ keys.insert(key);
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ std::map<std::string, bufferlist>::iterator it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bufferlist bl = (*it).second;
+ std::string value_out(bl.c_str(), bl.length());
+
+ EXPECT_EQ(stream.str(), value_out);
+
+ // add another value to the existing one
+
+ double new_value_in = 3.001;
+
+ ASSERT_EQ(0, rados::cls::numops::add(&ioctx, "myobject", key, new_value_in));
+
+ // check that the omap entry's value matches
+
+ omap.clear();
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ stream.str("");
+ stream << (value_in + new_value_in);
+
+ EXPECT_EQ(stream.str(), value_out);
+
+ // set the omap entry with some non-numeric value
+
+ omap.clear();
+
+ std::string non_numeric_value("some-non-numeric-text");
+ omap[key].append(non_numeric_value);
+
+ ASSERT_EQ(0, ioctx.omap_set("myobject", omap));
+
+ // check that adding a number does not succeed
+
+ omap.clear();
+
+ ASSERT_EQ(-EBADMSG, rados::cls::numops::add(&ioctx, "myobject", key, 2.0));
+
+ // check that the omap entry was not changed
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ EXPECT_EQ(non_numeric_value, value_out);
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsNumOps, Sub) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ // subtract a number from a non-existing key
+
+ std::string key = "my-key";
+ double value_in = 0.5;
+
+ std::stringstream stream;
+ stream << value_in;
+
+ ASSERT_EQ(0, rados::cls::numops::sub(&ioctx, "myobject", key, value_in));
+
+ // check that the omap entry was set and the value matches
+
+ std::set<std::string> keys;
+ std::map<std::string, bufferlist> omap;
+ keys.insert(key);
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ std::map<std::string, bufferlist>::iterator it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bufferlist bl = (*it).second;
+ std::string value_out(bl.c_str(), bl.length());
+
+ EXPECT_EQ("-" + stream.str(), value_out);
+
+ // subtract another value to the existing one
+
+ double new_value_in = 3.001;
+
+ ASSERT_EQ(0, rados::cls::numops::sub(&ioctx, "myobject", key, new_value_in));
+
+ // check that the omap entry's value matches
+
+ omap.clear();
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ stream.str("");
+ stream << -(value_in + new_value_in);
+
+ EXPECT_EQ(stream.str(), value_out);
+
+ // set the omap entry with some non-numeric value
+
+ omap.clear();
+
+ std::string non_numeric_value("some-non-numeric-text");
+ omap[key].append(non_numeric_value);
+
+ ASSERT_EQ(0, ioctx.omap_set("myobject", omap));
+
+ // check that subtracting a number does not succeed
+
+ omap.clear();
+
+ ASSERT_EQ(-EBADMSG, rados::cls::numops::sub(&ioctx, "myobject", key, 2.0));
+
+ // check that the omap entry was not changed
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ EXPECT_EQ(non_numeric_value, value_out);
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsNumOps, Mul) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ // exec numops mul method with an empty bufferlist
+
+ bufferlist in, out;
+
+ ASSERT_EQ(-EINVAL, ioctx.exec("myobject", "numops", "mul", in, out));
+
+ // multiply a number to a non-existing key
+
+ std::string key = "my-key";
+ double value_in = 0.5;
+
+ std::stringstream stream;
+ stream << value_in;
+
+ ASSERT_EQ(0, rados::cls::numops::mul(&ioctx, "myobject", key, value_in));
+
+ // check that the omap entry was set and the value is zero
+
+ std::set<std::string> keys;
+ std::map<std::string, bufferlist> omap;
+ keys.insert(key);
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ std::map<std::string, bufferlist>::iterator it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bufferlist bl = (*it).second;
+ std::string value_out(bl.c_str(), bl.length());
+
+ EXPECT_EQ("0", value_out);
+
+ // set a non-zero value so we can effectively test multiplications
+
+ omap.clear();
+
+ omap[key].append(stream.str());
+
+ ASSERT_EQ(0, ioctx.omap_set("myobject", omap));
+
+ // multiply another value to the existing one
+
+ double new_value_in = 3.001;
+
+ ASSERT_EQ(0, rados::cls::numops::mul(&ioctx, "myobject", key, new_value_in));
+
+ // check that the omap entry's value matches
+
+ omap.clear();
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ stream.str("");
+ stream << (value_in * new_value_in);
+
+ EXPECT_EQ(stream.str(), value_out);
+
+ // set the omap entry with some non-numeric value
+
+ omap.clear();
+
+ std::string non_numeric_value("some-non-numeric-text");
+ omap[key].append(non_numeric_value);
+
+ ASSERT_EQ(0, ioctx.omap_set("myobject", omap));
+
+ // check that adding a number does not succeed
+
+ ASSERT_EQ(-EBADMSG, rados::cls::numops::mul(&ioctx, "myobject", key, 2.0));
+
+ // check that the omap entry was not changed
+
+ omap.clear();
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ EXPECT_EQ(non_numeric_value, value_out);
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsNumOps, Div) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ // divide a non-existing key by a number
+
+ std::string key = "my-key";
+ double value_in = 0.5;
+
+ std::stringstream stream;
+ stream << value_in;
+
+ ASSERT_EQ(0, rados::cls::numops::div(&ioctx, "myobject", key, value_in));
+
+ // check that the omap entry was set and the value is zero
+
+ std::set<std::string> keys;
+ std::map<std::string, bufferlist> omap;
+ keys.insert(key);
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ std::map<std::string, bufferlist>::iterator it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bufferlist bl = (*it).second;
+ std::string value_out(bl.c_str(), bl.length());
+
+ EXPECT_EQ("0", value_out);
+
+ // check that division by zero is not allowed
+
+ ASSERT_EQ(-EINVAL, rados::cls::numops::div(&ioctx, "myobject", key, 0));
+
+ // set a non-zero value so we can effectively test divisions
+
+ omap.clear();
+
+ omap[key].append(stream.str());
+
+ ASSERT_EQ(0, ioctx.omap_set("myobject", omap));
+
+ // divide another value to the existing one
+
+ double new_value_in = 3.001;
+
+ ASSERT_EQ(0, rados::cls::numops::div(&ioctx, "myobject", key, new_value_in));
+
+ // check that the omap entry's value matches
+
+ omap.clear();
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ stream.str("");
+ stream << (value_in / new_value_in);
+
+ EXPECT_EQ(stream.str(), value_out);
+
+ omap.clear();
+
+ // set the omap entry with some non-numeric value
+
+ std::string non_numeric_value("some-non-numeric-text");
+ omap[key].append(non_numeric_value);
+
+ ASSERT_EQ(0, ioctx.omap_set("myobject", omap));
+
+ // check that adding a number does not succeed
+
+ ASSERT_EQ(-EBADMSG, rados::cls::numops::div(&ioctx, "myobject", key, 2.0));
+
+ // check that the omap entry was not changed
+
+ omap.clear();
+
+ ASSERT_EQ(0, ioctx.omap_get_vals_by_keys("myobject", keys, &omap));
+
+ it = omap.find(key);
+
+ ASSERT_NE(omap.end(), it);
+
+ bl = (*it).second;
+ value_out.assign(bl.c_str(), bl.length());
+
+ EXPECT_EQ(non_numeric_value, value_out);
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
diff --git a/src/test/cls_queue/CMakeLists.txt b/src/test/cls_queue/CMakeLists.txt
new file mode 100644
index 000000000..d600537c9
--- /dev/null
+++ b/src/test/cls_queue/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_executable(ceph_test_cls_queue
+ test_cls_queue.cc
+)
+target_link_libraries(ceph_test_cls_queue
+ cls_queue_client
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx)
+
+install(TARGETS
+ ceph_test_cls_queue
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
diff --git a/src/test/cls_queue/test_cls_queue.cc b/src/test/cls_queue/test_cls_queue.cc
new file mode 100644
index 000000000..5dbbccb82
--- /dev/null
+++ b/src/test/cls_queue/test_cls_queue.cc
@@ -0,0 +1,607 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+
+#include "cls/queue/cls_queue_types.h"
+#include "cls/queue/cls_queue_client.h"
+#include "cls/queue/cls_queue_ops.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+#include "global/global_context.h"
+
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <thread>
+#include <chrono>
+#include <atomic>
+
+using namespace std;
+
+class TestClsQueue : public ::testing::Test {
+protected:
+ librados::Rados rados;
+ std::string pool_name;
+ librados::IoCtx ioctx;
+
+ void SetUp() override {
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+
+ void TearDown() override {
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+ }
+
+ void test_enqueue(const std::string& queue_name,
+ int number_of_ops,
+ int number_of_elements,
+ int expected_rc) {
+ librados::ObjectWriteOperation op;
+ // test multiple enqueues
+ for (auto i = 0; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ return bl;
+ });
+
+ // enqueue vector
+ cls_queue_enqueue(op, 0, data);
+ ASSERT_EQ(expected_rc, ioctx.operate(queue_name, &op));
+ }
+ }
+};
+
+TEST_F(TestClsQueue, GetCapacity)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size;
+ const int ret = cls_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(queue_size, size);
+}
+
+TEST_F(TestClsQueue, Enqueue)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // test multiple enqueues
+ // 10 iterations, 100 elelemts each
+ // expect 0 (OK)
+ test_enqueue(queue_name, 10, 100, 0);
+}
+
+TEST_F(TestClsQueue, QueueFull)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // 8 iterations, 5 elelemts each
+ // expect 0 (OK)
+ test_enqueue(queue_name, 8, 5, 0);
+ // 2 iterations, 5 elelemts each
+ // expect -28 (Q FULL)
+ test_enqueue(queue_name, 2, 5, -28);
+}
+
+TEST_F(TestClsQueue, List)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ const auto number_of_ops = 10;
+ const auto number_of_elements = 100;
+
+ // test multiple enqueues
+ test_enqueue(queue_name, number_of_ops, number_of_elements, 0);
+
+ const auto max_elements = 42;
+ std::string marker;
+ bool truncated = false;
+ std::string next_marker;
+ auto total_elements = 0;
+ do {
+ std::vector<cls_queue_entry> entries;
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, next_marker);
+ ASSERT_EQ(0, ret);
+ marker = next_marker;
+ total_elements += entries.size();
+ } while (truncated);
+
+ ASSERT_EQ(total_elements, number_of_ops*number_of_elements);
+}
+
+TEST_F(TestClsQueue, Dequeue)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // test multiple enqueues
+ test_enqueue(queue_name, 10, 100, 0);
+
+ // get the end marker for 42 elements
+ const auto remove_elements = 42;
+ const std::string marker;
+ bool truncated;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, remove_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(truncated, true);
+ // remove up to end marker
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+}
+
+TEST_F(TestClsQueue, DequeueMarker)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // test multiple enqueues
+ test_enqueue(queue_name, 10, 1000, 0);
+
+ const auto remove_elements = 1024;
+ const std::string marker;
+ bool truncated;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ auto ret = cls_queue_list_entries(ioctx, queue_name, marker, remove_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(truncated, true);
+ cls_queue_marker after_deleted_marker;
+ // remove specific markers
+ for (const auto& entry : entries) {
+ cls_queue_marker marker;
+ marker.from_str(entry.marker.c_str());
+ ASSERT_EQ(marker.from_str(entry.marker.c_str()), 0);
+ if (marker.offset > 0 && marker.offset % 2 == 0) {
+ after_deleted_marker = marker;
+ cls_queue_remove_entries(op, marker.to_str());
+ }
+ }
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ entries.clear();
+ ret = cls_queue_list_entries(ioctx, queue_name, marker, remove_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ for (const auto& entry : entries) {
+ cls_queue_marker marker;
+ marker.from_str(entry.marker.c_str());
+ ASSERT_EQ(marker.from_str(entry.marker.c_str()), 0);
+ ASSERT_GE(marker.gen, after_deleted_marker.gen);
+ ASSERT_GE(marker.offset, after_deleted_marker.offset);
+ }
+}
+
+TEST_F(TestClsQueue, ListEmpty)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ const auto max_elements = 50;
+ const std::string marker;
+ bool truncated;
+ std::string next_marker;
+ std::vector<cls_queue_entry> entries;
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, next_marker);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(truncated, false);
+ ASSERT_EQ(entries.size(), 0);
+}
+
+TEST_F(TestClsQueue, DequeueEmpty)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ const auto max_elements = 50;
+ const std::string marker;
+ bool truncated;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+}
+
+TEST_F(TestClsQueue, ListAll)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // test multiple enqueues
+ test_enqueue(queue_name, 10, 100, 0);
+
+ const auto total_elements = 10*100;
+ std::string marker;
+ bool truncated;
+ std::string next_marker;
+ std::vector<cls_queue_entry> entries;
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, total_elements, entries, &truncated, next_marker);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(entries.size(), total_elements);
+ ASSERT_EQ(truncated, false);
+}
+
+TEST_F(TestClsQueue, DeleteAll)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // test multiple enqueues
+ test_enqueue(queue_name, 10, 100, 0);
+
+ const auto total_elements = 10*100;
+ const std::string marker;
+ bool truncated;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ auto ret = cls_queue_list_entries(ioctx, queue_name, marker, total_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ // list again to make sure that queue is empty
+ ret = cls_queue_list_entries(ioctx, queue_name, marker, 10, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(truncated, false);
+ ASSERT_EQ(entries.size(), 0);
+}
+
+TEST_F(TestClsQueue, EnqueueDequeue)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ bool done = false;
+ const int number_of_ops = 10;
+ const int number_of_elements = 100;
+
+ std::thread producer([this, &queue_name, &done] {
+ test_enqueue(queue_name, number_of_ops, number_of_elements, 0);
+ done = true;
+ });
+
+ auto consume_count = 0U;
+ std::thread consumer([this, &queue_name, &consume_count, &done] {
+ librados::ObjectWriteOperation op;
+ const auto max_elements = 42;
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ while (!done || truncated) {
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ consume_count += entries.size();
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+
+ producer.join();
+ consumer.join();
+ ASSERT_EQ(consume_count, number_of_ops*number_of_elements);
+}
+
+TEST_F(TestClsQueue, QueueFullDequeue)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 4096;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ bool done = false;
+ const auto number_of_ops = 100;
+ const auto number_of_elements = 50;
+
+ std::thread producer([this, &queue_name, &done] {
+ librados::ObjectWriteOperation op;
+ // test multiple enqueues
+ for (auto i = 0; i < number_of_ops; ++i) {
+ const std::string element_prefix("op-" +to_string(i) + "-element-");
+ std::vector<bufferlist> data(number_of_elements);
+ // create vector of buffer lists
+ std::generate(data.begin(), data.end(), [j = 0, &element_prefix] () mutable {
+ bufferlist bl;
+ bl.append(element_prefix + to_string(j++));
+ return bl;
+ });
+
+ // enqueue vector
+ cls_queue_enqueue(op, 0, data);
+ if (ioctx.operate(queue_name, &op) == -28) {
+ // queue is full - wait and retry
+ --i;
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ }
+ done = true;
+ });
+
+ auto consume_count = 0;
+ std::thread consumer([this, &queue_name, &consume_count, &done] {
+ librados::ObjectWriteOperation op;
+ const auto max_elements = 42;
+ std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ while (!done || truncated) {
+ auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ consume_count += entries.size();
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+
+ producer.join();
+ consumer.join();
+ ASSERT_EQ(consume_count, number_of_ops*number_of_elements);
+}
+
+TEST_F(TestClsQueue, MultiProducer)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ const int max_producer_count = 10;
+ int producer_count = max_producer_count;
+ const int number_of_ops = 10;
+ const int number_of_elements = 100;
+
+ std::vector<std::thread> producers(max_producer_count);
+ for (auto& p : producers) {
+ p = std::thread([this, &queue_name, &producer_count] {
+ test_enqueue(queue_name, number_of_ops, number_of_elements, 0);
+ --producer_count;
+ });
+ }
+
+ auto consume_count = 0U;
+ std::thread consumer([this, &queue_name, &consume_count, &producer_count] {
+ librados::ObjectWriteOperation op;
+ const auto max_elements = 42;
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ while (producer_count > 0 || truncated) {
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ consume_count += entries.size();
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+
+ for (auto& p : producers) {
+ p.join();
+ }
+ consumer.join();
+ ASSERT_EQ(consume_count, number_of_ops*number_of_elements*max_producer_count);
+}
+
+TEST_F(TestClsQueue, MultiConsumer)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ bool done = false;
+ const int number_of_ops = 10;
+ const int number_of_elements = 100;
+
+ std::thread producer([this, &queue_name, &done] {
+ test_enqueue(queue_name, number_of_ops, number_of_elements, 0);
+ done = true;
+ });
+
+ int consume_count = 0;
+ std::mutex list_and_remove_lock;
+
+ std::vector<std::thread> consumers(10);
+ for (auto& c : consumers) {
+ c = std::thread([this, &queue_name, &consume_count, &done, &list_and_remove_lock] {
+ librados::ObjectWriteOperation op;
+ const auto max_elements = 42;
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ while (!done || truncated) {
+ std::lock_guard lock(list_and_remove_lock);
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ consume_count += entries.size();
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+ }
+
+ producer.join();
+ for (auto& c : consumers) {
+ c.join();
+ }
+ ASSERT_EQ(consume_count, number_of_ops*number_of_elements);
+}
+
+TEST_F(TestClsQueue, NoLockMultiConsumer)
+{
+ const std::string queue_name = "my-queue";
+ const uint64_t queue_size = 1024*1024;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ bool done = false;
+ const int number_of_ops = 10;
+ const int number_of_elements = 100;
+
+ std::thread producer([this, &queue_name, &done] {
+ test_enqueue(queue_name, number_of_ops, number_of_elements, 0);
+ done = true;
+ });
+
+ std::vector<std::thread> consumers(5);
+ for (auto& c : consumers) {
+ c = std::thread([this, &queue_name, &done] {
+ librados::ObjectWriteOperation op;
+ const auto max_elements = 42;
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ while (!done || truncated) {
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ });
+ }
+
+ producer.join();
+ for (auto& c : consumers) {
+ c.join();
+ }
+
+ // make sure queue is empty
+ const auto max_elements = 1000;
+ const std::string marker;
+ bool truncated = false;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(entries.size(), 0);
+ ASSERT_EQ(truncated, false);
+}
+
+TEST_F(TestClsQueue, WrapAround)
+{
+ const std::string queue_name = "my-queue";
+ const auto number_of_entries = 10U;
+ const auto max_entry_size = 2000;
+ const auto min_entry_size = 1000;
+ const uint64_t queue_size = number_of_entries*max_entry_size;
+ const auto entry_overhead = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_queue_init(op, queue_name, queue_size);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ std::list<bufferlist> total_bl;
+
+ // fill up the queue
+ for (auto i = 0U; i < number_of_entries; ++i) {
+ const auto entry_size = rand()%(max_entry_size - min_entry_size + 1) + min_entry_size;
+ std::string entry_str(entry_size-entry_overhead, 0);
+ std::generate_n(entry_str.begin(), entry_str.size(), [](){return (char)(rand());});
+ bufferlist entry_bl;
+ entry_bl.append(entry_str);
+ std::vector<bufferlist> data{{entry_bl}};
+ // enqueue vector
+ cls_queue_enqueue(op, 0, data);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ total_bl.push_back(entry_bl);
+ }
+
+ std::string marker;
+ for (auto j = 0; j < 10; ++j) {
+ // empty half+1 of the queue
+ const auto max_elements = number_of_entries/2 + 1;
+ bool truncated;
+ std::string end_marker;
+ std::vector<cls_queue_entry> entries;
+ const auto ret = cls_queue_list_entries(ioctx, queue_name, marker, max_elements, entries, &truncated, end_marker);
+ ASSERT_EQ(0, ret);
+ for (auto& entry : entries) {
+ ASSERT_EQ(entry.data, total_bl.front());
+ total_bl.pop_front();
+ }
+ marker = end_marker;
+ cls_queue_remove_entries(op, end_marker);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ // fill half+1 of the queue
+ for (auto i = 0U; i < number_of_entries/2 + 1; ++i) {
+ const auto entry_size = rand()%(max_entry_size - min_entry_size + 1) + min_entry_size;
+ std::string entry_str(entry_size-entry_overhead, 0);
+ std::generate_n(entry_str.begin(), entry_str.size(), [](){return (char)(rand());});
+ bufferlist entry_bl;
+ entry_bl.append(entry_str);
+ std::vector<bufferlist> data{{entry_bl}};
+ cls_queue_enqueue(op, 0, data);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ total_bl.push_back(entry_bl);
+ }
+ }
+}
+
diff --git a/src/test/cls_rbd/CMakeLists.txt b/src/test/cls_rbd/CMakeLists.txt
new file mode 100644
index 000000000..74c5804e7
--- /dev/null
+++ b/src/test/cls_rbd/CMakeLists.txt
@@ -0,0 +1,18 @@
+# cls_test_cls_rbd
+add_executable(ceph_test_cls_rbd
+ test_cls_rbd.cc
+ $<TARGET_OBJECTS:common_texttable_obj>)
+target_link_libraries(ceph_test_cls_rbd
+ cls_rbd_client
+ cls_lock_client
+ libneorados
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ radostest-cxx)
+install(TARGETS
+ ceph_test_cls_rbd
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc
new file mode 100644
index 000000000..2f553a0f4
--- /dev/null
+++ b/src/test/cls_rbd/test_cls_rbd.cc
@@ -0,0 +1,3428 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/compat.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "common/snap_types.h"
+#include "common/Clock.h"
+#include "common/bit_vector.hpp"
+#include "include/encoding.h"
+#include "include/types.h"
+#include "include/rados/librados.h"
+#include "include/rbd/object_map_types.h"
+#include "include/rbd_types.h"
+#include "include/stringify.h"
+#include "cls/rbd/cls_rbd.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/Types.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+
+using namespace std;
+using namespace librbd::cls_client;
+using cls::rbd::MIRROR_PEER_DIRECTION_RX;
+using cls::rbd::MIRROR_PEER_DIRECTION_TX;
+using cls::rbd::MIRROR_PEER_DIRECTION_RX_TX;
+using ::librbd::ParentImageInfo;
+using ceph::encode;
+using ceph::decode;
+
+static int snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
+ uint64_t snap_id, const std::string &snap_name) {
+ librados::ObjectWriteOperation op;
+ ::librbd::cls_client::snapshot_add(&op, snap_id, snap_name, cls::rbd::UserSnapshotNamespace());
+ return ioctx->operate(oid, &op);
+}
+
+static int snapshot_remove(librados::IoCtx *ioctx, const std::string &oid,
+ uint64_t snap_id) {
+ librados::ObjectWriteOperation op;
+ ::librbd::cls_client::snapshot_remove(&op, snap_id);
+ return ioctx->operate(oid, &op);
+}
+
+static int snapshot_rename(librados::IoCtx *ioctx, const std::string &oid,
+ uint64_t snap_id, const std::string &snap_name) {
+ librados::ObjectWriteOperation op;
+ ::librbd::cls_client::snapshot_rename(&op, snap_id, snap_name);
+ return ioctx->operate(oid, &op);
+}
+
+static int old_snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
+ uint64_t snap_id, const std::string &snap_name) {
+ librados::ObjectWriteOperation op;
+ ::librbd::cls_client::old_snapshot_add(&op, snap_id, snap_name);
+ return ioctx->operate(oid, &op);
+}
+
+static char *random_buf(size_t len)
+{
+ char *b = new char[len];
+ for (size_t i = 0; i < len; i++)
+ b[i] = (rand() % (128 - 32)) + 32;
+ return b;
+}
+
+static bool is_sparse_read_supported(librados::IoCtx &ioctx,
+ const std::string &oid) {
+ EXPECT_EQ(0, ioctx.create(oid, true));
+ bufferlist inbl;
+ inbl.append(std::string(1, 'X'));
+ EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 1));
+ EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 3));
+
+ std::map<uint64_t, uint64_t> m;
+ bufferlist outbl;
+ int r = ioctx.sparse_read(oid, m, outbl, 4, 0);
+ ioctx.remove(oid);
+
+ int expected_r = 2;
+ std::map<uint64_t, uint64_t> expected_m = {{1, 1}, {3, 1}};
+ bufferlist expected_outbl;
+ expected_outbl.append(std::string(2, 'X'));
+
+ return (r == expected_r && m == expected_m &&
+ outbl.contents_equal(expected_outbl));
+}
+
+class TestClsRbd : public ::testing::Test {
+public:
+
+ static void SetUpTestCase() {
+ _pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(_pool_name, _rados));
+ }
+
+ static void TearDownTestCase() {
+ ASSERT_EQ(0, destroy_one_pool_pp(_pool_name, _rados));
+ }
+
+ std::string get_temp_image_name() {
+ ++_image_number;
+ return "image" + stringify(_image_number);
+ }
+
+ static std::string _pool_name;
+ static librados::Rados _rados;
+ static uint64_t _image_number;
+
+};
+
+std::string TestClsRbd::_pool_name;
+librados::Rados TestClsRbd::_rados;
+uint64_t TestClsRbd::_image_number = 0;
+
+TEST_F(TestClsRbd, get_all_features)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, ioctx.create(oid, false));
+
+ uint64_t all_features = 0;
+ ASSERT_EQ(0, get_all_features(&ioctx, oid, &all_features));
+ ASSERT_EQ(static_cast<uint64_t>(RBD_FEATURES_ALL),
+ static_cast<uint64_t>(all_features & RBD_FEATURES_ALL));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, copyup)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ bufferlist inbl, outbl;
+
+ // copyup of 0-len nonexistent object should create new 0-len object
+ ioctx.remove(oid);
+ ASSERT_EQ(0, copyup(&ioctx, oid, inbl));
+ uint64_t size;
+ ASSERT_EQ(0, ioctx.stat(oid, &size, NULL));
+ ASSERT_EQ(0U, size);
+
+ // create some random data to write
+ size_t l = 4 << 20;
+ char *b = random_buf(l);
+ inbl.append(b, l);
+ delete [] b;
+ ASSERT_EQ(l, inbl.length());
+
+ // copyup to nonexistent object should create new object
+ ioctx.remove(oid);
+ ASSERT_EQ(-ENOENT, ioctx.remove(oid));
+ ASSERT_EQ(0, copyup(&ioctx, oid, inbl));
+ // and its contents should match
+ ASSERT_EQ(l, (size_t)ioctx.read(oid, outbl, l, 0));
+ ASSERT_TRUE(outbl.contents_equal(inbl));
+
+ // now send different data, but with a preexisting object
+ bufferlist inbl2;
+ b = random_buf(l);
+ inbl2.append(b, l);
+ delete [] b;
+ ASSERT_EQ(l, inbl2.length());
+
+ // should still succeed
+ ASSERT_EQ(0, copyup(&ioctx, oid, inbl));
+ ASSERT_EQ(l, (size_t)ioctx.read(oid, outbl, l, 0));
+ // but contents should not have changed
+ ASSERT_FALSE(outbl.contents_equal(inbl2));
+ ASSERT_TRUE(outbl.contents_equal(inbl));
+
+ ASSERT_EQ(0, ioctx.remove(oid));
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, sparse_copyup)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ioctx.remove(oid);
+
+ bool sparse_read_supported = is_sparse_read_supported(ioctx, oid);
+
+ // copyup of 0-len nonexistent object should create new 0-len object
+ uint64_t size;
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, nullptr));
+ std::map<uint64_t, uint64_t> m;
+ bufferlist inbl;
+ ASSERT_EQ(0, sparse_copyup(&ioctx, oid, m, inbl));
+ ASSERT_EQ(0, ioctx.stat(oid, &size, nullptr));
+ ASSERT_EQ(0U, size);
+
+ // create some data to write
+ inbl.append(std::string(4096, '1'));
+ inbl.append(std::string(4096, '2'));
+ m = {{1024, 4096}, {8192, 4096}};
+
+ // copyup to nonexistent object should create new object
+ ioctx.remove(oid);
+ ASSERT_EQ(-ENOENT, ioctx.remove(oid));
+ ASSERT_EQ(0, sparse_copyup(&ioctx, oid, m, inbl));
+ // and its contents should match
+ bufferlist outbl;
+ bufferlist expected_outbl;
+ expected_outbl.append(std::string(1024, '\0'));
+ expected_outbl.append(std::string(4096, '1'));
+ expected_outbl.append(std::string(8192 - 4096 - 1024, '\0'));
+ expected_outbl.append(std::string(4096, '2'));
+ ASSERT_EQ((int)expected_outbl.length(),
+ ioctx.read(oid, outbl, expected_outbl.length() + 1, 0));
+ ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+ std::map<uint64_t, uint64_t> expected_m;
+ if (sparse_read_supported) {
+ expected_m = m;
+ expected_outbl = inbl;
+ } else {
+ expected_m = {{0, expected_outbl.length()}};
+ }
+ m.clear();
+ outbl.clear();
+ ASSERT_EQ((int)expected_m.size(), ioctx.sparse_read(oid, m, outbl, 65536, 0));
+ ASSERT_EQ(m, expected_m);
+ ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+
+ // now send different data, but with a preexisting object
+ bufferlist inbl2;
+ inbl2.append(std::string(1024, 'X'));
+
+ // should still succeed
+ ASSERT_EQ(0, sparse_copyup(&ioctx, oid, {{0, 1024}}, inbl2));
+ // but contents should not have changed
+ m.clear();
+ outbl.clear();
+ ASSERT_EQ((int)expected_m.size(), ioctx.sparse_read(oid, m, outbl, 65536, 0));
+ ASSERT_EQ(m, expected_m);
+ ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+
+ ASSERT_EQ(0, ioctx.remove(oid));
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, get_and_set_id)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ string id;
+ string valid_id = "0123abcxyzZYXCBA";
+ string invalid_id = ".abc";
+ string empty_id;
+
+ ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id));
+ ASSERT_EQ(-ENOENT, set_id(&ioctx, oid, valid_id));
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+ ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, invalid_id));
+ ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, empty_id));
+ ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id));
+
+ ASSERT_EQ(0, set_id(&ioctx, oid, valid_id));
+ ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id));
+ ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id + valid_id));
+ ASSERT_EQ(0, get_id(&ioctx, oid, &id));
+ ASSERT_EQ(id, valid_id);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, add_remove_child)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ string snapname = "parent_snap";
+ snapid_t snapid(10);
+ string parent_image = "parent_id";
+ set<string>children;
+ cls::rbd::ParentImageSpec pspec(ioctx.get_id(), "", parent_image, snapid);
+
+ // nonexistent children cannot be listed or removed
+ ASSERT_EQ(-ENOENT, get_children(&ioctx, oid, pspec, children));
+ ASSERT_EQ(-ENOENT, remove_child(&ioctx, oid, pspec, "child1"));
+
+ // create the parent and snapshot
+ ASSERT_EQ(0, create_image(&ioctx, parent_image, 2<<20, 0,
+ RBD_FEATURE_LAYERING, parent_image, -1));
+ ASSERT_EQ(0, snapshot_add(&ioctx, parent_image, snapid, snapname));
+
+ // add child to it, verify it showed up
+ ASSERT_EQ(0, add_child(&ioctx, oid, pspec, "child1"));
+ ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children));
+ ASSERT_TRUE(children.find("child1") != children.end());
+ // add another child to it, verify it showed up
+ ASSERT_EQ(0, add_child(&ioctx, oid, pspec, "child2"));
+ ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children));
+ ASSERT_TRUE(children.find("child2") != children.end());
+ // add child2 again, expect -EEXIST
+ ASSERT_EQ(-EEXIST, add_child(&ioctx, oid, pspec, "child2"));
+ // remove first, verify it's gone
+ ASSERT_EQ(0, remove_child(&ioctx, oid, pspec, "child1"));
+ ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children));
+ ASSERT_FALSE(children.find("child1") != children.end());
+ // remove second, verify list empty
+ ASSERT_EQ(0, remove_child(&ioctx, oid, pspec, "child2"));
+ ASSERT_EQ(-ENOENT, get_children(&ioctx, oid, pspec, children));
+ // try to remove again, validate -ENOENT to that as well
+ ASSERT_EQ(-ENOENT, remove_child(&ioctx, oid, pspec, "child2"));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, directory_methods)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ string id, name;
+ string imgname = get_temp_image_name();
+ string imgname2 = get_temp_image_name();
+ string imgname3 = get_temp_image_name();
+ string valid_id = "0123abcxyzZYXCBA";
+ string valid_id2 = "5";
+ string invalid_id = ".abc";
+ string empty;
+
+ ASSERT_EQ(-ENOENT, dir_state_assert(&ioctx, oid,
+ cls::rbd::DIRECTORY_STATE_READY));
+ ASSERT_EQ(-ENOENT, dir_state_set(&ioctx, oid,
+ cls::rbd::DIRECTORY_STATE_ADD_DISABLED));
+
+ ASSERT_EQ(-ENOENT, dir_get_id(&ioctx, oid, imgname, &id));
+ ASSERT_EQ(-ENOENT, dir_get_name(&ioctx, oid, valid_id, &name));
+ ASSERT_EQ(-ENOENT, dir_remove_image(&ioctx, oid, imgname, valid_id));
+
+ ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, imgname, invalid_id));
+ ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, imgname, empty));
+ ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, empty, valid_id));
+
+ map<string, string> images;
+ ASSERT_EQ(-ENOENT, dir_list(&ioctx, oid, "", 30, &images));
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+ ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images));
+ ASSERT_EQ(0u, images.size());
+ ASSERT_EQ(0, ioctx.remove(oid));
+
+ ASSERT_EQ(0, dir_state_set(&ioctx, oid, cls::rbd::DIRECTORY_STATE_READY));
+ ASSERT_EQ(0, dir_state_assert(&ioctx, oid, cls::rbd::DIRECTORY_STATE_READY));
+
+ ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname, valid_id));
+ ASSERT_EQ(-EBUSY, dir_state_set(&ioctx, oid,
+ cls::rbd::DIRECTORY_STATE_ADD_DISABLED));
+
+ ASSERT_EQ(-EEXIST, dir_add_image(&ioctx, oid, imgname, valid_id2));
+ ASSERT_EQ(-EBADF, dir_add_image(&ioctx, oid, imgname2, valid_id));
+ ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images));
+ ASSERT_EQ(1u, images.size());
+ ASSERT_EQ(valid_id, images[imgname]);
+ ASSERT_EQ(0, dir_list(&ioctx, oid, "", 0, &images));
+ ASSERT_EQ(0u, images.size());
+ ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id, &name));
+ ASSERT_EQ(imgname, name);
+ ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname, &id));
+ ASSERT_EQ(valid_id, id);
+
+ ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname2, valid_id2));
+ ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images));
+ ASSERT_EQ(2u, images.size());
+ ASSERT_EQ(valid_id, images[imgname]);
+ ASSERT_EQ(valid_id2, images[imgname2]);
+ ASSERT_EQ(0, dir_list(&ioctx, oid, imgname, 0, &images));
+ ASSERT_EQ(0u, images.size());
+ ASSERT_EQ(0, dir_list(&ioctx, oid, imgname, 2, &images));
+ ASSERT_EQ(1u, images.size());
+ ASSERT_EQ(valid_id2, images[imgname2]);
+ ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name));
+ ASSERT_EQ(imgname2, name);
+ ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname2, &id));
+ ASSERT_EQ(valid_id2, id);
+
+ librados::ObjectWriteOperation op1;
+ dir_rename_image(&op1, imgname, imgname2, valid_id2);
+ ASSERT_EQ(-ESTALE, ioctx.operate(oid, &op1));
+ ASSERT_EQ(-ESTALE, dir_remove_image(&ioctx, oid, imgname, valid_id2));
+ librados::ObjectWriteOperation op2;
+ dir_rename_image(&op2, imgname, imgname2, valid_id);
+ ASSERT_EQ(-EEXIST, ioctx.operate(oid, &op2));
+ ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname, &id));
+ ASSERT_EQ(valid_id, id);
+ ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name));
+ ASSERT_EQ(imgname2, name);
+
+ librados::ObjectWriteOperation op3;
+ dir_rename_image(&op3, imgname, imgname3, valid_id);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+ ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname3, &id));
+ ASSERT_EQ(valid_id, id);
+ ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id, &name));
+ ASSERT_EQ(imgname3, name);
+ librados::ObjectWriteOperation op4;
+ dir_rename_image(&op4, imgname3, imgname, valid_id);
+ ASSERT_EQ(0, ioctx.operate(oid, &op4));
+
+ ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname, valid_id));
+ ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images));
+ ASSERT_EQ(1u, images.size());
+ ASSERT_EQ(valid_id2, images[imgname2]);
+ ASSERT_EQ(0, dir_list(&ioctx, oid, imgname2, 30, &images));
+ ASSERT_EQ(0u, images.size());
+ ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name));
+ ASSERT_EQ(imgname2, name);
+ ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname2, &id));
+ ASSERT_EQ(valid_id2, id);
+ ASSERT_EQ(-ENOENT, dir_get_name(&ioctx, oid, valid_id, &name));
+ ASSERT_EQ(-ENOENT, dir_get_id(&ioctx, oid, imgname, &id));
+
+ ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname, valid_id));
+ ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images));
+ ASSERT_EQ(2u, images.size());
+ ASSERT_EQ(valid_id, images[imgname]);
+ ASSERT_EQ(valid_id2, images[imgname2]);
+ ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname, valid_id));
+ ASSERT_EQ(-ENOENT, dir_remove_image(&ioctx, oid, imgname, valid_id));
+ ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname2, valid_id2));
+ ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images));
+ ASSERT_EQ(0u, images.size());
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, create)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ uint64_t size = 20ULL << 30;
+ uint64_t features = 0;
+ uint8_t order = 22;
+ string object_prefix = oid;
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, size, order,
+ features, object_prefix, -1));
+ ASSERT_EQ(-EEXIST, create_image(&ioctx, oid, size, order,
+ features, object_prefix, -1));
+ ASSERT_EQ(0, ioctx.remove(oid));
+
+ ASSERT_EQ(-EINVAL, create_image(&ioctx, oid, size, order,
+ features, "", -1));
+ ASSERT_EQ(-ENOENT, ioctx.remove(oid));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, order,
+ features, object_prefix, -1));
+ ASSERT_EQ(0, ioctx.remove(oid));
+
+ ASSERT_EQ(-ENOSYS, create_image(&ioctx, oid, size, order,
+ -1, object_prefix, -1));
+ ASSERT_EQ(-ENOENT, ioctx.remove(oid));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, size, order, RBD_FEATURE_DATA_POOL,
+ object_prefix, 123));
+ ASSERT_EQ(0, ioctx.remove(oid));
+ ASSERT_EQ(-EINVAL, create_image(&ioctx, oid, size, order,
+ RBD_FEATURE_OPERATIONS, object_prefix, -1));
+ ASSERT_EQ(-EINVAL, create_image(&ioctx, oid, size, order,
+ RBD_FEATURE_DATA_POOL, object_prefix, -1));
+ ASSERT_EQ(-EINVAL, create_image(&ioctx, oid, size, order, 0, object_prefix,
+ 123));
+
+ bufferlist inbl, outbl;
+ ASSERT_EQ(-EINVAL, ioctx.exec(oid, "rbd", "create", inbl, outbl));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, get_features)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+
+ uint64_t features;
+ uint64_t incompatible_features;
+ ASSERT_EQ(-ENOENT, get_features(&ioctx, oid, false, &features,
+ &incompatible_features));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+ ASSERT_EQ(0, get_features(&ioctx, oid, false, &features,
+ &incompatible_features));
+ ASSERT_EQ(0u, features);
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, get_object_prefix)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+
+ string object_prefix;
+ ASSERT_EQ(-ENOENT, get_object_prefix(&ioctx, oid, &object_prefix));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+ ASSERT_EQ(0, get_object_prefix(&ioctx, oid, &object_prefix));
+ ASSERT_EQ(oid, object_prefix);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, get_create_timestamp)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+ utime_t timestamp;
+ ASSERT_EQ(0, get_create_timestamp(&ioctx, oid, &timestamp));
+ ASSERT_LT(0U, timestamp.tv.tv_sec);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, get_access_timestamp)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+ utime_t timestamp;
+ ASSERT_EQ(0, get_access_timestamp(&ioctx, oid, &timestamp));
+ ASSERT_LT(0U, timestamp.tv.tv_sec);
+
+ ioctx.close();
+}
+TEST_F(TestClsRbd, get_modify_timestamp)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+ utime_t timestamp;
+ ASSERT_EQ(0, get_modify_timestamp(&ioctx, oid, &timestamp));
+ ASSERT_LT(0U, timestamp.tv.tv_sec);
+
+ ioctx.close();
+}
+TEST_F(TestClsRbd, get_data_pool)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+
+ int64_t data_pool_id;
+ ASSERT_EQ(0, ioctx.create(oid, true));
+ ASSERT_EQ(0, get_data_pool(&ioctx, oid, &data_pool_id));
+ ASSERT_EQ(-1, data_pool_id);
+ ASSERT_EQ(0, ioctx.remove(oid));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, RBD_FEATURE_DATA_POOL, oid,
+ 12));
+ ASSERT_EQ(0, get_data_pool(&ioctx, oid, &data_pool_id));
+ ASSERT_EQ(12, data_pool_id);
+}
+
+TEST_F(TestClsRbd, get_size)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ uint64_t size;
+ uint8_t order;
+ ASSERT_EQ(-ENOENT, get_size(&ioctx, oid, CEPH_NOSNAP, &size, &order));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+ ASSERT_EQ(0, get_size(&ioctx, oid, CEPH_NOSNAP, &size, &order));
+ ASSERT_EQ(0u, size);
+ ASSERT_EQ(22, order);
+ ASSERT_EQ(0, ioctx.remove(oid));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 2 << 22, 0, 0, oid, -1));
+ ASSERT_EQ(0, get_size(&ioctx, oid, CEPH_NOSNAP, &size, &order));
+ ASSERT_EQ(2u << 22, size);
+ ASSERT_EQ(0, order);
+
+ ASSERT_EQ(-ENOENT, get_size(&ioctx, oid, 1, &size, &order));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, set_size)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(-ENOENT, set_size(&ioctx, oid, 5));
+
+ uint64_t size;
+ uint8_t order;
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+ ASSERT_EQ(0, get_size(&ioctx, oid, CEPH_NOSNAP, &size, &order));
+ ASSERT_EQ(0u, size);
+ ASSERT_EQ(22, order);
+
+ ASSERT_EQ(0, set_size(&ioctx, oid, 0));
+ ASSERT_EQ(0, get_size(&ioctx, oid, CEPH_NOSNAP, &size, &order));
+ ASSERT_EQ(0u, size);
+ ASSERT_EQ(22, order);
+
+ ASSERT_EQ(0, set_size(&ioctx, oid, 3 << 22));
+ ASSERT_EQ(0, get_size(&ioctx, oid, CEPH_NOSNAP, &size, &order));
+ ASSERT_EQ(3u << 22, size);
+ ASSERT_EQ(22, order);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, protection_status)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ string oid2 = get_temp_image_name();
+ uint8_t status = RBD_PROTECTION_STATUS_UNPROTECTED;
+ ASSERT_EQ(-ENOENT, get_protection_status(&ioctx, oid,
+ CEPH_NOSNAP, &status));
+ ASSERT_EQ(-ENOENT, set_protection_status(&ioctx, oid,
+ CEPH_NOSNAP, status));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, RBD_FEATURE_LAYERING, oid, -1));
+ ASSERT_EQ(0, create_image(&ioctx, oid2, 0, 22, 0, oid, -1));
+ ASSERT_EQ(-EINVAL, get_protection_status(&ioctx, oid2,
+ CEPH_NOSNAP, &status));
+ ASSERT_EQ(-ENOEXEC, set_protection_status(&ioctx, oid2,
+ CEPH_NOSNAP, status));
+ ASSERT_EQ(-EINVAL, get_protection_status(&ioctx, oid,
+ CEPH_NOSNAP, &status));
+ ASSERT_EQ(-EINVAL, set_protection_status(&ioctx, oid,
+ CEPH_NOSNAP, status));
+ ASSERT_EQ(-ENOENT, get_protection_status(&ioctx, oid,
+ 2, &status));
+ ASSERT_EQ(-ENOENT, set_protection_status(&ioctx, oid,
+ 2, status));
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 10, "snap1"));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid,
+ 10, &status));
+ ASSERT_EQ(+RBD_PROTECTION_STATUS_UNPROTECTED, status);
+
+ ASSERT_EQ(0, set_protection_status(&ioctx, oid,
+ 10, RBD_PROTECTION_STATUS_PROTECTED));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid,
+ 10, &status));
+ ASSERT_EQ(+RBD_PROTECTION_STATUS_PROTECTED, status);
+ ASSERT_EQ(-EBUSY, snapshot_remove(&ioctx, oid, 10));
+
+ ASSERT_EQ(0, set_protection_status(&ioctx, oid,
+ 10, RBD_PROTECTION_STATUS_UNPROTECTING));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid,
+ 10, &status));
+ ASSERT_EQ(+RBD_PROTECTION_STATUS_UNPROTECTING, status);
+ ASSERT_EQ(-EBUSY, snapshot_remove(&ioctx, oid, 10));
+
+ ASSERT_EQ(-EINVAL, set_protection_status(&ioctx, oid,
+ 10, RBD_PROTECTION_STATUS_LAST));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid,
+ 10, &status));
+ ASSERT_EQ(+RBD_PROTECTION_STATUS_UNPROTECTING, status);
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 20, "snap2"));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid,
+ 20, &status));
+ ASSERT_EQ(+RBD_PROTECTION_STATUS_UNPROTECTED, status);
+ ASSERT_EQ(0, set_protection_status(&ioctx, oid,
+ 10, RBD_PROTECTION_STATUS_UNPROTECTED));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid,
+ 10, &status));
+ ASSERT_EQ(+RBD_PROTECTION_STATUS_UNPROTECTED, status);
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 10));
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 20));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, snapshot_limits)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ librados::ObjectWriteOperation op;
+ string oid = get_temp_image_name();
+ uint64_t limit;
+
+ ASSERT_EQ(-ENOENT, snapshot_get_limit(&ioctx, oid, &limit));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, RBD_FEATURE_LAYERING, oid, -1));
+
+ // if snapshot doesn't set limit, the limit is UINT64_MAX
+ ASSERT_EQ(0, snapshot_get_limit(&ioctx, oid, &limit));
+ ASSERT_EQ(UINT64_MAX, limit);
+
+ snapshot_set_limit(&op, 2);
+
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ ASSERT_EQ(0, snapshot_get_limit(&ioctx, oid, &limit));
+ ASSERT_EQ(2U, limit);
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 10, "snap1"));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 20, "snap2"));
+ ASSERT_EQ(-EDQUOT, snapshot_add(&ioctx, oid, 30, "snap3"));
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 10));
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 20));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, parents_v1)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ cls::rbd::ParentImageSpec pspec;
+ uint64_t size;
+
+ ASSERT_EQ(-ENOENT, get_parent(&ioctx, "doesnotexist", CEPH_NOSNAP, &pspec, &size));
+
+ // old image should fail
+ std::string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 33<<20, 22, 0, "old_blk.", -1));
+ // get nonexistent parent: succeed, return (-1, "", CEPH_NOSNAP), overlap 0
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, -1);
+ ASSERT_STREQ("", pspec.image_id.c_str());
+ ASSERT_EQ(pspec.snap_id, CEPH_NOSNAP);
+ ASSERT_EQ(size, 0ULL);
+ pspec = {-1, "", "parent", 3};
+ ASSERT_EQ(-ENOEXEC, set_parent(&ioctx, oid, {-1, "", "parent", 3}, 10<<20));
+ ASSERT_EQ(-ENOEXEC, remove_parent(&ioctx, oid));
+
+ // new image will work
+ oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 33<<20, 22, RBD_FEATURE_LAYERING,
+ "foo.", -1));
+
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(-1, pspec.pool_id);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 123, &pspec, &size));
+ ASSERT_EQ(-1, pspec.pool_id);
+
+ ASSERT_EQ(-EINVAL, set_parent(&ioctx, oid, {-1, "", "parent", 3}, 10<<20));
+ ASSERT_EQ(-EINVAL, set_parent(&ioctx, oid, {1, "", "", 3}, 10<<20));
+ ASSERT_EQ(-EINVAL, set_parent(&ioctx, oid, {1, "", "parent", CEPH_NOSNAP}, 10<<20));
+ ASSERT_EQ(-EINVAL, set_parent(&ioctx, oid, {1, "", "parent", 3}, 0));
+
+ pspec = {1, "", "parent", 3};
+ ASSERT_EQ(0, set_parent(&ioctx, oid, pspec, 10<<20));
+ ASSERT_EQ(-EEXIST, set_parent(&ioctx, oid, pspec, 10<<20));
+ ASSERT_EQ(-EEXIST, set_parent(&ioctx, oid, {2, "", "parent", 34}, 10<<20));
+
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+ ASSERT_EQ(-ENOENT, remove_parent(&ioctx, oid));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(-1, pspec.pool_id);
+
+ // snapshots
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 3}, 10<<20));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 10, "snap1"));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 10, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 10ull<<20);
+
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 3}, 5<<20));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 11, "snap2"));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 10, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 10ull<<20);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 11, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 5ull<<20);
+
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 12, "snap3"));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 10, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 10ull<<20);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 11, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 5ull<<20);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 12, &pspec, &size));
+ ASSERT_EQ(-1, pspec.pool_id);
+
+ // make sure set_parent takes min of our size and parent's size
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 3}, 1<<20));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 1ull<<20);
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 3}, 100<<20));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 33ull<<20);
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+
+ // make sure resize adjust parent overlap
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 3}, 10<<20));
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 14, "snap4"));
+ ASSERT_EQ(0, set_size(&ioctx, oid, 3 << 20));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 3ull<<20);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 14, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 10ull<<20);
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 15, "snap5"));
+ ASSERT_EQ(0, set_size(&ioctx, oid, 30 << 20));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 3ull<<20);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 14, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 10ull<<20);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 15, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 3ull<<20);
+
+ ASSERT_EQ(0, set_size(&ioctx, oid, 2 << 20));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 2ull<<20);
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 16, "snap6"));
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 16, &pspec, &size));
+ ASSERT_EQ(pspec.pool_id, 1);
+ ASSERT_EQ(pspec.image_id, "parent");
+ ASSERT_EQ(pspec.snap_id, snapid_t(3));
+ ASSERT_EQ(size, 2ull<<20);
+
+ ASSERT_EQ(0, ioctx.remove(oid));
+ ASSERT_EQ(0, create_image(&ioctx, oid, 33<<20, 22,
+ RBD_FEATURE_LAYERING | RBD_FEATURE_DEEP_FLATTEN,
+ "foo.", -1));
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 3}, 100<<20));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 1, "snap1"));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 2, "snap2"));
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 1, &pspec, &size));
+ ASSERT_EQ(-1, pspec.pool_id);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 2, &pspec, &size));
+ ASSERT_EQ(-1, pspec.pool_id);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, CEPH_NOSNAP, &pspec, &size));
+ ASSERT_EQ(-1, pspec.pool_id);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, parents_v2)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ std::string oid = get_temp_image_name();
+ cls::rbd::ParentImageSpec parent_image_spec;
+ std::optional<uint64_t> parent_overlap;
+
+ ASSERT_EQ(-ENOENT, parent_get(&ioctx, oid, &parent_image_spec));
+ ASSERT_EQ(-ENOENT, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP,
+ &parent_overlap));
+ ASSERT_EQ(-ENOENT, parent_attach(&ioctx, oid, parent_image_spec, 0ULL,
+ false));
+ ASSERT_EQ(-ENOENT, parent_detach(&ioctx, oid));
+
+ // no layering support should fail
+ oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 33<<20, 22, 0, "old_blk.", -1));
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &parent_image_spec));
+ ASSERT_FALSE(parent_image_spec.exists());
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP, &parent_overlap));
+ ASSERT_EQ(std::nullopt, parent_overlap);
+ ASSERT_EQ(-ENOEXEC, parent_attach(&ioctx, oid, parent_image_spec, 0ULL, false));
+ ASSERT_EQ(-ENOEXEC, parent_detach(&ioctx, oid));
+
+ // layering support available -- no pool namespaces
+ oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 33<<20, 22, RBD_FEATURE_LAYERING,
+ "foo.", -1));
+
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &parent_image_spec));
+ ASSERT_FALSE(parent_image_spec.exists());
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP, &parent_overlap));
+ ASSERT_EQ(std::nullopt, parent_overlap);
+ ASSERT_EQ(-EINVAL, parent_attach(&ioctx, oid, parent_image_spec, 0ULL, false));
+ ASSERT_EQ(-ENOENT, parent_detach(&ioctx, oid));
+
+ parent_image_spec = {1, "", "parent", 2};
+ parent_overlap = (33 << 20) + 1;
+ ASSERT_EQ(0, parent_attach(&ioctx, oid, parent_image_spec, *parent_overlap,
+ false));
+ ASSERT_EQ(-EEXIST, parent_attach(&ioctx, oid, parent_image_spec,
+ *parent_overlap, false));
+ ASSERT_EQ(0, parent_attach(&ioctx, oid, parent_image_spec, *parent_overlap,
+ true));
+ --(*parent_overlap);
+
+ cls::rbd::ParentImageSpec on_disk_parent_image_spec;
+ std::optional<uint64_t> on_disk_parent_overlap;
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &on_disk_parent_image_spec));
+ ASSERT_EQ(parent_image_spec, on_disk_parent_image_spec);
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP,
+ &on_disk_parent_overlap));
+ ASSERT_EQ(parent_overlap, on_disk_parent_overlap);
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 10, "snap1"));
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, 10, &on_disk_parent_overlap));
+ std::optional<uint64_t> snap_1_parent_overlap = parent_overlap;
+ ASSERT_EQ(snap_1_parent_overlap, on_disk_parent_overlap);
+
+ parent_overlap = (32 << 20);
+ ASSERT_EQ(0, set_size(&ioctx, oid, *parent_overlap));
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP,
+ &on_disk_parent_overlap));
+ ASSERT_EQ(parent_overlap, on_disk_parent_overlap);
+
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, 10, &on_disk_parent_overlap));
+ ASSERT_EQ(snap_1_parent_overlap, on_disk_parent_overlap);
+
+ ASSERT_EQ(0, parent_detach(&ioctx, oid));
+ ASSERT_EQ(-ENOENT, parent_detach(&ioctx, oid));
+
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &on_disk_parent_image_spec));
+ ASSERT_EQ(parent_image_spec, on_disk_parent_image_spec);
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP,
+ &on_disk_parent_overlap));
+ ASSERT_EQ(std::nullopt, on_disk_parent_overlap);
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 10));
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &on_disk_parent_image_spec));
+ ASSERT_FALSE(on_disk_parent_image_spec.exists());
+
+ // clone across pool namespaces
+ parent_image_spec.pool_namespace = "ns";
+ parent_overlap = 31 << 20;
+ ASSERT_EQ(0, parent_attach(&ioctx, oid, parent_image_spec, *parent_overlap,
+ false));
+ ASSERT_EQ(-EEXIST, parent_attach(&ioctx, oid, parent_image_spec,
+ *parent_overlap, false));
+ ASSERT_EQ(0, parent_attach(&ioctx, oid, parent_image_spec, *parent_overlap,
+ true));
+
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &on_disk_parent_image_spec));
+ ASSERT_EQ(parent_image_spec, on_disk_parent_image_spec);
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP,
+ &on_disk_parent_overlap));
+ ASSERT_EQ(parent_overlap, on_disk_parent_overlap);
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 10, "snap1"));
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, 10, &on_disk_parent_overlap));
+ snap_1_parent_overlap = parent_overlap;
+ ASSERT_EQ(snap_1_parent_overlap, on_disk_parent_overlap);
+
+ parent_overlap = (30 << 20);
+ ASSERT_EQ(0, set_size(&ioctx, oid, *parent_overlap));
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP,
+ &on_disk_parent_overlap));
+ ASSERT_EQ(parent_overlap, on_disk_parent_overlap);
+
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, 10, &on_disk_parent_overlap));
+ ASSERT_EQ(snap_1_parent_overlap, on_disk_parent_overlap);
+
+ ASSERT_EQ(-EXDEV, remove_parent(&ioctx, oid));
+ ASSERT_EQ(0, parent_detach(&ioctx, oid));
+ ASSERT_EQ(-ENOENT, parent_detach(&ioctx, oid));
+
+ cls::rbd::ParentImageSpec on_disk_parent_spec;
+ uint64_t legacy_parent_overlap;
+ ASSERT_EQ(-EXDEV, get_parent(&ioctx, oid, CEPH_NOSNAP, &on_disk_parent_spec,
+ &legacy_parent_overlap));
+ ASSERT_EQ(-EXDEV, get_parent(&ioctx, oid, 10, &on_disk_parent_spec,
+ &legacy_parent_overlap));
+
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &on_disk_parent_image_spec));
+ ASSERT_EQ(parent_image_spec, on_disk_parent_image_spec);
+ ASSERT_EQ(0, parent_overlap_get(&ioctx, oid, CEPH_NOSNAP,
+ &on_disk_parent_overlap));
+ ASSERT_EQ(std::nullopt, on_disk_parent_overlap);
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 10));
+ ASSERT_EQ(0, parent_get(&ioctx, oid, &on_disk_parent_image_spec));
+ ASSERT_FALSE(on_disk_parent_image_spec.exists());
+}
+
+TEST_F(TestClsRbd, snapshots)
+{
+ cls::rbd::SnapshotNamespace userSnapNamespace = cls::rbd::UserSnapshotNamespace();
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(-ENOENT, snapshot_add(&ioctx, oid, 0, "snap1"));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 10, 22, 0, oid, -1));
+
+ SnapContext snapc;
+ cls::rbd::SnapshotInfo snap;
+ std::string snap_name;
+ uint64_t snap_size;
+ uint8_t snap_order;
+ ParentImageInfo parent;
+ uint8_t protection_status;
+ utime_t snap_timestamp;
+
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(0u, snapc.snaps.size());
+ ASSERT_EQ(0u, snapc.seq);
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 0, "snap1"));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(1u, snapc.snaps.size());
+ ASSERT_EQ(0u, snapc.snaps[0]);
+ ASSERT_EQ(0u, snapc.seq);
+
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 0, &snap));
+ ASSERT_EQ("snap1", snap.name);
+ ASSERT_EQ(userSnapNamespace, snap.snapshot_namespace);
+ ASSERT_EQ(0, get_snapshot_name(&ioctx, oid, 0, &snap_name));
+ ASSERT_EQ("snap1", snap_name);
+ ASSERT_EQ(0, get_size(&ioctx, oid, 0, &snap_size, &snap_order));
+ ASSERT_EQ(10U, snap_size);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 0, &parent.spec, &parent.overlap));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid, 0, &protection_status));
+ ASSERT_EQ(0, get_snapshot_timestamp(&ioctx, oid, 0, &snap_timestamp));
+
+ // snap with same id and name
+ ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, oid, 0, "snap1"));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(1u, snapc.snaps.size());
+ ASSERT_EQ(0u, snapc.snaps[0]);
+ ASSERT_EQ(0u, snapc.seq);
+
+ // snap with same id, different name
+ ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, oid, 0, "snap2"));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(1u, snapc.snaps.size());
+ ASSERT_EQ(0u, snapc.snaps[0]);
+ ASSERT_EQ(0u, snapc.seq);
+
+ // snap with different id, same name
+ ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, oid, 1, "snap1"));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(1u, snapc.snaps.size());
+ ASSERT_EQ(0u, snapc.snaps[0]);
+ ASSERT_EQ(0u, snapc.seq);
+
+ // snap with different id, different name
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 1, "snap2"));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(2u, snapc.snaps.size());
+ ASSERT_EQ(1u, snapc.snaps[0]);
+ ASSERT_EQ(0u, snapc.snaps[1]);
+ ASSERT_EQ(1u, snapc.seq);
+
+ // snap id less than current snap seq
+ ASSERT_EQ(-ESTALE, snapshot_add(&ioctx, oid, 0, "snap3"));
+
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 1, &snap));
+ ASSERT_EQ("snap2", snap.name);
+ ASSERT_EQ(userSnapNamespace, snap.snapshot_namespace);
+ ASSERT_EQ(0, get_snapshot_name(&ioctx, oid, 1, &snap_name));
+ ASSERT_EQ("snap2", snap_name);
+ ASSERT_EQ(0, get_size(&ioctx, oid, 1, &snap_size, &snap_order));
+ ASSERT_EQ(10U, snap_size);
+ ASSERT_EQ(0, get_parent(&ioctx, oid, 1, &parent.spec, &parent.overlap));
+ ASSERT_EQ(0, get_protection_status(&ioctx, oid, 1, &protection_status));
+ ASSERT_EQ(0, get_snapshot_timestamp(&ioctx, oid, 1, &snap_timestamp));
+
+ ASSERT_EQ(0, snapshot_rename(&ioctx, oid, 0, "snap1-rename"));
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 0, &snap));
+ ASSERT_EQ("snap1-rename", snap.name);
+ ASSERT_EQ(0, get_snapshot_name(&ioctx, oid, 0, &snap_name));
+ ASSERT_EQ("snap1-rename", snap_name);
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 0));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(1u, snapc.snaps.size());
+ ASSERT_EQ(1u, snapc.snaps[0]);
+ ASSERT_EQ(1u, snapc.seq);
+
+ uint64_t size;
+ uint8_t order;
+ ASSERT_EQ(0, set_size(&ioctx, oid, 0));
+ ASSERT_EQ(0, get_size(&ioctx, oid, CEPH_NOSNAP, &size, &order));
+ ASSERT_EQ(0u, size);
+ ASSERT_EQ(22u, order);
+
+ uint64_t large_snap_id = 1ull << 63;
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, large_snap_id, "snap3"));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(2u, snapc.snaps.size());
+ ASSERT_EQ(large_snap_id, snapc.snaps[0]);
+ ASSERT_EQ(1u, snapc.snaps[1]);
+ ASSERT_EQ(large_snap_id, snapc.seq);
+
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, large_snap_id, &snap));
+ ASSERT_EQ("snap3", snap.name);
+ ASSERT_EQ(0, get_snapshot_name(&ioctx, oid, large_snap_id, &snap_name));
+ ASSERT_EQ("snap3", snap_name);
+ ASSERT_EQ(0, get_size(&ioctx, oid, large_snap_id, &snap_size, &snap_order));
+ ASSERT_EQ(0U, snap_size);
+ ASSERT_EQ(22u, snap_order);
+
+ ASSERT_EQ(0, get_size(&ioctx, oid, 1, &snap_size, &snap_order));
+ ASSERT_EQ(10u, snap_size);
+ ASSERT_EQ(22u, snap_order);
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, large_snap_id));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(1u, snapc.snaps.size());
+ ASSERT_EQ(1u, snapc.snaps[0]);
+ ASSERT_EQ(large_snap_id, snapc.seq);
+
+ ASSERT_EQ(-ENOENT, snapshot_remove(&ioctx, oid, large_snap_id));
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 1));
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(0u, snapc.snaps.size());
+ ASSERT_EQ(large_snap_id, snapc.seq);
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, snapid_race)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ buffer::list bl;
+ buffer::ptr bp(4096);
+ bp.zero();
+ bl.append(bp);
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, ioctx.write(oid, bl, 4096, 0));
+ ASSERT_EQ(0, old_snapshot_add(&ioctx, oid, 1, "test1"));
+ ASSERT_EQ(0, old_snapshot_add(&ioctx, oid, 3, "test3"));
+ ASSERT_EQ(-ESTALE, old_snapshot_add(&ioctx, oid, 2, "test2"));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, stripingv2)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ string oid2 = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 10, 22, 0, oid, -1));
+
+ uint64_t su = 65536, sc = 12;
+ ASSERT_EQ(-ENOEXEC, get_stripe_unit_count(&ioctx, oid, &su, &sc));
+ ASSERT_EQ(-ENOEXEC, set_stripe_unit_count(&ioctx, oid, su, sc));
+
+ ASSERT_EQ(0, create_image(&ioctx, oid2, 10, 22, RBD_FEATURE_STRIPINGV2,
+ oid2, -1));
+ ASSERT_EQ(0, get_stripe_unit_count(&ioctx, oid2, &su, &sc));
+ ASSERT_EQ(1ull << 22, su);
+ ASSERT_EQ(1ull, sc);
+ su = 8192;
+ sc = 456;
+ ASSERT_EQ(0, set_stripe_unit_count(&ioctx, oid2, su, sc));
+ su = sc = 0;
+ ASSERT_EQ(0, get_stripe_unit_count(&ioctx, oid2, &su, &sc));
+ ASSERT_EQ(8192ull, su);
+ ASSERT_EQ(456ull, sc);
+
+ // su must not be larger than an object
+ ASSERT_EQ(-EINVAL, set_stripe_unit_count(&ioctx, oid2, 1 << 23, 1));
+ // su must be a factor of object size
+ ASSERT_EQ(-EINVAL, set_stripe_unit_count(&ioctx, oid2, 511, 1));
+ // su and sc must be non-zero
+ ASSERT_EQ(-EINVAL, set_stripe_unit_count(&ioctx, oid2, 0, 1));
+ ASSERT_EQ(-EINVAL, set_stripe_unit_count(&ioctx, oid2, 1, 0));
+ ASSERT_EQ(-EINVAL, set_stripe_unit_count(&ioctx, oid2, 0, 0));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, object_map_save)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ BitVector<2> ref_bit_vector;
+ ref_bit_vector.resize(32);
+ for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) {
+ ref_bit_vector[i] = 1;
+ }
+
+ librados::ObjectWriteOperation op;
+ object_map_save(&op, ref_bit_vector);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ BitVector<2> osd_bit_vector;
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+}
+
+TEST_F(TestClsRbd, object_map_resize)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ BitVector<2> ref_bit_vector;
+ ref_bit_vector.resize(32);
+ for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) {
+ ref_bit_vector[i] = 1;
+ }
+
+ librados::ObjectWriteOperation op1;
+ object_map_resize(&op1, ref_bit_vector.size(), 1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ BitVector<2> osd_bit_vector;
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ ref_bit_vector.resize(64);
+ for (uint64_t i = 32; i < ref_bit_vector.size(); ++i) {
+ ref_bit_vector[i] = 2;
+ }
+
+ librados::ObjectWriteOperation op2;
+ object_map_resize(&op2, ref_bit_vector.size(), 2);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ ref_bit_vector.resize(32);
+
+ librados::ObjectWriteOperation op3;
+ object_map_resize(&op3, ref_bit_vector.size(), 1);
+ ASSERT_EQ(-ESTALE, ioctx.operate(oid, &op3));
+
+ librados::ObjectWriteOperation op4;
+ object_map_resize(&op4, ref_bit_vector.size(), 2);
+ ASSERT_EQ(0, ioctx.operate(oid, &op4));
+
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, object_map_update)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ BitVector<2> ref_bit_vector;
+ ref_bit_vector.resize(16);
+ for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) {
+ ref_bit_vector[i] = 2;
+ }
+
+ BitVector<2> osd_bit_vector;
+
+ librados::ObjectWriteOperation op1;
+ object_map_resize(&op1, ref_bit_vector.size(), 2);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ ref_bit_vector[7] = 1;
+ ref_bit_vector[8] = 1;
+
+ librados::ObjectWriteOperation op2;
+ object_map_update(&op2, 7, 9, 1, boost::optional<uint8_t>());
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ ref_bit_vector[7] = 3;
+ ref_bit_vector[8] = 3;
+
+ librados::ObjectWriteOperation op3;
+ object_map_update(&op3, 6, 10, 3, 1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, object_map_load_enoent)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ BitVector<2> osd_bit_vector;
+ ASSERT_EQ(-ENOENT, object_map_load(&ioctx, oid, &osd_bit_vector));
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, object_map_snap_add)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ BitVector<2> ref_bit_vector;
+ ref_bit_vector.resize(16);
+ for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) {
+ if (i < 4) {
+ ref_bit_vector[i] = OBJECT_NONEXISTENT;
+ } else {
+ ref_bit_vector[i] = OBJECT_EXISTS;
+ }
+ }
+
+ BitVector<2> osd_bit_vector;
+
+ librados::ObjectWriteOperation op1;
+ object_map_resize(&op1, ref_bit_vector.size(), OBJECT_EXISTS);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ object_map_update(&op2, 0, 4, OBJECT_NONEXISTENT, boost::optional<uint8_t>());
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ librados::ObjectWriteOperation op3;
+ object_map_snap_add(&op3);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+
+ for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) {
+ if (ref_bit_vector[i] == OBJECT_EXISTS) {
+ ref_bit_vector[i] = OBJECT_EXISTS_CLEAN;
+ }
+ }
+
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+}
+
+TEST_F(TestClsRbd, object_map_snap_remove)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ BitVector<2> ref_bit_vector;
+ ref_bit_vector.resize(16);
+ for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) {
+ if (i < 4) {
+ ref_bit_vector[i] = OBJECT_EXISTS_CLEAN;
+ } else {
+ ref_bit_vector[i] = OBJECT_EXISTS;
+ }
+ }
+
+ BitVector<2> osd_bit_vector;
+
+ librados::ObjectWriteOperation op1;
+ object_map_resize(&op1, ref_bit_vector.size(), OBJECT_EXISTS);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ object_map_update(&op2, 0, 4, OBJECT_EXISTS_CLEAN, boost::optional<uint8_t>());
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+
+ BitVector<2> snap_bit_vector;
+ snap_bit_vector.resize(4);
+ for (uint64_t i = 0; i < snap_bit_vector.size(); ++i) {
+ if (i == 1 || i == 2) {
+ snap_bit_vector[i] = OBJECT_EXISTS;
+ } else {
+ snap_bit_vector[i] = OBJECT_NONEXISTENT;
+ }
+ }
+
+ librados::ObjectWriteOperation op3;
+ object_map_snap_remove(&op3, snap_bit_vector);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+
+ ref_bit_vector[1] = OBJECT_EXISTS;
+ ref_bit_vector[2] = OBJECT_EXISTS;
+ ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
+ ASSERT_EQ(ref_bit_vector, osd_bit_vector);
+}
+
+TEST_F(TestClsRbd, flags)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+ uint64_t flags;
+ ASSERT_EQ(0, get_flags(&ioctx, oid, CEPH_NOSNAP, &flags));
+ ASSERT_EQ(0U, flags);
+
+ librados::ObjectWriteOperation op1;
+ set_flags(&op1, CEPH_NOSNAP, 3, 2);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+ ASSERT_EQ(0, get_flags(&ioctx, oid, CEPH_NOSNAP, &flags));
+ ASSERT_EQ(2U, flags);
+
+ uint64_t snap_id = 10;
+ ASSERT_EQ(-ENOENT, get_flags(&ioctx, oid, snap_id, &flags));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, snap_id, "snap"));
+
+ librados::ObjectWriteOperation op2;
+ set_flags(&op2, snap_id, 31, 4);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+ ASSERT_EQ(0, get_flags(&ioctx, oid, snap_id, &flags));
+ ASSERT_EQ(6U, flags);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, metadata)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+ map<string, bufferlist> pairs;
+ string value;
+ ASSERT_EQ(0, metadata_list(&ioctx, oid, "", 0, &pairs));
+ ASSERT_TRUE(pairs.empty());
+
+ pairs["key1"].append("value1");
+ pairs["key2"].append("value2");
+ ASSERT_EQ(0, metadata_set(&ioctx, oid, pairs));
+ ASSERT_EQ(0, metadata_get(&ioctx, oid, "key1", &value));
+ ASSERT_EQ(0, strcmp("value1", value.c_str()));
+ pairs.clear();
+ ASSERT_EQ(0, metadata_list(&ioctx, oid, "", 0, &pairs));
+ ASSERT_EQ(2U, pairs.size());
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ pairs.clear();
+ ASSERT_EQ(0, metadata_remove(&ioctx, oid, "key1"));
+ ASSERT_EQ(0, metadata_remove(&ioctx, oid, "key3"));
+ ASSERT_TRUE(metadata_get(&ioctx, oid, "key1", &value) < 0);
+ ASSERT_EQ(0, metadata_list(&ioctx, oid, "", 0, &pairs));
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ pairs.clear();
+ char key[10], val[20];
+ for (int i = 0; i < 1024; i++) {
+ sprintf(key, "key%d", i);
+ sprintf(val, "value%d", i);
+ pairs[key].append(val, strlen(val));
+ }
+ ASSERT_EQ(0, metadata_set(&ioctx, oid, pairs));
+
+ string last_read = "";
+ uint64_t max_read = 48, r;
+ uint64_t size = 0;
+ map<string, bufferlist> data;
+ do {
+ map<string, bufferlist> cur;
+ metadata_list(&ioctx, oid, last_read, max_read, &cur);
+ size += cur.size();
+ for (map<string, bufferlist>::iterator it = cur.begin();
+ it != cur.end(); ++it)
+ data[it->first] = it->second;
+ last_read = cur.rbegin()->first;
+ r = cur.size();
+ } while (r == max_read);
+ ASSERT_EQ(size, 1024U);
+ for (map<string, bufferlist>::iterator it = data.begin();
+ it != data.end(); ++it) {
+ ASSERT_TRUE(it->second.contents_equal(pairs[it->first]));
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, set_features)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ uint64_t base_features = RBD_FEATURE_LAYERING | RBD_FEATURE_DEEP_FLATTEN;
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, base_features, oid, -1));
+
+ uint64_t features = RBD_FEATURES_MUTABLE;
+ uint64_t mask = RBD_FEATURES_MUTABLE;
+ ASSERT_EQ(0, set_features(&ioctx, oid, features, mask));
+
+ uint64_t actual_features;
+ uint64_t incompatible_features;
+ ASSERT_EQ(0, get_features(&ioctx, oid, true, &actual_features,
+ &incompatible_features));
+
+ uint64_t expected_features = RBD_FEATURES_MUTABLE | base_features;
+ ASSERT_EQ(expected_features, actual_features);
+
+ features = 0;
+ mask = RBD_FEATURE_OBJECT_MAP;
+ ASSERT_EQ(0, set_features(&ioctx, oid, features, mask));
+
+ ASSERT_EQ(0, get_features(&ioctx, oid, true, &actual_features,
+ &incompatible_features));
+
+ expected_features = (RBD_FEATURES_MUTABLE | base_features) &
+ ~RBD_FEATURE_OBJECT_MAP;
+ ASSERT_EQ(expected_features, actual_features);
+
+ ASSERT_EQ(0, set_features(&ioctx, oid, 0, RBD_FEATURE_DEEP_FLATTEN));
+ ASSERT_EQ(-EINVAL, set_features(&ioctx, oid, RBD_FEATURE_DEEP_FLATTEN,
+ RBD_FEATURE_DEEP_FLATTEN));
+
+ ASSERT_EQ(-EINVAL, set_features(&ioctx, oid, 0, RBD_FEATURE_LAYERING));
+ ASSERT_EQ(-EINVAL, set_features(&ioctx, oid, 0, RBD_FEATURE_OPERATIONS));
+}
+
+TEST_F(TestClsRbd, mirror) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_MIRRORING);
+
+ std::vector<cls::rbd::MirrorPeer> peers;
+ ASSERT_EQ(-ENOENT, mirror_peer_list(&ioctx, &peers));
+
+ std::string uuid;
+ ASSERT_EQ(-ENOENT, mirror_uuid_get(&ioctx, &uuid));
+ ASSERT_EQ(-EINVAL, mirror_peer_add(&ioctx, {"uuid1", MIRROR_PEER_DIRECTION_RX,
+ "siteA", "client",
+ "mirror uuid"}));
+ ASSERT_EQ(-EINVAL, mirror_peer_ping(&ioctx, "siteA", "mirror uuid"));
+
+ cls::rbd::MirrorMode mirror_mode;
+ ASSERT_EQ(0, mirror_mode_get(&ioctx, &mirror_mode));
+ ASSERT_EQ(cls::rbd::MIRROR_MODE_DISABLED, mirror_mode);
+
+ ASSERT_EQ(-EINVAL, mirror_mode_set(&ioctx, cls::rbd::MIRROR_MODE_IMAGE));
+ ASSERT_EQ(-EINVAL, mirror_uuid_set(&ioctx, ""));
+ ASSERT_EQ(0, mirror_uuid_set(&ioctx, "mirror-uuid"));
+ ASSERT_EQ(0, mirror_uuid_get(&ioctx, &uuid));
+ ASSERT_EQ("mirror-uuid", uuid);
+
+ ASSERT_EQ(0, mirror_mode_set(&ioctx, cls::rbd::MIRROR_MODE_IMAGE));
+ ASSERT_EQ(0, mirror_mode_get(&ioctx, &mirror_mode));
+ ASSERT_EQ(cls::rbd::MIRROR_MODE_IMAGE, mirror_mode);
+
+ ASSERT_EQ(-EINVAL, mirror_uuid_set(&ioctx, "new-mirror-uuid"));
+
+ ASSERT_EQ(0, mirror_mode_set(&ioctx, cls::rbd::MIRROR_MODE_POOL));
+ ASSERT_EQ(0, mirror_mode_get(&ioctx, &mirror_mode));
+ ASSERT_EQ(cls::rbd::MIRROR_MODE_POOL, mirror_mode);
+
+ ASSERT_EQ(-EINVAL, mirror_peer_add(&ioctx, {"mirror-uuid",
+ MIRROR_PEER_DIRECTION_RX, "siteA",
+ "client", ""}));
+ ASSERT_EQ(-EINVAL, mirror_peer_add(&ioctx, {"uuid1", MIRROR_PEER_DIRECTION_TX,
+ "siteA", "client",
+ "mirror uuid"}));
+ ASSERT_EQ(0, mirror_peer_add(&ioctx, {"uuid1", MIRROR_PEER_DIRECTION_RX,
+ "siteA", "client", "fsidA"}));
+ ASSERT_EQ(0, mirror_peer_add(&ioctx, {"uuid2", MIRROR_PEER_DIRECTION_RX,
+ "siteB", "admin", ""}));
+ ASSERT_EQ(-ESTALE, mirror_peer_add(&ioctx, {"uuid2", MIRROR_PEER_DIRECTION_RX,
+ "siteC", "foo", ""}));
+ ASSERT_EQ(-EEXIST, mirror_peer_add(&ioctx, {"uuid3", MIRROR_PEER_DIRECTION_RX,
+ "siteA", "foo", ""}));
+ ASSERT_EQ(-EEXIST, mirror_peer_add(&ioctx, {"uuid3", MIRROR_PEER_DIRECTION_RX,
+ "siteC", "client", "fsidA"}));
+ ASSERT_EQ(0, mirror_peer_add(&ioctx, {"uuid3", MIRROR_PEER_DIRECTION_RX,
+ "siteC", "admin", ""}));
+ ASSERT_EQ(0, mirror_peer_add(&ioctx, {"uuid4", MIRROR_PEER_DIRECTION_RX,
+ "siteD", "admin", ""}));
+
+ ASSERT_EQ(0, mirror_peer_list(&ioctx, &peers));
+ std::vector<cls::rbd::MirrorPeer> expected_peers = {
+ {"uuid1", MIRROR_PEER_DIRECTION_RX, "siteA", "client", "fsidA"},
+ {"uuid2", MIRROR_PEER_DIRECTION_RX, "siteB", "admin", ""},
+ {"uuid3", MIRROR_PEER_DIRECTION_RX, "siteC", "admin", ""},
+ {"uuid4", MIRROR_PEER_DIRECTION_RX, "siteD", "admin", ""}};
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(0, mirror_peer_remove(&ioctx, "uuid5"));
+ ASSERT_EQ(0, mirror_peer_remove(&ioctx, "uuid4"));
+ ASSERT_EQ(0, mirror_peer_remove(&ioctx, "uuid2"));
+
+ ASSERT_EQ(0, mirror_peer_list(&ioctx, &peers));
+ expected_peers = {
+ {"uuid1", MIRROR_PEER_DIRECTION_RX, "siteA", "client", "fsidA"},
+ {"uuid3", MIRROR_PEER_DIRECTION_RX, "siteC", "admin", ""}};
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(-ENOENT, mirror_peer_set_client(&ioctx, "uuid4", "new client"));
+ ASSERT_EQ(0, mirror_peer_set_client(&ioctx, "uuid1", "new client"));
+
+ ASSERT_EQ(-ENOENT, mirror_peer_set_cluster(&ioctx, "uuid4", "new site"));
+ ASSERT_EQ(0, mirror_peer_set_cluster(&ioctx, "uuid3", "new site"));
+
+ ASSERT_EQ(0, mirror_peer_list(&ioctx, &peers));
+ expected_peers = {
+ {"uuid1", MIRROR_PEER_DIRECTION_RX, "siteA", "new client", "fsidA"},
+ {"uuid3", MIRROR_PEER_DIRECTION_RX, "new site", "admin", ""}};
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(0, mirror_peer_remove(&ioctx, "uuid1"));
+
+ ASSERT_EQ(0, mirror_peer_list(&ioctx, &peers));
+ expected_peers = {
+ {"uuid3", MIRROR_PEER_DIRECTION_RX, "new site", "admin", ""}};
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(-EINVAL, mirror_peer_ping(&ioctx, "", "mirror uuid"));
+ ASSERT_EQ(-EINVAL, mirror_peer_ping(&ioctx, "new site", ""));
+ ASSERT_EQ(0, mirror_peer_ping(&ioctx, "new site", "mirror uuid"));
+
+ ASSERT_EQ(0, mirror_peer_list(&ioctx, &peers));
+ ASSERT_EQ(1U, peers.size());
+ ASSERT_LT(utime_t{}, peers[0].last_seen);
+ expected_peers = {
+ {"uuid3", MIRROR_PEER_DIRECTION_RX_TX, "new site", "admin", "mirror uuid"}};
+ expected_peers[0].last_seen = peers[0].last_seen;
+ ASSERT_EQ(expected_peers, peers);
+ ASSERT_EQ(0, mirror_peer_remove(&ioctx, "uuid3"));
+
+ ASSERT_EQ(0, mirror_peer_ping(&ioctx, "siteA", "mirror uuid"));
+
+ ASSERT_EQ(0, mirror_peer_list(&ioctx, &peers));
+ ASSERT_EQ(1U, peers.size());
+ ASSERT_FALSE(peers[0].uuid.empty());
+ ASSERT_LT(utime_t{}, peers[0].last_seen);
+ expected_peers = {
+ {peers[0].uuid, MIRROR_PEER_DIRECTION_TX, "siteA", "", "mirror uuid"}};
+ expected_peers[0].last_seen = peers[0].last_seen;
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(-EBUSY, mirror_mode_set(&ioctx, cls::rbd::MIRROR_MODE_DISABLED));
+ ASSERT_EQ(0, mirror_peer_remove(&ioctx, peers[0].uuid));
+
+ ASSERT_EQ(0, mirror_peer_remove(&ioctx, "DNE"));
+ ASSERT_EQ(0, mirror_peer_list(&ioctx, &peers));
+ expected_peers = {};
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(0, mirror_mode_set(&ioctx, cls::rbd::MIRROR_MODE_DISABLED));
+ ASSERT_EQ(0, mirror_mode_get(&ioctx, &mirror_mode));
+ ASSERT_EQ(cls::rbd::MIRROR_MODE_DISABLED, mirror_mode);
+ ASSERT_EQ(-ENOENT, mirror_uuid_get(&ioctx, &uuid));
+}
+
+TEST_F(TestClsRbd, mirror_image) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_MIRRORING);
+
+ std::map<std::string, std::string> mirror_image_ids;
+ ASSERT_EQ(-ENOENT, mirror_image_list(&ioctx, "", 0, &mirror_image_ids));
+
+ cls::rbd::MirrorImage image1(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid1",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED);
+ cls::rbd::MirrorImage image2(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid2",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING);
+ cls::rbd::MirrorImage image3(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid3",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED);
+
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id1", image1));
+ ASSERT_EQ(-ENOENT, mirror_image_set(&ioctx, "image_id2", image2));
+ image2.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id2", image2));
+ image2.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id2", image2));
+ ASSERT_EQ(-EINVAL, mirror_image_set(&ioctx, "image_id1", image2));
+ ASSERT_EQ(-EEXIST, mirror_image_set(&ioctx, "image_id3", image2));
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id3", image3));
+
+ std::string image_id;
+ ASSERT_EQ(0, mirror_image_get_image_id(&ioctx, "uuid2", &image_id));
+ ASSERT_EQ("image_id2", image_id);
+
+ cls::rbd::MirrorImage read_image;
+ ASSERT_EQ(0, mirror_image_get(&ioctx, "image_id1", &read_image));
+ ASSERT_EQ(read_image, image1);
+ ASSERT_EQ(0, mirror_image_get(&ioctx, "image_id2", &read_image));
+ ASSERT_EQ(read_image, image2);
+ ASSERT_EQ(0, mirror_image_get(&ioctx, "image_id3", &read_image));
+ ASSERT_EQ(read_image, image3);
+
+ ASSERT_EQ(0, mirror_image_list(&ioctx, "", 1, &mirror_image_ids));
+ std::map<std::string, std::string> expected_mirror_image_ids = {
+ {"image_id1", "uuid1"}};
+ ASSERT_EQ(expected_mirror_image_ids, mirror_image_ids);
+
+ ASSERT_EQ(0, mirror_image_list(&ioctx, "image_id1", 2, &mirror_image_ids));
+ expected_mirror_image_ids = {{"image_id2", "uuid2"}, {"image_id3", "uuid3"}};
+ ASSERT_EQ(expected_mirror_image_ids, mirror_image_ids);
+
+ ASSERT_EQ(0, mirror_image_remove(&ioctx, "image_id2"));
+ ASSERT_EQ(-ENOENT, mirror_image_get_image_id(&ioctx, "uuid2", &image_id));
+ ASSERT_EQ(-EBUSY, mirror_image_remove(&ioctx, "image_id1"));
+
+ ASSERT_EQ(0, mirror_image_list(&ioctx, "", 3, &mirror_image_ids));
+ expected_mirror_image_ids = {{"image_id1", "uuid1"}, {"image_id3", "uuid3"}};
+ ASSERT_EQ(expected_mirror_image_ids, mirror_image_ids);
+
+ image1.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
+ image3.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id1", image1));
+ ASSERT_EQ(0, mirror_image_get(&ioctx, "image_id1", &read_image));
+ ASSERT_EQ(read_image, image1);
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id3", image3));
+ ASSERT_EQ(0, mirror_image_remove(&ioctx, "image_id1"));
+ ASSERT_EQ(0, mirror_image_remove(&ioctx, "image_id3"));
+
+ ASSERT_EQ(0, mirror_image_list(&ioctx, "", 3, &mirror_image_ids));
+ expected_mirror_image_ids = {};
+ ASSERT_EQ(expected_mirror_image_ids, mirror_image_ids);
+}
+
+TEST_F(TestClsRbd, mirror_image_status) {
+ struct WatchCtx : public librados::WatchCtx2 {
+ librados::IoCtx *m_ioctx;
+
+ explicit WatchCtx(librados::IoCtx *ioctx) : m_ioctx(ioctx) {}
+ void handle_notify(uint64_t notify_id, uint64_t cookie,
+ uint64_t notifier_id, bufferlist& bl_) override {
+ bufferlist bl;
+ m_ioctx->notify_ack(RBD_MIRRORING, notify_id, cookie, bl);
+ }
+ void handle_error(uint64_t cookie, int err) override {}
+ };
+
+ map<std::string, cls::rbd::MirrorImage> images;
+ map<std::string, cls::rbd::MirrorImageStatus> statuses;
+ std::map<cls::rbd::MirrorImageStatusState, int32_t> states;
+ std::map<std::string, entity_inst_t> instances;
+ cls::rbd::MirrorImageStatus read_status;
+ entity_inst_t read_instance;
+ uint64_t watch_handle;
+ librados::IoCtx ioctx;
+
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_MIRRORING);
+
+ int64_t instance_id = librados::Rados(ioctx).get_instance_id();
+
+ // Test list fails on nonexistent RBD_MIRRORING object
+
+ ASSERT_EQ(-ENOENT, mirror_image_status_list(&ioctx, "", 1024, &images,
+ &statuses));
+
+ // Test status set
+
+ cls::rbd::MirrorImage image1(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid1",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED);
+ cls::rbd::MirrorImage image2(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid2",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED);
+ cls::rbd::MirrorImage image3(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid3",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED);
+
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id1", image1));
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id2", image2));
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id3", image3));
+
+ cls::rbd::MirrorImageSiteStatus status1(
+ "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN, "");
+ cls::rbd::MirrorImageSiteStatus status2(
+ "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "");
+ cls::rbd::MirrorImageSiteStatus status3(
+ "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR, "");
+
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid1", status1));
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, "", 1024, &images, &statuses));
+ ASSERT_EQ(3U, images.size());
+ ASSERT_EQ(1U, statuses.size());
+
+ // Test status is down due to RBD_MIRRORING is not watched
+
+ status1.up = false;
+ ASSERT_EQ(statuses["image_id1"], cls::rbd::MirrorImageStatus{{status1}});
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+ ASSERT_EQ(read_status, cls::rbd::MirrorImageStatus{{status1}});
+
+ // Test status summary. All statuses are unknown due to down.
+ states.clear();
+ cls::rbd::MirrorPeer mirror_peer{
+ "uuid", cls::rbd::MIRROR_PEER_DIRECTION_RX, "siteA", "client", "fsidA"};
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(1U, states.size());
+ ASSERT_EQ(3, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN]);
+
+ // Test get instance return -ESTALE due to down.
+
+ ASSERT_EQ(-ESTALE, mirror_image_instance_get(&ioctx, "uuid1", &read_instance));
+ instances.clear();
+ ASSERT_EQ(0, mirror_image_instance_list(&ioctx, "", 1024, &instances));
+ ASSERT_TRUE(instances.empty());
+
+ // Test remove_down removes stale statuses
+
+ ASSERT_EQ(0, mirror_image_status_remove_down(&ioctx));
+ ASSERT_EQ(-ENOENT, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+ ASSERT_EQ(-ENOENT, mirror_image_instance_get(&ioctx, "uuid1", &read_instance));
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, "", 1024, &images, &statuses));
+ ASSERT_EQ(3U, images.size());
+ ASSERT_TRUE(statuses.empty());
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(1U, states.size());
+ ASSERT_EQ(3, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN]);
+
+ // Test remove of status
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid1", status1));
+ ASSERT_EQ(0, mirror_image_status_remove(&ioctx, "uuid1"));
+ ASSERT_EQ(-ENOENT, mirror_image_instance_get(&ioctx, "uuid1", &read_instance));
+
+ // Test statuses are not down after watcher is started
+
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid1", status1));
+
+ WatchCtx watch_ctx(&ioctx);
+ ASSERT_EQ(0, ioctx.watch2(RBD_MIRRORING, &watch_handle, &watch_ctx));
+
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid2", status2));
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid3", status3));
+
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+ status1.up = true;
+ ASSERT_EQ(read_status, cls::rbd::MirrorImageStatus{{status1}});
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid2", &read_status));
+ status2.up = true;
+ ASSERT_EQ(read_status, cls::rbd::MirrorImageStatus{{status2}});
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid3", &read_status));
+ status3.up = true;
+ ASSERT_EQ(read_status, cls::rbd::MirrorImageStatus{{status3}});
+
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, "", 1024, &images, &statuses));
+ ASSERT_EQ(3U, images.size());
+ ASSERT_EQ(3U, statuses.size());
+ ASSERT_EQ(statuses["image_id1"], cls::rbd::MirrorImageStatus{{status1}});
+ ASSERT_EQ(statuses["image_id2"], cls::rbd::MirrorImageStatus{{status2}});
+ ASSERT_EQ(statuses["image_id3"], cls::rbd::MirrorImageStatus{{status3}});
+
+ read_instance = {};
+ ASSERT_EQ(0, mirror_image_instance_get(&ioctx, "uuid1", &read_instance));
+ ASSERT_EQ(read_instance.name.num(), instance_id);
+ instances.clear();
+ ASSERT_EQ(0, mirror_image_instance_list(&ioctx, "", 1024, &instances));
+ ASSERT_EQ(3U, instances.size());
+ ASSERT_EQ(instances["image_id1"].name.num(), instance_id);
+ ASSERT_EQ(instances["image_id2"].name.num(), instance_id);
+ ASSERT_EQ(instances["image_id3"].name.num(), instance_id);
+
+ ASSERT_EQ(0, mirror_image_status_remove_down(&ioctx));
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+ ASSERT_EQ(read_status, cls::rbd::MirrorImageStatus{{status1}});
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, "", 1024, &images, &statuses));
+ ASSERT_EQ(3U, images.size());
+ ASSERT_EQ(3U, statuses.size());
+ ASSERT_EQ(statuses["image_id1"], cls::rbd::MirrorImageStatus{{status1}});
+ ASSERT_EQ(statuses["image_id2"], cls::rbd::MirrorImageStatus{{status2}});
+ ASSERT_EQ(statuses["image_id3"], cls::rbd::MirrorImageStatus{{status3}});
+
+ states.clear();
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(3U, states.size());
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN]);
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING]);
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR]);
+
+ // Test update
+
+ status1.state = status3.state = cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING;
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid1", status1));
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid3", status3));
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid3", &read_status));
+ ASSERT_EQ(read_status, cls::rbd::MirrorImageStatus{{status3}});
+
+ states.clear();
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(1U, states.size());
+ ASSERT_EQ(3, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING]);
+
+ // Remote status
+
+ ASSERT_EQ(0, mirror_uuid_set(&ioctx, "mirror-uuid"));
+ ASSERT_EQ(0, mirror_mode_set(&ioctx, cls::rbd::MIRROR_MODE_POOL));
+
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+ cls::rbd::MirrorImageStatus expected_status1({status1});
+ ASSERT_EQ(expected_status1, read_status);
+
+ cls::rbd::MirrorImageSiteStatus remote_status1(
+ "fsidA", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "");
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid1", remote_status1));
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+ remote_status1.up = true;
+ expected_status1 = {{status1, remote_status1}};
+ ASSERT_EQ(expected_status1, read_status);
+
+ // summary under different modes
+ cls::rbd::MirrorImageSiteStatus remote_status2(
+ "fsidA", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "");
+ remote_status2.up = true;
+ cls::rbd::MirrorImageSiteStatus remote_status3(
+ "fsidA", cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN, "");
+ remote_status3.up = true;
+
+ status1.state = cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR;
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid1", status1));
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid2", status2));
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid3", status3));
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid2", remote_status2));
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, "uuid3", remote_status3));
+
+ expected_status1 = {{status1, remote_status1}};
+ cls::rbd::MirrorImageStatus expected_status2({status2, remote_status2});
+ cls::rbd::MirrorImageStatus expected_status3({status3, remote_status3});
+
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, "", 1024, &images, &statuses));
+ ASSERT_EQ(3U, images.size());
+ ASSERT_EQ(3U, statuses.size());
+ ASSERT_EQ(statuses["image_id1"], expected_status1);
+ ASSERT_EQ(statuses["image_id2"], expected_status2);
+ ASSERT_EQ(statuses["image_id3"], expected_status3);
+
+ states.clear();
+ mirror_peer.mirror_peer_direction = cls::rbd::MIRROR_PEER_DIRECTION_RX;
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(2U, states.size());
+ ASSERT_EQ(2, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING]);
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR]);
+
+ states.clear();
+ mirror_peer.mirror_peer_direction = cls::rbd::MIRROR_PEER_DIRECTION_TX;
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(2U, states.size());
+ ASSERT_EQ(2, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING]);
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN]);
+
+ states.clear();
+ mirror_peer.mirror_peer_direction = cls::rbd::MIRROR_PEER_DIRECTION_RX_TX;
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(3U, states.size());
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING]);
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN]);
+ ASSERT_EQ(1, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR]);
+
+ // Test statuses are down after removing watcher
+ ioctx.unwatch2(watch_handle);
+
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, "", 1024, &images, &statuses));
+ ASSERT_EQ(3U, images.size());
+ ASSERT_EQ(3U, statuses.size());
+ status1.up = false;
+ remote_status1.up = false;
+ expected_status1 = {{status1, remote_status1}};
+ ASSERT_EQ(statuses["image_id1"], expected_status1);
+ status2.up = false;
+ remote_status2.up = false;
+ expected_status2 = {{status2, remote_status2}};
+ ASSERT_EQ(statuses["image_id2"], expected_status2);
+ status3.up = false;
+ remote_status3.up = false;
+ expected_status3 = {{status3, remote_status3}};
+ ASSERT_EQ(statuses["image_id3"], expected_status3);
+
+ ASSERT_EQ(0, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+ ASSERT_EQ(read_status, expected_status1);
+
+ states.clear();
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(1U, states.size());
+ ASSERT_EQ(3, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN]);
+
+ ASSERT_EQ(-ESTALE, mirror_image_instance_get(&ioctx, "uuid1", &read_instance));
+ instances.clear();
+ ASSERT_EQ(0, mirror_image_instance_list(&ioctx, "", 1024, &instances));
+ ASSERT_TRUE(instances.empty());
+
+ ASSERT_EQ(0, mirror_image_status_remove_down(&ioctx));
+ ASSERT_EQ(-ENOENT, mirror_image_status_get(&ioctx, "uuid1", &read_status));
+
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, "", 1024, &images, &statuses));
+ ASSERT_EQ(3U, images.size());
+ ASSERT_TRUE(statuses.empty());
+
+ states.clear();
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {mirror_peer}, &states));
+ ASSERT_EQ(1U, states.size());
+ ASSERT_EQ(3, states[cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN]);
+
+ // Remove images
+
+ image1.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
+ image2.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
+ image3.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
+
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id1", image1));
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id2", image2));
+ ASSERT_EQ(0, mirror_image_set(&ioctx, "image_id3", image3));
+
+ ASSERT_EQ(0, mirror_image_remove(&ioctx, "image_id1"));
+ ASSERT_EQ(0, mirror_image_remove(&ioctx, "image_id2"));
+ ASSERT_EQ(0, mirror_image_remove(&ioctx, "image_id3"));
+
+ states.clear();
+ ASSERT_EQ(0, mirror_image_status_get_summary(&ioctx, {}, &states));
+ ASSERT_EQ(0U, states.size());
+
+ // Test status list with large number of images
+
+ size_t N = 1024;
+ ASSERT_EQ(0U, N % 2);
+
+ for (size_t i = 0; i < N; i++) {
+ std::string id = "id" + stringify(i);
+ std::string uuid = "uuid" + stringify(i);
+ cls::rbd::MirrorImage image(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, uuid,
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED);
+ cls::rbd::MirrorImageSiteStatus status(
+ "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN, "");
+ ASSERT_EQ(0, mirror_image_set(&ioctx, id, image));
+ ASSERT_EQ(0, mirror_image_status_set(&ioctx, uuid, status));
+ }
+
+ std::string last_read = "";
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, last_read, N * 2, &images,
+ &statuses));
+ ASSERT_EQ(N, images.size());
+ ASSERT_EQ(N, statuses.size());
+
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, last_read, N / 2, &images,
+ &statuses));
+ ASSERT_EQ(N / 2, images.size());
+ ASSERT_EQ(N / 2, statuses.size());
+
+ last_read = images.rbegin()->first;
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, last_read, N / 2, &images,
+ &statuses));
+ ASSERT_EQ(N / 2, images.size());
+ ASSERT_EQ(N / 2, statuses.size());
+
+ last_read = images.rbegin()->first;
+ images.clear();
+ statuses.clear();
+ ASSERT_EQ(0, mirror_image_status_list(&ioctx, last_read, N / 2, &images,
+ &statuses));
+ ASSERT_EQ(0U, images.size());
+ ASSERT_EQ(0U, statuses.size());
+}
+
+TEST_F(TestClsRbd, mirror_image_map)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_MIRRORING);
+
+ std::map<std::string, cls::rbd::MirrorImageMap> image_mapping;
+ ASSERT_EQ(-ENOENT, mirror_image_map_list(&ioctx, "", 0, &image_mapping));
+
+ utime_t expected_time = ceph_clock_now();
+
+ bufferlist expected_data;
+ expected_data.append("test");
+
+ std::map<std::string, cls::rbd::MirrorImageMap> expected_image_mapping;
+ while (expected_image_mapping.size() < 1024) {
+ librados::ObjectWriteOperation op;
+ for (uint32_t i = 0; i < 32; ++i) {
+ std::string global_image_id{stringify(expected_image_mapping.size())};
+ cls::rbd::MirrorImageMap mirror_image_map{
+ stringify(i), expected_time, expected_data};
+ expected_image_mapping.emplace(global_image_id, mirror_image_map);
+
+ mirror_image_map_update(&op, global_image_id, mirror_image_map);
+ }
+ ASSERT_EQ(0, ioctx.operate(RBD_MIRRORING, &op));
+ }
+
+ ASSERT_EQ(0, mirror_image_map_list(&ioctx, "", 1000, &image_mapping));
+ ASSERT_EQ(1000U, image_mapping.size());
+
+ ASSERT_EQ(0, mirror_image_map_list(&ioctx, image_mapping.rbegin()->first,
+ 1000, &image_mapping));
+ ASSERT_EQ(24U, image_mapping.size());
+
+ const auto& image_map = *image_mapping.begin();
+ ASSERT_EQ("978", image_map.first);
+
+ cls::rbd::MirrorImageMap expected_mirror_image_map{
+ stringify(18), expected_time, expected_data};
+ ASSERT_EQ(expected_mirror_image_map, image_map.second);
+
+ expected_time = ceph_clock_now();
+ expected_mirror_image_map.mapped_time = expected_time;
+
+ expected_data.append("update");
+ expected_mirror_image_map.data = expected_data;
+
+ librados::ObjectWriteOperation op;
+ mirror_image_map_remove(&op, "1");
+ mirror_image_map_update(&op, "10", expected_mirror_image_map);
+ ASSERT_EQ(0, ioctx.operate(RBD_MIRRORING, &op));
+
+ ASSERT_EQ(0, mirror_image_map_list(&ioctx, "0", 1, &image_mapping));
+ ASSERT_EQ(1U, image_mapping.size());
+
+ const auto& updated_image_map = *image_mapping.begin();
+ ASSERT_EQ("10", updated_image_map.first);
+ ASSERT_EQ(expected_mirror_image_map, updated_image_map.second);
+}
+
+TEST_F(TestClsRbd, mirror_instances) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_MIRROR_LEADER);
+
+ std::vector<std::string> instance_ids;
+ ASSERT_EQ(-ENOENT, mirror_instances_list(&ioctx, &instance_ids));
+
+ ASSERT_EQ(0, ioctx.create(RBD_MIRROR_LEADER, true));
+ ASSERT_EQ(0, mirror_instances_list(&ioctx, &instance_ids));
+ ASSERT_EQ(0U, instance_ids.size());
+
+ ASSERT_EQ(0, mirror_instances_add(&ioctx, "instance_id1"));
+ ASSERT_EQ(0, mirror_instances_list(&ioctx, &instance_ids));
+ ASSERT_EQ(1U, instance_ids.size());
+ ASSERT_EQ(instance_ids[0], "instance_id1");
+
+ ASSERT_EQ(0, mirror_instances_add(&ioctx, "instance_id1"));
+ ASSERT_EQ(0, mirror_instances_add(&ioctx, "instance_id2"));
+ ASSERT_EQ(0, mirror_instances_list(&ioctx, &instance_ids));
+ ASSERT_EQ(2U, instance_ids.size());
+
+ ASSERT_EQ(0, mirror_instances_remove(&ioctx, "instance_id1"));
+ ASSERT_EQ(0, mirror_instances_list(&ioctx, &instance_ids));
+ ASSERT_EQ(1U, instance_ids.size());
+ ASSERT_EQ(instance_ids[0], "instance_id2");
+
+ ASSERT_EQ(0, mirror_instances_remove(&ioctx, "instance_id2"));
+ ASSERT_EQ(0, mirror_instances_list(&ioctx, &instance_ids));
+ ASSERT_EQ(0U, instance_ids.size());
+}
+
+TEST_F(TestClsRbd, mirror_snapshot) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 10, 22, 0, oid, -1));
+
+ cls::rbd::MirrorSnapshotNamespace primary = {
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer1", "peer2"}, "",
+ CEPH_NOSNAP};
+ cls::rbd::MirrorSnapshotNamespace non_primary = {
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {"peer1"}, "uuid", 123};
+ librados::ObjectWriteOperation op;
+ ::librbd::cls_client::snapshot_add(&op, 1, "primary", primary);
+ ::librbd::cls_client::snapshot_add(&op, 2, "non_primary", non_primary);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ cls::rbd::SnapshotInfo snap;
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 1, &snap));
+ auto sn = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &snap.snapshot_namespace);
+ ASSERT_NE(nullptr, sn);
+ ASSERT_EQ(primary, *sn);
+ ASSERT_EQ(2U, sn->mirror_peer_uuids.size());
+ ASSERT_EQ(1U, sn->mirror_peer_uuids.count("peer1"));
+ ASSERT_EQ(1U, sn->mirror_peer_uuids.count("peer2"));
+
+ ASSERT_EQ(-ENOENT, mirror_image_snapshot_unlink_peer(&ioctx, oid, 1, "peer"));
+ ASSERT_EQ(0, mirror_image_snapshot_unlink_peer(&ioctx, oid, 1, "peer1"));
+ ASSERT_EQ(-ENOENT, mirror_image_snapshot_unlink_peer(&ioctx, oid, 1,
+ "peer1"));
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 1, &snap));
+ sn = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &snap.snapshot_namespace);
+ ASSERT_NE(nullptr, sn);
+ ASSERT_EQ(1U, sn->mirror_peer_uuids.size());
+ ASSERT_EQ(1U, sn->mirror_peer_uuids.count("peer2"));
+
+ ASSERT_EQ(0, mirror_image_snapshot_unlink_peer(&ioctx, oid, 1, "peer2"));
+ ASSERT_EQ(-ENOENT, mirror_image_snapshot_unlink_peer(&ioctx, oid, 1,
+ "peer2"));
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 1, &snap));
+ sn = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &snap.snapshot_namespace);
+ ASSERT_NE(nullptr, sn);
+ ASSERT_EQ(0U, sn->mirror_peer_uuids.size());
+
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 2, &snap));
+ auto nsn = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &snap.snapshot_namespace);
+ ASSERT_NE(nullptr, nsn);
+ ASSERT_EQ(non_primary, *nsn);
+ ASSERT_EQ(1U, nsn->mirror_peer_uuids.size());
+ ASSERT_EQ(1U, nsn->mirror_peer_uuids.count("peer1"));
+ ASSERT_FALSE(nsn->complete);
+ ASSERT_EQ(nsn->last_copied_object_number, 0);
+
+ ASSERT_EQ(0, mirror_image_snapshot_set_copy_progress(&ioctx, oid, 2, true,
+ 10));
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 2, &snap));
+ nsn = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &snap.snapshot_namespace);
+ ASSERT_NE(nullptr, nsn);
+ ASSERT_TRUE(nsn->complete);
+ ASSERT_EQ(nsn->last_copied_object_number, 10);
+
+ ASSERT_EQ(0, mirror_image_snapshot_unlink_peer(&ioctx, oid, 2, "peer1"));
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 1));
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 2));
+}
+
+TEST_F(TestClsRbd, group_dir_list) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id1 = "cgid1";
+ string group_name1 = "cgname1";
+ string group_id2 = "cgid2";
+ string group_name2 = "cgname2";
+ ASSERT_EQ(0, group_dir_add(&ioctx, RBD_GROUP_DIRECTORY, group_name1, group_id1));
+ ASSERT_EQ(0, group_dir_add(&ioctx, RBD_GROUP_DIRECTORY, group_name2, group_id2));
+
+ map<string, string> cgs;
+ ASSERT_EQ(0, group_dir_list(&ioctx, RBD_GROUP_DIRECTORY, "", 10, &cgs));
+
+ ASSERT_EQ(2U, cgs.size());
+
+ auto it = cgs.begin();
+ ASSERT_EQ(group_id1, it->second);
+ ASSERT_EQ(group_name1, it->first);
+
+ ++it;
+ ASSERT_EQ(group_id2, it->second);
+ ASSERT_EQ(group_name2, it->first);
+}
+
+void add_group_to_dir(librados::IoCtx ioctx, string group_id, string group_name) {
+ ASSERT_EQ(0, group_dir_add(&ioctx, RBD_GROUP_DIRECTORY, group_name, group_id));
+
+ set<string> keys;
+ ASSERT_EQ(0, ioctx.omap_get_keys(RBD_GROUP_DIRECTORY, "", 10, &keys));
+ ASSERT_EQ(2U, keys.size());
+ ASSERT_EQ("id_" + group_id, *keys.begin());
+ ASSERT_EQ("name_" + group_name, *keys.rbegin());
+}
+
+TEST_F(TestClsRbd, group_dir_add) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_GROUP_DIRECTORY);
+
+ string group_id = "cgid";
+ string group_name = "cgname";
+ add_group_to_dir(ioctx, group_id, group_name);
+}
+
+TEST_F(TestClsRbd, dir_add_already_existing) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_GROUP_DIRECTORY);
+
+ string group_id = "cgidexisting";
+ string group_name = "cgnameexisting";
+ add_group_to_dir(ioctx, group_id, group_name);
+
+ ASSERT_EQ(-EEXIST, group_dir_add(&ioctx, RBD_GROUP_DIRECTORY, group_name, group_id));
+}
+
+TEST_F(TestClsRbd, group_dir_rename) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_GROUP_DIRECTORY);
+
+ string group_id = "cgid";
+ string src_name = "cgnamesrc";
+ string dest_name = "cgnamedest";
+ add_group_to_dir(ioctx, group_id, src_name);
+
+ ASSERT_EQ(0, group_dir_rename(&ioctx, RBD_GROUP_DIRECTORY,
+ src_name, dest_name, group_id));
+ map<string, string> cgs;
+ ASSERT_EQ(0, group_dir_list(&ioctx, RBD_GROUP_DIRECTORY, "", 10, &cgs));
+ ASSERT_EQ(1U, cgs.size());
+ auto it = cgs.begin();
+ ASSERT_EQ(group_id, it->second);
+ ASSERT_EQ(dest_name, it->first);
+
+ // destination group name existing
+ ASSERT_EQ(-EEXIST, group_dir_rename(&ioctx, RBD_GROUP_DIRECTORY,
+ dest_name, dest_name, group_id));
+ ASSERT_EQ(0, group_dir_remove(&ioctx, RBD_GROUP_DIRECTORY, dest_name, group_id));
+ // source group name missing
+ ASSERT_EQ(-ENOENT, group_dir_rename(&ioctx, RBD_GROUP_DIRECTORY,
+ dest_name, src_name, group_id));
+}
+
+TEST_F(TestClsRbd, group_dir_remove) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_GROUP_DIRECTORY);
+
+ string group_id = "cgidtodel";
+ string group_name = "cgnametodel";
+ add_group_to_dir(ioctx, group_id, group_name);
+
+ ASSERT_EQ(0, group_dir_remove(&ioctx, RBD_GROUP_DIRECTORY, group_name, group_id));
+
+ set<string> keys;
+ ASSERT_EQ(0, ioctx.omap_get_keys(RBD_GROUP_DIRECTORY, "", 10, &keys));
+ ASSERT_EQ(0U, keys.size());
+}
+
+TEST_F(TestClsRbd, group_dir_remove_missing) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_GROUP_DIRECTORY);
+
+ string group_id = "cgidtodelmissing";
+ string group_name = "cgnametodelmissing";
+ // These two lines ensure that RBD_GROUP_DIRECTORY exists. It's important for the
+ // last two lines.
+ add_group_to_dir(ioctx, group_id, group_name);
+
+ ASSERT_EQ(0, group_dir_remove(&ioctx, RBD_GROUP_DIRECTORY, group_name, group_id));
+
+ // Removing missing
+ ASSERT_EQ(-ENOENT, group_dir_remove(&ioctx, RBD_GROUP_DIRECTORY, group_name, group_id));
+
+ set<string> keys;
+ ASSERT_EQ(0, ioctx.omap_get_keys(RBD_GROUP_DIRECTORY, "", 10, &keys));
+ ASSERT_EQ(0U, keys.size());
+}
+
+void test_image_add(librados::IoCtx &ioctx, const string& group_id,
+ const string& image_id, int64_t pool_id) {
+
+ cls::rbd::GroupImageStatus st(image_id, pool_id,
+ cls::rbd::GROUP_IMAGE_LINK_STATE_INCOMPLETE);
+ ASSERT_EQ(0, group_image_set(&ioctx, group_id, st));
+
+ set<string> keys;
+ ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+
+ auto it = keys.begin();
+ ASSERT_EQ(1U, keys.size());
+
+ string image_key = cls::rbd::GroupImageSpec(image_id, pool_id).image_key();
+ ASSERT_EQ(image_key, *it);
+}
+
+TEST_F(TestClsRbd, group_image_add) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ int64_t pool_id = ioctx.get_id();
+ string image_id = "image_id";
+ test_image_add(ioctx, group_id, image_id, pool_id);
+}
+
+TEST_F(TestClsRbd, group_image_remove) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id1";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ int64_t pool_id = ioctx.get_id();
+ string image_id = "image_id";
+ test_image_add(ioctx, group_id, image_id, pool_id);
+
+ cls::rbd::GroupImageSpec spec(image_id, pool_id);
+ ASSERT_EQ(0, group_image_remove(&ioctx, group_id, spec));
+ set<string> keys;
+ ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+ ASSERT_EQ(0U, keys.size());
+}
+
+TEST_F(TestClsRbd, group_image_list) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id2";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ int64_t pool_id = ioctx.get_id();
+ string image_id = "imageid"; // Image id shouldn't contain underscores
+ test_image_add(ioctx, group_id, image_id, pool_id);
+
+ vector<cls::rbd::GroupImageStatus> images;
+ cls::rbd::GroupImageSpec empty_image_spec = cls::rbd::GroupImageSpec();
+ ASSERT_EQ(0, group_image_list(&ioctx, group_id, empty_image_spec, 1024,
+ &images));
+ ASSERT_EQ(1U, images.size());
+ ASSERT_EQ(image_id, images[0].spec.image_id);
+ ASSERT_EQ(pool_id, images[0].spec.pool_id);
+ ASSERT_EQ(cls::rbd::GROUP_IMAGE_LINK_STATE_INCOMPLETE, images[0].state);
+
+ cls::rbd::GroupImageStatus last_image = *images.rbegin();
+ ASSERT_EQ(0, group_image_list(&ioctx, group_id, last_image.spec, 1024,
+ &images));
+ ASSERT_EQ(0U, images.size());
+}
+
+TEST_F(TestClsRbd, group_image_clean) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id3";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ int64_t pool_id = ioctx.get_id();
+ string image_id = "image_id";
+ test_image_add(ioctx, group_id, image_id, pool_id);
+
+ cls::rbd::GroupImageStatus incomplete_st(image_id, pool_id,
+ cls::rbd::GROUP_IMAGE_LINK_STATE_INCOMPLETE);
+
+ ASSERT_EQ(0, group_image_set(&ioctx, group_id, incomplete_st));
+ // Set to dirty first in order to make sure that group_image_clean
+ // actually does something.
+ cls::rbd::GroupImageStatus attached_st(image_id, pool_id,
+ cls::rbd::GROUP_IMAGE_LINK_STATE_ATTACHED);
+ ASSERT_EQ(0, group_image_set(&ioctx, group_id, attached_st));
+
+ string image_key = cls::rbd::GroupImageSpec(image_id, pool_id).image_key();
+
+ map<string, bufferlist> vals;
+ ASSERT_EQ(0, ioctx.omap_get_vals(group_id, "", 10, &vals));
+
+ cls::rbd::GroupImageLinkState ref_state;
+ auto it = vals[image_key].cbegin();
+ decode(ref_state, it);
+ ASSERT_EQ(cls::rbd::GROUP_IMAGE_LINK_STATE_ATTACHED, ref_state);
+}
+
+TEST_F(TestClsRbd, image_group_add) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ int64_t pool_id = ioctx.get_id();
+ string image_id = "imageid";
+
+ ASSERT_EQ(0, create_image(&ioctx, image_id, 2<<20, 0,
+ RBD_FEATURE_LAYERING, image_id, -1));
+
+ string group_id = "group_id";
+
+ cls::rbd::GroupSpec spec(group_id, pool_id);
+ ASSERT_EQ(0, image_group_add(&ioctx, image_id, spec));
+
+ map<string, bufferlist> vals;
+ ASSERT_EQ(0, ioctx.omap_get_vals(image_id, "", RBD_GROUP_REF, 10, &vals));
+
+ cls::rbd::GroupSpec val_spec;
+ auto it = vals[RBD_GROUP_REF].cbegin();
+ decode(val_spec, it);
+
+ ASSERT_EQ(group_id, val_spec.group_id);
+ ASSERT_EQ(pool_id, val_spec.pool_id);
+}
+
+TEST_F(TestClsRbd, image_group_remove) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ int64_t pool_id = ioctx.get_id();
+ string image_id = "image_id";
+
+ ASSERT_EQ(0, create_image(&ioctx, image_id, 2<<20, 0,
+ RBD_FEATURE_LAYERING, image_id, -1));
+
+ string group_id = "group_id";
+
+ cls::rbd::GroupSpec spec(group_id, pool_id);
+ ASSERT_EQ(0, image_group_add(&ioctx, image_id, spec));
+ // Add reference in order to make sure that image_group_remove actually
+ // does something.
+ ASSERT_EQ(0, image_group_remove(&ioctx, image_id, spec));
+
+ map<string, bufferlist> vals;
+ ASSERT_EQ(0, ioctx.omap_get_vals(image_id, "", RBD_GROUP_REF, 10, &vals));
+
+ ASSERT_EQ(0U, vals.size());
+}
+
+TEST_F(TestClsRbd, image_group_get) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ int64_t pool_id = ioctx.get_id();
+ string image_id = "imageidgroupspec";
+
+ ASSERT_EQ(0, create_image(&ioctx, image_id, 2<<20, 0,
+ RBD_FEATURE_LAYERING, image_id, -1));
+
+ string group_id = "group_id_get_group_spec";
+
+ cls::rbd::GroupSpec spec_add(group_id, pool_id);
+ ASSERT_EQ(0, image_group_add(&ioctx, image_id, spec_add));
+
+ cls::rbd::GroupSpec spec;
+ ASSERT_EQ(0, image_group_get(&ioctx, image_id, &spec));
+
+ ASSERT_EQ(group_id, spec.group_id);
+ ASSERT_EQ(pool_id, spec.pool_id);
+}
+
+TEST_F(TestClsRbd, group_snap_set_empty_name) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_add_empty_name";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id = "snap_id";
+ cls::rbd::GroupSnapshot snap = {snap_id, "", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(-EINVAL, group_snap_set(&ioctx, group_id, snap));
+}
+
+TEST_F(TestClsRbd, group_snap_set_empty_id) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_add_empty_id";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id = "snap_id";
+ cls::rbd::GroupSnapshot snap = {"", "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(-EINVAL, group_snap_set(&ioctx, group_id, snap));
+}
+
+TEST_F(TestClsRbd, group_snap_set_duplicate_id) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_add_duplicate_id";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id = "snap_id";
+ cls::rbd::GroupSnapshot snap = {snap_id, "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap));
+
+ cls::rbd::GroupSnapshot snap1 = {snap_id, "snap_name1", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(-EEXIST, group_snap_set(&ioctx, group_id, snap1));
+}
+
+TEST_F(TestClsRbd, group_snap_set_duplicate_name) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_add_duplicate_name";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id1 = "snap_id1";
+ cls::rbd::GroupSnapshot snap = {snap_id1, "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap));
+
+ string snap_id2 = "snap_id2";
+ cls::rbd::GroupSnapshot snap1 = {snap_id2, "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(-EEXIST, group_snap_set(&ioctx, group_id, snap1));
+}
+
+TEST_F(TestClsRbd, group_snap_set) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_add";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id = "snap_id";
+ cls::rbd::GroupSnapshot snap = {snap_id, "test_snapshot", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap));
+
+ set<string> keys;
+ ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+
+ auto it = keys.begin();
+ ASSERT_EQ(1U, keys.size());
+
+ string snap_key = "snapshot_" + stringify(snap.id);
+ ASSERT_EQ(snap_key, *it);
+}
+
+TEST_F(TestClsRbd, group_snap_list) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_list";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id1 = "snap_id1";
+ cls::rbd::GroupSnapshot snap1 = {snap_id1, "test_snapshot1", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap1));
+
+ string snap_id2 = "snap_id2";
+ cls::rbd::GroupSnapshot snap2 = {snap_id2, "test_snapshot2", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap2));
+
+ std::vector<cls::rbd::GroupSnapshot> snapshots;
+ ASSERT_EQ(0, group_snap_list(&ioctx, group_id, cls::rbd::GroupSnapshot(), 10, &snapshots));
+ ASSERT_EQ(2U, snapshots.size());
+ ASSERT_EQ(snap_id1, snapshots[0].id);
+ ASSERT_EQ(snap_id2, snapshots[1].id);
+}
+
+static std::string hexify(int v) {
+ ostringstream oss;
+ oss << std::setw(8) << std::setfill('0') << std::hex << v;
+ //oss << v;
+ return oss.str();
+}
+
+TEST_F(TestClsRbd, group_snap_list_max_return) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_list_max_return";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ for (int i = 0; i < 15; ++i) {
+ string snap_id = "snap_id" + hexify(i);
+ cls::rbd::GroupSnapshot snap = {snap_id,
+ "test_snapshot" + hexify(i),
+ cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap));
+ }
+
+ std::vector<cls::rbd::GroupSnapshot> snapshots;
+ ASSERT_EQ(0, group_snap_list(&ioctx, group_id, cls::rbd::GroupSnapshot(), 10, &snapshots));
+ ASSERT_EQ(10U, snapshots.size());
+
+ for (int i = 0; i < 10; ++i) {
+ string snap_id = "snap_id" + hexify(i);
+ ASSERT_EQ(snap_id, snapshots[i].id);
+ }
+
+ cls::rbd::GroupSnapshot last_snap = *snapshots.rbegin();
+
+ ASSERT_EQ(0, group_snap_list(&ioctx, group_id, last_snap, 10, &snapshots));
+ ASSERT_EQ(5U, snapshots.size());
+ for (int i = 10; i < 15; ++i) {
+ string snap_id = "snap_id" + hexify(i);
+ ASSERT_EQ(snap_id, snapshots[i - 10].id);
+ }
+}
+
+TEST_F(TestClsRbd, group_snap_list_max_read) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_list_max_read";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ // 2 * RBD_MAX_KEYS_READ + a few
+ for (int i = 0; i < 150; ++i) {
+ string snap_id = "snap_id" + hexify(i);
+ cls::rbd::GroupSnapshot snap = {snap_id,
+ "test_snapshot" + hexify(i),
+ cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap));
+ }
+
+ std::vector<cls::rbd::GroupSnapshot> snapshots;
+ ASSERT_EQ(0, group_snap_list(&ioctx, group_id, cls::rbd::GroupSnapshot(), 500, &snapshots));
+ ASSERT_EQ(150U, snapshots.size());
+
+ for (int i = 0; i < 150; ++i) {
+ string snap_id = "snap_id" + hexify(i);
+ ASSERT_EQ(snap_id, snapshots[i].id);
+ }
+}
+
+TEST_F(TestClsRbd, group_snap_remove) {
+ librados::IoCtx ioctx;
+
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_remove";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id = "snap_id";
+ cls::rbd::GroupSnapshot snap = {snap_id, "test_snapshot", cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap));
+
+ set<string> keys;
+ ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+
+ auto it = keys.begin();
+ ASSERT_EQ(1U, keys.size());
+
+ string snap_key = "snapshot_" + stringify(snap.id);
+ ASSERT_EQ(snap_key, *it);
+
+ // Remove the snapshot
+
+ ASSERT_EQ(0, group_snap_remove(&ioctx, group_id, snap_id));
+
+ ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+
+ ASSERT_EQ(0U, keys.size());
+}
+
+TEST_F(TestClsRbd, group_snap_get_by_id) {
+ librados::IoCtx ioctx;
+
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string group_id = "group_id_snap_get_by_id";
+ ASSERT_EQ(0, ioctx.create(group_id, true));
+
+ string snap_id = "snap_id";
+ cls::rbd::GroupSnapshot snap = {snap_id,
+ "test_snapshot",
+ cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE};
+ ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap));
+
+ cls::rbd::GroupSnapshot received_snap;
+ ASSERT_EQ(0, group_snap_get_by_id(&ioctx, group_id, snap_id, &received_snap));
+
+ ASSERT_EQ(snap.id, received_snap.id);
+ ASSERT_EQ(snap.name, received_snap.name);
+ ASSERT_EQ(snap.state, received_snap.state);
+}
+
+TEST_F(TestClsRbd, trash_methods)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string id = "123456789";
+ string id2 = "123456780";
+
+ std::map<string, cls::rbd::TrashImageSpec> entries;
+ ASSERT_EQ(-ENOENT, trash_list(&ioctx, "", 1024, &entries));
+
+ utime_t now1 = ceph_clock_now();
+ utime_t now1_delay = now1;
+ now1_delay += 380;
+ cls::rbd::TrashImageSpec trash_spec(cls::rbd::TRASH_IMAGE_SOURCE_USER, "name",
+ now1, now1_delay);
+ ASSERT_EQ(0, trash_add(&ioctx, id, trash_spec));
+
+ utime_t now2 = ceph_clock_now();
+ utime_t now2_delay = now2;
+ now2_delay += 480;
+ cls::rbd::TrashImageSpec trash_spec2(cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING,
+ "name2", now2, now2_delay);
+ ASSERT_EQ(-EEXIST, trash_add(&ioctx, id, trash_spec2));
+
+ ASSERT_EQ(0, trash_remove(&ioctx, id));
+ ASSERT_EQ(-ENOENT, trash_remove(&ioctx, id));
+
+ ASSERT_EQ(0, trash_list(&ioctx, "", 1024, &entries));
+ ASSERT_TRUE(entries.empty());
+
+ ASSERT_EQ(0, trash_add(&ioctx, id, trash_spec2));
+ ASSERT_EQ(0, trash_add(&ioctx, id2, trash_spec));
+
+ ASSERT_EQ(0, trash_list(&ioctx, "", 1, &entries));
+ ASSERT_TRUE(entries.find(id2) != entries.end());
+ ASSERT_EQ(cls::rbd::TRASH_IMAGE_SOURCE_USER, entries[id2].source);
+ ASSERT_EQ(std::string("name"), entries[id2].name);
+ ASSERT_EQ(now1, entries[id2].deletion_time);
+ ASSERT_EQ(now1_delay, entries[id2].deferment_end_time);
+
+ ASSERT_EQ(0, trash_list(&ioctx, id2, 1, &entries));
+ ASSERT_TRUE(entries.find(id) != entries.end());
+ ASSERT_EQ(cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, entries[id].source);
+ ASSERT_EQ(std::string("name2"), entries[id].name);
+ ASSERT_EQ(now2, entries[id].deletion_time);
+ ASSERT_EQ(now2_delay, entries[id].deferment_end_time);
+
+ ASSERT_EQ(0, trash_list(&ioctx, id, 1, &entries));
+ ASSERT_TRUE(entries.empty());
+
+ cls::rbd::TrashImageSpec spec_res1;
+ ASSERT_EQ(0, trash_get(&ioctx, id, &spec_res1));
+ cls::rbd::TrashImageSpec spec_res2;
+ ASSERT_EQ(0, trash_get(&ioctx, id2, &spec_res2));
+
+ ASSERT_EQ(spec_res1.name, "name2");
+ ASSERT_EQ(spec_res1.deletion_time, now2);
+ ASSERT_EQ(spec_res1.deferment_end_time, now2_delay);
+
+ ASSERT_EQ(spec_res2.name, "name");
+ ASSERT_EQ(spec_res2.deletion_time, now1);
+ ASSERT_EQ(spec_res2.deferment_end_time, now1_delay);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, op_features)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+ uint64_t op_features = RBD_OPERATION_FEATURE_CLONE_PARENT;
+ uint64_t mask = ~RBD_OPERATION_FEATURES_ALL;
+ ASSERT_EQ(-EINVAL, op_features_set(&ioctx, oid, op_features, mask));
+
+ mask = 0;
+ ASSERT_EQ(0, op_features_set(&ioctx, oid, op_features, mask));
+
+ uint64_t actual_op_features;
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &actual_op_features));
+ ASSERT_EQ(0u, actual_op_features);
+
+ uint64_t features;
+ uint64_t incompatible_features;
+ ASSERT_EQ(0, get_features(&ioctx, oid, true, &features,
+ &incompatible_features));
+ ASSERT_EQ(0u, features);
+
+ op_features = RBD_OPERATION_FEATURES_ALL;
+ mask = RBD_OPERATION_FEATURES_ALL;
+ ASSERT_EQ(0, op_features_set(&ioctx, oid, op_features, mask));
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &actual_op_features));
+ ASSERT_EQ(mask, actual_op_features);
+
+ ASSERT_EQ(0, get_features(&ioctx, oid, true, &features,
+ &incompatible_features));
+ ASSERT_EQ(RBD_FEATURE_OPERATIONS, features);
+
+ op_features = 0;
+ mask = RBD_OPERATION_FEATURE_CLONE_PARENT;
+ ASSERT_EQ(0, op_features_set(&ioctx, oid, op_features, mask));
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &actual_op_features));
+
+ uint64_t expected_op_features = RBD_OPERATION_FEATURES_ALL &
+ ~RBD_OPERATION_FEATURE_CLONE_PARENT;
+ ASSERT_EQ(expected_op_features, actual_op_features);
+
+ mask = RBD_OPERATION_FEATURES_ALL;
+ ASSERT_EQ(0, op_features_set(&ioctx, oid, op_features, mask));
+ ASSERT_EQ(0, get_features(&ioctx, oid, true, &features,
+ &incompatible_features));
+ ASSERT_EQ(0u, features);
+}
+
+TEST_F(TestClsRbd, clone_parent)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 123, "user_snap"));
+
+ ASSERT_EQ(-ENOENT, child_attach(&ioctx, oid, 345, {}));
+ ASSERT_EQ(-ENOENT, child_detach(&ioctx, oid, 123, {}));
+ ASSERT_EQ(-ENOENT, child_detach(&ioctx, oid, 345, {}));
+
+ ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {1, "", "image1"}));
+ ASSERT_EQ(-EEXIST, child_attach(&ioctx, oid, 123, {1, "", "image1"}));
+ ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {1, "", "image2"}));
+ ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {2, "", "image2"}));
+
+ cls::rbd::SnapshotInfo snap;
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 123, &snap));
+ ASSERT_EQ(3U, snap.child_count);
+
+ // op feature should have been enabled
+ uint64_t op_features;
+ uint64_t expected_op_features = RBD_OPERATION_FEATURE_CLONE_PARENT;
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & expected_op_features) == expected_op_features);
+
+ // cannot attach to trashed snapshot
+ librados::ObjectWriteOperation op1;
+ ::librbd::cls_client::snapshot_add(&op1, 234, "trash_snap",
+ cls::rbd::UserSnapshotNamespace());
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+ librados::ObjectWriteOperation op2;
+ ::librbd::cls_client::snapshot_trash_add(&op2, 234);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+ ASSERT_EQ(-ENOENT, child_attach(&ioctx, oid, 234, {}));
+
+ cls::rbd::ChildImageSpecs child_images;
+ ASSERT_EQ(0, children_list(&ioctx, oid, 123, &child_images));
+
+ cls::rbd::ChildImageSpecs expected_child_images = {
+ {1, "", "image1"}, {1, "", "image2"}, {2, "", "image2"}};
+ ASSERT_EQ(expected_child_images, child_images);
+
+ // move snapshot to the trash
+ ASSERT_EQ(-EBUSY, snapshot_remove(&ioctx, oid, 123));
+ librados::ObjectWriteOperation op3;
+ ::librbd::cls_client::snapshot_trash_add(&op3, 123);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+ ASSERT_EQ(0, snapshot_get(&ioctx, oid, 123, &snap));
+ ASSERT_EQ(cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH,
+ cls::rbd::get_snap_namespace_type(snap.snapshot_namespace));
+
+ expected_op_features |= RBD_OPERATION_FEATURE_SNAP_TRASH;
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & expected_op_features) == expected_op_features);
+
+ expected_child_images = {{1, "", "image1"}, {2, "", "image2"}};
+ ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {1, "", "image2"}));
+ ASSERT_EQ(0, children_list(&ioctx, oid, 123, &child_images));
+ ASSERT_EQ(expected_child_images, child_images);
+
+ ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {2, "", "image2"}));
+
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & expected_op_features) == expected_op_features);
+
+ ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {1, "", "image1"}));
+ ASSERT_EQ(-ENOENT, children_list(&ioctx, oid, 123, &child_images));
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 234));
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & expected_op_features) ==
+ RBD_OPERATION_FEATURE_SNAP_TRASH);
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 123));
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & expected_op_features) == 0);
+}
+
+TEST_F(TestClsRbd, clone_parent_ns)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 123, "user_snap"));
+
+ ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {1, "ns1", "image1"}));
+ ASSERT_EQ(-EEXIST, child_attach(&ioctx, oid, 123, {1, "ns1", "image1"}));
+ ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {1, "ns2", "image1"}));
+
+ cls::rbd::ChildImageSpecs child_images;
+ ASSERT_EQ(0, children_list(&ioctx, oid, 123, &child_images));
+
+ cls::rbd::ChildImageSpecs expected_child_images = {
+ {1, "ns1", "image1"}, {1, "ns2", "image1"}};
+ ASSERT_EQ(expected_child_images, child_images);
+
+ expected_child_images = {{1, "ns1", "image1"}};
+ ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {1, "ns2", "image1"}));
+ ASSERT_EQ(0, children_list(&ioctx, oid, 123, &child_images));
+ ASSERT_EQ(expected_child_images, child_images);
+
+ ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {1, "ns1", "image1"}));
+ ASSERT_EQ(-ENOENT, children_list(&ioctx, oid, 123, &child_images));
+
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 123));
+}
+
+TEST_F(TestClsRbd, clone_child)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22,
+ RBD_FEATURE_LAYERING | RBD_FEATURE_DEEP_FLATTEN,
+ oid, -1));
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 2}, 1));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 123, "user_snap1"));
+ ASSERT_EQ(0, op_features_set(&ioctx, oid, RBD_OPERATION_FEATURE_CLONE_CHILD,
+ RBD_OPERATION_FEATURE_CLONE_CHILD));
+
+ // clone child should be disabled due to deep flatten
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+ uint64_t op_features;
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_CHILD) == 0ULL);
+
+ ASSERT_EQ(0, set_features(&ioctx, oid, 0, RBD_FEATURE_DEEP_FLATTEN));
+ ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "", "parent", 2}, 1));
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 124, "user_snap2"));
+ ASSERT_EQ(0, op_features_set(&ioctx, oid, RBD_OPERATION_FEATURE_CLONE_CHILD,
+ RBD_OPERATION_FEATURE_CLONE_CHILD));
+
+ // clone child should remain enabled w/o deep flatten
+ ASSERT_EQ(0, remove_parent(&ioctx, oid));
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_CHILD) ==
+ RBD_OPERATION_FEATURE_CLONE_CHILD);
+
+ // ... but removing the last linked snapshot should disable it
+ ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 124));
+ ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_CHILD) == 0ULL);
+}
+
+TEST_F(TestClsRbd, namespace_methods)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string name1 = "123456789";
+ string name2 = "123456780";
+
+ std::list<std::string> entries;
+ ASSERT_EQ(-ENOENT, namespace_list(&ioctx, "", 1024, &entries));
+
+ ASSERT_EQ(0, namespace_add(&ioctx, name1));
+ ASSERT_EQ(-EEXIST, namespace_add(&ioctx, name1));
+
+ ASSERT_EQ(0, namespace_remove(&ioctx, name1));
+ ASSERT_EQ(-ENOENT, namespace_remove(&ioctx, name1));
+
+ ASSERT_EQ(0, namespace_list(&ioctx, "", 1024, &entries));
+ ASSERT_TRUE(entries.empty());
+
+ ASSERT_EQ(0, namespace_add(&ioctx, name1));
+ ASSERT_EQ(0, namespace_add(&ioctx, name2));
+
+ ASSERT_EQ(0, namespace_list(&ioctx, "", 1, &entries));
+ ASSERT_EQ(1U, entries.size());
+ ASSERT_EQ(name2, entries.front());
+
+ ASSERT_EQ(0, namespace_list(&ioctx, name2, 1, &entries));
+ ASSERT_EQ(1U, entries.size());
+ ASSERT_EQ(name1, entries.front());
+
+ ASSERT_EQ(0, namespace_list(&ioctx, name1, 1, &entries));
+ ASSERT_TRUE(entries.empty());
+}
+
+TEST_F(TestClsRbd, migration)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+ cls::rbd::MigrationSpec migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST,
+ -1, "", "", "",
+ "{\"format\": \"raw\"}", {}, 0, false,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ false,
+ cls::rbd::MIGRATION_STATE_PREPARING,
+ "123");
+ cls::rbd::MigrationSpec read_migration_spec;
+
+ ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+
+ uint64_t features;
+ uint64_t incompatible_features;
+ ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features,
+ &incompatible_features));
+ ASSERT_EQ(0U, features);
+
+ ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
+ ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+ ASSERT_EQ(migration_spec, read_migration_spec);
+
+ ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features,
+ &incompatible_features));
+ ASSERT_EQ(RBD_FEATURE_MIGRATING, features);
+
+ ASSERT_EQ(-EEXIST, migration_set(&ioctx, oid, migration_spec));
+
+ migration_spec.state = cls::rbd::MIGRATION_STATE_PREPARED;
+ migration_spec.state_description = "456";
+ ASSERT_EQ(0, migration_set_state(&ioctx, oid, migration_spec.state,
+ migration_spec.state_description));
+ ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+ ASSERT_EQ(migration_spec, read_migration_spec);
+
+ ASSERT_EQ(0, migration_remove(&ioctx, oid));
+
+ ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features,
+ &incompatible_features));
+ ASSERT_EQ(0U, features);
+
+ ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+ ASSERT_EQ(-EINVAL, migration_set_state(&ioctx, oid, migration_spec.state,
+ migration_spec.state_description));
+
+ migration_spec.header_type = cls::rbd::MIGRATION_HEADER_TYPE_SRC;
+
+ ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
+
+ ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features,
+ &incompatible_features));
+ ASSERT_EQ(RBD_FEATURE_MIGRATING, features);
+
+ ASSERT_EQ(0, migration_remove(&ioctx, oid));
+
+ ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+ ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features,
+ &incompatible_features));
+ ASSERT_EQ(0U, features);
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, migration_v1)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ bufferlist header;
+ header.append(RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT));
+ string oid = get_temp_image_name();
+ ASSERT_EQ(0, ioctx.write(oid, header, header.length(), 0));
+
+ cls::rbd::MigrationSpec migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST, 1,
+ "name", "ns", "id", "", {}, 0, false,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ false,
+ cls::rbd::MIGRATION_STATE_PREPARING,
+ "123");
+ cls::rbd::MigrationSpec read_migration_spec;
+
+ ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+
+ // v1 format image can only be migration source
+ ASSERT_EQ(-EINVAL, migration_set(&ioctx, oid, migration_spec));
+
+ migration_spec.header_type = cls::rbd::MIGRATION_HEADER_TYPE_SRC;
+ ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
+
+ ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+ ASSERT_EQ(migration_spec, read_migration_spec);
+
+ header.clear();
+ ASSERT_EQ(static_cast<int>(sizeof(RBD_MIGRATE_HEADER_TEXT)),
+ ioctx.read(oid, header, sizeof(RBD_MIGRATE_HEADER_TEXT), 0));
+ ASSERT_STREQ(RBD_MIGRATE_HEADER_TEXT, header.c_str());
+
+ ASSERT_EQ(-EEXIST, migration_set(&ioctx, oid, migration_spec));
+
+ migration_spec.state = cls::rbd::MIGRATION_STATE_PREPARED;
+ migration_spec.state_description = "456";
+ ASSERT_EQ(0, migration_set_state(&ioctx, oid, migration_spec.state,
+ migration_spec.state_description));
+ ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+ ASSERT_EQ(migration_spec, read_migration_spec);
+
+ ASSERT_EQ(0, migration_remove(&ioctx, oid));
+
+ ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+ ASSERT_EQ(-EINVAL, migration_set_state(&ioctx, oid, migration_spec.state,
+ migration_spec.state_description));
+ header.clear();
+ ASSERT_EQ(static_cast<int>(sizeof(RBD_HEADER_TEXT)),
+ ioctx.read(oid, header, sizeof(RBD_HEADER_TEXT), 0));
+ ASSERT_STREQ(RBD_HEADER_TEXT, header.c_str());
+
+ ioctx.close();
+}
+
+TEST_F(TestClsRbd, assert_snapc_seq)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, 0,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, 0,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ uint64_t snapc_seq = 0;
+
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ std::vector<uint64_t> snaps;
+ snaps.push_back(CEPH_NOSNAP);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&snaps.back()));
+ snapc_seq = snaps[0];
+
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(snaps[0], snaps));
+ bufferlist bl;
+ bl.append("foo");
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq + 1,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq + 1,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(snapc_seq));
+}
+
+TEST_F(TestClsRbd, sparsify)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+ ioctx.remove(oid);
+
+ bool sparse_read_supported = is_sparse_read_supported(ioctx, oid);
+
+ // test sparsify on a non-existent object
+
+ ASSERT_EQ(-ENOENT, sparsify(&ioctx, oid, 16, false));
+ uint64_t size;
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, NULL));
+ ASSERT_EQ(-ENOENT, sparsify(&ioctx, oid, 16, true));
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, NULL));
+
+ // test sparsify on an empty object
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+ ASSERT_EQ(0, sparsify(&ioctx, oid, 16, false));
+ ASSERT_EQ(0, sparsify(&ioctx, oid, 16, true));
+ ASSERT_EQ(-ENOENT, sparsify(&ioctx, oid, 16, false));
+
+ // test sparsify on a zeroed object
+
+ bufferlist inbl;
+ inbl.append(std::string(4096, '\0'));
+ ASSERT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 0));
+ ASSERT_EQ(0, sparsify(&ioctx, oid, 16, false));
+ std::map<uint64_t, uint64_t> m;
+ bufferlist outbl;
+ std::map<uint64_t, uint64_t> expected_m;
+ bufferlist expected_outbl;
+ switch (int r = ioctx.sparse_read(oid, m, outbl, inbl.length(), 0); r) {
+ case 0:
+ expected_m = {};
+ ASSERT_EQ(expected_m, m);
+ break;
+ case 1:
+ expected_m = {{0, 0}};
+ ASSERT_EQ(expected_m, m);
+ break;
+ default:
+ FAIL() << r << " is odd";
+ }
+ ASSERT_EQ(m, expected_m);
+ ASSERT_EQ(0, sparsify(&ioctx, oid, 16, true));
+ ASSERT_EQ(-ENOENT, sparsify(&ioctx, oid, 16, true));
+ ASSERT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 0));
+ ASSERT_EQ(0, sparsify(&ioctx, oid, 16, true));
+ ASSERT_EQ(-ENOENT, sparsify(&ioctx, oid, 16, true));
+
+ // test sparsify on an object with zeroes
+
+ inbl.append(std::string(4096, '1'));
+ inbl.append(std::string(4096, '\0'));
+ inbl.append(std::string(4096, '2'));
+ inbl.append(std::string(4096, '\0'));
+ ASSERT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 0));
+
+ // try to sparsify with sparse_size too large
+
+ ASSERT_EQ(0, sparsify(&ioctx, oid, inbl.length(), true));
+ expected_m = {{0, inbl.length()}};
+ expected_outbl = inbl;
+ ASSERT_EQ((int)expected_m.size(),
+ ioctx.sparse_read(oid, m, outbl, inbl.length(), 0));
+ ASSERT_EQ(m, expected_m);
+ ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+
+ // sparsify with small sparse_size
+
+ ASSERT_EQ(0, sparsify(&ioctx, oid, 16, true));
+ outbl.clear();
+ ASSERT_EQ((int)(inbl.length() - 4096),
+ ioctx.read(oid, outbl, inbl.length(), 0));
+ outbl.append(std::string(4096, '\0'));
+ ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+ if (sparse_read_supported) {
+ expected_m = {{4096 * 1, 4096}, {4096 * 3, 4096}};
+ expected_outbl.clear();
+ expected_outbl.append(std::string(4096, '1'));
+ expected_outbl.append(std::string(4096, '2'));
+ } else {
+ expected_m = {{0, 4 * 4096}};
+ expected_outbl.clear();
+ expected_outbl.append(std::string(4096, '\0'));
+ expected_outbl.append(std::string(4096, '1'));
+ expected_outbl.append(std::string(4096, '\0'));
+ expected_outbl.append(std::string(4096, '2'));
+ }
+ m.clear();
+ outbl.clear();
+ ASSERT_EQ((int)expected_m.size(),
+ ioctx.sparse_read(oid, m, outbl, inbl.length(), 0));
+ ASSERT_EQ(m, expected_m);
+ ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+
+ // test it is the same after yet another sparsify
+
+ ASSERT_EQ(0, sparsify(&ioctx, oid, 16, true));
+ m.clear();
+ outbl.clear();
+ ASSERT_EQ((int)expected_m.size(),
+ ioctx.sparse_read(oid, m, outbl, inbl.length(), 0));
+ ASSERT_EQ(m, expected_m);
+ ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+
+ ASSERT_EQ(0, ioctx.remove(oid));
+ ioctx.close();
+}
diff --git a/src/test/cls_refcount/CMakeLists.txt b/src/test/cls_refcount/CMakeLists.txt
new file mode 100644
index 000000000..c8589bdb5
--- /dev/null
+++ b/src/test/cls_refcount/CMakeLists.txt
@@ -0,0 +1,18 @@
+# ceph_test_cls_refcount
+add_executable(ceph_test_cls_refcount
+ test_cls_refcount.cc
+ )
+target_link_libraries(ceph_test_cls_refcount
+ librados
+ cls_refcount_client
+ global
+ ${UNITTEST_LIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ radostest-cxx
+ )
+install(TARGETS
+ ceph_test_cls_refcount
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_refcount/test_cls_refcount.cc b/src/test/cls_refcount/test_cls_refcount.cc
new file mode 100644
index 000000000..2868b48b6
--- /dev/null
+++ b/src/test/cls_refcount/test_cls_refcount.cc
@@ -0,0 +1,778 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "cls/refcount/cls_refcount_client.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+static librados::ObjectWriteOperation *new_op() {
+ return new librados::ObjectWriteOperation();
+}
+
+TEST(cls_rgw, test_implicit) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+ string oldtag = "oldtag";
+ string newtag = "newtag";
+
+
+ /* get on a missing object will fail */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, op));
+ delete op;
+
+ /* create object */
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> refs;
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ string wildcard_tag;
+ string tag = refs.front();
+
+ ASSERT_EQ(wildcard_tag, tag);
+
+ /* take another reference, verify */
+ op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(2, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ ASSERT_EQ(1, (int)refs_map.count(wildcard_tag));
+ ASSERT_EQ(1, (int)refs_map.count(newtag));
+
+ delete op;
+
+ /* drop reference to oldtag */
+
+ op = new_op();
+ cls_refcount_put(*op, oldtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(newtag, tag);
+
+ delete op;
+
+ /* drop oldtag reference again, op should return success, wouldn't do anything */
+
+ op = new_op();
+ cls_refcount_put(*op, oldtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(newtag, tag);
+
+ delete op;
+
+ /* drop newtag reference, make sure object removed */
+ op = new_op();
+ cls_refcount_put(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+}
+
+/*
+ * similar to test_implicit, just changes the order of the tags removal
+ * see issue #20107
+ */
+TEST(cls_rgw, test_implicit_idempotent) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+ string oldtag = "oldtag";
+ string newtag = "newtag";
+
+
+ /* get on a missing object will fail */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, op));
+ delete op;
+
+ /* create object */
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> refs;
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ string wildcard_tag;
+ string tag = refs.front();
+
+ ASSERT_EQ(wildcard_tag, tag);
+
+ /* take another reference, verify */
+ op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(2, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ ASSERT_EQ(1, (int)refs_map.count(wildcard_tag));
+ ASSERT_EQ(1, (int)refs_map.count(newtag));
+
+ delete op;
+
+ /* drop reference to newtag */
+
+ op = new_op();
+ cls_refcount_put(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(string(), tag);
+
+ delete op;
+
+ /* drop newtag reference again, op should return success, wouldn't do anything */
+
+ op = new_op();
+ cls_refcount_put(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(string(), tag);
+
+ delete op;
+
+ /* drop oldtag reference, make sure object removed */
+ op = new_op();
+ cls_refcount_put(*op, oldtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+}
+
+
+TEST(cls_rgw, test_put_snap) {
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ bufferlist bl;
+ bl.append("hi there");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+ ASSERT_EQ(0, ioctx.remove("foo"));
+
+ sleep(2);
+
+ ASSERT_EQ(0, ioctx.snap_create("snapbar"));
+
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_put(*op, "notag", true);
+ ASSERT_EQ(-ENOENT, ioctx.operate("foo", op));
+
+ EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+ EXPECT_EQ(0, ioctx.snap_remove("snapbar"));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+}
+
+TEST(cls_rgw, test_explicit) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> refs;
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(0, (int)refs.size());
+
+
+ /* take first reference, verify */
+
+ string newtag = "newtag";
+
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_get(*op, newtag);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(1, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ ASSERT_EQ(1, (int)refs_map.count(newtag));
+
+ delete op;
+
+ /* try to drop reference to unexisting tag */
+
+ string nosuchtag = "nosuchtag";
+
+ op = new_op();
+ cls_refcount_put(*op, nosuchtag);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(1, (int)refs.size());
+
+ string tag = refs.front();
+ ASSERT_EQ(newtag, tag);
+
+ delete op;
+
+ /* drop newtag reference, make sure object removed */
+ op = new_op();
+ cls_refcount_put(*op, newtag);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+}
+
+TEST(cls_rgw, set) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> tag_refs, refs;
+
+#define TAGS_NUM 5
+ string tags[TAGS_NUM];
+
+ char buf[16];
+ for (int i = 0; i < TAGS_NUM; i++) {
+ snprintf(buf, sizeof(buf), "tag%d", i);
+ tags[i] = buf;
+ tag_refs.push_back(tags[i]);
+ }
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(0, (int)refs.size());
+
+ /* set reference list, verify */
+
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_set(*op, tag_refs);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ refs.clear();
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(TAGS_NUM, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ for (int i = 0; i < TAGS_NUM; i++) {
+ ASSERT_EQ(1, (int)refs_map.count(tags[i]));
+ }
+
+ delete op;
+
+ /* remove all refs */
+
+ for (int i = 0; i < TAGS_NUM; i++) {
+ op = new_op();
+ cls_refcount_put(*op, tags[i]);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ delete op;
+ }
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+}
+
+TEST(cls_rgw, test_implicit_ec) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+ string oldtag = "oldtag";
+ string newtag = "newtag";
+
+
+ /* get on a missing object will fail */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, op));
+ delete op;
+
+ /* create object */
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> refs;
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ string wildcard_tag;
+ string tag = refs.front();
+
+ ASSERT_EQ(wildcard_tag, tag);
+
+ /* take another reference, verify */
+ op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(2, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ ASSERT_EQ(1, (int)refs_map.count(wildcard_tag));
+ ASSERT_EQ(1, (int)refs_map.count(newtag));
+
+ delete op;
+
+ /* drop reference to oldtag */
+
+ op = new_op();
+ cls_refcount_put(*op, oldtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(newtag, tag);
+
+ delete op;
+
+ /* drop oldtag reference again, op should return success, wouldn't do anything */
+
+ op = new_op();
+ cls_refcount_put(*op, oldtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(newtag, tag);
+
+ delete op;
+
+ /* drop newtag reference, make sure object removed */
+ op = new_op();
+ cls_refcount_put(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, rados));
+}
+
+/*
+ * similar to test_implicit, just changes the order of the tags removal
+ * see issue #20107
+ */
+TEST(cls_rgw, test_implicit_idempotent_ec) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+ string oldtag = "oldtag";
+ string newtag = "newtag";
+
+
+ /* get on a missing object will fail */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, op));
+ delete op;
+
+ /* create object */
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> refs;
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ string wildcard_tag;
+ string tag = refs.front();
+
+ ASSERT_EQ(wildcard_tag, tag);
+
+ /* take another reference, verify */
+ op = new_op();
+ cls_refcount_get(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(2, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ ASSERT_EQ(1, (int)refs_map.count(wildcard_tag));
+ ASSERT_EQ(1, (int)refs_map.count(newtag));
+
+ delete op;
+
+ /* drop reference to newtag */
+
+ op = new_op();
+ cls_refcount_put(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(string(), tag);
+
+ delete op;
+
+ /* drop newtag reference again, op should return success, wouldn't do anything */
+
+ op = new_op();
+ cls_refcount_put(*op, newtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs, true));
+ ASSERT_EQ(1, (int)refs.size());
+
+ tag = refs.front();
+ ASSERT_EQ(string(), tag);
+
+ delete op;
+
+ /* drop oldtag reference, make sure object removed */
+ op = new_op();
+ cls_refcount_put(*op, oldtag, true);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, rados));
+}
+
+
+TEST(cls_rgw, test_put_snap_ec) {
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ bufferlist bl;
+ bl.append("hi there");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+ ASSERT_EQ(0, ioctx.remove("foo"));
+
+ sleep(2);
+
+ ASSERT_EQ(0, ioctx.snap_create("snapbar"));
+
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_put(*op, "notag", true);
+ ASSERT_EQ(-ENOENT, ioctx.operate("foo", op));
+
+ EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+ EXPECT_EQ(0, ioctx.snap_remove("snapbar"));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, rados));
+}
+
+TEST(cls_rgw, test_explicit_ec) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> refs;
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(0, (int)refs.size());
+
+
+ /* take first reference, verify */
+
+ string newtag = "newtag";
+
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_get(*op, newtag);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(1, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ ASSERT_EQ(1, (int)refs_map.count(newtag));
+
+ delete op;
+
+ /* try to drop reference to unexisting tag */
+
+ string nosuchtag = "nosuchtag";
+
+ op = new_op();
+ cls_refcount_put(*op, nosuchtag);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(1, (int)refs.size());
+
+ string tag = refs.front();
+ ASSERT_EQ(newtag, tag);
+
+ delete op;
+
+ /* drop newtag reference, make sure object removed */
+ op = new_op();
+ cls_refcount_put(*op, newtag);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ delete op;
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, rados));
+}
+
+TEST(cls_rgw, set_ec) /* test refcount using implicit referencing of newly created objects */
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ /* read reference, should return a single wildcard entry */
+
+ list<string> tag_refs, refs;
+
+#define TAGS_NUM 5
+ string tags[TAGS_NUM];
+
+ char buf[16];
+ for (int i = 0; i < TAGS_NUM; i++) {
+ snprintf(buf, sizeof(buf), "tag%d", i);
+ tags[i] = buf;
+ tag_refs.push_back(tags[i]);
+ }
+
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(0, (int)refs.size());
+
+ /* set reference list, verify */
+
+ librados::ObjectWriteOperation *op = new_op();
+ cls_refcount_set(*op, tag_refs);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ refs.clear();
+ ASSERT_EQ(0, cls_refcount_read(ioctx, oid, &refs));
+ ASSERT_EQ(TAGS_NUM, (int)refs.size());
+
+ map<string, bool> refs_map;
+ for (list<string>::iterator iter = refs.begin(); iter != refs.end(); ++iter) {
+ refs_map[*iter] = true;
+ }
+
+ for (int i = 0; i < TAGS_NUM; i++) {
+ ASSERT_EQ(1, (int)refs_map.count(tags[i]));
+ }
+
+ delete op;
+
+ /* remove all refs */
+
+ for (int i = 0; i < TAGS_NUM; i++) {
+ op = new_op();
+ cls_refcount_put(*op, tags[i]);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ delete op;
+ }
+
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, NULL, NULL));
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, rados));
+}
diff --git a/src/test/cls_rgw/CMakeLists.txt b/src/test/cls_rgw/CMakeLists.txt
new file mode 100644
index 000000000..67b8beb6c
--- /dev/null
+++ b/src/test/cls_rgw/CMakeLists.txt
@@ -0,0 +1,24 @@
+if(${WITH_RADOSGW})
+ add_executable(ceph_test_cls_rgw
+ test_cls_rgw.cc
+ )
+ target_link_libraries(ceph_test_cls_rgw
+ cls_rgw_client
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx)
+ install(TARGETS
+ ceph_test_cls_rgw
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+ add_executable(ceph_test_cls_rgw_stats test_cls_rgw_stats.cc
+ $<TARGET_OBJECTS:unit-main>)
+ target_link_libraries(ceph_test_cls_rgw_stats cls_rgw_client global
+ librados ${UNITTEST_LIBS} radostest-cxx)
+ install(TARGETS ceph_test_cls_rgw_stats DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(${WITH_RADOSGW})
+
diff --git a/src/test/cls_rgw/test_cls_rgw.cc b/src/test/cls_rgw/test_cls_rgw.cc
new file mode 100644
index 000000000..bf60dfdd0
--- /dev/null
+++ b/src/test/cls_rgw/test_cls_rgw.cc
@@ -0,0 +1,1342 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "cls/rgw/cls_rgw_client.h"
+#include "cls/rgw/cls_rgw_ops.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+#include "global/global_context.h"
+#include "common/ceph_context.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+using namespace std;
+using namespace librados;
+
+// creates a temporary pool and initializes an IoCtx shared by all tests
+class cls_rgw : public ::testing::Test {
+ static librados::Rados rados;
+ static std::string pool_name;
+ protected:
+ static librados::IoCtx ioctx;
+
+ static void SetUpTestCase() {
+ pool_name = get_temp_pool_name();
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+ static void TearDownTestCase() {
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+ }
+};
+librados::Rados cls_rgw::rados;
+std::string cls_rgw::pool_name;
+librados::IoCtx cls_rgw::ioctx;
+
+
+string str_int(string s, int i)
+{
+ char buf[32];
+ snprintf(buf, sizeof(buf), "-%d", i);
+ s.append(buf);
+
+ return s;
+}
+
+void test_stats(librados::IoCtx& ioctx, string& oid, RGWObjCategory category, uint64_t num_entries, uint64_t total_size)
+{
+ map<int, struct rgw_cls_list_ret> results;
+ map<int, string> oids;
+ oids[0] = oid;
+ ASSERT_EQ(0, CLSRGWIssueGetDirHeader(ioctx, oids, results, 8)());
+
+ uint64_t entries = 0;
+ uint64_t size = 0;
+ map<int, struct rgw_cls_list_ret>::iterator iter = results.begin();
+ for (; iter != results.end(); ++iter) {
+ entries += (iter->second).dir.header.stats[category].num_entries;
+ size += (iter->second).dir.header.stats[category].total_size;
+ }
+ ASSERT_EQ(total_size, size);
+ ASSERT_EQ(num_entries, entries);
+}
+
+void index_prepare(librados::IoCtx& ioctx, string& oid, RGWModifyOp index_op,
+ string& tag, const cls_rgw_obj_key& key, string& loc,
+ uint16_t bi_flags = 0, bool log_op = true)
+{
+ ObjectWriteOperation op;
+ rgw_zone_set zones_trace;
+ cls_rgw_bucket_prepare_op(op, index_op, tag, key, loc, log_op, bi_flags, zones_trace);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+}
+
+void index_complete(librados::IoCtx& ioctx, string& oid, RGWModifyOp index_op,
+ string& tag, int epoch, const cls_rgw_obj_key& key,
+ rgw_bucket_dir_entry_meta& meta, uint16_t bi_flags = 0,
+ bool log_op = true)
+{
+ ObjectWriteOperation op;
+ rgw_bucket_entry_ver ver;
+ ver.pool = ioctx.get_id();
+ ver.epoch = epoch;
+ meta.accounted_size = meta.size;
+ cls_rgw_bucket_complete_op(op, index_op, tag, ver, key, meta, nullptr, log_op, bi_flags, nullptr);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ if (!key.instance.empty()) {
+ bufferlist olh_tag;
+ olh_tag.append(tag);
+ rgw_zone_set zone_set;
+ ASSERT_EQ(0, cls_rgw_bucket_link_olh(ioctx, oid, key, olh_tag,
+ false, tag, &meta, epoch,
+ ceph::real_time{}, true, true, zone_set));
+ }
+}
+
+TEST_F(cls_rgw, index_basic)
+{
+ string bucket_oid = str_int("bucket", 0);
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ uint64_t epoch = 1;
+
+ uint64_t obj_size = 1024;
+
+#define NUM_OBJS 10
+ for (int i = 0; i < NUM_OBJS; i++) {
+ cls_rgw_obj_key obj = str_int("obj", i);
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, i, obj_size * i);
+
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::None;
+ meta.size = obj_size;
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta);
+ }
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS,
+ obj_size * NUM_OBJS);
+}
+
+TEST_F(cls_rgw, index_multiple_obj_writers)
+{
+ string bucket_oid = str_int("bucket", 1);
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ uint64_t obj_size = 1024;
+
+ cls_rgw_obj_key obj = str_int("obj", 0);
+ string loc = str_int("loc", 0);
+ /* multi prepare on a single object */
+ for (int i = 0; i < NUM_OBJS; i++) {
+ string tag = str_int("tag", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, 0, 0);
+ }
+
+ for (int i = NUM_OBJS; i > 0; i--) {
+ string tag = str_int("tag", i - 1);
+
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::None;
+ meta.size = obj_size * i;
+
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, i, obj, meta);
+
+ /* verify that object size doesn't change, as we went back with epoch */
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, 1,
+ obj_size * NUM_OBJS);
+ }
+}
+
+TEST_F(cls_rgw, index_remove_object)
+{
+ string bucket_oid = str_int("bucket", 2);
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ uint64_t obj_size = 1024;
+ uint64_t total_size = 0;
+
+ int epoch = 0;
+
+ /* prepare multiple objects */
+ for (int i = 0; i < NUM_OBJS; i++) {
+ cls_rgw_obj_key obj = str_int("obj", i);
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, i, total_size);
+
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::None;
+ meta.size = i * obj_size;
+ total_size += i * obj_size;
+
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, i + 1, total_size);
+ }
+
+ int i = NUM_OBJS / 2;
+ string tag_remove = "tag-rm";
+ string tag_modify = "tag-mod";
+ cls_rgw_obj_key obj = str_int("obj", i);
+ string loc = str_int("loc", i);
+
+ /* prepare both removal and modification on the same object */
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, obj, loc);
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS, total_size);
+
+ rgw_bucket_dir_entry_meta meta;
+
+ /* complete object removal */
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, ++epoch, obj, meta);
+
+ /* verify stats correct */
+ total_size -= i * obj_size;
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS - 1, total_size);
+
+ meta.size = 512;
+ meta.category = RGWObjCategory::None;
+
+ /* complete object modification */
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, ++epoch, obj, meta);
+
+ /* verify stats correct */
+ total_size += meta.size;
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS, total_size);
+
+
+ /* prepare both removal and modification on the same object, this time we'll
+ * first complete modification then remove*/
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, obj, loc);
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_modify, obj, loc);
+
+ /* complete modification */
+ total_size -= meta.size;
+ meta.size = i * obj_size * 2;
+ meta.category = RGWObjCategory::None;
+
+ /* complete object modification */
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, ++epoch, obj, meta);
+
+ /* verify stats correct */
+ total_size += meta.size;
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS, total_size);
+
+ /* complete object removal */
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, ++epoch, obj, meta);
+
+ /* verify stats correct */
+ total_size -= meta.size;
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS - 1,
+ total_size);
+}
+
+TEST_F(cls_rgw, index_suggest)
+{
+ string bucket_oid = str_int("suggest", 1);
+ {
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+ }
+ uint64_t total_size = 0;
+
+ int epoch = 0;
+
+ int num_objs = 100;
+
+ uint64_t obj_size = 1024;
+
+ /* create multiple objects */
+ for (int i = 0; i < num_objs; i++) {
+ cls_rgw_obj_key obj = str_int("obj", i);
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, i, total_size);
+
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::None;
+ meta.size = obj_size;
+ total_size += meta.size;
+
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, i + 1, total_size);
+ }
+
+ /* prepare (without completion) some of the objects */
+ for (int i = 0; i < num_objs; i += 2) {
+ cls_rgw_obj_key obj = str_int("obj", i);
+ string tag = str_int("tag-prepare", i);
+ string loc = str_int("loc", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, num_objs, total_size);
+ }
+
+ int actual_num_objs = num_objs;
+ /* remove half of the objects */
+ for (int i = num_objs / 2; i < num_objs; i++) {
+ cls_rgw_obj_key obj = str_int("obj", i);
+ string tag = str_int("tag-rm", i);
+ string loc = str_int("loc", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, actual_num_objs, total_size);
+
+ rgw_bucket_dir_entry_meta meta;
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag, ++epoch, obj, meta);
+
+ total_size -= obj_size;
+ actual_num_objs--;
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, actual_num_objs, total_size);
+ }
+
+ bufferlist updates;
+
+ for (int i = 0; i < num_objs; i += 2) {
+ cls_rgw_obj_key obj = str_int("obj", i);
+ string tag = str_int("tag-rm", i);
+ string loc = str_int("loc", i);
+
+ rgw_bucket_dir_entry dirent;
+ dirent.key.name = obj.name;
+ dirent.locator = loc;
+ dirent.exists = (i < num_objs / 2); // we removed half the objects
+ dirent.meta.size = 1024;
+ dirent.meta.accounted_size = 1024;
+
+ char suggest_op = (i < num_objs / 2 ? CEPH_RGW_UPDATE : CEPH_RGW_REMOVE);
+ cls_rgw_encode_suggestion(suggest_op, dirent, updates);
+ }
+
+ map<int, string> bucket_objs;
+ bucket_objs[0] = bucket_oid;
+ int r = CLSRGWIssueSetTagTimeout(ioctx, bucket_objs, 8 /* max aio */, 1)();
+ ASSERT_EQ(0, r);
+
+ sleep(1);
+
+ /* suggest changes! */
+ {
+ ObjectWriteOperation op;
+ cls_rgw_suggest_changes(op, updates);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+ }
+ /* suggest changes twice! */
+ {
+ ObjectWriteOperation op;
+ cls_rgw_suggest_changes(op, updates);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+ }
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, num_objs / 2, total_size);
+}
+
+static void list_entries(librados::IoCtx& ioctx,
+ const std::string& oid,
+ uint32_t num_entries,
+ std::map<int, rgw_cls_list_ret>& results)
+{
+ std::map<int, std::string> oids = { {0, oid} };
+ cls_rgw_obj_key start_key;
+ string empty_prefix;
+ string empty_delimiter;
+ ASSERT_EQ(0, CLSRGWIssueBucketList(ioctx, start_key, empty_prefix,
+ empty_delimiter, num_entries,
+ true, oids, results, 1)());
+}
+
+TEST_F(cls_rgw, index_suggest_complete)
+{
+ string bucket_oid = str_int("suggest", 2);
+ {
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+ }
+
+ cls_rgw_obj_key obj = str_int("obj", 0);
+ string tag = str_int("tag-prepare", 0);
+ string loc = str_int("loc", 0);
+
+ // prepare entry
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+
+ // list entry before completion
+ rgw_bucket_dir_entry dirent;
+ {
+ std::map<int, rgw_cls_list_ret> listing;
+ list_entries(ioctx, bucket_oid, 1, listing);
+ ASSERT_EQ(1, listing.size());
+ const auto& entries = listing.begin()->second.dir.m;
+ ASSERT_EQ(1, entries.size());
+ dirent = entries.begin()->second;
+ ASSERT_EQ(obj, dirent.key);
+ }
+ // complete entry
+ {
+ rgw_bucket_dir_entry_meta meta;
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, 1, obj, meta);
+ }
+ // suggest removal of listed entry
+ {
+ bufferlist updates;
+ cls_rgw_encode_suggestion(CEPH_RGW_REMOVE, dirent, updates);
+
+ ObjectWriteOperation op;
+ cls_rgw_suggest_changes(op, updates);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+ }
+ // list entry again, verify that suggested removal was not applied
+ {
+ std::map<int, rgw_cls_list_ret> listing;
+ list_entries(ioctx, bucket_oid, 1, listing);
+ ASSERT_EQ(1, listing.size());
+ const auto& entries = listing.begin()->second.dir.m;
+ ASSERT_EQ(1, entries.size());
+ EXPECT_TRUE(entries.begin()->second.exists);
+ }
+}
+
+/*
+ * This case is used to test whether get_obj_vals will
+ * return all validate utf8 objnames and filter out those
+ * in BI_PREFIX_CHAR private namespace.
+ */
+TEST_F(cls_rgw, index_list)
+{
+ string bucket_oid = str_int("bucket", 4);
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ uint64_t epoch = 1;
+ uint64_t obj_size = 1024;
+ const int num_objs = 4;
+ const string keys[num_objs] = {
+ /* single byte utf8 character */
+ { static_cast<char>(0x41) },
+ /* double byte utf8 character */
+ { static_cast<char>(0xCF), static_cast<char>(0x8F) },
+ /* treble byte utf8 character */
+ { static_cast<char>(0xDF), static_cast<char>(0x8F), static_cast<char>(0x8F) },
+ /* quadruble byte utf8 character */
+ { static_cast<char>(0xF7), static_cast<char>(0x8F), static_cast<char>(0x8F), static_cast<char>(0x8F) },
+ };
+
+ for (int i = 0; i < num_objs; i++) {
+ string obj = keys[i];
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
+ 0 /* bi_flags */, false /* log_op */);
+
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::None;
+ meta.size = obj_size;
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
+ 0 /* bi_flags */, false /* log_op */);
+ }
+
+ map<string, bufferlist> entries;
+ /* insert 998 omap key starts with BI_PREFIX_CHAR,
+ * so bucket list first time will get one key before 0x80 and one key after */
+ for (int i = 0; i < 998; ++i) {
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%c%s%d", 0x80, "1000_", i);
+ entries.emplace(string{buf}, bufferlist{});
+ }
+ ioctx.omap_set(bucket_oid, entries);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None,
+ num_objs, obj_size * num_objs);
+
+ map<int, string> oids = { {0, bucket_oid} };
+ map<int, struct rgw_cls_list_ret> list_results;
+ cls_rgw_obj_key start_key("", "");
+ string empty_prefix;
+ string empty_delimiter;
+ int r = CLSRGWIssueBucketList(ioctx, start_key,
+ empty_prefix, empty_delimiter,
+ 1000, true, oids, list_results, 1)();
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(1u, list_results.size());
+
+ auto it = list_results.begin();
+ auto m = (it->second).dir.m;
+
+ ASSERT_EQ(4u, m.size());
+ int i = 0;
+ for(auto it2 = m.cbegin(); it2 != m.cend(); it2++, i++) {
+ ASSERT_EQ(it2->first.compare(keys[i]), 0);
+ }
+}
+
+
+/*
+ * This case is used to test when bucket index list that includes a
+ * delimiter can handle the first chunk ending in a delimiter.
+ */
+TEST_F(cls_rgw, index_list_delimited)
+{
+ string bucket_oid = str_int("bucket", 7);
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ uint64_t epoch = 1;
+ uint64_t obj_size = 1024;
+ const int file_num_objs = 5;
+ const int dir_num_objs = 1005;
+
+ std::vector<std::string> file_prefixes =
+ { "a", "c", "e", "g", "i", "k", "m", "o", "q", "s", "u" };
+ std::vector<std::string> dir_prefixes =
+ { "b/", "d/", "f/", "h/", "j/", "l/", "n/", "p/", "r/", "t/" };
+
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::None;
+ meta.size = obj_size;
+
+ // create top-level files
+ for (const auto& p : file_prefixes) {
+ for (int i = 0; i < file_num_objs; i++) {
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+ const string obj = str_int(p, i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
+ 0 /* bi_flags */, false /* log_op */);
+
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
+ 0 /* bi_flags */, false /* log_op */);
+ }
+ }
+
+ // create large directories
+ for (const auto& p : dir_prefixes) {
+ for (int i = 0; i < dir_num_objs; i++) {
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+ const string obj = p + str_int("f", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
+ 0 /* bi_flags */, false /* log_op */);
+
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
+ 0 /* bi_flags */, false /* log_op */);
+ }
+ }
+
+ map<int, string> oids = { {0, bucket_oid} };
+ map<int, struct rgw_cls_list_ret> list_results;
+ cls_rgw_obj_key start_key("", "");
+ const string empty_prefix;
+ const string delimiter = "/";
+ int r = CLSRGWIssueBucketList(ioctx, start_key,
+ empty_prefix, delimiter,
+ 1000, true, oids, list_results, 1)();
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(1u, list_results.size()) <<
+ "Because we only have one bucket index shard, we should "
+ "only get one list_result.";
+
+ auto it = list_results.begin();
+ auto id_entry_map = it->second.dir.m;
+ bool truncated = it->second.is_truncated;
+
+ // the cls code will make 4 tries to get 1000 entries; however
+ // because each of the subdirectories is so large, each attempt will
+ // only retrieve the first part of the subdirectory
+
+ ASSERT_EQ(48u, id_entry_map.size()) <<
+ "We should get 40 top-level entries and the tops of 8 \"subdirectories\".";
+ ASSERT_EQ(true, truncated) << "We did not get all entries.";
+
+ ASSERT_EQ("a-0", id_entry_map.cbegin()->first);
+ ASSERT_EQ("p/", id_entry_map.crbegin()->first);
+
+ // now let's get the rest of the entries
+
+ list_results.clear();
+
+ cls_rgw_obj_key start_key2("p/", "");
+ r = CLSRGWIssueBucketList(ioctx, start_key2,
+ empty_prefix, delimiter,
+ 1000, true, oids, list_results, 1)();
+ ASSERT_EQ(r, 0);
+
+ it = list_results.begin();
+ id_entry_map = it->second.dir.m;
+ truncated = it->second.is_truncated;
+
+ ASSERT_EQ(17u, id_entry_map.size()) <<
+ "We should get 15 top-level entries and the tops of 2 \"subdirectories\".";
+ ASSERT_EQ(false, truncated) << "We now have all entries.";
+
+ ASSERT_EQ("q-0", id_entry_map.cbegin()->first);
+ ASSERT_EQ("u-4", id_entry_map.crbegin()->first);
+}
+
+
+TEST_F(cls_rgw, bi_list)
+{
+ string bucket_oid = str_int("bucket", 5);
+
+ CephContext *cct = reinterpret_cast<CephContext *>(ioctx.cct());
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ const std::string empty_name_filter;
+ uint64_t max = 10;
+ std::list<rgw_cls_bi_entry> entries;
+ bool is_truncated;
+ std::string marker;
+
+ int ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(entries.size(), 0u) <<
+ "The listing of an empty bucket as 0 entries.";
+ ASSERT_EQ(is_truncated, false) <<
+ "The listing of an empty bucket is not truncated.";
+
+ uint64_t epoch = 1;
+ uint64_t obj_size = 1024;
+ const uint64_t num_objs = 35;
+
+ for (uint64_t i = 0; i < num_objs; i++) {
+ string obj = str_int(i % 4 ? "obj" : "об'єкт", i);
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
+ RGW_BILOG_FLAG_VERSIONED_OP);
+
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = RGWObjCategory::None;
+ meta.size = obj_size;
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
+ RGW_BILOG_FLAG_VERSIONED_OP);
+ }
+
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, num_objs + 10,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0);
+ if (is_truncated) {
+ ASSERT_LT(entries.size(), num_objs);
+ } else {
+ ASSERT_EQ(entries.size(), num_objs);
+ }
+
+ uint64_t num_entries = 0;
+
+ is_truncated = true;
+ marker.clear();
+ while(is_truncated) {
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0);
+ if (is_truncated) {
+ ASSERT_LT(entries.size(), num_objs - num_entries);
+ } else {
+ ASSERT_EQ(entries.size(), num_objs - num_entries);
+ }
+ num_entries += entries.size();
+ marker = entries.back().idx;
+ }
+
+ // try with marker as final entry
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(entries.size(), 0u);
+ ASSERT_EQ(is_truncated, false);
+
+ if (cct->_conf->osd_max_omap_entries_per_request < 15) {
+ num_entries = 0;
+ max = 15;
+ is_truncated = true;
+ marker.clear();
+ while(is_truncated) {
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0);
+ if (is_truncated) {
+ ASSERT_LT(entries.size(), num_objs - num_entries);
+ } else {
+ ASSERT_EQ(entries.size(), num_objs - num_entries);
+ }
+ num_entries += entries.size();
+ marker = entries.back().idx;
+ }
+
+ // try with marker as final entry
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(entries.size(), 0u);
+ ASSERT_EQ(is_truncated, false);
+ }
+
+ // test with name filters; pairs contain filter and expected number of elements returned
+ const std::list<std::pair<const std::string,unsigned>> filters_results =
+ { { str_int("obj", 9), 1 },
+ { str_int("об'єкт", 8), 1 },
+ { str_int("obj", 8), 0 } };
+ for (const auto& filter_result : filters_results) {
+ is_truncated = true;
+ entries.clear();
+ marker.clear();
+
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, filter_result.first, marker, max,
+ &entries, &is_truncated);
+
+ ASSERT_EQ(ret, 0) << "bi list test with name filters should succeed";
+ ASSERT_EQ(entries.size(), filter_result.second) <<
+ "bi list test with filters should return the correct number of results";
+ ASSERT_EQ(is_truncated, false) <<
+ "bi list test with filters should return correct truncation indicator";
+ }
+
+ // test whether combined segment count is correcgt
+ is_truncated = false;
+ entries.clear();
+ marker.clear();
+
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, num_objs - 1,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0) << "combined segment count should succeed";
+ ASSERT_EQ(entries.size(), num_objs - 1) <<
+ "combined segment count should return the correct number of results";
+ ASSERT_EQ(is_truncated, true) <<
+ "combined segment count should return correct truncation indicator";
+
+
+ marker = entries.back().idx; // advance marker
+ ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, num_objs - 1,
+ &entries, &is_truncated);
+ ASSERT_EQ(ret, 0) << "combined segment count should succeed";
+ ASSERT_EQ(entries.size(), 1) <<
+ "combined segment count should return the correct number of results";
+ ASSERT_EQ(is_truncated, false) <<
+ "combined segment count should return correct truncation indicator";
+}
+
+/* test garbage collection */
+static void create_obj(cls_rgw_obj& obj, int i, int j)
+{
+ char buf[32];
+ snprintf(buf, sizeof(buf), "-%d.%d", i, j);
+ obj.pool = "pool";
+ obj.pool.append(buf);
+ obj.key.name = "oid";
+ obj.key.name.append(buf);
+ obj.loc = "loc";
+ obj.loc.append(buf);
+}
+
+static bool cmp_objs(cls_rgw_obj& obj1, cls_rgw_obj& obj2)
+{
+ return (obj1.pool == obj2.pool) &&
+ (obj1.key == obj2.key) &&
+ (obj1.loc == obj2.loc);
+}
+
+
+TEST_F(cls_rgw, gc_set)
+{
+ /* add chains */
+ string oid = "obj";
+ for (int i = 0; i < 10; i++) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "chain-%d", i);
+ string tag = buf;
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ op.create(false); // create object
+
+ info.tag = tag;
+ cls_rgw_gc_set_entry(op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ }
+
+ bool truncated;
+ list<cls_rgw_gc_obj_info> entries;
+ string marker;
+ string next_marker;
+
+ /* list chains, verify truncated */
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries, &truncated, next_marker));
+ ASSERT_EQ(8, (int)entries.size());
+ ASSERT_EQ(1, truncated);
+
+ entries.clear();
+ next_marker.clear();
+
+ /* list all chains, verify not truncated */
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 10, true, entries, &truncated, next_marker));
+ ASSERT_EQ(10, (int)entries.size());
+ ASSERT_EQ(0, truncated);
+
+ /* verify all chains are valid */
+ list<cls_rgw_gc_obj_info>::iterator iter = entries.begin();
+ for (int i = 0; i < 10; i++, ++iter) {
+ cls_rgw_gc_obj_info& entry = *iter;
+
+ /* create expected chain name */
+ char buf[32];
+ snprintf(buf, sizeof(buf), "chain-%d", i);
+ string tag = buf;
+
+ /* verify chain name as expected */
+ ASSERT_EQ(entry.tag, tag);
+
+ /* verify expected num of objects in chain */
+ ASSERT_EQ(2, (int)entry.chain.objs.size());
+
+ list<cls_rgw_obj>::iterator oiter = entry.chain.objs.begin();
+ cls_rgw_obj obj1, obj2;
+
+ /* create expected objects */
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+
+ /* assign returned object names */
+ cls_rgw_obj& ret_obj1 = *oiter++;
+ cls_rgw_obj& ret_obj2 = *oiter;
+
+ /* verify objects are as expected */
+ ASSERT_EQ(1, (int)cmp_objs(obj1, ret_obj1));
+ ASSERT_EQ(1, (int)cmp_objs(obj2, ret_obj2));
+ }
+}
+
+TEST_F(cls_rgw, gc_list)
+{
+ /* add chains */
+ string oid = "obj";
+ for (int i = 0; i < 10; i++) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "chain-%d", i);
+ string tag = buf;
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ op.create(false); // create object
+
+ info.tag = tag;
+ cls_rgw_gc_set_entry(op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ }
+
+ bool truncated;
+ list<cls_rgw_gc_obj_info> entries;
+ list<cls_rgw_gc_obj_info> entries2;
+ string marker;
+ string next_marker;
+
+ /* list chains, verify truncated */
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries, &truncated, next_marker));
+ ASSERT_EQ(8, (int)entries.size());
+ ASSERT_EQ(1, truncated);
+
+ marker = next_marker;
+ next_marker.clear();
+
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries2, &truncated, next_marker));
+ ASSERT_EQ(2, (int)entries2.size());
+ ASSERT_EQ(0, truncated);
+
+ entries.splice(entries.end(), entries2);
+
+ /* verify all chains are valid */
+ list<cls_rgw_gc_obj_info>::iterator iter = entries.begin();
+ for (int i = 0; i < 10; i++, ++iter) {
+ cls_rgw_gc_obj_info& entry = *iter;
+
+ /* create expected chain name */
+ char buf[32];
+ snprintf(buf, sizeof(buf), "chain-%d", i);
+ string tag = buf;
+
+ /* verify chain name as expected */
+ ASSERT_EQ(entry.tag, tag);
+
+ /* verify expected num of objects in chain */
+ ASSERT_EQ(2, (int)entry.chain.objs.size());
+
+ list<cls_rgw_obj>::iterator oiter = entry.chain.objs.begin();
+ cls_rgw_obj obj1, obj2;
+
+ /* create expected objects */
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+
+ /* assign returned object names */
+ cls_rgw_obj& ret_obj1 = *oiter++;
+ cls_rgw_obj& ret_obj2 = *oiter;
+
+ /* verify objects are as expected */
+ ASSERT_EQ(1, (int)cmp_objs(obj1, ret_obj1));
+ ASSERT_EQ(1, (int)cmp_objs(obj2, ret_obj2));
+ }
+}
+
+TEST_F(cls_rgw, gc_defer)
+{
+ librados::IoCtx ioctx;
+ librados::Rados rados;
+
+ string gc_pool_name = get_temp_pool_name();
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(gc_pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(gc_pool_name.c_str(), ioctx));
+
+ string oid = "obj";
+ string tag = "mychain";
+
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ op.create(false);
+
+ info.tag = tag;
+
+ /* create chain */
+ cls_rgw_gc_set_entry(op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ bool truncated;
+ list<cls_rgw_gc_obj_info> entries;
+ string marker;
+ string next_marker;
+
+ /* list chains, verify num entries as expected */
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
+ ASSERT_EQ(1, (int)entries.size());
+ ASSERT_EQ(0, truncated);
+
+ librados::ObjectWriteOperation op2;
+
+ /* defer chain */
+ cls_rgw_gc_defer_entry(op2, 5, tag);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ entries.clear();
+ next_marker.clear();
+
+ /* verify list doesn't show deferred entry (this may fail if cluster is thrashing) */
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
+ ASSERT_EQ(0, (int)entries.size());
+ ASSERT_EQ(0, truncated);
+
+ /* wait enough */
+ sleep(5);
+ next_marker.clear();
+
+ /* verify list shows deferred entry */
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
+ ASSERT_EQ(1, (int)entries.size());
+ ASSERT_EQ(0, truncated);
+
+ librados::ObjectWriteOperation op3;
+ vector<string> tags;
+ tags.push_back(tag);
+
+ /* remove chain */
+ cls_rgw_gc_remove(op3, tags);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+
+ entries.clear();
+ next_marker.clear();
+
+ /* verify entry was removed */
+ ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
+ ASSERT_EQ(0, (int)entries.size());
+ ASSERT_EQ(0, truncated);
+
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(gc_pool_name, rados));
+}
+
+auto populate_usage_log_info(std::string user, std::string payer, int total_usage_entries)
+{
+ rgw_usage_log_info info;
+
+ for (int i=0; i < total_usage_entries; i++){
+ auto bucket = str_int("bucket", i);
+ info.entries.emplace_back(rgw_usage_log_entry(user, payer, bucket));
+ }
+
+ return info;
+}
+
+auto gen_usage_log_info(std::string payer, std::string bucket, int total_usage_entries)
+{
+ rgw_usage_log_info info;
+ for (int i=0; i < total_usage_entries; i++){
+ auto user = str_int("user", i);
+ info.entries.emplace_back(rgw_usage_log_entry(user, payer, bucket));
+ }
+
+ return info;
+}
+
+TEST_F(cls_rgw, usage_basic)
+{
+ string oid="usage.1";
+ string user="user1";
+ uint64_t start_epoch{0}, end_epoch{(uint64_t) -1};
+ int total_usage_entries = 512;
+ uint64_t max_entries = 2000;
+ string payer;
+
+ auto info = populate_usage_log_info(user, payer, total_usage_entries);
+ ObjectWriteOperation op;
+ cls_rgw_usage_log_add(op, info);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ string read_iter;
+ map <rgw_user_bucket, rgw_usage_log_entry> usage, usage2;
+ bool truncated;
+
+
+ int ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
+ max_entries, read_iter, usage, &truncated);
+ // read the entries, and see that we have all the added entries
+ ASSERT_EQ(0, ret);
+ ASSERT_FALSE(truncated);
+ ASSERT_EQ(static_cast<uint64_t>(total_usage_entries), usage.size());
+
+ // delete and read to assert that we've deleted all the values
+ ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, user, "", start_epoch, end_epoch));
+
+
+ ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
+ max_entries, read_iter, usage2, &truncated);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0u, usage2.size());
+
+ // add and read to assert that bucket option is valid for usage reading
+ string bucket1 = "bucket-usage-1";
+ string bucket2 = "bucket-usage-2";
+ info = gen_usage_log_info(payer, bucket1, 100);
+ cls_rgw_usage_log_add(op, info);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ info = gen_usage_log_info(payer, bucket2, 100);
+ cls_rgw_usage_log_add(op, info);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ ret = cls_rgw_usage_log_read(ioctx, oid, "", bucket1, start_epoch, end_epoch,
+ max_entries, read_iter, usage2, &truncated);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(100u, usage2.size());
+
+ // delete and read to assert that bucket option is valid for usage trim
+ ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, "", bucket1, start_epoch, end_epoch));
+
+ ret = cls_rgw_usage_log_read(ioctx, oid, "", bucket1, start_epoch, end_epoch,
+ max_entries, read_iter, usage2, &truncated);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0u, usage2.size());
+ ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, "", bucket2, start_epoch, end_epoch));
+}
+
+TEST_F(cls_rgw, usage_clear_no_obj)
+{
+ string user="user1";
+ string oid="usage.10";
+ librados::ObjectWriteOperation op;
+ cls_rgw_usage_log_clear(op);
+ int ret = ioctx.operate(oid, &op);
+ ASSERT_EQ(0, ret);
+
+}
+
+TEST_F(cls_rgw, usage_clear)
+{
+ string user="user1";
+ string payer;
+ string oid="usage.10";
+ librados::ObjectWriteOperation op;
+ int max_entries=2000;
+
+ auto info = populate_usage_log_info(user, payer, max_entries);
+
+ cls_rgw_usage_log_add(op, info);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ ObjectWriteOperation op2;
+ cls_rgw_usage_log_clear(op2);
+ int ret = ioctx.operate(oid, &op2);
+ ASSERT_EQ(0, ret);
+
+ map <rgw_user_bucket, rgw_usage_log_entry> usage;
+ bool truncated;
+ uint64_t start_epoch{0}, end_epoch{(uint64_t) -1};
+ string read_iter;
+ ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
+ max_entries, read_iter, usage, &truncated);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0u, usage.size());
+}
+
+static int bilog_list(librados::IoCtx& ioctx, const std::string& oid,
+ cls_rgw_bi_log_list_ret *result)
+{
+ int retcode = 0;
+ librados::ObjectReadOperation op;
+ cls_rgw_bilog_list(op, "", 128, result, &retcode);
+ int ret = ioctx.operate(oid, &op, nullptr);
+ if (ret < 0) {
+ return ret;
+ }
+ return retcode;
+}
+
+static int bilog_trim(librados::IoCtx& ioctx, const std::string& oid,
+ const std::string& start_marker,
+ const std::string& end_marker)
+{
+ librados::ObjectWriteOperation op;
+ cls_rgw_bilog_trim(op, start_marker, end_marker);
+ return ioctx.operate(oid, &op);
+}
+
+TEST_F(cls_rgw, bi_log_trim)
+{
+ string bucket_oid = str_int("bucket", 6);
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ // create 10 versioned entries. this generates instance and olh bi entries,
+ // allowing us to check that bilog trim doesn't remove any of those
+ for (int i = 0; i < 10; i++) {
+ cls_rgw_obj_key obj{str_int("obj", i), "inst"};
+ string tag = str_int("tag", i);
+ string loc = str_int("loc", i);
+
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+ rgw_bucket_dir_entry_meta meta;
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, 1, obj, meta);
+ }
+ // bi list
+ {
+ list<rgw_cls_bi_entry> entries;
+ bool truncated{false};
+ ASSERT_EQ(0, cls_rgw_bi_list(ioctx, bucket_oid, "", "", 128,
+ &entries, &truncated));
+ // prepare/complete/instance/olh entry for each
+ EXPECT_EQ(40u, entries.size());
+ EXPECT_FALSE(truncated);
+ }
+ // bilog list
+ vector<rgw_bi_log_entry> bilog1;
+ {
+ cls_rgw_bi_log_list_ret bilog;
+ ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
+ // complete/olh entry for each
+ EXPECT_EQ(20u, bilog.entries.size());
+
+ bilog1.assign(std::make_move_iterator(bilog.entries.begin()),
+ std::make_move_iterator(bilog.entries.end()));
+ }
+ // trim front of bilog
+ {
+ const std::string from = "";
+ const std::string to = bilog1[0].id;
+ ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
+ cls_rgw_bi_log_list_ret bilog;
+ ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
+ EXPECT_EQ(19u, bilog.entries.size());
+ EXPECT_EQ(bilog1[1].id, bilog.entries.begin()->id);
+ ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
+ }
+ // trim back of bilog
+ {
+ const std::string from = bilog1[18].id;
+ const std::string to = "9";
+ ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
+ cls_rgw_bi_log_list_ret bilog;
+ ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
+ EXPECT_EQ(18u, bilog.entries.size());
+ EXPECT_EQ(bilog1[18].id, bilog.entries.rbegin()->id);
+ ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
+ }
+ // trim middle of bilog
+ {
+ const std::string from = bilog1[13].id;
+ const std::string to = bilog1[14].id;
+ ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
+ cls_rgw_bi_log_list_ret bilog;
+ ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
+ EXPECT_EQ(17u, bilog.entries.size());
+ ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
+ }
+ // trim full bilog
+ {
+ const std::string from = "";
+ const std::string to = "9";
+ ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
+ cls_rgw_bi_log_list_ret bilog;
+ ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
+ EXPECT_EQ(0u, bilog.entries.size());
+ ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
+ }
+ // bi list should be the same
+ {
+ list<rgw_cls_bi_entry> entries;
+ bool truncated{false};
+ ASSERT_EQ(0, cls_rgw_bi_list(ioctx, bucket_oid, "", "", 128,
+ &entries, &truncated));
+ EXPECT_EQ(40u, entries.size());
+ EXPECT_FALSE(truncated);
+ }
+}
+
+TEST_F(cls_rgw, index_racing_removes)
+{
+ string bucket_oid = str_int("bucket", 8);
+
+ ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
+
+ int epoch = 0;
+ rgw_bucket_dir_entry dirent;
+ rgw_bucket_dir_entry_meta meta;
+
+ // prepare/complete add for single object
+ const cls_rgw_obj_key obj{"obj"};
+ std::string loc = "loc";
+ {
+ std::string tag = "tag-add";
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, 1, 0);
+ }
+
+ // list to verify no pending ops
+ {
+ std::map<int, rgw_cls_list_ret> results;
+ list_entries(ioctx, bucket_oid, 1, results);
+ ASSERT_EQ(1, results.size());
+ const auto& entries = results.begin()->second.dir.m;
+ ASSERT_EQ(1, entries.size());
+ dirent = std::move(entries.begin()->second);
+ ASSERT_EQ(obj, dirent.key);
+ ASSERT_TRUE(dirent.exists);
+ ASSERT_TRUE(dirent.pending_map.empty());
+ }
+
+ // prepare three racing removals
+ std::string tag1 = "tag-rm1";
+ std::string tag2 = "tag-rm2";
+ std::string tag3 = "tag-rm3";
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag1, obj, loc);
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag2, obj, loc);
+ index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag3, obj, loc);
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, 1, 0);
+
+ // complete on tag2
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag2, ++epoch, obj, meta);
+ {
+ std::map<int, rgw_cls_list_ret> results;
+ list_entries(ioctx, bucket_oid, 1, results);
+ ASSERT_EQ(1, results.size());
+ const auto& entries = results.begin()->second.dir.m;
+ ASSERT_EQ(1, entries.size());
+ dirent = std::move(entries.begin()->second);
+ ASSERT_EQ(obj, dirent.key);
+ ASSERT_FALSE(dirent.exists);
+ ASSERT_FALSE(dirent.pending_map.empty());
+ }
+
+ // cancel on tag1
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_CANCEL, tag1, ++epoch, obj, meta);
+ {
+ std::map<int, rgw_cls_list_ret> results;
+ list_entries(ioctx, bucket_oid, 1, results);
+ ASSERT_EQ(1, results.size());
+ const auto& entries = results.begin()->second.dir.m;
+ ASSERT_EQ(1, entries.size());
+ dirent = std::move(entries.begin()->second);
+ ASSERT_EQ(obj, dirent.key);
+ ASSERT_FALSE(dirent.exists);
+ ASSERT_FALSE(dirent.pending_map.empty());
+ }
+
+ // final cancel on tag3
+ index_complete(ioctx, bucket_oid, CLS_RGW_OP_CANCEL, tag3, ++epoch, obj, meta);
+
+ // verify that the key was removed
+ {
+ std::map<int, rgw_cls_list_ret> results;
+ list_entries(ioctx, bucket_oid, 1, results);
+ EXPECT_EQ(1, results.size());
+ const auto& entries = results.begin()->second.dir.m;
+ ASSERT_EQ(0, entries.size());
+ }
+
+ test_stats(ioctx, bucket_oid, RGWObjCategory::None, 0, 0);
+}
diff --git a/src/test/cls_rgw/test_cls_rgw_stats.cc b/src/test/cls_rgw/test_cls_rgw_stats.cc
new file mode 100644
index 000000000..004ccc6d1
--- /dev/null
+++ b/src/test/cls_rgw/test_cls_rgw_stats.cc
@@ -0,0 +1,646 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <vector>
+#include <boost/circular_buffer.hpp>
+#include <boost/intrusive/set.hpp>
+#include <gtest/gtest.h>
+#include "cls/rgw/cls_rgw_client.h"
+#include "common/debug.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "common/random_string.h"
+#include "global/global_context.h"
+#include "test/librados/test_cxx.h"
+
+#define dout_subsys ceph_subsys_rgw
+#define dout_context g_ceph_context
+
+
+// simulation parameters:
+
+// total number of index operations to prepare
+constexpr size_t max_operations = 2048;
+// total number of object names. each operation chooses one at random
+constexpr size_t max_entries = 32;
+// maximum number of pending operations. once the limit is reached, the oldest
+// pending operation is finished before another can start
+constexpr size_t max_pending = 16;
+// object size is randomly distributed between 0 and 4M
+constexpr size_t max_object_size = 4*1024*1024;
+// multipart upload threshold
+constexpr size_t max_part_size = 1024*1024;
+
+
+// create/destroy a pool that's shared by all tests in the process
+struct RadosEnv : public ::testing::Environment {
+ static std::optional<std::string> pool_name;
+ public:
+ static librados::Rados rados;
+ static librados::IoCtx ioctx;
+
+ void SetUp() override {
+ // create pool
+ std::string name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(name, rados));
+ pool_name = name;
+ ASSERT_EQ(rados.ioctx_create(name.c_str(), ioctx), 0);
+ }
+ void TearDown() override {
+ ioctx.close();
+ if (pool_name) {
+ ASSERT_EQ(destroy_one_pool_pp(*pool_name, rados), 0);
+ }
+ }
+};
+std::optional<std::string> RadosEnv::pool_name;
+librados::Rados RadosEnv::rados;
+librados::IoCtx RadosEnv::ioctx;
+
+auto *const rados_env = ::testing::AddGlobalTestEnvironment(new RadosEnv);
+
+
+std::ostream& operator<<(std::ostream& out, const rgw_bucket_category_stats& c) {
+ return out << "{count=" << c.num_entries << " size=" << c.total_size << '}';
+}
+
+
+// librados helpers
+rgw_bucket_entry_ver last_version(librados::IoCtx& ioctx)
+{
+ rgw_bucket_entry_ver ver;
+ ver.pool = ioctx.get_id();
+ ver.epoch = ioctx.get_last_version();
+ return ver;
+}
+
+int index_init(librados::IoCtx& ioctx, const std::string& oid)
+{
+ librados::ObjectWriteOperation op;
+ cls_rgw_bucket_init_index(op);
+ return ioctx.operate(oid, &op);
+}
+
+int index_prepare(librados::IoCtx& ioctx, const std::string& oid,
+ const cls_rgw_obj_key& key, const std::string& tag,
+ RGWModifyOp type)
+{
+ librados::ObjectWriteOperation op;
+ const std::string loc; // empty
+ constexpr bool log_op = false;
+ constexpr int flags = 0;
+ rgw_zone_set zones;
+ cls_rgw_bucket_prepare_op(op, type, tag, key, loc, log_op, flags, zones);
+ return ioctx.operate(oid, &op);
+}
+
+int index_complete(librados::IoCtx& ioctx, const std::string& oid,
+ const cls_rgw_obj_key& key, const std::string& tag,
+ RGWModifyOp type, const rgw_bucket_entry_ver& ver,
+ const rgw_bucket_dir_entry_meta& meta,
+ std::list<cls_rgw_obj_key>* remove_objs)
+{
+ librados::ObjectWriteOperation op;
+ constexpr bool log_op = false;
+ constexpr int flags = 0;
+ constexpr rgw_zone_set* zones = nullptr;
+ cls_rgw_bucket_complete_op(op, type, tag, ver, key, meta,
+ remove_objs, log_op, flags, zones);
+ return ioctx.operate(oid, &op);
+}
+
+void read_stats(librados::IoCtx& ioctx, const std::string& oid,
+ rgw_bucket_dir_stats& stats)
+{
+ auto oids = std::map<int, std::string>{{0, oid}};
+ std::map<int, rgw_cls_list_ret> results;
+ ASSERT_EQ(0, CLSRGWIssueGetDirHeader(ioctx, oids, results, 8)());
+ ASSERT_EQ(1, results.size());
+ stats = std::move(results.begin()->second.dir.header.stats);
+}
+
+static void account_entry(rgw_bucket_dir_stats& stats,
+ const rgw_bucket_dir_entry_meta& meta)
+{
+ rgw_bucket_category_stats& c = stats[meta.category];
+ c.num_entries++;
+ c.total_size += meta.accounted_size;
+ c.total_size_rounded += cls_rgw_get_rounded_size(meta.accounted_size);
+ c.actual_size += meta.size;
+}
+
+static void unaccount_entry(rgw_bucket_dir_stats& stats,
+ const rgw_bucket_dir_entry_meta& meta)
+{
+ rgw_bucket_category_stats& c = stats[meta.category];
+ c.num_entries--;
+ c.total_size -= meta.accounted_size;
+ c.total_size_rounded -= cls_rgw_get_rounded_size(meta.accounted_size);
+ c.actual_size -= meta.size;
+}
+
+
+// a map of cached dir entries representing the expected state of cls_rgw
+struct object : rgw_bucket_dir_entry, boost::intrusive::set_base_hook<> {
+ explicit object(const cls_rgw_obj_key& key) {
+ this->key = key;
+ }
+};
+
+struct object_key {
+ using type = cls_rgw_obj_key;
+ const type& operator()(const object& o) const { return o.key; }
+};
+
+using object_map_base = boost::intrusive::set<object,
+ boost::intrusive::key_of_value<object_key>>;
+
+struct object_map : object_map_base {
+ ~object_map() {
+ clear_and_dispose(std::default_delete<object>{});
+ }
+};
+
+
+// models a bucket index operation, starting with cls_rgw_bucket_prepare_op().
+// stores all of the state necessary to complete the operation, either with
+// cls_rgw_bucket_complete_op() or cls_rgw_suggest_changes(). uploads larger
+// than max_part_size are modeled as multipart uploads
+struct operation {
+ RGWModifyOp type;
+ cls_rgw_obj_key key;
+ std::string tag;
+ rgw_bucket_entry_ver ver;
+ std::string upload_id; // empty unless multipart
+ rgw_bucket_dir_entry_meta meta;
+};
+
+
+class simulator {
+ public:
+ simulator(librados::IoCtx& ioctx, std::string oid)
+ : ioctx(ioctx), oid(std::move(oid)), pending(max_pending)
+ {
+ // generate a set of object keys. each operation chooses one at random
+ keys.reserve(max_entries);
+ for (size_t i = 0; i < max_entries; i++) {
+ keys.emplace_back(gen_rand_alphanumeric_upper(g_ceph_context, 12));
+ }
+ }
+
+ void run();
+
+ private:
+ void start();
+ int try_start(const cls_rgw_obj_key& key,
+ const std::string& tag);
+
+ void finish(const operation& op);
+ void complete(const operation& op, RGWModifyOp type);
+ void suggest(const operation& op, char suggestion);
+
+ int init_multipart(const operation& op);
+ void complete_multipart(const operation& op);
+
+ object_map::iterator find_or_create(const cls_rgw_obj_key& key);
+
+ librados::IoCtx& ioctx;
+ std::string oid;
+
+ std::vector<cls_rgw_obj_key> keys;
+ object_map objects;
+ boost::circular_buffer<operation> pending;
+ rgw_bucket_dir_stats stats;
+};
+
+
+void simulator::run()
+{
+ // init the bucket index object
+ ASSERT_EQ(0, index_init(ioctx, oid));
+ // run the simulation for N steps
+ for (size_t i = 0; i < max_operations; i++) {
+ if (pending.full()) {
+ // if we're at max_pending, finish the oldest operation
+ auto& op = pending.front();
+ finish(op);
+ pending.pop_front();
+
+ // verify bucket stats
+ rgw_bucket_dir_stats stored_stats;
+ read_stats(ioctx, oid, stored_stats);
+
+ const rgw_bucket_dir_stats& expected_stats = stats;
+ ASSERT_EQ(expected_stats, stored_stats);
+ }
+
+ // initiate the next operation
+ start();
+
+ // verify bucket stats
+ rgw_bucket_dir_stats stored_stats;
+ read_stats(ioctx, oid, stored_stats);
+
+ const rgw_bucket_dir_stats& expected_stats = stats;
+ ASSERT_EQ(expected_stats, stored_stats);
+ }
+}
+
+object_map::iterator simulator::find_or_create(const cls_rgw_obj_key& key)
+{
+ object_map::insert_commit_data commit;
+ auto result = objects.insert_check(key, std::less<cls_rgw_obj_key>{}, commit);
+ if (result.second) { // inserting new entry
+ auto p = new object(key);
+ result.first = objects.insert_commit(*p, commit);
+ }
+ return result.first;
+}
+
+int simulator::try_start(const cls_rgw_obj_key& key, const std::string& tag)
+{
+ // choose randomly betwen create and delete
+ const auto type = static_cast<RGWModifyOp>(
+ ceph::util::generate_random_number<size_t, size_t>(CLS_RGW_OP_ADD,
+ CLS_RGW_OP_DEL));
+ auto op = operation{type, key, tag};
+
+ op.meta.category = RGWObjCategory::Main;
+ op.meta.size = op.meta.accounted_size =
+ ceph::util::generate_random_number(1, max_object_size);
+
+ if (type == CLS_RGW_OP_ADD && op.meta.size > max_part_size) {
+ // simulate multipart for uploads over the max_part_size threshold
+ op.upload_id = gen_rand_alphanumeric_upper(g_ceph_context, 12);
+
+ int r = init_multipart(op);
+ if (r != 0) {
+ derr << "> failed to prepare multipart upload key=" << key
+ << " upload=" << op.upload_id << " tag=" << tag
+ << " type=" << type << ": " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ dout(1) << "> prepared multipart upload key=" << key
+ << " upload=" << op.upload_id << " tag=" << tag
+ << " type=" << type << " size=" << op.meta.size << dendl;
+ } else {
+ // prepare operation
+ int r = index_prepare(ioctx, oid, op.key, op.tag, op.type);
+ if (r != 0) {
+ derr << "> failed to prepare operation key=" << key
+ << " tag=" << tag << " type=" << type
+ << ": " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ dout(1) << "> prepared operation key=" << key
+ << " tag=" << tag << " type=" << type
+ << " size=" << op.meta.size << dendl;
+ }
+ op.ver = last_version(ioctx);
+
+ ceph_assert(!pending.full());
+ pending.push_back(std::move(op));
+ return 0;
+}
+
+void simulator::start()
+{
+ // choose a random object key
+ const size_t index = ceph::util::generate_random_number(0, keys.size() - 1);
+ const auto& key = keys[index];
+ // generate a random tag
+ const auto tag = gen_rand_alphanumeric_upper(g_ceph_context, 12);
+
+ // retry until success. failures don't count towards max_operations
+ while (try_start(key, tag) != 0)
+ ;
+}
+
+void simulator::finish(const operation& op)
+{
+ if (op.type == CLS_RGW_OP_ADD && !op.upload_id.empty()) {
+ // multipart uploads either complete or abort based on part uploads
+ complete_multipart(op);
+ return;
+ }
+
+ // complete most operations, but finish some with cancel or dir suggest
+ constexpr int cancel_percent = 10;
+ constexpr int suggest_update_percent = 10;
+ constexpr int suggest_remove_percent = 10;
+
+ int result = ceph::util::generate_random_number(0, 99);
+ if (result < cancel_percent) {
+ complete(op, CLS_RGW_OP_CANCEL);
+ return;
+ }
+ result -= cancel_percent;
+ if (result < suggest_update_percent) {
+ suggest(op, CEPH_RGW_UPDATE);
+ return;
+ }
+ result -= suggest_update_percent;
+ if (result < suggest_remove_percent) {
+ suggest(op, CEPH_RGW_REMOVE);
+ return;
+ }
+ complete(op, op.type);
+}
+
+void simulator::complete(const operation& op, RGWModifyOp type)
+{
+ int r = index_complete(ioctx, oid, op.key, op.tag, type,
+ op.ver, op.meta, nullptr);
+ if (r != 0) {
+ derr << "< failed to complete operation key=" << op.key
+ << " tag=" << op.tag << " type=" << op.type
+ << " size=" << op.meta.size
+ << ": " << cpp_strerror(r) << dendl;
+ return;
+ }
+
+ if (type == CLS_RGW_OP_CANCEL) {
+ dout(1) << "< canceled operation key=" << op.key
+ << " tag=" << op.tag << " type=" << op.type
+ << " size=" << op.meta.size << dendl;
+ } else if (type == CLS_RGW_OP_ADD) {
+ auto obj = find_or_create(op.key);
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ obj->exists = true;
+ obj->meta = op.meta;
+ account_entry(stats, obj->meta);
+ dout(1) << "< completed write operation key=" << op.key
+ << " tag=" << op.tag << " type=" << type
+ << " size=" << op.meta.size << dendl;
+ } else {
+ ceph_assert(type == CLS_RGW_OP_DEL);
+ auto obj = objects.find(op.key, std::less<cls_rgw_obj_key>{});
+ if (obj != objects.end()) {
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ objects.erase_and_dispose(obj, std::default_delete<object>{});
+ }
+ dout(1) << "< completed delete operation key=" << op.key
+ << " tag=" << op.tag << " type=" << type << dendl;
+ }
+}
+
+void simulator::suggest(const operation& op, char suggestion)
+{
+ // read and decode the current dir entry
+ rgw_cls_bi_entry bi_entry;
+ int r = cls_rgw_bi_get(ioctx, oid, BIIndexType::Plain, op.key, &bi_entry);
+ if (r != 0) {
+ derr << "< no bi entry to suggest for operation key=" << op.key
+ << " tag=" << op.tag << " type=" << op.type
+ << " size=" << op.meta.size
+ << ": " << cpp_strerror(r) << dendl;
+ return;
+ }
+ ASSERT_EQ(bi_entry.type, BIIndexType::Plain);
+
+ rgw_bucket_dir_entry entry;
+ auto p = bi_entry.data.cbegin();
+ ASSERT_NO_THROW(decode(entry, p));
+
+ ASSERT_EQ(entry.key, op.key);
+
+ // clear pending info and write it back; this cancels those pending
+ // operations (we'll see EINVAL when we try to complete them), but dir
+ // suggest is ignored unless the pending_map is empty
+ entry.pending_map.clear();
+
+ bi_entry.data.clear();
+ encode(entry, bi_entry.data);
+
+ r = cls_rgw_bi_put(ioctx, oid, bi_entry);
+ ASSERT_EQ(0, r);
+
+ // now suggest changes for this entry
+ entry.ver = last_version(ioctx);
+ entry.exists = (suggestion == CEPH_RGW_UPDATE);
+ entry.meta = op.meta;
+
+ bufferlist update;
+ cls_rgw_encode_suggestion(suggestion, entry, update);
+
+ librados::ObjectWriteOperation write_op;
+ cls_rgw_suggest_changes(write_op, update);
+ r = ioctx.operate(oid, &write_op);
+ if (r != 0) {
+ derr << "< failed to suggest operation key=" << op.key
+ << " tag=" << op.tag << " type=" << op.type
+ << " size=" << op.meta.size
+ << ": " << cpp_strerror(r) << dendl;
+ return;
+ }
+
+ // update our cache accordingly
+ if (suggestion == CEPH_RGW_UPDATE) {
+ auto obj = find_or_create(op.key);
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ obj->exists = true;
+ obj->meta = op.meta;
+ account_entry(stats, obj->meta);
+ dout(1) << "< suggested update operation key=" << op.key
+ << " tag=" << op.tag << " type=" << op.type
+ << " size=" << op.meta.size << dendl;
+ } else {
+ ceph_assert(suggestion == CEPH_RGW_REMOVE);
+ auto obj = objects.find(op.key, std::less<cls_rgw_obj_key>{});
+ if (obj != objects.end()) {
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ objects.erase_and_dispose(obj, std::default_delete<object>{});
+ }
+ dout(1) << "< suggested remove operation key=" << op.key
+ << " tag=" << op.tag << " type=" << op.type << dendl;
+ }
+}
+
+int simulator::init_multipart(const operation& op)
+{
+ // create (not just prepare) the meta object
+ const auto meta_key = cls_rgw_obj_key{
+ fmt::format("_multipart_{}.2~{}.meta", op.key.name, op.upload_id)};
+ const std::string empty_tag; // empty tag enables complete without prepare
+ const rgw_bucket_entry_ver empty_ver;
+ rgw_bucket_dir_entry_meta meta_meta;
+ meta_meta.category = RGWObjCategory::MultiMeta;
+ int r = index_complete(ioctx, oid, meta_key, empty_tag, CLS_RGW_OP_ADD,
+ empty_ver, meta_meta, nullptr);
+ if (r != 0) {
+ derr << " < failed to create multipart meta key=" << meta_key
+ << ": " << cpp_strerror(r) << dendl;
+ return r;
+ } else {
+ // account for meta object
+ auto obj = find_or_create(meta_key);
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ obj->exists = true;
+ obj->meta = meta_meta;
+ account_entry(stats, obj->meta);
+ }
+
+ // prepare part uploads
+ std::list<cls_rgw_obj_key> remove_objs;
+ size_t part_id = 0;
+
+ size_t remaining = op.meta.size;
+ while (remaining > max_part_size) {
+ remaining -= max_part_size;
+ const auto part_size = std::min(remaining, max_part_size);
+ const auto part_key = cls_rgw_obj_key{
+ fmt::format("_multipart_{}.2~{}.{}", op.key.name, op.upload_id, part_id)};
+ part_id++;
+
+ r = index_prepare(ioctx, oid, part_key, op.tag, op.type);
+ if (r != 0) {
+ // if part prepare fails, remove the meta object and remove_objs
+ [[maybe_unused]] int ignored =
+ index_complete(ioctx, oid, meta_key, empty_tag, CLS_RGW_OP_DEL,
+ empty_ver, meta_meta, &remove_objs);
+ derr << " > failed to prepare part key=" << part_key
+ << " size=" << part_size << dendl;
+ return r; // return the error from prepare
+ }
+ dout(1) << " > prepared part key=" << part_key
+ << " size=" << part_size << dendl;
+ remove_objs.push_back(part_key);
+ }
+ return 0;
+}
+
+void simulator::complete_multipart(const operation& op)
+{
+ const std::string empty_tag; // empty tag enables complete without prepare
+ const rgw_bucket_entry_ver empty_ver;
+
+ // try to finish part uploads
+ size_t part_id = 0;
+ std::list<cls_rgw_obj_key> remove_objs;
+
+ RGWModifyOp type = op.type; // OP_ADD, or OP_CANCEL for abort
+
+ size_t remaining = op.meta.size;
+ while (remaining > max_part_size) {
+ remaining -= max_part_size;
+ const auto part_size = std::min(remaining, max_part_size);
+ const auto part_key = cls_rgw_obj_key{
+ fmt::format("_multipart_{}.2~{}.{}", op.key.name, op.upload_id, part_id)};
+ part_id++;
+
+ // cancel 10% of part uploads (and abort the multipart upload)
+ constexpr int cancel_percent = 10;
+ const int result = ceph::util::generate_random_number(0, 99);
+ if (result < cancel_percent) {
+ type = CLS_RGW_OP_CANCEL; // abort multipart
+ dout(1) << " < canceled part key=" << part_key
+ << " size=" << part_size << dendl;
+ } else {
+ rgw_bucket_dir_entry_meta meta;
+ meta.category = op.meta.category;
+ meta.size = meta.accounted_size = part_size;
+
+ int r = index_complete(ioctx, oid, part_key, op.tag, op.type,
+ empty_ver, meta, nullptr);
+ if (r != 0) {
+ derr << " < failed to complete part key=" << part_key
+ << " size=" << meta.size << ": " << cpp_strerror(r) << dendl;
+ type = CLS_RGW_OP_CANCEL; // abort multipart
+ } else {
+ dout(1) << " < completed part key=" << part_key
+ << " size=" << meta.size << dendl;
+ // account for successful part upload
+ auto obj = find_or_create(part_key);
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ obj->exists = true;
+ obj->meta = meta;
+ account_entry(stats, obj->meta);
+ }
+ }
+ remove_objs.push_back(part_key);
+ }
+
+ // delete the multipart meta object
+ const auto meta_key = cls_rgw_obj_key{
+ fmt::format("_multipart_{}.2~{}.meta", op.key.name, op.upload_id)};
+ rgw_bucket_dir_entry_meta meta_meta;
+ meta_meta.category = RGWObjCategory::MultiMeta;
+
+ int r = index_complete(ioctx, oid, meta_key, empty_tag, CLS_RGW_OP_DEL,
+ empty_ver, meta_meta, nullptr);
+ if (r != 0) {
+ derr << " < failed to remove multipart meta key=" << meta_key
+ << ": " << cpp_strerror(r) << dendl;
+ } else {
+ // unaccount for meta object
+ auto obj = objects.find(meta_key, std::less<cls_rgw_obj_key>{});
+ if (obj != objects.end()) {
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ objects.erase_and_dispose(obj, std::default_delete<object>{});
+ }
+ }
+
+ // create or cancel the head object
+ r = index_complete(ioctx, oid, op.key, empty_tag, type,
+ empty_ver, op.meta, &remove_objs);
+ if (r != 0) {
+ derr << "< failed to complete multipart upload key=" << op.key
+ << " upload=" << op.upload_id << " tag=" << op.tag
+ << " type=" << type << " size=" << op.meta.size
+ << ": " << cpp_strerror(r) << dendl;
+ return;
+ }
+
+ if (type == CLS_RGW_OP_ADD) {
+ dout(1) << "< completed multipart upload key=" << op.key
+ << " upload=" << op.upload_id << " tag=" << op.tag
+ << " type=" << op.type << " size=" << op.meta.size << dendl;
+
+ // account for head stats
+ auto obj = find_or_create(op.key);
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ obj->exists = true;
+ obj->meta = op.meta;
+ account_entry(stats, obj->meta);
+ } else {
+ dout(1) << "< canceled multipart upload key=" << op.key
+ << " upload=" << op.upload_id << " tag=" << op.tag
+ << " type=" << op.type << " size=" << op.meta.size << dendl;
+ }
+
+ // unaccount for remove_objs
+ for (const auto& part_key : remove_objs) {
+ auto obj = objects.find(part_key, std::less<cls_rgw_obj_key>{});
+ if (obj != objects.end()) {
+ if (obj->exists) {
+ unaccount_entry(stats, obj->meta);
+ }
+ objects.erase_and_dispose(obj, std::default_delete<object>{});
+ }
+ }
+}
+
+TEST(cls_rgw_stats, simulate)
+{
+ const char* bucket_oid = __func__;
+ auto sim = simulator{RadosEnv::ioctx, bucket_oid};
+ sim.run();
+}
diff --git a/src/test/cls_rgw_gc/CMakeLists.txt b/src/test/cls_rgw_gc/CMakeLists.txt
new file mode 100644
index 000000000..dd1661525
--- /dev/null
+++ b/src/test/cls_rgw_gc/CMakeLists.txt
@@ -0,0 +1,18 @@
+if(${WITH_RADOSGW})
+ add_executable(ceph_test_cls_rgw_gc
+ test_cls_rgw_gc.cc
+ )
+ target_link_libraries(ceph_test_cls_rgw_gc
+ cls_rgw_gc_client
+ librados
+ global
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx)
+ install(TARGETS
+ ceph_test_cls_rgw_gc
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(${WITH_RADOSGW})
+
diff --git a/src/test/cls_rgw_gc/test_cls_rgw_gc.cc b/src/test/cls_rgw_gc/test_cls_rgw_gc.cc
new file mode 100644
index 000000000..24df4ce5c
--- /dev/null
+++ b/src/test/cls_rgw_gc/test_cls_rgw_gc.cc
@@ -0,0 +1,700 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+
+#include "cls/rgw/cls_rgw_types.h"
+#include "cls/rgw_gc/cls_rgw_gc_client.h"
+#include "cls/rgw_gc/cls_rgw_gc_ops.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+#include "global/global_context.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+using namespace std;
+using namespace librados;
+
+librados::Rados rados;
+librados::IoCtx ioctx;
+string pool_name;
+
+
+/* must be the first test! */
+TEST(cls_rgw_gc, init)
+{
+ pool_name = get_temp_pool_name();
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+}
+
+
+string str_int(string s, int i)
+{
+ char buf[32];
+ snprintf(buf, sizeof(buf), "-%d", i);
+ s.append(buf);
+
+ return s;
+}
+
+/* test garbage collection */
+static void create_obj(cls_rgw_obj& obj, int i, int j)
+{
+ char buf[32];
+ snprintf(buf, sizeof(buf), "-%d.%d", i, j);
+ obj.pool = "pool";
+ obj.pool.append(buf);
+ obj.key.name = "oid";
+ obj.key.name.append(buf);
+ obj.loc = "loc";
+ obj.loc.append(buf);
+}
+
+TEST(cls_rgw_gc, gc_queue_ops1)
+{
+ //Testing queue ops when data size is NOT a multiple of queue size
+ string queue_name = "my-queue";
+ uint64_t queue_size = 322, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(queue_size, size);
+
+ //Test enqueue
+ for (int i = 0; i < 2; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 0, info);
+ if (i == 1) {
+ ASSERT_EQ(-ENOSPC, ioctx.operate(queue_name, &op));
+ } else {
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ }
+
+ //Test remove queue entries
+ librados::ObjectWriteOperation remove_op;
+ string marker1;
+ uint64_t num_entries = 1;
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ //Test enqueue again
+ for (int i = 0; i < 1; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 0, info);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+
+ //Test list queue
+ list<cls_rgw_gc_obj_info> list_info1;
+ string marker, next_marker;
+ uint64_t max = 1;
+ bool expired_only = false, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(1, list_info1.size());
+
+ for (auto it : list_info1) {
+ std::cerr << "[ ] list info tag = " << it.tag << std::endl;
+ ASSERT_EQ("chain-0", it.tag);
+ }
+}
+
+TEST(cls_rgw_gc, gc_queue_ops2)
+{
+ //Testing list queue
+ string queue_name = "my-second-queue";
+ uint64_t queue_size = 334, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ //Test list queue, when queue is empty
+ list<cls_rgw_gc_obj_info> list_info;
+ string marker1, next_marker1;
+ uint64_t max1 = 2;
+ bool expired_only1 = false, truncated1;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker1, max1, expired_only1, list_info, &truncated1, next_marker1);
+ ASSERT_EQ(0, list_info.size());
+
+ //Test enqueue
+ for (int i = 0; i < 3; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 0, info);
+ if (i == 2) {
+ ASSERT_EQ(-ENOSPC, ioctx.operate(queue_name, &op));
+ } else {
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ }
+
+ //Test list queue
+ list<cls_rgw_gc_obj_info> list_info1, list_info2, list_info3;
+ string marker, next_marker;
+ uint64_t max = 2;
+ bool expired_only = false, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(2, list_info1.size());
+
+ int i = 0;
+ for (auto it : list_info1) {
+ string tag = "chain-" + to_string(i);
+ ASSERT_EQ(tag, it.tag);
+ i++;
+ }
+
+ max = 1;
+ truncated = false;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info2, &truncated, next_marker);
+ auto it = list_info2.front();
+ ASSERT_EQ(1, list_info2.size());
+ ASSERT_EQ(true, truncated);
+ ASSERT_EQ("chain-0", it.tag);
+ std::cerr << "[ ] next_marker is: = " << next_marker << std::endl;
+
+ marker = next_marker;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info3, &truncated, next_marker);
+ it = list_info3.front();
+ ASSERT_EQ(1, list_info3.size());
+ ASSERT_EQ(false, truncated);
+ ASSERT_EQ("chain-1", it.tag);
+}
+
+#if 0 // TODO: fix or remove defer_gc()
+TEST(cls_rgw_gc, gc_queue_ops3)
+{
+ //Testing remove queue entries
+ string queue_name = "my-third-queue";
+ uint64_t queue_size = 501, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ //Test remove queue, when queue is empty
+ librados::ObjectWriteOperation remove_op;
+ string marker1;
+ uint64_t num_entries = 2;
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ cls_rgw_gc_obj_info defer_info;
+
+ //Test enqueue
+ for (int i = 0; i < 2; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 5, info);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ if (i == 0)
+ defer_info = info;
+ }
+
+ //Test defer entry for 1st element
+ librados::ObjectWriteOperation defer_op;
+ cls_rgw_gc_queue_defer_entry(defer_op, 10, defer_info);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &defer_op));
+
+ //Test list queue
+ list<cls_rgw_gc_obj_info> list_info1, list_info2;
+ string marker, next_marker;
+ uint64_t max = 2;
+ bool expired_only = false, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(2, list_info1.size());
+
+ int i = 0;
+ for (auto it : list_info1) {
+ std::cerr << "[ ] list info tag = " << it.tag << std::endl;
+ if (i == 0) {
+ ASSERT_EQ("chain-1", it.tag);
+ }
+ if (i == 1) {
+ ASSERT_EQ("chain-0", it.tag);
+ }
+ i++;
+ }
+
+ //Test remove entries
+ num_entries = 2;
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ //Test list queue again
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info2, &truncated, next_marker);
+ ASSERT_EQ(0, list_info2.size());
+
+}
+
+TEST(cls_rgw_gc, gc_queue_ops4)
+{
+ //Testing remove queue entries
+ string queue_name = "my-fourth-queue";
+ uint64_t queue_size = 501, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ //Test remove queue, when queue is empty
+ librados::ObjectWriteOperation remove_op;
+ string marker1;
+ uint64_t num_entries = 2;
+
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ cls_rgw_gc_obj_info defer_info;
+
+ //Test enqueue
+ for (int i = 0; i < 2; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 5, info);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ defer_info = info;
+ }
+
+ //Test defer entry for last element
+ librados::ObjectWriteOperation defer_op;
+ cls_rgw_gc_queue_defer_entry(defer_op, 10, defer_info);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &defer_op));
+
+ //Test list queue
+ list<cls_rgw_gc_obj_info> list_info1, list_info2;
+ string marker, next_marker;
+ uint64_t max = 2;
+ bool expired_only = false, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(2, list_info1.size());
+
+ int i = 0;
+ for (auto it : list_info1) {
+ string tag = "chain-" + to_string(i);
+ ASSERT_EQ(tag, it.tag);
+ i++;
+ }
+
+ //Test remove entries
+ num_entries = 2;
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ //Test list queue again
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info2, &truncated, next_marker);
+ ASSERT_EQ(0, list_info2.size());
+
+}
+#endif // defer_gc() disabled
+
+TEST(cls_rgw_gc, gc_queue_ops5)
+{
+ //Testing remove queue entries
+ string queue_name = "my-fifth-queue";
+ uint64_t queue_size = 501, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ //Test enqueue
+ for (int i = 0; i < 3; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ if (i == 2) {
+ cls_rgw_gc_queue_enqueue(op, 300, info);
+ } else {
+ cls_rgw_gc_queue_enqueue(op, 0, info);
+ }
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+ //Test list queue for expired entries only
+ list<cls_rgw_gc_obj_info> list_info1, list_info2;
+ string marker, next_marker, marker1;
+ uint64_t max = 10;
+ bool expired_only = true, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(2, list_info1.size());
+
+ int i = 0;
+ for (auto it : list_info1) {
+ string tag = "chain-" + to_string(i);
+ ASSERT_EQ(tag, it.tag);
+ i++;
+ }
+
+ //Test remove entries
+ librados::ObjectWriteOperation remove_op;
+ auto num_entries = list_info1.size();
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ //Test list queue again for all entries
+ expired_only = false;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info2, &truncated, next_marker);
+ ASSERT_EQ(1, list_info2.size());
+
+}
+
+TEST(cls_rgw_gc, gc_queue_ops6)
+{
+ //Testing list queue, when data size is split at the end of the queue
+ string queue_name = "my-sixth-queue";
+ uint64_t queue_size = 341, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ //Test enqueue
+ for (int i = 0; i < 2; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+
+ //Remove one element from queue
+ librados::ObjectWriteOperation remove_op;
+ string marker1;
+ uint64_t num_entries = 1;
+
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ //Enqueue one more element
+ librados::ObjectWriteOperation enq_op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, 2, 1);
+ create_obj(obj2, 2, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = "chain-2";
+ cls_rgw_gc_queue_enqueue(enq_op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(queue_name, &enq_op));
+
+ //Test list queue
+ list<cls_rgw_gc_obj_info> list_info1, list_info2, list_info3;
+ string marker, next_marker;
+ uint64_t max = 2;
+ bool expired_only = false, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(2, list_info1.size());
+
+ int i = 1;
+ for (auto it : list_info1) {
+ string tag = "chain-" + to_string(i);
+ ASSERT_EQ(tag, it.tag);
+ i++;
+ }
+}
+
+TEST(cls_rgw_gc, gc_queue_ops7)
+{
+ //Testing list queue, when data size is written at the end of queue and data is written after wrap around
+ string queue_name = "my-seventh-queue";
+ uint64_t queue_size = 342, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ //Test enqueue
+ for (int i = 0; i < 2; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+
+ //Remove one element from queue
+ librados::ObjectWriteOperation remove_op;
+ string marker1;
+ uint64_t num_entries = 1;
+
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ //Enqueue one more element
+ librados::ObjectWriteOperation enq_op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, 2, 1);
+ create_obj(obj2, 2, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = "chain-2";
+ cls_rgw_gc_queue_enqueue(enq_op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(queue_name, &enq_op));
+
+ //Test list queue
+ list<cls_rgw_gc_obj_info> list_info1, list_info2, list_info3;
+ string marker, next_marker;
+ uint64_t max = 2;
+ bool expired_only = false, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(2, list_info1.size());
+
+ int i = 1;
+ for (auto it : list_info1) {
+ string tag = "chain-" + to_string(i);
+ ASSERT_EQ(tag, it.tag);
+ i++;
+ }
+}
+
+TEST(cls_rgw_gc, gc_queue_ops8)
+{
+ //Testing list queue, when data is split at the end of the queue
+ string queue_name = "my-eighth-queue";
+ uint64_t queue_size = 344, num_urgent_data_entries = 10;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ //Test enqueue
+ for (int i = 0; i < 2; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ }
+
+ //Remove one element from queue
+ librados::ObjectWriteOperation remove_op;
+ string marker1;
+ uint64_t num_entries = 1;
+
+ cls_rgw_gc_queue_remove_entries(remove_op, num_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &remove_op));
+
+ //Enqueue one more element
+ librados::ObjectWriteOperation enq_op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, 2, 1);
+ create_obj(obj2, 2, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = "chain-2";
+ cls_rgw_gc_queue_enqueue(enq_op, 0, info);
+
+ ASSERT_EQ(0, ioctx.operate(queue_name, &enq_op));
+
+ //Test list queue
+ list<cls_rgw_gc_obj_info> list_info1, list_info2, list_info3;
+ string marker, next_marker;
+ uint64_t max = 2;
+ bool expired_only = false, truncated;
+ cls_rgw_gc_queue_list_entries(ioctx, queue_name, marker, max, expired_only, list_info1, &truncated, next_marker);
+ ASSERT_EQ(2, list_info1.size());
+
+ int i = 1;
+ for (auto it : list_info1) {
+ string tag = "chain-" + to_string(i);
+ ASSERT_EQ(tag, it.tag);
+ i++;
+ }
+}
+
+TEST(cls_rgw_gc, gc_queue_ops9)
+{
+ //Testing remove queue entries
+ string queue_name = "my-ninth-queue";
+ uint64_t queue_size = 668, num_urgent_data_entries = 1;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_rgw_gc_queue_init(op, queue_size, num_urgent_data_entries);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+
+ uint64_t size = 0;
+ int ret = cls_rgw_gc_queue_get_capacity(ioctx, queue_name, size);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(size, queue_size);
+
+ cls_rgw_gc_obj_info defer_info1, defer_info2;
+
+ //Test enqueue
+ for (int i = 0; i < 2; i++) {
+ string tag = "chain-" + to_string(i);
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+
+ cls_rgw_obj obj1, obj2;
+ create_obj(obj1, i, 1);
+ create_obj(obj2, i, 2);
+ info.chain.objs.push_back(obj1);
+ info.chain.objs.push_back(obj2);
+
+ info.tag = tag;
+ cls_rgw_gc_queue_enqueue(op, 5, info);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &op));
+ if (i == 0) {
+ defer_info1 = info;
+ }
+ if (i == 1) {
+ defer_info2 = info;
+ }
+ }
+
+ //Test defer entry for last element
+ librados::ObjectWriteOperation defer_op;
+ cls_rgw_gc_queue_defer_entry(defer_op, 10, defer_info2);
+ ASSERT_EQ(0, ioctx.operate(queue_name, &defer_op));
+
+ //Test defer entry for first element
+ cls_rgw_gc_queue_defer_entry(defer_op, 10, defer_info1);
+ ASSERT_EQ(-ENOSPC, ioctx.operate(queue_name, &defer_op));
+}
+
+/* must be last test! */
+TEST(cls_rgw_gc, finalize)
+{
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+}
diff --git a/src/test/cls_sdk/CMakeLists.txt b/src/test/cls_sdk/CMakeLists.txt
new file mode 100644
index 000000000..68085af28
--- /dev/null
+++ b/src/test/cls_sdk/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_executable(ceph_test_cls_sdk
+ test_cls_sdk.cc
+ )
+target_link_libraries(ceph_test_cls_sdk
+ librados
+ global
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ )
+install(TARGETS
+ ceph_test_cls_sdk
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/cls_sdk/test_cls_sdk.cc b/src/test/cls_sdk/test_cls_sdk.cc
new file mode 100644
index 000000000..44b32196b
--- /dev/null
+++ b/src/test/cls_sdk/test_cls_sdk.cc
@@ -0,0 +1,35 @@
+#include <iostream>
+#include <errno.h>
+
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+using namespace librados;
+
+TEST(ClsSDK, TestSDKCoverageWrite) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist in, out;
+ ASSERT_EQ(0, ioctx.exec("myobject", "sdk", "test_coverage_write", in, out));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsSDK, TestSDKCoverageReplay) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist in, out;
+ ASSERT_EQ(0, ioctx.exec("myobject", "sdk", "test_coverage_write", in, out));
+ ASSERT_EQ(0, ioctx.exec("myobject", "sdk", "test_coverage_replay", in, out));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
diff --git a/src/test/cls_version/CMakeLists.txt b/src/test/cls_version/CMakeLists.txt
new file mode 100644
index 000000000..05264017b
--- /dev/null
+++ b/src/test/cls_version/CMakeLists.txt
@@ -0,0 +1,16 @@
+# ceph_test_cls_version
+add_executable(ceph_test_cls_version
+ test_cls_version.cc
+ )
+target_link_libraries(ceph_test_cls_version
+ librados
+ cls_version_client
+ global
+ ${UNITTEST_LIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ radostest-cxx
+ )
+
diff --git a/src/test/cls_version/test_cls_version.cc b/src/test/cls_version/test_cls_version.cc
new file mode 100644
index 000000000..b86756a78
--- /dev/null
+++ b/src/test/cls_version/test_cls_version.cc
@@ -0,0 +1,322 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "include/types.h"
+
+#include "cls/version/cls_version_types.h"
+#include "cls/version/cls_version_client.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+static librados::ObjectWriteOperation *new_op() {
+ return new librados::ObjectWriteOperation();
+}
+
+static librados::ObjectReadOperation *new_rop() {
+ return new librados::ObjectReadOperation();
+}
+
+TEST(cls_rgw, test_version_inc_read)
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ obj_version ver;
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver));
+ ASSERT_EQ(0, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver.tag.size());
+
+
+ /* inc version */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_version_inc(*op);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver));
+ ASSERT_GT((long long)ver.ver, 0);
+ ASSERT_NE(0, (int)ver.tag.size());
+
+ /* inc version again! */
+ delete op;
+ op = new_op();
+ cls_version_inc(*op);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ obj_version ver2;
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver2));
+ ASSERT_GT((long long)ver2.ver, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver2.tag.compare(ver.tag));
+
+ delete op;
+
+ obj_version ver3;
+
+ librados::ObjectReadOperation *rop = new_rop();
+ cls_version_read(*rop, &ver3);
+ bufferlist outbl;
+ ASSERT_EQ(0, ioctx.operate(oid, rop, &outbl));
+ ASSERT_EQ(ver2.ver, ver3.ver);
+ ASSERT_EQ(1, (long long)ver2.compare(&ver3));
+
+ delete rop;
+}
+
+
+TEST(cls_rgw, test_version_set)
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ obj_version ver;
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver));
+ ASSERT_EQ(0, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver.tag.size());
+
+
+ ver.ver = 123;
+ ver.tag = "foo";
+
+ /* set version */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_version_set(*op, ver);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ /* read version */
+ obj_version ver2;
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver2));
+ ASSERT_EQ((long long)ver2.ver, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver2.tag.compare(ver.tag));
+
+ delete op;
+}
+
+TEST(cls_rgw, test_version_inc_cond)
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ obj_version ver;
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver));
+ ASSERT_EQ(0, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver.tag.size());
+
+ /* inc version */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_version_inc(*op);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver));
+ ASSERT_GT((long long)ver.ver, 0);
+ ASSERT_NE(0, (int)ver.tag.size());
+
+ obj_version cond_ver = ver;
+
+
+ /* inc version again! */
+ delete op;
+ op = new_op();
+ cls_version_inc(*op);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ obj_version ver2;
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver2));
+ ASSERT_GT((long long)ver2.ver, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver2.tag.compare(ver.tag));
+
+
+ /* now check various condition tests */
+ cls_version_inc(*op, cond_ver, VER_COND_NONE);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver2));
+ ASSERT_GT((long long)ver2.ver, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver2.tag.compare(ver.tag));
+
+ /* a bunch of conditions that should fail */
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, cond_ver, VER_COND_EQ);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, op));
+
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, cond_ver, VER_COND_LT);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, op));
+
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, cond_ver, VER_COND_LE);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, op));
+
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, cond_ver, VER_COND_TAG_NE);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver2));
+ ASSERT_GT((long long)ver2.ver, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver2.tag.compare(ver.tag));
+
+ /* a bunch of conditions that should succeed */
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, ver2, VER_COND_EQ);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, cond_ver, VER_COND_GT);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, cond_ver, VER_COND_GE);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ delete op;
+ op = new_op();
+ cls_version_inc(*op, cond_ver, VER_COND_TAG_EQ);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ delete op;
+}
+
+TEST(cls_rgw, test_version_inc_check)
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ /* add chains */
+ string oid = "obj";
+
+
+ /* create object */
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ obj_version ver;
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver));
+ ASSERT_EQ(0, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver.tag.size());
+
+ /* inc version */
+ librados::ObjectWriteOperation *op = new_op();
+ cls_version_inc(*op);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver));
+ ASSERT_GT((long long)ver.ver, 0);
+ ASSERT_NE(0, (int)ver.tag.size());
+
+ obj_version cond_ver = ver;
+
+ /* a bunch of conditions that should succeed */
+ librados::ObjectReadOperation *rop = new_rop();
+ cls_version_check(*rop, cond_ver, VER_COND_EQ);
+ bufferlist bl;
+ ASSERT_EQ(0, ioctx.operate(oid, rop, &bl));
+
+ delete rop;
+ rop = new_rop();
+ cls_version_check(*rop, cond_ver, VER_COND_GE);
+ ASSERT_EQ(0, ioctx.operate(oid, rop, &bl));
+
+ delete rop;
+ rop = new_rop();
+ cls_version_check(*rop, cond_ver, VER_COND_LE);
+ ASSERT_EQ(0, ioctx.operate(oid, rop, &bl));
+
+ delete rop;
+ rop = new_rop();
+ cls_version_check(*rop, cond_ver, VER_COND_TAG_EQ);
+ ASSERT_EQ(0, ioctx.operate(oid, rop, &bl));
+
+ obj_version ver2;
+
+ delete op;
+ op = new_op();
+ cls_version_inc(*op);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+
+ ASSERT_EQ(0, cls_version_read(ioctx, oid, &ver2));
+ ASSERT_GT((long long)ver2.ver, (long long)ver.ver);
+ ASSERT_EQ(0, (int)ver2.tag.compare(ver.tag));
+
+ delete op;
+
+ /* a bunch of conditions that should fail */
+ delete rop;
+ rop = new_rop();
+ cls_version_check(*rop, ver, VER_COND_LT);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, rop, &bl));
+
+ delete rop;
+ rop = new_rop();
+ cls_version_check(*rop, cond_ver, VER_COND_LE);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, rop, &bl));
+
+ delete rop;
+ rop = new_rop();
+ cls_version_check(*rop, cond_ver, VER_COND_TAG_NE);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, rop, &bl));
+
+ delete rop;
+}
diff --git a/src/test/common/CMakeLists.txt b/src/test/common/CMakeLists.txt
new file mode 100644
index 000000000..1179fbdfb
--- /dev/null
+++ b/src/test/common/CMakeLists.txt
@@ -0,0 +1,392 @@
+if(NOT WIN32)
+# get_command_descriptions
+# libmon not currently available on Windows.
+add_executable(get_command_descriptions
+ get_command_descriptions.cc
+ $<TARGET_OBJECTS:common_texttable_obj>
+ )
+target_link_libraries(get_command_descriptions
+ mon
+ global
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ )
+endif(NOT WIN32)
+
+# Though FreeBSD has blkdev support, the unittests' mocks only work in Linux
+if(HAVE_BLKID AND LINUX)
+ # unittest_blkdev
+ add_executable(unittest_blkdev
+ test_blkdev.cc)
+ add_ceph_unittest(unittest_blkdev)
+ target_link_libraries(unittest_blkdev global ${BLKID_LIBRARIES})
+endif()
+
+# unittest_lockdep
+if(WITH_CEPH_DEBUG_MUTEX)
+ add_executable(unittest_lockdep
+ test_lockdep.cc)
+ add_ceph_unittest(unittest_lockdep)
+ target_link_libraries(unittest_lockdep ceph-common)
+endif()
+
+# unittest_counter
+add_executable(unittest_counter
+ test_counter.cc)
+add_ceph_unittest(unittest_counter)
+target_link_libraries(unittest_counter ceph-common)
+
+# FreeBSD only has shims to support NUMA, no functional code.
+if(LINUX)
+# unittest_numa
+add_executable(unittest_numa
+ test_numa.cc
+ )
+add_ceph_unittest(unittest_numa)
+target_link_libraries(unittest_numa ceph-common)
+endif()
+
+# unittest_bloom_filter
+add_executable(unittest_bloom_filter
+ test_bloom_filter.cc
+ )
+add_ceph_unittest(unittest_bloom_filter)
+target_link_libraries(unittest_bloom_filter ceph-common)
+
+# unittest_lruset
+add_executable(unittest_lruset
+ test_lruset.cc
+ )
+add_ceph_unittest(unittest_lruset)
+target_link_libraries(unittest_lruset)
+
+# unittest_histogram
+add_executable(unittest_histogram
+ histogram.cc
+ )
+add_ceph_unittest(unittest_histogram)
+target_link_libraries(unittest_histogram ceph-common)
+
+# unittest_prioritized_queue
+add_executable(unittest_prioritized_queue
+ test_prioritized_queue.cc
+ )
+target_link_libraries(unittest_prioritized_queue ceph-common)
+add_ceph_unittest(unittest_prioritized_queue)
+
+if(NOT WIN32)
+# unittest_mclock_priority_queue
+add_executable(unittest_mclock_priority_queue
+ test_mclock_priority_queue.cc
+ )
+add_ceph_unittest(unittest_mclock_priority_queue)
+target_link_libraries(unittest_mclock_priority_queue
+ ceph-common
+ dmclock::dmclock)
+endif(NOT WIN32)
+
+# unittest_str_map
+add_executable(unittest_str_map
+ test_str_map.cc
+ )
+add_ceph_unittest(unittest_str_map)
+target_link_libraries(unittest_str_map ceph-common)
+
+# unittest_json_formattable
+add_executable(unittest_json_formattable
+ test_json_formattable.cc
+ )
+add_ceph_unittest(unittest_json_formattable ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_json_formattable)
+# add_dependencies(unittest_json_formattable ceph-common)
+target_link_libraries(unittest_json_formattable ceph-common global ${BLKID_LIBRARIES})
+
+# unittest_json_formatter
+add_executable(unittest_json_formatter
+ test_json_formatter.cc
+ )
+add_ceph_unittest(unittest_json_formatter ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_json_formatter)
+# add_dependencies(unittest_json_formatter ceph-common)
+target_link_libraries(unittest_json_formatter ceph-common global ${BLKID_LIBRARIES})
+
+# unittest_sharedptr_registry
+add_executable(unittest_sharedptr_registry
+ test_sharedptr_registry.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_sharedptr_registry)
+target_link_libraries(unittest_sharedptr_registry global)
+
+# unittest_shared_cache
+add_executable(unittest_shared_cache
+ test_shared_cache.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_shared_cache)
+target_link_libraries(unittest_shared_cache global)
+
+# unittest_sloppy_crc_map
+add_executable(unittest_sloppy_crc_map
+ test_sloppy_crc_map.cc
+ )
+add_ceph_unittest(unittest_sloppy_crc_map)
+target_link_libraries(unittest_sloppy_crc_map global)
+
+# unittest_time
+add_executable(unittest_time
+ test_time.cc
+ ${CMAKE_SOURCE_DIR}/src/common/ceph_time.cc
+ )
+add_ceph_unittest(unittest_time)
+target_link_libraries(unittest_time ceph-common)
+
+# unittest_util
+add_executable(unittest_util
+ test_util.cc
+ ${CMAKE_SOURCE_DIR}/src/common/util.cc
+ )
+add_ceph_unittest(unittest_util)
+target_link_libraries(unittest_util global StdFilesystem::filesystem)
+
+# unittest_random
+add_executable(unittest_random
+ test_random.cc
+ )
+add_ceph_unittest(unittest_random)
+target_link_libraries(unittest_random Boost::random)
+
+# unittest_throttle
+add_executable(unittest_throttle
+ Throttle.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_throttle PARALLEL)
+target_link_libraries(unittest_throttle global)
+
+# unittest_lru
+add_executable(unittest_lru
+ test_lru.cc
+ )
+add_ceph_unittest(unittest_lru)
+target_link_libraries(unittest_lru ceph-common)
+
+# unittest_intrusive_lru
+add_executable(unittest_intrusive_lru
+ test_intrusive_lru.cc
+ )
+add_ceph_unittest(unittest_intrusive_lru)
+target_link_libraries(unittest_intrusive_lru ceph-common)
+
+# unittest_crc32c
+add_executable(unittest_crc32c
+ test_crc32c.cc
+ )
+add_ceph_unittest(unittest_crc32c)
+target_link_libraries(unittest_crc32c ceph-common)
+
+# unittest_config
+add_executable(unittest_config
+ test_config.cc
+ test_hostname.cc
+ )
+add_ceph_unittest(unittest_config)
+target_link_libraries(unittest_config ceph-common)
+
+# unittest_context
+add_executable(unittest_context
+ test_context.cc
+ )
+add_ceph_unittest(unittest_context)
+target_link_libraries(unittest_context ceph-common)
+
+# unittest_safe_io
+add_executable(unittest_safe_io
+ test_safe_io.cc
+ )
+add_ceph_unittest(unittest_safe_io)
+target_link_libraries(unittest_safe_io ceph-common)
+
+# unittest_url_escape
+add_executable(unittest_url_escape
+ test_url_escape.cc
+ )
+add_ceph_unittest(unittest_url_escape)
+target_link_libraries(unittest_url_escape ceph-common)
+
+# unittest_pretty_binary
+add_executable(unittest_pretty_binary
+ test_pretty_binary.cc
+ )
+add_ceph_unittest(unittest_pretty_binary)
+target_link_libraries(unittest_pretty_binary ceph-common)
+
+# unittest_readahead
+add_executable(unittest_readahead
+ Readahead.cc
+ )
+add_ceph_unittest(unittest_readahead)
+target_link_libraries(unittest_readahead ceph-common)
+
+# unittest_tableformatter
+add_executable(unittest_tableformatter
+ test_tableformatter.cc
+ )
+add_ceph_unittest(unittest_tableformatter)
+target_link_libraries(unittest_tableformatter ceph-common)
+
+add_executable(unittest_xmlformatter
+ test_xmlformatter.cc
+ )
+add_ceph_unittest(unittest_xmlformatter)
+target_link_libraries(unittest_xmlformatter ceph-common)
+
+# unittest_bit_vector
+add_executable(unittest_bit_vector
+ test_bit_vector.cc
+ )
+add_ceph_unittest(unittest_bit_vector)
+target_link_libraries(unittest_bit_vector ceph-common)
+
+# unittest_interval_map
+add_executable(unittest_interval_map
+ test_interval_map.cc
+)
+add_ceph_unittest(unittest_interval_map)
+target_link_libraries(unittest_interval_map ceph-common)
+
+# unittest_interval_set
+add_executable(unittest_interval_set
+ test_interval_set.cc
+)
+add_ceph_unittest(unittest_interval_set)
+target_link_libraries(unittest_interval_set ceph-common GTest::Main)
+
+# unittest_weighted_priority_queue
+add_executable(unittest_weighted_priority_queue
+ test_weighted_priority_queue.cc
+ )
+target_link_libraries(unittest_weighted_priority_queue ceph-common)
+add_ceph_unittest(unittest_weighted_priority_queue)
+
+if(WITH_CEPH_DEBUG_MUTEX)
+ add_executable(unittest_mutex_debug
+ test_mutex_debug.cc)
+ add_ceph_unittest(unittest_mutex_debug)
+ target_link_libraries(unittest_mutex_debug ceph-common)
+endif()
+
+# unittest_shunique_lock
+add_executable(unittest_shunique_lock
+ test_shunique_lock.cc
+ )
+add_ceph_unittest(unittest_shunique_lock)
+target_link_libraries(unittest_shunique_lock ceph-common)
+
+add_executable(unittest_fair_mutex
+ test_fair_mutex.cc)
+add_ceph_unittest(unittest_fair_mutex)
+target_link_libraries(unittest_fair_mutex ceph-common)
+
+# unittest_perf_histogram
+add_executable(unittest_perf_histogram
+ test_perf_histogram.cc
+ )
+add_ceph_unittest(unittest_perf_histogram)
+target_link_libraries(unittest_perf_histogram ceph-common)
+
+# unittest_perf_cache_key
+add_executable(unittest_perf_counters_key test_perf_counters_key.cc)
+add_ceph_unittest(unittest_perf_counters_key)
+target_link_libraries(unittest_perf_counters_key ceph-common)
+
+# unittest_global_doublefree
+if(WITH_CEPHFS)
+ add_executable(unittest_global_doublefree
+ test_global_doublefree.cc
+ )
+ add_ceph_unittest(unittest_global_doublefree)
+ target_link_libraries(unittest_global_doublefree cephfs librados ceph-common)
+endif(WITH_CEPHFS)
+
+if(NOT WIN32)
+add_executable(unittest_dns_resolve
+ dns_resolve.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(unittest_dns_resolve global)
+add_ceph_unittest(unittest_dns_resolve)
+endif()
+
+add_executable(unittest_back_trace
+ test_back_trace.cc)
+set_source_files_properties(test_back_trace.cc PROPERTIES
+ COMPILE_FLAGS -fno-inline)
+add_ceph_unittest(unittest_back_trace)
+target_link_libraries(unittest_back_trace ceph-common)
+
+add_executable(unittest_hostname
+ test_hostname.cc)
+add_ceph_unittest(unittest_hostname)
+target_link_libraries(unittest_hostname ceph-common)
+
+add_executable(unittest_iso_8601
+ test_iso_8601.cc)
+add_ceph_unittest(unittest_iso_8601)
+target_link_libraries(unittest_iso_8601 ceph-common)
+
+add_executable(unittest_convenience test_convenience.cc)
+add_ceph_unittest(unittest_convenience)
+
+add_executable(unittest_bounded_key_counter
+ test_bounded_key_counter.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(unittest_bounded_key_counter global)
+add_ceph_unittest(unittest_bounded_key_counter)
+
+add_executable(unittest_split test_split.cc)
+add_ceph_unittest(unittest_split)
+
+add_executable(unittest_static_ptr test_static_ptr.cc)
+add_ceph_unittest(unittest_static_ptr)
+
+add_executable(unittest_hobject test_hobject.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(unittest_hobject global ceph-common)
+add_ceph_unittest(unittest_hobject)
+
+add_executable(unittest_async_completion test_async_completion.cc)
+add_ceph_unittest(unittest_async_completion)
+target_link_libraries(unittest_async_completion ceph-common Boost::system)
+
+add_executable(unittest_async_shared_mutex test_async_shared_mutex.cc)
+add_ceph_unittest(unittest_async_shared_mutex)
+target_link_libraries(unittest_async_shared_mutex ceph-common Boost::system)
+
+add_executable(unittest_cdc test_cdc.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(unittest_cdc global ceph-common)
+add_ceph_unittest(unittest_cdc)
+
+add_executable(unittest_ceph_timer test_ceph_timer.cc)
+add_ceph_unittest(unittest_ceph_timer)
+
+add_executable(unittest_option test_option.cc)
+target_link_libraries(unittest_option ceph-common GTest::Main)
+add_ceph_unittest(unittest_option)
+
+add_executable(unittest_fault_injector test_fault_injector.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(unittest_fault_injector global)
+add_ceph_unittest(unittest_fault_injector)
+
+add_executable(unittest_blocked_completion test_blocked_completion.cc)
+add_ceph_unittest(unittest_blocked_completion)
+target_link_libraries(unittest_blocked_completion Boost::system GTest::GTest)
+
+add_executable(unittest_allocate_unique test_allocate_unique.cc)
+add_ceph_unittest(unittest_allocate_unique)
+
+if(WITH_SYSTEMD)
+ add_executable(unittest_journald_logger test_journald_logger.cc)
+ target_link_libraries(unittest_journald_logger ceph-common)
+ add_ceph_unittest(unittest_journald_logger)
+endif()
diff --git a/src/test/common/ObjectContents.cc b/src/test/common/ObjectContents.cc
new file mode 100644
index 000000000..381c59c7c
--- /dev/null
+++ b/src/test/common/ObjectContents.cc
@@ -0,0 +1,128 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "ObjectContents.h"
+#include "include/buffer.h"
+#include <iostream>
+#include <map>
+
+bool test_object_contents()
+{
+ ObjectContents c, d;
+ ceph_assert(!c.exists());
+ c.debug(std::cerr);
+ c.write(10, 10, 10);
+ ceph_assert(c.exists());
+ ceph_assert(c.size() == 20);
+
+ c.debug(std::cerr);
+ bufferlist bl;
+ for (ObjectContents::Iterator iter = c.get_iterator();
+ iter.valid();
+ ++iter) {
+ bl.append(*iter);
+ }
+ ceph_assert(bl.length() == 20);
+
+ bufferlist bl2;
+ for (unsigned i = 0; i < 8; ++i) bl2.append(bl[i]);
+ c.write(10, 8, 4);
+ c.debug(std::cerr);
+ ObjectContents::Iterator iter = c.get_iterator();
+ iter.seek_to(8);
+ for (uint64_t i = 8;
+ i < 12;
+ ++i, ++iter) {
+ bl2.append(*iter);
+ }
+ for (unsigned i = 12; i < 20; ++i) bl2.append(bl[i]);
+ ceph_assert(bl2.length() == 20);
+
+ for (ObjectContents::Iterator iter3 = c.get_iterator();
+ iter.valid();
+ ++iter) {
+ ceph_assert(bl2[iter3.get_pos()] == *iter3);
+ }
+
+ ceph_assert(bl2[0] == '\0');
+ ceph_assert(bl2[7] == '\0');
+
+ interval_set<uint64_t> to_clone;
+ to_clone.insert(5, 10);
+ d.clone_range(c, to_clone);
+ ceph_assert(d.size() == 15);
+
+ c.debug(std::cerr);
+ d.debug(std::cerr);
+
+ ObjectContents::Iterator iter2 = d.get_iterator();
+ iter2.seek_to(5);
+ for (uint64_t i = 5; i < 15; ++i, ++iter2) {
+ std::cerr << "i is " << i << std::endl;
+ ceph_assert(iter2.get_pos() == i);
+ ceph_assert(*iter2 == bl2[i]);
+ }
+ return true;
+}
+
+
+unsigned int ObjectContents::Iterator::get_state(uint64_t _pos)
+{
+ if (parent->seeds.count(_pos)) {
+ return parent->seeds[_pos];
+ }
+ seek_to(_pos - 1);
+ return current_state;
+}
+
+void ObjectContents::clone_range(ObjectContents &other,
+ interval_set<uint64_t> &intervals)
+{
+ interval_set<uint64_t> written_to_clone;
+ written_to_clone.intersection_of(intervals, other.written);
+
+ interval_set<uint64_t> zeroed = intervals;
+ zeroed.subtract(written_to_clone);
+
+ written.union_of(intervals);
+ written.subtract(zeroed);
+
+ for (interval_set<uint64_t>::iterator i = written_to_clone.begin();
+ i != written_to_clone.end();
+ ++i) {
+ uint64_t start = i.get_start();
+ uint64_t len = i.get_len();
+
+ unsigned int seed = get_iterator().get_state(start+len);
+
+ seeds[start+len] = seed;
+ seeds.erase(seeds.lower_bound(start), seeds.lower_bound(start+len));
+
+ seeds[start] = other.get_iterator().get_state(start);
+ seeds.insert(other.seeds.upper_bound(start),
+ other.seeds.lower_bound(start+len));
+ }
+
+ if (intervals.range_end() > _size)
+ _size = intervals.range_end();
+ _exists = true;
+ return;
+}
+
+void ObjectContents::write(unsigned int seed,
+ uint64_t start,
+ uint64_t len)
+{
+ _exists = true;
+ unsigned int _seed = get_iterator().get_state(start+len);
+ seeds[start+len] = _seed;
+ seeds.erase(seeds.lower_bound(start),
+ seeds.lower_bound(start+len));
+ seeds[start] = seed;
+
+ interval_set<uint64_t> to_write;
+ to_write.insert(start, len);
+ written.union_of(to_write);
+
+ if (start + len > _size)
+ _size = start + len;
+ return;
+}
diff --git a/src/test/common/ObjectContents.h b/src/test/common/ObjectContents.h
new file mode 100644
index 000000000..7834bfedf
--- /dev/null
+++ b/src/test/common/ObjectContents.h
@@ -0,0 +1,122 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer_fwd.h"
+#include <map>
+
+#ifndef COMMON_OBJECT_H
+#define COMMON_OBJECT_H
+
+enum {
+ RANDOMWRITEFULL,
+ DELETED,
+ CLONERANGE
+};
+
+bool test_object_contents();
+
+class ObjectContents {
+ uint64_t _size;
+ std::map<uint64_t, unsigned int> seeds;
+ interval_set<uint64_t> written;
+ bool _exists;
+public:
+ class Iterator {
+ ObjectContents *parent;
+ std::map<uint64_t, unsigned int>::iterator iter;
+ unsigned int current_state;
+ int current_val;
+ uint64_t pos;
+ private:
+ unsigned int get_state(uint64_t pos);
+ public:
+ explicit Iterator(ObjectContents *parent) :
+ parent(parent), iter(parent->seeds.end()),
+ current_state(0), current_val(0), pos(-1) {
+ seek_to_first();
+ }
+ char operator*() {
+ return parent->written.contains(pos) ?
+ static_cast<char>(current_val % 256) : '\0';
+ }
+ uint64_t get_pos() {
+ return pos;
+ }
+ void seek_to(uint64_t _pos) {
+ if (pos > _pos ||
+ (iter != parent->seeds.end() && _pos >= iter->first)) {
+ iter = parent->seeds.upper_bound(_pos);
+ --iter;
+ current_state = iter->second;
+ current_val = rand_r(&current_state);
+ pos = iter->first;
+ ++iter;
+ }
+ while (pos < _pos) ++(*this);
+ }
+
+ void seek_to_first() {
+ seek_to(0);
+ }
+ Iterator &operator++() {
+ ++pos;
+ if (iter != parent->seeds.end() && pos >= iter->first) {
+ ceph_assert(pos == iter->first);
+ current_state = iter->second;
+ ++iter;
+ }
+ current_val = rand_r(&current_state);
+ return *this;
+ }
+ bool valid() {
+ return pos < parent->size();
+ }
+ friend class ObjectContents;
+ };
+
+ ObjectContents() : _size(0), _exists(false) {
+ seeds[0] = 0;
+ }
+
+ explicit ObjectContents(bufferlist::const_iterator &bp) {
+ decode(_size, bp);
+ decode(seeds, bp);
+ decode(written, bp);
+ decode(_exists, bp);
+ }
+
+ void clone_range(ObjectContents &other,
+ interval_set<uint64_t> &intervals);
+ void write(unsigned int seed,
+ uint64_t from,
+ uint64_t len);
+ Iterator get_iterator() {
+ return Iterator(this);
+ }
+
+ uint64_t size() const { return _size; }
+
+ bool exists() { return _exists; }
+
+ void debug(std::ostream &out) {
+ out << "_size is " << _size << std::endl;
+ out << "seeds is: (";
+ for (std::map<uint64_t, unsigned int>::iterator i = seeds.begin();
+ i != seeds.end();
+ ++i) {
+ out << "[" << i->first << "," << i->second << "], ";
+ }
+ out << ")" << std::endl;
+ out << "written is " << written << std::endl;
+ out << "_exists is " << _exists << std::endl;
+ }
+
+ void encode(bufferlist &bl) const {
+ using ceph::encode;
+ encode(_size, bl);
+ encode(seeds, bl);
+ encode(written, bl);
+ encode(_exists, bl);
+ }
+};
+
+#endif
diff --git a/src/test/common/Readahead.cc b/src/test/common/Readahead.cc
new file mode 100644
index 000000000..30402b022
--- /dev/null
+++ b/src/test/common/Readahead.cc
@@ -0,0 +1,132 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Adam Crume <adamcrume@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/Readahead.h"
+#include "gtest/gtest.h"
+#include <stdint.h>
+#include <boost/foreach.hpp>
+#include <cstdarg>
+
+
+#define ASSERT_RA(expected_offset, expected_length, ra) \
+ do { \
+ Readahead::extent_t e = ra; \
+ ASSERT_EQ((uint64_t)expected_length, e.second); \
+ if (expected_length) { \
+ ASSERT_EQ((uint64_t)expected_offset, e.first); \
+ } \
+ } while(0)
+
+using namespace std;
+
+TEST(Readahead, random_access) {
+ Readahead r;
+ r.set_trigger_requests(2);
+ ASSERT_RA(0, 0, r.update(1000, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1010, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1030, 20, r.update(1020, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1040, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1060, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1080, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1100, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1200, 10, Readahead::NO_LIMIT));
+}
+
+TEST(Readahead, min_size_limit) {
+ Readahead r;
+ r.set_trigger_requests(2);
+ r.set_min_readahead_size(40);
+ ASSERT_RA(0, 0, r.update(1000, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1010, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1030, 40, r.update(1020, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1030, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1070, 80, r.update(1040, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1050, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1060, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1070, 10, Readahead::NO_LIMIT));
+}
+
+TEST(Readahead, max_size_limit) {
+ Readahead r;
+ r.set_trigger_requests(2);
+ r.set_max_readahead_size(50);
+ ASSERT_RA(0, 0, r.update(1000, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1010, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1030, 20, r.update(1020, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1050, 40, r.update(1030, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1040, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1050, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1090, 50, r.update(1060, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1070, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1080, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1090, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1100, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1140, 50, r.update(1110, 10, Readahead::NO_LIMIT));
+}
+
+TEST(Readahead, limit) {
+ Readahead r;
+ r.set_trigger_requests(2);
+ r.set_max_readahead_size(50);
+ uint64_t limit = 1100;
+ ASSERT_RA(0, 0, r.update(1000, 10, limit));
+ ASSERT_RA(0, 0, r.update(1010, 10, limit));
+ ASSERT_RA(1030, 20, r.update(1020, 10, limit));
+ ASSERT_RA(1050, 40, r.update(1030, 10, limit));
+ ASSERT_RA(0, 0, r.update(1040, 10, limit));
+ ASSERT_RA(0, 0, r.update(1050, 10, limit));
+ ASSERT_RA(1090, 10, r.update(1060, 10, limit));
+ ASSERT_RA(0, 0, r.update(1070, 10, limit));
+ ASSERT_RA(0, 0, r.update(1080, 10, limit));
+ ASSERT_RA(0, 0, r.update(1090, 10, limit));
+}
+
+TEST(Readahead, alignment) {
+ Readahead r;
+ r.set_trigger_requests(2);
+ vector<uint64_t> alignment;
+ alignment.push_back(100);
+ r.set_alignments(alignment);
+ ASSERT_RA(0, 0, r.update(1000, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1010, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1030, 20, r.update(1020, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1050, 50, r.update(1030, 10, Readahead::NO_LIMIT)); // internal readahead size 40
+ ASSERT_RA(0, 0, r.update(1040, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1050, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1060, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1100, 100, r.update(1070, 10, Readahead::NO_LIMIT)); // internal readahead size 80
+ ASSERT_RA(0, 0, r.update(1080, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1090, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1100, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1110, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1120, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1130, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1200, 200, r.update(1140, 10, Readahead::NO_LIMIT)); // internal readahead size 160
+ ASSERT_RA(0, 0, r.update(1150, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1160, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1170, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1180, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1190, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1200, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1210, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1220, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1230, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1240, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1250, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1260, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1270, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(0, 0, r.update(1280, 10, Readahead::NO_LIMIT));
+ ASSERT_RA(1400, 300, r.update(1290, 10, Readahead::NO_LIMIT)); // internal readahead size 320
+ ASSERT_RA(0, 0, r.update(1300, 10, Readahead::NO_LIMIT));
+}
diff --git a/src/test/common/Throttle.cc b/src/test/common/Throttle.cc
new file mode 100644
index 000000000..b36d0a901
--- /dev/null
+++ b/src/test/common/Throttle.cc
@@ -0,0 +1,386 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+
+#include <chrono>
+#include <list>
+#include <mutex>
+#include <random>
+#include <thread>
+
+#include "gtest/gtest.h"
+#include "common/Thread.h"
+#include "common/Throttle.h"
+#include "common/ceph_argparse.h"
+
+using namespace std;
+
+class ThrottleTest : public ::testing::Test {
+protected:
+
+ class Thread_get : public Thread {
+ public:
+ Throttle &throttle;
+ int64_t count;
+ bool waited = false;
+
+ Thread_get(Throttle& _throttle, int64_t _count) :
+ throttle(_throttle), count(_count) {}
+
+ void *entry() override {
+ usleep(5);
+ waited = throttle.get(count);
+ throttle.put(count);
+ return nullptr;
+ }
+ };
+};
+
+TEST_F(ThrottleTest, Throttle) {
+ int64_t throttle_max = 10;
+ Throttle throttle(g_ceph_context, "throttle", throttle_max);
+ ASSERT_EQ(throttle.get_max(), throttle_max);
+ ASSERT_EQ(throttle.get_current(), 0);
+}
+
+TEST_F(ThrottleTest, take) {
+ int64_t throttle_max = 10;
+ Throttle throttle(g_ceph_context, "throttle", throttle_max);
+ ASSERT_EQ(throttle.take(throttle_max), throttle_max);
+ ASSERT_EQ(throttle.take(throttle_max), throttle_max * 2);
+}
+
+TEST_F(ThrottleTest, get) {
+ int64_t throttle_max = 10;
+ Throttle throttle(g_ceph_context, "throttle");
+
+ // test increasing max from 0 to throttle_max
+ {
+ ASSERT_FALSE(throttle.get(throttle_max, throttle_max));
+ ASSERT_EQ(throttle.get_max(), throttle_max);
+ ASSERT_EQ(throttle.put(throttle_max), 0);
+ }
+
+ ASSERT_FALSE(throttle.get(5));
+ ASSERT_EQ(throttle.put(5), 0);
+
+ ASSERT_FALSE(throttle.get(throttle_max));
+ ASSERT_FALSE(throttle.get_or_fail(1));
+ ASSERT_FALSE(throttle.get(1, throttle_max + 1));
+ ASSERT_EQ(throttle.put(throttle_max + 1), 0);
+ ASSERT_FALSE(throttle.get(0, throttle_max));
+ ASSERT_FALSE(throttle.get(throttle_max));
+ ASSERT_FALSE(throttle.get_or_fail(1));
+ ASSERT_EQ(throttle.put(throttle_max), 0);
+
+ useconds_t delay = 1;
+
+ bool waited;
+
+ do {
+ cout << "Trying (1) with delay " << delay << "us\n";
+
+ ASSERT_FALSE(throttle.get(throttle_max));
+ ASSERT_FALSE(throttle.get_or_fail(throttle_max));
+
+ Thread_get t(throttle, 7);
+ t.create("t_throttle_1");
+ usleep(delay);
+ ASSERT_EQ(throttle.put(throttle_max), 0);
+ t.join();
+
+ if (!(waited = t.waited))
+ delay *= 2;
+ } while(!waited);
+
+ delay = 1;
+ do {
+ cout << "Trying (2) with delay " << delay << "us\n";
+
+ ASSERT_FALSE(throttle.get(throttle_max / 2));
+ ASSERT_FALSE(throttle.get_or_fail(throttle_max));
+
+ Thread_get t(throttle, throttle_max);
+ t.create("t_throttle_2");
+ usleep(delay);
+
+ Thread_get u(throttle, 1);
+ u.create("u_throttle_2");
+ usleep(delay);
+
+ throttle.put(throttle_max / 2);
+
+ t.join();
+ u.join();
+
+ if (!(waited = t.waited && u.waited))
+ delay *= 2;
+ } while(!waited);
+
+}
+
+TEST_F(ThrottleTest, get_or_fail) {
+ {
+ Throttle throttle(g_ceph_context, "throttle");
+
+ ASSERT_TRUE(throttle.get_or_fail(5));
+ ASSERT_TRUE(throttle.get_or_fail(5));
+ }
+
+ {
+ int64_t throttle_max = 10;
+ Throttle throttle(g_ceph_context, "throttle", throttle_max);
+
+ ASSERT_TRUE(throttle.get_or_fail(throttle_max));
+ ASSERT_EQ(throttle.put(throttle_max), 0);
+
+ ASSERT_TRUE(throttle.get_or_fail(throttle_max * 2));
+ ASSERT_FALSE(throttle.get_or_fail(1));
+ ASSERT_FALSE(throttle.get_or_fail(throttle_max * 2));
+ ASSERT_EQ(throttle.put(throttle_max * 2), 0);
+
+ ASSERT_TRUE(throttle.get_or_fail(throttle_max));
+ ASSERT_FALSE(throttle.get_or_fail(1));
+ ASSERT_EQ(throttle.put(throttle_max), 0);
+ }
+}
+
+TEST_F(ThrottleTest, wait) {
+ int64_t throttle_max = 10;
+ Throttle throttle(g_ceph_context, "throttle");
+
+ // test increasing max from 0 to throttle_max
+ {
+ ASSERT_FALSE(throttle.wait(throttle_max));
+ ASSERT_EQ(throttle.get_max(), throttle_max);
+ }
+
+ useconds_t delay = 1;
+
+ bool waited;
+
+ do {
+ cout << "Trying (3) with delay " << delay << "us\n";
+
+ ASSERT_FALSE(throttle.get(throttle_max / 2));
+ ASSERT_FALSE(throttle.get_or_fail(throttle_max));
+
+ Thread_get t(throttle, throttle_max);
+ t.create("t_throttle_3");
+ usleep(delay);
+
+ //
+ // Throttle::_reset_max(int64_t m) used to contain a test
+ // that blocked the following statement, only if
+ // the argument was greater than throttle_max.
+ // Although a value lower than throttle_max would cover
+ // the same code in _reset_max, the throttle_max * 100
+ // value is left here to demonstrate that the problem
+ // has been solved.
+ //
+ throttle.wait(throttle_max * 100);
+ usleep(delay);
+ t.join();
+ ASSERT_EQ(throttle.get_current(), throttle_max / 2);
+
+ if (!(waited = t.waited)) {
+ delay *= 2;
+ // undo the changes we made
+ throttle.put(throttle_max / 2);
+ throttle.wait(throttle_max);
+ }
+ } while(!waited);
+}
+
+std::pair<double, std::chrono::duration<double> > test_backoff(
+ double low_threshhold,
+ double high_threshhold,
+ double expected_throughput,
+ double high_multiple,
+ double max_multiple,
+ uint64_t max,
+ double put_delay_per_count,
+ unsigned getters,
+ unsigned putters)
+{
+ std::mutex l;
+ std::condition_variable c;
+ uint64_t total = 0;
+ std::list<uint64_t> in_queue;
+ bool stop_getters = false;
+ bool stop_putters = false;
+
+ auto wait_time = std::chrono::duration<double>(0);
+ uint64_t waits = 0;
+
+ uint64_t total_observed_total = 0;
+ uint64_t total_observations = 0;
+
+ BackoffThrottle throttle(g_ceph_context, "backoff_throttle_test", 5);
+ bool valid = throttle.set_params(
+ low_threshhold,
+ high_threshhold,
+ expected_throughput,
+ high_multiple,
+ max_multiple,
+ max,
+ 0);
+ ceph_assert(valid);
+
+ auto getter = [&]() {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<> dis(0, 10);
+
+ std::unique_lock<std::mutex> g(l);
+ while (!stop_getters) {
+ g.unlock();
+
+ uint64_t to_get = dis(gen);
+ auto waited = throttle.get(to_get);
+
+ g.lock();
+ wait_time += waited;
+ waits += to_get;
+ total += to_get;
+ in_queue.push_back(to_get);
+ c.notify_one();
+ }
+ };
+
+ auto putter = [&]() {
+ std::unique_lock<std::mutex> g(l);
+ while (!stop_putters || !in_queue.empty()) {
+ if (in_queue.empty()) {
+ c.wait(g);
+ continue;
+ }
+
+ uint64_t c = in_queue.front();
+
+ total_observed_total += total;
+ total_observations++;
+ in_queue.pop_front();
+ ceph_assert(total <= max);
+
+ g.unlock();
+ std::this_thread::sleep_for(
+ c * std::chrono::duration<double>(put_delay_per_count*putters));
+ g.lock();
+
+ total -= c;
+ throttle.put(c);
+ }
+ };
+
+ vector<std::thread> gts(getters);
+ for (auto &&i: gts) i = std::thread(getter);
+
+ vector<std::thread> pts(putters);
+ for (auto &&i: pts) i = std::thread(putter);
+
+ std::this_thread::sleep_for(std::chrono::duration<double>(5));
+ {
+ std::unique_lock<std::mutex> g(l);
+ stop_getters = true;
+ c.notify_all();
+ }
+ for (auto &&i: gts) i.join();
+ gts.clear();
+
+ {
+ std::unique_lock<std::mutex> g(l);
+ stop_putters = true;
+ c.notify_all();
+ }
+ for (auto &&i: pts) i.join();
+ pts.clear();
+
+ return make_pair(
+ ((double)total_observed_total)/((double)total_observations),
+ wait_time / waits);
+}
+
+TEST(BackoffThrottle, undersaturated)
+{
+ auto results = test_backoff(
+ 0.4,
+ 0.6,
+ 1000,
+ 2,
+ 10,
+ 100,
+ 0.0001,
+ 3,
+ 6);
+ ASSERT_LT(results.first, 45);
+ ASSERT_GT(results.first, 35);
+ ASSERT_LT(results.second.count(), 0.0002);
+ ASSERT_GT(results.second.count(), 0.00005);
+}
+
+TEST(BackoffThrottle, balanced)
+{
+ auto results = test_backoff(
+ 0.4,
+ 0.6,
+ 1000,
+ 2,
+ 10,
+ 100,
+ 0.001,
+ 7,
+ 2);
+ ASSERT_LT(results.first, 60);
+ ASSERT_GT(results.first, 40);
+ ASSERT_LT(results.second.count(), 0.002);
+ ASSERT_GT(results.second.count(), 0.0005);
+}
+
+TEST(BackoffThrottle, oversaturated)
+{
+ auto results = test_backoff(
+ 0.4,
+ 0.6,
+ 10000000,
+ 2,
+ 10,
+ 100,
+ 0.001,
+ 1,
+ 3);
+ ASSERT_LT(results.first, 101);
+ ASSERT_GT(results.first, 85);
+ ASSERT_LT(results.second.count(), 0.002);
+ ASSERT_GT(results.second.count(), 0.0005);
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make unittest_throttle ;
+ * ./unittest_throttle # --gtest_filter=ThrottleTest.take \
+ * --log-to-stderr=true --debug-filestore=20
+ * "
+ * End:
+ */
diff --git a/src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n1 b/src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n1
new file mode 100644
index 000000000..f4fdd636e
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n1
@@ -0,0 +1,17 @@
+P: /devices/pci0000:00/0000:00:1d.0/0000:04:00.0/nvme/nvme0/nvme0n1
+N: nvme0n1
+S: disk/by-id/nvme-Samsung_SSD_960_EVO_250GB_S3ESNX0J958081E
+S: disk/by-id/nvme-eui.0025385971b11793
+E: DEVLINKS=/dev/disk/by-id/nvme-Samsung_SSD_960_EVO_250GB_S3ESNX0J958081E /dev/disk/by-id/nvme-eui.0025385971b11793
+E: DEVNAME=/dev/nvme0n1
+E: DEVPATH=/devices/pci0000:00/0000:00:1d.0/0000:04:00.0/nvme/nvme0/nvme0n1
+E: DEVTYPE=disk
+E: ID_PART_TABLE_TYPE=gpt
+E: ID_PART_TABLE_UUID=c83d5616-676b-4667-bcf3-c82fd4fc7e64
+E: ID_SERIAL=Samsung SSD 960 EVO 250GB_S3ESNX0J958081E
+E: ID_SERIAL_SHORT=S3ESNX0J958081E
+E: MAJOR=259
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=1875432
diff --git a/src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n1.devid b/src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n1.devid
new file mode 100644
index 000000000..844c6a956
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/autriche.nvme0n1.devid
@@ -0,0 +1 @@
+Samsung_SSD_960_EVO_250GB_S3ESNX0J958081E
diff --git a/src/test/common/blkdev-udevadm-info-samples/cpach.sdn b/src/test/common/blkdev-udevadm-info-samples/cpach.sdn
new file mode 100644
index 000000000..a47a5270a
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/cpach.sdn
@@ -0,0 +1,53 @@
+P: /devices/pci0000:80/0000:80:01.0/0000:82:00.0/host10/port-10:0/expander-10:0/port-10:0:13/end_device-10:0:13/target10:0:13/10:0:13:0/block/sdn
+N: sdn
+S: disk/by-id/ata-WDC_WDS200T2B0A-00SM50_183503800168
+S: disk/by-id/lvm-pv-uuid-LUClYG-Oyte-jcM6-npfZ-ncsl-ycL0-bkOH0m
+S: disk/by-id/wwn-0x5001b448b96ce4fd
+S: disk/by-path/pci-0000:82:00.0-sas-exp0x50030480091072bf-phy29-lun-0
+E: DEVLINKS=/dev/disk/by-path/pci-0000:82:00.0-sas-exp0x50030480091072bf-phy29-lun-0 /dev/disk/by-id/wwn-0x5001b448b96ce4fd /dev/disk/by-id/ata-WDC_WDS200T2B0A-00SM50_183503800168 /dev/disk/by-id/lvm-pv-uuid-LUClYG-Oyte-jcM6-npfZ-ncsl-ycL0-bkOH0m
+E: DEVNAME=/dev/sdn
+E: DEVPATH=/devices/pci0000:80/0000:80:01.0/0000:82:00.0/host10/port-10:0/expander-10:0/port-10:0:13/end_device-10:0:13/target10:0:13/10:0:13:0/block/sdn
+E: DEVTYPE=disk
+E: ID_ATA=1
+E: ID_ATA_DOWNLOAD_MICROCODE=1
+E: ID_ATA_FEATURE_SET_APM=1
+E: ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=254
+E: ID_ATA_FEATURE_SET_APM_ENABLED=1
+E: ID_ATA_FEATURE_SET_PM=1
+E: ID_ATA_FEATURE_SET_PM_ENABLED=1
+E: ID_ATA_FEATURE_SET_SECURITY=1
+E: ID_ATA_FEATURE_SET_SECURITY_ENABLED=0
+E: ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=2
+E: ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=2
+E: ID_ATA_FEATURE_SET_SMART=1
+E: ID_ATA_FEATURE_SET_SMART_ENABLED=1
+E: ID_ATA_ROTATION_RATE_RPM=0
+E: ID_ATA_SATA=1
+E: ID_ATA_SATA_SIGNAL_RATE_GEN1=1
+E: ID_ATA_SATA_SIGNAL_RATE_GEN2=1
+E: ID_ATA_WRITE_CACHE=1
+E: ID_ATA_WRITE_CACHE_ENABLED=1
+E: ID_BUS=ata
+E: ID_FS_TYPE=LVM2_member
+E: ID_FS_USAGE=raid
+E: ID_FS_UUID=LUClYG-Oyte-jcM6-npfZ-ncsl-ycL0-bkOH0m
+E: ID_FS_UUID_ENC=LUClYG-Oyte-jcM6-npfZ-ncsl-ycL0-bkOH0m
+E: ID_FS_VERSION=LVM2 001
+E: ID_MODEL=LVM PV LUClYG-Oyte-jcM6-npfZ-ncsl-ycL0-bkOH0m on /dev/sdn
+E: ID_MODEL_ENC=WDC\x20WDS200T2B0A-00SM50\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
+E: ID_PATH=pci-0000:82:00.0-sas-exp0x50030480091072bf-phy29-lun-0
+E: ID_PATH_TAG=pci-0000_82_00_0-sas-exp0x50030480091072bf-phy29-lun-0
+E: ID_REVISION=X61190WD
+E: ID_SERIAL=WDC_WDS200T2B0A-00SM50_183503800168
+E: ID_SERIAL_SHORT=183503800168
+E: ID_TYPE=disk
+E: ID_WWN=0x5001b448b96ce4fd
+E: ID_WWN_WITH_EXTENSION=0x5001b448b96ce4fd
+E: MAJOR=8
+E: MINOR=208
+E: SUBSYSTEM=block
+E: SYSTEMD_ALIAS=/dev/block/8:208
+E: SYSTEMD_READY=1
+E: SYSTEMD_WANTS=lvm2-pvscan@8:208.service
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=20972398
diff --git a/src/test/common/blkdev-udevadm-info-samples/cpach.sdn.devid b/src/test/common/blkdev-udevadm-info-samples/cpach.sdn.devid
new file mode 100644
index 000000000..004460cf7
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/cpach.sdn.devid
@@ -0,0 +1 @@
+WDC_WDS200T2B0A-00SM50_183503800168
diff --git a/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda
new file mode 100644
index 000000000..7e82dfb41
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda
@@ -0,0 +1,31 @@
+P: /devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/target0:2:0/0:2:0:0/block/sda
+N: sda
+S: disk/by-id/scsi-36c81f660e62885001b3147f40c5abb67
+S: disk/by-id/wwn-0x6c81f660e62885001b3147f40c5abb67
+S: disk/by-path/pci-0000:01:00.0-scsi-0:2:0:0
+E: DEVLINKS=/dev/disk/by-id/scsi-36c81f660e62885001b3147f40c5abb67 /dev/disk/by-id/wwn-0x6c81f660e62885001b3147f40c5abb67 /dev/disk/by-path/pci-0000:01:00.0-scsi-0:2:0:0
+E: DEVNAME=/dev/sda
+E: DEVPATH=/devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/target0:2:0/0:2:0:0/block/sda
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=PERC_H310
+E: ID_MODEL_ENC=PERC\x20H310
+E: ID_PART_TABLE_TYPE=dos
+E: ID_PATH=pci-0000:01:00.0-scsi-0:2:0:0
+E: ID_PATH_TAG=pci-0000_01_00_0-scsi-0_2_0_0
+E: ID_REVISION=2.12
+E: ID_SCSI=1
+E: ID_SCSI_SERIAL=0067bb5a0cf447311b008528e660f681
+E: ID_SERIAL=36c81f660e62885001b3147f40c5abb67
+E: ID_SERIAL_SHORT=6c81f660e62885001b3147f40c5abb67
+E: ID_TYPE=disk
+E: ID_VENDOR=DELL
+E: ID_VENDOR_ENC=DELL
+E: ID_WWN=0x6c81f660e6288500
+E: ID_WWN_VENDOR_EXTENSION=0x1b3147f40c5abb67
+E: ID_WWN_WITH_EXTENSION=0x6c81f660e62885001b3147f40c5abb67
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=26959
diff --git a/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda.devid b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda.devid
new file mode 100644
index 000000000..5269648e2
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sda.devid
@@ -0,0 +1 @@
+DELL_PERC_H310_0067bb5a0cf447311b008528e660f681
diff --git a/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb
new file mode 100644
index 000000000..b922d3178
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb
@@ -0,0 +1,32 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:02:00.0/0000:03:00.0/0000:04:00.0/0000:05:10.0/0000:08:00.0/host7/target7:2:1/7:2:1:0/block/sdb
+N: sdb
+S: disk/by-id/scsi-36c81f660ee4cf7001aecfd72a34e6992
+S: disk/by-id/wwn-0x6c81f660ee4cf7001aecfd72a34e6992
+S: disk/by-path/pci-0000:08:00.0-scsi-0:2:1:0
+E: DEVLINKS=/dev/disk/by-id/scsi-36c81f660ee4cf7001aecfd72a34e6992 /dev/disk/by-id/wwn-0x6c81f660ee4cf7001aecfd72a34e6992 /dev/disk/by-path/pci-0000:08:00.0-scsi-0:2:1:0
+E: DEVNAME=/dev/sdb
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/0000:03:00.0/0000:04:00.0/0000:05:10.0/0000:08:00.0/host7/target7:2:1/7:2:1:0/block/sdb
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=Shared_PERC8
+E: ID_MODEL_ENC=Shared\x20PERC8\x20\x20\x20\x20
+E: ID_PART_TABLE_TYPE=gpt
+E: ID_PATH=pci-0000:08:00.0-scsi-0:2:1:0
+E: ID_PATH_TAG=pci-0000_08_00_0-scsi-0_2_1_0
+E: ID_REVISION=3.24
+E: ID_SCSI=1
+E: ID_SCSI_SERIAL=6192694ea372fdec1a00f74cee60f681
+E: ID_SERIAL=36c81f660ee4cf7001aecfd72a34e6992
+E: ID_SERIAL_SHORT=6c81f660ee4cf7001aecfd72a34e6992
+E: ID_TARGET_PORT=100
+E: ID_TYPE=disk
+E: ID_VENDOR=DELL
+E: ID_VENDOR_ENC=DELL\x20\x20\x20\x20
+E: ID_WWN=0x6c81f660ee4cf700
+E: ID_WWN_VENDOR_EXTENSION=0x1aecfd72a34e6992
+E: ID_WWN_WITH_EXTENSION=0x6c81f660ee4cf7001aecfd72a34e6992
+E: MAJOR=8
+E: MINOR=16
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=28137
diff --git a/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb.devid b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb.devid
new file mode 100644
index 000000000..f8005c46b
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/erwan.v1.sdb.devid
@@ -0,0 +1 @@
+DELL_Shared_PERC8_6192694ea372fdec1a00f74cee60f681
diff --git a/src/test/common/blkdev-udevadm-info-samples/erwan1 b/src/test/common/blkdev-udevadm-info-samples/erwan1
new file mode 100644
index 000000000..5db39ea61
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/erwan1
@@ -0,0 +1,31 @@
+P: /devices/pci0000:00/0000:00:02.2/0000:02:00.0/host0/target0:1:0/0:1:0:0/block/sda
+N: sda
+S: disk/by-id/scsi-3600508b1001ca3a81462043ff6d56249
+S: disk/by-id/wwn-0x600508b1001ca3a81462043ff6d56249
+S: disk/by-path/pci-0000:02:00.0-scsi-0:1:0:0
+E: DEVLINKS=/dev/disk/by-id/scsi-3600508b1001ca3a81462043ff6d56249 /dev/disk/by-id/wwn-0x600508b1001ca3a81462043ff6d56249 /dev/disk/by-path/pci-0000:02:00.0-scsi-0:1:0:0
+E: DEVNAME=/dev/sda
+E: DEVPATH=/devices/pci0000:00/0000:00:02.2/0000:02:00.0/host0/target0:1:0/0:1:0:0/block/sda
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=LOGICAL_VOLUME
+E: ID_MODEL_ENC=LOGICAL\x20VOLUME\x20\x20
+E: ID_PART_TABLE_TYPE=dos
+E: ID_PATH=pci-0000:02:00.0-scsi-0:1:0:0
+E: ID_PATH_TAG=pci-0000_02_00_0-scsi-0_1_0_0
+E: ID_REVISION=8.32
+E: ID_SCSI=1
+E: ID_SCSI_SERIAL=0014380281544E0
+E: ID_SERIAL=3600508b1001ca3a81462043ff6d56249
+E: ID_SERIAL_SHORT=600508b1001ca3a81462043ff6d56249
+E: ID_TYPE=disk
+E: ID_VENDOR=HP
+E: ID_VENDOR_ENC=HP\x20\x20\x20\x20\x20\x20
+E: ID_WWN=0x600508b1001ca3a8
+E: ID_WWN_VENDOR_EXTENSION=0x1462043ff6d56249
+E: ID_WWN_WITH_EXTENSION=0x600508b1001ca3a81462043ff6d56249
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=50763
diff --git a/src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n1 b/src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n1
new file mode 100644
index 000000000..906f544c6
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n1
@@ -0,0 +1,25 @@
+P: /devices/pci0000:80/0000:80:03.0/0000:82:00.0/nvme/nvme0/nvme0n1
+N: nvme0n1
+S: disk/by-id/nvme-INTEL_SSDPEDMD400G4_CVFT520200G7400BGN
+S: disk/by-id/nvme-nvme.8086-43564654353230323030473734303042474e-494e54454c205353445045444d443430304734-00000001
+S: disk/by-path/pci-0000:82:00.0-nvme-1
+S: disk/by-uuid/860d4503-9c9d-4c24-af09-4266b7717a5c
+E: DEVLINKS=/dev/disk/by-id/nvme-nvme.8086-43564654353230323030473734303042474e-494e54454c205353445045444d443430304734-00000001 /dev/disk/by-uuid/860d4503-9c9d-4c24-af09-4266b7717a5c /dev/disk/by-path/pci-0000:82:00.0-nvme-1 /dev/disk/by-id/nvme-INTEL_SSDPEDMD400G4_CVFT520200G7400BGN
+E: DEVNAME=/dev/nvme0n1
+E: DEVPATH=/devices/pci0000:80/0000:80:03.0/0000:82:00.0/nvme/nvme0/nvme0n1
+E: DEVTYPE=disk
+E: ID_FS_TYPE=xfs
+E: ID_FS_USAGE=filesystem
+E: ID_FS_UUID=860d4503-9c9d-4c24-af09-4266b7717a5c
+E: ID_FS_UUID_ENC=860d4503-9c9d-4c24-af09-4266b7717a5c
+E: ID_MODEL=INTEL SSDPEDMD400G4
+E: ID_PATH=pci-0000:82:00.0-nvme-1
+E: ID_PATH_TAG=pci-0000_82_00_0-nvme-1
+E: ID_SERIAL=INTEL SSDPEDMD400G4_CVFT520200G7400BGN
+E: ID_SERIAL_SHORT=CVFT520200G7400BGN
+E: ID_WWN=nvme.8086-43564654353230323030473734303042474e-494e54454c205353445045444d443430304734-00000001
+E: MAJOR=259
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=5097198
diff --git a/src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n1.devid b/src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n1.devid
new file mode 100644
index 000000000..9238afd19
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/gnit.nvme0n1.devid
@@ -0,0 +1 @@
+INTEL_SSDPEDMD400G4_CVFT520200G7400BGN
diff --git a/src/test/common/blkdev-udevadm-info-samples/gnit.sda b/src/test/common/blkdev-udevadm-info-samples/gnit.sda
new file mode 100644
index 000000000..7673a6afd
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/gnit.sda
@@ -0,0 +1,46 @@
+P: /devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
+N: sda
+S: disk/by-id/ata-INTEL_SSDSC2BB240G4_BTWL3414034J240NGN
+S: disk/by-id/wwn-0x55cd2e404b4e47d8
+S: disk/by-path/pci-0000:00:1f.2-ata-1
+E: DEVLINKS=/dev/disk/by-id/wwn-0x55cd2e404b4e47d8 /dev/disk/by-path/pci-0000:00:1f.2-ata-1 /dev/disk/by-id/ata-INTEL_SSDSC2BB240G4_BTWL3414034J240NGN
+E: DEVNAME=/dev/sda
+E: DEVPATH=/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
+E: DEVTYPE=disk
+E: ID_ATA=1
+E: ID_ATA_DOWNLOAD_MICROCODE=1
+E: ID_ATA_FEATURE_SET_HPA=1
+E: ID_ATA_FEATURE_SET_HPA_ENABLED=1
+E: ID_ATA_FEATURE_SET_PM=1
+E: ID_ATA_FEATURE_SET_PM_ENABLED=1
+E: ID_ATA_FEATURE_SET_SECURITY=1
+E: ID_ATA_FEATURE_SET_SECURITY_ENABLED=0
+E: ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=2
+E: ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=2
+E: ID_ATA_FEATURE_SET_SECURITY_FROZEN=1
+E: ID_ATA_FEATURE_SET_SMART=1
+E: ID_ATA_FEATURE_SET_SMART_ENABLED=1
+E: ID_ATA_ROTATION_RATE_RPM=0
+E: ID_ATA_SATA=1
+E: ID_ATA_SATA_SIGNAL_RATE_GEN1=1
+E: ID_ATA_SATA_SIGNAL_RATE_GEN2=1
+E: ID_ATA_WRITE_CACHE=1
+E: ID_ATA_WRITE_CACHE_ENABLED=1
+E: ID_BUS=ata
+E: ID_MODEL=INTEL_SSDSC2BB240G4
+E: ID_MODEL_ENC=INTEL\x20SSDSC2BB240G4\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
+E: ID_PART_TABLE_TYPE=dos
+E: ID_PART_TABLE_UUID=bb35118c
+E: ID_PATH=pci-0000:00:1f.2-ata-1
+E: ID_PATH_TAG=pci-0000_00_1f_2-ata-1
+E: ID_REVISION=D2010355
+E: ID_SERIAL=INTEL_SSDSC2BB240G4_BTWL3414034J240NGN
+E: ID_SERIAL_SHORT=BTWL3414034J240NGN
+E: ID_TYPE=disk
+E: ID_WWN=0x55cd2e404b4e47d8
+E: ID_WWN_WITH_EXTENSION=0x55cd2e404b4e47d8
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=5064443
diff --git a/src/test/common/blkdev-udevadm-info-samples/gnit.sda.devid b/src/test/common/blkdev-udevadm-info-samples/gnit.sda.devid
new file mode 100644
index 000000000..154a89d28
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/gnit.sda.devid
@@ -0,0 +1 @@
+INTEL_SSDSC2BB240G4_BTWL3414034J240NGN
diff --git a/src/test/common/blkdev-udevadm-info-samples/mira055.sda b/src/test/common/blkdev-udevadm-info-samples/mira055.sda
new file mode 100644
index 000000000..5136db3b2
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/mira055.sda
@@ -0,0 +1,28 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:01:00.0/host0/target0:0:0/0:0:0:0/block/sda
+N: sda
+S: disk/by-id/scsi-2001b4d2050244200
+S: disk/by-path/pci-0000:01:00.0-scsi-0:0:0:0
+E: DEVLINKS=/dev/disk/by-id/scsi-2001b4d2050244200 /dev/disk/by-path/pci-0000:01:00.0-scsi-0:0:0:0
+E: DEVNAME=/dev/sda
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:01:00.0/host0/target0:0:0/0:0:0:0/block/sda
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=HUA722010CLA330
+E: ID_MODEL_ENC=HUA722010CLA330\x20
+E: ID_PART_TABLE_TYPE=dos
+E: ID_PART_TABLE_UUID=0005eff9
+E: ID_PATH=pci-0000:01:00.0-scsi-0:0:0:0
+E: ID_PATH_TAG=pci-0000_01_00_0-scsi-0_0_0_0
+E: ID_REVISION=R001
+E: ID_SCSI=1
+E: ID_SCSI_SERIAL=JPW9P0N10U422D
+E: ID_SERIAL=2001b4d2050244200
+E: ID_SERIAL_SHORT=001b4d2050244200
+E: ID_TYPE=disk
+E: ID_VENDOR=Hitachi
+E: ID_VENDOR_ENC=Hitachi\x20
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=1207668
diff --git a/src/test/common/blkdev-udevadm-info-samples/mira055.sda.devid b/src/test/common/blkdev-udevadm-info-samples/mira055.sda.devid
new file mode 100644
index 000000000..4cd2eac71
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/mira055.sda.devid
@@ -0,0 +1 @@
+Hitachi_HUA722010CLA330_JPW9P0N10U422D
diff --git a/src/test/common/blkdev-udevadm-info-samples/mira055.sdb b/src/test/common/blkdev-udevadm-info-samples/mira055.sdb
new file mode 100644
index 000000000..38040fb04
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/mira055.sdb
@@ -0,0 +1,28 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:01:00.0/host0/target0:0:0/0:0:0:1/block/sdb
+N: sdb
+S: disk/by-id/scsi-2001b4d2058da3a00
+S: disk/by-path/pci-0000:01:00.0-scsi-0:0:0:1
+E: DEVLINKS=/dev/disk/by-id/scsi-2001b4d2058da3a00 /dev/disk/by-path/pci-0000:01:00.0-scsi-0:0:0:1
+E: DEVNAME=/dev/sdb
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:01:00.0/host0/target0:0:0/0:0:0:1/block/sdb
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=HUS724040ALA640
+E: ID_MODEL_ENC=HUS724040ALA640\x20
+E: ID_PART_TABLE_TYPE=gpt
+E: ID_PART_TABLE_UUID=957b2db6-de5c-46cb-a672-243fa12d55b2
+E: ID_PATH=pci-0000:01:00.0-scsi-0:0:0:1
+E: ID_PATH_TAG=pci-0000_01_00_0-scsi-0_0_0_1
+E: ID_REVISION=R001
+E: ID_SCSI=1
+E: ID_SCSI_SERIAL=PN1334PBH5JMJS
+E: ID_SERIAL=2001b4d2058da3a00
+E: ID_SERIAL_SHORT=001b4d2058da3a00
+E: ID_TYPE=disk
+E: ID_VENDOR=HGST
+E: ID_VENDOR_ENC=HGST\x20\x20\x20\x20
+E: MAJOR=8
+E: MINOR=16
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=1256327
diff --git a/src/test/common/blkdev-udevadm-info-samples/mira055.sdb.devid b/src/test/common/blkdev-udevadm-info-samples/mira055.sdb.devid
new file mode 100644
index 000000000..8bfe4ce6f
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/mira055.sdb.devid
@@ -0,0 +1 @@
+HGST_HUS724040ALA640_PN1334PBH5JMJS
diff --git a/src/test/common/blkdev-udevadm-info-samples/mira055.sde b/src/test/common/blkdev-udevadm-info-samples/mira055.sde
new file mode 100644
index 000000000..d7a929c64
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/mira055.sde
@@ -0,0 +1,28 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:01:00.0/host0/target0:0:0/0:0:0:4/block/sde
+N: sde
+S: disk/by-id/scsi-2001b4d20a20c660b
+S: disk/by-path/pci-0000:01:00.0-scsi-0:0:0:4
+E: DEVLINKS=/dev/disk/by-path/pci-0000:01:00.0-scsi-0:0:0:4 /dev/disk/by-id/scsi-2001b4d20a20c660b
+E: DEVNAME=/dev/sde
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:01:00.0/host0/target0:0:0/0:0:0:4/block/sde
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=WD40EFRX-68N32N0
+E: ID_MODEL_ENC=WD40EFRX-68N32N0
+E: ID_PART_TABLE_TYPE=gpt
+E: ID_PART_TABLE_UUID=444372c2-981b-402a-9af1-2eb734d99ebe
+E: ID_PATH=pci-0000:01:00.0-scsi-0:0:0:4
+E: ID_PATH_TAG=pci-0000_01_00_0-scsi-0_0_0_4
+E: ID_REVISION=R001
+E: ID_SCSI=1
+E: ID_SCSI_SERIAL=WD-WCC7K2ZL0V6K
+E: ID_SERIAL=2001b4d20a20c660b
+E: ID_SERIAL_SHORT=001b4d20a20c660b
+E: ID_TYPE=disk
+E: ID_VENDOR=WDC
+E: ID_VENDOR_ENC=WDC\x20\x20\x20\x20\x20
+E: MAJOR=8
+E: MINOR=64
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=1123351
diff --git a/src/test/common/blkdev-udevadm-info-samples/mira055.sde.devid b/src/test/common/blkdev-udevadm-info-samples/mira055.sde.devid
new file mode 100644
index 000000000..8de6f598c
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/mira055.sde.devid
@@ -0,0 +1 @@
+WDC_WD40EFRX-68N32N0_WD-WCC7K2ZL0V6K
diff --git a/src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n1 b/src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n1
new file mode 100644
index 000000000..48eada772
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n1
@@ -0,0 +1,16 @@
+P: /devices/pci0000:00/0000:00:02.1/0000:04:00.0/nvme/nvme0/nvme0n1
+N: nvme0n1
+S: disk/by-id/nvme-MTFDHBG800MCG-1AN1ZABYY_ZF1000MT
+E: DEVLINKS=/dev/disk/by-id/nvme-MTFDHBG800MCG-1AN1ZABYY_ZF1000MT
+E: DEVNAME=/dev/nvme0n1
+E: DEVPATH=/devices/pci0000:00/0000:00:02.1/0000:04:00.0/nvme/nvme0/nvme0n1
+E: DEVTYPE=disk
+E: ID_PART_TABLE_TYPE=gpt
+E: ID_PART_TABLE_UUID=c206417c-54fd-4938-8223-32343c5d1057
+E: ID_SERIAL=MTFDHBG800MCG-1AN1ZABYY_ZF1000MT
+E: ID_SERIAL_SHORT=ZF1000MT
+E: MAJOR=259
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=2455622
diff --git a/src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n1.devid b/src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n1.devid
new file mode 100644
index 000000000..6341dd250
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/reesi001.nvme0n1.devid
@@ -0,0 +1 @@
+Micron_MTFDHBG800MCG-1AN1ZABYY_ZF1000MT
diff --git a/src/test/common/blkdev-udevadm-info-samples/stud.nvme0n1 b/src/test/common/blkdev-udevadm-info-samples/stud.nvme0n1
new file mode 100644
index 000000000..e72c49768
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/stud.nvme0n1
@@ -0,0 +1,29 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:04:00.0/nvme/nvme0/nvme0n1
+N: nvme0n1
+S: disk/by-id/lvm-pv-uuid-eQ399W-P00T-Fdv1-DkWO-4bGJ-VMCN-RstGEU
+S: disk/by-id/nvme-INTEL_SSDPE2MX012T4_CVPD6185002R1P2QGN
+S: disk/by-id/nvme-nvme.8086-43565044363138353030325231503251474e-494e54454c205353445045324d583031325434-00000001
+S: disk/by-path/pci-0000:04:00.0-nvme-1
+E: DEVLINKS=/dev/disk/by-path/pci-0000:04:00.0-nvme-1 /dev/disk/by-id/lvm-pv-uuid-eQ399W-P00T-Fdv1-DkWO-4bGJ-VMCN-RstGEU /dev/disk/by-id/nvme-nvme.8086-43565044363138353030325231503251474e-494e54454c205353445045324d583031325434-00000001 /dev/disk/by-id/nvme-INTEL_SSDPE2MX012T4_CVPD6185002R1P2QGN
+E: DEVNAME=/dev/nvme0n1
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:04:00.0/nvme/nvme0/nvme0n1
+E: DEVTYPE=disk
+E: ID_FS_TYPE=LVM2_member
+E: ID_FS_USAGE=raid
+E: ID_FS_UUID=eQ399W-P00T-Fdv1-DkWO-4bGJ-VMCN-RstGEU
+E: ID_FS_UUID_ENC=eQ399W-P00T-Fdv1-DkWO-4bGJ-VMCN-RstGEU
+E: ID_FS_VERSION=LVM2 001
+E: ID_MODEL=LVM PV eQ399W-P00T-Fdv1-DkWO-4bGJ-VMCN-RstGEU on /dev/nvme0n1
+E: ID_PATH=pci-0000:04:00.0-nvme-1
+E: ID_PATH_TAG=pci-0000_04_00_0-nvme-1
+E: ID_SERIAL=INTEL SSDPE2MX012T4_CVPD6185002R1P2QGN
+E: ID_SERIAL_SHORT=CVPD6185002R1P2QGN
+E: ID_WWN=nvme.8086-43565044363138353030325231503251474e-494e54454c205353445045324d583031325434-00000001
+E: MAJOR=259
+E: MINOR=0
+E: SUBSYSTEM=block
+E: SYSTEMD_ALIAS=/dev/block/259:0
+E: SYSTEMD_READY=1
+E: SYSTEMD_WANTS=lvm2-pvscan@259:0.service
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=13656184
diff --git a/src/test/common/blkdev-udevadm-info-samples/stud.nvme0n1.devid b/src/test/common/blkdev-udevadm-info-samples/stud.nvme0n1.devid
new file mode 100644
index 000000000..70a46678d
--- /dev/null
+++ b/src/test/common/blkdev-udevadm-info-samples/stud.nvme0n1.devid
@@ -0,0 +1 @@
+INTEL_SSDPE2MX012T4_CVPD6185002R1P2QGN
diff --git a/src/test/common/dns_messages.h b/src/test/common/dns_messages.h
new file mode 100644
index 000000000..1b282688a
--- /dev/null
+++ b/src/test/common/dns_messages.h
@@ -0,0 +1,100 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#ifndef CEPH_TEST_DNS_MESSAGES_H
+#define CEPH_TEST_DNS_MESSAGES_H
+
+#include "common/dns_resolve.h"
+#include "gmock/gmock.h"
+
+u_char ns_search_msg_ok_payload[] = {
+ 0x00, 0x55, 0x85, 0x80, 0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x09,
+ 0x5F, 0x63, 0x65, 0x70, 0x68, 0x2D, 0x6D, 0x6F, 0x6E, 0x04, 0x5F, 0x74, 0x63,
+ 0x70, 0x04, 0x63, 0x65, 0x70, 0x68, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0x00, 0x21,
+ 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x21, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00,
+ 0x16, 0x00, 0x0A, 0x00, 0x28, 0x1A, 0x85, 0x03, 0x6D, 0x6F, 0x6E, 0x01, 0x61,
+ 0x04, 0x63, 0x65, 0x70, 0x68, 0x03, 0x63, 0x6F, 0x6D, 0x00, 0xC0, 0x0C, 0x00,
+ 0x21, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x16, 0x00, 0x0A, 0x00, 0x19,
+ 0x1A, 0x85, 0x03, 0x6D, 0x6F, 0x6E, 0x01, 0x63, 0x04, 0x63, 0x65, 0x70, 0x68,
+ 0x03, 0x63, 0x6F, 0x6D, 0x00, 0xC0, 0x0C, 0x00, 0x21, 0x00, 0x01, 0x00, 0x09,
+ 0x3A, 0x80, 0x00, 0x16, 0x00, 0x0A, 0x00, 0x23, 0x1A, 0x85, 0x03, 0x6D, 0x6F,
+ 0x6E, 0x01, 0x62, 0x04, 0x63, 0x65, 0x70, 0x68, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0xC0, 0x85, 0x00, 0x02, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x06, 0x03,
+ 0x6E, 0x73, 0x32, 0xC0, 0x85, 0xC0, 0x85, 0x00, 0x02, 0x00, 0x01, 0x00, 0x09,
+ 0x3A, 0x80, 0x00, 0x06, 0x03, 0x6E, 0x73, 0x31, 0xC0, 0x85, 0xC0, 0x5D, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x0D,
+ 0xC0, 0x7F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0,
+ 0xA8, 0x01, 0x0C, 0xC0, 0x3B, 0x00, 0x01, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80,
+ 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x0B, 0xC0, 0xAD, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x59, 0xC0, 0x9B, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0xFE
+};
+
+
+u_char ns_query_msg_mon_c_payload[] = {
+ 0x46, 0x4D, 0x85, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x03,
+ 0x6D, 0x6F, 0x6E, 0x01, 0x63, 0x04, 0x63, 0x65, 0x70, 0x68, 0x03, 0x63, 0x6F,
+ 0x6D, 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x0D, 0xC0, 0x12, 0x00, 0x02,
+ 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x06, 0x03, 0x6E, 0x73, 0x31, 0xC0,
+ 0x12, 0xC0, 0x12, 0x00, 0x02, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x06,
+ 0x03, 0x6E, 0x73, 0x32, 0xC0, 0x12, 0xC0, 0x3C, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x59, 0xC0, 0x4E, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0xFE
+};
+
+u_char ns_query_msg_mon_b_payload[] = {
+ 0x64, 0xCC, 0x85, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x03,
+ 0x6D, 0x6F, 0x6E, 0x01, 0x62, 0x04, 0x63, 0x65, 0x70, 0x68, 0x03, 0x63, 0x6F,
+ 0x6D, 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x0C, 0xC0, 0x12, 0x00, 0x02,
+ 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x06, 0x03, 0x6E, 0x73, 0x32, 0xC0,
+ 0x12, 0xC0, 0x12, 0x00, 0x02, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x06,
+ 0x03, 0x6E, 0x73, 0x31, 0xC0, 0x12, 0xC0, 0x4E, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x59, 0xC0, 0x3C, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0xFE
+};
+
+u_char ns_query_msg_mon_a_payload[] = {
+ 0x86, 0xAD, 0x85, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x03,
+ 0x6D, 0x6F, 0x6E, 0x01, 0x61, 0x04, 0x63, 0x65, 0x70, 0x68, 0x03, 0x63, 0x6F,
+ 0x6D, 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x0B, 0xC0, 0x12, 0x00, 0x02,
+ 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x06, 0x03, 0x6E, 0x73, 0x32, 0xC0,
+ 0x12, 0xC0, 0x12, 0x00, 0x02, 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x06,
+ 0x03, 0x6E, 0x73, 0x31, 0xC0, 0x12, 0xC0, 0x4E, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0x59, 0xC0, 0x3C, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x09, 0x3A, 0x80, 0x00, 0x04, 0xC0, 0xA8, 0x01, 0xFE
+};
+
+class MockResolvHWrapper : public ResolvHWrapper {
+
+public:
+#ifdef HAVE_RES_NQUERY
+ MOCK_METHOD6(res_nquery, int(res_state s, const char *hostname, int cls,
+ int type, u_char *buf, int bufsz));
+
+ MOCK_METHOD6(res_nsearch, int(res_state s, const char *hostname, int cls,
+ int type, u_char *buf, int bufsz));
+#else
+ MOCK_METHOD5(res_query, int(const char *hostname, int cls,
+ int type, u_char *buf, int bufsz));
+
+ MOCK_METHOD5(res_search, int(const char *hostname, int cls,
+ int type, u_char *buf, int bufsz));
+#endif
+
+};
+
+
+#endif
diff --git a/src/test/common/dns_resolve.cc b/src/test/common/dns_resolve.cc
new file mode 100644
index 000000000..437004860
--- /dev/null
+++ b/src/test/common/dns_resolve.cc
@@ -0,0 +1,263 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <arpa/nameser_compat.h>
+
+#include "common/dns_resolve.h"
+#include "test/common/dns_messages.h"
+
+#include "common/debug.h"
+#include "gmock/gmock.h"
+
+
+#include <sstream>
+
+#define TEST_DEBUG 20
+
+#define dout_subsys ceph_subsys_
+
+using namespace std;
+
+using ::testing::Return;
+using ::testing::_;
+using ::testing::SetArrayArgument;
+using ::testing::DoAll;
+using ::testing::StrEq;
+
+class DNSResolverTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ g_ceph_context->_conf->subsys.set_log_level(dout_subsys, TEST_DEBUG);
+ }
+
+ void TearDown() override {
+ DNSResolver::get_instance(nullptr);
+ }
+};
+
+TEST_F(DNSResolverTest, resolve_ip_addr) {
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+
+ int lena = sizeof(ns_query_msg_mon_a_payload);
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+#else
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+#endif
+
+ entity_addr_t addr;
+ DNSResolver::get_instance(resolvH)->resolve_ip_addr(g_ceph_context,
+ "mon.a.ceph.com", &addr);
+
+ std::ostringstream os;
+ os << addr;
+ ASSERT_EQ(os.str(), "v2:192.168.1.11:0/0");
+}
+
+TEST_F(DNSResolverTest, resolve_ip_addr_fail) {
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("not_exists.com"), C_IN, T_A,_,_))
+ .WillOnce(Return(0));
+#else
+ EXPECT_CALL(*resolvH, res_query(StrEq("not_exists.com"), C_IN, T_A,_,_))
+ .WillOnce(Return(0));
+#endif
+
+ entity_addr_t addr;
+ int ret = DNSResolver::get_instance(resolvH)->resolve_ip_addr(g_ceph_context,
+ "not_exists.com", &addr);
+
+ ASSERT_EQ(ret, -1);
+ std::ostringstream os;
+ os << addr;
+ ASSERT_EQ(os.str(), "-");
+}
+
+
+TEST_F(DNSResolverTest, resolve_srv_hosts_empty_domain) {
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+
+
+ int len = sizeof(ns_search_msg_ok_payload);
+ int lena = sizeof(ns_query_msg_mon_a_payload);
+ int lenb = sizeof(ns_query_msg_mon_b_payload);
+ int lenc = sizeof(ns_query_msg_mon_c_payload);
+
+ using ::testing::InSequence;
+ {
+ InSequence s;
+
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nsearch(_, StrEq("_cephmon._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_, StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#else
+ EXPECT_CALL(*resolvH, res_search(StrEq("_cephmon._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#endif
+ }
+
+ map<string, DNSResolver::Record> records;
+ DNSResolver::get_instance(resolvH)->resolve_srv_hosts(g_ceph_context, "cephmon",
+ DNSResolver::SRV_Protocol::TCP, &records);
+
+ ASSERT_EQ(records.size(), (unsigned int)3);
+ auto it = records.find("mon.a");
+ ASSERT_NE(it, records.end());
+ std::ostringstream os;
+ os << it->second.addr;
+ ASSERT_EQ(os.str(), "v2:192.168.1.11:6789/0");
+ os.str("");
+ ASSERT_EQ(it->second.priority, 10);
+ ASSERT_EQ(it->second.weight, 40);
+ it = records.find("mon.b");
+ ASSERT_NE(it, records.end());
+ os << it->second.addr;
+ ASSERT_EQ(os.str(), "v2:192.168.1.12:6789/0");
+ os.str("");
+ ASSERT_EQ(it->second.priority, 10);
+ ASSERT_EQ(it->second.weight, 35);
+ it = records.find("mon.c");
+ ASSERT_NE(it, records.end());
+ os << it->second.addr;
+ ASSERT_EQ(os.str(), "v2:192.168.1.13:6789/0");
+ ASSERT_EQ(it->second.priority, 10);
+ ASSERT_EQ(it->second.weight, 25);
+}
+
+TEST_F(DNSResolverTest, resolve_srv_hosts_full_domain) {
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+
+
+ int len = sizeof(ns_search_msg_ok_payload);
+ int lena = sizeof(ns_query_msg_mon_a_payload);
+ int lenb = sizeof(ns_query_msg_mon_b_payload);
+ int lenc = sizeof(ns_query_msg_mon_c_payload);
+
+ using ::testing::InSequence;
+ {
+ InSequence s;
+
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nsearch(_, StrEq("_cephmon._tcp.ceph.com"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_, StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#else
+ EXPECT_CALL(*resolvH, res_search(StrEq("_cephmon._tcp.ceph.com"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#endif
+ }
+
+ map<string, DNSResolver::Record> records;
+ DNSResolver::get_instance(resolvH)->resolve_srv_hosts(g_ceph_context, "cephmon",
+ DNSResolver::SRV_Protocol::TCP, "ceph.com", &records);
+
+ ASSERT_EQ(records.size(), (unsigned int)3);
+ auto it = records.find("mon.a");
+ ASSERT_NE(it, records.end());
+ std::ostringstream os;
+ os << it->second.addr;
+ ASSERT_EQ(os.str(), "v2:192.168.1.11:6789/0");
+ os.str("");
+ it = records.find("mon.b");
+ ASSERT_NE(it, records.end());
+ os << it->second.addr;
+ ASSERT_EQ(os.str(), "v2:192.168.1.12:6789/0");
+ os.str("");
+ it = records.find("mon.c");
+ ASSERT_NE(it, records.end());
+ os << it->second.addr;
+ ASSERT_EQ(os.str(), "v2:192.168.1.13:6789/0");
+}
+
+TEST_F(DNSResolverTest, resolve_srv_hosts_fail) {
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+
+
+ using ::testing::InSequence;
+ {
+ InSequence s;
+
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nsearch(_, StrEq("_noservice._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(Return(0));
+#else
+ EXPECT_CALL(*resolvH, res_search(StrEq("_noservice._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(Return(0));
+#endif
+ }
+
+ map<string, DNSResolver::Record> records;
+ int ret = DNSResolver::get_instance(resolvH)->resolve_srv_hosts(
+ g_ceph_context, "noservice", DNSResolver::SRV_Protocol::TCP, "", &records);
+
+ ASSERT_EQ(0, ret);
+ ASSERT_TRUE(records.empty());
+}
+
diff --git a/src/test/common/get_command_descriptions.cc b/src/test/common/get_command_descriptions.cc
new file mode 100644
index 000000000..003ebb35c
--- /dev/null
+++ b/src/test/common/get_command_descriptions.cc
@@ -0,0 +1,131 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include "mon/Monitor.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+
+using namespace std;
+
+static void usage(ostream &out)
+{
+ out << "usage: get_command_descriptions [options ...]" << std::endl;
+ out << "print on stdout the result of JSON formatted options\n";
+ out << "found in mon/MonCommands.h as produced by the\n";
+ out << "Monitor.cc::get_command_descriptions function.\n";
+ out << "Designed as a helper for ceph_argparse.py unit tests.\n";
+ out << "\n";
+ out << " --all all of mon/MonCommands.h \n";
+ out << " --pull585 reproduce the bug fixed by #585\n";
+ out << "\n";
+ out << "Examples:\n";
+ out << " get_command_descriptions --all\n";
+ out << " get_command_descriptions --pull585\n";
+}
+
+static void json_print(const std::vector<MonCommand> &mon_commands)
+{
+ bufferlist rdata;
+ auto f = Formatter::create_unique("json");
+ Monitor::format_command_descriptions(mon_commands, f.get(),
+ CEPH_FEATURES_ALL, &rdata);
+ string data(rdata.c_str(), rdata.length());
+ cout << data << std::endl;
+}
+
+static void all()
+{
+#undef FLAG
+#undef COMMAND
+#undef COMMAND_WITH_FLAG
+ std::vector<MonCommand> mon_commands = {
+#define FLAG(f) (MonCommand::FLAG_##f)
+#define COMMAND(parsesig, helptext, modulename, req_perms) \
+ {parsesig, helptext, modulename, req_perms, 0},
+#define COMMAND_WITH_FLAG(parsesig, helptext, modulename, req_perms, flags) \
+ {parsesig, helptext, modulename, req_perms, flags},
+#include <mon/MonCommands.h>
+#undef COMMAND
+#undef COMMAND_WITH_FLAG
+
+#define COMMAND(parsesig, helptext, modulename, req_perms) \
+ {parsesig, helptext, modulename, req_perms, FLAG(MGR)},
+#define COMMAND_WITH_FLAG(parsesig, helptext, modulename, req_perms, flags) \
+ {parsesig, helptext, modulename, req_perms, flags | FLAG(MGR)},
+#include <mgr/MgrCommands.h>
+ #undef COMMAND
+#undef COMMAND_WITH_FLAG
+ };
+
+ json_print(mon_commands);
+}
+
+// syntax error https://github.com/ceph/ceph/pull/585
+static void pull585()
+{
+ std::vector<MonCommand> mon_commands = {
+ { "osd pool create "
+ "name=pool,type=CephPoolname "
+ "name=pg_num,type=CephInt,range=0,req=false "
+ "name=pgp_num,type=CephInt,range=0,req=false" // !!! missing trailing space
+ "name=properties,type=CephString,n=N,req=false,goodchars=[A-Za-z0-9-_.=]",
+ "create pool", "osd", "rw" }
+ };
+
+ json_print(mon_commands);
+}
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ if (args.empty()) {
+ usage(cerr);
+ exit(1);
+ }
+ for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ++i) {
+ string err;
+
+ if (*i == string("help") || *i == string("-h") || *i == string("--help")) {
+ usage(cout);
+ exit(0);
+ } else if (*i == string("--all")) {
+ all();
+ } else if (*i == string("--pull585")) {
+ pull585();
+ }
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make get_command_descriptions &&
+ * ./get_command_descriptions --all --pull585"
+ * End:
+ */
+
diff --git a/src/test/common/histogram.cc b/src/test/common/histogram.cc
new file mode 100644
index 000000000..fbecc6722
--- /dev/null
+++ b/src/test/common/histogram.cc
@@ -0,0 +1,129 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Inktank <info@inktank.com>
+ *
+ * LGPL-2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "common/histogram.h"
+#include "include/stringify.h"
+
+TEST(Histogram, Basic) {
+ pow2_hist_t h;
+
+ h.add(0);
+ h.add(0);
+ h.add(0);
+ ASSERT_EQ(3, h.h[0]);
+ ASSERT_EQ(1u, h.h.size());
+
+ h.add(1);
+ ASSERT_EQ(3, h.h[0]);
+ ASSERT_EQ(1, h.h[1]);
+ ASSERT_EQ(2u, h.h.size());
+
+ h.add(2);
+ h.add(2);
+ ASSERT_EQ(3, h.h[0]);
+ ASSERT_EQ(1, h.h[1]);
+ ASSERT_EQ(2, h.h[2]);
+ ASSERT_EQ(3u, h.h.size());
+}
+
+TEST(Histogram, Set) {
+ pow2_hist_t h;
+ h.set_bin(0, 12);
+ h.set_bin(2, 12);
+ ASSERT_EQ(12, h.h[0]);
+ ASSERT_EQ(0, h.h[1]);
+ ASSERT_EQ(12, h.h[2]);
+ ASSERT_EQ(3u, h.h.size());
+}
+
+TEST(Histogram, Position) {
+ pow2_hist_t h;
+ uint64_t lb, ub;
+ h.add(0);
+ ASSERT_EQ(-1, h.get_position_micro(-20, &lb, &ub));
+}
+
+TEST(Histogram, Position1) {
+ pow2_hist_t h;
+ h.add(0);
+ uint64_t lb, ub;
+ h.get_position_micro(0, &lb, &ub);
+ ASSERT_EQ(0u, lb);
+ ASSERT_EQ(1000000u, ub);
+ h.add(0);
+ h.add(0);
+ h.add(0);
+ h.get_position_micro(0, &lb, &ub);
+ ASSERT_EQ(0u, lb);
+ ASSERT_EQ(1000000u, ub);
+}
+
+TEST(Histogram, Position2) {
+ pow2_hist_t h;
+ h.add(1);
+ h.add(1);
+ uint64_t lb, ub;
+ h.get_position_micro(0, &lb, &ub);
+ ASSERT_EQ(0u, lb);
+ ASSERT_EQ(0u, ub);
+ h.add(0);
+ h.get_position_micro(0, &lb, &ub);
+ ASSERT_EQ(0u, lb);
+ ASSERT_EQ(333333u, ub);
+ h.get_position_micro(1, &lb, &ub);
+ ASSERT_EQ(333333u, lb);
+ ASSERT_EQ(1000000u, ub);
+}
+
+TEST(Histogram, Position3) {
+ pow2_hist_t h;
+ h.h.resize(10, 0);
+ h.h[0] = 1;
+ h.h[5] = 1;
+ uint64_t lb, ub;
+ h.get_position_micro(4, &lb, &ub);
+ ASSERT_EQ(500000u, lb);
+ ASSERT_EQ(500000u, ub);
+}
+
+TEST(Histogram, Position4) {
+ pow2_hist_t h;
+ h.h.resize(10, 0);
+ h.h[0] = UINT_MAX;
+ h.h[5] = UINT_MAX;
+ uint64_t lb, ub;
+ h.get_position_micro(4, &lb, &ub);
+ ASSERT_EQ(0u, lb);
+ ASSERT_EQ(0u, ub);
+}
+
+TEST(Histogram, Decay) {
+ pow2_hist_t h;
+ h.set_bin(0, 123);
+ h.set_bin(3, 12);
+ h.set_bin(5, 1);
+ h.decay(1);
+ ASSERT_EQ(61, h.h[0]);
+ ASSERT_EQ(6, h.h[3]);
+ ASSERT_EQ(4u, h.h.size());
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_histogram &&
+ * valgrind --tool=memcheck --leak-check=full \
+ * ./unittest_histogram
+ * "
+ * End:
+ */
diff --git a/src/test/common/test_allocate_unique.cc b/src/test/common/test_allocate_unique.cc
new file mode 100644
index 000000000..94cb025a4
--- /dev/null
+++ b/src/test/common/test_allocate_unique.cc
@@ -0,0 +1,97 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/allocate_unique.h"
+#include <string>
+#include <vector>
+#include <gtest/gtest.h>
+
+namespace {
+
+// allocation events recorded by logging_allocator
+struct event {
+ size_t size;
+ bool allocated; // true for allocate(), false for deallocate()
+};
+using event_log = std::vector<event>;
+
+template <typename T>
+struct logging_allocator {
+ event_log *const log;
+
+ using value_type = T;
+
+ explicit logging_allocator(event_log *log) : log(log) {}
+ logging_allocator(const logging_allocator& other) : log(other.log) {}
+
+ template <typename U>
+ logging_allocator(const logging_allocator<U>& other) : log(other.log) {}
+
+ T* allocate(size_t n)
+ {
+ auto p = std::allocator<T>{}.allocate(n);
+ log->emplace_back(event{n * sizeof(T), true});
+ return p;
+ }
+ void deallocate(T* p, size_t n)
+ {
+ std::allocator<T>{}.deallocate(p, n);
+ if (p) {
+ log->emplace_back(event{n * sizeof(T), false});
+ }
+ }
+};
+
+} // anonymous namespace
+
+namespace ceph {
+
+TEST(AllocateUnique, Allocate)
+{
+ event_log log;
+ auto alloc = logging_allocator<char>{&log};
+ {
+ auto p = allocate_unique<char>(alloc, 'a');
+ static_assert(std::is_same_v<decltype(p),
+ std::unique_ptr<char, deallocator<logging_allocator<char>>>>);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(1, log.size());
+ EXPECT_EQ(1, log.front().size);
+ EXPECT_EQ(true, log.front().allocated);
+ }
+ ASSERT_EQ(2, log.size());
+ EXPECT_EQ(1, log.back().size);
+ EXPECT_EQ(false, log.back().allocated);
+}
+
+TEST(AllocateUnique, RebindAllocate)
+{
+ event_log log;
+ auto alloc = logging_allocator<char>{&log};
+ {
+ auto p = allocate_unique<std::string>(alloc, "a");
+ static_assert(std::is_same_v<decltype(p),
+ std::unique_ptr<std::string,
+ deallocator<logging_allocator<std::string>>>>);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(1, log.size());
+ EXPECT_EQ(sizeof(std::string), log.front().size);
+ EXPECT_EQ(true, log.front().allocated);
+ }
+ ASSERT_EQ(2, log.size());
+ EXPECT_EQ(sizeof(std::string), log.back().size);
+ EXPECT_EQ(false, log.back().allocated);
+}
+
+} // namespace ceph
diff --git a/src/test/common/test_async_completion.cc b/src/test/common/test_async_completion.cc
new file mode 100644
index 000000000..4cf4394e1
--- /dev/null
+++ b/src/test/common/test_async_completion.cc
@@ -0,0 +1,256 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/async/completion.h"
+#include <optional>
+#include <boost/intrusive/list.hpp>
+#include <gtest/gtest.h>
+
+namespace ceph::async {
+
+using boost::system::error_code;
+
+struct move_only {
+ move_only() = default;
+ move_only(move_only&&) = default;
+ move_only& operator=(move_only&&) = default;
+ move_only(const move_only&) = delete;
+ move_only& operator=(const move_only&) = delete;
+};
+
+TEST(AsyncCompletion, BindHandler)
+{
+ auto h1 = [] (int i, char c) {};
+ auto b1 = bind_handler(std::move(h1), 5, 'a');
+ b1();
+ const auto& c1 = b1;
+ c1();
+ std::move(b1)();
+
+ // move-only types can be forwarded with 'operator() &&'
+ auto h2 = [] (move_only&& m) {};
+ auto b2 = bind_handler(std::move(h2), move_only{});
+ std::move(b2)();
+
+ // references bound with std::ref() can be passed to all operator() overloads
+ auto h3 = [] (int& c) { c++; };
+ int count = 0;
+ auto b3 = bind_handler(std::move(h3), std::ref(count));
+ EXPECT_EQ(0, count);
+ b3();
+ EXPECT_EQ(1, count);
+ const auto& c3 = b3;
+ c3();
+ EXPECT_EQ(2, count);
+ std::move(b3)();
+ EXPECT_EQ(3, count);
+}
+
+TEST(AsyncCompletion, ForwardHandler)
+{
+ // move-only types can be forwarded with 'operator() &'
+ auto h = [] (move_only&& m) {};
+ auto b = bind_handler(std::move(h), move_only{});
+ auto f = forward_handler(std::move(b));
+ f();
+}
+
+TEST(AsyncCompletion, MoveOnly)
+{
+ boost::asio::io_context context;
+ auto ex1 = context.get_executor();
+
+ std::optional<error_code> ec1, ec2, ec3;
+ std::optional<move_only> arg3;
+ {
+ // move-only user data
+ using Completion = Completion<void(error_code), move_only>;
+ auto c = Completion::create(ex1, [&ec1] (error_code ec) { ec1 = ec; });
+ Completion::post(std::move(c), boost::asio::error::operation_aborted);
+ EXPECT_FALSE(ec1);
+ }
+ {
+ // move-only handler
+ using Completion = Completion<void(error_code)>;
+ auto c = Completion::create(ex1, [&ec2, m=move_only{}] (error_code ec) {
+ static_cast<void>(m);
+ ec2 = ec; });
+ Completion::post(std::move(c), boost::asio::error::operation_aborted);
+ EXPECT_FALSE(ec2);
+ }
+ {
+ // move-only arg in signature
+ using Completion = Completion<void(error_code, move_only)>;
+ auto c = Completion::create(ex1, [&] (error_code ec, move_only m) {
+ ec3 = ec;
+ arg3 = std::move(m);
+ });
+ Completion::post(std::move(c), boost::asio::error::operation_aborted, move_only{});
+ EXPECT_FALSE(ec3);
+ }
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec1);
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec2);
+ ASSERT_TRUE(ec3);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec3);
+ ASSERT_TRUE(arg3);
+}
+
+TEST(AsyncCompletion, VoidCompletion)
+{
+ boost::asio::io_context context;
+ auto ex1 = context.get_executor();
+
+ using Completion = Completion<void(error_code)>;
+ std::optional<error_code> ec1;
+
+ auto c = Completion::create(ex1, [&ec1] (error_code ec) { ec1 = ec; });
+ Completion::post(std::move(c), boost::asio::error::operation_aborted);
+
+ EXPECT_FALSE(ec1);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec1);
+}
+
+TEST(AsyncCompletion, CompletionList)
+{
+ boost::asio::io_context context;
+ auto ex1 = context.get_executor();
+
+ using T = AsBase<boost::intrusive::list_base_hook<>>;
+ using Completion = Completion<void(), T>;
+ boost::intrusive::list<Completion> completions;
+ int completed = 0;
+ for (int i = 0; i < 3; i++) {
+ auto c = Completion::create(ex1, [&] { completed++; });
+ completions.push_back(*c.release());
+ }
+ completions.clear_and_dispose([] (Completion *c) {
+ Completion::post(std::unique_ptr<Completion>{c});
+ });
+
+ EXPECT_EQ(0, completed);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ EXPECT_EQ(3, completed);
+}
+
+TEST(AsyncCompletion, CompletionPair)
+{
+ boost::asio::io_context context;
+ auto ex1 = context.get_executor();
+
+ using T = std::pair<int, std::string>;
+ using Completion = Completion<void(int, std::string), T>;
+
+ std::optional<T> t;
+ auto c = Completion::create(ex1, [&] (int first, std::string second) {
+ t = T{first, std::move(second)};
+ }, 2, "hello");
+
+ auto data = std::move(c->user_data);
+ Completion::post(std::move(c), data.first, std::move(data.second));
+
+ EXPECT_FALSE(t);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(t);
+ EXPECT_EQ(2, t->first);
+ EXPECT_EQ("hello", t->second);
+}
+
+TEST(AsyncCompletion, CompletionReference)
+{
+ boost::asio::io_context context;
+ auto ex1 = context.get_executor();
+
+ using Completion = Completion<void(int&)>;
+
+ auto c = Completion::create(ex1, [] (int& i) { ++i; });
+
+ int i = 42;
+ Completion::post(std::move(c), std::ref(i));
+
+ EXPECT_EQ(42, i);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ EXPECT_EQ(43, i);
+}
+
+struct throws_on_move {
+ throws_on_move() = default;
+ throws_on_move(throws_on_move&&) {
+ throw std::runtime_error("oops");
+ }
+ throws_on_move& operator=(throws_on_move&&) {
+ throw std::runtime_error("oops");
+ }
+ throws_on_move(const throws_on_move&) = default;
+ throws_on_move& operator=(const throws_on_move&) = default;
+};
+
+TEST(AsyncCompletion, ThrowOnCtor)
+{
+ boost::asio::io_context context;
+ auto ex1 = context.get_executor();
+ {
+ using Completion = Completion<void(int&)>;
+
+ // throw on Handler move construction
+ EXPECT_THROW(Completion::create(ex1, [t=throws_on_move{}] (int& i) {
+ static_cast<void>(t);
+ ++i; }),
+ std::runtime_error);
+ }
+ {
+ using T = throws_on_move;
+ using Completion = Completion<void(int&), T>;
+
+ // throw on UserData construction
+ EXPECT_THROW(Completion::create(ex1, [] (int& i) { ++i; }, throws_on_move{}),
+ std::runtime_error);
+ }
+}
+
+TEST(AsyncCompletion, FreeFunctions)
+{
+ boost::asio::io_context context;
+ auto ex1 = context.get_executor();
+
+ auto c1 = create_completion<void(), void>(ex1, [] {});
+ post(std::move(c1));
+
+ auto c2 = create_completion<void(int), int>(ex1, [] (int) {}, 5);
+ defer(std::move(c2), c2->user_data);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+}
+
+} // namespace ceph::async
diff --git a/src/test/common/test_async_shared_mutex.cc b/src/test/common/test_async_shared_mutex.cc
new file mode 100644
index 000000000..aed6e7b00
--- /dev/null
+++ b/src/test/common/test_async_shared_mutex.cc
@@ -0,0 +1,428 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/async/shared_mutex.h"
+#include <optional>
+#include <gtest/gtest.h>
+
+namespace ceph::async {
+
+using executor_type = boost::asio::io_context::executor_type;
+using unique_lock = std::unique_lock<SharedMutex<executor_type>>;
+using shared_lock = std::shared_lock<SharedMutex<executor_type>>;
+
+// return a lambda that captures its error code and lock
+auto capture(std::optional<boost::system::error_code>& ec, unique_lock& lock)
+{
+ return [&] (boost::system::error_code e, unique_lock l) {
+ ec = e;
+ lock = std::move(l);
+ };
+}
+auto capture(std::optional<boost::system::error_code>& ec, shared_lock& lock)
+{
+ return [&] (boost::system::error_code e, shared_lock l) {
+ ec = e;
+ lock = std::move(l);
+ };
+}
+
+TEST(SharedMutex, async_exclusive)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+
+ std::optional<boost::system::error_code> ec1, ec2, ec3;
+ unique_lock lock1, lock2, lock3;
+
+ // request three exclusive locks
+ mutex.async_lock(capture(ec1, lock1));
+ mutex.async_lock(capture(ec2, lock2));
+ mutex.async_lock(capture(ec3, lock3));
+
+ EXPECT_FALSE(ec1); // no callbacks until poll()
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+
+ context.poll();
+ EXPECT_FALSE(context.stopped()); // second lock still pending
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ EXPECT_FALSE(ec2);
+
+ lock1.unlock();
+
+ EXPECT_FALSE(ec2);
+
+ context.poll();
+ EXPECT_FALSE(context.stopped());
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(lock2);
+ EXPECT_FALSE(ec3);
+
+ lock2.unlock();
+
+ EXPECT_FALSE(ec3);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec3);
+ EXPECT_EQ(boost::system::errc::success, *ec3);
+ ASSERT_TRUE(lock3);
+}
+
+TEST(SharedMutex, async_shared)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+
+ std::optional<boost::system::error_code> ec1, ec2;
+ shared_lock lock1, lock2;
+
+ // request two shared locks
+ mutex.async_lock_shared(capture(ec1, lock1));
+ mutex.async_lock_shared(capture(ec2, lock2));
+
+ EXPECT_FALSE(ec1); // no callbacks until poll()
+ EXPECT_FALSE(ec2);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(lock2);
+}
+
+TEST(SharedMutex, async_exclusive_while_shared)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+
+ std::optional<boost::system::error_code> ec1, ec2;
+ shared_lock lock1;
+ unique_lock lock2;
+
+ // request a shared and exclusive lock
+ mutex.async_lock_shared(capture(ec1, lock1));
+ mutex.async_lock(capture(ec2, lock2));
+
+ EXPECT_FALSE(ec1); // no callbacks until poll()
+ EXPECT_FALSE(ec2);
+
+ context.poll();
+ EXPECT_FALSE(context.stopped()); // second lock still pending
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ EXPECT_FALSE(ec2);
+
+ lock1.unlock();
+
+ EXPECT_FALSE(ec2);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(lock2);
+}
+
+TEST(SharedMutex, async_shared_while_exclusive)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+
+ std::optional<boost::system::error_code> ec1, ec2;
+ unique_lock lock1;
+ shared_lock lock2;
+
+ // request an exclusive and shared lock
+ mutex.async_lock(capture(ec1, lock1));
+ mutex.async_lock_shared(capture(ec2, lock2));
+
+ EXPECT_FALSE(ec1); // no callbacks until poll()
+ EXPECT_FALSE(ec2);
+
+ context.poll();
+ EXPECT_FALSE(context.stopped()); // second lock still pending
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ EXPECT_FALSE(ec2);
+
+ lock1.unlock();
+
+ EXPECT_FALSE(ec2);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(lock2);
+}
+
+TEST(SharedMutex, async_prioritize_exclusive)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+
+ std::optional<boost::system::error_code> ec1, ec2, ec3;
+ shared_lock lock1, lock3;
+ unique_lock lock2;
+
+ // acquire a shared lock, then request an exclusive and another shared lock
+ mutex.async_lock_shared(capture(ec1, lock1));
+ mutex.async_lock(capture(ec2, lock2));
+ mutex.async_lock_shared(capture(ec3, lock3));
+
+ EXPECT_FALSE(ec1); // no callbacks until poll()
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+
+ context.poll();
+ EXPECT_FALSE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ EXPECT_FALSE(ec2);
+ // exclusive waiter blocks the second shared lock
+ EXPECT_FALSE(ec3);
+
+ lock1.unlock();
+
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+
+ context.poll();
+ EXPECT_FALSE(context.stopped());
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(lock2);
+ EXPECT_FALSE(ec3);
+}
+
+TEST(SharedMutex, async_cancel)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+
+ std::optional<boost::system::error_code> ec1, ec2, ec3, ec4;
+ unique_lock lock1, lock2;
+ shared_lock lock3, lock4;
+
+ // request 2 exclusive and shared locks
+ mutex.async_lock(capture(ec1, lock1));
+ mutex.async_lock(capture(ec2, lock2));
+ mutex.async_lock_shared(capture(ec3, lock3));
+ mutex.async_lock_shared(capture(ec4, lock4));
+
+ EXPECT_FALSE(ec1); // no callbacks until poll()
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+ EXPECT_FALSE(ec4);
+
+ context.poll();
+ EXPECT_FALSE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+ EXPECT_FALSE(ec4);
+
+ mutex.cancel();
+
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+ EXPECT_FALSE(ec4);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec2);
+ EXPECT_FALSE(lock2);
+ ASSERT_TRUE(ec3);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec3);
+ EXPECT_FALSE(lock3);
+ ASSERT_TRUE(ec4);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec4);
+ EXPECT_FALSE(lock4);
+}
+
+TEST(SharedMutex, async_destruct)
+{
+ boost::asio::io_context context;
+
+ std::optional<boost::system::error_code> ec1, ec2, ec3, ec4;
+ unique_lock lock1, lock2;
+ shared_lock lock3, lock4;
+
+ {
+ SharedMutex mutex(context.get_executor());
+
+ // request 2 exclusive and shared locks
+ mutex.async_lock(capture(ec1, lock1));
+ mutex.async_lock(capture(ec2, lock2));
+ mutex.async_lock_shared(capture(ec3, lock3));
+ mutex.async_lock_shared(capture(ec4, lock4));
+ }
+
+ EXPECT_FALSE(ec1); // no callbacks until poll()
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+ EXPECT_FALSE(ec4);
+
+ context.poll();
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec2);
+ EXPECT_FALSE(lock2);
+ ASSERT_TRUE(ec3);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec3);
+ EXPECT_FALSE(lock3);
+ ASSERT_TRUE(ec4);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec4);
+ EXPECT_FALSE(lock4);
+}
+
+// return a capture() lambda that's bound to the given executor
+template <typename Executor, typename ...Args>
+auto capture_ex(const Executor& ex, Args&& ...args)
+{
+ return boost::asio::bind_executor(ex, capture(std::forward<Args>(args)...));
+}
+
+TEST(SharedMutex, cross_executor)
+{
+ boost::asio::io_context mutex_context;
+ SharedMutex mutex(mutex_context.get_executor());
+
+ boost::asio::io_context callback_context;
+ auto ex2 = callback_context.get_executor();
+
+ std::optional<boost::system::error_code> ec1, ec2;
+ unique_lock lock1, lock2;
+
+ // request two exclusive locks
+ mutex.async_lock(capture_ex(ex2, ec1, lock1));
+ mutex.async_lock(capture_ex(ex2, ec2, lock2));
+
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ mutex_context.poll();
+ EXPECT_FALSE(mutex_context.stopped()); // maintains work on both executors
+
+ EXPECT_FALSE(ec1); // no callbacks until poll() on callback_context
+ EXPECT_FALSE(ec2);
+
+ callback_context.poll();
+ EXPECT_FALSE(callback_context.stopped()); // second lock still pending
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(lock1);
+ EXPECT_FALSE(ec2);
+
+ lock1.unlock();
+
+ mutex_context.poll();
+ EXPECT_TRUE(mutex_context.stopped());
+
+ EXPECT_FALSE(ec2);
+
+ callback_context.poll();
+ EXPECT_TRUE(callback_context.stopped());
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(lock2);
+}
+
+TEST(SharedMutex, try_exclusive)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+ {
+ std::lock_guard lock{mutex};
+ ASSERT_FALSE(mutex.try_lock()); // fail during exclusive
+ }
+ {
+ std::shared_lock lock{mutex};
+ ASSERT_FALSE(mutex.try_lock()); // fail during shared
+ }
+ ASSERT_TRUE(mutex.try_lock());
+ mutex.unlock();
+}
+
+TEST(SharedMutex, try_shared)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+ {
+ std::lock_guard lock{mutex};
+ ASSERT_FALSE(mutex.try_lock_shared()); // fail during exclusive
+ }
+ {
+ std::shared_lock lock{mutex};
+ ASSERT_TRUE(mutex.try_lock_shared()); // succeed during shared
+ mutex.unlock_shared();
+ }
+ ASSERT_TRUE(mutex.try_lock_shared());
+ mutex.unlock_shared();
+}
+
+TEST(SharedMutex, cancel)
+{
+ boost::asio::io_context context;
+ SharedMutex mutex(context.get_executor());
+
+ std::lock_guard l{mutex}; // exclusive lock blocks others
+
+ // make synchronous lock calls in other threads
+ auto f1 = std::async(std::launch::async, [&] { mutex.lock(); });
+ auto f2 = std::async(std::launch::async, [&] { mutex.lock_shared(); });
+
+ // this will race with spawned threads. just keep canceling until the
+ // futures are ready
+ const auto t = std::chrono::milliseconds(1);
+ do { mutex.cancel(); } while (f1.wait_for(t) != std::future_status::ready);
+ do { mutex.cancel(); } while (f2.wait_for(t) != std::future_status::ready);
+
+ EXPECT_THROW(f1.get(), boost::system::system_error);
+ EXPECT_THROW(f2.get(), boost::system::system_error);
+}
+
+} // namespace ceph::async
diff --git a/src/test/common/test_back_trace.cc b/src/test/common/test_back_trace.cc
new file mode 100644
index 000000000..97db32686
--- /dev/null
+++ b/src/test/common/test_back_trace.cc
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <boost/algorithm/string.hpp>
+#include <gtest/gtest.h>
+#include <regex>
+#include <sstream>
+#include <string>
+
+#include "common/BackTrace.h"
+#include "common/version.h"
+
+// a dummy function, so we can check "foo" in the backtrace.
+// do not mark this function as static or put it into an anonymous namespace,
+// otherwise it's function name will be removed in the backtrace.
+std::string foo()
+{
+ std::ostringstream oss;
+ oss << ceph::ClibBackTrace(1);
+ return oss.str();
+}
+
+// a typical backtrace looks like:
+//
+// ceph version Development (no_version)
+// 1: (foo[abi:cxx11]()+0x4a) [0x5562231cf22a]
+// 2: (BackTrace_Basic_Test::TestBody()+0x28) [0x5562231cf2fc]
+TEST(BackTrace, Basic) {
+ std::string bt = foo();
+ std::vector<std::string> lines;
+ boost::split(lines, bt, boost::is_any_of("\n"));
+ const unsigned lineno = 1;
+ ASSERT_GT(lines.size(), lineno);
+ ASSERT_EQ(lines[0].find(pretty_version_to_str()), 1U);
+ std::regex e{"^ 1: "
+#ifdef __FreeBSD__
+ "<foo.*>\\s"
+ "at\\s.*$"};
+#else
+ "\\(foo.*\\)\\s"
+ "\\[0x[[:xdigit:]]+\\]$"};
+#endif
+ EXPECT_TRUE(std::regex_match(lines[lineno], e));
+}
diff --git a/src/test/common/test_bit_vector.cc b/src/test/common/test_bit_vector.cc
new file mode 100644
index 000000000..b986c232a
--- /dev/null
+++ b/src/test/common/test_bit_vector.cc
@@ -0,0 +1,308 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * LGPL-2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <gtest/gtest.h>
+#include <cmath>
+#include "common/bit_vector.hpp"
+#include <boost/assign/list_of.hpp>
+
+using namespace ceph;
+
+template <uint8_t _bit_count>
+class TestParams {
+public:
+ static const uint8_t BIT_COUNT = _bit_count;
+};
+
+template <typename T>
+class BitVectorTest : public ::testing::Test {
+public:
+ typedef BitVector<T::BIT_COUNT> bit_vector_t;
+};
+
+typedef ::testing::Types<TestParams<2> > BitVectorTypes;
+TYPED_TEST_SUITE(BitVectorTest, BitVectorTypes);
+
+TYPED_TEST(BitVectorTest, resize) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ size_t size = 2357;
+
+ double elements_per_byte = 8 / bit_vector.BIT_COUNT;
+
+ bit_vector.resize(size);
+ ASSERT_EQ(bit_vector.size(), size);
+ ASSERT_EQ(bit_vector.get_data().length(), static_cast<uint64_t>(std::ceil(
+ size / elements_per_byte)));
+}
+
+TYPED_TEST(BitVectorTest, clear) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ bit_vector.resize(123);
+ bit_vector.clear();
+ ASSERT_EQ(0ull, bit_vector.size());
+ ASSERT_EQ(0ull, bit_vector.get_data().length());
+}
+
+TYPED_TEST(BitVectorTest, bit_order) {
+ typename TestFixture::bit_vector_t bit_vector;
+ bit_vector.resize(1);
+
+ uint8_t value = 1;
+ bit_vector[0] = value;
+
+ value <<= (8 - bit_vector.BIT_COUNT);
+ ASSERT_EQ(value, bit_vector.get_data()[0]);
+}
+
+TYPED_TEST(BitVectorTest, get_set) {
+ typename TestFixture::bit_vector_t bit_vector;
+ std::vector<uint64_t> ref;
+
+ uint64_t radix = 1 << bit_vector.BIT_COUNT;
+
+ size_t size = 1024;
+ bit_vector.resize(size);
+ ref.resize(size);
+ for (size_t i = 0; i < size; ++i) {
+ uint64_t v = rand() % radix;
+ ref[i] = v;
+ bit_vector[i] = v;
+ }
+
+ const typename TestFixture::bit_vector_t &const_bit_vector(bit_vector);
+ for (size_t i = 0; i < size; ++i) {
+ ASSERT_EQ(ref[i], bit_vector[i]);
+ ASSERT_EQ(ref[i], const_bit_vector[i]);
+ }
+}
+
+TYPED_TEST(BitVectorTest, get_buffer_extents) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ uint64_t element_count = 2 * bit_vector.BLOCK_SIZE + 51;
+ uint64_t elements_per_byte = 8 / bit_vector.BIT_COUNT;
+ bit_vector.resize(element_count * elements_per_byte);
+
+ uint64_t offset = (bit_vector.BLOCK_SIZE + 11) * elements_per_byte;
+ uint64_t length = (bit_vector.BLOCK_SIZE + 31) * elements_per_byte;
+ uint64_t data_byte_offset;
+ uint64_t object_byte_offset;
+ uint64_t byte_length;
+ bit_vector.get_data_extents(offset, length, &data_byte_offset,
+ &object_byte_offset, &byte_length);
+ ASSERT_EQ(bit_vector.BLOCK_SIZE, data_byte_offset);
+ ASSERT_EQ(bit_vector.BLOCK_SIZE + (element_count % bit_vector.BLOCK_SIZE),
+ byte_length);
+
+ bit_vector.get_data_extents(1, 1, &data_byte_offset, &object_byte_offset,
+ &byte_length);
+ ASSERT_EQ(0U, data_byte_offset);
+ ASSERT_EQ(bit_vector.get_header_length(), object_byte_offset);
+ ASSERT_EQ(bit_vector.BLOCK_SIZE, byte_length);
+}
+
+TYPED_TEST(BitVectorTest, get_header_length) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ bufferlist bl;
+ bit_vector.encode_header(bl);
+ ASSERT_EQ(bl.length(), bit_vector.get_header_length());
+}
+
+TYPED_TEST(BitVectorTest, get_footer_offset) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ bit_vector.resize(5111);
+
+ uint64_t data_byte_offset;
+ uint64_t object_byte_offset;
+ uint64_t byte_length;
+ bit_vector.get_data_extents(0, bit_vector.size(), &data_byte_offset,
+ &object_byte_offset, &byte_length);
+
+ ASSERT_EQ(bit_vector.get_header_length() + byte_length,
+ bit_vector.get_footer_offset());
+}
+
+TYPED_TEST(BitVectorTest, partial_decode_encode) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ uint64_t elements_per_byte = 8 / bit_vector.BIT_COUNT;
+ bit_vector.resize(9161 * elements_per_byte);
+ for (uint64_t i = 0; i < bit_vector.size(); ++i) {
+ bit_vector[i] = i % 4;
+ }
+
+ bufferlist bl;
+ encode(bit_vector, bl);
+ bit_vector.clear();
+
+ bufferlist header_bl;
+ header_bl.substr_of(bl, 0, bit_vector.get_header_length());
+ auto header_it = header_bl.cbegin();
+ bit_vector.decode_header(header_it);
+
+ uint64_t object_byte_offset;
+ uint64_t byte_length;
+ bit_vector.get_header_crc_extents(&object_byte_offset, &byte_length);
+ ASSERT_EQ(bit_vector.get_footer_offset() + 4, object_byte_offset);
+ ASSERT_EQ(4ULL, byte_length);
+
+ typedef std::pair<uint64_t, uint64_t> Extent;
+ typedef std::list<Extent> Extents;
+
+ Extents extents = boost::assign::list_of(
+ std::make_pair(0, 1))(
+ std::make_pair((bit_vector.BLOCK_SIZE * elements_per_byte) - 2, 4))(
+ std::make_pair((bit_vector.BLOCK_SIZE * elements_per_byte) + 2, 2))(
+ std::make_pair((2 * bit_vector.BLOCK_SIZE * elements_per_byte) - 2, 4))(
+ std::make_pair((2 * bit_vector.BLOCK_SIZE * elements_per_byte) + 2, 2))(
+ std::make_pair(2, 2 * bit_vector.BLOCK_SIZE));
+ for (Extents::iterator it = extents.begin(); it != extents.end(); ++it) {
+ bufferlist footer_bl;
+ uint64_t footer_byte_offset;
+ uint64_t footer_byte_length;
+ bit_vector.get_data_crcs_extents(it->first, it->second, &footer_byte_offset,
+ &footer_byte_length);
+ ASSERT_TRUE(footer_byte_offset + footer_byte_length <= bl.length());
+ footer_bl.substr_of(bl, footer_byte_offset, footer_byte_length);
+ auto footer_it = footer_bl.cbegin();
+ bit_vector.decode_data_crcs(footer_it, it->first);
+
+ uint64_t element_offset = it->first;
+ uint64_t element_length = it->second;
+ uint64_t data_byte_offset;
+ bit_vector.get_data_extents(element_offset, element_length,
+ &data_byte_offset, &object_byte_offset,
+ &byte_length);
+
+ bufferlist data_bl;
+ data_bl.substr_of(bl, bit_vector.get_header_length() + data_byte_offset,
+ byte_length);
+ auto data_it = data_bl.cbegin();
+ bit_vector.decode_data(data_it, data_byte_offset);
+
+ data_bl.clear();
+ bit_vector.encode_data(data_bl, data_byte_offset, byte_length);
+
+ footer_bl.clear();
+ bit_vector.encode_data_crcs(footer_bl, it->first, it->second);
+
+ bufferlist updated_bl;
+ updated_bl.substr_of(bl, 0,
+ bit_vector.get_header_length() + data_byte_offset);
+ updated_bl.append(data_bl);
+
+ if (data_byte_offset + byte_length < bit_vector.get_footer_offset()) {
+ uint64_t tail_data_offset = bit_vector.get_header_length() +
+ data_byte_offset + byte_length;
+ data_bl.substr_of(bl, tail_data_offset,
+ bit_vector.get_footer_offset() - tail_data_offset);
+ updated_bl.append(data_bl);
+ }
+
+ bufferlist full_footer;
+ full_footer.substr_of(bl, bit_vector.get_footer_offset(),
+ footer_byte_offset - bit_vector.get_footer_offset());
+ full_footer.append(footer_bl);
+
+ if (footer_byte_offset + footer_byte_length < bl.length()) {
+ bufferlist footer_bit;
+ auto footer_offset = footer_byte_offset + footer_byte_length;
+ footer_bit.substr_of(bl, footer_offset, bl.length() - footer_offset);
+ full_footer.append(footer_bit);
+ }
+
+ updated_bl.append(full_footer);
+ ASSERT_EQ(bl, updated_bl);
+
+ auto updated_it = updated_bl.cbegin();
+ decode(bit_vector, updated_it);
+ }
+}
+
+TYPED_TEST(BitVectorTest, header_crc) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ bufferlist header;
+ bit_vector.encode_header(header);
+
+ bufferlist footer;
+ bit_vector.encode_footer(footer);
+
+ auto it = footer.cbegin();
+ bit_vector.decode_footer(it);
+
+ bit_vector.resize(1);
+ bit_vector.encode_header(header);
+
+ it = footer.begin();
+ ASSERT_THROW(bit_vector.decode_footer(it), buffer::malformed_input);
+}
+
+TYPED_TEST(BitVectorTest, data_crc) {
+ typename TestFixture::bit_vector_t bit_vector1;
+ typename TestFixture::bit_vector_t bit_vector2;
+
+ uint64_t elements_per_byte = 8 / bit_vector1.BIT_COUNT;
+ bit_vector1.resize((bit_vector1.BLOCK_SIZE + 1) * elements_per_byte);
+ bit_vector2.resize((bit_vector2.BLOCK_SIZE + 1) * elements_per_byte);
+
+ uint64_t data_byte_offset;
+ uint64_t object_byte_offset;
+ uint64_t byte_length;
+ bit_vector1.get_data_extents(0, bit_vector1.size(), &data_byte_offset,
+ &object_byte_offset, &byte_length);
+
+ bufferlist data;
+ bit_vector1.encode_data(data, data_byte_offset, byte_length);
+
+ auto data_it = data.cbegin();
+ bit_vector1.decode_data(data_it, data_byte_offset);
+
+ bit_vector2[bit_vector2.size() - 1] = 1;
+
+ bufferlist dummy_data;
+ bit_vector2.encode_data(dummy_data, data_byte_offset, byte_length);
+
+ data_it = data.begin();
+ ASSERT_THROW(bit_vector2.decode_data(data_it, data_byte_offset),
+ buffer::malformed_input);
+}
+
+TYPED_TEST(BitVectorTest, iterator) {
+ typename TestFixture::bit_vector_t bit_vector;
+
+ uint64_t radix = 1 << bit_vector.BIT_COUNT;
+ uint64_t size = 25 * (1ULL << 20);
+ uint64_t offset = 0;
+
+ // create fragmented in-memory bufferlist layout
+ uint64_t resize = 0;
+ while (resize < size) {
+ resize += 4096;
+ if (resize > size) {
+ resize = size;
+ }
+ bit_vector.resize(resize);
+ }
+
+ for (auto it = bit_vector.begin(); it != bit_vector.end(); ++it, ++offset) {
+ *it = offset % radix;
+ }
+
+ offset = 123;
+ auto end_it = bit_vector.begin() + (size - 1024);
+ for (auto it = bit_vector.begin() + offset; it != end_it; ++it, ++offset) {
+ ASSERT_EQ(offset % radix, *it);
+ }
+}
diff --git a/src/test/common/test_blkdev.cc b/src/test/common/test_blkdev.cc
new file mode 100644
index 000000000..733273ad4
--- /dev/null
+++ b/src/test/common/test_blkdev.cc
@@ -0,0 +1,114 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <linux/kdev_t.h>
+
+#include "include/types.h"
+#include "common/blkdev.h"
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include <iostream>
+
+using namespace std;
+using namespace testing;
+
+class MockBlkDev : public BlkDev {
+ public:
+ // pass 0 as fd, so it won't try to use the empty devname
+ MockBlkDev() : BlkDev(0) {};
+ virtual ~MockBlkDev() {}
+
+ MOCK_CONST_METHOD0(sysfsdir, const char*());
+ MOCK_CONST_METHOD2(wholedisk, int(char* device, size_t max));
+};
+
+
+class BlockDevTest : public ::testing::Test {
+public:
+ string *root;
+
+protected:
+ virtual void SetUp() {
+ const char *sda_name = "sda";
+ const char *sdb_name = "sdb";
+ const char* env = getenv("CEPH_ROOT");
+ ASSERT_NE(env, nullptr) << "Environment Variable CEPH_ROOT not found!";
+ root = new string(env);
+ *root += "/src/test/common/test_blkdev_sys_block/sys";
+
+ EXPECT_CALL(sda, sysfsdir())
+ .WillRepeatedly(Return(root->c_str()));
+ EXPECT_CALL(sda, wholedisk(NotNull(), Ge(0ul)))
+ .WillRepeatedly(
+ DoAll(
+ SetArrayArgument<0>(sda_name, sda_name + strlen(sda_name) + 1),
+ Return(0)));
+
+ EXPECT_CALL(sdb, sysfsdir())
+ .WillRepeatedly(Return(root->c_str()));
+ EXPECT_CALL(sdb, wholedisk(NotNull(), Ge(0ul)))
+ .WillRepeatedly(
+ DoAll(
+ SetArrayArgument<0>(sdb_name, sdb_name + strlen(sdb_name) + 1),
+ Return(0)));
+ }
+
+ virtual void TearDown() {
+ delete root;
+ }
+
+ MockBlkDev sda, sdb;
+};
+
+TEST_F(BlockDevTest, device_model)
+{
+ char model[1000] = {0};
+ int rc = sda.model(model, sizeof(model));
+ ASSERT_EQ(0, rc);
+ ASSERT_STREQ(model, "myfancymodel");
+}
+
+TEST_F(BlockDevTest, discard)
+{
+ EXPECT_TRUE(sda.support_discard());
+ EXPECT_TRUE(sdb.support_discard());
+}
+
+TEST_F(BlockDevTest, is_rotational)
+{
+ EXPECT_FALSE(sda.is_rotational());
+ EXPECT_TRUE(sdb.is_rotational());
+}
+
+TEST(blkdev, _decode_model_enc)
+{
+
+ const char *foo[][2] = {
+ { "WDC\\x20WDS200T2B0A-00SM50\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20",
+ "WDC_WDS200T2B0A-00SM50" },
+ { 0, 0},
+ };
+
+ for (unsigned i = 0; foo[i][0]; ++i) {
+ std::string d = _decode_model_enc(foo[i][0]);
+ cout << " '" << foo[i][0] << "' -> '" << d << "'" << std::endl;
+ ASSERT_EQ(std::string(foo[i][1]), d);
+ }
+}
+
+TEST(blkdev, get_device_id)
+{
+ // this doesn't really test anything; it's just a way to exercise the
+ // get_device_id() code.
+ for (char c = 'a'; c < 'z'; ++c) {
+ char devname[4] = {'s', 'd', c, 0};
+ std::string err;
+ auto i = get_device_id(devname, &err);
+ cout << "devname " << devname << " -> '" << i
+ << "' (" << err << ")" << std::endl;
+ }
+}
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/add_random b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/add_random
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/add_random
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_granularity b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_granularity
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_granularity
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_max_bytes b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_max_bytes
new file mode 100644
index 000000000..eba4c7ccb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_max_bytes
@@ -0,0 +1 @@
+2147450880
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_zeroes_data b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_zeroes_data
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/discard_zeroes_data
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/hw_sector_size b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/hw_sector_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/hw_sector_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/fifo_batch b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/fifo_batch
new file mode 100644
index 000000000..b6a7d89c6
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/fifo_batch
@@ -0,0 +1 @@
+16
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/front_merges b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/front_merges
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/front_merges
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/read_expire b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/read_expire
new file mode 100644
index 000000000..1b79f38e2
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/read_expire
@@ -0,0 +1 @@
+500
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/write_expire b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/write_expire
new file mode 100644
index 000000000..e9c02dad1
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/write_expire
@@ -0,0 +1 @@
+5000
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/writes_starved b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/writes_starved
new file mode 100644
index 000000000..0cfbf0888
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iosched/writes_starved
@@ -0,0 +1 @@
+2
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iostats b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iostats
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/iostats
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/logical_block_size b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/logical_block_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/logical_block_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_hw_sectors_kb b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_hw_sectors_kb
new file mode 100644
index 000000000..10130bb02
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_hw_sectors_kb
@@ -0,0 +1 @@
+32767
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_integrity_segments b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_integrity_segments
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_integrity_segments
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_sectors_kb b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_sectors_kb
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_sectors_kb
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segment_size b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segment_size
new file mode 100644
index 000000000..e2ed8f4da
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segment_size
@@ -0,0 +1 @@
+65536
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segments b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segments
new file mode 100644
index 000000000..de8febe1c
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/max_segments
@@ -0,0 +1 @@
+168
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/minimum_io_size b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/minimum_io_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/minimum_io_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nomerges b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nomerges
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nomerges
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nr_requests b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nr_requests
new file mode 100644
index 000000000..a949a93df
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/nr_requests
@@ -0,0 +1 @@
+128
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/optimal_io_size b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/optimal_io_size
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/optimal_io_size
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/physical_block_size b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/physical_block_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/physical_block_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/read_ahead_kb b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/read_ahead_kb
new file mode 100644
index 000000000..a949a93df
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/read_ahead_kb
@@ -0,0 +1 @@
+128
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rotational b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rotational
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rotational
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rq_affinity b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rq_affinity
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/rq_affinity
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/scheduler b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/scheduler
new file mode 100644
index 000000000..7b940d86f
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/scheduler
@@ -0,0 +1 @@
+noop [deadline] cfq
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/write_same_max_bytes b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/write_same_max_bytes
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/cciss!c0d1/queue/write_same_max_bytes
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/bar b/src/test/common/test_blkdev_sys_block/sys/block/sda/bar
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/bar
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/dev b/src/test/common/test_blkdev_sys_block/sys/block/sda/dev
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/dev
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/device/model b/src/test/common/test_blkdev_sys_block/sys/block/sda/device/model
new file mode 100644
index 000000000..892d30d18
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/device/model
@@ -0,0 +1 @@
+myfancymodel
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/foo b/src/test/common/test_blkdev_sys_block/sys/block/sda/foo
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/foo
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/add_random b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/add_random
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/add_random
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_granularity b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_granularity
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_granularity
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_max_bytes b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_max_bytes
new file mode 100644
index 000000000..eba4c7ccb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_max_bytes
@@ -0,0 +1 @@
+2147450880
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_zeroes_data b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_zeroes_data
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/discard_zeroes_data
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/hw_sector_size b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/hw_sector_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/hw_sector_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/fifo_batch b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/fifo_batch
new file mode 100644
index 000000000..b6a7d89c6
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/fifo_batch
@@ -0,0 +1 @@
+16
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/front_merges b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/front_merges
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/front_merges
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/read_expire b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/read_expire
new file mode 100644
index 000000000..1b79f38e2
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/read_expire
@@ -0,0 +1 @@
+500
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/write_expire b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/write_expire
new file mode 100644
index 000000000..e9c02dad1
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/write_expire
@@ -0,0 +1 @@
+5000
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/writes_starved b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/writes_starved
new file mode 100644
index 000000000..0cfbf0888
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iosched/writes_starved
@@ -0,0 +1 @@
+2
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iostats b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iostats
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/iostats
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/logical_block_size b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/logical_block_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/logical_block_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_hw_sectors_kb b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_hw_sectors_kb
new file mode 100644
index 000000000..10130bb02
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_hw_sectors_kb
@@ -0,0 +1 @@
+32767
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_integrity_segments b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_integrity_segments
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_integrity_segments
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_sectors_kb b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_sectors_kb
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_sectors_kb
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segment_size b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segment_size
new file mode 100644
index 000000000..e2ed8f4da
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segment_size
@@ -0,0 +1 @@
+65536
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segments b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segments
new file mode 100644
index 000000000..de8febe1c
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/max_segments
@@ -0,0 +1 @@
+168
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/minimum_io_size b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/minimum_io_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/minimum_io_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nomerges b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nomerges
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nomerges
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nr_requests b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nr_requests
new file mode 100644
index 000000000..a949a93df
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/nr_requests
@@ -0,0 +1 @@
+128
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/optimal_io_size b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/optimal_io_size
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/optimal_io_size
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/physical_block_size b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/physical_block_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/physical_block_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/read_ahead_kb b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/read_ahead_kb
new file mode 100644
index 000000000..a949a93df
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/read_ahead_kb
@@ -0,0 +1 @@
+128
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rotational b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rotational
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rotational
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rq_affinity b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rq_affinity
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/rq_affinity
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/scheduler b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/scheduler
new file mode 100644
index 000000000..7b940d86f
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/scheduler
@@ -0,0 +1 @@
+noop [deadline] cfq
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/write_same_max_bytes b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/write_same_max_bytes
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sda/queue/write_same_max_bytes
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/bar b/src/test/common/test_blkdev_sys_block/sys/block/sdb/bar
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/bar
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/dev b/src/test/common/test_blkdev_sys_block/sys/block/sdb/dev
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/dev
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/device/model b/src/test/common/test_blkdev_sys_block/sys/block/sdb/device/model
new file mode 100644
index 000000000..892d30d18
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/device/model
@@ -0,0 +1 @@
+myfancymodel
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/foo b/src/test/common/test_blkdev_sys_block/sys/block/sdb/foo
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/foo
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/add_random b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/add_random
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/add_random
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_granularity b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_granularity
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_granularity
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_max_bytes b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_max_bytes
new file mode 100644
index 000000000..eba4c7ccb
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_max_bytes
@@ -0,0 +1 @@
+2147450880
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_zeroes_data b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_zeroes_data
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/discard_zeroes_data
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/hw_sector_size b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/hw_sector_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/hw_sector_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/fifo_batch b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/fifo_batch
new file mode 100644
index 000000000..b6a7d89c6
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/fifo_batch
@@ -0,0 +1 @@
+16
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/front_merges b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/front_merges
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/front_merges
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/read_expire b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/read_expire
new file mode 100644
index 000000000..1b79f38e2
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/read_expire
@@ -0,0 +1 @@
+500
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/write_expire b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/write_expire
new file mode 100644
index 000000000..e9c02dad1
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/write_expire
@@ -0,0 +1 @@
+5000
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/writes_starved b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/writes_starved
new file mode 100644
index 000000000..0cfbf0888
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iosched/writes_starved
@@ -0,0 +1 @@
+2
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iostats b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iostats
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/iostats
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/logical_block_size b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/logical_block_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/logical_block_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_hw_sectors_kb b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_hw_sectors_kb
new file mode 100644
index 000000000..10130bb02
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_hw_sectors_kb
@@ -0,0 +1 @@
+32767
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_integrity_segments b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_integrity_segments
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_integrity_segments
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_sectors_kb b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_sectors_kb
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_sectors_kb
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segment_size b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segment_size
new file mode 100644
index 000000000..e2ed8f4da
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segment_size
@@ -0,0 +1 @@
+65536
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segments b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segments
new file mode 100644
index 000000000..de8febe1c
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/max_segments
@@ -0,0 +1 @@
+168
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/minimum_io_size b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/minimum_io_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/minimum_io_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nomerges b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nomerges
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nomerges
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nr_requests b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nr_requests
new file mode 100644
index 000000000..a949a93df
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/nr_requests
@@ -0,0 +1 @@
+128
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/optimal_io_size b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/optimal_io_size
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/optimal_io_size
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/physical_block_size b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/physical_block_size
new file mode 100644
index 000000000..4d0e90cbc
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/physical_block_size
@@ -0,0 +1 @@
+512
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/queue b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/queue
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/queue
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/read_ahead_kb b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/read_ahead_kb
new file mode 100644
index 000000000..a949a93df
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/read_ahead_kb
@@ -0,0 +1 @@
+128
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rotational b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rotational
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rotational
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rq_affinity b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rq_affinity
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/rq_affinity
@@ -0,0 +1 @@
+1
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/scheduler b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/scheduler
new file mode 100644
index 000000000..7b940d86f
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/scheduler
@@ -0,0 +1 @@
+noop [deadline] cfq
diff --git a/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/write_same_max_bytes b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/write_same_max_bytes
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/src/test/common/test_blkdev_sys_block/sys/block/sdb/queue/write_same_max_bytes
@@ -0,0 +1 @@
+0
diff --git a/src/test/common/test_blocked_completion.cc b/src/test/common/test_blocked_completion.cc
new file mode 100644
index 000000000..71e5784af
--- /dev/null
+++ b/src/test/common/test_blocked_completion.cc
@@ -0,0 +1,237 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#include <boost/asio.hpp>
+#include <boost/system/error_code.hpp>
+
+#include <gtest/gtest.h>
+
+#include "common/async/bind_handler.h"
+#include "common/async/blocked_completion.h"
+#include "common/async/forward_handler.h"
+
+using namespace std::literals;
+
+namespace ba = boost::asio;
+namespace bs = boost::system;
+namespace ca = ceph::async;
+
+class context_thread {
+ ba::io_context c;
+ ba::executor_work_guard<ba::io_context::executor_type> guard;
+ std::thread th;
+
+public:
+ context_thread() noexcept
+ : guard(ba::make_work_guard(c)),
+ th([this]() noexcept { c.run();}) {}
+
+ ~context_thread() {
+ guard.reset();
+ th.join();
+ }
+
+ ba::io_context& io_context() noexcept {
+ return c;
+ }
+
+ ba::io_context::executor_type get_executor() noexcept {
+ return c.get_executor();
+ }
+};
+
+struct move_only {
+ move_only() = default;
+ move_only(move_only&&) = default;
+ move_only& operator=(move_only&&) = default;
+ move_only(const move_only&) = delete;
+ move_only& operator=(const move_only&) = delete;
+};
+
+struct defaultless {
+ int a;
+ defaultless(int a) : a(a) {}
+};
+
+template<typename Executor, typename CompletionToken, typename... Args>
+auto id(const Executor& executor, CompletionToken&& token,
+ Args&& ...args)
+{
+ ba::async_completion<CompletionToken, void(Args...)> init(token);
+ auto a = ba::get_associated_allocator(init.completion_handler);
+ executor.post(ca::forward_handler(
+ ca::bind_handler(std::move(init.completion_handler),
+ std::forward<Args>(args)...)),
+ a);
+ return init.result.get();
+}
+
+TEST(BlockedCompletion, Void)
+{
+ context_thread t;
+
+ ba::post(t.get_executor(), ca::use_blocked);
+}
+
+TEST(BlockedCompletion, Timer)
+{
+ context_thread t;
+ ba::steady_timer timer(t.io_context(), 50ms);
+ timer.async_wait(ca::use_blocked);
+}
+
+TEST(BlockedCompletion, NoError)
+{
+ context_thread t;
+ ba::steady_timer timer(t.io_context(), 1s);
+ bs::error_code ec;
+
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked, bs::error_code{}));
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec], bs::error_code{}));
+ EXPECT_FALSE(ec);
+
+ int i;
+ EXPECT_NO_THROW(i = id(t.get_executor(), ca::use_blocked,
+ bs::error_code{}, 5));
+ ASSERT_EQ(5, i);
+ EXPECT_NO_THROW(i = id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{}, 7));
+ EXPECT_FALSE(ec);
+ ASSERT_EQ(7, i);
+
+ float j;
+
+ EXPECT_NO_THROW(std::tie(i, j) = id(t.get_executor(), ca::use_blocked, 9,
+ 3.5));
+ ASSERT_EQ(9, i);
+ ASSERT_EQ(3.5, j);
+ EXPECT_NO_THROW(std::tie(i, j) = id(t.get_executor(), ca::use_blocked[ec],
+ 11, 2.25));
+ EXPECT_FALSE(ec);
+ ASSERT_EQ(11, i);
+ ASSERT_EQ(2.25, j);
+}
+
+TEST(BlockedCompletion, AnError)
+{
+ context_thread t;
+ ba::steady_timer timer(t.io_context(), 1s);
+ bs::error_code ec;
+
+ EXPECT_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{EDOM, bs::system_category()}),
+ bs::system_error);
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{EDOM, bs::system_category()}));
+ EXPECT_EQ(bs::error_code(EDOM, bs::system_category()), ec);
+
+ EXPECT_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{EDOM, bs::system_category()}, 5),
+ bs::system_error);
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{EDOM, bs::system_category()}, 5));
+ EXPECT_EQ(bs::error_code(EDOM, bs::system_category()), ec);
+
+ EXPECT_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{EDOM, bs::system_category()}, 5, 3),
+ bs::system_error);
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{EDOM, bs::system_category()}, 5, 3));
+ EXPECT_EQ(bs::error_code(EDOM, bs::system_category()), ec);
+}
+
+TEST(BlockedCompletion, MoveOnly)
+{
+ context_thread t;
+ ba::steady_timer timer(t.io_context(), 1s);
+ bs::error_code ec;
+
+
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{}, move_only{}));
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{}, move_only{}));
+ EXPECT_FALSE(ec);
+
+ {
+ auto [i, j] = id(t.get_executor(), ca::use_blocked, move_only{}, 5);
+ EXPECT_EQ(j, 5);
+ }
+ {
+ auto [i, j] = id(t.get_executor(), ca::use_blocked[ec], move_only{}, 5);
+ EXPECT_EQ(j, 5);
+ }
+ EXPECT_FALSE(ec);
+
+
+ EXPECT_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{EDOM, bs::system_category()}, move_only{}),
+ bs::system_error);
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{EDOM, bs::system_category()}, move_only{}));
+ EXPECT_EQ(bs::error_code(EDOM, bs::system_category()), ec);
+
+ EXPECT_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{EDOM, bs::system_category()}, move_only{}, 3),
+ bs::system_error);
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{EDOM, bs::system_category()},
+ move_only{}, 3));
+ EXPECT_EQ(bs::error_code(EDOM, bs::system_category()), ec);
+}
+
+TEST(BlockedCompletion, DefaultLess)
+{
+ context_thread t;
+ ba::steady_timer timer(t.io_context(), 1s);
+ bs::error_code ec;
+
+
+ {
+ auto l = id(t.get_executor(), ca::use_blocked, bs::error_code{}, defaultless{5});
+ EXPECT_EQ(5, l.a);
+ }
+ {
+ auto l = id(t.get_executor(), ca::use_blocked[ec], bs::error_code{}, defaultless{7});
+ EXPECT_EQ(7, l.a);
+ }
+
+ {
+ auto [i, j] = id(t.get_executor(), ca::use_blocked, defaultless{3}, 5);
+ EXPECT_EQ(i.a, 3);
+ EXPECT_EQ(j, 5);
+ }
+ {
+ auto [i, j] = id(t.get_executor(), ca::use_blocked[ec], defaultless{3}, 5);
+ EXPECT_EQ(i.a, 3);
+ EXPECT_EQ(j, 5);
+ }
+ EXPECT_FALSE(ec);
+
+ EXPECT_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{EDOM, bs::system_category()}, move_only{}),
+ bs::system_error);
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{EDOM, bs::system_category()}, move_only{}));
+ EXPECT_EQ(bs::error_code(EDOM, bs::system_category()), ec);
+
+ EXPECT_THROW(id(t.get_executor(), ca::use_blocked,
+ bs::error_code{EDOM, bs::system_category()}, move_only{}, 3),
+ bs::system_error);
+ EXPECT_NO_THROW(id(t.get_executor(), ca::use_blocked[ec],
+ bs::error_code{EDOM, bs::system_category()},
+ move_only{}, 3));
+ EXPECT_EQ(bs::error_code(EDOM, bs::system_category()), ec);
+}
diff --git a/src/test/common/test_bloom_filter.cc b/src/test/common/test_bloom_filter.cc
new file mode 100644
index 000000000..9eb45689b
--- /dev/null
+++ b/src/test/common/test_bloom_filter.cc
@@ -0,0 +1,323 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank <info@inktank.com>
+ *
+ * LGPL-2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "include/stringify.h"
+#include "common/bloom_filter.hpp"
+
+TEST(BloomFilter, Basic) {
+ bloom_filter bf(10, .1, 1);
+ bf.insert("foo");
+ bf.insert("bar");
+
+ ASSERT_TRUE(bf.contains("foo"));
+ ASSERT_TRUE(bf.contains("bar"));
+
+ ASSERT_EQ(2U, bf.element_count());
+}
+
+TEST(BloomFilter, Empty) {
+ bloom_filter bf;
+ for (int i=0; i<100; ++i) {
+ ASSERT_FALSE(bf.contains((uint32_t) i));
+ ASSERT_FALSE(bf.contains(stringify(i)));
+ }
+}
+
+TEST(BloomFilter, Sweep) {
+ std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
+ std::cout.precision(5);
+ std::cout << "# max\tfpp\tactual\tsize\tB/insert" << std::endl;
+ for (int ex = 3; ex < 12; ex += 2) {
+ for (float fpp = .001; fpp < .5; fpp *= 4.0) {
+ int max = 2 << ex;
+ bloom_filter bf(max, fpp, 1);
+ bf.insert("foo");
+ bf.insert("bar");
+
+ ASSERT_TRUE(bf.contains("foo"));
+ ASSERT_TRUE(bf.contains("bar"));
+
+ for (int n = 0; n < max; n++)
+ bf.insert("ok" + stringify(n));
+
+ int test = max * 100;
+ int hit = 0;
+ for (int n = 0; n < test; n++)
+ if (bf.contains("asdf" + stringify(n)))
+ hit++;
+
+ ASSERT_TRUE(bf.contains("foo"));
+ ASSERT_TRUE(bf.contains("bar"));
+
+ double actual = (double)hit / (double)test;
+
+ bufferlist bl;
+ encode(bf, bl);
+
+ double byte_per_insert = (double)bl.length() / (double)max;
+
+ std::cout << max << "\t" << fpp << "\t" << actual << "\t" << bl.length() << "\t" << byte_per_insert << std::endl;
+ ASSERT_TRUE(actual < fpp * 10);
+
+ }
+ }
+}
+
+TEST(BloomFilter, SweepInt) {
+ unsigned int seed = 0;
+ std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
+ std::cout.precision(5);
+ std::cout << "# max\tfpp\tactual\tsize\tB/insert\tdensity\tapprox_element_count" << std::endl;
+ for (int ex = 3; ex < 12; ex += 2) {
+ for (float fpp = .001; fpp < .5; fpp *= 4.0) {
+ int max = 2 << ex;
+ bloom_filter bf(max, fpp, 1);
+ bf.insert("foo");
+ bf.insert("bar");
+
+ ASSERT_TRUE(123);
+ ASSERT_TRUE(456);
+
+ // In Ceph code, the uint32_t input routines to the bloom filter
+ // are used with hash values that are uniformly distributed over
+ // the uint32_t range. To model this behavior in the test, we
+ // pass in values generated by a pseudo-random generator.
+ // To make the test reproducible anyway, use a fixed seed here,
+ // but a different one in each instance.
+ srand(seed++);
+
+ for (int n = 0; n < max; n++)
+ bf.insert((uint32_t) rand());
+
+ int test = max * 100;
+ int hit = 0;
+ for (int n = 0; n < test; n++)
+ if (bf.contains((uint32_t) rand()))
+ hit++;
+
+ ASSERT_TRUE(123);
+ ASSERT_TRUE(456);
+
+ double actual = (double)hit / (double)test;
+
+ bufferlist bl;
+ encode(bf, bl);
+
+ double byte_per_insert = (double)bl.length() / (double)max;
+
+ std::cout << max << "\t" << fpp << "\t" << actual << "\t" << bl.length() << "\t" << byte_per_insert
+ << "\t" << bf.density() << "\t" << bf.approx_unique_element_count() << std::endl;
+ ASSERT_TRUE(actual < fpp * 3);
+ ASSERT_TRUE(actual > fpp / 3);
+ ASSERT_TRUE(bf.density() > 0.40);
+ ASSERT_TRUE(bf.density() < 0.60);
+ }
+ }
+}
+
+
+TEST(BloomFilter, CompressibleSweep) {
+ unsigned int seed = 0;
+ std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
+ std::cout.precision(5);
+ std::cout << "# max\tins\test ins\tafter\ttgtfpp\tactual\tsize\tb/elem\n";
+ float fpp = .01;
+ int max = 1024;
+ for (int div = 1; div < 10; div++) {
+ compressible_bloom_filter bf(max, fpp, 1);
+
+ // See the comment in SweepInt.
+ srand(seed++);
+
+ std::vector<uint32_t> values;
+ int t = max/div;
+ for (int n = 0; n < t; n++) {
+ uint32_t val = (uint32_t) rand();
+ bf.insert(val);
+ values.push_back(val);
+ }
+
+ unsigned est = bf.approx_unique_element_count();
+ if (div > 1)
+ bf.compress(1.0 / div);
+
+ for (auto val : values)
+ ASSERT_TRUE(bf.contains(val));
+
+ int test = max * 100;
+ int hit = 0;
+ for (int n = 0; n < test; n++)
+ if (bf.contains((uint32_t) rand()))
+ hit++;
+
+ double actual = (double)hit / (double)test;
+
+ bufferlist bl;
+ encode(bf, bl);
+
+ double byte_per_insert = (double)bl.length() / (double)max;
+ unsigned est_after = bf.approx_unique_element_count();
+ std::cout << max
+ << "\t" << t
+ << "\t" << est
+ << "\t" << est_after
+ << "\t" << fpp
+ << "\t" << actual
+ << "\t" << bl.length() << "\t" << byte_per_insert
+ << std::endl;
+
+ ASSERT_TRUE(actual < fpp * 2.0);
+ ASSERT_TRUE(actual > fpp / 2.0);
+ ASSERT_TRUE(est_after < est * 2);
+ ASSERT_TRUE(est_after > est / 2);
+ }
+}
+
+
+
+TEST(BloomFilter, BinSweep) {
+ std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
+ std::cout.precision(5);
+ int total_max = 16384;
+ float total_fpp = .01;
+ std::cout << "total_inserts " << total_max << " target-fpp " << total_fpp << std::endl;
+ for (int bins = 1; bins < 16; ++bins) {
+ int max = total_max / bins;
+ float fpp = total_fpp / bins;//pow(total_fpp, bins);
+
+ std::vector<std::unique_ptr<bloom_filter>> ls;
+ bufferlist bl;
+ for (int i=0; i<bins; i++) {
+ ls.push_back(std::make_unique<bloom_filter>(max, fpp, i));
+ for (int j=0; j<max; j++) {
+ ls.back()->insert(10000 * (i+1) + j);
+ }
+ encode(*ls.front(), bl);
+ }
+
+ int hit = 0;
+ int test = max * 100;
+ for (int i=0; i<test; ++i) {
+ for (std::vector<std::unique_ptr<bloom_filter>>::iterator j = ls.begin(); j != ls.end(); ++j) {
+ if ((*j)->contains(i * 732)) { // note: sequential i does not work here; the intenral int hash is weak!!
+ hit++;
+ break;
+ }
+ }
+ }
+
+ double actual = (double)hit / (double)test;
+ std::cout << "bins " << bins << " bin-max " << max << " bin-fpp " << fpp
+ << " actual-fpp " << actual
+ << " total-size " << bl.length() << std::endl;
+ }
+}
+
+// disable these tests; doing dual insertions in consecutive filters
+// appears to be equivalent to doing a single insertion in a bloom
+// filter that is twice as big.
+#if 0
+
+// test the fpp over a sequence of bloom filters, each with unique
+// items inserted into it.
+//
+// we expect: actual_fpp = num_filters * per_filter_fpp
+TEST(BloomFilter, Sequence) {
+
+ int max = 1024;
+ double fpp = .01;
+ for (int seq = 2; seq <= 128; seq *= 2) {
+ std::vector<bloom_filter*> ls;
+ for (int i=0; i<seq; i++) {
+ ls.push_back(new bloom_filter(max*2, fpp, i));
+ for (int j=0; j<max; j++) {
+ ls.back()->insert("ok" + stringify(j) + "_" + stringify(i));
+ if (ls.size() > 1)
+ ls[ls.size() - 2]->insert("ok" + stringify(j) + "_" + stringify(i));
+ }
+ }
+
+ int hit = 0;
+ int test = max * 100;
+ for (int i=0; i<test; ++i) {
+ for (std::vector<bloom_filter*>::iterator j = ls.begin(); j != ls.end(); ++j) {
+ if ((*j)->contains("bad" + stringify(i))) {
+ hit++;
+ break;
+ }
+ }
+ }
+
+ double actual = (double)hit / (double)test;
+ std::cout << "seq " << seq << " max " << max << " fpp " << fpp << " actual " << actual << std::endl;
+ }
+}
+
+// test the ffp over a sequence of bloom filters, where actual values
+// are always inserted into a consecutive pair of filters. in order
+// to have a false positive, we need to falsely match two consecutive
+// filters.
+//
+// we expect: actual_fpp = num_filters * per_filter_fpp^2
+TEST(BloomFilter, SequenceDouble) {
+ int max = 1024;
+ double fpp = .01;
+ for (int seq = 2; seq <= 128; seq *= 2) {
+ std::vector<bloom_filter*> ls;
+ for (int i=0; i<seq; i++) {
+ ls.push_back(new bloom_filter(max*2, fpp, i));
+ for (int j=0; j<max; j++) {
+ ls.back()->insert("ok" + stringify(j) + "_" + stringify(i));
+ if (ls.size() > 1)
+ ls[ls.size() - 2]->insert("ok" + stringify(j) + "_" + stringify(i));
+ }
+ }
+
+ int hit = 0;
+ int test = max * 100;
+ int run = 0;
+ for (int i=0; i<test; ++i) {
+ for (std::vector<bloom_filter*>::iterator j = ls.begin(); j != ls.end(); ++j) {
+ if ((*j)->contains("bad" + stringify(i))) {
+ run++;
+ if (run >= 2) {
+ hit++;
+ break;
+ }
+ } else {
+ run = 0;
+ }
+ }
+ }
+
+ double actual = (double)hit / (double)test;
+ std::cout << "seq " << seq << " max " << max << " fpp " << fpp << " actual " << actual
+ << " expected " << (fpp*fpp*(double)seq) << std::endl;
+ }
+}
+
+#endif
+
+TEST(BloomFilter, Assignement) {
+ bloom_filter bf1(10, .1, 1), bf2;
+
+ bf1.insert("foo");
+ bf2 = bf1;
+ bf1.insert("bar");
+
+ ASSERT_TRUE(bf2.contains("foo"));
+ ASSERT_FALSE(bf2.contains("bar"));
+
+ ASSERT_EQ(2U, bf1.element_count());
+ ASSERT_EQ(1U, bf2.element_count());
+}
diff --git a/src/test/common/test_bounded_key_counter.cc b/src/test/common/test_bounded_key_counter.cc
new file mode 100644
index 000000000..a87394051
--- /dev/null
+++ b/src/test/common/test_bounded_key_counter.cc
@@ -0,0 +1,200 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "common/bounded_key_counter.h"
+#include <gtest/gtest.h>
+
+namespace {
+
+// call get_highest() and return the number of callbacks
+template <typename Key, typename Count>
+size_t count_highest(BoundedKeyCounter<Key, Count>& counter, size_t count)
+{
+ size_t callbacks = 0;
+ counter.get_highest(count, [&callbacks] (const Key& key, Count count) {
+ ++callbacks;
+ });
+ return callbacks;
+}
+
+// call get_highest() and return the key/value pairs as a vector
+template <typename Key, typename Count,
+ typename Vector = std::vector<std::pair<Key, Count>>>
+Vector get_highest(BoundedKeyCounter<Key, Count>& counter, size_t count)
+{
+ Vector results;
+ counter.get_highest(count, [&results] (const Key& key, Count count) {
+ results.emplace_back(key, count);
+ });
+ return results;
+}
+
+} // anonymous namespace
+
+TEST(BoundedKeyCounter, Insert)
+{
+ BoundedKeyCounter<int, int> counter(2);
+ EXPECT_EQ(1, counter.insert(0)); // insert new key
+ EXPECT_EQ(2, counter.insert(0)); // increment counter
+ EXPECT_EQ(7, counter.insert(0, 5)); // add 5 to counter
+ EXPECT_EQ(1, counter.insert(1)); // insert new key
+ EXPECT_EQ(0, counter.insert(2)); // reject new key
+}
+
+TEST(BoundedKeyCounter, Erase)
+{
+ BoundedKeyCounter<int, int> counter(10);
+
+ counter.erase(0); // ok to erase nonexistent key
+ EXPECT_EQ(1, counter.insert(1, 1));
+ EXPECT_EQ(2, counter.insert(2, 2));
+ EXPECT_EQ(3, counter.insert(3, 3));
+ counter.erase(2);
+ counter.erase(1);
+ counter.erase(3);
+ counter.erase(3);
+ EXPECT_EQ(0u, count_highest(counter, 10));
+}
+
+TEST(BoundedKeyCounter, Size)
+{
+ BoundedKeyCounter<int, int> counter(4);
+ EXPECT_EQ(0u, counter.size());
+ EXPECT_EQ(1, counter.insert(1, 1));
+ EXPECT_EQ(1u, counter.size());
+ EXPECT_EQ(2, counter.insert(2, 2));
+ EXPECT_EQ(2u, counter.size());
+ EXPECT_EQ(3, counter.insert(3, 3));
+ EXPECT_EQ(3u, counter.size());
+ EXPECT_EQ(4, counter.insert(4, 4));
+ EXPECT_EQ(4u, counter.size());
+ EXPECT_EQ(0, counter.insert(5, 5)); // reject new key
+ EXPECT_EQ(4u, counter.size()); // size unchanged
+ EXPECT_EQ(5, counter.insert(4, 1)); // update existing key
+ EXPECT_EQ(4u, counter.size()); // size unchanged
+ counter.erase(2);
+ EXPECT_EQ(3u, counter.size());
+ counter.erase(2); // erase duplicate
+ EXPECT_EQ(3u, counter.size()); // size unchanged
+ counter.erase(4);
+ EXPECT_EQ(2u, counter.size());
+ counter.erase(1);
+ EXPECT_EQ(1u, counter.size());
+ counter.erase(3);
+ EXPECT_EQ(0u, counter.size());
+ EXPECT_EQ(6, counter.insert(6, 6));
+ EXPECT_EQ(1u, counter.size());
+ counter.clear();
+ EXPECT_EQ(0u, counter.size());
+}
+
+TEST(BoundedKeyCounter, GetHighest)
+{
+ BoundedKeyCounter<int, int> counter(10);
+ using Vector = std::vector<std::pair<int, int>>;
+
+ EXPECT_EQ(0u, count_highest(counter, 0)); // ok to request 0
+ EXPECT_EQ(0u, count_highest(counter, 10)); // empty
+ EXPECT_EQ(0u, count_highest(counter, 999)); // ok to request count >> 10
+
+ EXPECT_EQ(1, counter.insert(1, 1));
+ EXPECT_EQ(Vector({{1,1}}), get_highest(counter, 10));
+ EXPECT_EQ(2, counter.insert(2, 2));
+ EXPECT_EQ(Vector({{2,2},{1,1}}), get_highest(counter, 10));
+ EXPECT_EQ(3, counter.insert(3, 3));
+ EXPECT_EQ(Vector({{3,3},{2,2},{1,1}}), get_highest(counter, 10));
+ EXPECT_EQ(3, counter.insert(4, 3)); // insert duplicated count=3
+ // still returns 4 entries (but order of {3,3} and {4,3} is unspecified)
+ EXPECT_EQ(4u, count_highest(counter, 10));
+ counter.erase(3);
+ EXPECT_EQ(Vector({{4,3},{2,2},{1,1}}), get_highest(counter, 10));
+ EXPECT_EQ(0u, count_highest(counter, 0)); // requesting 0 still returns 0
+}
+
+TEST(BoundedKeyCounter, Clear)
+{
+ BoundedKeyCounter<int, int> counter(2);
+ EXPECT_EQ(1, counter.insert(0)); // insert new key
+ EXPECT_EQ(1, counter.insert(1)); // insert new key
+ EXPECT_EQ(2u, count_highest(counter, 2)); // return 2 entries
+
+ counter.clear();
+
+ EXPECT_EQ(0u, count_highest(counter, 2)); // return 0 entries
+ EXPECT_EQ(1, counter.insert(1)); // insert new key
+ EXPECT_EQ(1, counter.insert(2)); // insert new unique key
+ EXPECT_EQ(2u, count_highest(counter, 2)); // return 2 entries
+}
+
+// tests for partial sort and invalidation
+TEST(BoundedKeyCounter, GetNumSorted)
+{
+ struct MockCounter : public BoundedKeyCounter<int, int> {
+ using BoundedKeyCounter<int, int>::BoundedKeyCounter;
+ // expose as public for testing sort invalidations
+ using BoundedKeyCounter<int, int>::get_num_sorted;
+ };
+
+ MockCounter counter(10);
+
+ EXPECT_EQ(0u, counter.get_num_sorted());
+ EXPECT_EQ(0u, count_highest(counter, 10));
+ EXPECT_EQ(0u, counter.get_num_sorted());
+
+ EXPECT_EQ(2, counter.insert(2, 2));
+ EXPECT_EQ(3, counter.insert(3, 3));
+ EXPECT_EQ(4, counter.insert(4, 4));
+ EXPECT_EQ(0u, counter.get_num_sorted());
+
+ EXPECT_EQ(0u, count_highest(counter, 0));
+ EXPECT_EQ(0u, counter.get_num_sorted());
+ EXPECT_EQ(1u, count_highest(counter, 1));
+ EXPECT_EQ(1u, counter.get_num_sorted());
+ EXPECT_EQ(2u, count_highest(counter, 2));
+ EXPECT_EQ(2u, counter.get_num_sorted());
+ EXPECT_EQ(3u, count_highest(counter, 10));
+ EXPECT_EQ(3u, counter.get_num_sorted());
+
+ EXPECT_EQ(1, counter.insert(1, 1)); // insert at bottom does not invalidate
+ EXPECT_EQ(3u, counter.get_num_sorted());
+
+ EXPECT_EQ(4u, count_highest(counter, 10));
+ EXPECT_EQ(4u, counter.get_num_sorted());
+
+ EXPECT_EQ(5, counter.insert(5, 5)); // insert at top invalidates sort
+ EXPECT_EQ(0u, counter.get_num_sorted());
+
+ EXPECT_EQ(0u, count_highest(counter, 0));
+ EXPECT_EQ(0u, counter.get_num_sorted());
+ EXPECT_EQ(1u, count_highest(counter, 1));
+ EXPECT_EQ(1u, counter.get_num_sorted());
+ EXPECT_EQ(2u, count_highest(counter, 2));
+ EXPECT_EQ(2u, counter.get_num_sorted());
+ EXPECT_EQ(3u, count_highest(counter, 3));
+ EXPECT_EQ(3u, counter.get_num_sorted());
+ EXPECT_EQ(4u, count_highest(counter, 4));
+ EXPECT_EQ(4u, counter.get_num_sorted());
+ EXPECT_EQ(5u, count_highest(counter, 10));
+ EXPECT_EQ(5u, counter.get_num_sorted());
+
+ // updating an existing counter only invalidates entries <= that counter
+ EXPECT_EQ(2, counter.insert(1)); // invalidates {1,2} and {2,2}
+ EXPECT_EQ(3u, counter.get_num_sorted());
+
+ EXPECT_EQ(5u, count_highest(counter, 10));
+ EXPECT_EQ(5u, counter.get_num_sorted());
+
+ counter.clear(); // invalidates sort
+ EXPECT_EQ(0u, counter.get_num_sorted());
+}
+
diff --git a/src/test/common/test_cdc.cc b/src/test/common/test_cdc.cc
new file mode 100644
index 000000000..620ecf467
--- /dev/null
+++ b/src/test/common/test_cdc.cc
@@ -0,0 +1,163 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <vector>
+#include <cstring>
+#include <random>
+
+#include "include/types.h"
+#include "include/buffer.h"
+
+#include "common/CDC.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+class CDCTest : public ::testing::Test,
+ public ::testing::WithParamInterface<const char*> {
+public:
+ std::unique_ptr<CDC> cdc;
+
+ CDCTest() {
+ auto plugin = GetParam();
+ cdc = CDC::create(plugin, 18);
+ }
+};
+
+TEST_P(CDCTest, insert_front)
+{
+ if (GetParam() == "fixed"s) return;
+ for (int frontlen = 1; frontlen < 163840; frontlen *= 3) {
+ bufferlist bl1, bl2;
+ generate_buffer(4*1024*1024, &bl1);
+ generate_buffer(frontlen, &bl2);
+ bl2.append(bl1);
+ bl2.rebuild();
+
+ vector<pair<uint64_t, uint64_t>> chunks1, chunks2;
+ cdc->calc_chunks(bl1, &chunks1);
+ cdc->calc_chunks(bl2, &chunks2);
+ cout << "1: " << chunks1 << std::endl;
+ cout << "2: " << chunks2 << std::endl;
+
+ ASSERT_GE(chunks2.size(), chunks1.size());
+ int match = 0;
+ for (unsigned i = 0; i < chunks1.size(); ++i) {
+ unsigned j = i + (chunks2.size() - chunks1.size());
+ if (chunks1[i].first + frontlen == chunks2[j].first &&
+ chunks1[i].second == chunks2[j].second) {
+ match++;
+ }
+ }
+ ASSERT_GE(match, chunks1.size() - 1);
+ }
+}
+
+TEST_P(CDCTest, insert_middle)
+{
+ if (GetParam() == "fixed"s) return;
+ for (int frontlen = 1; frontlen < 163840; frontlen *= 3) {
+ bufferlist bl1, bl2;
+ generate_buffer(4*1024*1024, &bl1);
+ bufferlist f, m, e;
+ generate_buffer(frontlen, &m);
+ f.substr_of(bl1, 0, bl1.length() / 2);
+ e.substr_of(bl1, bl1.length() / 2, bl1.length() / 2);
+ bl2 = f;
+ bl2.append(m);
+ bl2.append(e);
+ bl2.rebuild();
+
+ vector<pair<uint64_t, uint64_t>> chunks1, chunks2;
+ cdc->calc_chunks(bl1, &chunks1);
+ cdc->calc_chunks(bl2, &chunks2);
+ cout << "1: " << chunks1 << std::endl;
+ cout << "2: " << chunks2 << std::endl;
+
+ ASSERT_GE(chunks2.size(), chunks1.size());
+ int match = 0;
+ unsigned i;
+ for (i = 0; i < chunks1.size()/2; ++i) {
+ unsigned j = i;
+ if (chunks1[i].first == chunks2[j].first &&
+ chunks1[i].second == chunks2[j].second) {
+ match++;
+ }
+ }
+ for (; i < chunks1.size(); ++i) {
+ unsigned j = i + (chunks2.size() - chunks1.size());
+ if (chunks1[i].first + frontlen == chunks2[j].first &&
+ chunks1[i].second == chunks2[j].second) {
+ match++;
+ }
+ }
+ ASSERT_GE(match, chunks1.size() - 2);
+ }
+}
+
+TEST_P(CDCTest, specific_result)
+{
+ map<string,vector<pair<uint64_t,uint64_t>>> expected = {
+ {"fixed", { {0, 262144}, {262144, 262144}, {524288, 262144}, {786432, 262144}, {1048576, 262144}, {1310720, 262144}, {1572864, 262144}, {1835008, 262144}, {2097152, 262144}, {2359296, 262144}, {2621440, 262144}, {2883584, 262144}, {3145728, 262144}, {3407872, 262144}, {3670016, 262144}, {3932160, 262144} }},
+ {"fastcdc", { {0, 151460}, {151460, 441676}, {593136, 407491}, {1000627, 425767}, {1426394, 602875}, {2029269, 327307}, {2356576, 155515}, {2512091, 159392}, {2671483, 829416}, {3500899, 539667}, {4040566, 153738}}},
+ };
+
+ bufferlist bl;
+ generate_buffer(4*1024*1024, &bl);
+ vector<pair<uint64_t,uint64_t>> chunks;
+ cdc->calc_chunks(bl, &chunks);
+ ASSERT_EQ(chunks, expected[GetParam()]);
+}
+
+
+void do_size_histogram(CDC& cdc, bufferlist& bl,
+ map<int,int> *h)
+{
+ vector<pair<uint64_t, uint64_t>> chunks;
+ cdc.calc_chunks(bl, &chunks);
+ uint64_t total = 0;
+ uint64_t num = 0;
+ for (auto& i : chunks) {
+ //unsigned b = i.second & 0xfffff000;
+ unsigned b = 1 << (cbits(i.second - 1));
+ (*h)[b]++;
+ ++num;
+ total += i.second;
+ }
+ (*h)[0] = total / num;
+}
+
+void print_histogram(map<int,int>& h)
+{
+ cout << "size\tcount" << std::endl;
+ for (auto i : h) {
+ if (i.first) {
+ cout << i.first << "\t" << i.second << std::endl;
+ } else {
+ cout << "avg\t" << i.second << std::endl;
+ }
+ }
+}
+
+TEST_P(CDCTest, chunk_random)
+{
+ map<int,int> h;
+ for (int i = 0; i < 32; ++i) {
+ cout << ".";
+ cout.flush();
+ bufferlist r;
+ generate_buffer(16*1024*1024, &r, i);
+ do_size_histogram(*cdc, r, &h);
+ }
+ cout << std::endl;
+ print_histogram(h);
+}
+
+
+INSTANTIATE_TEST_SUITE_P(
+ CDC,
+ CDCTest,
+ ::testing::Values(
+ "fixed", // note: we skip most tests bc this is not content-based
+ "fastcdc"
+ ));
diff --git a/src/test/common/test_ceph_timer.cc b/src/test/common/test_ceph_timer.cc
new file mode 100644
index 000000000..12391c7ea
--- /dev/null
+++ b/src/test/common/test_ceph_timer.cc
@@ -0,0 +1,163 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <chrono>
+#include <future>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "common/ceph_timer.h"
+
+using namespace std::literals;
+
+namespace {
+template<typename TC>
+void run_some()
+{
+ static constexpr auto MAX_FUTURES = 5;
+ ceph::timer<TC> timer;
+ std::vector<std::future<void>> futures;
+ for (auto i = 0; i < MAX_FUTURES; ++i) {
+ auto t = TC::now() + 2s;
+ std::promise<void> p;
+ futures.push_back(p.get_future());
+ timer.add_event(t, [p = std::move(p)]() mutable {
+ p.set_value();
+ });
+ }
+ for (auto& f : futures)
+ f.get();
+}
+
+template<typename TC>
+void run_orderly()
+{
+ ceph::timer<TC> timer;
+
+ std::future<typename TC::time_point> first;
+ std::future<typename TC::time_point> second;
+
+
+ {
+ std::promise<typename TC::time_point> p;
+ second = p.get_future();
+ timer.add_event(4s, [p = std::move(p)]() mutable {
+ p.set_value(TC::now());
+ });
+ }
+ {
+ std::promise<typename TC::time_point> p;
+ first = p.get_future();
+ timer.add_event(2s, [p = std::move(p)]() mutable {
+ p.set_value(TC::now());
+ });
+ }
+
+ EXPECT_LT(first.get(), second.get());
+}
+
+struct Destructo {
+ bool armed = true;
+ std::promise<void> p;
+
+ Destructo(std::promise<void>&& p) : p(std::move(p)) {}
+ Destructo(const Destructo&) = delete;
+ Destructo& operator =(const Destructo&) = delete;
+ Destructo(Destructo&& rhs) {
+ p = std::move(rhs.p);
+ armed = rhs.armed;
+ rhs.armed = false;
+ }
+ Destructo& operator =(Destructo& rhs) {
+ p = std::move(rhs.p);
+ rhs.armed = false;
+ armed = rhs.armed;
+ rhs.armed = false;
+ return *this;
+ }
+
+ ~Destructo() {
+ if (armed)
+ p.set_value();
+ }
+ void operator ()() const {
+ FAIL();
+ }
+};
+
+template<typename TC>
+void cancel_all()
+{
+ ceph::timer<TC> timer;
+ static constexpr auto MAX_FUTURES = 5;
+ std::vector<std::future<void>> futures;
+ for (auto i = 0; i < MAX_FUTURES; ++i) {
+ std::promise<void> p;
+ futures.push_back(p.get_future());
+ timer.add_event(100s + i*1s, Destructo(std::move(p)));
+ }
+ timer.cancel_all_events();
+ for (auto& f : futures)
+ f.get();
+}
+
+template<typename TC>
+void cancellation()
+{
+ ceph::timer<TC> timer;
+ {
+ std::promise<void> p;
+ auto f = p.get_future();
+ auto e = timer.add_event(100s, Destructo(std::move(p)));
+ EXPECT_TRUE(timer.cancel_event(e));
+ }
+ {
+ std::promise<void> p;
+ auto f = p.get_future();
+ auto e = timer.add_event(1s, [p = std::move(p)]() mutable {
+ p.set_value();
+ });
+ f.get();
+ EXPECT_FALSE(timer.cancel_event(e));
+ }
+}
+}
+
+TEST(RunSome, Steady)
+{
+ run_some<std::chrono::steady_clock>();
+}
+TEST(RunSome, Wall)
+{
+ run_some<std::chrono::system_clock>();
+}
+
+TEST(RunOrderly, Steady)
+{
+ run_orderly<std::chrono::steady_clock>();
+}
+TEST(RunOrderly, Wall)
+{
+ run_orderly<std::chrono::system_clock>();
+}
+
+TEST(CancelAll, Steady)
+{
+ cancel_all<std::chrono::steady_clock>();
+}
+TEST(CancelAll, Wall)
+{
+ cancel_all<std::chrono::system_clock>();
+}
diff --git a/src/test/common/test_config.cc b/src/test/common/test_config.cc
new file mode 100644
index 000000000..a70d567a4
--- /dev/null
+++ b/src/test/common/test_config.cc
@@ -0,0 +1,313 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ *
+ */
+#include "common/config_proxy.h"
+#include "common/errno.h"
+#include "gtest/gtest.h"
+#include "common/hostname.h"
+
+using namespace std;
+
+extern std::string exec(const char* cmd); // defined in test_hostname.cc
+
+class test_config_proxy : public ConfigProxy, public ::testing::Test {
+public:
+
+ test_config_proxy()
+ : ConfigProxy{true}, Test()
+ {}
+
+ void test_expand_meta() {
+ // successfull meta expansion $run_dir and ${run_dir}
+ {
+ ostringstream oss;
+ std::string before = " BEFORE ";
+ std::string after = " AFTER ";
+
+ std::string val(before + "$run_dir${run_dir}" + after);
+ early_expand_meta(val, &oss);
+ EXPECT_EQ(before + "/var/run/ceph/var/run/ceph" + after, val);
+ EXPECT_EQ("", oss.str());
+ }
+ {
+ ostringstream oss;
+ std::string before = " BEFORE ";
+ std::string after = " AFTER ";
+
+ std::string val(before + "$$1$run_dir$2${run_dir}$3$" + after);
+ early_expand_meta(val, &oss);
+ EXPECT_EQ(before + "$$1/var/run/ceph$2/var/run/ceph$3$" + after, val);
+ EXPECT_EQ("", oss.str());
+ }
+ {
+ ostringstream oss;
+ std::string before = " BEFORE ";
+ std::string after = " AFTER ";
+ std::string val(before + "$host${host}" + after);
+ early_expand_meta(val, &oss);
+ std::string hostname = ceph_get_short_hostname();
+ EXPECT_EQ(before + hostname + hostname + after, val);
+ EXPECT_EQ("", oss.str());
+ }
+ // no meta expansion if variables are unknown
+ {
+ ostringstream oss;
+ std::string expected = "expect $foo and ${bar} to not expand";
+ std::string val = expected;
+ early_expand_meta(val, &oss);
+ EXPECT_EQ(expected, val);
+ EXPECT_EQ("", oss.str());
+ }
+ // recursive variable expansion
+ {
+ std::string host = "localhost";
+ EXPECT_EQ(0, set_val("host", host.c_str()));
+
+ std::string mon_host = "$cluster_network";
+ EXPECT_EQ(0, set_val("mon_host", mon_host.c_str()));
+
+ std::string lockdep = "true";
+ EXPECT_EQ(0, set_val("lockdep", lockdep.c_str()));
+
+ std::string cluster_network = "$public_network $public_network $lockdep $host";
+ EXPECT_EQ(0, set_val("cluster_network", cluster_network.c_str()));
+
+ std::string public_network = "NETWORK";
+ EXPECT_EQ(0, set_val("public_network", public_network.c_str()));
+
+ ostringstream oss;
+ std::string val = "$mon_host";
+ early_expand_meta(val, &oss);
+ EXPECT_EQ(public_network + " " +
+ public_network + " " +
+ lockdep + " " +
+ "localhost", val);
+ EXPECT_EQ("", oss.str());
+ }
+ // variable expansion loops are non fatal
+ {
+ std::string mon_host = "$cluster_network";
+ EXPECT_EQ(0, set_val("mon_host", mon_host.c_str()));
+
+ std::string cluster_network = "$public_network";
+ EXPECT_EQ(0, set_val("cluster_network", cluster_network.c_str()));
+
+ std::string public_network = "$mon_host";
+ EXPECT_EQ(0, set_val("public_network", public_network.c_str()));
+
+ ostringstream oss;
+ std::string val = "$mon_host";
+ early_expand_meta(val, &oss);
+ EXPECT_EQ("$mon_host", val);
+ const char *expected_oss =
+ "variable expansion loop at mon_host=$cluster_network\n"
+ "expansion stack:\n"
+ "public_network=$mon_host\n"
+ "cluster_network=$public_network\n"
+ "mon_host=$cluster_network\n";
+ EXPECT_EQ(expected_oss, oss.str());
+ }
+ }
+};
+
+TEST_F(test_config_proxy, expand_meta)
+{
+ test_expand_meta();
+}
+
+TEST(md_config_t, parse_env)
+{
+ {
+ ConfigProxy conf{false};
+ setenv("POD_MEMORY_REQUEST", "1", 1);
+ conf.parse_env(CEPH_ENTITY_TYPE_OSD);
+ }
+ {
+ ConfigProxy conf{false};
+ setenv("POD_MEMORY_REQUEST", "0", 1);
+ conf.parse_env(CEPH_ENTITY_TYPE_OSD);
+ }
+ {
+ ConfigProxy conf{false};
+ setenv("CEPH_KEYRING", "", 1);
+ conf.parse_env(CEPH_ENTITY_TYPE_OSD);
+ }
+}
+
+TEST(md_config_t, set_val)
+{
+ int buf_size = 1024;
+ ConfigProxy conf{false};
+ {
+ char *run_dir = (char*)malloc(buf_size);
+ EXPECT_EQ(0, conf.get_val("run_dir", &run_dir, buf_size));
+ EXPECT_EQ(0, conf.set_val("admin_socket", "$run_dir"));
+ char *admin_socket = (char*)malloc(buf_size);
+ EXPECT_EQ(0, conf.get_val("admin_socket", &admin_socket, buf_size));
+ EXPECT_EQ(std::string(run_dir), std::string(admin_socket));
+ free(run_dir);
+ free(admin_socket);
+ }
+ // set_val should support SI conversion
+ {
+ auto expected = Option::size_t{512 << 20};
+ EXPECT_EQ(0, conf.set_val("mgr_osd_bytes", "512M", nullptr));
+ EXPECT_EQ(expected, conf.get_val<Option::size_t>("mgr_osd_bytes"));
+ EXPECT_EQ(-EINVAL, conf.set_val("mgr_osd_bytes", "512 bits", nullptr));
+ EXPECT_EQ(expected, conf.get_val<Option::size_t>("mgr_osd_bytes"));
+ }
+ // set_val should support 1 days 2 hours 4 minutes
+ {
+ using namespace std::chrono;
+ const string s{"1 days 2 hours 4 minutes"};
+ using days_t = duration<int, std::ratio<3600 * 24>>;
+ auto expected = (duration_cast<seconds>(days_t{1}) +
+ duration_cast<seconds>(hours{2}) +
+ duration_cast<seconds>(minutes{4}));
+ EXPECT_EQ(0, conf.set_val("mgr_tick_period",
+ "1 days 2 hours 4 minutes", nullptr));
+ EXPECT_EQ(expected.count(), conf.get_val<seconds>("mgr_tick_period").count());
+ EXPECT_EQ(-EINVAL, conf.set_val("mgr_tick_period", "21 centuries", nullptr));
+ EXPECT_EQ(expected.count(), conf.get_val<seconds>("mgr_tick_period").count());
+ }
+
+ using namespace std::chrono;
+
+ using days_t = duration<int, std::ratio<3600 * 24>>;
+
+ struct testcase {
+ std::string s;
+ std::chrono::seconds r;
+ };
+ std::vector<testcase> good = {
+ { "23"s, duration_cast<seconds>(seconds{23}) },
+ { " 23 "s, duration_cast<seconds>(seconds{23}) },
+ { " 23s "s, duration_cast<seconds>(seconds{23}) },
+ { " 23 s "s, duration_cast<seconds>(seconds{23}) },
+ { " 23 sec "s, duration_cast<seconds>(seconds{23}) },
+ { "23 second "s, duration_cast<seconds>(seconds{23}) },
+ { "23 seconds"s, duration_cast<seconds>(seconds{23}) },
+ { "2m5s"s, duration_cast<seconds>(seconds{2*60+5}) },
+ { "2 m 5 s "s, duration_cast<seconds>(seconds{2*60+5}) },
+ { "2 m5"s, duration_cast<seconds>(seconds{2*60+5}) },
+ { "2 min5"s, duration_cast<seconds>(seconds{2*60+5}) },
+ { "2 minutes 5"s, duration_cast<seconds>(seconds{2*60+5}) },
+ { "1w"s, duration_cast<seconds>(seconds{3600*24*7}) },
+ { "1wk"s, duration_cast<seconds>(seconds{3600*24*7}) },
+ { "1week"s, duration_cast<seconds>(seconds{3600*24*7}) },
+ { "1weeks"s, duration_cast<seconds>(seconds{3600*24*7}) },
+ { "1month"s, duration_cast<seconds>(seconds{3600*24*30}) },
+ { "1months"s, duration_cast<seconds>(seconds{3600*24*30}) },
+ { "1mo"s, duration_cast<seconds>(seconds{3600*24*30}) },
+ { "1y"s, duration_cast<seconds>(seconds{3600*24*365}) },
+ { "1yr"s, duration_cast<seconds>(seconds{3600*24*365}) },
+ { "1year"s, duration_cast<seconds>(seconds{3600*24*365}) },
+ { "1years"s, duration_cast<seconds>(seconds{3600*24*365}) },
+ { "1d2h3m4s"s,
+ duration_cast<seconds>(days_t{1}) +
+ duration_cast<seconds>(hours{2}) +
+ duration_cast<seconds>(minutes{3}) +
+ duration_cast<seconds>(seconds{4}) },
+ { "1 days 2 hours 4 minutes"s,
+ duration_cast<seconds>(days_t{1}) +
+ duration_cast<seconds>(hours{2}) +
+ duration_cast<seconds>(minutes{4}) },
+ };
+
+ for (auto& i : good) {
+ cout << "good: " << i.s << " -> " << i.r.count() << std::endl;
+ EXPECT_EQ(0, conf.set_val("mgr_tick_period", i.s, nullptr));
+ EXPECT_EQ(i.r.count(), conf.get_val<seconds>("mgr_tick_period").count());
+ }
+
+ std::vector<std::string> bad = {
+ "12x",
+ "_ 12",
+ "1 2",
+ "21 centuries",
+ "1 y m",
+ };
+ for (auto& i : bad) {
+ std::stringstream err;
+ EXPECT_EQ(-EINVAL, conf.set_val("mgr_tick_period", i, &err));
+ cout << "bad: " << i << " -> " << err.str() << std::endl;
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ std::chrono::seconds j = std::chrono::seconds(rand());
+ string s = exact_timespan_str(j);
+ std::chrono::seconds k = parse_timespan(s);
+ cout << "rt: " << j.count() << " -> " << s << " -> " << k.count() << std::endl;
+ EXPECT_EQ(j.count(), k.count());
+ }
+}
+
+TEST(Option, validation)
+{
+ Option opt_int("foo", Option::TYPE_INT, Option::LEVEL_BASIC);
+ opt_int.set_min_max(5, 10);
+
+ std::string msg;
+ EXPECT_EQ(-EINVAL, opt_int.validate(Option::value_t(int64_t(4)), &msg));
+ EXPECT_EQ(-EINVAL, opt_int.validate(Option::value_t(int64_t(11)), &msg));
+ EXPECT_EQ(0, opt_int.validate(Option::value_t(int64_t(7)), &msg));
+
+ Option opt_enum("foo", Option::TYPE_STR, Option::LEVEL_BASIC);
+ opt_enum.set_enum_allowed({"red", "blue"});
+ EXPECT_EQ(0, opt_enum.validate(Option::value_t(std::string("red")), &msg));
+ EXPECT_EQ(0, opt_enum.validate(Option::value_t(std::string("blue")), &msg));
+ EXPECT_EQ(-EINVAL, opt_enum.validate(Option::value_t(std::string("green")), &msg));
+
+ Option opt_validator("foo", Option::TYPE_INT, Option::LEVEL_BASIC);
+ opt_validator.set_validator([](std::string *value, std::string *error_message){
+ if (*value == std::string("one")) {
+ *value = "1";
+ return 0;
+ } else if (*value == std::string("666")) {
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+ });
+
+ std::string input = "666"; // An explicitly forbidden value
+ EXPECT_EQ(-EINVAL, opt_validator.pre_validate(&input, &msg));
+ EXPECT_EQ(input, "666");
+
+ input = "123"; // A permitted value with no special behaviour
+ EXPECT_EQ(0, opt_validator.pre_validate(&input, &msg));
+ EXPECT_EQ(input, "123");
+
+ input = "one"; // A value that has a magic conversion
+ EXPECT_EQ(0, opt_validator.pre_validate(&input, &msg));
+ EXPECT_EQ(input, "1");
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make unittest_config &&
+ * valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_config # --gtest_filter=md_config_t.set_val
+ * "
+ * End:
+ */
diff --git a/src/test/common/test_context.cc b/src/test/common/test_context.cc
new file mode 100644
index 000000000..2c846a9ae
--- /dev/null
+++ b/src/test/common/test_context.cc
@@ -0,0 +1,145 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ *
+ */
+#include "gtest/gtest.h"
+#include "include/types.h"
+#include "include/msgr.h"
+#include "common/ceph_context.h"
+#include "common/config_proxy.h"
+#include "log/Log.h"
+
+using namespace std;
+
+TEST(CephContext, do_command)
+{
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+
+ cct->_conf->cluster = "ceph";
+
+ string key("key");
+ string value("value");
+ cct->_conf.set_val(key.c_str(), value.c_str());
+ cmdmap_t cmdmap;
+ cmdmap["var"] = key;
+
+ {
+ stringstream ss;
+ bufferlist out;
+ std::unique_ptr<Formatter> f{Formatter::create_unique("xml", "xml")};
+ cct->do_command("config get", cmdmap, f.get(), ss, &out);
+ f->flush(out);
+ string s(out.c_str(), out.length());
+ EXPECT_EQ("<config_get><key>" + value + "</key></config_get>", s);
+ }
+
+ {
+ stringstream ss;
+ bufferlist out;
+ cmdmap_t bad_cmdmap; // no 'var' field
+ std::unique_ptr<Formatter> f{Formatter::create_unique("xml", "xml")};
+ int r = cct->do_command("config get", bad_cmdmap, f.get(), ss, &out);
+ if (r >= 0) {
+ f->flush(out);
+ }
+ string s(out.c_str(), out.length());
+ EXPECT_EQ(-EINVAL, r);
+ EXPECT_EQ("", s);
+ EXPECT_EQ("", ss.str()); // no error string :/
+ }
+ {
+ stringstream ss;
+ bufferlist out;
+ cmdmap_t bad_cmdmap;
+ bad_cmdmap["var"] = string("doesnotexist123");
+ std::unique_ptr<Formatter> f{Formatter::create_unique("xml", "xml")};
+ int r = cct->do_command("config help", bad_cmdmap, f.get(), ss, &out);
+ if (r >= 0) {
+ f->flush(out);
+ }
+ string s(out.c_str(), out.length());
+ EXPECT_EQ(-ENOENT, r);
+ EXPECT_EQ("", s);
+ EXPECT_EQ("Setting not found: 'doesnotexist123'", ss.str());
+ }
+
+ {
+ stringstream ss;
+ bufferlist out;
+ std::unique_ptr<Formatter> f{Formatter::create_unique("xml", "xml")};
+ cct->do_command("config diff get", cmdmap, f.get(), ss, &out);
+ f->flush(out);
+ string s(out.c_str(), out.length());
+ EXPECT_EQ("<config_diff_get><diff><key><default></default><override>" + value + "</override><final>value</final></key><rbd_default_features><default>61</default><final>61</final></rbd_default_features><rbd_qos_exclude_ops><default>0</default><final>0</final></rbd_qos_exclude_ops></diff></config_diff_get>", s);
+ }
+ cct->put();
+}
+
+TEST(CephContext, experimental_features)
+{
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+
+ cct->_conf->cluster = "ceph";
+
+ ASSERT_FALSE(cct->check_experimental_feature_enabled("foo"));
+ ASSERT_FALSE(cct->check_experimental_feature_enabled("bar"));
+ ASSERT_FALSE(cct->check_experimental_feature_enabled("baz"));
+
+ cct->_conf.set_val("enable_experimental_unrecoverable_data_corrupting_features",
+ "foo,bar");
+ cct->_conf.apply_changes(&cout);
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("foo"));
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("bar"));
+ ASSERT_FALSE(cct->check_experimental_feature_enabled("baz"));
+
+ cct->_conf.set_val("enable_experimental_unrecoverable_data_corrupting_features",
+ "foo bar");
+ cct->_conf.apply_changes(&cout);
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("foo"));
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("bar"));
+ ASSERT_FALSE(cct->check_experimental_feature_enabled("baz"));
+
+ cct->_conf.set_val("enable_experimental_unrecoverable_data_corrupting_features",
+ "baz foo");
+ cct->_conf.apply_changes(&cout);
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("foo"));
+ ASSERT_FALSE(cct->check_experimental_feature_enabled("bar"));
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("baz"));
+
+ cct->_conf.set_val("enable_experimental_unrecoverable_data_corrupting_features",
+ "*");
+ cct->_conf.apply_changes(&cout);
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("foo"));
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("bar"));
+ ASSERT_TRUE(cct->check_experimental_feature_enabled("baz"));
+
+ cct->_log->flush();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make unittest_context &&
+ * valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_context # --gtest_filter=CephContext.*
+ * "
+ * End:
+ */
diff --git a/src/test/common/test_convenience.cc b/src/test/common/test_convenience.cc
new file mode 100644
index 000000000..4f4ba386e
--- /dev/null
+++ b/src/test/common/test_convenience.cc
@@ -0,0 +1,69 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Casey Bodley <cbodley@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/convenience.h" // include first: tests that header is standalone
+
+#include <string>
+#include <boost/optional.hpp>
+#include <gtest/gtest.h>
+
+// A just god would not allow the C++ standard to make taking the
+// address of member functions in the standard library undefined behavior.
+static std::string::size_type l(const std::string& s) {
+ return s.size();
+}
+
+TEST(Convenience, MaybeDo)
+{
+ boost::optional<std::string> s("qwerty");
+ boost::optional<std::string> t;
+ auto r = ceph::maybe_do(s, l);
+ EXPECT_TRUE(r);
+ EXPECT_EQ(*r, s->size());
+
+ EXPECT_FALSE(ceph::maybe_do(t, l));
+}
+
+TEST(Convenience, MaybeDoOr)
+{
+ const boost::optional<std::string> s("qwerty");
+ const boost::optional<std::string> t;
+ auto r = ceph::maybe_do_or(s, l, 0);
+ EXPECT_EQ(r, s->size());
+
+ EXPECT_EQ(ceph::maybe_do_or(t, l, 0u), 0u);
+}
+
+TEST(Convenience, StdMaybeDo)
+{
+ std::optional<std::string> s("qwerty");
+ std::optional<std::string> t;
+ auto r = ceph::maybe_do(s, l);
+ EXPECT_TRUE(r);
+ EXPECT_EQ(*r, s->size());
+
+ EXPECT_FALSE(ceph::maybe_do(t, l));
+}
+
+TEST(Convenience, StdMaybeDoOr)
+{
+ const std::optional<std::string> s("qwerty");
+ const std::optional<std::string> t;
+ auto r = ceph::maybe_do_or(s, l, 0);
+ EXPECT_EQ(r, s->size());
+
+ EXPECT_EQ(ceph::maybe_do_or(t, l, 0u), 0u);
+}
diff --git a/src/test/common/test_counter.cc b/src/test/common/test_counter.cc
new file mode 100644
index 000000000..f9a7d6e6c
--- /dev/null
+++ b/src/test/common/test_counter.cc
@@ -0,0 +1,40 @@
+#include "common/DecayCounter.h"
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <cmath>
+
+TEST(DecayCounter, steady)
+{
+ static const double duration = 2.0;
+ static const double max = 2048.0;
+ static const double rate = 3.5;
+
+ DecayCounter d{DecayRate{rate}};
+ d.hit(max);
+ const auto start = DecayCounter::clock::now();
+ double total = 0.0;
+ while (1) {
+ const auto now = DecayCounter::clock::now();
+ auto el = std::chrono::duration<double>(now-start);
+ if (el.count() > duration) {
+ break;
+ }
+
+ double v = d.get();
+ double diff = max-v;
+ if (diff > 0.0) {
+ d.hit(diff);
+ total += diff;
+ }
+ }
+
+ /* Decay function: dN/dt = -λM where λ = ln(0.5)/rate
+ * (where M is the maximum value of the counter, not varying with time.)
+ * Integrating over t: N = -λMt (+c)
+ */
+ double expected = -1*std::log(0.5)/rate*max*duration;
+ std::cerr << "t " << total << " e " << expected << std::endl;
+ ASSERT_LT(std::abs(total-expected)/expected, 0.05);
+}
diff --git a/src/test/common/test_crc32c.cc b/src/test/common/test_crc32c.cc
new file mode 100644
index 000000000..ff5d0019d
--- /dev/null
+++ b/src/test/common/test_crc32c.cc
@@ -0,0 +1,365 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include <string.h>
+
+#include "include/types.h"
+#include "include/crc32c.h"
+#include "include/utime.h"
+#include "common/Clock.h"
+
+#include "gtest/gtest.h"
+
+#include "common/sctp_crc32.h"
+#include "common/crc32c_intel_baseline.h"
+#include "common/crc32c_aarch64.h"
+
+TEST(Crc32c, Small) {
+ const char *a = "foo bar baz";
+ const char *b = "whiz bang boom";
+ ASSERT_EQ(4119623852u, ceph_crc32c(0, (unsigned char *)a, strlen(a)));
+ ASSERT_EQ(881700046u, ceph_crc32c(1234, (unsigned char *)a, strlen(a)));
+ ASSERT_EQ(2360230088u, ceph_crc32c(0, (unsigned char *)b, strlen(b)));
+ ASSERT_EQ(3743019208u, ceph_crc32c(5678, (unsigned char *)b, strlen(b)));
+}
+
+TEST(Crc32c, PartialWord) {
+ const char *a = (const char *)malloc(5);
+ const char *b = (const char *)malloc(35);
+ memset((void *)a, 1, 5);
+ memset((void *)b, 1, 35);
+ ASSERT_EQ(2715569182u, ceph_crc32c(0, (unsigned char *)a, 5));
+ ASSERT_EQ(440531800u, ceph_crc32c(0, (unsigned char *)b, 35));
+ free((void*)a);
+ free((void*)b);
+}
+
+TEST(Crc32c, Big) {
+ int len = 4096000;
+ char *a = (char *)malloc(len);
+ memset(a, 1, len);
+ ASSERT_EQ(31583199u, ceph_crc32c(0, (unsigned char *)a, len));
+ ASSERT_EQ(1400919119u, ceph_crc32c(1234, (unsigned char *)a, len));
+ free(a);
+}
+
+TEST(Crc32c, Performance) {
+ int len = 1000 * 1024 * 1024;
+ char *a = (char *)malloc(len);
+ std::cout << "populating large buffer" << std::endl;
+ for (int i=0; i<len; i++)
+ a[i] = i & 0xff;
+ std::cout << "calculating crc" << std::endl;
+
+ {
+ utime_t start = ceph_clock_now();
+ unsigned val = ceph_crc32c(0, (unsigned char *)a, len);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "best choice = " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(261108528u, val);
+ }
+ {
+ utime_t start = ceph_clock_now();
+ unsigned val = ceph_crc32c(0xffffffff, (unsigned char *)a, len);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "best choice 0xffffffff = " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(3895876243u, val);
+ }
+ {
+ utime_t start = ceph_clock_now();
+ unsigned val = ceph_crc32c_sctp(0, (unsigned char *)a, len);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "sctp = " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(261108528u, val);
+ }
+ {
+ utime_t start = ceph_clock_now();
+ unsigned val = ceph_crc32c_intel_baseline(0, (unsigned char *)a, len);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "intel baseline = " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(261108528u, val);
+ }
+#if defined(__arm__) || defined(__aarch64__)
+ if (ceph_arch_aarch64_crc32) // Skip if CRC32C instructions are not defined.
+ {
+ utime_t start = ceph_clock_now();
+ unsigned val = ceph_crc32c_aarch64(0, (unsigned char *)a, len);
+ utime_t end = ceph_clock_now();
+ float rate = (float)len / (float)(1024*1024) / (float)(end - start);
+ std::cout << "aarch64 = " << rate << " MB/sec" << std::endl;
+ ASSERT_EQ(261108528u, val);
+ }
+#endif
+ free(a);
+}
+
+
+static uint32_t crc_check_table[] = {
+0xcfc75c75, 0x7aa1b1a7, 0xd761a4fe, 0xd699eeb6, 0x2a136fff, 0x9782190d, 0xb5017bb0, 0xcffb76a9,
+0xc79d0831, 0x4a5da87e, 0x76fb520c, 0x9e19163d, 0xe8eacd22, 0xefd4319e, 0x1eaa804b, 0x7ff41ccb,
+0x94141dab, 0xb4c2588f, 0x484bf16f, 0x77725048, 0xf27d43ee, 0x3604f655, 0x20bb9b79, 0xd6ee30ba,
+0xf402f02d, 0x59992eec, 0x159c0449, 0xe2d72e60, 0xc519c744, 0xf56f7995, 0x7e40be36, 0x695ccedc,
+0xc95c4ae3, 0xb0d2d6bc, 0x85872e14, 0xea2c01b0, 0xe9b75f1a, 0xebb23ae3, 0x39faee13, 0x313cb413,
+0xe683eb7d, 0xd22e2ae1, 0xf49731dd, 0x897a8e60, 0x923b510e, 0xe0e0f3b, 0x357dd0f, 0x63b7aa7d,
+0x6f5c2a40, 0x46b09a37, 0x80324751, 0x380fd024, 0x78b122c6, 0xb29d1dde, 0x22f19ddc, 0x9d6ee6d6,
+0xfb4e7e1c, 0xb9780044, 0x85feef90, 0x8e4fae11, 0x1a71394a, 0xbe21c888, 0xde2f6f47, 0x93c365f0,
+0xfd1d3814, 0x6e0a23df, 0xc6739c17, 0x2d48520d, 0x3357e475, 0x5d57058a, 0x22c4b9f7, 0x5a498b58,
+0x7bed8ddb, 0xcf1eb035, 0x2094f389, 0xb6a7c977, 0x289d29e2, 0x498d5b7, 0x8db77420, 0x85300608,
+0x5d1c04c4, 0x5acfee62, 0x99ad4694, 0x799f9833, 0x50e76ce1, 0x72dc498, 0x70a393be, 0x905a364d,
+0x1af66b95, 0x5b3eed9e, 0xa3e4da14, 0xc720fece, 0x555200df, 0x169fd3e0, 0x531c18c0, 0x6f9b6092,
+0x6d16638b, 0x5a8c8b6a, 0x818ebab2, 0xd75b10bb, 0xcaa01bfa, 0x67377804, 0xf8a085ae, 0xfc7d88b8,
+0x5e2debc1, 0x9759cb1f, 0x24c39b63, 0x210afbba, 0x22f7c6f7, 0xa8f8dc11, 0xf1d4550c, 0x1d2b1e47,
+0x59a44605, 0x25402e97, 0x18401ea, 0xb1884203, 0xd6ef715, 0x1797b686, 0x9e7f5aa7, 0x30795e88,
+0xb280b636, 0x77258b7d, 0x5f8dbff3, 0xbb57ea03, 0xa2c35cce, 0x1acce538, 0xa50be97a, 0x417f4b57,
+0x6d94792f, 0x4bb6fb34, 0x3787440c, 0x9a77b0b9, 0x67ece3d0, 0x5a8450fe, 0x8e66f55b, 0x3cefce93,
+0xf7ca60ab, 0xce7cd3b7, 0x97976493, 0xa05632f8, 0x77ac4546, 0xed24c705, 0x92a2f20, 0xc0b1cc9,
+0x831ae4e1, 0x5b3f28b1, 0xee6fca02, 0x74acc743, 0xaf40043f, 0x5f21e837, 0x9e168fc0, 0x64e28de,
+0x88ae891d, 0xac2e4ff5, 0xaeaf9c27, 0x158a2d3, 0x5226fb01, 0x9bf56ae1, 0xe4a2dd8d, 0x2599d6de,
+0xe798b5ee, 0x39efe57a, 0xbb9965c7, 0x4516fde0, 0xa41831f5, 0xd7cd0797, 0xd07b7d5c, 0xb330d048,
+0x3a47e35d, 0x87dd39e5, 0xa806fb31, 0xad228dd, 0xcc390816, 0x9237a4de, 0x8dfe1c20, 0x304f6bc,
+0x3ad98572, 0xec13f349, 0x4e5278d7, 0x784c4bf4, 0x7b93cb23, 0xa18c87ae, 0x84ff79dd, 0x8e95061d,
+0xd972f4d4, 0x4ad50380, 0x23cbc187, 0x7fa7f22c, 0x6062c18e, 0x42381901, 0x10cf51d9, 0x674e22a4,
+0x28a63445, 0x6fc1b591, 0xa4dc117a, 0x744a00d0, 0x8a5470ea, 0x9539c6a7, 0xc961a584, 0x22f81498,
+0xae299e51, 0x5653fcd3, 0x7bfa474f, 0x7f502c42, 0xfb41c744, 0xd478fb95, 0x7b676978, 0xb22f5610,
+0xbcbe730c, 0x70ff5773, 0xde990b63, 0xebcbf9d5, 0x2d029133, 0xf39513e1, 0x56229640, 0x660529e5,
+0x3b90bdf8, 0xc9822978, 0x4e3daab1, 0x2e43ce72, 0x572bb6ff, 0xdc4b17bd, 0x6c290d46, 0x7d9644ca,
+0x7652fd89, 0x66d72059, 0x521e93d4, 0xd626ff95, 0xdc4eb57e, 0xb0b3307c, 0x409adbed, 0x49ae2d28,
+0x8edd249a, 0x8e4fb6ec, 0x5a191fbf, 0xe1751948, 0xb4ae5d00, 0xabeb1bdd, 0xbe204b60, 0xbc97aad4,
+0xb8cb5915, 0x54f33261, 0xc5d83b28, 0x99d0d099, 0xfb06f8b2, 0x57305f66, 0xf9fde17b, 0x192f143c,
+0xcc3c58fd, 0x36e2e420, 0x17118208, 0xcac7e42a, 0xb45ad63d, 0x8ad5e475, 0xb7a3bc1e, 0xe03e64ad,
+0x2c197d77, 0x1a0ff1fe, 0xbcd443fb, 0x7589393a, 0xd66b1f67, 0xdddf0a66, 0x4750b7c7, 0xc62a79db,
+0xcf02a0d3, 0xb4012205, 0x9733d16c, 0x9a29cff8, 0xdd3d6427, 0x15c0273a, 0x97b289b, 0x358ff573,
+0x73a9ceb7, 0xc3788b1a, 0xda7a5155, 0x2990a31, 0x9fa4705, 0x5eb4e2e2, 0x98465bb2, 0x74a17883,
+0xe87df542, 0xe20f22f1, 0x48ffd67e, 0xc94fab5f, 0x9eb431d2, 0xffd673cb, 0xc374dc18, 0xa542fbf7,
+0xb8fea538, 0x43f5431f, 0xcbe3fb7d, 0x2734e0e4, 0x5cb05a8, 0xd00fcf47, 0x248dbbae, 0x47d4de6c,
+0xecc97151, 0xca8c379b, 0x49049fd, 0xeb2acd18, 0xab178ac, 0xc98ab95d, 0xb9e0be20, 0x36664a13,
+0x95d81459, 0xb54973a9, 0x27f9579c, 0xa24fb6df, 0x3f6f8cea, 0xe11efdd7, 0x68166281, 0x586e0a6,
+0x5fad7b57, 0xd58f50ad, 0x6e0d3be8, 0x27a00831, 0x543b3761, 0x96c862fb, 0xa823ed4f, 0xf6043f37,
+0x980703eb, 0xf5e69514, 0x42a2082, 0x495732a2, 0x793eea23, 0x6a6a17fb, 0x77d75dc5, 0xb3320ec4,
+0x10d4d01e, 0xa17508a6, 0x6d578355, 0xd136c445, 0xafa6acc6, 0x2307831d, 0x5bf345fd, 0xb9a04582,
+0x2627a686, 0xf6f4ce3b, 0xd0ac868f, 0x78d6bdb3, 0xfe42945a, 0x8b06cbf3, 0x2b169628, 0xf072b8b7,
+0x8652a0ca, 0x3f52fc42, 0xa0415b9a, 0x16e99341, 0x7394e9c7, 0xac92956c, 0x7bff7137, 0xb0e8ea5c,
+0x42d8c22, 0x4318a18, 0x42097180, 0x57d17dba, 0xb1f7a567, 0x55186d60, 0xf527e0ca, 0xd58b0b48,
+0x31d9155b, 0xd5fd0441, 0x6024d751, 0xe14d03c3, 0xba032e1c, 0xd6d89ae7, 0x54f1967a, 0xe401c200,
+0x8ee973ff, 0x3d24277e, 0xab394cbf, 0xe3b39762, 0x87f43766, 0xe4c2bdff, 0x1234c0d7, 0x8ef3e1bd,
+0xeeb00f61, 0x15d17d4b, 0x7d40ac8d, 0xada8606f, 0x7ba5e3a1, 0xcf487cf9, 0x98dda708, 0x6d7c9bea,
+0xaecb321c, 0x9f7801b2, 0x53340341, 0x7ae27355, 0xbf859829, 0xa36a00b, 0x99339435, 0x8342d1e,
+0x4ab4d7ea, 0x862d01cd, 0x7f94fbee, 0xe329a5a3, 0x2cb7ba81, 0x50bae57a, 0x5bbd65cf, 0xf06f60e4,
+0x569ad444, 0xfa0c16c, 0xb8c2b472, 0x3ea64ea1, 0xc6dc4c18, 0x5d6d654a, 0x5369a931, 0x2163bf7f,
+0xe45bd590, 0xcc826d18, 0xb4ce22f6, 0x200f7232, 0x5f2f869c, 0xffd5cc17, 0x1a578942, 0x930da3ea,
+0x216377f, 0x9f07a04b, 0x1f2a777c, 0x13c95089, 0x8a64d032, 0x1eecb206, 0xc537dc4, 0x319f9ac8,
+0xe2131194, 0x25d2f716, 0xa27f471a, 0xf6434ce2, 0xd51a10b9, 0x4e28a61, 0x647c888a, 0xb383d2ff,
+0x93aa0d0d, 0x670d1317, 0x607f36e2, 0x73e01833, 0x2bd372b0, 0x86404ad2, 0x253d5cc4, 0x1348811c,
+0x8756f2d5, 0xe1e55a59, 0x5247e2d1, 0x798ab6b, 0x181bbc57, 0xb9ea36e0, 0x66081c68, 0x9bf0bad7,
+0x892b1a6, 0x8a6a9aed, 0xda955d0d, 0x170e5128, 0x81733d84, 0x6d9f6b10, 0xd60046fd, 0x7e401823,
+0xf9904ce6, 0xaa765665, 0x2fd5c4ee, 0xbb9c1580, 0x391dac53, 0xbffe4270, 0x866c30b1, 0xd629f22,
+0x1ee5bfee, 0x5af91c96, 0x96b613bf, 0xa65204c9, 0x9b8cb68c, 0xd08b37c1, 0xf1863f8f, 0x1e4c844a,
+0x876abd30, 0x70c07eff, 0x63d8e875, 0x74351f92, 0xffe7712d, 0x58c0171d, 0x7b826b99, 0xc09afc78,
+0xd81d3065, 0xccced8b1, 0xe258b1c9, 0x5659d6b, 0x1959c406, 0x53bd05e6, 0xa32f784b, 0x33351e4b,
+0xb6b9d769, 0x59e5802c, 0x118c7ff7, 0x46326e0b, 0xa7376fbe, 0x7218aed1, 0x28c8f707, 0x44610a2f,
+0xf8eafea1, 0xfe36fdae, 0xb4b546f1, 0x2e27ce89, 0xc1fde8a0, 0x99f2f157, 0xfde687a1, 0x40a75f50,
+0x6c653330, 0xf3e38821, 0xf4663e43, 0x2f7e801e, 0xfca360af, 0x53cd3c59, 0xd20da292, 0x812a0241 };
+
+TEST(Crc32c, Range) {
+ int len = sizeof(crc_check_table) / sizeof(crc_check_table[0]);
+ unsigned char *b = (unsigned char *)malloc(len);
+ memset(b, 1, len);
+ uint32_t crc = 0;
+ uint32_t *check = crc_check_table;
+ for (int i = 0 ; i < len; i++, check++) {
+ crc = ceph_crc32c(crc, b+i, len-i);
+ ASSERT_EQ(crc, *check);
+ }
+ free(b);
+}
+
+static uint32_t crc_zero_check_table[] = {
+0xbd6f81f8, 0x6213374d, 0x72952aeb, 0x8ecb5e52, 0xa04914b4, 0xaf3aaea9, 0xb88d42d6, 0x81797724,
+0xc0022634, 0x4dbf46a4, 0xc7813aa, 0x172150e0, 0x13d8d958, 0x339fd933, 0xd9e725f4, 0x20b65b14,
+0x349c971c, 0x7f812818, 0x5228e357, 0x811f231f, 0xe4bdaeee, 0xcdd22442, 0x26ae3c58, 0xf9628c5e,
+0x8118e80b, 0xca0ea635, 0xc5028f6d, 0xbd2270, 0x4d9171a3, 0xe810af42, 0x904c7218, 0xdc62c735,
+0x3c8b3748, 0x7cae4eef, 0xed170242, 0xdc0a6a28, 0x4afb0591, 0x4643748a, 0xad28d5b, 0xeb2d60d3,
+0x479d21a9, 0x2a0916c1, 0x144cd9fb, 0x2498ba7a, 0x196489f, 0x330bb594, 0x5abe491d, 0x195658fe,
+0xc6ef898f, 0x94b251a1, 0x4f968332, 0xfbf5f29d, 0x7b4828ce, 0x3af20a6f, 0x653a721f, 0x6d92d018,
+0xf43ca065, 0xf55da16e, 0x94af47c6, 0xf08abdc, 0x11344631, 0xb249e575, 0x1f9f992b, 0xfdb6f490,
+0xbd40d84b, 0x945c69e1, 0x2a94e2e3, 0xe5aa9b91, 0x89cebb57, 0x175a3097, 0x502b7d34, 0x174f2c92,
+0x2a8f01c0, 0x645a2db8, 0x9e9a4a8, 0x13adac02, 0x2759a24b, 0x8bfcb972, 0xfa1edbfe, 0x5a88365e,
+0x5c107fd9, 0x91ac73a8, 0xbd40e99e, 0x513011ca, 0x97bd2841, 0x336c1c4e, 0x4e88563e, 0x6948813e,
+0x96e1cbee, 0x64b2faa5, 0x9671e44, 0x7d492fcb, 0x3539d74a, 0xcbe26ad7, 0x6106e673, 0x162115d,
+0x8534e6a6, 0xd28a1ea0, 0xf73beb20, 0x481bdbae, 0xcd12e442, 0x8ab52843, 0x171d72c4, 0xd97cb216,
+0x60fa0ecf, 0x74336ebb, 0x4d67fd86, 0x9393e96a, 0x63670234, 0x3f2a31da, 0x4036c11f, 0x55cc2ceb,
+0xf75b27dc, 0xcabdca83, 0x80699d1a, 0x228c13a1, 0x5ea7f8a9, 0xc7631f40, 0x710b867a, 0xaa6e67b9,
+0x27444987, 0xd693cd2a, 0xc4e21e0c, 0xd340e1cb, 0x2a2a346f, 0xac55e843, 0xfcd2750c, 0x4529a016,
+0x7ac5802, 0xa2eb291f, 0x4a0fb9ea, 0x6a58a9a0, 0x51f56797, 0xda595134, 0x267aba96, 0x8ba80ee,
+0x4474659e, 0x2b7bacb, 0xba524d37, 0xb60981bb, 0x5fd43811, 0xca41594a, 0x98ace58, 0x3fc5b984,
+0x6a290b91, 0x6576108a, 0x8c33c85e, 0x52622407, 0x99cf8723, 0x68198dc8, 0x18b7341d, 0x540fc0f9,
+0xf4a7b6f6, 0xfade9dfa, 0x725471ca, 0x5c160723, 0x5f33b243, 0xecec5d09, 0x6f520abb, 0x139c7bca,
+0x58349acb, 0x1fccef32, 0x1d01aa0f, 0x3f477a65, 0xebf55472, 0xde9ae082, 0x76d3119e, 0x937e2708,
+0xba565506, 0xbe820951, 0xc1f336fa, 0xfc41afb6, 0x4ef12d88, 0xd6f6d4f, 0xb33fb3fe, 0x9c6d1ae,
+0x24ae1c29, 0xf9ae57f7, 0x51d1e4c9, 0x86dc73fc, 0x54b7bf38, 0x688a141c, 0x91d4ea7a, 0xd57a0fd0,
+0x5cdcd16f, 0xc59c135a, 0x5bb003b5, 0x730b52f3, 0xc1dc5b1e, 0xf083f53, 0x8159e7c8, 0xf396d2e3,
+0x1c7f18ec, 0x5bedc75e, 0x2f11fbfd, 0xb4437094, 0x77c55e3, 0x1d8636e1, 0x159bf2f, 0x6cbabf5b,
+0xf4d005bc, 0x39f0bc55, 0x3d525f54, 0x8422e29d, 0xfb8a413d, 0x66e78593, 0xa0e14663, 0x880b8fa1,
+0x24b53713, 0x12105ff3, 0xa94dd90f, 0x3ff981bc, 0xaf2366af, 0x8e98710, 0x48eb45c6, 0xbc3aee53,
+0x6933d852, 0xe236cfd3, 0x3e6c50af, 0xe309e3fd, 0x452eac88, 0x725bf633, 0xbe89339a, 0x4b54eff7,
+0xa57e392f, 0x6ee15bef, 0x67630f96, 0x31656c71, 0x77fc97f0, 0x1d29682f, 0xa4b0fc5d, 0xb3fd0ee1,
+0x9d10aa57, 0xf104e21, 0x478b5f75, 0xaf1ca64b, 0x13e8a297, 0x21caa105, 0xb3cb8e9d, 0xd4536cb,
+0x425bdfce, 0x90462d05, 0x8cace1cf, 0xc0ab7293, 0xbcf288cb, 0x5edcdc11, 0x4ec8b5e0, 0x42738654,
+0x4ba49663, 0x2b264337, 0x41d1a5ce, 0xaa8acb92, 0xe79714aa, 0x86695e7c, 0x1330c69a, 0xe0c6485f,
+0xb038b81a, 0x6f823a85, 0x4eeff0e4, 0x7355d58f, 0x7cc87e83, 0xe23e4619, 0x7093faa0, 0x7328cb2f,
+0x7856db5e, 0xbc38d892, 0x1e4307c8, 0x347997e1, 0xb26958, 0x997ddf1e, 0x58dc72e3, 0x4b6e9a77,
+0x49eb9924, 0x36d555db, 0x59456efd, 0x904bd6d2, 0xd932837d, 0xf96a24ec, 0x525aa449, 0x5fd05bc7,
+0x84778138, 0xd869bfe1, 0xe6bbd546, 0x2f796af4, 0xbaab980f, 0x7f18a176, 0x3a8e00d9, 0xb589ea81,
+0x77920ee3, 0xc6730dbc, 0x8a5df534, 0xb7df9a12, 0xdc93009c, 0x215b885, 0x309104b, 0xf47e380b,
+0x23f6cdef, 0xe112a923, 0x83686f38, 0xde2c7871, 0x9f728ec7, 0xeaae7af6, 0x6d7b7b0a, 0xaf0cde04,
+0xfcb51a1f, 0xf0cd53cf, 0x7aa5556a, 0xa64ccf7e, 0x854c2084, 0xc493ddd4, 0x92684099, 0x913beb92,
+0xe4067ea8, 0x9557605a, 0x934346d6, 0x23a3a7c7, 0x588b2805, 0xe1e755ae, 0xe4c05e84, 0x8e09d0f3,
+0x1343a510, 0x6175c2c3, 0x39bb7947, 0x4a1b9b6b, 0xf0e373da, 0xe7b9a201, 0x24b7a392, 0x91a27584,
+0x9ac3a10f, 0x91fc9314, 0xc495d878, 0x3fcbc776, 0x7f81d6da, 0x973edb2f, 0xa9d731c6, 0x2dc022a8,
+0xa066c881, 0x7e082dff, 0xa1ff394d, 0x1cb0c2bb, 0xef87a116, 0x5179810b, 0xa1594c92, 0xe291e155,
+0x3578c98f, 0xb801f82c, 0xa1778ad9, 0xbdd48b76, 0x74f1ce54, 0x46b8de63, 0x3861112c, 0x46a8920f,
+0x3e1075e7, 0x220a49dd, 0x3e51d6d2, 0xbf1f22cd, 0x5d1490c5, 0x7f1e05f5, 0xa0c1691d, 0x9108debf,
+0xe69899b, 0xe771d8b6, 0x878c92c1, 0x973e37c0, 0x833c4c25, 0xcffe7b03, 0x92e0921e, 0xccee9836,
+0xa9739832, 0xc774f2f2, 0xf34f9467, 0x608cef83, 0x97a584d2, 0xf5218c9, 0x73eb9524, 0xb3fb4870,
+0x53296e3d, 0x8836f46f, 0x9d6a40b0, 0x789b5e91, 0x62a915ba, 0x32c02d74, 0xc93de2f3, 0xefa67fc7,
+0x169ee4f1, 0x72bbbe9e, 0x49357cf2, 0x219207bf, 0x12516225, 0x182df160, 0x230c9a3f, 0x137a8497,
+0xa429ad30, 0x4aa66f88, 0x40319931, 0xfa241c42, 0x1e5189ec, 0xca693ada, 0xe7b923f4, 0xff546a06,
+0xf01103c2, 0x99875a32, 0x4bbf55a9, 0x48abdf3e, 0x85eb3dec, 0x2d009057, 0x14c2a682, 0xfabe68af,
+0x96a31fa6, 0xf52f4686, 0x73f72b61, 0x92f39e13, 0x66794863, 0x7ca4c2aa, 0x37a2fe39, 0x33be288a,
+0x1ff9a59c, 0xd65e667, 0x5d7c9332, 0x8a6a2d8b, 0x37ec2d3b, 0x9f935ab9, 0x67fcd589, 0x48a09508,
+0xc446e984, 0x58f69202, 0x968dfbbb, 0xc93d7626, 0x82344e, 0xf1d930a4, 0xcc3acdde, 0x20cf92bf,
+0x94b7616d, 0xb0e45050, 0xdc36c072, 0x74cba0, 0x6478300a, 0x27803b97, 0xb7b2ebd0, 0xb3a691e,
+0x35c2f261, 0x3fcff45a, 0x3e4b7b93, 0x86b680bd, 0x720333ce, 0x67f933ca, 0xb10256de, 0xe939bb3f,
+0xb540a02f, 0x39a8b8e4, 0xb6a63aa5, 0x5e1d56ee, 0xa415a16, 0xcb5753d, 0x17fabd19, 0x90eac10d,
+0x2308857d, 0xb8f6224c, 0x71790390, 0x18749d48, 0xed778f1b, 0x69f0e17c, 0xbd622f4, 0x52c3a79e,
+0x9697bf51, 0xa768755c, 0x9fe860ea, 0xa852b0ac, 0x9549ec64, 0x8669c603, 0x120e289c, 0x3f0520f5,
+0x9b15884, 0x2d06fa7f, 0x767b12f6, 0xcb232dd6, 0x4e2b4590, 0x97821835, 0x4506a582, 0xd974dbaa,
+0x379bd22f, 0xb9d65a2f, 0x8fad14d9, 0x72a55b5f, 0x34d56c6e, 0xc0badd55, 0xc20ee31b, 0xeb567f69,
+0xdadac1c, 0xb6dcc8f5, 0xc6d89117, 0x16c4999d, 0xc9b0da2a, 0xfcd6e9b3, 0x72d299ae, 0x4c2b345b,
+0x5d2c06cb, 0x9b9a3ce2, 0x8e84866, 0x876d1806, 0xbaeb6183, 0xe2a89d5d, 0x4604d2fe, 0x9909c5e0,
+0xf2fb7bec, 0x7e04dcd0, 0xe5b24865, 0xda96b760, 0x74a4d01, 0xb0f35bea, 0x9a2edb2, 0x5327a0d3 };
+
+
+TEST(Crc32c, RangeZero) {
+ int len = sizeof(crc_zero_check_table) / sizeof(crc_zero_check_table[0]);
+ unsigned char *b = (unsigned char *)malloc(len);
+ memset(b, 0, len);
+ uint32_t crc = 1; /* when checking zero buffer we want to start with a non zero crc, otherwise
+ all the results are going to be zero */
+ uint32_t *check = crc_zero_check_table;
+ for (int i = 0 ; i < len; i++, check++) {
+ crc = ceph_crc32c(crc, b+i, len-i);
+ ASSERT_EQ(crc, *check);
+ }
+ free(b);
+}
+
+TEST(Crc32c, RangeNull) {
+ int len = sizeof(crc_zero_check_table) / sizeof(crc_zero_check_table[0]);
+ uint32_t crc = 1; /* when checking zero buffer we want to start with a non zero crc, otherwise
+ all the results are going to be zero */
+ uint32_t *check = crc_zero_check_table;
+
+ for (int i = 0 ; i < len; i++, check++) {
+ crc = ceph_crc32c(crc, NULL, len-i);
+ ASSERT_EQ(crc, *check);
+ }
+}
+
+double estimate_clock_resolution()
+{
+ volatile char* p = (volatile char*)malloc(1024);
+ utime_t start;
+ utime_t end;
+ std::set<double> S;
+ for(int j=10; j<200; j+=1) {
+ start = ceph_clock_now();
+ for (int i=0; i<j; i++)
+ p[i]=1;
+ end = ceph_clock_now();
+ S.insert((double)(end - start));
+ }
+ auto head = S.begin();
+ auto tail = S.end();
+ for (size_t i=0; i<S.size()/4; i++) {
+ ++head;
+ --tail;
+ }
+ double v = *(head++);
+ double range=0;
+ while (head != tail) {
+ range = std::max(range, *head - v);
+ v = *head;
+ head++;
+ }
+ free((void*)p);
+ return range;
+}
+
+TEST(Crc32c, zeros_performance_compare) {
+ double resolution = estimate_clock_resolution();
+ utime_t start;
+ utime_t pre_start;
+ utime_t end;
+ double time_adjusted;
+ using namespace std::chrono;
+ high_resolution_clock::now();
+ for (size_t scale=1; scale < 31; scale++)
+ {
+ size_t size = (1<<scale) + rand()%(1<<scale);
+ pre_start = ceph_clock_now();
+ start = ceph_clock_now();
+ uint32_t crc_a = ceph_crc32c(111, nullptr, size);
+ end = ceph_clock_now();
+ time_adjusted = (end - start) - (start - pre_start);
+ std::cout << "regular method. size=" << size << " time= " << (double)(end-start)
+ << " at " << (double)size/(1024*1024)/(time_adjusted) << " MB/sec"
+ << " error=" << resolution / time_adjusted * 100 << "%" << std::endl;
+
+ pre_start = ceph_clock_now();
+ start = ceph_clock_now();
+#ifdef HAVE_POWER8
+ uint32_t crc_b = ceph_crc32c_zeros(111, size);
+#else
+ uint32_t crc_b = ceph_crc32c_func(111, nullptr, size);
+#endif
+ end = ceph_clock_now();
+ time_adjusted = (end - start) - (start - pre_start);
+#ifdef HAVE_POWER8
+ std::cout << "ceph_crc32c_zeros method. size=" << size << " time="
+ << (double)(end-start) << " at " << (double)size/(1024*1024)/(time_adjusted)
+ << " MB/sec" << " error=" << resolution / time_adjusted * 100 << "%"
+ << std::endl;
+#else
+ std::cout << "fallback method. size=" << size << " time=" << (double)(end-start)
+ << " at " << (double)size/(1024*1024)/(time_adjusted) << " MB/sec"
+ << " error=" << resolution / time_adjusted * 100 << "%" << std::endl;
+#endif
+ EXPECT_EQ(crc_a, crc_b);
+ }
+}
+
+TEST(Crc32c, zeros_performance) {
+ constexpr size_t ITER=100000;
+ utime_t start;
+ utime_t end;
+
+ start = ceph_clock_now();
+ for (size_t i=0; i<ITER; i++)
+ {
+ for (size_t scale=1; scale < 31; scale++)
+ {
+ size_t size = (1<<scale) + rand() % (1<<scale);
+ ceph_crc32c(rand(), nullptr, size);
+ }
+ }
+ end = ceph_clock_now();
+ std::cout << "iterations="<< ITER*31 << " time=" << (double)(end-start) << std::endl;
+
+}
+
diff --git a/src/test/common/test_fair_mutex.cc b/src/test/common/test_fair_mutex.cc
new file mode 100644
index 000000000..10ba835a2
--- /dev/null
+++ b/src/test/common/test_fair_mutex.cc
@@ -0,0 +1,68 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+
+#include <array>
+#include <mutex>
+#include <numeric>
+#include <future>
+#include <gtest/gtest.h>
+#include "common/fair_mutex.h"
+
+TEST(FairMutex, simple)
+{
+ ceph::fair_mutex mutex{"fair::simple"};
+ {
+ std::unique_lock lock{mutex};
+ ASSERT_TRUE(mutex.is_locked());
+ // fair_mutex does not recursive ownership semantics
+ ASSERT_FALSE(mutex.try_lock());
+ }
+ // re-acquire the lock
+ {
+ std::unique_lock lock{mutex};
+ ASSERT_TRUE(mutex.is_locked());
+ }
+ ASSERT_FALSE(mutex.is_locked());
+}
+
+TEST(FairMutex, fair)
+{
+ // waiters are queued in FIFO order, and they are woken up in the same order
+ // we have a marathon participated by multiple teams:
+ // - each team is represented by a thread.
+ // - each team should have equal chance of being selected and scoring, assuming
+ // the runners in each team are distributed evenly in the waiting queue.
+ ceph::fair_mutex mutex{"fair::fair"};
+ const int NR_TEAMS = 2;
+ std::array<unsigned, NR_TEAMS> scoreboard{0, 0};
+ const int NR_ROUNDS = 512;
+ auto play = [&](int team) {
+ for (int i = 0; i < NR_ROUNDS; i++) {
+ std::unique_lock lock{mutex};
+ // pretent that i am running.. and it takes time
+ std::this_thread::sleep_for(std::chrono::microseconds(20));
+ // score!
+ scoreboard[team]++;
+ // fair?
+ unsigned total = std::accumulate(scoreboard.begin(),
+ scoreboard.end(),
+ 0);
+ for (unsigned score : scoreboard) {
+ if (total < NR_ROUNDS) {
+ // not quite statistically significant. to reduce the false positive,
+ // just consider it fair
+ continue;
+ }
+ // check if any team is donimating the game.
+ unsigned avg = total / scoreboard.size();
+ // leave at least half of the average to other teams
+ ASSERT_LE(score, total - avg / 2);
+ // don't treat myself too bad
+ ASSERT_GT(score, avg / 2);
+ };
+ }
+ };
+ std::array<std::future<void>, NR_TEAMS> completed;
+ for (int team = 0; team < NR_TEAMS; team++) {
+ completed[team] = std::async(std::launch::async, play, team);
+ }
+}
diff --git a/src/test/common/test_fault_injector.cc b/src/test/common/test_fault_injector.cc
new file mode 100644
index 000000000..dfa147478
--- /dev/null
+++ b/src/test/common/test_fault_injector.cc
@@ -0,0 +1,248 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/fault_injector.h"
+#include "common/common_init.h"
+#include "common/ceph_argparse.h"
+#include <gtest/gtest.h>
+
+TEST(FaultInjectorDeathTest, InjectAbort)
+{
+ constexpr FaultInjector f{false, InjectAbort{}};
+ EXPECT_EQ(f.check(true), 0);
+ EXPECT_DEATH([[maybe_unused]] int r = f.check(false), "FaultInjector");
+}
+
+TEST(FaultInjectorDeathTest, AssignAbort)
+{
+ FaultInjector<bool> f;
+ ASSERT_EQ(f.check(false), 0);
+ f.inject(false, InjectAbort{});
+ EXPECT_DEATH([[maybe_unused]] int r = f.check(false), "FaultInjector");
+}
+
+// death tests have to run in single-threaded mode, so we can't initialize a
+// CephContext until after those have run (gtest automatically runs them first)
+class Fixture : public testing::Test {
+ boost::intrusive_ptr<CephContext> cct;
+ std::optional<NoDoutPrefix> prefix;
+ protected:
+ void SetUp() override {
+ CephInitParameters params(CEPH_ENTITY_TYPE_CLIENT);
+ cct = common_preinit(params, CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ prefix.emplace(cct.get(), ceph_subsys_context);
+ }
+ void TearDown() override {
+ prefix.reset();
+ cct.reset();
+ }
+ const DoutPrefixProvider* dpp() { return &*prefix; }
+};
+
+// test int as a Key type
+using FaultInjectorInt = Fixture;
+
+TEST_F(FaultInjectorInt, Default)
+{
+ constexpr FaultInjector<int> f;
+ EXPECT_EQ(f.check(0), 0);
+ EXPECT_EQ(f.check(1), 0);
+ EXPECT_EQ(f.check(2), 0);
+ EXPECT_EQ(f.check(3), 0);
+}
+
+TEST_F(FaultInjectorInt, InjectError)
+{
+ constexpr FaultInjector f{2, InjectError{-EINVAL}};
+ EXPECT_EQ(f.check(0), 0);
+ EXPECT_EQ(f.check(1), 0);
+ EXPECT_EQ(f.check(2), -EINVAL);
+ EXPECT_EQ(f.check(3), 0);
+}
+
+TEST_F(FaultInjectorInt, InjectErrorMessage)
+{
+ FaultInjector f{2, InjectError{-EINVAL, dpp()}};
+ EXPECT_EQ(f.check(0), 0);
+ EXPECT_EQ(f.check(1), 0);
+ EXPECT_EQ(f.check(2), -EINVAL);
+ EXPECT_EQ(f.check(3), 0);
+}
+
+TEST_F(FaultInjectorInt, AssignError)
+{
+ FaultInjector<int> f;
+ ASSERT_EQ(f.check(0), 0);
+ f.inject(0, InjectError{-EINVAL});
+ EXPECT_EQ(f.check(0), -EINVAL);
+}
+
+TEST_F(FaultInjectorInt, AssignErrorMessage)
+{
+ FaultInjector<int> f;
+ ASSERT_EQ(f.check(0), 0);
+ f.inject(0, InjectError{-EINVAL, dpp()});
+ EXPECT_EQ(f.check(0), -EINVAL);
+}
+
+// test std::string_view as a Key type
+using FaultInjectorString = Fixture;
+
+TEST_F(FaultInjectorString, Default)
+{
+ constexpr FaultInjector<std::string_view> f;
+ EXPECT_EQ(f.check("Red"), 0);
+ EXPECT_EQ(f.check("Green"), 0);
+ EXPECT_EQ(f.check("Blue"), 0);
+}
+
+TEST_F(FaultInjectorString, InjectError)
+{
+ FaultInjector<std::string_view> f{"Red", InjectError{-EIO}};
+ EXPECT_EQ(f.check("Red"), -EIO);
+ EXPECT_EQ(f.check("Green"), 0);
+ EXPECT_EQ(f.check("Blue"), 0);
+}
+
+TEST_F(FaultInjectorString, InjectErrorMessage)
+{
+ FaultInjector<std::string_view> f{"Red", InjectError{-EIO, dpp()}};
+ EXPECT_EQ(f.check("Red"), -EIO);
+ EXPECT_EQ(f.check("Green"), 0);
+ EXPECT_EQ(f.check("Blue"), 0);
+}
+
+TEST_F(FaultInjectorString, AssignError)
+{
+ FaultInjector<std::string_view> f;
+ ASSERT_EQ(f.check("Red"), 0);
+ f.inject("Red", InjectError{-EINVAL});
+ EXPECT_EQ(f.check("Red"), -EINVAL);
+}
+
+TEST_F(FaultInjectorString, AssignErrorMessage)
+{
+ FaultInjector<std::string_view> f;
+ ASSERT_EQ(f.check("Red"), 0);
+ f.inject("Red", InjectError{-EINVAL, dpp()});
+ EXPECT_EQ(f.check("Red"), -EINVAL);
+}
+
+// test enum class as a Key type
+using FaultInjectorEnum = Fixture;
+
+enum class Color { Red, Green, Blue };
+
+static std::ostream& operator<<(std::ostream& out, const Color& c) {
+ switch (c) {
+ case Color::Red: return out << "Red";
+ case Color::Green: return out << "Green";
+ case Color::Blue: return out << "Blue";
+ }
+ return out;
+}
+
+TEST_F(FaultInjectorEnum, Default)
+{
+ constexpr FaultInjector<Color> f;
+ EXPECT_EQ(f.check(Color::Red), 0);
+ EXPECT_EQ(f.check(Color::Green), 0);
+ EXPECT_EQ(f.check(Color::Blue), 0);
+}
+
+TEST_F(FaultInjectorEnum, InjectError)
+{
+ FaultInjector f{Color::Red, InjectError{-EIO}};
+ EXPECT_EQ(f.check(Color::Red), -EIO);
+ EXPECT_EQ(f.check(Color::Green), 0);
+ EXPECT_EQ(f.check(Color::Blue), 0);
+}
+
+TEST_F(FaultInjectorEnum, InjectErrorMessage)
+{
+ FaultInjector f{Color::Red, InjectError{-EIO, dpp()}};
+ EXPECT_EQ(f.check(Color::Red), -EIO);
+ EXPECT_EQ(f.check(Color::Green), 0);
+ EXPECT_EQ(f.check(Color::Blue), 0);
+}
+
+TEST_F(FaultInjectorEnum, AssignError)
+{
+ FaultInjector<Color> f;
+ ASSERT_EQ(f.check(Color::Red), 0);
+ f.inject(Color::Red, InjectError{-EINVAL});
+ EXPECT_EQ(f.check(Color::Red), -EINVAL);
+}
+
+TEST_F(FaultInjectorEnum, AssignErrorMessage)
+{
+ FaultInjector<Color> f;
+ ASSERT_EQ(f.check(Color::Red), 0);
+ f.inject(Color::Red, InjectError{-EINVAL, dpp()});
+ EXPECT_EQ(f.check(Color::Red), -EINVAL);
+}
+
+// test custom move-only Key type
+using FaultInjectorMoveOnly = Fixture;
+
+struct MoveOnlyKey {
+ MoveOnlyKey() = default;
+ MoveOnlyKey(const MoveOnlyKey&) = delete;
+ MoveOnlyKey& operator=(const MoveOnlyKey&) = delete;
+ MoveOnlyKey(MoveOnlyKey&&) = default;
+ MoveOnlyKey& operator=(MoveOnlyKey&&) = default;
+ ~MoveOnlyKey() = default;
+};
+
+static bool operator==(const MoveOnlyKey&, const MoveOnlyKey&) {
+ return true; // all keys are equal
+}
+static std::ostream& operator<<(std::ostream& out, const MoveOnlyKey&) {
+ return out;
+}
+
+TEST_F(FaultInjectorMoveOnly, Default)
+{
+ constexpr FaultInjector<MoveOnlyKey> f;
+ EXPECT_EQ(f.check(MoveOnlyKey{}), 0);
+}
+
+TEST_F(FaultInjectorMoveOnly, InjectError)
+{
+ FaultInjector f{MoveOnlyKey{}, InjectError{-EIO}};
+ EXPECT_EQ(f.check(MoveOnlyKey{}), -EIO);
+}
+
+TEST_F(FaultInjectorMoveOnly, InjectErrorMessage)
+{
+ FaultInjector f{MoveOnlyKey{}, InjectError{-EIO, dpp()}};
+ EXPECT_EQ(f.check(MoveOnlyKey{}), -EIO);
+}
+
+TEST_F(FaultInjectorMoveOnly, AssignError)
+{
+ FaultInjector<MoveOnlyKey> f;
+ ASSERT_EQ(f.check({}), 0);
+ f.inject({}, InjectError{-EINVAL});
+ EXPECT_EQ(f.check({}), -EINVAL);
+}
+
+TEST_F(FaultInjectorMoveOnly, AssignErrorMessage)
+{
+ FaultInjector<MoveOnlyKey> f;
+ ASSERT_EQ(f.check({}), 0);
+ f.inject({}, InjectError{-EINVAL, dpp()});
+ EXPECT_EQ(f.check({}), -EINVAL);
+}
diff --git a/src/test/common/test_global_doublefree.cc b/src/test/common/test_global_doublefree.cc
new file mode 100644
index 000000000..ef8fefb6b
--- /dev/null
+++ b/src/test/common/test_global_doublefree.cc
@@ -0,0 +1,30 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+/*
+ * This test is linked against librados and libcephfs to try and detect issues
+ * with global, static, non-POD variables as seen in the following trackers.
+ * http://tracker.ceph.com/issues/16504
+ * http://tracker.ceph.com/issues/16686
+ * In those trackers such variables caused segfaults with glibc reporting
+ * "double free or corruption".
+ *
+ * Don't be fooled by its emptiness. It does serve a purpose :)
+ */
+
+int main(int, char**)
+{
+ return 0;
+}
diff --git a/src/test/common/test_hobject.cc b/src/test/common/test_hobject.cc
new file mode 100644
index 000000000..0bb4aef9e
--- /dev/null
+++ b/src/test/common/test_hobject.cc
@@ -0,0 +1,11 @@
+#include "common/hobject.h"
+#include "gtest/gtest.h"
+
+TEST(HObject, cmp)
+{
+ hobject_t c{object_t{"fooc"}, "food", CEPH_NOSNAP, 42, 0, "nspace"};
+ hobject_t d{object_t{"food"}, "", CEPH_NOSNAP, 42, 0, "nspace"};
+ hobject_t e{object_t{"fooe"}, "food", CEPH_NOSNAP, 42, 0, "nspace"};
+ ASSERT_EQ(-1, cmp(c, d));
+ ASSERT_EQ(-1, cmp(d, e));
+}
diff --git a/src/test/common/test_hostname.cc b/src/test/common/test_hostname.cc
new file mode 100644
index 000000000..b0e631d20
--- /dev/null
+++ b/src/test/common/test_hostname.cc
@@ -0,0 +1,71 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "common/hostname.h"
+#include "common/SubProcess.h"
+#include "stdio.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "unistd.h"
+
+#include <array>
+#include <iostream>
+#include <stdexcept>
+#include <stdio.h>
+#include <string>
+#include <memory>
+
+std::string exec(const char* cmd) {
+ std::array<char, 128> buffer;
+ std::string result;
+ std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
+ if (!pipe) throw std::runtime_error("popen() failed!");
+ while (!feof(pipe.get())) {
+ if (fgets(buffer.data(), 128, pipe.get()) != NULL)
+ result += buffer.data();
+ }
+ // remove \n
+ return result.substr(0, result.size()-1);;
+}
+
+TEST(Hostname, full) {
+ std::string hn = ceph_get_hostname();
+ if (const char *nn = getenv("NODE_NAME")) {
+ // we are in a container
+ std::cout << "we are in a container on " << nn << ", reporting " << hn
+ << std::endl;
+ ASSERT_EQ(hn, nn);
+ } else {
+ ASSERT_EQ(hn, exec("hostname")) ;
+ }
+}
+
+TEST(Hostname, short) {
+ std::string shn = ceph_get_short_hostname();
+ if (const char *nn = getenv("NODE_NAME")) {
+ // we are in a container
+ std::cout << "we are in a container on " << nn << ", reporting short " << shn
+ << ", skipping test because env var may or may not be short form"
+ << std::endl;
+ } else {
+ #ifdef _WIN32
+ ASSERT_EQ(shn, exec("hostname"));
+ #else
+ ASSERT_EQ(shn, exec("hostname -s"));
+ #endif
+ }
+}
diff --git a/src/test/common/test_interval_map.cc b/src/test/common/test_interval_map.cc
new file mode 100644
index 000000000..99f676f28
--- /dev/null
+++ b/src/test/common/test_interval_map.cc
@@ -0,0 +1,337 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <gtest/gtest.h>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int_distribution.hpp>
+#include <boost/mpl/apply.hpp>
+#include "include/buffer.h"
+#include "common/interval_map.h"
+
+using namespace std;
+
+template<typename T>
+class IntervalMapTest : public ::testing::Test {
+public:
+ using TestType = T;
+};
+
+template <typename _key>
+struct bufferlist_test_type {
+ using key = _key;
+ using value = bufferlist;
+
+ struct make_splitter {
+ template <typename merge_t>
+ struct apply {
+ bufferlist split(
+ key offset,
+ key len,
+ bufferlist &bu) const {
+ bufferlist bl;
+ bl.substr_of(bu, offset, len);
+ return bl;
+ }
+ bool can_merge(const bufferlist &left, const bufferlist &right) const {
+ return merge_t::value;
+ }
+ bufferlist merge(bufferlist &&left, bufferlist &&right) const {
+ bufferlist bl;
+ left.claim_append(right);
+ return std::move(left);
+ }
+ uint64_t length(const bufferlist &r) const {
+ return r.length();
+ }
+ };
+ };
+
+ struct generate_random {
+ bufferlist operator()(key len) {
+ bufferlist bl;
+ boost::random::mt19937 rng;
+ boost::random::uniform_int_distribution<> chr(0,255);
+ for (key i = 0; i < len; ++i) {
+ bl.append((char)chr(rng));
+ }
+ return bl;
+ }
+ };
+};
+
+using IntervalMapTypes = ::testing::Types< bufferlist_test_type<uint64_t> >;
+
+TYPED_TEST_SUITE(IntervalMapTest, IntervalMapTypes);
+
+#define USING(_can_merge) \
+ using TT = typename TestFixture::TestType; \
+ using key = typename TT::key; (void)key(0); \
+ using val = typename TT::value; (void)val(0); \
+ using splitter = typename boost::mpl::apply< \
+ typename TT::make_splitter, \
+ _can_merge>; \
+ using imap = interval_map<key, val, splitter>; (void)imap(); \
+ typename TT::generate_random gen; \
+ val v(gen(5)); \
+ splitter split; (void)split.split(0, 0, v);
+
+#define USING_NO_MERGE USING(std::false_type)
+#define USING_WITH_MERGE USING(std::true_type)
+
+TYPED_TEST(IntervalMapTest, empty) {
+ USING_NO_MERGE;
+ imap m;
+ ASSERT_TRUE(m.empty());
+}
+
+TYPED_TEST(IntervalMapTest, insert) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5)};
+ m.insert(0, 5, vals[0]);
+ m.insert(10, 5, vals[2]);
+ m.insert(5, 5, vals[1]);
+ ASSERT_EQ(m.ext_count(), 3u);
+
+ unsigned i = 0;
+ for (auto &&ext: m) {
+ ASSERT_EQ(ext.get_len(), 5u);
+ ASSERT_EQ(ext.get_off(), 5u * i);
+ ASSERT_EQ(ext.get_val(), vals[i]);
+ ++i;
+ }
+ ASSERT_EQ(i, m.ext_count());
+}
+
+TYPED_TEST(IntervalMapTest, insert_begin_overlap) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5)};
+ m.insert(5, 5, vals[1]);
+ m.insert(10, 5, vals[2]);
+ m.insert(1, 5, vals[0]);
+
+ auto iter = m.begin();
+ ASSERT_EQ(iter.get_off(), 1u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[0]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 6u);
+ ASSERT_EQ(iter.get_len(), 4u);
+ ASSERT_EQ(iter.get_val(), split.split(1, 4, vals[1]));
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 10u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[2]);
+ ++iter;
+
+ ASSERT_EQ(iter, m.end());
+}
+
+TYPED_TEST(IntervalMapTest, insert_end_overlap) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5)};
+ m.insert(0, 5, vals[0]);
+ m.insert(5, 5, vals[1]);
+ m.insert(8, 5, vals[2]);
+
+ auto iter = m.begin();
+ ASSERT_EQ(iter.get_off(), 0u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[0]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 5u);
+ ASSERT_EQ(iter.get_len(), 3u);
+ ASSERT_EQ(iter.get_val(), split.split(0, 3, vals[1]));
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 8u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[2]);
+ ++iter;
+
+ ASSERT_EQ(iter, m.end());
+}
+
+TYPED_TEST(IntervalMapTest, insert_middle_overlap) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(7), gen(5)};
+ m.insert(0, 5, vals[0]);
+ m.insert(10, 5, vals[2]);
+ m.insert(4, 7, vals[1]);
+
+ auto iter = m.begin();
+ ASSERT_EQ(iter.get_off(), 0u);
+ ASSERT_EQ(iter.get_len(), 4u);
+ ASSERT_EQ(iter.get_val(), split.split(0, 4, vals[0]));
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 4u);
+ ASSERT_EQ(iter.get_len(), 7u);
+ ASSERT_EQ(iter.get_val(), vals[1]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 11u);
+ ASSERT_EQ(iter.get_len(), 4u);
+ ASSERT_EQ(iter.get_val(), split.split(1, 4, vals[2]));
+ ++iter;
+
+ ASSERT_EQ(iter, m.end());
+}
+
+TYPED_TEST(IntervalMapTest, insert_single_exact_overlap) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5)};
+ m.insert(0, 5, gen(5));
+ m.insert(5, 5, vals[1]);
+ m.insert(10, 5, vals[2]);
+ m.insert(0, 5, vals[0]);
+
+ auto iter = m.begin();
+ ASSERT_EQ(iter.get_off(), 0u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[0]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 5u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[1]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 10u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[2]);
+ ++iter;
+
+ ASSERT_EQ(iter, m.end());
+}
+
+TYPED_TEST(IntervalMapTest, insert_single_exact_overlap_end) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5)};
+ m.insert(0, 5, vals[0]);
+ m.insert(5, 5, vals[1]);
+ m.insert(10, 5, gen(5));
+ m.insert(10, 5, vals[2]);
+
+ auto iter = m.begin();
+ ASSERT_EQ(iter.get_off(), 0u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[0]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 5u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[1]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 10u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[2]);
+ ++iter;
+
+ ASSERT_EQ(iter, m.end());
+}
+
+TYPED_TEST(IntervalMapTest, erase) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5)};
+ m.insert(0, 5, vals[0]);
+ m.insert(5, 5, vals[1]);
+ m.insert(10, 5, vals[2]);
+
+ m.erase(3, 5);
+
+ auto iter = m.begin();
+ ASSERT_EQ(iter.get_off(), 0u);
+ ASSERT_EQ(iter.get_len(), 3u);
+ ASSERT_EQ(iter.get_val(), split.split(0, 3, vals[0]));
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 8u);
+ ASSERT_EQ(iter.get_len(), 2u);
+ ASSERT_EQ(iter.get_val(), split.split(3, 2, vals[1]));
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 10u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[2]);
+ ++iter;
+
+ ASSERT_EQ(iter, m.end());
+}
+
+TYPED_TEST(IntervalMapTest, erase_exact) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5)};
+ m.insert(0, 5, vals[0]);
+ m.insert(5, 5, vals[1]);
+ m.insert(10, 5, vals[2]);
+
+ m.erase(5, 5);
+
+ auto iter = m.begin();
+ ASSERT_EQ(iter.get_off(), 0u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[0]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 10u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[2]);
+ ++iter;
+
+ ASSERT_EQ(iter, m.end());
+}
+
+TYPED_TEST(IntervalMapTest, get_containing_range) {
+ USING_NO_MERGE;
+ imap m;
+ vector<val> vals{gen(5), gen(5), gen(5), gen(5)};
+ m.insert(0, 5, vals[0]);
+ m.insert(10, 5, vals[1]);
+ m.insert(20, 5, vals[2]);
+ m.insert(30, 5, vals[3]);
+
+ auto rng = m.get_containing_range(5, 21);
+ auto iter = rng.first;
+
+ ASSERT_EQ(iter.get_off(), 10u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[1]);
+ ++iter;
+
+ ASSERT_EQ(iter.get_off(), 20u);
+ ASSERT_EQ(iter.get_len(), 5u);
+ ASSERT_EQ(iter.get_val(), vals[2]);
+ ++iter;
+
+ ASSERT_EQ(iter, rng.second);
+}
+
+TYPED_TEST(IntervalMapTest, merge) {
+ USING_WITH_MERGE;
+ imap m;
+ m.insert(10, 4, gen(4));
+ m.insert(11, 1, gen(1));
+}
diff --git a/src/test/common/test_interval_set.cc b/src/test/common/test_interval_set.cc
new file mode 100644
index 000000000..7eb1dcbe2
--- /dev/null
+++ b/src/test/common/test_interval_set.cc
@@ -0,0 +1,600 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Mirantis, Inc.
+ *
+ * Author: Igor Fedotov <ifedotov@mirantis.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <gtest/gtest.h>
+#include <boost/container/flat_map.hpp>
+#include "include/interval_set.h"
+#include "include/btree_map.h"
+
+using namespace ceph;
+
+typedef uint64_t IntervalValueType;
+
+template<typename T> // tuple<type to test on, test array size>
+class IntervalSetTest : public ::testing::Test {
+
+ public:
+ typedef T ISet;
+};
+
+typedef ::testing::Types<
+ interval_set<IntervalValueType>,
+ interval_set<IntervalValueType, btree::btree_map>,
+ interval_set<IntervalValueType, boost::container::flat_map>
+ > IntervalSetTypes;
+
+TYPED_TEST_SUITE(IntervalSetTest, IntervalSetTypes);
+
+TYPED_TEST(IntervalSetTest, compare) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1, iset2;
+ ASSERT_TRUE(iset1 == iset1);
+ ASSERT_TRUE(iset1 == iset2);
+
+ iset1.insert(1);
+ ASSERT_FALSE(iset1 == iset2);
+
+ iset2.insert(1);
+ ASSERT_TRUE(iset1 == iset2);
+
+ iset1.insert(2, 3);
+ iset2.insert(2, 4);
+ ASSERT_FALSE(iset1 == iset2);
+
+ iset2.erase(2, 4);
+ iset2.erase(1);
+ iset2.insert(2, 3);
+ iset2.insert(1);
+ ASSERT_TRUE(iset1 == iset2);
+
+ iset1.insert(100, 10);
+ iset2.insert(100, 5);
+ ASSERT_FALSE(iset1 == iset2);
+ iset2.insert(105, 5);
+ ASSERT_TRUE(iset1 == iset2);
+
+ iset1.insert(200, 10);
+ iset2.insert(205, 5);
+ ASSERT_FALSE(iset1 == iset2);
+ iset2.insert(200, 1);
+ iset2.insert(202, 3);
+ ASSERT_FALSE(iset1 == iset2);
+ iset2.insert(201, 1);
+ ASSERT_TRUE(iset1 == iset2);
+
+ iset1.clear();
+ ASSERT_FALSE(iset1 == iset2);
+ iset2.clear();
+ ASSERT_TRUE(iset1 == iset2);
+}
+
+TYPED_TEST(IntervalSetTest, contains) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1;
+ ASSERT_FALSE(iset1.contains( 1 ));
+ ASSERT_FALSE(iset1.contains( 0, 1 ));
+
+ iset1.insert(1);
+ ASSERT_TRUE(iset1.contains( 1 ));
+ ASSERT_FALSE(iset1.contains( 0 ));
+ ASSERT_FALSE(iset1.contains( 2 ));
+ ASSERT_FALSE(iset1.contains( 0, 1 ));
+ ASSERT_FALSE(iset1.contains( 0, 2 ));
+ ASSERT_TRUE(iset1.contains( 1, 1 ));
+ ASSERT_FALSE(iset1.contains( 1, 2 ));
+
+ iset1.insert(2, 3);
+ ASSERT_TRUE(iset1.contains( 1 ));
+ ASSERT_FALSE(iset1.contains( 0 ));
+ ASSERT_TRUE(iset1.contains( 2 ));
+ ASSERT_FALSE(iset1.contains( 0, 1 ));
+ ASSERT_FALSE(iset1.contains( 0, 2 ));
+ ASSERT_TRUE(iset1.contains( 1, 1 ));
+ ASSERT_TRUE(iset1.contains( 1, 2 ));
+ ASSERT_TRUE(iset1.contains( 1, 3 ));
+ ASSERT_TRUE(iset1.contains( 1, 4 ));
+ ASSERT_FALSE(iset1.contains( 1, 5 ));
+ ASSERT_TRUE(iset1.contains( 2, 1 ));
+ ASSERT_TRUE(iset1.contains( 2, 2 ));
+ ASSERT_TRUE(iset1.contains( 2, 3 ));
+ ASSERT_FALSE(iset1.contains( 2, 4 ));
+ ASSERT_TRUE(iset1.contains( 3, 2 ));
+ ASSERT_TRUE(iset1.contains( 4, 1 ));
+ ASSERT_FALSE(iset1.contains( 4, 2 ));
+
+ iset1.insert(10, 10);
+ ASSERT_TRUE(iset1.contains( 1, 4 ));
+ ASSERT_FALSE(iset1.contains( 1, 5 ));
+ ASSERT_TRUE(iset1.contains( 2, 2 ));
+ ASSERT_FALSE(iset1.contains( 2, 4 ));
+
+ ASSERT_FALSE(iset1.contains( 1, 10 ));
+ ASSERT_FALSE(iset1.contains( 9, 1 ));
+ ASSERT_FALSE(iset1.contains( 9 ));
+ ASSERT_FALSE(iset1.contains( 9, 11 ));
+ ASSERT_TRUE(iset1.contains( 10, 1 ));
+ ASSERT_TRUE(iset1.contains( 11, 9 ));
+ ASSERT_TRUE(iset1.contains( 11, 2 ));
+ ASSERT_TRUE(iset1.contains( 18, 2 ));
+ ASSERT_TRUE(iset1.contains( 18, 2 ));
+ ASSERT_TRUE(iset1.contains( 10 ));
+ ASSERT_TRUE(iset1.contains( 19 ));
+ ASSERT_FALSE(iset1.contains( 20 ));
+ ASSERT_FALSE(iset1.contains( 21 ));
+
+ ASSERT_FALSE(iset1.contains( 11, 11 ));
+ ASSERT_FALSE(iset1.contains( 18, 9 ));
+
+ iset1.clear();
+ ASSERT_FALSE(iset1.contains( 1 ));
+ ASSERT_FALSE(iset1.contains( 0 ));
+ ASSERT_FALSE(iset1.contains( 2 ));
+ ASSERT_FALSE(iset1.contains( 0, 1 ));
+ ASSERT_FALSE(iset1.contains( 0, 2 ));
+ ASSERT_FALSE(iset1.contains( 1, 1 ));
+ ASSERT_FALSE(iset1.contains( 10, 2 ));
+}
+
+TYPED_TEST(IntervalSetTest, intersects) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1;
+ ASSERT_FALSE(iset1.intersects( 1, 1 ));
+ ASSERT_FALSE(iset1.intersects( 0, 1 ));
+ ASSERT_FALSE(iset1.intersects( 0, 10 ));
+
+ iset1.insert(1);
+ ASSERT_TRUE(iset1.intersects( 1, 1 ));
+ ASSERT_FALSE(iset1.intersects( 0, 1 ));
+ ASSERT_FALSE(iset1.intersects( 2, 1 ));
+ ASSERT_TRUE(iset1.intersects( 0, 2 ));
+ ASSERT_TRUE(iset1.intersects( 0, 20 ));
+ ASSERT_TRUE(iset1.intersects( 1, 2 ));
+ ASSERT_TRUE(iset1.intersects( 1, 20 ));
+
+ iset1.insert(2, 3);
+ ASSERT_FALSE(iset1.intersects( 0, 1 ));
+ ASSERT_TRUE(iset1.intersects( 0, 2 ));
+ ASSERT_TRUE(iset1.intersects( 0, 200 ));
+ ASSERT_TRUE(iset1.intersects( 1, 1 ));
+ ASSERT_TRUE(iset1.intersects( 1, 4 ));
+ ASSERT_TRUE(iset1.intersects( 1, 5 ));
+ ASSERT_TRUE(iset1.intersects( 2, 1 ));
+ ASSERT_TRUE(iset1.intersects( 2, 2 ));
+ ASSERT_TRUE(iset1.intersects( 2, 3 ));
+ ASSERT_TRUE(iset1.intersects( 2, 4 ));
+ ASSERT_TRUE(iset1.intersects( 3, 2 ));
+ ASSERT_TRUE(iset1.intersects( 4, 1 ));
+ ASSERT_TRUE(iset1.intersects( 4, 2 ));
+ ASSERT_FALSE(iset1.intersects( 5, 2 ));
+
+ iset1.insert(10, 10);
+ ASSERT_TRUE(iset1.intersects( 1, 4 ));
+ ASSERT_TRUE(iset1.intersects( 1, 5 ));
+ ASSERT_TRUE(iset1.intersects( 1, 10 ));
+ ASSERT_TRUE(iset1.intersects( 2, 2 ));
+ ASSERT_TRUE(iset1.intersects( 2, 4 ));
+ ASSERT_FALSE(iset1.intersects( 5, 1 ));
+ ASSERT_FALSE(iset1.intersects( 5, 2 ));
+ ASSERT_FALSE(iset1.intersects( 5, 5 ));
+ ASSERT_TRUE(iset1.intersects( 5, 12 ));
+ ASSERT_TRUE(iset1.intersects( 5, 20 ));
+
+ ASSERT_FALSE(iset1.intersects( 9, 1 ));
+ ASSERT_TRUE(iset1.intersects( 9, 2 ));
+
+ ASSERT_TRUE(iset1.intersects( 9, 11 ));
+ ASSERT_TRUE(iset1.intersects( 10, 1 ));
+ ASSERT_TRUE(iset1.intersects( 11, 9 ));
+ ASSERT_TRUE(iset1.intersects( 11, 2 ));
+ ASSERT_TRUE(iset1.intersects( 11, 11 ));
+ ASSERT_TRUE(iset1.intersects( 18, 2 ));
+ ASSERT_TRUE(iset1.intersects( 18, 9 ));
+ ASSERT_FALSE(iset1.intersects( 20, 1 ));
+ ASSERT_FALSE(iset1.intersects( 21, 12 ));
+
+ iset1.clear();
+ ASSERT_FALSE(iset1.intersects( 0, 1 ));
+ ASSERT_FALSE(iset1.intersects( 0, 2 ));
+ ASSERT_FALSE(iset1.intersects( 1, 1 ));
+ ASSERT_FALSE(iset1.intersects( 5, 2 ));
+ ASSERT_FALSE(iset1.intersects( 10, 2 ));
+}
+
+TYPED_TEST(IntervalSetTest, insert_erase) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1, iset2;
+ IntervalValueType start, len;
+
+ iset1.insert(3, 5, &start, &len);
+ ASSERT_EQ(3, start);
+ ASSERT_EQ(5, len);
+ ASSERT_EQ(1, iset1.num_intervals());
+ ASSERT_EQ(5, iset1.size());
+
+ //adding standalone interval
+ iset1.insert(15, 10, &start, &len);
+ ASSERT_EQ(15, start);
+ ASSERT_EQ(10, len);
+ ASSERT_EQ(2, iset1.num_intervals());
+ ASSERT_EQ(15, iset1.size());
+
+ //adding leftmost standalone interval
+ iset1.insert(1, 1, &start, &len);
+ ASSERT_EQ(1, start);
+ ASSERT_EQ(1, len);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(16, iset1.size());
+
+ //adding leftmost adjucent interval
+ iset1.insert(0, 1, &start, &len);
+ ASSERT_EQ(0, start);
+ ASSERT_EQ(2, len);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(17, iset1.size());
+
+ //adding interim interval that merges leftmost and subseqent intervals
+ iset1.insert(2, 1, &start, &len);
+ ASSERT_EQ(0, start);
+ ASSERT_EQ(8, len);
+ ASSERT_EQ(2, iset1.num_intervals());
+ ASSERT_EQ(18, iset1.size());
+
+ //adding rigtmost standalone interval
+ iset1.insert(30, 5, &start, &len);
+ ASSERT_EQ(30, start);
+ ASSERT_EQ(5, len);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(23, iset1.size());
+
+ //adding rigtmost adjusent interval
+ iset1.insert(35, 10, &start, &len);
+ ASSERT_EQ(30, start);
+ ASSERT_EQ(15, len );
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(33, iset1.size());
+
+ //adding interim interval that merges with the interval preceeding the rightmost
+ iset1.insert(25, 1, &start, &len);
+ ASSERT_EQ(15, start);
+ ASSERT_EQ(11, len);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(34, iset1.size());
+
+ //adding interim interval that merges with the rightmost and preceeding intervals
+ iset1.insert(26, 4, &start, &len);
+ ASSERT_EQ(15, start);
+ ASSERT_EQ(30, len);
+ ASSERT_EQ(2, iset1.num_intervals());
+ ASSERT_EQ(38, iset1.size());
+
+ //and finally build single interval filling the gap at 8-15 using different interval set
+ iset2.insert( 8, 1 );
+ iset2.insert( 14, 1 );
+ iset2.insert( 9, 4 );
+ iset1.insert( iset2 );
+ iset1.insert(13, 1, &start, &len);
+ ASSERT_EQ(0, start);
+ ASSERT_EQ(45, len);
+ ASSERT_EQ(1, iset1.num_intervals());
+ ASSERT_EQ(45, iset1.size());
+
+ //now reverses the process using subtract & erase
+ iset1.subtract( iset2 );
+ iset1.erase(13, 1);
+ ASSERT_EQ( 2, iset1.num_intervals() );
+ ASSERT_EQ(38, iset1.size());
+ ASSERT_TRUE( iset1.contains( 7, 1 ));
+ ASSERT_FALSE( iset1.contains( 8, 7 ));
+ ASSERT_TRUE( iset1.contains( 15, 1 ));
+ ASSERT_TRUE( iset1.contains( 26, 4 ));
+
+ iset1.erase(26, 4);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(34, iset1.size());
+ ASSERT_TRUE( iset1.contains( 7, 1 ));
+ ASSERT_FALSE( iset1.intersects( 8, 7 ));
+ ASSERT_TRUE( iset1.contains( 15, 1 ));
+ ASSERT_TRUE( iset1.contains( 25, 1 ));
+ ASSERT_FALSE( iset1.contains( 26, 4 ));
+ ASSERT_TRUE( iset1.contains( 30, 1 ));
+
+ iset1.erase(25, 1);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(33, iset1.size());
+ ASSERT_TRUE( iset1.contains( 24, 1 ));
+ ASSERT_FALSE( iset1.contains( 25, 1 ));
+ ASSERT_FALSE( iset1.intersects( 26, 4 ));
+ ASSERT_TRUE( iset1.contains( 30, 1 ));
+ ASSERT_TRUE( iset1.contains( 35, 10 ));
+
+ iset1.erase(35, 10);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(23, iset1.size());
+ ASSERT_TRUE( iset1.contains( 30, 5 ));
+ ASSERT_TRUE( iset1.contains( 34, 1 ));
+ ASSERT_FALSE( iset1.contains( 35, 10 ));
+ ASSERT_FALSE(iset1.contains( 45, 1 ));
+
+ iset1.erase(30, 5);
+ ASSERT_EQ(2, iset1.num_intervals());
+ ASSERT_EQ(18, iset1.size());
+ ASSERT_TRUE( iset1.contains( 2, 1 ));
+ ASSERT_TRUE( iset1.contains( 24, 1 ));
+ ASSERT_FALSE( iset1.contains( 25, 1 ));
+ ASSERT_FALSE( iset1.contains( 29, 1 ));
+ ASSERT_FALSE( iset1.contains( 30, 5 ));
+ ASSERT_FALSE( iset1.contains( 35, 1 ));
+
+ iset1.erase(2, 1);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ( iset1.size(), 17 );
+ ASSERT_TRUE( iset1.contains( 0, 1 ));
+ ASSERT_TRUE( iset1.contains( 1, 1 ));
+ ASSERT_FALSE( iset1.contains( 2, 1 ));
+ ASSERT_TRUE( iset1.contains( 3, 1 ));
+ ASSERT_TRUE( iset1.contains( 15, 1 ));
+ ASSERT_FALSE( iset1.contains( 25, 1 ));
+
+ iset1.erase( 0, 1);
+ ASSERT_EQ(3, iset1.num_intervals());
+ ASSERT_EQ(16, iset1.size());
+ ASSERT_FALSE( iset1.contains( 0, 1 ));
+ ASSERT_TRUE( iset1.contains( 1, 1 ));
+ ASSERT_FALSE( iset1.contains( 2, 1 ));
+ ASSERT_TRUE( iset1.contains( 3, 1 ));
+ ASSERT_TRUE( iset1.contains( 15, 1 ));
+
+ iset1.erase(1, 1);
+ ASSERT_EQ(2, iset1.num_intervals());
+ ASSERT_EQ(15, iset1.size());
+ ASSERT_FALSE( iset1.contains( 1, 1 ));
+ ASSERT_TRUE( iset1.contains( 15, 10 ));
+ ASSERT_TRUE( iset1.contains( 3, 5 ));
+
+ iset1.erase(15, 10);
+ ASSERT_EQ(1, iset1.num_intervals());
+ ASSERT_EQ(5, iset1.size());
+ ASSERT_FALSE( iset1.contains( 1, 1 ));
+ ASSERT_FALSE( iset1.contains( 15, 10 ));
+ ASSERT_FALSE( iset1.contains( 25, 1 ));
+ ASSERT_TRUE( iset1.contains( 3, 5 ));
+
+ iset1.erase( 3, 1);
+ ASSERT_EQ(1, iset1.num_intervals());
+ ASSERT_EQ(4, iset1.size());
+ ASSERT_FALSE( iset1.contains( 1, 1 ));
+ ASSERT_FALSE( iset1.contains( 15, 10 ));
+ ASSERT_FALSE( iset1.contains( 25, 1 ));
+ ASSERT_TRUE( iset1.contains( 4, 4 ));
+ ASSERT_FALSE( iset1.contains( 3, 5 ));
+
+ iset1.erase( 4, 4);
+ ASSERT_EQ(0, iset1.num_intervals());
+ ASSERT_EQ(0, iset1.size());
+ ASSERT_FALSE( iset1.contains( 1, 1 ));
+ ASSERT_FALSE( iset1.contains( 15, 10 ));
+ ASSERT_FALSE( iset1.contains( 25, 1 ));
+ ASSERT_FALSE( iset1.contains( 3, 4 ));
+ ASSERT_FALSE( iset1.contains( 3, 5 ));
+ ASSERT_FALSE( iset1.contains( 4, 4 ));
+
+
+}
+
+TYPED_TEST(IntervalSetTest, intersect_of) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1, iset2, iset3;
+
+ iset1.intersection_of( iset2, iset3 );
+ ASSERT_TRUE( iset1.num_intervals() == 0);
+ ASSERT_TRUE( iset1.size() == 0);
+
+ iset2.insert( 0, 1 );
+ iset2.insert( 5, 10 );
+ iset2.insert( 30, 10 );
+
+ iset3.insert( 0, 2 );
+ iset3.insert( 15, 1 );
+ iset3.insert( 20, 5 );
+ iset3.insert( 29, 3 );
+ iset3.insert( 35, 3 );
+ iset3.insert( 39, 3 );
+
+ iset1.intersection_of( iset2, iset3 );
+ ASSERT_TRUE( iset1.num_intervals() == 4);
+ ASSERT_TRUE( iset1.size() == 7);
+
+ ASSERT_TRUE( iset1.contains( 0, 1 ));
+ ASSERT_FALSE( iset1.contains( 0, 2 ));
+
+ ASSERT_FALSE( iset1.contains( 5, 11 ));
+ ASSERT_FALSE( iset1.contains( 4, 1 ));
+ ASSERT_FALSE( iset1.contains( 16, 1 ));
+
+ ASSERT_FALSE( iset1.contains( 20, 5 ));
+
+ ASSERT_FALSE( iset1.contains( 29, 1 ));
+ ASSERT_FALSE( iset1.contains( 30, 10 ));
+
+ ASSERT_TRUE( iset1.contains( 30, 2 ));
+ ASSERT_TRUE( iset1.contains( 35, 3 ));
+ ASSERT_FALSE( iset1.contains( 35, 4 ));
+
+ ASSERT_TRUE( iset1.contains( 39, 1 ));
+ ASSERT_FALSE( iset1.contains( 38, 2 ));
+ ASSERT_FALSE( iset1.contains( 39, 2 ));
+
+ iset3=iset1;
+ iset1.intersection_of(iset2);
+ ASSERT_TRUE( iset1 == iset3);
+
+ iset2.clear();
+ iset2.insert(0,1);
+ iset1.intersection_of(iset2);
+ ASSERT_TRUE( iset1.num_intervals() == 1);
+ ASSERT_TRUE( iset1.size() == 1);
+
+ iset1 = iset3;
+ iset2.clear();
+ iset1.intersection_of(iset2);
+ ASSERT_TRUE( iset1.num_intervals() == 0);
+ ASSERT_TRUE( iset1.size() == 0);
+
+}
+
+TYPED_TEST(IntervalSetTest, union_of) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1, iset2, iset3;
+
+ iset1.union_of( iset2, iset3 );
+ ASSERT_TRUE( iset1.num_intervals() == 0);
+ ASSERT_TRUE( iset1.size() == 0);
+
+ iset2.insert( 0, 1 );
+ iset2.insert( 5, 10 );
+ iset2.insert( 30, 10 );
+
+ iset3.insert( 0, 2 );
+ iset3.insert( 15, 1 );
+ iset3.insert( 20, 5 );
+ iset3.insert( 29, 3 );
+ iset3.insert( 39, 3 );
+
+ iset1.union_of( iset2, iset3 );
+ ASSERT_TRUE( iset1.num_intervals() == 4);
+ ASSERT_EQ( iset1.size(), 31);
+ ASSERT_TRUE( iset1.contains( 0, 2 ));
+ ASSERT_FALSE( iset1.contains( 0, 3 ));
+
+ ASSERT_TRUE( iset1.contains( 5, 11 ));
+ ASSERT_FALSE( iset1.contains( 4, 1 ));
+ ASSERT_FALSE( iset1.contains( 16, 1 ));
+
+ ASSERT_TRUE( iset1.contains( 20, 5 ));
+
+ ASSERT_TRUE( iset1.contains( 30, 10 ));
+ ASSERT_TRUE( iset1.contains( 29, 13 ));
+ ASSERT_FALSE( iset1.contains( 29, 14 ));
+ ASSERT_FALSE( iset1.contains( 42, 1 ));
+
+ iset2.clear();
+ iset1.union_of(iset2);
+ ASSERT_TRUE( iset1.num_intervals() == 4);
+ ASSERT_EQ( iset1.size(), 31);
+
+ iset3.clear();
+ iset3.insert( 29, 3 );
+ iset3.insert( 39, 2 );
+ iset1.union_of(iset3);
+
+ ASSERT_TRUE( iset1.num_intervals() == 4);
+ ASSERT_EQ( iset1.size(), 31); //actually we added nothing
+ ASSERT_TRUE( iset1.contains( 29, 13 ));
+ ASSERT_FALSE( iset1.contains( 29, 14 ));
+ ASSERT_FALSE( iset1.contains( 42, 1 ));
+
+}
+
+TYPED_TEST(IntervalSetTest, subset_of) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1, iset2;
+
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset1.insert(5,10);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset2.insert(6,8);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset2.insert(5,1);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset2.insert(14,10);
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset1.insert( 20, 4);
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset1.insert( 24, 1);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset2.insert( 24, 1);
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset1.insert( 30, 5);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset2.insert( 30, 5);
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset2.erase( 30, 1);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset1.erase( 30, 1);
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset2.erase( 34, 1);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset1.erase( 34, 1);
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset1.insert( 40, 5);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+
+ iset2.insert( 39, 7);
+ ASSERT_TRUE(iset1.subset_of(iset2));
+
+ iset1.insert( 50, 5);
+ iset2.insert( 55, 2);
+ ASSERT_FALSE(iset1.subset_of(iset2));
+}
+
+TYPED_TEST(IntervalSetTest, span_of) {
+ typedef typename TestFixture::ISet ISet;
+ ISet iset1, iset2;
+
+ iset2.insert(5,5);
+ iset2.insert(20,5);
+
+ iset1.span_of( iset2, 8, 5 );
+ ASSERT_EQ( iset1.num_intervals(), 2);
+ ASSERT_EQ( iset1.size(), 5);
+ ASSERT_TRUE( iset1.contains( 8, 2 ));
+ ASSERT_TRUE( iset1.contains( 20, 3 ));
+
+ iset1.span_of( iset2, 3, 5 );
+ ASSERT_EQ( iset1.num_intervals(), 1);
+ ASSERT_EQ( iset1.size(), 5);
+ ASSERT_TRUE( iset1.contains( 5, 5 ));
+
+ iset1.span_of( iset2, 10, 7 );
+ ASSERT_EQ( iset1.num_intervals(), 1);
+ ASSERT_EQ( iset1.size(), 5);
+ ASSERT_TRUE( iset1.contains( 20, 5 ));
+ ASSERT_FALSE( iset1.contains( 20, 6 ));
+
+ iset1.span_of( iset2, 5, 10);
+ ASSERT_EQ( iset1.num_intervals(), 2);
+ ASSERT_EQ( iset1.size(), 10);
+ ASSERT_TRUE( iset1.contains( 5, 5 ));
+ ASSERT_TRUE( iset1.contains( 20, 5 ));
+
+ iset1.span_of( iset2, 100, 5 );
+ ASSERT_EQ( iset1.num_intervals(), 0);
+ ASSERT_EQ( iset1.size(), 0);
+}
diff --git a/src/test/common/test_intrusive_lru.cc b/src/test/common/test_intrusive_lru.cc
new file mode 100644
index 000000000..0654bd97d
--- /dev/null
+++ b/src/test/common/test_intrusive_lru.cc
@@ -0,0 +1,208 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "common/intrusive_lru.h"
+
+template <typename TestLRUItem>
+struct item_to_unsigned {
+ using type = unsigned;
+ const type &operator()(const TestLRUItem &item) {
+ return item.key;
+ }
+};
+
+struct TestLRUItem : public ceph::common::intrusive_lru_base<
+ ceph::common::intrusive_lru_config<
+ unsigned, TestLRUItem, item_to_unsigned<TestLRUItem>>> {
+ unsigned key = 0;
+ int value = 0;
+
+ TestLRUItem(unsigned key) : key(key) {}
+};
+
+class LRUTest : public TestLRUItem::lru_t {
+public:
+ auto add(unsigned int key, int value) {
+ auto [ref, key_existed] = get_or_create(key);
+ if (!key_existed) {
+ ref->value = value;
+ }
+ return std::pair(ref, key_existed);
+ }
+};
+
+TEST(LRU, add_immediate_evict) {
+ LRUTest cache;
+ unsigned int key = 1;
+ int value1 = 2;
+ int value2 = 3;
+ {
+ auto [ref, existed] = cache.add(key, value1);
+ ASSERT_TRUE(ref);
+ ASSERT_EQ(value1, ref->value);
+ ASSERT_FALSE(existed);
+ }
+ {
+ auto [ref2, existed] = cache.add(key, value2);
+ ASSERT_EQ(value2, ref2->value);
+ ASSERT_FALSE(existed);
+ }
+}
+
+TEST(LRU, lookup_lru_size) {
+ LRUTest cache;
+ int key = 1;
+ int value = 1;
+ cache.set_target_size(1);
+ {
+ auto [ref, existed] = cache.add(key, value);
+ ASSERT_TRUE(ref);
+ ASSERT_FALSE(existed);
+ }
+ {
+ auto [ref, existed] = cache.add(key, value);
+ ASSERT_TRUE(ref);
+ ASSERT_TRUE(existed);
+ }
+ cache.set_target_size(0);
+ auto [ref2, existed2] = cache.add(key, value);
+ ASSERT_TRUE(ref2);
+ ASSERT_FALSE(existed2);
+ {
+ auto [ref, existed] = cache.add(key, value);
+ ASSERT_TRUE(ref);
+ ASSERT_TRUE(existed);
+ }
+}
+
+TEST(LRU, eviction) {
+ const unsigned SIZE = 3;
+ LRUTest cache;
+ cache.set_target_size(SIZE);
+
+ for (unsigned i = 0; i < SIZE; ++i) {
+ auto [ref, existed] = cache.add(i, i);
+ ASSERT_TRUE(ref && !existed);
+ }
+
+ {
+ auto [ref, existed] = cache.add(0, 0);
+ ASSERT_TRUE(ref && existed);
+ }
+
+ for (unsigned i = SIZE; i < (2*SIZE) - 1; ++i) {
+ auto [ref, existed] = cache.add(i, i);
+ ASSERT_TRUE(ref && !existed);
+ }
+
+ {
+ auto [ref, existed] = cache.add(0, 0);
+ ASSERT_TRUE(ref && existed);
+ }
+
+ for (unsigned i = 1; i < SIZE; ++i) {
+ auto [ref, existed] = cache.add(i, i);
+ ASSERT_TRUE(ref && !existed);
+ }
+}
+
+TEST(LRU, eviction_live_ref) {
+ const unsigned SIZE = 3;
+ LRUTest cache;
+ cache.set_target_size(SIZE);
+
+ auto [live_ref, existed2] = cache.add(1, 1);
+ ASSERT_TRUE(live_ref && !existed2);
+
+ for (unsigned i = 0; i < SIZE; ++i) {
+ auto [ref, existed] = cache.add(i, i);
+ ASSERT_TRUE(ref);
+ if (i == 1) {
+ ASSERT_TRUE(existed);
+ } else {
+ ASSERT_FALSE(existed);
+ }
+ }
+
+ {
+ auto [ref, existed] = cache.add(0, 0);
+ ASSERT_TRUE(ref && existed);
+ }
+
+ for (unsigned i = SIZE; i < (2*SIZE) - 1; ++i) {
+ auto [ref, existed] = cache.add(i, i);
+ ASSERT_TRUE(ref && !existed);
+ }
+
+ for (unsigned i = 0; i < SIZE; ++i) {
+ auto [ref, existed] = cache.add(i, i);
+ ASSERT_TRUE(ref);
+ if (i == 1) {
+ ASSERT_TRUE(existed);
+ } else {
+ ASSERT_FALSE(existed);
+ }
+ }
+}
+
+TEST(LRU, clear_range) {
+ LRUTest cache;
+ const unsigned SIZE = 10;
+ cache.set_target_size(SIZE);
+ {
+ auto [ref, existed] = cache.add(1, 4);
+ ASSERT_FALSE(existed);
+ }
+ {
+ auto [ref, existed] = cache.add(2, 4);
+ ASSERT_FALSE(existed);
+ }
+ {
+ auto [ref, existed] = cache.add(3, 4);
+ ASSERT_FALSE(existed);
+ }
+ // Unlike above, the reference is not being destroyed
+ auto [live_ref1, existed1] = cache.add(4, 4);
+ ASSERT_FALSE(existed1);
+
+ auto [live_ref2, existed2] = cache.add(5, 4);
+ ASSERT_FALSE(existed2);
+
+ cache.clear_range(0,4);
+
+ // Should not exists (Unreferenced):
+ {
+ auto [ref, existed] = cache.add(1, 4);
+ ASSERT_FALSE(existed);
+ }
+ {
+ auto [ref, existed] = cache.add(2, 4);
+ ASSERT_FALSE(existed);
+ }
+ {
+ auto [ref, existed] = cache.add(3, 4);
+ ASSERT_FALSE(existed);
+ }
+ // Should exist (Still being referenced):
+ {
+ auto [ref, existed] = cache.add(4, 4);
+ ASSERT_TRUE(existed);
+ }
+ // Should exists (Still being referenced and wasn't removed)
+ {
+ auto [ref, existed] = cache.add(5, 4);
+ ASSERT_TRUE(existed);
+ }
+ // Test out of bound deletion:
+ {
+ cache.clear_range(3,8);
+ auto [ref, existed] = cache.add(4, 4);
+ ASSERT_TRUE(existed);
+ }
+ {
+ auto [ref, existed] = cache.add(3, 4);
+ ASSERT_FALSE(existed);
+ }
+}
diff --git a/src/test/common/test_iso_8601.cc b/src/test/common/test_iso_8601.cc
new file mode 100644
index 000000000..e012c99c5
--- /dev/null
+++ b/src/test/common/test_iso_8601.cc
@@ -0,0 +1,60 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat <contact@redhat.com>
+ *
+ * LGPL-2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <chrono>
+
+#include <gtest/gtest.h>
+
+#include "common/ceph_time.h"
+#include "common/iso_8601.h"
+
+using std::chrono::minutes;
+using std::chrono::seconds;
+using std::chrono::time_point_cast;
+
+using ceph::from_iso_8601;
+using ceph::iso_8601_format;
+using ceph::real_clock;
+using ceph::real_time;
+using ceph::to_iso_8601;
+
+TEST(iso_8601, epoch) {
+ const auto epoch = real_clock::from_time_t(0);
+
+ ASSERT_EQ("1970", to_iso_8601(epoch, iso_8601_format::Y));
+ ASSERT_EQ("1970-01", to_iso_8601(epoch, iso_8601_format::YM));
+ ASSERT_EQ("1970-01-01", to_iso_8601(epoch, iso_8601_format::YMD));
+ ASSERT_EQ("1970-01-01T00Z", to_iso_8601(epoch, iso_8601_format::YMDh));
+ ASSERT_EQ("1970-01-01T00:00Z", to_iso_8601(epoch, iso_8601_format::YMDhm));
+ ASSERT_EQ("1970-01-01T00:00:00Z",
+ to_iso_8601(epoch, iso_8601_format::YMDhms));
+ ASSERT_EQ("1970-01-01T00:00:00.000000000Z",
+ to_iso_8601(epoch, iso_8601_format::YMDhmsn));
+
+ ASSERT_EQ(epoch, *from_iso_8601("1970"));
+ ASSERT_EQ(epoch, *from_iso_8601("1970-01"));
+ ASSERT_EQ(epoch, *from_iso_8601("1970-01-01"));
+ ASSERT_EQ(epoch, *from_iso_8601("1970-01-01T00:00Z"));
+ ASSERT_EQ(epoch, *from_iso_8601("1970-01-01T00:00:00Z"));
+ ASSERT_EQ(epoch, *from_iso_8601("1970-01-01T00:00:00.000000000Z"));
+}
+
+TEST(iso_8601, now) {
+ const auto now = real_clock::now();
+
+ ASSERT_EQ(real_time(time_point_cast<minutes>(now)),
+ *from_iso_8601(to_iso_8601(now, iso_8601_format::YMDhm)));
+ ASSERT_EQ(real_time(time_point_cast<seconds>(now)),
+ *from_iso_8601(
+ to_iso_8601(now, iso_8601_format::YMDhms)));
+ ASSERT_EQ(now,
+ *from_iso_8601(
+ to_iso_8601(now, iso_8601_format::YMDhmsn)));
+}
diff --git a/src/test/common/test_journald_logger.cc b/src/test/common/test_journald_logger.cc
new file mode 100644
index 000000000..cf8df6dbc
--- /dev/null
+++ b/src/test/common/test_journald_logger.cc
@@ -0,0 +1,41 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <cerrno>
+#include <gtest/gtest.h>
+#include <sys/stat.h>
+
+#include "common/Journald.h"
+#include "log/Entry.h"
+#include "log/SubsystemMap.h"
+
+using namespace ceph::logging;
+
+class JournaldLoggerTest : public ::testing::Test {
+ protected:
+ SubsystemMap subs;
+ JournaldLogger journald = {&subs};
+ MutableEntry entry = {0, 0};
+
+ void SetUp() override {
+ struct stat buffer;
+ if (stat("/run/systemd/journal/socket", &buffer) < 0) {
+ if (errno == ENOENT) {
+ GTEST_SKIP() << "No journald socket present.";
+ }
+ FAIL() << "Unexpected stat error: " << strerror(errno);
+ }
+ }
+};
+
+TEST_F(JournaldLoggerTest, Log)
+{
+ entry.get_ostream() << "This is a testing regular log message.";
+ EXPECT_EQ(journald.log_entry(entry), 0);
+}
+
+TEST_F(JournaldLoggerTest, VeryLongLog)
+{
+ entry.get_ostream() << std::string(16 * 1024 * 1024, 'a');
+ EXPECT_EQ(journald.log_entry(entry), 0);
+}
diff --git a/src/test/common/test_json_formattable.cc b/src/test/common/test_json_formattable.cc
new file mode 100644
index 000000000..62448e808
--- /dev/null
+++ b/src/test/common/test_json_formattable.cc
@@ -0,0 +1,453 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include "common/ceph_json.h"
+
+#include <sstream>
+
+using namespace std;
+
+
+static void get_jf(const string& s, JSONFormattable *f)
+{
+ JSONParser p;
+ bool result = p.parse(s.c_str(), s.size());
+ if (!result) {
+ cout << "Failed to parse: '" << s << "'" << std::endl;
+ }
+ ASSERT_EQ(true, result);
+ try {
+ decode_json_obj(*f, &p);
+ } catch (JSONDecoder::err& e) {
+ ASSERT_TRUE(0 == "Failed to decode JSON object");
+ }
+}
+
+TEST(formatable, str) {
+ JSONFormattable f;
+ get_jf("{ \"foo\": \"bar\" }", &f);
+ ASSERT_EQ((string)f["foo"], "bar");
+ ASSERT_EQ((string)f["fooz"], "");
+ ASSERT_EQ((string)f["fooz"]("lala"), "lala");
+}
+
+TEST(formatable, str2) {
+ JSONFormattable f;
+ get_jf("{ \"foo\": \"bar\" }", &f);
+ ASSERT_EQ((string)f["foo"], "bar");
+ ASSERT_EQ((string)f["fooz"], "");
+ ASSERT_EQ((string)f["fooz"]("lala"), "lala");
+
+ JSONFormattable f2;
+ get_jf("{ \"foo\": \"bar\", \"fooz\": \"zzz\" }", &f2);
+ ASSERT_EQ((string)f2["foo"], "bar");
+ ASSERT_NE((string)f2["fooz"], "");
+ ASSERT_EQ((string)f2["fooz"], "zzz");
+ ASSERT_EQ((string)f2["fooz"]("lala"), "zzz");
+
+}
+
+TEST(formatable, str3) {
+ JSONFormattable f;
+ get_jf("{ \"foo\": \"1234bar56\" }", &f);
+ ASSERT_EQ((string)f["foo"], "1234bar56");
+}
+
+TEST(formatable, int) {
+ JSONFormattable f;
+ get_jf("{ \"foo\": 1 }", &f);
+ ASSERT_EQ((int)f["foo"], 1);
+ ASSERT_EQ((int)f["fooz"], 0);
+ ASSERT_EQ((int)f["fooz"](3), 3);
+
+ JSONFormattable f2;
+ get_jf("{ \"foo\": \"bar\", \"fooz\": \"123\" }", &f2);
+ ASSERT_EQ((string)f2["foo"], "bar");
+ ASSERT_NE((int)f2["fooz"], 0);
+ ASSERT_EQ((int)f2["fooz"], 123);
+ ASSERT_EQ((int)f2["fooz"](111), 123);
+}
+
+TEST(formatable, bool) {
+ JSONFormattable f;
+ get_jf("{ \"foo\": \"true\" }", &f);
+ ASSERT_EQ((bool)f["foo"], true);
+ ASSERT_EQ((bool)f["fooz"], false);
+ ASSERT_EQ((bool)f["fooz"](true), true);
+
+ JSONFormattable f2;
+ get_jf("{ \"foo\": \"false\" }", &f);
+ ASSERT_EQ((bool)f["foo"], false);
+}
+
+TEST(formatable, nested) {
+ JSONFormattable f;
+ get_jf("{ \"obj\": { \"foo\": 1, \"inobj\": { \"foo\": 2 } } }", &f);
+ ASSERT_EQ((int)f["foo"], 0);
+ ASSERT_EQ((int)f["obj"]["foo"], 1);
+ ASSERT_EQ((int)f["obj"]["inobj"]["foo"], 2);
+}
+
+TEST(formatable, array) {
+ JSONFormattable f;
+ get_jf("{ \"arr\": [ { \"foo\": 1, \"inobj\": { \"foo\": 2 } },"
+ "{ \"foo\": 2 } ] }", &f);
+
+ int i = 1;
+ for (auto a : f.array()) {
+ ASSERT_EQ((int)a["foo"], i);
+ ++i;
+ }
+
+ JSONFormattable f2;
+ get_jf("{ \"arr\": [ 0, 1, 2, 3, 4 ]}", &f2);
+
+ i = 0;
+ for (auto a : f2.array()) {
+ ASSERT_EQ((int)a, i);
+ ++i;
+ }
+}
+
+TEST(formatable, bin_encode) {
+ JSONFormattable f, f2;
+ get_jf("{ \"arr\": [ { \"foo\": 1, \"bar\": \"aaa\", \"inobj\": { \"foo\": 2 } },"
+ "{ \"foo\": 2, \"inobj\": { \"foo\": 3 } } ] }", &f);
+
+ int i = 1;
+ for (auto a : f.array()) {
+ ASSERT_EQ((int)a["foo"], i);
+ ASSERT_EQ((int)a["foo"]["inobj"], i + 1);
+ ASSERT_EQ((string)a["bar"], "aaa");
+ ++i;
+ }
+
+ bufferlist bl;
+ ::encode(f, bl);
+ auto iter = bl.cbegin();
+ try {
+ ::decode(f2, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0 == "Failed to decode object");
+ }
+
+ i = 1;
+ for (auto a : f2.array()) {
+ ASSERT_EQ((int)a["foo"], i);
+ ASSERT_EQ((int)a["foo"]["inobj"], i + 1);
+ ASSERT_EQ((string)a["bar"], "aaa");
+ ++i;
+ }
+
+}
+
+TEST(formatable, json_encode) {
+ JSONFormattable f, f2;
+ get_jf("{ \"arr\": [ { \"foo\": 1, \"bar\": \"aaa\", \"inobj\": { \"foo\": 2 } },"
+ "{ \"foo\": 2, \"inobj\": { \"foo\": 3 } } ] }", &f);
+
+ JSONFormatter formatter;
+ formatter.open_object_section("bla");
+ ::encode_json("f", f, &formatter);
+ formatter.close_section();
+
+ stringstream ss;
+ formatter.flush(ss);
+
+ get_jf(ss.str(), &f2);
+
+ int i = 1;
+ for (auto a : f2.array()) {
+ ASSERT_EQ((int)a["foo"], i);
+ ASSERT_EQ((int)a["foo"]["inobj"], i + 1);
+ ASSERT_EQ((string)a["bar"], "aaa");
+ ++i;
+ }
+
+}
+
+TEST(formatable, set) {
+ JSONFormattable f, f2;
+
+ f.set("", "{ \"abc\": \"xyz\"}");
+ ASSERT_EQ((string)f["abc"], "xyz");
+
+ f.set("aaa", "111");
+ ASSERT_EQ((string)f["abc"], "xyz");
+ ASSERT_EQ((int)f["aaa"], 111);
+
+ f.set("obj", "{ \"a\": \"10\", \"b\": \"20\"}");
+ ASSERT_EQ((int)f["obj"]["a"], 10);
+ ASSERT_EQ((int)f["obj"]["b"], 20);
+
+ f.set("obj.c", "30");
+
+ ASSERT_EQ((int)f["obj"]["c"], 30);
+}
+
+TEST(formatable, set2) {
+ JSONFormattable f;
+ f.set("foo", "1234bar56");
+ ASSERT_EQ((string)f["foo"], "1234bar56");
+}
+
+TEST(formatable, erase) {
+ JSONFormattable f, f2;
+
+ f.set("", "{ \"abc\": \"xyz\"}");
+ ASSERT_EQ((string)f["abc"], "xyz");
+
+ f.set("aaa", "111");
+ ASSERT_EQ((string)f["abc"], "xyz");
+ ASSERT_EQ((int)f["aaa"], 111);
+ f.erase("aaa");
+ ASSERT_EQ((int)f["aaa"], 0);
+
+ f.set("obj", "{ \"a\": \"10\", \"b\": \"20\"}");
+ ASSERT_EQ((int)f["obj"]["a"], 10);
+ ASSERT_EQ((int)f["obj"]["b"], 20);
+ f.erase("obj.a");
+ ASSERT_EQ((int)f["obj"]["a"], 0);
+ ASSERT_EQ((int)f["obj"]["b"], 20);
+}
+
+template <class T>
+static void dumpt(const T& t, const char *n)
+{
+ JSONFormatter formatter;
+ formatter.open_object_section("bla");
+ ::encode_json(n, t, &formatter);
+ formatter.close_section();
+ formatter.flush(cout);
+}
+
+static void dumpf(const JSONFormattable& f) {
+ dumpt(f, "f");
+}
+
+TEST(formatable, set_array) {
+ JSONFormattable f, f2;
+
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_EQ(1u, f["asd"].array().size());
+ ASSERT_EQ((string)f["asd"][0], "xyz");
+
+ f.set("bbb[0][0]", "10");
+ f.set("bbb[0][1]", "20");
+ ASSERT_EQ(1u, f["bbb"].array().size());
+ ASSERT_EQ(2u, f["bbb"][0].array().size());
+ ASSERT_EQ("10", (string)f["bbb"][0][0]);
+ ASSERT_EQ(20, (int)f["bbb"][0][1]);
+ f.set("bbb[0][1]", "25");
+ ASSERT_EQ(2u, f["bbb"][0].array().size());
+ ASSERT_EQ(25, (int)f["bbb"][0][1]);
+
+ f.set("bbb[0][]", "26"); /* append operation */
+ ASSERT_EQ(26, (int)f["bbb"][0][2]);
+ f.set("bbb[0][-1]", "27"); /* replace last */
+ ASSERT_EQ(27, (int)f["bbb"][0][2]);
+ ASSERT_EQ(3u, f["bbb"][0].array().size());
+
+ f.set("foo.asd[0][0]", "{ \"field\": \"xyz\"}");
+ ASSERT_EQ((string)f["foo"]["asd"][0][0]["field"], "xyz");
+
+ ASSERT_EQ(f.set("foo[0]", "\"zzz\""), -EINVAL); /* can't assign array to an obj entity */
+
+ f2.set("[0]", "{ \"field\": \"xyz\"}");
+ ASSERT_EQ((string)f2[0]["field"], "xyz");
+}
+
+TEST(formatable, erase_array) {
+ JSONFormattable f;
+
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_EQ(1u, f["asd"].array().size());
+ ASSERT_EQ("xyz", (string)f["asd"][0]);
+ ASSERT_TRUE(f["asd"].exists(0));
+ f.erase("asd[0]");
+ ASSERT_FALSE(f["asd"].exists(0));
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_TRUE(f["asd"].exists(0));
+ f["asd"].erase("[0]");
+ ASSERT_FALSE(f["asd"].exists(0));
+ f.set("asd[0]", "\"xyz\"");
+ ASSERT_TRUE(f["asd"].exists(0));
+ f.erase("asd");
+ ASSERT_FALSE(f["asd"].exists(0));
+ ASSERT_FALSE(f.exists("asd"));
+
+ f.set("bbb[]", "10");
+ f.set("bbb[]", "20");
+ f.set("bbb[]", "30");
+ ASSERT_EQ((int)f["bbb"][0], 10);
+ ASSERT_EQ((int)f["bbb"][1], 20);
+ ASSERT_EQ((int)f["bbb"][2], 30);
+ f.erase("bbb[-2]");
+ ASSERT_FALSE(f.exists("bbb[2]"));
+
+ ASSERT_EQ((int)f["bbb"][0], 10);
+ ASSERT_EQ((int)f["bbb"][1], 30);
+
+ if (0) { /* for debugging when needed */
+ dumpf(f);
+ }
+}
+
+void formatter_convert(JSONFormatter& formatter, JSONFormattable *dest)
+{
+ stringstream ss;
+ formatter.flush(ss);
+ get_jf(ss.str(), dest);
+}
+
+TEST(formatable, encode_simple) {
+ JSONFormattable f;
+
+ encode_json("foo", "bar", &f);
+
+ ASSERT_EQ((string)f["foo"], "bar");
+
+
+ JSONFormatter formatter;
+ {
+ Formatter::ObjectSection s(formatter, "os");
+ encode_json("f", f, &formatter);
+ }
+
+ JSONFormattable jf2;
+ formatter_convert(formatter, &jf2);
+
+ ASSERT_EQ((string)jf2["f"]["foo"], "bar");
+}
+
+
+struct struct1 {
+ long i;
+ string s;
+ bool b;
+
+ struct1() {
+ void *p = (void *)this;
+ i = (long)p;
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%p", p);
+ s = buf;
+ b = (bool)(i % 2);
+ }
+
+ void dump(Formatter *f) const {
+ encode_json("i", i, f);
+ encode_json("s", s, f);
+ encode_json("b", b, f);
+ }
+
+ void decode_json(JSONObj *obj) {
+ JSONDecoder::decode_json("i", i, obj);
+ JSONDecoder::decode_json("s", s, obj);
+ JSONDecoder::decode_json("b", b, obj);
+ }
+
+ bool compare(const JSONFormattable& jf) const {
+ bool ret = (s == (string)jf["s"] &&
+ i == (long)jf["i"] &&
+ b == (bool)jf["b"]);
+
+ if (!ret) {
+ cout << "failed comparison: s=" << s << " jf[s]=" << (string)jf["s"] <<
+ " i=" << i << " jf[i]=" << (long)jf["i"] << " b=" << b << " jf[b]=" << (bool)jf["b"] << std::endl;
+ dumpf(jf);
+ }
+
+ return ret;
+ }
+};
+
+
+struct struct2 {
+ struct1 s1;
+ vector<struct1> v;
+
+ struct2() {
+ void *p = (void *)this;
+ long i = (long)p;
+ v.resize((i >> 16) % 16 + 1);
+ }
+
+ void dump(Formatter *f) const {
+ encode_json("s1", s1, f);
+ encode_json("v", v, f);
+ }
+
+ void decode_json(JSONObj *obj) {
+ JSONDecoder::decode_json("s1", s1, obj);
+ JSONDecoder::decode_json("v", v, obj);
+ }
+
+ bool compare(const JSONFormattable& jf) const {
+ if (!s1.compare(jf["s1"])) {
+ cout << "s1.compare(jf[s1] failed" << std::endl;
+ return false;
+ }
+
+ if (v.size() != jf["v"].array().size()) {
+ cout << "v.size()=" << v.size() << " jf[v].array().size()=" << jf["v"].array().size() << std::endl;
+ return false;
+ }
+
+ auto viter = v.begin();
+ auto jiter = jf["v"].array().begin();
+
+ for (; viter != v.end(); ++viter, ++jiter) {
+ if (!viter->compare(*jiter)) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+
+TEST(formatable, encode_struct) {
+ JSONFormattable f;
+
+ struct2 s2;
+
+ {
+ Formatter::ObjectSection s(f, "section");
+ encode_json("foo", "bar", &f);
+ encode_json("s2", s2, &f);
+ }
+
+ dumpt(s2, "s2");
+ cout << std::endl;
+ cout << std::endl;
+
+ ASSERT_EQ((string)f["foo"], "bar");
+ ASSERT_TRUE(s2.compare(f["s2"]));
+
+
+ JSONFormatter formatter;
+ encode_json("f", f, &formatter);
+
+ JSONFormattable jf2;
+
+ formatter_convert(formatter, &jf2);
+
+ ASSERT_EQ((string)jf2["foo"], "bar");
+ ASSERT_TRUE(s2.compare(jf2["s2"]));
+}
+
diff --git a/src/test/common/test_json_formatter.cc b/src/test/common/test_json_formatter.cc
new file mode 100644
index 000000000..8a0f547a9
--- /dev/null
+++ b/src/test/common/test_json_formatter.cc
@@ -0,0 +1,81 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include "common/ceph_json.h"
+#include "common/Clock.h"
+
+#include <sstream>
+
+using namespace std;
+
+
+TEST(formatter, bug_37706) {
+ vector<std::string> pgs;
+
+ string outstring =
+"{\"pg_ready\":true, \"pg_stats\":[ { \"pgid\":\"1.0\", \"version\":\"16'56\",\"reported_seq\":\"62\",\"reported_epoch\":\"20\",\"state\":\"active+clean+inconsistent\",\"last_fresh\":\"2018-12-18 15:21:22.173804\",\"last_change\":\"2018-12-18 15:21:22.173804\",\"last_active\":\"2018-12-18 15:21:22.173804\",\"last_peered\":\"2018-12-18 15:21:22.173804\",\"last_clean\":\"2018-12-18 15:21:22.173804\",\"last_became_active\":\"2018-12-18 15:21:21.347685\",\"last_became_peered\":\"2018-12-18 15:21:21.347685\",\"last_unstale\":\"2018-12-18 15:21:22.173804\",\"last_undegraded\":\"2018-12-18 15:21:22.173804\",\"last_fullsized\":\"2018-12-18 15:21:22.173804\",\"mapping_epoch\":19,\"log_start\":\"0'0\",\"ondisk_log_start\":\"0'0\",\"created\":7,\"last_epoch_clean\":20,\"parent\":\"0.0\",\"parent_split_bits\":0,\"last_scrub\":\"16'56\",\"last_scrub_stamp\":\"2018-12-18 15:21:22.173684\",\"last_deep_scrub\":\"0'0\",\"last_deep_scrub_stamp\":\"2018-12-18 15:21:06.514438\",\"last_clean_scrub_stamp\":\"2018-12-18 15:21:06.514438\",\"log_size\":56,\"ondisk_log_size\":56,\"stats_invalid\":false,\"dirty_stats_invalid\":false,\"omap_stats_invalid\":false,\"hitset_stats_invalid\":false,\"hitset_bytes_stats_invalid\":false,\"pin_stats_invalid\":false,\"manifest_stats_invalid\":false,\"snaptrimq_len\":0,\"stat_sum\":{\"num_bytes\":24448,\"num_objects\":36,\"num_object_clones\":20,\"num_object_copies\":36,\"num_objects_missing_on_primary\":0,\"num_objects_missing\":0,\"num_objects_degraded\":0,\"num_objects_misplaced\":0,\"num_objects_unfound\":0,\"num_objects_dirty\":36,\"num_whiteouts\":3,\"num_read\":0,\"num_read_kb\":0,\"num_write\":36,\"num_write_kb\":50,\"num_scrub_errors\":20,\"num_shallow_scrub_errors\":20,\"num_deep_scrub_errors\":0,\"num_objects_recovered\":0,\"num_bytes_recovered\":0,\"num_keys_recovered\":0,\"num_objects_omap\":0,\"num_objects_hit_set_archive\":0,\"num_bytes_hit_set_archive\":0,\"num_flush\":0,\"num_flush_kb\":0,\"num_evict\":0,\"num_evict_kb\":0,\"num_promote\":0,\"num_flush_mode_high\":0,\"num_flush_mode_low\":0,\"num_evict_mode_some\":0,\"num_evict_mode_full\":0,\"num_objects_pinned\":0,\"num_legacy_snapsets\":0,\"num_large_omap_objects\":0,\"num_objects_manifest\":0},\"up\":[0],\"acting\":[0],\"blocked_by\":[],\"up_primary\":0,\"acting_primary\":0,\"purged_snaps\":[] }]}";
+
+
+ JSONParser parser;
+ ASSERT_TRUE(parser.parse(outstring.c_str(), outstring.size()));
+
+ vector<string> v;
+
+ ASSERT_TRUE (!parser.is_array());
+
+ JSONObj *pgstat_obj = parser.find_obj("pg_stats");
+ ASSERT_TRUE (!!pgstat_obj);
+ auto s = pgstat_obj->get_data();
+
+ ASSERT_TRUE(!s.empty());
+ JSONParser pg_stats;
+ ASSERT_TRUE(pg_stats.parse(s.c_str(), s.length()));
+ v = pg_stats.get_array_elements();
+
+ for (auto i : v) {
+ JSONParser pg_json;
+ ASSERT_TRUE(pg_json.parse(i.c_str(), i.length()));
+ string pgid;
+ JSONDecoder::decode_json("pgid", pgid, &pg_json);
+ pgs.emplace_back(std::move(pgid));
+ }
+
+ ASSERT_EQ(pgs.back(), "1.0");
+}
+
+TEST(formatter, utime)
+{
+ JSONFormatter formatter;
+
+ utime_t input = ceph_clock_now();
+ input.gmtime_nsec(formatter.dump_stream("timestamp"));
+
+ bufferlist bl;
+ formatter.flush(bl);
+
+ JSONParser parser;
+ EXPECT_TRUE(parser.parse(bl.c_str(), bl.length()));
+
+ cout << input << " -> '" << std::string(bl.c_str(), bl.length())
+ << std::endl;
+
+ utime_t output;
+ decode_json_obj(output, &parser);
+ cout << " -> " << output << std::endl;
+ EXPECT_EQ(input.sec(), output.sec());
+ EXPECT_EQ(input.nsec(), output.nsec());
+}
diff --git a/src/test/common/test_lockdep.cc b/src/test/common/test_lockdep.cc
new file mode 100644
index 000000000..7a5ab6f1e
--- /dev/null
+++ b/src/test/common/test_lockdep.cc
@@ -0,0 +1,74 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "gtest/gtest.h"
+
+#include "common/ceph_argparse.h"
+#include "common/ceph_context.h"
+#include "common/ceph_mutex.h"
+#include "common/common_init.h"
+#include "common/lockdep.h"
+#include "include/util.h"
+#include "include/coredumpctl.h"
+#include "log/Log.h"
+
+class lockdep : public ::testing::Test
+{
+protected:
+ void SetUp() override {
+#ifndef CEPH_DEBUG_MUTEX
+ GTEST_SKIP() << "WARNING: CEPH_DEBUG_MUTEX is not defined, lockdep will not work";
+#endif
+ CephInitParameters params(CEPH_ENTITY_TYPE_CLIENT);
+ cct = common_preinit(params, CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ cct->_conf->cluster = "ceph";
+ cct->_conf.set_val("lockdep", "true");
+ cct->_conf.apply_changes(nullptr);
+ ASSERT_TRUE(g_lockdep);
+ }
+ void TearDown() final
+ {
+ if (cct) {
+ cct->put();
+ cct = nullptr;
+ }
+ }
+protected:
+ CephContext *cct = nullptr;
+};
+
+TEST_F(lockdep, abba)
+{
+ ceph::mutex a(ceph::make_mutex("a")), b(ceph::make_mutex("b"));
+ a.lock();
+ ASSERT_TRUE(ceph_mutex_is_locked(a));
+ ASSERT_TRUE(ceph_mutex_is_locked_by_me(a));
+ b.lock();
+ ASSERT_TRUE(ceph_mutex_is_locked(b));
+ ASSERT_TRUE(ceph_mutex_is_locked_by_me(b));
+ a.unlock();
+ b.unlock();
+
+ b.lock();
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(a.lock(), "");
+ b.unlock();
+}
+
+TEST_F(lockdep, recursive)
+{
+ ceph::mutex a(ceph::make_mutex("a"));
+ a.lock();
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(a.lock(), "");
+ a.unlock();
+
+ ceph::recursive_mutex b(ceph::make_recursive_mutex("b"));
+ b.lock();
+ ASSERT_TRUE(ceph_mutex_is_locked(b));
+ ASSERT_TRUE(ceph_mutex_is_locked_by_me(b));
+ b.lock();
+ b.unlock();
+ b.unlock();
+}
diff --git a/src/test/common/test_lru.cc b/src/test/common/test_lru.cc
new file mode 100644
index 000000000..29f32aac3
--- /dev/null
+++ b/src/test/common/test_lru.cc
@@ -0,0 +1,158 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Sahid Orentino Ferdjaoui <sahid.ferdjaoui@cloudwatt.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include "include/lru.h"
+
+
+class Item : public LRUObject {
+public:
+ int id;
+ Item() : id(0) {}
+ explicit Item(int i) : id(i) {}
+ void set(int i) {id = i;}
+};
+
+
+TEST(lru, InsertTop) {
+ LRU lru;
+ static const int n = 100;
+ Item items[n];
+
+ lru.lru_set_midpoint(.5); // 50% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_top(&items[i]);
+ }
+ ASSERT_EQ(50U, lru.lru_get_top());
+ ASSERT_EQ(50U, lru.lru_get_bot());
+ ASSERT_EQ(100U, lru.lru_get_size());
+
+ ASSERT_EQ(0, (static_cast<Item*>(lru.lru_expire()))->id);
+}
+
+TEST(lru, InsertMid) {
+ LRU lru;
+ static const int n = 102;
+ Item items[n];
+
+ lru.lru_set_midpoint(.7); // 70% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_mid(&items[i]);
+ }
+ ASSERT_EQ(71U, lru.lru_get_top());
+ ASSERT_EQ(31U, lru.lru_get_bot());
+ ASSERT_EQ(102U, lru.lru_get_size());
+
+ ASSERT_EQ(0, (static_cast<Item*>(lru.lru_expire()))->id);
+}
+
+TEST(lru, InsertBot) {
+ LRU lru;
+ static const int n = 100;
+ Item items[n];
+
+ lru.lru_set_midpoint(.7); // 70% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_bot(&items[i]);
+ }
+ ASSERT_EQ(70U, lru.lru_get_top());
+ ASSERT_EQ(30U, lru.lru_get_bot());
+ ASSERT_EQ(100U, lru.lru_get_size());
+
+ ASSERT_EQ(99, (static_cast<Item*>(lru.lru_expire()))->id);
+}
+
+TEST(lru, Adjust) {
+ LRU lru;
+ static const int n = 100;
+ Item items[n];
+
+ lru.lru_set_midpoint(.6); // 60% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_top(&items[i]);
+ if (i % 5 == 0)
+ items[i].lru_pin();
+ }
+ ASSERT_EQ(48U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(52U, lru.lru_get_bot());
+ ASSERT_EQ(100U, lru.lru_get_size());
+
+ ASSERT_EQ(1, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(1U, lru.lru_get_pintail());
+ ASSERT_EQ(47U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(51U, lru.lru_get_bot());
+ ASSERT_EQ(99U, lru.lru_get_size());
+ ASSERT_EQ(2, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(1U, lru.lru_get_pintail());
+ ASSERT_EQ(46U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(51U, lru.lru_get_bot());
+ ASSERT_EQ(98U, lru.lru_get_size());
+ ASSERT_EQ(3, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(4, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(6, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(2U, lru.lru_get_pintail());
+ ASSERT_EQ(45U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(48U, lru.lru_get_bot());
+ ASSERT_EQ(95U, lru.lru_get_size());
+}
+
+TEST(lru, Pinning) {
+ LRU lru;
+
+ Item ob0(0), ob1(1);
+
+ // test before ob1 are in a LRU
+ ob1.lru_pin();
+ ASSERT_FALSE(ob1.lru_is_expireable());
+
+ ob1.lru_unpin();
+ ASSERT_TRUE(ob1.lru_is_expireable());
+
+ // test when ob1 are in a LRU
+ lru.lru_insert_top(&ob0);
+ lru.lru_insert_top(&ob1);
+
+ ob1.lru_pin();
+ ob1.lru_pin(); // Verify that, one incr.
+ ASSERT_EQ(1U, lru.lru_get_num_pinned());
+ ASSERT_FALSE(ob1.lru_is_expireable());
+
+ ob1.lru_unpin();
+ ob1.lru_unpin(); // Verify that, one decr.
+ ASSERT_EQ(0U, lru.lru_get_num_pinned());
+ ASSERT_TRUE(ob1.lru_is_expireable());
+
+ ASSERT_EQ(0, (static_cast<Item*>(lru.lru_expire()))->id);
+ ob0.lru_pin();
+ ASSERT_EQ(1, (static_cast<Item*>(lru.lru_expire()))->id);
+}
+
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_lru &&
+ * valgrind --tool=memcheck --leak-check=full \
+ * ./unittest_lru
+ * "
+ * End:
+ */
diff --git a/src/test/common/test_lruset.cc b/src/test/common/test_lruset.cc
new file mode 100644
index 000000000..64e823e2f
--- /dev/null
+++ b/src/test/common/test_lruset.cc
@@ -0,0 +1,109 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank <info@inktank.com>
+ *
+ * LGPL-2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "common/LRUSet.h"
+
+struct thing {
+ int a;
+ thing(int i) : a(i) {}
+ friend bool operator==(const thing &a, const thing &b) {
+ return a.a == b.a;
+ }
+ friend std::size_t hash_value(const thing &value) {
+ return value.a;
+ }
+};
+
+namespace std {
+ template<> struct hash<thing> {
+ size_t operator()(const thing& r) const {
+ return r.a;
+ }
+ };
+}
+
+
+TEST(LRUSet, insert_complex) {
+ LRUSet<thing> s;
+ s.insert(thing(1));
+ s.insert(thing(2));
+
+ ASSERT_TRUE(s.contains(thing(1)));
+ ASSERT_TRUE(s.contains(thing(2)));
+ ASSERT_FALSE(s.contains(thing(3)));
+}
+
+TEST(LRUSet, insert) {
+ LRUSet<int> s;
+ s.insert(1);
+ s.insert(2);
+
+ ASSERT_TRUE(s.contains(1));
+ ASSERT_TRUE(s.contains(2));
+ ASSERT_FALSE(s.contains(3));
+}
+
+TEST(LRUSet, erase) {
+ LRUSet<int> s;
+ s.insert(1);
+ s.insert(2);
+ s.insert(3);
+
+ s.erase(2);
+ ASSERT_TRUE(s.contains(1));
+ ASSERT_FALSE(s.contains(2));
+ ASSERT_TRUE(s.contains(3));
+ s.prune(1);
+ ASSERT_TRUE(s.contains(3));
+ ASSERT_FALSE(s.contains(1));
+}
+
+TEST(LRUSet, prune) {
+ LRUSet<int> s;
+ int max = 1000;
+ for (int i=0; i<max; ++i) {
+ s.insert(i);
+ s.prune(max / 10);
+ }
+ s.prune(0);
+ ASSERT_TRUE(s.empty());
+}
+
+TEST(LRUSet, lru) {
+ LRUSet<int> s;
+ s.insert(1);
+ s.insert(2);
+ s.insert(3);
+ s.prune(2);
+ ASSERT_FALSE(s.contains(1));
+ ASSERT_TRUE(s.contains(2));
+ ASSERT_TRUE(s.contains(3));
+
+ s.insert(2);
+ s.insert(4);
+ s.prune(2);
+ ASSERT_FALSE(s.contains(3));
+ ASSERT_TRUE(s.contains(2));
+ ASSERT_TRUE(s.contains(4));
+}
+
+TEST(LRUSet, copy) {
+ LRUSet<int> a, b;
+ a.insert(1);
+ b.insert(2);
+ b.insert(3);
+ a = b;
+ ASSERT_FALSE(a.contains(1));
+ ASSERT_TRUE(a.contains(2));
+ ASSERT_TRUE(a.contains(3));
+}
diff --git a/src/test/common/test_mclock_priority_queue.cc b/src/test/common/test_mclock_priority_queue.cc
new file mode 100644
index 000000000..8e8bcdf38
--- /dev/null
+++ b/src/test/common/test_mclock_priority_queue.cc
@@ -0,0 +1,320 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <thread>
+#include <chrono>
+#include <iostream>
+#include "gtest/gtest.h"
+#include "common/mClockPriorityQueue.h"
+
+
+struct Request {
+ int value;
+ Request() :
+ value(0)
+ {}
+ Request(const Request& o) = default;
+ explicit Request(int value) :
+ value(value)
+ {}
+};
+
+
+struct Client {
+ int client_num;
+ Client() :
+ Client(-1)
+ {}
+ Client(int client_num) :
+ client_num(client_num)
+ {}
+ friend bool operator<(const Client& r1, const Client& r2) {
+ return r1.client_num < r2.client_num;
+ }
+ friend bool operator==(const Client& r1, const Client& r2) {
+ return r1.client_num == r2.client_num;
+ }
+};
+
+
+const crimson::dmclock::ClientInfo* client_info_func(const Client& c) {
+ static const crimson::dmclock::ClientInfo
+ the_info(10.0, 10.0, 10.0);
+ return &the_info;
+}
+
+
+TEST(mClockPriorityQueue, Create)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+}
+
+
+TEST(mClockPriorityQueue, Sizes)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+
+ ASSERT_TRUE(q.empty());
+ ASSERT_EQ(0u, q.get_size_slow());
+
+ Client c1(1);
+ Client c2(2);
+
+ q.enqueue_strict(c1, 1, Request(1));
+ q.enqueue_strict(c2, 2, Request(2));
+ q.enqueue_strict(c1, 2, Request(3));
+ q.enqueue(c2, 1, 1u, Request(4));
+ q.enqueue(c1, 2, 1u, Request(5));
+ q.enqueue_strict(c2, 1, Request(6));
+
+ ASSERT_FALSE(q.empty());
+ ASSERT_EQ(6u, q.get_size_slow());
+
+
+ for (int i = 0; i < 6; ++i) {
+ (void) q.dequeue();
+ }
+
+ ASSERT_TRUE(q.empty());
+ ASSERT_EQ(0u, q.get_size_slow());
+}
+
+
+TEST(mClockPriorityQueue, JustStrict)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+
+ Client c1(1);
+ Client c2(2);
+
+ q.enqueue_strict(c1, 1, Request(1));
+ q.enqueue_strict(c2, 2, Request(2));
+ q.enqueue_strict(c1, 2, Request(3));
+ q.enqueue_strict(c2, 1, Request(4));
+
+ Request r;
+
+ r = q.dequeue();
+ ASSERT_EQ(2, r.value);
+ r = q.dequeue();
+ ASSERT_EQ(3, r.value);
+ r = q.dequeue();
+ ASSERT_EQ(1, r.value);
+ r = q.dequeue();
+ ASSERT_EQ(4, r.value);
+}
+
+
+TEST(mClockPriorityQueue, StrictPriorities)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+
+ Client c1(1);
+ Client c2(2);
+
+ q.enqueue_strict(c1, 1, Request(1));
+ q.enqueue_strict(c2, 2, Request(2));
+ q.enqueue_strict(c1, 3, Request(3));
+ q.enqueue_strict(c2, 4, Request(4));
+
+ Request r;
+
+ r = q.dequeue();
+ ASSERT_EQ(4, r.value);
+ r = q.dequeue();
+ ASSERT_EQ(3, r.value);
+ r = q.dequeue();
+ ASSERT_EQ(2, r.value);
+ r = q.dequeue();
+ ASSERT_EQ(1, r.value);
+}
+
+
+TEST(mClockPriorityQueue, JustNotStrict)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+
+ Client c1(1);
+ Client c2(2);
+
+ // non-strict queue ignores priorites, but will divide between
+ // clients evenly and maintain orders between clients
+ q.enqueue(c1, 1, 1u, Request(1));
+ q.enqueue(c1, 2, 1u, Request(2));
+ q.enqueue(c2, 3, 1u, Request(3));
+ q.enqueue(c2, 4, 1u, Request(4));
+
+ Request r1, r2;
+
+ r1 = q.dequeue();
+ ASSERT_TRUE(1 == r1.value || 3 == r1.value);
+
+ r2 = q.dequeue();
+ ASSERT_TRUE(1 == r2.value || 3 == r2.value);
+
+ ASSERT_NE(r1.value, r2.value);
+
+ r1 = q.dequeue();
+ ASSERT_TRUE(2 == r1.value || 4 == r1.value);
+
+ r2 = q.dequeue();
+ ASSERT_TRUE(2 == r2.value || 4 == r2.value);
+
+ ASSERT_NE(r1.value, r2.value);
+}
+
+
+TEST(mClockPriorityQueue, EnqueuFront)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+
+ Client c1(1);
+ Client c2(2);
+
+ // non-strict queue ignores priorites, but will divide between
+ // clients evenly and maintain orders between clients
+ q.enqueue(c1, 1, 1u, Request(1));
+ q.enqueue(c1, 2, 1u, Request(2));
+ q.enqueue(c2, 3, 1u, Request(3));
+ q.enqueue(c2, 4, 1u, Request(4));
+ q.enqueue_strict(c2, 6, Request(6));
+ q.enqueue_strict(c1, 7, Request(7));
+
+ std::list<Request> reqs;
+
+ for (uint i = 0; i < 4; ++i) {
+ reqs.emplace_back(q.dequeue());
+ }
+
+ for (uint i = 0; i < 4; ++i) {
+ Request& r = reqs.front();
+ if (r.value > 5) {
+ q.enqueue_strict_front(r.value == 6 ? c2 : 1, r.value, std::move(r));
+ } else {
+ q.enqueue_front(r.value <= 2 ? c1 : c2, r.value, 0, std::move(r));
+ }
+ reqs.pop_front();
+ }
+
+ Request r;
+
+ r = q.dequeue();
+ ASSERT_EQ(7, r.value);
+
+ r = q.dequeue();
+ ASSERT_EQ(6, r.value);
+
+ r = q.dequeue();
+ ASSERT_TRUE(1 == r.value || 3 == r.value);
+
+ r = q.dequeue();
+ ASSERT_TRUE(1 == r.value || 3 == r.value);
+
+ r = q.dequeue();
+ ASSERT_TRUE(2 == r.value || 4 == r.value);
+
+ r = q.dequeue();
+ ASSERT_TRUE(2 == r.value || 4 == r.value);
+}
+
+
+TEST(mClockPriorityQueue, RemoveByClass)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+
+ Client c1(1);
+ Client c2(2);
+ Client c3(3);
+
+ q.enqueue(c1, 1, 1u, Request(1));
+ q.enqueue(c2, 1, 1u, Request(2));
+ q.enqueue(c3, 1, 1u, Request(4));
+ q.enqueue_strict(c1, 2, Request(8));
+ q.enqueue_strict(c2, 1, Request(16));
+ q.enqueue_strict(c3, 3, Request(32));
+ q.enqueue(c3, 1, 1u, Request(64));
+ q.enqueue(c2, 1, 1u, Request(128));
+ q.enqueue(c1, 1, 1u, Request(256));
+
+ int out_mask = 2 | 16 | 128;
+ int in_mask = 1 | 8 | 256;
+
+ std::list<Request> out;
+ q.remove_by_class(c2, &out);
+
+ ASSERT_EQ(3u, out.size());
+ while (!out.empty()) {
+ ASSERT_TRUE((out.front().value & out_mask) > 0) <<
+ "had value that was not expected after first removal";
+ out.pop_front();
+ }
+
+ ASSERT_EQ(6u, q.get_size_slow()) << "after removal of three from client c2";
+
+ q.remove_by_class(c3);
+
+ ASSERT_EQ(3u, q.get_size_slow()) << "after removal of three from client c3";
+ while (!q.empty()) {
+ Request r = q.dequeue();
+ ASSERT_TRUE((r.value & in_mask) > 0) <<
+ "had value that was not expected after two removals";
+ }
+}
+
+
+TEST(mClockPriorityQueue, RemoveByFilter)
+{
+ ceph::mClockQueue<Request,Client> q(&client_info_func);
+
+ Client c1(1);
+ Client c2(2);
+ Client c3(3);
+
+ q.enqueue(c1, 1, 1u, Request(1));
+ q.enqueue(c2, 1, 1u, Request(2));
+ q.enqueue(c3, 1, 1u, Request(3));
+ q.enqueue_strict(c1, 2, Request(4));
+ q.enqueue_strict(c2, 1, Request(5));
+ q.enqueue_strict(c3, 3, Request(6));
+ q.enqueue(c3, 1, 1u, Request(7));
+ q.enqueue(c2, 1, 1u, Request(8));
+ q.enqueue(c1, 1, 1u, Request(9));
+
+ std::list<Request> filtered;
+
+ q.remove_by_filter([&](const Request& r) -> bool {
+ if (r.value & 2) {
+ filtered.push_back(r);
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ ASSERT_EQ(4u, filtered.size()) <<
+ "filter should have removed four elements";
+ while (!filtered.empty()) {
+ ASSERT_TRUE((filtered.front().value & 2) > 0) <<
+ "expect this value to have been filtered out";
+ filtered.pop_front();
+ }
+
+ ASSERT_EQ(5u, q.get_size_slow()) <<
+ "filter should have left five remaining elements";
+ while (!q.empty()) {
+ Request r = q.dequeue();
+ ASSERT_TRUE((r.value & 2) == 0) <<
+ "expect this value to have been left in";
+ }
+}
diff --git a/src/test/common/test_mutex_debug.cc b/src/test/common/test_mutex_debug.cc
new file mode 100644
index 000000000..977dfe738
--- /dev/null
+++ b/src/test/common/test_mutex_debug.cc
@@ -0,0 +1,101 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 &smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <future>
+#include <mutex>
+#include <thread>
+
+#include "common/mutex_debug.h"
+
+#include "gtest/gtest.h"
+
+
+template<typename Mutex>
+static bool test_try_lock(Mutex* m) {
+ if (!m->try_lock())
+ return false;
+ m->unlock();
+ return true;
+}
+
+template<typename Mutex>
+static void test_lock() {
+ Mutex m("mutex");
+ auto ttl = &test_try_lock<Mutex>;
+
+ m.lock();
+ ASSERT_TRUE(m.is_locked());
+ auto f1 = std::async(std::launch::async, ttl, &m);
+ ASSERT_FALSE(f1.get());
+
+ ASSERT_TRUE(m.is_locked());
+ ASSERT_TRUE(!!m);
+
+ m.unlock();
+ ASSERT_FALSE(m.is_locked());
+ ASSERT_FALSE(!!m);
+
+ auto f3 = std::async(std::launch::async, ttl, &m);
+ ASSERT_TRUE(f3.get());
+
+ ASSERT_FALSE(m.is_locked());
+ ASSERT_FALSE(!!m);
+}
+
+TEST(MutexDebug, Lock) {
+ test_lock<ceph::mutex_debug>();
+}
+
+TEST(MutexDebug, NotRecursive) {
+ ceph::mutex_debug m("foo");
+ auto ttl = &test_try_lock<mutex_debug>;
+
+ ASSERT_NO_THROW(m.lock());
+ ASSERT_TRUE(m.is_locked());
+ ASSERT_FALSE(std::async(std::launch::async, ttl, &m).get());
+
+ ASSERT_THROW(m.lock(), std::system_error);
+ ASSERT_TRUE(m.is_locked());
+ ASSERT_FALSE(std::async(std::launch::async, ttl, &m).get());
+
+ ASSERT_NO_THROW(m.unlock());
+ ASSERT_FALSE(m.is_locked());
+ ASSERT_TRUE(std::async(std::launch::async, ttl, &m).get());
+}
+
+TEST(MutexRecursiveDebug, Lock) {
+ test_lock<ceph::mutex_recursive_debug>();
+}
+
+
+TEST(MutexRecursiveDebug, Recursive) {
+ ceph::mutex_recursive_debug m("m");
+ auto ttl = &test_try_lock<mutex_recursive_debug>;
+
+ ASSERT_NO_THROW(m.lock());
+ ASSERT_TRUE(m.is_locked());
+ ASSERT_FALSE(std::async(std::launch::async, ttl, &m).get());
+
+ ASSERT_NO_THROW(m.lock());
+ ASSERT_TRUE(m.is_locked());
+ ASSERT_FALSE(std::async(std::launch::async, ttl, &m).get());
+
+ ASSERT_NO_THROW(m.unlock());
+ ASSERT_TRUE(m.is_locked());
+ ASSERT_FALSE(std::async(std::launch::async, ttl, &m).get());
+
+ ASSERT_NO_THROW(m.unlock());
+ ASSERT_FALSE(m.is_locked());
+ ASSERT_TRUE(std::async(std::launch::async, ttl, &m).get());
+}
diff --git a/src/test/common/test_numa.cc b/src/test/common/test_numa.cc
new file mode 100644
index 000000000..a3bbdf223
--- /dev/null
+++ b/src/test/common/test_numa.cc
@@ -0,0 +1,72 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "gtest/gtest.h"
+#include "common/numa.h"
+
+TEST(cpu_set, parse_list) {
+ cpu_set_t cpu_set;
+ size_t size;
+
+ ASSERT_EQ(0, parse_cpu_set_list("0-3", &size, &cpu_set));
+ ASSERT_EQ(size, 4u);
+ for (unsigned i = 0; i < size; ++i) {
+ ASSERT_TRUE(CPU_ISSET(i, &cpu_set));
+ }
+
+ ASSERT_EQ(0, parse_cpu_set_list("0-3,6-7", &size, &cpu_set));
+ ASSERT_EQ(size, 8u);
+ for (unsigned i = 0; i < 4; ++i) {
+ ASSERT_TRUE(CPU_ISSET(i, &cpu_set));
+ }
+ for (unsigned i = 4; i < 6; ++i) {
+ ASSERT_FALSE(CPU_ISSET(i, &cpu_set));
+ }
+ for (unsigned i = 6; i < 8; ++i) {
+ ASSERT_TRUE(CPU_ISSET(i, &cpu_set));
+ }
+
+ ASSERT_EQ(0, parse_cpu_set_list("0-31", &size, &cpu_set));
+ ASSERT_EQ(size, 32u);
+ for (unsigned i = 0; i < size; ++i) {
+ ASSERT_TRUE(CPU_ISSET(i, &cpu_set));
+ }
+}
+
+TEST(cpu_set, to_str_list) {
+ cpu_set_t cpu_set;
+ CPU_ZERO(&cpu_set);
+ CPU_SET(0, &cpu_set);
+ ASSERT_EQ(std::string("0"), cpu_set_to_str_list(8, &cpu_set));
+ CPU_SET(1, &cpu_set);
+ CPU_SET(2, &cpu_set);
+ CPU_SET(3, &cpu_set);
+ ASSERT_EQ(std::string("0-3"), cpu_set_to_str_list(8, &cpu_set));
+ CPU_SET(5, &cpu_set);
+ ASSERT_EQ(std::string("0-3,5"), cpu_set_to_str_list(8, &cpu_set));
+ CPU_SET(6, &cpu_set);
+ CPU_SET(7, &cpu_set);
+ ASSERT_EQ(std::string("0-3,5-7"), cpu_set_to_str_list(8, &cpu_set));
+}
+
+TEST(cpu_set, round_trip_list)
+{
+ for (unsigned i = 0; i < 100; ++i) {
+ cpu_set_t cpu_set;
+ size_t size = 32;
+ CPU_ZERO(&cpu_set);
+ for (unsigned i = 0; i < 32; ++i) {
+ if (rand() % 1) {
+ CPU_SET(i, &cpu_set);
+ }
+ }
+ std::string v = cpu_set_to_str_list(size, &cpu_set);
+ cpu_set_t cpu_set_2;
+ size_t size2;
+ ASSERT_EQ(0, parse_cpu_set_list(v.c_str(), &size2, &cpu_set_2));
+ for (unsigned i = 0; i < 32; ++i) {
+ ASSERT_TRUE(CPU_ISSET(i, &cpu_set) == CPU_ISSET(i, &cpu_set_2));
+ }
+ }
+}
+
diff --git a/src/test/common/test_option.cc b/src/test/common/test_option.cc
new file mode 100644
index 000000000..a75f74dc9
--- /dev/null
+++ b/src/test/common/test_option.cc
@@ -0,0 +1,73 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+// vim: ts=8 sw=2 smarttab expandtab
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+
+#include "common/options.h"
+
+using namespace std;
+
+TEST(Option, validate_min_max)
+{
+ auto opt = Option{"foo", Option::TYPE_MILLISECS, Option::LEVEL_ADVANCED}
+ .set_default(42)
+ .set_min_max(10, 128);
+ struct test_t {
+ unsigned new_val;
+ int expected_retval;
+ };
+ test_t tests[] =
+ {{9, -EINVAL},
+ {10, 0},
+ {11, 0},
+ {128, 0},
+ {1024, -EINVAL}
+ };
+ for (auto& test : tests) {
+ Option::value_t new_value = std::chrono::milliseconds{test.new_val};
+ std::string err;
+ GTEST_ASSERT_EQ(test.expected_retval, opt.validate(new_value, &err));
+ }
+}
+
+TEST(Option, parse)
+{
+ auto opt = Option{"foo", Option::TYPE_MILLISECS, Option::LEVEL_ADVANCED}
+ .set_default(42)
+ .set_min_max(10, 128);
+ struct test_t {
+ string new_val;
+ int expected_retval;
+ unsigned expected_parsed_val;
+ };
+ test_t tests[] =
+ {{"9", -EINVAL, 0},
+ {"10", 0, 10},
+ {"11", 0, 11},
+ {"128", 0, 128},
+ {"1024", -EINVAL, 0}
+ };
+ for (auto& test : tests) {
+ Option::value_t parsed_val;
+ std::string err;
+ GTEST_ASSERT_EQ(test.expected_retval,
+ opt.parse_value(test.new_val, &parsed_val, &err));
+ if (test.expected_retval == 0) {
+ Option::value_t expected_parsed_val =
+ std::chrono::milliseconds{test.expected_parsed_val};
+ GTEST_ASSERT_EQ(parsed_val, expected_parsed_val);
+ }
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../../../build ;
+ * ninja unittest_option && bin/unittest_option
+ * "
+ * End:
+ */
diff --git a/src/test/common/test_perf_counters_key.cc b/src/test/common/test_perf_counters_key.cc
new file mode 100644
index 000000000..5a156c749
--- /dev/null
+++ b/src/test/common/test_perf_counters_key.cc
@@ -0,0 +1,129 @@
+#include "common/perf_counters_key.h"
+#include <gtest/gtest.h>
+
+namespace ceph::perf_counters {
+
+TEST(PerfCounters, key_create)
+{
+ EXPECT_EQ(key_create(""),
+ std::string_view("\0", 1));
+
+ EXPECT_EQ(key_create("perf"),
+ std::string_view("perf\0", 5));
+
+ EXPECT_EQ(key_create("perf", {{"",""}}),
+ std::string_view("perf\0\0\0", 7));
+
+ EXPECT_EQ(key_create("perf", {{"","a"}, {"",""}}),
+ std::string_view("perf\0\0a\0", 8));
+
+ EXPECT_EQ(key_create("perf", {{"a","b"}}),
+ std::string_view("perf\0a\0b\0", 9));
+
+ EXPECT_EQ(key_create("perf", {{"y","z"}, {"a","b"}}),
+ std::string_view("perf\0a\0b\0y\0z\0", 13));
+
+ EXPECT_EQ(key_create("perf", {{"a","b"}, {"a","c"}}),
+ std::string_view("perf\0a\0b\0", 9));
+
+ EXPECT_EQ(key_create("perf", {{"a","z"}, {"a","b"}}),
+ std::string_view("perf\0a\0z\0", 9));
+
+ EXPECT_EQ(key_create("perf", {{"d",""}, {"c",""}, {"b",""}, {"a",""}}),
+ std::string_view("perf\0a\0\0b\0\0c\0\0d\0\0", 17));
+}
+
+TEST(PerfCounters, key_insert)
+{
+ EXPECT_EQ(key_insert("", {{"",""}}),
+ std::string_view("\0\0\0", 3));
+
+ EXPECT_EQ(key_insert("", {{"",""}, {"",""}}),
+ std::string_view("\0\0\0", 3));
+
+ EXPECT_EQ(key_insert(std::string_view{"\0\0\0", 3}, {{"",""}}),
+ std::string_view("\0\0\0", 3));
+
+ EXPECT_EQ(key_insert(std::string_view{"\0", 1}, {{"",""}}),
+ std::string_view("\0\0\0", 3));
+
+ EXPECT_EQ(key_insert("", {{"a","b"}}),
+ std::string_view("\0a\0b\0", 5));
+
+ EXPECT_EQ(key_insert(std::string_view{"\0", 1}, {{"a","b"}}),
+ std::string_view("\0a\0b\0", 5));
+
+ EXPECT_EQ(key_insert("a", {{"",""}}),
+ std::string_view("a\0\0\0", 4));
+
+ EXPECT_EQ(key_insert(std::string_view{"a\0", 2}, {{"",""}}),
+ std::string_view("a\0\0\0", 4));
+
+ EXPECT_EQ(key_insert(std::string_view{"p\0", 2}, {{"a","b"}}),
+ std::string_view("p\0a\0b\0", 6));
+
+ EXPECT_EQ(key_insert(std::string_view{"p\0a\0a\0", 6}, {{"a","b"}}),
+ std::string_view("p\0a\0b\0", 6));
+
+ EXPECT_EQ(key_insert(std::string_view{"p\0a\0z\0", 6}, {{"a","b"}}),
+ std::string_view("p\0a\0b\0", 6));
+
+ EXPECT_EQ(key_insert(std::string_view{"p\0z\0z\0", 6}, {{"a","b"}}),
+ std::string_view("p\0a\0b\0z\0z\0", 10));
+
+ EXPECT_EQ(key_insert(std::string_view{"p\0b\0b\0", 6},
+ {{"a","a"}, {"c","c"}}),
+ std::string_view("p\0a\0a\0b\0b\0c\0c\0", 14));
+
+ EXPECT_EQ(key_insert(std::string_view{"p\0a\0a\0b\0b\0c\0c\0", 14},
+ {{"z","z"}, {"b","z"}}),
+ std::string_view("p\0a\0a\0b\0z\0c\0c\0z\0z\0", 18));
+}
+
+TEST(PerfCounters, key_name)
+{
+ EXPECT_EQ(key_name(""),
+ "");
+ EXPECT_EQ(key_name({"\0", 1}),
+ "");
+ EXPECT_EQ(key_name({"perf\0", 5}),
+ "perf");
+ EXPECT_EQ(key_name({"perf\0\0\0", 7}),
+ "perf");
+}
+
+TEST(PerfCounters, key_labels)
+{
+ {
+ auto labels = key_labels("");
+ EXPECT_EQ(labels.begin(), labels.end());
+ }
+ {
+ auto labels = key_labels({"\0", 1});
+ EXPECT_EQ(labels.begin(), labels.end());
+ }
+ {
+ auto labels = key_labels({"perf\0", 5});
+ EXPECT_EQ(labels.begin(), labels.end());
+ }
+ {
+ auto labels = key_labels({"\0\0\0", 3});
+ ASSERT_EQ(1, std::distance(labels.begin(), labels.end()));
+ EXPECT_EQ(label_pair("", ""), *labels.begin());
+ }
+ {
+ auto labels = key_labels({"\0a\0b\0", 5});
+ ASSERT_EQ(1, std::distance(labels.begin(), labels.end()));
+ EXPECT_EQ(label_pair("a", "b"), *labels.begin());
+ EXPECT_EQ(std::next(labels.begin()), labels.end());
+ }
+ {
+ auto labels = key_labels({"\0a\0b\0c\0d\0", 9});
+ ASSERT_EQ(2, std::distance(labels.begin(), labels.end()));
+ EXPECT_EQ(label_pair("a", "b"), *labels.begin());
+ EXPECT_EQ(label_pair("c", "d"), *std::next(labels.begin()));
+ EXPECT_EQ(std::next(labels.begin(), 2), labels.end());
+ }
+}
+
+} // namespace ceph::perf_counters
diff --git a/src/test/common/test_perf_histogram.cc b/src/test/common/test_perf_histogram.cc
new file mode 100644
index 000000000..4ca8d687a
--- /dev/null
+++ b/src/test/common/test_perf_histogram.cc
@@ -0,0 +1,247 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 &smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 OVH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/perf_histogram.h"
+
+#include "gtest/gtest.h"
+
+template <int DIM>
+class PerfHistogramAccessor : public PerfHistogram<DIM> {
+public:
+ typedef PerfHistogram<DIM> Base;
+
+ using Base::PerfHistogram;
+
+ static int64_t get_bucket_for_axis(
+ int64_t value, const PerfHistogramCommon::axis_config_d& axis_config) {
+ return Base::get_bucket_for_axis(value, axis_config);
+ }
+
+ static std::vector<std::pair<int64_t, int64_t>> get_axis_bucket_ranges(
+ const PerfHistogramCommon::axis_config_d& axis_config) {
+ return Base::get_axis_bucket_ranges(axis_config);
+ }
+
+ const typename Base::axis_config_d& get_axis_config(int num) {
+ return Base::m_axes_config[num];
+ }
+
+ template <typename F1, typename F2, typename F3>
+ void visit_values(F1 f1, F2 f2, F3 f3) {
+ Base::visit_values(f1, f2, f3);
+ }
+};
+
+TEST(PerfHistogram, GetBucketForAxis) {
+ PerfHistogramCommon::axis_config_d linear{
+ "", PerfHistogramCommon::SCALE_LINEAR, 100, 3, 4};
+
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(-1, linear));
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(0, linear));
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(99, linear));
+ ASSERT_EQ(1, PerfHistogramAccessor<1>::get_bucket_for_axis(100, linear));
+ ASSERT_EQ(1, PerfHistogramAccessor<1>::get_bucket_for_axis(101, linear));
+ ASSERT_EQ(1, PerfHistogramAccessor<1>::get_bucket_for_axis(102, linear));
+ ASSERT_EQ(2, PerfHistogramAccessor<1>::get_bucket_for_axis(103, linear));
+ ASSERT_EQ(2, PerfHistogramAccessor<1>::get_bucket_for_axis(105, linear));
+ ASSERT_EQ(3, PerfHistogramAccessor<1>::get_bucket_for_axis(106, linear));
+ ASSERT_EQ(3, PerfHistogramAccessor<1>::get_bucket_for_axis(108, linear));
+ ASSERT_EQ(3, PerfHistogramAccessor<1>::get_bucket_for_axis(109, linear));
+
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(
+ std::numeric_limits<int64_t>::min(), linear));
+ ASSERT_EQ(3, PerfHistogramAccessor<1>::get_bucket_for_axis(
+ std::numeric_limits<int64_t>::max(), linear));
+
+ PerfHistogramCommon::axis_config_d logarithmic{
+ "", PerfHistogramCommon::SCALE_LOG2, 100, 3, 5};
+
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(-1, logarithmic));
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(0, logarithmic));
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(99, logarithmic));
+ ASSERT_EQ(1, PerfHistogramAccessor<1>::get_bucket_for_axis(100, logarithmic));
+ ASSERT_EQ(1, PerfHistogramAccessor<1>::get_bucket_for_axis(101, logarithmic));
+ ASSERT_EQ(1, PerfHistogramAccessor<1>::get_bucket_for_axis(102, logarithmic));
+ ASSERT_EQ(2, PerfHistogramAccessor<1>::get_bucket_for_axis(103, logarithmic));
+ ASSERT_EQ(2, PerfHistogramAccessor<1>::get_bucket_for_axis(105, logarithmic));
+ ASSERT_EQ(3, PerfHistogramAccessor<1>::get_bucket_for_axis(106, logarithmic));
+ ASSERT_EQ(3, PerfHistogramAccessor<1>::get_bucket_for_axis(111, logarithmic));
+ ASSERT_EQ(4, PerfHistogramAccessor<1>::get_bucket_for_axis(112, logarithmic));
+ ASSERT_EQ(4, PerfHistogramAccessor<1>::get_bucket_for_axis(124, logarithmic));
+
+ ASSERT_EQ(0, PerfHistogramAccessor<1>::get_bucket_for_axis(
+ std::numeric_limits<int64_t>::min(), logarithmic));
+ ASSERT_EQ(4, PerfHistogramAccessor<1>::get_bucket_for_axis(
+ std::numeric_limits<int64_t>::max(), logarithmic));
+}
+
+static const int XS = 5;
+static const int YS = 7;
+
+static const auto x_axis = PerfHistogramCommon::axis_config_d{
+ "x", PerfHistogramCommon::SCALE_LINEAR, 0, 1, XS};
+static const auto y_axis = PerfHistogramCommon::axis_config_d{
+ "y", PerfHistogramCommon::SCALE_LOG2, 0, 1, YS};
+
+TEST(PerfHistogram, ZeroedInitially) {
+ PerfHistogramAccessor<2> h{x_axis, y_axis};
+ for (int x = 0; x < XS; ++x) {
+ for (int y = 0; y < YS; ++y) {
+ ASSERT_EQ(0UL, h.read_bucket(x, y));
+ }
+ }
+}
+
+TEST(PerfHistogram, Copy) {
+ PerfHistogramAccessor<2> h1{x_axis, y_axis};
+ h1.inc_bucket(1, 1);
+ h1.inc_bucket(2, 3);
+ h1.inc_bucket(4, 5);
+
+ PerfHistogramAccessor<2> h2 = h1;
+
+ const int cx = 1;
+ const int cy = 2;
+
+ h1.inc_bucket(cx, cy);
+
+ // Axes configuration must be equal
+ for (int i = 0; i < 2; i++) {
+ const auto& ac1 = h1.get_axis_config(i);
+ const auto& ac2 = h2.get_axis_config(i);
+ ASSERT_EQ(ac1.m_name, ac2.m_name);
+ ASSERT_EQ(ac1.m_scale_type, ac2.m_scale_type);
+ ASSERT_EQ(ac1.m_min, ac2.m_min);
+ ASSERT_EQ(ac1.m_quant_size, ac2.m_quant_size);
+ ASSERT_EQ(ac1.m_buckets, ac2.m_buckets);
+ }
+
+ // second histogram must have histogram values equal to the first
+ // one at the time of copy
+ for (int x = 0; x < XS; x++) {
+ for (int y = 0; y < YS; y++) {
+ if (x == cx && y == cy) {
+ ASSERT_NE(h1.read_bucket(x, y), h2.read_bucket(x, y));
+ } else {
+ ASSERT_EQ(h1.read_bucket(x, y), h2.read_bucket(x, y));
+ }
+ }
+ }
+}
+
+TEST(PerfHistogram, SimpleValues) {
+ PerfHistogramAccessor<2> h{x_axis, y_axis};
+ ASSERT_EQ(0UL, h.read_bucket(1, 1));
+ h.inc(0, 0);
+ ASSERT_EQ(1UL, h.read_bucket(1, 1));
+
+ ASSERT_EQ(0UL, h.read_bucket(2, 2));
+ h.inc(1, 1);
+ ASSERT_EQ(1UL, h.read_bucket(2, 2));
+
+ ASSERT_EQ(0UL, h.read_bucket(3, 3));
+ h.inc(2, 2);
+ ASSERT_EQ(1UL, h.read_bucket(3, 3));
+
+ ASSERT_EQ(0UL, h.read_bucket(4, 3));
+ h.inc(3, 3);
+ ASSERT_EQ(1UL, h.read_bucket(4, 3));
+}
+
+TEST(PerfHistogram, OneBucketRange) {
+ auto ranges = PerfHistogramAccessor<1>::get_axis_bucket_ranges(
+ PerfHistogramCommon::axis_config_d{"", PerfHistogramCommon::SCALE_LINEAR,
+ 0, 1, 1});
+
+ ASSERT_EQ(1UL, ranges.size());
+ ASSERT_EQ(std::numeric_limits<int64_t>::min(), ranges[0].first);
+ ASSERT_EQ(std::numeric_limits<int64_t>::max(), ranges[0].second);
+}
+
+TEST(PerfHistogram, TwoBucketRange) {
+ auto ranges = PerfHistogramAccessor<1>::get_axis_bucket_ranges(
+ PerfHistogramCommon::axis_config_d{"", PerfHistogramCommon::SCALE_LINEAR,
+ 0, 1, 2});
+
+ ASSERT_EQ(2UL, ranges.size());
+ ASSERT_EQ(std::numeric_limits<int64_t>::min(), ranges[0].first);
+ ASSERT_EQ(-1, ranges[0].second);
+ ASSERT_EQ(0, ranges[1].first);
+ ASSERT_EQ(std::numeric_limits<int64_t>::max(), ranges[1].second);
+}
+
+TEST(PerfHistogram, LinearBucketRange) {
+ PerfHistogramCommon::axis_config_d ac{"", PerfHistogramCommon::SCALE_LINEAR,
+ 100, 10, 15};
+ auto ranges = PerfHistogramAccessor<1>::get_axis_bucket_ranges(ac);
+
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ ASSERT_EQ(
+ static_cast<long>(i), PerfHistogramAccessor<1>::get_bucket_for_axis(ranges[i].first, ac));
+ ASSERT_EQ(
+ static_cast<long>(i), PerfHistogramAccessor<1>::get_bucket_for_axis(ranges[i].second, ac));
+ }
+
+ for (size_t i = 1; i < ranges.size(); ++i) {
+ ASSERT_EQ(ranges[i].first, ranges[i - 1].second + 1);
+ }
+}
+
+TEST(PerfHistogram, LogarithmicBucketRange) {
+ PerfHistogramCommon::axis_config_d ac{"", PerfHistogramCommon::SCALE_LOG2,
+ 100, 10, 15};
+ auto ranges = PerfHistogramAccessor<1>::get_axis_bucket_ranges(ac);
+
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ ASSERT_EQ(
+ static_cast<long>(i), PerfHistogramAccessor<1>::get_bucket_for_axis(ranges[i].first, ac));
+ ASSERT_EQ(
+ static_cast<long>(i), PerfHistogramAccessor<1>::get_bucket_for_axis(ranges[i].second, ac));
+ }
+
+ for (size_t i = 1; i < ranges.size(); ++i) {
+ ASSERT_EQ(ranges[i].first, ranges[i - 1].second + 1);
+ }
+}
+
+TEST(PerfHistogram, AxisAddressing) {
+ PerfHistogramCommon::axis_config_d ac1{"", PerfHistogramCommon::SCALE_LINEAR,
+ 0, 1, 7};
+ PerfHistogramCommon::axis_config_d ac2{"", PerfHistogramCommon::SCALE_LINEAR,
+ 0, 1, 9};
+ PerfHistogramCommon::axis_config_d ac3{"", PerfHistogramCommon::SCALE_LINEAR,
+ 0, 1, 11};
+
+ PerfHistogramAccessor<3> h{ac1, ac2, ac3};
+
+ h.inc(1, 2, 3); // Should end up in buckets 2, 3, 4
+ h.inc_bucket(4, 5, 6);
+
+ std::vector<int64_t> rawValues;
+ h.visit_values([](int) {},
+ [&rawValues](int64_t value) { rawValues.push_back(value); },
+ [](int) {});
+
+ for (size_t i = 0; i < rawValues.size(); ++i) {
+ switch (i) {
+ case 4 + 11 * (3 + 9 * 2):
+ case 6 + 11 * (5 + 9 * 4):
+ ASSERT_EQ(1, rawValues[i]);
+ break;
+ default:
+ ASSERT_EQ(0, rawValues[i]);
+ break;
+ }
+ }
+}
diff --git a/src/test/common/test_pretty_binary.cc b/src/test/common/test_pretty_binary.cc
new file mode 100644
index 000000000..837dbefc1
--- /dev/null
+++ b/src/test/common/test_pretty_binary.cc
@@ -0,0 +1,50 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/pretty_binary.h"
+
+#include "gtest/gtest.h"
+
+TEST(pretty_binary, print) {
+ ASSERT_EQ(pretty_binary_string(std::string("foo\001\002bars")), std::string("'foo'0x0102'bars'"));
+ ASSERT_EQ(pretty_binary_string(std::string("foo''bars")), std::string("'foo''''bars'"));
+ ASSERT_EQ(pretty_binary_string(std::string("foo\377 \200!!")), std::string("'foo'0xFF2020802121"));
+ ASSERT_EQ(pretty_binary_string(std::string("\001\002\003\004")), std::string("0x01020304"));
+}
+
+TEST(pretty_binary, unprint) {
+ ASSERT_EQ(pretty_binary_string_reverse(std::string("'foo'0x0102'bars'")), std::string("foo\001\002bars"));
+ ASSERT_EQ(pretty_binary_string_reverse(std::string("'foo''''bars'")), std::string("foo''bars"));
+ ASSERT_EQ(pretty_binary_string_reverse(std::string("'foo'0xFF2020802121")), std::string("foo\377 \200!!"));
+ ASSERT_EQ(pretty_binary_string_reverse(std::string("0x01020304")),std::string("\001\002\003\004"));
+}
+
+TEST(pretty_binary, all_chars) {
+ std::string a;
+ for (unsigned j = 0; j < 256; ++j) {
+ a.push_back((char)j);
+ }
+ std::string b = pretty_binary_string(a);
+ ASSERT_EQ(a, pretty_binary_string_reverse(b));
+}
+
+TEST(pretty_binary, random) {
+ for (size_t i = 0; i < 100000; i++) {
+ std::string a;
+ size_t r = rand() % 100;
+ for (size_t j = 0; j < r; ++j) {
+ a.push_back((unsigned char)rand());
+ }
+ std::string b = pretty_binary_string(a);
+ ASSERT_EQ(a, pretty_binary_string_reverse(b));
+ }
+}
+
+TEST(pretty_binary, invalid) {
+ ASSERT_THROW(pretty_binary_string_reverse("'"), std::invalid_argument);
+ ASSERT_THROW(pretty_binary_string_reverse("0x1"), std::invalid_argument);
+ ASSERT_THROW(pretty_binary_string_reverse("0x"), std::invalid_argument);
+ ASSERT_THROW(pretty_binary_string_reverse("'''"), std::invalid_argument);
+ ASSERT_THROW(pretty_binary_string_reverse("'a'x"), std::invalid_argument);
+ ASSERT_THROW(pretty_binary_string_reverse("'a'0x"), std::invalid_argument);
+}
diff --git a/src/test/common/test_prioritized_queue.cc b/src/test/common/test_prioritized_queue.cc
new file mode 100644
index 000000000..83bd41f8c
--- /dev/null
+++ b/src/test/common/test_prioritized_queue.cc
@@ -0,0 +1,206 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "gtest/gtest.h"
+#include "common/PrioritizedQueue.h"
+
+#include <numeric>
+#include <vector>
+#include <algorithm>
+#include <random>
+
+using std::vector;
+
+class PrioritizedQueueTest : public testing::Test
+{
+protected:
+ typedef int Klass;
+ typedef unsigned Item;
+ typedef PrioritizedQueue<Item, Klass> PQ;
+ enum { item_size = 100, };
+ vector<Item> items;
+
+ void SetUp() override {
+ for (int i = 0; i < item_size; i++) {
+ items.push_back(Item(i));
+ }
+ std::random_device rd;
+ std::default_random_engine rng(rd());
+ std::shuffle(items.begin(), items.end(), rng);
+ }
+ void TearDown() override {
+ items.clear();
+ }
+};
+
+TEST_F(PrioritizedQueueTest, capacity) {
+ const unsigned min_cost = 10;
+ const unsigned max_tokens_per_subqueue = 50;
+ PQ pq(max_tokens_per_subqueue, min_cost);
+ EXPECT_TRUE(pq.empty());
+ EXPECT_EQ(0u, pq.length());
+
+ pq.enqueue_strict(Klass(1), 0, Item(0));
+ EXPECT_FALSE(pq.empty());
+ EXPECT_EQ(1u, pq.length());
+
+ for (int i = 0; i < 3; i++) {
+ pq.enqueue(Klass(1), 0, 10, Item(0));
+ }
+ for (unsigned i = 4; i > 0; i--) {
+ EXPECT_FALSE(pq.empty());
+ EXPECT_EQ(i, pq.length());
+ pq.dequeue();
+ }
+ EXPECT_TRUE(pq.empty());
+ EXPECT_EQ(0u, pq.length());
+}
+
+TEST_F(PrioritizedQueueTest, strict_pq) {
+ const unsigned min_cost = 1;
+ const unsigned max_tokens_per_subqueue = 50;
+ PQ pq(max_tokens_per_subqueue, min_cost);
+ // 0 .. item_size-1
+ for (unsigned i = 0; i < item_size; i++) {
+ unsigned priority = items[i];
+ const Item& item = items[i];
+ pq.enqueue_strict(Klass(0), priority, Item(item));
+ }
+ // item_size-1 .. 0
+ for (unsigned i = item_size; i > 0; i--) {
+ Item item = pq.dequeue();
+ unsigned priority = item;
+ EXPECT_EQ(i - 1, priority);
+ }
+}
+
+TEST_F(PrioritizedQueueTest, lowest_among_eligible_otherwise_highest) {
+ // to minimize the effect of `distribute_tokens()`
+ // all eligible items will be assigned with cost of min_cost
+ const unsigned min_cost = 0;
+ const unsigned max_tokens_per_subqueue = 100;
+ PQ pq(max_tokens_per_subqueue, min_cost);
+
+#define ITEM_TO_COST(item_) (item_ % 5 ? min_cost : max_tokens_per_subqueue)
+ unsigned num_low_cost = 0, num_high_cost = 0;
+ for (int i = 0; i < item_size; i++) {
+ const Item& item = items[i];
+ unsigned cost = ITEM_TO_COST(item);
+ unsigned priority = item;
+ if (cost == min_cost) {
+ num_low_cost++;
+ } else {
+ num_high_cost++;
+ }
+ pq.enqueue(Klass(0), priority, cost, Item(item));
+ }
+ // the token in all buckets is 0 at the beginning, so dequeue() should pick
+ // the first one with the highest priority.
+ unsigned highest_priority;
+ {
+ Item item = pq.dequeue();
+ unsigned cost = ITEM_TO_COST(item);
+ unsigned priority = item;
+ if (cost == min_cost) {
+ num_low_cost--;
+ } else {
+ num_high_cost--;
+ }
+ EXPECT_EQ(item_size - 1u, priority);
+ highest_priority = priority;
+ }
+ unsigned lowest_priority = 0;
+ for (unsigned i = 0; i < num_low_cost; i++) {
+ Item item = pq.dequeue();
+ unsigned cost = ITEM_TO_COST(item);
+ unsigned priority = item;
+ EXPECT_EQ(min_cost, cost);
+ EXPECT_GT(priority, lowest_priority);
+ lowest_priority = priority;
+ }
+ for (unsigned i = 0; i < num_high_cost; i++) {
+ Item item = pq.dequeue();
+ unsigned cost = ITEM_TO_COST(item);
+ unsigned priority = item;
+ EXPECT_EQ(max_tokens_per_subqueue, cost);
+ EXPECT_LT(priority, highest_priority);
+ highest_priority = priority;
+ }
+#undef ITEM_TO_COST
+}
+
+static const unsigned num_classes = 4;
+// just a determinitic number
+#define ITEM_TO_CLASS(item_) Klass((item_ + 43) % num_classes)
+
+TEST_F(PrioritizedQueueTest, fairness_by_class) {
+ // dequeue should be fair to all classes in a certain bucket
+ const unsigned min_cost = 1;
+ const unsigned max_tokens_per_subqueue = 50;
+ PQ pq(max_tokens_per_subqueue, min_cost);
+
+ for (int i = 0; i < item_size; i++) {
+ const Item& item = items[i];
+ Klass k = ITEM_TO_CLASS(item);
+ unsigned priority = 0;
+ unsigned cost = 1;
+ pq.enqueue(k, priority, cost, Item(item));
+ }
+ // just sample first 1/2 of the items
+ // if i pick too small a dataset, the result won't be statisitcally
+ // significant. if the sample dataset is too large, it will converge to the
+ // distribution of the full set.
+ vector<unsigned> num_picked_in_class(num_classes, 0u);
+ for (int i = 0; i < item_size / 2; i++) {
+ Item item = pq.dequeue();
+ Klass k = ITEM_TO_CLASS(item);
+ num_picked_in_class[k]++;
+ }
+ unsigned total = std::accumulate(num_picked_in_class.begin(),
+ num_picked_in_class.end(),
+ 0);
+ float avg = float(total) / num_classes;
+ for (unsigned i = 0; i < num_classes; i++) {
+ EXPECT_NEAR(avg, num_picked_in_class[i], 0.5);
+ }
+}
+
+
+TEST_F(PrioritizedQueueTest, remove_by_class) {
+ const unsigned min_cost = 1;
+ const unsigned max_tokens_per_subqueue = 50;
+ PQ pq(max_tokens_per_subqueue, min_cost);
+ const Klass class_to_remove(2);
+ unsigned num_to_remove = 0;
+ for (int i = 0; i < item_size; i++) {
+ const Item& item = items[i];
+ Klass k = ITEM_TO_CLASS(item);
+ pq.enqueue(k, 0, 0, Item(item));
+ if (k == class_to_remove) {
+ num_to_remove++;
+ }
+ }
+ std::list<Item> removed;
+ pq.remove_by_class(class_to_remove, &removed);
+
+ // see if the removed items are expected ones.
+ for (std::list<Item>::iterator it = removed.begin();
+ it != removed.end();
+ ++it) {
+ const Item& item = *it;
+ Klass k = ITEM_TO_CLASS(item);
+ EXPECT_EQ(class_to_remove, k);
+ items.erase(remove(items.begin(), items.end(), item), items.end());
+ }
+ EXPECT_EQ(num_to_remove, removed.size());
+ EXPECT_EQ(item_size - num_to_remove, pq.length());
+ EXPECT_EQ(item_size - num_to_remove, items.size());
+ // see if the remainder are expeceted also.
+ while (!pq.empty()) {
+ const Item item = pq.dequeue();
+ Klass k = ITEM_TO_CLASS(item);
+ EXPECT_NE(class_to_remove, k);
+ items.erase(remove(items.begin(), items.end(), item), items.end());
+ }
+ EXPECT_TRUE(items.empty());
+}
diff --git a/src/test/common/test_rabin_chunk.cc b/src/test/common/test_rabin_chunk.cc
new file mode 100644
index 000000000..dc33ba0fe
--- /dev/null
+++ b/src/test/common/test_rabin_chunk.cc
@@ -0,0 +1,151 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <vector>
+#include <cstring>
+#include <random>
+
+#include "include/types.h"
+#include "include/buffer.h"
+
+#include "common/rabin.h"
+#include "gtest/gtest.h"
+
+TEST(Rabin, rabin_hash_simple) {
+ uint64_t expected = 680425538102669423;
+ uint64_t result;
+
+ unsigned int window_size = 48;
+ char data[window_size + 1];
+ RabinChunk rabin;
+ memset(data, 0, window_size + 1);
+ for (unsigned int i = 0; i < window_size; ++i) {
+ data[i] = i;
+ }
+ result = rabin.gen_rabin_hash(data, 0);
+ ASSERT_EQ(expected, result);
+}
+
+TEST(Rabin, chunk_check_min_max) {
+ const char buf[] = "0123456789";
+
+ bufferlist bl;
+ RabinChunk rabin;
+ for (int i = 0; i < 250; i++) {
+ bl.append(buf);
+ }
+
+ vector<pair<uint64_t, uint64_t>> chunks;
+ size_t min_chunk = 2000;
+ size_t max_chunk = 8000;
+
+ rabin.do_rabin_chunks(bl, chunks, min_chunk, max_chunk);
+ uint64_t chunk_size = chunks[0].second;
+ ASSERT_GE(chunk_size , min_chunk);
+ ASSERT_LE(chunk_size , max_chunk);
+}
+
+TEST(Rabin, test_cdc) {
+ const char *base_str = "123456789012345678901234567890123456789012345678";
+ bufferlist bl, cmp_bl;
+ for (int i = 0; i < 100; i++) {
+ bl.append(base_str);
+ }
+ cmp_bl.append('a');
+ for (int i = 0; i < 100; i++) {
+ cmp_bl.append(base_str);
+ }
+
+ RabinChunk rabin;
+ vector<pair<uint64_t, uint64_t>> chunks;
+ vector<pair<uint64_t, uint64_t>> cmp_chunks;
+ size_t min_chunk = 200;
+ size_t max_chunk = 800;
+ rabin.do_rabin_chunks(bl, chunks, min_chunk, max_chunk);
+ rabin.do_rabin_chunks(cmp_bl, cmp_chunks, min_chunk, max_chunk);
+ // offset, len will be the same, except in the case of first offset
+ ASSERT_EQ(chunks[4].first + 1, cmp_chunks[4].first);
+ ASSERT_EQ(chunks[4].second, cmp_chunks[4].second);
+}
+
+void generate_buffer(int size, bufferlist *outbl)
+{
+ outbl->clear();
+ outbl->append_zero(size);
+ char *b = outbl->c_str();
+ std::mt19937_64 engine;
+ for (size_t i = 0; i < size / sizeof(uint64_t); ++i) {
+ ((uint64_t*)b)[i] = engine();
+ }
+}
+
+#if 0
+this fails!
+TEST(Rabin, shifts)
+{
+ RabinChunk rabin;
+ rabin.set_target_bits(18, 2);
+
+ for (int frontlen = 1; frontlen < 5; frontlen++) {
+ bufferlist bl1, bl2;
+ generate_buffer(4*1024*1024, &bl1);
+ generate_buffer(frontlen, &bl2);
+ bl2.append(bl1);
+ bl2.rebuild();
+
+ vector<pair<uint64_t, uint64_t>> chunks1, chunks2;
+ rabin.do_rabin_chunks(bl1, chunks1);
+ rabin.do_rabin_chunks(bl2, chunks2);
+ cout << "1: " << chunks1 << std::endl;
+ cout << "2: " << chunks2 << std::endl;
+
+ ASSERT_GE(chunks2.size(), chunks1.size());
+ int match = 0;
+ for (unsigned i = 0; i < chunks1.size(); ++i) {
+ unsigned j = i + (chunks2.size() - chunks1.size());
+ if (chunks1[i].first + frontlen == chunks2[j].first &&
+ chunks1[i].second == chunks2[j].second) {
+ match++;
+ }
+ }
+ ASSERT_GE(match, chunks1.size() - 1);
+ }
+}
+#endif
+
+void do_size_histogram(RabinChunk& rabin, bufferlist& bl,
+ map<int,int> *h)
+{
+ vector<pair<uint64_t, uint64_t>> chunks;
+ rabin.do_rabin_chunks(bl, chunks);
+ for (auto& i : chunks) {
+ unsigned b = i.second & 0xfffff000;
+ //unsigned b = 1 << cbits(i.second);
+ (*h)[b]++;
+ }
+}
+
+void print_histogram(map<int,int>& h)
+{
+ cout << "size\tcount" << std::endl;
+ for (auto i : h) {
+ cout << i.first << "\t" << i.second << std::endl;
+ }
+}
+
+TEST(Rabin, chunk_random)
+{
+ RabinChunk rabin;
+ rabin.set_target_bits(18, 2);
+
+ map<int,int> h;
+ for (int i = 0; i < 8; ++i) {
+ cout << ".";
+ cout.flush();
+ bufferlist r;
+ generate_buffer(16*1024*1024, &r);
+ do_size_histogram(rabin, r, &h);
+ }
+ cout << std::endl;
+ print_histogram(h);
+}
diff --git a/src/test/common/test_random.cc b/src/test/common/test_random.cc
new file mode 100644
index 000000000..490f8d547
--- /dev/null
+++ b/src/test/common/test_random.cc
@@ -0,0 +1,246 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+*/
+
+#include <sstream>
+
+#include "include/random.h"
+
+#include "gtest/gtest.h"
+
+// Helper to see if calls compile with various types:
+template <typename T>
+T type_check_ok(const T min, const T max)
+{
+ return ceph::util::generate_random_number(min, max);
+}
+
+/* Help wrangle "unused variable" warnings: */
+template <typename X>
+void swallow_values(const X x)
+{
+ static_cast<void>(x);
+}
+
+template <typename X, typename ...XS>
+void swallow_values(const X x, const XS... xs)
+{
+ swallow_values(x), swallow_values(xs...);
+}
+
+// Mini-examples showing canonical usage:
+TEST(util, test_random_canonical)
+{
+ // Seed random number generation:
+ ceph::util::randomize_rng();
+
+ // Get a random int between 0 and max int:
+ auto a = ceph::util::generate_random_number();
+
+ // Get a random int between 0 and 20:
+ auto b = ceph::util::generate_random_number(20);
+
+ // Get a random int between 1 and 20:
+ auto c = ceph::util::generate_random_number(1, 20);
+
+ // Get a random float between 0.0 and 20.0:
+ auto d = ceph::util::generate_random_number(20.0);
+
+ // Get a random float between 0.001 and 0.991:
+ auto e = ceph::util::generate_random_number(0.001, 0.991);
+
+ // Make a function object RNG suitable for putting on its own thread:
+ auto gen_fn = ceph::util::random_number_generator<int>();
+ auto z = gen_fn();
+ gen_fn.seed(42); // re-seed
+
+ // Placate the compiler:
+ swallow_values(a, b, c, d, e, z);
+}
+
+TEST(util, test_random)
+{
+ /* The intent of this test is not to formally test random number generation,
+ but rather to casually check that "it works" and catch regressions: */
+
+ // The default overload should compile:
+ ceph::util::randomize_rng();
+
+ {
+ int a = ceph::util::generate_random_number();
+ int b = ceph::util::generate_random_number();
+
+ /* Technically, this can still collide and cause a false negative, but let's
+ be optimistic: */
+ if (std::numeric_limits<int>::max() > 32767) {
+ ASSERT_NE(a, b);
+ }
+ }
+
+ // Check that the nullary version accepts different numeric types:
+ {
+ long def = ceph::util::generate_random_number();
+ long l = ceph::util::generate_random_number<long>();
+ int64_t i = ceph::util::generate_random_number<int64_t>();
+ double d = ceph::util::generate_random_number<double>();
+
+ swallow_values(def, l, i, d);
+ }
+
+ // (optimistically) Check that the nullary and unary versions never return < 0:
+ {
+ for(long i = 0; 1000000 != i; i++) {
+ ASSERT_LE(0, ceph::util::generate_random_number());
+ ASSERT_LE(0, ceph::util::generate_random_number(1));
+ ASSERT_LE(0, ceph::util::generate_random_number<float>(1.0));
+ }
+ }
+
+ {
+ auto a = ceph::util::generate_random_number(1, std::numeric_limits<int>::max());
+ auto b = ceph::util::generate_random_number(1, std::numeric_limits<int>::max());
+
+ if (std::numeric_limits<int>::max() > 32767) {
+ ASSERT_GT(a, 0);
+ ASSERT_GT(b, 0);
+
+ ASSERT_NE(a, b);
+ }
+ }
+
+ for (auto n = 100000; n; --n) {
+ int a = ceph::util::generate_random_number(0, 6);
+ ASSERT_GT(a, -1);
+ ASSERT_LT(a, 7);
+ }
+
+ // Check bounding on zero (checking appropriate value for zero compiles and works):
+ for (auto n = 10; n; --n) {
+ ASSERT_EQ(0, ceph::util::generate_random_number<int>(0, 0));
+ ASSERT_EQ(0, ceph::util::generate_random_number<float>(0.0, 0.0));
+ }
+
+ // Multiple types (integral):
+ {
+ int min = 0, max = 1;
+ type_check_ok(min, max);
+ }
+
+ {
+ long min = 0, max = 1l;
+ type_check_ok(min, max);
+ }
+
+ // Multiple types (floating point):
+ {
+ double min = 0.0, max = 1.0;
+ type_check_ok(min, max);
+ }
+
+ {
+ float min = 0.0, max = 1.0;
+ type_check_ok(min, max);
+ }
+
+ // When combining types, everything should convert to the largest type:
+ {
+ // Check with integral types:
+ {
+ int x = 0;
+ long long y = 1;
+
+ auto z = ceph::util::generate_random_number(x, y);
+
+ bool result = std::is_same_v<decltype(z), decltype(y)>;
+
+ ASSERT_TRUE(result);
+ }
+
+ // Check with floating-point types:
+ {
+ float x = 0.0;
+ long double y = 1.0;
+
+ auto z = ceph::util::generate_random_number(x, y);
+
+ bool result = std::is_same_v<decltype(z), decltype(y)>;
+
+ ASSERT_TRUE(result);
+ }
+
+ // It would be nice to have a test to check that mixing integral and floating point
+ // numbers should not compile, however we currently have no good way I know of
+ // to do such negative tests.
+ }
+}
+
+TEST(util, test_random_class_interface)
+{
+ ceph::util::random_number_generator<int> rng_i;
+ ceph::util::random_number_generator<float> rng_f;
+
+ // Other ctors:
+ {
+ ceph::util::random_number_generator<int> rng(1234); // seed
+ }
+
+ // Test deduction guides:
+ {
+ { ceph::util::random_number_generator rng(1234); }
+#pragma clang diagnostic push
+ // Turn this warning off, since we're checking that the deduction
+ // guide works. (And we don't know what the seed type will
+ // actually be.)
+#pragma clang diagnostic ignored "-Wliteral-conversion"
+ { ceph::util::random_number_generator rng(1234.1234); }
+#pragma clang diagnostic pop
+
+ {
+ int x = 1234;
+ ceph::util::random_number_generator rng(x);
+ }
+ }
+
+ {
+ int a = rng_i();
+ int b = rng_i();
+
+ // Technically can fail, but should "almost never" happen:
+ ASSERT_NE(a, b);
+ }
+
+ {
+ int a = rng_i(10);
+ ASSERT_LE(a, 10);
+ ASSERT_GE(a, 0);
+ }
+
+ {
+ float a = rng_f(10.0);
+ ASSERT_LE(a, 10.0);
+ ASSERT_GE(a, 0.0);
+ }
+
+ {
+ int a = rng_i(10, 20);
+ ASSERT_LE(a, 20);
+ ASSERT_GE(a, 10);
+ }
+
+ {
+ float a = rng_f(10.0, 20.0);
+ ASSERT_LE(a, 20.0);
+ ASSERT_GE(a, 10.0);
+ }
+}
+
diff --git a/src/test/common/test_safe_io.cc b/src/test/common/test_safe_io.cc
new file mode 100644
index 000000000..8b98a9655
--- /dev/null
+++ b/src/test/common/test_safe_io.cc
@@ -0,0 +1,37 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <algorithm>
+#include <cstring>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "common/safe_io.h"
+
+#include "gtest/gtest.h"
+
+
+TEST(SafeIO, safe_read_file) {
+ const char *fname = "safe_read_testfile";
+ ::unlink(fname);
+ int fd = ::open(fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ ASSERT_NE(fd, -1);
+ const char buf[] = "0123456789";
+ for (int i = 0; i < 8; i++) {
+ ASSERT_EQ((ssize_t)sizeof(buf), write(fd, buf, sizeof(buf)));
+ }
+ ::close(fd);
+ char rdata[80];
+ ASSERT_EQ((int)sizeof(rdata),
+ safe_read_file(".", fname, rdata, sizeof(rdata)));
+ for (char *p = rdata, *end = rdata+sizeof(rdata); p < end; p+=sizeof(buf)) {
+ ASSERT_EQ(0, std::memcmp(p, buf, std::min(size_t(end-p), sizeof(buf))));
+ }
+ ::unlink(fname);
+}
+
+// Local Variables:
+// compile-command: "cd ../.. ;
+// make unittest_safe_io &&
+// ./unittest_safe_io"
+// End:
diff --git a/src/test/common/test_shared_cache.cc b/src/test/common/test_shared_cache.cc
new file mode 100644
index 000000000..91120c7e5
--- /dev/null
+++ b/src/test/common/test_shared_cache.cc
@@ -0,0 +1,400 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ * Cheng Cheng <ccheng.leo@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include "gtest/gtest.h"
+#include "common/Thread.h"
+#include "common/shared_cache.hpp"
+
+using namespace std;
+
+class SharedLRUTest : public SharedLRU<unsigned int, int> {
+public:
+ auto& get_lock() { return lock; }
+ auto& get_cond() { return cond; }
+ map<unsigned int, pair< std::weak_ptr<int>, int* > > &get_weak_refs() {
+ return weak_refs;
+ }
+};
+
+class SharedLRU_all : public ::testing::Test {
+public:
+
+ class Thread_wait : public Thread {
+ public:
+ SharedLRUTest &cache;
+ unsigned int key;
+ int value;
+ std::shared_ptr<int> ptr;
+ enum in_method_t { LOOKUP, LOWER_BOUND } in_method;
+
+ Thread_wait(SharedLRUTest& _cache, unsigned int _key,
+ int _value, in_method_t _in_method) :
+ cache(_cache),
+ key(_key),
+ value(_value),
+ in_method(_in_method) { }
+
+ void * entry() override {
+ switch (in_method) {
+ case LOWER_BOUND:
+ ptr = cache.lower_bound(key);
+ break;
+ case LOOKUP:
+ ptr = std::shared_ptr<int>(new int);
+ *ptr = value;
+ ptr = cache.lookup(key);
+ break;
+ }
+ return NULL;
+ }
+ };
+
+ static const useconds_t DELAY_MAX = 20 * 1000 * 1000;
+ static useconds_t delay;
+
+ bool wait_for(SharedLRUTest &cache, int waitting) {
+ do {
+ //
+ // the delay variable is supposed to be initialized to zero. It would be fine
+ // to usleep(0) but we take this opportunity to test the loop. It will try
+ // again and therefore show that the logic ( increasing the delay ) actually
+ // works.
+ //
+ if (delay > 0)
+ usleep(delay);
+ {
+ std::lock_guard l{cache.get_lock()};
+ if (cache.waiting == waitting) {
+ break;
+ }
+ }
+ if (delay > 0) {
+ cout << "delay " << delay << "us, is not long enough, try again\n";
+ }
+ } while ((delay = delay * 2 + 1) < DELAY_MAX);
+ return delay < DELAY_MAX;
+ }
+};
+
+useconds_t SharedLRU_all::delay = 0;
+
+TEST_F(SharedLRU_all, add) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ int value1 = 2;
+ bool existed = false;
+ {
+ std::shared_ptr<int> ptr = cache.add(key, new int(value1), &existed);
+ ASSERT_EQ(value1, *ptr);
+ ASSERT_FALSE(existed);
+ }
+ {
+ int value2 = 3;
+ auto p = new int(value2);
+ std::shared_ptr<int> ptr = cache.add(key, p, &existed);
+ ASSERT_EQ(value1, *ptr);
+ ASSERT_TRUE(existed);
+ delete p;
+ }
+}
+TEST_F(SharedLRU_all, empty) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ bool existed = false;
+
+ ASSERT_TRUE(cache.empty());
+ {
+ int value1 = 2;
+ std::shared_ptr<int> ptr = cache.add(key, new int(value1), &existed);
+ ASSERT_EQ(value1, *ptr);
+ ASSERT_FALSE(existed);
+ }
+ ASSERT_FALSE(cache.empty());
+
+ cache.clear(key);
+ ASSERT_TRUE(cache.empty());
+}
+
+TEST_F(SharedLRU_all, lookup) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ {
+ int value = 2;
+ ASSERT_TRUE(cache.add(key, new int(value)).get());
+ ASSERT_TRUE(cache.lookup(key).get());
+ ASSERT_EQ(value, *cache.lookup(key));
+ }
+ ASSERT_TRUE(cache.lookup(key).get());
+}
+TEST_F(SharedLRU_all, lookup_or_create) {
+ SharedLRUTest cache;
+ {
+ int value = 2;
+ unsigned int key = 1;
+ ASSERT_TRUE(cache.add(key, new int(value)).get());
+ ASSERT_TRUE(cache.lookup_or_create(key).get());
+ ASSERT_EQ(value, *cache.lookup(key));
+ }
+ {
+ unsigned int key = 2;
+ ASSERT_TRUE(cache.lookup_or_create(key).get());
+ ASSERT_EQ(0, *cache.lookup(key));
+ }
+ ASSERT_TRUE(cache.lookup(1).get());
+ ASSERT_TRUE(cache.lookup(2).get());
+}
+
+TEST_F(SharedLRU_all, wait_lookup) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ int value = 2;
+
+ {
+ std::shared_ptr<int> ptr(new int);
+ cache.get_weak_refs()[key] = make_pair(ptr, &*ptr);
+ }
+ EXPECT_FALSE(cache.get_weak_refs()[key].first.lock());
+
+ Thread_wait t(cache, key, value, Thread_wait::LOOKUP);
+ t.create("wait_lookup_1");
+ ASSERT_TRUE(wait_for(cache, 1));
+ EXPECT_EQ(value, *t.ptr);
+ // waiting on a key does not block lookups on other keys
+ EXPECT_FALSE(cache.lookup(key + 12345));
+ {
+ std::lock_guard l{cache.get_lock()};
+ cache.get_weak_refs().erase(key);
+ cache.get_cond().notify_one();
+ }
+ ASSERT_TRUE(wait_for(cache, 0));
+ t.join();
+ EXPECT_FALSE(t.ptr);
+}
+TEST_F(SharedLRU_all, wait_lookup_or_create) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ int value = 2;
+
+ {
+ std::shared_ptr<int> ptr(new int);
+ cache.get_weak_refs()[key] = make_pair(ptr, &*ptr);
+ }
+ EXPECT_FALSE(cache.get_weak_refs()[key].first.lock());
+
+ Thread_wait t(cache, key, value, Thread_wait::LOOKUP);
+ t.create("wait_lookup_2");
+ ASSERT_TRUE(wait_for(cache, 1));
+ EXPECT_EQ(value, *t.ptr);
+ // waiting on a key does not block lookups on other keys
+ EXPECT_TRUE(cache.lookup_or_create(key + 12345).get());
+ {
+ std::lock_guard l{cache.get_lock()};
+ cache.get_weak_refs().erase(key);
+ cache.get_cond().notify_one();
+ }
+ ASSERT_TRUE(wait_for(cache, 0));
+ t.join();
+ EXPECT_FALSE(t.ptr);
+}
+
+TEST_F(SharedLRU_all, lower_bound) {
+ SharedLRUTest cache;
+
+ {
+ unsigned int key = 1;
+ ASSERT_FALSE(cache.lower_bound(key));
+ int value = 2;
+
+ ASSERT_TRUE(cache.add(key, new int(value)).get());
+ ASSERT_TRUE(cache.lower_bound(key).get());
+ EXPECT_EQ(value, *cache.lower_bound(key));
+ }
+}
+
+TEST_F(SharedLRU_all, wait_lower_bound) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ int value = 2;
+ unsigned int other_key = key + 1;
+ int other_value = value + 1;
+
+ ASSERT_TRUE(cache.add(other_key, new int(other_value)).get());
+
+ {
+ std::shared_ptr<int> ptr(new int);
+ cache.get_weak_refs()[key] = make_pair(ptr, &*ptr);
+ }
+ EXPECT_FALSE(cache.get_weak_refs()[key].first.lock());
+
+ Thread_wait t(cache, key, value, Thread_wait::LOWER_BOUND);
+ t.create("wait_lower_bnd");
+ ASSERT_TRUE(wait_for(cache, 1));
+ EXPECT_FALSE(t.ptr);
+ // waiting on a key does not block getting lower_bound on other keys
+ EXPECT_TRUE(cache.lower_bound(other_key).get());
+ {
+ std::lock_guard l{cache.get_lock()};
+ cache.get_weak_refs().erase(key);
+ cache.get_cond().notify_one();
+ }
+ ASSERT_TRUE(wait_for(cache, 0));
+ t.join();
+ EXPECT_TRUE(t.ptr.get());
+}
+TEST_F(SharedLRU_all, get_next) {
+
+ {
+ SharedLRUTest cache;
+ const unsigned int key = 0;
+ pair<unsigned int, int> i;
+ EXPECT_FALSE(cache.get_next(key, &i));
+ }
+ {
+ SharedLRUTest cache;
+
+ const unsigned int key2 = 333;
+ std::shared_ptr<int> ptr2 = cache.lookup_or_create(key2);
+ const int value2 = *ptr2 = 400;
+
+ // entries with expired pointers are silently ignored
+ const unsigned int key_gone = 222;
+ cache.get_weak_refs()[key_gone] = make_pair(std::shared_ptr<int>(), (int*)0);
+
+ const unsigned int key1 = 111;
+ std::shared_ptr<int> ptr1 = cache.lookup_or_create(key1);
+ const int value1 = *ptr1 = 800;
+
+ pair<unsigned int, int> i;
+ EXPECT_TRUE(cache.get_next(0, &i));
+ EXPECT_EQ(key1, i.first);
+ EXPECT_EQ(value1, i.second);
+
+ EXPECT_TRUE(cache.get_next(i.first, &i));
+ EXPECT_EQ(key2, i.first);
+ EXPECT_EQ(value2, i.second);
+
+ EXPECT_FALSE(cache.get_next(i.first, &i));
+
+ cache.get_weak_refs().clear();
+ }
+ {
+ SharedLRUTest cache;
+ const unsigned int key1 = 111;
+ std::shared_ptr<int> *ptr1 = new shared_ptr<int>(cache.lookup_or_create(key1));
+ const unsigned int key2 = 222;
+ std::shared_ptr<int> ptr2 = cache.lookup_or_create(key2);
+
+ pair<unsigned int, std::shared_ptr<int> > i;
+ EXPECT_TRUE(cache.get_next(i.first, &i));
+ EXPECT_EQ(key1, i.first);
+ delete ptr1;
+ EXPECT_TRUE(cache.get_next(i.first, &i));
+ EXPECT_EQ(key2, i.first);
+ }
+}
+
+TEST_F(SharedLRU_all, clear) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ int value = 2;
+ {
+ std::shared_ptr<int> ptr = cache.add(key, new int(value));
+ ASSERT_EQ(value, *cache.lookup(key));
+ }
+ ASSERT_TRUE(cache.lookup(key).get());
+ cache.clear(key);
+ ASSERT_FALSE(cache.lookup(key));
+
+ {
+ std::shared_ptr<int> ptr = cache.add(key, new int(value));
+ }
+ ASSERT_TRUE(cache.lookup(key).get());
+ cache.clear(key);
+ ASSERT_FALSE(cache.lookup(key));
+}
+TEST_F(SharedLRU_all, clear_all) {
+ SharedLRUTest cache;
+ unsigned int key = 1;
+ int value = 2;
+ {
+ std::shared_ptr<int> ptr = cache.add(key, new int(value));
+ ASSERT_EQ(value, *cache.lookup(key));
+ }
+ ASSERT_TRUE(cache.lookup(key).get());
+ cache.clear();
+ ASSERT_FALSE(cache.lookup(key));
+
+ std::shared_ptr<int> ptr2 = cache.add(key, new int(value));
+ ASSERT_TRUE(cache.lookup(key).get());
+ cache.clear();
+ ASSERT_TRUE(cache.lookup(key).get());
+ ASSERT_FALSE(cache.empty());
+}
+
+TEST(SharedCache_all, add) {
+ SharedLRU<int, int> cache;
+ unsigned int key = 1;
+ int value = 2;
+ std::shared_ptr<int> ptr = cache.add(key, new int(value));
+ ASSERT_EQ(ptr, cache.lookup(key));
+ ASSERT_EQ(value, *cache.lookup(key));
+}
+
+TEST(SharedCache_all, lru) {
+ const size_t SIZE = 5;
+ SharedLRU<int, int> cache(NULL, SIZE);
+
+ bool existed = false;
+ std::shared_ptr<int> ptr = cache.add(0, new int(0), &existed);
+ ASSERT_FALSE(existed);
+ {
+ int *tmpint = new int(0);
+ std::shared_ptr<int> ptr2 = cache.add(0, tmpint, &existed);
+ ASSERT_TRUE(existed);
+ delete tmpint;
+ }
+ for (size_t i = 1; i < 2*SIZE; ++i) {
+ cache.add(i, new int(i), &existed);
+ ASSERT_FALSE(existed);
+ }
+
+ ASSERT_TRUE(cache.lookup(0).get());
+ ASSERT_EQ(0, *cache.lookup(0));
+
+ ASSERT_FALSE(cache.lookup(SIZE-1));
+ ASSERT_FALSE(cache.lookup(SIZE));
+ ASSERT_TRUE(cache.lookup(SIZE+1).get());
+ ASSERT_EQ((int)SIZE+1, *cache.lookup(SIZE+1));
+
+ cache.purge(0);
+ ASSERT_FALSE(cache.lookup(0));
+ std::shared_ptr<int> ptr2 = cache.add(0, new int(0), &existed);
+ ASSERT_FALSE(ptr == ptr2);
+ ptr = std::shared_ptr<int>();
+ ASSERT_TRUE(cache.lookup(0).get());
+}
+
+// Local Variables:
+// compile-command: "cd ../.. ; make unittest_shared_cache && ./unittest_shared_cache # --gtest_filter=*.* --log-to-stderr=true"
+// End:
diff --git a/src/test/common/test_sharedptr_registry.cc b/src/test/common/test_sharedptr_registry.cc
new file mode 100644
index 000000000..9a856652e
--- /dev/null
+++ b/src/test/common/test_sharedptr_registry.cc
@@ -0,0 +1,330 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include "gtest/gtest.h"
+#include "common/Thread.h"
+#include "common/sharedptr_registry.hpp"
+#include "common/ceph_argparse.h"
+
+using namespace std;
+
+class SharedPtrRegistryTest : public SharedPtrRegistry<unsigned int, int> {
+public:
+ ceph::mutex &get_lock() { return lock; }
+ map<unsigned int, pair<std::weak_ptr<int>, int*> > &get_contents() {
+ return contents;
+ }
+};
+
+class SharedPtrRegistry_all : public ::testing::Test {
+public:
+
+ class Thread_wait : public Thread {
+ public:
+ SharedPtrRegistryTest &registry;
+ unsigned int key;
+ int value;
+ std::shared_ptr<int> ptr;
+ enum in_method_t { LOOKUP, LOOKUP_OR_CREATE } in_method;
+
+ Thread_wait(SharedPtrRegistryTest& _registry, unsigned int _key, int _value, in_method_t _in_method) :
+ registry(_registry),
+ key(_key),
+ value(_value),
+ in_method(_in_method)
+ {
+ }
+
+ void *entry() override {
+ switch(in_method) {
+ case LOOKUP_OR_CREATE:
+ if (value)
+ ptr = registry.lookup_or_create<int>(key, value);
+ else
+ ptr = registry.lookup_or_create(key);
+ break;
+ case LOOKUP:
+ ptr = std::shared_ptr<int>(new int);
+ *ptr = value;
+ ptr = registry.lookup(key);
+ break;
+ }
+ return NULL;
+ }
+ };
+
+ static const useconds_t DELAY_MAX = 20 * 1000 * 1000;
+ static useconds_t delay;
+
+ bool wait_for(SharedPtrRegistryTest &registry, int waiting) {
+ do {
+ //
+ // the delay variable is supposed to be initialized to zero. It would be fine
+ // to usleep(0) but we take this opportunity to test the loop. It will try
+ // again and therefore show that the logic ( increasing the delay ) actually
+ // works.
+ //
+ if (delay > 0)
+ usleep(delay);
+ {
+ std::lock_guard l(registry.get_lock());
+ if (registry.waiting == waiting)
+ break;
+ }
+ if (delay > 0)
+ cout << "delay " << delay << "us, is not long enough, try again\n";
+ } while (( delay = delay * 2 + 1) < DELAY_MAX);
+ return delay < DELAY_MAX;
+ }
+};
+
+useconds_t SharedPtrRegistry_all::delay = 0;
+
+TEST_F(SharedPtrRegistry_all, lookup_or_create) {
+ SharedPtrRegistryTest registry;
+ unsigned int key = 1;
+ int value = 2;
+ std::shared_ptr<int> ptr = registry.lookup_or_create(key);
+ *ptr = value;
+ ASSERT_EQ(value, *registry.lookup_or_create(key));
+}
+
+TEST_F(SharedPtrRegistry_all, wait_lookup_or_create) {
+ SharedPtrRegistryTest registry;
+
+ //
+ // simulate the following: The last reference to a shared_ptr goes
+ // out of scope and the shared_ptr object is about to be removed and
+ // marked as such. The weak_ptr stored in the registry will show
+ // that it has expired(). However, the SharedPtrRegistry::OnRemoval
+ // object has not yet been called and did not get a chance to
+ // acquire the lock. The lookup_or_create and lookup methods must
+ // detect that situation and wait until the weak_ptr is removed from
+ // the registry.
+ //
+ {
+ unsigned int key = 1;
+ {
+ std::shared_ptr<int> ptr(new int);
+ registry.get_contents()[key] = make_pair(ptr, ptr.get());
+ }
+ EXPECT_FALSE(registry.get_contents()[key].first.lock());
+
+ Thread_wait t(registry, key, 0, Thread_wait::LOOKUP_OR_CREATE);
+ t.create("wait_lookcreate");
+ ASSERT_TRUE(wait_for(registry, 1));
+ EXPECT_FALSE(t.ptr);
+ // waiting on a key does not block lookups on other keys
+ EXPECT_TRUE(registry.lookup_or_create(key + 12345).get());
+ registry.remove(key);
+ ASSERT_TRUE(wait_for(registry, 0));
+ t.join();
+ EXPECT_TRUE(t.ptr.get());
+ }
+ {
+ unsigned int key = 2;
+ int value = 3;
+ {
+ std::shared_ptr<int> ptr(new int);
+ registry.get_contents()[key] = make_pair(ptr, ptr.get());
+ }
+ EXPECT_FALSE(registry.get_contents()[key].first.lock());
+
+ Thread_wait t(registry, key, value, Thread_wait::LOOKUP_OR_CREATE);
+ t.create("wait_lookcreate");
+ ASSERT_TRUE(wait_for(registry, 1));
+ EXPECT_FALSE(t.ptr);
+ // waiting on a key does not block lookups on other keys
+ {
+ int other_value = value + 1;
+ unsigned int other_key = key + 1;
+ std::shared_ptr<int> ptr = registry.lookup_or_create<int>(other_key, other_value);
+ EXPECT_TRUE(ptr.get());
+ EXPECT_EQ(other_value, *ptr);
+ }
+ registry.remove(key);
+ ASSERT_TRUE(wait_for(registry, 0));
+ t.join();
+ EXPECT_TRUE(t.ptr.get());
+ EXPECT_EQ(value, *t.ptr);
+ }
+}
+
+TEST_F(SharedPtrRegistry_all, lookup) {
+ SharedPtrRegistryTest registry;
+ unsigned int key = 1;
+ {
+ std::shared_ptr<int> ptr = registry.lookup_or_create(key);
+ int value = 2;
+ *ptr = value;
+ ASSERT_EQ(value, *registry.lookup(key));
+ }
+ ASSERT_FALSE(registry.lookup(key));
+}
+
+TEST_F(SharedPtrRegistry_all, wait_lookup) {
+ SharedPtrRegistryTest registry;
+
+ unsigned int key = 1;
+ int value = 2;
+ {
+ std::shared_ptr<int> ptr(new int);
+ registry.get_contents()[key] = make_pair(ptr, ptr.get());
+ }
+ EXPECT_FALSE(registry.get_contents()[key].first.lock());
+
+ Thread_wait t(registry, key, value, Thread_wait::LOOKUP);
+ t.create("wait_lookup");
+ ASSERT_TRUE(wait_for(registry, 1));
+ EXPECT_EQ(value, *t.ptr);
+ // waiting on a key does not block lookups on other keys
+ EXPECT_FALSE(registry.lookup(key + 12345));
+ registry.remove(key);
+ ASSERT_TRUE(wait_for(registry, 0));
+ t.join();
+ EXPECT_FALSE(t.ptr);
+}
+
+TEST_F(SharedPtrRegistry_all, get_next) {
+
+ {
+ SharedPtrRegistry<unsigned int,int> registry;
+ const unsigned int key = 0;
+ pair<unsigned int, int> i;
+ EXPECT_FALSE(registry.get_next(key, &i));
+ }
+ {
+ SharedPtrRegistryTest registry;
+
+ const unsigned int key2 = 333;
+ std::shared_ptr<int> ptr2 = registry.lookup_or_create(key2);
+ const int value2 = *ptr2 = 400;
+
+ // entries with expired pointers are silentely ignored
+ const unsigned int key_gone = 222;
+ registry.get_contents()[key_gone] = make_pair(std::shared_ptr<int>(), (int*)0);
+
+ const unsigned int key1 = 111;
+ std::shared_ptr<int> ptr1 = registry.lookup_or_create(key1);
+ const int value1 = *ptr1 = 800;
+
+ pair<unsigned int, int> i;
+ EXPECT_TRUE(registry.get_next(i.first, &i));
+ EXPECT_EQ(key1, i.first);
+ EXPECT_EQ(value1, i.second);
+
+ EXPECT_TRUE(registry.get_next(i.first, &i));
+ EXPECT_EQ(key2, i.first);
+ EXPECT_EQ(value2, i.second);
+
+ EXPECT_FALSE(registry.get_next(i.first, &i));
+ }
+ {
+ //
+ // http://tracker.ceph.com/issues/6117
+ // reproduce the issue.
+ //
+ SharedPtrRegistryTest registry;
+ const unsigned int key1 = 111;
+ std::shared_ptr<int> *ptr1 = new std::shared_ptr<int>(registry.lookup_or_create(key1));
+ const unsigned int key2 = 222;
+ std::shared_ptr<int> ptr2 = registry.lookup_or_create(key2);
+
+ pair<unsigned int, std::shared_ptr<int> > i;
+ EXPECT_TRUE(registry.get_next(i.first, &i));
+ EXPECT_EQ(key1, i.first);
+ delete ptr1;
+ EXPECT_TRUE(registry.get_next(i.first, &i));
+ EXPECT_EQ(key2, i.first);
+ }
+}
+
+TEST_F(SharedPtrRegistry_all, remove) {
+ {
+ SharedPtrRegistryTest registry;
+ const unsigned int key1 = 1;
+ std::shared_ptr<int> ptr1 = registry.lookup_or_create(key1);
+ *ptr1 = 400;
+ registry.remove(key1);
+
+ std::shared_ptr<int> ptr2 = registry.lookup_or_create(key1);
+ *ptr2 = 500;
+
+ ptr1 = std::shared_ptr<int>();
+ std::shared_ptr<int> res = registry.lookup(key1);
+ ceph_assert(res);
+ ceph_assert(res == ptr2);
+ ceph_assert(*res == 500);
+ }
+ {
+ SharedPtrRegistryTest registry;
+ const unsigned int key1 = 1;
+ std::shared_ptr<int> ptr1 = registry.lookup_or_create(key1, 400);
+ registry.remove(key1);
+
+ std::shared_ptr<int> ptr2 = registry.lookup_or_create(key1, 500);
+
+ ptr1 = std::shared_ptr<int>();
+ std::shared_ptr<int> res = registry.lookup(key1);
+ ceph_assert(res);
+ ceph_assert(res == ptr2);
+ ceph_assert(*res == 500);
+ }
+}
+
+class SharedPtrRegistry_destructor : public ::testing::Test {
+public:
+
+ typedef enum { UNDEFINED, YES, NO } DieEnum;
+ static DieEnum died;
+
+ struct TellDie {
+ TellDie() { died = NO; }
+ ~TellDie() { died = YES; }
+
+ int value = 0;
+ };
+
+ void SetUp() override {
+ died = UNDEFINED;
+ }
+};
+
+SharedPtrRegistry_destructor::DieEnum SharedPtrRegistry_destructor::died = SharedPtrRegistry_destructor::UNDEFINED;
+
+TEST_F(SharedPtrRegistry_destructor, destructor) {
+ SharedPtrRegistry<int,TellDie> registry;
+ EXPECT_EQ(UNDEFINED, died);
+ int key = 101;
+ {
+ std::shared_ptr<TellDie> a = registry.lookup_or_create(key);
+ EXPECT_EQ(NO, died);
+ EXPECT_TRUE(a.get());
+ }
+ EXPECT_EQ(YES, died);
+ EXPECT_FALSE(registry.lookup(key));
+}
+
+// Local Variables:
+// compile-command: "cd ../.. ; make unittest_sharedptr_registry && ./unittest_sharedptr_registry # --gtest_filter=*.* --log-to-stderr=true"
+// End:
diff --git a/src/test/common/test_shunique_lock.cc b/src/test/common/test_shunique_lock.cc
new file mode 100644
index 000000000..b7696fe6e
--- /dev/null
+++ b/src/test/common/test_shunique_lock.cc
@@ -0,0 +1,575 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 &smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <future>
+#include <mutex>
+#include <shared_mutex>
+#include <thread>
+
+#include "common/ceph_time.h"
+#include "common/shunique_lock.h"
+
+#include "gtest/gtest.h"
+
+template<typename SharedMutex>
+static bool test_try_lock(SharedMutex* sm) {
+ if (!sm->try_lock())
+ return false;
+ sm->unlock();
+ return true;
+}
+
+template<typename SharedMutex>
+static bool test_try_lock_shared(SharedMutex* sm) {
+ if (!sm->try_lock_shared())
+ return false;
+ sm->unlock_shared();
+ return true;
+}
+
+template<typename SharedMutex, typename AcquireType>
+static void check_conflicts(SharedMutex sm, AcquireType) {
+}
+
+template<typename SharedMutex>
+static void ensure_conflicts(SharedMutex& sm, ceph::acquire_unique_t) {
+ auto ttl = &test_try_lock<std::shared_timed_mutex>;
+ auto ttls = &test_try_lock_shared<std::shared_timed_mutex>;
+ ASSERT_FALSE(std::async(std::launch::async, ttl, &sm).get());
+ ASSERT_FALSE(std::async(std::launch::async, ttls, &sm).get());
+}
+
+template<typename SharedMutex>
+static void ensure_conflicts(SharedMutex& sm, ceph::acquire_shared_t) {
+ auto ttl = &test_try_lock<std::shared_timed_mutex>;
+ auto ttls = &test_try_lock_shared<std::shared_timed_mutex>;
+ ASSERT_FALSE(std::async(std::launch::async, ttl, &sm).get());
+ ASSERT_TRUE(std::async(std::launch::async, ttls, &sm).get());
+}
+
+template<typename SharedMutex>
+static void ensure_free(SharedMutex& sm) {
+ auto ttl = &test_try_lock<std::shared_timed_mutex>;
+ auto ttls = &test_try_lock_shared<std::shared_timed_mutex>;
+ ASSERT_TRUE(std::async(std::launch::async, ttl, &sm).get());
+ ASSERT_TRUE(std::async(std::launch::async, ttls, &sm).get());
+}
+
+template<typename SharedMutex, typename AcquireType>
+static void check_owns_lock(const SharedMutex& sm,
+ const ceph::shunique_lock<SharedMutex>& sul,
+ AcquireType) {
+}
+
+template<typename SharedMutex>
+static void check_owns_lock(const SharedMutex& sm,
+ const ceph::shunique_lock<SharedMutex>& sul,
+ ceph::acquire_unique_t) {
+ ASSERT_TRUE(sul.mutex() == &sm);
+ ASSERT_TRUE(sul.owns_lock());
+ ASSERT_TRUE(!!sul);
+}
+
+template<typename SharedMutex>
+static void check_owns_lock(const SharedMutex& sm,
+ const ceph::shunique_lock<SharedMutex>& sul,
+ ceph::acquire_shared_t) {
+ ASSERT_TRUE(sul.owns_lock_shared());
+ ASSERT_TRUE(!!sul);
+}
+
+template<typename SharedMutex>
+static void check_abjures_lock(const SharedMutex& sm,
+ const ceph::shunique_lock<SharedMutex>& sul) {
+ ASSERT_EQ(sul.mutex(), &sm);
+ ASSERT_FALSE(sul.owns_lock());
+ ASSERT_FALSE(sul.owns_lock_shared());
+ ASSERT_FALSE(!!sul);
+}
+
+template<typename SharedMutex>
+static void check_abjures_lock(const ceph::shunique_lock<SharedMutex>& sul) {
+ ASSERT_EQ(sul.mutex(), nullptr);
+ ASSERT_FALSE(sul.owns_lock());
+ ASSERT_FALSE(sul.owns_lock_shared());
+ ASSERT_FALSE(!!sul);
+}
+
+TEST(ShuniqueLock, DefaultConstructor) {
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ shunique_lock l;
+
+ ASSERT_EQ(l.mutex(), nullptr);
+ ASSERT_FALSE(l.owns_lock());
+ ASSERT_FALSE(!!l);
+
+ ASSERT_THROW(l.lock(), std::system_error);
+ ASSERT_THROW(l.try_lock(), std::system_error);
+
+ ASSERT_THROW(l.lock_shared(), std::system_error);
+ ASSERT_THROW(l.try_lock_shared(), std::system_error);
+
+ ASSERT_THROW(l.unlock(), std::system_error);
+
+ ASSERT_EQ(l.mutex(), nullptr);
+ ASSERT_FALSE(l.owns_lock());
+ ASSERT_FALSE(!!l);
+
+ ASSERT_EQ(l.release(), nullptr);
+
+ ASSERT_EQ(l.mutex(), nullptr);
+ ASSERT_FALSE(l.owns_lock());
+ ASSERT_FALSE(l.owns_lock_shared());
+ ASSERT_FALSE(!!l);
+}
+
+template<typename AcquireType>
+void lock_unlock(AcquireType at) {
+ std::shared_timed_mutex sm;
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ shunique_lock l(sm, at);
+
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+
+ l.unlock();
+
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+
+ l.lock(at);
+
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+}
+
+TEST(ShuniqueLock, LockUnlock) {
+ lock_unlock(ceph::acquire_unique);
+ lock_unlock(ceph::acquire_shared);
+}
+
+template<typename AcquireType>
+void lock_destruct(AcquireType at) {
+ std::shared_timed_mutex sm;
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, at);
+
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+ }
+
+ ensure_free(sm);
+}
+
+TEST(ShuniqueLock, LockDestruct) {
+ lock_destruct(ceph::acquire_unique);
+ lock_destruct(ceph::acquire_shared);
+}
+
+template<typename AcquireType>
+void move_construct(AcquireType at) {
+ std::shared_timed_mutex sm;
+
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, at);
+
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+
+ shunique_lock o(std::move(l));
+
+ check_abjures_lock(l);
+
+ check_owns_lock(sm, o, at);
+ ensure_conflicts(sm, at);
+
+ o.unlock();
+
+ shunique_lock c(std::move(o));
+
+
+ ASSERT_EQ(o.mutex(), nullptr);
+ ASSERT_FALSE(!!o);
+
+ check_abjures_lock(sm, c);
+
+ ensure_free(sm);
+ }
+}
+
+TEST(ShuniqueLock, MoveConstruct) {
+ move_construct(ceph::acquire_unique);
+ move_construct(ceph::acquire_shared);
+
+ std::shared_timed_mutex sm;
+ {
+ std::unique_lock<std::shared_timed_mutex> ul(sm);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ ceph::shunique_lock<std::shared_timed_mutex> l(std::move(ul));
+ check_owns_lock(sm, l, ceph::acquire_unique);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ }
+ {
+ std::unique_lock<std::shared_timed_mutex> ul(sm, std::defer_lock);
+ ensure_free(sm);
+ ceph::shunique_lock<std::shared_timed_mutex> l(std::move(ul));
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+ }
+ {
+ std::unique_lock<std::shared_timed_mutex> ul;
+ ceph::shunique_lock<std::shared_timed_mutex> l(std::move(ul));
+ check_abjures_lock(l);
+ }
+ {
+ std::shared_lock<std::shared_timed_mutex> sl(sm);
+ ensure_conflicts(sm, ceph::acquire_shared);
+ ceph::shunique_lock<std::shared_timed_mutex> l(std::move(sl));
+ check_owns_lock(sm, l, ceph::acquire_shared);
+ ensure_conflicts(sm, ceph::acquire_shared);
+ }
+ {
+ std::shared_lock<std::shared_timed_mutex> sl;
+ ceph::shunique_lock<std::shared_timed_mutex> l(std::move(sl));
+ check_abjures_lock(l);
+ }
+}
+
+template<typename AcquireType>
+void move_assign(AcquireType at) {
+ std::shared_timed_mutex sm;
+
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, at);
+
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+
+ shunique_lock o;
+
+ o = std::move(l);
+
+ check_abjures_lock(l);
+
+ check_owns_lock(sm, o, at);
+ ensure_conflicts(sm, at);
+
+ o.unlock();
+
+ shunique_lock c(std::move(o));
+
+ check_abjures_lock(o);
+ check_abjures_lock(sm, c);
+
+ ensure_free(sm);
+
+ shunique_lock k;
+
+ c = std::move(k);
+
+ check_abjures_lock(k);
+ check_abjures_lock(c);
+
+ ensure_free(sm);
+ }
+}
+
+TEST(ShuniqueLock, MoveAssign) {
+ move_assign(ceph::acquire_unique);
+ move_assign(ceph::acquire_shared);
+
+ std::shared_timed_mutex sm;
+ {
+ std::unique_lock<std::shared_timed_mutex> ul(sm);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ ceph::shunique_lock<std::shared_timed_mutex> l;
+ l = std::move(ul);
+ check_owns_lock(sm, l, ceph::acquire_unique);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ }
+ {
+ std::unique_lock<std::shared_timed_mutex> ul(sm, std::defer_lock);
+ ensure_free(sm);
+ ceph::shunique_lock<std::shared_timed_mutex> l;
+ l = std::move(ul);
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+ }
+ {
+ std::unique_lock<std::shared_timed_mutex> ul;
+ ceph::shunique_lock<std::shared_timed_mutex> l;
+ l = std::move(ul);
+ check_abjures_lock(l);
+ }
+ {
+ std::shared_lock<std::shared_timed_mutex> sl(sm);
+ ensure_conflicts(sm, ceph::acquire_shared);
+ ceph::shunique_lock<std::shared_timed_mutex> l;
+ l = std::move(sl);
+ check_owns_lock(sm, l, ceph::acquire_shared);
+ ensure_conflicts(sm, ceph::acquire_shared);
+ }
+ {
+ std::shared_lock<std::shared_timed_mutex> sl;
+ ceph::shunique_lock<std::shared_timed_mutex> l;
+ l = std::move(sl);
+ check_abjures_lock(l);
+ }
+
+}
+
+template<typename AcquireType>
+void construct_deferred(AcquireType at) {
+ std::shared_timed_mutex sm;
+
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, std::defer_lock);
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+
+ ASSERT_THROW(l.unlock(), std::system_error);
+
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+
+ l.lock(at);
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+ }
+
+ {
+ shunique_lock l(sm, std::defer_lock);
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+
+ ASSERT_THROW(l.unlock(), std::system_error);
+
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+ }
+ ensure_free(sm);
+}
+
+TEST(ShuniqueLock, ConstructDeferred) {
+ construct_deferred(ceph::acquire_unique);
+ construct_deferred(ceph::acquire_shared);
+}
+
+template<typename AcquireType>
+void construct_try(AcquireType at) {
+ std::shared_timed_mutex sm;
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, at, std::try_to_lock);
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+ }
+
+ {
+ std::unique_lock<std::shared_timed_mutex> l(sm);
+ ensure_conflicts(sm, ceph::acquire_unique);
+
+ std::async(std::launch::async, [&sm, at]() {
+ shunique_lock l(sm, at, std::try_to_lock);
+ check_abjures_lock(sm, l);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ }).get();
+
+ l.unlock();
+
+ std::async(std::launch::async, [&sm, at]() {
+ shunique_lock l(sm, at, std::try_to_lock);
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+ }).get();
+ }
+}
+
+TEST(ShuniqueLock, ConstructTry) {
+ construct_try(ceph::acquire_unique);
+ construct_try(ceph::acquire_shared);
+}
+
+template<typename AcquireType>
+void construct_adopt(AcquireType at) {
+ std::shared_timed_mutex sm;
+
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock d(sm, at);
+ d.release();
+ }
+
+ ensure_conflicts(sm, at);
+
+ {
+ shunique_lock l(sm, at, std::adopt_lock);
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+ }
+
+ ensure_free(sm);
+}
+
+TEST(ShuniqueLock, ConstructAdopt) {
+ construct_adopt(ceph::acquire_unique);
+ construct_adopt(ceph::acquire_shared);
+}
+
+template<typename AcquireType>
+void try_lock(AcquireType at) {
+ std::shared_timed_mutex sm;
+
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, std::defer_lock);
+ l.try_lock(at);
+
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+ }
+
+ {
+ std::unique_lock<std::shared_timed_mutex> l(sm);
+
+ std::async(std::launch::async, [&sm, at]() {
+ shunique_lock l(sm, std::defer_lock);
+ l.try_lock(at);
+
+ check_abjures_lock(sm, l);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ }).get();
+
+
+ l.unlock();
+ std::async(std::launch::async, [&sm, at]() {
+ shunique_lock l(sm, std::defer_lock);
+ l.try_lock(at);
+
+ check_owns_lock(sm, l, at);
+ ensure_conflicts(sm, at);
+ }).get();
+ }
+}
+
+TEST(ShuniqueLock, TryLock) {
+ try_lock(ceph::acquire_unique);
+ try_lock(ceph::acquire_shared);
+}
+
+TEST(ShuniqueLock, Release) {
+ std::shared_timed_mutex sm;
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, ceph::acquire_unique);
+ check_owns_lock(sm, l, ceph::acquire_unique);
+ ensure_conflicts(sm, ceph::acquire_unique);
+
+ l.release();
+ check_abjures_lock(l);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ }
+ ensure_conflicts(sm, ceph::acquire_unique);
+ sm.unlock();
+ ensure_free(sm);
+
+ {
+ shunique_lock l(sm, ceph::acquire_shared);
+ check_owns_lock(sm, l, ceph::acquire_shared);
+ ensure_conflicts(sm, ceph::acquire_shared);
+
+ l.release();
+ check_abjures_lock(l);
+ ensure_conflicts(sm, ceph::acquire_shared);
+ }
+ ensure_conflicts(sm, ceph::acquire_shared);
+ sm.unlock_shared();
+ ensure_free(sm);
+
+ sm.lock();
+ {
+ shunique_lock l(sm, std::defer_lock);
+ check_abjures_lock(sm, l);
+ ensure_conflicts(sm, ceph::acquire_unique);
+
+ l.release();
+ check_abjures_lock(l);
+ ensure_conflicts(sm, ceph::acquire_unique);
+ }
+ ensure_conflicts(sm, ceph::acquire_unique);
+ sm.unlock();
+
+ ensure_free(sm);
+
+ {
+ std::unique_lock<std::shared_timed_mutex> ul;
+ shunique_lock l(sm, std::defer_lock);
+ check_abjures_lock(sm, l);
+ ensure_free(sm);
+
+ ASSERT_NO_THROW(ul = l.release_to_unique());
+ check_abjures_lock(l);
+ ASSERT_EQ(ul.mutex(), &sm);
+ ASSERT_FALSE(ul.owns_lock());
+ ensure_free(sm);
+ }
+ ensure_free(sm);
+
+ {
+ std::unique_lock<std::shared_timed_mutex> ul;
+ shunique_lock l;
+ check_abjures_lock(l);
+
+ ASSERT_NO_THROW(ul = l.release_to_unique());
+ check_abjures_lock(l);
+ ASSERT_EQ(ul.mutex(), nullptr);
+ ASSERT_FALSE(ul.owns_lock());
+ }
+}
+
+TEST(ShuniqueLock, NoRecursion) {
+ std::shared_timed_mutex sm;
+
+ typedef ceph::shunique_lock<std::shared_timed_mutex> shunique_lock;
+
+ {
+ shunique_lock l(sm, ceph::acquire_unique);
+ ASSERT_THROW(l.lock(), std::system_error);
+ ASSERT_THROW(l.try_lock(), std::system_error);
+ ASSERT_THROW(l.lock_shared(), std::system_error);
+ ASSERT_THROW(l.try_lock_shared(), std::system_error);
+ }
+
+ {
+ shunique_lock l(sm, ceph::acquire_shared);
+ ASSERT_THROW(l.lock(), std::system_error);
+ ASSERT_THROW(l.try_lock(), std::system_error);
+ ASSERT_THROW(l.lock_shared(), std::system_error);
+ ASSERT_THROW(l.try_lock_shared(), std::system_error);
+ }
+}
diff --git a/src/test/common/test_sloppy_crc_map.cc b/src/test/common/test_sloppy_crc_map.cc
new file mode 100644
index 000000000..9d9130cb7
--- /dev/null
+++ b/src/test/common/test_sloppy_crc_map.cc
@@ -0,0 +1,116 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+
+#include "common/SloppyCRCMap.h"
+#include "common/Formatter.h"
+#include <gtest/gtest.h>
+
+using namespace std;
+
+void dump(const SloppyCRCMap& scm)
+{
+ auto f = Formatter::create_unique("json-pretty");
+ f->open_object_section("map");
+ scm.dump(f.get());
+ f->close_section();
+ f->flush(cout);
+}
+
+TEST(SloppyCRCMap, basic) {
+ SloppyCRCMap scm(4);
+
+ bufferlist a, b;
+ a.append("The quick brown fox jumped over a fence whose color I forget.");
+ b.append("asdf");
+
+ scm.write(0, a.length(), a);
+ if (0)
+ dump(scm);
+ ASSERT_EQ(0, scm.read(0, a.length(), a, &cout));
+
+ scm.write(12, b.length(), b);
+ if (0)
+ dump(scm);
+
+ ASSERT_EQ(0, scm.read(12, b.length(), b, &cout));
+ ASSERT_EQ(1, scm.read(0, a.length(), a, &cout));
+}
+
+TEST(SloppyCRCMap, truncate) {
+ SloppyCRCMap scm(4);
+
+ bufferlist a, b;
+ a.append("asdf");
+ b.append("qwer");
+
+ scm.write(0, a.length(), a);
+ scm.write(4, a.length(), a);
+ ASSERT_EQ(0, scm.read(4, 4, a, &cout));
+ ASSERT_EQ(1, scm.read(4, 4, b, &cout));
+ scm.truncate(4);
+ ASSERT_EQ(0, scm.read(4, 4, b, &cout));
+}
+
+TEST(SloppyCRCMap, zero) {
+ SloppyCRCMap scm(4);
+
+ bufferlist a, b;
+ a.append("asdf");
+ b.append("qwer");
+
+ scm.write(0, a.length(), a);
+ scm.write(4, a.length(), a);
+ ASSERT_EQ(0, scm.read(4, 4, a, &cout));
+ ASSERT_EQ(1, scm.read(4, 4, b, &cout));
+ scm.zero(4, 4);
+ ASSERT_EQ(1, scm.read(4, 4, a, &cout));
+ ASSERT_EQ(1, scm.read(4, 4, b, &cout));
+
+ bufferptr bp(4);
+ bp.zero();
+ bufferlist c;
+ c.append(bp);
+ ASSERT_EQ(0, scm.read(0, 4, a, &cout));
+ ASSERT_EQ(0, scm.read(4, 4, c, &cout));
+ scm.zero(0, 15);
+ ASSERT_EQ(1, scm.read(0, 4, a, &cout));
+ ASSERT_EQ(0, scm.read(0, 4, c, &cout));
+}
+
+TEST(SloppyCRCMap, clone_range) {
+ SloppyCRCMap src(4);
+ SloppyCRCMap dst(4);
+
+ bufferlist a, b;
+ a.append("asdfghjkl");
+ b.append("qwertyui");
+
+ src.write(0, a.length(), a);
+ src.write(8, a.length(), a);
+ src.write(16, a.length(), a);
+
+ dst.write(0, b.length(), b);
+ dst.clone_range(0, 8, 0, src);
+ ASSERT_EQ(2, dst.read(0, 8, b, &cout));
+ ASSERT_EQ(0, dst.read(8, 8, b, &cout));
+
+ dst.write(16, b.length(), b);
+ ASSERT_EQ(2, dst.read(16, 8, a, &cout));
+ dst.clone_range(16, 8, 16, src);
+ ASSERT_EQ(0, dst.read(16, 8, a, &cout));
+
+ dst.write(16, b.length(), b);
+ ASSERT_EQ(1, dst.read(16, 4, a, &cout));
+ dst.clone_range(16, 8, 2, src);
+ ASSERT_EQ(0, dst.read(16, 4, a, &cout));
+
+ dst.write(0, b.length(), b);
+ dst.write(8, b.length(), b);
+ ASSERT_EQ(2, dst.read(0, 8, a, &cout));
+ ASSERT_EQ(2, dst.read(8, 8, a, &cout));
+ dst.clone_range(2, 8, 0, src);
+ ASSERT_EQ(0, dst.read(0, 8, a, &cout));
+ ASSERT_EQ(0, dst.read(8, 4, a, &cout));
+}
diff --git a/src/test/common/test_split.cc b/src/test/common/test_split.cc
new file mode 100644
index 000000000..285dea752
--- /dev/null
+++ b/src/test/common/test_split.cc
@@ -0,0 +1,119 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/split.h"
+#include <algorithm>
+#include <gtest/gtest.h>
+
+namespace ceph {
+
+using string_list = std::initializer_list<std::string_view>;
+
+bool operator==(const split& lhs, const string_list& rhs) {
+ return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+}
+bool operator==(const string_list& lhs, const split& rhs) {
+ return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+}
+
+TEST(split, split)
+{
+ EXPECT_EQ(string_list({}), split(""));
+ EXPECT_EQ(string_list({}), split(","));
+ EXPECT_EQ(string_list({}), split(",;"));
+
+ EXPECT_EQ(string_list({"a"}), split("a,;"));
+ EXPECT_EQ(string_list({"a"}), split(",a;"));
+ EXPECT_EQ(string_list({"a"}), split(",;a"));
+
+ EXPECT_EQ(string_list({"a", "b"}), split("a,b;"));
+ EXPECT_EQ(string_list({"a", "b"}), split("a,;b"));
+ EXPECT_EQ(string_list({"a", "b"}), split(",a;b"));
+}
+
+TEST(split, iterator_indirection)
+{
+ const auto parts = split("a,b");
+ auto i = parts.begin();
+ ASSERT_NE(i, parts.end());
+ EXPECT_EQ("a", *i); // test operator*
+}
+
+TEST(split, iterator_dereference)
+{
+ const auto parts = split("a,b");
+ auto i = parts.begin();
+ ASSERT_NE(i, parts.end());
+ EXPECT_EQ(1, i->size()); // test operator->
+}
+
+TEST(split, iterator_pre_increment)
+{
+ const auto parts = split("a,b");
+ auto i = parts.begin();
+ ASSERT_NE(i, parts.end());
+
+ ASSERT_EQ("a", *i);
+ EXPECT_EQ("b", *++i); // test operator++()
+ EXPECT_EQ("b", *i);
+}
+
+TEST(split, iterator_post_increment)
+{
+ const auto parts = split("a,b");
+ auto i = parts.begin();
+ ASSERT_NE(i, parts.end());
+
+ ASSERT_EQ("a", *i);
+ EXPECT_EQ("a", *i++); // test operator++(int)
+ ASSERT_NE(parts.end(), i);
+ EXPECT_EQ("b", *i);
+}
+
+TEST(split, iterator_singular)
+{
+ const auto parts = split("a,b");
+ auto i = parts.begin();
+
+ // test comparions against default-constructed 'singular' iterators
+ split::iterator j;
+ split::iterator k;
+ EXPECT_EQ(j, parts.end()); // singular == end
+ EXPECT_EQ(j, k); // singular == singular
+ EXPECT_NE(j, i); // singular != valid
+}
+
+TEST(split, iterator_multipass)
+{
+ const auto parts = split("a,b");
+ auto i = parts.begin();
+ ASSERT_NE(i, parts.end());
+
+ // copy the iterator to test LegacyForwardIterator's multipass guarantee
+ auto j = i;
+ ASSERT_EQ(i, j);
+
+ ASSERT_EQ("a", *i);
+ ASSERT_NE(parts.end(), ++i);
+ EXPECT_EQ("b", *i);
+
+ ASSERT_EQ("a", *j); // test that ++i left j unmodified
+ ASSERT_NE(parts.end(), ++j);
+ EXPECT_EQ("b", *j);
+
+ EXPECT_EQ(i, j);
+}
+
+} // namespace ceph
diff --git a/src/test/common/test_static_ptr.cc b/src/test/common/test_static_ptr.cc
new file mode 100644
index 000000000..0a4073e75
--- /dev/null
+++ b/src/test/common/test_static_ptr.cc
@@ -0,0 +1,216 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <compare>
+#include <gtest/gtest.h>
+#include "common/static_ptr.h"
+
+using ceph::static_ptr;
+using ceph::make_static;
+
+class base {
+public:
+ virtual int func() = 0;
+ virtual ~base() = default;
+};
+
+class sibling1 : public base {
+public:
+ int func() override { return 0; }
+};
+
+class sibling2 : public base {
+public:
+ int func() override { return 9; }
+ virtual int call(int) = 0;
+};
+
+class grandchild : public sibling2 {
+protected:
+ int val;
+public:
+ explicit grandchild(int val) : val(val) {}
+ virtual int call(int n) override { return n * val; }
+};
+
+class great_grandchild : public grandchild {
+public:
+ explicit great_grandchild(int val) : grandchild(val) {}
+ int call(int n) override { return n + val; }
+};
+
+#ifdef __cpp_lib_three_way_comparison
+TEST(StaticPtr, EmptyCreation) {
+ static_ptr<base, sizeof(grandchild)> p;
+ EXPECT_FALSE(p);
+ EXPECT_EQ(p, nullptr);
+ EXPECT_EQ(nullptr, p);
+ EXPECT_TRUE(p.get() == nullptr);
+}
+
+TEST(StaticPtr, CreationCall) {
+ {
+ static_ptr<base, sizeof(grandchild)> p(std::in_place_type_t<sibling1>{});
+ EXPECT_TRUE(p);
+ EXPECT_FALSE(p == nullptr);
+ EXPECT_FALSE(nullptr == p);
+ EXPECT_FALSE(p.get() == nullptr);
+ EXPECT_EQ(p->func(), 0);
+ EXPECT_EQ((*p).func(), 0);
+ EXPECT_EQ((p.get())->func(), 0);
+ }
+ {
+ auto p = make_static<base, sibling1>();
+ EXPECT_TRUE(p);
+ EXPECT_FALSE(p == nullptr);
+ EXPECT_FALSE(nullptr == p);
+ EXPECT_FALSE(p.get() == nullptr);
+ EXPECT_EQ(p->func(), 0);
+ EXPECT_EQ((*p).func(), 0);
+ EXPECT_EQ((p.get())->func(), 0);
+ }
+}
+
+TEST(StaticPtr, CreateReset) {
+ {
+ static_ptr<base, sizeof(grandchild)> p(std::in_place_type_t<sibling1>{});
+ EXPECT_EQ((p.get())->func(), 0);
+ p.reset();
+ EXPECT_FALSE(p);
+ EXPECT_EQ(p, nullptr);
+ EXPECT_EQ(nullptr, p);
+ EXPECT_TRUE(p.get() == nullptr);
+ }
+ {
+ static_ptr<base, sizeof(grandchild)> p(std::in_place_type_t<sibling1>{});
+ EXPECT_EQ((p.get())->func(), 0);
+ p = nullptr;
+ EXPECT_FALSE(p);
+ EXPECT_EQ(p, nullptr);
+ EXPECT_EQ(nullptr, p);
+ EXPECT_TRUE(p.get() == nullptr);
+ }
+}
+#endif // __cpp_lib_three_way_comparison
+
+TEST(StaticPtr, CreateEmplace) {
+ static_ptr<base, sizeof(grandchild)> p(std::in_place_type_t<sibling1>{});
+ EXPECT_EQ((p.get())->func(), 0);
+ p.emplace<grandchild>(30);
+ EXPECT_EQ(p->func(), 9);
+}
+
+TEST(StaticPtr, Move) {
+ // Won't compile. Good.
+ // static_ptr<base, sizeof(base)> p1(std::in_place_type_t<grandchild>{}, 3);
+
+ static_ptr<base, sizeof(base)> p1(std::in_place_type_t<sibling1>{});
+ static_ptr<base, sizeof(grandchild)> p2(std::in_place_type_t<grandchild>{},
+ 3);
+
+ p2 = std::move(p1);
+ EXPECT_EQ(p1->func(), 0);
+}
+
+TEST(StaticPtr, ImplicitUpcast) {
+ static_ptr<base, sizeof(grandchild)> p1;
+ static_ptr<sibling2, sizeof(grandchild)> p2(std::in_place_type_t<grandchild>{}, 3);
+
+ p1 = std::move(p2);
+ EXPECT_EQ(p1->func(), 9);
+
+ p2.reset();
+
+ // Doesn't compile. Good.
+ // p2 = p1;
+}
+
+TEST(StaticPtr, StaticCast) {
+ static_ptr<base, sizeof(grandchild)> p1(std::in_place_type_t<grandchild>{}, 3);
+ static_ptr<sibling2, sizeof(grandchild)> p2;
+
+ p2 = ceph::static_pointer_cast<sibling2, sizeof(grandchild)>(std::move(p1));
+ EXPECT_EQ(p2->func(), 9);
+ EXPECT_EQ(p2->call(10), 30);
+}
+
+TEST(StaticPtr, DynamicCast) {
+ static constexpr auto sz = sizeof(great_grandchild);
+ {
+ static_ptr<base, sz> p1(std::in_place_type_t<grandchild>{}, 3);
+ auto p2 = ceph::dynamic_pointer_cast<great_grandchild, sz>(std::move(p1));
+ EXPECT_FALSE(p2);
+ }
+
+ {
+ static_ptr<base, sz> p1(std::in_place_type_t<grandchild>{}, 3);
+ auto p2 = ceph::dynamic_pointer_cast<grandchild, sz>(std::move(p1));
+ EXPECT_TRUE(p2);
+ EXPECT_EQ(p2->func(), 9);
+ EXPECT_EQ(p2->call(10), 30);
+ }
+}
+
+class constable {
+public:
+ int foo() {
+ return 2;
+ }
+ int foo() const {
+ return 5;
+ }
+};
+
+TEST(StaticPtr, ConstCast) {
+ static constexpr auto sz = sizeof(constable);
+ {
+ auto p1 = make_static<const constable>();
+ EXPECT_EQ(p1->foo(), 5);
+ auto p2 = ceph::const_pointer_cast<constable, sz>(std::move(p1));
+ static_assert(!std::is_const<decltype(p2)::element_type>{},
+ "Things are more const than they ought to be.");
+ EXPECT_TRUE(p2);
+ EXPECT_EQ(p2->foo(), 2);
+ }
+}
+
+TEST(StaticPtr, ReinterpretCast) {
+ static constexpr auto sz = sizeof(grandchild);
+ {
+ auto p1 = make_static<grandchild>(3);
+ auto p2 = ceph::reinterpret_pointer_cast<constable, sz>(std::move(p1));
+ static_assert(std::is_same<decltype(p2)::element_type, constable>{},
+ "Reinterpret is screwy.");
+ auto p3 = ceph::reinterpret_pointer_cast<grandchild, sz>(std::move(p2));
+ static_assert(std::is_same<decltype(p3)::element_type, grandchild>{},
+ "Reinterpret is screwy.");
+ EXPECT_EQ(p3->func(), 9);
+ EXPECT_EQ(p3->call(10), 30);
+ }
+}
+
+struct exceptional {
+ exceptional() = default;
+ exceptional(const exceptional& e) {
+ throw std::exception();
+ }
+ exceptional(exceptional&& e) {
+ throw std::exception();
+ }
+};
+
+TEST(StaticPtr, Exceptional) {
+ static_ptr<exceptional> p1(std::in_place_type_t<exceptional>{});
+ EXPECT_ANY_THROW(static_ptr<exceptional> p2(std::move(p1)));
+}
diff --git a/src/test/common/test_str_map.cc b/src/test/common/test_str_map.cc
new file mode 100644
index 000000000..b61739e8f
--- /dev/null
+++ b/src/test/common/test_str_map.cc
@@ -0,0 +1,89 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include "include/str_map.h"
+
+using namespace std;
+
+TEST(str_map, json) {
+ map<string,string> str_map;
+ stringstream ss;
+ // well formatted
+ ASSERT_EQ(0, get_json_str_map("{\"key\": \"value\"}", ss, &str_map));
+ ASSERT_EQ("value", str_map["key"]);
+ // well formatted but not a JSON object
+ ASSERT_EQ(-EINVAL, get_json_str_map("\"key\"", ss, &str_map));
+ ASSERT_NE(string::npos, ss.str().find("must be a JSON object"));
+}
+
+TEST(str_map, plaintext) {
+ {
+ map<string,string> str_map;
+ ASSERT_EQ(0, get_str_map(" foo=bar\t\nfrob=nitz yeah right= \n\t",
+ &str_map));
+ ASSERT_EQ(4u, str_map.size());
+ ASSERT_EQ("bar", str_map["foo"]);
+ ASSERT_EQ("nitz", str_map["frob"]);
+ ASSERT_EQ("", str_map["yeah"]);
+ ASSERT_EQ("", str_map["right"]);
+ }
+ {
+ map<string,string> str_map;
+ ASSERT_EQ(0, get_str_map("that", &str_map));
+ ASSERT_EQ(1u, str_map.size());
+ ASSERT_EQ("", str_map["that"]);
+ }
+ {
+ map<string,string> str_map;
+ ASSERT_EQ(0, get_str_map(" \t \n ", &str_map));
+ ASSERT_EQ(0u, str_map.size());
+ ASSERT_EQ(0, get_str_map("", &str_map));
+ ASSERT_EQ(0u, str_map.size());
+ }
+ {
+ map<string,string> str_map;
+ ASSERT_EQ(0, get_str_map(" key1=val1; key2=\tval2; key3\t = \t val3; \n ", &str_map, "\n;"));
+ ASSERT_EQ(4u, str_map.size());
+ ASSERT_EQ("val1", str_map["key1"]);
+ ASSERT_EQ("val2", str_map["key2"]);
+ ASSERT_EQ("val3", str_map["key3"]);
+ }
+}
+
+TEST(str_map, empty_values) {
+ {
+ map<string,string> str_map;
+ ASSERT_EQ(0, get_str_map("M= P= L=",
+ &str_map));
+ ASSERT_EQ(3u, str_map.size());
+ ASSERT_EQ("", str_map["M"]);
+ ASSERT_EQ("", str_map["P"]);
+ ASSERT_EQ("", str_map["L"]);
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_str_map &&
+ * valgrind --tool=memcheck --leak-check=full \
+ * ./unittest_str_map
+ * "
+ * End:
+ */
diff --git a/src/test/common/test_tableformatter.cc b/src/test/common/test_tableformatter.cc
new file mode 100644
index 000000000..b152014a2
--- /dev/null
+++ b/src/test/common/test_tableformatter.cc
@@ -0,0 +1,263 @@
+#include "gtest/gtest.h"
+
+#include "common/Formatter.h"
+#include <iostream>
+#include <sstream>
+#include <string>
+
+using namespace ceph;
+
+TEST(tableformatter, singleline)
+{
+ std::stringstream sout;
+ TableFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout);
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "+----------+--------+---------+\n";
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, longfloat)
+{
+ std::stringstream sout;
+ TableFormatter formatter;
+ formatter.dump_float("float", 1.0 / 7);
+ formatter.flush(sout);
+
+ std::string cmp = ""
+ "+----------------------+\n"
+ "| float |\n"
+ "+----------------------+\n"
+ "| 0.14285714285714285 |\n"
+ "+----------------------+\n";
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, multiline)
+{
+ std::stringstream sout;
+ TableFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.dump_int("integer", 20);
+ formatter.dump_float("float", 20.0);
+ formatter.dump_string("string", "string");
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "| 20 | 20 | string |\n"
+ "+----------+--------+---------+\n";
+
+ formatter.flush(sout);
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, multiflush)
+{
+ std::stringstream sout1;
+ std::stringstream sout2;
+ TableFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout1);
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "+----------+--------+---------+\n";
+
+ EXPECT_EQ(cmp, sout1.str());
+
+ formatter.dump_int("integer", 20);
+ formatter.dump_float("float", 20.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout2);
+
+ cmp = ""
+ "| 20 | 20 | string |\n"
+ "+----------+--------+---------+\n";
+
+ EXPECT_EQ(cmp, sout2.str());
+
+}
+
+TEST(tableformatter, multireset)
+{
+ std::stringstream sout;
+ TableFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout);
+ formatter.reset();
+ formatter.dump_int("integer", 20);
+ formatter.dump_float("float", 20.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout);
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "+----------+--------+---------+\n"
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 20 | 20 | string |\n"
+ "+----------+--------+---------+\n";
+
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, changingheaderlength)
+{
+ std::stringstream sout;
+ TableFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout);
+ formatter.dump_int("integer", 20);
+ formatter.dump_float("float", 20.0);
+ formatter.dump_string("string", "stringstring");
+ formatter.flush(sout);
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "+----------+--------+---------+\n"
+ "+----------+--------+---------------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------------+\n"
+ "| 20 | 20 | stringstring |\n"
+ "+----------+--------+---------------+\n";
+
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, changingheader)
+{
+ std::stringstream sout;
+ TableFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout);
+ formatter.dump_int("longinteger", 20);
+ formatter.dump_float("double", 20.0);
+ formatter.dump_string("char*", "stringstring");
+ formatter.flush(sout);
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "+----------+--------+---------+\n"
+ "+--------------+---------+---------------+\n"
+ "| longinteger | double | char* |\n"
+ "+--------------+---------+---------------+\n"
+ "| 20 | 20 | stringstring |\n"
+ "+--------------+---------+---------------+\n";
+
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, extendingheader)
+{
+ std::stringstream sout;
+ TableFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout);
+ formatter.dump_int("integer", 20);
+ formatter.dump_float("float", 20.0);
+ formatter.dump_string("string", "string");
+ formatter.dump_string("char*", "abcde");
+ formatter.flush(sout);
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "+----------+--------+---------+\n"
+ "+----------+--------+---------+--------+\n"
+ "| integer | float | string | char* |\n"
+ "+----------+--------+---------+--------+\n"
+ "| 20 | 20 | string | abcde |\n"
+ "+----------+--------+---------+--------+\n";
+
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, stream)
+{
+ std::stringstream sout;
+ TableFormatter* formatter = (TableFormatter*) Formatter::create("table");
+ formatter->dump_stream("integer") << 10;
+ formatter->dump_stream("float") << 10.0;
+ formatter->dump_stream("string") << "string";
+ formatter->flush(sout);
+ delete formatter;
+
+ std::string cmp = ""
+ "+----------+--------+---------+\n"
+ "| integer | float | string |\n"
+ "+----------+--------+---------+\n"
+ "| 10 | 10 | string |\n"
+ "+----------+--------+---------+\n";
+
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(tableformatter, multiline_keyval)
+{
+ std::stringstream sout;
+ TableFormatter* formatter = (TableFormatter*) Formatter::create("table-kv");
+ formatter->dump_int("integer", 10);
+ formatter->dump_float("float", 10.0);
+ formatter->dump_string("string", "string");
+ formatter->dump_int("integer", 20);
+ formatter->dump_float("float", 20.0);
+ formatter->dump_string("string", "string");
+ formatter->flush(sout);
+ delete formatter;
+
+ std::string cmp = ""
+ "key::integer=\"10\" key::float=\"10\" key::string=\"string\" \n"
+ "key::integer=\"20\" key::float=\"20\" key::string=\"string\" \n";
+
+ EXPECT_EQ(cmp, sout.str());
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_tableformatter &&
+ * ./unittest_tableformatter
+ * '
+ * End:
+ */
+
+
+
diff --git a/src/test/common/test_time.cc b/src/test/common/test_time.cc
new file mode 100644
index 000000000..bc19ba573
--- /dev/null
+++ b/src/test/common/test_time.cc
@@ -0,0 +1,235 @@
+
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <ctime>
+
+#include "common/ceph_time.h"
+#include "include/rados.h"
+#include "gtest/gtest.h"
+#include "include/stringify.h"
+
+using namespace std;
+
+using ceph::real_clock;
+using ceph::real_time;
+
+using ceph::real_clock;
+using ceph::real_time;
+
+using ceph::coarse_real_clock;
+using ceph::coarse_mono_clock;
+
+using ceph::timespan;
+using ceph::signedspan;
+
+using std::chrono::seconds;
+using std::chrono::microseconds;
+using std::chrono::nanoseconds;
+
+static_assert(!real_clock::is_steady, "ceph::real_clock must not be steady.");
+static_assert(!coarse_real_clock::is_steady,
+ "ceph::coarse_real_clock must not be steady.");
+
+static_assert(mono_clock::is_steady, "ceph::mono_clock must be steady.");
+static_assert(coarse_mono_clock::is_steady,
+ "ceph::coarse_mono_clock must be steady.");
+
+// Before this file was written.
+static constexpr uint32_t bs = 1440701569;
+static constexpr uint32_t bns = 123456789;
+static constexpr uint32_t bus = 123456;
+static constexpr time_t btt = bs;
+static constexpr struct timespec bts = { bs, bns };
+static struct ceph_timespec bcts = { ceph_le32(bs), ceph_le32(bns) };
+static constexpr struct timeval btv = { bs, bus };
+static constexpr double bd = bs + ((double)bns / 1000000000.);
+
+template<typename Clock>
+static void system_clock_sanity() {
+ static const typename Clock::time_point brt(seconds(bs) + nanoseconds(bns));
+ const typename Clock::time_point now(Clock::now());
+
+ ASSERT_GT(now, brt);
+
+ ASSERT_GT(Clock::to_time_t(now), btt);
+
+ ASSERT_GT(Clock::to_timespec(now).tv_sec, bts.tv_sec);
+ ASSERT_LT(Clock::to_timespec(now).tv_nsec, 1000000000L);
+
+ ASSERT_GT(Clock::to_ceph_timespec(now).tv_sec, bcts.tv_sec);
+ ASSERT_LT(Clock::to_ceph_timespec(now).tv_nsec, 1000000000UL);
+
+ ASSERT_GT(Clock::to_timeval(now).tv_sec, btv.tv_sec);
+ ASSERT_LT(Clock::to_timeval(now).tv_usec, 1000000L);
+}
+
+template<typename Clock>
+static void system_clock_conversions() {
+ static typename Clock::time_point brt(seconds(bs) +
+ nanoseconds(bns));
+
+ ASSERT_EQ(Clock::to_time_t(brt), btt);
+ ASSERT_EQ(Clock::from_time_t(btt) + nanoseconds(bns), brt);
+
+ {
+ const struct timespec tts = Clock::to_timespec(brt);
+ ASSERT_EQ(tts.tv_sec, bts.tv_sec);
+ ASSERT_EQ(tts.tv_nsec, bts.tv_nsec);
+ }
+ ASSERT_EQ(Clock::from_timespec(bts), brt);
+ {
+ struct timespec tts;
+ Clock::to_timespec(brt, tts);
+ ASSERT_EQ(tts.tv_sec, bts.tv_sec);
+ ASSERT_EQ(tts.tv_nsec, bts.tv_nsec);
+ }
+
+ {
+ const struct ceph_timespec tcts = Clock::to_ceph_timespec(brt);
+ ASSERT_EQ(tcts.tv_sec, bcts.tv_sec);
+ ASSERT_EQ(tcts.tv_nsec, bcts.tv_nsec);
+ }
+ ASSERT_EQ(Clock::from_ceph_timespec(bcts), brt);
+ {
+ struct ceph_timespec tcts;
+ Clock::to_ceph_timespec(brt, tcts);
+ ASSERT_EQ(tcts.tv_sec, bcts.tv_sec);
+ ASSERT_EQ(tcts.tv_nsec, bcts.tv_nsec);
+ }
+
+ {
+ const struct timeval ttv = Clock::to_timeval(brt);
+ ASSERT_EQ(ttv.tv_sec, btv.tv_sec);
+ ASSERT_EQ(ttv.tv_usec, btv.tv_usec);
+ }
+ ASSERT_EQ(Clock::from_timeval(btv), brt - nanoseconds(bns - bus * 1000));
+ {
+ struct timeval ttv;
+ Clock::to_timeval(brt, ttv);
+ ASSERT_EQ(ttv.tv_sec, btv.tv_sec);
+ ASSERT_EQ(ttv.tv_usec, btv.tv_usec);
+ }
+
+ ASSERT_EQ(Clock::to_double(brt), bd);
+ // Fudge factor
+ ASSERT_LT(std::abs((Clock::from_double(bd) - brt).count()), 30);
+}
+
+TEST(RealClock, Sanity) {
+ system_clock_sanity<real_clock>();
+}
+
+
+TEST(RealClock, Conversions) {
+ system_clock_conversions<real_clock>();
+}
+
+TEST(CoarseRealClock, Sanity) {
+ system_clock_sanity<coarse_real_clock>();
+}
+
+
+TEST(CoarseRealClock, Conversions) {
+ system_clock_conversions<coarse_real_clock>();
+}
+
+TEST(TimePoints, SignedSubtraciton) {
+ ceph::real_time rta(std::chrono::seconds(3));
+ ceph::real_time rtb(std::chrono::seconds(5));
+
+ ceph::coarse_real_time crta(std::chrono::seconds(3));
+ ceph::coarse_real_time crtb(std::chrono::seconds(5));
+
+ ceph::mono_time mta(std::chrono::seconds(3));
+ ceph::mono_time mtb(std::chrono::seconds(5));
+
+ ceph::coarse_mono_time cmta(std::chrono::seconds(3));
+ ceph::coarse_mono_time cmtb(std::chrono::seconds(5));
+
+ ASSERT_LT(rta - rtb, ceph::signedspan::zero());
+ ASSERT_LT((rta - rtb).count(), 0);
+ ASSERT_GT(rtb - rta, ceph::signedspan::zero());
+ ASSERT_GT((rtb - rta).count(), 0);
+
+ ASSERT_LT(crta - crtb, ceph::signedspan::zero());
+ ASSERT_LT((crta - crtb).count(), 0);
+ ASSERT_GT(crtb - crta, ceph::signedspan::zero());
+ ASSERT_GT((crtb - crta).count(), 0);
+
+ ASSERT_LT(mta - mtb, ceph::signedspan::zero());
+ ASSERT_LT((mta - mtb).count(), 0);
+ ASSERT_GT(mtb - mta, ceph::signedspan::zero());
+ ASSERT_GT((mtb - mta).count(), 0);
+
+ ASSERT_LT(cmta - cmtb, ceph::signedspan::zero());
+ ASSERT_LT((cmta - cmtb).count(), 0);
+ ASSERT_GT(cmtb - cmta, ceph::signedspan::zero());
+ ASSERT_GT((cmtb - cmta).count(), 0);
+}
+
+TEST(TimePoints, stringify) {
+ ceph::real_clock::time_point tp(seconds(1556122013) + nanoseconds(39923122));
+ string s = stringify(tp);
+ ASSERT_EQ(s.size(), strlen("2019-04-24T11:06:53.039923-0500"));
+ ASSERT_TRUE(s[26] == '-' || s[26] == '+');
+ ASSERT_EQ(s.substr(0, 9), "2019-04-2");
+
+ ceph::coarse_real_clock::time_point ctp(seconds(1556122013) +
+ nanoseconds(399000000));
+ s = stringify(ctp);
+ ASSERT_EQ(s.size(), strlen("2019-04-24T11:06:53.399000-0500"));
+ ASSERT_TRUE(s[26] == '-' || s[26] == '+');
+ ASSERT_EQ(s.substr(0, 9), "2019-04-2");
+}
+
+namespace {
+ template<typename Rep, typename Period>
+ std::string to_string(const chrono::duration<Rep, Period>& t)
+ {
+ std::ostringstream ss;
+ ss << t;
+ return ss.str();
+ }
+
+ void float_format_eq(string_view lhs,
+ string_view rhs,
+ unsigned precision)
+ {
+ const float TOLERANCE = 10.0F / pow(10.0F, static_cast<float>(precision));
+ ASSERT_FALSE(lhs.empty());
+ ASSERT_EQ(lhs.back(), 's');
+ float lhs_v = std::stof(string{lhs, 0, lhs.find('s')});
+ ASSERT_NE(lhs.npos, lhs.find('.'));
+ ASSERT_EQ(precision, lhs.find('s') - lhs.find('.') - 1);
+
+ ASSERT_FALSE(rhs.empty());
+ ASSERT_EQ(rhs.back(), 's');
+ float rhs_v = std::stof(string{rhs, 0, rhs.find('s')});
+ EXPECT_NEAR(lhs_v, rhs_v, TOLERANCE);
+ ASSERT_NE(rhs.npos, rhs.find('.'));
+ EXPECT_EQ(precision, rhs.find('s') - rhs.find('.') - 1);
+ }
+}
+
+TEST(TimeDurations, print) {
+ float_format_eq("0.123456700s",
+ to_string(std::chrono::duration_cast<ceph::timespan>(0.1234567s)),
+ 9);
+ float_format_eq("-0.123456700s",
+ to_string(std::chrono::duration_cast<ceph::signedspan>(-0.1234567s)),
+ 9);
+ EXPECT_EQ("42s", to_string(42s));
+ float_format_eq("0.123000000s", to_string(123ms), 9);
+}
diff --git a/src/test/common/test_url_escape.cc b/src/test/common/test_url_escape.cc
new file mode 100644
index 000000000..6c27b64da
--- /dev/null
+++ b/src/test/common/test_url_escape.cc
@@ -0,0 +1,36 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/url_escape.h"
+
+#include "gtest/gtest.h"
+
+TEST(url_escape, escape) {
+ ASSERT_EQ(url_escape("foo bar"), std::string("foo%20bar"));
+ ASSERT_EQ(url_escape("foo\nbar"), std::string("foo%0abar"));
+}
+
+TEST(url_escape, unescape) {
+ ASSERT_EQ(url_unescape("foo%20bar"), std::string("foo bar"));
+ ASSERT_EQ(url_unescape("foo%0abar"), std::string("foo\nbar"));
+ ASSERT_EQ(url_unescape("%20"), std::string(" "));
+ ASSERT_EQ(url_unescape("\0%20"), std::string("\0 "));
+ ASSERT_EQ(url_unescape("\x01%20"), std::string("\x01 "));
+}
+
+TEST(url_escape, all_chars) {
+ std::string a;
+ for (unsigned j=0; j<256; ++j) {
+ a.push_back((char)j);
+ }
+ std::string b = url_escape(a);
+ std::cout << "escaped: " << b << std::endl;
+ ASSERT_EQ(a, url_unescape(b));
+}
+
+TEST(url_escape, invalid) {
+ ASSERT_THROW(url_unescape("foo%xx"), std::runtime_error);
+ ASSERT_THROW(url_unescape("foo%%"), std::runtime_error);
+ ASSERT_THROW(url_unescape("foo%"), std::runtime_error);
+ ASSERT_THROW(url_unescape("foo%0"), std::runtime_error);
+}
diff --git a/src/test/common/test_util.cc b/src/test/common/test_util.cc
new file mode 100644
index 000000000..91ac771f8
--- /dev/null
+++ b/src/test/common/test_util.cc
@@ -0,0 +1,42 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <filesystem>
+
+#include "gtest/gtest.h"
+#include "common/ceph_context.h"
+#include "include/util.h"
+
+using namespace std;
+
+namespace fs = std::filesystem;
+
+#if defined(__linux__)
+TEST(util, collect_sys_info)
+{
+ if (!fs::exists("/etc/os-release")) {
+ GTEST_SKIP() << "skipping as '/etc/os-release' does not exist";
+ }
+
+ map<string, string> sys_info;
+
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+ collect_sys_info(&sys_info, cct);
+
+ ASSERT_TRUE(sys_info.find("distro") != sys_info.end());
+ ASSERT_TRUE(sys_info.find("distro_description") != sys_info.end());
+
+ cct->put();
+}
+#endif
diff --git a/src/test/common/test_weighted_priority_queue.cc b/src/test/common/test_weighted_priority_queue.cc
new file mode 100644
index 000000000..263fc4cb4
--- /dev/null
+++ b/src/test/common/test_weighted_priority_queue.cc
@@ -0,0 +1,240 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "gtest/gtest.h"
+#include "common/Formatter.h"
+#include "common/WeightedPriorityQueue.h"
+
+#include <numeric>
+#include <vector>
+#include <map>
+#include <list>
+#include <tuple>
+
+#define CEPH_OP_CLASS_STRICT 0
+#define CEPH_OP_CLASS_NORMAL 0
+#define CEPH_OP_QUEUE_BACK 0
+#define CEPH_OP_QUEUE_FRONT 0
+
+class WeightedPriorityQueueTest : public testing::Test
+{
+protected:
+ typedef unsigned Klass;
+ // tuple<Prio, Klass, OpID> so that we can verfiy the op
+ typedef std::tuple<unsigned, unsigned, unsigned> Item;
+ typedef unsigned Prio;
+ typedef unsigned Kost;
+ typedef WeightedPriorityQueue<Item, Klass> WQ;
+ // Simulate queue structure
+ typedef std::list<std::pair<Kost, Item> > ItemList;
+ typedef std::map<Klass, ItemList> KlassItem;
+ typedef std::map<Prio, KlassItem> LQ;
+ typedef std::list<Item> Removed;
+ const unsigned max_prios = 5; // (0-4) * 64
+ const unsigned klasses = 37; // Make prime to help get good coverage
+
+ void fill_queue(WQ &wq, LQ &strictq, LQ &normq,
+ unsigned item_size, bool randomize = false) {
+ unsigned p, k, c, o, op_queue, fob;
+ for (unsigned i = 1; i <= item_size; ++i) {
+ // Choose priority, class, cost and 'op' for this op.
+ if (randomize) {
+ p = (rand() % max_prios) * 64;
+ k = rand() % klasses;
+ c = rand() % (1<<22); // 4M cost
+ // Make some of the costs 0, but make sure small costs
+ // still work ok.
+ if (c > (1<<19) && c < (1<<20)) {
+ c = 0;
+ }
+ op_queue = rand() % 10;
+ fob = rand() % 10;
+ } else {
+ p = (i % max_prios) * 64;
+ k = i % klasses;
+ c = (i % 8 == 0 || i % 16 == 0) ? 0 : 1 << (i % 23);
+ op_queue = i % 7; // Use prime numbers to
+ fob = i % 11; // get better coverage
+ }
+ o = rand() % (1<<16);
+ // Choose how to enqueue this op.
+ switch (op_queue) {
+ case 6 :
+ // Strict Queue
+ if (fob == 4) {
+ // Queue to the front.
+ strictq[p][k].push_front(std::make_pair(
+ c, std::make_tuple(p, k, o)));
+ wq.enqueue_strict_front(Klass(k), p, std::make_tuple(p, k, o));
+ } else {
+ //Queue to the back.
+ strictq[p][k].push_back(std::make_pair(
+ c, std::make_tuple(p, k, o)));
+ wq.enqueue_strict(Klass(k), p, std::make_tuple(p, k, o));
+ }
+ break;
+ default:
+ // Normal queue
+ if (fob == 4) {
+ // Queue to the front.
+ normq[p][k].push_front(std::make_pair(
+ c, std::make_tuple(p, k, o)));
+ wq.enqueue_front(Klass(k), p, c, std::make_tuple(p, k, o));
+ } else {
+ //Queue to the back.
+ normq[p][k].push_back(std::make_pair(
+ c, std::make_tuple(p, k, o)));
+ wq.enqueue(Klass(k), p, c, std::make_tuple(p, k, o));
+ }
+ break;
+ }
+ }
+ }
+ void test_queue(unsigned item_size, bool randomize = false) {
+ // Due to the WRR queue having a lot of probabilistic logic
+ // we can't determine the exact order OPs will be dequeued.
+ // However, the queue should not dequeue a priority out of
+ // order. It should also dequeue the strict priority queue
+ // first and in order. In both the strict and normal queues
+ // push front and back should be respected. Here we keep
+ // track of the ops queued and make sure they dequeue
+ // correctly.
+
+ // Set up local tracking queues
+ WQ wq(0, 0);
+ LQ strictq, normq;
+ fill_queue(wq, strictq, normq, item_size, randomize);
+ // Test that the queue is dequeuing properly.
+ typedef std::map<unsigned, unsigned> LastKlass;
+ LastKlass last_strict, last_norm;
+ while (!(wq.empty())) {
+ Item r = wq.dequeue();
+ if (!(strictq.empty())) {
+ // Check that there are no higher priorities
+ // in the strict queue.
+ LQ::reverse_iterator ri = strictq.rbegin();
+ EXPECT_EQ(std::get<0>(r), ri->first);
+ // Check that if there are multiple classes in a priority
+ // that it is not dequeueing the same class each time.
+ LastKlass::iterator si = last_strict.find(std::get<0>(r));
+ if (strictq[std::get<0>(r)].size() > 1 && si != last_strict.end()) {
+ EXPECT_NE(std::get<1>(r), si->second);
+ }
+ last_strict[std::get<0>(r)] = std::get<1>(r);
+
+ Item t = strictq[std::get<0>(r)][std::get<1>(r)].front().second;
+ EXPECT_EQ(std::get<2>(r), std::get<2>(t));
+ strictq[std::get<0>(r)][std::get<1>(r)].pop_front();
+ if (strictq[std::get<0>(r)][std::get<1>(r)].empty()) {
+ strictq[std::get<0>(r)].erase(std::get<1>(r));
+ }
+ if (strictq[std::get<0>(r)].empty()) {
+ strictq.erase(std::get<0>(r));
+ }
+ } else {
+ // Check that if there are multiple classes in a priority
+ // that it is not dequeueing the same class each time.
+ LastKlass::iterator si = last_norm.find(std::get<0>(r));
+ if (normq[std::get<0>(r)].size() > 1 && si != last_norm.end()) {
+ EXPECT_NE(std::get<1>(r), si->second);
+ }
+ last_norm[std::get<0>(r)] = std::get<1>(r);
+
+ Item t = normq[std::get<0>(r)][std::get<1>(r)].front().second;
+ EXPECT_EQ(std::get<2>(r), std::get<2>(t));
+ normq[std::get<0>(r)][std::get<1>(r)].pop_front();
+ if (normq[std::get<0>(r)][std::get<1>(r)].empty()) {
+ normq[std::get<0>(r)].erase(std::get<1>(r));
+ }
+ if (normq[std::get<0>(r)].empty()) {
+ normq.erase(std::get<0>(r));
+ }
+ }
+ }
+ }
+
+ void SetUp() override {
+ srand(time(0));
+ }
+ void TearDown() override {
+ }
+};
+
+TEST_F(WeightedPriorityQueueTest, wpq_size){
+ WQ wq(0, 0);
+ EXPECT_TRUE(wq.empty());
+ EXPECT_EQ(0u, wq.get_size_slow());
+
+ // Test the strict queue size.
+ for (unsigned i = 1; i < 5; ++i) {
+ wq.enqueue_strict(Klass(i),i, std::make_tuple(i, i, i));
+ EXPECT_FALSE(wq.empty());
+ EXPECT_EQ(i, wq.get_size_slow());
+ }
+ // Test the normal queue size.
+ for (unsigned i = 5; i < 10; ++i) {
+ wq.enqueue(Klass(i), i, i, std::make_tuple(i, i, i));
+ EXPECT_FALSE(wq.empty());
+ EXPECT_EQ(i, wq.get_size_slow());
+ }
+ // Test that as both queues are emptied
+ // the size is correct.
+ for (unsigned i = 8; i >0; --i) {
+ wq.dequeue();
+ EXPECT_FALSE(wq.empty());
+ EXPECT_EQ(i, wq.get_size_slow());
+ }
+ wq.dequeue();
+ EXPECT_TRUE(wq.empty());
+ EXPECT_EQ(0u, wq.get_size_slow());
+}
+
+TEST_F(WeightedPriorityQueueTest, wpq_test_static) {
+ test_queue(1000);
+}
+
+TEST_F(WeightedPriorityQueueTest, wpq_test_random) {
+ test_queue(rand() % 500 + 500, true);
+}
+
+TEST_F(WeightedPriorityQueueTest, wpq_test_remove_by_class_null) {
+ WQ wq(0, 0);
+ LQ strictq, normq;
+ unsigned num_items = 10;
+ fill_queue(wq, strictq, normq, num_items);
+ Removed wq_removed;
+ // Pick a klass that was not enqueued
+ wq.remove_by_class(klasses + 1, &wq_removed);
+ EXPECT_EQ(0u, wq_removed.size());
+}
+
+TEST_F(WeightedPriorityQueueTest, wpq_test_remove_by_class) {
+ WQ wq(0, 0);
+ LQ strictq, normq;
+ unsigned num_items = 1000;
+ fill_queue(wq, strictq, normq, num_items);
+ unsigned num_to_remove = 0;
+ const Klass k = 5;
+ // Find how many ops are in the class
+ for (LQ::iterator it = strictq.begin();
+ it != strictq.end(); ++it) {
+ num_to_remove += it->second[k].size();
+ }
+ for (LQ::iterator it = normq.begin();
+ it != normq.end(); ++it) {
+ num_to_remove += it->second[k].size();
+ }
+ Removed wq_removed;
+ wq.remove_by_class(k, &wq_removed);
+ // Check that the right ops were removed.
+ EXPECT_EQ(num_to_remove, wq_removed.size());
+ EXPECT_EQ(num_items - num_to_remove, wq.get_size_slow());
+ for (Removed::iterator it = wq_removed.begin();
+ it != wq_removed.end(); ++it) {
+ EXPECT_EQ(k, std::get<1>(*it));
+ }
+ // Check that none were missed
+ while (!(wq.empty())) {
+ EXPECT_NE(k, std::get<1>(wq.dequeue()));
+ }
+}
diff --git a/src/test/common/test_xmlformatter.cc b/src/test/common/test_xmlformatter.cc
new file mode 100644
index 000000000..9ac6dde45
--- /dev/null
+++ b/src/test/common/test_xmlformatter.cc
@@ -0,0 +1,165 @@
+#include "gtest/gtest.h"
+
+#include "common/Formatter.h"
+#include <sstream>
+#include <string>
+
+using namespace ceph;
+
+
+TEST(xmlformatter, oneline)
+{
+
+ std::stringstream sout;
+ XMLFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout);
+ std::string cmp = "<integer>10</integer><float>10</float><string>string</string>";
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(xmlformatter, multiline)
+{
+ std::stringstream sout;
+ XMLFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.dump_int("integer", 20);
+ formatter.dump_float("float", 20.0);
+ formatter.dump_string("string", "string");
+
+ std::string cmp = ""
+ "<integer>10</integer><float>10</float><string>string</string>"
+ "<integer>20</integer><float>20</float><string>string</string>";
+
+ formatter.flush(sout);
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(xmlformatter, multiflush)
+{
+ std::stringstream sout1;
+ std::stringstream sout2;
+ XMLFormatter formatter;
+ formatter.dump_int("integer", 10);
+ formatter.dump_float("float", 10.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout1);
+
+ std::string cmp = ""
+ "<integer>10</integer>"
+ "<float>10</float>"
+ "<string>string</string>";
+
+ EXPECT_EQ(cmp, sout1.str());
+
+ formatter.dump_int("integer", 20);
+ formatter.dump_float("float", 20.0);
+ formatter.dump_string("string", "string");
+ formatter.flush(sout2);
+
+ cmp = ""
+ "<integer>20</integer>"
+ "<float>20</float>"
+ "<string>string</string>";
+
+ EXPECT_EQ(cmp, sout2.str());
+}
+
+TEST(xmlformatter, pretty)
+{
+ std::stringstream sout;
+ XMLFormatter formatter(
+ true, // pretty
+ false, // lowercased
+ false); // underscored
+ formatter.open_object_section("xml");
+ formatter.dump_int("Integer", 10);
+ formatter.dump_float("Float", 10.0);
+ formatter.dump_string("String", "String");
+ formatter.close_section();
+ formatter.flush(sout);
+ std::string cmp = ""
+ "<xml>\n"
+ " <Integer>10</Integer>\n"
+ " <Float>10</Float>\n"
+ " <String>String</String>\n"
+ "</xml>\n\n";
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(xmlformatter, lowercased)
+{
+ std::stringstream sout;
+ XMLFormatter formatter(
+ false, // pretty
+ true, // lowercased
+ false); // underscored
+ formatter.dump_int("Integer", 10);
+ formatter.dump_float("Float", 10.0);
+ formatter.dump_string("String", "String");
+ formatter.flush(sout);
+ std::string cmp = ""
+ "<integer>10</integer>"
+ "<float>10</float>"
+ "<string>String</string>";
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(xmlformatter, underscored)
+{
+ std::stringstream sout;
+ XMLFormatter formatter(
+ false, // pretty
+ false, // lowercased
+ true); // underscored
+ formatter.dump_int("Integer Item", 10);
+ formatter.dump_float("Float Item", 10.0);
+ formatter.dump_string("String Item", "String");
+ formatter.flush(sout);
+ std::string cmp = ""
+ "<Integer_Item>10</Integer_Item>"
+ "<Float_Item>10</Float_Item>"
+ "<String_Item>String</String_Item>";
+
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(xmlformatter, lowercased_underscored)
+{
+ std::stringstream sout;
+ XMLFormatter formatter(
+ false, // pretty
+ true, // lowercased
+ true); // underscored
+ formatter.dump_int("Integer Item", 10);
+ formatter.dump_float("Float Item", 10.0);
+ formatter.dump_string("String Item", "String");
+ formatter.flush(sout);
+ std::string cmp = ""
+ "<integer_item>10</integer_item>"
+ "<float_item>10</float_item>"
+ "<string_item>String</string_item>";
+ EXPECT_EQ(cmp, sout.str());
+}
+
+TEST(xmlformatter, pretty_lowercased_underscored)
+{
+ std::stringstream sout;
+ XMLFormatter formatter(
+ true, // pretty
+ true, // lowercased
+ true); // underscored
+ formatter.dump_int("Integer Item", 10);
+ formatter.dump_float("Float Item", 10.0);
+ formatter.dump_string("String Item", "String");
+ formatter.flush(sout);
+ std::string cmp = ""
+ "<integer_item>10</integer_item>\n"
+ "<float_item>10</float_item>\n"
+ "<string_item>String</string_item>\n\n";
+ EXPECT_EQ(cmp, sout.str());
+}
diff --git a/src/test/compressor/CMakeLists.txt b/src/test/compressor/CMakeLists.txt
new file mode 100644
index 000000000..3a170a942
--- /dev/null
+++ b/src/test/compressor/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(ceph_example SHARED compressor_plugin_example.cc)
+target_link_libraries(ceph_example ceph-common)
+
+# unittest_compression
+add_executable(unittest_compression
+ test_compression.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_compression)
+target_link_libraries(unittest_compression global GTest::GTest)
+add_dependencies(unittest_compression ceph_example) \ No newline at end of file
diff --git a/src/test/compressor/compressor_example.h b/src/test/compressor/compressor_example.h
new file mode 100644
index 000000000..3e360e525
--- /dev/null
+++ b/src/test/compressor/compressor_example.h
@@ -0,0 +1,53 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2015 Mirantis, Inc.
+ *
+ * Author: Alyona Kiseleva <akiselyova@mirantis.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef CEPH_COMPRESSOR_EXAMPLE_H
+#define CEPH_COMPRESSOR_EXAMPLE_H
+
+#include <unistd.h>
+#include <errno.h>
+#include <algorithm>
+#include <sstream>
+
+#include "crush/CrushWrapper.h"
+#include "osd/osd_types.h"
+#include "compressor/Compressor.h"
+
+class CompressorExample : public Compressor {
+public:
+ CompressorExample() : Compressor(COMP_ALG_NONE, "example") {}
+ ~CompressorExample() override {}
+
+ int compress(const bufferlist &in, bufferlist &out, std::optional<int32_t> &compressor_message) override
+ {
+ out = in;
+ return 0;
+ }
+
+ int decompress(const bufferlist &in, bufferlist &out, std::optional<int32_t> compressor_message) override
+ {
+ out = in;
+ return 0;
+ }
+ int decompress(bufferlist::const_iterator &p, size_t compressed_len, bufferlist &out, std::optional<int32_t> compressor_message) override
+ {
+ p.copy(std::min<size_t>(p.get_remaining(), compressed_len), out);
+ return 0;
+ }
+};
+
+#endif
diff --git a/src/test/compressor/compressor_plugin_example.cc b/src/test/compressor/compressor_plugin_example.cc
new file mode 100644
index 000000000..014f1fb9d
--- /dev/null
+++ b/src/test/compressor/compressor_plugin_example.cc
@@ -0,0 +1,59 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2015 Mirantis, Inc.
+ *
+ * Author: Alyona Kiseleva <akiselyova@mirantis.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <unistd.h>
+
+#include "ceph_ver.h"
+#include "compressor/CompressionPlugin.h"
+#include "compressor_example.h"
+
+using namespace std;
+
+class CompressorPluginExample : public CompressionPlugin {
+public:
+
+ explicit CompressorPluginExample(CephContext* cct) : CompressionPlugin(cct)
+ {}
+
+ int factory(CompressorRef *cs,
+ ostream *ss) override
+ {
+ if (compressor == 0) {
+ CompressorExample *interface = new CompressorExample();
+ compressor = CompressorRef(interface);
+ }
+ *cs = compressor;
+ return 0;
+ }
+};
+
+// -----------------------------------------------------------------------------
+
+const char *__ceph_plugin_version()
+{
+ return CEPH_GIT_NICE_VER;
+}
+
+// -----------------------------------------------------------------------------
+
+int __ceph_plugin_init(CephContext *cct,
+ const std::string& type,
+ const std::string& name)
+{
+ PluginRegistry *instance = cct->get_plugin_registry();
+
+ return instance->add(type, name, new CompressorPluginExample(cct));
+}
diff --git a/src/test/compressor/osdmaps/osdmap.2982809 b/src/test/compressor/osdmaps/osdmap.2982809
new file mode 100644
index 000000000..8de30c689
--- /dev/null
+++ b/src/test/compressor/osdmaps/osdmap.2982809
Binary files differ
diff --git a/src/test/compressor/osdmaps/osdmap.2982809.h b/src/test/compressor/osdmaps/osdmap.2982809.h
new file mode 100644
index 000000000..d6a7a0966
--- /dev/null
+++ b/src/test/compressor/osdmaps/osdmap.2982809.h
@@ -0,0 +1,38257 @@
+unsigned char osdmap_a[] = {
+0x8,0x7,0xdf,0x56,0x9,0x0,0x7,0x1,0x91,0x47,0x5,0x0,0xb4,0xf4,0x63,0xa0,
+0xc6,0x71,0x43,0xa8,0xbd,0x36,0xe4,0xa,0xb8,0xd2,0x33,0xd2,0x99,0x83,0x2d,0x0,
+0x6d,0x34,0x16,0x52,0xe8,0x59,0x97,0x1c,0xa,0x4d,0x4e,0x5e,0x13,0x5c,0x3b,0x35,
+0x4,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x5,0x20,0xe,
+0x0,0x0,0x1,0x3,0x0,0x2,0x0,0x20,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x83,0x2d,0x0,0x76,0x4a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x40,0x83,0x2d,0x0,0x0,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x1e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x1f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x28,
+0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x39,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x49,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x53,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x6b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x6b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x6d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x71,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x72,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x72,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x7a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x7b,
+0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x7d,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x7e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x7e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x7e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x7e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x7e,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x81,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x81,
+0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x86,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x87,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x88,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x89,
+0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x89,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x91,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x92,
+0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x94,
+0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x95,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x95,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x95,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x97,
+0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x99,
+0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x9a,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x9b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x9b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x9d,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x9f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x9f,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0xa3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xa4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0xa6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xa6,
+0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0xa7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xa7,
+0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0xa8,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xa9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xae,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0xb0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0xb0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0xb1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xb2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0xb2,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xb3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xb3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xb3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0xb3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xb6,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xbb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xbb,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0xbc,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xbf,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xc0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xc0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xc2,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0xc6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xc7,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0xd1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0xd1,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0xd1,
+0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0xd3,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0xd4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xd4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0xd5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xd7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0xdd,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xdd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xdf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xe1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0xe1,
+0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xe5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xe5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0xe5,
+0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0xe6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xe8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0xe9,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0xec,
+0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0xee,
+0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xee,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0xf0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0xf0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0xf0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xf1,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0xf1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xf2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0xf4,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0xf7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0xf7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xf9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0xfa,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xfb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x0,
+0x1,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x0,
+0x1,0x0,0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x0,
+0x1,0x0,0x0,0x0,0x0,0x0,0xad,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x3,
+0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x3,
+0x1,0x0,0x0,0x0,0x0,0x0,0x59,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x5,
+0x1,0x0,0x0,0x0,0x0,0x0,0x78,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x7,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x7,
+0x1,0x0,0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x8,
+0x1,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x8,
+0x1,0x0,0x0,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x9,
+0x1,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x9,
+0x1,0x0,0x0,0x0,0x0,0x0,0x73,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0xa,
+0x1,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0xa,
+0x1,0x0,0x0,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0xc,
+0x1,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0xc,
+0x1,0x0,0x0,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0xd,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xd,
+0x1,0x0,0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xe,
+0x1,0x0,0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0xe,
+0x1,0x0,0x0,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xf,
+0x1,0x0,0x0,0x0,0x0,0x0,0x91,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x12,
+0x1,0x0,0x0,0x0,0x0,0x0,0xbb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x14,
+0x1,0x0,0x0,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x15,
+0x1,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x16,
+0x1,0x0,0x0,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x17,
+0x1,0x0,0x0,0x0,0x0,0x0,0x8f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x18,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x19,
+0x1,0x0,0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x19,
+0x1,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x19,
+0x1,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x19,
+0x1,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x19,
+0x1,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x19,
+0x1,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x1a,
+0x1,0x0,0x0,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x1b,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x1b,
+0x1,0x0,0x0,0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x1d,
+0x1,0x0,0x0,0x0,0x0,0x0,0x25,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x1f,
+0x1,0x0,0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x1f,
+0x1,0x0,0x0,0x0,0x0,0x0,0x32,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x21,
+0x1,0x0,0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x23,
+0x1,0x0,0x0,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x24,
+0x1,0x0,0x0,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x26,
+0x1,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x27,
+0x1,0x0,0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x28,
+0x1,0x0,0x0,0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x2a,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x2a,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x2a,
+0x1,0x0,0x0,0x0,0x0,0x0,0x74,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x30,
+0x1,0x0,0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x30,
+0x1,0x0,0x0,0x0,0x0,0x0,0xf2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x32,
+0x1,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x32,
+0x1,0x0,0x0,0x0,0x0,0x0,0xc2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x35,
+0x1,0x0,0x0,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x36,
+0x1,0x0,0x0,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x38,
+0x1,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x38,
+0x1,0x0,0x0,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x39,
+0x1,0x0,0x0,0x0,0x0,0x0,0x26,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x3c,
+0x1,0x0,0x0,0x0,0x0,0x0,0x4c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x3d,
+0x1,0x0,0x0,0x0,0x0,0x0,0x7d,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x42,
+0x1,0x0,0x0,0x0,0x0,0x0,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x42,
+0x1,0x0,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x42,
+0x1,0x0,0x0,0x0,0x0,0x0,0xd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x43,
+0x1,0x0,0x0,0x0,0x0,0x0,0x7b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x45,
+0x1,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x45,
+0x1,0x0,0x0,0x0,0x0,0x0,0x8a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x48,
+0x1,0x0,0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x48,
+0x1,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x48,
+0x1,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x48,
+0x1,0x0,0x0,0x0,0x0,0x0,0x42,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x4a,
+0x1,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xfa,0x21,0x0,0x1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x27,0x9,0x0,0x1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x1,0x34,
+0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x18,0x15,0x41,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x75,0x42,0x41,0x2,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x75,0x42,0x41,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,
+0x0,0x0,0x0,0x72,0x62,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x5,0x10,0xda,0x0,0x0,
+0x1,0x3,0x0,0x2,0x0,0x8,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x90,0x83,0x2d,0x0,0x98,0xbc,0x22,0x0,0x0,0x0,0x0,0x0,
+0x90,0x83,0x2d,0x0,0x0,0x0,0x0,0x0,0x8f,0xd,0x0,0x0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xee,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x8,0x0,0x0,
+0x0,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0xa,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xa,0x0,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xa,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0xd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0xd,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x14,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x14,0x0,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x14,0x0,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x14,0x0,0x0,
+0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x15,0x0,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x15,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x15,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x15,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x15,0x0,0x0,
+0x0,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x15,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x16,0x0,0x0,
+0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x1b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x1b,0x0,0x0,
+0x0,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x1d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x1e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x1e,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x20,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x21,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x21,0x0,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x21,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x21,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x21,0x0,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x22,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x22,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x22,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x22,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x22,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x22,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x23,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x24,0x0,0x0,
+0x0,0x0,0x0,0x0,0x50,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x26,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x26,0x0,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x26,0x0,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x26,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x26,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x26,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x26,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x27,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x27,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x27,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x27,0x0,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x27,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x27,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x27,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x28,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x28,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x28,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x29,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x29,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x29,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x29,0x0,0x0,
+0x0,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x29,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x29,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x2b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x2b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x2b,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x2b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x2b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x2d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x2d,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x2d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x2d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x2e,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x2e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x2e,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x2e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x2f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x2f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x2f,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x30,0x0,0x0,
+0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x30,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x30,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x30,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x30,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x30,0x0,0x0,
+0x0,0x0,0x0,0x0,0x42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x30,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x31,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x31,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x31,0x0,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x31,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x35,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x36,0x0,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x36,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x36,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x38,0x0,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x38,0x0,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x38,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x3a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x3c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x3c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x3d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x21,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x3e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x3e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x3f,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x40,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x41,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x43,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x44,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x48,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x4d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x4d,0x0,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x56,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x56,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x56,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x57,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x57,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd4,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x5b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x5d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x5d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x60,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x60,0x0,0x0,
+0x0,0x0,0x0,0x0,0x79,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x67,0x0,0x0,
+0x0,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x68,0x0,0x0,
+0x0,0x0,0x0,0x0,0xed,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x6a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x6a,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x99,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x6d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x6d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x6d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x6d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x6d,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x6e,0x0,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x6e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x70,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x72,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x72,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x73,0x0,0x0,
+0x0,0x0,0x0,0x0,0x86,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x75,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x76,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x76,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x76,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x76,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x76,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x76,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x76,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x77,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x77,0x0,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x78,0x0,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x78,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x78,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x78,0x0,0x0,
+0x0,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x78,0x0,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x78,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x78,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x79,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x79,0x0,0x0,
+0x0,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x7a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x7a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x7b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x7c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x7d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x7e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x7e,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x7f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x7f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x80,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x81,0x0,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x81,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x81,0x0,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x81,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x81,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x81,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x81,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x82,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x82,0x0,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x82,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x82,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x82,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x82,0x0,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x82,0x0,0x0,
+0x0,0x0,0x0,0x0,0x65,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x87,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x88,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x88,0x0,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x89,0x0,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x89,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x89,0x0,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x90,0x0,0x0,
+0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x90,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x91,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x92,0x0,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x92,0x0,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x92,0x0,0x0,
+0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x0,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x93,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x93,0x0,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x93,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x95,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x95,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x95,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x95,0x0,0x0,
+0x0,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x96,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x96,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa8,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x98,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x99,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x99,0x0,0x0,
+0x0,0x0,0x0,0x0,0x29,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x9b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x9b,0x0,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x9b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x9c,0x0,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x9d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x9d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x9e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x9e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x9e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x9f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x9f,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x9f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x9f,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0xa0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xba,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xa2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0xa2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0xa2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0xa3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0xa3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xa4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xa4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xa4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xa4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0xa5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0xa5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0xa5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x38,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0xab,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xab,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xac,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xac,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0xac,0x0,0x0,
+0x0,0x0,0x0,0x0,0xcd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0xae,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0xaf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0xb0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0xb1,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0xb2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xb2,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xb2,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0xb3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x50,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0xb6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xb8,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0xb8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xb9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xb9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xbb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xbb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xbb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0xbb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xbb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0xbb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0xbf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xbf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0xc1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0xc1,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc4,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0xc4,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xc7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x21,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0xc9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x70,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xcf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xcf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xcf,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xd0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0xd3,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdc,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xd9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xd9,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0xda,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xdb,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xdc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0xdc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xe1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0xe1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x43,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xe5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0xe5,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xe9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0xe9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xea,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0xea,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0xea,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0xea,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xeb,0x0,0x0,
+0x0,0x0,0x0,0x0,0xbf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xeb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xec,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xed,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xf1,0x0,0x0,
+0x0,0x0,0x0,0x0,0xcc,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0xf5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xf6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xf6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x89,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0xf8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0xf8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xf8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0xf8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0xf8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xfe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xfe,0x0,0x0,
+0x0,0x0,0x0,0x0,0xde,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x1,0x1,0x0,
+0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x2,0x1,0x0,
+0x0,0x0,0x0,0x0,0xcf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x3,0x1,0x0,
+0x0,0x0,0x0,0x0,0x25,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0xa,0x1,0x0,
+0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xb,0x1,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0xb,0x1,0x0,
+0x0,0x0,0x0,0x0,0x33,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xe,0x1,0x0,
+0x0,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x10,0x1,0x0,
+0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x11,0x1,0x0,
+0x0,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x13,0x1,0x0,
+0x0,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x14,0x1,0x0,
+0x0,0x0,0x0,0x0,0x13,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x16,0x1,0x0,
+0x0,0x0,0x0,0x0,0xd9,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x1c,0x1,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x1d,0x1,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x1d,0x1,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x1d,0x1,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x1d,0x1,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x1e,0x1,0x0,
+0x0,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x1f,0x1,0x0,
+0x0,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x1f,0x1,0x0,
+0x0,0x0,0x0,0x0,0x76,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x22,0x1,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x22,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x22,0x1,0x0,
+0x0,0x0,0x0,0x0,0x29,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x24,0x1,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x24,0x1,0x0,
+0x0,0x0,0x0,0x0,0x96,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x31,0x1,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x33,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x33,0x1,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x33,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x33,0x1,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x33,0x1,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x33,0x1,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x33,0x1,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x34,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x34,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x35,0x1,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x35,0x1,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x35,0x1,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x35,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x35,0x1,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x35,0x1,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x36,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x36,0x1,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x36,0x1,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x36,0x1,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x3e,0x1,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x43,0x1,0x0,
+0x0,0x0,0x0,0x0,0x8b,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x50,0x1,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x50,0x1,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x50,0x1,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x50,0x1,0x0,
+0x0,0x0,0x0,0x0,0xaf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x53,0x1,0x0,
+0x0,0x0,0x0,0x0,0x24,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x54,0x1,0x0,
+0x0,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x54,0x1,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x55,0x1,0x0,
+0x0,0x0,0x0,0x0,0xce,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x58,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x58,0x1,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x59,0x1,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x59,0x1,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x59,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x59,0x1,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x59,0x1,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x59,0x1,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x59,0x1,0x0,
+0x0,0x0,0x0,0x0,0x30,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x63,0x1,0x0,
+0x0,0x0,0x0,0x0,0xed,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x66,0x1,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x66,0x1,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x66,0x1,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x68,0x1,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x69,0x1,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x69,0x1,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x6a,0x1,0x0,
+0x0,0x0,0x0,0x0,0xa,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x71,0x1,0x0,
+0x0,0x0,0x0,0x0,0x54,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x74,0x1,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x77,0x1,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x77,0x1,0x0,
+0x0,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x77,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x7a,0x1,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x7b,0x1,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x7b,0x1,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x7b,0x1,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x7b,0x1,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x7b,0x1,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x7b,0x1,0x0,
+0x0,0x0,0x0,0x0,0xcf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x7e,0x1,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x83,0x1,0x0,
+0x0,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x84,0x1,0x0,
+0x0,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x86,0x1,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x89,0x1,0x0,
+0x0,0x0,0x0,0x0,0x68,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x8d,0x1,0x0,
+0x0,0x0,0x0,0x0,0x70,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x91,0x1,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x91,0x1,0x0,
+0x0,0x0,0x0,0x0,0xb2,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x9c,0x1,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x9d,0x1,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x9f,0x1,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x9f,0x1,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x9f,0x1,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0xa0,0x1,0x0,
+0x0,0x0,0x0,0x0,0xfa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xa1,0x1,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0xa1,0x1,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xa1,0x1,0x0,
+0x0,0x0,0x0,0x0,0x25,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0xa4,0x1,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xa7,0x1,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xa7,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0xa7,0x1,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0xa7,0x1,0x0,
+0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xa8,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0xa9,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xa9,0x1,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xa9,0x1,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0xa9,0x1,0x0,
+0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xa9,0x1,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0xaa,0x1,0x0,
+0x0,0x0,0x0,0x0,0x19,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xab,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0xab,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0xab,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xab,0x1,0x0,
+0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0xab,0x1,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0xab,0x1,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0xab,0x1,0x0,
+0x0,0x0,0x0,0x0,0xb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0xad,0x1,0x0,
+0x0,0x0,0x0,0x0,0x69,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0xb0,0x1,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0xb0,0x1,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xb8,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0xb8,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xbb,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0xbd,0x1,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0xbe,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0xc0,0x1,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0xc1,0x1,0x0,
+0x0,0x0,0x0,0x0,0x91,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0xc5,0x1,0x0,
+0x0,0x0,0x0,0x0,0x2,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0xcd,0x1,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0xcd,0x1,0x0,
+0x0,0x0,0x0,0x0,0x35,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0xd5,0x1,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xd6,0x1,0x0,
+0x0,0x0,0x0,0x0,0x5c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0xd6,0x1,0x0,
+0x0,0x0,0x0,0x0,0x57,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xe0,0x1,0x0,
+0x0,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0xe2,0x1,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0xe4,0x1,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xe6,0x1,0x0,
+0x0,0x0,0x0,0x0,0xf9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xe9,0x1,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0xea,0x1,0x0,
+0x0,0x0,0x0,0x0,0x43,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xf0,0x1,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf3,0x1,0x0,
+0x0,0x0,0x0,0x0,0xb2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xf4,0x1,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0xf5,0x1,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0xf5,0x1,0x0,
+0x0,0x0,0x0,0x0,0xdf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xf8,0x1,0x0,
+0x0,0x0,0x0,0x0,0xf0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0xfa,0x1,0x0,
+0x0,0x0,0x0,0x0,0x9d,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x6,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x8,0x2,0x0,
+0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x8,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xa,0x2,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0xb,0x2,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0xb,0x2,0x0,
+0x0,0x0,0x0,0x0,0xfa,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x11,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x11,0x2,0x0,
+0x0,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x14,0x2,0x0,
+0x0,0x0,0x0,0x0,0x92,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x17,0x2,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x18,0x2,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x19,0x2,0x0,
+0x0,0x0,0x0,0x0,0xec,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x1e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x22,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x24,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x25,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x25,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x26,0x2,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x27,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x28,0x2,0x0,
+0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x29,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x29,0x2,0x0,
+0x0,0x0,0x0,0x0,0x33,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x2a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x2b,0x2,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x2b,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x2e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x2f,0x2,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x30,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x30,0x2,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x30,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x31,0x2,0x0,
+0x0,0x0,0x0,0x0,0x67,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x37,0x2,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x37,0x2,0x0,
+0x0,0x0,0x0,0x0,0x96,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x38,0x2,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x39,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x3a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x3a,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x3c,0x2,0x0,
+0x0,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x3d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x3d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x3d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x3d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x3e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x68,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x42,0x2,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x43,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x46,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x46,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x47,0x2,0x0,
+0x0,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x47,0x2,0x0,
+0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x48,0x2,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x48,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x48,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x49,0x2,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x49,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x49,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x4c,0x2,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x4d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x4e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x4e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x4e,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x53,0x2,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x53,0x2,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x53,0x2,0x0,
+0x0,0x0,0x0,0x0,0x71,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x55,0x2,0x0,
+0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x55,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x56,0x2,0x0,
+0x0,0x0,0x0,0x0,0xfc,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x5b,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x5d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x5f,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x60,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x60,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x67,0x2,0x0,
+0x0,0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x68,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x6a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x6a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x41,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x74,0x2,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x74,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x75,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x75,0x2,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x75,0x2,0x0,
+0x0,0x0,0x0,0x0,0x73,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x77,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x77,0x2,0x0,
+0x0,0x0,0x0,0x0,0x69,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x79,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x79,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x7f,0x2,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x80,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x82,0x2,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x85,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x85,0x2,0x0,
+0x0,0x0,0x0,0x0,0x40,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x87,0x2,0x0,
+0x0,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x89,0x2,0x0,
+0x0,0x0,0x0,0x0,0x91,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x8a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x8a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x8a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x8b,0x2,0x0,
+0x0,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x8c,0x2,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x8c,0x2,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x8c,0x2,0x0,
+0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x8d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x8d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x8d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x8e,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x8e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x8e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x8e,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x8f,0x2,0x0,
+0x0,0x0,0x0,0x0,0xef,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x90,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x90,0x2,0x0,
+0x0,0x0,0x0,0x0,0x42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x91,0x2,0x0,
+0x0,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x91,0x2,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x92,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x92,0x2,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x92,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x92,0x2,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x93,0x2,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x93,0x2,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x93,0x2,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x93,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x96,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x96,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x9a,0x2,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x9b,0x2,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x9b,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x9b,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x9b,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x9b,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x9b,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x9b,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x9d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x9d,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x9d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x9d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x9d,0x2,0x0,
+0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x9e,0x2,0x0,
+0x0,0x0,0x0,0x0,0x73,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xa9,0x2,0x0,
+0x0,0x0,0x0,0x0,0x11,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xac,0x2,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0xad,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xaf,0x2,0x0,
+0x0,0x0,0x0,0x0,0x35,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0xb0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0xb2,0x2,0x0,
+0x0,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xb6,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xb7,0x2,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xb7,0x2,0x0,
+0x0,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xb8,0x2,0x0,
+0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xb9,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0xba,0x2,0x0,
+0x0,0x0,0x0,0x0,0x76,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xbc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0xbc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0xc1,0x2,0x0,
+0x0,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xc2,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xc2,0x2,0x0,
+0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xc2,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xc2,0x2,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0xc3,0x2,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0xc3,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xc3,0x2,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0xc3,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xc6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xc6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xc6,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0xc7,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0xc7,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xc7,0x2,0x0,
+0x0,0x0,0x0,0x0,0x57,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0xc8,0x2,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xc9,0x2,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xca,0x2,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xca,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0xca,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xca,0x2,0x0,
+0x0,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xcb,0x2,0x0,
+0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0xcb,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xcb,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xcb,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0xcb,0x2,0x0,
+0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xcc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xcc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xcd,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0xcd,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xcd,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xcd,0x2,0x0,
+0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xce,0x2,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xcf,0x2,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0xcf,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xcf,0x2,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0xd0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0xd0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xd1,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0xd3,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0xd3,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0xd3,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0xd6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x72,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xd7,0x2,0x0,
+0x0,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0xd8,0x2,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xda,0x2,0x0,
+0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xda,0x2,0x0,
+0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0xdb,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0xdc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xdd,0x2,0x0,
+0x0,0x0,0x0,0x0,0xce,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xdf,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0xdf,0x2,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xdf,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xe0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xe1,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0xe3,0x2,0x0,
+0x0,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xe4,0x2,0x0,
+0x0,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0xe4,0x2,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xe4,0x2,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xe4,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0xe5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0xe6,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0xe7,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0xe8,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0xe9,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xe9,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xe9,0x2,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xe9,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xe9,0x2,0x0,
+0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0xea,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0xea,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0xec,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xec,0x2,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0xec,0x2,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xec,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0xec,0x2,0x0,
+0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0xed,0x2,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xed,0x2,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xee,0x2,0x0,
+0x0,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0xef,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0xf0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0xf0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xf0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xf1,0x2,0x0,
+0x0,0x0,0x0,0x0,0x95,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0xf1,0x2,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0xf2,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0xf2,0x2,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0xf2,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xf2,0x2,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0xf2,0x2,0x0,
+0x0,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xf3,0x2,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xf4,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0xf4,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xf5,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xf6,0x2,0x0,
+0x0,0x0,0x0,0x0,0x23,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xf8,0x2,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0xf8,0x2,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xfa,0x2,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xfa,0x2,0x0,
+0x0,0x0,0x0,0x0,0x69,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0xfc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0xfc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xfc,0x2,0x0,
+0x0,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xfd,0x2,0x0,
+0x0,0x0,0x0,0x0,0x85,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0xfd,0x2,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xfd,0x2,0x0,
+0x0,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xfe,0x2,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0xfe,0x2,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xff,0x2,0x0,
+0x0,0x0,0x0,0x0,0x63,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x23,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x1,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x3,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x7,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x21,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x8,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x9,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0xa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0xa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0xa,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0xa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0xb,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0xb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0xb,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xd,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0xd,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0xd,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0xe,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0xe,0x3,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0xf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0xf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x10,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x10,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x10,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x11,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x12,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x12,0x3,0x0,
+0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x12,0x3,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x12,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x13,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x13,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x14,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x14,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x14,0x3,0x0,
+0x0,0x0,0x0,0x0,0x83,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x16,0x3,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x16,0x3,0x0,
+0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x16,0x3,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x16,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x16,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x16,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x16,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x17,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x17,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x17,0x3,0x0,
+0x0,0x0,0x0,0x0,0x61,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x1b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x1b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x1b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x1e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x1e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x1f,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x1f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x1f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x1f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x1f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x20,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x20,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x20,0x3,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x21,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x24,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x24,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x25,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x27,0x3,0x0,
+0x0,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x27,0x3,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x27,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x27,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x28,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x28,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x28,0x3,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x28,0x3,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x29,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x29,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x29,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x29,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x29,0x3,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x29,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x29,0x3,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x2a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x2b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x2b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x2b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x2c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x2c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x2c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x2d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x2d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x2d,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x2e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x2f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x2f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x2f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x30,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x30,0x3,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x30,0x3,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x30,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x31,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x31,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x31,0x3,0x0,
+0x0,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x32,0x3,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x32,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x32,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x32,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x32,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x32,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x33,0x3,0x0,
+0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x33,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x34,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x34,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x34,0x3,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x34,0x3,0x0,
+0x0,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x36,0x3,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x36,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x36,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x37,0x3,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x37,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x37,0x3,0x0,
+0x0,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x38,0x3,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x39,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x39,0x3,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x3a,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x3a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x3a,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x3a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x33,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x3b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x3b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x79,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x3e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x3e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x3e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x3f,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x3f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x3f,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x41,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x41,0x3,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x41,0x3,0x0,
+0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x42,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x43,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x43,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x43,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x45,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x45,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x45,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x46,0x3,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x46,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x46,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x46,0x3,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x46,0x3,0x0,
+0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x47,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x47,0x3,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x47,0x3,0x0,
+0x0,0x0,0x0,0x0,0xcf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x4a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x4a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x4a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x4a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x4e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x45,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x4f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x4f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x4f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x4f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x50,0x3,0x0,
+0x0,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x53,0x3,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x54,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x54,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x54,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x54,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x56,0x3,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x56,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x58,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x58,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x59,0x3,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x59,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x5a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x5a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x5a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x5a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x5b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x5c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x41,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x5d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x5e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x5f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x5f,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x60,0x3,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x60,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x60,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x61,0x3,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x61,0x3,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x61,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x63,0x3,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x63,0x3,0x0,
+0x0,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x64,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x64,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x64,0x3,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x64,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x64,0x3,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x65,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc4,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x3,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x67,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x67,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x68,0x3,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x68,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x68,0x3,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x69,0x3,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x69,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x6a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x6a,0x3,0x0,
+0x0,0x0,0x0,0x0,0xad,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x6b,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x6c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x6c,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x6c,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x6d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x6d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x6d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x35,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x73,0x3,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x73,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x74,0x3,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x74,0x3,0x0,
+0x0,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x74,0x3,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x77,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x78,0x3,0x0,
+0x0,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x78,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x78,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x78,0x3,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x79,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x79,0x3,0x0,
+0x0,0x0,0x0,0x0,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x7a,0x3,0x0,
+0x0,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x7d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x7d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x7d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x7d,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x7e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x7e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x7f,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x80,0x3,0x0,
+0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x81,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x81,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x81,0x3,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x82,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x82,0x3,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x82,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x82,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x82,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x83,0x3,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x84,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x84,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x84,0x3,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x85,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x85,0x3,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x85,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x86,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x86,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x86,0x3,0x0,
+0x0,0x0,0x0,0x0,0x85,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x87,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x87,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x87,0x3,0x0,
+0x0,0x0,0x0,0x0,0xce,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x8a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x8a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x61,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x8c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x90,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x92,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x95,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x95,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x96,0x3,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x96,0x3,0x0,
+0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x96,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x96,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x98,0x3,0x0,
+0x0,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x99,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x9a,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x9a,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x9c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x9c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x9c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x9c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x9c,0x3,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x9d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x9d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x9d,0x3,0x0,
+0x0,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x9e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x9e,0x3,0x0,
+0x0,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x9f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x9f,0x3,0x0,
+0x0,0x0,0x0,0x0,0x87,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xa0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xa0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xa0,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xa0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xa1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0xa1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xa2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0xa5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0xa5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0xa6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0xa6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xa6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0xa6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xa6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xa6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xa7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0xa7,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xa9,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xa9,0x3,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xa9,0x3,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xa9,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xa9,0x3,0x0,
+0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xaa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xaa,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xaa,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xab,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xac,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0xad,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0xae,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xae,0x3,0x0,
+0x0,0x0,0x0,0x0,0x95,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xae,0x3,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xae,0x3,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xaf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x27,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xb1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0xb1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0xb2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0xb2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0xb2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0xb2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0xb2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0xb3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xb4,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xb4,0x3,0x0,
+0x0,0x0,0x0,0x0,0x29,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xb5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0xb5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xb5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0xb5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xb6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xb6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xb6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xb7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0xb7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xb7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0xb7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xb7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0xb7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0xb8,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xb8,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0xb8,0x3,0x0,
+0x0,0x0,0x0,0x0,0x97,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xba,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xba,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xba,0x3,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xba,0x3,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0xbb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xbb,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0xbb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xbb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xbb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0xbc,0x3,0x0,
+0x0,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0xbc,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xbc,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0xbc,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0xbc,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xbd,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0xbe,0x3,0x0,
+0x0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xbe,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0xbe,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0xbf,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0xc0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xc0,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xc0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xc0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0xc0,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xc0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xc0,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0xc2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0xc2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xc2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xc2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xc3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0xc3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0xc3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xc3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xc3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xc4,0x3,0x0,
+0x0,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0xc6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xc6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x52,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0xc7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xc7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xc7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xc8,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xc8,0x3,0x0,
+0x0,0x0,0x0,0x0,0x59,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xca,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0xca,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0xca,0x3,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xcb,0x3,0x0,
+0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0xcc,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0xcc,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0xce,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xcf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xcf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x49,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xd2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0xd2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xd3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xd3,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xd4,0x3,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xd4,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xd4,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xd6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xd6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xd7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0xd7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0xd7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xd7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0xd7,0x3,0x0,
+0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xd8,0x3,0x0,
+0x0,0x0,0x0,0x0,0x50,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0xdb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xdb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xdd,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0xde,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xde,0x3,0x0,
+0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xdf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xdf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xdf,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xdf,0x3,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xe1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0xe1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xe1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xe2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0xe2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x62,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xe3,0x3,0x0,
+0x0,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xe5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xe5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xe6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xe6,0x3,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xe8,0x3,0x0,
+0x0,0x0,0x0,0x0,0x53,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xe9,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x3,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0xec,0x3,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xec,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xed,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0xed,0x3,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0xed,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xed,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0xed,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xee,0x3,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0xee,0x3,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xee,0x3,0x0,
+0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0xee,0x3,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0xee,0x3,0x0,
+0x0,0x0,0x0,0x0,0x85,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0xef,0x3,0x0,
+0x0,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xf0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0xf0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xf0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xf1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0xf1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0xf1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xf1,0x3,0x0,
+0x0,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0xf1,0x3,0x0,
+0x0,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xf2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xf2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xf2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0xf2,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xf3,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0xf5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xf5,0x3,0x0,
+0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xf5,0x3,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xf6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0xf6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xf6,0x3,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xf6,0x3,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xf7,0x3,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0xfb,0x3,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xfd,0x3,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x2,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x3,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x3,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x3,0x4,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x3,0x4,0x0,
+0x0,0x0,0x0,0x0,0xbf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x4,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x4,0x4,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x5,0x4,0x0,
+0x0,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x6,0x4,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x6,0x4,0x0,
+0x0,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x7,0x4,0x0,
+0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x7,0x4,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x7,0x4,0x0,
+0x0,0x0,0x0,0x0,0xad,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x9,0x4,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x9,0x4,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xa,0x4,0x0,
+0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xb,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0xc,0x4,0x0,
+0x0,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xd,0x4,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0xd,0x4,0x0,
+0x0,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0xe,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xe,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0xe,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0xe,0x4,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xe,0x4,0x0,
+0x0,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xf,0x4,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xf,0x4,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xf,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xf,0x4,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xf,0x4,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xf,0x4,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x10,0x4,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x10,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x10,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x12,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x12,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x12,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x12,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x12,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x13,0x4,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x13,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x13,0x4,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x13,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x13,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x13,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x14,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x17,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x17,0x4,0x0,
+0x0,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x17,0x4,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x17,0x4,0x0,
+0x0,0x0,0x0,0x0,0xad,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x18,0x4,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x1a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x1a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x1a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x1a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x1b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x1b,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x1b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x1c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x1e,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x1e,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x1e,0x4,0x0,
+0x0,0x0,0x0,0x0,0xb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x1f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x1f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x1f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x20,0x4,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x20,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x22,0x4,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x25,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x25,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x27,0x4,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x28,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x29,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x29,0x4,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x2a,0x4,0x0,
+0x0,0x0,0x0,0x0,0xc7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x2c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x2c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x2c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x2c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x2c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x2c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x2c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x2d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x2d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x2d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x2d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x2d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x2d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x2e,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x30,0x4,0x0,
+0x0,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x30,0x4,0x0,
+0x0,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x31,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x31,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x31,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x31,0x4,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x31,0x4,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x32,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x34,0x4,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x34,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x34,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x35,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x35,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x35,0x4,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x35,0x4,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x36,0x4,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x36,0x4,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x36,0x4,0x0,
+0x0,0x0,0x0,0x0,0xbb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x38,0x4,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x38,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x38,0x4,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x39,0x4,0x0,
+0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x39,0x4,0x0,
+0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x3a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x3a,0x4,0x0,
+0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x3b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x3b,0x4,0x0,
+0x0,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x3c,0x4,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x3c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x3c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x3d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x3d,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x3e,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x3e,0x4,0x0,
+0x0,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x3f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x3f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x3f,0x4,0x0,
+0x0,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x41,0x4,0x0,
+0x0,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x42,0x4,0x0,
+0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x43,0x4,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x43,0x4,0x0,
+0x0,0x0,0x0,0x0,0x39,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x44,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x44,0x4,0x0,
+0x0,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x45,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x47,0x4,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x47,0x4,0x0,
+0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x48,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x48,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x48,0x4,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x48,0x4,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x49,0x4,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x4a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x4a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x4a,0x4,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x4b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x4b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x4b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x4b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x4b,0x4,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x4b,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x4c,0x4,0x0,
+0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x4d,0x4,0x0,
+0x0,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x4f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x4f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x4f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x4f,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x4f,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x50,0x4,0x0,
+0x0,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x51,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x51,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x51,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x51,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x51,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x51,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x53,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x54,0x4,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x54,0x4,0x0,
+0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x54,0x4,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x55,0x4,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x55,0x4,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x55,0x4,0x0,
+0x0,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x56,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x5b,0x4,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x62,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x73,0x4,0x0,
+0x0,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x74,0x4,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x77,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x7f,0x4,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x9d,0x4,0x0,
+0x0,0x0,0x0,0x0,0xb4,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0xa6,0x4,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0xa6,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0xa8,0x4,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xa8,0x4,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0xa8,0x4,0x0,
+0x0,0x0,0x0,0x0,0x82,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xb5,0x4,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0xb5,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xb9,0x4,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0xbd,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd2,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xee,0x4,0x0,
+0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xee,0x4,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xee,0x4,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xf1,0x4,0x0,
+0x0,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xf3,0x4,0x0,
+0x0,0x0,0x0,0x0,0xb,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xf8,0x4,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0xf8,0x4,0x0,
+0x0,0x0,0x0,0x0,0x41,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xfe,0x4,0x0,
+0x0,0x0,0x0,0x0,0x31,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x10,0x5,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x23,0x5,0x0,
+0x0,0x0,0x0,0x0,0x7b,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x2f,0x5,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x33,0x5,0x0,
+0x0,0x0,0x0,0x0,0x81,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x45,0x5,0x0,
+0x0,0x0,0x0,0x0,0xd2,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x6c,0x5,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xad,0x5,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x1b,0x6,0x0,
+0x0,0x0,0x0,0x0,0x96,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x2e,0x6,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x30,0x6,0x0,
+0x0,0x0,0x0,0x0,0x60,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x37,0x6,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x37,0x6,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x37,0x6,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x37,0x6,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x37,0x6,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x37,0x6,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x37,0x6,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x38,0x6,0x0,
+0x0,0x0,0x0,0x0,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x39,0x6,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x39,0x6,0x0,
+0x0,0x0,0x0,0x0,0xd6,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x5b,0x6,0x0,
+0x0,0x0,0x0,0x0,0x68,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x60,0x6,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x60,0x6,0x0,
+0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x61,0x6,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x61,0x6,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x61,0x6,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x61,0x6,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x61,0x6,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x61,0x6,0x0,
+0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x62,0x6,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x62,0x6,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x64,0x6,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x64,0x6,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x64,0x6,0x0,
+0x0,0x0,0x0,0x0,0x86,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x6c,0x6,0x0,
+0x0,0x0,0x0,0x0,0xc8,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x76,0x6,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x78,0x6,0x0,
+0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x79,0x6,0x0,
+0x0,0x0,0x0,0x0,0x10,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x80,0x6,0x0,
+0x0,0x0,0x0,0x0,0x2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x82,0x6,0x0,
+0x0,0x0,0x0,0x0,0xab,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x8d,0x6,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0xa9,0x6,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xa9,0x6,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xa9,0x6,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0xa9,0x6,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xa9,0x6,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0xa9,0x6,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xaa,0x6,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xaa,0x6,0x0,
+0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0xaa,0x6,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0xaa,0x6,0x0,
+0x0,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0xae,0x6,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0xae,0x6,0x0,
+0x0,0x0,0x0,0x0,0x6b,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0xb8,0x6,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xb8,0x6,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xb8,0x6,0x0,
+0x0,0x0,0x0,0x0,0xa2,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xc2,0x6,0x0,
+0x0,0x0,0x0,0x0,0x31,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xca,0x6,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xcb,0x6,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0xcc,0x6,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xcc,0x6,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xcc,0x6,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xcd,0x6,0x0,
+0x0,0x0,0x0,0x0,0x44,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0xf0,0x6,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0xf0,0x6,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xf7,0x6,0x0,
+0x0,0x0,0x0,0x0,0x88,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x1,0x7,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x1,0x7,0x0,
+0x0,0x0,0x0,0x0,0x45,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x2,0x7,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x2,0x7,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x3,0x7,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x3,0x7,0x0,
+0x0,0x0,0x0,0x0,0x82,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x5,0x7,0x0,
+0x0,0x0,0x0,0x0,0x7,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xb,0x7,0x0,
+0x0,0x0,0x0,0x0,0xd8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0xd,0x7,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xd,0x7,0x0,
+0x0,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xd,0x7,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xe,0x7,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x2b,0x7,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x2d,0x7,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x2e,0x7,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x2e,0x7,0x0,
+0x0,0x0,0x0,0x0,0x51,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x34,0x7,0x0,
+0x0,0x0,0x0,0x0,0xf1,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x43,0x7,0x0,
+0x0,0x0,0x0,0x0,0x63,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x4b,0x7,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x4b,0x7,0x0,
+0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x4c,0x7,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x4c,0x7,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x4c,0x7,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x4c,0x7,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x4e,0x7,0x0,
+0x0,0x0,0x0,0x0,0xf9,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x60,0x7,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x62,0x7,0x0,
+0x0,0x0,0x0,0x0,0x6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x63,0x7,0x0,
+0x0,0x0,0x0,0x0,0x85,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x69,0x7,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x69,0x7,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x69,0x7,0x0,
+0x0,0x0,0x0,0x0,0x39,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x70,0x7,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x70,0x7,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x70,0x7,0x0,
+0x0,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x70,0x7,0x0,
+0x0,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x71,0x7,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x71,0x7,0x0,
+0x0,0x0,0x0,0x0,0x19,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x76,0x7,0x0,
+0x0,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x78,0x7,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x78,0x7,0x0,
+0x0,0x0,0x0,0x0,0x42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x79,0x7,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x79,0x7,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x7f,0x7,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x80,0x7,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x80,0x7,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x80,0x7,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x80,0x7,0x0,
+0x0,0x0,0x0,0x0,0x75,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x95,0x7,0x0,
+0x0,0x0,0x0,0x0,0x92,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x97,0x7,0x0,
+0x0,0x0,0x0,0x0,0xf7,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xa3,0x7,0x0,
+0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xa4,0x7,0x0,
+0x0,0x0,0x0,0x0,0x49,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xa5,0x7,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0xa5,0x7,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xac,0x7,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xac,0x7,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0xac,0x7,0x0,
+0x0,0x0,0x0,0x0,0x4e,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xba,0x7,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0xc2,0x7,0x0,
+0x0,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xc3,0x7,0x0,
+0x0,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xc6,0x7,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0xe3,0x7,0x0,
+0x0,0x0,0x0,0x0,0x33,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0xe4,0x7,0x0,
+0x0,0x0,0x0,0x0,0xef,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0xee,0x7,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0xf8,0x7,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xf8,0x7,0x0,
+0x0,0x0,0x0,0x0,0x18,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xf9,0x7,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0xf9,0x7,0x0,
+0x0,0x0,0x0,0x0,0x97,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x1,0x8,0x0,
+0x0,0x0,0x0,0x0,0xa5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x3,0x8,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0xc,0x8,0x0,
+0x0,0x0,0x0,0x0,0x65,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x2b,0x8,0x0,
+0x0,0x0,0x0,0x0,0x50,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x2d,0x8,0x0,
+0x0,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x2e,0x8,0x0,
+0x0,0x0,0x0,0x0,0xc8,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x36,0x8,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x36,0x8,0x0,
+0x0,0x0,0x0,0x0,0x78,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x42,0x8,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x42,0x8,0x0,
+0x0,0x0,0x0,0x0,0xad,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x58,0x8,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x58,0x8,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x58,0x8,0x0,
+0x0,0x0,0x0,0x0,0x52,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x65,0x8,0x0,
+0x0,0x0,0x0,0x0,0x27,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x6b,0x8,0x0,
+0x0,0x0,0x0,0x0,0x45,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x76,0x8,0x0,
+0x0,0x0,0x0,0x0,0x45,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x80,0x8,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x80,0x8,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x82,0x8,0x0,
+0x0,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x84,0x8,0x0,
+0x0,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x86,0x8,0x0,
+0x0,0x0,0x0,0x0,0xe,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x8b,0x8,0x0,
+0x0,0x0,0x0,0x0,0x43,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x97,0x8,0x0,
+0x0,0x0,0x0,0x0,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0xa0,0x8,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xa1,0x8,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0xa1,0x8,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0xa1,0x8,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xa1,0x8,0x0,
+0x0,0x0,0x0,0x0,0xf,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xb7,0x8,0x0,
+0x0,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0xb7,0x8,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xbf,0x8,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xc0,0x8,0x0,
+0x0,0x0,0x0,0x0,0xd5,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x8,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0xca,0x8,0x0,
+0x0,0x0,0x0,0x0,0x58,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0xcb,0x8,0x0,
+0x0,0x0,0x0,0x0,0x4f,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0xd5,0x8,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xd5,0x8,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xd6,0x8,0x0,
+0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xd6,0x8,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0xd6,0x8,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xd6,0x8,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xdd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xdd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xdd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xdd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xdd,0x8,0x0,
+0x0,0x0,0x0,0x0,0xdc,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xe6,0x8,0x0,
+0x0,0x0,0x0,0x0,0x20,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xeb,0x8,0x0,
+0x0,0x0,0x0,0x0,0xe7,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x9,0x9,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0xc,0x9,0x0,
+0x0,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x11,0x9,0x0,
+0x0,0x0,0x0,0x0,0xd,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x1f,0x9,0x0,
+0x0,0x0,0x0,0x0,0x44,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x2b,0x9,0x0,
+0x0,0x0,0x0,0x0,0xd5,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x51,0x9,0x0,
+0x0,0x0,0x0,0x0,0x40,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x52,0x9,0x0,
+0x0,0x0,0x0,0x0,0x40,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x6b,0x9,0x0,
+0x0,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x6b,0x9,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x6b,0x9,0x0,
+0x0,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x70,0x9,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x77,0x9,0x0,
+0x0,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x79,0x9,0x0,
+0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x7a,0x9,0x0,
+0x0,0x0,0x0,0x0,0x93,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x7f,0x9,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x7f,0x9,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x7f,0x9,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x80,0x9,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x80,0x9,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x80,0x9,0x0,
+0x0,0x0,0x0,0x0,0xf7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x82,0x9,0x0,
+0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x82,0x9,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x8c,0x9,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xb7,0x9,0x0,
+0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xb8,0x9,0x0,
+0x0,0x0,0x0,0x0,0xce,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0xba,0x9,0x0,
+0x0,0x0,0x0,0x0,0xb4,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0xc6,0x9,0x0,
+0x0,0x0,0x0,0x0,0xd7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0xc8,0x9,0x0,
+0x0,0x0,0x0,0x0,0x72,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0xf2,0x9,0x0,
+0x0,0x0,0x0,0x0,0x2e,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xff,0x9,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x0,0xa,0x0,
+0x0,0x0,0x0,0x0,0x78,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x2,0xa,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xb,0xa,0x0,
+0x0,0x0,0x0,0x0,0xd5,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x16,0xa,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x19,0xa,0x0,
+0x0,0x0,0x0,0x0,0x53,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x39,0xa,0x0,
+0x0,0x0,0x0,0x0,0x8e,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x47,0xa,0x0,
+0x0,0x0,0x0,0x0,0xa2,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x4e,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x4e,0xa,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x4e,0xa,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x4e,0xa,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x4e,0xa,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x4e,0xa,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x4f,0xa,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x4f,0xa,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x50,0xa,0x0,
+0x0,0x0,0x0,0x0,0xf,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x55,0xa,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x5b,0xa,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x5b,0xa,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x5b,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x5e,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x5f,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x62,0xa,0x0,
+0x0,0x0,0x0,0x0,0x84,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x71,0xa,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x75,0xa,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x75,0xa,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x75,0xa,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x75,0xa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x78,0xa,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x79,0xa,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x79,0xa,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x79,0xa,0x0,
+0x0,0x0,0x0,0x0,0x53,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x7f,0xa,0x0,
+0x0,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x80,0xa,0x0,
+0x0,0x0,0x0,0x0,0xc4,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x8b,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x8c,0xa,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x8c,0xa,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x8c,0xa,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x8c,0xa,0x0,
+0x0,0x0,0x0,0x0,0x68,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x8e,0xa,0x0,
+0x0,0x0,0x0,0x0,0x43,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x8f,0xa,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x8f,0xa,0x0,
+0x0,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x91,0xa,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x92,0xa,0x0,
+0x0,0x0,0x0,0x0,0xc4,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x9e,0xa,0x0,
+0x0,0x0,0x0,0x0,0xca,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xa1,0xa,0x0,
+0x0,0x0,0x0,0x0,0x16,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0xaa,0xa,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0xb4,0xa,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xb4,0xa,0x0,
+0x0,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0xb6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0xb7,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xb9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x14,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0xba,0xa,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0xba,0xa,0x0,
+0x0,0x0,0x0,0x0,0x96,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xbc,0xa,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xbc,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0xbc,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0xbc,0xa,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0xbc,0xa,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0xbc,0xa,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xbd,0xa,0x0,
+0x0,0x0,0x0,0x0,0x20,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0xbe,0xa,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xbe,0xa,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xbf,0xa,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0xc3,0xa,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xc3,0xa,0x0,
+0x0,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0xc5,0xa,0x0,
+0x0,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xc6,0xa,0x0,
+0x0,0x0,0x0,0x0,0x42,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xc8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xc9,0xa,0x0,
+0x0,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xca,0xa,0x0,
+0x0,0x0,0x0,0x0,0x82,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xcd,0xa,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0xcd,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4a,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xd7,0xa,0x0,
+0x0,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0xd8,0xa,0x0,
+0x0,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xd9,0xa,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xdb,0xa,0x0,
+0x0,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0xdc,0xa,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xdc,0xa,0x0,
+0x0,0x0,0x0,0x0,0xf7,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0xe5,0xa,0x0,
+0x0,0x0,0x0,0x0,0xc9,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0xfb,0xa,0x0,
+0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0xfc,0xa,0x0,
+0x0,0x0,0x0,0x0,0x81,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x13,0xb,0x0,
+0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x14,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x14,0xb,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x15,0xb,0x0,
+0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x16,0xb,0x0,
+0x0,0x0,0x0,0x0,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x17,0xb,0x0,
+0x0,0x0,0x0,0x0,0x74,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x1d,0xb,0x0,
+0x0,0x0,0x0,0x0,0x8c,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x2b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x2b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x2b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x2b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x2b,0xb,0x0,
+0x0,0x0,0x0,0x0,0xc8,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x46,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x46,0xb,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x46,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x46,0xb,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x46,0xb,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x47,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x47,0xb,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x47,0xb,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x47,0xb,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x47,0xb,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x47,0xb,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x48,0xb,0x0,
+0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x48,0xb,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x48,0xb,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x48,0xb,0x0,
+0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x49,0xb,0x0,
+0x0,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x49,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x49,0xb,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x49,0xb,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x49,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x4a,0xb,0x0,
+0x0,0x0,0x0,0x0,0x99,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x4a,0xb,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x4a,0xb,0x0,
+0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x4b,0xb,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x4b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x4b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x4e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x4f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x4f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x4f,0xb,0x0,
+0x0,0x0,0x0,0x0,0xab,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x53,0xb,0x0,
+0x0,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x53,0xb,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x53,0xb,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x54,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x54,0xb,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x54,0xb,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x54,0xb,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x54,0xb,0x0,
+0x0,0x0,0x0,0x0,0x95,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x54,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x54,0xb,0x0,
+0x0,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x55,0xb,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x55,0xb,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x55,0xb,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x55,0xb,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x55,0xb,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x55,0xb,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x56,0xb,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x56,0xb,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x56,0xb,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x56,0xb,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x57,0xb,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x57,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x57,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x57,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x57,0xb,0x0,
+0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x58,0xb,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x58,0xb,0x0,
+0x0,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x58,0xb,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x58,0xb,0x0,
+0x0,0x0,0x0,0x0,0xfe,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x5a,0xb,0x0,
+0x0,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x5b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x5e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x5e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x5e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x44,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x60,0xb,0x0,
+0x0,0x0,0x0,0x0,0x18,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x61,0xb,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x61,0xb,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x61,0xb,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x61,0xb,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x61,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x61,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xb,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x62,0xb,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x62,0xb,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x62,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x62,0xb,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x62,0xb,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x62,0xb,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x63,0xb,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x63,0xb,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x63,0xb,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x63,0xb,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x63,0xb,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x63,0xb,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x64,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x64,0xb,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x64,0xb,0x0,
+0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x64,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x65,0xb,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x65,0xb,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x65,0xb,0x0,
+0x0,0x0,0x0,0x0,0x9f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x69,0xb,0x0,
+0x0,0x0,0x0,0x0,0xbb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x6c,0xb,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x6c,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x6c,0xb,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x6c,0xb,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x6c,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x6c,0xb,0x0,
+0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x6d,0xb,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x6d,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x6d,0xb,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x6d,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x6d,0xb,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x6d,0xb,0x0,
+0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x6e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x6e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x6e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x6e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x6e,0xb,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x6e,0xb,0x0,
+0x0,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x6f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x6f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x6f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x6f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x6f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x6f,0xb,0x0,
+0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x70,0xb,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x70,0xb,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x70,0xb,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x70,0xb,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x70,0xb,0x0,
+0x0,0x0,0x0,0x0,0xa3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x71,0xb,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x71,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x71,0xb,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x71,0xb,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x83,0xb,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x83,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x83,0xb,0x0,
+0x0,0x0,0x0,0x0,0xcf,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x90,0xb,0x0,
+0x0,0x0,0x0,0x0,0x66,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x9b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x9b,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x9c,0xb,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x9c,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x9d,0xb,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x9d,0xb,0x0,
+0x0,0x0,0x0,0x0,0xdf,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0xa4,0xb,0x0,
+0x0,0x0,0x0,0x0,0xed,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xa4,0xb,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xa7,0xb,0x0,
+0x0,0x0,0x0,0x0,0x58,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xb0,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0xb1,0xb,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0xb1,0xb,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0xb1,0xb,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xb1,0xb,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0xb2,0xb,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0xb3,0xb,0x0,
+0x0,0x0,0x0,0x0,0xf3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xb5,0xb,0x0,
+0x0,0x0,0x0,0x0,0x53,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xbb,0xb,0x0,
+0x0,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0xbb,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0xbb,0xb,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xbb,0xb,0x0,
+0x0,0x0,0x0,0x0,0x32,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xc1,0xb,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xc6,0xb,0x0,
+0x0,0x0,0x0,0x0,0x45,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0xcf,0xb,0x0,
+0x0,0x0,0x0,0x0,0xc5,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0xf8,0xb,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xf8,0xb,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x21,0xc,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x43,0xc,0x0,
+0x0,0x0,0x0,0x0,0x77,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x49,0xc,0x0,
+0x0,0x0,0x0,0x0,0x34,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x57,0xc,0x0,
+0x0,0x0,0x0,0x0,0xed,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x58,0xc,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x61,0xc,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x62,0xc,0x0,
+0x0,0x0,0x0,0x0,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x6c,0xc,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x6c,0xc,0x0,
+0x0,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x6e,0xc,0x0,
+0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x6f,0xc,0x0,
+0x0,0x0,0x0,0x0,0x22,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x7a,0xc,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x7c,0xc,0x0,
+0x0,0x0,0x0,0x0,0x45,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0xa0,0xc,0x0,
+0x0,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xa0,0xc,0x0,
+0x0,0x0,0x0,0x0,0xda,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xad,0xc,0x0,
+0x0,0x0,0x0,0x0,0x78,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0xaf,0xc,0x0,
+0x0,0x0,0x0,0x0,0x95,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0xb5,0xc,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xb5,0xc,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xb6,0xc,0x0,
+0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xb6,0xc,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xb9,0xc,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xb9,0xc,0x0,
+0x0,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0xba,0xc,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0xbf,0xc,0x0,
+0x0,0x0,0x0,0x0,0xdf,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0xc5,0xc,0x0,
+0x0,0x0,0x0,0x0,0xfa,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xd4,0xc,0x0,
+0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0xd5,0xc,0x0,
+0x0,0x0,0x0,0x0,0x3,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0xf7,0xc,0x0,
+0x0,0x0,0x0,0x0,0x9d,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x5,0xd,0x0,
+0x0,0x0,0x0,0x0,0x50,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x8,0xd,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x11,0xd,0x0,
+0x0,0x0,0x0,0x0,0x25,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x14,0xd,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x14,0xd,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x14,0xd,0x0,
+0x0,0x0,0x0,0x0,0x1b,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x1e,0xd,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x1e,0xd,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x1e,0xd,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x1e,0xd,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x1e,0xd,0x0,
+0x0,0x0,0x0,0x0,0xee,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x21,0xd,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x21,0xd,0x0,
+0x0,0x0,0x0,0x0,0xb4,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x2e,0xd,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x2e,0xd,0x0,
+0x0,0x0,0x0,0x0,0xa,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x5d,0xd,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x5e,0xd,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x5e,0xd,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x5e,0xd,0x0,
+0x0,0x0,0x0,0x0,0xb1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x60,0xd,0x0,
+0x0,0x0,0x0,0x0,0xb5,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x82,0xd,0x0,
+0x0,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x84,0xd,0x0,
+0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x84,0xd,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x84,0xd,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x84,0xd,0x0,
+0x0,0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x85,0xd,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xa7,0xd,0x0,
+0x0,0x0,0x0,0x0,0xed,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xab,0xd,0x0,
+0x0,0x0,0x0,0x0,0x6b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0xaf,0xd,0x0,
+0x0,0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xb4,0xd,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xb5,0xd,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xb7,0xd,0x0,
+0x0,0x0,0x0,0x0,0x90,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xb8,0xd,0x0,
+0x0,0x0,0x0,0x0,0x25,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xc3,0xd,0x0,
+0x0,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xc3,0xd,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0xc3,0xd,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xc4,0xd,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xc4,0xd,0x0,
+0x0,0x0,0x0,0x0,0x78,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xd2,0xd,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xd3,0xd,0x0,
+0x0,0x0,0x0,0x0,0xc9,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xdb,0xd,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xdb,0xd,0x0,
+0x0,0x0,0x0,0x0,0xed,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xdd,0xd,0x0,
+0x0,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0xdd,0xd,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0xde,0xd,0x0,
+0x0,0x0,0x0,0x0,0x27,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xf5,0xd,0x0,
+0x0,0x0,0x0,0x0,0x6d,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x1,0xe,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x2,0xe,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x3,0xe,0x0,
+0x0,0x0,0x0,0x0,0xab,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x4,0xe,0x0,
+0x0,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x6,0xe,0x0,
+0x0,0x0,0x0,0x0,0x13,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x8,0xe,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x8,0xe,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x10,0xe,0x0,
+0x0,0x0,0x0,0x0,0x94,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x11,0xe,0x0,
+0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x12,0xe,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x12,0xe,0x0,
+0x0,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x14,0xe,0x0,
+0x0,0x0,0x0,0x0,0xbb,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x1b,0xe,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x1b,0xe,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x1b,0xe,0x0,
+0x0,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x1d,0xe,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x1d,0xe,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x1d,0xe,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x1d,0xe,0x0,
+0x0,0x0,0x0,0x0,0xfd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x20,0xe,0x0,
+0x0,0x0,0x0,0x0,0xc,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x27,0xe,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x27,0xe,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x27,0xe,0x0,
+0x0,0x0,0x0,0x0,0x70,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x28,0xe,0x0,
+0x0,0x0,0x0,0x0,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x29,0xe,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x29,0xe,0x0,
+0x0,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x2b,0xe,0x0,
+0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x2c,0xe,0x0,
+0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x2c,0xe,0x0,
+0x0,0x0,0x0,0x0,0x22,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x36,0xe,0x0,
+0x0,0x0,0x0,0x0,0x97,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x36,0xe,0x0,
+0x0,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x37,0xe,0x0,
+0x0,0x0,0x0,0x0,0x86,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x43,0xe,0x0,
+0x0,0x0,0x0,0x0,0x45,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x5b,0xe,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x5e,0xe,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x67,0xe,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x68,0xe,0x0,
+0x0,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x68,0xe,0x0,
+0x0,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x6a,0xe,0x0,
+0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x6a,0xe,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x6a,0xe,0x0,
+0x0,0x0,0x0,0x0,0x73,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x73,0xe,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x73,0xe,0x0,
+0x0,0x0,0x0,0x0,0xb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x75,0xe,0x0,
+0x0,0x0,0x0,0x0,0x73,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x80,0xe,0x0,
+0x0,0x0,0x0,0x0,0x20,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x82,0xe,0x0,
+0x0,0x0,0x0,0x0,0x64,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x8c,0xe,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x8c,0xe,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x8c,0xe,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x8f,0xe,0x0,
+0x0,0x0,0x0,0x0,0xe5,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0xb4,0xe,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xcc,0xe,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xcc,0xe,0x0,
+0x0,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0xce,0xe,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xce,0xe,0x0,
+0x0,0x0,0x0,0x0,0xe4,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xdf,0xe,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xdf,0xe,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0xdf,0xe,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xdf,0xe,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0xdf,0xe,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xe5,0xe,0x0,
+0x0,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0xe7,0xe,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xfe,0xe,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xfe,0xe,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xfe,0xe,0x0,
+0x0,0x0,0x0,0x0,0xe3,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xb,0xf,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x15,0xf,0x0,
+0x0,0x0,0x0,0x0,0x9f,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x24,0xf,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x25,0xf,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x25,0xf,0x0,
+0x0,0x0,0x0,0x0,0x45,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x30,0xf,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x30,0xf,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x31,0xf,0x0,
+0x0,0x0,0x0,0x0,0x71,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x5a,0xf,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x64,0xf,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x64,0xf,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x68,0xf,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x6e,0xf,0x0,
+0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x6e,0xf,0x0,
+0x0,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x6f,0xf,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x70,0xf,0x0,
+0x0,0x0,0x0,0x0,0x5c,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x7a,0xf,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x7a,0xf,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x7a,0xf,0x0,
+0x0,0x0,0x0,0x0,0xac,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0xbb,0xf,0x0,
+0x0,0x0,0x0,0x0,0xbf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xbc,0xf,0x0,
+0x0,0x0,0x0,0x0,0x68,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xbd,0xf,0x0,
+0x0,0x0,0x0,0x0,0x30,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xc6,0xf,0x0,
+0x0,0x0,0x0,0x0,0xf9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xc9,0xf,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xc9,0xf,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0xe1,0xf,0x0,
+0x0,0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0xe2,0xf,0x0,
+0x0,0x0,0x0,0x0,0x7f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0xe4,0xf,0x0,
+0x0,0x0,0x0,0x0,0x57,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x8,0x10,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x9,0x10,0x0,
+0x0,0x0,0x0,0x0,0xd7,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x16,0x10,0x0,
+0x0,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x17,0x10,0x0,
+0x0,0x0,0x0,0x0,0xaf,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x20,0x10,0x0,
+0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x20,0x10,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x20,0x10,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x22,0x10,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x22,0x10,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x3b,0x10,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x3b,0x10,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x3b,0x10,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x3f,0x10,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x6d,0x10,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x74,0x10,0x0,
+0x0,0x0,0x0,0x0,0x4d,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x7e,0x10,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x87,0x10,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x87,0x10,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x87,0x10,0x0,
+0x0,0x0,0x0,0x0,0xe6,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x93,0x10,0x0,
+0x0,0x0,0x0,0x0,0x10,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0xa1,0x10,0x0,
+0x0,0x0,0x0,0x0,0x65,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0xba,0x10,0x0,
+0x0,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xbd,0x10,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0xc7,0x10,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0xc7,0x10,0x0,
+0x0,0x0,0x0,0x0,0xb4,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xd5,0x10,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0xf0,0x10,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0xf0,0x10,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xe,0x11,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0xe,0x11,0x0,
+0x0,0x0,0x0,0x0,0xd7,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x17,0x11,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x17,0x11,0x0,
+0x0,0x0,0x0,0x0,0x17,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x22,0x11,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x26,0x11,0x0,
+0x0,0x0,0x0,0x0,0xb2,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x2e,0x11,0x0,
+0x0,0x0,0x0,0x0,0xa,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x32,0x11,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x3b,0x11,0x0,
+0x0,0x0,0x0,0x0,0xd0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x3d,0x11,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x3d,0x11,0x0,
+0x0,0x0,0x0,0x0,0xe0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x48,0x11,0x0,
+0x0,0x0,0x0,0x0,0xe6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x4a,0x11,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x52,0x11,0x0,
+0x0,0x0,0x0,0x0,0x99,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x57,0x11,0x0,
+0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x58,0x11,0x0,
+0x0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x58,0x11,0x0,
+0x0,0x0,0x0,0x0,0xa3,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x63,0x11,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x63,0x11,0x0,
+0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x64,0x11,0x0,
+0x0,0x0,0x0,0x0,0xb3,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x6e,0x11,0x0,
+0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x6f,0x11,0x0,
+0x0,0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x70,0x11,0x0,
+0x0,0x0,0x0,0x0,0xe,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x71,0x11,0x0,
+0x0,0x0,0x0,0x0,0x19,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x73,0x11,0x0,
+0x0,0x0,0x0,0x0,0xed,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x7a,0x11,0x0,
+0x0,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x7c,0x11,0x0,
+0x0,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x7e,0x11,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x84,0x11,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x84,0x11,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x84,0x11,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x84,0x11,0x0,
+0x0,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x87,0x11,0x0,
+0x0,0x0,0x0,0x0,0x5b,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x91,0x11,0x0,
+0x0,0x0,0x0,0x0,0x8,0x36,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xc7,0x11,0x0,
+0x0,0x0,0x0,0x0,0xd2,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0xd0,0x11,0x0,
+0x0,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0xd4,0x11,0x0,
+0x0,0x0,0x0,0x0,0x7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0xd7,0x11,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xd7,0x11,0x0,
+0x0,0x0,0x0,0x0,0x30,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0xeb,0x11,0x0,
+0x0,0x0,0x0,0x0,0xb8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xed,0x11,0x0,
+0x0,0x0,0x0,0x0,0xef,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xf1,0x11,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xf9,0x11,0x0,
+0x0,0x0,0x0,0x0,0xed,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0xfc,0x11,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x22,0x12,0x0,
+0x0,0x0,0x0,0x0,0x82,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x27,0x12,0x0,
+0x0,0x0,0x0,0x0,0x21,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x3c,0x12,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x3c,0x12,0x0,
+0x0,0x0,0x0,0x0,0xb1,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x71,0x12,0x0,
+0x0,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x73,0x12,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x73,0x12,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x74,0x12,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x7c,0x12,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x7d,0x12,0x0,
+0x0,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x80,0x12,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x80,0x12,0x0,
+0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x80,0x12,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x89,0x12,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x89,0x12,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x8b,0x12,0x0,
+0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x8b,0x12,0x0,
+0x0,0x0,0x0,0x0,0x94,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x98,0x12,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x99,0x12,0x0,
+0x0,0x0,0x0,0x0,0xd6,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0xa7,0x12,0x0,
+0x0,0x0,0x0,0x0,0x99,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0xca,0x12,0x0,
+0x0,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xcb,0x12,0x0,
+0x0,0x0,0x0,0x0,0xfa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xcc,0x12,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0xcc,0x12,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0xcd,0x12,0x0,
+0x0,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xce,0x12,0x0,
+0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xcf,0x12,0x0,
+0x0,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xd0,0x12,0x0,
+0x0,0x0,0x0,0x0,0x9d,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xd8,0x12,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0xda,0x12,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xda,0x12,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0xda,0x12,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0xda,0x12,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0xda,0x12,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0xdd,0x12,0x0,
+0x0,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0xdd,0x12,0x0,
+0x0,0x0,0x0,0x0,0x46,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xdf,0x12,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xe0,0x12,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0xe5,0x12,0x0,
+0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0xe6,0x12,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xe9,0x12,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xf3,0x12,0x0,
+0x0,0x0,0x0,0x0,0x6,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x13,0x0,
+0x0,0x0,0x0,0x0,0x19,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x1,0x13,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x3,0x13,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x21,0x13,0x0,
+0x0,0x0,0x0,0x0,0x43,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x28,0x13,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x29,0x13,0x0,
+0x0,0x0,0x0,0x0,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x2a,0x13,0x0,
+0x0,0x0,0x0,0x0,0xc9,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x35,0x13,0x0,
+0x0,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x37,0x13,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x38,0x13,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x38,0x13,0x0,
+0x0,0x0,0x0,0x0,0xa9,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x41,0x13,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x42,0x13,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x43,0x13,0x0,
+0x0,0x0,0x0,0x0,0x3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x44,0x13,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x44,0x13,0x0,
+0x0,0x0,0x0,0x0,0x74,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x4f,0x13,0x0,
+0x0,0x0,0x0,0x0,0x74,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x83,0x13,0x0,
+0x0,0x0,0x0,0x0,0xb8,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x92,0x13,0x0,
+0x0,0x0,0x0,0x0,0xe5,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x9f,0x13,0x0,
+0x0,0x0,0x0,0x0,0x8c,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0xa3,0x13,0x0,
+0x0,0x0,0x0,0x0,0x35,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0xbb,0x13,0x0,
+0x0,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xbe,0x13,0x0,
+0x0,0x0,0x0,0x0,0xd9,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xe3,0x13,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xe7,0x13,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0xe7,0x13,0x0,
+0x0,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xe7,0x13,0x0,
+0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xe8,0x13,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xf0,0x13,0x0,
+0x0,0x0,0x0,0x0,0xef,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x1,0x14,0x0,
+0x0,0x0,0x0,0x0,0x77,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x3,0x14,0x0,
+0x0,0x0,0x0,0x0,0x7d,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x11,0x14,0x0,
+0x0,0x0,0x0,0x0,0xae,0xee,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x0,0x15,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x0,0x15,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x2,0x15,0x0,
+0x0,0x0,0x0,0x0,0x97,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x1b,0x15,0x0,
+0x0,0x0,0x0,0x0,0x59,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x26,0x15,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x40,0x15,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x5a,0x15,0x0,
+0x0,0x0,0x0,0x0,0x64,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x69,0x15,0x0,
+0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x6a,0x15,0x0,
+0x0,0x0,0x0,0x0,0x94,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x6a,0x15,0x0,
+0x0,0x0,0x0,0x0,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x6b,0x15,0x0,
+0x0,0x0,0x0,0x0,0x9,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x83,0x15,0x0,
+0x0,0x0,0x0,0x0,0x7d,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x8f,0x15,0x0,
+0x0,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x90,0x15,0x0,
+0x0,0x0,0x0,0x0,0x13,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xb9,0x15,0x0,
+0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0xb9,0x15,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0xb9,0x15,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0xc3,0x15,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xc6,0x15,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xc6,0x15,0x0,
+0x0,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xc7,0x15,0x0,
+0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0xc8,0x15,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0xc8,0x15,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc9,0x15,0x0,
+0x0,0x0,0x0,0x0,0xe0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0xd1,0x15,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xd1,0x15,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xd2,0x15,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0xd2,0x15,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xd2,0x15,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xd2,0x15,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0xd2,0x15,0x0,
+0x0,0x0,0x0,0x0,0x66,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0xd3,0x15,0x0,
+0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xd4,0x15,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0xd4,0x15,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xd4,0x15,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xd5,0x15,0x0,
+0x0,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xd5,0x15,0x0,
+0x0,0x0,0x0,0x0,0xb8,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xdd,0x15,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0xdd,0x15,0x0,
+0x0,0x0,0x0,0x0,0xe6,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0xe0,0x15,0x0,
+0x0,0x0,0x0,0x0,0xa3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xe0,0x15,0x0,
+0x0,0x0,0x0,0x0,0x94,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0xe8,0x15,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xec,0x15,0x0,
+0x0,0x0,0x0,0x0,0x84,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x14,0x16,0x0,
+0x0,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x17,0x16,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x17,0x16,0x0,
+0x0,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x18,0x16,0x0,
+0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x18,0x16,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x1b,0x16,0x0,
+0x0,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x1e,0x16,0x0,
+0x0,0x0,0x0,0x0,0x6b,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x25,0x16,0x0,
+0x0,0x0,0x0,0x0,0xbb,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x29,0x16,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x2a,0x16,0x0,
+0x0,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x2c,0x16,0x0,
+0x0,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x2d,0x16,0x0,
+0x0,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x2e,0x16,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x2f,0x16,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x6c,0x16,0x0,
+0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x6c,0x16,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x6e,0x16,0x0,
+0x0,0x0,0x0,0x0,0x22,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x78,0x16,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x78,0x16,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x79,0x16,0x0,
+0x0,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x7a,0x16,0x0,
+0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x7b,0x16,0x0,
+0x0,0x0,0x0,0x0,0xa5,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x94,0x16,0x0,
+0x0,0x0,0x0,0x0,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x95,0x16,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x95,0x16,0x0,
+0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x96,0x16,0x0,
+0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x96,0x16,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0xa0,0x16,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xa1,0x16,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xa3,0x16,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xa3,0x16,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xa3,0x16,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xa6,0x16,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xa6,0x16,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xd5,0x16,0x0,
+0x0,0x0,0x0,0x0,0x58,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0xe3,0x16,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0xe3,0x16,0x0,
+0x0,0x0,0x0,0x0,0x5c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0xe3,0x16,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0xe5,0x16,0x0,
+0x0,0x0,0x0,0x0,0x39,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xe6,0x16,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xe6,0x16,0x0,
+0x0,0x0,0x0,0x0,0xf0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xed,0x16,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xee,0x16,0x0,
+0x0,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xef,0x16,0x0,
+0x0,0x0,0x0,0x0,0x58,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0xf0,0x16,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0xf9,0x16,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xf9,0x16,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xf9,0x16,0x0,
+0x0,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0xfd,0x16,0x0,
+0x0,0x0,0x0,0x0,0xd,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x1d,0x17,0x0,
+0x0,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x1d,0x17,0x0,
+0x0,0x0,0x0,0x0,0xfa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x1e,0x17,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x1f,0x17,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x1f,0x17,0x0,
+0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x1f,0x17,0x0,
+0x0,0x0,0x0,0x0,0x41,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x20,0x17,0x0,
+0x0,0x0,0x0,0x0,0xa,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x21,0x17,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x2b,0x17,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x2d,0x17,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x2e,0x17,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x37,0x17,0x0,
+0x0,0x0,0x0,0x0,0x14,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x44,0x17,0x0,
+0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x45,0x17,0x0,
+0x0,0x0,0x0,0x0,0xa3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x47,0x17,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x47,0x17,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x50,0x17,0x0,
+0x0,0x0,0x0,0x0,0x10,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x77,0x17,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x77,0x17,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x78,0x17,0x0,
+0x0,0x0,0x0,0x0,0x35,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x79,0x17,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x17,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x7b,0x17,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x7b,0x17,0x0,
+0x0,0x0,0x0,0x0,0xe0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x93,0x17,0x0,
+0x0,0x0,0x0,0x0,0x6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x94,0x17,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x94,0x17,0x0,
+0x0,0x0,0x0,0x0,0x85,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0xa9,0x17,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xac,0x17,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xae,0x17,0x0,
+0x0,0x0,0x0,0x0,0x4b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xb3,0x17,0x0,
+0x0,0x0,0x0,0x0,0x55,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0xb8,0x17,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0xd1,0x17,0x0,
+0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xd2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x53,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xdd,0x17,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0xdd,0x17,0x0,
+0x0,0x0,0x0,0x0,0x98,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0xe1,0x17,0x0,
+0x0,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0xe2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xe3,0x17,0x0,
+0x0,0x0,0x0,0x0,0x6,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0xe7,0x17,0x0,
+0x0,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0xe8,0x17,0x0,
+0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xe8,0x17,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0xe9,0x17,0x0,
+0x0,0x0,0x0,0x0,0x12,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xee,0x17,0x0,
+0x0,0x0,0x0,0x0,0x74,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xf3,0x17,0x0,
+0x0,0x0,0x0,0x0,0xd0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xff,0x17,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xff,0x17,0x0,
+0x0,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x18,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x0,0x18,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x0,0x18,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x1,0x18,0x0,
+0x0,0x0,0x0,0x0,0x62,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0xa,0x18,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0xa,0x18,0x0,
+0x0,0x0,0x0,0x0,0xd7,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x10,0x18,0x0,
+0x0,0x0,0x0,0x0,0x23,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x21,0x18,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x21,0x18,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x22,0x18,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x22,0x18,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x22,0x18,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x22,0x18,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x23,0x18,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x18,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x27,0x18,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x27,0x18,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x27,0x18,0x0,
+0x0,0x0,0x0,0x0,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x28,0x18,0x0,
+0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x28,0x18,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x2d,0x18,0x0,
+0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x2d,0x18,0x0,
+0x0,0x0,0x0,0x0,0x86,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x33,0x18,0x0,
+0x0,0x0,0x0,0x0,0x5c,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x38,0x18,0x0,
+0x0,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x39,0x18,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x3a,0x18,0x0,
+0x0,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x3a,0x18,0x0,
+0x0,0x0,0x0,0x0,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x3b,0x18,0x0,
+0x0,0x0,0x0,0x0,0xf5,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x4c,0x18,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x4e,0x18,0x0,
+0x0,0x0,0x0,0x0,0xd6,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x68,0x18,0x0,
+0x0,0x0,0x0,0x0,0x7e,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x74,0x18,0x0,
+0x0,0x0,0x0,0x0,0x96,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x7e,0x18,0x0,
+0x0,0x0,0x0,0x0,0x19,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x97,0x18,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xa0,0x18,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xa5,0x18,0x0,
+0x0,0x0,0x0,0x0,0xa5,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xb3,0x18,0x0,
+0x0,0x0,0x0,0x0,0x23,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xb5,0x18,0x0,
+0x0,0x0,0x0,0x0,0x98,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0xbf,0x18,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0xbf,0x18,0x0,
+0x0,0x0,0x0,0x0,0x37,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0xc0,0x18,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0xc8,0x18,0x0,
+0x0,0x0,0x0,0x0,0x69,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xcb,0x18,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xcb,0x18,0x0,
+0x0,0x0,0x0,0x0,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xd5,0x18,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0xd6,0x18,0x0,
+0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0xd6,0x18,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xd6,0x18,0x0,
+0x0,0x0,0x0,0x0,0x63,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xd8,0x18,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0xd8,0x18,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xd8,0x18,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0xd9,0x18,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0xd9,0x18,0x0,
+0x0,0x0,0x0,0x0,0x95,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0xe7,0x18,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x9,0x19,0x0,
+0x0,0x0,0x0,0x0,0x94,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x9,0x19,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0xa,0x19,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0xa,0x19,0x0,
+0x0,0x0,0x0,0x0,0x51,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x27,0x19,0x0,
+0x0,0x0,0x0,0x0,0x3,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x86,0x19,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0xa7,0x19,0x0,
+0x0,0x0,0x0,0x0,0x97,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xa8,0x19,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0xa8,0x19,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0xa8,0x19,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xa8,0x19,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0xb1,0x19,0x0,
+0x0,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0xb2,0x19,0x0,
+0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0xb3,0x19,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0xb4,0x19,0x0,
+0x0,0x0,0x0,0x0,0xbf,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xc4,0x19,0x0,
+0x0,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xc5,0x19,0x0,
+0x0,0x0,0x0,0x0,0x13,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0xe4,0x19,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xe4,0x19,0x0,
+0x0,0x0,0x0,0x0,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xe5,0x19,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0xe5,0x19,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xe5,0x19,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xe6,0x19,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0xe6,0x19,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xeb,0x19,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xed,0x19,0x0,
+0x0,0x0,0x0,0x0,0xe6,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xf2,0x19,0x0,
+0x0,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xf4,0x19,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xf4,0x19,0x0,
+0x0,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xf4,0x19,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0xf7,0x19,0x0,
+0x0,0x0,0x0,0x0,0x65,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x19,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xfb,0x19,0x0,
+0x0,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xfd,0x19,0x0,
+0x0,0x0,0x0,0x0,0x36,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x3,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x57,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x1b,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x1c,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x1c,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x24,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x24,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x24,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x26,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x2c,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xe,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x2e,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x22,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x35,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x37,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x38,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x24,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x3e,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x40,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x57,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x57,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x57,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x43,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x58,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x58,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x58,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x7,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x60,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x30,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x69,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x6,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x6a,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x6e,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x63,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x78,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xe6,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x89,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x17,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x90,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x90,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x90,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x93,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x9a,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x20,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x9b,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xa5,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0xa2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x85,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0xa3,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xa,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xa4,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x99,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xa5,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x50,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xa7,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xa7,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xd2,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0xb7,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x40,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0xb8,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xd8,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0xd0,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x68,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xd1,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0xd2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x31,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xd4,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x55,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xda,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0xdb,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xdb,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x67,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0xe4,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0xeb,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0xeb,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0xec,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xfe,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0xf4,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0xf4,0x1a,0x0,
+0x0,0x0,0x0,0x0,0xb,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x18,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xfd,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x22,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x52,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x2b,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x2b,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x2c,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x2e,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x4c,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x55,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x5a,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x5a,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x58,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x5c,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x5c,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xc0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x66,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xe,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x6f,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x70,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x70,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x23,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x76,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x79,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x79,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x79,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x79,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xa0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x96,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x22,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x98,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x98,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x9b,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x9b,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xad,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x9f,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x9f,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xb1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0xa5,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x55,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xa7,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xa8,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0xa9,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0xaa,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xaf,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xb3,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x98,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xbb,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xcc,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x7f,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xd6,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xf2,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0xdf,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xdf,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0xe2,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x22,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xeb,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0xf2,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0xf3,0x1b,0x0,
+0x0,0x0,0x0,0x0,0xde,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xfc,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x18,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x97,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x25,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x29,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x2a,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x2b,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x2b,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x2c,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x2c,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xe,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x2e,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x2e,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xef,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x2f,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x2f,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x2f,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x31,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x31,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x31,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x76,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x33,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x33,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xfd,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x37,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x39,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x39,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x67,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x3d,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x3f,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x3f,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xee,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x5a,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x63,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x62,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xed,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x69,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x83,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x75,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x76,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x5c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x76,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x79,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x74,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x7e,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x7e,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xbf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x7f,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x7f,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x50,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x8a,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x8b,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xb5,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x99,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x9a,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x9b,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x9b,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x9b,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x9b,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x2,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0xa0,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xa1,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xa1,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xa1,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xe1,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xe2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0xe3,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xec,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xec,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xec,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xed,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0xed,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x43,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0xee,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0xef,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xf0,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x45,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xf5,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xf8,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xfa,0x1c,0x0,
+0x0,0x0,0x0,0x0,0xa5,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x3,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x6,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x53,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x11,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xa,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x29,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x6f,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x34,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x49,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x49,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xf,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x62,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x85,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x85,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x85,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x56,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x8f,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x90,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x90,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x91,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x91,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x91,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x91,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x80,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x99,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x4,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x9b,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x9b,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x21,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x9f,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x5d,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xa9,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x9e,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xb4,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xb4,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0xb4,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x60,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0xb6,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xca,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xff,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xd6,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0xd6,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xd1,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0xe2,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xe2,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xe7,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xe8,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xe9,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xe9,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xd6,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xef,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0xef,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x5c,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xf7,0x1d,0x0,
+0x0,0x0,0x0,0x0,0xe7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0xf9,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x4a,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x4,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x4,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x5,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xc,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xe,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x10,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x10,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x11,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x19,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xef,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x1b,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x1c,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xe0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x25,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x2e,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x31,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x58,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x3b,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x42,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x48,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xae,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x57,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x5c,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x5c,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x1,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x64,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x65,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x71,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x72,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x6,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x78,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xcf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x7b,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x11,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x7d,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x37,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0xbb,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xd6,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0xc9,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xcc,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xcc,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x94,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0xd2,0x1e,0x0,
+0x0,0x0,0x0,0x0,0x13,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0xd8,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xd,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xe3,0x1e,0x0,
+0x0,0x0,0x0,0x0,0xdc,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x17,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x3,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x23,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x24,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x2c,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x96,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x2d,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x2e,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xce,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3b,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x55,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x41,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xd5,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x50,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x51,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x48,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x56,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x4c,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x60,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x61,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x82,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x67,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x6c,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x6c,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xab,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x72,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xae,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x74,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x74,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x74,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x74,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x74,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x75,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x75,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x75,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x17,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x76,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x96,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x7c,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x7d,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x7e,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x7e,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x89,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x82,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x65,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x87,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x88,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x88,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x6a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x8a,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x8d,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x8e,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x8e,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xa5,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0xa5,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xa6,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xa6,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xa6,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x68,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xac,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xac,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x57,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xaf,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x21,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xb0,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0xb1,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x8e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xb3,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xb3,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xb3,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x8f,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0xce,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x67,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xd0,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xd1,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x9f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0xd4,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xd4,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0xd5,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xd5,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xa9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xd7,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0xd7,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xd8,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xd8,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x43,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0xda,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0xda,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xda,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0xda,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xdb,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xde,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0xdf,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0xdf,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0xdf,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xe0,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x51,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0xe1,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0xe4,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xe5,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xe5,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0xe5,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x94,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xf1,0x1f,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xf7,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x9d,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x0,0x20,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x0,0x20,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x1,0x20,0x0,
+0x0,0x0,0x0,0x0,0x9d,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x7,0x20,0x0,
+0x0,0x0,0x0,0x0,0x6,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x20,0x20,0x0,
+0x0,0x0,0x0,0x0,0xa9,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x25,0x20,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x25,0x20,0x0,
+0x0,0x0,0x0,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x27,0x20,0x0,
+0x0,0x0,0x0,0x0,0x69,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x29,0x20,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x29,0x20,0x0,
+0x0,0x0,0x0,0x0,0x49,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x31,0x20,0x0,
+0x0,0x0,0x0,0x0,0x6f,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x36,0x20,0x0,
+0x0,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x37,0x20,0x0,
+0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x37,0x20,0x0,
+0x0,0x0,0x0,0x0,0x64,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x46,0x20,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x46,0x20,0x0,
+0x0,0x0,0x0,0x0,0x98,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x4c,0x20,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x4c,0x20,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x4d,0x20,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x4d,0x20,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x4d,0x20,0x0,
+0x0,0x0,0x0,0x0,0x90,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x51,0x20,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x51,0x20,0x0,
+0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x52,0x20,0x0,
+0x0,0x0,0x0,0x0,0x18,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x53,0x20,0x0,
+0x0,0x0,0x0,0x0,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x54,0x20,0x0,
+0x0,0x0,0x0,0x0,0xc,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x57,0x20,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x57,0x20,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x59,0x20,0x0,
+0x0,0x0,0x0,0x0,0xb9,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x66,0x20,0x0,
+0x0,0x0,0x0,0x0,0x23,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x67,0x20,0x0,
+0x0,0x0,0x0,0x0,0x8,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x6a,0x20,0x0,
+0x0,0x0,0x0,0x0,0x96,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x6f,0x20,0x0,
+0x0,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x70,0x20,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x70,0x20,0x0,
+0x0,0x0,0x0,0x0,0x17,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x75,0x20,0x0,
+0x0,0x0,0x0,0x0,0x33,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x80,0x20,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x80,0x20,0x0,
+0x0,0x0,0x0,0x0,0xe7,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x9a,0x20,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x9b,0x20,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x9b,0x20,0x0,
+0x0,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x9c,0x20,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x9c,0x20,0x0,
+0x0,0x0,0x0,0x0,0x67,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xa2,0x20,0x0,
+0x0,0x0,0x0,0x0,0xbe,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xa7,0x20,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xa8,0x20,0x0,
+0x0,0x0,0x0,0x0,0x60,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0xb9,0x20,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xb9,0x20,0x0,
+0x0,0x0,0x0,0x0,0xa8,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xbd,0x20,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0xbf,0x20,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0xc2,0x20,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0xc2,0x20,0x0,
+0x0,0x0,0x0,0x0,0x48,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xc3,0x20,0x0,
+0x0,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0xc4,0x20,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0xc4,0x20,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xc4,0x20,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xc5,0x20,0x0,
+0x0,0x0,0x0,0x0,0x14,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0xc9,0x20,0x0,
+0x0,0x0,0x0,0x0,0x41,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0xd0,0x20,0x0,
+0x0,0x0,0x0,0x0,0x94,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0xdb,0x20,0x0,
+0x0,0x0,0x0,0x0,0x44,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xe5,0x20,0x0,
+0x0,0x0,0x0,0x0,0x15,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0xec,0x20,0x0,
+0x0,0x0,0x0,0x0,0xec,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xf1,0x20,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xf1,0x20,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0xf2,0x20,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0xf2,0x20,0x0,
+0x0,0x0,0x0,0x0,0x8,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xf6,0x20,0x0,
+0x0,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xf7,0x20,0x0,
+0x0,0x0,0x0,0x0,0xc6,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x5,0x21,0x0,
+0x0,0x0,0x0,0x0,0x68,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0xa,0x21,0x0,
+0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xa,0x21,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xa,0x21,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xb,0x21,0x0,
+0x0,0x0,0x0,0x0,0xf,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x11,0x21,0x0,
+0x0,0x0,0x0,0x0,0x9a,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x14,0x21,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x14,0x21,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x16,0x21,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x17,0x21,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x17,0x21,0x0,
+0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x17,0x21,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x17,0x21,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x17,0x21,0x0,
+0x0,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x17,0x21,0x0,
+0x0,0x0,0x0,0x0,0x25,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x1c,0x21,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x1c,0x21,0x0,
+0x0,0x0,0x0,0x0,0xc7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x1e,0x21,0x0,
+0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x1e,0x21,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x1e,0x21,0x0,
+0x0,0x0,0x0,0x0,0xe3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x23,0x21,0x0,
+0x0,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x24,0x21,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x24,0x21,0x0,
+0x0,0x0,0x0,0x0,0xb2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x36,0x21,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x3c,0x21,0x0,
+0x0,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x3d,0x21,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x3e,0x21,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x41,0x21,0x0,
+0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x41,0x21,0x0,
+0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x41,0x21,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x42,0x21,0x0,
+0x0,0x0,0x0,0x0,0xe6,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x6d,0x21,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x6d,0x21,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x82,0x21,0x0,
+0x0,0x0,0x0,0x0,0xd7,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x8d,0x21,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x8d,0x21,0x0,
+0x0,0x0,0x0,0x0,0xdd,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x98,0x21,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x98,0x21,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xb9,0x21,0x0,
+0x0,0x0,0x0,0x0,0x6e,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xbe,0x21,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xbe,0x21,0x0,
+0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xbf,0x21,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xbf,0x21,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0xc3,0x21,0x0,
+0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0xc3,0x21,0x0,
+0x0,0x0,0x0,0x0,0xef,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xc6,0x21,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xc6,0x21,0x0,
+0x0,0x0,0x0,0x0,0xab,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0xc8,0x21,0x0,
+0x0,0x0,0x0,0x0,0x58,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xcb,0x21,0x0,
+0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x21,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0xcc,0x21,0x0,
+0x0,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xcd,0x21,0x0,
+0x0,0x0,0x0,0x0,0x7e,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xd1,0x21,0x0,
+0x0,0x0,0x0,0x0,0xed,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xd2,0x21,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0xd2,0x21,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xd2,0x21,0x0,
+0x0,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xd3,0x21,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xe5,0x21,0x0,
+0x0,0x0,0x0,0x0,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x21,0x0,
+0x0,0x0,0x0,0x0,0x81,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0xeb,0x21,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xef,0x21,0x0,
+0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0xef,0x21,0x0,
+0x0,0x0,0x0,0x0,0x90,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0xf6,0x21,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xf7,0x21,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf7,0x21,0x0,
+0x0,0x0,0x0,0x0,0x59,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xf8,0x21,0x0,
+0x0,0x0,0x0,0x0,0xd7,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0xfe,0x21,0x0,
+0x0,0x0,0x0,0x0,0x84,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x1,0x22,0x0,
+0x0,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x2,0x22,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x3,0x22,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x3,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x3,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7b,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x7,0x22,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x8,0x22,0x0,
+0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x8,0x22,0x0,
+0x0,0x0,0x0,0x0,0xe9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0xa,0x22,0x0,
+0x0,0x0,0x0,0x0,0x91,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xc,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0xd,0x22,0x0,
+0x0,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0xe,0x22,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0xe,0x22,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0xe,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0xe,0x22,0x0,
+0x0,0x0,0x0,0x0,0x14,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xf,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x10,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x12,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x12,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x12,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x12,0x22,0x0,
+0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x12,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x12,0x22,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x13,0x22,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x13,0x22,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x13,0x22,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x14,0x22,0x0,
+0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x14,0x22,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x14,0x22,0x0,
+0x0,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x14,0x22,0x0,
+0x0,0x0,0x0,0x0,0x86,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x19,0x22,0x0,
+0x0,0x0,0x0,0x0,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x1a,0x22,0x0,
+0x0,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x1a,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x1a,0x22,0x0,
+0x0,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x1b,0x22,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x1b,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2f,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x2c,0x22,0x0,
+0x0,0x0,0x0,0x0,0x85,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x2d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x79,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x32,0x22,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x32,0x22,0x0,
+0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x32,0x22,0x0,
+0x0,0x0,0x0,0x0,0x93,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x36,0x22,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x36,0x22,0x0,
+0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x37,0x22,0x0,
+0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x37,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x38,0x22,0x0,
+0x0,0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x39,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x39,0x22,0x0,
+0x0,0x0,0x0,0x0,0xbf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x3b,0x22,0x0,
+0x0,0x0,0x0,0x0,0x99,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x3d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x3d,0x22,0x0,
+0x0,0x0,0x0,0x0,0xb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x3e,0x22,0x0,
+0x0,0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x43,0x22,0x0,
+0x0,0x0,0x0,0x0,0x97,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x43,0x22,0x0,
+0x0,0x0,0x0,0x0,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x44,0x22,0x0,
+0x0,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x45,0x22,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x45,0x22,0x0,
+0x0,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x45,0x22,0x0,
+0x0,0x0,0x0,0x0,0xa3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x4a,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x4a,0x22,0x0,
+0x0,0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x4e,0x22,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x4e,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x4e,0x22,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x4e,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x4e,0x22,0x0,
+0x0,0x0,0x0,0x0,0xb0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x56,0x22,0x0,
+0x0,0x0,0x0,0x0,0x18,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x62,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x63,0x22,0x0,
+0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x63,0x22,0x0,
+0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x63,0x22,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x63,0x22,0x0,
+0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x64,0x22,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x64,0x22,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x64,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x64,0x22,0x0,
+0x0,0x0,0x0,0x0,0x15,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x68,0x22,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x68,0x22,0x0,
+0x0,0x0,0x0,0x0,0x32,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x6a,0x22,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x6a,0x22,0x0,
+0x0,0x0,0x0,0x0,0x26,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x6e,0x22,0x0,
+0x0,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x6f,0x22,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x6f,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x70,0x22,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x70,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x70,0x22,0x0,
+0x0,0x0,0x0,0x0,0x48,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x78,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x78,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x78,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x78,0x22,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x78,0x22,0x0,
+0x0,0x0,0x0,0x0,0xbc,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x7b,0x22,0x0,
+0x0,0x0,0x0,0x0,0x56,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x81,0x22,0x0,
+0x0,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x81,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x82,0x22,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x82,0x22,0x0,
+0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x82,0x22,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x83,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x83,0x22,0x0,
+0x0,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x84,0x22,0x0,
+0x0,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x86,0x22,0x0,
+0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x86,0x22,0x0,
+0x0,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x87,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x87,0x22,0x0,
+0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x88,0x22,0x0,
+0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x88,0x22,0x0,
+0x0,0x0,0x0,0x0,0xe2,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x8b,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x8b,0x22,0x0,
+0x0,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x8c,0x22,0x0,
+0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x8c,0x22,0x0,
+0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x8c,0x22,0x0,
+0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x8d,0x22,0x0,
+0x0,0x0,0x0,0x0,0xc7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x92,0x22,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x92,0x22,0x0,
+0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x92,0x22,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x93,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x94,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x94,0x22,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x94,0x22,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x94,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x94,0x22,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x98,0x22,0x0,
+0x0,0x0,0x0,0x0,0x31,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0xa2,0x22,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xa2,0x22,0x0,
+0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0xa2,0x22,0x0,
+0x0,0x0,0x0,0x0,0x87,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xaa,0x22,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0xaa,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0xab,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0xab,0x22,0x0,
+0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xab,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0xab,0x22,0x0,
+0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0xac,0x22,0x0,
+0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xac,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xac,0x22,0x0,
+0x0,0x0,0x0,0x0,0xe4,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0xb0,0x22,0x0,
+0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0xb1,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0xb1,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0xb1,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xb1,0x22,0x0,
+0x0,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xb1,0x22,0x0,
+0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xb1,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0xb1,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0xb2,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0xb2,0x22,0x0,
+0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0xb2,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xb5,0x22,0x0,
+0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0xb5,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xb6,0x22,0x0,
+0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0xb6,0x22,0x0,
+0x0,0x0,0x0,0x0,0x4e,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xb7,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xb7,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xb8,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0xb8,0x22,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0xb8,0x22,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0xb8,0x22,0x0,
+0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xb8,0x22,0x0,
+0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xb8,0x22,0x0,
+0x0,0x0,0x0,0x0,0x44,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x71,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x27,0x9,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x1,0x34,0x0,0x0,
+0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x18,0x5,0x41,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x75,0x32,0x41,0x2,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x75,0x32,0x41,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0x72,0x62,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x5,0x7d,0x2,0x0,0x0,0x1,0x3,
+0x3,0x2,0x0,0x20,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x54,0x3d,0x2d,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x3d,
+0x2d,0x0,0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x71,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x80,0x1a,0x6,0x0,0x0,0x35,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x63,0x72,0x69,0x74,0x69,0x63,0x61,0x6c,0x0,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,
+0x27,0x9,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1,0x1,0x59,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x5,0x41,0x1,0x0,0x0,0x0,0x2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x32,0x41,0x2,0x0,0x0,0x0,0x2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x32,0x41,0x6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x61,0x67,0x67,0x72,0x65,0x73,0x73,0x69,0x76,
+0x65,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x6c,0x7a,0x34,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x72,0x62,0x64,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1b,0x5,0x1,0x1,0x0,0x0,0x1,0x3,0x0,0x2,0x0,0x4,0x0,
+0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x32,0x2a,
+0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x35,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x20,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x1a,
+0x6,0x0,0x0,0x35,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc0,0x27,0x9,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x74,0x65,0x73,0x74,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x76,0x6f,0x6c,0x75,0x6d,0x65,
+0x73,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x69,0x6d,0x61,
+0x67,0x65,0x73,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x63,
+0x69,0x6e,0x64,0x65,0x72,0x2d,0x63,0x72,0x69,0x74,0x69,0x63,0x61,0x6c,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x74,0x65,0x73,0x74,0x4e,0x0,
+0x0,0x0,0x0,0x80,0x58,0x0,0xc4,0x5,0x0,0x0,0xc4,0x5,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x5,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x5,0x0,
+0x0,0x0,0x5,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x81,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0xc4,0x5,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x27,0xad,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x33,0xb3,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x2f,0x99,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x66,0xe6,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0xb0,0x4d,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x33,0xf3,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x89,0xa7,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0xb8,0xab,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x4,0xae,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0xd5,0x9a,0x0,0x0,0xcd,0xcc,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x66,0xe6,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0xb9,0xad,0x0,0x0,0x99,0xd9,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x34,0xf3,0x0,0x0,0x0,0x0,
+0x1,0x0,0x99,0xd9,0x0,0x0,0x0,0x0,0x1,0x0,0x66,0xe6,0x0,0x0,0x0,0x0,
+0x1,0x0,0x32,0x96,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x64,0xa0,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0xff,0xbf,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0xc0,0x0,0x0,0xaf,0x98,0x0,0x0,0x0,0x0,0x1,0x0,0x33,0xf3,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x33,0xf3,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0xc2,0x9a,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x37,0x74,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x66,0xe6,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x33,0xf3,0x0,0x0,0x0,0x0,0x1,0x0,0x66,0xe6,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x33,0xf3,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x33,0xf3,0x0,0x0,0xc3,0x91,0x0,0x0,0x33,0xf3,0x0,0x0,0x29,0xb5,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0xff,0xbf,
+0x0,0x0,0x0,0x0,0x1,0x0,0x99,0xd9,0x0,0x0,0x1,0x90,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x47,0xaa,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0xc1,0xa5,0x0,0x0,0x66,0xe6,
+0x0,0x0,0x0,0x0,0x1,0x0,0x33,0xf3,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x33,0xf3,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0xbb,0xb0,0x0,0x0,0x0,0x0,
+0x1,0x0,0x33,0xf3,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x34,0xf3,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x0,0xc4,0x5,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb8,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x61,0x99,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,
+0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x1e,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x48,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x37,0xb1,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xe3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5d,0x99,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,
+0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd,0xe7,0x13,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x1a,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,
+0x52,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xdf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x7,0x54,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x48,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe2,0x1e,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x99,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xbc,
+0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x66,0x28,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,
+0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x91,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x88,0x99,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xb6,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7a,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x50,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x1e,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc0,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x8b,0x8,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0xc4,0x1f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,
+0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xdb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x59,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,
+0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x50,0x28,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbc,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0x15,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xb3,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x6b,
+0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xe6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xdf,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,
+0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf6,0x6b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x49,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x31,0x3a,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x97,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x8c,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdb,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0x49,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xf9,0x2f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,
+0x31,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,
+0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa3,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe3,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,0x49,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0x8b,0x8,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xbd,
+0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xdb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x56,0xa4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,
+0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe2,0x6b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x15,0x30,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb5,0xb3,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb1,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf7,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,
+0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x74,0xa4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdd,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x49,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb0,0x69,0x8,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,
+0xbd,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xdb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x99,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,
+0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xad,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdf,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0x8b,0x8,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0xb3,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xce,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x93,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9a,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,
+0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa,0x4b,0x3a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0x98,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb3,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf5,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,
+0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc4,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x6b,0x33,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0x8b,0x8,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf1,
+0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,
+0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3f,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xec,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x99,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xc8,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0xa4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa1,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4f,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0x4a,0x3a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x8c,0x8,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa2,0x1d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x27,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x3e,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc7,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0xc4,0x1f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xb3,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,
+0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xe6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x51,0xbd,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,
+0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xdb,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb9,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x49,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0xc8,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x15,
+0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xd9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6b,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,
+0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9d,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,
+0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xae,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,0x99,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x48,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x81,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9f,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,
+0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdb,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xb3,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x88,0xb3,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,
+0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xce,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xbc,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,
+0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa5,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa3,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0x51,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0x86,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x86,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xcf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb0,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,
+0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa7,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,
+0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x51,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x52,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa2,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xba,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,
+0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcb,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0x8b,0x8,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x86,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,
+0xa4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd,0x6c,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,
+0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x48,0xbd,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbe,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x86,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0xc7,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x1d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5e,0x31,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,
+0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe1,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,
+0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x86,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0x86,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb2,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb8,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,
+0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xba,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xc4,0x1f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0x51,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,
+0x52,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xcf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa9,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,
+0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xaa,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xce,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x86,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0xb1,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0x7e,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x16,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,
+0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3c,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x95,0xc8,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd4,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb5,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x7e,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xc7,0x12,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,
+0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3b,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,
+0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3d,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x94,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0x15,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x61,0x59,
+0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x60,0xa4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,
+0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0x6c,0x33,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xca,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0xbd,0x2b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xc8,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd9,0xf7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x1d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf4,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,
+0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd8,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x4e,0x17,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,
+0xfa,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xd9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc6,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,
+0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb,0x6c,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa0,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xd5,0x2b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x4b,
+0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xe0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x92,0x28,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,
+0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x53,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,
+0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x29,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x0,0x8c,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xd3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb1,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc7,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xbd,0x2b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x8d,0x33,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,
+0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe8,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,
+0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x42,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd8,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x8b,0x8,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0xa4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3d,0xbd,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,
+0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x11,0x6c,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0x49,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x85,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x16,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xde,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,
+0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xec,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0x78,0x26,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0xe8,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,
+0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x27,0x31,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4f,0x72,0xa,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xd5,0x2b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0xc4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe5,0x6b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe5,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,
+0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0xc7,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x2,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x20,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xd5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfa,0x1f,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0x4d,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc0,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0x6,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,0x59,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,
+0x2d,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xd6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x31,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,
+0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x40,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc7,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xb6,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xd5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd8,0x9b,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,
+0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x86,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x6,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0x3e,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd7,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x1d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x60,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0x2d,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x98,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x6c,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0x78,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfb,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,
+0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3e,0x2,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9c,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x6,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x15,
+0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xd9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xef,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xff,0x3e,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x8b,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0x1e,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xdf,0xb2,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xd6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x62,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9c,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xc4,0x1f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xb6,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,
+0x4e,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x18,0x8e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa0,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9f,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x62,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xb7,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa2,0x16,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x97,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x65,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x59,0xc,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0x3f,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x34,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x19,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd9,0x1e,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xb4,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc9,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb8,0x68,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x48,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,
+0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa2,0x19,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf3,0x14,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd6,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd2,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x62,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x34,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5b,0x59,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,
+0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5b,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,
+0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0x2,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0x6,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf7,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa2,0x1d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x25,0x3f,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x2d,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb4,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0x78,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0xb4,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,
+0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xd8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x49,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,
+0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xeb,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd4,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x78,0x26,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x68,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x99,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,
+0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1b,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,
+0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x7e,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0x68,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3f,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x17,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x83,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcc,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0xb1,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x31,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xed,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcc,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0x78,0x26,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xc8,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xcd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb1,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xba,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0xc8,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0x73,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x37,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,
+0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xef,0x14,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb2,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xd3,0x37,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0xb6,0x1b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,
+0x62,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xe2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc5,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x33,0xb4,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa0,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0x59,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xb1,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x7e,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa2,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x39,0x62,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9d,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x98,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0xb4,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0x59,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x72,0x62,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf8,0x14,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbc,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbb,0xc8,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0x59,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,
+0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xe2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x42,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,
+0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x89,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xac,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x51,0x1,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0xc8,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xcd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc4,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x45,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,
+0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0x68,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0xe9,0x3,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa6,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xd0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x35,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa8,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0x51,0x1,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,
+0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xd4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x80,0x1a,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,
+0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x53,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd6,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0x7e,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,
+0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,
+0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdc,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7b,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x65,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa2,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x12,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0x12,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,
+0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x70,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd0,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xac,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0x7c,0x22,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x90,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x7e,0x22,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x80,
+0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x18,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa1,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x7,0x6c,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8,0x84,0x22,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9c,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xb6,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x86,0x22,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x22,0x31,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9e,0x28,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc6,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf0,0x8c,0x22,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x90,0x22,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,
+0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xd9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfb,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,
+0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9d,0x92,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xac,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0xda,0x24,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb9,0x95,0x22,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0x39,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xde,0x99,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x18,
+0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe8,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,
+0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0xa4,0x22,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0x48,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xba,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x86,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xa9,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbc,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0xac,0x22,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,0x6c,0x33,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,
+0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xe7,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfc,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc3,0xaf,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0xb4,0x22,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc8,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0xc0,0x22,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xa8,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xd0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xec,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9b,0x52,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,
+0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x27,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0xf0,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0xc3,0x22,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xbb,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xd3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xbf,0xc6,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,
+0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcd,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xc4,0x1f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0xcb,0x22,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,
+0xce,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x18,0x8d,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x49,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,
+0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x92,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbb,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0x85,0x33,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0x51,
+0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x18,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x70,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,
+0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5e,0x2d,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,
+0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0x53,0xe,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x98,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x4a,0x3a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x56,0xe,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xaf,0x59,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x18,0x8e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa2,0x28,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x5e,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa4,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0x61,0xe,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb9,0x9b,0x3,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,
+0x3f,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xe7,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xae,0x63,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,
+0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5c,0x66,0x1a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0x66,0xe,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb0,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x15,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x69,
+0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x18,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe3,0x6f,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x18,
+0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4f,0x72,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,
+0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x74,0xe,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x77,0xe,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x57,0x7a,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x18,0x8e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x61,0x2,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,
+0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0x6c,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcf,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x7f,0xe,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0x82,0xe,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,
+0x84,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x18,0x8e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x64,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,
+0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4,0x6c,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe4,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xe7,0x13,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xd9,0xe,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x39,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4c,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,
+0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6b,0x8a,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,
+0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x29,0x28,0x16,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x90,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0x2a,0x16,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xc4,0x1f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6a,0x31,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x22,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa2,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x2d,0x16,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0x2d,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,
+0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xe7,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x2f,0x30,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,
+0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbc,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd5,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x8d,0x17,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0xf0,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x10,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x19,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x93,0x32,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x18,
+0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x24,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0x69,0x8,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x13,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0x37,0x16,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x2e,0xbd,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x54,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,
+0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0xdb,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x90,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0xbc,0x3,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x8d,0x33,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,
+0x16,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x56,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,
+0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x72,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb7,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xa,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x90,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x3a,0x16,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xc4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3,0x4b,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,
+0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xbf,0xde,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,
+0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xae,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x18,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x48,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa4,0xc,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x18,0x88,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x94,0x3c,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,
+0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x90,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xf0,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x61,0x59,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,
+0xe1,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x18,0x8f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd3,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,
+0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5,0xf,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x98,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x1b,0x24,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa0,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xe3,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x3f,
+0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x89,0x8a,0x18,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x71,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,
+0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x15,0x6c,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x11,0x6,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9c,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0xb6,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9a,0x23,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xaa,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xd0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc5,0xe6,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,
+0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x9b,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x42,0x16,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,
+0x26,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x58,0x31,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xf0,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0xeb,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x18,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,
+0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb2,0x14,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0x8b,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xf0,0x7,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xba,0x47,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x18,0x8b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x69,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xff,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb8,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x48,0x3b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x29,0x24,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,
+0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xe6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc9,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,
+0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb2,0xed,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa8,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x28,0x19,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa4,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x31,0x3a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xd9,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xdf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5c,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,
+0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,
+0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xfa,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x2,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0x8b,0x8,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7a,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x10,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf3,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,
+0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe7,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0xd5,0x2b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x6,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,
+0x4a,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,0x18,0x8b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb7,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,
+0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf8,0xae,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0x31,0x3a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x28,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xbc,
+0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa0,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,
+0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x50,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x66,0x1a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xac,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0x39,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x59,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x2f,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x51,0x59,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,
+0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe7,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xb4,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x62,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,
+0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xcc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe6,0x14,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x96,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc1,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x2b,0x24,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb0,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x59,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x79,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xdb,0x14,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,
+0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5,0xf1,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,
+0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x6,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x56,0xb4,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x59,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x48,0x62,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xbf,0x14,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc0,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x59,0xc,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0x79,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,
+0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xd8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x66,0xb4,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,
+0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfd,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd9,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x1c,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa8,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x4c,0x16,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0x73,
+0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4a,0xb2,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x19,
+0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf4,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,
+0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x15,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xb4,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x59,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x71,0x2e,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x19,0xc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xae,0x8,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,
+0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe0,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x34,0x3b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xc8,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,
+0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xcc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xff,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,
+0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd3,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd6,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x73,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xf3,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x89,0x8a,0x18,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5d,0x1f,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x18,
+0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,
+0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xb4,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x4f,0x16,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x57,0x59,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x27,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaa,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0xb4,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xc8,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,
+0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xcc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf9,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,
+0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xcb,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xae,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xb4,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x73,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0x62,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xba,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,
+0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa3,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,
+0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0x59,0xc,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xac,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x79,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x34,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x60,0xb4,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xcd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe1,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,
+0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x62,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0x36,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x78,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x77,0x27,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,
+0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9b,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xda,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x51,0x1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0xf5,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0xb,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x19,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xce,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x22,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,
+0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x68,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x52,0x16,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4,0xb7,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x19,0x9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa8,0x39,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,
+0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa0,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xfb,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x11,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,
+0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x10,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf6,0xb7,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,
+0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x59,0x25,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb4,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xba,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa0,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x57,0x16,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x3c,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x19,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xfc,0x13,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x14,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,
+0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0xfd,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbc,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x2a,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xc1,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc6,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3b,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,
+0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x3e,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc4,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x17,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,
+0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x14,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe8,0x0,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,
+0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6f,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9e,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0x2d,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbc,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0x50,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xdc,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4d,0x59,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,
+0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x7e,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xc4,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xbf,0x41,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x19,0xc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x19,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd8,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,
+0x3,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,0x18,0x8f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4e,0x30,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,
+0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x35,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd4,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x5b,0x16,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xc6,
+0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x19,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4f,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa2,
+0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0xa0,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x90,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x78,0x26,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x4a,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x10,0x1c,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x19,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x52,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,
+0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc4,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0x5,0x8,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x51,0x1,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe9,
+0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x18,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x64,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,
+0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfc,0x32,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0xc9,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb0,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0x6c,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x64,0x5d,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,0x18,
+0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xab,0x7f,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xa3,0x31,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x77,0xcd,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x19,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc8,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,
+0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xb,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcc,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x4c,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0x7e,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x22,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,
+0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x93,0x36,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc8,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xcb,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb4,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xe8,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0xc8,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x16,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,
+0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd8,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,
+0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0xa5,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x98,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x7e,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0x68,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x69,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x16,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3b,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x61,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x51,0x1,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,
+0xcf,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x19,0xf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xbe,0x4f,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,
+0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xcc,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa8,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xd4,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb8,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x7e,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0x68,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd0,0xa8,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x19,
+0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xfc,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xe,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xe8,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x41,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x19,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc6,0x3b,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,
+0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xec,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x7e,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x68,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,
+0xd2,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x19,0xf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x8b,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,
+0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfe,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xba,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x51,0x1,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x52,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,0x19,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe5,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xde,0xd7,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,
+0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xab,0x31,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0x11,0x8,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x80,0xd5,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x19,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,
+0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe8,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x3f,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,
+0x4d,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf8,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,
+0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x60,0xb1,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa4,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,0x2d,0xe,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x44,0xda,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x19,
+0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa5,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,
+0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x44,0x17,0x8,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd8,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xee,0x54,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0x78,0x26,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd2,0xd7,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x19,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,
+0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0xb3,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa8,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x44,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x41,0x6,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,
+0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa2,0x18,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa4,0xdc,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,
+0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xcc,0xb6,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xac,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0x78,
+0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x14,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x62,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,
+0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6a,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,
+0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xf0,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0xf0,0x7,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4d,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa2,0x10,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x68,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,
+0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xf0,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xf0,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,
+0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa2,0x10,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x35,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,
+0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3b,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9c,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xe2,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,0xe2,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3d,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,
+0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x37,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,
+0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xf7,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x90,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0xe2,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xe2,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x28,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,0x13,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x43,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,
+0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb1,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xe2,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xe2,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,
+0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x2c,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,
+0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x30,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd8,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0xe2,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xe2,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x11,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa2,
+0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x32,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,
+0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xe1,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xe8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb1,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc6,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,
+0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa0,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0xe8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xe8,0x34,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,
+0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa2,0x11,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa3,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,
+0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc9,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd4,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xe8,0x34,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0xe8,
+0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x94,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,
+0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xbc,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,
+0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0xdf,0x3f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0x19,0x8,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd6,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd0,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,
+0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x94,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0xe8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcc,0xe8,0x34,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,
+0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa2,0x11,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb8,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,
+0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc4,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xe8,0x34,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1f,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,
+0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,
+0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xec,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x3b,0x9,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x3b,0x9,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x2a,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x1a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xff,0x3a,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,
+0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9f,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x3b,0x9,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0x3b,0x9,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,
+0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5a,0x44,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,
+0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf0,0xdf,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc8,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0xb9,0x31,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb0,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x3b,0x9,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xe1,
+0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x19,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4e,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,
+0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xbf,0xe8,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,
+0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x47,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x3b,0x9,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xfa,0x3a,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x1a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1a,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,
+0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x94,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x3b,0x9,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x3b,0x9,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,
+0xbc,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x19,0xe,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4c,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,
+0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf4,0xa,0x1c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd6,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xe4,0x3f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xac,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xeb,0x6,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc9,0xc1,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x19,
+0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x79,0xe7,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,
+0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0xed,0x6,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd4,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0xc4,0x31,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xe9,0x3f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xba,0x57,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x18,0x89,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,
+0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc0,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x80,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x29,0x2e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,
+0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xe4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x86,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,
+0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x31,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xef,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd8,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xc7,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,0x5b,
+0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x18,0x89,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe8,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,
+0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x14,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x92,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x81,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0x11,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x40,0xf3,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x19,0x9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x19,0xca,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,
+0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb0,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0x80,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x81,0x2d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,
+0x5d,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x18,0x89,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9b,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,
+0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9,0x60,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9c,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa0,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xcc,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7a,0x11,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x18,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x81,0xd3,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x19,
+0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6b,0x63,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x93,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xd5,0x31,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x67,0x17,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xbc,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x18,0x8a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x67,0x6b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,
+0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xd8,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd4,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x6d,0x17,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,
+0xe6,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x19,0x8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xaf,0xdb,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,
+0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5d,0xe9,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbc,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x70,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0xde,
+0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x19,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x81,0x39,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa0,0xec,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,
+0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x72,0x17,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x81,0x2d,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x24,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x1b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,
+0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb8,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x81,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0x81,0x2d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,
+0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa2,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3c,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,
+0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfb,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xac,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9f,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x81,0x2d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x3b,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x19,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5,0xef,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,0x19,
+0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x72,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,
+0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x78,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb8,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0xef,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xef,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x83,0x3e,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x19,0xb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc,0xfa,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa8,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xef,0x2,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xf1,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,
+0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,0x12,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd3,0x7a,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,
+0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf2,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb0,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xee,0x2,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xee,0x2,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x11,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x18,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xff,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,
+0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x71,0xfc,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0x41,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9c,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xf9,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xee,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x50,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x12,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf2,0x43,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,
+0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd1,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd0,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xef,0x2,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xef,0x2,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,
+0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x12,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x8c,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xfe,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9c,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xee,0x2,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xee,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x42,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,
+0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe0,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,
+0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x25,0x9,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x90,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0xee,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4,0xef,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xfd,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x12,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x38,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,
+0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x7e,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc0,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb9,
+0xb,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x25,0x80,0x8e,0x17,0x6b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6f,0x84,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x7,0x80,0x8e,
+0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xcb,0x60,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x80,0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x29,0xfc,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd0,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x1,0x15,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x85,
+0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x10,0x80,0x8e,0x17,0x48,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x11,0x86,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x14,0x80,0x8e,0x17,
+0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6e,0x4c,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,
+0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x96,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,0x80,0x17,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0xff,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd6,0x4e,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x19,0xb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x22,0x9,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x83,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc8,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x1,0xd,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,
+0xb,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x19,0xd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6e,0x51,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,
+0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd0,0x88,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcc,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x94,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x2,0x2a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x4,
+0xd,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x19,0x8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x18,0xe,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x19,
+0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x7d,0x54,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,
+0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x5,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xdd,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xfc,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf0,0xce,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5a,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x18,0x8a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb7,0xd8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,
+0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x8b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd0,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0xd3,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x9e,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,
+0xd2,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0x17,0x49,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x14,0xd7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,
+0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3b,0xe6,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x96,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xdc,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdd,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0xd9,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xc0,
+0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x19,0x8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x74,0x10,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x89,0x8a,0x19,
+0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x63,0xdb,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,
+0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0x7,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd4,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xde,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1b,0x28,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xd1,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x41,0xd6,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0x17,0x49,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb,0xa2,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,
+0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x56,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb4,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0x8e,0x17,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,
+0xc3,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x19,0x8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x60,0xa,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,
+0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x69,0xa4,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x98,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x12,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb4,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x11,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0x5f,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x19,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x17,0x91,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,0x18,
+0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd2,0xa6,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,
+0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xc6,0xc,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x98,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0xd,0x2a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x1c,0x15,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4f,0x62,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,0x19,0xb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd9,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x89,
+0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x93,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xc8,0xc,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xa9,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,
+0xab,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x89,0x8a,0x19,0xa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x67,0x1f,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,
+0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf2,0x64,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc0,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xae,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa4,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0xcb,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xae,
+0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,0x19,0xa,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x81,0xa5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,
+0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6,0x68,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,
+0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0xb0,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa8,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0xd2,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xa,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0x17,0x44,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x2,0xb1,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x19,0xa,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x7,0x22,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x6a,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc8,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xb4,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0xd5,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,
+0xd5,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0x17,0x49,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe4,0xd7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,
+0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc0,0xcf,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9b,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xd4,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb6,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xdf,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1b,0x2c,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xd0,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0x17,0x49,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3b,0x2,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xdf,0x3e,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,
+0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0xb3,0x29,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9c,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x47,0x29,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1b,0x1d,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x39,0x29,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6e,0x41,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x5,0x80,0x8e,0x17,0x74,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x38,0x49,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x21,0x80,
+0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb5,0x3f,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd4,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x4b,0x29,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1b,0x29,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x43,0x29,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0xd,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,
+0x3e,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0x17,0x74,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc,0x26,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,
+0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6b,0x3c,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xad,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0x44,0x29,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1b,0x15,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0x40,0x29,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1b,0x1,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0x73,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x19,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa9,0xb6,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x89,0x8a,0x18,
+0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe2,0x46,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x19,
+0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0x39,0x29,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x95,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x42,0x29,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1b,0x9,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0x3a,0x29,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3b,0x3d,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0x17,0x74,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x18,0x44,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x11,0x80,
+0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x3b,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa5,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0x3e,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0x3f,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,
+0x41,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x28,0x80,0x8e,0x17,0x6f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,
+0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6f,0x3d,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd8,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x3a,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcc,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xd8,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x33,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0x17,0x6f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb5,0x35,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0x17,
+0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb4,0xb6,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x3c,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd4,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0x28,0x15,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x75,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x34,0x38,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0x17,0x6f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x75,0x32,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,
+0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x83,0x3b,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd0,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x0,0x2,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x36,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,
+0xb9,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x18,0x8c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb3,0xfe,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,
+0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa2,0xda,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb0,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0xfd,0x1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa3,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x37,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x34,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0x17,0x6f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb5,0xa,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x1c,0x80,0x8e,0x17,
+0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x13,0xfd,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,
+0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x3,0x2,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc7,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xbe,0x29,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa4,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0xff,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd0,0x2,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0x17,0x6b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x15,0x34,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,
+0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x30,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcc,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x30,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x12,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,
+0x40,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x24,0x80,0x8e,0x17,0x6f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x77,0x4,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,
+0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa5,0x31,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x94,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0x1,0x2,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xba,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0xfb,0x1,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0x7,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0x17,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd7,0x9,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x13,0x80,0x8e,0x17,
+0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x24,0x1,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,
+0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x78,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd4,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0x8,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x6,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x43,0xfc,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0x17,0x6b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x76,0xbe,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,
+0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x8,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xc0,0x29,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa8,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xcb,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,
+0xcd,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0x17,0x64,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x11,0xcc,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,
+0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x63,0xca,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xdd,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb4,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xf7,0x39,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xce,
+0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0x17,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf1,0xd2,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x24,0x80,0x8e,0x17,
+0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4d,0xc6,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,
+0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0xc3,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x98,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0xc9,0x30,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe2,0xcc,0x30,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6a,0x7b,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x89,0x8a,0x19,0xb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc2,0x33,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0xc7,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0xd1,0x30,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1b,0x20,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xc8,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,
+0xc4,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0x17,0x64,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xde,0xc0,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,
+0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3d,0xc2,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xcf,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe1,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0xc5,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0xd0,
+0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x11,0x80,0x8e,0x17,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd,0xc3,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0x17,
+0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x98,0x2,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,
+0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xc3,0x29,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xac,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x6,0x25,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xff,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7f,0xf8,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0x17,0x66,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5a,0x36,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf6,0x3,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcc,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xf5,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xfd,0x24,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,
+0x8,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0x17,0x66,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6a,0xf4,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,
+0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xae,0xfa,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xac,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xfc,0x24,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb4,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x1,0x25,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0xf6,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0x17,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x52,0x5,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0x17,
+0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf5,0x6,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,
+0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0x0,0x25,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xfc,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xf7,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x47,0xc3,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x18,0x8c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x96,0xf9,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,
+0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xc5,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb0,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xf6,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x18,0x6,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,
+0x22,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x1,0x80,0x8e,0x17,0x65,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x42,0x15,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,
+0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe,0x7e,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdc,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0x12,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe2,0x16,0x6,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb1,0x1c,
+0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0x17,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4c,0x90,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0x17,
+0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x72,0x14,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,
+0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0xd4,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb0,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xd3,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0xcf,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x64,0xd1,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0x17,0x65,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb3,0xdd,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,
+0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x56,0x39,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd8,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xc6,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc4,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xcc,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,
+0xd5,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0x17,0x65,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdd,0xd6,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,
+0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x91,0xd8,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb8,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xdc,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb4,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xc8,0x29,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0xca,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0x17,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x27,0xda,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0x17,
+0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb9,0x3b,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0x89,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x98,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0x7c,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0xd1,0x29,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf3,0x85,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0x17,0x68,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x22,0x88,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,
+0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x21,0xf5,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1b,0x20,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x8c,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0xe8,0x2,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,
+0xf4,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf6,0x80,0x8e,0x17,0x68,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfd,0xc8,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,
+0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9c,0xe7,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa0,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0xec,0x2,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbd,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0xce,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0xe5,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x68,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd0,0xd3,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,0x19,
+0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x65,0xf2,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,
+0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xea,0x2,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb5,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xed,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xef,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3c,0xe9,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0x17,0x68,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6,0xf6,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x24,0x80,
+0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xeb,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb9,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,0xf1,0x2,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0xd0,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,
+0xd6,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x19,0xa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x51,0xd3,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,
+0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x71,0xd9,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xd6,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd8,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xdb,0x29,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0xd8,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x18,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd5,0x70,0x27,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0x17,
+0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x88,0x4e,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x28,
+0x80,0x8e,0x17,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x12,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0x12,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6c,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xcc,0xf9,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,
+0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0x12,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0x12,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,
+0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xe4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6d,0x53,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,
+0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9a,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc1,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x12,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x12,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x33,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,
+0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x86,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,
+0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0x35,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0xbc,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0xcf,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x60,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa8,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbe,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xbc,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0xe0,0x1b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,
+0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xda,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x8f,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc9,0xf0,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x91,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xbf,0x1b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf6,0xbc,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xe5,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xda,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x44,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,
+0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x18,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,
+0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xe0,0x1b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0xe5,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3d,0x9e,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x88,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,
+0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc8,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xb0,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0x75,0x1f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,
+0x9e,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa3,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,
+0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3d,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa9,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xe5,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xbc,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xed,0xf2,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,
+0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb7,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,
+0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xce,0x13,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xe5,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xbe,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x42,0x35,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,
+0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x19,0x7d,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd5,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xbd,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xe5,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,
+0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x8c,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,
+0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4b,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x98,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0xe5,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x7d,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa4,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,
+0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4f,0xbf,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,
+0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x9e,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x94,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xe5,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa5,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1a,0xf6,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xce,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xe5,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,
+0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xcb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x35,0x9e,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,
+0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x68,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd6,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x57,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,0xe0,
+0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xcb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2d,0x9e,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,
+0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb,0x7d,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x93,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xe0,0x1b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe0,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9,0x7d,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc1,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0xe5,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,
+0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xcb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf1,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,
+0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5b,0xf8,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe4,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xcf,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x57,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0xbd,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb9,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xfe,0x7c,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x57,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0xbc,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x91,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x58,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x50,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xce,0x13,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0x9e,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,
+0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xc9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x42,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb0,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcf,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xcf,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x57,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xbc,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x21,0xbf,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x89,0xfa,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xcf,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xbd,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9b,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfa,0x7c,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc1,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,0xbd,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xe0,0x1b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,
+0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xc8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x1f,0x35,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x69,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xec,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0xe0,0x1b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xb0,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x11,0xa2,0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,
+0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x45,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,
+0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xde,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0xfc,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x61,0xbf,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb4,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xc9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x53,0xcf,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,
+0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xbc,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0xe8,0x1b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,
+0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xc8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9b,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x58,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd0,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xce,0x13,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xce,
+0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,
+0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6b,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,
+0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x96,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xce,0x13,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xce,0x13,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x33,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xdd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x33,0xba,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,
+0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb0,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xce,0x13,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0xce,0x13,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x28,
+0xba,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xdd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x39,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,
+0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe,0xba,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb2,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0xba,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0xce,
+0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x40,0x98,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,
+0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x11,0xe7,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xde,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0xbc,0x3,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0x59,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe7,0x80,0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xde,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x7e,0x2d,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,
+0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd5,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0xb6,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,0xe7,0x13,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,
+0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x78,0x2d,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc9,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x4a,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,0x3f,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x36,0x98,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,
+0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x82,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0x9b,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x23,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x48,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xdf,0x57,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x1c,0x80,0x8e,0xa1,0xde,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5b,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x1e,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xb5,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x6,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xba,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x1,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbb,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0x50,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1b,0x0,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x48,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x1e,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x79,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,
+0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x59,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0x2,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xbc,0x3,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x55,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x14,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x24,0x98,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb5,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x59,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0xe7,0x13,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,
+0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4d,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x65,0x53,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,
+0xc,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x98,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x48,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x39,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7d,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,
+0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x31,0xe7,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x2,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0x5a,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x4d,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf4,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x14,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xd5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb6,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,
+0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xea,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x59,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x72,0x6,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,
+0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdc,0x9b,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe6,0x4b,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe6,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x1f,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0xb6,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6f,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,
+0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x8,0xe7,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x39,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0x4f,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfc,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x86,0x2d,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xd6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x3f,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd4,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x5,0x36,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xe5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xd5,0x2b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,
+0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xe6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa2,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,
+0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf9,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd6,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0x28,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x15,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x4,
+0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xe5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd5,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x47,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0x6b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0xa4,0x1f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x23,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0x2,
+0x2,0x0,0x0,0x5b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x16,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xb9,0x3,0x0,0x0,
+0x2,0x2,0x0,0x0,0x26,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x52,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x34,0x5,0x0,
+0x0,0xc8,0x0,0x0,0x0,0x2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xee,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xaf,0x2,
+0x0,0x0,0x40,0x3,0x0,0x0,0xfb,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfe,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x90,
+0x0,0x0,0x0,0x2,0x2,0x0,0x0,0xb9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x55,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x4,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x63,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x2,0x2,0x0,0x0,0xbf,0x2,0x0,0x0,0x29,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x16,0x1,0x0,0x0,0xfd,0x2,0x0,0x0,0x2,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x2,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xb1,0x2,0x0,0x0,0x60,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x94,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x3d,0x0,0x0,0x0,0x64,0x1,0x0,0x0,0x2,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x86,0x5,0x0,0x0,0x2,0x2,0x0,0x0,0x18,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x6c,0x5,0x0,0x0,0x7d,0x1,0x0,0x0,0x2,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x12,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0xea,0x2,0x0,0x0,0x34,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x6a,0x5,0x0,0x0,0xf2,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x4c,0x2,0x0,0x0,0x6c,0x5,0x0,0x0,0xb9,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x47,0x2,0x0,0x0,0xe1,0x4,0x0,0x0,0x2,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x15,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0xb6,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x94,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xa6,0x5,0x0,0x0,0x9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x85,0x2,
+0x0,0x0,0x27,0x2,0x0,0x0,0x2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x57,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x17,
+0x5,0x0,0x0,0x32,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2d,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xb1,0x5,0x0,0x0,
+0x32,0x1,0x0,0x0,0x2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa9,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x45,0x1,0x0,
+0x0,0x28,0x5,0x0,0x0,0x76,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8f,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x43,0x1,
+0x0,0x0,0x2,0x2,0x0,0x0,0x70,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x96,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x48,
+0x0,0x0,0x0,0xc7,0x1,0x0,0x0,0xa9,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x2,0x2,0x0,0x0,0xe,0x3,0x0,0x0,0xe8,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc4,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x5c,0x2,0x0,0x0,0x2,0x2,0x0,0x0,0x3d,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0xef,0x4,0x0,0x0,0xb3,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0xf9,0x4,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0x26,0x2,0x0,0x0,0xc3,0x0,0x0,0x0,0x70,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x1f,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0x2,0x2,0x0,0x0,0x9a,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x9e,0x5,0x0,0x0,0x71,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x9d,0x2,0x0,0x0,0xe9,0x4,0x0,0x0,0x1f,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x4b,0x5,0x0,0x0,0x25,0x0,0x0,0x0,
+0x16,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x76,0x1,0x0,0x0,0x55,0x1,0x0,
+0x0,0x2,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x5,0x0,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0xa9,0xf3,0x0,
+0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x5,0x0,0x0,0x0,0xc4,0x5,0x0,
+0x0,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x6,0x0,0x5,0x0,0x97,0xc7,0x78,
+0x19,0x2,0x0,0x0,0x0,0xfe,0xff,0xff,0xff,0xbf,0xff,0xff,0xff,0x1e,0xfb,0xeb,
+0x14,0x79,0xcc,0x8c,0x4,0x5,0x0,0x0,0x0,0xfe,0xff,0xff,0xff,0x4,0x0,0x5,
+0x0,0x1e,0xfb,0xeb,0x14,0x6,0x0,0x0,0x0,0xb8,0xff,0xff,0xff,0xfc,0xff,0xff,
+0xff,0xfa,0xff,0xff,0xff,0xf7,0xff,0xff,0xff,0xf2,0xff,0xff,0xff,0x6d,0xff,0xff,
+0xff,0x1b,0x47,0x95,0x3,0x84,0x47,0x95,0x3,0x48,0x41,0x95,0x3,0x30,0x3d,0x12,
+0x3,0x57,0xea,0x84,0x3,0xb0,0x3,0x95,0x3,0x5,0x0,0x0,0x0,0xfd,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0xe1,0x0,0x0,
+0x0,0xec,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x13,0x1,0x0,0x0,0x55,0x1,0x0,
+0x0,0x70,0x1,0x0,0x0,0x8f,0x1,0x0,0x0,0xb6,0x1,0x0,0x0,0xcc,0x1,0x0,
+0x0,0xf5,0x1,0x0,0x0,0x13,0x2,0x0,0x0,0x20,0x4,0x0,0x0,0x72,0x5,0x0,
+0x0,0x7a,0x5,0x0,0x0,0x83,0x5,0x0,0x0,0x8b,0x5,0x0,0x0,0x92,0x5,0x0,
+0x0,0x9a,0x5,0x0,0x0,0xa2,0x5,0x0,0x0,0xaa,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xfc,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0x84,0x47,0x95,0x3,0x7,0x0,0x0,
+0x0,0xfd,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xef,0xff,0xff,
+0xff,0xe9,0xff,0xff,0xff,0xe8,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0x30,0xa,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x30,0xa,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x5,0x0,0x0,0x0,0xfb,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x20,0x0,0x0,
+0x0,0x24,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x43,0x0,0x0,
+0x0,0x4a,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x5c,0x0,0x0,0x0,0x80,0x0,0x0,
+0x0,0x97,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0xaf,0x0,0x0,0x0,0xba,0x0,0x0,
+0x0,0xcb,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x75,0x1,0x0,0x0,0x8b,0x1,0x0,
+0x0,0xa3,0x1,0x0,0x0,0xc5,0x1,0x0,0x0,0xe0,0x1,0x0,0x0,0xfc,0x1,0x0,
+0x0,0xe,0x2,0x0,0x0,0x1d,0x2,0x0,0x0,0xbc,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xfa,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0x87,0x41,0x95,0x3,0x7,0x0,0x0,
+0x0,0xfb,0xff,0xff,0xff,0xf9,0xff,0xff,0xff,0xed,0xff,0xff,0xff,0xec,0xff,0xff,
+0xff,0xeb,0xff,0xff,0xff,0xe5,0xff,0xff,0xff,0xe4,0xff,0xff,0xff,0x3d,0xa,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x19,0x4,0x83,0x0,0x5,0x0,0x0,0x0,0xf9,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x1d,0x0,0x0,
+0x0,0x22,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x42,0x0,0x0,
+0x0,0x4b,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x82,0x0,0x0,
+0x0,0x9e,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0xbc,0x0,0x0,
+0x0,0xca,0x0,0x0,0x0,0xef,0x0,0x0,0x0,0x7a,0x1,0x0,0x0,0x92,0x1,0x0,
+0x0,0xae,0x1,0x0,0x0,0xd1,0x1,0x0,0x0,0xe7,0x1,0x0,0x0,0x2,0x2,0x0,
+0x0,0x12,0x2,0x0,0x0,0x21,0x2,0x0,0x0,0xbe,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xf8,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0x9f,0x0,0x0,0x0,0x2,0x1,0x0,0x0,0xc,0x1,0x0,0x0,0x10,0x1,0x0,
+0x0,0x2c,0x1,0x0,0x0,0x34,0x1,0x0,0x0,0x38,0x1,0x0,0x0,0x3d,0x1,0x0,
+0x0,0x44,0x1,0x0,0x0,0x49,0x1,0x0,0x0,0x26,0x2,0x0,0x0,0x2a,0x2,0x0,
+0x0,0x2e,0x2,0x0,0x0,0x34,0x2,0x0,0x0,0x38,0x2,0x0,0x0,0x3f,0x2,0x0,
+0x0,0x44,0x2,0x0,0x0,0x4a,0x2,0x0,0x0,0x4e,0x2,0x0,0x0,0x55,0x2,0x0,
+0x0,0x5a,0x2,0x0,0x0,0x5e,0x2,0x0,0x0,0x62,0x2,0x0,0x0,0x66,0x2,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xf7,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0x6e,0x3d,0x12,
+0x3,0x6,0x0,0x0,0x0,0xf8,0xff,0xff,0xff,0xf6,0xff,0xff,0xff,0xe1,0xff,0xff,
+0xff,0xe0,0xff,0xff,0xff,0xf5,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x3d,0xa,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x5,0x0,0x0,0x0,0xf6,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0x4,0x1,0x0,
+0x0,0xb,0x1,0x0,0x0,0xf,0x1,0x0,0x0,0x1a,0x1,0x0,0x0,0x31,0x1,0x0,
+0x0,0x37,0x1,0x0,0x0,0x3b,0x1,0x0,0x0,0x41,0x1,0x0,0x0,0x46,0x1,0x0,
+0x0,0x4d,0x1,0x0,0x0,0x27,0x2,0x0,0x0,0x2b,0x2,0x0,0x0,0x30,0x2,0x0,
+0x0,0x36,0x2,0x0,0x0,0x3a,0x2,0x0,0x0,0x42,0x2,0x0,0x0,0x48,0x2,0x0,
+0x0,0x4c,0x2,0x0,0x0,0x52,0x2,0x0,0x0,0x57,0x2,0x0,0x0,0x5c,0x2,0x0,
+0x0,0x60,0x2,0x0,0x0,0x64,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xf5,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x1e,0x0,0x0,
+0x0,0x2c,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x3c,0x0,0x0,
+0x0,0x3f,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x4d,0x0,0x0,
+0x0,0x52,0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0x62,0x0,0x0,
+0x0,0x63,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x67,0x0,0x0,
+0x0,0x68,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xf4,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0x6b,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x6e,0x0,0x0,
+0x0,0x6f,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x72,0x0,0x0,
+0x0,0x73,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x76,0x0,0x0,
+0x0,0x77,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x83,0x0,0x0,
+0x0,0x84,0x0,0x0,0x0,0x85,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x87,0x0,0x0,
+0x0,0x89,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x8d,0x0,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xf3,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x95,0x0,0x0,0x0,0xad,0x0,0x0,
+0x0,0xc6,0x0,0x0,0x0,0xe2,0x0,0x0,0x0,0x1c,0x1,0x0,0x0,0x22,0x1,0x0,
+0x0,0x29,0x1,0x0,0x0,0x3e,0x1,0x0,0x0,0x50,0x1,0x0,0x0,0x5d,0x1,0x0,
+0x0,0x6c,0x1,0x0,0x0,0x70,0x2,0x0,0x0,0x85,0x2,0x0,0x0,0x90,0x2,0x0,
+0x0,0x9d,0x2,0x0,0x0,0xaa,0x2,0x0,0x0,0xb6,0x2,0x0,0x0,0xc1,0x2,0x0,
+0x0,0xcc,0x2,0x0,0x0,0xd7,0x2,0x0,0x0,0xe0,0x2,0x0,0x0,0xed,0x2,0x0,
+0x0,0xfa,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xf2,0xff,0xff,0xff,0x2,0x0,0x5,
+0x0,0x57,0xea,0x84,0x3,0x7,0x0,0x0,0x0,0xf3,0xff,0xff,0xff,0xee,0xff,0xff,
+0xff,0xea,0xff,0xff,0xff,0xe6,0xff,0xff,0xff,0xe2,0xff,0xff,0xff,0xdf,0xff,0xff,
+0xff,0xde,0xff,0xff,0xff,0x3d,0xa,0x83,0x0,0x7b,0x94,0x7d,0x0,0x2f,0xe,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x7b,0x94,0x7d,0x0,0x3d,0xa,0x83,0x0,0x7b,0x94,0x7d,
+0x0,0x5,0x0,0x0,0x0,0xf1,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x16,0x0,0x0,
+0x0,0xd9,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x3,0x1,0x0,
+0x0,0x16,0x1,0x0,0x0,0x66,0x1,0x0,0x0,0x8a,0x1,0x0,0x0,0xaa,0x1,0x0,
+0x0,0xca,0x1,0x0,0x0,0xe9,0x1,0x0,0x0,0x10,0x2,0x0,0x0,0x24,0x2,0x0,
+0x0,0x74,0x5,0x0,0x0,0x7f,0x5,0x0,0x0,0x86,0x5,0x0,0x0,0x90,0x5,0x0,
+0x0,0x98,0x5,0x0,0x0,0xa0,0x5,0x0,0x0,0xa8,0x5,0x0,0x0,0xb0,0x5,0x0,
+0x0,0xb6,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xf0,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0xc,0x0,0x0,
+0x0,0x15,0x0,0x0,0x0,0xd8,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0xee,0x0,0x0,
+0x0,0x1,0x1,0x0,0x0,0x14,0x1,0x0,0x0,0x5a,0x1,0x0,0x0,0x77,0x1,0x0,
+0x0,0x97,0x1,0x0,0x0,0xbd,0x1,0x0,0x0,0xd6,0x1,0x0,0x0,0xf8,0x1,0x0,
+0x0,0x19,0x2,0x0,0x0,0x71,0x5,0x0,0x0,0x79,0x5,0x0,0x0,0x81,0x5,0x0,
+0x0,0x89,0x5,0x0,0x0,0x91,0x5,0x0,0x0,0x99,0x5,0x0,0x0,0xa1,0x5,0x0,
+0x0,0xa9,0x5,0x0,0x0,0xb1,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xef,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x5,0x0,0x0,
+0x0,0xf,0x0,0x0,0x0,0xd6,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0xea,0x0,0x0,
+0x0,0xf7,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x54,0x1,0x0,0x0,0x6b,0x1,0x0,
+0x0,0x86,0x1,0x0,0x0,0xa9,0x1,0x0,0x0,0xc0,0x1,0x0,0x0,0xd8,0x1,0x0,
+0x0,0xfb,0x1,0x0,0x0,0x1f,0x2,0x0,0x0,0x73,0x5,0x0,0x0,0x7b,0x5,0x0,
+0x0,0x82,0x5,0x0,0x0,0x8a,0x5,0x0,0x0,0x93,0x5,0x0,0x0,0x9b,0x5,0x0,
+0x0,0xab,0x5,0x0,0x0,0xb2,0x5,0x0,0x0,0x60,0x0,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xee,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x6e,0x94,0x7d,0x0,0x17,0x0,0x0,
+0x0,0x91,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0xd7,0x0,0x0,
+0x0,0xfc,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0x24,0x1,0x0,0x0,0x2d,0x1,0x0,
+0x0,0x4b,0x1,0x0,0x0,0x53,0x1,0x0,0x0,0x60,0x1,0x0,0x0,0x6a,0x2,0x0,
+0x0,0x73,0x2,0x0,0x0,0x8c,0x2,0x0,0x0,0x92,0x2,0x0,0x0,0xa1,0x2,0x0,
+0x0,0xab,0x2,0x0,0x0,0xba,0x2,0x0,0x0,0xc4,0x2,0x0,0x0,0xcf,0x2,0x0,
+0x0,0xd9,0x2,0x0,0x0,0xea,0x2,0x0,0x0,0xf6,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xed,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x18,0x0,0x0,
+0x0,0x27,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x3e,0x0,0x0,
+0x0,0x49,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x81,0x0,0x0,
+0x0,0x9d,0x0,0x0,0x0,0xa9,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0xbe,0x0,0x0,
+0x0,0xcc,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x81,0x1,0x0,0x0,0x95,0x1,0x0,
+0x0,0xb2,0x1,0x0,0x0,0xd2,0x1,0x0,0x0,0xeb,0x1,0x0,0x0,0x3,0x2,0x0,
+0x0,0x14,0x2,0x0,0x0,0x3c,0x2,0x0,0x0,0xc0,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xec,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0x1a,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x3a,0x0,0x0,
+0x0,0x47,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x7c,0x0,0x0,
+0x0,0x96,0x0,0x0,0x0,0xa3,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0xb8,0x0,0x0,
+0x0,0xc0,0x0,0x0,0x0,0xce,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x82,0x1,0x0,
+0x0,0x9d,0x1,0x0,0x0,0xbb,0x1,0x0,0x0,0xd5,0x1,0x0,0x0,0xf0,0x1,0x0,
+0x0,0x6,0x2,0x0,0x0,0x18,0x2,0x0,0x0,0xb9,0x5,0x0,0x0,0xc1,0x5,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xeb,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x31,0x0,0x0,
+0x0,0x3b,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x5a,0x0,0x0,
+0x0,0x7b,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,0xac,0x0,0x0,
+0x0,0xb9,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0x71,0x1,0x0,
+0x0,0x89,0x1,0x0,0x0,0x9e,0x1,0x0,0x0,0xbc,0x1,0x0,0x0,0xd9,0x1,0x0,
+0x0,0xf1,0x1,0x0,0x0,0xa,0x2,0x0,0x0,0x17,0x2,0x0,0x0,0xba,0x5,0x0,
+0x0,0xc2,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xea,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x2f,0xe,0x83,0x0,0x18,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x98,0x0,0x0,
+0x0,0xb1,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0x1b,0x1,0x0,
+0x0,0x23,0x1,0x0,0x0,0x2a,0x1,0x0,0x0,0x3f,0x1,0x0,0x0,0x51,0x1,0x0,
+0x0,0x5e,0x1,0x0,0x0,0xb7,0x1,0x0,0x0,0x86,0x2,0x0,0x0,0x93,0x2,0x0,
+0x0,0xa0,0x2,0x0,0x0,0xae,0x2,0x0,0x0,0xb9,0x2,0x0,0x0,0xc3,0x2,0x0,
+0x0,0xd8,0x2,0x0,0x0,0xe2,0x2,0x0,0x0,0xee,0x2,0x0,0x0,0xfd,0x2,0x0,
+0x0,0x1b,0x0,0x0,0x0,0xce,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x3f,0x75,0x5,0x0,0x44,0x7a,0x5,0x0,0x5,0x0,0x0,0x0,0xe9,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0xd,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0xe6,0x0,0x0,
+0x0,0xf4,0x0,0x0,0x0,0x6,0x1,0x0,0x0,0x52,0x1,0x0,0x0,0x5c,0x1,0x0,
+0x0,0x7f,0x1,0x0,0x0,0xa0,0x1,0x0,0x0,0xbf,0x1,0x0,0x0,0xdb,0x1,0x0,
+0x0,0x0,0x2,0x0,0x0,0x23,0x2,0x0,0x0,0x77,0x5,0x0,0x0,0x7e,0x5,0x0,
+0x0,0x87,0x5,0x0,0x0,0x8f,0x5,0x0,0x0,0x96,0x5,0x0,0x0,0x9f,0x5,0x0,
+0x0,0xa7,0x5,0x0,0x0,0xad,0x5,0x0,0x0,0xb3,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xe8,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0x2,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0xdf,0x0,0x0,
+0x0,0xeb,0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0x57,0x1,0x0,
+0x0,0x73,0x1,0x0,0x0,0x91,0x1,0x0,0x0,0xcd,0x1,0x0,0x0,0xf3,0x1,0x0,
+0x0,0x16,0x2,0x0,0x0,0x70,0x5,0x0,0x0,0x78,0x5,0x0,0x0,0x80,0x5,0x0,
+0x0,0x88,0x5,0x0,0x0,0x8c,0x5,0x0,0x0,0x95,0x5,0x0,0x0,0x9d,0x5,0x0,
+0x0,0xa5,0x5,0x0,0x0,0xae,0x5,0x0,0x0,0xb7,0x5,0x0,0x0,0x44,0x0,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xe7,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x64,0x0,0x0,
+0x0,0xda,0x0,0x0,0x0,0xe7,0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0x9,0x1,0x0,
+0x0,0x17,0x1,0x0,0x0,0x5f,0x1,0x0,0x0,0x84,0x1,0x0,0x0,0xa1,0x1,0x0,
+0x0,0xc9,0x1,0x0,0x0,0xe4,0x1,0x0,0x0,0x8,0x2,0x0,0x0,0x20,0x2,0x0,
+0x0,0x76,0x5,0x0,0x0,0x7c,0x5,0x0,0x0,0x84,0x5,0x0,0x0,0x8e,0x5,0x0,
+0x0,0x97,0x5,0x0,0x0,0x9e,0x5,0x0,0x0,0xa6,0x5,0x0,0x0,0xb5,0x5,0x0,
+0x0,0xaf,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xe6,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x9c,0x0,0x0,
+0x0,0xb7,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x1e,0x1,0x0,
+0x0,0x26,0x1,0x0,0x0,0x2e,0x1,0x0,0x0,0x47,0x1,0x0,0x0,0x56,0x1,0x0,
+0x0,0x62,0x1,0x0,0x0,0x69,0x2,0x0,0x0,0x72,0x2,0x0,0x0,0x8a,0x2,0x0,
+0x0,0x96,0x2,0x0,0x0,0xa3,0x2,0x0,0x0,0xb1,0x2,0x0,0x0,0xbb,0x2,0x0,
+0x0,0xc6,0x2,0x0,0x0,0xd1,0x2,0x0,0x0,0xdc,0x2,0x0,0x0,0xeb,0x2,0x0,
+0x0,0xf4,0x2,0x0,0x0,0xff,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xe5,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x19,0x0,0x0,
+0x0,0x25,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x41,0x0,0x0,
+0x0,0x4f,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x88,0x0,0x0,
+0x0,0xa0,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0xbf,0x0,0x0,
+0x0,0xcf,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x85,0x1,0x0,0x0,0x98,0x1,0x0,
+0x0,0xc4,0x1,0x0,0x0,0xdf,0x1,0x0,0x0,0xf7,0x1,0x0,0x0,0xb,0x2,0x0,
+0x0,0x1b,0x2,0x0,0x0,0xbb,0x5,0x0,0x0,0xc3,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xe4,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xc,0x4,0x83,0x0,0x18,0x0,0x0,
+0x0,0x28,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x4c,0x0,0x0,
+0x0,0x56,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0xa7,0x0,0x0,
+0x0,0xb0,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0xd2,0x0,0x0,0x0,0x76,0x1,0x0,
+0x0,0xa6,0x1,0x0,0x0,0xc6,0x1,0x0,0x0,0xe2,0x1,0x0,0x0,0xf,0x2,0x0,
+0x0,0x1e,0x2,0x0,0x0,0xbd,0x5,0x0,0x0,0xa,0x0,0x0,0x0,0x10,0x0,0x0,
+0x0,0x1c,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x32,0x0,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xbc,0x74,0x5,0x0,0xbc,0x74,0x5,
+0x0,0xbc,0x74,0x5,0x0,0xbc,0x74,0x5,0x0,0xbc,0x74,0x5,0x0,0xbc,0x74,0x5,
+0x0,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0xe2,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x6e,0x94,0x7d,0x0,0x17,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x9a,0x0,0x0,
+0x0,0xb3,0x0,0x0,0x0,0xc9,0x0,0x0,0x0,0xed,0x0,0x0,0x0,0x1d,0x1,0x0,
+0x0,0x25,0x1,0x0,0x0,0x2b,0x1,0x0,0x0,0x42,0x1,0x0,0x0,0x58,0x1,0x0,
+0x0,0x63,0x1,0x0,0x0,0x6b,0x2,0x0,0x0,0x77,0x2,0x0,0x0,0x88,0x2,0x0,
+0x0,0x98,0x2,0x0,0x0,0xa6,0x2,0x0,0x0,0xaf,0x2,0x0,0x0,0xbe,0x2,0x0,
+0x0,0xc8,0x2,0x0,0x0,0xd4,0x2,0x0,0x0,0xdd,0x2,0x0,0x0,0xe7,0x2,0x0,
+0x0,0xf8,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xe1,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0xfd,0x0,0x0,0x0,0x7,0x1,0x0,0x0,0xd,0x1,0x0,
+0x0,0x19,0x1,0x0,0x0,0x2f,0x1,0x0,0x0,0x32,0x1,0x0,0x0,0x39,0x1,0x0,
+0x0,0x3c,0x1,0x0,0x0,0x43,0x1,0x0,0x0,0x48,0x1,0x0,0x0,0x4f,0x1,0x0,
+0x0,0x28,0x2,0x0,0x0,0x2c,0x2,0x0,0x0,0x33,0x2,0x0,0x0,0x37,0x2,0x0,
+0x0,0x3b,0x2,0x0,0x0,0x43,0x2,0x0,0x0,0x49,0x2,0x0,0x0,0x4d,0x2,0x0,
+0x0,0x53,0x2,0x0,0x0,0x59,0x2,0x0,0x0,0x5d,0x2,0x0,0x0,0x61,0x2,0x0,
+0x0,0x65,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xe0,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x5,0x1,0x0,
+0x0,0xe,0x1,0x0,0x0,0x18,0x1,0x0,0x0,0x30,0x1,0x0,0x0,0x35,0x1,0x0,
+0x0,0x3a,0x1,0x0,0x0,0x40,0x1,0x0,0x0,0x45,0x1,0x0,0x0,0x4a,0x1,0x0,
+0x0,0x25,0x2,0x0,0x0,0x29,0x2,0x0,0x0,0x2f,0x2,0x0,0x0,0x35,0x2,0x0,
+0x0,0x39,0x2,0x0,0x0,0x41,0x2,0x0,0x0,0x47,0x2,0x0,0x0,0x4b,0x2,0x0,
+0x0,0x51,0x2,0x0,0x0,0x56,0x2,0x0,0x0,0x5b,0x2,0x0,0x0,0x5f,0x2,0x0,
+0x0,0x63,0x2,0x0,0x0,0x67,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xdf,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x93,0x0,0x0,
+0x0,0xa4,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0xff,0x0,0x0,
+0x0,0x20,0x1,0x0,0x0,0x27,0x1,0x0,0x0,0x33,0x1,0x0,0x0,0x4c,0x1,0x0,
+0x0,0x5b,0x1,0x0,0x0,0x6a,0x1,0x0,0x0,0x6c,0x2,0x0,0x0,0x7b,0x2,0x0,
+0x0,0x8e,0x2,0x0,0x0,0x99,0x2,0x0,0x0,0xa7,0x2,0x0,0x0,0xb2,0x2,0x0,
+0x0,0xbf,0x2,0x0,0x0,0xc5,0x2,0x0,0x0,0xd2,0x2,0x0,0x0,0xdb,0x2,0x0,
+0x0,0xe9,0x2,0x0,0x0,0xf1,0x2,0x0,0x0,0xfe,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xde,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x6e,0x94,0x7d,0x0,0x17,0x0,0x0,
+0x0,0x94,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0xdb,0x0,0x0,
+0x0,0xa,0x1,0x0,0x0,0x21,0x1,0x0,0x0,0x28,0x1,0x0,0x0,0x36,0x1,0x0,
+0x0,0x4e,0x1,0x0,0x0,0x59,0x1,0x0,0x0,0x64,0x1,0x0,0x0,0x6d,0x2,0x0,
+0x0,0x81,0x2,0x0,0x0,0x8f,0x2,0x0,0x0,0x9c,0x2,0x0,0x0,0xa9,0x2,0x0,
+0x0,0xb4,0x2,0x0,0x0,0xc0,0x2,0x0,0x0,0xc9,0x2,0x0,0x0,0xd5,0x2,0x0,
+0x0,0xde,0x2,0x0,0x0,0xe6,0x2,0x0,0x0,0xef,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xdd,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x65,0x1,0x0,
+0x0,0x67,0x1,0x0,0x0,0x68,0x1,0x0,0x0,0x69,0x1,0x0,0x0,0x9c,0x1,0x0,
+0x0,0x30,0x3,0x0,0x0,0x6a,0x3,0x0,0x0,0xbc,0x3,0x0,0x0,0x10,0x4,0x0,
+0x0,0x59,0x4,0x0,0x0,0xd2,0x4,0x0,0x0,0xd3,0x4,0x0,0x0,0xd4,0x4,0x0,
+0x0,0xd5,0x4,0x0,0x0,0xd6,0x4,0x0,0x0,0xd7,0x4,0x0,0x0,0xd8,0x4,0x0,
+0x0,0xd9,0x4,0x0,0x0,0xda,0x4,0x0,0x0,0xdb,0x4,0x0,0x0,0xdc,0x4,0x0,
+0x0,0xdd,0x4,0x0,0x0,0xde,0x4,0x0,0x0,0xdf,0x4,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xdc,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0xe0,0x4,0x0,0x0,0xe7,0x4,0x0,0x0,0xec,0x4,0x0,0x0,0xf0,0x4,0x0,
+0x0,0xf8,0x4,0x0,0x0,0xfc,0x4,0x0,0x0,0x0,0x5,0x0,0x0,0x7,0x5,0x0,
+0x0,0xc,0x5,0x0,0x0,0xf,0x5,0x0,0x0,0x16,0x5,0x0,0x0,0x1c,0x5,0x0,
+0x0,0x21,0x5,0x0,0x0,0x26,0x5,0x0,0x0,0x2c,0x5,0x0,0x0,0x31,0x5,0x0,
+0x0,0x36,0x5,0x0,0x0,0x3b,0x5,0x0,0x0,0x41,0x5,0x0,0x0,0x46,0x5,0x0,
+0x0,0x4a,0x5,0x0,0x0,0x4f,0x5,0x0,0x0,0x53,0x5,0x0,0x0,0x5a,0x5,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xdb,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xad,0x9,0x83,
+0x0,0x18,0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0xe6,0x4,0x0,0x0,0xeb,0x4,0x0,
+0x0,0xf2,0x4,0x0,0x0,0xf7,0x4,0x0,0x0,0xfd,0x4,0x0,0x0,0x2,0x5,0x0,
+0x0,0x6,0x5,0x0,0x0,0xd,0x5,0x0,0x0,0x10,0x5,0x0,0x0,0x14,0x5,0x0,
+0x0,0x19,0x5,0x0,0x0,0x1e,0x5,0x0,0x0,0x24,0x5,0x0,0x0,0x29,0x5,0x0,
+0x0,0x2e,0x5,0x0,0x0,0x33,0x5,0x0,0x0,0x3a,0x5,0x0,0x0,0x3e,0x5,0x0,
+0x0,0x44,0x5,0x0,0x0,0x4b,0x5,0x0,0x0,0x55,0x5,0x0,0x0,0x5b,0x5,0x0,
+0x0,0xc9,0x3,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x3f,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xda,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0xe1,0x4,0x0,0x0,0xe9,0x4,0x0,
+0x0,0xed,0x4,0x0,0x0,0xf3,0x4,0x0,0x0,0xf6,0x4,0x0,0x0,0xfb,0x4,0x0,
+0x0,0x1,0x5,0x0,0x0,0x5,0x5,0x0,0x0,0xb,0x5,0x0,0x0,0x11,0x5,0x0,
+0x0,0x15,0x5,0x0,0x0,0x1a,0x5,0x0,0x0,0x1f,0x5,0x0,0x0,0x23,0x5,0x0,
+0x0,0x2a,0x5,0x0,0x0,0x30,0x5,0x0,0x0,0x35,0x5,0x0,0x0,0x3c,0x5,0x0,
+0x0,0x40,0x5,0x0,0x0,0x45,0x5,0x0,0x0,0x49,0x5,0x0,0x0,0x4e,0x5,0x0,
+0x0,0x54,0x5,0x0,0x0,0x59,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xd9,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0xe4,0x4,0x0,
+0x0,0xe5,0x4,0x0,0x0,0xea,0x4,0x0,0x0,0xef,0x4,0x0,0x0,0xf4,0x4,0x0,
+0x0,0xf9,0x4,0x0,0x0,0xfe,0x4,0x0,0x0,0x4,0x5,0x0,0x0,0x9,0x5,0x0,
+0x0,0xe,0x5,0x0,0x0,0x13,0x5,0x0,0x0,0x18,0x5,0x0,0x0,0x1d,0x5,0x0,
+0x0,0x22,0x5,0x0,0x0,0x28,0x5,0x0,0x0,0x2d,0x5,0x0,0x0,0x32,0x5,0x0,
+0x0,0x37,0x5,0x0,0x0,0x3d,0x5,0x0,0x0,0x42,0x5,0x0,0x0,0x47,0x5,0x0,
+0x0,0x4c,0x5,0x0,0x0,0x51,0x5,0x0,0x0,0x56,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xd8,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0xe3,0x4,0x0,0x0,0xe8,0x4,0x0,0x0,0xee,0x4,0x0,0x0,0xf1,0x4,0x0,
+0x0,0xf5,0x4,0x0,0x0,0xfa,0x4,0x0,0x0,0xff,0x4,0x0,0x0,0x8,0x5,0x0,
+0x0,0xa,0x5,0x0,0x0,0x12,0x5,0x0,0x0,0x17,0x5,0x0,0x0,0x1b,0x5,0x0,
+0x0,0x20,0x5,0x0,0x0,0x25,0x5,0x0,0x0,0x2b,0x5,0x0,0x0,0x2f,0x5,0x0,
+0x0,0x34,0x5,0x0,0x0,0x39,0x5,0x0,0x0,0x3f,0x5,0x0,0x0,0x43,0x5,0x0,
+0x0,0x48,0x5,0x0,0x0,0x4d,0x5,0x0,0x0,0x52,0x5,0x0,0x0,0x58,0x5,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xd7,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x5d,0x5,0x0,0x0,0x57,0x5,0x0,0x0,0x5c,0x5,0x0,
+0x0,0x38,0x5,0x0,0x0,0x5f,0x5,0x0,0x0,0x5e,0x5,0x0,0x0,0x60,0x5,0x0,
+0x0,0x3,0x5,0x0,0x0,0x61,0x5,0x0,0x0,0x27,0x5,0x0,0x0,0x62,0x5,0x0,
+0x0,0x63,0x5,0x0,0x0,0x64,0x5,0x0,0x0,0x65,0x5,0x0,0x0,0x66,0x5,0x0,
+0x0,0x67,0x5,0x0,0x0,0x68,0x5,0x0,0x0,0x69,0x5,0x0,0x0,0x6a,0x5,0x0,
+0x0,0x6b,0x5,0x0,0x0,0x6c,0x5,0x0,0x0,0x6d,0x5,0x0,0x0,0x6e,0x5,0x0,
+0x0,0x6f,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xd6,0xff,0xff,0xff,0x2,0x0,0x5,
+0x0,0xcd,0x46,0x95,0x3,0x7,0x0,0x0,0x0,0xdd,0xff,0xff,0xff,0xdc,0xff,0xff,
+0xff,0xdb,0xff,0xff,0xff,0xda,0xff,0xff,0xff,0xd9,0xff,0xff,0xff,0xd8,0xff,0xff,
+0xff,0xd7,0xff,0xff,0xff,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0xad,0x9,0x83,
+0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,
+0x0,0x5,0x0,0x0,0x0,0xd5,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x11,0x0,0x0,
+0x0,0xd4,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x0,0x1,0x0,
+0x0,0x13,0x1,0x0,0x0,0x55,0x1,0x0,0x0,0x70,0x1,0x0,0x0,0x8f,0x1,0x0,
+0x0,0xb6,0x1,0x0,0x0,0xcc,0x1,0x0,0x0,0xf5,0x1,0x0,0x0,0x13,0x2,0x0,
+0x0,0x20,0x4,0x0,0x0,0x72,0x5,0x0,0x0,0x7a,0x5,0x0,0x0,0x83,0x5,0x0,
+0x0,0x8b,0x5,0x0,0x0,0x92,0x5,0x0,0x0,0x9a,0x5,0x0,0x0,0xa2,0x5,0x0,
+0x0,0xaa,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xd4,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0xb,0x0,0x0,
+0x0,0x16,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0xf1,0x0,0x0,
+0x0,0x3,0x1,0x0,0x0,0x16,0x1,0x0,0x0,0x66,0x1,0x0,0x0,0x8a,0x1,0x0,
+0x0,0xaa,0x1,0x0,0x0,0xca,0x1,0x0,0x0,0xe9,0x1,0x0,0x0,0x10,0x2,0x0,
+0x0,0x24,0x2,0x0,0x0,0x74,0x5,0x0,0x0,0x7f,0x5,0x0,0x0,0x86,0x5,0x0,
+0x0,0x90,0x5,0x0,0x0,0x98,0x5,0x0,0x0,0xa0,0x5,0x0,0x0,0xa8,0x5,0x0,
+0x0,0xb0,0x5,0x0,0x0,0xb6,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xd3,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x8,0x0,0x0,
+0x0,0xc,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0xd8,0x0,0x0,0x0,0xe3,0x0,0x0,
+0x0,0xee,0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x14,0x1,0x0,0x0,0x5a,0x1,0x0,
+0x0,0x77,0x1,0x0,0x0,0x97,0x1,0x0,0x0,0xbd,0x1,0x0,0x0,0xd6,0x1,0x0,
+0x0,0xf8,0x1,0x0,0x0,0x19,0x2,0x0,0x0,0x71,0x5,0x0,0x0,0x79,0x5,0x0,
+0x0,0x81,0x5,0x0,0x0,0x89,0x5,0x0,0x0,0x91,0x5,0x0,0x0,0x99,0x5,0x0,
+0x0,0xa1,0x5,0x0,0x0,0xa9,0x5,0x0,0x0,0xb1,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xd2,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0x5,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0xd6,0x0,0x0,0x0,0xe0,0x0,0x0,
+0x0,0xea,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x54,0x1,0x0,
+0x0,0x6b,0x1,0x0,0x0,0x86,0x1,0x0,0x0,0xa9,0x1,0x0,0x0,0xc0,0x1,0x0,
+0x0,0xd8,0x1,0x0,0x0,0xfb,0x1,0x0,0x0,0x1f,0x2,0x0,0x0,0x73,0x5,0x0,
+0x0,0x7b,0x5,0x0,0x0,0x82,0x5,0x0,0x0,0x8a,0x5,0x0,0x0,0x93,0x5,0x0,
+0x0,0x9b,0x5,0x0,0x0,0xab,0x5,0x0,0x0,0xb2,0x5,0x0,0x0,0x60,0x0,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xd1,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x17,0x0,0x0,
+0x0,0xdc,0x0,0x0,0x0,0xe6,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0x6,0x1,0x0,
+0x0,0x52,0x1,0x0,0x0,0x5c,0x1,0x0,0x0,0x7f,0x1,0x0,0x0,0xa0,0x1,0x0,
+0x0,0xbf,0x1,0x0,0x0,0xdb,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x23,0x2,0x0,
+0x0,0x77,0x5,0x0,0x0,0x7e,0x5,0x0,0x0,0x87,0x5,0x0,0x0,0x8f,0x5,0x0,
+0x0,0x96,0x5,0x0,0x0,0x9f,0x5,0x0,0x0,0xa7,0x5,0x0,0x0,0xad,0x5,0x0,
+0x0,0xb3,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xd0,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x14,0x0,0x0,
+0x0,0xd5,0x0,0x0,0x0,0xdf,0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0xf9,0x0,0x0,
+0x0,0x12,0x1,0x0,0x0,0x57,0x1,0x0,0x0,0x73,0x1,0x0,0x0,0x91,0x1,0x0,
+0x0,0xcd,0x1,0x0,0x0,0xf3,0x1,0x0,0x0,0x16,0x2,0x0,0x0,0x70,0x5,0x0,
+0x0,0x78,0x5,0x0,0x0,0x80,0x5,0x0,0x0,0x88,0x5,0x0,0x0,0x8c,0x5,0x0,
+0x0,0x95,0x5,0x0,0x0,0x9d,0x5,0x0,0x0,0xa5,0x5,0x0,0x0,0xae,0x5,0x0,
+0x0,0xb7,0x5,0x0,0x0,0x44,0x0,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xcf,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x4,0x0,0x0,
+0x0,0x12,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0xda,0x0,0x0,0x0,0xe7,0x0,0x0,
+0x0,0xf5,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x17,0x1,0x0,0x0,0x5f,0x1,0x0,
+0x0,0x84,0x1,0x0,0x0,0xa1,0x1,0x0,0x0,0xc9,0x1,0x0,0x0,0xe4,0x1,0x0,
+0x0,0x8,0x2,0x0,0x0,0x20,0x2,0x0,0x0,0x76,0x5,0x0,0x0,0x7c,0x5,0x0,
+0x0,0x84,0x5,0x0,0x0,0x8e,0x5,0x0,0x0,0x97,0x5,0x0,0x0,0x9e,0x5,0x0,
+0x0,0xa6,0x5,0x0,0x0,0xb5,0x5,0x0,0x0,0xaf,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xce,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0x50,0x47,0x95,0x3,0x7,0x0,0x0,
+0x0,0xd5,0xff,0xff,0xff,0xd4,0xff,0xff,0xff,0xd3,0xff,0xff,0xff,0xd2,0xff,0xff,
+0xff,0xd1,0xff,0xff,0xff,0xd0,0xff,0xff,0xff,0xcf,0xff,0xff,0xff,0x30,0xa,0x83,
+0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,
+0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x5,0x0,0x0,0x0,0xcd,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x20,0x0,0x0,
+0x0,0x24,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x43,0x0,0x0,
+0x0,0x4a,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x5c,0x0,0x0,0x0,0x80,0x0,0x0,
+0x0,0x97,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0xaf,0x0,0x0,0x0,0xba,0x0,0x0,
+0x0,0xcb,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x75,0x1,0x0,0x0,0x8b,0x1,0x0,
+0x0,0xa3,0x1,0x0,0x0,0xc5,0x1,0x0,0x0,0xe0,0x1,0x0,0x0,0xfc,0x1,0x0,
+0x0,0xe,0x2,0x0,0x0,0x1d,0x2,0x0,0x0,0xbc,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xcc,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0x1d,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x33,0x0,0x0,
+0x0,0x42,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x5f,0x0,0x0,
+0x0,0x82,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0xb2,0x0,0x0,
+0x0,0xbc,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0xef,0x0,0x0,0x0,0x7a,0x1,0x0,
+0x0,0x92,0x1,0x0,0x0,0xae,0x1,0x0,0x0,0xd1,0x1,0x0,0x0,0xe7,0x1,0x0,
+0x0,0x2,0x2,0x0,0x0,0x12,0x2,0x0,0x0,0x21,0x2,0x0,0x0,0xbe,0x5,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xcb,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x2e,0x0,0x0,
+0x0,0x39,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x54,0x0,0x0,
+0x0,0x78,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0xa9,0x0,0x0,
+0x0,0xb5,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0xcc,0x0,0x0,0x0,0xf3,0x0,0x0,
+0x0,0x81,0x1,0x0,0x0,0x95,0x1,0x0,0x0,0xb2,0x1,0x0,0x0,0xd2,0x1,0x0,
+0x0,0xeb,0x1,0x0,0x0,0x3,0x2,0x0,0x0,0x14,0x2,0x0,0x0,0x3c,0x2,0x0,
+0x0,0xc0,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xca,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x26,0x0,0x0,
+0x0,0x2f,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x51,0x0,0x0,
+0x0,0x5b,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0xa3,0x0,0x0,
+0x0,0xae,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0xce,0x0,0x0,
+0x0,0xf6,0x0,0x0,0x0,0x82,0x1,0x0,0x0,0x9d,0x1,0x0,0x0,0xbb,0x1,0x0,
+0x0,0xd5,0x1,0x0,0x0,0xf0,0x1,0x0,0x0,0x6,0x2,0x0,0x0,0x18,0x2,0x0,
+0x0,0xb9,0x5,0x0,0x0,0xc1,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xc9,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x1f,0x0,0x0,
+0x0,0x21,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x48,0x0,0x0,
+0x0,0x50,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x8b,0x0,0x0,
+0x0,0xa2,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0xc1,0x0,0x0,
+0x0,0xd0,0x0,0x0,0x0,0x71,0x1,0x0,0x0,0x89,0x1,0x0,0x0,0x9e,0x1,0x0,
+0x0,0xbc,0x1,0x0,0x0,0xd9,0x1,0x0,0x0,0xf1,0x1,0x0,0x0,0xa,0x2,0x0,
+0x0,0x17,0x2,0x0,0x0,0xba,0x5,0x0,0x0,0xc2,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xc8,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0x19,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x37,0x0,0x0,
+0x0,0x41,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x7a,0x0,0x0,
+0x0,0x88,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0xb6,0x0,0x0,
+0x0,0xbf,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x85,0x1,0x0,
+0x0,0x98,0x1,0x0,0x0,0xc4,0x1,0x0,0x0,0xdf,0x1,0x0,0x0,0xf7,0x1,0x0,
+0x0,0xb,0x2,0x0,0x0,0x1b,0x2,0x0,0x0,0xbb,0x5,0x0,0x0,0xc3,0x5,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xc7,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xc,0x4,0x83,
+0x0,0x18,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x38,0x0,0x0,
+0x0,0x4c,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x9b,0x0,0x0,
+0x0,0xa7,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0xd2,0x0,0x0,
+0x0,0x76,0x1,0x0,0x0,0xa6,0x1,0x0,0x0,0xc6,0x1,0x0,0x0,0xe2,0x1,0x0,
+0x0,0xf,0x2,0x0,0x0,0x1e,0x2,0x0,0x0,0xbd,0x5,0x0,0x0,0xa,0x0,0x0,
+0x0,0x10,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x2b,0x0,0x0,
+0x0,0x32,0x0,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xbc,0x74,0x5,
+0x0,0xbc,0x74,0x5,0x0,0xbc,0x74,0x5,0x0,0xbc,0x74,0x5,0x0,0xbc,0x74,0x5,
+0x0,0xbc,0x74,0x5,0x0,0x5,0x0,0x0,0x0,0xc6,0xff,0xff,0xff,0x2,0x0,0x5,
+0x0,0x2c,0x41,0x95,0x3,0x7,0x0,0x0,0x0,0xcd,0xff,0xff,0xff,0xcc,0xff,0xff,
+0xff,0xcb,0xff,0xff,0xff,0xca,0xff,0xff,0xff,0xc9,0xff,0xff,0xff,0xc8,0xff,0xff,
+0xff,0xc7,0xff,0xff,0xff,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,
+0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0xc,0x4,0x83,
+0x0,0x5,0x0,0x0,0x0,0xc5,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x2,0x1,0x0,0x0,0xc,0x1,0x0,
+0x0,0x10,0x1,0x0,0x0,0x2c,0x1,0x0,0x0,0x34,0x1,0x0,0x0,0x38,0x1,0x0,
+0x0,0x3d,0x1,0x0,0x0,0x44,0x1,0x0,0x0,0x49,0x1,0x0,0x0,0x26,0x2,0x0,
+0x0,0x2a,0x2,0x0,0x0,0x2e,0x2,0x0,0x0,0x34,0x2,0x0,0x0,0x38,0x2,0x0,
+0x0,0x3f,0x2,0x0,0x0,0x44,0x2,0x0,0x0,0x4a,0x2,0x0,0x0,0x4e,0x2,0x0,
+0x0,0x55,0x2,0x0,0x0,0x5a,0x2,0x0,0x0,0x5e,0x2,0x0,0x0,0x62,0x2,0x0,
+0x0,0x66,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xc4,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0x4,0x1,0x0,
+0x0,0xb,0x1,0x0,0x0,0xf,0x1,0x0,0x0,0x1a,0x1,0x0,0x0,0x31,0x1,0x0,
+0x0,0x37,0x1,0x0,0x0,0x3b,0x1,0x0,0x0,0x41,0x1,0x0,0x0,0x46,0x1,0x0,
+0x0,0x4d,0x1,0x0,0x0,0x27,0x2,0x0,0x0,0x2b,0x2,0x0,0x0,0x30,0x2,0x0,
+0x0,0x36,0x2,0x0,0x0,0x3a,0x2,0x0,0x0,0x42,0x2,0x0,0x0,0x48,0x2,0x0,
+0x0,0x4c,0x2,0x0,0x0,0x52,0x2,0x0,0x0,0x57,0x2,0x0,0x0,0x5c,0x2,0x0,
+0x0,0x60,0x2,0x0,0x0,0x64,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xc3,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0xfd,0x0,0x0,
+0x0,0x7,0x1,0x0,0x0,0xd,0x1,0x0,0x0,0x19,0x1,0x0,0x0,0x2f,0x1,0x0,
+0x0,0x32,0x1,0x0,0x0,0x39,0x1,0x0,0x0,0x3c,0x1,0x0,0x0,0x43,0x1,0x0,
+0x0,0x48,0x1,0x0,0x0,0x4f,0x1,0x0,0x0,0x28,0x2,0x0,0x0,0x2c,0x2,0x0,
+0x0,0x33,0x2,0x0,0x0,0x37,0x2,0x0,0x0,0x3b,0x2,0x0,0x0,0x43,0x2,0x0,
+0x0,0x49,0x2,0x0,0x0,0x4d,0x2,0x0,0x0,0x53,0x2,0x0,0x0,0x59,0x2,0x0,
+0x0,0x5d,0x2,0x0,0x0,0x61,0x2,0x0,0x0,0x65,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xc2,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0xfe,0x0,0x0,0x0,0x5,0x1,0x0,0x0,0xe,0x1,0x0,0x0,0x18,0x1,0x0,
+0x0,0x30,0x1,0x0,0x0,0x35,0x1,0x0,0x0,0x3a,0x1,0x0,0x0,0x40,0x1,0x0,
+0x0,0x45,0x1,0x0,0x0,0x4a,0x1,0x0,0x0,0x25,0x2,0x0,0x0,0x29,0x2,0x0,
+0x0,0x2f,0x2,0x0,0x0,0x35,0x2,0x0,0x0,0x39,0x2,0x0,0x0,0x41,0x2,0x0,
+0x0,0x47,0x2,0x0,0x0,0x4b,0x2,0x0,0x0,0x51,0x2,0x0,0x0,0x56,0x2,0x0,
+0x0,0x5b,0x2,0x0,0x0,0x5f,0x2,0x0,0x0,0x63,0x2,0x0,0x0,0x67,0x2,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0xc1,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0xe,0x0,0x0,
+0x0,0x13,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x34,0x0,0x0,
+0x0,0x35,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x40,0x0,0x0,
+0x0,0x45,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x59,0x0,0x0,
+0x0,0x61,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x65,0x0,0x0,
+0x0,0x66,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x69,0x0,0x0,
+0x0,0x6a,0x0,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xc0,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x6b,0x0,0x0,0x0,0x6c,0x0,0x0,
+0x0,0x6d,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0x70,0x0,0x0,
+0x0,0x71,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x74,0x0,0x0,
+0x0,0x75,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x79,0x0,0x0,
+0x0,0x7e,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x85,0x0,0x0,
+0x0,0x86,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x8a,0x0,0x0,
+0x0,0x8c,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xbf,0xff,0xff,
+0xff,0x4,0x0,0x5,0x0,0x79,0xcc,0x8c,0x4,0x2,0x0,0x0,0x0,0xb9,0xff,0xff,
+0xff,0xb4,0xff,0xff,0xff,0x11,0x66,0x46,0x2,0x68,0x66,0x46,0x2,0x5,0x0,0x0,
+0x0,0xbe,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0x20,0x3d,0x12,0x3,0x6,0x0,0x0,
+0x0,0xc5,0xff,0xff,0xff,0xc4,0xff,0xff,0xff,0xc3,0xff,0xff,0xff,0xc2,0xff,0xff,
+0xff,0xc1,0xff,0xff,0xff,0xc0,0xff,0xff,0xff,0x30,0xa,0x83,0x0,0x30,0xa,0x83,
+0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,0x0,0x30,0xa,0x83,
+0x0,0x5,0x0,0x0,0x0,0xbd,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x95,0x0,0x0,0x0,0xad,0x0,0x0,
+0x0,0xc6,0x0,0x0,0x0,0xe2,0x0,0x0,0x0,0x1c,0x1,0x0,0x0,0x22,0x1,0x0,
+0x0,0x29,0x1,0x0,0x0,0x3e,0x1,0x0,0x0,0x50,0x1,0x0,0x0,0x5d,0x1,0x0,
+0x0,0x6c,0x1,0x0,0x0,0x70,0x2,0x0,0x0,0x85,0x2,0x0,0x0,0x90,0x2,0x0,
+0x0,0x9d,0x2,0x0,0x0,0xaa,0x2,0x0,0x0,0xb6,0x2,0x0,0x0,0xc1,0x2,0x0,
+0x0,0xcc,0x2,0x0,0x0,0xd7,0x2,0x0,0x0,0xe0,0x2,0x0,0x0,0xed,0x2,0x0,
+0x0,0xfa,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xbc,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x65,0x1,0x0,0x0,0x67,0x1,0x0,
+0x0,0x68,0x1,0x0,0x0,0x69,0x1,0x0,0x0,0x9c,0x1,0x0,0x0,0x30,0x3,0x0,
+0x0,0x6a,0x3,0x0,0x0,0xbc,0x3,0x0,0x0,0x10,0x4,0x0,0x0,0x59,0x4,0x0,
+0x0,0xd2,0x4,0x0,0x0,0xd3,0x4,0x0,0x0,0xd4,0x4,0x0,0x0,0xd5,0x4,0x0,
+0x0,0xd6,0x4,0x0,0x0,0xd7,0x4,0x0,0x0,0xd8,0x4,0x0,0x0,0xd9,0x4,0x0,
+0x0,0xda,0x4,0x0,0x0,0xdb,0x4,0x0,0x0,0xdc,0x4,0x0,0x0,0xdd,0x4,0x0,
+0x0,0xde,0x4,0x0,0x0,0xdf,0x4,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xbb,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x87,0x1,0x0,
+0x0,0x6e,0x1,0x0,0x0,0x88,0x1,0x0,0x0,0x94,0x1,0x0,0x0,0x6f,0x1,0x0,
+0x0,0x9a,0x1,0x0,0x0,0x99,0x1,0x0,0x0,0x72,0x1,0x0,0x0,0x6d,0x1,0x0,
+0x0,0x96,0x1,0x0,0x0,0x78,0x1,0x0,0x0,0x83,0x1,0x0,0x0,0x8e,0x1,0x0,
+0x0,0x80,0x1,0x0,0x0,0x7c,0x1,0x0,0x0,0x79,0x1,0x0,0x0,0x8c,0x1,0x0,
+0x0,0x74,0x1,0x0,0x0,0x8d,0x1,0x0,0x0,0x7e,0x1,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0xba,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0xdd,0x32,0x23,0x1,0x4,0x0,0x0,
+0x0,0xbb,0xff,0xff,0xff,0xb7,0xff,0xff,0xff,0xad,0xff,0xff,0xff,0xaa,0xff,0xff,
+0xff,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,0x0,0x76,0xcc,0x48,0x0,0xcd,0xcc,0x48,
+0x0,0x5,0x0,0x0,0x0,0xb9,0xff,0xff,0xff,0x3,0x0,0x5,0x0,0x11,0x66,0x46,
+0x2,0x2,0x0,0x0,0x0,0xba,0xff,0xff,0xff,0xb0,0xff,0xff,0xff,0xdd,0x32,0x23,
+0x1,0x34,0x33,0x23,0x1,0x5,0x0,0x0,0x0,0xb8,0xff,0xff,0xff,0x2,0x0,0x5,
+0x0,0x1b,0x47,0x95,0x3,0x7,0x0,0x0,0x0,0xbc,0xff,0xff,0xff,0xa3,0xff,0xff,
+0xff,0xa2,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0xa0,0xff,0xff,0xff,0x9f,0xff,0xff,
+0xff,0x9e,0xff,0xff,0xff,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0xad,0x9,0x83,
+0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,0x0,0x3d,0xa,0x83,
+0x0,0x5,0x0,0x0,0x0,0xb7,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0xbe,0x1,0x0,0x0,0xba,0x1,0x0,0x0,0xb0,0x1,0x0,
+0x0,0x9f,0x1,0x0,0x0,0xa5,0x1,0x0,0x0,0xad,0x1,0x0,0x0,0xb3,0x1,0x0,
+0x0,0xb9,0x1,0x0,0x0,0xab,0x1,0x0,0x0,0xb8,0x1,0x0,0x0,0xfa,0x0,0x0,
+0x0,0xa4,0x1,0x0,0x0,0xaf,0x1,0x0,0x0,0xa8,0x1,0x0,0x0,0xb5,0x1,0x0,
+0x0,0xb4,0x1,0x0,0x0,0xa7,0x1,0x0,0x0,0xa2,0x1,0x0,0x0,0xc1,0x1,0x0,
+0x0,0xb1,0x1,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xb6,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x89,0x2,0x0,0x0,0x79,0x2,0x0,
+0x0,0x7,0x2,0x0,0x0,0x97,0x2,0x0,0x0,0xa5,0x2,0x0,0x0,0x5d,0x3,0x0,
+0x0,0x6f,0x2,0x0,0x0,0x64,0x3,0x0,0x0,0x4f,0x3,0x0,0x0,0x11,0x2,0x0,
+0x0,0x46,0x2,0x0,0x0,0xb5,0x2,0x0,0x0,0x32,0x2,0x0,0x0,0xca,0x2,0x0,
+0x0,0x33,0x3,0x0,0x0,0xda,0x2,0x0,0x0,0x61,0x3,0x0,0x0,0x80,0x2,0x0,
+0x0,0xe5,0x2,0x0,0x0,0xf5,0x2,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xb5,0xff,0xff,
+0xff,0x2,0x0,0x5,0x0,0x34,0x33,0x23,0x1,0x4,0x0,0x0,0x0,0xb6,0xff,0xff,
+0xff,0xac,0xff,0xff,0xff,0xab,0xff,0xff,0xff,0xa7,0xff,0xff,0xff,0xcd,0xcc,0x48,
+0x0,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,0x0,0x5,0x0,0x0,
+0x0,0xb4,0xff,0xff,0xff,0x3,0x0,0x5,0x0,0x68,0x66,0x46,0x2,0x2,0x0,0x0,
+0x0,0xb5,0xff,0xff,0xff,0xb2,0xff,0xff,0xff,0x34,0x33,0x23,0x1,0x34,0x33,0x23,
+0x1,0x5,0x0,0x0,0x0,0xb3,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,0x1c,0x2,0x0,0x0,0xcd,0x2,0x0,
+0x0,0x58,0x2,0x0,0x0,0x94,0x2,0x0,0x0,0x62,0x3,0x0,0x0,0xe1,0x2,0x0,
+0x0,0x84,0x2,0x0,0x0,0x40,0x2,0x0,0x0,0x7d,0x2,0x0,0x0,0xbd,0x2,0x0,
+0x0,0x5e,0x3,0x0,0x0,0xf0,0x2,0x0,0x0,0x75,0x3,0x0,0x0,0x9f,0x2,0x0,
+0x0,0xad,0x2,0x0,0x0,0xfb,0x2,0x0,0x0,0x51,0x3,0x0,0x0,0x75,0x2,0x0,
+0x0,0x6d,0x3,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xb2,0xff,0xff,0xff,0x2,0x0,0x5,
+0x0,0x34,0x33,0x23,0x1,0x4,0x0,0x0,0x0,0xb3,0xff,0xff,0xff,0xaf,0xff,0xff,
+0xff,0xae,0xff,0xff,0xff,0xa9,0xff,0xff,0xff,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,
+0x0,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,0x0,0x5,0x0,0x0,0x0,0xb1,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x30,0x4,0x0,
+0x0,0xc9,0x4,0x0,0x0,0xe9,0x3,0x0,0x0,0xa8,0x4,0x0,0x0,0x9,0x4,0x0,
+0x0,0x12,0x4,0x0,0x0,0xf6,0x3,0x0,0x0,0xd,0x4,0x0,0x0,0x95,0x4,0x0,
+0x0,0xfc,0x3,0x0,0x0,0x66,0x4,0x0,0x0,0xcd,0x4,0x0,0x0,0x7b,0x4,0x0,
+0x0,0xbb,0x4,0x0,0x0,0xcf,0x4,0x0,0x0,0xbe,0x4,0x0,0x0,0xcb,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x18,0x4,0x0,0x0,0x4a,0x4,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0xb0,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0x34,0x33,0x23,0x1,0x4,0x0,0x0,
+0x0,0xb1,0xff,0xff,0xff,0xa8,0xff,0xff,0xff,0xa6,0xff,0xff,0xff,0xa5,0xff,0xff,
+0xff,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,0x0,0xcd,0xcc,0x48,
+0x0,0x5,0x0,0x0,0x0,0xaf,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0x8,0x4,0x0,0x0,0x4c,0x4,0x0,0x0,0x2,0x4,0x0,
+0x0,0xb3,0x3,0x0,0x0,0xe,0x4,0x0,0x0,0xa9,0x3,0x0,0x0,0x3e,0x4,0x0,
+0x0,0x8c,0x3,0x0,0x0,0xfa,0x3,0x0,0x0,0xef,0x3,0x0,0x0,0x19,0x4,0x0,
+0x0,0xcd,0x3,0x0,0x0,0x13,0x4,0x0,0x0,0xa0,0x3,0x0,0x0,0xdf,0x3,0x0,
+0x0,0x6d,0x4,0x0,0x0,0x8a,0x3,0x0,0x0,0xd4,0x3,0x0,0x0,0xd9,0x3,0x0,
+0x0,0x91,0x3,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xae,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0xa,0x4,0x0,0x0,0xc0,0x4,0x0,
+0x0,0xe2,0x3,0x0,0x0,0xf,0x4,0x0,0x0,0xcc,0x4,0x0,0x0,0x82,0x4,0x0,
+0x0,0xce,0x4,0x0,0x0,0xf2,0x3,0x0,0x0,0xde,0x3,0x0,0x0,0x15,0x4,0x0,
+0x0,0x22,0x4,0x0,0x0,0xca,0x4,0x0,0x0,0xfb,0x3,0x0,0x0,0xb4,0x4,0x0,
+0x0,0x68,0x4,0x0,0x0,0x3,0x4,0x0,0x0,0x53,0x4,0x0,0x0,0xae,0x4,0x0,
+0x0,0x97,0x4,0x0,0x0,0x41,0x4,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xad,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x76,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x6b,0x3,0x0,
+0x0,0x6,0x4,0x0,0x0,0x85,0x3,0x0,0x0,0xd2,0x3,0x0,0x0,0xdd,0x3,0x0,
+0x0,0xe5,0x3,0x0,0x0,0x7b,0x3,0x0,0x0,0xc1,0x3,0x0,0x0,0xd8,0x3,0x0,
+0x0,0x7d,0x3,0x0,0x0,0x74,0x3,0x0,0x0,0x8d,0x3,0x0,0x0,0x88,0x3,0x0,
+0x0,0x82,0x3,0x0,0x0,0xaf,0x3,0x0,0x0,0xfe,0x3,0x0,0x0,0x9d,0x3,0x0,
+0x0,0xf8,0x3,0x0,0x0,0x7f,0x3,0x0,0x0,0xc8,0x3,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x81,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0xac,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,
+0x0,0xff,0x3,0x0,0x0,0x44,0x4,0x0,0x0,0x90,0x3,0x0,0x0,0xd1,0x3,0x0,
+0x0,0xdb,0x3,0x0,0x0,0xb6,0x3,0x0,0x0,0xd5,0x3,0x0,0x0,0x11,0x4,0x0,
+0x0,0xf7,0x3,0x0,0x0,0x75,0x4,0x0,0x0,0x9f,0x3,0x0,0x0,0x2f,0x4,0x0,
+0x0,0xe1,0x3,0x0,0x0,0x17,0x4,0x0,0x0,0x62,0x4,0x0,0x0,0x9c,0x4,0x0,
+0x0,0xc,0x4,0x0,0x0,0xa5,0x3,0x0,0x0,0xb2,0x3,0x0,0x0,0x5,0x4,0x0,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0x5,0x0,0x0,0x0,0xab,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0x9,0x2,0x0,0x0,0x76,0x2,0x0,0x0,0xf4,0x1,0x0,
+0x0,0xd3,0x1,0x0,0x0,0xfa,0x1,0x0,0x0,0x45,0x2,0x0,0x0,0x95,0x2,0x0,
+0x0,0xda,0x1,0x0,0x0,0x68,0x2,0x0,0x0,0xed,0x1,0x0,0x0,0x7f,0x2,0x0,
+0x0,0xb8,0x2,0x0,0x0,0xcb,0x2,0x0,0x0,0xcf,0x1,0x0,0x0,0x87,0x2,0x0,
+0x0,0xdf,0x2,0x0,0x0,0x2d,0x2,0x0,0x0,0xa4,0x2,0x0,0x0,0xe3,0x1,0x0,
+0x0,0xf3,0x2,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xaa,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0xc2,0x1,0x0,
+0x0,0x5,0x2,0x0,0x0,0xc7,0x2,0x0,0x0,0x9e,0x2,0x0,0x0,0xef,0x1,0x0,
+0x0,0x1a,0x2,0x0,0x0,0x3e,0x2,0x0,0x0,0x54,0x2,0x0,0x0,0xcb,0x1,0x0,
+0x0,0x7e,0x2,0x0,0x0,0x74,0x2,0x0,0x0,0xe6,0x1,0x0,0x0,0xd0,0x1,0x0,
+0x0,0xd4,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0xf9,0x1,0x0,0x0,0xde,0x1,0x0,
+0x0,0xc3,0x1,0x0,0x0,0xb0,0x2,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xa9,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0xbd,0x3,0x0,
+0x0,0xfd,0x3,0x0,0x0,0xa7,0x4,0x0,0x0,0xb1,0x4,0x0,0x0,0x16,0x4,0x0,
+0x0,0xb1,0x3,0x0,0x0,0xa6,0x3,0x0,0x0,0x2b,0x4,0x0,0x0,0xb,0x4,0x0,
+0x0,0x86,0x4,0x0,0x0,0x43,0x4,0x0,0x0,0x76,0x4,0x0,0x0,0xce,0x3,0x0,
+0x0,0xda,0x3,0x0,0x0,0x57,0x4,0x0,0x0,0x12,0x3,0x0,0x0,0xd6,0x3,0x0,
+0x0,0xe0,0x3,0x0,0x0,0xf0,0x3,0x0,0x0,0x4,0x4,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0xa8,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,
+0x0,0x7,0x4,0x0,0x0,0x6f,0x3,0x0,0x0,0x93,0x3,0x0,0x0,0xa2,0x3,0x0,
+0x0,0x89,0x3,0x0,0x0,0xd7,0x3,0x0,0x0,0x8e,0x3,0x0,0x0,0xd3,0x3,0x0,
+0x0,0xdc,0x3,0x0,0x0,0x86,0x3,0x0,0x0,0x7c,0x3,0x0,0x0,0x81,0x3,0x0,
+0x0,0x7a,0x3,0x0,0x0,0x84,0x3,0x0,0x0,0xe7,0x3,0x0,0x0,0xab,0x3,0x0,
+0x0,0x65,0x3,0x0,0x0,0xc7,0x3,0x0,0x0,0x0,0x4,0x0,0x0,0xf9,0x3,0x0,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0x5,0x0,0x0,0x0,0xa7,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0xec,0x2,0x0,0x0,0xe3,0x2,0x0,0x0,0x6e,0x3,0x0,
+0x0,0x76,0x3,0x0,0x0,0x8f,0x3,0x0,0x0,0x5a,0x3,0x0,0x0,0x87,0x3,0x0,
+0x0,0x80,0x3,0x0,0x0,0xf7,0x2,0x0,0x0,0x83,0x3,0x0,0x0,0xfc,0x2,0x0,
+0x0,0xd0,0x2,0x0,0x0,0x63,0x3,0x0,0x0,0xa2,0x2,0x0,0x0,0x4d,0x3,0x0,
+0x0,0xb3,0x2,0x0,0x0,0x60,0x3,0x0,0x0,0x7e,0x3,0x0,0x0,0x8b,0x3,0x0,
+0x0,0xc2,0x2,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xa6,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0xc,0x2,0x0,
+0x0,0x8b,0x2,0x0,0x0,0x6e,0x2,0x0,0x0,0xe1,0x1,0x0,0x0,0xf6,0x1,0x0,
+0x0,0xe4,0x2,0x0,0x0,0xd7,0x1,0x0,0x0,0x82,0x2,0x0,0x0,0xd3,0x2,0x0,
+0x0,0x78,0x2,0x0,0x0,0x34,0x3,0x0,0x0,0x9a,0x2,0x0,0x0,0x31,0x2,0x0,
+0x0,0xb7,0x2,0x0,0x0,0xee,0x1,0x0,0x0,0xff,0x1,0x0,0x0,0xf2,0x2,0x0,
+0x0,0xa8,0x2,0x0,0x0,0xea,0x1,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0xa5,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x83,0x2,0x0,
+0x0,0x50,0x2,0x0,0x0,0x71,0x2,0x0,0x0,0xac,0x2,0x0,0x0,0xd,0x2,0x0,
+0x0,0x9b,0x2,0x0,0x0,0xe5,0x1,0x0,0x0,0xe8,0x2,0x0,0x0,0x3d,0x2,0x0,
+0x0,0xf2,0x1,0x0,0x0,0xec,0x1,0x0,0x0,0x53,0x3,0x0,0x0,0x1,0x2,0x0,
+0x0,0xd6,0x2,0x0,0x0,0x4b,0x3,0x0,0x0,0xdd,0x1,0x0,0x0,0x7c,0x2,0x0,
+0x0,0xbc,0x2,0x0,0x0,0xf9,0x2,0x0,0x0,0x8d,0x2,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x0,0x0,0x0,
+0x0,0x5,0x0,0x0,0x0,0xa3,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0xe0,0x4,0x0,0x0,0xe7,0x4,0x0,0x0,0xec,0x4,0x0,
+0x0,0xf0,0x4,0x0,0x0,0xf8,0x4,0x0,0x0,0xfc,0x4,0x0,0x0,0x0,0x5,0x0,
+0x0,0x7,0x5,0x0,0x0,0xc,0x5,0x0,0x0,0xf,0x5,0x0,0x0,0x16,0x5,0x0,
+0x0,0x1c,0x5,0x0,0x0,0x21,0x5,0x0,0x0,0x26,0x5,0x0,0x0,0x2c,0x5,0x0,
+0x0,0x31,0x5,0x0,0x0,0x36,0x5,0x0,0x0,0x3b,0x5,0x0,0x0,0x41,0x5,0x0,
+0x0,0x46,0x5,0x0,0x0,0x4a,0x5,0x0,0x0,0x4f,0x5,0x0,0x0,0x53,0x5,0x0,
+0x0,0x5a,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xa2,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xad,0x9,0x83,0x0,0x18,0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0xe6,0x4,0x0,
+0x0,0xeb,0x4,0x0,0x0,0xf2,0x4,0x0,0x0,0xf7,0x4,0x0,0x0,0xfd,0x4,0x0,
+0x0,0x2,0x5,0x0,0x0,0x6,0x5,0x0,0x0,0xd,0x5,0x0,0x0,0x10,0x5,0x0,
+0x0,0x14,0x5,0x0,0x0,0x19,0x5,0x0,0x0,0x1e,0x5,0x0,0x0,0x24,0x5,0x0,
+0x0,0x29,0x5,0x0,0x0,0x2e,0x5,0x0,0x0,0x33,0x5,0x0,0x0,0x3a,0x5,0x0,
+0x0,0x3e,0x5,0x0,0x0,0x44,0x5,0x0,0x0,0x4b,0x5,0x0,0x0,0x55,0x5,0x0,
+0x0,0x5b,0x5,0x0,0x0,0xc9,0x3,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x3f,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0xa1,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0xe1,0x4,0x0,
+0x0,0xe9,0x4,0x0,0x0,0xed,0x4,0x0,0x0,0xf3,0x4,0x0,0x0,0xf6,0x4,0x0,
+0x0,0xfb,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x5,0x5,0x0,0x0,0xb,0x5,0x0,
+0x0,0x11,0x5,0x0,0x0,0x15,0x5,0x0,0x0,0x1a,0x5,0x0,0x0,0x1f,0x5,0x0,
+0x0,0x23,0x5,0x0,0x0,0x2a,0x5,0x0,0x0,0x30,0x5,0x0,0x0,0x35,0x5,0x0,
+0x0,0x3c,0x5,0x0,0x0,0x40,0x5,0x0,0x0,0x45,0x5,0x0,0x0,0x49,0x5,0x0,
+0x0,0x4e,0x5,0x0,0x0,0x54,0x5,0x0,0x0,0x59,0x5,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0xa0,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,
+0x0,0xe4,0x4,0x0,0x0,0xe5,0x4,0x0,0x0,0xea,0x4,0x0,0x0,0xef,0x4,0x0,
+0x0,0xf4,0x4,0x0,0x0,0xf9,0x4,0x0,0x0,0xfe,0x4,0x0,0x0,0x4,0x5,0x0,
+0x0,0x9,0x5,0x0,0x0,0xe,0x5,0x0,0x0,0x13,0x5,0x0,0x0,0x18,0x5,0x0,
+0x0,0x1d,0x5,0x0,0x0,0x22,0x5,0x0,0x0,0x28,0x5,0x0,0x0,0x2d,0x5,0x0,
+0x0,0x32,0x5,0x0,0x0,0x37,0x5,0x0,0x0,0x3d,0x5,0x0,0x0,0x42,0x5,0x0,
+0x0,0x47,0x5,0x0,0x0,0x4c,0x5,0x0,0x0,0x51,0x5,0x0,0x0,0x56,0x5,0x0,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x5,0x0,0x0,0x0,0x9f,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,
+0x0,0x18,0x0,0x0,0x0,0xe3,0x4,0x0,0x0,0xe8,0x4,0x0,0x0,0xee,0x4,0x0,
+0x0,0xf1,0x4,0x0,0x0,0xf5,0x4,0x0,0x0,0xfa,0x4,0x0,0x0,0xff,0x4,0x0,
+0x0,0x8,0x5,0x0,0x0,0xa,0x5,0x0,0x0,0x12,0x5,0x0,0x0,0x17,0x5,0x0,
+0x0,0x1b,0x5,0x0,0x0,0x20,0x5,0x0,0x0,0x25,0x5,0x0,0x0,0x2b,0x5,0x0,
+0x0,0x2f,0x5,0x0,0x0,0x34,0x5,0x0,0x0,0x39,0x5,0x0,0x0,0x3f,0x5,0x0,
+0x0,0x43,0x5,0x0,0x0,0x48,0x5,0x0,0x0,0x4d,0x5,0x0,0x0,0x52,0x5,0x0,
+0x0,0x58,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0x9e,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x5d,0x5,0x0,0x0,0x57,0x5,0x0,
+0x0,0x5c,0x5,0x0,0x0,0x38,0x5,0x0,0x0,0x5f,0x5,0x0,0x0,0x5e,0x5,0x0,
+0x0,0x60,0x5,0x0,0x0,0x3,0x5,0x0,0x0,0x61,0x5,0x0,0x0,0x27,0x5,0x0,
+0x0,0x62,0x5,0x0,0x0,0x63,0x5,0x0,0x0,0x64,0x5,0x0,0x0,0x65,0x5,0x0,
+0x0,0x66,0x5,0x0,0x0,0x67,0x5,0x0,0x0,0x68,0x5,0x0,0x0,0x69,0x5,0x0,
+0x0,0x6a,0x5,0x0,0x0,0x6b,0x5,0x0,0x0,0x6c,0x5,0x0,0x0,0x6d,0x5,0x0,
+0x0,0x6e,0x5,0x0,0x0,0x6f,0x5,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0x9d,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x6e,0x94,0x7d,0x0,0x17,0x0,0x0,0x0,0x91,0x0,0x0,
+0x0,0xa1,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0xd7,0x0,0x0,0x0,0xfc,0x0,0x0,
+0x0,0x1f,0x1,0x0,0x0,0x24,0x1,0x0,0x0,0x2d,0x1,0x0,0x0,0x4b,0x1,0x0,
+0x0,0x53,0x1,0x0,0x0,0x60,0x1,0x0,0x0,0x6a,0x2,0x0,0x0,0x73,0x2,0x0,
+0x0,0x8c,0x2,0x0,0x0,0x92,0x2,0x0,0x0,0xa1,0x2,0x0,0x0,0xab,0x2,0x0,
+0x0,0xba,0x2,0x0,0x0,0xc4,0x2,0x0,0x0,0xcf,0x2,0x0,0x0,0xd9,0x2,0x0,
+0x0,0xea,0x2,0x0,0x0,0xf6,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0x9c,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0x2f,0xe,0x83,0x0,0x18,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x98,0x0,0x0,
+0x0,0xb1,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0x1b,0x1,0x0,
+0x0,0x23,0x1,0x0,0x0,0x2a,0x1,0x0,0x0,0x3f,0x1,0x0,0x0,0x51,0x1,0x0,
+0x0,0x5e,0x1,0x0,0x0,0xb7,0x1,0x0,0x0,0x86,0x2,0x0,0x0,0x93,0x2,0x0,
+0x0,0xa0,0x2,0x0,0x0,0xae,0x2,0x0,0x0,0xb9,0x2,0x0,0x0,0xc3,0x2,0x0,
+0x0,0xd8,0x2,0x0,0x0,0xe2,0x2,0x0,0x0,0xee,0x2,0x0,0x0,0xfd,0x2,0x0,
+0x0,0x1b,0x0,0x0,0x0,0xce,0x2,0x0,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0x3f,0x75,0x5,0x0,0x44,0x7a,0x5,0x0,0x5,0x0,0x0,0x0,0x9b,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x92,0x0,0x0,
+0x0,0x9c,0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xe9,0x0,0x0,
+0x0,0x1e,0x1,0x0,0x0,0x26,0x1,0x0,0x0,0x2e,0x1,0x0,0x0,0x47,0x1,0x0,
+0x0,0x56,0x1,0x0,0x0,0x62,0x1,0x0,0x0,0x69,0x2,0x0,0x0,0x72,0x2,0x0,
+0x0,0x8a,0x2,0x0,0x0,0x96,0x2,0x0,0x0,0xa3,0x2,0x0,0x0,0xb1,0x2,0x0,
+0x0,0xbb,0x2,0x0,0x0,0xc6,0x2,0x0,0x0,0xd1,0x2,0x0,0x0,0xdc,0x2,0x0,
+0x0,0xeb,0x2,0x0,0x0,0xf4,0x2,0x0,0x0,0xff,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0x9a,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x6e,0x94,0x7d,0x0,0x17,0x0,0x0,
+0x0,0x90,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0xc9,0x0,0x0,
+0x0,0xed,0x0,0x0,0x0,0x1d,0x1,0x0,0x0,0x25,0x1,0x0,0x0,0x2b,0x1,0x0,
+0x0,0x42,0x1,0x0,0x0,0x58,0x1,0x0,0x0,0x63,0x1,0x0,0x0,0x6b,0x2,0x0,
+0x0,0x77,0x2,0x0,0x0,0x88,0x2,0x0,0x0,0x98,0x2,0x0,0x0,0xa6,0x2,0x0,
+0x0,0xaf,0x2,0x0,0x0,0xbe,0x2,0x0,0x0,0xc8,0x2,0x0,0x0,0xd4,0x2,0x0,
+0x0,0xdd,0x2,0x0,0x0,0xe7,0x2,0x0,0x0,0xf8,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0x99,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0x30,0xa,0x83,0x0,0x18,0x0,0x0,0x0,0x93,0x0,0x0,
+0x0,0xa4,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0xff,0x0,0x0,
+0x0,0x20,0x1,0x0,0x0,0x27,0x1,0x0,0x0,0x33,0x1,0x0,0x0,0x4c,0x1,0x0,
+0x0,0x5b,0x1,0x0,0x0,0x6a,0x1,0x0,0x0,0x6c,0x2,0x0,0x0,0x7b,0x2,0x0,
+0x0,0x8e,0x2,0x0,0x0,0x99,0x2,0x0,0x0,0xa7,0x2,0x0,0x0,0xb2,0x2,0x0,
+0x0,0xbf,0x2,0x0,0x0,0xc5,0x2,0x0,0x0,0xd2,0x2,0x0,0x0,0xdb,0x2,0x0,
+0x0,0xe9,0x2,0x0,0x0,0xf1,0x2,0x0,0x0,0xfe,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,
+0x0,0x98,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x6e,0x94,0x7d,0x0,0x17,0x0,0x0,
+0x0,0x94,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0xdb,0x0,0x0,
+0x0,0xa,0x1,0x0,0x0,0x21,0x1,0x0,0x0,0x28,0x1,0x0,0x0,0x36,0x1,0x0,
+0x0,0x4e,0x1,0x0,0x0,0x59,0x1,0x0,0x0,0x64,0x1,0x0,0x0,0x6d,0x2,0x0,
+0x0,0x81,0x2,0x0,0x0,0x8f,0x2,0x0,0x0,0x9c,0x2,0x0,0x0,0xa9,0x2,0x0,
+0x0,0xb4,0x2,0x0,0x0,0xc0,0x2,0x0,0x0,0xc9,0x2,0x0,0x0,0xd5,0x2,0x0,
+0x0,0xde,0x2,0x0,0x0,0xe6,0x2,0x0,0x0,0xef,0x2,0x0,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,
+0x0,0xc2,0x75,0x5,0x0,0xc2,0x75,0x5,0x0,0x5,0x0,0x0,0x0,0x97,0xff,0xff,
+0xff,0x2,0x0,0x5,0x0,0x9,0xea,0x84,0x3,0x7,0x0,0x0,0x0,0xbd,0xff,0xff,
+0xff,0x9d,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x9b,0xff,0xff,0xff,0x9a,0xff,0xff,
+0xff,0x99,0xff,0xff,0xff,0x98,0xff,0xff,0xff,0x30,0xa,0x83,0x0,0x6e,0x94,0x7d,
+0x0,0x2f,0xe,0x83,0x0,0x30,0xa,0x83,0x0,0x6e,0x94,0x7d,0x0,0x30,0xa,0x83,
+0x0,0x6e,0x94,0x7d,0x0,0x5,0x0,0x0,0x0,0x96,0xff,0xff,0xff,0x4,0x0,0x5,
+0x0,0x22,0xfa,0xeb,0x14,0x6,0x0,0x0,0x0,0xd6,0xff,0xff,0xff,0xce,0xff,0xff,
+0xff,0xc6,0xff,0xff,0xff,0xbe,0xff,0xff,0xff,0x97,0xff,0xff,0xff,0x6c,0xff,0xff,
+0xff,0xcd,0x46,0x95,0x3,0x50,0x47,0x95,0x3,0x2c,0x41,0x95,0x3,0x20,0x3d,0x12,
+0x3,0x9,0xea,0x84,0x3,0xb0,0x3,0x95,0x3,0x5,0x0,0x0,0x0,0x95,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x87,0x1,0x0,
+0x0,0x6e,0x1,0x0,0x0,0x88,0x1,0x0,0x0,0x94,0x1,0x0,0x0,0x6f,0x1,0x0,
+0x0,0x9a,0x1,0x0,0x0,0x99,0x1,0x0,0x0,0x72,0x1,0x0,0x0,0x6d,0x1,0x0,
+0x0,0x96,0x1,0x0,0x0,0x78,0x1,0x0,0x0,0x83,0x1,0x0,0x0,0x8e,0x1,0x0,
+0x0,0x80,0x1,0x0,0x0,0x7c,0x1,0x0,0x0,0x79,0x1,0x0,0x0,0x8c,0x1,0x0,
+0x0,0x74,0x1,0x0,0x0,0x8d,0x1,0x0,0x0,0x7e,0x1,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0x94,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,
+0x0,0xbe,0x1,0x0,0x0,0xba,0x1,0x0,0x0,0xb0,0x1,0x0,0x0,0x9f,0x1,0x0,
+0x0,0xa5,0x1,0x0,0x0,0xad,0x1,0x0,0x0,0xb3,0x1,0x0,0x0,0xb9,0x1,0x0,
+0x0,0xab,0x1,0x0,0x0,0xb8,0x1,0x0,0x0,0xfa,0x0,0x0,0x0,0xa4,0x1,0x0,
+0x0,0xaf,0x1,0x0,0x0,0xa8,0x1,0x0,0x0,0xb5,0x1,0x0,0x0,0xb4,0x1,0x0,
+0x0,0xa7,0x1,0x0,0x0,0xa2,0x1,0x0,0x0,0xc1,0x1,0x0,0x0,0xb1,0x1,0x0,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0x5,0x0,0x0,0x0,0x93,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0x76,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0x6b,0x3,0x0,0x0,0x6,0x4,0x0,0x0,0x85,0x3,0x0,
+0x0,0xd2,0x3,0x0,0x0,0xdd,0x3,0x0,0x0,0xe5,0x3,0x0,0x0,0x7b,0x3,0x0,
+0x0,0xc1,0x3,0x0,0x0,0xd8,0x3,0x0,0x0,0x7d,0x3,0x0,0x0,0x74,0x3,0x0,
+0x0,0x8d,0x3,0x0,0x0,0x88,0x3,0x0,0x0,0x82,0x3,0x0,0x0,0xaf,0x3,0x0,
+0x0,0xfe,0x3,0x0,0x0,0x9d,0x3,0x0,0x0,0xf8,0x3,0x0,0x0,0x7f,0x3,0x0,
+0x0,0xc8,0x3,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0x81,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x92,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0xc2,0x1,0x0,
+0x0,0x5,0x2,0x0,0x0,0xc7,0x2,0x0,0x0,0x9e,0x2,0x0,0x0,0xef,0x1,0x0,
+0x0,0x1a,0x2,0x0,0x0,0x3e,0x2,0x0,0x0,0x54,0x2,0x0,0x0,0xcb,0x1,0x0,
+0x0,0x7e,0x2,0x0,0x0,0x74,0x2,0x0,0x0,0xe6,0x1,0x0,0x0,0xd0,0x1,0x0,
+0x0,0xd4,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0xf9,0x1,0x0,0x0,0xde,0x1,0x0,
+0x0,0xc3,0x1,0x0,0x0,0xb0,0x2,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x91,0xff,0xff,
+0xff,0x2,0x0,0x5,0x0,0xda,0x32,0x23,0x1,0x4,0x0,0x0,0x0,0x95,0xff,0xff,
+0xff,0x94,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0x92,0xff,0xff,0xff,0xcc,0xcc,0x48,
+0x0,0xcc,0xcc,0x48,0x0,0x76,0xcc,0x48,0x0,0xcc,0xcc,0x48,0x0,0x5,0x0,0x0,
+0x0,0x90,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,
+0x0,0x30,0x4,0x0,0x0,0xc9,0x4,0x0,0x0,0xe9,0x3,0x0,0x0,0xa8,0x4,0x0,
+0x0,0x9,0x4,0x0,0x0,0x12,0x4,0x0,0x0,0xf6,0x3,0x0,0x0,0xd,0x4,0x0,
+0x0,0x95,0x4,0x0,0x0,0xfc,0x3,0x0,0x0,0x66,0x4,0x0,0x0,0xcd,0x4,0x0,
+0x0,0x7b,0x4,0x0,0x0,0xbb,0x4,0x0,0x0,0xcf,0x4,0x0,0x0,0xbe,0x4,0x0,
+0x0,0xcb,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x18,0x4,0x0,0x0,0x4a,0x4,0x0,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0x5,0x0,0x0,0x0,0x8f,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x6f,0x3,0x0,0x0,0x93,0x3,0x0,
+0x0,0xa2,0x3,0x0,0x0,0x89,0x3,0x0,0x0,0xd7,0x3,0x0,0x0,0x8e,0x3,0x0,
+0x0,0xd3,0x3,0x0,0x0,0xdc,0x3,0x0,0x0,0x86,0x3,0x0,0x0,0x7c,0x3,0x0,
+0x0,0x81,0x3,0x0,0x0,0x7a,0x3,0x0,0x0,0x84,0x3,0x0,0x0,0xe7,0x3,0x0,
+0x0,0xab,0x3,0x0,0x0,0x65,0x3,0x0,0x0,0xc7,0x3,0x0,0x0,0x0,0x4,0x0,
+0x0,0xf9,0x3,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x8e,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0xc,0x2,0x0,
+0x0,0x8b,0x2,0x0,0x0,0x6e,0x2,0x0,0x0,0xe1,0x1,0x0,0x0,0xf6,0x1,0x0,
+0x0,0xe4,0x2,0x0,0x0,0xd7,0x1,0x0,0x0,0x82,0x2,0x0,0x0,0xd3,0x2,0x0,
+0x0,0x78,0x2,0x0,0x0,0x34,0x3,0x0,0x0,0x9a,0x2,0x0,0x0,0x31,0x2,0x0,
+0x0,0xb7,0x2,0x0,0x0,0xee,0x1,0x0,0x0,0xff,0x1,0x0,0x0,0xf2,0x2,0x0,
+0x0,0xa8,0x2,0x0,0x0,0xea,0x1,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x8d,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x83,0x2,0x0,
+0x0,0x50,0x2,0x0,0x0,0x71,0x2,0x0,0x0,0xac,0x2,0x0,0x0,0xd,0x2,0x0,
+0x0,0x9b,0x2,0x0,0x0,0xe5,0x1,0x0,0x0,0xe8,0x2,0x0,0x0,0x3d,0x2,0x0,
+0x0,0xf2,0x1,0x0,0x0,0xec,0x1,0x0,0x0,0x53,0x3,0x0,0x0,0x1,0x2,0x0,
+0x0,0xd6,0x2,0x0,0x0,0x4b,0x3,0x0,0x0,0xdd,0x1,0x0,0x0,0x7c,0x2,0x0,
+0x0,0xbc,0x2,0x0,0x0,0xf9,0x2,0x0,0x0,0x8d,0x2,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0x8c,0xff,0xff,0xff,0x2,0x0,0x5,0x0,0x30,0x33,0x23,0x1,0x4,0x0,0x0,
+0x0,0x90,0xff,0xff,0xff,0x8f,0xff,0xff,0xff,0x8e,0xff,0xff,0xff,0x8d,0xff,0xff,
+0xff,0xcc,0xcc,0x48,0x0,0xcc,0xcc,0x48,0x0,0xcc,0xcc,0x48,0x0,0xcc,0xcc,0x48,
+0x0,0x5,0x0,0x0,0x0,0x8b,0xff,0xff,0xff,0x3,0x0,0x5,0x0,0xa,0x66,0x46,
+0x2,0x2,0x0,0x0,0x0,0x91,0xff,0xff,0xff,0x8c,0xff,0xff,0xff,0xda,0x32,0x23,
+0x1,0x30,0x33,0x23,0x1,0x5,0x0,0x0,0x0,0x8a,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x89,0x2,0x0,0x0,0x79,0x2,0x0,
+0x0,0x7,0x2,0x0,0x0,0x97,0x2,0x0,0x0,0xa5,0x2,0x0,0x0,0x5d,0x3,0x0,
+0x0,0x6f,0x2,0x0,0x0,0x64,0x3,0x0,0x0,0x4f,0x3,0x0,0x0,0x11,0x2,0x0,
+0x0,0x46,0x2,0x0,0x0,0xb5,0x2,0x0,0x0,0x32,0x2,0x0,0x0,0xca,0x2,0x0,
+0x0,0x33,0x3,0x0,0x0,0xda,0x2,0x0,0x0,0x61,0x3,0x0,0x0,0x80,0x2,0x0,
+0x0,0xe5,0x2,0x0,0x0,0xf5,0x2,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x89,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0xff,0x3,0x0,
+0x0,0x44,0x4,0x0,0x0,0x90,0x3,0x0,0x0,0xd1,0x3,0x0,0x0,0xdb,0x3,0x0,
+0x0,0xb6,0x3,0x0,0x0,0xd5,0x3,0x0,0x0,0x11,0x4,0x0,0x0,0xf7,0x3,0x0,
+0x0,0x75,0x4,0x0,0x0,0x9f,0x3,0x0,0x0,0x2f,0x4,0x0,0x0,0xe1,0x3,0x0,
+0x0,0x17,0x4,0x0,0x0,0x62,0x4,0x0,0x0,0x9c,0x4,0x0,0x0,0xc,0x4,0x0,
+0x0,0xa5,0x3,0x0,0x0,0xb2,0x3,0x0,0x0,0x5,0x4,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0x88,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,
+0x0,0x9,0x2,0x0,0x0,0x76,0x2,0x0,0x0,0xf4,0x1,0x0,0x0,0xd3,0x1,0x0,
+0x0,0xfa,0x1,0x0,0x0,0x45,0x2,0x0,0x0,0x95,0x2,0x0,0x0,0xda,0x1,0x0,
+0x0,0x68,0x2,0x0,0x0,0xed,0x1,0x0,0x0,0x7f,0x2,0x0,0x0,0xb8,0x2,0x0,
+0x0,0xcb,0x2,0x0,0x0,0xcf,0x1,0x0,0x0,0x87,0x2,0x0,0x0,0xdf,0x2,0x0,
+0x0,0x2d,0x2,0x0,0x0,0xa4,0x2,0x0,0x0,0xe3,0x1,0x0,0x0,0xf3,0x2,0x0,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0x5,0x0,0x0,0x0,0x87,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0xec,0x2,0x0,0x0,0xe3,0x2,0x0,0x0,0x6e,0x3,0x0,
+0x0,0x76,0x3,0x0,0x0,0x8f,0x3,0x0,0x0,0x5a,0x3,0x0,0x0,0x87,0x3,0x0,
+0x0,0x80,0x3,0x0,0x0,0xf7,0x2,0x0,0x0,0x83,0x3,0x0,0x0,0xfc,0x2,0x0,
+0x0,0xd0,0x2,0x0,0x0,0x63,0x3,0x0,0x0,0xa2,0x2,0x0,0x0,0x4d,0x3,0x0,
+0x0,0xb3,0x2,0x0,0x0,0x60,0x3,0x0,0x0,0x7e,0x3,0x0,0x0,0x8b,0x3,0x0,
+0x0,0xc2,0x2,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x86,0xff,0xff,0xff,0x2,0x0,0x5,
+0x0,0x30,0x33,0x23,0x1,0x4,0x0,0x0,0x0,0x8a,0xff,0xff,0xff,0x89,0xff,0xff,
+0xff,0x88,0xff,0xff,0xff,0x87,0xff,0xff,0xff,0xcc,0xcc,0x48,0x0,0xcc,0xcc,0x48,
+0x0,0xcc,0xcc,0x48,0x0,0xcc,0xcc,0x48,0x0,0x5,0x0,0x0,0x0,0x85,0xff,0xff,
+0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0x4c,0x3,0x0,
+0x0,0x1c,0x2,0x0,0x0,0xcd,0x2,0x0,0x0,0x58,0x2,0x0,0x0,0x94,0x2,0x0,
+0x0,0x62,0x3,0x0,0x0,0xe1,0x2,0x0,0x0,0x84,0x2,0x0,0x0,0x40,0x2,0x0,
+0x0,0x7d,0x2,0x0,0x0,0xbd,0x2,0x0,0x0,0x5e,0x3,0x0,0x0,0xf0,0x2,0x0,
+0x0,0x75,0x3,0x0,0x0,0x9f,0x2,0x0,0x0,0xad,0x2,0x0,0x0,0xfb,0x2,0x0,
+0x0,0x51,0x3,0x0,0x0,0x75,0x2,0x0,0x0,0x6d,0x3,0x0,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,
+0x0,0x84,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,
+0x0,0x8,0x4,0x0,0x0,0x4c,0x4,0x0,0x0,0x2,0x4,0x0,0x0,0xb3,0x3,0x0,
+0x0,0xe,0x4,0x0,0x0,0xa9,0x3,0x0,0x0,0x3e,0x4,0x0,0x0,0x8c,0x3,0x0,
+0x0,0xfa,0x3,0x0,0x0,0xef,0x3,0x0,0x0,0x19,0x4,0x0,0x0,0xcd,0x3,0x0,
+0x0,0x13,0x4,0x0,0x0,0xa0,0x3,0x0,0x0,0xdf,0x3,0x0,0x0,0x6d,0x4,0x0,
+0x0,0x8a,0x3,0x0,0x0,0xd4,0x3,0x0,0x0,0xd9,0x3,0x0,0x0,0x91,0x3,0x0,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0x5,0x0,0x0,0x0,0x83,0xff,0xff,0xff,0x1,0x0,0x5,0x0,0xcc,0xcc,0x48,
+0x0,0x14,0x0,0x0,0x0,0xa,0x4,0x0,0x0,0xc0,0x4,0x0,0x0,0xe2,0x3,0x0,
+0x0,0xf,0x4,0x0,0x0,0xcc,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0xce,0x4,0x0,
+0x0,0xf2,0x3,0x0,0x0,0xde,0x3,0x0,0x0,0x15,0x4,0x0,0x0,0x22,0x4,0x0,
+0x0,0xca,0x4,0x0,0x0,0xfb,0x3,0x0,0x0,0xb4,0x4,0x0,0x0,0x68,0x4,0x0,
+0x0,0x3,0x4,0x0,0x0,0x53,0x4,0x0,0x0,0xae,0x4,0x0,0x0,0x97,0x4,0x0,
+0x0,0x41,0x4,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x82,0xff,0xff,0xff,0x1,0x0,0x5,
+0x0,0xcc,0xcc,0x48,0x0,0x14,0x0,0x0,0x0,0xbd,0x3,0x0,0x0,0xfd,0x3,0x0,
+0x0,0xa7,0x4,0x0,0x0,0xb1,0x4,0x0,0x0,0x16,0x4,0x0,0x0,0xb1,0x3,0x0,
+0x0,0xa6,0x3,0x0,0x0,0x2b,0x4,0x0,0x0,0xb,0x4,0x0,0x0,0x86,0x4,0x0,
+0x0,0x43,0x4,0x0,0x0,0x76,0x4,0x0,0x0,0xce,0x3,0x0,0x0,0xda,0x3,0x0,
+0x0,0x57,0x4,0x0,0x0,0x12,0x3,0x0,0x0,0xd6,0x3,0x0,0x0,0xe0,0x3,0x0,
+0x0,0xf0,0x3,0x0,0x0,0x4,0x4,0x0,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,
+0x0,0xd7,0xa3,0x3,0x0,0xd7,0xa3,0x3,0x0,0x5,0x0,0x0,0x0,0x81,0xff,0xff,
+0xff,0x2,0x0,0x5,0x0,0x30,0x33,0x23,0x1,0x4,0x0,0x0,0x0,0x85,0xff,0xff,
+0xff,0x84,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0x82,0xff,0xff,0xff,0xcc,0xcc,0x48,
+0x0,0xcc,0xcc,0x48,0x0,0xcc,0xcc,0x48,0x0,0xcc,0xcc,0x48,0x0,0x5,0x0,0x0,
+0x0,0x80,0xff,0xff,0xff,0x3,0x0,0x5,0x0,0x60,0x66,0x46,0x2,0x2,0x0,0x0,
+0x0,0x86,0xff,0xff,0xff,0x81,0xff,0xff,0xff,0x30,0x33,0x23,0x1,0x30,0x33,0x23,
+0x1,0x5,0x0,0x0,0x0,0x7f,0xff,0xff,0xff,0x4,0x0,0x5,0x0,0x6a,0xcc,0x8c,
+0x4,0x2,0x0,0x0,0x0,0x8b,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0xa,0x66,0x46,
+0x2,0x60,0x66,0x46,0x2,0x5,0x0,0x0,0x0,0x7e,0xff,0xff,0xff,0x6,0x0,0x5,
+0x0,0x8c,0xc6,0x78,0x19,0x2,0x0,0x0,0x0,0x96,0xff,0xff,0xff,0x7f,0xff,0xff,
+0xff,0x22,0xfa,0xeb,0x14,0x6a,0xcc,0x8c,0x4,0x4,0x0,0x0,0x0,0x7d,0xff,0xff,
+0xff,0x1,0x0,0x4,0x0,0xd0,0x10,0x83,0x0,0x18,0x0,0x0,0x0,0x7d,0x0,0x0,
+0x0,0x46,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0x15,0x1,0x0,
+0x0,0xb4,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x8,0x1,0x0,0x0,0x7b,0x1,0x0,
+0x0,0x7f,0x0,0x0,0x0,0xdd,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0xcd,0x0,0x0,
+0x0,0xd3,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0xe5,0x0,0x0,
+0x0,0x90,0x1,0x0,0x0,0x61,0x1,0x0,0x0,0x4e,0x0,0x0,0x0,0x7d,0x1,0x0,
+0x0,0x55,0x0,0x0,0x0,0x99,0x0,0x0,0x0,0xf2,0x0,0x0,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0xbe,0x7e,0x5,
+0x0,0x12,0x0,0x1,0x0,0xbe,0x7e,0x5,0x0,0x12,0x0,0x1,0x0,0x0,0x0,0x0,
+0x0,0x4,0x0,0x0,0x0,0x7b,0xff,0xff,0xff,0x1,0x0,0x4,0x0,0xd0,0x10,0x83,
+0x0,0x18,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x3d,0x0,0x0,
+0x0,0xa5,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0xb4,0x0,0x0,0x0,0xbd,0x0,0x0,
+0x0,0x8,0x1,0x0,0x0,0x7b,0x1,0x0,0x0,0x7f,0x0,0x0,0x0,0xdd,0x0,0x0,
+0x0,0xbb,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0xf0,0x0,0x0,
+0x0,0x5e,0x0,0x0,0x0,0xe5,0x0,0x0,0x0,0x90,0x1,0x0,0x0,0x61,0x1,0x0,
+0x0,0x4e,0x0,0x0,0x0,0x7d,0x1,0x0,0x0,0x55,0x0,0x0,0x0,0x99,0x0,0x0,
+0x0,0xf2,0x0,0x0,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0xbe,0x7e,0x5,0x0,0x12,0x0,0x1,0x0,0xbe,0x7e,0x5,
+0x0,0x12,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x79,0xff,0xff,
+0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,0x0,0xdc,0x1,0x0,
+0x0,0xce,0x1,0x0,0x0,0x93,0x1,0x0,0x0,0x4,0x3,0x0,0x0,0x9,0x3,0x0,
+0x0,0x7a,0x2,0x0,0x0,0x8,0x3,0x0,0x0,0x6,0x3,0x0,0x0,0x7,0x3,0x0,
+0x0,0x9b,0x1,0x0,0x0,0xe8,0x1,0x0,0x0,0x5,0x3,0x0,0x0,0x1,0x3,0x0,
+0x0,0xa,0x3,0x0,0x0,0xfe,0x1,0x0,0x0,0xc7,0x1,0x0,0x0,0x4,0x2,0x0,
+0x0,0x2,0x3,0x0,0x0,0x15,0x2,0x0,0x0,0xfd,0x1,0x0,0x0,0x0,0x3,0x0,
+0x0,0x22,0x2,0x0,0x0,0xac,0x1,0x0,0x0,0x3,0x3,0x0,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,
+0x0,0x78,0xff,0xff,0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,
+0x0,0xdc,0x1,0x0,0x0,0xce,0x1,0x0,0x0,0x93,0x1,0x0,0x0,0x4,0x3,0x0,
+0x0,0x9,0x3,0x0,0x0,0x7a,0x2,0x0,0x0,0x8,0x3,0x0,0x0,0x6,0x3,0x0,
+0x0,0x7,0x3,0x0,0x0,0x9b,0x1,0x0,0x0,0xe8,0x1,0x0,0x0,0x5,0x3,0x0,
+0x0,0x1,0x3,0x0,0x0,0xa,0x3,0x0,0x0,0xfe,0x1,0x0,0x0,0xc7,0x1,0x0,
+0x0,0x4,0x2,0x0,0x0,0x2,0x3,0x0,0x0,0x15,0x2,0x0,0x0,0xfd,0x1,0x0,
+0x0,0x0,0x3,0x0,0x0,0x22,0x2,0x0,0x0,0xac,0x1,0x0,0x0,0x3,0x3,0x0,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x4,0x0,0x0,0x0,0x77,0xff,0xff,0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,
+0x0,0x18,0x0,0x0,0x0,0xb,0x3,0x0,0x0,0x23,0x3,0x0,0x0,0x1f,0x3,0x0,
+0x0,0x1b,0x3,0x0,0x0,0x1e,0x3,0x0,0x0,0x19,0x3,0x0,0x0,0x17,0x3,0x0,
+0x0,0x1d,0x3,0x0,0x0,0xd,0x3,0x0,0x0,0x13,0x3,0x0,0x0,0x15,0x3,0x0,
+0x0,0x18,0x3,0x0,0x0,0x14,0x3,0x0,0x0,0x1c,0x3,0x0,0x0,0x22,0x3,0x0,
+0x0,0xe,0x3,0x0,0x0,0xc,0x3,0x0,0x0,0x16,0x3,0x0,0x0,0x1a,0x3,0x0,
+0x0,0x20,0x3,0x0,0x0,0x11,0x3,0x0,0x0,0x10,0x3,0x0,0x0,0x21,0x3,0x0,
+0x0,0xf,0x3,0x0,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,0x0,0x76,0xff,0xff,0xff,0x1,0x0,0x4,
+0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,0x0,0xb,0x3,0x0,0x0,0x23,0x3,0x0,
+0x0,0x1f,0x3,0x0,0x0,0x1b,0x3,0x0,0x0,0x1e,0x3,0x0,0x0,0x19,0x3,0x0,
+0x0,0x17,0x3,0x0,0x0,0x1d,0x3,0x0,0x0,0xd,0x3,0x0,0x0,0x13,0x3,0x0,
+0x0,0x15,0x3,0x0,0x0,0x18,0x3,0x0,0x0,0x14,0x3,0x0,0x0,0x1c,0x3,0x0,
+0x0,0x22,0x3,0x0,0x0,0xe,0x3,0x0,0x0,0xc,0x3,0x0,0x0,0x16,0x3,0x0,
+0x0,0x1a,0x3,0x0,0x0,0x20,0x3,0x0,0x0,0x11,0x3,0x0,0x0,0x10,0x3,0x0,
+0x0,0x21,0x3,0x0,0x0,0xf,0x3,0x0,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,0x0,0x75,0xff,0xff,
+0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,0x0,0x28,0x3,0x0,
+0x0,0x27,0x3,0x0,0x0,0x29,0x3,0x0,0x0,0x2b,0x3,0x0,0x0,0x26,0x3,0x0,
+0x0,0x35,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x3a,0x3,0x0,0x0,0x25,0x3,0x0,
+0x0,0x24,0x3,0x0,0x0,0x32,0x3,0x0,0x0,0x3b,0x3,0x0,0x0,0x2f,0x3,0x0,
+0x0,0x39,0x3,0x0,0x0,0x2e,0x3,0x0,0x0,0x37,0x3,0x0,0x0,0x3c,0x3,0x0,
+0x0,0x2c,0x3,0x0,0x0,0x36,0x3,0x0,0x0,0x2d,0x3,0x0,0x0,0x3d,0x3,0x0,
+0x0,0x38,0x3,0x0,0x0,0x31,0x3,0x0,0x0,0x2a,0x3,0x0,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,
+0x0,0x74,0xff,0xff,0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,
+0x0,0x28,0x3,0x0,0x0,0x27,0x3,0x0,0x0,0x29,0x3,0x0,0x0,0x2b,0x3,0x0,
+0x0,0x26,0x3,0x0,0x0,0x35,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x3a,0x3,0x0,
+0x0,0x25,0x3,0x0,0x0,0x24,0x3,0x0,0x0,0x32,0x3,0x0,0x0,0x3b,0x3,0x0,
+0x0,0x2f,0x3,0x0,0x0,0x39,0x3,0x0,0x0,0x2e,0x3,0x0,0x0,0x37,0x3,0x0,
+0x0,0x3c,0x3,0x0,0x0,0x2c,0x3,0x0,0x0,0x36,0x3,0x0,0x0,0x2d,0x3,0x0,
+0x0,0x3d,0x3,0x0,0x0,0x38,0x3,0x0,0x0,0x31,0x3,0x0,0x0,0x2a,0x3,0x0,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x4,0x0,0x0,0x0,0x73,0xff,0xff,0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,
+0x0,0x18,0x0,0x0,0x0,0x5c,0x3,0x0,0x0,0x5b,0x3,0x0,0x0,0x3f,0x3,0x0,
+0x0,0x44,0x3,0x0,0x0,0x45,0x3,0x0,0x0,0x55,0x3,0x0,0x0,0x5f,0x3,0x0,
+0x0,0x50,0x3,0x0,0x0,0x4a,0x3,0x0,0x0,0x47,0x3,0x0,0x0,0x41,0x3,0x0,
+0x0,0x40,0x3,0x0,0x0,0x57,0x3,0x0,0x0,0x49,0x3,0x0,0x0,0x42,0x3,0x0,
+0x0,0x43,0x3,0x0,0x0,0x46,0x3,0x0,0x0,0x54,0x3,0x0,0x0,0x4e,0x3,0x0,
+0x0,0x52,0x3,0x0,0x0,0x48,0x3,0x0,0x0,0x56,0x3,0x0,0x0,0x58,0x3,0x0,
+0x0,0x59,0x3,0x0,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,0x0,0x72,0xff,0xff,0xff,0x1,0x0,0x4,
+0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,0x0,0x5c,0x3,0x0,0x0,0x5b,0x3,0x0,
+0x0,0x3f,0x3,0x0,0x0,0x44,0x3,0x0,0x0,0x45,0x3,0x0,0x0,0x55,0x3,0x0,
+0x0,0x5f,0x3,0x0,0x0,0x50,0x3,0x0,0x0,0x4a,0x3,0x0,0x0,0x47,0x3,0x0,
+0x0,0x41,0x3,0x0,0x0,0x40,0x3,0x0,0x0,0x57,0x3,0x0,0x0,0x49,0x3,0x0,
+0x0,0x42,0x3,0x0,0x0,0x43,0x3,0x0,0x0,0x46,0x3,0x0,0x0,0x54,0x3,0x0,
+0x0,0x4e,0x3,0x0,0x0,0x52,0x3,0x0,0x0,0x48,0x3,0x0,0x0,0x56,0x3,0x0,
+0x0,0x58,0x3,0x0,0x0,0x59,0x3,0x0,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,0x0,0x71,0xff,0xff,
+0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,0x0,0xc4,0x3,0x0,
+0x0,0xc6,0x3,0x0,0x0,0xc3,0x3,0x0,0x0,0xa7,0x3,0x0,0x0,0xac,0x3,0x0,
+0x0,0xad,0x3,0x0,0x0,0xbf,0x3,0x0,0x0,0xb7,0x3,0x0,0x0,0xae,0x3,0x0,
+0x0,0xa4,0x3,0x0,0x0,0xbb,0x3,0x0,0x0,0xc5,0x3,0x0,0x0,0xbe,0x3,0x0,
+0x0,0xc2,0x3,0x0,0x0,0xb0,0x3,0x0,0x0,0xb5,0x3,0x0,0x0,0xa8,0x3,0x0,
+0x0,0xa3,0x3,0x0,0x0,0xaa,0x3,0x0,0x0,0xb4,0x3,0x0,0x0,0xc0,0x3,0x0,
+0x0,0xb9,0x3,0x0,0x0,0xba,0x3,0x0,0x0,0xb8,0x3,0x0,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,
+0x0,0x70,0xff,0xff,0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,
+0x0,0xc4,0x3,0x0,0x0,0xc6,0x3,0x0,0x0,0xc3,0x3,0x0,0x0,0xa7,0x3,0x0,
+0x0,0xac,0x3,0x0,0x0,0xad,0x3,0x0,0x0,0xbf,0x3,0x0,0x0,0xb7,0x3,0x0,
+0x0,0xae,0x3,0x0,0x0,0xa4,0x3,0x0,0x0,0xbb,0x3,0x0,0x0,0xc5,0x3,0x0,
+0x0,0xbe,0x3,0x0,0x0,0xc2,0x3,0x0,0x0,0xb0,0x3,0x0,0x0,0xb5,0x3,0x0,
+0x0,0xa8,0x3,0x0,0x0,0xa3,0x3,0x0,0x0,0xaa,0x3,0x0,0x0,0xb4,0x3,0x0,
+0x0,0xc0,0x3,0x0,0x0,0xb9,0x3,0x0,0x0,0xba,0x3,0x0,0x0,0xb8,0x3,0x0,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x4,0x0,0x0,0x0,0x6f,0xff,0xff,0xff,0x1,0x0,0x4,0x0,0xd0,0xfd,0x82,
+0x0,0x18,0x0,0x0,0x0,0x66,0x3,0x0,0x0,0x69,0x3,0x0,0x0,0x71,0x3,0x0,
+0x0,0x79,0x3,0x0,0x0,0x97,0x3,0x0,0x0,0x95,0x3,0x0,0x0,0x94,0x3,0x0,
+0x0,0x9a,0x3,0x0,0x0,0x70,0x3,0x0,0x0,0x68,0x3,0x0,0x0,0x6c,0x3,0x0,
+0x0,0x9b,0x3,0x0,0x0,0xa1,0x3,0x0,0x0,0x92,0x3,0x0,0x0,0x9c,0x3,0x0,
+0x0,0x96,0x3,0x0,0x0,0x73,0x3,0x0,0x0,0x9e,0x3,0x0,0x0,0x77,0x3,0x0,
+0x0,0x99,0x3,0x0,0x0,0x72,0x3,0x0,0x0,0x98,0x3,0x0,0x0,0x67,0x3,0x0,
+0x0,0x78,0x3,0x0,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,
+0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,0x0,0x6e,0xff,0xff,0xff,0x1,0x0,0x4,
+0x0,0xd0,0xfd,0x82,0x0,0x18,0x0,0x0,0x0,0x66,0x3,0x0,0x0,0x69,0x3,0x0,
+0x0,0x71,0x3,0x0,0x0,0x79,0x3,0x0,0x0,0x97,0x3,0x0,0x0,0x95,0x3,0x0,
+0x0,0x94,0x3,0x0,0x0,0x9a,0x3,0x0,0x0,0x70,0x3,0x0,0x0,0x68,0x3,0x0,
+0x0,0x6c,0x3,0x0,0x0,0x9b,0x3,0x0,0x0,0xa1,0x3,0x0,0x0,0x92,0x3,0x0,
+0x0,0x9c,0x3,0x0,0x0,0x96,0x3,0x0,0x0,0x73,0x3,0x0,0x0,0x9e,0x3,0x0,
+0x0,0x77,0x3,0x0,0x0,0x99,0x3,0x0,0x0,0x72,0x3,0x0,0x0,0x98,0x3,0x0,
+0x0,0x67,0x3,0x0,0x0,0x78,0x3,0x0,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,
+0x0,0x3e,0x75,0x5,0x0,0x0,0x0,0x1,0x0,0x4,0x0,0x0,0x0,0x6d,0xff,0xff,
+0xff,0x2,0x0,0x4,0x0,0xb0,0x3,0x95,0x3,0x7,0x0,0x0,0x0,0x73,0xff,0xff,
+0xff,0x6f,0xff,0xff,0xff,0x7d,0xff,0xff,0xff,0x77,0xff,0xff,0xff,0x75,0xff,0xff,
+0xff,0x79,0xff,0xff,0xff,0x71,0xff,0xff,0xff,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,
+0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,0x0,0xd0,0x10,0x83,0x0,0x5,0x0,0x1,
+0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,
+0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,
+0x0,0x4,0x0,0x0,0x0,0x6c,0xff,0xff,0xff,0x2,0x0,0x4,0x0,0xb0,0x3,0x95,
+0x3,0x7,0x0,0x0,0x0,0x72,0xff,0xff,0xff,0x6e,0xff,0xff,0xff,0x7b,0xff,0xff,
+0xff,0x76,0xff,0xff,0xff,0x74,0xff,0xff,0xff,0x78,0xff,0xff,0xff,0x70,0xff,0xff,
+0xff,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,
+0x0,0xd0,0x10,0x83,0x0,0x5,0x0,0x1,0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,
+0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,
+0x0,0xd0,0xfd,0x82,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0x0,0x1,0x1,0xa,0x1,0x0,0x0,0x0,0xfe,0xff,0xff,0xff,0x0,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0x1,0x1,0x1,0xa,0x1,0x0,0x0,0x0,0xfe,0xff,0xff,0xff,0x0,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0x2,0x1,0x1,0xa,0x1,0x0,0x0,0x0,0xfe,0xff,0xff,0xff,0x0,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0x3,0x1,0x1,0xa,0x1,0x0,0x0,0x0,0xbf,0xff,0xff,0xff,0x0,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x4,0x1,0x1,0xa,0x1,0x0,0x0,0x0,0xbf,0xff,0xff,0xff,0x0,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0xff,0xff,
+0xff,0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0xfe,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x6f,0x73,0x64,0x1,0x0,0x0,0x0,
+0x4,0x0,0x0,0x0,0x68,0x6f,0x73,0x74,0x2,0x0,0x0,0x0,0x4,0x0,0x0,0x0,
+0x72,0x61,0x63,0x6b,0x3,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x69,0x70,0x73,0x65,
+0x72,0x76,0x69,0x63,0x65,0x4,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x72,0x6f,0x6f,
+0x6d,0x5,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x64,0x61,0x74,0x61,0x63,0x65,0x6e,
+0x74,0x65,0x72,0x6,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x72,0x6f,0x6f,0x74,0xa5,
+0x5,0x0,0x0,0x6c,0xff,0xff,0xff,0x8,0x0,0x0,0x0,0x52,0x41,0x32,0x31,0x7e,
+0x68,0x64,0x64,0x6d,0xff,0xff,0xff,0x4,0x0,0x0,0x0,0x52,0x41,0x32,0x31,0x6e,
+0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,
+0x65,0x33,0x35,0x33,0x39,0x32,0x7e,0x68,0x64,0x64,0x6f,0xff,0xff,0xff,0xf,0x0,
+0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x65,0x33,0x35,0x33,0x39,
+0x32,0x70,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,
+0x33,0x39,0x79,0x36,0x31,0x38,0x32,0x36,0x7e,0x68,0x64,0x64,0x71,0xff,0xff,0xff,
+0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x79,0x36,0x31,
+0x38,0x32,0x36,0x72,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,
+0x33,0x39,0x33,0x39,0x65,0x30,0x33,0x38,0x36,0x36,0x7e,0x68,0x64,0x64,0x73,0xff,
+0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x65,
+0x30,0x33,0x38,0x36,0x36,0x74,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,
+0x32,0x35,0x33,0x39,0x33,0x39,0x79,0x33,0x31,0x34,0x31,0x36,0x7e,0x68,0x64,0x64,
+0x75,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,
+0x39,0x79,0x33,0x31,0x34,0x31,0x36,0x76,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,
+0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x79,0x32,0x32,0x39,0x35,0x32,0x7e,0x68,
+0x64,0x64,0x77,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,
+0x39,0x33,0x39,0x79,0x32,0x32,0x39,0x35,0x32,0x78,0xff,0xff,0xff,0x13,0x0,0x0,
+0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x79,0x33,0x33,0x30,0x34,0x39,
+0x7e,0x68,0x64,0x64,0x79,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,
+0x35,0x33,0x39,0x33,0x39,0x79,0x33,0x33,0x30,0x34,0x39,0x7b,0xff,0xff,0xff,0x13,
+0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x66,0x34,0x34,0x39,
+0x32,0x38,0x7e,0x68,0x64,0x64,0x7d,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,
+0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x66,0x34,0x34,0x39,0x32,0x38,0x7e,0xff,0xff,
+0xff,0xb,0x0,0x0,0x0,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x7e,0x68,0x64,0x64,
+0x7f,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x30,0x35,0x31,0x33,0x2d,0x52,0x2d,0x30,
+0x30,0x36,0x30,0x7e,0x68,0x64,0x64,0x80,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x53,
+0x35,0x31,0x33,0x2d,0x41,0x2d,0x49,0x50,0x36,0x33,0x7e,0x68,0x64,0x64,0x81,0xff,
+0xff,0xff,0x8,0x0,0x0,0x0,0x42,0x41,0x31,0x32,0x7e,0x68,0x64,0x64,0x82,0xff,
+0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x62,
+0x34,0x30,0x39,0x35,0x31,0x7e,0x68,0x64,0x64,0x83,0xff,0xff,0xff,0x13,0x0,0x0,
+0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x62,0x37,0x38,0x34,0x32,0x39,
+0x7e,0x68,0x64,0x64,0x84,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,
+0x39,0x38,0x38,0x31,0x38,0x62,0x33,0x37,0x33,0x32,0x37,0x7e,0x68,0x64,0x64,0x85,
+0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,
+0x62,0x31,0x32,0x34,0x33,0x31,0x7e,0x68,0x64,0x64,0x86,0xff,0xff,0xff,0x8,0x0,
+0x0,0x0,0x42,0x41,0x31,0x31,0x7e,0x68,0x64,0x64,0x87,0xff,0xff,0xff,0x13,0x0,
+0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x73,0x34,0x30,0x31,0x38,
+0x35,0x7e,0x68,0x64,0x64,0x88,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,
+0x37,0x39,0x38,0x38,0x31,0x38,0x73,0x34,0x39,0x32,0x30,0x34,0x7e,0x68,0x64,0x64,
+0x89,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,
+0x38,0x73,0x36,0x33,0x37,0x34,0x37,0x7e,0x68,0x64,0x64,0x8a,0xff,0xff,0xff,0x13,
+0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x73,0x39,0x38,0x33,
+0x31,0x33,0x7e,0x68,0x64,0x64,0x8b,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x53,0x35,
+0x31,0x33,0x2d,0x41,0x2d,0x49,0x50,0x33,0x38,0x7e,0x68,0x64,0x64,0x8c,0xff,0xff,
+0xff,0x8,0x0,0x0,0x0,0x42,0x41,0x31,0x30,0x7e,0x68,0x64,0x64,0x8d,0xff,0xff,
+0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x76,0x35,
+0x31,0x35,0x35,0x39,0x7e,0x68,0x64,0x64,0x8e,0xff,0xff,0xff,0x13,0x0,0x0,0x0,
+0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x77,0x30,0x33,0x31,0x36,0x36,0x7e,
+0x68,0x64,0x64,0x8f,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,
+0x38,0x38,0x31,0x38,0x76,0x36,0x34,0x33,0x33,0x34,0x7e,0x68,0x64,0x64,0x90,0xff,
+0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x76,
+0x34,0x37,0x31,0x30,0x30,0x7e,0x68,0x64,0x64,0x91,0xff,0xff,0xff,0x8,0x0,0x0,
+0x0,0x42,0x41,0x30,0x39,0x7e,0x68,0x64,0x64,0x92,0xff,0xff,0xff,0x13,0x0,0x0,
+0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x62,0x30,0x34,0x33,0x32,0x32,
+0x7e,0x68,0x64,0x64,0x93,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,
+0x39,0x38,0x38,0x31,0x38,0x62,0x30,0x30,0x31,0x37,0x34,0x7e,0x68,0x64,0x64,0x94,
+0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,
+0x62,0x30,0x30,0x30,0x34,0x37,0x7e,0x68,0x64,0x64,0x95,0xff,0xff,0xff,0x13,0x0,
+0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x61,0x38,0x32,0x38,0x35,
+0x37,0x7e,0x68,0x64,0x64,0x96,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x30,0x35,0x31,
+0x33,0x2d,0x52,0x2d,0x30,0x30,0x35,0x30,0x7e,0x68,0x64,0x64,0x97,0xff,0xff,0xff,
+0x8,0x0,0x0,0x0,0x52,0x41,0x31,0x37,0x7e,0x68,0x64,0x64,0x98,0xff,0xff,0xff,
+0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x66,0x39,0x39,
+0x39,0x32,0x31,0x7e,0x68,0x64,0x64,0x99,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,
+0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x77,0x36,0x36,0x37,0x32,0x36,0x7e,0x68,
+0x64,0x64,0x9a,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,
+0x39,0x33,0x39,0x68,0x30,0x38,0x39,0x32,0x34,0x7e,0x68,0x64,0x64,0x9b,0xff,0xff,
+0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x76,0x32,
+0x30,0x32,0x30,0x35,0x7e,0x68,0x64,0x64,0x9c,0xff,0xff,0xff,0x13,0x0,0x0,0x0,
+0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x68,0x37,0x30,0x36,0x35,0x35,0x7e,
+0x68,0x64,0x64,0x9d,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,
+0x33,0x39,0x33,0x39,0x77,0x38,0x36,0x39,0x30,0x36,0x7e,0x68,0x64,0x64,0x9e,0xff,
+0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x70,
+0x34,0x34,0x36,0x32,0x33,0x9f,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,
+0x32,0x35,0x33,0x39,0x33,0x39,0x72,0x37,0x36,0x38,0x34,0x38,0xa0,0xff,0xff,0xff,
+0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6e,0x36,0x36,
+0x37,0x31,0x35,0xa1,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,
+0x33,0x39,0x33,0x39,0x72,0x31,0x39,0x39,0x37,0x32,0xa2,0xff,0xff,0xff,0xf,0x0,
+0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x72,0x39,0x38,0x36,0x36,
+0x33,0xa3,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,
+0x33,0x39,0x72,0x33,0x35,0x37,0x33,0x35,0xa5,0xff,0xff,0xff,0xf,0x0,0x0,0x0,
+0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x76,0x35,0x31,0x35,0x35,0x39,0xa6,
+0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,
+0x77,0x30,0x33,0x31,0x36,0x36,0xa7,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,
+0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x73,0x34,0x30,0x31,0x38,0x35,0xa8,0xff,0xff,
+0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x76,0x36,
+0x34,0x33,0x33,0x34,0xa9,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,
+0x39,0x38,0x38,0x31,0x38,0x62,0x34,0x30,0x39,0x35,0x31,0xaa,0xff,0xff,0xff,0xf,
+0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x62,0x30,0x34,0x33,
+0x32,0x32,0xab,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,
+0x38,0x31,0x38,0x73,0x34,0x39,0x32,0x30,0x34,0xac,0xff,0xff,0xff,0xf,0x0,0x0,
+0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x73,0x36,0x33,0x37,0x34,0x37,
+0xad,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,
+0x38,0x62,0x30,0x30,0x31,0x37,0x34,0xae,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,
+0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x62,0x37,0x38,0x34,0x32,0x39,0xaf,0xff,
+0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x62,
+0x33,0x37,0x33,0x32,0x37,0xb0,0xff,0xff,0xff,0x4,0x0,0x0,0x0,0x42,0x41,0x31,
+0x30,0xb1,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,
+0x31,0x38,0x76,0x34,0x37,0x31,0x30,0x30,0xb2,0xff,0xff,0xff,0x4,0x0,0x0,0x0,
+0x42,0x41,0x31,0x32,0xb3,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,
+0x39,0x38,0x38,0x31,0x38,0x62,0x31,0x32,0x34,0x33,0x31,0xb4,0xff,0xff,0xff,0xb,
+0x0,0x0,0x0,0x53,0x35,0x31,0x33,0x2d,0x41,0x2d,0x49,0x50,0x36,0x33,0xb5,0xff,
+0xff,0xff,0x4,0x0,0x0,0x0,0x42,0x41,0x31,0x31,0xb6,0xff,0xff,0xff,0xf,0x0,
+0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,0x31,0x38,0x73,0x39,0x38,0x33,0x31,
+0x33,0xb7,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,0x38,0x38,
+0x31,0x38,0x62,0x30,0x30,0x30,0x34,0x37,0xb8,0xff,0xff,0xff,0x4,0x0,0x0,0x0,
+0x52,0x41,0x30,0x31,0xb9,0xff,0xff,0xff,0xb,0x0,0x0,0x0,0x53,0x35,0x31,0x33,
+0x2d,0x41,0x2d,0x49,0x50,0x33,0x38,0xba,0xff,0xff,0xff,0x4,0x0,0x0,0x0,0x42,
+0x41,0x30,0x39,0xbb,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x35,0x37,0x39,
+0x38,0x38,0x31,0x38,0x61,0x38,0x32,0x38,0x35,0x37,0xbc,0xff,0xff,0xff,0xf,0x0,
+0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6e,0x34,0x34,0x35,0x36,
+0x31,0xbd,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,
+0x33,0x39,0x68,0x34,0x39,0x33,0x32,0x30,0x7e,0x68,0x64,0x64,0xbe,0xff,0xff,0xff,
+0x8,0x0,0x0,0x0,0x52,0x41,0x31,0x33,0x7e,0x68,0x64,0x64,0xbf,0xff,0xff,0xff,
+0xb,0x0,0x0,0x0,0x30,0x35,0x31,0x33,0x2d,0x52,0x2d,0x30,0x30,0x36,0x30,0xc0,
+0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,
+0x64,0x33,0x32,0x33,0x30,0x38,0x7e,0x68,0x64,0x64,0xc1,0xff,0xff,0xff,0x13,0x0,
+0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x62,0x38,0x34,0x36,0x35,
+0x39,0x7e,0x68,0x64,0x64,0xc2,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,
+0x32,0x35,0x33,0x39,0x33,0x39,0x75,0x31,0x39,0x30,0x36,0x38,0x7e,0x68,0x64,0x64,
+0xc3,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,
+0x39,0x75,0x30,0x35,0x36,0x35,0x30,0x7e,0x68,0x64,0x64,0xc4,0xff,0xff,0xff,0x13,
+0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x74,0x35,0x31,0x37,
+0x36,0x30,0x7e,0x68,0x64,0x64,0xc5,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,
+0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x76,0x31,0x35,0x31,0x37,0x34,0x7e,0x68,0x64,
+0x64,0xc6,0xff,0xff,0xff,0x8,0x0,0x0,0x0,0x52,0x41,0x30,0x39,0x7e,0x68,0x64,
+0x64,0xc7,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,
+0x33,0x39,0x68,0x39,0x34,0x31,0x34,0x34,0x7e,0x68,0x64,0x64,0xc8,0xff,0xff,0xff,
+0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x74,0x31,0x31,
+0x35,0x33,0x37,0x7e,0x68,0x64,0x64,0xc9,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,
+0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6a,0x38,0x38,0x38,0x33,0x35,0x7e,0x68,
+0x64,0x64,0xca,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,
+0x39,0x33,0x39,0x6a,0x34,0x34,0x31,0x39,0x38,0x7e,0x68,0x64,0x64,0xcb,0xff,0xff,
+0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x73,0x30,
+0x39,0x31,0x39,0x30,0x7e,0x68,0x64,0x64,0xcc,0xff,0xff,0xff,0x13,0x0,0x0,0x0,
+0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6a,0x30,0x33,0x39,0x35,0x37,0x7e,
+0x68,0x64,0x64,0xcd,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,
+0x33,0x39,0x33,0x39,0x73,0x39,0x30,0x32,0x39,0x38,0x7e,0x68,0x64,0x64,0xce,0xff,
+0xff,0xff,0x8,0x0,0x0,0x0,0x52,0x41,0x30,0x35,0x7e,0x68,0x64,0x64,0xcf,0xff,
+0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x71,
+0x35,0x34,0x31,0x32,0x31,0x7e,0x68,0x64,0x64,0xd0,0xff,0xff,0xff,0x13,0x0,0x0,
+0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6b,0x32,0x34,0x32,0x33,0x39,
+0x7e,0x68,0x64,0x64,0xd1,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,
+0x35,0x33,0x39,0x33,0x39,0x71,0x38,0x30,0x35,0x38,0x32,0x7e,0x68,0x64,0x64,0xd2,
+0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,
+0x71,0x37,0x38,0x39,0x34,0x31,0x7e,0x68,0x64,0x64,0xd3,0xff,0xff,0xff,0x13,0x0,
+0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6d,0x35,0x38,0x30,0x36,
+0x36,0x7e,0x68,0x64,0x64,0xd4,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,
+0x32,0x35,0x33,0x39,0x33,0x39,0x6b,0x35,0x39,0x39,0x32,0x36,0x7e,0x68,0x64,0x64,
+0xd5,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,
+0x39,0x71,0x35,0x36,0x37,0x38,0x32,0x7e,0x68,0x64,0x64,0xd6,0xff,0xff,0xff,0x8,
+0x0,0x0,0x0,0x52,0x41,0x30,0x31,0x7e,0x68,0x64,0x64,0xd7,0xff,0xff,0xff,0x13,
+0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x70,0x34,0x34,0x36,
+0x32,0x33,0x7e,0x68,0x64,0x64,0xd8,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,
+0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x72,0x37,0x36,0x38,0x34,0x38,0x7e,0x68,0x64,
+0x64,0xd9,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,
+0x33,0x39,0x6e,0x36,0x36,0x37,0x31,0x35,0x7e,0x68,0x64,0x64,0xda,0xff,0xff,0xff,
+0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x72,0x31,0x39,
+0x39,0x37,0x32,0x7e,0x68,0x64,0x64,0xdb,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,
+0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x72,0x39,0x38,0x36,0x36,0x33,0x7e,0x68,
+0x64,0x64,0xdc,0xff,0xff,0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,
+0x39,0x33,0x39,0x72,0x33,0x35,0x37,0x33,0x35,0x7e,0x68,0x64,0x64,0xdd,0xff,0xff,
+0xff,0x13,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6e,0x34,
+0x34,0x35,0x36,0x31,0x7e,0x68,0x64,0x64,0xde,0xff,0xff,0xff,0xf,0x0,0x0,0x0,
+0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x66,0x39,0x39,0x39,0x32,0x31,0xdf,
+0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,
+0x77,0x36,0x36,0x37,0x32,0x36,0xe0,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,
+0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x75,0x31,0x39,0x30,0x36,0x38,0xe1,0xff,0xff,
+0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x75,0x30,
+0x35,0x36,0x35,0x30,0xe2,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,
+0x35,0x33,0x39,0x33,0x39,0x68,0x30,0x38,0x39,0x32,0x34,0xe4,0xff,0xff,0xff,0xf,
+0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x68,0x39,0x34,0x31,
+0x34,0x34,0xe5,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,
+0x39,0x33,0x39,0x74,0x31,0x31,0x35,0x33,0x37,0xe6,0xff,0xff,0xff,0xf,0x0,0x0,
+0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x76,0x32,0x30,0x32,0x30,0x35,
+0xe7,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,
+0x39,0x71,0x35,0x34,0x31,0x32,0x31,0xe8,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,
+0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6b,0x32,0x34,0x32,0x33,0x39,0xe9,0xff,
+0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x71,
+0x38,0x30,0x35,0x38,0x32,0xea,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,
+0x32,0x35,0x33,0x39,0x33,0x39,0x68,0x37,0x30,0x36,0x35,0x35,0xeb,0xff,0xff,0xff,
+0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6a,0x38,0x38,
+0x38,0x33,0x35,0xec,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,
+0x33,0x39,0x33,0x39,0x6a,0x34,0x34,0x31,0x39,0x38,0xed,0xff,0xff,0xff,0xf,0x0,
+0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x73,0x30,0x39,0x31,0x39,
+0x30,0xee,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,
+0x33,0x39,0x77,0x38,0x36,0x39,0x30,0x36,0xef,0xff,0xff,0xff,0xf,0x0,0x0,0x0,
+0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x71,0x37,0x38,0x39,0x34,0x31,0xf0,
+0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,
+0x6d,0x35,0x38,0x30,0x36,0x36,0xf1,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,
+0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6b,0x35,0x39,0x39,0x32,0x36,0xf2,0xff,0xff,
+0xff,0x4,0x0,0x0,0x0,0x52,0x41,0x31,0x37,0xf3,0xff,0xff,0xff,0xf,0x0,0x0,
+0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x68,0x34,0x39,0x33,0x32,0x30,
+0xf4,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,
+0x39,0x64,0x33,0x32,0x33,0x30,0x38,0xf5,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,
+0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x62,0x38,0x34,0x36,0x35,0x39,0xf6,0xff,
+0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x74,
+0x35,0x31,0x37,0x36,0x30,0xf7,0xff,0xff,0xff,0x4,0x0,0x0,0x0,0x52,0x41,0x31,
+0x33,0xf8,0xff,0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,
+0x33,0x39,0x76,0x31,0x35,0x31,0x37,0x34,0xf9,0xff,0xff,0xff,0xf,0x0,0x0,0x0,
+0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x6a,0x30,0x33,0x39,0x35,0x37,0xfa,
+0xff,0xff,0xff,0x4,0x0,0x0,0x0,0x52,0x41,0x30,0x39,0xfb,0xff,0xff,0xff,0xf,
+0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x73,0x39,0x30,0x32,
+0x39,0x38,0xfc,0xff,0xff,0xff,0x4,0x0,0x0,0x0,0x52,0x41,0x30,0x35,0xfd,0xff,
+0xff,0xff,0xf,0x0,0x0,0x0,0x70,0x30,0x36,0x32,0x35,0x33,0x39,0x33,0x39,0x71,
+0x35,0x36,0x37,0x38,0x32,0xfe,0xff,0xff,0xff,0xb,0x0,0x0,0x0,0x30,0x35,0x31,
+0x33,0x2d,0x52,0x2d,0x30,0x30,0x35,0x30,0xff,0xff,0xff,0xff,0x7,0x0,0x0,0x0,
+0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x30,0x1,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x2,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x3,0x0,
+0x0,0x0,0x5,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x4,0x0,0x0,0x0,0x5,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x5,0x0,0x0,0x0,0x5,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x35,0x6,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x7,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x8,
+0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x9,0x0,0x0,0x0,
+0x5,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0xa,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0xb,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x31,0xc,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x32,0xd,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x33,0xe,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0xf,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x10,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x36,0x11,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x12,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,0x13,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x39,0x14,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x30,0x15,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x31,0x16,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x32,0x17,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x18,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x34,0x19,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x1a,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,0x1b,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x32,0x37,0x1c,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x38,0x1d,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x39,0x1e,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x30,0x1f,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x20,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x32,0x21,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x22,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,0x23,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x33,0x35,0x24,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x33,0x36,0x25,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,
+0x37,0x26,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x38,0x27,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x28,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x30,0x29,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x2a,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,0x2b,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x33,0x2c,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x34,0x34,0x2d,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,
+0x35,0x2e,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x36,0x2f,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x30,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x38,0x31,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x32,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,0x33,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x31,0x34,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x32,0x35,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x33,0x36,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x34,0x37,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x38,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x36,0x39,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x3a,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,0x3b,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x39,0x3c,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x30,0x3d,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x31,0x3e,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x32,0x3f,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x40,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x34,0x41,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x42,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,0x43,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x36,0x37,0x44,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x38,0x45,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x39,0x46,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x30,0x47,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x48,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x32,0x49,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x4a,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,0x4b,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x37,0x35,0x4c,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x37,0x36,0x4d,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,
+0x37,0x4e,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x38,0x4f,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x50,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x30,0x51,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x52,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,0x53,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x33,0x54,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x38,0x34,0x55,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,
+0x35,0x56,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x36,0x57,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x58,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x38,0x59,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x5a,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,0x5b,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x39,0x31,0x5c,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x39,0x32,0x5d,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,
+0x33,0x5e,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x34,0x5f,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x60,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x36,0x61,0x0,0x0,0x0,0x6,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x37,0x62,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x39,0x38,0x63,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x39,0x39,0x64,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x30,0x30,0x65,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x30,0x31,0x66,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x30,0x32,0x67,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,
+0x33,0x68,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,
+0x69,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x35,0x6a,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x36,0x6b,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x37,0x6c,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x38,0x6d,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x39,0x6e,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x30,0x6f,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x31,0x70,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x32,0x71,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x31,0x31,0x33,0x72,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x31,0x34,0x73,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x31,0x31,0x35,0x74,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x31,0x36,0x75,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x31,0x37,0x76,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x31,0x38,0x77,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,
+0x39,0x78,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x30,
+0x79,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x31,0x7a,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x32,0x7b,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x7c,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x7d,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x7e,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x7f,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x80,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x81,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x82,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x33,0x30,0x83,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x31,0x33,0x31,0x84,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x33,0x32,0x85,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x33,0x33,0x86,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x33,0x34,0x87,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,
+0x35,0x88,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,
+0x89,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x8a,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x8b,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x8c,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x8d,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x8e,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x8f,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x90,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x91,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x92,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x34,0x36,0x93,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x31,0x34,0x37,0x94,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x34,0x38,0x95,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x34,0x39,0x96,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x35,0x30,0x97,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,
+0x31,0x98,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x32,
+0x99,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x33,0x9a,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x34,0x9b,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x35,0x9c,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x36,0x9d,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x37,0x9e,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x38,0x9f,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x35,0x39,0xa0,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x36,0x30,0xa1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x31,0x36,0x31,0xa2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x36,0x32,0xa3,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x31,0x36,0x33,0xa4,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x36,0x34,0xa5,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x36,0x35,0xa6,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x36,0x36,0xa7,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x36,
+0x37,0xa8,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x36,0x38,
+0xa9,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x36,0x39,0xaa,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x30,0xab,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x31,0xac,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x32,0xad,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x33,0xae,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x34,0xaf,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x35,0xb0,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x37,0x36,0xb1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x31,0x37,0x37,0xb2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x37,0x38,0xb3,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x31,0x37,0x39,0xb4,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x38,0x30,0xb5,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x38,0x31,0xb6,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x38,0x32,0xb7,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,
+0x33,0xb8,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,0x34,
+0xb9,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,0x35,0xba,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,0x36,0xbb,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,0x37,0xbc,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,0x38,0xbd,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x38,0x39,0xbe,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x39,0x30,0xbf,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x39,0x31,0xc0,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x39,0x32,0xc1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x31,0x39,0x33,0xc2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x31,0x39,0x34,0xc3,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x31,0x39,0x35,0xc4,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x31,0x39,0x36,0xc5,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x31,0x39,0x37,0xc6,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,
+0x39,0x38,0xc7,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x31,0x39,
+0x39,0xc8,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x30,
+0xc9,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x31,0xca,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x32,0xcb,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x33,0xcc,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x34,0xcd,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x35,0xce,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x36,0xcf,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x37,0xd0,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x32,0x30,0x38,0xd1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x32,0x30,0x39,0xd2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x32,0x31,0x30,0xd3,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x32,0x31,0x31,0xd4,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x31,0x32,0xd5,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x32,0x31,0x33,0xd6,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x31,0x34,0xd7,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x31,
+0x35,0xd8,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x31,0x36,
+0xd9,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x31,0x37,0xda,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x31,0x38,0xdb,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x31,0x39,0xdc,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x32,0x30,0xdd,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x32,0x31,0xde,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x32,0x32,0xdf,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x32,0x33,0xe0,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x32,0x32,0x34,0xe1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x32,0x32,0x35,0xe2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x32,0x32,0x36,0xe3,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x32,0x32,0x37,0xe4,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x32,0x38,0xe5,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x32,0x32,0x39,0xe6,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x33,0x30,0xe7,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,
+0x31,0xe8,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x32,
+0xe9,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x33,0xea,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x34,0xeb,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x35,0xec,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x36,0xed,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x37,0xee,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x38,0xef,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x33,0x39,0xf0,0x0,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x32,0x34,0x30,0xf1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x32,0x34,0x31,0xf2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x32,0x34,0x32,0xf3,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x32,0x34,0x33,0xf4,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x34,0x34,0xf5,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x32,0x34,0x35,0xf6,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x34,0x36,0xf7,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x34,
+0x37,0xf8,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x34,0x38,
+0xf9,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x34,0x39,0xfa,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x30,0xfb,0x0,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x31,0xfc,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x32,0xfd,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x33,0xfe,0x0,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x34,0xff,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x35,0x0,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x32,0x35,0x36,0x1,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x32,0x35,0x37,0x2,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x32,0x35,0x38,0x3,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x32,0x35,0x39,0x4,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x36,0x30,0x5,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x32,0x36,0x31,0x6,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x36,0x32,0x7,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,
+0x33,0x8,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,0x34,
+0x9,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,0x35,0xa,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,0x36,0xb,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,0x37,0xc,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,0x38,0xd,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x36,0x39,0xe,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x37,0x30,0xf,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x37,0x31,0x10,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x32,0x37,0x32,0x11,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x32,0x37,0x33,0x12,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x32,0x37,0x34,0x13,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x32,0x37,0x35,0x14,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x37,0x36,0x15,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x32,0x37,0x37,0x16,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x37,0x38,0x17,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x37,
+0x39,0x18,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x30,
+0x19,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x31,0x1a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x32,0x1b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x33,0x1c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x34,0x1d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x35,0x1e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x36,0x1f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x37,0x20,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x32,0x38,0x38,0x21,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x32,0x38,0x39,0x22,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x32,0x39,0x30,0x23,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x32,0x39,0x31,0x24,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x32,0x39,0x32,0x25,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x32,0x39,0x33,0x26,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,
+0x39,0x34,0x27,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x39,
+0x35,0x28,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x39,0x36,
+0x29,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x39,0x37,0x2a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x39,0x38,0x2b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x32,0x39,0x39,0x2c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x30,0x30,0x2d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x30,0x31,0x2e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x30,0x32,0x2f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x30,0x33,0x30,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x33,0x30,0x34,0x31,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x33,0x30,0x35,0x32,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x33,0x30,0x36,0x33,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x33,0x30,0x37,0x34,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x33,0x30,0x38,0x35,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x33,0x30,0x39,0x36,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,
+0x31,0x30,0x37,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,
+0x31,0x38,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x32,
+0x39,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x33,0x3a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x34,0x3b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x35,0x3c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x36,0x3d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x37,0x3e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x38,0x3f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x31,0x39,0x40,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x33,0x32,0x30,0x41,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x33,0x32,0x31,0x42,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x33,0x32,0x32,0x43,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x33,0x32,0x33,0x44,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x33,0x32,0x34,0x45,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x33,0x32,0x35,0x46,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,
+0x32,0x36,0x47,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x32,
+0x37,0x48,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x32,0x38,
+0x49,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x32,0x39,0x4a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x30,0x4b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x31,0x4c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x32,0x4d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x33,0x4e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x34,0x4f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x35,0x50,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x33,0x33,0x36,0x51,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x33,0x33,0x37,0x52,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x33,0x33,0x38,0x53,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x33,0x33,0x39,0x54,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x33,0x34,0x30,0x55,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x33,0x34,0x31,0x56,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,
+0x34,0x32,0x57,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,
+0x33,0x58,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,0x34,
+0x59,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,0x35,0x5a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,0x36,0x5b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,0x37,0x5c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,0x38,0x5d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x34,0x39,0x5e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x35,0x30,0x5f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x35,0x31,0x60,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x33,0x35,0x32,0x61,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x33,0x35,0x33,0x62,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x33,0x35,0x34,0x63,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x33,0x35,0x35,0x64,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x33,0x35,0x36,0x65,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x33,0x35,0x37,0x66,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,
+0x35,0x38,0x67,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x35,
+0x39,0x68,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x30,
+0x69,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x31,0x6a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x32,0x6b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x33,0x6c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x34,0x6d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x35,0x6e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x36,0x6f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x37,0x70,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x33,0x36,0x38,0x71,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x33,0x36,0x39,0x72,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x33,0x37,0x30,0x73,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x33,0x37,0x31,0x74,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x33,0x37,0x32,0x75,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x33,0x37,0x33,0x76,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,
+0x37,0x34,0x77,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x37,
+0x35,0x78,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x37,0x36,
+0x79,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x37,0x37,0x7a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x37,0x38,0x7b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x37,0x39,0x7c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x38,0x30,0x7d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x38,0x31,0x7e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x38,0x32,0x7f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x38,0x33,0x80,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x33,0x38,0x34,0x81,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x33,0x38,0x35,0x82,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x33,0x38,0x36,0x83,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x33,0x38,0x37,0x84,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x33,0x38,0x38,0x85,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x33,0x38,0x39,0x86,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,
+0x39,0x30,0x87,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,
+0x31,0x88,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x32,
+0x89,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x33,0x8a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x34,0x8b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x35,0x8c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x36,0x8d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x37,0x8e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x38,0x8f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x33,0x39,0x39,0x90,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x30,0x30,0x91,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x34,0x30,0x31,0x92,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x30,0x32,0x93,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x34,0x30,0x33,0x94,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x34,0x30,0x34,0x95,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x34,0x30,0x35,0x96,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,
+0x30,0x36,0x97,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x30,
+0x37,0x98,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x30,0x38,
+0x99,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x30,0x39,0x9a,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x30,0x9b,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x31,0x9c,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x32,0x9d,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x33,0x9e,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x34,0x9f,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x35,0xa0,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x31,0x36,0xa1,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x34,0x31,0x37,0xa2,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x31,0x38,0xa3,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x34,0x31,0x39,0xa4,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x34,0x32,0x30,0xa5,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x34,0x32,0x31,0xa6,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,
+0x32,0x32,0xa7,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,
+0x33,0xa8,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,0x34,
+0xa9,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,0x35,0xaa,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,0x36,0xab,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,0x37,0xac,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,0x38,0xad,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x32,0x39,0xae,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x33,0x30,0xaf,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x33,0x31,0xb0,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x33,0x32,0xb1,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x34,0x33,0x33,0xb2,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x33,0x34,0xb3,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x34,0x33,0x35,0xb4,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x34,0x33,0x36,0xb5,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x34,0x33,0x37,0xb6,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,
+0x33,0x38,0xb7,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x33,
+0x39,0xb8,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x30,
+0xb9,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x31,0xba,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x32,0xbb,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x33,0xbc,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x34,0xbd,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x35,0xbe,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x36,0xbf,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x37,0xc0,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x34,0x38,0xc1,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x34,0x34,0x39,0xc2,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x35,0x30,0xc3,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x34,0x35,0x31,0xc4,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x34,0x35,0x32,0xc5,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x34,0x35,0x33,0xc6,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,
+0x35,0x34,0xc7,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x35,
+0x35,0xc8,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x35,0x36,
+0xc9,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x35,0x37,0xca,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x35,0x38,0xcb,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x35,0x39,0xcc,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x36,0x30,0xcd,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x36,0x31,0xce,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x36,0x32,0xcf,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x36,0x33,0xd0,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x36,0x34,0xd1,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x34,0x36,0x35,0xd2,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x36,0x36,0xd3,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x34,0x36,0x37,0xd4,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x34,0x36,0x38,0xd5,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x34,0x36,0x39,0xd6,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,
+0x37,0x30,0xd7,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,
+0x31,0xd8,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x32,
+0xd9,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x33,0xda,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x34,0xdb,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x35,0xdc,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x36,0xdd,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x37,0xde,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x38,0xdf,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x37,0x39,0xe0,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x38,0x30,0xe1,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x34,0x38,0x31,0xe2,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x38,0x32,0xe3,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x34,0x38,0x33,0xe4,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x34,0x38,0x34,0xe5,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x34,0x38,0x35,0xe6,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,
+0x38,0x36,0xe7,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x38,
+0x37,0xe8,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x38,0x38,
+0xe9,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x38,0x39,0xea,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x30,0xeb,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x31,0xec,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x32,0xed,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x33,0xee,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x34,0xef,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x35,0xf0,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x34,0x39,0x36,0xf1,0x1,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x34,0x39,0x37,0xf2,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x34,0x39,0x38,0xf3,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x34,0x39,0x39,0xf4,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x30,0x30,0xf5,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x35,0x30,0x31,0xf6,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x30,0x32,0xf7,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,
+0x33,0xf8,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,0x34,
+0xf9,0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,0x35,0xfa,
+0x1,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,0x36,0xfb,0x1,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,0x37,0xfc,0x1,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,0x38,0xfd,0x1,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x30,0x39,0xfe,0x1,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x31,0x30,0xff,0x1,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x31,0x31,0x0,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x31,0x32,0x1,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x35,0x31,0x33,0x2,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x31,0x34,0x3,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x35,0x31,0x35,0x4,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x31,0x36,0x5,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x35,0x31,0x37,0x6,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x31,0x38,0x7,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x31,
+0x39,0x8,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x30,
+0x9,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x31,0xa,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x32,0xb,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x33,0xc,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x34,0xd,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x35,0xe,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x36,0xf,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x37,0x10,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x32,0x38,0x11,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x35,0x32,0x39,0x12,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x33,0x30,0x13,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x35,0x33,0x31,0x14,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x33,0x32,0x15,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x35,0x33,0x33,0x16,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x33,0x34,0x17,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x33,
+0x35,0x18,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x33,0x36,
+0x19,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x33,0x37,0x1a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x33,0x38,0x1b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x33,0x39,0x1c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x34,0x30,0x1d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x34,0x31,0x1e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x34,0x32,0x1f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x34,0x33,0x20,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x34,0x34,0x21,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x35,0x34,0x35,0x22,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x34,0x36,0x23,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x35,0x34,0x37,0x24,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x34,0x38,0x25,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x35,0x34,0x39,0x26,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x35,0x30,0x27,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,
+0x31,0x28,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x32,
+0x29,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x33,0x2a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x34,0x2b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x35,0x2c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x36,0x2d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x37,0x2e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x38,0x2f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x35,0x39,0x30,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x36,0x30,0x31,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x35,0x36,0x31,0x32,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x36,0x32,0x33,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x35,0x36,0x33,0x34,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x36,0x34,0x35,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x35,0x36,0x35,0x36,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x36,0x36,0x37,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x36,
+0x37,0x38,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x36,0x38,
+0x39,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x36,0x39,0x3a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x30,0x3b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x31,0x3c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x32,0x3d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x33,0x3e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x34,0x3f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x35,0x40,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x37,0x36,0x41,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x35,0x37,0x37,0x42,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x37,0x38,0x43,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x35,0x37,0x39,0x44,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x38,0x30,0x45,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x35,0x38,0x31,0x46,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x38,0x32,0x47,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,
+0x33,0x48,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,0x34,
+0x49,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,0x35,0x4a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,0x36,0x4b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,0x37,0x4c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,0x38,0x4d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x38,0x39,0x4e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x39,0x30,0x4f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x39,0x31,0x50,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x35,0x39,0x32,0x51,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x35,0x39,0x33,0x52,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x35,0x39,0x34,0x53,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x35,0x39,0x35,0x54,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x35,0x39,0x36,0x55,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x35,0x39,0x37,0x56,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,
+0x39,0x38,0x57,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x35,0x39,
+0x39,0x58,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x30,
+0x59,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x31,0x5a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x32,0x5b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x33,0x5c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x34,0x5d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x35,0x5e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x36,0x5f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x37,0x60,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x36,0x30,0x38,0x61,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x36,0x30,0x39,0x62,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x36,0x31,0x30,0x63,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x36,0x31,0x31,0x64,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x31,0x32,0x65,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x36,0x31,0x33,0x66,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x31,0x34,0x67,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x31,
+0x35,0x68,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x31,0x36,
+0x69,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x31,0x37,0x6a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x31,0x38,0x6b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x31,0x39,0x6c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x32,0x30,0x6d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x32,0x31,0x6e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x32,0x32,0x6f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x32,0x33,0x70,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x36,0x32,0x34,0x71,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x36,0x32,0x35,0x72,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x36,0x32,0x36,0x73,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x36,0x32,0x37,0x74,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x32,0x38,0x75,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x36,0x32,0x39,0x76,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x33,0x30,0x77,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,
+0x31,0x78,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x32,
+0x79,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x33,0x7a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x34,0x7b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x35,0x7c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x36,0x7d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x37,0x7e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x38,0x7f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x33,0x39,0x80,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x36,0x34,0x30,0x81,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x36,0x34,0x31,0x82,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x36,0x34,0x32,0x83,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x36,0x34,0x33,0x84,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x34,0x34,0x85,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x36,0x34,0x35,0x86,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x34,0x36,0x87,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x34,
+0x37,0x88,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x34,0x38,
+0x89,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x34,0x39,0x8a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x30,0x8b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x31,0x8c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x32,0x8d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x33,0x8e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x34,0x8f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x35,0x90,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x36,0x35,0x36,0x91,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x36,0x35,0x37,0x92,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x36,0x35,0x38,0x93,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x36,0x35,0x39,0x94,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x36,0x30,0x95,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x36,0x36,0x31,0x96,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x36,0x32,0x97,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,
+0x33,0x98,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,0x34,
+0x99,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,0x35,0x9a,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,0x36,0x9b,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,0x37,0x9c,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,0x38,0x9d,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x36,0x39,0x9e,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x37,0x30,0x9f,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x37,0x31,0xa0,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x36,0x37,0x32,0xa1,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x36,0x37,0x33,0xa2,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x36,0x37,0x34,0xa3,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x36,0x37,0x35,0xa4,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x37,0x36,0xa5,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x36,0x37,0x37,0xa6,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x37,0x38,0xa7,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x37,
+0x39,0xa8,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x30,
+0xa9,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x31,0xaa,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x32,0xab,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x33,0xac,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x34,0xad,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x35,0xae,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x36,0xaf,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x37,0xb0,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x36,0x38,0x38,0xb1,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x36,0x38,0x39,0xb2,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x36,0x39,0x30,0xb3,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x36,0x39,0x31,0xb4,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x36,0x39,0x32,0xb5,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x36,0x39,0x33,0xb6,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,
+0x39,0x34,0xb7,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x39,
+0x35,0xb8,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x39,0x36,
+0xb9,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x39,0x37,0xba,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x39,0x38,0xbb,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x36,0x39,0x39,0xbc,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x30,0x30,0xbd,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x30,0x31,0xbe,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x30,0x32,0xbf,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x30,0x33,0xc0,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x37,0x30,0x34,0xc1,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x37,0x30,0x35,0xc2,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x37,0x30,0x36,0xc3,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x37,0x30,0x37,0xc4,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x37,0x30,0x38,0xc5,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x37,0x30,0x39,0xc6,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,
+0x31,0x30,0xc7,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,
+0x31,0xc8,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x32,
+0xc9,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x33,0xca,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x34,0xcb,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x35,0xcc,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x36,0xcd,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x37,0xce,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x38,0xcf,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x31,0x39,0xd0,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x37,0x32,0x30,0xd1,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x37,0x32,0x31,0xd2,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x37,0x32,0x32,0xd3,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x37,0x32,0x33,0xd4,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x37,0x32,0x34,0xd5,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x37,0x32,0x35,0xd6,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,
+0x32,0x36,0xd7,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x32,
+0x37,0xd8,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x32,0x38,
+0xd9,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x32,0x39,0xda,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x30,0xdb,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x31,0xdc,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x32,0xdd,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x33,0xde,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x34,0xdf,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x35,0xe0,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x37,0x33,0x36,0xe1,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x37,0x33,0x37,0xe2,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x37,0x33,0x38,0xe3,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x37,0x33,0x39,0xe4,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x37,0x34,0x30,0xe5,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x37,0x34,0x31,0xe6,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,
+0x34,0x32,0xe7,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,
+0x33,0xe8,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,0x34,
+0xe9,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,0x35,0xea,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,0x36,0xeb,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,0x37,0xec,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,0x38,0xed,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x34,0x39,0xee,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x35,0x30,0xef,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x35,0x31,0xf0,0x2,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x37,0x35,0x32,0xf1,0x2,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x37,0x35,0x33,0xf2,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x37,0x35,0x34,0xf3,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x37,0x35,0x35,0xf4,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x37,0x35,0x36,0xf5,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x37,0x35,0x37,0xf6,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,
+0x35,0x38,0xf7,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x35,
+0x39,0xf8,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x30,
+0xf9,0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x31,0xfa,
+0x2,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x32,0xfb,0x2,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x33,0xfc,0x2,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x34,0xfd,0x2,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x35,0xfe,0x2,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x36,0xff,0x2,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x37,0x0,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x37,0x36,0x38,0x1,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x37,0x36,0x39,0x2,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x37,0x37,0x30,0x3,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x37,0x37,0x31,0x4,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x37,0x37,0x32,0x5,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x37,0x37,0x33,0x6,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,
+0x37,0x34,0x7,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x37,
+0x35,0x8,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x37,0x36,
+0x9,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x37,0x37,0xa,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x37,0x38,0xb,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x37,0x39,0xc,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x38,0x30,0xd,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x38,0x31,0xe,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x38,0x32,0xf,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x38,0x33,0x10,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x37,0x38,0x34,0x11,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x37,0x38,0x35,0x12,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x37,0x38,0x36,0x13,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x37,0x38,0x37,0x14,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x37,0x38,0x38,0x15,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x37,0x38,0x39,0x16,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,
+0x39,0x30,0x17,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,
+0x31,0x18,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x32,
+0x19,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x33,0x1a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x34,0x1b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x35,0x1c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x36,0x1d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x37,0x1e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x38,0x1f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x37,0x39,0x39,0x20,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x30,0x30,0x21,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x38,0x30,0x31,0x22,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x30,0x32,0x23,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x38,0x30,0x33,0x24,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x38,0x30,0x34,0x25,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x38,0x30,0x35,0x26,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,
+0x30,0x36,0x27,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x30,
+0x37,0x28,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x30,0x38,
+0x29,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x30,0x39,0x2a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x30,0x2b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x31,0x2c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x32,0x2d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x33,0x2e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x34,0x2f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x35,0x30,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x31,0x36,0x31,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x38,0x31,0x37,0x32,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x31,0x38,0x33,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x38,0x31,0x39,0x34,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x38,0x32,0x30,0x35,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x38,0x32,0x31,0x36,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,
+0x32,0x32,0x37,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,
+0x33,0x38,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,0x34,
+0x39,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,0x35,0x3a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,0x36,0x3b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,0x37,0x3c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,0x38,0x3d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x32,0x39,0x3e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x33,0x30,0x3f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x33,0x31,0x40,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x33,0x32,0x41,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x38,0x33,0x33,0x42,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x33,0x34,0x43,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x38,0x33,0x35,0x44,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x38,0x33,0x36,0x45,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x38,0x33,0x37,0x46,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,
+0x33,0x38,0x47,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x33,
+0x39,0x48,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x30,
+0x49,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x31,0x4a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x32,0x4b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x33,0x4c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x34,0x4d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x35,0x4e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x36,0x4f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x37,0x50,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x34,0x38,0x51,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x38,0x34,0x39,0x52,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x35,0x30,0x53,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x38,0x35,0x31,0x54,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x38,0x35,0x32,0x55,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x38,0x35,0x33,0x56,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,
+0x35,0x34,0x57,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x35,
+0x35,0x58,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x35,0x36,
+0x59,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x35,0x37,0x5a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x35,0x38,0x5b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x35,0x39,0x5c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x36,0x30,0x5d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x36,0x31,0x5e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x36,0x32,0x5f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x36,0x33,0x60,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x36,0x34,0x61,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x38,0x36,0x35,0x62,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x36,0x36,0x63,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x38,0x36,0x37,0x64,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x38,0x36,0x38,0x65,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x38,0x36,0x39,0x66,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,
+0x37,0x30,0x67,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,
+0x31,0x68,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x32,
+0x69,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x33,0x6a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x34,0x6b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x35,0x6c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x36,0x6d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x37,0x6e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x38,0x6f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x37,0x39,0x70,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x38,0x30,0x71,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x38,0x38,0x31,0x72,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x38,0x32,0x73,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x38,0x38,0x33,0x74,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x38,0x38,0x34,0x75,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x38,0x38,0x35,0x76,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,
+0x38,0x36,0x77,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x38,
+0x37,0x78,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x38,0x38,
+0x79,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x38,0x39,0x7a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x30,0x7b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x31,0x7c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x32,0x7d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x33,0x7e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x34,0x7f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x35,0x80,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x38,0x39,0x36,0x81,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x38,0x39,0x37,0x82,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x38,0x39,0x38,0x83,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x38,0x39,0x39,0x84,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x39,0x30,0x30,0x85,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x39,0x30,0x31,0x86,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,
+0x30,0x32,0x87,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,
+0x33,0x88,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,0x34,
+0x89,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,0x35,0x8a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,0x36,0x8b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,0x37,0x8c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,0x38,0x8d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x30,0x39,0x8e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x31,0x30,0x8f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x31,0x31,0x90,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x39,0x31,0x32,0x91,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x39,0x31,0x33,0x92,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x39,0x31,0x34,0x93,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x39,0x31,0x35,0x94,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x39,0x31,0x36,0x95,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x39,0x31,0x37,0x96,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,
+0x31,0x38,0x97,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x31,
+0x39,0x98,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x30,
+0x99,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x31,0x9a,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x32,0x9b,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x33,0x9c,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x34,0x9d,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x35,0x9e,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x36,0x9f,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x37,0xa0,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x39,0x32,0x38,0xa1,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x39,0x32,0x39,0xa2,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x39,0x33,0x30,0xa3,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x39,0x33,0x31,0xa4,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x39,0x33,0x32,0xa5,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x39,0x33,0x33,0xa6,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,
+0x33,0x34,0xa7,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x33,
+0x35,0xa8,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x33,0x36,
+0xa9,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x33,0x37,0xaa,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x33,0x38,0xab,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x33,0x39,0xac,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x34,0x30,0xad,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x34,0x31,0xae,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x34,0x32,0xaf,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x34,0x33,0xb0,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x39,0x34,0x34,0xb1,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x39,0x34,0x35,0xb2,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x39,0x34,0x36,0xb3,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x39,0x34,0x37,0xb4,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x39,0x34,0x38,0xb5,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x39,0x34,0x39,0xb6,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,
+0x35,0x30,0xb7,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,
+0x31,0xb8,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x32,
+0xb9,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x33,0xba,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x34,0xbb,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x35,0xbc,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x36,0xbd,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x37,0xbe,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x38,0xbf,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x35,0x39,0xc0,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x39,0x36,0x30,0xc1,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x39,0x36,0x31,0xc2,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x39,0x36,0x32,0xc3,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x39,0x36,0x33,0xc4,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x39,0x36,0x34,0xc5,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x39,0x36,0x35,0xc6,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,
+0x36,0x36,0xc7,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x36,
+0x37,0xc8,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x36,0x38,
+0xc9,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x36,0x39,0xcd,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x37,0x33,0xce,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x37,0x34,0xd1,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x37,0x37,0xd2,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x37,0x38,0xd3,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x37,0x39,0xd4,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x38,0x30,0xd5,0x3,0x0,0x0,0x7,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x39,0x38,0x31,0xd6,0x3,0x0,0x0,0x7,0x0,0x0,0x0,
+0x6f,0x73,0x64,0x2e,0x39,0x38,0x32,0xd7,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,
+0x73,0x64,0x2e,0x39,0x38,0x33,0xd8,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,
+0x64,0x2e,0x39,0x38,0x34,0xd9,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,
+0x2e,0x39,0x38,0x35,0xda,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,
+0x39,0x38,0x36,0xdb,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,
+0x38,0x37,0xdc,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x38,
+0x38,0xdd,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x38,0x39,
+0xde,0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x39,0x30,0xdf,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x39,0x31,0xe0,0x3,
+0x0,0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x39,0x32,0xe1,0x3,0x0,
+0x0,0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x39,0x33,0xe2,0x3,0x0,0x0,
+0x7,0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x39,0x34,0xe5,0x3,0x0,0x0,0x7,
+0x0,0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x39,0x37,0xe7,0x3,0x0,0x0,0x7,0x0,
+0x0,0x0,0x6f,0x73,0x64,0x2e,0x39,0x39,0x39,0xe9,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x30,0x31,0xef,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x30,0x37,0xf0,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x30,0x38,0xf2,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x31,0x30,0xf6,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x31,0x34,0xf7,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x31,0x35,0xf8,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x31,0x36,0xf9,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x31,0x37,0xfa,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x31,0x38,0xfb,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x31,0x39,0xfc,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x30,0xfd,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x31,0xfe,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x32,0xff,0x3,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x33,0x0,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x34,0x1,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x35,0x2,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x36,0x3,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x37,0x4,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x38,0x5,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x32,0x39,0x6,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x30,0x7,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x31,0x8,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x32,0x9,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x33,0xa,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x34,0xb,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x35,0xc,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x36,0xd,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x37,0xe,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x38,0xf,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x33,0x39,0x10,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x30,0x11,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x31,0x12,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x32,0x13,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x33,0x15,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x35,0x16,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x36,0x17,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x37,0x18,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x38,0x19,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x34,0x39,0x20,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x35,0x36,0x22,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x35,0x38,0x2b,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x36,0x37,0x2f,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x37,0x31,0x30,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x37,0x32,0x3e,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x38,0x36,0x41,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x38,0x39,0x43,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x39,0x31,0x44,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x39,0x32,0x4a,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x30,0x39,0x38,0x4c,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x30,0x30,0x53,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x30,0x37,0x57,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x31,0x31,0x59,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x31,0x33,0x62,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x32,0x32,0x66,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x32,0x36,0x68,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x32,0x38,0x6d,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x33,0x33,0x75,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x34,0x31,0x76,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x34,0x32,0x7b,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x34,0x37,0x82,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x35,0x34,0x86,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x35,0x38,0x95,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x37,0x33,0x97,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x37,0x35,0x9c,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x38,0x30,0xa7,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x39,0x31,0xa8,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x39,0x32,0xae,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x31,0x39,0x38,0xb1,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x30,0x31,0xb4,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x30,0x34,0xbb,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x31,0x31,0xbe,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x31,0x34,0xc0,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x31,0x36,0xc9,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x32,0x35,0xca,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x32,0x36,0xcb,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x32,0x37,0xcc,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x32,0x38,0xcd,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x32,0x39,0xce,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x30,0xcf,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x31,0xd2,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x34,0xd3,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x35,0xd4,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x36,0xd5,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x37,0xd6,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x38,0xd7,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x33,0x39,0xd8,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x30,0xd9,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x31,0xda,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x32,0xdb,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x33,0xdc,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x34,0xdd,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x35,0xde,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x36,0xdf,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x37,0xe0,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x38,0xe1,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x34,0x39,0xe2,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x30,0xe3,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x31,0xe4,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x32,0xe5,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x33,0xe6,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x34,0xe7,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x35,0xe8,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x36,0xe9,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x37,0xea,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x38,0xeb,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x35,0x39,0xec,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x30,0xed,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x31,0xee,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x32,0xef,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x33,0xf0,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x34,0xf1,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x35,0xf2,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x36,0xf3,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x37,0xf4,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x38,0xf5,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x36,0x39,0xf6,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x30,0xf7,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x31,0xf8,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x32,0xf9,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x33,0xfa,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x34,0xfb,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x35,0xfc,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x36,0xfd,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x37,0xfe,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x38,0xff,0x4,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x37,0x39,0x0,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x30,0x1,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x31,0x2,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x32,0x3,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x33,0x4,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x34,0x5,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x35,0x6,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x36,0x7,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x37,0x8,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x38,0x9,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x38,0x39,0xa,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x30,0xb,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x31,0xc,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x32,0xd,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x33,0xe,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x34,0xf,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x35,0x10,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x36,0x11,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x37,0x12,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x38,0x13,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x32,0x39,0x39,0x14,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x30,0x15,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x31,0x16,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x32,0x17,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x33,0x18,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x34,0x19,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x35,0x1a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x36,0x1b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x37,0x1c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x38,0x1d,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x30,0x39,0x1e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x30,0x1f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x31,0x20,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x32,0x21,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x33,0x22,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x34,0x23,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x35,0x24,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x36,0x25,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x37,0x26,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x38,0x27,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x31,0x39,0x28,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x30,0x29,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x31,0x2a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x32,0x2b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x33,0x2c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x34,0x2d,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x35,0x2e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x36,0x2f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x37,0x30,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x38,0x31,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x32,0x39,0x32,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x30,0x33,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x31,0x34,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x32,0x35,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x33,0x36,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x34,0x37,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x35,0x38,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x36,0x39,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x37,0x3a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x38,0x3b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x33,0x39,0x3c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x30,0x3d,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x31,0x3e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x32,0x3f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x33,0x40,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x34,0x41,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x35,0x42,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x36,0x43,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x37,0x44,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x38,0x45,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x34,0x39,0x46,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x30,0x47,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x31,0x48,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x32,0x49,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x33,0x4a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x34,0x4b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x35,0x4c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x36,0x4d,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x37,0x4e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x38,0x4f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x35,0x39,0x51,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x31,0x52,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x32,0x53,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x33,0x54,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x34,0x55,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x35,0x56,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x36,0x57,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x37,0x58,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x38,0x59,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x36,0x39,0x5a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x30,0x5b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x31,0x5c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x32,0x5d,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x33,0x5e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x34,0x5f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x35,0x60,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x36,0x61,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x37,0x62,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x38,0x63,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x37,0x39,0x64,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x30,0x65,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x31,0x66,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x32,0x67,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x33,0x68,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x34,0x69,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x35,0x6a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x36,0x6b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x37,0x6c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x38,0x6d,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x38,0x39,0x6e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x30,0x6f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x31,0x70,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x32,0x71,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x33,0x72,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x34,0x73,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x35,0x74,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x36,0x76,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x38,0x77,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x33,0x39,0x39,0x78,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x30,0x79,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x31,0x7a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x32,0x7b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x33,0x7c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x34,0x7e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x36,0x7f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x37,0x80,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x38,0x81,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x30,0x39,0x82,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x30,0x83,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x31,0x84,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x32,0x86,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x34,0x87,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x35,0x88,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x36,0x89,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x37,0x8a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x38,0x8b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x31,0x39,0x8c,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x30,0x8e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x32,0x8f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x33,0x90,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x34,0x91,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x35,0x92,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x36,0x93,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x37,0x95,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x32,0x39,0x96,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x30,0x97,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x31,0x98,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x32,0x99,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x33,0x9a,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x34,0x9b,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x35,0x9d,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x37,0x9e,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x38,0x9f,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x33,0x39,0xa0,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x30,0xa1,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x31,0xa2,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x32,0xa5,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x35,0xa6,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x36,0xa7,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x37,0xa8,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x38,0xa9,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x34,0x39,0xaa,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x30,0xab,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x31,0xad,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x33,0xae,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x34,0xaf,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x35,0xb0,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x36,0xb1,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x37,0xb2,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x38,0xb3,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x35,0x39,0xb5,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x31,0xb6,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x32,0xb7,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x33,0xb9,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x35,0xba,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x36,0xbb,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x37,0xbc,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x38,0xbd,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x36,0x39,0xbe,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x37,0x30,0xc0,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x37,0x32,0xc1,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x37,0x33,0xc2,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x37,0x34,0xc3,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6f,0x73,0x64,0x2e,0x31,0x34,0x37,0x35,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4,0x0,0x0,0x0,0x64,0x61,0x74,0x61,0x1,0x0,0x0,0x0,0x8,0x0,0x0,
+0x0,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x2,0x0,0x0,0x0,0x3,0x0,0x0,
+0x0,0x72,0x62,0x64,0x3,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x62,0x61,0x72,0x6e,
+0x4,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x63,0x72,0x69,0x74,0x69,0x63,0x61,0x6c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4,0x1,0x16,0x0,0x0,0x0,0x0,0x5d,0x5,0x0,0x0,0x6c,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x6e,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x70,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x72,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x74,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x76,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x78,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x7b,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x7e,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x7f,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x80,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x81,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x82,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x83,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x84,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x85,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x86,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x87,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x88,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x89,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x8a,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x8b,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x8c,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x8d,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x8e,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x8f,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x90,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x91,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x92,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x93,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x94,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x95,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x96,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x97,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x98,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x99,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x9a,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x9b,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x9c,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0x9d,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xbd,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xbe,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xc0,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xc1,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xc2,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xc3,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xc4,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xc5,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xc6,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xc7,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xc8,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xc9,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xca,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xcb,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xcc,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xcd,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xce,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xcf,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xd0,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xd1,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xd2,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xd3,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xd4,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xd5,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xd6,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xd7,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xd8,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xd9,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xda,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xdb,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0xdc,0xff,0xff,0xff,0x0,
+0x0,0x0,0x0,0xdd,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x43,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x59,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x61,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x85,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x95,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x97,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x99,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xad,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xed,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x13,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x17,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x19,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x21,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x23,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x25,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x29,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x33,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x35,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x37,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x39,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x43,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x45,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x49,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x51,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x53,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x55,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x57,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x59,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x61,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x63,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x65,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x67,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x69,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x71,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x73,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x75,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x77,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x81,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x83,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x85,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x87,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x89,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x91,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x93,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x95,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x97,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x99,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9b,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9d,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9f,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xab,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xad,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdf,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xeb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xed,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf5,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf7,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfb,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x11,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x13,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x15,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x17,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x19,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x21,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x23,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x25,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x27,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x29,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x31,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x33,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x35,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x37,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x45,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x49,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x51,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x53,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x55,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x57,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x59,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x61,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x63,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x65,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x67,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x69,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x71,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x73,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x75,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x77,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x81,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x83,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x85,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x87,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x89,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x91,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x93,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x95,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x97,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x99,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9b,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9d,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9f,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xab,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xad,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdf,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xeb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xed,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf5,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf7,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfb,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfd,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x11,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x13,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x15,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x17,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x19,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x21,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x23,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x25,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x27,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x29,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x31,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x33,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x35,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x37,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x39,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x43,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x45,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x49,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x51,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x53,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x57,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x59,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x61,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x63,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x65,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x71,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x73,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x75,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x77,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x81,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x83,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x85,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x87,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x89,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x91,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x93,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x95,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x97,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x99,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9d,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9f,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa5,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa9,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xab,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xad,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb5,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb9,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc1,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc5,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xce,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd2,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd4,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xda,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdc,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xde,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe2,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf2,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf7,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfb,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfd,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x11,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x13,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x16,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x18,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x20,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2b,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x44,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x57,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x62,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x68,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x75,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7b,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x86,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x97,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xae,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb4,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbe,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc9,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcd,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcf,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd5,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd9,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdd,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdf,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe5,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe9,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xeb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xed,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf3,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf5,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfb,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfd,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x11,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x13,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x15,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x17,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x19,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1d,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x21,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x23,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x25,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x29,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2d,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2f,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x31,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x33,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x35,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x37,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x39,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3d,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x43,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x45,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x49,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4d,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4f,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x52,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x54,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x56,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x58,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5c,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5e,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x60,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x62,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x64,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x66,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x68,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6a,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6c,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6e,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x70,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x72,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x74,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x77,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7e,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x80,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x82,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x84,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x87,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x89,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8e,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x90,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x92,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x95,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x97,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x99,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9b,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9e,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa6,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa8,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaa,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xad,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb3,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb6,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb9,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc2,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x68,
+0x64,0x64,0x48,0x0,0x0,0x0,0x6d,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6c,0xff,0xff,0xff,0x6f,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6e,0xff,0xff,0xff,0x71,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x70,0xff,0xff,0xff,0x73,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x72,0xff,0xff,0xff,0x75,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x74,0xff,0xff,0xff,0x77,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x76,0xff,0xff,0xff,0x79,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x78,0xff,0xff,0xff,0x7d,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7b,0xff,0xff,0xff,0x9e,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd7,0xff,0xff,0xff,0x9f,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd8,0xff,0xff,0xff,0xa0,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd9,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xda,0xff,0xff,0xff,0xa2,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xdb,0xff,0xff,0xff,0xa3,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xdc,0xff,0xff,0xff,0xa5,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8d,0xff,0xff,0xff,0xa6,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8e,0xff,0xff,0xff,0xa7,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x87,0xff,0xff,0xff,0xa8,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8f,0xff,0xff,0xff,0xa9,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x82,0xff,0xff,0xff,0xaa,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x92,0xff,0xff,0xff,0xab,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x88,0xff,0xff,0xff,0xac,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x89,0xff,0xff,0xff,0xad,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x93,0xff,0xff,0xff,0xae,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x83,0xff,0xff,0xff,0xaf,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x84,0xff,0xff,0xff,0xb0,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8c,0xff,0xff,0xff,0xb1,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x90,0xff,0xff,0xff,0xb2,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x81,0xff,0xff,0xff,0xb3,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x85,0xff,0xff,0xff,0xb4,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x80,0xff,0xff,0xff,0xb5,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x86,0xff,0xff,0xff,0xb6,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8a,0xff,0xff,0xff,0xb7,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x94,0xff,0xff,0xff,0xb8,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd6,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8b,0xff,0xff,0xff,0xba,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x91,0xff,0xff,0xff,0xbb,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x95,0xff,0xff,0xff,0xbc,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xdd,0xff,0xff,0xff,0xbf,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7f,0xff,0xff,0xff,0xde,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x98,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x99,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc2,0xff,0xff,0xff,0xe1,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc3,0xff,0xff,0xff,0xe2,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9a,0xff,0xff,0xff,0xe4,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc7,0xff,0xff,0xff,0xe5,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc8,0xff,0xff,0xff,0xe6,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9b,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcf,0xff,0xff,0xff,0xe8,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd0,0xff,0xff,0xff,0xe9,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd1,0xff,0xff,0xff,0xea,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9c,0xff,0xff,0xff,0xeb,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc9,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xca,0xff,0xff,0xff,0xed,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcb,0xff,0xff,0xff,0xee,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9d,0xff,0xff,0xff,0xef,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd2,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd3,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd4,0xff,0xff,0xff,0xf2,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x97,0xff,0xff,0xff,0xf3,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbd,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc0,0xff,0xff,0xff,0xf5,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc1,0xff,0xff,0xff,0xf6,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc4,0xff,0xff,0xff,0xf7,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbe,0xff,0xff,0xff,0xf8,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc5,0xff,0xff,0xff,0xf9,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcc,0xff,0xff,0xff,0xfa,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc6,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcd,0xff,0xff,0xff,0xfc,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xce,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd5,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x96,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7e,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x47,0x13,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0x78,
+0x0,0x0,0x0,0xf,0x2,0x0,0x0,0x44,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x92,0x1,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x3,0x0,
+0x0,0x11,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x9d,0x1,
+0x0,0x0,0x23,0x2,0x0,0x0,0x3e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,
+0x3,0x0,0x0,0xff,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x1,0x0,0x0,
+0x2a,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x5,0x0,0x0,0x15,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x4a,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x2,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x88,0x2,0x0,0x0,0x4b,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x13,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x91,0x0,0x0,
+0x0,0xaa,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0x12,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x13,0x2,0x0,0x0,0x66,
+0x1,0x0,0x0,0x2c,0x2,0x0,0x0,0xba,0x3,0x0,0x0,0x22,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x8b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe0,0x2,0x0,0x0,0xc1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1e,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3a,0x5,
+0x0,0x0,0x31,0x5,0x0,0x0,0xbf,0x3,0x0,0x0,0xc4,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x24,0x3,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xfa,0x2,0x0,0x0,0xed,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x22,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,
+0x0,0x57,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x5,0x0,0x0,0x17,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0xf8,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xae,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2c,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x5,
+0x0,0x0,0x32,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x19,0x2,0x0,0x0,0xee,
+0x0,0x0,0x0,0x64,0x5,0x0,0x0,0x4c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc9,0x3,0x0,0x0,0x1e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x31,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xc4,0x3,0x0,
+0x0,0xa1,0x2,0x0,0x0,0x5c,0x5,0x0,0x0,0xf5,0x1,0x0,0x0,0xb6,0x5,0x0,
+0x0,0x11,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x22,0x5,0x0,0x0,0xbb,0x3,
+0x0,0x0,0xa7,0x5,0x0,0x0,0x27,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x33,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x22,
+0x3,0x0,0x0,0xbc,0x1,0x0,0x0,0x9c,0x1,0x0,0x0,0x24,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x35,0x2,0x0,0x0,0x47,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x31,0x5,0x0,0x0,0x5b,0x5,0x0,0x0,0xbf,0x0,0x0,0x0,0xfc,0x0,0x0,
+0x0,0xa1,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x39,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5c,0x2,
+0x0,0x0,0xb6,0x1,0x0,0x0,0x77,0x2,0x0,0x0,0x37,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0x55,0x2,0x0,0x0,0xb6,0x0,0x0,0x0,0xee,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe8,0x4,0x0,0x0,0xe,0x0,0x0,0x0,
+0x5b,0x2,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x41,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x5,0x0,
+0x0,0x8,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x80,0x5,
+0x0,0x0,0xb6,0x1,0x0,0x0,0xa3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,
+0x3,0x0,0x0,0x6a,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x50,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,
+0x27,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x89,0x5,0x0,0x0,0x10,0x5,0x0,
+0x0,0xe,0x5,0x0,0x0,0x13,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x57,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x44,0x0,
+0x0,0x0,0xa7,0x5,0x0,0x0,0xab,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe1,0x4,0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x49,0x5,0x0,0x0,0xf6,0x4,0x0,0x0,0x42,0x1,0x0,0x0,0xc4,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xeb,0x2,0x0,0x0,0xe2,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x97,0x1,0x0,0x0,0xda,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x63,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe2,
+0x4,0x0,0x0,0x2,0x5,0x0,0x0,0xc4,0x2,0x0,0x0,0x5b,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x99,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb9,0x2,0x0,0x0,0x53,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6d,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x39,0x2,
+0x0,0x0,0x2a,0x2,0x0,0x0,0x1a,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0xc4,0x2,
+0x0,0x0,0xcf,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x54,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5,0x1,0x0,0x0,0x20,0x4,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xa0,0x2,0x0,0x0,0x6b,0x5,0x0,0x0,0xe5,0x4,0x0,0x0,0x1b,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0xb2,0x0,0x0,0x0,0x66,
+0x3,0x0,0x0,0x93,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7d,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x1,0x0,0x0,
+0xdf,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0xf8,0x2,0x0,
+0x0,0xa5,0x0,0x0,0x0,0x11,0x5,0x0,0x0,0x8f,0x1,0x0,0x0,0xee,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x99,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0xba,0x3,0x0,0x0,0xb0,0x5,0x0,0x0,0xef,0x2,0x0,0x0,0x8c,0x2,0x0,0x0,
+0x4f,0x1,0x0,0x0,0x2c,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x85,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x3,0x0,
+0x0,0x22,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa0,0x5,0x0,0x0,0xc0,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x44,0x3,0x0,0x0,0xf4,0x0,0x0,0x0,0x1c,
+0x1,0x0,0x0,0x8c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x8a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x5,0x0,0x0,
+0xec,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbb,0x2,0x0,0x0,0xd9,0x0,0x0,
+0x0,0xb4,0x0,0x0,0x0,0xdb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x96,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfb,0x4,
+0x0,0x0,0x10,0x5,0x0,0x0,0x84,0x5,0x0,0x0,0xf5,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x3d,0x5,0x0,0x0,0xb8,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3a,0x5,0x0,0x0,0x33,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x1,0x0,
+0x0,0x34,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x58,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x5,0x0,0x0,0x8,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xae,0x5,0x0,0x0,0xda,0x0,0x0,0x0,0x6c,0x5,0x0,0x0,
+0xe4,0x4,0x0,0x0,0xf,0x2,0x0,0x0,0x2b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x41,0x2,0x0,0x0,0x30,0x3,0x0,0x0,0x2c,0x3,0x0,0x0,0x1e,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0x2b,0x3,0x0,0x0,0x11,0x3,0x0,0x0,0x27,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x6b,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0xa2,0x0,0x0,0x0,0x73,0x2,0x0,
+0x0,0xf5,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0xe2,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x0,0x0,0x0,0xa1,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0xf5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xbb,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x51,0x0,
+0x0,0x0,0xea,0x2,0x0,0x0,0x9a,0x5,0x0,0x0,0xf5,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x3c,0x5,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x51,0x5,0x0,0x0,0x61,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xce,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x31,0x5,0x0,
+0x0,0xfb,0x1,0x0,0x0,0x7a,0x0,0x0,0x0,0xfc,0x1,0x0,0x0,0x5e,0x0,0x0,
+0x0,0x5,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc5,0x3,0x0,0x0,0x1b,0x0,
+0x0,0x0,0xaf,0x0,0x0,0x0,0x94,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,
+0x2,0x0,0x0,0xf5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfa,0x4,0x0,0x0,
+0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x19,0x5,0x0,0x0,0x94,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0x55,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1b,0x2,0x0,0x0,0xb7,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf0,0x1,0x0,0x0,0xa7,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xdf,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9f,0x0,0x0,
+0x0,0x34,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0xaa,0x5,
+0x0,0x0,0x5c,0x2,0x0,0x0,0x4e,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfb,
+0x0,0x0,0x0,0xaa,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1b,0x2,0x0,0x0,
+0x58,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,0x1b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x79,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xeb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x4,
+0x0,0x0,0xdc,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0x8,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x1,0x0,0x0,0xea,0x2,0x0,0x0,
+0x16,0x1,0x0,0x0,0xe,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x66,0x5,0x0,
+0x0,0x2,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0xe1,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0x5c,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x9e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xff,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x51,0x0,0x0,0x0,0x9d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9b,0x0,
+0x0,0x0,0x9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x3,0x0,0x0,0xc7,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0xc9,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x25,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x30,0x2,0x0,0x0,0xe,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x18,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,
+0x5,0x0,0x0,0x73,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x19,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x71,0x1,0x0,0x0,
+0x9a,0x5,0x0,0x0,0xd6,0x4,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xfd,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x31,0x5,0x0,0x0,0xf8,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x30,0x5,0x0,0x0,0xcb,0x0,0x0,0x0,0xb9,0x0,
+0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x1c,
+0x0,0x0,0x0,0x5c,0x2,0x0,0x0,0x55,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xb6,0x5,0x0,0x0,0x0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x23,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,
+0x0,0x7,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x74,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0xda,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xfc,0x4,0x0,0x0,0x22,0x5,0x0,0x0,0x5d,0x1,0x0,0x0,
+0x86,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x4f,0x5,0x0,0x0,0x6c,0x5,0x0,
+0x0,0xbb,0x1,0x0,0x0,0xd5,0x1,0x0,0x0,0xf,0x0,0x0,0x0,0xa7,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0xd5,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1b,0x5,0x0,0x0,0x5,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x49,0x5,0x0,0x0,0xf6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x33,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x0,0x0,
+0x0,0xe,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x25,0x3,0x0,0x0,0xe9,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd1,0x2,0x0,0x0,0x3f,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x98,0x2,0x0,0x0,0x2b,0x1,0x0,0x0,0x37,0x5,0x0,0x0,0xeb,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x5f,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x8,0x5,0x0,0x0,0x2e,0x3,0x0,0x0,0xb,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0x31,0x5,0x0,0x0,
+0x1e,0x2,0x0,0x0,0xb,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x41,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x4,0x0,
+0x0,0x73,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0xcc,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x1d,0x0,0x0,0x0,0xbf,
+0x3,0x0,0x0,0x47,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x46,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x2,0x0,0x0,
+0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x86,0x5,0x0,0x0,0x6a,0x3,0x0,
+0x0,0x3f,0x5,0x0,0x0,0xf3,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4d,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe9,0x0,
+0x0,0x0,0xe2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0xc5,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,
+0xe1,0x4,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x51,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x2,0x0,
+0x0,0x55,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0xd8,0x0,
+0x0,0x0,0xb2,0x5,0x0,0x0,0xfd,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x57,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,
+0x2,0x0,0x0,0x22,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,
+0x56,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfb,0x4,0x0,0x0,0x33,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x1,0x0,0x0,0x93,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0xbd,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x62,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x25,0x2,0x0,0x0,0x47,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x65,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x79,0x5,0x0,
+0x0,0x3e,0x3,0x0,0x0,0x18,0x1,0x0,0x0,0x8f,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x14,0x5,0x0,0x0,0x2e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9b,
+0x3,0x0,0x0,0xf8,0x2,0x0,0x0,0xed,0x0,0x0,0x0,0x22,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd6,0x4,0x0,0x0,0xf0,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x76,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x93,0x5,0x0,0x0,0xe4,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x78,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x28,0x2,
+0x0,0x0,0x42,0x2,0x0,0x0,0xd6,0x1,0x0,0x0,0x8a,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0xae,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x8f,0x0,0x0,0x0,0xea,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x5,0x0,
+0x0,0x13,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x1a,0x3,0x0,0x0,0x43,0x0,
+0x0,0x0,0xdc,0x4,0x0,0x0,0x53,0x1,0x0,0x0,0xe7,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x34,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xce,0x0,0x0,0x0,0xe2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x85,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x34,0x2,0x0,0x0,0xe1,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x2a,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x1,0x0,0x0,0x86,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xc6,0x2,0x0,0x0,0x49,0x2,0x0,0x0,0x5e,0x2,0x0,0x0,0x2b,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbb,0x3,0x0,0x0,0x69,0x1,0x0,0x0,
+0x57,0x5,0x0,0x0,0x5a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8c,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x11,0x3,0x0,
+0x0,0x38,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x5,0x0,0x0,0x45,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x3,0x0,0x0,0x47,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0x61,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x96,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x0,0x1,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x97,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1c,0x1,
+0x0,0x0,0xce,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0xe2,
+0x2,0x0,0x0,0x8f,0x0,0x0,0x0,0x6a,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x41,0x0,0x0,0x0,0xeb,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x49,0x0,0x0,
+0x0,0x71,0x1,0x0,0x0,0xf,0x3,0x0,0x0,0xda,0x0,0x0,0x0,0x61,0x5,0x0,
+0x0,0xe7,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x2,0x0,0x0,0x44,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2c,0x3,0x0,0x0,0xbb,0x3,0x0,0x0,0x4d,
+0x2,0x0,0x0,0xf5,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa5,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x10,0x4,0x0,0x0,
+0x5b,0x5,0x0,0x0,0x14,0x2,0x0,0x0,0xbd,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x28,0x2,0x0,0x0,0xd8,0x0,0x0,0x0,0x54,0x1,0x0,0x0,0x2f,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,0x5d,0x1,
+0x0,0x0,0x6d,0x5,0x0,0x0,0x2f,0x3,0x0,0x0,0xff,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x20,0x2,0x0,0x0,0xea,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3f,0x2,0x0,0x0,0x56,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xab,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe4,0x0,0x0,
+0x0,0x0,0x3,0x0,0x0,0xc7,0x1,0x0,0x0,0xc9,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x73,0x0,0x0,0x0,0xba,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf8,
+0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x6,0x1,0x0,0x0,0xe4,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x6,0x2,0x0,0x0,0xac,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x55,0x5,0x0,0x0,0x61,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xba,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf9,0x4,
+0x0,0x0,0xbd,0x5,0x0,0x0,0xbc,0x0,0x0,0x0,0x95,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x47,0x5,0x0,0x0,0x2d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xbf,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x61,0x1,0x0,0x0,0x2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5f,0x0,0x0,
+0x0,0x4e,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0x60,0x1,
+0x0,0x0,0x5f,0x1,0x0,0x0,0xfd,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc7,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,
+0x0,0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x20,0x4,0x0,0x0,
+0x22,0x5,0x0,0x0,0xc,0x3,0x0,0x0,0xbc,0x1,0x0,0x0,0x3e,0x1,0x0,0x0,
+0x8e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x5,0x0,0x0,0x99,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x99,0x3,
+0x0,0x0,0xc3,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x11,
+0x1,0x0,0x0,0xa1,0x0,0x0,0x0,0x6c,0x1,0x0,0x0,0xb6,0x5,0x0,0x0,0x35,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x2a,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xae,0x5,0x0,0x0,0xaf,0x5,0x0,0x0,0x1c,0x5,0x0,0x0,0xc1,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x5,0x0,0x0,0x9,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0x86,0x5,0x0,0x0,
+0xea,0x2,0x0,0x0,0x56,0x0,0x0,0x0,0x77,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xdb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x7f,0x5,0x0,0x0,0x97,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xdc,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x13,0x3,
+0x0,0x0,0x1b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x3,0x0,0x0,0x96,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0xc0,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x9b,0x1,0x0,0x0,0xc0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,
+0x0,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe7,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x3,0x0,0x0,
+0xce,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x2,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xdd,0x4,0x0,0x0,0x33,0x5,0x0,0x0,0x7e,0x0,
+0x0,0x0,0x35,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0xba,
+0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xef,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x27,0x5,0x0,0x0,0x4e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x3,0x0,
+0x0,0xf1,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x18,0x1,
+0x0,0x0,0xb6,0x0,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x21,
+0x2,0x0,0x0,0xda,0x0,0x0,0x0,0xdb,0x1,0x0,0x0,0xb2,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0x71,0x3,0x0,0x0,0x5a,0x0,0x0,0x0,0x7a,0x1,0x0,0x0,
+0x2b,0x1,0x0,0x0,0x8e,0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x3,0x5,0x0,0x0,0x7b,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x5,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x26,0x3,0x0,0x0,0x63,0x2,0x0,0x0,0xaf,0x2,
+0x0,0x0,0x93,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x2,0x0,0x0,0xf3,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2b,0x1,0x0,0x0,0x4a,0x3,0x0,0x0,
+0x6a,0x5,0x0,0x0,0x41,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x4,0x0,
+0x0,0xf7,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x1,0x0,0x0,0xca,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc4,0x2,0x0,0x0,0xea,0x2,0x0,0x0,0xe,
+0x3,0x0,0x0,0xd,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x13,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,
+0x6,0x5,0x0,0x0,0x72,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x14,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xad,0x0,0x0,0x0,0xea,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x18,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf0,0x1,
+0x0,0x0,0xb2,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x74,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x51,0x0,0x0,0x0,0x64,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0x9e,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbb,0x1,0x0,0x0,0xc1,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x77,0x5,0x0,0x0,0x10,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x28,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x12,
+0x1,0x0,0x0,0x4a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2a,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,
+0x53,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0x60,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x3a,0x2,0x0,0x0,0x4d,0x0,
+0x0,0x0,0xd5,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x2,0x0,0x0,0xc4,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x0,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x57,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x59,0x3,0x0,0x0,0x62,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x42,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x98,
+0x1,0x0,0x0,0x57,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x44,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbe,0x5,0x0,0x0,
+0xc1,0x5,0x0,0x0,0x66,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x46,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x89,0x5,0x0,0x0,0xc2,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x47,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x5,
+0x0,0x0,0xf,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x5,0x0,0x0,0x52,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x2b,0x3,0x0,0x0,
+0x6c,0x2,0x0,0x0,0xd5,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x54,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x1,0x0,
+0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,0x0,0x0,0xc4,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x5f,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x3f,0x3,0x0,0x0,0x57,0x0,0x0,0x0,0xdd,0x2,0x0,0x0,0x36,0x2,0x0,
+0x0,0xf,0x1,0x0,0x0,0x17,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x63,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x5,
+0x0,0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0xdb,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x5,0x0,0x0,0xed,0x4,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x3c,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xc4,0x2,0x0,0x0,0xb7,0x1,0x0,0x0,0x16,0x1,0x0,0x0,0x6b,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x0,0x0,0x0,0x9,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x7c,0x5,0x0,0x0,0x47,0x3,0x0,0x0,0x10,0x1,0x0,0x0,
+0x74,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x52,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0xfd,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf7,0x4,0x0,0x0,0x65,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x87,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x92,0x2,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x89,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa1,0x0,0x0,
+0x0,0x16,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf5,0x4,0x0,0x0,0x11,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x38,0x0,0x0,0x0,0x56,
+0x3,0x0,0x0,0xb7,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x90,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,
+0x4d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x5,0x0,0x0,0xec,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x8e,0x5,
+0x0,0x0,0xf5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x76,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x3,0x0,0x0,0xb7,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbe,0x2,0x0,0x0,0x93,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa0,0x2,0x0,0x0,0x4e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa5,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,
+0x5,0x0,0x0,0xf,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa6,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x0,0x2,0x0,0x0,
+0x6c,0x5,0x0,0x0,0x57,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa7,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2d,0x3,0x0,0x0,0x3c,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xaf,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6c,0x3,
+0x0,0x0,0x8c,0x0,0x0,0x0,0x3e,0x1,0x0,0x0,0x87,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x20,0x2,0x0,0x0,0xf9,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb1,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x36,0x2,0x0,0x0,0x6b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x0,0x0,
+0x0,0x1f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0xf2,0x0,
+0x0,0x0,0x19,0x5,0x0,0x0,0xbe,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x69,
+0x3,0x0,0x0,0xad,0x3,0x0,0x0,0x91,0x1,0x0,0x0,0x6d,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc1,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x15,0x3,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x3,
+0x0,0x0,0x68,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x28,0x5,0x0,0x0,0x10,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0xfd,0x4,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xf8,0x1,0x0,0x0,0x96,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x3,0x3,0x0,0x0,0x65,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe4,
+0x4,0x0,0x0,0x9b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xcd,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x1,0x0,0x0,
+0x77,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x1,0x0,0x0,0x2a,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x5,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x91,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x30,0x2,0x0,0x0,0x5f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x70,0x5,0x0,
+0x0,0x42,0x5,0x0,0x0,0xef,0x4,0x0,0x0,0xa2,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x37,0x1,0x0,0x0,0x1d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb5,
+0x5,0x0,0x0,0x60,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x4,0x0,0x0,
+0x9e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0x56,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0x53,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x11,0x1,0x0,0x0,0xe0,0x0,0x0,0x0,0xa,0x2,0x0,0x0,0x4e,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x71,0x3,0x0,0x0,0xd5,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0x54,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x6,0x3,0x0,0x0,0x75,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfa,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcc,
+0x1,0x0,0x0,0x7b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xfb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x5,0x0,0x0,
+0xf5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x18,0x0,0x0,
+0x0,0x67,0x1,0x0,0x0,0xfe,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xfe,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x3,
+0x0,0x0,0xf8,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x3,0x0,0x0,0x5e,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x4e,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0xb,0x2,0x0,0x0,0xdb,0x1,0x0,
+0x0,0xf4,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6b,0x1,0x0,0x0,0x8a,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x18,0x3,0x0,0x0,0x85,
+0x0,0x0,0x0,0x13,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x5,0x0,0x0,
+0xe6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc5,0x2,0x0,0x0,0x38,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x5,0x0,0x0,0xfb,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x16,0x5,0x0,0x0,0x41,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x23,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x18,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x24,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbf,0x3,0x0,
+0x0,0x64,0x1,0x0,0x0,0x13,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe,0x3,0x0,0x0,0xa2,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2a,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe,
+0x5,0x0,0x0,0xf3,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe6,0x4,0x0,0x0,
+0xf4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x5f,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x21,0x5,0x0,0x0,0xee,0x4,0x0,0x0,0x92,0x1,
+0x0,0x0,0x1f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x1e,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x22,0x2,0x0,0x0,
+0xbb,0x2,0x0,0x0,0x7b,0x2,0x0,0x0,0xa6,0x1,0x0,0x0,0xe2,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2a,0x3,0x0,0x0,0x45,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x6a,0x1,0x0,0x0,0x2c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,
+0x5,0x0,0x0,0xf8,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x42,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,0x0,0x0,
+0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9b,0x3,0x0,0x0,0x3f,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x1c,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x9c,0x3,0x0,0x0,0x5e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x29,0x3,0x0,0x0,0xf8,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x3,0x0,
+0x0,0xdc,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcc,0x1,0x0,0x0,0xff,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x3,0x0,0x0,0xf5,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0xd9,0x4,0x0,0x0,0x7,0x5,0x0,0x0,
+0x13,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x4a,0x5,0x0,
+0x0,0x96,0x5,0x0,0x0,0xb3,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5a,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa,0x2,
+0x0,0x0,0x9d,0x1,0x0,0x0,0x89,0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x57,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x98,0x2,0x0,0x0,0x11,0x5,0x0,0x0,0x7,0x5,0x0,0x0,0x4b,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x77,0x5,0x0,0x0,0xa3,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x73,0x2,0x0,0x0,0xea,0x2,0x0,0x0,0x5a,0x5,0x0,0x0,0x22,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0x49,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xf8,0x2,0x0,0x0,0x68,0x0,0x0,0x0,0x3d,0x1,0x0,0x0,
+0xfd,0x4,0x0,0x0,0xf,0x3,0x0,0x0,0x9e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x75,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x39,0x2,0x0,0x0,0xe2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x77,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3d,0x5,
+0x0,0x0,0xb,0x0,0x0,0x0,0x45,0x3,0x0,0x0,0xd4,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x77,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x79,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd9,0x4,0x0,0x0,0x42,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xaf,0x5,0x0,
+0x0,0xec,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x5f,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xa9,0x5,0x0,0x0,0x68,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0xd5,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x2f,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x9d,0x1,0x0,0x0,0x43,0x5,0x0,0x0,
+0x41,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0xcb,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xfb,0x4,0x0,0x0,0xb,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x5,0x1,0x0,0x0,0x4b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x95,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xa1,0x0,0x0,0x0,0xe7,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x96,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x2,0x0,
+0x0,0xa2,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x5,0x0,0x0,0xae,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x50,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa0,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x78,0x3,0x0,0x0,0x15,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa5,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x5,
+0x0,0x0,0xc4,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x90,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x7d,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x8a,0x2,0x0,0x0,0xba,0x2,0x0,0x0,0x86,0x5,0x0,
+0x0,0x74,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0x6d,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x6d,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xfb,0x1,0x0,0x0,0xb2,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,
+0x36,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x28,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0x2b,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4c,0x1,0x0,0x0,0x4b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x5d,0x1,0x0,0x0,0x47,0x1,0x0,0x0,0x6f,0x0,0x0,0x0,0x77,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x82,0x5,0x0,0x0,0xfb,0x1,0x0,
+0x0,0x73,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0xf3,0x4,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x46,0x0,0x0,0x0,0x5,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd3,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3e,
+0x1,0x0,0x0,0xa0,0x5,0x0,0x0,0x2a,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x49,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xed,0x4,0x0,0x0,0x42,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd7,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7b,0x1,
+0x0,0x0,0xb7,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0x34,
+0x0,0x0,0x0,0xf7,0x4,0x0,0x0,0xe,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x78,0x3,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xdc,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x46,0x3,0x0,
+0x0,0xd1,0x2,0x0,0x0,0xaf,0x2,0x0,0x0,0x9f,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd8,0x4,0x0,0x0,0x6d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe0,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,
+0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe3,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x1,0x0,0x0,
+0x9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfc,0x4,0x0,0x0,0x42,0x5,0x0,
+0x0,0x47,0x2,0x0,0x0,0x35,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe9,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x40,0x3,
+0x0,0x0,0xcc,0x2,0x0,0x0,0xea,0x4,0x0,0x0,0x65,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x3c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x50,0x1,0x0,0x0,0x29,0x0,0x0,0x0,0x31,0x3,0x0,0x0,0x13,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe7,0x0,0x0,0x0,0xc0,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x19,0x3,0x0,0x0,0x9,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,
+0x3,0x0,0x0,0x34,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x2,0x0,0x0,
+0xc2,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x3,0x0,0x0,0xff,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x5,0x0,0x0,0x9,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x81,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3,0x2,0x0,0x0,0x98,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x3,0x0,
+0x0,0xe,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x3,0x0,0x0,0x9,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x2,0x0,0x0,0xfc,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0x4e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x14,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x28,0x2,0x0,0x0,0x42,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x17,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xed,0x2,
+0x0,0x0,0x90,0x2,0x0,0x0,0xbc,0x5,0x0,0x0,0x3b,0x0,0x0,0x0,0x27,0x3,
+0x0,0x0,0x64,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0xa4,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x37,0x0,0x0,0x0,
+0xf,0x2,0x0,0x0,0x5f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1d,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x5,0x0,
+0x0,0x56,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x3,0x0,0x0,0xa5,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,0x0,0x0,0xe,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0xbd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2c,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x4b,0x2,0x0,0x0,0xba,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0x46,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x3,0x0,0x0,0xd6,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x2a,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x6a,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x64,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x1b,0x2,0x0,0x0,0x33,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x86,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x40,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,
+0x3,0x0,0x0,0xab,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x41,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe6,0x4,0x0,0x0,
+0xa7,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0x13,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf1,0x2,0x0,0x0,0x4e,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xea,0x4,0x0,0x0,0x12,0x5,0x0,0x0,0x1e,0x3,0x0,0x0,0x2d,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x19,0x2,0x0,0x0,0x11,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc1,0x5,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x6c,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x6d,0x5,0x0,0x0,0x27,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x2,0x0,0x0,0xbe,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x2f,0x5,0x0,0x0,0x62,0x5,0x0,0x0,0x21,0x2,0x0,0x0,
+0xf3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xaa,0x3,0x0,0x0,0xc5,0x1,0x0,
+0x0,0xaf,0x0,0x0,0x0,0xff,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe4,0x0,
+0x0,0x0,0xc9,0x1,0x0,0x0,0xd1,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xa,0x5,0x0,0x0,0xf8,0x2,0x0,0x0,0x85,0x0,0x0,0x0,0x6a,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0x75,0x1,0x0,0x0,
+0x2f,0x3,0x0,0x0,0xdf,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x62,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x5,0x0,
+0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2b,0x2,0x0,0x0,0x8e,0x0,
+0x0,0x0,0xed,0x0,0x0,0x0,0x37,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x66,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,
+0x5,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x68,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x3,0x0,0x0,
+0x6,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0xcf,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf3,0x1,0x0,0x0,0x1f,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x1b,0x2,0x0,0x0,0xb2,0x1,0x0,0x0,0xd7,0x0,0x0,0x0,0x46,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa1,0x1,0x0,0x0,0xcc,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0x30,0x5,0x0,0x0,0x49,0x2,0x0,0x0,0xb6,0x5,0x0,
+0x0,0x82,0x5,0x0,0x0,0x7,0x3,0x0,0x0,0x1e,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xdb,0x1,0x0,0x0,0x32,0x3,0x0,0x0,0x51,0x0,0x0,0x0,0x7b,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x6,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x82,0x5,0x0,0x0,0x91,0x1,0x0,0x0,
+0xdc,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x2,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0xf5,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x41,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x90,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd2,0x2,0x0,0x0,0x7b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x93,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x5,0x0,
+0x0,0x92,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x84,0x5,0x0,0x0,0x25,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0xea,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0xe1,0x0,0x0,0x0,0x4,0x2,0x0,0x0,
+0x9d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x1,0x0,0x0,0x0,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x82,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4,0x3,0x0,0x0,0x97,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x92,0x2,0x0,0x0,0x25,0x1,0x0,0x0,0x27,0x2,0x0,0x0,0x4e,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf7,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb4,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1b,
+0x0,0x0,0x0,0xc3,0x2,0x0,0x0,0xdb,0x1,0x0,0x0,0x11,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1c,0x1,0x0,0x0,0xfe,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb6,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x9c,0x3,0x0,0x0,0xf5,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbc,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x11,0x1,
+0x0,0x0,0x48,0x1,0x0,0x0,0x3,0x5,0x0,0x0,0x5b,0x5,0x0,0x0,0x36,0x3,
+0x0,0x0,0x16,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0xba,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x0,0x0,0x0,0xb9,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5b,0x3,0x0,0x0,0x50,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb,0x3,0x0,0x0,0x27,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,
+0x0,0x0,0x0,0x57,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc5,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x2,0x0,0x0,
+0xd1,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x5,0x0,0x0,0x12,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0xe6,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0x56,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xce,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x10,0x4,0x0,0x0,0x44,0x5,0x0,0x0,0x1,0x1,0x0,0x0,0x3c,0x1,0x0,0x0,
+0x5e,0x2,0x0,0x0,0x21,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd2,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x2,0x0,
+0x0,0x67,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x1,0x0,0x0,0xb1,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x2,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xdc,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x6,0x2,0x0,0x0,0xd9,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xdd,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf4,0x0,
+0x0,0x0,0x79,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0xf4,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x79,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xfd,0x2,0x0,0x0,0x32,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x56,0x2,0x0,0x0,0x5d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,
+0x3,0x0,0x0,0x55,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf3,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x5,0x0,0x0,
+0x7a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0x46,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0x6,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6a,0x3,0x0,0x0,0x5b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x90,0x0,0x0,0x0,0xef,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfe,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x92,0x5,0x0,
+0x0,0xe0,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0x1d,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3c,0x1,0x0,0x0,0x15,0x3,0x0,0x0,0xa6,
+0x2,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x81,0x0,0x0,0x0,
+0x99,0x5,0x0,0x0,0x5,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x7,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc0,0x5,0x0,0x0,0xd2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x0,
+0x0,0x0,0x1d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0x42,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2b,0x5,0x0,0x0,0xe0,0x4,0x0,0x0,
+0xcf,0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x10,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x1,0x0,
+0x0,0xbe,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x35,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0xc0,0x1,0x0,0x0,0x19,0x2,0x0,0x0,
+0x19,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa5,0x5,0x0,0x0,0xec,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x37,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x85,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xbc,0x0,0x0,0x0,0xab,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x31,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4d,0x1,0x0,
+0x0,0x4b,0x2,0x0,0x0,0xed,0x4,0x0,0x0,0x36,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x3,0x1,0x0,0x0,0xa9,0x1,0x0,0x0,0x3e,0x1,0x0,0x0,0x8c,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x5,0x0,0x0,0xc,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x2f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x41,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x4b,0x5,0x0,0x0,0xf1,0x4,0x0,0x0,0x6c,0x0,0x0,0x0,0xb7,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xef,0x4,0x0,0x0,0x37,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x66,0x0,0x0,0x0,0xe,0x1,0x0,0x0,0x21,0x5,0x0,0x0,0xfe,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf1,0x4,0x0,0x0,0x2d,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xd8,0x1,0x0,0x0,0xad,0x5,0x0,0x0,0xed,0x0,0x0,
+0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0xf5,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x2,0x0,0x0,0x2b,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x9c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x53,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x6c,0x5,0x0,0x0,0x1c,0x5,0x0,0x0,0x60,0x2,0x0,0x0,0x16,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x24,0x5,0x0,0x0,0x59,0x3,
+0x0,0x0,0x37,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x73,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x91,0x5,0x0,0x0,0x44,0x3,0x0,0x0,
+0xd2,0x4,0x0,0x0,0x65,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x60,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x3,0x0,
+0x0,0x81,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x4,0x0,0x0,0x24,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0xd1,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x28,0x5,0x0,0x0,0xb6,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x68,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf7,0x1,0x0,0x0,0x2a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x69,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x2,
+0x0,0x0,0x4e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x4,0x0,0x0,0x48,
+0x5,0x0,0x0,0x28,0x5,0x0,0x0,0x1d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x43,0x0,0x0,0x0,0x35,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6e,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x0,0x0,
+0x0,0xc5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x9c,0x0,
+0x0,0x0,0x78,0x3,0x0,0x0,0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x72,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb5,
+0x5,0x0,0x0,0x8a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x75,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x28,0x2,0x0,0x0,
+0x27,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0xee,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x77,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf2,0x4,0x0,0x0,0x20,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x78,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x75,0x0,
+0x0,0x0,0x34,0x0,0x0,0x0,0x39,0x5,0x0,0x0,0xf,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x9d,0x5,0x0,0x0,0xf3,0x1,0x0,0x0,0x6c,0x0,0x0,0x0,0x5b,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x8b,0x0,0x0,0x0,
+0x4d,0x2,0x0,0x0,0x64,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8e,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x3,0x0,
+0x0,0xe,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,0xe4,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x79,0x5,0x0,0x0,0x10,0x3,0x0,0x0,0xc2,
+0x3,0x0,0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x92,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb5,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb6,0x2,0x0,0x0,0x8f,0x2,0x0,
+0x0,0xf9,0x4,0x0,0x0,0x60,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9a,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,0x3,
+0x0,0x0,0x2,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x47,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x61,0x2,0x0,0x0,
+0x5e,0x0,0x0,0x0,0x75,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6a,0x5,0x0,
+0x0,0x50,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x69,0x2,0x0,0x0,0xfb,0x4,
+0x0,0x0,0x96,0x3,0x0,0x0,0xa5,0x5,0x0,0x0,0x8f,0x5,0x0,0x0,0x37,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x2,0x0,0x0,0x3b,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x60,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb1,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x26,0x3,0x0,0x0,0x8a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb2,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x67,0x2,
+0x0,0x0,0xe,0x1,0x0,0x0,0xc8,0x0,0x0,0x0,0x23,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0x8c,0x5,0x0,0x0,0x5f,0x1,0x0,0x0,0x1e,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0x97,0x1,0x0,0x0,
+0x4d,0x1,0x0,0x0,0x37,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xbc,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,0x0,
+0x0,0xbc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,0x55,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x3,0x0,0x0,0x32,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0xe0,0x2,0x0,0x0,0xb7,0x5,0x0,0x0,
+0xd9,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1a,0x3,0x0,0x0,0x91,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x24,0x3,0x0,0x0,0xa4,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd4,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x96,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0x37,0x5,0x0,0x0,0xc6,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x70,0x3,0x0,0x0,0x3f,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x4,0x3,0x0,0x0,0x5c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xde,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x99,
+0x2,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe1,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x1,0x0,0x0,
+0xb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x98,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0x39,0x3,
+0x0,0x0,0x3e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x2,0x0,0x0,0xc9,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x24,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x22,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x10,0x4,0x0,0x0,0x69,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf5,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,
+0x5,0x0,0x0,0xf8,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf7,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x7a,0x5,0x0,0x0,
+0xf4,0x4,0x0,0x0,0xde,0x4,0x0,0x0,0x87,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf8,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0xe4,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x47,0x2,0x0,0x0,0xbf,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfd,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x5b,0x2,0x0,0x0,0xd9,0x1,0x0,0x0,0x24,0x3,0x0,0x0,0x15,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0xf3,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x2a,0x5,0x0,0x0,0xba,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,
+0x0,0x0,0x0,0x9a,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x10,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,
+0xf1,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x46,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x1,0x0,0x0,0x19,0x3,
+0x0,0x0,0x24,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xad,0x3,0x0,0x0,0x86,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x9f,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0xaa,0x3,0x0,0x0,0xf6,0x0,0x0,
+0x0,0xfc,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xeb,0x2,0x0,0x0,0xc5,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0xfd,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2f,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xb5,0x5,0x0,0x0,0xed,0x4,0x0,0x0,0xc5,0x3,0x0,0x0,0x8f,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xee,0x2,0x0,0x0,0xb4,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x44,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x84,0x5,0x0,0x0,0x73,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x45,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x2,0x0,
+0x0,0x1a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0x6a,0x2,
+0x0,0x0,0x36,0x1,0x0,0x0,0x1f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,
+0x5,0x0,0x0,0xa1,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4e,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x69,0x0,0x0,0x0,
+0x45,0x3,0x0,0x0,0xb5,0x5,0x0,0x0,0x57,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4f,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb8,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x51,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x0,
+0x0,0x0,0x8f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4d,0x2,0x0,0x0,0x81,
+0x1,0x0,0x0,0x15,0x3,0x0,0x0,0x36,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x56,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x63,0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x57,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x2,0x0,
+0x0,0x56,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0xea,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x5,0x0,0x0,0x38,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0xad,0x5,0x0,0x0,0xfa,0x4,0x0,0x0,
+0x3c,0x1,0x0,0x0,0x85,0x2,0x0,0x0,0x60,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x62,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc4,0x2,0x0,0x0,0xd1,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6e,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x3,
+0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x60,0x2,0x0,0x0,0x92,
+0x1,0x0,0x0,0xba,0x0,0x0,0x0,0x2c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x79,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7a,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x0,0x0,
+0x0,0xd5,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x7b,0x2,
+0x0,0x0,0x43,0x3,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7f,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,
+0x3,0x0,0x0,0xc1,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x84,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,
+0x78,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x0,0x0,0x0,0x36,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x65,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x2c,0x2,0x0,0x0,0xdd,0x2,0x0,0x0,0xe2,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x67,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xe,0x1,0x0,0x0,0x61,0x0,0x0,
+0x0,0x86,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0xf5,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x5f,0x2,0x0,0x0,0x70,0x0,0x0,0x0,
+0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0xa2,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0xe2,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x60,0x1,0x0,0x0,0xf3,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x1d,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0xae,0x0,0x0,0x0,
+0x1e,0x2,0x0,0x0,0x1e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa7,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd1,0x1,0x0,
+0x0,0xb2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x58,0x3,0x0,0x0,0xc7,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x43,0x5,0x0,0x0,0x9b,0x3,0x0,0x0,0x3d,
+0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x5,0x0,0x0,
+0xea,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x3,0x0,0x0,0x13,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0x55,0x2,0x0,0x0,0x79,0x3,
+0x0,0x0,0xd,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x1,0x0,0x0,0x48,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x11,0x5,0x0,0x0,
+0xbf,0x3,0x0,0x0,0x1e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc4,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8c,0x5,0x0,
+0x0,0x72,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0x2a,0x2,
+0x0,0x0,0x7,0x1,0x0,0x0,0xec,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xca,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9b,
+0x0,0x0,0x0,0x81,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd0,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xdc,0x4,0x0,0x0,
+0x84,0x0,0x0,0x0,0x45,0x3,0x0,0x0,0x45,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd1,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x59,0x2,0x0,0x0,0xdd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd4,0x2,
+0x0,0x0,0x37,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x67,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x1,0x0,0x0,0xb2,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x84,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb,0x1,0x0,0x0,0x72,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe1,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb,
+0x1,0x0,0x0,0x2,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe3,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x42,0x3,0x0,0x0,
+0x21,0x3,0x0,0x0,0xaa,0x5,0x0,0x0,0xf3,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe6,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x6a,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0xd4,0x4,0x0,0x0,0x86,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x5,0x0,0x0,0xb1,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x11,0x3,0x0,0x0,0xc3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xef,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x25,0x0,0x0,0x0,0xdf,0x1,0x0,0x0,0x3a,0x3,0x0,0x0,0x61,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x62,0x5,0x0,0x0,0xb7,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x9c,0x1,0x0,0x0,0x30,0x5,0x0,0x0,0x16,0x3,0x0,0x0,0x55,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x8c,0x0,0x0,0x0,0x86,
+0x1,0x0,0x0,0xe6,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf6,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x65,0x1,0x0,0x0,
+0x85,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf7,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xaa,0x1,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xfe,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xbb,0x3,
+0x0,0x0,0x53,0x1,0x0,0x0,0xe4,0x4,0x0,0x0,0x5,0x5,0x0,0x0,0xab,0x0,
+0x0,0x0,0x37,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x33,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7d,0x1,0x0,0x0,0x4a,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0xb,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf7,0x1,0x0,0x0,0x3b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x38,
+0x0,0x0,0x0,0xdf,0x1,0x0,0x0,0x15,0x2,0x0,0x0,0xe,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x8e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x7,0x0,0x0,0x0,0x7d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe3,0x4,
+0x0,0x0,0x58,0x1,0x0,0x0,0x6,0x3,0x0,0x0,0x70,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0xad,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x12,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x69,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x19,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x2,0x0,
+0x0,0xb2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5c,0x5,0x0,0x0,0x4c,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x2e,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x81,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x22,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x1,0x3,0x0,0x0,0x75,0x1,0x0,0x0,0x14,0x5,0x0,0x0,0xd6,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x4,0x0,0x0,0xc,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x20,0x2,0x0,0x0,0x1d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x33,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x2b,0x3,0x0,0x0,0x9d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x40,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x2,0x0,
+0x0,0x56,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd8,0x1,0x0,0x0,0x50,0x1,
+0x0,0x0,0x70,0x2,0x0,0x0,0x74,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x44,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5f,
+0x1,0x0,0x0,0xc3,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4d,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x28,0x1,0x0,0x0,
+0x4b,0x0,0x0,0x0,0x16,0x3,0x0,0x0,0x1b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4e,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x1a,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x1a,0x3,0x0,0x0,0x65,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0xe2,0x0,0x0,0x0,0xaa,0x2,
+0x0,0x0,0x14,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0xc3,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x75,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x14,0x2,0x0,0x0,0xe7,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5,0x3,0x0,0x0,0xfd,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5f,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xeb,
+0x2,0x0,0x0,0xe7,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x61,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x0,0x0,0x0,
+0x43,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0x54,0x3,0x0,
+0x0,0x24,0x2,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x69,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x2,
+0x0,0x0,0xc0,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x80,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0xa8,0x5,0x0,0x0,
+0xdd,0x4,0x0,0x0,0x51,0x5,0x0,0x0,0x17,0x3,0x0,0x0,0x45,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x85,0x2,0x0,0x0,0xff,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x40,0x0,0x0,0x0,0x2c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x75,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf1,
+0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0x73,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x42,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x82,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x5d,0x1,0x0,0x0,0x2b,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x83,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x3,
+0x0,0x0,0x34,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x94,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x8b,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x56,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x20,0x3,0x0,0x0,0x25,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x92,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5a,
+0x5,0x0,0x0,0x31,0x5,0x0,0x0,0xba,0x5,0x0,0x0,0x76,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0x52,0x1,0x0,0x0,0x46,0x1,0x0,0x0,
+0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0xaa,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x3b,0x0,0x0,0x0,0x9b,0x3,
+0x0,0x0,0xee,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x86,
+0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xfd,0x0,0x0,0x0,0x2a,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa2,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x0,0x0,
+0x0,0x4,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x5,0x0,0x0,0x4a,0x3,
+0x0,0x0,0x42,0x1,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xab,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x65,
+0x1,0x0,0x0,0x1c,0x5,0x0,0x0,0x0,0x1,0x0,0x0,0x70,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x5d,0x1,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xbb,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x6,0x3,0x0,0x0,0xc3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbe,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x5,
+0x0,0x0,0x7a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x0,0x0,0x0,0x13,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7a,0x1,0x0,0x0,0x74,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x41,0x2,0x0,0x0,0xe9,0x2,0x0,0x0,0x77,0x3,0x0,0x0,0x22,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7a,0x2,0x0,0x0,0xde,0x4,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x14,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd7,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x99,0x3,0x0,0x0,0x59,0x5,0x0,0x0,0xbb,0x1,0x0,0x0,0xc3,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0x4,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xde,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xdf,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x4,0x0,
+0x0,0x26,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x90,0x5,
+0x0,0x0,0xf4,0x0,0x0,0x0,0xea,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf2,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1e,
+0x3,0x0,0x0,0xb9,0x5,0x0,0x0,0x1f,0x0,0x0,0x0,0xab,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdb,0x4,0x0,0x0,0xe9,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf7,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x14,0x2,0x0,0x0,0x2f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf8,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x5,
+0x0,0x0,0x6b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x57,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x8,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x5,0x0,0x0,0xb1,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x8,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xab,0x5,0x0,0x0,0x95,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xfc,0x0,0x0,0x0,0x25,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb9,
+0x0,0x0,0x0,0x38,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0x3c,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xad,0x3,0x0,0x0,0xb2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xd2,0x2,0x0,0x0,0x49,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0xb3,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x57,0x2,0x0,0x0,0xd,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x40,0x3,0x0,0x0,0x5c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1c,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x77,0x0,0x0,
+0x0,0x1e,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0xc1,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf5,0x4,0x0,0x0,0x16,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x22,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,
+0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x24,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x48,0x2,0x0,0x0,
+0xbc,0x1,0x0,0x0,0x8e,0x5,0x0,0x0,0x4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2d,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x7f,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x31,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb6,0x5,
+0x0,0x0,0xb9,0x3,0x0,0x0,0x37,0x5,0x0,0x0,0x2b,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4d,0x2,0x0,0x0,0x59,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x38,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x54,0x5,0x0,0x0,0x31,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x39,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,0x0,0x0,
+0x0,0xe2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x8,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x2,0x0,0x0,0x89,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x8,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0x3f,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0xd4,0x4,0x0,0x0,0x52,0x2,0x0,0x0,
+0xa6,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x8,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x2,0x0,0x0,0x6a,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x0,0x0,0x0,0xa1,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x94,0x3,0x0,0x0,0x26,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe2,0x4,0x0,0x0,0x31,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x54,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4c,0x1,0x0,
+0x0,0x45,0x3,0x0,0x0,0x4d,0x1,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x70,0x3,0x0,0x0,0x68,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x58,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,
+0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x60,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x43,0x3,0x0,0x0,
+0xe6,0x2,0x0,0x0,0xde,0x4,0x0,0x0,0x57,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x69,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x31,0x5,0x0,0x0,0xd1,0x2,0x0,0x0,0x61,0x2,0x0,0x0,0x61,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x5c,0x2,0x0,0x0,0x11,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x0,0x2,0x0,0x0,0xf5,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x70,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf0,0x1,0x0,0x0,0xb2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x71,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x3,0x0,
+0x0,0x29,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x8,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x3,0x0,0x0,0x39,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x8,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x3,0x0,0x0,0xc3,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x2f,0x5,0x0,0x0,0x68,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x78,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x24,0x3,0x0,0x0,0x4c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x82,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x3,
+0x0,0x0,0xff,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,
+0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0xe9,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x8,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0xf6,0x4,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x8,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x26,0x3,0x0,0x0,0xe,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x98,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x68,
+0x5,0x0,0x0,0x2c,0x0,0x0,0x0,0x5,0x1,0x0,0x0,0x47,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0xe8,0x1,0x0,0x0,
+0xa4,0x3,0x0,0x0,0xf7,0x4,0x0,0x0,0xe9,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9c,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x2b,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0xc,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x2,0x0,0x0,0xef,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6c,0x2,0x0,0x0,0x6b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x41,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa7,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x5,0x0,
+0x0,0x2d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x8,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0x7d,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x8,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa1,0x1,0x0,0x0,0xc9,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x48,0x2,0x0,0x0,0x61,0x5,0x0,0x0,0x90,0x0,0x0,0x0,
+0xb4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x8,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x48,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0xba,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x15,0x5,0x0,0x0,0x7,0x3,0x0,0x0,0xb9,0x0,0x0,0x0,0xc3,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x8,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x0,0x0,0x0,0xc5,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x8,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0x47,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x10,0x4,0x0,0x0,0x3,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc9,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,
+0x0,0x0,0x0,0xa4,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xcc,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x2,0x0,0x0,
+0x1d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x8,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x61,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x24,0x5,0x0,0x0,0x47,0x5,
+0x0,0x0,0xe,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,
+0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x2,0x0,0x0,0x6c,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x8,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x23,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x8,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x37,0x5,0x0,0x0,0x38,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xff,0x4,0x0,0x0,0xfc,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdf,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,
+0x3,0x0,0x0,0xae,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe1,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1b,0x3,0x0,0x0,
+0xff,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x8,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0xc0,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x61,0x2,0x0,0x0,0x4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xea,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x42,0x1,0x0,0x0,0x4e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xeb,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x33,0x2,0x0,
+0x0,0x68,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x8,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,0x2e,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x8,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0xff,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1b,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3a,0x5,0x0,0x0,0x6d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x5,
+0x0,0x0,0x48,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,
+0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x65,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x9,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x22,0x3,0x0,0x0,0x70,0x1,0x0,0x0,
+0xf9,0x4,0x0,0x0,0xdf,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x0,0x0,
+0x0,0x10,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x9,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x2,0x0,0x0,0xc0,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x8,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x9,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x2f,0x1,0x0,0x0,0x46,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x12,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x95,0x3,0x0,0x0,0x5f,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x14,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x3,
+0x0,0x0,0x50,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,
+0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0xfc,
+0x0,0x0,0x0,0x29,0x3,0x0,0x0,0x7d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x22,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x9f,0x5,0x0,0x0,0x2a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2b,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x40,0x0,0x0,
+0x0,0x2c,0x3,0x0,0x0,0xf1,0x1,0x0,0x0,0x96,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x50,0x0,0x0,0x0,0xa7,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x39,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,
+0x4,0x0,0x0,0x68,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3b,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x2,0x0,0x0,
+0xef,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0x26,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x5,0x0,0x0,0xb7,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc6,0x3,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x42,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xff,0x2,0x0,0x0,0xef,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x48,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7a,0x2,0x0,
+0x0,0x31,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x9,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0x7,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x1e,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x9,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0x18,0x1,0x0,0x0,0x27,0x0,0x0,0x0,
+0x39,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x5,0x0,0x0,0x24,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x71,0x1,0x0,0x0,0x3,0x2,0x0,0x0,0x67,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x9,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x7,0x1,0x0,0x0,0x49,0x2,0x0,0x0,
+0x9b,0x1,0x0,0x0,0x5f,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x60,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x5,0x0,
+0x0,0xdf,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x9,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x5,0x0,0x0,0xec,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x3,0x0,0x0,0x56,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x9,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x49,0x1,0x0,0x0,0x59,0x0,0x0,0x0,0xac,0x1,0x0,0x0,
+0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0x45,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe,0x5,0x0,0x0,0x3,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0xc1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7a,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x89,0x5,0x0,0x0,0x8,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x81,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc4,0x3,0x0,
+0x0,0x56,0x1,0x0,0x0,0x64,0x2,0x0,0x0,0xce,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x38,0x1,0x0,0x0,0x41,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x86,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,
+0x3,0x0,0x0,0xc0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x88,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x3,0x0,0x0,
+0x13,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0xe0,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xa5,0x5,0x0,0x0,0x57,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x94,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x37,0x1,0x0,0x0,0xf5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x98,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x3,0x0,
+0x0,0xbd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x9,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6a,0x5,0x0,0x0,0xfd,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4,0x3,0x0,0x0,0x59,0x4,0x0,0x0,0x4d,
+0x0,0x0,0x0,0xf,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa9,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0xd1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x90,0x5,0x0,0x0,0x80,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0x32,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x33,0x1,0x0,0x0,0x1f,0x1,0x0,0x0,0x27,0x5,0x0,0x0,0xf8,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x9,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x2,0x0,0x0,0x80,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x9,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0x66,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xb5,0x0,0x0,0x0,0x11,0x5,0x0,0x0,0x26,0x3,0x0,0x0,0x8c,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x22,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x9,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x9c,0x1,0x0,0x0,0x2b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd6,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3b,0x1,0x0,0x0,0x70,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd9,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x0,
+0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,
+0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0x63,
+0x2,0x0,0x0,0x9f,0x5,0x0,0x0,0xea,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdb,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xce,0x2,0x0,0x0,0x4e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xde,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,0x2,0x0,
+0x0,0x26,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x9,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0xb9,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x59,0x2,0x0,0x0,0x90,0x1,0x0,0x0,0x7b,
+0x0,0x0,0x0,0xc6,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe6,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,
+0x1d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf4,0x4,0x0,0x0,0x1e,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0xea,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x14,0x5,0x0,0x0,0x66,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf1,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x96,0x0,0x0,0x0,0x71,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf2,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9a,0x3,0x0,
+0x0,0xba,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x9,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0x9e,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x3,0x0,0x0,0x2d,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x9,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xfa,0x2,0x0,0x0,0x21,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xff,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2a,0x3,0x0,0x0,0x3a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x23,0x3,
+0x0,0x0,0x6a,0x2,0x0,0x0,0xdc,0x2,0x0,0x0,0x5e,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x17,0x5,0x0,0x0,0xb9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x11,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x1a,0x1,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x14,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x61,0x2,0x0,
+0x0,0x75,0x1,0x0,0x0,0xe2,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x16,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x3f,0x2,0x0,0x0,0xce,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x18,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,
+0x2,0x0,0x0,0xe9,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1d,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x1,0x0,0x0,
+0x33,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xa,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x19,0x2,0x0,0x0,0x8,0x0,0x0,
+0x0,0x8a,0x2,0x0,0x0,0xe6,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x22,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x2,
+0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x45,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x1,0x0,0x0,0x90,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xfa,0x2,0x0,0x0,0x20,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1f,0x5,0x0,0x0,0xeb,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2d,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4a,
+0x2,0x0,0x0,0xc6,0x2,0x0,0x0,0x8e,0x0,0x0,0x0,0xf,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x67,0x5,0x0,0x0,0xeb,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x31,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x94,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x33,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x1,
+0x0,0x0,0x78,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbd,0x5,0x0,0x0,0xcb,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0xb,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x16,0x5,0x0,0x0,0xec,0x4,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe1,0x4,0x0,0x0,0x3d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x38,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,
+0x2,0x0,0x0,0xb3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3a,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x1,0x0,0x0,
+0x46,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0xa,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x8b,0x5,0x0,
+0x0,0x2d,0x1,0x0,0x0,0x96,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3e,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe9,0x0,
+0x0,0x0,0xdb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0x33,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x5,0x0,0x0,0x21,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3b,0x3,0x0,0x0,0xc9,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x46,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x4b,0x2,0x0,0x0,0xc3,0x2,0x0,0x0,0xb3,0x0,0x0,0x0,0x76,0x1,
+0x0,0x0,0x4,0x3,0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5b,
+0x3,0x0,0x0,0x7,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4b,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x1,0x0,0x0,
+0x6d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xa,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x3,0x0,0x0,0x43,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xa,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xe5,0x4,0x0,0x0,0x92,0x1,0x0,0x0,0x57,0x3,
+0x0,0x0,0x33,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x5,0x0,0x0,0xd2,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x3,0x0,0x0,0x73,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x22,0x3,0x0,0x0,0x1e,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x56,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd1,0x1,0x0,0x0,0xbc,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x58,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf0,
+0x1,0x0,0x0,0x18,0x0,0x0,0x0,0x6a,0x1,0x0,0x0,0x63,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x3,0x1,0x0,0x0,0x84,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x61,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x61,0x1,0x0,0x0,0x3c,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x64,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x2,
+0x0,0x0,0x4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdf,0x4,0x0,0x0,0x37,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0xf1,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x2,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x82,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x40,0x1,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x83,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x59,
+0x2,0x0,0x0,0xe8,0x4,0x0,0x0,0xf,0x5,0x0,0x0,0x7f,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0x7a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x85,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x22,0x3,0x0,0x0,0xf8,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x88,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x3,
+0x0,0x0,0x81,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x3,0x0,0x0,0xb,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x2,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x43,0x5,0x0,0x0,0x70,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x93,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa1,0x5,0x0,0x0,0x20,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x95,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,
+0x0,0x0,0x0,0x19,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x99,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x3,0x0,0x0,
+0x2e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0xa,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x7b,0x2,0x0,
+0x0,0x71,0x3,0x0,0x0,0x16,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9f,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x5,
+0x0,0x0,0x52,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x4,0x0,0x0,0x4c,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0xe0,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb1,0x2,0x0,0x0,0x64,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x7f,0x1,0x0,0x0,0x97,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6c,
+0x2,0x0,0x0,0x39,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x2b,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0xf5,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb4,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2f,0x1,0x0,0x0,0x43,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb5,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x0,
+0x0,0x0,0x10,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x5b,
+0x2,0x0,0x0,0xfb,0x0,0x0,0x0,0x8f,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb9,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x21,0x3,0x0,0x0,0xed,0x4,0x0,0x0,0xbb,0x2,0x0,0x0,0xb7,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,0x54,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x69,0x0,0x0,0x0,0x43,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfb,
+0x0,0x0,0x0,0xb1,0x0,0x0,0x0,0x35,0x3,0x0,0x0,0x5,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x23,0x2,0x0,0x0,0xda,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xcb,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xfc,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x65,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0xa,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x30,0x3,0x0,0x0,0x60,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd4,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x8f,0x5,0x0,0x0,0x8a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd9,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa,0x1,0x0,
+0x0,0x42,0x2,0x0,0x0,0x6a,0x0,0x0,0x0,0x25,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x6c,0x2,0x0,0x0,0xf9,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0xfd,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xa,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x3,0x0,0x0,0x69,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf,0x3,0x0,0x0,0xe,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf8,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe4,0x0,0x0,0x0,0x44,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xfa,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x89,0x0,
+0x0,0x0,0xc0,0x1,0x0,0x0,0x15,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xed,0x0,0x0,0x0,0xd4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xfe,0x0,0x0,0x0,0x43,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x78,0x3,0x0,
+0x0,0xc7,0x1,0x0,0x0,0x90,0x0,0x0,0x0,0x63,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe8,0x1,0x0,0x0,0x4e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,
+0x2,0x0,0x0,0x6b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5b,0x3,0x0,0x0,
+0x75,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xb,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x19,0x5,0x0,0x0,0x22,0x5,0x0,
+0x0,0xe,0x2,0x0,0x0,0x3e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x10,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x71,0x1,
+0x0,0x0,0x64,0x5,0x0,0x0,0x8c,0x2,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x51,0x0,0x0,0x0,0xbb,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x15,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3b,0x1,0x0,0x0,0xad,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x19,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x5,0x0,
+0x0,0x9e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x5,0x0,0x0,0xec,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xb,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x8a,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0xb,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x23,0x1,0x0,0x0,0xd9,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x25,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4a,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x26,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x5,
+0x0,0x0,0xbf,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,
+0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9b,0x3,0x0,0x0,0x70,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xb,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x2,0x0,0x0,0x4d,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xb,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3b,0x3,0x0,0x0,0xb7,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x70,0x5,0x0,0x0,0x49,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2e,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,
+0x2,0x0,0x0,0xc4,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x30,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x98,0x0,0x0,0x0,
+0x6d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xb,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0x61,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0xb,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0xc2,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x74,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe0,0x4,0x0,0x0,0x65,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3c,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x1,0x0,
+0x0,0xfc,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x18,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0xb,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x99,0x3,0x0,0x0,0xe7,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0xb,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x54,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x20,0x3,0x0,0x0,0x90,0x2,0x0,0x0,0x45,0x1,0x0,0x0,0x8e,0x5,0x0,
+0x0,0x28,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x55,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x1,
+0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,
+0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x1,0x0,0x0,0x9a,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0xb,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x55,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0xb,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0x6d,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x2a,0x2,0x0,0x0,0x7,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x64,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,
+0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x6e,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x5,0x0,0x0,
+0x42,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0xb,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xce,0x0,0x0,0x0,0x5c,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0xb,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x7b,0x5,0x0,0x0,0x6c,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xca,0x1,0x0,0x0,0x9f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7e,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x59,0x1,0x0,0x0,0xd1,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x80,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,
+0x0,0x41,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0xe9,0x2,
+0x0,0x0,0x68,0x3,0x0,0x0,0xa1,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x86,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,
+0x0,0x0,0x0,0xe7,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x87,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa1,0x1,0x0,0x0,
+0x24,0x1,0x0,0x0,0xac,0x1,0x0,0x0,0x77,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x8b,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe0,0x2,0x0,0x0,0xc4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8e,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x28,0x0,
+0x0,0x0,0x2f,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x67,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x92,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x78,0x3,0x0,0x0,0x4e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x93,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x5,0x0,
+0x0,0xf7,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6a,0x3,0x0,0x0,0xe3,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xb,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0x42,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0xb,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x60,0x2,0x0,0x0,0x45,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xaa,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa8,0x5,0x0,0x0,0xbc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xae,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x3,
+0x0,0x0,0x9d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,
+0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x54,0x3,0x0,0x0,0x59,
+0x4,0x0,0x0,0x2d,0x1,0x0,0x0,0x53,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb1,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xb2,0x2,0x0,0x0,0x8c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb3,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,0x3,0x0,
+0x0,0x64,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0xd3,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0xb,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x3,0x0,0x0,0x94,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xb,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x98,0x5,0x0,0x0,0x18,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xbf,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x23,0x3,0x0,0x0,0x49,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc5,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x3,
+0x0,0x0,0x0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,
+0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x98,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0xb,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x4,0x0,0x0,0x6f,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0xb,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x4c,0x1,0x0,0x0,0xd5,0x2,0x0,0x0,0xf7,0x0,0x0,
+0x0,0x99,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,0xf3,0x4,
+0x0,0x0,0x37,0x5,0x0,0x0,0xbf,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdc,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,
+0x2,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe1,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,
+0x1d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xb,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0x82,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xb,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x2,0x0,0x0,0xc3,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x50,0x3,0x0,0x0,0x98,0x1,0x0,0x0,0x86,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0xb,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x56,0x2,0x0,0x0,
+0x7a,0x0,0x0,0x0,0xd5,0x1,0x0,0x0,0xaf,0x5,0x0,0x0,0xd6,0x4,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0xb,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x8e,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x40,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,
+0x3,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x3,0x0,0x0,
+0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0xe4,0x4,0x0,
+0x0,0x26,0x3,0x0,0x0,0xd5,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x16,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa0,0x2,
+0x0,0x0,0xc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x0,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x26,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x58,0x3,0x0,0x0,0xdd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x23,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,
+0x5,0x0,0x0,0x17,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x26,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x0,0x0,0x0,
+0x27,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x1,0x0,0x0,0x84,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x66,0x2,0x0,0x0,0x49,0x1,0x0,0x0,0xa0,0x2,
+0x0,0x0,0x6d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0x2d,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x99,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x87,0x0,0x0,0x0,0x18,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,
+0x1,0x0,0x0,0x81,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x42,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x3,0x0,0x0,
+0x1d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x29,0x5,0x0,0x0,0xf0,0x4,0x0,
+0x0,0x7f,0x0,0x0,0x0,0x41,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x44,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xb6,0x0,
+0x0,0x0,0xa1,0x5,0x0,0x0,0xc7,0x1,0x0,0x0,0xea,0x4,0x0,0x0,0x29,0x2,
+0x0,0x0,0xd,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0x75,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,0x15,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x43,0x1,0x0,0x0,0x5e,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xcf,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4c,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,
+0x1,0x0,0x0,0x1b,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x52,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x0,0x0,0x0,
+0x73,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x2c,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0xe,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x90,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5b,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xcf,0x0,0x0,0x0,0xb,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5d,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x3,0x0,
+0x0,0x67,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0xc,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0xd5,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xc,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x3,0x0,0x0,0x38,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xc,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0x95,0x3,0x0,0x0,
+0x22,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0xaa,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x1,0x0,0x0,0xb7,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x72,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x92,0x3,0x0,0x0,0x22,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x73,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x18,0x0,0x0,
+0x0,0xaf,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x1f,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x74,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x54,0x0,0x0,0x0,0x2b,0x2,0x0,0x0,0xb6,0x5,0x0,0x0,0x24,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0xc,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x1d,0x2,0x0,0x0,0xa5,
+0x0,0x0,0x0,0x77,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7a,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x0,0x0,0x0,
+0xe7,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x33,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x5,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xea,0x4,0x0,0x0,0x74,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8b,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd,0x0,0x0,0x0,0x24,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8e,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2e,0x0,0x0,
+0x0,0xd5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0xc,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x6a,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xc,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0xc,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x76,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x97,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xbb,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x99,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,
+0x0,0x0,0x59,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x28,0x5,0x0,0x0,0x4a,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x2,0x0,0x0,0x91,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x48,0x2,0x0,0x0,0x62,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x49,0x5,0x0,0x0,0xe6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa2,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa1,
+0x1,0x0,0x0,0x17,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa3,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x5,0x0,0x0,
+0xd8,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x71,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xac,0x1,0x0,0x0,0x35,0x3,0x0,0x0,0x63,0x0,0x0,0x0,0x37,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x5,0x0,0x0,0x45,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb2,0x2,0x0,0x0,0xc1,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbc,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,
+0x1,0x0,0x0,0x0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbe,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x18,0x3,0x0,0x0,
+0x4b,0x5,0x0,0x0,0x98,0x2,0x0,0x0,0x34,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc2,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd2,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc3,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x1,
+0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x1,0x0,0x0,0xc6,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x33,0x1,0x0,0x0,0x93,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x47,0x2,0x0,0x0,0xbf,0x3,0x0,
+0x0,0xdf,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0xc,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x5,0x0,0x0,0x23,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xc,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0x92,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xc,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0x22,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd1,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x30,0x1,0x0,0x0,0x36,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd3,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1b,0x3,
+0x0,0x0,0x7a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x2,0x0,0x0,0xa3,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x3,0x0,0x0,0x61,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0x65,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x87,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xee,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,
+0x2,0x0,0x0,0x61,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xef,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x0,0x0,0x0,
+0x25,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x12,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x5,0x0,0x0,0xa3,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0xb1,0x5,0x0,0x0,0x7,0x3,0x0,0x0,0xbd,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x5,0x0,0x0,0x22,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xd,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x4c,0x2,0x0,
+0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0xbd,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0xb,0x2,0x0,0x0,0xe8,
+0x0,0x0,0x0,0xd5,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x43,0x5,0x0,0x0,
+0xf9,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0x94,0x0,0x0,0x0,0x86,0x0,0x0,0x0,
+0x47,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xd,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0x1d,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x3,0x0,0x0,0x10,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x65,0x5,0x0,0x0,0xd2,0x0,0x0,0x0,0xd9,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xd,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xad,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xd,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x28,0x5,0x0,0x0,0x4,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x3,0x2,0x0,0x0,0x20,0x3,0x0,0x0,0x95,0x3,0x0,0x0,0x15,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x1,0x0,0x0,0x13,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xd,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0x9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x29,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x69,0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x8e,0x2,0x0,0x0,0x90,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xa7,0x5,0x0,0x0,0xdd,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x31,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x3,0x1,0x0,0x0,0xa2,0x5,0x0,0x0,0x5f,0x0,0x0,0x0,0x67,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0xd,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc0,0x5,0x0,0x0,0xa6,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa,0x3,0x0,0x0,0x59,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x34,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa5,
+0x0,0x0,0x0,0x59,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x36,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x0,0x0,0x0,
+0xac,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xd,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x3,0x0,0x0,0x2,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x3,0x0,0x0,0x36,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xba,0x2,0x0,0x0,0xcf,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3c,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x5e,0x5,0x0,0x0,0x47,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x40,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x38,0x2,0x0,
+0x0,0x7e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x98,0x5,
+0x0,0x0,0xcc,0x1,0x0,0x0,0x43,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,
+0x2,0x0,0x0,0x89,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4e,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x3,0x0,0x0,
+0x5e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0xd,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x19,0x3,0x0,0x0,0x4c,0x5,0x0,
+0x0,0xb5,0x5,0x0,0x0,0xf4,0x2,0x0,0x0,0x27,0x0,0x0,0x0,0x47,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x82,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xab,0x2,0x0,0x0,0xe0,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x57,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf1,0x0,0x0,0x0,0xda,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x58,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x1,0x0,
+0x0,0x86,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0xe0,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x5,0x0,0x0,0x20,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0xd,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x8e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x61,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x6,0x2,0x0,0x0,0xf4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x62,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x34,0x5,
+0x0,0x0,0xf8,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,
+0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x8f,
+0x5,0x0,0x0,0xa1,0x0,0x0,0x0,0xde,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x67,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc5,0x3,0x0,0x0,0x16,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x69,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x3,0x0,
+0x0,0xb2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x71,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0xd7,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0xd,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x6,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x7d,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd8,0x1,0x0,0x0,0x13,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7e,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x4,
+0x0,0x0,0x38,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,
+0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x97,0x5,0x0,0x0,0x55,
+0x1,0x0,0x0,0xb7,0x3,0x0,0x0,0x6,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x84,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x6c,0x2,0x0,0x0,0x57,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x87,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x3,0x0,
+0x0,0x68,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x2,0x0,0x0,0x84,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0xbd,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0xd,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x47,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x98,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xa9,0x2,0x0,0x0,0x53,0x2,0x0,0x0,0x74,0x0,0x0,0x0,0x25,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x51,0x1,0x0,0x0,0xb0,0x3,0x0,0x0,0xfa,0x4,
+0x0,0x0,0x51,0x2,0x0,0x0,0x95,0x1,0x0,0x0,0x76,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x7,0x0,0x0,0x0,0xa9,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa2,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xef,0x2,0x0,0x0,0xc2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa8,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6b,0x5,0x0,
+0x0,0x65,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0x2c,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x4e,0x2,0x0,0x0,0x9d,
+0x5,0x0,0x0,0x73,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb6,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x91,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xd,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x3,0x0,0x0,0x9e,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0x91,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc3,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xbb,0x0,0x0,0x0,0x5c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc7,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbb,0x3,0x0,
+0x0,0xa2,0x0,0x0,0x0,0x74,0x5,0x0,0x0,0x7b,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xbe,0x3,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcc,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x48,
+0x3,0x0,0x0,0xd1,0x2,0x0,0x0,0x18,0x5,0x0,0x0,0x12,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xd,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x55,0x1,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xcf,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd2,0x2,0x0,0x0,0x58,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd4,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x0,
+0x0,0x0,0xfe,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,
+0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0x2a,
+0x1,0x0,0x0,0xfa,0x2,0x0,0x0,0xf,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdc,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xfd,0x4,0x0,0x0,0xa6,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xdd,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x3,0x0,
+0x0,0x7a,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x2,0x0,0x0,0xf8,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0xeb,0x1,0x0,0x0,0x2f,
+0x5,0x0,0x0,0xf0,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe4,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x55,0x0,0x0,0x0,
+0x60,0x0,0x0,0x0,0x86,0x2,0x0,0x0,0x77,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe6,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x43,0x0,0x0,0x0,0xc0,0x3,0x0,0x0,0xab,0x2,0x0,0x0,0xf6,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0xcc,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfa,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x14,0x5,0x0,0x0,0xf4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfd,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbf,0x2,0x0,
+0x0,0x23,0x1,0x0,0x0,0x1f,0x5,0x0,0x0,0xdd,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x4,0x5,0x0,0x0,0xfa,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x31,
+0x1,0x0,0x0,0x4d,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x66,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x7,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x31,0x3,0x0,0x0,0xc5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2a,0x3,
+0x0,0x0,0x51,0x5,0x0,0x0,0xb5,0x5,0x0,0x0,0x9,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0x9d,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x60,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x19,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x69,0x1,0x0,0x0,0xdc,0x4,0x0,
+0x0,0x1d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x0,0x0,0x0,0xa9,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x1,0x0,0x0,0x80,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0x26,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1f,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x73,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x20,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x1,
+0x0,0x0,0x7a,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x67,0x2,0x0,0x0,0x4e,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x38,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xd7,0x2,0x0,0x0,0xe7,0x2,0x0,0x0,0x69,0x3,0x0,
+0x0,0x57,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x5f,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0x71,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0xc4,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3e,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb,0x3,0x0,0x0,0x50,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3f,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x1,
+0x0,0x0,0xe1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0x68,
+0x1,0x0,0x0,0xb7,0x3,0x0,0x0,0x25,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x47,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x16,0x5,0x0,0x0,0x41,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x48,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x5,0x0,
+0x0,0xf0,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x99,0x2,0x0,0x0,0xc4,0x0,
+0x0,0x0,0xf2,0x4,0x0,0x0,0x66,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4d,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,
+0x3,0x0,0x0,0x5f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x53,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,
+0x11,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xe,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x5,0x0,0x0,0x1d,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xe,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x43,0x3,0x0,0x0,0x91,0x0,0x0,0x0,0xff,0x4,
+0x0,0x0,0x9d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x86,0x5,0x0,0x0,0xf8,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x10,0x2,0x0,0x0,
+0x2a,0x3,0x0,0x0,0xc0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5c,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x3,0x0,
+0x0,0xab,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x71,0x5,0x0,0x0,0xa1,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x1d,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xde,0x2,0x0,0x0,0xf4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x67,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x68,0x1,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6a,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x0,
+0x0,0x0,0x91,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0x84,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x46,0x3,0x0,0x0,0x1e,0x0,0x0,0x0,0xbb,0x5,0x0,
+0x0,0xf5,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x3,0x0,0x0,0x7d,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x29,0x0,0x0,0x0,0x26,
+0x5,0x0,0x0,0x6a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x82,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf,0x3,0x0,0x0,
+0x8,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x85,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf6,0x2,0x0,0x0,0xf4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x90,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbd,0x1,
+0x0,0x0,0x15,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe8,0x1,0x0,0x0,0x22,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0xf3,0x1,0x0,0x0,
+0x72,0x2,0x0,0x0,0xe,0x2,0x0,0x0,0xb6,0x5,0x0,0x0,0xf,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x18,0x2,0x0,0x0,0x47,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x27,0x3,0x0,0x0,0x77,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9e,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,
+0x5,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x9f,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,
+0xc1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0xe,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x64,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xe,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0xe7,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd6,0x4,0x0,0x0,0x4f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa8,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xed,0x2,0x0,0x0,0xcc,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xab,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x0,0x0,
+0x0,0x81,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0x4b,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x2,0x0,0x0,0x22,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0xed,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb7,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x92,0x2,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbc,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x0,
+0x0,0x0,0x96,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x1,0x0,0x0,0x1e,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x5c,0x0,0x0,0x0,
+0xd8,0x4,0x0,0x0,0x66,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc1,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x3,0x0,
+0x0,0x82,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x3d,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0xd5,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x59,0x3,0x0,0x0,0xeb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xce,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x73,0x2,0x0,0x0,0x22,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd2,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x5,
+0x0,0x0,0xee,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0x40,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0x98,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd9,0x4,0x0,0x0,0x26,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xde,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa1,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe2,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,
+0x3,0x0,0x0,0x6a,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe3,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xee,0x0,0x0,0x0,
+0x4e,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0xe,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x6,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0xe,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x3,0x0,0x0,0xce,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x20,0x2,0x0,0x0,0xd2,0x1,0x0,0x0,0x9e,0x0,0x0,0x0,0xd6,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x3,0x0,0x0,0x4b,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x44,0x1,0x0,0x0,0x8e,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xef,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x41,0x3,0x0,0x0,0x49,0x3,0x0,0x0,0x5a,0x2,0x0,0x0,0xc4,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x5,0x0,0x0,0x3e,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x21,0x2,0x0,0x0,0x0,0x3,0x0,0x0,0x22,0x2,0x0,0x0,
+0x34,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xe,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0x1e,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0xe,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0xc,0x5,0x0,0x0,0x53,0x2,
+0x0,0x0,0x21,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x3,0x0,0x0,0x48,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x49,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x15,0x5,0x0,0x0,0x39,0x5,0x0,0x0,0x47,0x1,0x0,
+0x0,0xe9,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x0,0x0,0x0,0xc,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x6b,0x1,0x0,0x0,0x1f,
+0x1,0x0,0x0,0x75,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x92,0x2,0x0,0x0,
+0x72,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb,0x3,0x0,0x0,0x7b,0x0,0x0,
+0x0,0xbc,0x3,0x0,0x0,0x5e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x12,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf1,0x0,
+0x0,0x0,0x7d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0x1d,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0xf,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,0x56,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0xf,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc5,0x3,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x86,0x2,0x0,0x0,0xd5,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x23,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xd8,
+0x0,0x0,0x0,0x49,0x1,0x0,0x0,0x98,0x1,0x0,0x0,0x42,0x0,0x0,0x0,0x3,
+0x3,0x0,0x0,0xd6,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x24,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe0,0x2,0x0,0x0,
+0xfc,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0xf,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xba,0x5,0x0,0x0,0xba,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0xf,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0xb9,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x97,0x5,0x0,0x0,0xd8,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2a,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf,0x1,0x0,0x0,0x2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x31,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x0,0x0,
+0x0,0x8c,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0xf3,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb5,0x5,0x0,0x0,0x41,0x3,0x0,0x0,0xf7,
+0x1,0x0,0x0,0x7f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x35,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,
+0xfc,0x4,0x0,0x0,0x1d,0x3,0x0,0x0,0x1d,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3d,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x52,0x3,0x0,0x0,0xc3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x40,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x3,
+0x0,0x0,0xf,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x82,
+0x5,0x0,0x0,0x1b,0x2,0x0,0x0,0x9d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x42,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x7c,0x0,0x0,0x0,0x4a,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x44,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1a,0x5,0x0,
+0x0,0x19,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0xb,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x47,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x39,0x3,0x0,0x0,0x8d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x51,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4a,
+0x2,0x0,0x0,0xbb,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x61,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x1,0x0,0x0,
+0x96,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0xf,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x97,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0xf,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x3,0x0,0x0,0x2e,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0xcc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x66,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x10,0x4,0x0,0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x69,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe,0x5,0x0,
+0x0,0x13,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0x2f,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x2,0x0,0x0,0xae,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0xf,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x83,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x47,0x3,0x0,0x0,0x75,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x84,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x13,0x5,
+0x0,0x0,0x2e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x1,0x0,0x0,0x36,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0xf,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa,0x1,0x0,0x0,0x43,0x0,0x0,0x0,
+0x7a,0x1,0x0,0x0,0xcc,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x90,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x5,0x0,
+0x0,0xea,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x1d,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x20,0x1,0x0,0x0,0x73,
+0x2,0x0,0x0,0x7a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x98,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,
+0xcd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0xf,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0xd2,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0xf,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x1,0x3,0x0,0x0,0xda,0x0,0x0,0x0,0xa,0x2,
+0x0,0x0,0xa2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0xe4,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0xf,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x73,0x5,0x0,0x0,0xe6,0x2,0x0,0x0,
+0xf3,0x4,0x0,0x0,0xb7,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa5,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x5,0x0,
+0x0,0xe,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb5,0x5,0x0,0x0,0xe0,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x98,0x5,0x0,0x0,0xeb,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0xf,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x91,0x5,0x0,0x0,0x20,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xaa,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x1d,0x5,0x0,0x0,0x6c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xab,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xad,0x3,
+0x0,0x0,0x13,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x3,0x0,0x0,0x15,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0xf,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0xa0,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0xf,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x9b,0x1,0x0,0x0,0x5a,0x1,0x0,0x0,0xed,0x0,0x0,
+0x0,0x6f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0xde,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x5,0x0,0x0,0x0,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xf,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0x3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xbb,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa1,0x0,0x0,0x0,0xdb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbc,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc4,0x3,
+0x0,0x0,0x2a,0x1,0x0,0x0,0xfe,0x2,0x0,0x0,0x8b,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x34,0x5,0x0,0x0,0xe3,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xbe,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x8b,0x5,0x0,0x0,0x9a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xbf,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5c,0x3,0x0,
+0x0,0x6,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x49,0x1,
+0x0,0x0,0xfd,0x1,0x0,0x0,0xa3,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcb,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,
+0x1,0x0,0x0,0xd8,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xcc,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,
+0xc,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0xf,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0xf6,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xf,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0xb2,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0xe0,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd4,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe8,0x4,0x0,0x0,0x2b,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd5,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x1a,0x3,0x0,
+0x0,0xd9,0x0,0x0,0x0,0xbd,0x1,0x0,0x0,0x59,0x0,0x0,0x0,0x10,0x4,0x0,
+0x0,0x27,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x22,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x99,
+0x3,0x0,0x0,0x62,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xdd,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,
+0xa4,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xf,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0x31,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0xf,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x21,0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe6,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x96,0x3,0x0,0x0,0x64,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe8,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x5,0x0,
+0x0,0x59,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0x26,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x2,0x0,0x0,0x94,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0xf,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x3e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf3,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x20,0x2,0x0,0x0,0x2c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xfa,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,0x0,
+0x0,0x0,0xbc,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3,0x5,0x0,0x0,0x29,
+0x5,0x0,0x0,0x87,0x5,0x0,0x0,0x20,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xff,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xaa,0x1,0x0,0x0,0x81,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,
+0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x10,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x3,0x0,0x0,0x1d,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x10,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x23,0x1,0x0,0x0,0x33,0x0,0x0,0x0,0xae,
+0x3,0x0,0x0,0xfe,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x8,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,
+0xf,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x10,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0xc6,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x10,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x51,0x2,0x0,0x0,0x50,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x24,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x21,0x2,0x0,0x0,0x37,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x27,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x0,0x0,
+0x0,0x6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x10,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1c,0x3,0x0,0x0,0x5b,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x10,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x1,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x10,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x31,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x29,0x3,0x0,0x0,0x32,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x32,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x0,
+0x0,0x0,0x4e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,
+0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0x2e,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x10,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x42,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x10,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb4,0x2,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x2,0x5,0x0,0x0,0x33,0x5,0x0,0x0,0xb5,0x5,0x0,0x0,0xec,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x10,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0xf8,0x4,0x0,0x0,0x6,
+0x1,0x0,0x0,0x84,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x43,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0xc,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x10,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x5,0x0,0x0,0xe,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x10,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x13,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x56,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x40,0x3,0x0,0x0,0x6e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x58,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x58,0x3,0x0,
+0x0,0x2d,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0x7,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x4,0x3,0x0,0x0,0x9e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5c,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x41,
+0x2,0x0,0x0,0x74,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5e,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,
+0x94,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x60,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x40,0x3,0x0,0x0,0x62,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x64,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x2,
+0x0,0x0,0x8e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,
+0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x3,0x0,0x0,0x1e,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x10,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0xd9,0x1,0x0,0x0,
+0xfe,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6c,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3b,0x5,0x0,
+0x0,0x32,0x1,0x0,0x0,0x7e,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1e,0x3,0x0,0x0,0xe2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x76,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x46,
+0x0,0x0,0x0,0x9d,0x1,0x0,0x0,0x4d,0x2,0x0,0x0,0x4,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x10,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1,0x3,0x0,0x0,0x71,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x7b,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x38,0x0,0x0,0x0,0x26,0x2,0x0,0x0,0x99,0x2,0x0,0x0,0x85,0x1,0x0,
+0x0,0x2d,0x3,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x81,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x0,
+0x0,0x0,0xbb,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,
+0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0xbb,
+0x1,0x0,0x0,0x77,0x1,0x0,0x0,0x8e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x83,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x21,0x5,0x0,0x0,0xa8,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x84,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,
+0x0,0x58,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x10,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0xd5,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x10,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0xc3,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x10,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x73,0x2,0x0,0x0,0xb2,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x96,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4d,0x2,0x0,0x0,0x95,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9e,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x19,0x2,
+0x0,0x0,0x10,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,
+0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0x76,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x10,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x2,0x0,0x0,0x5f,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x10,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0xb6,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x29,0x2,0x0,0x0,0x2c,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa9,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x38,
+0x5,0x0,0x0,0x69,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xaa,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,
+0xe6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x10,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbd,0x1,0x0,0x0,0x6a,0x2,0x0,
+0x0,0x8a,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb3,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x24,0x5,
+0x0,0x0,0x63,0x2,0x0,0x0,0x4f,0x1,0x0,0x0,0x57,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xfc,0x4,0x0,0x0,0x53,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x45,0x5,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb9,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x5,0x0,
+0x0,0xd2,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x10,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x5,0x0,0x0,0x41,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x10,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x11,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x10,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xbe,0x3,0x0,0x0,0xbc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xca,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd3,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcf,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,
+0x0,0x0,0x9a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,
+0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0xf3,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x10,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x98,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x10,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x23,0x5,0x0,0x0,0x30,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x21,0x2,0x0,0x0,0xbc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdd,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3e,
+0x3,0x0,0x0,0xbe,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xde,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x3,0x0,0x0,
+0x27,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x10,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0xb8,0x3,0x0,
+0x0,0x9f,0x0,0x0,0x0,0x7f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe0,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3c,0x5,
+0x0,0x0,0xd4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,
+0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0xc,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x10,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc6,0x1,0x0,0x0,0xb7,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x10,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x1c,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x92,0x2,0x0,0x0,0xab,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xea,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,
+0x0,0x0,0x0,0x2b,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xeb,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc5,0x1,0x0,0x0,
+0xd9,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x10,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x2,0x0,0x0,0x5d,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x10,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x3,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x53,0x0,0x0,0x0,0xb9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x88,0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0x86,0x0,0x0,0x0,0x3c,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x30,0x5,0x0,0x0,0xa1,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x3b,0x5,0x0,0x0,0x24,0x5,0x0,0x0,0x9d,0x5,0x0,0x0,0x77,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x11,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0xe0,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x11,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x12,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0xac,0x1,0x0,0x0,0x19,0x1,0x0,0x0,0x66,0x0,0x0,0x0,0xab,0x0,0x0,
+0x0,0x27,0x5,0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x13,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x2,
+0x0,0x0,0xe0,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,
+0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x4e,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x11,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc1,0x2,0x0,0x0,0xbe,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x71,0x5,0x0,0x0,0x9b,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc0,0x3,0x0,0x0,0xf4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x28,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x85,
+0x2,0x0,0x0,0xf4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2e,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x1,0x0,0x0,
+0x8f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x11,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x2,0x0,0x0,0xd6,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x11,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0xe9,0x2,0x0,0x0,0xe7,0x0,
+0x0,0x0,0x6,0x1,0x0,0x0,0x67,0x5,0x0,0x0,0x6f,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1b,0x2,0x0,0x0,0xb2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x36,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x5,0x0,0x0,0x0,0x82,0x5,0x0,0x0,0x90,0x0,0x0,0x0,0x6d,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0x9f,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x4c,0x1,0x0,0x0,0x90,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x45,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,
+0x3,0x0,0x0,0x63,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4f,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xd,0x5,0x0,0x0,
+0x6d,0x2,0x0,0x0,0xfe,0x0,0x0,0x0,0x1d,0x5,0x0,0x0,0x69,0x3,0x0,0x0,
+0x10,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x11,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x2a,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x11,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xf7,0x1,0x0,0x0,0xd,0x1,0x0,0x0,0x85,0x2,
+0x0,0x0,0x65,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,
+0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0xe4,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x11,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x41,0x3,0x0,0x0,0x49,0x3,0x0,0x0,
+0x12,0x5,0x0,0x0,0x1e,0x1,0x0,0x0,0xb1,0x2,0x0,0x0,0x26,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x16,0x3,0x0,0x0,0x47,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6c,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,
+0x2,0x0,0x0,0x21,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x71,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc1,0x2,0x0,0x0,
+0x60,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x11,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x93,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x11,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0xec,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xa1,0x2,0x0,0x0,0x68,0x0,0x0,0x0,0x57,0x2,0x0,0x0,0xcf,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x11,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,0x0,0x0,0x81,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x7a,0x2,0x0,0x0,0x4,0x3,0x0,
+0x0,0x56,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x11,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0x56,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x11,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0xc9,
+0x3,0x0,0x0,0x17,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x96,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x0,0x0,0x0,
+0x8b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x11,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,0x42,0x5,0x0,
+0x0,0xde,0x4,0x0,0x0,0xf7,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x99,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa5,0x0,
+0x0,0x0,0x9e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,
+0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0xa4,
+0x0,0x0,0x0,0xb7,0x3,0x0,0x0,0x7d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf0,0x1,0x0,0x0,0x3c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa2,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x4,0x0,
+0x0,0x59,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x11,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,0x1,0x0,0x0,0x1,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x11,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2e,0x3,0x0,0x0,0xa2,0x0,0x0,0x0,0x5f,
+0x5,0x0,0x0,0x9a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbc,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x3,0x0,0x0,
+0x5e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x11,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0x9b,0x3,0x0,
+0x0,0xf,0x3,0x0,0x0,0x4c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc5,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x91,0x1,
+0x0,0x0,0x82,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,
+0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x5,0x0,0x0,0x70,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x11,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb8,0x3,0x0,0x0,0x4a,0x3,0x0,0x0,
+0x6a,0x1,0x0,0x0,0xb3,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xca,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x1,0x0,
+0x0,0x4b,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x11,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x5,0x0,0x0,0x3e,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x11,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0xd5,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x11,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x70,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd2,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x19,0x3,0x0,0x0,0x42,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd6,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x1,
+0x0,0x0,0x46,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,
+0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0x42,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x11,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x58,0x5,0x0,0x0,0x6c,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0x72,0x5,0x0,0x0,0x50,0x1,0x0,0x0,0xa3,0x1,0x0,
+0x0,0xee,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x9,0x5,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,
+0x3,0x0,0x0,0x17,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf1,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x23,0x1,0x0,0x0,
+0xe8,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0xfc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf2,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x69,0x3,0x0,0x0,0x5c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf6,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x5,
+0x0,0x0,0x6a,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,
+0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0x4c,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x11,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x33,0x2,0x0,0x0,0x48,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0xc3,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x69,0x5,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2f,
+0x1,0x0,0x0,0x2e,0x5,0x0,0x0,0x1,0x3,0x0,0x0,0x13,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x12,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x8,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x98,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0xa3,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x12,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x2,0x0,0x0,0xb3,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xae,0x5,0x0,0x0,0x60,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x13,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x99,0x2,0x0,0x0,0x16,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x14,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x2,0x0,
+0x0,0x69,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x12,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0xdb,0x0,
+0x0,0x0,0x3c,0x1,0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x16,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,
+0x4,0x0,0x0,0x26,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x18,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x92,0x3,0x0,0x0,
+0x4,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x12,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0xc7,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x12,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x4e,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xa1,0x5,0x0,0x0,0x12,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x33,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x14,0x3,0x0,0x0,0x91,0x0,0x0,0x0,0x24,0x2,0x0,0x0,0xcc,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x12,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x8e,0x2,0x0,0x0,0x21,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x22,0x1,0x0,0x0,0x84,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3d,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,
+0x2,0x0,0x0,0x53,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3e,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x1,0x0,0x0,
+0xb2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x12,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,0x37,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x12,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x49,0x2,0x0,0x0,0x2d,0x5,
+0x0,0x0,0x11,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,
+0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x51,
+0x5,0x0,0x0,0xbd,0x1,0x0,0x0,0xf,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x48,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x15,0x5,0x0,0x0,0x8,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4a,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x3,0x0,
+0x0,0xee,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x12,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x4,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x12,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x67,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x12,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x56,0x5,0x0,0x0,0x1b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x54,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3b,0x1,0x0,0x0,0x59,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x59,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf9,0x4,
+0x0,0x0,0x5c,0x3,0x0,0x0,0x9d,0x5,0x0,0x0,0x3e,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x37,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x16,0x5,0x0,0x0,0xec,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x62,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x3,0x0,
+0x0,0xc,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x12,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x50,0x1,0x0,0x0,0x74,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x12,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x51,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x12,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x74,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xaf,0x0,0x0,0x0,0x52,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7a,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x2,
+0x0,0x0,0x19,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,
+0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x3,0x0,0x0,0xd8,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x12,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x2,0x0,0x0,0xa7,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x12,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0x5a,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb4,0x2,0x0,0x0,0x77,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8c,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x28,
+0x1,0x0,0x0,0x81,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x90,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x5,0x0,0x0,
+0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x12,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf3,0x1,0x0,0x0,0xdf,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x12,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0x72,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x17,0x5,0x0,0x0,0x70,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe6,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xaa,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x62,0x5,0x0,
+0x0,0x1c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x12,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0x55,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x12,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x97,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x12,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0x32,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb4,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x9b,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb5,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x3,
+0x0,0x0,0x84,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,
+0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x36,
+0x2,0x0,0x0,0x37,0x3,0x0,0x0,0x4e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xb9,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc3,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,0x0,
+0x0,0x10,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x12,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0xe6,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x12,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0x11,0x3,0x0,0x0,0xc6,
+0x1,0x0,0x0,0xbb,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xcd,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x4,0x0,0x0,
+0x46,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x12,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x5,0x0,0x0,0x1a,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x12,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0xd7,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x94,0x3,0x0,0x0,0x34,0x1,0x0,0x0,0x2,0x5,0x0,0x0,0xc,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x12,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2,0x3,0x0,0x0,0x78,0x0,0x0,0x0,
+0x7c,0x0,0x0,0x0,0xc3,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xea,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,0x0,
+0x0,0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x12,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x3,0x0,0x0,0x39,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x12,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8,0x3,0x0,0x0,0x67,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x12,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xa9,0x5,0x0,0x0,0x32,0x5,0x0,0x0,0x11,0x5,0x0,0x0,
+0x55,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x12,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x9,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x12,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x6a,0x3,0x0,0x0,0x7f,0x0,
+0x0,0x0,0x9f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0xb2,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x50,0x1,0x0,0x0,0x1b,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x11,0x3,0x0,0x0,0x4a,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x88,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x38,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x13,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x2,0x0,0x0,0x76,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x13,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x2e,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd1,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x10,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x4,
+0x0,0x0,0x1c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,
+0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,0x2,0x0,0x0,0x7d,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x78,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xee,0x2,0x0,0x0,0xa1,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf,0x3,0x0,0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x27,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1d,
+0x2,0x0,0x0,0x94,0x0,0x0,0x0,0x26,0x3,0x0,0x0,0xf5,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x13,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x39,0x3,0x0,0x0,0x27,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,
+0xff,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x13,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x1,0x0,0x0,0x70,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x13,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0xf3,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6c,0x2,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x35,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xfa,0x4,0x0,0x0,0x26,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x39,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x2,0x0,
+0x0,0xc,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x13,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1b,0x5,0x0,0x0,0xec,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x13,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0xba,
+0x3,0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x41,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x0,0x0,0x0,
+0xa3,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x13,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,0x0,0x0,0xb2,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x13,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xeb,0x2,0x0,0x0,0xb0,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0xc6,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x5,0x0,0x0,0x17,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0x1e,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xe0,0x1,0x0,0x0,0x36,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0x3c,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x13,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0xe7,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x13,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x10,0x5,0x0,0x0,0x59,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x69,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x52,0x2,0x0,0x0,0x63,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x70,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x4,
+0x0,0x0,0x84,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,
+0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x2,0x0,0x0,0xce,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0xb1,0x5,0x0,0x0,
+0x23,0x3,0x0,0x0,0x26,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x76,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x5,0x0,
+0x0,0x7,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x13,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0x6b,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x13,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0xaa,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x13,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x3b,0x5,0x0,0x0,0xd5,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x89,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x88,0x5,0x0,0x0,0xa6,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8e,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1a,0x0,
+0x0,0x0,0xcb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,
+0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x44,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0x84,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb2,0x2,0x0,0x0,0xe9,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x76,0x0,0x0,0x0,0x77,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9f,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,
+0x0,0x0,0x0,0xce,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa2,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x64,0x1,0x0,0x0,
+0x94,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x13,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x79,0x3,0x0,0x0,0x15,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x13,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x1,0x0,0x0,0xf0,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x61,0x1,0x0,0x0,0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xbf,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc3,0x3,0x0,0x0,0xb8,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc0,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,
+0x0,0x67,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x13,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x58,0x5,0x0,0x0,0x86,0x0,
+0x0,0x0,0x1e,0x3,0x0,0x0,0x5e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc8,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x23,
+0x3,0x0,0x0,0x44,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd5,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2b,0x3,0x0,0x0,
+0x4,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd7,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe2,0x4,0x0,0x0,0xb1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe5,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb,0x3,
+0x0,0x0,0xe1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,
+0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x5,0x0,0x0,0xcf,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x3,0x0,0x0,0x89,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x65,0x5,0x0,0x0,0x3e,0x5,0x0,0x0,0xba,0x5,0x0,
+0x0,0xbd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x13,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0xfe,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x13,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x13,0x3,0x0,0x0,0x16,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0x34,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x88,0x2,0x0,0x0,0x38,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x11,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x0,
+0x0,0x0,0xbd,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,
+0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x70,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x14,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x5,0x0,0x0,0xc9,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x14,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x19,0x5,0x0,0x0,0xb1,0x5,0x0,0x0,0xd1,0x2,0x0,
+0x0,0x53,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x14,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0x68,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x14,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x3,0x0,0x0,0xd5,0x4,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd8,0x2,0x0,0x0,0x94,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x26,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe,0x5,0x0,0x0,0x57,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x28,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc8,0x0,
+0x0,0x0,0x5a,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0xe6,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x26,0x5,0x0,0x0,0x11,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2d,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x3,0x0,
+0x0,0x2b,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x14,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0x73,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x14,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x1c,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x59,0x3,0x0,0x0,0xcd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3d,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4c,0x1,0x0,0x0,0x5c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x42,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x73,0x3,
+0x0,0x0,0x4b,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x5b,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x97,0x5,0x0,0x0,0xd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x47,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x15,0x5,0x0,0x0,0x9a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x49,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x5,0x0,
+0x0,0x17,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x14,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0xe4,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x14,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x5,0x0,0x0,0x39,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x36,0x2,0x0,0x0,0x3a,0x0,0x0,0x0,
+0x82,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x14,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0xa7,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x14,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x5,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x4e,0x1,0x0,0x0,0x39,0x2,0x0,0x0,0x56,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x14,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0xd9,0x1,0x0,0x0,
+0xe5,0x4,0x0,0x0,0xbd,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x75,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x2,0x0,
+0x0,0x35,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x14,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,0x0,0x0,0xbd,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x14,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0xa8,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x77,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x89,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x77,0x2,0x0,0x0,0xff,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8a,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x0,
+0x0,0x0,0x65,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,
+0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x14,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x4,0x0,0x0,0x6c,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x14,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0x74,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe5,0x4,0x0,0x0,0x6,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa2,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,
+0x3,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa6,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,
+0xfc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x14,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0x35,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x14,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe8,0x4,0x0,0x0,0xca,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x26,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb5,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x21,0x2,0x0,0x0,0xa1,0x3,0x0,0x0,0x42,0x1,0x0,0x0,0xb2,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x14,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0x42,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa3,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd2,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,
+0x5,0x0,0x0,0xf6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd4,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x4,0x0,0x0,
+0x63,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x14,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0xaa,0x0,0x0,
+0x0,0x9a,0x3,0x0,0x0,0x9b,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xdb,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfb,0x4,
+0x0,0x0,0x56,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,
+0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x3c,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x14,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0xce,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x14,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x8a,0x5,0x0,0x0,0xf8,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x0,0x2,0x0,0x0,0x8b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,
+0x2,0x0,0x0,0x1b,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xfd,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x2,0x0,0x0,
+0x4b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x14,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x68,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x15,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x2,0x0,0x0,0x4e,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf1,0x4,0x0,0x0,0x68,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x10,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x47,0x1,0x0,0x0,0x82,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x11,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x5,0x0,
+0x0,0xe1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x15,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x68,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x15,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x5,0x0,0x0,0x73,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x15,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0x29,0x5,0x0,0x0,0x34,0x1,0x0,0x0,
+0x8d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x15,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x1,0x0,0x0,0x99,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x15,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x43,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x35,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xea,0x2,0x0,0x0,0x52,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3d,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x1,0x0,
+0x0,0xb1,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x15,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xad,0x0,0x0,0x0,0x4e,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x15,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x15,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7d,0x1,0x0,0x0,0x15,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x58,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x1b,0x3,0x0,0x0,0x93,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5d,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x3,
+0x0,0x0,0xbb,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,
+0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x6e,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x15,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x1,0x0,0x0,0x2a,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x15,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x60,0x2,0x0,0x0,0x16,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfd,
+0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7a,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x2,0x0,0x0,
+0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x15,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x3,0x0,0x0,0xa3,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x15,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0x7b,0x2,0x0,0x0,0x54,0x0,
+0x0,0x0,0x95,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,
+0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x3c,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x15,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7a,0x5,0x0,0x0,0x84,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x15,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xe1,0x4,0x0,0x0,0x4a,0x3,0x0,0x0,0x5f,0x1,0x0,0x0,0x54,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x15,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x1,0x0,0x0,0x67,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x15,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x7b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x91,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x67,0x0,0x0,0x0,0x70,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x93,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x5,
+0x0,0x0,0x87,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,
+0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x24,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x15,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x6d,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x15,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,0x77,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa,0x3,0x0,0x0,0x5e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,
+0x1,0x0,0x0,0x51,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb4,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,
+0xcc,0x1,0x0,0x0,0x1,0x3,0x0,0x0,0x98,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb8,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x70,0x0,0x0,0x0,0x37,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbb,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc4,0x2,
+0x0,0x0,0x63,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,
+0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x5,0x0,0x0,0xef,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x15,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x3,0x0,0x0,0x0,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x15,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0xd4,0x4,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x34,0x2,0x0,0x0,0xfc,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd6,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,
+0x1,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xdd,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,
+0x85,0x1,0x0,0x0,0x76,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xee,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x71,0x3,0x0,0x0,0x3d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf8,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,
+0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,
+0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0xb8,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x15,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x15,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x77,0x3,0x0,0x0,0xda,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5e,0x5,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x10,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,
+0x0,0x0,0x0,0x6a,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x11,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x2,0x0,0x0,
+0xc9,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0xd5,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x94,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x8a,0x2,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x6c,0x0,0x0,0x0,0x70,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x26,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,
+0x0,0x53,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x16,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x97,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x16,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0xef,0x4,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x16,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0xf6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3a,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xbc,0x5,0x0,0x0,0xba,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3b,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xeb,0x1,
+0x0,0x0,0x4b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,
+0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x1,0x0,0x0,0x7f,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x16,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0x1c,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x16,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x22,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa6,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x52,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x87,
+0x5,0x0,0x0,0x20,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x55,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x51,0x0,0x0,0x0,
+0xf1,0x1,0x0,0x0,0x68,0x3,0x0,0x0,0x20,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x56,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x41,0x1,0x0,0x0,0x8d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x59,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x3,
+0x0,0x0,0xbd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,
+0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x3,0x0,0x0,0x99,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x16,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4,0x5,0x0,0x0,0x10,0x5,0x0,0x0,
+0x66,0x2,0x0,0x0,0x19,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x64,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x5,0x0,
+0x0,0xe1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x16,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x0,0x0,0x0,0x1c,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x16,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x5,0x0,0x0,0x70,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x16,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x3b,0x3,0x0,0x0,0xac,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x77,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc6,0x1,0x0,0x0,0xce,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x78,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x0,
+0x0,0x0,0x44,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,
+0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x45,0x1,0x0,0x0,0xe,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x16,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x1,0x0,0x0,0xa8,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x16,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x24,0x2,0x0,0x0,0xdc,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x42,0x1,0x0,0x0,0xe9,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x89,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,
+0x5,0x0,0x0,0x0,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x8f,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x3,0x0,0x0,
+0xc0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0xd,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xde,0x0,0x0,0x0,0x69,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa4,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3f,0x5,0x0,0x0,0xd2,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa7,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1b,0x2,0x0,
+0x0,0x91,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x16,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0x32,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x16,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x69,0x5,0x0,0x0,0x46,0x5,0x0,0x0,0x31,
+0x3,0x0,0x0,0x40,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xaf,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x3,0x0,0x0,
+0x4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x46,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0xe0,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x6,0x1,0x0,0x0,0xee,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0x6d,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x16,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5f,0x5,0x0,0x0,0x99,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x16,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe9,0x0,0x0,0x0,0x64,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd3,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,
+0x5,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xda,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x79,0x3,0x0,0x0,
+0x21,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x2e,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x31,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x18,0x5,0x0,0x0,0xdc,0x4,0x0,0x0,0xa6,0x5,0x0,0x0,0x8f,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x16,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x18,0x1,0x0,0x0,
+0x78,0x3,0x0,0x0,0x94,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xeb,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x0,0x0,
+0x0,0x78,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x16,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe3,0x4,0x0,0x0,0x36,0x5,
+0x0,0x0,0xba,0x3,0x0,0x0,0x63,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9f,
+0x0,0x0,0x0,0x48,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf0,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x98,0x0,0x0,0x0,
+0x6b,0x1,0x0,0x0,0x5a,0x5,0x0,0x0,0x5c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf3,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3a,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,
+0x0,0x0,0x4b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,
+0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0xf3,
+0x0,0x0,0x0,0x49,0x3,0x0,0x0,0x35,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd8,0x0,0x0,0x0,0x52,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x58,0x3,0x0,
+0x0,0x5b,0x1,0x0,0x0,0x4b,0x5,0x0,0x0,0x6d,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x2b,0x5,0x0,0x0,0xd4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x12,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,
+0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x17,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,0x0,0x0,
+0x20,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x17,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5d,0x2,0x0,0x0,0x63,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x17,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0xf,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x65,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x1f,0x1,0x0,0x0,0x50,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2f,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x0,0x0,
+0x0,0x62,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x17,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xce,0x2,0x0,0x0,0xaa,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x17,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe9,0x4,0x0,0x0,0x21,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x17,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x43,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3,0x2,0x0,0x0,0x97,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x45,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,
+0x0,0x0,0x37,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,
+0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x0,0x0,0x0,0xac,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x17,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x77,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x17,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x96,0x2,0x0,0x0,0xfa,0x4,0x0,
+0x0,0x5f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x17,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x8e,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x17,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x5,0x0,0x0,0x60,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x17,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x80,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x5c,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x56,0x5,0x0,0x0,0x66,0x5,0x0,0x0,0x12,0x1,0x0,0x0,0x7,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x17,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0x7e,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xfa,0x4,0x0,0x0,0x30,0x5,0x0,0x0,0x3c,0x1,0x0,0x0,0xa2,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x17,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x95,0x1,0x0,0x0,
+0x74,0x5,0x0,0x0,0xe6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x66,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,
+0x0,0x77,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x17,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x1,0x0,0x0,0xc1,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x17,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0x9c,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x17,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x39,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x72,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x52,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x76,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x1,
+0x0,0x0,0x15,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,
+0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x5,0x0,0x0,0x7c,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x17,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x32,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x17,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb4,0x2,0x0,0x0,0xe7,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd3,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x85,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,
+0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x89,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x2,0x0,0x0,
+0x37,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x17,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x6b,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x17,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x5c,0x5,0x0,0x0,0x64,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x85,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x94,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xa1,0x1,0x0,0x0,0xa6,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9e,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x40,0x3,0x0,
+0x0,0x9c,0x3,0x0,0x0,0x71,0x5,0x0,0x0,0x7c,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x45,0x1,0x0,0x0,0x13,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb2,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8,
+0x2,0x0,0x0,0x60,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb8,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xac,0x3,0x0,0x0,
+0xb,0x0,0x0,0x0,0xf,0x2,0x0,0x0,0x46,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd3,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xe1,0x4,0x0,0x0,0xa7,0x0,0x0,0x0,0x6,0x2,0x0,0x0,0x67,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x17,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x96,0x3,0x0,0x0,0x46,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x6c,0x2,0x0,0x0,0x8e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xdf,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x0,0x0,
+0x0,0x82,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x17,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x3,0x0,0x0,0xce,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x17,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0x9e,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x17,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x75,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf4,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x63,0x5,0x0,0x0,0x4d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf7,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x5,
+0x0,0x0,0x59,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,
+0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0xa4,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x18,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x5,0x0,0x0,0x5b,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x18,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2d,0x1,0x0,0x0,0xc4,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x6,0x2,0x0,0x0,0xa6,0x1,0x0,0x0,0xd8,0x1,0x0,0x0,0x9e,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x18,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x8,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x18,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x82,0x1,0x0,0x0,0xad,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x16,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd1,0x1,0x0,0x0,0xec,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1c,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x1,
+0x0,0x0,0xc2,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,
+0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0x7f,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x18,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc5,0x2,0x0,0x0,0x21,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x18,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0x35,0x3,0x0,0x0,0x3a,0x5,0x0,
+0x0,0x6e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x18,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0x26,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x18,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x70,0x2,0x0,0x0,0x89,0x5,0x0,0x0,0xf0,
+0x4,0x0,0x0,0x34,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x33,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x0,0x0,0x0,
+0xb2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x18,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0xc9,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x18,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0xf8,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xaf,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x45,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x9d,0x5,0x0,0x0,0x73,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4c,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6b,0x5,0x0,
+0x0,0x42,0x5,0x0,0x0,0x5e,0x2,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb5,0x5,0x0,0x0,0x35,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x56,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,
+0x2,0x0,0x0,0x53,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x58,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,
+0x6c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x18,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x5,0x0,0x0,0x2d,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x18,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x5,0x0,0x0,0xbb,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x41,0x2,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7e,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x58,0x5,0x0,
+0x0,0xd4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x18,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x2,0x0,0x0,0x6d,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x18,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x23,0x5,0x0,0x0,0xe5,0x4,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x18,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa,0x2,0x0,0x0,0x48,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x92,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xad,0x0,0x0,0x0,0xd5,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x95,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x2,
+0x0,0x0,0x75,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,
+0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0xa6,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x18,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0xce,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x18,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x47,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x29,0x2,0x0,0x0,0xbd,0x5,0x0,0x0,0xa9,0x5,0x0,0x0,0xe1,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x18,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x5,0x0,0x0,0x12,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x18,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,
+0xe,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x18,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x2,0x0,0x0,0xff,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x18,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0xee,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc0,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x1e,0x2,0x0,0x0,0x47,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc1,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6,0x2,0x0,
+0x0,0x96,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x18,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0x2b,0x1,
+0x0,0x0,0xaf,0x5,0x0,0x0,0x11,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcc,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,
+0x5,0x0,0x0,0x6d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xcd,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,
+0x29,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x18,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa5,0x5,0x0,0x0,0x84,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x18,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x5,0x0,0x0,0x0,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x58,0x5,0x0,0x0,0x6c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x0,0x2,0x0,0x0,0x84,0x1,0x0,0x0,0xd2,0x1,0x0,0x0,0x9d,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x18,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0xe7,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x40,0x5,0x0,0x0,0x52,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xec,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,
+0x2,0x0,0x0,0xf2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xed,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x0,0x0,0x0,
+0xc,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x18,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x71,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x18,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x5,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x4d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xbd,0x5,0x0,0x0,0x7a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfb,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1a,0x3,0x0,
+0x0,0x13,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x18,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x2,0x0,0x0,0x1e,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x5b,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x2b,0x5,0x0,0x0,0x4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x49,0x5,0x0,0x0,0xf6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x2,
+0x0,0x0,0x20,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x5,0x0,0x0,0x5e,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x5,0x0,0x0,0xf6,0x4,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x19,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x24,0x3,0x0,0x0,0x22,0x3,0x0,0x0,0xce,0x2,0x0,0x0,0x9c,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x5,0x0,0x0,0x1c,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0xdd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2a,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xf,0x2,0x0,0x0,0x4,0x2,0x0,0x0,0x77,0x1,0x0,0x0,0x7f,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x19,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x53,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xeb,0x2,0x0,0x0,0xdb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3a,0x5,0x0,0x0,0x94,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x40,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x5,0x0,
+0x0,0xe7,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x19,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9b,0x1,0x0,0x0,0x39,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x2,0x0,0x0,0x93,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x11,0x3,0x0,0x0,0x77,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4c,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x38,0x2,0x0,0x0,0x23,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x53,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x3,
+0x0,0x0,0x32,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0xbd,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x5,0x0,0x0,0x2a,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x19,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xa1,0x5,0x0,0x0,0xda,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xed,0x0,0x0,0x0,0x44,0x3,0x0,0x0,0x4d,0x5,0x0,0x0,0x2e,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0x6,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x9,0x3,0x0,0x0,0xf1,0x0,0x0,0x0,
+0xb,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x19,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x62,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x19,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x21,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x96,0x5,0x0,0x0,0xb3,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x92,0x0,0x0,0x0,0x6a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8e,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x0,0x0,
+0x0,0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x19,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0xc8,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x2,0x0,0x0,0x59,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf5,0x4,0x0,0x0,0xe4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x97,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x49,0x0,0x0,0x0,0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9a,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x1,
+0x0,0x0,0x8d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x26,0x3,0x0,0x0,0x22,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0xfb,0x1,0x0,0x0,
+0x2a,0x3,0x0,0x0,0x15,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb5,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6,0x5,0x0,
+0x0,0x3e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x19,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x91,0x5,0x0,0x0,0x8f,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x2,0x0,0x0,0x53,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x56,0x2,0x0,0x0,0x81,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc9,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe7,0x2,0x0,0x0,0x29,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xca,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x1,
+0x0,0x0,0xe,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x23,0x3,0x0,0x0,0x36,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0xb3,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x19,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x7b,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x42,0x1,0x0,0x0,0xed,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd7,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,
+0x4,0x0,0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xda,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x2,0x0,0x0,
+0x86,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x19,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x5,0x0,0x0,0x3d,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x19,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x2b,0x5,
+0x0,0x0,0xe6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x10,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0xf4,0x4,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x1a,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x34,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x48,0x5,0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,
+0x3,0x0,0x0,0x2f,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,
+0xaa,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x1a,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0xa,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x1a,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0x75,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x48,0x2,0x0,0x0,0x19,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4c,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x47,0x5,0x0,0x0,0x61,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4f,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x66,0x0,0x0,
+0x0,0x35,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x1a,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1b,0x2,0x0,0x0,0x29,0x0,
+0x0,0x0,0xf9,0x4,0x0,0x0,0x20,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x54,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,
+0x5,0x0,0x0,0x17,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x60,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x5,0x0,0x0,
+0x52,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x1a,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,0x0,0x0,0x0,0x8f,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x1a,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x5,0x0,0x0,0xec,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x91,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x75,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x8a,0x5,0x0,0x0,0x91,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7c,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x0,0x0,
+0x0,0xbf,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x1a,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0x65,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x1a,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x5,0x0,0x0,0x12,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0xe2,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x8f,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x63,0x1,0x0,0x0,0x73,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9a,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x5,
+0x0,0x0,0xaa,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,
+0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x5,0x0,0x0,0xf3,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x1a,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0xed,0x2,0x0,0x0,
+0x41,0x2,0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xab,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1a,0x0,0x0,
+0x0,0x12,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x1a,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0x2f,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x1a,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x90,0x5,0x0,0x0,0x44,
+0x2,0x0,0x0,0xe7,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb6,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xff,0x4,0x0,0x0,
+0x20,0x5,0x0,0x0,0x67,0x2,0x0,0x0,0x34,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb7,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x23,0x5,0x0,0x0,0x61,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb9,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x2,
+0x0,0x0,0xc1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,
+0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x5,0x0,0x0,0xda,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x1a,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0x13,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x1a,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0xb9,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x70,0x0,0x0,0x0,0x66,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd1,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,
+0x4,0x0,0x0,0xed,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd5,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x3,0x0,0x0,
+0xb7,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x1a,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xff,0x4,0x0,0x0,0x7a,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x1a,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb6,0x5,0x0,0x0,0xec,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfa,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc0,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfe,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x98,0x0,0x0,
+0x0,0xfc,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x1b,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0xb9,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x1b,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x65,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x13,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xef,0x4,0x0,0x0,0xe2,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x17,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3b,0x3,
+0x0,0x0,0xe8,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,0xe1,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x1b,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x9,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x1b,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0xe7,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x42,0x3,0x0,0x0,0xb7,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x22,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaa,
+0x2,0x0,0x0,0xa3,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x24,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9f,0x5,0x0,0x0,
+0xf8,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x1b,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0xd8,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x1b,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0x2d,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0xe1,0x4,0x0,0x0,0x2c,
+0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0xd,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3a,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xac,0x1,0x0,0x0,0x99,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3c,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x2,0x0,
+0x0,0x5b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x1b,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0xc,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x1b,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0xc6,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf7,0x0,0x0,0x0,0x82,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x47,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfc,0x4,0x0,0x0,0x2d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x48,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,
+0x0,0x0,0xfd,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0x1b,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x1b,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x76,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x1b,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x40,0x5,0x0,0x0,0xfd,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x52,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,
+0x3,0x0,0x0,0xb4,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x53,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x0,0x0,0x0,
+0x9,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x1b,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0xd,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x1b,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x3,0x0,0x0,0x37,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x62,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6d,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xac,0x3,0x0,0x0,0x68,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x77,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x28,0x5,0x0,
+0x0,0xf3,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x1b,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x5,0x0,0x0,0xa9,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x1b,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8,0x3,0x0,0x0,0xcc,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x95,0x1,0x0,0x0,0x3b,0x0,0x0,0x0,0x7a,0x5,0x0,0x0,
+0x7b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x1b,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0x63,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x1b,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x22,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x88,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x92,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x35,0x0,0x0,0x0,0x4a,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x96,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x1,0x0,
+0x0,0x57,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x1b,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x3,0x0,0x0,0x21,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x1b,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x9b,
+0x1,0x0,0x0,0xb,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa8,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x49,0x5,0x0,0x0,
+0x29,0x5,0x0,0x0,0xd3,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa9,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x8f,0x2,0x0,0x0,0x6c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb1,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x5,
+0x0,0x0,0x45,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x62,
+0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x1b,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x2,0x0,0x0,0xa,0x1,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x1b,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2e,0x3,0x0,0x0,0x2,0x3,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc0,0x5,0x0,0x0,0x4e,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc1,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,
+0x1,0x0,0x0,0xb9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xca,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,
+0xbb,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x1b,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0xd,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x1b,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x2,0x0,0x0,0xb7,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0xf1,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xda,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x8,0x5,0x0,0x0,0x44,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe0,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2b,0x5,0x0,
+0x0,0xcc,0x1,0x0,0x0,0x6c,0x1,0x0,0x0,0xe6,0x4,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x9b,0x1,0x0,0x0,0xb0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xee,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,
+0x2,0x0,0x0,0x23,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf0,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,
+0x62,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x1b,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8f,0x0,0x0,0x0,0xdc,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x1b,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4a,0x5,0x0,0x0,0xa,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0x13,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xff,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xaa,0x5,0x0,0x0,0xb3,0x5,0x0,0x0,0x33,0x2,0x0,0x0,0x26,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0x26,0x2,0x0,0x0,0xfd,0x1,0x0,
+0x0,0x35,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0xc6,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x1c,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0xf1,0x0,0x0,0x0,
+0x3c,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x1c,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x4,0x0,0x0,0xd5,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x1c,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd3,0x4,0x0,0x0,0xbb,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x40,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1f,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x28,0x3,0x0,
+0x0,0x20,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x90,0x2,0x0,0x0,0x96,0x2,
+0x0,0x0,0x39,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x36,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa0,
+0x1,0x0,0x0,0x8b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3b,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,
+0xa,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x1c,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0xfb,0x4,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x1c,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x4,0x0,0x0,0x3,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x84,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x49,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x2a,0x2,0x0,0x0,0x5b,0x5,0x0,0x0,0xe,0x5,0x0,0x0,0x3c,0x2,0x0,0x0,
+0x7f,0x1,0x0,0x0,0xad,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4a,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x4,0x0,
+0x0,0xc0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0xc0,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x1c,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x5,0x0,0x0,0x1c,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x54,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x23,0x3,0x0,0x0,0x9d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5e,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x1,
+0x0,0x0,0x20,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,
+0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x41,0x1,0x0,0x0,0x59,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x1c,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x1,0x0,0x0,0x1f,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x1c,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0x5a,0x1,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xba,0x3,0x0,0x0,0xb9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6c,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,
+0x5,0x0,0x0,0x45,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x74,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,
+0x4a,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x1c,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0xe,0x3,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x1c,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x5,0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x3,0x5,0x0,0x0,0x64,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x6,0x2,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8e,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x3,0x0,
+0x0,0x14,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x33,0x2,0x0,0x0,0x1a,0x3,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x1c,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0xc9,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xca,0x0,0x0,0x0,0xbd,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x96,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x8,0x5,0x0,0x0,0x12,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x99,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x2,
+0x0,0x0,0x2e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,
+0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x1,0x0,0x0,0x21,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x1c,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x4e,0x5,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x1c,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x35,0x1,0x0,0x0,0x37,0x2,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x90,0x0,0x0,0x0,0x90,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb1,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,
+0x5,0x0,0x0,0xec,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb3,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x4,0x0,0x0,
+0x48,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x1c,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x65,0x1,0x0,0x0,0x12,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x1c,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x9c,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc0,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd9,0x4,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc7,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,0x0,
+0x0,0x61,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x60,0x1,0x0,0x0,0x64,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x1c,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x45,0x5,0x0,0x0,0x46,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x83,0x5,0x0,0x0,0xd8,0x0,0x0,0x0,0x15,0x2,0x0,0x0,
+0x2c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x1c,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x65,0x2,0x0,0x0,0x3b,0x2,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x1c,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0x74,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xfd,0x0,0x0,0x0,0x22,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe8,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc4,0x3,0x0,0x0,0xb0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xed,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x0,0x0,
+0x0,0x9d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x2c,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0x61,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x69,0x1,0x0,0x0,0x1a,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x7e,0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf7,0x1,
+0x0,0x0,0x5f,0x3,0x0,0x0,0x4f,0x1,0x0,0x0,0x2a,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x29,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x79,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x12,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x3,0x0,
+0x0,0x40,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0x7b,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x1d,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x1,0x0,0x0,0x9,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0x48,0x3,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1d,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xad,0x0,0x0,0x0,0x8c,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x23,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2a,0x5,
+0x0,0x0,0xf3,0x4,0x0,0x0,0x5e,0x2,0x0,0x0,0x49,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x26,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x15,0x2,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2a,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x2,0x0,
+0x0,0x4e,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x23,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x1d,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x3,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x36,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf5,0x4,0x0,0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x38,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x78,0x5,
+0x0,0x0,0x4,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,
+0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x5,0x0,0x0,0xeb,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x1d,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0x61,0x0,0x0,0x0,
+0x69,0x0,0x0,0x0,0xa3,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x48,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x5,0x0,
+0x0,0x5b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x5,0x0,0x0,0x60,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x1d,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x5,0x0,0x0,0x5a,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0x3d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4e,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfe,0x1,0x0,0x0,0xa1,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4f,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x5,
+0x0,0x0,0xfb,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,
+0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6a,0x1,0x0,0x0,0xf8,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x1d,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0x27,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x1d,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x3a,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5c,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,
+0x1,0x0,0x0,0x84,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5e,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x0,0x0,0x0,
+0xa,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x1d,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x5b,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x1d,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0xee,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x24,0x5,0x0,0x0,0xd3,0x4,0x0,0x0,0x67,0x0,0x0,0x0,0x44,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x1d,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0xf4,0x4,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x1d,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x18,0x2,0x0,0x0,0xad,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x19,0x5,0x0,0x0,0xe6,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7c,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,
+0x1,0x0,0x0,0xbe,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7f,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,
+0x4b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x1d,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x5,0x0,0x0,0x11,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x1d,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x33,0x1,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x3e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x88,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x2e,0x5,0x0,0x0,0xa4,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x90,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x5,0x0,
+0x0,0x41,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x5,0x0,0x0,0x16,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x1d,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x4,0x0,0x0,0x3d,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,0xaa,0x0,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xaf,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe6,0x2,0x0,0x0,0x25,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb1,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,
+0x0,0x0,0xb8,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,
+0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0xbd,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x1d,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9b,0x1,0x0,0x0,0x7d,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x1d,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6b,0x5,0x0,0x0,0x42,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc,0x1,0x0,0x0,0x4b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc0,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,
+0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc6,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x0,0x0,0x0,
+0x9d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x1d,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0xfc,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x1d,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x98,0x5,0x0,0x0,0x91,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x90,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd5,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x80,0x5,0x0,0x0,0x8a,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd6,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x5,0x0,
+0x0,0x9,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0xbb,0x5,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x1d,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x59,0x2,0x0,0x0,0x26,0x2,0x0,0x0,0xfd,
+0x1,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe1,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,
+0xbb,0x5,0x0,0x0,0x31,0x1,0x0,0x0,0x4,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe8,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfa,0x4,0x0,0x0,0x51,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xeb,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x2,
+0x0,0x0,0x63,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,
+0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0xfc,
+0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x1d,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x65,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x1d,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x25,0x0,0x0,0x0,0xbd,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xef,0x0,0x0,0x0,0x2f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x23,
+0x3,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,
+0x5e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x1e,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0xda,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x1e,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0x19,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x70,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x5c,0x5,0x0,0x0,0x0,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1f,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x30,0x2,0x0,
+0x0,0x85,0x0,0x0,0x0,0x38,0x3,0x0,0x0,0x97,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe,0x5,0x0,0x0,0x38,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x27,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,
+0x5,0x0,0x0,0x9d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x30,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x3,0x0,0x0,
+0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x1e,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0x80,0x0,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x1e,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0xe8,0x4,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb6,0x5,0x0,0x0,0xe1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x48,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x86,0x1,0x0,0x0,0xa7,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x54,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc9,0x3,0x0,
+0x0,0x3e,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x1e,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x2,0x0,0x0,0x29,0x1,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe6,0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1d,0x1,0x0,0x0,0xf1,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x81,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x6f,0x0,0x0,0x0,0xb8,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x82,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,0x0,
+0x0,0x0,0x8f,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,
+0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0x16,
+0x2,0x0,0x0,0x3a,0x3,0x0,0x0,0xbb,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x87,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc1,0x2,0x0,0x0,0xc6,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8c,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x2,0x0,
+0x0,0x90,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x1e,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0xd5,0x4,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0xa4,0x3,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x14,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa4,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe2,0x4,0x0,0x0,0x13,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa6,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x5,
+0x0,0x0,0xe4,0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,
+0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe2,0x1,0x0,0x0,0x20,
+0x4,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x1e,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0x1b,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x1e,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0x8f,0x5,0x0,0x0,0xd4,0x2,0x0,
+0x0,0xb0,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x1e,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0x62,0x2,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x99,0x2,0x0,0x0,0xd2,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc7,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x35,0x0,0x0,0x0,0x81,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd4,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x0,
+0x0,0x0,0xe9,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,
+0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x42,0x1,0x0,0x0,0x7b,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x1e,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0xee,0x2,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x1e,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x6e,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xba,0x2,0x0,0x0,0xc,0x5,0x0,0x0,0x57,0x2,0x0,0x0,0x2f,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6,0x5,0x0,0x0,0x51,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf0,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x14,0x2,0x0,0x0,0x76,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf5,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x0,
+0x0,0x0,0x64,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,
+0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x4e,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x1e,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0x3,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x1f,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5b,0x1,0x0,0x0,0x9a,0x0,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x28,0x1,0x0,0x0,0x58,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x16,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,
+0x1,0x0,0x0,0x35,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1a,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x0,0x0,0x0,
+0x94,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x1f,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5d,0x2,0x0,0x0,0x3d,0x1,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x1f,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x5,0x0,0x0,0x86,0x1,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd7,0x0,0x0,0x0,0xaa,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2b,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x1f,0x5,0x0,0x0,0x22,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x30,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x1,0x0,
+0x0,0xc6,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x1f,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x10,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x1f,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc6,0x1,0x0,0x0,0x27,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x3c,0x5,0x0,0x0,0xfd,0x4,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x40,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfb,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x41,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x5,
+0x0,0x0,0x41,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,
+0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x3,0x0,0x0,0x1c,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x1f,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0x66,0x1,0x0,0x0,
+0xcf,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x47,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x2,0x0,
+0x0,0x9d,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x1f,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4a,0x1,0x0,0x0,0x6e,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x1f,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x52,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x45,0x1,0x0,0x0,0x8b,0x5,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x56,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x1e,0x2,0x0,0x0,0x50,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5f,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5b,0x0,
+0x0,0x0,0x5c,0x0,0x0,0x0,0x4b,0x1,0x0,0x0,0xd9,0x2,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0xa2,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x61,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x66,0x2,0x0,0x0,0x74,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x64,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x0,0x0,
+0x0,0x90,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x1f,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xaf,0x2,
+0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x1f,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x46,0x5,0x0,0x0,0x1,
+0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0x63,0x2,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x75,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb6,0x5,0x0,0x0,0xf8,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x80,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x2,
+0x0,0x0,0x2b,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,
+0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x1,0x0,0x0,0x40,
+0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x1f,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0xc0,0x3,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x1f,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x81,0x0,0x0,0x0,0xa0,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb6,0x5,0x0,0x0,0x11,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x91,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,
+0x2,0x0,0x0,0x9c,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x92,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x5,0x0,0x0,
+0x6d,0x5,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x1f,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x5,0x0,0x0,0x5,0x5,0x0,
+0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x1f,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x5,0x0,0x0,0x4f,0x5,0x0,0x0,0x1,0x4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x48,0x2,0x0,0x0,0x6a,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa6,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xa9,0x1,0x0,0x0,0x57,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xad,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf9,0x4,0x0,
+0x0,0x20,0x5,0x0,0x0,0xbd,0x1,0x0,0x0,0x82,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x8f,0x0,0x0,0x0,0xe0,0x2,0x0,0x0,0x18,0x1,0x0,0x0,0x52,0x2,
+0x0,0x0,0xe9,0x4,0x0,0x0,0x44,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb4,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,
+0x0,0x0,0x0,0x92,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbf,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x90,0x0,0x0,0x0,
+0x26,0x1,0x0,0x0,0xa1,0x1,0x0,0x0,0x91,0x1,0x0,0x0,0x1,0x4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc4,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2f,0x5,0x0,0x0,0xff,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd4,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfb,0x1,
+0x0,0x0,0xc0,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,
+0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x2,0x0,0x0,0xcf,
+0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x1f,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x5,0x0,0x0,0xb,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x1f,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0xa7,0x5,0x0,0x0,0x1,0x4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x47,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe8,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,
+0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xed,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe0,0x4,0x0,0x0,
+0x70,0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x1f,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x60,0x5,0x0,0x0,0x68,0x1,0x0,
+0x0,0xfd,0x2,0x0,0x0,0xb7,0x1,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf1,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xca,0x0,
+0x0,0x0,0xe,0x2,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,
+0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0xa4,
+0x3,0x0,0x0,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x1f,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x2,0x0,0x0,0x9c,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x3b,0x3,0x0,0x0,0x2e,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x4a,0x2,0x0,0x0,0x52,0x5,0x0,0x0,0xb5,0x0,0x0,0x0,0xfd,0x0,
+0x0,0x0,0x96,0x3,0x0,0x0,0x47,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,
+0x1,0x0,0x0,0x5d,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x40,0x1,0x0,0x0,
+0x30,0x1,0x0,0x0,0x71,0x1,0x0,0x0,0xeb,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2f,0x1,0x0,0x0,0x27,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x0,
+0x0,0x0,0xdf,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x7a,0x5,0x0,0x0,0xb7,
+0x1,0x0,0x0,0xe7,0x4,0x0,0x0,0xd7,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x21,0x3,0x0,0x0,0x48,0x2,0x0,0x0,0xa,0x2,0x0,0x0,0x31,0x0,0x0,0x0,
+0x4d,0x2,0x0,0x0,0xcf,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x11,0x5,0x0,
+0x0,0x73,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0xda,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x1,0x0,0x0,0x41,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x37,0x2,0x0,0x0,0x59,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x15,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x22,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0xd5,0x2,0x0,0x0,0xab,0x2,0x0,
+0x0,0x46,0x0,0x0,0x0,0x66,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x16,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xd1,0x0,
+0x0,0x0,0x92,0x2,0x0,0x0,0x2,0x3,0x0,0x0,0x14,0x5,0x0,0x0,0xcc,0x2,
+0x0,0x0,0x6,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0x58,
+0x1,0x0,0x0,0xf6,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe2,0x2,0x0,0x0,0xfd,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1d,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x3,0x0,
+0x0,0xe0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0x31,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x67,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0x68,0x5,0x0,0x0,0x3a,0x0,0x0,0x0,0x49,0x0,0x0,0x0,
+0x4,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x3f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2f,0x0,0x0,0x0,0xc1,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x24,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x5,
+0x0,0x0,0x5d,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0xb2,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x3,0x0,0x0,0x25,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xbd,0x1,0x0,0x0,0x99,0x3,0x0,0x0,0xef,0x4,0x0,
+0x0,0xb9,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x0,0x0,0x0,0xe9,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x26,
+0x2,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xba,0x3,0x0,0x0,
+0x11,0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0x26,0x0,0x0,0x0,0xde,0x4,0x0,0x0,
+0xf6,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x53,0x1,0x0,0x0,0xfa,0x2,0x0,
+0x0,0x47,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2e,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,0x1,
+0x0,0x0,0x91,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0xd4,
+0x0,0x0,0x0,0x3,0x2,0x0,0x0,0xc4,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x60,0x2,0x0,0x0,0x37,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x34,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x4,0x0,
+0x0,0x59,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x4,0x0,0x0,0x6f,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x3,0x0,0x0,0xc7,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x55,0x2,0x0,0x0,0x30,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xc2,0x0,0x0,0x0,0xd8,0x2,0x0,0x0,0x3e,0x0,0x0,0x0,0x7a,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0x79,0x5,0x0,0x0,0x72,0x5,
+0x0,0x0,0x42,0x2,0x0,0x0,0x76,0x1,0x0,0x0,0x1b,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xe2,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0xcd,0x0,0x0,0x0,0xa7,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x66,0x5,0x0,0x0,0x25,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0xb2,0x2,0x0,0x0,0x36,0x1,0x0,
+0x0,0xdf,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xab,0x5,0x0,0x0,0x1a,0x1,
+0x0,0x0,0x3f,0x1,0x0,0x0,0x8b,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x41,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4c,
+0x0,0x0,0x0,0x30,0x3,0x0,0x0,0x3c,0x5,0x0,0x0,0xe2,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x70,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x43,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x63,0x1,0x0,0x0,0x70,0x2,0x0,0x0,0xbc,0x5,0x0,0x0,0xd2,0x0,0x0,
+0x0,0x37,0x2,0x0,0x0,0xf,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x44,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x5,
+0x0,0x0,0xc9,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x5,0x0,0x0,0x1e,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,0x3,0x0,0x0,0x97,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0x6d,0x2,0x0,0x0,0x3c,0x0,0x0,
+0x0,0x16,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0xe9,0x1,
+0x0,0x0,0x4d,0x2,0x0,0x0,0xe2,0x0,0x0,0x0,0x77,0x1,0x0,0x0,0xc6,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x92,0x2,0x0,0x0,0xeb,
+0x2,0x0,0x0,0x6d,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x53,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x43,0x5,0x0,0x0,
+0x25,0x5,0x0,0x0,0xa3,0x2,0x0,0x0,0x26,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa6,0x5,0x0,0x0,0x1f,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5c,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2d,0x0,
+0x0,0x0,0xbe,0x0,0x0,0x0,0x59,0x5,0x0,0x0,0x58,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x7b,0x5,0x0,0x0,0xd5,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x22,0x5,0x0,0x0,0x34,0x2,0x0,0x0,0x3f,0x2,0x0,0x0,0x24,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0xbe,0x5,0x0,0x0,0xc9,0x2,0x0,
+0x0,0xe7,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x4,0x0,0x0,0x2e,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x99,0x5,0x0,0x0,0xd4,0x0,0x0,0x0,0x46,
+0x3,0x0,0x0,0x85,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x63,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,
+0xe,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x6b,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x5,0x0,0x0,0x51,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x66,0x5,0x0,0x0,0xfa,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x69,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x31,0x3,0x0,0x0,0x11,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x2,0x0,
+0x0,0xfb,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0xbc,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x3,0x0,0x0,0xe6,0x4,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0x1e,0x0,0x0,0x0,
+0xc0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0xe4,0x1,0x0,
+0x0,0x36,0x1,0x0,0x0,0x6e,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x73,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x81,0x5,
+0x0,0x0,0x90,0x1,0x0,0x0,0x3d,0x1,0x0,0x0,0x21,0x0,0x0,0x0,0x2f,0x3,
+0x0,0x0,0x74,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x54,0x1,0x0,0x0,0x3e,
+0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0xee,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x81,0x5,0x0,0x0,0xcc,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7c,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x62,0x2,0x0,
+0x0,0x6c,0x5,0x0,0x0,0x35,0x3,0x0,0x0,0x6d,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x88,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe4,
+0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x81,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x5,0x0,0x0,
+0xd8,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x65,0x2,0x0,0x0,0x9,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x5b,0x5,0x0,0x0,0x19,0x5,0x0,0x0,0x9b,0x3,
+0x0,0x0,0x6f,0x0,0x0,0x0,0x55,0x2,0x0,0x0,0xb1,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x55,0x3,0x0,0x0,0xd8,0x4,0x0,0x0,0x36,0x1,0x0,0x0,0x6a,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x4,0x0,0x0,0xfa,0x4,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x8,0x5,0x0,0x0,0xf3,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xa,0x2,0x0,0x0,0x22,0x0,0x0,0x0,0x62,0x2,0x0,0x0,0x4d,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x1,0x0,0x0,0xcf,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0xd2,0x1,0x0,0x0,
+0x92,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x7e,0x5,0x0,
+0x0,0x3c,0x5,0x0,0x0,0xa8,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x98,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x3,
+0x0,0x0,0xe3,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5b,0x5,0x0,0x0,0x48,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x83,0x5,0x0,0x0,
+0xd3,0x4,0x0,0x0,0x3f,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x43,0x0,0x0,
+0x0,0x41,0x0,0x0,0x0,0x8f,0x1,0x0,0x0,0x5,0x1,0x0,0x0,0x67,0x3,0x0,
+0x0,0x8f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x51,0x1,0x0,0x0,0x50,0x1,
+0x0,0x0,0x2c,0x3,0x0,0x0,0xfc,0x1,0x0,0x0,0x5,0x0,0x0,0x0,0xf,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x8c,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0x99,0x3,0x0,0x0,0x67,0x5,0x0,0x0,
+0x6c,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0x91,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x73,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc4,0x2,0x0,0x0,0x6,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x64,0x2,0x0,0x0,0x9,0x0,0x0,0x0,0x6,0x3,0x0,0x0,0x33,0x0,0x0,0x0,
+0x9e,0x5,0x0,0x0,0x4d,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x39,0x3,0x0,
+0x0,0x18,0x1,0x0,0x0,0x3b,0x1,0x0,0x0,0x72,0x2,0x0,0x0,0x21,0x1,0x0,
+0x0,0xe,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x1,0x0,0x0,0x65,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0x4e,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x37,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x76,0x0,0x0,0x0,0x6b,0x1,0x0,0x0,0xd4,0x4,0x0,0x0,0x37,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x92,0x3,0x0,0x0,0x9b,0x1,0x0,0x0,0x43,0x5,0x0,0x0,0xdf,
+0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0x13,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0xb0,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x12,0x0,0x0,0x0,0xc9,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x2a,0x0,
+0x0,0x0,0xc2,0x0,0x0,0x0,0x95,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x12,
+0x2,0x0,0x0,0x73,0x3,0x0,0x0,0x67,0x5,0x0,0x0,0x67,0x1,0x0,0x0,0x95,
+0x5,0x0,0x0,0x17,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbd,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x64,0x0,0x0,0x0,
+0x70,0x5,0x0,0x0,0x4b,0x2,0x0,0x0,0xfb,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x37,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbf,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x10,0x4,
+0x0,0x0,0x19,0x5,0x0,0x0,0x92,0x5,0x0,0x0,0x1b,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x82,0x1,0x0,0x0,0x38,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x31,0x5,0x0,0x0,0x67,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x78,0x0,0x0,
+0x0,0x2a,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x1,0x0,0x0,0x58,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x50,0x1,0x0,0x0,0xf,
+0x5,0x0,0x0,0x17,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,0x0,0x0,
+0x48,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0xc4,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x8f,0x2,0x0,0x0,0xa3,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x81,0x2,0x0,0x0,0x85,0x2,0x0,0x0,0x5e,0x5,0x0,0x0,0xdc,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0x69,0x2,0x0,0x0,
+0x45,0x5,0x0,0x0,0x9a,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xcf,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9f,0x5,0x0,
+0x0,0xc6,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x1,0x0,0x0,0xa6,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x2,0x0,0x0,0x14,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x24,0x2,0x0,0x0,0x17,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,
+0x51,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x48,0x2,0x0,0x0,0x91,0x5,0x0,
+0x0,0x49,0x5,0x0,0x0,0xa1,0x2,0x0,0x0,0xa5,0x0,0x0,0x0,0xaa,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x72,0x3,0x0,0x0,0x82,0x1,0x0,0x0,0x4d,0x5,
+0x0,0x0,0xbd,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0x6c,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x5,0x0,0x0,0xf0,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0x23,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe,0x5,0x0,0x0,0x9e,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdf,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x55,
+0x1,0x0,0x0,0x9,0x1,0x0,0x0,0x67,0x0,0x0,0x0,0x4,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x28,0x1,0x0,0x0,0x11,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf0,0x4,0x0,0x0,0x69,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x2,
+0x0,0x0,0x3c,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x9,
+0x1,0x0,0x0,0x22,0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x5c,0x0,0x0,0x0,0x23,0x2,0x0,0x0,0x46,0x5,0x0,0x0,0x67,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x13,0x1,0x0,0x0,0x5f,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb7,0x3,0x0,0x0,0xc,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,
+0x2,0x0,0x0,0x23,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xea,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc5,0x3,0x0,0x0,
+0x7c,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x2,0x0,0x0,0x9b,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x3,0x0,0x0,0x2e,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf2,0x0,0x0,0x0,0x73,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x8e,0x2,0x0,0x0,0x6a,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x19,0x3,0x0,
+0x0,0x1d,0x2,0x0,0x0,0xad,0x5,0x0,0x0,0xbf,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x37,0x1,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,
+0x5,0x0,0x0,0xbf,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xfb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xca,0x0,0x0,0x0,
+0x69,0x5,0x0,0x0,0x89,0x5,0x0,0x0,0x1e,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb2,0x1,0x0,0x0,0x43,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x1,
+0x0,0x0,0x41,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x11,0x3,0x0,0x0,0x17,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0xe4,0x1,0x0,0x0,
+0x5b,0x1,0x0,0x0,0x5e,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x5,0x0,
+0x0,0x33,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0xc0,0x0,
+0x0,0x0,0x66,0x2,0x0,0x0,0x43,0x1,0x0,0x0,0xaf,0x2,0x0,0x0,0xb7,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x8c,0x2,0x0,0x0,0xa0,0x1,0x0,0x0,0x5e,
+0x0,0x0,0x0,0x18,0x2,0x0,0x0,0x27,0x0,0x0,0x0,0xf1,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xba,0x2,0x0,0x0,0x10,0x1,0x0,0x0,0xe,0x0,0x0,0x0,
+0xd7,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x1c,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x15,0x2,
+0x0,0x0,0x7a,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x4,0x0,0x0,0x9,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x4c,0x5,0x0,0x0,0x90,0x2,0x0,0x0,
+0x14,0x0,0x0,0x0,0x99,0x0,0x0,0x0,0xa5,0x0,0x0,0x0,0x44,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0x60,0x5,0x0,0x0,0x19,0x5,0x0,0x0,0x2f,0x0,0x0,
+0x0,0x85,0x1,0x0,0x0,0x7f,0x5,0x0,0x0,0x2d,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xbf,0x3,0x0,0x0,0x82,0x1,0x0,0x0,0x27,0x0,0x0,0x0,0x5d,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x19,0x5,0x0,0x0,0xa4,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x17,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x62,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0xd3,0x0,0x0,0x0,0x3c,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x2,0x0,0x0,0x3c,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x2d,0x5,0x0,0x0,0xeb,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x5b,0x5,0x0,0x0,0x61,0x5,0x0,0x0,0x4e,0x0,0x0,0x0,0x92,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xd7,0x0,0x0,0x0,0x9d,0x2,0x0,0x0,0x1f,0x3,0x0,
+0x0,0x7f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0x36,0x0,
+0x0,0x0,0x6a,0x2,0x0,0x0,0x13,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x24,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfd,
+0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x26,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x5,0x0,0x0,
+0x18,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x18,0x5,0x0,
+0x0,0x7f,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x29,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x98,0x0,
+0x0,0x0,0xfd,0x4,0x0,0x0,0xd,0x1,0x0,0x0,0xc5,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x68,0x5,0x0,0x0,0x38,0x5,0x0,0x0,0xb5,0x0,0x0,0x0,0xd1,
+0x1,0x0,0x0,0xc6,0x3,0x0,0x0,0x15,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf,0x3,0x0,0x0,0x9b,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x49,0x3,0x0,
+0x0,0x34,0x2,0x0,0x0,0x4b,0x2,0x0,0x0,0x5d,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa1,0x3,0x0,0x0,0x6,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x31,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe,
+0x5,0x0,0x0,0x24,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x32,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x3,0x0,0x0,
+0x55,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0x9d,0x2,0x0,
+0x0,0xe2,0x2,0x0,0x0,0x3f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x35,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa3,0x2,
+0x0,0x0,0x86,0x2,0x0,0x0,0x35,0x2,0x0,0x0,0x1b,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x9c,0x1,0x0,0x0,0x2d,0x5,0x0,0x0,0xcf,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x33,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0xa1,0x2,0x0,0x0,0xdd,0x4,0x0,
+0x0,0xfb,0x4,0x0,0x0,0x4e,0x3,0x0,0x0,0x5d,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1f,0x3,0x0,0x0,0x30,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xe2,
+0x2,0x0,0x0,0xa0,0x1,0x0,0x0,0x9e,0x5,0x0,0x0,0x9c,0x1,0x0,0x0,0x15,
+0x3,0x0,0x0,0x66,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3b,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x48,0x3,0x0,0x0,
+0xaa,0x2,0x0,0x0,0xbf,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x37,0x1,0x0,0x0,
+0x5c,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0xa1,0x2,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x2,0x0,0x0,0x27,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x42,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xa5,0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x43,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x5,0x0,
+0x0,0x33,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbd,0x5,0x0,0x0,0xb0,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x1,0x0,0x0,0x63,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0xb6,0x0,0x0,0x0,0x12,0x0,0x0,0x0,
+0x9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0x86,0x2,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x28,0x2,0x0,0x0,0x56,0x1,
+0x0,0x0,0x50,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0x3e,
+0x5,0x0,0x0,0xd4,0x4,0x0,0x0,0x17,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x50,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xea,0x2,0x0,0x0,0x9c,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x52,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x4,0x0,
+0x0,0x17,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x51,0x0,
+0x0,0x0,0x9e,0x5,0x0,0x0,0xd4,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x56,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,
+0x5,0x0,0x0,0xd,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x57,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,0x1,0x0,0x0,
+0x5,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x9c,0x3,0x0,0x0,0xe0,0x2,0x0,
+0x0,0x64,0x1,0x0,0x0,0xa9,0x1,0x0,0x0,0x34,0x2,0x0,0x0,0x4d,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x5,0x0,0x0,0x46,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x31,0x3,0x0,0x0,0xf9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x56,0x5,0x0,0x0,0x63,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7b,0x2,0x0,
+0x0,0x24,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0x9f,0x0,
+0x0,0x0,0x1e,0x2,0x0,0x0,0x5c,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x62,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb,
+0x3,0x0,0x0,0x9d,0x2,0x0,0x0,0x55,0x5,0x0,0x0,0x3e,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0x5b,0x1,0x0,0x0,0x62,0x1,0x0,0x0,
+0x64,0x2,0x0,0x0,0x37,0x1,0x0,0x0,0x95,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x68,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x9d,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0xd,0x3,0x0,0x0,0x25,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0x2e,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0xc5,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6b,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x70,0x5,0x0,0x0,0x11,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x3a,0x3,0x0,0x0,
+0x1d,0x5,0x0,0x0,0x3e,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x37,0x1,0x0,
+0x0,0x55,0x0,0x0,0x0,0x99,0x5,0x0,0x0,0xcc,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1,0x1,0x0,0x0,0x86,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x71,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x66,
+0x5,0x0,0x0,0x48,0x3,0x0,0x0,0x51,0x2,0x0,0x0,0x50,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x73,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf,0x3,0x0,0x0,0x3d,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x74,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x21,0x5,
+0x0,0x0,0x3,0x3,0x0,0x0,0x74,0x5,0x0,0x0,0x23,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x44,0x2,0x0,0x0,0xf7,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x77,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x7b,0x2,0x0,0x0,0xf,0x1,0x0,0x0,0x2b,0x2,0x0,0x0,0xc3,0x2,0x0,0x0,
+0x57,0x3,0x0,0x0,0x6f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x78,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf,0x3,0x0,
+0x0,0xd1,0x1,0x0,0x0,0x88,0x5,0x0,0x0,0xbb,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xdd,0x2,0x0,0x0,0x2a,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x26,
+0x5,0x0,0x0,0x6c,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x80,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,
+0x56,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0x99,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x81,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0xd9,0x2,0x0,0x0,0xa1,0x2,0x0,0x0,0x22,0x0,0x0,0x0,0x1a,0x5,0x0,
+0x0,0xc9,0x3,0x0,0x0,0xa6,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x82,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x7a,0x5,
+0x0,0x0,0xeb,0x0,0x0,0x0,0x29,0x5,0x0,0x0,0xc3,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x6f,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x3f,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x85,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5,0x3,0x0,0x0,0xb4,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x13,0x5,0x0,0x0,0x3a,0x3,0x0,0x0,0x2d,0x3,0x0,0x0,0xe,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x6c,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xea,0x2,0x0,0x0,0xc9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x8f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xa5,0x5,0x0,0x0,0xb0,0x5,0x0,0x0,0x2b,0x0,0x0,0x0,0x7d,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x3b,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0xc3,0x2,0x0,0x0,0xec,0x4,0x0,0x0,0x16,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xda,0x0,0x0,0x0,0x31,0x0,0x0,0x0,
+0x1f,0x3,0x0,0x0,0x5b,0x3,0x0,0x0,0x2f,0x1,0x0,0x0,0x5,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x58,0x0,0x0,
+0x0,0x30,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x2,0x0,0x0,0x9c,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0xc2,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9b,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x93,0x2,0x0,0x0,0x31,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9d,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x22,0x0,
+0x0,0x0,0xe8,0x4,0x0,0x0,0x2b,0x5,0x0,0x0,0x21,0x2,0x0,0x0,0xf3,0x1,
+0x0,0x0,0x92,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x2,0x0,0x0,0x28,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x15,0x1,0x0,0x0,0x74,0x5,0x0,0x0,
+0x66,0x5,0x0,0x0,0x26,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xe8,0x1,0x0,
+0x0,0xb8,0x3,0x0,0x0,0x4,0x1,0x0,0x0,0x4a,0x5,0x0,0x0,0x57,0x0,0x0,
+0x0,0xd7,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x0,0x0,0x0,0x59,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x4,0x0,0x0,0xeb,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x3,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xaf,0x5,0x0,0x0,0x4d,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa9,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa1,0x5,
+0x0,0x0,0x28,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0xf5,
+0x0,0x0,0x0,0x68,0x5,0x0,0x0,0x34,0x5,0x0,0x0,0x4f,0x0,0x0,0x0,0xdf,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x48,0x3,0x0,0x0,0xfa,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc0,0x1,0x0,0x0,0x5a,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x72,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb9,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x62,
+0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xba,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x78,0x0,0x0,0x0,
+0xd9,0x1,0x0,0x0,0xb7,0x0,0x0,0x0,0x5d,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xbb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4c,0x2,0x0,0x0,0x86,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbd,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4a,0x0,
+0x0,0x0,0xf0,0x1,0x0,0x0,0x4f,0x5,0x0,0x0,0x6c,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xa0,0x5,0x0,0x0,0x8e,0x5,0x0,0x0,0x96,0x3,0x0,0x0,0x13,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0x80,0x5,0x0,0x0,
+0x9a,0x5,0x0,0x0,0x1c,0x3,0x0,0x0,0x5a,0x0,0x0,0x0,0x25,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xea,0x0,0x0,0x0,0x95,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0xb9,0x0,0x0,0x0,0x18,0x2,0x0,0x0,0xfe,0x1,0x0,0x0,0x2a,0x3,
+0x0,0x0,0xf7,0x4,0x0,0x0,0x5f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,
+0x5,0x0,0x0,0x30,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc5,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x42,0x2,0x0,0x0,
+0x60,0x2,0x0,0x0,0xbd,0x0,0x0,0x0,0xb,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x1e,0x2,0x0,0x0,0xb9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xca,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x48,0x2,
+0x0,0x0,0x38,0x2,0x0,0x0,0x4b,0x5,0x0,0x0,0x7a,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0x66,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xcd,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x33,0x5,0x0,0x0,0x3b,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xcf,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x5,0x0,
+0x0,0xb2,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x1,0x0,0x0,0x11,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x16,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0x5b,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x5,0x3,0x0,0x0,0x94,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd5,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x0,
+0x0,0x0,0x39,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x3,0x0,0x0,0x1e,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd3,0x4,0x0,0x0,0xd8,0x2,0x0,0x0,
+0x77,0x2,0x0,0x0,0x32,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xdb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xea,0x2,0x0,
+0x0,0x9a,0x0,0x0,0x0,0x3d,0x1,0x0,0x0,0x43,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf9,0x4,0x0,0x0,0x21,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,
+0x2,0x0,0x0,0xae,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xde,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x61,0x5,0x0,0x0,
+0xb2,0x0,0x0,0x0,0x47,0x3,0x0,0x0,0x3d,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xdf,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xab,0x2,0x0,0x0,0x84,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe1,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x0,
+0x0,0x0,0x69,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x18,0x3,0x0,0x0,0x30,
+0x3,0x0,0x0,0xcc,0x2,0x0,0x0,0xb6,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x77,0x2,0x0,0x0,0x5d,0x1,0x0,0x0,0xc2,0x5,0x0,0x0,0xac,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0xfb,0x4,0x0,0x0,0xc7,0x0,0x0,
+0x0,0x8b,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0x69,0x2,
+0x0,0x0,0x96,0x0,0x0,0x0,0xd5,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe7,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,
+0x0,0x0,0x0,0x19,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc7,0x1,0x0,0x0,
+0xaa,0x5,0x0,0x0,0xbe,0x2,0x0,0x0,0x26,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x5b,0x0,0x0,0x0,0xc1,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xeb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf7,0x0,
+0x0,0x0,0xc0,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x71,0x3,0x0,0x0,0x1d,
+0x2,0x0,0x0,0x80,0x5,0x0,0x0,0x6,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x7b,0x0,0x0,0x0,0xc3,0x2,0x0,0x0,0x63,0x1,0x0,0x0,0xc6,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x85,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x19,0x2,0x0,0x0,0x2e,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,
+0x5,0x0,0x0,0x63,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf7,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x60,0x5,0x0,0x0,
+0x4e,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x8f,0x2,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x8a,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x48,0x5,0x0,0x0,0x97,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x29,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0x16,0x2,0x0,0x0,0x6,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x13,0x5,0x0,0x0,0x69,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf4,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xff,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd,
+0x0,0x0,0x0,0xda,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0x60,0x2,0x0,0x0,0x5b,0x0,0x0,0x0,
+0xc1,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x1,0x0,0x0,0x44,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x90,0x1,0x0,0x0,0x42,0x0,0x0,0x0,0x2b,0x1,0x0,0x0,0x8b,
+0x5,0x0,0x0,0x84,0x1,0x0,0x0,0x27,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x64,0x2,0x0,0x0,0x79,0x3,0x0,0x0,0x55,0x1,0x0,0x0,0x25,0x5,0x0,0x0,
+0x77,0x3,0x0,0x0,0x8f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x90,0x2,0x0,
+0x0,0x20,0x5,0x0,0x0,0x59,0x2,0x0,0x0,0xa8,0x0,0x0,0x0,0x37,0x3,0x0,
+0x0,0x63,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9,0x5,0x0,0x0,0xc7,0x1,
+0x0,0x0,0xad,0x5,0x0,0x0,0xda,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5c,
+0x2,0x0,0x0,0x2c,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x0,0x0,0x0,
+0x78,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x38,0x5,0x0,
+0x0,0x56,0x5,0x0,0x0,0x3,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x0,0x3,
+0x0,0x0,0x28,0x0,0x0,0x0,0xd2,0x1,0x0,0x0,0xa7,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd4,0x2,0x0,0x0,0x2d,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x50,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x11,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x43,0x0,0x0,
+0x0,0xe0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0xad,0x0,0x0,0x0,0x25,0x3,0x0,
+0x0,0x9e,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x66,0x2,0x0,0x0,0x59,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x70,0x2,0x0,0x0,0xa1,
+0x0,0x0,0x0,0x53,0x2,0x0,0x0,0xf,0x3,0x0,0x0,0x67,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xe,0x5,0x0,0x0,0xd1,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x18,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x44,0x5,0x0,0x0,0xf,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1b,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb,0x0,
+0x0,0x0,0x5d,0x1,0x0,0x0,0x8b,0x1,0x0,0x0,0xe6,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xae,0x3,0x0,0x0,0x21,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x62,0x0,0x0,0x0,0xb0,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x22,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x56,0x5,0x0,
+0x0,0x58,0x5,0x0,0x0,0xe,0x3,0x0,0x0,0xc5,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0xf0,0x4,0x0,0x0,0xb0,0x5,0x0,0x0,0xc1,0x0,0x0,0x0,0xf5,0x4,
+0x0,0x0,0x73,0x3,0x0,0x0,0x6,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x25,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x41,
+0x3,0x0,0x0,0xc6,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x27,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x12,0x2,0x0,0x0,
+0xe0,0x1,0x0,0x0,0x35,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0xb6,0x5,0x0,0x0,
+0xc6,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x65,0x1,0x0,0x0,0x18,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x4,0x0,0x0,0x73,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x56,0x5,0x0,0x0,0xd7,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x34,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe0,0x4,0x0,0x0,0xd8,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x35,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x5,0x0,
+0x0,0x70,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3c,0x1,0x0,0x0,0xc0,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x81,0x1,0x0,0x0,0xd5,0x4,0x0,0x0,0x62,
+0x5,0x0,0x0,0x26,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x39,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,
+0xc7,0x1,0x0,0x0,0x81,0x2,0x0,0x0,0x77,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3b,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x50,0x3,0x0,0x0,0x6c,0x0,0x0,0x0,0x7b,0x5,0x0,0x0,0x5f,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xad,0x5,0x0,0x0,0xd2,0x1,0x0,0x0,0x27,0x0,
+0x0,0x0,0x14,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x1,0x0,0x0,0xe7,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x63,0x1,0x0,0x0,0x5c,0x5,0x0,0x0,
+0x65,0x5,0x0,0x0,0x3d,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x40,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1a,0x3,0x0,
+0x0,0xc6,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x64,0x5,0x0,0x0,0x36,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0xc0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xf3,0x4,0x0,0x0,0xbc,0x5,0x0,0x0,0x62,0x0,0x0,0x0,0x12,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,0x70,0x5,0x0,0x0,0xe7,0x2,
+0x0,0x0,0xa4,0x0,0x0,0x0,0x65,0x5,0x0,0x0,0x29,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xa,0x5,0x0,0x0,0x41,0x3,0x0,0x0,0xc4,0x3,0x0,0x0,0xba,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0x4,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x42,0x3,0x0,0x0,0x6,0x5,0x0,
+0x0,0x43,0x0,0x0,0x0,0x9d,0x1,0x0,0x0,0x5f,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xc0,0x3,0x0,0x0,0x5c,0x3,0x0,0x0,0x6b,0x5,0x0,0x0,0x3e,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb,0x2,0x0,0x0,0x26,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc4,0x2,0x0,0x0,0xb7,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5c,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xef,0x2,
+0x0,0x0,0xfe,0x4,0x0,0x0,0x0,0x3,0x0,0x0,0x1f,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x29,0x5,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x60,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf7,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x61,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x1,0x0,
+0x0,0x9b,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0x22,0x2,
+0x0,0x0,0x1f,0x1,0x0,0x0,0x48,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x63,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x44,
+0x2,0x0,0x0,0x4f,0x1,0x0,0x0,0xde,0x4,0x0,0x0,0x27,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x68,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x77,0x3,0x0,0x0,0x23,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6a,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x50,0x0,
+0x0,0x0,0xa,0x3,0x0,0x0,0x52,0x2,0x0,0x0,0x9b,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x76,0x1,0x0,0x0,0x5b,0x2,0x0,0x0,0x20,0x2,0x0,0x0,0x19,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x5,0x0,0x0,0x70,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x75,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5c,0x3,0x0,0x0,0xdb,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x74,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x48,
+0x3,0x0,0x0,0xd8,0x1,0x0,0x0,0xc,0x1,0x0,0x0,0x30,0x1,0x0,0x0,0xa7,
+0x5,0x0,0x0,0x3c,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x76,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xba,0x5,0x0,0x0,
+0xe7,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x68,0x1,0x0,0x0,0x4b,0x1,0x0,
+0x0,0xfc,0x0,0x0,0x0,0x64,0x5,0x0,0x0,0xfd,0x1,0x0,0x0,0xb5,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0xd1,0x2,0x0,0x0,0x1c,0x1,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0x28,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x5,0x0,0x0,0xeb,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0xd9,0x2,0x0,0x0,0xad,0x0,0x0,0x0,0x43,0x1,0x0,
+0x0,0x49,0x5,0x0,0x0,0xb7,0x5,0x0,0x0,0x87,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x2e,0x0,0x0,0x0,0xe,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x86,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,
+0x0,0x0,0x0,0xa0,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x87,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa7,0x5,0x0,0x0,
+0xe6,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x8a,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfd,0x1,0x0,0x0,0xfc,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8c,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x11,0x3,
+0x0,0x0,0x70,0x2,0x0,0x0,0x55,0x1,0x0,0x0,0x15,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x43,0x1,0x0,0x0,0xbb,0x5,0x0,0x0,0x23,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x69,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0x9b,0x0,0x0,0x0,0x5e,0x0,0x0,
+0x0,0xd4,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x34,0x0,
+0x0,0x0,0xea,0x4,0x0,0x0,0xb6,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x93,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc7,
+0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x94,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x62,0x2,0x0,0x0,
+0x49,0x1,0x0,0x0,0xbb,0x5,0x0,0x0,0x39,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x97,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x7b,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,0xad,0x3,0x0,0x0,0x5a,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x4,0x0,0x0,0x20,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x3c,0x3,0x0,0x0,0x5c,0x1,0x0,0x0,0xa3,0x0,0x0,0x0,0x9e,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0xaa,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x11,0x5,0x0,0x0,0xaa,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe1,0x4,0x0,0x0,0xe6,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa3,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x54,
+0x0,0x0,0x0,0xef,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0x58,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xba,0x5,0x0,0x0,0xc6,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa6,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x3,0x2,0x0,0x0,0x68,0x3,0x0,0x0,0xfe,0x4,0x0,0x0,0x7c,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x70,0x1,0x0,0x0,0x29,0x3,0x0,0x0,0x15,0x2,
+0x0,0x0,0xbb,0x1,0x0,0x0,0x57,0x2,0x0,0x0,0xe3,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0xc3,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xb9,0x3,0x0,0x0,0x30,0x1,0x0,0x0,0x90,0x5,0x0,0x0,0x9b,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0xf6,0x4,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x22,0x1,0x0,0x0,0x28,0x0,0x0,0x0,0xc3,0x3,0x0,0x0,0x62,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x38,0x3,0x0,0x0,0x70,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x15,0x3,0x0,0x0,0x65,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb3,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x7b,0x5,0x0,0x0,0xcc,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb4,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x78,0x0,
+0x0,0x0,0x2a,0x3,0x0,0x0,0xa,0x5,0x0,0x0,0x1e,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x79,0x5,0x0,0x0,0x10,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x4b,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x22,0x2,0x0,0x0,
+0x95,0x3,0x0,0x0,0x8f,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xba,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2c,0x2,0x0,
+0x0,0x15,0x1,0x0,0x0,0x83,0x5,0x0,0x0,0x86,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf,0x3,0x0,0x0,0x2f,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbe,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,
+0x0,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,
+0xe9,0x1,0x0,0x0,0x3b,0x1,0x0,0x0,0x9f,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x15,0x2,0x0,0x0,0xed,0x2,0x0,0x0,0x76,0x0,0x0,0x0,0x38,0x1,0x0,
+0x0,0x68,0x5,0x0,0x0,0x4e,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc3,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x1,
+0x0,0x0,0x6c,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0xae,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x5,0x0,0x0,0x77,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6a,0x3,0x0,0x0,0x61,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe8,0x1,0x0,0x0,0xdc,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xca,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,
+0x0,0x0,0x0,0xed,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xcc,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x5,0x0,0x0,
+0x46,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0xdc,0x4,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x44,0x2,0x0,0x0,0x6a,0x1,0x0,0x0,0x8e,0x2,
+0x0,0x0,0x57,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa3,0x0,0x0,0x0,0x70,
+0x1,0x0,0x0,0x24,0x2,0x0,0x0,0x33,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd2,0x0,0x0,0x0,0x8,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x3,0x0,
+0x0,0x33,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x4,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x5,0x0,0x0,0x16,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0xb9,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xdf,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa6,0x0,0x0,0x0,0xf,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe1,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x0,
+0x0,0x0,0xb4,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0xc5,
+0x0,0x0,0x0,0x6e,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x4a,0x2,0x0,0x0,0x18,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe4,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4a,0x0,0x0,
+0x0,0x2b,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x25,0x3,0x0,0x0,0x6d,0x0,
+0x0,0x0,0x2f,0x2,0x0,0x0,0x2e,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xec,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x12,
+0x0,0x0,0x0,0xb,0x1,0x0,0x0,0x5e,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xee,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xe8,0x4,0x0,0x0,0xa6,0x5,0x0,0x0,0xbb,0x0,0x0,0x0,0xcf,0x2,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x71,0x1,0x0,0x0,0x15,0x2,
+0x0,0x0,0x86,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x6c,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,0x5,0x0,0x0,0x2c,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x13,0x5,0x0,0x0,0x6f,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xc0,0x2,0x0,0x0,0x5d,0x1,0x0,0x0,0x99,0x5,0x0,0x0,0xaf,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x65,0x1,0x0,0x0,0x2f,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x10,0x1,0x0,0x0,0x79,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xfc,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x84,0x0,0x0,0x0,0x35,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xfd,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x46,0x1,
+0x0,0x0,0xed,0x2,0x0,0x0,0xb5,0x5,0x0,0x0,0xb8,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1,0x3,0x0,0x0,0xb9,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x45,0x5,0x0,0x0,0x31,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe,0x1,0x0,
+0x0,0x55,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x21,0x2,0x0,0x0,0x68,0x5,
+0x0,0x0,0x6,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa3,
+0x2,0x0,0x0,0x7,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x79,0x5,0x0,0x0,
+0x8b,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x3,0x0,0x0,0xf4,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x2,0x0,0x0,0x36,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x35,0x3,0x0,0x0,0x11,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x10,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x1,0x0,
+0x0,0x6d,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x85,0x2,0x0,0x0,0x90,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0xa7,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x67,0x5,0x0,0x0,0x4e,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1e,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x0,
+0x0,0x0,0x42,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xeb,0x2,0x0,0x0,0xef,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x3,0x0,0x0,0x97,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x29,0x3,0x0,0x0,0x36,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x41,0x5,0x0,0x0,0x46,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x28,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa2,
+0x0,0x0,0x0,0x89,0x1,0x0,0x0,0xb7,0x1,0x0,0x0,0x99,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0x7b,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xea,0x4,0x0,0x0,0x3c,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd4,0x2,
+0x0,0x0,0x8f,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5c,0x5,0x0,0x0,0x44,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x2,0x0,0x0,0xc7,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0x46,0x5,0x0,
+0x0,0x3,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x26,0x3,0x0,0x0,0xdf,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0xb0,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0xfc,0x1,0x0,0x0,0x6d,0x0,0x0,0x0,
+0x5d,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x34,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x5,0x0,0x0,0xba,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x5b,0x5,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xee,0x2,0x0,0x0,0xd2,0x2,0x0,0x0,0x0,0x2,0x0,0x0,0x20,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x68,0x5,0x0,0x0,0xec,0x0,0x0,0x0,0x92,0x2,0x0,
+0x0,0xd,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x92,0x3,0x0,0x0,0x30,0x2,
+0x0,0x0,0x9d,0x5,0x0,0x0,0x24,0x2,0x0,0x0,0x6d,0x5,0x0,0x0,0x7,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x3d,
+0x1,0x0,0x0,0xd5,0x1,0x0,0x0,0xa7,0x5,0x0,0x0,0x2e,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x64,0x1,0x0,0x0,0xc6,0x3,0x0,0x0,
+0xf6,0x4,0x0,0x0,0x1f,0x5,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x43,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x81,0x1,0x0,0x0,0xd9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x45,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x3,
+0x0,0x0,0x74,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x5,0x0,0x0,0x35,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,0x4,0x0,0x0,0x21,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x82,0x5,0x0,0x0,0x7a,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x7b,0x5,0x0,0x0,0x14,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb2,
+0x1,0x0,0x0,0xbb,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x53,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x25,0x3,0x0,0x0,
+0x15,0x5,0x0,0x0,0x76,0x1,0x0,0x0,0x39,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x54,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xc0,0x5,0x0,0x0,0xc4,0x1,0x0,0x0,0x26,0x3,0x0,0x0,0x6e,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xba,0x5,0x0,0x0,0xb4,0x3,0x0,0x0,0x7a,0x5,0x0,0x0,0x52,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8f,0x2,0x0,0x0,0x93,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0x8,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe5,0x4,0x0,0x0,0x9e,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x62,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,
+0x5,0x0,0x0,0x9f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x64,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x64,0x0,0x0,0x0,
+0x79,0x5,0x0,0x0,0xff,0x0,0x0,0x0,0xba,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x66,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x44,0x0,0x0,0x0,0x3c,0x1,0x0,0x0,0x4d,0x0,0x0,0x0,0xd5,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x97,0x1,0x0,0x0,0x64,0x2,0x0,0x0,0xaf,0x2,
+0x0,0x0,0x9d,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0x23,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0xe2,0x1,0x0,0x0,
+0xc0,0x2,0x0,0x0,0x2b,0x3,0x0,0x0,0x7f,0x0,0x0,0x0,0x92,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbe,0x3,0x0,0x0,0x6c,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x67,0x5,0x0,0x0,0x23,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,
+0x0,0x0,0x0,0x24,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x73,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x5,0x0,0x0,
+0xaa,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x3,0x0,0x0,0x1e,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x2,0x0,0x0,0x47,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4c,0x0,0x0,0x0,0x17,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x79,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0xc0,0x2,0x0,0x0,0xde,0x0,0x0,0x0,0xe,0x2,0x0,0x0,0x35,0x3,0x0,0x0,
+0x62,0x5,0x0,0x0,0x9c,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7e,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xeb,0x2,0x0,
+0x0,0xc6,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x19,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x71,0x1,0x0,0x0,0x3a,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0x8f,0x1,0x0,0x0,0x87,0x0,0x0,0x0,
+0x9b,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x5,0x0,0x0,0xe7,0x2,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0xa6,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x5b,0x5,0x0,0x0,0x23,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x89,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x1e,0x2,0x0,0x0,0x39,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8a,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xea,0x4,0x0,
+0x0,0x23,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0xf5,0x4,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x14,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x3,0x0,0x0,0xb2,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf3,0x4,0x0,0x0,0xb1,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x5c,0x2,0x0,0x0,0x98,0x3,0x0,0x0,0xde,0x2,0x0,0x0,
+0xab,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0xb1,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x50,0x3,0x0,0x0,0x89,0x1,0x0,0x0,0x40,0x5,
+0x0,0x0,0xa1,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0xb7,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbb,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,
+0x54,0x1,0x0,0x0,0x2c,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x93,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1a,0x3,0x0,
+0x0,0x49,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0x4a,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x49,0x0,0x0,0x0,0x5d,0x2,0x0,0x0,0x55,0x0,0x0,0x0,0xd5,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x13,0x2,0x0,0x0,0x52,0x3,0x0,0x0,0x44,
+0x1,0x0,0x0,0x3b,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x97,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x5,0x0,0x0,
+0xb6,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x84,0x5,0x0,0x0,0x12,0x1,0x0,
+0x0,0x5a,0x5,0x0,0x0,0x50,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x99,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x62,0x5,
+0x0,0x0,0x68,0x1,0x0,0x0,0x58,0x0,0x0,0x0,0xc3,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x9,0x3,0x0,0x0,0xb6,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0xee,
+0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x5,0x0,0x0,0x76,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2b,0x5,0x0,0x0,0x93,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xe9,0x4,0x0,0x0,0xf8,0x4,0x0,0x0,0x9a,0x0,0x0,0x0,0x58,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x5,0x0,0x0,0x98,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x4,0x3,0x0,0x0,0xcc,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x55,0x1,0x0,0x0,0x9,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x3,
+0x0,0x0,0x6,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x21,0x5,0x0,0x0,0x32,
+0x5,0x0,0x0,0x3a,0x3,0x0,0x0,0x1c,0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0x5f,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x10,0x2,0x0,0x0,0x23,0x2,0x0,0x0,
+0x49,0x3,0x0,0x0,0x8c,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x3,0x0,
+0x0,0x43,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x5,0x0,0x0,0xe8,0x4,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2e,0x2,0x0,0x0,0xa2,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0xa8,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xaf,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x29,0x2,0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x2,
+0x0,0x0,0x2d,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x2,0x0,0x0,0xb4,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x5,0x0,0x0,0xbf,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2a,0x1,0x0,0x0,0xce,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x24,0x3,0x0,0x0,0xeb,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd9,
+0x2,0x0,0x0,0x31,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0xc5,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x99,0x3,0x0,0x0,0xfb,0x1,0x0,0x0,0x81,0x1,0x0,0x0,
+0x56,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x0,0x0,0x0,0x56,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0x86,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x5f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x62,0x2,0x0,0x0,0x66,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xcb,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x5,0x0,
+0x0,0xbb,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x3,0x0,0x0,0xde,0x4,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0xe3,0x4,0x0,0x0,0x2f,
+0x3,0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd3,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,
+0x6a,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x5,0x0,0x0,0xa7,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xaa,0x3,0x0,0x0,0x18,0x0,0x0,0x0,0x1e,0x2,
+0x0,0x0,0xed,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,0x5a,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0xfd,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xb1,0x2,0x0,0x0,0x71,0x1,0x0,0x0,0xaa,0x1,0x0,
+0x0,0x37,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc5,0x3,0x0,0x0,0x42,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x2,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xaf,0x5,0x0,0x0,0x97,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x36,0x5,0x0,0x0,0x40,0x0,0x0,0x0,0x46,0x1,0x0,0x0,0xcb,0x0,0x0,
+0x0,0x6c,0x3,0x0,0x0,0x18,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x3,
+0x0,0x0,0xaa,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x94,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x2,0x0,0x0,0x66,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0x12,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb5,0x3,0x0,0x0,0x26,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3a,
+0x3,0x0,0x0,0x59,0x4,0x0,0x0,0xb3,0x0,0x0,0x0,0xb7,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf3,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x31,0x3,0x0,0x0,0x97,0x1,0x0,0x0,0x69,0x5,0x0,0x0,0x1d,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x57,0x2,0x0,0x0,0x5f,0x2,0x0,0x0,0x56,0x5,
+0x0,0x0,0x66,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x45,0x1,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x27,0x2,0x0,0x0,0x42,0x2,0x0,0x0,
+0xd1,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfb,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc4,0x3,0x0,
+0x0,0xcb,0x0,0x0,0x0,0x7c,0x5,0x0,0x0,0xb,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x22,0x3,0x0,0x0,0x4c,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,0x4,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x2,0x0,0x0,0x2b,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x73,0x0,0x0,0x0,0x77,0x1,0x0,0x0,
+0xe6,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0xf3,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc5,0x1,0x0,0x0,0xbf,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x84,0x5,0x0,0x0,0x80,0x5,0x0,0x0,0x57,0x2,0x0,0x0,0xa3,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x5,0x0,0x0,0x4,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x41,0x5,0x0,0x0,0x2c,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1a,0x5,0x0,0x0,0x13,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x27,
+0x5,0x0,0x0,0xf4,0x4,0x0,0x0,0xc3,0x5,0x0,0x0,0xa6,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0x63,0x0,0x0,0x0,0xb6,0x5,0x0,0x0,
+0x8f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x25,0x2,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x3,0x0,0x0,0x58,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0x23,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x86,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x18,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x3,0x0,
+0x0,0x3c,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x0,0x0,0x0,0x2e,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x5,0x0,0x0,0x5c,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x35,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xaf,0x2,0x0,0x0,0x18,0x1,0x0,0x0,0x7f,0x0,0x0,0x0,0x1b,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x23,0x3,0x0,0x0,0x7d,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x34,0x5,0x0,0x0,0x8b,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x23,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x30,0x5,0x0,0x0,0xb8,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x26,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x2,0x0,
+0x0,0xb2,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x3,0x0,0x0,0x3d,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa,0x2,0x0,0x0,0x3a,0x0,0x0,0x0,0x3e,
+0x3,0x0,0x0,0x3f,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x29,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3,0x3,0x0,0x0,
+0x97,0x5,0x0,0x0,0xf4,0x4,0x0,0x0,0x43,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x24,0x5,0x0,0x0,0xd3,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2c,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xb,0x3,
+0x0,0x0,0x24,0x1,0x0,0x0,0xb,0x1,0x0,0x0,0x8a,0x0,0x0,0x0,0xf,0x5,
+0x0,0x0,0xf4,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0xa,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x43,0x2,0x0,0x0,0x1b,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc2,0x0,0x0,0x0,0x13,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3b,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x26,
+0x5,0x0,0x0,0xc9,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3d,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x2,0x0,0x0,
+0x5d,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,0x2,0x0,0x0,0x80,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x37,0x0,
+0x0,0x0,0xed,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x1,0x0,0x0,0x60,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x37,0x0,0x0,0x0,0xc1,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x57,0x3,0x0,0x0,0xfb,0x4,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1d,0x5,0x0,0x0,0xed,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,
+0x0,0x0,0x0,0xd,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4c,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x44,0x2,0x0,0x0,
+0x48,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x71,0x5,0x0,0x0,0xc9,0x1,0x0,
+0x0,0x9c,0x1,0x0,0x0,0x67,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x52,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x0,
+0x0,0x0,0xc3,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x2,0x0,0x0,0x21,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0xfa,0x4,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4e,0x3,0x0,0x0,0x18,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xbb,0x5,0x0,0x0,0x7c,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5b,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc0,
+0x2,0x0,0x0,0x92,0x5,0x0,0x0,0xca,0x1,0x0,0x0,0xce,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x24,0x2,0x0,0x0,0x2e,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x5f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xaf,0x5,0x0,0x0,0xa5,0x5,0x0,0x0,0x76,0x1,0x0,0x0,0x6f,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x5,0x0,0x0,0x2e,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xea,0x4,0x0,0x0,0x85,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x64,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x43,0x2,0x0,0x0,0x74,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x69,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x2,0x0,
+0x0,0x6d,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x99,0x3,0x0,0x0,0x60,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,0x0,0x0,0xc4,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0xa4,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x72,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf4,0x4,0x0,0x0,0x79,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x74,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x62,0x5,
+0x0,0x0,0xe8,0x1,0x0,0x0,0xad,0x3,0x0,0x0,0x9d,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe7,0x2,0x0,0x0,0x7f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x79,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x9b,0x3,0x0,0x0,0xfc,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7a,0x5,0x0,
+0x0,0xd,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x2,0x0,0x0,0x8a,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x62,0x5,0x0,0x0,0x47,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1e,0x5,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x7f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa3,0x3,0x0,0x0,0xb0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x82,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x12,0x2,
+0x0,0x0,0xf,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x99,0x2,0x0,0x0,0x7f,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x3,0x0,0x0,0x38,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0xa,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x76,0x0,0x0,0x0,0x52,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8b,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd,
+0x5,0x0,0x0,0x56,0x5,0x0,0x0,0xbe,0x2,0x0,0x0,0x8e,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x19,0x2,0x0,0x0,0x77,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x8f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xcc,0x1,0x0,0x0,0x73,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x93,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x5,
+0x0,0x0,0x5,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfa,0x4,0x0,0x0,0x59,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x38,0x3,0x0,0x0,0x78,0x5,0x0,0x0,
+0x49,0x1,0x0,0x0,0x20,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x98,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x1,0x0,
+0x0,0x58,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0x63,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0xc6,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x20,0x2,0x0,0x0,0x1c,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9d,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x68,0x5,0x0,0x0,0xba,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9e,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x3,
+0x0,0x0,0x9d,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x3,0x0,0x0,0x77,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc6,0x3,0x0,0x0,0x25,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x60,0x5,0x0,0x0,0x40,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc6,0x0,0x0,0x0,0x7a,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa8,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,
+0x5,0x0,0x0,0x98,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa9,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x2,0x0,0x0,
+0x94,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xab,0x0,0x0,0x0,0x25,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x98,0x5,0x0,0x0,0xbf,0x2,
+0x0,0x0,0x25,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x5,0x0,0x0,0x92,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4a,0x3,0x0,0x0,0xa6,0x2,0x0,0x0,
+0xbe,0x2,0x0,0x0,0x7,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb0,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x0,0x0,
+0x0,0x6c,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe2,0x2,0x0,0x0,0x60,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc7,0x0,0x0,0x0,0x9c,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf,0x5,0x0,0x0,0x2a,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb7,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3b,0x3,0x0,0x0,0x40,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xba,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x0,
+0x0,0x0,0xb2,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8d,0x0,0x0,0x0,0x64,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x4c,0x1,0x0,0x0,
+0x44,0x5,0x0,0x0,0xd,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xbe,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,0x4,0x0,
+0x0,0x36,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x1,0x0,0x0,0xae,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0x94,0x3,0x0,0x0,0x74,
+0x0,0x0,0x0,0xff,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xce,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcc,0x1,0x0,0x0,
+0x3e,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x7a,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0x91,0x1,0x0,0x0,0x2f,0x3,
+0x0,0x0,0xda,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x2,0x0,0x0,0xc6,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2b,0x3,0x0,0x0,0xc5,0x1,0x0,0x0,
+0x71,0x0,0x0,0x0,0xac,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd3,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x73,0x2,0x0,
+0x0,0x4e,0x1,0x0,0x0,0x54,0x5,0x0,0x0,0x64,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc6,0x0,0x0,0x0,0xb,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd6,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,
+0x2,0x0,0x0,0x14,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xda,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,
+0x13,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x96,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x2,0x0,0x0,0x0,0x7c,0x5,
+0x0,0x0,0xc4,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc0,0x5,0x0,0x0,0xf0,
+0x1,0x0,0x0,0xe6,0x4,0x0,0x0,0x39,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe1,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd2,0x1,0x0,0x0,0xc5,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe4,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x0,0x0,
+0x0,0x9b,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0xba,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0xfc,0x1,0x0,0x0,0x6c,
+0x0,0x0,0x0,0xf6,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xeb,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x18,0x0,0x0,0x0,
+0xd0,0x0,0x0,0x0,0x5a,0x5,0x0,0x0,0x4c,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xed,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xa6,0x2,0x0,0x0,0x22,0x2,0x0,0x0,0x2e,0x2,0x0,0x0,0x64,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x3,0x0,0x0,0x0,0x69,0x2,0x0,0x0,0x69,0x3,0x0,0x0,0x1,0x3,
+0x0,0x0,0xb2,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xbd,0x0,0x0,0x0,0xd,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x14,0x2,0x0,0x0,0xbe,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf5,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc7,0x0,0x0,
+0x0,0xad,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x1,0x0,0x0,0xd7,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x65,0x1,0x0,0x0,0x2,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xb9,0x5,0x0,0x0,0xc2,0x3,0x0,0x0,0xc,0x3,0x0,0x0,
+0x52,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x98,0x0,0x0,0x0,0x0,0x3,0x0,
+0x0,0xab,0x5,0x0,0x0,0x1b,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xfd,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x84,0x1,
+0x0,0x0,0xa0,0x5,0x0,0x0,0x55,0x3,0x0,0x0,0x97,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x55,0x2,0x0,0x0,0x61,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x0,0x0,
+0x0,0xa,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x7d,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaa,0x3,0x0,0x0,0xa8,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,
+0x73,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x59,0x4,0x0,
+0x0,0xd,0x0,0x0,0x0,0x37,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2f,0x2,
+0x0,0x0,0xef,0x2,0x0,0x0,0x54,0x3,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x59,0x3,0x0,0x0,0xa1,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0xd9,0x4,0x0,0x0,0x8a,0x1,0x0,0x0,0x72,0x5,0x0,0x0,0x63,0x5,0x0,0x0,
+0x66,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x10,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x2,0x0,
+0x0,0x93,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x82,0x1,0x0,0x0,0xf5,0x0,
+0x0,0x0,0x1c,0x1,0x0,0x0,0x36,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x14,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x75,
+0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x16,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x3,0x0,0x0,
+0xec,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x3,0x0,0x0,0x85,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe8,0x0,0x0,0x0,0x99,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1d,0x5,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1c,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x54,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1d,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x4,0x0,
+0x0,0x3,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd4,0x2,0x0,0x0,0xce,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x5,0x0,0x0,0xc4,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x54,0x5,0x0,0x0,0xef,0x0,0x0,0x0,0x17,0x2,0x0,0x0,
+0x17,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0x28,0x1,0x0,
+0x0,0x22,0x2,0x0,0x0,0x2a,0x5,0x0,0x0,0xba,0x5,0x0,0x0,0x9d,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0xc,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x46,0x1,0x0,0x0,0x49,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x56,0x1,0x0,0x0,0x59,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2d,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x0,0x0,
+0x0,0x7,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x92,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x33,0x5,0x0,0x0,0x6a,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x75,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x31,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x34,0x1,0x0,0x0,0x59,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x32,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x2,
+0x0,0x0,0x53,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x73,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf7,0x4,0x0,0x0,0x19,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x41,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5f,0x5,0x0,0x0,0x30,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x40,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,
+0x3,0x0,0x0,0x89,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x41,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xaf,0x5,0x0,0x0,
+0x5f,0x2,0x0,0x0,0x33,0x5,0x0,0x0,0xac,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x45,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,
+0x0,0x6c,0x2,0x0,0x0,0x20,0x0,0x0,0x0,0x35,0x0,0x0,0x0,0x61,0x0,0x0,
+0x0,0xbe,0x3,0x0,0x0,0xfa,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x48,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x5,
+0x0,0x0,0x17,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfc,0x4,0x0,0x0,0x42,
+0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0xa7,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4a,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x72,0x5,0x0,0x0,0xec,0x0,0x0,0x0,0x6b,0x5,0x0,0x0,0x2f,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x9a,0x3,0x0,0x0,0x41,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x41,0x5,0x0,0x0,0xbc,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x55,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xeb,
+0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x56,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x5,0x0,0x0,
+0xcc,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7a,0x2,0x0,0x0,0x11,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0xfc,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0x5b,0x5,0x0,0x0,0x57,0x2,0x0,0x0,0x3f,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x45,0x0,0x0,0x0,0x4d,0x0,0x0,0x0,
+0xad,0x3,0x0,0x0,0x16,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x63,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,0x2,0x0,
+0x0,0xfe,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x2,0x0,0x0,0x59,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0xbb,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x68,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x35,0x1,0x0,0x0,0x59,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6b,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x3,
+0x0,0x0,0x77,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0xbd,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3c,0x5,0x0,0x0,0xfa,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe9,0x2,0x0,0x0,0xa1,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xad,0x3,0x0,0x0,0xc,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x76,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x99,
+0x2,0x0,0x0,0x15,0x0,0x0,0x0,0xd4,0x0,0x0,0x0,0xa7,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x36,0x3,0x0,0x0,0xe,0x2,0x0,0x0,
+0x1e,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9f,0x5,0x0,0x0,0x11,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x9d,0x1,0x0,0x0,0x4d,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x65,0x5,0x0,0x0,0x5a,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0xc3,0x5,0x0,0x0,0x5a,0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x10,0x5,0x0,0x0,
+0x32,0x3,0x0,0x0,0x41,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7e,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x5,0x0,
+0x0,0x19,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xf2,0x4,0x0,0x0,0xa8,0x3,
+0x0,0x0,0xb5,0x0,0x0,0x0,0xe,0x5,0x0,0x0,0xbe,0x3,0x0,0x0,0xa7,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x0,0x0,0x0,0x5e,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x22,0x5,0x0,0x0,0x2,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x85,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2e,0x0,0x0,0x0,0x4a,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x87,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x3,
+0x0,0x0,0x37,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x1,0x0,0x0,0x3f,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x3,0x0,0x0,0xb6,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x85,0x0,0x0,0x0,0x68,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x35,0x3,0x0,0x0,0xfb,0x4,0x0,0x0,0xd9,0x2,0x0,0x0,0x88,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0xc1,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x28,0x3,0x0,0x0,0xbc,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x93,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x29,0x2,0x0,0x0,0x43,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x98,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x1,
+0x0,0x0,0xe,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x33,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3e,0x1,0x0,0x0,0xaa,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x55,0x5,0x0,0x0,0x29,0x5,0x0,0x0,0x14,0x2,0x0,
+0x0,0xf1,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0x19,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x1,0x0,0x0,0x4,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x3c,0x3,0x0,0x0,0x6,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa5,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc,0x0,0x0,0x0,0xdf,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa7,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2,0x0,
+0x0,0x0,0x32,0x5,0x0,0x0,0x9b,0x1,0x0,0x0,0x8b,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x62,0x5,0x0,0x0,0xfd,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x27,0x1,0x0,0x0,0xf6,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xaa,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x2,0x0,
+0x0,0x7a,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x5,0x0,0x0,0x60,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x5,0x0,0x0,0x6f,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xc2,0x5,0x0,0x0,0x53,0x0,0x0,0x0,0xc7,0x1,0x0,0x0,
+0x4d,0x1,0x0,0x0,0xb1,0x2,0x0,0x0,0x27,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb0,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x1a,0x3,0x0,0x0,0x22,0x1,0x0,0x0,0x43,0x0,0x0,0x0,0x37,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x8e,0x2,0x0,0x0,0x4c,0x1,0x0,0x0,0x2b,0x5,
+0x0,0x0,0xc0,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x9e,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4a,0x2,0x0,0x0,0x2e,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,0x7a,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x81,0x1,0x0,0x0,0x5b,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb8,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,
+0x2,0x0,0x0,0x82,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa,0x1,0x0,0x0,
+0x84,0x0,0x0,0x0,0x76,0x5,0x0,0x0,0xf5,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xbe,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe,0x3,0x0,0x0,0x5f,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbf,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6d,0x5,
+0x0,0x0,0x4d,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0xa8,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x31,0x3,0x0,0x0,0xd8,0x4,0x0,0x0,
+0x4c,0x2,0x0,0x0,0x16,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc2,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x3,0x0,
+0x0,0x23,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,0x0,0x0,0x0,0x7,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0xe5,0x4,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa3,0x0,0x0,0x0,0x5c,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x8a,0x5,0x0,0x0,0x14,0x1,0x0,0x0,0xd7,0x4,0x0,0x0,0x57,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x9a,0x3,0x0,0x0,0xd9,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x3f,0x3,0x0,0x0,0xd7,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xce,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xad,0x3,0x0,0x0,0x7a,0x2,0x0,0x0,0x17,0x2,0x0,0x0,0xdf,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x38,0x5,0x0,0x0,0x99,0x5,0x0,0x0,0x72,0x5,0x0,
+0x0,0x7,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x2,0x0,0x0,0x5f,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0x1b,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xc1,0x2,0x0,0x0,0x78,0x0,0x0,0x0,0x19,0x5,0x0,0x0,
+0x29,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x5,0x0,0x0,0x9d,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0xe2,0x0,0x0,0x0,0x97,0x3,
+0x0,0x0,0x64,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfd,0x4,0x0,0x0,0x8,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0xd,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x65,0x2,0x0,0x0,0x1e,0x0,0x0,0x0,0x4,0x3,0x0,
+0x0,0x99,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x0,0x0,0x0,0x3b,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x9c,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x6b,0x5,0x0,0x0,0x68,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf2,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x90,0x5,0x0,0x0,0x10,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf4,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4a,0x1,
+0x0,0x0,0x50,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x87,0x5,0x0,0x0,0xd,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x4,0x0,0x0,0x24,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe,0x1,0x0,0x0,0x67,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x6a,0x0,0x0,0x0,0xc,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfc,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,
+0x0,0x0,0x0,0x1d,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xfd,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x2,0x0,0x0,
+0x1e,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0x13,0x2,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x5,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x18,0x5,0x0,0x0,0x31,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x51,0x0,0x0,0x0,0x43,0x3,0x0,0x0,0x4a,0x1,0x0,0x0,0x34,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0x28,0x3,0x0,0x0,0xac,0x3,0x0,
+0x0,0xf5,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x5,0x0,0x0,0x94,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x0,0x0,0x0,0x51,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xb8,0x3,0x0,0x0,0x25,0x1,0x0,0x0,0xdb,0x2,0x0,0x0,
+0x16,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0x54,0x1,0x0,
+0x0,0x56,0x1,0x0,0x0,0x1d,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x12,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x99,0x5,
+0x0,0x0,0xf9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x1,0x0,0x0,0x51,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf0,0x4,0x0,0x0,0x72,0x3,0x0,0x0,
+0x40,0x1,0x0,0x0,0x53,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1d,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x50,0x0,0x0,
+0x0,0xbe,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x1,0x0,0x0,0x1e,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0x63,
+0x1,0x0,0x0,0xe4,0x0,0x0,0x0,0x90,0x1,0x0,0x0,0xa4,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x88,0x0,0x0,0x0,0xb3,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x26,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xea,0x2,0x0,0x0,0x74,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x27,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x2,
+0x0,0x0,0xfa,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x2,0x0,0x0,0xa,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x45,0x3,0x0,0x0,0x71,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xfd,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x22,0x5,0x0,0x0,0xd3,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x30,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x77,
+0x3,0x0,0x0,0x42,0x3,0x0,0x0,0x27,0x0,0x0,0x0,0xc,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0x30,0x1,0x0,0x0,0x33,0x5,0x0,0x0,
+0x3,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x5,0x0,0x0,0x7e,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x64,0x5,0x0,0x0,0x5e,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd,0x3,0x0,0x0,0x1d,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3d,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x90,0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x43,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc7,0x1,0x0,
+0x0,0x77,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,0x2,0x0,0x0,0x3e,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2e,0x2,0x0,0x0,0xc7,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x38,0x2,0x0,0x0,0x92,0x1,0x0,0x0,
+0x98,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x0,0x0,0x0,0xa,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xe9,0x1,0x0,0x0,0xa0,0x1,0x0,0x0,0x10,0x0,
+0x0,0x0,0xfa,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x5,0x0,0x0,0xae,
+0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x3,0x0,0x0,0x43,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0x6,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb5,0x3,0x0,0x0,0xb,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x65,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x89,
+0x0,0x0,0x0,0xe2,0x1,0x0,0x0,0x30,0x5,0x0,0x0,0x4e,0x2,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1c,0x3,0x0,0x0,0x61,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x6d,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xbc,0x3,0x0,0x0,0x3f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6e,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x0,
+0x0,0x0,0x4,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0xc7,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x1,0x0,0x0,0xbf,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0xa7,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xfd,0x2,0x0,0x0,0x52,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,
+0x4,0x0,0x0,0xf,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7a,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,
+0x97,0x3,0x0,0x0,0xd9,0x2,0x0,0x0,0xc9,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x7b,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x55,0x1,0x0,0x0,0x1f,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7c,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x0,
+0x0,0x0,0x5f,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x72,0x3,0x0,0x0,0x30,
+0x1,0x0,0x0,0x2e,0x2,0x0,0x0,0xc1,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7f,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x6b,0x5,0x0,0x0,0x19,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x80,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcc,0x2,0x0,
+0x0,0xc2,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x22,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x5,0x0,0x0,0x78,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd9,0x4,0x0,0x0,0xc2,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x89,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x98,0x1,0x0,0x0,0x18,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x13,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0xea,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xd3,0x0,0x0,0x0,0xeb,0x4,0x0,0x0,0x91,0x0,0x0,0x0,0xa6,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x91,0x1,0x0,0x0,0x55,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x66,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0xa7,0x5,0x0,
+0x0,0x6d,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6c,0x2,0x0,0x0,0x4e,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5f,0x3,0x0,0x0,0x4d,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0xe2,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x99,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x13,0x5,0x0,0x0,0x8c,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9a,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x4,
+0x0,0x0,0xd4,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x49,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0xa,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0xbe,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x96,0x3,0x0,0x0,0x8f,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x50,
+0x3,0x0,0x0,0x9,0x5,0x0,0x0,0x62,0x1,0x0,0x0,0x76,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0x9,0x3,0x0,0x0,0xbe,0x3,0x0,0x0,
+0xdf,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,0x5,0x0,0x0,0xd,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x5,0x0,0x0,0x6,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1d,0x5,0x0,0x0,0x27,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xaf,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x48,0x3,0x0,0x0,0xd8,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb0,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x0,0x0,
+0x0,0x77,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x3,0x0,0x0,0xba,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x3,0x0,0x0,0xe6,0x0,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa3,0x3,0x0,0x0,0x8f,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x1a,0x3,0x0,0x0,0x3f,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb6,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x2,
+0x0,0x0,0xdb,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x2a,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0x50,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x99,0x3,0x0,0x0,0xe0,0x4,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x8c,0x2,0x0,0x0,0x48,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,
+0x0,0x0,0x0,0xb8,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x5,0x0,0x0,
+0x7d,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x5,0x0,0x0,0x89,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x48,0x3,0x0,0x0,0x8f,0x2,
+0x0,0x0,0x3,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x73,0x2,0x0,0x0,0x8a,
+0x5,0x0,0x0,0xf1,0x1,0x0,0x0,0x24,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xcc,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x7a,0x5,0x0,0x0,0xd8,0x1,0x0,0x0,0x3e,0x5,0x0,0x0,0xd4,0x4,0x0,0x0,
+0xf,0x3,0x0,0x0,0x2f,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xcd,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x3,0x0,
+0x0,0x58,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x5,0x1,
+0x0,0x0,0xe1,0x4,0x0,0x0,0xfe,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd0,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf7,
+0x4,0x0,0x0,0xee,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd1,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,
+0xa1,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x3,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0xbf,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x78,0x5,0x0,0x0,0xe9,0x1,0x0,0x0,0xa,0x0,0x0,0x0,0xe2,
+0x1,0x0,0x0,0xc5,0x0,0x0,0x0,0x67,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd7,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xf9,0x4,0x0,0x0,0x1c,0x5,0x0,0x0,0x49,0x3,0x0,0x0,0x29,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xc3,0x3,0x0,0x0,0x5a,0x1,0x0,0x0,0x84,0x0,0x0,
+0x0,0x47,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x6,0x0,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x22,0x3,0x0,0x0,0x10,0x3,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe2,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x5e,0x2,0x0,0x0,0x1a,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x15,0x5,
+0x0,0x0,0xdd,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x3,0x0,0x0,0xba,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4d,0x5,0x0,0x0,0x9c,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x10,0x3,0x0,0x0,0x20,0x1,0x0,0x0,0x84,0x5,0x0,
+0x0,0xb6,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x26,0x5,0x0,0x0,0x5c,0x3,
+0x0,0x0,0x78,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xed,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb9,
+0x2,0x0,0x0,0x90,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xee,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,
+0x61,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x56,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4a,0x3,0x0,0x0,0xb7,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xda,0x0,0x0,0x0,0x9d,0x1,0x0,0x0,0xbe,0x3,0x0,0x0,0xb6,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x5,0x0,0x0,0x62,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x70,0x2,0x0,0x0,0x5b,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfb,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa3,
+0x0,0x0,0x0,0x3c,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xfc,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x0,0x0,0x0,
+0x3a,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0xd6,0x1,0x0,
+0x0,0x5e,0x0,0x0,0x0,0xe7,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xff,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x5,
+0x0,0x0,0x95,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf1,0x2,0x0,0x0,0xc2,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x2,0x0,0x0,0x94,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe9,0x4,0x0,0x0,0xf2,0x4,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x15,0x2,0x0,0x0,0x7a,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x12,
+0x0,0x0,0x0,0xbf,0x1,0x0,0x0,0x4a,0x1,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x4e,0x3,0x0,0x0,0x28,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xda,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x0,
+0x0,0x0,0x40,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf7,0x4,0x0,0x0,0x1e,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x3,0x0,0x0,0x24,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2e,0x2,0x0,0x0,0x3f,0x0,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xe1,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x65,0x1,0x0,0x0,0x29,0x3,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb1,0x5,0x0,0x0,0x52,0x1,0x0,0x0,0xf3,
+0x4,0x0,0x0,0x9e,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1c,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x2,0x0,0x0,
+0xe0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x38,0x3,0x0,0x0,0x95,0x3,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xad,0x3,0x0,0x0,0x19,0x0,0x0,0x0,0xc5,0x1,
+0x0,0x0,0x4b,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x56,0x1,0x0,0x0,0x55,
+0x3,0x0,0x0,0x1e,0x2,0x0,0x0,0x94,0x0,0x0,0x0,0x59,0x5,0x0,0x0,0x19,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x1c,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x55,0x0,0x0,0x0,0x6d,0x2,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xbd,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x29,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,
+0x3,0x0,0x0,0x6a,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2c,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb5,0x3,0x0,0x0,
+0xac,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,0x3d,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x81,0x5,0x0,0x0,0x4c,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf8,0x4,0x0,0x0,0x72,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3b,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x22,0x2,0x0,0x0,0x93,0x5,0x0,0x0,0x9b,0x0,0x0,0x0,0xdc,0x4,0x0,0x0,
+0xc4,0x2,0x0,0x0,0x3e,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3c,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8f,0x5,0x0,
+0x0,0x20,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0x35,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x65,0x1,0x0,0x0,0xd8,0x4,0x0,0x0,0x88,
+0x5,0x0,0x0,0xc,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x41,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1b,0x2,0x0,0x0,
+0x72,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x26,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0xbc,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x6d,0x2,0x0,0x0,0x98,0x2,0x0,0x0,0x76,0x0,0x0,0x0,0x6a,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe2,0x4,0x0,0x0,0x23,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x59,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x90,0x2,0x0,0x0,0x36,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4d,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,
+0x2,0x0,0x0,0x7a,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4f,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,0x0,0x0,0x0,
+0x1b,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x91,0x1,0x0,0x0,0x8f,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0x17,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x57,0x2,0x0,0x0,0x67,0x0,0x0,0x0,0xd4,0x2,0x0,0x0,0x1d,
+0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x1,0x0,0x0,0x56,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x34,0x1,0x0,0x0,0x50,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x3a,0x5,0x0,0x0,0xaa,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x64,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,
+0x3,0x0,0x0,0x2,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x67,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x5,0x0,0x0,
+0x32,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x83,0x0,0x0,0x0,0xa8,0x3,0x0,
+0x0,0x3c,0x3,0x0,0x0,0x66,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6b,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x71,0x3,
+0x0,0x0,0xfe,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0xb6,0x2,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x8c,0x2,0x0,0x0,0xc0,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6f,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xe9,0x1,0x0,0x0,0xa1,0x5,0x0,0x0,0xa,0x2,0x0,0x0,0x4a,0x5,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0xd3,0x4,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x90,0x1,0x0,0x0,0x90,0x5,0x0,0x0,0x59,0x5,0x0,0x0,0xc,0x5,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa5,0x5,0x0,0x0,0x72,0x5,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0x15,0x2,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x7a,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x28,0x0,0x0,0x0,0xba,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7c,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x86,0x1,
+0x0,0x0,0x92,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x27,0x5,0x0,0x0,0x21,
+0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc9,0x1,0x0,0x0,0x31,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2c,0x3,0x0,0x0,0xd,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x87,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x70,0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0x22,0x0,0x0,0x0,0x4d,0x2,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x3,0x0,0x0,0xd2,0x1,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0x2b,0x3,0x0,0x0,0xaf,0x0,0x0,0x0,
+0xf7,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x3,0x0,0x0,0x4c,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0x2,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0xf7,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x97,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0xd2,0x2,0x0,0x0,0x14,0x1,0x0,0x0,0x5c,0x3,0x0,0x0,0x5c,0x0,0x0,0x0,
+0x56,0x0,0x0,0x0,0xfd,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9a,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x2,0x0,
+0x0,0x33,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x0,0x0,0x0,0x85,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6d,0x5,0x0,0x0,0x18,0x3,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0x60,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa2,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x52,0x3,0x0,0x0,0x9e,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa6,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x50,0x0,
+0x0,0x0,0xe0,0x1,0x0,0x0,0x73,0x2,0x0,0x0,0xa1,0x1,0x0,0x0,0x7d,0x1,
+0x0,0x0,0x33,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x84,0x5,0x0,0x0,0xd3,
+0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x2,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xec,0x4,0x0,0x0,0x2c,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x77,0x3,0x0,0x0,0x3b,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xad,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x19,
+0x1,0x0,0x0,0x1b,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb0,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,
+0x47,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x1,0x0,0x0,0x99,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x86,0x5,0x0,0x0,0x52,0x1,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x17,0x5,0x0,0x0,0x5f,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb9,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x62,0x1,0x0,0x0,0x2a,0x3,0x0,0x0,0x46,0x3,0x0,0x0,0xc3,0x2,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x11,0x3,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x32,0x3,0x0,0x0,0xd5,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbe,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,
+0x5,0x0,0x0,0x62,0x1,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbf,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x1,0x0,0x0,
+0x9a,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x67,0x3,0x0,0x0,0x1b,0x1,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0xc0,0x3,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0xb4,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xca,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x45,0x1,0x0,0x0,0x40,0x0,0x0,0x0,0x2f,0x5,0x0,0x0,0x68,0x1,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xcf,0x0,0x0,0x0,0xb,0x1,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x10,0x1,0x0,0x0,0xe0,0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd2,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe2,
+0x2,0x0,0x0,0xdb,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd3,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x5,0x0,0x0,
+0xed,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x1,0x0,0x0,0x34,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0x98,0x5,0x0,0x0,0xeb,0x4,
+0x0,0x0,0x6,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,0xf6,
+0x2,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x98,0x3,0x0,0x0,
+0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x77,0x1,0x0,0x0,0x79,0x5,0x0,0x0,0x1,0x5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x80,0x0,0x0,0x0,0xc2,0x5,0x0,0x0,0x45,0x3,0x0,0x0,0x8c,0x0,
+0x0,0x0,0x76,0x0,0x0,0x0,0xe5,0x4,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe1,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7a,
+0x0,0x0,0x0,0x73,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe4,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfe,0x1,0x0,0x0,
+0xa0,0x1,0x0,0x0,0x35,0x2,0x0,0x0,0x4d,0x0,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe6,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xb9,0x5,0x0,0x0,0xa,0x2,0x0,0x0,0x4e,0x0,0x0,0x0,0x14,0x5,0x0,
+0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x1,0x0,0x0,0xe3,0x0,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6a,0x2,0x0,0x0,0x68,0x3,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xeb,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x90,0x1,0x0,0x0,0x15,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xed,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdb,0x2,0x0,
+0x0,0xfc,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0xf0,0x1,
+0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x5,0x0,0x0,0xeb,0x4,0x0,0x0,0x1,
+0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0x1,0x5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf5,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x2b,0x2,0x0,0x0,0x32,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf6,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf1,0x0,
+0x0,0x0,0xb9,0x5,0x0,0x0,0x13,0x5,0x0,0x0,0x12,0x5,0x0,0x0,0x1,0x5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x7d,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf9,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x24,0x1,0x0,0x0,0xa9,0x5,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfb,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x0,0x0,
+0x0,0x33,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x56,0x2,0x0,0x0,0x67,0x2,
+0x0,0x0,0x77,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfe,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,
+0x1,0x0,0x0,0xf8,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x4,0x0,0x0,
+0x15,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0x7e,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x3,0x0,0x0,0x74,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xcf,0x1,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x21,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc8,0x3,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x4,0x0,
+0x0,0xbe,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0xf9,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe6,0x1,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xec,0x1,0x0,0x0,0xee,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xda,0x2,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x2,
+0x0,0x0,0xb3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x4,0x0,0x0,0x97,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x4,0x0,0x0,0x16,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbb,0x4,0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd5,0x3,0x0,0x0,0x44,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x59,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,
+0x1,0x0,0x0,0xb5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5c,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x1,0x0,0x0,
+0x33,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0x65,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x1,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0x32,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x77,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x2d,0x2,0x0,0x0,0xf7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x87,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xff,0x3,0x0,
+0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x3,0x0,0x0,0xcd,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x1,0x0,0x0,0xec,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0x7d,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x68,0x4,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x3,
+0x0,0x0,0xfa,0x0,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0xa8,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xb3,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x57,0x4,0x0,0x0,0xef,0x3,0x0,0x0,0xe6,0x1,0x0,
+0x0,0x76,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x43,0x4,0x0,0x0,0x8,0x4,
+0x0,0x0,0x9c,0x4,0x0,0x0,0x83,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,
+0x2,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1c,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,0x0,
+0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0xa2,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x4,0x0,0x0,0xb,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x51,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x1,0x4,0x0,0x0,0x82,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x57,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,0x2,0x0,
+0x0,0xda,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x2,0x0,0x0,0xed,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0x8d,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xdd,0x1,0x0,0x0,0x8d,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x4,
+0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xef,0x1,0x0,0x0,0x94,
+0x1,0x0,0x0,0x2,0x4,0x0,0x0,0x13,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xbb,0x4,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x3,0x0,
+0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x1,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5e,0x3,0x0,0x0,0xa9,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x87,0x1,0x0,0x0,0x7b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xee,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfc,0x2,0x0,0x0,0xf3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x74,0x2,
+0x0,0x0,0x7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0xe9,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x1,0x0,0x0,0xcd,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd2,0x3,0x0,0x0,0xb5,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x43,0x4,0x0,0x0,0xad,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,
+0x4,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,
+0xab,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0xce,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x87,0x2,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4,
+0x0,0x0,0x84,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x2,0x0,0x0,0xe9,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x75,0x2,0x0,0x0,0x13,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xa8,0x1,0x0,0x0,0xac,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x76,0x4,0x0,0x0,0x86,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x79,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,
+0x1,0x0,0x0,0x74,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7a,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd5,0x3,0x0,0x0,
+0x75,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x3,0x0,0x0,0x58,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe0,0x3,0x0,0x0,0x58,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xef,0x1,0x0,0x0,0xa5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x91,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xcb,0x1,0x0,0x0,0xe2,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x93,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x3,0x0,
+0x0,0x44,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x4,0x0,0x0,0xa0,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0xd,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xbb,0x4,0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x98,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4,0x4,0x0,0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x2,
+0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0xb5,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x31,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x41,0x4,0x0,0x0,0x13,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf6,0x3,0x0,0x0,0xc7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,
+0x3,0x0,0x0,0x12,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xda,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,
+0xce,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x3,0x0,0x0,0xd,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x2,0x0,0x0,0xa6,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0x33,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x16,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xf,0x4,0x0,0x0,0x8,0x4,0x0,0x0,0xfe,0x3,0x0,0x0,0xd0,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0x80,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x6f,0x2,0x0,0x0,0x75,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,
+0x2,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x31,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,
+0xa5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x3,0x0,0x0,0x82,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x43,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc8,0x1,0x0,0x0,0x50,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x44,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x85,0x3,0x0,
+0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xa9,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x2,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x74,0x2,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x6c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xcb,0x1,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x79,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x4,
+0x0,0x0,0x71,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xff,0x3,0x0,0x0,0xfa,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,0x4,0x0,0x0,0x6d,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x41,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x97,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xea,0x1,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,
+0x4,0x0,0x0,0x3e,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xaa,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,
+0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb2,0x3,0x0,0x0,0x7,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x4,0x0,0x0,0x40,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x93,0x3,0x0,0x0,0xbe,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xcb,0x1,0x0,0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xcc,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x1,0x0,
+0x0,0xd4,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x4,0x0,0x0,0x82,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfe,0x3,0x0,0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8d,0x1,
+0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0x86,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0x9d,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xea,0x1,0x0,0x0,0xbe,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,
+0x4,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x40,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,
+0xa,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x3,0x0,0x0,0x71,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0xc3,0x1,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0x7f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x59,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xda,0x2,0x0,0x0,0x64,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x60,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x85,0x3,0x0,
+0x0,0xb5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x8,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x4,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xda,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x4,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0xa7,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x86,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf8,0x3,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x92,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,
+0x0,0x0,0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,
+0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0xcd,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x4,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x1,0x0,0x0,0xd4,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x4,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xcf,0x4,0x0,0x0,0xbe,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xea,0x1,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb8,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,
+0x1,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb9,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x4,0x0,0x0,
+0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x4,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0xc7,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x4,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x4,0x0,0x0,0xe1,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xda,0x2,0x0,0x0,0x33,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe0,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf2,0x1,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe3,0x4,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x4,0x0,
+0x0,0xca,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x4,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0x5,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x5,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x4,0x0,0x0,0x11,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0xa0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x7,0x4,0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x19,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,
+0x0,0x0,0xf7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,
+0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0xc2,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x5,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x4,0x0,0x0,0xbd,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x5,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xec,0x1,0x0,0x0,0x82,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe5,0x3,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,
+0x4,0x0,0x0,0xca,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5b,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x1,0x0,0x0,
+0x82,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x86,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x3,0x0,0x0,0x9,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0xbe,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc,0x2,0x0,0x0,0x63,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x91,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb7,0x2,0x0,
+0x0,0xbe,0x4,0x0,0x0,0xc2,0x1,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x87,0x3,0x0,0x0,0xa2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa1,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,
+0x4,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,
+0x75,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x5,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0xd0,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x5,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xed,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x9f,0x2,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf1,0x5,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,
+0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x5,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0xa0,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x2,0x0,0x0,0xf3,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x85,0x3,0x0,0x0,0x84,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x12,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf9,0x3,0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x20,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,
+0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,0x2,0x0,0x0,0x64,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x4a,0x4,0x0,0x0,
+0xd9,0x3,0x0,0x0,0x86,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x36,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,
+0x0,0x82,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0x7c,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd0,0x2,0x0,0x0,0x76,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x45,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc,0x4,0x0,0x0,0xf7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4b,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x1,
+0x0,0x0,0xc3,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0x99,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0xf9,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x75,0x2,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x7f,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x64,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,
+0x2,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x69,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,
+0x62,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x4,0x0,0x0,0xa0,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x2,0x0,0x0,0xb3,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0xf9,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x85,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf2,0x1,0x0,0x0,0x86,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x87,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,0x0,
+0x0,0x96,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x2,0x0,0x0,0xe5,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0x86,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x6,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa2,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf,0x4,0x0,0x0,0xce,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa9,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,
+0x0,0x0,0xed,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,
+0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xfa,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x6,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xb8,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x6,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0xa7,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x75,0x2,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd2,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,
+0x2,0x0,0x0,0xc1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd5,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3d,0x2,0x0,0x0,
+0x65,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x6,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x4,0x0,0x0,0xbd,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x6,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x7a,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xee,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xea,0x1,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xef,0x6,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,
+0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x6,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0x82,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x6,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0x11,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0xf0,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x6f,0x2,0x0,0x0,0xb3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2f,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x4,
+0x0,0x0,0xa0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xff,0x3,0x0,0x0,0x75,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x4,0x0,0x0,0x1c,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0x80,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd7,0x1,0x0,0x0,0xc7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x40,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x87,
+0x3,0x0,0x0,0x11,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x41,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,
+0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0xcd,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x2,0x0,0x0,0xf3,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x61,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x2d,0x2,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x66,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x1,0x0,
+0x0,0x7c,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x88,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0xca,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x89,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd5,0x3,0x0,0x0,0x5,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x90,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x85,0x3,
+0x0,0x0,0x6,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,
+0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x94,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x7,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xa0,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x7,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xf7,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb7,0x2,0x0,0x0,0xa0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9f,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,
+0x1,0x0,0x0,0xd3,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa6,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x2,0x0,0x0,
+0x76,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0xf0,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x7,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x4,0x0,0x0,0xa9,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x76,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd2,0x7,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,
+0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x7,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x3,0x0,0x0,0x1c,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0x7,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0x74,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x7,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xfa,0x3,0x0,0x0,0xbd,0x3,0x0,0x0,0xec,0x1,0x0,0x0,
+0xed,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x7,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0xca,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x83,0x1,0x0,0x0,0xc1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x2d,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x7f,0x3,0x0,0x0,0xce,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3d,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x87,0x3,0x0,
+0x0,0xfa,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x8,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0x9f,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x8,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x2,0x0,0x0,0xa5,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xcc,0x4,0x0,0x0,0xad,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x5b,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x87,0x3,0x0,0x0,0x79,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x63,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,
+0x0,0x0,0x75,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,
+0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x4,0x0,0x0,0x8,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x8,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0xec,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x8,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd3,0x2,0x0,0x0,0xe9,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe3,0x1,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x72,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,
+0x3,0x0,0x0,0x1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x77,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,
+0x80,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x8,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5a,0x3,0x0,0x0,0x60,0x3,0x0,
+0x0,0xd,0x2,0x0,0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7d,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x4,
+0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,
+0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,0x88,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x8,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x1,0x0,0x0,0x80,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x8,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xda,0x2,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xaf,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfb,
+0x3,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb5,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,
+0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x8,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0xe1,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x8,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0xa9,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xcf,0x1,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd0,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x76,0x4,0x0,0x0,0xe1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd3,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa5,0x3,0x0,
+0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x8,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,0x2,0x0,0x0,0xec,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x8,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,0xe4,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x8,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xea,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4b,0x3,0x0,0x0,0xd6,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xef,0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,
+0x0,0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,
+0x8,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,0xee,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x9,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0x7e,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x9,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0xb3,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xaf,0x1,0x0,0x0,0xb1,0x1,0x0,0x0,0xfb,0x3,0x0,0x0,0xf0,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x9,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xb9,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x9,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x53,0x3,0x0,0x0,0x9,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2b,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe0,0x3,0x0,0x0,0x97,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3d,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x3,
+0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,
+0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x74,0x2,0x0,0x0,0xac,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x9,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xc1,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x9,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0x80,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x6f,0x2,0x0,0x0,0xb5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x7b,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,
+0x4,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7f,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x4,0x0,0x0,
+0x82,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0x7f,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x3,0x0,0x0,0x65,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0x40,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa5,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc2,0x1,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xaf,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6e,0x2,0x0,
+0x0,0x12,0x4,0x0,0x0,0xff,0x3,0x0,0x0,0xdb,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x17,0x4,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,
+0x1,0x0,0x0,0x1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbf,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd0,0x2,0x0,0x0,
+0xa5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x9,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0xd,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x9,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd5,0x3,0x0,0x0,0xda,0x1,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xfa,0x3,0x0,0x0,0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf8,0x9,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xdd,0x1,0x0,0x0,0xe8,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,
+0x0,0x1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0xa,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x3,0x0,0x0,0xf9,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd,0xa,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,0xac,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x19,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa8,0x2,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1c,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd5,0x3,
+0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x3,0x0,0x0,0x4a,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcc,0x4,0x0,0x0,0xf0,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2f,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x44,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x9f,0x2,0x0,0x0,0xa6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x50,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,
+0x1,0x0,0x0,0x6,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x51,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe6,0x1,0x0,0x0,
+0x80,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xa,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x1,0x0,0x0,0xf7,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0xa,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xf9,0x1,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x7b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5a,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x93,0x3,0x0,0x0,0xa8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5f,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,
+0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0xa,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0xef,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xa,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,0xb3,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0xa,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x73,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x18,0x4,0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x75,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x1,
+0x0,0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xec,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe0,0x3,0x0,0x0,0x75,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbe,0x1,0x0,0x0,0x7b,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x4c,0x3,0x0,0x0,0x97,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa9,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,
+0x2,0x0,0x0,0xb3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xba,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xf,0x4,0x0,0x0,
+0xa0,0x3,0x0,0x0,0xb2,0x3,0x0,0x0,0xda,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xbe,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf,0x4,0x0,0x0,0xad,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc0,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,
+0x0,0x0,0x80,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,
+0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0x64,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xa,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0xc0,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0xa,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0xf9,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd2,0x3,0x0,0x0,0xc7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdd,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,
+0x3,0x0,0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe6,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,
+0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0xa,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xa9,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xa,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf,0x4,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfe,0xa,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe6,0x1,0x0,0x0,0xa6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,
+0x0,0xa9,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x1,0x0,0x0,0x7e,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0xb,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x3,0x0,0x0,0x71,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0xb,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7e,0x1,0x0,0x0,0x64,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x23,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xbb,0x4,0x0,0x0,0x9b,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x27,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,
+0x0,0x0,0xe9,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,
+0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,0x6d,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0xb,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x4,0x0,0x0,0x97,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xb,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe6,0x1,0x0,0x0,0xec,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x65,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb8,0x1,0x0,0x0,0xb5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x68,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,
+0x2,0x0,0x0,0xf3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x85,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,
+0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0xb,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0xd0,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0xb,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x1,0x0,0x0,0x4d,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa4,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf,0x4,0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xaf,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x4,0x0,
+0x0,0x75,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x87,0x3,0x0,0x0,0x75,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xb,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0x6d,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0xb,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xc7,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc8,0x3,0x0,0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcf,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb7,0x2,
+0x0,0x0,0xe9,0x3,0x0,0x0,0xe5,0x3,0x0,0x0,0x6,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd4,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x84,0x2,0x0,0x0,0xe1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe2,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xfe,0x3,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe3,0xb,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,0x0,
+0x0,0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0xb,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0xf5,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0xb,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xfa,0x0,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xb,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xf3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa5,0x3,0x0,0x0,0x7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x3,
+0x0,0x0,0x0,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x2,0x0,0x0,0xf3,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0xc3,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x57,0x4,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x14,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xda,0x2,0x0,0x0,0xfa,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x18,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,
+0x3,0x0,0x0,0xf0,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2e,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x2,0x0,0x0,
+0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,0x0,0xf9,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x3,0x0,0x0,0x1,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd0,0x2,0x0,0x0,0x76,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x47,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xde,0x1,0x0,0x0,0xa5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x49,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,
+0x0,0x82,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xc,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x3,0x0,0x0,0xca,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0xc,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xa7,0x1,0x0,0x0,0xbb,
+0x4,0x0,0x0,0xa,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x62,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x4,0x0,0x0,
+0x1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x51,0x3,0x0,0x0,0x6d,0x4,0x0,
+0x0,0xff,0x3,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6d,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x4,
+0x0,0x0,0xa0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,
+0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0xb1,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0xc,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x3,0x0,0x0,0xd,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0xc,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x74,0x2,0x0,0x0,0x8d,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd9,0x3,0x0,0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x90,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,
+0x3,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x94,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,
+0xab,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0xc,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x12,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa9,0xc,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x3,0x0,0x0,0x11,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb1,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xa4,0x2,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb6,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x4,0x0,
+0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0xc,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0x84,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,0xc,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x4,0x0,0x0,0xfd,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0xc,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0xc9,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xea,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc8,0x3,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xec,0xc,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x3,
+0x0,0x0,0xad,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,
+0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0xd6,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0xd,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xfa,0x0,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0xd,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x2,0x4,0x0,0x0,0x97,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x7e,0x1,0x0,0x0,0xa,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x31,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,
+0x1,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x37,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,
+0xa7,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0xd,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0xd,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x3,0x0,0x0,0x78,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0x76,0x2,0x0,0x0,0xfb,0x3,0x0,0x0,0x86,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xd,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x94,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0xd,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0x6b,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x60,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe5,0x3,0x0,0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x61,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,
+0x2,0x0,0x0,0x11,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x65,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,
+0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0xd,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x2,0x0,0x0,0xa8,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0xd,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8f,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xcb,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x91,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,
+0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x97,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x3,0x0,0x0,0x8,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0xa6,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,0xd,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x2d,0x2,0x0,0x0,0x61,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa0,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xbe,0x1,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa1,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,
+0x0,0x0,0xf9,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,
+0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x80,0x3,0x0,0x0,0x80,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0xd,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0x7a,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0xd,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,0x75,0x3,0x0,0x0,0xf2,0x1,0x0,
+0x0,0xcb,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0xd,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x3,0x0,0x0,0xe5,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0xd,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x1,0x0,0x0,0xd0,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0xd,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xe1,0x3,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe2,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x6e,0x2,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe3,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb8,0x1,
+0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,
+0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x2,0x0,0x0,0x9f,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0xd,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8d,0x1,0x0,0x0,0x99,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0xd,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0x8d,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x53,0x3,0x0,0x0,0x86,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xfd,0xd,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,
+0x4,0x0,0x0,0x65,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,
+0x1c,0x2,0x0,0x0,0xf8,0x3,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x19,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd8,0x3,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1e,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x1,
+0x0,0x0,0x63,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0xa8,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x27,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0x1c,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0x86,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb4,0x1,0x0,0x0,0xb3,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x38,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,
+0x3,0x0,0x0,0x9,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x3c,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,
+0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0xe,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0xe5,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0xe,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x3,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe1,0x3,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x59,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x43,0x4,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5c,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,
+0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x3,0x0,0x0,0x7c,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x92,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x53,0x3,0x0,0x0,0xb3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9c,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x3,
+0x0,0x0,0x2f,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,
+0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0x7,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0xe,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xb6,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0xe,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0x74,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc8,0x1,0x0,0x0,0x64,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbd,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,
+0x3,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbe,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x74,0x2,0x0,0x0,
+0xcb,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xe,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0x16,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0xe,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xda,0x2,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd9,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x43,0x4,0x0,0x0,0x75,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe5,0xe,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x4,0x0,
+0x0,0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0xe,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0xd,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0xe,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0xf2,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0xe,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x34,0x3,0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb7,0x2,0x0,0x0,0xbe,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x84,0x2,
+0x0,0x0,0x75,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0xbe,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0xf,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf9,0x3,0x0,0x0,0x31,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0xf,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc,0x4,0x0,0x0,0x8f,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb2,0x3,0x0,0x0,0xf5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x47,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,
+0x2,0x0,0x0,0xf5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x48,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,
+0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0xf,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0xff,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xf,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x82,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5d,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xaf,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x64,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,
+0x0,0xd6,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,0xb9,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfb,0x3,0x0,0x0,0x40,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0xf,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0x64,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb6,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x30,0x4,0x0,0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc3,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x2,
+0x0,0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,
+0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x2,0x0,0x0,0x12,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0xf,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x4,0x0,0x0,0x82,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0xf,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x8d,0x3,0x0,0x0,0x3,0x4,0x0,
+0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xf,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x9d,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0xf,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x3,0x0,0x0,0x75,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0xf,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x83,0x2,0x0,0x0,0xc7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xfb,0xf,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4f,0x2,0x0,0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,
+0x0,0x0,0xa5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,
+0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0x71,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x10,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x3,0x0,0x0,0xf7,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0x10,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x46,0x2,0x0,0x0,0x60,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x32,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,
+0x4,0x0,0x0,0x71,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x35,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x3,0x0,0x0,
+0xe9,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x10,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0xdf,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x10,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc,0x4,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x54,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x30,0x4,0x0,0x0,0x1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x58,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x1,0x0,
+0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x10,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x6b,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x10,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x3,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x10,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0x12,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x8f,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x85,0x3,0x0,0x0,0xee,0x1,0x0,0x0,0x57,0x4,0x0,0x0,0x3e,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x10,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x1,0x0,0x0,0x4d,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0xfa,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa8,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xfc,0x2,0x0,0x0,0xf7,0x3,0x0,0x0,0xcb,0x1,0x0,0x0,0x1,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x10,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd8,0x3,0x0,0x0,0xcd,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xed,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,
+0x4,0x0,0x0,0x7c,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf0,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,
+0xf9,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x10,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0xce,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x10,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x10,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xcb,0x1,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x23,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa7,0x4,0x0,
+0x0,0xf0,0x3,0x0,0x0,0xda,0x2,0x0,0x0,0xa2,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf8,0x3,0x0,0x0,0xa7,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x3f,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xda,
+0x3,0x0,0x0,0x16,0x4,0x0,0x0,0xff,0x3,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x11,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd5,0x3,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x49,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf,0x4,0x0,0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4b,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x2,
+0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,
+0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0x8d,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x11,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,0x4,0x0,0x0,0x6d,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x11,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0x84,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5a,0x3,0x0,0x0,0xfa,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x85,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,
+0x4,0x0,0x0,0x76,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x8e,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,
+0x91,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x11,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xd4,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x91,0x11,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0x8d,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9e,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x7,0x4,0x0,0x0,0x50,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa2,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,0x4,0x0,
+0x0,0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x11,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0xb3,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,0x11,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6f,0x3,0x0,0x0,0x1,0x2,0x0,0x0,0xb4,
+0x1,0x0,0x0,0xd1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc3,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x4,0x0,0x0,
+0xa2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x11,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xfa,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x11,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x4,0x0,0x0,0xad,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x68,0x4,0x0,0x0,0x97,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe9,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x4c,0x3,0x0,0x0,0x7d,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xef,0x11,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,
+0x0,0x11,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x11,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0x82,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x11,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x3,0x0,0x0,0xd,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x11,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf6,0x3,0x0,0x0,0x58,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xcf,0x1,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x1,
+0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,
+0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0x82,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x12,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0x82,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x12,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0xc9,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa8,0x1,0x0,0x0,0x71,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x32,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,
+0x4,0x0,0x0,0x13,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x34,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,
+0x84,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x12,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xff,0x3,0x0,0x0,0xfa,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x12,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xef,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x62,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe1,0x3,0x0,0x0,0xb3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6e,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x2,0x0,
+0x0,0x86,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x71,0x12,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0xcd,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x12,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x2,0x0,0x0,0xfa,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x12,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0x62,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9f,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc,0x2,0x0,0x0,0xd6,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xac,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,
+0x0,0x0,0xfa,0x0,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,
+0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,0x2,0x0,0x0,0xe5,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdc,0x12,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0xd,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x12,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x34,0x3,0x0,0x0,0x65,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xda,0x2,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf6,0x12,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,
+0x2,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x6,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x1,0x0,0x0,
+0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x13,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x62,0x3,0x0,0x0,0x99,0x1,0x0,
+0x0,0xe7,0x3,0x0,0x0,0x5e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,
+0x0,0x0,0x9d,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,
+0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x4,0x0,0x0,0x82,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x6,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x7f,0x2,0x0,0x0,0x75,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x51,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,
+0x1,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x56,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,0x0,
+0x8f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x13,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0xce,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x13,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x3,0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x46,0x2,0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6b,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x51,0x3,0x0,0x0,0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7f,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,
+0x0,0x50,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0x13,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0x7a,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x13,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcc,0x4,0x0,0x0,0xa6,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x13,0x0,0x0,0xff,0xff,0xff,0xff,
+0x3,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x8,0x4,0x0,0x0,0x81,0x3,0x0,0x0,
+0x12,0x4,0x0,0x0,0x5a,0x3,0x0,0x0,0xfa,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x93,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xda,0x2,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xb0,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,
+0x0,0x0,0xf3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,
+0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0x44,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x13,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x2,0x0,0x0,0xf7,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x13,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xa8,0x2,0x0,0x0,0x65,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb4,0x1,0x0,0x0,0x74,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc4,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,
+0x1,0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc6,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,
+0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x13,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x4,0x0,0x0,0xe1,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x13,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0xc9,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf2,0x1,0x0,0x0,0x7c,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfa,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x9e,0x2,0x0,0x0,0x66,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfb,0x13,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd0,0x2,0x0,
+0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x13,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0x9a,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x14,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x3,0x0,0x0,0x7d,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe3,0x2,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x2,
+0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,
+0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0xd6,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x14,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x3,0x0,0x0,0xe9,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x25,0x14,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xe1,0x1,0x0,0x0,0x31,0x2,0x0,0x0,0xfe,0x3,0x0,
+0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x14,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x4,0x0,0x0,0x97,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x14,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x34,0x3,0x0,0x0,0x66,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0x6,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x48,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x89,0x3,0x0,0x0,0x0,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4d,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc,0x4,
+0x0,0x0,0x75,0x4,0x0,0x0,0x8e,0x3,0x0,0x0,0xe8,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x3,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x60,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x7,0x4,0x0,0x0,0x82,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x62,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x4,0x0,
+0x0,0x40,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x14,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x8,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x14,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0xe1,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x14,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0xe4,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9b,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x6e,0x2,0x0,0x0,0x63,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa8,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,
+0x0,0x0,0xc1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,
+0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0xcd,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x14,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0xd6,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x14,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x8e,0x3,0x0,0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xce,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,
+0x2,0x0,0x0,0xdf,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd6,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,
+0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x14,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x4,0x0,0x0,0xe,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,0x14,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0x9d,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe9,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0xfb,0x3,0x0,0x0,0xfd,0x3,0x0,0x0,0x30,0x4,0x0,0x0,0xe4,0x2,0x0,0x0,
+0xd2,0x3,0x0,0x0,0xfa,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf7,0x14,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,
+0x0,0xa0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x15,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xa7,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x15,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0x94,0x1,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x15,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xe7,0x3,0x0,0x0,0x1,0x2,0x0,0x0,0xde,0x1,0x0,0x0,
+0x88,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x15,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8d,0x1,0x0,0x0,0xc3,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x15,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x44,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe7,0x3,0x0,0x0,0xd7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x46,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,
+0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x15,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xf7,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x15,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0xc0,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x15,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0xce,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x79,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xd3,0x1,0x0,0x0,0x9f,0x3,0x0,0x0,0x4c,0x3,0x0,0x0,0xce,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x15,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x80,0x1,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x43,0x4,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x9c,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd3,0x1,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa2,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,
+0x0,0xad,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x15,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x2,0x0,0x0,0xc9,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x15,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x4,0x0,0x0,0x16,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x15,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x18,0x4,0x0,0x0,0x97,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xcf,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x68,0x4,0x0,0x0,0xad,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd6,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x3,
+0x0,0x0,0x11,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,
+0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x75,0x2,0x0,0x0,0xa0,
+0x3,0x0,0x0,0xc8,0x3,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe4,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x82,0x3,0x0,0x0,0xe1,0x2,0x0,0x0,0x80,0x3,0x0,0x0,0xed,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x15,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4b,0x3,0x0,0x0,0x62,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xec,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xde,0x1,0x0,0x0,0xc9,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xef,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,
+0x2,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xfd,0x15,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,
+0x96,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x1,0x0,0x0,0xf3,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0x74,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x87,0x1,0x0,0x0,0xa5,0x1,0x0,0x0,0xf2,0x1,0x0,0x0,0xcb,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x16,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0x9d,0x3,0x0,0x0,
+0x30,0x4,0x0,0x0,0x1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1d,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfa,0x3,0x0,
+0x0,0x40,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x16,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x3,0x0,0x0,0xf0,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x16,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd3,0x2,0x0,0x0,0xd7,0x3,0x0,0x0,0xb2,
+0x3,0x0,0x0,0x75,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2d,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,
+0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0xa5,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0x71,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x9b,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5a,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xd,0x2,0x0,0x0,0xcd,0x4,0x0,0x0,0xde,0x1,0x0,0x0,0x5,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x16,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe3,0x1,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x78,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,
+0x3,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x7c,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x4,0x0,0x0,
+0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x80,0x3,0x0,0x0,0xe5,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0x60,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8d,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xb4,0x1,0x0,0x0,0x80,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x96,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x1,0x0,
+0x0,0x8f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x16,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x4f,0x2,0x0,0x0,0xe9,0x3,
+0x0,0x0,0x7f,0x3,0x0,0x0,0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa0,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,
+0x4,0x0,0x0,0xa0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa4,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x4,0x0,0x0,
+0x97,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0xd,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x1,0x0,0x0,0xf2,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0x7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb5,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x2,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0xfe,0x3,0x0,0x0,0x82,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x16,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x76,0x4,0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xda,0x2,0x0,0x0,0x75,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xcb,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd3,
+0x1,0x0,0x0,0x63,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xcd,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x95,0x2,0x0,0x0,
+0xa5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x16,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x7c,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x16,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xd0,0x2,0x0,0x0,0xf7,0x3,0x0,0x0,0xb7,0x2,
+0x0,0x0,0xf9,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,
+0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x80,0x3,0x0,0x0,0xed,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,0x16,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0xf0,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x16,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd3,0x2,0x0,0x0,0x75,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe8,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x7f,0x3,0x0,0x0,0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf7,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xff,0x16,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x1,0x0,0x0,
+0x76,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x17,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x4,0x0,0x0,0xe8,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x17,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xec,0x1,0x0,0x0,0x1,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc8,0x3,0x0,0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x36,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x7f,0x3,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x37,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,
+0x0,0xec,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x17,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x4,0x0,0x0,0xa9,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x17,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd0,0x2,0x0,0x0,0x80,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x17,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x83,0x2,0x0,0x0,0xcb,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x59,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf8,0x3,0x0,0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x5e,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,0x1,
+0x0,0x0,0xef,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,
+0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x18,0x4,0x0,0x0,0x1,
+0x2,0x0,0x0,0xff,0x3,0x0,0x0,0x11,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x85,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x90,0x3,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8b,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x54,0x2,0x0,
+0x0,0x4d,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x17,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0xb5,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x17,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x91,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x17,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xaa,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xff,0x3,0x0,0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xab,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,
+0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,
+0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x1,0x0,0x0,0x3d,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x17,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xfa,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb8,0x17,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xcf,0x4,0x0,0x0,0x12,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xe1,0x1,0x0,0x0,0x83,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc3,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,
+0x2,0x0,0x0,0x44,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd0,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,
+0xd4,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x17,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0xf9,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x17,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x4,0x0,0x0,0xfd,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0x62,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf7,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x9e,0x2,0x0,0x0,0xc3,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xfd,0x17,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,
+0x0,0xf7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x18,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0xf7,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x18,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x2,0x0,0x0,0x65,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x18,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xff,0x3,0x0,0x0,0x7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3b,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x30,0x4,0x0,0x0,0xd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x3f,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x84,0x2,
+0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,
+0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x3,0x0,0x0,0xd6,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x18,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x63,0x3,0x0,0x0,
+0x43,0x4,0x0,0x0,0xe,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5f,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x1,0x0,
+0x0,0xe4,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x18,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x4,0x0,0x0,0x86,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x18,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,0x2,0x0,0x0,0xa2,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x18,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xec,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x72,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf,0x4,0x0,0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x76,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfc,0x3,
+0x0,0x0,0x5,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,
+0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x99,
+0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x18,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x1,0x0,0x0,0xcd,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x18,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x75,0x2,0x0,0x0,0x62,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb4,0x1,0x0,0x0,0x5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x8b,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xde,
+0x1,0x0,0x0,0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x8c,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x1,0x0,0x0,
+0x12,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8e,0x18,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0x7c,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x18,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0x82,0x4,0x0,0x0,0x54,0x2,
+0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,
+0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xe5,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x18,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x3,0x0,0x0,0xad,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x18,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xaf,0x1,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf2,0x2,0x0,0x0,0x95,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xdf,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,
+0x1,0x0,0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe8,0x18,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,
+0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x18,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0x16,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x18,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0xef,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0x9d,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf8,0x3,0x0,0x0,0x40,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x4,0x0,
+0x0,0xdb,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x19,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0x94,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0x86,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x57,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2e,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4f,0x3,0x0,0x0,0x7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4c,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,
+0x0,0x0,0xe5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0xb5,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x18,0x4,0x0,0x0,0x84,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x19,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xef,0x1,0x0,0x0,0xf9,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5a,0x3,0x0,0x0,0x33,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x69,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,
+0x4,0x0,0x0,0xcd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x6a,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,0x0,
+0xb9,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x19,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xb5,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x19,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd3,0x2,0x0,0x0,0xa8,0x4,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0xf9,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x80,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe5,0x3,0x0,0x0,0xb1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x82,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd2,0x3,0x0,
+0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x19,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x3,0x0,0x0,0x71,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x19,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0xca,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x19,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x7e,0x1,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x94,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xdd,0x1,0x0,0x0,0x1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9b,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x2,
+0x0,0x0,0x62,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0xfd,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,0x40,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,0x19,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,0x0,0x94,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x89,0x3,0x0,0x0,0xf9,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb4,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,
+0x1,0x0,0x0,0xee,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb5,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x11,0x2,0x0,0x0,
+0xf7,0x2,0x0,0x0,0xcb,0x1,0x0,0x0,0xad,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb7,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa4,0x1,0x0,0x0,0xd4,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbd,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xec,0x1,
+0x0,0x0,0xdf,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,
+0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x4,0x0,0x0,0xe1,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x19,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2,0x4,0x0,0x0,0x8,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xca,0x19,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x2d,0x2,0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xeb,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,
+0x1,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf3,0x19,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,0x1,0x0,0x0,
+0xdb,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,0x19,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa7,0x4,0x0,0x0,0x6d,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x19,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x60,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xae,0x4,0x0,0x0,0xfd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x4,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x9c,0x4,0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x7f,0x3,0x0,
+0x0,0xa7,0x1,0x0,0x0,0xcf,0x4,0x0,0x0,0xcb,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xae,0x4,0x0,0x0,0xfd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x17,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,
+0x3,0x0,0x0,0xa2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1a,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x3,0x0,0x0,
+0xc1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x1a,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0xc7,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x1a,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x8c,0x1,0x0,0x0,0x91,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x87,0x2,0x0,0x0,0xf7,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x38,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x89,0x3,0x0,0x0,0xfb,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3b,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,
+0x0,0x64,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x1a,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x2,0x0,0x0,0x33,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x1a,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0x31,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0xe9,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x68,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x68,0x4,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x70,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,
+0x0,0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,
+0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,0x5,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x1a,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0xe5,0x2,0x0,0x0,
+0xd6,0x3,0x0,0x0,0xe1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x79,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xfb,0x3,0x0,
+0x0,0x8,0x4,0x0,0x0,0xda,0x2,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc8,0x3,0x0,0x0,0x74,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa0,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,
+0x1,0x0,0x0,0x9d,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xaa,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x4,0x0,0x0,
+0x75,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x1a,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9f,0x2,0x0,0x0,0xa6,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x1a,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,0x0,0xa5,0x1,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0xc1,0x3,0x0,0x0,0xda,0x2,0x0,0x0,0xf7,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x1a,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0xdb,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x1a,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xae,0x4,0x0,0x0,0x75,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xbb,0x4,0x0,0x0,0x12,0x4,0x0,0x0,0xe5,0x3,0x0,0x0,0xfa,0x0,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x1a,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0x0,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xff,0x3,0x0,0x0,0xf3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xfc,0x1a,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x54,0x2,0x0,0x0,0xc1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xef,0x1,
+0x0,0x0,0xa5,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xd,0x2,0x0,0x0,0xd,
+0x4,0x0,0x0,0xf,0x4,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1d,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x54,0x2,0x0,0x0,0x9f,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x21,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x2,0x0,
+0x0,0xf3,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x1b,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0xa5,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x1b,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x3,0x0,0x0,0x86,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xe6,0x1,0x0,0x0,0x12,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x52,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb7,0x2,0x0,0x0,0x65,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x54,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,
+0x0,0x0,0x7b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0xf7,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x1b,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x3,0x0,0x0,0x32,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x1b,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0xd,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x77,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc8,0x3,0x0,0x0,0x3e,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x78,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,
+0x3,0x0,0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x79,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,
+0x66,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x1b,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x1,0x0,0x0,0xe8,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0x1b,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x54,0x2,0x0,0x0,0x99,0x1,0x0,0x0,0xd0,0x2,
+0x0,0x0,0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0xce,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x1b,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0xc7,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x1b,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xf8,0x3,0x0,0x0,0x86,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9f,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xe3,0x1,0x0,0x0,0xa2,0x2,0x0,0x0,0x94,0x2,0x0,0x0,0xfd,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa4,0x1b,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,0x4,0x0,0x0,0x15,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdd,0x3,0x0,0x0,0xd7,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa9,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa4,0x1,0x0,0x0,0xef,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xaa,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3,0x4,
+0x0,0x0,0x3e,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0xf0,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x1b,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0x16,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x1b,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xea,0x1,0x0,0x0,0xc9,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1,0x4,0x0,0x0,0x71,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xe1,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,
+0x4,0x0,0x0,0x65,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xe4,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,
+0xf9,0x2,0x0,0x0,0x4c,0x3,0x0,0x0,0xca,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xef,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x9e,0x2,0x0,0x0,0xc3,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf0,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x3,
+0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf7,
+0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2,0x4,0x0,0x0,0x75,
+0x3,0x0,0x0,0xb0,0x1,0x0,0x0,0xfa,0x0,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfc,0x1b,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xde,0x1,0x0,0x0,0xb9,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x15,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd9,0x3,0x0,
+0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x4,0x0,0x0,0x50,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x1c,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0xf2,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2b,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x31,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x7f,0x3,0x0,0x0,0x99,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x47,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x2,
+0x0,0x0,0x12,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,
+0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x1,0x0,0x0,0x12,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0x1c,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x8d,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x1c,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5e,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xd,0x2,0x0,0x0,0xd,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x71,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x80,
+0x3,0x0,0x0,0xed,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x74,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,
+0xa2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x1c,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x4,0x0,0x0,0xf9,0x2,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x1c,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4f,0x3,0x0,0x0,0x7,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x8c,0x1,0x0,0x0,0x8,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x92,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x53,0x4,0x0,0x0,0x16,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x98,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x3,0x0,
+0x0,0xe8,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x1c,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xed,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x1c,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x1,0x0,0x0,0x64,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb7,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb7,0x2,0x0,0x0,0x12,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xda,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc8,0x3,0x0,0x0,0xc3,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf0,0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x4,
+0x0,0x0,0xc9,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,
+0x1c,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xe5,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x1d,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0x16,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x1d,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xa2,0x1,0x0,0x0,0xb5,0x1,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xbe,0x1,0x0,0x0,0x33,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1e,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,
+0x1,0x0,0x0,0xc1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1f,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x3,0x0,0x0,
+0x44,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x1d,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0xc0,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x1d,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xfb,0x3,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd6,0x3,0x0,0x0,0xe1,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3c,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xfe,0x3,0x0,0x0,0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3f,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7f,0x3,0x0,
+0x0,0x8e,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa4,0x1,0x0,0x0,0xf9,0x1,
+0x0,0x0,0x6e,0x2,0x0,0x0,0xbe,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4c,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,
+0x1,0x0,0x0,0xe2,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x62,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x2,0x0,0x0,
+0xa5,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x1d,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x2,0x0,0x0,0xc9,0x4,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x1d,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x2,0x0,0x0,0xc7,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf,0x4,0x0,0x0,0xf0,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x82,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf8,0x3,0x0,0x0,0xac,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x87,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x1,0x0,
+0x0,0x78,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x4,0x0,0x0,0x16,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x1d,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0x7,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x46,0x2,0x0,0x0,0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb8,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfc,0x2,0x0,0x0,0xec,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xba,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x4,
+0x0,0x0,0x86,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,
+0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,0xc1,
+0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,0x1d,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x82,0x3,0x0,0x0,0x91,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x1d,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0x62,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc2,0x1,0x0,0x0,0x1a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xca,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,
+0x2,0x0,0x0,0xa2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xce,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x3,0x0,0x0,
+0xfa,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x1d,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0xa9,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x1d,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xf2,0x1,0x0,0x0,0xe4,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xc2,0x1,0x0,0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xea,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xcf,0x4,0x0,0x0,0x9a,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf0,0x1d,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,
+0x0,0xd4,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x1d,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x3,0x0,0x0,0x9f,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x3,0x0,0x0,0x31,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x51,0x3,0x0,0x0,0x97,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x10,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x43,0x4,0x0,0x0,0xbd,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x16,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa2,0x1,
+0x0,0x0,0x91,0x2,0x0,0x0,0xa5,0x3,0x0,0x0,0x9f,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf6,0x3,0x0,0x0,0x31,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x26,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x43,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x30,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x4,0x0,
+0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x1e,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x3,0x0,0x0,0xe4,0x2,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb4,0x1,0x0,0x0,0x6,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0xc0,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4d,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xd8,0x3,0x0,0x0,0xc3,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x6b,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x2,
+0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,
+0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0x1a,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x1e,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,0x1,0x0,0x0,0x80,0x1,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x72,0x1e,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0x6,0x4,0x0,0x0,0xe1,0x1,0x0,
+0x0,0x66,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x1e,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1,0x4,0x0,0x0,0x7a,0x3,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x89,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x1,0x0,0x0,0x4d,0x3,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xe0,0x3,0x0,0x0,0x97,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x97,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xfc,0x3,0x0,0x0,0xc9,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x9b,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcc,0x4,
+0x0,0x0,0x86,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9f,
+0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x83,0x1,0x0,0x0,0x82,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x1e,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x2,0x0,0x0,0xcd,0x4,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x1e,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xd9,0x3,0x0,0x0,0x86,0x4,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xb4,0x4,0x0,0x0,0xa9,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x76,
+0x4,0x0,0x0,0x1c,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbf,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,
+0xc2,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc3,0x1e,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9c,0x4,0x0,0x0,0xdb,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x1e,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x1,0x0,0x0,0x33,0x3,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xfe,0x3,0x0,0x0,0xd0,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xdd,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xf,0x4,0x0,0x0,0x82,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xde,0x1e,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8c,0x1,0x0,
+0x0,0x44,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x1e,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x2,0x0,0x0,0xfa,0x1,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x1e,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x4,0x0,0x0,0xb,0x4,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0x8b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xe3,0x1,0x0,0x0,0x7e,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xc,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa2,0x1,
+0x0,0x0,0x60,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,
+0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x3,0x0,0x0,0xad,
+0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x1f,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0xd6,0x2,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x32,0x1f,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x89,0x3,0x0,0x0,0xf9,0x2,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xc,0x2,0x0,0x0,0xca,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x59,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa4,
+0x1,0x0,0x0,0x7b,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5f,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe5,0x3,0x0,0x0,
+0x80,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x1f,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0x7e,0x3,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x67,0x1f,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x4c,0x4,0x0,0x0,0xfb,0x2,0x0,0x0,0x1,0x4b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb8,0x1,0x0,0x0,0x88,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x71,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc2,0x1,0x0,0x0,0xc1,0x1,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x88,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x1,0x0,
+0x0,0xc1,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x1f,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0x4a,0x4,
+0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x1f,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x94,0x2,0x0,0x0,0xcd,0x2,0x0,0x0,0x1,
+0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa5,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0x84,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa7,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x51,0x3,0x0,0x0,0x12,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xab,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xea,0x1,
+0x0,0x0,0x4a,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,
+0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x3,0x0,0x0,0x16,
+0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x1f,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd0,0x2,0x0,0x0,0x60,0x3,0x0,0x0,
+0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x1f,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4c,0x3,0x0,0x0,0x12,0x3,0x0,0x0,0x1,0x4b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x4c,0x4,0x0,0x0,0x40,0x2,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xde,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,
+0x4,0x0,0x0,0x6d,0x4,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xef,0x1f,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x4,0x0,0x0,
+0xb6,0x3,0x0,0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x1f,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xaf,0x1,0x0,0x0,0xc1,0x1,0x0,
+0x0,0x1,0x4b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf5,0x1f,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x81,0x3,0x0,0x0,0xd,0x4,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xe,0x1,0x0,0x0,0x7e,0x0,0x0,0x0,0x22,0x1,0x0,0x0,0xdb,
+0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa9,0x2,0x0,0x0,0x70,0x2,0x0,0x0,
+0x64,0x5,0x0,0x0,0x45,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x11,0x5,0x0,
+0x0,0x65,0x1,0x0,0x0,0xea,0x2,0x0,0x0,0xe7,0x2,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x30,0x5,0x0,0x0,0x89,0x5,0x0,0x0,0x70,0x5,0x0,0x0,0x1d,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x1,0x0,0x0,0xe4,0x0,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x57,0x2,0x0,0x0,0x65,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf8,0x4,0x0,0x0,0x17,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x11,0x5,
+0x0,0x0,0x81,0x2,0x0,0x0,0x1d,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xab,0x2,0x0,0x0,0xb5,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xb5,0x5,0x0,0x0,0xa1,0x1,0x0,0x0,0x24,0x5,0x0,0x0,0x4f,0x5,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x63,0x1,0x0,0x0,0x64,0x1,0x0,0x0,0x5a,0x0,0x0,
+0x0,0x3e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x30,0x3,0x0,0x0,0x25,0x2,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x77,0x5,0x0,0x0,0xaa,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x11,0x3,0x0,0x0,
+0x10,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa9,0x5,0x0,0x0,0x6b,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x5,0x0,0x0,0xe6,0x4,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x36,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x7,0x5,0x0,0x0,0x5a,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x20,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x73,0x5,0x0,
+0x0,0xf5,0x1,0x0,0x0,0x58,0x0,0x0,0x0,0xeb,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x7a,0x1,0x0,0x0,0xa6,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x24,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb,
+0x5,0x0,0x0,0xfa,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x25,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,0x2,0x0,0x0,
+0xf4,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb7,0x3,0x0,0x0,0x52,0x1,0x0,
+0x0,0x57,0x2,0x0,0x0,0x6e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x64,0x5,
+0x0,0x0,0x51,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xff,0x0,0x0,0x0,0xf8,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0x90,0x5,0x0,0x0,
+0x9e,0x5,0x0,0x0,0xbf,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x32,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x1,0x0,
+0x0,0x2a,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0xf8,0x2,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x45,0x1,0x0,0x0,0x30,
+0x5,0x0,0x0,0x48,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x37,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x5,0x0,0x0,
+0xe4,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc6,0x0,0x0,0x0,0x2a,0x2,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x3,0x0,0x0,0x7f,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x46,0x1,0x0,0x0,0xa,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x3f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x14,0x0,0x0,0x0,0x97,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x40,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,
+0x0,0x6b,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6,0x3,0x0,0x0,0x17,0x3,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xaa,0x5,0x0,0x0,0x4,0x5,0x0,0x0,0x55,
+0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x43,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x2,0x0,0x0,
+0x68,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x45,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0x90,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x2,0x0,0x0,0x70,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xc0,0x3,0x0,0x0,0xeb,0x1,0x0,0x0,0xba,0x0,0x0,0x0,0xdc,
+0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x34,0x5,0x0,0x0,0x6f,0x5,0x0,0x0,
+0x47,0x2,0x0,0x0,0x65,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x3,0x0,
+0x0,0x49,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0xf5,0x1,
+0x0,0x0,0x38,0x1,0x0,0x0,0x47,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x4e,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,
+0x5,0x0,0x0,0x1c,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x4f,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x3,0x0,0x0,
+0x3,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0x3,0x0,0x0,
+0x0,0xfd,0x4,0x0,0x0,0xa6,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x51,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x60,0x2,
+0x0,0x0,0xc8,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe0,0x2,0x0,0x0,0x86,
+0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb2,0x2,0x0,0x0,0xb9,0x5,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5e,0x1,0x0,0x0,0x21,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa8,0x5,0x0,0x0,0x20,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb6,
+0x5,0x0,0x0,0xcc,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x5c,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x1,0x0,0x0,
+0xd8,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0xb2,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x2,0x0,0x0,0x8e,0x2,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x63,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x17,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3c,0x5,0x0,0x0,0x52,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x66,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3c,0x3,0x0,
+0x0,0x1d,0x2,0x0,0x0,0x2c,0x0,0x0,0x0,0x56,0x2,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x9d,0x2,0x0,0x0,0x52,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x6a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb7,
+0x5,0x0,0x0,0xe7,0x0,0x0,0x0,0x2c,0x2,0x0,0x0,0x36,0x2,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x1c,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x71,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xc,0x5,0x0,0x0,0x59,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x72,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe,0x1,
+0x0,0x0,0x8d,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x77,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x2,0x0,0x0,0x18,
+0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x2,0x0,0x0,0x1e,0x1,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x15,0x3,0x0,0x0,0x4a,0x3,0x0,0x0,0x2e,0x2,0x0,
+0x0,0x37,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xd8,0x4,0x0,0x0,0x30,0x0,
+0x0,0x0,0xb0,0x5,0x0,0x0,0xe0,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0xe6,0x4,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x41,0x2,0x0,0x0,0x83,0x0,0x0,0x0,0x79,
+0x5,0x0,0x0,0xf3,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x81,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6c,0x2,0x0,0x0,
+0xed,0x0,0x0,0x0,0x4,0x5,0x0,0x0,0x6f,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x82,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xde,0x0,0x0,0x0,0xd7,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x86,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x0,
+0x0,0x0,0x18,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x87,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x1,0x0,0x0,0x8d,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x88,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x61,0x5,0x0,0x0,0x36,0x5,0x0,0x0,
+0xb0,0x5,0x0,0x0,0x55,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x89,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8c,0x0,0x0,
+0x0,0xb,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8e,0x5,0x0,0x0,0x8f,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x51,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x9e,0x5,0x0,0x0,0xf1,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x8b,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x93,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc3,0x5,
+0x0,0x0,0xe0,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3c,0x3,0x0,0x0,0x98,
+0x1,0x0,0x0,0xfd,0x2,0x0,0x0,0x4e,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x97,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe9,0x2,0x0,0x0,0x33,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x98,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xb0,0x5,0x0,
+0x0,0x20,0x2,0x0,0x0,0x7b,0x2,0x0,0x0,0xe9,0x0,0x0,0x0,0xc9,0x3,0x0,
+0x0,0xeb,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xed,0x4,0x0,0x0,0xe8,0x4,
+0x0,0x0,0xae,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x9a,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,
+0x2,0x0,0x0,0x9,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x9b,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9,0x3,0x0,0x0,
+0x4b,0x2,0x0,0x0,0x5,0x1,0x0,0x0,0xd7,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa2,0x5,0x0,0x0,0x15,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf9,0x4,
+0x0,0x0,0x13,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa1,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0xaf,
+0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x91,0x0,0x0,0x0,0xa1,0x2,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0xb,0x3,0x0,0x0,0xf0,0x4,0x0,
+0x0,0xd7,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x3,0x0,0x0,0x0,0x3,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x21,0x5,0x0,0x0,0x3c,
+0x5,0x0,0x0,0xa3,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xce,0x1,0x0,0x0,
+0x2b,0x3,0x0,0x0,0x10,0x4,0x0,0x0,0xd6,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xad,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x69,0x0,0x0,0x0,0x49,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xae,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x97,0x5,
+0x0,0x0,0x80,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0xf7,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,0x6c,0x2,0x0,0x0,
+0xd5,0x2,0x0,0x0,0x9e,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd8,0x0,0x0,
+0x0,0x24,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x0,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xae,0x0,0x0,0x0,0x43,0x0,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x21,0x2,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x28,0x3,0x0,0x0,0x67,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x70,0x0,0x0,0x0,0x34,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xbe,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb4,0x0,
+0x0,0x0,0xa5,0x0,0x0,0x0,0xbe,0x2,0x0,0x0,0x88,0x2,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf9,0x4,0x0,0x0,0x3f,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x61,0x5,0x0,0x0,0x37,0x5,0x0,0x0,0x9e,0x5,0x0,0x0,0xf3,0x1,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x7b,0x5,0x0,0x0,0x8f,0x5,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xe0,0x1,0x0,0x0,0x21,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x7d,0x1,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc9,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x71,0x1,0x0,0x0,0xd2,0x1,0x0,0x0,0x24,
+0x5,0x0,0x0,0x60,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xca,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x1,0x0,0x0,
+0xaf,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0x40,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x0,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x47,0x2,0x0,0x0,0x67,0x2,0x0,0x0,0x12,0x0,
+0x0,0x0,0x23,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x51,0x2,0x0,0x0,0x53,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x1,0x0,0x0,0x40,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x69,0x5,0x0,0x0,0xf2,0x4,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xa0,0x0,0x0,0x0,0x3a,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x68,
+0x5,0x0,0x0,0x27,0x5,0x0,0x0,0x23,0x3,0x0,0x0,0x7b,0x1,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x42,0x5,0x0,0x0,0x1b,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xd7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x48,0x1,0x0,0x0,0xc2,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xda,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x58,0x0,
+0x0,0x0,0x2f,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xde,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x1c,
+0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2f,0x3,0x0,0x0,0x1,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xfe,0x1,0x0,0x0,0x15,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xea,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xbd,
+0x5,0x0,0x0,0xce,0x0,0x0,0x0,0x48,0x3,0x0,0x0,0x9b,0x1,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x59,0x5,0x0,0x0,0x15,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xee,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb7,0x5,0x0,0x0,0x57,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xef,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcf,0x2,
+0x0,0x0,0xbe,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xc,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,0x2,0x0,0x0,0xfa,0x2,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x93,0x5,0x0,0x0,0xa8,0x5,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x2e,0x1,0x0,0x0,0x29,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf9,
+0x0,0x0,0x0,0x14,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xfa,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x3,0x0,0x0,
+0x5f,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x44,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x63,0x1,0x0,0x0,0x89,0x0,0x0,0x0,0x4,0x1,0x0,0x0,0x13,
+0x5,0x0,0x0,0x15,0x2,0x0,0x0,0xbb,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xb1,0x2,0x0,0x0,0x20,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x2,0x0,
+0x0,0x41,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x59,0x3,0x0,0x0,0x24,0x3,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xef,0x4,0x0,0x0,0xe4,0x4,0x0,0x0,0x2b,
+0x2,0x0,0x0,0x6f,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x0,0x0,0x0,
+0x6d,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x1,0x0,0x0,0xb2,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xb1,0x2,0x0,0x0,0x4b,0x5,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x5,0x1,0x0,0x0,0xfb,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x17,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x53,0x2,0x0,0x0,0x4d,0x1,0x0,0x0,0x1a,0x0,0x0,0x0,0xbe,0x0,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x96,0x2,0x0,0x0,0xb7,0x0,0x0,0x0,0x67,0x5,0x0,
+0x0,0xf7,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe3,0x4,0x0,0x0,0x5c,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2d,0x5,0x0,0x0,0xdf,0x4,0x0,0x0,0xa4,
+0x3,0x0,0x0,0x4e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1d,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x84,0x1,0x0,0x0,
+0xf3,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0x4d,0x5,0x0,
+0x0,0x95,0x5,0x0,0x0,0x19,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x22,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf0,0x1,
+0x0,0x0,0x12,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x4b,0x5,0x0,0x0,0xe2,
+0x4,0x0,0x0,0x24,0x0,0x0,0x0,0x81,0x1,0x0,0x0,0xa1,0x3,0x0,0x0,0x5e,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x51,0x0,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x5e,0x5,0x0,0x0,0x3b,0x5,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x27,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x90,0x2,0x0,0x0,0x16,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,
+0x3,0x0,0x0,0x95,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x28,0x3,0x0,0x0,
+0x12,0x0,0x0,0x0,0xc6,0x2,0x0,0x0,0xc6,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xa9,0x2,0x0,0x0,0x4c,0x2,0x0,0x0,0x87,0x5,0x0,0x0,0x70,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x1,0x0,0x0,0x68,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xae,0x5,0x0,0x0,0xaa,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x39,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x96,0x2,0x0,0x0,0xb7,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x3a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x3,0x0,
+0x0,0x6b,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0xaf,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x56,0x5,0x0,0x0,0x6a,0x3,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0x33,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x41,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3a,0x2,0x0,0x0,0x41,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x46,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x8c,0x0,
+0x0,0x0,0x18,0x1,0x0,0x0,0xd2,0x4,0x0,0x0,0xe5,0x4,0x0,0x0,0x97,0x3,
+0x0,0x0,0x7d,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x0,
+0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x59,0x3,0x0,0x0,0x97,0x3,0x0,0x0,
+0x2b,0x2,0x0,0x0,0x64,0x2,0x0,0x0,0xf4,0x4,0x0,0x0,0xd4,0x4,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4d,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0x94,0x0,0x0,0x0,0xe7,0x4,0x0,
+0x0,0x57,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf6,0x4,0x0,0x0,0x35,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x51,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0x3b,0x0,0x0,0x0,0xe3,
+0x0,0x0,0x0,0x81,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x52,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xce,0x1,0x0,0x0,
+0x21,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x9d,0x0,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x5,0x0,0x0,0x3,0x5,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xe1,0x0,0x0,0x0,0x17,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x61,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x10,0x1,0x0,0x0,0x3c,0x0,0x0,0x0,0xf6,0x4,0x0,0x0,0x6a,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0xb1,0x0,0x0,0x0,0xc0,0x5,0x0,0x0,0x62,0x0,0x0,
+0x0,0x65,0x2,0x0,0x0,0xa7,0x3,0x0,0x0,0x3d,0x0,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,
+0x0,0x0,0x8a,0x0,0x0,0x0,0x9f,0x0,0x0,0x0,0x2d,0x5,0x0,0x0,0xfb,0x4,
+0x0,0x0,0x2b,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x66,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xc0,
+0x5,0x0,0x0,0x20,0x0,0x0,0x0,0x8a,0x0,0x0,0x0,0x2f,0x1,0x0,0x0,0x5b,
+0x1,0x0,0x0,0x28,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x6a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc4,0x1,0x0,0x0,
+0x29,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6c,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x2,0x0,0x0,0x66,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc8,0x2,0x0,0x0,0x6a,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x79,0x5,0x0,0x0,0x98,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x6f,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x6c,0x2,0x0,0x0,0x6d,0x2,0x0,0x0,0x7d,0x1,0x0,0x0,0x2b,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0x69,0x2,0x0,0x0,0x96,0x3,0x0,
+0x0,0x7f,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x73,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x21,0x2,0x0,0x0,0xc5,0x3,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x1,0x0,0x0,0x19,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x75,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xc5,0x3,0x0,0x0,0xb9,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x76,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x28,0x2,0x0,0x0,0x35,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x7a,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x52,0x0,
+0x0,0x0,0x36,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x81,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0xf1,
+0x0,0x0,0x0,0x57,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x82,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x7,0x1,0x0,0x0,0x9e,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x83,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7e,0x5,0x0,
+0x0,0xa0,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x85,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5d,0x0,0x0,0x0,0x7c,0x0,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe0,0x2,0x0,0x0,0x8e,0x2,0x0,0x0,0x64,
+0x5,0x0,0x0,0x2,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x8e,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x2,0x0,0x0,
+0x8e,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x72,0x0,0x0,0x0,0x10,0x0,0x0,
+0x0,0xb6,0x1,0x0,0x0,0x9,0x1,0x0,0x0,0x17,0x2,0x0,0x0,0x4c,0x2,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xef,0x4,0x0,0x0,0x66,0x2,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xe6,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1f,0x3,0x0,0x0,0x19,
+0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x5,0x0,0x0,0x45,0x0,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xed,0x2,0x0,0x0,0xed,0x0,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x6,0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x97,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x79,
+0x0,0x0,0x0,0x2,0x1,0x0,0x0,0x11,0x5,0x0,0x0,0x60,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0xdb,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,
+0x77,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x38,0x1,0x0,0x0,0xbc,0x3,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x3,0x0,0x0,0xfd,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x2a,0x1,0x0,0x0,0x64,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa1,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xa6,0x5,0x0,0x0,0xa0,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xa2,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x2,0x0,
+0x0,0x5b,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5,0x1,0x0,0x0,0x6c,0x0,
+0x0,0x0,0xcc,0x2,0x0,0x0,0xc4,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa7,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x8b,
+0x0,0x0,0x0,0x61,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xa9,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x16,0x3,0x0,0x0,
+0x97,0x3,0x0,0x0,0x9f,0x5,0x0,0x0,0x92,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xaa,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x33,0x0,0x0,0x0,0xa7,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xac,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,0x5,
+0x0,0x0,0x3,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xad,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x3,0x0,0x0,0x18,
+0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x1,0x0,0x0,0x44,0x1,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x1e,0x5,0x0,0x0,0x54,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xb2,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x81,
+0x0,0x0,0x0,0x7b,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xb4,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x78,0x3,0x0,0x0,
+0xbb,0x0,0x0,0x0,0x7a,0x5,0x0,0x0,0xbd,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xde,0x0,0x0,0x0,0x21,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xba,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x1,
+0x0,0x0,0x28,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb,
+0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x81,
+0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x17,0x2,0x0,0x0,0xb0,0x0,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbf,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0x45,0x0,0x0,0x0,0x26,0x1,0x0,
+0x0,0x9f,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,0x46,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc6,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0x3b,0x1,0x0,0x0,0x34,
+0x5,0x0,0x0,0x51,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xc7,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x98,0x3,0x0,0x0,
+0x2a,0x3,0x0,0x0,0xe4,0x1,0x0,0x0,0x97,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xca,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x9e,0x5,0x0,0x0,0xea,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xcb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x85,0x1,
+0x0,0x0,0xb6,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0x90,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcf,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xd1,0x0,0x0,0x0,0xc3,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd1,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x34,0x2,0x0,
+0x0,0x6b,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x1e,0x0,
+0x0,0x0,0x61,0x2,0x0,0x0,0xf7,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xd3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe4,
+0x1,0x0,0x0,0xcc,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xd5,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x3,0x0,0x0,
+0x9c,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x1,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0xfd,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x1,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0x59,0x5,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x81,0x2,0x0,0x0,0x42,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xde,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x26,0x5,0x0,0x0,0x33,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe0,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x26,0x5,0x0,
+0x0,0x4e,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe5,0x1,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x0,0x0,0x0,0x53,0x0,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe7,0x1,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x3,0x0,0x0,0x24,0x2,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x1,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0x62,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xeb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xdf,0x4,0x0,0x0,0x61,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xec,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x58,0x0,
+0x0,0x0,0x95,0x1,0x0,0x0,0x1a,0x1,0x0,0x0,0x4a,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xed,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x18,0x2,0x0,0x0,0xa5,0x5,0x0,0x0,0xcc,
+0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef,0x1,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x35,0x3,0x0,0x0,0x7a,0x2,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xbd,0x5,0x0,0x0,0x89,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xf1,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x46,0x0,0x0,0x0,0x40,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xf3,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xed,
+0x4,0x0,0x0,0x6a,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf6,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc5,0x2,0x0,0x0,
+0x90,0x0,0x0,0x0,0x5c,0x1,0x0,0x0,0xe0,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf8,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa4,0x3,0x0,0x0,0x5e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xfa,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe3,0x4,
+0x0,0x0,0x32,0x5,0x0,0x0,0x7b,0x5,0x0,0x0,0x6b,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x72,0x5,0x0,0x0,0xa1,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xfd,0x1,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xb0,0x5,0x0,0x0,0x24,0x2,0x0,0x0,0x96,0x3,0x0,0x0,0xf,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x1,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xe6,0x0,0x0,0x0,0xf5,0x0,0x0,0x0,0x3d,0x5,0x0,
+0x0,0xe4,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb3,0x5,0x0,0x0,0x39,0x0,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xc4,0x2,0x0,0x0,0xb6,0x2,0x0,0x0,0x2c,
+0x3,0x0,0x0,0x3f,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x0,0x0,0x0,
+0xaa,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x24,0x5,0x0,0x0,0x2d,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x0,0x0,0x0,0x5d,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xea,0x0,0x0,0x0,0xf1,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xba,0x3,0x0,
+0x0,0x43,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x61,0x5,0x0,0x0,0x6,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9,0x5,0x0,0x0,0x6d,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x5d,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x14,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x5c,0x2,0x0,0x0,0x34,0x0,0x0,0x0,0xb,0x5,0x0,0x0,0x6b,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x3,0x1,0x0,0x0,0x73,0x0,0x0,0x0,0x2c,0x2,
+0x0,0x0,0x7f,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf7,0x1,0x0,0x0,0x9d,
+0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd7,0x4,0x0,0x0,0xe2,0x2,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1a,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x76,0x5,0x0,0x0,0x98,0x5,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xca,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,0x67,0x2,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x3,0x0,0x0,0xc5,0x3,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x95,0x3,0x0,0x0,0x39,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x28,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xa7,0x5,0x0,0x0,0x72,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x29,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x32,0x1,
+0x0,0x0,0x45,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2d,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb2,0x0,0x0,0x0,0x4c,
+0x0,0x0,0x0,0xd5,0x2,0x0,0x0,0xad,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe4,0x1,0x0,0x0,0x66,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x33,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x10,0x4,0x0,
+0x0,0xfb,0x4,0x0,0x0,0x8e,0x5,0x0,0x0,0x16,0x0,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x19,0x0,0x0,0x0,0x1e,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x36,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xdf,
+0x1,0x0,0x0,0x2a,0x2,0x0,0x0,0xf4,0x4,0x0,0x0,0xd4,0x2,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdc,0x1,0x0,0x0,0x4c,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x3c,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0x57,0x2,0x0,0x0,0x4d,0x1,0x0,0x0,0x5c,0x3,0x0,0x0,0x7b,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0xaf,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x4c,0x1,0x0,0x0,0xab,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x40,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xc8,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x43,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x2,0x0,
+0x0,0x4a,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe7,0x4,0x0,0x0,0x87,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x73,0x5,0x0,0x0,0xed,0x4,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4b,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x64,0x5,0x0,0x0,0xf3,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x4c,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xb,0x5,0x0,0x0,0x35,0x5,0x0,0x0,0xfe,0x1,0x0,0x0,0x1b,0x3,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xb8,0x3,0x0,0x0,0xc4,0x3,0x0,0x0,0x27,0x0,
+0x0,0x0,0x14,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xae,0x2,0x0,0x0,0xad,
+0x0,0x0,0x0,0xa2,0x5,0x0,0x0,0xdb,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x50,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x78,0x3,0x0,0x0,0x2e,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x53,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x45,0x1,0x0,
+0x0,0x3d,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc6,0x0,0x0,0x0,0xb6,0x2,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x57,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x92,0x3,0x0,0x0,0x2d,0x3,0x0,0x0,0x4a,
+0x5,0x0,0x0,0xce,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x47,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x58,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xb5,0x3,0x0,0x0,0xc6,0x3,0x0,0x0,0x53,0x5,0x0,0x0,
+0xc,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5a,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x1,0x0,0x0,0x5,0x3,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5d,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x79,0x5,0x0,0x0,0xf5,0x0,0x0,0x0,0x24,0x5,
+0x0,0x0,0xdf,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5e,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x64,0x5,0x0,0x0,0xea,
+0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x1,0x0,0x0,0x2f,0x1,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xc5,0x2,0x0,0x0,0xc1,0x2,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x62,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0xed,0x4,0x0,0x0,0x20,0x5,0x0,0x0,0x61,0x0,0x0,0x0,0x3c,0x1,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xdc,0x4,0x0,0x0,0x2e,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x98,0x5,0x0,0x0,0x55,0x0,0x0,0x0,
+0xd2,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbb,0x3,0x0,0x0,0xa3,0x3,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x48,0x5,0x0,0x0,0xd9,0x1,0x0,0x0,0x2e,0x1,
+0x0,0x0,0x77,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf5,0x4,0x0,0x0,0x50,
+0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xe0,0x2,0x0,0x0,0xf3,0x0,0x0,0x0,
+0x31,0x5,0x0,0x0,0x5c,0x5,0x0,0x0,0x6,0x3,0x0,0x0,0xe8,0x1,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6e,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xed,0x4,0x0,0x0,0x56,0x2,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x86,0x5,0x0,0x0,0x71,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x73,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,
+0x4,0x0,0x0,0xf3,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x75,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x53,0x5,0x0,0x0,
+0xec,0x4,0x0,0x0,0x98,0x5,0x0,0x0,0xea,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x76,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x4a,0x5,0x0,0x0,0x25,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x77,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x20,0x3,
+0x0,0x0,0x94,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x80,0x5,0x0,0x0,0x78,
+0x5,0x0,0x0,0xbc,0x3,0x0,0x0,0x94,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7d,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xed,0x2,0x0,0x0,0xf1,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x7e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x5,0x0,
+0x0,0x97,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,0xf,0x2,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x82,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x14,0x5,0x0,0x0,0xbc,0x0,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xf,0x3,0x0,0x0,0x18,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x88,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x92,0x2,0x0,0x0,0xd2,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x89,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x47,0x3,
+0x0,0x0,0xdc,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8c,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x3,0x0,0x0,0xf2,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb1,0x5,0x0,0x0,0xf5,0x1,0x0,0x0,
+0x76,0x1,0x0,0x0,0x3b,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x90,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x68,0x3,0x0,
+0x0,0x3d,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x93,0x1,0x0,0x0,0x99,0x3,
+0x0,0x0,0xf6,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x94,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf8,
+0x4,0x0,0x0,0x5d,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x95,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe4,0x1,0x0,0x0,
+0x8,0x2,0x0,0x0,0xf6,0x4,0x0,0x0,0x4a,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x97,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xee,0x4,0x0,0x0,0xe5,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x99,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x38,0x5,
+0x0,0x0,0xff,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9a,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,0x5,0x0,0x0,0x2f,
+0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9b,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x8b,0x0,0x0,0x0,0x41,0x0,0x0,0x0,
+0xde,0x0,0x0,0x0,0x51,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9d,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x35,0x3,0x0,
+0x0,0x7f,0x0,0x0,0x0,0x9f,0x5,0x0,0x0,0xfb,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x35,0x1,0x0,0x0,0x1e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x52,
+0x3,0x0,0x0,0xe2,0x4,0x0,0x0,0x9c,0x1,0x0,0x0,0x2d,0x3,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa3,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0xb4,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa7,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3f,0x5,0x0,0x0,0x6a,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x1,
+0x0,0x0,0xa3,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4e,0x2,0x0,0x0,0x30,
+0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x1,0x0,0x0,0xc6,0x2,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x46,0x1,0x0,0x0,0xfe,0x4,0x0,0x0,0xfe,0x2,0x0,
+0x0,0xe2,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xae,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x2,0x0,0x0,0xdb,0x0,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbe,0x5,0x0,0x0,0x29,0x0,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xb1,0x0,0x0,0x0,0xfc,0x4,0x0,0x0,0x10,0x3,0x0,0x0,
+0xb2,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb3,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x2,0x0,0x0,0x5f,0x2,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x0,0x0,0x0,0xe,0x2,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb5,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x93,0x1,0x0,0x0,0x97,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb6,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x68,0x1,0x0,0x0,0xda,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb8,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x0,0x0,
+0x0,0x54,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb9,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x22,0x2,0x0,0x0,0x25,0x1,
+0x0,0x0,0x38,0x5,0x0,0x0,0x45,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xbb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xef,
+0x2,0x0,0x0,0xf1,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xbd,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x9e,0x3,0x0,0x0,
+0x17,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x5,0x0,0x0,0x0,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0x2,0x3,0x0,0x0,0x6a,0x0,
+0x0,0x0,0xe,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc1,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x10,0x4,0x0,0x0,0x5b,
+0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2d,0x0,0x0,0x0,0x42,0x0,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc5,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0xf8,0x4,0x0,0x0,0x62,0x2,0x0,0x0,0x96,0x2,0x0,
+0x0,0x21,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0xfe,0x2,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc8,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf5,0x4,0x0,0x0,0x21,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcc,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x64,0x5,0x0,0x0,0x48,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xcf,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xbf,0x3,0x0,0x0,0x2e,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xd0,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x97,0x0,
+0x0,0x0,0x2e,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0xe6,0x2,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb3,0x0,0x0,0x0,0x68,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd4,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x7,0x0,0x0,0x0,0x66,0x1,0x0,0x0,0xb8,0x3,0x0,0x0,0x57,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd8,0x2,0x0,0x0,0xff,0xff,0xff,
+0xff,0x2,0x0,0x0,0x0,0x5a,0x2,0x0,0x0,0x3d,0x1,0x0,0x0,0x3c,0x3,0x0,
+0x0,0xa6,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd9,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0xa5,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x57,0x5,0x0,0x0,0x1c,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0xd,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xdc,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xae,0x2,0x0,0x0,0x6b,0x2,0x0,0x0,0x4a,0x5,0x0,0x0,0x2e,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdd,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x86,0x1,0x0,0x0,0xf,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe4,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0xeb,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe5,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xe0,0x2,0x0,0x0,0x8e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xe7,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x98,0x0,0x0,
+0x0,0x8c,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0x24,0x1,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x2,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0xdd,0x4,0x0,0x0,0x58,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xec,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x48,0x2,0x0,0x0,0x7e,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xed,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7b,0x5,
+0x0,0x0,0xda,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,
+0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x37,
+0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x2,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0xf6,0x2,0x0,0x0,
+0x23,0x3,0x0,0x0,0x8,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xf5,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x1,0x0,
+0x0,0x60,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x2,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x93,0x1,0x0,0x0,0x28,0x3,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x2,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x49,0x1,0x0,0x0,0x99,
+0x5,0x0,0x0,0x8,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xf9,0x2,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x69,0x3,0x0,0x0,
+0x15,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x2,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x70,0x3,0x0,0x0,0x5c,0x3,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x2,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x3,0x0,0x0,0x1a,0x3,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,
+0x0,0x0,0x0,0xd2,0x0,0x0,0x0,0x6,0x2,0x0,0x0,0x5c,0x5,0x0,0x0,0x10,
+0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x12,0x5,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x65,0x2,0x0,0x0,0x4b,0x2,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5a,0x5,0x0,0x0,0x9c,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x5,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x63,
+0x2,0x0,0x0,0x64,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5f,0x1,0x0,0x0,
+0xb2,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x61,0x0,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x26,0x1,0x0,0x0,0x2d,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x85,0x2,0x0,0x0,0xdb,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xa,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x8a,0x5,0x0,0x0,0x9,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x28,0x5,0x0,
+0x0,0x30,0x5,0x0,0x0,0x89,0x5,0x0,0x0,0x55,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xd,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x73,0x2,0x0,0x0,0xe7,0x2,0x0,0x0,0x53,0x5,0x0,0x0,0x16,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x5,0x0,0x0,0x20,0x5,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x22,0x3,0x0,0x0,0x79,0x3,0x0,0x0,0x34,0x2,0x0,0x0,
+0x34,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x72,0x3,0x0,0x0,0x20,0x3,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x15,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x11,0x1,0x0,0x0,0x9,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x5d,0x2,0x0,0x0,0x9a,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa6,0x1,0x0,
+0x0,0x48,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1d,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x9,0x1,0x0,0x0,0x9,0x0,
+0x0,0x0,0x55,0x0,0x0,0x0,0xf,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1e,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x89,
+0x5,0x0,0x0,0x13,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5a,0x0,0x0,0x0,
+0xe0,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x43,0x3,0x0,0x0,0x37,0x3,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x16,0x3,0x0,0x0,0x6c,0x3,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xa1,0x0,0x0,0x0,0xd2,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x25,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x48,0x3,0x0,0x0,0x42,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x28,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xa9,0x2,0x0,
+0x0,0xfd,0x2,0x0,0x0,0x80,0x5,0x0,0x0,0xee,0x0,0x0,0x0,0x43,0x3,0x0,
+0x0,0xfd,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc4,0x1,0x0,0x0,0xba,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0xb9,0x0,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x5b,0x2,0x0,0x0,0xd8,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x2d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x84,0x1,0x0,0x0,0xd8,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x2e,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x3,
+0x0,0x0,0x13,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xe1,0x4,0x0,0x0,0xde,
+0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x1,0x5,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x4b,0x0,0x0,0x0,0x9d,0x0,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x35,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xf6,0x4,0x0,0x0,0xef,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x36,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4a,
+0x2,0x0,0x0,0x3b,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x37,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x6d,0x2,0x0,0x0,
+0xcf,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2a,0x5,0x0,0x0,0x58,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3a,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0xbe,0x2,0x0,0x0,0x2a,0x3,0x0,0x0,0xbf,0x3,
+0x0,0x0,0x97,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0x41,
+0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x96,0x2,0x0,0x0,0x6c,0x1,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x92,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x42,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xbb,0x3,0x0,0x0,0x9,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x44,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2e,
+0x1,0x0,0x0,0x3,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x46,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2c,0x2,0x0,0x0,
+0xb,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x63,0x2,0x0,0x0,0x64,0x2,0x0,
+0x0,0x20,0x4,0x0,0x0,0xb3,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x4b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x87,0x0,
+0x0,0x0,0xfb,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x41,0x3,0x0,0x0,0x1f,0x5,
+0x0,0x0,0x52,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa0,0x2,0x0,0x0,0xd5,
+0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4e,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x71,0x3,0x0,0x0,0x38,0x3,0x0,0x0,
+0x1b,0x0,0x0,0x0,0x72,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x4f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x50,0x1,0x0,
+0x0,0xf,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1d,0x5,0x0,0x0,0x5a,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x52,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x1,0x0,0x0,0x85,0x0,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x70,0x3,0x0,0x0,0xdd,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x55,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xb6,0x5,0x0,0x0,0x9,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x58,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x2a,0x1,
+0x0,0x0,0x3e,0x1,0x0,0x0,0x11,0x1,0x0,0x0,0xe3,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x8b,0x5,0x0,0x0,0x70,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x5d,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x70,0x3,0x0,0x0,0xf,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x5e,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x39,0x5,0x0,
+0x0,0x5a,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xee,0x2,0x0,0x0,0xa,0x0,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x66,0x5,0x0,0x0,0x1b,0x5,0x0,0x0,0x57,
+0x2,0x0,0x0,0x9f,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x63,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,
+0x3b,0x3,0x0,0x0,0xbb,0x5,0x0,0x0,0xe,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x64,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x3c,0x2,0x0,0x0,0x26,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x66,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x86,0x0,
+0x0,0x0,0x43,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x41,0x1,0x0,0x0,0x41,
+0x2,0x0,0x0,0x14,0x5,0x0,0x0,0x5d,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x69,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x10,0x1,0x0,0x0,0x20,0x4,0x0,0x0,0x15,0x3,0x0,0x0,0x27,0x2,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6b,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x89,0x0,0x0,0x0,0x19,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x6f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x5a,0x0,0x0,0x0,0x96,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x73,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,
+0x2,0x0,0x0,0xb2,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x75,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xac,0x0,0x0,0x0,
+0x4f,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3a,0x5,0x0,0x0,0x42,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x2,0x0,0x0,0x0,0x20,0x3,0x0,0x0,0xe8,0x1,0x0,0x0,0xd5,0x4,
+0x0,0x0,0xe,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7b,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x87,0x0,0x0,0x0,0x2f,
+0x1,0x0,0x0,0xc0,0x5,0x0,0x0,0xeb,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x7c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x22,0x2,0x0,0x0,0x54,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x80,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb9,0x0,0x0,
+0x0,0x7a,0x0,0x0,0x0,0xa3,0x3,0x0,0x0,0xfc,0x4,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x81,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0xce,0x1,0x0,0x0,0x21,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x84,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x64,
+0x5,0x0,0x0,0xd4,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x85,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0x2e,0x1,0x0,0x0,
+0xd4,0x2,0x0,0x0,0x9d,0x1,0x0,0x0,0x89,0x1,0x0,0x0,0x72,0x3,0x0,0x0,
+0xcd,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x88,0x5,0x0,0x0,0x8,0x0,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8a,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x8a,0x5,0x0,0x0,0x96,0x5,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,
+0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x66,0x2,0x0,0x0,0xd3,0x4,0x0,0x0,0x12,
+0x5,0x0,0x0,0x22,0x2,0x0,0x0,0x95,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x8c,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x5d,0x0,0x0,0x0,0x3,0x2,0x0,0x0,0xb9,0x3,0x0,0x0,0xad,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8d,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0x55,0x5,0x0,0x0,0x25,0x5,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x8f,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x2c,0x2,0x0,0x0,0x66,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x90,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb7,
+0x1,0x0,0x0,0x1e,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x92,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x5,0x0,0x0,0x0,
+0xd4,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x94,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x46,0x3,0x0,0x0,0xe8,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x95,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x5b,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x96,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xd1,0x1,0x0,0x0,0x39,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x98,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0x3a,0x3,0x0,0x0,0x3f,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x9b,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xee,0x4,0x0,
+0x0,0xdc,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xc0,0x5,0x0,0x0,0xa6,0x0,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7,0x5,0x0,0x0,0x36,0x2,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa0,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x8c,0x5,0x0,0x0,0x10,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xa1,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0xf8,0x4,0x0,0x0,0xb4,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xa3,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x3f,0x0,
+0x0,0x0,0x8,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa6,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x2b,0x0,0x0,0x0,0xcf,
+0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa7,0x3,0x0,0x0,
+0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x29,0x2,0x0,0x0,0x65,0x5,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa8,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xee,0x0,0x0,0x0,0xaa,0x0,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xac,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x86,0x5,0x0,0x0,0xa2,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xad,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x46,
+0x1,0x0,0x0,0x72,0x0,0x0,0x0,0xc2,0x3,0x0,0x0,0x41,0x3,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x90,0x2,0x0,0x0,0x1c,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xb3,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,
+0x0,0xaa,0x0,0x0,0x0,0x1c,0x0,0x0,0x0,0xb7,0x1,0x0,0x0,0xde,0x0,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb4,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xc,0x5,0x0,0x0,0x65,0x5,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xf,0x1,0x0,0x0,0x9b,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xb7,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xbb,0x3,0x0,0x0,0x3e,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xb9,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1a,0x1,0x0,
+0x0,0x89,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xf3,0x0,0x0,0x0,0xc0,0x0,
+0x0,0x0,0x7c,0x5,0x0,0x0,0x50,0x3,0x0,0x0,0xe,0x3,0x0,0x0,0x83,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x3,0x0,0x0,0x0,0xd8,0x2,0x0,0x0,0x6b,0x2,0x0,0x0,0x82,
+0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x35,0x3,0x0,0x0,0x39,0x3,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbd,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0xd9,0x0,0x0,0x0,0xd8,0x1,0x0,0x0,0xfc,0x1,0x0,0x0,
+0x85,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbe,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x22,0x2,0x0,0x0,0x29,0x1,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0xbf,0x3,0x0,0x0,0x58,0x3,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0xb3,0x5,0x0,0x0,0xf3,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xc5,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,
+0xaf,0x5,0x0,0x0,0x57,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc6,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xa8,0x5,0x0,
+0x0,0xe7,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc7,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xe3,0x0,0x0,0x0,0x24,0x1,
+0x0,0x0,0xe3,0x4,0x0,0x0,0x62,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc9,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xcb,
+0x0,0x0,0x0,0x42,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0xca,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x55,0x0,0x0,0x0,
+0xc3,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x40,0x5,0x0,0x0,0x4c,0x5,0x0,
+0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x3,0x0,0x0,0xff,0xff,
+0xff,0xff,0x1,0x0,0x0,0x0,0x53,0x2,0x0,0x0,0x3d,0x1,0x0,0x0,0x1,0x4e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xce,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,
+0x0,0x0,0x0,0x1f,0x5,0x0,0x0,0x35,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xd0,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x3,0x0,0x0,0x0,
+0x12,0x1,0x0,0x0,0x21,0x0,0x0,0x0,0xcb,0x0,0x0,0x0,0x69,0x5,0x0,0x0,
+0xbb,0x3,0x0,0x0,0xf2,0x0,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xd2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x7d,0x1,0x0,
+0x0,0x1a,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xda,0x3,
+0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x4,0x3,0x0,0x0,0x18,0x3,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xdf,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0x1e,0x5,0x0,0x0,0xea,0x4,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe2,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x49,0x5,0x0,0x0,0x48,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xe3,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x47,0x3,0x0,0x0,0xc7,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xe4,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf9,0x4,
+0x0,0x0,0xde,0x4,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe6,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0x1a,0x1,0x0,0x0,0x9c,
+0x3,0x0,0x0,0xe9,0x2,0x0,0x0,0x2d,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xe7,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0xca,0x0,0x0,0x0,0xdf,0x1,0x0,0x0,0xa5,0x0,0x0,0x0,0x3e,0x3,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe9,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x3,0x0,0x0,0x0,0x51,0x2,0x0,0x0,0x73,0x0,0x0,0x0,0x7,0x3,0x0,
+0x0,0x54,0x3,0x0,0x0,0xe9,0x4,0x0,0x0,0x85,0x1,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xeb,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,
+0x0,0x0,0x59,0x4,0x0,0x0,0x2,0x5,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xed,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xb7,
+0x5,0x0,0x0,0x66,0x1,0x0,0x0,0x81,0x2,0x0,0x0,0xdb,0x0,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xee,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x1,0x0,0x0,0x0,0x5a,0x1,0x0,0x0,0x84,0x1,0x0,0x0,0x1,0x4e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xf0,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,
+0x0,0x1,0x5,0x0,0x0,0x45,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0xf1,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x3,
+0x0,0x0,0x9b,0x3,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,
+0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,0xa8,0x5,0x0,0x0,0xd6,
+0x1,0x0,0x0,0x90,0x2,0x0,0x0,0x9c,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0xf4,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,0x0,0x0,
+0x2c,0x1,0x0,0x0,0x73,0x0,0x0,0x0,0x89,0x5,0x0,0x0,0x97,0x5,0x0,0x0,
+0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x3,0x0,0x0,0xff,0xff,0xff,
+0xff,0x1,0x0,0x0,0x0,0xf9,0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x1,0x4e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x3,0x0,0x0,0xff,0xff,0xff,0xff,0x2,0x0,
+0x0,0x0,0x1b,0x1,0x0,0x0,0x36,0x1,0x0,0x0,0x39,0x5,0x0,0x0,0x43,0x5,
+0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x3,0x0,0x0,0xff,
+0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xb0,0x5,0x0,0x0,0x19,0x2,0x0,0x0,0x1,
+0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0x3,0x0,0x0,0xff,0xff,0xff,0xff,
+0x2,0x0,0x0,0x0,0x79,0x0,0x0,0x0,0x4f,0x1,0x0,0x0,0x8c,0x5,0x0,0x0,
+0x23,0x2,0x0,0x0,0x1,0x4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0x3,0x0,
+0x0,0xff,0xff,0xff,0xff,0x1,0x0,0x0,0x0,0xf,0x5,0x0,0x0,0x6e,0x5,0x0,
+0x0,0xf8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x1,0x3e,
+0xf,0x4,0x0,0xc4,0x5,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb8,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x21,0x60,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa4,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x6a,0x84,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x77,0xf3,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,
+0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5d,0xa2,0x65,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,
+0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0x32,0x60,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x1a,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa8,0x52,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xdf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x7,0x54,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x48,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x95,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe2,0x27,0x75,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0xe4,0x74,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe6,0xac,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,
+0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x91,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd1,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0xe4,0x74,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xb6,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xd5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7a,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x50,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x6a,0x84,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x8b,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0xc4,0x1f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6d,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x59,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,
+0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x90,0x73,0x4c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xf1,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0x15,0x30,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xb3,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,
+0xf0,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xe6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdf,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x36,0xb7,0x7f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbb,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x49,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0xf7,
+0x67,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xe0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x97,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,
+0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x8c,0x8,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0x49,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x3c,0x3f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7b,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe6,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,0x49,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0x8b,0x8,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,
+0x84,0x59,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xdb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x56,0xad,0x5c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,
+0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa2,0x32,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9e,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x49,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x15,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb5,0xb3,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xce,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb1,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf7,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,
+0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x6b,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x49,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x30,0x36,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x53,0xc6,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x99,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,
+0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe2,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0xc7,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0x8b,0x8,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,
+0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xce,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x93,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,
+0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9a,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xde,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x57,0x3f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa,0x4b,0x3a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0x5e,
+0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xd5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb3,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf5,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,
+0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x74,0x70,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0x8b,0x8,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf1,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,
+0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xee,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe9,0x26,0x84,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xc8,0x12,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,
+0xef,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa1,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4f,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xee,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0x4a,0x3a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x8c,
+0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xd3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x27,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x3e,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0xc4,0x1f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xb3,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9f,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xe6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x91,0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc3,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x49,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0xc8,0x12,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,
+0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xd9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6b,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9d,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0xe4,0x74,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x81,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9f,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,
+0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0xf5,0x37,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x88,0xb3,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xaf,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xbc,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,
+0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xab,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0x5a,0x41,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0x86,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,
+0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xcf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb0,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,
+0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa7,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x86,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,0x9d,0x50,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x9d,
+0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xcf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa2,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,
+0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xba,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,
+0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0x8b,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x86,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x64,0xad,0x5c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4d,0xb7,0x7f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,
+0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x48,0xc6,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xab,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0xc7,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x86,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,
+0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xde,0xb5,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe1,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc2,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0x15,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x86,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0x86,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xcf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb2,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,
+0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb8,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,
+0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xc4,0x1f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0x5a,0x41,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x26,0x5b,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa9,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,
+0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xda,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x86,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0xb1,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x16,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,
+0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3c,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc7,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x95,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd4,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x7e,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5c,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x17,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3b,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x96,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0x8b,0x8,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0x15,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,
+0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xcc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa0,0xef,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,
+0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbd,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xb7,0x7f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0x8,0x78,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xc8,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x16,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd9,0xf7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf4,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,
+0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xde,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x51,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x57,0x54,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x50,0x45,0x7c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xd9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc6,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x75,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb4,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0xb1,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xd5,0x2b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,
+0x4b,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x52,0xef,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,
+0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x53,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd1,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x57,0x3f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0xe8,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0xc7,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x1d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x40,0xce,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb1,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,
+0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0xff,0x3a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x8d,0x33,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xfe,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe8,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,
+0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x42,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xde,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xc7,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x8b,0x8,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,
+0xad,0x5c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x7d,0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,
+0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x11,0x75,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa6,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x68,0x21,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0x49,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x85,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,
+0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xde,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,
+0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0xc0,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xad,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0x78,0x26,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0xe8,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6f,0x57,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xd9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x67,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,
+0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x72,0xa,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x92,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0xc7,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xd5,0x2b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,
+0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe5,0x74,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,
+0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe5,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa7,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x49,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0xc7,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xb,
+0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xd0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x20,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,
+0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xfa,0x28,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,
+0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x99,0x6d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x94,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0x6,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,0x59,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x68,0x36,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x31,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,
+0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x7b,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xf1,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xc8,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,
+0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xd5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x98,0x62,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x86,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xea,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x6,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x8a,
+0x6d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd7,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x60,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,
+0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xf4,0x68,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xb7,0x8a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0x78,0x26,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x60,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xe3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfb,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,
+0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x96,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x6,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,
+0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xd9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xef,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,
+0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xff,0x47,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9f,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x8b,0x8,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0x27,
+0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xdf,0xb2,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,
+0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x62,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xc4,0x1f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xb6,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9b,0x90,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x18,0x8e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa0,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa3,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xc8,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x6b,0x75,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,
+0x3,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x97,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x65,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd5,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x8a,0x6d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0x76,
+0x4a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xd8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x19,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd9,0x27,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,
+0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xaa,0xf,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x48,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x23,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,0x19,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf3,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0xc8,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0xad,0x84,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,
+0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xd8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5b,0x62,0x49,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,
+0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5b,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd8,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x4d,0x6a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0x6,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xd4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf7,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe5,0x5,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0x78,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0xbd,0x42,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb6,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x49,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,
+0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd6,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x78,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x99,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,
+0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x1b,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9e,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x7e,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0x68,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3f,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,
+0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x51,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0xb1,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x2e,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x1f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x31,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,
+0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xed,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd0,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0x68,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0x78,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,
+0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xcd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,
+0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb1,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcf,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0x73,
+0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4c,0xbb,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x37,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,
+0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x60,0x87,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xd3,0x37,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0xb6,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb0,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc5,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0x41,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xed,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0x20,0x3a,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xb1,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf9,0x28,0x66,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,
+0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9d,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9b,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,0xff,0x51,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xa4,
+0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x72,0x6b,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf8,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc4,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbb,0xc8,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xa4,0x58,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x15,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x42,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,
+0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x89,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x34,0x3b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x51,0x1,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,
+0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xcd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc4,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,
+0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x85,0xc0,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xf1,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x39,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0x68,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0xe9,
+0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa6,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x35,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x69,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xd4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x80,0x1a,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,
+0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdd,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xb1,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0x7e,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,
+0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xd6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,
+0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd6,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xe8,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7b,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,
+0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x65,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x12,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0x12,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa1,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x16,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x70,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x10,0xf4,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xf1,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xc7,0x6e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xc9,0x6e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,
+0xcc,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x18,0x8d,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa1,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7,0x75,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xad,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0xcf,0x6e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x92,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xb6,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb9,0xd1,
+0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x18,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x22,0x3a,0x77,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,
+0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1e,0xad,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0xd8,0x6e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xdb,0x6e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x14,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xd9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfb,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,
+0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x59,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x98,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x70,0xbd,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x5c,0x50,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,
+0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x1e,0xdc,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,
+0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe8,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc8,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0xa4,0x22,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xba,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,
+0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x86,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,
+0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xa9,0x22,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbe,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0xac,0x22,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xf0,0x51,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6b,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfc,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,
+0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0xaf,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc6,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0xb4,0x22,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xca,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0xc0,0x22,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,
+0xa8,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xec,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,
+0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9b,0x52,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd1,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x27,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0xf0,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x5,
+0x32,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x18,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbb,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3f,0x4b,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,
+0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xc4,0x1f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xd,0x32,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x47,0xce,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x18,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x49,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,
+0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x92,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbf,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xd5,0x2b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0x85,0x33,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,
+0x93,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,0x8a,0x18,0x8e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x70,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,
+0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9e,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9d,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x96,0x1d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x96,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x4a,0x3a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x56,
+0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x18,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xaf,0x59,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x18,
+0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa2,0x31,0x3d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x5e,0xe,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa6,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0xa3,0x1d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0xe6,0x4f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x53,0x8a,0x6d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xae,0x63,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,
+0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xfc,0xb2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x97,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0x66,0xe,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x15,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,
+0x69,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x18,0x8e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe3,0x6f,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,
+0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4f,0x72,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbe,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x74,0xe,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x77,
+0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,0x18,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x97,0xbc,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x18,
+0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x61,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0x75,0x7b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0xc2,0x1d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0x82,0xe,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xad,0x84,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x18,0x8e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x64,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0x32,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x92,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x32,0x60,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xd9,0xe,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,
+0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4c,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6b,0x8a,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xde,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x29,0x28,0x16,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x92,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0x2a,0x16,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xc4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2a,0xf8,0x67,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,
+0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x22,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x2d,0x16,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x78,0x87,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x62,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2f,0x30,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,
+0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdb,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x8d,0x17,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0xf0,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,
+0x10,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x93,0x32,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,
+0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x24,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb8,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xb4,0x54,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x13,0x24,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0x7a,
+0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x18,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6e,0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,
+0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x54,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0xdb,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x92,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0xbc,0x3,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x8d,0x33,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x84,0x16,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x19,0xc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x56,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x32,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x94,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xa,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x3a,0x16,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,
+0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3,0x4b,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbf,0xde,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x96,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0x49,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x18,0x24,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa4,0xc,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x18,
+0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x94,0x3c,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,
+0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x92,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xf0,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x61,0x59,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1d,0xe1,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x18,0x8f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd3,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,
+0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xf,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9a,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x1b,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xe3,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,
+0x3f,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x18,0x8b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x71,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,
+0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd5,0x32,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x11,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9e,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0xb6,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9a,0x23,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x19,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xaa,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc5,0xe6,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x9b,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x42,0x16,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x68,0x26,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x19,0xc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc1,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0xf0,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xf0,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,
+0xeb,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x18,0x8f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,
+0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb2,0x14,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa2,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0x8b,0x8,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xf0,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xba,0x47,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x18,
+0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x69,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xff,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xba,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x48,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x29,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x96,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xe6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc9,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xed,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaa,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x28,0x19,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x7c,0x86,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,
+0xd9,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xdf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5c,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbe,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x45,0x7c,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0x4d,0x6a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0x8b,
+0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xd3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7a,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,
+0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf3,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,
+0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0xd5,0x2b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x6,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1a,0x4a,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,0x8a,0x18,0x8b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb7,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xae,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x92,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0xb5,0x58,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xb5,0x5b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa0,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,
+0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x50,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd4,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x81,0xd1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2f,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x51,0x62,0x49,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,
+0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xea,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xbd,0x42,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0xad,0x84,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd0,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x66,0x99,0x59,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x96,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xca,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x2b,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x89,0xa4,0x58,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,
+0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xe2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x1b,0x60,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5,0xf1,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xae,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x6,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc2,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0x7b,0x33,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0x9b,
+0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x88,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xbf,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xa4,0x58,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0x79,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xcd,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xd8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x66,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,
+0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x1c,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x4c,0x16,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,
+0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xcc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4a,0xb2,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,
+0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf4,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdb,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xdb,0x68,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xbd,0x42,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xa4,
+0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x71,0x2e,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x19,
+0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xae,0x8,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,
+0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x34,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xc8,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe8,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xff,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,
+0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd9,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xc8,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x73,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,
+0xf3,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x18,0x8f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5d,0x1f,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,
+0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbd,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xff,0x51,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x4f,
+0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,0x18,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd7,0xdd,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,
+0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x27,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,
+0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xac,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0xb4,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xc8,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xdd,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf9,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,
+0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb0,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xbd,0x42,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x73,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9a,
+0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xe2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xba,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa3,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb7,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x79,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x34,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xd8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x20,0x7b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,
+0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe1,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,
+0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0x79,0x33,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x78,0x26,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x68,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x1c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x77,0x27,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,
+0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xde,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x7a,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,
+0x4d,0xf,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x19,0xf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xce,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,
+0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x1,0x2b,0x43,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd2,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x68,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x94,
+0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x18,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4,0xb7,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,
+0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa8,0x39,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,
+0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xfb,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x53,0xf,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x60,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x10,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x36,0x3,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,
+0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x99,0x67,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xce,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xba,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x57,0x16,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,
+0x3c,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfc,0x13,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,
+0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x14,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc2,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0xfd,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x2a,0x6,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xc1,
+0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x19,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc6,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3b,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,
+0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x5,0x52,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xba,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xdd,0x2d,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xed,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x14,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x68,0x85,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,
+0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa2,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xb2,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,
+0x50,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x19,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdc,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,
+0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xcd,0xdd,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd2,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x68,0x21,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x7e,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xc4,
+0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x19,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbf,0x41,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,0x8a,0x19,
+0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x9e,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa2,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0xc8,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x47,0x3,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,0x18,0x8f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8e,0x72,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,
+0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd6,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xb1,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x5b,0x16,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,
+0x8,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,0x19,0x9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4f,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,
+0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc8,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0xe3,0x40,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x78,0x26,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x4a,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x19,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x10,0x1c,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x52,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,
+0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xe,0x45,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe9,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x64,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,
+0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x32,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc6,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0xc9,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0xb7,0x8a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,
+0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x24,0x24,0x44,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,
+0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xab,0x7f,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd6,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x6a,0x5f,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x77,0xd6,0x7c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc8,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,
+0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x4d,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe1,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x4c,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0x7e,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3b,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x1c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x22,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0x78,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe1,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xe,0x16,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xe8,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,
+0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x2f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,
+0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd8,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa6,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0xae,0x6e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa6,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x7e,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0x68,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x69,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,
+0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3b,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xa3,0x25,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xca,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd8,0xcf,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x19,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xbe,0x4f,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,
+0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcc,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaa,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xdd,0x43,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x7e,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x50,0x2d,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,
+0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfc,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xea,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0x92,0x26,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x41,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,
+0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6,0x7e,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,
+0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xee,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x7e,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x68,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x78,0x1d,0x8c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,0x19,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xcb,0xa,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,
+0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc3,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x51,0x1,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,
+0xd6,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe5,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,
+0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xde,0xe0,0x43,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe1,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xab,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0x11,
+0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x18,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x80,0xd5,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,
+0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xea,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x81,0x15,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xc8,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7b,0xdb,0x7c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa2,0x1c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf8,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x78,0x5f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaa,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xb1,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x81,0x97,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,
+0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa2,0x19,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc4,0x5e,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,
+0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa5,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc2,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x59,0x17,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb6,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x97,0x33,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0x78,
+0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x14,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x12,0x1a,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xf6,0x40,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9a,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x44,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0x8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe3,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe4,0x1e,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,
+0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x3b,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x96,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x7e,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,
+0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x14,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x62,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,
+0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6a,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcc,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xf0,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0xf0,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4d,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,
+0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x68,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,
+0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xf0,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xf0,0x7,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6c,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa2,0x10,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x35,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,
+0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9e,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xe2,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xe2,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,
+0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3d,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,
+0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x37,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe4,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xf7,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x92,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0xe2,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xe2,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x28,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,
+0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x43,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,
+0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xe2,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xe2,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1b,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x13,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2c,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,
+0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x30,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdd,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xe2,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0xe2,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,
+0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x11,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,
+0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x32,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x96,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xe1,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xe8,
+0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb1,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,
+0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc6,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,
+0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0xe8,0x34,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xe8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xaf,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa3,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xda,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0xe8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xe8,0x34,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,
+0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x11,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd4,0x54,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,
+0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbc,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xae,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x63,0x5e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x9e,
+0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x18,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd6,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,
+0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x10,0x2b,0x44,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,
+0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x96,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0xe8,0x34,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcc,0xe8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa2,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb8,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,
+0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x92,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xe8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xe8,0x34,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,
+0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x1f,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,
+0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xaa,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x3b,0x9,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2a,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa2,
+0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xff,0x3a,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,
+0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa1,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x3b,0x9,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0x3b,0x9,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x35,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x1a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xda,0xc8,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,
+0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf0,0xdf,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xca,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0xb9,0x31,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x3b,0x9,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,
+0xe1,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x19,0xf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4e,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,
+0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbf,0xe8,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xce,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x47,0x6,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xfa,0x3a,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,
+0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1a,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,
+0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x97,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x3b,0x9,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x3b,0x9,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1a,0xbc,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x19,0xe,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4c,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,
+0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x74,0xa1,0xb4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x91,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xe4,0x3f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xeb,0x6,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,
+0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc9,0xc1,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,
+0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x79,0xe7,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb2,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0xed,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0xc4,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xe9,
+0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x19,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xfa,0x99,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x18,
+0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,
+0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x80,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x29,0x2e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xac,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x86,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x89,
+0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc6,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xef,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xc7,0x31,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,
+0x5b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x18,0x89,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe8,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,
+0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x14,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa2,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x96,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x81,0x2d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0x11,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x89,0x8a,0x18,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x40,0xf3,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x19,
+0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x19,0xca,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,
+0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0x80,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x81,0x2d,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x97,0x5d,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x18,0x89,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9b,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,
+0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x60,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9e,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb8,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xcc,0x31,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7a,
+0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x18,0x8a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x81,0xd3,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,
+0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6b,0x63,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa2,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc6,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xd5,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x67,
+0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x18,0x89,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbc,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x18,
+0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x67,0x6b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,
+0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xd8,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd6,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x11,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x6d,0x17,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf7,0xe6,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x19,0x8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xaf,0xdb,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,
+0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0xe9,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbe,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd0,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x70,0x17,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,
+0xde,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x19,0xe,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x81,0x39,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,
+0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa0,0xec,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc2,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x72,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x81,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x1b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x24,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,
+0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,
+0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xba,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x81,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0x81,0x2d,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x22,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa2,0x1b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3c,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,
+0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb4,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x81,0x2d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,
+0x3b,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x19,0xb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5,0xef,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,
+0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x72,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe2,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xba,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0xef,0x2,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xef,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc3,0x80,0x37,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc,0xfa,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaa,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xef,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x3c,0x59,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x22,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x12,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x93,0x41,0x45,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,
+0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb2,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xee,0x2,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xee,0x2,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,
+0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x18,0x8a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xff,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,
+0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x71,0xfc,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9a,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0x41,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xf9,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xee,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x50,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa2,
+0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf2,0x43,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd1,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xef,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xef,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x35,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x12,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,
+0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb9,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xfe,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xee,0x2,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,
+0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa2,0x12,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x42,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,
+0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe0,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd7,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x37,0x83,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0xee,0x2,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4,0xef,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xfd,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,
+0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x38,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,
+0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x7e,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc2,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x11,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf9,0x4d,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x6,0x80,0x8e,0x17,0x6b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xaf,0xc6,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,
+0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0x60,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x92,0x80,0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x29,0xfc,0xc,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x1,0x15,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,
+0xc7,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0x17,0x48,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x51,0xc8,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,
+0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6e,0x4c,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa6,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc2,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,0x80,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0xff,
+0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x19,0x8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd6,0x4e,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x22,0x9,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x83,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xca,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x11,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x1,0xd,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x8f,0xb,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x19,0xd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6e,0x51,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,
+0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0x88,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xce,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb0,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x2,0x2a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,
+0x4,0xd,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x19,0x8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x18,0xe,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,
+0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7d,0x54,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb2,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x5,0x2a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd2,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0x61,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x53,
+0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0x17,0x49,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5a,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x18,
+0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x37,0x5d,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,
+0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x8b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd2,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x15,0x22,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x9e,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x70,0x14,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0x17,0x49,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x54,0x19,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,
+0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xe6,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9a,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x1e,0x22,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0x1b,0x22,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,
+0xc0,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x19,0x8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x74,0x10,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,
+0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa3,0x1d,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xea,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0x7,0x2a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0x20,0x22,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x13,
+0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0x17,0x49,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc1,0x5a,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0x17,
+0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb,0xa2,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,
+0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x56,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb6,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x11,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0x8e,0x17,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf7,0xc3,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x19,0x8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x60,0xa,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,
+0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xa4,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9a,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x12,0x15,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x11,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,
+0x5f,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x19,0xb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x17,0x91,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,
+0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x12,0xe9,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe1,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xc6,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0xd,0x2a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x1c,
+0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x19,0xd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4f,0x62,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd9,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,
+0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x93,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xde,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xc8,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xa9,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xcd,0xab,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x19,0xa,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x67,0x1f,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x64,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc2,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xae,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0xcb,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,
+0xae,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,0x19,0xa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x81,0xa5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,
+0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6,0x68,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc6,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0xb0,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaa,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0xd2,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x4c,
+0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0x17,0x44,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2,0xb1,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x19,
+0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x7,0x22,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x6a,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xca,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xb4,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0xd5,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf1,0x59,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0x17,0x49,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x64,0x5c,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,
+0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x12,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe1,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x16,0x22,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xf8,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x21,0x22,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,
+0x12,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfa,0x80,0x8e,0x17,0x49,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3b,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x1f,0x81,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x97,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0xb3,0x29,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9e,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xcc,0x47,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x7b,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0x17,0x74,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2e,0x8,0x57,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0x17,
+0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x78,0x8b,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,
+0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0x81,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc7,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xd0,0x47,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0xc7,0x47,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4f,0x80,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0x17,0x74,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc,0x26,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x7e,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe3,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x87,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0xc5,0x47,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,
+0x73,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x19,0xb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa9,0xb6,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x89,0x8a,
+0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x62,0xcb,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9b,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7b,0xbe,0x47,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa6,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0xc6,0x47,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xbf,
+0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0x17,0x74,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbb,0xc1,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0x17,
+0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x58,0x86,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,
+0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0x7d,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc2,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x80,0x47,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xc3,0x56,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xde,0x83,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0x17,0x6f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x44,0x7b,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,
+0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x3d,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xda,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x7c,0x47,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xfd,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xd8,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,
+0xb7,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0x17,0x6f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x35,0xba,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,
+0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb4,0xb6,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa2,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x7e,0x47,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbf,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0x28,0x15,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x75,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x19,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb4,0xbc,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0x17,
+0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb5,0x74,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x6,
+0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x7d,0x47,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1b,0x0,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x84,0x20,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xbb,0x56,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc,0xb9,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x18,0x8c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf3,0x40,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfa,0x80,
+0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0xda,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb2,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x82,0x20,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xbb,0x56,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,
+0x77,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0x17,0x6f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x35,0x8f,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,
+0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x93,0x81,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0x45,0x11,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1b,0x1,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xbe,0x29,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0x41,
+0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0x17,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x50,0x87,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0x17,
+0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x95,0xb8,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,
+0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x30,0x15,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xce,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xb5,0x56,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x12,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa3,0x82,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfa,0x80,0x8e,0x17,0x6f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb7,0x46,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,
+0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x73,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xf7,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x44,0x11,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xff,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0x7f,0x20,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,
+0x49,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0x17,0x6b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x57,0x8e,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,
+0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x64,0x43,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x94,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x78,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x4b,0x11,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1b,0x3,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x8a,
+0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0x17,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x83,0x3e,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0x17,
+0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x76,0xbe,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,
+0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x8c,0x20,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb9,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xc0,0x29,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0xd,0x40,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xff,0xf,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0x17,0x64,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x51,0xe,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,
+0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x4e,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9d,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xdd,0xc,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xf7,0x39,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,
+0x11,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x5,0x80,0x8e,0x17,0x64,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x31,0x15,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,
+0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x8d,0x8,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb1,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x48,0x4f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa3,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xb,0x40,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1b,0x2,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0xf,
+0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfc,0x80,0x8e,0x17,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6a,0x7b,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc2,0x33,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x9,0x40,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xf8,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x14,0x40,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xff,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x4d,0x4f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xed,0x6,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0x17,0x64,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xde,0xc0,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,
+0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xbd,0x46,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa9,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x11,0x40,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x8c,0x5e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,
+0x12,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf5,0x80,0x8e,0x17,0x64,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x8d,0x47,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,
+0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x98,0x2,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xca,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xc3,0x29,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xae,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x6,0x25,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xff,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0x17,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7f,0xf8,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0x17,
+0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5a,0x36,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf6,0x3,0x25,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xce,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xf5,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xfd,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc,0x8,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0x17,0x66,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6a,0xf4,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,
+0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xfa,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xfc,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x1,0x25,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,
+0xf6,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0x17,0x66,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x52,0x5,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,
+0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf5,0x6,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xda,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0x0,0x25,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc2,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xfc,0x24,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xf7,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0x17,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x47,0xc3,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x18,
+0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x96,0xf9,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,
+0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xc5,0x29,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb2,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xf6,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x5a,0x15,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x8a,0x64,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0x17,0x65,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc2,0x99,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,
+0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe,0x7e,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xde,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x97,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x59,0x15,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,
+0xa1,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0x17,0x65,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4c,0x90,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,
+0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf2,0x98,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc7,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0xd4,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb2,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xd3,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0xcf,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0x17,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x64,0xd1,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,
+0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb3,0xdd,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,
+0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x56,0x39,0x15,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xda,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xc6,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc6,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xcc,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc5,0xd5,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0x17,0x65,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xdd,0xd6,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,
+0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xd8,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xba,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xdc,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xc8,0x29,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,
+0xca,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0x17,0x65,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x27,0xda,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,
+0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb9,0x3b,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xde,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0x89,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0x7c,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0xd1,
+0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x19,0xa,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf3,0x85,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0x17,
+0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x22,0x88,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,
+0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x79,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb7,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x8c,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x2a,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x96,0x78,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0x17,0x68,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfd,0xc8,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,
+0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0x6c,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9d,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbd,0x2e,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1b,0x6,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0xce,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,
+0x28,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0x17,0x68,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd0,0xd3,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,0x8a,
+0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa5,0x34,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbc,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0x2d,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x91,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x71,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x31,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0x17,0x68,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7c,0x2b,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0x17,
+0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x46,0x38,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x3,
+0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2d,0x70,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x99,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0x33,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0xd0,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x26,0xd6,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x89,0x8a,0x19,0xa,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x51,0xd3,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,
+0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x71,0xd9,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc6,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xd6,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xda,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xdb,0x29,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,
+0xd8,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x18,0x8c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x15,0xb3,0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,
+0x17,0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x8,0xd3,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xda,0x80,0x8e,0x17,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x54,0x2d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x12,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x54,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6c,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,
+0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc,0x45,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,
+0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x54,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0x12,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0x12,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa0,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6d,0x53,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,
+0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9a,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc7,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x12,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x12,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,
+0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xe4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x33,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x86,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb8,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x72,0x80,0x6a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0xbc,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x1a,
+0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xda,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x60,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,
+0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa8,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,
+0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xbc,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0xe0,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x47,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xcf,0x99,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,
+0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xf0,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x93,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0xa,0x68,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf6,0xbc,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,
+0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xda,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x44,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x18,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbf,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xe0,0x1b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0xe5,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xda,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7d,0xe9,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,
+0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc8,0x22,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x99,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xb0,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0x75,0x1f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x19,0xa7,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa3,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,
+0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xab,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x57,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xe5,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,
+0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xed,0xf2,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb7,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xde,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xe5,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xda,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbe,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x2,0xfc,0x4b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,
+0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd9,0x43,0x40,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xbd,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xe5,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8c,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,
+0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa0,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x57,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0xe5,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,
+0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xc8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa4,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4f,0xc8,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9a,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0xe9,0x51,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xe5,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x57,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xc9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa5,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1a,0xf6,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xe5,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb4,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x35,0xa7,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,
+0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x68,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd8,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xe5,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,
+0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xcb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6d,0xe9,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,
+0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4b,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xe0,0x1b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x57,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xc9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe0,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,
+0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9,0x86,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0xe5,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa1,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf1,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,
+0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xf8,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe6,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x1a,0x6b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,
+0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb9,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,
+0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3e,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x57,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0xbc,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x91,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x58,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0x27,0x2e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xce,0x13,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x22,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x93,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xc9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x42,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb0,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd5,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x1a,0x6b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,
+0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x21,0xc8,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,
+0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x89,0xfa,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xea,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xd8,0x5b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xbd,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x57,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xc9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9b,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3a,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,0xbd,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xe0,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x48,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xc8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1f,0x3e,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,
+0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xee,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xbc,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0xe0,0x1b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,
+0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xc8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x11,0xa2,0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x45,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd0,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0xfc,0x1,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xa,
+0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xcb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb4,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,
+0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x13,0x96,0x4c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,
+0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xbc,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0xe8,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x53,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xc8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9b,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,
+0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd6,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xce,0x13,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xce,0x13,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,
+0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xdd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,
+0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6b,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe0,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0xce,0x13,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xce,
+0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x33,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,
+0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x73,0x5,0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,
+0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xce,0x13,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x10,0x23,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x68,0x5,0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xdd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x39,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,
+0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe,0xc3,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaa,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0xce,0x13,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0x80,0x41,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,
+0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xdd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x40,0xa1,0x65,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,
+0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x11,0xf0,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9f,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xfe,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x67,0x5,0x55,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0xe,0x80,0x8e,0xa1,
+0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xbe,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,
+0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xde,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0xb6,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x32,0x60,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xae,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xd0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb8,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa7,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x8c,0x43,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xfa,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x39,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,
+0x48,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xe7,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x76,0xe3,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,
+0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x82,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbc,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0xe7,0x4f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x23,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9f,0x1e,0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,
+0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5b,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x27,0x75,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x93,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xb5,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x6,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x65,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xe3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xba,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,
+0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc3,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x17,0x62,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x48,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,
+0xe5,0x65,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x79,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x59,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xee,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xbc,0x3,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x1c,
+0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xde,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x64,0xe3,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,
+0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,
+0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x59,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0xf0,0x50,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc2,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xd0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4d,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0xd7,0x52,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xf5,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0xe3,0x74,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x48,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,
+0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x7d,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x31,0xf0,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa0,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x4d,0x6a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0x5a,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x14,
+0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xde,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x14,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,
+0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb6,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,
+0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xee,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x59,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x72,0x6,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb2,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xd0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xdc,0xa4,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x12,0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa0,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0x6a,0x84,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0xb6,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,
+0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xd6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6f,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc8,0xad,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x98,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xd4,
+0x52,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xde,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc6,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,
+0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1,0x48,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x89,0x54,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1b,0x13,0x80,0x8e,0xa1,0xe5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xd5,0x2b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9d,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xe6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa2,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xef,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x15,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,
+0x88,0x54,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfd,0x80,0x8e,0xa1,0xe5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd5,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,
+0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x47,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xba,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0x74,0x70,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0xef,0x6b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0xc4,0x5,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0x6d,0x65,0x2c,0x0,
+0x34,0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,0x6d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1e,0x64,0x2c,0x0,0xeb,0x65,0x2c,0x0,0xec,0x65,0x2c,0x0,0xc2,0x40,0x2d,
+0x0,0xe9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x8b,0x65,
+0x2c,0x0,0x2e,0x66,0x2c,0x0,0xa3,0x41,0x2d,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x3a,0x64,0x2c,0x0,0xc0,0x65,0x2c,0x0,0xc1,0x65,0x2c,0x0,0x94,
+0x83,0x2d,0x0,0xc0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,
+0x40,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0xf0,0x82,0x2d,0x0,0x40,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x71,0x65,0x2c,0x0,0xbd,0x65,0x2c,0x0,0xbe,0x65,0x2c,
+0x0,0x86,0x7b,0x2d,0x0,0xbb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,0x64,
+0x2c,0x0,0x81,0x65,0x2c,0x0,0x82,0x65,0x2c,0x0,0x75,0x83,0x2d,0x0,0x81,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x33,
+0x66,0x2c,0x0,0x51,0x49,0x2d,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x30,0x64,0x2c,0x0,0x86,0x65,0x2c,0x0,0x87,0x65,0x2c,0x0,0x87,0x83,0x2d,0x0,
+0x86,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0x9a,0x65,0x2c,
+0x0,0x2e,0x66,0x2c,0x0,0x9c,0x55,0x2d,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2d,0x66,0x2c,0x0,0x93,0x66,0x2c,0x0,0xb0,0x66,0x2c,0x0,0x6c,0x83,
+0x2d,0x0,0xaf,0x66,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x7a,
+0x65,0x2c,0x0,0x98,0x65,0x2c,0x0,0x52,0x3c,0x2d,0x0,0x7e,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0x23,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,
+0xa4,0xea,0x2c,0x0,0x22,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,
+0x0,0xb4,0x65,0x2c,0x0,0xb5,0x65,0x2c,0x0,0xe1,0x7b,0x2d,0x0,0xb4,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x21,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,0xc8,0x65,
+0x2c,0x0,0xb7,0xce,0x2c,0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,
+0x64,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x35,0x66,0x2c,0x0,0x7e,0x4e,0x2d,0x0,0x4f,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x12,0x64,0x2c,0x0,0x1e,0x65,0x2c,0x0,
+0x1f,0x65,0x2c,0x0,0x2c,0xe0,0x2c,0x0,0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x34,0x64,0x2c,0x0,0x31,0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,0xe,0x47,0x2d,
+0x0,0x31,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3d,0x64,0x2c,0x0,0xa9,0x65,
+0x2c,0x0,0x2c,0x66,0x2c,0x0,0x91,0x83,0x2d,0x0,0xa9,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1e,0x64,0x2c,0x0,0xcc,0x65,0x2c,0x0,0xcd,0x65,0x2c,0x0,0x72,
+0x83,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,
+0xdb,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,0x7d,0x3c,0x2d,0x0,0xdb,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x2c,0x66,0x2c,
+0x0,0xf0,0x82,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,
+0x2c,0x0,0xe0,0x65,0x2c,0x0,0x24,0x66,0x2c,0x0,0x90,0x73,0x2c,0x0,0xe0,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xa4,0x65,0x2c,0x0,0xa5,
+0x65,0x2c,0x0,0x1f,0x49,0x2d,0x0,0xa3,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xfd,0x63,0x2c,0x0,0x60,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0x15,0x76,0x2d,0x0,
+0x60,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfa,0x63,0x2c,0x0,0xb1,0x64,0x2c,
+0x0,0x31,0x66,0x2c,0x0,0x74,0x7b,0x2d,0x0,0xb1,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2f,0x64,0x2c,0x0,0x86,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0x9f,0x50,
+0x2d,0x0,0x86,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x13,0x65,0x2c,0x0,0xac,
+0x65,0x2c,0x0,0x31,0x66,0x2c,0x0,0x63,0x7b,0x2d,0x0,0xac,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x15,0x64,0x2c,0x0,0xfb,0x6d,0x2c,0x0,0xfc,0x6d,0x2c,0x0,
+0x2d,0xc,0x2d,0x0,0xfb,0x6d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,
+0x0,0x4f,0x65,0x2c,0x0,0x31,0x66,0x2c,0x0,0xea,0xc1,0x2c,0x0,0x4f,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1d,0x64,0x2c,0x0,0x82,0x65,0x2c,0x0,0x34,0x66,
+0x2c,0x0,0xa5,0x3e,0x2d,0x0,0x82,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,
+0x64,0x2c,0x0,0x5,0x65,0x2c,0x0,0x6,0x65,0x2c,0x0,0x79,0x47,0x2d,0x0,0x4,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x43,0x64,0x2c,0x0,0x7c,0x65,0x2c,0x0,
+0x38,0x66,0x2c,0x0,0xb1,0x75,0x2d,0x0,0x7c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x30,0x64,0x2c,0x0,0xe0,0x65,0x2c,0x0,0xe1,0x65,0x2c,0x0,0x48,0x45,0x2d,
+0x0,0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0xae,0x65,
+0x2c,0x0,0x2a,0x66,0x2c,0x0,0x45,0x56,0x2d,0x0,0xae,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1b,0x64,0x2c,0x0,0x0,0x66,0x2c,0x0,0x26,0x66,0x2c,0x0,0xba,
+0xcf,0x2c,0x0,0xfe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,
+0x83,0x65,0x2c,0x0,0x84,0x65,0x2c,0x0,0x55,0x3d,0x2d,0x0,0x82,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x33,0x66,0x2c,
+0x0,0x1c,0x7b,0x2d,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,
+0x2c,0x0,0x3f,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x66,0xe2,0x2c,0x0,0x3f,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,0x64,0x2c,0x0,0x90,0x65,0x2c,0x0,0x2e,
+0x66,0x2c,0x0,0x9e,0x44,0x2d,0x0,0x90,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2c,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0xaf,0x44,0x2d,0x0,
+0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,0x64,0x2c,0x0,0xa5,0x65,0x2c,
+0x0,0xa6,0x65,0x2c,0x0,0x30,0xe0,0x2c,0x0,0xa4,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x47,0x64,0x2c,0x0,0xd1,0x65,0x2c,0x0,0xd2,0x65,0x2c,0x0,0xd4,0x43,
+0x2d,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x16,0x64,0x2c,0x0,0xe1,
+0x64,0x2c,0x0,0x3d,0x66,0x2c,0x0,0x8f,0x46,0x2d,0x0,0xe1,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x20,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,
+0x52,0x3c,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,
+0x0,0x18,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0x50,0x3c,0x2d,0x0,0x18,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x33,0x66,
+0x2c,0x0,0x25,0xdc,0x2c,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x27,
+0x64,0x2c,0x0,0xa0,0x64,0x2c,0x0,0xa2,0x64,0x2c,0x0,0x5d,0x76,0x2d,0x0,0xa0,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0xd3,0x65,0x2c,0x0,
+0xd4,0x65,0x2c,0x0,0xf4,0x48,0x2d,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x38,0x64,0x2c,0x0,0xf9,0x65,0x2c,0x0,0xfa,0x65,0x2c,0x0,0x14,0x45,0x2d,
+0x0,0xf7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x18,0x64,0x2c,0x0,0xaa,0x65,
+0x2c,0x0,0x2c,0x66,0x2c,0x0,0xcc,0xc,0x2d,0x0,0xa9,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0x36,0x65,0x2c,0x0,0x34,0x66,0x2c,0x0,0xcc,
+0xd0,0x2c,0x0,0x36,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x21,0x64,0x2c,0x0,
+0x91,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x95,0x83,0x2d,0x0,0x91,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x21,0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x33,0x66,0x2c,
+0x0,0x0,0x75,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x49,0x64,
+0x2c,0x0,0xe2,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x95,0x1c,0x2d,0x0,0xe0,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xf7,0x65,0x2c,0x0,0xf8,
+0x65,0x2c,0x0,0x5e,0x76,0x2d,0x0,0xf4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2d,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x80,0x57,0x2d,0x0,
+0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0xf7,0x65,0x2c,
+0x0,0xf8,0x65,0x2c,0x0,0xff,0x3c,0x2d,0x0,0xf4,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2f,0x64,0x2c,0x0,0x86,0x65,0x2c,0x0,0x87,0x65,0x2c,0x0,0x55,0xed,
+0x2c,0x0,0x86,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0x64,0x2c,0x0,0x9f,
+0x65,0x2c,0x0,0x31,0x66,0x2c,0x0,0x91,0x47,0x2d,0x0,0x9f,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1b,0x64,0x2c,0x0,0xef,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,
+0xe2,0x48,0x2d,0x0,0xef,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,0x2a,
+0x0,0x64,0x5e,0x2c,0x0,0x49,0x6d,0x2c,0x0,0xf0,0x82,0x2d,0x0,0x64,0x5e,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x36,0x65,0x2c,0x0,0x37,0x66,
+0x2c,0x0,0x75,0x40,0x2d,0x0,0x36,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1e,
+0x64,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x30,0x66,0x2c,0x0,0x9b,0xc1,0x2c,0x0,0xb8,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3,0x64,0x2c,0x0,0xe3,0x64,0x2c,0x0,
+0x32,0x66,0x2c,0x0,0x65,0xec,0x2c,0x0,0xe3,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x37,0x64,0x2c,0x0,0xf9,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x6c,0xd,0x2d,
+0x0,0xf7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x66,0x2c,0x0,0x86,0x83,
+0x2d,0x0,0x87,0x83,0x2d,0x0,0x87,0x83,0x2d,0x0,0x86,0x83,0x2d,0x0,0x0,0x0,
+0x0,0x0,0x1,0x41,0x64,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x49,
+0x49,0x2d,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,
+0x90,0x65,0x2c,0x0,0x92,0x65,0x2c,0x0,0x86,0xdd,0x2c,0x0,0x90,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xfb,0x63,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x2e,0x66,0x2c,
+0x0,0xcb,0x48,0x2d,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,
+0x2a,0x0,0xa6,0x64,0x2c,0x0,0x2a,0x6d,0x2c,0x0,0xfe,0x82,0x2d,0x0,0xa6,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x2a,
+0x66,0x2c,0x0,0x0,0x40,0x2d,0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x25,0x64,0x2c,0x0,0xcd,0x65,0x2c,0x0,0xce,0x65,0x2c,0x0,0x72,0x83,0x2d,0x0,
+0xcd,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x31,0x65,0x2c,
+0x0,0x38,0x66,0x2c,0x0,0x31,0x76,0x2d,0x0,0x31,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x3b,0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0xe4,0xcf,
+0x2c,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0x64,0x2c,0x0,0xef,
+0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0x29,0x48,0x2d,0x0,0xef,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,
+0x7,0x48,0x2d,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x64,0x2c,
+0x0,0xc0,0x7e,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0xc,0x48,0x2d,0x0,0xbd,0x7e,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe4,0x22,0x2a,0x0,0xc4,0x5d,0x2c,0x0,0x47,0x6d,
+0x2c,0x0,0x1f,0x45,0x2d,0x0,0xc4,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3f,
+0x64,0x2c,0x0,0xf4,0x65,0x2c,0x0,0xf5,0x65,0x2c,0x0,0x7a,0xe,0x2d,0x0,0xf4,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,
+0x37,0x66,0x2c,0x0,0xd6,0x75,0x2d,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2f,0x64,0x2c,0x0,0xf0,0x64,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x81,0x3d,0x2d,
+0x0,0xf0,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0x9d,0x65,
+0x2c,0x0,0x32,0x66,0x2c,0x0,0xb4,0x7b,0x2d,0x0,0x9d,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x48,0x64,0x2c,0x0,0x45,0x65,0x2c,0x0,0x40,0x66,0x2c,0x0,0xa6,
+0x4e,0x2d,0x0,0x45,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,
+0x90,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0x6a,0x3f,0x2d,0x0,0x90,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0xa,0x5e,0x2c,0x0,0x46,0x6d,0x2c,
+0x0,0xde,0x44,0x2d,0x0,0x8,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,
+0x2c,0x0,0xe7,0x64,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x87,0x40,0x2d,0x0,0xe7,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,0x66,0x2c,0x0,0xd8,0x69,0x2c,0x0,0xf2,
+0x69,0x2c,0x0,0x94,0xe2,0x2c,0x0,0xf1,0x69,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x38,0x64,0x2c,0x0,0xf9,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0x9a,0x56,0x2d,0x0,
+0xf9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x18,0x64,0x2c,0x0,0x21,0x65,0x2c,
+0x0,0x39,0x66,0x2c,0x0,0x7d,0x3c,0x2d,0x0,0x20,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2f,0x64,0x2c,0x0,0x48,0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x7a,0x4e,
+0x2d,0x0,0x48,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0xef,
+0x65,0x2c,0x0,0xf0,0x65,0x2c,0x0,0xaa,0x7b,0x2d,0x0,0xef,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3f,0x64,0x2c,0x0,0x9c,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,
+0xf4,0x48,0x2d,0x0,0x9c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,
+0x0,0xe6,0x64,0x2c,0x0,0x3b,0x66,0x2c,0x0,0xc5,0xea,0x2c,0x0,0xe6,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0x52,0x5e,0x2c,0x0,0x4b,0x6d,
+0x2c,0x0,0x7c,0x56,0x2d,0x0,0x52,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,
+0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0x7c,0x3c,0x2d,0x0,0x94,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,0x2c,0x0,0xff,0x64,0x2c,0x0,
+0x41,0x66,0x2c,0x0,0xa9,0x56,0x2d,0x0,0xff,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1c,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0x87,0x83,0x2d,
+0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x64,0x2c,0x0,0x18,0x65,
+0x2c,0x0,0x3e,0x66,0x2c,0x0,0x73,0x3c,0x2d,0x0,0x18,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1d,0x64,0x2c,0x0,0xcc,0x65,0x2c,0x0,0xcd,0x65,0x2c,0x0,0x34,
+0x44,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,
+0x81,0x65,0x2c,0x0,0x30,0x66,0x2c,0x0,0xca,0x56,0x2d,0x0,0x81,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xff,0x63,0x2c,0x0,0xae,0x65,0x2c,0x0,0x30,0x66,0x2c,
+0x0,0x5c,0x48,0x2d,0x0,0xae,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1b,0x64,
+0x2c,0x0,0xeb,0x64,0x2c,0x0,0x3d,0x66,0x2c,0x0,0x17,0x40,0x2d,0x0,0xeb,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfa,0x63,0x2c,0x0,0xfd,0x64,0x2c,0x0,0x30,
+0x66,0x2c,0x0,0xca,0x44,0x2d,0x0,0xfd,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x34,0x66,0x2c,0x0,0x4c,0x70,0x2c,0x0,0x4d,0x70,0x2c,0x0,0xb3,0x3f,0x2d,0x0,
+0x4a,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,0x64,0x2c,0x0,0x9a,0x65,0x2c,
+0x0,0x31,0x66,0x2c,0x0,0x9e,0x46,0x2d,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1e,0x64,0x2c,0x0,0x72,0x65,0x2c,0x0,0x34,0x66,0x2c,0x0,0x7a,0x3c,
+0x2d,0x0,0x72,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1d,0x64,0x2c,0x0,0x31,
+0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,0x3d,0x4e,0x2d,0x0,0x31,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x20,0x64,0x2c,0x0,0x31,0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,
+0x62,0x42,0x2d,0x0,0x31,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x64,0x2c,
+0x0,0x9c,0x65,0x2c,0x0,0x9d,0x65,0x2c,0x0,0x1f,0xe,0x2d,0x0,0x9c,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x5f,0x64,0x2c,0x0,0xb4,0x65,0x2c,0x0,0x31,0x66,
+0x2c,0x0,0xd4,0x52,0x2d,0x0,0xb3,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x21,
+0x64,0x2c,0x0,0x36,0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,0xf8,0x3f,0x2d,0x0,0x36,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1d,0x64,0x2c,0x0,0xe1,0x64,0x2c,0x0,
+0x40,0x66,0x2c,0x0,0xa8,0x3e,0x2d,0x0,0xe1,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x20,0x64,0x2c,0x0,0x4,0x65,0x2c,0x0,0x3d,0x66,0x2c,0x0,0x7c,0xc,0x2d,
+0x0,0x4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0xd8,0x64,
+0x2c,0x0,0x45,0x66,0x2c,0x0,0x74,0x40,0x2d,0x0,0xd8,0x64,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1f,0x64,0x2c,0x0,0x90,0x65,0x2c,0x0,0x91,0x65,0x2c,0x0,0x76,
+0x76,0x2d,0x0,0x90,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x64,0x2c,0x0,
+0x73,0x65,0x2c,0x0,0x75,0x65,0x2c,0x0,0xa,0x57,0x2d,0x0,0x72,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0x59,0x65,0x2c,0x0,0x36,0x66,0x2c,
+0x0,0xd,0x46,0x2d,0x0,0x59,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,0x64,
+0x2c,0x0,0x86,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x87,0x83,0x2d,0x0,0x86,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0xd2,0x65,0x2c,0x0,0x31,
+0x66,0x2c,0x0,0xb,0x47,0x2d,0x0,0xd2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2f,0x64,0x2c,0x0,0x72,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x0,0xeb,0x2c,0x0,
+0x72,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0x7c,0x65,0x2c,
+0x0,0x35,0x66,0x2c,0x0,0x7d,0x45,0x2d,0x0,0x7c,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x36,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,0xc8,0x65,0x2c,0x0,0xfc,0x3e,
+0x2d,0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xa9,
+0x65,0x2c,0x0,0xaa,0x65,0x2c,0x0,0x3,0x49,0x2d,0x0,0xa9,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x96,0x65,0x2c,0x0,
+0xa7,0x46,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,
+0x0,0x89,0x5e,0x2c,0x0,0x46,0x6d,0x2c,0x0,0x89,0x7b,0x2d,0x0,0x89,0x5e,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1e,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x36,0x66,
+0x2c,0x0,0xc7,0x75,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,
+0x22,0x2a,0x0,0x95,0x5e,0x2c,0x0,0x39,0x6d,0x2c,0x0,0xa0,0xd,0x2d,0x0,0x93,
+0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x48,0x64,0x2c,0x0,0x8c,0x65,0x2c,0x0,
+0x8d,0x65,0x2c,0x0,0x6d,0xd5,0x2c,0x0,0x8c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2c,0x64,0x2c,0x0,0x78,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0xbd,0x56,0x2d,
+0x0,0x78,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x68,0x65,
+0x2c,0x0,0x2f,0x66,0x2c,0x0,0xf6,0x55,0x2d,0x0,0x68,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1f,0x64,0x2c,0x0,0x1a,0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x29,
+0xc,0x2d,0x0,0x1a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x64,0x2c,0x0,
+0x9a,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x87,0xd1,0x2c,0x0,0x9a,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1f,0x64,0x2c,0x0,0xfa,0x64,0x2c,0x0,0x3f,0x66,0x2c,
+0x0,0x2a,0x7c,0x2d,0x0,0xfa,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,0x64,
+0x2c,0x0,0x59,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0xef,0x82,0x2d,0x0,0x59,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x20,0x64,0x2c,0x0,0x78,0x65,0x2c,0x0,0x34,
+0x66,0x2c,0x0,0x91,0x83,0x2d,0x0,0x78,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x35,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x2a,0x54,0x2d,0x0,
+0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x64,0x2c,0x0,0x62,0x65,0x2c,
+0x0,0x64,0x65,0x2c,0x0,0x95,0x83,0x2d,0x0,0x62,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x25,0x64,0x2c,0x0,0xbd,0x65,0x2c,0x0,0xbe,0x65,0x2c,0x0,0x5d,0xeb,
+0x2c,0x0,0xbd,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0xf0,
+0x64,0x2c,0x0,0x43,0x66,0x2c,0x0,0x92,0x4e,0x2d,0x0,0xf0,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1f,0x64,0x2c,0x0,0xfa,0x64,0x2c,0x0,0x41,0x66,0x2c,0x0,
+0x0,0x83,0x2d,0x0,0xfa,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,0x64,0x2c,
+0x0,0x72,0x65,0x2c,0x0,0x35,0x66,0x2c,0x0,0xc,0x55,0x2d,0x0,0x72,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xd3,0x5d,0x2c,0x0,0x1c,0x68,
+0x2c,0x0,0xcc,0x43,0x2d,0x0,0xd3,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x46,
+0x65,0x2c,0x0,0xe4,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0xab,0x7b,0x2d,0x0,0xe4,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,0x2a,0x0,0xb6,0x5d,0x2c,0x0,
+0xfb,0x67,0x2c,0x0,0x87,0x83,0x2d,0x0,0xb6,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xde,0x64,0x2c,0x0,0x79,0x65,0x2c,0x0,0x3b,0x67,0x2c,0x0,0xb0,0xe2,0x2c,
+0x0,0x79,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xb7,0x5d,
+0x2c,0x0,0x7a,0x66,0x2c,0x0,0x94,0x51,0x2d,0x0,0xb7,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2e,0x65,0x2c,0x0,0xc5,0x65,0x2c,0x0,0x56,0x66,0x2c,0x0,0x6b,
+0xec,0x2c,0x0,0xc4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,
+0x8b,0x5d,0x2c,0x0,0x22,0x69,0x2c,0x0,0x4c,0x48,0x2d,0x0,0x8b,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xb4,0x22,0x2a,0x0,0xa3,0x5d,0x2c,0x0,0x1f,0x68,0x2c,
+0x0,0xe1,0xe,0x2d,0x0,0xa3,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,
+0x2c,0x0,0x9f,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0x73,0x3c,0x2d,0x0,0x9f,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x50,0x64,0x2c,0x0,0xbd,0x65,0x2c,0x0,0x30,
+0x66,0x2c,0x0,0xc2,0x57,0x2d,0x0,0xbd,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x42,0x65,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x8b,0x83,0x2d,0x0,
+0xdf,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,0x2a,0x0,0x5c,0x5d,0x2c,
+0x0,0x3c,0x6d,0x2c,0x0,0x4b,0x3c,0x2d,0x0,0x5c,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xb3,0x22,0x2a,0x0,0xb4,0x5d,0x2c,0x0,0x9,0x68,0x2c,0x0,0x7b,0xdd,
+0x2c,0x0,0xb2,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0x12,
+0x65,0x2c,0x0,0x38,0x66,0x2c,0x0,0x6d,0x7b,0x2d,0x0,0x12,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0x4a,0x5e,0x2c,0x0,0x75,0x66,0x2c,0x0,
+0xf,0x3f,0x2d,0x0,0x4a,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,
+0x0,0x95,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0x6,0x55,0x2d,0x0,0x95,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x32,0x64,0x2c,0x0,0xee,0x65,0x2c,0x0,0x28,0x66,
+0x2c,0x0,0x37,0xc4,0x2c,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,
+0x64,0x2c,0x0,0xdc,0x65,0x2c,0x0,0xdd,0x65,0x2c,0x0,0xe0,0x3e,0x2d,0x0,0xdb,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0xfa,0x64,0x2c,0x0,
+0xfb,0x64,0x2c,0x0,0x14,0x57,0x2d,0x0,0xfa,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xe8,0x64,0x2c,0x0,0x87,0x65,0x2c,0x0,0x38,0x67,0x2c,0x0,0x88,0x76,0x2d,
+0x0,0x87,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xf0,0x65,
+0x2c,0x0,0xf1,0x65,0x2c,0x0,0x61,0xec,0x2c,0x0,0xef,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xf9,0x65,0x2c,0x0,0xfb,0x65,0x2c,0x0,0x20,
+0x7b,0x2d,0x0,0xf9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x65,0x2c,0x0,
+0xf4,0x65,0x2c,0x0,0x49,0x66,0x2c,0x0,0xc3,0x3e,0x2d,0x0,0xf4,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0x4,0x70,0x2c,0x0,0x9,0x70,0x2c,
+0x0,0xf5,0x44,0x2d,0x0,0x5,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x49,0x64,
+0x2c,0x0,0x31,0x65,0x2c,0x0,0x42,0x66,0x2c,0x0,0xe4,0x7b,0x2d,0x0,0x31,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3d,0x64,0x2c,0x0,0x72,0x65,0x2c,0x0,0x31,
+0x66,0x2c,0x0,0x31,0x53,0x2d,0x0,0x72,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xaf,0x22,0x2a,0x0,0xbf,0x5d,0x2c,0x0,0x21,0x69,0x2c,0x0,0xb2,0xd4,0x2c,0x0,
+0xbf,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x22,0x2a,0x0,0x87,0x5e,0x2c,
+0x0,0x88,0x5e,0x2c,0x0,0xc3,0xe8,0x27,0x0,0x85,0x5e,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x30,0x64,0x2c,0x0,0x90,0x65,0x2c,0x0,0x91,0x65,0x2c,0x0,0x93,0x1c,
+0x2d,0x0,0x90,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x4e,0x64,0x2c,0x0,0x4f,
+0x65,0x2c,0x0,0x40,0x66,0x2c,0x0,0xe3,0xea,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x37,0x64,0x2c,0x0,0xae,0x65,0x2c,0x0,0xaf,0x65,0x2c,0x0,
+0xf0,0x82,0x2d,0x0,0xae,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,
+0x0,0xec,0x5d,0x2c,0x0,0x1d,0x68,0x2c,0x0,0x2a,0x49,0x2d,0x0,0xec,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x2e,0x66,
+0x2c,0x0,0x22,0x49,0x2d,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x40,
+0x64,0x2c,0x0,0x9f,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x69,0x45,0x2d,0x0,0x9f,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x40,0x64,0x2c,0x0,0x94,0x65,0x2c,0x0,
+0x95,0x65,0x2c,0x0,0xd1,0xc,0x2d,0x0,0x94,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x55,0x65,0x2c,0x0,0xf2,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x94,0x49,0x2d,
+0x0,0xf2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x66,0x2c,0x0,0x91,0x83,
+0x2d,0x0,0x92,0x83,0x2d,0x0,0x92,0x83,0x2d,0x0,0x91,0x83,0x2d,0x0,0x0,0x0,
+0x0,0x0,0x1,0xb2,0x22,0x2a,0x0,0xce,0x5d,0x2c,0x0,0xfc,0x67,0x2c,0x0,0x21,
+0xc1,0x2c,0x0,0xce,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,
+0x7d,0x5e,0x2c,0x0,0x3e,0x6d,0x2c,0x0,0x49,0x47,0x2d,0x0,0x7c,0x5e,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2c,0x66,0x2c,0x0,0x3c,0x70,0x2c,0x0,0x3d,0x70,0x2c,
+0x0,0x29,0x54,0x2d,0x0,0x3c,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,
+0x2c,0x0,0x95,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0xb6,0x41,0x2d,0x0,0x95,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xa7,0x5d,0x2c,0x0,0x7f,
+0x66,0x2c,0x0,0x1b,0xec,0x2c,0x0,0xa7,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2a,0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x8d,0x65,0x2c,0x0,0x12,0xe,0x2d,0x0,
+0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0x8b,0x65,0x2c,
+0x0,0x34,0x66,0x2c,0x0,0x93,0x49,0x2d,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x4c,0x64,0x2c,0x0,0xc2,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0xd7,0x75,
+0x2d,0x0,0xc2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0xaf,
+0x5d,0x2c,0x0,0x3d,0x6d,0x2c,0x0,0x7d,0x3c,0x2d,0x0,0xaf,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,0x2a,0x66,0x2c,0x0,
+0x8a,0xd2,0x2c,0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe9,0x22,0x2a,
+0x0,0xdd,0x5d,0x2c,0x0,0x3f,0x6d,0x2c,0x0,0xe5,0x46,0x2d,0x0,0xdd,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xf6,0x63,0x2c,0x0,0xaa,0x64,0x2c,0x0,0x2d,0x66,
+0x2c,0x0,0x7d,0x3c,0x2d,0x0,0xaa,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,
+0x64,0x2c,0x0,0xd6,0x65,0x2c,0x0,0xd7,0x65,0x2c,0x0,0xcd,0x43,0x2d,0x0,0xd6,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xef,0x65,0x2c,0x0,
+0xf0,0x65,0x2c,0x0,0xcf,0x40,0x2d,0x0,0xef,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x34,0x64,0x2c,0x0,0xd6,0x65,0x2c,0x0,0xd7,0x65,0x2c,0x0,0x1c,0x7b,0x2d,
+0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x64,0x2c,0x0,0xbd,0x65,
+0x2c,0x0,0x36,0x67,0x2c,0x0,0x0,0x47,0x2d,0x0,0xbd,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0x50,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x76,
+0xd6,0x2c,0x0,0x50,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,
+0xaa,0x5d,0x2c,0x0,0x29,0x69,0x2c,0x0,0x2d,0x49,0x2d,0x0,0xaa,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2d,0x65,0x2c,0x0,0xc7,0x65,0x2c,0x0,0x4b,0x66,0x2c,
+0x0,0x5e,0x47,0x2d,0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,
+0x2a,0x0,0xad,0x5d,0x2c,0x0,0x2a,0x68,0x2c,0x0,0x41,0xd,0x2d,0x0,0xad,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,0x66,0x2c,0x0,0xbe,0x7e,0x2c,0x0,0xbf,
+0x7e,0x2c,0x0,0x8a,0x76,0x2d,0x0,0xbd,0x7e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb1,0x22,0x2a,0x0,0xad,0x5d,0x2c,0x0,0x7c,0x66,0x2c,0x0,0x38,0x76,0x2d,0x0,
+0xad,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb4,0x22,0x2a,0x0,0xb6,0x5d,0x2c,
+0x0,0xfe,0x67,0x2c,0x0,0x10,0x44,0x2d,0x0,0xb6,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x3b,0x66,0x2c,0x0,0x8d,0x83,0x2d,0x0,0x94,0x83,0x2d,0x0,0x94,0x83,
+0x2d,0x0,0x8b,0x83,0x2d,0x0,0x0,0x0,0x0,0x0,0x1,0x41,0x64,0x2c,0x0,0xcc,
+0x65,0x2c,0x0,0xcd,0x65,0x2c,0x0,0x3,0x46,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x8e,0x37,0x28,0x0,0xa0,0x37,0x28,0x0,0xa2,0x37,0x28,0x0,
+0xd0,0x9d,0x26,0x0,0x1,0x3d,0x28,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,0x2a,
+0x0,0xad,0x5e,0x2c,0x0,0x48,0x6d,0x2c,0x0,0x7c,0x3c,0x2d,0x0,0xab,0x5e,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x3c,0x65,0x2c,0x0,0x36,0x66,
+0x2c,0x0,0x9b,0x54,0x2d,0x0,0x3b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,
+0x64,0x2c,0x0,0x77,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0xe3,0x7b,0x2d,0x0,0x77,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,
+0x8c,0x65,0x2c,0x0,0x46,0x46,0x2d,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x48,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x3c,0x66,0x2c,0x0,0xd7,0x45,0x2d,
+0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xe1,0x65,
+0x2c,0x0,0x29,0x66,0x2c,0x0,0xec,0x48,0x2d,0x0,0xe0,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0x77,0x5e,0x2c,0x0,0x4f,0x6d,0x2c,0x0,0xd3,
+0x7b,0x2d,0x0,0x75,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,
+0x54,0x65,0x2c,0x0,0x55,0x65,0x2c,0x0,0xa2,0x55,0x2d,0x0,0x54,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x29,0x66,0x2c,
+0x0,0x14,0x83,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfb,0x21,
+0x2a,0x0,0x4,0x65,0x2c,0x0,0x5,0x65,0x2c,0x0,0xf5,0xef,0x1e,0x0,0x2,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x64,0x2c,0x0,0xeb,0x65,0x2c,0x0,0xec,
+0x65,0x2c,0x0,0x94,0x83,0x2d,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x3b,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0xf3,0x3d,0x2d,0x0,
+0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x7e,0x65,0x2c,
+0x0,0x30,0x66,0x2c,0x0,0x73,0x56,0x2d,0x0,0x7e,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2b,0x64,0x2c,0x0,0xb4,0x65,0x2c,0x0,0xb5,0x65,0x2c,0x0,0x92,0x83,
+0x2d,0x0,0xb2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0x8e,
+0x5d,0x2c,0x0,0x20,0x69,0x2c,0x0,0xa1,0x47,0x2d,0x0,0x8e,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x2b,0x66,0x2c,0x0,0x8f,0x70,0x2c,0x0,0x95,0x70,0x2c,0x0,
+0x87,0x83,0x2d,0x0,0x8e,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe1,0x22,0x2a,
+0x0,0xad,0x5d,0x2c,0x0,0x4a,0x6d,0x2c,0x0,0x9d,0x7b,0x2d,0x0,0xad,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x65,0x2c,0x0,0xcb,0x65,0x2c,0x0,0x4a,0x66,
+0x2c,0x0,0x10,0x18,0x2d,0x0,0xcb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,
+0x64,0x2c,0x0,0xa4,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x52,0x45,0x2d,0x0,0xa4,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x60,0x65,0x2c,0x0,
+0x61,0x65,0x2c,0x0,0xbe,0xd6,0x2c,0x0,0x60,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2e,0x64,0x2c,0x0,0x1d,0x65,0x2c,0x0,0x3d,0x66,0x2c,0x0,0x86,0x83,0x2d,
+0x0,0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xf5,0x5d,
+0x2c,0x0,0x10,0x68,0x2c,0x0,0x2d,0x57,0x2d,0x0,0xf5,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x79,
+0x46,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,
+0xbb,0x65,0x2c,0x0,0xbc,0x65,0x2c,0x0,0xf3,0x82,0x2d,0x0,0xbb,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0x32,0x5e,0x2c,0x0,0x45,0x6d,0x2c,
+0x0,0x30,0x77,0x2d,0x0,0x32,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,
+0x2c,0x0,0x9a,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0xa3,0x57,0x2d,0x0,0x9a,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x97,
+0x65,0x2c,0x0,0xb4,0x7b,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x49,0x65,0x2c,0x0,0xef,0x65,0x2c,0x0,0xf0,0x65,0x2c,0x0,0xc9,0xe2,0x2c,0x0,
+0xef,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,0x2a,0x0,0x9a,0x5d,0x2c,
+0x0,0x7d,0x66,0x2c,0x0,0xa7,0x55,0x2d,0x0,0x9a,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x24,0x64,0x2c,0x0,0x7c,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x94,0x83,
+0x2d,0x0,0x7c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0x54,
+0x65,0x2c,0x0,0x30,0x66,0x2c,0x0,0x91,0xd4,0x2c,0x0,0x54,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3e,0x64,0x2c,0x0,0xd7,0x65,0x2c,0x0,0xd8,0x65,0x2c,0x0,
+0x94,0x83,0x2d,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb4,0x22,0x2a,
+0x0,0xd3,0x5d,0x2c,0x0,0xfc,0x67,0x2c,0x0,0x5,0xed,0x2c,0x0,0xd3,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0xf0,0x64,0x2c,0x0,0x3d,0x66,
+0x2c,0x0,0x30,0x56,0x2d,0x0,0xf0,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,
+0x64,0x2c,0x0,0xb3,0x65,0x2c,0x0,0x2a,0x66,0x2c,0x0,0xaf,0x76,0x2d,0x0,0xb2,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,0x2a,0x0,0x52,0x5e,0x2c,0x0,
+0x4f,0x6d,0x2c,0x0,0x4,0x47,0x2d,0x0,0x52,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x30,0x64,0x2c,0x0,0xcc,0x65,0x2c,0x0,0xcd,0x65,0x2c,0x0,0xbf,0xd0,0x2c,
+0x0,0xcb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0x69,0x5d,
+0x2c,0x0,0x4a,0x6d,0x2c,0x0,0x86,0x83,0x2d,0x0,0x69,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x2b,0x66,0x2c,0x0,0x7,
+0xdf,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,
+0xe1,0x64,0x2c,0x0,0xe2,0x64,0x2c,0x0,0x8,0xe0,0x2c,0x0,0xe1,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x36,0x66,0x2c,0x0,0xd,0x6d,0x2c,0x0,0x31,0x6d,0x2c,
+0x0,0x59,0xd,0x2d,0x0,0x30,0x6d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,
+0x2c,0x0,0xf5,0x64,0x2c,0x0,0x39,0x66,0x2c,0x0,0x10,0xec,0x2c,0x0,0xf3,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf5,0x63,0x2c,0x0,0xcd,0x64,0x2c,0x0,0x2c,
+0x66,0x2c,0x0,0xcf,0x47,0x2d,0x0,0xcd,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2f,0x64,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x75,0xc6,0x2c,0x0,
+0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0xcc,0x65,0x2c,
+0x0,0x24,0x66,0x2c,0x0,0x4d,0x56,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe4,0x56,0x2a,0x0,0xe3,0x5e,0x2c,0x0,0xe4,0x5e,0x2c,0x0,0xb4,0xee,
+0x2c,0x0,0xe0,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xe0,
+0x65,0x2c,0x0,0x30,0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,0xe0,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xef,0x64,0x2c,0x0,0xae,0x65,0x2c,0x0,0x34,0x67,0x2c,0x0,
+0x91,0x83,0x2d,0x0,0xae,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,0x64,0x2c,
+0x0,0x9f,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0xa1,0x48,0x2d,0x0,0x9f,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0x4,0x65,0x2c,0x0,0x5,0x65,
+0x2c,0x0,0x1d,0xeb,0x2c,0x0,0x4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,
+0x65,0x2c,0x0,0xdc,0x65,0x2c,0x0,0x3,0x66,0x2c,0x0,0x7f,0x52,0x2d,0x0,0xdb,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0x1d,0x65,0x2c,0x0,
+0x3e,0x66,0x2c,0x0,0x4e,0x45,0x2d,0x0,0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x32,0x64,0x2c,0x0,0xd7,0x65,0x2c,0x0,0x25,0x66,0x2c,0x0,0x10,0x46,0x2d,
+0x0,0xd7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,0x2c,0x0,0xcc,0x65,
+0x2c,0x0,0xcd,0x65,0x2c,0x0,0x98,0x40,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0xb3,0x65,0x2c,0x0,0xb4,0x65,0x2c,0x0,0x11,
+0x3e,0x2d,0x0,0xb2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x66,0x2c,0x0,
+0xbe,0x7e,0x2c,0x0,0xbf,0x7e,0x2c,0x0,0xce,0xc,0x2d,0x0,0xbd,0x7e,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x3a,0x64,0x2c,0x0,0xb9,0x65,0x2c,0x0,0x36,0x66,0x2c,
+0x0,0xa6,0x46,0x2d,0x0,0xb9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,
+0x2c,0x0,0x81,0x65,0x2c,0x0,0x82,0x65,0x2c,0x0,0x6a,0x56,0x2d,0x0,0x81,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xbd,0x65,0x2c,0x0,0xbe,
+0x65,0x2c,0x0,0x78,0x3c,0x2d,0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb7,0xc9,0x26,0x0,0xad,0x23,0x27,0x0,0xaf,0x23,0x27,0x0,0x7,0xec,0x28,0x0,
+0xf,0xec,0x28,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0xa4,0x65,0x2c,
+0x0,0x2b,0x66,0x2c,0x0,0x12,0x40,0x2d,0x0,0xa4,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xaf,0x22,0x2a,0x0,0xec,0x5d,0x2c,0x0,0x21,0x69,0x2c,0x0,0x79,0x41,
+0x2d,0x0,0xec,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,0x2c,0x0,0x9f,
+0x65,0x2c,0x0,0xa0,0x65,0x2c,0x0,0x7d,0xec,0x2c,0x0,0x9f,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3d,0x64,0x2c,0x0,0x78,0x65,0x2c,0x0,0x3c,0x66,0x2c,0x0,
+0x6,0xd4,0x2c,0x0,0x78,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3d,0x64,0x2c,
+0x0,0x9a,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0x7c,0x3c,0x2d,0x0,0x9a,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0x91,0x65,0x2c,0x0,0x92,0x65,
+0x2c,0x0,0x86,0x83,0x2d,0x0,0x90,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,
+0x64,0x2c,0x0,0x90,0x65,0x2c,0x0,0x38,0x66,0x2c,0x0,0x78,0x3c,0x2d,0x0,0x8f,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3e,0x64,0x2c,0x0,0x96,0x65,0x2c,0x0,
+0x97,0x65,0x2c,0x0,0x43,0xe0,0x2c,0x0,0x96,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x21,0x64,0x2c,0x0,0x34,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0xbf,0x48,0x2d,
+0x0,0x34,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0x94,0x65,
+0x2c,0x0,0x28,0x66,0x2c,0x0,0xe9,0x3e,0x2d,0x0,0x92,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x56,0x65,0x2c,0x0,0x5,
+0x83,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x64,0x2c,0x0,
+0xaa,0x65,0x2c,0x0,0x2a,0x66,0x2c,0x0,0x86,0x83,0x2d,0x0,0xaa,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe3,0x22,0x2a,0x0,0xd2,0x5e,0x2c,0x0,0x3e,0x6d,0x2c,
+0x0,0xd1,0xc,0x2d,0x0,0xd1,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,
+0x2c,0x0,0x9f,0x65,0x2c,0x0,0xa0,0x65,0x2c,0x0,0xf0,0x82,0x2d,0x0,0x9f,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0xd1,0x65,0x2c,0x0,0xd2,
+0x65,0x2c,0x0,0xe8,0xdf,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x32,0x64,0x2c,0x0,0x81,0x65,0x2c,0x0,0x3a,0x66,0x2c,0x0,0xa1,0xe0,0x2c,0x0,
+0x81,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,0x64,0x2c,0x0,0xea,0x65,0x2c,
+0x0,0xec,0x65,0x2c,0x0,0xff,0x55,0x2d,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2c,0x64,0x2c,0x0,0x18,0x65,0x2c,0x0,0x43,0x66,0x2c,0x0,0x7c,0x45,
+0x2d,0x0,0x18,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x43,0x65,0x2c,0x0,0xe0,
+0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0x7d,0xe0,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xd3,0x5d,0x2c,0x0,0x13,0x68,0x2c,0x0,
+0x94,0x83,0x2d,0x0,0xd3,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,0x2a,
+0x0,0xb2,0x5d,0x2c,0x0,0xfb,0x67,0x2c,0x0,0xa2,0x47,0x2d,0x0,0xb2,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xa8,0x5d,0x2c,0x0,0x8a,0x66,
+0x2c,0x0,0xbd,0x40,0x2d,0x0,0xa8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe9,
+0x64,0x2c,0x0,0x83,0x65,0x2c,0x0,0x3a,0x67,0x2c,0x0,0x67,0xc9,0x2c,0x0,0x83,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x65,0x2c,0x0,0xd2,0x65,0x2c,0x0,
+0x4a,0x66,0x2c,0x0,0x7c,0x3c,0x2d,0x0,0xcf,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xaf,0x22,0x2a,0x0,0xa2,0x5d,0x2c,0x0,0x1f,0x69,0x2c,0x0,0xdb,0x3e,0x2d,
+0x0,0xa1,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0xd0,0x5d,
+0x2c,0x0,0x10,0x68,0x2c,0x0,0xc5,0x3d,0x2d,0x0,0xce,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x16,0x65,0x2c,0x0,0xaf,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0xe8,
+0x44,0x2d,0x0,0xaf,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe4,0x64,0x2c,0x0,
+0x7b,0x65,0x2c,0x0,0x44,0x67,0x2c,0x0,0x15,0x47,0x2d,0x0,0x7b,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xb4,0x22,0x2a,0x0,0x7e,0x5e,0x2c,0x0,0xfb,0x67,0x2c,
+0x0,0xc6,0x47,0x2d,0x0,0x7c,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,
+0x2a,0x0,0x26,0x5e,0x2c,0x0,0x79,0x66,0x2c,0x0,0x11,0x18,0x2d,0x0,0x25,0x5e,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x65,0x2c,0x0,0xcc,0x65,0x2c,0x0,0x4a,
+0x66,0x2c,0x0,0x87,0x83,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb0,0x22,0x2a,0x0,0xad,0x5d,0x2c,0x0,0x2d,0x69,0x2c,0x0,0xe1,0x43,0x2d,0x0,
+0xad,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xa8,0x5d,0x2c,
+0x0,0xdb,0x67,0x2c,0x0,0x77,0x76,0x2d,0x0,0xa8,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xf7,0x64,0x2c,0x0,0xc2,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0xea,0x48,
+0x2d,0x0,0xc2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,0x2a,0x0,0x46,
+0x5e,0x2c,0x0,0x0,0x68,0x2c,0x0,0x61,0x83,0x2d,0x0,0x45,0x5e,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3d,0x64,0x2c,0x0,0xd6,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,
+0x92,0x56,0x2d,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xea,0x64,0x2c,
+0x0,0xbf,0x65,0x2c,0x0,0x37,0x67,0x2c,0x0,0xcd,0xc6,0x2c,0x0,0xbf,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,0x83,0x5d,0x2c,0x0,0x7b,0x66,
+0x2c,0x0,0x7,0x83,0x2d,0x0,0x83,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,
+0x64,0x2c,0x0,0xbd,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x52,0x3c,0x2d,0x0,0xbd,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0x86,0x65,0x2c,0x0,
+0x3c,0x66,0x2c,0x0,0xef,0x82,0x2d,0x0,0x86,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x31,0x64,0x2c,0x0,0x9f,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0x1a,0x83,0x2d,
+0x0,0x9f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0x4f,0x65,
+0x2c,0x0,0x3e,0x66,0x2c,0x0,0xb3,0xc7,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x36,0x65,0x2c,0x0,0xd7,0x65,0x2c,0x0,0x51,0x66,0x2c,0x0,0xad,
+0x7b,0x2d,0x0,0xd7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,
+0x54,0x65,0x2c,0x0,0x40,0x66,0x2c,0x0,0x85,0x7b,0x2d,0x0,0x54,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x3e,0x66,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0xc2,0x7e,0x2c,
+0x0,0x8b,0x83,0x2d,0x0,0xbd,0x7e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,
+0x2a,0x0,0xc8,0x64,0x2c,0x0,0xf3,0x68,0x2c,0x0,0x2,0x3f,0x2d,0x0,0xc8,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xfe,0x65,0x2c,0x0,0xff,
+0x65,0x2c,0x0,0x91,0x83,0x2d,0x0,0xfe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x69,0x22,0x2a,0x0,0x89,0x5e,0x2c,0x0,0xb6,0x16,0x2d,0x0,0xa8,0x3f,0x2d,0x0,
+0xe,0x6,0x2d,0x0,0x0,0x0,0x0,0x0,0x1,0xd,0x5d,0x26,0x0,0xe7,0xe9,0x27,
+0x0,0x38,0xea,0x27,0x0,0xfa,0xea,0x28,0x0,0xfe,0xea,0x28,0x0,0x0,0x0,0x0,
+0x0,0x1,0x3d,0x64,0x2c,0x0,0xcc,0x65,0x2c,0x0,0xcd,0x65,0x2c,0x0,0xf0,0x44,
+0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x45,
+0x65,0x2c,0x0,0x41,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0x45,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3e,0x64,0x2c,0x0,0xfb,0x6d,0x2c,0x0,0xfc,0x6d,0x2c,0x0,
+0x91,0x83,0x2d,0x0,0xfb,0x6d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,
+0x0,0xb1,0x65,0x2c,0x0,0xb2,0x65,0x2c,0x0,0x1,0x77,0x2d,0x0,0xb1,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0x18,0x5f,0x2c,0x0,0x12,0x68,
+0x2c,0x0,0x5f,0xe,0x2d,0x0,0x17,0x5f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x47,
+0x65,0x2c,0x0,0xe5,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0x27,0x44,0x2d,0x0,0xe4,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,
+0xc8,0x65,0x2c,0x0,0x85,0x46,0x2d,0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xfd,0x63,0x2c,0x0,0xcd,0x64,0x2c,0x0,0x36,0x66,0x2c,0x0,0x2c,0x54,0x2d,
+0x0,0xcd,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,0x2a,0x0,0xd,0x5e,
+0x2c,0x0,0xfe,0x67,0x2c,0x0,0x71,0x83,0x2d,0x0,0xd,0x5e,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xeb,0x65,0x2c,0x0,0xec,0x65,0x2c,0x0,0xf1,
+0x82,0x2d,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,
+0x68,0x65,0x2c,0x0,0x69,0x65,0x2c,0x0,0x79,0x56,0x2d,0x0,0x68,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0xdb,0x65,0x2c,0x0,0xdc,0x65,0x2c,
+0x0,0xf0,0x82,0x2d,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,
+0x2c,0x0,0xe5,0x65,0x2c,0x0,0xe6,0x65,0x2c,0x0,0x91,0x83,0x2d,0x0,0xe5,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xb6,0x5d,0x2c,0x0,0x7e,
+0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0xb6,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x3b,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0x87,0x83,0x2d,0x0,
+0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,0x64,0x2c,0x0,0xb3,0x65,0x2c,
+0x0,0xb4,0x65,0x2c,0x0,0xd6,0x3f,0x2d,0x0,0xb2,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x33,0x64,0x2c,0x0,0x91,0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,0xc7,0x47,
+0x2d,0x0,0x91,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xed,0x64,0x2c,0x0,0xb5,
+0x65,0x2c,0x0,0x35,0x67,0x2c,0x0,0x1c,0x3e,0x2d,0x0,0xb5,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x35,0x65,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x63,0x66,0x2c,0x0,
+0x5,0xc4,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,
+0x0,0x3c,0x65,0x2c,0x0,0x40,0x66,0x2c,0x0,0xdc,0x56,0x2d,0x0,0x3b,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xdd,0x5d,0x2c,0x0,0x1e,0x69,
+0x2c,0x0,0xcf,0x45,0x2d,0x0,0xdd,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,
+0x64,0x2c,0x0,0xe7,0x65,0x2c,0x0,0x30,0x66,0x2c,0x0,0xda,0xdf,0x2c,0x0,0xe5,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,0xec,0x5d,0x2c,0x0,
+0xe,0x68,0x2c,0x0,0xf8,0xc5,0x2c,0x0,0xec,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x27,0x66,0x2c,0x0,0x4a,0x70,0x2c,0x0,0x4b,0x70,0x2c,0x0,0x8e,0xea,0x2c,
+0x0,0x4a,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfc,0x63,0x2c,0x0,0x4f,0x65,
+0x2c,0x0,0x29,0x66,0x2c,0x0,0xc5,0x56,0x2d,0x0,0x4f,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xdb,0x64,0x2c,0x0,0x75,0x65,0x2c,0x0,0x39,0x67,0x2c,0x0,0xca,
+0x3d,0x2d,0x0,0x74,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3c,0x66,0x2c,0x0,
+0xe,0x6a,0x2c,0x0,0x1c,0x6a,0x2c,0x0,0x46,0x41,0x2d,0x0,0x1b,0x6a,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x2f,0x66,0x2c,
+0x0,0x6b,0x46,0x2d,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb2,0x22,
+0x2a,0x0,0xa8,0x5e,0x2c,0x0,0x72,0x66,0x2c,0x0,0x88,0xd6,0x2c,0x0,0xa8,0x5e,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0xe4,0x64,0x2c,0x0,0x3d,
+0x66,0x2c,0x0,0x35,0xd5,0x2c,0x0,0xe4,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb4,0x22,0x2a,0x0,0xa8,0x5d,0x2c,0x0,0x1,0x68,0x2c,0x0,0x48,0x44,0x2d,0x0,
+0xa8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb2,0x22,0x2a,0x0,0x95,0x5d,0x2c,
+0x0,0x22,0x69,0x2c,0x0,0x67,0x83,0x2d,0x0,0x95,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2a,0x64,0x2c,0x0,0xdc,0x64,0x2c,0x0,0x3e,0x66,0x2c,0x0,0x2c,0x46,
+0x2d,0x0,0xdc,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x48,0x66,0x2c,0x0,0x60,
+0x6c,0x2c,0x0,0x74,0x6c,0x2c,0x0,0x7d,0xea,0x2c,0x0,0x73,0x6c,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0x7c,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,
+0x2d,0xdc,0x2c,0x0,0x7c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,
+0x0,0xc1,0x5e,0x2c,0x0,0x1,0x68,0x2c,0x0,0x5a,0xea,0x2c,0x0,0xc1,0x5e,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x5a,0x65,0x2c,0x0,0xf4,0x65,0x2c,0x0,0x28,0x66,
+0x2c,0x0,0xb8,0x56,0x2d,0x0,0xf2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,
+0x64,0x2c,0x0,0x86,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0x81,0x83,0x2d,0x0,0x86,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xd7,0x64,0x2c,0x0,0x72,0x65,0x2c,0x0,
+0x45,0x67,0x2c,0x0,0x77,0x83,0x2d,0x0,0x71,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xe3,0x22,0x2a,0x0,0xb1,0x5e,0x2c,0x0,0x48,0x6d,0x2c,0x0,0x76,0xc7,0x2c,
+0x0,0xaf,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xda,0x5d,
+0x2c,0x0,0x89,0x66,0x2c,0x0,0xd0,0x44,0x2d,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xb5,0x22,0x2a,0x0,0xd1,0x5d,0x2c,0x0,0xb,0x68,0x2c,0x0,0x55,
+0xdf,0x2c,0x0,0xce,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,
+0xd3,0x5d,0x2c,0x0,0x1d,0x69,0x2c,0x0,0xbe,0x47,0x2d,0x0,0xd3,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe2,0x63,0x2c,0x0,0x8c,0x64,0x2c,0x0,0x23,0x66,0x2c,
+0x0,0x66,0x83,0x2d,0x0,0x8c,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,
+0x2c,0x0,0x85,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x24,0x1c,0x2d,0x0,0x85,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x63,0x2c,0x0,0xaf,0x64,0x2c,0x0,0x1f,
+0x66,0x2c,0x0,0x56,0xed,0x2c,0x0,0xaf,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe3,0x63,0x2c,0x0,0x64,0x65,0x2c,0x0,0x14,0x66,0x2c,0x0,0x7b,0x45,0x2d,0x0,
+0x64,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x63,0x2c,0x0,0xa0,0x64,0x2c,
+0x0,0x20,0x66,0x2c,0x0,0xe1,0x46,0x2d,0x0,0xa0,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xef,0x64,0x2c,0x0,0xdd,0x65,0x2c,0x0,0x50,0x66,0x2c,0x0,0x39,0xcf,
+0x2c,0x0,0xdd,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xdb,
+0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x1a,0xe0,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xb,0x68,0x2c,0x0,0xd0,0xc,0x2d,0x0,0xd1,0xc,0x2d,0x0,
+0x52,0x3c,0x2d,0x0,0xce,0xc,0x2d,0x0,0x0,0x0,0x0,0x0,0x1,0x42,0x29,0x2a,
+0x0,0xa0,0x5e,0x2c,0x0,0xa1,0x5e,0x2c,0x0,0xa1,0x5e,0x2c,0x0,0xbf,0x5e,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x4a,0x29,0x2a,0x0,0xe2,0x5e,0x2c,0x0,0xe3,0x5e,
+0x2c,0x0,0xe3,0x5e,0x2c,0x0,0xff,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x53,
+0x29,0x2a,0x0,0xdc,0x5e,0x2c,0x0,0xdd,0x5e,0x2c,0x0,0xde,0x5e,0x2c,0x0,0xe6,
+0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,
+0x35,0x66,0x2c,0x0,0x86,0x48,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x34,0x64,0x2c,0x0,0x81,0x65,0x2c,0x0,0x82,0x65,0x2c,0x0,0xea,0x46,0x2d,
+0x0,0x81,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5a,0x29,0x2a,0x0,0x10,0x5f,
+0x2c,0x0,0x11,0x5f,0x2c,0x0,0x11,0x5f,0x2c,0x0,0x1d,0x5f,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0x68,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0xf9,
+0x46,0x2d,0x0,0x68,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5e,0x29,0x2a,0x0,
+0x8,0x60,0x2c,0x0,0x9,0x60,0x2c,0x0,0xd0,0xed,0x2c,0x0,0x8,0x60,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x3e,0x64,0x2c,0x0,0xcc,0x65,0x2c,0x0,0xce,0x65,0x2c,
+0x0,0xf,0xdf,0x2c,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x64,
+0x2c,0x0,0xc1,0x65,0x2c,0x0,0xc2,0x65,0x2c,0x0,0x6c,0x83,0x2d,0x0,0xc0,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x32,
+0x66,0x2c,0x0,0x8a,0x4e,0x2d,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x6d,0x29,0x2a,0x0,0x3a,0x5f,0x2c,0x0,0x3b,0x5f,0x2c,0x0,0x3b,0x5f,0x2c,0x0,
+0x43,0x5f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7e,0x29,0x2a,0x0,0x27,0x5f,0x2c,
+0x0,0x28,0x5f,0x2c,0x0,0x28,0x5f,0x2c,0x0,0x3d,0x5f,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x27,0x64,0x2c,0x0,0xe1,0x64,0x2c,0x0,0x3e,0x66,0x2c,0x0,0x64,0x3e,
+0x2d,0x0,0xe1,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x22,0x2a,0x0,0xda,
+0x64,0x2c,0x0,0x29,0x6d,0x2c,0x0,0x5e,0x3e,0x2d,0x0,0xda,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x88,0x29,0x2a,0x0,0x13,0x5f,0x2c,0x0,0x14,0x5f,0x2c,0x0,
+0x9,0xee,0x2c,0x0,0x13,0x5f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe2,0x22,0x2a,
+0x0,0xfb,0x6d,0x2c,0x0,0xfc,0x6d,0x2c,0x0,0xbf,0x7e,0x2c,0x0,0xfb,0x6d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x8e,0x29,0x2a,0x0,0x49,0x5f,0x2c,0x0,0x4a,0x5f,
+0x2c,0x0,0x3,0xf0,0x2c,0x0,0x48,0x5f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,
+0x64,0x2c,0x0,0x36,0x65,0x2c,0x0,0x38,0x66,0x2c,0x0,0x86,0x83,0x2d,0x0,0x36,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x93,0x29,0x2a,0x0,0x13,0x5f,0x2c,0x0,
+0x14,0x5f,0x2c,0x0,0x61,0xef,0x2c,0x0,0x13,0x5f,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x38,0x64,0x2c,0x0,0x5e,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x78,0x56,0x2d,
+0x0,0x5e,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0x7b,0x65,
+0x2c,0x0,0x31,0x66,0x2c,0x0,0xd9,0xe9,0x2c,0x0,0x7b,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x67,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x9e,0x29,0x2a,0x0,0xb2,
+0xef,0x2c,0x0,0xa,0x29,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,
+0xa4,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0xa4,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x22,0x65,0x2c,0x0,0x40,0x66,0x2c,
+0x0,0x4d,0x46,0x2d,0x0,0x22,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,
+0x2c,0x0,0xd1,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x51,0xe0,0x2c,0x0,0xd1,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0xa5,
+0x29,0x2a,0x0,0x8a,0xef,0x2c,0x0,0xa,0x29,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,
+0x66,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0xab,0x29,0x2a,0x0,0x2c,0xf0,0x2c,0x0,
+0xa,0x29,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,0x2c,0x0,0x98,0x65,0x2c,
+0x0,0x99,0x65,0x2c,0x0,0x7a,0x3c,0x2d,0x0,0x97,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x27,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x68,0x41,
+0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,0x2c,0x0,0x62,
+0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0xf9,0x47,0x2d,0x0,0x62,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0xb3,0x29,0x2a,0x0,
+0xf5,0xef,0x2c,0x0,0xa,0x29,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,
+0x0,0x42,0x1f,0x2a,0x0,0xb7,0x29,0x2a,0x0,0x1,0xed,0x2c,0x0,0xa,0x29,0x2a,
+0x0,0x0,0x0,0x0,0x0,0x1,0x68,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0xc0,0x29,
+0x2a,0x0,0xe5,0xef,0x2c,0x0,0xa,0x29,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x34,
+0x64,0x2c,0x0,0xe8,0x6f,0x2c,0x0,0xf5,0x6f,0x2c,0x0,0xf7,0x6f,0x2c,0x0,0xe9,
+0x6f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe2,0x22,0x2a,0x0,0x9e,0x64,0x2c,0x0,
+0x30,0x6d,0x2c,0x0,0x94,0x83,0x2d,0x0,0x9e,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2a,0x66,0x2c,0x0,0x2a,0x70,0x2c,0x0,0x30,0x70,0x2c,0x0,0xd,0x48,0x2d,
+0x0,0x2b,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x66,0x2c,0x0,0x38,0x67,
+0x2c,0x0,0x58,0x67,0x2c,0x0,0x16,0x51,0x2d,0x0,0x57,0x67,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xf5,0x22,0x2a,0x0,0xaa,0x64,0x2c,0x0,0xaa,0x66,0x2c,0x0,0x8,
+0x3d,0x2d,0x0,0xaa,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xc6,0x29,0x2a,0x0,
+0xd9,0x63,0x2c,0x0,0xda,0x63,0x2c,0x0,0xb7,0xee,0x2c,0x0,0xd7,0x63,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x0,0x64,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x2e,0x66,0x2c,
+0x0,0xd1,0xdd,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xcc,0x29,
+0x2a,0x0,0x63,0x5e,0x2c,0x0,0x64,0x5e,0x2c,0x0,0xc2,0xef,0x2c,0x0,0x60,0x5e,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xa9,0x65,0x2c,0x0,0x29,
+0x66,0x2c,0x0,0xb3,0x75,0x2d,0x0,0xa9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x36,0x64,0x2c,0x0,0x13,0x65,0x2c,0x0,0x43,0x66,0x2c,0x0,0x33,0xdf,0x2c,0x0,
+0x12,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xd3,0x29,0x2a,0x0,0x58,0x5e,0x2c,
+0x0,0x59,0x5e,0x2c,0x0,0xd,0xee,0x2c,0x0,0x56,0x5e,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0xd7,0x29,0x2a,0x0,0x42,0xee,
+0x2c,0x0,0xa,0x29,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xec,0x22,0x2a,0x0,0x9a,
+0x65,0x2c,0x0,0xa8,0x66,0x2c,0x0,0x32,0xd5,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xe3,0x63,0x2c,0x0,0xa0,0x64,0x2c,0x0,0x24,0x66,0x2c,0x0,
+0xf7,0xd2,0x2c,0x0,0xa0,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,
+0x0,0xd6,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x68,0x7b,0x2d,0x0,0xd6,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0x7a,0x65,0x2c,0x0,0xa0,0x65,
+0x2c,0x0,0xed,0x48,0x2d,0x0,0x7e,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xeb,
+0x56,0x2a,0x0,0x1c,0x5e,0x2c,0x0,0x1d,0x5e,0x2c,0x0,0x23,0xe8,0x2c,0x0,0x1b,
+0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xd1,0x65,0x2c,0x0,
+0x29,0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x31,0x64,0x2c,0x0,0xae,0x65,0x2c,0x0,0xaf,0x65,0x2c,0x0,0xb2,0xd4,0x2c,
+0x0,0xab,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf3,0x56,0x2a,0x0,0x1c,0x5e,
+0x2c,0x0,0x1d,0x5e,0x2c,0x0,0xca,0xeb,0x2c,0x0,0x1b,0x5e,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x4b,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0x7c,
+0x3c,0x2d,0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x63,0x19,0x2a,0x0,
+0x42,0x1f,0x2a,0x0,0x21,0x57,0x2a,0x0,0xe1,0xef,0x2c,0x0,0xb4,0x56,0x2a,0x0,
+0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x21,0x57,0x2a,
+0x0,0xff,0xed,0x2c,0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,
+0x2c,0x0,0xa9,0x65,0x2c,0x0,0xaa,0x65,0x2c,0x0,0x17,0x51,0x2d,0x0,0xa9,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x21,
+0x57,0x2a,0x0,0xfb,0xee,0x2c,0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,
+0x21,0x57,0x2a,0x0,0xd2,0xe1,0x2a,0x0,0xd4,0xe1,0x2a,0x0,0x16,0xee,0x2c,0x0,
+0xd2,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xfc,0x6d,0x2c,
+0x0,0xfd,0x6d,0x2c,0x0,0x48,0xd,0x2d,0x0,0xfb,0x6d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x38,0x64,0x2c,0x0,0xf2,0x65,0x2c,0x0,0xf3,0x65,0x2c,0x0,0x1c,0x45,
+0x2d,0x0,0xef,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,0x42,
+0x1f,0x2a,0x0,0x21,0x57,0x2a,0x0,0xee,0xd3,0x2c,0x0,0xb4,0x56,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x1,0xf5,0x22,0x2a,0x0,0xfe,0x6d,0x2c,0x0,0xff,0x6d,0x2c,0x0,
+0x87,0x83,0x2d,0x0,0xfb,0x6d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,
+0x0,0x42,0x1f,0x2a,0x0,0x21,0x57,0x2a,0x0,0xcb,0xee,0x2c,0x0,0xb4,0x56,0x2a,
+0x0,0x0,0x0,0x0,0x0,0x1,0xf8,0x63,0x2c,0x0,0xd2,0x64,0x2c,0x0,0x2d,0x66,
+0x2c,0x0,0x1e,0xd1,0x2c,0x0,0xd2,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x61,
+0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x21,0x57,0x2a,0x0,0x58,0xef,0x2c,0x0,0xb4,
+0x56,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,
+0x2f,0x57,0x2a,0x0,0x77,0xef,0x2c,0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,0x0,
+0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x36,0x57,0x2a,0x0,0xbc,0xef,0x2c,
+0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x27,0x65,
+0x2c,0x0,0x39,0x66,0x2c,0x0,0x7d,0xc,0x2d,0x0,0x27,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x3e,0x57,0x2a,0x0,0x17,
+0xed,0x2c,0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,
+0x42,0x1f,0x2a,0x0,0x45,0x57,0x2a,0x0,0x83,0xef,0x2c,0x0,0xb4,0x56,0x2a,0x0,
+0x0,0x0,0x0,0x0,0x1,0x48,0x57,0x2a,0x0,0xf3,0x5d,0x2c,0x0,0xf6,0x5d,0x2c,
+0x0,0x46,0xee,0x2c,0x0,0xf0,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,
+0x2c,0x0,0x22,0x65,0x2c,0x0,0x23,0x65,0x2c,0x0,0xa2,0x41,0x2d,0x0,0x22,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5a,0x65,0x2c,0x0,0x1,0x66,0x2c,0x0,0xe,
+0x66,0x2c,0x0,0xf5,0x3d,0x2d,0x0,0xfe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x59,0x57,0x2a,0x0,0x3c,0x5e,0x2c,0x0,0x3d,0x5e,0x2c,0x0,0xf3,0xef,0x2c,0x0,
+0x3b,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,
+0x0,0x68,0x57,0x2a,0x0,0x82,0xee,0x2c,0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x7b,0x57,0x2a,0x0,0x8,0xf0,
+0x2c,0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0x71,
+0x65,0x2c,0x0,0x30,0x66,0x2c,0x0,0x7c,0x3c,0x2d,0x0,0x71,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0xb0,0x65,0x2c,0x0,0xb1,0x65,0x2c,0x0,
+0x72,0x19,0x2d,0x0,0xaf,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,
+0x0,0x81,0x65,0x2c,0x0,0x82,0x65,0x2c,0x0,0xab,0x46,0x2d,0x0,0x81,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x84,0x57,0x2a,0x0,0xaf,0x58,0x2a,0x0,0xb4,0x58,
+0x2a,0x0,0xe9,0xef,0x2c,0x0,0xb0,0x58,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xfd,
+0x63,0x2c,0x0,0xbe,0x64,0x2c,0x0,0x2c,0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,0xbe,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,
+0x35,0x66,0x2c,0x0,0xcf,0xd0,0x2c,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x88,0x57,0x2a,0x0,0x9c,0xef,0x2c,
+0x0,0xb4,0x56,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,
+0x2a,0x0,0xc0,0x6c,0x2a,0x0,0xc,0xf0,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,
+0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0xc8,0x6c,0x2a,0x0,0x3e,
+0xed,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,
+0x64,0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,0x7c,0x3c,0x2d,0x0,0x64,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x42,0x64,0x2c,0x0,0xb4,0x64,0x2c,0x0,0xb5,0x64,0x2c,
+0x0,0x67,0xe2,0x2c,0x0,0xb3,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,
+0x2c,0x0,0x95,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0xdc,0x44,0x2d,0x0,0x95,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf4,0x22,0x2a,0x0,0x63,0x64,0x2c,0x0,0xaf,
+0x66,0x2c,0x0,0x21,0x3d,0x2d,0x0,0x63,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x61,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0xd1,0x6c,0x2a,0x0,0xe7,0xef,0x2c,0x0,
+0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0xb5,0x65,0x2c,
+0x0,0xb6,0x65,0x2c,0x0,0x96,0x45,0x2d,0x0,0xb5,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x30,0x64,0x2c,0x0,0x64,0x65,0x2c,0x0,0x31,0x66,0x2c,0x0,0x76,0x7b,
+0x2d,0x0,0x64,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,
+0x1f,0x2a,0x0,0xda,0x6c,0x2a,0x0,0x41,0xef,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x1,0x32,0x64,0x2c,0x0,0xb4,0x65,0x2c,0x0,0x2a,0x66,0x2c,0x0,
+0xc,0xea,0x2c,0x0,0xb4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xff,0x21,0x2a,
+0x0,0x8a,0x5c,0x2b,0x0,0xff,0x67,0x2b,0x0,0x2b,0x5f,0x2b,0x0,0x30,0x68,0x2b,
+0x0,0x0,0x0,0x0,0x0,0x1,0xf1,0x22,0x2a,0x0,0x2b,0x65,0x2c,0x0,0xa3,0x66,
+0x2c,0x0,0xd4,0x40,0x2d,0x0,0x2a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x94,
+0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x6a,0xf6,0x2b,0x0,0xcd,0xee,0x2c,0x0,0x4b,
+0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,
+0xe8,0x6c,0x2a,0x0,0xbd,0xe8,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,
+0x1,0x27,0x64,0x2c,0x0,0x13,0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,0x26,0x1c,0x2d,
+0x0,0x13,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0xaa,0x65,
+0x2c,0x0,0xac,0x65,0x2c,0x0,0xc4,0x52,0x2d,0x0,0xa9,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0xcc,0xe1,0x2a,0x0,0x6f,0xf6,0x2b,0x0,0xbb,
+0xef,0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x2,0x6d,0x2a,0x0,
+0xd3,0xe1,0x2a,0x0,0xd4,0xe1,0x2a,0x0,0x16,0xcf,0x2c,0x0,0xd2,0xe1,0x2a,0x0,
+0x0,0x0,0x0,0x0,0x1,0x24,0x64,0x2c,0x0,0xc3,0x65,0x2c,0x0,0xc4,0x65,0x2c,
+0x0,0x1c,0x56,0x2d,0x0,0xc0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,
+0x2c,0x0,0x92,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0x87,0x83,0x2d,0x0,0x92,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xd9,0xb0,0x2a,0x0,0xfe,0xe1,0x2a,0x0,0xe9,
+0xf9,0x2a,0x0,0xed,0xee,0x2c,0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,
+0x36,0x64,0x2c,0x0,0x81,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0xba,0x7b,0x2d,0x0,
+0x81,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,0x64,0x2c,0x0,0xbb,0x65,0x2c,
+0x0,0x2e,0x66,0x2c,0x0,0x91,0x4e,0x2d,0x0,0xbb,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x78,0x1f,0x2a,0x0,0xc8,0xe1,0x2a,0x0,0x78,0xf6,0x2b,0x0,0xa4,0xee,
+0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xe,
+0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x3b,0x40,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xa1,0x66,0x2c,0x0,0xbf,0x7e,0x2c,0x0,0xc0,0x7e,0x2c,0x0,
+0x63,0x1c,0x2d,0x0,0xbd,0x7e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,
+0x0,0x1e,0xa7,0x2a,0x0,0x92,0xe1,0x2a,0x0,0x1e,0xee,0x2c,0x0,0x70,0xe1,0x2a,
+0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x8,0x6d,
+0x2a,0x0,0x85,0xef,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,
+0x64,0x2c,0x0,0xaa,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x93,0x47,0x2d,0x0,0xa9,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x42,0x64,0x2c,0x0,0xa4,0x65,0x2c,0x0,
+0x33,0x66,0x2c,0x0,0x7a,0xc2,0x2c,0x0,0xa4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x60,0x1f,0x2a,0x0,0xb6,0xe1,0x2a,0x0,0xf1,0xf9,0x2a,0x0,0x42,0xee,0x2c,
+0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0x86,0x65,
+0x2c,0x0,0x2f,0x66,0x2c,0x0,0x7a,0x3c,0x2d,0x0,0x86,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x7b,0x1f,0x2a,0x0,0xea,0x94,0x2a,0x0,0x81,0xf6,0x2b,0x0,0xf2,
+0xee,0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,
+0x9c,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0xdb,0xc1,0x2c,0x0,0x9c,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0x19,0xa7,0x2a,0x0,0xa0,0xe1,0x2a,
+0x0,0xb9,0xef,0x2c,0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,
+0x2a,0x0,0x42,0x1f,0x2a,0x0,0x11,0x6d,0x2a,0x0,0x4b,0xef,0x2c,0x0,0x96,0x6c,
+0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0x13,0x65,0x2c,0x0,0x36,
+0x66,0x2c,0x0,0x20,0xe,0x2d,0x0,0x13,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x72,0x62,0x2c,0x0,0xf2,0x64,0x2c,0x0,0xa8,0x66,0x2c,0x0,0x73,0x3c,0x2d,0x0,
+0xf2,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0x44,0x65,0x2c,
+0x0,0x32,0x66,0x2c,0x0,0xfa,0x51,0x2d,0x0,0x44,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x81,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0xfb,0xf9,0x2a,0x0,0x50,0xeb,
+0x2c,0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xcc,
+0x65,0x2c,0x0,0x2a,0x66,0x2c,0x0,0x8b,0x56,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xc,0xa7,0x2a,0x0,0xaf,0xe1,0x2a,0x0,
+0x24,0xef,0x2c,0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,
+0x0,0xc2,0xe1,0x2a,0x0,0x84,0xf6,0x2b,0x0,0xd7,0xef,0x2c,0x0,0x4b,0xf6,0x2b,
+0x0,0x0,0x0,0x0,0x0,0x1,0x81,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x4,0xfa,
+0x2a,0x0,0x26,0xee,0x2c,0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x62,
+0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x16,0x6d,0x2a,0x0,0x3e,0xef,0x2c,0x0,0x96,
+0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0xb5,0x65,0x2c,0x0,
+0x2b,0x66,0x2c,0x0,0xd1,0xc,0x2d,0x0,0xb4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2f,0x64,0x2c,0x0,0x9f,0x65,0x2c,0x0,0xa0,0x65,0x2c,0x0,0x7c,0x3c,0x2d,
+0x0,0x9f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0x77,0xb2,
+0x2a,0x0,0xc1,0xe1,0x2a,0x0,0x1,0xee,0x2c,0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0x9,0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x42,
+0x56,0x2d,0x0,0x9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,
+0xd3,0xe1,0x2a,0x0,0x94,0xf6,0x2b,0x0,0x14,0xe2,0x2c,0x0,0x4b,0xf6,0x2b,0x0,
+0x0,0x0,0x0,0x0,0x1,0xd,0x64,0x2c,0x0,0x1a,0x65,0x2c,0x0,0x3d,0x66,0x2c,
+0x0,0xd3,0x1b,0x2d,0x0,0x1a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x60,0x1f,
+0x2a,0x0,0x98,0xb0,0x2a,0x0,0x7,0xfa,0x2a,0x0,0x9b,0xee,0x2c,0x0,0xc1,0xf9,
+0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0xfb,0x64,0x2c,0x0,0x45,
+0x66,0x2c,0x0,0xfd,0x56,0x2d,0x0,0xfb,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x27,0x66,0x2c,0x0,0xba,0x6f,0x2c,0x0,0xbe,0x6f,0x2c,0x0,0x5,0x49,0x2d,0x0,
+0xbb,0x6f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x63,0x19,0x2a,0x0,0x42,0x1f,0x2a,
+0x0,0x19,0x6d,0x2a,0x0,0x78,0xed,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x1,0x95,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x9c,0xf6,0x2b,0x0,0xb9,0xee,
+0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0x72,
+0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0xde,0x7b,0x2d,0x0,0x72,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0x3d,0x65,0x2c,0x0,0x3e,0x65,0x2c,0x0,
+0x20,0x47,0x2d,0x0,0x3b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xed,0x22,0x2a,
+0x0,0xaa,0x64,0x2c,0x0,0xac,0x66,0x2c,0x0,0xbf,0x7e,0x2c,0x0,0xaa,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xf0,0x22,0x2a,0x0,0x5,0x65,0x2c,0x0,0xaa,0x66,
+0x2c,0x0,0x73,0x3c,0x2d,0x0,0x5,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x81,
+0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x14,0xfa,0x2a,0x0,0xa7,0xee,0x2c,0x0,0xc1,
+0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0xeb,0x65,0x2c,0x0,
+0x28,0x66,0x2c,0x0,0x81,0x57,0x2d,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x61,0x1f,0x2a,0x0,0xc,0xa7,0x2a,0x0,0xd1,0xe1,0x2a,0x0,0xe0,0xef,0x2c,
+0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0x72,0x65,
+0x2c,0x0,0x2e,0x66,0x2c,0x0,0xf0,0x82,0x2d,0x0,0x72,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x4b,
+0x3f,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x73,0x62,0x2c,0x0,
+0xe1,0x64,0x2c,0x0,0xa6,0x66,0x2c,0x0,0x58,0xeb,0x2c,0x0,0xe1,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x29,0x6d,0x2a,
+0x0,0x2f,0xef,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x64,
+0x2c,0x0,0x86,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0x4e,0x57,0x2d,0x0,0x86,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x94,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x6a,
+0x26,0x2c,0x0,0x86,0xef,0x2c,0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x34,0x64,0x2c,0x0,0x59,0x65,0x2c,0x0,0x38,0x66,0x2c,0x0,0x8e,0xd5,0x2c,0x0,
+0x59,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x79,0x1f,0x2a,0x0,0x8,0xe2,0x2a,
+0x0,0xa4,0xf6,0x2b,0x0,0x52,0xe1,0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,
+0x0,0x1,0x30,0x64,0x2c,0x0,0x40,0x65,0x2c,0x0,0x3e,0x66,0x2c,0x0,0x7a,0x3c,
+0x2d,0x0,0x40,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0x63,
+0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x9f,0xc,0x2d,0x0,0x63,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x82,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x1a,0xfa,0x2a,0x0,
+0xfe,0xef,0x2c,0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,
+0x0,0xd,0xa7,0x2a,0x0,0xf9,0xe1,0x2a,0x0,0x4e,0xee,0x2c,0x0,0x70,0xe1,0x2a,
+0x0,0x0,0x0,0x0,0x0,0x1,0x45,0x64,0x2c,0x0,0x1d,0x65,0x2c,0x0,0x1e,0x65,
+0x2c,0x0,0xaa,0x1c,0x2d,0x0,0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x43,
+0x64,0x2c,0x0,0xc7,0x6f,0x2c,0x0,0xd0,0x6f,0x2c,0x0,0x78,0x46,0x2d,0x0,0xc8,
+0x6f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0xe,0x65,0x2c,0x0,
+0x3a,0x66,0x2c,0x0,0x86,0x83,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x79,0x1f,0x2a,0x0,0x9,0xe2,0x2a,0x0,0x6e,0x26,0x2c,0x0,0xc0,0xee,0x2c,
+0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0xac,0x65,
+0x2c,0x0,0xad,0x65,0x2c,0x0,0xc1,0x3d,0x2d,0x0,0xab,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0xe0,0x65,0x2c,0x0,0xe1,0x65,0x2c,0x0,0x4d,
+0x57,0x2d,0x0,0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0x64,0x2c,0x0,
+0x22,0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x11,0x44,0x2d,0x0,0x22,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xee,0x22,0x2a,0x0,0xc2,0x65,0x2c,0x0,0xa7,0x66,0x2c,
+0x0,0xac,0x3e,0x2d,0x0,0xc2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,
+0x2c,0x0,0x4f,0x65,0x2c,0x0,0x31,0x66,0x2c,0x0,0xce,0xc5,0x2c,0x0,0x4f,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xcc,0x65,0x2c,0x0,0x2e,
+0x66,0x2c,0x0,0x73,0x7b,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x35,0x64,0x2c,0x0,0xaa,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0x42,0x40,0x2d,0x0,
+0xa9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0x4a,0x65,0x2c,
+0x0,0x33,0x66,0x2c,0x0,0x50,0x3c,0x2d,0x0,0x4a,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x31,0x6d,0x2a,0x0,0x33,0xee,
+0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x31,
+0x65,0x2c,0x0,0x40,0x66,0x2c,0x0,0x7d,0x3c,0x2d,0x0,0x31,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0xc5,0xe1,0x2a,0x0,0x5f,0x39,0x2c,0x0,
+0x9e,0xed,0x2c,0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x44,0x64,0x2c,
+0x0,0x87,0x64,0x2c,0x0,0x88,0x64,0x2c,0x0,0x45,0x47,0x2d,0x0,0x87,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0x4a,0x70,0x2c,0x0,0x4b,0x70,
+0x2c,0x0,0x1f,0x52,0x2d,0x0,0x4a,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,
+0x64,0x2c,0x0,0xc2,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x96,0x83,0x2d,0x0,0xc2,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,
+0x2d,0x66,0x2c,0x0,0x4f,0xd2,0x2c,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2b,0x64,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0xa7,0x44,0x2d,
+0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xef,0x22,0x2a,0x0,0xfb,0x6d,
+0x2c,0x0,0xfc,0x6d,0x2c,0x0,0x8d,0xc6,0x2c,0x0,0xfb,0x6d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0xe,0x65,0x2c,0x0,0x3d,0x66,0x2c,0x0,0x87,
+0x83,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,
+0x12,0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0x11,0x44,0x2d,0x0,0x11,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0xeb,0x65,0x2c,0x0,0x31,0x66,0x2c,
+0x0,0x91,0x83,0x2d,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3d,0x64,
+0x2c,0x0,0xfb,0x6d,0x2c,0x0,0xfc,0x6d,0x2c,0x0,0x6c,0x83,0x2d,0x0,0xfb,0x6d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xab,0x65,0x2c,0x0,0x34,
+0x66,0x2c,0x0,0x6,0x48,0x2d,0x0,0xab,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x38,0x64,0x2c,0x0,0xb9,0x65,0x2c,0x0,0xba,0x65,0x2c,0x0,0x58,0x49,0x2d,0x0,
+0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0xe2,0x65,0x2c,
+0x0,0xe3,0x65,0x2c,0x0,0x51,0xdc,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2f,0x64,0x2c,0x0,0x7,0x65,0x2c,0x0,0x48,0x66,0x2c,0x0,0xa2,0x48,
+0x2d,0x0,0x7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x95,
+0x65,0x2c,0x0,0x96,0x65,0x2c,0x0,0xaf,0xd,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xfc,0x63,0x2c,0x0,0xd7,0x64,0x2c,0x0,0x36,0x66,0x2c,0x0,
+0x7d,0x3c,0x2d,0x0,0xd7,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x93,0x19,0x2a,
+0x0,0x73,0x1f,0x2a,0x0,0xad,0xf6,0x2b,0x0,0x1,0xf0,0x2c,0x0,0x4b,0xf6,0x2b,
+0x0,0x0,0x0,0x0,0x0,0x1,0x3e,0x64,0x2c,0x0,0xdb,0x65,0x2c,0x0,0xdc,0x65,
+0x2c,0x0,0x8f,0xd6,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,
+0x64,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x20,0x49,0x2d,0x0,0xe0,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0xd1,0x65,0x2c,0x0,
+0xd2,0x65,0x2c,0x0,0x9f,0x45,0x2d,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x82,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x21,0xfa,0x2a,0x0,0xb5,0xef,0x2c,
+0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,0x0,0x12,0xe2,
+0x2a,0x0,0x72,0x26,0x2c,0x0,0x8d,0xef,0x2c,0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0xd3,0x65,0x2c,0x0,0xd4,0x65,0x2c,0x0,0xf4,
+0xeb,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,
+0xe4,0x64,0x2c,0x0,0xe5,0x64,0x2c,0x0,0xbf,0x47,0x2d,0x0,0xe4,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x37,0x64,0x2c,0x0,0xd6,0x65,0x2c,0x0,0xd7,0x65,0x2c,
+0x0,0x39,0x49,0x2d,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x40,0x64,
+0x2c,0x0,0xe2,0x64,0x2c,0x0,0xe3,0x64,0x2c,0x0,0x92,0x83,0x2d,0x0,0xe1,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0x68,0x65,0x2c,0x0,0x3e,
+0x66,0x2c,0x0,0x47,0x45,0x2d,0x0,0x68,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x3a,0x64,0x2c,0x0,0xfe,0x65,0x2c,0x0,0xff,0x65,0x2c,0x0,0x65,0x40,0x2d,0x0,
+0xfe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0xb8,0x65,0x2c,
+0x0,0x36,0x66,0x2c,0x0,0xa7,0xdc,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x3d,0x64,0x2c,0x0,0xcc,0x65,0x2c,0x0,0x32,0x66,0x2c,0x0,0x1,0xd4,
+0x2c,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0xcc,
+0x65,0x2c,0x0,0xcd,0x65,0x2c,0x0,0xd8,0xd0,0x2c,0x0,0xcb,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0x12,0x65,0x2c,0x0,0x3e,0x66,0x2c,0x0,
+0x59,0x75,0x2c,0x0,0x12,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,
+0x0,0xc,0xa7,0x2a,0x0,0xb,0xe2,0x2a,0x0,0xe,0xef,0x2c,0x0,0x70,0xe1,0x2a,
+0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x3b,0x6d,
+0x2a,0x0,0x4a,0xd1,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x3d,
+0x64,0x2c,0x0,0xcb,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0xd4,0x7b,0x2d,0x0,0xcb,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xba,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,
+0x65,0x39,0x2c,0x0,0x83,0xee,0x2c,0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x9,0x64,0x2c,0x0,0xe6,0x64,0x2c,0x0,0x34,0x66,0x2c,0x0,0x4a,0xd5,0x2c,
+0x0,0xe6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,0x64,0x2c,0x0,0x8f,0x70,
+0x2c,0x0,0x90,0x70,0x2c,0x0,0x89,0x76,0x2d,0x0,0x8d,0x70,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0x97,0x65,0x2c,0x0,0x98,0x65,0x2c,0x0,0x86,
+0x83,0x2d,0x0,0x97,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,
+0x9d,0x65,0x2c,0x0,0x9e,0x65,0x2c,0x0,0xed,0xc8,0x2c,0x0,0x9c,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x79,0x1f,0x2a,0x0,0xc0,0xe1,0x2a,0x0,0xb3,0xf6,0x2b,
+0x0,0x6d,0xeb,0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x94,0x19,
+0x2a,0x0,0x73,0x1f,0x2a,0x0,0x77,0x26,0x2c,0x0,0x49,0xec,0x2c,0x0,0xde,0x25,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0xb3,0x65,0x2c,0x0,0x35,
+0x66,0x2c,0x0,0xf0,0x82,0x2d,0x0,0xb3,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x3a,0x64,0x2c,0x0,0x90,0x65,0x2c,0x0,0x39,0x66,0x2c,0x0,0xf9,0x51,0x2d,0x0,
+0x90,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xae,0x65,0x2c,
+0x0,0x36,0x66,0x2c,0x0,0x59,0x49,0x2d,0x0,0xae,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x44,0x64,0x2c,0x0,0xbd,0x65,0x2c,0x0,0x35,0x66,0x2c,0x0,0x87,0x83,
+0x2d,0x0,0xbb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0xcc,
+0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x71,0x3c,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xea,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,
+0x72,0x83,0x2d,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,
+0x0,0x9a,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0xd,0x49,0x2d,0x0,0x9a,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xb3,0x65,0x2c,0x0,0x37,0x66,
+0x2c,0x0,0x73,0xd6,0x2c,0x0,0xb3,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5f,
+0x1f,0x2a,0x0,0x92,0xb0,0x2a,0x0,0x28,0xfa,0x2a,0x0,0x6b,0xef,0x2c,0x0,0x9c,
+0xb0,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x63,0x1f,0x2a,0x0,0xc,0xa7,0x2a,0x0,
+0x1c,0xe2,0x2a,0x0,0x60,0xeb,0x2c,0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,
+0x1,0x30,0x64,0x2c,0x0,0x27,0x65,0x2c,0x0,0x41,0x66,0x2c,0x0,0xb7,0x53,0x2d,
+0x0,0x27,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,0x64,0x2c,0x0,0x9,0x65,
+0x2c,0x0,0x45,0x66,0x2c,0x0,0xe9,0xc,0x2d,0x0,0x9,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0xd4,0x65,0x2c,0x0,0xd5,0x65,0x2c,0x0,0x16,
+0x46,0x2d,0x0,0xd1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,
+0x42,0x1f,0x2a,0x0,0x43,0x6d,0x2a,0x0,0xd2,0xb2,0x2c,0x0,0x96,0x6c,0x2a,0x0,
+0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x65,0x65,0x2c,
+0x0,0xb0,0x77,0x2c,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,
+0x2c,0x0,0x98,0x65,0x2c,0x0,0x37,0x66,0x2c,0x0,0xc6,0x44,0x2d,0x0,0x98,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7,0x64,0x2c,0x0,0x10,0x65,0x2c,0x0,0x33,
+0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,0x10,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xbb,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0x6c,0x39,0x2c,0x0,0x16,0xed,0x2c,0x0,
+0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,0x64,0x2c,0x0,0x6d,0x65,0x2c,
+0x0,0x3e,0x66,0x2c,0x0,0x2a,0x41,0x2d,0x0,0x6d,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x35,0x64,0x2c,0x0,0x7c,0x65,0x2c,0x0,0x3d,0x66,0x2c,0x0,0xe9,0x54,
+0x2d,0x0,0x7c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5,0x64,0x2c,0x0,0x9,
+0x65,0x2c,0x0,0x34,0x66,0x2c,0x0,0x40,0x3d,0x2d,0x0,0x9,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3e,0x64,0x2c,0x0,0x3b,0x65,0x2c,0x0,0x45,0x66,0x2c,0x0,
+0xf0,0x82,0x2d,0x0,0x3b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,
+0x0,0xf3,0x65,0x2c,0x0,0xf4,0x65,0x2c,0x0,0x92,0x83,0x2d,0x0,0xef,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x13,0x65,0x2c,0x0,0x46,0x66,
+0x2c,0x0,0x92,0xc9,0x2c,0x0,0x13,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3b,
+0x64,0x2c,0x0,0xe0,0x65,0x2c,0x0,0xe1,0x65,0x2c,0x0,0x15,0xdd,0x2c,0x0,0xe0,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0x68,0x65,0x2c,0x0,
+0x3f,0x66,0x2c,0x0,0x2d,0x46,0x2d,0x0,0x67,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x36,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x3e,0x66,0x2c,0x0,0x50,0x3c,0x2d,
+0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x77,0x65,
+0x2c,0x0,0x78,0x65,0x2c,0x0,0xc9,0xea,0x2c,0x0,0x77,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x41,0x64,0x2c,0x0,0xdd,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0xd1,
+0x51,0x2d,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,
+0x40,0x65,0x2c,0x0,0x42,0x66,0x2c,0x0,0x71,0x3c,0x2d,0x0,0x40,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x38,0x64,0x2c,0x0,0x18,0x65,0x2c,0x0,0x19,0x65,0x2c,
+0x0,0xac,0x47,0x2d,0x0,0x18,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,
+0x2c,0x0,0x98,0x65,0x2c,0x0,0x3b,0x66,0x2c,0x0,0xd8,0x45,0x2d,0x0,0x95,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xf5,0x65,0x2c,0x0,0xf6,
+0x65,0x2c,0x0,0xd4,0x45,0x2d,0x0,0xf4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xc3,0xf6,0x2b,0x0,0x1c,0x65,0x2c,0x0,0x1e,0x65,0x2c,0x0,0xf4,0xe7,0x2c,0x0,
+0x1a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,0xc4,0x5d,0x2c,
+0x0,0x7e,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0xc4,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe8,0x64,0x2c,0x0,0x7d,0x65,0x2c,0x0,0x41,0x67,0x2c,0x0,0x9a,0x7b,
+0x2d,0x0,0x7d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb4,0x22,0x2a,0x0,0xb6,
+0x5d,0x2c,0x0,0xf,0xbb,0x2c,0x0,0xc,0x7b,0x2d,0x0,0xe9,0xa7,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x32,0x65,0x2c,0x0,0xf7,0x65,0x2c,0x0,0x46,0x66,0x2c,0x0,
+0xf5,0x47,0x2d,0x0,0xf7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,
+0x0,0xc9,0x5d,0x2c,0x0,0x1c,0x69,0x2c,0x0,0xa0,0x48,0x2d,0x0,0xc9,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0xfa,0x2a,0x0,0x4,0x65,0x2c,0x0,0x5,0x65,
+0x2c,0x0,0xef,0xee,0x2c,0x0,0x1,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7b,
+0x26,0x2c,0x0,0x92,0x5d,0x2c,0x0,0x93,0x5d,0x2c,0x0,0x28,0xe2,0x2c,0x0,0x90,
+0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xd8,0x5d,0x2c,0x0,
+0x1b,0x68,0x2c,0x0,0x4d,0xdd,0x2c,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2f,0xe2,0x2a,0x0,0xb0,0x65,0x2c,0x0,0xb1,0x65,0x2c,0x0,0xe9,0xed,0x2c,
+0x0,0xaf,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb2,0x22,0x2a,0x0,0xf1,0x5d,
+0x2c,0x0,0x78,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0xf0,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xf3,0x64,0x2c,0x0,0xb4,0x65,0x2c,0x0,0x36,0x67,0x2c,0x0,0x92,
+0x83,0x2d,0x0,0xb4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x4a,0x6d,0x2a,0x0,
+0x3,0x5d,0x2c,0x0,0x5,0x5d,0x2c,0x0,0xa3,0xef,0x2c,0x0,0x2,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0x3e,0x3d,0x2a,0x0,0x71,0x39,0x2c,
+0x0,0x70,0xef,0x2c,0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,
+0x2a,0x0,0x6,0xe2,0x2a,0x0,0xcc,0xf6,0x2b,0x0,0x82,0xef,0x2c,0x0,0x4b,0xf6,
+0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,0x2a,0x0,0xf5,0x5d,0x2c,0x0,0x1,
+0x68,0x2c,0x0,0xa8,0x55,0x2d,0x0,0xf5,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x81,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x3a,0xfa,0x2a,0x0,0xbe,0xef,0x2c,0x0,
+0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x88,0x26,0x2c,0x0,0xfc,0x5d,0x2c,
+0x0,0xfd,0x5d,0x2c,0x0,0x84,0xef,0x2c,0x0,0xfa,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x76,0x62,0x2c,0x0,0x4a,0x65,0x2c,0x0,0x9a,0x66,0x2c,0x0,0xfe,0x3d,
+0x2d,0x0,0x4a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3e,0x65,0x2c,0x0,0xdc,
+0x65,0x2c,0x0,0xf0,0x65,0x2c,0x0,0x8c,0xd5,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x3e,0xe2,0x2a,0x0,0x30,0x64,0x2c,0x0,0x31,0x64,0x2c,0x0,
+0x5f,0xec,0x2c,0x0,0x2e,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,
+0x0,0xb,0xe2,0x2a,0x0,0x80,0x39,0x2c,0x0,0xf3,0xed,0x2c,0x0,0x3e,0x39,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x58,0x6d,
+0x2a,0x0,0x91,0xef,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x93,
+0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0xd5,0xf6,0x2b,0x0,0xf5,0xdd,0x2c,0x0,0x4b,
+0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x79,0x1f,0x2a,0x0,0xb6,0xe1,0x2a,0x0,
+0x8d,0x26,0x2c,0x0,0x63,0xef,0x2c,0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xb0,0x22,0x2a,0x0,0xf0,0x5d,0x2c,0x0,0x1d,0x69,0x2c,0x0,0x91,0x83,0x2d,
+0x0,0xf0,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x1f,0x2a,0x0,0xd0,0xb0,
+0x2a,0x0,0x41,0xfa,0x2a,0x0,0xf5,0xed,0x2c,0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,
+0x0,0x0,0x1,0x63,0x1f,0x2a,0x0,0xd,0xa7,0x2a,0x0,0x50,0xe2,0x2a,0x0,0xa9,
+0xef,0x2c,0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,
+0xb5,0xe1,0x2a,0x0,0x8c,0x39,0x2c,0x0,0x23,0xef,0x2c,0x0,0x3e,0x39,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,0xdd,0x5d,0x2c,0x0,0x22,0x68,0x2c,
+0x0,0xf0,0x82,0x2d,0x0,0xdd,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x14,0x65,
+0x2c,0x0,0xab,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0xd3,0x76,0x2c,0x0,0xab,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xdd,0xf6,0x2b,0x0,0x7c,0x65,0x2c,0x0,0x7d,
+0x65,0x2c,0x0,0x36,0xec,0x2c,0x0,0x7c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb3,0x22,0x2a,0x0,0xfa,0x5d,0x2c,0x0,0xfa,0x67,0x2c,0x0,0x1c,0xd4,0x2c,0x0,
+0xfa,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x92,0x26,0x2c,0x0,0xbf,0x5d,0x2c,
+0x0,0xc0,0x5d,0x2c,0x0,0xf8,0xed,0x2c,0x0,0xbf,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xb0,0x22,0x2a,0x0,0xc5,0x5d,0x2c,0x0,0x4c,0x66,0x2c,0x0,0x9b,0xd,
+0x2d,0x0,0xc4,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x49,0xfa,0x2a,0x0,0xa3,
+0x5d,0x2c,0x0,0xa5,0x5d,0x2c,0x0,0x27,0xef,0x2c,0x0,0xa3,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xf6,0x64,0x2c,0x0,0x88,0x65,0x2c,0x0,0x3a,0x67,0x2c,0x0,
+0xf3,0x45,0x2d,0x0,0x88,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x58,0xe2,0x2a,
+0x0,0x63,0x65,0x2c,0x0,0x64,0x65,0x2c,0x0,0x9,0xef,0x2c,0x0,0x60,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xfd,0x64,0x2c,0x0,0x98,0x65,0x2c,0x0,0x51,0x66,
+0x2c,0x0,0x51,0x76,0x2d,0x0,0x98,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb1,
+0x22,0x2a,0x0,0xa7,0x5d,0x2c,0x0,0xfc,0x68,0x2c,0x0,0x14,0x49,0x2d,0x0,0xa7,
+0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0xd8,0x5d,0x2c,0x0,
+0x14,0x68,0x2c,0x0,0x8c,0x48,0x2d,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x60,0x6d,0x2a,0x0,0xbf,0x5d,0x2c,0x0,0xc1,0x5d,0x2c,0x0,0x54,0xe1,0x2c,
+0x0,0xbf,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xec,0x64,0x2c,0x0,0x81,0x65,
+0x2c,0x0,0x3a,0x67,0x2c,0x0,0x87,0x45,0x2d,0x0,0x80,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x14,0x65,0x2c,0x0,0xaa,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0x73,
+0x3c,0x2d,0x0,0xaa,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,
+0x9,0x3d,0x2a,0x0,0x92,0x39,0x2c,0x0,0x5f,0xea,0x2c,0x0,0x3e,0x39,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x94,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0xe6,0xf6,0x2b,
+0x0,0x9,0xed,0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,
+0x2a,0x0,0x46,0x5e,0x2c,0x0,0x77,0x66,0x2c,0x0,0x19,0x49,0x2d,0x0,0x45,0x5e,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x99,0x26,0x2c,0x0,0x69,0x65,0x2c,0x0,0x6a,
+0x65,0x2c,0x0,0x72,0xee,0x2c,0x0,0x69,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb2,0x22,0x2a,0x0,0xfa,0x5d,0x2c,0x0,0x5,0x68,0x2c,0x0,0x94,0x83,0x2d,0x0,
+0xfa,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x37,0x65,0x2c,0x0,0xca,0x65,0x2c,
+0x0,0x53,0x66,0x2c,0x0,0xd7,0x44,0x2d,0x0,0xca,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x60,0x1f,0x2a,0x0,0xd3,0xe1,0x2a,0x0,0x4d,0xfa,0x2a,0x0,0xcc,0xef,
+0x2c,0x0,0xc1,0xf9,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x5f,0xe2,0x2a,0x0,0xf6,
+0x5d,0x2c,0x0,0xf7,0x5d,0x2c,0x0,0xeb,0xea,0x2c,0x0,0xf6,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xc5,0x5d,0x2c,0x0,0x22,0x69,0x2c,0x0,
+0xd2,0xe0,0x2c,0x0,0xc4,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb2,0x22,0x2a,
+0x0,0xdd,0x5d,0x2c,0x0,0xa,0x68,0x2c,0x0,0x94,0x83,0x2d,0x0,0xdd,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x19,0x2a,0x0,0x42,0x1f,0x2a,0x0,0x67,0x6d,
+0x2a,0x0,0xd2,0xef,0x2c,0x0,0x96,0x6c,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x98,
+0x39,0x2c,0x0,0xa,0x65,0x2c,0x0,0xb,0x65,0x2c,0x0,0x62,0xef,0x2c,0x0,0x9,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x48,0x65,0x2c,0x0,0xe3,0x65,0x2c,0x0,
+0x27,0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,0xe3,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xef,0x64,0x2c,0x0,0x8d,0x65,0x2c,0x0,0x3b,0x67,0x2c,0x0,0x86,0x83,0x2d,
+0x0,0x8d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x56,0xdd,0x2b,0x0,0xc5,0x5d,
+0x2c,0x0,0xc6,0x5d,0x2c,0x0,0x7f,0xef,0x2c,0x0,0xc4,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0xce,0x5d,0x2c,0x0,0x75,0x66,0x2c,0x0,0xd3,
+0x3d,0x2d,0x0,0xce,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,
+0xbc,0xe1,0x2a,0x0,0xfb,0xf6,0x2b,0x0,0xa3,0xee,0x2c,0x0,0x4b,0xf6,0x2b,0x0,
+0x0,0x0,0x0,0x0,0x1,0x95,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x9b,0x26,0x2c,
+0x0,0x73,0xee,0x2c,0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,
+0x2a,0x0,0xbf,0x5d,0x2c,0x0,0xfe,0x67,0x2c,0x0,0xe9,0x46,0x2d,0x0,0xbf,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x65,0x2c,0x0,0xd8,0x65,0x2c,0x0,0x47,
+0x66,0x2c,0x0,0x52,0x3c,0x2d,0x0,0xd8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x50,0xfa,0x2a,0x0,0x1e,0x65,0x2c,0x0,0x20,0x65,0x2c,0x0,0xbb,0xb8,0x2c,0x0,
+0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0x37,0x5e,0x2c,
+0x0,0x1c,0x69,0x2c,0x0,0x58,0x1b,0x2d,0x0,0x37,0x5e,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xaf,0x22,0x2a,0x0,0xd8,0x5d,0x2c,0x0,0xda,0x67,0x2c,0x0,0x1b,0x51,
+0x2d,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe9,0x64,0x2c,0x0,0x85,
+0x65,0x2c,0x0,0x38,0x67,0x2c,0x0,0x83,0x7b,0x2d,0x0,0x85,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xc,0xa7,0x2a,0x0,0x65,0xe2,0x2a,0x0,
+0x32,0xee,0x2c,0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x19,0x2a,
+0x0,0xa6,0x1f,0x2a,0x0,0x9e,0x39,0x2c,0x0,0xe4,0xee,0x2c,0x0,0x3e,0x39,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x4e,0x65,0x2c,0x0,0xfe,0x65,0x2c,0x0,0xff,0x65,
+0x2c,0x0,0x3d,0x40,0x2d,0x0,0xfe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb4,
+0x22,0x2a,0x0,0xb6,0x5d,0x2c,0x0,0xff,0x67,0x2c,0x0,0x72,0x83,0x2d,0x0,0xb6,
+0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x70,0x6d,0x2a,0x0,0xb9,0x65,0x2c,0x0,
+0xba,0x65,0x2c,0x0,0xd8,0xc3,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x25,0x66,0x2c,0x0,0x3d,0x66,0x2c,0x0,0x5f,0x66,0x2c,0x0,0x95,0x83,0x2d,
+0x0,0x5e,0x66,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x65,0x2c,0x0,0xce,0x65,
+0x2c,0x0,0x64,0x66,0x2c,0x0,0xdd,0x55,0x2d,0x0,0xce,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x60,0xdd,0x2b,0x0,0x4c,0x65,0x2c,0x0,0x4d,0x65,0x2c,0x0,0x3,
+0xea,0x2c,0x0,0x4a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,
+0x90,0x5d,0x2c,0x0,0x29,0x69,0x2c,0x0,0xe5,0xcf,0x2c,0x0,0x90,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x8,0x26,0x2c,0x0,0x5d,0x5e,0x2c,0x0,0x5e,0x5e,0x2c,
+0x0,0xf6,0xe7,0x2c,0x0,0x5b,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,
+0x2a,0x0,0xa8,0x5d,0x2c,0x0,0x15,0x68,0x2c,0x0,0x82,0xe8,0x2c,0x0,0xa8,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5d,0xfa,0x2a,0x0,0x1c,0x5e,0x2c,0x0,0x1d,
+0x5e,0x2c,0x0,0xe9,0xec,0x2c,0x0,0x1b,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x94,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x0,0xf7,0x2b,0x0,0x78,0xef,0x2c,0x0,
+0x4b,0xf6,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x57,0x65,0x2c,0x0,0xef,0x65,0x2c,
+0x0,0x28,0x66,0x2c,0x0,0x2c,0x3c,0x2d,0x0,0xef,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xfc,0x64,0x2c,0x0,0x94,0x65,0x2c,0x0,0x37,0x67,0x2c,0x0,0x81,0xd,
+0x2d,0x0,0x94,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,0x95,
+0x5d,0x2c,0x0,0x7b,0x66,0x2c,0x0,0x4e,0xdd,0x2c,0x0,0x95,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x74,0xe2,0x2a,0x0,0xf6,0x5d,0x2c,0x0,0xf7,0x5d,0x2c,0x0,
+0x48,0xee,0x2c,0x0,0xf6,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xa3,0x39,0x2c,
+0x0,0xde,0x5d,0x2c,0x0,0xe0,0x5d,0x2c,0x0,0xc6,0xde,0x2c,0x0,0xdd,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb4,0x22,0x2a,0x0,0xb6,0x5d,0x2c,0x0,0x6,0x68,
+0x2c,0x0,0xa,0x83,0x2d,0x0,0xb6,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x44,
+0x65,0x2c,0x0,0xe5,0x65,0x2c,0x0,0x48,0x66,0x2c,0x0,0xbd,0x7e,0x2c,0x0,0xe3,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xfb,0x5d,0x2c,0x0,
+0x22,0x69,0x2c,0x0,0xdb,0xc,0x2d,0x0,0xfa,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xb1,0x22,0x2a,0x0,0xbb,0x5d,0x2c,0x0,0x16,0x68,0x2c,0x0,0x6c,0x3d,0x2d,
+0x0,0xbb,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x67,0xdd,0x2b,0x0,0x82,0x65,
+0x2c,0x0,0x83,0x65,0x2c,0x0,0x92,0xef,0x2c,0x0,0x81,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x4b,0x65,0x2c,0x0,0xe8,0x65,0x2c,0x0,0x27,0x66,0x2c,0x0,0xa4,
+0x3d,0x2d,0x0,0xe8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xdf,0x64,0x2c,0x0,
+0x7b,0x65,0x2c,0x0,0x39,0x67,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0x7b,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x42,0x65,0x2c,0x0,0xda,0x65,0x2c,0x0,0x52,0x66,0x2c,
+0x0,0x62,0x57,0x2d,0x0,0xda,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,
+0x2a,0x0,0xce,0x5d,0x2c,0x0,0x7a,0x66,0x2c,0x0,0x4,0x49,0x2d,0x0,0xce,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x6d,0x2a,0x0,0x64,0x5d,0x2c,0x0,0x66,
+0x5d,0x2c,0x0,0x5,0xef,0x2c,0x0,0x63,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb5,0x22,0x2a,0x0,0xe2,0x5d,0x2c,0x0,0x2,0x68,0x2c,0x0,0x99,0x45,0x2d,0x0,
+0xe2,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0xbb,0x5d,0x2c,
+0x0,0x25,0x69,0x2c,0x0,0x9b,0x7b,0x2d,0x0,0xbb,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x7a,0x1f,0x2a,0x0,0xff,0xe1,0x2a,0x0,0xf,0x26,0x2c,0x0,0x7a,0xee,
+0x2c,0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x95,0x19,0x2a,0x0,0x73,
+0x1f,0x2a,0x0,0x8,0xf7,0x2b,0x0,0x49,0xee,0x2c,0x0,0x4b,0xf6,0x2b,0x0,0x0,
+0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0xbb,0x5d,0x2c,0x0,0x14,0x68,0x2c,0x0,
+0x11,0x73,0x2c,0x0,0xbb,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x39,0x2c,
+0x0,0x4f,0x65,0x2c,0x0,0x50,0x65,0x2c,0x0,0x5f,0xef,0x2c,0x0,0x4f,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1a,0x65,0x2c,0x0,0xd6,0x65,0x2c,0x0,0x2f,0x66,
+0x2c,0x0,0x92,0x83,0x2d,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe4,
+0x64,0x2c,0x0,0x78,0x65,0x2c,0x0,0x48,0x67,0x2c,0x0,0x2a,0x3f,0x2d,0x0,0x78,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x70,0xdd,0x2b,0x0,0x18,0x65,0x2c,0x0,
+0x19,0x65,0x2c,0x0,0x64,0xee,0x2c,0x0,0x17,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xb0,0x22,0x2a,0x0,0xdd,0x5d,0x2c,0x0,0x7e,0x66,0x2c,0x0,0x10,0x49,0x2d,
+0x0,0xdd,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xdf,0x64,0x2c,0x0,0x71,0x65,
+0x2c,0x0,0x51,0x66,0x2c,0x0,0x9c,0xdc,0x2c,0x0,0x71,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x67,0xfa,0x2a,0x0,0xb3,0x5d,0x2c,0x0,0xb4,0x5d,0x2c,0x0,0xe3,
+0xee,0x2c,0x0,0xb3,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,
+0xa7,0x5d,0x2c,0x0,0xb8,0x67,0x2c,0x0,0x91,0x83,0x2d,0x0,0xa7,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xd8,0x5d,0x2c,0x0,0x1e,0x69,0x2c,
+0x0,0x69,0x56,0x2d,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x80,0xe2,
+0x2a,0x0,0x22,0x5e,0x2c,0x0,0x23,0x5e,0x2c,0x0,0x7e,0xef,0x2c,0x0,0x20,0x5e,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb2,0x22,0x2a,0x0,0xc0,0x5d,0x2c,0x0,0x24,
+0x68,0x2c,0x0,0xa5,0xd0,0x2c,0x0,0xc0,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xfc,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0x6a,0x76,0x2d,0x0,
+0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe2,0x64,0x2c,0x0,0x83,0x65,0x2c,
+0x0,0x3a,0x67,0x2c,0x0,0xfa,0xc,0x2d,0x0,0x81,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x16,0x26,0x2c,0x0,0x68,0x65,0x2c,0x0,0x69,0x65,0x2c,0x0,0xc0,0xef,
+0x2c,0x0,0x67,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x4f,0x66,0x2c,0x0,0xc1,
+0x7e,0x2c,0x0,0xc2,0x7e,0x2c,0x0,0x8b,0x83,0x2d,0x0,0xbe,0x7e,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xb3,0x22,0x2a,0x0,0xb7,0x5d,0x2c,0x0,0x80,0x66,0x2c,0x0,
+0xfd,0xdb,0x2c,0x0,0xb7,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb2,0x22,0x2a,
+0x0,0xc5,0x5d,0x2c,0x0,0xfa,0x67,0x2c,0x0,0x92,0x83,0x2d,0x0,0xc4,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xdd,0x5d,0x2c,0x0,0x23,0x69,
+0x2c,0x0,0x3a,0x3c,0x2d,0x0,0xdd,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x10,
+0xf7,0x2b,0x0,0xfa,0x5d,0x2c,0x0,0xfb,0x5d,0x2c,0x0,0x2b,0xef,0x2c,0x0,0xfa,
+0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x22,0x2a,0x0,0xb6,0x5d,0x2c,0x0,
+0xf,0x68,0x2c,0x0,0x94,0x83,0x2d,0x0,0xb6,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xb1,0x39,0x2c,0x0,0xc7,0x5d,0x2c,0x0,0xc8,0x5d,0x2c,0x0,0xcc,0xee,0x2c,
+0x0,0xc5,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x4a,0x65,0x2c,0x0,0xe4,0x65,
+0x2c,0x0,0x28,0x66,0x2c,0x0,0x5,0xdc,0x2c,0x0,0xe4,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,0x76,0xdd,0x2b,0x0,0xdb,
+0xe9,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x63,0x1f,0x2a,0x0,
+0xcc,0xb0,0x2a,0x0,0x6c,0xfa,0x2a,0x0,0x4d,0xeb,0x2c,0x0,0xc1,0xf9,0x2a,0x0,
+0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0x8e,0xb2,0x2a,0x0,0x1c,0x26,0x2c,
+0x0,0x9a,0xed,0x2c,0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb3,0x22,
+0x2a,0x0,0x90,0x5d,0x2c,0x0,0xf3,0x68,0x2c,0x0,0x68,0x40,0x2d,0x0,0x90,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb4,0x22,0x2a,0x0,0xeb,0x64,0x2c,0x0,0xbd,
+0x67,0x2c,0x0,0x23,0x49,0x2d,0x0,0xeb,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x85,0xe2,0x2a,0x0,0xff,0x64,0x2c,0x0,0x0,0x65,0x2c,0x0,0x7,0xef,0x2c,0x0,
+0xff,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x65,0x2c,0x0,0xd6,0x65,0x2c,
+0x0,0x52,0x66,0x2c,0x0,0x4e,0xed,0x2c,0x0,0xd5,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe2,0x64,0x2c,0x0,0xfd,0x6d,0x2c,0x0,0xfe,0x6d,0x2c,0x0,0x92,0x83,
+0x2d,0x0,0xfb,0x6d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,0x0,0xf0,
+0x5d,0x2c,0x0,0x74,0x66,0x2c,0x0,0x9a,0x55,0x2d,0x0,0xf0,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x82,0xdd,0x2b,0x0,0xe0,0x65,0x2c,0x0,0xe1,0x65,0x2c,0x0,
+0x4a,0xef,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,0x22,0x2a,
+0x0,0xed,0x5d,0x2c,0x0,0x5,0x68,0x2c,0x0,0xff,0x1b,0x2d,0x0,0xec,0x5d,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xb1,0x22,0x2a,0x0,0xbe,0x7e,0x2c,0x0,0xbf,0x7e,
+0x2c,0x0,0x79,0x83,0x2d,0x0,0xbd,0x7e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,
+0x22,0x2a,0x0,0x96,0x5d,0x2c,0x0,0x27,0x69,0x2c,0x0,0x47,0x53,0x2d,0x0,0x96,
+0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb6,0x39,0x2c,0x0,0xb2,0x5d,0x2c,0x0,
+0xb3,0x5d,0x2c,0x0,0xba,0xef,0x2c,0x0,0xb2,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x34,0x65,0x2c,0x0,0xcb,0x65,0x2c,0x0,0x4a,0x66,0x2c,0x0,0x7d,0x3c,0x2d,
+0x0,0xcb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x75,0xfa,0x2a,0x0,0x84,0x5e,
+0x2c,0x0,0x85,0x5e,0x2c,0x0,0xef,0xe9,0x2c,0x0,0x83,0x5e,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x15,0xf7,0x2b,0x0,0xc3,0x5e,0x2c,0x0,0xc4,0x5e,0x2c,0x0,0xd3,
+0xef,0x2c,0x0,0xc1,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb2,0x22,0x2a,0x0,
+0xd8,0x5d,0x2c,0x0,0x50,0x66,0x2c,0x0,0x4d,0x70,0x2c,0x0,0xd8,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x20,0x26,0x2c,0x0,0x50,0x5d,0x2c,0x0,0x51,0x5d,0x2c,
+0x0,0xc9,0xed,0x2c,0x0,0x50,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe4,0x64,
+0x2c,0x0,0x7c,0x65,0x2c,0x0,0x3b,0x67,0x2c,0x0,0x8b,0x83,0x2d,0x0,0x7c,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x8a,0xdd,0x2b,0x0,0xf,0x5e,0x2c,0x0,0x10,
+0x5e,0x2c,0x0,0xb3,0xef,0x2c,0x0,0xd,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xb4,0x22,0x2a,0x0,0xbb,0x5d,0x2c,0x0,0xfa,0x67,0x2c,0x0,0x87,0x83,0x2d,0x0,
+0xbb,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x89,0xe2,0x2a,0x0,0xb9,0x5d,0x2c,
+0x0,0xba,0x5d,0x2c,0x0,0x16,0xef,0x2c,0x0,0xb6,0x5d,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xb0,0x22,0x2a,0x0,0xa3,0x5d,0x2c,0x0,0x19,0x68,0x2c,0x0,0x9e,0xd,
+0x2d,0x0,0xa3,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbb,0x39,0x2c,0x0,0x52,
+0x5e,0x2c,0x0,0x53,0x5e,0x2c,0x0,0xea,0xef,0x2c,0x0,0x4f,0x5e,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x94,0xdd,0x2b,0x0,0x75,0x65,0x2c,0x0,0x76,0x65,0x2c,0x0,
+0x89,0xde,0x2c,0x0,0x74,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x46,0x65,0x2c,
+0x0,0xe5,0x65,0x2c,0x0,0x27,0x66,0x2c,0x0,0x84,0xd6,0x2c,0x0,0xe5,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x65,0x2c,0x0,0xef,0x65,0x2c,0x0,0x48,0x66,
+0x2c,0x0,0x27,0x1c,0x2d,0x0,0xef,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xb0,
+0x22,0x2a,0x0,0xe3,0x5d,0x2c,0x0,0x78,0x66,0x2c,0x0,0xc4,0x74,0x2c,0x0,0xe3,
+0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x77,0x62,0x2c,0x0,0xa8,0x64,0x2c,0x0,
+0x9c,0x66,0x2c,0x0,0x1c,0x46,0x2d,0x0,0xa8,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x77,0x62,0x2c,0x0,0x9f,0x64,0x2c,0x0,0x9f,0x66,0x2c,0x0,0x86,0x83,0x2d,
+0x0,0x9f,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0xd2,0x64,
+0x2c,0x0,0x9d,0x66,0x2c,0x0,0x61,0xd5,0x2c,0x0,0xd2,0x64,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0xca,0x64,0x2c,0x0,0xa2,0x66,0x2c,0x0,0xcf,
+0x18,0x2d,0x0,0xca,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,0x0,
+0x82,0x64,0x2c,0x0,0xa7,0x66,0x2c,0x0,0x91,0x83,0x2d,0x0,0x82,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x72,0x62,0x2c,0x0,0xa8,0x64,0x2c,0x0,0xb0,0x66,0x2c,
+0x0,0x27,0xc6,0x2c,0x0,0xa8,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe4,0x22,
+0x2a,0x0,0x7d,0x64,0x2c,0x0,0xac,0x66,0x2c,0x0,0x7e,0x83,0x2d,0x0,0x7d,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe2,0x22,0x2a,0x0,0xc5,0x64,0x2c,0x0,0xb4,
+0x66,0x2c,0x0,0x15,0xe1,0x2c,0x0,0xc5,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe5,0x22,0x2a,0x0,0xd6,0x64,0x2c,0x0,0xac,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,
+0xd6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe4,0x22,0x2a,0x0,0xb9,0x64,0x2c,
+0x0,0xad,0x66,0x2c,0x0,0xf,0x3d,0x2d,0x0,0xb9,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe5,0x22,0x2a,0x0,0xa9,0x64,0x2c,0x0,0xab,0x66,0x2c,0x0,0x60,0xde,
+0x2c,0x0,0xa9,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xdf,0x22,0x2a,0x0,0x5d,
+0x64,0x2c,0x0,0x19,0x6d,0x2c,0x0,0x5e,0xd6,0x2c,0x0,0x5d,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xe0,0x22,0x2a,0x0,0xe7,0x5d,0x2c,0x0,0x2e,0x6d,0x2c,0x0,
+0x7b,0x83,0x2d,0x0,0xe7,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe1,0x22,0x2a,
+0x0,0x8c,0x64,0x2c,0x0,0x17,0x6d,0x2c,0x0,0x8f,0x50,0x2d,0x0,0x8a,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe0,0x22,0x2a,0x0,0xc5,0x64,0x2c,0x0,0x16,0x6d,
+0x2c,0x0,0x8c,0x1c,0x2d,0x0,0xc5,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe0,
+0x22,0x2a,0x0,0xd8,0x64,0x2c,0x0,0x18,0x6d,0x2c,0x0,0x71,0x3c,0x2d,0x0,0xd8,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe0,0x22,0x2a,0x0,0xf5,0x5d,0x2c,0x0,
+0x2d,0x6d,0x2c,0x0,0xd,0x7b,0x2d,0x0,0xf5,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xe6,0x22,0x2a,0x0,0x13,0x65,0x2c,0x0,0x13,0x6d,0x2c,0x0,0xda,0x44,0x2d,
+0x0,0x13,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0xed,0x6e,
+0x2c,0x0,0xae,0x7f,0x2c,0x0,0x6c,0xef,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0xd3,0x5d,0x2c,0x0,0x2f,0x6d,0x2c,0x0,0xe,
+0x3d,0x2d,0x0,0xd3,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,0x2a,0x0,
+0x8b,0x64,0x2c,0x0,0x18,0x6d,0x2c,0x0,0xa6,0xd5,0x2c,0x0,0x8b,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe6,0x22,0x2a,0x0,0xd8,0x5d,0x2c,0x0,0x32,0x6d,0x2c,
+0x0,0xfa,0x46,0x2d,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,
+0x2a,0x0,0x89,0x64,0x2c,0x0,0x17,0x6d,0x2c,0x0,0x3b,0x47,0x2d,0x0,0x89,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe6,0x22,0x2a,0x0,0xe6,0x64,0x2c,0x0,0x1c,
+0x6d,0x2c,0x0,0xf8,0xd0,0x2c,0x0,0xe6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe2,0x22,0x2a,0x0,0xfa,0x5d,0x2c,0x0,0x32,0x6d,0x2c,0x0,0x73,0x3c,0x2d,0x0,
+0xfa,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x22,0x2a,0x0,0xb2,0x64,0x2c,
+0x0,0x1b,0x6d,0x2c,0x0,0x9,0xec,0x2c,0x0,0xb2,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe4,0x22,0x2a,0x0,0xd8,0x5d,0x2c,0x0,0x36,0x6d,0x2c,0x0,0xd3,0x1b,
+0x2d,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe1,0x22,0x2a,0x0,0xdd,
+0x5d,0x2c,0x0,0x30,0x6d,0x2c,0x0,0x71,0x3c,0x2d,0x0,0xdd,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xdf,0x22,0x2a,0x0,0xd2,0x64,0x2c,0x0,0x19,0x6d,0x2c,0x0,
+0x11,0xec,0x2c,0x0,0xd2,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x22,0x2a,
+0x0,0xbb,0x64,0x2c,0x0,0x1d,0x6d,0x2c,0x0,0x86,0x83,0x2d,0x0,0xbb,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0xc4,0x5d,0x2c,0x0,0x3a,0x6d,
+0x2c,0x0,0x2a,0x48,0x2d,0x0,0xc4,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe4,
+0x22,0x2a,0x0,0x3,0x65,0x2c,0x0,0x19,0x6d,0x2c,0x0,0xbf,0x3d,0x2d,0x0,0x3,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,0x0,0x9b,0x64,0x2c,0x0,
+0x1d,0x6d,0x2c,0x0,0xba,0x56,0x2d,0x0,0x9b,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xe7,0x22,0x2a,0x0,0xb3,0x64,0x2c,0x0,0x1a,0x6d,0x2c,0x0,0x92,0x83,0x2d,
+0x0,0xb3,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0xc5,0x5d,
+0x2c,0x0,0x36,0x6d,0x2c,0x0,0x3d,0x47,0x2d,0x0,0xc4,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0x78,0x64,0x2c,0x0,0x1d,0x6d,0x2c,0x0,0x6a,
+0x48,0x2d,0x0,0x78,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfb,0x64,0x2c,0x0,
+0x91,0x65,0x2c,0x0,0x6a,0x66,0x2c,0x0,0xc9,0x40,0x2d,0x0,0x91,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x65,0x2c,0x0,0x97,0x65,0x2c,0x0,0x6a,0x66,0x2c,
+0x0,0x9a,0xc7,0x2c,0x0,0x97,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfd,0x64,
+0x2c,0x0,0x9a,0x65,0x2c,0x0,0x72,0x66,0x2c,0x0,0x73,0x3c,0x2d,0x0,0x9a,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2,0x65,0x2c,0x0,0x9c,0x65,0x2c,0x0,0x6b,
+0x66,0x2c,0x0,0x8e,0xd3,0x2c,0x0,0x9c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2,0x65,0x2c,0x0,0x91,0x65,0x2c,0x0,0x6a,0x66,0x2c,0x0,0x28,0x1b,0x2d,0x0,
+0x91,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfd,0x64,0x2c,0x0,0x93,0x65,0x2c,
+0x0,0x6a,0x66,0x2c,0x0,0xc6,0xd1,0x2c,0x0,0x93,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xfc,0x64,0x2c,0x0,0x92,0x65,0x2c,0x0,0x71,0x66,0x2c,0x0,0x2,0x55,
+0x2d,0x0,0x92,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfd,0x64,0x2c,0x0,0xa0,
+0x65,0x2c,0x0,0x71,0x66,0x2c,0x0,0x8e,0xcf,0x2c,0x0,0xa0,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xfd,0x64,0x2c,0x0,0x93,0x65,0x2c,0x0,0x71,0x66,0x2c,0x0,
+0x7c,0x3c,0x2d,0x0,0x93,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfd,0x64,0x2c,
+0x0,0x8f,0x65,0x2c,0x0,0x6a,0x66,0x2c,0x0,0xc9,0x45,0x2d,0x0,0x8f,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xf8,0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x75,0x66,
+0x2c,0x0,0xec,0x3e,0x2d,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfd,
+0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x6a,0x66,0x2c,0x0,0x88,0xc,0x2d,0x0,0x9a,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,0x66,0x2c,0x0,0x7b,0x3c,0x2d,0x0,
+0x7d,0x3c,0x2d,0x0,0x7d,0x3c,0x2d,0x0,0x7a,0x3c,0x2d,0x0,0x0,0x0,0x0,0x0,
+0x1,0xfd,0x64,0x2c,0x0,0x98,0x65,0x2c,0x0,0x74,0x66,0x2c,0x0,0x52,0x3c,0x2d,
+0x0,0x98,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xff,0x64,0x2c,0x0,0x99,0x65,
+0x2c,0x0,0x6f,0x66,0x2c,0x0,0xab,0x45,0x2d,0x0,0x99,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2d,0x26,0x2c,0x0,0x1f,0x65,0x2c,0x0,0x20,0x65,0x2c,0x0,0xc6,
+0xee,0x2c,0x0,0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x79,0xfa,0x2a,0x0,
+0x2a,0x5e,0x2c,0x0,0x2c,0x5e,0x2c,0x0,0x5e,0xef,0x2c,0x0,0x2a,0x5e,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xfd,0x64,0x2c,0x0,0x93,0x65,0x2c,0x0,0x72,0x66,0x2c,
+0x0,0x4b,0x49,0x2d,0x0,0x93,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x6b,0x66,
+0x2c,0x0,0xc0,0x7e,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0xbd,0x7e,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfe,0x64,0x2c,0x0,0x9b,0x65,0x2c,0x0,0x6a,
+0x66,0x2c,0x0,0x7f,0x78,0x2c,0x0,0x9b,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x65,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x69,0x66,0x2c,0x0,0x4d,0xd2,0x2c,0x0,
+0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5,0x65,0x2c,0x0,0x96,0x65,0x2c,
+0x0,0x6b,0x66,0x2c,0x0,0xeb,0xe,0x2d,0x0,0x96,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xb,0x65,0x2c,0x0,0xa3,0x65,0x2c,0x0,0x6c,0x66,0x2c,0x0,0x2d,0x47,
+0x2d,0x0,0xa3,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x9,0x65,0x2c,0x0,0xa0,
+0x65,0x2c,0x0,0x6a,0x66,0x2c,0x0,0xb,0x17,0x2d,0x0,0xa0,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x5,0x65,0x2c,0x0,0xd6,0x65,0x2c,0x0,0x69,0x66,0x2c,0x0,
+0x51,0xc5,0x2c,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x6,0x65,0x2c,
+0x0,0x9c,0x65,0x2c,0x0,0x6b,0x66,0x2c,0x0,0x89,0x70,0x2c,0x0,0x9c,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x6,0x65,0x2c,0x0,0xcf,0x65,0x2c,0x0,0x6b,0x66,
+0x2c,0x0,0xe9,0xdf,0x2c,0x0,0xcf,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe0,
+0x22,0x2a,0x0,0xb9,0x64,0x2c,0x0,0xe8,0x66,0x2c,0x0,0xda,0x48,0x2d,0x0,0xb9,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe1,0x22,0x2a,0x0,0xab,0x64,0x2c,0x0,
+0xeb,0x66,0x2c,0x0,0x90,0x1c,0x2d,0x0,0xab,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xe1,0x22,0x2a,0x0,0xce,0x5d,0x2c,0x0,0x1,0x67,0x2c,0x0,0x8a,0xce,0x2c,
+0x0,0xce,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe0,0x22,0x2a,0x0,0x95,0x64,
+0x2c,0x0,0xec,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0x95,0x64,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xe0,0x22,0x2a,0x0,0x93,0x64,0x2c,0x0,0xeb,0x66,0x2c,0x0,0xe2,
+0x46,0x2d,0x0,0x93,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe0,0x22,0x2a,0x0,
+0xc4,0x5d,0x2c,0x0,0x3,0x67,0x2c,0x0,0x60,0x4e,0x2d,0x0,0xc4,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xea,0x22,0x2a,0x0,0xb6,0x5d,0x2c,0x0,0x6,0x67,0x2c,
+0x0,0x1c,0xc,0x2d,0x0,0xb6,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x22,
+0x2a,0x0,0xa8,0x64,0x2c,0x0,0xf5,0x66,0x2c,0x0,0x19,0x7a,0x2c,0x0,0xa8,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xea,0x22,0x2a,0x0,0xa8,0x5d,0x2c,0x0,0x3,
+0x67,0x2c,0x0,0x20,0xd1,0x2c,0x0,0xa8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe8,0x22,0x2a,0x0,0xec,0x64,0x2c,0x0,0xef,0x66,0x2c,0x0,0x8b,0x4e,0x2d,0x0,
+0xec,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe9,0x22,0x2a,0x0,0x22,0x5e,0x2c,
+0x0,0xc,0x67,0x2c,0x0,0x4e,0xc4,0x2c,0x0,0x20,0x5e,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xea,0x22,0x2a,0x0,0xdd,0x5d,0x2c,0x0,0x6,0x67,0x2c,0x0,0xcd,0x56,
+0x2d,0x0,0xdd,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x8e,0xe2,0x2a,0x0,0x37,
+0x65,0x2c,0x0,0x38,0x65,0x2c,0x0,0xc6,0xed,0x2c,0x0,0x34,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xbc,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0xc0,0x39,0x2c,0x0,
+0xa6,0x97,0x2c,0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7b,0x1f,0x2a,
+0x0,0xb3,0x53,0x2b,0x0,0x9f,0xdd,0x2b,0x0,0xce,0xef,0x2c,0x0,0x30,0xdd,0x2b,
+0x0,0x0,0x0,0x0,0x0,0x1,0xef,0x22,0x2a,0x0,0xe7,0x5d,0x2c,0x0,0xd,0x67,
+0x2c,0x0,0x94,0x83,0x2d,0x0,0xe7,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,
+0x1f,0x2a,0x0,0xef,0xe1,0x2a,0x0,0x34,0x26,0x2c,0x0,0x8e,0xe9,0x2c,0x0,0xdd,
+0x25,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xed,0x22,0x2a,0x0,0xff,0x5d,0x2c,0x0,
+0x1,0x67,0x2c,0x0,0xb9,0x55,0x2d,0x0,0xff,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xbd,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0xd1,0x39,0x2c,0x0,0xa2,0xe8,0x2c,
+0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xed,0x22,0x2a,0x0,0xff,0x5d,
+0x2c,0x0,0xe,0x67,0x2c,0x0,0x1e,0x3d,0x2d,0x0,0xff,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xd,0xa7,0x2a,0x0,0x92,0xe2,0x2a,0x0,0xd3,
+0xb8,0x2c,0x0,0x70,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xec,0x22,0x2a,0x0,
+0xc2,0x65,0x2c,0x0,0xe9,0x66,0x2c,0x0,0xf7,0x46,0x2d,0x0,0xc2,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xee,0x22,0x2a,0x0,0xff,0x5d,0x2c,0x0,0x1,0x67,0x2c,
+0x0,0xc2,0x45,0x2d,0x0,0xff,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xee,0x22,
+0x2a,0x0,0xce,0x5d,0x2c,0x0,0x7,0x67,0x2c,0x0,0x28,0x79,0x2c,0x0,0xce,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xeb,0x22,0x2a,0x0,0x90,0x64,0x2c,0x0,0xf2,
+0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0x90,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe9,0x22,0x2a,0x0,0xf5,0x5d,0x2c,0x0,0x8,0x67,0x2c,0x0,0x7d,0x3c,0x2d,0x0,
+0xf5,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xeb,0x22,0x2a,0x0,0x97,0x64,0x2c,
+0x0,0xf1,0x66,0x2c,0x0,0xf7,0x54,0x2d,0x0,0x97,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x79,0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,0xa3,0xdd,0x2b,0x0,0xab,0xef,
+0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0xea,0x22,0x2a,0x0,0xe2,
+0x5d,0x2c,0x0,0x6,0x67,0x2c,0x0,0x7b,0x4e,0x2d,0x0,0xe2,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xeb,0x22,0x2a,0x0,0xfa,0x65,0x2c,0x0,0xfb,0x65,0x2c,0x0,
+0xec,0x47,0x2d,0x0,0xf9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x94,0x19,0x2a,
+0x0,0x73,0x1f,0x2a,0x0,0x40,0x26,0x2c,0x0,0x50,0xef,0x2c,0x0,0xdd,0x25,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0xe0,0xe1,0x2a,0x0,0xd6,0x39,
+0x2c,0x0,0x4c,0x99,0x2c,0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe9,
+0x22,0x2a,0x0,0xb,0x64,0x2c,0x0,0xf7,0x66,0x2c,0x0,0xcb,0x56,0x2d,0x0,0xb,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0xa8,0x53,0x2b,0x0,
+0xc2,0xdd,0x2b,0x0,0xf0,0xea,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,
+0x1,0x95,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x46,0x26,0x2c,0x0,0xc8,0xef,0x2c,
+0x0,0xdd,0x25,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbb,0x19,0x2a,0x0,0xa6,0x1f,
+0x2a,0x0,0xdd,0x39,0x2c,0x0,0x28,0xf0,0x2c,0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,0xcb,0xdd,0x2b,0x0,0xe4,
+0xef,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,0x0,
+0xdd,0xe1,0x2a,0x0,0x4a,0x26,0x2c,0x0,0x46,0xef,0x2c,0x0,0xdd,0x25,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xb1,0xf4,0x2a,0x0,0x18,0x5e,0x2c,0x0,0x19,0x5e,0x2c,
+0x0,0xd2,0xef,0x2c,0x0,0x16,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x22,
+0x2a,0x0,0xbf,0x5d,0x2c,0x0,0x1b,0x67,0x2c,0x0,0x17,0x47,0x2d,0x0,0xbf,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xeb,0x22,0x2a,0x0,0x85,0x64,0x2c,0x0,0xfe,
+0x66,0x2c,0x0,0x21,0xd7,0x2c,0x0,0x85,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe8,0x22,0x2a,0x0,0xa5,0x64,0x2c,0x0,0x3,0x67,0x2c,0x0,0xe6,0x46,0x2d,0x0,
+0xa5,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfb,0x66,0x2c,0x0,0x16,0x70,0x2c,
+0x0,0x1c,0x70,0x2c,0x0,0x92,0x83,0x2d,0x0,0x17,0x70,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe2,0x63,0x2c,0x0,0x96,0x64,0x2c,0x0,0x23,0x66,0x2c,0x0,0x2b,0x78,
+0x2c,0x0,0x96,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x17,0x6b,0x2a,0x0,0x5b,
+0x97,0x2c,0x0,0x87,0x97,0x2c,0x0,0x86,0xef,0x2c,0x0,0x61,0x97,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,0x0,0xbf,0x5d,0x2c,0x0,0xe,0x67,0x2c,0x0,
+0x63,0x44,0x2d,0x0,0xbf,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xc4,0x19,0x2a,
+0x0,0xa6,0x1f,0x2a,0x0,0xe3,0x39,0x2c,0x0,0xa6,0xee,0x2c,0x0,0x3e,0x39,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,0xd1,0xdd,
+0x2b,0x0,0x84,0xde,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x19,
+0x3e,0x2a,0x0,0xc7,0xe1,0x2a,0x0,0xb8,0xf4,0x2a,0x0,0x7b,0xe1,0x2c,0x0,0x93,
+0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xea,0x22,0x2a,0x0,0xad,0x5d,0x2c,0x0,
+0xd,0x67,0x2c,0x0,0x7d,0x3c,0x2d,0x0,0xad,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xf2,0x22,0x2a,0x0,0x69,0x64,0x2c,0x0,0x3,0x67,0x2c,0x0,0x61,0xe8,0x2c,
+0x0,0x69,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf2,0x22,0x2a,0x0,0xaf,0x64,
+0x2c,0x0,0x1,0x67,0x2c,0x0,0xe0,0xc,0x2d,0x0,0xaf,0x64,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xf1,0x22,0x2a,0x0,0x22,0x5e,0x2c,0x0,0x18,0x67,0x2c,0x0,0x86,
+0x45,0x2d,0x0,0x20,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1e,0x6b,0x2a,0x0,
+0x5b,0x97,0x2c,0x0,0x87,0x97,0x2c,0x0,0x50,0xe1,0x2c,0x0,0x61,0x97,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0x17,0xe2,0x2a,0x0,0xe6,0x39,0x2c,
+0x0,0x40,0xef,0x2c,0x0,0x3e,0x39,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,
+0x2a,0x0,0xb3,0x53,0x2b,0x0,0xd5,0xdd,0x2b,0x0,0xc7,0xed,0x2c,0x0,0x30,0xdd,
+0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0xf1,0x22,0x2a,0x0,0x6d,0x65,0x2c,0x0,0xfc,
+0x66,0x2c,0x0,0x91,0x83,0x2d,0x0,0x6d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xf0,0x22,0x2a,0x0,0xc,0x65,0x2c,0x0,0x0,0x67,0x2c,0x0,0x92,0x3d,0x2d,0x0,
+0xc,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf3,0x22,0x2a,0x0,0xb8,0x65,0x2c,
+0x0,0xfa,0x66,0x2c,0x0,0x5a,0xcf,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2b,0x3e,0x2a,0x0,0xde,0xe1,0x2a,0x0,0xbf,0xf4,0x2a,0x0,0x0,0xee,
+0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x6b,0x2a,0x0,0x5b,
+0x97,0x2c,0x0,0x89,0x97,0x2c,0x0,0x56,0xef,0x2c,0x0,0x61,0x97,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x61,0x1f,0x2a,0x0,0x73,0x3c,0x2a,0x0,0xc7,0xf4,0x2a,0x0,
+0x2,0xee,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x6b,0x2a,
+0x0,0x5b,0x97,0x2c,0x0,0x88,0x97,0x2c,0x0,0x54,0xed,0x2c,0x0,0x61,0x97,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x7c,0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,0xdb,0xdd,
+0x2b,0x0,0x6e,0xef,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,
+0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x88,0x97,0x2c,0x0,0x4f,0xe1,0x2c,0x0,0x61,
+0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x77,0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,
+0xe9,0xdd,0x2b,0x0,0xdf,0xec,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1a,0x3e,0x2a,0x0,0xde,0xe1,0x2a,0x0,0xcb,0xf4,0x2a,0x0,0xfc,0xe0,0x2c,
+0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x6b,0x2a,0x0,0x5b,0x97,
+0x2c,0x0,0x85,0x97,0x2c,0x0,0x3f,0xe9,0x2c,0x0,0x60,0x97,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x79,0x1f,0x2a,0x0,0x81,0x53,0x2b,0x0,0xf0,0xdd,0x2b,0x0,0xde,
+0xee,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x1f,0x2a,0x0,
+0xf5,0x3d,0x2a,0x0,0xd5,0xf4,0x2a,0x0,0x15,0xee,0x2c,0x0,0x93,0xf4,0x2a,0x0,
+0x0,0x0,0x0,0x0,0x1,0x40,0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x82,0x97,0x2c,
+0x0,0x9,0xe8,0x2c,0x0,0x61,0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,
+0x2a,0x0,0xf5,0x3d,0x2a,0x0,0xdd,0xf4,0x2a,0x0,0x4a,0xec,0x2c,0x0,0x93,0xf4,
+0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,0xf6,
+0xdd,0x2b,0x0,0x1a,0xee,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,
+0x48,0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x81,0x97,0x2c,0x0,0x3d,0xe9,0x2c,0x0,
+0x61,0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x63,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,
+0x0,0xe4,0xf4,0x2a,0x0,0xd5,0xef,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x1,0xab,0x1f,0x2a,0x0,0xbc,0xb1,0x2a,0x0,0x4d,0x4c,0x2c,0x0,0xbb,0xd3,
+0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x77,0x1f,0x2a,0x0,0xb3,
+0x53,0x2b,0x0,0xfd,0xdd,0x2b,0x0,0x59,0xef,0x2c,0x0,0x30,0xdd,0x2b,0x0,0x0,
+0x0,0x0,0x0,0x1,0xbb,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0x51,0x4c,0x2c,0x0,
+0x4,0xee,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x50,0x6b,0x2a,
+0x0,0x5b,0x97,0x2c,0x0,0x82,0x97,0x2c,0x0,0x1a,0xe9,0x2c,0x0,0x61,0x97,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x63,0x1f,0x2a,0x0,0xdd,0x3d,0x2a,0x0,0xec,0xf4,
+0x2a,0x0,0xe6,0xec,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,
+0x1f,0x2a,0x0,0xb3,0x53,0x2b,0x0,0x3,0xde,0x2b,0x0,0xbc,0xef,0x2c,0x0,0x30,
+0xdd,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,0x0,0xdc,0xe1,0x2a,0x0,
+0x3e,0xc,0x2c,0x0,0x7a,0xee,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xad,0x1f,0x2a,0x0,0xcf,0x3d,0x2a,0x0,0x66,0x4c,0x2c,0x0,0x77,0xea,0x2c,
+0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe9,0x22,0x2a,0x0,0xe7,0x5d,
+0x2c,0x0,0x2b,0x67,0x2c,0x0,0x47,0x46,0x2d,0x0,0xe7,0x5d,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,0x0,0xf0,0xf4,0x2a,0x0,0x4f,
+0xd4,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,
+0xbb,0x5d,0x2c,0x0,0x23,0x67,0x2c,0x0,0xdd,0x18,0x2d,0x0,0xbb,0x5d,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe7,0x22,0x2a,0x0,0x71,0x64,0x2c,0x0,0xa,0x67,0x2c,
+0x0,0x58,0x56,0x2d,0x0,0x71,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,
+0x2a,0x0,0xbf,0x5d,0x2c,0x0,0x23,0x67,0x2c,0x0,0x25,0x49,0x2d,0x0,0xbf,0x5d,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,0x0,0x8f,0x64,0x2c,0x0,0xd,
+0x67,0x2c,0x0,0x52,0x3c,0x2d,0x0,0x8f,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe9,0x22,0x2a,0x0,0xf1,0x5d,0x2c,0x0,0x22,0x67,0x2c,0x0,0xfa,0x45,0x2d,0x0,
+0xf0,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,0x0,0x9,0x65,0x2c,
+0x0,0x7,0x67,0x2c,0x0,0x7e,0xe0,0x2c,0x0,0x9,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xea,0x22,0x2a,0x0,0xc2,0x64,0x2c,0x0,0x0,0x67,0x2c,0x0,0x6b,0x1c,
+0x2d,0x0,0xc2,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe9,0x22,0x2a,0x0,0xfa,
+0x5d,0x2c,0x0,0x18,0x67,0x2c,0x0,0xe9,0xeb,0x2c,0x0,0xfa,0x5d,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0xdc,0x5d,0x2c,0x0,0x15,0x67,0x2c,0x0,
+0x7b,0x47,0x2d,0x0,0xd8,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x56,0x6b,0x2a,
+0x0,0xf,0x95,0x2c,0x0,0x8b,0x97,0x2c,0x0,0x19,0xde,0x2c,0x0,0x11,0x95,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,0x0,0xc4,0x63,0x2c,0x0,0x4,0x67,
+0x2c,0x0,0x93,0x4e,0x2d,0x0,0xc1,0x63,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,
+0x1f,0x2a,0x0,0x21,0x3d,0x2a,0x0,0x43,0xc,0x2c,0x0,0x9f,0xee,0x2c,0x0,0x15,
+0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xab,0x1f,0x2a,0x0,0xf7,0xe1,0x2a,0x0,
+0x75,0x4c,0x2c,0x0,0xff,0xee,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xe8,0x22,0x2a,0x0,0xa5,0x5d,0x2c,0x0,0x19,0x67,0x2c,0x0,0x1f,0xed,0x2c,
+0x0,0xa5,0x5d,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfa,0xf4,0x2a,0x0,0x3f,0x64,
+0x2c,0x0,0x40,0x64,0x2c,0x0,0xf3,0xce,0x2c,0x0,0x3c,0x64,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0xf9,0x64,0x2c,0x0,0x2,0x6d,0x2c,0x0,0xad,
+0xed,0x2c,0x0,0xf9,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe4,0x22,0x2a,0x0,
+0x59,0x64,0x2c,0x0,0x0,0x6d,0x2c,0x0,0x64,0x56,0x2d,0x0,0x59,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x49,0xc,0x2c,0x0,0xf,0x5e,0x2c,0x0,0x10,0x5e,0x2c,
+0x0,0x69,0xeb,0x2c,0x0,0xd,0x5e,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xae,0x1f,
+0x2a,0x0,0x13,0x5e,0x2c,0x0,0xb5,0x7f,0x2c,0x0,0x9d,0xef,0x2c,0x0,0x8f,0x7f,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x66,0x62,0x2c,0x0,0xa6,0x64,0x2c,0x0,0x0,
+0x6d,0x2c,0x0,0x86,0x83,0x2d,0x0,0xa6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x65,0x62,0x2c,0x0,0x5b,0x64,0x2c,0x0,0x4,0x6d,0x2c,0x0,0x98,0xed,0x2c,0x0,
+0x5b,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x4c,0x2c,0x0,0xac,0x5e,0x2c,
+0x0,0xad,0x5e,0x2c,0x0,0x19,0xef,0x2c,0x0,0xab,0x5e,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x6e,0x62,0x2c,0x0,0x89,0x64,0x2c,0x0,0x3,0x6d,0x2c,0x0,0x94,0x83,
+0x2d,0x0,0x89,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x3,0xf5,0x2a,0x0,0x95,
+0x5e,0x2c,0x0,0x96,0x5e,0x2c,0x0,0x9e,0xdc,0x2c,0x0,0x93,0x5e,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x6a,0x62,0x2c,0x0,0x4b,0x64,0x2c,0x0,0xff,0x6c,0x2c,0x0,
+0x75,0x45,0x2d,0x0,0x4b,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5f,0x62,0x2c,
+0x0,0xa2,0x64,0x2c,0x0,0x8,0x6d,0x2c,0x0,0xa9,0xce,0x2c,0x0,0xa2,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe2,0x22,0x2a,0x0,0x82,0x64,0x2c,0x0,0xd,0x6d,
+0x2c,0x0,0x56,0x4e,0x2d,0x0,0x82,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5f,
+0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x83,0x97,0x2c,0x0,0x8f,0xef,0x2c,0x0,0x60,
+0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x65,0x62,0x2c,0x0,0xa7,0x64,0x2c,0x0,
+0x7,0x6d,0x2c,0x0,0xd1,0xc,0x2d,0x0,0xa7,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xad,0x1f,0x2a,0x0,0xe0,0xe1,0x2a,0x0,0xbd,0x7f,0x2c,0x0,0x92,0x97,0x2c,
+0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x19,0x2a,0x0,0x73,0x1f,
+0x2a,0x0,0x4f,0xc,0x2c,0x0,0xd0,0xb6,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0xf4,0xe1,0x2a,0x0,0x87,0x4c,0x2c,0x0,0xa0,
+0xea,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xdf,0x22,0x2a,0x0,
+0xe,0x64,0x2c,0x0,0x7,0x6d,0x2c,0x0,0x15,0x57,0x2d,0x0,0xd,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x64,0x62,0x2c,0x0,0xb2,0x64,0x2c,0x0,0x8,0x6d,0x2c,
+0x0,0x2a,0xee,0x2c,0x0,0xb2,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x79,0x1f,
+0x2a,0x0,0xf1,0xe1,0x2a,0x0,0x52,0xc,0x2c,0x0,0xeb,0xef,0x2c,0x0,0x15,0xc,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe1,0x22,0x2a,0x0,0x81,0x64,0x2c,0x0,0x9,
+0x6d,0x2c,0x0,0xe4,0xea,0x2c,0x0,0x81,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x67,0x62,0x2c,0x0,0xb6,0x64,0x2c,0x0,0x6,0x6d,0x2c,0x0,0x92,0x83,0x2d,0x0,
+0xb6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5e,0x62,0x2c,0x0,0xab,0x64,0x2c,
+0x0,0xd,0x6d,0x2c,0x0,0xf0,0x82,0x2d,0x0,0xab,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe7,0x22,0x2a,0x0,0xa4,0x64,0x2c,0x0,0xd,0x6d,0x2c,0x0,0xf2,0x7b,
+0x2d,0x0,0xa4,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe8,0x22,0x2a,0x0,0x55,
+0x64,0x2c,0x0,0x9,0x6d,0x2c,0x0,0xf0,0x3e,0x2d,0x0,0x55,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xe1,0x63,0x2c,0x0,0xf0,0x64,0x2c,0x0,0x1a,0x66,0x2c,0x0,
+0x28,0x44,0x2d,0x0,0xf0,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbb,0x19,0x2a,
+0x0,0xa6,0x1f,0x2a,0x0,0xc4,0x7f,0x2c,0x0,0xf6,0xef,0x2c,0x0,0x8e,0x7f,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe5,0x22,0x2a,0x0,0xb4,0x64,0x2c,0x0,0x5,0x6d,
+0x2c,0x0,0x19,0x57,0x2d,0x0,0xb4,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x66,
+0x62,0x2c,0x0,0x9d,0x64,0x2c,0x0,0xc,0x6d,0x2c,0x0,0x3c,0x45,0x2d,0x0,0x9d,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x66,0x62,0x2c,0x0,0x7b,0x64,0x2c,0x0,
+0xfb,0x6c,0x2c,0x0,0x4d,0xee,0x2c,0x0,0x7b,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x68,0x6b,0x2a,0x0,0x8f,0x96,0x2c,0x0,0x84,0x97,0x2c,0x0,0x70,0xe2,0x2c,
+0x0,0x8d,0x96,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x22,0x2a,0x0,0xd7,0x65,
+0x2c,0x0,0xec,0x65,0x2c,0x0,0xe4,0x75,0x2c,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x5d,0x62,0x2c,0x0,0x9e,0x64,0x2c,0x0,0xff,0x6c,0x2c,0x0,0xe6,
+0xe2,0x2c,0x0,0x9e,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x66,0x62,0x2c,0x0,
+0x97,0x64,0x2c,0x0,0xfc,0x6c,0x2c,0x0,0x2f,0xdd,0x2c,0x0,0x97,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe3,0x22,0x2a,0x0,0xb1,0x64,0x2c,0x0,0xfd,0x6c,0x2c,
+0x0,0x7c,0x3c,0x2d,0x0,0xb1,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x5f,0x62,
+0x2c,0x0,0x13,0x65,0x2c,0x0,0xfe,0x6c,0x2c,0x0,0xcf,0x43,0x2d,0x0,0x13,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,0x0,0xa,
+0xf5,0x2a,0x0,0xe,0xee,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,
+0x6f,0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x85,0x97,0x2c,0x0,0x99,0xdf,0x2c,0x0,
+0x61,0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf8,0x63,0x2c,0x0,0xb5,0x64,0x2c,
+0x0,0x25,0x66,0x2c,0x0,0x1f,0x76,0x2d,0x0,0xb5,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xcd,0x89,0x5,0x0,0x44,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0x86,0xfa,
+0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,0x44,
+0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0x7b,0xbd,0x7,0x0,0x79,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0x98,0x89,0x5,0x0,0xf9,0xb9,0x7,0x0,0xa2,0xbd,0x7,0x0,
+0x9f,0xf5,0x7,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xae,0x1f,0x2a,
+0x0,0xb2,0xe1,0x2a,0x0,0x8a,0x4c,0x2c,0x0,0xbd,0xd3,0x2c,0x0,0xe2,0x4b,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0xf4,0xe1,0x2a,0x0,0xc7,0x7f,
+0x2c,0x0,0xf2,0xed,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x98,
+0x89,0x5,0x0,0x37,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0xfd,0xfe,0x7,0x0,0x79,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,0x44,0x8e,0x5,0x0,
+0x46,0x8e,0x5,0x0,0x77,0x1,0x8,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0x79,0x1f,0x2a,0x0,0xdf,0xe1,0x2a,0x0,0x5d,0xc,0x2c,0x0,0x58,0xed,0x2c,
+0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x6b,0x2a,0x0,0x43,0x95,
+0x2c,0x0,0x8c,0x97,0x2c,0x0,0xb9,0xdd,0x2c,0x0,0x41,0x95,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,0x0,0x13,0xf5,0x2a,0x0,0x44,
+0xee,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,
+0xcc,0x3d,0x2a,0x0,0x8f,0x4c,0x2c,0x0,0x49,0xe2,0x2c,0x0,0xe2,0x4b,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,0x0,0xef,0xe1,0x2a,0x0,0x64,0xc,0x2c,
+0x0,0xd6,0xb7,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x19,
+0x2a,0x0,0xa6,0x1f,0x2a,0x0,0xd2,0x7f,0x2c,0x0,0xcc,0x98,0x2c,0x0,0x8e,0x7f,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x61,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,0x0,0x17,
+0xf5,0x2a,0x0,0x4a,0xee,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,
+0x7b,0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x82,0x97,0x2c,0x0,0xad,0xef,0x2c,0x0,
+0x61,0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbb,0x19,0x2a,0x0,0xa6,0x1f,0x2a,
+0x0,0x93,0x4c,0x2c,0x0,0x88,0x97,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xac,0x1f,0x2a,0x0,0x1e,0x6f,0x2c,0x0,0xd8,0x7f,0x2c,0x0,0xc5,0x98,
+0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,0x0,0x41,
+0xb1,0x2a,0x0,0x6c,0xc,0x2c,0x0,0xe2,0xef,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x61,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,0x0,0x24,0xf5,0x2a,0x0,
+0xd0,0xef,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x8b,0x6b,0x2a,
+0x0,0xc2,0x96,0x2c,0x0,0x89,0x97,0x2c,0x0,0xdf,0xb8,0x2c,0x0,0xc0,0x96,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xc3,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0xd0,0xa8,
+0x2c,0x0,0xd1,0xee,0x2c,0x0,0xf9,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xc5,
+0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0x96,0x4c,0x2c,0x0,0x71,0xee,0x2c,0x0,0xe2,
+0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0x13,0x6f,0x2c,0x0,
+0xde,0x7f,0x2c,0x0,0x88,0x97,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x77,0x1f,0x2a,0x0,0x1a,0xe2,0x2a,0x0,0x72,0xc,0x2c,0x0,0x1,0xe9,0x2c,
+0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xae,0x1f,0x2a,0x0,0x1b,0xe2,
+0x2a,0x0,0xd5,0xa8,0x2c,0x0,0xd4,0xcf,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x97,0x89,0x5,0x0,0xa5,0xa6,0x5,0x0,0xa7,0xa6,0x5,0x0,0x5c,
+0x4,0x8,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,
+0xa3,0xa6,0x5,0x0,0xa5,0xa6,0x5,0x0,0xcc,0xff,0x7,0x0,0x79,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x91,0x6b,0x2a,0x0,0xb,0x95,0x2c,0x0,0x92,0x97,0x2c,
+0x0,0xde,0xd5,0x2c,0x0,0xa,0x95,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,
+0x5,0x0,0xa5,0xa6,0x5,0x0,0xa7,0xa6,0x5,0x0,0xb7,0xeb,0x7,0x0,0x79,0xd,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x3e,0x2a,0x0,0xef,0xe1,0x2a,0x0,0x2a,
+0xf5,0x2a,0x0,0x2b,0xed,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,
+0x95,0x89,0x5,0x0,0x33,0x8e,0x5,0x0,0x34,0x8e,0x5,0x0,0x5a,0xf6,0x7,0x0,
+0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x81,0x19,0x2a,0x0,0x5b,0x1f,0x2a,
+0x0,0xf,0xcb,0x2a,0x0,0xe8,0xec,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x1,0x95,0x89,0x5,0x0,0x34,0x8e,0x5,0x0,0x37,0x8e,0x5,0x0,0x57,0xce,
+0x7,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,0x44,
+0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0xba,0xff,0x7,0x0,0x79,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,0x42,0xba,0x7,0x0,0xdc,0xbd,0x7,0x0,
+0xbd,0xff,0x7,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x98,0x89,0x5,
+0x0,0x37,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0xca,0xb,0x8,0x0,0x79,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,0x37,0x8e,0x5,0x0,0x38,0x8e,
+0x5,0x0,0x82,0xb,0x8,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xac,
+0x1f,0x2a,0x0,0xdc,0xe1,0x2a,0x0,0x2,0x4c,0x2c,0x0,0x6,0xf0,0x2c,0x0,0xe2,
+0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbb,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,
+0xe3,0x7f,0x2c,0x0,0xf4,0xef,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x96,0x89,0x5,0x0,0x37,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0x49,0xba,0x7,
+0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xbb,0x19,0x2a,0x0,0xa6,0x1f,
+0x2a,0x0,0xdc,0xa8,0x2c,0x0,0x2d,0xf0,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x97,0x89,0x5,0x0,0x33,0x8e,0x5,0x0,0x35,0x8e,0x5,0x0,0x79,
+0xbd,0x7,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x96,0x89,0x5,0x0,
+0x33,0x8e,0x5,0x0,0x34,0x8e,0x5,0x0,0xe8,0x3,0x8,0x0,0x79,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x96,0x89,0x5,0x0,0xa3,0xa6,0x5,0x0,0xa5,0xa6,0x5,
+0x0,0x80,0xb,0x8,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x82,0x19,
+0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x17,0xcb,0x2a,0x0,0xa4,0xb4,0x2c,0x0,0xd5,0xca,
+0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x77,0x1f,0x2a,0x0,0x19,0xb1,0x2a,0x0,0x79,
+0xc,0x2c,0x0,0xe6,0xef,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x98,0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x88,0x97,0x2c,0x0,0x61,0xeb,0x2c,0x0,
+0x61,0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,
+0x0,0x3c,0xf5,0x2a,0x0,0xcd,0xe0,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x1,0xaf,0x1f,0x2a,0x0,0xc7,0xe1,0x2a,0x0,0x8,0x4c,0x2c,0x0,0x1b,0x98,
+0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbf,0x19,0x2a,0x0,0xa6,
+0x1f,0x2a,0x0,0xe2,0xa8,0x2c,0x0,0x5e,0xba,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x85,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x26,0xcb,0x2a,0x0,
+0x20,0xb9,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,
+0x0,0xc7,0xe1,0x2a,0x0,0xe7,0x7f,0x2c,0x0,0x73,0xef,0x2c,0x0,0x8e,0x7f,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xa2,0x6b,0x2a,0x0,0x5b,0x97,0x2c,0x0,0x89,0x97,
+0x2c,0x0,0x10,0xe8,0x2c,0x0,0x60,0x97,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x98,
+0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x84,0xc,0x2c,0x0,0xeb,0xed,0x2c,0x0,0x15,
+0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x62,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,0x0,
+0x49,0xf5,0x2a,0x0,0xa3,0xee,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,
+0x1,0x33,0xcb,0x2a,0x0,0xd3,0xe1,0x2a,0x0,0xd4,0xe1,0x2a,0x0,0x1a,0xe1,0x2c,
+0x0,0xd2,0xe1,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xaf,0x1f,0x2a,0x0,0xd8,0xe1,
+0x2a,0x0,0xf,0x4c,0x2c,0x0,0x89,0x97,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0xad,0x96,0x2c,0x0,0xe6,0xa8,0x2c,0x0,0x56,
+0xba,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,
+0x8,0xe2,0x2a,0x0,0xf1,0x7f,0x2c,0x0,0x9b,0x99,0x2c,0x0,0x8e,0x7f,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,0x28,0xe2,0x2a,0x0,0x89,0xc,0x2c,
+0x0,0x57,0xef,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xa7,0x6b,
+0x2a,0x0,0x91,0x95,0x2c,0x0,0x8c,0x97,0x2c,0x0,0xf8,0xe7,0x2c,0x0,0x91,0x95,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x63,0x1f,0x2a,0x0,0xf5,0x3d,0x2a,0x0,0x4e,
+0xf5,0x2a,0x0,0x96,0xb7,0x2c,0x0,0x93,0xf4,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,
+0xac,0x1f,0x2a,0x0,0xfb,0xe1,0x2a,0x0,0x16,0x4c,0x2c,0x0,0xb9,0xef,0x2c,0x0,
+0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x83,0x19,0x2a,0x0,0x5b,0x1f,0x2a,
+0x0,0x3b,0xcb,0x2a,0x0,0xf1,0xef,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x1,0xad,0x1f,0x2a,0x0,0x9b,0x95,0x2c,0x0,0x17,0xa8,0x2c,0x0,0xdc,0xce,
+0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x19,0x2a,0x0,0xa6,
+0x1f,0x2a,0x0,0xf5,0x7f,0x2c,0x0,0xe8,0xef,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,0x0,0x41,0x3d,0x2a,0x0,0x8f,0xc,0x2c,0x0,
+0x65,0xe0,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x83,0x19,0x2a,
+0x0,0x5b,0x1f,0x2a,0x0,0x4b,0xcb,0x2a,0x0,0x67,0xee,0x2c,0x0,0xd5,0xca,0x2a,
+0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0xe9,0xb1,0x2a,0x0,0x19,0x4c,
+0x2c,0x0,0xb0,0xef,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xab,
+0x1f,0x2a,0x0,0xff,0xe1,0x2a,0x0,0x1d,0xa8,0x2c,0x0,0xe6,0xba,0x2c,0x0,0xf8,
+0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x66,0x2c,0x0,0xd,0x70,0x2c,0x0,
+0x12,0x70,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0xe,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x96,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x96,0xc,0x2c,0x0,0x20,0xf0,0x2c,
+0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x81,0x19,0x2a,0x0,0x5b,0x1f,
+0x2a,0x0,0x54,0xcb,0x2a,0x0,0xc4,0xef,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,
+0x0,0x0,0x1,0xab,0x1f,0x2a,0x0,0xc9,0x3d,0x2a,0x0,0x25,0x4c,0x2c,0x0,0xec,
+0xed,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,
+0x39,0x8e,0x5,0x0,0x3b,0x8e,0x5,0x0,0xaf,0x9,0x8,0x0,0x79,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0x9d,0x96,0x2c,0x0,0x22,0xa8,0x2c,
+0x0,0x3c,0xee,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x19,
+0x2a,0x0,0xa6,0x1f,0x2a,0x0,0xfb,0x7f,0x2c,0x0,0x43,0xee,0x2c,0x0,0x8e,0x7f,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x96,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0x98,
+0xc,0x2c,0x0,0xb4,0xb8,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x85,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x63,0xcb,0x2a,0x0,0x3b,0xef,0x2c,0x0,
+0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xab,0x1f,0x2a,0x0,0x20,0xe2,0x2a,
+0x0,0x2d,0x4c,0x2c,0x0,0x55,0xef,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x96,0x89,0x5,0x0,0xa3,0xa6,0x5,0x0,0xa4,0xa6,0x5,0x0,0xa7,0xbd,
+0x7,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x97,0x89,0x5,0x0,0xb0,
+0xa6,0x5,0x0,0xb1,0xa6,0x5,0x0,0x64,0x2,0x8,0x0,0x79,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0x95,0x89,0x5,0x0,0x35,0x8e,0x5,0x0,0x37,0x8e,0x5,0x0,
+0x60,0x4,0x8,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x95,0x89,0x5,
+0x0,0x41,0x8e,0x5,0x0,0x42,0x8e,0x5,0x0,0x5d,0xf7,0x7,0x0,0x79,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0x98,0x89,0x5,0x0,0x38,0x8e,0x5,0x0,0x39,0x8e,
+0x5,0x0,0xbf,0xf2,0x7,0x0,0x79,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x96,
+0x89,0x5,0x0,0x41,0x8e,0x5,0x0,0x42,0x8e,0x5,0x0,0xb8,0xb,0x8,0x0,0x79,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,
+0x64,0x65,0x2c,0x0,0xcb,0x75,0x2c,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xca,0x89,0x5,0x0,0x32,0x8e,0x5,0x0,0x34,0x8e,0x5,0x0,0x2a,0xe1,0x7,
+0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0xdf,0xe1,
+0x2a,0x0,0x26,0xa8,0x2c,0x0,0xda,0xee,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xce,0x89,0x5,0x0,0xa1,0xa6,0x5,0x0,0xa3,0xa6,0x5,0x0,0xd3,
+0x6,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xc8,0x89,0x5,0x0,
+0x37,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0xab,0xbd,0x7,0x0,0x7a,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0x97,0xbb,0x7,0x0,0x9a,0xbb,0x7,
+0x0,0xca,0xfc,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcd,0x89,
+0x5,0x0,0x35,0x8e,0x5,0x0,0x36,0x8e,0x5,0x0,0x3b,0xf7,0x7,0x0,0x7a,0xd,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0x36,0x8e,0x5,0x0,0x37,
+0x8e,0x5,0x0,0x5d,0xf2,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0xce,0x89,0x5,0x0,0xa4,0xa6,0x5,0x0,0xa5,0xa6,0x5,0x0,0xb1,0x2,0x8,0x0,
+0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0xa1,0xa6,0x5,
+0x0,0xa2,0xa6,0x5,0x0,0x41,0xb,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0xcb,0x89,0x5,0x0,0x35,0x8e,0x5,0x0,0x37,0x8e,0x5,0x0,0xf8,0xf3,
+0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xbb,0x19,0x2a,0x0,0xa6,
+0x1f,0x2a,0x0,0x2,0x80,0x2c,0x0,0x4c,0xc5,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xc9,0x89,0x5,0x0,0x39,0x8e,0x5,0x0,0x3a,0x8e,0x5,0x0,
+0x68,0xf5,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,
+0x0,0x34,0x8e,0x5,0x0,0x35,0x8e,0x5,0x0,0x9d,0xbd,0x7,0x0,0x7a,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0xa7,0xa6,0x5,0x0,0xa9,0xa6,
+0x5,0x0,0x10,0xfa,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x96,
+0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0xa8,0xc,0x2c,0x0,0x37,0xef,0x2c,0x0,0x15,
+0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x81,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,
+0x6b,0xcb,0x2a,0x0,0xc2,0xb1,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,0x0,
+0x1,0xcc,0x89,0x5,0x0,0xc0,0xe5,0x6,0x0,0xc1,0xe5,0x6,0x0,0x55,0x0,0x8,
+0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xc9,0x89,0x5,0x0,0xa1,0xa6,
+0x5,0x0,0xa4,0xa6,0x5,0x0,0xdc,0xfb,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0xcc,0x89,0x5,0x0,0xad,0xa6,0x5,0x0,0xaf,0xa6,0x5,0x0,0xb3,
+0x2,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xc8,0x89,0x5,0x0,
+0xb7,0xa6,0x5,0x0,0xb8,0xa6,0x5,0x0,0x2d,0xb,0x8,0x0,0x7a,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,0x0,0xa5,0xa6,0x5,0x0,0xa6,0xa6,0x5,
+0x0,0x8d,0x2,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,
+0x5,0x0,0x40,0x8e,0x5,0x0,0x42,0x8e,0x5,0x0,0x83,0xff,0x7,0x0,0x7a,0xd,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,0x0,0x35,0x8e,0x5,0x0,0x37,
+0x8e,0x5,0x0,0x68,0xe5,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0xcc,0x89,0x5,0x0,0x35,0x8e,0x5,0x0,0x37,0x8e,0x5,0x0,0x96,0xfd,0x7,0x0,
+0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcd,0x89,0x5,0x0,0xa5,0xa6,0x5,
+0x0,0xa7,0xa6,0x5,0x0,0x86,0xe5,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0xcd,0x89,0x5,0x0,0x37,0x8e,0x5,0x0,0x38,0x8e,0x5,0x0,0x73,0xee,
+0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0x37,
+0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0x20,0xa9,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0xc1,0x3e,0x5,0x0,0xa6,0x89,0x5,0x0,0xcd,0x89,0x5,0x0,
+0x2e,0x3,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcd,0x89,0x5,
+0x0,0x3a,0x8e,0x5,0x0,0x3b,0x8e,0x5,0x0,0xd2,0x1,0x8,0x0,0x7a,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0xd6,0xb1,0x2a,0x0,0x22,0xe2,0x2a,0x0,0x35,0x4c,
+0x2c,0x0,0x24,0xe8,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xc9,
+0x89,0x5,0x0,0xb0,0xa6,0x5,0x0,0xb1,0xa6,0x5,0x0,0x84,0x7,0x8,0x0,0x7a,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,0x0,0xb7,0xa6,0x5,0x0,
+0xb9,0xa6,0x5,0x0,0xff,0xa,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0xad,0x1f,0x2a,0x0,0xc5,0x6e,0x2c,0x0,0x29,0xa8,0x2c,0x0,0x8e,0xb8,0x2c,
+0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,0x0,0x35,0x8e,
+0x5,0x0,0x37,0x8e,0x5,0x0,0x72,0x9,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0xfa,0xe1,0x2a,0x0,0x5,0x80,0x2c,0x0,0x58,
+0x98,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x7a,0x1f,0x2a,0x0,
+0xb6,0xe1,0x2a,0x0,0xb0,0xc,0x2c,0x0,0x9d,0xee,0x2c,0x0,0x15,0xc,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xcc,0x89,0x5,0x0,0x5d,0xcc,0x6,0x0,0x5e,0xcc,0x6,
+0x0,0x5d,0xe2,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xc9,0x89,
+0x5,0x0,0x43,0x8e,0x5,0x0,0x44,0x8e,0x5,0x0,0xdd,0xdf,0x7,0x0,0x7a,0xd,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcc,0x89,0x5,0x0,0x41,0x8e,0x5,0x0,0x42,
+0x8e,0x5,0x0,0x6a,0xbd,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0xcd,0x89,0x5,0x0,0xa3,0xa6,0x5,0x0,0xa5,0xa6,0x5,0x0,0xdf,0x7,0x8,0x0,
+0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0xaf,0xa6,0x5,
+0x0,0xb1,0xa6,0x5,0x0,0x13,0xcf,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0x82,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x78,0xcb,0x2a,0x0,0x8b,0xee,
+0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xcc,0x89,0x5,0x0,0x3f,
+0x8e,0x5,0x0,0x40,0x8e,0x5,0x0,0x60,0x6,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0xc9,0x3d,0x2a,0x0,0x3c,0x4c,0x2c,0x0,
+0xce,0x98,0x2c,0x0,0xe2,0x4b,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,
+0x0,0xb1,0xa6,0x5,0x0,0xb2,0xa6,0x5,0x0,0x6d,0xb,0x8,0x0,0x7a,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0xaf,0xa6,0x5,0x0,0xb1,0xa6,
+0x5,0x0,0xe8,0xbd,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xca,
+0x89,0x5,0x0,0x33,0x8e,0x5,0x0,0x34,0x8e,0x5,0x0,0xcd,0xfc,0x7,0x0,0x7a,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xce,0x89,0x5,0x0,0xa3,0xa6,0x5,0x0,
+0xa5,0xa6,0x5,0x0,0xe,0xf3,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0xca,0x89,0x5,0x0,0xa3,0xa6,0x5,0x0,0xa5,0xa6,0x5,0x0,0x8c,0x4,0x8,
+0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcc,0x89,0x5,0x0,0x44,0x8e,
+0x5,0x0,0x45,0x8e,0x5,0x0,0x1e,0xb,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0x2b,0xe2,0x2a,0x0,0x35,0xa8,0x2c,0x0,0xd5,
+0xb9,0x2c,0x0,0xf9,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,0x0,
+0x35,0x8e,0x5,0x0,0x37,0x8e,0x5,0x0,0xb5,0xbc,0x7,0x0,0x7a,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0xca,0x89,0x5,0x0,0xa4,0xa6,0x5,0x0,0xa6,0xa6,0x5,
+0x0,0x4d,0xde,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,
+0x5,0x0,0xb5,0xa6,0x5,0x0,0xb6,0xa6,0x5,0x0,0xfd,0xf5,0x7,0x0,0x7a,0xd,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,0x2a,0x0,0xdc,0xe1,0x2a,0x0,0x11,
+0x80,0x2c,0x0,0x58,0x99,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xca,0x89,0x5,0x0,0xaf,0xa6,0x5,0x0,0xb1,0xa6,0x5,0x0,0x85,0x6,0x8,0x0,
+0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x63,0x2c,0x0,0x9b,0x64,0x2c,
+0x0,0x23,0x66,0x2c,0x0,0x4d,0xe,0x2d,0x0,0x9b,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xcd,0x89,0x5,0x0,0x39,0x8e,0x5,0x0,0x3a,0x8e,0x5,0x0,0xf,0x1,
+0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcc,0x89,0x5,0x0,0x34,
+0x8e,0x5,0x0,0x35,0x8e,0x5,0x0,0x50,0xcd,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0xc8,0x89,0x5,0x0,0x38,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,
+0x88,0xfe,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,
+0x0,0x41,0x8e,0x5,0x0,0x44,0x8e,0x5,0x0,0xcd,0xfa,0x7,0x0,0x7a,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0xcb,0x89,0x5,0x0,0xa4,0xa6,0x5,0x0,0xa6,0xa6,
+0x5,0x0,0x1b,0x4,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcc,
+0x89,0x5,0x0,0x35,0x8e,0x5,0x0,0x37,0x8e,0x5,0x0,0x45,0xbb,0x7,0x0,0x7a,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xce,0x89,0x5,0x0,0xa4,0xa6,0x5,0x0,
+0xa5,0xa6,0x5,0x0,0xa5,0xf2,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0xca,0x89,0x5,0x0,0x33,0x8e,0x5,0x0,0x34,0x8e,0x5,0x0,0xc,0x6,0x8,
+0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x96,0x19,0x2a,0x0,0x73,0x1f,
+0x2a,0x0,0xb8,0xc,0x2c,0x0,0x3f,0xba,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xcd,0x89,0x5,0x0,0x44,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0x87,
+0xd4,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xcd,0x89,0x5,0x0,
+0xb0,0xa6,0x5,0x0,0xb1,0xa6,0x5,0x0,0xba,0xf3,0x7,0x0,0x7a,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0xc8,0x89,0x5,0x0,0x35,0x8e,0x5,0x0,0x37,0x8e,0x5,
+0x0,0xd5,0xba,0x7,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x83,0x19,
+0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x83,0xcb,0x2a,0x0,0xf6,0xed,0x2c,0x0,0xd5,0xca,
+0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0xce,0x89,0x5,0x0,0xb0,0xa6,0x5,0x0,0xb2,
+0xa6,0x5,0x0,0xdd,0x7,0x8,0x0,0x7a,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0xac,0x1f,0x2a,0x0,0xd,0xe2,0x2a,0x0,0x3c,0xa8,0x2c,0x0,0x9f,0xef,0x2c,0x0,
+0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x8a,0x5,0x0,0x35,0x8e,0x5,
+0x0,0x37,0x8e,0x5,0x0,0xaa,0x2,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0x27,0x8a,0x5,0x0,0x38,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0xa9,0xe3,
+0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x8a,0x5,0x0,0x35,
+0x8e,0x5,0x0,0x37,0x8e,0x5,0x0,0xe1,0x7,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0x23,0x8a,0x5,0x0,0xa7,0xa6,0x5,0x0,0xa8,0xa6,0x5,0x0,
+0x79,0xfc,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,
+0x0,0x1,0xe2,0x2a,0x0,0x40,0x4c,0x2c,0x0,0xc5,0xee,0x2c,0x0,0xe2,0x4b,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x8a,0x5,0x0,0x18,0xba,0x7,0x0,0xe3,0xbd,
+0x7,0x0,0x97,0x3,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,
+0x8a,0x5,0x0,0x45,0x8e,0x5,0x0,0x46,0x8e,0x5,0x0,0x9a,0xfa,0x7,0x0,0x7b,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x8a,0x5,0x0,0x44,0x8e,0x5,0x0,
+0x45,0x8e,0x5,0x0,0x62,0x0,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1e,0x8a,0x5,0x0,0x43,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0x7d,0x3,0x8,
+0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x8a,0x5,0x0,0xaf,0xa6,
+0x5,0x0,0xb1,0xa6,0x5,0x0,0x84,0xff,0x7,0x0,0xf0,0xd,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1f,0x8a,0x5,0x0,0x44,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0xd,
+0xf5,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x8a,0x5,0x0,
+0x44,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0x68,0x0,0x8,0x0,0x7b,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x96,0x19,0x2a,0x0,0x73,0x1f,0x2a,0x0,0xbe,0xc,0x2c,
+0x0,0x8b,0xef,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xad,0x1f,
+0x2a,0x0,0xfd,0x6e,0x2c,0x0,0x18,0x80,0x2c,0x0,0xc3,0x98,0x2c,0x0,0x8e,0x7f,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x8a,0x5,0x0,0x41,0x8e,0x5,0x0,0x42,
+0x8e,0x5,0x0,0x5,0x9,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2c,0x8a,0x5,0x0,0x44,0x8e,0x5,0x0,0x46,0x8e,0x5,0x0,0x82,0xff,0x7,0x0,
+0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x8a,0x5,0x0,0xc6,0xce,0x6,
+0x0,0xc7,0xce,0x6,0x0,0x7d,0xe8,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0x25,0x8a,0x5,0x0,0x36,0x8e,0x5,0x0,0x38,0x8e,0x5,0x0,0x8d,0xff,
+0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x82,0x19,0x2a,0x0,0x5b,
+0x1f,0x2a,0x0,0x8a,0xcb,0x2a,0x0,0x85,0xb8,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1f,0x8a,0x5,0x0,0xb0,0xa6,0x5,0x0,0xb2,0xa6,0x5,0x0,
+0x38,0xfd,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x8a,0x5,
+0x0,0x37,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0x91,0xa,0x8,0x0,0x7b,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x8a,0x5,0x0,0x42,0xed,0x6,0x0,0x43,0xed,
+0x6,0x0,0x74,0xf9,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x25,
+0x8a,0x5,0x0,0x40,0x8e,0x5,0x0,0x42,0x8e,0x5,0x0,0x76,0xfa,0x7,0x0,0x7b,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x21,0x8a,0x5,0x0,0xa5,0xa6,0x5,0x0,
+0xa7,0xa6,0x5,0x0,0x0,0xf8,0x7,0x0,0xef,0xd,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2d,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x91,0x1f,0x8,0x0,0x3b,0xb,0x8,
+0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0xfe,0xe1,
+0x2a,0x0,0x41,0xa8,0x2c,0x0,0xb,0xef,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2a,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x90,0x1f,0x8,0x0,0x87,
+0xee,0x7,0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x8a,0x5,0x0,
+0x7a,0xd,0x8,0x0,0x86,0x1f,0x8,0x0,0x82,0xb,0x8,0x0,0x9b,0x1f,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x25,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x85,0x1f,0x8,
+0x0,0xe2,0x2,0x8,0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x19,
+0x2a,0x0,0xa6,0x1f,0x2a,0x0,0x24,0x80,0x2c,0x0,0xee,0xef,0x2c,0x0,0x8e,0x7f,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x8d,
+0x1f,0x8,0x0,0x9f,0xf5,0x7,0x0,0x9f,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2d,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x8c,0x1f,0x8,0x0,0xfe,0xff,0x7,0x0,
+0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0x8a,0x5,0x0,0x7a,0xd,0x8,
+0x0,0x89,0x1f,0x8,0x0,0x31,0x9,0x8,0x0,0x9e,0x1f,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0x26,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x87,0x1f,0x8,0x0,0x30,0x9,
+0x8,0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x8a,0x5,0x0,0x7a,
+0xd,0x8,0x0,0x78,0x1f,0x8,0x0,0x3a,0xf3,0x7,0x0,0xa1,0x1f,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0x29,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x8b,0x1f,0x8,0x0,
+0xc9,0x0,0x8,0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x8a,0x5,
+0x0,0x7a,0xd,0x8,0x0,0x87,0x1f,0x8,0x0,0x8,0xf0,0x7,0x0,0xa1,0x1f,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0xf4,0xbd,0x7,0x0,0x7a,0xd,0x8,0x0,0x7a,0x1f,
+0x8,0x0,0x73,0xa,0x8,0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,
+0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x84,0x1f,0x8,0x0,0x97,0xbd,0x7,0x0,0xa1,
+0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,
+0x89,0x1f,0x8,0x0,0x88,0xe2,0x7,0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2b,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x8f,0x1f,0x8,0x0,0x88,0xdd,0x7,
+0x0,0xa1,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x8a,0x5,0x0,0x7a,0xd,
+0x8,0x0,0x83,0x1f,0x8,0x0,0x84,0xfa,0x7,0x0,0x9c,0x1f,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0x27,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x8c,0x1f,0x8,0x0,0xbe,
+0xd7,0x7,0x0,0x9d,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x8a,0x5,0x0,
+0x7a,0xd,0x8,0x0,0x8e,0x1f,0x8,0x0,0x8b,0xdf,0x7,0x0,0xa1,0x1f,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x82,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x96,0xcb,0x2a,
+0x0,0xf6,0xee,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x21,0x8a,
+0x5,0x0,0x7a,0xd,0x8,0x0,0x8a,0x1f,0x8,0x0,0x21,0xbb,0x7,0x0,0xa1,0x1f,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xab,0x1f,0x2a,0x0,0x49,0x6e,0x2c,0x0,0x45,
+0xa8,0x2c,0x0,0x1f,0xee,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x21,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x7d,0x1f,0x8,0x0,0xe7,0x3,0x8,0x0,
+0xa0,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x20,0x8a,0x5,0x0,0x33,0x8e,0x5,
+0x0,0x34,0x8e,0x5,0x0,0xc8,0xe1,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0x25,0x8a,0x5,0x0,0x37,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0x34,0x9,
+0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x1c,0x8a,0x5,0x0,0xb0,
+0xa6,0x5,0x0,0xb1,0xa6,0x5,0x0,0x55,0xfb,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0x78,0x1f,0x2a,0x0,0xed,0xe1,0x2a,0x0,0xc3,0xc,0x2c,0x0,
+0x94,0xef,0x2c,0x0,0x15,0xc,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x8a,0x5,
+0x0,0xa2,0xa6,0x5,0x0,0xa4,0xa6,0x5,0x0,0x2f,0xfc,0x7,0x0,0x7b,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x8a,0x5,0x0,0x35,0x8e,0x5,0x0,0x36,0x8e,
+0x5,0x0,0x7a,0x7,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x20,
+0x8a,0x5,0x0,0xa6,0xa6,0x5,0x0,0xa7,0xa6,0x5,0x0,0xf2,0x9,0x8,0x0,0x7b,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x8a,0x5,0x0,0x3b,0xba,0x7,0x0,
+0xd8,0xbd,0x7,0x0,0x7b,0xf9,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1b,0x8a,0x5,0x0,0xb0,0xa6,0x5,0x0,0xb1,0xa6,0x5,0x0,0x2f,0xe6,0x7,
+0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x1e,0x8a,0x5,0x0,0x7a,0xd,
+0x8,0x0,0x84,0x1f,0x8,0x0,0x70,0x4,0x8,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2b,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x82,0x1f,0x8,0x0,0x87,
+0x5,0x8,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x1e,0x8a,0x5,0x0,
+0x7a,0xd,0x8,0x0,0x82,0x1f,0x8,0x0,0xea,0x3,0x8,0x0,0xa3,0x1f,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x20,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x82,0x1f,0x8,
+0x0,0x28,0xf4,0x7,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x21,0x8a,
+0x5,0x0,0x7a,0xd,0x8,0x0,0x86,0x1f,0x8,0x0,0x8f,0xc8,0x7,0x0,0xa3,0x1f,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0x31,
+0x80,0x2c,0x0,0x88,0x97,0x2c,0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x81,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0x9f,0xcb,0x2a,0x0,0x36,0xed,0x2c,0x0,
+0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x1d,0x8a,0x5,0x0,0x7a,0xd,0x8,
+0x0,0x76,0x1f,0x8,0x0,0x87,0x5,0x8,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1f,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x82,0x1f,0x8,0x0,0x6b,0xe1,
+0x7,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x8a,0x5,0x0,0x7a,
+0xd,0x8,0x0,0x81,0x1f,0x8,0x0,0x59,0xf1,0x7,0x0,0xa3,0x1f,0x8,0x0,0x0,
+0x0,0x0,0x0,0x1,0x24,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x83,0x1f,0x8,0x0,
+0x2,0xe8,0x7,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x8a,0x5,
+0x0,0x7a,0xd,0x8,0x0,0x7a,0x1f,0x8,0x0,0x31,0xe9,0x7,0x0,0xa3,0x1f,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0xab,0x1f,0x2a,0x0,0x9,0xe2,0x2a,0x0,0x49,0xa8,
+0x2c,0x0,0xd9,0xb9,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1b,
+0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x80,0x1f,0x8,0x0,0x2f,0xe9,0x7,0x0,0xa3,
+0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,
+0x82,0x1f,0x8,0x0,0x3c,0xf5,0x7,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0xae,0x1f,0x2a,0x0,0x1,0xe2,0x2a,0x0,0x37,0x80,0x2c,0x0,0x9b,0x97,0x2c,
+0x0,0x8e,0x7f,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x8a,0x5,0x0,0x7a,0xd,
+0x8,0x0,0x8e,0x1f,0x8,0x0,0x65,0x7,0x8,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1e,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x88,0x1f,0x8,0x0,0x32,
+0x3,0x8,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xbc,0x19,0x2a,0x0,
+0xa6,0x1f,0x2a,0x0,0x54,0xa8,0x2c,0x0,0xe7,0xee,0x2c,0x0,0xf8,0xa7,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x20,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x8b,0x1f,0x8,
+0x0,0x72,0xf7,0x7,0x0,0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x8a,
+0x5,0x0,0x7a,0xd,0x8,0x0,0x78,0x1f,0x8,0x0,0xa3,0xf4,0x7,0x0,0xa3,0x1f,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x8a,0x5,0x0,0xb0,0xa6,0x5,0x0,0xb1,
+0xa6,0x5,0x0,0x5,0xd7,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0x21,0x8a,0x5,0x0,0x7a,0xd,0x8,0x0,0x85,0x1f,0x8,0x0,0xc8,0xdd,0x7,0x0,
+0xa3,0x1f,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x8a,0x5,0x0,0x34,0x8e,0x5,
+0x0,0x3a,0x8e,0x5,0x0,0x9b,0xbd,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2a,0x8a,0x5,0x0,0xb0,0xa6,0x5,0x0,0xb1,0xa6,0x5,0x0,0x98,0xbd,
+0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x82,0x19,0x2a,0x0,0x5b,
+0x1f,0x2a,0x0,0xac,0xcb,0x2a,0x0,0xca,0xef,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x1,0x21,0x8a,0x5,0x0,0xa2,0xa6,0x5,0x0,0xa4,0xa6,0x5,0x0,
+0x35,0xfa,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,0x8a,0x5,
+0x0,0x44,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0x20,0xcf,0x7,0x0,0x7b,0xd,0x8,
+0x0,0x0,0x0,0x0,0x0,0x1,0x82,0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0xbf,0xcb,
+0x2a,0x0,0x64,0xed,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x26,
+0x8a,0x5,0x0,0x37,0x8e,0x5,0x0,0x39,0x8e,0x5,0x0,0x41,0xf3,0x7,0x0,0x7b,
+0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0xab,0x1f,0x2a,0x0,0x28,0xe2,0x2a,0x0,
+0x59,0xa8,0x2c,0x0,0x5c,0xef,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x21,0x8a,0x5,0x0,0x44,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0xe1,0xe7,0x7,
+0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x8a,0x5,0x0,0x33,0x8e,
+0x5,0x0,0x34,0x8e,0x5,0x0,0xef,0x0,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,
+0x0,0x0,0x1,0x22,0x8a,0x5,0x0,0xa4,0xa6,0x5,0x0,0xa6,0xa6,0x5,0x0,0x8c,
+0xf6,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x8a,0x5,0x0,
+0x36,0x8e,0x5,0x0,0x38,0x8e,0x5,0x0,0x68,0xf5,0x7,0x0,0x7b,0xd,0x8,0x0,
+0x0,0x0,0x0,0x0,0x1,0x21,0x8a,0x5,0x0,0x33,0x8e,0x5,0x0,0x34,0x8e,0x5,
+0x0,0x1b,0x5,0x8,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x8a,
+0x5,0x0,0x44,0x8e,0x5,0x0,0x45,0x8e,0x5,0x0,0x33,0xc,0x8,0x0,0x7b,0xd,
+0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x20,0x8a,0x5,0x0,0xa1,0xa6,0x5,0x0,0xa3,
+0xa6,0x5,0x0,0x9a,0xfe,0x7,0x0,0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,
+0x28,0x8a,0x5,0x0,0x34,0x8e,0x5,0x0,0x35,0x8e,0x5,0x0,0xc5,0xee,0x7,0x0,
+0x7b,0xd,0x8,0x0,0x0,0x0,0x0,0x0,0x1,0x81,0x19,0x2a,0x0,0x5b,0x1f,0x2a,
+0x0,0xc4,0xcb,0x2a,0x0,0xee,0xed,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,0x0,0x0,
+0x0,0x1,0xbb,0x19,0x2a,0x0,0xa6,0x1f,0x2a,0x0,0x5e,0xa8,0x2c,0x0,0x4,0xf0,
+0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x81,0x19,0x2a,0x0,0x5b,
+0x1f,0x2a,0x0,0xc9,0xcb,0x2a,0x0,0xd8,0xee,0x2c,0x0,0xd5,0xca,0x2a,0x0,0x0,
+0x0,0x0,0x0,0x1,0xae,0x1f,0x2a,0x0,0xac,0x96,0x2c,0x0,0x62,0xa8,0x2c,0x0,
+0x8f,0xb9,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x81,0x19,0x2a,
+0x0,0x5b,0x1f,0x2a,0x0,0xcd,0xcb,0x2a,0x0,0x7a,0xed,0x2c,0x0,0xd5,0xca,0x2a,
+0x0,0x0,0x0,0x0,0x0,0x1,0xac,0x1f,0x2a,0x0,0x9f,0x96,0x2c,0x0,0x66,0xa8,
+0x2c,0x0,0x35,0xef,0x2c,0x0,0xf8,0xa7,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x82,
+0x19,0x2a,0x0,0x5b,0x1f,0x2a,0x0,0xd0,0xcb,0x2a,0x0,0x96,0xb2,0x2c,0x0,0xd5,
+0xca,0x2a,0x0,0x0,0x0,0x0,0x0,0x1,0x6a,0x89,0x5,0x0,0x33,0x8e,0x5,0x0,
+0x34,0x8e,0x5,0x0,0xa6,0x84,0x7,0x0,0xde,0x85,0x7,0x0,0x0,0x0,0x0,0x0,
+0x1,0x53,0x89,0x5,0x0,0xa5,0xa6,0x5,0x0,0xa6,0xa6,0x5,0x0,0x2f,0x74,0x7,
+0x0,0xdc,0x85,0x7,0x0,0x0,0x0,0x0,0x0,0x1,0x1a,0x66,0x2c,0x0,0x7b,0x3c,
+0x2d,0x0,0x7c,0x3c,0x2d,0x0,0x7c,0x3c,0x2d,0x0,0x78,0x3c,0x2d,0x0,0x0,0x0,
+0x0,0x0,0x1,0xd,0x20,0x2a,0x0,0x5e,0x65,0x2c,0x0,0x16,0x66,0x2c,0x0,0x92,
+0x83,0x2d,0x0,0x5c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x66,0x2c,0x0,
+0x51,0x3c,0x2d,0x0,0x52,0x3c,0x2d,0x0,0x52,0x3c,0x2d,0x0,0x50,0x3c,0x2d,0x0,
+0x0,0x0,0x0,0x0,0x1,0xe2,0x63,0x2c,0x0,0x18,0x65,0x2c,0x0,0x1b,0x66,0x2c,
+0x0,0xce,0x7b,0x2d,0x0,0x18,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x63,
+0x2c,0x0,0xa0,0x64,0x2c,0x0,0xa2,0x64,0x2c,0x0,0xb6,0xc,0x2d,0x0,0xa0,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x66,0x2c,0x0,0x72,0x3c,0x2d,0x0,0x73,
+0x3c,0x2d,0x0,0x73,0x3c,0x2d,0x0,0x71,0x3c,0x2d,0x0,0x0,0x0,0x0,0x0,0x1,
+0xe3,0x63,0x2c,0x0,0x96,0x64,0x2c,0x0,0x23,0x66,0x2c,0x0,0x2b,0x48,0x2d,0x0,
+0x96,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe2,0x63,0x2c,0x0,0xa0,0x64,0x2c,
+0x0,0x21,0x66,0x2c,0x0,0x4,0xe9,0x2c,0x0,0x9f,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0xe3,0x63,0x2c,0x0,0xc8,0x64,0x2c,0x0,0x1e,0x66,0x2c,0x0,0x98,0x1c,
+0x2d,0x0,0xc8,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x63,0x2c,0x0,0x9a,
+0x64,0x2c,0x0,0x8c,0x50,0x2d,0x0,0x4e,0x74,0x2c,0x0,0x80,0x50,0x2d,0x0,0x0,
+0x0,0x0,0x0,0x1,0xe3,0x63,0x2c,0x0,0xa0,0x64,0x2c,0x0,0x22,0x66,0x2c,0x0,
+0xf3,0x82,0x2d,0x0,0xa0,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe2,0x63,0x2c,
+0x0,0x96,0x64,0x2c,0x0,0x22,0x66,0x2c,0x0,0x77,0xd3,0x2c,0x0,0x96,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0xe3,0x63,0x2c,0x0,0xc8,0x64,0x2c,0x0,0x1e,0x66,
+0x2c,0x0,0xc1,0x7e,0x2c,0x0,0xc8,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe3,
+0x63,0x2c,0x0,0xbe,0x64,0x2c,0x0,0x1f,0x66,0x2c,0x0,0x25,0xc,0x2d,0x0,0xbe,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0x91,0x65,0x2c,0x0,
+0x1a,0x66,0x2c,0x0,0x86,0x83,0x2d,0x0,0x91,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xf0,0x63,0x2c,0x0,0x71,0x65,0x2c,0x0,0x16,0x66,0x2c,0x0,0xf9,0x3d,0x2d,
+0x0,0x71,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x90,0x65,
+0x2c,0x0,0x91,0x65,0x2c,0x0,0x34,0xec,0x2c,0x0,0x8f,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0xe0,
+0x46,0x2d,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x64,0x2c,0x0,
+0x81,0x65,0x2c,0x0,0x82,0x65,0x2c,0x0,0x11,0x46,0x2d,0x0,0x81,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x27,0x64,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x12,0x66,0x2c,
+0x0,0xe7,0x56,0x2d,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,
+0x2c,0x0,0x63,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0x4,0xc8,0x2c,0x0,0x63,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x15,
+0x66,0x2c,0x0,0xc9,0x75,0x2d,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x34,0x64,0x2c,0x0,0x9,0x65,0x2c,0x0,0x3a,0x66,0x2c,0x0,0x94,0x44,0x2d,0x0,
+0x9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfb,0x63,0x2c,0x0,0xb4,0x64,0x2c,
+0x0,0x1a,0x66,0x2c,0x0,0xa3,0x55,0x2d,0x0,0xb4,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x27,0x64,0x2c,0x0,0xe1,0x64,0x2c,0x0,0x23,0x66,0x2c,0x0,0x32,0xd1,
+0x2c,0x0,0xe1,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x66,0x2c,0x0,0xc0,
+0x7e,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0x5a,0x47,0x2d,0x0,0xbe,0x7e,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xcf,0x19,0x2a,0x0,0x8,0x20,0x2a,0x0,0xe9,0x3c,0x2d,0x0,
+0x87,0x48,0x2d,0x0,0x1e,0x36,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,
+0x0,0xc6,0x65,0x2c,0x0,0xc8,0x65,0x2c,0x0,0x8f,0xc2,0x2c,0x0,0xc6,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x64,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x20,0x66,
+0x2c,0x0,0x7a,0xc9,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,
+0x64,0x2c,0x0,0x5d,0x65,0x2c,0x0,0x1a,0x66,0x2c,0x0,0xf0,0x82,0x2d,0x0,0x5d,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0x96,0x65,0x2c,0x0,
+0x1b,0x66,0x2c,0x0,0x4,0x48,0x2d,0x0,0x96,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2e,0x64,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,0x0,0xdd,0x2c,
+0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0xe,0x65,
+0x2c,0x0,0x32,0x66,0x2c,0x0,0x98,0x83,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0x45,0x65,0x2c,0x0,0x1f,0x66,0x2c,0x0,0x4c,
+0xc4,0x2c,0x0,0x44,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x64,0x2c,0x0,
+0xdc,0x64,0x2c,0x0,0x25,0x66,0x2c,0x0,0xb0,0x73,0x2c,0x0,0xdc,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0x91,0x70,0x2c,0x0,0x92,0x70,0x2c,
+0x0,0x49,0x48,0x2d,0x0,0x8d,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x19,0x66,
+0x2c,0x0,0xbe,0x7e,0x2c,0x0,0xbf,0x7e,0x2c,0x0,0x9f,0xd0,0x2c,0x0,0xbd,0x7e,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x66,0x2c,0x0,0x4b,0x70,0x2c,0x0,0x4d,
+0x70,0x2c,0x0,0xf7,0x55,0x2d,0x0,0x4a,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2a,0x64,0x2c,0x0,0xdc,0x65,0x2c,0x0,0x15,0x66,0x2c,0x0,0xbf,0x7e,0x2c,0x0,
+0xd8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0xa3,0x6f,0x2c,
+0x0,0xad,0x6f,0x2c,0x0,0x77,0x45,0x2d,0x0,0xa4,0x6f,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x22,0x64,0x2c,0x0,0xdb,0x65,0x2c,0x0,0xdc,0x65,0x2c,0x0,0xf6,0xc3,
+0x2c,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x64,0x2c,0x0,0x54,
+0x65,0x2c,0x0,0x1b,0x66,0x2c,0x0,0xab,0x1a,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,
+0xab,0xd,0x2d,0x0,0x4f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,
+0x0,0xcc,0x65,0x2c,0x0,0x21,0x66,0x2c,0x0,0xaa,0x3d,0x2d,0x0,0xcc,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x19,0x66,
+0x2c,0x0,0xff,0xc4,0x2c,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,
+0x64,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x27,0x66,0x2c,0x0,0xd9,0xd1,0x2c,0x0,0x8b,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xe,0x20,0x2a,0x0,0x85,0x2e,0x2b,0x0,
+0xee,0x3c,0x2d,0x0,0x87,0x83,0x2d,0x0,0x1d,0x36,0x2b,0x0,0x0,0x0,0x0,0x0,
+0x1,0x24,0x64,0x2c,0x0,0x13,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,0xac,0xd4,0x2c,
+0x0,0x13,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0x5,0x65,
+0x2c,0x0,0x32,0x66,0x2c,0x0,0x87,0x83,0x2d,0x0,0x5,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1d,0x64,0x2c,0x0,0xc3,0x64,0x2c,0x0,0x2a,0x66,0x2c,0x0,0xfc,
+0x82,0x2d,0x0,0xc3,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,
+0xbf,0x65,0x2c,0x0,0x14,0x66,0x2c,0x0,0x56,0x47,0x2d,0x0,0xbd,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xa0,0x65,0x2c,0x0,0x15,0x66,0x2c,
+0x0,0xe6,0xc,0x2d,0x0,0x9f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,
+0x2c,0x0,0x9a,0x65,0x2c,0x0,0x9b,0x65,0x2c,0x0,0x40,0x46,0x2d,0x0,0x9a,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x9d,0x65,0x2c,0x0,0x9f,
+0x65,0x2c,0x0,0x69,0xcf,0x2c,0x0,0x9d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x30,0x64,0x2c,0x0,0x7c,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0x7e,0x3d,0x2d,0x0,
+0x7c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0xff,0x64,0x2c,
+0x0,0x1f,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0xff,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x29,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x27,0x66,0x2c,0x0,0x10,0xeb,
+0x2c,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfb,0x63,0x2c,0x0,0xb4,
+0x64,0x2c,0x0,0x1a,0x66,0x2c,0x0,0x8b,0x76,0x2d,0x0,0xb4,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x24,0x64,0x2c,0x0,0x1d,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,
+0x8e,0x3d,0x2d,0x0,0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,
+0x0,0xea,0x65,0x2c,0x0,0x20,0x66,0x2c,0x0,0x4c,0x45,0x2d,0x0,0xea,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x64,0x2c,0x0,0x45,0x65,0x2c,0x0,0x1a,0x66,
+0x2c,0x0,0xe5,0xc8,0x2c,0x0,0x45,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,
+0x64,0x2c,0x0,0xb8,0x65,0x2c,0x0,0xba,0x65,0x2c,0x0,0xbd,0xdd,0x2c,0x0,0xb8,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xff,0x64,0x2c,0x0,
+0x31,0x66,0x2c,0x0,0x7a,0xe2,0x2c,0x0,0xff,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x30,0x64,0x2c,0x0,0x77,0x65,0x2c,0x0,0x78,0x65,0x2c,0x0,0xba,0x47,0x2d,
+0x0,0x77,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0x64,0x2c,0x0,0x97,0x65,
+0x2c,0x0,0x98,0x65,0x2c,0x0,0x6b,0x83,0x2d,0x0,0x97,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0xe,0x65,0x2c,0x0,0x20,0x66,0x2c,0x0,0x46,
+0x3c,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x32,0x64,0x2c,0x0,
+0x36,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0x5a,0x44,0x2d,0x0,0x35,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x1b,0x66,0x2c,
+0x0,0xb0,0xd3,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xce,0x19,
+0x2a,0x0,0x8,0x20,0x2a,0x0,0xf2,0x3c,0x2d,0x0,0x10,0x44,0x2d,0x0,0x19,0x36,
+0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x64,0x2c,0x0,0x12,0x65,0x2c,0x0,0x36,
+0x66,0x2c,0x0,0x95,0x83,0x2d,0x0,0x11,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x27,0x64,0x2c,0x0,0x81,0x65,0x2c,0x0,0x17,0x66,0x2c,0x0,0x6c,0x76,0x2d,0x0,
+0x81,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x18,0x65,0x2c,
+0x0,0x30,0x66,0x2c,0x0,0xbb,0xd,0x2d,0x0,0x18,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x29,0x64,0x2c,0x0,0x60,0x65,0x2c,0x0,0x1a,0x66,0x2c,0x0,0x87,0x83,
+0x2d,0x0,0x5e,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0xd9,
+0x64,0x2c,0x0,0xda,0x64,0x2c,0x0,0x83,0x44,0x2d,0x0,0xd7,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x21,0x64,0x2c,0x0,0x9f,0x65,0x2c,0x0,0x17,0x66,0x2c,0x0,
+0x87,0x83,0x2d,0x0,0x9f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,
+0x0,0x18,0x65,0x2c,0x0,0x1d,0x66,0x2c,0x0,0xbd,0x1c,0x2d,0x0,0x18,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0x96,0x65,0x2c,0x0,0x25,0x66,
+0x2c,0x0,0x87,0x83,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf9,
+0x63,0x2c,0x0,0x1d,0x65,0x2c,0x0,0x18,0x66,0x2c,0x0,0xef,0x82,0x2d,0x0,0x1d,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0xf2,0x65,0x2c,0x0,
+0xf3,0x65,0x2c,0x0,0xd7,0x3d,0x2d,0x0,0xf2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x31,0x64,0x2c,0x0,0x72,0x65,0x2c,0x0,0x73,0x65,0x2c,0x0,0xe7,0xc4,0x2c,
+0x0,0x72,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0x3b,0x65,
+0x2c,0x0,0x1b,0x66,0x2c,0x0,0x76,0xc,0x2d,0x0,0x3b,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0xf0,0x64,0x2c,0x0,0x28,0x66,0x2c,0x0,0x92,
+0x83,0x2d,0x0,0xf0,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x64,0x2c,0x0,
+0xcf,0x64,0x2c,0x0,0x38,0x66,0x2c,0x0,0x24,0xc5,0x2c,0x0,0xcf,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xae,0x65,0x2c,0x0,0x24,0x66,0x2c,
+0x0,0xc8,0x46,0x2d,0x0,0xae,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,
+0x2c,0x0,0x6d,0x65,0x2c,0x0,0x6e,0x65,0x2c,0x0,0x1a,0x75,0x2c,0x0,0x6d,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x20,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x1a,
+0x66,0x2c,0x0,0x86,0x83,0x2d,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1e,0x64,0x2c,0x0,0xd2,0x65,0x2c,0x0,0x12,0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,
+0xd2,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xcc,0x65,0x2c,
+0x0,0x21,0x66,0x2c,0x0,0x8,0x48,0x2d,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x22,0x64,0x2c,0x0,0x27,0x65,0x2c,0x0,0x1f,0x66,0x2c,0x0,0xc1,0x7e,
+0x2c,0x0,0x27,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x21,0x64,0x2c,0x0,0x6d,
+0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0x87,0x83,0x2d,0x0,0x6d,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xcd,0x19,0x2a,0x0,0x8,0x20,0x2a,0x0,0xf7,0x3c,0x2d,0x0,
+0x49,0x44,0x2d,0x0,0x20,0x36,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,
+0x0,0x54,0x65,0x2c,0x0,0x55,0x65,0x2c,0x0,0xbd,0x48,0x2d,0x0,0x54,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x23,0x66,
+0x2c,0x0,0xb2,0x44,0x2d,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,
+0x64,0x2c,0x0,0xbd,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,0x9,0x70,0x2c,0x0,0xbd,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0xe6,0x64,0x2c,0x0,
+0x25,0x66,0x2c,0x0,0x56,0xc4,0x2c,0x0,0xe6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2d,0x64,0x2c,0x0,0xa6,0x65,0x2c,0x0,0xa8,0x65,0x2c,0x0,0xdd,0xc,0x2d,
+0x0,0xa4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xe,0x65,
+0x2c,0x0,0x1f,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x2b,0x66,0x2c,0x0,0x4d,
+0xd2,0x2c,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,
+0xf0,0x64,0x2c,0x0,0x3a,0x66,0x2c,0x0,0xf0,0x82,0x2d,0x0,0xf0,0x64,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0xf9,0x63,0x2c,0x0,0xb9,0x64,0x2c,0x0,0x19,0x66,0x2c,
+0x0,0x65,0xd1,0x2c,0x0,0xb9,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,
+0x2c,0x0,0x8b,0x65,0x2c,0x0,0x1b,0x66,0x2c,0x0,0xd1,0x7b,0x2d,0x0,0x8b,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x14,0x66,0x2c,0x0,0x44,0x70,0x2c,0x0,0x45,
+0x70,0x2c,0x0,0xf0,0x82,0x2d,0x0,0x43,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x29,0x64,0x2c,0x0,0x5e,0x65,0x2c,0x0,0x19,0x66,0x2c,0x0,0x94,0x83,0x2d,0x0,
+0x5d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x8b,0x65,0x2c,
+0x0,0x8c,0x65,0x2c,0x0,0x58,0xc9,0x2c,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2f,0x64,0x2c,0x0,0x59,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0xf8,0x79,
+0x2c,0x0,0x59,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0x4f,
+0x65,0x2c,0x0,0x22,0x66,0x2c,0x0,0xb6,0x77,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0x4a,0x65,0x2c,0x0,0x1d,0x66,0x2c,0x0,
+0x8d,0x3c,0x2d,0x0,0x4a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,
+0x0,0x31,0x65,0x2c,0x0,0x32,0x65,0x2c,0x0,0xdf,0xc2,0x2c,0x0,0x31,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x28,0x66,
+0x2c,0x0,0x4c,0x46,0x2d,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,
+0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x24,0x45,0x2d,0x0,0x9a,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x90,0x70,0x2c,0x0,
+0x91,0x70,0x2c,0x0,0xd8,0xc,0x2d,0x0,0x8d,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0xcd,0x19,0x2a,0x0,0x8,0x20,0x2a,0x0,0xfc,0x3c,0x2d,0x0,0xee,0x48,0x2d,
+0x0,0x1d,0x36,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0x64,0x2c,0x0,0xfe,0x65,
+0x2c,0x0,0xff,0x65,0x2c,0x0,0xa3,0xd5,0x2c,0x0,0xfe,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0xa9,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,0xbf,
+0x7e,0x2c,0x0,0xa9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,
+0x13,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0xf6,0x48,0x2d,0x0,0x13,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0x27,0x65,0x2c,0x0,0x22,0x66,0x2c,
+0x0,0x92,0x83,0x2d,0x0,0x27,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,
+0x2c,0x0,0xfa,0x64,0x2c,0x0,0xfb,0x64,0x2c,0x0,0xde,0x48,0x2d,0x0,0xfa,0x64,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0xe7,0x64,0x2c,0x0,0x21,
+0x66,0x2c,0x0,0x7a,0x75,0x2c,0x0,0xe7,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x25,0x64,0x2c,0x0,0xe0,0x65,0x2c,0x0,0x20,0x66,0x2c,0x0,0x8b,0x83,0x2d,0x0,
+0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf4,0x63,0x2c,0x0,0xa5,0x64,0x2c,
+0x0,0x1a,0x66,0x2c,0x0,0x8b,0x83,0x2d,0x0,0xa5,0x64,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x28,0x64,0x2c,0x0,0x36,0x65,0x2c,0x0,0x22,0x66,0x2c,0x0,0xe8,0x72,
+0x2c,0x0,0x35,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xab,
+0x65,0x2c,0x0,0xac,0x65,0x2c,0x0,0x65,0x83,0x2d,0x0,0xab,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x25,0x64,0x2c,0x0,0xa9,0x65,0x2c,0x0,0x14,0x66,0x2c,0x0,
+0x1d,0xd2,0x2c,0x0,0xa9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,
+0x0,0xb2,0x65,0x2c,0x0,0x22,0x66,0x2c,0x0,0x7f,0x45,0x2d,0x0,0xb2,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0x54,0x65,0x2c,0x0,0x1a,0x66,
+0x2c,0x0,0xdd,0x56,0x2d,0x0,0x54,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,
+0x64,0x2c,0x0,0x45,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,0xcd,0x75,0x2d,0x0,0x45,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x92,0x1f,0x8,0x0,0x6b,0x2a,0x8,0x0,
+0x6f,0x2a,0x8,0x0,0x8e,0x5,0x8,0x0,0x0,0x38,0x8,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1c,0x64,0x2c,0x0,0xdc,0x64,0x2c,0x0,0x22,0x66,0x2c,0x0,0x9a,0x6f,0x2c,
+0x0,0xdc,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0x40,0x65,
+0x2c,0x0,0x2f,0x66,0x2c,0x0,0xbd,0x7e,0x2c,0x0,0x40,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xce,0x19,0x2a,0x0,0x8,0x20,0x2a,0x0,0x1,0x3d,0x2d,0x0,0x26,
+0x49,0x2d,0x0,0x1c,0x36,0x2b,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,
+0xe0,0x65,0x2c,0x0,0xe1,0x65,0x2c,0x0,0xd9,0x75,0x2d,0x0,0xe0,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xd6,0x65,0x2c,0x0,0x20,0x66,0x2c,
+0x0,0x8a,0x7b,0x2d,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x64,
+0x2c,0x0,0x9a,0x65,0x2c,0x0,0x9b,0x65,0x2c,0x0,0x52,0xc6,0x2c,0x0,0x9a,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,0x13,
+0x66,0x2c,0x0,0xe1,0x1b,0x2d,0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0xfb,0x63,0x2c,0x0,0xcd,0x64,0x2c,0x0,0x27,0x66,0x2c,0x0,0x65,0x73,0x2c,0x0,
+0xcd,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1b,0x66,0x2c,0x0,0x2f,0x66,0x2c,
+0x0,0x4c,0x66,0x2c,0x0,0x9d,0x18,0x2d,0x0,0x4b,0x66,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x31,0x64,0x2c,0x0,0xf9,0x65,0x2c,0x0,0x12,0x66,0x2c,0x0,0xd9,0x55,
+0x2d,0x0,0xf9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x95,
+0x65,0x2c,0x0,0x24,0x66,0x2c,0x0,0xcf,0x75,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1f,0x64,0x2c,0x0,0xe6,0x64,0x2c,0x0,0x25,0x66,0x2c,0x0,
+0x8,0xc4,0x2c,0x0,0xe6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,
+0x0,0xef,0x65,0x2c,0x0,0x11,0x66,0x2c,0x0,0xc0,0x7e,0x2c,0x0,0xef,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0x65,0x65,0x2c,0x0,0x1a,0x66,
+0x2c,0x0,0x7e,0x70,0x2c,0x0,0x65,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,
+0x64,0x2c,0x0,0xd1,0x65,0x2c,0x0,0x12,0x66,0x2c,0x0,0xf6,0xd0,0x2c,0x0,0xd1,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,
+0x14,0x66,0x2c,0x0,0x18,0xc8,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x29,0x64,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x14,0x66,0x2c,0x0,0xb0,0xd,0x2d,
+0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xf5,0x63,0x2c,0x0,0xaf,0x64,
+0x2c,0x0,0x18,0x66,0x2c,0x0,0xcc,0x79,0x2c,0x0,0xaf,0x64,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,0xeb,0x65,0x2c,0x0,0x10,0x66,0x2c,0x0,0xab,
+0x57,0x2d,0x0,0xea,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,
+0xa1,0x65,0x2c,0x0,0x15,0x66,0x2c,0x0,0x5a,0xd,0x2d,0x0,0x9f,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1e,0x64,0x2c,0x0,0x5c,0x65,0x2c,0x0,0x19,0x66,0x2c,
+0x0,0x7,0x55,0x2d,0x0,0x5c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x64,
+0x2c,0x0,0xae,0x65,0x2c,0x0,0xaf,0x65,0x2c,0x0,0xc1,0x7e,0x2c,0x0,0xae,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x24,0x64,0x2c,0x0,0x2e,0x65,0x2c,0x0,0x1f,
+0x66,0x2c,0x0,0x9f,0x7b,0x2d,0x0,0x2e,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x20,0x64,0x2c,0x0,0x12,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,0x52,0x49,0x2d,0x0,
+0x12,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x11,0x66,0x2c,0x0,0xc0,0x7e,0x2c,
+0x0,0xc2,0x7e,0x2c,0x0,0xe,0x46,0x2d,0x0,0xbe,0x7e,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x25,0x64,0x2c,0x0,0xe4,0x65,0x2c,0x0,0xe5,0x65,0x2c,0x0,0x95,0x83,
+0x2d,0x0,0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x23,0x64,0x2c,0x0,0xf0,
+0x64,0x2c,0x0,0x26,0x66,0x2c,0x0,0xfc,0x76,0x2d,0x0,0xf0,0x64,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xc5,0x65,0x2c,0x0,0xc6,0x65,0x2c,0x0,
+0xf0,0x82,0x2d,0x0,0xc4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x64,0x2c,
+0x0,0x54,0x65,0x2c,0x0,0x1b,0x66,0x2c,0x0,0x23,0xeb,0x2c,0x0,0x54,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x27,0x64,0x2c,0x0,0xa7,0x64,0x2c,0x0,0xa8,0x64,
+0x2c,0x0,0xce,0xc,0x2d,0x0,0xa7,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x1f,
+0x64,0x2c,0x0,0x18,0x65,0x2c,0x0,0x20,0x66,0x2c,0x0,0x87,0x83,0x2d,0x0,0x18,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x36,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,
+0x9c,0x65,0x2c,0x0,0x75,0x3e,0x2d,0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x33,0x64,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x9b,0x65,0x2c,0x0,0x28,0xdf,0x2c,
+0x0,0x9a,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x36,0x65,
+0x2c,0x0,0x3c,0x66,0x2c,0x0,0x92,0x83,0x2d,0x0,0x36,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x3e,0x66,0x2c,0x0,0x4d,0x70,0x2c,0x0,0x4f,0x70,0x2c,0x0,0x24,
+0xd1,0x2c,0x0,0x4a,0x70,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,
+0x4,0x65,0x2c,0x0,0x3c,0x66,0x2c,0x0,0x8,0x4f,0x2d,0x0,0x4,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x7b,0x5c,0x8,0x0,0xc9,0xe5,0x8,0x0,0xca,0xe5,0x8,
+0x0,0x50,0x36,0x9,0x0,0x64,0x67,0x9,0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x64,
+0x2c,0x0,0xe0,0x65,0x2c,0x0,0xe1,0x65,0x2c,0x0,0x1,0x79,0x2c,0x0,0xe0,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfe,0x63,0x2c,0x0,0x95,0x65,0x2c,0x0,0x2a,
+0x66,0x2c,0x0,0xbd,0x7e,0x2c,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2c,0x64,0x2c,0x0,0x9,0x65,0x2c,0x0,0x38,0x66,0x2c,0x0,0xec,0x56,0x2d,0x0,
+0x9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xef,0x65,0x2c,
+0x0,0xf0,0x65,0x2c,0x0,0x9f,0xd3,0x2c,0x0,0xef,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x3a,0x64,0x2c,0x0,0x72,0x65,0x2c,0x0,0x33,0x66,0x2c,0x0,0x94,0x83,
+0x2d,0x0,0x72,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0x9a,
+0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0x9f,0xd4,0x2c,0x0,0x9a,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0xb3,0x65,0x2c,0x0,0xb4,0x65,0x2c,0x0,
+0xce,0x1c,0x2d,0x0,0xb3,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x86,0x2a,0x8,
+0x0,0xc8,0xe5,0x8,0x0,0xca,0xe5,0x8,0x0,0xd8,0x5a,0x9,0x0,0x5f,0x67,0x9,
+0x0,0x0,0x0,0x0,0x0,0x1,0xfe,0x63,0x2c,0x0,0xe,0x65,0x2c,0x0,0x2a,0x66,
+0x2c,0x0,0x7d,0x3c,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,
+0x64,0x2c,0x0,0xaa,0x65,0x2c,0x0,0xab,0x65,0x2c,0x0,0x94,0x83,0x2d,0x0,0xaa,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2d,0x64,0x2c,0x0,0xaf,0x65,0x2c,0x0,
+0xb0,0x65,0x2c,0x0,0x9c,0x70,0x2c,0x0,0xae,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2a,0x64,0x2c,0x0,0xe3,0x65,0x2c,0x0,0x24,0x66,0x2c,0x0,0x29,0x76,0x2d,
+0x0,0xe0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xbf,0x65,
+0x2c,0x0,0xc0,0x65,0x2c,0x0,0x87,0x83,0x2d,0x0,0xbd,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x2d,0x64,0x2c,0x0,0x93,0x66,0x2c,0x0,0xb5,0x66,0x2c,0x0,0x48,
+0x53,0x2d,0x0,0xb4,0x66,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2a,0x64,0x2c,0x0,
+0x86,0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0xbf,0x7e,0x2c,0x0,0x86,0x65,0x2c,0x0,
+0x0,0x0,0x0,0x0,0x1,0x8b,0x2a,0x8,0x0,0x73,0x62,0x9,0x0,0x74,0x62,0x9,
+0x0,0x74,0x62,0x9,0x0,0x66,0x67,0x9,0x0,0x0,0x0,0x0,0x0,0x1,0x3a,0x64,
+0x2c,0x0,0x90,0x65,0x2c,0x0,0x2c,0x66,0x2c,0x0,0xf0,0x82,0x2d,0x0,0x90,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0xb8,0x65,0x2c,0x0,0xb9,
+0x65,0x2c,0x0,0x1d,0x7b,0x2d,0x0,0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x27,0x64,0x2c,0x0,0x22,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0x5,0x56,0x2d,0x0,
+0x22,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0x8f,0x65,0x2c,
+0x0,0x2c,0x66,0x2c,0x0,0x91,0x83,0x2d,0x0,0x8f,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x31,0x64,0x2c,0x0,0x1d,0x65,0x2c,0x0,0x3c,0x66,0x2c,0x0,0x8d,0x83,
+0x2d,0x0,0x1d,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xda,
+0x65,0x2c,0x0,0x27,0x66,0x2c,0x0,0x62,0x6f,0x2c,0x0,0xd8,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xdb,0x65,0x2c,0x0,0x23,0x66,0x2c,0x0,
+0xc7,0x76,0x2d,0x0,0xdb,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x89,0x2a,0x8,
+0x0,0x84,0x62,0x9,0x0,0x85,0x62,0x9,0x0,0x91,0x47,0x9,0x0,0x6a,0x67,0x9,
+0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0x63,0x65,0x2c,0x0,0x33,0x66,
+0x2c,0x0,0xaa,0x56,0x2d,0x0,0x63,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,
+0x64,0x2c,0x0,0xef,0x65,0x2c,0x0,0xf0,0x65,0x2c,0x0,0xb3,0xed,0x2c,0x0,0xef,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0xe6,0x64,0x2c,0x0,
+0x3e,0x66,0x2c,0x0,0xa2,0x70,0x2c,0x0,0xe6,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x33,0x64,0x2c,0x0,0x9,0x65,0x2c,0x0,0x3c,0x66,0x2c,0x0,0xc4,0xe2,0x2c,
+0x0,0x9,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0x8b,0x65,
+0x2c,0x0,0x8c,0x65,0x2c,0x0,0x8e,0x48,0x2d,0x0,0x8b,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x95,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0xe1,
+0x43,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x88,0x2a,0x8,0x0,
+0x87,0x62,0x9,0x0,0x88,0x62,0x9,0x0,0x8f,0x47,0x9,0x0,0x63,0x67,0x9,0x0,
+0x0,0x0,0x0,0x0,0x1,0x2c,0x64,0x2c,0x0,0xc1,0x65,0x2c,0x0,0xc2,0x65,0x2c,
+0x0,0x94,0x83,0x2d,0x0,0xc0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfd,0x63,
+0x2c,0x0,0x9c,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0x6,0x49,0x2d,0x0,0x9c,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0x72,0x65,0x2c,0x0,0x32,
+0x66,0x2c,0x0,0x5b,0x7c,0x2d,0x0,0x72,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x2c,0x64,0x2c,0x0,0x81,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0x78,0x3c,0x2d,0x0,
+0x81,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x81,0x65,0x2c,
+0x0,0x82,0x65,0x2c,0x0,0x17,0x48,0x2d,0x0,0x81,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x3d,0x64,0x2c,0x0,0xa4,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0xc2,0x7e,
+0x2c,0x0,0xa4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x95,
+0x65,0x2c,0x0,0x2f,0x66,0x2c,0x0,0xb,0x41,0x2d,0x0,0x95,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x89,0x2a,0x8,0x0,0xc8,0xe5,0x8,0x0,0xca,0xe5,0x8,0x0,
+0xb7,0x5c,0x9,0x0,0x6a,0x67,0x9,0x0,0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,
+0x0,0x91,0x65,0x2c,0x0,0x92,0x65,0x2c,0x0,0xc7,0xe9,0x2c,0x0,0x90,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x3b,0x64,0x2c,0x0,0x79,0x65,0x2c,0x0,0x30,0x66,
+0x2c,0x0,0xb2,0x75,0x2d,0x0,0x79,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,
+0x64,0x2c,0x0,0x4a,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0xcb,0x45,0x2d,0x0,0x4a,
+0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2e,0x64,0x2c,0x0,0xd6,0x65,0x2c,0x0,
+0x25,0x66,0x2c,0x0,0x4f,0x76,0x2d,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2c,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,0xc8,0x65,0x2c,0x0,0x36,0x3d,0x2d,
+0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x39,0x64,0x2c,0x0,0x82,0x65,
+0x2c,0x0,0x83,0x65,0x2c,0x0,0xe,0x57,0x2d,0x0,0x82,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x61,0x42,0x6,0x0,0x6d,0x42,0x6,0x0,0x80,0x42,0x6,0x0,0x0,
+0x0,0x0,0x0,0xa7,0x42,0x6,0x0,0x0,0x0,0x0,0x0,0x1,0x87,0x2a,0x8,0x0,
+0x76,0x62,0x9,0x0,0x77,0x62,0x9,0x0,0xd0,0x51,0x9,0x0,0x6a,0x67,0x9,0x0,
+0x0,0x0,0x0,0x0,0x1,0x29,0x64,0x2c,0x0,0xf0,0x64,0x2c,0x0,0x3c,0x66,0x2c,
+0x0,0x42,0x3c,0x2d,0x0,0xf0,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2c,0x64,
+0x2c,0x0,0xef,0x65,0x2c,0x0,0x24,0x66,0x2c,0x0,0x95,0x83,0x2d,0x0,0xef,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x33,0x64,0x2c,0x0,0x68,0x65,0x2c,0x0,0x31,
+0x66,0x2c,0x0,0xb5,0x47,0x2d,0x0,0x68,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x29,0x64,0x2c,0x0,0xb8,0x65,0x2c,0x0,0x28,0x66,0x2c,0x0,0x8f,0x4e,0x2d,0x0,
+0xb8,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x26,0x64,0x2c,0x0,0x9,0x65,0x2c,
+0x0,0x39,0x66,0x2c,0x0,0x67,0xd3,0x2c,0x0,0x9,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x2e,0x64,0x2c,0x0,0x4f,0x65,0x2c,0x0,0x36,0x66,0x2c,0x0,0xc1,0x3f,
+0x2d,0x0,0x4f,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0xd1,
+0x65,0x2c,0x0,0xd2,0x65,0x2c,0x0,0x38,0x48,0x2d,0x0,0xd1,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0x88,0x2a,0x8,0x0,0x71,0x62,0x9,0x0,0x72,0x62,0x9,0x0,
+0xfb,0x4a,0x9,0x0,0x6a,0x67,0x9,0x0,0x0,0x0,0x0,0x0,0x1,0x4c,0x64,0x2c,
+0x0,0xe0,0x65,0x2c,0x0,0xe1,0x65,0x2c,0x0,0xf8,0xc7,0x2c,0x0,0xe0,0x65,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x22,0x64,0x2c,0x0,0xfe,0x65,0x2c,0x0,0x1f,0x66,
+0x2c,0x0,0x61,0xe,0x2d,0x0,0xfe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0xfa,
+0x63,0x2c,0x0,0xb9,0x64,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x66,0xdd,0x2c,0x0,0xb9,
+0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0xab,0x65,0x2c,0x0,
+0x27,0x66,0x2c,0x0,0xf1,0x3d,0x2d,0x0,0xab,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x2d,0x64,0x2c,0x0,0xc7,0x65,0x2c,0x0,0xc8,0x65,0x2c,0x0,0xf0,0x82,0x2d,
+0x0,0xc7,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0x90,0x65,
+0x2c,0x0,0x2f,0x66,0x2c,0x0,0x61,0x73,0x2c,0x0,0x8f,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0xfe,0x63,0x2c,0x0,0x54,0x65,0x2c,0x0,0x29,0x66,0x2c,0x0,0x7f,
+0x17,0x2d,0x0,0x52,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x89,0x2a,0x8,0x0,
+0x72,0x62,0x9,0x0,0x73,0x62,0x9,0x0,0x4c,0x36,0x9,0x0,0x6a,0x67,0x9,0x0,
+0x0,0x0,0x0,0x0,0x1,0x31,0x64,0x2c,0x0,0xd7,0x65,0x2c,0x0,0xd8,0x65,0x2c,
+0x0,0xfb,0x55,0x2d,0x0,0xd6,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,
+0x2c,0x0,0x90,0x65,0x2c,0x0,0x91,0x65,0x2c,0x0,0xb2,0x7b,0x2d,0x0,0x90,0x65,
+0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x28,0x64,0x2c,0x0,0x13,0x65,0x2c,0x0,0x3b,
+0x66,0x2c,0x0,0x66,0x76,0x2d,0x0,0x13,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,
+0x94,0x2a,0x8,0x0,0xca,0xe5,0x8,0x0,0xcb,0xe5,0x8,0x0,0x1,0x37,0x9,0x0,
+0x67,0x67,0x9,0x0,0x0,0x0,0x0,0x0,0x1,0x2b,0x64,0x2c,0x0,0xe,0x65,0x2c,
+0x0,0x37,0x66,0x2c,0x0,0x70,0x70,0x2c,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,0x0,
+0x0,0x1,0x33,0x64,0x2c,0x0,0xc2,0x65,0x2c,0x0,0x2e,0x66,0x2c,0x0,0xb3,0xd4,
+0x2c,0x0,0xc0,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x30,0x64,0x2c,0x0,0x5d,
+0x65,0x2c,0x0,0x38,0x66,0x2c,0x0,0x73,0x3c,0x2d,0x0,0x5d,0x65,0x2c,0x0,0x0,
+0x0,0x0,0x0,0x1,0xea,0x63,0x2c,0x0,0x9b,0x64,0x2c,0x0,0x49,0x66,0x2c,0x0,
+0xef,0x82,0x2d,0x0,0x9b,0x64,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,
+0x0,0xcd,0x64,0x2c,0x0,0xce,0x64,0x2c,0x0,0xb9,0xec,0x2c,0x0,0xcd,0x64,0x2c,
+0x0,0x0,0x0,0x0,0x0,0x1,0x8,0x64,0x2c,0x0,0x4c,0x65,0x2c,0x0,0x33,0x66,
+0x2c,0x0,0x7a,0x3c,0x2d,0x0,0x4c,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x95,
+0x2a,0x8,0x0,0xc8,0xe5,0x8,0x0,0xc9,0xe5,0x8,0x0,0xee,0x52,0x9,0x0,0x6a,
+0x67,0x9,0x0,0x0,0x0,0x0,0x0,0x1,0x2f,0x64,0x2c,0x0,0xe,0x65,0x2c,0x0,
+0x3d,0x66,0x2c,0x0,0x50,0x3c,0x2d,0x0,0xe,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,
+0x1,0x26,0x64,0x2c,0x0,0x90,0x65,0x2c,0x0,0x2d,0x66,0x2c,0x0,0x7d,0x3c,0x2d,
+0x0,0x90,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x35,0x64,0x2c,0x0,0xcc,0x65,
+0x2c,0x0,0xcd,0x65,0x2c,0x0,0x4d,0x70,0x2c,0x0,0xcc,0x65,0x2c,0x0,0x0,0x0,
+0x0,0x0,0x1,0x34,0x64,0x2c,0x0,0xf9,0x65,0x2c,0x0,0xfa,0x65,0x2c,0x0,0xbf,
+0x7e,0x2c,0x0,0xf4,0x65,0x2c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,
+0x5,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb8,0x1a,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xd0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x21,0x60,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x26,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,
+0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x6a,0x84,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x96,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x48,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,0xf3,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5d,0xa2,0x65,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5f,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0x32,0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x99,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x1a,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0x52,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,
+0x54,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xe7,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x48,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,
+0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe2,0x27,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x91,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0xe4,0x74,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xbc,0x3,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xac,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xdf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x91,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x86,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,
+0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0xe4,0x74,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x94,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xb6,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7a,0x6,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x50,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x24,0x6a,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0xc4,0x1f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0xd5,0x2b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x90,0x73,0x4c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,
+0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x48,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb2,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0xf0,0x51,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0x4a,
+0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xe0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x36,0xb7,0x7f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x2b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0xf7,0x67,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xc4,0x1f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6,0x8c,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,
+0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x18,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x3c,0x3f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7b,0x7c,0x86,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,
+0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xdf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa3,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,
+0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x2c,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x84,0x59,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x56,0xad,
+0x5c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa2,0x32,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb5,0xb3,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb1,0xb3,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf7,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x34,0x6b,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcf,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x30,0x36,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xc6,0x68,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,
+0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xe6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xad,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,
+0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe4,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb0,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0xb3,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0xb3,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xce,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9a,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,
+0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x62,0x57,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa,0x4b,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0x5e,0x56,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0xb3,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf5,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x1d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x75,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x74,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x97,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0x8b,0x8,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf1,0x4a,0x3a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,
+0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xd9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3f,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,
+0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe9,0x26,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xca,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xef,0x6b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x8d,
+0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xe6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4f,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,
+0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa9,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,
+0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x8c,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0xc8,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x27,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xdf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3c,0x3e,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,
+0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcf,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xb3,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x8d,0x33,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,
+0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xdb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdb,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x39,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa7,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x15,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0xbc,
+0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9d,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x99,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,
+0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0xe4,0x74,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x48,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x81,0xb3,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9f,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa1,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,
+0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0xf5,0x37,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xf0,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x88,0xb3,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0xb3,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,
+0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xcf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa5,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,
+0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x83,0x5a,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x90,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0x86,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x86,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb0,0x86,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xcf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa7,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,
+0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9f,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,
+0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,0x9d,0x50,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x9d,0x50,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x86,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xba,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xac,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,
+0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc0,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x86,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xad,0x5c,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,
+0xb7,0x7f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xe6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x48,0xc6,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,
+0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf3,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x91,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x86,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0xc7,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xb5,
+0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xe0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe1,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x32,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xae,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0x86,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x86,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb8,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xbe,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,
+0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe7,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0x5a,0x41,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0x5b,0x41,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,
+0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xe6,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xaa,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x98,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdc,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0x7e,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3c,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,
+0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x2e,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x95,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xed,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x51,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0xb1,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd4,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,
+0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc9,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xc7,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0xe8,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,
+0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xdf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3d,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,
+0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf5,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa3,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0x15,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xa4,0x58,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xef,
+0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,
+0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x59,0xb7,0x7f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xc8,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd9,0xf7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf4,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x34,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa5,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x57,0x54,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0x45,0x7c,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,
+0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb,0x75,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,
+0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xca,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcd,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x4b,0x3a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xef,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xdf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x53,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,
+0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x69,0x57,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0xc7,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xce,0x17,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb1,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xd2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x39,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0xff,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa0,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x8d,0x33,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0x4a,0x3a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,
+0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x42,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,
+0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfd,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xab,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0xad,0x5c,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x8,
+0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xdb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x11,0x75,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x60,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd,0x51,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0xc8,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xde,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8d,0xc0,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,
+0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbe,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0xe8,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x57,0x3f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,
+0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4f,0x72,0xa,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,
+0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xcf,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe1,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0xc4,0x1f,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x74,
+0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xe6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe5,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,
+0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x2f,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xb,0x5b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0xb6,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xfa,0x28,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xe3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb,0x99,0x6d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,
+0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x76,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc9,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,0x59,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x36,0x78,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,
+0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x19,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x80,0x7b,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,
+0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x1,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd7,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xb6,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x62,
+0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x86,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd6,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,
+0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x8a,0x6d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x60,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xd1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4c,0xf4,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,
+0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xb7,0x8a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x91,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0x78,0x26,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xbc,0x3,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,
+0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xd5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3e,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x54,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x91,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x6,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x15,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0xc7,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x1d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xff,0x47,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,
+0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xea,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,
+0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0x27,0x75,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0xb2,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x62,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x45,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa9,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xb6,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x90,0x1d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,
+0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xd8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,
+0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x99,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe1,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x6b,0x75,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0x3,0x61,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0x1a,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xd0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x65,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x7e,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,
+0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x8a,0x6d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x93,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0x76,0x4a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0x79,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd9,0x27,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xd1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x62,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,
+0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xaa,0xf,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x48,0x3b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,0x51,0x1,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,
+0x1d,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xd8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd6,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xaf,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb6,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0xad,0x84,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x34,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x62,
+0x49,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5b,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,
+0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3f,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,
+0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x4d,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0x6,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe5,0x5,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc2,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,
+0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd5,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0xbd,0x42,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x34,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xeb,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,
+0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x58,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd5,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xed,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x68,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xc8,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x16,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1b,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,
+0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe7,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,
+0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x93,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0x68,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xe8,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x14,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x83,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,
+0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe5,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0xb1,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x7e,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,
+0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xed,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,
+0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb3,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xc8,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4,0x79,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb1,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,
+0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa6,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,
+0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0x73,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xbb,0x47,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x37,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x19,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2f,0x60,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xd3,0x37,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9d,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0xb6,0x1b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb0,0xad,0x84,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,
+0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xd8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb3,0x41,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,
+0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x15,0x20,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x91,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x7e,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x28,
+0x66,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9d,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,
+0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x35,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,
+0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,0xff,0x51,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x95,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xa4,0x58,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x72,0x6b,0x75,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf8,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xd8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x33,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xbb,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd6,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xa4,0x58,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0x79,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x89,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,
+0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbf,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd1,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x51,0x1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0xc8,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0xb1,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x18,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x85,0xc0,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa2,
+0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x69,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,
+0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0xe9,0x3,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x1a,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x35,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa2,0x14,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1b,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,
+0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x6,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,
+0x1a,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x53,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,
+0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc1,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x99,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x48,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0x68,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x1c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x2a,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x51,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7b,0x12,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x65,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x98,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,
+0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa1,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0x12,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x10,0xf4,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,
+0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x59,0xc7,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd5,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xc9,0x6e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9d,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xcc,0x6e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x1a,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xd0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7,0x75,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x48,0xcf,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,
+0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xba,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb9,0xd1,0x6e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9a,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x3a,0x77,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1e,0xad,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xdf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5b,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,
+0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x30,0xd8,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x90,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xdb,0x6e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x15,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,
+0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5d,0x59,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x89,0x8a,
+0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xaf,0x70,0xbd,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xec,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x5c,0x50,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x90,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xdc,
+0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x89,0x8a,0x18,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe8,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x52,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0xa4,0x22,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb9,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0x48,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0xc4,0x1f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x86,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xe3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x53,0xa9,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,
+0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x86,0xac,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc1,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xf0,0x51,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x59,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,
+0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc3,0xaf,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,
+0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x18,0xb4,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc9,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0xc0,0x22,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xa8,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0xc7,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x1d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9b,0x52,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,
+0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x38,0x27,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x5,0x32,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbb,0x8b,0x8,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3f,0x4b,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x18,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6d,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,
+0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xab,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xd,0x32,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xce,0x22,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,
+0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x10,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x92,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,
+0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x66,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe8,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0x85,0x33,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0x93,0x1d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x39,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9e,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,
+0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3e,0x96,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,
+0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x56,0xe,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9d,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x59,0xe,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa2,0x31,0x3d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xdf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x47,0x5e,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,
+0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0xa3,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe0,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0xe6,0x4f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x8a,0x6d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,
+0x63,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x18,0x8e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdc,0xfc,0xb2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,
+0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5,0x66,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb1,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x15,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x69,0xe,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x6f,
+0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x18,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4f,0x72,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x18,
+0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe4,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,
+0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x74,0xe,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc1,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x77,0xe,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xbc,0x1d,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x61,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xd0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa8,0x75,0x7b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,
+0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x25,0xc2,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x99,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0x82,0xe,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd1,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0x84,0xe,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,
+0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xdb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc4,0x32,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,
+0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x53,0x32,0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbb,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xd9,0xe,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd9,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xbc,
+0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6b,0x8a,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x18,
+0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x29,0x28,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,
+0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0x2a,0x16,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x95,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xc4,0x1f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xf8,0x67,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x22,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xdf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5e,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,
+0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x2d,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x99,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x78,0x87,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x59,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,
+0x30,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x89,0x8a,0x18,0x8b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xbc,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb,0x8d,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbb,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x10,0x24,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0x32,
+0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x24,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,
+0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd2,0xb4,0x54,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,
+0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x13,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x95,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0x7a,0x25,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x8,0x78,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x54,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa1,0xd4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6f,0xdb,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,
+0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x67,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb2,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x8d,0x33,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x16,0x24,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x56,
+0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb2,0x32,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,
+0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x40,0xa,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x91,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x3a,0x16,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa9,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xc4,0x1f,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x4b,
+0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xe0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbf,0xde,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x18,
+0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x36,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x18,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9d,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x48,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0xc,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x94,0x3c,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x18,0x8b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x35,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,
+0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaf,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x61,0x59,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0xe1,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,
+0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xd3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5,0xf,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,
+0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xba,0x1b,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xe3,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9d,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x3f,0x16,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0xd5,
+0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,0xdb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd5,0x32,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6b,0x11,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,
+0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x91,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9a,0x23,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0x1a,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc5,0xe6,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,0x8f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa9,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x9b,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x42,0x16,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x26,0x24,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x98,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x70,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd1,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0xeb,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x39,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xd1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb2,0x14,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,
+0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xf0,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x47,0x16,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x69,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x13,0xff,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,
+0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc7,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x29,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x8d,0x33,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc9,
+0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb2,0xed,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,
+0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x28,0x19,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa5,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xd9,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc9,0x2,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4c,0x45,0x7c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0x4d,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x99,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0x8b,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7a,0xf0,0x7,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf3,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xd5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xae,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,
+0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcc,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x6,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0x4a,0x16,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,
+0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf8,0xae,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,
+0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xec,0xb5,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdb,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xb5,0x5b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xbc,0x3,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x50,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,
+0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x2f,0x81,0xd1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,
+0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x59,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x79,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x51,0x62,0x49,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xcf,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x90,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0xad,0x84,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0x73,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,
+0x99,0x59,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xd8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x96,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,
+0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa7,0x2b,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb1,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x89,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x79,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0x60,
+0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xd8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5,0xf1,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x18,
+0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4f,0x6,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,
+0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0x7b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0x9b,0x1b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x88,0xad,0x84,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xbf,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb6,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,
+0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa3,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0x79,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0x34,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,
+0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xcd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfd,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,
+0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbc,0x1c,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa9,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x4c,0x16,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc1,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0x73,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0xb2,
+0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x19,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf4,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc0,0xdb,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xa4,0x58,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0x2e,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xae,0x8,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x19,0xf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x13,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,
+0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcc,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xc8,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x73,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,
+0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xe2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd3,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xcb,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe9,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x73,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xf3,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x1f,
+0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x18,0x88,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa4,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xff,0x51,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xae,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x4f,0x16,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xdd,0x2a,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x27,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x96,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0xb4,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x99,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xc8,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0x73,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,
+0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xe2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xcb,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x37,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9a,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x73,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9a,0xad,0x84,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x34,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa3,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,
+0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5f,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,
+0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x99,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x34,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x7b,0x33,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe1,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xae,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,
+0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0x79,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdd,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x78,0x26,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x68,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,
+0x27,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9b,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,
+0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x33,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x7a,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd9,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x4d,0xf,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0xb1,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x18,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1,0x2b,0x43,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x89,0x8a,0x18,
+0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x13,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x94,0x25,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4,0xb7,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa8,0x39,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x19,0xc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2f,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,
+0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xfb,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb9,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x53,0xf,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xf0,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,
+0x3,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x99,0x67,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,
+0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x51,0xba,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x57,0x16,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x3c,0x24,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x13,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x89,0x8a,0x19,0xf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x14,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,
+0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9c,0xfd,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,
+0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x2a,0x6,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb9,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xc1,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,0xb1,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3b,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa2,0x1f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb2,0x5,0x52,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,
+0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbc,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xdd,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0x77,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,
+0x85,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,0x8a,0x18,0x8f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6f,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,
+0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x66,0xb2,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc2,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0x50,0x1,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xb1,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x18,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xcd,0xdd,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x89,0x8a,0x18,
+0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x62,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xc4,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x41,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x14,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2e,0x9e,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,
+0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x46,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xab,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0xc8,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x3,0x8,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,
+0x72,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,0x8a,0x18,0x88,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x35,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,
+0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xad,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc9,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x5b,0x16,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd5,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0x8,0x16,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x7e,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4e,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,
+0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1f,0xe3,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,
+0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x4a,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0x1c,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x52,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x17,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8f,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,
+0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xe,0x45,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc1,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe9,0xb1,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfc,0x32,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,
+0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x21,0xc9,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb1,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0xb7,0x8a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x96,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0xe8,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x24,
+0x44,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x18,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xab,0x7f,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa2,
+0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa9,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,
+0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x6a,0x5f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe0,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x51,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,0xd6,0x7c,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc8,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8d,0x4d,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,
+0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x4c,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd1,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0x7e,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x68,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,
+0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x14,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd3,0x78,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,
+0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x1e,0xe,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc1,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd8,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf2,0xae,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,
+0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc4,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0x68,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xc8,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3b,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa2,0x14,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x87,0xa3,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,
+0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc5,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xcf,0x3f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,
+0x4f,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xcc,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,
+0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x8f,0xdd,0x43,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xad,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0x68,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0x2d,
+0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x19,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xfc,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,
+0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x97,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,
+0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0x92,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd1,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xe8,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6,0x7e,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x18,0x88,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe1,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,
+0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x68,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x1d,0x8c,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,
+0xa,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfe,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,
+0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x50,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb0,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x51,0x1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0xd6,0x42,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0xb1,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x18,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xde,0xe0,0x43,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x19,
+0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x55,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,
+0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xab,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa1,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0x11,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0xd5,0x3f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa2,0x19,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x13,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,
+0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x81,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb5,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xc8,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7b,0xdb,0x7c,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,
+0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x14,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x20,0x78,0x5f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,
+0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbf,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xaf,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x81,0x97,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0x51,0x1,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0x5e,
+0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x19,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa5,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa2,
+0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x84,0x59,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,
+0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x97,0x33,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc5,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0x78,0x26,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x12,0x1a,0x4f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa2,0x1c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1b,0xf6,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,
+0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x44,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaa,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0x8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0xb1,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,
+0x1e,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x19,0x9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4c,0x3b,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,
+0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x51,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdc,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0x78,0x26,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xf0,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6a,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,
+0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x74,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,
+0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xab,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0xf0,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xf0,0x7,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x68,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x10,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5c,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,
+0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd5,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xf0,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0xf0,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,
+0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3b,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,
+0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb9,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,0xe2,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0xe2,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x37,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,
+0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa3,0xf7,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x91,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xe2,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x28,0xe2,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x43,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x13,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1e,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,
+0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb2,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0xe2,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xe2,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,
+0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x30,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,
+0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x17,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xed,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xe2,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x11,0xe2,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x32,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,
+0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x19,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,
+0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xe1,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xe8,0x34,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb1,0xe8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc6,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb4,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,
+0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb1,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xe8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0xe8,0x34,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,
+0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x11,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc9,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc2,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x99,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0xe8,0x34,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x54,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbc,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,
+0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xce,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,
+0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x63,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa5,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x9e,0x26,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0xe8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x10,0x2b,0x44,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xab,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,
+0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9d,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcc,0xe8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0xe8,0x34,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb8,
+0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x11,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc4,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,
+0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa9,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc9,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x3b,0x9,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,
+0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x31,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,
+0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x3b,0x9,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0x3b,0x9,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xff,0x3a,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x1a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x25,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,
+0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd2,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0x3b,0x9,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x3b,0x9,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,
+0xc8,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x18,0x88,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf0,0xdf,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,0x8a,
+0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa4,0xb9,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb1,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xe1,0x3f,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbf,0xe8,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x19,
+0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x10,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,
+0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x47,0x6,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdd,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x3b,0x9,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0x3a,0x9,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1a,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa2,0x1a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2f,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,
+0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe8,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x3b,0x9,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0xbc,0x31,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,
+0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x74,0xa1,0xb4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,
+0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x27,0xe4,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xad,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xeb,0x6,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd1,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x3b,0x9,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xc1,
+0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x19,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x79,0xe7,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x96,0xed,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,
+0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0xc4,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbd,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xe9,0x3f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0x99,0x26,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa2,0x1b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x74,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe5,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x29,0x2e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x12,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,
+0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x18,0x8a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x31,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,
+0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf5,0xef,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd9,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xc7,0x31,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc1,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,0x5b,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x80,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x1b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x14,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa2,
+0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x20,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,
+0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0x11,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xf3,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x19,0xca,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x19,0xe,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x58,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,
+0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xce,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbd,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x81,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0x5d,0x17,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,
+0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x89,0x8a,0x18,0x8a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9,0x60,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x89,0x8a,
+0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x8d,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xae,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xcc,0x31,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc9,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7a,0x11,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x81,0xd3,
+0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x19,0xe,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6b,0x63,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,
+0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa3,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xd5,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd1,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x67,0x17,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x11,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x67,0x6b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x18,0x89,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd3,0xd8,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,
+0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xad,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x6d,0x17,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0xe6,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,
+0xdb,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x19,0xe,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5d,0xe9,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,
+0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x68,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb7,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x70,0x17,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb1,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0xde,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x81,0x39,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x19,0xb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa0,0xec,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,0x19,
+0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x64,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,
+0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x72,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb5,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x81,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x81,0x2d,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x1b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x70,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,
+0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xca,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0x81,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x81,0x2d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,
+0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfb,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,
+0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x80,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa7,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x91,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x3b,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xef,
+0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x19,0x8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x72,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,
+0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xab,0xba,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,
+0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xed,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xef,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x80,0x37,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc,0xfa,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x19,0xd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8f,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,
+0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xca,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x3c,0x59,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0xef,0x2,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,
+0x41,0x45,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x18,0x89,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf2,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,
+0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf8,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xac,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xee,0x2,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x11,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0xee,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x71,0xfc,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x19,
+0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x8b,0x41,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,
+0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xf9,0xc,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcd,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xee,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0xef,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf2,0x43,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x19,0xb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd1,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,
+0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd5,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xef,0x2,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xef,0x2,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,
+0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x12,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x8c,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,
+0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe8,0xfe,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9d,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xee,0x2,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xee,0x2,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0xef,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe0,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x89,0x8a,0x18,
+0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x78,0x37,0x83,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,
+0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x95,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4,0xef,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xee,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x38,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x12,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x46,0x7e,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,
+0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x97,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0x57,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x4d,0x11,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x5,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,
+0xc6,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0x17,0x48,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xcb,0x60,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,
+0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x29,0xfc,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd1,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x1,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa1,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0xc7,0x2f,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xc8,
+0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0x17,0x48,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6e,0x4c,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xab,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,
+0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,0x80,0x17,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc5,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0xff,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0x4e,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x22,0x9,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x89,0x8a,0x19,0xd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x34,0x83,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,
+0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa9,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x1,0xd,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xb,0x15,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,
+0x51,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x19,0xb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd0,0x88,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,
+0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe8,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9b,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x2,0x2a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x4,0xd,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0xe,
+0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x89,0x8a,0x19,0xd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7d,0x54,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5e,0x5,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,
+0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0x61,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa8,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x53,0x31,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0x11,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x37,0x5d,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0x17,0x49,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3d,0x8b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x89,
+0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x15,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x93,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x9e,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x14,0x22,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,
+0x19,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0x17,0x49,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3b,0xe6,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,
+0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa0,0x1e,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xec,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0x1b,0x22,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe6,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xc0,0xc,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x10,
+0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,0x8a,0x19,0xd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa3,0x1d,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0x17,
+0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf4,0x7,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,
+0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0x20,0x22,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb3,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x13,0x22,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x90,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0x5a,0x31,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb,0xa2,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x18,0x8c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe4,0x56,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,
+0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9a,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0x8e,0x17,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0xc3,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,
+0xa,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x19,0xa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x69,0xa4,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,
+0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd7,0x12,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xca,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0x5f,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0x91,
+0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x18,0x89,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x12,0xe9,0x4d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x89,0x8a,0x18,
+0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5e,0xc6,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,
+0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0xd,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdd,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x1c,0x15,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x62,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd9,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x89,0x8a,0x18,0x8a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x84,0x93,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,
+0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xc8,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9d,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xa9,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0xab,0x29,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,
+0x1f,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x19,0xd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf2,0x64,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,
+0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa0,0xae,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa5,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,0xcb,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa1,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xae,0x29,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x81,0xa5,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6,0x68,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x19,
+0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xfb,0xb0,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,
+0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0xd2,0xc,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa5,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x4c,0x30,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0x17,0x44,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0xb1,0x29,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7,0x22,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,0x19,0xd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6e,0x6a,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,
+0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xb4,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xad,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0xd5,0xc,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa9,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf1,0x59,0x31,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,
+0x5c,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0x17,0x49,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x0,0x12,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,
+0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe1,0x16,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd8,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x21,0x22,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xef,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0x12,0x22,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xb,
+0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xd0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1f,0x81,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0x17,
+0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6d,0xb3,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,
+0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xcc,0x47,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x91,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x7b,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x8,0x57,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x78,0x8b,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0x17,0x74,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf5,0x81,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,
+0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xd0,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xac,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0xc7,0x47,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x80,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,
+0x26,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x19,0xd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xab,0x7e,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,
+0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x2b,0x87,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb3,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0xc5,0x47,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc6,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0x73,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xb6,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,0x8a,0x18,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x62,0xcb,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,
+0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x7b,0xbe,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0xc6,0x47,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xbf,0x47,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbb,0xc1,0x47,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x58,0x86,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0x17,0x74,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xdb,0x7d,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x80,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe2,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xc3,0x56,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0x83,0x47,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x44,
+0x7b,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0x17,0x6f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6f,0x3d,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,
+0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe7,0x7c,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xfc,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xd8,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xad,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0xb7,0x56,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xba,
+0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0x17,0x6f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb4,0xb6,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x89,0x8a,0x19,
+0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa9,0x7e,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,
+0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0x28,0x15,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc9,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x75,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd1,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0xbc,0x56,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb5,0x74,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x5,0x80,0x8e,0x17,0x6f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc3,0x7d,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xff,0x80,
+0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x84,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x98,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xbb,0x56,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xb9,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,
+0x40,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf9,0x80,0x8e,0x17,0x6b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa2,0xda,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,0x8a,
+0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x63,0x82,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbc,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xbb,0x56,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa5,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x77,0x47,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x8f,
+0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0x17,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x93,0x81,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0x17,
+0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe0,0x45,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,
+0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xbe,0x29,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa5,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0x41,0x11,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0x87,0x20,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x95,0xb8,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x6f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xcf,0x30,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xb5,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9a,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x12,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x82,0x47,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf9,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,
+0x46,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0x17,0x6b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe5,0x73,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf6,0x80,0x8e,
+0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x37,0x44,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc6,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0x7f,0x20,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x49,0x11,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x8e,
+0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0x17,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x64,0x43,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0x17,
+0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xfc,0x78,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,
+0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x4b,0x11,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xca,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x8a,0x20,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0x3e,0x11,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x76,0xbe,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x18,0x8c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x82,0x8c,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,
+0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xc0,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa9,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0xd,0x40,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0xf,0x40,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,
+0xe,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0x17,0x64,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe3,0x4e,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,
+0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6,0xdd,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0xf7,0x39,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb0,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x11,0x40,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x15,
+0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0x17,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x8d,0x8,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0x17,
+0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5d,0x48,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,
+0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xb,0x40,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1b,0x1,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0xf,0x40,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xfb,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0x7b,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc2,0x33,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x89,0x8a,0x19,0xd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5d,0x9,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf7,0x80,
+0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x14,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xfe,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x4d,0x4f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0x6,0x40,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,
+0xc0,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x18,0x8c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xbd,0x46,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,
+0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf2,0x11,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe4,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x8c,0x5e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa6,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x12,0x40,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xf4,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x47,
+0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x98,0x2,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0x17,
+0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3e,0xc3,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,
+0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x6,0x25,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd5,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xff,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,0xf8,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5a,0x36,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x19,0xd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf6,0x3,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,
+0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xf5,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x95,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xfd,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x8,0x25,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,
+0xf4,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0x17,0x66,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xae,0xfa,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,
+0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xde,0xfc,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x1,0x25,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc5,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0xf6,0x24,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x5,
+0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0x17,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf5,0x6,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0x17,
+0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6a,0x0,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,
+0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xfc,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb1,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xf7,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xc3,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x96,0xf9,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x66,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9f,0xc5,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x89,
+0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xf6,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x99,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x5a,0x15,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,0x64,0x15,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,
+0x99,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0x17,0x65,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe,0x7e,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,
+0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x52,0x97,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa5,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x59,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbe,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0xa1,0x24,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x90,
+0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0x17,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf2,0x98,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0x17,
+0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xaa,0xd4,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,
+0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xd3,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xad,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0xcf,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xd1,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb3,0xdd,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0x17,0x65,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x56,0x39,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x89,
+0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xc6,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc5,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xcc,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0xd5,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,
+0xd6,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0x17,0x65,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x91,0xd8,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,
+0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x54,0xdc,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb5,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xc8,0x29,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb5,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0xca,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xda,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x65,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb9,0x3b,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x19,
+0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3a,0x89,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,
+0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0x7c,0x14,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x95,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0xd1,0x29,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb9,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0x85,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x22,0x88,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0x17,0x68,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa1,0x79,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,
+0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x8c,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x91,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x2a,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x78,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,
+0xc8,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,0x8a,0x18,0x8c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x1c,0x6c,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,
+0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbd,0x2e,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,
+0x5,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0xce,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x28,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0xd3,
+0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x89,0x8a,0x19,0xa,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa5,0x34,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0x17,
+0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1c,0x2d,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,
+0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x71,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa7,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x31,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,0x2b,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x46,0x38,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x2,0x80,0x8e,0x17,0x68,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2d,0x70,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,
+0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xca,0x33,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0xd0,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd1,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0xd6,0x29,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,
+0xd3,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x18,0x8c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x71,0xd9,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,
+0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x97,0xd6,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd9,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xdb,0x29,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc9,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0xd8,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0xb3,
+0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0x17,0x54,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x8,0xd3,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0x17,
+0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe4,0x54,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,
+0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x54,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6c,0x12,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc,0x45,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe8,0x54,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,
+0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbe,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0x12,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x12,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,
+0x53,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xe4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9a,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,
+0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x96,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd5,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x12,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0xb0,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x86,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x72,0x80,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,
+0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x1a,0x6b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xe5,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa8,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xc9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4f,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbe,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0xe0,0x1b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xe5,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,
+0x99,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xc9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc9,0xf0,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x57,0xa,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x91,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf6,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xe5,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x44,0xb0,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x18,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,
+0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x75,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0xe5,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0xe9,0x51,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc8,0x22,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa3,0x99,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,
+0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xef,0x75,0x1f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0xa7,0x42,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,
+0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xcb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3d,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xaf,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbc,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xbc,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0xf2,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb7,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9d,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,
+0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xe5,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0xe0,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x2,0xfc,0x4b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xc9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd9,0x43,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9f,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xe5,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0xbd,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,
+0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xcb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4b,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x96,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb7,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xc8,0x5e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x57,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xc9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4f,0xc8,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x71,0xe9,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,
+0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x57,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xe0,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1a,0xf6,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xc8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf4,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,
+0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb8,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0x57,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0xe0,0x1b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,
+0xa7,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x68,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x55,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd2,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,0xe0,0x1b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0xe9,
+0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4b,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,
+0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x52,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,
+0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x57,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0xbc,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9,0x86,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xc8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,
+0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x65,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe9,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x57,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xe0,0x1b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf1,
+0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5b,0xf8,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,
+0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb6,0x1a,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa7,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0xbd,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb9,0xe0,
+0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xcb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3e,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,
+0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5e,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,
+0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0xbc,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xe0,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x58,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xc8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x90,0x27,0x2e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,
+0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc6,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x22,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,
+0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xc8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb0,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa2,0x1a,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa9,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xbc,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0xc8,
+0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa1,0xcb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x89,0xfa,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,
+0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x64,0xd8,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,
+0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x57,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0xe0,0x1b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3a,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xc8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x62,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,
+0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb6,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xe0,0x1b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0xb0,0x12,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,
+0x3e,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xc9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x69,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,
+0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe6,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcf,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xb0,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x11,0xa2,
+0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xc9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x45,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,
+0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf8,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,
+0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0xfc,0x1,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xed,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xa,0x68,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x13,0x96,0x4c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x45,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,
+0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0xe8,0x1b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xb0,0x12,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,
+0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xc9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x58,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x64,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xce,0x13,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xce,
+0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6b,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,
+0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x49,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,
+0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xce,0x13,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0xce,0x13,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x73,0x5,0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xdd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x43,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,
+0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbc,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x10,0x23,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xf0,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x5,0x60,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,
+0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xdd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe,0xc3,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,
+0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9c,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0x80,0x41,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xa1,
+0x65,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xd5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x11,0xf0,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9c,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xfe,0x12,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x99,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0x59,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0x5,0x55,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0xd,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xbe,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xd6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x45,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x22,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc9,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x32,0x60,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x1a,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb8,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,
+0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6f,0x8c,0x43,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd9,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x39,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,0x48,0x5e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x91,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xe3,
+0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xd5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x82,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x20,0xe7,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x23,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x48,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x1e,0x62,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5b,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc7,0x27,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xae,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x6,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0xbc,0x3,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,
+0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x1,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,
+0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4b,0x17,0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc7,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0xe5,0x65,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x59,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4d,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x1c,0x62,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xe3,0x74,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa9,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,
+0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcb,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0xf0,0x50,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x1a,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe5,0xd7,0x52,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf3,0x80,0x8e,
+0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7e,0xe3,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa5,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x31,0xf0,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x78,0x4d,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x91,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0x5a,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x95,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x14,0x62,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0xb6,0x28,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb6,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xd6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb9,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x72,0x6,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x1a,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,
+0xa4,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa6,0x12,0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,
+0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x65,0x6a,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x95,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0x48,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc8,0xad,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x8e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,
+0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xd4,0x52,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,0x78,0x87,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x48,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xe7,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,
+0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x89,0x54,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd9,0x80,0x8e,0xa1,0xe5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xd5,0x2b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0x8d,0x33,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,
+0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf9,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x43,0xef,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x95,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x15,0x30,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0x88,0x54,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,0xe5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x8b,
+0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xd3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x47,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,
+0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xea,0x74,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x90,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0xef,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0x5,0x0,0x0,0xe,0x33,0xda,0x95,0x66,
+0xd9,0x46,0xa2,0x89,0xc2,0xf3,0x79,0xfa,0x5,0x13,0x9c,0xe6,0xfd,0x82,0xe4,0xf6,
+0x9d,0x46,0xd2,0x81,0xa1,0xa3,0xe5,0x5e,0x3a,0x4a,0xb7,0xce,0x79,0x42,0x24,0x42,
+0x96,0x48,0xac,0xa5,0x86,0x5f,0x83,0xd4,0x72,0x6a,0x7d,0x70,0x39,0xd5,0xc,0xd3,
+0x8b,0x49,0xec,0xb9,0x4c,0x27,0x68,0x50,0xac,0x13,0xd9,0xa0,0xb6,0x97,0xf9,0x8d,
+0x6d,0x41,0x8c,0x81,0x9c,0xef,0xf1,0x95,0x39,0x28,0xd4,0x27,0x51,0xa9,0xd2,0x34,
+0x60,0x4b,0x9a,0x87,0xdd,0x0,0xb1,0xe1,0x29,0x33,0xac,0x6e,0xa7,0x7d,0x8e,0x78,
+0x54,0x4f,0xaf,0x8e,0xad,0x65,0xfe,0x99,0xf9,0x96,0x56,0xf8,0x46,0x94,0x8a,0x4,
+0x4b,0x43,0x6b,0x95,0x80,0xc9,0x46,0x3,0x54,0xd1,0x7,0xfe,0x43,0x82,0x9c,0x1d,
+0x88,0x4b,0x40,0x9c,0x64,0x4b,0xe5,0xa3,0x79,0x6e,0x7,0xaa,0xe3,0xb1,0xb2,0xe9,
+0x51,0x44,0xaa,0xa8,0xc2,0x0,0x2a,0xe5,0x22,0xb1,0xff,0x6,0x63,0x6d,0xa1,0xe8,
+0xbe,0x44,0x7a,0x93,0x8c,0xd2,0xd4,0x59,0x5a,0x28,0x6,0x62,0x3b,0xda,0x60,0xc3,
+0x84,0x4d,0x67,0xb8,0xc5,0x38,0x2c,0x14,0x6b,0x29,0x6a,0x43,0xec,0xbc,0xfc,0xa3,
+0x7a,0x4b,0x7f,0xa7,0x4d,0x11,0x64,0x3a,0x71,0xa6,0xc8,0xc6,0x7d,0x40,0x3a,0x8f,
+0xf,0x4a,0xc6,0x86,0x47,0x8e,0xf,0x5e,0xb8,0xe9,0xb4,0x8b,0x60,0x40,0x7a,0xda,
+0xa0,0x4a,0x61,0xba,0x91,0xbb,0x4b,0x29,0x62,0xcf,0xdd,0x4a,0x21,0xca,0xe6,0xec,
+0x32,0x49,0xb1,0xbf,0xa2,0x94,0xe6,0xc,0x6c,0x1,0x55,0x36,0xd7,0x62,0xd1,0xa8,
+0x3c,0x40,0xc2,0x93,0x25,0xa,0xed,0x4e,0xcb,0xd,0x5b,0x9,0xd6,0xe4,0xd4,0x89,
+0x42,0x45,0x83,0x93,0x78,0x74,0x15,0xd0,0x35,0x9a,0x96,0x17,0x9,0x56,0x6c,0x27,
+0x4c,0x40,0x62,0x93,0x72,0x9f,0x7d,0x3a,0x53,0x6c,0x90,0xd8,0x50,0x6c,0x83,0x7e,
+0x8c,0x4a,0xa8,0x99,0xd9,0xdf,0x55,0x35,0x43,0x44,0x73,0xe6,0x80,0xe6,0x40,0x92,
+0xfe,0x49,0xf3,0xbc,0xc,0x3a,0xa1,0x9d,0xec,0xbf,0xe1,0xdf,0x70,0xc2,0x65,0x3f,
+0xa5,0x4b,0x4,0x86,0xfb,0x17,0xe,0x77,0xfa,0x77,0xe7,0x24,0xb8,0xbe,0x69,0xa7,
+0x9a,0x45,0x54,0x93,0x2e,0x41,0xc5,0x10,0x66,0xd2,0xb5,0xdd,0xb5,0x89,0x70,0x60,
+0xfb,0x4c,0xd1,0x8d,0xcb,0x69,0x8f,0x8d,0x20,0x97,0x95,0xcd,0x8a,0xf7,0x4b,0x1b,
+0x23,0x4f,0x4c,0xb9,0x17,0x75,0xcd,0xae,0x2f,0xfd,0x4e,0x56,0x71,0xa2,0x8e,0xe9,
+0x76,0x40,0x1b,0xba,0xbb,0x44,0xa4,0x7,0x34,0xb7,0x22,0x80,0xca,0x6a,0xed,0x32,
+0x68,0x4b,0xf5,0x88,0x46,0x9d,0xd8,0x1f,0x2a,0x19,0x24,0xc6,0x8e,0xde,0x81,0x98,
+0x9c,0x45,0x27,0x91,0x4e,0xa9,0x1e,0x1a,0x79,0x2d,0x38,0xf5,0x1f,0x65,0x11,0x4a,
+0x9b,0x4b,0x1a,0xad,0x0,0x90,0x17,0x77,0xe4,0x79,0xbb,0x95,0x15,0x9f,0x8,0x39,
+0xbc,0x42,0xba,0xa1,0xed,0xe3,0xa2,0x8c,0xa,0x45,0x40,0xd3,0xd1,0x7a,0xae,0x3c,
+0xd0,0x42,0x5b,0x8d,0x5f,0xcb,0x3f,0xd6,0xfe,0xa4,0x3b,0xe5,0x5e,0xaf,0xec,0x19,
+0x15,0x4e,0xe,0x99,0x5b,0x2d,0xf8,0x4c,0x20,0x64,0x0,0x17,0x70,0xae,0xa4,0xff,
+0x59,0x48,0x85,0xad,0x30,0x2a,0xd4,0xb5,0x7e,0x18,0x84,0xfd,0x27,0x2,0x56,0xbb,
+0x6a,0x4e,0x2f,0xbe,0xcb,0x3d,0x4e,0x31,0x84,0x82,0x43,0x85,0x59,0x34,0x1d,0x10,
+0x9c,0x4f,0x65,0x96,0xcf,0xf9,0xb1,0x53,0x60,0x25,0xae,0x95,0xf1,0x9c,0x4c,0x88,
+0x91,0x4d,0x64,0xa4,0x26,0xcc,0xf4,0xd,0x4d,0x88,0xdf,0xa3,0x52,0xda,0xfa,0xba,
+0x1c,0x40,0x4a,0xbf,0xea,0x6b,0x3d,0x88,0x7e,0xa0,0x4e,0x40,0xfa,0xb0,0x19,0xb5,
+0xfb,0x45,0xfc,0x83,0x1,0x6b,0xef,0x65,0xb0,0xfe,0xd9,0xb,0x13,0x70,0xe9,0x33,
+0x5d,0x48,0x5c,0x81,0xc3,0x36,0xdb,0x30,0xc3,0x52,0xb7,0xa4,0xb8,0x5c,0xd8,0x27,
+0xf9,0x47,0xa1,0xbd,0x97,0x3e,0x31,0x53,0xbb,0x3,0x77,0xdc,0x4d,0x22,0x75,0x13,
+0x15,0x4a,0xd8,0x95,0x77,0x69,0xda,0x6,0xd0,0xbc,0xe,0xc3,0x46,0x96,0x6e,0x50,
+0x93,0x47,0x68,0xbd,0x5,0xb0,0xa2,0x19,0x85,0x80,0x7f,0x35,0x6a,0x59,0xea,0x8b,
+0xbf,0x46,0x71,0x9b,0x3b,0x4f,0xc2,0xe2,0xa2,0xfe,0x46,0xa3,0x8,0xfa,0x7b,0x13,
+0x5,0x40,0x1d,0x94,0x3c,0xd3,0x93,0xa6,0x8a,0x1f,0x8c,0xfd,0x69,0x1f,0x2e,0x1a,
+0x41,0x41,0xed,0xb2,0xf5,0x4c,0x2e,0xe4,0x6,0x7b,0xa4,0x20,0xe2,0xd8,0x50,0xd2,
+0xf3,0x42,0xe4,0xbf,0x6c,0x51,0xd,0xd3,0xb8,0x96,0x3c,0x20,0x24,0xc,0x4e,0x3b,
+0xc4,0x43,0x6a,0xba,0xfa,0xc6,0x81,0xd8,0xab,0x60,0x8,0x2e,0xfb,0x5c,0xa9,0x17,
+0x94,0x4d,0x57,0xad,0xac,0xd8,0xac,0xc,0xb3,0x8b,0x2,0x49,0x8f,0x30,0xc4,0xc5,
+0xc8,0x48,0x8f,0x8a,0xf7,0x79,0xc1,0xb9,0x4d,0x36,0x45,0xdd,0xbf,0xcf,0x1b,0xb9,
+0x83,0x47,0x5a,0x9d,0x15,0xf5,0x86,0xe0,0x44,0xef,0xaa,0x3a,0xd7,0xc4,0x81,0xe,
+0x1f,0x4b,0x10,0x92,0x38,0xe8,0xcb,0x85,0x73,0x51,0xf5,0x42,0x3a,0x57,0x51,0x10,
+0x5b,0x4a,0x4,0xb7,0x3a,0x5a,0xf7,0x53,0xac,0xfb,0x22,0xb2,0x45,0x80,0x4c,0x6,
+0xd,0x45,0x47,0x9c,0x8b,0xb4,0xdf,0xb0,0x29,0x8a,0x8d,0xb9,0x77,0xd0,0xb2,0x19,
+0x95,0x44,0x4a,0x8e,0x4,0xae,0xc8,0x2f,0xf2,0x4f,0x90,0x2a,0xde,0xc,0x42,0xd4,
+0x6b,0x4d,0x3d,0xb6,0x1,0x90,0x47,0x1d,0xdb,0x4a,0xb3,0x7b,0xf1,0x9b,0xfd,0x91,
+0x60,0x47,0x17,0xa4,0x7d,0xa8,0xb2,0xed,0x61,0x13,0x38,0x91,0xf4,0xea,0xaf,0x29,
+0x61,0x4f,0xb7,0x8c,0xbe,0x24,0x73,0xe1,0x4f,0x9,0x6f,0x1d,0xdf,0xd9,0xc4,0x91,
+0xbe,0x48,0x66,0x86,0xe1,0x4,0x67,0xf6,0xbb,0xe7,0xa,0x8d,0x58,0x7c,0xb5,0x7d,
+0x64,0x46,0x1d,0xb9,0x4e,0x55,0x16,0x1,0xeb,0xdc,0xa0,0x13,0x8c,0xe6,0xef,0xaa,
+0x70,0x4b,0xd7,0xa8,0xf8,0x53,0x8d,0xeb,0x94,0xef,0x7,0x2b,0x56,0xc8,0xf5,0xbc,
+0xc3,0x44,0xed,0xbf,0xd,0xa5,0x58,0x2e,0xe6,0x1e,0xa1,0xe1,0xdd,0xd8,0x22,0x23,
+0x4e,0x46,0xd2,0x8e,0xd2,0x84,0xff,0x6d,0x86,0xb1,0xd9,0xd3,0x72,0x91,0xfc,0x2,
+0x87,0x40,0xf1,0xb5,0x88,0xa3,0x7d,0xcd,0xd5,0x99,0x7f,0x46,0xd3,0x77,0x56,0x42,
+0x84,0x43,0x44,0xb7,0x21,0x9a,0x11,0xd6,0xa1,0xda,0xe0,0x27,0x81,0xfd,0xcb,0x4b,
+0x4,0x4b,0xb2,0xa3,0x20,0xbd,0xe6,0x74,0xa1,0x76,0xd6,0x29,0x7e,0xf8,0xf5,0xe0,
+0xce,0x4a,0x7a,0x88,0x61,0x3a,0x29,0xef,0xbb,0xb5,0x66,0x19,0x9,0x7b,0x42,0x48,
+0xa2,0x41,0xca,0xb4,0x73,0x62,0x8,0xad,0xff,0x90,0xcc,0x4f,0x59,0x1c,0xb3,0x22,
+0xac,0x4d,0xec,0xa5,0x15,0x95,0x4d,0x18,0xf3,0x1f,0xee,0xfe,0x30,0x9b,0xaa,0xce,
+0x82,0x4e,0xd5,0x99,0x39,0xd0,0x15,0x99,0x99,0x74,0x66,0xf1,0x23,0x2e,0x71,0xba,
+0xd3,0x4d,0xb0,0x87,0x7f,0xca,0xe,0x9d,0x47,0x43,0x1b,0xf2,0xab,0x2a,0xbf,0x51,
+0x44,0x4d,0x8,0x9f,0xd4,0x56,0x41,0xd0,0x26,0xce,0x1d,0x14,0x89,0xe3,0x7a,0x9a,
+0xac,0x4a,0x6c,0xa0,0x65,0xe1,0x90,0xa5,0xbd,0x56,0xff,0x7e,0x20,0x43,0xac,0xcf,
+0x28,0x4f,0xdb,0xae,0xc5,0x27,0x28,0x9e,0x6b,0xb8,0x19,0x7f,0x6d,0xbc,0xb6,0xe6,
+0x40,0x49,0x3f,0x9a,0x90,0x41,0x4e,0xe8,0xf8,0x4,0x2d,0x70,0x64,0xa7,0xdd,0xee,
+0xbe,0x40,0x12,0xb3,0xe,0xcf,0x9b,0x91,0xd5,0xc5,0x69,0xaa,0x7a,0x97,0x98,0x9f,
+0x35,0x4e,0xac,0x8a,0xfa,0xce,0x2f,0xe8,0xa4,0xbc,0xf,0xa4,0x84,0x1,0x95,0x1a,
+0x2b,0x4d,0xe6,0x92,0xf6,0xba,0xdf,0x1e,0x10,0x70,0xa6,0xc3,0x86,0xc8,0xff,0xc0,
+0x95,0x4e,0xe6,0x99,0xe0,0x7a,0x2,0x61,0x36,0xbb,0xfb,0xd0,0x15,0x71,0x5a,0x8c,
+0x9c,0x4c,0x3b,0x9c,0x94,0xad,0x66,0x36,0x2d,0x71,0x6f,0x89,0xc0,0xd,0xe6,0x4,
+0xd5,0x44,0x67,0xa9,0x1a,0xad,0x8e,0x70,0xb4,0x65,0xf4,0x9d,0xf5,0x8f,0x2f,0xa9,
+0xdc,0x4e,0x3,0x8b,0xa6,0x8a,0xcd,0x3b,0x6b,0x4f,0xf,0x59,0x12,0x4c,0xf,0xc8,
+0xec,0x48,0x31,0x8c,0x49,0xd6,0x6f,0x41,0x5d,0xf6,0x65,0x18,0x98,0x3b,0xbc,0x95,
+0x2c,0x44,0x16,0xa0,0x88,0x46,0x7c,0x4b,0x5b,0x7c,0x30,0xed,0x20,0xf,0x5c,0x7f,
+0x1a,0x4e,0x7d,0x83,0x4c,0xc6,0x6b,0x7a,0x98,0xe2,0x12,0x9b,0x18,0x88,0xa6,0x22,
+0xa8,0x4b,0x62,0x89,0xdd,0xdf,0x78,0xa7,0x15,0xe5,0x6a,0x9c,0x7,0xfc,0xc6,0xff,
+0xf5,0x45,0x2d,0x95,0xa1,0x19,0xb1,0x62,0x3f,0xff,0x41,0xe0,0x33,0x48,0xef,0x87,
+0x94,0x46,0x19,0xb7,0x3,0xe3,0x19,0xf3,0xae,0x5a,0x9c,0x91,0x43,0xe3,0x55,0xd2,
+0x82,0x46,0xc3,0xab,0x19,0x9,0xd9,0xfc,0x5b,0x96,0x2f,0xc5,0x29,0x25,0x11,0x19,
+0x9b,0x4b,0xbb,0x8f,0x3e,0x9d,0x64,0x72,0xd4,0x8a,0x4,0x6c,0xe1,0x35,0x0,0x69,
+0x59,0x4e,0xb8,0xab,0xdf,0xaa,0xea,0x5d,0xd1,0x59,0x6d,0xa8,0x28,0x8d,0x59,0xcb,
+0xd5,0x45,0x2f,0xaa,0x7e,0x9c,0x24,0x83,0x3c,0x4f,0xe7,0x5e,0xe5,0x6d,0x51,0x91,
+0x9,0x4a,0xb0,0xa5,0xa2,0x58,0x7b,0x2c,0x3d,0xa5,0xd,0x84,0xaf,0xd8,0x70,0x57,
+0x27,0x41,0x63,0xb6,0xbd,0x5a,0xd3,0x49,0xb,0xa,0x19,0x39,0xe8,0xfe,0x41,0x1c,
+0xb8,0x42,0x3c,0x88,0xa2,0x7f,0xbb,0x49,0x1e,0x32,0xca,0x7f,0xd5,0xd6,0x8,0xa8,
+0x66,0x48,0x35,0x88,0xb0,0xed,0x50,0xb2,0x6b,0xea,0xf2,0xeb,0xde,0x71,0x78,0xab,
+0x4d,0x48,0xe4,0x95,0x56,0x84,0xc7,0x7f,0xba,0x42,0xec,0x13,0xb4,0x37,0x72,0x2f,
+0xb,0x4f,0xb7,0x8c,0x78,0x34,0x83,0xd,0x7d,0x70,0xc0,0xa2,0xcf,0xfe,0xcb,0x32,
+0xb4,0x44,0x36,0x9c,0xee,0x8a,0x50,0xe4,0xee,0x5d,0x68,0x5c,0x78,0x1,0xb5,0x5f,
+0xdc,0x45,0xd2,0x9e,0x70,0xd0,0x5f,0x4,0x67,0x13,0xd7,0x45,0x92,0x30,0xd9,0x86,
+0xdd,0x42,0x16,0x83,0x94,0x97,0xc4,0xa9,0xd0,0x6e,0xd8,0xa6,0xec,0x8b,0x7b,0x5d,
+0xd7,0x4a,0x48,0x96,0x96,0x65,0x7f,0x65,0xbc,0x3,0xa5,0xbe,0x8e,0x19,0x57,0xcb,
+0x67,0x41,0x5,0xae,0x1d,0x30,0x44,0x65,0x11,0xb4,0xd5,0xef,0xc6,0xff,0x55,0x6b,
+0xd8,0x41,0x1b,0xb6,0x21,0xfb,0x6b,0xc,0x16,0xa1,0x44,0x7c,0x54,0x6d,0x69,0xac,
+0xd6,0x40,0xf0,0x92,0xd8,0x19,0x75,0x74,0xd0,0x1e,0xb0,0x42,0x6a,0x92,0xdf,0x80,
+0xda,0x47,0xb1,0xba,0x9f,0x64,0x21,0xc3,0x6a,0x81,0x3c,0x7d,0x30,0xbc,0xec,0x51,
+0xb1,0x49,0x77,0x87,0x94,0xd3,0xc3,0x0,0x2c,0x30,0x4e,0xf,0x8c,0x41,0x4,0x6a,
+0x77,0x48,0x17,0x91,0xb,0xec,0x80,0xed,0xc,0x10,0x9,0xf5,0xb,0xd0,0x3,0xd2,
+0x3b,0x4d,0xb9,0x8e,0xc3,0x19,0x27,0x7a,0x3f,0x23,0xac,0xa0,0xe,0xb1,0x24,0x44,
+0xbc,0x42,0xc5,0x82,0xed,0x47,0x2d,0x2f,0xb5,0xde,0x33,0xd8,0x8,0x14,0x12,0x54,
+0xc3,0x41,0xcd,0x89,0x1c,0xb3,0x33,0x1b,0x5d,0x6,0x69,0x1b,0x89,0x21,0x3d,0x18,
+0x18,0x4e,0x69,0x96,0xc3,0xcc,0x19,0x6,0x1d,0xbe,0x84,0xcb,0x71,0xac,0x33,0x3d,
+0x98,0x40,0x99,0xb9,0x83,0xfc,0x4e,0x24,0xb3,0xe3,0x18,0x70,0xc2,0xb6,0x26,0xb,
+0x2a,0x4a,0x61,0x8e,0x85,0x5f,0xbf,0x41,0x7a,0xcb,0x6f,0x7a,0x3e,0x52,0x79,0x11,
+0x7e,0x4d,0x73,0xa5,0xd2,0x4d,0x3,0x7,0xa8,0x1b,0x36,0xfa,0xd7,0xeb,0x83,0x88,
+0xe9,0x4d,0x81,0x9c,0xa8,0x10,0x9,0x8b,0xff,0xda,0xc3,0x64,0x1,0x7a,0x72,0xb8,
+0x97,0x46,0xcf,0x9e,0x76,0xe9,0xb9,0x51,0x24,0xec,0x11,0x5a,0xb9,0xf1,0xe1,0x98,
+0xf0,0x42,0xb,0xa0,0xbf,0x8f,0xbb,0xf7,0x47,0xfd,0xc5,0xec,0xed,0x1f,0x6f,0x52,
+0x5f,0x40,0x99,0xaf,0xc6,0xb3,0x86,0x35,0x5,0x7b,0xba,0x78,0x21,0xfe,0xa7,0x59,
+0x6f,0x42,0x28,0x96,0x1a,0x57,0x10,0x30,0xf1,0x2e,0xf6,0x9,0xbe,0xd5,0x9c,0x49,
+0xab,0x4b,0x24,0xbb,0xd9,0x8d,0x15,0x4d,0xb3,0x33,0x4b,0xdc,0xb5,0x6d,0xec,0x4f,
+0x9b,0x44,0x93,0x86,0x23,0xec,0xdf,0x47,0xa4,0x6e,0x6e,0xd0,0xe5,0xbf,0x19,0xd5,
+0xa1,0x4c,0xf7,0x82,0xae,0x5c,0xb,0xd7,0xe7,0xfb,0x9b,0x8d,0xec,0x59,0xf,0xcf,
+0x20,0x45,0x2d,0x8c,0xb7,0x97,0x1,0x83,0x8c,0xbc,0x91,0x1c,0x6e,0x71,0x8c,0x65,
+0x80,0x42,0xda,0xa0,0x17,0x9,0x3a,0x48,0x93,0x18,0xd7,0xde,0x70,0xa7,0x25,0x52,
+0x37,0x45,0xe8,0x99,0xc7,0xe6,0x7,0xd4,0x16,0x59,0x22,0x49,0x3a,0x71,0x5c,0x6f,
+0x3b,0x4b,0xfc,0x96,0x90,0x0,0x46,0x77,0x28,0x9,0x1a,0x25,0xba,0xd,0xfd,0x8,
+0x5d,0x41,0x10,0x80,0xff,0x23,0x10,0x5a,0xd,0x85,0xec,0xdf,0x9c,0x9c,0xa6,0x78,
+0x36,0x4c,0xcb,0xb6,0x43,0x83,0x9e,0xea,0xfc,0x7c,0x82,0x7c,0x28,0x79,0x50,0x1f,
+0x5f,0x4f,0xbd,0xaa,0xd7,0xb4,0xad,0xa7,0x58,0xd9,0xc6,0x69,0x58,0xe3,0xb3,0xf2,
+0xb4,0x46,0x94,0x85,0xd5,0x43,0xb0,0x92,0xfd,0xd7,0x15,0xa9,0x2e,0xde,0x42,0x6e,
+0x58,0x43,0x18,0xb0,0x7b,0x75,0x1d,0xb8,0x2f,0xb8,0x5d,0x44,0x87,0xbf,0x86,0xb6,
+0xc9,0x47,0x21,0xb5,0xc2,0x6d,0x32,0xe0,0xe3,0xa3,0x52,0x8b,0x2a,0x7f,0xe0,0x2e,
+0x2b,0x4c,0x13,0xa9,0x13,0x13,0xf1,0xdb,0x19,0x80,0xfc,0xda,0xcf,0xea,0x4d,0xc,
+0x8a,0x4b,0xa,0xb0,0xdd,0x92,0x52,0x1,0x48,0x63,0xdd,0x65,0xb9,0x56,0x32,0x20,
+0x69,0x45,0x67,0x99,0xfe,0x5a,0xba,0x2b,0x1,0x46,0xa0,0x7b,0x8c,0xbc,0x13,0x2b,
+0xcb,0x42,0xba,0xa5,0xe3,0xe5,0x97,0xe3,0xa4,0x41,0xe5,0x4,0x3c,0x78,0xbc,0xe8,
+0xae,0x47,0x9,0xa3,0xf4,0xcd,0xd9,0x67,0xe9,0xdb,0xae,0x8f,0x64,0x82,0x5,0x46,
+0x4c,0x43,0x92,0x91,0x78,0xe5,0x3b,0x24,0x48,0xb1,0x46,0xee,0xaf,0xcc,0x32,0xc0,
+0xef,0x45,0x7,0x9e,0x50,0x3c,0x69,0xac,0x96,0x35,0x8b,0x5a,0xa5,0x8a,0x25,0xd1,
+0xc6,0x46,0x22,0x96,0x55,0x1a,0x98,0x56,0xe9,0x94,0x11,0x27,0x4d,0xd,0x26,0x3f,
+0xe3,0x41,0x65,0xa1,0xa6,0xf9,0xb0,0x3b,0xce,0x6,0xc6,0x7e,0xbb,0x4c,0x8b,0x38,
+0xb6,0x4c,0x74,0x99,0xf3,0xa1,0xb,0xcc,0x25,0x97,0xac,0xf9,0xcb,0x55,0x6,0x5d,
+0x5c,0x43,0xce,0xbb,0x52,0x2d,0x1a,0xe0,0x11,0xf6,0x4a,0x10,0x64,0x24,0x43,0xe1,
+0x28,0x4d,0xd6,0x84,0x71,0xac,0x97,0xd8,0x68,0x5e,0x8a,0xd6,0x85,0x38,0x4,0x3d,
+0x59,0x40,0xf6,0x88,0x71,0x84,0xde,0xf8,0x17,0xc3,0xa7,0x6a,0x98,0xed,0x65,0x37,
+0x90,0x49,0x28,0xb4,0x5f,0x6f,0x29,0x96,0xaa,0x1d,0x56,0x41,0xd5,0xc7,0x86,0x5f,
+0xd1,0x42,0x24,0x8b,0x3b,0x2c,0x9e,0xd0,0x7a,0xbc,0xfe,0x80,0x78,0x89,0x7f,0xae,
+0x24,0x49,0x27,0xaa,0x53,0x6b,0xe6,0x36,0x1f,0xc7,0x97,0xb6,0x26,0xdb,0xdb,0x7b,
+0xec,0x47,0xd2,0x9a,0x39,0xf7,0xc3,0x75,0xc5,0xe1,0x10,0x89,0x84,0x6e,0xdd,0x7a,
+0xbe,0x40,0x4c,0x95,0x1a,0x16,0xf,0x76,0x9a,0xbb,0x5d,0x2a,0x48,0x3,0xb5,0x83,
+0x5e,0x43,0xad,0x94,0xce,0xd2,0xd7,0xb9,0x5e,0x6e,0xa,0xbb,0xd4,0x30,0xc7,0x5a,
+0x2c,0x41,0x82,0x99,0xee,0x2,0x17,0x3,0xfb,0x89,0xee,0xe2,0x17,0xee,0x9a,0x1b,
+0xef,0x46,0x18,0x80,0x91,0xd5,0x7a,0xb7,0xc1,0xd5,0x3f,0x7c,0x5d,0x6,0xe,0xc4,
+0x76,0x47,0xc8,0xbf,0xeb,0x4d,0x65,0x9c,0x17,0xb4,0x73,0x16,0x81,0xe5,0x3c,0xfe,
+0x50,0x4d,0xbe,0xbb,0x22,0xbf,0x6b,0xc,0xab,0x8f,0xa,0xf3,0x82,0x62,0xd,0x4c,
+0xf1,0x4e,0xcb,0x81,0xbb,0x73,0x9d,0xd,0xe1,0xbd,0x15,0xca,0x1c,0x8c,0x15,0x1d,
+0x37,0x49,0xd,0xaa,0x40,0x2d,0x4d,0xe7,0x30,0xa5,0x94,0x83,0x9c,0x6c,0x96,0x40,
+0x1a,0x42,0x5f,0x8b,0xed,0xf7,0x15,0x1f,0x66,0x25,0xe,0xbd,0x95,0xb6,0xc2,0x3c,
+0xbf,0x4f,0x74,0x87,0xbc,0xc1,0x51,0xc1,0xa1,0x8d,0x92,0x83,0xf9,0xad,0xd1,0x16,
+0xed,0x44,0x8,0x8f,0x53,0x70,0x74,0x76,0xa0,0x36,0x96,0x3c,0x56,0xce,0xb8,0x7e,
+0xfc,0x4c,0x76,0x95,0x7c,0xa7,0xf6,0xe,0x27,0x29,0x46,0xa2,0x8,0xf4,0x44,0x6d,
+0x85,0x4e,0xe8,0x96,0x60,0x22,0x4,0x6e,0x3d,0xb1,0xc1,0x18,0xf0,0x4f,0x14,0x36,
+0xb5,0x4e,0x49,0x96,0x6b,0x55,0x63,0x29,0x63,0xb7,0x0,0x16,0x55,0x83,0x6e,0x4f,
+0xcf,0x43,0xc,0x93,0xa,0x47,0x54,0x68,0x25,0xbe,0xe,0x45,0x59,0x31,0x8a,0x60,
+0x3c,0x49,0x74,0xad,0x24,0xd4,0xa,0x7a,0xa6,0xb,0xdd,0xe,0xfb,0x9c,0x3e,0x88,
+0x5d,0x48,0x47,0xb7,0xf3,0x19,0x88,0xed,0xe1,0x79,0x8e,0x6,0x43,0x4b,0x4e,0x1,
+0x45,0x42,0x66,0xab,0xab,0x11,0x45,0xfa,0x8b,0xc3,0x40,0x42,0xab,0xd4,0x7,0xfc,
+0x27,0x47,0x86,0xa1,0x72,0xb7,0x72,0xaf,0xb6,0x0,0xf5,0x86,0xc2,0x8b,0xc4,0x35,
+0xc9,0x4e,0xbe,0x9d,0xd9,0xc1,0x8a,0xbd,0xee,0x98,0x1b,0xb9,0x45,0xc5,0xec,0x6d,
+0xf5,0x4a,0xde,0xa1,0x8d,0xd2,0xb9,0xf9,0xcc,0xd3,0x52,0xc1,0x37,0xef,0x58,0x36,
+0xe4,0x4a,0xad,0xac,0x4e,0xa5,0x2e,0x5c,0xf0,0xc5,0x4c,0xa3,0x2,0xf1,0x8e,0xa0,
+0x88,0x40,0xfa,0xad,0x5,0xe,0xa2,0xc0,0xf8,0x6d,0x8d,0xc2,0xa0,0xa9,0xb9,0x7,
+0x77,0x4b,0xde,0x9d,0xc4,0xfc,0x97,0xdf,0xc3,0xf7,0x96,0xbf,0xa0,0x72,0xd7,0xfa,
+0x92,0x47,0x41,0xa3,0x41,0xe9,0x93,0xf5,0xab,0x87,0x5,0x77,0x38,0xd0,0x76,0x7f,
+0x85,0x4a,0x6,0x8e,0x10,0xd3,0xce,0x6b,0x97,0x6e,0xd4,0x1b,0xe,0x4b,0x6b,0x9a,
+0x85,0x4a,0x92,0x89,0xb3,0x48,0x8b,0x97,0xd5,0xfe,0xf,0x1,0x5c,0xca,0x79,0xe5,
+0x8e,0x47,0x6f,0x99,0x4,0x26,0x54,0xc0,0x5c,0x9e,0xb3,0x3f,0x5e,0xd6,0x4a,0x47,
+0x28,0x4d,0x94,0xa3,0x1d,0xeb,0x45,0x3,0xb5,0x2b,0x7e,0xba,0x68,0xe2,0xd9,0xdd,
+0x92,0x41,0x5b,0x83,0x3d,0x12,0xc8,0xf6,0x80,0x7f,0x1e,0x5e,0x9,0x65,0x55,0x18,
+0x79,0x4e,0xec,0xac,0x25,0x94,0x23,0x9,0xfd,0xbd,0xb6,0x81,0xc7,0xc0,0xd3,0x44,
+0x18,0x40,0x12,0xad,0xea,0xd7,0xc9,0x11,0x53,0x0,0x3c,0x87,0x72,0x7d,0xb1,0xf0,
+0x5b,0x4a,0x9a,0xbe,0x48,0xa1,0x3b,0x75,0x54,0xe7,0xc6,0xe0,0xcd,0x35,0xb1,0xba,
+0x2b,0x4e,0xc8,0x93,0xf4,0xd0,0xef,0x81,0xbd,0xdd,0xde,0x9d,0x4a,0x55,0xd4,0xa,
+0x7b,0x40,0xb8,0x8b,0x2a,0xb1,0x35,0xfc,0xf1,0x43,0xe1,0x7a,0x38,0x2a,0x86,0x7e,
+0xd1,0x4b,0x82,0xa0,0x98,0x30,0xbb,0xf2,0x50,0xa6,0x98,0x2f,0xb6,0x4f,0xd5,0xca,
+0x75,0x48,0xa2,0x98,0x20,0x85,0xe0,0x6e,0x42,0xea,0x18,0xf7,0x13,0x99,0x2a,0xe9,
+0xf9,0x43,0xa8,0x8b,0xd,0x1d,0x5b,0x4e,0xe9,0xa4,0x34,0xe0,0xe9,0x6f,0x20,0x6a,
+0x22,0x4d,0xd6,0x90,0x7b,0x86,0x74,0xe3,0xfe,0xbe,0x2c,0x58,0x42,0xd8,0x50,0x81,
+0x1c,0x4b,0x3f,0x85,0xc8,0x6e,0x7f,0x98,0x74,0xea,0x26,0x76,0x99,0x37,0x81,0x61,
+0x52,0x41,0xd,0xb3,0xaa,0x1c,0x84,0xe2,0x5e,0x66,0x63,0x8d,0x8,0x61,0xf8,0x9,
+0x45,0x4f,0x67,0xb3,0xfb,0x2c,0xf0,0x49,0xd5,0x94,0x27,0xea,0xa7,0xeb,0x9b,0xa8,
+0x1d,0x4f,0x1,0x9b,0xff,0xf6,0x3e,0x57,0x29,0xef,0x40,0x7a,0xeb,0xca,0xc5,0x29,
+0xbe,0x47,0x8e,0xb3,0x2a,0xa7,0x4c,0x27,0x14,0x84,0xef,0x71,0xe8,0x4f,0x82,0xa2,
+0xfc,0x47,0x3a,0x83,0x86,0x35,0xc,0x17,0x91,0x9e,0x9d,0x74,0xca,0x95,0x29,0x77,
+0xe3,0x4a,0x82,0xaa,0xfa,0xa8,0xb3,0xd7,0x36,0x5c,0x4f,0xda,0x97,0x44,0x16,0xb4,
+0xa8,0x49,0x67,0x91,0x9f,0x75,0x41,0xa9,0x80,0x8e,0x4b,0xc8,0x0,0xe6,0x43,0xa4,
+0xfc,0x4b,0xeb,0x91,0xed,0xeb,0xff,0x83,0x15,0xd8,0x9f,0x60,0xf5,0x7c,0xc6,0x13,
+0xda,0x43,0xa1,0xbe,0x30,0xa7,0x91,0xd0,0x7a,0x82,0x9e,0xbd,0x33,0x31,0x9c,0x35,
+0xf5,0x49,0x8d,0x88,0xda,0xe,0x40,0xd3,0x27,0x95,0x97,0x5d,0xd7,0x4d,0xb1,0x3e,
+0x3d,0x47,0xa9,0xbb,0x17,0xb9,0x32,0xf4,0xa5,0x5a,0xaf,0xc5,0xfa,0x89,0x26,0xd5,
+0x35,0x4a,0x51,0xba,0x4d,0x9c,0x6,0x57,0x1b,0x28,0xfc,0x49,0x7,0x79,0x90,0x18,
+0xb9,0x45,0x8a,0xb7,0x37,0x63,0x94,0xa6,0x14,0x33,0x97,0xab,0x12,0x3b,0x63,0x2f,
+0x43,0x4f,0xe6,0xa9,0xfd,0x6d,0xf,0x90,0xf8,0xb2,0x5d,0xc9,0x9,0x7d,0xfa,0x9,
+0x9d,0x40,0xf8,0xad,0x34,0x73,0x59,0xab,0x88,0xb4,0x88,0xba,0xd4,0x92,0xd3,0x65,
+0x33,0x40,0xa7,0x82,0x33,0x90,0xe8,0x1f,0x19,0x2c,0xf1,0xe6,0x64,0xb3,0x13,0x55,
+0x6a,0x48,0xa9,0x84,0xef,0xca,0x5a,0xd0,0x12,0x1e,0xda,0x6b,0xd7,0xbf,0x17,0x15,
+0x7,0x48,0x8a,0xad,0x87,0x35,0xf4,0x10,0xea,0x4,0x58,0x25,0xff,0xc0,0x5e,0x5e,
+0xf6,0x4f,0x11,0x9d,0xac,0x24,0x9a,0x46,0xb4,0xab,0x47,0xa0,0x56,0xa7,0xb9,0x7,
+0xbe,0x47,0xff,0x84,0x55,0xc1,0x63,0x79,0xda,0x9e,0x44,0x9,0xc3,0x74,0xd8,0x19,
+0x9b,0x42,0x71,0x8f,0x10,0xa9,0xbc,0x29,0x9f,0x72,0x54,0xf3,0x1b,0xb9,0x3d,0xfb,
+0x81,0x49,0x4c,0xb7,0xac,0x56,0x89,0xbf,0x34,0x7d,0x51,0xd3,0xb5,0xa1,0x31,0x23,
+0xd7,0x4d,0x2d,0x92,0x83,0xaa,0xfe,0xe0,0x23,0x33,0x7b,0xa1,0x4e,0xaa,0x77,0x66,
+0x18,0x44,0x27,0x95,0x43,0x17,0xe6,0x47,0x4c,0x9a,0x24,0xee,0x45,0x57,0x4b,0xfc,
+0xc2,0x4f,0xef,0x90,0xbb,0xdd,0x48,0x68,0x2b,0xff,0x92,0xa3,0x39,0xdd,0xab,0xf1,
+0x87,0x4d,0xf6,0xba,0x61,0xc7,0xf8,0x47,0xa4,0x2a,0x9d,0xa1,0xb0,0xba,0xd1,0xb6,
+0xb0,0x4d,0x28,0x9d,0xe7,0xf8,0xc7,0x5,0x37,0xaf,0xda,0x5e,0xa6,0xd7,0x97,0x74,
+0xe3,0x4f,0x97,0xae,0xbf,0x91,0xbb,0x94,0xf2,0xf5,0xd9,0x4c,0xee,0xd9,0x81,0xc1,
+0x4f,0x4f,0x18,0xa2,0x22,0x42,0x86,0x87,0x5a,0x90,0xef,0x1,0x8c,0xb2,0x6d,0xe2,
+0x77,0x43,0x2f,0xa7,0xe6,0x2b,0xed,0xbd,0xbc,0x18,0xc3,0x7c,0x24,0xc6,0xd2,0x6a,
+0x87,0x41,0xe6,0x96,0x5d,0x58,0x8f,0x12,0xdd,0xb9,0x2c,0xe0,0x59,0xe7,0x1f,0x36,
+0xa2,0x4d,0xef,0xb6,0x3d,0x9b,0x36,0x35,0x15,0x45,0x1a,0x10,0xde,0xd4,0x82,0xf2,
+0xb0,0x47,0xcb,0x91,0xc3,0x53,0x60,0xa6,0xe7,0x35,0xac,0xd2,0x8b,0x4d,0xe5,0xed,
+0x58,0x4b,0x71,0x96,0x87,0xde,0x6c,0xa3,0x5,0xf6,0x8f,0x1f,0xb,0x83,0xbd,0x9c,
+0xa4,0x46,0xf,0x84,0x1d,0xb4,0xd8,0x5a,0x8b,0xd,0x16,0xd1,0x85,0xf9,0x19,0x74,
+0x7d,0x43,0x83,0x9a,0xe,0x9c,0x85,0x2,0xb8,0xda,0xed,0xd1,0xe9,0x26,0x55,0xe2,
+0x86,0x4c,0x1d,0x98,0xa7,0x46,0xd,0x78,0xcf,0x36,0xba,0x32,0xad,0x13,0x11,0x3b,
+0x1,0x48,0xf,0x90,0xc7,0x26,0x37,0x64,0x9c,0x8,0xd1,0x6,0xa3,0x3a,0x25,0xd3,
+0x1d,0x4a,0x99,0x92,0x51,0x99,0xca,0xb3,0xd8,0xf7,0x8,0x4d,0x9f,0x6f,0x68,0x95,
+0x76,0x4d,0xfa,0x81,0xba,0x79,0x96,0xa5,0xff,0x46,0xf1,0x39,0xe9,0x90,0x41,0xd8,
+0x51,0x4d,0xb1,0xbf,0x15,0x6b,0x97,0x97,0x66,0x8a,0xee,0xc7,0x2b,0x8b,0xed,0x52,
+0xb0,0x4f,0x4,0x86,0x9e,0xb6,0x83,0x82,0x34,0x6e,0x70,0x7c,0xd,0xb6,0x4,0x67,
+0x5a,0x47,0x52,0xbf,0x5c,0x4d,0x6b,0x96,0xb0,0xa3,0xbb,0xd1,0x3e,0x95,0x8b,0x4f,
+0x79,0x43,0xdb,0x90,0xc0,0xb7,0xa3,0x24,0x3e,0x9b,0xa5,0x64,0x62,0xe1,0xfd,0xf1,
+0x48,0x46,0x2e,0xa1,0xfd,0xa1,0x95,0xb6,0x1,0xf,0x43,0x23,0x19,0xbc,0xe5,0x6d,
+0x7e,0x43,0x5c,0x89,0x80,0x83,0xaf,0x5a,0x5d,0x52,0x16,0x21,0xd1,0xcf,0xca,0xf5,
+0x39,0x41,0x35,0x83,0xa1,0x2,0xde,0x44,0xd0,0xb2,0x20,0xc6,0xf2,0xc4,0xfa,0xec,
+0xf7,0x4e,0xcc,0xa9,0x70,0x91,0xd6,0x16,0x9b,0xc9,0x21,0x44,0xd1,0xf1,0x6b,0x52,
+0x71,0x46,0x95,0xb4,0x3,0xfe,0x4a,0xde,0xfb,0x40,0x9e,0x50,0xa2,0xc9,0xad,0xe3,
+0xba,0x48,0xa6,0x9e,0x70,0x66,0x1f,0xb3,0x1e,0xfb,0xce,0x2,0x35,0x3a,0x2b,0xd1,
+0xcb,0x4c,0x3d,0xa0,0xfd,0xe1,0x19,0xd,0xdb,0x6,0xbe,0xc,0xf1,0x8c,0xf5,0x8a,
+0x7,0x44,0x82,0x96,0x9,0x80,0xac,0x74,0x17,0x67,0x92,0x74,0x80,0x5d,0xf6,0xa0,
+0xc5,0x48,0xe8,0x88,0x1d,0xe7,0x97,0x6e,0x6c,0x40,0x98,0x34,0x1e,0x30,0x16,0xd,
+0xd6,0x47,0x70,0xa7,0x9f,0xac,0xa4,0x80,0xfa,0xb1,0xae,0x2,0xb1,0x2e,0x1e,0xa5,
+0xc8,0x41,0xc2,0xbe,0x99,0x48,0x77,0x68,0x88,0xd3,0x47,0x28,0xa0,0xf9,0xac,0x55,
+0x48,0x4c,0xe2,0xbf,0xcf,0x1e,0x41,0x67,0xf5,0x6,0x54,0x5e,0xcd,0x89,0xf,0xf3,
+0xec,0x4a,0x19,0xb0,0xe8,0x40,0x5d,0xbc,0xf5,0xf7,0x7,0x96,0xb6,0xa8,0x5c,0x81,
+0xb4,0x46,0xc4,0x8b,0xf6,0x10,0x12,0xde,0xf2,0xc8,0x45,0xe8,0x7,0x57,0xfe,0xbb,
+0x70,0x49,0x5d,0x9c,0x4f,0x78,0xcb,0xdc,0x5e,0x68,0x80,0x5d,0x49,0x8b,0x92,0x12,
+0xaf,0x48,0xa6,0x83,0x54,0xc,0xac,0x10,0x10,0xf8,0x1e,0xd4,0xf9,0x8d,0x20,0x24,
+0xb6,0x46,0x35,0xbf,0x8a,0x64,0xdf,0x83,0xa9,0xc,0x91,0x5,0x47,0x53,0x8d,0x44,
+0x23,0x42,0x68,0x9a,0xfd,0xbf,0xf,0xa2,0x2d,0x74,0xd0,0x6e,0x60,0x41,0x98,0x50,
+0x5,0x44,0x2b,0x9e,0x17,0xfc,0xc7,0x7e,0x12,0x69,0xa9,0x6e,0x45,0x66,0xf4,0xd9,
+0xf6,0x4f,0x20,0xb0,0x45,0xbf,0xed,0x33,0x2a,0x47,0x11,0x92,0xe,0x59,0x19,0x76,
+0x36,0x4d,0x74,0x84,0xe8,0xce,0xd6,0xcc,0xa0,0x58,0x56,0x21,0x9f,0x81,0x15,0xc5,
+0x33,0x41,0x38,0xb9,0x18,0xd3,0x1e,0xbe,0xb1,0x16,0x25,0x11,0xfa,0x9,0x55,0x43,
+0xaa,0x40,0x6c,0x88,0x6,0x7d,0x99,0xf6,0x7c,0xfe,0x14,0x3d,0x16,0x20,0x66,0x38,
+0xd2,0x4d,0xaf,0xab,0xd6,0x95,0x7,0x86,0xd0,0xb5,0x49,0x77,0xa8,0x98,0x1,0x19,
+0xc9,0x45,0x5c,0x89,0x97,0xb8,0x98,0xb,0x9,0x14,0x28,0x96,0x7b,0x47,0x37,0x2f,
+0x45,0x49,0xf2,0x81,0x1a,0x5a,0x5c,0x2d,0x98,0x88,0xc8,0x8a,0x4b,0x33,0xe0,0x83,
+0x74,0x4c,0xf0,0xbe,0xf2,0xc1,0x9,0x28,0x24,0xb5,0xab,0x6c,0x58,0x9b,0x58,0x34,
+0xf8,0x4c,0x36,0xa6,0x62,0xaf,0xfa,0x4b,0xab,0x5d,0x9d,0xab,0x3a,0x1b,0x3c,0xed,
+0x2,0x4d,0x6d,0xbc,0x5d,0xc5,0x47,0xf6,0x7d,0xe,0xf6,0xe0,0xa,0xac,0xe0,0x4c,
+0xbc,0x4b,0x67,0x8d,0xb,0xa6,0x5c,0xe3,0x73,0xb4,0xad,0xb,0x3d,0x1b,0xf7,0x7f,
+0x11,0x4f,0xc0,0xa2,0x5f,0x6,0xdf,0x34,0x26,0xe,0x34,0xdc,0xfc,0x3f,0xe6,0x42,
+0x60,0x48,0x22,0x87,0xd1,0x4c,0x36,0x54,0x39,0xe3,0x43,0x46,0x81,0x8d,0x94,0x4d,
+0xd2,0x4a,0x89,0xb8,0xe6,0xbe,0xd1,0x50,0x23,0x50,0xed,0xa,0x44,0xde,0x91,0x7b,
+0xad,0x4f,0xed,0xb9,0x32,0x8e,0xf8,0x20,0xeb,0x61,0x9b,0x41,0xa7,0xa2,0xee,0x6,
+0xf7,0x4b,0x10,0xbe,0x1f,0x95,0xf9,0xeb,0x62,0xa4,0x7d,0x2b,0xb6,0x1b,0xa,0x36,
+0xab,0x4b,0xdc,0x8a,0xe0,0xc9,0x9b,0x18,0x27,0xf7,0x37,0x79,0x8a,0x90,0x40,0x4f,
+0xab,0x4e,0xd8,0xb8,0xf7,0x43,0x2e,0xcc,0x1e,0x70,0x6c,0x69,0xed,0x5d,0x28,0xf2,
+0x28,0x43,0xe5,0x84,0x5,0x82,0x49,0x5a,0xed,0x31,0x46,0xe0,0x89,0xd,0xfc,0xea,
+0xf,0x4b,0xe9,0xb6,0xd1,0xf3,0x9c,0x79,0x79,0x9c,0x1f,0x14,0xc3,0xff,0x87,0x6e,
+0xc6,0x44,0xaf,0xb9,0x7a,0x37,0xd6,0x21,0x8,0x6a,0xe,0x75,0x12,0x46,0xe7,0x14,
+0x37,0x49,0xbf,0xb1,0xc7,0x13,0xf9,0xb9,0x51,0xc8,0xa4,0xba,0xe9,0xe3,0xc7,0xb1,
+0x8c,0x47,0x14,0x8d,0x1c,0x84,0x7f,0x9e,0x4,0xdf,0xdb,0xb7,0x9f,0x6f,0xf2,0x14,
+0xd,0x4f,0x11,0xb7,0xd7,0x3e,0xfb,0x1b,0xb2,0x98,0x2d,0xba,0x6,0x9,0x44,0xa2,
+0xc2,0x42,0x3d,0xa3,0xe,0xc9,0x67,0xcd,0x7c,0xa5,0xf5,0x71,0x13,0xc4,0xc0,0xc9,
+0xfa,0x42,0x1e,0x8c,0x19,0xb9,0xa5,0x8b,0x8f,0x1d,0xe4,0x2a,0x5e,0xc8,0xff,0x83,
+0x53,0x4b,0x42,0xbf,0x56,0x9c,0xc5,0xc,0x43,0xf8,0x1d,0x78,0x2a,0x36,0x1,0x6c,
+0x6e,0x4d,0x2c,0xa2,0x1f,0x56,0x57,0xbd,0x97,0x54,0x66,0x48,0x36,0xc,0x99,0xbe,
+0xc6,0x40,0x3f,0x9c,0x93,0x54,0x4c,0xc4,0x2f,0x8c,0x8a,0x88,0x82,0x4f,0x93,0xc0,
+0xf9,0x4f,0x2a,0x90,0x81,0x71,0xa7,0xc2,0x4a,0xae,0xba,0xb7,0xc2,0x72,0x32,0x95,
+0x5f,0x4d,0x41,0xa9,0x54,0x91,0x78,0x35,0xe6,0x59,0xeb,0xad,0xe,0x6,0x5,0xf3,
+0x97,0x40,0x20,0xaa,0x29,0xba,0x69,0x17,0x31,0xfb,0xe4,0x2c,0x6c,0x9a,0x6d,0x41,
+0x98,0x4c,0x81,0xb9,0xc1,0x23,0x78,0x2b,0xc2,0x58,0x1d,0x3c,0x32,0x3,0xc6,0x89,
+0x3,0x4e,0xa5,0xb8,0x96,0x98,0x58,0x2,0x2d,0x22,0x6d,0x9b,0x42,0x21,0x6a,0x6c,
+0x45,0x48,0x68,0x85,0x12,0x7a,0xa9,0xe8,0x48,0x79,0x38,0xba,0xad,0xf7,0x2d,0x46,
+0x31,0x4a,0xed,0xb4,0x5b,0xf2,0xb5,0x3d,0xff,0xe3,0x1b,0x8c,0x89,0x50,0xe4,0x7,
+0x94,0x4f,0xe2,0xb3,0x42,0x70,0xcb,0x63,0x7b,0xa7,0xbd,0x4,0x7f,0x2e,0xcb,0x1d,
+0xe5,0x4f,0x23,0x95,0xfa,0xef,0xa9,0x4a,0x93,0x18,0xbe,0xb7,0x41,0x49,0xa5,0xbf,
+0x14,0x4d,0xeb,0xa4,0x53,0x20,0x65,0x30,0xe0,0x31,0x7b,0xd5,0xcb,0x15,0xc7,0x12,
+0x48,0x45,0xb6,0xa5,0x1e,0xb9,0xc6,0x3c,0x6a,0x39,0x4f,0xfd,0x1b,0x51,0x42,0xb2,
+0xc5,0x45,0x73,0x8b,0xec,0xf3,0x27,0xfe,0xee,0x2b,0x66,0x95,0x2d,0x37,0xe,0x4e,
+0x14,0x4c,0xd9,0xa9,0x22,0x8b,0xc7,0xf6,0xc6,0xd7,0x5d,0x35,0xaa,0xe1,0x66,0x54,
+0xce,0x49,0x1b,0x8e,0x34,0x5f,0xca,0xdc,0x87,0xe1,0x45,0xf7,0x35,0x65,0xf0,0x27,
+0x4e,0x42,0x1b,0xbd,0x0,0xa3,0xe2,0xdd,0xe2,0xdd,0x64,0x53,0xce,0x48,0xf1,0x47,
+0x50,0x46,0x29,0xa9,0xef,0xd8,0x6d,0x1,0xb7,0xfd,0x95,0x7f,0xdc,0x20,0x86,0x27,
+0x8e,0x45,0x40,0x82,0x92,0x2b,0x3d,0x4d,0xc4,0xf9,0xc0,0xc2,0x4a,0xc3,0xda,0xf8,
+0x20,0x4c,0xc2,0xb8,0x8a,0xcf,0xf1,0xd1,0x52,0xc9,0x47,0xce,0x3e,0x6b,0x79,0x8b,
+0x7f,0x42,0x87,0x86,0xb9,0xa2,0x1,0x16,0x79,0xd7,0x3,0xa0,0x1c,0x27,0x93,0x3d,
+0x45,0x48,0x54,0xa0,0x25,0xa0,0x4f,0x6b,0xb4,0x1f,0x6f,0x1c,0x4c,0xb9,0xd5,0xaa,
+0x35,0x4e,0x37,0xb9,0x5,0xe1,0x8,0x70,0xf8,0x21,0x22,0x5e,0xc,0x0,0x91,0x59,
+0x94,0x4c,0xff,0xb6,0x62,0xbe,0x61,0x13,0xfa,0x6c,0xa3,0xa1,0x6c,0x66,0xb8,0x5c,
+0xcc,0x4c,0x79,0x95,0x55,0xb2,0xcc,0xfe,0x5d,0xc,0x94,0x45,0xa5,0x4d,0x40,0xb5,
+0x1e,0x4e,0x61,0x9f,0xfe,0xd1,0xb8,0xeb,0xdc,0x4a,0xd2,0x89,0x3c,0xef,0x1b,0xab,
+0x6c,0x4f,0x4e,0xb4,0x3e,0xd4,0xd8,0x89,0xad,0xd7,0x4e,0xe3,0xa2,0x46,0xe0,0x3,
+0xd8,0x48,0x78,0xb0,0x4b,0x56,0x93,0x9f,0x36,0xf9,0xc1,0xd,0x79,0x43,0x75,0x97,
+0x37,0x46,0x50,0xa5,0x14,0x17,0x10,0xc6,0x3f,0x78,0xc6,0xf9,0x69,0xd3,0xf9,0xc,
+0x18,0x41,0xa5,0x91,0x72,0x41,0xe5,0x7f,0x1a,0x87,0xd7,0x44,0xa8,0x83,0x17,0x99,
+0xa4,0x44,0x61,0xb6,0xb2,0xb,0xd8,0xcb,0x5,0xa2,0x59,0x35,0xaf,0x9e,0x60,0x7e,
+0x2d,0x41,0x72,0xb3,0x73,0xe,0xf0,0xb3,0xe6,0x66,0x20,0x6b,0x3a,0x78,0x5e,0x15,
+0x21,0x4f,0x8,0xac,0x84,0x88,0xc3,0x20,0xc0,0x5,0xaf,0xd4,0x19,0x16,0x36,0xab,
+0xce,0x41,0xe2,0x95,0x35,0x44,0x24,0x96,0xc9,0x93,0xcd,0x39,0x21,0x7c,0xf,0x84,
+0xf4,0x4d,0x13,0xa6,0xc3,0xbe,0xc8,0xcc,0xdf,0x90,0x75,0x4c,0x6b,0xf6,0x38,0xe1,
+0xe7,0x4f,0xbb,0x96,0xb8,0x6d,0x8c,0xb0,0x56,0xf3,0x23,0xb5,0x84,0xc7,0xca,0x9,
+0xad,0x4b,0x35,0xb1,0x22,0x4d,0x80,0x24,0xe,0xf8,0x43,0xb9,0x96,0xa6,0x22,0x6f,
+0x53,0x4e,0xe4,0x84,0x91,0xf0,0xcf,0x1c,0x8,0xc4,0x76,0x86,0x15,0xf4,0xe4,0x10,
+0xa3,0x45,0x9a,0x98,0x7c,0x7,0xfd,0x87,0x9d,0x3e,0xc2,0x95,0xfb,0x78,0x5e,0x5,
+0xc4,0x47,0x31,0xb2,0x52,0xed,0x26,0xbc,0x31,0x8c,0x2,0x13,0x5e,0x70,0x44,0xd3,
+0xe3,0x41,0xa6,0x9b,0x25,0xb2,0x75,0xa7,0xe4,0x2,0x65,0xed,0x5a,0xf5,0x3c,0x14,
+0x56,0x42,0x94,0x98,0xbb,0xb3,0x7b,0xa7,0x77,0x62,0xa0,0x8b,0x72,0x60,0x2f,0xbd,
+0xfd,0x4b,0x24,0x8a,0xc7,0x51,0xfb,0x6e,0x96,0xee,0xb3,0xc7,0x2f,0xfa,0x95,0xe9,
+0x24,0x42,0xdf,0x8e,0xe0,0xdb,0xa1,0x49,0xa,0xd8,0xa9,0x16,0x3,0x81,0x3f,0xaa,
+0x81,0x48,0xb,0xa1,0x21,0x5,0xd,0x5c,0xc0,0xcf,0xd7,0xa2,0x82,0x86,0x92,0xeb,
+0xea,0x42,0x63,0x8c,0x98,0x35,0x65,0x29,0xbb,0x4f,0xb4,0xb5,0xe4,0x57,0xd3,0x29,
+0x68,0x42,0x11,0x84,0xa4,0x1a,0xf9,0x17,0x8,0x8a,0xda,0x33,0xd2,0x4b,0x56,0xcf,
+0x89,0x40,0xd7,0x9e,0xf5,0x62,0xf,0x67,0xfa,0xde,0x73,0xf4,0xe9,0xdc,0x35,0x65,
+0x9b,0x4b,0xfb,0xb9,0x66,0x58,0x25,0x62,0xde,0x61,0x7b,0x1a,0x6b,0xa4,0x6d,0xc7,
+0x31,0x4f,0xad,0x9d,0x75,0xaa,0x83,0x6a,0x8,0xf9,0x7a,0x3e,0x65,0x7b,0xf5,0x2e,
+0x3,0x4a,0xea,0x8d,0xfc,0x4f,0x96,0x58,0x69,0x8e,0x6f,0x7a,0x98,0xc1,0x60,0xe5,
+0x25,0x4c,0x18,0xa2,0x1d,0x38,0x6b,0x4a,0x56,0xee,0x9c,0x50,0x6,0x1,0xf8,0xce,
+0x95,0x47,0x66,0xae,0xf5,0xf1,0x12,0xec,0xb1,0xcd,0xe0,0x80,0xd7,0xc5,0x7d,0xf7,
+0xec,0x48,0x19,0x9d,0x6a,0xf0,0x58,0x39,0x3a,0xbf,0xd2,0x37,0x24,0x31,0x98,0xbf,
+0x9a,0x43,0x3e,0x88,0xdb,0x80,0xb6,0x69,0x88,0x81,0x7d,0x30,0x75,0x1a,0xf3,0x97,
+0xce,0x40,0xd5,0x8e,0x19,0xf5,0xba,0xc9,0xcb,0xcc,0x3e,0xd2,0xb5,0x6f,0xe7,0x43,
+0xae,0x4e,0xbc,0xb5,0x52,0xc7,0x4b,0xbf,0x7d,0xb9,0x74,0x8e,0x62,0x70,0x36,0xe4,
+0x74,0x4c,0x46,0x80,0xc8,0x29,0x2f,0x4f,0x32,0xce,0x21,0xd,0xa,0x3c,0x79,0xe8,
+0x89,0x4e,0x71,0x8b,0x18,0x12,0x35,0x6c,0x27,0x90,0x4,0xcb,0x87,0xc0,0xe5,0xb6,
+0x77,0x42,0x9e,0xa2,0x25,0xec,0x40,0x6e,0x5f,0x4a,0x59,0xcc,0x7a,0xa6,0x56,0xc8,
+0x9,0x49,0x53,0xa5,0x9b,0x92,0x4e,0x13,0x5c,0x9f,0x4f,0x15,0xd2,0x26,0x2e,0xd8,
+0x12,0x4c,0x9d,0xb5,0x6,0x7,0x7c,0x40,0x96,0x62,0x11,0x7f,0x22,0x78,0xe7,0xe8,
+0xbe,0x42,0x7d,0x80,0xe9,0x91,0x5e,0x8,0xbe,0x25,0x26,0xa9,0x49,0x30,0x68,0x13,
+0xbe,0x49,0x2d,0x9e,0x5,0xdf,0xb9,0x52,0x63,0xa3,0x8f,0x42,0xc5,0xe8,0x24,0x87,
+0x28,0x48,0xe4,0xa0,0x38,0xe2,0x29,0xf7,0xee,0x88,0xca,0x84,0x5b,0x5,0x85,0x41,
+0xa0,0x46,0xc8,0xa4,0x1f,0xfa,0x70,0x68,0x7e,0x89,0x7b,0xdd,0xf1,0x4a,0xec,0xff,
+0x38,0x4b,0x8c,0x99,0x8,0xc5,0xfd,0x93,0xa3,0xd3,0xcd,0x46,0x86,0xb1,0x62,0xce,
+0x91,0x4b,0xc1,0x87,0x1,0x28,0x7b,0xe3,0xbf,0x6e,0x2,0xe5,0x7d,0x97,0x7f,0x1f,
+0x93,0x48,0x5f,0x80,0x44,0x63,0xc5,0xcc,0xa6,0xc,0xf8,0xd6,0x80,0x46,0x39,0xbf,
+0xd2,0x46,0x5b,0xb5,0x85,0x6d,0xe2,0x5e,0x7,0x61,0xd7,0x62,0x19,0x88,0x2b,0xa2,
+0x64,0x49,0xb1,0x8e,0x55,0x15,0xfc,0xcd,0xa1,0x69,0x56,0x3f,0x11,0x1,0x8e,0xf2,
+0x93,0x47,0xa,0xb2,0x16,0xa0,0xac,0xf8,0x22,0xad,0x27,0x7d,0x1f,0x9,0xdc,0x1f,
+0xf5,0x4f,0x99,0xbd,0x9,0x1c,0xf2,0xe0,0x64,0xec,0x19,0xb7,0x9a,0x36,0xfc,0x8b,
+0xe5,0x4e,0x99,0xbe,0x16,0x87,0x86,0xc1,0xda,0x97,0xf3,0xd6,0x4d,0x55,0xf3,0x79,
+0x6b,0x4a,0x4b,0x9f,0xf,0x95,0xf2,0xbd,0xf8,0xe,0x84,0xe2,0xe8,0x13,0x4b,0x2a,
+0xf8,0x4e,0x78,0x98,0x79,0x40,0x8a,0x34,0x4e,0xa0,0xe3,0xc9,0xc5,0x3d,0xb9,0xf0,
+0x64,0x4b,0xf6,0xbc,0xab,0x5e,0x39,0x90,0xf3,0xa3,0x99,0xf0,0xb1,0x48,0xfa,0x90,
+0xe8,0x40,0xeb,0xaa,0xd5,0xec,0xa7,0x3b,0xda,0x9a,0xee,0x7e,0x3a,0x94,0x32,0xd,
+0x1f,0x4f,0xcc,0xbf,0x30,0xc6,0xab,0xce,0x24,0xe,0x61,0xb0,0x25,0x82,0x3a,0xce,
+0xe7,0x4b,0x4d,0x86,0xc4,0xc5,0xd9,0x4f,0xb1,0x47,0x7f,0xbd,0x3f,0x13,0x9f,0xdf,
+0x2c,0x48,0xd1,0xad,0x7c,0x29,0xad,0x5e,0xee,0x97,0x77,0x9,0x51,0x16,0x3d,0x14,
+0x4e,0x40,0x3d,0xb1,0xc3,0xdd,0x7b,0xe6,0x28,0x6,0xb6,0xd,0xc4,0x89,0x4f,0x5e,
+0x1b,0x45,0xff,0xb0,0x98,0x23,0x91,0x97,0x5d,0x69,0xa6,0x84,0xf,0x4c,0x5b,0x93,
+0x50,0x40,0x4,0x90,0xb4,0x4b,0xc5,0x5b,0x4d,0x50,0x35,0xe0,0xc6,0x33,0xc9,0x4,
+0x13,0x48,0xb7,0x8b,0x20,0xf0,0xdf,0xae,0xc1,0xbb,0xbb,0x86,0xa2,0x3e,0x77,0x4c,
+0xc3,0x4c,0xe2,0xa9,0xfd,0x6e,0xe2,0xde,0xe9,0xa,0xe9,0x43,0xc2,0x55,0x53,0xe7,
+0x55,0x43,0xfb,0x87,0x1e,0x98,0x73,0xa5,0xa0,0x4a,0xfb,0x7d,0x0,0xe3,0x64,0x5f,
+0x5,0x47,0x36,0xaa,0xa4,0x4b,0xac,0x80,0xd9,0xbd,0xe4,0x6d,0xd7,0xf9,0xe7,0x65,
+0xe0,0x45,0x9b,0xb6,0x7f,0x47,0xbb,0x53,0x23,0xd0,0x43,0x59,0xb5,0x26,0xd3,0x16,
+0xee,0x47,0xc6,0xbf,0xf3,0xaf,0x76,0xff,0xe5,0x80,0xe,0x78,0xc6,0xdf,0x18,0xc9,
+0xbe,0x43,0x5,0x89,0xe8,0x58,0x40,0x87,0xf2,0xfb,0x9b,0x9e,0xcf,0x7b,0x38,0xfc,
+0x9e,0x44,0xd3,0xa3,0x0,0xaf,0x45,0x16,0x37,0x57,0xc1,0x9,0x37,0x2a,0xd3,0x88,
+0xc8,0x40,0x7e,0x97,0x40,0x92,0xea,0x1,0xd0,0x7a,0x67,0x18,0xa1,0xc4,0x6a,0x59,
+0x59,0x4b,0xe0,0x8e,0xcd,0xaa,0x53,0x52,0x7b,0xd9,0x55,0xa,0xc0,0xc1,0x4a,0x89,
+0x14,0x40,0xe,0x89,0xfa,0x52,0xc9,0xad,0x74,0x54,0x1c,0x71,0x30,0x81,0xc1,0xf4,
+0xc1,0x4f,0xb0,0x95,0xb8,0x9f,0x1,0x2d,0xc,0x41,0x9f,0x1f,0x51,0x21,0xe5,0x8a,
+0xe7,0x40,0x6d,0x8f,0x3f,0xc5,0x60,0x7e,0xe5,0x2d,0xb3,0xd5,0xac,0x69,0xdd,0x69,
+0x65,0x46,0xdf,0xa0,0xc8,0x81,0x6e,0xe0,0x7c,0x11,0x23,0x9c,0x5a,0x7e,0xad,0x41,
+0xa,0x4b,0x86,0xa5,0xa0,0x8e,0x5c,0x50,0xec,0x77,0x95,0xc5,0xb6,0xaa,0x1e,0x18,
+0xfd,0x46,0xd3,0xbf,0xc0,0xea,0x74,0x8d,0xc2,0x13,0xf7,0xd6,0x18,0xf6,0xcf,0x86,
+0xdc,0x42,0xf3,0xae,0xe,0xc8,0x79,0x89,0x86,0x94,0x37,0xa3,0x55,0x16,0x53,0x38,
+0x37,0x40,0x3b,0xaf,0xf,0x9b,0xd8,0xe3,0x74,0x83,0x8d,0x86,0x88,0xb0,0xe9,0xeb,
+0xd5,0x46,0x3a,0xae,0x5d,0x6f,0x67,0x25,0xcb,0x5d,0xe2,0x70,0x3c,0x70,0x31,0xf3,
+0x92,0x40,0x6,0x99,0x51,0xcd,0x5b,0x34,0x46,0x7,0x1e,0x6c,0xf4,0xa7,0x40,0x47,
+0xe2,0x44,0xdf,0xb6,0xab,0xa8,0xde,0xff,0x6a,0x24,0xe5,0x35,0xca,0x16,0x5,0x13,
+0x71,0x41,0xbb,0x8d,0xe,0x87,0xb5,0x5b,0xdb,0xb7,0xf9,0x5,0xdc,0xb4,0x31,0xef,
+0xa9,0x45,0x9,0x9f,0xfd,0x2d,0x10,0x91,0xf7,0x2c,0xa2,0x47,0x73,0xbf,0xf1,0xb8,
+0x3f,0x43,0x69,0x90,0x58,0x18,0x17,0xdf,0x1d,0xc,0x67,0x51,0x96,0x3,0xba,0x62,
+0x14,0x43,0xab,0x8e,0x4e,0xec,0xf3,0xa4,0x95,0xfb,0x34,0xa9,0x84,0x7b,0xbc,0xff,
+0xa8,0x43,0x27,0x96,0xdb,0x1d,0xea,0xe7,0x17,0x35,0x8b,0xc2,0x48,0xfc,0x43,0x28,
+0x59,0x49,0xd2,0x95,0x85,0x84,0x17,0x40,0x33,0x58,0x7a,0x4,0xf9,0x30,0x51,0x89,
+0x28,0x45,0x1d,0x8f,0xaa,0x13,0x5f,0x18,0x73,0xfa,0x3d,0x36,0xeb,0x26,0xd2,0x90,
+0xc8,0x4e,0x92,0xa5,0x24,0x97,0x9,0xf5,0x53,0x81,0x2d,0xb1,0xa0,0xb,0xe8,0x62,
+0x5c,0x44,0xe1,0x8d,0xed,0xb4,0x84,0xad,0x78,0xb4,0xf,0x85,0xda,0x3f,0x26,0xad,
+0xfd,0x43,0x20,0x87,0x42,0x69,0xeb,0x28,0xed,0x2e,0x44,0xcd,0xc7,0x96,0x41,0x49,
+0x10,0x41,0x9c,0x8d,0x80,0xce,0xa6,0xf1,0xf4,0x5c,0x90,0xd0,0x7b,0xe,0x7c,0x5a,
+0x43,0x46,0xbb,0xb9,0x2d,0x49,0xb2,0xd6,0xef,0x6,0xd7,0x36,0xef,0x97,0x9,0x67,
+0x2e,0x4c,0x47,0xbe,0xa6,0xe0,0x96,0xfa,0xea,0x5e,0x4d,0x8d,0x9a,0x30,0x3b,0xe3,
+0x72,0x45,0x77,0x96,0xa3,0x62,0x39,0xd0,0x6c,0x94,0xf6,0x98,0xbc,0xa5,0xad,0xef,
+0x9f,0x4a,0xed,0xae,0xce,0x76,0xa7,0xd3,0x8f,0xd9,0x6c,0xca,0x69,0xc6,0x31,0x16,
+0xd5,0x44,0xb,0x99,0x6b,0x42,0xf4,0x40,0xa,0x25,0x97,0x8b,0xe6,0x88,0x81,0xca,
+0xa7,0x41,0xf9,0x80,0xd3,0x15,0xac,0x11,0x64,0xb5,0x25,0x4e,0xa5,0xfa,0x70,0x8e,
+0x9e,0x4a,0xe,0xb3,0x15,0xc,0xb7,0x8d,0x4c,0x15,0x8e,0xde,0xaa,0xc6,0xba,0x90,
+0x15,0x4a,0xad,0x89,0x8a,0x93,0x46,0x2f,0x9a,0x55,0xde,0x3e,0x91,0x1b,0x74,0xf2,
+0x74,0x41,0xd6,0x8d,0x7a,0xbc,0x4c,0xa8,0x0,0xf9,0xae,0x2f,0xee,0x20,0x61,0xda,
+0xaa,0x47,0x6d,0x97,0xb5,0x63,0xcc,0xfc,0x83,0x7a,0x1a,0x5d,0x73,0x5c,0xdf,0x5a,
+0x19,0x44,0xb5,0x9c,0x97,0xa0,0x0,0x7e,0xd8,0xb5,0xb4,0xe8,0xda,0x52,0x6f,0x59,
+0x88,0x44,0xa3,0x9d,0x7b,0xe8,0x55,0x6c,0x35,0x2f,0xb0,0x7b,0x47,0xdf,0xf7,0xfc,
+0x63,0x49,0x22,0xb5,0x51,0xfc,0xb8,0xb1,0xbc,0x41,0xec,0xe0,0x25,0xf2,0x7c,0x55,
+0x6d,0x42,0x37,0x80,0x4d,0x12,0x6d,0xf1,0x9b,0x4f,0x5c,0xd4,0x83,0xcc,0xbb,0xb4,
+0xbd,0x47,0xaf,0x89,0x41,0xfb,0xb9,0xbf,0xe4,0x95,0x1d,0x2a,0x31,0x58,0x20,0x3d,
+0x60,0x42,0xe8,0xbb,0x43,0xd8,0xb7,0xae,0x16,0xdc,0x5a,0xb4,0x5a,0xa6,0x18,0xd6,
+0x72,0x47,0xcf,0xaa,0x2e,0x1f,0x12,0x43,0x50,0x8e,0x54,0x27,0x6d,0x2a,0x55,0xe5,
+0x64,0x42,0xb1,0x87,0x4b,0x1e,0x9f,0xf0,0x55,0xe,0xd8,0xea,0x7d,0x65,0xdc,0xc1,
+0xd5,0x4f,0x30,0x97,0x69,0x1f,0xff,0xec,0x8,0xcd,0x92,0xb4,0xf5,0xcd,0xa6,0x3e,
+0xba,0x44,0xb0,0xb5,0xa7,0x32,0xaa,0xe3,0x31,0x52,0x61,0x29,0xb,0x2,0x97,0x39,
+0x56,0x4f,0x1f,0xb7,0x55,0xa1,0x82,0x66,0xa0,0xac,0x22,0x7d,0xe4,0x8f,0x26,0x98,
+0xa6,0x4c,0xc,0x9d,0xe7,0x21,0xd1,0xaa,0x8a,0x6d,0x4f,0x51,0x7d,0x6c,0xa4,0xa5,
+0x8d,0x4a,0x92,0x9f,0xe5,0xed,0x6a,0x9,0x1,0xf1,0x6e,0xdb,0x24,0x43,0x5a,0x69,
+0xdc,0x41,0xa9,0xae,0x6b,0xf0,0xd8,0x3b,0x28,0x83,0x63,0xce,0xcb,0xdf,0xbc,0x2f,
+0xa1,0x44,0x30,0x86,0x90,0xc0,0x2,0xf2,0xa0,0xfd,0x2c,0x4c,0x29,0x25,0x9e,0x8f,
+0x2c,0x4b,0xb8,0xa8,0x1b,0xf3,0xf0,0x2e,0xc2,0xa3,0x10,0x5e,0x30,0x59,0x18,0x96,
+0x56,0x48,0xd8,0x81,0xeb,0x96,0xb4,0x95,0x2a,0x33,0x8a,0xec,0x1b,0x5c,0x7d,0xba,
+0x58,0x4a,0x38,0xa9,0x84,0x9e,0xe1,0xc3,0x3,0x31,0x32,0x32,0x6c,0xb9,0x10,0xab,
+0xba,0x4f,0x9a,0xae,0x3d,0x47,0xbf,0xef,0xa2,0xf4,0x8,0x7d,0xd8,0x77,0x13,0x38,
+0x82,0x48,0xbd,0xad,0x48,0xdd,0x11,0xb,0xa2,0x5d,0xfc,0x77,0x1b,0xf2,0xac,0x4d,
+0xdf,0x41,0x9,0x92,0x90,0x98,0xb9,0xc8,0x86,0x30,0xa7,0xf5,0x43,0x83,0x25,0xc6,
+0x7,0x41,0x16,0xa3,0x15,0xeb,0x28,0x51,0x98,0x4a,0x7a,0xcc,0x71,0xc,0xfd,0x3b,
+0x57,0x42,0xaf,0x8e,0x6d,0x42,0xac,0x44,0xd7,0xc,0x51,0x69,0x53,0x73,0xd3,0xba,
+0xf5,0x47,0x4d,0xb6,0x4a,0xb4,0x3c,0x6e,0x7,0xae,0xd0,0x89,0xf5,0xd3,0x22,0x63,
+0x8a,0x4a,0x2a,0xb8,0x8a,0x48,0xb2,0xb,0x32,0x1,0xb0,0x3a,0xd8,0xe4,0xe0,0xfb,
+0xeb,0x43,0x87,0xa5,0x1,0xc9,0xb6,0xe7,0x19,0x19,0xd9,0xce,0x2e,0xb8,0xd4,0x29,
+0x3a,0x44,0xa8,0x82,0xf5,0x94,0x24,0x8c,0x66,0xb9,0xdf,0xeb,0x9,0x29,0xea,0x20,
+0xeb,0x49,0xb0,0x89,0xab,0x49,0xa0,0xaa,0xc6,0x95,0x9c,0xfc,0xba,0xc,0x74,0x9c,
+0x7c,0x45,0xe9,0xb8,0xb1,0x47,0x8f,0xdc,0xad,0x44,0x7a,0x13,0xb2,0x60,0xaf,0x17,
+0xe1,0x4e,0x1a,0xb0,0xd5,0x21,0x37,0x92,0x3b,0xa2,0x7e,0x97,0x11,0x71,0x6c,0x6a,
+0xd3,0x41,0xc7,0x8d,0x32,0xd9,0xb9,0x65,0xb3,0x46,0x13,0xd9,0xba,0xb,0xcc,0xcd,
+0xf5,0x49,0x23,0xb7,0x82,0x6e,0x4,0x4c,0xc9,0xe0,0x82,0x4b,0x11,0x52,0x6f,0xc9,
+0xbd,0x41,0x54,0x8f,0x91,0xe9,0x0,0x67,0x80,0x1e,0xe6,0x2e,0x13,0x1b,0x3d,0xf7,
+0xe2,0x46,0x8,0xb5,0xb2,0x82,0x61,0xd0,0x47,0x43,0x72,0xb8,0xdf,0xac,0x6a,0x7f,
+0xd0,0x42,0x90,0xb6,0xc0,0x9a,0xe7,0x7d,0xa2,0xb1,0x8b,0xd6,0x8a,0x3a,0x80,0x9a,
+0x94,0x49,0x2a,0xb2,0xe3,0x42,0x59,0x1,0x23,0x65,0x45,0xb9,0x59,0x24,0x9b,0x1d,
+0x6d,0x4a,0xaf,0x81,0x3e,0xbe,0xe9,0x16,0xe2,0xf4,0x60,0x6f,0x29,0x1a,0xa8,0xf3,
+0x61,0x48,0x95,0x8f,0x3,0x60,0xd2,0x29,0xe8,0x71,0xe0,0x85,0x44,0xb8,0xab,0xb7,
+0x78,0x4e,0x39,0xbd,0x85,0xd9,0x44,0xa2,0xec,0x68,0xb6,0x1e,0x5,0x1f,0x6d,0xc,
+0xa3,0x40,0x36,0x8b,0x66,0x16,0x45,0x8d,0x15,0xf8,0xe3,0x17,0xd0,0x1d,0x44,0x9b,
+0x1,0x42,0x7b,0x82,0xa1,0x31,0x12,0xfc,0xbe,0x1f,0xb2,0xcb,0x72,0x7c,0xf8,0x57,
+0xb7,0x44,0xa,0x8c,0xd,0x95,0xa1,0x78,0x8f,0xa1,0x9a,0xee,0xd6,0x1a,0x3b,0x94,
+0x7e,0x48,0xda,0xac,0x5,0x3d,0xec,0x8a,0xbd,0xc5,0xc,0x6c,0x89,0xf5,0x7d,0x75,
+0x29,0x48,0x80,0xb4,0xf1,0xe7,0xe8,0xe5,0x55,0x85,0xb7,0x37,0xab,0xd4,0x16,0x43,
+0x16,0x4b,0xb8,0x8c,0x76,0x55,0x77,0x71,0x8e,0xc7,0x10,0x82,0xc2,0x69,0x25,0xbf,
+0xe5,0x4c,0x55,0xb3,0xbc,0xbd,0x54,0xc,0x29,0xe3,0xb5,0x33,0x59,0x8f,0xf1,0xe1,
+0xf1,0x4f,0x55,0x97,0x9b,0x53,0xa7,0x8f,0x86,0x6b,0x98,0x46,0x2e,0xbb,0xa3,0x4b,
+0xd3,0x49,0x5c,0x87,0x4,0x8a,0xc8,0x8e,0xa0,0xde,0xfe,0xa8,0xe2,0x3a,0xbf,0x94,
+0x5d,0x41,0xb2,0x89,0xaa,0xb4,0x2d,0xf9,0xc,0x93,0x6f,0xb3,0xc1,0xd2,0x4c,0x8a,
+0x60,0x4c,0x8c,0x9a,0x37,0xea,0x50,0x2e,0xa9,0xf3,0x57,0x6e,0x7d,0x9e,0xa4,0xd,
+0xf1,0x47,0x65,0x8c,0xd6,0x52,0xce,0x9a,0x40,0x13,0x5b,0x25,0x7b,0x30,0x32,0xaf,
+0x11,0x4d,0x1,0x90,0xc3,0x3d,0x97,0x7e,0x19,0xaa,0xec,0x8d,0x82,0x73,0xaf,0x24,
+0x4d,0x47,0xce,0x80,0x90,0xd9,0xd,0x64,0x3e,0x64,0xf6,0xd8,0xc,0x55,0x14,0x94,
+0xb4,0x4c,0xa4,0xb9,0xdb,0x1d,0x38,0x57,0x15,0x5a,0xdd,0xb8,0x3,0x9a,0xdd,0x30,
+0x1c,0x41,0xe2,0xac,0x1b,0x44,0x87,0x9f,0x76,0xa2,0x9a,0xea,0xfd,0x25,0x4f,0xc7,
+0x14,0x4c,0xfb,0xa8,0x5a,0xcd,0xb8,0x5e,0x22,0x33,0x2b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6a,0x9a,0x29,0x3f,0x59,
+0x5,0x42,0x11,0x87,0xc9,0xc3,0x59,0xd6,0xb0,0xf5,0xa7,0x95,0x75,0x11,0xf4,0x6e,
+0x31,0x49,0x25,0x8a,0x8f,0xf,0x58,0x49,0xb2,0x11,0x53,0x83,0x6d,0xcc,0xa4,0x84,
+0x5f,0x4e,0xd6,0xb0,0x53,0x11,0xb8,0xaa,0x62,0x40,0xae,0xc5,0x70,0x0,0x99,0xb3,
+0x3a,0x49,0xb2,0xbf,0xfb,0x67,0x6,0x4d,0xfe,0x6e,0xb8,0x4b,0xe9,0xe1,0xe7,0x98,
+0xa4,0x41,0xad,0xb4,0xe8,0x73,0x72,0x92,0x34,0xa5,0x65,0xfa,0x5a,0xc6,0x88,0xf7,
+0x82,0x42,0xbf,0x8f,0x10,0x4e,0xa4,0xd1,0x5c,0x8f,0xf0,0x84,0xd3,0x60,0xd2,0x62,
+0xe7,0x4d,0xcc,0xbf,0x74,0x7b,0x1e,0x61,0x7a,0x58,0x1a,0x7c,0x1a,0xcd,0xb2,0xa0,
+0xfa,0x46,0x6b,0x95,0xfd,0xfa,0x46,0x4c,0xdd,0xc8,0xd4,0xbc,0x66,0x6f,0x1b,0xf6,
+0xcb,0x4a,0x5,0xa0,0xdd,0xe4,0xf3,0x68,0x5e,0x58,0xe3,0x61,0xb4,0x67,0x44,0x44,
+0x3a,0x40,0x59,0xa4,0xc4,0xe0,0xf6,0x48,0xab,0x28,0x86,0x3a,0xc6,0x5a,0xd5,0x70,
+0xbb,0x46,0x2,0xb4,0xca,0xb5,0x7a,0x8c,0xc0,0xc7,0xab,0x87,0x2d,0x69,0x2d,0x13,
+0x9f,0x47,0x62,0xb2,0xcd,0x8d,0xb1,0x1e,0x8c,0x6,0x88,0x64,0x99,0x5,0x88,0xd4,
+0x1f,0x48,0xc9,0x92,0x77,0x62,0xa2,0x57,0x38,0x3f,0xc1,0x7b,0x16,0xcc,0xfd,0x9c,
+0x43,0x41,0xc6,0x8d,0xe5,0x98,0x70,0x1,0x68,0x21,0xee,0xb7,0xf7,0x80,0x66,0xec,
+0x94,0x4f,0x46,0x80,0x92,0x80,0x1e,0x38,0x17,0x17,0xea,0x6f,0xa,0xae,0x9b,0xc9,
+0x99,0x41,0x1,0x8e,0x6f,0x52,0x3,0x8f,0x19,0x47,0xc4,0x53,0xaa,0xb8,0xe2,0xa9,
+0x81,0x40,0x76,0xa0,0xa3,0x98,0x10,0x2e,0x7f,0x58,0x73,0x69,0xb4,0xa9,0x7b,0xa8,
+0x3d,0x4f,0x95,0x93,0x37,0x94,0xe6,0x38,0x91,0x93,0x18,0x61,0x65,0x38,0xb1,0xe2,
+0xec,0x4d,0x65,0x81,0x10,0x4,0xb7,0x2d,0x3,0xe0,0x9d,0x16,0x20,0x3c,0x16,0xe1,
+0x40,0x46,0xa8,0x92,0x59,0xdf,0x8e,0xf7,0x1a,0xec,0x32,0xa9,0x1,0xd2,0x72,0x17,
+0x84,0x4b,0x34,0x8b,0xeb,0x69,0x97,0xb1,0x34,0xcd,0x2e,0xc5,0xeb,0x30,0x59,0x57,
+0xe2,0x4e,0xcb,0x97,0xf0,0x65,0xcb,0x20,0xd4,0xcb,0x0,0xd4,0x22,0x6,0x8,0xb6,
+0x98,0x44,0xf8,0x80,0x38,0x17,0xae,0x46,0xcb,0xab,0x51,0x5f,0x50,0x61,0xe0,0x7f,
+0x99,0x44,0xb0,0xb7,0x45,0xf1,0x3e,0x46,0x84,0x51,0xed,0xb8,0x1e,0xef,0x63,0x3c,
+0xa1,0x44,0xbc,0xbe,0xf4,0x24,0x88,0xad,0x4f,0xa9,0xc5,0x48,0xfc,0xdf,0xac,0x3d,
+0xe1,0x43,0x33,0x87,0xc7,0xb3,0xe7,0xcf,0xcb,0xfc,0x3c,0xa8,0x99,0x31,0xe9,0x12,
+0xa4,0x40,0x5c,0xb7,0xca,0x94,0x6f,0xbc,0x6e,0xa8,0x2b,0xc0,0x6c,0x75,0x1e,0x3f,
+0x1a,0x4f,0x72,0xa1,0xe9,0x41,0x23,0xc5,0xdb,0x6,0x68,0x43,0x12,0x2e,0x4,0x27,
+0x71,0x4c,0x6b,0x9f,0x39,0x2b,0x2e,0xb8,0xc7,0x70,0xe3,0xaf,0x6c,0x7d,0x16,0x20,
+0x2d,0x4a,0x80,0xbe,0xe0,0x8c,0xcc,0x4,0x85,0x7c,0x3,0xa4,0xea,0xa6,0x34,0xe9,
+0x95,0x43,0x3d,0xa0,0x4,0xae,0x7e,0x35,0xb9,0xb5,0xdd,0x14,0x92,0xd7,0x32,0xd9,
+0xe3,0x4e,0x3c,0x85,0x82,0x28,0x38,0x42,0x98,0x20,0x3,0xeb,0xd1,0x35,0xdd,0x58,
+0xa1,0x40,0x56,0x95,0x45,0x27,0xcf,0xdd,0x50,0xa0,0xf7,0xe6,0x2a,0x52,0xe5,0xd6,
+0xd0,0x48,0x7d,0x93,0x49,0xfb,0xae,0x67,0x7a,0xa7,0xce,0x36,0xf1,0x5,0x92,0x9a,
+0xbe,0x4d,0x2b,0x82,0x48,0xda,0x9,0x15,0x86,0x6,0x2f,0x5,0xba,0xdd,0x0,0x4d,
+0x6b,0x4c,0x70,0xae,0xe4,0xe4,0x59,0x43,0xf5,0xe4,0x3c,0x97,0x7e,0x25,0x59,0x48,
+0xe2,0x4b,0x6c,0x87,0x26,0x47,0xa,0xfc,0x26,0x29,0x4f,0x52,0x8,0x64,0x7a,0xde,
+0x45,0x47,0x65,0x97,0xb2,0x72,0xf0,0x51,0xb6,0xb0,0x58,0x4c,0xcd,0x4,0x81,0xfa,
+0x84,0x44,0x2c,0x83,0x82,0x7,0xf0,0x15,0x1c,0xc7,0xbe,0xdf,0x61,0x2a,0xba,0xbc,
+0x5a,0x49,0x5a,0xb0,0x81,0xff,0x17,0xc5,0xbe,0x9,0x1d,0x16,0x46,0x9f,0x79,0x76,
+0x9e,0x48,0x8,0x9b,0x69,0x30,0x76,0x4e,0xd5,0x9d,0xb2,0xe2,0x4d,0xfb,0x73,0x80,
+0xc9,0x45,0xbb,0x9e,0xc5,0xee,0x5,0x45,0xd5,0xe1,0x6f,0x7,0x9f,0x7f,0xa9,0xc9,
+0xca,0x44,0x6d,0xab,0x23,0x52,0x1d,0x31,0xe5,0x9d,0xee,0x65,0xb4,0x3e,0x8b,0x5,
+0x2e,0x49,0x5c,0xbc,0xa7,0x4d,0x4a,0xf2,0x88,0x3c,0x5,0xb9,0xc9,0xdc,0xc5,0x1d,
+0xff,0x41,0x2,0xa2,0x26,0x27,0xc7,0xd6,0xe9,0xe3,0xe7,0xd4,0x24,0x16,0xad,0x87,
+0x38,0x4b,0x25,0xb3,0x23,0x4f,0x46,0xc2,0x78,0xaf,0x65,0x5f,0xb5,0x8,0xca,0xf8,
+0xe5,0x4c,0xb9,0xaa,0x9,0xa5,0xc8,0x2e,0x5e,0xaf,0x2e,0xf4,0x31,0xdb,0xbf,0x5a,
+0x82,0x44,0xb7,0xb3,0xeb,0x2f,0xb9,0x2a,0x1d,0xb3,0x55,0x69,0xde,0x66,0x9d,0x4b,
+0x74,0x47,0x78,0xaa,0x58,0xd6,0x80,0x2b,0x9,0x86,0x75,0x83,0x51,0x2a,0xf3,0x7b,
+0x51,0x43,0xd3,0xb0,0xfd,0xc0,0x18,0x8b,0x65,0x77,0x8b,0xb6,0x1d,0x5d,0x21,0xc9,
+0xbd,0x4d,0xb5,0xb9,0x9b,0x2d,0x8a,0x17,0xff,0x2f,0xfc,0x9c,0x89,0x16,0x97,0xd0,
+0xb4,0x41,0x42,0xa0,0xb2,0x9c,0x18,0xcc,0xd4,0x53,0x60,0xab,0x1f,0x6f,0x6f,0x6f,
+0xd7,0x4b,0x8c,0xa2,0xda,0x9c,0xa1,0x31,0x85,0x46,0x75,0x82,0xa6,0xa0,0xa7,0x4f,
+0xf7,0x4a,0xd4,0x85,0x90,0x3e,0xf7,0x62,0x97,0x38,0xe8,0x12,0xe1,0xf3,0x22,0x96,
+0xc7,0x42,0xdd,0xa8,0x44,0x1e,0xd5,0xfa,0xbc,0x69,0x50,0x8a,0x25,0xbf,0xc7,0x2,
+0xc7,0x4b,0x94,0xb5,0x54,0xa,0xff,0x6,0x6d,0xe,0x9b,0x7e,0x9c,0xd8,0x5a,0xa9,
+0x19,0x4b,0xba,0x8b,0x16,0x92,0xc7,0x1a,0xb8,0x61,0x85,0xa2,0x11,0xb4,0xbe,0xd3,
+0x66,0x41,0xd5,0xbb,0x22,0x73,0x9a,0xa2,0x3e,0x7e,0xd4,0xb5,0x3a,0xb5,0x73,0xc1,
+0x9c,0x4a,0x36,0xa5,0x75,0xa0,0xdd,0x69,0xb6,0xd6,0x70,0xbd,0x32,0x4f,0xf9,0x94,
+0xec,0x49,0xb3,0x87,0xe8,0xd4,0x72,0x9b,0xb8,0x6e,0x74,0x88,0x19,0xdf,0x86,0xac,
+0xfa,0x41,0x88,0x95,0x7,0x8a,0x83,0x5f,0x50,0xd0,0x73,0x31,0x5c,0x10,0xf4,0x79,
+0x19,0x42,0x2e,0xa0,0x8b,0x46,0x26,0x72,0x52,0xd0,0xf4,0x40,0x1b,0xfb,0xd3,0x7,
+0x18,0x4e,0x1a,0x83,0x89,0xf5,0x6a,0xdb,0x6e,0xa4,0x62,0x98,0x63,0xeb,0x7c,0x49,
+0x45,0x40,0xe7,0x9d,0x35,0xea,0x9b,0xff,0x1d,0x7,0x5d,0xbd,0xf6,0x42,0xc7,0xa7,
+0x65,0x4f,0x5f,0x92,0x90,0x8f,0x69,0x91,0xbd,0x77,0xac,0x7a,0xbd,0xf8,0x5d,0x2a,
+0x2,0x47,0x7f,0x86,0xd6,0x90,0xe2,0x29,0xdf,0xbf,0x69,0x9b,0x21,0xee,0x63,0xeb,
+0x51,0x48,0x25,0xb6,0xd4,0xa5,0xbb,0x8c,0x7f,0x52,0x55,0x9b,0x96,0xa2,0x34,0xe5,
+0xc8,0x4d,0x18,0x85,0x90,0x6e,0xd1,0x2c,0x51,0x31,0xce,0x5b,0xfd,0x1b,0xce,0x5e,
+0x86,0x45,0x13,0xaa,0xed,0x5f,0x30,0xa3,0x24,0xe7,0x10,0xb4,0x9e,0x14,0x8b,0xd7,
+0x8b,0x4b,0x67,0x97,0x26,0x2a,0xea,0xfa,0xe1,0x86,0x8f,0x38,0x1d,0x53,0x0,0x64,
+0x25,0x4d,0xa2,0x92,0x4f,0xd3,0x6d,0x3f,0x9e,0x12,0xab,0x2f,0x24,0x72,0xb8,0xba,
+0x92,0x4c,0xd,0xb6,0xdd,0xe2,0xda,0x33,0xc7,0xf4,0x9f,0x7,0xa8,0xbe,0x54,0xe7,
+0x2d,0x49,0xc1,0x91,0x33,0xda,0x20,0xb3,0x55,0xf5,0x9,0xc2,0x13,0xc9,0xb3,0x9f,
+0x54,0x4f,0x21,0xb6,0xe3,0x38,0xcf,0x25,0xd5,0x5b,0xa5,0x22,0x2d,0x86,0x82,0x38,
+0x49,0x4b,0xdf,0xa5,0x61,0x96,0xc5,0x49,0x9e,0x60,0x43,0xf2,0xf1,0x44,0x53,0xf4,
+0xd1,0x42,0xb4,0x99,0x4b,0x8c,0x11,0x5,0xe4,0xb,0xc1,0xd6,0x2c,0x37,0xc7,0xeb,
+0xae,0x43,0x4a,0x93,0x67,0x78,0x30,0x1f,0x42,0xd,0xf8,0x6d,0xea,0x99,0x3b,0x7f,
+0x36,0x45,0xab,0xba,0xf,0x15,0x18,0xde,0xae,0x74,0xc3,0x61,0x25,0x97,0x5b,0x46,
+0x92,0x45,0x68,0x8a,0x7b,0x7c,0xce,0xfe,0x7b,0x14,0xd1,0x2b,0x21,0x98,0xb6,0x48,
+0x98,0x49,0xf6,0x88,0x3c,0x1a,0x1c,0x41,0x91,0x3a,0xea,0x0,0x1,0x54,0x37,0x3d,
+0x40,0x48,0xb3,0x8a,0x15,0x30,0x4b,0x79,0x77,0xd,0xd7,0xf5,0x9c,0x65,0xde,0x7a,
+0xf6,0x48,0x5c,0xa3,0x0,0x8d,0xd6,0xaa,0x72,0xdd,0x4d,0x7f,0xbe,0xb4,0x6a,0x2,
+0x77,0x40,0x8e,0xbd,0x2f,0x52,0x4b,0x9a,0x8b,0xb1,0xe6,0x85,0xdb,0x1c,0xb0,0xf1,
+0xf2,0x44,0xcf,0x9a,0x6c,0x94,0x5b,0xe8,0xfe,0xea,0x5e,0xbc,0x6,0xc,0x4a,0x19,
+0x10,0x42,0x60,0xa8,0x36,0x22,0x4a,0xff,0x6a,0x12,0x36,0x4,0x22,0x73,0x1f,0xa6,
+0xf0,0x4c,0xcd,0x88,0xb1,0xd6,0xbb,0x8b,0x81,0x2d,0xae,0xe4,0x26,0x22,0x85,0x3d,
+0xdd,0x41,0x76,0x99,0x2e,0x85,0x3a,0xa2,0x60,0x93,0x89,0x2,0xaa,0x91,0xea,0x10,
+0x5f,0x45,0xa2,0xa7,0xc8,0x37,0x6e,0x2b,0x55,0x15,0x4c,0x33,0x6,0x8e,0x17,0xdf,
+0xcc,0x4f,0x37,0x9c,0x7a,0xc5,0xcc,0x12,0xfb,0xc3,0x13,0x4b,0xde,0x67,0xee,0xdb,
+0xc7,0x42,0xbc,0x95,0xb7,0xb3,0xe,0x28,0x50,0xd9,0x5f,0x10,0xb4,0xd7,0xbe,0x9a,
+0x31,0x4d,0x42,0x8c,0xf1,0x7,0x53,0x2c,0x7b,0xbb,0xbe,0x3f,0xb,0x6b,0x16,0x4d,
+0xb3,0x4b,0x82,0xbc,0x77,0x51,0xd6,0x35,0x4d,0x50,0x97,0x73,0x8,0xc0,0x36,0xbd,
+0x3c,0x4f,0x79,0xb4,0x85,0x24,0x9f,0xd0,0xc9,0xdb,0xad,0xfe,0x93,0x93,0x37,0x40,
+0xf,0x4c,0xa3,0xa1,0x51,0x68,0xf7,0xa8,0xb6,0xf7,0x10,0x1d,0xad,0xeb,0xaa,0x5e,
+0x22,0x43,0xa3,0x87,0x6f,0x15,0x9c,0x99,0x2b,0xdf,0xa4,0x2f,0x3d,0xb3,0x0,0xaa,
+0x59,0x42,0x2,0xb5,0x4f,0x9c,0xf7,0x2b,0x11,0x89,0x86,0x7a,0x71,0x4c,0x29,0x21,
+0x51,0x40,0xfe,0xa1,0xd3,0xcf,0xc8,0xef,0xf1,0x17,0x91,0x9e,0x0,0xd9,0xd6,0x7d,
+0xe0,0x4a,0x28,0x95,0x0,0xe2,0xdf,0xd9,0xf5,0xa5,0x28,0x2c,0x39,0xd5,0x71,0xac,
+0x34,0x4c,0x5b,0x81,0x5f,0xfa,0xa9,0x7c,0x5c,0x5a,0x7f,0xcc,0x87,0xda,0xd6,0xe0,
+0x23,0x44,0x22,0x93,0x66,0xa5,0x58,0x97,0x8a,0xae,0xfe,0x13,0xbc,0x94,0xb2,0x47,
+0x3a,0x41,0x57,0x83,0xe8,0x89,0xd,0xb1,0x70,0x68,0x90,0xfa,0xdf,0x7f,0x7c,0x61,
+0x7d,0x4d,0x99,0x9f,0xbc,0x33,0x17,0xe9,0xea,0x1a,0x4,0xf9,0xce,0x72,0x9a,0x3b,
+0x99,0x43,0x98,0x98,0xf6,0x68,0x34,0xd4,0x96,0xe0,0x81,0x8b,0xb6,0x74,0x2e,0x6d,
+0x61,0x41,0x68,0xb7,0x2e,0x61,0xc1,0xf7,0x7b,0x80,0xf0,0xfb,0xf5,0x35,0x56,0xdf,
+0xc5,0x48,0x27,0xb3,0x77,0xb6,0xd4,0xe6,0xd8,0xd3,0x62,0xa1,0x35,0x74,0x30,0x70,
+0x5e,0x4a,0xf6,0xbb,0x16,0x6e,0x36,0x7a,0xb0,0x54,0x7,0x9,0xe4,0x26,0xe2,0x16,
+0x1,0x4e,0xd8,0xba,0x88,0xdd,0xd2,0x49,0xa3,0xa,0x24,0xcc,0xa9,0x69,0xb8,0xed,
+0xba,0x46,0xd1,0x95,0xc7,0x35,0x1e,0xe7,0x15,0x42,0x1d,0xac,0x32,0x83,0x2b,0x55,
+0xc1,0x40,0x89,0x83,0xca,0xfa,0xb,0x4e,0x9e,0x16,0x42,0x8c,0x11,0xa4,0x99,0xcb,
+0xdc,0x48,0xf2,0xaf,0x22,0xcd,0x2a,0xc9,0x79,0x10,0xfd,0x79,0xe3,0x20,0xb9,0x2a,
+0x6d,0x44,0x4e,0xb4,0xad,0x88,0xcf,0xc2,0x98,0xac,0xc6,0xde,0xf7,0x22,0x7e,0x57,
+0x8,0x4d,0x6,0xb7,0x11,0x93,0x23,0x95,0xcf,0x96,0x75,0x93,0x52,0x7,0x8f,0xa5,
+0x47,0x44,0xdd,0xb5,0x5d,0xf,0x77,0x4e,0x6e,0xab,0xd7,0x9c,0x9d,0x7c,0xea,0x89,
+0x26,0x47,0x36,0xae,0xb1,0x12,0x47,0xbc,0x1a,0x48,0xcd,0xe7,0x8f,0xba,0x1b,0xb,
+0x99,0x49,0xfe,0x80,0x4a,0xbb,0x3e,0x22,0x13,0xf9,0xe2,0x1d,0xc0,0xf8,0x63,0x60,
+0xa3,0x49,0xe7,0xa9,0x1,0x79,0x4b,0xbd,0x75,0x5d,0x96,0xd5,0x63,0xe2,0xfd,0xd5,
+0x6d,0x4f,0xf7,0xad,0x99,0x28,0xf3,0x2a,0xf1,0x63,0xf2,0xae,0x6f,0x81,0xfe,0x57,
+0x2f,0x41,0x47,0x95,0xd5,0x82,0x24,0x53,0x51,0xc3,0xe9,0x3b,0xa,0x94,0x2e,0x5b,
+0xb2,0x41,0x2a,0xbd,0xb0,0xdc,0x37,0x69,0x95,0x8b,0xf5,0x24,0xf7,0x52,0x83,0xb9,
+0xb5,0x41,0xb6,0xb5,0xbe,0x1e,0x58,0x11,0xe9,0xd3,0x5a,0x97,0xc3,0x73,0x2c,0x12,
+0x6,0x4f,0x9d,0x83,0x9f,0x86,0xd8,0xe4,0x4b,0x52,0x23,0xe2,0xea,0x20,0xb0,0xa5,
+0xb3,0x42,0x6e,0xbf,0xa3,0xf7,0x40,0xd7,0x55,0x4c,0x5e,0xb3,0xe0,0x9b,0xe0,0x98,
+0x3d,0x4e,0x4e,0xaf,0x99,0x48,0xf3,0x37,0xe,0xc,0xe9,0xc0,0xa3,0xc,0x40,0x4f,
+0xfd,0x4c,0x12,0x93,0x36,0x4,0x6e,0x77,0xe6,0xea,0x64,0x93,0x1d,0x63,0x63,0x84,
+0xd4,0x42,0xee,0xad,0xf6,0x65,0x56,0xfd,0x98,0x13,0xa8,0x4d,0x11,0x94,0xbe,0x6a,
+0x6d,0x41,0xee,0xaf,0x91,0xee,0x22,0xfe,0x6e,0xec,0x6b,0xf4,0xc5,0x75,0x9d,0x55,
+0xb7,0x4c,0x28,0xa9,0x68,0x4e,0x29,0x48,0x17,0xf3,0x88,0xb5,0xbf,0xd1,0xdc,0x19,
+0xf9,0x46,0x2d,0xb9,0x21,0xae,0xac,0x49,0xf4,0xfc,0x3b,0x9c,0xcc,0x45,0xbb,0x87,
+0x7b,0x4f,0x81,0x89,0xcf,0xfa,0x1,0x4c,0xa0,0x2b,0x29,0x4b,0xf1,0xba,0x52,0xa6,
+0x6f,0x4d,0xf4,0xb2,0xc6,0x75,0xe2,0x40,0x90,0x26,0x16,0x99,0x8f,0x24,0x8,0x1a,
+0xa5,0x47,0xfb,0x97,0x83,0x8e,0xef,0x20,0x80,0x2,0x41,0xf7,0xe7,0x94,0xcb,0x41,
+0x1f,0x4a,0xb1,0xa6,0x7f,0x18,0xff,0x99,0x49,0x88,0xfe,0x47,0x31,0x4f,0x5c,0x46,
+0xe2,0x49,0xb4,0xa3,0x50,0x36,0x46,0xf6,0xa8,0x1a,0xbc,0x6b,0xf5,0x38,0x31,0xcf,
+0x90,0x49,0x6d,0xa0,0xba,0x79,0x9e,0xb7,0x2e,0x4d,0xde,0x6f,0xe0,0xd3,0xf5,0x1d,
+0xbe,0x46,0x9,0xbf,0x44,0xb7,0x8b,0x31,0x95,0x77,0xab,0x5b,0x3a,0xd7,0x72,0x9c,
+0xa0,0x48,0xb4,0x8a,0x78,0x4c,0xcc,0xbc,0xb0,0x62,0x30,0xb1,0x6,0xd5,0x4,0xa,
+0xab,0x4f,0x22,0x9d,0x9d,0x7f,0xbc,0x6d,0x33,0x82,0xa2,0xb1,0x41,0x85,0x22,0xa8,
+0xb7,0x4a,0x8f,0x89,0x9d,0x5e,0x2a,0xc7,0x72,0x10,0xf0,0x7f,0x3a,0x84,0xeb,0x73,
+0xe7,0x4f,0xd5,0x98,0x79,0x7,0xe5,0x74,0xf7,0xe5,0x4b,0x13,0xd,0x78,0x3a,0xf0,
+0xad,0x4a,0x95,0xb4,0xea,0xf,0x86,0xb2,0xe1,0x72,0x9d,0x6f,0xff,0x85,0xd0,0x22,
+0xb5,0x45,0xc6,0xb6,0xba,0x51,0x1e,0xc8,0x58,0xd8,0xe4,0x4,0xe,0xbc,0x5e,0x46,
+0x87,0x4f,0xbb,0x9b,0xe7,0x1d,0xdb,0xf,0xc5,0x3e,0xb1,0xde,0x49,0x11,0xa2,0x26,
+0xb6,0x42,0x4,0xa8,0x53,0xc0,0xf,0xeb,0x27,0xe1,0xe4,0x3b,0x83,0x59,0xa3,0x76,
+0x6c,0x46,0x7c,0x80,0x83,0x44,0xa1,0xc5,0x72,0xd6,0x77,0xf3,0x56,0x3b,0xdf,0xbb,
+0xff,0x4f,0xc,0xb8,0xd5,0xe,0xbf,0x15,0x6,0x5,0x51,0x25,0xa0,0xdd,0xda,0xe2,
+0xbc,0x4c,0xe9,0x98,0x50,0x18,0x25,0x53,0x79,0xe2,0xe5,0x45,0x7a,0x83,0xa4,0x21,
+0x1b,0x4b,0x4f,0x8c,0x68,0x45,0x51,0x79,0xf0,0xe5,0xc8,0x31,0x83,0xff,0xd6,0x42,
+0x54,0x48,0xcc,0x9c,0x96,0x68,0x71,0x64,0x62,0x62,0x91,0x60,0xf9,0xaa,0x2c,0xcd,
+0x7c,0x47,0xfe,0xb2,0xe9,0x78,0x65,0xa5,0xb3,0x65,0x77,0x68,0x18,0xa,0x3,0x50,
+0x89,0x49,0xcf,0x92,0x9a,0xab,0x9e,0x4c,0x13,0x1e,0x41,0xd8,0x43,0x37,0xa0,0x67,
+0x3d,0x43,0x2a,0x9c,0xf0,0xf2,0xdd,0xd5,0x56,0xec,0x2a,0x2b,0x1e,0xc6,0x8e,0x97,
+0x8e,0x47,0x27,0x87,0x30,0x35,0xa3,0xbe,0xf6,0x1,0x85,0x8,0x94,0x78,0x1a,0x66,
+0x85,0x41,0x81,0x90,0xe9,0xa4,0x58,0x72,0x37,0xe4,0x8e,0xe8,0xda,0x5b,0x7b,0x62,
+0xe5,0x43,0xcd,0x9f,0x5f,0x8c,0x23,0xd8,0x73,0xb5,0x79,0xfd,0x59,0x3f,0x8,0x6c,
+0x78,0x40,0xe9,0x87,0x82,0x43,0xb8,0xa0,0xad,0xb3,0xbe,0x68,0x65,0x99,0x50,0xd7,
+0x88,0x4c,0x11,0x8b,0x5f,0x37,0x72,0x6f,0x60,0xa9,0x56,0x34,0x43,0x80,0x36,0x50,
+0x68,0x49,0x91,0x97,0x8b,0x1b,0xb6,0x73,0xb6,0x4f,0xb7,0x4,0xca,0x8c,0x2d,0x99,
+0x6e,0x4e,0x20,0xad,0xdf,0xf1,0x2a,0xf6,0x47,0x19,0x4b,0xe9,0xf1,0x3f,0x95,0xe0,
+0xf4,0x4b,0xf0,0x9d,0x31,0xf5,0xa3,0x9f,0xc5,0x27,0xe,0x2d,0xf0,0x5e,0xb,0xd0,
+0xfe,0x42,0x7e,0xb7,0xc6,0x49,0xd0,0x76,0x6a,0x9b,0x1e,0x82,0x9,0x8f,0x3d,0xfe,
+0x1b,0x4d,0x8b,0xa1,0x82,0x1a,0x86,0x6e,0x9e,0x13,0x95,0xad,0x86,0x21,0xa9,0xa8,
+0xde,0x41,0xdf,0x82,0x7,0x9f,0xcd,0x5d,0x1f,0x12,0x55,0xf,0xd,0x2b,0xec,0xbe,
+0x7c,0x47,0xe1,0xaa,0x33,0xe7,0x5d,0xe0,0x33,0x95,0x34,0x69,0x42,0xfd,0x1e,0x7b,
+0xec,0x40,0x3b,0x84,0x90,0xe3,0x92,0x46,0xc,0xfc,0xbe,0x22,0xd2,0xbe,0x2c,0xcd,
+0xcb,0x4f,0xa9,0xae,0x6b,0x4d,0x90,0x7b,0x8e,0xc6,0x75,0x96,0x14,0x8c,0x7c,0x38,
+0x44,0x46,0xe4,0x8b,0xdb,0x60,0xdd,0xfe,0xb5,0x61,0x2,0xee,0xa9,0x80,0x30,0x2f,
+0xe4,0x48,0xbd,0x85,0x2f,0xd1,0xea,0x4d,0xaf,0xac,0xa1,0x36,0x8e,0x36,0x8d,0x23,
+0xb8,0x4e,0x4e,0x89,0x4d,0x85,0x53,0x4f,0x67,0xad,0xa7,0x80,0x4b,0xc3,0x4c,0x56,
+0x81,0x4d,0xca,0xa1,0x48,0xff,0xfe,0xa7,0xfa,0x4,0x55,0x25,0x6a,0xfb,0xf9,0x17,
+0x8f,0x42,0x32,0xa4,0x47,0x5d,0xc,0x6d,0x5c,0x9a,0x63,0x26,0xff,0x4e,0x7c,0xab,
+0x97,0x43,0xf1,0xb4,0x56,0x5e,0x66,0xed,0x43,0xf,0xd5,0x34,0x9b,0x71,0x47,0x7a,
+0x9d,0x4b,0x33,0x81,0x4,0xd5,0x8e,0x46,0x2b,0xf5,0xec,0xae,0xf,0xfa,0xe6,0x3d,
+0x7c,0x4d,0x56,0x93,0x63,0x3f,0x3a,0x13,0x5c,0x46,0x60,0xf7,0x80,0xa4,0x62,0x90,
+0x86,0x44,0xfe,0x84,0x30,0x5,0x38,0x87,0xda,0x7a,0x40,0x4b,0x25,0xe0,0x35,0x34,
+0xe1,0x4e,0x38,0x8c,0xd2,0x6d,0x52,0x46,0x4c,0x21,0xee,0x9f,0xcb,0x5b,0xde,0x14,
+0x13,0x4b,0x61,0x83,0xf0,0x8b,0x18,0xbd,0x47,0x9,0x31,0x8b,0x16,0xd9,0xe9,0xc2,
+0x3c,0x44,0x1f,0xbf,0xea,0x7d,0x62,0x61,0x3a,0x63,0x1e,0x8c,0x95,0x7f,0x5c,0xf0,
+0xc0,0x44,0xbf,0xb0,0xd9,0xd3,0x27,0x5,0x8e,0x86,0x18,0x94,0x4a,0x80,0xa0,0x5a,
+0x6,0x4c,0x5b,0xb7,0x5a,0x6a,0xe1,0xe9,0x19,0x69,0xd4,0xa0,0x93,0x56,0xd4,0xf6,
+0xbd,0x42,0x42,0x97,0xd0,0x58,0xd6,0xda,0x45,0x37,0xb2,0x2,0xfc,0x9f,0x4b,0xea,
+0x6,0x47,0xe9,0x85,0xf,0x25,0x90,0x77,0xa0,0x37,0x28,0xff,0x15,0xf3,0x73,0xe6,
+0x89,0x49,0x42,0xac,0xbf,0xcf,0x3f,0x68,0x49,0x8e,0x4c,0x53,0xa6,0x70,0xe1,0x4,
+0xeb,0x42,0x95,0x8f,0xb1,0x79,0x56,0x75,0x80,0xb9,0x30,0x9e,0x26,0xf7,0x77,0xb7,
+0xd8,0x4b,0x98,0x86,0xa4,0x81,0xee,0x1b,0xf0,0xf7,0x65,0xed,0x8f,0xe8,0xc8,0x7f,
+0x77,0x41,0x4f,0x93,0x42,0xe0,0x45,0xa3,0x75,0x4e,0xbe,0x92,0x13,0x18,0x33,0x42,
+0x8e,0x40,0xaa,0xbb,0xc7,0xb1,0xd5,0x85,0x49,0x4d,0x65,0x1,0x25,0x8d,0xc2,0xc4,
+0x4,0x42,0xdd,0x92,0x94,0x26,0x43,0x15,0xfc,0x88,0xad,0xbc,0x5f,0xec,0x65,0x3b,
+0x8f,0x44,0x27,0x95,0xce,0xd0,0xa8,0x4a,0xb7,0x57,0x25,0xa,0x4c,0x6,0xc1,0xc3,
+0xa2,0x43,0x91,0x94,0x5d,0x5f,0x76,0x54,0xd8,0x9c,0x42,0xe8,0x22,0x38,0xcc,0xf9,
+0x80,0x46,0x29,0xab,0x21,0x82,0x2a,0x11,0xa4,0x78,0xb5,0xcd,0xd8,0xbe,0xf0,0x2c,
+0x85,0x4b,0x57,0x8e,0x80,0x23,0x63,0x68,0x9f,0x85,0x53,0xb8,0x9,0x6b,0x32,0x25,
+0xfd,0x4a,0x1b,0x8e,0x24,0xee,0x7,0xdf,0xae,0xc,0x5e,0x9e,0x99,0x72,0x40,0x6d,
+0xc2,0x4d,0x85,0xb6,0xbf,0x9,0x50,0x50,0x5,0x84,0x57,0x3e,0xc6,0xd6,0x12,0x65,
+0xbe,0x4a,0xf8,0xb7,0x9,0xf8,0xde,0xa5,0x7b,0x85,0x28,0x50,0xf,0xc1,0x1e,0x10,
+0x76,0x4c,0x1f,0xbf,0xae,0x83,0x6d,0xc0,0xb2,0xf0,0xe5,0xae,0xae,0x17,0xbc,0x66,
+0x42,0x4a,0xc2,0x90,0x92,0x4e,0x35,0xa0,0xef,0x75,0x7,0xaf,0xc6,0x13,0xfb,0x88,
+0x19,0x4c,0x8e,0xa5,0x7c,0x3d,0x84,0xd3,0xd7,0xbe,0x3d,0xda,0xc1,0xb8,0x52,0xda,
+0xbf,0x42,0xd1,0xab,0x78,0xda,0xde,0x83,0xce,0xca,0xa1,0x13,0x10,0x15,0x51,0x76,
+0xdb,0x46,0x29,0x8d,0xbc,0xaf,0x8a,0x2d,0x1b,0x17,0x17,0x45,0x7c,0x6,0x38,0x4c,
+0x7a,0x4d,0xcd,0x8c,0x2,0x42,0xde,0xa6,0x76,0xe7,0xac,0x4b,0xb2,0x98,0x12,0x8f,
+0x2f,0x47,0xd7,0x95,0xfe,0xd,0xc2,0x5b,0x4,0x70,0x89,0xb5,0xfc,0xe0,0x48,0x85,
+0x32,0x45,0x3,0xba,0x4f,0x2,0x46,0xcd,0xf7,0x55,0x66,0xaf,0x7c,0x97,0x59,0x25,
+0x27,0x41,0x9c,0xad,0xd0,0xfc,0x8e,0xa6,0xf2,0x53,0xe6,0x94,0x7a,0x22,0x3a,0xf,
+0xca,0x4e,0x42,0x94,0x3b,0x42,0xbe,0x5f,0x28,0x5c,0x46,0xd6,0x4c,0x18,0x92,0x7b,
+0xc9,0x43,0x99,0xa0,0x67,0x45,0x5b,0x28,0xd,0xf7,0xff,0x48,0xe4,0x8d,0x55,0x60,
+0xde,0x47,0x1e,0xb6,0x27,0x9f,0x17,0x60,0x6c,0xf1,0x34,0x1e,0xd6,0xbf,0x47,0xfa,
+0x13,0x43,0x87,0xa6,0xae,0x30,0xe0,0x51,0x54,0x93,0xff,0x5f,0x21,0x7f,0x6a,0x5e,
+0xa7,0x49,0xc4,0xad,0xf0,0xca,0xa2,0xdd,0x71,0x9b,0xe1,0xcc,0x6c,0xa0,0xaf,0xe3,
+0xb2,0x41,0xe4,0xab,0x10,0x75,0x70,0xed,0x97,0x2f,0x97,0x21,0xb4,0x63,0x56,0xf8,
+0xc0,0x49,0x24,0x80,0x53,0x49,0x27,0x73,0xc6,0x84,0x6c,0x58,0xc5,0xdc,0xe6,0x3d,
+0x6b,0x42,0xa8,0xad,0x6b,0x3b,0x70,0x6a,0x69,0x43,0x8,0xf5,0x36,0xa8,0x91,0x9,
+0xbe,0x4b,0x15,0x97,0x6f,0x7,0xb9,0x72,0x98,0x27,0x8c,0x3,0xf6,0x3a,0xc1,0xb0,
+0x5d,0x4b,0x1a,0x9e,0x4e,0xbe,0x96,0x12,0x3,0x64,0x50,0xcc,0x4e,0xe9,0x90,0xdb,
+0xa3,0x45,0xf5,0x9d,0xef,0xf1,0x2d,0xf6,0xef,0x3,0xf4,0x69,0x49,0x52,0x10,0x7c,
+0xc4,0x40,0x7a,0x9f,0x1f,0x9e,0xa3,0x28,0x9e,0xf6,0xda,0x61,0x53,0x7,0x43,0xa2,
+0x7c,0x41,0xf,0x92,0xee,0x49,0xdd,0x4b,0xca,0x52,0xf4,0x58,0x89,0xa4,0x69,0xc5,
+0x84,0x4f,0x4,0xba,0x5,0x1c,0x1e,0xeb,0xbe,0xb,0x64,0xe1,0x92,0xa0,0xde,0x58,
+0x90,0x4c,0xda,0x84,0x11,0xa5,0x51,0xa,0x25,0x35,0x26,0x66,0x94,0xc8,0x2,0x76,
+0xa1,0x4a,0xf6,0x92,0xc6,0x51,0x86,0x8,0xf5,0x67,0xe5,0x4b,0x3a,0x42,0x8a,0x7f,
+0xed,0x42,0x65,0xba,0xf3,0xb2,0xc,0x9c,0xf6,0x49,0x3f,0xaa,0x96,0xd5,0x61,0xb,
+0x7e,0x4c,0x12,0xb1,0xcb,0x9a,0xe8,0xb6,0x27,0xb6,0x1c,0x50,0x91,0xc4,0x5b,0x98,
+0x4e,0x49,0x67,0x94,0x29,0x12,0x21,0x7c,0x17,0x50,0x20,0xd,0x7c,0xd7,0xbc,0xd5,
+0x2c,0x4d,0x7d,0x95,0x3d,0xda,0xec,0x7c,0xaf,0xfa,0x3,0x57,0x69,0x40,0xb6,0x99,
+0x80,0x45,0x58,0x98,0xa7,0xc9,0xb2,0x44,0xcb,0xd6,0xa1,0xf1,0xb5,0xf,0xb3,0xcb,
+0xa,0x42,0xaa,0xaf,0x97,0x7d,0x53,0xf8,0x3e,0xe1,0x69,0xc0,0x3,0x26,0x9e,0x46,
+0x52,0x4d,0xc,0x81,0x6d,0xcb,0x4a,0xe9,0x1e,0xec,0xdc,0x3b,0x5e,0x3b,0x6e,0x86,
+0x1c,0x46,0x7,0x8d,0x42,0x21,0xf7,0xa,0x41,0x7e,0x8b,0xf8,0xf,0x28,0x60,0x60,
+0x39,0x42,0x6d,0x8d,0xd0,0x83,0x24,0x10,0x2d,0x8b,0xd7,0x2f,0x27,0xd9,0x56,0xc8,
+0xad,0x48,0xfb,0xb4,0xdf,0x6a,0x21,0x34,0xd8,0x43,0xf2,0x25,0xef,0xea,0x9c,0x62,
+0x6c,0x43,0x56,0xbe,0x9b,0xea,0x53,0xca,0x5d,0x27,0x40,0xda,0xcf,0xca,0x12,0x9c,
+0xe6,0x4e,0x27,0xb0,0xa8,0x73,0x83,0x6e,0x5f,0x66,0x98,0x1e,0xa2,0x10,0x9f,0xaa,
+0xc2,0x41,0x27,0x9d,0xf2,0x79,0x14,0x96,0x4d,0x5d,0xc1,0x85,0x7c,0x2f,0x6b,0x2c,
+0xf2,0x4b,0xb8,0x9f,0x19,0x4c,0xc,0xcc,0x6e,0x5a,0xbb,0xd3,0x26,0xd,0xa5,0xc9,
+0x63,0x4f,0xda,0x91,0xa8,0xf3,0xc0,0x23,0x32,0x89,0x8e,0xb0,0xc3,0xa0,0xda,0xd2,
+0x4c,0x48,0x70,0x81,0xf2,0x7e,0xbf,0xaa,0x52,0xe4,0x16,0x86,0x88,0xce,0x6e,0x94,
+0xf9,0x4e,0xf4,0x87,0xdf,0x8b,0xb,0x24,0x1e,0xaa,0xd0,0xbe,0x33,0x34,0x1b,0x81,
+0xfd,0x4b,0xf4,0x8c,0x52,0xd,0x56,0x4b,0x89,0xce,0xd3,0x1c,0xe4,0xa,0x5f,0xf3,
+0x9b,0x4b,0x8d,0x88,0xf4,0x95,0xd2,0x69,0x4d,0x29,0x93,0x6e,0x7e,0xe5,0x91,0xd3,
+0xb5,0x48,0x15,0x87,0x64,0xa8,0x7f,0x4d,0xa,0x19,0x33,0xb2,0xc1,0x7d,0x8b,0xb7,
+0x42,0x4d,0xe5,0xaa,0x5a,0x84,0x4d,0xb1,0xfe,0x85,0xb5,0x83,0xe5,0x3a,0x73,0x6,
+0x91,0x4f,0x0,0x81,0x59,0xff,0x3a,0x8,0xb4,0x6b,0xd1,0x9b,0x98,0x50,0xb6,0xd2,
+0x14,0x4f,0xc6,0xa8,0xdf,0x9f,0xd5,0xd2,0x9f,0x18,0xee,0x7c,0x81,0xdf,0x1d,0x33,
+0x54,0x45,0x40,0xad,0x3e,0xa6,0x22,0xf2,0xa7,0x67,0x68,0xaa,0x33,0x3b,0xdb,0xb7,
+0xe5,0x49,0x5b,0xad,0xb0,0xbd,0xce,0x95,0xa4,0xfa,0x3f,0xd1,0xaa,0x4b,0x4a,0x2b,
+0x91,0x42,0x54,0xbc,0x1b,0x60,0x8f,0x46,0x7e,0x26,0x74,0x8a,0xa,0x39,0xe6,0x57,
+0xc5,0x4d,0xfa,0xad,0x3f,0x10,0xbb,0x18,0xb5,0x71,0x5f,0x3e,0x6b,0x3b,0x41,0x20,
+0x99,0x4a,0xdd,0xa8,0x4a,0xf9,0xce,0x6,0x6c,0x86,0xb3,0x1e,0x88,0x7c,0x34,0x54,
+0xc7,0x43,0xdc,0xac,0xc7,0xa7,0xc9,0xb1,0x73,0x10,0x19,0x68,0x22,0xc,0x1d,0x62,
+0x29,0x41,0xc7,0xa0,0x4a,0x6d,0x1c,0x12,0xba,0x33,0x41,0xde,0x14,0x0,0xe8,0x58,
+0x9b,0x4d,0xff,0xa1,0xcd,0x14,0x88,0xcf,0xf7,0xeb,0xa4,0x21,0x1,0xa0,0x87,0x62,
+0xb5,0x43,0xf5,0xad,0x12,0x92,0x3b,0xf2,0x54,0x94,0x31,0x5d,0x6d,0xe7,0xa4,0x4e,
+0x13,0x44,0x1c,0xbd,0x15,0x6c,0x94,0xf3,0xf8,0x5d,0x2f,0xf0,0xc9,0xa9,0x96,0x66,
+0x60,0x40,0x34,0x9d,0xa3,0x52,0x22,0x25,0xca,0x93,0xb9,0xc1,0x84,0x80,0xe9,0x22,
+0xeb,0x4b,0xc8,0x8c,0x53,0x12,0xc5,0x43,0xbc,0x7d,0x33,0xb5,0x87,0x61,0x8b,0x9d,
+0x85,0x4c,0xc2,0x95,0xd9,0x85,0xbe,0x8,0x5d,0x13,0xf2,0xf7,0x27,0xdb,0xfd,0xd9,
+0x6d,0x4e,0xbb,0xa8,0xa5,0x66,0xc7,0xf8,0x61,0x20,0xa8,0xf0,0xad,0x7,0x61,0x75,
+0x19,0x41,0xd9,0xb1,0xe2,0x26,0xb,0x8a,0x25,0xc5,0xaf,0xf0,0x5e,0xce,0xcf,0xc0,
+0x73,0x44,0xe8,0x8c,0x8c,0xa1,0x77,0x4a,0xfe,0x68,0x68,0xf8,0xc6,0x57,0x1c,0xb4,
+0x4c,0x4e,0x3,0x85,0x81,0x4a,0xee,0x1e,0x1c,0x74,0xf4,0x4f,0x6b,0xa8,0xa0,0x67,
+0x6,0x49,0xfa,0xbc,0xb4,0x72,0xdb,0xbb,0x30,0x37,0x12,0x21,0xb,0x71,0x4a,0xdf,
+0x7e,0x46,0x91,0xb8,0x91,0xa3,0x10,0x8f,0x94,0x7,0xdd,0xb7,0x33,0x7a,0x78,0xef,
+0xcf,0x4d,0x39,0xbb,0xc6,0xec,0x45,0xb8,0x95,0x5f,0xc3,0x80,0x4b,0xa4,0x69,0x30,
+0x6e,0x41,0x8f,0xa7,0x71,0xf6,0xcd,0xb6,0x7,0x0,0x89,0xa1,0xda,0xbe,0x7b,0xe5,
+0x5e,0x4d,0x7f,0xa5,0x79,0x8e,0x71,0x8a,0xbf,0xc2,0x73,0x75,0xb,0x62,0x8f,0x19,
+0xba,0x48,0x9c,0x93,0xfe,0x3b,0xc2,0xbc,0x1f,0xd2,0xb,0xdf,0x86,0xcd,0x6f,0x7c,
+0xeb,0x4a,0x6c,0x93,0x6f,0x96,0x97,0x53,0xff,0x50,0xe7,0xe2,0x4c,0xe4,0xe1,0x6d,
+0x23,0x40,0x5a,0x93,0x6d,0xae,0x6b,0x5b,0x4e,0x20,0xc4,0xfc,0xea,0x5f,0x1d,0x70,
+0x3b,0x4a,0x44,0xb2,0x7b,0xd8,0xc2,0x32,0x76,0x51,0xd0,0xdb,0x1c,0xbb,0x1,0xec,
+0x7e,0x44,0xe7,0xa3,0x5,0xd9,0x82,0xa8,0xfb,0x94,0x30,0xe5,0x7d,0xea,0x3b,0x5a,
+0xec,0x44,0x5e,0x99,0x9a,0x5c,0xa1,0xca,0x60,0xdb,0x49,0x59,0x2d,0x33,0xc6,0x3,
+0x75,0x4b,0xb,0x9f,0xd,0xb5,0x39,0xf7,0xde,0x5d,0x7b,0xef,0x78,0x53,0x0,0x75,
+0xa8,0x4c,0xe6,0x88,0x7e,0x32,0x9a,0x97,0x98,0xc0,0x79,0xfd,0x87,0xf4,0xb6,0x6,
+0xf7,0x46,0xc1,0x9d,0xc3,0x41,0x99,0x50,0xb0,0x2e,0x74,0x14,0x7c,0x5f,0x27,0x1e,
+0xba,0x4d,0xe0,0x8d,0xbe,0x17,0xd3,0x2f,0x5e,0x2b,0x93,0x85,0x97,0x42,0x5e,0x6d,
+0x33,0x49,0x4d,0x9f,0x88,0xa1,0x7,0x87,0x9b,0x52,0x96,0xe,0xa,0xa0,0xcb,0x4,
+0x2e,0x42,0x36,0xa4,0xd8,0x9,0x9a,0x10,0xdc,0x2b,0xef,0x8d,0x2e,0xd0,0x23,0x30,
+0x7a,0x45,0xab,0xb8,0x70,0x32,0x68,0x6,0x3d,0xa9,0x9a,0xfd,0x8d,0xd,0x68,0x59,
+0x33,0x4b,0xa4,0x8e,0x6b,0x50,0xfa,0x6b,0x56,0x2c,0x2b,0x50,0x7f,0x93,0x5,0xaf,
+0x48,0x41,0x6f,0xaa,0x44,0xb,0x60,0x5b,0x35,0xad,0xe2,0x89,0xaa,0xe6,0x33,0x9,
+0x89,0x40,0xf4,0x89,0xf,0xe3,0x9b,0x4a,0xbe,0x82,0x7c,0xa0,0x8c,0x3b,0xde,0x6d,
+0xca,0x42,0x5a,0x80,0x43,0xbc,0x4,0x82,0xd7,0x51,0x98,0x92,0x8f,0x19,0x13,0x96,
+0xd2,0x4e,0x15,0x99,0xee,0xa1,0xc5,0x1b,0xa7,0x86,0xef,0x81,0x9e,0x6a,0xf8,0x65,
+0x1d,0x49,0xb0,0x86,0x2,0x59,0xf8,0xb8,0xaa,0x36,0xea,0x6f,0x8c,0x25,0xe,0x48,
+0xed,0x4f,0x5b,0xa4,0x1b,0x5d,0x40,0x49,0x80,0x9c,0xc9,0x12,0xad,0x50,0x57,0x67,
+0x1f,0x40,0xc6,0xb5,0xdc,0x28,0xfd,0x9e,0x73,0xee,0xd3,0x5e,0xd2,0x1a,0x8e,0x9c,
+0x22,0x49,0x2e,0xbc,0x2b,0xbf,0xfc,0x54,0xfb,0xa0,0x7b,0x74,0xb6,0x8d,0x1b,0x16,
+0x8a,0x4e,0x1f,0x96,0x7c,0x2f,0x38,0x4f,0xf6,0xb7,0x32,0xb,0x76,0x8c,0xe2,0x5,
+0xc3,0x41,0x33,0x91,0x8b,0xff,0x37,0x37,0xe3,0x32,0x87,0x8f,0x72,0x80,0x24,0x69,
+0x2c,0x42,0x1e,0x8c,0x90,0xef,0xde,0x84,0x68,0xe5,0x95,0xd6,0xfc,0x7f,0x55,0xd7,
+0x46,0x44,0x97,0x91,0x81,0x46,0x18,0x12,0x9a,0x8e,0x42,0xa4,0x91,0xfb,0xef,0xa2,
+0x86,0x49,0x7e,0xaa,0x95,0xc8,0xf9,0x87,0x82,0x36,0x4,0xa0,0xd2,0x39,0x99,0xe,
+0x4e,0x42,0xf6,0xad,0x60,0xff,0x92,0x14,0x6a,0x8c,0x3b,0x26,0x9,0xf,0x8a,0xc6,
+0xa,0x42,0xfe,0xa9,0x4a,0x73,0x98,0xa3,0x83,0x51,0xe4,0xde,0xab,0xba,0x24,0x3e,
+0x94,0x48,0x32,0x9f,0x81,0xc5,0xdd,0x3f,0xa0,0xa4,0x26,0x55,0xf9,0x69,0x28,0xd8,
+0xc,0x4b,0xd2,0xb3,0x14,0x48,0x62,0x6d,0xf,0x1d,0x1c,0xf5,0x24,0x6e,0xb0,0x4b,
+0xe3,0x48,0xd4,0x99,0x6,0xae,0x8f,0x7e,0x12,0x3d,0x10,0x5c,0x5f,0xb8,0x79,0x9a,
+0xf,0x43,0xcc,0x98,0x4c,0xcd,0x3f,0xec,0x8a,0x24,0x7b,0xad,0x5e,0x71,0xdf,0x5f,
+0x80,0x4f,0x1,0xaf,0x89,0xbe,0xf5,0xe0,0xb0,0xd8,0x78,0xb5,0x51,0x66,0xd1,0x21,
+0x9a,0x40,0xba,0x96,0x8f,0x3e,0xa,0x11,0x35,0xd6,0xe2,0x69,0x78,0x86,0xf6,0xbb,
+0xcb,0x42,0x93,0xb6,0xa,0x98,0xbe,0x46,0xdd,0xe9,0xab,0x1b,0x59,0x53,0x15,0x5b,
+0xc0,0x44,0xc6,0xad,0x7e,0x7d,0x56,0xf,0x97,0x8d,0x40,0x8a,0xf3,0xb3,0x28,0xa3,
+0x3e,0x4f,0x75,0xae,0x32,0x28,0x4a,0xc5,0xa4,0xb4,0xa8,0xc,0xf7,0x3b,0xf1,0x83,
+0xc0,0x42,0x8d,0xa9,0xd,0x45,0x25,0xd3,0x12,0x71,0xc9,0x1c,0x44,0x1f,0x1b,0x55,
+0xd8,0x4c,0xbe,0x98,0x20,0xa,0xa6,0xf2,0xef,0x79,0x48,0x10,0xf4,0x4b,0xc9,0xbc,
+0x28,0x41,0x88,0x9c,0xb6,0xfc,0xeb,0x1a,0x93,0xc2,0xe4,0xfc,0x89,0xfa,0x42,0xb1,
+0xc1,0x47,0x13,0x92,0x47,0x77,0x91,0x9,0xe4,0x1a,0x2d,0x53,0x17,0x48,0x47,0xb6,
+0x91,0x46,0xcf,0x8f,0x3e,0x5a,0x38,0x5f,0x12,0xda,0x89,0x9a,0x49,0xc6,0xea,0x16,
+0xc4,0x4b,0x2f,0xb9,0x37,0xbf,0x9d,0x44,0x63,0xda,0x85,0xf,0xc5,0xe9,0xc4,0x8a,
+0x59,0x49,0x75,0xa5,0xa7,0xf,0x14,0x99,0x3e,0xf,0x91,0xc0,0xa1,0xdd,0x23,0x8d,
+0x52,0x4e,0xdf,0x91,0x72,0xce,0xc6,0xea,0x19,0xbc,0x8a,0x13,0x20,0x54,0x2,0x33,
+0x97,0x40,0x93,0xa5,0x24,0xa2,0xd2,0x42,0xe4,0x35,0xb1,0xd0,0xd,0x4f,0xa7,0x66,
+0xef,0x47,0x71,0x84,0xd5,0x8,0x2,0xaa,0x7f,0xe5,0xa5,0x58,0x4a,0x52,0x40,0x5a,
+0xa,0x4f,0x2e,0xb0,0x2b,0x96,0x6e,0xf2,0x34,0x2f,0xd5,0x7,0x23,0xb2,0xe4,0xdd,
+0x9c,0x4e,0x4f,0x81,0x49,0xec,0x99,0xa5,0xd8,0x22,0x3a,0x79,0xd1,0x5e,0x9a,0x5e,
+0x50,0x45,0xd3,0xb7,0x86,0xa7,0xa8,0x5f,0xbf,0x4b,0x7b,0x53,0xcb,0x16,0x67,0xb4,
+0x25,0x4b,0xdc,0x8d,0xe,0x54,0xb1,0xb4,0x19,0x1a,0x5d,0xe,0x94,0x29,0x8f,0xe6,
+0x30,0x48,0xb7,0xa8,0xce,0x81,0x79,0xcc,0xb8,0xa9,0x45,0x33,0x5e,0xd3,0xe2,0xb6,
+0xb7,0x45,0x11,0xa1,0x28,0x11,0x8a,0x2f,0x67,0x58,0x11,0x3f,0x61,0x62,0x34,0x23,
+0xcc,0x4e,0x3b,0x8f,0xca,0xcd,0x97,0x1f,0x90,0x8d,0x55,0x7,0x95,0x1c,0x6,0xb5,
+0x78,0x4d,0x14,0x8c,0xa4,0xd2,0x10,0xd6,0x56,0x9b,0xcf,0xf9,0xb8,0x53,0xd9,0x1b,
+0xc7,0x49,0x44,0x99,0x1f,0x90,0x7a,0xf6,0x8b,0x4e,0x45,0x55,0x4b,0x76,0x22,0x30,
+0xe3,0x42,0x37,0xb8,0x98,0xf3,0x19,0x21,0x26,0x67,0xbd,0x5f,0xfb,0xa8,0xcf,0xc0,
+0x90,0x4f,0xfe,0x98,0x9c,0xe2,0x4b,0xf3,0xf9,0x25,0x65,0xfa,0x9b,0x2,0xe1,0x84,
+0x14,0x4b,0x82,0x8a,0x4,0x87,0xcb,0x22,0xb3,0x5e,0xb6,0xbb,0x9b,0xb5,0xcc,0x77,
+0xc2,0x48,0x33,0xb2,0x53,0xee,0x68,0x8,0x73,0x20,0x33,0x52,0xb4,0xd5,0xfa,0xfb,
+0x4b,0x46,0xcb,0xab,0x21,0x5,0xf6,0xff,0xf1,0x3a,0xc4,0x77,0x82,0x54,0xaf,0xe8,
+0x96,0x44,0x5d,0xa4,0xfd,0x44,0x65,0x61,0x86,0x8a,0x16,0xf8,0xb3,0x4e,0x8e,0xaf,
+0xeb,0x4f,0xea,0xba,0xac,0x58,0xe3,0x9e,0x3d,0x60,0xa4,0xe1,0x2d,0xc7,0x85,0xc9,
+0x5c,0x42,0x3a,0x90,0x2c,0xfa,0xce,0x43,0xa,0xd1,0xf5,0xba,0x38,0x51,0x71,0xb3,
+0xd1,0x4f,0x81,0x9d,0xbe,0x5,0x57,0x5f,0x6c,0xd,0x92,0xf4,0xa4,0x9b,0xcc,0xa4,
+0x23,0x48,0x7f,0xbb,0x59,0x45,0xda,0xf1,0x78,0x5e,0xb2,0x24,0xa,0xc7,0xd1,0xf2,
+0x41,0x48,0xb6,0x8d,0xe0,0x5d,0x15,0x18,0x67,0x46,0xaf,0x11,0xb5,0xa7,0x5b,0x45,
+0xed,0x43,0xa2,0x90,0x97,0x29,0xdd,0x60,0x49,0x55,0xf8,0xe,0xb3,0x9f,0xef,0xad,
+0xdf,0x42,0xee,0x8e,0x15,0x43,0x16,0x19,0xa8,0x19,0x5,0xc3,0x5d,0x87,0x30,0x13,
+0xc3,0x40,0x85,0x95,0x79,0x92,0x38,0xf1,0xe8,0x3c,0xeb,0xf1,0x5f,0xf9,0x79,0x40,
+0xa1,0x4e,0x2,0xb8,0x81,0xd,0x6a,0xc5,0x24,0x6d,0x33,0x40,0x62,0xab,0xb1,0xcf,
+0x83,0x42,0x46,0x8d,0x4a,0x86,0x19,0x35,0xbb,0x7f,0x57,0xed,0x37,0x33,0xb1,0xa3,
+0xac,0x4e,0xc9,0x87,0x71,0xcb,0x4,0xb7,0x60,0x0,0x25,0x6a,0x30,0xa6,0xde,0x2f,
+0x50,0x4e,0x34,0x8e,0x80,0x9,0x7a,0x2b,0xcc,0xf6,0x2b,0xc4,0x8f,0x73,0x9f,0x13,
+0xe,0x42,0xf4,0x98,0x97,0x42,0x41,0xcd,0x5c,0x53,0x10,0x5f,0x98,0xc3,0xbf,0xb2,
+0x3,0x45,0xd4,0x91,0x48,0x49,0x45,0xee,0x91,0xa6,0x83,0x87,0x48,0xa4,0x28,0x4e,
+0x4b,0x4b,0x81,0xa0,0x9b,0x8c,0x42,0x64,0xb6,0x4d,0x78,0xff,0xf8,0x79,0x49,0xff,
+0xaa,0x4d,0xf9,0x98,0x1d,0x8f,0xea,0xa2,0xe6,0x4e,0x14,0xc1,0x8,0xf5,0x45,0x9a,
+0xc5,0x4b,0xf8,0x8c,0x9b,0x36,0xfb,0x73,0x3a,0xab,0xa6,0x19,0x2c,0x6f,0xd8,0x71,
+0x75,0x41,0xa7,0xad,0xf4,0xa5,0xcc,0x63,0x3e,0x11,0x30,0x8a,0xbf,0xfa,0x56,0x79,
+0x73,0x43,0xbc,0x9f,0xc4,0x12,0x55,0xfb,0xe0,0x4a,0x49,0xfe,0x87,0x94,0x66,0xbf,
+0xcc,0x4b,0x48,0xb2,0x1e,0x51,0xa2,0xbb,0xd5,0xb2,0x44,0x55,0xa0,0x51,0x73,0xd4,
+0x30,0x42,0xd0,0x97,0xf0,0xe5,0xec,0x2a,0x4,0x68,0x3a,0xcc,0x14,0xa4,0x34,0xee,
+0x1c,0x41,0xda,0x91,0x0,0xf9,0x20,0x56,0x7c,0xc2,0xe5,0xb8,0xe6,0xed,0xf6,0x9a,
+0x7f,0x43,0x6c,0x87,0x56,0x9b,0x8d,0xe3,0x6e,0x2c,0xfa,0x30,0x41,0x3c,0x83,0x2,
+0x47,0x49,0x97,0x8c,0xc7,0x78,0xf6,0xda,0xdc,0x7c,0x10,0xf4,0xde,0xa7,0xac,0x1,
+0x1e,0x49,0x53,0xb5,0xa,0xce,0xba,0x8b,0x9e,0x37,0x5b,0xc7,0x2f,0xb5,0xb2,0x1,
+0x1d,0x4f,0x18,0xb2,0x7e,0xa1,0xdb,0xe2,0xd2,0xb,0x44,0xb8,0x7d,0x7c,0xde,0xf8,
+0xa8,0x46,0x90,0x8a,0x64,0xf4,0x3a,0x30,0xe1,0x77,0xeb,0xa3,0xad,0xa5,0x4,0xbe,
+0xe9,0x49,0xa7,0x86,0xa8,0x84,0xdf,0xaa,0x87,0x81,0xea,0xaf,0xd6,0x82,0xb9,0x43,
+0x6f,0x40,0xe2,0x9e,0x34,0x6e,0xec,0x72,0x3b,0x96,0x3,0x7f,0x18,0xf7,0x3e,0xde,
+0xad,0x44,0x9d,0x87,0xb2,0x25,0x32,0x15,0x74,0xce,0x4b,0xd1,0xbf,0xa6,0x39,0x25,
+0x6f,0x48,0x9c,0x8d,0xe5,0xc5,0x7b,0x3d,0xd6,0xa2,0x82,0x48,0x5,0x5a,0x7b,0x87,
+0x22,0x49,0xf5,0x91,0xf6,0xa1,0x91,0x12,0x90,0x4e,0xbb,0x4a,0x68,0x8b,0x12,0xcd,
+0x7f,0x48,0xb2,0xb5,0xdf,0xad,0xe3,0xda,0xea,0xc5,0xd4,0xae,0xc5,0xad,0x22,0xb3,
+0xf3,0x4a,0xa7,0x91,0x28,0xba,0xd,0x27,0x91,0x85,0xa7,0x90,0xd1,0x0,0xa0,0x8a,
+0x14,0x42,0x19,0xaa,0x9,0x1a,0xcc,0x88,0x59,0x9a,0x65,0x32,0x21,0xda,0x1d,0xad,
+0x7b,0x42,0xb1,0xb2,0x76,0x23,0x19,0x8,0x20,0x90,0xb6,0xc1,0xa6,0x87,0x5d,0xb5,
+0x9e,0x48,0x4e,0xa3,0x2e,0x36,0x3c,0xf0,0xe8,0xe6,0x28,0x80,0xe2,0x38,0xe3,0x6d,
+0x95,0x49,0x48,0x99,0xed,0x79,0xf,0x25,0x79,0x23,0x50,0x60,0xc1,0x43,0x7e,0xc8,
+0x16,0x4c,0x9a,0xa8,0xaa,0xfd,0x16,0xcd,0x1c,0x60,0xc1,0x75,0x67,0x49,0xcb,0x7c,
+0x38,0x45,0xd9,0x97,0x22,0xdd,0xbd,0x56,0x50,0x8b,0xb,0x29,0x39,0xf6,0xc6,0x8e,
+0x64,0x4c,0x9,0x95,0xb,0x63,0xe7,0xa7,0x18,0x4c,0x8,0x38,0x5,0x3e,0x93,0x3e,
+0x4,0x4d,0xfb,0xbb,0x93,0x6c,0xd6,0xae,0x40,0x3b,0xd8,0x1d,0x84,0x9,0xa1,0x15,
+0x90,0x42,0x91,0x9d,0x32,0xd8,0xdd,0x5,0x75,0x81,0x47,0xf9,0x62,0xda,0xe6,0x72,
+0xf0,0x4e,0x15,0xb6,0x7f,0x59,0x55,0xe4,0x8e,0xf7,0x7a,0x3c,0x8f,0xd2,0x28,0x94,
+0xba,0x46,0x8c,0xbf,0x15,0x44,0x41,0x6e,0xe4,0xf6,0xcb,0x7b,0xd9,0xbf,0x59,0x0,
+0x12,0x4c,0x22,0xb4,0xb3,0xa,0xba,0x65,0x1d,0xb4,0x82,0x5c,0x95,0x83,0x87,0x70,
+0xf1,0x49,0x6d,0x81,0x89,0x21,0x39,0x57,0x9a,0x81,0x45,0x4,0xe7,0x6b,0x78,0xa2,
+0x59,0x48,0x21,0xa4,0x25,0xb8,0x13,0x24,0x82,0xf4,0x1b,0x4d,0x33,0x32,0x3d,0x41,
+0xba,0x47,0xd8,0xb2,0x49,0xe2,0x6c,0x71,0xf8,0x7f,0x47,0x75,0xbd,0x26,0xd8,0x8b,
+0xea,0x4b,0x93,0xb3,0xe,0x13,0xe2,0x4,0x5d,0x9f,0x2f,0xbf,0x7b,0x66,0x2c,0x8c,
+0x66,0x48,0xf6,0x99,0x58,0x79,0x50,0x8c,0x87,0x66,0xcf,0xf5,0xd8,0x2a,0x24,0xe8,
+0xfc,0x46,0x7a,0xbb,0xd9,0x45,0x14,0xf1,0x15,0xf8,0x37,0xae,0x2,0x9a,0xf5,0xc5,
+0x74,0x42,0xd7,0xb7,0xc9,0x7e,0x2e,0x7,0x90,0x1a,0xf1,0xf6,0xd1,0x9a,0xc7,0x13,
+0x8a,0x41,0x75,0x98,0xfa,0x56,0x87,0x43,0xeb,0xd3,0x29,0x45,0x1f,0xaa,0x78,0x5e,
+0xc2,0x44,0x8c,0x8a,0x44,0x74,0x15,0xc4,0xa1,0x7c,0x3,0x36,0x20,0xd,0x8a,0x17,
+0x77,0x47,0xa3,0xa7,0x51,0x6c,0xcb,0xba,0x23,0xef,0xfb,0x8b,0xd5,0x83,0x23,0xd7,
+0x2c,0x4b,0xac,0x97,0xe1,0x7,0x4e,0x2e,0xf4,0xc8,0x15,0x1,0x8e,0x7f,0xfd,0xcc,
+0xa7,0x49,0xc0,0xb9,0xd3,0xd4,0xd3,0x6c,0xde,0x4a,0xf1,0x3a,0x71,0x9e,0x68,0x4f,
+0x38,0x48,0xba,0x90,0xfd,0x6f,0x38,0x7a,0x6c,0x65,0xc5,0x54,0x88,0x1d,0xa7,0x82,
+0xf,0x40,0x20,0xbc,0x7f,0x13,0xfc,0xa0,0x70,0x71,0xdd,0x62,0xcf,0x89,0x64,0x10,
+0x32,0x4d,0x4f,0xa5,0x59,0xbf,0x56,0x81,0xf2,0x1d,0x78,0x8e,0xb7,0x6,0x77,0xd7,
+0xd1,0x4a,0x0,0x81,0xd1,0x51,0x96,0xa1,0x8a,0x99,0x4f,0xd,0x17,0x33,0x98,0x25,
+0xa1,0x44,0x8c,0x9e,0xd,0x83,0xb0,0xc,0x23,0xcc,0xd7,0x7c,0xe3,0xc3,0xc7,0x47,
+0x47,0x42,0x4e,0x88,0xc2,0x9c,0xb8,0x33,0x9c,0x60,0x36,0x9f,0xc9,0xda,0x4e,0xf9,
+0xf0,0x40,0xb3,0xbe,0x78,0xa7,0xcc,0x8e,0x63,0xaa,0x1a,0x17,0x30,0x94,0xb9,0x2b,
+0x67,0x40,0x42,0x9b,0x3e,0x2f,0x1b,0xa9,0x27,0x75,0x79,0x6f,0x21,0x68,0x2b,0x93,
+0x43,0x47,0xf7,0x8c,0xca,0x13,0x68,0x57,0x8f,0xd5,0x1e,0x8b,0x66,0x6,0xc7,0x3a,
+0x67,0x45,0xee,0xa1,0x93,0x8c,0xc8,0x52,0xd7,0x4,0x2,0xb,0x86,0x62,0x97,0xf,
+0xc2,0x4d,0xe5,0xad,0x19,0x2b,0x61,0x1f,0x1d,0xfa,0x47,0xf7,0x1a,0x7b,0xbb,0xaf,
+0x27,0x46,0x27,0x97,0x1f,0x85,0xb0,0xd2,0x5e,0x6c,0x86,0x7b,0x1b,0x22,0x23,0xc8,
+0x7,0x41,0x92,0x8c,0x19,0xd0,0xf6,0x2d,0xd6,0x50,0x3e,0x6,0x50,0x8f,0xff,0x3a,
+0xbe,0x4a,0xdd,0x82,0x2d,0xe3,0x8f,0xc2,0xc0,0xf6,0x45,0x68,0x7a,0x6,0x16,0xc6,
+0x93,0x48,0xa6,0xa0,0x0,0xd4,0x9a,0x58,0xf9,0x5a,0x80,0x97,0xcf,0xc0,0x9b,0x3d,
+0xf5,0x4f,0xf5,0x96,0x37,0xd,0x4d,0x5e,0xfc,0x2a,0x55,0x3a,0xf4,0x42,0x78,0xb4,
+0x51,0x4b,0xef,0x80,0x5e,0xa,0x80,0xae,0x1e,0x51,0x58,0x46,0xbb,0x6f,0x29,0x32,
+0x12,0x49,0x44,0x9f,0x15,0xe2,0x33,0xdd,0xf2,0xaf,0xc8,0xdd,0x4c,0x5d,0xd,0x29,
+0xfb,0x41,0x77,0xa0,0xd7,0x19,0x90,0x5,0xc2,0x51,0xf,0xcc,0x27,0x1f,0x93,0x28,
+0x1f,0x4a,0xa9,0x98,0x78,0xd1,0x93,0x81,0x3b,0x30,0xf2,0xf0,0x1,0x60,0x35,0x81,
+0x12,0x4c,0x4a,0x8b,0xa6,0xc4,0xe9,0x35,0x53,0x80,0x16,0xd6,0x81,0xc,0x96,0x9,
+0x58,0x4e,0xc3,0x9d,0x19,0xd,0x5f,0xf7,0xce,0x6d,0x86,0x7f,0xbf,0x31,0xa8,0xd3,
+0x82,0x4d,0xad,0xb2,0x1f,0xd8,0x24,0x90,0x9b,0x36,0x31,0x14,0x8f,0x95,0x14,0xf3,
+0xf3,0x40,0x7b,0xba,0x67,0x90,0xa0,0xde,0xfe,0xce,0xed,0x7d,0x31,0x66,0x92,0x3a,
+0x67,0x45,0x69,0x8f,0x7b,0x30,0x4d,0x24,0x89,0x30,0xd5,0xf0,0x90,0x37,0x6e,0x97,
+0xd8,0x4b,0xbf,0x90,0xa9,0xae,0x8a,0x64,0x30,0xb7,0xd8,0x8e,0xbd,0xb1,0x49,0x2f,
+0xa2,0x48,0x3,0x83,0xa0,0x71,0x55,0x99,0x80,0xb0,0x26,0xd0,0x1f,0x21,0x22,0x5f,
+0x3a,0x43,0x5b,0xbb,0x87,0x1,0x91,0x3c,0xbe,0xd2,0x96,0x56,0xf3,0x99,0xb5,0x8b,
+0x5b,0x4c,0x14,0xac,0xc0,0xf8,0x5e,0xe6,0x8b,0x46,0xc9,0xed,0x6c,0x8c,0x9a,0xc4,
+0xf3,0x45,0xc5,0x81,0x8,0x11,0x9,0x36,0x30,0xcd,0x72,0x4,0x7a,0xee,0xaf,0x67,
+0x60,0x46,0xf3,0x9b,0xaf,0xbe,0xff,0xf,0xd1,0x66,0xb9,0xe1,0x18,0x41,0xcd,0xe4,
+0x4f,0x42,0xf0,0x98,0x47,0xb7,0x6e,0x4f,0x11,0x20,0xad,0xfb,0x88,0x75,0x29,0xe8,
+0x45,0x41,0xb7,0xb4,0x96,0xe3,0x39,0x3f,0xd,0x32,0x27,0xe1,0xf4,0x62,0xdc,0xc6,
+0x8c,0x43,0x92,0x8a,0x2f,0xce,0x42,0x4d,0x45,0xf,0xce,0x6b,0xf7,0xca,0x6e,0xaf,
+0xc0,0x49,0x95,0x98,0x8d,0x6c,0x57,0x47,0x51,0x0,0x5d,0xa3,0x28,0x46,0xbb,0xec,
+0x86,0x4d,0x57,0x8a,0xce,0x6d,0xe2,0x47,0xdf,0x24,0x36,0xf5,0x2d,0x4a,0xb9,0x8b,
+0xeb,0x41,0x23,0x9e,0xdb,0x50,0xde,0xcb,0xaf,0x25,0xef,0x15,0x8f,0xe9,0x6a,0x6c,
+0xbc,0x40,0x9d,0x8c,0x9f,0xe1,0x8d,0x25,0xf1,0x83,0x35,0xd1,0x86,0x8,0x4d,0x99,
+0x3c,0x45,0xb3,0xbf,0x70,0xd1,0x29,0xbe,0x6a,0x8c,0x45,0xb9,0xd4,0x45,0xf1,0xc8,
+0x8e,0x46,0x10,0x8d,0x28,0xcd,0xb1,0xc0,0x21,0xd7,0x8b,0x3d,0x1e,0x24,0xe0,0x6,
+0xa1,0x4f,0x32,0xa9,0x22,0x6f,0x3,0x99,0x22,0x5f,0xd2,0x65,0x5d,0x24,0xa0,0x27,
+0x58,0x46,0xcf,0xa3,0xb7,0xa3,0x85,0xdd,0x85,0x9a,0xdc,0x67,0xdb,0x2,0x9f,0xd,
+0x54,0x4f,0x74,0x8b,0xa7,0xb1,0x20,0x59,0xb9,0x10,0xe0,0xa7,0x5e,0xd7,0xd,0x2a,
+0x32,0x45,0xdb,0x98,0x9b,0x6e,0x91,0x7d,0x65,0x77,0xe1,0x28,0x9c,0xc8,0x80,0xa7,
+0x8d,0x42,0x60,0xab,0x45,0xa2,0x3e,0x3e,0xc8,0x2a,0x4a,0xa8,0x4b,0x41,0xc1,0xfc,
+0x6b,0x4f,0x6b,0xae,0xd7,0x7f,0x1,0xce,0xf3,0x8e,0xcf,0x34,0x49,0x73,0x52,0xd8,
+0xb4,0x4d,0xc9,0x83,0x94,0x7d,0x9c,0x71,0xff,0xcc,0xa9,0x16,0xce,0xeb,0x9a,0x27,
+0x80,0x4c,0x71,0xb7,0x7d,0x32,0x60,0xad,0x3,0x90,0xfe,0x71,0xac,0x9,0x6d,0xd8,
+0x6,0x4e,0x26,0x87,0x49,0x33,0x83,0x48,0x73,0xac,0x12,0x1e,0x7,0x64,0x6,0xbd,
+0x7a,0x4b,0x6a,0x81,0x6c,0x14,0x9a,0x30,0xfd,0xb2,0x33,0x7,0x9b,0x3c,0xad,0xed,
+0x48,0x4b,0x35,0xad,0x7e,0xdc,0xca,0x11,0x6d,0x20,0x6a,0x6f,0xbb,0xc9,0x5d,0xf5,
+0xc1,0x47,0xe9,0x95,0x8e,0x49,0x85,0x91,0x37,0x95,0x7f,0x63,0x10,0xfb,0xc9,0x27,
+0xd8,0x4a,0xdd,0x8d,0x2b,0x88,0xf,0xdf,0x5b,0xb2,0x1c,0xfe,0xf,0x25,0x24,0xf8,
+0x4b,0x47,0x8b,0xbc,0xf4,0xae,0x9,0x6f,0xec,0xfc,0x33,0x74,0xac,0x5b,0xcc,0xdb,
+0xad,0x4a,0xe,0xbd,0x4b,0xb2,0xb2,0xe8,0xdf,0x6e,0x9b,0x8b,0xcd,0x39,0xb9,0x9e,
+0xc8,0x4d,0x8c,0x83,0xc9,0xa2,0x7a,0x80,0xcb,0x65,0x6c,0x2e,0x77,0x44,0x91,0x46,
+0x35,0x42,0x34,0x83,0xde,0x18,0xff,0xba,0x94,0x44,0x6e,0x12,0x26,0xff,0xe2,0xd4,
+0xb4,0x49,0x7b,0xab,0x91,0x16,0xff,0x7a,0x31,0xd1,0x46,0x40,0x80,0xc0,0x57,0x2,
+0xe6,0x46,0x26,0x8b,0x1d,0x68,0x51,0x64,0xbb,0x44,0x1d,0xe8,0xe,0xd1,0xce,0xcb,
+0x3e,0x4a,0x98,0xa3,0x55,0xa1,0x7,0xab,0x9d,0x48,0x35,0xdd,0x28,0xd0,0x6e,0xc,
+0xd6,0x41,0x28,0xaa,0x12,0x67,0x7b,0xde,0x12,0x87,0xac,0x9c,0xcf,0x27,0x8a,0x7f,
+0x2f,0x47,0x8c,0xb5,0x92,0x7f,0x8e,0xd8,0x39,0xc8,0xe4,0x17,0x86,0xb2,0x3e,0x63,
+0x53,0x4d,0xb0,0xaf,0xf3,0xa7,0x63,0x9d,0xb3,0xc,0x1f,0x7e,0x71,0x56,0xcb,0xf3,
+0x6b,0x4b,0x79,0xa9,0xa2,0xf2,0xdc,0xfb,0x10,0x5a,0xaf,0x7b,0xa3,0x88,0x8d,0xd7,
+0x5c,0x49,0x7d,0xb4,0x6,0x4f,0x10,0xcb,0x41,0x92,0x8f,0x7,0x14,0xa2,0x62,0x5f,
+0x57,0x4b,0x71,0xb1,0x7,0x4a,0x13,0x9a,0x1f,0x77,0x9a,0xf,0x6f,0xc7,0x54,0x4f,
+0xe6,0x4a,0x3c,0xa3,0x25,0xf7,0xfa,0xc2,0xb6,0x25,0x2d,0xd7,0x49,0xd6,0x9b,0x80,
+0x2f,0x43,0xe1,0x81,0x63,0x43,0xe3,0x15,0x32,0xf6,0x97,0x7f,0x8c,0xb0,0x58,0xb5,
+0x78,0x4d,0x39,0x80,0x6,0xd5,0x52,0x35,0x27,0x5b,0x2f,0xea,0x61,0xdf,0x1a,0x3a,
+0x3d,0x44,0xff,0xbe,0xed,0xf5,0x74,0x4a,0xe,0x99,0xa9,0x1c,0x5b,0x55,0x4d,0xd,
+0x97,0x47,0xf1,0xbd,0x76,0x18,0x87,0x57,0x5a,0xd1,0x79,0x2f,0xae,0xd7,0x26,0xb6,
+0xeb,0x44,0xd2,0x9d,0xce,0xb9,0x36,0x62,0x98,0x17,0x9b,0x46,0xc9,0xe0,0xd6,0x39,
+0x9,0x4d,0xdd,0x94,0x13,0xce,0xdf,0xa1,0x2d,0xe4,0x43,0x9d,0xe,0xba,0x30,0xf,
+0xe,0x4b,0x2d,0x96,0xa6,0x12,0x47,0x18,0xef,0x77,0xb7,0x55,0x99,0x52,0x87,0xbe,
+0x7c,0x4a,0x67,0xba,0x19,0xd9,0xc3,0x11,0x5a,0xd5,0xad,0xa9,0xa6,0x63,0x14,0xc,
+0x93,0x42,0x66,0xa3,0xa7,0x53,0xbe,0xd6,0x8f,0x1b,0xda,0x9a,0x19,0x9,0x6c,0xc0,
+0x2b,0x42,0x2,0x9f,0x2,0x88,0xc3,0xbf,0xfd,0x4,0x1e,0xdc,0xc0,0xfa,0x88,0x3c,
+0xb0,0x43,0xcd,0xb0,0xaf,0x35,0x42,0xc4,0xd4,0xb2,0x45,0xae,0x10,0x7b,0x6b,0xb,
+0x90,0x4b,0x95,0x8a,0xa1,0x15,0x15,0xc3,0x17,0xd8,0x18,0xee,0xe,0x6b,0xba,0xa9,
+0xec,0x4f,0xe6,0xb0,0xf6,0xf5,0x6,0xad,0xe6,0x3f,0xd9,0x1e,0xae,0xad,0x14,0xb9,
+0x47,0x43,0xe2,0x86,0x5c,0x36,0xa6,0xaa,0xe9,0x1f,0x4d,0x2b,0x10,0x7c,0x3a,0x5c,
+0x1e,0x48,0xad,0xba,0xdb,0x93,0x9c,0xc8,0x9,0xc1,0x5b,0xd4,0xfa,0xa3,0x1b,0xfd,
+0xe,0x49,0x40,0x80,0xd9,0xc7,0x18,0x69,0xe,0xa2,0x59,0xb8,0x49,0x73,0x48,0xc5,
+0x41,0x43,0x20,0xa5,0x9b,0x6e,0xef,0x70,0xe2,0x3a,0x8f,0xdb,0x25,0xb5,0x4b,0x5e,
+0xa9,0x41,0x5d,0x89,0xa3,0xc5,0xb3,0x1b,0x46,0x2f,0x22,0xee,0x49,0x5a,0x58,0xc,
+0xb2,0x4d,0x1e,0x9b,0x6b,0x18,0x55,0xb8,0x24,0xa0,0xfa,0x9c,0xde,0x67,0xe1,0x7e,
+0xa1,0x49,0x6,0x83,0x2a,0x21,0x2e,0x71,0xba,0x9d,0x8c,0xb1,0x73,0xa3,0xdb,0x1,
+0x67,0x45,0x95,0x93,0x8a,0xbe,0x40,0x92,0x3c,0xdc,0xaf,0xba,0xe1,0xd7,0x96,0xe3,
+0x26,0x45,0x5d,0x87,0x95,0xea,0xac,0x6,0xd1,0x7f,0x87,0x37,0xc3,0xea,0xe2,0xc8,
+0x2b,0x4f,0xc0,0x8d,0x8d,0x57,0xb7,0x3c,0x61,0xd3,0x9f,0x46,0x52,0x7d,0xe0,0xd7,
+0xb7,0x42,0xfb,0x9f,0x82,0xf0,0x8f,0xd3,0xea,0xa2,0xaf,0xf5,0x79,0x9d,0x14,0x0,
+0x71,0x40,0xe6,0x89,0xe5,0x52,0x5b,0x25,0xfb,0x94,0x8a,0x78,0xb3,0xb4,0x91,0x70,
+0xce,0x4c,0xb9,0x84,0x91,0x58,0x2f,0xdd,0xeb,0x61,0xde,0x69,0xb4,0x58,0xf,0x7d,
+0xdb,0x44,0x70,0xa3,0x3d,0xa1,0x85,0x6a,0x7f,0xde,0xf2,0x72,0xc9,0x69,0xb5,0x45,
+0x5b,0x48,0x15,0xaf,0xe2,0xfe,0x1a,0x20,0xce,0xd7,0x1d,0xd2,0x8,0xb1,0xcd,0xeb,
+0x88,0x49,0x36,0x80,0xf2,0x85,0x70,0xa3,0xe4,0x10,0xa1,0x7d,0x60,0xb1,0x78,0x79,
+0x90,0x48,0xe8,0xb8,0x16,0xaf,0xf8,0x1,0xf9,0x18,0xb2,0x54,0x47,0x9d,0x10,0xed,
+0xb9,0x41,0xc1,0x89,0xf2,0x60,0x42,0x36,0x19,0x5a,0xc0,0x9,0xf,0x8b,0xe2,0xc4,
+0xb,0x42,0xa,0xba,0x62,0xe2,0x11,0x1,0xf8,0x7e,0x3,0xd7,0x71,0x55,0x4d,0xb,
+0xfc,0x45,0x3c,0xa3,0x29,0xe6,0xfd,0xff,0x1b,0x3,0x13,0xa6,0xad,0x33,0x4d,0x51,
+0x96,0x4a,0x52,0x9f,0x78,0x39,0x1c,0x75,0x6f,0x38,0x27,0xb0,0x2a,0xbc,0xc8,0x6e,
+0x7b,0x44,0x11,0xb9,0xf8,0xf9,0xfb,0x4e,0xec,0x2a,0x2a,0x5d,0x2f,0x8e,0xd6,0x29,
+0x29,0x4c,0xd7,0x8b,0xc8,0x4d,0x58,0xfb,0xd0,0x9d,0xa1,0xde,0x91,0x1a,0x9e,0xf6,
+0x6d,0x4b,0x2f,0x97,0x7a,0x21,0x6e,0x8e,0x80,0xc6,0x11,0x3c,0x99,0xb8,0x14,0xfd,
+0xc4,0x43,0x3b,0xbe,0x3c,0x4d,0x38,0xb8,0x4b,0x39,0x8d,0x26,0xc,0x81,0x8,0x38,
+0xfb,0x48,0x72,0x89,0xdc,0x25,0xb3,0x42,0x54,0x4a,0xad,0x56,0xe5,0x84,0x1a,0xfa,
+0xbc,0x4c,0x6d,0xad,0xa7,0x41,0xf8,0xb4,0x4c,0x5,0x4f,0xb9,0xdd,0x75,0xc4,0xa2,
+0x83,0x42,0x38,0x8c,0x32,0x45,0x2,0x72,0xf,0x48,0x59,0x50,0x57,0xc8,0xce,0x5,
+0x13,0x4a,0x4a,0xa7,0x79,0x4a,0xce,0x1c,0x7,0xa9,0xf4,0x3d,0x93,0x69,0xaa,0x85,
+0xfd,0x45,0x19,0xaa,0xfd,0x48,0x27,0xd8,0x8a,0xdd,0xb,0xc7,0x3,0x46,0x5e,0x32,
+0x1a,0x46,0x64,0xab,0xba,0x1c,0x7e,0x35,0x62,0xe7,0xd2,0x20,0x1c,0x1e,0xe9,0xaa,
+0x36,0x4c,0xcc,0x81,0x8f,0xac,0x8c,0xaa,0x60,0x1a,0x8,0x3a,0x93,0x13,0x74,0xf5,
+0xed,0x42,0x75,0xab,0x73,0xf8,0xda,0x4,0xa,0x67,0xdf,0xc7,0x5e,0x25,0x1e,0xb9,
+0x34,0x42,0x61,0x99,0x7e,0x77,0x9f,0xb8,0xbb,0x80,0xd7,0x90,0xe,0xee,0x94,0x30,
+0x81,0x4f,0x17,0xa6,0xa1,0x95,0xd5,0x94,0xc3,0x73,0xa,0x1b,0xeb,0x3,0x7b,0xa6,
+0x59,0x43,0xb,0x92,0x76,0xf5,0xef,0x4c,0x23,0x78,0x1,0x54,0xd0,0x57,0x42,0xf0,
+0x7c,0x4b,0x2f,0xae,0x2e,0x17,0x8a,0xa,0x52,0xa9,0x53,0xae,0xc4,0x85,0x6e,0xea,
+0xb8,0x44,0x9b,0xba,0x35,0x4b,0x56,0x3,0x12,0x8f,0x43,0x1c,0x59,0x10,0xb9,0xeb,
+0xe,0x46,0x3c,0x9d,0xb8,0xba,0xdd,0x34,0x19,0x70,0xef,0x6,0x90,0x6,0x90,0x5e,
+0x67,0x46,0x59,0xb6,0x6f,0x58,0xb8,0x3a,0x31,0xa4,0xab,0xb2,0xdb,0x9f,0xe9,0x7d,
+0xf7,0x41,0xc8,0x8d,0x39,0xa4,0xb8,0x99,0xa1,0x7c,0x5e,0xe1,0x40,0xb6,0xf2,0x64,
+0xf7,0x43,0xf9,0xbf,0x93,0x7b,0x73,0xc1,0xc3,0x82,0x63,0x28,0x3f,0x31,0x3f,0xca,
+0x64,0x42,0x27,0xb2,0xba,0xa7,0x18,0xbc,0x21,0xc,0x3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x28,0xfb,0x48,0x30,
+0xbf,0x4d,0x4b,0x91,0xbf,0xda,0x61,0x99,0xb7,0x5d,0x40,0xe5,0x4e,0xd5,0x82,0x7f,
+0x28,0x4e,0x2,0x88,0x19,0x61,0xd7,0x88,0x34,0x2b,0x1c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xab,0xba,0xe9,0x99,0x21,
+0x97,0x43,0x52,0xaf,0x8b,0x3d,0x96,0xf7,0x22,0xb1,0xe7,0x6e,0xf5,0x5d,0xd3,0x53,
+0x44,0x4c,0xf7,0xb2,0xb4,0x9e,0xf1,0x3f,0xd1,0x7b,0xbf,0x67,0xf,0x8d,0x82,0xd3,
+0xa3,0x4b,0x0,0xa4,0x4f,0x66,0x31,0x11,0xd,0xe0,0x81,0x6b,0x71,0x66,0x10,0xee,
+0x73,0x49,0xec,0x9c,0x4d,0x6b,0x5e,0x9e,0x38,0xee,0x10,0xfa,0x79,0xb5,0x9a,0xd2,
+0x70,0x49,0xd0,0x96,0x16,0x82,0xa8,0x5f,0xc6,0xb9,0x96,0x1d,0xfc,0xe1,0x5a,0xf,
+0x7e,0x47,0x55,0xb9,0xc2,0x51,0x15,0x98,0x33,0x94,0x70,0x17,0x33,0x76,0xea,0x3e,
+0x42,0x4f,0x1c,0x81,0x2c,0xb4,0xa4,0x40,0x94,0xf1,0x88,0x2a,0x11,0x84,0xad,0x7f,
+0xd1,0x4c,0x31,0x89,0x10,0xd,0x60,0x99,0xd5,0x62,0x5d,0x92,0xbd,0x38,0x9,0x16,
+0x2,0x45,0x20,0xaf,0x58,0xd5,0xa4,0xfa,0x3,0x58,0x25,0xb0,0x6f,0xd3,0xae,0x3d,
+0xfe,0x4b,0x9a,0xb2,0xdb,0xa5,0x2b,0x3c,0xa2,0xf3,0xab,0x53,0x26,0xa3,0xc8,0x4f,
+0x23,0x4b,0x5d,0xb3,0x81,0xb7,0x5b,0x4,0xe1,0xbd,0x94,0x36,0x0,0x69,0x66,0x9e,
+0xe0,0x4b,0x5f,0xba,0x61,0x23,0xbb,0xe,0x63,0xfd,0x47,0x2e,0x7f,0x37,0x90,0xaa,
+0xd,0x42,0x2b,0x97,0xe3,0x64,0x57,0xa1,0x2e,0xda,0x9,0x3f,0x5d,0x85,0x60,0x30,
+0xe4,0x4f,0xe9,0x80,0x26,0xff,0x58,0x9a,0x70,0x2a,0x16,0x1d,0xf6,0x65,0x1,0x33,
+0xe0,0x41,0x9c,0xaf,0x36,0x1c,0xd,0xd4,0x27,0xa7,0xea,0x62,0xf9,0x6c,0x76,0xc0,
+0x62,0x49,0x80,0xb1,0x40,0xe5,0xb6,0x73,0x3f,0x9f,0x43,0x5f,0x34,0xa6,0x63,0xe5,
+0xe6,0x45,0xba,0x90,0x18,0xa3,0xeb,0xd2,0x31,0x1e,0x80,0xeb,0x27,0xbf,0xab,0xb3,
+0x2,0x47,0xe0,0x88,0xae,0x7d,0x4e,0xeb,0xed,0x11,0x2b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3d,0xe1,0xed,0xfd,0xf7,
+0x94,0x4e,0x1,0xb2,0xb2,0x36,0xe2,0x4d,0xb0,0x2e,0x5d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6d,0x8e,0x98,0x91,0x2c,
+0x5b,0x49,0x66,0x86,0x1a,0x11,0xbf,0xc6,0x1c,0x9d,0xec,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe3,0x66,0xc3,0xdc,0x51,
+0xcb,0x4b,0xe5,0xae,0xd7,0x6e,0xcb,0x74,0xc7,0xcd,0x86,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9e,0x8c,0xeb,0x83,0xd2,
+0xf1,0x4e,0xa7,0xa3,0xfa,0x3d,0xf7,0xc6,0x68,0x80,0xea,0x62,0xdf,0x5c,0x48,0xea,
+0xae,0x41,0x8c,0x91,0x67,0xfc,0x9d,0x61,0xc4,0xce,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0x58,0xac,0x7b,0xbf,
+0x47,0x4a,0xd6,0xbc,0xe4,0x10,0xd9,0xf6,0x9e,0x43,0x3a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x93,0xa7,0x3,0xa1,0xdf,
+0x22,0x48,0x1c,0xa4,0x7d,0xe3,0xe3,0x43,0x6f,0x92,0x6b,0x3f,0x41,0x5f,0x6f,0x2e,
+0xba,0x45,0x5f,0xb0,0xa0,0x87,0x4e,0x77,0xbd,0x79,0xe3,0x35,0x11,0xb7,0x9b,0xb4,
+0x74,0x49,0xbf,0xa9,0xa0,0xb8,0xa5,0xf2,0x9b,0x70,0xe4,0x14,0x69,0xcd,0xa4,0x25,
+0xb3,0x45,0x4e,0x84,0x4a,0x48,0xe8,0x38,0xb2,0x63,0x5b,0xe4,0x7a,0x41,0xd2,0x70,
+0x57,0x47,0xec,0x8d,0x52,0x2d,0x17,0xfe,0x74,0x56,0xbe,0x8,0x99,0xe2,0x8e,0xb3,
+0x96,0x49,0x88,0xaf,0x6,0x30,0x37,0x39,0x1b,0x1b,0x20,0xfa,0xb9,0xbe,0x3b,0xc7,
+0xe5,0x46,0xfa,0x92,0x95,0xad,0xb2,0x82,0x2b,0xb8,0xd6,0x8,0x46,0xf8,0x5f,0x2,
+0xcf,0x49,0xd,0xa6,0x17,0xfd,0x34,0x65,0xab,0x2b,0x47,0xdd,0xbb,0xc3,0x2f,0x48,
+0x3c,0x47,0x10,0x91,0x20,0xa8,0x13,0x15,0xde,0xa4,0x55,0x78,0x4c,0x61,0xcf,0x41,
+0x1c,0x41,0x26,0xb9,0xac,0x4f,0x7f,0x1f,0x38,0x50,0x65,0x8f,0xda,0x11,0x65,0x25,
+0x29,0x45,0xad,0xb9,0xf5,0xac,0xea,0xf,0x3d,0xdb,0xad,0x3a,0x27,0xf2,0x64,0x90,
+0xeb,0x42,0x85,0x92,0x4f,0x3e,0xed,0xe5,0xc8,0xbc,0x85,0x78,0xb6,0x2d,0x17,0xfb,
+0xd4,0x41,0x34,0x8c,0x4,0xca,0xc3,0x6f,0x78,0xff,0x69,0xd1,0x83,0xb4,0x21,0x90,
+0xb3,0x43,0x35,0x81,0xbc,0x83,0x1,0x7c,0xd2,0x9a,0x58,0x3e,0xc,0xa,0xd4,0xba,
+0xa0,0x4b,0x79,0x92,0xe7,0x55,0xb7,0x92,0x7b,0x30,0x71,0xb0,0xbe,0xc5,0xb2,0xc2,
+0xd7,0x4f,0xbb,0xb6,0x28,0x45,0x1f,0x77,0xe6,0xce,0x7e,0xd7,0x29,0xed,0x85,0x62,
+0xc0,0x47,0x22,0xa2,0x20,0xdf,0x15,0xad,0xbe,0x3e,0x6f,0x93,0xba,0x3f,0x6d,0x74,
+0x37,0x47,0x73,0xae,0x67,0x79,0xbf,0x49,0xc,0xfa,0x53,0x8a,0x1d,0xb9,0x3c,0x8d,
+0x83,0x49,0xe0,0xac,0x43,0xa0,0xe7,0xdb,0x5d,0xee,0xf0,0x9,0xb4,0xa9,0x61,0xb7,
+0xbd,0x48,0x5f,0x9f,0x83,0x8e,0xd8,0xa2,0xd3,0xab,0xc5,0x4c,0xe0,0x4e,0xcf,0xec,
+0x4f,0x4f,0x8e,0x9a,0x9e,0xbb,0xb8,0x64,0x4c,0x6,0x23,0xc,0x57,0xa1,0x7b,0x5,
+0x2f,0x4c,0x35,0x92,0x55,0x2f,0xdb,0x1e,0xa3,0x63,0x45,0x55,0xcb,0x4b,0x1f,0x96,
+0xa1,0x47,0xa1,0x97,0xe1,0x9c,0xeb,0x93,0x4b,0x97,0xb7,0xa0,0x12,0x9b,0x59,0xfe,
+0x8c,0x4a,0xbc,0x99,0xd0,0x10,0x18,0xea,0xcd,0xdc,0x8c,0x5d,0x9a,0xa8,0x4f,0xd3,
+0x38,0x4d,0x45,0xa4,0xf4,0xc6,0xd2,0xdb,0x30,0x6a,0x26,0xe,0xf4,0x55,0x14,0xb9,
+0xfb,0x45,0x56,0x9d,0x37,0x27,0x97,0x58,0x7d,0x44,0xda,0x72,0x31,0xc2,0xc5,0xc5,
+0x84,0x4c,0xe9,0x82,0x86,0xc3,0x48,0x57,0x18,0x63,0x78,0xc2,0xe6,0x21,0x99,0x33,
+0xd7,0x4a,0x35,0x84,0x99,0x7f,0x71,0x57,0xc4,0x6f,0x7a,0x7,0xa7,0x3,0x53,0x29,
+0xe9,0x4f,0x7c,0x86,0x9d,0xb,0x4,0x2d,0x23,0x5e,0x5b,0x14,0x74,0xab,0xfa,0x10,
+0xac,0x4b,0xa1,0x9c,0xa7,0xf4,0x9d,0x29,0x82,0x7b,0x92,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x43,0xca,0x95,0x13,0x7a,
+0x93,0x46,0x64,0xbc,0x85,0x97,0x77,0xc1,0x13,0xa3,0xb8,0x33,0x3f,0xe,0x92,0x25,
+0xe5,0x44,0x74,0x81,0xb4,0x5b,0x36,0x5c,0xb0,0x31,0x9a,0x5,0x46,0x25,0x17,0x3d,
+0xe0,0x45,0x2c,0xb7,0xba,0xb2,0x8a,0x85,0x6c,0x0,0x2b,0xb1,0x5a,0x44,0x74,0x40,
+0xd0,0x41,0x74,0x9e,0xb8,0xda,0x5e,0x7b,0xff,0x4c,0xae,0x7c,0x30,0xd7,0xf7,0xa9,
+0xd1,0x4c,0xbc,0x9a,0x65,0x4f,0x29,0xba,0x94,0x35,0x15,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0x32,0x42,0xa8,0xfa,
+0xf4,0x44,0xde,0xa4,0x86,0x9f,0x57,0x14,0x61,0x4c,0x15,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7d,0xaa,0xb7,0xf,0xe2,
+0xf5,0x48,0x66,0xae,0x92,0x9b,0xb9,0x86,0x49,0xc7,0x9d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x2d,0xd3,0xec,0x39,
+0xa2,0x41,0xd8,0xb0,0x65,0x83,0x70,0x85,0x48,0xa,0xc3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x99,0x23,0x1,0x4f,
+0x81,0x45,0x1d,0x9d,0x5f,0xd4,0x8a,0x3c,0xb1,0x3b,0xa7,0x88,0x25,0xf6,0xa5,0x51,
+0xd,0x4a,0x85,0xb9,0xcc,0x72,0xd9,0x26,0x99,0xb2,0x81,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x1b,0xe,0x59,0xda,
+0x7f,0x4b,0x3b,0x85,0x12,0xc4,0xb2,0x4e,0x56,0xb7,0xc5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9c,0x91,0xc8,0xb7,0xd2,
+0xe9,0x4e,0x9e,0xa3,0x35,0xa0,0x18,0x60,0xc9,0x65,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc2,0x8e,0xf9,0x80,0x6e,
+0x30,0x45,0x56,0xb7,0x86,0xba,0x93,0x8d,0x69,0x24,0x49,0x77,0xaa,0x3d,0x14,0x35,
+0xe4,0x4a,0x60,0xa2,0x1d,0xd6,0x73,0x1c,0xea,0x40,0x3c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfb,0xca,0xe9,0x80,0x49,
+0x74,0x4f,0xf1,0x8a,0xf0,0xe6,0xa4,0xd9,0x5d,0x8e,0xf0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x86,0xbd,0xb8,0x7d,0x73,
+0x56,0x44,0x45,0xa6,0xc3,0x9b,0xd8,0xad,0xff,0x51,0xec,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x83,0x9c,0x14,0x5a,
+0x10,0x4f,0x85,0xa3,0xe,0x37,0xcc,0x1e,0x52,0x4e,0xd8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4f,0x62,0xe1,0x6a,0xc7,
+0xf5,0x4f,0xce,0x8c,0x2e,0xa9,0x90,0xfb,0x7,0x35,0x93,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5f,0xe5,0x27,0x6a,0x3a,
+0xf6,0x4b,0x94,0xa4,0x72,0xbc,0xc5,0x85,0xe6,0xf8,0xf1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x7b,0x32,0x15,0x5,
+0x19,0x47,0x90,0x9e,0x2e,0x83,0xd6,0x53,0x82,0x22,0xa0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x66,0x58,0x45,0x46,0x4b,
+0xf,0x48,0x13,0xb5,0xea,0xca,0x98,0x50,0x1c,0xe6,0x4b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x96,0xc0,0x98,0x97,
+0x17,0x40,0xd5,0x90,0x47,0x23,0x3f,0x16,0x30,0x93,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x2a,0x4,0x3b,0xf,
+0x4c,0x45,0xf8,0xb6,0x20,0x20,0xd6,0x28,0x3d,0x72,0x90,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x5d,0x66,0x5d,0x28,
+0x38,0x43,0x98,0xb5,0xcd,0x48,0xbf,0xb2,0x17,0x36,0xc7,0x96,0x4d,0xd9,0x4f,0x14,
+0x35,0x41,0xfd,0xbf,0x61,0x2e,0xc2,0xc6,0x2a,0xf7,0x5e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x49,0x53,0xf9,0xbd,0x73,
+0x39,0x41,0x8b,0x8e,0xdc,0xef,0x65,0x51,0x8f,0x30,0xf4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b,0xef,0x43,0x99,0x8,
+0xf9,0x48,0x35,0xbf,0x9a,0x89,0xe7,0x20,0xee,0xbc,0xe9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf3,0x5c,0x38,0x69,0x5a,
+0x5e,0x48,0xe5,0xa0,0x1,0xaa,0xac,0x2c,0x45,0xff,0xf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaf,0x54,0xf4,0x45,0x6b,
+0x6,0x41,0x9c,0x80,0x9a,0xde,0x82,0x98,0x1b,0x3b,0xb5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x8,0x3a,0x65,0x3,
+0x52,0x49,0x79,0xad,0x24,0xe5,0x4,0x2d,0x49,0x93,0x4f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x99,0xdd,0x3e,0x93,0x95,
+0xa3,0x4f,0x73,0x9f,0xa9,0x11,0xf7,0xe0,0x1e,0x53,0x5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x69,0x8c,0x18,0x12,
+0x17,0x47,0x99,0xa7,0xd5,0xcb,0xab,0x57,0xf1,0xae,0x9a,0x1e,0x17,0x8,0x18,0xc9,
+0xfb,0x40,0x67,0x89,0xeb,0xe1,0x0,0xae,0xeb,0x6,0xd0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x17,0x1f,0xda,0xca,
+0x6,0x40,0xe9,0xa0,0x81,0x37,0xf9,0x2,0x2,0x9d,0x27,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x37,0x5b,0x74,0xb4,0x97,
+0x32,0x4b,0xe1,0x88,0x95,0xf7,0x5c,0x88,0x5e,0xa3,0x2f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd7,0x9d,0xda,0x8e,0xc6,
+0x2c,0x44,0x39,0xb3,0x42,0x8,0x7e,0xc7,0xfe,0x6,0x41,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0xa5,0x55,0xe9,0x90,
+0xfe,0x4a,0x30,0xbf,0xfb,0x21,0xde,0x9e,0x11,0xa2,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb1,0xb6,0x33,0xed,0x9f,
+0x6c,0x45,0x22,0x89,0x1c,0x72,0x9f,0xed,0xbd,0x2e,0xad,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xf9,0x31,0x96,0xad,
+0xd8,0x49,0xfa,0xae,0xd0,0x3b,0xbe,0xf,0x22,0xfc,0x5b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1b,0x5a,0x1b,0x5a,0x52,
+0x57,0x4a,0x87,0x8e,0xdf,0x59,0xac,0x91,0x40,0xa0,0x30,0x5b,0x62,0x29,0x29,0xbf,
+0xb1,0x4c,0x1e,0xa6,0x6c,0x5f,0xf2,0x9e,0xca,0x3a,0x25,0x2c,0xe7,0xf2,0x61,0x53,
+0x1e,0x40,0xdf,0x88,0x6e,0xba,0x41,0x13,0x4,0xd1,0x0,0x4c,0x6f,0xa2,0xd8,0x94,
+0xe6,0x49,0x30,0xb4,0x7c,0x87,0xae,0x40,0xf7,0xf,0xdf,0xd0,0x0,0x99,0xd7,0x9b,
+0x82,0x44,0xf3,0xad,0xc1,0xa7,0x35,0x2,0x16,0x68,0xc1,0xf,0xfa,0x6d,0x42,0x4e,
+0xfc,0x41,0xac,0xb4,0x82,0x90,0x24,0x8a,0x7d,0xae,0x57,0xf7,0x9f,0x9,0x2c,0xfe,
+0x95,0x4e,0x70,0xa8,0xcf,0x5a,0x9d,0xab,0x1c,0x16,0x35,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xb2,0x7a,0x7d,0x95,0x73,
+0x6d,0x4d,0x48,0xbe,0xe4,0xb5,0xe5,0xaf,0x6e,0x20,0xd0,0x54,0xd6,0xd6,0xa7,0x6c,
+0x71,0x40,0x6b,0xbc,0xd8,0x2a,0xb8,0xbf,0x4c,0x15,0xb9,0x92,0xf9,0x75,0xa0,0xe9,
+0x1c,0x42,0x4a,0x95,0x8b,0x59,0x90,0x57,0xfb,0x79,0x91,0xe0,0xd9,0xac,0xde,0xb2,
+0x5a,0x43,0x24,0x8b,0xbf,0xe4,0x6c,0x5a,0x9e,0x16,0xfa,0xfe,0xa9,0xf1,0xcb,0x6d,
+0xe4,0x49,0xb2,0x87,0x9a,0x7e,0xf4,0xd0,0x1c,0x2a,0x2c,0xf4,0xb,0x4d,0x44,0xaa,
+0x96,0x49,0xee,0xb9,0x11,0x93,0xbb,0x65,0xcb,0xb,0xa6,0x8f,0xb4,0xfd,0xda,0xdc,
+0x3c,0x4f,0x7,0x88,0x65,0xd8,0x59,0x84,0xfa,0xc3,0x28,0xba,0xbd,0xf7,0x12,0x30,
+0xbf,0x43,0xee,0xbd,0x8a,0x2a,0xb7,0x46,0xc8,0x2c,0x6c,0x91,0x27,0xa3,0x3e,0x6e,
+0x27,0x4e,0x45,0x85,0xf9,0x3d,0x6a,0x44,0x41,0xde,0x79,0x4c,0x6e,0xe4,0xc7,0xf7,
+0xfd,0x41,0x1e,0x84,0xa4,0x4e,0x55,0x99,0x5c,0xdc,0x11,0x35,0xed,0x3c,0xf0,0x2e,
+0x7a,0x4b,0x8c,0xa7,0xd1,0x9e,0x45,0xf3,0x3e,0x84,0x4e,0xe1,0xf5,0x67,0xe9,0x94,
+0xd7,0x4f,0xc1,0x89,0xf9,0xb5,0xce,0xb,0xfe,0x31,0x4e,0xcc,0x2d,0x24,0xbc,0x3c,
+0xa,0x4d,0xc0,0x9e,0x9f,0x8b,0x8e,0x3c,0x7b,0x6f,0x1,0x2f,0x27,0x7b,0xf0,0xc7,
+0x62,0x4d,0x4c,0x8f,0x2f,0x88,0x4c,0x74,0xc,0x6a,0x76,0xc,0x6f,0x1b,0xe0,0xfd,
+0xbe,0x4b,0x2b,0x8f,0xa3,0x4e,0xf5,0xaf,0x33,0x32,0xba,0xe5,0x59,0xce,0xa4,0x44,
+0x74,0x49,0x85,0x8c,0xf0,0xd4,0x28,0x7e,0x7b,0x85,0xfb,0x21,0x77,0x5f,0xfe,0x1f,
+0x2e,0x47,0xe1,0xa1,0xcd,0x74,0x43,0x24,0x51,0xce,0xdf,0xaf,0x8e,0xfc,0x9e,0xef,
+0x64,0x4a,0x3b,0x86,0xb1,0x7b,0xb9,0x63,0xce,0xed,0x28,0xda,0x33,0xd6,0x53,0xfc,
+0xc5,0x48,0x28,0xb5,0xfa,0xfe,0xfe,0xa6,0x9e,0x33,0x60,0x63,0x35,0xe6,0x4c,0x13,
+0x3c,0x46,0xc2,0x88,0x7c,0x8f,0x93,0x59,0x7c,0x82,0x77,0x50,0x89,0x7e,0x78,0x29,
+0xcd,0x4c,0x5f,0xaa,0x64,0x6e,0x8d,0xdc,0x90,0x96,0xcb,0xb,0x88,0xcc,0x93,0x6a,
+0xfb,0x4d,0xc6,0x8d,0xb1,0xf0,0x8a,0x8d,0xbd,0xdc,0xa6,0xf,0xea,0xbe,0x58,0x4,
+0x13,0x4d,0x47,0x90,0x8b,0xb6,0x9a,0x54,0x30,0xf8,0xab,0x64,0x4c,0x78,0x32,0xc3,
+0xb3,0x43,0x15,0x95,0x2a,0xe6,0x48,0x3f,0x5e,0x42,0x2c,0x9,0x47,0x25,0x51,0xad,
+0x78,0x4c,0x1e,0xb5,0x2e,0xc,0xd0,0xd2,0x7e,0xa1,0xb3,0x58,0x6f,0x8c,0x62,0x23,
+0xfa,0x47,0x75,0x89,0x7f,0x8a,0xfc,0xe8,0x64,0xa1,0x1f,0x51,0x25,0xcc,0xc5,0xdb,
+0x1f,0x4f,0xd4,0xb8,0x35,0xa5,0x53,0x94,0x89,0x1f,0x5f,0xec,0x3f,0x1b,0x1c,0xaa,
+0xb5,0x43,0xdc,0x87,0x7d,0x56,0x87,0x49,0x7d,0x26,0x12,0xc6,0xfc,0x69,0x2b,0x31,
+0xf2,0x48,0xf,0x84,0x72,0xce,0xc5,0x4b,0xf8,0x7d,0xda,0x3d,0x57,0x56,0x2c,0xd2,
+0x3c,0x48,0xd5,0x82,0x36,0xc5,0x42,0xac,0x1f,0xcb,0x74,0x77,0x28,0xe8,0x41,0x59,
+0xab,0x4c,0x80,0xb0,0xb,0x24,0x37,0x53,0xd1,0x3a,0xf2,0xa5,0x4d,0x15,0xd4,0x70,
+0x5b,0x4c,0x2b,0x89,0x19,0x60,0xde,0x2e,0x62,0x77,0xf4,0xf7,0x39,0xc6,0x3b,0x53,
+0x54,0x4a,0x1,0x86,0x36,0x46,0x34,0xec,0x76,0x37,0xdc,0xcd,0xfb,0x1,0xc1,0x7d,
+0x1f,0x4f,0xe4,0xa3,0xe7,0x7b,0xee,0x80,0xbe,0x65,0x73,0xd8,0x96,0x57,0x40,0x59,
+0xd9,0x4e,0x8b,0xbd,0xa3,0xc3,0x91,0x1a,0x58,0x34,0x6f,0xfd,0xd6,0xea,0xbd,0xb,
+0x32,0x4d,0x84,0xbd,0x15,0x2d,0x33,0x31,0x43,0xbd,0x1a,0x1e,0xdd,0xd2,0x3c,0x4e,
+0x7f,0x42,0x64,0xba,0xf4,0xe8,0x93,0x41,0xa3,0x33,0x6c,0xe1,0x10,0xd1,0xb9,0x20,
+0x42,0x4d,0x7e,0x80,0x54,0xdb,0xf7,0x38,0x61,0xfb,0xd3,0xae,0xf9,0xf5,0xb2,0xde,
+0xcb,0x47,0xd6,0xab,0x29,0x1,0x89,0x40,0x18,0x23,0x78,0xb2,0xe9,0x23,0x9,0xce,
+0xbf,0x42,0xdd,0x8d,0x99,0x6,0xb4,0x85,0xfd,0x54,0x56,0x1,0xce,0xcc,0x16,0x93,
+0x3c,0x42,0xed,0xaa,0xa1,0x45,0xf2,0x30,0xf0,0xc8,0x40,0x99,0x2b,0x53,0x6b,0x9b,
+0x6f,0x44,0x2e,0x85,0x82,0x95,0xf,0x51,0x45,0x21,0x3b,0x2c,0xea,0x57,0x35,0x3a,
+0xc8,0x46,0x4c,0xa6,0xc5,0x78,0x2f,0x7c,0xe0,0xfa,0x27,0x73,0x8,0x4,0x98,0xd2,
+0xd,0x4b,0x3,0xa3,0x7,0x12,0x32,0x6e,0x56,0xa1,0x48,0x8f,0x6a,0xd8,0xc8,0x4e,
+0xd1,0x4c,0x63,0x8e,0x9f,0xec,0x8b,0x84,0xc2,0x9c,0x81,0xed,0xa7,0x74,0x39,0xc,
+0xa6,0x43,0x53,0xa7,0xc9,0x17,0xe4,0x83,0xd6,0x31,0x94,0x82,0xb,0x6,0x9b,0x2,
+0xdc,0x49,0xa7,0xa3,0xcc,0x2d,0x34,0x81,0xa5,0x2,0x68,0xd3,0xa3,0xf4,0x83,0x1a,
+0xae,0x45,0x28,0xba,0xaf,0x1b,0xbf,0x4,0xcd,0x4e,0xc9,0x4b,0x8a,0x17,0x31,0x8a,
+0x4d,0x4c,0x52,0x83,0x1a,0x13,0x40,0xfd,0x3,0xb8,0x4c,0xe0,0x45,0xfc,0xf9,0xb5,
+0x91,0x48,0x70,0xab,0xf0,0xc1,0x4a,0x50,0x7d,0xc3,0xe9,0xad,0xc2,0xab,0xe,0xfb,
+0x36,0x41,0x6d,0xa1,0x36,0xf,0x7e,0x91,0x4b,0xa4,0xec,0x7e,0xa2,0x71,0xd7,0x94,
+0x63,0x4b,0x4d,0xa8,0x46,0x63,0x30,0xfa,0x53,0x72,0x75,0xa8,0x47,0x60,0x61,0xb,
+0x1f,0x47,0x60,0x9a,0x59,0x1d,0x7b,0x0,0x89,0x27,0xd0,0xc8,0x59,0x2,0x65,0xb4,
+0x57,0x4d,0x43,0xb8,0xe5,0x82,0xd9,0xd1,0xb9,0xf,0xeb,0x5e,0xa2,0x43,0x8d,0x54,
+0x7b,0x44,0xa4,0x89,0x5f,0x61,0xb2,0x38,0x3f,0x57,0x21,0xcd,0xbb,0xeb,0x78,0xca,
+0x12,0x4d,0x77,0x8d,0x67,0x99,0xcb,0x6d,0x11,0x10,0x54,0x92,0x37,0xfa,0x2c,0x99,
+0xbd,0x46,0x98,0xa8,0x4a,0xc6,0x21,0xcf,0x13,0xd5,0xac,0xed,0x94,0x88,0xf,0x9b,
+0x49,0x43,0x8d,0x80,0x70,0x49,0xec,0xa6,0x57,0xd1,0x78,0x9f,0x8f,0xae,0xee,0xef,
+0x4e,0x42,0xeb,0xa7,0x7b,0x20,0xb1,0x46,0x41,0xaa,0x2c,0x28,0x5e,0x90,0x5a,0xd3,
+0xb0,0x4a,0x55,0xb2,0x70,0xed,0x2d,0xa0,0x86,0xbf,0xe0,0x16,0x9e,0xd5,0x1f,0xc5,
+0xa0,0x40,0x6b,0xa5,0xe0,0xb9,0xc6,0x9e,0x96,0xfa,0xc6,0xdb,0xb8,0x36,0xbb,0xc9,
+0x43,0x42,0xf7,0xb4,0xa2,0x24,0xce,0x43,0x55,0xd6,0x10,0xa7,0x7d,0x4f,0x6c,0x50,
+0xe,0x47,0x12,0xa5,0xf9,0x69,0x53,0xf1,0xdc,0x88,0xf6,0x1b,0x7b,0xbe,0xef,0x54,
+0x5,0x4a,0xc4,0xa9,0x65,0x5d,0xf5,0x6b,0xa1,0x25,0x6c,0xef,0xb,0x5a,0xf0,0x63,
+0x1e,0x4a,0x36,0x95,0xac,0xb,0xc5,0x4b,0x9e,0x65,0x6c,0x51,0x68,0xce,0x65,0xfe,
+0xe9,0x46,0x62,0x86,0x52,0x44,0xe1,0x9f,0x52,0x55,0xde,0x31,0xcf,0x49,0xc8,0x17,
+0x83,0x4e,0x71,0xb7,0xc2,0x3e,0x4d,0xdf,0xbb,0x8c,0x70,0x77,0x3,0xc,0x10,0xa4,
+0xb6,0x49,0xcf,0x9c,0xd3,0x27,0x3a,0xe3,0x2,0xb1,0xf9,0xff,0x44,0xed,0x87,0x18,
+0xb8,0x4c,0xe3,0xb5,0x78,0x35,0x83,0xa8,0x56,0x83,0x46,0x55,0xe,0x9c,0x92,0xfb,
+0x48,0x47,0x17,0x9b,0xf1,0x64,0x52,0x45,0xbd,0x98,0x22,0xb8,0x8d,0xa5,0x6c,0x7c,
+0xe3,0x40,0x1a,0xb5,0x48,0xd9,0x70,0xa6,0x51,0xdf,0x73,0x55,0xde,0x23,0x3,0x94,
+0x8b,0x40,0x64,0xb3,0x68,0x66,0x16,0xf0,0xfb,0x61,0x1b,0x8e,0xb2,0xce,0x8d,0xba,
+0x46,0x42,0x4c,0xa4,0xe5,0x4f,0x46,0x67,0x43,0x33,0xa8,0x12,0x2a,0xc2,0xdc,0x6b,
+0x92,0x40,0xa9,0xbd,0x1e,0xe3,0x67,0x9f,0xbf,0x38,0x2,0x3c,0x70,0x54,0x18,0x28,
+0x97,0x43,0x9f,0x80,0x63,0xd9,0xe7,0x7d,0x42,0x58,0x4d,0xac,0x47,0x44,0x87,0xee,
+0x38,0x4f,0x3a,0x88,0xc1,0x51,0xd7,0xa2,0xb9,0x4d,0x1b,0x78,0xd5,0x95,0x36,0x2,
+0x61,0x4d,0x9,0xb8,0xfc,0xdb,0x28,0xc3,0xc9,0xf1,0xe5,0xfc,0xba,0xc3,0x0,0x1,
+0x42,0x42,0x54,0x94,0xab,0xbb,0xbe,0xa1,0xc1,0xc1,0x3f,0xa9,0x6e,0x2d,0x50,0x45,
+0x62,0x4f,0x28,0x89,0x69,0x3c,0xb8,0x7e,0xfe,0x3d,0x3c,0x3d,0x9f,0x38,0xcc,0x9d,
+0x5,0x4b,0xb0,0xa1,0xb6,0x12,0x25,0x8,0x6c,0xfe,0x2f,0x6b,0x9f,0x5e,0xff,0xdc,
+0x71,0x4e,0x66,0x93,0xf1,0xfd,0x3d,0x14,0xb6,0x4b,0xd2,0x4d,0xe2,0xd4,0x96,0xba,
+0x93,0x40,0xdd,0xaf,0x9d,0xf2,0x57,0x54,0x88,0x26,0x7,0xf4,0x13,0xbc,0xe0,0xf0,
+0xaf,0x4e,0xef,0x9e,0xf,0x89,0xe2,0x96,0x95,0xf3,0x96,0x3b,0x91,0x1e,0x15,0x40,
+0xc7,0x42,0xaa,0xb1,0x16,0xa6,0x40,0x76,0x71,0xcc,0xbc,0x4c,0x13,0xdb,0x1b,0xce,
+0x65,0x49,0xcc,0x88,0x99,0xf1,0xe3,0x4b,0xb4,0xda,0xeb,0xfc,0x8,0xbe,0xc1,0x40,
+0xb4,0x45,0x3a,0xa2,0x9,0x5c,0x9e,0x19,0x61,0x48,0xa6,0x9b,0x6f,0xf8,0xf9,0xff,
+0xa,0x46,0xe8,0x87,0xd1,0xa3,0x2d,0x31,0x33,0xfc,0xfc,0xd0,0x6b,0xea,0x32,0x94,
+0xee,0x47,0x8a,0xb8,0xf7,0x22,0x1d,0x39,0x4f,0x9d,0x52,0xd7,0x65,0x28,0x8d,0x8,
+0x5f,0x46,0xef,0xbc,0x9b,0xa,0x70,0x7d,0xe,0x4a,0x36,0x3c,0x3b,0x83,0xca,0x13,
+0x20,0x49,0x5a,0x91,0x8,0x39,0xb7,0x49,0x7f,0x9a,0x6e,0xec,0xd4,0xee,0xcc,0x7c,
+0x5e,0x4b,0x5f,0xa0,0xb9,0x5d,0x29,0x2f,0x1d,0xeb,0x17,0x14,0xf5,0x54,0x15,0xb2,
+0x3e,0x46,0xd8,0x89,0xbf,0x29,0x87,0x44,0x79,0x65,0xe6,0x16,0x5f,0x32,0x55,0x53,
+0xbe,0x4f,0xb1,0x93,0x7f,0xf0,0x38,0xfa,0xd9,0x15,0xc1,0x9d,0x31,0x49,0xb3,0xed,
+0xeb,0x43,0x9,0xa4,0xc9,0x77,0xaf,0x75,0x75,0x2d,0x46,0x41,0x70,0x2a,0x41,0x6a,
+0x10,0x43,0x5b,0x93,0xec,0x24,0x56,0xeb,0xbd,0x7c,0xa8,0x87,0x54,0x31,0x2d,0x4b,
+0xbb,0x43,0xfa,0xa9,0x5d,0x36,0x83,0x35,0xe4,0x17,0x81,0x4c,0x65,0x2f,0x63,0x42,
+0x1a,0x4c,0x15,0xa7,0x60,0x38,0x41,0x6a,0xdb,0x1,0x32,0x12,0x52,0x91,0x1d,0x7c,
+0xdc,0x44,0xb,0xb8,0x3e,0x17,0x45,0xbb,0x94,0xf6,0x5f,0x53,0xbb,0x88,0xfb,0xe9,
+0xbe,0x48,0x4e,0xb4,0xcb,0x41,0x87,0x60,0xfa,0xea,0xb8,0x66,0xfb,0x51,0xa7,0xd8,
+0x4,0x4b,0x65,0x9c,0x53,0xd7,0xf0,0x5c,0x11,0x8f,0x48,0xbc,0x69,0xa6,0xf8,0x5e,
+0xc1,0x42,0x72,0xa6,0x6,0xeb,0x3d,0x9a,0x6f,0x6c,0x60,0xdf,0x21,0x73,0xb3,0x4,
+0x32,0x4d,0x28,0xbc,0x75,0x8c,0x27,0xb6,0x6a,0x92,0x11,0x95,0xf5,0x2d,0x7a,0x2b,
+0xee,0x4a,0x4b,0xab,0x21,0x41,0x6f,0x8c,0x6d,0xfb,0x5b,0x98,0x58,0x7b,0x7a,0x52,
+0x29,0x4b,0x0,0x89,0xd7,0x4d,0x39,0xd7,0xd5,0x31,0xe9,0x64,0xc6,0x89,0xa2,0xe0,
+0x59,0x49,0x5f,0x9b,0x6c,0xc,0x87,0x4d,0xc1,0x43,0xfe,0x4c,0x1d,0x6a,0x1d,0x9e,
+0x2e,0x43,0x75,0x93,0x14,0xbc,0x31,0xe7,0xc3,0xb3,0xe0,0x50,0xed,0x9e,0xa2,0x47,
+0x8d,0x4a,0xc6,0xa3,0x81,0xa6,0x5e,0xca,0x84,0x94,0xc,0x2c,0xcf,0x14,0xc9,0xe9,
+0x76,0x42,0x2d,0xb2,0xc,0xf0,0x1d,0x57,0x25,0xb9,0xa6,0x24,0xd0,0xf1,0x85,0x57,
+0x80,0x44,0x45,0xac,0x62,0x7d,0xe3,0xce,0x61,0x97,0xa4,0x68,0xcb,0xec,0x4d,0x3a,
+0x1b,0x43,0xbe,0xaf,0x79,0x29,0x2f,0xc4,0xa7,0xfe,0x34,0xa3,0x88,0xe3,0xf2,0x95,
+0x1,0x40,0xab,0xa4,0x16,0xa8,0xd3,0x73,0xbd,0xd1,0x70,0xaf,0x94,0x7b,0x42,0x5f,
+0xf1,0x40,0x67,0x9a,0x5,0x6,0x12,0xcd,0x51,0x72,0x4,0xa4,0xe9,0x99,0xf4,0x83,
+0xf4,0x4e,0x7f,0x8f,0x9d,0x83,0x39,0x4d,0x87,0xf5,0xd8,0x5,0x10,0xaf,0x83,0x77,
+0x7d,0x49,0xd7,0x92,0xea,0x43,0x38,0x65,0x6a,0xc,0x7c,0x2,0x40,0xc3,0xa5,0x8a,
+0x62,0x48,0x9f,0x9b,0x91,0xb2,0x72,0xc4,0xac,0xb7,0xf9,0xbe,0x13,0x1c,0xe5,0x65,
+0xea,0x41,0x72,0x9d,0xda,0x31,0x7,0x5f,0x35,0xf8,0x6f,0x27,0x7c,0xda,0x4b,0x36,
+0x9e,0x44,0x2f,0xb2,0x65,0x68,0x9c,0x9e,0xca,0xab,0xa9,0xe9,0xcc,0xb7,0xb8,0x97,
+0x7,0x47,0x29,0x99,0x73,0xa6,0xcb,0xf6,0xc9,0xf0,0x7e,0xf8,0x3b,0x45,0x6,0x82,
+0x14,0x48,0x9c,0x98,0xb4,0x53,0xe6,0x8e,0x78,0x59,0x84,0x2e,0x49,0xf,0x7f,0x6,
+0x48,0x46,0x97,0x9a,0x55,0xae,0x9e,0x3a,0xc4,0x5f,0x40,0xba,0xca,0x54,0x2,0x3c,
+0x9,0x49,0x38,0xb5,0xd7,0x18,0x47,0xfb,0xb7,0x46,0x41,0x56,0x60,0xb0,0xf1,0xcc,
+0x56,0x49,0xc4,0xb8,0xe8,0xe2,0x1f,0xd0,0xac,0xdc,0xb7,0xcc,0x51,0xa3,0xb1,0xe4,
+0x83,0x4f,0xc1,0x8c,0x9,0x33,0xfc,0x33,0x4c,0xaf,0x51,0x96,0x89,0xf7,0x59,0x1f,
+0xce,0x40,0x55,0xa1,0xb5,0xcf,0xe4,0xe1,0xd9,0xad,0x57,0x5c,0x46,0x67,0xf9,0xea,
+0x6e,0x42,0xa3,0x8e,0x4d,0x49,0x5b,0xf5,0x8b,0x17,0xd2,0xa4,0x9b,0x52,0x35,0xda,
+0x88,0x41,0x56,0x96,0xc,0x74,0x3a,0xce,0x92,0x36,0x27,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa2,0x94,0x55,0x47,0x4,
+0xdb,0x4b,0xb3,0xb0,0xe5,0xd4,0x56,0x1c,0xf2,0xb6,0x15,0x46,0xad,0x45,0xcf,0xf4,
+0x76,0x48,0x68,0xb2,0x18,0x29,0x99,0x67,0x8a,0x5,0xd4,0xcf,0x55,0x45,0xf7,0x1c,
+0x7,0x46,0x3c,0x92,0x17,0x7,0x36,0x16,0xe0,0xda,0x30,0x82,0x32,0x3c,0x95,0x32,
+0xbe,0x48,0x2b,0xbb,0x10,0x5e,0xd5,0x17,0x2b,0x12,0xb2,0x3,0xfa,0x9a,0x47,0x8b,
+0xeb,0x4a,0xb4,0x8e,0xed,0x76,0x2a,0x59,0x82,0xc4,0xf1,0xcc,0xfd,0x74,0x5,0x91,
+0xb9,0x43,0xa,0x94,0x20,0x73,0x3,0xab,0xfa,0xe6,0x9d,0xdd,0xeb,0x62,0xb1,0xe6,
+0xf6,0x46,0xb8,0xae,0x1c,0x93,0x79,0xfd,0x9b,0xb7,0xbc,0xc6,0xbb,0xb2,0x1a,0x69,
+0x81,0x48,0xd9,0x86,0xc9,0xf3,0xbb,0xfa,0xb2,0xbd,0xf6,0xad,0xa3,0xab,0x14,0x17,
+0xf5,0x43,0xb3,0xb4,0x8b,0x38,0x96,0x9c,0xf1,0x26,0xbd,0x41,0x2f,0x28,0x92,0xb,
+0xec,0x4f,0x9d,0x89,0x81,0xee,0x9a,0x3c,0x28,0xa7,0xf4,0x3,0xad,0x57,0x4a,0xd,
+0x68,0x40,0x2,0xb5,0x2f,0x6e,0x41,0xb0,0xbb,0x4a,0xca,0x7c,0xef,0x2,0x9c,0xf4,
+0x35,0x43,0xf1,0xb5,0x3f,0xae,0xad,0x9e,0xf4,0xa,0xb2,0x80,0x30,0xe1,0xdc,0x73,
+0x64,0x49,0xc6,0x84,0xdd,0x1f,0x37,0x79,0x90,0xf2,0x9a,0x83,0x3e,0xb8,0x75,0x12,
+0xbe,0x49,0x16,0x93,0xc4,0x36,0x53,0x82,0x9e,0x14,0xa4,0xe7,0x3,0x6d,0xdb,0xde,
+0xce,0x40,0x45,0x91,0x1b,0x2c,0x83,0xbd,0x2,0x47,0x73,0x4b,0xf8,0xab,0xc6,0x6c,
+0xcd,0x4a,0x8b,0xb5,0xa6,0x74,0x2a,0xc0,0x76,0xca,0x4c,0xa7,0xbf,0x31,0xf9,0x45,
+0x57,0x4f,0xb2,0x8f,0x9a,0xcd,0x9e,0x14,0x42,0xc,0xdd,0x7,0x1,0xde,0xa,0x40,
+0x1c,0x41,0x9c,0xa8,0x43,0x2e,0xbf,0xb7,0x6e,0xfc,0x2b,0x1,0x39,0xe5,0xcd,0x60,
+0x41,0x4e,0x63,0xa9,0x24,0x7,0x10,0x87,0x73,0x2,0xfc,0xa2,0xeb,0xb0,0x19,0xbb,
+0x37,0x45,0xd1,0x85,0x4b,0x4d,0x86,0xe3,0x16,0xd1,0x4e,0xc5,0x48,0x14,0xd1,0x9b,
+0xab,0x41,0x8d,0xab,0xf2,0xf,0x24,0x56,0x0,0x31,0x1c,0xa2,0x12,0xc,0x76,0xbe,
+0x5f,0x4c,0xb0,0x9b,0x62,0xb3,0x7f,0xea,0x8f,0xba,0x43,0x3b,0x45,0x17,0x25,0xc7,
+0xcf,0x4b,0xf6,0xa8,0xf3,0xbe,0x8a,0xa7,0xa3,0x4e,0x40,0x9f,0xa6,0xcf,0x2a,0xc3,
+0x6b,0x46,0x48,0xb5,0xff,0x3,0x7f,0xd9,0x5d,0xef,0xe,0xf3,0xeb,0x12,0x6a,0x65,
+0x26,0x4b,0xc0,0xb0,0xdc,0xd8,0xf6,0x75,0xa7,0x84,0x98,0x37,0x1,0x51,0xe,0x7f,
+0x12,0x4a,0x1a,0x8d,0xb6,0x2a,0x9f,0x63,0xf3,0x65,0xf1,0x38,0x52,0x9f,0xf0,0xed,
+0x92,0x43,0xfb,0xb4,0xbc,0x60,0xeb,0x88,0x1d,0x12,0x54,0xe,0xa6,0x9f,0x11,0x8b,
+0x1b,0x4d,0x6,0xb7,0xf5,0xd4,0x12,0xfe,0xda,0x2f,0xa8,0xa6,0x2,0x51,0x1f,0x3e,
+0xd7,0x4c,0xf,0xa6,0x99,0xbc,0x54,0xd7,0x4d,0x6b,0x8b,0xd6,0xec,0xe3,0x83,0xd7,
+0x87,0x4d,0x5e,0x9a,0x2,0xcf,0x30,0x78,0x28,0x88,0x6f,0x40,0xaf,0xd3,0xef,0xa0,
+0xa9,0x4f,0x6,0xb5,0x85,0x96,0x9a,0x4f,0x12,0xff,0x42,0x3e,0x88,0x80,0x38,0x2,
+0xd0,0x48,0xd8,0xb5,0x2e,0x3e,0x42,0x9d,0x73,0x77,0xc3,0xef,0x1d,0xf7,0xb7,0x2,
+0x48,0x4f,0x8c,0xbc,0x71,0xf9,0x33,0x8d,0x96,0xe6,0xc4,0x7f,0x46,0x4c,0x4c,0x26,
+0xab,0x46,0xc4,0xb6,0x11,0x6b,0x63,0xb2,0xa0,0xe6,0x52,0xcc,0xa3,0x24,0xc3,0xff,
+0x2d,0x4d,0x73,0xb0,0x4b,0x3a,0xf8,0x5f,0xd3,0xc5,0xd8,0x50,0x64,0xf2,0x9e,0x31,
+0x37,0x4e,0xfd,0xb1,0x95,0x9f,0xe,0xdc,0xfe,0xf0,0x71,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcb,0x7,0xbd,0xb1,0xb3,
+0x9b,0x4e,0x5b,0x8e,0xbf,0x1,0x98,0x80,0x54,0xd,0x66,0x4b,0xe,0x45,0xa2,0xe,
+0x42,0x49,0x32,0x83,0x92,0x8b,0x28,0x10,0x5,0x9d,0xa1,0xe8,0x4d,0x34,0x6f,0xdf,
+0x50,0x4d,0xb4,0xad,0xe1,0x93,0x53,0x74,0xb7,0xd6,0x4b,0xca,0x74,0x61,0xe,0x93,
+0x7,0x48,0x3a,0x87,0x6d,0xb7,0xd8,0xb2,0x97,0x2e,0x38,0xf0,0x3d,0x1b,0x23,0x90,
+0x50,0x45,0x6,0x84,0x7e,0x78,0x66,0xf,0xca,0xbe,0xf0,0x41,0x26,0x81,0xee,0x10,
+0x49,0x47,0x78,0x81,0x22,0x1e,0xa9,0x1,0x30,0x31,0x98,0xfd,0xeb,0xa,0x34,0x9b,
+0xa3,0x48,0xdd,0x9c,0x7f,0xd8,0x1d,0x46,0x73,0x8e,0xba,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xba,0x58,0x43,0x64,0xa2,
+0x4c,0x43,0x65,0x9c,0x9,0x1f,0x65,0xf4,0xb7,0x4b,0x73,0x6f,0xc1,0xb9,0xbf,0xe4,
+0x3,0x4c,0x95,0xa2,0xdc,0x5b,0xa8,0x2,0xb7,0xf0,0xa6,0x46,0x1a,0x8a,0xb6,0xd8,
+0x30,0x4f,0x42,0xa8,0xfd,0xd3,0x31,0x41,0x9c,0x5d,0x10,0x48,0x61,0xcd,0xde,0x25,
+0x9c,0x4d,0xd9,0xb7,0xaa,0x52,0x83,0x32,0xd4,0xb1,0x8a,0x2f,0x88,0xaf,0xe3,0xdb,
+0xd8,0x43,0xa,0x9b,0x31,0xe2,0xcc,0x8f,0xe0,0x39,0x6a,0x52,0xf4,0xb,0x78,0x48,
+0xa4,0x4d,0x5f,0x9a,0x82,0x22,0x93,0xd7,0x19,0xfe,0xca,0xf,0xf0,0x3a,0x9,0x1b,
+0x40,0x4e,0x40,0xb7,0xf3,0x56,0x5b,0x39,0x73,0xe7,0x98,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24,0xd2,0x46,0x6c,0xe9,
+0xd7,0x4f,0x2b,0x87,0xd1,0x6d,0x3a,0xcb,0xd6,0xa0,0x7f,0x9f,0xed,0xd4,0x7d,0x86,
+0xe5,0x46,0x79,0xa3,0x96,0x67,0x5a,0xad,0x92,0x78,0x50,0xfe,0x64,0xb1,0xac,0x18,
+0x99,0x4a,0xf8,0x8f,0x3f,0x8e,0x73,0xfb,0x9d,0x34,0x5b,0xd3,0xa2,0x96,0xbe,0xbd,
+0xe,0x44,0x36,0xb9,0x12,0x4d,0x16,0xef,0x4,0x5,0xb8,0x4c,0x78,0xd8,0x18,0x3d,
+0x32,0x48,0x5a,0x97,0x76,0xab,0x4c,0x14,0x84,0xa,0xfd,0xa6,0xed,0x2b,0xa3,0x24,
+0x76,0x43,0x2b,0xbf,0xb6,0x14,0x7f,0x7b,0x4a,0xca,0xd0,0x26,0x11,0xa,0x68,0xa9,
+0xb7,0x45,0x50,0x92,0xb8,0x9f,0xf,0xa5,0x7a,0x5b,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0xc9,0xdb,0xf0,0xe4,
+0x3,0x45,0x2f,0x9c,0x18,0x89,0xc9,0x11,0x82,0x5a,0xe,0xe1,0x70,0xb4,0xe8,0x6a,
+0x61,0x4d,0xa1,0x9d,0xba,0xf9,0xed,0xc5,0xe1,0xeb,0x99,0xa3,0xa6,0x65,0xeb,0x5a,
+0xe6,0x4d,0xae,0x80,0x54,0xe7,0xa7,0xda,0xb6,0xba,0xc0,0x22,0xc7,0x8c,0x15,0x58,
+0x92,0x4f,0xe4,0x9c,0x41,0xec,0x96,0x1f,0x12,0x54,0x1f,0xc6,0x58,0xa3,0xa8,0x24,
+0x2,0x40,0x14,0x8b,0x88,0xc2,0x29,0xbe,0x2d,0xc3,0x36,0xef,0x41,0xee,0x10,0x6f,
+0xc5,0x4c,0xd0,0x96,0xf6,0xaf,0x8,0x18,0x5,0x56,0x41,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0xf9,0x25,0x86,0xd3,
+0xd3,0x4e,0x99,0xa9,0xc2,0x4a,0x96,0x74,0xcf,0x96,0xd6,0x34,0x55,0x2c,0x2b,0xc5,
+0xc8,0x44,0xca,0xa7,0xa8,0xf2,0xeb,0xa5,0x67,0x34,0x5e,0x9a,0x9,0xd4,0x1b,0x3e,
+0x98,0x43,0x82,0x81,0xf3,0xe4,0x5b,0x7e,0x3a,0xe8,0x8f,0x4,0x89,0xd1,0x68,0x0,
+0xac,0x46,0x15,0x8b,0x3a,0x6a,0xbf,0x5d,0x52,0x73,0x8b,0xd2,0x96,0xed,0x61,0x6d,
+0xbf,0x4d,0x60,0x81,0x8c,0x36,0x60,0xe5,0xa4,0xc7,0xbd,0x5c,0xbd,0xf2,0x4a,0xe5,
+0xf,0x48,0x93,0x8e,0xfc,0x64,0xfe,0x7e,0x9b,0xa0,0xf3,0xe2,0x10,0xe3,0x5a,0x25,
+0xa4,0x40,0xa5,0x8f,0x64,0x73,0xb6,0x52,0xfe,0x46,0xbd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc,0xa6,0xa2,0x19,0x12,
+0x10,0x4a,0x8e,0xb4,0x7f,0xc2,0x25,0xec,0x32,0x1f,0x61,0x61,0xc5,0xe0,0xc5,0x4f,
+0x5b,0x49,0xb4,0x97,0x2b,0x5,0x6e,0x7,0xd9,0xcb,0xc1,0x35,0x23,0x3a,0x82,0x79,
+0x61,0x42,0xe5,0xae,0x4f,0xe1,0x4,0xf1,0x1e,0xb2,0x60,0xd1,0x6,0x6d,0x70,0xa1,
+0x23,0x46,0x9e,0xa4,0xe6,0xcf,0x68,0x6,0x9f,0xa0,0x26,0xf3,0xaf,0x3b,0x80,0xda,
+0x50,0x4d,0x3b,0x89,0x68,0xa4,0x97,0x6c,0x3,0x6b,0xec,0xd3,0x41,0xa6,0x58,0x3a,
+0x68,0x41,0x12,0xa7,0xc4,0x52,0xe1,0xdd,0xea,0xf3,0x2a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0xfa,0xfc,0x9a,0xec,
+0xa0,0x4e,0x38,0xb4,0xd2,0x9a,0xf6,0x9,0x3a,0x6e,0xdd,0xa4,0xcc,0xf0,0x85,0x37,
+0x80,0x43,0xd9,0x80,0x6f,0xbb,0x1e,0x35,0x72,0xc6,0xe8,0x58,0x84,0xae,0xa8,0x8,
+0xe1,0x4c,0x63,0xac,0xca,0x8f,0x38,0x7e,0x2f,0x6,0x44,0xd5,0xfc,0xe3,0x14,0x77,
+0x7c,0x47,0x53,0x86,0x9e,0x87,0xb0,0xec,0x3,0xc4,0xde,0xa4,0xc3,0xae,0x17,0xb6,
+0x70,0x47,0xbf,0xb2,0x3b,0x38,0x29,0x18,0xc6,0xce,0xcd,0xab,0x15,0x4e,0xb,0x84,
+0xd0,0x4e,0x53,0x9e,0x44,0xc9,0xee,0x34,0x52,0xe3,0xa7,0x9b,0x3d,0x82,0x76,0xf6,
+0x19,0x44,0x14,0xb7,0x8b,0x71,0xd3,0xba,0xee,0xcf,0x31,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xec,0xb,0xb6,0x30,0xea,
+0xc7,0x4d,0xc5,0x9a,0xb8,0xa6,0xf6,0xdc,0xe4,0xe,0xa0,0xa8,0xa1,0x6,0x1a,0x2f,
+0x61,0x4d,0x61,0xaf,0x22,0x5e,0xab,0x98,0x37,0xf,0xdb,0x3e,0xc2,0xc6,0xba,0x1c,
+0x43,0x44,0x52,0x9b,0xfc,0x62,0xbf,0x3c,0xe5,0xbc,0x74,0x1,0x2d,0x32,0xec,0xc2,
+0xf4,0x4d,0xa1,0xb0,0xbe,0x3,0xbe,0x38,0x34,0x3c,0x51,0x73,0xba,0xde,0x2f,0x7a,
+0x43,0x4e,0x21,0x98,0x38,0x88,0x36,0x8b,0x62,0xe3,0xde,0x3b,0x80,0xa,0x97,0xb5,
+0x1b,0x4b,0x18,0xb2,0x28,0xb3,0x4c,0x18,0x83,0xec,0xa0,0x18,0x50,0x30,0xcb,0x2a,
+0x52,0x49,0xed,0xa5,0x7c,0x6a,0xaf,0xe,0x85,0xfd,0x7d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4c,0xa6,0x2e,0x40,0x9c,
+0xbd,0x42,0x4,0xa8,0x13,0x8f,0x54,0x6e,0x2,0xee,0x8a,0x46,0xa,0x3d,0x4a,0xfc,
+0x7b,0x4a,0xbb,0xab,0xf8,0x14,0xe7,0xed,0x18,0x70,0x98,0xf4,0x2a,0xba,0x34,0xfc,
+0x5e,0x4c,0xc8,0x8f,0xfd,0xee,0xba,0x60,0x98,0x9,0x4d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2e,0xaa,0xdf,0x6c,0xc4,
+0xe5,0x4d,0x68,0xa9,0x87,0xbc,0xf9,0x47,0xcf,0xc8,0x4d,0x64,0xa3,0x94,0x3b,0x66,
+0x9c,0x48,0x29,0x8a,0x56,0xc9,0xb4,0xa1,0x7a,0xbe,0xa6,0x96,0xd4,0x8c,0xe2,0x5e,
+0x17,0x47,0xd9,0xa3,0x6d,0x99,0xc8,0x7c,0x90,0x57,0x88,0x8e,0xfa,0x69,0xf0,0xad,
+0x17,0x46,0xe,0xbe,0xb8,0xac,0xd1,0x66,0x5c,0x3a,0x66,0x1,0xdd,0x21,0x66,0xb3,
+0x53,0x46,0xab,0xb1,0x11,0x75,0x58,0xde,0x57,0xbc,0xe3,0x4a,0xcd,0x54,0x80,0x9a,
+0xde,0x45,0x9,0xab,0x88,0x6a,0xce,0xe9,0x57,0xc3,0xd4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xea,0xa,0x45,0x14,0x3,
+0xe3,0x41,0x8c,0xab,0x78,0xf8,0x30,0xd1,0x92,0xb,0xce,0xd9,0x37,0x7c,0x36,0x86,
+0x67,0x4f,0x60,0x93,0x60,0x37,0x8f,0x84,0xe0,0x9f,0xa8,0x1,0x9d,0x49,0xcf,0x39,
+0x54,0x4d,0x78,0xac,0x3f,0x4f,0xb6,0xfd,0x2,0xc0,0x40,0x3a,0x4,0xef,0xe0,0x36,
+0x79,0x4d,0xac,0xad,0xf6,0x83,0x7d,0x5e,0xdc,0xb2,0x3d,0xc4,0x5,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xcb,0x42,0x29,0x5e,0x73,0x3f,0x8a,0x25,0x0,0x3e,0x9e,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4a,0x43,0x29,0x5e,0x88,0xa8,0xbb,0x3a,0x0,
+0xd0,0x8f,0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,
+0x1b,0x0,0x1d,0x8c,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x21,0x43,0x29,0x5e,0xd7,
+0x2e,0x60,0x5,0x0,0x8,0x88,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9d,0x42,0x29,
+0x5e,0xa7,0x8b,0xd6,0x9,0x0,0x88,0xa0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1b,
+0x43,0x29,0x5e,0x3c,0xe7,0xae,0x35,0x0,0xdc,0xba,0xbe,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,0x8f,0x22,0xe9,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0x36,0x8c,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,0x15,0x0,0x50,0x74,
+0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,
+0x8e,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x30,0x44,0x29,0x5e,0x52,0x46,0x5d,
+0x31,0x80,0xed,0xa7,0x75,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x42,0x29,0x5e,0x6,
+0x8f,0xa9,0x14,0x0,0x84,0xac,0xa2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,0x42,0x29,
+0x5e,0x10,0xab,0x9f,0x1c,0x0,0x6d,0xb3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x14,
+0x43,0x29,0x5e,0x94,0x3e,0xef,0x22,0x0,0xbe,0x82,0xe8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x27,0x43,0x29,0x5e,0x18,0x44,0x4c,0x32,0x0,0x34,0x12,0xf0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xac,0x42,0x29,0x5e,0x7,0xe0,0x11,0x25,0x0,0xa,0xb8,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x79,0x42,0x29,0x5e,0x7d,0x4c,0x53,0x16,0x0,0xea,0x60,
+0xd1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8d,0x42,0x29,0x5e,0xee,0x8,0xdd,0x2c,0x0,
+0x79,0xa1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,0x27,0x94,
+0xc,0x0,0x5b,0x9c,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,
+0x3,0x14,0x3b,0x0,0xf1,0x25,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,
+0x5e,0xcc,0x2c,0xc8,0x12,0x0,0xf1,0xaf,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,
+0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x79,0x8a,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0xfa,0x97,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x3,0x43,0x29,0x5e,0x6f,0x5f,0xb6,0x37,0x0,0xa4,0x23,0xf0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xbe,0x42,0x29,0x5e,0xe3,0x67,0x9f,0x1e,0x0,0x94,0x7c,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa,0x42,0x29,0x5e,0xb5,0x58,0x4b,0x27,0x0,
+0xb5,0xf0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,
+0x15,0x0,0xf,0x0,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc,0x43,0x29,0x5e,0x78,
+0xab,0x5b,0x23,0x0,0xc9,0xf0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0x52,0x29,
+0x5e,0xe9,0xdb,0x2,0x3,0x0,0xf1,0x28,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,
+0x42,0x29,0x5e,0x7,0xe0,0x11,0x25,0x0,0x35,0x26,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe1,0x42,0x29,0x5e,0x83,0xa6,0x29,0x15,0x0,0x71,0x1b,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x5f,0x42,0x29,0x5e,0x2a,0xb5,0xc9,0x25,0x0,0xaa,0x9a,0xd1,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xdb,0x42,0x29,0x5e,0xe8,0x1e,0xdb,0x3,0x0,0xb8,0x1f,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,
+0x61,0x2,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x43,0x29,0x5e,0xda,0x6a,0xb2,
+0x11,0x0,0x6f,0xe,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x60,0x43,0x29,0x5e,0xd3,
+0xd7,0xbc,0x4,0x0,0x79,0x14,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe1,0x42,0x29,
+0x5e,0x83,0xa6,0x29,0x15,0x0,0x21,0xf9,0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,
+0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x87,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x9c,0x42,0x29,0x5e,0x8f,0xe0,0x38,0x26,0x0,0xa4,0x25,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0x50,0xb6,0x15,0x21,0x0,0xc0,0xc0,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x33,0x1e,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x43,0x29,0x5e,0xc0,0xaa,0x1,0x7,0x0,
+0x46,0x33,0xbd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,
+0x8,0x0,0x11,0x2f,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0x42,0x29,0x5e,0xb,
+0xaf,0x1e,0x2c,0x0,0xe1,0xf2,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,
+0x5e,0xde,0x78,0xde,0x32,0x0,0xab,0xf4,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,
+0x42,0x29,0x5e,0x26,0x44,0x4c,0x10,0x0,0x32,0xb,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0xff,0xb0,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf8,0x41,0x29,0x5e,0xa5,0x83,0xf2,0x34,0x0,0x88,0xa6,0xdf,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,0x65,0x15,
+0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x59,0x43,0x29,0x5e,0xae,0x59,0x1d,0x11,0x0,
+0xf6,0x68,0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,0x27,0x94,
+0xc,0x0,0x21,0xde,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x92,0x42,0x29,0x5e,0x13,
+0xb7,0x6a,0x36,0x0,0x44,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf0,0x42,0x29,
+0x5e,0x2,0xdf,0xdb,0x29,0x0,0x86,0xfd,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,
+0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0x69,0x67,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0x85,0x2a,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x55,0x43,0x29,0x5e,0xb7,0x88,0x56,0x33,0x0,0xe0,0x92,0xdf,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0xf4,0x35,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x55,0x43,0x29,0x5e,0xb7,0x88,0x56,0x33,0x0,
+0xc2,0x72,0xdf,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,
+0x15,0x0,0x4f,0x44,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,0x42,0x29,0x5e,0x9c,
+0x70,0x12,0x39,0x0,0x46,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,
+0x5e,0xae,0x6e,0x8a,0x2e,0x0,0xbd,0xc,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1b,
+0x0,0x29,0x5e,0x67,0xc7,0xfe,0x29,0x0,0x44,0x1a,0xa5,0x5,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x92,0x42,0x29,0x5e,0x13,0xb7,0x6a,0x36,0x0,0x77,0x26,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0x44,0x5e,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x3d,0x42,0x29,0x5e,0xa,0xfb,0xa7,0x39,0x0,0x52,0xce,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x59,0x43,0x29,0x5e,0xae,0x59,0x1d,0x11,0x0,
+0xce,0xf,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x87,0x4c,0x4e,0x5e,0xe6,0xbb,0x2f,
+0x32,0x0,0x5f,0x7f,0xc2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,
+0x2c,0xc8,0x12,0x0,0xff,0xf1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,
+0x5e,0x50,0xb6,0x15,0x21,0x0,0x6d,0x4f,0xde,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,
+0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,0xd6,0x10,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xff,0x41,0x29,0x5e,0xa0,0xc3,0x37,0xb,0x0,0x9d,0x8a,0xb1,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0xc4,0x5,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x2e,0x43,0x29,0x5e,0xee,0x24,0xbc,0x8,0x0,0xf9,0x72,
+0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8d,0x42,0x29,0x5e,0xee,0x8,0xdd,0x2c,0x0,
+0xf3,0x20,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,
+0x1b,0x0,0x8c,0x26,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,
+0x6e,0x8a,0x2e,0x0,0xbd,0xc,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,
+0x5e,0xe0,0xc1,0x39,0x32,0x0,0xf0,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,
+0xa2,0x2a,0x5e,0xbe,0x8c,0xc6,0xc,0x0,0x47,0xd9,0xf4,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6c,0xff,0x28,0x5e,0x92,0xb1,0x45,0x30,0x0,0x44,0x1a,0xa5,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x55,0x43,0x29,0x5e,0xb7,0x88,0x56,0x33,0x0,0x38,0x22,0xf0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0x66,0x1c,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x42,0x29,0x5e,0x88,0xd8,0x6,0x9,0x0,
+0x1,0xd7,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfd,0x42,0x29,0x5e,0x13,0x8d,0x8f,
+0xe,0x0,0x84,0x66,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa2,0x42,0x29,0x5e,0xb4,
+0x9b,0x65,0x12,0x0,0xc0,0xdc,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,
+0x5e,0x50,0xb6,0x15,0x21,0x0,0x5b,0xbb,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb4,
+0xff,0x28,0x5e,0xfd,0x5a,0x32,0x38,0x0,0x44,0x1a,0xa5,0x7,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x42,0x42,0x29,0x5e,0xc6,0x4b,0x35,0x0,0x0,0x16,0xf,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x51,0x48,0x29,0x5e,0xa4,0xb9,0xbc,0xe,0x80,0x65,0x9b,0x75,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x5b,0x43,0x29,0x5e,0x75,0xd1,0x5,0x0,0x0,0x37,0x9,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7c,0x42,0x29,0x5e,0xf4,0x92,0x89,0x38,0x0,
+0x94,0x5d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa5,0x42,0x29,0x5e,0x79,0xdd,0xc6,
+0x2b,0x0,0x75,0x1e,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,
+0x6e,0x8a,0x2e,0x0,0xf6,0x29,0xf0,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,0x42,0x29,
+0x5e,0xb2,0x95,0x22,0x6,0x0,0xce,0xf,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x40,
+0x42,0x29,0x5e,0x2a,0x4d,0xaf,0x33,0x0,0xcd,0x25,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x7,0x0,0x29,0x5e,0x21,0x20,0x11,0xb,0x0,0x44,0x1a,0xa5,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xc7,0x76,0x73,0x7,0x0,0xc0,0xbc,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x5a,0x42,0x29,0x5e,0x86,0x68,0xf,0x1c,0x0,0xdd,0x98,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,
+0x83,0x5d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,0x42,0x29,0x5e,0x26,0x44,0x4c,
+0x10,0x0,0x1,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,
+0x3,0x14,0x3b,0x0,0x4e,0x13,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,
+0x5e,0x6c,0x31,0x84,0xc,0x0,0x59,0xb7,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,
+0x43,0x29,0x5e,0xda,0x6a,0xb2,0x11,0x0,0x2c,0xf6,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x46,0x42,0x29,0x5e,0x2c,0xb3,0xda,0x1,0x0,0x95,0x24,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x58,0x42,0x29,0x5e,0xa1,0x76,0x6c,0x2c,0x0,0xe0,0xd1,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4,0x57,0x29,0x5e,0x3d,0xf9,0x1c,0x10,0x0,0xff,0x6f,
+0xc2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,
+0x7e,0x75,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,0xd,0x6b,
+0x2e,0x0,0x9d,0x1a,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8d,0x42,0x29,0x5e,0xee,
+0x8,0xdd,0x2c,0x0,0x6c,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8d,0x42,0x29,
+0x5e,0xee,0x8,0xdd,0x2c,0x0,0x73,0x22,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,
+0x42,0x29,0x5e,0xb2,0x95,0x22,0x6,0x0,0x48,0x5e,0xe9,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x13,0x43,0x29,0x5e,0xd6,0xae,0x40,0x1a,0x0,0x4f,0xb3,0xa4,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x92,0x42,0x29,0x5e,0x13,0xb7,0x6a,0x36,0x0,0x29,0x1c,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x3b,0x42,0x29,0x5e,0xb,0xaf,0x1e,0x2c,0x0,0xaf,0xde,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5f,0x42,0x29,0x5e,0x2a,0xb5,0xc9,0x25,0x0,
+0x37,0x10,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x42,0x29,0x5e,0x53,0xe8,0x59,
+0x25,0x0,0x35,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0x50,
+0xb6,0x15,0x21,0x0,0x8b,0x28,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,
+0x5e,0x41,0xd,0x6b,0x2e,0x0,0x30,0x12,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb7,
+0x42,0x29,0x5e,0xdb,0x4e,0xfc,0x7,0x0,0x40,0x22,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,0x15,0x0,0x8e,0x25,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x33,0x43,0x29,0x5e,0x1f,0x8a,0x61,0xd,0x0,0x25,0x27,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,0xd,0x6b,0x2e,0x0,0x74,0x2d,
+0xa9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdb,0x42,0x29,0x5e,0xe8,0x1e,0xdb,0x3,0x0,
+0x12,0x12,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,0x43,0x29,0x5e,0x18,0x44,0x4c,
+0x32,0x0,0x36,0x75,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,
+0x27,0x94,0xc,0x0,0x13,0x2a,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,
+0x5e,0xe,0xc5,0x8d,0x29,0x0,0x9b,0x6d,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x53,
+0x0,0x29,0x5e,0xc6,0xb3,0x70,0x3a,0x0,0x66,0x2a,0xa6,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0x81,0x18,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x63,0x0,0x29,0x5e,0xa,0xc8,0x89,0x14,0x0,0x44,0x1a,0xa5,0x3,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xeb,0x42,0x29,0x5e,0x35,0x75,0x60,0x2d,0x0,0x7e,0x6a,
+0xd1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd7,0x42,0x29,0x5e,0xad,0x63,0xd5,0x3,0x0,
+0x54,0x28,0xa9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x42,0x29,0x5e,0xfb,0x68,0xd7,
+0x1f,0x0,0x89,0xe,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x76,0x42,0x29,0x5e,0x2d,
+0xe0,0x26,0x1e,0x0,0x1,0x23,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,
+0x5e,0xe0,0xc1,0x39,0x32,0x0,0x3c,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x55,
+0x42,0x29,0x5e,0x7b,0x3e,0xa8,0x15,0x0,0x14,0x1f,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb7,0x42,0x29,0x5e,0xdb,0x4e,0xfc,0x7,0x0,0xcc,0x27,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd7,0x42,0x29,0x5e,0xad,0x63,0xd5,0x3,0x0,0x2a,0x1c,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x98,0x30,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc0,0x42,0x29,0x5e,0xf,0xc3,0x22,0x2f,0x0,
+0x96,0x8,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1d,0x43,0x29,0x5e,0x1,0x96,0xcf,
+0x24,0x0,0x34,0x71,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x42,0x29,0x5e,0x88,
+0xd8,0x6,0x9,0x0,0x5e,0x1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x55,0x42,0x29,
+0x5e,0x7b,0x3e,0xa8,0x15,0x0,0x64,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,
+0x42,0x29,0x5e,0x41,0xd,0x6b,0x2e,0x0,0x43,0x22,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x7c,0xff,0x28,0x5e,0x64,0xcc,0x8b,0x15,0x0,0x23,0xe6,0xa0,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x45,0x43,0x29,0x5e,0x7d,0x33,0x1d,0x35,0x0,0x53,0x1,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x5d,0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,0x39,0xfc,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0x42,0x29,0x5e,0x42,0x18,0xd0,0xc,0x0,
+0x9f,0x1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5e,0xff,0x28,0x5e,0xc5,0xf5,0x46,
+0x13,0x0,0x57,0x4a,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0x43,0x29,0x5e,0xbc,
+0x6a,0xae,0x3a,0x0,0xd2,0xee,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2e,0xff,0x28,
+0x5e,0x30,0x87,0xf9,0x8,0x0,0x6,0xcc,0x9a,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x48,
+0xff,0x28,0x5e,0x7e,0xfd,0x20,0xe,0x0,0x68,0x53,0xa0,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xfe,0x42,0x29,0x5e,0x9c,0x70,0x12,0x39,0x0,0x88,0x1b,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x1d,0x43,0x29,0x5e,0x1,0x96,0xcf,0x24,0x0,0x7c,0x10,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x40,0x43,0x29,0x5e,0xcf,0x44,0xd5,0x31,0x0,0x9b,0x1,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfd,0xfe,0x28,0x5e,0x3e,0x9a,0x4a,0x8,0x0,
+0xf4,0x14,0x95,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x57,0xff,0x28,0x5e,0x96,0x9f,0x53,
+0x38,0x0,0xec,0xed,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6e,0x42,0x29,0x5e,0xdf,
+0xf,0xa1,0x1e,0x0,0x99,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,0xff,0x28,
+0x5e,0x66,0x2c,0x3f,0x36,0x0,0x5,0x9d,0x9e,0x5,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,
+0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x63,0xa7,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4b,0x43,0x29,0x5e,0x18,0x63,0x66,0x29,0x0,0x4e,0x4,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,0x2c,0xc8,0x12,0x0,0x18,0xb,0xf1,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x55,0x42,0x29,0x5e,0x7b,0x3e,0xa8,0x15,0x0,0x50,0x32,
+0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe6,0x42,0x29,0x5e,0xa0,0xcd,0xba,0x1a,0x0,
+0xf5,0x3,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,
+0x2e,0x0,0xbf,0x15,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5b,0x43,0x29,0x5e,0x75,
+0xd1,0x5,0x0,0x0,0x1e,0x27,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x55,0x43,0x29,
+0x5e,0xb7,0x88,0x56,0x33,0x0,0x3,0x3,0xa8,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2b,
+0x56,0x29,0x5e,0x7f,0xef,0xa4,0xa,0x0,0x56,0x5d,0xae,0x9,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x8d,0x42,0x29,0x5e,0xee,0x8,0xdd,0x2c,0x0,0x40,0x23,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,0xd,0x6b,0x2e,0x0,0x68,0x1c,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x67,0xff,0x28,0x5e,0x80,0x3e,0x6e,0x25,0x0,0x8f,0xf,
+0xa2,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x49,0x0,0x29,0x5e,0xa0,0x96,0x55,0x29,0x0,
+0xf1,0x62,0xc6,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0x50,0xb6,0x15,
+0x21,0x0,0x3a,0x26,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,0x42,0x29,0x5e,0x7,
+0xe0,0x11,0x25,0x0,0x9c,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x43,0x29,
+0x5e,0xda,0x6a,0xb2,0x11,0x0,0x36,0x46,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x96,
+0xff,0x28,0x5e,0x5e,0x48,0x65,0x6,0x0,0xb4,0xbc,0x9f,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0x67,0x26,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xfe,0x42,0x29,0x5e,0x9c,0x70,0x12,0x39,0x0,0xc0,0x3,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xc7,0x76,0x73,0x7,0x0,0x30,0xac,
+0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x43,0x29,0x5e,0x1e,0x30,0x8b,0x8,0x0,
+0xf2,0xe,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf5,0x4c,0x4e,0x5e,0x3d,0x94,0x78,
+0x4,0x0,0xbb,0x80,0xc2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x77,0xff,0x28,0x5e,0xf,
+0x85,0x2,0x9,0x0,0x65,0xec,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3f,0x0,0x29,
+0x5e,0x1,0xd,0xf,0x21,0x0,0x44,0x1a,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd6,
+0x56,0x29,0x5e,0x20,0xe2,0x7d,0x1b,0x0,0x3b,0xe9,0xc2,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0xdf,0x6,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4c,0xff,0x28,0x5e,0x5,0xd7,0x5f,0x36,0x0,0xb4,0x5f,0xa1,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0x28,0x61,
+0xbd,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,
+0xa9,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x22,0x43,0x29,0x5e,0x97,0x3e,0xf8,
+0x29,0x0,0x59,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x55,0xff,0x28,0x5e,0xc8,
+0x2d,0xf2,0x3,0x0,0xe0,0x4b,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,0x43,0x29,
+0x5e,0x18,0x44,0x4c,0x32,0x0,0x65,0xe,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x86,
+0xff,0x28,0x5e,0x9,0xa7,0x88,0x27,0x0,0x44,0x1a,0xa5,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x3,0x42,0x29,0x5e,0x93,0x4,0x98,0xc,0x0,0x32,0x98,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x37,0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,0x25,0x62,0xe9,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,0xa1,0x2a,
+0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,
+0xde,0x35,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1d,0x43,0x29,0x5e,0x1,0x96,0xcf,
+0x24,0x0,0xb4,0xee,0xa7,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x42,0x29,0x5e,0x6e,
+0x44,0x43,0x2d,0x0,0xe0,0x6,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4f,0xff,0x28,
+0x5e,0xde,0xb1,0xbb,0x30,0x0,0x11,0x4b,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,
+0x43,0x29,0x5e,0x18,0x44,0x4c,0x32,0x0,0x3f,0xc0,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x52,0xff,0x28,0x5e,0x42,0x6d,0xd0,0x29,0x0,0xa3,0xd7,0xa0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xfc,0xa2,0x2a,0x5e,0xbe,0x8c,0xc6,0xc,0x0,0x9a,0x5b,0xc2,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x52,0xff,0x28,0x5e,0x42,0x6d,0xd0,0x29,0x0,0x53,0x71,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5d,0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,
+0xe0,0x70,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xae,0x4c,0x4e,0x5e,0x82,0xae,0xbd,
+0x2,0x0,0x9c,0x79,0xc2,0x19,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,
+0x3,0x14,0x3b,0x0,0xd3,0x43,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa1,0x5,0x97,
+0x5d,0x6f,0xfb,0xb0,0x13,0xc0,0x4a,0xb1,0x11,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x82,
+0x0,0x29,0x5e,0x21,0x89,0x40,0x0,0x0,0x44,0x1a,0xa5,0x4,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x98,0x42,0x29,0x5e,0x24,0xf8,0x10,0x4,0x0,0xa0,0x1c,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd5,0x42,0x29,0x5e,0x84,0xad,0xe8,0x36,0x0,0x1f,0x30,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0xa,0x62,
+0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,
+0x87,0x4,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,
+0x1b,0x0,0xef,0xda,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x35,0x0,0x29,0x5e,0x7,
+0x48,0x0,0x11,0x0,0x44,0x1a,0xa5,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,
+0x5e,0xde,0x78,0xde,0x32,0x0,0x75,0x76,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,
+0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x8,0xa5,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x5d,0x42,0x29,0x5e,0x60,0xe3,0xd7,0x36,0x0,0x2e,0xa7,0xc9,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4b,0x43,0x29,0x5e,0x18,0x63,0x66,0x29,0x0,0x5c,0x15,0xf0,0xa,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x25,0xaa,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x42,0x29,0x5e,0x6,0x8f,0xa9,0x14,0x0,
+0x3e,0x97,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x12,0x43,0x29,0x5e,0x69,0x5c,0xad,
+0x33,0x0,0xcf,0x50,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x31,0xff,0x28,0x5e,0xd5,
+0x8f,0xba,0x26,0x0,0x95,0xdc,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xeb,0x57,0x29,
+0x5e,0x4e,0x90,0xfc,0x26,0x0,0x21,0xb3,0xc1,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x52,
+0xff,0x28,0x5e,0x42,0x6d,0xd0,0x29,0x0,0x5d,0xa,0xa1,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x2c,0x43,0x29,0x5e,0x3,0x48,0x77,0x18,0x0,0xdf,0xf,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4,0x43,0x29,0x5e,0xc0,0xaa,0x1,0x7,0x0,0xfb,0xa9,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xbe,0x42,0x29,0x5e,0xe3,0x67,0x9f,0x1e,0x0,0xc9,0x8b,
+0xde,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x79,0x42,0x29,0x5e,0x7d,0x4c,0x53,0x16,0x0,
+0x36,0xa2,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa0,0xff,0x28,0x5e,0xc,0x1b,0x92,
+0x16,0x0,0x60,0x5d,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,
+0x78,0xde,0x32,0x0,0xe2,0xa0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1b,0x43,0x29,
+0x5e,0x3c,0xe7,0xae,0x35,0x0,0xff,0x10,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe3,
+0xff,0x28,0x5e,0xcb,0x90,0x4e,0xe,0x0,0x44,0x1a,0xa5,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x3b,0xab,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0xb1,0xdc,0xdd,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,0x71,0x2,
+0xf0,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3d,0xff,0x28,0x5e,0x8b,0xed,0x99,0x36,0x0,
+0x6e,0xef,0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdb,0x42,0x29,0x5e,0xe8,0x1e,0xdb,
+0x3,0x0,0x26,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,
+0x78,0xde,0x32,0x0,0x7e,0x93,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,0x43,0x29,
+0x5e,0xc0,0x10,0x91,0xd,0x0,0xd8,0x63,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7c,
+0xff,0x28,0x5e,0x64,0xcc,0x8b,0x15,0x0,0x73,0xdc,0xa0,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4b,0x42,0x29,0x5e,0x88,0xd8,0x6,0x9,0x0,0xe2,0xa0,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x12,0x43,0x29,0x5e,0x69,0x5c,0xad,0x33,0x0,0x1b,0xd,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x7,0x0,0x29,0x5e,0x21,0x20,0x11,0xb,0x0,0x44,0x1a,
+0xa5,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0x3,0x48,0x77,0x18,0x0,
+0x37,0x91,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa,0xff,0x28,0x5e,0x71,0xc5,0xa9,
+0x28,0x0,0xf4,0x14,0x95,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,
+0x9a,0x51,0x8,0x0,0x66,0x4a,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0x42,0x29,
+0x5e,0xb,0xaf,0x1e,0x2c,0x0,0xf8,0x4e,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x90,
+0x51,0x29,0x5e,0x26,0x63,0x59,0x1c,0x0,0xf9,0x3f,0x75,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4e,0x42,0x29,0x5e,0x1a,0xc,0x1c,0x1e,0x0,0xb8,0xc,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x27,0x42,0x29,0x5e,0xf2,0xb7,0x8e,0xf,0x0,0x19,0x91,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0xf7,0x26,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,0x3b,0x0,
+0xf8,0xae,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe2,0x0,0x29,0x5e,0x89,0xd3,0xf9,
+0x2e,0x0,0x2b,0xb8,0x58,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,
+0x51,0x1f,0x1b,0x0,0xe7,0x20,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x43,0x29,
+0x5e,0xda,0x6a,0xb2,0x11,0x0,0xa1,0xf,0xa8,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,
+0x42,0x29,0x5e,0x9c,0x70,0x12,0x39,0x0,0x45,0x20,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x5f,0x42,0x29,0x5e,0x2a,0xb5,0xc9,0x25,0x0,0x80,0x2c,0xe9,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,0x2c,0xc8,0x12,0x0,0x27,0x2a,0xf0,0xe,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x79,0x42,0x29,0x5e,0x7d,0x4c,0x53,0x16,0x0,0x92,0x1,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x38,0x43,0x29,0x5e,0xd4,0xa0,0x3c,0x13,0x0,
+0x89,0xa1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,
+0x3b,0x0,0xc4,0xe7,0xef,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x12,0x43,0x29,0x5e,0x69,
+0x5c,0xad,0x33,0x0,0x42,0x86,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,0xa2,0x2a,
+0x5e,0xbe,0x8c,0xc6,0xc,0x0,0xe4,0x75,0xc2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x19,
+0x43,0x29,0x5e,0x1,0x61,0x10,0x25,0x0,0xce,0x25,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,0x84,0x1a,0xe8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0xd6,0x60,0xe9,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xdf,0xdb,0xb2,0x5d,0x13,0x1,0x5b,0x2f,0x0,0xce,0x36,
+0x9f,0x22,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x43,0x29,0x5e,0xc0,0xaa,0x1,0x7,0x0,
+0x3,0xa1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x96,0xff,0x28,0x5e,0x5e,0x48,0x65,
+0x6,0x0,0xfd,0x39,0xa0,0x7,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,0x42,0x29,0x5e,0x9c,
+0x70,0x12,0x39,0x0,0x91,0x67,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd7,0x42,0x29,
+0x5e,0xad,0x63,0xd5,0x3,0x0,0x36,0xb1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,
+0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x85,0x2f,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xef,0x42,0x29,0x5e,0x50,0xb6,0x15,0x21,0x0,0x4f,0x3a,0xf0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0xe2,0xce,0x4e,0xb,0x0,0xc1,0x29,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf5,0x42,0x29,0x5e,0x51,0x94,0xcb,0x31,0x0,0x94,0x37,
+0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x42,0x29,0x5e,0x40,0xc,0xb6,0xb,0x0,
+0x32,0xaf,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf1,0x42,0x29,0x5e,0x2b,0x23,0xf5,
+0x31,0x0,0x55,0x97,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,
+0x78,0xde,0x32,0x0,0x1a,0x85,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa,0x43,0x29,
+0x5e,0xf,0x89,0xed,0x11,0x0,0x84,0xbd,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xba,
+0x0,0x29,0x5e,0x97,0x53,0x1c,0xc,0x0,0x44,0x1a,0xa5,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xfe,0x42,0x29,0x5e,0x9c,0x70,0x12,0x39,0x0,0xb7,0x86,0xdd,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,0x4c,0x86,0xef,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,0x7e,0xf0,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x43,0x29,0x5e,0x18,0x63,0x66,0x29,0x0,
+0x9f,0x46,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,0x42,0x29,0x5e,0x26,0x44,0x4c,
+0x10,0x0,0x41,0x20,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,
+0x51,0x1f,0x1b,0x0,0x5e,0xe,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7c,0xff,0x28,
+0x5e,0x64,0xcc,0x8b,0x15,0x0,0x7a,0xc5,0x9e,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x57,
+0xff,0x28,0x5e,0x96,0x9f,0x53,0x38,0x0,0x7a,0xe3,0xa0,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4d,0xff,0x28,0x5e,0xc0,0x1f,0x5,0x1c,0x0,0xfa,0x4f,0xa1,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe2,0x42,0x29,0x5e,0x26,0xa2,0x30,0x1e,0x0,0x88,0x11,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x30,0x43,0x29,0x5e,0xc0,0x42,0x65,0x1a,0x0,0xf4,0xc2,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x46,0xff,0x28,0x5e,0x26,0x8e,0xf6,0x20,0x0,
+0x32,0x39,0xa1,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x77,0xff,0x28,0x5e,0xf,0x85,0x2,
+0x9,0x0,0xa,0xee,0xa0,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x43,0x29,0x5e,0xbf,
+0x1d,0x28,0x1a,0x0,0x6,0xf9,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xda,0x42,0x29,
+0x5e,0xf,0x31,0x84,0x1d,0x0,0x3f,0xf6,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3f,
+0x0,0x29,0x5e,0x1,0xd,0xf,0x21,0x0,0x92,0x4c,0xa1,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd3,0xff,0x28,0x5e,0x88,0xd,0xd4,0x32,0x0,0x39,0xe6,0xa0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,0x3b,0x0,0x89,0x1d,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x52,0xff,0x28,0x5e,0x42,0x6d,0xd0,0x29,0x0,0x18,0xc0,
+0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4d,0xff,0x28,0x5e,0xc0,0x1f,0x5,0x1c,0x0,
+0x79,0x48,0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x22,0x43,0x29,0x5e,0x97,0x3e,0xf8,
+0x29,0x0,0x24,0xb,0xaa,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf7,0xff,0x28,0x5e,0x67,
+0x4a,0x9d,0x2e,0x0,0x8c,0x18,0xa2,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,0x43,0x29,
+0x5e,0xc0,0x10,0x91,0xd,0x0,0x95,0xc1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1f,
+0x43,0x29,0x5e,0xa9,0xa7,0xd7,0x32,0x0,0xd0,0xf,0xa8,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x25,0xff,0x28,0x5e,0xe3,0x3e,0x43,0x3a,0x0,0xfc,0xda,0xa1,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x1d,0x43,0x29,0x5e,0x1,0x96,0xcf,0x24,0x0,0x5a,0xf,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,0x15,0x0,0x32,0xba,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,0x42,0x29,0x5e,0x9c,0x70,0x12,0x39,0x0,
+0x6e,0x14,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,0x42,0x29,0x5e,0x7,0xe0,0x11,
+0x25,0x0,0xe4,0x13,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x38,0x43,0x29,0x5e,0xd4,
+0xa0,0x3c,0x13,0x0,0x12,0x3,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,
+0x5e,0xde,0x78,0xde,0x32,0x0,0x66,0x4,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,
+0xa2,0x2a,0x5e,0xbe,0x8c,0xc6,0xc,0x0,0x9e,0x87,0xc2,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x22,0x42,0x29,0x5e,0xc8,0xe7,0x5e,0x8,0x0,0x41,0x6f,0xb2,0x4,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x60,0x43,0x29,0x5e,0xd3,0xd7,0xbc,0x4,0x0,0x64,0x33,0xf0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x1d,0x67,0x39,0x5e,0x17,0x2e,0xf5,0x25,0x0,0xe,0x21,
+0x74,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x76,0xbb,0xb2,0x5d,0x33,0x8c,0x1b,0x11,0x0,
+0x5c,0x42,0x97,0x7,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,
+0x3b,0x0,0xad,0x44,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa2,0x42,0x29,0x5e,0xb4,
+0x9b,0x65,0x12,0x0,0x66,0x15,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0x52,0x29,
+0x5e,0xe9,0xdb,0x2,0x3,0x0,0x5f,0xea,0xf4,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x11,
+0x43,0x29,0x5e,0x9,0x18,0x99,0x2b,0x0,0x3e,0xdc,0xdf,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6b,0x1,0x29,0x5e,0xdd,0x3c,0xeb,0x1b,0x0,0xb5,0x32,0xa8,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x45,0x43,0x29,0x5e,0x7d,0x33,0x1d,0x35,0x0,0x5c,0xa7,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x27,0x43,0x29,0x5e,0x18,0x44,0x4c,0x32,0x0,0xca,0x88,
+0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,0x42,0x29,0x5e,0xf2,0xb7,0x8e,0xf,0x0,
+0x7f,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xba,0xff,0x28,0x5e,0xa9,0xa0,0x1c,
+0x5,0x0,0xfd,0x55,0xa0,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x43,0x29,0x5e,0x18,
+0x63,0x66,0x29,0x0,0x1a,0x2b,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x42,0x29,
+0x5e,0xfb,0x68,0xd7,0x1f,0x0,0x90,0xb,0xf1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,
+0x43,0x29,0x5e,0xcc,0x2c,0xc8,0x12,0x0,0xdc,0x2b,0xe9,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x46,0x43,0x29,0x5e,0xf1,0xe,0xbb,0x20,0x0,0xb9,0x67,0xe9,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x5d,0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,0x2d,0x24,0xa1,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x9a,0x1d,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x12,0x43,0x29,0x5e,0x69,0x5c,0xad,0x33,0x0,
+0x39,0x13,0xf1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf0,0x42,0x29,0x5e,0x2,0xdf,0xdb,
+0x29,0x0,0x31,0x3d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x15,0x43,0x29,0x5e,0x15,
+0xcf,0x52,0x2c,0x0,0xc8,0xf,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,
+0x5e,0xb0,0x9a,0x51,0x8,0x0,0xe8,0xf2,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x98,
+0x42,0x29,0x5e,0x24,0xf8,0x10,0x4,0x0,0xde,0x22,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x86,0xff,0x28,0x5e,0x9,0xa7,0x88,0x27,0x0,0x25,0xe6,0xa0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x46,0x43,0x29,0x5e,0xf1,0xe,0xbb,0x20,0x0,0xa5,0x11,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x96,0xff,0x28,0x5e,0x5e,0x48,0x65,0x6,0x0,0xe9,0x14,
+0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x57,0x29,0x5e,0x3d,0xf9,0x1c,0x10,0x0,
+0x3,0x5a,0xc2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,0x42,0x29,0x5e,0x7,0xe0,0x11,
+0x25,0x0,0xa5,0xfa,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd3,0x42,0x29,0x5e,0x75,
+0xdd,0x37,0x3,0x0,0xee,0xe,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9d,0x48,0x29,
+0x5e,0xa9,0xc0,0x6b,0x3b,0x80,0xac,0x62,0x75,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,
+0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0xac,0x98,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x7c,0x0,0x29,0x5e,0x1c,0xfc,0x5f,0x34,0x0,0xa7,0xdb,0xa0,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x3f,0x42,0x29,0x5e,0xc2,0x59,0xc8,0x6,0x0,0x64,0xae,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4d,0xff,0x28,0x5e,0xc0,0x1f,0x5,0x1c,0x0,0x3,0xdb,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x38,0xff,0x28,0x5e,0xc,0xdc,0xa6,0x27,0x0,
+0x2a,0xe6,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x36,0x42,0x29,0x5e,0x41,0x9,0x12,
+0x26,0x0,0xe2,0xa0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc0,0x4e,0x29,0x5e,0x38,
+0x15,0x54,0x1,0x80,0xbb,0x9a,0x75,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdb,0x42,0x29,
+0x5e,0xe8,0x1e,0xdb,0x3,0x0,0x25,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa5,
+0x0,0x29,0x5e,0x6c,0xe6,0x5a,0x2e,0x0,0x86,0xe6,0xa0,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x54,0x43,0x29,0x5e,0x1e,0x30,0x8b,0x8,0x0,0x6b,0x1,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,0x15,0x0,0x44,0xab,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0xb2,0x3,0x33,0xb,0x0,0xd7,0xf2,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x87,0x0,0x29,0x5e,0xa,0xe0,0x4f,0x8,0x0,
+0x44,0x1a,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x81,0xff,0x28,0x5e,0x2,0xc2,0xfc,
+0x1e,0x0,0x54,0x5,0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x77,0xff,0x28,0x5e,0xf,
+0x85,0x2,0x9,0x0,0x33,0x5a,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7c,0xff,0x28,
+0x5e,0x64,0xcc,0x8b,0x15,0x0,0xc5,0xdb,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe4,
+0x41,0x29,0x5e,0x2b,0x3c,0x1b,0xd,0x0,0xf5,0xb2,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe4,0x42,0x29,0x5e,0x6c,0xfa,0xc2,0x2e,0x0,0x23,0xb7,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x8,0x42,0x29,0x5e,0x64,0x4,0x42,0x16,0x0,0xf5,0xb2,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xc2,0x42,0x29,0x5e,0xbd,0x70,0x8d,0x1f,0x0,0xb0,0xb2,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf8,0x41,0x29,0x5e,0xa5,0x83,0xf2,0x34,0x0,
+0xad,0xb1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3e,0x43,0x29,0x5e,0x20,0xb3,0x83,
+0x24,0x0,0x1,0xa2,0xa8,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,
+0x2c,0xc8,0x12,0x0,0x9e,0xb1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x55,0x84,0x3a,
+0x5e,0x7b,0x10,0x51,0x4,0x0,0xe7,0x6d,0xbd,0x5,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9b,
+0x0,0x29,0x5e,0x1,0x7c,0xbd,0x25,0x0,0x6,0x37,0xd5,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x29,0x1,0x29,0x5e,0x5,0x98,0x8c,0x24,0x0,0xf7,0x63,0xd5,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf1,0x0,0x29,0x5e,0x8c,0x99,0xca,0x37,0x0,0xb7,0x57,0xd9,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0xe9,0xb0,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,
+0x41,0x76,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x89,0x1,0x29,0x5e,0x3a,0x7,0xe9,
+0x26,0x0,0x30,0xc2,0xd5,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x42,0x29,0x5e,0xfb,
+0x68,0xd7,0x1f,0x0,0x20,0x9b,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x3,0x29,
+0x5e,0xa1,0xea,0xa7,0x1b,0x0,0x62,0x4d,0xd9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,
+0x43,0x29,0x5e,0xcd,0x3,0x14,0x3b,0x0,0xc3,0x5c,0xe9,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x21,0x43,0x29,0x5e,0xd7,0x2e,0x60,0x5,0x0,0xaa,0xe0,0xd1,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0xa0,0xb0,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xfd,0x1,0x29,0x5e,0x71,0xc3,0x67,0x36,0x0,0xf7,0x43,
+0xdb,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdf,0x1,0x29,0x5e,0x22,0xea,0x51,0x26,0x0,
+0x9f,0x62,0xd5,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0x42,0x29,0x5e,0xb,0xaf,0x1e,
+0x2c,0x0,0x2,0xff,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x34,0x42,0x29,0x5e,0x20,
+0xda,0x82,0x35,0x0,0x91,0xa1,0xb2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x66,0x1,0x29,
+0x5e,0x72,0x50,0x73,0x13,0x0,0xbf,0xe1,0xad,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,
+0x52,0x29,0x5e,0xe9,0xdb,0x2,0x3,0x0,0xd5,0xde,0xfc,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x3a,0x2,0x29,0x5e,0xd5,0xe5,0xbc,0x7,0x0,0x27,0xbe,0xa9,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x92,0x42,0x29,0x5e,0x13,0xb7,0x6a,0x36,0x0,0x56,0xac,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x66,0x1,0x29,0x5e,0x72,0x50,0x73,0x13,0x0,0xa1,0xed,
+0x5b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xbc,0x42,0x29,0x5e,0x3a,0x57,0xf1,0xd,0x0,
+0x22,0x23,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xda,0x42,0x29,0x5e,0xf,0x31,0x84,
+0x1d,0x0,0x73,0x1b,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,0xa,0xe9,0x5d,0xab,
+0x7e,0x4,0x23,0x80,0xd6,0xba,0x10,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x43,0x29,
+0x5e,0xc0,0xaa,0x1,0x7,0x0,0x21,0x8a,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,
+0x42,0x29,0x5e,0x10,0xab,0x9f,0x1c,0x0,0x4a,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,0xd6,0xa6,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x7e,0xa,0xe9,0x5d,0xab,0x7e,0x4,0x23,0xc0,0xd9,0x55,0x10,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x7e,0xa,0xe9,0x5d,0xab,0x7e,0x4,0x23,0xa0,0x2e,0xe6,
+0x1d,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf6,0x42,0x29,0x5e,0xc4,0xfa,0x1b,0x3a,0x0,
+0xa5,0x3a,0xd2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,
+0x32,0x0,0x76,0xad,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc0,0x42,0x29,0x5e,0xf,
+0xc3,0x22,0x2f,0x0,0x24,0x20,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,0xa,0xe9,
+0x5d,0xab,0x7e,0x4,0x23,0x60,0x7,0xf1,0x1b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,
+0xa,0xe9,0x5d,0xab,0x7e,0x4,0x23,0x20,0xa1,0x2f,0x14,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x7e,0xa,0xe9,0x5d,0xab,0x7e,0x4,0x23,0xa0,0x81,0xeb,0x14,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xfd,0x55,0x29,0x5e,0xc2,0xb,0x45,0x22,0x0,0xa5,0xa1,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf7,0x41,0x29,0x5e,0x41,0xaa,0x8a,0x8,0x0,0xb1,0xd0,
+0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9a,0x56,0x29,0x5e,0x11,0xc6,0xf4,0x11,0x0,
+0xaf,0xaa,0x75,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xff,0x44,0x29,0x5e,0x48,0xcc,0x8d,
+0x31,0x0,0x25,0xad,0x75,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3,0x42,0x29,0x5e,0x93,
+0x4,0x98,0xc,0x0,0x39,0x39,0xb0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1c,0x41,0x29,
+0x5e,0x10,0x12,0x74,0x1c,0x0,0x7a,0xf5,0x4f,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,
+0x42,0x29,0x5e,0x7,0xe0,0x11,0x25,0x0,0x18,0xa0,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x16,0x0,0x29,0x5e,0xc0,0x3e,0x9d,0x22,0x0,0xb3,0xa6,0x93,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,0x27,0x94,0xc,0x0,0xf6,0xb,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x6e,0x42,0x29,0x5e,0xdf,0xf,0xa1,0x1e,0x0,0x8c,0x26,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc,0x0,0x29,0x5e,0x3a,0x4a,0xb0,0x13,0x80,
+0xe9,0xd3,0x5e,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,0xa,0xe9,0x5d,0xab,0x7e,0x4,
+0x23,0x20,0x2d,0x93,0x10,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,
+0xc1,0x39,0x32,0x0,0xd5,0xde,0xb0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf8,0x41,0x29,
+0x5e,0xa5,0x83,0xf2,0x34,0x0,0xf4,0xb2,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,
+0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,0x72,0x1b,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xdd,0x42,0x29,0x5e,0x6,0x8f,0xa9,0x14,0x0,0xb7,0x69,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc9,0xff,0x28,0x5e,0xba,0x12,0xe5,0x21,0x0,0xfb,0x55,0x5e,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,0x8b,0xab,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb,0x43,0x29,0x5e,0x52,0xe,0x9a,0x1a,0x0,
+0x8,0x91,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc9,0xff,0x28,0x5e,0xba,0x12,0xe5,
+0x21,0x0,0xcf,0xd4,0x5c,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,0x43,0x29,0x5e,0x18,
+0x44,0x4c,0x32,0x0,0x76,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,
+0x5d,0x53,0x4b,0x61,0x28,0x20,0xaa,0xfe,0x15,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,
+0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0x20,0x24,0x26,0x7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x9,0x43,0x29,0x5e,0xdd,0x27,0x94,0xc,0x0,0x8a,0x5e,0xe9,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0x40,0x95,0x9d,0x26,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x93,0x18,0xfa,0x5d,0x1b,0x60,0x25,0x1b,0x0,0xe2,0x86,
+0x62,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0x52,0x29,0x5e,0xe9,0xdb,0x2,0x3,0x0,
+0xa3,0x82,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,
+0x2e,0x0,0xde,0x8f,0xef,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,
+0x4b,0x61,0x28,0x40,0x5f,0x70,0x16,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0x52,0x29,
+0x5e,0xe9,0xdb,0x2,0x3,0x0,0x6a,0xac,0xfc,0xd,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,
+0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0xa0,0xf6,0xf3,0x10,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x2c,0x42,0x29,0x5e,0x15,0x4b,0x99,0x16,0x0,0x5f,0xfa,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0xe0,0x67,0x12,0x16,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0x8,0x62,0xa1,
+0x5,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0xa0,
+0xa4,0x2b,0x18,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x42,0x29,0x5e,0x54,0x85,0xee,
+0x21,0x0,0xd7,0xd,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,
+0x4b,0x61,0x28,0xc0,0x58,0x38,0x15,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,
+0x5d,0x53,0x4b,0x61,0x28,0x80,0xc4,0xf6,0x10,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9b,
+0xff,0x28,0x5e,0xc3,0xa0,0x16,0xe,0x0,0xf5,0x31,0x5c,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x7e,0x42,0x29,0x5e,0x10,0xab,0x9f,0x1c,0x0,0x34,0x76,0xe8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x60,0x43,0x29,0x5e,0xd3,0xd7,0xbc,0x4,0x0,0xf,0x28,0xe9,0xc,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xed,0xff,0x28,0x5e,0xeb,0x2f,0x15,0x1e,0x80,0x9a,0xab,
+0x58,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0x60,
+0xef,0x7e,0x16,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,0x4b,0x61,
+0x28,0x40,0x9b,0xf1,0x27,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0xb2,
+0x3,0x33,0xb,0x0,0x16,0x35,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x43,0x29,
+0x5e,0xbf,0x1d,0x28,0x1a,0x0,0x3,0xaf,0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,
+0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,0x5a,0x7f,0xef,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x9b,0x26,0xee,0x5d,0xa8,0xa7,0x16,0x9,0xd0,0x4f,0x51,0x7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x17,0x42,0x29,0x5e,0xbb,0xea,0xb0,0x31,0x0,0xab,0xef,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0xdd,0xa3,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x12,0xee,0x5d,0x53,0x4b,0x61,0x28,0xe0,
+0xcd,0xdd,0x10,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,
+0x36,0x80,0xba,0x11,0x16,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,
+0x80,0x1e,0x36,0x40,0xe2,0xf9,0x10,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc2,0x42,0x29,
+0x5e,0xbd,0x70,0x8d,0x1f,0x0,0xac,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc,
+0x42,0x29,0x5e,0x7e,0x26,0x9a,0x37,0x0,0x15,0xfb,0xde,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x7c,0x10,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xba,0x41,0x29,0x5e,0x3a,0x65,0x13,0x13,0x0,0x58,0xcf,0xb2,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0x40,0x41,0x9,
+0x11,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x15,0x43,0x29,0x5e,0x15,0xcf,0x52,0x2c,0x0,
+0x9c,0x8e,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc2,0x42,0x29,0x5e,0xbd,0x70,0x8d,
+0x1f,0x0,0xde,0xa1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,
+0x80,0x1e,0x36,0x80,0x9f,0xea,0x11,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x14,0x43,0x29,
+0x5e,0x94,0x3e,0xef,0x22,0x0,0x9e,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,
+0xdc,0x9,0x5e,0x6,0xe0,0x97,0x30,0x0,0xd1,0x31,0x87,0x3a,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x86,0x42,0x29,0x5e,0x23,0xc7,0xe6,0x36,0x0,0x61,0x1d,0xb0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0x20,0x4,0xf6,0x10,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0xa0,0x7,0x35,
+0x17,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0x42,0x29,0x5e,0x70,0x7b,0xc0,0x6,0x0,
+0xfd,0x1b,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,0x27,0x94,
+0xc,0x0,0xa2,0x1d,0xf1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,
+0x12,0xd6,0x30,0x80,0x4,0x49,0x5e,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x93,0x18,0xfa,
+0x5d,0x1b,0x60,0x25,0x1b,0x80,0xe1,0xea,0x60,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x21,
+0x43,0x29,0x5e,0xd7,0x2e,0x60,0x5,0x0,0xff,0x3,0xf0,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf1,0x42,0x29,0x5e,0x2b,0x23,0xf5,0x31,0x0,0xbe,0xb2,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,0x1f,0x80,0xb1,0x7,0x3f,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,0x91,0x1d,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1b,0x43,0x29,0x5e,0x3c,0xe7,0xae,0x35,0x0,
+0x84,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,0x12,0xd6,
+0x30,0x0,0xcf,0x47,0x3a,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,
+0x1,0x33,0x39,0x0,0x46,0x22,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,0xa2,0x2a,
+0x5e,0xbe,0x8c,0xc6,0xc,0x0,0x85,0x7b,0xc5,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,
+0x17,0xfa,0x5d,0x40,0x20,0x69,0x19,0x0,0xdb,0xa2,0x64,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0xc0,0xab,0x27,0x16,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,0x27,0x94,0xc,0x0,0xe,0x13,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4,0x43,0x29,0x5e,0xc0,0xaa,0x1,0x7,0x0,0x43,0x27,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,0x1f,0x0,
+0x6e,0x5b,0x4c,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,
+0x15,0x0,0x82,0xbb,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,
+0x12,0xd6,0x30,0x80,0xe4,0x73,0x66,0xb,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,0x42,0x29,
+0x5e,0xb2,0x95,0x22,0x6,0x0,0xe7,0x55,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,
+0x17,0xfa,0x5d,0x40,0x20,0x69,0x19,0x80,0xfc,0x98,0x64,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0xe0,0xb6,0xbe,0x18,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x6f,0x42,0x29,0x5e,0x70,0x7b,0xc0,0x6,0x0,0x3,0xff,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4d,0x42,0x29,0x5e,0xd7,0x30,0x1b,0x16,0x0,0x5d,0x88,
+0xa8,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa1,0x42,0x29,0x5e,0x3b,0xbd,0x9c,0x2b,0x0,
+0x70,0xa2,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,
+0x1f,0xe0,0x3a,0xdf,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,
+0x3,0x14,0x3b,0x0,0x5,0xa7,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x17,0xfa,
+0x5d,0x40,0x20,0x69,0x19,0x0,0x73,0x3f,0x50,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,
+0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0xc0,0xa0,0xe4,0x39,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,0x1f,0xa0,0x37,0xc,0x14,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0xc0,0x7,0xfc,0x10,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x14,0x43,0x29,0x5e,0x94,0x3e,0xef,0x22,0x0,0x4c,0x1d,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,0x42,0x29,0x5e,0x9c,0x70,0x12,0x39,0x0,
+0x14,0xb2,0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x17,0xfa,0x5d,0x40,0x20,0x69,
+0x19,0x80,0xed,0x26,0x7c,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,
+0x33,0x32,0x2f,0x0,0xb0,0x51,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,
+0x5e,0x1c,0x12,0xd6,0x30,0x0,0x65,0x9d,0x45,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x76,
+0x42,0x29,0x5e,0x2d,0xe0,0x26,0x1e,0x0,0x10,0x81,0xa9,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,0x1f,0x40,0x13,0x11,0x3d,0x2c,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x56,0x42,0x29,0x5e,0x38,0xb5,0x2,0x1b,0x0,0x50,0x29,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xa4,0x55,0x29,0x5e,0x1e,0xac,0x5d,0x2c,0x80,0x86,0x5d,
+0x75,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0x40,
+0x2e,0xf8,0x10,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,0x12,0xd6,
+0x30,0xb0,0x51,0x2c,0xd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,
+0xd,0x6b,0x2e,0x0,0x78,0xa2,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x98,0x42,0x29,
+0x5e,0x24,0xf8,0x10,0x4,0x0,0x59,0xcb,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3,
+0x42,0x29,0x5e,0x93,0x4,0x98,0xc,0x0,0xa5,0xd4,0xb0,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x60,0x42,0x29,0x5e,0x6,0xbc,0xe1,0x2d,0x0,0xfa,0xe0,0xaf,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,0x1f,0xe0,0x14,0x74,0x1a,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4b,0x43,0x29,0x5e,0x18,0x63,0x66,0x29,0x0,0xac,0x48,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x17,0xfa,0x5d,0x40,0x20,0x69,0x19,0x80,
+0xff,0xd9,0x48,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,0xd,0x6b,
+0x2e,0x0,0xd4,0x1a,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,
+0x78,0xde,0x32,0x0,0x78,0x2f,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0x42,0x29,
+0x5e,0xb,0xaf,0x1e,0x2c,0x0,0x65,0xbb,0xab,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,
+0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0x50,0xe8,0x62,0x6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe5,0x42,0x29,0x5e,0xeb,0xc6,0x70,0x15,0x0,0x2c,0x7c,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x83,0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0xa0,0x2,0xcc,0x12,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb7,0x42,0x29,0x5e,0xdb,0x4e,0xfc,0x7,0x0,0x56,0x54,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0x0,
+0x87,0xb8,0x45,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9d,0x42,0x29,0x5e,0xa7,0x8b,0xd6,
+0x9,0x0,0x3a,0x22,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc1,0x42,0x29,0x5e,0x84,
+0x84,0xe3,0x16,0x0,0xf2,0x18,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,
+0x5d,0x71,0xd3,0xe7,0x1f,0xc0,0xf6,0x39,0x21,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,
+0x17,0xfa,0x5d,0x40,0x20,0x69,0x19,0xc0,0x60,0xb9,0x3b,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x79,0x42,0x29,0x5e,0x7d,0x4c,0x53,0x16,0x0,0xe5,0x36,0xf0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc0,0x55,0x29,0x5e,0x70,0x93,0xa,0x11,0x0,0x26,0x6a,0xa3,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,0x1,0x33,0x39,0x0,0xc8,0xaa,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0x80,
+0xcf,0xf,0x6b,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb,0x43,0x29,0x5e,0x52,0xe,0x9a,
+0x1a,0x0,0xd6,0x95,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,
+0x51,0x1f,0x1b,0x0,0xf4,0x78,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,0x42,0x29,
+0x5e,0x10,0xab,0x9f,0x1c,0x0,0xac,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x22,
+0x43,0x29,0x5e,0x97,0x3e,0xf8,0x29,0x0,0x2e,0x74,0xae,0x4,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xac,0x42,0x29,0x5e,0x7,0xe0,0x11,0x25,0x0,0x83,0xb5,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,0x3b,0x0,0x81,0x1c,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,0x27,0x94,0xc,0x0,0x7d,0x1c,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa7,0x42,0x29,0x5e,0x85,0x43,0x6a,0x1b,0x0,
+0x7e,0xad,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,
+0x36,0x60,0x74,0x5b,0x16,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8d,0x42,0x29,0x5e,0xee,
+0x8,0xdd,0x2c,0x0,0x78,0xa6,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,
+0x5e,0x1c,0xc7,0xdf,0x4,0x80,0xca,0xed,0x3c,0x8,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdf,
+0x41,0x29,0x5e,0xf1,0x50,0x5a,0x3,0x0,0xc2,0xec,0xcf,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4,0x57,0x29,0x5e,0x3d,0xf9,0x1c,0x10,0x0,0x41,0xe2,0xf4,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x22,0x43,0x29,0x5e,0x97,0x3e,0xf8,0x29,0x0,0x13,0xaa,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x40,0xab,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,
+0xc3,0x21,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0x52,0x29,0x5e,0xe9,0xdb,0x2,
+0x3,0x0,0xb2,0x60,0xfe,0x8,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,
+0x1,0x33,0x39,0x0,0x6e,0xf9,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0x42,0x29,
+0x5e,0x1b,0xd1,0x1f,0x16,0x0,0x25,0xad,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,
+0x43,0x29,0x5e,0x18,0x63,0x66,0x29,0x0,0xe9,0xf4,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x62,0x52,0x29,0x5e,0xe9,0xdb,0x2,0x3,0x0,0xda,0x43,0xe9,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb,0x43,0x29,0x5e,0x52,0xe,0x9a,0x1a,0x0,0x9e,0x1c,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0x3,0x59,
+0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,
+0x58,0x4f,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x63,0x42,0x29,0x5e,0x3c,0x4,0xf2,
+0x2,0x0,0x3c,0xf,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,
+0xc5,0x8d,0x29,0x0,0x94,0x6e,0xd1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x31,0x42,0x29,
+0x5e,0x23,0xce,0x11,0x1d,0x0,0x58,0xfe,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,
+0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0x40,0x67,0xd0,0x16,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x3c,0x43,0x29,0x5e,0xcc,0x2c,0xc8,0x12,0x0,0x9a,0x28,0xf0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0x78,0x31,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,0xe4,0x9a,
+0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,0x1f,0x80,
+0x81,0xb0,0x24,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x84,0x25,0x5e,0x28,0xfe,0x2d,
+0x6,0x80,0x29,0xea,0x47,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,
+0x9a,0x51,0x8,0x0,0xd7,0x89,0xe0,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3f,0x42,0x29,
+0x5e,0xc2,0x59,0xc8,0x6,0x0,0xa0,0xf9,0xc1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,
+0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,0x47,0x95,0xf0,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x3b,0x42,0x29,0x5e,0xb,0xaf,0x1e,0x2c,0x0,0xd,0x15,0xe9,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x42,0x29,0x5e,0xfb,0x68,0xd7,0x1f,0x0,0x2d,0x2e,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x60,0x43,0x29,0x5e,0xd3,0xd7,0xbc,0x4,0x0,0x37,0x13,
+0xf1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,
+0xb1,0x27,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,
+0x3b,0x0,0x64,0x21,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0x3,
+0x48,0x77,0x18,0x0,0x20,0x44,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6e,0x42,0x29,
+0x5e,0xdf,0xf,0xa1,0x1e,0x0,0x39,0x23,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,
+0x17,0xfa,0x5d,0x40,0x20,0x69,0x19,0x80,0x71,0xbb,0x42,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0xa0,0xfd,0x4,0x1f,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0x3,0x48,0x77,0x18,0x0,0xc7,0x2,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0xa0,0xd4,0x5d,
+0x13,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x40,0x42,0x29,0x5e,0x2a,0x4d,0xaf,0x33,0x0,
+0x7d,0xda,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,0x57,0x29,0x5e,0x47,0x25,0x2c,
+0x21,0x0,0xb9,0xb6,0xdf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf6,0x42,0x29,0x5e,0xc4,
+0xfa,0x1b,0x3a,0x0,0x62,0x72,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,0x42,0x29,
+0x5e,0xb2,0x95,0x22,0x6,0x0,0xbb,0x28,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,
+0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0x80,0x47,0x11,0x40,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x84,0x84,0x25,0x5e,0x5a,0x2b,0xec,0xd,0x40,0x1d,0x88,0x1f,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x13,0x43,0x29,0x5e,0xd6,0xae,0x40,0x1a,0x0,0x14,0x1e,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0x50,0xb6,0x15,0x21,0x0,0xf7,0x3e,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x43,0x29,0x5e,0xda,0x6a,0xb2,0x11,0x0,
+0x92,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1b,0x43,0x29,0x5e,0x3c,0xe7,0xae,
+0x35,0x0,0x99,0xc6,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,
+0x3,0x14,0x3b,0x0,0xb8,0x39,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x43,0x29,
+0x5e,0x18,0x63,0x66,0x29,0x0,0xb2,0x37,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,
+0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x34,0x1e,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x13,0x43,0x29,0x5e,0xd6,0xae,0x40,0x1a,0x0,0x5f,0xf,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd4,0xb4,0xf4,0x5d,0x14,0x8c,0xc2,0x28,0x0,0xea,0x73,0x98,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x91,0x17,0xfa,0x5d,0x40,0x20,0x69,0x19,0x0,0xe3,0xa1,
+0x48,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x42,0x29,0x5e,0x54,0x85,0xee,0x21,0x0,
+0x2b,0x1e,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,0x33,0x32,
+0x2f,0x0,0xcb,0x22,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,
+0x9a,0x51,0x8,0x0,0xde,0x28,0xf0,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x75,0xef,
+0x5d,0xdb,0x80,0x1e,0x36,0x40,0xa2,0x99,0x1e,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc1,
+0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0x43,0xb6,0xd1,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf8,0x42,0x29,0x5e,0xea,0x1c,0xeb,0x6,0x0,0x87,0x1d,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x6c,0x42,0x29,0x5e,0x73,0x39,0x14,0xe,0x0,0xad,0xe5,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0xe0,0xa2,0x21,
+0xa,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xcb,0x42,0x29,0x5e,0x73,0x3f,0x8a,0x25,0x0,
+0xa6,0x11,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdb,0x42,0x29,0x5e,0xe8,0x1e,0xdb,
+0x3,0x0,0x61,0x1c,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,
+0x33,0x32,0x2f,0x0,0x1,0x1f,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x98,0x42,0x29,
+0x5e,0x24,0xf8,0x10,0x4,0x0,0x4,0x20,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,
+0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,0xd5,0x60,0xe9,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6f,0x42,0x29,0x5e,0x70,0x7b,0xc0,0x6,0x0,0x65,0x1d,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0xea,0x35,0xf0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xc5,0x42,0x29,0x5e,0xb3,0x23,0xf3,0x37,0x0,0x6e,0x14,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,
+0x5d,0x21,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd5,0x42,0x29,0x5e,0x84,0xad,0xe8,
+0x36,0x0,0x9c,0x28,0xf0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,
+0x2c,0xc8,0x12,0x0,0x4c,0x31,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9d,0x42,0x29,
+0x5e,0xa7,0x8b,0xd6,0x9,0x0,0xa3,0x21,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,
+0x42,0x29,0x5e,0x26,0x44,0x4c,0x10,0x0,0xe3,0xae,0xdf,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0xb8,0xb1,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x55,0x43,0x29,0x5e,0xb7,0x88,0x56,0x33,0x0,0x2c,0x2a,0xf0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x76,0x42,0x29,0x5e,0x2d,0xe0,0x26,0x1e,0x80,0x57,0x79,
+0x79,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6c,0xff,0x28,0x5e,0x92,0xb1,0x45,0x30,0x0,
+0xed,0x58,0xa1,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdc,0x42,0x29,0x5e,0xc,0x6,0x26,
+0xc,0x0,0xac,0x1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0xe1,0x2e,0x5e,0xaf,
+0xeb,0x36,0x3,0x0,0x73,0xa1,0x70,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x59,0x43,0x29,
+0x5e,0xae,0x59,0x1d,0x11,0x0,0x9d,0xfc,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x71,
+0xff,0x28,0x5e,0xd5,0x23,0x49,0x3a,0x0,0xca,0x35,0xa0,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x5c,0x42,0x29,0x5e,0x4e,0xaa,0x21,0x2e,0x0,0x36,0xb0,0x86,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x33,0xff,0x28,0x5e,0x8a,0xde,0xab,0x18,0x80,0xe9,0xec,0x5a,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x81,0xff,0x28,0x5e,0x2,0xc2,0xfc,0x1e,0x0,0x79,0xee,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf,0x43,0x29,0x5e,0xbf,0x1d,0x28,0x1a,0x0,
+0x91,0xf6,0xd1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9b,0xff,0x28,0x5e,0xc3,0xa0,0x16,
+0xe,0x0,0x9d,0x5e,0xa0,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x14,0x43,0x29,0x5e,0x94,
+0x3e,0xef,0x22,0x0,0x62,0x1,0xa8,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x96,0xfe,0x28,
+0x5e,0x2b,0x43,0x22,0x2c,0x0,0xdb,0xae,0x58,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,
+0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0x80,0xfb,0x9d,0x63,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x3b,0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0x0,0xe4,0xe8,0x6a,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xa0,0xff,0x28,0x5e,0xc,0x1b,0x92,0x16,0x0,0xae,0x50,0xa1,0x7,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,0x1f,0x20,0xb3,0x82,
+0x8,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa5,0xff,0x28,0x5e,0xcf,0xe1,0xd3,0x1e,0x80,
+0x54,0xc3,0x5e,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa7,0x42,0x29,0x5e,0x85,0x43,0x6a,
+0x1b,0x0,0x8a,0x81,0xa8,0x9,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,
+0x2c,0xc8,0x12,0x0,0x4b,0x15,0xf0,0xb,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x41,0x29,
+0x5e,0x4a,0x90,0xdd,0x14,0x0,0x80,0x39,0x93,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,
+0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0x0,0xa9,0xe3,0x4b,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0x40,0xbe,0xe6,0x21,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0xa0,0xd1,0x47,0x7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x83,0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0x80,0xd8,0x1d,
+0x49,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9b,0xff,0x28,0x5e,0xc3,0xa0,0x16,0xe,0x0,
+0xc,0xdb,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,
+0x1f,0x0,0xe2,0xe1,0x4b,0xb,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x17,0xfa,0x5d,0x40,
+0x20,0x69,0x19,0x80,0x30,0xcd,0x43,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,
+0x5e,0x1c,0xc7,0xdf,0x4,0x0,0x7,0x8a,0x42,0xc,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x86,
+0xff,0x28,0x5e,0x9,0xa7,0x88,0x27,0x0,0x95,0xf3,0xa0,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb,0x43,0x29,0x5e,0x52,0xe,0x9a,0x1a,0x0,0x47,0xf5,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xdb,0x42,0x29,0x5e,0xe8,0x1e,0xdb,0x3,0x0,0x3d,0xc6,0xc8,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xa5,0xff,0x28,0x5e,0xcf,0xe1,0xd3,0x1e,0x0,0xfb,0x4a,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x67,0xff,0x28,0x5e,0x80,0x3e,0x6e,0x25,0x0,
+0xc4,0x21,0xbf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6c,0xff,0x28,0x5e,0x92,0xb1,0x45,
+0x30,0x0,0x19,0xbb,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x48,0xff,0x28,0x5e,0x7e,
+0xfd,0x20,0xe,0x0,0x99,0xe,0xa8,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe7,0x42,0x29,
+0x5e,0x8c,0xc3,0xe5,0x22,0x0,0xff,0xf1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xbe,
+0x42,0x29,0x5e,0xe3,0x67,0x9f,0x1e,0x0,0xa1,0x71,0xa0,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf8,0x42,0x29,0x5e,0xea,0x1c,0xeb,0x6,0x0,0x2f,0x2,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4c,0xff,0x28,0x5e,0x5,0xd7,0x5f,0x36,0x0,0x69,0x92,0xa0,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x81,0xff,0x28,0x5e,0x2,0xc2,0xfc,0x1e,0x0,0xd,0x5c,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x67,0xff,0x28,0x5e,0x80,0x3e,0x6e,0x25,0x0,
+0xb0,0x2b,0x93,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdf,0x42,0x29,0x5e,0x6,0x2,0xdb,
+0x25,0x0,0x78,0x10,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa,0x43,0x29,0x5e,0xf,
+0x89,0xed,0x11,0x0,0x48,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,
+0x5e,0x1c,0xc7,0xdf,0x4,0x80,0x1a,0xf8,0x47,0xf,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,
+0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0xa0,0xbb,0xb,0xe,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf7,0xff,0x28,0x5e,0x67,0x4a,0x9d,0x2e,0x0,0x85,0xf4,0xa0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc7,0x42,0x29,0x5e,0xfb,0x8c,0x21,0x25,0x0,0x63,0x3e,0xaa,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xa5,0xff,0x28,0x5e,0xcf,0xe1,0xd3,0x1e,0x0,0x8c,0xe4,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2b,0x43,0x29,0x5e,0x5a,0xe2,0xd7,0xf,0x0,
+0xea,0x10,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,0x5d,0x71,0xd3,0xe7,
+0x1f,0x0,0xc4,0xe6,0x42,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa1,0xff,0x28,0x5e,0x72,
+0x4b,0xf2,0x1e,0x80,0x74,0x2b,0x7f,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6c,0xff,0x28,
+0x5e,0x92,0xb1,0x45,0x30,0x0,0x7a,0xee,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x86,
+0xff,0x28,0x5e,0x9,0xa7,0x88,0x27,0x0,0x5,0xee,0xa0,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf,0x75,0xef,0x5d,0xdb,0x80,0x1e,0x36,0xe0,0xe8,0xf9,0x10,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,0x33,0x32,0x2f,0x80,0xf9,0x4d,0x7c,0x4,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x44,0x43,0x29,0x5e,0x7e,0x65,0x6,0x2d,0x0,0xd6,0xf,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xec,0x42,0x29,0x5e,0xb1,0xd5,0x72,0x36,0x0,
+0xec,0xf1,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6c,0xff,0x28,0x5e,0x92,0xb1,0x45,
+0x30,0x0,0x5f,0xcf,0x9b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x77,0xff,0x28,0x5e,0xf,
+0x85,0x2,0x9,0x0,0x61,0xde,0x9f,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,
+0x5e,0x1c,0x12,0xd6,0x30,0x0,0x4a,0x81,0x45,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,
+0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0xe0,0xfc,0xa0,0x1a,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x67,0xff,0x28,0x5e,0x80,0x3e,0x6e,0x25,0x0,0x67,0x53,0xa1,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x39,0x43,0x29,0x5e,0xed,0xa7,0xe0,0x1b,0x0,0x83,0x26,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x79,0x42,0x29,0x5e,0x7d,0x4c,0x53,0x16,0x0,0xc0,0x9,
+0xe0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe8,0xff,0x28,0x5e,0x6b,0xfe,0x7b,0x16,0x0,
+0xf1,0xf3,0xa0,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x81,0xff,0x28,0x5e,0x2,0xc2,0xfc,
+0x1e,0x0,0xd0,0xa3,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe4,0x42,0x29,0x5e,0x6c,
+0xfa,0xc2,0x2e,0x0,0x92,0x1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x17,0xfa,
+0x5d,0x40,0x20,0x69,0x19,0x80,0xd0,0xea,0x4b,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,
+0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0x60,0xff,0x5,0x1a,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x60,0x43,0x29,0x5e,0xd3,0xd7,0xbc,0x4,0x0,0xde,0xeb,0xef,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x5d,0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,0x3a,0x56,0xa0,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0xcc,0x76,
+0xb2,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x43,0x29,0x5e,0xf0,0xd5,0xd6,0x7,0x0,
+0x40,0xf9,0x79,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2f,0x43,0x29,0x5e,0xaa,0x2a,0x47,
+0x11,0x0,0x95,0xf0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa7,0x42,0x29,0x5e,0x85,
+0x43,0x6a,0x1b,0x0,0xaf,0x64,0xc1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x33,0xff,0x28,
+0x5e,0x8a,0xde,0xab,0x18,0x0,0x89,0x4c,0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x11,
+0x0,0x29,0x5e,0xa0,0x94,0x1e,0x1b,0x0,0x3b,0x5b,0xd3,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4d,0xff,0x28,0x5e,0xc0,0x1f,0x5,0x1c,0x0,0x38,0x45,0xa0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc9,0xff,0x28,0x5e,0xba,0x12,0xe5,0x21,0x0,0xe9,0x79,0x66,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,0x12,0xd6,0x30,0x40,0xc,0x11,
+0xd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,
+0xd6,0xee,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xc7,0x76,0x73,
+0x7,0x0,0xcc,0xf,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x38,0xff,0x28,0x5e,0xc,
+0xdc,0xa6,0x27,0x0,0x95,0x4f,0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa1,0xff,0x28,
+0x5e,0x72,0x4b,0xf2,0x1e,0x80,0x86,0x85,0x7b,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x86,
+0xff,0x28,0x5e,0x9,0xa7,0x88,0x27,0x0,0x6b,0x3,0x5f,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x5d,0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,0x1f,0x5d,0xa1,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x44,0x43,0x29,0x5e,0x7e,0x65,0x6,0x2d,0x0,0xaa,0xf0,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xa5,0xff,0x28,0x5e,0xcf,0xe1,0xd3,0x1e,0x0,0xa8,0xdb,
+0xa0,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0xff,0x28,0x5e,0x4c,0x64,0x91,0x18,0x0,
+0xed,0x58,0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,
+0xc,0x0,0xcc,0x80,0xd3,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x49,0x43,0x29,0x5e,0x10,
+0xc3,0xe,0x36,0x0,0x80,0x1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xda,0x42,0x29,
+0x5e,0xf,0x31,0x84,0x1d,0x0,0x1,0x3,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,
+0x43,0x29,0x5e,0x43,0x5b,0xec,0x2b,0x0,0xfa,0xf1,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x77,0xff,0x28,0x5e,0xf,0x85,0x2,0x9,0x0,0x32,0x5e,0xa0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4,0xff,0x28,0x5e,0x86,0x63,0xef,0xd,0x80,0x65,0xc6,0x61,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x8b,0xff,0x28,0x5e,0x13,0xee,0x3e,0x32,0x0,0x8a,0x93,
+0x9f,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0xff,0x28,0x5e,0x4c,0x64,0x91,0x18,0x0,
+0x42,0x3a,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x84,0x25,0x5e,0x28,0xfe,0x2d,
+0x6,0x80,0x7b,0xd4,0x4b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0xd3,0x1e,0x5e,0x1c,
+0x12,0xd6,0x30,0xc0,0xbe,0x58,0x7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0xff,0x28,
+0x5e,0x4c,0x64,0x91,0x18,0x0,0x28,0xe7,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,
+0x42,0x29,0x5e,0x7,0xe0,0x11,0x25,0x0,0xc2,0x76,0xc7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x37,0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,0xc9,0xf0,0xa7,0x4,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd7,0x42,0x29,0x5e,0xad,0x63,0xd5,0x3,0x0,0xea,0x3,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x73,0x42,0x29,0x5e,0xa5,0x8a,0xf,0x28,0x0,0xc9,0xd9,
+0x9e,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x86,0xff,0x28,0x5e,0x9,0xa7,0x88,0x27,0x0,
+0x1f,0x5f,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0xb2,0x3,0x33,
+0xb,0x0,0x9a,0xcb,0xa9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x59,0xff,0x28,0x5e,0xaa,
+0x7c,0x5,0x6,0x0,0xd5,0x8a,0x95,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4c,0xff,0x28,
+0x5e,0x5,0xd7,0x5f,0x36,0x0,0x86,0x52,0x9d,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x81,
+0xff,0x28,0x5e,0x2,0xc2,0xfc,0x1e,0x0,0xcb,0x8e,0x9f,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xce,0xff,0x28,0x5e,0xad,0x3e,0x52,0x2a,0x0,0xbd,0xc2,0x7f,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x68,0xff,0x28,0x5e,0xd6,0x4b,0x67,0x2e,0x0,0x64,0xdf,0xa0,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x15,0xca,
+0xa9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,
+0x79,0x1,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc5,0x42,0x29,0x5e,0xb3,0x23,0xf3,
+0x37,0x0,0xb7,0x92,0xe5,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfd,0xa2,0x2a,0x5e,0x38,
+0x82,0xb,0x12,0x0,0x4a,0xa7,0xc3,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5e,0xff,0x28,
+0x5e,0xc5,0xf5,0x46,0x13,0x0,0x52,0xee,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6c,
+0xff,0x28,0x5e,0x92,0xb1,0x45,0x30,0x0,0x99,0x4b,0xa0,0x3,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x86,0xff,0x28,0x5e,0x9,0xa7,0x88,0x27,0x0,0x11,0x4b,0xa0,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xa5,0xff,0x28,0x5e,0xcf,0xe1,0xd3,0x1e,0x0,0xb9,0xcb,0x89,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x5d,0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,0x9c,0x91,
+0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xff,0x28,0x5e,0x1f,0xc3,0xe8,0x38,0x0,
+0x30,0xe6,0xc8,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x45,0x43,0x29,0x5e,0x7d,0x33,0x1d,
+0x35,0x0,0xe6,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,
+0xb0,0xa7,0x19,0x80,0xdb,0x30,0x16,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdd,0x62,0xfb,
+0x5d,0x71,0xd3,0xe7,0x1f,0x80,0x65,0xe9,0x4b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,
+0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0x0,0x4a,0xd9,0x42,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x33,0xff,0x28,0x5e,0x8a,0xde,0xab,0x18,0x0,0x70,0x86,0xa0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x46,0x42,0x29,0x5e,0x2c,0xb3,0xda,0x1,0x0,0x13,0xa5,0xaf,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x5a,0x42,0x29,0x5e,0x86,0x68,0xf,0x1c,0x0,0xcc,0xc6,
+0x82,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x36,0x43,0x29,0x5e,0xa2,0x46,0x66,0x22,0x0,
+0xd2,0xf,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0x52,0x29,0x5e,0xe9,0xdb,0x2,
+0x3,0x0,0x68,0xbd,0xf4,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9b,0xff,0x28,0x5e,0xc3,
+0xa0,0x16,0xe,0x0,0x45,0xe6,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,
+0x5e,0xcc,0x2c,0xc8,0x12,0x0,0x6b,0xc8,0xbc,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x96,
+0xff,0x28,0x5e,0x5e,0x48,0x65,0x6,0x0,0xe4,0xf3,0xa0,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xfc,0xa2,0x2a,0x5e,0xbe,0x8c,0xc6,0xc,0x0,0x0,0x49,0xf9,0xa,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x39,0xff,0x28,0x5e,0x7f,0x13,0xc7,0x2f,0x0,0xee,0x5a,0xa1,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x57,0xff,0x28,0x5e,0x96,0x9f,0x53,0x38,0x0,0xd5,0xbb,
+0x8b,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0x3,0x48,0x77,0x18,0x0,
+0x30,0x13,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x47,0x0,0x29,0x5e,0xee,0x4a,0xf5,
+0x23,0x0,0x72,0xe1,0x59,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa5,0x0,0x29,0x5e,0x6c,
+0xe6,0x5a,0x2e,0x0,0x8a,0x1,0x56,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x81,0xff,0x28,
+0x5e,0x2,0xc2,0xfc,0x1e,0x0,0x9f,0x73,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,
+0xfe,0x28,0x5e,0x10,0x4f,0x32,0x10,0x80,0x89,0x1,0x7f,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xdb,0x42,0x29,0x5e,0xe8,0x1e,0xdb,0x3,0x0,0xa1,0x1,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xba,0xff,0x28,0x5e,0xa9,0xa0,0x1c,0x5,0x80,0x9e,0x35,0x6f,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x62,0xff,0x28,0x5e,0x4c,0x64,0x91,0x18,0x0,0xfa,0x14,
+0xa1,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5d,0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,
+0xb4,0x86,0xc3,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x48,0xff,0x28,0x5e,0x7e,0xfd,0x20,
+0xe,0x0,0x15,0x36,0xa1,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2,0x0,0x29,0x5e,0x16,
+0x89,0x56,0x3,0x0,0xb2,0x52,0x7c,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd3,0x42,0x29,
+0x5e,0x75,0xdd,0x37,0x3,0x0,0x6a,0x93,0x86,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x46,
+0x43,0x29,0x5e,0xf1,0xe,0xbb,0x20,0x0,0xfc,0x10,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,0xd6,0xf,0xa8,0x4,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x8c,0xff,0x28,0x5e,0x64,0x8f,0x5f,0x3a,0x0,0x1c,0x3a,0xa0,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x42,0x29,0x5e,0x28,0x74,0xe5,0x1b,0x0,0x4e,0x85,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf8,0x41,0x29,0x5e,0x1e,0x7b,0x13,0x11,0x0,
+0x6,0x86,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x42,0x29,0x5e,0x15,0x4b,0x99,
+0x16,0x0,0x8,0xda,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0x42,0x29,0x5e,0x30,
+0x11,0x35,0x16,0x0,0xb8,0xdc,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd9,0x41,0x29,
+0x5e,0xb3,0x7c,0x9a,0x35,0x0,0xcc,0xdd,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x42,0x29,0x5e,0x28,0x74,0xe5,0x1b,0x0,0x80,0x68,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd4,0x41,0x29,0x5e,0x59,0x1a,0xc2,0x2b,0x0,0x6c,0x17,0xb0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x1f,0x42,0x29,0x5e,0x52,0xc6,0x6d,0x10,0x0,0xcd,0xdd,0xb0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x30,0x42,0x29,0x5e,0xbb,0xf0,0xf7,0x30,0x0,0x42,0x8d,
+0xb1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x12,0x42,0x29,0x5e,0xa5,0xb2,0x71,0x2a,0x0,
+0x9,0xda,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2,0x42,0x29,0x5e,0xce,0xdb,0x80,
+0x24,0x0,0x9,0xda,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb4,0x41,0x29,0x5e,0x56,
+0xbb,0x2b,0x2,0x0,0x8,0x65,0xae,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x90,0xff,0x28,
+0x5e,0x69,0xf0,0xf7,0x39,0x0,0xc9,0x50,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe2,
+0x41,0x29,0x5e,0xfa,0x32,0x7,0x1c,0x0,0xd,0xda,0xb0,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x1f,0x42,0x29,0x5e,0x52,0xc6,0x6d,0x10,0x0,0xff,0xdd,0xaf,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x32,0x42,0x29,0x5e,0x53,0xe8,0x59,0x25,0x0,0x68,0xd8,0xaf,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xa0,0xff,0x28,0x5e,0xc,0x1b,0x92,0x16,0x0,0xc9,0x50,
+0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0x42,0x29,0x5e,0x70,0x7b,0xc0,0x6,0x0,
+0x27,0xc6,0xae,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,
+0x13,0x0,0x1c,0x61,0xaa,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7c,0xff,0x28,0x5e,0x64,
+0xcc,0x8b,0x15,0x0,0xe6,0x40,0xa6,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe3,0x41,0x29,
+0x5e,0x5b,0xbc,0x4f,0x24,0x0,0xf5,0xdd,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x81,
+0xff,0x28,0x5e,0x2,0xc2,0xfc,0x1e,0x0,0xb8,0x50,0xa5,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe1,0x41,0x29,0x5e,0xa5,0x7e,0xe3,0x13,0x0,0xaa,0xdc,0xb0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x40,0x42,0x29,0x5e,0x2a,0x4d,0xaf,0x33,0x0,0x9,0xda,0xb0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xa5,0xff,0x28,0x5e,0xcf,0xe1,0xd3,0x1e,0x0,0x9a,0x3b,
+0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb,0x42,0x29,0x5e,0x38,0xec,0x99,0x2f,0x0,
+0xc5,0xdd,0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x81,0xff,0x28,0x5e,0x2,0xc2,0xfc,
+0x1e,0x0,0xb1,0x50,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x86,0xff,0x28,0x5e,0x9,
+0xa7,0x88,0x27,0x0,0x48,0x3a,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x42,0x29,
+0x5e,0x15,0x4b,0x99,0x16,0x0,0xc6,0xdd,0xaf,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x14,
+0x42,0x29,0x5e,0xeb,0x7c,0x70,0x38,0x0,0x54,0x68,0xae,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6c,0xff,0x28,0x5e,0x92,0xb1,0x45,0x30,0x0,0x51,0x3a,0xa5,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x5f,0x42,0x29,0x5e,0x23,0xc4,0x8,0x4,0x0,0xcb,0xdd,0xb0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf3,0x41,0x29,0x5e,0x81,0xfd,0x61,0x2a,0x0,0x45,0xad,
+0xb1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc,0x42,0x29,0x5e,0x7e,0x26,0x9a,0x37,0x0,
+0x63,0x8e,0xb1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6c,0xff,0x28,0x5e,0x92,0xb1,0x45,
+0x30,0x0,0xbd,0x50,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xcf,0x41,0x29,0x5e,0xf7,
+0x63,0xce,0x20,0x0,0x9,0xda,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf0,0x42,0x29,
+0x5e,0x2,0xdf,0xdb,0x29,0x0,0x57,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf6,
+0x42,0x29,0x5e,0xc4,0xfa,0x1b,0x3a,0x0,0x44,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x6d,0xf3,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xfc,0x42,0x29,0x5e,0xb2,0x95,0x22,0x6,0x0,0x6d,0xf3,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf0,0x42,0x29,0x5e,0x2,0xdf,0xdb,0x29,0x0,0x45,0xf5,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf2,0x42,0x29,0x5e,0xfb,0xf,0xaa,0x3a,0x0,
+0x45,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf1,0x42,0x29,0x5e,0x2b,0x23,0xf5,
+0x31,0x0,0x6c,0xf3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x0,0x43,0x29,0x5e,0x5e,
+0xc9,0x34,0x6,0x0,0x6c,0xf3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf2,0x42,0x29,
+0x5e,0xfb,0xf,0xaa,0x3a,0x0,0x6f,0xf3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,
+0x42,0x29,0x5e,0xe2,0xce,0x4e,0xb,0x0,0x59,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0x6d,0xf3,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x44,0xf5,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x76,0xb1,0x42,0x5e,0x48,0x37,0x46,0x1b,0x0,0x9b,0xea,
+0xc2,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf8,0x42,0x29,0x5e,0xea,0x1c,0xeb,0x6,0x0,
+0x6c,0xf3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0x4d,0x7c,0x6e,
+0xf,0x0,0x6f,0xf3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x79,0x42,0x29,0x5e,0x7d,
+0x4c,0x53,0x16,0x0,0x9b,0x4e,0xb8,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xff,0x28,
+0x5e,0xd3,0xba,0x9e,0x3a,0x0,0x47,0x62,0x8f,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf2,
+0x42,0x29,0x5e,0xfb,0xf,0xaa,0x3a,0x0,0x4e,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xfc,0xa2,0x2a,0x5e,0xbe,0x8c,0xc6,0xc,0x0,0xe7,0x5e,0xc2,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xfa,0x42,0x29,0x5e,0x22,0xeb,0xc3,0x38,0x0,0x74,0xf3,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x58,0xa,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf5,0x42,0x29,0x5e,0x51,0x94,0xcb,0x31,0x0,
+0x70,0xf3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3,0x43,0x29,0x5e,0x6f,0x5f,0xb6,
+0x37,0x0,0x8b,0xf3,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x0,0x43,0x29,0x5e,0x5e,
+0xc9,0x34,0x6,0x0,0x50,0xc5,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,0x43,0x29,
+0x5e,0xc0,0x10,0x91,0xd,0x0,0x8c,0xf3,0xa7,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfc,
+0x42,0x29,0x5e,0xb2,0x95,0x22,0x6,0x0,0x48,0xf5,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x30,0x43,0x29,0x5e,0xc0,0x42,0x65,0x1a,0x0,0x6f,0xf3,0xa7,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x12,0x42,0x29,0x5e,0xa5,0xb2,0x71,0x2a,0x0,0x42,0xdd,0xac,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4,0x42,0x29,0x5e,0xc6,0x56,0xc1,0x14,0x0,0x16,0xd6,
+0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x77,0xff,0x28,0x5e,0xf,0x85,0x2,0x9,0x0,
+0xc8,0x30,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xed,0x41,0x29,0x5e,0x6a,0xa,0x3b,
+0x37,0x0,0xe3,0x6,0xb1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xeb,0x41,0x29,0x5e,0xbe,
+0x97,0x35,0x27,0x0,0xd9,0xd9,0xaf,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6c,0xff,0x28,
+0x5e,0x92,0xb1,0x45,0x30,0x0,0xc5,0x30,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5d,
+0xff,0x28,0x5e,0x6b,0x14,0x3e,0x9,0x0,0xe9,0x30,0xa5,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x42,0x29,0x5e,0x28,0x74,0xe5,0x1b,0x0,0x4c,0xd6,0xaf,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4d,0xff,0x28,0x5e,0xc0,0x1f,0x5,0x1c,0x0,0xc1,0x30,0xa5,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x47,0x42,0x29,0x5e,0xc7,0xab,0x3,0x8,0x0,0xd1,0x15,
+0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xce,0xff,0x28,0x5e,0xad,0x3e,0x52,0x2a,0x0,
+0x19,0x48,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x86,0xff,0x28,0x5e,0x9,0xa7,0x88,
+0x27,0x0,0x51,0x41,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x42,0x29,0x5e,0x40,
+0xc,0xb6,0xb,0x0,0xaa,0x7c,0xa9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,
+0x5e,0x1c,0xc7,0xdf,0x4,0x0,0x35,0xe5,0x10,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,
+0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0xc0,0x4e,0x29,0x32,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x90,0xff,0x28,0x5e,0x69,0xf0,0xf7,0x39,0x0,0x89,0x45,0xa6,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x83,0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0x0,0x55,0xe1,0x42,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xaa,0xff,0x28,0x5e,0x86,0x11,0xd0,0x27,0x0,0xb4,0x30,
+0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0x80,
+0xa3,0xb,0x15,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0xff,0x28,0x5e,0x86,0x11,0xd0,
+0x27,0x0,0x3b,0xdd,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x17,0xfa,0x5d,0x40,
+0x20,0x69,0x19,0xc0,0xb2,0x8,0x3d,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x22,0x43,0x29,
+0x5e,0x97,0x3e,0xf8,0x29,0x0,0xe0,0x6,0xb1,0xb,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,
+0xff,0x28,0x5e,0x86,0x11,0xd0,0x27,0x0,0xc7,0x30,0xa5,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x77,0xff,0x28,0x5e,0xf,0x85,0x2,0x9,0x0,0xc0,0x3a,0xa6,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe8,0x41,0x29,0x5e,0x53,0x6,0xe1,0x2e,0x0,0x6a,0xd8,0xb0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xa0,0xff,0x28,0x5e,0xc,0x1b,0x92,0x16,0x0,0xae,0x30,
+0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x41,0x29,0x5e,0x5c,0x7d,0x8c,0x28,0x0,
+0xe,0xdb,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,
+0x19,0x48,0x26,0x4f,0x7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8b,0xff,0x28,0x5e,0x13,
+0xee,0x3e,0x32,0x0,0x54,0x31,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5b,0x43,0x29,
+0x5e,0x75,0xd1,0x5,0x0,0x0,0xe5,0xa2,0xfc,0xf,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,
+0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0x80,0x17,0x21,0x14,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x24,0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0x80,0x29,0xda,0x42,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x5e,0x41,0x29,0x5e,0x7a,0x2b,0x45,0x1a,0x0,0x3e,0xd6,0xaf,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0x0,0x4e,0x30,
+0x32,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x84,0x25,0x5e,0x28,0xfe,0x2d,0x6,0x80,
+0x5f,0x4e,0x25,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0xd3,0x26,0x5e,0x1c,0xc7,0xdf,
+0x4,0xe0,0x5c,0xab,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,
+0xb0,0xa7,0x19,0x80,0x18,0xab,0x32,0x7,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x84,0x25,
+0x5e,0x28,0xfe,0x2d,0x6,0x80,0xff,0xea,0x42,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc4,
+0xff,0x28,0x5e,0xc,0xb5,0x7f,0x1a,0x80,0x6,0xbd,0x5c,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x67,0xff,0x28,0x5e,0x80,0x3e,0x6e,0x25,0x0,0xda,0x60,0xa6,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xdd,0x41,0x29,0x5e,0xb,0x3e,0xd5,0x12,0x0,0xb9,0xf1,0xaf,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xfe,0x41,0x29,0x5e,0x1b,0x1d,0x93,0x4,0x0,0x0,0x90,
+0xb1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5e,0x56,0x29,0x5e,0x33,0xa,0x88,0x33,0x80,
+0x27,0x3,0x7b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xee,0x41,0x29,0x5e,0xa4,0xf6,0x95,
+0x20,0x0,0xc6,0xb2,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe8,0x6c,0x2c,0x5e,0xa7,
+0x3d,0x5d,0x14,0xc0,0xf2,0x8e,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x67,0xff,0x28,
+0x5e,0x80,0x3e,0x6e,0x25,0x0,0x11,0xe1,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,
+0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0x40,0x19,0xfd,0x1a,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0x40,0x8a,0x49,0x12,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x40,0x3b,0xa7,0x3f,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x52,0xff,0x28,0x5e,0x42,0x6d,0xd0,0x29,0x0,0xa,0x48,
+0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc0,0x41,0x29,0x5e,0x8f,0x83,0x20,0x6,0x0,
+0xb2,0xdb,0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x42,0x29,0x5e,0x64,0x4,0x42,
+0x16,0x0,0x66,0xae,0xb1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xce,0xff,0x28,0x5e,0xad,
+0x3e,0x52,0x2a,0x0,0xf1,0x68,0xa5,0x7,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe8,0x6c,0x2c,
+0x5e,0xa7,0x3d,0x5d,0x14,0x0,0xf2,0x1d,0x1f,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,
+0xd3,0x26,0x5e,0x1c,0xc7,0xdf,0x4,0x0,0xbf,0xf5,0x47,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0x20,0xb8,0x36,0xd,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xcb,0x42,0x29,0x5e,0x73,0x3f,0x8a,0x25,0x0,0xad,0xdb,0xaf,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x68,0x42,0x29,0x5e,0x98,0xe1,0x6e,0xd,0x0,0x26,0xe7,
+0xaf,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,
+0xb7,0xe1,0xb0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,
+0x7,0x0,0x6f,0xea,0x39,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe8,0x6c,0x2c,0x5e,0xa7,
+0x3d,0x5d,0x14,0xc0,0xc,0x64,0x1d,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,
+0x5d,0x6e,0xb9,0x8f,0x7,0xe0,0xd0,0x21,0xe,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe8,
+0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0x80,0x9c,0x88,0x17,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0x80,0xd8,0xe8,0x46,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe8,0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0x40,0x15,0xcf,0x1a,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0x80,0x6c,0xdf,
+0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x0,
+0x2,0xb9,0x41,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe7,0x6c,0x2c,0x5e,0xf8,0x76,0x94,
+0xe,0x0,0x6e,0x6f,0x15,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,
+0xb0,0xa7,0x19,0x20,0x1c,0xed,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,
+0x5d,0x6e,0xb9,0x8f,0x7,0xd0,0xbb,0x1d,0xe,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe8,
+0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0x40,0x36,0x29,0x1c,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x0,0x7d,0xc9,0x16,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0xa0,0xde,0x52,0x12,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe8,0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0xc0,0xc5,0x2,
+0x11,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x60,
+0xc8,0x2b,0x16,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,
+0xe,0x0,0x93,0x81,0xa2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,
+0xb0,0xa7,0x19,0x0,0x6c,0xe3,0x4d,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,
+0x5e,0x25,0xde,0xa,0xe,0x40,0xc6,0x36,0x29,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe8,
+0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0x0,0x49,0xa8,0x15,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x80,0x5a,0x23,0x1c,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4b,0x92,0x1c,0x5e,0x2d,0xb0,0xa7,0x19,0xc0,0x48,0x40,0x28,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x80,0xb5,0xdf,
+0x42,0x7,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x80,
+0x98,0xe3,0x4b,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x90,0xff,0x28,0x5e,0x69,0xf0,0xf7,
+0x39,0x0,0x11,0x48,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,
+0xb9,0x8f,0x7,0x0,0xc8,0x26,0xd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x62,0xff,0x28,
+0x5e,0x4c,0x64,0x91,0x18,0x0,0x14,0x68,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc8,
+0x41,0x29,0x5e,0x5d,0x99,0xcf,0x24,0x0,0x58,0xdc,0xb0,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x67,0xff,0x28,0x5e,0x80,0x3e,0x6e,0x25,0x0,0xe,0x48,0xa5,0x3,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe7,0x41,0x29,0x5e,0xd1,0xcf,0x49,0x26,0x0,0xb0,0xdb,0xaf,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x9b,0xff,0x28,0x5e,0xc3,0xa0,0x16,0xe,0x0,0x56,0x69,
+0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,0x33,0x32,0x2f,0x0,
+0x1b,0x1f,0xb0,0x4,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1c,0x42,0x29,0x5e,0x27,0x42,0x6e,
+0x17,0x0,0xb3,0xdb,0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa5,0xff,0x28,0x5e,0xcf,
+0xe1,0xd3,0x1e,0x0,0x56,0x69,0xa5,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x81,0xff,0x28,
+0x5e,0x2,0xc2,0xfc,0x1e,0x0,0xe,0x68,0xa5,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xdf,
+0x57,0x2c,0x5e,0xa,0x2d,0x9f,0x11,0x0,0x48,0xa3,0x15,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf3,0x40,0x29,0x5e,0xd1,0x55,0xc2,0x34,0x0,0xb8,0xe1,0xb0,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x80,0xd4,0xe9,0x43,0x12,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x0,0x90,0xec,
+0x80,0x9,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4a,0xff,0x28,0x5e,0xd8,0x29,0x6d,0x21,0x0,
+0xe4,0x6b,0xa0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x91,0x41,0x29,0x5e,0xce,0x76,0xe6,
+0x28,0x0,0x6a,0xae,0x56,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x54,0x42,0x29,0x5e,0x2a,
+0xe3,0xeb,0x2c,0x0,0x15,0xd6,0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb0,0x41,0x29,
+0x5e,0x7c,0xce,0x30,0x0,0x0,0x5d,0xdc,0xb0,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xba,
+0xff,0x28,0x5e,0xa9,0xa0,0x1c,0x5,0x0,0xe,0xb7,0x93,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x70,0xc4,0x2a,0x5e,0x8c,0xd2,0xee,0x18,0x80,0xff,0x4c,0x6b,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xff,0x41,0x29,0x5e,0xa0,0xc3,0x37,0xb,0x0,0xb2,0xa4,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb1,0x41,0x29,0x5e,0x31,0x60,0xb0,0x2d,0x0,0x2e,0x84,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x82,0x0,0x29,0x5e,0x21,0x89,0x40,0x0,0x0,
+0xdd,0xf0,0xe6,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe1,0x41,0x29,0x5e,0xa5,0x7e,0xe3,
+0x13,0x0,0x97,0xb9,0xab,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x63,0x0,0x29,0x5e,0xa,
+0xc8,0x89,0x14,0x0,0x83,0xcc,0xbd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa1,0x41,0x29,
+0x5e,0x7b,0xb1,0x4d,0x3,0x0,0x2d,0x84,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfb,
+0x41,0x29,0x5e,0xb3,0x2,0x4e,0x17,0x0,0x6,0x86,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd9,0x41,0x29,0x5e,0xb3,0x7c,0x9a,0x35,0x0,0x6c,0xd8,0xb0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe7,0x6c,0x2c,0x5e,0xf8,0x76,0x94,0xe,0x0,0x48,0xbe,0x23,0x9,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x0,0x42,0x29,0x5e,0x96,0xaf,0x5b,0x13,0x0,0x49,0x84,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x0,
+0xbd,0xea,0x4f,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,
+0x29,0x0,0x8d,0x5,0x21,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,
+0xde,0xa,0xe,0x0,0x79,0x99,0x6f,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x60,0x41,0x29,
+0x5e,0x10,0xb0,0x7f,0x27,0x0,0xd,0xdb,0xb0,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb,
+0x42,0x29,0x5e,0x38,0xec,0x99,0x2f,0x0,0x2f,0x84,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x80,0x32,0xb7,0x4a,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd9,0x41,0x29,0x5e,0x5b,0xa8,0xcc,0x10,0x0,0xc,0xdb,0xb0,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf,0x42,0x29,0x5e,0x4,0xd,0xd9,0x30,0x0,0xad,0x84,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x42,0x29,0x5e,0xc6,0x56,0xc1,0x14,0x0,
+0x4f,0x85,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfd,0x41,0x29,0x5e,0xe8,0x79,0x8e,
+0x2a,0x0,0x9c,0xd4,0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xab,0x41,0x29,0x5e,0xfe,
+0xe8,0x29,0x1d,0x0,0x35,0xd7,0xaf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x42,0x29,
+0x5e,0x88,0xd8,0x6,0x9,0x0,0xf4,0xb2,0xa8,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,
+0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x60,0x8e,0xa7,0x1f,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd,0x42,0x29,0x5e,0x7d,0xfe,0x9f,0x20,0x0,0x49,0xd7,0xb0,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf6,0x41,0x29,0x5e,0x29,0x9a,0x4d,0x0,0x0,0xd9,0xa2,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xd2,0x41,0x29,0x5e,0xee,0xc1,0x20,0x3a,0x0,0xee,0x86,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xed,0x67,0x2c,0x5e,0xd,0x23,0xca,0x20,0x0,
+0xca,0x2,0x99,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,0x43,0x29,0x5e,0xc0,0x10,0x91,
+0xd,0x0,0x8,0x2e,0xf9,0xe,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf7,0x41,0x29,0x5e,0x41,
+0xaa,0x8a,0x8,0x0,0xed,0x86,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x41,0x29,
+0x5e,0x5c,0x7d,0x8c,0x28,0x0,0x25,0x87,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa,
+0x42,0x29,0x5e,0xb5,0x58,0x4b,0x27,0x0,0xdc,0xdc,0xb0,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6f,0x42,0x29,0x5e,0x70,0x7b,0xc0,0x6,0x0,0xb0,0x8a,0xa8,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x60,0xe1,0xc1,0x16,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe8,0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0x0,0xf6,0x37,
+0x18,0x9,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x42,0x29,0x5e,0xca,0x31,0xe0,0x28,0x0,
+0x11,0x16,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,
+0x1b,0x0,0x50,0xe2,0x4d,0xa,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,
+0xb9,0x3c,0x8,0x0,0xdf,0xf5,0x4f,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,
+0x57,0xb1,0xb9,0x3c,0x8,0x80,0x84,0xd1,0x76,0x4,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x80,0x51,0xe1,0x6a,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x80,0x49,0x92,0x5f,0x6,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x0,0x89,0x1c,0x52,0x4,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x0,0xdf,0xf5,
+0x4f,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x0,
+0x18,0xd6,0x96,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1b,0x5e,0x2c,0x5e,0xd8,0x2,0xf,
+0x16,0x40,0x7b,0x6d,0x15,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,
+0xb9,0x8f,0x7,0x40,0xea,0x28,0x16,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,
+0x5e,0x25,0xde,0xa,0xe,0x80,0x91,0xea,0x4b,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,
+0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x80,0xbf,0x54,0x6b,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x60,0x40,0x83,0x8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x50,0x23,0x30,0xd,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe8,0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0x60,0xe0,0xb0,
+0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x80,
+0xe8,0x8e,0x2c,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,
+0x13,0x80,0xf8,0x6,0x43,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,
+0xbf,0x0,0x29,0x80,0x4c,0xa9,0x7e,0xb,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,
+0x5d,0x6e,0xb9,0x8f,0x7,0x30,0xd7,0x16,0xe,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,
+0x68,0x2c,0x5e,0xf1,0x3a,0x83,0x20,0xe0,0x9c,0x5c,0x1b,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd9,0xe2,0x2e,0x5e,0x2d,0x5d,0x92,0xb,0xe0,0xdf,0x15,0x10,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x0,0x4e,0x92,0x20,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x80,0xca,0xb0,
+0x64,0x5,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x0,
+0x1e,0xe4,0x4b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,
+0x5,0x80,0x78,0xb6,0x68,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,
+0xb9,0x3c,0x8,0x0,0x92,0xd0,0x82,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,
+0x57,0xb1,0xb9,0x3c,0x8,0x0,0x69,0xa7,0x82,0x8,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1b,
+0x57,0x2c,0x5e,0x1d,0x41,0x74,0x27,0x40,0x6d,0xfb,0x10,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x0,0x69,0xa7,0x82,0x8,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0xc0,0xbe,0xa6,0x3f,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x80,0xce,0x20,
+0x4d,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0xa0,
+0x1c,0x7,0x13,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,
+0x8,0x0,0x78,0xe7,0x4c,0x3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,
+0xb9,0x3c,0x8,0x0,0x7d,0xdc,0x4c,0x8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,
+0x57,0xb1,0xb9,0x3c,0x8,0x0,0xd3,0x97,0x5b,0x9,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,
+0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x80,0x1e,0x19,0x4d,0x2,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x80,0xf9,0x32,0x4f,0x3,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x0,0x4c,0xd5,0x85,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x80,0x5f,0x2f,
+0x14,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x0,
+0x5c,0xec,0x4c,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,
+0x5,0x40,0xf9,0xbf,0xf,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,
+0xb9,0x3c,0x8,0x80,0x1e,0x19,0x4d,0x3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,
+0x57,0xb1,0xb9,0x3c,0x8,0x80,0xfb,0x86,0x4e,0x1,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,
+0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x0,0xe3,0xc4,0x83,0x1,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0xa0,0xb5,0xb2,0x19,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x80,0x33,0xe3,0x43,0x3,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe8,0x6c,0x2c,0x5e,0xa7,0x3d,0x5d,0x14,0x80,0xb1,0xbf,
+0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x90,
+0x9d,0x37,0xd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,
+0xe,0x0,0xe1,0xe3,0xa2,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,
+0x8f,0x15,0x5,0x30,0xd7,0xe2,0xd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,
+0x5d,0xd3,0x4d,0xc9,0x18,0x40,0x68,0xc9,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,
+0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x0,0xe4,0xf9,0x42,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe7,0x6c,0x2c,0x5e,0xf8,0x76,0x94,0xe,0x60,0x71,0xba,0x12,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0xa0,0x4a,0xe,0x1b,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0x10,0xcb,0x3,
+0xe,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x93,0x18,0xfa,0x5d,0x1b,0x60,0x25,0x1b,0x0,
+0xcd,0xa1,0x52,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,
+0xe,0x80,0x97,0x91,0x6f,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,
+0x8f,0x15,0x5,0x0,0xbf,0xeb,0x7a,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,
+0x5e,0xdb,0xe8,0x6a,0x13,0x80,0x82,0xbb,0x3b,0xd,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,
+0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x0,0xb4,0xa3,0x64,0x2,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xaf,0x61,0x2c,0x5e,0x4,0x50,0x7a,0xf,0x0,0x41,0x7e,0x99,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xad,0x2c,0xfb,0x5d,0x6e,0xb9,0x8f,0x7,0xa0,0x70,0x18,0x16,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x0,0x31,0x8d,
+0xa6,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0xa0,
+0x78,0xf3,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,
+0x5,0x80,0x0,0xca,0x7d,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,
+0xe8,0x6a,0x13,0x20,0xe8,0x86,0x8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,
+0x5e,0x9f,0xbf,0x0,0x29,0x0,0x7d,0xf8,0x42,0x7,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,
+0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0x0,0x19,0xa2,0x8,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x0,0xfb,0xde,0x6c,0xb,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x0,0xf1,0x31,0x7b,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x48,0x56,0x29,0x5e,0x91,0xdc,0x30,0x33,0x0,0x2f,0x17,
+0x76,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x40,
+0x31,0xbf,0x24,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,
+0x18,0x40,0x6e,0xd3,0x24,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,
+0xde,0xa,0xe,0x0,0xc6,0xe2,0x4b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,
+0x57,0xb1,0xb9,0x3c,0x8,0x0,0x78,0xe7,0x4c,0x2,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,
+0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x0,0xd6,0x8c,0x66,0x6,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x40,0x6c,0xe0,0x12,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x80,0x1b,0x9b,0x20,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0x0,0xc9,0x1e,
+0x14,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x80,
+0x81,0x59,0x6d,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,
+0x8,0x0,0x2a,0xca,0x82,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,
+0xb9,0x3c,0x8,0x0,0x69,0xc,0x9d,0xd,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0x1d,0xac,
+0x57,0xb1,0xb9,0x3c,0x8,0x80,0x65,0xa9,0x72,0x0,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,
+0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x80,0x23,0xe,0x4d,0x7,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x80,0x1e,0x19,0x4d,0x4,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xaa,0x1d,0xac,0x57,0xb1,0xb9,0x3c,0x8,0x80,0x23,0xe,0x4d,0x8,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0x90,0x44,
+0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,
+0xad,0xa4,0x50,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,
+0x5,0xc0,0x95,0x95,0x3d,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,
+0xa8,0xf9,0x1b,0x0,0xba,0xba,0x84,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,
+0x57,0x20,0xa8,0xf9,0x1b,0x80,0x8d,0x11,0x52,0x2,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xe9,0xb5,0xa9,0x9,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x80,0xf1,0x6a,0x51,0x0,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x1,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xba,0xba,
+0x84,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,
+0xdc,0xca,0x85,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,
+0x1b,0x80,0xf1,0x6a,0x51,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,
+0xe8,0x6a,0x13,0x40,0x57,0xe1,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,
+0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x6,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x5,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x7d,0x3f,0x86,0x4,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0x40,0xd8,0xab,0x28,0x5,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0xc0,0xc8,0xac,
+0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,
+0xba,0xba,0x84,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,
+0x1b,0x0,0xdc,0xca,0x85,0x8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,
+0xa8,0xf9,0x1b,0x0,0xdc,0xca,0x85,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,
+0x57,0x20,0xa8,0xf9,0x1b,0x0,0xba,0xba,0x84,0xc,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x1e,0x14,0x84,0x9,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x80,0x8d,0x11,0x52,0x6,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x4,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,
+0x4f,0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,
+0x7d,0x3f,0x86,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,
+0x1b,0x0,0x2f,0xe6,0x4f,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,
+0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,
+0x57,0x20,0xa8,0xf9,0x1b,0xf0,0x67,0x6d,0x4,0x0,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x80,0x8d,0x11,0x52,0x3,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x0,0xb1,0xf1,0x5a,0x2,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x7d,0x3f,0x86,0xa,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xba,0xba,
+0x84,0xa,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x0,
+0x7e,0xfd,0x7d,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,
+0x1b,0x0,0x2f,0xe6,0x4f,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,
+0xe8,0x6a,0x13,0x80,0x90,0xc6,0x3b,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,
+0x5e,0x9f,0xbf,0x0,0x29,0x0,0x0,0xb,0x48,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x7d,0x3f,0x86,0x4,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0xc,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x5,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x1e,0x14,
+0x84,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,
+0xba,0xba,0x84,0x7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,
+0x18,0x60,0xbf,0x86,0x8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,
+0xa8,0xf9,0x1b,0x80,0x17,0xaa,0x75,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,0x5,0x28,
+0x5e,0x25,0xde,0xa,0xe,0x0,0x1d,0x83,0x58,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x44,0x2a,0x9f,0x8,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x7d,0x3f,0x86,0xb,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x80,0x8d,0x11,0x52,0x1,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xdc,0xca,
+0x85,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,
+0xdc,0xca,0x85,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,
+0x1b,0x0,0x2f,0xe6,0x4f,0x8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd9,0xe2,0x2e,0x5e,0x2d,
+0x5d,0x92,0xb,0x80,0x2e,0x34,0x46,0x6,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,
+0x57,0x20,0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x3,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xba,0xba,0x84,0x4,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x7d,0x3f,0x86,0x12,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x80,0x9d,0x92,0x5f,0x1,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xba,0xba,
+0x84,0xc,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf3,0x41,0x29,0x5e,0x81,0xfd,0x61,0x2a,0x0,
+0xc,0xbc,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,
+0x1b,0x80,0x8d,0x11,0x52,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,
+0xa8,0xf9,0x1b,0x80,0xf1,0x6a,0x51,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,
+0x57,0x20,0xa8,0xf9,0x1b,0x80,0x8d,0x11,0x52,0x2,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x80,0x8d,0x11,0x52,0x8,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xfa,0xfd,0x86,0x3,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x80,0x8d,0x11,0x52,0x0,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0x7d,0x3f,
+0x86,0x3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,
+0xaf,0x21,0x53,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,0x5e,0x9f,0xbf,0x0,
+0x29,0x40,0x40,0xec,0x2c,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,
+0xa8,0xf9,0x1b,0x0,0x2f,0xe6,0x4f,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x1d,0xac,
+0x57,0x20,0xa8,0xf9,0x1b,0x0,0x9c,0xd2,0x84,0x9,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,
+0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xaf,0x21,0x53,0x0,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0xc0,0x8e,0xdd,0x12,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xb8,0x1d,0xac,0x57,0x20,0xa8,0xf9,0x1b,0x0,0xfa,0xfd,0x86,0x9,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x80,0xd3,0x46,
+0x46,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,
+0x8d,0x11,0x52,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,
+0x1e,0x80,0xf1,0x6a,0x51,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,
+0xa7,0xb2,0x1e,0x0,0xaf,0x21,0x53,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,
+0x57,0x55,0xa7,0xb2,0x1e,0x0,0x7d,0x3f,0x86,0x4,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x5,0x28,0x5e,0x25,0xde,0xa,0xe,0x80,0xa0,0x4b,0x6f,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0xc0,0xaf,0x72,0x39,0x4,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0xaf,0x21,0x53,0xb,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,
+0x52,0x8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,
+0x2f,0xe6,0x4f,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x21,0xac,0x57,0xce,0xa8,0x72,
+0x39,0x0,0xba,0xba,0x84,0x9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,
+0xa7,0xb2,0x1e,0x0,0x2f,0xe6,0x4f,0xa,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,
+0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,0x52,0xa,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,
+0x5e,0x20,0x5e,0x9f,0xbf,0x0,0x29,0xe0,0x6c,0xdf,0x12,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x80,0xe8,0xa3,0x5f,0x8,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0xb,0xd0,0x52,0xb,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,
+0x52,0x7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,
+0xdc,0xca,0x85,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,
+0x1e,0x80,0x8d,0x11,0x52,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,
+0x4d,0xc9,0x18,0xe0,0x70,0xb3,0x1f,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,
+0x57,0x55,0xa7,0xb2,0x1e,0x0,0xc6,0x6b,0x9e,0x9,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,
+0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0x2f,0xe6,0x4f,0x6,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0x67,0x74,0xaa,0x3,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0xb,0xd0,0x52,0x8,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xec,0x20,0xac,0x57,0xc,0x63,0x18,0x35,0x0,0x7d,0x3f,
+0x86,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x80,
+0xb2,0x95,0x5d,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,
+0x5,0x80,0x9,0x32,0x62,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,
+0x6c,0x3e,0x24,0x80,0xa9,0x3a,0x5d,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5f,0xee,0xc7,
+0x57,0x69,0x39,0x62,0x33,0x40,0x6e,0x62,0x38,0x1,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,
+0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x80,0x1,0x57,0x37,0x0,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0xc0,0xcd,0xbb,0x1f,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x68,0xee,0xc7,0x57,0x63,0xff,0xd5,0x6,0x80,0xb2,0x95,0x5d,0x2,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x80,0x2c,0xe2,
+0x38,0x8,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x65,0xee,0xc7,0x57,0xb4,0xa,0xf7,0x34,0x80,
+0x76,0x7f,0x5c,0x7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,
+0x24,0x80,0xde,0x98,0x37,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,
+0x6c,0x3e,0x24,0x0,0x84,0x1,0x5d,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,
+0x57,0x86,0x6c,0x3e,0x24,0x0,0x52,0xb8,0x5d,0xa,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,
+0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x80,0x1,0x57,0x37,0x5,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x80,0xf8,0x67,0x26,0x0,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x40,0x6e,0x62,0x38,0x7,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0xc0,0x4c,0x52,
+0x37,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x80,
+0xa9,0x3a,0x5d,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x61,0xee,0xc7,0x57,0xb0,0x67,0xd3,
+0x5,0x80,0xb2,0x95,0x5d,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x63,0xee,0xc7,0x57,0xd8,
+0x66,0x0,0x26,0x40,0x6e,0x62,0x38,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6d,0xee,0xc7,
+0x57,0x86,0x6c,0x3e,0x24,0x40,0x6e,0x62,0x38,0x2,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,
+0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0x20,0x32,0x96,0x8,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x6d,0xee,0xc7,0x57,0x86,0x6c,0x3e,0x24,0x0,0xd3,0x6e,0x38,0x4,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x80,0x49,0x32,0x67,0x2,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x6a,0xee,0xc7,0x57,0x62,0x54,0xe7,0xf,0x80,0x7b,0x74,
+0x5c,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,
+0xaf,0x21,0x53,0x3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,
+0x1e,0x80,0xf1,0x6a,0x51,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,
+0xa7,0xb2,0x1e,0x0,0xfa,0xfd,0x86,0xb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb3,0x5e,0x20,
+0x5e,0x9f,0xbf,0x0,0x29,0x80,0x26,0xa0,0x52,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,
+0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0xfa,0xfd,0x86,0x4,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0xf6,0x69,0x4e,0x2,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0xfa,0xfd,0x86,0x3,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x95,0x7e,
+0x5e,0x3,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x33,0xf3,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,
+0xfa,0xfd,0x86,0xb,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,
+0x1f,0xc0,0x2d,0x31,0x3a,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,
+0x3e,0x48,0x1f,0x80,0x29,0x8,0x3b,0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,
+0x57,0x42,0x3e,0x48,0x1f,0xc0,0x2d,0x31,0x3a,0x4,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,
+0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0xc0,0x2d,0x31,0x3a,0x4,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0x80,0x29,0x8,0x3b,0x4,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0xa0,0x89,0xb8,0x12,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0x20,0x98,0xcd,
+0x1f,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0x0,
+0x45,0xdf,0xa2,0x18,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,
+0x1f,0x80,0x95,0x7e,0x5e,0x7,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,
+0x3e,0x48,0x1f,0x80,0x95,0x7e,0x5e,0x2,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,
+0x57,0x42,0x3e,0x48,0x1f,0xc0,0x2d,0x31,0x3a,0x0,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,
+0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0x0,0xf8,0xe7,0x77,0x2,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x0,0xc2,0xcc,0x62,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0xc0,0x2d,0x31,0x3a,0x1,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0x80,0xdf,0x0,
+0x70,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0xc4,0x2a,0x5e,0xdb,0xe8,0x6a,0x13,0x0,
+0xa1,0x1,0x43,0x8,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,
+0x1f,0x0,0x3e,0xf9,0x5d,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,
+0x3e,0x48,0x1f,0x0,0x3e,0xf9,0x5d,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,
+0x5e,0x54,0x8f,0x15,0x5,0xa0,0x69,0x80,0x17,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,
+0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0x0,0x3e,0xf9,0x5d,0x3,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0x0,0x3e,0xf9,0x5d,0x1,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0x1e,0x14,0x84,0x9,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe0,0xef,0xc7,0x57,0x42,0x3e,0x48,0x1f,0x0,0x1c,0xe9,
+0x5c,0x6,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,
+0x8d,0x11,0x52,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,
+0x1e,0x0,0x7d,0x3f,0x86,0x9,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,
+0x4d,0xc9,0x18,0xe0,0x31,0xa5,0x1f,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,
+0x57,0x55,0xa7,0xb2,0x1e,0x0,0x7d,0x3f,0x86,0x3,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,
+0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,0x52,0x6,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0x0,0x17,0xa4,0x1f,0x1,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,0x52,0x3,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x80,0xca,0xe0,
+0x62,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,
+0x8d,0x11,0x52,0xa,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,
+0x1e,0x80,0x8d,0x11,0x52,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,
+0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,
+0xa7,0xb2,0x1e,0x0,0x7d,0x3f,0x86,0x1,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,
+0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,0x52,0x3,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xc6,
+0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,0x52,0x1,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,0x52,0xc,0x0,0x0,
+0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x0,0xba,0xba,0x84,0x7,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xc6,0x1d,0xac,0x57,0x55,0xa7,0xb2,0x1e,0x80,0x8d,0x11,
+0x52,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0xe0,
+0xe6,0xdb,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,
+0x5,0x40,0x4b,0x38,0x11,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,
+0x4d,0xc9,0x18,0xc0,0x2e,0xdf,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd8,0xe2,0x2e,
+0x5e,0x54,0x8f,0x15,0x5,0x80,0x25,0x47,0x46,0x13,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,
+0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0x60,0x56,0x2d,0x14,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd8,0xe2,0x2e,0x5e,0x54,0x8f,0x15,0x5,0x80,0x82,0x8c,0x63,0x4,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x8,0x95,0xf8,0x5d,0xd3,0x4d,0xc9,0x18,0xa0,0x17,0xd3,0x1f,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x36,0x8d,0xa1,0x57,0x8c,0xc8,0x1c,0x32,0x0,0xcd,0xcc,
+0x4c,0x4,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x22,0x8d,0xa1,0x57,0x14,0x5a,0xdc,0x28,0x0,
+0x5c,0x8f,0x82,0x5,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,0xb1,0x42,0x5e,0xd5,0x17,0x82,
+0x7,0x0,0x3c,0x92,0xc1,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xba,0x42,0x29,0x5e,0x25,
+0xdf,0xce,0x1f,0x0,0xbc,0x60,0xb2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xaa,0xae,0x42,
+0x5e,0xec,0x6e,0x8f,0x20,0x0,0x93,0xe3,0xc2,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,
+0x42,0x29,0x5e,0x26,0x44,0x4c,0x10,0x0,0x8d,0xb5,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf8,0x41,0x29,0x5e,0xa5,0x83,0xf2,0x34,0x0,0xa6,0xff,0xf0,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x47,0xb1,0x42,0x5e,0xb9,0x93,0xaf,0x1,0x0,0xb0,0xe3,0xc2,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xee,0x41,0x29,0x5e,0xa4,0xf6,0x95,0x20,0x0,0x13,0xb3,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf8,0x41,0x29,0x5e,0x1e,0x7b,0x13,0x11,0x0,
+0x88,0xb5,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x22,0x42,0x29,0x5e,0xc8,0xe7,0x5e,
+0x8,0x0,0xb0,0xb2,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8c,0x6c,0x45,0x5e,0xa3,
+0xfa,0x59,0x14,0x0,0xb0,0x18,0x76,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf8,0x41,0x29,
+0x5e,0xa5,0x83,0xf2,0x34,0x0,0x3f,0xb7,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xee,
+0x41,0x29,0x5e,0xa4,0xf6,0x95,0x20,0x0,0xdc,0xb1,0xa8,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x22,0x42,0x29,0x5e,0xc8,0xe7,0x5e,0x8,0x0,0x23,0xb7,0xa8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x17,0x42,0x29,0x5e,0xbb,0xea,0xb0,0x31,0x0,0xf6,0xb2,0xa8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf0,0x42,0x29,0x5e,0x2,0xdf,0xdb,0x29,0x0,0x67,0x1d,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0xb2,0x3,0x33,0xb,0x0,
+0x24,0xb6,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0xe2,0xce,0x4e,
+0xb,0x0,0x9a,0xcb,0xee,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,
+0xc1,0x39,0x32,0x0,0x23,0x1e,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,
+0x5e,0x6c,0x31,0x84,0xc,0x0,0x38,0x63,0xee,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,
+0x43,0x29,0x5e,0xcc,0x2c,0xc8,0x12,0x0,0x87,0xe9,0xa6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0x6a,0x22,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,0x2c,0xc8,0x12,0x0,0x84,0x1c,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,0x33,0x32,0x2f,0x0,0x9a,0x28,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd,0x42,0x29,0x5e,0x7d,0xfe,0x9f,0x20,0x0,
+0xcf,0x13,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3b,0x42,0x29,0x5e,0xb,0xaf,0x1e,
+0x2c,0x0,0x92,0xfa,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfd,0xa2,0x2a,0x5e,0x38,
+0x82,0xb,0x12,0x0,0x80,0x88,0xc3,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7a,0x1f,0x2,
+0x5e,0x9,0xbe,0x29,0x25,0x40,0x87,0xfe,0x19,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,
+0x43,0x29,0x5e,0x8f,0x5c,0x1a,0xd,0x0,0xe9,0x87,0xee,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0x2f,0x22,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xbb,0x42,0x29,0x5e,0xf5,0x27,0x97,0x24,0x0,0xd6,0xf6,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf5,0x42,0x29,0x5e,0x51,0x94,0xcb,0x31,0x0,0x17,0x1e,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,
+0x85,0x23,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,0x1,0x33,
+0x39,0x0,0x9b,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa1,0x42,0x29,0x5e,0x3b,
+0xbd,0x9c,0x2b,0x0,0x1d,0xfd,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x36,0x42,0x29,
+0x5e,0x41,0x9,0x12,0x26,0x0,0xe4,0xf6,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,
+0x57,0x29,0x5e,0x47,0x25,0x2c,0x21,0x0,0x8d,0xb6,0xee,0x1,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xfc,0xa2,0x2a,0x5e,0xbe,0x8c,0xc6,0xc,0x0,0x29,0x89,0xc1,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x4,0x57,0x29,0x5e,0x3d,0xf9,0x1c,0x10,0x0,0x40,0xc3,0xc1,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x39,0x43,0x29,0x5e,0xed,0xa7,0xe0,0x1b,0x0,0xcf,0x1d,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7c,0x55,0x29,0x5e,0x95,0x82,0x6a,0x18,0x0,
+0xaf,0x87,0x8f,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,0x5e,0xcc,0x2c,0xc8,
+0x12,0x0,0x7a,0x9e,0xe7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,
+0x78,0xde,0x32,0x0,0xa0,0xca,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,0x42,0x29,
+0x5e,0x7,0xe0,0x11,0x25,0x0,0x62,0x28,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,
+0x43,0x29,0x5e,0xcd,0x3,0x14,0x3b,0x0,0xee,0x21,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0x1d,0xf6,0xa6,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0x23,0xf8,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x79,0x1f,0x2,0x5e,0xa9,0x19,0xb8,0x20,0x0,0x4d,0x76,
+0x4b,0x3,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0x42,0x29,0x5e,0x70,0x7b,0xc0,0x6,0x0,
+0x28,0xf8,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x60,0x42,0x29,0x5e,0x6,0xbc,0xe1,
+0x2d,0x0,0x34,0x58,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1d,0x42,0x29,0x5e,0xe9,
+0x27,0x60,0x0,0x0,0x4a,0xf5,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1d,0x43,0x29,
+0x5e,0x1,0x96,0xcf,0x24,0x0,0xc9,0xfc,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,
+0x42,0x29,0x5e,0x9c,0x70,0x12,0x39,0x0,0x4a,0xfb,0xa6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0xee,0xd1,0xdc,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xfd,0x42,0x29,0x5e,0x13,0x8d,0x8f,0xe,0x0,0x7d,0x75,0xdd,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xdb,0x42,0x29,0x5e,0xe8,0x1e,0xdb,0x3,0x0,0x34,0x22,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5a,0x42,0x29,0x5e,0x86,0x68,0xf,0x1c,0x0,
+0x3f,0xfa,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,
+0x32,0x0,0xea,0xa,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd,0x42,0x29,0x5e,0x7d,
+0xfe,0x9f,0x20,0x0,0x95,0xb5,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x79,0x42,0x29,
+0x5e,0x7d,0x4c,0x53,0x16,0x0,0xc2,0x1d,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,
+0x43,0x29,0x5e,0x18,0x63,0x66,0x29,0x0,0xc3,0x21,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xa2,0x42,0x29,0x5e,0xb4,0x9b,0x65,0x12,0x0,0x3,0xf7,0xa6,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0xd7,0xbc,0xee,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x5a,0x42,0x29,0x5e,0x86,0x68,0xf,0x1c,0x0,0x64,0x22,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd5,0x42,0x29,0x5e,0x84,0xad,0xe8,0x36,0x0,
+0x87,0xa,0xe7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf6,0x42,0x29,0x5e,0xc4,0xfa,0x1b,
+0x3a,0x0,0x5c,0xb5,0xee,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,
+0x1,0x33,0x39,0x0,0xb,0xf7,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x92,0x42,0x29,
+0x5e,0xf0,0x9d,0xc4,0x14,0x0,0x16,0x1e,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,
+0x42,0x29,0x5e,0x7,0xe0,0x11,0x25,0x0,0xd1,0xfc,0xa6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x4b,0x1f,0x2,0x5e,0x60,0x4f,0x1d,0x29,0xc0,0x2a,0xd8,0x19,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x6d,0x42,0x29,0x5e,0x1b,0xd1,0x1f,0x16,0x0,0x36,0x13,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,0x32,0x20,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,0x42,0x29,0x5e,0x26,0x44,0x4c,0x10,0x0,
+0xc3,0x21,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xbc,0x42,0x29,0x5e,0x3a,0x57,0xf1,
+0xd,0x0,0x2c,0x1c,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x31,0x42,0x29,0x5e,0x23,
+0xce,0x11,0x1d,0x0,0xe1,0x5f,0xe7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,0x42,0x29,
+0x5e,0x9c,0x70,0x12,0x39,0x0,0x27,0x1d,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x74,
+0x42,0x29,0x5e,0x26,0x44,0x4c,0x10,0x0,0xb4,0xa,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0x27,0x1d,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x79,0x42,0x29,0x5e,0x7d,0x4c,0x53,0x16,0x0,0x81,0xb6,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x54,0x43,0x29,0x5e,0x1e,0x30,0x8b,0x8,0x0,0x24,0x7c,
+0xee,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,0xd,0x6b,0x2e,0x0,
+0x40,0xb7,0xee,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x98,0x42,0x29,0x5e,0x24,0xf8,0x10,
+0x4,0x0,0xc6,0xd3,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x42,0x29,0x5e,0x88,
+0xd8,0x6,0x9,0x0,0x27,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x29,0x42,0x29,
+0x5e,0x8a,0x6d,0xa5,0x1d,0x0,0xcf,0xe8,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,
+0x43,0x29,0x5e,0xda,0x6a,0xb2,0x11,0x0,0x7f,0x1d,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xcb,0x42,0x29,0x5e,0x73,0x3f,0x8a,0x25,0x0,0xea,0x94,0xe7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0xc3,0xfa,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x33,0x43,0x29,0x5e,0x1f,0x8a,0x61,0xd,0x0,0xd9,0xdc,
+0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,0x3b,0x0,
+0x92,0xfa,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,0x42,0x29,0x5e,0x54,0x85,0xee,
+0x21,0x0,0xa1,0xf4,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xcb,0x42,0x29,0x5e,0x73,
+0x3f,0x8a,0x25,0x0,0x2f,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7e,0x1f,0x2,
+0x5e,0x5a,0x9f,0xc0,0x12,0x80,0x90,0x9,0x19,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,
+0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0x76,0x8c,0xee,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0xac,0x22,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x1d,0x43,0x29,0x5e,0x1,0x96,0xcf,0x24,0x0,0x87,0x28,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x40,0x42,0x29,0x5e,0x2a,0x4d,0xaf,0x33,0x0,0x72,0xf4,
+0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x43,0x29,0x5e,0xc0,0xaa,0x1,0x7,0x0,
+0x8f,0x94,0xee,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,0x1,0x33,
+0x39,0x0,0xec,0xf9,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,0x42,0x29,0x5e,0xde,
+0x78,0xde,0x32,0x0,0x2f,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x42,0x29,
+0x5e,0x88,0xd8,0x6,0x9,0x0,0xc0,0x2a,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x12,
+0x42,0x29,0x5e,0xa5,0xb2,0x71,0x2a,0x0,0x1,0xbd,0xa6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,0x3,0x25,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf0,0x56,0x29,0x5e,0x83,0xad,0xe6,0x2d,0x0,0x55,0xdd,0xc1,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xbb,0x42,0x29,0x5e,0xf5,0x27,0x97,0x24,0x0,0x89,0xfa,
+0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,0x1b,0x0,
+0xbe,0xd9,0xcd,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb7,0x42,0x29,0x5e,0xdb,0x4e,0xfc,
+0x7,0x0,0xc2,0x1d,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,0x42,0x29,0x5e,0x7,
+0xe0,0x11,0x25,0x0,0x47,0x21,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xa7,0x42,0x29,
+0x5e,0x85,0x43,0x6a,0x1b,0x0,0x3d,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8d,
+0x42,0x29,0x5e,0xee,0x8,0xdd,0x2c,0x0,0xa6,0x8a,0xee,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0xd8,0x21,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0xa2,0xde,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xea,0x57,0x29,0x5e,0x47,0x25,0x2c,0x21,0x0,0xe9,0xf,
+0xe7,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x79,0x1f,0x2,0x5e,0xa9,0x19,0xb8,0x20,0x60,
+0x29,0xa8,0x1e,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x60,0x43,0x29,0x5e,0xd3,0xd7,0xbc,
+0x4,0x0,0x17,0xed,0xe6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9,0x43,0x29,0x5e,0xdd,
+0x27,0x94,0xc,0x0,0xa2,0x19,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x6f,0x42,0x29,
+0x5e,0x70,0x7b,0xc0,0x6,0x0,0xf7,0x18,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x83,
+0x42,0x29,0x5e,0x54,0x85,0xee,0x21,0x0,0x27,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x55,0x42,0x29,0x5e,0x7b,0x3e,0xa8,0x15,0x0,0x1e,0xb6,0xee,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x42,0x42,0x29,0x5e,0xc6,0x4b,0x35,0x0,0x0,0x33,0xfa,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0x1e,0x22,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,0x41,0x29,0x5e,0x1b,0x1d,0x93,0x4,0x0,
+0x71,0xba,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x92,0x42,0x29,0x5e,0xf0,0x9d,0xc4,
+0x14,0x0,0x33,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb,0x43,0x29,0x5e,0x52,
+0xe,0x9a,0x1a,0x0,0x90,0x3e,0xe7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9,0x43,0x29,
+0x5e,0xdd,0x27,0x94,0xc,0x0,0x36,0xfb,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x12,
+0x43,0x29,0x5e,0x69,0x5c,0xad,0x33,0x0,0x5b,0x23,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xb1,0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0x14,0xdd,0xa6,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xa2,0x42,0x29,0x5e,0xb4,0x9b,0x65,0x12,0x0,0x17,0x1e,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x61,0x97,0xdf,0x57,0xaf,0xb2,0x73,0x16,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x36,0x42,0x29,0x5e,0x41,0x9,0x12,0x26,0x0,
+0x33,0xfa,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x9d,0x42,0x29,0x5e,0xa7,0x8b,0xd6,
+0x9,0x0,0x97,0x3e,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x78,0x1f,0x2,0x5e,0xe,
+0x52,0x1b,0x17,0x60,0x40,0x25,0x12,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,
+0x5e,0x78,0x51,0x1f,0x1b,0x0,0xed,0x8a,0xee,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,
+0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,0x1e,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x7d,0x60,0xdc,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x27,0x43,0x29,0x5e,0x18,0x44,0x4c,0x32,0x0,0x11,0xf4,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x27,0x42,0x29,0x5e,0xf2,0xb7,0x8e,0xf,0x0,0x4c,0xf3,
+0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb8,0x43,0x29,0x5e,0xbf,0xe8,0x62,0x33,0x80,
+0x9f,0xe2,0x74,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5b,0x43,0x29,0x5e,0x75,0xd1,0x5,
+0x0,0x0,0x81,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,
+0xc5,0x8d,0x29,0x0,0x37,0x22,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x40,0x42,0x29,
+0x5e,0x2a,0x4d,0xaf,0x33,0x0,0x72,0xfa,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,
+0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,0x6d,0xf7,0xa6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc3,0x42,0x29,0x5e,0x66,0x39,0x85,0x27,0x0,0x0,0x3e,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,0x9a,0x51,0x8,0x0,0x1c,0x3e,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x72,0xfa,
+0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,
+0xb0,0x3a,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x8,0x42,0x29,0x5e,0x64,0x4,0x42,
+0x16,0x0,0x4d,0xed,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x43,0x29,0x5e,0x18,
+0x63,0x66,0x29,0x0,0xe2,0xf3,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,0x42,0x29,
+0x5e,0x9c,0x70,0x12,0x39,0x0,0xb8,0xfa,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xba,
+0x42,0x29,0x5e,0x25,0xdf,0xce,0x1f,0x0,0xf8,0xfb,0xa6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xe,0x43,0x29,0x5e,0xda,0x6a,0xb2,0x11,0x0,0x59,0x8a,0xee,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x8a,0x42,0x29,0x5e,0xae,0x28,0x34,0x34,0x0,0x26,0xfb,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x6e,0x42,0x29,0x5e,0xdf,0xf,0xa1,0x1e,0x0,0xca,0xcc,
+0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfd,0xa2,0x2a,0x5e,0x38,0x82,0xb,0x12,0x0,
+0x2,0xae,0xc1,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,
+0x1b,0x0,0x94,0x84,0xee,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,0x42,0x29,0x5e,0x88,
+0xd8,0x6,0x9,0x0,0x24,0xfd,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x24,0x43,0x29,
+0x5e,0xbc,0x6a,0xae,0x3a,0x0,0xb5,0x4,0xe7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb1,
+0x42,0x29,0x5e,0xde,0x78,0xde,0x32,0x0,0xa5,0xe8,0xa6,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x0,0x42,0x29,0x5e,0x96,0xaf,0x5b,0x13,0x0,0x80,0x43,0xdc,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x74,0x42,0x29,0x5e,0x26,0x44,0x4c,0x10,0x0,0xca,0xdc,0xa6,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,0x26,0x5c,
+0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,0xc1,0x39,0x32,0x0,
+0x9d,0x9a,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x92,0x42,0x29,0x5e,0x13,0xb7,0x6a,
+0x36,0x0,0x8d,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x57,0x29,0x5e,0x3d,
+0xf9,0x1c,0x10,0x0,0x5f,0x23,0xc2,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x5f,0x42,0x29,
+0x5e,0x2a,0xb5,0xc9,0x25,0x0,0xca,0xb0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,
+0x3e,0xd2,0x58,0x53,0x16,0x6f,0x31,0x0,0x5c,0x8f,0x82,0x45,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0xfb,0x99,0xef,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,0xc5,0x8d,0x29,0x0,0xbf,0x90,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,0x33,0x32,0x2f,0x0,0x2,0xaa,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,
+0x36,0x90,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,0xd,0x6b,
+0x2e,0x0,0x75,0xab,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf9,0x42,0x29,0x5e,0xe0,
+0xc1,0x39,0x32,0x0,0xb1,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x13,0x43,0x29,
+0x5e,0xd6,0xae,0x40,0x1a,0x0,0x31,0x86,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xfe,
+0x3d,0xd2,0x58,0xb8,0x81,0x9c,0x1e,0x80,0x97,0x6e,0x72,0x55,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x69,0x42,0x29,0x5e,0x62,0x1,0x33,0x39,0x0,0x34,0x99,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xa,0x43,0x29,0x5e,0xf,0x89,0xed,0x11,0x0,0x2f,0x74,0xe8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe,0x43,0x29,0x5e,0xda,0x6a,0xb2,0x11,0x0,0x66,0x83,
+0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,
+0x87,0x9b,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x1d,0x43,0x29,0x5e,0x1,0x96,0xcf,
+0x24,0x0,0xdb,0x9c,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x36,0x44,0x29,0x5e,0x51,
+0xf1,0x10,0x1f,0x0,0x5a,0xa0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe5,0x42,0x29,
+0x5e,0xeb,0xc6,0x70,0x15,0x0,0x15,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x7,
+0x3e,0xd2,0x58,0x79,0x2d,0xc8,0x11,0x0,0x27,0x31,0xa8,0x33,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xef,0x42,0x29,0x5e,0x50,0xb6,0x15,0x21,0x0,0xfb,0xaa,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0x47,0x76,0xe8,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x7e,0x42,0x29,0x5e,0x10,0xab,0x9f,0x1c,0x0,0xeb,0xa9,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0xe2,0xce,0x4e,0xb,0x0,
+0xc0,0xab,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x79,0x42,0x29,0x5e,0x7d,0x4c,0x53,
+0x16,0x0,0xc6,0xc4,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x39,0x43,0x29,0x5e,0xed,
+0xa7,0xe0,0x1b,0x0,0x9e,0xaa,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3c,0x43,0x29,
+0x5e,0xcc,0x2c,0xc8,0x12,0x0,0x8f,0xa0,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,
+0x3e,0xd2,0x58,0x3,0xed,0x98,0x1a,0x0,0x27,0x31,0xa8,0x4d,0x0,0x0,0x0,0xff,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xc1,0x42,0x29,0x5e,0x84,0x84,0xe3,0x16,0x0,0xd4,0xa9,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,0x1e,0x5e,0xde,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x40,0x42,0x29,0x5e,0x2a,0x4d,0xaf,0x33,0x0,0x1b,0xb1,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,0x33,0x32,0x2f,0x0,
+0x3c,0x5d,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xea,0x42,0x29,0x5e,0x6b,0x4d,0x3b,
+0x1b,0x0,0x15,0x76,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,
+0xc5,0x8d,0x29,0x0,0xeb,0xa9,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x3,0x3e,0xd2,
+0x58,0x9a,0xe8,0xd,0x28,0x0,0x27,0x31,0xa8,0x36,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x21,
+0x43,0x29,0x5e,0xd7,0x2e,0x60,0x5,0x0,0x87,0x84,0xef,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xfc,0x42,0x29,0x5e,0xb2,0x95,0x22,0x6,0x0,0x3d,0xb8,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xd0,0x42,0x29,0x5e,0x41,0xd,0x6b,0x2e,0x0,0xb7,0xa1,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,0xa5,0xb7,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe0,0x42,0x29,0x5e,0x6c,0x31,0x84,0xc,0x0,
+0x51,0x36,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4,0x43,0x29,0x5e,0xc0,0xaa,0x1,
+0x7,0x0,0x96,0x59,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf4,0x42,0x29,0x5e,0xe,
+0xc5,0x8d,0x29,0x0,0x7c,0x9b,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x3e,0xd2,
+0x58,0x3,0xed,0x98,0x1a,0x0,0x5c,0x8f,0x82,0x48,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,
+0x42,0x29,0x5e,0x50,0xb6,0x15,0x21,0x0,0xb1,0x88,0xef,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xd8,0x42,0x29,0x5e,0x42,0x18,0xd0,0xc,0x0,0xbc,0xaa,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xa7,0x42,0x29,0x5e,0x85,0x43,0x6a,0x1b,0x0,0xae,0xaa,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x37,0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,0xd4,0x8e,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,0x43,0x29,0x5e,0x18,0x44,0x4c,0x32,0x0,
+0xbd,0x33,0xe8,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe1,0x42,0x29,0x5e,0x83,0xa6,0x29,
+0x15,0x0,0xed,0x78,0xef,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x5c,0x87,0x57,0xbd,
+0x86,0x63,0x2d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,
+0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x3e,0xd2,
+0x58,0x3,0xed,0x98,0x1a,0x0,0x27,0x31,0xa8,0x35,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x4b,
+0x42,0x29,0x5e,0x88,0xd8,0x6,0x9,0x0,0x16,0xab,0xa7,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x50,0x43,0x29,0x5e,0xae,0x6e,0x8a,0x2e,0x0,0x2b,0x8b,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xc6,0x42,0x29,0x5e,0xfb,0x68,0xd7,0x1f,0x0,0xc7,0xa6,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x18,0x43,0x29,0x5e,0x6a,0xc2,0x96,0x1f,0x0,0xbb,0xb7,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x1,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x64,0x42,0x29,0x5e,0x96,0x33,0x32,0x2f,0x0,
+0x3,0xd2,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xac,0x42,0x29,0x5e,0x7,0xe0,0x11,
+0x25,0x0,0xbb,0x97,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x32,0x43,0x29,0x5e,0xb0,
+0x9a,0x51,0x8,0x0,0xc9,0x7e,0xe8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x3e,0xd2,
+0x58,0x3,0xed,0x98,0x1a,0x0,0x27,0x31,0xa8,0x34,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x41,
+0x43,0x29,0x5e,0x78,0x51,0x1f,0x1b,0x0,0xd9,0x77,0xef,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0x60,0x43,0x29,0x5e,0xd3,0xd7,0xbc,0x4,0x0,0x2,0xaa,0xa7,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x12,0x42,0x29,0x5e,0xa5,0xb2,0x71,0x2a,0x0,0xd8,0x74,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0xb,0x43,0x29,0x5e,0x52,0xe,0x9a,0x1a,0x0,0x3f,0xa0,
+0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,0x43,0x29,0x5e,0x18,0x44,0x4c,0x32,0x0,
+0xe2,0xf,0xde,0x1,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0xe2,0xce,0x4e,
+0xb,0x0,0xe,0xa7,0xa7,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xb0,0x42,0x29,0x5e,0xc0,
+0x1d,0xc4,0x1,0x0,0x6,0x9f,0xa6,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xe,0x3e,0xd2,
+0x58,0x3,0xed,0x98,0x1a,0x0,0x5c,0x8f,0x82,0x30,0x0,0x0,0x0,0xff,0xff,0xff,
+0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x37,
+0x43,0x29,0x5e,0xc0,0x10,0x91,0xd,0x0,0x13,0x78,0xef,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xef,0x42,0x29,0x5e,0x50,0xb6,0x15,0x21,0x0,0x8d,0x32,0xe8,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0x6f,0x42,0x29,0x5e,0x70,0x7b,0xc0,0x6,0x0,0xab,0xa7,0xa7,0x0,
+0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x8,0x3e,0xd2,0x58,0xb6,0x87,0xc4,0x1b,0x0,0x5c,0x8f,
+0x82,0x48,0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,0x1,0x33,0x39,0x0,
+0x5c,0x1d,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x21,0x43,0x29,0x5e,0xd7,0x2e,0x60,
+0x5,0x0,0x8a,0x25,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xbb,0x42,0x29,0x5e,0xf5,
+0x27,0x97,0x24,0x0,0xf6,0x23,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xf3,0x41,0x29,
+0x5e,0x81,0xfd,0x61,0x2a,0x0,0x67,0x1b,0xaa,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,
+0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x27,
+0x42,0x29,0x5e,0xf2,0xb7,0x8e,0xf,0x0,0x74,0xf5,0xdf,0x0,0x0,0x0,0x0,0xfb,
+0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,
+0x0,0xa9,0x42,0x29,0x5e,0xfe,0x10,0xe,0x2d,0x0,0x9d,0xd1,0xa9,0x0,0x0,0x0,
+0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,0x0,0x3,0x1,0x1c,
+0x0,0x0,0x0,0xe,0x3e,0xd2,0x58,0x3,0xed,0x98,0x1a,0x0,0x5c,0x8f,0x82,0x4b,
+0x0,0x0,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xc3,0x0,0x0,0x0,0x0,0x0,0x3,
+0x1,0x1c,0x0,0x0,0x0,0x69,0x42,0x29,0x5e,0x62,0x1,0x33,0x39,0x0,0x6a,0x1c,
+0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,0x0,0x0,
+0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0xef,0x42,0x29,0x5e,0x50,0xb6,0x15,0x21,0x0,
+0x3f,0x2f,0xa8,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,0x3f,0x0,
+0x0,0x0,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x2c,0x43,0x29,0x5e,0xcd,0x3,0x14,
+0x3b,0x0,0xf9,0x6b,0xe9,0x0,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,0xdf,0xfd,
+0x3f,0x0,0x0,0x1,0x0,0x3,0x1,0x1c,0x0,0x0,0x0,0x55,0x43,0x29,0x5e,0xb7,
+0x88,0x56,0x33,0x0,0x64,0x6,0xf0,0x2,0x0,0x0,0x0,0xfb,0xff,0xac,0xff,0xf8,
+0xdf,0xfd,0x3f,0x0,0x0,0x1,0x0,0xc4,0x5,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb8,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb9,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0x60,0x56,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x26,0xb6,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,
+0x6a,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x8d,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,
+0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x77,0xf3,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x94,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0xa2,0x65,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0x59,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0x32,
+0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xd4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x8d,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,
+0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa8,0x52,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,
+0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0x54,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0x6,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe2,0x27,0x75,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb3,0xe4,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x59,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xac,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x93,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0x1a,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0x48,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,
+0xe4,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xce,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x2a,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,
+0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7a,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc5,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0x59,0x21,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x6a,0x84,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x8b,
+0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xd3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x92,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,
+0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6d,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xeb,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0x73,0x4c,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0x15,0x30,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa7,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xce,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x63,0xf0,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xdf,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcf,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0xb7,0x7f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x15,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,
+0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xdf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe5,0xf7,0x67,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,
+0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x97,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe8,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x8c,0x8,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0x49,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xdf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9,0x3c,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,
+0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x7b,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xb3,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,0x49,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xeb,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xd3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc,0x84,0x59,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,
+0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x56,0xad,0x5c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xac,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x32,0x61,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x49,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,
+0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xd9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb5,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,
+0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb1,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc8,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf7,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x6b,0x4d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x49,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xdf,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x70,0x30,0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x53,0xc6,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,
+0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xb3,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd0,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xd3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9b,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,
+0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x93,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc4,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9a,0xc4,0x1f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x57,0x3f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa,
+0x4b,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdc,0x5e,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,
+0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb3,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xda,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0xd5,0x2b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0x74,
+0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xe6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf7,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf1,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,
+0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x49,0x0,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe9,0x26,0x84,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x7,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa2,0x1d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc0,0xef,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdc,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0xd5,0x2b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xb3,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,
+0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x2,0x8c,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,
+0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0x49,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x3e,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0xc4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa5,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9f,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0x4a,0x3a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x49,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x9,0xc8,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa2,0x1d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3d,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,
+0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd7,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0xb3,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xb3,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,
+0xe4,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xce,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x96,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,
+0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x81,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x93,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0xb3,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0xf5,
+0x37,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xce,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x88,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,
+0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xaf,0xb3,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,
+0x80,0x8e,0xa1,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xea,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0x86,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0x5a,0x41,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x92,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x94,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,
+0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb0,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbf,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x86,0x4,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x86,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x23,
+0x9d,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xcf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6e,0x9d,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,
+0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa2,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcc,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x86,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,0x86,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0x8b,
+0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,0xd3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xae,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,
+0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x64,0xad,0x5c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xb7,0x7f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0xc6,0x68,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb6,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xcf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd5,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,
+0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xb5,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbc,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x8b,0x8,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0x15,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,
+0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xcf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb4,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,
+0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb2,0x86,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe5,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb8,0x86,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x86,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xc4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb3,0x5a,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,
+0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x26,0x5b,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,
+0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0x86,0x4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x86,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xcf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xda,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x41,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,
+0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x16,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc3,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x68,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x78,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x95,
+0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3d,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,
+0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd4,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa3,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x4a,0x3a,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x7e,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe6,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,
+0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x5c,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,
+0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x78,0x26,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0x8b,0x8,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x55,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xd9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa1,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,
+0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xef,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xea,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x68,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xb7,0x7f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,
+0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xdb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9f,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd9,0xf7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x96,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x49,0x0,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x94,0x57,0x54,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,
+0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x50,0x45,0x7c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x75,0x70,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0xb1,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5b,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6,0x4b,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,
+0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xef,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa2,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x7e,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x57,0x3f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,
+0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdf,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,
+0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x40,0xce,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xee,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb1,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x78,0x26,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0xff,
+0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xdb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa7,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xfe,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,
+0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa9,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0x15,0x30,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc7,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd3,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6a,0xad,0x5c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x93,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x11,0x75,0x70,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x68,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,
+0x49,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xdf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,
+0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x85,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdd,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0xc0,0x4d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0x78,
+0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x14,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x25,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,
+0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6f,0x57,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x72,0xa,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0xc7,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x60,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xdb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xcd,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x74,0x70,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa3,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x4a,0x3a,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x49,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,
+0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,0x1d,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5b,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,
+0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x20,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe3,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,0x28,0x50,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x99,0x6d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0x6,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xd4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x77,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,
+0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x68,0x36,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,
+0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x7b,0x47,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xc8,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x76,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,0x16,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x7,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,
+0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x62,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa9,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0x1a,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0xb1,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,
+0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xd4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x2f,0x8a,0x6d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd7,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xeb,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0x39,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xf4,0x68,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xb7,
+0x8a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x30,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,
+0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x60,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,
+0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xb,0x5b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xe8,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x78,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xd4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x5e,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,
+0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xef,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcd,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0x47,0x5e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0xc7,0x12,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,
+0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xd3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc5,0x27,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,
+0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xdf,0xb2,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe2,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xbc,0x3,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xc4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3b,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,
+0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9b,0x90,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,
+0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xef,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x68,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xc8,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x74,0x6b,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x26,0x3,0x61,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,
+0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x97,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc6,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0x6,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0xa4,0x58,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,
+0x8a,0x6d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xe7,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xeb,0x76,0x4a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x19,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdd,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd9,0x27,0x75,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xbd,0x42,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xaa,
+0xf,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x1d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x84,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,
+0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x23,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,
+0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd6,0x73,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0xc8,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x94,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xe2,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc2,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,
+0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x62,0x49,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb9,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xbc,0x3,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xb6,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,
+0x4d,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x7f,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,
+0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf7,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe7,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x5,0x4f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x78,0x87,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0x78,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa1,0xe2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x46,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa1,
+0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb6,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xeb,0xb1,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xe8,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa2,0x14,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3d,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,
+0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x99,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbb,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0xb1,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x71,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,
+0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x3f,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa7,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x83,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x51,
+0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x19,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xed,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,
+0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x2e,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,
+0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdd,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xed,0x73,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0x68,0x21,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x14,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb2,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,
+0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd1,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb1,0x34,0x3b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0xc8,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,
+0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa2,0x16,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xdb,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,
+0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4c,0xbb,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xf2,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x51,0x1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x60,0x87,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xd3,
+0x37,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xb7,0xb6,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,
+0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb0,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,
+0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xba,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0x41,0x61,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x15,0x20,0x3a,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd2,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x18,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x57,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,
+0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x28,0x66,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x93,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0x34,0x3b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xe8,0x4,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8a,
+0xff,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xcd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x99,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,
+0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x72,0x6b,0x75,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xaf,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x78,0x26,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbb,0xc8,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xcd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9f,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,
+0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x15,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,
+0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x99,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x89,0xc8,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x34,0x3b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x39,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x19,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xcd,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,
+0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb7,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x85,0xc0,0x4d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0x39,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xfe,0xe9,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xed,0x80,0x8e,
+0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa6,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc5,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xb6,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x19,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,
+0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x69,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,
+0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x1a,0x15,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x39,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc1,0xb1,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x36,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,0x1f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa6,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,
+0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe7,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xc8,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2a,0x78,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,
+0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x17,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x3f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7b,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe0,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0x59,0x21,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x12,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x12,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x87,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,
+0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa1,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,
+0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0xf4,0x4d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0xc7,0x6e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xc0,0xc9,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x18,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x17,0xcc,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x89,
+0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb1,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0x75,0x70,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x48,0xcf,0x6e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,
+0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xd5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb9,0xd1,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x89,0x8a,
+0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x22,0x3a,0x77,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa1,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xad,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x6,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0xd8,
+0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x18,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x75,0xdb,0x6e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x89,0x8a,0x18,
+0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x14,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,
+0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x59,0x50,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x99,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x70,0xbd,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x79,0x5c,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x89,0x8a,0x18,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x59,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xdc,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xab,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x8b,0x8,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xd5,0x2b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfa,
+0xa4,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,0x18,0x8d,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x91,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xba,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc0,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0xa9,0x22,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0xac,
+0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x18,0x8d,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x8f,0xf0,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6b,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0xaf,0x22,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0xb4,0x22,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x2b,0xc0,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x18,0x8d,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4d,0xa8,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,
+0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xec,0xc7,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc9,0x80,0x8e,0xa2,0x1d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x52,0x29,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0x27,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7f,
+0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa2,0x10,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc3,0x5,0x32,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x89,0x8a,
+0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbb,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9f,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x4b,0x41,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdb,0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0x6,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xc4,
+0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xd2,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x59,0xd,0x32,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x18,
+0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x47,0xce,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,
+0x89,0x8a,0x18,0x8d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,0x12,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xd5,0x2b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x16,0x85,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xe6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xde,0x93,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,
+0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd7,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0x78,0x87,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0x96,0x1d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,
+0x4a,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xe0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x64,0x56,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,
+0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xaf,0x59,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa3,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x31,0x3d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x5e,0xe,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0xa3,
+0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,0x18,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf9,0xe6,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,
+0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x53,0x8a,0x6d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x63,0xe,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaf,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xfc,0xb2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0x66,0xe,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x37,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa1,0xd9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x53,0x69,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,
+0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x6f,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbb,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x72,0xe,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x8b,0x8,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xac,
+0x74,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x18,0x8e,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9,0x77,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,
+0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x97,0xbc,0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xab,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x61,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0x75,0x7b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0xc2,
+0x1d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x18,0x8e,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x4a,0x82,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x18,
+0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xad,0x84,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,
+0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0x32,0x61,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x32,0x60,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd3,0xd9,0xe,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x89,0x8a,0x18,0x8e,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x39,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe8,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x8a,0xe,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x18,0x8e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x29,0x28,0x16,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,
+0x2a,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x89,0x8a,0x18,0x8b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xad,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,
+0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x2a,0xf8,0x67,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xed,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x49,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xf0,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x2d,
+0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x18,0x8b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xbc,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,
+0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x62,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,
+0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x30,0x16,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9f,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x1a,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x8d,0x17,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x6e,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x10,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe1,0x10,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,
+0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x93,0x32,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa3,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x15,0x30,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd2,0xb4,0x54,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,
+0x13,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe,0x7a,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,
+0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6e,0x8,0x78,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x95,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x6,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0xdb,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0xbc,
+0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xe3,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xab,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,
+0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x84,0x16,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,
+0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x56,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x32,0x17,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xa,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x34,0x3a,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,0x18,0x8b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xbf,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,
+0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x4b,0x3a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa3,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xde,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x97,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0x49,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,
+0x18,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9c,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,
+0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa4,0xc,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x97,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x94,0x3c,0x16,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaf,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x15,0x30,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xf0,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x61,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,
+0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1d,0xe1,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,
+0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xf,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x1b,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x87,0xe3,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x18,0x8f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf2,0x3f,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,
+0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x71,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd3,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x32,0x61,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x11,0x6,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,
+0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xd5,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9a,0x23,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,
+0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xaa,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdc,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc5,0xe6,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa3,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xc4,0x1f,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x9b,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xd4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x51,0x42,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x18,
+0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x68,0x26,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,
+0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x7c,0x86,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0xf0,0x7,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x78,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x10,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4f,0xeb,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,
+0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x14,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x15,0x30,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,
+0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xd3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x58,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,
+0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xba,0x47,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbb,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xff,0x3f,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x48,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xd6,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x45,0x29,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x19,
+0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x96,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdb,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xed,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x28,0x19,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa2,0x7c,0x86,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xe0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4e,0xd9,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,
+0x8e,0xa1,0xdf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdd,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0x2,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x45,0x7c,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,
+0x4d,0x6a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xed,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,
+0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7a,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xce,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0xb5,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x8d,0x33,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0xd5,
+0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xdb,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x43,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1a,0x4a,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,
+0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xae,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xec,0xb5,0x58,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf4,0xb5,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xea,0x80,0x8e,0xa1,0xdf,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x75,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,
+0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xeb,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0x15,0x30,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xd9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x81,0xd1,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,
+0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5e,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x2f,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb3,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x62,0x49,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x34,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xbd,
+0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xcd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9e,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd0,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,
+0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0x99,0x59,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0xc8,0x5,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa7,0x2b,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x89,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2b,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbe,0x80,
+0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0x60,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x95,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xf1,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x6,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x16,
+0x7b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xcd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x9d,0x9b,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,
+0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x88,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xaa,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x1d,0x78,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0xc8,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xa4,
+0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xcc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1e,0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,
+0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xcd,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,
+0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xad,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0x8b,0x8,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0x1c,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x85,0x4c,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x18,0x8b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xdf,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,
+0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0xb2,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x97,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0x78,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,0xdb,0x68,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,
+0xbd,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xcd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x78,0xa4,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,
+0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x71,0x2e,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb7,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0x8,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc7,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x79,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x34,
+0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xd8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa0,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,
+0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe8,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,
+0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xef,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0x34,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xc8,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe3,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x69,0xf3,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,
+0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x1f,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaf,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x79,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x34,0x3b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,
+0xff,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xcd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xbf,0x4f,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,
+0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd7,0xdd,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc3,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0x79,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x34,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0xb4,
+0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x19,0x9,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xab,0xc8,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,
+0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xdd,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,
+0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x78,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0x34,0x3b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xbd,0x42,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe5,0x73,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xcc,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9a,0xad,0x84,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,
+0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa9,0x80,0x8e,0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xc8,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5f,0xa4,0x58,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,
+0x79,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xe2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc7,0x34,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,0x8e,
+0xa1,0xd8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x20,0x7b,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa6,0x80,0x8e,0xa1,0xcd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x73,0xc,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe5,0x80,0x8e,0xa1,0xcc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xad,0x84,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xe2,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0x79,
+0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x19,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x20,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,
+0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x68,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x77,0x27,0x20,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0xc8,0x14,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x33,0x51,0x1,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x47,0x7a,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x89,0x8a,0x18,0x8f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x60,0x4d,0xf,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,
+0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xce,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc0,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x2b,0x43,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x78,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,
+0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa2,0x1c,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x62,0x94,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,
+0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4,0xb7,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9f,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0x39,0x24,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbf,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0xe8,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0xfb,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,0x18,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xe1,0x53,0xf,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x60,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,
+0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x36,0x3,0x61,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa1,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x99,0x67,0x15,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xba,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3e,0x57,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x18,0x8b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8d,0x3c,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,
+0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x13,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd3,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x51,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0xfd,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,
+0x2a,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,0x18,0x88,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc,0xc1,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,
+0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc6,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xeb,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0x5,0x52,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4a,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xfd,0xdd,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x19,
+0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xed,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,
+0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x85,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x68,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x66,0xb2,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x87,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x16,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfe,0x50,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,
+0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc4,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,0xdd,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0x68,0x21,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5e,0xc4,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,
+0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbf,0x41,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcb,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,0x9e,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x8d,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,
+0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x47,0x3,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,
+0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0x72,0x15,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x51,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xad,0xb1,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x58,0x5b,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x18,0x8b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xff,0x8,0x16,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,
+0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb0,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0x68,0x21,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0xe3,0x40,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,
+0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x14,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x52,0x4a,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,
+0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x10,0x1c,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdf,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xc8,0x14,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xe,
+0x45,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x18,0x8f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3b,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,
+0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe9,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,
+0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x32,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x21,0xc9,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xce,0xb7,0x8a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa2,0x1f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x48,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x80,
+0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x24,0x24,0x44,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x7f,0x26,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,
+0x6a,0x5f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,0x19,0xe,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x1d,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,
+0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x77,0xd6,0x7c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcf,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x4d,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x4c,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x19,0xc,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x21,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,
+0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3b,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd3,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0x78,0x15,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xe,0x16,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x37,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x17,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9d,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,
+0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xeb,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,0xb1,0x3e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0xae,0x6e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,
+0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x1f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6c,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,
+0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x69,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe7,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0x78,0x26,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x87,0xa3,0x25,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x18,0x8b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x43,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,
+0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xd8,0xcf,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,
+0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x4f,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd7,0x89,0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcc,0xb1,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xdd,0x43,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x43,0x7e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0xa2,0x1f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x66,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,
+0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x50,0x2d,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaf,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x77,0x26,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xc8,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,
+0x92,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x18,0x8f,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x5,0xe8,0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,
+0xa2,0x17,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x41,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb3,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x7e,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xdb,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0xb1,0x3e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0x7e,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa2,0x1f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3f,0x68,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa2,
+0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x78,0x1d,0x8c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,
+0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0xa,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xf2,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfe,0x77,0x26,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x50,0xe8,0x4,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x45,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa2,0x19,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x9d,0xd6,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x89,
+0x8a,0x19,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa0,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xe0,0x43,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0x7e,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,
+0xab,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0xe,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc1,0x11,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,
+0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x80,0xd5,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9f,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0x51,0x1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xe8,0x4,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa2,0x17,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,0x81,
+0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x18,0x88,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa3,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa2,
+0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x7b,0xdb,0x7c,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,
+0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0x77,0x26,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x78,0x5f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xb1,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x4f,0x81,0x97,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x1f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2a,0x51,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,
+0x8e,0xa2,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0x5e,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbb,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0xc8,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x16,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x59,0x17,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x18,0x8f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,
+0x97,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x19,0xc,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x26,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,
+0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x12,0x1a,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe2,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0x68,0x21,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1b,0xf6,0x40,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x44,0xe8,
+0x4,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa2,0x17,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x76,0x8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x18,
+0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xe3,0xb1,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,
+0x80,0x8e,0xa2,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x1e,0x16,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb7,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x3b,0x50,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x97,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0x7e,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x1f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa7,0xc8,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x16,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x18,0x78,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,
+0x8e,0xa2,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc1,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0xf0,0x7,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0xf0,0x7,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,
+0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa2,0x10,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x7c,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,
+0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4d,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xef,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0xf0,0x7,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xf0,
+0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x10,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x64,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa2,
+0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x6c,0xf0,0x7,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,
+0x80,0x8e,0xa2,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xec,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xe2,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xe2,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x45,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa2,0x13,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x23,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,
+0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0xe2,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xf7,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2e,
+0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x47,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,
+0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x28,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd4,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1e,0xe2,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xe2,
+0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa2,0x13,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x2a,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,
+0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1b,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,
+0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2c,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0xe2,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x17,0xe2,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x15,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa2,0x13,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,
+0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x11,0xe2,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd1,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32,0xe2,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0xe2,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa2,0x13,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,
+0xe1,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa2,0x13,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x91,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,
+0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb1,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd3,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0xe8,0x34,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0xe8,
+0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc0,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa2,
+0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xaf,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,
+0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc2,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xe8,0x34,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0xe8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa7,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd4,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,
+0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x54,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,0xe8,0x34,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0xe8,0x34,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,
+0x63,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x19,0xf,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x37,0x9e,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,
+0x18,0x8f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd6,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe7,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0x2b,0x44,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xe8,0x34,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0xe8,
+0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0xa2,0x11,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xcc,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa2,
+0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa2,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,
+0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb8,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xab,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc4,0xe8,0x34,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xe8,0x34,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa2,0x11,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd2,0xe8,0x34,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa2,0x11,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x14,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,
+0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9e,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0x3b,0x9,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x3b,0x9,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,
+0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa2,0x1a,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x46,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,
+0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x2a,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc6,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,0x3a,0x9,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,0x3b,0x9,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3f,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x21,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,
+0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x35,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,
+0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xda,0xc8,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbf,0x89,0x8a,0x18,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf0,0xdf,0x6,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0xb9,0x31,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3c,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa2,0x1a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc0,0xe1,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,
+0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd3,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0xe8,0x6,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x10,0x3b,0x9,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,
+0x47,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x18,0x88,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x37,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,
+0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfa,0x3a,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdc,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,0x3b,0x9,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1d,0x3b,
+0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa2,0x1a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x39,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xde,0x80,0x8e,0xa2,
+0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x1a,0xbc,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,
+0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb6,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0xa1,0xb4,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xe4,0x3f,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x1b,0xeb,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x19,0x9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x51,0x3b,0x9,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd0,0x80,
+0x8e,0xa2,0x1a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xc1,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbb,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x79,0xe7,0x3f,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0xed,0x6,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2f,
+0xc4,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x19,0xe,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xd7,0xe9,0x3f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,
+0x19,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfa,0x99,0x26,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe2,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xef,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x81,0x2d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x80,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa2,0x1b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x38,0x29,0x2e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa2,
+0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xac,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,
+0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd1,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x81,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0xef,0x6,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x27,0xc7,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x19,0xe,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2c,0x5b,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x89,
+0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x80,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x81,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0x81,0x2d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,
+0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x1b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc8,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x89,0x8a,
+0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x40,0xf3,0x6,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdf,0x89,0x8a,0x19,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x19,0xca,0x31,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc7,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x81,0x2d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xce,0x80,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa2,0x1b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x52,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa2,
+0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x97,0x5d,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,
+0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc3,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x60,0x17,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x11,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x90,0xcc,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x19,0xe,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x7a,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x89,
+0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x81,0xd3,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcf,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x63,0x17,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x11,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,
+0xd5,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x19,0xe,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xf9,0x67,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,
+0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbc,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd8,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0x6b,0x17,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xd8,0x31,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x11,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x89,0x8a,0x18,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd8,0x6d,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x18,
+0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf7,0xe6,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,
+0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0xdb,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdb,0x89,0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0xe9,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0x11,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x46,0x70,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x18,0x89,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1a,0xde,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,
+0x8a,0x19,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x81,0x39,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x93,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xec,0xc,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x81,0x2d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,
+0x72,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x18,0x89,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x2f,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x24,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9b,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xda,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x70,0x81,0x2d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x81,
+0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x1b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5a,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa2,
+0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x22,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,
+0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdc,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0x80,0x2d,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x11,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbc,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x60,0x81,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0xa2,0x1b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe3,0x3b,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x89,
+0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x5,0xef,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc7,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x72,0x81,0x2d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa2,0x1b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0xba,0x26,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,
+0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa2,0x12,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4e,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,
+0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc3,0x80,0x37,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xe2,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xfa,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x97,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8f,0xee,0x2,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xef,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa6,0x3c,0x59,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,0x19,
+0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x22,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,
+0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0x41,0x45,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x93,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0xee,0x2,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xee,0x2,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xcb,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa2,0x12,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x6f,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x89,
+0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xff,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa7,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0xfc,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8b,0x41,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc0,
+0xf9,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x19,0x8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc1,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,
+0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x50,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xeb,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x43,0x28,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd1,0xee,0x2,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xef,
+0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa2,0x12,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x54,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa2,
+0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x35,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,
+0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdc,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0x12,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xfe,0x14,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa5,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa2,0x12,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd7,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,
+0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x42,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb7,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0x11,0x0,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xde,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x37,0x83,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x92,
+0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa2,0x12,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4,0xef,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,
+0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xfd,0xee,0x2,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbf,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x38,0xef,0x2,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa2,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x7e,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbf,0x11,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,0x18,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6d,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,0x8e,0xa1,
+0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf9,0x4d,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x7,
+0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0xc6,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdf,0x80,0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcb,0x60,0x7,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x29,0xfc,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x47,0x1,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0xd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x7f,0xc7,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x80,
+0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xc8,0x2f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xef,0x80,0x8e,0x17,0x48,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x4c,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x11,0x0,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc6,
+0x80,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x18,0x89,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x73,0xff,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,
+0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xd6,0x4e,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xab,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x9,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa7,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x34,0x83,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x11,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x89,0x8a,0x18,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xd8,0x1,0xd,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x89,0x8a,0x19,
+0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x8f,0xb,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,
+0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x51,0x28,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xaf,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0x88,0x17,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x11,0x0,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3c,0x2,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x19,0xa,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x4b,0x4,0xd,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,
+0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x18,0xe,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaf,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x54,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0x5,0x2a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcd,
+0x61,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0x17,0x49,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x70,0x53,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,
+0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x5a,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdd,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x5d,0x31,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xb1,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0x8b,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x40,0x15,
+0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0x17,0x49,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc7,0x9e,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,0x8a,0x18,
+0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x70,0x14,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,
+0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0x19,0x22,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xe6,0x2f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0x1e,0x22,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xe6,0x1b,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe8,0x80,0x8e,0x17,0x49,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa7,0xc0,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x89,
+0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x74,0x10,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x1d,0x22,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0x7,0x2a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7c,
+0x20,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0x17,0x49,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa0,0x13,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,
+0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc1,0x5a,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9d,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xa2,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x97,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x56,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x11,
+0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,0x89,0x8a,0x18,0x8a,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa5,0x8e,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x18,
+0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf7,0xc3,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,
+0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x60,0xa,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdb,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xa4,0x3e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0x12,0x15,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xaf,0x11,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x18,0x8a,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xe6,0x5f,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,
+0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x17,0x91,0x17,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdb,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x12,0xe9,0x4d,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe2,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xc6,0xc,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,
+0xd,0x2a,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x19,0xa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xcf,0x1c,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,
+0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x4f,0x62,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbf,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd9,0x11,0x0,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xda,0x89,0x8a,0x18,0x8a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x84,0x93,0x17,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x18,0x89,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,0xc8,
+0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x19,0x8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x3c,0xa9,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x18,
+0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xcd,0xab,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,
+0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x67,0x1f,0x15,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbf,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x64,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc3,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa0,0xae,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x16,0xcb,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0x8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa5,0xae,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x89,
+0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x81,0xa5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xea,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x68,0x28,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfb,0xb0,0x3e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,
+0xd2,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x19,0x8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa2,0x4c,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,
+0x17,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x2,0xb1,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x9b,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,0x22,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xc3,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6e,0x6a,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,0xb4,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x18,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xdd,0xd5,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,0x19,
+0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xf1,0x59,0x31,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa6,
+0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x5c,0x31,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xb8,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x12,0x22,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe1,0x16,0x22,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf9,0x80,0x8e,0x17,0x49,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa9,0x21,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf1,0x80,0x8e,0x17,0x49,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd0,0x12,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfe,0x80,
+0x8e,0x17,0x49,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xb,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc8,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0x81,0x38,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0xb3,0x29,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,
+0xcc,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0x17,0x74,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6b,0x7b,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,
+0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x2e,0x8,0x57,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xaa,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x8b,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xba,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0x81,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xd0,
+0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0x17,0x74,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc5,0xc7,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0x17,
+0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x4f,0x80,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,
+0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x26,0x15,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc7,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xab,0x7e,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2b,0x87,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x10,0xc5,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcd,0x80,0x8e,0x17,0x74,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x42,0x73,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,
+0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0xb6,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb3,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xcb,0x47,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7b,0xbe,0x47,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,
+0xc6,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0x17,0x74,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x4b,0xbf,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,
+0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbb,0xc1,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc4,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0x86,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe6,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdb,0x7d,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0x17,0x74,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0x80,
+0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0x17,0x6f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc1,0xc3,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0x17,
+0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xde,0x83,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,
+0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x44,0x7b,0x47,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xf0,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x3d,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe7,0x7c,0x47,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfe,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x47,0xd8,0xc,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x19,0x8,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xc5,0xb7,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,
+0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xba,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xaf,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb4,0xb6,0x29,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa3,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x7e,0x47,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,
+0x28,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x19,0xd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xe7,0x75,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,
+0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb4,0xbc,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb1,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb5,0x74,0x47,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1b,0x7,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc3,0x7d,0x47,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1b,0x1,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x84,
+0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,0x6b,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x5,0xbb,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0x17,
+0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc,0xb9,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,
+0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf3,0x40,0x11,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xfb,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0xda,0xc,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x63,0x82,0x20,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xd8,0xbb,0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0x17,0x6f,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x25,0x77,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,
+0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x35,0x8f,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9e,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x93,0x81,0x20,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,0x45,0x11,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x2,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,
+0xbe,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x89,0x8a,0x19,0xa,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc4,0x41,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe9,0x80,0x8e,
+0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x50,0x87,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa9,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x95,0xb8,0x56,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x30,0x15,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xb5,
+0x56,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0x17,0x6f,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x9c,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,
+0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa3,0x82,0x47,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfb,
+0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0x46,0x11,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa6,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0x73,0x47,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xf8,0x80,0x8e,0x17,0x6f,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x37,0x44,0x11,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x0,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf3,0x7f,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0x17,0x6b,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x57,0x49,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,
+0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x57,0x8e,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb1,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0x43,0x11,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x95,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfc,0x78,0x28,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,
+0x4b,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x4,0x80,0x8e,0x17,0x6b,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc7,0x8a,0x20,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,0x8e,
+0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x83,0x3e,0x11,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdd,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xbe,0x3e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbb,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x8c,0x20,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0x17,0x6b,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd7,0xc0,
+0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xab,0x89,0x8a,0x19,0xa,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x7e,0xd,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0x17,
+0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xff,0xf,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,
+0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xe,0x40,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcf,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe3,0x4e,0x4f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0xdd,0xc,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x19,0x8,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa7,0xf7,0x39,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0x17,0x64,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xb,0x11,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x6,0x80,
+0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x31,0x15,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1b,0x4,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x8,0x40,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xfa,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x48,0x4f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,
+0xb,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x3,0x80,0x8e,0x17,0x64,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x22,0xf,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfd,0x80,0x8e,
+0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6a,0x7b,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdb,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x33,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd3,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5d,0x9,0x40,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xf9,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x14,0x14,
+0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x0,0x80,0x8e,0x17,0x64,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x40,0x4d,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0x17,
+0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xed,0x6,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,
+0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xc0,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbf,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbd,0x46,0x4f,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x11,0x40,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3d,0x8c,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,0x80,0x8e,0x17,0x64,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfc,0x12,0x40,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf6,0x80,
+0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x8d,0x47,0x4f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x99,0x80,0x8e,0x17,0x64,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x98,0x2,0x25,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xc3,0x29,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x24,
+0x6,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0x17,0x66,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x54,0xff,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,
+0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7f,0xf8,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa7,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5a,0x36,0x15,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd7,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf6,0x3,0x25,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3b,0xf5,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0x17,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xf5,0xfd,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0x17,
+0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc,0x8,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,
+0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0xf4,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x93,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,0xfa,0x24,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xde,0xfc,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3b,0x1,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0x17,0x66,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xdd,0xf6,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9f,0x80,
+0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x5,0x25,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd3,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf5,0x6,0x25,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6a,0x0,0x25,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,
+0xfc,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0x17,0x66,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xae,0xf7,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,
+0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x47,0xc3,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xc3,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0xf9,0x24,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0x17,0x66,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9f,0xc5,0x29,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0xf6,
+0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0x17,0x66,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xc2,0x5a,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0x17,
+0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x8a,0x64,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xee,
+0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc2,0x99,0x24,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd8,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0x7e,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x19,0xb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0x97,0x24,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x22,0x59,0x15,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0x17,0x65,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x31,0xa1,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,
+0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0x90,0x22,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa2,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf2,0x98,0x24,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaa,0xd4,0x38,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb3,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,
+0xd3,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0x17,0x65,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x33,0xcf,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,
+0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x64,0xd1,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x97,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb3,0xdd,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xbf,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x56,0x39,0x15,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0xc6,
+0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x18,0x8c,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x78,0xcc,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0x17,
+0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc5,0xd5,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,
+0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdd,0xd6,0x38,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9f,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x91,0xd8,0x38,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x54,0xdc,0x38,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf5,0xc8,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x89,0x8a,0x19,0xa,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd6,0xca,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,
+0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x27,0xda,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xab,0x80,0x8e,0x17,0x65,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb9,0x3b,0x15,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdf,0x89,0x8a,0x19,0xd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0x89,0x14,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,
+0x7c,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0x17,0x68,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6c,0xd1,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x89,0x8a,
+0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xf3,0x85,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa3,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0x88,0x14,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa7,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa1,0x79,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x80,0x8c,
+0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0x17,0x68,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xac,0x2a,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0x17,
+0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x96,0x78,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc0,
+0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xc8,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xcb,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0x6c,0x21,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbd,0x2e,0x12,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x7,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x8b,0xce,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcf,0x89,0x8a,0x18,0x8c,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3c,0x28,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf0,0x80,
+0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd0,0xd3,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbf,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa5,0x34,0x12,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1b,0xa,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1c,0x2d,0x12,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd8,
+0x71,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0x17,0x68,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x46,0x31,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,
+0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7c,0x2b,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa2,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x46,0x38,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1b,0x4,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2d,0x70,0x21,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0x17,0x68,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xca,0x33,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0x17,0x68,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xef,0xd0,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x89,0x8a,0x18,
+0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x26,0xd6,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,
+0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xd3,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd7,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0xd9,0x29,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc7,0x89,0x8a,0x19,0xa,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x97,0xd6,0x3e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x89,0x8a,0x18,0x8c,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xcb,0xdb,0x29,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x89,0x8a,0x19,0xa,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xfa,0xd8,0x3e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdf,0x89,
+0x8a,0x18,0x8c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x15,0xb3,0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe9,0x80,0x8e,0x17,0x54,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8,0xd3,0x33,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0x17,0x4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe4,0x54,0x2d,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,
+0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xe4,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xc3,0x54,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,
+0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x6c,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xcd,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc,0x45,0x6a,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0x54,0x2d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9e,0x12,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xe4,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xaa,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,
+0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa0,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,
+0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0x53,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9a,0x12,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x12,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xe4,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa6,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xe4,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x79,0x12,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,
+0x8e,0xa1,0xe4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x33,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xce,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x86,0xe0,0x1b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x72,0x80,0x6a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x98,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,
+0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xbe,0x1a,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,
+0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x60,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdf,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa8,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcd,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0xb0,0x12,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe8,0xbc,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x79,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x47,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,
+0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xcf,0x99,0x2d,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc9,0xf0,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x57,0xa,0x68,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xf6,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xca,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x3f,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,
+0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x44,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xb1,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x18,0xbd,0x5,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb2,
+0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xcb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x49,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,
+0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x7d,0xe9,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xf2,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0x22,0x2b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0x99,0x2d,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xb0,
+0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb6,0x80,0x8e,0xa1,0xc8,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xef,0x75,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,
+0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x19,0xa7,0x42,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,
+0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3d,0xb0,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xad,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xaf,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc3,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x43,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb2,0x80,0x8e,0xa1,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xdc,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb4,0x80,
+0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xed,0xf2,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xde,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb7,0xe0,0x1b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xdf,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9d,0x57,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x53,
+0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xdd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x59,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,
+0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xbe,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xd4,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0xfc,0x4b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa0,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd9,0x43,0x40,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0xbd,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x58,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,
+0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x3,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xac,
+0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8c,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbc,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xb0,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x96,0x57,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x32,0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd5,0x80,0x8e,0xa1,0xda,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x43,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x95,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xa4,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9f,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4f,0xc8,0x58,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x71,0xe9,0x51,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x41,
+0xe5,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc8,0x80,0x8e,0xa1,0xda,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x73,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xa5,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xea,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1a,0xf6,0x1,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf4,0xbc,0x5,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xd8,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xe5,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xda,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x87,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,
+0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb4,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,
+0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x35,0xa7,0x42,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x68,0xb0,0x12,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd9,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,0xe5,0x1e,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa0,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb1,0x80,0x8e,0xa1,0xc9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x8a,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9c,0x80,
+0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6d,0xe9,0x51,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x99,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0xc8,0x5e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xb9,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x52,0xe5,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xae,
+0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xcb,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa6,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xe0,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xa7,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9,0x86,0x4f,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4c,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0xe5,
+0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xda,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xab,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdd,0x80,0x8e,0xa1,
+0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xa1,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,
+0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf1,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xdc,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0xf8,0x1,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x1a,0x6b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x79,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,0xc9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x1a,0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,0x80,
+0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xb9,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xc9,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3e,0xc8,0x5e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5e,0xe5,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,
+0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xc9,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xec,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,
+0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x91,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xbd,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd0,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x90,0x27,0x2e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xce,
+0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xa7,0x22,0x24,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,
+0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x93,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xca,
+0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x42,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb0,0xe0,0x1b,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0x1a,0x6b,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x85,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,0xa1,0xc9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xd7,0xbc,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,
+0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x21,0xc8,0x58,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xf2,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x89,0xfa,0x1,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xd8,0x5b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x92,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7,
+0xbd,0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,0x8e,0xa1,0xca,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x8d,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,
+0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9b,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xdd,0x80,0x8e,0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3a,0xc8,0x5e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xcf,0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x62,0xe5,0x1e,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf,0xbd,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x8f,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd1,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x48,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1f,0x3e,0x5b,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x69,0xe5,0x1e,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe6,0xbc,0x5,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xac,0xe0,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xae,0x80,0x8e,0xa1,0xcb,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x37,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa8,0x80,
+0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x11,0xa2,0x36,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xcb,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xe5,0x1e,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf8,0xbc,0x5,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,0xca,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,
+0xfc,0x1,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xc8,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xa1,0xa,0x68,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x99,0x80,0x8e,
+0xa1,0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xb4,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xef,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x13,0x96,0x4c,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xda,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd3,0xbc,
+0x5,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb0,0x80,0x8e,0xa1,0xca,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x73,0xe8,0x1b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,
+0xcb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x53,0xb0,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd4,
+0x80,0x8e,0xa1,0xc8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9b,0x57,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc5,0x80,0x8e,0xa1,0xc9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x58,0xce,0x13,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xd7,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x64,0xce,0x13,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa4,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x3e,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xdd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x69,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe5,0x80,
+0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x3c,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xd4,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0xce,0x13,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,0xce,0x13,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9a,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x55,
+0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xdd,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x60,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbb,0x80,0x8e,
+0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x33,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xae,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x5,0x60,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0x95,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xce,0x13,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xba,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x51,0xce,
+0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbf,0x80,0x8e,0xa1,0xdd,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0xae,0x10,0x23,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xf2,0x80,0x8e,0xa1,
+0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x68,0x5,0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,
+0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x39,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xc0,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe,0xc3,0x50,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xaf,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5c,0xce,0x13,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,0x8e,0xa1,0xdd,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xdc,0x80,0x41,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,0xa1,0xdd,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x2e,0xce,0x13,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xeb,0x80,
+0x8e,0xa1,0xdd,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x40,0xa1,0x65,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0x9d,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x11,0xf0,0x50,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x9c,0x1a,0x1e,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa3,
+0xfe,0x12,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9b,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x71,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,
+0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x67,0x5,0x55,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,
+0xf,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbe,0x78,0x87,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xa5,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x45,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x22,0xb6,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcb,0x80,0x8e,0xa1,0xd5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x63,0x32,0x60,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x93,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xae,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4e,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb8,0x78,0x87,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xab,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x8c,0x43,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfb,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x5e,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb7,0x80,0x8e,0xa1,0xd1,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf,0x48,0x5e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9e,0x80,
+0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x76,0xe3,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xa1,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x82,0x6,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbe,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x20,0xe7,0x4f,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x25,
+0x23,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xd0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x94,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe7,0x80,0x8e,
+0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x9f,0x1e,0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb0,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5b,0x59,0x21,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd4,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc7,0x27,0x75,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xfd,0xb5,
+0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xd5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x6f,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xec,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x65,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xba,
+0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xba,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xe4,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0xb6,0x28,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xc6,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x17,0x62,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc9,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xb2,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xd6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xa8,0xe5,0x65,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa3,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x79,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xe4,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x59,0x6,0x14,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xb,0x5b,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc4,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,
+0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc2,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x63,0x1c,0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xce,0x80,0x8e,
+0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x64,0xe3,0x74,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0x97,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4b,0x39,0x38,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xca,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9,0x48,0x3b,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xb8,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x73,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd2,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x1d,0xf0,0x50,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc7,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xc2,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xcc,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4d,0xbc,0x3,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xef,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe5,0xd7,0x52,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xf6,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x7e,0xe3,0x74,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0xa4,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe1,0x80,0x8e,0xa1,0xd6,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x64,0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,
+0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x7d,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xef,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x31,0xf0,0x50,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa3,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x78,0x4d,0x6a,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xe0,
+0x5a,0x0,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xe3,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0x6e,0x14,0x62,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,
+0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0x14,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xb8,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb6,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xd6,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6b,0x39,0x38,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xef,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x75,0x59,
+0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xe7,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x72,0x6,0x14,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xdb,0x80,0x8e,0xa1,
+0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0xb2,0x1a,0x1e,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xd7,
+0x80,0x8e,0xa1,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xdc,0xa4,0x40,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xad,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa6,0x12,0x62,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xa1,0x80,0x8e,0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x65,0x6a,0x84,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa7,0x80,0x8e,0xa1,0xd1,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x42,0xb6,0x28,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe6,0x80,0x8e,0xa1,0xd5,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0x7e,0x48,0x3b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x94,0x80,
+0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0x6f,0x59,0x21,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xdf,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xc8,0xad,0x41,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xa2,0x80,0x8e,0xa1,0xd4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x8e,0xbc,0x3,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe2,0x80,0x8e,0xa1,0xe3,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x49,
+0x39,0x38,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xb5,0x80,0x8e,0xa1,0xd1,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,
+0x0,0x0,0xb,0xd4,0x52,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xa9,0x80,0x8e,
+0xa1,0xde,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,
+0x0,0x1,0x0,0x0,0x0,0xc6,0x78,0x87,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,
+0xae,0x80,0x8e,0xa1,0xd6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,
+0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x48,0x5e,0x0,0x10,0x0,0x0,0x0,
+0x2,0x0,0x1a,0xaa,0x80,0x8e,0xa1,0xe7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,0xb6,0x28,0x0,0x10,
+0x0,0x0,0x0,0x2,0x0,0x1a,0xda,0x80,0x8e,0xa1,0xd5,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xd4,0x89,
+0x54,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1b,0x19,0x80,0x8e,0xa1,0xe5,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x40,0xd5,0x2b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xe3,0x80,0x8e,0xa1,
+0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,
+0x1,0x0,0x0,0x0,0x9d,0x8d,0x33,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xef,
+0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,
+0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa2,0xc4,0x1f,0x0,0x10,0x0,0x0,0x0,0x2,
+0x0,0x1a,0xd3,0x80,0x8e,0xa1,0xd2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,
+0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xf9,0x4a,0x3a,0x0,0x10,0x0,
+0x0,0x0,0x2,0x0,0x1a,0xe0,0x80,0x8e,0xa1,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x43,0xef,0x2d,
+0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x97,0x80,0x8e,0xa1,0xdf,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+0x45,0x15,0x30,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xc1,0x80,0x8e,0xa1,0xd9,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,
+0x0,0x0,0x0,0xf8,0x88,0x54,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0xfe,0x80,
+0x8e,0xa1,0xe5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,
+0x0,0x0,0x1,0x0,0x0,0x0,0xd5,0x8b,0x8,0x0,0x10,0x0,0x0,0x0,0x2,0x0,
+0x1a,0xbe,0x80,0x8e,0xa1,0xd3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,
+0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0xd5,0x2b,0x0,0x10,0x0,0x0,
+0x0,0x2,0x0,0x1a,0xbd,0x80,0x8e,0xa1,0xdb,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xea,0x74,0x70,0x0,
+0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x96,0x80,0x8e,0xa1,0xe6,0x0,0x0,0x0,0x0,
+0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1c,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xbc,
+0xef,0x6b,0x0,0x10,0x0,0x0,0x0,0x2,0x0,0x1a,0x9d,0x80,0x8e,0xa1,0xd2,0x0,
+0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf6,0x28,0x5c,0x3f,0x33,0x33,0x73,0x3f,0x66,
+0x66,0x66,0x3f,0xc,0xd,0x2,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc,0x0,0x0,0x0,0x5f,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x61,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x63,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x65,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x67,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x69,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x6b,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x6d,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x6f,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x71,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x73,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x75,0x4a,0x1,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+0x0,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x0,
+0x0,0x89,0xbc,0x22,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8d,0xbc,0x22,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x8f,0xbc,0x22,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x91,0xbc,0x22,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x94,0xbc,0x22,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0x96,0xbc,0x22,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
+0x0,0xc4,0x45,0x1f,0x8b
+};
diff --git a/src/test/compressor/test_compression.cc b/src/test/compressor/test_compression.cc
new file mode 100644
index 000000000..6ae49daf5
--- /dev/null
+++ b/src/test/compressor/test_compression.cc
@@ -0,0 +1,612 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2015 Mirantis, Inc.
+ *
+ * Author: Alyona Kiseleva <akiselyova@mirantis.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include "gtest/gtest.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "compressor/Compressor.h"
+#include "compressor/CompressionPlugin.h"
+#include "global/global_context.h"
+#include "osd/OSDMap.h"
+
+using namespace std;
+
+class CompressorTest : public ::testing::Test,
+ public ::testing::WithParamInterface<const char*> {
+public:
+ std::string plugin;
+ CompressorRef compressor;
+ bool old_zlib_isal;
+
+ CompressorTest() {
+ // note for later
+ old_zlib_isal = g_conf()->compressor_zlib_isal;
+
+ plugin = GetParam();
+ size_t pos = plugin.find('/');
+ if (pos != std::string::npos) {
+ string isal = plugin.substr(pos + 1);
+ plugin = plugin.substr(0, pos);
+ if (isal == "isal") {
+ g_conf().set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ } else if (isal == "noisal") {
+ g_conf().set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ } else {
+ ceph_abort_msg("bad option");
+ }
+ }
+ cout << "[plugin " << plugin << " (" << GetParam() << ")]" << std::endl;
+ }
+ ~CompressorTest() override {
+ g_conf().set_val("compressor_zlib_isal", old_zlib_isal ? "true" : "false");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ }
+
+ void SetUp() override {
+ compressor = Compressor::create(g_ceph_context, plugin);
+ ASSERT_TRUE(compressor);
+ }
+ void TearDown() override {
+ compressor.reset();
+ }
+};
+
+TEST_P(CompressorTest, load_plugin)
+{
+}
+
+TEST_P(CompressorTest, small_round_trip)
+{
+ bufferlist orig;
+ orig.append("This is a short string. There are many strings like it but this one is mine.");
+ bufferlist compressed;
+ std::optional<int32_t> compressor_message;
+ int r = compressor->compress(orig, compressed, compressor_message);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed, compressor_message);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+
+TEST_P(CompressorTest, big_round_trip_repeated)
+{
+ unsigned len = 1048576 * 4;
+ bufferlist orig;
+ while (orig.length() < len) {
+ orig.append("This is a short string. There are many strings like it but this one is mine.");
+ }
+ bufferlist compressed;
+ std::optional<int32_t> compressor_message;
+ int r = compressor->compress(orig, compressed, compressor_message);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed, compressor_message);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+
+TEST_P(CompressorTest, big_round_trip_randomish)
+{
+ unsigned len = 1048576 * 10;//269;
+ bufferlist orig;
+ const char *alphabet = "abcdefghijklmnopqrstuvwxyz";
+ if (false) {
+ while (orig.length() < len) {
+ orig.append(alphabet[rand() % 10]);
+ }
+ } else {
+ bufferptr bp(len);
+ char *p = bp.c_str();
+ for (unsigned i=0; i<len; ++i) {
+ p[i] = alphabet[rand() % 10];
+ }
+ orig.append(bp);
+ }
+ bufferlist compressed;
+ std::optional<int32_t> compressor_message;
+ int r = compressor->compress(orig, compressed, compressor_message);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed, compressor_message);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+
+#if 0
+TEST_P(CompressorTest, big_round_trip_file)
+{
+ bufferlist orig;
+ int fd = ::open("bin/ceph-osd", O_RDONLY);
+ struct stat st;
+ ::fstat(fd, &st);
+ orig.read_fd(fd, st.st_size);
+
+ bufferlist compressed;
+ int r = compressor->compress(orig, compressed);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+#endif
+
+
+TEST_P(CompressorTest, round_trip_osdmap)
+{
+#include "osdmaps/osdmap.2982809.h"
+
+ auto compressor = Compressor::create(g_ceph_context, plugin);
+ bufferlist orig;
+ orig.append((char*)osdmap_a, sizeof(osdmap_a));
+ cout << "orig length " << orig.length() << std::endl;
+ uint32_t size = 128*1024;
+ OSDMap *o = new OSDMap;
+ o->decode(orig);
+ bufferlist fbl;
+ o->encode(fbl, o->get_encoding_features() | CEPH_FEATURE_RESERVED);
+ ASSERT_TRUE(fbl.contents_equal(orig));
+ for (int j = 0; j < 3; j++) {
+ bufferlist chunk;
+ uint32_t l = std::min(size, fbl.length() - j*size);
+ chunk.substr_of(fbl, j*size, l);
+ //fbl.rebuild();
+ bufferlist compressed;
+ std::optional<int32_t> compressor_message;
+ int r = compressor->compress(chunk, compressed, compressor_message);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed, compressor_message);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), chunk.length());
+ if (!decompressed.contents_equal(chunk)) {
+ cout << "FAILED, orig bl was\n" << fbl << std::endl;
+ ASSERT_TRUE(decompressed.contents_equal(chunk));
+ }
+ cout << "chunk " << chunk.length()
+ << " compressed " << compressed.length()
+ << " decompressed " << decompressed.length()
+ << " with " << plugin << std::endl;
+ }
+ delete o;
+}
+
+TEST_P(CompressorTest, compress_decompress)
+{
+ const char* test = "This is test text";
+ int res;
+ int len = strlen(test);
+ bufferlist in, out;
+ bufferlist after;
+ bufferlist exp;
+ in.append(test, len);
+ std::optional<int32_t> compressor_message;
+ res = compressor->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ res = compressor->decompress(out, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ exp.append(test);
+ EXPECT_TRUE(exp.contents_equal(after));
+ after.clear();
+ size_t compressed_len = out.length();
+ out.append_zero(12);
+ auto it = out.cbegin();
+ res = compressor->decompress(it, compressed_len, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ EXPECT_TRUE(exp.contents_equal(after));
+
+ //large block and non-begin iterator for continuous block
+ std::string data;
+ data.resize(0x10000 * 1);
+ for(size_t i = 0; i < data.size(); i++)
+ data[i] = i / 256;
+ in.clear();
+ out.clear();
+ in.append(data);
+ exp = in;
+ res = compressor->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ compressed_len = out.length();
+ out.append_zero(0x10000 - out.length());
+ after.clear();
+ out.c_str();
+ bufferlist prefix;
+ prefix.append(string("some prefix"));
+ size_t prefix_len = prefix.length();
+ prefix.claim_append(out);
+ out.swap(prefix);
+ it = out.cbegin();
+ it += prefix_len;
+ res = compressor->decompress(it, compressed_len, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ EXPECT_TRUE(exp.contents_equal(after));
+}
+
+TEST_P(CompressorTest, sharded_input_decompress)
+{
+ const size_t small_prefix_size=3;
+
+ string test(128*1024,0);
+ int len = test.size();
+ bufferlist in, out;
+ in.append(test.c_str(), len);
+ std::optional<int32_t> compressor_message;
+ int res = compressor->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ EXPECT_GT(out.length(), small_prefix_size);
+
+ bufferlist out2, tmp;
+ tmp.substr_of(out, 0, small_prefix_size );
+ out2.append( tmp );
+ size_t left = out.length()-small_prefix_size;
+ size_t offs = small_prefix_size;
+ while( left > 0 ){
+ size_t shard_size = std::min<size_t>(2048, left);
+ tmp.substr_of(out, offs, shard_size );
+ out2.append( tmp );
+ left -= shard_size;
+ offs += shard_size;
+ }
+
+ bufferlist after;
+ res = compressor->decompress(out2, after, compressor_message);
+ EXPECT_EQ(res, 0);
+}
+
+void test_compress(CompressorRef compressor, size_t size)
+{
+ char* data = (char*) malloc(size);
+ for (size_t t = 0; t < size; t++) {
+ data[t] = (t & 0xff) | (t >> 8);
+ }
+ bufferlist in;
+ in.append(data, size);
+ for (size_t t = 0; t < 10000; t++) {
+ bufferlist out;
+ std::optional<int32_t> compressor_message;
+ int res = compressor->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ }
+ free(data);
+}
+
+void test_decompress(CompressorRef compressor, size_t size)
+{
+ char* data = (char*) malloc(size);
+ for (size_t t = 0; t < size; t++) {
+ data[t] = (t & 0xff) | (t >> 8);
+ }
+ bufferlist in, out;
+ in.append(data, size);
+ std::optional<int32_t> compressor_message;
+ int res = compressor->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ for (size_t t = 0; t < 10000; t++) {
+ bufferlist out_dec;
+ int res = compressor->decompress(out, out_dec, compressor_message);
+ EXPECT_EQ(res, 0);
+ }
+ free(data);
+}
+
+TEST_P(CompressorTest, compress_1024)
+{
+ test_compress(compressor, 1024);
+}
+
+TEST_P(CompressorTest, compress_2048)
+{
+ test_compress(compressor, 2048);
+}
+
+TEST_P(CompressorTest, compress_4096)
+{
+ test_compress(compressor, 4096);
+}
+
+TEST_P(CompressorTest, compress_8192)
+{
+ test_compress(compressor, 8192);
+}
+
+TEST_P(CompressorTest, compress_16384)
+{
+ test_compress(compressor, 16384);
+}
+
+TEST_P(CompressorTest, decompress_1024)
+{
+ test_decompress(compressor, 1024);
+}
+
+TEST_P(CompressorTest, decompress_2048)
+{
+ test_decompress(compressor, 2048);
+}
+
+TEST_P(CompressorTest, decompress_4096)
+{
+ test_decompress(compressor, 4096);
+}
+
+TEST_P(CompressorTest, decompress_8192)
+{
+ test_decompress(compressor, 8192);
+}
+
+TEST_P(CompressorTest, decompress_16384)
+{
+ test_decompress(compressor, 16384);
+}
+
+
+INSTANTIATE_TEST_SUITE_P(
+ Compressor,
+ CompressorTest,
+ ::testing::Values(
+#ifdef HAVE_LZ4
+ "lz4",
+#endif
+#if defined(__x86_64__) || defined(__aarch64__)
+ "zlib/isal",
+#endif
+ "zlib/noisal",
+ "snappy",
+#ifdef HAVE_BROTLI
+ "brotli",
+#endif
+ "zstd"));
+
+#if defined(__x86_64__) || defined(__aarch64__)
+
+TEST(ZlibCompressor, zlib_isal_compatibility)
+{
+ g_conf().set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ CompressorRef isal = Compressor::create(g_ceph_context, "zlib");
+ if (!isal) {
+ // skip the test if the plugin is not ready
+ return;
+ }
+ g_conf().set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ CompressorRef zlib = Compressor::create(g_ceph_context, "zlib");
+ char test[101];
+ srand(time(0));
+ for (int i=0; i<100; ++i)
+ test[i] = 'a' + rand()%26;
+ test[100] = '\0';
+ int len = strlen(test);
+ bufferlist in, out;
+ in.append(test, len);
+ // isal -> zlib
+ std::optional<int32_t> compressor_message;
+ int res = isal->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = zlib->decompress(out, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(static_cast<char*>(test));
+ EXPECT_TRUE(exp.contents_equal(after));
+ after.clear();
+ out.clear();
+ exp.clear();
+ // zlib -> isal
+ res = zlib->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ res = isal->decompress(out, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ exp.append(static_cast<char*>(test));
+ EXPECT_TRUE(exp.contents_equal(after));
+}
+#endif
+
+TEST(CompressionPlugin, all)
+{
+ CompressorRef compressor;
+ PluginRegistry *reg = g_ceph_context->get_plugin_registry();
+ EXPECT_TRUE(reg);
+ CompressionPlugin *factory = dynamic_cast<CompressionPlugin*>(reg->get_with_load("compressor", "invalid"));
+ EXPECT_FALSE(factory);
+ factory = dynamic_cast<CompressionPlugin*>(reg->get_with_load("compressor", "example"));
+ ASSERT_TRUE(factory);
+ stringstream ss;
+ EXPECT_EQ(0, factory->factory(&compressor, &ss));
+ EXPECT_TRUE(compressor.get());
+ {
+ std::lock_guard l(reg->lock);
+ EXPECT_EQ(-ENOENT, reg->remove("compressor", "does not exist"));
+ EXPECT_EQ(0, reg->remove("compressor", "example"));
+ EXPECT_EQ(0, reg->load("compressor", "example"));
+ }
+}
+
+#if defined(__x86_64__) || defined(__aarch64__)
+
+TEST(ZlibCompressor, isal_compress_zlib_decompress_random)
+{
+ g_conf().set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ CompressorRef isal = Compressor::create(g_ceph_context, "zlib");
+ if (!isal) {
+ // skip the test if the plugin is not ready
+ return;
+ }
+ g_conf().set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ CompressorRef zlib = Compressor::create(g_ceph_context, "zlib");
+
+ for (int cnt=0; cnt<100; cnt++)
+ {
+ srand(cnt + 1000);
+ int log2 = (rand()%18) + 1;
+ int size = (rand() % (1 << log2)) + 1;
+
+ char test[size];
+ for (int i=0; i<size; ++i)
+ test[i] = rand()%256;
+ bufferlist in, out;
+ in.append(test, size);
+
+ std::optional<int32_t> compressor_message;
+ int res = isal->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = zlib->decompress(out, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(test, size);
+ EXPECT_TRUE(exp.contents_equal(after));
+ }
+}
+
+TEST(ZlibCompressor, isal_compress_zlib_decompress_walk)
+{
+ g_conf().set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ CompressorRef isal = Compressor::create(g_ceph_context, "zlib");
+ if (!isal) {
+ // skip the test if the plugin is not ready
+ return;
+ }
+ g_conf().set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ CompressorRef zlib = Compressor::create(g_ceph_context, "zlib");
+
+ for (int cnt=0; cnt<100; cnt++)
+ {
+ srand(cnt + 1000);
+ int log2 = (rand()%18) + 1;
+ int size = (rand() % (1 << log2)) + 1;
+
+ int range = 1;
+
+ char test[size];
+ test[0] = rand()%256;
+ for (int i=1; i<size; ++i)
+ test[i] = test[i-1] + rand()%(range*2+1) - range;
+ bufferlist in, out;
+ in.append(test, size);
+
+ std::optional<int32_t> compressor_message;
+ int res = isal->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = zlib->decompress(out, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(test, size);
+ EXPECT_TRUE(exp.contents_equal(after));
+ }
+}
+
+#endif // __x86_64__
+
+#ifdef HAVE_QATZIP
+TEST(QAT, enc_qat_dec_noqat) {
+#ifdef HAVE_LZ4
+ const char* alg_collection[] = {"zlib", "lz4", "snappy"};
+#else
+ const char* alg_collection[] = {"zlib", "snappy"};
+#endif
+ for (auto alg : alg_collection) {
+ g_conf().set_val("qat_compressor_enabled", "true");
+ CompressorRef q = Compressor::create(g_ceph_context, alg);
+ g_conf().set_val("qat_compressor_enabled", "false");
+ CompressorRef noq = Compressor::create(g_ceph_context, alg);
+
+ // generate random buffer
+ for (int cnt=0; cnt<100; cnt++) {
+ srand(cnt + 1000);
+ int log2 = (rand()%18) + 1;
+ int size = (rand() % (1 << log2)) + 1;
+
+ char test[size];
+ for (int i=0; i<size; ++i)
+ test[i] = rand()%256;
+ bufferlist in, out;
+ in.append(test, size);
+
+ std::optional<int32_t> compressor_message;
+ int res = q->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = noq->decompress(out, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(test, size);
+ EXPECT_TRUE(exp.contents_equal(after));
+ }
+ }
+}
+
+TEST(QAT, enc_noqat_dec_qat) {
+#ifdef HAVE_LZ4
+ const char* alg_collection[] = {"zlib", "lz4", "snappy"};
+#else
+ const char* alg_collection[] = {"zlib", "snappy"};
+#endif
+ for (auto alg : alg_collection) {
+ g_conf().set_val("qat_compressor_enabled", "true");
+ CompressorRef q = Compressor::create(g_ceph_context, alg);
+ g_conf().set_val("qat_compressor_enabled", "false");
+ CompressorRef noq = Compressor::create(g_ceph_context, alg);
+
+ // generate random buffer
+ for (int cnt=0; cnt<100; cnt++) {
+ srand(cnt + 1000);
+ int log2 = (rand()%18) + 1;
+ int size = (rand() % (1 << log2)) + 1;
+
+ char test[size];
+ for (int i=0; i<size; ++i)
+ test[i] = rand()%256;
+ bufferlist in, out;
+ in.append(test, size);
+
+ std::optional<int32_t> compressor_message;
+ int res = noq->compress(in, out, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = q->decompress(out, after, compressor_message);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(test, size);
+ EXPECT_TRUE(exp.contents_equal(after));
+ }
+ }
+}
+
+#endif // HAVE_QATZIP
diff --git a/src/test/confutils.cc b/src/test/confutils.cc
new file mode 100644
index 000000000..f69e22316
--- /dev/null
+++ b/src/test/confutils.cc
@@ -0,0 +1,458 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "common/ConfUtils.h"
+#include "common/config_proxy.h"
+#include "common/errno.h"
+#include "gtest/gtest.h"
+#include "include/buffer.h"
+
+#include <errno.h>
+#include <filesystem>
+#include <iostream>
+#include <sstream>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+namespace fs = std::filesystem;
+
+using namespace std;
+using ceph::bufferlist;
+using std::cerr;
+using std::ostringstream;
+
+#define MAX_FILES_TO_DELETE 1000UL
+
+static size_t config_idx = 0;
+static size_t unlink_idx = 0;
+static char *to_unlink[MAX_FILES_TO_DELETE];
+
+static std::string get_temp_dir()
+{
+ static std::string temp_dir;
+
+ if (temp_dir.empty()) {
+ const char *tmpdir = getenv("TMPDIR");
+ if (!tmpdir)
+ tmpdir = "/tmp";
+ srand(time(NULL));
+ ostringstream oss;
+ oss << tmpdir << "/confutils_test_dir." << rand() << "." << getpid();
+ umask(022);
+ if (!fs::exists(oss.str())) {
+ std::error_code ec;
+ if (!fs::create_directory(oss.str(), ec)) {
+ cerr << "failed to create temp directory '" << temp_dir << "' "
+ << ec.message() << std::endl;
+ return "";
+ }
+ fs::permissions(oss.str(), fs::perms::sticky_bit | fs::perms::all);
+ }
+ temp_dir = oss.str();
+ }
+ return temp_dir;
+}
+
+static void unlink_all(void)
+{
+ for (size_t i = 0; i < unlink_idx; ++i) {
+ unlink(to_unlink[i]);
+ }
+ for (size_t i = 0; i < unlink_idx; ++i) {
+ free(to_unlink[i]);
+ }
+ rmdir(get_temp_dir().c_str());
+}
+
+static int create_tempfile(const std::string &fname, const char *text)
+{
+ FILE *fp = fopen(fname.c_str(), "w");
+ if (!fp) {
+ int err = errno;
+ cerr << "Failed to write file '" << fname << "' to temp directory '"
+ << get_temp_dir() << "'. " << cpp_strerror(err) << std::endl;
+ return err;
+ }
+ std::shared_ptr<FILE> fpp(fp, fclose);
+ if (unlink_idx >= MAX_FILES_TO_DELETE)
+ return -ENOBUFS;
+ if (unlink_idx == 0) {
+ memset(to_unlink, 0, sizeof(to_unlink));
+ atexit(unlink_all);
+ }
+ to_unlink[unlink_idx++] = strdup(fname.c_str());
+ size_t strlen_text = strlen(text);
+ size_t res = fwrite(text, 1, strlen_text, fp);
+ if (res != strlen_text) {
+ int err = errno;
+ cerr << "fwrite error while writing to " << fname
+ << ": " << cpp_strerror(err) << std::endl;
+ return err;
+ }
+ return 0;
+}
+
+static std::string next_tempfile(const char *text)
+{
+ ostringstream oss;
+ std::string temp_dir(get_temp_dir());
+ if (temp_dir.empty())
+ return "";
+ oss << temp_dir << "/test_config." << config_idx++ << ".config";
+ int ret = create_tempfile(oss.str(), text);
+ if (ret)
+ return "";
+ return oss.str();
+}
+
+const char * const trivial_conf_1 = "";
+
+const char * const trivial_conf_2 = "log dir = foobar";
+
+const char * const trivial_conf_3 = "log dir = barfoo\n";
+
+const char * const trivial_conf_4 = "log dir = \"barbaz\"\n";
+
+const char * const simple_conf_1 = "\
+; here's a comment\n\
+[global]\n\
+ keyring = .my_ceph_keyring\n\
+\n\
+[mds]\n\
+ log dir = out\n\
+ log per instance = true\n\
+ log sym history = 100\n\
+ profiling logger = true\n\
+ profiling logger dir = wowsers\n\
+ chdir = ""\n\
+ pid file = out/$name.pid\n\
+\n\
+ mds debug frag = true\n\
+[osd]\n\
+ pid file = out/$name.pid\n\
+ osd scrub load threshold = 5.0\n\
+\n\
+ lockdep = 1\n\
+[osd0]\n\
+ osd data = dev/osd0\n\
+ osd journal size = 100\n\
+[mds.a]\n\
+[mds.b]\n\
+[mds.c]\n\
+";
+
+// we can add whitespace at odd locations and it will get stripped out.
+const char * const simple_conf_2 = "\
+[mds.a]\n\
+ log dir = special_mds_a\n\
+[mds]\n\
+ log sym history = 100\n\
+ log dir = out # after a comment, anything # can ### happen ;;; right?\n\
+ log per instance = true\n\
+ profiling logger = true\n\
+ profiling logger dir = log\n\
+ chdir = ""\n\
+ pid file\t=\tfoo2\n\
+[osd0]\n\
+ keyring = osd_keyring ; osd's keyring\n\
+\n\
+ \n\
+[global]\n\
+ # I like pound signs as comment markers.\n\
+ ; Do you like pound signs as comment markers?\n\
+ keyring = shenanigans ; The keyring of a leprechaun\n\
+\n\
+ # Let's just have a line with a lot of whitespace and nothing else.\n\
+ \n\
+ lockdep = 1\n\
+";
+
+// test line-combining
+const char * const conf3 = "\
+[global]\n\
+ log file = /quite/a/long/path\\\n\
+/for/a/log/file\n\
+ pid file = \\\n\
+ spork\\\n\
+\n\
+[mon] #nothing here \n\
+";
+
+const char * const escaping_conf_1 = "\
+[global]\n\
+ log file = the \"scare quotes\"\n\
+ pid file = a \\\n\
+pid file\n\
+[mon]\n\
+ keyring = \"nested \\\"quotes\\\"\"\n\
+";
+
+const char * const escaping_conf_2 = "\
+[apple \\]\\[]\n\
+ log file = floppy disk\n\
+[mon]\n\
+ keyring = \"backslash\\\\\"\n\
+";
+
+// illegal because it contains an invalid utf8 sequence.
+const char illegal_conf1[] = "\
+[global]\n\
+ log file = foo\n\
+ pid file = invalid-utf-\xe2\x28\xa1\n\
+[osd0]\n\
+ keyring = osd_keyring ; osd's keyring\n\
+";
+
+// illegal because it contains a malformed section header.
+const char illegal_conf2[] = "\
+[global\n\
+ log file = foo\n\
+[osd0]\n\
+ keyring = osd_keyring ; osd's keyring\n\
+";
+
+// illegal because it contains a line that doesn't parse
+const char illegal_conf3[] = "\
+[global]\n\
+ who_what_where\n\
+[osd0]\n\
+ keyring = osd_keyring ; osd's keyring\n\
+";
+
+// illegal because it has unterminated quotes
+const char illegal_conf4[] = "\
+[global]\n\
+ keyring = \"unterminated quoted string\n\
+[osd0]\n\
+ keyring = osd_keyring ; osd's keyring\n\
+";
+
+const char override_config_1[] = "\
+[global]\n\
+ log file = global_log\n\
+[mds]\n\
+ log file = mds_log\n\
+[osd]\n\
+ log file = osd_log\n\
+[osd.0]\n\
+ log file = osd0_log\n\
+";
+
+const char dup_key_config_1[] = "\
+[mds.a]\n\
+ log_file = 1\n\
+ log_file = 3\n\
+";
+
+TEST(ConfUtils, ParseFiles0) {
+ std::string val;
+
+ {
+ std::ostringstream err;
+ std::string trivial_conf_1_f(next_tempfile(trivial_conf_1));
+ ConfFile cf1;
+ ASSERT_EQ(cf1.parse_file(trivial_conf_1_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+ }
+ {
+ std::ostringstream err;
+ std::string trivial_conf_2_f(next_tempfile(trivial_conf_2));
+ ConfFile cf2;
+ ASSERT_EQ(cf2.parse_file(trivial_conf_2_f.c_str(), &err), -EINVAL);
+ ASSERT_GT(err.tellp(), 0U);
+ }
+ {
+ std::ostringstream err;
+ bufferlist bl3;
+ bl3.append(trivial_conf_3, strlen(trivial_conf_3));
+ ConfFile cf3;
+ ASSERT_EQ(cf3.parse_bufferlist(&bl3, &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+ ASSERT_EQ(cf3.read("global", "log dir", val), 0);
+ ASSERT_EQ(val, "barfoo");
+ }
+ {
+ std::ostringstream err;
+ std::string trivial_conf_4_f(next_tempfile(trivial_conf_4));
+ ConfFile cf4;
+ ASSERT_EQ(cf4.parse_file(trivial_conf_4_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+ ASSERT_EQ(cf4.read("global", "log dir", val), 0);
+ ASSERT_EQ(val, "barbaz");
+ }
+}
+
+TEST(ConfUtils, ParseFiles1) {
+ std::ostringstream err;
+ std::string simple_conf_1_f(next_tempfile(simple_conf_1));
+ ConfFile cf1;
+ ASSERT_EQ(cf1.parse_file(simple_conf_1_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+
+ std::string simple_conf_2_f(next_tempfile(simple_conf_1));
+ ConfFile cf2;
+ ASSERT_EQ(cf2.parse_file(simple_conf_2_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+
+ bufferlist bl3;
+ bl3.append(simple_conf_1, strlen(simple_conf_1));
+ ConfFile cf3;
+ ASSERT_EQ(cf3.parse_bufferlist(&bl3, &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+
+ bufferlist bl4;
+ bl4.append(simple_conf_2, strlen(simple_conf_2));
+ ConfFile cf4;
+ ASSERT_EQ(cf4.parse_bufferlist(&bl4, &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+}
+
+TEST(ConfUtils, ReadFiles1) {
+ std::ostringstream err;
+ std::string simple_conf_1_f(next_tempfile(simple_conf_1));
+ ConfFile cf1;
+ ASSERT_EQ(cf1.parse_file(simple_conf_1_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+
+ std::string val;
+ ASSERT_EQ(cf1.read("global", "keyring", val), 0);
+ ASSERT_EQ(val, ".my_ceph_keyring");
+
+ ASSERT_EQ(cf1.read("mds", "profiling logger dir", val), 0);
+ ASSERT_EQ(val, "wowsers");
+
+ ASSERT_EQ(cf1.read("mds", "something that does not exist", val), -ENOENT);
+
+ // exists in mds section, but not in global
+ ASSERT_EQ(cf1.read("global", "profiling logger dir", val), -ENOENT);
+
+ bufferlist bl2;
+ bl2.append(simple_conf_2, strlen(simple_conf_2));
+ ConfFile cf2;
+ ASSERT_EQ(cf2.parse_bufferlist(&bl2, &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+ ASSERT_EQ(cf2.read("osd0", "keyring", val), 0);
+ ASSERT_EQ(val, "osd_keyring");
+
+ ASSERT_EQ(cf2.read("mds", "pid file", val), 0);
+ ASSERT_EQ(val, "foo2");
+ ASSERT_EQ(cf2.read("nonesuch", "keyring", val), -ENOENT);
+}
+
+TEST(ConfUtils, ReadFiles2) {
+ std::ostringstream err;
+ std::string conf3_f(next_tempfile(conf3));
+ ConfFile cf1;
+ std::string val;
+ ASSERT_EQ(cf1.parse_file(conf3_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+ ASSERT_EQ(cf1.read("global", "log file", val), 0);
+ ASSERT_EQ(val, "/quite/a/long/path/for/a/log/file");
+ ASSERT_EQ(cf1.read("global", "pid file", val), 0);
+ ASSERT_EQ(val, "spork");
+}
+
+TEST(ConfUtils, IllegalFiles) {
+ {
+ std::ostringstream err;
+ ConfFile cf1;
+ std::string illegal_conf1_f(next_tempfile(illegal_conf1));
+ ASSERT_EQ(cf1.parse_file(illegal_conf1_f.c_str(), &err), -EINVAL);
+ ASSERT_GT(err.tellp(), 0U);
+ }
+ {
+ std::ostringstream err;
+ bufferlist bl2;
+ bl2.append(illegal_conf2, strlen(illegal_conf2));
+ ConfFile cf2;
+ ASSERT_EQ(cf2.parse_bufferlist(&bl2, &err), -EINVAL);
+ ASSERT_GT(err.tellp(), 0U);
+ }
+ {
+ std::ostringstream err;
+ std::string illegal_conf3_f(next_tempfile(illegal_conf3));
+ ConfFile cf3;
+ ASSERT_EQ(cf3.parse_file(illegal_conf3_f.c_str(), &err), -EINVAL);
+ ASSERT_GT(err.tellp(), 0U);
+ }
+ {
+ std::ostringstream err;
+ std::string illegal_conf4_f(next_tempfile(illegal_conf4));
+ ConfFile cf4;
+ ASSERT_EQ(cf4.parse_file(illegal_conf4_f.c_str(), &err), -EINVAL);
+ ASSERT_GT(err.tellp(), 0U);
+ }
+}
+
+TEST(ConfUtils, EscapingFiles) {
+ std::ostringstream err;
+ std::string escaping_conf_1_f(next_tempfile(escaping_conf_1));
+ ConfFile cf1;
+ std::string val;
+ ASSERT_EQ(cf1.parse_file(escaping_conf_1_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+
+ ASSERT_EQ(cf1.read("global", "log file", val), 0);
+ ASSERT_EQ(val, "the \"scare quotes\"");
+ ASSERT_EQ(cf1.read("global", "pid file", val), 0);
+ ASSERT_EQ(val, "a pid file");
+ ASSERT_EQ(cf1.read("mon", "keyring", val), 0);
+ ASSERT_EQ(val, "nested \"quotes\"");
+
+ std::string escaping_conf_2_f(next_tempfile(escaping_conf_2));
+ ConfFile cf2;
+ ASSERT_EQ(cf2.parse_file(escaping_conf_2_f.c_str(), &err), 0);
+ ASSERT_EQ(err.tellp(), 0U);
+
+ ASSERT_EQ(cf2.read("apple ][", "log file", val), 0);
+ ASSERT_EQ(val, "floppy disk");
+ ASSERT_EQ(cf2.read("mon", "keyring", val), 0);
+ ASSERT_EQ(val, "backslash\\");
+}
+
+TEST(ConfUtils, Overrides) {
+ ConfigProxy conf{false};
+ std::ostringstream warn;
+ std::string override_conf_1_f(next_tempfile(override_config_1));
+
+ conf->name.set(CEPH_ENTITY_TYPE_MON, "0");
+ conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
+ ASSERT_FALSE(conf.has_parse_error());
+ ASSERT_EQ(conf->log_file, "global_log");
+
+ conf->name.set(CEPH_ENTITY_TYPE_MDS, "a");
+ conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
+ ASSERT_FALSE(conf.has_parse_error());
+ ASSERT_EQ(conf->log_file, "mds_log");
+
+ conf->name.set(CEPH_ENTITY_TYPE_OSD, "0");
+ conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
+ ASSERT_FALSE(conf.has_parse_error());
+ ASSERT_EQ(conf->log_file, "osd0_log");
+}
+
+TEST(ConfUtils, DupKey) {
+ ConfigProxy conf{false};
+ std::ostringstream warn;
+ std::string dup_key_config_f(next_tempfile(dup_key_config_1));
+
+ conf->name.set(CEPH_ENTITY_TYPE_MDS, "a");
+ conf.parse_config_files(dup_key_config_f.c_str(), &warn, 0);
+ ASSERT_FALSE(conf.has_parse_error());
+ ASSERT_EQ(conf->log_file, string("3"));
+}
+
+
diff --git a/src/test/coverage.sh b/src/test/coverage.sh
new file mode 100755
index 000000000..76591719a
--- /dev/null
+++ b/src/test/coverage.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+set -e
+
+usage () {
+ printf '%s: usage: %s [-d srcdir] [-o output_basename] COMMAND [ARGS..]\n' "$(basename "$0")" "$(basename "$0")" 1>&2
+ exit 1
+}
+
+OUTPUT_BASENAME=coverage
+SRCDIR=.
+
+while getopts "d:o:h" flag
+do
+ case $flag in
+ d) SRCDIR=$OPTARG;;
+ o) OUTPUT_BASENAME=$OPTARG;;
+ *) usage;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+
+lcov -d $SRCDIR -z > /dev/null 2>&1
+lcov -d $SRCDIR -c -i -o "${OUTPUT_BASENAME}_base_full.lcov" > /dev/null 2>&1
+"$@"
+lcov -d $SRCDIR -c -o "${OUTPUT_BASENAME}_tested_full.lcov" > /dev/null 2>&1
+lcov -r "${OUTPUT_BASENAME}_base_full.lcov" /usr/include\* -o "${OUTPUT_BASENAME}_base.lcov" > /dev/null 2>&1
+lcov -r "${OUTPUT_BASENAME}_tested_full.lcov" /usr/include\* -o "${OUTPUT_BASENAME}_tested.lcov" > /dev/null 2>&1
+lcov -a "${OUTPUT_BASENAME}_base.lcov" -a "${OUTPUT_BASENAME}_tested.lcov" -o "${OUTPUT_BASENAME}.lcov" | tail -n 3
diff --git a/src/test/crimson/CMakeLists.txt b/src/test/crimson/CMakeLists.txt
new file mode 100644
index 000000000..b1851cca2
--- /dev/null
+++ b/src/test/crimson/CMakeLists.txt
@@ -0,0 +1,105 @@
+# the crimson's backfill doesn't need nor use seastar
+add_executable(unittest-crimson-backfill
+ test_backfill.cc
+ ${PROJECT_SOURCE_DIR}/src/auth/Crypto.cc
+ ${PROJECT_SOURCE_DIR}/src/crimson/osd/backfill_state.cc
+ ${PROJECT_SOURCE_DIR}/src/osd/recovery_types.cc)
+add_ceph_unittest(unittest-crimson-backfill
+ --memory 256M --smp 1)
+target_link_libraries(unittest-crimson-backfill crimson GTest::Main)
+
+add_executable(unittest-seastar-buffer
+ test_buffer.cc)
+add_ceph_unittest(unittest-seastar-buffer
+ --memory 256M --smp 1)
+target_link_libraries(unittest-seastar-buffer crimson)
+
+add_executable(unittest-seastar-denc
+ test_denc.cc)
+add_ceph_unittest(unittest-seastar-denc --memory 256M --smp 1)
+target_link_libraries(unittest-seastar-denc crimson GTest::Main)
+
+add_executable(unittest-seastar-socket test_socket.cc)
+add_ceph_unittest(unittest-seastar-socket
+ --memory 256M --smp 4)
+target_link_libraries(unittest-seastar-socket crimson)
+
+add_executable(unittest-seastar-messenger test_messenger.cc)
+add_ceph_unittest(unittest-seastar-messenger
+ --memory 256M --smp 4)
+target_link_libraries(unittest-seastar-messenger crimson)
+
+add_executable(test-seastar-messenger-peer test_messenger_peer.cc)
+target_link_libraries(test-seastar-messenger-peer ceph-common global ${ALLOC_LIBS})
+
+add_executable(test-seastar-echo
+ test_alien_echo.cc)
+target_link_libraries(test-seastar-echo crimson)
+
+add_executable(test-async-echo
+ test_async_echo.cc)
+target_link_libraries(test-async-echo ceph-common global)
+
+add_executable(unittest-seastar-alienstore-thread-pool
+ test_alienstore_thread_pool.cc
+ ${PROJECT_SOURCE_DIR}/src/crimson/osd/lsan_suppressions.cc)
+add_ceph_unittest(unittest-seastar-alienstore-thread-pool
+ --memory 256M --smp 1)
+target_link_libraries(unittest-seastar-alienstore-thread-pool
+ crimson-alienstore
+ crimson)
+
+add_executable(unittest-seastar-config
+ test_config.cc)
+add_ceph_unittest(unittest-seastar-config
+ --memory 256M --smp 4)
+target_link_libraries(unittest-seastar-config crimson)
+
+add_executable(unittest-seastar-monc
+ test_monc.cc)
+target_link_libraries(unittest-seastar-monc crimson)
+
+add_executable(unittest-seastar-perfcounters
+ test_perfcounters.cc)
+add_ceph_unittest(unittest-seastar-perfcounters
+ --memory 256M --smp 1)
+target_link_libraries(unittest-seastar-perfcounters crimson)
+
+add_executable(unittest-seastar-lru
+ test_lru.cc)
+add_ceph_unittest(unittest-seastar-lru
+ --memory 256M --smp 1)
+target_link_libraries(unittest-seastar-lru crimson GTest::Main)
+
+add_executable(unittest-fixed-kv-node-layout
+ test_fixed_kv_node_layout.cc)
+add_ceph_unittest(unittest-fixed-kv-node-layout)
+
+add_executable(unittest-interruptible-future
+ test_interruptible_future.cc
+ gtest_seastar.cc)
+add_ceph_unittest(unittest-interruptible-future
+ --memory 256M --smp 1)
+target_link_libraries(
+ unittest-interruptible-future
+ crimson-common)
+
+add_executable(unittest-seastar-messenger-thrash test_messenger_thrash.cc)
+add_ceph_unittest(unittest-seastar-messenger-thrash
+ --memory 256M --smp 1)
+target_link_libraries(unittest-seastar-messenger-thrash crimson)
+
+add_subdirectory(seastore)
+
+add_library(crimson-gtest STATIC
+ gtest_seastar.cc)
+target_link_libraries(crimson-gtest crimson-common GTest::GTest)
+add_library(crimson::gtest ALIAS crimson-gtest)
+
+add_executable(unittest-seastar-errorator
+ test_errorator.cc)
+target_link_libraries(
+ unittest-seastar-errorator
+ crimson::gtest)
+add_ceph_unittest(unittest-seastar-errorator
+ --memory 256M --smp 1)
diff --git a/src/test/crimson/cbt/radosbench_4K_read.yaml b/src/test/crimson/cbt/radosbench_4K_read.yaml
new file mode 100644
index 000000000..219ce643a
--- /dev/null
+++ b/src/test/crimson/cbt/radosbench_4K_read.yaml
@@ -0,0 +1,36 @@
+meta:
+- desc: |
+ Run radosbench benchmark using cbt.
+ 4K read workload.
+
+tasks:
+- cbt:
+ benchmarks:
+ radosbench:
+ concurrent_ops: 16
+ concurrent_procs: 2
+ op_size: [4096]
+ pool_profile: 'replicated'
+ read_time: 30
+ read_only: true
+ readmode: 'rand'
+ prefill_time: 3
+ acceptable:
+ bandwidth: '(or (greater) (near 0.05))'
+ iops_avg: '(or (greater) (near 0.05))'
+ iops_stddev: '(or (less) (near 2.00))'
+ latency_avg: '(or (less) (near 0.05))'
+ cpu_cycles_per_op: '(or (less) (near 0.05))'
+ monitoring_profiles:
+ perf:
+ nodes:
+ - osds
+ args: 'stat -p {pid} -o {perf_dir}/perf_stat.{pid}'
+ cluster:
+ osds_per_node: 3
+ iterations: 1
+ pool_profiles:
+ replicated:
+ pg_size: 128
+ pgp_size: 128
+ replication: 'replicated'
diff --git a/src/test/crimson/cbt/radosbench_4K_write.yaml b/src/test/crimson/cbt/radosbench_4K_write.yaml
new file mode 100644
index 000000000..526982b10
--- /dev/null
+++ b/src/test/crimson/cbt/radosbench_4K_write.yaml
@@ -0,0 +1,34 @@
+meta:
+- desc: |
+ Run radosbench benchmark using cbt.
+ 4K write workload.
+
+tasks:
+- cbt:
+ benchmarks:
+ radosbench:
+ concurrent_ops: 16
+ concurrent_procs: 2
+ op_size: [4096]
+ pool_profile: 'replicated'
+ write_time: 3
+ write_only: true
+ acceptable:
+ bandwidth: '(or (greater) (near 0.05))'
+ iops_avg: '(or (greater) (near 0.05))'
+ iops_stddev: '(or (less) (near 2.00))'
+ latency_avg: '(or (less) (near 0.05))'
+ cpu_cycles_per_op: '(or (less) (near 0.05))'
+ monitoring_profiles:
+ perf:
+ nodes:
+ - osds
+ args: 'stat -p {pid} -o {perf_dir}/perf_stat.{pid}'
+ cluster:
+ osds_per_node: 3
+ iterations: 1
+ pool_profiles:
+ replicated:
+ pg_size: 128
+ pgp_size: 128
+ replication: 'replicated'
diff --git a/src/test/crimson/cbt/t2c.py b/src/test/crimson/cbt/t2c.py
new file mode 100755
index 000000000..0d4ee49e5
--- /dev/null
+++ b/src/test/crimson/cbt/t2c.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+
+from __future__ import print_function
+import argparse
+import os
+import os.path
+import socket
+import sys
+import yaml
+
+
+class Translator(object):
+ def __init__(self, build_dir):
+ self.build_dir = build_dir
+
+ def translate(self, config):
+ cluster = config.get('cluster', {})
+ benchmarks = config.get('benchmarks', [])
+ monitoring_profiles = config.get('monitoring_profiles', {})
+ return dict(cluster=self._create_cluster_config(cluster),
+ benchmarks=benchmarks,
+ monitoring_profiles=monitoring_profiles)
+
+ def _create_cluster_config(self, cluster):
+ # prepare the "cluster" section consumed by CBT
+ localhost = socket.getfqdn()
+ num_osds = cluster.get('osds_per_node', 3)
+ items_to_copy = ['iterations', 'pool_profiles']
+ conf = dict((k, cluster[k]) for k in items_to_copy if k in cluster)
+ conf.update(dict(
+ head=localhost,
+ osds=[localhost],
+ osds_per_node=num_osds,
+ mons=[localhost],
+ clients=[localhost],
+ rebuild_every_test=False,
+ conf_file=os.path.join(self.build_dir, 'ceph.conf'),
+ ceph_cmd=os.path.join(self.build_dir, 'bin', 'ceph'),
+ rados_cmd=os.path.join(self.build_dir, 'bin', 'rados'),
+ pid_dir=os.path.join(self.build_dir, 'out')
+ ))
+ return conf
+
+def get_cbt_tasks(path):
+ with open(path) as input:
+ teuthology_config = yaml.load(input)
+ for task in teuthology_config['tasks']:
+ for name, conf in task.items():
+ if name == 'cbt':
+ yield conf
+
+def main():
+ parser = argparse.ArgumentParser(description='translate teuthology yaml to CBT yaml')
+ parser.add_argument('--build-dir',
+ default=os.getcwd(),
+ required=False,
+ help='Directory where CMakeCache.txt is located')
+ parser.add_argument('--input',
+ required=True,
+ help='The path to the input YAML file')
+ parser.add_argument('--output',
+ required=True,
+ help='The path to the output YAML file')
+ options = parser.parse_args(sys.argv[1:])
+ cbt_tasks = [task for task in get_cbt_tasks(options.input)]
+ if not cbt_tasks:
+ print('cbt not found in "tasks" section', file=sys.stderr)
+ return sys.exit(1)
+ elif len(cbt_tasks) > 1:
+ print('more than one cbt task found in "tasks" section', file=sys.stderr)
+ return sys.exit(1)
+ translator = Translator(options.build_dir)
+ cbt_config = translator.translate(cbt_tasks[0])
+ with open(options.output, 'w') as output:
+ yaml.dump(cbt_config, output)
+
+if __name__ == '__main__':
+ main()
diff --git a/src/test/crimson/gtest_seastar.cc b/src/test/crimson/gtest_seastar.cc
new file mode 100644
index 000000000..abb1f88f2
--- /dev/null
+++ b/src/test/crimson/gtest_seastar.cc
@@ -0,0 +1,65 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <cstdlib>
+#include <iostream>
+
+#include "include/ceph_assert.h"
+#include "gtest_seastar.h"
+
+#include "common/ceph_argparse.h"
+#include "crimson/common/config_proxy.h"
+#include "crimson/common/perf_counters_collection.h"
+
+SeastarRunner seastar_test_suite_t::seastar_env;
+
+int main(int argc, char **argv)
+{
+ // preprocess args
+ std::vector<const char*> args;
+ bool global_log_level_is_set = false;
+ const char* prefix_log_level = "--default-log-level";
+ for (int i = 0; i < argc; ++i) {
+ if (std::strncmp(argv[i], prefix_log_level,
+ std::strlen(prefix_log_level)) == 0) {
+ global_log_level_is_set = true;
+ }
+ args.push_back(argv[i]);
+ }
+ // HACK: differentiate between the `make check` bot and human user
+ // for the sake of log flooding
+ if (!global_log_level_is_set && !std::getenv("FOR_MAKE_CHECK")) {
+ std::cout << "WARNING: set default seastar log level to debug" << std::endl;
+ ++argc;
+ args.push_back("--default-log-level=debug");
+ }
+
+ auto app_argv = const_cast<char**>(args.data());
+ auto app_argc = static_cast<int>(args.size());
+ ::testing::InitGoogleTest(&app_argc, app_argv);
+
+ int ret = seastar_test_suite_t::seastar_env.init(app_argc, app_argv);
+ if (ret != 0) {
+ seastar_test_suite_t::seastar_env.stop();
+ return ret;
+ }
+
+ seastar_test_suite_t::seastar_env.run([] {
+ return crimson::common::sharded_conf().start(
+ EntityName{}, std::string_view{"ceph"}
+ ).then([] {
+ return crimson::common::sharded_perf_coll().start();
+ });
+ });
+
+ ret = RUN_ALL_TESTS();
+
+ seastar_test_suite_t::seastar_env.run([] {
+ return crimson::common::sharded_perf_coll().stop().then([] {
+ return crimson::common::sharded_conf().stop();
+ });
+ });
+
+ seastar_test_suite_t::seastar_env.stop();
+ return ret;
+}
diff --git a/src/test/crimson/gtest_seastar.h b/src/test/crimson/gtest_seastar.h
new file mode 100644
index 000000000..20709a3ee
--- /dev/null
+++ b/src/test/crimson/gtest_seastar.h
@@ -0,0 +1,35 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include "gtest/gtest.h"
+
+#include "seastar_runner.h"
+
+struct seastar_test_suite_t : public ::testing::Test {
+ static SeastarRunner seastar_env;
+
+ template <typename Func>
+ void run(Func &&func) {
+ return seastar_env.run(std::forward<Func>(func));
+ }
+
+ template <typename Func>
+ void run_async(Func &&func) {
+ run(
+ [func=std::forward<Func>(func)]() mutable {
+ return seastar::async(std::forward<Func>(func));
+ });
+ }
+
+ virtual seastar::future<> set_up_fut() { return seastar::now(); }
+ void SetUp() final {
+ return run([this] { return set_up_fut(); });
+ }
+
+ virtual seastar::future<> tear_down_fut() { return seastar::now(); }
+ void TearDown() final {
+ return run([this] { return tear_down_fut(); });
+ }
+};
diff --git a/src/test/crimson/seastar_runner.h b/src/test/crimson/seastar_runner.h
new file mode 100644
index 000000000..58d3f8119
--- /dev/null
+++ b/src/test/crimson/seastar_runner.h
@@ -0,0 +1,102 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <stdio.h>
+#include <signal.h>
+#include <thread>
+
+#include <seastar/core/app-template.hh>
+#include <seastar/core/future-util.hh>
+#include <seastar/core/reactor.hh>
+#include <seastar/core/alien.hh>
+#include <seastar/core/thread.hh>
+
+struct SeastarRunner {
+ static constexpr eventfd_t APP_RUNNING = 1;
+ static constexpr eventfd_t APP_NOT_RUN = 2;
+
+ seastar::app_template app;
+ seastar::file_desc begin_fd;
+ std::unique_ptr<seastar::readable_eventfd> on_end;
+
+ std::thread thread;
+
+ bool begin_signaled = false;
+
+ SeastarRunner() :
+ begin_fd{seastar::file_desc::eventfd(0, 0)} {}
+
+ ~SeastarRunner() {}
+
+ bool is_running() const {
+ return !!on_end;
+ }
+
+ int init(int argc, char **argv)
+ {
+ thread = std::thread([argc, argv, this] { reactor(argc, argv); });
+ eventfd_t result;
+ if (int r = ::eventfd_read(begin_fd.get(), &result); r < 0) {
+ std::cerr << "unable to eventfd_read():" << errno << std::endl;
+ return r;
+ }
+ assert(begin_signaled == true);
+ if (result == APP_RUNNING) {
+ assert(is_running());
+ return 0;
+ } else {
+ assert(result == APP_NOT_RUN);
+ assert(!is_running());
+ return 1;
+ }
+ }
+
+ void stop()
+ {
+ if (is_running()) {
+ run([this] {
+ on_end->write_side().signal(1);
+ return seastar::now();
+ });
+ }
+ thread.join();
+ }
+
+ void reactor(int argc, char **argv)
+ {
+ auto ret = app.run(argc, argv, [this] {
+ on_end.reset(new seastar::readable_eventfd);
+ return seastar::now().then([this] {
+ begin_signaled = true;
+ [[maybe_unused]] auto r = ::eventfd_write(begin_fd.get(), APP_RUNNING);
+ assert(r == 0);
+ return seastar::now();
+ }).then([this] {
+ return on_end->wait().then([](size_t){});
+ }).handle_exception([](auto ep) {
+ std::cerr << "Error: " << ep << std::endl;
+ }).finally([this] {
+ on_end.reset();
+ });
+ });
+ if (ret != 0) {
+ std::cerr << "Seastar app returns " << ret << std::endl;
+ }
+ if (!begin_signaled) {
+ begin_signaled = true;
+ ::eventfd_write(begin_fd.get(), APP_NOT_RUN);
+ }
+ }
+
+ template <typename Func>
+ void run(Func &&func) {
+ assert(is_running());
+ auto fut = seastar::alien::submit_to(app.alien(), 0,
+ std::forward<Func>(func));
+ fut.get();
+ }
+};
+
+
diff --git a/src/test/crimson/seastore/CMakeLists.txt b/src/test/crimson/seastore/CMakeLists.txt
new file mode 100644
index 000000000..5c6c2771c
--- /dev/null
+++ b/src/test/crimson/seastore/CMakeLists.txt
@@ -0,0 +1,128 @@
+add_executable(unittest-transaction-manager
+ test_block.cc
+ test_transaction_manager.cc
+ ../gtest_seastar.cc)
+add_ceph_unittest(unittest-transaction-manager
+ --memory 256M --smp 1)
+target_link_libraries(
+ unittest-transaction-manager
+ ${CMAKE_DL_LIBS}
+ crimson-seastore)
+
+add_executable(unittest-btree-lba-manager
+ test_btree_lba_manager.cc
+ ../gtest_seastar.cc)
+add_ceph_unittest(unittest-btree-lba-manager
+ --memory 256M --smp 1)
+target_link_libraries(
+ unittest-btree-lba-manager
+ ${CMAKE_DL_LIBS}
+ crimson-seastore)
+
+add_executable(unittest-seastore-journal
+ test_seastore_journal.cc)
+add_ceph_test(unittest-seastore-journal
+ unittest-seastore-journal --memory 256M --smp 1)
+target_link_libraries(
+ unittest-seastore-journal
+ crimson::gtest
+ crimson-seastore)
+
+add_executable(unittest-seastore-cache
+ test_block.cc
+ test_seastore_cache.cc)
+add_ceph_test(unittest-seastore-cache
+ unittest-seastore-cache --memory 256M --smp 1)
+target_link_libraries(
+ unittest-seastore-cache
+ crimson::gtest
+ crimson-seastore)
+
+add_executable(unittest-object-data-handler
+ test_object_data_handler.cc
+ ../gtest_seastar.cc
+ ${PROJECT_SOURCE_DIR}/src/crimson/osd/lsan_suppressions.cc)
+add_ceph_unittest(unittest-object-data-handler
+ --memory 256M --smp 1)
+target_link_libraries(
+ unittest-object-data-handler
+ crimson::gtest
+ crimson-seastore
+ crimson-os
+ crimson-common)
+
+add_executable(unittest-collection-manager
+ test_collection_manager.cc
+ ../gtest_seastar.cc
+ ${PROJECT_SOURCE_DIR}/src/crimson/osd/lsan_suppressions.cc)
+add_ceph_test(unittest-collection-manager
+ unittest-collection-manager --memory 256M --smp 1)
+target_link_libraries(
+ unittest-collection-manager
+ crimson::gtest
+ crimson-seastore
+ crimson-os
+ crimson-common)
+
+add_executable(unittest-omap-manager
+ test_omap_manager.cc
+ ../gtest_seastar.cc)
+add_ceph_unittest(unittest-omap-manager
+ --memory 256M --smp 1)
+target_link_libraries(
+ unittest-omap-manager
+ ${CMAKE_DL_LIBS}
+ crimson-seastore)
+
+add_executable(unittest-seastore
+ test_seastore.cc
+ ../gtest_seastar.cc)
+add_ceph_unittest(unittest-seastore
+ --memory 256M --smp 1)
+target_link_libraries(
+ unittest-seastore
+ ${CMAKE_DL_LIBS}
+ crimson-seastore
+ crimson-common)
+
+add_executable(unittest-seastore-randomblock-manager
+ test_randomblock_manager.cc)
+add_ceph_test(unittest-seastore-randomblock-manager
+ unittest-seastore-randomblock-manager --memory 256M --smp 1)
+target_link_libraries(
+ unittest-seastore-randomblock-manager
+ crimson::gtest
+ ${CMAKE_DL_LIBS}
+ crimson-seastore)
+
+add_executable(unittest-seastore-nvmedevice
+ nvmedevice/test_nvmedevice.cc)
+add_ceph_test(unittest-seastore-nvmedevice
+ unittest-seastore-nvmedevice --memory 256M --smp 1)
+target_link_libraries(
+ unittest-seastore-nvmedevice
+ crimson::gtest
+ crimson-seastore
+ aio)
+
+add_executable(unittest-seastore-cbjournal
+ test_cbjournal.cc)
+add_ceph_test(unittest-seastore-cbjournal
+ unittest-seastore-cbjournal --memory 256M --smp 1)
+target_link_libraries(
+ unittest-seastore-cbjournal
+ crimson::gtest
+ crimson-seastore
+ aio)
+
+add_executable(unittest-seastore-extent-allocator
+ test_extent_allocator.cc)
+add_ceph_test(unittest-seastore-extent-allocator
+ unittest-seastore-extent-allocator --memory 256M --smp 1)
+target_link_libraries(
+ unittest-seastore-extent-allocator
+ crimson::gtest
+ crimson-seastore
+ aio)
+
+add_subdirectory(onode_tree)
diff --git a/src/test/crimson/seastore/nvmedevice/test_nvmedevice.cc b/src/test/crimson/seastore/nvmedevice/test_nvmedevice.cc
new file mode 100644
index 000000000..9c2f4c246
--- /dev/null
+++ b/src/test/crimson/seastore/nvmedevice/test_nvmedevice.cc
@@ -0,0 +1,105 @@
+//-*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/buffer.h"
+#include "crimson/os/seastore/random_block_manager/rbm_device.h"
+#include "crimson/os/seastore/random_block_manager/nvme_block_device.h"
+#include "test/crimson/gtest_seastar.h"
+#include "include/stringify.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+using namespace random_block_device;
+using namespace random_block_device::nvme;
+
+struct nvdev_test_t : seastar_test_suite_t {
+ std::unique_ptr<RBMDevice> device;
+ std::string dev_path;
+
+ static const uint64_t DEV_SIZE = 1024 * 1024 * 1024;
+
+ nvdev_test_t() :
+ device(nullptr),
+ dev_path("randomblock_manager.test_nvmedevice" + stringify(getpid())) {
+ int fd = ::open(dev_path.c_str(), O_CREAT|O_RDWR|O_TRUNC, 0644);
+ ceph_assert(fd >= 0);
+ ::ftruncate(fd, DEV_SIZE);
+ ::close(fd);
+ }
+ ~nvdev_test_t() {
+ ::unlink(dev_path.c_str());
+ }
+};
+
+static const uint64_t BUF_SIZE = 1024;
+static const uint64_t BLK_SIZE = 4096;
+
+struct nvdev_test_block_t {
+ uint8_t data[BUF_SIZE];
+
+ DENC(nvdev_test_block_t, v, p) {
+ DENC_START(1, 1, p);
+ for (uint64_t i = 0 ; i < BUF_SIZE; i++)
+ {
+ denc(v.data[i], p);
+ }
+ DENC_FINISH(p);
+ }
+};
+
+WRITE_CLASS_DENC_BOUNDED(
+ nvdev_test_block_t
+)
+
+using crimson::common::local_conf;
+TEST_F(nvdev_test_t, write_and_verify_test)
+{
+ run_async([this] {
+ device.reset(new random_block_device::nvme::NVMeBlockDevice(dev_path));
+ local_conf().set_val("seastore_cbjournal_size", "1048576").get();
+ device->start().get();
+ device->mkfs(
+ device_config_t{
+ true,
+ device_spec_t{
+ (magic_t)std::rand(),
+ device_type_t::RANDOM_BLOCK_SSD,
+ static_cast<device_id_t>(DEVICE_ID_RANDOM_BLOCK_MIN)},
+ seastore_meta_t{uuid_d()},
+ secondary_device_set_t()}
+ ).unsafe_get();
+ device->mount().unsafe_get();
+ nvdev_test_block_t original_data;
+ std::minstd_rand0 generator;
+ uint8_t value = generator();
+ memset(original_data.data, value, BUF_SIZE);
+ uint64_t bl_length = 0;
+ Device& d = device->get_sharded_device();
+ {
+ bufferlist bl;
+ encode(original_data, bl);
+ bl_length = bl.length();
+ auto write_buf = ceph::bufferptr(buffer::create_page_aligned(BLK_SIZE));
+ bl.begin().copy(bl_length, write_buf.c_str());
+ ((RBMDevice*)&d)->write(0, std::move(write_buf)).unsafe_get();
+ }
+
+ nvdev_test_block_t read_data;
+ {
+ auto read_buf = ceph::bufferptr(buffer::create_page_aligned(BLK_SIZE));
+ ((RBMDevice*)&d)->read(0, read_buf).unsafe_get();
+ bufferlist bl;
+ bl.push_back(read_buf);
+ auto bliter = bl.cbegin();
+ decode(read_data, bliter);
+ }
+
+ int ret = memcmp(original_data.data, read_data.data, BUF_SIZE);
+ ((RBMDevice*)&d)->close().unsafe_get();
+ device->stop().get();
+ ASSERT_TRUE(ret == 0);
+ device.reset(nullptr);
+ });
+}
+
diff --git a/src/test/crimson/seastore/onode_tree/CMakeLists.txt b/src/test/crimson/seastore/onode_tree/CMakeLists.txt
new file mode 100644
index 000000000..bea208601
--- /dev/null
+++ b/src/test/crimson/seastore/onode_tree/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_executable(unittest-staged-fltree
+ test_staged_fltree.cc
+ ../../gtest_seastar.cc)
+add_ceph_unittest(unittest-staged-fltree
+ --memory 256M --smp 1)
+target_link_libraries(unittest-staged-fltree
+ crimson-seastore)
+
+add_executable(unittest-fltree-onode-manager
+ test_fltree_onode_manager.cc
+ ../../gtest_seastar.cc)
+add_ceph_unittest(unittest-fltree-onode-manager
+ --memory 256M --smp 1)
+target_link_libraries(unittest-fltree-onode-manager
+ crimson-seastore)
diff --git a/src/test/crimson/seastore/onode_tree/test_fltree_onode_manager.cc b/src/test/crimson/seastore/onode_tree/test_fltree_onode_manager.cc
new file mode 100644
index 000000000..1f661cdca
--- /dev/null
+++ b/src/test/crimson/seastore/onode_tree/test_fltree_onode_manager.cc
@@ -0,0 +1,330 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <boost/range/combine.hpp>
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+
+#include "crimson/os/seastore/onode_manager/staged-fltree/fltree_onode_manager.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+using namespace crimson::os::seastore::onode;
+using CTransaction = ceph::os::Transaction;
+using namespace std;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+struct onode_item_t {
+ uint32_t size;
+ uint64_t id;
+ uint64_t block_size;
+ uint32_t cnt_modify = 0;
+
+ void initialize(Transaction& t, Onode& value) const {
+ auto& layout = value.get_mutable_layout(t);
+ layout.size = size;
+ layout.omap_root.update(omap_root_t(id, cnt_modify,
+ value.get_metadata_hint(block_size)));
+ validate(value);
+ }
+
+ void validate(Onode& value) const {
+ auto& layout = value.get_layout();
+ ceph_assert(laddr_t(layout.size) == laddr_t{size});
+ ceph_assert(layout.omap_root.get(value.get_metadata_hint(block_size)).addr == id);
+ ceph_assert(layout.omap_root.get(value.get_metadata_hint(block_size)).depth == cnt_modify);
+ }
+
+ void modify(Transaction& t, Onode& value) {
+ validate(value);
+ ++cnt_modify;
+ initialize(t, value);
+ }
+
+ static onode_item_t create(std::size_t size, std::size_t id, uint64_t block_size) {
+ ceph_assert(size <= std::numeric_limits<uint32_t>::max());
+ return {(uint32_t)size, id, block_size};
+ }
+};
+
+struct fltree_onode_manager_test_t
+ : public seastar_test_suite_t, TMTestState {
+ using iterator_t = typename KVPool<onode_item_t>::iterator_t;
+
+ FLTreeOnodeManagerRef manager;
+
+ seastar::future<> set_up_fut() final {
+ return tm_setup();
+ }
+
+ seastar::future<> tear_down_fut() final {
+ return tm_teardown();
+ }
+
+ virtual seastar::future<> _init() final {
+ return TMTestState::_init().then([this] {
+ manager.reset(new FLTreeOnodeManager(*tm));
+ });
+ }
+
+ virtual seastar::future<> _destroy() final {
+ manager.reset();
+ return TMTestState::_destroy();
+ }
+
+ virtual FuturizedStore::mkfs_ertr::future<> _mkfs() final {
+ return TMTestState::_mkfs(
+ ).safe_then([this] {
+ return restart_fut();
+ }).safe_then([this] {
+ return repeat_eagain([this] {
+ return seastar::do_with(
+ create_mutate_transaction(),
+ [this](auto &ref_t)
+ {
+ return with_trans_intr(*ref_t, [&](auto &t) {
+ return manager->mkfs(t
+ ).si_then([this, &t] {
+ return submit_transaction_fut2(t);
+ });
+ });
+ });
+ });
+ }).handle_error(
+ crimson::ct_error::assert_all{"Invalid error in _mkfs"}
+ );
+ }
+
+ template <typename F>
+ void with_transaction(F&& f) {
+ auto t = create_mutate_transaction();
+ std::invoke(f, *t);
+ submit_transaction(std::move(t));
+ }
+
+ template <typename F>
+ void with_onode_write(iterator_t& it, F&& f) {
+ with_transaction([this, &it, f=std::move(f)] (auto& t) {
+ auto p_kv = *it;
+ auto onode = with_trans_intr(t, [&](auto &t) {
+ return manager->get_or_create_onode(t, p_kv->key);
+ }).unsafe_get0();
+ std::invoke(f, t, *onode, p_kv->value);
+ with_trans_intr(t, [&](auto &t) {
+ if (onode->is_alive()) {
+ return manager->write_dirty(t, {onode});
+ } else {
+ return OnodeManager::write_dirty_iertr::now();
+ }
+ }).unsafe_get0();
+ });
+ }
+
+ void validate_onode(iterator_t& it) {
+ with_transaction([this, &it] (auto& t) {
+ auto p_kv = *it;
+ auto onode = with_trans_intr(t, [&](auto &t) {
+ return manager->get_onode(t, p_kv->key);
+ }).unsafe_get0();
+ p_kv->value.validate(*onode);
+ });
+ }
+
+ void validate_erased(iterator_t& it) {
+ with_transaction([this, &it] (auto& t) {
+ auto p_kv = *it;
+ auto exist = with_trans_intr(t, [&](auto &t) {
+ return manager->contains_onode(t, p_kv->key);
+ }).unsafe_get0();
+ ceph_assert(exist == false);
+ });
+ }
+
+ template <typename F>
+ void with_onodes_process(
+ const iterator_t& start, const iterator_t& end, F&& f) {
+ std::vector<ghobject_t> oids;
+ std::vector<onode_item_t*> items;
+ auto it = start;
+ while(it != end) {
+ auto p_kv = *it;
+ oids.emplace_back(p_kv->key);
+ items.emplace_back(&p_kv->value);
+ ++it;
+ }
+ with_transaction([&oids, &items, f=std::move(f)] (auto& t) mutable {
+ std::invoke(f, t, oids, items);
+ });
+ }
+
+ template <typename F>
+ void with_onodes_write(
+ const iterator_t& start, const iterator_t& end, F&& f) {
+ with_onodes_process(start, end,
+ [this, f=std::move(f)] (auto& t, auto& oids, auto& items) {
+ auto onodes = with_trans_intr(t, [&](auto &t) {
+ return manager->get_or_create_onodes(t, oids);
+ }).unsafe_get0();
+ for (auto tup : boost::combine(onodes, items)) {
+ OnodeRef onode;
+ onode_item_t* p_item;
+ boost::tie(onode, p_item) = tup;
+ std::invoke(f, t, *onode, *p_item);
+ }
+ with_trans_intr(t, [&](auto &t) {
+ return manager->write_dirty(t, onodes);
+ }).unsafe_get0();
+ });
+ }
+
+ void validate_onodes(
+ const iterator_t& start, const iterator_t& end) {
+ with_onodes_process(start, end,
+ [this] (auto& t, auto& oids, auto& items) {
+ for (auto tup : boost::combine(oids, items)) {
+ ghobject_t oid;
+ onode_item_t* p_item;
+ boost::tie(oid, p_item) = tup;
+ auto onode = with_trans_intr(t, [&](auto &t) {
+ return manager->get_onode(t, oid);
+ }).unsafe_get0();
+ p_item->validate(*onode);
+ }
+ });
+ }
+
+ void validate_erased(
+ const iterator_t& start, const iterator_t& end) {
+ with_onodes_process(start, end,
+ [this] (auto& t, auto& oids, auto& items) {
+ for (auto& oid : oids) {
+ auto exist = with_trans_intr(t, [&](auto &t) {
+ return manager->contains_onode(t, oid);
+ }).unsafe_get0();
+ ceph_assert(exist == false);
+ }
+ });
+ }
+
+ static constexpr uint64_t LIST_LIMIT = 10;
+ void validate_list_onodes(KVPool<onode_item_t>& pool) {
+ with_onodes_process(pool.begin(), pool.end(),
+ [this] (auto& t, auto& oids, auto& items) {
+ std::vector<ghobject_t> listed_oids;
+ auto start = ghobject_t();
+ auto end = ghobject_t::get_max();
+ assert(start < end);
+ assert(start < oids[0]);
+ assert(oids[0] < end);
+ while (start != end) {
+ auto [list_ret, list_end] = with_trans_intr(t, [&](auto &t) {
+ return manager->list_onodes(t, start, end, LIST_LIMIT);
+ }).unsafe_get0();
+ listed_oids.insert(listed_oids.end(), list_ret.begin(), list_ret.end());
+ start = list_end;
+ }
+ ceph_assert(oids.size() == listed_oids.size());
+ });
+ }
+
+ fltree_onode_manager_test_t() {}
+};
+
+TEST_P(fltree_onode_manager_test_t, 1_single)
+{
+ run_async([this] {
+ uint64_t block_size = tm->get_block_size();
+ auto pool = KVPool<onode_item_t>::create_range({0, 1}, {128, 256}, block_size);
+ auto iter = pool.begin();
+ with_onode_write(iter, [](auto& t, auto& onode, auto& item) {
+ item.initialize(t, onode);
+ });
+ validate_onode(iter);
+
+ with_onode_write(iter, [](auto& t, auto& onode, auto& item) {
+ item.modify(t, onode);
+ });
+ validate_onode(iter);
+
+ validate_list_onodes(pool);
+
+ with_onode_write(iter, [this](auto& t, auto& onode, auto& item) {
+ OnodeRef onode_ref = &onode;
+ with_trans_intr(t, [&](auto &t) {
+ return manager->erase_onode(t, onode_ref);
+ }).unsafe_get0();
+ });
+ validate_erased(iter);
+ });
+}
+
+TEST_P(fltree_onode_manager_test_t, 2_synthetic)
+{
+ run_async([this] {
+ uint64_t block_size = tm->get_block_size();
+ auto pool = KVPool<onode_item_t>::create_range(
+ {0, 100}, {32, 64, 128, 256, 512}, block_size);
+ auto start = pool.begin();
+ auto end = pool.end();
+ with_onodes_write(start, end,
+ [](auto& t, auto& onode, auto& item) {
+ item.initialize(t, onode);
+ });
+ validate_onodes(start, end);
+
+ validate_list_onodes(pool);
+
+ auto rd_start = pool.random_begin();
+ auto rd_end = rd_start + 50;
+ with_onodes_write(rd_start, rd_end,
+ [](auto& t, auto& onode, auto& item) {
+ item.modify(t, onode);
+ });
+ validate_onodes(start, end);
+
+ pool.shuffle();
+ rd_start = pool.random_begin();
+ rd_end = rd_start + 50;
+ with_onodes_write(rd_start, rd_end,
+ [](auto& t, auto& onode, auto& item) {
+ item.modify(t, onode);
+ });
+ validate_onodes(start, end);
+
+ pool.shuffle();
+ rd_start = pool.random_begin();
+ rd_end = rd_start + 50;
+ with_onodes_write(rd_start, rd_end,
+ [this](auto& t, auto& onode, auto& item) {
+ OnodeRef onode_ref = &onode;
+ with_trans_intr(t, [&](auto &t) {
+ return manager->erase_onode(t, onode_ref);
+ }).unsafe_get0();
+ });
+ validate_erased(rd_start, rd_end);
+ pool.erase_from_random(rd_start, rd_end);
+ start = pool.begin();
+ end = pool.end();
+ validate_onodes(start, end);
+
+ validate_list_onodes(pool);
+ });
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ fltree_onode__manager_test,
+ fltree_onode_manager_test_t,
+ ::testing::Values (
+ "segmented",
+ "circularbounded"
+ )
+);
diff --git a/src/test/crimson/seastore/onode_tree/test_staged_fltree.cc b/src/test/crimson/seastore/onode_tree/test_staged_fltree.cc
new file mode 100644
index 000000000..7357b5ced
--- /dev/null
+++ b/src/test/crimson/seastore/onode_tree/test_staged_fltree.cc
@@ -0,0 +1,1792 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <array>
+#include <cstring>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <vector>
+
+#include "crimson/common/log.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager/dummy.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager/seastore.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_layout.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/tree.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h"
+
+#include "test/crimson/gtest_seastar.h"
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+#include "test_value.h"
+
+using namespace crimson::os::seastore::onode;
+
+#define INTR(fun, t) \
+ with_trans_intr( \
+ t, \
+ [&] (auto &tr) { \
+ return fun(tr); \
+ } \
+ )
+
+#define INTR_R(fun, t, args...) \
+ with_trans_intr( \
+ t, \
+ [&] (auto &tr) { \
+ return fun(tr, args); \
+ } \
+ )
+
+#define INTR_WITH_PARAM(fun, c, b, v) \
+ with_trans_intr( \
+ c.t, \
+ [=] (auto &t) { \
+ return fun(c, L_ADDR_MIN, b, v); \
+ } \
+ )
+
+namespace {
+ constexpr bool IS_DUMMY_SYNC = false;
+ using DummyManager = DummyNodeExtentManager<IS_DUMMY_SYNC>;
+
+ using UnboundedBtree = Btree<UnboundedValue>;
+
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+
+ ghobject_t make_ghobj(
+ shard_t shard, pool_t pool, crush_hash_t crush,
+ std::string ns, std::string oid, snap_t snap, gen_t gen) {
+ return ghobject_t{shard_id_t{shard}, pool, crush, ns, oid, snap, gen};
+ }
+
+ // return a key_view_t and its underlying memory buffer.
+ // the buffer needs to be freed manually.
+ std::pair<key_view_t, void*> build_key_view(const ghobject_t& hobj) {
+ key_hobj_t key_hobj(hobj);
+ size_t key_size = sizeof(shard_pool_crush_t) + sizeof(snap_gen_t) +
+ ns_oid_view_t::estimate_size(key_hobj);
+ void* p_mem = std::malloc(key_size);
+
+ key_view_t key_view;
+ char* p_fill = (char*)p_mem + key_size;
+
+ auto spc = shard_pool_crush_t::from_key(key_hobj);
+ p_fill -= sizeof(shard_pool_crush_t);
+ std::memcpy(p_fill, &spc, sizeof(shard_pool_crush_t));
+ key_view.set(*reinterpret_cast<const shard_pool_crush_t*>(p_fill));
+
+ auto p_ns_oid = p_fill;
+ ns_oid_view_t::test_append(key_hobj, p_fill);
+ ns_oid_view_t ns_oid_view(p_ns_oid);
+ key_view.set(ns_oid_view);
+
+ auto sg = snap_gen_t::from_key(key_hobj);
+ p_fill -= sizeof(snap_gen_t);
+ ceph_assert(p_fill == (char*)p_mem);
+ std::memcpy(p_fill, &sg, sizeof(snap_gen_t));
+ key_view.set(*reinterpret_cast<const snap_gen_t*>(p_fill));
+
+ return {key_view, p_mem};
+ }
+}
+
+struct a_basic_test_t : public seastar_test_suite_t {};
+
+TEST_F(a_basic_test_t, 1_basic_sizes)
+{
+ logger().info("\n"
+ "Bytes of struct:\n"
+ " node_header_t: {}\n"
+ " shard_pool_t: {}\n"
+ " shard_pool_crush_t: {}\n"
+ " crush_t: {}\n"
+ " snap_gen_t: {}\n"
+ " slot_0_t: {}\n"
+ " slot_1_t: {}\n"
+ " slot_3_t: {}\n"
+ " node_fields_0_t: {}\n"
+ " node_fields_1_t: {}\n"
+ " node_fields_2_t: {}\n"
+ " internal_fields_3_t: {}\n"
+ " leaf_fields_3_t: {}\n"
+ " internal_sub_item_t: {}",
+ sizeof(node_header_t), sizeof(shard_pool_t),
+ sizeof(shard_pool_crush_t), sizeof(crush_t), sizeof(snap_gen_t),
+ sizeof(slot_0_t), sizeof(slot_1_t), sizeof(slot_3_t),
+ sizeof(node_fields_0_t), sizeof(node_fields_1_t), sizeof(node_fields_2_t),
+ sizeof(internal_fields_3_t), sizeof(leaf_fields_3_t), sizeof(internal_sub_item_t)
+ );
+
+ auto hobj = make_ghobj(0, 0, 0, "n", "o", 0, 0);
+ key_hobj_t key(hobj);
+ auto [key_view, p_mem] = build_key_view(hobj);
+ value_config_t value;
+ value.payload_size = 8;
+#define _STAGE_T(NodeType) node_to_stage_t<typename NodeType::node_stage_t>
+#define NXT_T(StageType) staged<typename StageType::next_param_t>
+ laddr_t i_value{0};
+ logger().info("\n"
+ "Bytes of a key-value insertion (full-string):\n"
+ " s-p-c, 'n'-'o', s-g => value_payload(8): typically internal 43B, leaf 59B\n"
+ " InternalNode0: {} {} {}\n"
+ " InternalNode1: {} {} {}\n"
+ " InternalNode2: {} {}\n"
+ " InternalNode3: {}\n"
+ " LeafNode0: {} {} {}\n"
+ " LeafNode1: {} {} {}\n"
+ " LeafNode2: {} {}\n"
+ " LeafNode3: {}",
+ _STAGE_T(InternalNode0)::insert_size(key_view, i_value),
+ NXT_T(_STAGE_T(InternalNode0))::insert_size(key_view, i_value),
+ NXT_T(NXT_T(_STAGE_T(InternalNode0)))::insert_size(key_view, i_value),
+ _STAGE_T(InternalNode1)::insert_size(key_view, i_value),
+ NXT_T(_STAGE_T(InternalNode1))::insert_size(key_view, i_value),
+ NXT_T(NXT_T(_STAGE_T(InternalNode1)))::insert_size(key_view, i_value),
+ _STAGE_T(InternalNode2)::insert_size(key_view, i_value),
+ NXT_T(_STAGE_T(InternalNode2))::insert_size(key_view, i_value),
+ _STAGE_T(InternalNode3)::insert_size(key_view, i_value),
+ _STAGE_T(LeafNode0)::insert_size(key, value),
+ NXT_T(_STAGE_T(LeafNode0))::insert_size(key, value),
+ NXT_T(NXT_T(_STAGE_T(LeafNode0)))::insert_size(key, value),
+ _STAGE_T(LeafNode1)::insert_size(key, value),
+ NXT_T(_STAGE_T(LeafNode1))::insert_size(key, value),
+ NXT_T(NXT_T(_STAGE_T(LeafNode1)))::insert_size(key, value),
+ _STAGE_T(LeafNode2)::insert_size(key, value),
+ NXT_T(_STAGE_T(LeafNode2))::insert_size(key, value),
+ _STAGE_T(LeafNode3)::insert_size(key, value)
+ );
+ std::free(p_mem);
+}
+
+TEST_F(a_basic_test_t, 2_node_sizes)
+{
+ run_async([] {
+ auto nm = NodeExtentManager::create_dummy(IS_DUMMY_SYNC);
+ auto t = make_test_transaction();
+ ValueBuilderImpl<UnboundedValue> vb;
+ context_t c{*nm, vb, *t};
+ std::array<std::pair<NodeImplURef, NodeExtentMutable>, 16> nodes = {
+ INTR_WITH_PARAM(InternalNode0::allocate, c, false, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(InternalNode1::allocate, c, false, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(InternalNode2::allocate, c, false, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(InternalNode3::allocate, c, false, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(InternalNode0::allocate, c, true, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(InternalNode1::allocate, c, true, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(InternalNode2::allocate, c, true, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(InternalNode3::allocate, c, true, 1u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode0::allocate, c, false, 0u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode1::allocate, c, false, 0u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode2::allocate, c, false, 0u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode3::allocate, c, false, 0u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode0::allocate, c, true, 0u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode1::allocate, c, true, 0u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode2::allocate, c, true, 0u).unsafe_get0().make_pair(),
+ INTR_WITH_PARAM(LeafNode3::allocate, c, true, 0u).unsafe_get0().make_pair()
+ };
+ std::ostringstream oss;
+ oss << "\nallocated nodes:";
+ for (auto iter = nodes.begin(); iter != nodes.end(); ++iter) {
+ oss << "\n ";
+ auto& ref_node = iter->first;
+ ref_node->dump_brief(oss);
+ }
+ logger().info("{}", oss.str());
+ });
+}
+
+struct b_dummy_tree_test_t : public seastar_test_suite_t {
+ TransactionRef ref_t;
+ std::unique_ptr<UnboundedBtree> tree;
+
+ b_dummy_tree_test_t() = default;
+
+ seastar::future<> set_up_fut() override final {
+ ref_t = make_test_transaction();
+ tree.reset(
+ new UnboundedBtree(NodeExtentManager::create_dummy(IS_DUMMY_SYNC))
+ );
+ return INTR(tree->mkfs, *ref_t).handle_error(
+ crimson::ct_error::all_same_way([] {
+ ASSERT_FALSE("Unable to mkfs");
+ })
+ );
+ }
+
+ seastar::future<> tear_down_fut() final {
+ ref_t.reset();
+ tree.reset();
+ return seastar::now();
+ }
+};
+
+TEST_F(b_dummy_tree_test_t, 3_random_insert_erase_leaf_node)
+{
+ run_async([this] {
+ logger().info("\n---------------------------------------------"
+ "\nrandomized leaf node insert:\n");
+ auto key_s = ghobject_t();
+ auto key_e = ghobject_t::get_max();
+ ASSERT_TRUE(INTR_R(tree->find, *ref_t, key_s).unsafe_get0().is_end());
+ ASSERT_TRUE(INTR(tree->begin, *ref_t).unsafe_get0().is_end());
+ ASSERT_TRUE(INTR(tree->last, *ref_t).unsafe_get0().is_end());
+
+ std::map<ghobject_t,
+ std::tuple<test_item_t, UnboundedBtree::Cursor>> insert_history;
+
+ auto f_validate_insert_new = [this, &insert_history] (
+ const ghobject_t& key, const test_item_t& value) {
+ auto conf = UnboundedBtree::tree_value_config_t{value.get_payload_size()};
+ auto [cursor, success] = INTR_R(tree->insert,
+ *ref_t, key, conf).unsafe_get0();
+ initialize_cursor_from_item(*ref_t, key, value, cursor, success);
+ insert_history.emplace(key, std::make_tuple(value, cursor));
+ auto cursor_ = INTR_R(tree->find, *ref_t, key).unsafe_get0();
+ ceph_assert(cursor_ != tree->end());
+ ceph_assert(cursor_.value() == cursor.value());
+ validate_cursor_from_item(key, value, cursor_);
+ return cursor.value();
+ };
+
+ auto f_validate_erase = [this, &insert_history] (const ghobject_t& key) {
+ auto cursor_erase = INTR_R(tree->find, *ref_t, key).unsafe_get0();
+ auto cursor_next = INTR(cursor_erase.get_next, *ref_t).unsafe_get0();
+ auto cursor_ret = INTR_R(tree->erase, *ref_t, cursor_erase).unsafe_get0();
+ ceph_assert(cursor_erase.is_end());
+ ceph_assert(cursor_ret == cursor_next);
+ auto cursor_lb = INTR_R(tree->lower_bound, *ref_t, key).unsafe_get0();
+ ceph_assert(cursor_lb == cursor_next);
+ auto it = insert_history.find(key);
+ ceph_assert(std::get<1>(it->second).is_end());
+ insert_history.erase(it);
+ };
+
+ auto f_insert_erase_insert = [&f_validate_insert_new, &f_validate_erase] (
+ const ghobject_t& key, const test_item_t& value) {
+ f_validate_insert_new(key, value);
+ f_validate_erase(key);
+ return f_validate_insert_new(key, value);
+ };
+
+ auto values = Values<test_item_t>(15);
+
+ // insert key1, value1 at STAGE_LEFT
+ auto key1 = make_ghobj(3, 3, 3, "ns3", "oid3", 3, 3);
+ auto value1 = values.pick();
+ auto test_value1 = f_insert_erase_insert(key1, value1);
+
+ // validate lookup
+ {
+ auto cursor1_s = INTR_R(tree->lower_bound, *ref_t, key_s).unsafe_get0();
+ ASSERT_EQ(cursor1_s.get_ghobj(), key1);
+ ASSERT_EQ(cursor1_s.value(), test_value1);
+ auto cursor1_e = INTR_R(tree->lower_bound, *ref_t, key_e).unsafe_get0();
+ ASSERT_TRUE(cursor1_e.is_end());
+ }
+
+ // insert the same key1 with a different value
+ {
+ auto value1_dup = values.pick();
+ auto conf = UnboundedBtree::tree_value_config_t{value1_dup.get_payload_size()};
+ auto [cursor1_dup, ret1_dup] = INTR_R(tree->insert,
+ *ref_t, key1, conf).unsafe_get0();
+ ASSERT_FALSE(ret1_dup);
+ validate_cursor_from_item(key1, value1, cursor1_dup);
+ }
+
+ // insert key2, value2 to key1's left at STAGE_LEFT
+ // insert node front at STAGE_LEFT
+ auto key2 = make_ghobj(2, 2, 2, "ns3", "oid3", 3, 3);
+ auto value2 = values.pick();
+ f_insert_erase_insert(key2, value2);
+
+ // insert key3, value3 to key1's right at STAGE_LEFT
+ // insert node last at STAGE_LEFT
+ auto key3 = make_ghobj(4, 4, 4, "ns3", "oid3", 3, 3);
+ auto value3 = values.pick();
+ f_insert_erase_insert(key3, value3);
+
+ // insert key4, value4 to key1's left at STAGE_STRING (collision)
+ auto key4 = make_ghobj(3, 3, 3, "ns2", "oid2", 3, 3);
+ auto value4 = values.pick();
+ f_insert_erase_insert(key4, value4);
+
+ // insert key5, value5 to key1's right at STAGE_STRING (collision)
+ auto key5 = make_ghobj(3, 3, 3, "ns4", "oid4", 3, 3);
+ auto value5 = values.pick();
+ f_insert_erase_insert(key5, value5);
+
+ // insert key6, value6 to key1's left at STAGE_RIGHT
+ auto key6 = make_ghobj(3, 3, 3, "ns3", "oid3", 2, 2);
+ auto value6 = values.pick();
+ f_insert_erase_insert(key6, value6);
+
+ // insert key7, value7 to key1's right at STAGE_RIGHT
+ auto key7 = make_ghobj(3, 3, 3, "ns3", "oid3", 4, 4);
+ auto value7 = values.pick();
+ f_insert_erase_insert(key7, value7);
+
+ // insert node front at STAGE_RIGHT
+ auto key8 = make_ghobj(2, 2, 2, "ns3", "oid3", 2, 2);
+ auto value8 = values.pick();
+ f_insert_erase_insert(key8, value8);
+
+ // insert node front at STAGE_STRING (collision)
+ auto key9 = make_ghobj(2, 2, 2, "ns2", "oid2", 3, 3);
+ auto value9 = values.pick();
+ f_insert_erase_insert(key9, value9);
+
+ // insert node last at STAGE_RIGHT
+ auto key10 = make_ghobj(4, 4, 4, "ns3", "oid3", 4, 4);
+ auto value10 = values.pick();
+ f_insert_erase_insert(key10, value10);
+
+ // insert node last at STAGE_STRING (collision)
+ auto key11 = make_ghobj(4, 4, 4, "ns4", "oid4", 3, 3);
+ auto value11 = values.pick();
+ f_insert_erase_insert(key11, value11);
+
+ // insert key, value randomly until a perfect 3-ary tree is formed
+ std::vector<std::pair<ghobject_t, test_item_t>> kvs{
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2), values.pick()},
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 4, 4), values.pick()},
+ {make_ghobj(2, 2, 2, "ns3", "oid3", 4, 4), values.pick()},
+ {make_ghobj(2, 2, 2, "ns4", "oid4", 2, 2), values.pick()},
+ {make_ghobj(2, 2, 2, "ns4", "oid4", 3, 3), values.pick()},
+ {make_ghobj(2, 2, 2, "ns4", "oid4", 4, 4), values.pick()},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2), values.pick()},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 4, 4), values.pick()},
+ {make_ghobj(3, 3, 3, "ns4", "oid4", 2, 2), values.pick()},
+ {make_ghobj(3, 3, 3, "ns4", "oid4", 4, 4), values.pick()},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2), values.pick()},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 3, 3), values.pick()},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 4, 4), values.pick()},
+ {make_ghobj(4, 4, 4, "ns3", "oid3", 2, 2), values.pick()},
+ {make_ghobj(4, 4, 4, "ns4", "oid4", 2, 2), values.pick()},
+ {make_ghobj(4, 4, 4, "ns4", "oid4", 4, 4), values.pick()}};
+ auto [smallest_key, smallest_value] = kvs[0];
+ auto [largest_key, largest_value] = kvs[kvs.size() - 1];
+ std::shuffle(kvs.begin(), kvs.end(), std::default_random_engine{});
+ std::for_each(kvs.begin(), kvs.end(), [&f_insert_erase_insert] (auto& kv) {
+ f_insert_erase_insert(kv.first, kv.second);
+ });
+ ASSERT_EQ(INTR(tree->height, *ref_t).unsafe_get0(), 1);
+ ASSERT_FALSE(tree->test_is_clean());
+
+ for (auto& [k, val] : insert_history) {
+ auto& [v, c] = val;
+ // validate values in tree keep intact
+ auto cursor = with_trans_intr(*ref_t, [this, &k=k](auto& tr) {
+ return tree->find(tr, k);
+ }).unsafe_get0();
+ EXPECT_NE(cursor, tree->end());
+ validate_cursor_from_item(k, v, cursor);
+ // validate values in cursors keep intact
+ validate_cursor_from_item(k, v, c);
+ }
+ {
+ auto cursor = INTR_R(tree->lower_bound, *ref_t, key_s).unsafe_get0();
+ validate_cursor_from_item(smallest_key, smallest_value, cursor);
+ }
+ {
+ auto cursor = INTR(tree->begin, *ref_t).unsafe_get0();
+ validate_cursor_from_item(smallest_key, smallest_value, cursor);
+ }
+ {
+ auto cursor = INTR(tree->last, *ref_t).unsafe_get0();
+ validate_cursor_from_item(largest_key, largest_value, cursor);
+ }
+
+ // validate range query
+ {
+ kvs.clear();
+ for (auto& [k, val] : insert_history) {
+ auto& [v, c] = val;
+ kvs.emplace_back(k, v);
+ }
+ insert_history.clear();
+ std::sort(kvs.begin(), kvs.end(), [](auto& l, auto& r) {
+ return l.first < r.first;
+ });
+ auto cursor = INTR(tree->begin, *ref_t).unsafe_get0();
+ for (auto& [k, v] : kvs) {
+ ASSERT_FALSE(cursor.is_end());
+ validate_cursor_from_item(k, v, cursor);
+ cursor = INTR(cursor.get_next, *ref_t).unsafe_get0();
+ }
+ ASSERT_TRUE(cursor.is_end());
+ }
+
+ std::ostringstream oss;
+ tree->dump(*ref_t, oss);
+ logger().info("\n{}\n", oss.str());
+
+ // randomized erase until empty
+ std::shuffle(kvs.begin(), kvs.end(), std::default_random_engine{});
+ for (auto& [k, v] : kvs) {
+ auto e_size = with_trans_intr(*ref_t, [this, &k=k](auto& tr) {
+ return tree->erase(tr, k);
+ }).unsafe_get0();
+ ASSERT_EQ(e_size, 1);
+ }
+ auto cursor = INTR(tree->begin, *ref_t).unsafe_get0();
+ ASSERT_TRUE(cursor.is_end());
+ ASSERT_EQ(INTR(tree->height, *ref_t).unsafe_get0(), 1);
+ });
+}
+
+static std::set<ghobject_t> build_key_set(
+ std::pair<unsigned, unsigned> range_2,
+ std::pair<unsigned, unsigned> range_1,
+ std::pair<unsigned, unsigned> range_0,
+ std::string padding = "",
+ bool is_internal = false) {
+ ceph_assert(range_1.second <= 10);
+ std::set<ghobject_t> ret;
+ ghobject_t key;
+ for (unsigned i = range_2.first; i < range_2.second; ++i) {
+ for (unsigned j = range_1.first; j < range_1.second; ++j) {
+ for (unsigned k = range_0.first; k < range_0.second; ++k) {
+ std::ostringstream os_ns;
+ os_ns << "ns" << j;
+ std::ostringstream os_oid;
+ os_oid << "oid" << j << padding;
+ key = make_ghobj(i, i, i, os_ns.str(), os_oid.str(), k, k);
+ ret.insert(key);
+ }
+ }
+ }
+ if (is_internal) {
+ ret.insert(make_ghobj(9, 9, 9, "ns~last", "oid~last", 9, 9));
+ }
+ return ret;
+}
+
+class TestTree {
+ public:
+ TestTree()
+ : moved_nm{NodeExtentManager::create_dummy(IS_DUMMY_SYNC)},
+ ref_t{make_test_transaction()},
+ t{*ref_t},
+ c{*moved_nm, vb, t},
+ tree{std::move(moved_nm)},
+ values{0} {}
+
+ seastar::future<> build_tree(
+ std::pair<unsigned, unsigned> range_2,
+ std::pair<unsigned, unsigned> range_1,
+ std::pair<unsigned, unsigned> range_0,
+ size_t value_size) {
+ return seastar::async([this, range_2, range_1, range_0, value_size] {
+ INTR(tree.mkfs, t).unsafe_get0();
+ //logger().info("\n---------------------------------------------"
+ // "\nbefore leaf node split:\n");
+ auto keys = build_key_set(range_2, range_1, range_0);
+ for (auto& key : keys) {
+ auto value = values.create(value_size);
+ insert_tree(key, value).get0();
+ }
+ ASSERT_EQ(INTR(tree.height, t).unsafe_get0(), 1);
+ ASSERT_FALSE(tree.test_is_clean());
+ //std::ostringstream oss;
+ //tree.dump(t, oss);
+ //logger().info("\n{}\n", oss.str());
+ });
+ }
+
+ seastar::future<> build_tree(
+ const std::vector<ghobject_t>& keys, const std::vector<test_item_t>& values) {
+ return seastar::async([this, keys, values] {
+ INTR(tree.mkfs, t).unsafe_get0();
+ //logger().info("\n---------------------------------------------"
+ // "\nbefore leaf node split:\n");
+ ASSERT_EQ(keys.size(), values.size());
+ auto key_iter = keys.begin();
+ auto value_iter = values.begin();
+ while (key_iter != keys.end()) {
+ insert_tree(*key_iter, *value_iter).get0();
+ ++key_iter;
+ ++value_iter;
+ }
+ ASSERT_EQ(INTR(tree.height, t).unsafe_get0(), 1);
+ ASSERT_FALSE(tree.test_is_clean());
+ //std::ostringstream oss;
+ //tree.dump(t, oss);
+ //logger().info("\n{}\n", oss.str());
+ });
+ }
+
+ seastar::future<> split_merge(
+ const ghobject_t& key,
+ const test_item_t& value,
+ const split_expectation_t& expected,
+ std::optional<ghobject_t> next_key) {
+ return seastar::async([this, key, value, expected, next_key] {
+ // clone
+ auto ref_dummy = NodeExtentManager::create_dummy(IS_DUMMY_SYNC);
+ auto p_dummy = static_cast<DummyManager*>(ref_dummy.get());
+ UnboundedBtree tree_clone(std::move(ref_dummy));
+ auto ref_t_clone = make_test_transaction();
+ Transaction& t_clone = *ref_t_clone;
+ INTR_R(tree_clone.test_clone_from, t_clone, t, tree).unsafe_get0();
+
+ // insert and split
+ logger().info("\n\nINSERT-SPLIT {}:", key_hobj_t(key));
+ auto conf = UnboundedBtree::tree_value_config_t{value.get_payload_size()};
+ auto [cursor, success] = INTR_R(tree_clone.insert,
+ t_clone, key, conf).unsafe_get0();
+ initialize_cursor_from_item(t, key, value, cursor, success);
+
+ {
+ std::ostringstream oss;
+ tree_clone.dump(t_clone, oss);
+ logger().info("dump new root:\n{}", oss.str());
+ }
+ EXPECT_EQ(INTR(tree_clone.height, t_clone).unsafe_get0(), 2);
+
+ for (auto& [k, val] : insert_history) {
+ auto& [v, c] = val;
+ auto result = with_trans_intr(t_clone, [&tree_clone, &k=k] (auto& tr) {
+ return tree_clone.find(tr, k);
+ }).unsafe_get0();
+ EXPECT_NE(result, tree_clone.end());
+ validate_cursor_from_item(k, v, result);
+ }
+ auto result = INTR_R(tree_clone.find, t_clone, key).unsafe_get0();
+ EXPECT_NE(result, tree_clone.end());
+ validate_cursor_from_item(key, value, result);
+ EXPECT_TRUE(last_split.match(expected));
+ EXPECT_EQ(p_dummy->size(), 3);
+
+ // erase and merge
+ logger().info("\n\nERASE-MERGE {}:", key_hobj_t(key));
+ auto nxt_cursor = with_trans_intr(t_clone, [&cursor=cursor](auto& tr) {
+ return cursor.erase<true>(tr);
+ }).unsafe_get0();
+
+ {
+ // track root again to dump
+ auto begin = INTR(tree_clone.begin, t_clone).unsafe_get0();
+ std::ignore = begin;
+ std::ostringstream oss;
+ tree_clone.dump(t_clone, oss);
+ logger().info("dump root:\n{}", oss.str());
+ }
+
+ if (next_key.has_value()) {
+ auto found = insert_history.find(*next_key);
+ ceph_assert(found != insert_history.end());
+ validate_cursor_from_item(
+ *next_key, std::get<0>(found->second), nxt_cursor);
+ } else {
+ EXPECT_TRUE(nxt_cursor.is_end());
+ }
+
+ for (auto& [k, val] : insert_history) {
+ auto& [v, c] = val;
+ auto result = with_trans_intr(t_clone, [&tree_clone, &k=k](auto& tr) {
+ return tree_clone.find(tr, k);
+ }).unsafe_get0();
+ EXPECT_NE(result, tree_clone.end());
+ validate_cursor_from_item(k, v, result);
+ }
+ EXPECT_EQ(INTR(tree_clone.height, t_clone).unsafe_get0(), 1);
+ EXPECT_EQ(p_dummy->size(), 1);
+ });
+ }
+
+ test_item_t create_value(size_t size) {
+ return values.create(size);
+ }
+
+ private:
+ seastar::future<> insert_tree(const ghobject_t& key, const test_item_t& value) {
+ return seastar::async([this, &key, &value] {
+ auto conf = UnboundedBtree::tree_value_config_t{value.get_payload_size()};
+ auto [cursor, success] = INTR_R(tree.insert,
+ t, key, conf).unsafe_get0();
+ initialize_cursor_from_item(t, key, value, cursor, success);
+ insert_history.emplace(key, std::make_tuple(value, cursor));
+ });
+ }
+
+ NodeExtentManagerURef moved_nm;
+ TransactionRef ref_t;
+ Transaction& t;
+ ValueBuilderImpl<UnboundedValue> vb;
+ context_t c;
+ UnboundedBtree tree;
+ Values<test_item_t> values;
+ std::map<ghobject_t,
+ std::tuple<test_item_t, UnboundedBtree::Cursor>> insert_history;
+};
+
+struct c_dummy_test_t : public seastar_test_suite_t {};
+
+TEST_F(c_dummy_test_t, 4_split_merge_leaf_node)
+{
+ run_async([] {
+ {
+ TestTree test;
+ test.build_tree({2, 5}, {2, 5}, {2, 5}, 120).get0();
+
+ auto value = test.create_value(1144);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to left front at stage 2, 1, 0\n");
+ test.split_merge(make_ghobj(1, 1, 1, "ns3", "oid3", 3, 3), value,
+ {2u, 2u, true, InsertType::BEGIN},
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(2, 2, 2, "ns1", "oid1", 3, 3), value,
+ {2u, 1u, true, InsertType::BEGIN},
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(2, 2, 2, "ns2", "oid2", 1, 1), value,
+ {2u, 0u, true, InsertType::BEGIN},
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2)}).get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to left back at stage 0, 1, 2, 1, 0\n");
+ test.split_merge(make_ghobj(2, 2, 2, "ns4", "oid4", 5, 5), value,
+ {2u, 0u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(2, 2, 2, "ns5", "oid5", 3, 3), value,
+ {2u, 1u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(2, 3, 3, "ns3", "oid3", 3, 3), value,
+ {2u, 2u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns1", "oid1", 3, 3), value,
+ {2u, 1u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns2", "oid2", 1, 1), value,
+ {2u, 0u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+
+ auto value0 = test.create_value(1416);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to right front at stage 0, 1, 2, 1, 0\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4", 5, 5), value0,
+ {2u, 0u, false, InsertType::BEGIN},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns5", "oid5", 3, 3), value0,
+ {2u, 1u, false, InsertType::BEGIN},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 4, 4, "ns3", "oid3", 3, 3), value0,
+ {2u, 2u, false, InsertType::BEGIN},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(4, 4, 4, "ns1", "oid1", 3, 3), value0,
+ {2u, 1u, false, InsertType::BEGIN},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(4, 4, 4, "ns2", "oid2", 1, 1), value0,
+ {2u, 0u, false, InsertType::BEGIN},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to right back at stage 0, 1, 2\n");
+ test.split_merge(make_ghobj(4, 4, 4, "ns4", "oid4", 5, 5), value0,
+ {2u, 0u, false, InsertType::LAST},
+ std::nullopt).get0();
+ test.split_merge(make_ghobj(4, 4, 4, "ns5", "oid5", 3, 3), value0,
+ {2u, 1u, false, InsertType::LAST},
+ std::nullopt).get0();
+ test.split_merge(make_ghobj(5, 5, 5, "ns3", "oid3", 3, 3), value0,
+ {2u, 2u, false, InsertType::LAST},
+ std::nullopt).get0();
+
+ auto value1 = test.create_value(316);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 1; insert to left middle at stage 0, 1, 2, 1, 0\n");
+ test.split_merge(make_ghobj(2, 2, 2, "ns4", "oid4", 5, 5), value1,
+ {1u, 0u, true, InsertType::MID},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(2, 2, 2, "ns5", "oid5", 3, 3), value1,
+ {1u, 1u, true, InsertType::MID},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(2, 2, 3, "ns3", "oid3", 3, 3), value1,
+ {1u, 2u, true, InsertType::MID},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns1", "oid1", 3, 3), value1,
+ {1u, 1u, true, InsertType::MID},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns2", "oid2", 1, 1), value1,
+ {1u, 0u, true, InsertType::MID},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 2, 2)}).get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 1; insert to left back at stage 0, 1, 0\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns2", "oid2", 5, 5), value1,
+ {1u, 0u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns3", "oid3", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns2", "oid3", 3, 3), value1,
+ {1u, 1u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns3", "oid3", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns3", "oid3", 1, 1), value1,
+ {1u, 0u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns3", "oid3", 2, 2)}).get0();
+
+ auto value2 = test.create_value(452);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 1; insert to right front at stage 0, 1, 0\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns3", "oid3", 5, 5), value2,
+ {1u, 0u, false, InsertType::BEGIN},
+ {make_ghobj(3, 3, 3, "ns4", "oid4", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns3", "oid4", 3, 3), value2,
+ {1u, 1u, false, InsertType::BEGIN},
+ {make_ghobj(3, 3, 3, "ns4", "oid4", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4", 1, 1), value2,
+ {1u, 0u, false, InsertType::BEGIN},
+ {make_ghobj(3, 3, 3, "ns4", "oid4", 2, 2)}).get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 1; insert to right middle at stage 0, 1, 2, 1, 0\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4", 5, 5), value2,
+ {1u, 0u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns5", "oid5", 3, 3), value2,
+ {1u, 1u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 4, "ns3", "oid3", 3, 3), value2,
+ {1u, 2u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(4, 4, 4, "ns1", "oid1", 3, 3), value2,
+ {1u, 1u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(4, 4, 4, "ns2", "oid2", 1, 1), value2,
+ {1u, 0u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+
+ auto value3 = test.create_value(834);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 0; insert to right middle at stage 0, 1, 2, 1, 0\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4", 5, 5), value3,
+ {0u, 0u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 3, "ns5", "oid5", 3, 3), value3,
+ {0u, 1u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(3, 3, 4, "ns3", "oid3", 3, 3), value3,
+ {0u, 2u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(4, 4, 4, "ns1", "oid1", 3, 3), value3,
+ {0u, 1u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+ test.split_merge(make_ghobj(4, 4, 4, "ns2", "oid2", 1, 1), value3,
+ {0u, 0u, false, InsertType::MID},
+ {make_ghobj(4, 4, 4, "ns2", "oid2", 2, 2)}).get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 0; insert to right front at stage 0\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4", 2, 3), value3,
+ {0u, 0u, false, InsertType::BEGIN},
+ {make_ghobj(3, 3, 3, "ns4", "oid4", 3, 3)}).get0();
+
+ auto value4 = test.create_value(572);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 0; insert to left back at stage 0\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns2", "oid2", 3, 4), value4,
+ {0u, 0u, true, InsertType::LAST},
+ {make_ghobj(3, 3, 3, "ns2", "oid2", 4, 4)}).get0();
+ }
+
+ {
+ TestTree test;
+ test.build_tree({2, 4}, {2, 4}, {2, 4}, 232).get0();
+ auto value = test.create_value(1996);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at [0, 0, 0]; insert to left front at stage 2, 1, 0\n");
+ test.split_merge(make_ghobj(1, 1, 1, "ns3", "oid3", 3, 3), value,
+ {2u, 2u, true, InsertType::BEGIN},
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2)}).get0();
+ EXPECT_TRUE(last_split.match_split_pos({0, {0, {0}}}));
+ test.split_merge(make_ghobj(2, 2, 2, "ns1", "oid1", 3, 3), value,
+ {2u, 1u, true, InsertType::BEGIN},
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2)}).get0();
+ EXPECT_TRUE(last_split.match_split_pos({0, {0, {0}}}));
+ test.split_merge(make_ghobj(2, 2, 2, "ns2", "oid2", 1, 1), value,
+ {2u, 0u, true, InsertType::BEGIN},
+ {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2)}).get0();
+ EXPECT_TRUE(last_split.match_split_pos({0, {0, {0}}}));
+ }
+
+ {
+ TestTree test;
+ std::vector<ghobject_t> keys = {
+ make_ghobj(2, 2, 2, "ns3", "oid3", 3, 3),
+ make_ghobj(3, 3, 3, "ns3", "oid3", 3, 3)};
+ std::vector<test_item_t> values = {
+ test.create_value(1360),
+ test.create_value(1632)};
+ test.build_tree(keys, values).get0();
+ auto value = test.create_value(1640);
+ logger().info("\n---------------------------------------------"
+ "\nsplit at [END, END, END]; insert to right at stage 0, 1, 2\n");
+ test.split_merge(make_ghobj(3, 3, 3, "ns3", "oid3", 4, 4), value,
+ {0u, 0u, false, InsertType::BEGIN},
+ std::nullopt).get0();
+ EXPECT_TRUE(last_split.match_split_pos({1, {0, {1}}}));
+ test.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4", 3, 3), value,
+ {1u, 1u, false, InsertType::BEGIN},
+ std::nullopt).get0();
+ EXPECT_TRUE(last_split.match_split_pos({1, {1, {0}}}));
+ test.split_merge(make_ghobj(4, 4, 4, "ns3", "oid3", 3, 3), value,
+ {2u, 2u, false, InsertType::BEGIN},
+ std::nullopt).get0();
+ EXPECT_TRUE(last_split.match_split_pos({2, {0, {0}}}));
+ }
+ });
+}
+
+namespace crimson::os::seastore::onode {
+
+class DummyChildPool {
+ class DummyChildImpl final : public NodeImpl {
+ public:
+ using URef = std::unique_ptr<DummyChildImpl>;
+ DummyChildImpl(const std::set<ghobject_t>& keys, bool is_level_tail, laddr_t laddr)
+ : keys{keys}, _is_level_tail{is_level_tail}, _laddr{laddr} {
+ std::tie(key_view, p_mem_key_view) = build_key_view(*keys.crbegin());
+ build_name();
+ }
+ ~DummyChildImpl() override {
+ std::free(p_mem_key_view);
+ }
+
+ const std::set<ghobject_t>& get_keys() const { return keys; }
+
+ void reset(const std::set<ghobject_t>& _keys, bool level_tail) {
+ keys = _keys;
+ _is_level_tail = level_tail;
+ std::free(p_mem_key_view);
+ std::tie(key_view, p_mem_key_view) = build_key_view(*keys.crbegin());
+ build_name();
+ }
+
+ public:
+ laddr_t laddr() const override { return _laddr; }
+ bool is_level_tail() const override { return _is_level_tail; }
+ std::optional<key_view_t> get_pivot_index() const override { return {key_view}; }
+ bool is_extent_retired() const override { return _is_extent_retired; }
+ const std::string& get_name() const override { return name; }
+ search_position_t make_tail() override {
+ _is_level_tail = true;
+ build_name();
+ return search_position_t::end();
+ }
+ eagain_ifuture<> retire_extent(context_t) override {
+ assert(!_is_extent_retired);
+ _is_extent_retired = true;
+ return eagain_iertr::now();
+ }
+
+ protected:
+ node_type_t node_type() const override { return node_type_t::LEAF; }
+ field_type_t field_type() const override { return field_type_t::N0; }
+ const char* read() const override {
+ ceph_abort("impossible path"); }
+ extent_len_t get_node_size() const override {
+ ceph_abort("impossible path"); }
+ nextent_state_t get_extent_state() const override {
+ ceph_abort("impossible path"); }
+ level_t level() const override { return 0u; }
+ void prepare_mutate(context_t) override {
+ ceph_abort("impossible path"); }
+ void validate_non_empty() const override {
+ ceph_abort("impossible path"); }
+ bool is_keys_empty() const override {
+ ceph_abort("impossible path"); }
+ bool has_single_value() const override {
+ ceph_abort("impossible path"); }
+ node_offset_t free_size() const override {
+ ceph_abort("impossible path"); }
+ extent_len_t total_size() const override {
+ ceph_abort("impossible path"); }
+ bool is_size_underflow() const override {
+ ceph_abort("impossible path"); }
+ std::tuple<match_stage_t, search_position_t> erase(const search_position_t&) override {
+ ceph_abort("impossible path"); }
+ std::tuple<match_stage_t, std::size_t> evaluate_merge(NodeImpl&) override {
+ ceph_abort("impossible path"); }
+ search_position_t merge(NodeExtentMutable&, NodeImpl&, match_stage_t, extent_len_t) override {
+ ceph_abort("impossible path"); }
+ eagain_ifuture<NodeExtentMutable> rebuild_extent(context_t) override {
+ ceph_abort("impossible path"); }
+ node_stats_t get_stats() const override {
+ ceph_abort("impossible path"); }
+ std::ostream& dump(std::ostream&) const override {
+ ceph_abort("impossible path"); }
+ std::ostream& dump_brief(std::ostream&) const override {
+ ceph_abort("impossible path"); }
+ void validate_layout() const override {
+ ceph_abort("impossible path"); }
+ void test_copy_to(NodeExtentMutable&) const override {
+ ceph_abort("impossible path"); }
+ void test_set_tail(NodeExtentMutable&) override {
+ ceph_abort("impossible path"); }
+
+ private:
+ void build_name() {
+ std::ostringstream sos;
+ sos << "DummyNode"
+ << "@0x" << std::hex << laddr() << std::dec
+ << "Lv" << (unsigned)level()
+ << (is_level_tail() ? "$" : "")
+ << "(" << key_view << ")";
+ name = sos.str();
+ }
+
+ std::set<ghobject_t> keys;
+ bool _is_level_tail;
+ laddr_t _laddr;
+ std::string name;
+ bool _is_extent_retired = false;
+
+ key_view_t key_view;
+ void* p_mem_key_view;
+ };
+
+ class DummyChild final : public Node {
+ public:
+ ~DummyChild() override = default;
+
+ key_view_t get_pivot_key() const { return *impl->get_pivot_index(); }
+
+ eagain_ifuture<> populate_split(
+ context_t c, std::set<Ref<DummyChild>>& splitable_nodes) {
+ ceph_assert(can_split());
+ ceph_assert(splitable_nodes.find(this) != splitable_nodes.end());
+
+ size_t index;
+ const auto& keys = impl->get_keys();
+ if (keys.size() == 2) {
+ index = 1;
+ } else {
+ index = rd() % (keys.size() - 2) + 1;
+ }
+ auto iter = keys.begin();
+ std::advance(iter, index);
+
+ std::set<ghobject_t> left_keys(keys.begin(), iter);
+ std::set<ghobject_t> right_keys(iter, keys.end());
+ bool right_is_tail = impl->is_level_tail();
+ impl->reset(left_keys, false);
+ auto right_child = DummyChild::create_new(right_keys, right_is_tail, pool);
+ if (!can_split()) {
+ splitable_nodes.erase(this);
+ }
+ if (right_child->can_split()) {
+ splitable_nodes.insert(right_child);
+ }
+ Ref<Node> this_ref = this;
+ return apply_split_to_parent(
+ c, std::move(this_ref), std::move(right_child), false);
+ }
+
+ eagain_ifuture<> insert_and_split(
+ context_t c, const ghobject_t& insert_key,
+ std::set<Ref<DummyChild>>& splitable_nodes) {
+ const auto& keys = impl->get_keys();
+ ceph_assert(keys.size() == 1);
+ auto& key = *keys.begin();
+ ceph_assert(insert_key < key);
+
+ std::set<ghobject_t> new_keys;
+ new_keys.insert(insert_key);
+ new_keys.insert(key);
+ impl->reset(new_keys, impl->is_level_tail());
+
+ splitable_nodes.clear();
+ splitable_nodes.insert(this);
+ auto fut = populate_split(c, splitable_nodes);
+ ceph_assert(splitable_nodes.size() == 0);
+ return fut;
+ }
+
+ eagain_ifuture<> merge(context_t c, Ref<DummyChild>&& this_ref) {
+ return parent_info().ptr->get_child_peers(c, parent_info().position
+ ).si_then([c, this_ref = std::move(this_ref), this] (auto lr_nodes) mutable {
+ auto& [lnode, rnode] = lr_nodes;
+ if (rnode) {
+ lnode.reset();
+ Ref<DummyChild> r_dummy(static_cast<DummyChild*>(rnode.get()));
+ rnode.reset();
+ pool.untrack_node(r_dummy);
+ assert(r_dummy->use_count() == 1);
+ return do_merge(c, std::move(this_ref), std::move(r_dummy), true);
+ } else {
+ ceph_assert(lnode);
+ Ref<DummyChild> l_dummy(static_cast<DummyChild*>(lnode.get()));
+ pool.untrack_node(this_ref);
+ assert(this_ref->use_count() == 1);
+ return do_merge(c, std::move(l_dummy), std::move(this_ref), false);
+ }
+ });
+ }
+
+ eagain_ifuture<> fix_key(context_t c, const ghobject_t& new_key) {
+ const auto& keys = impl->get_keys();
+ ceph_assert(keys.size() == 1);
+ assert(impl->is_level_tail() == false);
+
+ std::set<ghobject_t> new_keys;
+ new_keys.insert(new_key);
+ impl->reset(new_keys, impl->is_level_tail());
+ Ref<Node> this_ref = this;
+ return fix_parent_index<true>(c, std::move(this_ref), false);
+ }
+
+ bool match_pos(const search_position_t& pos) const {
+ ceph_assert(!is_root());
+ return pos == parent_info().position;
+ }
+
+ static Ref<DummyChild> create(
+ const std::set<ghobject_t>& keys, bool is_level_tail,
+ laddr_t addr, DummyChildPool& pool) {
+ auto ref_impl = std::make_unique<DummyChildImpl>(keys, is_level_tail, addr);
+ return new DummyChild(ref_impl.get(), std::move(ref_impl), pool);
+ }
+
+ static Ref<DummyChild> create_new(
+ const std::set<ghobject_t>& keys, bool is_level_tail, DummyChildPool& pool) {
+ static laddr_t seed = 0;
+ return create(keys, is_level_tail, seed++, pool);
+ }
+
+ static eagain_ifuture<Ref<DummyChild>> create_initial(
+ context_t c, const std::set<ghobject_t>& keys,
+ DummyChildPool& pool, RootNodeTracker& root_tracker) {
+ auto initial = create_new(keys, true, pool);
+ return c.nm.get_super(c.t, root_tracker
+ ).handle_error_interruptible(
+ eagain_iertr::pass_further{},
+ crimson::ct_error::assert_all{"Invalid error during create_initial()"}
+ ).si_then([c, initial](auto super) {
+ initial->make_root_new(c, std::move(super));
+ return initial->upgrade_root(c, L_ADDR_MIN).si_then([initial] {
+ return initial;
+ });
+ });
+ }
+
+ protected:
+ eagain_ifuture<> test_clone_non_root(
+ context_t, Ref<InternalNode> new_parent) const override {
+ ceph_assert(!is_root());
+ auto p_pool_clone = pool.pool_clone_in_progress;
+ ceph_assert(p_pool_clone != nullptr);
+ auto clone = create(
+ impl->get_keys(), impl->is_level_tail(), impl->laddr(), *p_pool_clone);
+ clone->as_child(parent_info().position, new_parent);
+ return eagain_iertr::now();
+ }
+ eagain_ifuture<Ref<tree_cursor_t>> lookup_smallest(context_t) override {
+ ceph_abort("impossible path"); }
+ eagain_ifuture<Ref<tree_cursor_t>> lookup_largest(context_t) override {
+ ceph_abort("impossible path"); }
+ eagain_ifuture<> test_clone_root(context_t, RootNodeTracker&) const override {
+ ceph_abort("impossible path"); }
+ eagain_ifuture<search_result_t> lower_bound_tracked(
+ context_t, const key_hobj_t&, MatchHistory&) override {
+ ceph_abort("impossible path"); }
+ eagain_ifuture<> do_get_tree_stats(context_t, tree_stats_t&) override {
+ ceph_abort("impossible path"); }
+ bool is_tracking() const override { return false; }
+ void track_merge(Ref<Node>, match_stage_t, search_position_t&) override {
+ ceph_abort("impossible path"); }
+
+ private:
+ DummyChild(DummyChildImpl* impl, DummyChildImpl::URef&& ref, DummyChildPool& pool)
+ : Node(std::move(ref)), impl{impl}, pool{pool} {
+ pool.track_node(this);
+ }
+
+ bool can_split() const { return impl->get_keys().size() > 1; }
+
+ static eagain_ifuture<> do_merge(
+ context_t c, Ref<DummyChild>&& left, Ref<DummyChild>&& right, bool stole_key) {
+ assert(right->use_count() == 1);
+ assert(left->impl->get_keys().size() == 1);
+ assert(right->impl->get_keys().size() == 1);
+ bool left_is_tail = right->impl->is_level_tail();
+ const std::set<ghobject_t>* p_keys;
+ if (stole_key) {
+ p_keys = &right->impl->get_keys();
+ } else {
+ p_keys = &left->impl->get_keys();
+ }
+ left->impl->reset(*p_keys, left_is_tail);
+ auto left_addr = left->impl->laddr();
+ return left->parent_info().ptr->apply_children_merge<true>(
+ c, std::move(left), left_addr, std::move(right), !stole_key);
+ }
+
+ DummyChildImpl* impl;
+ DummyChildPool& pool;
+ mutable std::random_device rd;
+ };
+
+ public:
+ DummyChildPool() = default;
+ ~DummyChildPool() { reset(); }
+
+ auto build_tree(const std::set<ghobject_t>& keys) {
+ reset();
+ // create tree
+ auto ref_dummy = NodeExtentManager::create_dummy(IS_DUMMY_SYNC);
+ p_dummy = static_cast<DummyManager*>(ref_dummy.get());
+ p_btree.emplace(std::move(ref_dummy));
+ return with_trans_intr(get_context().t, [this, &keys] (auto &tr) {
+ return DummyChild::create_initial(get_context(), keys, *this, *p_btree->root_tracker
+ ).si_then([this](auto initial_child) {
+ // split
+ splitable_nodes.insert(initial_child);
+ return trans_intr::repeat([this] ()
+ -> eagain_ifuture<seastar::stop_iteration> {
+ if (splitable_nodes.empty()) {
+ return seastar::make_ready_future<seastar::stop_iteration>(
+ seastar::stop_iteration::yes);
+ }
+ auto index = rd() % splitable_nodes.size();
+ auto iter = splitable_nodes.begin();
+ std::advance(iter, index);
+ Ref<DummyChild> child = *iter;
+ return child->populate_split(get_context(), splitable_nodes
+ ).si_then([] {
+ return seastar::stop_iteration::no;
+ });
+ });
+ }).si_then([this] {
+ //std::ostringstream oss;
+ //p_btree->dump(t(), oss);
+ //logger().info("\n{}\n", oss.str());
+ return p_btree->height(t());
+ }).si_then([](auto height) {
+ ceph_assert(height == 2);
+ });
+ });
+ }
+
+ seastar::future<> split_merge(ghobject_t key, search_position_t pos,
+ const split_expectation_t& expected) {
+ return seastar::async([this, key, pos, expected] {
+ DummyChildPool pool_clone;
+ clone_to(pool_clone);
+
+ // insert and split
+ logger().info("\n\nINSERT-SPLIT {} at pos({}):", key_hobj_t(key), pos);
+ auto node_to_split = pool_clone.get_node_by_pos(pos);
+ with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
+ return node_to_split->insert_and_split(
+ pool_clone.get_context(), key, pool_clone.splitable_nodes);
+ }).unsafe_get0();
+ {
+ std::ostringstream oss;
+ pool_clone.p_btree->dump(pool_clone.t(), oss);
+ logger().info("dump new root:\n{}", oss.str());
+ }
+ auto &pt = pool_clone.t();
+ EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 3);
+ EXPECT_TRUE(last_split.match(expected));
+ EXPECT_EQ(pool_clone.p_dummy->size(), 3);
+
+ // erase and merge
+ [[maybe_unused]] auto pivot_key = node_to_split->get_pivot_key();
+ logger().info("\n\nERASE-MERGE {}:", node_to_split->get_name());
+ assert(pivot_key == key_hobj_t(key));
+ with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
+ return node_to_split->merge(
+ pool_clone.get_context(), std::move(node_to_split));
+ }).unsafe_get0();
+ auto &pt2 = pool_clone.t();
+ EXPECT_EQ(INTR(pool_clone.p_btree->height ,pt2).unsafe_get0(), 2);
+ EXPECT_EQ(pool_clone.p_dummy->size(), 1);
+ });
+ }
+
+ seastar::future<> fix_index(
+ ghobject_t new_key, search_position_t pos, bool expect_split) {
+ return seastar::async([this, new_key, pos, expect_split] {
+ DummyChildPool pool_clone;
+ clone_to(pool_clone);
+
+ // fix
+ auto node_to_fix = pool_clone.get_node_by_pos(pos);
+ auto old_key = node_to_fix->get_pivot_key().to_ghobj();
+ logger().info("\n\nFIX pos({}) from {} to {}, expect_split={}:",
+ pos, node_to_fix->get_name(), key_hobj_t(new_key), expect_split);
+ with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
+ return node_to_fix->fix_key(pool_clone.get_context(), new_key);
+ }).unsafe_get0();
+ if (expect_split) {
+ std::ostringstream oss;
+ pool_clone.p_btree->dump(pool_clone.t(), oss);
+ logger().info("dump new root:\n{}", oss.str());
+ auto &pt = pool_clone.t();
+ EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 3);
+ EXPECT_EQ(pool_clone.p_dummy->size(), 3);
+ } else {
+ auto &pt = pool_clone.t();
+ EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 2);
+ EXPECT_EQ(pool_clone.p_dummy->size(), 1);
+ }
+
+ // fix back
+ logger().info("\n\nFIX pos({}) from {} back to {}:",
+ pos, node_to_fix->get_name(), key_hobj_t(old_key));
+ with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
+ return node_to_fix->fix_key(pool_clone.get_context(), old_key);
+ }).unsafe_get0();
+ auto &pt = pool_clone.t();
+ EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 2);
+ EXPECT_EQ(pool_clone.p_dummy->size(), 1);
+ });
+ }
+
+ private:
+ void clone_to(DummyChildPool& pool_clone) {
+ pool_clone_in_progress = &pool_clone;
+ auto ref_dummy = NodeExtentManager::create_dummy(IS_DUMMY_SYNC);
+ pool_clone.p_dummy = static_cast<DummyManager*>(ref_dummy.get());
+ pool_clone.p_btree.emplace(std::move(ref_dummy));
+ auto &pt = pool_clone.t();
+ [[maybe_unused]] auto &tr = t();
+ INTR_R(pool_clone.p_btree->test_clone_from,
+ pt, tr, *p_btree).unsafe_get0();
+ pool_clone_in_progress = nullptr;
+ }
+
+ void reset() {
+ ceph_assert(pool_clone_in_progress == nullptr);
+ if (tracked_children.size()) {
+ ceph_assert(!p_btree->test_is_clean());
+ tracked_children.clear();
+ ceph_assert(p_btree->test_is_clean());
+ p_dummy = nullptr;
+ p_btree.reset();
+ } else {
+ ceph_assert(!p_btree.has_value());
+ }
+ splitable_nodes.clear();
+ }
+
+ void track_node(Ref<DummyChild> node) {
+ ceph_assert(tracked_children.find(node) == tracked_children.end());
+ tracked_children.insert(node);
+ }
+
+ void untrack_node(Ref<DummyChild> node) {
+ auto ret = tracked_children.erase(node);
+ ceph_assert(ret == 1);
+ }
+
+ Ref<DummyChild> get_node_by_pos(const search_position_t& pos) const {
+ auto iter = std::find_if(
+ tracked_children.begin(), tracked_children.end(), [&pos](auto& child) {
+ return child->match_pos(pos);
+ });
+ ceph_assert(iter != tracked_children.end());
+ return *iter;
+ }
+
+ context_t get_context() {
+ ceph_assert(p_dummy != nullptr);
+ return {*p_dummy, vb, t()};
+ }
+
+ Transaction& t() const { return *ref_t; }
+
+ std::set<Ref<DummyChild>> tracked_children;
+ std::optional<UnboundedBtree> p_btree;
+ DummyManager* p_dummy = nullptr;
+ ValueBuilderImpl<UnboundedValue> vb;
+ TransactionRef ref_t = make_test_transaction();
+
+ std::random_device rd;
+ std::set<Ref<DummyChild>> splitable_nodes;
+
+ DummyChildPool* pool_clone_in_progress = nullptr;
+};
+
+}
+
+TEST_F(c_dummy_test_t, 5_split_merge_internal_node)
+{
+ run_async([] {
+ DummyChildPool pool;
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert:\n");
+ auto padding = std::string(250, '_');
+ auto keys = build_key_set({2, 6}, {2, 5}, {2, 5}, padding, true);
+ keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 2, 2));
+ keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 3, 3));
+ keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 4, 4));
+ keys.erase(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 2, 2));
+ keys.erase(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 3, 3));
+ keys.erase(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 4, 4));
+ auto padding_s = std::string(257, '_');
+ keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 2, 2));
+ keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 3, 3));
+ keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 4, 4));
+ auto padding_e = std::string(247, '_');
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 2, 2));
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 3, 3));
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 4, 4));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to right front at stage 0, 1, 2, 1, 0\n");
+ pool.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4" + padding, 5, 5), {2, {0, {0}}},
+ {2u, 0u, false, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(3, 3, 3, "ns5", "oid5", 3, 3), {2, {0, {0}}},
+ {2u, 1u, false, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(3, 4, 4, "ns3", "oid3", 3, 3), {2, {0, {0}}},
+ {2u, 2u, false, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(4, 4, 4, "ns1", "oid1", 3, 3), {2, {0, {0}}},
+ {2u, 1u, false, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(4, 4, 4, "ns2", "oid2" + padding, 1, 1), {2, {0, {0}}},
+ {2u, 0u, false, InsertType::BEGIN}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to right middle at stage 0, 1, 2, 1, 0\n");
+ pool.split_merge(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 5, 5), {3, {0, {0}}},
+ {2u, 0u, false, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(4, 4, 4, "ns5", "oid5", 3, 3), {3, {0, {0}}},
+ {2u, 1u, false, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(4, 4, 5, "ns3", "oid3", 3, 3), {3, {0, {0}}},
+ {2u, 2u, false, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(5, 5, 5, "ns1", "oid1", 3, 3), {3, {0, {0}}},
+ {2u, 1u, false, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(5, 5, 5, "ns2", "oid2" + padding, 1, 1), {3, {0, {0}}},
+ {2u, 0u, false, InsertType::MID}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to right back at stage 0, 1, 2\n");
+ pool.split_merge(make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 5, 5), search_position_t::end() ,
+ {2u, 0u, false, InsertType::LAST}).get();
+ pool.split_merge(make_ghobj(5, 5, 5, "ns5", "oid5", 3, 3), search_position_t::end(),
+ {2u, 1u, false, InsertType::LAST}).get();
+ pool.split_merge(make_ghobj(6, 6, 6, "ns3", "oid3", 3, 3), search_position_t::end(),
+ {2u, 2u, false, InsertType::LAST}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 0; insert to left front at stage 2, 1, 0\n");
+ pool.split_merge(make_ghobj(1, 1, 1, "ns3", "oid3", 3, 3), {0, {0, {0}}},
+ {0u, 2u, true, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(2, 2, 2, "ns1", "oid1", 3, 3), {0, {0, {0}}},
+ {0u, 1u, true, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 1, 1), {0, {0, {0}}},
+ {0u, 0u, true, InsertType::BEGIN}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 0; insert to left middle at stage 0, 1, 2, 1, 0\n");
+ pool.split_merge(make_ghobj(2, 2, 2, "ns4", "oid4" + padding, 5, 5), {1, {0, {0}}},
+ {0u, 0u, true, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(2, 2, 2, "ns5", "oid5", 3, 3), {1, {0, {0}}},
+ {0u, 1u, true, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(2, 2, 3, "ns3", "oid3" + std::string(80, '_'), 3, 3), {1, {0, {0}}} ,
+ {0u, 2u, true, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(3, 3, 3, "ns1", "oid1", 3, 3), {1, {0, {0}}},
+ {0u, 1u, true, InsertType::MID}).get();
+ pool.split_merge(make_ghobj(3, 3, 3, "ns2", "oid2" + padding, 1, 1), {1, {0, {0}}},
+ {0u, 0u, true, InsertType::MID}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 0; insert to left back at stage 0\n");
+ pool.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4" + padding, 3, 4), {1, {2, {2}}},
+ {0u, 0u, true, InsertType::LAST}).get();
+ }
+
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert (1):\n");
+ auto padding = std::string(244, '_');
+ auto keys = build_key_set({2, 6}, {2, 5}, {2, 5}, padding, true);
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 5, 5));
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 6, 6));
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 7, 7));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to left back at stage 0, 1, 2, 1\n");
+ pool.split_merge(make_ghobj(3, 3, 3, "ns4", "oid4" + padding, 5, 5), {2, {0, {0}}},
+ {2u, 0u, true, InsertType::LAST}).get();
+ pool.split_merge(make_ghobj(3, 3, 3, "ns5", "oid5", 3, 3), {2, {0, {0}}},
+ {2u, 1u, true, InsertType::LAST}).get();
+ pool.split_merge(make_ghobj(3, 4, 4, "n", "o", 3, 3), {2, {0, {0}}},
+ {2u, 2u, true, InsertType::LAST}).get();
+ pool.split_merge(make_ghobj(4, 4, 4, "n", "o", 3, 3), {2, {0, {0}}},
+ {2u, 1u, true, InsertType::LAST}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to left middle at stage 2\n");
+ pool.split_merge(make_ghobj(2, 3, 3, "n", "o", 3, 3), {1, {0, {0}}},
+ {2u, 2u, true, InsertType::MID}).get();
+ }
+
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert (2):\n");
+ auto padding = std::string(243, '_');
+ auto keys = build_key_set({2, 6}, {2, 5}, {2, 5}, padding, true);
+ keys.insert(make_ghobj(4, 4, 4, "n", "o", 3, 3));
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 5, 5));
+ keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 6, 6));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 2; insert to left back at stage (0, 1, 2, 1,) 0\n");
+ pool.split_merge(make_ghobj(4, 4, 4, "n", "o", 2, 2), {2, {0, {0}}},
+ {2u, 0u, true, InsertType::LAST}).get();
+ }
+
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert (3):\n");
+ auto padding = std::string(419, '_');
+ auto keys = build_key_set({2, 5}, {2, 5}, {2, 5}, padding, true);
+ keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 2, 2));
+ keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 3, 3));
+ keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 4, 4));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 1; insert to right front at stage 0, 1, 0\n");
+ pool.split_merge(make_ghobj(3, 3, 3, "ns2", "oid2" + padding, 5, 5), {1, {1, {0}}},
+ {1u, 0u, false, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(3, 3, 3, "ns2", "oid3", 3, 3), {1, {1, {0}}},
+ {1u, 1u, false, InsertType::BEGIN}).get();
+ pool.split_merge(make_ghobj(3, 3, 3, "ns3", "oid3" + padding, 1, 1), {1, {1, {0}}},
+ {1u, 0u, false, InsertType::BEGIN}).get();
+ }
+
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert (4):\n");
+ auto padding = std::string(361, '_');
+ auto keys = build_key_set({2, 5}, {2, 5}, {2, 5}, padding, true);
+ keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 2, 2));
+ keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 3, 3));
+ keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 4, 4));
+ auto padding_s = std::string(386, '_');
+ keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 2, 2));
+ keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 3, 3));
+ keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 4, 4));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 1; insert to left back at stage 0, 1\n");
+ pool.split_merge(make_ghobj(3, 3, 3, "ns2", "oid2" + padding, 5, 5), {1, {1, {0}}},
+ {1u, 0u, true, InsertType::LAST}).get();
+ pool.split_merge(make_ghobj(3, 3, 3, "ns2", "oid3", 3, 3), {1, {1, {0}}},
+ {1u, 1u, true, InsertType::LAST}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nfix end index from stage 0 to 0, 1, 2\n");
+ auto padding1 = std::string(400, '_');
+ pool.fix_index(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 5, 5),
+ {2, {2, {2}}}, false).get();
+ pool.fix_index(make_ghobj(4, 4, 4, "ns5", "oid5" + padding1, 3, 3),
+ {2, {2, {2}}}, true).get();
+ pool.fix_index(make_ghobj(5, 5, 5, "ns3", "oid3" + padding1, 3, 3),
+ {2, {2, {2}}}, true).get();
+ }
+
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert (5):\n");
+ auto padding = std::string(412, '_');
+ auto keys = build_key_set({2, 5}, {2, 5}, {2, 5}, padding);
+ keys.insert(make_ghobj(3, 3, 3, "ns2", "oid3", 3, 3));
+ keys.insert(make_ghobj(4, 4, 4, "ns3", "oid3" + padding, 5, 5));
+ keys.insert(make_ghobj(9, 9, 9, "ns~last", "oid~last", 9, 9));
+ keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 2, 2));
+ keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 3, 3));
+ keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 4, 4));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 1; insert to left back at stage (0, 1,) 0\n");
+ pool.split_merge(make_ghobj(3, 3, 3, "ns2", "oid3", 2, 2), {1, {1, {0}}},
+ {1u, 0u, true, InsertType::LAST}).get();
+ }
+
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert (6):\n");
+ auto padding = std::string(328, '_');
+ auto keys = build_key_set({2, 5}, {2, 5}, {2, 5}, padding);
+ keys.insert(make_ghobj(5, 5, 5, "ns3", "oid3" + std::string(270, '_'), 3, 3));
+ keys.insert(make_ghobj(9, 9, 9, "ns~last", "oid~last", 9, 9));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nsplit at stage 0; insert to right front at stage 0\n");
+ pool.split_merge(make_ghobj(3, 3, 3, "ns3", "oid3" + padding, 2, 3), {1, {1, {1}}},
+ {0u, 0u, false, InsertType::BEGIN}).get();
+
+ logger().info("\n---------------------------------------------"
+ "\nfix end index from stage 2 to 0, 1, 2\n");
+ auto padding1 = std::string(400, '_');
+ pool.fix_index(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 5, 5),
+ {3, {0, {0}}}, false).get();
+ pool.fix_index(make_ghobj(4, 4, 4, "ns5", "oid5" + padding1, 3, 3),
+ {3, {0, {0}}}, true).get();
+ pool.fix_index(make_ghobj(5, 5, 5, "ns4", "oid4" + padding1, 3, 3),
+ {3, {0, {0}}}, true).get();
+ }
+
+ {
+ logger().info("\n---------------------------------------------"
+ "\nbefore internal node insert (7):\n");
+ auto padding = std::string(323, '_');
+ auto keys = build_key_set({2, 5}, {2, 5}, {2, 5}, padding);
+ keys.insert(make_ghobj(4, 4, 4, "ns5", "oid5" + padding, 3, 3));
+ keys.insert(make_ghobj(9, 9, 9, "ns~last", "oid~last", 9, 9));
+ pool.build_tree(keys).unsafe_get0();
+
+ logger().info("\n---------------------------------------------"
+ "\nfix end index from stage 1 to 0, 1, 2\n");
+ auto padding1 = std::string(400, '_');
+ pool.fix_index(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 5, 5),
+ {2, {3, {0}}}, false).get();
+ pool.fix_index(make_ghobj(4, 4, 4, "ns6", "oid6" + padding1, 3, 3),
+ {2, {3, {0}}}, true).get();
+ pool.fix_index(make_ghobj(5, 5, 5, "ns3", "oid3" + padding1, 3, 3),
+ {2, {3, {0}}}, true).get();
+ }
+
+ // Impossible to split at {0, 0, 0}
+ // Impossible to split at [END, END, END]
+ });
+}
+
+struct d_seastore_tm_test_t :
+ public seastar_test_suite_t, TMTestState {
+ seastar::future<> set_up_fut() override final {
+ return tm_setup();
+ }
+ seastar::future<> tear_down_fut() override final {
+ return tm_teardown();
+ }
+};
+
+TEST_P(d_seastore_tm_test_t, 6_random_tree_insert_erase)
+{
+ run_async([this] {
+ constexpr bool TEST_SEASTORE = true;
+ constexpr bool TRACK_CURSORS = true;
+ auto kvs = KVPool<test_item_t>::create_raw_range(
+ {8, 11, 64, 256, 301, 320},
+ {8, 11, 64, 256, 301, 320},
+ {8, 16, 128, 512, 576, 640},
+ {0, 16}, {0, 10}, {0, 4});
+ auto moved_nm = (TEST_SEASTORE ? NodeExtentManager::create_seastore(*tm)
+ : NodeExtentManager::create_dummy(IS_DUMMY_SYNC));
+ auto p_nm = moved_nm.get();
+ auto tree = std::make_unique<TreeBuilder<TRACK_CURSORS, BoundedValue>>(
+ kvs, std::move(moved_nm));
+ {
+ auto t = create_mutate_transaction();
+ INTR(tree->bootstrap, *t).unsafe_get();
+ submit_transaction(std::move(t));
+ }
+
+ // test insert
+ {
+ auto t = create_mutate_transaction();
+ INTR(tree->insert, *t).unsafe_get();
+ submit_transaction(std::move(t));
+ }
+ {
+ auto t = create_read_transaction();
+ INTR(tree->get_stats, *t).unsafe_get();
+ }
+ if constexpr (TEST_SEASTORE) {
+ restart();
+ tree->reload(NodeExtentManager::create_seastore(*tm));
+ }
+ {
+ // Note: create_weak_transaction() can also work, but too slow.
+ auto t = create_read_transaction();
+ INTR(tree->validate, *t).unsafe_get();
+ }
+
+ // test erase 3/4
+ {
+ auto t = create_mutate_transaction();
+ auto size = kvs.size() / 4 * 3;
+ INTR_R(tree->erase, *t, size).unsafe_get();
+ submit_transaction(std::move(t));
+ }
+ {
+ auto t = create_read_transaction();
+ INTR(tree->get_stats, *t).unsafe_get();
+ }
+ if constexpr (TEST_SEASTORE) {
+ restart();
+ tree->reload(NodeExtentManager::create_seastore(*tm));
+ }
+ {
+ auto t = create_read_transaction();
+ INTR(tree->validate, *t).unsafe_get();
+ }
+
+ // test erase remaining
+ {
+ auto t = create_mutate_transaction();
+ auto size = kvs.size();
+ INTR_R(tree->erase, *t, size).unsafe_get();
+ submit_transaction(std::move(t));
+ }
+ {
+ auto t = create_read_transaction();
+ INTR(tree->get_stats, *t).unsafe_get();
+ }
+ if constexpr (TEST_SEASTORE) {
+ restart();
+ tree->reload(NodeExtentManager::create_seastore(*tm));
+ }
+ {
+ auto t = create_read_transaction();
+ INTR(tree->validate, *t).unsafe_get();
+ EXPECT_EQ(INTR(tree->height, *t).unsafe_get0(), 1);
+ }
+
+ if constexpr (!TEST_SEASTORE) {
+ auto p_dummy = static_cast<DummyManager*>(p_nm);
+ EXPECT_EQ(p_dummy->size(), 1);
+ }
+ tree.reset();
+ });
+}
+
+TEST_P(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
+{
+ run_async([this] {
+ constexpr double EAGAIN_PROBABILITY = 0.1;
+ constexpr bool TRACK_CURSORS = false;
+ auto kvs = KVPool<test_item_t>::create_raw_range(
+ {8, 11, 64, 128, 255, 256},
+ {8, 13, 64, 512, 2035, 2048},
+ {8, 16, 128, 576, 992, 1200},
+ {0, 8}, {0, 10}, {0, 4});
+ auto moved_nm = NodeExtentManager::create_seastore(
+ *tm, L_ADDR_MIN, EAGAIN_PROBABILITY);
+ auto p_nm = static_cast<SeastoreNodeExtentManager<true>*>(moved_nm.get());
+ auto tree = std::make_unique<TreeBuilder<TRACK_CURSORS, ExtendedValue>>(
+ kvs, std::move(moved_nm));
+ unsigned num_ops = 0;
+ unsigned num_ops_eagain = 0;
+
+ // bootstrap
+ ++num_ops;
+ repeat_eagain([this, &tree, &num_ops_eagain] {
+ ++num_ops_eagain;
+ return seastar::do_with(
+ create_mutate_transaction(),
+ [this, &tree](auto &t) {
+ return INTR(tree->bootstrap, *t
+ ).safe_then([this, &t] {
+ return submit_transaction_fut(*t);
+ });
+ });
+ }).unsafe_get0();
+ epm->run_background_work_until_halt().get0();
+
+ // insert
+ logger().warn("start inserting {} kvs ...", kvs.size());
+ {
+ auto iter = kvs.random_begin();
+ while (iter != kvs.random_end()) {
+ ++num_ops;
+ repeat_eagain([this, &tree, &num_ops_eagain, &iter] {
+ ++num_ops_eagain;
+ return seastar::do_with(
+ create_mutate_transaction(),
+ [this, &tree, &iter](auto &t) {
+ return INTR_R(tree->insert_one, *t, iter
+ ).safe_then([this, &t](auto cursor) {
+ cursor.invalidate();
+ return submit_transaction_fut(*t);
+ });
+ });
+ }).unsafe_get0();
+ epm->run_background_work_until_halt().get0();
+ ++iter;
+ }
+ }
+
+ {
+ p_nm->set_generate_eagain(false);
+ auto t = create_read_transaction();
+ INTR(tree->get_stats, *t).unsafe_get0();
+ p_nm->set_generate_eagain(true);
+ }
+
+ // lookup
+ logger().warn("start lookup {} kvs ...", kvs.size());
+ {
+ auto iter = kvs.begin();
+ while (iter != kvs.end()) {
+ ++num_ops;
+ repeat_eagain([this, &tree, &num_ops_eagain, &iter] {
+ ++num_ops_eagain;
+ auto t = create_read_transaction();
+ return INTR_R(tree->validate_one, *t, iter
+ ).safe_then([t=std::move(t)]{});
+ }).unsafe_get0();
+ ++iter;
+ }
+ }
+
+ // erase
+ logger().warn("start erase {} kvs ...", kvs.size());
+ {
+ kvs.shuffle();
+ auto iter = kvs.random_begin();
+ while (iter != kvs.random_end()) {
+ ++num_ops;
+ repeat_eagain([this, &tree, &num_ops_eagain, &iter] {
+ ++num_ops_eagain;
+ return seastar::do_with(
+ create_mutate_transaction(),
+ [this, &tree, &iter](auto &t) {
+ return INTR_R(tree->erase_one, *t, iter
+ ).safe_then([this, &t] () mutable {
+ return submit_transaction_fut(*t);
+ });
+ });
+ }).unsafe_get0();
+ epm->run_background_work_until_halt().get0();
+ ++iter;
+ }
+ kvs.erase_from_random(kvs.random_begin(), kvs.random_end());
+ }
+
+ {
+ p_nm->set_generate_eagain(false);
+ auto t = create_read_transaction();
+ INTR(tree->get_stats, *t).unsafe_get0();
+ INTR(tree->validate, *t).unsafe_get0();
+ EXPECT_EQ(INTR(tree->height,*t).unsafe_get0(), 1);
+ }
+
+ // we can adjust EAGAIN_PROBABILITY to get a proper eagain_rate
+ double eagain_rate = num_ops_eagain;
+ eagain_rate /= num_ops;
+ logger().info("eagain rate: {}", eagain_rate);
+
+ tree.reset();
+ });
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ d_seastore_tm_test,
+ d_seastore_tm_test_t,
+ ::testing::Values (
+ "segmented",
+ "circularbounded"
+ )
+);
diff --git a/src/test/crimson/seastore/onode_tree/test_value.h b/src/test/crimson/seastore/onode_tree/test_value.h
new file mode 100644
index 000000000..98249f8c9
--- /dev/null
+++ b/src/test/crimson/seastore/onode_tree/test_value.h
@@ -0,0 +1,240 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <fmt/format.h>
+
+#include "crimson/common/log.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/value.h"
+
+namespace crimson::os::seastore::onode {
+
+struct test_item_t {
+ using id_t = uint16_t;
+ using magic_t = uint32_t;
+
+ value_size_t size;
+ id_t id;
+ magic_t magic;
+
+ value_size_t get_payload_size() const {
+ assert(size > sizeof(value_header_t));
+ return static_cast<value_size_t>(size - sizeof(value_header_t));
+ }
+
+ static test_item_t create(std::size_t _size, std::size_t _id) {
+ ceph_assert(_size <= std::numeric_limits<value_size_t>::max());
+ ceph_assert(_size > sizeof(value_header_t));
+ value_size_t size = _size;
+
+ ceph_assert(_id <= std::numeric_limits<id_t>::max());
+ id_t id = _id;
+
+ return {size, id, (magic_t)id * 137};
+ }
+};
+inline std::ostream& operator<<(std::ostream& os, const test_item_t& item) {
+ return os << "TestItem(#" << item.id << ", " << item.size << "B)";
+}
+
+enum class delta_op_t : uint8_t {
+ UPDATE_ID,
+ UPDATE_TAIL_MAGIC,
+};
+
+inline std::ostream& operator<<(std::ostream& os, const delta_op_t op) {
+ switch (op) {
+ case delta_op_t::UPDATE_ID:
+ return os << "update_id";
+ case delta_op_t::UPDATE_TAIL_MAGIC:
+ return os << "update_tail_magic";
+ default:
+ return os << "unknown";
+ }
+}
+
+} // namespace crimson::os::seastore::onode
+
+#if FMT_VERSION >= 90000
+template<> struct fmt::formatter<crimson::os::seastore::onode::delta_op_t> : fmt::ostream_formatter {};
+#endif
+
+namespace crimson::os::seastore::onode {
+
+template <value_magic_t MAGIC,
+ string_size_t MAX_NS_SIZE,
+ string_size_t MAX_OID_SIZE,
+ value_size_t MAX_VALUE_PAYLOAD_SIZE,
+ extent_len_t INTERNAL_NODE_SIZE,
+ extent_len_t LEAF_NODE_SIZE,
+ bool DO_SPLIT_CHECK>
+class TestValue final : public Value {
+ public:
+ static constexpr tree_conf_t TREE_CONF = {
+ MAGIC,
+ MAX_NS_SIZE,
+ MAX_OID_SIZE,
+ MAX_VALUE_PAYLOAD_SIZE,
+ INTERNAL_NODE_SIZE,
+ LEAF_NODE_SIZE,
+ DO_SPLIT_CHECK
+ };
+
+ using id_t = test_item_t::id_t;
+ using magic_t = test_item_t::magic_t;
+ struct magic_packed_t {
+ magic_t value;
+ } __attribute__((packed));
+
+ private:
+ struct payload_t {
+ id_t id;
+ } __attribute__((packed));
+
+ struct Replayable {
+ static void set_id(NodeExtentMutable& payload_mut, id_t id) {
+ auto p_payload = get_write(payload_mut);
+ p_payload->id = id;
+ }
+
+ static void set_tail_magic(NodeExtentMutable& payload_mut, magic_t magic) {
+ auto length = payload_mut.get_length();
+ auto offset_magic = length - sizeof(magic_t);
+ payload_mut.copy_in_relative(offset_magic, magic);
+ }
+
+ private:
+ static payload_t* get_write(NodeExtentMutable& payload_mut) {
+ return reinterpret_cast<payload_t*>(payload_mut.get_write());
+ }
+ };
+
+ public:
+ class Recorder final : public ValueDeltaRecorder {
+
+ public:
+ Recorder(ceph::bufferlist& encoded)
+ : ValueDeltaRecorder(encoded) {}
+ ~Recorder() override = default;
+
+ void encode_set_id(NodeExtentMutable& payload_mut, id_t id) {
+ auto& encoded = get_encoded(payload_mut);
+ ceph::encode(delta_op_t::UPDATE_ID, encoded);
+ ceph::encode(id, encoded);
+ }
+
+ void encode_set_tail_magic(NodeExtentMutable& payload_mut, magic_t magic) {
+ auto& encoded = get_encoded(payload_mut);
+ ceph::encode(delta_op_t::UPDATE_TAIL_MAGIC, encoded);
+ ceph::encode(magic, encoded);
+ }
+
+ protected:
+ value_magic_t get_header_magic() const override {
+ return TREE_CONF.value_magic;
+ }
+
+ void apply_value_delta(ceph::bufferlist::const_iterator& delta,
+ NodeExtentMutable& payload_mut,
+ laddr_t value_addr) override {
+ delta_op_t op;
+ try {
+ ceph::decode(op, delta);
+ switch (op) {
+ case delta_op_t::UPDATE_ID: {
+ logger().debug("OTree::TestValue::Replay: decoding UPDATE_ID ...");
+ id_t id;
+ ceph::decode(id, delta);
+ logger().debug("OTree::TestValue::Replay: apply id={} ...", id);
+ Replayable::set_id(payload_mut, id);
+ break;
+ }
+ case delta_op_t::UPDATE_TAIL_MAGIC: {
+ logger().debug("OTree::TestValue::Replay: decoding UPDATE_TAIL_MAGIC ...");
+ magic_t magic;
+ ceph::decode(magic, delta);
+ logger().debug("OTree::TestValue::Replay: apply magic={} ...", magic);
+ Replayable::set_tail_magic(payload_mut, magic);
+ break;
+ }
+ default:
+ logger().error("OTree::TestValue::Replay: got unknown op {} when replay {:#x}+{:#x}",
+ op, value_addr, payload_mut.get_length());
+ ceph_abort();
+ }
+ } catch (buffer::error& e) {
+ logger().error("OTree::TestValue::Replay: got decode error {} when replay {:#x}+{:#x}",
+ e.what(), value_addr, payload_mut.get_length());
+ ceph_abort();
+ }
+ }
+
+ private:
+ seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+ };
+
+ TestValue(NodeExtentManager& nm, const ValueBuilder& vb, Ref<tree_cursor_t>& p_cursor)
+ : Value(nm, vb, p_cursor) {}
+ ~TestValue() override = default;
+
+ id_t get_id() const {
+ return read_payload<payload_t>()->id;
+ }
+ void set_id_replayable(Transaction& t, id_t id) {
+ auto value_mutable = prepare_mutate_payload<payload_t, Recorder>(t);
+ if (value_mutable.second) {
+ value_mutable.second->encode_set_id(value_mutable.first, id);
+ }
+ Replayable::set_id(value_mutable.first, id);
+ }
+
+ magic_t get_tail_magic() const {
+ auto p_payload = read_payload<payload_t>();
+ auto offset_magic = get_payload_size() - sizeof(magic_t);
+ auto p_magic = reinterpret_cast<const char*>(p_payload) + offset_magic;
+ return reinterpret_cast<const magic_packed_t*>(p_magic)->value;
+ }
+ void set_tail_magic_replayable(Transaction& t, magic_t magic) {
+ auto value_mutable = prepare_mutate_payload<payload_t, Recorder>(t);
+ if (value_mutable.second) {
+ value_mutable.second->encode_set_tail_magic(value_mutable.first, magic);
+ }
+ Replayable::set_tail_magic(value_mutable.first, magic);
+ }
+
+ /*
+ * tree_util.h related interfaces
+ */
+
+ using item_t = test_item_t;
+
+ void initialize(Transaction& t, const item_t& item) {
+ ceph_assert(get_payload_size() + sizeof(value_header_t) == item.size);
+ set_id_replayable(t, item.id);
+ set_tail_magic_replayable(t, item.magic);
+ }
+
+ void validate(const item_t& item) const {
+ ceph_assert(get_payload_size() + sizeof(value_header_t) == item.size);
+ ceph_assert(get_id() == item.id);
+ ceph_assert(get_tail_magic() == item.magic);
+ }
+};
+
+using UnboundedValue = TestValue<
+ value_magic_t::TEST_UNBOUND, 4096, 4096, 4096, 4096, 4096, false>;
+using BoundedValue = TestValue<
+ value_magic_t::TEST_BOUNDED, 320, 320, 640, 4096, 4096, true>;
+// should be the same configuration with FLTreeOnode
+using ExtendedValue = TestValue<
+ value_magic_t::TEST_EXTENDED, 256, 2048, 1200, 8192, 16384, true>;
+
+}
+
+#if FMT_VERSION >= 90000
+template<>
+struct fmt::formatter<crimson::os::seastore::onode::test_item_t> : fmt::ostream_formatter {};
+#endif
diff --git a/src/test/crimson/seastore/test_block.cc b/src/test/crimson/seastore/test_block.cc
new file mode 100644
index 000000000..f7a39b0ef
--- /dev/null
+++ b/src/test/crimson/seastore/test_block.cc
@@ -0,0 +1,41 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/seastore/test_block.h"
+
+namespace crimson::os::seastore {
+
+
+ceph::bufferlist TestBlock::get_delta() {
+ ceph::bufferlist bl;
+ encode(delta, bl);
+ return bl;
+}
+
+
+void TestBlock::apply_delta(const ceph::bufferlist &bl) {
+ auto biter = bl.begin();
+ decltype(delta) deltas;
+ decode(deltas, biter);
+ for (auto &&d : deltas) {
+ set_contents(d.val, d.offset, d.len);
+ }
+}
+
+ceph::bufferlist TestBlockPhysical::get_delta() {
+ ceph::bufferlist bl;
+ encode(delta, bl);
+ return bl;
+}
+
+void TestBlockPhysical::apply_delta_and_adjust_crc(
+ paddr_t, const ceph::bufferlist &bl) {
+ auto biter = bl.begin();
+ decltype(delta) deltas;
+ decode(deltas, biter);
+ for (auto &&d : deltas) {
+ set_contents(d.val, d.offset, d.len);
+ }
+}
+
+}
diff --git a/src/test/crimson/seastore/test_block.h b/src/test/crimson/seastore/test_block.h
new file mode 100644
index 000000000..ccdafb784
--- /dev/null
+++ b/src/test/crimson/seastore/test_block.h
@@ -0,0 +1,154 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <random>
+
+#include "crimson/os/seastore/transaction_manager.h"
+
+namespace crimson::os::seastore {
+
+struct test_extent_desc_t {
+ size_t len = 0;
+ unsigned checksum = 0;
+
+ bool operator==(const test_extent_desc_t &rhs) const {
+ return (len == rhs.len &&
+ checksum == rhs.checksum);
+ }
+ bool operator!=(const test_extent_desc_t &rhs) const {
+ return !(*this == rhs);
+ }
+};
+
+struct test_block_delta_t {
+ int8_t val = 0;
+ uint16_t offset = 0;
+ uint16_t len = 0;
+
+
+ DENC(test_block_delta_t, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.val, p);
+ denc(v.offset, p);
+ denc(v.len, p);
+ DENC_FINISH(p);
+ }
+};
+
+inline std::ostream &operator<<(
+ std::ostream &lhs, const test_extent_desc_t &rhs) {
+ return lhs << "test_extent_desc_t(len=" << rhs.len
+ << ", checksum=" << rhs.checksum << ")";
+}
+
+struct TestBlock : crimson::os::seastore::LogicalCachedExtent {
+ constexpr static extent_len_t SIZE = 4<<10;
+ using Ref = TCachedExtentRef<TestBlock>;
+
+ std::vector<test_block_delta_t> delta = {};
+
+ TestBlock(ceph::bufferptr &&ptr)
+ : LogicalCachedExtent(std::move(ptr)) {}
+ TestBlock(const TestBlock &other)
+ : LogicalCachedExtent(other) {}
+
+ CachedExtentRef duplicate_for_write(Transaction&) final {
+ return CachedExtentRef(new TestBlock(*this));
+ };
+
+ static constexpr extent_types_t TYPE = extent_types_t::TEST_BLOCK;
+ extent_types_t get_type() const final {
+ return TYPE;
+ }
+
+ ceph::bufferlist get_delta() final;
+
+ void set_contents(char c, uint16_t offset, uint16_t len) {
+ ::memset(get_bptr().c_str() + offset, c, len);
+ delta.push_back({c, offset, len});
+ }
+
+ void set_contents(char c) {
+ set_contents(c, 0, get_length());
+ }
+
+ test_extent_desc_t get_desc() {
+ return { get_length(), get_crc32c() };
+ }
+
+ void apply_delta(const ceph::bufferlist &bl) final;
+};
+using TestBlockRef = TCachedExtentRef<TestBlock>;
+
+struct TestBlockPhysical : crimson::os::seastore::CachedExtent{
+ constexpr static extent_len_t SIZE = 4<<10;
+ using Ref = TCachedExtentRef<TestBlockPhysical>;
+
+ std::vector<test_block_delta_t> delta = {};
+
+ TestBlockPhysical(ceph::bufferptr &&ptr)
+ : CachedExtent(std::move(ptr)) {}
+ TestBlockPhysical(const TestBlockPhysical &other)
+ : CachedExtent(other) {}
+
+ CachedExtentRef duplicate_for_write(Transaction&) final {
+ return CachedExtentRef(new TestBlockPhysical(*this));
+ };
+
+ static constexpr extent_types_t TYPE = extent_types_t::TEST_BLOCK_PHYSICAL;
+ extent_types_t get_type() const final {
+ return TYPE;
+ }
+
+ void set_contents(char c, uint16_t offset, uint16_t len) {
+ ::memset(get_bptr().c_str() + offset, c, len);
+ delta.push_back({c, offset, len});
+ }
+
+ void set_contents(char c) {
+ set_contents(c, 0, get_length());
+ }
+
+ ceph::bufferlist get_delta() final;
+
+ void apply_delta_and_adjust_crc(paddr_t, const ceph::bufferlist &bl) final;
+};
+using TestBlockPhysicalRef = TCachedExtentRef<TestBlockPhysical>;
+
+struct test_block_mutator_t {
+ std::uniform_int_distribution<int8_t>
+ contents_distribution = std::uniform_int_distribution<int8_t>(
+ std::numeric_limits<int8_t>::min(),
+ std::numeric_limits<int8_t>::max());
+
+ std::uniform_int_distribution<uint16_t>
+ offset_distribution = std::uniform_int_distribution<uint16_t>(
+ 0, TestBlock::SIZE - 1);
+
+ std::uniform_int_distribution<uint16_t> length_distribution(uint16_t offset) {
+ return std::uniform_int_distribution<uint16_t>(
+ 0, TestBlock::SIZE - offset - 1);
+ }
+
+
+ template <typename generator_t>
+ void mutate(TestBlock &block, generator_t &gen) {
+ auto offset = offset_distribution(gen);
+ block.set_contents(
+ contents_distribution(gen),
+ offset,
+ length_distribution(offset)(gen));
+ }
+};
+
+}
+
+WRITE_CLASS_DENC_BOUNDED(crimson::os::seastore::test_block_delta_t)
+
+#if FMT_VERSION >= 90000
+template <> struct fmt::formatter<crimson::os::seastore::test_extent_desc_t> : fmt::ostream_formatter {};
+template <> struct fmt::formatter<crimson::os::seastore::TestBlock> : fmt::ostream_formatter {};
+template <> struct fmt::formatter<crimson::os::seastore::TestBlockPhysical> : fmt::ostream_formatter {};
+#endif
diff --git a/src/test/crimson/seastore/test_btree_lba_manager.cc b/src/test/crimson/seastore/test_btree_lba_manager.cc
new file mode 100644
index 000000000..f18c3ac67
--- /dev/null
+++ b/src/test/crimson/seastore/test_btree_lba_manager.cc
@@ -0,0 +1,752 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "crimson/common/log.h"
+
+#include "crimson/os/seastore/journal.h"
+#include "crimson/os/seastore/cache.h"
+#include "crimson/os/seastore/segment_manager/ephemeral.h"
+#include "crimson/os/seastore/lba_manager/btree/btree_lba_manager.h"
+
+#include "test/crimson/seastore/test_block.h"
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+using namespace crimson::os::seastore::lba_manager;
+using namespace crimson::os::seastore::lba_manager::btree;
+
+struct btree_test_base :
+ public seastar_test_suite_t, SegmentProvider, JournalTrimmer {
+
+ segment_manager::EphemeralSegmentManagerRef segment_manager;
+ SegmentManagerGroupRef sms;
+ JournalRef journal;
+ ExtentPlacementManagerRef epm;
+ CacheRef cache;
+
+ size_t block_size;
+
+ WritePipeline pipeline;
+
+ segment_id_t next;
+
+ std::map<segment_id_t, segment_seq_t> segment_seqs;
+ std::map<segment_id_t, segment_type_t> segment_types;
+
+ journal_seq_t dummy_tail;
+
+ mutable segment_info_t tmp_info;
+
+ btree_test_base() = default;
+
+ /*
+ * JournalTrimmer interfaces
+ */
+ journal_seq_t get_journal_head() const final { return dummy_tail; }
+
+ void set_journal_head(journal_seq_t) final {}
+
+ journal_seq_t get_dirty_tail() const final { return dummy_tail; }
+
+ journal_seq_t get_alloc_tail() const final { return dummy_tail; }
+
+ void update_journal_tails(journal_seq_t, journal_seq_t) final {}
+
+ bool try_reserve_inline_usage(std::size_t) final { return true; }
+
+ void release_inline_usage(std::size_t) final {}
+
+ std::size_t get_trim_size_per_cycle() const final {
+ return 0;
+ }
+
+ /*
+ * SegmentProvider interfaces
+ */
+ const segment_info_t& get_seg_info(segment_id_t id) const final {
+ tmp_info = {};
+ tmp_info.seq = segment_seqs.at(id);
+ tmp_info.type = segment_types.at(id);
+ return tmp_info;
+ }
+
+ segment_id_t allocate_segment(
+ segment_seq_t seq,
+ segment_type_t type,
+ data_category_t,
+ rewrite_gen_t
+ ) final {
+ auto ret = next;
+ next = segment_id_t{
+ segment_manager->get_device_id(),
+ next.device_segment_id() + 1};
+ segment_seqs[ret] = seq;
+ segment_types[ret] = type;
+ return ret;
+ }
+
+ void close_segment(segment_id_t) final {}
+
+ void update_segment_avail_bytes(segment_type_t, paddr_t) final {}
+
+ void update_modify_time(segment_id_t, sea_time_point, std::size_t) final {}
+
+ SegmentManagerGroup* get_segment_manager_group() final { return sms.get(); }
+
+ virtual void complete_commit(Transaction &t) {}
+ seastar::future<> submit_transaction(TransactionRef t)
+ {
+ auto record = cache->prepare_record(*t, JOURNAL_SEQ_NULL, JOURNAL_SEQ_NULL);
+ return journal->submit_record(std::move(record), t->get_handle()).safe_then(
+ [this, t=std::move(t)](auto submit_result) mutable {
+ cache->complete_commit(
+ *t,
+ submit_result.record_block_base,
+ submit_result.write_result.start_seq);
+ complete_commit(*t);
+ }).handle_error(crimson::ct_error::assert_all{});
+ }
+
+ virtual LBAManager::mkfs_ret test_structure_setup(Transaction &t) = 0;
+ seastar::future<> set_up_fut() final {
+ segment_manager = segment_manager::create_test_ephemeral();
+ return segment_manager->init(
+ ).safe_then([this] {
+ return segment_manager->mkfs(
+ segment_manager::get_ephemeral_device_config(0, 1, 0));
+ }).safe_then([this] {
+ sms.reset(new SegmentManagerGroup());
+ journal = journal::make_segmented(*this, *this);
+ epm.reset(new ExtentPlacementManager());
+ cache.reset(new Cache(*epm));
+
+ block_size = segment_manager->get_block_size();
+ next = segment_id_t{segment_manager->get_device_id(), 0};
+ sms->add_segment_manager(segment_manager.get());
+ epm->test_init_no_background(segment_manager.get());
+ journal->set_write_pipeline(&pipeline);
+
+ return journal->open_for_mkfs().discard_result();
+ }).safe_then([this] {
+ dummy_tail = journal_seq_t{0,
+ paddr_t::make_seg_paddr(segment_id_t(segment_manager->get_device_id(), 0), 0)};
+ return epm->open_for_write();
+ }).safe_then([this] {
+ return seastar::do_with(
+ cache->create_transaction(
+ Transaction::src_t::MUTATE, "test_set_up_fut", false),
+ [this](auto &ref_t) {
+ return with_trans_intr(*ref_t, [&](auto &t) {
+ cache->init();
+ return cache->mkfs(t
+ ).si_then([this, &t] {
+ return test_structure_setup(t);
+ });
+ }).safe_then([this, &ref_t] {
+ return submit_transaction(std::move(ref_t));
+ });
+ });
+ }).handle_error(
+ crimson::ct_error::all_same_way([] {
+ ceph_assert(0 == "error");
+ })
+ );
+ }
+
+ virtual void test_structure_reset() {}
+ seastar::future<> tear_down_fut() final {
+ return cache->close(
+ ).safe_then([this] {
+ return journal->close();
+ }).safe_then([this] {
+ return epm->close();
+ }).safe_then([this] {
+ test_structure_reset();
+ segment_manager.reset();
+ sms.reset();
+ journal.reset();
+ epm.reset();
+ cache.reset();
+ }).handle_error(
+ crimson::ct_error::all_same_way([] {
+ ASSERT_FALSE("Unable to close");
+ })
+ );
+ }
+};
+
+struct lba_btree_test : btree_test_base {
+ std::map<laddr_t, lba_map_val_t> check;
+
+ auto get_op_context(Transaction &t) {
+ return op_context_t<laddr_t>{*cache, t};
+ }
+
+ LBAManager::mkfs_ret test_structure_setup(Transaction &t) final {
+ return cache->get_root(
+ t
+ ).si_then([this, &t](RootBlockRef croot) {
+ auto mut_croot = cache->duplicate_for_write(
+ t, croot
+ )->cast<RootBlock>();
+ mut_croot->root.lba_root =
+ LBABtree::mkfs(mut_croot, get_op_context(t));
+ });
+ }
+
+ template <typename F>
+ auto lba_btree_update(F &&f) {
+ auto tref = cache->create_transaction(
+ Transaction::src_t::MUTATE, "test_btree_update", false);
+ auto &t = *tref;
+ with_trans_intr(
+ t,
+ [this, tref=std::move(tref), f=std::forward<F>(f)](auto &t) mutable {
+ return cache->get_root(
+ t
+ ).si_then([f=std::move(f), &t](RootBlockRef croot) {
+ return seastar::do_with(
+ LBABtree(croot),
+ [f=std::move(f), &t](auto &btree) mutable {
+ return std::invoke(
+ std::move(f), btree, t
+ );
+ });
+ }).si_then([this, tref=std::move(tref)]() mutable {
+ return submit_transaction(std::move(tref));
+ });
+ }).unsafe_get0();
+ }
+
+ template <typename F>
+ auto lba_btree_read(F &&f) {
+ auto t = cache->create_transaction(
+ Transaction::src_t::READ, "test_btree_read", false);
+ return with_trans_intr(
+ *t,
+ [this, f=std::forward<F>(f)](auto &t) mutable {
+ return cache->get_root(
+ t
+ ).si_then([f=std::move(f), &t](RootBlockRef croot) mutable {
+ return seastar::do_with(
+ LBABtree(croot),
+ [f=std::move(f), &t](auto &btree) mutable {
+ return std::invoke(
+ std::move(f), btree, t
+ );
+ });
+ });
+ }).unsafe_get0();
+ }
+
+ static auto get_map_val(extent_len_t len) {
+ return lba_map_val_t{0, (pladdr_t)P_ADDR_NULL, len, 0};
+ }
+
+ device_off_t next_off = 0;
+ paddr_t get_paddr() {
+ next_off += block_size;
+ return make_fake_paddr(next_off);
+ }
+
+ void insert(laddr_t addr, extent_len_t len) {
+ ceph_assert(check.count(addr) == 0);
+ check.emplace(addr, get_map_val(len));
+ lba_btree_update([=, this](auto &btree, auto &t) {
+ auto extent = cache->alloc_new_extent<TestBlock>(
+ t,
+ TestBlock::SIZE,
+ placement_hint_t::HOT,
+ 0,
+ get_paddr());
+ return btree.insert(
+ get_op_context(t), addr, get_map_val(len), extent.get()
+ ).si_then([addr, extent](auto p){
+ auto& [iter, inserted] = p;
+ assert(inserted);
+ extent->set_laddr(addr);
+ });
+ });
+ }
+
+ void remove(laddr_t addr) {
+ auto iter = check.find(addr);
+ ceph_assert(iter != check.end());
+ auto len = iter->second.len;
+ check.erase(iter++);
+ lba_btree_update([=, this](auto &btree, auto &t) {
+ return btree.lower_bound(
+ get_op_context(t), addr
+ ).si_then([this, len, addr, &btree, &t](auto iter) {
+ EXPECT_FALSE(iter.is_end());
+ EXPECT_TRUE(iter.get_key() == addr);
+ EXPECT_TRUE(iter.get_val().len == len);
+ return btree.remove(
+ get_op_context(t), iter
+ );
+ });
+ });
+ }
+
+ void check_lower_bound(laddr_t addr) {
+ auto iter = check.lower_bound(addr);
+ auto result = lba_btree_read([=, this](auto &btree, auto &t) {
+ return btree.lower_bound(
+ get_op_context(t), addr
+ ).si_then([](auto iter)
+ -> std::optional<std::pair<const laddr_t, const lba_map_val_t>> {
+ if (iter.is_end()) {
+ return std::nullopt;
+ } else {
+ return std::make_optional(
+ std::make_pair(iter.get_key(), iter.get_val()));
+ }
+ });
+ });
+ if (iter == check.end()) {
+ EXPECT_FALSE(result);
+ } else {
+ EXPECT_TRUE(result);
+ decltype(result) to_check = *iter;
+ EXPECT_EQ(to_check, *result);
+ }
+ }
+};
+
+TEST_F(lba_btree_test, basic)
+{
+ run_async([this] {
+ constexpr unsigned total = 16<<10;
+ for (unsigned i = 0; i < total; i += 16) {
+ insert(i, 8);
+ }
+
+ for (unsigned i = 0; i < total; i += 16) {
+ check_lower_bound(i);
+ check_lower_bound(i + 4);
+ check_lower_bound(i + 8);
+ check_lower_bound(i + 12);
+ }
+ });
+}
+
+struct btree_lba_manager_test : btree_test_base {
+ BtreeLBAManagerRef lba_manager;
+
+ btree_lba_manager_test() = default;
+
+ void complete_commit(Transaction &t) final {}
+
+ LBAManager::mkfs_ret test_structure_setup(Transaction &t) final {
+ lba_manager.reset(new BtreeLBAManager(*cache));
+ return lba_manager->mkfs(t);
+ }
+
+ void test_structure_reset() final {
+ lba_manager.reset();
+ }
+
+ struct test_extent_t {
+ paddr_t addr;
+ size_t len = 0;
+ unsigned refcount = 0;
+ };
+ using test_lba_mapping_t = std::map<laddr_t, test_extent_t>;
+ test_lba_mapping_t test_lba_mappings;
+ struct test_transaction_t {
+ TransactionRef t;
+ test_lba_mapping_t mappings;
+ };
+
+ auto create_transaction(bool create_fake_extent=true) {
+ auto t = test_transaction_t{
+ cache->create_transaction(
+ Transaction::src_t::MUTATE, "test_mutate_lba", false),
+ test_lba_mappings
+ };
+ if (create_fake_extent) {
+ cache->alloc_new_extent<TestBlockPhysical>(
+ *t.t,
+ TestBlockPhysical::SIZE,
+ placement_hint_t::HOT,
+ 0);
+ };
+ return t;
+ }
+
+ auto create_weak_transaction() {
+ auto t = test_transaction_t{
+ cache->create_transaction(
+ Transaction::src_t::READ, "test_read_weak", true),
+ test_lba_mappings
+ };
+ return t;
+ }
+
+ void submit_test_transaction(test_transaction_t t) {
+ submit_transaction(std::move(t.t)).get();
+ test_lba_mappings.swap(t.mappings);
+ }
+
+ auto get_overlap(test_transaction_t &t, laddr_t addr, size_t len) {
+ auto bottom = t.mappings.upper_bound(addr);
+ if (bottom != t.mappings.begin())
+ --bottom;
+ if (bottom != t.mappings.end() &&
+ bottom->first + bottom->second.len <= addr)
+ ++bottom;
+
+ auto top = t.mappings.lower_bound(addr + len);
+ return std::make_pair(
+ bottom,
+ top
+ );
+ }
+
+ device_off_t next_off = 0;
+ paddr_t get_paddr() {
+ next_off += block_size;
+ return make_fake_paddr(next_off);
+ }
+
+ auto alloc_mapping(
+ test_transaction_t &t,
+ laddr_t hint,
+ size_t len) {
+ auto ret = with_trans_intr(
+ *t.t,
+ [=, this](auto &t) {
+ auto extent = cache->alloc_new_extent<TestBlock>(
+ t,
+ TestBlock::SIZE,
+ placement_hint_t::HOT,
+ 0,
+ get_paddr());
+ return lba_manager->alloc_extent(
+ t, hint, len, extent->get_paddr(), *extent);
+ }).unsafe_get0();
+ logger().debug("alloc'd: {}", *ret);
+ EXPECT_EQ(len, ret->get_length());
+ auto [b, e] = get_overlap(t, ret->get_key(), len);
+ EXPECT_EQ(b, e);
+ t.mappings.emplace(
+ std::make_pair(
+ ret->get_key(),
+ test_extent_t{
+ ret->get_val(),
+ ret->get_length(),
+ 1
+ }
+ ));
+ return ret;
+ }
+
+ auto decref_mapping(
+ test_transaction_t &t,
+ laddr_t addr) {
+ return decref_mapping(t, t.mappings.find(addr));
+ }
+
+ void decref_mapping(
+ test_transaction_t &t,
+ test_lba_mapping_t::iterator target) {
+ ceph_assert(target != t.mappings.end());
+ ceph_assert(target->second.refcount > 0);
+ target->second.refcount--;
+
+ (void) with_trans_intr(
+ *t.t,
+ [=, this](auto &t) {
+ return lba_manager->decref_extent(
+ t,
+ target->first,
+ true
+ ).si_then([this, &t, target](auto result) {
+ EXPECT_EQ(result.refcount, target->second.refcount);
+ if (result.refcount == 0) {
+ return cache->retire_extent_addr(
+ t, result.addr.get_paddr(), result.length);
+ }
+ return Cache::retire_extent_iertr::now();
+ });
+ }).unsafe_get0();
+ if (target->second.refcount == 0) {
+ t.mappings.erase(target);
+ }
+ }
+
+ auto incref_mapping(
+ test_transaction_t &t,
+ laddr_t addr) {
+ return incref_mapping(t, t.mappings.find(addr));
+ }
+
+ void incref_mapping(
+ test_transaction_t &t,
+ test_lba_mapping_t::iterator target) {
+ ceph_assert(target->second.refcount > 0);
+ target->second.refcount++;
+ auto refcnt = with_trans_intr(
+ *t.t,
+ [=, this](auto &t) {
+ return lba_manager->incref_extent(
+ t,
+ target->first);
+ }).unsafe_get0().refcount;
+ EXPECT_EQ(refcnt, target->second.refcount);
+ }
+
+ std::vector<laddr_t> get_mapped_addresses() {
+ std::vector<laddr_t> addresses;
+ addresses.reserve(test_lba_mappings.size());
+ for (auto &i: test_lba_mappings) {
+ addresses.push_back(i.first);
+ }
+ return addresses;
+ }
+
+ std::vector<laddr_t> get_mapped_addresses(test_transaction_t &t) {
+ std::vector<laddr_t> addresses;
+ addresses.reserve(t.mappings.size());
+ for (auto &i: t.mappings) {
+ addresses.push_back(i.first);
+ }
+ return addresses;
+ }
+
+ void check_mappings() {
+ auto t = create_transaction();
+ check_mappings(t);
+ }
+
+ void check_mappings(test_transaction_t &t) {
+ (void)with_trans_intr(
+ *t.t,
+ [=, this](auto &t) {
+ return lba_manager->check_child_trackers(t);
+ }).unsafe_get0();
+ for (auto &&i: t.mappings) {
+ auto laddr = i.first;
+ auto len = i.second.len;
+
+ auto ret_list = with_trans_intr(
+ *t.t,
+ [=, this](auto &t) {
+ return lba_manager->get_mappings(
+ t, laddr, len);
+ }).unsafe_get0();
+ EXPECT_EQ(ret_list.size(), 1);
+ auto &ret = *ret_list.begin();
+ EXPECT_EQ(i.second.addr, ret->get_val());
+ EXPECT_EQ(laddr, ret->get_key());
+ EXPECT_EQ(len, ret->get_length());
+
+ auto ret_pin = with_trans_intr(
+ *t.t,
+ [=, this](auto &t) {
+ return lba_manager->get_mapping(
+ t, laddr);
+ }).unsafe_get0();
+ EXPECT_EQ(i.second.addr, ret_pin->get_val());
+ EXPECT_EQ(laddr, ret_pin->get_key());
+ EXPECT_EQ(len, ret_pin->get_length());
+ }
+ with_trans_intr(
+ *t.t,
+ [=, &t, this](auto &) {
+ return lba_manager->scan_mappings(
+ *t.t,
+ 0,
+ L_ADDR_MAX,
+ [iter=t.mappings.begin(), &t](auto l, auto p, auto len) mutable {
+ EXPECT_NE(iter, t.mappings.end());
+ EXPECT_EQ(l, iter->first);
+ EXPECT_EQ(p, iter->second.addr);
+ EXPECT_EQ(len, iter->second.len);
+ ++iter;
+ });
+ }).unsafe_get();
+ }
+};
+
+TEST_F(btree_lba_manager_test, basic)
+{
+ run_async([this] {
+ laddr_t laddr = 0x12345678 * block_size;
+ {
+ // write initial mapping
+ auto t = create_transaction();
+ check_mappings(t); // check in progress transaction sees mapping
+ check_mappings(); // check concurrent does not
+ auto ret = alloc_mapping(t, laddr, block_size);
+ submit_test_transaction(std::move(t));
+ }
+ check_mappings(); // check new transaction post commit sees it
+ });
+}
+
+TEST_F(btree_lba_manager_test, force_split)
+{
+ run_async([this] {
+ for (unsigned i = 0; i < 40; ++i) {
+ auto t = create_transaction();
+ logger().debug("opened transaction");
+ for (unsigned j = 0; j < 5; ++j) {
+ auto ret = alloc_mapping(t, 0, block_size);
+ if ((i % 10 == 0) && (j == 3)) {
+ check_mappings(t);
+ check_mappings();
+ }
+ }
+ logger().debug("submitting transaction");
+ submit_test_transaction(std::move(t));
+ check_mappings();
+ }
+ });
+}
+
+TEST_F(btree_lba_manager_test, force_split_merge)
+{
+ run_async([this] {
+ for (unsigned i = 0; i < 80; ++i) {
+ auto t = create_transaction();
+ logger().debug("opened transaction");
+ for (unsigned j = 0; j < 5; ++j) {
+ auto ret = alloc_mapping(t, 0, block_size);
+ // just to speed things up a bit
+ if ((i % 100 == 0) && (j == 3)) {
+ check_mappings(t);
+ check_mappings();
+ }
+ incref_mapping(t, ret->get_key());
+ decref_mapping(t, ret->get_key());
+ }
+ logger().debug("submitting transaction");
+ submit_test_transaction(std::move(t));
+ if (i % 50 == 0) {
+ check_mappings();
+ }
+ }
+ {
+ auto addresses = get_mapped_addresses();
+ auto t = create_transaction();
+ for (unsigned i = 0; i != addresses.size(); ++i) {
+ if (i % 2 == 0) {
+ incref_mapping(t, addresses[i]);
+ decref_mapping(t, addresses[i]);
+ decref_mapping(t, addresses[i]);
+ }
+ logger().debug("submitting transaction");
+ if (i % 7 == 0) {
+ submit_test_transaction(std::move(t));
+ t = create_transaction();
+ }
+ if (i % 13 == 0) {
+ check_mappings();
+ check_mappings(t);
+ }
+ }
+ submit_test_transaction(std::move(t));
+ }
+ {
+ auto addresses = get_mapped_addresses();
+ auto t = create_transaction();
+ for (unsigned i = 0; i != addresses.size(); ++i) {
+ incref_mapping(t, addresses[i]);
+ decref_mapping(t, addresses[i]);
+ decref_mapping(t, addresses[i]);
+ }
+ check_mappings(t);
+ submit_test_transaction(std::move(t));
+ check_mappings();
+ }
+ });
+}
+
+TEST_F(btree_lba_manager_test, single_transaction_split_merge)
+{
+ run_async([this] {
+ {
+ auto t = create_transaction();
+ for (unsigned i = 0; i < 400; ++i) {
+ alloc_mapping(t, 0, block_size);
+ }
+ check_mappings(t);
+ submit_test_transaction(std::move(t));
+ }
+ check_mappings();
+
+ {
+ auto addresses = get_mapped_addresses();
+ auto t = create_transaction();
+ for (unsigned i = 0; i != addresses.size(); ++i) {
+ if (i % 4 != 0) {
+ decref_mapping(t, addresses[i]);
+ }
+ }
+ check_mappings(t);
+ submit_test_transaction(std::move(t));
+ }
+ check_mappings();
+
+ {
+ auto t = create_transaction();
+ for (unsigned i = 0; i < 600; ++i) {
+ alloc_mapping(t, 0, block_size);
+ }
+ auto addresses = get_mapped_addresses(t);
+ for (unsigned i = 0; i != addresses.size(); ++i) {
+ decref_mapping(t, addresses[i]);
+ }
+ check_mappings(t);
+ submit_test_transaction(std::move(t));
+ }
+ check_mappings();
+ });
+}
+
+TEST_F(btree_lba_manager_test, split_merge_multi)
+{
+ run_async([this] {
+ auto iterate = [&](auto f) {
+ for (uint64_t i = 0; i < (1<<10); ++i) {
+ auto t = create_transaction(false);
+ logger().debug("opened transaction");
+ for (unsigned j = 0; j < 5; ++j) {
+ f(t, (i * 5) + j);
+ }
+ logger().debug("submitting transaction");
+ submit_test_transaction(std::move(t));
+ }
+ };
+ iterate([&](auto &t, auto idx) {
+ alloc_mapping(t, idx * block_size, block_size);
+ });
+ check_mappings();
+ iterate([&](auto &t, auto idx) {
+ if ((idx % 32) > 0) {
+ decref_mapping(t, idx * block_size);
+ }
+ });
+ check_mappings();
+ iterate([&](auto &t, auto idx) {
+ if ((idx % 32) > 0) {
+ alloc_mapping(t, idx * block_size, block_size);
+ }
+ });
+ check_mappings();
+ iterate([&](auto &t, auto idx) {
+ decref_mapping(t, idx * block_size);
+ });
+ check_mappings();
+ });
+}
diff --git a/src/test/crimson/seastore/test_cbjournal.cc b/src/test/crimson/seastore/test_cbjournal.cc
new file mode 100644
index 000000000..0bf2d4135
--- /dev/null
+++ b/src/test/crimson/seastore/test_cbjournal.cc
@@ -0,0 +1,583 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+
+#include <random>
+
+#include "crimson/common/log.h"
+#include "crimson/os/seastore/async_cleaner.h"
+#include "crimson/os/seastore/journal.h"
+#include "crimson/os/seastore/journal/circular_bounded_journal.h"
+#include "crimson/os/seastore/random_block_manager.h"
+#include "crimson/os/seastore/random_block_manager/rbm_device.h"
+#include "crimson/os/seastore/seastore_types.h"
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+#include "crimson/os/seastore/random_block_manager/block_rb_manager.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+using namespace crimson::os::seastore::journal;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+std::optional<record_t> decode_record(
+ bufferlist& bl)
+{
+ record_t record;
+ record_group_header_t r_header;
+ auto bliter = bl.cbegin();
+ decode(r_header, bliter);
+ logger().debug(" decode_record mdlength {} records {}",
+ r_header.mdlength, r_header.records);
+ device_id_t d_id = 1 << (std::numeric_limits<device_id_t>::digits - 1);
+
+ auto del_infos = try_decode_deltas(r_header, bl,
+ paddr_t::make_blk_paddr(d_id, 0));
+ for (auto &iter : *del_infos) {
+ for (auto r : iter.deltas) {
+ record.deltas.push_back(r.second);
+ }
+ }
+ auto ex_infos = try_decode_extent_infos(r_header, bl);
+ auto bliter_ex = bl.cbegin();
+ bliter_ex += r_header.mdlength;
+ for (auto &iter: *ex_infos) {
+ for (auto e : iter.extent_infos) {
+ extent_t ex;
+ auto bptr = bufferptr(ceph::buffer::create_page_aligned(e.len));
+ logger().debug(" exten len {} remaining {} ", e.len, bliter_ex.get_remaining());
+ bliter_ex.copy(e.len, bptr.c_str());
+ ex.bl.append(bptr);
+ record.extents.push_back(ex);
+ }
+ }
+ return record;
+}
+
+struct entry_validator_t {
+ bufferlist bl;
+ int entries;
+ record_t record;
+ segment_nonce_t magic = 0;
+ journal_seq_t seq;
+
+ template <typename... T>
+ entry_validator_t(T&&... entry) : record(std::forward<T>(entry)...) {}
+
+ void validate(record_t read) {
+ auto iter = read.extents.begin();
+ for (auto &&block : record.extents) {
+ ASSERT_EQ(
+ iter->bl.length(),
+ block.bl.length());
+ ASSERT_EQ(
+ iter->bl.begin().crc32c(iter->bl.length(), 1),
+ block.bl.begin().crc32c(block.bl.length(), 1));
+ ++iter;
+ }
+ auto iter_delta = read.deltas.begin();
+ for (auto &&block : record.deltas) {
+ ASSERT_EQ(
+ iter_delta->bl.length(),
+ block.bl.length());
+ ASSERT_EQ(
+ iter_delta->bl.begin().crc32c(iter_delta->bl.length(), 1),
+ block.bl.begin().crc32c(block.bl.length(), 1));
+ ++iter_delta;
+ }
+ }
+ void validate(CircularBoundedJournal &cbj) {
+ rbm_abs_addr offset = 0;
+ auto cursor = scan_valid_records_cursor(seq);
+ cbj.test_initialize_cursor(cursor);
+ for (int i = 0; i < entries; i++) {
+ paddr_t paddr = seq.offset.add_offset(offset);
+ cursor.seq.offset = paddr;
+ auto md = cbj.test_read_validate_record_metadata(
+ cursor, magic).unsafe_get0();
+ assert(md);
+ auto& [header, md_bl] = *md;
+ auto dbuf = cbj.read(
+ paddr.add_offset(header.mdlength),
+ header.dlength).unsafe_get0();
+
+ bufferlist bl;
+ bl.append(md_bl);
+ bl.append(dbuf);
+ auto record = decode_record(bl);
+ validate(*record);
+ offset += header.mdlength + header.dlength;
+ cursor.last_committed = header.committed_to;
+ }
+ }
+
+ rbm_abs_addr get_abs_addr() {
+ return convert_paddr_to_abs_addr(seq.offset);
+ }
+
+ bool validate_delta(bufferlist bl) {
+ for (auto &&block : record.deltas) {
+ if (bl.begin().crc32c(bl.length(), 1) ==
+ block.bl.begin().crc32c(block.bl.length(), 1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
+struct cbjournal_test_t : public seastar_test_suite_t, JournalTrimmer
+{
+ std::vector<entry_validator_t> entries;
+ std::unique_ptr<CircularBoundedJournal> cbj;
+ random_block_device::EphemeralRBMDeviceRef device;
+
+ std::default_random_engine generator;
+ uint64_t block_size;
+ WritePipeline pipeline;
+
+ cbjournal_test_t() = default;
+
+ /*
+ * JournalTrimmer interfaces
+ */
+ journal_seq_t get_journal_head() const {
+ return JOURNAL_SEQ_NULL;
+ }
+
+ journal_seq_t get_dirty_tail() const final {
+ return JOURNAL_SEQ_NULL;
+ }
+
+ journal_seq_t get_alloc_tail() const final {
+ return JOURNAL_SEQ_NULL;
+ }
+
+ void set_journal_head(journal_seq_t head) final {}
+
+ void update_journal_tails(
+ journal_seq_t dirty_tail,
+ journal_seq_t alloc_tail) final {}
+
+ bool try_reserve_inline_usage(std::size_t) final { return true; }
+
+ void release_inline_usage(std::size_t) final {}
+
+ std::size_t get_trim_size_per_cycle() const final {
+ return 0;
+ }
+
+ auto submit_record(record_t&& record) {
+ entries.push_back(record);
+ OrderingHandle handle = get_dummy_ordering_handle();
+ auto [addr, w_result] = cbj->submit_record(
+ std::move(record),
+ handle).unsafe_get0();
+ entries.back().seq = w_result.start_seq;
+ entries.back().entries = 1;
+ entries.back().magic = cbj->get_cjs().get_cbj_header().magic;
+ logger().debug("submit entry to addr {}", entries.back().seq);
+ return convert_paddr_to_abs_addr(entries.back().seq.offset);
+ }
+
+ seastar::future<> tear_down_fut() final {
+ return close();
+ }
+
+ extent_t generate_extent(size_t blocks) {
+ std::uniform_int_distribution<char> distribution(
+ std::numeric_limits<char>::min(),
+ std::numeric_limits<char>::max()
+ );
+ char contents = distribution(generator);
+ bufferlist bl;
+ bl.append(buffer::ptr(buffer::create(blocks * block_size, contents)));
+ return extent_t{extent_types_t::TEST_BLOCK, L_ADDR_NULL, bl};
+ }
+
+ delta_info_t generate_delta(size_t bytes) {
+ std::uniform_int_distribution<char> distribution(
+ std::numeric_limits<char>::min(),
+ std::numeric_limits<char>::max()
+ );
+ char contents = distribution(generator);
+ bufferlist bl;
+ bl.append(buffer::ptr(buffer::create(bytes, contents)));
+ return delta_info_t{
+ extent_types_t::TEST_BLOCK,
+ paddr_t{},
+ L_ADDR_NULL,
+ 0, 0,
+ device->get_block_size(),
+ 1,
+ 0,
+ segment_type_t::JOURNAL,
+ bl
+ };
+ }
+
+ auto replay_and_check() {
+ for (auto &i : entries) {
+ i.validate(*(cbj.get()));
+ }
+ }
+
+ auto replay() {
+ return cbj->replay(
+ [this](const auto &offsets,
+ const auto &e,
+ auto &dirty_seq,
+ auto &alloc_seq,
+ auto last_modified) {
+ bool found = false;
+ for (auto &i : entries) {
+ paddr_t base = offsets.write_result.start_seq.offset;
+ rbm_abs_addr addr = convert_paddr_to_abs_addr(base);
+ if (addr == i.get_abs_addr()) {
+ logger().debug(" compare addr: {} and i.addr {} ", base, i.get_abs_addr());
+ found = i.validate_delta(e.bl);
+ break;
+ }
+ }
+ assert(found == true);
+ return Journal::replay_ertr::make_ready_future<bool>(true);
+ });
+ }
+
+ auto mkfs() {
+ device_config_t config = get_rbm_ephemeral_device_config(0, 1);
+ return device->mkfs(config
+ ).safe_then([this]() {
+ return device->mount(
+ ).safe_then([this]() {
+ return cbj->open_for_mkfs(
+ ).safe_then([](auto q) {
+ return seastar::now();
+ });
+ });
+ }).safe_then([this] {
+ return cbj->close();
+ });
+ }
+ auto open() {
+ return cbj->open_for_mount(
+ ).safe_then([](auto q) {
+ return seastar::now();
+ });
+ }
+ seastar::future<> close() {
+ return cbj->close().handle_error(crimson::ct_error::assert_all{});
+ }
+ auto get_records_available_size() {
+ return cbj->get_cjs().get_records_available_size();
+ }
+ auto get_records_total_size() {
+ return cbj->get_cjs().get_records_total_size();
+ }
+ auto get_block_size() {
+ return device->get_block_size();
+ }
+ auto get_written_to_rbm_addr() {
+ return cbj->get_rbm_addr(cbj->get_cjs().get_written_to());
+ }
+ auto get_written_to() {
+ return cbj->get_cjs().get_written_to();
+ }
+ auto get_journal_tail() {
+ return cbj->get_dirty_tail();
+ }
+ auto get_records_used_size() {
+ return cbj->get_cjs().get_records_used_size();
+ }
+ bool is_available_size(uint64_t size) {
+ return cbj->get_cjs().is_available_size(size);
+ }
+ void update_journal_tail(rbm_abs_addr addr, uint32_t len) {
+ paddr_t paddr =
+ convert_abs_addr_to_paddr(
+ addr + len,
+ cbj->get_device_id());
+ journal_seq_t seq = {0, paddr};
+ cbj->update_journal_tail(
+ seq,
+ seq
+ ).get0();
+ }
+ void set_written_to(journal_seq_t seq) {
+ cbj->set_written_to(seq);
+ }
+
+ seastar::future<> set_up_fut() final {
+ device = random_block_device::create_test_ephemeral(
+ random_block_device::DEFAULT_TEST_CBJOURNAL_SIZE, 0);
+ cbj.reset(new CircularBoundedJournal(*this, device.get(), std::string()));
+ block_size = device->get_block_size();
+ cbj->set_write_pipeline(&pipeline);
+ return mkfs(
+ ).safe_then([this] {
+ return replay(
+ ).safe_then([this] {
+ return open(
+ ).safe_then([this] {
+ return replay();
+ });
+ });
+ }).handle_error(crimson::ct_error::assert_all{});
+ }
+};
+
+TEST_F(cbjournal_test_t, submit_one_record)
+{
+ run_async([this] {
+ submit_record(
+ record_t{
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(3), generate_delta(4) }
+ });
+ replay_and_check();
+ });
+}
+
+TEST_F(cbjournal_test_t, submit_three_records)
+{
+ run_async([this] {
+ submit_record(
+ record_t{
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(3), generate_delta(4) }
+ });
+ submit_record(
+ record_t{
+ { generate_extent(8), generate_extent(9) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ submit_record(
+ record_t{
+ { generate_extent(5), generate_extent(6) },
+ { generate_delta(200), generate_delta(210) }
+ });
+ replay_and_check();
+ });
+}
+
+TEST_F(cbjournal_test_t, submit_full_records)
+{
+ run_async([this] {
+ record_t rec {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ };
+ auto r_size = record_group_size_t(rec.size, block_size);
+ auto record_total_size = r_size.get_encoded_length();
+
+ submit_record(std::move(rec));
+ while (is_available_size(record_total_size)) {
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ }
+
+ update_journal_tail(entries.back().get_abs_addr(), record_total_size);
+ ASSERT_EQ(get_records_total_size(),
+ get_records_available_size());
+
+ // will be appended at the begining of log
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+
+ while (is_available_size(record_total_size)) {
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ }
+ ASSERT_TRUE(record_total_size > get_records_available_size());
+ });
+}
+
+TEST_F(cbjournal_test_t, boudary_check_verify)
+{
+ run_async([this] {
+ record_t rec {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ };
+ auto r_size = record_group_size_t(rec.size, block_size);
+ auto record_total_size = r_size.get_encoded_length();
+ submit_record(std::move(rec));
+ while (is_available_size(record_total_size)) {
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ }
+
+ uint64_t avail = get_records_available_size();
+ // forward 2 recod size here because 1 block is reserved between head and tail
+ update_journal_tail(entries.front().get_abs_addr(), record_total_size * 2);
+ entries.erase(entries.begin());
+ entries.erase(entries.begin());
+ ASSERT_EQ(avail + (record_total_size * 2), get_records_available_size());
+ avail = get_records_available_size();
+ // will be appended at the begining of WAL
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ ASSERT_TRUE(avail - record_total_size >= get_records_available_size());
+ replay_and_check();
+ });
+}
+
+TEST_F(cbjournal_test_t, update_header)
+{
+ run_async([this] {
+ auto [header, _buf] = *(cbj->get_cjs().read_header().unsafe_get0());
+ record_t rec {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ };
+ auto r_size = record_group_size_t(rec.size, block_size);
+ auto record_total_size = r_size.get_encoded_length();
+ submit_record(std::move(rec));
+
+ update_journal_tail(entries.front().get_abs_addr(), record_total_size);
+ cbj->get_cjs().write_header().unsafe_get0();
+ auto [update_header, update_buf2] = *(cbj->get_cjs().read_header().unsafe_get0());
+ cbj->close().unsafe_get0();
+ replay().unsafe_get0();
+
+ ASSERT_EQ(update_header.dirty_tail.offset, update_header.dirty_tail.offset);
+ });
+}
+
+TEST_F(cbjournal_test_t, replay)
+{
+ run_async([this] {
+ record_t rec {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ };
+ auto r_size = record_group_size_t(rec.size, block_size);
+ auto record_total_size = r_size.get_encoded_length();
+ submit_record(std::move(rec));
+ while (is_available_size(record_total_size)) {
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ }
+ // will be appended at the begining of WAL
+ uint64_t avail = get_records_available_size();
+ update_journal_tail(entries.front().get_abs_addr(), record_total_size * 2);
+ entries.erase(entries.begin());
+ entries.erase(entries.begin());
+ ASSERT_EQ(avail + (record_total_size * 2), get_records_available_size());
+ avail = get_records_available_size();
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ ASSERT_TRUE(avail - record_total_size >= get_records_available_size());
+ cbj->close().unsafe_get0();
+ replay().unsafe_get0();
+ });
+}
+
+TEST_F(cbjournal_test_t, replay_after_reset)
+{
+ run_async([this] {
+ record_t rec {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ };
+ auto r_size = record_group_size_t(rec.size, block_size);
+ auto record_total_size = r_size.get_encoded_length();
+ submit_record(std::move(rec));
+ while (is_available_size(record_total_size)) {
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ }
+ auto old_written_to = get_written_to();
+ auto old_used_size = get_records_used_size();
+ set_written_to(
+ journal_seq_t{0,
+ convert_abs_addr_to_paddr(
+ cbj->get_records_start(),
+ cbj->get_device_id())});
+ cbj->close().unsafe_get0();
+ replay().unsafe_get0();
+ ASSERT_EQ(old_written_to, get_written_to());
+ ASSERT_EQ(old_used_size,
+ get_records_used_size());
+ });
+}
+
+TEST_F(cbjournal_test_t, multiple_submit_at_end)
+{
+ run_async([this] {
+ record_t rec {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ };
+ auto r_size = record_group_size_t(rec.size, block_size);
+ auto record_total_size = r_size.get_encoded_length();
+ submit_record(std::move(rec));
+ while (is_available_size(record_total_size)) {
+ submit_record(
+ record_t {
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(20), generate_delta(21) }
+ });
+ }
+ update_journal_tail(entries.front().get_abs_addr(), record_total_size * 8);
+ for (int i = 0; i < 8; i++) {
+ entries.erase(entries.begin());
+ }
+ seastar::parallel_for_each(
+ boost::make_counting_iterator(0u),
+ boost::make_counting_iterator(4u),
+ [&](auto) {
+ return seastar::async([&] {
+ auto writes = 0;
+ while (writes < 2) {
+ record_t rec {
+ { generate_extent(1) },
+ { generate_delta(20) } };
+ submit_record(std::move(rec));
+ writes++;
+ }
+ });
+ }).get0();
+ auto old_written_to = get_written_to();
+ cbj->close().unsafe_get0();
+ cbj->replay(
+ [](const auto &offsets,
+ const auto &e,
+ auto &dirty_seq,
+ auto &alloc_seq,
+ auto last_modified) {
+ return Journal::replay_ertr::make_ready_future<bool>(true);
+ }).unsafe_get0();
+ assert(get_written_to() == old_written_to);
+ });
+}
diff --git a/src/test/crimson/seastore/test_collection_manager.cc b/src/test/crimson/seastore/test_collection_manager.cc
new file mode 100644
index 000000000..cedcc5e8f
--- /dev/null
+++ b/src/test/crimson/seastore/test_collection_manager.cc
@@ -0,0 +1,195 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "os/ObjectStore.h"
+#include "test/crimson/gtest_seastar.h"
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+
+#include "crimson/os/seastore/cache.h"
+#include "crimson/os/seastore/transaction_manager.h"
+#include "crimson/os/seastore/segment_manager.h"
+#include "crimson/os/seastore/collection_manager.h"
+
+#include "test/crimson/seastore/test_block.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+
+#define TEST_COLL_FORWARD(METHOD) \
+ template <typename... Args> \
+ auto METHOD(coll_root_t &root, Transaction &t, Args&&... args) const { \
+ return with_trans_intr( \
+ t, \
+ [this](auto &t, auto &root, auto&&... args) { \
+ return collection_manager->METHOD( \
+ root, \
+ t, \
+ std::forward<decltype(args)>(args)...); \
+ }, \
+ root, \
+ std::forward<Args>(args)...).unsafe_get0(); \
+ }
+
+struct collection_manager_test_t :
+ public seastar_test_suite_t,
+ TMTestState {
+
+ CollectionManagerRef collection_manager;
+
+ collection_manager_test_t() {}
+
+ seastar::future<> set_up_fut() final {
+ return tm_setup().then([this] {
+ collection_manager = collection_manager::create_coll_manager(*tm);
+ return seastar::now();
+ });
+ }
+
+ seastar::future<> tear_down_fut() final {
+ return tm_teardown().then([this] {
+ collection_manager.reset();
+ return seastar::now();
+ });
+ }
+
+ using test_collection_t = std::map<coll_t, coll_info_t>;
+ test_collection_t test_coll_mappings;
+
+ void replay() {
+ restart();
+ collection_manager = collection_manager::create_coll_manager(*tm);
+ }
+
+ auto get_root() {
+ auto tref = create_mutate_transaction();
+ auto coll_root = with_trans_intr(
+ *tref,
+ [this](auto &t) {
+ return collection_manager->mkfs(t);
+ }).unsafe_get0();
+ submit_transaction(std::move(tref));
+ return coll_root;
+ }
+
+ TEST_COLL_FORWARD(remove)
+ TEST_COLL_FORWARD(list)
+ TEST_COLL_FORWARD(create)
+ TEST_COLL_FORWARD(update)
+
+ void checking_mappings(coll_root_t &coll_root, Transaction &t) {
+ auto coll_list = list(coll_root, t);
+ EXPECT_EQ(test_coll_mappings.size(), coll_list.size());
+ for (std::pair<coll_t, coll_info_t> p : test_coll_mappings) {
+ EXPECT_NE(
+ std::find(coll_list.begin(), coll_list.end(), p),
+ coll_list.end());
+ }
+ }
+
+ void checking_mappings(coll_root_t &coll_root) {
+ auto t = create_read_transaction();
+ checking_mappings(coll_root, *t);
+ }
+};
+
+TEST_P(collection_manager_test_t, basic)
+{
+ run_async([this] {
+ coll_root_t coll_root = get_root();
+ {
+ auto t = create_mutate_transaction();
+ for (int i = 0; i < 20; i++) {
+ coll_t cid(spg_t(pg_t(i+1,i+2), shard_id_t::NO_SHARD));
+ create(coll_root, *t, cid, coll_info_t(i));
+ test_coll_mappings.emplace(cid, coll_info_t(i));
+ }
+ checking_mappings(coll_root, *t);
+ submit_transaction(std::move(t));
+ EXPECT_EQ(test_coll_mappings.size(), 20);
+ }
+
+ replay();
+ checking_mappings(coll_root);
+ {
+ auto t = create_mutate_transaction();
+ for (auto iter = test_coll_mappings.begin();
+ iter != test_coll_mappings.end();) {
+ remove(coll_root, *t, iter->first);
+ iter = test_coll_mappings.erase(iter);
+ }
+ submit_transaction(std::move(t));
+ }
+ replay();
+ {
+ auto t = create_mutate_transaction();
+ auto list_ret = list(coll_root, *t);
+ submit_transaction(std::move(t));
+ EXPECT_EQ(list_ret.size(), test_coll_mappings.size());
+ }
+ });
+}
+
+TEST_P(collection_manager_test_t, overflow)
+{
+ run_async([this] {
+ coll_root_t coll_root = get_root();
+ auto old_location = coll_root.get_location();
+
+ auto t = create_mutate_transaction();
+ for (int i = 0; i < 412; i++) {
+ coll_t cid(spg_t(pg_t(i+1,i+2), shard_id_t::NO_SHARD));
+ create(coll_root, *t, cid, coll_info_t(i));
+ test_coll_mappings.emplace(cid, coll_info_t(i));
+ }
+ submit_transaction(std::move(t));
+ EXPECT_NE(old_location, coll_root.get_location());
+ checking_mappings(coll_root);
+
+ replay();
+ checking_mappings(coll_root);
+ });
+}
+
+TEST_P(collection_manager_test_t, update)
+{
+ run_async([this] {
+ coll_root_t coll_root = get_root();
+ {
+ auto t = create_mutate_transaction();
+ for (int i = 0; i < 2; i++) {
+ coll_t cid(spg_t(pg_t(1,i+1), shard_id_t::NO_SHARD));
+ create(coll_root, *t, cid, coll_info_t(i));
+ test_coll_mappings.emplace(cid, coll_info_t(i));
+ }
+ submit_transaction(std::move(t));
+ }
+ {
+ auto iter1= test_coll_mappings.begin();
+ auto iter2 = std::next(test_coll_mappings.begin(), 1);
+ EXPECT_NE(iter1->second.split_bits, iter2->second.split_bits);
+ auto t = create_mutate_transaction();
+ update(coll_root, *t, iter1->first, iter2->second);
+ submit_transaction(std::move(t));
+ iter1->second.split_bits = iter2->second.split_bits;
+ }
+ replay();
+ checking_mappings(coll_root);
+ });
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ collection_manager_test,
+ collection_manager_test_t,
+ ::testing::Values (
+ "segmented",
+ "circularbounded"
+ )
+);
diff --git a/src/test/crimson/seastore/test_extent_allocator.cc b/src/test/crimson/seastore/test_extent_allocator.cc
new file mode 100644
index 000000000..8217e5a66
--- /dev/null
+++ b/src/test/crimson/seastore/test_extent_allocator.cc
@@ -0,0 +1,181 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <random>
+
+#include <boost/iterator/counting_iterator.hpp>
+
+#include "test/crimson/gtest_seastar.h"
+#include "crimson/os/seastore/random_block_manager.h"
+#include "crimson/os/seastore/random_block_manager/extent_allocator.h"
+#include "crimson/os/seastore/random_block_manager/avlallocator.h"
+#include "include/interval_set.h"
+
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+struct allocator_test_t :
+ public seastar_test_suite_t,
+ ::testing::WithParamInterface<const char*> {
+ std::random_device rd;
+ std::mt19937 gen;
+ ExtentAllocatorRef allocator;
+
+ allocator_test_t()
+ : gen(rd()) {}
+
+ seastar::future<> set_up_fut() final {
+ std::string a_type = GetParam();
+ if (a_type == "avl") {
+ allocator.reset(new AvlAllocator(false));
+ return seastar::now();
+ }
+ ceph_assert(0 == "no support");
+ }
+ seastar::future<> tear_down_fut() final {
+ if (allocator) {
+ allocator->close();
+ }
+ return seastar::now();
+ }
+ void init_alloc(uint64_t block_size, uint64_t total_size) {
+ assert(allocator);
+ allocator->init(0, total_size, block_size);
+ }
+ void close() {
+ assert(allocator);
+ allocator->close();
+ }
+ auto allocate(size_t size) {
+ return allocator->alloc_extent(size);
+ }
+ void free(uint64_t start, uint64_t length) {
+ allocator->free_extent(start, length);
+ }
+ rbm_abs_addr get_random_addr(size_t block_size, size_t capacity) {
+ return block_size *
+ std::uniform_int_distribution<>(0, (capacity / block_size) - 1)(gen);
+ }
+};
+
+TEST_P(allocator_test_t, test_alloc_init)
+{
+ init_alloc(4096, 4096 * 64);
+ ASSERT_EQ((4096 * 64), allocator->get_available_size());
+ close();
+ init_alloc(8192, 8192 * 32);
+ allocate(8192);
+ ASSERT_EQ(8192 * 32 - 8192, allocator->get_available_size());
+ close();
+ init_alloc(4096, 4096 * 128);
+ allocate(8192);
+ ASSERT_EQ(4096 * 128 - 8192, allocator->get_available_size());
+}
+
+TEST_P(allocator_test_t, test_init_alloc_free)
+{
+ uint64_t block_size = 4096;
+ uint64_t capacity = 4 * 1024 * block_size;
+
+ {
+ init_alloc(block_size, capacity);
+
+ auto free_length = allocator->get_available_size();
+ allocate(allocator->get_max_alloc_size());
+ ASSERT_EQ(free_length - allocator->get_max_alloc_size(),
+ allocator->get_available_size());
+
+ free(0, allocator->get_max_alloc_size());
+ ASSERT_EQ(free_length, allocator->get_available_size());
+ }
+}
+
+TEST_P(allocator_test_t, test_alloc_failure)
+{
+ uint64_t block_size = 8192;
+ uint64_t capacity = 1024 * block_size;
+
+ {
+ init_alloc(block_size, capacity);
+ allocator->mark_extent_used(0, block_size * 256);
+ allocator->mark_extent_used(block_size * 512, block_size * 256);
+
+ auto result = allocate(block_size * 512);
+ ASSERT_EQ(false, result.has_value());
+
+ free(0, block_size * 256);
+ allocator->mark_extent_used(0, block_size * 512);
+
+ result = allocate(block_size * 512);
+ ASSERT_EQ(false, result.has_value());
+ }
+}
+
+TEST_P(allocator_test_t, test_random_alloc_verify)
+{
+ uint64_t block_size = 4096;
+ uint64_t capacity = 64 * 1024 * block_size;
+ uint64_t avail = capacity;
+ interval_set<rbm_abs_addr> alloc_map;
+ init_alloc(block_size, capacity);
+
+ {
+ for (int i = 0; i < 256; i++) {
+ auto addr = get_random_addr(block_size, capacity);
+ auto size = get_random_addr(block_size, capacity) % (4 << 20);
+ if (addr + size > capacity || size == 0 ||
+ alloc_map.intersects(addr, size) ) continue;
+ allocator->mark_extent_used(addr, size);
+ alloc_map.insert(addr, size);
+ avail -= size;
+ }
+ ASSERT_EQ(avail, allocator->get_available_size());
+
+ for (auto p : alloc_map) {
+ free(p.first, p.second);
+ avail += p.second;
+ alloc_map.erase(p.first, p.second);
+ ASSERT_EQ(avail, allocator->get_available_size());
+ }
+ ASSERT_EQ(capacity, allocator->get_available_size());
+
+ for (int i = 0; i < 100; i++) {
+ auto addr = get_random_addr(block_size, capacity);
+ auto size = get_random_addr(block_size, capacity) % (4 << 20);
+ if (addr + size > capacity || size == 0 ||
+ alloc_map.intersects(addr, size) ) continue;
+ allocator->mark_extent_used(addr, size);
+ alloc_map.insert(addr, size);
+ avail -= size;
+ }
+
+ for (int i = 0; i < 50; i++) {
+ free((*alloc_map.begin()).first, (*alloc_map.begin()).second);
+ avail += (*alloc_map.begin()).second;
+ alloc_map.erase((*alloc_map.begin()).first, (*alloc_map.begin()).second);
+ ASSERT_EQ(avail, allocator->get_available_size());
+
+ auto addr = get_random_addr(block_size, capacity);
+ auto size = get_random_addr(block_size, capacity) % (4 << 20);
+ if (addr + size > capacity || size == 0 ||
+ alloc_map.intersects(addr, size) ) continue;
+ allocator->mark_extent_used(addr, size);
+ alloc_map.insert(addr, size);
+ avail -= size;
+ }
+ ASSERT_EQ(avail, allocator->get_available_size());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ allocator_test,
+ allocator_test_t,
+ ::testing::Values("avl"));
diff --git a/src/test/crimson/seastore/test_object_data_handler.cc b/src/test/crimson/seastore/test_object_data_handler.cc
new file mode 100644
index 000000000..6510cb5d9
--- /dev/null
+++ b/src/test/crimson/seastore/test_object_data_handler.cc
@@ -0,0 +1,431 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+
+#include "crimson/os/seastore/onode.h"
+#include "crimson/os/seastore/object_data_handler.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+#define MAX_OBJECT_SIZE (16<<20)
+#define DEFAULT_OBJECT_DATA_RESERVATION (16<<20)
+#define DEFAULT_OBJECT_METADATA_RESERVATION (16<<20)
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+class TestOnode final : public Onode {
+ onode_layout_t layout;
+ bool dirty = false;
+
+public:
+ TestOnode(uint32_t ddr, uint32_t dmr) : Onode(ddr, dmr) {}
+ const onode_layout_t &get_layout() const final {
+ return layout;
+ }
+ onode_layout_t &get_mutable_layout(Transaction &t) final {
+ dirty = true;
+ return layout;
+ }
+ bool is_alive() const {
+ return true;
+ }
+ bool is_dirty() const { return dirty; }
+ laddr_t get_hint() const final {return L_ADDR_MIN; }
+ ~TestOnode() final = default;
+};
+
+struct object_data_handler_test_t:
+ public seastar_test_suite_t,
+ TMTestState {
+ OnodeRef onode;
+
+ bufferptr known_contents;
+ extent_len_t size = 0;
+
+ object_data_handler_test_t() {}
+
+ void write(Transaction &t, objaddr_t offset, extent_len_t len, char fill) {
+ ceph_assert(offset + len <= known_contents.length());
+ size = std::max<extent_len_t>(size, offset + len);
+ memset(
+ known_contents.c_str() + offset,
+ fill,
+ len);
+ bufferlist bl;
+ bl.append(
+ bufferptr(
+ known_contents,
+ offset,
+ len));
+ with_trans_intr(t, [&](auto &t) {
+ return ObjectDataHandler(MAX_OBJECT_SIZE).write(
+ ObjectDataHandler::context_t{
+ *tm,
+ t,
+ *onode,
+ },
+ offset,
+ bl);
+ }).unsafe_get0();
+ }
+ void write(objaddr_t offset, extent_len_t len, char fill) {
+ auto t = create_mutate_transaction();
+ write(*t, offset, len, fill);
+ return submit_transaction(std::move(t));
+ }
+
+ void truncate(Transaction &t, objaddr_t offset) {
+ if (size > offset) {
+ memset(
+ known_contents.c_str() + offset,
+ 0,
+ size - offset);
+ with_trans_intr(t, [&](auto &t) {
+ return ObjectDataHandler(MAX_OBJECT_SIZE).truncate(
+ ObjectDataHandler::context_t{
+ *tm,
+ t,
+ *onode
+ },
+ offset);
+ }).unsafe_get0();
+ }
+ size = offset;
+ }
+ void truncate(objaddr_t offset) {
+ auto t = create_mutate_transaction();
+ truncate(*t, offset);
+ return submit_transaction(std::move(t));
+ }
+
+ void read(Transaction &t, objaddr_t offset, extent_len_t len) {
+ bufferlist bl = with_trans_intr(t, [&](auto &t) {
+ return ObjectDataHandler(MAX_OBJECT_SIZE).read(
+ ObjectDataHandler::context_t{
+ *tm,
+ t,
+ *onode
+ },
+ offset,
+ len);
+ }).unsafe_get0();
+ bufferlist known;
+ known.append(
+ bufferptr(
+ known_contents,
+ offset,
+ len));
+ EXPECT_EQ(bl.length(), known.length());
+ EXPECT_EQ(bl, known);
+ }
+ void read(objaddr_t offset, extent_len_t len) {
+ auto t = create_read_transaction();
+ read(*t, offset, len);
+ }
+ void read_near(objaddr_t offset, extent_len_t len, extent_len_t fuzz) {
+ auto fuzzes = std::vector<int32_t>{-1 * (int32_t)fuzz, 0, (int32_t)fuzz};
+ for (auto left_fuzz : fuzzes) {
+ for (auto right_fuzz : fuzzes) {
+ read(offset + left_fuzz, len - left_fuzz + right_fuzz);
+ }
+ }
+ }
+ std::list<LBAMappingRef> get_mappings(objaddr_t offset, extent_len_t length) {
+ auto t = create_mutate_transaction();
+ auto ret = with_trans_intr(*t, [&](auto &t) {
+ return tm->get_pins(t, offset, length);
+ }).unsafe_get0();
+ return ret;
+ }
+
+ seastar::future<> set_up_fut() final {
+ onode = new TestOnode(
+ DEFAULT_OBJECT_DATA_RESERVATION,
+ DEFAULT_OBJECT_METADATA_RESERVATION);
+ known_contents = buffer::create(4<<20 /* 4MB */);
+ memset(known_contents.c_str(), 0, known_contents.length());
+ size = 0;
+ return tm_setup();
+ }
+
+ seastar::future<> tear_down_fut() final {
+ onode.reset();
+ size = 0;
+ return tm_teardown();
+ }
+};
+
+TEST_P(object_data_handler_test_t, single_write)
+{
+ run_async([this] {
+ write(1<<20, 8<<10, 'c');
+
+ read_near(1<<20, 8<<10, 1);
+ read_near(1<<20, 8<<10, 512);
+ });
+}
+
+TEST_P(object_data_handler_test_t, multi_write)
+{
+ run_async([this] {
+ write((1<<20) - (4<<10), 4<<10, 'a');
+ write(1<<20, 4<<10, 'b');
+ write((1<<20) + (4<<10), 4<<10, 'c');
+
+ read_near(1<<20, 4<<10, 1);
+ read_near(1<<20, 4<<10, 512);
+
+ read_near((1<<20)-(4<<10), 12<<10, 1);
+ read_near((1<<20)-(4<<10), 12<<10, 512);
+ });
+}
+
+TEST_P(object_data_handler_test_t, write_hole)
+{
+ run_async([this] {
+ write((1<<20) - (4<<10), 4<<10, 'a');
+ // hole at 1<<20
+ write((1<<20) + (4<<10), 4<<10, 'c');
+
+ read_near(1<<20, 4<<10, 1);
+ read_near(1<<20, 4<<10, 512);
+
+ read_near((1<<20)-(4<<10), 12<<10, 1);
+ read_near((1<<20)-(4<<10), 12<<10, 512);
+ });
+}
+
+TEST_P(object_data_handler_test_t, overwrite_single)
+{
+ run_async([this] {
+ write((1<<20), 4<<10, 'a');
+ write((1<<20), 4<<10, 'c');
+
+ read_near(1<<20, 4<<10, 1);
+ read_near(1<<20, 4<<10, 512);
+ });
+}
+
+TEST_P(object_data_handler_test_t, overwrite_double)
+{
+ run_async([this] {
+ write((1<<20), 4<<10, 'a');
+ write((1<<20)+(4<<10), 4<<10, 'c');
+ write((1<<20), 8<<10, 'b');
+
+ read_near(1<<20, 8<<10, 1);
+ read_near(1<<20, 8<<10, 512);
+
+ read_near(1<<20, 4<<10, 1);
+ read_near(1<<20, 4<<10, 512);
+
+ read_near((1<<20) + (4<<10), 4<<10, 1);
+ read_near((1<<20) + (4<<10), 4<<10, 512);
+ });
+}
+
+TEST_P(object_data_handler_test_t, overwrite_partial)
+{
+ run_async([this] {
+ write((1<<20), 12<<10, 'a');
+ read_near(1<<20, 12<<10, 1);
+
+ write((1<<20)+(8<<10), 4<<10, 'b');
+ read_near(1<<20, 12<<10, 1);
+
+ write((1<<20)+(4<<10), 4<<10, 'c');
+ read_near(1<<20, 12<<10, 1);
+
+ write((1<<20), 4<<10, 'd');
+
+ read_near(1<<20, 12<<10, 1);
+ read_near(1<<20, 12<<10, 512);
+
+ read_near(1<<20, 4<<10, 1);
+ read_near(1<<20, 4<<10, 512);
+
+ read_near((1<<20) + (4<<10), 4<<10, 1);
+ read_near((1<<20) + (4<<10), 4<<10, 512);
+ });
+}
+
+TEST_P(object_data_handler_test_t, unaligned_write)
+{
+ run_async([this] {
+ objaddr_t base = 1<<20;
+ write(base, (4<<10)+(1<<10), 'a');
+ read_near(base-(4<<10), 12<<10, 512);
+
+ base = (1<<20) + (64<<10);
+ write(base+(1<<10), (4<<10)+(1<<10), 'b');
+ read_near(base-(4<<10), 12<<10, 512);
+
+ base = (1<<20) + (128<<10);
+ write(base-(1<<10), (4<<10)+(2<<20), 'c');
+ read_near(base-(4<<10), 12<<10, 512);
+ });
+}
+
+TEST_P(object_data_handler_test_t, unaligned_overwrite)
+{
+ run_async([this] {
+ objaddr_t base = 1<<20;
+ write(base, (128<<10) + (16<<10), 'x');
+
+ write(base, (4<<10)+(1<<10), 'a');
+ read_near(base-(4<<10), 12<<10, 2<<10);
+
+ base = (1<<20) + (64<<10);
+ write(base+(1<<10), (4<<10)+(1<<10), 'b');
+ read_near(base-(4<<10), 12<<10, 2<<10);
+
+ base = (1<<20) + (128<<10);
+ write(base-(1<<10), (4<<10)+(2<<20), 'c');
+ read_near(base-(4<<10), 12<<10, 2<<10);
+
+ read(base, (128<<10) + (16<<10));
+ });
+}
+
+TEST_P(object_data_handler_test_t, truncate)
+{
+ run_async([this] {
+ objaddr_t base = 1<<20;
+ write(base, 8<<10, 'a');
+ write(base+(8<<10), 8<<10, 'b');
+ write(base+(16<<10), 8<<10, 'c');
+
+ truncate(base + (32<<10));
+ read(base, 64<<10);
+
+ truncate(base + (24<<10));
+ read(base, 64<<10);
+
+ truncate(base + (12<<10));
+ read(base, 64<<10);
+
+ truncate(base - (12<<10));
+ read(base, 64<<10);
+ });
+}
+
+TEST_P(object_data_handler_test_t, no_split) {
+ run_async([this] {
+ write(0, 8<<10, 'x');
+ write(0, 8<<10, 'a');
+
+ auto pins = get_mappings(0, 8<<10);
+ EXPECT_EQ(pins.size(), 1);
+
+ read(0, 8<<10);
+ });
+}
+
+TEST_P(object_data_handler_test_t, split_left) {
+ run_async([this] {
+ write(0, 128<<10, 'x');
+
+ write(64<<10, 60<<10, 'a');
+
+ auto pins = get_mappings(0, 128<<10);
+ EXPECT_EQ(pins.size(), 2);
+
+ size_t res[2] = {0, 64<<10};
+ auto base = pins.front()->get_key();
+ int i = 0;
+ for (auto &pin : pins) {
+ EXPECT_EQ(pin->get_key() - base, res[i]);
+ i++;
+ }
+ read(0, 128<<10);
+ });
+}
+
+TEST_P(object_data_handler_test_t, split_right) {
+ run_async([this] {
+ write(0, 128<<10, 'x');
+ write(4<<10, 60<<10, 'a');
+
+ auto pins = get_mappings(0, 128<<10);
+ EXPECT_EQ(pins.size(), 2);
+
+ size_t res[2] = {0, 64<<10};
+ auto base = pins.front()->get_key();
+ int i = 0;
+ for (auto &pin : pins) {
+ EXPECT_EQ(pin->get_key() - base, res[i]);
+ i++;
+ }
+ read(0, 128<<10);
+ });
+}
+TEST_P(object_data_handler_test_t, split_left_right) {
+ run_async([this] {
+ write(0, 128<<10, 'x');
+ write(48<<10, 32<<10, 'a');
+
+ auto pins = get_mappings(0, 128<<10);
+ EXPECT_EQ(pins.size(), 3);
+
+ size_t res[3] = {0, 48<<10, 80<<10};
+ auto base = pins.front()->get_key();
+ int i = 0;
+ for (auto &pin : pins) {
+ EXPECT_EQ(pin->get_key() - base, res[i]);
+ i++;
+ }
+ });
+}
+TEST_P(object_data_handler_test_t, multiple_split) {
+ run_async([this] {
+ write(0, 128<<10, 'x');
+
+ auto t = create_mutate_transaction();
+ // normal split
+ write(*t, 120<<10, 4<<10, 'a');
+ // not aligned right
+ write(*t, 4<<10, 5<<10, 'b');
+ // split right extent of last split result
+ write(*t, 32<<10, 4<<10, 'c');
+ // non aligned overwrite
+ write(*t, 13<<10, 4<<10, 'd');
+
+ write(*t, 64<<10, 32<<10, 'e');
+ // not split right
+ write(*t, 60<<10, 8<<10, 'f');
+
+ submit_transaction(std::move(t));
+
+ auto pins = get_mappings(0, 128<<10);
+ EXPECT_EQ(pins.size(), 10);
+
+ size_t res[10] = {0, 4<<10, 12<<10, 20<<10, 32<<10,
+ 36<<10, 60<<10, 96<<10, 120<<10, 124<<10};
+ auto base = pins.front()->get_key();
+ int i = 0;
+ for (auto &pin : pins) {
+ EXPECT_EQ(pin->get_key() - base, res[i]);
+ i++;
+ }
+ read(0, 128<<10);
+ });
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ object_data_handler_test,
+ object_data_handler_test_t,
+ ::testing::Values (
+ "segmented",
+ "circularbounded"
+ )
+);
+
+
diff --git a/src/test/crimson/seastore/test_omap_manager.cc b/src/test/crimson/seastore/test_omap_manager.cc
new file mode 100644
index 000000000..ab2218565
--- /dev/null
+++ b/src/test/crimson/seastore/test_omap_manager.cc
@@ -0,0 +1,730 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+
+#include "crimson/os/seastore/cache.h"
+#include "crimson/os/seastore/transaction_manager.h"
+#include "crimson/os/seastore/segment_manager.h"
+#include "crimson/os/seastore/omap_manager.h"
+
+#include "test/crimson/seastore/test_block.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+using namespace std;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+const int STR_LEN = 50;
+
+std::string rand_name(const int len)
+{
+ std::string ret;
+ ret.reserve(len);
+ for (int i = 0; i < len; ++i) {
+ ret.append(1, (char)(rand() % ('z' - '0')) + '0');
+ }
+ return ret;
+}
+
+bufferlist rand_buffer(const int len) {
+ bufferptr ptr(len);
+ for (auto i = ptr.c_str(); i < ptr.c_str() + len; ++i) {
+ *i = (char)rand();
+ }
+ bufferlist bl;
+ bl.append(ptr);
+ return bl;
+}
+
+struct omap_manager_test_t :
+ public seastar_test_suite_t,
+ TMTestState {
+
+ OMapManagerRef omap_manager;
+
+ omap_manager_test_t() {}
+
+ seastar::future<> set_up_fut() final {
+ return tm_setup().then([this] {
+ omap_manager = omap_manager::create_omap_manager(*tm);
+ return seastar::now();
+ });
+ }
+
+ seastar::future<> tear_down_fut() final {
+ return tm_teardown().then([this] {
+ omap_manager.reset();
+ return seastar::now();
+ });
+ }
+
+ using test_omap_t = std::map<std::string, ceph::bufferlist>;
+ test_omap_t test_omap_mappings;
+
+ void set_key(
+ omap_root_t &omap_root,
+ Transaction &t,
+ const string &key,
+ const bufferlist &val) {
+ with_trans_intr(
+ t,
+ [&, this](auto &t) {
+ return omap_manager->omap_set_key(omap_root, t, key, val);
+ }).unsafe_get0();
+ test_omap_mappings[key] = val;
+ }
+
+ void set_key(
+ omap_root_t &omap_root,
+ Transaction &t,
+ const string &key,
+ const string &val) {
+ bufferlist bl;
+ bl.append(val);
+ set_key(omap_root, t, key, bl);
+ }
+
+ std::string set_random_key(
+ omap_root_t &omap_root,
+ Transaction &t) {
+ auto key = rand_name(STR_LEN);
+ set_key(
+ omap_root,
+ t,
+ key,
+ rand_buffer(STR_LEN));
+ return key;
+ }
+
+ void get_value(
+ omap_root_t &omap_root,
+ Transaction &t,
+ const string &key) {
+ auto ret = with_trans_intr(
+ t,
+ [&, this](auto &t) {
+ return omap_manager->omap_get_value(omap_root, t, key);
+ }).unsafe_get0();
+ auto iter = test_omap_mappings.find(key);
+ if (iter == test_omap_mappings.end()) {
+ EXPECT_FALSE(ret);
+ } else {
+ EXPECT_TRUE(ret);
+ if (ret) {
+ EXPECT_TRUE(*ret == iter->second);
+ }
+ }
+ }
+
+ void rm_key(
+ omap_root_t &omap_root,
+ Transaction &t,
+ const string &key) {
+ with_trans_intr(
+ t,
+ [&, this](auto &t) {
+ return omap_manager->omap_rm_key(omap_root, t, key);
+ }).unsafe_get0();
+ test_omap_mappings.erase(test_omap_mappings.find(key));
+ }
+
+ std::vector<std::string> rm_key_range(
+ omap_root_t &omap_root,
+ Transaction &t,
+ const std::string &first,
+ const std::string &last) {
+ logger().debug("rm keys in range {} ~ {}", first, last);
+ auto config = OMapManager::omap_list_config_t()
+ .with_max(3000)
+ .with_inclusive(true, false);
+
+ with_trans_intr(
+ t,
+ [&, this](auto &t) {
+ return omap_manager->omap_rm_key_range(
+ omap_root, t, first, last, config);
+ }).unsafe_get0();
+
+ std::vector<std::string> keys;
+ size_t count = 0;
+ for (auto iter = test_omap_mappings.begin();
+ iter != test_omap_mappings.end(); ) {
+ if (iter->first >= first && iter->first < last) {
+ keys.push_back(iter->first);
+ iter = test_omap_mappings.erase(iter);
+ count++;
+ } else {
+ iter++;
+ }
+ if (count == config.max_result_size) {
+ break;
+ }
+ }
+ return keys;
+ }
+
+ void list(
+ const omap_root_t &omap_root,
+ Transaction &t,
+ const std::optional<std::string> &first,
+ const std::optional<std::string> &last,
+ size_t max = 128,
+ bool inclusive = false) {
+
+ if (first && last) {
+ logger().debug("list on {} ~ {}", *first, *last);
+ } else if (first) {
+ logger().debug("list on {} ~ end", *first);
+ } else if (last) {
+ logger().debug("list on start ~ {}", *last);
+ } else {
+ logger().debug("list on start ~ end");
+ }
+
+ auto config = OMapManager::omap_list_config_t()
+ .with_max(max)
+ .with_inclusive(inclusive, false);
+
+ auto [complete, results] = with_trans_intr(
+ t,
+ [&, this](auto &t) {
+ return omap_manager->omap_list(omap_root, t, first, last, config);
+ }).unsafe_get0();
+
+ test_omap_t::iterator it, lit;
+ if (first) {
+ it = config.first_inclusive ?
+ test_omap_mappings.lower_bound(*first) :
+ test_omap_mappings.upper_bound(*first);
+ } else {
+ it = test_omap_mappings.begin();
+ }
+ if (last) {
+ lit = config.last_inclusive ?
+ test_omap_mappings.upper_bound(*last) :
+ test_omap_mappings.lower_bound(*last);
+ } else {
+ lit = test_omap_mappings.end();
+ }
+
+ for (auto &&[k, v]: results) {
+ EXPECT_NE(it, test_omap_mappings.end());
+ if (it == test_omap_mappings.end()) {
+ return;
+ }
+ EXPECT_EQ(k, it->first);
+ EXPECT_EQ(v, it->second);
+ it++;
+ }
+ if (it == lit) {
+ EXPECT_TRUE(complete);
+ } else {
+ EXPECT_EQ(results.size(), max);
+ }
+ }
+
+ void clear(
+ omap_root_t &omap_root,
+ Transaction &t) {
+ with_trans_intr(
+ t,
+ [&, this](auto &t) {
+ return omap_manager->omap_clear(omap_root, t);
+ }).unsafe_get0();
+ EXPECT_EQ(omap_root.get_location(), L_ADDR_NULL);
+ }
+
+ void check_mappings(omap_root_t &omap_root, Transaction &t) {
+ for (const auto &i: test_omap_mappings){
+ get_value(omap_root, t, i.first);
+ }
+ }
+
+ void check_mappings(omap_root_t &omap_root) {
+ auto t = create_read_transaction();
+ check_mappings(omap_root, *t);
+ }
+
+ std::vector<std::string> get_mapped_keys() {
+ std::vector<std::string> mkeys;
+ mkeys.reserve(test_omap_mappings.size());
+ for (auto &k: test_omap_mappings) {
+ mkeys.push_back(k.first);
+ }
+ return mkeys;
+ }
+
+ void replay() {
+ restart();
+ omap_manager = omap_manager::create_omap_manager(*tm);
+ }
+
+ auto initialize() {
+ auto t = create_mutate_transaction();
+ omap_root_t omap_root = with_trans_intr(
+ *t,
+ [this](auto &t) {
+ return omap_manager->initialize_omap(t, L_ADDR_MIN);
+ }).unsafe_get0();
+ submit_transaction(std::move(t));
+ return omap_root;
+ }
+};
+
+TEST_P(omap_manager_test_t, basic)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ string key = "owner";
+ string val = "test";
+
+ {
+ auto t = create_mutate_transaction();
+ logger().debug("first transaction");
+ set_key(omap_root, *t, key, val);
+ get_value(omap_root, *t, key);
+ submit_transaction(std::move(t));
+ }
+ {
+ auto t = create_mutate_transaction();
+ logger().debug("second transaction");
+ get_value(omap_root, *t, key);
+ rm_key(omap_root, *t, key);
+ get_value(omap_root, *t, key);
+ submit_transaction(std::move(t));
+ }
+ {
+ auto t = create_mutate_transaction();
+ logger().debug("third transaction");
+ get_value(omap_root, *t, key);
+ submit_transaction(std::move(t));
+ }
+ });
+}
+
+TEST_P(omap_manager_test_t, force_leafnode_split)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ for (unsigned i = 0; i < 40; i++) {
+ auto t = create_mutate_transaction();
+ logger().debug("opened transaction");
+ for (unsigned j = 0; j < 10; ++j) {
+ set_random_key(omap_root, *t);
+ if ((i % 20 == 0) && (j == 5)) {
+ check_mappings(omap_root, *t);
+ }
+ }
+ logger().debug("force split submit transaction i = {}", i);
+ submit_transaction(std::move(t));
+ check_mappings(omap_root);
+ }
+ });
+}
+
+TEST_P(omap_manager_test_t, force_leafnode_split_merge)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ for (unsigned i = 0; i < 80; i++) {
+ auto t = create_mutate_transaction();
+ logger().debug("opened split_merge transaction");
+ for (unsigned j = 0; j < 5; ++j) {
+ set_random_key(omap_root, *t);
+ if ((i % 10 == 0) && (j == 3)) {
+ check_mappings(omap_root, *t);
+ }
+ }
+ logger().debug("submitting transaction");
+ submit_transaction(std::move(t));
+ if (i % 50 == 0) {
+ check_mappings(omap_root);
+ }
+ }
+ auto mkeys = get_mapped_keys();
+ auto t = create_mutate_transaction();
+ for (unsigned i = 0; i < mkeys.size(); i++) {
+ if (i % 3 != 0) {
+ rm_key(omap_root, *t, mkeys[i]);
+ }
+
+ if (i % 10 == 0) {
+ logger().debug("submitting transaction i= {}", i);
+ submit_transaction(std::move(t));
+ t = create_mutate_transaction();
+ }
+ if (i % 100 == 0) {
+ logger().debug("check_mappings i= {}", i);
+ check_mappings(omap_root, *t);
+ check_mappings(omap_root);
+ }
+ }
+ logger().debug("finally submitting transaction ");
+ submit_transaction(std::move(t));
+ });
+}
+
+TEST_P(omap_manager_test_t, force_leafnode_split_merge_fullandbalanced)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ for (unsigned i = 0; i < 50; i++) {
+ auto t = create_mutate_transaction();
+ logger().debug("opened split_merge transaction");
+ for (unsigned j = 0; j < 5; ++j) {
+ set_random_key(omap_root, *t);
+ if ((i % 10 == 0) && (j == 3)) {
+ check_mappings(omap_root, *t);
+ }
+ }
+ logger().debug("submitting transaction");
+ submit_transaction(std::move(t));
+ if (i % 50 == 0) {
+ check_mappings(omap_root);
+ }
+ }
+ auto mkeys = get_mapped_keys();
+ auto t = create_mutate_transaction();
+ for (unsigned i = 0; i < mkeys.size(); i++) {
+ if (30 < i && i < 100) {
+ rm_key(omap_root, *t, mkeys[i]);
+ }
+
+ if (i % 10 == 0) {
+ logger().debug("submitting transaction i= {}", i);
+ submit_transaction(std::move(t));
+ t = create_mutate_transaction();
+ }
+ if (i % 50 == 0) {
+ logger().debug("check_mappings i= {}", i);
+ check_mappings(omap_root, *t);
+ check_mappings(omap_root);
+ }
+ if (i == 100) {
+ break;
+ }
+ }
+ logger().debug("finally submitting transaction ");
+ submit_transaction(std::move(t));
+ check_mappings(omap_root);
+ });
+}
+
+TEST_P(omap_manager_test_t, force_split_listkeys_list_rmkey_range_clear)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ string first, last;
+ for (unsigned i = 0; i < 40; i++) {
+ auto t = create_mutate_transaction();
+ logger().debug("opened transaction");
+ for (unsigned j = 0; j < 10; ++j) {
+ auto key = set_random_key(omap_root, *t);
+ if (i == 10) {
+ first = key;
+ }
+ if (i == 30) {
+ last = key;
+ if (first > last) {
+ std::swap(first, last);
+ }
+ }
+ if ((i % 20 == 0) && (j == 5)) {
+ check_mappings(omap_root, *t);
+ }
+ }
+ logger().debug("force split submit transaction i = {}", i);
+ submit_transaction(std::move(t));
+ check_mappings(omap_root);
+ }
+
+ std::optional<std::string> first_temp;
+ std::optional<std::string> last_temp;
+ {
+ auto t = create_read_transaction();
+ first_temp = std::nullopt;
+ last_temp = std::nullopt;
+ list(omap_root, *t, first_temp, last_temp);
+ }
+
+ {
+ auto t = create_read_transaction();
+ first_temp = first;
+ last_temp = std::nullopt;
+ list(omap_root, *t, first_temp, last_temp, 100);
+ }
+
+ {
+ auto t = create_read_transaction();
+ first_temp = first;
+ last_temp = std::nullopt;
+ list(omap_root, *t, first_temp, last_temp, 100, true);
+ }
+
+ {
+ auto t = create_read_transaction();
+ first_temp = std::nullopt;
+ last_temp = last;
+ list(omap_root, *t, first_temp, last_temp, 10240);
+ }
+
+ {
+ auto t = create_read_transaction();
+ first_temp = first;
+ last_temp = last;
+ list(omap_root, *t, first_temp, last_temp, 10240, true);
+ }
+
+ {
+ auto t = create_read_transaction();
+ list(omap_root, *t, first, last, 10240, true);
+ }
+
+ {
+ auto t = create_mutate_transaction();
+ auto keys = rm_key_range(omap_root, *t, first, last);
+ for (const auto& key : keys) {
+ get_value(omap_root, *t, key);
+ }
+ submit_transaction(std::move(t));
+ }
+
+ {
+ auto t = create_mutate_transaction();
+ clear(omap_root, *t);
+ submit_transaction(std::move(t));
+ }
+ });
+}
+
+TEST_P(omap_manager_test_t, force_inner_node_split_list_rmkey_range)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ string first = "";
+ string last;
+ while (cache->get_omap_tree_depth() < 3) {
+ for (unsigned i = 0; i < 40; i++) {
+ auto t = create_mutate_transaction();
+ logger().debug("opened transaction");
+ for (unsigned j = 0; j < 10; ++j) {
+ auto key = set_random_key(omap_root, *t);
+ if (key.compare(first) < 0 || !first.length()) {
+ first = key;
+ }
+ if (i == 10) {
+ last = key;
+ }
+ }
+ logger().debug("force split submit transaction i = {}", i);
+ submit_transaction(std::move(t));
+ }
+ }
+
+ std::optional<std::string> first_temp;
+ std::optional<std::string> last_temp;
+ {
+ auto t = create_read_transaction();
+ first_temp = first;
+ last_temp = std::nullopt;
+ list(omap_root, *t, first_temp, last_temp, 10240);
+ }
+
+ {
+ auto t = create_read_transaction();
+ first_temp = first;
+ last_temp = std::nullopt;
+ list(omap_root, *t, first_temp, last_temp, 10240, true);
+ }
+
+ {
+ auto t = create_read_transaction();
+ first_temp = std::nullopt;
+ last_temp = last;
+ list(omap_root, *t, first_temp, last_temp, 10240);
+ }
+
+ {
+ auto t = create_read_transaction();
+ first_temp = first;
+ last_temp = last;
+ list(omap_root, *t, first_temp, last_temp, 10240, true);
+ }
+
+ {
+ auto t = create_mutate_transaction();
+ auto keys = rm_key_range(omap_root, *t, first, last);
+ for (const auto& key : keys) {
+ get_value(omap_root, *t, key);
+ }
+ submit_transaction(std::move(t));
+ }
+
+ {
+ auto t = create_mutate_transaction();
+ clear(omap_root, *t);
+ submit_transaction(std::move(t));
+ }
+ });
+}
+
+
+TEST_P(omap_manager_test_t, internal_force_split)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ for (unsigned i = 0; i < 10; i++) {
+ logger().debug("opened split transaction");
+ auto t = create_mutate_transaction();
+
+ for (unsigned j = 0; j < 80; ++j) {
+ set_random_key(omap_root, *t);
+ if ((i % 2 == 0) && (j % 50 == 0)) {
+ check_mappings(omap_root, *t);
+ }
+ }
+ logger().debug("submitting transaction i = {}", i);
+ submit_transaction(std::move(t));
+ }
+ check_mappings(omap_root);
+ });
+}
+
+TEST_P(omap_manager_test_t, internal_force_merge_fullandbalanced)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ for (unsigned i = 0; i < 8; i++) {
+ logger().debug("opened split transaction");
+ auto t = create_mutate_transaction();
+
+ for (unsigned j = 0; j < 80; ++j) {
+ set_random_key(omap_root, *t);
+ if ((i % 2 == 0) && (j % 50 == 0)) {
+ check_mappings(omap_root, *t);
+ }
+ }
+ logger().debug("submitting transaction");
+ submit_transaction(std::move(t));
+ }
+ auto mkeys = get_mapped_keys();
+ auto t = create_mutate_transaction();
+ for (unsigned i = 0; i < mkeys.size(); i++) {
+ rm_key(omap_root, *t, mkeys[i]);
+
+ if (i % 10 == 0) {
+ logger().debug("submitting transaction i= {}", i);
+ submit_transaction(std::move(t));
+ t = create_mutate_transaction();
+ }
+ if (i % 50 == 0) {
+ logger().debug("check_mappings i= {}", i);
+ check_mappings(omap_root, *t);
+ check_mappings(omap_root);
+ }
+ }
+ logger().debug("finally submitting transaction ");
+ submit_transaction(std::move(t));
+ check_mappings(omap_root);
+ });
+}
+
+TEST_P(omap_manager_test_t, replay)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ for (unsigned i = 0; i < 8; i++) {
+ logger().debug("opened split transaction");
+ auto t = create_mutate_transaction();
+
+ for (unsigned j = 0; j < 80; ++j) {
+ set_random_key(omap_root, *t);
+ if ((i % 2 == 0) && (j % 50 == 0)) {
+ check_mappings(omap_root, *t);
+ }
+ }
+ logger().debug("submitting transaction i = {}", i);
+ submit_transaction(std::move(t));
+ }
+ replay();
+ check_mappings(omap_root);
+
+ auto mkeys = get_mapped_keys();
+ auto t = create_mutate_transaction();
+ for (unsigned i = 0; i < mkeys.size(); i++) {
+ rm_key(omap_root, *t, mkeys[i]);
+
+ if (i % 10 == 0) {
+ logger().debug("submitting transaction i= {}", i);
+ submit_transaction(std::move(t));
+ replay();
+ t = create_mutate_transaction();
+ }
+ if (i % 50 == 0) {
+ logger().debug("check_mappings i= {}", i);
+ check_mappings(omap_root, *t);
+ check_mappings(omap_root);
+ }
+ }
+ logger().debug("finally submitting transaction ");
+ submit_transaction(std::move(t));
+ replay();
+ check_mappings(omap_root);
+ });
+}
+
+
+TEST_P(omap_manager_test_t, internal_force_split_to_root)
+{
+ run_async([this] {
+ omap_root_t omap_root = initialize();
+
+ logger().debug("set big keys");
+ for (unsigned i = 0; i < 53; i++) {
+ auto t = create_mutate_transaction();
+
+ for (unsigned j = 0; j < 8; ++j) {
+ set_random_key(omap_root, *t);
+ }
+ logger().debug("submitting transaction i = {}", i);
+ submit_transaction(std::move(t));
+ }
+ logger().debug("set small keys");
+ for (unsigned i = 0; i < 100; i++) {
+ auto t = create_mutate_transaction();
+ for (unsigned j = 0; j < 8; ++j) {
+ set_random_key(omap_root, *t);
+ }
+ logger().debug("submitting transaction last");
+ submit_transaction(std::move(t));
+ }
+ check_mappings(omap_root);
+ });
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ omap_manager_test,
+ omap_manager_test_t,
+ ::testing::Values (
+ "segmented",
+ "circularbounded"
+ )
+);
diff --git a/src/test/crimson/seastore/test_randomblock_manager.cc b/src/test/crimson/seastore/test_randomblock_manager.cc
new file mode 100644
index 000000000..9ddb7f9ad
--- /dev/null
+++ b/src/test/crimson/seastore/test_randomblock_manager.cc
@@ -0,0 +1,178 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+
+#include <random>
+
+#include "crimson/common/log.h"
+#include "crimson/os/seastore/random_block_manager/block_rb_manager.h"
+#include "crimson/os/seastore/random_block_manager/rbm_device.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+constexpr uint64_t DEFAULT_TEST_SIZE = 1 << 20;
+
+struct rbm_test_t :
+ public seastar_test_suite_t {
+ std::unique_ptr<BlockRBManager> rbm_manager;
+ std::unique_ptr<random_block_device::RBMDevice> device;
+
+ struct rbm_transaction {
+ void add_rbm_allocated_blocks(alloc_delta_t &d) {
+ allocated_blocks.push_back(d);
+ }
+ void clear_rbm_allocated_blocks() {
+ if (!allocated_blocks.empty()) {
+ allocated_blocks.clear();
+ }
+ }
+ const auto &get_rbm_allocated_blocks() {
+ return allocated_blocks;
+ }
+ std::vector<alloc_delta_t> allocated_blocks;
+ };
+
+ std::default_random_engine generator;
+
+ uint64_t block_size = 0;
+ uint64_t size = 0;
+
+ device_config_t config;
+
+ rbm_test_t() = default;
+
+ seastar::future<> set_up_fut() final {
+ device = random_block_device::create_test_ephemeral(
+ random_block_device::DEFAULT_TEST_CBJOURNAL_SIZE, DEFAULT_TEST_SIZE);
+ block_size = device->get_block_size();
+ size = device->get_available_size();
+ rbm_manager.reset(new BlockRBManager(device.get(), std::string(), false));
+ config = get_rbm_ephemeral_device_config(0, 1);
+ return device->mkfs(config).handle_error(crimson::ct_error::assert_all{}
+ ).then([this] {
+ return device->mount().handle_error(crimson::ct_error::assert_all{}
+ ).then([this] {
+ return rbm_manager->open().handle_error(crimson::ct_error::assert_all{});
+ });
+ });
+ }
+
+ seastar::future<> tear_down_fut() final {
+ rbm_manager->close().unsafe_get0();
+ device->close().unsafe_get0();
+ rbm_manager.reset();
+ device.reset();
+ return seastar::now();
+ }
+
+ auto mkfs() {
+ return device->mkfs(config).unsafe_get0();
+ }
+
+ auto read_rbm_header() {
+ return device->read_rbm_header(RBM_START_ADDRESS).unsafe_get0();
+ }
+
+ auto open() {
+ device->mount().unsafe_get0();
+ return rbm_manager->open().unsafe_get0();
+ }
+
+ auto write(uint64_t addr, bufferptr &ptr) {
+ paddr_t paddr = convert_abs_addr_to_paddr(
+ addr,
+ rbm_manager->get_device_id());
+ return rbm_manager->write(paddr, ptr).unsafe_get0();
+ }
+
+ auto read(uint64_t addr, bufferptr &ptr) {
+ paddr_t paddr = convert_abs_addr_to_paddr(
+ addr,
+ rbm_manager->get_device_id());
+ return rbm_manager->read(paddr, ptr).unsafe_get0();
+ }
+
+ bufferptr generate_extent(size_t blocks) {
+ std::uniform_int_distribution<char> distribution(
+ std::numeric_limits<char>::min(),
+ std::numeric_limits<char>::max()
+ );
+ char contents = distribution(generator);
+ return buffer::ptr(buffer::create(blocks * block_size, contents));
+ }
+
+ void close() {
+ rbm_manager->close().unsafe_get0();
+ return;
+ }
+
+};
+
+TEST_F(rbm_test_t, mkfs_test)
+{
+ run_async([this] {
+ auto super = read_rbm_header();
+ ASSERT_TRUE(
+ super.block_size == block_size &&
+ super.size == size
+ );
+ config.spec.id = DEVICE_ID_NULL;
+ mkfs();
+ super = read_rbm_header();
+ ASSERT_TRUE(
+ super.config.spec.id == DEVICE_ID_NULL &&
+ super.size == size
+ );
+ });
+}
+
+TEST_F(rbm_test_t, open_read_write_test)
+{
+ run_async([this] {
+ auto content = generate_extent(1);
+ {
+ write(
+ block_size,
+ content
+ );
+ auto bp = bufferptr(ceph::buffer::create_page_aligned(block_size));
+ read(
+ block_size,
+ bp
+ );
+ bufferlist bl;
+ bufferlist block;
+ bl.append(bp);
+ block.append(content);
+ ASSERT_EQ(
+ bl.begin().crc32c(bl.length(), 1),
+ block.begin().crc32c(block.length(), 1));
+ }
+ close();
+ open();
+ {
+ auto bp = bufferptr(ceph::buffer::create_page_aligned(block_size));
+ read(
+ block_size,
+ bp
+ );
+ bufferlist bl;
+ bufferlist block;
+ bl.append(bp);
+ block.append(content);
+ ASSERT_EQ(
+ bl.begin().crc32c(bl.length(), 1),
+ block.begin().crc32c(block.length(), 1));
+ }
+ });
+}
+
diff --git a/src/test/crimson/seastore/test_seastore.cc b/src/test/crimson/seastore/test_seastore.cc
new file mode 100644
index 000000000..63bf4c51f
--- /dev/null
+++ b/src/test/crimson/seastore/test_seastore.cc
@@ -0,0 +1,1268 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <string>
+#include <iostream>
+#include <sstream>
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+
+#include "crimson/os/futurized_collection.h"
+#include "crimson/os/seastore/seastore.h"
+#include "crimson/os/seastore/onode.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+using SeaStoreShard = FuturizedStore::Shard;
+using CTransaction = ceph::os::Transaction;
+using namespace std;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+ghobject_t make_oid(int i) {
+ stringstream ss;
+ ss << "object_" << i;
+ auto ret = ghobject_t(
+ hobject_t(
+ sobject_t(ss.str(), CEPH_NOSNAP)));
+ ret.set_shard(shard_id_t(shard_id_t::NO_SHARD));
+ ret.hobj.nspace = "asdf";
+ ret.hobj.pool = 0;
+ uint32_t reverse_hash = hobject_t::_reverse_bits(0);
+ ret.hobj.set_bitwise_key_u32(reverse_hash + i * 100);
+ return ret;
+}
+
+ghobject_t make_temp_oid(int i) {
+ stringstream ss;
+ ss << "temp_object_" << i;
+ auto ret = ghobject_t(
+ hobject_t(
+ sobject_t(ss.str(), CEPH_NOSNAP)));
+ ret.set_shard(shard_id_t(shard_id_t::NO_SHARD));
+ ret.hobj.nspace = "hjkl";
+ ret.hobj.pool = -2ll;
+ uint32_t reverse_hash = hobject_t::_reverse_bits(0);
+ ret.hobj.set_bitwise_key_u32(reverse_hash + i * 100);
+ return ret;
+}
+
+struct seastore_test_t :
+ public seastar_test_suite_t,
+ SeaStoreTestState {
+
+ coll_t coll_name{spg_t{pg_t{0, 0}}};
+ CollectionRef coll;
+
+ seastore_test_t() {}
+
+ seastar::future<> set_up_fut() final {
+ return tm_setup(
+ ).then([this] {
+ return sharded_seastore->create_new_collection(coll_name);
+ }).then([this](auto coll_ref) {
+ coll = coll_ref;
+ CTransaction t;
+ t.create_collection(coll_name, 0);
+ return sharded_seastore->do_transaction(
+ coll,
+ std::move(t));
+ });
+ }
+
+ seastar::future<> tear_down_fut() final {
+ coll.reset();
+ return tm_teardown();
+ }
+
+ void do_transaction(CTransaction &&t) {
+ return sharded_seastore->do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void set_meta(
+ const std::string& key,
+ const std::string& value) {
+ return seastore->write_meta(key, value).get0();
+ }
+
+ std::tuple<int, std::string> get_meta(
+ const std::string& key) {
+ return seastore->read_meta(key).get();
+ }
+
+ struct object_state_t {
+ const coll_t cid;
+ const CollectionRef coll;
+ const ghobject_t oid;
+
+ std::map<string, bufferlist> omap;
+ bufferlist contents;
+
+ std::map<snapid_t, bufferlist> clone_contents;
+
+ void touch(
+ CTransaction &t) {
+ t.touch(cid, oid);
+ }
+
+ void touch(
+ SeaStoreShard &sharded_seastore) {
+ CTransaction t;
+ touch(t);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void truncate(
+ CTransaction &t,
+ uint64_t off) {
+ t.truncate(cid, oid, off);
+ }
+
+ void truncate(
+ SeaStoreShard &sharded_seastore,
+ uint64_t off) {
+ CTransaction t;
+ truncate(t, off);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ std::map<uint64_t, uint64_t> fiemap(
+ SeaStoreShard &sharded_seastore,
+ uint64_t off,
+ uint64_t len) {
+ return sharded_seastore.fiemap(coll, oid, off, len).unsafe_get0();
+ }
+
+ bufferlist readv(
+ SeaStoreShard &sharded_seastore,
+ interval_set<uint64_t>&m) {
+ return sharded_seastore.readv(coll, oid, m).unsafe_get0();
+ }
+
+ void remove(
+ CTransaction &t) {
+ t.remove(cid, oid);
+ t.remove_collection(cid);
+ }
+
+ void remove(
+ SeaStoreShard &sharded_seastore) {
+ CTransaction t;
+ remove(t);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void set_omap(
+ CTransaction &t,
+ const string &key,
+ const bufferlist &val) {
+ omap[key] = val;
+ std::map<string, bufferlist> arg;
+ arg[key] = val;
+ t.omap_setkeys(
+ cid,
+ oid,
+ arg);
+ }
+
+ void set_omap(
+ SeaStoreShard &sharded_seastore,
+ const string &key,
+ const bufferlist &val) {
+ CTransaction t;
+ set_omap(t, key, val);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void write(
+ SeaStoreShard &sharded_seastore,
+ CTransaction &t,
+ uint64_t offset,
+ bufferlist bl) {
+ bufferlist new_contents;
+ if (offset > 0 && contents.length()) {
+ new_contents.substr_of(
+ contents,
+ 0,
+ std::min<size_t>(offset, contents.length())
+ );
+ }
+ new_contents.append_zero(offset - new_contents.length());
+ new_contents.append(bl);
+
+ auto tail_offset = offset + bl.length();
+ if (contents.length() > tail_offset) {
+ bufferlist tail;
+ tail.substr_of(
+ contents,
+ tail_offset,
+ contents.length() - tail_offset);
+ new_contents.append(tail);
+ }
+ contents.swap(new_contents);
+
+ t.write(
+ cid,
+ oid,
+ offset,
+ bl.length(),
+ bl);
+ }
+
+ void write(
+ SeaStoreShard &sharded_seastore,
+ uint64_t offset,
+ bufferlist bl) {
+ CTransaction t;
+ write(sharded_seastore, t, offset, bl);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void clone(
+ SeaStoreShard &sharded_seastore,
+ snapid_t snap) {
+ ghobject_t coid = oid;
+ coid.hobj.snap = snap;
+ CTransaction t;
+ t.clone(cid, oid, coid);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ clone_contents[snap].reserve(contents.length());
+ auto it = contents.begin();
+ it.copy_all(clone_contents[snap]);
+ }
+
+ object_state_t get_clone(snapid_t snap) {
+ auto coid = oid;
+ coid.hobj.snap = snap;
+ auto clone_obj = object_state_t{cid, coll, coid};
+ clone_obj.contents.reserve(clone_contents[snap].length());
+ auto it = clone_contents[snap].begin();
+ it.copy_all(clone_obj.contents);
+ return clone_obj;
+ }
+
+ void write(
+ SeaStoreShard &sharded_seastore,
+ uint64_t offset,
+ size_t len,
+ char fill) {
+ auto buffer = bufferptr(buffer::create(len));
+ ::memset(buffer.c_str(), fill, len);
+ bufferlist bl;
+ bl.append(buffer);
+ write(sharded_seastore, offset, bl);
+ }
+
+ void zero(
+ SeaStoreShard &sharded_seastore,
+ CTransaction &t,
+ uint64_t offset,
+ size_t len) {
+ ceph::buffer::list bl;
+ bl.append_zero(len);
+ bufferlist new_contents;
+ if (offset > 0 && contents.length()) {
+ new_contents.substr_of(
+ contents,
+ 0,
+ std::min<size_t>(offset, contents.length())
+ );
+ }
+ new_contents.append_zero(offset - new_contents.length());
+ new_contents.append(bl);
+
+ auto tail_offset = offset + bl.length();
+ if (contents.length() > tail_offset) {
+ bufferlist tail;
+ tail.substr_of(
+ contents,
+ tail_offset,
+ contents.length() - tail_offset);
+ new_contents.append(tail);
+ }
+ contents.swap(new_contents);
+
+ t.zero(
+ cid,
+ oid,
+ offset,
+ len);
+ }
+
+ void zero(
+ SeaStoreShard &sharded_seastore,
+ uint64_t offset,
+ size_t len) {
+ CTransaction t;
+ zero(sharded_seastore, t, offset, len);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void read(
+ SeaStoreShard &sharded_seastore,
+ uint64_t offset,
+ uint64_t len) {
+ bufferlist to_check;
+ if (contents.length() >= offset) {
+ to_check.substr_of(
+ contents,
+ offset,
+ std::min(len, (uint64_t)contents.length()));
+ }
+ auto ret = sharded_seastore.read(
+ coll,
+ oid,
+ offset,
+ len).unsafe_get0();
+ EXPECT_EQ(ret.length(), to_check.length());
+ EXPECT_EQ(ret, to_check);
+ }
+
+ void check_size(SeaStoreShard &sharded_seastore) {
+ auto st = sharded_seastore.stat(
+ coll,
+ oid).get0();
+ EXPECT_EQ(contents.length(), st.st_size);
+ }
+
+ void set_attr(
+ SeaStoreShard &sharded_seastore,
+ std::string key,
+ bufferlist& val) {
+ CTransaction t;
+ t.setattr(cid, oid, key, val);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void rm_attr(
+ SeaStoreShard &sharded_seastore,
+ std::string key) {
+ CTransaction t;
+ t.rmattr(cid, oid, key);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ void rm_attrs(
+ SeaStoreShard &sharded_seastore) {
+ CTransaction t;
+ t.rmattrs(cid, oid);
+ sharded_seastore.do_transaction(
+ coll,
+ std::move(t)).get0();
+ }
+
+ SeaStoreShard::attrs_t get_attrs(
+ SeaStoreShard &sharded_seastore) {
+ return sharded_seastore.get_attrs(coll, oid)
+ .handle_error(SeaStoreShard::get_attrs_ertr::discard_all{})
+ .get();
+ }
+
+ ceph::bufferlist get_attr(
+ SeaStoreShard& sharded_seastore,
+ std::string_view name) {
+ return sharded_seastore.get_attr(coll, oid, name)
+ .handle_error(
+ SeaStoreShard::get_attr_errorator::discard_all{})
+ .get();
+ }
+
+ void check_omap_key(
+ SeaStoreShard &sharded_seastore,
+ const string &key) {
+ std::set<string> to_check;
+ to_check.insert(key);
+ auto result = sharded_seastore.omap_get_values(
+ coll,
+ oid,
+ to_check).unsafe_get0();
+ if (result.empty()) {
+ EXPECT_EQ(omap.find(key), omap.end());
+ } else {
+ auto iter = omap.find(key);
+ EXPECT_NE(iter, omap.end());
+ if (iter != omap.end()) {
+ EXPECT_EQ(result.size(), 1);
+ EXPECT_EQ(iter->second, result.begin()->second);
+ }
+ }
+ }
+
+ void check_omap(SeaStoreShard &sharded_seastore) {
+ auto refiter = omap.begin();
+ std::optional<std::string> start;
+ while(true) {
+ auto [done, kvs] = sharded_seastore.omap_get_values(
+ coll,
+ oid,
+ start).unsafe_get0();
+ auto iter = kvs.begin();
+ while (true) {
+ if ((done && iter == kvs.end()) && refiter == omap.end()) {
+ return; // finished
+ } else if (!done && iter == kvs.end()) {
+ break; // reload kvs
+ }
+ if (iter == kvs.end() || refiter->first < iter->first) {
+ logger().debug(
+ "check_omap: missing omap key {}",
+ refiter->first);
+ GTEST_FAIL() << "missing omap key " << refiter->first;
+ ++refiter;
+ } else if (refiter == omap.end() || refiter->first > iter->first) {
+ logger().debug(
+ "check_omap: extra omap key {}",
+ iter->first);
+ GTEST_FAIL() << "extra omap key " << iter->first;
+ ++iter;
+ } else {
+ EXPECT_EQ(iter->second, refiter->second);
+ ++iter;
+ ++refiter;
+ }
+ }
+ if (!done) {
+ start = kvs.rbegin()->first;
+ }
+ }
+ }
+ };
+
+ map<ghobject_t, object_state_t> test_objects;
+ object_state_t &get_object(
+ const ghobject_t &oid) {
+ return test_objects.emplace(
+ std::make_pair(
+ oid,
+ object_state_t{coll_name, coll, oid})).first->second;
+ }
+
+ void remove_object(
+ object_state_t &sobj) {
+
+ sobj.remove(*sharded_seastore);
+ auto erased = test_objects.erase(sobj.oid);
+ ceph_assert(erased == 1);
+ }
+
+ void validate_objects() const {
+ std::vector<ghobject_t> oids;
+ for (auto& [oid, obj] : test_objects) {
+ oids.emplace_back(oid);
+ }
+ auto ret = sharded_seastore->list_objects(
+ coll,
+ ghobject_t(),
+ ghobject_t::get_max(),
+ std::numeric_limits<uint64_t>::max()).get0();
+ EXPECT_EQ(std::get<1>(ret), ghobject_t::get_max());
+ EXPECT_EQ(std::get<0>(ret), oids);
+ }
+
+ // create temp objects
+ struct bound_t {
+ enum class type_t {
+ MIN,
+ MAX,
+ TEMP,
+ TEMP_END,
+ NORMAL_BEGIN,
+ NORMAL,
+ } type = type_t::MIN;
+ unsigned index = 0;
+
+ static bound_t get_temp(unsigned index) {
+ return bound_t{type_t::TEMP, index};
+ }
+ static bound_t get_normal(unsigned index) {
+ return bound_t{type_t::NORMAL, index};
+ }
+ static bound_t get_min() { return bound_t{type_t::MIN}; }
+ static bound_t get_max() { return bound_t{type_t::MAX}; }
+ static bound_t get_temp_end() { return bound_t{type_t::TEMP_END}; }
+ static bound_t get_normal_begin() {
+ return bound_t{type_t::NORMAL_BEGIN};
+ }
+
+ ghobject_t get_oid(SeaStore &seastore, CollectionRef &coll) const {
+ switch (type) {
+ case type_t::MIN:
+ return ghobject_t();
+ case type_t::MAX:
+ return ghobject_t::get_max();
+ case type_t::TEMP:
+ return make_temp_oid(index);
+ case type_t::TEMP_END:
+ return seastore.get_objs_range(coll, 0).temp_end;
+ case type_t::NORMAL_BEGIN:
+ return seastore.get_objs_range(coll, 0).obj_begin;
+ case type_t::NORMAL:
+ return make_oid(index);
+ default:
+ assert(0 == "impossible");
+ return ghobject_t();
+ }
+ }
+ };
+ struct list_test_case_t {
+ bound_t left;
+ bound_t right;
+ unsigned limit;
+ };
+ // list_test_cases_t :: [<limit, left_bound, right_bound>]
+ using list_test_cases_t = std::list<std::tuple<unsigned, bound_t, bound_t>>;
+
+ void test_list(
+ unsigned temp_to_create, /// create temp 0..temp_to_create-1
+ unsigned normal_to_create, /// create normal 0..normal_to_create-1
+ list_test_cases_t cases /// cases to test
+ ) {
+ std::vector<ghobject_t> objs;
+
+ // setup
+ auto create = [this, &objs](ghobject_t hoid) {
+ objs.emplace_back(std::move(hoid));
+ auto &obj = get_object(objs.back());
+ obj.touch(*sharded_seastore);
+ obj.check_size(*sharded_seastore);
+ };
+ for (unsigned i = 0; i < temp_to_create; ++i) {
+ create(make_temp_oid(i));
+ }
+ for (unsigned i = 0; i < normal_to_create; ++i) {
+ create(make_oid(i));
+ }
+
+ // list and validate each case
+ for (auto [limit, in_left_bound, in_right_bound] : cases) {
+ auto left_bound = in_left_bound.get_oid(*seastore, coll);
+ auto right_bound = in_right_bound.get_oid(*seastore, coll);
+
+ // get results from seastore
+ auto [listed, next] = sharded_seastore->list_objects(
+ coll, left_bound, right_bound, limit).get0();
+
+ // compute correct answer
+ auto correct_begin = std::find_if(
+ objs.begin(), objs.end(),
+ [&left_bound](const auto &in) {
+ return in >= left_bound;
+ });
+ unsigned count = 0;
+ auto correct_end = correct_begin;
+ for (; count < limit &&
+ correct_end != objs.end() &&
+ *correct_end < right_bound;
+ ++correct_end, ++count);
+
+ // validate return -- [correct_begin, correct_end) should match listed
+ decltype(objs) correct_listed(correct_begin, correct_end);
+ EXPECT_EQ(listed, correct_listed);
+
+ if (count < limit) {
+ if (correct_end == objs.end()) {
+ // if listed extends to end of range, next should be >= right_bound
+ EXPECT_GE(next, right_bound);
+ } else {
+ // next <= *correct_end since *correct_end is the next object to list
+ EXPECT_LE(next, *correct_end);
+ // next > *(correct_end - 1) since we already listed it
+ EXPECT_GT(next, *(correct_end - 1));
+ }
+ } else {
+ // we listed exactly limit objects
+ EXPECT_EQ(limit, listed.size());
+
+ EXPECT_GE(next, left_bound);
+ if (limit == 0) {
+ if (correct_end != objs.end()) {
+ // next <= *correct_end since *correct_end is the next object to list
+ EXPECT_LE(next, *correct_end);
+ }
+ } else {
+ // next > *(correct_end - 1) since we already listed it
+ EXPECT_GT(next, *(correct_end - 1));
+ }
+ }
+ }
+
+ // teardown
+ for (auto &&hoid : objs) { get_object(hoid).remove(*sharded_seastore); }
+ }
+};
+
+template <typename T, typename V>
+auto contains(const T &t, const V &v) {
+ return std::find(
+ t.begin(),
+ t.end(),
+ v) != t.end();
+}
+
+TEST_P(seastore_test_t, collection_create_list_remove)
+{
+ run_async([this] {
+ coll_t test_coll{spg_t{pg_t{1, 0}}};
+ {
+ sharded_seastore->create_new_collection(test_coll).get0();
+ {
+ CTransaction t;
+ t.create_collection(test_coll, 4);
+ do_transaction(std::move(t));
+ }
+ auto colls_cores = seastore->list_collections().get0();
+ std::vector<coll_t> colls;
+ colls.resize(colls_cores.size());
+ std::transform(
+ colls_cores.begin(), colls_cores.end(), colls.begin(),
+ [](auto p) { return p.first; });
+ EXPECT_EQ(colls.size(), 2);
+ EXPECT_TRUE(contains(colls, coll_name));
+ EXPECT_TRUE(contains(colls, test_coll));
+ }
+
+ {
+ {
+ CTransaction t;
+ t.remove_collection(test_coll);
+ do_transaction(std::move(t));
+ }
+ auto colls_cores = seastore->list_collections().get0();
+ std::vector<coll_t> colls;
+ colls.resize(colls_cores.size());
+ std::transform(
+ colls_cores.begin(), colls_cores.end(), colls.begin(),
+ [](auto p) { return p.first; });
+ EXPECT_EQ(colls.size(), 1);
+ EXPECT_TRUE(contains(colls, coll_name));
+ }
+ });
+}
+
+TEST_P(seastore_test_t, meta) {
+ run_async([this] {
+ set_meta("key1", "value1");
+ set_meta("key2", "value2");
+
+ const auto [ret1, value1] = get_meta("key1");
+ const auto [ret2, value2] = get_meta("key2");
+ EXPECT_EQ(ret1, 0);
+ EXPECT_EQ(ret2, 0);
+ EXPECT_EQ(value1, "value1");
+ EXPECT_EQ(value2, "value2");
+ });
+}
+
+TEST_P(seastore_test_t, touch_stat_list_remove)
+{
+ run_async([this] {
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.touch(*sharded_seastore);
+ test_obj.check_size(*sharded_seastore);
+ validate_objects();
+
+ remove_object(test_obj);
+ validate_objects();
+ });
+}
+
+using bound_t = seastore_test_t::bound_t;
+constexpr unsigned MAX_LIMIT = std::numeric_limits<unsigned>::max();
+static const seastore_test_t::list_test_cases_t temp_list_cases{
+ // list all temp, maybe overlap to normal on right
+ {MAX_LIMIT, bound_t::get_min() , bound_t::get_max() },
+ { 5, bound_t::get_min() , bound_t::get_temp_end()},
+ { 6, bound_t::get_min() , bound_t::get_temp_end()},
+ { 6, bound_t::get_min() , bound_t::get_max() },
+
+ // list temp starting at min up to but not past boundary
+ { 3, bound_t::get_min() , bound_t::get_temp(3) },
+ { 3, bound_t::get_min() , bound_t::get_temp(4) },
+ { 3, bound_t::get_min() , bound_t::get_temp(2) },
+
+ // list temp starting > min up to or past boundary
+ { 3, bound_t::get_temp(2) , bound_t::get_temp_end()},
+ { 3, bound_t::get_temp(2) , bound_t::get_max() },
+ { 3, bound_t::get_temp(3) , bound_t::get_max() },
+ { 3, bound_t::get_temp(1) , bound_t::get_max() },
+
+ // 0 limit
+ { 0, bound_t::get_min() , bound_t::get_max() },
+ { 0, bound_t::get_temp(1) , bound_t::get_max() },
+ { 0, bound_t::get_temp_end(), bound_t::get_max() },
+};
+
+TEST_P(seastore_test_t, list_objects_temp_only)
+{
+ run_async([this] { test_list(5, 0, temp_list_cases); });
+}
+
+TEST_P(seastore_test_t, list_objects_temp_overlap)
+{
+ run_async([this] { test_list(5, 5, temp_list_cases); });
+}
+
+static const seastore_test_t::list_test_cases_t normal_list_cases{
+ // list all normal, maybe overlap to temp on left
+ {MAX_LIMIT, bound_t::get_min() , bound_t::get_max() },
+ { 5, bound_t::get_normal_begin(), bound_t::get_max() },
+ { 6, bound_t::get_normal_begin(), bound_t::get_max() },
+ { 6, bound_t::get_temp(4) , bound_t::get_max() },
+
+ // list normal starting <= normal_begin < end
+ { 3, bound_t::get_normal_begin(), bound_t::get_normal(3)},
+ { 3, bound_t::get_normal_begin(), bound_t::get_normal(4)},
+ { 3, bound_t::get_normal_begin(), bound_t::get_normal(2)},
+ { 3, bound_t::get_temp(5) , bound_t::get_normal(2)},
+ { 3, bound_t::get_temp(4) , bound_t::get_normal(2)},
+
+ // list normal starting > min up to end
+ { 3, bound_t::get_normal(2) , bound_t::get_max() },
+ { 3, bound_t::get_normal(2) , bound_t::get_max() },
+ { 3, bound_t::get_normal(3) , bound_t::get_max() },
+ { 3, bound_t::get_normal(1) , bound_t::get_max() },
+
+ // 0 limit
+ { 0, bound_t::get_min() , bound_t::get_max() },
+ { 0, bound_t::get_normal(1) , bound_t::get_max() },
+ { 0, bound_t::get_normal_begin(), bound_t::get_max() },
+};
+
+TEST_P(seastore_test_t, list_objects_normal_only)
+{
+ run_async([this] { test_list(5, 0, normal_list_cases); });
+}
+
+TEST_P(seastore_test_t, list_objects_normal_overlap)
+{
+ run_async([this] { test_list(5, 5, normal_list_cases); });
+}
+
+bufferlist make_bufferlist(size_t len) {
+ bufferptr ptr(len);
+ bufferlist bl;
+ bl.append(ptr);
+ return bl;
+}
+
+TEST_P(seastore_test_t, omap_test_simple)
+{
+ run_async([this] {
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.touch(*sharded_seastore);
+ test_obj.set_omap(
+ *sharded_seastore,
+ "asdf",
+ make_bufferlist(128));
+ test_obj.check_omap_key(
+ *sharded_seastore,
+ "asdf");
+ });
+}
+
+TEST_P(seastore_test_t, clone_aligned_extents)
+{
+ run_async([this] {
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.write(*sharded_seastore, 0, 4096, 'a');
+
+ test_obj.clone(*sharded_seastore, 10);
+ std::cout << "reading origin after clone10" << std::endl;
+ test_obj.read(*sharded_seastore, 0, 4096);
+ test_obj.write(*sharded_seastore, 0, 4096, 'b');
+ test_obj.write(*sharded_seastore, 4096, 4096, 'c');
+ std::cout << "reading origin after clone10 and write" << std::endl;
+ test_obj.read(*sharded_seastore, 0, 8192);
+ auto clone_obj10 = test_obj.get_clone(10);
+ std::cout << "reading clone after clone10 and write" << std::endl;
+ clone_obj10.read(*sharded_seastore, 0, 8192);
+
+ test_obj.clone(*sharded_seastore, 20);
+ std::cout << "reading origin after clone20" << std::endl;
+ test_obj.read(*sharded_seastore, 0, 4096);
+ test_obj.write(*sharded_seastore, 0, 4096, 'd');
+ test_obj.write(*sharded_seastore, 4096, 4096, 'e');
+ test_obj.write(*sharded_seastore, 8192, 4096, 'f');
+ std::cout << "reading origin after clone20 and write" << std::endl;
+ test_obj.read(*sharded_seastore, 0, 12288);
+ auto clone_obj20 = test_obj.get_clone(20);
+ std::cout << "reading clone after clone20 and write" << std::endl;
+ clone_obj10.read(*sharded_seastore, 0, 12288);
+ clone_obj20.read(*sharded_seastore, 0, 12288);
+ });
+}
+
+TEST_P(seastore_test_t, clone_unaligned_extents)
+{
+ run_async([this] {
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.write(*sharded_seastore, 0, 8192, 'a');
+ test_obj.write(*sharded_seastore, 8192, 8192, 'b');
+ test_obj.write(*sharded_seastore, 16384, 8192, 'c');
+
+ test_obj.clone(*sharded_seastore, 10);
+ test_obj.write(*sharded_seastore, 4096, 12288, 'd');
+ std::cout << "reading origin after clone10 and write" << std::endl;
+ test_obj.read(*sharded_seastore, 0, 24576);
+
+ auto clone_obj10 = test_obj.get_clone(10);
+ std::cout << "reading clone after clone10 and write" << std::endl;
+ clone_obj10.read(*sharded_seastore, 0, 24576);
+
+ test_obj.clone(*sharded_seastore, 20);
+ test_obj.write(*sharded_seastore, 8192, 12288, 'e');
+ std::cout << "reading origin after clone20 and write" << std::endl;
+ test_obj.read(*sharded_seastore, 0, 24576);
+
+ auto clone_obj20 = test_obj.get_clone(20);
+ std::cout << "reading clone after clone20 and write" << std::endl;
+ clone_obj10.read(*sharded_seastore, 0, 24576);
+ clone_obj20.read(*sharded_seastore, 0, 24576);
+
+ test_obj.write(*sharded_seastore, 0, 24576, 'f');
+ test_obj.clone(*sharded_seastore, 30);
+ test_obj.write(*sharded_seastore, 8192, 4096, 'g');
+ std::cout << "reading origin after clone30 and write" << std::endl;
+ test_obj.read(*sharded_seastore, 0, 24576);
+
+ auto clone_obj30 = test_obj.get_clone(30);
+ std::cout << "reading clone after clone30 and write" << std::endl;
+ clone_obj10.read(*sharded_seastore, 0, 24576);
+ clone_obj20.read(*sharded_seastore, 0, 24576);
+ clone_obj30.read(*sharded_seastore, 0, 24576);
+ });
+}
+
+TEST_P(seastore_test_t, attr)
+{
+ run_async([this] {
+ auto& test_obj = get_object(make_oid(0));
+ test_obj.touch(*sharded_seastore);
+ {
+ std::string oi("asdfasdfasdf");
+ bufferlist bl;
+ encode(oi, bl);
+ test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
+
+ std::string ss("fdsfdsfs");
+ bl.clear();
+ encode(ss, bl);
+ test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
+
+ std::string test_val("ssssssssssss");
+ bl.clear();
+ encode(test_val, bl);
+ test_obj.set_attr(*sharded_seastore, "test_key", bl);
+
+ auto attrs = test_obj.get_attrs(*sharded_seastore);
+ std::string oi2;
+ bufferlist bl2 = attrs[OI_ATTR];
+ decode(oi2, bl2);
+ bl2.clear();
+ bl2 = attrs[SS_ATTR];
+ std::string ss2;
+ decode(ss2, bl2);
+ std::string test_val2;
+ bl2.clear();
+ bl2 = attrs["test_key"];
+ decode(test_val2, bl2);
+ EXPECT_EQ(ss, ss2);
+ EXPECT_EQ(oi, oi2);
+ EXPECT_EQ(test_val, test_val2);
+
+ bl2.clear();
+ bl2 = test_obj.get_attr(*sharded_seastore, "test_key");
+ test_val2.clear();
+ decode(test_val2, bl2);
+ EXPECT_EQ(test_val, test_val2);
+ //test rm_attrs
+ test_obj.rm_attrs(*sharded_seastore);
+ attrs = test_obj.get_attrs(*sharded_seastore);
+ EXPECT_EQ(attrs.find(OI_ATTR), attrs.end());
+ EXPECT_EQ(attrs.find(SS_ATTR), attrs.end());
+ EXPECT_EQ(attrs.find("test_key"), attrs.end());
+
+ std::cout << "test_key passed" << std::endl;
+ //create OI_ATTR with len > onode_layout_t::MAX_OI_LENGTH, rm OI_ATTR
+ //create SS_ATTR with len > onode_layout_t::MAX_SS_LENGTH, rm SS_ATTR
+ char oi_array[onode_layout_t::MAX_OI_LENGTH + 1] = {'a'};
+ std::string oi_str(&oi_array[0], sizeof(oi_array));
+ bl.clear();
+ encode(oi_str, bl);
+ test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
+
+ char ss_array[onode_layout_t::MAX_SS_LENGTH + 1] = {'b'};
+ std::string ss_str(&ss_array[0], sizeof(ss_array));
+ bl.clear();
+ encode(ss_str, bl);
+ test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
+
+ attrs = test_obj.get_attrs(*sharded_seastore);
+ bl2.clear();
+ bl2 = attrs[OI_ATTR];
+ std::string oi_str2;
+ decode(oi_str2, bl2);
+ EXPECT_EQ(oi_str, oi_str2);
+
+ bl2.clear();
+ bl2 = attrs[SS_ATTR];
+ std::string ss_str2;
+ decode(ss_str2, bl2);
+ EXPECT_EQ(ss_str, ss_str2);
+
+ bl2.clear();
+ ss_str2.clear();
+ bl2 = test_obj.get_attr(*sharded_seastore, SS_ATTR);
+ decode(ss_str2, bl2);
+ EXPECT_EQ(ss_str, ss_str2);
+
+ bl2.clear();
+ oi_str2.clear();
+ bl2 = test_obj.get_attr(*sharded_seastore, OI_ATTR);
+ decode(oi_str2, bl2);
+ EXPECT_EQ(oi_str, oi_str2);
+
+ test_obj.rm_attr(*sharded_seastore, OI_ATTR);
+ test_obj.rm_attr(*sharded_seastore, SS_ATTR);
+
+ attrs = test_obj.get_attrs(*sharded_seastore);
+ EXPECT_EQ(attrs.find(OI_ATTR), attrs.end());
+ EXPECT_EQ(attrs.find(SS_ATTR), attrs.end());
+ }
+ {
+ //create OI_ATTR with len <= onode_layout_t::MAX_OI_LENGTH, rm OI_ATTR
+ //create SS_ATTR with len <= onode_layout_t::MAX_SS_LENGTH, rm SS_ATTR
+ std::string oi("asdfasdfasdf");
+ bufferlist bl;
+ encode(oi, bl);
+ test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
+
+ std::string ss("f");
+ bl.clear();
+ encode(ss, bl);
+ test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
+
+ std::string test_val("ssssssssssss");
+ bl.clear();
+ encode(test_val, bl);
+ test_obj.set_attr(*sharded_seastore, "test_key", bl);
+
+ auto attrs = test_obj.get_attrs(*sharded_seastore);
+ std::string oi2;
+ bufferlist bl2 = attrs[OI_ATTR];
+ decode(oi2, bl2);
+ bl2.clear();
+ bl2 = attrs[SS_ATTR];
+ std::string ss2;
+ decode(ss2, bl2);
+ std::string test_val2;
+ bl2.clear();
+ bl2 = attrs["test_key"];
+ decode(test_val2, bl2);
+ EXPECT_EQ(ss, ss2);
+ EXPECT_EQ(oi, oi2);
+ EXPECT_EQ(test_val, test_val2);
+
+ test_obj.rm_attr(*sharded_seastore, OI_ATTR);
+ test_obj.rm_attr(*sharded_seastore, SS_ATTR);
+ test_obj.rm_attr(*sharded_seastore, "test_key");
+
+ attrs = test_obj.get_attrs(*sharded_seastore);
+ EXPECT_EQ(attrs.find(OI_ATTR), attrs.end());
+ EXPECT_EQ(attrs.find(SS_ATTR), attrs.end());
+ EXPECT_EQ(attrs.find("test_key"), attrs.end());
+ }
+ {
+ // create OI_ATTR with len > onode_layout_t::MAX_OI_LENGTH, then
+ // overwrite it with another OI_ATTR len of which < onode_layout_t::MAX_OI_LENGTH
+ // create SS_ATTR with len > onode_layout_t::MAX_SS_LENGTH, then
+ // overwrite it with another SS_ATTR len of which < onode_layout_t::MAX_SS_LENGTH
+ char oi_array[onode_layout_t::MAX_OI_LENGTH + 1] = {'a'};
+ std::string oi(&oi_array[0], sizeof(oi_array));
+ bufferlist bl;
+ encode(oi, bl);
+ test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
+
+ oi = "asdfasdfasdf";
+ bl.clear();
+ encode(oi, bl);
+ test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
+
+ char ss_array[onode_layout_t::MAX_SS_LENGTH + 1] = {'b'};
+ std::string ss(&ss_array[0], sizeof(ss_array));
+ bl.clear();
+ encode(ss, bl);
+ test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
+
+ ss = "f";
+ bl.clear();
+ encode(ss, bl);
+ test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
+
+ auto attrs = test_obj.get_attrs(*sharded_seastore);
+ std::string oi2, ss2;
+ bufferlist bl2 = attrs[OI_ATTR];
+ decode(oi2, bl2);
+ bl2.clear();
+ bl2 = attrs[SS_ATTR];
+ decode(ss2, bl2);
+ EXPECT_EQ(oi, oi2);
+ EXPECT_EQ(ss, ss2);
+ }
+ });
+}
+
+TEST_P(seastore_test_t, omap_test_iterator)
+{
+ run_async([this] {
+ auto make_key = [](unsigned i) {
+ std::stringstream ss;
+ ss << "key" << i;
+ return ss.str();
+ };
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.touch(*sharded_seastore);
+ for (unsigned i = 0; i < 20; ++i) {
+ test_obj.set_omap(
+ *sharded_seastore,
+ make_key(i),
+ make_bufferlist(128));
+ }
+ test_obj.check_omap(*sharded_seastore);
+ });
+}
+
+TEST_P(seastore_test_t, object_data_omap_remove)
+{
+ run_async([this] {
+ auto make_key = [](unsigned i) {
+ std::stringstream ss;
+ ss << "key" << i;
+ return ss.str();
+ };
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.touch(*sharded_seastore);
+ for (unsigned i = 0; i < 1024; ++i) {
+ test_obj.set_omap(
+ *sharded_seastore,
+ make_key(i),
+ make_bufferlist(128));
+ }
+ test_obj.check_omap(*sharded_seastore);
+
+ for (uint64_t i = 0; i < 16; i++) {
+ test_obj.write(
+ *sharded_seastore,
+ 4096 * i,
+ 4096,
+ 'a');
+ }
+ test_obj.remove(*sharded_seastore);
+ });
+}
+
+
+TEST_P(seastore_test_t, simple_extent_test)
+{
+ run_async([this] {
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.write(
+ *sharded_seastore,
+ 1024,
+ 1024,
+ 'a');
+ test_obj.read(
+ *sharded_seastore,
+ 1024,
+ 1024);
+ test_obj.check_size(*sharded_seastore);
+ });
+}
+
+TEST_P(seastore_test_t, fiemap_empty)
+{
+ run_async([this] {
+ auto &test_obj = get_object(make_oid(0));
+ test_obj.touch(*sharded_seastore);
+ test_obj.truncate(*sharded_seastore, 100000);
+
+ std::map<uint64_t, uint64_t> m;
+ m = test_obj.fiemap(*sharded_seastore, 0, 100000);
+ EXPECT_TRUE(m.empty());
+
+ test_obj.remove(*sharded_seastore);
+ });
+}
+
+TEST_P(seastore_test_t, fiemap_holes)
+{
+ run_async([this] {
+ const uint64_t MAX_EXTENTS = 100;
+
+ // large enough to ensure that seastore will allocate each write seperately
+ const uint64_t SKIP_STEP = 16 << 10;
+ auto &test_obj = get_object(make_oid(0));
+ bufferlist bl;
+ bl.append("foo");
+
+ test_obj.touch(*sharded_seastore);
+ for (uint64_t i = 0; i < MAX_EXTENTS; i++) {
+ test_obj.write(*sharded_seastore, SKIP_STEP * i, bl);
+ }
+
+ { // fiemap test from 0 to SKIP_STEP * (MAX_EXTENTS - 1) + 3
+ auto m = test_obj.fiemap(
+ *sharded_seastore, 0, SKIP_STEP * (MAX_EXTENTS - 1) + 3);
+ ASSERT_EQ(m.size(), MAX_EXTENTS);
+ for (uint64_t i = 0; i < MAX_EXTENTS; i++) {
+ ASSERT_TRUE(m.count(SKIP_STEP * i));
+ ASSERT_GE(m[SKIP_STEP * i], bl.length());
+ }
+ }
+
+ { // fiemap test from SKIP_STEP to SKIP_STEP * (MAX_EXTENTS - 2) + 3
+ auto m = test_obj.fiemap(
+ *sharded_seastore, SKIP_STEP, SKIP_STEP * (MAX_EXTENTS - 3) + 3);
+ ASSERT_EQ(m.size(), MAX_EXTENTS - 2);
+ for (uint64_t i = 1; i < MAX_EXTENTS - 1; i++) {
+ ASSERT_TRUE(m.count(SKIP_STEP * i));
+ ASSERT_GE(m[SKIP_STEP * i], bl.length());
+ }
+ }
+
+ { // fiemap test SKIP_STEP + 1 to 2 * SKIP_STEP + 1 (partial overlap)
+ auto m = test_obj.fiemap(
+ *sharded_seastore, SKIP_STEP + 1, SKIP_STEP + 1);
+ ASSERT_EQ(m.size(), 2);
+ ASSERT_EQ(m.begin()->first, SKIP_STEP + 1);
+ ASSERT_GE(m.begin()->second, bl.length());
+ ASSERT_LE(m.rbegin()->first, (2 * SKIP_STEP) + 1);
+ ASSERT_EQ(m.rbegin()->first + m.rbegin()->second, 2 * SKIP_STEP + 2);
+ }
+
+ test_obj.remove(*sharded_seastore);
+ });
+}
+
+TEST_P(seastore_test_t, sparse_read)
+{
+ run_async([this] {
+ const uint64_t MAX_EXTENTS = 100;
+ const uint64_t SKIP_STEP = 16 << 10;
+ auto &test_obj = get_object(make_oid(0));
+ bufferlist wbl;
+ wbl.append("foo");
+
+ test_obj.touch(*sharded_seastore);
+ for (uint64_t i = 0; i < MAX_EXTENTS; i++) {
+ test_obj.write(*sharded_seastore, SKIP_STEP * i, wbl);
+ }
+ interval_set<uint64_t> m;
+ m = interval_set<uint64_t>(
+ test_obj.fiemap(*sharded_seastore, 0, SKIP_STEP * (MAX_EXTENTS - 1) + 3));
+ ASSERT_TRUE(!m.empty());
+ uint64_t off = 0;
+ auto rbl = test_obj.readv(*sharded_seastore, m);
+
+ for (auto &&miter : m) {
+ bufferlist subl;
+ subl.substr_of(rbl, off, std::min(miter.second, uint64_t(wbl.length())));
+ ASSERT_TRUE(subl.contents_equal(wbl));
+ off += miter.second;
+ }
+ test_obj.remove(*sharded_seastore);
+ });
+}
+
+TEST_P(seastore_test_t, zero)
+{
+ run_async([this] {
+ auto test_zero = [this](
+ // [(off, len, repeat)]
+ std::vector<std::tuple<uint64_t, uint64_t, uint64_t>> writes,
+ uint64_t zero_off, uint64_t zero_len) {
+
+ // Test zero within a block
+ auto &test_obj = get_object(make_oid(0));
+ uint64_t size = 0;
+ for (auto &[off, len, repeat]: writes) {
+ for (decltype(repeat) i = 0; i < repeat; ++i) {
+ test_obj.write(*sharded_seastore, off + (len * repeat), len, 'a');
+ }
+ size = off + (len * (repeat + 1));
+ }
+ test_obj.read(
+ *sharded_seastore,
+ 0,
+ size);
+ test_obj.check_size(*sharded_seastore);
+ test_obj.zero(*sharded_seastore, zero_off, zero_len);
+ test_obj.read(
+ *sharded_seastore,
+ 0,
+ size);
+ test_obj.check_size(*sharded_seastore);
+ remove_object(test_obj);
+ };
+
+ const uint64_t BS = 4<<10;
+
+ // Test zero within a block
+ test_zero(
+ {{1<<10, 1<<10, 1}},
+ 1124, 200);
+
+ // Multiple writes, partial on left, partial on right.
+ test_zero(
+ {{BS, BS, 10}},
+ BS + 128,
+ BS * 4);
+
+ // Single large write, block boundary on right, partial on left.
+ test_zero(
+ {{BS, BS * 10, 1}},
+ BS + 128,
+ (BS * 4) - 128);
+
+ // Multiple writes, block boundary on left, partial on right.
+ test_zero(
+ {{BS, BS, 10}},
+ BS,
+ (BS * 4) + 128);
+ });
+}
+INSTANTIATE_TEST_SUITE_P(
+ seastore_test,
+ seastore_test_t,
+ ::testing::Values (
+ "segmented",
+ "circularbounded"
+ )
+);
diff --git a/src/test/crimson/seastore/test_seastore_cache.cc b/src/test/crimson/seastore/test_seastore_cache.cc
new file mode 100644
index 000000000..b249d27e4
--- /dev/null
+++ b/src/test/crimson/seastore/test_seastore_cache.cc
@@ -0,0 +1,260 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "crimson/common/log.h"
+#include "crimson/os/seastore/cache.h"
+#include "crimson/os/seastore/segment_manager/ephemeral.h"
+
+#include "test/crimson/seastore/test_block.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+struct cache_test_t : public seastar_test_suite_t {
+ segment_manager::EphemeralSegmentManagerRef segment_manager;
+ ExtentPlacementManagerRef epm;
+ CacheRef cache;
+ paddr_t current;
+ journal_seq_t seq = JOURNAL_SEQ_MIN;
+
+ cache_test_t() = default;
+
+ seastar::future<paddr_t> submit_transaction(
+ TransactionRef t) {
+ auto record = cache->prepare_record(*t, JOURNAL_SEQ_NULL, JOURNAL_SEQ_NULL);
+
+ bufferlist bl;
+ for (auto &&block : record.extents) {
+ bl.append(block.bl);
+ }
+
+ ceph_assert((segment_off_t)bl.length() <
+ segment_manager->get_segment_size());
+ if (current.as_seg_paddr().get_segment_off() + (segment_off_t)bl.length() >
+ segment_manager->get_segment_size())
+ current = paddr_t::make_seg_paddr(
+ segment_id_t(
+ current.as_seg_paddr().get_segment_id().device_id(),
+ current.as_seg_paddr().get_segment_id().device_segment_id() + 1),
+ 0);
+
+ auto prev = current;
+ current.as_seg_paddr().set_segment_off(
+ current.as_seg_paddr().get_segment_off()
+ + bl.length());
+ return segment_manager->segment_write(
+ prev,
+ std::move(bl),
+ true
+ ).safe_then(
+ [this, prev, t=std::move(t)]() mutable {
+ cache->complete_commit(*t, prev, seq /* TODO */);
+ return prev;
+ },
+ crimson::ct_error::all_same_way([](auto e) {
+ ASSERT_FALSE("failed to submit");
+ })
+ );
+ }
+
+ auto get_transaction() {
+ return cache->create_transaction(
+ Transaction::src_t::MUTATE, "test_cache", false);
+ }
+
+ template <typename T, typename... Args>
+ auto get_extent(Transaction &t, Args&&... args) {
+ return with_trans_intr(
+ t,
+ [this](auto &&... args) {
+ return cache->get_extent<T>(args...);
+ },
+ std::forward<Args>(args)...);
+ }
+
+ seastar::future<> set_up_fut() final {
+ segment_manager = segment_manager::create_test_ephemeral();
+ return segment_manager->init(
+ ).safe_then([this] {
+ return segment_manager->mkfs(
+ segment_manager::get_ephemeral_device_config(0, 1, 0));
+ }).safe_then([this] {
+ epm.reset(new ExtentPlacementManager());
+ cache.reset(new Cache(*epm));
+ current = paddr_t::make_seg_paddr(segment_id_t(segment_manager->get_device_id(), 0), 0);
+ epm->test_init_no_background(segment_manager.get());
+ return seastar::do_with(
+ get_transaction(),
+ [this](auto &ref_t) {
+ cache->init();
+ return with_trans_intr(*ref_t, [&](auto &t) {
+ return cache->mkfs(t);
+ }).safe_then([this, &ref_t] {
+ return submit_transaction(std::move(ref_t)
+ ).then([](auto p) {});
+ });
+ });
+ }).handle_error(
+ crimson::ct_error::all_same_way([](auto e) {
+ ASSERT_FALSE("failed to submit");
+ })
+ );
+ }
+
+ seastar::future<> tear_down_fut() final {
+ return cache->close(
+ ).safe_then([this] {
+ segment_manager.reset();
+ epm.reset();
+ cache.reset();
+ }).handle_error(
+ Cache::close_ertr::assert_all{}
+ );
+ }
+};
+
+TEST_F(cache_test_t, test_addr_fixup)
+{
+ run_async([this] {
+ paddr_t addr;
+ int csum = 0;
+ {
+ auto t = get_transaction();
+ auto extent = cache->alloc_new_extent<TestBlockPhysical>(
+ *t,
+ TestBlockPhysical::SIZE,
+ placement_hint_t::HOT,
+ 0);
+ extent->set_contents('c');
+ csum = extent->get_crc32c();
+ submit_transaction(std::move(t)).get0();
+ addr = extent->get_paddr();
+ }
+ {
+ auto t = get_transaction();
+ auto extent = get_extent<TestBlockPhysical>(
+ *t,
+ addr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ ASSERT_EQ(extent->get_paddr(), addr);
+ ASSERT_EQ(extent->get_crc32c(), csum);
+ }
+ });
+}
+
+TEST_F(cache_test_t, test_dirty_extent)
+{
+ run_async([this] {
+ paddr_t addr;
+ int csum = 0;
+ int csum2 = 0;
+ {
+ // write out initial test block
+ auto t = get_transaction();
+ auto extent = cache->alloc_new_extent<TestBlockPhysical>(
+ *t,
+ TestBlockPhysical::SIZE,
+ placement_hint_t::HOT,
+ 0);
+ extent->set_contents('c');
+ csum = extent->get_crc32c();
+ auto reladdr = extent->get_paddr();
+ ASSERT_TRUE(reladdr.is_relative());
+ {
+ // test that read with same transaction sees new block though
+ // uncommitted
+ auto extent = get_extent<TestBlockPhysical>(
+ *t,
+ reladdr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ ASSERT_TRUE(extent->is_clean());
+ ASSERT_TRUE(extent->is_pending());
+ ASSERT_TRUE(extent->get_paddr().is_relative());
+ ASSERT_EQ(extent->get_version(), 0);
+ ASSERT_EQ(csum, extent->get_crc32c());
+ }
+ submit_transaction(std::move(t)).get0();
+ addr = extent->get_paddr();
+ }
+ {
+ // test that consecutive reads on the same extent get the same ref
+ auto t = get_transaction();
+ auto extent = get_extent<TestBlockPhysical>(
+ *t,
+ addr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ auto t2 = get_transaction();
+ auto extent2 = get_extent<TestBlockPhysical>(
+ *t2,
+ addr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ ASSERT_EQ(&*extent, &*extent2);
+ }
+ {
+ // read back test block
+ auto t = get_transaction();
+ auto extent = get_extent<TestBlockPhysical>(
+ *t,
+ addr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ // duplicate and reset contents
+ extent = cache->duplicate_for_write(*t, extent)->cast<TestBlockPhysical>();
+ extent->set_contents('c');
+ csum2 = extent->get_crc32c();
+ ASSERT_EQ(extent->get_paddr(), addr);
+ {
+ // test that concurrent read with fresh transaction sees old
+ // block
+ auto t2 = get_transaction();
+ auto extent = get_extent<TestBlockPhysical>(
+ *t2,
+ addr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ ASSERT_TRUE(extent->is_clean());
+ ASSERT_FALSE(extent->is_pending());
+ ASSERT_EQ(addr, extent->get_paddr());
+ ASSERT_EQ(extent->get_version(), 0);
+ ASSERT_EQ(csum, extent->get_crc32c());
+ }
+ {
+ // test that read with same transaction sees new block
+ auto extent = get_extent<TestBlockPhysical>(
+ *t,
+ addr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ ASSERT_TRUE(extent->is_dirty());
+ ASSERT_TRUE(extent->is_pending());
+ ASSERT_EQ(addr, extent->get_paddr());
+ ASSERT_EQ(extent->get_version(), 1);
+ ASSERT_EQ(csum2, extent->get_crc32c());
+ }
+ // submit transaction
+ submit_transaction(std::move(t)).get0();
+ ASSERT_TRUE(extent->is_dirty());
+ ASSERT_EQ(addr, extent->get_paddr());
+ ASSERT_EQ(extent->get_version(), 1);
+ ASSERT_EQ(extent->get_crc32c(), csum2);
+ }
+ {
+ // test that fresh transaction now sees newly dirty block
+ auto t = get_transaction();
+ auto extent = get_extent<TestBlockPhysical>(
+ *t,
+ addr,
+ TestBlockPhysical::SIZE).unsafe_get0();
+ ASSERT_TRUE(extent->is_dirty());
+ ASSERT_EQ(addr, extent->get_paddr());
+ ASSERT_EQ(extent->get_version(), 1);
+ ASSERT_EQ(csum2, extent->get_crc32c());
+ }
+ });
+}
diff --git a/src/test/crimson/seastore/test_seastore_journal.cc b/src/test/crimson/seastore/test_seastore_journal.cc
new file mode 100644
index 000000000..46ec723a3
--- /dev/null
+++ b/src/test/crimson/seastore/test_seastore_journal.cc
@@ -0,0 +1,343 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+
+#include <random>
+
+#include "crimson/common/log.h"
+#include "crimson/os/seastore/async_cleaner.h"
+#include "crimson/os/seastore/journal.h"
+#include "crimson/os/seastore/segment_manager/ephemeral.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+struct record_validator_t {
+ record_t record;
+ paddr_t record_final_offset;
+
+ template <typename... T>
+ record_validator_t(T&&... record) : record(std::forward<T>(record)...) {}
+
+ void validate(SegmentManager &manager) {
+ paddr_t addr = make_record_relative_paddr(0);
+ for (auto &&block : record.extents) {
+ auto test = manager.read(
+ record_final_offset.add_relative(addr),
+ block.bl.length()).unsafe_get0();
+ addr = addr.add_offset(block.bl.length());
+ bufferlist bl;
+ bl.push_back(test);
+ ASSERT_EQ(
+ bl.length(),
+ block.bl.length());
+ ASSERT_EQ(
+ bl.begin().crc32c(bl.length(), 1),
+ block.bl.begin().crc32c(block.bl.length(), 1));
+ }
+ }
+
+ auto get_replay_handler() {
+ auto checker = [this, iter=record.deltas.begin()] (
+ paddr_t base,
+ const delta_info_t &di) mutable {
+ EXPECT_EQ(base, record_final_offset);
+ ceph_assert(iter != record.deltas.end());
+ EXPECT_EQ(di, *iter++);
+ EXPECT_EQ(base, record_final_offset);
+ return iter != record.deltas.end();
+ };
+ if (record.deltas.size()) {
+ return std::make_optional(std::move(checker));
+ } else {
+ return std::optional<decltype(checker)>();
+ }
+ }
+};
+
+struct journal_test_t : seastar_test_suite_t, SegmentProvider, JournalTrimmer {
+ segment_manager::EphemeralSegmentManagerRef segment_manager;
+ WritePipeline pipeline;
+ JournalRef journal;
+
+ std::vector<record_validator_t> records;
+
+ std::default_random_engine generator;
+
+ extent_len_t block_size;
+
+ SegmentManagerGroupRef sms;
+
+ segment_id_t next;
+
+ std::map<segment_id_t, segment_seq_t> segment_seqs;
+ std::map<segment_id_t, segment_type_t> segment_types;
+
+ journal_seq_t dummy_tail;
+
+ mutable segment_info_t tmp_info;
+
+ journal_test_t() = default;
+
+ /*
+ * JournalTrimmer interfaces
+ */
+ journal_seq_t get_journal_head() const final { return dummy_tail; }
+
+ void set_journal_head(journal_seq_t) final {}
+
+ journal_seq_t get_dirty_tail() const final { return dummy_tail; }
+
+ journal_seq_t get_alloc_tail() const final { return dummy_tail; }
+
+ void update_journal_tails(journal_seq_t, journal_seq_t) final {}
+
+ bool try_reserve_inline_usage(std::size_t) final { return true; }
+
+ void release_inline_usage(std::size_t) final {}
+
+ std::size_t get_trim_size_per_cycle() const final {
+ return 0;
+ }
+
+ /*
+ * SegmentProvider interfaces
+ */
+ const segment_info_t& get_seg_info(segment_id_t id) const final {
+ tmp_info = {};
+ tmp_info.seq = segment_seqs.at(id);
+ tmp_info.type = segment_types.at(id);
+ return tmp_info;
+ }
+
+ segment_id_t allocate_segment(
+ segment_seq_t seq,
+ segment_type_t type,
+ data_category_t,
+ rewrite_gen_t
+ ) final {
+ auto ret = next;
+ next = segment_id_t{
+ segment_manager->get_device_id(),
+ next.device_segment_id() + 1};
+ segment_seqs[ret] = seq;
+ segment_types[ret] = type;
+ return ret;
+ }
+
+ void close_segment(segment_id_t) final {}
+
+ void update_segment_avail_bytes(segment_type_t, paddr_t) final {}
+
+ void update_modify_time(segment_id_t, sea_time_point, std::size_t) final {}
+
+ SegmentManagerGroup* get_segment_manager_group() final { return sms.get(); }
+
+ seastar::future<> set_up_fut() final {
+ segment_manager = segment_manager::create_test_ephemeral();
+ return segment_manager->init(
+ ).safe_then([this] {
+ return segment_manager->mkfs(
+ segment_manager::get_ephemeral_device_config(0, 1, 0));
+ }).safe_then([this] {
+ block_size = segment_manager->get_block_size();
+ sms.reset(new SegmentManagerGroup());
+ next = segment_id_t(segment_manager->get_device_id(), 0);
+ journal = journal::make_segmented(*this, *this);
+ journal->set_write_pipeline(&pipeline);
+ sms->add_segment_manager(segment_manager.get());
+ return journal->open_for_mkfs();
+ }).safe_then([this](auto) {
+ dummy_tail = journal_seq_t{0,
+ paddr_t::make_seg_paddr(segment_id_t(segment_manager->get_device_id(), 0), 0)};
+ }, crimson::ct_error::all_same_way([] {
+ ASSERT_FALSE("Unable to mount");
+ }));
+ }
+
+ seastar::future<> tear_down_fut() final {
+ return journal->close(
+ ).safe_then([this] {
+ segment_manager.reset();
+ sms.reset();
+ journal.reset();
+ }).handle_error(
+ crimson::ct_error::all_same_way([](auto e) {
+ ASSERT_FALSE("Unable to close");
+ })
+ );
+ }
+
+ template <typename T>
+ auto replay(T &&f) {
+ return journal->close(
+ ).safe_then([this, f=std::move(f)]() mutable {
+ journal = journal::make_segmented(*this, *this);
+ journal->set_write_pipeline(&pipeline);
+ return journal->replay(std::forward<T>(std::move(f)));
+ }).safe_then([this] {
+ return journal->open_for_mount();
+ });
+ }
+
+ auto replay_and_check() {
+ auto record_iter = records.begin();
+ decltype(record_iter->get_replay_handler()) delta_checker = std::nullopt;
+ auto advance = [this, &record_iter, &delta_checker] {
+ ceph_assert(!delta_checker);
+ while (record_iter != records.end()) {
+ auto checker = record_iter->get_replay_handler();
+ record_iter++;
+ if (checker) {
+ delta_checker.emplace(std::move(*checker));
+ break;
+ }
+ }
+ };
+ advance();
+ replay(
+ [&advance,
+ &delta_checker]
+ (const auto &offsets,
+ const auto &di,
+ const journal_seq_t &,
+ const journal_seq_t &,
+ auto t) mutable {
+ if (!delta_checker) {
+ EXPECT_FALSE("No Deltas Left");
+ }
+ if (!(*delta_checker)(offsets.record_block_base, di)) {
+ delta_checker = std::nullopt;
+ advance();
+ }
+ return Journal::replay_ertr::make_ready_future<bool>(true);
+ }).unsafe_get0();
+ ASSERT_EQ(record_iter, records.end());
+ for (auto &i : records) {
+ i.validate(*segment_manager);
+ }
+ }
+
+ template <typename... T>
+ auto submit_record(T&&... _record) {
+ auto record{std::forward<T>(_record)...};
+ records.push_back(record);
+ OrderingHandle handle = get_dummy_ordering_handle();
+ auto [addr, _] = journal->submit_record(
+ std::move(record),
+ handle).unsafe_get0();
+ records.back().record_final_offset = addr;
+ return addr;
+ }
+
+ extent_t generate_extent(size_t blocks) {
+ std::uniform_int_distribution<char> distribution(
+ std::numeric_limits<char>::min(),
+ std::numeric_limits<char>::max()
+ );
+ char contents = distribution(generator);
+ bufferlist bl;
+ bl.append(buffer::ptr(buffer::create(blocks * block_size, contents)));
+ return extent_t{
+ extent_types_t::TEST_BLOCK,
+ L_ADDR_NULL,
+ bl};
+ }
+
+ delta_info_t generate_delta(size_t bytes) {
+ std::uniform_int_distribution<char> distribution(
+ std::numeric_limits<char>::min(),
+ std::numeric_limits<char>::max()
+ );
+ char contents = distribution(generator);
+ bufferlist bl;
+ bl.append(buffer::ptr(buffer::create(bytes, contents)));
+ return delta_info_t{
+ extent_types_t::TEST_BLOCK,
+ paddr_t{},
+ L_ADDR_NULL,
+ 0, 0,
+ block_size,
+ 1,
+ MAX_SEG_SEQ,
+ segment_type_t::NULL_SEG,
+ bl
+ };
+ }
+};
+
+TEST_F(journal_test_t, replay_one_journal_segment)
+{
+ run_async([this] {
+ submit_record(record_t{
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(23), generate_delta(30) }
+ });
+ replay_and_check();
+ });
+}
+
+TEST_F(journal_test_t, replay_two_records)
+{
+ run_async([this] {
+ submit_record(record_t{
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(23), generate_delta(30) }
+ });
+ submit_record(record_t{
+ { generate_extent(4), generate_extent(1) },
+ { generate_delta(23), generate_delta(400) }
+ });
+ replay_and_check();
+ });
+}
+
+TEST_F(journal_test_t, replay_twice)
+{
+ run_async([this] {
+ submit_record(record_t{
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(23), generate_delta(30) }
+ });
+ submit_record(record_t{
+ { generate_extent(4), generate_extent(1) },
+ { generate_delta(23), generate_delta(400) }
+ });
+ replay_and_check();
+ submit_record(record_t{
+ { generate_extent(2), generate_extent(5) },
+ { generate_delta(230), generate_delta(40) }
+ });
+ replay_and_check();
+ });
+}
+
+TEST_F(journal_test_t, roll_journal_and_replay)
+{
+ run_async([this] {
+ paddr_t current = submit_record(
+ record_t{
+ { generate_extent(1), generate_extent(2) },
+ { generate_delta(23), generate_delta(30) }
+ });
+ auto starting_segment = current.as_seg_paddr().get_segment_id();
+ unsigned so_far = 0;
+ while (current.as_seg_paddr().get_segment_id() == starting_segment) {
+ current = submit_record(record_t{
+ { generate_extent(512), generate_extent(512) },
+ { generate_delta(23), generate_delta(400) }
+ });
+ ++so_far;
+ ASSERT_FALSE(so_far > 10);
+ }
+ replay_and_check();
+ });
+}
diff --git a/src/test/crimson/seastore/test_transaction_manager.cc b/src/test/crimson/seastore/test_transaction_manager.cc
new file mode 100644
index 000000000..1148884a0
--- /dev/null
+++ b/src/test/crimson/seastore/test_transaction_manager.cc
@@ -0,0 +1,1995 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <random>
+
+#include <boost/iterator/counting_iterator.hpp>
+
+#include "test/crimson/gtest_seastar.h"
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+
+#include "crimson/os/seastore/cache.h"
+#include "crimson/os/seastore/transaction_manager.h"
+#include "crimson/os/seastore/segment_manager/ephemeral.h"
+#include "crimson/os/seastore/segment_manager.h"
+
+#include "test/crimson/seastore/test_block.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+namespace {
+ [[maybe_unused]] seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+ }
+}
+
+struct test_extent_record_t {
+ test_extent_desc_t desc;
+ unsigned refcount = 0;
+ test_extent_record_t() = default;
+ test_extent_record_t(
+ const test_extent_desc_t &desc,
+ unsigned refcount) : desc(desc), refcount(refcount) {}
+
+ void update(const test_extent_desc_t &to) {
+ desc = to;
+ }
+
+ bool operator==(const test_extent_desc_t &rhs) const {
+ return desc == rhs;
+ }
+ bool operator!=(const test_extent_desc_t &rhs) const {
+ return desc != rhs;
+ }
+};
+
+template<>
+struct fmt::formatter<test_extent_record_t> : fmt::formatter<std::string_view> {
+ template <typename FormatContext>
+ auto format(const test_extent_record_t& r, FormatContext& ctx) const {
+ return fmt::format_to(ctx.out(), "test_extent_record_t({}, refcount={})",
+ r.desc, r.refcount);
+ }
+};
+
+struct transaction_manager_test_t :
+ public seastar_test_suite_t,
+ TMTestState {
+
+ std::random_device rd;
+ std::mt19937 gen;
+
+ transaction_manager_test_t(std::size_t num_main_devices, std::size_t num_cold_devices)
+ : TMTestState(num_main_devices, num_cold_devices), gen(rd()) {
+ }
+
+ laddr_t get_random_laddr(size_t block_size, laddr_t limit) {
+ return block_size *
+ std::uniform_int_distribution<>(0, (limit / block_size) - 1)(gen);
+ }
+
+ char get_random_contents() {
+ return static_cast<char>(std::uniform_int_distribution<>(0, 255)(gen));
+ }
+
+ seastar::future<> set_up_fut() final {
+ return tm_setup();
+ }
+
+ seastar::future<> tear_down_fut() final {
+ return tm_teardown();
+ }
+
+ struct test_extents_t : std::map<laddr_t, test_extent_record_t> {
+ using delta_t = std::map<laddr_t, std::optional<test_extent_record_t>>;
+ std::map<laddr_t, uint64_t> laddr_write_seq;
+
+ struct delta_overlay_t {
+ const test_extents_t &extents;
+ const delta_t &delta;
+
+ delta_overlay_t(
+ const test_extents_t &extents,
+ const delta_t &delta)
+ : extents(extents), delta(delta) {}
+
+
+ class iterator {
+ friend class test_extents_t;
+
+ const delta_overlay_t &parent;
+ test_extents_t::const_iterator biter;
+ delta_t::const_iterator oiter;
+ std::optional<std::pair<laddr_t, test_extent_record_t>> cur;
+
+ iterator(
+ const delta_overlay_t &parent,
+ test_extents_t::const_iterator biter,
+ delta_t::const_iterator oiter)
+ : parent(parent), biter(biter), oiter(oiter) {}
+
+ laddr_t get_bkey() {
+ return biter == parent.extents.end() ? L_ADDR_MAX : biter->first;
+ }
+
+ laddr_t get_okey() {
+ return oiter == parent.delta.end() ? L_ADDR_MAX : oiter->first;
+ }
+
+ bool is_end() {
+ return oiter == parent.delta.end() && biter == parent.extents.end();
+ }
+
+ bool is_valid() {
+ return is_end() ||
+ ((get_okey() < get_bkey()) && (oiter->second)) ||
+ (get_okey() > get_bkey());
+ }
+
+ auto get_pair() {
+ assert(is_valid());
+ assert(!is_end());
+ auto okey = get_okey();
+ auto bkey = get_bkey();
+ return (
+ bkey < okey ?
+ std::pair<laddr_t, test_extent_record_t>(*biter) :
+ std::make_pair(okey, *(oiter->second)));
+ }
+
+ void adjust() {
+ while (!is_valid()) {
+ if (get_okey() < get_bkey()) {
+ assert(!oiter->second);
+ ++oiter;
+ } else {
+ assert(get_okey() == get_bkey());
+ ++biter;
+ }
+ }
+ assert(is_valid());
+ if (!is_end()) {
+ cur = get_pair();
+ } else {
+ cur = std::nullopt;
+ }
+ }
+
+ public:
+ iterator(const iterator &) = default;
+ iterator(iterator &&) = default;
+
+ iterator &operator++() {
+ assert(is_valid());
+ assert(!is_end());
+ if (get_bkey() < get_okey()) {
+ ++biter;
+ } else {
+ ++oiter;
+ }
+ adjust();
+ return *this;
+ }
+
+ bool operator==(const iterator &o) const {
+ return o.biter == biter && o.oiter == oiter;
+ }
+ bool operator!=(const iterator &o) const {
+ return !(*this == o);
+ }
+
+ auto operator*() {
+ assert(!is_end());
+ return *cur;
+ }
+ auto operator->() {
+ assert(!is_end());
+ return &*cur;
+ }
+ };
+
+ iterator begin() {
+ auto ret = iterator{*this, extents.begin(), delta.begin()};
+ ret.adjust();
+ return ret;
+ }
+
+ iterator end() {
+ auto ret = iterator{*this, extents.end(), delta.end()};
+ // adjust unnecessary
+ return ret;
+ }
+
+ iterator lower_bound(laddr_t l) {
+ auto ret = iterator{*this, extents.lower_bound(l), delta.lower_bound(l)};
+ ret.adjust();
+ return ret;
+ }
+
+ iterator upper_bound(laddr_t l) {
+ auto ret = iterator{*this, extents.upper_bound(l), delta.upper_bound(l)};
+ ret.adjust();
+ return ret;
+ }
+
+ iterator find(laddr_t l) {
+ auto ret = lower_bound(l);
+ if (ret == end() || ret->first != l) {
+ return end();
+ } else {
+ return ret;
+ }
+ }
+ };
+ private:
+ void check_available(
+ laddr_t addr, extent_len_t len, const delta_t &delta
+ ) const {
+ delta_overlay_t overlay(*this, delta);
+ for (const auto &i: overlay) {
+ if (i.first < addr) {
+ EXPECT_FALSE(i.first + i.second.desc.len > addr);
+ } else {
+ EXPECT_FALSE(addr + len > i.first);
+ }
+ }
+ }
+
+ void check_hint(
+ laddr_t hint,
+ laddr_t addr,
+ extent_len_t len,
+ delta_t &delta) const {
+ delta_overlay_t overlay(*this, delta);
+ auto iter = overlay.lower_bound(hint);
+ laddr_t last = hint;
+ while (true) {
+ if (iter == overlay.end() || iter->first > addr) {
+ EXPECT_EQ(addr, last);
+ break;
+ }
+ EXPECT_FALSE(iter->first - last > len);
+ last = iter->first + iter->second.desc.len;
+ ++iter;
+ }
+ }
+
+ std::optional<test_extent_record_t> &populate_delta(
+ laddr_t addr, delta_t &delta, const test_extent_desc_t *desc) const {
+ auto diter = delta.find(addr);
+ if (diter != delta.end())
+ return diter->second;
+
+ auto iter = find(addr);
+ if (iter == end()) {
+ assert(desc);
+ auto ret = delta.emplace(
+ std::make_pair(addr, test_extent_record_t{*desc, 0}));
+ assert(ret.second);
+ return ret.first->second;
+ } else {
+ auto ret = delta.emplace(*iter);
+ assert(ret.second);
+ return ret.first->second;
+ }
+ }
+ public:
+ delta_overlay_t get_overlay(const delta_t &delta) const {
+ return delta_overlay_t{*this, delta};
+ }
+
+ void insert(TestBlock &extent, delta_t &delta) const {
+ check_available(extent.get_laddr(), extent.get_length(), delta);
+ delta[extent.get_laddr()] =
+ test_extent_record_t{extent.get_desc(), 1};
+ }
+
+ void alloced(laddr_t hint, TestBlock &extent, delta_t &delta) const {
+ check_hint(hint, extent.get_laddr(), extent.get_length(), delta);
+ insert(extent, delta);
+ }
+
+ bool contains(laddr_t addr, const delta_t &delta) const {
+ delta_overlay_t overlay(*this, delta);
+ return overlay.find(addr) != overlay.end();
+ }
+
+ test_extent_record_t get(laddr_t addr, const delta_t &delta) const {
+ delta_overlay_t overlay(*this, delta);
+ auto iter = overlay.find(addr);
+ assert(iter != overlay.end());
+ return iter->second;
+ }
+
+ void update(
+ laddr_t addr,
+ const test_extent_desc_t &desc,
+ delta_t &delta) const {
+ auto &rec = populate_delta(addr, delta, &desc);
+ assert(rec);
+ rec->desc = desc;
+ }
+
+ int inc_ref(
+ laddr_t addr,
+ delta_t &delta) const {
+ auto &rec = populate_delta(addr, delta, nullptr);
+ assert(rec);
+ return ++rec->refcount;
+ }
+
+ int dec_ref(
+ laddr_t addr,
+ delta_t &delta) const {
+ auto &rec = populate_delta(addr, delta, nullptr);
+ assert(rec);
+ assert(rec->refcount > 0);
+ rec->refcount--;
+ if (rec->refcount == 0) {
+ delta[addr] = std::nullopt;
+ return 0;
+ } else {
+ return rec->refcount;
+ }
+ }
+
+ void consume(const delta_t &delta, const uint64_t write_seq = 0) {
+ for (const auto &i : delta) {
+ if (i.second) {
+ if (laddr_write_seq.find(i.first) == laddr_write_seq.end() ||
+ laddr_write_seq[i.first] <= write_seq) {
+ (*this)[i.first] = *i.second;
+ laddr_write_seq[i.first] = write_seq;
+ }
+ } else {
+ erase(i.first);
+ }
+ }
+ }
+
+ } test_mappings;
+
+ struct test_transaction_t {
+ TransactionRef t;
+ test_extents_t::delta_t mapping_delta;
+ };
+
+ test_transaction_t create_transaction() {
+ return { create_mutate_transaction(), {} };
+ }
+
+ test_transaction_t create_read_test_transaction() {
+ return {create_read_transaction(), {} };
+ }
+
+ test_transaction_t create_weak_test_transaction() {
+ return { create_weak_transaction(), {} };
+ }
+
+ TestBlockRef alloc_extent(
+ test_transaction_t &t,
+ laddr_t hint,
+ extent_len_t len,
+ char contents) {
+ auto extent = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->alloc_extent<TestBlock>(trans, hint, len);
+ }).unsafe_get0();
+ extent->set_contents(contents);
+ EXPECT_FALSE(test_mappings.contains(extent->get_laddr(), t.mapping_delta));
+ EXPECT_EQ(len, extent->get_length());
+ test_mappings.alloced(hint, *extent, t.mapping_delta);
+ return extent;
+ }
+
+ TestBlockRef alloc_extent(
+ test_transaction_t &t,
+ laddr_t hint,
+ extent_len_t len) {
+ return alloc_extent(
+ t,
+ hint,
+ len,
+ get_random_contents());
+ }
+
+ bool check_usage() {
+ return epm->check_usage();
+ }
+
+ void replay() {
+ EXPECT_TRUE(check_usage());
+ restart();
+ }
+
+ void check() {
+ check_mappings();
+ check_usage();
+ }
+
+ void check_mappings() {
+ auto t = create_weak_test_transaction();
+ check_mappings(t);
+ }
+
+ TestBlockRef get_extent(
+ test_transaction_t &t,
+ laddr_t addr,
+ extent_len_t len) {
+ ceph_assert(test_mappings.contains(addr, t.mapping_delta));
+ ceph_assert(test_mappings.get(addr, t.mapping_delta).desc.len == len);
+
+ auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->read_extent<TestBlock>(trans, addr, len);
+ }).unsafe_get0();
+ EXPECT_EQ(addr, ext->get_laddr());
+ return ext;
+ }
+
+ TestBlockRef try_get_extent(
+ test_transaction_t &t,
+ laddr_t addr) {
+ ceph_assert(test_mappings.contains(addr, t.mapping_delta));
+
+ using ertr = with_trans_ertr<TransactionManager::read_extent_iertr>;
+ using ret = ertr::future<TestBlockRef>;
+ auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->read_extent<TestBlock>(trans, addr);
+ }).safe_then([](auto ext) -> ret {
+ return ertr::make_ready_future<TestBlockRef>(ext);
+ }).handle_error(
+ [](const crimson::ct_error::eagain &e) {
+ return seastar::make_ready_future<TestBlockRef>();
+ },
+ crimson::ct_error::assert_all{
+ "get_extent got invalid error"
+ }
+ ).get0();
+ if (ext) {
+ EXPECT_EQ(addr, ext->get_laddr());
+ }
+ return ext;
+ }
+
+ TestBlockRef try_get_extent(
+ test_transaction_t &t,
+ laddr_t addr,
+ extent_len_t len) {
+ ceph_assert(test_mappings.contains(addr, t.mapping_delta));
+ ceph_assert(test_mappings.get(addr, t.mapping_delta).desc.len == len);
+
+ using ertr = with_trans_ertr<TransactionManager::read_extent_iertr>;
+ using ret = ertr::future<TestBlockRef>;
+ auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->read_extent<TestBlock>(trans, addr, len);
+ }).safe_then([](auto ext) -> ret {
+ return ertr::make_ready_future<TestBlockRef>(ext);
+ }).handle_error(
+ [](const crimson::ct_error::eagain &e) {
+ return seastar::make_ready_future<TestBlockRef>();
+ },
+ crimson::ct_error::assert_all{
+ "get_extent got invalid error"
+ }
+ ).get0();
+ if (ext) {
+ EXPECT_EQ(addr, ext->get_laddr());
+ }
+ return ext;
+ }
+
+ TestBlockRef try_read_pin(
+ test_transaction_t &t,
+ LBAMappingRef &&pin) {
+ using ertr = with_trans_ertr<TransactionManager::base_iertr>;
+ using ret = ertr::future<TestBlockRef>;
+ auto addr = pin->get_key();
+ auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->read_pin<TestBlock>(trans, std::move(pin));
+ }).safe_then([](auto ext) -> ret {
+ return ertr::make_ready_future<TestBlockRef>(ext);
+ }).handle_error(
+ [](const crimson::ct_error::eagain &e) {
+ return seastar::make_ready_future<TestBlockRef>();
+ },
+ crimson::ct_error::assert_all{
+ "read_pin got invalid error"
+ }
+ ).get0();
+ if (ext) {
+ EXPECT_EQ(addr, ext->get_laddr());
+ }
+ if (t.t->is_conflicted()) {
+ return nullptr;
+ }
+ return ext;
+ }
+
+ test_block_mutator_t mutator;
+ TestBlockRef mutate_extent(
+ test_transaction_t &t,
+ TestBlockRef ref) {
+ ceph_assert(test_mappings.contains(ref->get_laddr(), t.mapping_delta));
+ ceph_assert(
+ test_mappings.get(ref->get_laddr(), t.mapping_delta).desc.len ==
+ ref->get_length());
+
+ auto ext = tm->get_mutable_extent(*t.t, ref)->cast<TestBlock>();
+ EXPECT_EQ(ext->get_laddr(), ref->get_laddr());
+ EXPECT_EQ(ext->get_desc(), ref->get_desc());
+ mutator.mutate(*ext, gen);
+
+ test_mappings.update(ext->get_laddr(), ext->get_desc(), t.mapping_delta);
+ return ext;
+ }
+
+ TestBlockRef mutate_addr(
+ test_transaction_t &t,
+ laddr_t offset,
+ size_t length) {
+ auto ext = get_extent(t, offset, length);
+ mutate_extent(t, ext);
+ return ext;
+ }
+
+ LBAMappingRef get_pin(
+ test_transaction_t &t,
+ laddr_t offset) {
+ ceph_assert(test_mappings.contains(offset, t.mapping_delta));
+ auto pin = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->get_pin(trans, offset);
+ }).unsafe_get0();
+ EXPECT_EQ(offset, pin->get_key());
+ return pin;
+ }
+
+ LBAMappingRef try_get_pin(
+ test_transaction_t &t,
+ laddr_t offset) {
+ ceph_assert(test_mappings.contains(offset, t.mapping_delta));
+ using ertr = with_trans_ertr<TransactionManager::get_pin_iertr>;
+ using ret = ertr::future<LBAMappingRef>;
+ auto pin = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->get_pin(trans, offset);
+ }).safe_then([](auto pin) -> ret {
+ return ertr::make_ready_future<LBAMappingRef>(std::move(pin));
+ }).handle_error(
+ [](const crimson::ct_error::eagain &e) {
+ return seastar::make_ready_future<LBAMappingRef>();
+ },
+ crimson::ct_error::assert_all{
+ "get_extent got invalid error"
+ }
+ ).get0();
+ if (pin) {
+ EXPECT_EQ(offset, pin->get_key());
+ }
+ return pin;
+ }
+
+ void inc_ref(test_transaction_t &t, laddr_t offset) {
+ ceph_assert(test_mappings.contains(offset, t.mapping_delta));
+ ceph_assert(test_mappings.get(offset, t.mapping_delta).refcount > 0);
+
+ auto refcnt = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->inc_ref(trans, offset);
+ }).unsafe_get0();
+ auto check_refcnt = test_mappings.inc_ref(offset, t.mapping_delta);
+ EXPECT_EQ(refcnt, check_refcnt);
+ }
+
+ void dec_ref(test_transaction_t &t, laddr_t offset) {
+ ceph_assert(test_mappings.contains(offset, t.mapping_delta));
+ ceph_assert(test_mappings.get(offset, t.mapping_delta).refcount > 0);
+
+ auto refcnt = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->dec_ref(trans, offset);
+ }).unsafe_get0();
+ auto check_refcnt = test_mappings.dec_ref(offset, t.mapping_delta);
+ EXPECT_EQ(refcnt, check_refcnt);
+ if (refcnt == 0)
+ logger().debug("dec_ref: {} at refcount 0", offset);
+ }
+
+ void check_mappings(test_transaction_t &t) {
+ auto overlay = test_mappings.get_overlay(t.mapping_delta);
+ for (const auto &i: overlay) {
+ logger().debug("check_mappings: {}->{}", i.first, i.second);
+ auto ext = get_extent(t, i.first, i.second.desc.len);
+ EXPECT_EQ(i.second, ext->get_desc());
+ }
+ with_trans_intr(
+ *t.t,
+ [this, &overlay](auto &t) {
+ return lba_manager->scan_mappings(
+ t,
+ 0,
+ L_ADDR_MAX,
+ [iter=overlay.begin(), &overlay](auto l, auto p, auto len) mutable {
+ EXPECT_NE(iter, overlay.end());
+ logger().debug(
+ "check_mappings: scan {}",
+ l);
+ EXPECT_EQ(l, iter->first);
+ ++iter;
+ });
+ }).unsafe_get0();
+ (void)with_trans_intr(
+ *t.t,
+ [=, this](auto &t) {
+ return lba_manager->check_child_trackers(t);
+ }).unsafe_get0();
+ }
+
+ bool try_submit_transaction(test_transaction_t t) {
+ using ertr = with_trans_ertr<TransactionManager::submit_transaction_iertr>;
+ using ret = ertr::future<bool>;
+ uint64_t write_seq = 0;
+ bool success = submit_transaction_fut_with_seq(*t.t
+ ).safe_then([&write_seq](auto seq) -> ret {
+ write_seq = seq;
+ return ertr::make_ready_future<bool>(true);
+ }).handle_error(
+ [](const crimson::ct_error::eagain &e) {
+ return seastar::make_ready_future<bool>(false);
+ },
+ crimson::ct_error::assert_all{
+ "try_submit_transaction hit invalid error"
+ }
+ ).then([this](auto ret) {
+ return epm->run_background_work_until_halt(
+ ).then([ret] { return ret; });
+ }).get0();
+
+ if (success) {
+ test_mappings.consume(t.mapping_delta, write_seq);
+ }
+
+ return success;
+ }
+
+ void submit_transaction(test_transaction_t &&t) {
+ bool success = try_submit_transaction(std::move(t));
+ EXPECT_TRUE(success);
+ }
+
+ void submit_transaction_expect_conflict(test_transaction_t &&t) {
+ bool success = try_submit_transaction(std::move(t));
+ EXPECT_FALSE(success);
+ }
+
+ auto allocate_sequentially(const size_t size, const int num, bool run_clean = true) {
+ return repeat_eagain([this, size, num] {
+ return seastar::do_with(
+ create_transaction(),
+ [this, size, num](auto &t) {
+ return with_trans_intr(
+ *t.t,
+ [&t, this, size, num](auto &) {
+ return trans_intr::do_for_each(
+ boost::make_counting_iterator(0),
+ boost::make_counting_iterator(num),
+ [&t, this, size](auto) {
+ return tm->alloc_extent<TestBlock>(
+ *(t.t), L_ADDR_MIN, size
+ ).si_then([&t, this, size](auto extent) {
+ extent->set_contents(get_random_contents());
+ EXPECT_FALSE(
+ test_mappings.contains(extent->get_laddr(), t.mapping_delta));
+ EXPECT_EQ(size, extent->get_length());
+ test_mappings.alloced(extent->get_laddr(), *extent, t.mapping_delta);
+ return seastar::now();
+ });
+ }).si_then([&t, this] {
+ return tm->submit_transaction(*t.t);
+ });
+ }).safe_then([&t, this] {
+ test_mappings.consume(t.mapping_delta);
+ });
+ });
+ }).safe_then([this, run_clean]() {
+ if (run_clean) {
+ return epm->run_background_work_until_halt();
+ } else {
+ return epm->background_process.trimmer->trim();
+ }
+ }).handle_error(
+ crimson::ct_error::assert_all{
+ "Invalid error in SeaStore::list_collections"
+ }
+ );
+ }
+
+ void test_parallel_extent_read() {
+ constexpr size_t TOTAL = 4<<20;
+ constexpr size_t BSIZE = 4<<10;
+ constexpr size_t BLOCKS = TOTAL / BSIZE;
+ run_async([this] {
+ for (unsigned i = 0; i < BLOCKS; ++i) {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ i * BSIZE,
+ BSIZE);
+ ASSERT_EQ(i * BSIZE, extent->get_laddr());
+ submit_transaction(std::move(t));
+ }
+
+ seastar::do_with(
+ create_read_test_transaction(),
+ [this](auto &t) {
+ return with_trans_intr(*(t.t), [this](auto &t) {
+ return trans_intr::parallel_for_each(
+ boost::make_counting_iterator(0lu),
+ boost::make_counting_iterator(BLOCKS),
+ [this, &t](auto i) {
+ return tm->read_extent<TestBlock>(t, i * BSIZE, BSIZE
+ ).si_then([](auto) {
+ return seastar::now();
+ });
+ });
+ });
+ }).unsafe_get0();
+ });
+ }
+
+ void test_random_writes_concurrent() {
+ constexpr unsigned WRITE_STREAMS = 256;
+
+ constexpr size_t TOTAL = 4<<20;
+ constexpr size_t BSIZE = 4<<10;
+ constexpr size_t BLOCKS = TOTAL / BSIZE;
+ run_async([this] {
+ std::for_each(
+ boost::make_counting_iterator(0u),
+ boost::make_counting_iterator(WRITE_STREAMS),
+ [&](auto idx) {
+ for (unsigned i = idx; i < BLOCKS; i += WRITE_STREAMS) {
+ while (true) {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ i * BSIZE,
+ BSIZE);
+ ASSERT_EQ(i * BSIZE, extent->get_laddr());
+ if (try_submit_transaction(std::move(t)))
+ break;
+ }
+ }
+ });
+
+ int writes = 0;
+ unsigned failures = 0;
+ seastar::parallel_for_each(
+ boost::make_counting_iterator(0u),
+ boost::make_counting_iterator(WRITE_STREAMS),
+ [&](auto) {
+ return seastar::async([&] {
+ while (writes < 300) {
+ auto t = create_transaction();
+ auto ext = try_get_extent(
+ t,
+ get_random_laddr(BSIZE, TOTAL),
+ BSIZE);
+ if (!ext){
+ failures++;
+ continue;
+ }
+ auto mut = mutate_extent(t, ext);
+ auto success = try_submit_transaction(std::move(t));
+ writes += success;
+ failures += !success;
+ }
+ });
+ }).get0();
+ replay();
+ logger().info("random_writes_concurrent: checking");
+ check();
+ logger().info(
+ "random_writes_concurrent: {} suceeded, {} failed",
+ writes,
+ failures
+ );
+ });
+ }
+
+ void test_evict() {
+ // only support segmented backend currently
+ ASSERT_EQ(epm->get_main_backend_type(), backend_type_t::SEGMENTED);
+ ASSERT_TRUE(epm->background_process.has_cold_tier());
+ constexpr size_t device_size =
+ segment_manager::DEFAULT_TEST_EPHEMERAL.size;
+ constexpr size_t block_size =
+ segment_manager::DEFAULT_TEST_EPHEMERAL.block_size;
+ constexpr size_t segment_size =
+ segment_manager::DEFAULT_TEST_EPHEMERAL.segment_size;
+ ASSERT_GE(segment_size, block_size * 20);
+
+ run_async([this] {
+ // indicates there is no available segments to reclaim
+ double stop_ratio = (double)segment_size / (double)device_size / 2;
+ // 1 segment
+ double default_ratio = stop_ratio * 2;
+ // 1.25 segment
+ double fast_ratio = stop_ratio * 2.5;
+
+ epm->background_process
+ .eviction_state
+ .init(stop_ratio, default_ratio, fast_ratio);
+
+ // these variables are described in
+ // EPM::BackgroundProcess::eviction_state_t::maybe_update_eviction_mode
+ size_t ratio_A_size = segment_size / 2 - block_size * 10;
+ size_t ratio_B_size = segment_size / 2 + block_size * 10;
+ size_t ratio_C_size = segment_size + block_size;
+ size_t ratio_D_size = segment_size * 1.25 + block_size;
+
+ auto run_until = [this](size_t size) -> seastar::future<> {
+ return seastar::repeat([this, size] {
+ size_t current_size = epm->background_process
+ .main_cleaner->get_stat().data_stored;
+ if (current_size >= size) {
+ return seastar::futurize_invoke([] {
+ return seastar::stop_iteration::yes;
+ });
+ } else {
+ int num = (size - current_size) / block_size;
+ return seastar::do_for_each(
+ boost::make_counting_iterator(0),
+ boost::make_counting_iterator(num),
+ [this](auto) {
+ // don't start background process to test the behavior
+ // of generation changes during alloc new extents
+ return allocate_sequentially(block_size, 1, false);
+ }).then([] {
+ return seastar::stop_iteration::no;
+ });
+ }
+ });
+ };
+
+ std::vector<extent_types_t> all_extent_types{
+ extent_types_t::ROOT,
+ extent_types_t::LADDR_INTERNAL,
+ extent_types_t::LADDR_LEAF,
+ extent_types_t::OMAP_INNER,
+ extent_types_t::OMAP_LEAF,
+ extent_types_t::ONODE_BLOCK_STAGED,
+ extent_types_t::COLL_BLOCK,
+ extent_types_t::OBJECT_DATA_BLOCK,
+ extent_types_t::RETIRED_PLACEHOLDER,
+ extent_types_t::ALLOC_INFO,
+ extent_types_t::JOURNAL_TAIL,
+ extent_types_t::TEST_BLOCK,
+ extent_types_t::TEST_BLOCK_PHYSICAL,
+ extent_types_t::BACKREF_INTERNAL,
+ extent_types_t::BACKREF_LEAF
+ };
+
+ std::vector<rewrite_gen_t> all_generations;
+ for (auto i = INIT_GENERATION; i < REWRITE_GENERATIONS; i++) {
+ all_generations.push_back(i);
+ }
+
+ // input target-generation -> expected generation after the adjustment
+ using generation_mapping_t = std::map<rewrite_gen_t, rewrite_gen_t>;
+ std::map<extent_types_t, generation_mapping_t> expected_generations;
+
+ // this loop should be consistent with EPM::adjust_generation
+ for (auto t : all_extent_types) {
+ expected_generations[t] = {};
+ if (!is_logical_type(t)) {
+ for (auto gen : all_generations) {
+ expected_generations[t][gen] = INLINE_GENERATION;
+ }
+ } else {
+ if (get_extent_category(t) == data_category_t::METADATA) {
+ expected_generations[t][INIT_GENERATION] = INLINE_GENERATION;
+ } else {
+ expected_generations[t][INIT_GENERATION] = OOL_GENERATION;
+ }
+
+ for (auto i = INIT_GENERATION + 1; i < REWRITE_GENERATIONS; i++) {
+ expected_generations[t][i] = i;
+ }
+ }
+ }
+
+ auto update_data_gen_mapping = [&](std::function<rewrite_gen_t(rewrite_gen_t)> func) {
+ for (auto t : all_extent_types) {
+ if (!is_logical_type(t)) {
+ continue;
+ }
+ for (auto i = INIT_GENERATION + 1; i < REWRITE_GENERATIONS; i++) {
+ expected_generations[t][i] = func(i);
+ }
+ }
+ // since background process didn't start in allocate_sequentially
+ // we update eviction mode manually.
+ epm->background_process.maybe_update_eviction_mode();
+ };
+
+ auto test_gen = [&](const char *caller) {
+ for (auto t : all_extent_types) {
+ for (auto gen : all_generations) {
+ auto epm_gen = epm->adjust_generation(
+ get_extent_category(t),
+ t,
+ placement_hint_t::HOT,
+ gen);
+ if (expected_generations[t][gen] != epm_gen) {
+ logger().error("caller: {}, extent type: {}, input generation: {}, "
+ "expected generation : {}, adjust result from EPM: {}",
+ caller, t, gen, expected_generations[t][gen], epm_gen);
+ }
+ EXPECT_EQ(expected_generations[t][gen], epm_gen);
+ }
+ }
+ };
+
+ // verify that no data should go to the cold tier
+ update_data_gen_mapping([](rewrite_gen_t gen) -> rewrite_gen_t {
+ if (gen == MIN_COLD_GENERATION) {
+ return MIN_COLD_GENERATION - 1;
+ } else {
+ return gen;
+ }
+ });
+ test_gen("init");
+
+ run_until(ratio_A_size).get();
+ EXPECT_TRUE(epm->background_process.eviction_state.is_stop_mode());
+ test_gen("exceed ratio A");
+ epm->run_background_work_until_halt().get();
+
+ run_until(ratio_B_size).get();
+ EXPECT_TRUE(epm->background_process.eviction_state.is_stop_mode());
+ test_gen("exceed ratio B");
+ epm->run_background_work_until_halt().get();
+
+ // verify that data may go to the cold tier
+ run_until(ratio_C_size).get();
+ update_data_gen_mapping([](rewrite_gen_t gen) { return gen; });
+ EXPECT_TRUE(epm->background_process.eviction_state.is_default_mode());
+ test_gen("exceed ratio C");
+ epm->run_background_work_until_halt().get();
+
+ // verify that data must go to the cold tier
+ run_until(ratio_D_size).get();
+ update_data_gen_mapping([](rewrite_gen_t gen) {
+ if (gen >= MIN_REWRITE_GENERATION && gen < MIN_COLD_GENERATION) {
+ return MIN_COLD_GENERATION;
+ } else {
+ return gen;
+ }
+ });
+ EXPECT_TRUE(epm->background_process.eviction_state.is_fast_mode());
+ test_gen("exceed ratio D");
+
+ auto main_size = epm->background_process.main_cleaner->get_stat().data_stored;
+ auto cold_size = epm->background_process.cold_cleaner->get_stat().data_stored;
+ EXPECT_EQ(cold_size, 0);
+ epm->run_background_work_until_halt().get();
+ auto new_main_size = epm->background_process.main_cleaner->get_stat().data_stored;
+ auto new_cold_size = epm->background_process.cold_cleaner->get_stat().data_stored;
+ EXPECT_GE(main_size, new_main_size);
+ EXPECT_NE(new_cold_size, 0);
+
+ update_data_gen_mapping([](rewrite_gen_t gen) { return gen; });
+ EXPECT_TRUE(epm->background_process.eviction_state.is_default_mode());
+ test_gen("finish evict");
+ });
+ }
+
+ using remap_entry = TransactionManager::remap_entry;
+ LBAMappingRef remap_pin(
+ test_transaction_t &t,
+ LBAMappingRef &&opin,
+ extent_len_t new_offset,
+ extent_len_t new_len) {
+ if (t.t->is_conflicted()) {
+ return nullptr;
+ }
+ auto o_laddr = opin->get_key();
+ auto pin = with_trans_intr(*(t.t), [&](auto& trans) {
+ return tm->remap_pin<TestBlock>(
+ trans, std::move(opin), std::array{
+ remap_entry(new_offset, new_len)}
+ ).si_then([](auto ret) {
+ return std::move(ret[0]);
+ });
+ }).handle_error(crimson::ct_error::eagain::handle([] {
+ LBAMappingRef t = nullptr;
+ return t;
+ }), crimson::ct_error::pass_further_all{}).unsafe_get0();
+ if (t.t->is_conflicted()) {
+ return nullptr;
+ }
+ test_mappings.dec_ref(o_laddr, t.mapping_delta);
+ EXPECT_FALSE(test_mappings.contains(o_laddr, t.mapping_delta));
+ EXPECT_TRUE(pin);
+ EXPECT_EQ(pin->get_length(), new_len);
+ EXPECT_EQ(pin->get_key(), o_laddr + new_offset);
+
+ auto extent = try_read_pin(t, pin->duplicate());
+ if (extent) {
+ test_mappings.alloced(pin->get_key(), *extent, t.mapping_delta);
+ EXPECT_TRUE(extent->is_exist_clean());
+ } else {
+ ceph_assert(t.t->is_conflicted());
+ return nullptr;
+ }
+ return pin;
+ }
+
+ using _overwrite_pin_iertr = TransactionManager::get_pin_iertr;
+ using _overwrite_pin_ret = _overwrite_pin_iertr::future<
+ std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>;
+ _overwrite_pin_ret _overwrite_pin(
+ Transaction &t,
+ LBAMappingRef &&opin,
+ extent_len_t new_offset,
+ extent_len_t new_len,
+ ceph::bufferlist &bl) {
+ auto o_laddr = opin->get_key();
+ auto o_len = opin->get_length();
+ if (new_offset != 0 && o_len != new_offset + new_len) {
+ return tm->remap_pin<TestBlock, 2>(
+ t,
+ std::move(opin),
+ std::array{
+ remap_entry(
+ 0,
+ new_offset),
+ remap_entry(
+ new_offset + new_len,
+ o_len - new_offset - new_len)
+ }
+ ).si_then([this, new_offset, new_len, o_laddr, &t, &bl](auto ret) {
+ return tm->alloc_extent<TestBlock>(t, o_laddr + new_offset, new_len
+ ).si_then([this, ret = std::move(ret), new_len,
+ new_offset, o_laddr, &t, &bl](auto ext) mutable {
+ ceph_assert(ret.size() == 2);
+ auto iter = bl.cbegin();
+ iter.copy(new_len, ext->get_bptr().c_str());
+ auto r_laddr = o_laddr + new_offset + new_len;
+ // old pins expired after alloc new extent, need to get it.
+ return tm->get_pin(t, o_laddr
+ ).si_then([this, &t, ext = std::move(ext), r_laddr](auto lpin) mutable {
+ return tm->get_pin(t, r_laddr
+ ).si_then([lpin = std::move(lpin), ext = std::move(ext)]
+ (auto rpin) mutable {
+ return _overwrite_pin_iertr::make_ready_future<
+ std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
+ std::make_tuple(
+ std::move(lpin), std::move(ext), std::move(rpin)));
+ });
+ });
+ });
+ });
+ } else if (new_offset == 0 && o_len != new_offset + new_len) {
+ return tm->remap_pin<TestBlock, 1>(
+ t,
+ std::move(opin),
+ std::array{
+ remap_entry(
+ new_offset + new_len,
+ o_len - new_offset - new_len)
+ }
+ ).si_then([this, new_offset, new_len, o_laddr, &t, &bl](auto ret) {
+ return tm->alloc_extent<TestBlock>(t, o_laddr + new_offset, new_len
+ ).si_then([this, ret = std::move(ret), new_offset, new_len,
+ o_laddr, &t, &bl](auto ext) mutable {
+ ceph_assert(ret.size() == 1);
+ auto iter = bl.cbegin();
+ iter.copy(new_len, ext->get_bptr().c_str());
+ auto r_laddr = o_laddr + new_offset + new_len;
+ return tm->get_pin(t, r_laddr
+ ).si_then([ext = std::move(ext)](auto rpin) mutable {
+ return _overwrite_pin_iertr::make_ready_future<
+ std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
+ std::make_tuple(
+ nullptr, std::move(ext), std::move(rpin)));
+ });
+ });
+ });
+ } else if (new_offset != 0 && o_len == new_offset + new_len) {
+ return tm->remap_pin<TestBlock, 1>(
+ t,
+ std::move(opin),
+ std::array{
+ remap_entry(
+ 0,
+ new_offset)
+ }
+ ).si_then([this, new_offset, new_len, o_laddr, &t, &bl](auto ret) {
+ return tm->alloc_extent<TestBlock>(t, o_laddr + new_offset, new_len
+ ).si_then([this, ret = std::move(ret), new_len, o_laddr, &t, &bl]
+ (auto ext) mutable {
+ ceph_assert(ret.size() == 1);
+ auto iter = bl.cbegin();
+ iter.copy(new_len, ext->get_bptr().c_str());
+ return tm->get_pin(t, o_laddr
+ ).si_then([ext = std::move(ext)](auto lpin) mutable {
+ return _overwrite_pin_iertr::make_ready_future<
+ std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
+ std::make_tuple(
+ std::move(lpin), std::move(ext), nullptr));
+ });
+ });
+ });
+ } else {
+ ceph_abort("impossible");
+ return _overwrite_pin_iertr::make_ready_future<
+ std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
+ std::make_tuple(nullptr, nullptr, nullptr));
+ }
+ }
+
+ using overwrite_pin_ret = std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>;
+ overwrite_pin_ret overwrite_pin(
+ test_transaction_t &t,
+ LBAMappingRef &&opin,
+ extent_len_t new_offset,
+ extent_len_t new_len,
+ ceph::bufferlist &bl) {
+ if (t.t->is_conflicted()) {
+ return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
+ nullptr, nullptr, nullptr);
+ }
+ auto o_laddr = opin->get_key();
+ auto o_paddr = opin->get_val();
+ auto o_len = opin->get_length();
+ auto res = with_trans_intr(*(t.t), [&](auto& trans) {
+ return _overwrite_pin(
+ trans, std::move(opin), new_offset, new_len, bl);
+ }).handle_error(crimson::ct_error::eagain::handle([] {
+ return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
+ nullptr, nullptr, nullptr);
+ }), crimson::ct_error::pass_further_all{}).unsafe_get0();
+ if (t.t->is_conflicted()) {
+ return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
+ nullptr, nullptr, nullptr);
+ }
+ test_mappings.dec_ref(o_laddr, t.mapping_delta);
+ EXPECT_FALSE(test_mappings.contains(o_laddr, t.mapping_delta));
+ auto &[lpin, ext, rpin] = res;
+
+ EXPECT_TRUE(ext);
+ EXPECT_TRUE(lpin || rpin);
+ EXPECT_TRUE(o_len > ext->get_length());
+ if (lpin) {
+ EXPECT_EQ(lpin->get_key(), o_laddr);
+ EXPECT_EQ(lpin->get_val(), o_paddr);
+ EXPECT_EQ(lpin->get_length(), new_offset);
+ auto lext = try_read_pin(t, lpin->duplicate());
+ if (lext) {
+ test_mappings.alloced(lpin->get_key(), *lext, t.mapping_delta);
+ EXPECT_TRUE(lext->is_exist_clean());
+ } else {
+ ceph_assert(t.t->is_conflicted());
+ return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
+ nullptr, nullptr, nullptr);
+ }
+ }
+ EXPECT_EQ(ext->get_laddr(), o_laddr + new_offset);
+ EXPECT_EQ(ext->get_length(), new_len);
+ test_mappings.alloced(ext->get_laddr(), *ext, t.mapping_delta);
+ if (rpin) {
+ EXPECT_EQ(rpin->get_key(), o_laddr + new_offset + new_len);
+ EXPECT_EQ(rpin->get_val(), o_paddr.add_offset(new_offset)
+ .add_offset(new_len));
+ EXPECT_EQ(rpin->get_length(), o_len - new_offset - new_len);
+ auto rext = try_read_pin(t, rpin->duplicate());
+ if (rext) {
+ test_mappings.alloced(rpin->get_key(), *rext, t.mapping_delta);
+ EXPECT_TRUE(rext->is_exist_clean());
+ } else {
+ ceph_assert(t.t->is_conflicted());
+ return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
+ nullptr, nullptr, nullptr);
+ }
+ }
+ return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
+ std::move(lpin), std::move(ext), std::move(rpin));
+ }
+
+ void test_remap_pin() {
+ run_async([this] {
+ constexpr size_t l_offset = 32 << 10;
+ constexpr size_t l_len = 32 << 10;
+ constexpr size_t r_offset = 64 << 10;
+ constexpr size_t r_len = 32 << 10;
+ {
+ auto t = create_transaction();
+ auto lext = alloc_extent(t, l_offset, l_len);
+ lext->set_contents('l', 0, 16 << 10);
+ auto rext = alloc_extent(t, r_offset, r_len);
+ rext->set_contents('r', 16 << 10, 16 << 10);
+ submit_transaction(std::move(t));
+ }
+ {
+ auto t = create_transaction();
+ auto lpin = get_pin(t, l_offset);
+ auto rpin = get_pin(t, r_offset);
+ //split left
+ auto pin1 = remap_pin(t, std::move(lpin), 0, 16 << 10);
+ ASSERT_TRUE(pin1);
+ auto pin2 = remap_pin(t, std::move(pin1), 0, 8 << 10);
+ ASSERT_TRUE(pin2);
+ auto pin3 = remap_pin(t, std::move(pin2), 0, 4 << 10);
+ ASSERT_TRUE(pin3);
+ auto lext = get_extent(t, pin3->get_key(), pin3->get_length());
+ EXPECT_EQ('l', lext->get_bptr().c_str()[0]);
+ auto mlext = mutate_extent(t, lext);
+ ASSERT_TRUE(mlext->is_exist_mutation_pending());
+ ASSERT_TRUE(mlext.get() == lext.get());
+
+ //split right
+ auto pin4 = remap_pin(t, std::move(rpin), 16 << 10, 16 << 10);
+ ASSERT_TRUE(pin4);
+ auto pin5 = remap_pin(t, std::move(pin4), 8 << 10, 8 << 10);
+ ASSERT_TRUE(pin5);
+ auto pin6 = remap_pin(t, std::move(pin5), 4 << 10, 4 << 10);
+ ASSERT_TRUE(pin6);
+ auto rext = get_extent(t, pin6->get_key(), pin6->get_length());
+ EXPECT_EQ('r', rext->get_bptr().c_str()[0]);
+ auto mrext = mutate_extent(t, rext);
+ ASSERT_TRUE(mrext->is_exist_mutation_pending());
+ ASSERT_TRUE(mrext.get() == rext.get());
+
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ check();
+ });
+ }
+
+ void test_overwrite_pin() {
+ run_async([this] {
+ constexpr size_t m_offset = 8 << 10;
+ constexpr size_t m_len = 56 << 10;
+ constexpr size_t l_offset = 64 << 10;
+ constexpr size_t l_len = 64 << 10;
+ constexpr size_t r_offset = 128 << 10;
+ constexpr size_t r_len = 64 << 10;
+ {
+ auto t = create_transaction();
+ auto m_ext = alloc_extent(t, m_offset, m_len);
+ m_ext->set_contents('a', 0 << 10, 8 << 10);
+ m_ext->set_contents('b', 16 << 10, 4 << 10);
+ m_ext->set_contents('c', 36 << 10, 4 << 10);
+ m_ext->set_contents('d', 52 << 10, 4 << 10);
+
+ auto l_ext = alloc_extent(t, l_offset, l_len);
+ auto r_ext = alloc_extent(t, r_offset, r_len);
+ submit_transaction(std::move(t));
+ }
+ {
+ auto t = create_transaction();
+ auto mpin = get_pin(t, m_offset);
+ auto lpin = get_pin(t, l_offset);
+ auto rpin = get_pin(t, r_offset);
+
+ bufferlist mbl1, mbl2, mbl3;
+ mbl1.append(ceph::bufferptr(ceph::buffer::create(8 << 10, 0)));
+ mbl2.append(ceph::bufferptr(ceph::buffer::create(16 << 10, 0)));
+ mbl3.append(ceph::bufferptr(ceph::buffer::create(12 << 10, 0)));
+ auto [mlp1, mext1, mrp1] = overwrite_pin(
+ t, std::move(mpin), 8 << 10 , 8 << 10, mbl1);
+ auto [mlp2, mext2, mrp2] = overwrite_pin(
+ t, std::move(mrp1), 4 << 10 , 16 << 10, mbl2);
+ auto [mlpin3, me3, mrpin3] = overwrite_pin(
+ t, std::move(mrp2), 4 << 10 , 12 << 10, mbl3);
+ auto mlext1 = get_extent(t, mlp1->get_key(), mlp1->get_length());
+ auto mlext2 = get_extent(t, mlp2->get_key(), mlp2->get_length());
+ auto mlext3 = get_extent(t, mlpin3->get_key(), mlpin3->get_length());
+ auto mrext3 = get_extent(t, mrpin3->get_key(), mrpin3->get_length());
+ EXPECT_EQ('a', mlext1->get_bptr().c_str()[0]);
+ EXPECT_EQ('b', mlext2->get_bptr().c_str()[0]);
+ EXPECT_EQ('c', mlext3->get_bptr().c_str()[0]);
+ EXPECT_EQ('d', mrext3->get_bptr().c_str()[0]);
+ auto mutate_mlext1 = mutate_extent(t, mlext1);
+ auto mutate_mlext2 = mutate_extent(t, mlext2);
+ auto mutate_mlext3 = mutate_extent(t, mlext3);
+ auto mutate_mrext3 = mutate_extent(t, mrext3);
+ ASSERT_TRUE(mutate_mlext1->is_exist_mutation_pending());
+ ASSERT_TRUE(mutate_mlext2->is_exist_mutation_pending());
+ ASSERT_TRUE(mutate_mlext3->is_exist_mutation_pending());
+ ASSERT_TRUE(mutate_mrext3->is_exist_mutation_pending());
+ ASSERT_TRUE(mutate_mlext1.get() == mlext1.get());
+ ASSERT_TRUE(mutate_mlext2.get() == mlext2.get());
+ ASSERT_TRUE(mutate_mlext3.get() == mlext3.get());
+ ASSERT_TRUE(mutate_mrext3.get() == mrext3.get());
+
+ bufferlist lbl1, rbl1;
+ lbl1.append(ceph::bufferptr(ceph::buffer::create(32 << 10, 0)));
+ auto [llp1, lext1, lrp1] = overwrite_pin(
+ t, std::move(lpin), 0 , 32 << 10, lbl1);
+ EXPECT_FALSE(llp1);
+ EXPECT_TRUE(lrp1);
+ EXPECT_TRUE(lext1);
+
+ rbl1.append(ceph::bufferptr(ceph::buffer::create(32 << 10, 0)));
+ auto [rlp1, rext1, rrp1] = overwrite_pin(
+ t, std::move(rpin), 32 << 10 , 32 << 10, rbl1);
+ EXPECT_TRUE(rlp1);
+ EXPECT_TRUE(rext1);
+ EXPECT_FALSE(rrp1);
+
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ check();
+ });
+ }
+
+ void test_remap_pin_concurrent() {
+ run_async([this] {
+ constexpr unsigned REMAP_NUM = 32;
+ constexpr size_t offset = 0;
+ constexpr size_t length = 256 << 10;
+ {
+ auto t = create_transaction();
+ auto extent = alloc_extent(t, offset, length);
+ ASSERT_EQ(length, extent->get_length());
+ submit_transaction(std::move(t));
+ }
+ int success = 0;
+ int early_exit = 0;
+ int conflicted = 0;
+
+ seastar::parallel_for_each(
+ boost::make_counting_iterator(0u),
+ boost::make_counting_iterator(REMAP_NUM),
+ [&](auto) {
+ return seastar::async([&] {
+ uint32_t pieces = std::uniform_int_distribution<>(6, 31)(gen);
+ std::set<uint32_t> split_points;
+ for (uint32_t i = 0; i < pieces; i++) {
+ auto p = std::uniform_int_distribution<>(1, 256)(gen);
+ split_points.insert(p - p % 4);
+ }
+
+ auto t = create_transaction();
+ auto pin0 = try_get_pin(t, offset);
+ if (!pin0 || pin0->get_length() != length) {
+ early_exit++;
+ return;
+ }
+
+ auto last_pin = pin0->duplicate();
+ ASSERT_TRUE(!split_points.empty());
+ for (auto off : split_points) {
+ if (off == 0 || off >= 255) {
+ continue;
+ }
+ auto new_off = (off << 10) - last_pin->get_key();
+ auto new_len = last_pin->get_length() - new_off;
+ //always remap right extent at new split_point
+ auto pin = remap_pin(t, std::move(last_pin), new_off, new_len);
+ if (!pin) {
+ conflicted++;
+ return;
+ }
+ last_pin = pin->duplicate();
+ }
+ auto last_ext = try_get_extent(t, last_pin->get_key());
+ if (last_ext) {
+ auto last_ext1 = mutate_extent(t, last_ext);
+ ASSERT_TRUE(last_ext1->is_exist_mutation_pending());
+ } else {
+ conflicted++;
+ return;
+ }
+
+ if (try_submit_transaction(std::move(t))) {
+ success++;
+ logger().info("transaction {} submit the transction",
+ static_cast<void*>(t.t.get()));
+ } else {
+ conflicted++;
+ }
+ });
+ }).handle_exception([](std::exception_ptr e) {
+ logger().info("{}", e);
+ }).get0();
+ logger().info("test_remap_pin_concurrent: "
+ "early_exit {} conflicted {} success {}",
+ early_exit, conflicted, success);
+ ASSERT_TRUE(success == 1);
+ ASSERT_EQ(success + conflicted + early_exit, REMAP_NUM);
+ replay();
+ check();
+ });
+ }
+
+ void test_overwrite_pin_concurrent() {
+ run_async([this] {
+ constexpr unsigned REMAP_NUM = 32;
+ constexpr size_t offset = 0;
+ constexpr size_t length = 256 << 10;
+ {
+ auto t = create_transaction();
+ auto extent = alloc_extent(t, offset, length);
+ ASSERT_EQ(length, extent->get_length());
+ submit_transaction(std::move(t));
+ }
+ int success = 0;
+ int early_exit = 0;
+ int conflicted = 0;
+
+ seastar::parallel_for_each(
+ boost::make_counting_iterator(0u),
+ boost::make_counting_iterator(REMAP_NUM),
+ [&](auto) {
+ return seastar::async([&] {
+ uint32_t pieces = std::uniform_int_distribution<>(6, 31)(gen);
+ if (pieces % 2 == 1) {
+ pieces++;
+ }
+ std::list<uint32_t> split_points;
+ for (uint32_t i = 0; i < pieces; i++) {
+ auto p = std::uniform_int_distribution<>(1, 120)(gen);
+ split_points.push_back(p - p % 4);
+ }
+ split_points.sort();
+
+ auto t = create_transaction();
+ auto pin0 = try_get_pin(t, offset);
+ if (!pin0 || pin0->get_length() != length) {
+ early_exit++;
+ return;
+ }
+
+ auto empty_transaction = true;
+ auto last_rpin = pin0->duplicate();
+ ASSERT_TRUE(!split_points.empty());
+ while(!split_points.empty()) {
+ // new overwrite area: start_off ~ end_off
+ auto start_off = split_points.front();
+ split_points.pop_front();
+ auto end_off = split_points.front();
+ split_points.pop_front();
+ ASSERT_TRUE(start_off <= end_off);
+ if (((end_off << 10) == pin0->get_key() + pin0->get_length())
+ || (start_off == end_off)) {
+ if (split_points.empty() && empty_transaction) {
+ early_exit++;
+ return;
+ }
+ continue;
+ }
+ empty_transaction = false;
+ auto new_off = (start_off << 10) - last_rpin->get_key();
+ auto new_len = (end_off - start_off) << 10;
+ bufferlist bl;
+ bl.append(ceph::bufferptr(ceph::buffer::create(new_len, 0)));
+ auto [lpin, ext, rpin] = overwrite_pin(
+ t, last_rpin->duplicate(), new_off, new_len, bl);
+ if (!ext) {
+ conflicted++;
+ return;
+ }
+ // lpin is nullptr might not cause by confliction,
+ // it might just not exist.
+ if (lpin) {
+ auto lext = try_get_extent(t, lpin->get_key());
+ if (!lext) {
+ conflicted++;
+ return;
+ }
+ if (get_random_contents() % 2 == 0) {
+ auto lext1 = mutate_extent(t, lext);
+ ASSERT_TRUE(lext1->is_exist_mutation_pending());
+ }
+ }
+ ASSERT_TRUE(rpin);
+ last_rpin = rpin->duplicate();
+ }
+ auto last_rext = try_get_extent(t, last_rpin->get_key());
+ if (!last_rext) {
+ conflicted++;
+ return;
+ }
+ if (get_random_contents() % 2 == 0) {
+ auto last_rext1 = mutate_extent(t, last_rext);
+ ASSERT_TRUE(last_rext1->is_exist_mutation_pending());
+ }
+
+ if (try_submit_transaction(std::move(t))) {
+ success++;
+ logger().info("transaction {} submit the transction",
+ static_cast<void*>(t.t.get()));
+ } else {
+ conflicted++;
+ }
+ });
+ }).handle_exception([](std::exception_ptr e) {
+ logger().info("{}", e);
+ }).get0();
+ logger().info("test_overwrite_pin_concurrent: "
+ "early_exit {} conflicted {} success {}",
+ early_exit, conflicted, success);
+ ASSERT_TRUE(success == 1 || early_exit == REMAP_NUM);
+ ASSERT_EQ(success + conflicted + early_exit, REMAP_NUM);
+ replay();
+ check();
+ });
+ }
+};
+
+struct tm_single_device_test_t :
+ public transaction_manager_test_t {
+
+ tm_single_device_test_t() : transaction_manager_test_t(1, 0) {}
+};
+
+struct tm_multi_device_test_t :
+ public transaction_manager_test_t {
+
+ tm_multi_device_test_t() : transaction_manager_test_t(3, 0) {}
+};
+
+struct tm_multi_tier_device_test_t :
+ public transaction_manager_test_t {
+
+ tm_multi_tier_device_test_t() : transaction_manager_test_t(1, 2) {}
+};
+
+TEST_P(tm_single_device_test_t, basic)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ constexpr laddr_t ADDR = 0xFF * SIZE;
+ {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ ADDR,
+ SIZE,
+ 'a');
+ ASSERT_EQ(ADDR, extent->get_laddr());
+ check_mappings(t);
+ check();
+ submit_transaction(std::move(t));
+ check();
+ }
+ });
+}
+
+TEST_P(tm_single_device_test_t, mutate)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ constexpr laddr_t ADDR = 0xFF * SIZE;
+ {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ ADDR,
+ SIZE,
+ 'a');
+ ASSERT_EQ(ADDR, extent->get_laddr());
+ check_mappings(t);
+ check();
+ submit_transaction(std::move(t));
+ check();
+ }
+ ASSERT_TRUE(check_usage());
+ replay();
+ {
+ auto t = create_transaction();
+ auto ext = get_extent(
+ t,
+ ADDR,
+ SIZE);
+ auto mut = mutate_extent(t, ext);
+ check_mappings(t);
+ check();
+ submit_transaction(std::move(t));
+ check();
+ }
+ ASSERT_TRUE(check_usage());
+ replay();
+ check();
+ });
+}
+
+TEST_P(tm_single_device_test_t, allocate_lba_conflict)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ constexpr laddr_t ADDR = 0xFF * SIZE;
+ constexpr laddr_t ADDR2 = 0xFE * SIZE;
+ auto t = create_transaction();
+ auto t2 = create_transaction();
+
+ // These should conflict as they should both modify the lba root
+ auto extent = alloc_extent(
+ t,
+ ADDR,
+ SIZE,
+ 'a');
+ ASSERT_EQ(ADDR, extent->get_laddr());
+ check_mappings(t);
+ check();
+
+ auto extent2 = alloc_extent(
+ t2,
+ ADDR2,
+ SIZE,
+ 'a');
+ ASSERT_EQ(ADDR2, extent2->get_laddr());
+ check_mappings(t2);
+ extent2.reset();
+
+ submit_transaction(std::move(t2));
+ submit_transaction_expect_conflict(std::move(t));
+ });
+}
+
+TEST_P(tm_single_device_test_t, mutate_lba_conflict)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ {
+ auto t = create_transaction();
+ for (unsigned i = 0; i < 300; ++i) {
+ auto extent = alloc_extent(
+ t,
+ laddr_t(i * SIZE),
+ SIZE);
+ }
+ check_mappings(t);
+ submit_transaction(std::move(t));
+ check();
+ }
+
+ constexpr laddr_t ADDR = 150 * SIZE;
+ {
+ auto t = create_transaction();
+ auto t2 = create_transaction();
+
+ mutate_addr(t, ADDR, SIZE);
+ mutate_addr(t2, ADDR, SIZE);
+
+ submit_transaction(std::move(t));
+ submit_transaction_expect_conflict(std::move(t2));
+ }
+ check();
+
+ {
+ auto t = create_transaction();
+ mutate_addr(t, ADDR, SIZE);
+ submit_transaction(std::move(t));
+ }
+ check();
+ });
+}
+
+TEST_P(tm_single_device_test_t, concurrent_mutate_lba_no_conflict)
+{
+ constexpr laddr_t SIZE = 4096;
+ constexpr size_t NUM = 500;
+ constexpr laddr_t addr = 0;
+ constexpr laddr_t addr2 = SIZE * (NUM - 1);
+ run_async([this] {
+ {
+ auto t = create_transaction();
+ for (unsigned i = 0; i < NUM; ++i) {
+ auto extent = alloc_extent(
+ t,
+ laddr_t(i * SIZE),
+ SIZE);
+ }
+ submit_transaction(std::move(t));
+ }
+
+ {
+ auto t = create_transaction();
+ auto t2 = create_transaction();
+
+ mutate_addr(t, addr, SIZE);
+ mutate_addr(t2, addr2, SIZE);
+
+ submit_transaction(std::move(t));
+ submit_transaction(std::move(t2));
+ }
+ check();
+ });
+}
+
+TEST_P(tm_single_device_test_t, create_remove_same_transaction)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ constexpr laddr_t ADDR = 0xFF * SIZE;
+ {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ ADDR,
+ SIZE,
+ 'a');
+ ASSERT_EQ(ADDR, extent->get_laddr());
+ check_mappings(t);
+ dec_ref(t, ADDR);
+ check_mappings(t);
+
+ extent = alloc_extent(
+ t,
+ ADDR,
+ SIZE,
+ 'a');
+
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ check();
+ });
+}
+
+TEST_P(tm_single_device_test_t, split_merge_read_same_transaction)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ {
+ auto t = create_transaction();
+ for (unsigned i = 0; i < 300; ++i) {
+ auto extent = alloc_extent(
+ t,
+ laddr_t(i * SIZE),
+ SIZE);
+ }
+ check_mappings(t);
+ submit_transaction(std::move(t));
+ check();
+ }
+ {
+ auto t = create_transaction();
+ for (unsigned i = 0; i < 240; ++i) {
+ dec_ref(
+ t,
+ laddr_t(i * SIZE));
+ }
+ check_mappings(t);
+ submit_transaction(std::move(t));
+ check();
+ }
+ });
+}
+
+TEST_P(tm_single_device_test_t, inc_dec_ref)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ constexpr laddr_t ADDR = 0xFF * SIZE;
+ {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ ADDR,
+ SIZE,
+ 'a');
+ ASSERT_EQ(ADDR, extent->get_laddr());
+ check_mappings(t);
+ check();
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ {
+ auto t = create_transaction();
+ inc_ref(t, ADDR);
+ check_mappings(t);
+ check();
+ submit_transaction(std::move(t));
+ check();
+ }
+ {
+ auto t = create_transaction();
+ dec_ref(t, ADDR);
+ check_mappings(t);
+ check();
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ {
+ auto t = create_transaction();
+ dec_ref(t, ADDR);
+ check_mappings(t);
+ check();
+ submit_transaction(std::move(t));
+ check();
+ }
+ });
+}
+
+TEST_P(tm_single_device_test_t, cause_lba_split)
+{
+ constexpr laddr_t SIZE = 4096;
+ run_async([this] {
+ for (unsigned i = 0; i < 200; ++i) {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ i * SIZE,
+ SIZE,
+ (char)(i & 0xFF));
+ ASSERT_EQ(i * SIZE, extent->get_laddr());
+ submit_transaction(std::move(t));
+ }
+ check();
+ });
+}
+
+TEST_P(tm_single_device_test_t, random_writes)
+{
+ constexpr size_t TOTAL = 4<<20;
+ constexpr size_t BSIZE = 4<<10;
+ constexpr size_t PADDING_SIZE = 256<<10;
+ constexpr size_t BLOCKS = TOTAL / BSIZE;
+ run_async([this] {
+ for (unsigned i = 0; i < BLOCKS; ++i) {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ i * BSIZE,
+ BSIZE);
+ ASSERT_EQ(i * BSIZE, extent->get_laddr());
+ submit_transaction(std::move(t));
+ }
+
+ for (unsigned i = 0; i < 4; ++i) {
+ for (unsigned j = 0; j < 65; ++j) {
+ auto t = create_transaction();
+ for (unsigned k = 0; k < 2; ++k) {
+ auto ext = get_extent(
+ t,
+ get_random_laddr(BSIZE, TOTAL),
+ BSIZE);
+ auto mut = mutate_extent(t, ext);
+ // pad out transaction
+ auto padding = alloc_extent(
+ t,
+ TOTAL + (k * PADDING_SIZE),
+ PADDING_SIZE);
+ dec_ref(t, padding->get_laddr());
+ }
+ submit_transaction(std::move(t));
+ }
+ replay();
+ logger().info("random_writes: {} checking", i);
+ check();
+ logger().info("random_writes: {} done replaying/checking", i);
+ }
+ });
+}
+
+TEST_P(tm_single_device_test_t, find_hole_assert_trigger)
+{
+ constexpr unsigned max = 10;
+ constexpr size_t BSIZE = 4<<10;
+ int num = 40;
+ run([&, this] {
+ return seastar::parallel_for_each(
+ boost::make_counting_iterator(0u),
+ boost::make_counting_iterator(max),
+ [&, this](auto idx) {
+ return allocate_sequentially(BSIZE, num);
+ });
+ });
+}
+
+TEST_P(tm_single_device_test_t, remap_lazy_read)
+{
+ constexpr laddr_t offset = 0;
+ constexpr size_t length = 256 << 10;
+ run_async([this, offset] {
+ {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ offset,
+ length,
+ 'a');
+ ASSERT_EQ(offset, extent->get_laddr());
+ check_mappings(t);
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ {
+ auto t = create_transaction();
+ auto pin = get_pin(t, offset);
+ auto rpin = remap_pin(t, std::move(pin), 0, 128 << 10);
+ check_mappings(t);
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ {
+ auto t = create_transaction();
+ auto pin = get_pin(t, offset);
+ bufferlist bl;
+ bl.append(ceph::bufferptr(ceph::buffer::create(64 << 10, 0)));
+ auto [lpin, ext, rpin] = overwrite_pin(
+ t, std::move(pin), 4 << 10 , 64 << 10, bl);
+ check_mappings(t);
+ submit_transaction(std::move(t));
+ check();
+ }
+ replay();
+ });
+}
+
+TEST_P(tm_single_device_test_t, random_writes_concurrent)
+{
+ test_random_writes_concurrent();
+}
+
+TEST_P(tm_multi_device_test_t, random_writes_concurrent)
+{
+ test_random_writes_concurrent();
+}
+
+TEST_P(tm_multi_tier_device_test_t, evict)
+{
+ test_evict();
+}
+
+TEST_P(tm_single_device_test_t, parallel_extent_read)
+{
+ test_parallel_extent_read();
+}
+
+TEST_P(tm_single_device_test_t, test_remap_pin)
+{
+ test_remap_pin();
+}
+
+TEST_P(tm_single_device_test_t, test_overwrite_pin)
+{
+ test_overwrite_pin();
+}
+
+TEST_P(tm_single_device_test_t, test_remap_pin_concurrent)
+{
+ test_remap_pin_concurrent();
+}
+
+TEST_P(tm_single_device_test_t, test_overwrite_pin_concurrent)
+{
+ test_overwrite_pin_concurrent();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ transaction_manager_test,
+ tm_single_device_test_t,
+ ::testing::Values (
+ "segmented",
+ "circularbounded"
+ )
+);
+
+INSTANTIATE_TEST_SUITE_P(
+ transaction_manager_test,
+ tm_multi_device_test_t,
+ ::testing::Values (
+ "segmented"
+ )
+);
+
+INSTANTIATE_TEST_SUITE_P(
+ transaction_manager_test,
+ tm_multi_tier_device_test_t,
+ ::testing::Values (
+ "segmented"
+ )
+);
diff --git a/src/test/crimson/seastore/transaction_manager_test_state.h b/src/test/crimson/seastore/transaction_manager_test_state.h
new file mode 100644
index 000000000..81200b1db
--- /dev/null
+++ b/src/test/crimson/seastore/transaction_manager_test_state.h
@@ -0,0 +1,450 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <random>
+#include <boost/iterator/counting_iterator.hpp>
+
+#include "crimson/os/seastore/cache.h"
+#include "crimson/os/seastore/extent_placement_manager.h"
+#include "crimson/os/seastore/logging.h"
+#include "crimson/os/seastore/transaction_manager.h"
+#include "crimson/os/seastore/segment_manager/ephemeral.h"
+#include "crimson/os/seastore/seastore.h"
+#include "crimson/os/seastore/segment_manager.h"
+#include "crimson/os/seastore/collection_manager/flat_collection_manager.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/fltree_onode_manager.h"
+#include "crimson/os/seastore/random_block_manager/rbm_device.h"
+#include "crimson/os/seastore/journal/circular_bounded_journal.h"
+#include "crimson/os/seastore/random_block_manager/block_rb_manager.h"
+#ifdef UNIT_TESTS_BUILT
+#include "test/crimson/gtest_seastar.h"
+#endif
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+
+class EphemeralDevices {
+public:
+ virtual seastar::future<> setup() = 0;
+ virtual void remount() = 0;
+ virtual std::size_t get_num_devices() const = 0;
+ virtual void reset() = 0;
+ virtual std::vector<Device*> get_secondary_devices() = 0;
+ virtual ~EphemeralDevices() {}
+ virtual Device* get_primary_device() = 0;
+ virtual DeviceRef get_primary_device_ref() = 0;
+ virtual void set_primary_device_ref(DeviceRef) = 0;
+};
+using EphemeralDevicesRef = std::unique_ptr<EphemeralDevices>;
+
+class EphemeralSegmentedDevices : public EphemeralDevices {
+ segment_manager::EphemeralSegmentManagerRef segment_manager;
+ std::list<segment_manager::EphemeralSegmentManagerRef> secondary_segment_managers;
+ std::size_t num_main_device_managers;
+ std::size_t num_cold_device_managers;
+
+public:
+ EphemeralSegmentedDevices(std::size_t num_main_devices,
+ std::size_t num_cold_devices)
+ : num_main_device_managers(num_main_devices),
+ num_cold_device_managers(num_cold_devices)
+ {
+ auto num_device_managers = num_main_device_managers + num_cold_device_managers;
+ assert(num_device_managers > 0);
+ secondary_segment_managers.resize(num_device_managers - 1);
+ }
+
+ seastar::future<> setup() final {
+ segment_manager = segment_manager::create_test_ephemeral();
+ for (auto &sec_sm : secondary_segment_managers) {
+ sec_sm = segment_manager::create_test_ephemeral();
+ }
+ return segment_manager->init(
+ ).safe_then([this] {
+ return crimson::do_for_each(
+ secondary_segment_managers.begin(),
+ secondary_segment_managers.end(),
+ [](auto &sec_sm)
+ {
+ return sec_sm->init();
+ });
+ }).safe_then([this] {
+ return segment_manager->mkfs(
+ segment_manager::get_ephemeral_device_config(
+ 0, num_main_device_managers, num_cold_device_managers));
+ }).safe_then([this] {
+ return seastar::do_with(std::size_t(0), [this](auto &cnt) {
+ return crimson::do_for_each(
+ secondary_segment_managers.begin(),
+ secondary_segment_managers.end(),
+ [this, &cnt](auto &sec_sm)
+ {
+ ++cnt;
+ return sec_sm->mkfs(
+ segment_manager::get_ephemeral_device_config(
+ cnt, num_main_device_managers, num_cold_device_managers));
+ });
+ });
+ }).handle_error(
+ crimson::ct_error::assert_all{}
+ );
+ }
+
+ void remount() final {
+ segment_manager->remount();
+ for (auto &sec_sm : secondary_segment_managers) {
+ sec_sm->remount();
+ }
+ }
+
+ std::size_t get_num_devices() const final {
+ return secondary_segment_managers.size() + 1;
+ }
+
+ void reset() final {
+ segment_manager.reset();
+ for (auto &sec_sm : secondary_segment_managers) {
+ sec_sm.reset();
+ }
+ }
+
+ std::vector<Device*> get_secondary_devices() final {
+ std::vector<Device*> sec_devices;
+ for (auto &sec_sm : secondary_segment_managers) {
+ sec_devices.emplace_back(sec_sm.get());
+ }
+ return sec_devices;
+ }
+
+ Device* get_primary_device() final {
+ return segment_manager.get();
+ }
+ DeviceRef get_primary_device_ref() final;
+ void set_primary_device_ref(DeviceRef) final;
+};
+
+class EphemeralRandomBlockDevices : public EphemeralDevices {
+ random_block_device::RBMDeviceRef rb_device;
+ std::list<random_block_device::RBMDeviceRef> secondary_rb_devices;
+
+public:
+ EphemeralRandomBlockDevices(std::size_t num_device_managers) {
+ assert(num_device_managers > 0);
+ secondary_rb_devices.resize(num_device_managers - 1);
+ }
+
+ seastar::future<> setup() final {
+ rb_device = random_block_device::create_test_ephemeral();
+ device_config_t config = get_rbm_ephemeral_device_config(0, 1);
+ return rb_device->mkfs(config).handle_error(crimson::ct_error::assert_all{});
+ }
+
+ void remount() final {}
+
+ std::size_t get_num_devices() const final {
+ return secondary_rb_devices.size() + 1;
+ }
+
+ void reset() final {
+ rb_device.reset();
+ for (auto &sec_rb : secondary_rb_devices) {
+ sec_rb.reset();
+ }
+ }
+
+ std::vector<Device*> get_secondary_devices() final {
+ std::vector<Device*> sec_devices;
+ for (auto &sec_rb : secondary_rb_devices) {
+ sec_devices.emplace_back(sec_rb.get());
+ }
+ return sec_devices;
+ }
+
+ Device* get_primary_device() final {
+ return rb_device.get();
+ }
+ DeviceRef get_primary_device_ref() final;
+ void set_primary_device_ref(DeviceRef) final;
+};
+
+class EphemeralTestState
+#ifdef UNIT_TESTS_BUILT
+ : public ::testing::WithParamInterface<const char*> {
+#else
+ {
+#endif
+protected:
+ journal_type_t journal_type;
+ size_t num_main_device_managers = 0;
+ size_t num_cold_device_managers = 0;
+ EphemeralDevicesRef devices;
+ bool secondary_is_cold;
+ EphemeralTestState(std::size_t num_main_device_managers,
+ std::size_t num_cold_device_managers) :
+ num_main_device_managers(num_main_device_managers),
+ num_cold_device_managers(num_cold_device_managers) {}
+
+ virtual seastar::future<> _init() = 0;
+
+ virtual seastar::future<> _destroy() = 0;
+ virtual seastar::future<> _teardown() = 0;
+ seastar::future<> teardown() {
+ return _teardown().then([this] {
+ return _destroy();
+ });
+ }
+
+ virtual FuturizedStore::mkfs_ertr::future<> _mkfs() = 0;
+ virtual FuturizedStore::mount_ertr::future<> _mount() = 0;
+
+ seastar::future<> restart_fut() {
+ LOG_PREFIX(EphemeralTestState::restart_fut);
+ SUBINFO(test, "begin ...");
+ return teardown().then([this] {
+ devices->remount();
+ return _init().then([this] {
+ return _mount().handle_error(crimson::ct_error::assert_all{});
+ });
+ }).then([FNAME] {
+ SUBINFO(test, "finish");
+ });
+ }
+
+ void restart() {
+ restart_fut().get0();
+ }
+
+ seastar::future<> tm_setup() {
+ LOG_PREFIX(EphemeralTestState::tm_setup);
+#ifdef UNIT_TESTS_BUILT
+ std::string j_type = GetParam();
+#else
+ std::string j_type = "segmented";
+#endif
+ if (j_type == "circularbounded") {
+ //TODO: multiple devices
+ ceph_assert(num_main_device_managers == 1);
+ ceph_assert(num_cold_device_managers == 0);
+ devices.reset(new EphemeralRandomBlockDevices(1));
+ } else {
+ // segmented by default
+ devices.reset(new
+ EphemeralSegmentedDevices(
+ num_main_device_managers, num_cold_device_managers));
+ }
+ SUBINFO(test, "begin with {} devices ...", devices->get_num_devices());
+ return devices->setup(
+ ).then([this] {
+ return _init();
+ }).then([this, FNAME] {
+ return _mkfs(
+ ).safe_then([this] {
+ return restart_fut();
+ }).handle_error(
+ crimson::ct_error::assert_all{}
+ ).then([FNAME] {
+ SUBINFO(test, "finish");
+ });
+ });
+ }
+
+ seastar::future<> tm_teardown() {
+ LOG_PREFIX(EphemeralTestState::tm_teardown);
+ SUBINFO(test, "begin");
+ return teardown().then([this, FNAME] {
+ devices->reset();
+ SUBINFO(test, "finish");
+ });
+ }
+};
+
+class TMTestState : public EphemeralTestState {
+protected:
+ TransactionManagerRef tm;
+ LBAManager *lba_manager;
+ Cache* cache;
+ ExtentPlacementManager *epm;
+ uint64_t seq = 0;
+
+ TMTestState() : EphemeralTestState(1, 0) {}
+
+ TMTestState(std::size_t num_main_devices, std::size_t num_cold_devices)
+ : EphemeralTestState(num_main_devices, num_cold_devices) {}
+
+ virtual seastar::future<> _init() override {
+ auto sec_devices = devices->get_secondary_devices();
+ auto p_dev = devices->get_primary_device();
+ tm = make_transaction_manager(p_dev, sec_devices, true);
+ epm = tm->get_epm();
+ lba_manager = tm->get_lba_manager();
+ cache = tm->get_cache();
+ return seastar::now();
+ }
+
+ virtual seastar::future<> _destroy() override {
+ epm = nullptr;
+ lba_manager = nullptr;
+ cache = nullptr;
+ tm.reset();
+ return seastar::now();
+ }
+
+ virtual seastar::future<> _teardown() {
+ return tm->close().handle_error(
+ crimson::ct_error::assert_all{"Error in teardown"}
+ );
+ }
+
+ virtual FuturizedStore::mount_ertr::future<> _mount() {
+ return tm->mount(
+ ).handle_error(
+ crimson::ct_error::assert_all{"Error in mount"}
+ ).then([this] {
+ return epm->stop_background();
+ }).then([this] {
+ return epm->run_background_work_until_halt();
+ });
+ }
+
+ virtual FuturizedStore::mkfs_ertr::future<> _mkfs() {
+ return tm->mkfs(
+ ).handle_error(
+ crimson::ct_error::assert_all{"Error in mkfs"}
+ );
+ }
+
+ auto create_mutate_transaction() {
+ return tm->create_transaction(
+ Transaction::src_t::MUTATE, "test_mutate");
+ }
+
+ auto create_read_transaction() {
+ return tm->create_transaction(
+ Transaction::src_t::READ, "test_read");
+ }
+
+ auto create_weak_transaction() {
+ return tm->create_transaction(
+ Transaction::src_t::READ, "test_read_weak", true);
+ }
+
+ auto submit_transaction_fut2(Transaction& t) {
+ return tm->submit_transaction(t);
+ }
+
+ auto submit_transaction_fut(Transaction &t) {
+ return with_trans_intr(
+ t,
+ [this](auto &t) {
+ return tm->submit_transaction(t);
+ });
+ }
+ auto submit_transaction_fut_with_seq(Transaction &t) {
+ using ertr = TransactionManager::base_iertr;
+ return with_trans_intr(
+ t,
+ [this](auto &t) {
+ return tm->submit_transaction(t
+ ).si_then([this] {
+ return ertr::make_ready_future<uint64_t>(seq++);
+ });
+ });
+ }
+
+ void submit_transaction(TransactionRef t) {
+ submit_transaction_fut(*t).unsafe_get0();
+ epm->run_background_work_until_halt().get0();
+ }
+};
+
+
+DeviceRef EphemeralSegmentedDevices::get_primary_device_ref() {
+ return std::move(segment_manager);
+}
+
+DeviceRef EphemeralRandomBlockDevices::get_primary_device_ref() {
+ return std::move(rb_device);
+}
+
+void EphemeralSegmentedDevices::set_primary_device_ref(DeviceRef dev) {
+ segment_manager =
+ segment_manager::EphemeralSegmentManagerRef(
+ static_cast<segment_manager::EphemeralSegmentManager*>(dev.release()));
+}
+
+void EphemeralRandomBlockDevices::set_primary_device_ref(DeviceRef dev) {
+ rb_device =
+ random_block_device::RBMDeviceRef(
+ static_cast<random_block_device::RBMDevice*>(dev.release()));
+}
+
+class SeaStoreTestState : public EphemeralTestState {
+ class TestMDStoreState {
+ std::map<std::string, std::string> md;
+ public:
+ class Store final : public SeaStore::MDStore {
+ TestMDStoreState &parent;
+ public:
+ Store(TestMDStoreState &parent) : parent(parent) {}
+
+ write_meta_ret write_meta(
+ const std::string& key, const std::string& value) final {
+ parent.md[key] = value;
+ return seastar::now();
+ }
+
+ read_meta_ret read_meta(const std::string& key) final {
+ auto iter = parent.md.find(key);
+ if (iter != parent.md.end()) {
+ return read_meta_ret(
+ read_meta_ertr::ready_future_marker{},
+ iter->second);
+ } else {
+ return read_meta_ret(
+ read_meta_ertr::ready_future_marker{},
+ std::nullopt);
+ }
+ }
+ };
+ Store get_mdstore() {
+ return Store(*this);
+ }
+ } mdstore_state;
+
+protected:
+ std::unique_ptr<SeaStore> seastore;
+ FuturizedStore::Shard *sharded_seastore;
+
+ SeaStoreTestState() : EphemeralTestState(1, 0) {}
+
+ virtual seastar::future<> _init() final {
+ seastore = make_test_seastore(
+ std::make_unique<TestMDStoreState::Store>(mdstore_state.get_mdstore()));
+ return seastore->test_start(devices->get_primary_device_ref()
+ ).then([this] {
+ sharded_seastore = &(seastore->get_sharded_store());
+ });
+ }
+
+ virtual seastar::future<> _destroy() final {
+ devices->set_primary_device_ref(seastore->get_primary_device_ref());
+ return seastore->stop().then([this] {
+ seastore.reset();
+ });
+ }
+
+ virtual seastar::future<> _teardown() final {
+ return seastore->umount();
+ }
+
+ virtual FuturizedStore::mount_ertr::future<> _mount() final {
+ return seastore->test_mount();
+ }
+
+ virtual FuturizedStore::mkfs_ertr::future<> _mkfs() final {
+ return seastore->test_mkfs(uuid_d{});
+ }
+};
diff --git a/src/test/crimson/test_alien_echo.cc b/src/test/crimson/test_alien_echo.cc
new file mode 100644
index 000000000..8bef5e651
--- /dev/null
+++ b/src/test/crimson/test_alien_echo.cc
@@ -0,0 +1,294 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+
+#include "auth/Auth.h"
+#include "messages/MPing.h"
+#include "common/ceph_argparse.h"
+#include "crimson/auth/DummyAuth.h"
+#include "crimson/common/throttle.h"
+#include "crimson/net/Connection.h"
+#include "crimson/net/Dispatcher.h"
+#include "crimson/net/Messenger.h"
+
+#include <seastar/core/alien.hh>
+#include <seastar/core/app-template.hh>
+#include <seastar/core/future-util.hh>
+#include <seastar/core/internal/pollable_fd.hh>
+#include <seastar/core/posix.hh>
+#include <seastar/core/reactor.hh>
+
+using crimson::common::local_conf;
+
+enum class echo_role {
+ as_server,
+ as_client,
+};
+
+namespace seastar_pingpong {
+struct DummyAuthAuthorizer : public AuthAuthorizer {
+ DummyAuthAuthorizer()
+ : AuthAuthorizer(CEPH_AUTH_CEPHX)
+ {}
+ bool verify_reply(bufferlist::const_iterator&,
+ std::string *connection_secret) override {
+ return true;
+ }
+ bool add_challenge(CephContext*, const bufferlist&) override {
+ return true;
+ }
+};
+
+struct Server {
+ crimson::common::Throttle byte_throttler;
+ crimson::net::MessengerRef msgr;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+ struct ServerDispatcher final : crimson::net::Dispatcher {
+ unsigned count = 0;
+ seastar::condition_variable on_reply;
+ std::optional<seastar::future<>> ms_dispatch(crimson::net::ConnectionRef c,
+ MessageRef m) final
+ {
+ std::cout << "server got ping " << *m << std::endl;
+ // reply with a pong
+ return c->send(crimson::make_message<MPing>()).then([this] {
+ ++count;
+ on_reply.signal();
+ return seastar::now();
+ });
+ }
+ } dispatcher;
+ Server(crimson::net::MessengerRef msgr)
+ : byte_throttler(local_conf()->osd_client_message_size_cap),
+ msgr{msgr}
+ { }
+};
+
+struct Client {
+ crimson::common::Throttle byte_throttler;
+ crimson::net::MessengerRef msgr;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+ struct ClientDispatcher final : crimson::net::Dispatcher {
+ unsigned count = 0;
+ seastar::condition_variable on_reply;
+ std::optional<seastar::future<>> ms_dispatch(crimson::net::ConnectionRef c,
+ MessageRef m) final
+ {
+ std::cout << "client got pong " << *m << std::endl;
+ ++count;
+ on_reply.signal();
+ return seastar::now();
+ }
+ } dispatcher;
+ Client(crimson::net::MessengerRef msgr)
+ : byte_throttler(local_conf()->osd_client_message_size_cap),
+ msgr{msgr}
+ { }
+};
+} // namespace seastar_pingpong
+
+class SeastarContext {
+ int begin_fd;
+ seastar::file_desc on_end;
+
+public:
+ SeastarContext()
+ : begin_fd{eventfd(0, 0)},
+ on_end{seastar::file_desc::eventfd(0, 0)}
+ {}
+
+ template<class Func>
+ std::thread with_seastar(Func&& func) {
+ return std::thread{[this, on_end = on_end.get(),
+ func = std::forward<Func>(func)] {
+ // alien: are you ready?
+ wait_for_seastar();
+ // alien: could you help me apply(func)?
+ func();
+ // alien: i've sent my request. have you replied it?
+ // wait_for_seastar();
+ // alien: you are free to go!
+ ::eventfd_write(on_end, 1);
+ }};
+ }
+
+ void run(seastar::app_template& app, int argc, char** argv) {
+ app.run(argc, argv, [this] {
+ std::vector<const char*> args;
+ std::string cluster;
+ std::string conf_file_list;
+ auto init_params = ceph_argparse_early_args(args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ &cluster,
+ &conf_file_list);
+ return crimson::common::sharded_conf().start(init_params.name, cluster)
+ .then([conf_file_list] {
+ return local_conf().parse_config_files(conf_file_list);
+ }).then([this] {
+ return set_seastar_ready();
+ }).then([on_end = std::move(on_end)] () mutable {
+ // seastar: let me know once i am free to leave.
+ return seastar::do_with(seastar::pollable_fd(std::move(on_end)), []
+ (seastar::pollable_fd& on_end_fds) {
+ return on_end_fds.readable().then([&on_end_fds] {
+ eventfd_t result = 0;
+ on_end_fds.get_file_desc().read(&result, sizeof(result));
+ return seastar::make_ready_future<>();
+ });
+ });
+ }).then([]() {
+ return crimson::common::sharded_conf().stop();
+ }).handle_exception([](auto ep) {
+ std::cerr << "Error: " << ep << std::endl;
+ }).finally([] {
+ seastar::engine().exit(0);
+ });
+ });
+ }
+
+ seastar::future<> set_seastar_ready() {
+ // seastar: i am ready to serve!
+ ::eventfd_write(begin_fd, 1);
+ return seastar::now();
+ }
+
+private:
+ void wait_for_seastar() {
+ eventfd_t result = 0;
+ if (int r = ::eventfd_read(begin_fd, &result); r < 0) {
+ std::cerr << "unable to eventfd_read():" << errno << std::endl;
+ }
+ }
+};
+
+static seastar::future<>
+seastar_echo(const entity_addr_t addr, echo_role role, unsigned count)
+{
+ std::cout << "seastar/";
+ if (role == echo_role::as_server) {
+ return seastar::do_with(
+ seastar_pingpong::Server{crimson::net::Messenger::create(
+ entity_name_t::OSD(0), "server", addr.get_nonce(), true)},
+ [addr, count](auto& server) mutable {
+ std::cout << "server listening at " << addr << std::endl;
+ // bind the server
+ server.msgr->set_default_policy(crimson::net::SocketPolicy::stateless_server(0));
+ server.msgr->set_policy_throttler(entity_name_t::TYPE_OSD,
+ &server.byte_throttler);
+ server.msgr->set_auth_client(&server.dummy_auth);
+ server.msgr->set_auth_server(&server.dummy_auth);
+ return server.msgr->bind(entity_addrvec_t{addr}
+ ).safe_then([&server] {
+ return server.msgr->start({&server.dispatcher});
+ }, crimson::net::Messenger::bind_ertr::all_same_way([](auto& e) {
+ ceph_abort_msg("bind failed");
+ })).then([&dispatcher=server.dispatcher, count] {
+ return dispatcher.on_reply.wait([&dispatcher, count] {
+ return dispatcher.count >= count;
+ });
+ }).finally([&server] {
+ std::cout << "server shutting down" << std::endl;
+ server.msgr->stop();
+ return server.msgr->shutdown();
+ });
+ });
+ } else {
+ return seastar::do_with(
+ seastar_pingpong::Client{crimson::net::Messenger::create(
+ entity_name_t::OSD(1), "client", addr.get_nonce(), true)},
+ [addr, count](auto& client) {
+ std::cout << "client sending to " << addr << std::endl;
+ client.msgr->set_default_policy(crimson::net::SocketPolicy::lossy_client(0));
+ client.msgr->set_policy_throttler(entity_name_t::TYPE_OSD,
+ &client.byte_throttler);
+ client.msgr->set_auth_client(&client.dummy_auth);
+ client.msgr->set_auth_server(&client.dummy_auth);
+ return client.msgr->start({&client.dispatcher}).then(
+ [addr, &client, &disp=client.dispatcher, count] {
+ auto conn = client.msgr->connect(addr, entity_name_t::TYPE_OSD);
+ return seastar::do_until(
+ [&disp,count] { return disp.count >= count; },
+ [&disp,conn] {
+ return conn->send(crimson::make_message<MPing>()).then([&] {
+ return disp.on_reply.wait();
+ });
+ }
+ );
+ }).finally([&client] {
+ std::cout << "client shutting down" << std::endl;
+ client.msgr->stop();
+ return client.msgr->shutdown();
+ });
+ });
+ }
+}
+
+int main(int argc, char** argv)
+{
+ namespace po = boost::program_options;
+ po::options_description desc{"Allowed options"};
+ desc.add_options()
+ ("help,h", "show help message")
+ ("role", po::value<std::string>()->default_value("pong"),
+ "role to play (ping | pong)")
+ ("port", po::value<uint16_t>()->default_value(9010),
+ "port #")
+ ("nonce", po::value<uint32_t>()->default_value(42),
+ "a unique number to identify the pong server")
+ ("count", po::value<unsigned>()->default_value(10),
+ "stop after sending/echoing <count> MPing messages");
+ po::variables_map vm;
+ std::vector<std::string> unrecognized_options;
+ try {
+ auto parsed = po::command_line_parser(argc, argv)
+ .options(desc)
+ .allow_unregistered()
+ .run();
+ po::store(parsed, vm);
+ if (vm.count("help")) {
+ std::cout << desc << std::endl;
+ return 0;
+ }
+ po::notify(vm);
+ unrecognized_options = po::collect_unrecognized(parsed.options, po::include_positional);
+ } catch(const po::error& e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ entity_addr_t addr;
+ addr.set_type(entity_addr_t::TYPE_MSGR2);
+ addr.set_family(AF_INET);
+ addr.set_port(vm["port"].as<std::uint16_t>());
+ addr.set_nonce(vm["nonce"].as<std::uint32_t>());
+
+ echo_role role = echo_role::as_server;
+ if (vm["role"].as<std::string>() == "ping") {
+ role = echo_role::as_client;
+ }
+
+ auto count = vm["count"].as<unsigned>();
+ seastar::app_template app;
+ SeastarContext sc;
+ auto job = sc.with_seastar([&] {
+ auto fut = seastar::alien::submit_to(app.alien(), 0, [addr, role, count] {
+ return seastar_echo(addr, role, count);
+ });
+ fut.wait();
+ });
+ std::vector<char*> av{argv[0]};
+ std::transform(begin(unrecognized_options),
+ end(unrecognized_options),
+ std::back_inserter(av),
+ [](auto& s) {
+ return const_cast<char*>(s.c_str());
+ });
+ sc.run(app, av.size(), av.data());
+ job.join();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "make -j4 \
+ * -C ../../../build \
+ * unittest_seastar_echo"
+ * End:
+ */
diff --git a/src/test/crimson/test_alienstore_thread_pool.cc b/src/test/crimson/test_alienstore_thread_pool.cc
new file mode 100644
index 000000000..dbeed26cd
--- /dev/null
+++ b/src/test/crimson/test_alienstore_thread_pool.cc
@@ -0,0 +1,78 @@
+#include <chrono>
+#include <iostream>
+#include <numeric>
+#include <seastar/core/app-template.hh>
+#include "common/ceph_argparse.h"
+#include "crimson/common/config_proxy.h"
+#include "crimson/os/alienstore/thread_pool.h"
+#include "include/msgr.h"
+
+using namespace std::chrono_literals;
+using ThreadPool = crimson::os::ThreadPool;
+using crimson::common::local_conf;
+
+seastar::future<> test_accumulate(ThreadPool& tp) {
+ static constexpr auto N = 5;
+ static constexpr auto M = 1;
+ auto slow_plus = [&tp](int i) {
+ return tp.submit(::rand() % 2, [=] {
+ std::this_thread::sleep_for(10ns);
+ return i + M;
+ });
+ };
+ return seastar::map_reduce(
+ boost::irange(0, N), slow_plus, 0, std::plus{}).then([] (int sum) {
+ auto r = boost::irange(0 + M, N + M);
+ if (sum != std::accumulate(r.begin(), r.end(), 0)) {
+ throw std::runtime_error("test_accumulate failed");
+ }
+ });
+}
+
+seastar::future<> test_void_return(ThreadPool& tp) {
+ return tp.submit(::rand() % 2, [=] {
+ std::this_thread::sleep_for(10ns);
+ });
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ return app.run(argc, argv, [] {
+ std::vector<const char*> args;
+ std::string cluster;
+ std::string conf_file_list;
+ auto init_params = ceph_argparse_early_args(args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ &cluster,
+ &conf_file_list);
+ return crimson::common::sharded_conf().start(init_params.name, cluster)
+ .then([conf_file_list] {
+ return local_conf().parse_config_files(conf_file_list);
+ }).then([] {
+ return seastar::do_with(std::make_unique<crimson::os::ThreadPool>(2, 128, seastar::resource::cpuset{0}),
+ [](auto& tp) {
+ return tp->start().then([&tp] {
+ return test_accumulate(*tp);
+ }).then([&tp] {
+ return test_void_return(*tp);
+ }).finally([&tp] {
+ return tp->stop();
+ });
+ });
+ }).finally([] {
+ return crimson::common::sharded_conf().stop();
+ }).handle_exception([](auto e) {
+ std::cerr << "Error: " << e << std::endl;
+ seastar::engine().exit(1);
+ });
+ });
+}
+
+/*
+ * Local Variables:
+ * compile-command: "make -j4 \
+ * -C ../../../build \
+ * unittest_seastar_thread_pool"
+ * End:
+ */
diff --git a/src/test/crimson/test_async_echo.cc b/src/test/crimson/test_async_echo.cc
new file mode 100644
index 000000000..758bcf626
--- /dev/null
+++ b/src/test/crimson/test_async_echo.cc
@@ -0,0 +1,234 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+#include "auth/Auth.h"
+#include "global/global_init.h"
+#include "messages/MPing.h"
+#include "msg/Dispatcher.h"
+#include "msg/Messenger.h"
+
+#include "auth/DummyAuth.h"
+
+enum class echo_role {
+ as_server,
+ as_client,
+};
+
+namespace native_pingpong {
+
+constexpr int CEPH_OSD_PROTOCOL = 10;
+
+struct Server {
+ Server(CephContext* cct, const entity_inst_t& entity)
+ : dummy_auth(cct), dispatcher(cct)
+ {
+ msgr.reset(Messenger::create(cct, "async", entity.name, "pong", entity.addr.get_nonce()));
+ dummy_auth.auth_registry.refresh_config();
+ msgr->set_cluster_protocol(CEPH_OSD_PROTOCOL);
+ msgr->set_default_policy(Messenger::Policy::stateless_server(0));
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ }
+ DummyAuthClientServer dummy_auth;
+ std::unique_ptr<Messenger> msgr;
+ struct ServerDispatcher : Dispatcher {
+ std::mutex mutex;
+ std::condition_variable on_reply;
+ bool replied = false;
+ ServerDispatcher(CephContext* cct)
+ : Dispatcher(cct)
+ {}
+ bool ms_can_fast_dispatch_any() const override {
+ return true;
+ }
+ bool ms_can_fast_dispatch(const Message* m) const override {
+ return m->get_type() == CEPH_MSG_PING;
+ }
+ void ms_fast_dispatch(Message* m) override {
+ m->get_connection()->send_message(new MPing);
+ m->put();
+ {
+ std::lock_guard lock{mutex};
+ replied = true;
+ }
+ on_reply.notify_one();
+ }
+ bool ms_dispatch(Message*) override {
+ ceph_abort();
+ }
+ bool ms_handle_reset(Connection*) override {
+ return true;
+ }
+ void ms_handle_remote_reset(Connection*) override {
+ }
+ bool ms_handle_refused(Connection*) override {
+ return true;
+ }
+ void echo() {
+ replied = false;
+ std::unique_lock lock{mutex};
+ return on_reply.wait(lock, [this] { return replied; });
+ }
+ } dispatcher;
+ void echo() {
+ dispatcher.echo();
+ }
+};
+
+struct Client {
+ std::unique_ptr<Messenger> msgr;
+ Client(CephContext *cct)
+ : dummy_auth(cct), dispatcher(cct)
+ {
+ msgr.reset(Messenger::create(cct, "async", entity_name_t::CLIENT(-1), "ping", getpid()));
+ dummy_auth.auth_registry.refresh_config();
+ msgr->set_cluster_protocol(CEPH_OSD_PROTOCOL);
+ msgr->set_default_policy(Messenger::Policy::lossy_client(0));
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ }
+ DummyAuthClientServer dummy_auth;
+ struct ClientDispatcher : Dispatcher {
+ std::mutex mutex;
+ std::condition_variable on_reply;
+ bool replied = false;
+
+ ClientDispatcher(CephContext* cct)
+ : Dispatcher(cct)
+ {}
+ bool ms_can_fast_dispatch_any() const override {
+ return true;
+ }
+ bool ms_can_fast_dispatch(const Message* m) const override {
+ return m->get_type() == CEPH_MSG_PING;
+ }
+ void ms_fast_dispatch(Message* m) override {
+ m->put();
+ {
+ std::lock_guard lock{mutex};
+ replied = true;
+ }
+ on_reply.notify_one();
+ }
+ bool ms_dispatch(Message*) override {
+ ceph_abort();
+ }
+ bool ms_handle_reset(Connection *) override {
+ return true;
+ }
+ void ms_handle_remote_reset(Connection*) override {
+ }
+ bool ms_handle_refused(Connection*) override {
+ return true;
+ }
+ bool ping(Messenger* msgr, const entity_inst_t& peer) {
+ using namespace std::chrono_literals;
+ auto conn = msgr->connect_to(peer.name.type(),
+ entity_addrvec_t{peer.addr});
+ replied = false;
+ conn->send_message(new MPing);
+ std::unique_lock lock{mutex};
+ return on_reply.wait_for(lock, 500ms, [&] {
+ return replied;
+ });
+ }
+ } dispatcher;
+ void ping(const entity_inst_t& peer) {
+ dispatcher.ping(msgr.get(), peer);
+ }
+};
+} // namespace native_pingpong
+
+static void ceph_echo(CephContext* cct,
+ entity_addr_t addr, echo_role role, unsigned count)
+{
+ std::cout << "ceph/";
+ entity_inst_t entity{entity_name_t::OSD(0), addr};
+ if (role == echo_role::as_server) {
+ std::cout << "server listening at " << addr << std::endl;
+ native_pingpong::Server server{cct, entity};
+ server.msgr->bind(addr);
+ server.msgr->add_dispatcher_head(&server.dispatcher);
+ server.msgr->start();
+ for (unsigned i = 0; i < count; i++) {
+ server.echo();
+ }
+ server.msgr->shutdown();
+ server.msgr->wait();
+ } else {
+ std::cout << "client sending to " << addr << std::endl;
+ native_pingpong::Client client{cct};
+ client.msgr->add_dispatcher_head(&client.dispatcher);
+ client.msgr->start();
+ auto conn = client.msgr->connect_to(entity.name.type(),
+ entity_addrvec_t{entity.addr});
+ for (unsigned i = 0; i < count; i++) {
+ std::cout << "seq=" << i << std::endl;
+ client.ping(entity);
+ }
+ client.msgr->shutdown();
+ client.msgr->wait();
+ }
+}
+
+int main(int argc, char** argv)
+{
+ namespace po = boost::program_options;
+ po::options_description desc{"Allowed options"};
+ desc.add_options()
+ ("help,h", "show help message")
+ ("role", po::value<std::string>()->default_value("pong"),
+ "role to play (ping | pong)")
+ ("port", po::value<uint16_t>()->default_value(9010),
+ "port #")
+ ("nonce", po::value<uint32_t>()->default_value(42),
+ "a unique number to identify the pong server")
+ ("count", po::value<unsigned>()->default_value(10),
+ "stop after sending/echoing <count> MPing messages")
+ ("v2", po::value<bool>()->default_value(false),
+ "using msgr v2 protocol");
+ po::variables_map vm;
+ std::vector<std::string> unrecognized_options;
+ try {
+ auto parsed = po::command_line_parser(argc, argv)
+ .options(desc)
+ .allow_unregistered()
+ .run();
+ po::store(parsed, vm);
+ if (vm.count("help")) {
+ std::cout << desc << std::endl;
+ return 0;
+ }
+ po::notify(vm);
+ unrecognized_options = po::collect_unrecognized(parsed.options, po::include_positional);
+ } catch(const po::error& e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ entity_addr_t addr;
+ if (vm["v2"].as<bool>()) {
+ addr.set_type(entity_addr_t::TYPE_MSGR2);
+ } else {
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ }
+ addr.set_family(AF_INET);
+ addr.set_port(vm["port"].as<std::uint16_t>());
+ addr.set_nonce(vm["nonce"].as<std::uint32_t>());
+
+ echo_role role = echo_role::as_server;
+ if (vm["role"].as<std::string>() == "ping") {
+ role = echo_role::as_client;
+ }
+
+ auto count = vm["count"].as<unsigned>();
+ std::vector<const char*> args(argv, argv + argc);
+ auto cct = global_init(nullptr, args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+ common_init_finish(cct.get());
+ ceph_echo(cct.get(), addr, role, count);
+}
diff --git a/src/test/crimson/test_backfill.cc b/src/test/crimson/test_backfill.cc
new file mode 100644
index 000000000..6d7d62ce5
--- /dev/null
+++ b/src/test/crimson/test_backfill.cc
@@ -0,0 +1,501 @@
+#include <algorithm>
+#include <cstdlib>
+#include <deque>
+#include <functional>
+#include <initializer_list>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <set>
+#include <string>
+
+#include <boost/statechart/event_base.hpp>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/hobject.h"
+#include "crimson/osd/backfill_state.h"
+#include "osd/recovery_types.h"
+
+
+// The sole purpose is to convert from the string representation.
+// An alternative approach could use boost::range in FakeStore's
+// constructor.
+struct improved_hobject_t : hobject_t {
+ improved_hobject_t(const char parsable_name[]) {
+ this->parse(parsable_name);
+ }
+ improved_hobject_t(const hobject_t& obj)
+ : hobject_t(obj) {
+ }
+ bool operator==(const improved_hobject_t& rhs) const {
+ return static_cast<const hobject_t&>(*this) == \
+ static_cast<const hobject_t&>(rhs);
+ }
+};
+
+
+struct FakeStore {
+ using objs_t = std::map<improved_hobject_t, eversion_t>;
+
+ objs_t objs;
+
+ void push(const hobject_t& obj, eversion_t version) {
+ objs[obj] = version;
+ }
+
+ void drop(const hobject_t& obj, const eversion_t version) {
+ auto it = objs.find(obj);
+ ceph_assert(it != std::end(objs));
+ ceph_assert(it->second == version);
+ objs.erase(it);
+ }
+
+ template <class Func>
+ hobject_t list(const hobject_t& start, Func&& per_entry) const {
+ auto it = objs.lower_bound(start);
+ for (auto max = std::numeric_limits<std::uint64_t>::max();
+ it != std::end(objs) && max > 0;
+ ++it, --max) {
+ per_entry(*it);
+ }
+ return it != std::end(objs) ? static_cast<const hobject_t&>(it->first)
+ : hobject_t::get_max();
+ }
+
+ bool operator==(const FakeStore& rhs) const {
+ return std::size(objs) == std::size(rhs.objs) && \
+ std::equal(std::begin(objs), std::end(objs), std::begin(rhs.objs));
+ }
+ bool operator!=(const FakeStore& rhs) const {
+ return !(*this == rhs);
+ }
+};
+
+
+struct FakeReplica {
+ FakeStore store;
+ hobject_t last_backfill;
+
+ FakeReplica(FakeStore&& store)
+ : store(std::move(store)) {
+ }
+};
+
+struct FakePrimary {
+ FakeStore store;
+ eversion_t last_update;
+ eversion_t projected_last_update;
+ eversion_t log_tail;
+
+ FakePrimary(FakeStore&& store)
+ : store(std::move(store)) {
+ }
+};
+
+class BackfillFixture : public crimson::osd::BackfillState::BackfillListener {
+ friend class BackfillFixtureBuilder;
+
+ FakePrimary backfill_source;
+ std::map<pg_shard_t, FakeReplica> backfill_targets;
+ std::map<pg_shard_t,
+ std::vector<std::pair<hobject_t, eversion_t>>> enqueued_drops;
+ std::deque<
+ boost::intrusive_ptr<
+ const boost::statechart::event_base>> events_to_dispatch;
+ crimson::osd::BackfillState backfill_state;
+
+ BackfillFixture(FakePrimary&& backfill_source,
+ std::map<pg_shard_t, FakeReplica>&& backfill_targets);
+
+ template <class EventT>
+ void schedule_event(const EventT& event) {
+ events_to_dispatch.emplace_back(event.intrusive_from_this());
+ }
+
+ // BackfillListener {
+ void request_replica_scan(
+ const pg_shard_t& target,
+ const hobject_t& begin,
+ const hobject_t& end) override;
+
+ void request_primary_scan(
+ const hobject_t& begin) override;
+
+ void enqueue_push(
+ const hobject_t& obj,
+ const eversion_t& v) override;
+
+ void enqueue_drop(
+ const pg_shard_t& target,
+ const hobject_t& obj,
+ const eversion_t& v) override;
+
+ void maybe_flush() override;
+
+ void update_peers_last_backfill(
+ const hobject_t& new_last_backfill) override;
+
+ bool budget_available() const override;
+
+public:
+ MOCK_METHOD(void, backfilled, (), (override));
+ // }
+
+ void next_round(std::size_t how_many=1) {
+ ceph_assert(events_to_dispatch.size() >= how_many);
+ while (how_many-- > 0) {
+ backfill_state.process_event(std::move(events_to_dispatch.front()));
+ events_to_dispatch.pop_front();
+ }
+ }
+
+ void next_till_done() {
+ while (!events_to_dispatch.empty()) {
+ next_round();
+ }
+ }
+
+ bool all_stores_look_like(const FakeStore& reference) const {
+ const bool all_replica_match = std::all_of(
+ std::begin(backfill_targets), std::end(backfill_targets),
+ [&reference] (const auto kv) {
+ return kv.second.store == reference;
+ });
+ return backfill_source.store == reference && all_replica_match;
+ }
+
+ struct PeeringFacade;
+ struct PGFacade;
+};
+
+struct BackfillFixture::PeeringFacade
+ : public crimson::osd::BackfillState::PeeringFacade {
+ FakePrimary& backfill_source;
+ std::map<pg_shard_t, FakeReplica>& backfill_targets;
+ // sorry, this is duplicative but that's the interface
+ std::set<pg_shard_t> backfill_targets_as_set;
+
+ PeeringFacade(FakePrimary& backfill_source,
+ std::map<pg_shard_t, FakeReplica>& backfill_targets)
+ : backfill_source(backfill_source),
+ backfill_targets(backfill_targets) {
+ std::transform(
+ std::begin(backfill_targets), std::end(backfill_targets),
+ std::inserter(backfill_targets_as_set, std::end(backfill_targets_as_set)),
+ [](auto pair) {
+ return pair.first;
+ });
+ }
+
+ hobject_t earliest_backfill() const override {
+ hobject_t e = hobject_t::get_max();
+ for (const auto& kv : backfill_targets) {
+ e = std::min(kv.second.last_backfill, e);
+ }
+ return e;
+ }
+ const std::set<pg_shard_t>& get_backfill_targets() const override {
+ return backfill_targets_as_set;
+ }
+ const hobject_t& get_peer_last_backfill(pg_shard_t peer) const override {
+ return backfill_targets.at(peer).last_backfill;
+ }
+ const eversion_t& get_last_update() const override {
+ return backfill_source.last_update;
+ }
+ const eversion_t& get_log_tail() const override {
+ return backfill_source.log_tail;
+ }
+
+ void scan_log_after(eversion_t, scan_log_func_t) const override {
+ /* NOP */
+ }
+
+ bool is_backfill_target(pg_shard_t peer) const override {
+ return backfill_targets.count(peer) == 1;
+ }
+ void update_complete_backfill_object_stats(const hobject_t &hoid,
+ const pg_stat_t &stats) override {
+ }
+ bool is_backfilling() const override {
+ return true;
+ }
+};
+
+struct BackfillFixture::PGFacade : public crimson::osd::BackfillState::PGFacade {
+ FakePrimary& backfill_source;
+
+ PGFacade(FakePrimary& backfill_source)
+ : backfill_source(backfill_source) {
+ }
+
+ const eversion_t& get_projected_last_update() const override {
+ return backfill_source.projected_last_update;
+ }
+};
+
+BackfillFixture::BackfillFixture(
+ FakePrimary&& backfill_source,
+ std::map<pg_shard_t, FakeReplica>&& backfill_targets)
+ : backfill_source(std::move(backfill_source)),
+ backfill_targets(std::move(backfill_targets)),
+ backfill_state(*this,
+ std::make_unique<PeeringFacade>(this->backfill_source,
+ this->backfill_targets),
+ std::make_unique<PGFacade>(this->backfill_source))
+{
+ backfill_state.process_event(crimson::osd::BackfillState::Triggered{}.intrusive_from_this());
+}
+
+void BackfillFixture::request_replica_scan(
+ const pg_shard_t& target,
+ const hobject_t& begin,
+ const hobject_t& end)
+{
+ BackfillInterval bi;
+ bi.end = backfill_targets.at(target).store.list(begin, [&bi](auto kv) {
+ bi.objects.insert(std::move(kv));
+ });
+ bi.begin = begin;
+ bi.version = backfill_source.last_update;
+
+ schedule_event(crimson::osd::BackfillState::ReplicaScanned{ target, std::move(bi) });
+}
+
+void BackfillFixture::request_primary_scan(
+ const hobject_t& begin)
+{
+ BackfillInterval bi;
+ bi.end = backfill_source.store.list(begin, [&bi](auto kv) {
+ bi.objects.insert(std::move(kv));
+ });
+ bi.begin = begin;
+ bi.version = backfill_source.last_update;
+
+ schedule_event(crimson::osd::BackfillState::PrimaryScanned{ std::move(bi) });
+}
+
+void BackfillFixture::enqueue_push(
+ const hobject_t& obj,
+ const eversion_t& v)
+{
+ for (auto& [ _, bt ] : backfill_targets) {
+ bt.store.push(obj, v);
+ }
+ schedule_event(crimson::osd::BackfillState::ObjectPushed{ obj });
+}
+
+void BackfillFixture::enqueue_drop(
+ const pg_shard_t& target,
+ const hobject_t& obj,
+ const eversion_t& v)
+{
+ enqueued_drops[target].emplace_back(obj, v);
+}
+
+void BackfillFixture::maybe_flush()
+{
+ for (const auto& [target, versioned_objs] : enqueued_drops) {
+ for (const auto& [obj, v] : versioned_objs) {
+ backfill_targets.at(target).store.drop(obj, v);
+ }
+ }
+ enqueued_drops.clear();
+}
+
+void BackfillFixture::update_peers_last_backfill(
+ const hobject_t& new_last_backfill)
+{
+}
+
+bool BackfillFixture::budget_available() const
+{
+ return true;
+}
+
+struct BackfillFixtureBuilder {
+ FakeStore backfill_source;
+ std::map<pg_shard_t, FakeReplica> backfill_targets;
+
+ static BackfillFixtureBuilder add_source(FakeStore::objs_t objs) {
+ BackfillFixtureBuilder bfb;
+ bfb.backfill_source = FakeStore{ std::move(objs) };
+ return bfb;
+ }
+
+ BackfillFixtureBuilder&& add_target(FakeStore::objs_t objs) && {
+ const auto new_osd_num = std::size(backfill_targets);
+ const auto [ _, inserted ] = backfill_targets.emplace(
+ new_osd_num, FakeReplica{ FakeStore{std::move(objs)} });
+ ceph_assert(inserted);
+ return std::move(*this);
+ }
+
+ BackfillFixture get_result() && {
+ return BackfillFixture{ std::move(backfill_source),
+ std::move(backfill_targets) };
+ }
+};
+
+// The straightest case: single primary, single replica. All have the same
+// content in their object stores, so the entire backfill boils into just
+// `request_primary_scan()` and `request_replica_scan()`.
+TEST(backfill, same_primary_same_replica)
+{
+ const auto reference_store = FakeStore{ {
+ { "1:00058bcc:::rbd_data.1018ac3e755.00000000000000d5:head", {10, 234} },
+ { "1:00ed7f8e:::rbd_data.1018ac3e755.00000000000000af:head", {10, 196} },
+ { "1:01483aea:::rbd_data.1018ac3e755.0000000000000095:head", {10, 169} },
+ }};
+ auto cluster_fixture = BackfillFixtureBuilder::add_source(
+ reference_store.objs
+ ).add_target(
+ reference_store.objs
+ ).get_result();
+
+ cluster_fixture.next_round();
+ EXPECT_CALL(cluster_fixture, backfilled);
+ cluster_fixture.next_round();
+ EXPECT_TRUE(cluster_fixture.all_stores_look_like(reference_store));
+}
+
+TEST(backfill, one_empty_replica)
+{
+ const auto reference_store = FakeStore{ {
+ { "1:00058bcc:::rbd_data.1018ac3e755.00000000000000d5:head", {10, 234} },
+ { "1:00ed7f8e:::rbd_data.1018ac3e755.00000000000000af:head", {10, 196} },
+ { "1:01483aea:::rbd_data.1018ac3e755.0000000000000095:head", {10, 169} },
+ }};
+ auto cluster_fixture = BackfillFixtureBuilder::add_source(
+ reference_store.objs
+ ).add_target(
+ { /* nothing */ }
+ ).get_result();
+
+ cluster_fixture.next_round();
+ cluster_fixture.next_round();
+ cluster_fixture.next_round(2);
+ EXPECT_CALL(cluster_fixture, backfilled);
+ cluster_fixture.next_round();
+ EXPECT_TRUE(cluster_fixture.all_stores_look_like(reference_store));
+}
+
+TEST(backfill, two_empty_replicas)
+{
+ const auto reference_store = FakeStore{ {
+ { "1:00058bcc:::rbd_data.1018ac3e755.00000000000000d5:head", {10, 234} },
+ { "1:00ed7f8e:::rbd_data.1018ac3e755.00000000000000af:head", {10, 196} },
+ { "1:01483aea:::rbd_data.1018ac3e755.0000000000000095:head", {10, 169} },
+ }};
+ auto cluster_fixture = BackfillFixtureBuilder::add_source(
+ reference_store.objs
+ ).add_target(
+ { /* nothing 1 */ }
+ ).add_target(
+ { /* nothing 2 */ }
+ ).get_result();
+
+ EXPECT_CALL(cluster_fixture, backfilled);
+ cluster_fixture.next_till_done();
+
+ EXPECT_TRUE(cluster_fixture.all_stores_look_like(reference_store));
+}
+
+namespace StoreRandomizer {
+ // FIXME: copied & pasted from test/test_snap_mapper.cc. We need to
+ // find a way to avoid code duplication in test. A static library?
+ std::string random_string(std::size_t size) {
+ std::string name;
+ for (size_t j = 0; j < size; ++j) {
+ name.push_back('a' + (std::rand() % 26));
+ }
+ return name;
+ }
+
+ hobject_t random_hobject() {
+ uint32_t mask{0};
+ uint32_t bits{0};
+ return hobject_t(
+ random_string(1+(std::rand() % 16)),
+ random_string(1+(std::rand() % 16)),
+ snapid_t(std::rand() % 1000),
+ (std::rand() & ((~0)<<bits)) | (mask & ~((~0)<<bits)),
+ 0, random_string(std::rand() % 16));
+ }
+
+ eversion_t random_eversion() {
+ return eversion_t{ std::rand() % 512U, std::rand() % 256UL };
+ }
+
+ FakeStore create() {
+ FakeStore store;
+ for (std::size_t i = std::rand() % 2048; i > 0; --i) {
+ store.push(random_hobject(), random_eversion());
+ }
+ return store;
+ }
+
+ template <class... Args>
+ void execute_random(Args&&... args) {
+ std::array<std::function<void()>, sizeof...(Args)> funcs = {
+ std::forward<Args>(args)...
+ };
+ return std::move(funcs[std::rand() % std::size(funcs)])();
+ }
+
+ FakeStore mutate(const FakeStore& source_store) {
+ FakeStore mutated_store;
+ source_store.list(hobject_t{}, [&] (const auto& kv) {
+ const auto &oid = kv.first;
+ const auto &version = kv.second;
+ execute_random(
+ [] { /* just drop the entry */ },
+ [&] { mutated_store.push(oid, version); },
+ [&] { mutated_store.push(oid, random_eversion()); },
+ [&] { mutated_store.push(random_hobject(), version); },
+ [&] {
+ for (auto how_many = std::rand() % 8; how_many > 0; --how_many) {
+ mutated_store.push(random_hobject(), random_eversion());
+ }
+ }
+ );
+ });
+ return mutated_store;
+ }
+}
+
+// The name might suggest randomness is involved here. Well, that's true
+// but till we know the seed the test still is repeatable.
+TEST(backfill, one_pseudorandomized_replica)
+{
+ const auto reference_store = StoreRandomizer::create();
+ auto cluster_fixture = BackfillFixtureBuilder::add_source(
+ reference_store.objs
+ ).add_target(
+ StoreRandomizer::mutate(reference_store).objs
+ ).get_result();
+
+ EXPECT_CALL(cluster_fixture, backfilled);
+ cluster_fixture.next_till_done();
+
+ EXPECT_TRUE(cluster_fixture.all_stores_look_like(reference_store));
+}
+
+TEST(backfill, two_pseudorandomized_replicas)
+{
+ const auto reference_store = StoreRandomizer::create();
+ auto cluster_fixture = BackfillFixtureBuilder::add_source(
+ reference_store.objs
+ ).add_target(
+ StoreRandomizer::mutate(reference_store).objs
+ ).add_target(
+ StoreRandomizer::mutate(reference_store).objs
+ ).get_result();
+
+ EXPECT_CALL(cluster_fixture, backfilled);
+ cluster_fixture.next_till_done();
+
+ EXPECT_TRUE(cluster_fixture.all_stores_look_like(reference_store));
+}
diff --git a/src/test/crimson/test_buffer.cc b/src/test/crimson/test_buffer.cc
new file mode 100644
index 000000000..64a815bd2
--- /dev/null
+++ b/src/test/crimson/test_buffer.cc
@@ -0,0 +1,50 @@
+#include <iostream>
+#include <seastar/core/app-template.hh>
+#include <seastar/core/future-util.hh>
+#include <seastar/core/reactor.hh>
+#include "include/buffer.h"
+
+// allocate a foreign buffer on each cpu, collect them all into a bufferlist,
+// and destruct it on this cpu
+seastar::future<> test_foreign_bufferlist()
+{
+ auto make_foreign_buffer = [] (unsigned cpu) {
+ return seastar::smp::submit_to(cpu, [=] {
+ bufferlist bl;
+ seastar::temporary_buffer<char> buf("abcd", 4);
+ bl.append(buffer::create(std::move(buf)));
+ return bl;
+ });
+ };
+ auto reduce = [] (bufferlist&& lhs, bufferlist&& rhs) {
+ bufferlist bl;
+ bl.claim_append(lhs);
+ bl.claim_append(rhs);
+ return bl;
+ };
+ return seastar::map_reduce(seastar::smp::all_cpus(), make_foreign_buffer,
+ bufferlist(), reduce).then(
+ [] (bufferlist&& bl) {
+ if (bl.length() != 4 * seastar::smp::count) {
+ auto e = std::make_exception_ptr(std::runtime_error("wrong buffer size"));
+ return seastar::make_exception_future<>(e);
+ }
+ bl.clear();
+ return seastar::make_ready_future<>();
+ });
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ return app.run(argc, argv, [] {
+ return seastar::now().then(
+ &test_foreign_bufferlist
+ ).then([] {
+ std::cout << "All tests succeeded" << std::endl;
+ }).handle_exception([] (auto eptr) {
+ std::cout << "Test failure" << std::endl;
+ return seastar::make_exception_future<>(eptr);
+ });
+ });
+}
diff --git a/src/test/crimson/test_config.cc b/src/test/crimson/test_config.cc
new file mode 100644
index 000000000..7541c0931
--- /dev/null
+++ b/src/test/crimson/test_config.cc
@@ -0,0 +1,109 @@
+#include <chrono>
+#include <string>
+#include <numeric>
+#include <seastar/core/app-template.hh>
+#include <seastar/core/sharded.hh>
+#include "common/ceph_argparse.h"
+#include "common/config_obs.h"
+#include "crimson/common/config_proxy.h"
+
+using namespace std::literals;
+using Config = crimson::common::ConfigProxy;
+const std::string test_uint_option = "osd_max_pgls";
+const uint64_t INVALID_VALUE = (uint64_t)(-1);
+const uint64_t EXPECTED_VALUE = 42;
+
+class ConfigObs : public ceph::md_config_obs_impl<Config> {
+ uint64_t last_change = INVALID_VALUE;
+ uint64_t num_changes = 0;
+
+ const char** get_tracked_conf_keys() const override {
+ static const char* keys[] = {
+ test_uint_option.c_str(),
+ nullptr,
+ };
+ return keys;
+ }
+ void handle_conf_change(const Config& conf,
+ const std::set <std::string> &changes) override{
+ if (changes.count(test_uint_option)) {
+ last_change = conf.get_val<uint64_t>(test_uint_option);
+ num_changes += 1;
+ }
+ }
+public:
+ ConfigObs() {
+ crimson::common::local_conf().add_observer(this);
+ }
+
+ uint64_t get_last_change() const { return last_change; }
+ uint64_t get_num_changes() const { return num_changes; }
+ seastar::future<> stop() {
+ crimson::common::local_conf().remove_observer(this);
+ return seastar::make_ready_future<>();
+ }
+};
+
+seastar::sharded<ConfigObs> sharded_cobs;
+
+static seastar::future<> test_config()
+{
+ return crimson::common::sharded_conf().start(EntityName{}, "ceph"sv).then([] {
+ std::vector<const char*> args;
+ std::string cluster;
+ std::string conf_file_list;
+ auto init_params = ceph_argparse_early_args(args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ &cluster,
+ &conf_file_list);
+ auto& conf = crimson::common::local_conf();
+ conf->name = init_params.name;
+ conf->cluster = cluster;
+ return conf.parse_config_files(conf_file_list);
+ }).then([] {
+ return crimson::common::sharded_conf().invoke_on(0, &Config::start);
+ }).then([] {
+ return sharded_cobs.start();
+ }).then([] {
+ auto& conf = crimson::common::local_conf();
+ return conf.set_val(test_uint_option, std::to_string(EXPECTED_VALUE));
+ }).then([] {
+ return crimson::common::sharded_conf().invoke_on_all([](Config& config) {
+ if (config.get_val<uint64_t>(test_uint_option) != EXPECTED_VALUE) {
+ throw std::runtime_error("configurations don't match");
+ }
+ if (sharded_cobs.local().get_last_change() != EXPECTED_VALUE) {
+ throw std::runtime_error("last applied changes don't match the latest config");
+ }
+ if (sharded_cobs.local().get_num_changes() != 1) {
+ throw std::runtime_error("num changes don't match actual changes");
+ }
+ });
+ }).finally([] {
+ return sharded_cobs.stop();
+ }).finally([] {
+ return crimson::common::sharded_conf().stop();
+ });
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ return app.run(argc, argv, [&] {
+ return test_config().then([] {
+ std::cout << "All tests succeeded" << std::endl;
+ }).handle_exception([] (auto eptr) {
+ std::cout << "Test failure" << std::endl;
+ return seastar::make_exception_future<>(eptr);
+ });
+ });
+}
+
+
+/*
+ * Local Variables:
+ * compile-command: "make -j4 \
+ * -C ../../../build \
+ * unittest_seastar_config"
+ * End:
+ */
diff --git a/src/test/crimson/test_denc.cc b/src/test/crimson/test_denc.cc
new file mode 100644
index 000000000..10ebd6dce
--- /dev/null
+++ b/src/test/crimson/test_denc.cc
@@ -0,0 +1,53 @@
+#include <string>
+#include <seastar/core/temporary_buffer.hh>
+#include <gtest/gtest.h>
+#include "include/denc.h"
+#include "common/buffer_seastar.h"
+
+using temporary_buffer = seastar::temporary_buffer<char>;
+using buffer_iterator = seastar_buffer_iterator;
+using const_buffer_iterator = const_seastar_buffer_iterator;
+
+template<typename T>
+void test_denc(T v) {
+ // estimate
+ size_t s = 0;
+ denc(v, s);
+ ASSERT_NE(s, 0u);
+
+ // encode
+ temporary_buffer buf{s};
+ buffer_iterator enc{buf};
+ denc(v, enc);
+ size_t len = enc.get() - buf.begin();
+ ASSERT_LE(len, s);
+
+ // decode
+ T out;
+ temporary_buffer encoded = buf.share();
+ encoded.trim(len);
+ const_buffer_iterator dec{encoded};
+ denc(out, dec);
+ ASSERT_EQ(v, out);
+ ASSERT_EQ(dec.get(), enc.get());
+}
+
+TEST(denc, simple)
+{
+ test_denc((uint8_t)4);
+ test_denc((int8_t)-5);
+ test_denc((uint16_t)6);
+ test_denc((int16_t)-7);
+ test_denc((uint32_t)8);
+ test_denc((int32_t)-9);
+ test_denc((uint64_t)10);
+ test_denc((int64_t)-11);
+}
+
+TEST(denc, string)
+{
+ std::string a, b("hi"), c("multi\nline\n");
+ test_denc(a);
+ test_denc(b);
+ test_denc(c);
+}
diff --git a/src/test/crimson/test_errorator.cc b/src/test/crimson/test_errorator.cc
new file mode 100644
index 000000000..939c6cde8
--- /dev/null
+++ b/src/test/crimson/test_errorator.cc
@@ -0,0 +1,99 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <boost/iterator/counting_iterator.hpp>
+#include <numeric>
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "crimson/common/errorator.h"
+#include "crimson/common/errorator-loop.h"
+#include "crimson/common/log.h"
+#include "seastar/core/sleep.hh"
+
+struct errorator_test_t : public seastar_test_suite_t {
+ using ertr = crimson::errorator<crimson::ct_error::invarg>;
+ ertr::future<> test_do_until() {
+ return crimson::repeat([i=0]() mutable {
+ if (i < 5) {
+ ++i;
+ return ertr::make_ready_future<seastar::stop_iteration>(
+ seastar::stop_iteration::no);
+ } else {
+ return ertr::make_ready_future<seastar::stop_iteration>(
+ seastar::stop_iteration::yes);
+ }
+ });
+ }
+ static constexpr int SIZE = 42;
+ ertr::future<> test_parallel_for_each() {
+ auto sum = std::make_unique<int>(0);
+ return ertr::parallel_for_each(
+ boost::make_counting_iterator(0),
+ boost::make_counting_iterator(SIZE),
+ [sum=sum.get()](int i) {
+ *sum += i;
+ }).safe_then([sum=std::move(sum)] {
+ int expected = std::accumulate(boost::make_counting_iterator(0),
+ boost::make_counting_iterator(SIZE),
+ 0);
+ ASSERT_EQ(*sum, expected);
+ });
+ }
+ struct noncopyable_t {
+ constexpr noncopyable_t() = default;
+ ~noncopyable_t() = default;
+ noncopyable_t(noncopyable_t&&) = default;
+ private:
+ noncopyable_t(const noncopyable_t&) = delete;
+ noncopyable_t& operator=(const noncopyable_t&) = delete;
+ };
+ ertr::future<> test_non_copy_then() {
+ return create_noncopyable().safe_then([](auto t) {
+ return ertr::now();
+ });
+ }
+ ertr::future<int> test_futurization() {
+ // we don't want to be enforced to always do `make_ready_future(...)`.
+ // as in seastar::future, the futurization should take care about
+ // turning non-future types (e.g. int) into futurized ones (e.g.
+ // ertr::future<int>).
+ return ertr::now().safe_then([] {
+ return 42;
+ }).safe_then([](int life) {
+ return ertr::make_ready_future<int>(life);
+ });
+ }
+private:
+ ertr::future<noncopyable_t> create_noncopyable() {
+ return ertr::make_ready_future<noncopyable_t>();
+ }
+};
+
+TEST_F(errorator_test_t, basic)
+{
+ run_async([this] {
+ test_do_until().unsafe_get0();
+ });
+}
+
+TEST_F(errorator_test_t, parallel_for_each)
+{
+ run_async([this] {
+ test_parallel_for_each().unsafe_get0();
+ });
+}
+
+TEST_F(errorator_test_t, non_copy_then)
+{
+ run_async([this] {
+ test_non_copy_then().unsafe_get0();
+ });
+}
+
+TEST_F(errorator_test_t, test_futurization)
+{
+ run_async([this] {
+ test_futurization().unsafe_get0();
+ });
+}
diff --git a/src/test/crimson/test_fixed_kv_node_layout.cc b/src/test/crimson/test_fixed_kv_node_layout.cc
new file mode 100644
index 000000000..e6377ec14
--- /dev/null
+++ b/src/test/crimson/test_fixed_kv_node_layout.cc
@@ -0,0 +1,376 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <stdio.h>
+#include <iostream>
+
+#include "gtest/gtest.h"
+
+#include "crimson/common/fixed_kv_node_layout.h"
+
+using namespace crimson;
+using namespace crimson::common;
+
+struct test_val_t {
+ uint32_t t1 = 0;
+ int32_t t2 = 0;
+
+ bool operator==(const test_val_t &rhs) const {
+ return rhs.t1 == t1 && rhs.t2 == t2;
+ }
+ bool operator!=(const test_val_t &rhs) const {
+ return !(*this == rhs);
+ }
+};
+
+struct test_val_le_t {
+ ceph_le32 t1{0};
+ ceph_les32 t2{0};
+
+ test_val_le_t() = default;
+ test_val_le_t(const test_val_le_t &) = default;
+ test_val_le_t(const test_val_t &nv)
+ : t1(nv.t1), t2(nv.t2) {}
+
+ operator test_val_t() const {
+ return test_val_t{t1, t2};
+ }
+};
+
+struct test_meta_t {
+ uint32_t t1 = 0;
+ uint32_t t2 = 0;
+
+ bool operator==(const test_meta_t &rhs) const {
+ return rhs.t1 == t1 && rhs.t2 == t2;
+ }
+ bool operator!=(const test_meta_t &rhs) const {
+ return !(*this == rhs);
+ }
+
+ std::pair<test_meta_t, test_meta_t> split_into(uint32_t pivot) const {
+ return std::make_pair(
+ test_meta_t{t1, pivot},
+ test_meta_t{pivot, t2});
+ }
+
+ static test_meta_t merge_from(const test_meta_t &lhs, const test_meta_t &rhs) {
+ return test_meta_t{lhs.t1, rhs.t2};
+ }
+
+ static std::pair<test_meta_t, test_meta_t>
+ rebalance(const test_meta_t &lhs, const test_meta_t &rhs, uint32_t pivot) {
+ return std::make_pair(
+ test_meta_t{lhs.t1, pivot},
+ test_meta_t{pivot, rhs.t2});
+ }
+};
+
+struct test_meta_le_t {
+ ceph_le32 t1{0};
+ ceph_le32 t2{0};
+
+ test_meta_le_t() = default;
+ test_meta_le_t(const test_meta_le_t &) = default;
+ test_meta_le_t(const test_meta_t &nv)
+ : t1(nv.t1), t2(nv.t2) {}
+
+ operator test_meta_t() const {
+ return test_meta_t{t1, t2};
+ }
+};
+
+constexpr size_t CAPACITY = 339;
+
+struct TestNode : FixedKVNodeLayout<
+ CAPACITY,
+ test_meta_t, test_meta_le_t,
+ uint32_t, ceph_le32,
+ test_val_t, test_val_le_t> {
+ char buf[4096];
+ TestNode() : FixedKVNodeLayout(buf) {
+ memset(buf, 0, sizeof(buf));
+ set_meta({0, std::numeric_limits<uint32_t>::max()});
+ }
+ TestNode(const TestNode &rhs)
+ : FixedKVNodeLayout(buf) {
+ ::memcpy(buf, rhs.buf, sizeof(buf));
+ }
+
+ TestNode &operator=(const TestNode &rhs) {
+ memcpy(buf, rhs.buf, sizeof(buf));
+ return *this;
+ }
+};
+
+TEST(FixedKVNodeTest, basic) {
+ auto node = TestNode();
+ ASSERT_EQ(node.get_size(), 0);
+
+ auto val = test_val_t{ 1, 1 };
+ node.journal_insert(node.begin(), 1, val, nullptr);
+ ASSERT_EQ(node.get_size(), 1);
+
+ auto iter = node.begin();
+ ASSERT_EQ(iter.get_key(), 1);
+ ASSERT_EQ(val, iter.get_val());
+
+ ASSERT_EQ(std::numeric_limits<uint32_t>::max(), iter.get_next_key_or_max());
+}
+
+TEST(FixedKVNodeTest, at_capacity) {
+ auto node = TestNode();
+ ASSERT_EQ(CAPACITY, node.get_capacity());
+
+ ASSERT_EQ(node.get_size(), 0);
+
+ unsigned short num = 0;
+ auto iter = node.begin();
+ while (num < CAPACITY) {
+ node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
+ ++num;
+ ++iter;
+ }
+ ASSERT_EQ(node.get_size(), CAPACITY);
+
+ num = 0;
+ for (auto &i : node) {
+ ASSERT_EQ(i.get_key(), num);
+ ASSERT_EQ(i.get_val(), (test_val_t{num, num}));
+ if (num < (CAPACITY - 1)) {
+ ASSERT_EQ(i.get_next_key_or_max(), num + 1);
+ } else {
+ ASSERT_EQ(std::numeric_limits<uint32_t>::max(), i.get_next_key_or_max());
+ }
+ ++num;
+ }
+}
+
+TEST(FixedKVNodeTest, split) {
+ auto node = TestNode();
+
+ ASSERT_EQ(node.get_size(), 0);
+
+ unsigned short num = 0;
+ auto iter = node.begin();
+ while (num < CAPACITY) {
+ node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
+ ++num;
+ ++iter;
+ }
+ ASSERT_EQ(node.get_size(), CAPACITY);
+
+ auto split_left = TestNode();
+ auto split_right = TestNode();
+ node.split_into(split_left, split_right);
+
+ ASSERT_EQ(split_left.get_size() + split_right.get_size(), CAPACITY);
+ ASSERT_EQ(split_left.get_meta().t1, split_left.begin()->get_key());
+ ASSERT_EQ(split_left.get_meta().t2, split_right.get_meta().t1);
+ ASSERT_EQ(split_right.get_meta().t2, std::numeric_limits<uint32_t>::max());
+
+ num = 0;
+ for (auto &i : split_left) {
+ ASSERT_EQ(i.get_key(), num);
+ ASSERT_EQ(i.get_val(), (test_val_t{num, num}));
+ if (num < split_left.get_size() - 1) {
+ ASSERT_EQ(i.get_next_key_or_max(), num + 1);
+ } else {
+ ASSERT_EQ(std::numeric_limits<uint32_t>::max(), i.get_next_key_or_max());
+ }
+ ++num;
+ }
+ for (auto &i : split_right) {
+ ASSERT_EQ(i.get_key(), num);
+ ASSERT_EQ(i.get_val(), (test_val_t{num, num}));
+ if (num < CAPACITY - 1) {
+ ASSERT_EQ(i.get_next_key_or_max(), num + 1);
+ } else {
+ ASSERT_EQ(std::numeric_limits<uint32_t>::max(), i.get_next_key_or_max());
+ }
+ ++num;
+ }
+ ASSERT_EQ(num, CAPACITY);
+}
+
+TEST(FixedKVNodeTest, merge) {
+ auto node = TestNode();
+ auto node2 = TestNode();
+
+ ASSERT_EQ(node.get_size(), 0);
+ ASSERT_EQ(node2.get_size(), 0);
+
+ unsigned short num = 0;
+ auto iter = node.begin();
+ while (num < CAPACITY/2) {
+ node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
+ ++num;
+ ++iter;
+ }
+ node.set_meta({0, num});
+ node2.set_meta({num, std::numeric_limits<uint32_t>::max()});
+ iter = node2.begin();
+ while (num < (2 * (CAPACITY / 2))) {
+ node2.journal_insert(iter, num, test_val_t{num, num}, nullptr);
+ ++num;
+ ++iter;
+ }
+
+ ASSERT_EQ(node.get_size(), CAPACITY / 2);
+ ASSERT_EQ(node2.get_size(), CAPACITY / 2);
+
+ auto total = node.get_size() + node2.get_size();
+
+ auto node_merged = TestNode();
+ node_merged.merge_from(node, node2);
+
+ ASSERT_EQ(
+ node_merged.get_meta(),
+ (test_meta_t{0, std::numeric_limits<uint32_t>::max()}));
+
+ ASSERT_EQ(node_merged.get_size(), total);
+ num = 0;
+ for (auto &i : node_merged) {
+ ASSERT_EQ(i.get_key(), num);
+ ASSERT_EQ(i.get_val(), (test_val_t{num, num}));
+ if (num < node_merged.get_size() - 1) {
+ ASSERT_EQ(i.get_next_key_or_max(), num + 1);
+ } else {
+ ASSERT_EQ(std::numeric_limits<uint32_t>::max(), i.get_next_key_or_max());
+ }
+ ++num;
+ }
+ ASSERT_EQ(num, total);
+}
+
+void run_balance_test(unsigned left, unsigned right, bool prefer_left)
+{
+ auto node = TestNode();
+ auto node2 = TestNode();
+
+ ASSERT_EQ(node.get_size(), 0);
+ ASSERT_EQ(node2.get_size(), 0);
+
+ unsigned short num = 0;
+ auto iter = node.begin();
+ while (num < left) {
+ node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
+ ++num;
+ ++iter;
+ }
+ node.set_meta({0, num});
+ node2.set_meta({num, std::numeric_limits<uint32_t>::max()});
+ iter = node2.begin();
+ while (num < (left + right)) {
+ node2.journal_insert(iter, num, test_val_t{num, num}, nullptr);
+ ++num;
+ ++iter;
+ }
+
+ ASSERT_EQ(node.get_size(), left);
+ ASSERT_EQ(node2.get_size(), right);
+
+ auto total = node.get_size() + node2.get_size();
+
+ auto node_balanced = TestNode();
+ auto node_balanced2 = TestNode();
+ auto pivot = TestNode::balance_into_new_nodes(
+ node,
+ node2,
+ prefer_left,
+ node_balanced,
+ node_balanced2);
+
+ ASSERT_EQ(total, node_balanced.get_size() + node_balanced2.get_size());
+
+ unsigned left_size, right_size;
+ if (total % 2) {
+ if (prefer_left) {
+ left_size = (total/2) + 1;
+ right_size = total/2;
+ } else {
+ left_size = total/2;
+ right_size = (total/2) + 1;
+ }
+ } else {
+ left_size = right_size = total/2;
+ }
+ ASSERT_EQ(pivot, left_size);
+ ASSERT_EQ(left_size, node_balanced.get_size());
+ ASSERT_EQ(right_size, node_balanced2.get_size());
+
+ ASSERT_EQ(
+ node_balanced.get_meta(),
+ (test_meta_t{0, left_size}));
+ ASSERT_EQ(
+ node_balanced2.get_meta(),
+ (test_meta_t{left_size, std::numeric_limits<uint32_t>::max()}));
+
+ num = 0;
+ for (auto &i: node_balanced) {
+ ASSERT_EQ(i.get_key(), num);
+ ASSERT_EQ(i.get_val(), (test_val_t{num, num}));
+ if (num < node_balanced.get_size() - 1) {
+ ASSERT_EQ(i.get_next_key_or_max(), num + 1);
+ } else {
+ ASSERT_EQ(std::numeric_limits<uint32_t>::max(), i.get_next_key_or_max());
+ }
+ ++num;
+ }
+ for (auto &i: node_balanced2) {
+ ASSERT_EQ(i.get_key(), num);
+ ASSERT_EQ(i.get_val(), (test_val_t{num, num}));
+ if (num < total - 1) {
+ ASSERT_EQ(i.get_next_key_or_max(), num + 1);
+ } else {
+ ASSERT_EQ(std::numeric_limits<uint32_t>::max(), i.get_next_key_or_max());
+ }
+ ++num;
+ }
+}
+
+TEST(FixedKVNodeTest, balanced) {
+ run_balance_test(CAPACITY / 2, CAPACITY, true);
+ run_balance_test(CAPACITY / 2, CAPACITY, false);
+ run_balance_test(CAPACITY, CAPACITY / 2, true);
+ run_balance_test(CAPACITY, CAPACITY / 2, false);
+ run_balance_test(CAPACITY - 1, CAPACITY / 2, true);
+ run_balance_test(CAPACITY / 2, CAPACITY - 1, false);
+ run_balance_test(CAPACITY / 2, CAPACITY / 2, false);
+}
+
+void run_replay_test(
+ std::vector<std::function<void(TestNode&, TestNode::delta_buffer_t&)>> &&f
+) {
+ TestNode node;
+ for (unsigned i = 0; i < f.size(); ++i) {
+ TestNode::delta_buffer_t buf;
+ TestNode replayed = node;
+ f[i](node, buf);
+ buf.replay(replayed);
+ ASSERT_EQ(node.get_size(), replayed.get_size());
+ ASSERT_EQ(node, replayed);
+ }
+}
+
+TEST(FixedKVNodeTest, replay) {
+ run_replay_test({
+ [](auto &n, auto &b) {
+ n.journal_insert(n.lower_bound(1), 1, test_val_t{1, 1}, &b);
+ ASSERT_EQ(1, n.get_size());
+ },
+ [](auto &n, auto &b) {
+ n.journal_insert(n.lower_bound(3), 3, test_val_t{1, 2}, &b);
+ ASSERT_EQ(2, n.get_size());
+ },
+ [](auto &n, auto &b) {
+ n.journal_remove(n.find(3), &b);
+ ASSERT_EQ(1, n.get_size());
+ },
+ [](auto &n, auto &b) {
+ n.journal_insert(n.lower_bound(2), 2, test_val_t{5, 1}, &b);
+ ASSERT_EQ(2, n.get_size());
+ }
+ });
+
+}
diff --git a/src/test/crimson/test_interruptible_future.cc b/src/test/crimson/test_interruptible_future.cc
new file mode 100644
index 000000000..bb938de24
--- /dev/null
+++ b/src/test/crimson/test_interruptible_future.cc
@@ -0,0 +1,301 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <seastar/core/sleep.hh>
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "crimson/common/interruptible_future.h"
+#include "crimson/common/log.h"
+
+using namespace crimson;
+
+class test_interruption : public std::exception
+{};
+
+class TestInterruptCondition {
+public:
+ TestInterruptCondition(bool interrupt)
+ : interrupt(interrupt) {}
+
+ template <typename T>
+ std::optional<T> may_interrupt() {
+ if (interrupt) {
+ return seastar::futurize<T>::make_exception_future(test_interruption());
+ } else {
+ return std::optional<T>();
+ }
+ }
+
+ template <typename T>
+ static constexpr bool is_interruption_v = std::is_same_v<T, test_interruption>;
+
+ static bool is_interruption(std::exception_ptr& eptr) {
+ if (*eptr.__cxa_exception_type() == typeid(test_interruption))
+ return true;
+ return false;
+ }
+private:
+ bool interrupt = false;
+};
+
+namespace crimson::interruptible {
+template
+thread_local interrupt_cond_t<TestInterruptCondition>
+interrupt_cond<TestInterruptCondition>;
+}
+
+TEST_F(seastar_test_suite_t, basic)
+{
+ using interruptor =
+ interruptible::interruptor<TestInterruptCondition>;
+ run_async([] {
+ interruptor::with_interruption(
+ [] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now())
+ .then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ }).then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return errorator<ct_error::enoent>::make_ready_future<>();
+ }).safe_then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, errorator<ct_error::enoent>::all_same_way([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ })
+ );
+ }, [](std::exception_ptr) {}, false).get0();
+
+ interruptor::with_interruption(
+ [] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now())
+ .then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ });
+ }, [](std::exception_ptr) {
+ ceph_assert(!interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, true).get0();
+
+
+ });
+}
+
+TEST_F(seastar_test_suite_t, loops)
+{
+ using interruptor =
+ interruptible::interruptor<TestInterruptCondition>;
+ std::cout << "testing interruptible loops" << std::endl;
+ run_async([] {
+ std::cout << "beginning" << std::endl;
+ interruptor::with_interruption(
+ [] {
+ std::cout << "interruptiion enabled" << std::endl;
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now())
+ .then_interruptible([] {
+ std::cout << "test seastar future do_for_each" << std::endl;
+ std::vector<int> vec = {1, 2};
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ });
+ });
+ }).then_interruptible([] {
+ std::cout << "test interruptible seastar future do_for_each" << std::endl;
+ std::vector<int> vec = {1, 2};
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now());
+ });
+ });
+ }).then_interruptible([] {
+ std::cout << "test seastar future repeat" << std::endl;
+ return interruptor::repeat([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(
+ seastar::make_ready_future<
+ seastar::stop_iteration>(
+ seastar::stop_iteration::yes));
+ });
+ }).then_interruptible([] {
+ std::cout << "test interruptible seastar future repeat" << std::endl;
+ return interruptor::repeat([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::make_ready_future<
+ seastar::stop_iteration>(
+ seastar::stop_iteration::yes);
+ });
+ }).then_interruptible([] {
+ std::cout << "test interruptible errorated future do_for_each" << std::endl;
+ std::vector<int> vec = {1, 2};
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ using namespace std::chrono_literals;
+ return interruptor::make_interruptible(seastar::now()).then_interruptible([&vec] {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(
+ errorator<ct_error::enoent>::make_ready_future<>());
+ }).safe_then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, errorator<ct_error::enoent>::all_same_way([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ }));
+ });
+ });
+ }).then_interruptible([] {
+ std::cout << "test errorated future do_for_each" << std::endl;
+ std::vector<int> vec;
+ // set a big enough iteration times to test if there is stack overflow in do_for_each
+ for (int i = 0; i < 1000000; i++) {
+ vec.push_back(i);
+ }
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ using namespace std::chrono_literals;
+ return interruptor::make_interruptible(seastar::now()).then_interruptible([&vec] {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return errorator<ct_error::enoent>::make_ready_future<>();
+ }).safe_then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, errorator<ct_error::enoent>::all_same_way([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ }));
+ });
+ });
+ }).then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ });
+ }, [](std::exception_ptr) {}, false).get0();
+ });
+}
+
+using base_intr = interruptible::interruptor<TestInterruptCondition>;
+
+using base_ertr = errorator<ct_error::enoent, ct_error::eagain>;
+using base_iertr = interruptible::interruptible_errorator<
+ TestInterruptCondition,
+ base_ertr>;
+
+using base2_ertr = base_ertr::extend<ct_error::input_output_error>;
+using base2_iertr = interruptible::interruptible_errorator<
+ TestInterruptCondition,
+ base2_ertr>;
+
+template <typename F>
+auto with_intr(F &&f) {
+ return base_intr::with_interruption_to_error<ct_error::eagain>(
+ std::forward<F>(f),
+ TestInterruptCondition(false));
+}
+
+TEST_F(seastar_test_suite_t, errorated)
+{
+ run_async([] {
+ base_ertr::future<> ret = with_intr(
+ []() {
+ return base_iertr::now();
+ }
+ );
+ ret.unsafe_get0();
+ });
+}
+
+TEST_F(seastar_test_suite_t, errorated_value)
+{
+ run_async([] {
+ base_ertr::future<int> ret = with_intr(
+ []() {
+ return base_iertr::make_ready_future<int>(
+ 1
+ );
+ });
+ EXPECT_EQ(ret.unsafe_get0(), 1);
+ });
+}
+
+TEST_F(seastar_test_suite_t, expand_errorated_value)
+{
+ run_async([] {
+ base2_ertr::future<> ret = with_intr(
+ []() {
+ return base_iertr::make_ready_future<int>(
+ 1
+ ).si_then([](auto) {
+ return base2_iertr::make_ready_future<>();
+ });
+ });
+ ret.unsafe_get0();
+ });
+}
+
+TEST_F(seastar_test_suite_t, interruptible_async)
+{
+ using interruptor =
+ interruptible::interruptor<TestInterruptCondition>;
+
+ run_async([] {
+ interruptor::with_interruption([] {
+ auto fut = interruptor::async([] {
+ interruptor::make_interruptible(
+ seastar::sleep(std::chrono::milliseconds(10))).get();
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.interrupt_cond);
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.ref_count == 1);
+ });
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.interrupt_cond);
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.ref_count == 1);
+ return fut;
+ }, [](std::exception_ptr) {}, false).get0();
+ });
+}
+
+TEST_F(seastar_test_suite_t, DISABLED_nested_interruptors)
+{
+ run_async([] {
+ base_ertr::future<> ret = with_intr(
+ []() {
+ return base_iertr::now().safe_then_interruptible([]() {
+ return with_intr(
+ []() {
+ return base_iertr::now();
+ }
+ );
+ });
+ }
+ );
+ ret.unsafe_get0();
+ });
+}
+
+#if 0
+// This seems to cause a hang in the gcc-9 linker on bionic
+TEST_F(seastar_test_suite_t, handle_error)
+{
+ run_async([] {
+ base_ertr::future<> ret = with_intr(
+ []() {
+ return base2_iertr::make_ready_future<int>(
+ 1
+ ).handle_error_interruptible(
+ base_iertr::pass_further{},
+ ct_error::assert_all{"crash on eio"}
+ ).si_then([](auto) {
+ return base_iertr::now();
+ });
+ });
+ ret.unsafe_get0();
+ });
+}
+#endif
diff --git a/src/test/crimson/test_lru.cc b/src/test/crimson/test_lru.cc
new file mode 100644
index 000000000..40ab41539
--- /dev/null
+++ b/src/test/crimson/test_lru.cc
@@ -0,0 +1,213 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ * Cheng Cheng <ccheng.leo@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "crimson/common/shared_lru.h"
+
+class LRUTest : public SharedLRU<unsigned int, int> {
+public:
+ auto add(unsigned int key, int value, bool* existed = nullptr) {
+ auto pv = new int{value};
+ auto ptr = insert(key, std::unique_ptr<int>{pv});
+ if (existed) {
+ *existed = (ptr.get() != pv);
+ }
+ return ptr;
+ }
+};
+
+TEST(LRU, add) {
+ LRUTest cache;
+ unsigned int key = 1;
+ int value1 = 2;
+ bool existed = false;
+ {
+ auto ptr = cache.add(key, value1, &existed);
+ ASSERT_TRUE(ptr);
+ ASSERT_TRUE(ptr.get());
+ ASSERT_EQ(value1, *ptr);
+ ASSERT_FALSE(existed);
+ }
+ {
+ auto ptr = cache.add(key, 3, &existed);
+ ASSERT_EQ(value1, *ptr);
+ ASSERT_TRUE(existed);
+ }
+}
+
+TEST(LRU, empty) {
+ LRUTest cache;
+ unsigned int key = 1;
+ bool existed = false;
+
+ ASSERT_TRUE(cache.empty());
+ {
+ int value1 = 2;
+ auto ptr = cache.add(key, value1, &existed);
+ ASSERT_EQ(value1, *ptr);
+ ASSERT_FALSE(existed);
+ }
+ ASSERT_FALSE(cache.empty());
+
+ cache.clear();
+ ASSERT_TRUE(cache.empty());
+}
+
+TEST(LRU, lookup) {
+ LRUTest cache;
+ unsigned int key = 1;
+ {
+ int value = 2;
+ auto ptr = cache.add(key, value);
+ ASSERT_TRUE(ptr);
+ ASSERT_TRUE(ptr.get());
+ ASSERT_TRUE(cache.find(key).get());
+ ASSERT_EQ(value, *cache.find(key));
+ }
+ ASSERT_TRUE(cache.find(key).get());
+}
+
+TEST(LRU, lookup_or_create) {
+ LRUTest cache;
+ {
+ int value = 2;
+ unsigned int key = 1;
+ ASSERT_TRUE(cache.add(key, value).get());
+ ASSERT_TRUE(cache[key].get());
+ ASSERT_EQ(value, *cache.find(key));
+ }
+ {
+ unsigned int key = 2;
+ ASSERT_TRUE(cache[key].get());
+ ASSERT_EQ(0, *cache.find(key));
+ }
+ ASSERT_TRUE(cache.find(1).get());
+ ASSERT_TRUE(cache.find(2).get());
+}
+
+TEST(LRU, lower_bound) {
+ LRUTest cache;
+
+ {
+ unsigned int key = 1;
+ ASSERT_FALSE(cache.lower_bound(key));
+ int value = 2;
+
+ ASSERT_TRUE(cache.add(key, value).get());
+ ASSERT_TRUE(cache.lower_bound(key).get());
+ EXPECT_EQ(value, *cache.lower_bound(key));
+ }
+}
+
+TEST(LRU, get_next) {
+
+ {
+ LRUTest cache;
+ const unsigned int key = 0;
+ EXPECT_FALSE(cache.upper_bound(key));
+ }
+ {
+ LRUTest cache;
+ const unsigned int key1 = 111;
+ auto ptr1 = cache[key1];
+ const unsigned int key2 = 222;
+ auto ptr2 = cache[key2];
+
+ auto i = cache.upper_bound(0);
+ ASSERT_TRUE(i);
+ EXPECT_EQ(i->first, key1);
+ auto j = cache.upper_bound(i->first);
+ ASSERT_TRUE(j);
+ EXPECT_EQ(j->first, key2);
+ }
+}
+
+TEST(LRU, clear) {
+ LRUTest cache;
+ unsigned int key = 1;
+ int value = 2;
+ cache.add(key, value);
+ {
+ auto found = cache.find(key);
+ ASSERT_TRUE(found);
+ ASSERT_EQ(value, *found);
+ }
+ ASSERT_TRUE(cache.find(key).get());
+ cache.clear();
+ ASSERT_FALSE(cache.find(key));
+ ASSERT_TRUE(cache.empty());
+}
+
+TEST(LRU, eviction) {
+ LRUTest cache{5};
+ bool existed;
+ // add a bunch of elements, some of them will be evicted
+ for (size_t i = 0; i < 2 * cache.capacity(); ++i) {
+ cache.add(i, i, &existed);
+ ASSERT_FALSE(existed);
+ }
+ size_t i = 0;
+ for (; i < cache.capacity(); ++i) {
+ ASSERT_FALSE(cache.find(i));
+ }
+ for (; i < 2 * cache.capacity(); ++i) {
+ ASSERT_TRUE(cache.find(i));
+ }
+}
+
+TEST(LRU, track_weak) {
+ constexpr int SIZE = 5;
+ LRUTest cache{SIZE};
+
+ bool existed = false;
+ // strong reference to keep 0 alive
+ auto ptr = cache.add(0, 0, &existed);
+ ASSERT_FALSE(existed);
+
+ // add a bunch of elements to get 0 evicted
+ for (size_t i = 1; i < 2 * cache.capacity(); ++i) {
+ cache.add(i, i, &existed);
+ ASSERT_FALSE(existed);
+ }
+ // 0 is still reachable via the cache
+ ASSERT_TRUE(cache.find(0));
+ ASSERT_TRUE(cache.find(0).get());
+ ASSERT_EQ(0, *cache.find(0));
+
+ // [0..SIZE) are evicted when adding [SIZE..2*SIZE)
+ // [SIZE..SIZE * 2) were still in the cache before accessing 0,
+ // but SIZE got evicted when accessing 0
+ ASSERT_FALSE(cache.find(SIZE-1));
+ ASSERT_FALSE(cache.find(SIZE));
+ ASSERT_TRUE(cache.find(SIZE+1));
+ ASSERT_TRUE(cache.find(SIZE+1).get());
+ ASSERT_EQ((int)SIZE+1, *cache.find(SIZE+1));
+
+ ptr.reset();
+ // 0 is still reachable, as it is now put back into LRU cache
+ ASSERT_TRUE(cache.find(0));
+}
+
+// Local Variables:
+// compile-command: "cmake --build ../../../build -j 8 --target unittest_seastar_lru && ctest -R unittest_seastar_lru # --gtest_filter=*.* --log-to-stderr=true"
+// End:
diff --git a/src/test/crimson/test_messenger.cc b/src/test/crimson/test_messenger.cc
new file mode 100644
index 000000000..a42572246
--- /dev/null
+++ b/src/test/crimson/test_messenger.cc
@@ -0,0 +1,3874 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/ceph_argparse.h"
+#include "common/ceph_time.h"
+#include "messages/MPing.h"
+#include "messages/MCommand.h"
+#include "messages/MCommandReply.h"
+#include "messages/MOSDOp.h"
+#include "messages/MOSDOpReply.h"
+#include "crimson/auth/DummyAuth.h"
+#include "crimson/common/log.h"
+#include "crimson/net/Connection.h"
+#include "crimson/net/Dispatcher.h"
+#include "crimson/net/Messenger.h"
+#include "crimson/net/Interceptor.h"
+
+#include <map>
+#include <random>
+#include <boost/program_options.hpp>
+#include <fmt/format.h>
+#include <fmt/ostream.h>
+#include <seastar/core/app-template.hh>
+#include <seastar/core/do_with.hh>
+#include <seastar/core/future-util.hh>
+#include <seastar/core/gate.hh>
+#include <seastar/core/reactor.hh>
+#include <seastar/core/sleep.hh>
+#include <seastar/core/with_timeout.hh>
+
+#include "test_messenger.h"
+
+using namespace std::chrono_literals;
+namespace bpo = boost::program_options;
+using crimson::common::local_conf;
+
+namespace {
+
+seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+}
+
+static std::random_device rd;
+static std::default_random_engine rng{rd()};
+static bool verbose = false;
+
+static entity_addr_t get_server_addr() {
+ static int port = 9030;
+ ++port;
+ entity_addr_t saddr;
+ saddr.parse("127.0.0.1", nullptr);
+ saddr.set_port(port);
+ return saddr;
+}
+
+template <typename T, typename... Args>
+seastar::future<T*> create_sharded(Args... args) {
+ // we should only construct/stop shards on #0
+ return seastar::smp::submit_to(0, [=] {
+ auto sharded_obj = seastar::make_lw_shared<seastar::sharded<T>>();
+ return sharded_obj->start(args...
+ ).then([sharded_obj] {
+ seastar::engine().at_exit([sharded_obj] {
+ return sharded_obj->stop().then([sharded_obj] {});
+ });
+ return sharded_obj.get();
+ });
+ }).then([](seastar::sharded<T> *ptr_shard) {
+ return &ptr_shard->local();
+ });
+}
+
+class ShardedGates
+ : public seastar::peering_sharded_service<ShardedGates> {
+public:
+ ShardedGates() = default;
+ ~ShardedGates() {
+ assert(gate.is_closed());
+ }
+
+ template <typename Func>
+ void dispatch_in_background(const char *what, Func &&f) {
+ std::ignore = seastar::with_gate(
+ container().local().gate, std::forward<Func>(f)
+ ).handle_exception([what](std::exception_ptr eptr) {
+ try {
+ std::rethrow_exception(eptr);
+ } catch (std::exception &e) {
+ logger().error("ShardedGates::dispatch_in_background: "
+ "{} got exxception {}", what, e.what());
+ }
+ });
+ }
+
+ seastar::future<> close() {
+ return container().invoke_on_all([](auto &local) {
+ return local.gate.close();
+ });
+ }
+
+ static seastar::future<ShardedGates*> create() {
+ return create_sharded<ShardedGates>();
+ }
+
+ // seastar::future<> stop() is intentially not implemented
+
+private:
+ seastar::gate gate;
+};
+
+static seastar::future<> test_echo(unsigned rounds,
+ double keepalive_ratio)
+{
+ struct test_state {
+ struct Server final
+ : public crimson::net::Dispatcher {
+ ShardedGates &gates;
+ crimson::net::MessengerRef msgr;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+
+ Server(ShardedGates &gates) : gates{gates} {}
+
+ void ms_handle_accept(
+ crimson::net::ConnectionRef conn,
+ seastar::shard_id prv_shard,
+ bool is_replace) override {
+ logger().info("server accepted {}", *conn);
+ ceph_assert(prv_shard == seastar::this_shard_id());
+ ceph_assert(!is_replace);
+ }
+
+ std::optional<seastar::future<>> ms_dispatch(
+ crimson::net::ConnectionRef c, MessageRef m) override {
+ if (verbose) {
+ logger().info("server got {}", *m);
+ }
+ // reply with a pong
+ gates.dispatch_in_background("echo_send_pong", [c] {
+ return c->send(crimson::make_message<MPing>());
+ });
+ return {seastar::now()};
+ }
+
+ seastar::future<> init(const entity_name_t& name,
+ const std::string& lname,
+ const uint64_t nonce,
+ const entity_addr_t& addr) {
+ msgr = crimson::net::Messenger::create(
+ name, lname, nonce, false);
+ msgr->set_default_policy(crimson::net::SocketPolicy::stateless_server(0));
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ return msgr->bind(entity_addrvec_t{addr}).safe_then([this] {
+ return msgr->start({this});
+ }, crimson::net::Messenger::bind_ertr::all_same_way(
+ [addr] (const std::error_code& e) {
+ logger().error("test_echo(): "
+ "there is another instance running at {}", addr);
+ ceph_abort();
+ }));
+ }
+ seastar::future<> shutdown() {
+ ceph_assert(msgr);
+ msgr->stop();
+ return msgr->shutdown();
+ }
+ };
+
+ class Client final
+ : public crimson::net::Dispatcher,
+ public seastar::peering_sharded_service<Client> {
+ public:
+ Client(seastar::shard_id primary_sid,
+ unsigned rounds,
+ double keepalive_ratio,
+ ShardedGates *gates)
+ : primary_sid{primary_sid},
+ keepalive_dist(std::bernoulli_distribution{keepalive_ratio}),
+ rounds(rounds),
+ gates{*gates} {}
+
+ seastar::future<> init(const entity_name_t& name,
+ const std::string& lname,
+ const uint64_t nonce) {
+ assert(seastar::this_shard_id() == primary_sid);
+ msgr = crimson::net::Messenger::create(
+ name, lname, nonce, false);
+ msgr->set_default_policy(crimson::net::SocketPolicy::lossy_client(0));
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ return msgr->start({this});
+ }
+
+ seastar::future<> shutdown() {
+ assert(seastar::this_shard_id() == primary_sid);
+ ceph_assert(msgr);
+ msgr->stop();
+ return msgr->shutdown();
+ }
+
+ seastar::future<> dispatch_pingpong(const entity_addr_t& peer_addr) {
+ assert(seastar::this_shard_id() == primary_sid);
+ mono_time start_time = mono_clock::now();
+ auto conn = msgr->connect(peer_addr, entity_name_t::TYPE_OSD);
+ return seastar::futurize_invoke([this, conn] {
+ return do_dispatch_pingpong(conn);
+ }).then([] {
+ // 500ms should be enough to establish the connection
+ return seastar::sleep(500ms);
+ }).then([this, conn, start_time] {
+ return container().invoke_on(
+ conn->get_shard_id(),
+ [pconn=&*conn, start_time](auto &local) {
+ assert(pconn->is_connected());
+ auto session = local.find_session(pconn);
+ std::chrono::duration<double> dur_handshake = session->connected_time - start_time;
+ std::chrono::duration<double> dur_pingpong = session->finish_time - session->connected_time;
+ logger().info("{}: handshake {}, pingpong {}",
+ *pconn, dur_handshake.count(), dur_pingpong.count());
+ }).then([conn] {});
+ });
+ }
+
+ static seastar::future<Client*> create(
+ unsigned rounds,
+ double keepalive_ratio,
+ ShardedGates *gates) {
+ return create_sharded<Client>(
+ seastar::this_shard_id(),
+ rounds,
+ keepalive_ratio,
+ gates);
+ }
+
+ private:
+ struct PingSession : public seastar::enable_shared_from_this<PingSession> {
+ unsigned count = 0u;
+ mono_time connected_time;
+ mono_time finish_time;
+ };
+ using PingSessionRef = seastar::shared_ptr<PingSession>;
+
+ void ms_handle_connect(
+ crimson::net::ConnectionRef conn,
+ seastar::shard_id prv_shard) override {
+ auto &local = container().local();
+ assert(prv_shard == seastar::this_shard_id());
+ auto session = seastar::make_shared<PingSession>();
+ auto [i, added] = local.sessions.emplace(&*conn, session);
+ std::ignore = i;
+ ceph_assert(added);
+ session->connected_time = mono_clock::now();
+ }
+
+ std::optional<seastar::future<>> ms_dispatch(
+ crimson::net::ConnectionRef c, MessageRef m) override {
+ auto &local = container().local();
+ auto session = local.find_session(&*c);
+ ++(session->count);
+ if (verbose) {
+ logger().info("client ms_dispatch {}", session->count);
+ }
+
+ if (session->count > rounds) {
+ logger().error("{}: got {} pongs, more than expected {}", *c, session->count, rounds);
+ ceph_abort();
+ } else if (session->count == rounds) {
+ logger().info("{}: finished receiving {} pongs", *c, session->count);
+ session->finish_time = mono_clock::now();
+ gates.dispatch_in_background("echo_notify_done", [c, this] {
+ return container().invoke_on(primary_sid, [pconn=&*c](auto &local) {
+ auto found = local.pending_conns.find(pconn);
+ ceph_assert(found != local.pending_conns.end());
+ found->second.set_value();
+ }).then([c] {});
+ });
+ }
+ return {seastar::now()};
+ }
+
+ PingSessionRef find_session(crimson::net::Connection *c) {
+ auto found = sessions.find(c);
+ if (found == sessions.end()) {
+ ceph_assert(false);
+ }
+ return found->second;
+ }
+
+ seastar::future<> do_dispatch_pingpong(crimson::net::ConnectionRef conn) {
+ auto [i, added] = pending_conns.emplace(&*conn, seastar::promise<>());
+ std::ignore = i;
+ ceph_assert(added);
+ return seastar::do_with(0u, 0u,
+ [this, conn](auto &count_ping, auto &count_keepalive) {
+ return seastar::do_until(
+ [this, conn, &count_ping, &count_keepalive] {
+ bool stop = (count_ping == rounds);
+ if (stop) {
+ logger().info("{}: finished sending {} pings with {} keepalives",
+ *conn, count_ping, count_keepalive);
+ }
+ return stop;
+ },
+ [this, conn, &count_ping, &count_keepalive] {
+ return seastar::repeat([this, conn, &count_ping, &count_keepalive] {
+ if (keepalive_dist(rng)) {
+ return conn->send_keepalive(
+ ).then([&count_keepalive] {
+ count_keepalive += 1;
+ return seastar::make_ready_future<seastar::stop_iteration>(
+ seastar::stop_iteration::no);
+ });
+ } else {
+ return conn->send(crimson::make_message<MPing>()
+ ).then([&count_ping] {
+ count_ping += 1;
+ return seastar::make_ready_future<seastar::stop_iteration>(
+ seastar::stop_iteration::yes);
+ });
+ }
+ });
+ }).then([this, conn] {
+ auto found = pending_conns.find(&*conn);
+ assert(found != pending_conns.end());
+ return found->second.get_future();
+ }
+ );
+ });
+ }
+
+ private:
+ // primary shard only
+ const seastar::shard_id primary_sid;
+ std::bernoulli_distribution keepalive_dist;
+ crimson::net::MessengerRef msgr;
+ std::map<crimson::net::Connection*, seastar::promise<>> pending_conns;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+
+ // per shard
+ const unsigned rounds;
+ std::map<crimson::net::Connection*, PingSessionRef> sessions;
+ ShardedGates &gates;
+ };
+ };
+
+ logger().info("test_echo(rounds={}, keepalive_ratio={}):",
+ rounds, keepalive_ratio);
+ return ShardedGates::create(
+ ).then([rounds, keepalive_ratio](auto *gates) {
+ return seastar::when_all_succeed(
+ test_state::Client::create(rounds, keepalive_ratio, gates),
+ test_state::Client::create(rounds, keepalive_ratio, gates),
+ seastar::make_ready_future<ShardedGates*>(gates));
+ }).then_unpack([](auto *client1, auto *client2, auto *gates) {
+ auto server1 = seastar::make_shared<test_state::Server>(*gates);
+ auto server2 = seastar::make_shared<test_state::Server>(*gates);
+ // start servers and clients
+ auto addr1 = get_server_addr();
+ auto addr2 = get_server_addr();
+ addr1.set_type(entity_addr_t::TYPE_MSGR2);
+ addr2.set_type(entity_addr_t::TYPE_MSGR2);
+ return seastar::when_all_succeed(
+ server1->init(entity_name_t::OSD(0), "server1", 1, addr1),
+ server2->init(entity_name_t::OSD(1), "server2", 2, addr2),
+ client1->init(entity_name_t::OSD(2), "client1", 3),
+ client2->init(entity_name_t::OSD(3), "client2", 4)
+ // dispatch pingpoing
+ ).then_unpack([client1, client2, server1, server2] {
+ return seastar::when_all_succeed(
+ // test connecting in parallel, accepting in parallel
+ client1->dispatch_pingpong(server1->msgr->get_myaddr()),
+ client1->dispatch_pingpong(server2->msgr->get_myaddr()),
+ client2->dispatch_pingpong(server1->msgr->get_myaddr()),
+ client2->dispatch_pingpong(server2->msgr->get_myaddr()));
+ // shutdown
+ }).then_unpack([client1] {
+ logger().info("client1 shutdown...");
+ return client1->shutdown();
+ }).then([client2] {
+ logger().info("client2 shutdown...");
+ return client2->shutdown();
+ }).then([server1] {
+ logger().info("server1 shutdown...");
+ return server1->shutdown();
+ }).then([server2] {
+ logger().info("server2 shutdown...");
+ return server2->shutdown();
+ }).then([] {
+ logger().info("test_echo() done!\n");
+ }).handle_exception([](auto eptr) {
+ logger().error("test_echo() failed: got exception {}", eptr);
+ throw;
+ }).finally([gates, server1, server2] {
+ return gates->close();
+ });
+ });
+}
+
+seastar::future<> test_preemptive_shutdown() {
+ struct test_state {
+ class Server final
+ : public crimson::net::Dispatcher {
+ crimson::net::MessengerRef msgr;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+
+ std::optional<seastar::future<>> ms_dispatch(
+ crimson::net::ConnectionRef c, MessageRef m) override {
+ std::ignore = c->send(crimson::make_message<MPing>());
+ return {seastar::now()};
+ }
+
+ public:
+ seastar::future<> init(const entity_name_t& name,
+ const std::string& lname,
+ const uint64_t nonce,
+ const entity_addr_t& addr) {
+ msgr = crimson::net::Messenger::create(
+ name, lname, nonce, true);
+ msgr->set_default_policy(crimson::net::SocketPolicy::stateless_server(0));
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ return msgr->bind(entity_addrvec_t{addr}).safe_then([this] {
+ return msgr->start({this});
+ }, crimson::net::Messenger::bind_ertr::all_same_way(
+ [addr] (const std::error_code& e) {
+ logger().error("test_preemptive_shutdown(): "
+ "there is another instance running at {}", addr);
+ ceph_abort();
+ }));
+ }
+ entity_addr_t get_addr() const {
+ return msgr->get_myaddr();
+ }
+ seastar::future<> shutdown() {
+ msgr->stop();
+ return msgr->shutdown();
+ }
+ };
+
+ class Client final
+ : public crimson::net::Dispatcher {
+ crimson::net::MessengerRef msgr;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+
+ bool stop_send = false;
+ seastar::promise<> stopped_send_promise;
+
+ std::optional<seastar::future<>> ms_dispatch(
+ crimson::net::ConnectionRef, MessageRef m) override {
+ return {seastar::now()};
+ }
+
+ public:
+ seastar::future<> init(const entity_name_t& name,
+ const std::string& lname,
+ const uint64_t nonce) {
+ msgr = crimson::net::Messenger::create(
+ name, lname, nonce, true);
+ msgr->set_default_policy(crimson::net::SocketPolicy::lossy_client(0));
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ return msgr->start({this});
+ }
+ void send_pings(const entity_addr_t& addr) {
+ auto conn = msgr->connect(addr, entity_name_t::TYPE_OSD);
+ // forwarded to stopped_send_promise
+ (void) seastar::do_until(
+ [this] { return stop_send; },
+ [conn] {
+ return conn->send(crimson::make_message<MPing>()).then([] {
+ return seastar::sleep(0ms);
+ });
+ }
+ ).then_wrapped([this, conn] (auto fut) {
+ fut.forward_to(std::move(stopped_send_promise));
+ });
+ }
+ seastar::future<> shutdown() {
+ msgr->stop();
+ return msgr->shutdown().then([this] {
+ stop_send = true;
+ return stopped_send_promise.get_future();
+ });
+ }
+ };
+ };
+
+ logger().info("test_preemptive_shutdown():");
+ auto server = seastar::make_shared<test_state::Server>();
+ auto client = seastar::make_shared<test_state::Client>();
+ auto addr = get_server_addr();
+ addr.set_type(entity_addr_t::TYPE_MSGR2);
+ addr.set_family(AF_INET);
+ return seastar::when_all_succeed(
+ server->init(entity_name_t::OSD(6), "server4", 7, addr),
+ client->init(entity_name_t::OSD(7), "client4", 8)
+ ).then_unpack([server, client] {
+ client->send_pings(server->get_addr());
+ return seastar::sleep(100ms);
+ }).then([client] {
+ logger().info("client shutdown...");
+ return client->shutdown();
+ }).then([server] {
+ logger().info("server shutdown...");
+ return server->shutdown();
+ }).then([] {
+ logger().info("test_preemptive_shutdown() done!\n");
+ }).handle_exception([server, client] (auto eptr) {
+ logger().error("test_preemptive_shutdown() failed: got exception {}", eptr);
+ throw;
+ });
+}
+
+using ceph::msgr::v2::Tag;
+using crimson::net::bp_action_t;
+using crimson::net::bp_type_t;
+using crimson::net::Breakpoint;
+using crimson::net::Connection;
+using crimson::net::ConnectionRef;
+using crimson::net::custom_bp_t;
+using crimson::net::Dispatcher;
+using crimson::net::Interceptor;
+using crimson::net::Messenger;
+using crimson::net::MessengerRef;
+using crimson::net::SocketPolicy;
+using crimson::net::tag_bp_t;
+using namespace ceph::net::test;
+
+struct counter_t { unsigned counter = 0; };
+
+enum class conn_state_t {
+ unknown = 0,
+ established,
+ closed,
+ replaced,
+};
+
+std::ostream& operator<<(std::ostream& out, const conn_state_t& state) {
+ switch(state) {
+ case conn_state_t::unknown:
+ return out << "unknown";
+ case conn_state_t::established:
+ return out << "established";
+ case conn_state_t::closed:
+ return out << "closed";
+ case conn_state_t::replaced:
+ return out << "replaced";
+ default:
+ ceph_abort();
+ }
+}
+
+} // anonymous namespace
+
+#if FMT_VERSION >= 90000
+template<>
+struct fmt::formatter<conn_state_t> : fmt::ostream_formatter {};
+#endif
+
+namespace {
+
+struct ConnResult {
+ ConnectionRef conn;
+ unsigned index;
+ conn_state_t state = conn_state_t::unknown;
+
+ unsigned connect_attempts = 0;
+ unsigned client_connect_attempts = 0;
+ unsigned client_reconnect_attempts = 0;
+ unsigned cnt_connect_dispatched = 0;
+
+ unsigned accept_attempts = 0;
+ unsigned server_connect_attempts = 0;
+ unsigned server_reconnect_attempts = 0;
+ unsigned cnt_accept_dispatched = 0;
+
+ unsigned cnt_reset_dispatched = 0;
+ unsigned cnt_remote_reset_dispatched = 0;
+
+ ConnResult(ConnectionRef conn, unsigned index)
+ : conn(conn), index(index) {}
+
+ template <typename T>
+ void _assert_eq(const char* expr_actual, T actual,
+ const char* expr_expected, T expected) const {
+ if (actual != expected) {
+ throw std::runtime_error(fmt::format(
+ "[{}] {} '{}' is actually {}, not the expected '{}' {}",
+ index, *conn, expr_actual, actual, expr_expected, expected));
+ }
+ }
+
+#define ASSERT_EQUAL(actual, expected) \
+ _assert_eq(#actual, actual, #expected, expected)
+
+ void assert_state_at(conn_state_t expected) const {
+ ASSERT_EQUAL(state, expected);
+ }
+
+ void assert_connect(unsigned attempts,
+ unsigned connects,
+ unsigned reconnects,
+ unsigned dispatched) const {
+ ASSERT_EQUAL(connect_attempts, attempts);
+ ASSERT_EQUAL(client_connect_attempts, connects);
+ ASSERT_EQUAL(client_reconnect_attempts, reconnects);
+ ASSERT_EQUAL(cnt_connect_dispatched, dispatched);
+ }
+
+ void assert_connect(unsigned attempts,
+ unsigned dispatched) const {
+ ASSERT_EQUAL(connect_attempts, attempts);
+ ASSERT_EQUAL(cnt_connect_dispatched, dispatched);
+ }
+
+ void assert_accept(unsigned attempts,
+ unsigned accepts,
+ unsigned reaccepts,
+ unsigned dispatched) const {
+ ASSERT_EQUAL(accept_attempts, attempts);
+ ASSERT_EQUAL(server_connect_attempts, accepts);
+ ASSERT_EQUAL(server_reconnect_attempts, reaccepts);
+ ASSERT_EQUAL(cnt_accept_dispatched, dispatched);
+ }
+
+ void assert_accept(unsigned attempts,
+ unsigned dispatched) const {
+ ASSERT_EQUAL(accept_attempts, attempts);
+ ASSERT_EQUAL(cnt_accept_dispatched, dispatched);
+ }
+
+ void assert_reset(unsigned local, unsigned remote) const {
+ ASSERT_EQUAL(cnt_reset_dispatched, local);
+ ASSERT_EQUAL(cnt_remote_reset_dispatched, remote);
+ }
+
+ void dump() const {
+ logger().info("\nResult({}):\n"
+ " conn: [{}] {}:\n"
+ " state: {}\n"
+ " connect_attempts: {}\n"
+ " client_connect_attempts: {}\n"
+ " client_reconnect_attempts: {}\n"
+ " cnt_connect_dispatched: {}\n"
+ " accept_attempts: {}\n"
+ " server_connect_attempts: {}\n"
+ " server_reconnect_attempts: {}\n"
+ " cnt_accept_dispatched: {}\n"
+ " cnt_reset_dispatched: {}\n"
+ " cnt_remote_reset_dispatched: {}\n",
+ static_cast<const void*>(this),
+ index, *conn,
+ state,
+ connect_attempts,
+ client_connect_attempts,
+ client_reconnect_attempts,
+ cnt_connect_dispatched,
+ accept_attempts,
+ server_connect_attempts,
+ server_reconnect_attempts,
+ cnt_accept_dispatched,
+ cnt_reset_dispatched,
+ cnt_remote_reset_dispatched);
+ }
+};
+using ConnResults = std::vector<ConnResult>;
+
+struct TestInterceptor : public Interceptor {
+ std::map<Breakpoint, std::map<unsigned, bp_action_t>> breakpoints;
+ std::map<Breakpoint, counter_t> breakpoints_counter;
+ std::map<Connection*, unsigned> conns;
+ ConnResults results;
+ std::optional<seastar::abort_source> signal;
+ const seastar::shard_id primary_sid;
+
+ TestInterceptor() : primary_sid{seastar::this_shard_id()} {}
+
+ // only used for copy breakpoint configurations
+ TestInterceptor(const TestInterceptor& other) : primary_sid{other.primary_sid} {
+ assert(other.breakpoints_counter.empty());
+ assert(other.conns.empty());
+ assert(other.results.empty());
+ breakpoints = other.breakpoints;
+ assert(!other.signal);
+ assert(seastar::this_shard_id() == primary_sid);
+ }
+
+ void make_fault(Breakpoint bp, unsigned round = 1) {
+ assert(round >= 1);
+ breakpoints[bp][round] = bp_action_t::FAULT;
+ }
+
+ void make_block(Breakpoint bp, unsigned round = 1) {
+ assert(round >= 1);
+ breakpoints[bp][round] = bp_action_t::BLOCK;
+ }
+
+ void make_stall(Breakpoint bp, unsigned round = 1) {
+ assert(round >= 1);
+ breakpoints[bp][round] = bp_action_t::STALL;
+ }
+
+ ConnResult* find_result(Connection *conn) {
+ assert(seastar::this_shard_id() == primary_sid);
+ auto it = conns.find(conn);
+ if (it == conns.end()) {
+ return nullptr;
+ } else {
+ return &results[it->second];
+ }
+ }
+
+ seastar::future<> wait() {
+ assert(seastar::this_shard_id() == primary_sid);
+ assert(!signal);
+ signal = seastar::abort_source();
+ return seastar::sleep_abortable(10s, *signal).then([] {
+ throw std::runtime_error("Timeout (10s) in TestInterceptor::wait()");
+ }).handle_exception_type([] (const seastar::sleep_aborted& e) {
+ // wait done!
+ });
+ }
+
+ void notify() {
+ assert(seastar::this_shard_id() == primary_sid);
+ if (signal) {
+ signal->request_abort();
+ signal = std::nullopt;
+ }
+ }
+
+ private:
+ void register_conn(ConnectionRef conn) override {
+ auto result = find_result(&*conn);
+ if (result != nullptr) {
+ logger().error("The connection [{}] {} already exists when register {}",
+ result->index, *result->conn, *conn);
+ ceph_abort();
+ }
+ unsigned index = results.size();
+ results.emplace_back(conn, index);
+ conns[&*conn] = index;
+ notify();
+ logger().info("[{}] {} new connection registered", index, *conn);
+ }
+
+ void register_conn_closed(ConnectionRef conn) override {
+ auto result = find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked closed connection: {}", *conn);
+ ceph_abort();
+ }
+
+ if (result->state != conn_state_t::replaced) {
+ result->state = conn_state_t::closed;
+ }
+ notify();
+ logger().info("[{}] {} closed({})", result->index, *conn, result->state);
+ }
+
+ void register_conn_ready(ConnectionRef conn) override {
+ auto result = find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked ready connection: {}", *conn);
+ ceph_abort();
+ }
+
+ ceph_assert(conn->is_protocol_ready());
+ notify();
+ logger().info("[{}] {} ready", result->index, *conn);
+ }
+
+ void register_conn_replaced(ConnectionRef conn) override {
+ auto result = find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked replaced connection: {}", *conn);
+ ceph_abort();
+ }
+
+ result->state = conn_state_t::replaced;
+ logger().info("[{}] {} {}", result->index, *conn, result->state);
+ }
+
+ seastar::future<bp_action_t>
+ intercept(Connection &_conn, std::vector<Breakpoint> bps) override {
+ assert(bps.size() >= 1);
+ Connection *conn = &_conn;
+
+ return seastar::smp::submit_to(primary_sid, [conn, bps, this] {
+ std::vector<bp_action_t> actions;
+ for (const Breakpoint &bp : bps) {
+ ++breakpoints_counter[bp].counter;
+
+ auto result = find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked intercepted connection: {}, at breakpoint {}({})",
+ *conn, bp, breakpoints_counter[bp].counter);
+ ceph_abort();
+ }
+
+ if (bp == custom_bp_t::SOCKET_CONNECTING) {
+ ++result->connect_attempts;
+ logger().info("[Test] connect_attempts={}", result->connect_attempts);
+ } else if (bp == tag_bp_t{Tag::CLIENT_IDENT, bp_type_t::WRITE}) {
+ ++result->client_connect_attempts;
+ logger().info("[Test] client_connect_attempts={}", result->client_connect_attempts);
+ } else if (bp == tag_bp_t{Tag::SESSION_RECONNECT, bp_type_t::WRITE}) {
+ ++result->client_reconnect_attempts;
+ logger().info("[Test] client_reconnect_attempts={}", result->client_reconnect_attempts);
+ } else if (bp == custom_bp_t::SOCKET_ACCEPTED) {
+ ++result->accept_attempts;
+ logger().info("[Test] accept_attempts={}", result->accept_attempts);
+ } else if (bp == tag_bp_t{Tag::CLIENT_IDENT, bp_type_t::READ}) {
+ ++result->server_connect_attempts;
+ logger().info("[Test] server_connect_attemps={}", result->server_connect_attempts);
+ } else if (bp == tag_bp_t{Tag::SESSION_RECONNECT, bp_type_t::READ}) {
+ ++result->server_reconnect_attempts;
+ logger().info("[Test] server_reconnect_attempts={}", result->server_reconnect_attempts);
+ }
+
+ auto it_bp = breakpoints.find(bp);
+ if (it_bp != breakpoints.end()) {
+ auto it_cnt = it_bp->second.find(breakpoints_counter[bp].counter);
+ if (it_cnt != it_bp->second.end()) {
+ logger().info("[{}] {} intercepted {}({}) => {}",
+ result->index, *conn, bp,
+ breakpoints_counter[bp].counter, it_cnt->second);
+ actions.emplace_back(it_cnt->second);
+ continue;
+ }
+ }
+ logger().info("[{}] {} intercepted {}({})",
+ result->index, *conn, bp, breakpoints_counter[bp].counter);
+ actions.emplace_back(bp_action_t::CONTINUE);
+ }
+
+ bp_action_t action = bp_action_t::CONTINUE;
+ for (bp_action_t &a : actions) {
+ if (a != bp_action_t::CONTINUE) {
+ if (action == bp_action_t::CONTINUE) {
+ action = a;
+ } else {
+ ceph_abort("got multiple incompatible actions");
+ }
+ }
+ }
+ return seastar::make_ready_future<bp_action_t>(action);
+ });
+ }
+};
+
+SocketPolicy to_socket_policy(policy_t policy) {
+ switch (policy) {
+ case policy_t::stateful_server:
+ return SocketPolicy::stateful_server(0);
+ case policy_t::stateless_server:
+ return SocketPolicy::stateless_server(0);
+ case policy_t::lossless_peer:
+ return SocketPolicy::lossless_peer(0);
+ case policy_t::lossless_peer_reuse:
+ return SocketPolicy::lossless_peer_reuse(0);
+ case policy_t::lossy_client:
+ return SocketPolicy::lossy_client(0);
+ case policy_t::lossless_client:
+ return SocketPolicy::lossless_client(0);
+ default:
+ logger().error("unexpected policy type");
+ ceph_abort();
+ }
+}
+
+class FailoverSuite : public Dispatcher {
+ crimson::auth::DummyAuthClientServer dummy_auth;
+ MessengerRef test_msgr;
+ const entity_addr_t test_peer_addr;
+ TestInterceptor interceptor;
+
+ unsigned tracked_index = 0;
+ Connection *tracked_conn = nullptr;
+ unsigned pending_send = 0;
+ unsigned pending_peer_receive = 0;
+ unsigned pending_receive = 0;
+
+ ShardedGates &gates;
+ const seastar::shard_id primary_sid;
+
+ std::optional<seastar::future<>> ms_dispatch(
+ ConnectionRef conn_ref, MessageRef m) override {
+ ceph_assert(m->get_type() == CEPH_MSG_OSD_OP);
+ Connection *conn = &*conn_ref;
+ gates.dispatch_in_background("TestSuite_ms_dispatch",
+ [this, conn, conn_ref] {
+ return seastar::smp::submit_to(primary_sid, [this, conn] {
+ auto result = interceptor.find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked ms dispatched connection: {}", *conn);
+ ceph_abort();
+ }
+
+ if (tracked_conn != &*conn) {
+ logger().warn("[{}] {} got op, but doesn't match tracked_conn [{}] {}",
+ result->index, *conn, tracked_index, *tracked_conn);
+ } else {
+ ceph_assert(result->index == tracked_index);
+ }
+
+ ceph_assert(pending_receive > 0);
+ --pending_receive;
+ if (pending_receive == 0) {
+ interceptor.notify();
+ }
+ logger().info("[Test] got op, left {} ops -- [{}] {}",
+ pending_receive, result->index, *conn);
+ }).then([conn_ref] {});
+ });
+ return {seastar::now()};
+ }
+
+ void ms_handle_accept(
+ ConnectionRef conn_ref,
+ seastar::shard_id prv_shard,
+ bool is_replace) override {
+ Connection *conn = &*conn_ref;
+ gates.dispatch_in_background("TestSuite_ms_dispatch",
+ [this, conn, conn_ref] {
+ return seastar::smp::submit_to(primary_sid, [this, conn] {
+ auto result = interceptor.find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked accepted connection: {}", *conn);
+ ceph_abort();
+ }
+
+ if (tracked_conn &&
+ !tracked_conn->is_protocol_closed() &&
+ tracked_conn != &*conn) {
+ logger().error("[{}] {} got accepted, but there's already a valid traced_conn [{}] {}",
+ result->index, *conn, tracked_index, *tracked_conn);
+ ceph_abort();
+ }
+
+ tracked_index = result->index;
+ tracked_conn = &*conn;
+ ++result->cnt_accept_dispatched;
+ logger().info("[Test] got accept (cnt_accept_dispatched={}), track [{}] {}",
+ result->cnt_accept_dispatched, result->index, *conn);
+ return flush_pending_send();
+ }).then([conn_ref] {});
+ });
+ }
+
+ void ms_handle_connect(
+ ConnectionRef conn_ref,
+ seastar::shard_id prv_shard) override {
+ Connection *conn = &*conn_ref;
+ gates.dispatch_in_background("TestSuite_ms_dispatch",
+ [this, conn, conn_ref] {
+ return seastar::smp::submit_to(primary_sid, [this, conn] {
+ auto result = interceptor.find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked connected connection: {}", *conn);
+ ceph_abort();
+ }
+
+ if (tracked_conn &&
+ !tracked_conn->is_protocol_closed() &&
+ tracked_conn != &*conn) {
+ logger().error("[{}] {} got connected, but there's already a avlid tracked_conn [{}] {}",
+ result->index, *conn, tracked_index, *tracked_conn);
+ ceph_abort();
+ }
+
+ if (tracked_conn == &*conn) {
+ ceph_assert(result->index == tracked_index);
+ }
+
+ ++result->cnt_connect_dispatched;
+ logger().info("[Test] got connected (cnt_connect_dispatched={}) -- [{}] {}",
+ result->cnt_connect_dispatched, result->index, *conn);
+ }).then([conn_ref] {});
+ });
+ }
+
+ void ms_handle_reset(
+ ConnectionRef conn_ref,
+ bool is_replace) override {
+ Connection *conn = &*conn_ref;
+ gates.dispatch_in_background("TestSuite_ms_dispatch",
+ [this, conn, conn_ref] {
+ return seastar::smp::submit_to(primary_sid, [this, conn] {
+ auto result = interceptor.find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked reset connection: {}", *conn);
+ ceph_abort();
+ }
+
+ if (tracked_conn != &*conn) {
+ logger().warn("[{}] {} got reset, but doesn't match tracked_conn [{}] {}",
+ result->index, *conn, tracked_index, *tracked_conn);
+ } else {
+ ceph_assert(result->index == tracked_index);
+ tracked_index = 0;
+ tracked_conn = nullptr;
+ }
+
+ ++result->cnt_reset_dispatched;
+ logger().info("[Test] got reset (cnt_reset_dispatched={}), untrack [{}] {}",
+ result->cnt_reset_dispatched, result->index, *conn);
+ }).then([conn_ref] {});
+ });
+ }
+
+ void ms_handle_remote_reset(
+ ConnectionRef conn_ref) override {
+ Connection *conn = &*conn_ref;
+ gates.dispatch_in_background("TestSuite_ms_dispatch",
+ [this, conn, conn_ref] {
+ return seastar::smp::submit_to(primary_sid, [this, conn] {
+ auto result = interceptor.find_result(&*conn);
+ if (result == nullptr) {
+ logger().error("Untracked remotely reset connection: {}", *conn);
+ ceph_abort();
+ }
+
+ if (tracked_conn != &*conn) {
+ logger().warn("[{}] {} got remotely reset, but doesn't match tracked_conn [{}] {}",
+ result->index, *conn, tracked_index, *tracked_conn);
+ } else {
+ ceph_assert(result->index == tracked_index);
+ }
+
+ ++result->cnt_remote_reset_dispatched;
+ logger().info("[Test] got remote reset (cnt_remote_reset_dispatched={}) -- [{}] {}",
+ result->cnt_remote_reset_dispatched, result->index, *conn);
+ }).then([conn_ref] {});
+ });
+ }
+
+ private:
+ seastar::future<> init(entity_addr_t test_addr, SocketPolicy policy) {
+ test_msgr->set_default_policy(policy);
+ test_msgr->set_auth_client(&dummy_auth);
+ test_msgr->set_auth_server(&dummy_auth);
+ test_msgr->set_interceptor(&interceptor);
+ return test_msgr->bind(entity_addrvec_t{test_addr}).safe_then([this] {
+ return test_msgr->start({this});
+ }, Messenger::bind_ertr::all_same_way([test_addr] (const std::error_code& e) {
+ logger().error("FailoverSuite: "
+ "there is another instance running at {}", test_addr);
+ ceph_abort();
+ }));
+ }
+
+ seastar::future<> send_op(bool expect_reply=true) {
+ ceph_assert(tracked_conn);
+ ceph_assert(!tracked_conn->is_protocol_closed());
+ if (expect_reply) {
+ ++pending_peer_receive;
+ }
+ pg_t pgid;
+ object_locator_t oloc;
+ hobject_t hobj(object_t(), oloc.key, CEPH_NOSNAP, pgid.ps(),
+ pgid.pool(), oloc.nspace);
+ spg_t spgid(pgid);
+ return tracked_conn->send(crimson::make_message<MOSDOp>(0, 0, hobj, spgid, 0, 0, 0));
+ }
+
+ seastar::future<> flush_pending_send() {
+ if (pending_send != 0) {
+ logger().info("[Test] flush sending {} ops", pending_send);
+ }
+ ceph_assert(tracked_conn);
+ ceph_assert(!tracked_conn->is_protocol_closed());
+ return seastar::do_until(
+ [this] { return pending_send == 0; },
+ [this] {
+ --pending_send;
+ return send_op();
+ });
+ }
+
+ seastar::future<> wait_ready(unsigned num_ready_conns,
+ unsigned num_replaced,
+ bool wait_received) {
+ assert(seastar::this_shard_id() == primary_sid);
+ unsigned pending_conns = 0;
+ unsigned pending_establish = 0;
+ unsigned replaced_conns = 0;
+ for (auto& result : interceptor.results) {
+ if (result.conn->is_protocol_closed_clean()) {
+ if (result.state == conn_state_t::replaced) {
+ ++replaced_conns;
+ }
+ } else if (result.conn->is_protocol_ready()) {
+ if (pending_send == 0 && pending_peer_receive == 0 && pending_receive == 0) {
+ result.state = conn_state_t::established;
+ } else {
+ ++pending_establish;
+ }
+ } else {
+ ++pending_conns;
+ }
+ }
+
+ bool do_wait = false;
+ if (num_ready_conns > 0) {
+ if (interceptor.results.size() > num_ready_conns) {
+ throw std::runtime_error(fmt::format(
+ "{} connections, more than expected: {}",
+ interceptor.results.size(), num_ready_conns));
+ } else if (interceptor.results.size() < num_ready_conns || pending_conns > 0) {
+ logger().info("[Test] wait_ready(): wait for connections,"
+ " currently {} out of {}, pending {} ready ...",
+ interceptor.results.size(), num_ready_conns, pending_conns);
+ do_wait = true;
+ }
+ }
+ if (wait_received) {
+ if (pending_send || pending_peer_receive || pending_receive) {
+ if (pending_conns || pending_establish) {
+ logger().info("[Test] wait_ready(): wait for pending_send={},"
+ " pending_peer_receive={}, pending_receive={},"
+ " pending {}/{} ready/establish connections ...",
+ pending_send, pending_peer_receive, pending_receive,
+ pending_conns, pending_establish);
+ do_wait = true;
+ } else {
+ // If there are pending messages, stop waiting if there are
+ // no longer pending connections.
+ }
+ } else {
+ // Stop waiting if there are no pending messages. Pending connections
+ // should not be important.
+ }
+ }
+ if (num_replaced > 0) {
+ if (replaced_conns > num_replaced) {
+ throw std::runtime_error(fmt::format(
+ "{} replaced connections, more than expected: {}",
+ replaced_conns, num_replaced));
+ }
+ if (replaced_conns < num_replaced) {
+ logger().info("[Test] wait_ready(): wait for {} replaced connections,"
+ " currently {} ...",
+ num_replaced, replaced_conns);
+ do_wait = true;
+ }
+ }
+
+ if (do_wait) {
+ return interceptor.wait(
+ ).then([this, num_ready_conns, num_replaced, wait_received] {
+ return wait_ready(num_ready_conns, num_replaced, wait_received);
+ });
+ } else {
+ logger().info("[Test] wait_ready(): wait done!");
+ return seastar::now();
+ }
+ }
+
+ // called by FailoverTest
+ public:
+ FailoverSuite(MessengerRef test_msgr,
+ entity_addr_t test_peer_addr,
+ const TestInterceptor& interceptor,
+ ShardedGates &gates)
+ : test_msgr(test_msgr),
+ test_peer_addr(test_peer_addr),
+ interceptor(interceptor),
+ gates{gates},
+ primary_sid{seastar::this_shard_id()} { }
+
+ entity_addr_t get_addr() const {
+ return test_msgr->get_myaddr();
+ }
+
+ seastar::future<> shutdown() {
+ test_msgr->stop();
+ return test_msgr->shutdown();
+ }
+
+ void needs_receive() {
+ ++pending_receive;
+ }
+
+ void notify_peer_reply() {
+ ceph_assert(pending_peer_receive > 0);
+ --pending_peer_receive;
+ logger().info("[Test] TestPeer said got op, left {} ops",
+ pending_peer_receive);
+ if (pending_peer_receive == 0) {
+ interceptor.notify();
+ }
+ }
+
+ void post_check() const {
+ // make sure all breakpoints were hit
+ for (auto& kv : interceptor.breakpoints) {
+ auto it = interceptor.breakpoints_counter.find(kv.first);
+ if (it == interceptor.breakpoints_counter.end()) {
+ throw std::runtime_error(fmt::format("{} was missed", kv.first));
+ }
+ auto expected = kv.second.rbegin()->first;
+ if (expected > it->second.counter) {
+ throw std::runtime_error(fmt::format(
+ "{} only triggered {} times, not the expected {}",
+ kv.first, it->second.counter, expected));
+ }
+ }
+ }
+
+ void dump_results() const {
+ for (auto& result : interceptor.results) {
+ result.dump();
+ }
+ }
+
+ static seastar::future<std::unique_ptr<FailoverSuite>>
+ create(entity_addr_t test_addr,
+ SocketPolicy test_policy,
+ entity_addr_t test_peer_addr,
+ const TestInterceptor& interceptor,
+ ShardedGates &gates) {
+ auto suite = std::make_unique<FailoverSuite>(
+ Messenger::create(
+ entity_name_t::OSD(TEST_OSD),
+ "Test",
+ TEST_NONCE,
+ false),
+ test_peer_addr,
+ interceptor,
+ gates);
+ return suite->init(test_addr, test_policy
+ ).then([suite = std::move(suite)] () mutable {
+ return std::move(suite);
+ });
+ }
+
+ // called by tests
+ public:
+ seastar::future<> connect_peer() {
+ logger().info("[Test] connect_peer({})", test_peer_addr);
+ assert(seastar::this_shard_id() == primary_sid);
+ auto conn = test_msgr->connect(test_peer_addr, entity_name_t::TYPE_OSD);
+ auto result = interceptor.find_result(&*conn);
+ ceph_assert(result != nullptr);
+
+ if (tracked_conn) {
+ if (tracked_conn->is_protocol_closed()) {
+ logger().info("[Test] this is a new session"
+ " replacing an closed one");
+ ceph_assert(tracked_conn != &*conn);
+ } else {
+ logger().info("[Test] this is not a new session");
+ ceph_assert(tracked_index == result->index);
+ ceph_assert(tracked_conn == &*conn);
+ }
+ } else {
+ logger().info("[Test] this is a new session");
+ }
+ tracked_index = result->index;
+ tracked_conn = &*conn;
+
+ return flush_pending_send();
+ }
+
+ seastar::future<> send_peer() {
+ assert(seastar::this_shard_id() == primary_sid);
+ if (tracked_conn) {
+ logger().info("[Test] send_peer()");
+ ceph_assert(!tracked_conn->is_protocol_closed());
+ ceph_assert(!pending_send);
+ return send_op();
+ } else {
+ ++pending_send;
+ logger().info("[Test] send_peer() (pending {})", pending_send);
+ return seastar::now();
+ }
+ }
+
+ seastar::future<> keepalive_peer() {
+ logger().info("[Test] keepalive_peer()");
+ assert(seastar::this_shard_id() == primary_sid);
+ ceph_assert(tracked_conn);
+ ceph_assert(!tracked_conn->is_protocol_closed());
+ return tracked_conn->send_keepalive();
+ }
+
+ seastar::future<> try_send_peer() {
+ logger().info("[Test] try_send_peer()");
+ assert(seastar::this_shard_id() == primary_sid);
+ ceph_assert(tracked_conn);
+ ceph_assert(!tracked_conn->is_protocol_closed());
+ return send_op(false);
+ }
+
+ seastar::future<> markdown() {
+ logger().info("[Test] markdown() in 100ms ...");
+ assert(seastar::this_shard_id() == primary_sid);
+ ceph_assert(tracked_conn);
+ // sleep to propagate potential remaining acks
+ return seastar::sleep(50ms
+ ).then([this] {
+ return seastar::smp::submit_to(
+ tracked_conn->get_shard_id(), [tracked_conn=tracked_conn] {
+ assert(tracked_conn->get_shard_id() == seastar::this_shard_id());
+ tracked_conn->mark_down();
+ });
+ }).then([] {
+ // sleep to wait for markdown propagate to the primary sid
+ return seastar::sleep(100ms);
+ });
+ }
+
+ seastar::future<> wait_blocked() {
+ logger().info("[Test] wait_blocked() ...");
+ assert(seastar::this_shard_id() == primary_sid);
+ return interceptor.blocker.wait_blocked();
+ }
+
+ void unblock() {
+ logger().info("[Test] unblock()");
+ assert(seastar::this_shard_id() == primary_sid);
+ return interceptor.blocker.unblock();
+ }
+
+ seastar::future<> wait_replaced(unsigned count) {
+ logger().info("[Test] wait_replaced({}) ...", count);
+ return wait_ready(0, count, false);
+ }
+
+ seastar::future<> wait_established() {
+ logger().info("[Test] wait_established() ...");
+ return wait_ready(0, 0, true);
+ }
+
+ seastar::future<std::reference_wrapper<ConnResults>>
+ wait_results(unsigned count) {
+ logger().info("[Test] wait_result({}) ...", count);
+ return wait_ready(count, 0, true).then([this] {
+ return std::reference_wrapper<ConnResults>(interceptor.results);
+ });
+ }
+
+ bool is_standby() {
+ assert(seastar::this_shard_id() == primary_sid);
+ ceph_assert(tracked_conn);
+ return tracked_conn->is_protocol_standby();
+ }
+};
+
+class FailoverTest : public Dispatcher {
+ crimson::auth::DummyAuthClientServer dummy_auth;
+ MessengerRef cmd_msgr;
+ ConnectionRef cmd_conn;
+ const entity_addr_t test_addr;
+ const entity_addr_t test_peer_addr;
+
+ std::optional<seastar::promise<>> recv_pong;
+ std::optional<seastar::promise<>> recv_cmdreply;
+
+ std::unique_ptr<FailoverSuite> test_suite;
+
+ std::optional<seastar::future<>> ms_dispatch(ConnectionRef c, MessageRef m) override {
+ switch (m->get_type()) {
+ case CEPH_MSG_PING:
+ ceph_assert(recv_pong);
+ recv_pong->set_value();
+ recv_pong = std::nullopt;
+ break;
+ case MSG_COMMAND_REPLY:
+ ceph_assert(recv_cmdreply);
+ recv_cmdreply->set_value();
+ recv_cmdreply = std::nullopt;
+ break;
+ case MSG_COMMAND: {
+ auto m_cmd = boost::static_pointer_cast<MCommand>(m);
+ ceph_assert(static_cast<cmd_t>(m_cmd->cmd[0][0]) == cmd_t::suite_recv_op);
+ ceph_assert(test_suite);
+ test_suite->notify_peer_reply();
+ break;
+ }
+ default:
+ logger().error("{} got unexpected msg from cmd server: {}", *c, *m);
+ ceph_abort();
+ }
+ return {seastar::now()};
+ }
+
+ private:
+ seastar::future<> prepare_cmd(
+ cmd_t cmd,
+ std::function<void(MCommand&)>
+ f_prepare = [] (auto& m) { return; }) {
+ assert(!recv_cmdreply);
+ recv_cmdreply = seastar::promise<>();
+ auto fut = recv_cmdreply->get_future();
+ auto m = crimson::make_message<MCommand>();
+ m->cmd.emplace_back(1, static_cast<char>(cmd));
+ f_prepare(*m);
+ return cmd_conn->send(std::move(m)).then([fut = std::move(fut)] () mutable {
+ return std::move(fut);
+ });
+ }
+
+ seastar::future<> start_peer(policy_t peer_policy) {
+ return prepare_cmd(cmd_t::suite_start,
+ [peer_policy] (auto& m) {
+ m.cmd.emplace_back(1, static_cast<char>(peer_policy));
+ });
+ }
+
+ seastar::future<> stop_peer() {
+ return prepare_cmd(cmd_t::suite_stop);
+ }
+
+ seastar::future<> pingpong() {
+ assert(!recv_pong);
+ recv_pong = seastar::promise<>();
+ auto fut = recv_pong->get_future();
+ return cmd_conn->send(crimson::make_message<MPing>()
+ ).then([fut = std::move(fut)] () mutable {
+ return std::move(fut);
+ });
+ }
+
+ seastar::future<> init(entity_addr_t cmd_peer_addr) {
+ cmd_msgr->set_default_policy(SocketPolicy::lossy_client(0));
+ cmd_msgr->set_auth_client(&dummy_auth);
+ cmd_msgr->set_auth_server(&dummy_auth);
+ return cmd_msgr->start({this}).then([this, cmd_peer_addr] {
+ logger().info("CmdCli connect to CmdSrv({}) ...", cmd_peer_addr);
+ cmd_conn = cmd_msgr->connect(cmd_peer_addr, entity_name_t::TYPE_OSD);
+ return pingpong();
+ });
+ }
+
+ public:
+ FailoverTest(MessengerRef cmd_msgr,
+ entity_addr_t test_addr,
+ entity_addr_t test_peer_addr)
+ : cmd_msgr(cmd_msgr),
+ test_addr(test_addr),
+ test_peer_addr(test_peer_addr) { }
+
+ seastar::future<> shutdown() {
+ logger().info("CmdCli shutdown...");
+ assert(!recv_cmdreply);
+ auto m = crimson::make_message<MCommand>();
+ m->cmd.emplace_back(1, static_cast<char>(cmd_t::shutdown));
+ return cmd_conn->send(std::move(m)).then([] {
+ return seastar::sleep(200ms);
+ }).then([this] {
+ cmd_msgr->stop();
+ return cmd_msgr->shutdown();
+ });
+ }
+
+ static seastar::future<seastar::lw_shared_ptr<FailoverTest>>
+ create(entity_addr_t test_addr,
+ entity_addr_t cmd_peer_addr,
+ entity_addr_t test_peer_addr) {
+ auto test = seastar::make_lw_shared<FailoverTest>(
+ Messenger::create(
+ entity_name_t::OSD(CMD_CLI_OSD),
+ "CmdCli",
+ CMD_CLI_NONCE,
+ true),
+ test_addr, test_peer_addr);
+ return test->init(cmd_peer_addr).then([test] {
+ logger().info("CmdCli ready");
+ return test;
+ });
+ }
+
+ // called by tests
+ public:
+ seastar::future<> run_suite(
+ std::string name,
+ const TestInterceptor& interceptor,
+ policy_t test_policy,
+ policy_t peer_policy,
+ std::function<seastar::future<>(FailoverSuite&)>&& f) {
+ logger().info("\n\n[{}]", name);
+ ceph_assert(!test_suite);
+ SocketPolicy test_policy_ = to_socket_policy(test_policy);
+ return ShardedGates::create(
+ ).then([this, test_policy_, peer_policy, interceptor,
+ f=std::move(f)](auto *gates) mutable {
+ return FailoverSuite::create(
+ test_addr, test_policy_, test_peer_addr, interceptor, *gates
+ ).then([this, peer_policy, f = std::move(f)](auto suite) mutable {
+ ceph_assert(suite->get_addr() == test_addr);
+ test_suite.swap(suite);
+ return start_peer(peer_policy
+ ).then([this, f = std::move(f)] {
+ return f(*test_suite);
+ }).then([this] {
+ test_suite->post_check();
+ logger().info("\n[SUCCESS]");
+ }).handle_exception([this](auto eptr) {
+ logger().info("\n[FAIL: {}]", eptr);
+ test_suite->dump_results();
+ throw;
+ }).then([this] {
+ return stop_peer();
+ }).then([this] {
+ return test_suite->shutdown(
+ ).then([this] {
+ test_suite.reset();
+ });
+ });
+ }).then([gates] {
+ return gates->close();
+ });
+ });
+ }
+
+ seastar::future<> peer_connect_me() {
+ logger().info("[Test] peer_connect_me({})", test_addr);
+ return prepare_cmd(cmd_t::suite_connect_me,
+ [this] (auto& m) {
+ m.cmd.emplace_back(fmt::format("{}", test_addr));
+ });
+ }
+
+ seastar::future<> peer_send_me() {
+ logger().info("[Test] peer_send_me()");
+ ceph_assert(test_suite);
+ test_suite->needs_receive();
+ return prepare_cmd(cmd_t::suite_send_me);
+ }
+
+ seastar::future<> try_peer_send_me() {
+ logger().info("[Test] try_peer_send_me()");
+ ceph_assert(test_suite);
+ return prepare_cmd(cmd_t::suite_send_me);
+ }
+
+ seastar::future<> send_bidirectional() {
+ ceph_assert(test_suite);
+ return test_suite->send_peer().then([this] {
+ return peer_send_me();
+ });
+ }
+
+ seastar::future<> peer_keepalive_me() {
+ logger().info("[Test] peer_keepalive_me()");
+ ceph_assert(test_suite);
+ return prepare_cmd(cmd_t::suite_keepalive_me);
+ }
+
+ seastar::future<> markdown_peer() {
+ logger().info("[Test] markdown_peer() in 150ms ...");
+ // sleep to propagate potential remaining acks
+ return seastar::sleep(50ms
+ ).then([this] {
+ return prepare_cmd(cmd_t::suite_markdown);
+ }).then([] {
+ // sleep awhile for peer markdown propagated
+ return seastar::sleep(100ms);
+ });
+ }
+};
+
+class FailoverSuitePeer : public Dispatcher {
+ using cb_t = std::function<seastar::future<>()>;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+ MessengerRef peer_msgr;
+ cb_t op_callback;
+
+ ConnectionRef tracked_conn;
+ unsigned pending_send = 0;
+
+ std::optional<seastar::future<>> ms_dispatch(ConnectionRef conn, MessageRef m) override {
+ logger().info("[TestPeer] got op from Test");
+ ceph_assert(m->get_type() == CEPH_MSG_OSD_OP);
+ std::ignore = op_callback();
+ return {seastar::now()};
+ }
+
+ void ms_handle_accept(
+ ConnectionRef conn,
+ seastar::shard_id prv_shard,
+ bool is_replace) override {
+ assert(prv_shard == seastar::this_shard_id());
+ logger().info("[TestPeer] got accept from Test");
+
+ if (tracked_conn &&
+ !tracked_conn->is_protocol_closed() &&
+ tracked_conn != conn) {
+ logger().error("[TestPeer] {} got accepted, but there's already a valid traced_conn {}",
+ *conn, *tracked_conn);
+ }
+ tracked_conn = conn;
+ std::ignore = flush_pending_send();
+ }
+
+ void ms_handle_reset(ConnectionRef conn, bool is_replace) override {
+ logger().info("[TestPeer] got reset from Test");
+ }
+
+ private:
+ seastar::future<> init(entity_addr_t test_peer_addr, SocketPolicy policy) {
+ peer_msgr->set_default_policy(policy);
+ peer_msgr->set_auth_client(&dummy_auth);
+ peer_msgr->set_auth_server(&dummy_auth);
+ return peer_msgr->bind(entity_addrvec_t{test_peer_addr}).safe_then([this] {
+ return peer_msgr->start({this});
+ }, Messenger::bind_ertr::all_same_way([test_peer_addr] (const std::error_code& e) {
+ logger().error("FailoverSuitePeer: "
+ "there is another instance running at {}", test_peer_addr);
+ ceph_abort();
+ }));
+ }
+
+ seastar::future<> send_op() {
+ ceph_assert(tracked_conn);
+ if (tracked_conn->is_protocol_closed()) {
+ logger().error("[TestPeer] send op but the connection is closed -- {}",
+ *tracked_conn);
+ }
+
+ pg_t pgid;
+ object_locator_t oloc;
+ hobject_t hobj(object_t(), oloc.key, CEPH_NOSNAP, pgid.ps(),
+ pgid.pool(), oloc.nspace);
+ spg_t spgid(pgid);
+ return tracked_conn->send(crimson::make_message<MOSDOp>(0, 0, hobj, spgid, 0, 0, 0));
+ }
+
+ seastar::future<> flush_pending_send() {
+ if (pending_send != 0) {
+ logger().info("[TestPeer] flush sending {} ops", pending_send);
+ }
+ ceph_assert(tracked_conn);
+ return seastar::do_until(
+ [this] { return pending_send == 0; },
+ [this] {
+ --pending_send;
+ return send_op();
+ });
+ }
+
+ public:
+ FailoverSuitePeer(MessengerRef peer_msgr, cb_t op_callback)
+ : peer_msgr(peer_msgr),
+ op_callback(op_callback) { }
+
+ seastar::future<> shutdown() {
+ peer_msgr->stop();
+ return peer_msgr->shutdown();
+ }
+
+ seastar::future<> connect_peer(entity_addr_t test_addr_decoded) {
+ logger().info("[TestPeer] connect_peer({})", test_addr_decoded);
+ auto conn = peer_msgr->connect(test_addr_decoded, entity_name_t::TYPE_OSD);
+
+ if (tracked_conn) {
+ if (tracked_conn->is_protocol_closed()) {
+ logger().info("[TestPeer] this is a new session"
+ " replacing an closed one");
+ ceph_assert(tracked_conn != conn);
+ } else {
+ logger().info("[TestPeer] this is not a new session");
+ ceph_assert(tracked_conn == conn);
+ }
+ } else {
+ logger().info("[TestPeer] this is a new session");
+ }
+ tracked_conn = conn;
+
+ return flush_pending_send();
+ }
+
+ seastar::future<> send_peer() {
+ if (tracked_conn) {
+ logger().info("[TestPeer] send_peer()");
+ ceph_assert(!pending_send);
+ return send_op();
+ } else {
+ ++pending_send;
+ logger().info("[TestPeer] send_peer() (pending {})", pending_send);
+ return seastar::now();
+ }
+ }
+
+ seastar::future<> keepalive_peer() {
+ logger().info("[TestPeer] keepalive_peer()");
+ ceph_assert(tracked_conn);
+ return tracked_conn->send_keepalive();
+ }
+
+ seastar::future<> markdown() {
+ logger().info("[TestPeer] markdown()");
+ ceph_assert(tracked_conn);
+ tracked_conn->mark_down();
+ return seastar::now();
+ }
+
+ static seastar::future<std::unique_ptr<FailoverSuitePeer>>
+ create(entity_addr_t test_peer_addr, const SocketPolicy& policy, cb_t op_callback) {
+ auto suite = std::make_unique<FailoverSuitePeer>(
+ Messenger::create(
+ entity_name_t::OSD(TEST_PEER_OSD),
+ "TestPeer",
+ TEST_PEER_NONCE,
+ true),
+ op_callback
+ );
+ return suite->init(test_peer_addr, policy
+ ).then([suite = std::move(suite)] () mutable {
+ return std::move(suite);
+ });
+ }
+};
+
+class FailoverTestPeer : public Dispatcher {
+ crimson::auth::DummyAuthClientServer dummy_auth;
+ MessengerRef cmd_msgr;
+ ConnectionRef cmd_conn;
+ const entity_addr_t test_peer_addr;
+ std::unique_ptr<FailoverSuitePeer> test_suite;
+
+ std::optional<seastar::future<>> ms_dispatch(ConnectionRef c, MessageRef m) override {
+ ceph_assert(cmd_conn == c);
+ switch (m->get_type()) {
+ case CEPH_MSG_PING:
+ std::ignore = c->send(crimson::make_message<MPing>());
+ break;
+ case MSG_COMMAND: {
+ auto m_cmd = boost::static_pointer_cast<MCommand>(m);
+ auto cmd = static_cast<cmd_t>(m_cmd->cmd[0][0]);
+ if (cmd == cmd_t::shutdown) {
+ logger().info("CmdSrv shutdown...");
+ // forwarded to FailoverTestPeer::wait()
+ cmd_msgr->stop();
+ std::ignore = cmd_msgr->shutdown();
+ } else {
+ std::ignore = handle_cmd(cmd, m_cmd).then([c] {
+ return c->send(crimson::make_message<MCommandReply>());
+ });
+ }
+ break;
+ }
+ default:
+ logger().error("{} got unexpected msg from cmd client: {}", *c, *m);
+ ceph_abort();
+ }
+ return {seastar::now()};
+ }
+
+ void ms_handle_accept(
+ ConnectionRef conn,
+ seastar::shard_id prv_shard,
+ bool is_replace) override {
+ assert(prv_shard == seastar::this_shard_id());
+ cmd_conn = conn;
+ }
+
+ private:
+ seastar::future<> notify_recv_op() {
+ ceph_assert(cmd_conn);
+ auto m = crimson::make_message<MCommand>();
+ m->cmd.emplace_back(1, static_cast<char>(cmd_t::suite_recv_op));
+ return cmd_conn->send(std::move(m));
+ }
+
+ seastar::future<> handle_cmd(cmd_t cmd, MRef<MCommand> m_cmd) {
+ switch (cmd) {
+ case cmd_t::suite_start: {
+ ceph_assert(!test_suite);
+ auto policy = to_socket_policy(static_cast<policy_t>(m_cmd->cmd[1][0]));
+ return FailoverSuitePeer::create(
+ test_peer_addr, policy, [this] { return notify_recv_op(); }
+ ).then([this] (auto suite) {
+ test_suite.swap(suite);
+ });
+ }
+ case cmd_t::suite_stop:
+ ceph_assert(test_suite);
+ return test_suite->shutdown().then([this] {
+ test_suite.reset();
+ });
+ case cmd_t::suite_connect_me: {
+ ceph_assert(test_suite);
+ entity_addr_t test_addr_decoded = entity_addr_t();
+ test_addr_decoded.parse(m_cmd->cmd[1].c_str(), nullptr);
+ return test_suite->connect_peer(test_addr_decoded);
+ }
+ case cmd_t::suite_send_me:
+ ceph_assert(test_suite);
+ return test_suite->send_peer();
+ case cmd_t::suite_keepalive_me:
+ ceph_assert(test_suite);
+ return test_suite->keepalive_peer();
+ case cmd_t::suite_markdown:
+ ceph_assert(test_suite);
+ return test_suite->markdown();
+ default:
+ logger().error("TestPeer got unexpected command {} from Test",
+ fmt::ptr(m_cmd.get()));
+ ceph_abort();
+ return seastar::now();
+ }
+ }
+
+ seastar::future<> init(entity_addr_t cmd_peer_addr) {
+ cmd_msgr->set_default_policy(SocketPolicy::stateless_server(0));
+ cmd_msgr->set_auth_client(&dummy_auth);
+ cmd_msgr->set_auth_server(&dummy_auth);
+ return cmd_msgr->bind(entity_addrvec_t{cmd_peer_addr}).safe_then([this] {
+ return cmd_msgr->start({this});
+ }, Messenger::bind_ertr::all_same_way([cmd_peer_addr] (const std::error_code& e) {
+ logger().error("FailoverTestPeer: "
+ "there is another instance running at {}", cmd_peer_addr);
+ ceph_abort();
+ }));
+ }
+
+ public:
+ FailoverTestPeer(MessengerRef cmd_msgr,
+ entity_addr_t test_peer_addr)
+ : cmd_msgr(cmd_msgr),
+ test_peer_addr(test_peer_addr) { }
+
+ seastar::future<> wait() {
+ return cmd_msgr->wait();
+ }
+
+ static seastar::future<std::unique_ptr<FailoverTestPeer>>
+ create(entity_addr_t cmd_peer_addr, entity_addr_t test_peer_addr) {
+ auto test_peer = std::make_unique<FailoverTestPeer>(
+ Messenger::create(
+ entity_name_t::OSD(CMD_SRV_OSD),
+ "CmdSrv",
+ CMD_SRV_NONCE,
+ true),
+ test_peer_addr);
+ return test_peer->init(cmd_peer_addr
+ ).then([test_peer = std::move(test_peer)] () mutable {
+ logger().info("CmdSrv ready");
+ return std::move(test_peer);
+ });
+ }
+};
+
+seastar::future<>
+test_v2_lossy_early_connect_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {custom_bp_t::SOCKET_CONNECTING},
+ {custom_bp_t::BANNER_WRITE},
+ {custom_bp_t::BANNER_READ},
+ {custom_bp_t::BANNER_PAYLOAD_READ},
+ {Tag::HELLO, bp_type_t::WRITE},
+ {Tag::HELLO, bp_type_t::READ},
+ {Tag::AUTH_REQUEST, bp_type_t::WRITE},
+ {Tag::AUTH_DONE, bp_type_t::READ},
+ {Tag::AUTH_SIGNATURE, bp_type_t::WRITE},
+ {Tag::AUTH_SIGNATURE, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossy_early_connect_fault -- {}", bp),
+ interceptor,
+ policy_t::lossy_client,
+ policy_t::stateless_server,
+ [] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossy_connect_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::CLIENT_IDENT, bp_type_t::WRITE},
+ {Tag::SERVER_IDENT, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossy_connect_fault -- {}", bp),
+ interceptor,
+ policy_t::lossy_client,
+ policy_t::stateless_server,
+ [] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 2, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossy_connected_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::MESSAGE, bp_type_t::WRITE},
+ {Tag::MESSAGE, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossy_connected_fault -- {}", bp),
+ interceptor,
+ policy_t::lossy_client,
+ policy_t::stateless_server,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(1, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossy_early_accept_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {custom_bp_t::BANNER_WRITE},
+ {custom_bp_t::BANNER_READ},
+ {custom_bp_t::BANNER_PAYLOAD_READ},
+ {Tag::HELLO, bp_type_t::WRITE},
+ {Tag::HELLO, bp_type_t::READ},
+ {Tag::AUTH_REQUEST, bp_type_t::READ},
+ {Tag::AUTH_DONE, bp_type_t::WRITE},
+ {Tag::AUTH_SIGNATURE, bp_type_t::WRITE},
+ {Tag::AUTH_SIGNATURE, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossy_early_accept_fault -- {}", bp),
+ interceptor,
+ policy_t::stateless_server,
+ policy_t::lossy_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossy_accept_fault(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::CLIENT_IDENT, bp_type_t::READ};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossy_accept_fault -- {}", bp),
+ interceptor,
+ policy_t::stateless_server,
+ policy_t::lossy_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossy_establishing_fault(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::SERVER_IDENT, bp_type_t::WRITE};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossy_establishing_fault -- {}", bp),
+ interceptor,
+ policy_t::stateless_server,
+ policy_t::lossy_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(1, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossy_accepted_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::MESSAGE, bp_type_t::WRITE},
+ {Tag::MESSAGE, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossy_accepted_fault -- {}", bp),
+ interceptor,
+ policy_t::stateless_server,
+ policy_t::lossy_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(1, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_connect_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::CLIENT_IDENT, bp_type_t::WRITE},
+ {Tag::SERVER_IDENT, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_connect_fault -- {}", bp),
+ interceptor,
+ policy_t::lossless_client,
+ policy_t::stateful_server,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 2, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_connected_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::MESSAGE, bp_type_t::WRITE},
+ {Tag::MESSAGE, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_connected_fault -- {}", bp),
+ interceptor,
+ policy_t::lossless_client,
+ policy_t::stateful_server,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 1, 1, 2);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_connected_fault2(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::ACK, bp_type_t::READ},
+ {Tag::ACK, bp_type_t::WRITE},
+ {Tag::KEEPALIVE2, bp_type_t::READ},
+ {Tag::KEEPALIVE2, bp_type_t::WRITE},
+ {Tag::KEEPALIVE2_ACK, bp_type_t::READ},
+ {Tag::KEEPALIVE2_ACK, bp_type_t::WRITE},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_connected_fault2 -- {}", bp),
+ interceptor,
+ policy_t::lossless_client,
+ policy_t::stateful_server,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.keepalive_peer();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_keepalive_me();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 1, 1, 2);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_reconnect_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<std::pair<Breakpoint, Breakpoint>>{
+ {{Tag::MESSAGE, bp_type_t::WRITE},
+ {Tag::SESSION_RECONNECT, bp_type_t::WRITE}},
+ {{Tag::MESSAGE, bp_type_t::WRITE},
+ {Tag::SESSION_RECONNECT_OK, bp_type_t::READ}},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp_pair) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp_pair.first);
+ interceptor.make_fault(bp_pair.second);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_reconnect_fault -- {}, {}",
+ bp_pair.first, bp_pair.second),
+ interceptor,
+ policy_t::lossless_client,
+ policy_t::stateful_server,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(3, 1, 2, 2);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_accept_fault(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::CLIENT_IDENT, bp_type_t::READ};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_accept_fault -- {}", bp),
+ interceptor,
+ policy_t::stateful_server,
+ policy_t::lossless_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_establishing_fault(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::SERVER_IDENT, bp_type_t::WRITE};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_establishing_fault -- {}", bp),
+ interceptor,
+ policy_t::stateful_server,
+ policy_t::lossless_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_accepted_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::MESSAGE, bp_type_t::WRITE},
+ {Tag::MESSAGE, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_accepted_fault -- {}", bp),
+ interceptor,
+ policy_t::stateful_server,
+ policy_t::lossless_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_reaccept_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<std::pair<Breakpoint, Breakpoint>>{
+ {{Tag::MESSAGE, bp_type_t::READ},
+ {Tag::SESSION_RECONNECT, bp_type_t::READ}},
+ {{Tag::MESSAGE, bp_type_t::READ},
+ {Tag::SESSION_RECONNECT_OK, bp_type_t::WRITE}},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp_pair) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp_pair.first);
+ interceptor.make_fault(bp_pair.second);
+ return test.run_suite(
+ fmt::format("test_v2_lossless_reaccept_fault -- {}, {}",
+ bp_pair.first, bp_pair.second),
+ interceptor,
+ policy_t::stateful_server,
+ policy_t::lossless_client,
+ [&test, bp = bp_pair.second] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.send_bidirectional();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(3);
+ }).then([bp] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ if (bp == Breakpoint{Tag::SESSION_RECONNECT, bp_type_t::READ}) {
+ results[0].assert_accept(1, 1, 0, 2);
+ } else {
+ results[0].assert_accept(1, 1, 0, 3);
+ }
+ results[0].assert_reset(0, 0);
+ if (bp == Breakpoint{Tag::SESSION_RECONNECT, bp_type_t::READ}) {
+ results[1].assert_state_at(conn_state_t::closed);
+ } else {
+ results[1].assert_state_at(conn_state_t::replaced);
+ }
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0, 1, 0);
+ results[1].assert_reset(0, 0);
+ results[2].assert_state_at(conn_state_t::replaced);
+ results[2].assert_connect(0, 0, 0, 0);
+ results[2].assert_accept(1, 0, 1, 0);
+ results[2].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_peer_connect_fault(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {Tag::CLIENT_IDENT, bp_type_t::WRITE},
+ {Tag::SERVER_IDENT, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_peer_connect_fault -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 2, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_peer_accept_fault(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::CLIENT_IDENT, bp_type_t::READ};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_peer_accept_fault -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_peer_establishing_fault(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::SERVER_IDENT, bp_type_t::WRITE};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_peer_establishing_fault -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_peer_connected_fault_reconnect(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::MESSAGE, bp_type_t::WRITE};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_peer_connected_fault_reconnect -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 1, 1, 2);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_peer_connected_fault_reaccept(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::MESSAGE, bp_type_t::READ};
+ TestInterceptor interceptor;
+ interceptor.make_fault(bp);
+ return test.run_suite(
+ fmt::format("test_v2_peer_connected_fault_reaccept -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 1);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0, 1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<bool>
+check_peer_wins(FailoverTest& test) {
+ return seastar::do_with(bool(), [&test] (auto& ret) {
+ return test.run_suite("check_peer_wins",
+ TestInterceptor(),
+ policy_t::lossy_client,
+ policy_t::stateless_server,
+ [&ret] (FailoverSuite& suite) {
+ return suite.connect_peer().then([&suite] {
+ return suite.wait_results(1);
+ }).then([&ret] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ ret = results[0].conn->peer_wins();
+ logger().info("check_peer_wins: {}", ret);
+ });
+ }).then([&ret] {
+ return ret;
+ });
+ });
+}
+
+seastar::future<>
+test_v2_racing_reconnect_acceptor_lose(FailoverTest& test) {
+ return seastar::do_with(std::vector<std::pair<unsigned, Breakpoint>>{
+ {1, {Tag::SESSION_RECONNECT, bp_type_t::READ}},
+ {2, {custom_bp_t::BANNER_WRITE}},
+ {2, {custom_bp_t::BANNER_READ}},
+ {2, {custom_bp_t::BANNER_PAYLOAD_READ}},
+ {2, {Tag::HELLO, bp_type_t::WRITE}},
+ {2, {Tag::HELLO, bp_type_t::READ}},
+ {2, {Tag::AUTH_REQUEST, bp_type_t::READ}},
+ {2, {Tag::AUTH_DONE, bp_type_t::WRITE}},
+ {2, {Tag::AUTH_SIGNATURE, bp_type_t::WRITE}},
+ {2, {Tag::AUTH_SIGNATURE, bp_type_t::READ}},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ // fault acceptor
+ interceptor.make_fault({Tag::MESSAGE, bp_type_t::READ});
+ // block acceptor
+ interceptor.make_block(bp.second, bp.first);
+ return test.run_suite(
+ fmt::format("test_v2_racing_reconnect_acceptor_lose -- {}({})",
+ bp.second, bp.first),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 0, 1, 1);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::closed);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_racing_reconnect_acceptor_win(FailoverTest& test) {
+ return seastar::do_with(std::vector<std::pair<unsigned, Breakpoint>>{
+ {1, {Tag::SESSION_RECONNECT, bp_type_t::WRITE}},
+ {2, {custom_bp_t::SOCKET_CONNECTING}},
+ {2, {custom_bp_t::BANNER_WRITE}},
+ {2, {custom_bp_t::BANNER_READ}},
+ {2, {custom_bp_t::BANNER_PAYLOAD_READ}},
+ {2, {Tag::HELLO, bp_type_t::WRITE}},
+ {2, {Tag::HELLO, bp_type_t::READ}},
+ {2, {Tag::AUTH_REQUEST, bp_type_t::WRITE}},
+ {2, {Tag::AUTH_DONE, bp_type_t::READ}},
+ {2, {Tag::AUTH_SIGNATURE, bp_type_t::WRITE}},
+ {2, {Tag::AUTH_SIGNATURE, bp_type_t::READ}},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ // fault connector
+ interceptor.make_fault({Tag::MESSAGE, bp_type_t::WRITE});
+ // block connector
+ interceptor.make_block(bp.second, bp.first);
+ return test.run_suite(
+ fmt::format("test_v2_racing_reconnect_acceptor_win -- {}({})",
+ bp.second, bp.first),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_replaced(1);
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 1);
+ results[0].assert_accept(0, 0, 0, 1);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0, 1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_racing_connect_acceptor_lose(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {custom_bp_t::BANNER_WRITE},
+ {custom_bp_t::BANNER_READ},
+ {custom_bp_t::BANNER_PAYLOAD_READ},
+ {Tag::HELLO, bp_type_t::WRITE},
+ {Tag::HELLO, bp_type_t::READ},
+ {Tag::AUTH_REQUEST, bp_type_t::READ},
+ {Tag::AUTH_DONE, bp_type_t::WRITE},
+ {Tag::AUTH_SIGNATURE, bp_type_t::WRITE},
+ {Tag::AUTH_SIGNATURE, bp_type_t::READ},
+ {Tag::CLIENT_IDENT, bp_type_t::READ},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ // block acceptor
+ interceptor.make_block(bp);
+ return test.run_suite(
+ fmt::format("test_v2_racing_connect_acceptor_lose -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(1, 1, 0, 1);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_racing_connect_acceptor_win(FailoverTest& test) {
+ return seastar::do_with(std::vector<Breakpoint>{
+ {custom_bp_t::SOCKET_CONNECTING},
+ {custom_bp_t::BANNER_WRITE},
+ {custom_bp_t::BANNER_READ},
+ {custom_bp_t::BANNER_PAYLOAD_READ},
+ {Tag::HELLO, bp_type_t::WRITE},
+ {Tag::HELLO, bp_type_t::READ},
+ {Tag::AUTH_REQUEST, bp_type_t::WRITE},
+ {Tag::AUTH_DONE, bp_type_t::READ},
+ {Tag::AUTH_SIGNATURE, bp_type_t::WRITE},
+ {Tag::AUTH_SIGNATURE, bp_type_t::READ},
+ {Tag::CLIENT_IDENT, bp_type_t::WRITE},
+ }, [&test] (auto& failure_cases) {
+ return seastar::do_for_each(failure_cases, [&test] (auto bp) {
+ TestInterceptor interceptor;
+ // block connector
+ interceptor.make_block(bp);
+ return test.run_suite(
+ fmt::format("test_v2_racing_connect_acceptor_win -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_replaced(1);
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 0);
+ results[0].assert_accept(0, 0, 0, 1);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+ });
+ });
+}
+
+seastar::future<>
+test_v2_racing_connect_reconnect_lose(FailoverTest& test) {
+ TestInterceptor interceptor;
+ interceptor.make_fault({Tag::SERVER_IDENT, bp_type_t::READ});
+ interceptor.make_block({Tag::CLIENT_IDENT, bp_type_t::WRITE}, 2);
+ return test.run_suite("test_v2_racing_connect_reconnect_lose",
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_replaced(1);
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 2, 0, 0);
+ results[0].assert_accept(0, 0, 0, 1);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_racing_connect_reconnect_win(FailoverTest& test) {
+ TestInterceptor interceptor;
+ interceptor.make_fault({Tag::SERVER_IDENT, bp_type_t::READ});
+ interceptor.make_block({Tag::SESSION_RECONNECT, bp_type_t::READ});
+ return test.run_suite("test_v2_racing_connect_reconnect_win",
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 2, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::closed);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0, 1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_stale_connect(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::SERVER_IDENT, bp_type_t::READ};
+ TestInterceptor interceptor;
+ interceptor.make_stall(bp);
+ return test.run_suite(
+ fmt::format("test_v2_stale_connect -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_replaced(1);
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 1, 0, 0);
+ results[0].assert_accept(0, 0, 0, 1);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_stale_reconnect(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::SESSION_RECONNECT_OK, bp_type_t::READ};
+ TestInterceptor interceptor;
+ interceptor.make_fault({Tag::MESSAGE, bp_type_t::WRITE});
+ interceptor.make_stall(bp);
+ return test.run_suite(
+ fmt::format("test_v2_stale_reconnect -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_replaced(1);
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(2, 1, 1, 1);
+ results[0].assert_accept(0, 0, 0, 1);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0, 1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_stale_accept(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::CLIENT_IDENT, bp_type_t::READ};
+ TestInterceptor interceptor;
+ interceptor.make_stall(bp);
+ return test.run_suite(
+ fmt::format("test_v2_stale_accept -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_established();
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_stale_establishing(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::SERVER_IDENT, bp_type_t::WRITE};
+ TestInterceptor interceptor;
+ interceptor.make_stall(bp);
+ return test.run_suite(
+ fmt::format("test_v2_stale_establishing -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_replaced(1);
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0);
+ results[1].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_stale_reaccept(FailoverTest& test) {
+ auto bp = Breakpoint{Tag::SESSION_RECONNECT_OK, bp_type_t::WRITE};
+ TestInterceptor interceptor;
+ interceptor.make_fault({Tag::MESSAGE, bp_type_t::READ});
+ interceptor.make_stall(bp);
+ return test.run_suite(
+ fmt::format("test_v2_stale_reaccept -- {}", bp),
+ interceptor,
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ return test.peer_send_me();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&suite] {
+ return suite.wait_blocked();
+ }).then([] {
+ logger().info("[Test] block the broken REPLACING for 210ms...");
+ return seastar::sleep(210ms);
+ }).then([&suite] {
+ suite.unblock();
+ return suite.wait_results(3);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 3);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 0, 1, 0);
+ results[1].assert_reset(0, 0);
+ results[2].assert_state_at(conn_state_t::replaced);
+ results[2].assert_connect(0, 0, 0, 0);
+ results[2].assert_accept(1, 0);
+ results[2].assert_reset(0, 0);
+ ceph_assert(results[2].server_reconnect_attempts >= 1);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossy_client(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_lossy_client",
+ TestInterceptor(),
+ policy_t::lossy_client,
+ policy_t::stateless_server,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return suite.connect_peer();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 1 --");
+ logger().info("[Test] client markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(1, 1, 0, 1);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 2 --");
+ logger().info("[Test] server markdown...");
+ return test.markdown_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::closed);
+ results[1].assert_connect(1, 1, 0, 1);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(1, 0);
+ }).then([&suite] {
+ logger().info("-- 3 --");
+ logger().info("[Test] client reconnect...");
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_results(3);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::closed);
+ results[1].assert_connect(1, 1, 0, 1);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(1, 0);
+ results[2].assert_state_at(conn_state_t::established);
+ results[2].assert_connect(1, 1, 0, 1);
+ results[2].assert_accept(0, 0, 0, 0);
+ results[2].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_stateless_server(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_stateless_server",
+ TestInterceptor(),
+ policy_t::stateless_server,
+ policy_t::lossy_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 1 --");
+ logger().info("[Test] client markdown...");
+ return test.markdown_peer();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(1, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 2 --");
+ logger().info("[Test] server markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(1, 0);
+ results[1].assert_state_at(conn_state_t::closed);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 3 --");
+ logger().info("[Test] client reconnect...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(3);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(1, 0);
+ results[1].assert_state_at(conn_state_t::closed);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 1);
+ results[1].assert_reset(0, 0);
+ results[2].assert_state_at(conn_state_t::established);
+ results[2].assert_connect(0, 0, 0, 0);
+ results[2].assert_accept(1, 1, 0, 1);
+ results[2].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_client(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_lossless_client",
+ TestInterceptor(),
+ policy_t::lossless_client,
+ policy_t::stateful_server,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return suite.connect_peer();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 1 --");
+ logger().info("[Test] client markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(1, 1, 0, 1);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 2 --");
+ logger().info("[Test] server markdown...");
+ return test.markdown_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(2, 2, 1, 2);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 1);
+ }).then([&suite] {
+ logger().info("-- 3 --");
+ logger().info("[Test] client reconnect...");
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(2, 2, 1, 2);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 1);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_stateful_server(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_stateful_server",
+ TestInterceptor(),
+ policy_t::stateful_server,
+ policy_t::lossless_client,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 1 --");
+ logger().info("[Test] client markdown...");
+ return test.markdown_peer();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 1);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 2 --");
+ logger().info("[Test] server markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.wait_results(3);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 1);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ results[2].assert_state_at(conn_state_t::established);
+ results[2].assert_connect(0, 0, 0, 0);
+ results[2].assert_accept(1, 1, 1, 1);
+ results[2].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 3 --");
+ logger().info("[Test] client reconnect...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(3);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 1);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ results[2].assert_state_at(conn_state_t::established);
+ results[2].assert_connect(0, 0, 0, 0);
+ results[2].assert_accept(1, 1, 1, 1);
+ results[2].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_peer_reuse_connector(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_peer_reuse_connector",
+ TestInterceptor(),
+ policy_t::lossless_peer_reuse,
+ policy_t::lossless_peer_reuse,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return suite.connect_peer();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 1 --");
+ logger().info("[Test] connector markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(1, 1, 0, 1);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 2 --");
+ logger().info("[Test] acceptor markdown...");
+ return test.markdown_peer();
+ }).then([&suite] {
+ ceph_assert(suite.is_standby());
+ logger().info("-- 3 --");
+ logger().info("[Test] connector reconnect...");
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.try_send_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(2, 2, 1, 2);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 1);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_peer_reuse_acceptor(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_peer_reuse_acceptor",
+ TestInterceptor(),
+ policy_t::lossless_peer_reuse,
+ policy_t::lossless_peer_reuse,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 1 --");
+ logger().info("[Test] connector markdown...");
+ return test.markdown_peer();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 1);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 2 --");
+ logger().info("[Test] acceptor markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 1);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 3 --");
+ logger().info("[Test] connector reconnect...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.try_peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(3);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 1);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ results[2].assert_state_at(conn_state_t::established);
+ results[2].assert_connect(0, 0, 0, 0);
+ results[2].assert_accept(1, 1, 1, 1);
+ results[2].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_peer_connector(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_lossless_peer_connector",
+ TestInterceptor(),
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&suite] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return suite.connect_peer();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 1 --");
+ logger().info("[Test] connector markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.send_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(1, 1, 0, 1);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 2 --");
+ logger().info("[Test] acceptor markdown...");
+ return test.markdown_peer();
+ }).then([&suite] {
+ ceph_assert(suite.is_standby());
+ logger().info("-- 3 --");
+ logger().info("[Test] connector reconnect...");
+ return suite.connect_peer();
+ }).then([&suite] {
+ return suite.try_send_peer();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(1, 1, 0, 1);
+ results[0].assert_accept(0, 0, 0, 0);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::established);
+ results[1].assert_connect(2, 2, 1, 2);
+ results[1].assert_accept(0, 0, 0, 0);
+ results[1].assert_reset(0, 1);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_lossless_peer_acceptor(FailoverTest& test) {
+ return test.run_suite(
+ "test_v2_lossless_peer_acceptor",
+ TestInterceptor(),
+ policy_t::lossless_peer,
+ policy_t::lossless_peer,
+ [&test] (FailoverSuite& suite) {
+ return seastar::futurize_invoke([&test] {
+ logger().info("-- 0 --");
+ logger().info("[Test] setup connection...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.send_bidirectional();
+ }).then([&suite] {
+ return suite.wait_results(1);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 1);
+ results[0].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 1 --");
+ logger().info("[Test] connector markdown...");
+ return test.markdown_peer();
+ }).then([&test] {
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::established);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&suite] {
+ logger().info("-- 2 --");
+ logger().info("[Test] acceptor markdown...");
+ return suite.markdown();
+ }).then([&suite] {
+ return suite.wait_results(2);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ }).then([&test] {
+ logger().info("-- 3 --");
+ logger().info("[Test] connector reconnect...");
+ return test.peer_connect_me();
+ }).then([&test] {
+ return test.try_peer_send_me();
+ }).then([&suite] {
+ return suite.wait_results(3);
+ }).then([] (ConnResults& results) {
+ results[0].assert_state_at(conn_state_t::closed);
+ results[0].assert_connect(0, 0, 0, 0);
+ results[0].assert_accept(1, 1, 0, 2);
+ results[0].assert_reset(0, 0);
+ results[1].assert_state_at(conn_state_t::replaced);
+ results[1].assert_connect(0, 0, 0, 0);
+ results[1].assert_accept(1, 1, 0, 0);
+ results[1].assert_reset(0, 0);
+ results[2].assert_state_at(conn_state_t::established);
+ results[2].assert_connect(0, 0, 0, 0);
+ results[2].assert_accept(1, 1, 1, 1);
+ results[2].assert_reset(0, 0);
+ });
+ });
+}
+
+seastar::future<>
+test_v2_protocol(entity_addr_t test_addr,
+ entity_addr_t cmd_peer_addr,
+ entity_addr_t test_peer_addr,
+ bool test_peer_islocal,
+ bool peer_wins) {
+ ceph_assert_always(test_addr.is_msgr2());
+ ceph_assert_always(cmd_peer_addr.is_msgr2());
+ ceph_assert_always(test_peer_addr.is_msgr2());
+
+ if (test_peer_islocal) {
+ // initiate crimson test peer locally
+ logger().info("test_v2_protocol: start local TestPeer at {}...", cmd_peer_addr);
+ return FailoverTestPeer::create(cmd_peer_addr, test_peer_addr
+ ).then([test_addr, cmd_peer_addr, test_peer_addr, peer_wins](auto peer) {
+ return test_v2_protocol(
+ test_addr,
+ cmd_peer_addr,
+ test_peer_addr,
+ false,
+ peer_wins
+ ).then([peer = std::move(peer)] () mutable {
+ return peer->wait().then([peer = std::move(peer)] {});
+ });
+ }).handle_exception([] (auto eptr) {
+ logger().error("FailoverTestPeer failed: got exception {}", eptr);
+ throw;
+ });
+ }
+
+ return FailoverTest::create(test_addr, cmd_peer_addr, test_peer_addr
+ ).then([peer_wins](auto test) {
+ return seastar::futurize_invoke([test] {
+ return test_v2_lossy_early_connect_fault(*test);
+ }).then([test] {
+ return test_v2_lossy_connect_fault(*test);
+ }).then([test] {
+ return test_v2_lossy_connected_fault(*test);
+ }).then([test] {
+ return test_v2_lossy_early_accept_fault(*test);
+ }).then([test] {
+ return test_v2_lossy_accept_fault(*test);
+ }).then([test] {
+ return test_v2_lossy_establishing_fault(*test);
+ }).then([test] {
+ return test_v2_lossy_accepted_fault(*test);
+ }).then([test] {
+ return test_v2_lossless_connect_fault(*test);
+ }).then([test] {
+ return test_v2_lossless_connected_fault(*test);
+ }).then([test] {
+ return test_v2_lossless_connected_fault2(*test);
+ }).then([test] {
+ return test_v2_lossless_reconnect_fault(*test);
+ }).then([test] {
+ return test_v2_lossless_accept_fault(*test);
+ }).then([test] {
+ return test_v2_lossless_establishing_fault(*test);
+ }).then([test] {
+ return test_v2_lossless_accepted_fault(*test);
+ }).then([test] {
+ return test_v2_lossless_reaccept_fault(*test);
+ }).then([test] {
+ return test_v2_peer_connect_fault(*test);
+ }).then([test] {
+ return test_v2_peer_accept_fault(*test);
+ }).then([test] {
+ return test_v2_peer_establishing_fault(*test);
+ }).then([test] {
+ return test_v2_peer_connected_fault_reconnect(*test);
+ }).then([test] {
+ return test_v2_peer_connected_fault_reaccept(*test);
+ }).then([test] {
+ return check_peer_wins(*test);
+ }).then([test, peer_wins](bool ret_peer_wins) {
+ ceph_assert(peer_wins == ret_peer_wins);
+ if (ret_peer_wins) {
+ return seastar::futurize_invoke([test] {
+ return test_v2_racing_connect_acceptor_win(*test);
+ }).then([test] {
+ return test_v2_racing_reconnect_acceptor_win(*test);
+ });
+ } else {
+ return seastar::futurize_invoke([test] {
+ return test_v2_racing_connect_acceptor_lose(*test);
+ }).then([test] {
+ return test_v2_racing_reconnect_acceptor_lose(*test);
+ });
+ }
+ }).then([test] {
+ return test_v2_racing_connect_reconnect_win(*test);
+ }).then([test] {
+ return test_v2_racing_connect_reconnect_lose(*test);
+ }).then([test] {
+ return test_v2_stale_connect(*test);
+ }).then([test] {
+ return test_v2_stale_reconnect(*test);
+ }).then([test] {
+ return test_v2_stale_accept(*test);
+ }).then([test] {
+ return test_v2_stale_establishing(*test);
+ }).then([test] {
+ return test_v2_stale_reaccept(*test);
+ }).then([test] {
+ return test_v2_lossy_client(*test);
+ }).then([test] {
+ return test_v2_stateless_server(*test);
+ }).then([test] {
+ return test_v2_lossless_client(*test);
+ }).then([test] {
+ return test_v2_stateful_server(*test);
+ }).then([test] {
+ return test_v2_peer_reuse_connector(*test);
+ }).then([test] {
+ return test_v2_peer_reuse_acceptor(*test);
+ }).then([test] {
+ return test_v2_lossless_peer_connector(*test);
+ }).then([test] {
+ return test_v2_lossless_peer_acceptor(*test);
+ }).then([test] {
+ return test->shutdown().then([test] {});
+ });
+ }).handle_exception([] (auto eptr) {
+ logger().error("FailoverTest failed: got exception {}", eptr);
+ throw;
+ });
+}
+
+}
+
+seastar::future<int> do_test(seastar::app_template& app)
+{
+ std::vector<const char*> args;
+ std::string cluster;
+ std::string conf_file_list;
+ auto init_params = ceph_argparse_early_args(args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ &cluster,
+ &conf_file_list);
+ return crimson::common::sharded_conf().start(
+ init_params.name, cluster
+ ).then([] {
+ return local_conf().start();
+ }).then([conf_file_list] {
+ return local_conf().parse_config_files(conf_file_list);
+ }).then([&app] {
+ auto&& config = app.configuration();
+ verbose = config["verbose"].as<bool>();
+ auto rounds = config["rounds"].as<unsigned>();
+ auto keepalive_ratio = config["keepalive-ratio"].as<double>();
+ auto testpeer_islocal = config["testpeer-islocal"].as<bool>();
+
+ entity_addr_t test_addr;
+ ceph_assert(test_addr.parse(
+ config["test-addr"].as<std::string>().c_str(), nullptr));
+ test_addr.set_nonce(TEST_NONCE);
+
+ entity_addr_t cmd_peer_addr;
+ ceph_assert(cmd_peer_addr.parse(
+ config["testpeer-addr"].as<std::string>().c_str(), nullptr));
+ cmd_peer_addr.set_nonce(CMD_SRV_NONCE);
+
+ entity_addr_t test_peer_addr = get_test_peer_addr(cmd_peer_addr);
+ bool peer_wins = (test_addr > test_peer_addr);
+
+ logger().info("test configuration: verbose={}, rounds={}, keepalive_ratio={}, "
+ "test_addr={}, cmd_peer_addr={}, test_peer_addr={}, "
+ "testpeer_islocal={}, peer_wins={}, smp={}",
+ verbose, rounds, keepalive_ratio,
+ test_addr, cmd_peer_addr, test_peer_addr,
+ testpeer_islocal, peer_wins,
+ seastar::smp::count);
+ return test_echo(rounds, keepalive_ratio
+ ).then([] {
+ return test_preemptive_shutdown();
+ }).then([test_addr, cmd_peer_addr, test_peer_addr, testpeer_islocal, peer_wins] {
+ return test_v2_protocol(
+ test_addr,
+ cmd_peer_addr,
+ test_peer_addr,
+ testpeer_islocal,
+ peer_wins);
+ }).then([] {
+ logger().info("All tests succeeded");
+ // Seastar has bugs to have events undispatched during shutdown,
+ // which will result in memory leak and thus fail LeakSanitizer.
+ return seastar::sleep(100ms);
+ });
+ }).then([] {
+ return crimson::common::sharded_conf().stop();
+ }).then([] {
+ return 0;
+ }).handle_exception([] (auto eptr) {
+ logger().error("Test failed: got exception {}", eptr);
+ return 1;
+ });
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ app.add_options()
+ ("verbose,v", bpo::value<bool>()->default_value(false),
+ "chatty if true")
+ ("rounds", bpo::value<unsigned>()->default_value(512),
+ "number of pingpong rounds")
+ ("keepalive-ratio", bpo::value<double>()->default_value(0.1),
+ "ratio of keepalive in ping messages")
+ ("test-addr", bpo::value<std::string>()->default_value("v2:127.0.0.1:9014"),
+ "address of v2 failover tests")
+ ("testpeer-addr", bpo::value<std::string>()->default_value("v2:127.0.0.1:9012"),
+ "addresses of v2 failover testpeer"
+ " (This is CmdSrv address, and TestPeer address is at port+=1)")
+ ("testpeer-islocal", bpo::value<bool>()->default_value(true),
+ "create a local crimson testpeer, or connect to a remote testpeer");
+ return app.run(argc, argv, [&app] {
+ // This test normally succeeds within 60 seconds, so kill it after 300
+ // seconds in case it is blocked forever due to unaddressed bugs.
+ return seastar::with_timeout(seastar::lowres_clock::now() + 300s, do_test(app))
+ .handle_exception_type([](seastar::timed_out_error&) {
+ logger().error("test_messenger timeout after 300s, abort! "
+ "Consider to extend the period if the test is still running.");
+ // use the retcode of timeout(1)
+ return 124;
+ });
+ });
+}
diff --git a/src/test/crimson/test_messenger.h b/src/test/crimson/test_messenger.h
new file mode 100644
index 000000000..635f7fae3
--- /dev/null
+++ b/src/test/crimson/test_messenger.h
@@ -0,0 +1,95 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include "msg/msg_types.h"
+
+namespace ceph::net::test {
+
+constexpr uint64_t CMD_CLI_NONCE = 1;
+constexpr int64_t CMD_CLI_OSD = 1;
+constexpr uint64_t TEST_NONCE = 2;
+constexpr int64_t TEST_OSD = 2;
+constexpr uint64_t CMD_SRV_NONCE = 3;
+constexpr int64_t CMD_SRV_OSD = 3;
+constexpr uint64_t TEST_PEER_NONCE = 2;
+constexpr int64_t TEST_PEER_OSD = 4;
+
+inline entity_addr_t get_test_peer_addr(
+ const entity_addr_t &cmd_peer_addr) {
+ entity_addr_t test_peer_addr = cmd_peer_addr;
+ test_peer_addr.set_port(cmd_peer_addr.get_port() + 1);
+ test_peer_addr.set_nonce(TEST_PEER_NONCE);
+ return test_peer_addr;
+}
+
+enum class cmd_t : char {
+ none = '\0',
+ shutdown,
+ suite_start,
+ suite_stop,
+ suite_connect_me,
+ suite_send_me,
+ suite_keepalive_me,
+ suite_markdown,
+ suite_recv_op
+};
+
+enum class policy_t : char {
+ none = '\0',
+ stateful_server,
+ stateless_server,
+ lossless_peer,
+ lossless_peer_reuse,
+ lossy_client,
+ lossless_client
+};
+
+inline std::ostream& operator<<(std::ostream& out, const cmd_t& cmd) {
+ switch(cmd) {
+ case cmd_t::none:
+ return out << "none";
+ case cmd_t::shutdown:
+ return out << "shutdown";
+ case cmd_t::suite_start:
+ return out << "suite_start";
+ case cmd_t::suite_stop:
+ return out << "suite_stop";
+ case cmd_t::suite_connect_me:
+ return out << "suite_connect_me";
+ case cmd_t::suite_send_me:
+ return out << "suite_send_me";
+ case cmd_t::suite_keepalive_me:
+ return out << "suite_keepalive_me";
+ case cmd_t::suite_markdown:
+ return out << "suite_markdown";
+ case cmd_t::suite_recv_op:
+ return out << "suite_recv_op";
+ default:
+ ceph_abort();
+ }
+}
+
+inline std::ostream& operator<<(std::ostream& out, const policy_t& policy) {
+ switch(policy) {
+ case policy_t::none:
+ return out << "none";
+ case policy_t::stateful_server:
+ return out << "stateful_server";
+ case policy_t::stateless_server:
+ return out << "stateless_server";
+ case policy_t::lossless_peer:
+ return out << "lossless_peer";
+ case policy_t::lossless_peer_reuse:
+ return out << "lossless_peer_reuse";
+ case policy_t::lossy_client:
+ return out << "lossy_client";
+ case policy_t::lossless_client:
+ return out << "lossless_client";
+ default:
+ ceph_abort();
+ }
+}
+
+} // namespace ceph::net::test
diff --git a/src/test/crimson/test_messenger_peer.cc b/src/test/crimson/test_messenger_peer.cc
new file mode 100644
index 000000000..28d8a3d38
--- /dev/null
+++ b/src/test/crimson/test_messenger_peer.cc
@@ -0,0 +1,462 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+
+#include <boost/pointer_cast.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+#include "auth/DummyAuth.h"
+#include "common/dout.h"
+#include "global/global_init.h"
+#include "messages/MPing.h"
+#include "messages/MCommand.h"
+#include "messages/MCommandReply.h"
+#include "messages/MOSDOp.h"
+#include "msg/Dispatcher.h"
+#include "msg/Messenger.h"
+
+#include "test_messenger.h"
+
+namespace {
+
+#define dout_subsys ceph_subsys_test
+
+using namespace ceph::net::test;
+using SocketPolicy = Messenger::Policy;
+
+constexpr int CEPH_OSD_PROTOCOL = 10;
+
+class FailoverSuitePeer : public Dispatcher {
+ using cb_t = std::function<void()>;
+ DummyAuthClientServer dummy_auth;
+ std::unique_ptr<Messenger> peer_msgr;
+ cb_t op_callback;
+
+ Connection* tracked_conn = nullptr;
+ unsigned pending_send = 0;
+
+ bool ms_can_fast_dispatch_any() const override { return true; }
+ bool ms_can_fast_dispatch(const Message* m) const override { return true; }
+ void ms_fast_dispatch(Message* m) override {
+ auto conn = m->get_connection().get();
+ if (tracked_conn == nullptr) {
+ ldout(cct, 0) << "[!TestPeer] got op from Test(conn "
+ << conn << "not tracked yet)" << dendl;
+ tracked_conn = conn;
+ } else if (tracked_conn != conn) {
+ lderr(cct) << "[TestPeer] got op from Test: conn(" << conn
+ << ") != tracked_conn(" << tracked_conn
+ << ")" << dendl;
+ ceph_abort();
+ } else {
+ ldout(cct, 0) << "[TestPeer] got op from Test" << dendl;
+ }
+ op_callback();
+ }
+ bool ms_dispatch(Message* m) override { ceph_abort(); }
+ void ms_handle_fast_connect(Connection* conn) override {
+ if (tracked_conn == conn) {
+ ldout(cct, 0) << "[TestPeer] connected: " << conn << dendl;
+ } else {
+ lderr(cct) << "[TestPeer] connected: conn(" << conn
+ << ") != tracked_conn(" << tracked_conn
+ << ")" << dendl;
+ ceph_abort();
+ }
+ }
+ void ms_handle_fast_accept(Connection* conn) override {
+ if (tracked_conn == nullptr) {
+ ldout(cct, 0) << "[TestPeer] accepted: " << conn << dendl;
+ tracked_conn = conn;
+ } else if (tracked_conn != conn) {
+ lderr(cct) << "[TestPeer] accepted: conn(" << conn
+ << ") != tracked_conn(" << tracked_conn
+ << ")" << dendl;
+ ceph_abort();
+ } else {
+ ldout(cct, 0) << "[!TestPeer] accepted(stale event): " << conn << dendl;
+ }
+ flush_pending_send();
+ }
+ bool ms_handle_reset(Connection* conn) override {
+ if (tracked_conn == conn) {
+ ldout(cct, 0) << "[TestPeer] reset: " << conn << dendl;
+ tracked_conn = nullptr;
+ } else {
+ ldout(cct, 0) << "[!TestPeer] reset(invalid event): conn(" << conn
+ << ") != tracked_conn(" << tracked_conn
+ << ")" << dendl;
+ }
+ return true;
+ }
+ void ms_handle_remote_reset(Connection* conn) override {
+ if (tracked_conn == conn) {
+ ldout(cct, 0) << "[TestPeer] remote reset: " << conn << dendl;
+ } else {
+ ldout(cct, 0) << "[!TestPeer] reset(invalid event): conn(" << conn
+ << ") != tracked_conn(" << tracked_conn
+ << ")" << dendl;
+ }
+ }
+ bool ms_handle_refused(Connection* conn) override {
+ ldout(cct, 0) << "[!TestPeer] refused: " << conn << dendl;
+ return true;
+ }
+
+ private:
+ void init(entity_addr_t test_peer_addr, SocketPolicy policy) {
+ peer_msgr.reset(Messenger::create(
+ cct, "async",
+ entity_name_t::OSD(TEST_PEER_OSD),
+ "TestPeer",
+ TEST_PEER_NONCE));
+ dummy_auth.auth_registry.refresh_config();
+ peer_msgr->set_cluster_protocol(CEPH_OSD_PROTOCOL);
+ peer_msgr->set_default_policy(policy);
+ peer_msgr->set_auth_client(&dummy_auth);
+ peer_msgr->set_auth_server(&dummy_auth);
+ peer_msgr->bind(test_peer_addr);
+ peer_msgr->add_dispatcher_head(this);
+ peer_msgr->start();
+ }
+
+ void send_op() {
+ ceph_assert(tracked_conn);
+ pg_t pgid;
+ object_locator_t oloc;
+ hobject_t hobj(object_t(), oloc.key, CEPH_NOSNAP, pgid.ps(),
+ pgid.pool(), oloc.nspace);
+ spg_t spgid(pgid);
+ tracked_conn->send_message2(make_message<MOSDOp>(0, 0, hobj, spgid, 0, 0, 0));
+ }
+
+ void flush_pending_send() {
+ if (pending_send != 0) {
+ ldout(cct, 0) << "[TestPeer] flush sending "
+ << pending_send << " ops" << dendl;
+ }
+ ceph_assert(tracked_conn);
+ while (pending_send) {
+ send_op();
+ --pending_send;
+ }
+ }
+
+ public:
+ FailoverSuitePeer(CephContext* cct, cb_t op_callback)
+ : Dispatcher(cct), dummy_auth(cct), op_callback(op_callback) { }
+
+ void shutdown() {
+ peer_msgr->shutdown();
+ peer_msgr->wait();
+ }
+
+ void connect_peer(entity_addr_t test_addr) {
+ ldout(cct, 0) << "[TestPeer] connect_peer(" << test_addr << ")" << dendl;
+ auto conn = peer_msgr->connect_to_osd(entity_addrvec_t{test_addr});
+ if (tracked_conn) {
+ if (tracked_conn == conn.get()) {
+ ldout(cct, 0) << "[TestPeer] this is not a new session " << conn.get() << dendl;
+ } else {
+ ldout(cct, 0) << "[TestPeer] this is a new session " << conn.get()
+ << ", replacing old one " << tracked_conn << dendl;
+ }
+ } else {
+ ldout(cct, 0) << "[TestPeer] this is a new session " << conn.get() << dendl;
+ }
+ tracked_conn = conn.get();
+ flush_pending_send();
+ }
+
+ void send_peer() {
+ if (tracked_conn) {
+ ldout(cct, 0) << "[TestPeer] send_peer()" << dendl;
+ send_op();
+ } else {
+ ++pending_send;
+ ldout(cct, 0) << "[TestPeer] send_peer() (pending " << pending_send << ")" << dendl;
+ }
+ }
+
+ void keepalive_peer() {
+ ldout(cct, 0) << "[TestPeer] keepalive_peer()" << dendl;
+ ceph_assert(tracked_conn);
+ tracked_conn->send_keepalive();
+ }
+
+ void markdown() {
+ ldout(cct, 0) << "[TestPeer] markdown()" << dendl;
+ ceph_assert(tracked_conn);
+ tracked_conn->mark_down();
+ tracked_conn = nullptr;
+ }
+
+ static std::unique_ptr<FailoverSuitePeer>
+ create(CephContext* cct, entity_addr_t test_peer_addr,
+ SocketPolicy policy, cb_t op_callback) {
+ auto suite = std::make_unique<FailoverSuitePeer>(cct, op_callback);
+ suite->init(test_peer_addr, policy);
+ return suite;
+ }
+};
+
+SocketPolicy to_socket_policy(CephContext* cct, policy_t policy) {
+ switch (policy) {
+ case policy_t::stateful_server:
+ return SocketPolicy::stateful_server(0);
+ case policy_t::stateless_server:
+ return SocketPolicy::stateless_server(0);
+ case policy_t::lossless_peer:
+ return SocketPolicy::lossless_peer(0);
+ case policy_t::lossless_peer_reuse:
+ return SocketPolicy::lossless_peer_reuse(0);
+ case policy_t::lossy_client:
+ return SocketPolicy::lossy_client(0);
+ case policy_t::lossless_client:
+ return SocketPolicy::lossless_client(0);
+ default:
+ lderr(cct) << "[CmdSrv] unexpected policy type" << dendl;
+ ceph_abort();
+ }
+}
+
+class FailoverTestPeer : public Dispatcher {
+ DummyAuthClientServer dummy_auth;
+ std::unique_ptr<Messenger> cmd_msgr;
+ Connection *cmd_conn = nullptr;
+ const entity_addr_t test_peer_addr;
+ std::unique_ptr<FailoverSuitePeer> test_suite;
+ const bool nonstop;
+
+ bool ms_can_fast_dispatch_any() const override { return false; }
+ bool ms_can_fast_dispatch(const Message* m) const override { return false; }
+ void ms_fast_dispatch(Message* m) override { ceph_abort(); }
+ bool ms_dispatch(Message* m) override {
+ auto conn = m->get_connection().get();
+ if (cmd_conn == nullptr) {
+ ldout(cct, 0) << "[!CmdSrv] got msg from CmdCli(conn "
+ << conn << "not tracked yet)" << dendl;
+ cmd_conn = conn;
+ } else if (cmd_conn != conn) {
+ lderr(cct) << "[CmdSrv] got msg from CmdCli: conn(" << conn
+ << ") != cmd_conn(" << cmd_conn
+ << ")" << dendl;
+ ceph_abort();
+ } else {
+ // good!
+ }
+ switch (m->get_type()) {
+ case CEPH_MSG_PING: {
+ ldout(cct, 0) << "[CmdSrv] got PING, sending PONG ..." << dendl;
+ cmd_conn->send_message2(make_message<MPing>());
+ break;
+ }
+ case MSG_COMMAND: {
+ auto m_cmd = boost::static_pointer_cast<MCommand>(m);
+ auto cmd = static_cast<cmd_t>(m_cmd->cmd[0][0]);
+ if (cmd == cmd_t::shutdown) {
+ ldout(cct, 0) << "All tests succeeded" << dendl;
+ if (!nonstop) {
+ ldout(cct, 0) << "[CmdSrv] shutdown ..." << dendl;
+ cmd_msgr->shutdown();
+ } else {
+ ldout(cct, 0) << "[CmdSrv] nonstop set ..." << dendl;
+ }
+ } else {
+ ldout(cct, 0) << "[CmdSrv] got cmd " << cmd << dendl;
+ handle_cmd(cmd, m_cmd);
+ ldout(cct, 0) << "[CmdSrv] done, send cmd reply ..." << dendl;
+ cmd_conn->send_message2(make_message<MCommandReply>());
+ }
+ break;
+ }
+ default:
+ lderr(cct) << "[CmdSrv] " << __func__ << " " << cmd_conn
+ << " got unexpected msg from CmdCli: "
+ << m << dendl;
+ ceph_abort();
+ }
+ m->put();
+ return true;
+ }
+ void ms_handle_fast_connect(Connection*) override { ceph_abort(); }
+ void ms_handle_fast_accept(Connection *conn) override {
+ if (cmd_conn == nullptr) {
+ ldout(cct, 0) << "[CmdSrv] accepted: " << conn << dendl;
+ cmd_conn = conn;
+ } else if (cmd_conn != conn) {
+ lderr(cct) << "[CmdSrv] accepted: conn(" << conn
+ << ") != cmd_conn(" << cmd_conn
+ << ")" << dendl;
+ ceph_abort();
+ } else {
+ ldout(cct, 0) << "[!CmdSrv] accepted(stale event): " << conn << dendl;
+ }
+ }
+ bool ms_handle_reset(Connection* conn) override {
+ if (cmd_conn == conn) {
+ ldout(cct, 0) << "[CmdSrv] reset: " << conn << dendl;
+ cmd_conn = nullptr;
+ } else {
+ ldout(cct, 0) << "[!CmdSrv] reset(invalid event): conn(" << conn
+ << ") != cmd_conn(" << cmd_conn
+ << ")" << dendl;
+ }
+ return true;
+ }
+ void ms_handle_remote_reset(Connection*) override { ceph_abort(); }
+ bool ms_handle_refused(Connection*) override { ceph_abort(); }
+
+ private:
+ void notify_recv_op() {
+ ceph_assert(cmd_conn);
+ auto m = make_message<MCommand>();
+ m->cmd.emplace_back(1, static_cast<char>(cmd_t::suite_recv_op));
+ cmd_conn->send_message2(m);
+ }
+
+ void handle_cmd(cmd_t cmd, MRef<MCommand> m_cmd) {
+ switch (cmd) {
+ case cmd_t::suite_start: {
+ if (test_suite) {
+ test_suite->shutdown();
+ test_suite.reset();
+ ldout(cct, 0) << "-------- suite stopped (force) --------\n\n" << dendl;
+ }
+ auto p = static_cast<policy_t>(m_cmd->cmd[1][0]);
+ ldout(cct, 0) << "[CmdSrv] suite starting (" << p
+ <<", " << test_peer_addr << ") ..." << dendl;
+ auto policy = to_socket_policy(cct, p);
+ auto suite = FailoverSuitePeer::create(cct, test_peer_addr, policy,
+ [this] { notify_recv_op(); });
+ test_suite.swap(suite);
+ return;
+ }
+ case cmd_t::suite_stop:
+ ceph_assert(test_suite);
+ test_suite->shutdown();
+ test_suite.reset();
+ ldout(cct, 0) << "-------- suite stopped --------\n\n" << dendl;
+ return;
+ case cmd_t::suite_connect_me: {
+ ceph_assert(test_suite);
+ entity_addr_t test_addr = entity_addr_t();
+ test_addr.parse(m_cmd->cmd[1].c_str(), nullptr);
+ test_suite->connect_peer(test_addr);
+ return;
+ }
+ case cmd_t::suite_send_me:
+ ceph_assert(test_suite);
+ test_suite->send_peer();
+ return;
+ case cmd_t::suite_keepalive_me:
+ ceph_assert(test_suite);
+ test_suite->keepalive_peer();
+ return;
+ case cmd_t::suite_markdown:
+ ceph_assert(test_suite);
+ test_suite->markdown();
+ return;
+ default:
+ lderr(cct) << "[CmdSrv] got unexpected command " << m_cmd
+ << " from CmdCli" << dendl;
+ ceph_abort();
+ }
+ }
+
+ void init(entity_addr_t cmd_peer_addr) {
+ cmd_msgr.reset(Messenger::create(
+ cct, "async",
+ entity_name_t::OSD(CMD_SRV_OSD),
+ "CmdSrv",
+ CMD_SRV_NONCE));
+ dummy_auth.auth_registry.refresh_config();
+ cmd_msgr->set_cluster_protocol(CEPH_OSD_PROTOCOL);
+ cmd_msgr->set_default_policy(Messenger::Policy::stateless_server(0));
+ cmd_msgr->set_auth_client(&dummy_auth);
+ cmd_msgr->set_auth_server(&dummy_auth);
+ cmd_msgr->bind(cmd_peer_addr);
+ cmd_msgr->add_dispatcher_head(this);
+ cmd_msgr->start();
+ }
+
+ public:
+ FailoverTestPeer(CephContext* cct,
+ entity_addr_t test_peer_addr,
+ bool nonstop)
+ : Dispatcher(cct),
+ dummy_auth(cct),
+ test_peer_addr(test_peer_addr),
+ nonstop(nonstop) { }
+
+ void wait() { cmd_msgr->wait(); }
+
+ static std::unique_ptr<FailoverTestPeer>
+ create(CephContext* cct,
+ entity_addr_t cmd_peer_addr,
+ entity_addr_t test_peer_addr,
+ bool nonstop) {
+ auto test_peer = std::make_unique<FailoverTestPeer>(
+ cct, test_peer_addr, nonstop);
+ test_peer->init(cmd_peer_addr);
+ ldout(cct, 0) << "[CmdSrv] ready" << dendl;
+ return test_peer;
+ }
+};
+
+}
+
+int main(int argc, char** argv)
+{
+ namespace po = boost::program_options;
+ po::options_description desc{"Allowed options"};
+ desc.add_options()
+ ("help,h", "show help message")
+ ("addr", po::value<std::string>()->default_value("v2:127.0.0.1:9012"),
+ "This is CmdSrv address, and TestPeer address is at port+=1")
+ ("nonstop", po::value<bool>()->default_value(false),
+ "Do not shutdown TestPeer when all tests are successful");
+ po::variables_map vm;
+ std::vector<std::string> unrecognized_options;
+ try {
+ auto parsed = po::command_line_parser(argc, argv)
+ .options(desc)
+ .allow_unregistered()
+ .run();
+ po::store(parsed, vm);
+ if (vm.count("help")) {
+ std::cout << desc << std::endl;
+ return 0;
+ }
+ po::notify(vm);
+ unrecognized_options = po::collect_unrecognized(parsed.options, po::include_positional);
+ } catch(const po::error& e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ std::vector<const char*> args(argv, argv + argc);
+ auto cct = global_init(nullptr, args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+ common_init_finish(cct.get());
+
+ auto addr = vm["addr"].as<std::string>();
+ entity_addr_t cmd_peer_addr;
+ cmd_peer_addr.parse(addr.c_str(), nullptr);
+ cmd_peer_addr.set_nonce(CMD_SRV_NONCE);
+ ceph_assert_always(cmd_peer_addr.is_msgr2());
+ auto test_peer_addr = get_test_peer_addr(cmd_peer_addr);
+ auto nonstop = vm["nonstop"].as<bool>();
+ ldout(cct, 0) << "test configuration: cmd_peer_addr=" << cmd_peer_addr
+ << ", test_peer_addr=" << test_peer_addr
+ << ", nonstop=" << nonstop
+ << dendl;
+
+ auto test_peer = FailoverTestPeer::create(
+ cct.get(),
+ cmd_peer_addr,
+ test_peer_addr,
+ nonstop);
+ test_peer->wait();
+}
diff --git a/src/test/crimson/test_messenger_thrash.cc b/src/test/crimson/test_messenger_thrash.cc
new file mode 100644
index 000000000..f2b1828f1
--- /dev/null
+++ b/src/test/crimson/test_messenger_thrash.cc
@@ -0,0 +1,672 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <map>
+#include <random>
+#include <fmt/format.h>
+#include <fmt/ostream.h>
+#include <seastar/core/app-template.hh>
+#include <seastar/core/do_with.hh>
+#include <seastar/core/future-util.hh>
+#include <seastar/core/reactor.hh>
+#include <seastar/core/sleep.hh>
+#include <seastar/core/with_timeout.hh>
+
+#include "common/ceph_argparse.h"
+#include "messages/MPing.h"
+#include "messages/MCommand.h"
+#include "crimson/auth/DummyAuth.h"
+#include "crimson/common/log.h"
+#include "crimson/net/Connection.h"
+#include "crimson/net/Dispatcher.h"
+#include "crimson/net/Messenger.h"
+
+using namespace std::chrono_literals;
+namespace bpo = boost::program_options;
+using crimson::common::local_conf;
+using payload_seq_t = uint64_t;
+
+struct Payload {
+ enum Who : uint8_t {
+ PING = 0,
+ PONG = 1,
+ };
+ uint8_t who = 0;
+ payload_seq_t seq = 0;
+ bufferlist data;
+
+ Payload(Who who, uint64_t seq, const bufferlist& data)
+ : who(who), seq(seq), data(data)
+ {}
+ Payload() = default;
+ DENC(Payload, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.who, p);
+ denc(v.seq, p);
+ denc(v.data, p);
+ DENC_FINISH(p);
+ }
+};
+WRITE_CLASS_DENC(Payload)
+
+template<>
+struct fmt::formatter<Payload> : fmt::formatter<std::string_view> {
+ template <typename FormatContext>
+ auto format(const Payload& pl, FormatContext& ctx) const {
+ return fmt::format_to(ctx.out(), "reply={} i={}", pl.who, pl.seq);
+ }
+};
+
+namespace {
+
+seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_test);
+}
+
+std::random_device rd;
+std::default_random_engine rng{rd()};
+std::uniform_int_distribution<> prob(0,99);
+bool verbose = false;
+
+entity_addr_t get_server_addr() {
+ static int port = 16800;
+ ++port;
+ entity_addr_t saddr;
+ saddr.parse("127.0.0.1", nullptr);
+ saddr.set_port(port);
+ return saddr;
+}
+
+uint64_t get_nonce() {
+ static uint64_t nonce = 1;
+ ++nonce;
+ return nonce;
+}
+
+struct thrash_params_t {
+ std::size_t servers;
+ std::size_t clients;
+ std::size_t connections;
+ std::size_t random_op;
+};
+
+class SyntheticWorkload;
+
+class SyntheticDispatcher final
+ : public crimson::net::Dispatcher {
+ public:
+ std::map<crimson::net::Connection*, std::deque<payload_seq_t> > conn_sent;
+ std::map<payload_seq_t, bufferlist> sent;
+ unsigned index;
+ SyntheticWorkload *workload;
+
+ SyntheticDispatcher(bool s, SyntheticWorkload *wl):
+ index(0), workload(wl) {
+ }
+
+ std::optional<seastar::future<>> ms_dispatch(crimson::net::ConnectionRef con,
+ MessageRef m) final {
+ if (verbose) {
+ logger().warn("{}: con = {}", __func__, *con);
+ }
+ // MSG_COMMAND is used to disorganize regular message flow
+ if (m->get_type() == MSG_COMMAND) {
+ return seastar::now();
+ }
+
+ Payload pl;
+ auto p = m->get_data().cbegin();
+ decode(pl, p);
+ if (pl.who == Payload::PING) {
+ logger().info(" {} conn= {} {}", __func__, *con, pl);
+ return reply_message(m, con, pl);
+ } else {
+ ceph_assert(pl.who == Payload::PONG);
+ if (sent.count(pl.seq)) {
+ logger().info(" {} conn= {} {}", __func__, *con, pl);
+ ceph_assert(conn_sent[&*con].front() == pl.seq);
+ ceph_assert(pl.data.contents_equal(sent[pl.seq]));
+ conn_sent[&*con].pop_front();
+ sent.erase(pl.seq);
+ }
+
+ return seastar::now();
+ }
+ }
+
+ void ms_handle_accept(
+ crimson::net::ConnectionRef conn,
+ seastar::shard_id prv_shard,
+ bool is_replace) final {
+ logger().info("{} - Connection:{}", __func__, *conn);
+ assert(prv_shard == seastar::this_shard_id());
+ }
+
+ void ms_handle_connect(
+ crimson::net::ConnectionRef conn,
+ seastar::shard_id prv_shard) final {
+ logger().info("{} - Connection:{}", __func__, *conn);
+ assert(prv_shard == seastar::this_shard_id());
+ }
+
+ void ms_handle_reset(crimson::net::ConnectionRef con, bool is_replace) final;
+
+ void ms_handle_remote_reset(crimson::net::ConnectionRef con) final {
+ clear_pending(con);
+ }
+
+ std::optional<seastar::future<>> reply_message(
+ const MessageRef m,
+ crimson::net::ConnectionRef con,
+ Payload& pl) {
+ pl.who = Payload::PONG;
+ bufferlist bl;
+ encode(pl, bl);
+ auto rm = crimson::make_message<MPing>();
+ rm->set_data(bl);
+ if (verbose) {
+ logger().info("{} conn= {} reply i= {}",
+ __func__, *con, pl.seq);
+ }
+ return con->send(std::move(rm));
+ }
+
+ seastar::future<> send_message_wrap(crimson::net::ConnectionRef con,
+ const bufferlist& data) {
+ auto m = crimson::make_message<MPing>();
+ Payload pl{Payload::PING, index++, data};
+ bufferlist bl;
+ encode(pl, bl);
+ m->set_data(bl);
+ sent[pl.seq] = pl.data;
+ conn_sent[&*con].push_back(pl.seq);
+ logger().info("{} conn= {} send i= {}",
+ __func__, *con, pl.seq);
+
+ return con->send(std::move(m));
+ }
+
+ uint64_t get_num_pending_msgs() {
+ return sent.size();
+ }
+
+ void clear_pending(crimson::net::ConnectionRef con) {
+ for (std::deque<uint64_t>::iterator it = conn_sent[&*con].begin();
+ it != conn_sent[&*con].end(); ++it)
+ sent.erase(*it);
+ conn_sent.erase(&*con);
+ }
+
+ void print() {
+ for (auto && [connptr, list] : conn_sent) {
+ if (!list.empty()) {
+ logger().info("{} {} wait {}", __func__,
+ (void*)connptr, list.size());
+ }
+ }
+ }
+};
+
+class SyntheticWorkload {
+ // messengers must be freed after its connections
+ std::set<crimson::net::MessengerRef> available_servers;
+ std::set<crimson::net::MessengerRef> available_clients;
+
+ crimson::net::SocketPolicy server_policy;
+ crimson::net::SocketPolicy client_policy;
+ std::map<crimson::net::ConnectionRef,
+ std::pair<crimson::net::MessengerRef,
+ crimson::net::MessengerRef>> available_connections;
+ SyntheticDispatcher dispatcher;
+ std::vector<bufferlist> rand_data;
+ crimson::auth::DummyAuthClientServer dummy_auth;
+
+ seastar::future<crimson::net::ConnectionRef> get_random_connection() {
+ return seastar::do_until(
+ [this] { return dispatcher.get_num_pending_msgs() <= max_in_flight; },
+ [] { return seastar::sleep(100ms); }
+ ).then([this] {
+ boost::uniform_int<> choose(0, available_connections.size() - 1);
+ int index = choose(rng);
+ std::map<crimson::net::ConnectionRef,
+ std::pair<crimson::net::MessengerRef, crimson::net::MessengerRef>>::iterator i
+ = available_connections.begin();
+ for (; index > 0; --index, ++i) ;
+ return seastar::make_ready_future<crimson::net::ConnectionRef>(i->first);
+ });
+ }
+
+ public:
+ const unsigned min_connections = 10;
+ const unsigned max_in_flight = 64;
+ const unsigned max_connections = 128;
+ const unsigned max_message_len = 1024 * 1024 * 4;
+ const uint64_t servers, clients;
+
+ SyntheticWorkload(int servers, int clients, int random_num,
+ crimson::net::SocketPolicy srv_policy,
+ crimson::net::SocketPolicy cli_policy)
+ : server_policy(srv_policy),
+ client_policy(cli_policy),
+ dispatcher(false, this),
+ servers(servers),
+ clients(clients) {
+
+ for (int i = 0; i < random_num; i++) {
+ bufferlist bl;
+ boost::uniform_int<> u(32, max_message_len);
+ uint64_t value_len = u(rng);
+ bufferptr bp(value_len);
+ bp.zero();
+ for (uint64_t j = 0; j < value_len-sizeof(i); ) {
+ memcpy(bp.c_str()+j, &i, sizeof(i));
+ j += 4096;
+ }
+
+ bl.append(bp);
+ rand_data.push_back(bl);
+ }
+ }
+
+
+ bool can_create_connection() {
+ return available_connections.size() < max_connections;
+ }
+
+ seastar::future<> maybe_generate_connection() {
+ if (!can_create_connection()) {
+ return seastar::now();
+ }
+ crimson::net::MessengerRef server, client;
+ {
+ boost::uniform_int<> choose(0, available_servers.size() - 1);
+ int index = choose(rng);
+ std::set<crimson::net::MessengerRef>::iterator i
+ = available_servers.begin();
+ for (; index > 0; --index, ++i) ;
+ server = *i;
+ }
+ {
+ boost::uniform_int<> choose(0, available_clients.size() - 1);
+ int index = choose(rng);
+ std::set<crimson::net::MessengerRef>::iterator i
+ = available_clients.begin();
+ for (; index > 0; --index, ++i) ;
+ client = *i;
+ }
+
+
+ std::pair<crimson::net::MessengerRef, crimson::net::MessengerRef>
+ connected_pair;
+ {
+ crimson::net::ConnectionRef conn = client->connect(
+ server->get_myaddr(),
+ entity_name_t::TYPE_OSD);
+ connected_pair = std::make_pair(client, server);
+ available_connections[conn] = connected_pair;
+ }
+ return seastar::now();
+ }
+
+ seastar::future<> random_op (const uint64_t& iter) {
+ return seastar::do_with(iter, [this] (uint64_t& iter) {
+ return seastar::do_until(
+ [&] { return iter == 0; },
+ [&, this]
+ {
+ if (!(iter % 10)) {
+ logger().info("{} Op {} : ", __func__ ,iter);
+ print_internal_state();
+ }
+ --iter;
+ int val = prob(rng);
+ if(val > 90) {
+ return maybe_generate_connection();
+ } else if (val > 80) {
+ return drop_connection();
+ } else if (val > 10) {
+ return send_message();
+ } else {
+ return seastar::sleep(
+ std::chrono::milliseconds(rand() % 1000 + 500));
+ }
+ });
+ });
+ }
+
+ seastar::future<> generate_connections (const uint64_t& iter) {
+ return seastar::do_with(iter, [this] (uint64_t& iter) {
+ return seastar::do_until(
+ [&] { return iter == 0; },
+ [&, this]
+ {
+ --iter;
+ if (!(connections_count() % 10)) {
+ logger().info("seeding connection {}",
+ connections_count());
+ }
+ return maybe_generate_connection();
+ });
+ });
+ }
+
+ seastar::future<> init_server(const entity_name_t& name,
+ const std::string& lname,
+ const uint64_t nonce,
+ const entity_addr_t& addr) {
+ crimson::net::MessengerRef msgr =
+ crimson::net::Messenger::create(
+ name, lname, nonce, true);
+ msgr->set_default_policy(server_policy);
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ available_servers.insert(msgr);
+ return msgr->bind(entity_addrvec_t{addr}).safe_then(
+ [this, msgr] {
+ return msgr->start({&dispatcher});
+ }, crimson::net::Messenger::bind_ertr::all_same_way(
+ [addr] (const std::error_code& e) {
+ logger().error("{} test_messenger_thrash(): "
+ "there is another instance running at {}",
+ __func__, addr);
+ ceph_abort();
+ }));
+ }
+
+ seastar::future<> init_client(const entity_name_t& name,
+ const std::string& lname,
+ const uint64_t nonce) {
+ crimson::net::MessengerRef msgr =
+ crimson::net::Messenger::create(
+ name, lname, nonce, true);
+ msgr->set_default_policy(client_policy);
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+ available_clients.insert(msgr);
+ return msgr->start({&dispatcher});
+ }
+
+ seastar::future<> send_message() {
+ return get_random_connection()
+ .then([this] (crimson::net::ConnectionRef conn) {
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val >= 95) {
+ uuid_d uuid;
+ uuid.generate_random();
+ auto m = crimson::make_message<MCommand>(uuid);
+ std::vector<std::string> cmds;
+ cmds.push_back("command");
+ m->cmd = cmds;
+ m->set_priority(200);
+ return conn->send(std::move(m));
+ } else {
+ boost::uniform_int<> u(0, rand_data.size()-1);
+ return dispatcher.send_message_wrap(conn, rand_data[u(rng)]);
+ }
+ });
+ }
+
+ seastar::future<> drop_connection() {
+ if (available_connections.size() < min_connections) {
+ return seastar::now();
+ }
+
+ return get_random_connection()
+ .then([this] (crimson::net::ConnectionRef conn) {
+ dispatcher.clear_pending(conn);
+ conn->mark_down();
+ if (!client_policy.server &&
+ client_policy.standby) {
+ // it's a lossless policy, so we need to mark down each side
+ std::pair<crimson::net::MessengerRef, crimson::net::MessengerRef> &p =
+ available_connections[conn];
+ if (!p.first->get_default_policy().server &&
+ !p.second->get_default_policy().server) {
+ //verify that equal-to operator applies here
+ ceph_assert(p.first->owns_connection(*conn));
+ crimson::net::ConnectionRef peer = p.second->connect(
+ p.first->get_myaddr(), p.first->get_mytype());
+ peer->mark_down();
+ dispatcher.clear_pending(peer);
+ available_connections.erase(peer);
+ }
+ }
+ ceph_assert(available_connections.erase(conn) == 1U);
+ return seastar::now();
+ });
+ }
+
+ void print_internal_state(bool detail=false) {
+ logger().info("available_connections: {} inflight messages: {}",
+ available_connections.size(),
+ dispatcher.get_num_pending_msgs());
+ if (detail && !available_connections.empty()) {
+ dispatcher.print();
+ }
+ }
+
+ seastar::future<> wait_for_done() {
+ int i = 0;
+ return seastar::do_until(
+ [this] { return !dispatcher.get_num_pending_msgs(); },
+ [this, &i]
+ {
+ if (i++ % 50 == 0){
+ print_internal_state(true);
+ }
+ return seastar::sleep(100ms);
+ }).then([this] {
+ return seastar::do_for_each(available_servers, [] (auto server) {
+ if (verbose) {
+ logger().info("server {} shutdown" , server->get_myaddrs());
+ }
+ server->stop();
+ return server->shutdown();
+ });
+ }).then([this] {
+ return seastar::do_for_each(available_clients, [] (auto client) {
+ if (verbose) {
+ logger().info("client {} shutdown" , client->get_myaddrs());
+ }
+ client->stop();
+ return client->shutdown();
+ });
+ });
+ }
+
+ void handle_reset(crimson::net::ConnectionRef con) {
+ available_connections.erase(con);
+ }
+
+ uint64_t servers_count() {
+ return available_servers.size();
+ }
+
+ uint64_t clients_count() {
+ return available_clients.size();
+ }
+
+ uint64_t connections_count() {
+ return available_connections.size();
+ }
+};
+
+void SyntheticDispatcher::ms_handle_reset(crimson::net::ConnectionRef con,
+ bool is_replace) {
+ workload->handle_reset(con);
+ clear_pending(con);
+}
+
+seastar::future<> reset_conf() {
+ return seastar::when_all_succeed(
+ local_conf().set_val("ms_inject_socket_failures", "0"),
+ local_conf().set_val("ms_inject_internal_delays", "0"),
+ local_conf().set_val("ms_inject_delay_probability", "0"),
+ local_conf().set_val("ms_inject_delay_max", "0")
+ ).then_unpack([] {
+ return seastar::now();
+ });
+}
+
+// Testing Crimson messenger (with msgr-v2 protocol) robustness against
+// network delays and failures. The test includes stress tests and
+// socket level delays/failures injection tests, letting time
+// and randomness achieve the best test coverage.
+
+// Test Parameters:
+// Clients: 8 (stateful)
+// Servers: 32 (lossless)
+// Connections: 100 (Generated between random clients/server)
+// Random Operations: 120 (Generate/Drop Connection, Send Message, Sleep)
+seastar::future<> test_stress(thrash_params_t tp)
+{
+
+ logger().info("test_stress():");
+
+ SyntheticWorkload test_msg(tp.servers, tp.clients, 100,
+ crimson::net::SocketPolicy::stateful_server(0),
+ crimson::net::SocketPolicy::lossless_client(0));
+
+ return seastar::do_with(test_msg, [tp]
+ (SyntheticWorkload& test_msg) {
+ return seastar::do_until([&test_msg] {
+ return test_msg.servers_count() == test_msg.servers; },
+ [&test_msg] {
+ entity_addr_t bind_addr = get_server_addr();
+ bind_addr.set_type(entity_addr_t::TYPE_MSGR2);
+ uint64_t server_num = get_nonce();
+ return test_msg.init_server(entity_name_t::OSD(server_num),
+ "server", server_num , bind_addr);
+ }).then([&test_msg] {
+ return seastar::do_until([&test_msg] {
+ return test_msg.clients_count() == test_msg.clients; },
+ [&test_msg] {
+ return test_msg.init_client(entity_name_t::CLIENT(-1),
+ "client", get_nonce());
+ });
+ }).then([&test_msg, tp] {
+ return test_msg.generate_connections(tp.connections);
+ }).then([&test_msg, tp] {
+ return test_msg.random_op(tp.random_op);
+ }).then([&test_msg] {
+ return test_msg.wait_for_done();
+ }).then([] {
+ logger().info("test_stress() DONE");
+ }).handle_exception([] (auto eptr) {
+ logger().error(
+ "test_stress() failed: got exception {}",
+ eptr);
+ throw;
+ });
+ });
+}
+
+// Test Parameters:
+// Clients: 8 (statefull)
+// Servers: 32 (loseless)
+// Connections: 100 (Generated between random clients/server)
+// Random Operations: 120 (Generate/Drop Connection, Send Message, Sleep)
+seastar::future<> test_injection(thrash_params_t tp)
+{
+
+ logger().info("test_injection():");
+
+ SyntheticWorkload test_msg(tp.servers, tp.clients, 100,
+ crimson::net::SocketPolicy::stateful_server(0),
+ crimson::net::SocketPolicy::lossless_client(0));
+
+ return seastar::do_with(test_msg, [tp]
+ (SyntheticWorkload& test_msg) {
+ return seastar::do_until([&test_msg] {
+ return test_msg.servers_count() == test_msg.servers; },
+ [&test_msg] {
+ entity_addr_t bind_addr = get_server_addr();
+ bind_addr.set_type(entity_addr_t::TYPE_MSGR2);
+ uint64_t server_num = get_nonce();
+ return test_msg.init_server(entity_name_t::OSD(server_num),
+ "server", server_num , bind_addr);
+ }).then([&test_msg] {
+ return seastar::do_until([&test_msg] {
+ return test_msg.clients_count() == test_msg.clients; },
+ [&test_msg] {
+ return test_msg.init_client(entity_name_t::CLIENT(-1),
+ "client", get_nonce());
+ });
+ }).then([] {
+ return seastar::when_all_succeed(
+ local_conf().set_val("ms_inject_socket_failures", "30"),
+ local_conf().set_val("ms_inject_internal_delays", "0.1"),
+ local_conf().set_val("ms_inject_delay_probability", "1"),
+ local_conf().set_val("ms_inject_delay_max", "5"));
+ }).then_unpack([] {
+ return seastar::now();
+ }).then([&test_msg, tp] {
+ return test_msg.generate_connections(tp.connections);
+ }).then([&test_msg, tp] {
+ return test_msg.random_op(tp.random_op);
+ }).then([&test_msg] {
+ return test_msg.wait_for_done();
+ }).then([] {
+ logger().info("test_inejction() DONE");
+ return seastar::now();
+ }).then([] {
+ return reset_conf();
+ }).handle_exception([] (auto eptr) {
+ logger().error(
+ "test_injection() failed: got exception {}",
+ eptr);
+ throw;
+ });
+ });
+}
+
+}
+
+seastar::future<int> do_test(seastar::app_template& app)
+{
+ std::vector<const char*> args;
+ std::string cluster;
+ std::string conf_file_list;
+ auto init_params = ceph_argparse_early_args(args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ &cluster,
+ &conf_file_list);
+ return crimson::common::sharded_conf().start(
+ init_params.name, cluster
+ ).then([] {
+ return local_conf().start();
+ }).then([conf_file_list] {
+ return local_conf().parse_config_files(conf_file_list);
+ }).then([&app] {
+ auto&& config = app.configuration();
+ verbose = config["verbose"].as<bool>();
+ return test_stress(thrash_params_t{8, 32, 50, 120})
+ .then([] {
+ return test_injection(thrash_params_t{16, 32, 50, 120});
+ }).then([] {
+ logger().info("All tests succeeded");
+ // Seastar has bugs to have events undispatched during shutdown,
+ // which will result in memory leak and thus fail LeakSanitizer.
+ return seastar::sleep(100ms);
+ });
+ }).then([] {
+ return crimson::common::sharded_conf().stop();
+ }).then([] {
+ return 0;
+ }).handle_exception([] (auto eptr) {
+ logger().error("Test failed: got exception {}", eptr);
+ return 1;
+ });
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ app.add_options()
+ ("verbose,v", bpo::value<bool>()->default_value(false),
+ "chatty if true");
+ return app.run(argc, argv, [&app] {
+ return do_test(app);
+ });
+}
diff --git a/src/test/crimson/test_monc.cc b/src/test/crimson/test_monc.cc
new file mode 100644
index 000000000..e60df4525
--- /dev/null
+++ b/src/test/crimson/test_monc.cc
@@ -0,0 +1,84 @@
+#include <seastar/core/app-template.hh>
+#include "common/ceph_argparse.h"
+#include "crimson/common/auth_handler.h"
+#include "crimson/common/config_proxy.h"
+#include "crimson/mon/MonClient.h"
+#include "crimson/net/Connection.h"
+#include "crimson/net/Messenger.h"
+
+using Config = crimson::common::ConfigProxy;
+using MonClient = crimson::mon::Client;
+
+namespace {
+
+class DummyAuthHandler : public crimson::common::AuthHandler {
+public:
+ void handle_authentication(const EntityName& name,
+ const AuthCapsInfo& caps) final
+ {}
+};
+
+DummyAuthHandler dummy_handler;
+
+}
+
+using namespace std::literals;
+
+static seastar::future<> test_monc()
+{
+ return crimson::common::sharded_conf().start(EntityName{}, "ceph"sv).then([] {
+ std::vector<const char*> args;
+ std::string cluster;
+ std::string conf_file_list;
+ auto init_params = ceph_argparse_early_args(args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ &cluster,
+ &conf_file_list);
+ auto& conf = crimson::common::local_conf();
+ conf->name = init_params.name;
+ conf->cluster = cluster;
+ return conf.parse_config_files(conf_file_list);
+ }).then([] {
+ return crimson::common::sharded_perf_coll().start();
+ }).then([]() mutable {
+ auto msgr = crimson::net::Messenger::create(entity_name_t::OSD(0), "monc", 0, true);
+ return seastar::do_with(MonClient{*msgr, dummy_handler},
+ [msgr](auto& monc) mutable {
+ return msgr->start({&monc}).then([&monc] {
+ return seastar::with_timeout(
+ seastar::lowres_clock::now() + std::chrono::seconds{10},
+ monc.start());
+ }).then([&monc] {
+ return monc.stop();
+ });
+ }).finally([msgr] {
+ return msgr->shutdown();
+ });
+ }).finally([] {
+ return crimson::common::sharded_perf_coll().stop().then([] {
+ return crimson::common::sharded_conf().stop();
+ });
+ });
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ return app.run(argc, argv, [&] {
+ return test_monc().then([] {
+ std::cout << "All tests succeeded" << std::endl;
+ }).handle_exception([] (auto eptr) {
+ std::cout << "Test failure" << std::endl;
+ return seastar::make_exception_future<>(eptr);
+ });
+ });
+}
+
+
+/*
+ * Local Variables:
+ * compile-command: "make -j4 \
+ * -C ../../../build \
+ * unittest_seastar_monc"
+ * End:
+ */
diff --git a/src/test/crimson/test_perfcounters.cc b/src/test/crimson/test_perfcounters.cc
new file mode 100644
index 000000000..8aecbf911
--- /dev/null
+++ b/src/test/crimson/test_perfcounters.cc
@@ -0,0 +1,62 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <iostream>
+#include <fmt/format.h>
+
+#include "common/Formatter.h"
+#include "common/perf_counters.h"
+#include "crimson/common/perf_counters_collection.h"
+
+#include <seastar/core/app-template.hh>
+#include <seastar/core/sharded.hh>
+
+enum {
+ PERFTEST_FIRST = 1000000,
+ PERFTEST_INDEX,
+ PERFTEST_LAST,
+};
+
+static constexpr uint64_t PERF_VAL = 42;
+
+static seastar::future<> test_perfcounters(){
+ return crimson::common::sharded_perf_coll().start().then([] {
+ return crimson::common::sharded_perf_coll().invoke_on_all([] (auto& s){
+ std::string name =fmt::format("seastar-osd::shard-{}",seastar::this_shard_id());
+ PerfCountersBuilder plb(NULL, name, PERFTEST_FIRST,PERFTEST_LAST);
+ plb.add_u64_counter(PERFTEST_INDEX, "perftest_count", "count perftest");
+ auto perf_logger = plb.create_perf_counters();
+ perf_logger->inc(PERFTEST_INDEX,PERF_VAL);
+ s.get_perf_collection()->add(perf_logger);
+ });
+ }).then([]{
+ return crimson::common::sharded_perf_coll().invoke_on_all([] (auto& s){
+ auto pcc = s.get_perf_collection();
+ pcc->with_counters([](auto& by_path){
+ for (auto& perf_counter : by_path) {
+ if (PERF_VAL != perf_counter.second.perf_counters->get(PERFTEST_INDEX)) {
+ throw std::runtime_error("perf counter does not match");
+ }
+ }
+ });
+ });
+ }).finally([] {
+ return crimson::common::sharded_perf_coll().stop();
+ });
+
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ return app.run(argc, argv, [&] {
+ return test_perfcounters().then([] {
+ std::cout << "All tests succeeded" << std::endl;
+ }).handle_exception([] (auto eptr) {
+ std::cout << "Test failure" << std::endl;
+ return seastar::make_exception_future<>(eptr);
+ });
+ });
+
+}
+
+
diff --git a/src/test/crimson/test_socket.cc b/src/test/crimson/test_socket.cc
new file mode 100644
index 000000000..2b61196ea
--- /dev/null
+++ b/src/test/crimson/test_socket.cc
@@ -0,0 +1,558 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/ceph_argparse.h"
+#include <fmt/os.h>
+#include <seastar/core/app-template.hh>
+#include <seastar/core/gate.hh>
+#include <seastar/core/sharded.hh>
+#include <seastar/core/sleep.hh>
+#include <seastar/core/when_all.hh>
+#include <seastar/util/later.hh>
+
+#include "crimson/common/log.h"
+#include "crimson/net/Errors.h"
+#include "crimson/net/Fwd.h"
+#include "crimson/net/Socket.h"
+
+using crimson::common::local_conf;
+
+namespace {
+
+using namespace std::chrono_literals;
+
+using seastar::engine;
+using seastar::future;
+using crimson::net::error;
+using crimson::net::listen_ertr;
+using crimson::net::ShardedServerSocket;
+using crimson::net::Socket;
+using crimson::net::SocketRef;
+using crimson::net::stop_t;
+
+using SocketFRef = seastar::foreign_ptr<SocketRef>;
+
+seastar::logger &logger() {
+ return crimson::get_logger(ceph_subsys_test);
+}
+
+entity_addr_t get_server_addr() {
+ entity_addr_t saddr;
+ saddr.parse("127.0.0.1", nullptr);
+ saddr.set_port(9020);
+ return saddr;
+}
+
+future<SocketRef> socket_connect(const entity_addr_t& saddr) {
+ logger().debug("socket_connect() to {} ...", saddr);
+ return Socket::connect(saddr).then([](auto socket) {
+ logger().debug("socket_connect() connected");
+ return socket;
+ });
+}
+
+future<> test_refused() {
+ logger().info("test_refused()...");
+ auto saddr = get_server_addr();
+ return socket_connect(saddr).discard_result().then([saddr] {
+ logger().error("test_refused(): connection to {} is not refused", saddr);
+ ceph_abort();
+ }).handle_exception_type([](const std::system_error& e) {
+ if (e.code() != std::errc::connection_refused) {
+ logger().error("test_refused() got unexpeted error {}", e);
+ ceph_abort();
+ } else {
+ logger().info("test_refused() ok\n");
+ }
+ }).handle_exception([](auto eptr) {
+ logger().error("test_refused() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+}
+
+future<> test_bind_same(bool is_fixed_cpu) {
+ logger().info("test_bind_same()...");
+ return ShardedServerSocket::create(is_fixed_cpu
+ ).then([is_fixed_cpu](auto pss1) {
+ auto saddr = get_server_addr();
+ return pss1->listen(saddr).safe_then([saddr, is_fixed_cpu] {
+ // try to bind the same address
+ return ShardedServerSocket::create(is_fixed_cpu
+ ).then([saddr](auto pss2) {
+ return pss2->listen(saddr).safe_then([] {
+ logger().error("test_bind_same() should raise address_in_use");
+ ceph_abort();
+ }, listen_ertr::all_same_way(
+ [](const std::error_code& e) {
+ if (e == std::errc::address_in_use) {
+ // successful!
+ logger().info("test_bind_same() ok\n");
+ } else {
+ logger().error("test_bind_same() got unexpected error {}", e);
+ ceph_abort();
+ }
+ // Note: need to return a explicit ready future, or there will be a
+ // runtime error: member access within null pointer of type 'struct promise_base'
+ return seastar::now();
+ })).then([pss2] {
+ return pss2->shutdown_destroy();
+ });
+ });
+ }, listen_ertr::all_same_way(
+ [saddr](const std::error_code& e) {
+ logger().error("test_bind_same(): there is another instance running at {}",
+ saddr);
+ ceph_abort();
+ })).then([pss1] {
+ return pss1->shutdown_destroy();
+ }).handle_exception([](auto eptr) {
+ logger().error("test_bind_same() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+ });
+}
+
+future<> test_accept(bool is_fixed_cpu) {
+ logger().info("test_accept()");
+ return ShardedServerSocket::create(is_fixed_cpu
+ ).then([](auto pss) {
+ auto saddr = get_server_addr();
+ return pss->listen(saddr
+ ).safe_then([pss] {
+ return pss->accept([](auto socket, auto paddr) {
+ logger().info("test_accept(): accepted at shard {}", seastar::this_shard_id());
+ // simple accept
+ return seastar::sleep(100ms
+ ).then([socket = std::move(socket)]() mutable {
+ return socket->close(
+ ).finally([cleanup = std::move(socket)] {});
+ });
+ });
+ }, listen_ertr::all_same_way(
+ [saddr](const std::error_code& e) {
+ logger().error("test_accept(): there is another instance running at {}",
+ saddr);
+ ceph_abort();
+ })).then([saddr] {
+ return seastar::when_all(
+ socket_connect(saddr).then([](auto socket) {
+ return socket->close().finally([cleanup = std::move(socket)] {}); }),
+ socket_connect(saddr).then([](auto socket) {
+ return socket->close().finally([cleanup = std::move(socket)] {}); }),
+ socket_connect(saddr).then([](auto socket) {
+ return socket->close().finally([cleanup = std::move(socket)] {}); })
+ ).discard_result();
+ }).then([] {
+ // should be enough to be connected locally
+ return seastar::sleep(50ms);
+ }).then([] {
+ logger().info("test_accept() ok\n");
+ }).then([pss] {
+ return pss->shutdown_destroy();
+ }).handle_exception([](auto eptr) {
+ logger().error("test_accept() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+ });
+}
+
+class SocketFactory {
+ static constexpr seastar::shard_id CLIENT_CPU = 0u;
+ SocketRef client_socket;
+ seastar::promise<> server_connected;
+
+ static constexpr seastar::shard_id SERVER_CPU = 1u;
+ ShardedServerSocket *pss = nullptr;
+
+ seastar::shard_id server_socket_CPU;
+ SocketFRef server_socket;
+
+ public:
+ template <typename FuncC, typename FuncS>
+ static future<> dispatch_sockets(
+ bool is_fixed_cpu,
+ FuncC&& cb_client,
+ FuncS&& cb_server) {
+ ceph_assert_always(seastar::this_shard_id() == CLIENT_CPU);
+ auto owner = std::make_unique<SocketFactory>();
+ auto psf = owner.get();
+ auto saddr = get_server_addr();
+ return seastar::smp::submit_to(SERVER_CPU, [psf, saddr, is_fixed_cpu] {
+ return ShardedServerSocket::create(is_fixed_cpu
+ ).then([psf, saddr](auto pss) {
+ psf->pss = pss;
+ return pss->listen(saddr
+ ).safe_then([] {
+ }, listen_ertr::all_same_way([saddr](const std::error_code& e) {
+ logger().error("dispatch_sockets(): there is another instance running at {}",
+ saddr);
+ ceph_abort();
+ }));
+ });
+ }).then([psf, saddr] {
+ return seastar::when_all_succeed(
+ seastar::smp::submit_to(CLIENT_CPU, [psf, saddr] {
+ return socket_connect(saddr).then([psf](auto socket) {
+ ceph_assert_always(seastar::this_shard_id() == CLIENT_CPU);
+ psf->client_socket = std::move(socket);
+ });
+ }),
+ seastar::smp::submit_to(SERVER_CPU, [psf] {
+ return psf->pss->accept([psf](auto _socket, auto paddr) {
+ logger().info("dispatch_sockets(): accepted at shard {}",
+ seastar::this_shard_id());
+ psf->server_socket_CPU = seastar::this_shard_id();
+ if (psf->pss->is_fixed_shard_dispatching()) {
+ ceph_assert_always(SERVER_CPU == seastar::this_shard_id());
+ }
+ SocketFRef socket = seastar::make_foreign(std::move(_socket));
+ psf->server_socket = std::move(socket);
+ return seastar::smp::submit_to(CLIENT_CPU, [psf] {
+ psf->server_connected.set_value();
+ });
+ });
+ })
+ );
+ }).then_unpack([] {
+ return seastar::now();
+ }).then([psf] {
+ return psf->server_connected.get_future();
+ }).then([psf] {
+ if (psf->pss) {
+ return seastar::smp::submit_to(SERVER_CPU, [psf] {
+ return psf->pss->shutdown_destroy();
+ });
+ }
+ return seastar::now();
+ }).then([psf,
+ cb_client = std::move(cb_client),
+ cb_server = std::move(cb_server)]() mutable {
+ logger().debug("dispatch_sockets(): client/server socket are ready");
+ return seastar::when_all_succeed(
+ seastar::smp::submit_to(CLIENT_CPU,
+ [socket = psf->client_socket.get(), cb_client = std::move(cb_client)] {
+ return cb_client(socket).then([socket] {
+ logger().debug("closing client socket...");
+ return socket->close();
+ }).handle_exception([](auto eptr) {
+ logger().error("dispatch_sockets():"
+ " cb_client() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+ }),
+ seastar::smp::submit_to(psf->server_socket_CPU,
+ [socket = psf->server_socket.get(), cb_server = std::move(cb_server)] {
+ return cb_server(socket).then([socket] {
+ logger().debug("closing server socket...");
+ return socket->close();
+ }).handle_exception([](auto eptr) {
+ logger().error("dispatch_sockets():"
+ " cb_server() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+ })
+ );
+ }).then_unpack([] {
+ return seastar::now();
+ }).finally([cleanup = std::move(owner)] {});
+ }
+};
+
+class Connection {
+ static const uint64_t DATA_TAIL = 5327;
+ static const unsigned DATA_SIZE = 4096;
+ std::array<uint64_t, DATA_SIZE> data = {0};
+
+ void verify_data_read(const uint64_t read_data[]) {
+ ceph_assert(read_data[0] == read_count);
+ ceph_assert(data[DATA_SIZE - 1] = DATA_TAIL);
+ }
+
+ Socket* socket = nullptr;
+ uint64_t write_count = 0;
+ uint64_t read_count = 0;
+
+ Connection(Socket* socket) : socket{socket} {
+ assert(socket);
+ data[DATA_SIZE - 1] = DATA_TAIL;
+ }
+
+ future<> dispatch_write(unsigned round = 0, bool force_shut = false) {
+ logger().debug("dispatch_write(round={}, force_shut={})...", round, force_shut);
+ return seastar::repeat([this, round, force_shut] {
+ if (round != 0 && round <= write_count) {
+ return seastar::futurize_invoke([this, force_shut] {
+ if (force_shut) {
+ logger().debug("dispatch_write() done, force shutdown output");
+ socket->force_shutdown_out();
+ } else {
+ logger().debug("dispatch_write() done");
+ }
+ }).then([] {
+ return seastar::make_ready_future<stop_t>(stop_t::yes);
+ });
+ } else {
+ data[0] = write_count;
+ bufferlist bl;
+ bl.append(buffer::copy(
+ reinterpret_cast<const char*>(&data), sizeof(data)));
+ return socket->write(bl
+ ).then([this] {
+ return socket->flush();
+ }).then([this] {
+ write_count += 1;
+ return seastar::make_ready_future<stop_t>(stop_t::no);
+ });
+ }
+ });
+ }
+
+ future<> dispatch_write_unbounded() {
+ return dispatch_write(
+ ).then([] {
+ ceph_abort();
+ }).handle_exception_type([this](const std::system_error& e) {
+ if (e.code() != std::errc::broken_pipe &&
+ e.code() != std::errc::connection_reset) {
+ logger().error("dispatch_write_unbounded(): "
+ "unexpected error {}", e);
+ throw;
+ }
+ // successful
+ logger().debug("dispatch_write_unbounded(): "
+ "expected error {}", e);
+ shutdown();
+ });
+ }
+
+ future<> dispatch_read(unsigned round = 0, bool force_shut = false) {
+ logger().debug("dispatch_read(round={}, force_shut={})...", round, force_shut);
+ return seastar::repeat([this, round, force_shut] {
+ if (round != 0 && round <= read_count) {
+ return seastar::futurize_invoke([this, force_shut] {
+ if (force_shut) {
+ logger().debug("dispatch_read() done, force shutdown input");
+ socket->force_shutdown_in();
+ } else {
+ logger().debug("dispatch_read() done");
+ }
+ }).then([] {
+ return seastar::make_ready_future<stop_t>(stop_t::yes);
+ });
+ } else {
+ return seastar::futurize_invoke([this] {
+ // we want to test both Socket::read() and Socket::read_exactly()
+ if (read_count % 2) {
+ return socket->read(DATA_SIZE * sizeof(uint64_t)
+ ).then([this](ceph::bufferlist bl) {
+ uint64_t read_data[DATA_SIZE];
+ auto p = bl.cbegin();
+ ::ceph::decode_raw(read_data, p);
+ verify_data_read(read_data);
+ });
+ } else {
+ return socket->read_exactly(DATA_SIZE * sizeof(uint64_t)
+ ).then([this](auto bptr) {
+ uint64_t read_data[DATA_SIZE];
+ std::memcpy(read_data, bptr.c_str(), DATA_SIZE * sizeof(uint64_t));
+ verify_data_read(read_data);
+ });
+ }
+ }).then([this] {
+ ++read_count;
+ return seastar::make_ready_future<stop_t>(stop_t::no);
+ });
+ }
+ });
+ }
+
+ future<> dispatch_read_unbounded() {
+ return dispatch_read(
+ ).then([] {
+ ceph_abort();
+ }).handle_exception_type([this](const std::system_error& e) {
+ if (e.code() != error::read_eof
+ && e.code() != std::errc::connection_reset) {
+ logger().error("dispatch_read_unbounded(): "
+ "unexpected error {}", e);
+ throw;
+ }
+ // successful
+ logger().debug("dispatch_read_unbounded(): "
+ "expected error {}", e);
+ shutdown();
+ });
+ }
+
+ void shutdown() {
+ socket->shutdown();
+ }
+
+ public:
+ static future<> dispatch_rw_bounded(Socket* socket, unsigned round,
+ bool force_shut = false) {
+ logger().debug("dispatch_rw_bounded(round={}, force_shut={})...",
+ round, force_shut);
+ return seastar::do_with(Connection{socket},
+ [round, force_shut](auto& conn) {
+ ceph_assert(round != 0);
+ return seastar::when_all_succeed(
+ conn.dispatch_write(round, force_shut),
+ conn.dispatch_read(round, force_shut)
+ ).then_unpack([] {
+ return seastar::now();
+ });
+ });
+ }
+
+ static future<> dispatch_rw_unbounded(Socket* socket, bool preemptive_shut = false) {
+ logger().debug("dispatch_rw_unbounded(preemptive_shut={})...", preemptive_shut);
+ return seastar::do_with(Connection{socket}, [preemptive_shut](auto& conn) {
+ return seastar::when_all_succeed(
+ conn.dispatch_write_unbounded(),
+ conn.dispatch_read_unbounded(),
+ seastar::futurize_invoke([&conn, preemptive_shut] {
+ if (preemptive_shut) {
+ return seastar::sleep(100ms).then([&conn] {
+ logger().debug("dispatch_rw_unbounded() shutdown socket preemptively(100ms)");
+ conn.shutdown();
+ });
+ } else {
+ return seastar::now();
+ }
+ })
+ ).then_unpack([] {
+ return seastar::now();
+ });
+ });
+ }
+};
+
+future<> test_read_write(bool is_fixed_cpu) {
+ logger().info("test_read_write()...");
+ return SocketFactory::dispatch_sockets(
+ is_fixed_cpu,
+ [](auto cs) { return Connection::dispatch_rw_bounded(cs, 128); },
+ [](auto ss) { return Connection::dispatch_rw_bounded(ss, 128); }
+ ).then([] {
+ logger().info("test_read_write() ok\n");
+ }).handle_exception([](auto eptr) {
+ logger().error("test_read_write() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+}
+
+future<> test_unexpected_down(bool is_fixed_cpu) {
+ logger().info("test_unexpected_down()...");
+ return SocketFactory::dispatch_sockets(
+ is_fixed_cpu,
+ [](auto cs) {
+ return Connection::dispatch_rw_bounded(cs, 128, true
+ ).handle_exception_type([](const std::system_error& e) {
+ logger().debug("test_unexpected_down(): client get error {}", e);
+ ceph_assert(e.code() == error::read_eof);
+ });
+ },
+ [](auto ss) { return Connection::dispatch_rw_unbounded(ss); }
+ ).then([] {
+ logger().info("test_unexpected_down() ok\n");
+ }).handle_exception([](auto eptr) {
+ logger().error("test_unexpected_down() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+}
+
+future<> test_shutdown_propagated(bool is_fixed_cpu) {
+ logger().info("test_shutdown_propagated()...");
+ return SocketFactory::dispatch_sockets(
+ is_fixed_cpu,
+ [](auto cs) {
+ logger().debug("test_shutdown_propagated() shutdown client socket");
+ cs->shutdown();
+ return seastar::now();
+ },
+ [](auto ss) { return Connection::dispatch_rw_unbounded(ss); }
+ ).then([] {
+ logger().info("test_shutdown_propagated() ok\n");
+ }).handle_exception([](auto eptr) {
+ logger().error("test_shutdown_propagated() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+}
+
+future<> test_preemptive_down(bool is_fixed_cpu) {
+ logger().info("test_preemptive_down()...");
+ return SocketFactory::dispatch_sockets(
+ is_fixed_cpu,
+ [](auto cs) { return Connection::dispatch_rw_unbounded(cs, true); },
+ [](auto ss) { return Connection::dispatch_rw_unbounded(ss); }
+ ).then([] {
+ logger().info("test_preemptive_down() ok\n");
+ }).handle_exception([](auto eptr) {
+ logger().error("test_preemptive_down() got unexpeted exception {}", eptr);
+ ceph_abort();
+ });
+}
+
+future<> do_test_with_type(bool is_fixed_cpu) {
+ return test_bind_same(is_fixed_cpu
+ ).then([is_fixed_cpu] {
+ return test_accept(is_fixed_cpu);
+ }).then([is_fixed_cpu] {
+ return test_read_write(is_fixed_cpu);
+ }).then([is_fixed_cpu] {
+ return test_unexpected_down(is_fixed_cpu);
+ }).then([is_fixed_cpu] {
+ return test_shutdown_propagated(is_fixed_cpu);
+ }).then([is_fixed_cpu] {
+ return test_preemptive_down(is_fixed_cpu);
+ });
+}
+
+}
+
+seastar::future<int> do_test(seastar::app_template& app)
+{
+ std::vector<const char*> args;
+ std::string cluster;
+ std::string conf_file_list;
+ auto init_params = ceph_argparse_early_args(args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ &cluster,
+ &conf_file_list);
+ return crimson::common::sharded_conf().start(
+ init_params.name, cluster
+ ).then([] {
+ return local_conf().start();
+ }).then([conf_file_list] {
+ return local_conf().parse_config_files(conf_file_list);
+ }).then([] {
+ return local_conf().set_val("ms_inject_internal_delays", "0");
+ }).then([] {
+ return test_refused();
+ }).then([] {
+ return do_test_with_type(true);
+ }).then([] {
+ return do_test_with_type(false);
+ }).then([] {
+ logger().info("All tests succeeded");
+ // Seastar has bugs to have events undispatched during shutdown,
+ // which will result in memory leak and thus fail LeakSanitizer.
+ return seastar::sleep(100ms);
+ }).then([] {
+ return crimson::common::sharded_conf().stop();
+ }).then([] {
+ return 0;
+ }).handle_exception([](auto eptr) {
+ logger().error("Test failed: got exception {}", eptr);
+ return 1;
+ });
+}
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ return app.run(argc, argv, [&app] {
+ return do_test(app);
+ });
+}
diff --git a/src/test/crush/CMakeLists.txt b/src/test/crush/CMakeLists.txt
new file mode 100644
index 000000000..1dae0ca9c
--- /dev/null
+++ b/src/test/crush/CMakeLists.txt
@@ -0,0 +1,13 @@
+# unittest_crush_wrapper
+add_executable(unittest_crush_wrapper
+ CrushWrapper.cc)
+add_ceph_unittest(unittest_crush_wrapper)
+target_link_libraries(unittest_crush_wrapper ceph-common)
+
+# unittest_crush
+add_executable(unittest_crush
+ crush.cc)
+add_ceph_unittest(unittest_crush PARALLEL)
+target_link_libraries(unittest_crush ceph-common)
+
+add_ceph_test(crush_weights.sh ${CMAKE_CURRENT_SOURCE_DIR}/crush_weights.sh)
diff --git a/src/test/crush/CrushWrapper.cc b/src/test/crush/CrushWrapper.cc
new file mode 100644
index 000000000..7989de386
--- /dev/null
+++ b/src/test/crush/CrushWrapper.cc
@@ -0,0 +1,1458 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "common/ceph_argparse.h"
+#include "common/common_init.h"
+#include "include/stringify.h"
+#include "include/Context.h"
+#include "osd/osd_types.h"
+
+#include "crush/CrushWrapper.h"
+
+using namespace std;
+
+class CrushWrapperTest : public ::testing::Test
+{
+public:
+ void SetUp() final
+ {
+ CephInitParameters params(CEPH_ENTITY_TYPE_CLIENT);
+ cct = common_preinit(params, CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ cct->_conf.set_val("debug_crush", "0");
+ }
+ void TearDown() final
+ {
+ cct->put();
+ cct = nullptr;
+ }
+protected:
+ CephContext *cct = nullptr;
+};
+
+TEST_F(CrushWrapperTest, get_immediate_parent) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ int item = 0;
+
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(item, &ret);
+ EXPECT_EQ(-ENOENT, ret);
+
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd.0", loc));
+ }
+
+ loc = c->get_immediate_parent(item, &ret);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ("root", loc.first);
+ EXPECT_EQ("default", loc.second);
+}
+
+TEST_F(CrushWrapperTest, move_bucket) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, "root0"));
+
+ {
+ map<string,string> loc;
+ loc["root"] = "root0";
+ loc["host"] = "host0";
+
+ int item = 0;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd.0", loc));
+ }
+ int host0 = c->get_item_id("host0");
+
+ int root1;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root1));
+ EXPECT_EQ(0, c->set_item_name(root1, "root1"));
+
+ map<string,string> loc;
+ loc["root"] = "root1";
+
+ // 0 is not a valid bucket number, must be negative
+ EXPECT_EQ(-EINVAL, c->move_bucket(cct, 0, loc));
+ // -100 is not an existing bucket
+ EXPECT_EQ(-ENOENT, c->move_bucket(cct, -100, loc));
+ // move host0 from root0 to root1
+ {
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(host0, &ret);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ("root", loc.first);
+ EXPECT_EQ("root0", loc.second);
+ }
+ EXPECT_EQ(0, c->move_bucket(cct, host0, loc));
+ {
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(host0, &ret);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ("root", loc.first);
+ EXPECT_EQ("root1", loc.second);
+ }
+}
+
+TEST_F(CrushWrapperTest, swap_bucket) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int root;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root));
+ EXPECT_EQ(0, c->set_item_name(root, "root"));
+
+ int a, b;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &a));
+ EXPECT_EQ(0, c->set_item_name(a, "a"));
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &b));
+ EXPECT_EQ(0, c->set_item_name(b, "b"));
+
+ {
+ map<string,string> loc;
+ loc["root"] = "root";
+ EXPECT_EQ(0, c->move_bucket(cct, a, loc));
+ }
+ {
+ map<string,string> loc;
+ loc["root"] = "root";
+ loc["host"] = "a";
+ EXPECT_EQ(0, c->insert_item(cct, 0, 1.0, "osd.0", loc));
+ EXPECT_EQ(0, c->insert_item(cct, 1, 1.0, "osd.1", loc));
+ EXPECT_EQ(0, c->insert_item(cct, 2, 1.0, "osd.2", loc));
+ }
+ {
+ map<string,string> loc;
+ loc["host"] = "b";
+ EXPECT_EQ(0, c->insert_item(cct, 3, 1.0, "osd.3", loc));
+ }
+ ASSERT_EQ(0x30000, c->get_item_weight(a));
+ ASSERT_EQ(string("a"), c->get_item_name(a));
+ ASSERT_EQ(0x10000, c->get_item_weight(b));
+ ASSERT_EQ(string("b"), c->get_item_name(b));
+ ASSERT_EQ(a, c->get_bucket_item(root, 0));
+ ASSERT_EQ(0, c->get_bucket_item(a, 0));
+ ASSERT_EQ(1, c->get_bucket_item(a, 1));
+ ASSERT_EQ(2, c->get_bucket_item(a, 2));
+ ASSERT_EQ(3, c->get_bucket_item(b, 0));
+
+ // check if it can swap parent with child
+ ASSERT_EQ(-EINVAL, c->swap_bucket(cct, root, a));
+
+ c->swap_bucket(cct, a, b);
+ ASSERT_EQ(0x30000, c->get_item_weight(b));
+ ASSERT_EQ(string("a"), c->get_item_name(b));
+ ASSERT_EQ(0x10000, c->get_item_weight(a));
+ ASSERT_EQ(string("b"), c->get_item_name(a));
+ ASSERT_EQ(a, c->get_bucket_item(root, 0));
+ ASSERT_EQ(0, c->get_bucket_item(b, 0));
+ ASSERT_EQ(1, c->get_bucket_item(b, 1));
+ ASSERT_EQ(2, c->get_bucket_item(b, 2));
+ ASSERT_EQ(3, c->get_bucket_item(a, 0));
+}
+
+TEST_F(CrushWrapperTest, rename_bucket_or_item) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, "root0"));
+
+ int item = 0;
+ {
+ map<string,string> loc;
+ loc["root"] = "root0";
+ loc["host"] = "host0";
+
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd.0", loc));
+ }
+ item++;
+ {
+ map<string,string> loc;
+ loc["root"] = "root0";
+ loc["host"] = "host1";
+
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd.1", loc));
+ }
+
+ stringstream ss;
+ EXPECT_EQ(-EINVAL, c->can_rename_item("host0", "????", &ss));
+ EXPECT_EQ(-EINVAL, c->rename_item("host0", "????", &ss));
+ EXPECT_EQ(-EINVAL, c->can_rename_bucket("host0", "????", &ss));
+ EXPECT_EQ(-EINVAL, c->rename_bucket("host0", "????", &ss));
+
+ EXPECT_EQ(-EEXIST, c->can_rename_item("host0", "host1", &ss));
+ EXPECT_EQ(-EEXIST, c->rename_item("host0", "host1", &ss));
+ EXPECT_EQ(-EEXIST, c->can_rename_bucket("host0", "host1", &ss));
+ EXPECT_EQ(-EEXIST, c->rename_bucket("host0", "host1", &ss));
+
+ EXPECT_EQ(-EALREADY, c->can_rename_item("gone", "host1", &ss));
+ EXPECT_EQ(-EALREADY, c->rename_item("gone", "host1", &ss));
+ EXPECT_EQ(-EALREADY, c->can_rename_bucket("gone", "host1", &ss));
+ EXPECT_EQ(-EALREADY, c->rename_bucket("gone", "host1", &ss));
+
+ EXPECT_EQ(-ENOENT, c->can_rename_item("doesnotexist", "somethingelse", &ss));
+ EXPECT_EQ(-ENOENT, c->rename_item("doesnotexist", "somethingelse", &ss));
+ EXPECT_EQ(-ENOENT, c->can_rename_bucket("doesnotexist", "somethingelse", &ss));
+ EXPECT_EQ(-ENOENT, c->rename_bucket("doesnotexist", "somethingelse", &ss));
+
+ EXPECT_EQ(-ENOTDIR, c->can_rename_bucket("osd.1", "somethingelse", &ss));
+ EXPECT_EQ(-ENOTDIR, c->rename_bucket("osd.1", "somethingelse", &ss));
+
+ int host0id = c->get_item_id("host0");
+ EXPECT_EQ(0, c->rename_bucket("host0", "host0renamed", &ss));
+ EXPECT_EQ(host0id, c->get_item_id("host0renamed"));
+
+ int osd0id = c->get_item_id("osd0");
+ EXPECT_EQ(0, c->rename_item("osd.0", "osd0renamed", &ss));
+ EXPECT_EQ(osd0id, c->get_item_id("osd0renamed"));
+}
+
+TEST_F(CrushWrapperTest, check_item_loc) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ int item = 0;
+ float expected_weight = 1.0;
+
+ // fail if loc is empty
+ {
+ float weight;
+ map<string,string> loc;
+ EXPECT_FALSE(c->check_item_loc(cct, item, loc, &weight));
+ }
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ // fail because the item is not found at the specified location
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "default";
+ EXPECT_FALSE(c->check_item_loc(cct, item, loc, &weight));
+ }
+ // fail because the bucket name does not match an existing bucket
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "default";
+ const string HOST("host0");
+ loc["host"] = HOST;
+ EXPECT_FALSE(c->check_item_loc(cct, item, loc, &weight));
+ }
+ const string OSD("osd.0");
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+ EXPECT_EQ(0, c->insert_item(cct, item, expected_weight,
+ OSD, loc));
+ }
+ // fail because osd.0 is not a bucket and must not be in loc, in
+ // addition to being of the wrong type
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "osd.0";
+ EXPECT_FALSE(c->check_item_loc(cct, item, loc, &weight));
+ }
+ // succeed and retrieves the expected weight
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "default";
+ EXPECT_TRUE(c->check_item_loc(cct, item, loc, &weight));
+ EXPECT_EQ(expected_weight, weight);
+ }
+}
+
+TEST_F(CrushWrapperTest, update_item) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ const string HOST0("host0");
+ int host0;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host0);
+ c->set_item_name(host0, HOST0);
+
+ const string HOST1("host1");
+ int host1;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host1);
+ c->set_item_name(host1, HOST1);
+
+ int item = 0;
+
+ // fail if invalid names anywhere in loc
+ {
+ map<string,string> loc;
+ loc["rack"] = "\001";
+ EXPECT_EQ(-EINVAL, c->update_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // fail if invalid item name
+ {
+ map<string,string> loc;
+ EXPECT_EQ(-EINVAL, c->update_item(cct, item, 1.0,
+ "\005", loc));
+ }
+ const string OSD0("osd.0");
+ const string OSD1("osd.1");
+ float original_weight = 1.0;
+ float modified_weight = 2.0;
+ float weight;
+
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = HOST0;
+ EXPECT_GE(0.0, c->get_item_weightf(host0));
+ EXPECT_EQ(0, c->insert_item(cct, item, original_weight,
+ OSD0, loc));
+
+ // updating nothing changes nothing
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(cct, item, loc, &weight));
+ EXPECT_EQ(0, c->update_item(cct, item, original_weight,
+ OSD0, loc));
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(cct, item, loc, &weight));
+
+ // update the name and weight of the item but not the location
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(cct, item, loc, &weight));
+ EXPECT_EQ(1, c->update_item(cct, item, modified_weight,
+ OSD1, loc));
+ EXPECT_EQ(OSD1, c->get_item_name(item));
+ EXPECT_EQ(modified_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(cct, item, loc, &weight));
+ c->set_item_name(item, OSD0);
+ c->adjust_item_weightf(cct, item, original_weight);
+
+ // update the name and weight of the item and change its location
+ map<string,string> other_loc;
+ other_loc["root"] = "default";
+ other_loc["host"] = HOST1;
+
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(cct, item, loc, &weight));
+ EXPECT_FALSE(c->check_item_loc(cct, item, other_loc, &weight));
+ EXPECT_EQ(1, c->update_item(cct, item, modified_weight,
+ OSD1, other_loc));
+ EXPECT_EQ(OSD1, c->get_item_name(item));
+ EXPECT_EQ(modified_weight, c->get_item_weightf(item));
+ EXPECT_FALSE(c->check_item_loc(cct, item, loc, &weight));
+ EXPECT_TRUE(c->check_item_loc(cct, item, other_loc, &weight));
+}
+
+TEST_F(CrushWrapperTest, adjust_item_weight) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ const string HOST0("host0");
+ int host0;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host0);
+ c->set_item_name(host0, HOST0);
+
+ const string FAKE("fake");
+ int hostfake;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &hostfake);
+ c->set_item_name(hostfake, FAKE);
+
+ int item = 0;
+
+ // construct crush map
+
+ {
+ map<string,string> loc;
+ loc["host"] = "host0";
+ float host_weight = 2.0;
+ int bucket_id = 0;
+
+ item = 0;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ item = 1;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+
+ bucket_id = c->get_item_id("host0");
+ EXPECT_EQ(true, c->bucket_exists(bucket_id));
+ EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
+
+ map<string,string> bloc;
+ bloc["root"] = "default";
+ EXPECT_EQ(0, c->insert_item(cct, host0, host_weight,
+ HOST0, bloc));
+ }
+
+ {
+ map<string,string> loc;
+ loc["host"] = "fake";
+ float host_weight = 2.0;
+ int bucket_id = 0;
+
+ item = 0;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ item = 1;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+
+ bucket_id = c->get_item_id("fake");
+ EXPECT_EQ(true, c->bucket_exists(bucket_id));
+ EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
+
+ map<string,string> bloc;
+ bloc["root"] = "default";
+ EXPECT_EQ(0, c->insert_item(cct, hostfake, host_weight,
+ FAKE, bloc));
+ }
+
+ //
+ // When there is:
+ //
+ // default --> host0 --> osd.0 1.0
+ // | |
+ // | +-> osd.1 1.0
+ // |
+ // +-> fake --> osd.0 1.0
+ // |
+ // +-> osd.1 1.0
+ //
+ // Trying to adjust osd.0 weight to 2.0 in all buckets
+ // Trying to adjust osd.1 weight to 2.0 in host=fake
+ //
+ // So the crush map will be:
+ //
+ // default --> host0 --> osd.0 2.0
+ // | |
+ // | +-> osd.1 1.0
+ // |
+ // +-> fake --> osd.0 2.0
+ // |
+ // +-> osd.1 2.0
+ //
+
+ float original_weight = 1.0;
+ float modified_weight = 2.0;
+ map<string,string> loc_one, loc_two;
+ loc_one["host"] = "host0";
+ loc_two["host"] = "fake";
+
+ item = 0;
+ EXPECT_EQ(2, c->adjust_item_weightf(cct, item, modified_weight));
+ EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_one));
+ EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
+
+ item = 1;
+ EXPECT_EQ(1, c->adjust_item_weightf_in_loc(cct, item, modified_weight, loc_two));
+ EXPECT_EQ(original_weight, c->get_item_weightf_in_loc(item, loc_one));
+ EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
+}
+
+TEST_F(CrushWrapperTest, adjust_subtree_weight) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ const string HOST0("host0");
+ int host0;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host0);
+ c->set_item_name(host0, HOST0);
+
+ const string FAKE("fake");
+ int hostfake;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &hostfake);
+ c->set_item_name(hostfake, FAKE);
+
+ int item = 0;
+
+ // construct crush map
+
+ {
+ map<string,string> loc;
+ loc["host"] = "host0";
+ float host_weight = 2.0;
+ int bucket_id = 0;
+
+ item = 0;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ item = 1;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+
+ bucket_id = c->get_item_id("host0");
+ EXPECT_EQ(true, c->bucket_exists(bucket_id));
+ EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
+
+ map<string,string> bloc;
+ bloc["root"] = "default";
+ EXPECT_EQ(0, c->insert_item(cct, host0, host_weight,
+ HOST0, bloc));
+ }
+
+ {
+ map<string,string> loc;
+ loc["host"] = "fake";
+ float host_weight = 2.0;
+ int bucket_id = 0;
+
+ item = 0;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ item = 1;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+
+ bucket_id = c->get_item_id("fake");
+ EXPECT_EQ(true, c->bucket_exists(bucket_id));
+ EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
+
+ map<string,string> bloc;
+ bloc["root"] = "default";
+ EXPECT_EQ(0, c->insert_item(cct, hostfake, host_weight,
+ FAKE, bloc));
+ }
+
+ //cout << "--------before---------" << std::endl;
+ //c->dump_tree(&cout, NULL);
+ ASSERT_EQ(c->get_bucket_weight(host0), 131072);
+ ASSERT_EQ(c->get_bucket_weight(rootno), 262144);
+
+ int r = c->adjust_subtree_weightf(cct, host0, 2.0);
+ ASSERT_EQ(r, 2); // 2 items changed
+
+ //cout << "--------after---------" << std::endl;
+ //c->dump_tree(&cout, NULL);
+
+ ASSERT_EQ(c->get_bucket_weight(host0), 262144);
+ ASSERT_EQ(c->get_item_weight(host0), 262144);
+ ASSERT_EQ(c->get_bucket_weight(rootno), 262144 + 131072);
+}
+
+TEST_F(CrushWrapperTest, insert_item) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ int item = 0;
+
+ // invalid names anywhere in loc trigger an error
+ {
+ map<string,string> loc;
+ loc["host"] = "\001";
+ EXPECT_EQ(-EINVAL, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+
+ // insert an item in an existing bucket
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ item++;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ int another_item = item + 1;
+ EXPECT_EQ(-EEXIST, c->insert_item(cct, another_item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // implicit creation of a bucket
+ {
+ string name = "NAME";
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = name;
+
+ item++;
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // adding to an existing item name that is not associated with a bucket
+ {
+ string name = "ITEM_WITHOUT_BUCKET";
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = name;
+ item++;
+ c->set_item_name(item, name);
+
+ item++;
+ EXPECT_EQ(-EINVAL, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ //
+ // When there is:
+ //
+ // default --> host0 --> item
+ //
+ // Trying to insert the same item higher in the hirarchy will fail
+ // because it would create a loop.
+ //
+ // default --> host0 --> item
+ // |
+ // +-> item
+ //
+ {
+ item++;
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = "host0";
+
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ EXPECT_EQ(-EINVAL, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ }
+ //
+ // When there is:
+ //
+ // default --> host0
+ //
+ // Trying to insert default under host0 must fail
+ // because it would create a loop.
+ //
+ // default --> host0 --> default
+ //
+ {
+ map<string,string> loc;
+ loc["host"] = "host0";
+
+ EXPECT_EQ(-ELOOP, c->insert_item(cct, rootno, 1.0,
+ "default", loc));
+ }
+ // fail when mapping a bucket to the wrong type
+ {
+ // create an OSD bucket
+ int osdno;
+ int r = c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ 10, 0, NULL, NULL, &osdno);
+ ASSERT_EQ(0, r);
+ c->set_item_name(osdno, "myosd");
+ map<string,string> loc;
+ loc["root"] = "default";
+ // wrongfully pretend the osd is of type host
+ loc["host"] = "myosd";
+
+ item++;
+ EXPECT_EQ(-EINVAL, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // fail when no location
+ {
+ map<string,string> loc;
+ item++;
+ EXPECT_EQ(-EINVAL, c->insert_item(cct, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+}
+
+TEST_F(CrushWrapperTest, remove_item) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ {
+ int root;
+ ASSERT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root));
+ c->set_item_name(root, "root0");
+ }
+
+ {
+ int host;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host);
+ c->set_item_name(host, "host0");
+ }
+
+ const int num_osd = 12;
+ {
+ map<string, string> loc = {{"root", "root0"},
+ {"host", "host0"}};
+ string name{"osd."};
+ for (int item = 0; item < num_osd; item++) {
+ ASSERT_EQ(0, c->insert_item(cct, item, 1.0,
+ name + to_string(item), loc));
+ }
+ }
+ const int item_to_remove = num_osd / 2;
+ map<string, string> loc;
+ loc.insert(c->get_immediate_parent(item_to_remove));
+ ASSERT_EQ(0, c->remove_item(cct, item_to_remove, true));
+ float weight;
+ EXPECT_FALSE(c->check_item_loc(cct, item_to_remove, loc, &weight));
+}
+
+TEST_F(CrushWrapperTest, item_bucket_names) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ int index = 123;
+ string name = "NAME";
+ EXPECT_EQ(-EINVAL, c->set_item_name(index, "\001"));
+ EXPECT_EQ(0, c->set_item_name(index, name));
+ EXPECT_TRUE(c->name_exists(name));
+ EXPECT_TRUE(c->item_exists(index));
+ EXPECT_EQ(index, c->get_item_id(name));
+ EXPECT_EQ(name, c->get_item_name(index));
+}
+
+TEST_F(CrushWrapperTest, bucket_types) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ int index = 123;
+ string name = "NAME";
+ c->set_type_name(index, name);
+ EXPECT_EQ(1, c->get_num_type_names());
+ EXPECT_EQ(index, c->get_type_id(name));
+ EXPECT_EQ(name, c->get_type_name(index));
+}
+
+TEST_F(CrushWrapperTest, is_valid_crush_name) {
+ EXPECT_TRUE(CrushWrapper::is_valid_crush_name("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012456789-_"));
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_name(""));
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_name("\001"));
+}
+
+TEST_F(CrushWrapperTest, is_valid_crush_loc) {
+ map<string,string> loc;
+ EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(cct, loc));
+ loc["good"] = "better";
+ EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(cct, loc));
+ {
+ map<string,string> loc;
+ loc["\005"] = "default";
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(cct, loc));
+ }
+ {
+ map<string,string> loc;
+ loc["host"] = "\003";
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(cct, loc));
+ }
+}
+
+TEST_F(CrushWrapperTest, dump_rules) {
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ string failure_domain_type("osd");
+ string root_name("default");
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, root_name);
+
+ int item = 0;
+
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(item, &ret);
+ EXPECT_EQ(-ENOENT, ret);
+
+ {
+ map<string,string> loc;
+ loc["root"] = root_name;
+
+ EXPECT_EQ(0, c->insert_item(cct, item, 1.0,
+ "osd.0", loc));
+ }
+
+ // no rule by default
+ {
+ auto f = Formatter::create_unique("json-pretty");
+ f->open_array_section("rules");
+ c->dump_rules(f.get());
+ f->close_section();
+ stringstream ss;
+ f->flush(ss);
+ EXPECT_EQ("[]\n", ss.str());
+ }
+
+ string name("NAME");
+ int rule = c->add_simple_rule(name, root_name, failure_domain_type, "",
+ "firstn", pg_pool_t::TYPE_ERASURE);
+ EXPECT_EQ(0, rule);
+
+ {
+ auto f = Formatter::create_unique("xml");
+ c->dump_rules(f.get());
+ stringstream ss;
+ f->flush(ss);
+ EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
+ }
+
+ {
+ auto f = Formatter::create_unique("xml");
+ c->dump_rule(rule, f.get());
+ stringstream ss;
+ f->flush(ss);
+ EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
+ EXPECT_NE(string::npos,
+ ss.str().find("<item_name>default</item_name></step>"));
+ }
+
+ map<int,float> wm;
+ c->get_rule_weight_osd_map(0, &wm);
+ ASSERT_TRUE(wm.size() == 1);
+ ASSERT_TRUE(wm[0] == 1.0);
+}
+
+TEST_F(CrushWrapperTest, distance) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "host");
+ c.set_type_name(2, "rack");
+ c.set_type_name(3, "root");
+ int bno;
+ int r = c.add_bucket(0, CRUSH_BUCKET_STRAW,
+ CRUSH_HASH_DEFAULT, 3, 0, NULL,
+ NULL, &bno);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(-1, bno);
+ c.set_item_name(bno, "default");
+
+ c.set_max_devices(10);
+
+ //JSONFormatter jf(true);
+
+ map<string,string> loc;
+ loc["host"] = "a1";
+ loc["rack"] = "a";
+ loc["root"] = "default";
+ c.insert_item(cct, 0, 1, "osd.0", loc);
+
+ loc.clear();
+ loc["host"] = "a2";
+ loc["rack"] = "a";
+ loc["root"] = "default";
+ c.insert_item(cct, 1, 1, "osd.1", loc);
+
+ loc.clear();
+ loc["host"] = "b1";
+ loc["rack"] = "b";
+ loc["root"] = "default";
+ c.insert_item(cct, 2, 1, "osd.2", loc);
+
+ loc.clear();
+ loc["host"] = "b2";
+ loc["rack"] = "b";
+ loc["root"] = "default";
+ c.insert_item(cct, 3, 1, "osd.3", loc);
+
+ vector<pair<string,string> > ol;
+ c.get_full_location_ordered(3, ol);
+ ASSERT_EQ(3u, ol.size());
+ ASSERT_EQ(make_pair(string("host"),string("b2")), ol[0]);
+ ASSERT_EQ(make_pair(string("rack"),string("b")), ol[1]);
+ ASSERT_EQ(make_pair(string("root"),string("default")), ol[2]);
+
+ //c.dump(&jf);
+ //jf.flush(cout);
+
+ multimap<string,string> p;
+ p.insert(make_pair("host","b2"));
+ p.insert(make_pair("rack","b"));
+ p.insert(make_pair("root","default"));
+ ASSERT_EQ(3, c.get_common_ancestor_distance(cct, 0, p));
+ ASSERT_EQ(3, c.get_common_ancestor_distance(cct, 1, p));
+ ASSERT_EQ(2, c.get_common_ancestor_distance(cct, 2, p));
+ ASSERT_EQ(1, c.get_common_ancestor_distance(cct, 3, p));
+ ASSERT_EQ(-ENOENT, c.get_common_ancestor_distance(cct, 123, p));
+
+ // make sure a "multipath" location will reflect a minimal
+ // distance for both paths
+ p.insert(make_pair("host","b1"));
+ ASSERT_EQ(1, c.get_common_ancestor_distance(cct, 2, p));
+ ASSERT_EQ(1, c.get_common_ancestor_distance(cct, 3, p));
+}
+
+TEST_F(CrushWrapperTest, choose_args_compat) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "host");
+ c.set_type_name(2, "rack");
+ c.set_type_name(3, "root");
+
+ int weight = 12;
+
+ map<string,string> loc;
+ loc["host"] = "b1";
+ loc["rack"] = "r11";
+ loc["root"] = "default";
+ int item = 1;
+ c.insert_item(cct, item, weight, "osd.1", loc);
+
+ loc["host"] = "b2";
+ loc["rack"] = "r12";
+ loc["root"] = "default";
+ item = 2;
+ c.insert_item(cct, item, weight, "osd.2", loc);
+
+ ceph_assert(c.add_simple_rule("rule1", "r11", "host", "",
+ "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
+
+ int id = c.get_item_id("b1");
+
+ __u32 weights = 666 * 0x10000;
+ crush_weight_set weight_set;
+ weight_set.size = 1;
+ weight_set.weights = &weights;
+ int maxbuckets = c.get_max_buckets();
+ ceph_assert(maxbuckets > 0);
+ crush_choose_arg choose_args[maxbuckets];
+ memset(choose_args, '\0', sizeof(crush_choose_arg) * maxbuckets);
+ choose_args[-1-id].ids_size = 0;
+ choose_args[-1-id].weight_set_positions = 1;
+ choose_args[-1-id].weight_set = &weight_set;
+ crush_choose_arg_map arg_map;
+ arg_map.size = c.get_max_buckets();
+ arg_map.args = choose_args;
+
+ uint64_t features = CEPH_FEATURE_CRUSH_TUNABLES5|CEPH_FEATURE_INCARNATION_2;
+ int64_t caid = CrushWrapper::DEFAULT_CHOOSE_ARGS;
+
+ // if the client is capable, encode choose_args
+ {
+ c.choose_args[caid] = arg_map;
+ bufferlist bl;
+ c.encode(bl, features|CEPH_FEATURE_CRUSH_CHOOSE_ARGS);
+ auto i = bl.cbegin();
+ CrushWrapper c_new;
+ c_new.decode(i);
+ ASSERT_EQ(1u, c_new.choose_args.size());
+ ASSERT_EQ(1u, c_new.choose_args[caid].args[-1-id].weight_set_positions);
+ ASSERT_EQ(weights, c_new.choose_args[caid].args[-1-id].weight_set[0].weights[0]);
+ ASSERT_EQ(weight, c_new.get_bucket_item_weightf(id, 0));
+ }
+
+ // if the client is not compatible, copy choose_arg in the weights
+ {
+ c.choose_args[caid] = arg_map;
+ bufferlist bl;
+ c.encode(bl, features);
+ c.choose_args.clear();
+ auto i = bl.cbegin();
+ CrushWrapper c_new;
+ c_new.decode(i);
+ ASSERT_EQ(0u, c_new.choose_args.size());
+ ASSERT_EQ((int)weights, c_new.get_bucket_item_weight(id, 0));
+ }
+}
+
+TEST_F(CrushWrapperTest, remove_root) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "host");
+ c.set_type_name(2, "rack");
+ c.set_type_name(3, "root");
+
+ int weight = 1;
+
+ map<string,string> loc;
+ loc["host"] = "b1";
+ loc["rack"] = "r11";
+ loc["root"] = "default";
+ int item = 1;
+ c.insert_item(cct, item, weight, "osd.1", loc);
+ item = 2;
+ loc["host"] = "b2";
+ loc["rack"] = "r12";
+ loc["root"] = "default";
+ c.insert_item(cct, item, weight, "osd.2", loc);
+
+ ceph_assert(c.add_simple_rule("rule1", "r11", "host", "",
+ "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
+ ASSERT_TRUE(c.name_exists("default"));
+ ASSERT_TRUE(c.name_exists("r11"));
+ ASSERT_TRUE(c.name_exists("r12"));
+ ASSERT_EQ(c.remove_root(cct, c.get_item_id("default")), 0);
+ ASSERT_FALSE(c.name_exists("default"));
+ ASSERT_FALSE(c.name_exists("r11"));
+ ASSERT_FALSE(c.name_exists("r12"));
+}
+
+TEST_F(CrushWrapperTest, trim_roots_with_class) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "root");
+
+ int weight = 1;
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int item = 1;
+ c.insert_item(cct, item, weight, "osd.1", loc);
+ int cl = c.get_or_create_class_id("ssd");
+ c.class_map[item] = cl;
+
+
+ int root_id = c.get_item_id("default");
+ int clone_id;
+ map<int32_t, map<int32_t, int32_t>> old_class_bucket;
+ map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> weights
+ set<int32_t> used_ids;
+
+ ASSERT_EQ(c.device_class_clone(root_id, cl, old_class_bucket, used_ids,
+ &clone_id, &cmap_item_weight), 0);
+
+ ASSERT_TRUE(c.name_exists("default"));
+ ASSERT_TRUE(c.name_exists("default~ssd"));
+ c.trim_roots_with_class(cct);
+ ASSERT_TRUE(c.name_exists("default"));
+ ASSERT_FALSE(c.name_exists("default~ssd"));
+}
+
+TEST_F(CrushWrapperTest, device_class_clone) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "host");
+ c.set_type_name(2, "root");
+
+ map<string,string> loc;
+ loc["host"] = "b1";
+ loc["root"] = "default";
+ int weight = 1;
+
+ int item = 1;
+ c.insert_item(cct, item, weight, "osd.1", loc);
+ int cl = c.get_or_create_class_id("ssd");
+ c.class_map[item] = cl;
+
+ int item_no_class = 2;
+ c.insert_item(cct, item_no_class, weight, "osd.2", loc);
+
+ c.reweight(cct);
+
+ map<int32_t, map<int32_t, int32_t>> old_class_bucket;
+ map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> weights
+ set<int32_t> used_ids;
+ int root_id = c.get_item_id("default");
+ int clone_id;
+ ASSERT_EQ(c.device_class_clone(root_id, cl, old_class_bucket, used_ids,
+ &clone_id, &cmap_item_weight), 0);
+ ASSERT_TRUE(c.name_exists("default~ssd"));
+ ASSERT_EQ(clone_id, c.get_item_id("default~ssd"));
+ ASSERT_TRUE(c.subtree_contains(clone_id, item));
+ ASSERT_FALSE(c.subtree_contains(clone_id, item_no_class));
+ ASSERT_TRUE(c.subtree_contains(root_id, item_no_class));
+ ASSERT_EQ(c.get_item_weightf(root_id), 2);
+ ASSERT_EQ(c.get_item_weightf(clone_id), 1);
+ // cloning again does nothing and returns the existing one
+ int other_clone_id;
+ ASSERT_EQ(c.device_class_clone(root_id, cl, old_class_bucket, used_ids,
+ &other_clone_id, &cmap_item_weight), 0);
+ ASSERT_EQ(clone_id, other_clone_id);
+ // invalid arguments
+ ASSERT_EQ(c.device_class_clone(12345, cl, old_class_bucket, used_ids,
+ &other_clone_id, &cmap_item_weight), -ECHILD);
+ ASSERT_EQ(c.device_class_clone(root_id, 12345, old_class_bucket, used_ids,
+ &other_clone_id, &cmap_item_weight), -EBADF);
+}
+
+TEST_F(CrushWrapperTest, split_id_class) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "root");
+
+ int weight = 1;
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int item = 1;
+ c.insert_item(cct, item, weight, "osd.1", loc);
+ int class_id = c.get_or_create_class_id("ssd");
+ c.class_map[item] = class_id;
+
+ map<int32_t, map<int32_t, int32_t>> old_class_bucket;
+ map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> weights
+ set<int32_t> used_ids;
+ int item_id = c.get_item_id("default");
+ int clone_id;
+ ASSERT_EQ(c.device_class_clone(item_id, class_id, old_class_bucket, used_ids,
+ &clone_id, &cmap_item_weight), 0);
+ int retrieved_item_id;
+ int retrieved_class_id;
+ ASSERT_EQ(c.split_id_class(clone_id, &retrieved_item_id, &retrieved_class_id), 0);
+ ASSERT_EQ(item_id, retrieved_item_id);
+ ASSERT_EQ(class_id, retrieved_class_id);
+
+ ASSERT_EQ(c.split_id_class(item_id, &retrieved_item_id, &retrieved_class_id), 0);
+ ASSERT_EQ(item_id, retrieved_item_id);
+ ASSERT_EQ(-1, retrieved_class_id);
+}
+
+TEST_F(CrushWrapperTest, populate_classes) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "root");
+
+ int weight = 1;
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int item = 1;
+ c.insert_item(cct, item, weight, "osd.1", loc);
+ int class_id = c.get_or_create_class_id("ssd");
+ c.class_map[item] = class_id;
+
+ map<int32_t, map<int32_t, int32_t>> old_class_bucket;
+ ASSERT_EQ(c.populate_classes(old_class_bucket), 0);
+
+ ASSERT_TRUE(c.name_exists("default~ssd"));
+
+ old_class_bucket = c.class_bucket;
+ ASSERT_EQ(c.populate_classes(old_class_bucket), 0);
+ ASSERT_EQ(old_class_bucket, c.class_bucket);
+}
+
+TEST_F(CrushWrapperTest, remove_class_name) {
+ CrushWrapper c;
+ c.create();
+
+ ASSERT_EQ(-ENOENT, c.remove_class_name("ssd"));
+ ASSERT_GE(0, c.get_or_create_class_id("ssd"));
+ ASSERT_EQ(0, c.remove_class_name("ssd"));
+ ASSERT_EQ(-ENOENT, c.remove_class_name("ssd"));
+}
+
+TEST_F(CrushWrapperTest, try_remap_rule) {
+ // build a simple 2 level map
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(0, "osd");
+ c.set_type_name(1, "host");
+ c.set_type_name(2, "rack");
+ c.set_type_name(3, "root");
+ int bno;
+ int r = c.add_bucket(0, CRUSH_BUCKET_STRAW2,
+ CRUSH_HASH_DEFAULT, 3, 0, NULL,
+ NULL, &bno);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(-1, bno);
+ c.set_item_name(bno, "default");
+
+ c.set_max_devices(20);
+
+ //JSONFormatter jf(true);
+
+ map<string,string> loc;
+ loc["host"] = "foo";
+ loc["rack"] = "a";
+ loc["root"] = "default";
+ c.insert_item(cct, 0, 1, "osd.0", loc);
+ c.insert_item(cct, 1, 1, "osd.1", loc);
+ c.insert_item(cct, 2, 1, "osd.2", loc);
+
+ loc.clear();
+ loc["host"] = "bar";
+ loc["rack"] = "a";
+ loc["root"] = "default";
+ c.insert_item(cct, 3, 1, "osd.3", loc);
+ c.insert_item(cct, 4, 1, "osd.4", loc);
+ c.insert_item(cct, 5, 1, "osd.5", loc);
+
+ loc.clear();
+ loc["host"] = "baz";
+ loc["rack"] = "b";
+ loc["root"] = "default";
+ c.insert_item(cct, 6, 1, "osd.6", loc);
+ c.insert_item(cct, 7, 1, "osd.7", loc);
+ c.insert_item(cct, 8, 1, "osd.8", loc);
+
+ loc.clear();
+ loc["host"] = "qux";
+ loc["rack"] = "b";
+ loc["root"] = "default";
+ c.insert_item(cct, 9, 1, "osd.9", loc);
+ c.insert_item(cct, 10, 1, "osd.10", loc);
+ c.insert_item(cct, 11, 1, "osd.11", loc);
+ c.finalize();
+
+ loc.clear();
+ loc["host"] = "bif";
+ loc["rack"] = "c";
+ loc["root"] = "default";
+ c.insert_item(cct, 12, 1, "osd.12", loc);
+ c.insert_item(cct, 13, 1, "osd.13", loc);
+ c.insert_item(cct, 14, 1, "osd.14", loc);
+ c.finalize();
+
+ loc.clear();
+ loc["host"] = "pop";
+ loc["rack"] = "c";
+ loc["root"] = "default";
+ c.insert_item(cct, 15, 1, "osd.15", loc);
+ c.insert_item(cct, 16, 1, "osd.16", loc);
+ c.insert_item(cct, 17, 1, "osd.17", loc);
+ c.finalize();
+
+ //c.dump(&jf);
+ //jf.flush(cout);
+
+ // take + emit
+ {
+ }
+
+ // take + choose device + emit
+ {
+ cout << "take + choose + emit" << std::endl;
+ ostringstream err;
+ int rule = c.add_simple_rule("one", "default", "osd", "",
+ "firstn", 0, &err);
+ ASSERT_EQ(rule, 0);
+
+ vector<int> orig = { 0, 3, 9 };
+ set<int> overfull = { 3 };
+ vector<int> underfull = { 0, 2, 5, 8, 11 };
+ vector<int> more_underfull = {};
+ vector<int> out;
+ int r = c.try_remap_rule(cct, rule, 3,
+ overfull, underfull, more_underfull,
+ orig, &out);
+ cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(3u, out.size());
+ ASSERT_EQ(0, out[0]);
+ ASSERT_EQ(2, out[1]);
+ ASSERT_EQ(9, out[2]);
+
+ // make sure we cope with dups between underfull and future values in orig
+ underfull = {9, 0, 2, 5};
+ orig = {1, 3, 9};
+
+ r = c.try_remap_rule(cct, rule, 3,
+ overfull, underfull, more_underfull,
+ orig, &out);
+ cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(3u, out.size());
+ ASSERT_EQ(1, out[0]);
+ ASSERT_EQ(0, out[1]);
+ ASSERT_EQ(9, out[2]);
+ //
+ // Check that more_underfull is used when underfull runs out
+ orig = { 0, 3, 9 };
+ overfull = { 3, 9 };
+ underfull = { 2 };
+ more_underfull = { 5, 8, 11 };
+ r = c.try_remap_rule(cct, rule, 3,
+ overfull, underfull, more_underfull,
+ orig, &out);
+ cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(3u, out.size());
+ ASSERT_EQ(0, out[0]);
+ ASSERT_EQ(2, out[1]);
+ ASSERT_EQ(5, out[2]);
+ }
+
+ // chooseleaf
+ {
+ cout << "take + chooseleaf + emit" << std::endl;
+ ostringstream err;
+ int rule = c.add_simple_rule("two", "default", "host", "",
+ "firstn", 0, &err);
+ ASSERT_EQ(rule, 1);
+
+ vector<int> orig = { 0, 3, 9 };
+ set<int> overfull = { 3 };
+ vector<int> underfull = { 0, 2, 5, 8, 11 };
+ vector<int> more_underfull = { };
+ vector<int> out;
+ int r = c.try_remap_rule(cct, rule, 3,
+ overfull, underfull, more_underfull,
+ orig, &out);
+ cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(3u, out.size());
+ ASSERT_EQ(0, out[0]);
+ ASSERT_EQ(5, out[1]);
+ ASSERT_EQ(9, out[2]);
+ }
+
+ // choose + choose
+ {
+ cout << "take + choose + choose + choose + emit" << std::endl;
+ int rule = c.add_rule(2, 5, 0);
+ ASSERT_EQ(2, rule);
+ c.set_rule_step_take(rule, 0, bno);
+ c.set_rule_step_choose_indep(rule, 1, 2, 2);
+ c.set_rule_step_choose_indep(rule, 2, 2, 1);
+ c.set_rule_step_choose_indep(rule, 3, 1, 0);
+ c.set_rule_step_emit(rule, 4);
+
+ vector<int> orig = { 0, 3, 16, 12 };
+ set<int> overfull = { 3, 12 };
+ vector<int> underfull = { 6, 7, 9, 3, 0, 1, 15, 16, 13, 2, 5, 8, 11 };
+ vector<int> more_underfull = { };
+ vector<int> out;
+ int r = c.try_remap_rule(cct, rule, 3,
+ overfull, underfull, more_underfull,
+ orig, &out);
+ cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(4u, out.size());
+ ASSERT_EQ(0, out[0]);
+ ASSERT_EQ(5, out[1]);
+ ASSERT_EQ(16, out[2]);
+ ASSERT_EQ(13, out[3]);
+
+ orig.pop_back();
+ out.clear();
+ r = c.try_remap_rule(cct, rule, 3,
+ overfull, underfull, more_underfull,
+ orig, &out);
+ cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(3u, out.size());
+ ASSERT_EQ(0, out[0]);
+ ASSERT_EQ(5, out[1]);
+ ASSERT_EQ(16, out[2]);
+ }
+}
+
+// Local Variables:
+// compile-command: "cd ../../../build ; make -j4 unittest_crush_wrapper && valgrind --tool=memcheck bin/unittest_crush_wrapper"
+// End:
diff --git a/src/test/crush/crush-choose-args-expected-one-more-0.txt b/src/test/crush/crush-choose-args-expected-one-more-0.txt
new file mode 100644
index 000000000..3e9eb68b7
--- /dev/null
+++ b/src/test/crush/crush-choose-args-expected-one-more-0.txt
@@ -0,0 +1,75 @@
+# begin crush map
+tunable choose_local_tries 0
+tunable choose_local_fallback_tries 0
+tunable choose_total_tries 50
+tunable chooseleaf_descend_once 1
+tunable chooseleaf_vary_r 1
+tunable chooseleaf_stable 1
+tunable straw_calc_version 1
+tunable allowed_bucket_algs 54
+
+# devices
+device 0 osd.0
+device 1 osd.1
+
+# types
+type 0 osd
+type 1 host
+type 2 chassis
+type 3 rack
+type 4 row
+type 5 pdu
+type 6 pod
+type 7 room
+type 8 datacenter
+type 9 zone
+type 10 region
+type 11 root
+
+# buckets
+host HOST {
+ id -2 # do not change unnecessarily
+ # weight 6.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item osd.0 weight 3.00000
+ item osd.1 weight 3.00000
+}
+root default {
+ id -1 # do not change unnecessarily
+ # weight 6.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item HOST weight 6.00000
+}
+
+# rules
+rule replicated_rule {
+ id 0
+ type replicated
+ step take default
+ step choose firstn 0 type osd
+ step emit
+}
+
+# choose_args
+choose_args 0 {
+ {
+ bucket_id -1
+ weight_set [
+ [ 2.00000 ]
+ [ 1.00000 ]
+ ]
+ ids [ -10 ]
+ }
+ {
+ bucket_id -2
+ weight_set [
+ [ 2.00000 0.00000 ]
+ [ 1.00000 0.00000 ]
+ ]
+ ids [ -20 1 ]
+ }
+}
+
+# end crush map
diff --git a/src/test/crush/crush-choose-args-expected-one-more-3.txt b/src/test/crush/crush-choose-args-expected-one-more-3.txt
new file mode 100644
index 000000000..f09d4a1ee
--- /dev/null
+++ b/src/test/crush/crush-choose-args-expected-one-more-3.txt
@@ -0,0 +1,75 @@
+# begin crush map
+tunable choose_local_tries 0
+tunable choose_local_fallback_tries 0
+tunable choose_total_tries 50
+tunable chooseleaf_descend_once 1
+tunable chooseleaf_vary_r 1
+tunable chooseleaf_stable 1
+tunable straw_calc_version 1
+tunable allowed_bucket_algs 54
+
+# devices
+device 0 osd.0
+device 1 osd.1
+
+# types
+type 0 osd
+type 1 host
+type 2 chassis
+type 3 rack
+type 4 row
+type 5 pdu
+type 6 pod
+type 7 room
+type 8 datacenter
+type 9 zone
+type 10 region
+type 11 root
+
+# buckets
+host HOST {
+ id -2 # do not change unnecessarily
+ # weight 6.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item osd.0 weight 3.00000
+ item osd.1 weight 3.00000
+}
+root default {
+ id -1 # do not change unnecessarily
+ # weight 6.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item HOST weight 6.00000
+}
+
+# rules
+rule replicated_rule {
+ id 0
+ type replicated
+ step take default
+ step choose firstn 0 type osd
+ step emit
+}
+
+# choose_args
+choose_args 0 {
+ {
+ bucket_id -1
+ weight_set [
+ [ 5.00000 ]
+ [ 5.00000 ]
+ ]
+ ids [ -10 ]
+ }
+ {
+ bucket_id -2
+ weight_set [
+ [ 2.00000 3.00000 ]
+ [ 2.00000 3.00000 ]
+ ]
+ ids [ -20 1 ]
+ }
+}
+
+# end crush map
diff --git a/src/test/crush/crush.cc b/src/test/crush/crush.cc
new file mode 100644
index 000000000..2d87958b3
--- /dev/null
+++ b/src/test/crush/crush.cc
@@ -0,0 +1,660 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank <info@inktank.com>
+ *
+ * LGPL-2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <gtest/gtest.h>
+#include <iostream>
+#include <memory>
+#include <set>
+
+#include "common/ceph_argparse.h"
+#include "common/common_init.h"
+#include "include/stringify.h"
+
+#include "crush/CrushWrapper.h"
+#include "osd/osd_types.h"
+
+using namespace std;
+
+std::unique_ptr<CrushWrapper> build_indep_map(CephContext *cct, int num_rack,
+ int num_host, int num_osd)
+{
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ c->create();
+
+ c->set_type_name(5, "root");
+ c->set_type_name(4, "row");
+ c->set_type_name(3, "rack");
+ c->set_type_name(2, "chasis");
+ c->set_type_name(1, "host");
+ c->set_type_name(0, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ 5, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int osd = 0;
+ for (int r=0; r<num_rack; ++r) {
+ loc["rack"] = string("rack-") + stringify(r);
+ for (int h=0; h<num_host; ++h) {
+ loc["host"] = string("host-") + stringify(r) + string("-") + stringify(h);
+ for (int o=0; o<num_osd; ++o, ++osd) {
+ c->insert_item(cct, osd, 1.0, string("osd.") + stringify(osd), loc);
+ }
+ }
+ }
+ int ret;
+ int ruleno = 0;
+ ret = c->add_rule(ruleno, 4, 123);
+ ceph_assert(ret == ruleno);
+ ret = c->set_rule_step(ruleno, 0, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 10, 0);
+ ceph_assert(ret == 0);
+ ret = c->set_rule_step(ruleno, 1, CRUSH_RULE_TAKE, rootno, 0);
+ ceph_assert(ret == 0);
+ ret = c->set_rule_step(ruleno, 2, CRUSH_RULE_CHOOSELEAF_INDEP, CRUSH_CHOOSE_N, 1);
+ ceph_assert(ret == 0);
+ ret = c->set_rule_step(ruleno, 3, CRUSH_RULE_EMIT, 0, 0);
+ ceph_assert(ret == 0);
+ c->set_rule_name(ruleno, "data");
+
+ c->finalize();
+
+ if (false) {
+ Formatter *f = Formatter::create("json-pretty");
+ f->open_object_section("crush_map");
+ c->dump(f);
+ f->close_section();
+ f->flush(cout);
+ delete f;
+ }
+
+ return c;
+}
+
+int get_num_dups(const vector<int>& v)
+{
+ std::set<int> s;
+ int dups = 0;
+ for (auto n : v) {
+ if (s.count(n))
+ ++dups;
+ else if (n != CRUSH_ITEM_NONE)
+ s.insert(n);
+ }
+ return dups;
+}
+
+class CRUSHTest : public ::testing::Test
+{
+public:
+ void SetUp() final
+ {
+ CephInitParameters params(CEPH_ENTITY_TYPE_CLIENT);
+ cct = common_preinit(params, CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ }
+ void TearDown() final
+ {
+ cct->put();
+ cct = nullptr;
+ }
+protected:
+ CephContext *cct = nullptr;
+};
+
+TEST_F(CRUSHTest, indep_toosmall) {
+ std::unique_ptr<CrushWrapper> c(build_indep_map(cct, 1, 3, 1));
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+ c->dump_tree(&cout, NULL);
+
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 5, weight, 0);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(2, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+}
+
+TEST_F(CRUSHTest, indep_basic) {
+ std::unique_ptr<CrushWrapper> c(build_indep_map(cct, 3, 3, 3));
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+ c->dump_tree(&cout, NULL);
+
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 5, weight, 0);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(0, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+}
+
+TEST_F(CRUSHTest, indep_out_alt) {
+ std::unique_ptr<CrushWrapper> c(build_indep_map(cct, 3, 3, 3));
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+
+ // mark a bunch of osds out
+ int num = 3*3*3;
+ for (int i=0; i<num / 2; ++i)
+ weight[i*2] = 0;
+ c->dump_tree(&cout, NULL);
+
+ // need more retries to get 9/9 hosts for x in 0..99
+ c->set_choose_total_tries(100);
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 9, weight, 0);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(0, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+}
+
+TEST_F(CRUSHTest, indep_out_contig) {
+ std::unique_ptr<CrushWrapper> c(build_indep_map(cct, 3, 3, 3));
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+
+ // mark a bunch of osds out
+ int num = 3*3*3;
+ for (int i=0; i<num / 3; ++i)
+ weight[i] = 0;
+ c->dump_tree(&cout, NULL);
+
+ c->set_choose_total_tries(100);
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 7, weight, 0);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(1, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+}
+
+
+TEST_F(CRUSHTest, indep_out_progressive) {
+ std::unique_ptr<CrushWrapper> c(build_indep_map(cct, 3, 3, 3));
+ c->set_choose_total_tries(100);
+ vector<__u32> tweight(c->get_max_devices(), 0x10000);
+ c->dump_tree(&cout, NULL);
+
+ int tchanged = 0;
+ for (int x = 1; x < 5; ++x) {
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+
+ std::map<int,unsigned> pos;
+ vector<int> prev;
+ for (unsigned i=0; i<weight.size(); ++i) {
+ vector<int> out;
+ c->do_rule(0, x, out, 7, weight, 0);
+ cout << "(" << i << "/" << weight.size() << " out) "
+ << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned k=0; k<out.size(); ++k) {
+ if (out[k] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(0, get_num_dups(out));
+
+ // make sure nothing moved
+ int moved = 0;
+ int changed = 0;
+ for (unsigned j=0; j<out.size(); ++j) {
+ if (i && out[j] != prev[j]) {
+ ++changed;
+ ++tchanged;
+ }
+ if (out[j] == CRUSH_ITEM_NONE) {
+ continue;
+ }
+ if (i && pos.count(out[j])) {
+ // result shouldn't have moved position
+ if (j != pos[out[j]]) {
+ cout << " " << out[j] << " moved from " << pos[out[j]] << " to " << j << std::endl;
+ ++moved;
+ }
+ //ASSERT_EQ(j, pos[out[j]]);
+ }
+ }
+ if (moved || changed)
+ cout << " " << moved << " moved, " << changed << " changed" << std::endl;
+ ASSERT_LE(moved, 1);
+ ASSERT_LE(changed, 3);
+
+ // mark another osd out
+ weight[i] = 0;
+ prev = out;
+ pos.clear();
+ for (unsigned j=0; j<out.size(); ++j) {
+ if (out[j] != CRUSH_ITEM_NONE)
+ pos[out[j]] = j;
+ }
+ }
+ }
+ cout << tchanged << " total changed" << std::endl;
+
+}
+
+TEST_F(CRUSHTest, straw_zero) {
+ // zero weight items should have no effect on placement.
+
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int n = 5;
+ int items[n], weights[n];
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ weights[i] = 0x10000 * (n-i-1);
+ }
+
+ c->set_max_devices(n);
+
+ string root_name0("root0");
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, root_name0));
+
+ string name0("rule0");
+ int rule0 = c->add_simple_rule(name0, root_name0, "osd", "",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(0, rule0);
+
+ string root_name1("root1");
+ int root1;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n-1, items, weights, &root1));
+ EXPECT_EQ(0, c->set_item_name(root1, root_name1));
+
+ string name1("rule1");
+ int rule1 = c->add_simple_rule(name1, root_name1, "osd", "",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(1, rule1);
+
+ c->finalize();
+
+ vector<unsigned> reweight(n, 0x10000);
+ for (int i=0; i<10000; ++i) {
+ vector<int> out0, out1;
+ c->do_rule(rule0, i, out0, 1, reweight, 0);
+ ASSERT_EQ(1u, out0.size());
+ c->do_rule(rule1, i, out1, 1, reweight, 0);
+ ASSERT_EQ(1u, out1.size());
+ ASSERT_EQ(out0[0], out1[0]);
+ //cout << i << "\t" << out0 << "\t" << out1 << std::endl;
+ }
+}
+
+TEST_F(CRUSHTest, straw_same) {
+ // items with the same weight should map about the same as items
+ // with very similar weights.
+ //
+ // give the 0 vector a paired stair pattern, with dup weights. note
+ // that the original straw flaw does not appear when there are 2 of
+ // the initial weight, but it does when there is just 1.
+ //
+ // give the 1 vector a similar stair pattern, but make the same
+ // steps weights slightly different (no dups). this works.
+ //
+ // compare the result and verify that the resulting mapping is
+ // almost identical.
+
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int n = 10;
+ int items[n], weights[n];
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ weights[i] = 0x10000 * ((i+1)/2 + 1);
+ }
+
+ c->set_max_devices(n);
+
+ string root_name0("root0");
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, root_name0));
+
+ string name0("rule0");
+ int rule0 = c->add_simple_rule(name0, root_name0, "osd", "",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(0, rule0);
+
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ weights[i] = 0x10000 * ((i+1)/2 + 1) + (i%2)*100;
+ }
+
+ string root_name1("root1");
+ int root1;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights, &root1));
+ EXPECT_EQ(0, c->set_item_name(root1, root_name1));
+
+ string name1("rule1");
+ int rule1 = c->add_simple_rule(name1, root_name1, "osd", "",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(1, rule1);
+
+ if (0) {
+ crush_bucket_straw *sb0 = reinterpret_cast<crush_bucket_straw*>(c->get_crush_map()->buckets[-1-root0]);
+ crush_bucket_straw *sb1 = reinterpret_cast<crush_bucket_straw*>(c->get_crush_map()->buckets[-1-root1]);
+
+ for (int i=0; i<n; ++i) {
+ cout << i
+ << "\t" << sb0->item_weights[i]
+ << "\t" << sb1->item_weights[i]
+ << "\t"
+ << "\t" << sb0->straws[i]
+ << "\t" << sb1->straws[i]
+ << std::endl;
+ }
+ }
+
+ if (0) {
+ JSONFormatter jf(true);
+ jf.open_object_section("crush");
+ c->dump(&jf);
+ jf.close_section();
+ jf.flush(cout);
+ }
+
+ c->finalize();
+
+ vector<int> sum0(n, 0), sum1(n, 0);
+ vector<unsigned> reweight(n, 0x10000);
+ int different = 0;
+ int max = 100000;
+ for (int i=0; i<max; ++i) {
+ vector<int> out0, out1;
+ c->do_rule(rule0, i, out0, 1, reweight, 0);
+ ASSERT_EQ(1u, out0.size());
+ c->do_rule(rule1, i, out1, 1, reweight, 0);
+ ASSERT_EQ(1u, out1.size());
+ sum0[out0[0]]++;
+ sum1[out1[0]]++;
+ if (out0[0] != out1[0])
+ different++;
+ }
+ for (int i=0; i<n; ++i) {
+ cout << i
+ << "\t" << ((double)weights[i] / (double)weights[0])
+ << "\t" << sum0[i] << "\t" << ((double)sum0[i]/(double)sum0[0])
+ << "\t" << sum1[i] << "\t" << ((double)sum1[i]/(double)sum1[0])
+ << std::endl;
+ }
+ double ratio = ((double)different / (double)max);
+ cout << different << " of " << max << " = "
+ << ratio
+ << " different" << std::endl;
+ ASSERT_LT(ratio, .001);
+}
+
+double calc_straw2_stddev(int *weights, int n, bool verbose)
+{
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int items[n];
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ }
+
+ c->set_max_devices(n);
+
+ string root_name0("root0");
+ int root0;
+ crush_bucket *b0 = crush_make_bucket(c->get_crush_map(),
+ CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights);
+ crush_add_bucket(c->get_crush_map(), 0, b0, &root0);
+ c->set_item_name(root0, root_name0);
+
+ string name0("rule0");
+ int rule0 = c->add_simple_rule(name0, root_name0, "osd", "",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+
+ int sum[n];
+ double totalweight = 0;
+ vector<unsigned> reweight(n);
+ for (int i=0; i<n; ++i) {
+ sum[i] = 0;
+ reweight[i] = 0x10000;
+ totalweight += weights[i];
+ }
+ totalweight /= (double)0x10000;
+ double avgweight = totalweight / n;
+
+ c->finalize();
+
+ int total = 1000000;
+ for (int i=0; i<total; ++i) {
+ vector<int> out;
+ c->do_rule(rule0, i, out, 1, reweight, 0);
+ sum[out[0]]++;
+ }
+
+ double expected = (double)total / (double)n;
+ if (verbose)
+ cout << "expect\t\t\t" << expected << std::endl;
+ double stddev = 0;
+ double exptotal = 0;
+ if (verbose)
+ cout << "osd\tweight\tcount\tadjusted\n";
+ std::streamsize p = cout.precision();
+ cout << std::setprecision(4);
+ for (int i=0; i<n; ++i) {
+ double w = (double)weights[i] / (double)0x10000;
+ double adj = (double)sum[i] * avgweight / w;
+ stddev += (adj - expected) * (adj - expected);
+ exptotal += adj;
+ if (verbose)
+ cout << i
+ << "\t" << w
+ << "\t" << sum[i]
+ << "\t" << (int)adj
+ << std::endl;
+ }
+ cout << std::setprecision(p);
+ {
+ stddev = sqrt(stddev / (double)n);
+ if (verbose)
+ cout << "std dev " << stddev << std::endl;
+
+ double p = 1.0 / (double)n;
+ double estddev = sqrt(exptotal * p * (1.0 - p));
+ if (verbose)
+ cout << " vs " << estddev << "\t(expected)" << std::endl;
+ }
+ return stddev;
+}
+
+TEST_F(CRUSHTest, straw2_stddev)
+{
+ int n = 15;
+ int weights[n];
+ cout << "maxskew\tstddev\n";
+ for (double step = 1.0; step < 2; step += .25) {
+ int w = 0x10000;
+ for (int i = 0; i < n; ++i) {
+ weights[i] = w;
+ w *= step;
+ }
+ double stddev = calc_straw2_stddev(weights, n, true);
+ cout << ((double)weights[n-1]/(double)weights[0])
+ << "\t" << stddev << std::endl;
+ }
+}
+
+TEST_F(CRUSHTest, straw2_reweight) {
+ // when we adjust the weight of an item in a straw2 bucket,
+ // we should *only* see movement from or to that item, never
+ // between other items.
+ int weights[] = {
+ 0x10000,
+ 0x10000,
+ 0x20000,
+ 0x20000,
+ 0x30000,
+ 0x50000,
+ 0x8000,
+ 0x20000,
+ 0x10000,
+ 0x10000,
+ 0x20000,
+ 0x10000,
+ 0x10000,
+ 0x20000,
+ 0x300000,
+ 0x10000,
+ 0x20000
+ };
+ int n = 15;
+
+ std::unique_ptr<CrushWrapper> c(new CrushWrapper);
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int items[n];
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ //weights[i] = 0x10000;
+ }
+
+ c->set_max_devices(n);
+
+ string root_name0("root0");
+ int root0;
+ crush_bucket *b0 = crush_make_bucket(c->get_crush_map(),
+ CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights);
+ EXPECT_EQ(0, crush_add_bucket(c->get_crush_map(), 0, b0, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, root_name0));
+
+ string name0("rule0");
+ int rule0 = c->add_simple_rule(name0, root_name0, "osd", "",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(0, rule0);
+
+ int changed = 1;
+ weights[changed] = weights[changed] / 10 * (rand() % 10);
+
+ string root_name1("root1");
+ int root1;
+ crush_bucket *b1 = crush_make_bucket(c->get_crush_map(),
+ CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights);
+ EXPECT_EQ(0, crush_add_bucket(c->get_crush_map(), 0, b1, &root1));
+ EXPECT_EQ(0, c->set_item_name(root1, root_name1));
+
+ string name1("rule1");
+ int rule1 = c->add_simple_rule(name1, root_name1, "osd", "",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(1, rule1);
+
+ int sum[n];
+ double totalweight = 0;
+ vector<unsigned> reweight(n);
+ for (int i=0; i<n; ++i) {
+ sum[i] = 0;
+ reweight[i] = 0x10000;
+ totalweight += weights[i];
+ }
+ totalweight /= (double)0x10000;
+ double avgweight = totalweight / n;
+
+ c->finalize();
+
+ int total = 1000000;
+ for (int i=0; i<total; ++i) {
+ vector<int> out0, out1;
+ c->do_rule(rule0, i, out0, 1, reweight, 0);
+ ASSERT_EQ(1u, out0.size());
+
+ c->do_rule(rule1, i, out1, 1, reweight, 0);
+ ASSERT_EQ(1u, out1.size());
+
+ sum[out1[0]]++;
+ //sum[rand()%n]++;
+
+ if (out1[0] == changed) {
+ ASSERT_EQ(changed, out0[0]);
+ } else if (out0[0] != changed) {
+ ASSERT_EQ(out0[0], out1[0]);
+ }
+ }
+
+ double expected = (double)total / (double)n;
+ cout << "expect\t\t\t" << expected << std::endl;
+ double stddev = 0;
+ cout << "osd\tweight\tcount\tadjusted\n";
+ std::streamsize p = cout.precision();
+ cout << std::setprecision(4);
+ for (int i=0; i<n; ++i) {
+ double w = (double)weights[i] / (double)0x10000;
+ double adj = (double)sum[i] * avgweight / w;
+ stddev += (adj - expected) * (adj - expected);
+ cout << i
+ << "\t" << w
+ << "\t" << sum[i]
+ << "\t" << (int)adj
+ << std::endl;
+ }
+ cout << std::setprecision(p);
+ {
+ stddev = sqrt(stddev / (double)n);
+ cout << "std dev " << stddev << std::endl;
+
+ double p = 1.0 / (double)n;
+ double estddev = sqrt((double)total * p * (1.0 - p));
+ cout << " vs " << estddev << std::endl;
+ }
+}
diff --git a/src/test/crush/crush_weights.sh b/src/test/crush/crush_weights.sh
new file mode 100755
index 000000000..64f854541
--- /dev/null
+++ b/src/test/crush/crush_weights.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+source $(dirname $0)/../detect-build-env-vars.sh
+
+if [ `uname` = FreeBSD ]; then
+ SED=gsed
+else
+ SED=sed
+fi
+
+read -r -d '' cm <<'EOF'
+# devices
+device 0 device0
+device 1 device1
+device 2 device2
+device 3 device3
+device 4 device4
+# types
+type 0 osd
+type 1 domain
+type 2 pool
+# buckets
+domain root {
+ id -1 # do not change unnecessarily
+ # weight 5.00000
+ alg straw2
+ hash 0 # rjenkins1
+ item device0 weight 10.00000
+ item device1 weight 10.00000
+ item device2 weight 10.00000
+ item device3 weight 10.00000
+ item device4 weight 1.00000
+}
+# rules
+rule data {
+ id 0
+ type replicated
+ step take root
+ step choose firstn 0 type osd
+ step emit
+}
+EOF
+
+three=($(echo "$cm" | crushtool -c /dev/fd/0 --test --show-utilization \
+ --min-x 1 --max-x 1000000 --num-rep 3 | \
+ grep "device \(0\|4\)" | $SED -e 's/^.*stored : \([0-9]\+\).*$/\1/'))
+
+if test $(echo "scale=5; (10 - ${three[0]}/${three[1]}) < .75" | bc) = 1; then
+ echo 3 replicas weights better distributed than they should be. 1>&2
+ exit 1
+fi
+
+one=($(echo "$cm" | crushtool -c /dev/fd/0 --test --show-utilization \
+ --min-x 1 --max-x 1000000 --num-rep 1 | \
+ grep "device \(0\|4\)" | $SED -e 's/^.*stored : \([0-9]\+\).*$/\1/'))
+
+if test $(echo "scale=5; (10 - ${one[0]}/${one[1]}) > .1 || (10 - ${one[0]}/${one[1]}) < -.1" | bc) = 1; then
+ echo 1 replica not distributed as they should be. 1>&2
+ exit 1
+fi
diff --git a/src/test/crypto.cc b/src/test/crypto.cc
new file mode 100644
index 000000000..819d41c72
--- /dev/null
+++ b/src/test/crypto.cc
@@ -0,0 +1,342 @@
+#include <errno.h>
+#include <time.h>
+
+#include <boost/container/small_vector.hpp>
+
+#include "gtest/gtest.h"
+#include "include/types.h"
+#include "auth/Crypto.h"
+#include "common/Clock.h"
+#include "common/ceph_crypto.h"
+#include "common/ceph_context.h"
+#include "global/global_context.h"
+
+using namespace std;
+
+class CryptoEnvironment: public ::testing::Environment {
+public:
+ void SetUp() override {
+ ceph::crypto::init();
+ }
+};
+
+TEST(AES, ValidateSecret) {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+ int l;
+
+ for (l=0; l<16; l++) {
+ bufferptr bp(l);
+ int err;
+ err = h->validate_secret(bp);
+ EXPECT_EQ(-EINVAL, err);
+ }
+
+ for (l=16; l<50; l++) {
+ bufferptr bp(l);
+ int err;
+ err = h->validate_secret(bp);
+ EXPECT_EQ(0, err);
+ }
+}
+
+TEST(AES, Encrypt) {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+ char secret_s[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ bufferptr secret(secret_s, sizeof(secret_s));
+
+ unsigned char plaintext_s[] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ };
+ bufferlist plaintext;
+ plaintext.append((char *)plaintext_s, sizeof(plaintext_s));
+
+ bufferlist cipher;
+ std::string error;
+ CryptoKeyHandler *kh = h->get_key_handler(secret, error);
+ int r = kh->encrypt(plaintext, cipher, &error);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(error, "");
+
+ unsigned char want_cipher[] = {
+ 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
+ 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
+ 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
+ 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
+ };
+ char cipher_s[sizeof(want_cipher)];
+
+ ASSERT_EQ(sizeof(cipher_s), cipher.length());
+ cipher.cbegin().copy(sizeof(cipher_s), &cipher_s[0]);
+
+ int err;
+ err = memcmp(cipher_s, want_cipher, sizeof(want_cipher));
+ ASSERT_EQ(0, err);
+
+ delete kh;
+}
+
+TEST(AES, EncryptNoBl) {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+ char secret_s[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ bufferptr secret(secret_s, sizeof(secret_s));
+
+ const unsigned char plaintext[] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ };
+
+ std::string error;
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler(secret, error));
+
+ const CryptoKey::in_slice_t plain_slice { sizeof(plaintext), plaintext };
+
+ // we need to deduce size first
+ const CryptoKey::out_slice_t probe_slice { 0, nullptr };
+ const auto needed = kh->encrypt(plain_slice, probe_slice);
+ ASSERT_GE(needed, plain_slice.length);
+
+ boost::container::small_vector<
+ // FIXME?
+ //unsigned char, sizeof(plaintext) + kh->get_block_size()> buf;
+ unsigned char, sizeof(plaintext) + 16> buf(needed);
+ const CryptoKey::out_slice_t cipher_slice { needed, buf.data() };
+ const auto cipher_size = kh->encrypt(plain_slice, cipher_slice);
+ ASSERT_EQ(cipher_size, needed);
+
+ const unsigned char want_cipher[] = {
+ 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
+ 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
+ 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
+ 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
+ };
+
+ ASSERT_EQ(sizeof(want_cipher), cipher_size);
+
+ const int err = memcmp(buf.data(), want_cipher, sizeof(want_cipher));
+ ASSERT_EQ(0, err);
+}
+
+TEST(AES, Decrypt) {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+ char secret_s[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ bufferptr secret(secret_s, sizeof(secret_s));
+
+ unsigned char cipher_s[] = {
+ 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
+ 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
+ 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
+ 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
+ };
+ bufferlist cipher;
+ cipher.append((char *)cipher_s, sizeof(cipher_s));
+
+ unsigned char want_plaintext[] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ };
+ char plaintext_s[sizeof(want_plaintext)];
+
+ std::string error;
+ bufferlist plaintext;
+ CryptoKeyHandler *kh = h->get_key_handler(secret, error);
+ int r = kh->decrypt(cipher, plaintext, &error);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(error, "");
+
+ ASSERT_EQ(sizeof(plaintext_s), plaintext.length());
+ plaintext.cbegin().copy(sizeof(plaintext_s), &plaintext_s[0]);
+
+ int err;
+ err = memcmp(plaintext_s, want_plaintext, sizeof(want_plaintext));
+ ASSERT_EQ(0, err);
+
+ delete kh;
+}
+
+TEST(AES, DecryptNoBl) {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+ const char secret_s[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ bufferptr secret(secret_s, sizeof(secret_s));
+
+ const unsigned char ciphertext[] = {
+ 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
+ 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
+ 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
+ 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
+ };
+
+ const unsigned char want_plaintext[] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ };
+ constexpr static std::size_t plain_buf_size = \
+ CryptoKey::get_max_outbuf_size(sizeof(want_plaintext));
+ unsigned char plaintext[plain_buf_size];
+
+ std::string error;
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler(secret, error));
+
+ CryptoKey::in_slice_t cipher_slice { sizeof(ciphertext), ciphertext };
+ CryptoKey::out_slice_t plain_slice { sizeof(plaintext), plaintext };
+ const auto plain_size = kh->decrypt(cipher_slice, plain_slice);
+
+ ASSERT_EQ(plain_size, sizeof(want_plaintext));
+
+ const int err = memcmp(plaintext, want_plaintext, sizeof(plain_size));
+ ASSERT_EQ(0, err);
+}
+
+template <std::size_t TextSizeV>
+static void aes_loop_cephx() {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+
+ CryptoRandom random;
+
+ bufferptr secret(16);
+ random.get_bytes(secret.c_str(), secret.length());
+ std::string error;
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler(secret, error));
+
+ unsigned char plaintext[TextSizeV];
+ random.get_bytes(reinterpret_cast<char*>(plaintext), sizeof(plaintext));
+
+ const CryptoKey::in_slice_t plain_slice { sizeof(plaintext), plaintext };
+
+ // we need to deduce size first
+ const CryptoKey::out_slice_t probe_slice { 0, nullptr };
+ const auto needed = kh->encrypt(plain_slice, probe_slice);
+ ASSERT_GE(needed, plain_slice.length);
+
+ boost::container::small_vector<
+ // FIXME?
+ //unsigned char, sizeof(plaintext) + kh->get_block_size()> buf;
+ unsigned char, sizeof(plaintext) + 16> buf(needed);
+
+ std::size_t cipher_size;
+ for (std::size_t i = 0; i < 1000000; i++) {
+ const CryptoKey::out_slice_t cipher_slice { needed, buf.data() };
+ cipher_size = kh->encrypt(plain_slice, cipher_slice);
+ ASSERT_EQ(cipher_size, needed);
+ }
+}
+
+// These magics reflects Cephx's signature size. Please consult
+// CephxSessionHandler::_calc_signature() for more details.
+TEST(AES, LoopCephx) {
+ aes_loop_cephx<29>();
+}
+
+TEST(AES, LoopCephxV2) {
+ aes_loop_cephx<32>();
+}
+
+static void aes_loop(const std::size_t text_size) {
+ CryptoRandom random;
+
+ bufferptr secret(16);
+ random.get_bytes(secret.c_str(), secret.length());
+
+ bufferptr orig_plaintext(text_size);
+ random.get_bytes(orig_plaintext.c_str(), orig_plaintext.length());
+
+ bufferlist plaintext;
+ plaintext.append(orig_plaintext.c_str(), orig_plaintext.length());
+
+ for (int i=0; i<10000; i++) {
+ bufferlist cipher;
+ {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+
+ std::string error;
+ CryptoKeyHandler *kh = h->get_key_handler(secret, error);
+ int r = kh->encrypt(plaintext, cipher, &error);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(error, "");
+
+ delete kh;
+ }
+ plaintext.clear();
+
+ {
+ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
+ std::string error;
+ CryptoKeyHandler *ckh = h->get_key_handler(secret, error);
+ int r = ckh->decrypt(cipher, plaintext, &error);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(error, "");
+
+ delete ckh;
+ }
+ }
+
+ bufferlist orig;
+ orig.append(orig_plaintext);
+ ASSERT_EQ(orig, plaintext);
+}
+
+TEST(AES, Loop) {
+ aes_loop(256);
+}
+
+// These magics reflects Cephx's signature size. Please consult
+// CephxSessionHandler::_calc_signature() for more details.
+TEST(AES, Loop_29) {
+ aes_loop(29);
+}
+
+TEST(AES, Loop_32) {
+ aes_loop(32);
+}
+
+void aes_loopkey(const std::size_t text_size) {
+ CryptoRandom random;
+ bufferptr k(16);
+ random.get_bytes(k.c_str(), k.length());
+ CryptoKey key(CEPH_CRYPTO_AES, ceph_clock_now(), k);
+
+ bufferlist data;
+ bufferptr r(text_size);
+ random.get_bytes(r.c_str(), r.length());
+ data.append(r);
+
+ utime_t start = ceph_clock_now();
+ int n = 100000;
+
+ for (int i=0; i<n; ++i) {
+ bufferlist encoded;
+ string error;
+ int r = key.encrypt(g_ceph_context, data, encoded, &error);
+ ASSERT_EQ(r, 0);
+ }
+
+ utime_t end = ceph_clock_now();
+ utime_t dur = end - start;
+ cout << n << " encoded in " << dur << std::endl;
+}
+
+TEST(AES, LoopKey) {
+ aes_loopkey(128);
+}
+
+// These magics reflects Cephx's signature size. Please consult
+// CephxSessionHandler::_calc_signature() for more details.
+TEST(AES, LoopKey_29) {
+ aes_loopkey(29);
+}
+
+TEST(AES, LoopKey_32) {
+ aes_loopkey(32);
+}
diff --git a/src/test/crypto_init.cc b/src/test/crypto_init.cc
new file mode 100644
index 000000000..0ca4f6f14
--- /dev/null
+++ b/src/test/crypto_init.cc
@@ -0,0 +1,21 @@
+#include <errno.h>
+#include <time.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <vector>
+
+#include "include/types.h"
+#include "common/code_environment.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "include/msgr.h"
+#include "gtest/gtest.h"
+#include "auth/Crypto.h"
+#include "common/ceph_crypto.h"
+
+// TODO: ensure OpenSSL init
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/cxx11_client.cc b/src/test/cxx11_client.cc
new file mode 100644
index 000000000..c30ab5cfa
--- /dev/null
+++ b/src/test/cxx11_client.cc
@@ -0,0 +1,6 @@
+#include "include/buffer.h"
+
+// We might want to include here all our public headers.
+// Not any file residing in src/include has this status.
+
+int main() {}
diff --git a/src/test/daemon_config.cc b/src/test/daemon_config.cc
new file mode 100644
index 000000000..c9afe5483
--- /dev/null
+++ b/src/test/daemon_config.cc
@@ -0,0 +1,380 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "global/global_context.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/rados/librados.h"
+
+#include <errno.h>
+#include <sstream>
+#include <string>
+#include <string.h>
+
+#include <boost/lexical_cast.hpp>
+
+
+using namespace std;
+
+TEST(DaemonConfig, SimpleSet) {
+ int ret;
+ ret = g_ceph_context->_conf.set_val("log_graylog_port", "21");
+ ASSERT_EQ(0, ret);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ char *tmp = buf;
+ ret = g_ceph_context->_conf.get_val("log_graylog_port", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("21"), string(buf));
+ g_ceph_context->_conf.rm_val("log_graylog_port");
+}
+
+TEST(DaemonConfig, Substitution) {
+ int ret;
+ g_conf()._clear_safe_to_start_threads();
+ ret = g_ceph_context->_conf.set_val("host", "foo");
+ ASSERT_EQ(0, ret);
+ ret = g_ceph_context->_conf.set_val("public_network", "bar$host.baz");
+ ASSERT_EQ(0, ret);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ char *tmp = buf;
+ ret = g_ceph_context->_conf.get_val("public_network", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("barfoo.baz"), string(buf));
+}
+
+TEST(DaemonConfig, SubstitutionTrailing) {
+ int ret;
+ g_conf()._clear_safe_to_start_threads();
+ ret = g_ceph_context->_conf.set_val("host", "foo");
+ ASSERT_EQ(0, ret);
+ ret = g_ceph_context->_conf.set_val("public_network", "bar$host");
+ ASSERT_EQ(0, ret);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ char *tmp = buf;
+ ret = g_ceph_context->_conf.get_val("public_network", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("barfoo"), string(buf));
+}
+
+TEST(DaemonConfig, SubstitutionBraces) {
+ int ret;
+ g_conf()._clear_safe_to_start_threads();
+ ret = g_ceph_context->_conf.set_val("host", "foo");
+ ASSERT_EQ(0, ret);
+ ret = g_ceph_context->_conf.set_val("public_network", "bar${host}baz");
+ ASSERT_EQ(0, ret);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ char *tmp = buf;
+ ret = g_ceph_context->_conf.get_val("public_network", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("barfoobaz"), string(buf));
+}
+TEST(DaemonConfig, SubstitutionBracesTrailing) {
+ int ret;
+ g_conf()._clear_safe_to_start_threads();
+ ret = g_ceph_context->_conf.set_val("host", "foo");
+ ASSERT_EQ(0, ret);
+ ret = g_ceph_context->_conf.set_val("public_network", "bar${host}");
+ ASSERT_EQ(0, ret);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ char *tmp = buf;
+ ret = g_ceph_context->_conf.get_val("public_network", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("barfoo"), string(buf));
+}
+
+// config: variable substitution happen only once http://tracker.ceph.com/issues/7103
+TEST(DaemonConfig, SubstitutionMultiple) {
+ int ret;
+ ret = g_ceph_context->_conf.set_val("mon_host", "localhost");
+ ASSERT_EQ(0, ret);
+ ret = g_ceph_context->_conf.set_val("keyring", "$mon_host/$cluster.keyring,$mon_host/$cluster.mon.keyring");
+ ASSERT_EQ(0, ret);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ char buf[512];
+ memset(buf, 0, sizeof(buf));
+ char *tmp = buf;
+ ret = g_ceph_context->_conf.get_val("keyring", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("localhost/ceph.keyring,localhost/ceph.mon.keyring"), tmp);
+ ASSERT_TRUE(strchr(buf, '$') == NULL);
+}
+
+TEST(DaemonConfig, ArgV) {
+ g_conf()._clear_safe_to_start_threads();
+
+ int ret;
+ const char *argv[] = { "foo", "--log-graylog-port", "22",
+ "--key", "my-key", NULL };
+ size_t argc = (sizeof(argv) / sizeof(argv[0])) - 1;
+ auto args = argv_to_vec(argc, argv);
+ g_ceph_context->_conf.parse_argv(args);
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ char buf[128];
+ char *tmp = buf;
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("key", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("my-key"), string(buf));
+
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_graylog_port", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("22"), string(buf));
+
+ g_conf().set_safe_to_start_threads();
+}
+
+TEST(DaemonConfig, InjectArgs) {
+ int ret;
+ std::string injection("--log-graylog-port 56 --leveldb-max-open-files 42");
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(0, ret);
+
+ char buf[128];
+ char *tmp = buf;
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("leveldb_max_open_files", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("42"), string(buf));
+
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_graylog_port", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("56"), string(buf));
+
+ injection = "--log-graylog-port 57";
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(0, ret);
+ ret = g_ceph_context->_conf.get_val("log_graylog_port", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("57"), string(buf));
+}
+
+TEST(DaemonConfig, InjectArgsReject) {
+ int ret;
+ char buf[128];
+ char *tmp = buf;
+ char buf2[128];
+ char *tmp2 = buf2;
+
+ // We should complain about the garbage in the input
+ std::string injection("--random-garbage-in-injectargs 26 --log-graylog-port 28");
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(-EINVAL, ret);
+
+ // But, debug should still be set...
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_graylog_port", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("28"), string(buf));
+
+ // What's the current value of osd_data?
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("osd_data", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+
+ // Injectargs shouldn't let us change this, since it is a string-valued
+ // variable and there isn't an observer for it.
+ std::string injection2("--osd_data /tmp/some-other-directory --log-graylog-port 4");
+ ret = g_ceph_context->_conf.injectargs(injection2, &cout);
+ ASSERT_EQ(-EPERM, ret);
+
+ // It should be unchanged.
+ memset(buf2, 0, sizeof(buf2));
+ ret = g_ceph_context->_conf.get_val("osd_data", &tmp2, sizeof(buf2));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string(buf), string(buf2));
+
+ // We should complain about the missing arguments.
+ std::string injection3("--log-graylog-port 28 --debug_ms");
+ ret = g_ceph_context->_conf.injectargs(injection3, &cout);
+ ASSERT_EQ(-EINVAL, ret);
+}
+
+TEST(DaemonConfig, InjectArgsBooleans) {
+ int ret;
+ char buf[128];
+ char *tmp = buf;
+
+ // Change log_to_syslog
+ std::string injection("--log_to_syslog --log-graylog-port 28");
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(0, ret);
+
+ // log_to_syslog should be set...
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_to_syslog", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("true"), string(buf));
+
+ // Turn off log_to_syslog
+ injection = "--log_to_syslog=false --log-graylog-port 28";
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(0, ret);
+
+ // log_to_syslog should be cleared...
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_to_syslog", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("false"), string(buf));
+
+ // Turn on log_to_syslog
+ injection = "--log-graylog-port=1 --log_to_syslog=true --leveldb-max-open-files 40";
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(0, ret);
+
+ // log_to_syslog should be set...
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_to_syslog", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("true"), string(buf));
+
+ // parse error
+ injection = "--log-graylog-port 1 --log_to_syslog=falsey --leveldb-max-open-files 42";
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(-EINVAL, ret);
+
+ // log_to_syslog should still be set...
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_to_syslog", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("true"), string(buf));
+
+ // debug-ms should still become 42...
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("leveldb_max_open_files", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("42"), string(buf));
+}
+
+TEST(DaemonConfig, InjectArgsLogfile) {
+ int ret;
+ char tmpfile[PATH_MAX];
+ const char *tmpdir = getenv("TMPDIR");
+ if (!tmpdir)
+ tmpdir = "/tmp";
+ snprintf(tmpfile, sizeof(tmpfile), "%s/daemon_config_test.%d",
+ tmpdir, getpid());
+ std::string injection("--log_file ");
+ injection += tmpfile;
+ // We're allowed to change log_file because there is an observer.
+ ret = g_ceph_context->_conf.injectargs(injection, &cout);
+ ASSERT_EQ(0, ret);
+
+ // It should have taken effect.
+ char buf[128];
+ char *tmp = buf;
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("log_file", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string(buf), string(tmpfile));
+
+ // The logfile should exist.
+ ASSERT_EQ(0, access(tmpfile, R_OK));
+
+ // Let's turn off the logfile.
+ ret = g_ceph_context->_conf.set_val("log_file", "");
+ ASSERT_EQ(0, ret);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ ret = g_ceph_context->_conf.get_val("log_file", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string(""), string(buf));
+
+ // Clean up the garbage
+ unlink(tmpfile);
+}
+
+TEST(DaemonConfig, ThreadSafety1) {
+ int ret;
+ // Verify that we can't change this, since safe_to_start_threads has
+ // been set.
+ ret = g_ceph_context->_conf.set_val("osd_data", "");
+ ASSERT_EQ(-EPERM, ret);
+
+ g_conf()._clear_safe_to_start_threads();
+
+ // Ok, now we can change this. Since this is just a test, and there are no
+ // OSD threads running, we know changing osd_data won't actually blow up the
+ // world.
+ ret = g_ceph_context->_conf.set_val("osd_data", "/tmp/crazydata");
+ ASSERT_EQ(0, ret);
+
+ char buf[128];
+ char *tmp = buf;
+ memset(buf, 0, sizeof(buf));
+ ret = g_ceph_context->_conf.get_val("osd_data", &tmp, sizeof(buf));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(string("/tmp/crazydata"), string(buf));
+
+ g_conf()._clear_safe_to_start_threads();
+ ASSERT_EQ(0, ret);
+}
+
+TEST(DaemonConfig, InvalidIntegers) {
+ {
+ int ret = g_ceph_context->_conf.set_val("log_graylog_port", "rhubarb");
+ ASSERT_EQ(-EINVAL, ret);
+ }
+
+ {
+ int64_t max = std::numeric_limits<int64_t>::max();
+ string str = boost::lexical_cast<string>(max);
+ str = str + "999"; // some extra digits to take us out of bounds
+ int ret = g_ceph_context->_conf.set_val("log_graylog_port", str);
+ ASSERT_EQ(-EINVAL, ret);
+ }
+
+ g_ceph_context->_conf.rm_val("log_graylog_port");
+}
+
+TEST(DaemonConfig, InvalidFloats) {
+ {
+ double bad_value = 2 * (double)std::numeric_limits<float>::max();
+ string str = boost::lexical_cast<string>(-bad_value);
+ int ret = g_ceph_context->_conf.set_val("log_stop_at_utilization", str);
+ ASSERT_EQ(-EINVAL, ret);
+ }
+ {
+ double bad_value = 2 * (double)std::numeric_limits<float>::max();
+ string str = boost::lexical_cast<string>(bad_value);
+ int ret = g_ceph_context->_conf.set_val("log_stop_at_utilization", str);
+ ASSERT_EQ(-EINVAL, ret);
+ }
+ {
+ int ret = g_ceph_context->_conf.set_val("log_stop_at_utilization", "not a float");
+ ASSERT_EQ(-EINVAL, ret);
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../../build ; \
+ * make unittest_daemon_config && ./bin/unittest_daemon_config"
+ * End:
+ */
diff --git a/src/test/debian-strech/Dockerfile.in b/src/test/debian-strech/Dockerfile.in
new file mode 100644
index 000000000..a27526aed
--- /dev/null
+++ b/src/test/debian-strech/Dockerfile.in
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2015 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+# Environment variables are substituted via envsubst(1)
+#
+# user_id=$(id -u)
+# os_version= the desired REPOSITORY TAG
+#
+FROM debian:%%os_version%%
+
+COPY install-deps.sh /root/
+RUN mkdir /root/debian
+COPY debian /root/debian/
+RUN apt-get update
+# build dependencies
+RUN cd /root ; ./install-deps.sh
+# development tools
+RUN apt-get install -y sudo ccache valgrind gdb python-virtualenv gdisk kpartx jq xmlstarlet
+RUN if test %%USER%% != root ; then useradd -M --uid %%user_id%% %%USER%% && echo '%%USER%% ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ; fi
diff --git a/src/test/debian-strech/debian b/src/test/debian-strech/debian
new file mode 120000
index 000000000..dfdd39e6b
--- /dev/null
+++ b/src/test/debian-strech/debian
@@ -0,0 +1 @@
+../../../debian \ No newline at end of file
diff --git a/src/test/debian-strech/install-deps.sh b/src/test/debian-strech/install-deps.sh
new file mode 120000
index 000000000..fc9c78b27
--- /dev/null
+++ b/src/test/debian-strech/install-deps.sh
@@ -0,0 +1 @@
+../../../install-deps.sh \ No newline at end of file
diff --git a/src/test/detect-build-env-vars.sh b/src/test/detect-build-env-vars.sh
new file mode 100644
index 000000000..9118de5f3
--- /dev/null
+++ b/src/test/detect-build-env-vars.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+if [ -n "$CEPH_BUILD_DIR" ] && [ -n "$CEPH_ROOT" ] && [ -n "$CEPH_BIN" ] && [ -n "$CEPH_LIB" ]; then
+ echo "Enivronment Variables Already Set"
+elif [ -e CMakeCache.txt ]; then
+ echo "Environment Variables Not All Set, Detected Build System CMake"
+ echo "Setting Environment Variables"
+ export CEPH_ROOT=`grep ceph_SOURCE_DIR CMakeCache.txt | cut -d "=" -f 2`
+ export CEPH_BUILD_DIR=`pwd`
+ export CEPH_BIN=$CEPH_BUILD_DIR/bin
+ export CEPH_LIB=$CEPH_BUILD_DIR/lib
+ export PATH=$CEPH_BIN:$PATH
+ export LD_LIBRARY_PATH=$CEPH_LIB
+else
+ echo "Please execute this command out of the proper directory"
+ exit 1
+fi
+
+
diff --git a/src/test/direct_messenger/CMakeLists.txt b/src/test/direct_messenger/CMakeLists.txt
new file mode 100644
index 000000000..6ca56946e
--- /dev/null
+++ b/src/test/direct_messenger/CMakeLists.txt
@@ -0,0 +1,5 @@
+# unittest_direct_messenger
+#add_library(QueueStrategy OBJECT QueueStrategy.cc)
+#add_executable(unittest_direct_messenger $<TARGET_OBJECTS:QueueStrategy> test_direct_messenger.cc DirectMessenger.cc)
+#add_ceph_unittest(unittest_direct_messenger)
+#target_link_libraries(unittest_direct_messenger global)
diff --git a/src/test/direct_messenger/DirectMessenger.cc b/src/test/direct_messenger/DirectMessenger.cc
new file mode 100644
index 000000000..3aeff4fee
--- /dev/null
+++ b/src/test/direct_messenger/DirectMessenger.cc
@@ -0,0 +1,252 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "DirectMessenger.h"
+#include "DispatchStrategy.h"
+
+
+class DirectConnection : public Connection {
+ /// sent messages are dispatched here
+ DispatchStrategy *const dispatchers;
+
+ /// the connection that will be attached to outgoing messages, so that replies
+ /// can be dispatched back to the sender. the pointer is atomic for
+ /// thread-safety between mark_down() and send_message(). no reference is held
+ /// on this Connection to avoid cyclical refs. we don't need a reference
+ /// because its owning DirectMessenger will mark both connections down (and
+ /// clear this pointer) before dropping its own reference
+ std::atomic<Connection*> reply_connection{nullptr};
+
+ private:
+ FRIEND_MAKE_REF(DirectConnection);
+ DirectConnection(CephContext *cct, DirectMessenger *m,
+ DispatchStrategy *dispatchers)
+ : Connection(cct, m),
+ dispatchers(dispatchers)
+ {}
+
+ public:
+ /// sets the Connection that will receive replies to outgoing messages
+ void set_direct_reply_connection(ConnectionRef conn);
+
+ /// return true if a peer connection exists
+ bool is_connected() override;
+
+ /// pass the given message directly to our dispatchers
+ int send_message(Message *m) override;
+
+ /// release our pointer to the peer connection. later calls to is_connected()
+ /// will return false, and send_message() will fail with -ENOTCONN
+ void mark_down() override;
+
+ /// noop - keepalive messages are not needed within a process
+ void send_keepalive() override {}
+
+ /// noop - reconnect/recovery semantics are not needed within a process
+ void mark_disposable() override {}
+};
+
+void DirectConnection::set_direct_reply_connection(ConnectionRef conn)
+{
+ reply_connection.store(conn.get());
+}
+
+bool DirectConnection::is_connected()
+{
+ // true between calls to set_direct_reply_connection() and mark_down()
+ return reply_connection.load() != nullptr;
+}
+
+int DirectConnection::send_message(Message *m)
+{
+ // read reply_connection atomically and take a reference
+ ConnectionRef conn = reply_connection.load();
+ if (!conn) {
+ m->put();
+ return -ENOTCONN;
+ }
+ // attach reply_connection to the Message, so that calls to
+ // m->get_connection()->send_message() can be dispatched back to the sender
+ m->set_connection(conn);
+
+ dispatchers->ds_dispatch(m);
+ return 0;
+}
+
+void DirectConnection::mark_down()
+{
+ Connection *conn = reply_connection.load();
+ if (!conn) {
+ return; // already marked down
+ }
+ if (!reply_connection.compare_exchange_weak(conn, nullptr)) {
+ return; // lost the race to mark down
+ }
+ // called only once to avoid loops
+ conn->mark_down();
+}
+
+
+static ConnectionRef create_loopback(DirectMessenger *m,
+ entity_name_t name,
+ DispatchStrategy *dispatchers)
+{
+ auto loopback = ceph::make_ref<DirectConnection>(m->cct, m, dispatchers);
+ // loopback replies go to itself
+ loopback->set_direct_reply_connection(loopback);
+ loopback->set_peer_type(name.type());
+ loopback->set_features(CEPH_FEATURES_ALL);
+ return loopback;
+}
+
+DirectMessenger::DirectMessenger(CephContext *cct, entity_name_t name,
+ string mname, uint64_t nonce,
+ DispatchStrategy *dispatchers)
+ : SimplePolicyMessenger(cct, name, mname, nonce),
+ dispatchers(dispatchers),
+ loopback_connection(create_loopback(this, name, dispatchers))
+{
+ dispatchers->set_messenger(this);
+}
+
+DirectMessenger::~DirectMessenger()
+{
+}
+
+int DirectMessenger::set_direct_peer(DirectMessenger *peer)
+{
+ if (get_myinst() == peer->get_myinst()) {
+ return -EADDRINUSE; // must have a different entity instance
+ }
+ peer_inst = peer->get_myinst();
+
+ // allocate a Connection that dispatches to the peer messenger
+ auto direct_connection = ceph::make_ref<DirectConnection>(cct, peer, peer->dispatchers.get());
+
+ direct_connection->set_peer_addr(peer_inst.addr);
+ direct_connection->set_peer_type(peer_inst.name.type());
+ direct_connection->set_features(CEPH_FEATURES_ALL);
+
+ // if set_direct_peer() was already called on the peer messenger, we can
+ // finish by attaching their connections. if not, the later call to
+ // peer->set_direct_peer() will attach their connection to ours
+ auto connection = peer->get_connection(get_myinst());
+ if (connection) {
+ auto p = static_cast<DirectConnection*>(connection.get());
+
+ p->set_direct_reply_connection(direct_connection);
+ direct_connection->set_direct_reply_connection(p);
+ }
+
+ peer_connection = std::move(direct_connection);
+ return 0;
+}
+
+int DirectMessenger::bind(const entity_addr_t &bind_addr)
+{
+ if (peer_connection) {
+ return -EINVAL; // can't change address after sharing it with the peer
+ }
+ set_myaddr(bind_addr);
+ loopback_connection->set_peer_addr(bind_addr);
+ return 0;
+}
+
+int DirectMessenger::client_bind(const entity_addr_t &bind_addr)
+{
+ // same as bind
+ return bind(bind_addr);
+}
+
+int DirectMessenger::start()
+{
+ if (!peer_connection) {
+ return -EINVAL; // did not connect to a peer
+ }
+ if (started) {
+ return -EINVAL; // already started
+ }
+
+ dispatchers->start();
+ return SimplePolicyMessenger::start();
+}
+
+int DirectMessenger::shutdown()
+{
+ if (!started) {
+ return -EINVAL; // not started
+ }
+
+ mark_down_all();
+ peer_connection.reset();
+ loopback_connection.reset();
+
+ dispatchers->shutdown();
+ SimplePolicyMessenger::shutdown();
+ sem.Put(); // signal wait()
+ return 0;
+}
+
+void DirectMessenger::wait()
+{
+ sem.Get(); // wait on signal from shutdown()
+ dispatchers->wait();
+}
+
+ConnectionRef DirectMessenger::get_connection(const entity_inst_t& dst)
+{
+ if (dst == peer_inst) {
+ return peer_connection;
+ }
+ if (dst == get_myinst()) {
+ return loopback_connection;
+ }
+ return nullptr;
+}
+
+ConnectionRef DirectMessenger::get_loopback_connection()
+{
+ return loopback_connection;
+}
+
+int DirectMessenger::send_message(Message *m, const entity_inst_t& dst)
+{
+ auto conn = get_connection(dst);
+ if (!conn) {
+ m->put();
+ return -ENOTCONN;
+ }
+ return conn->send_message(m);
+}
+
+void DirectMessenger::mark_down(const entity_addr_t& addr)
+{
+ ConnectionRef conn;
+ if (addr == peer_inst.addr) {
+ conn = peer_connection;
+ } else if (addr == get_myaddr_legacy()) {
+ conn = loopback_connection;
+ }
+ if (conn) {
+ conn->mark_down();
+ }
+}
+
+void DirectMessenger::mark_down_all()
+{
+ if (peer_connection) {
+ peer_connection->mark_down();
+ }
+ loopback_connection->mark_down();
+}
diff --git a/src/test/direct_messenger/DirectMessenger.h b/src/test/direct_messenger/DirectMessenger.h
new file mode 100644
index 000000000..710fcfb37
--- /dev/null
+++ b/src/test/direct_messenger/DirectMessenger.h
@@ -0,0 +1,98 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_MSG_DIRECTMESSENGER_H
+#define CEPH_MSG_DIRECTMESSENGER_H
+
+#include "msg/SimplePolicyMessenger.h"
+#include "common/Semaphore.h"
+
+
+class DispatchStrategy;
+
+/**
+ * DirectMessenger provides a direct path between two messengers
+ * within a process. A pair of DirectMessengers share their
+ * DispatchStrategy with each other, and calls to send_message()
+ * forward the message directly to the other.
+ *
+ * This is for testing and i/o injection only, and cannot be used
+ * for normal messengers with ms_type.
+ */
+class DirectMessenger : public SimplePolicyMessenger {
+ private:
+ /// strategy for local dispatch
+ std::unique_ptr<DispatchStrategy> dispatchers;
+ /// peer instance for comparison in get_connection()
+ entity_inst_t peer_inst;
+ /// connection that sends to the peer's dispatchers
+ ConnectionRef peer_connection;
+ /// connection that sends to my own dispatchers
+ ConnectionRef loopback_connection;
+ /// semaphore for signalling wait() from shutdown()
+ Semaphore sem;
+
+ public:
+ DirectMessenger(CephContext *cct, entity_name_t name,
+ string mname, uint64_t nonce,
+ DispatchStrategy *dispatchers);
+ ~DirectMessenger();
+
+ /// attach to a peer messenger. must be called before start()
+ int set_direct_peer(DirectMessenger *peer);
+
+
+ // Messenger interface
+
+ /// sets the addr. must not be called after set_direct_peer() or start()
+ int bind(const entity_addr_t& bind_addr) override;
+
+ /// sets the addr. must not be called after set_direct_peer() or start()
+ int client_bind(const entity_addr_t& bind_addr) override;
+
+ /// starts dispatchers
+ int start() override;
+
+ /// breaks connections, stops dispatchers, and unblocks callers of wait()
+ int shutdown() override;
+
+ /// blocks until shutdown() completes
+ void wait() override;
+
+ /// returns a connection to the peer instance, a loopback connection to our
+ /// own instance, or null if not connected
+ ConnectionRef get_connection(const entity_inst_t& dst) override;
+
+ /// returns a loopback connection that dispatches to this messenger
+ ConnectionRef get_loopback_connection() override;
+
+ /// dispatches a message to the peer instance if connected
+ int send_message(Message *m, const entity_inst_t& dst) override;
+
+ /// mark down the connection for the given address
+ void mark_down(const entity_addr_t& a) override;
+
+ /// mark down all connections
+ void mark_down_all() override;
+
+
+ // unimplemented Messenger interface
+ void set_addr_unknowns(const entity_addr_t &addr) override {}
+ void set_addr(const entity_addr_t &addr) override {}
+ int get_dispatch_queue_len() override { return 0; }
+ double get_dispatch_queue_max_age(utime_t now) override { return 0; }
+ void set_cluster_protocol(int p) override {}
+};
+
+#endif
diff --git a/src/test/direct_messenger/DispatchStrategy.h b/src/test/direct_messenger/DispatchStrategy.h
new file mode 100644
index 000000000..4c9726ed6
--- /dev/null
+++ b/src/test/direct_messenger/DispatchStrategy.h
@@ -0,0 +1,37 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 CohortFS, LLC
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef DISPATCH_STRATEGY_H
+#define DISPATCH_STRATEGY_H
+
+#include "msg/Message.h"
+
+class Messenger;
+
+class DispatchStrategy
+{
+protected:
+ Messenger *msgr = nullptr;
+public:
+ DispatchStrategy() {}
+ Messenger *get_messenger() { return msgr; }
+ void set_messenger(Messenger *_msgr) { msgr = _msgr; }
+ virtual void ds_dispatch(Message *m) = 0;
+ virtual void shutdown() = 0;
+ virtual void start() = 0;
+ virtual void wait() = 0;
+ virtual ~DispatchStrategy() {}
+};
+
+#endif /* DISPATCH_STRATEGY_H */
diff --git a/src/test/direct_messenger/FastStrategy.h b/src/test/direct_messenger/FastStrategy.h
new file mode 100644
index 000000000..001ff4004
--- /dev/null
+++ b/src/test/direct_messenger/FastStrategy.h
@@ -0,0 +1,35 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 CohortFS, LLC
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#ifndef FAST_STRATEGY_H
+#define FAST_STRATEGY_H
+#include "DispatchStrategy.h"
+
+class FastStrategy : public DispatchStrategy {
+public:
+ FastStrategy() {}
+ void ds_dispatch(Message *m) override {
+ msgr->ms_fast_preprocess(m);
+ if (msgr->ms_can_fast_dispatch(m))
+ msgr->ms_fast_dispatch(m);
+ else
+ msgr->ms_deliver_dispatch(m);
+ }
+ void shutdown() override {}
+ void start() override {}
+ void wait() override {}
+ virtual ~FastStrategy() {}
+};
+#endif /* FAST_STRATEGY_H */
diff --git a/src/test/direct_messenger/QueueStrategy.cc b/src/test/direct_messenger/QueueStrategy.cc
new file mode 100644
index 000000000..342494c5a
--- /dev/null
+++ b/src/test/direct_messenger/QueueStrategy.cc
@@ -0,0 +1,107 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 CohortFS, LLC
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <string>
+#include "QueueStrategy.h"
+#define dout_subsys ceph_subsys_ms
+#include "common/debug.h"
+
+QueueStrategy::QueueStrategy(int _n_threads)
+ : n_threads(_n_threads),
+ stop(false),
+ mqueue(),
+ disp_threads()
+{
+}
+
+void QueueStrategy::ds_dispatch(Message *m) {
+ msgr->ms_fast_preprocess(m);
+ if (msgr->ms_can_fast_dispatch(m)) {
+ msgr->ms_fast_dispatch(m);
+ return;
+ }
+ std::lock_guard l{lock};
+ mqueue.push_back(*m);
+ if (disp_threads.size()) {
+ if (! disp_threads.empty()) {
+ QSThread *thrd = &disp_threads.front();
+ disp_threads.pop_front();
+ thrd->cond.notify_all();
+ }
+ }
+}
+
+void QueueStrategy::entry(QSThread *thrd)
+{
+ for (;;) {
+ ceph::ref_t<Message> m;
+ std::unique_lock l{lock};
+ for (;;) {
+ if (! mqueue.empty()) {
+ m = ceph::ref_t<Message>(&mqueue.front(), false);
+ mqueue.pop_front();
+ break;
+ }
+ if (stop)
+ break;
+ disp_threads.push_front(*thrd);
+ thrd->cond.wait(l);
+ }
+ l.unlock();
+ if (stop) {
+ if (!m) break;
+ continue;
+ }
+ get_messenger()->ms_deliver_dispatch(m);
+ }
+}
+
+void QueueStrategy::shutdown()
+{
+ QSThread *thrd;
+ std::lock_guard l{lock};
+ stop = true;
+ while (disp_threads.size()) {
+ thrd = &(disp_threads.front());
+ disp_threads.pop_front();
+ thrd->cond.notify_all();
+ }
+}
+
+void QueueStrategy::wait()
+{
+ std::unique_lock l{lock};
+ ceph_assert(stop);
+ for (auto& thread : threads) {
+ l.unlock();
+
+ // join outside of lock
+ thread->join();
+
+ l.lock();
+ }
+}
+
+void QueueStrategy::start()
+{
+ ceph_assert(!stop);
+ std::lock_guard l{lock};
+ threads.reserve(n_threads);
+ for (int ix = 0; ix < n_threads; ++ix) {
+ std::string thread_name = "ms_qs_";
+ thread_name.append(std::to_string(ix));
+ auto thrd = std::make_unique<QSThread>(this);
+ thrd->create(thread_name.c_str());
+ threads.emplace_back(std::move(thrd));
+ }
+}
diff --git a/src/test/direct_messenger/QueueStrategy.h b/src/test/direct_messenger/QueueStrategy.h
new file mode 100644
index 000000000..b7f6df85d
--- /dev/null
+++ b/src/test/direct_messenger/QueueStrategy.h
@@ -0,0 +1,63 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 CohortFS, LLC
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#ifndef QUEUE_STRATEGY_H
+#define QUEUE_STRATEGY_H
+
+#include <vector>
+#include <memory>
+#include <boost/intrusive/list.hpp>
+#include "DispatchStrategy.h"
+#include "msg/Messenger.h"
+
+namespace bi = boost::intrusive;
+
+class QueueStrategy : public DispatchStrategy {
+ ceph::mutex lock = ceph::make_mutex("QueueStrategy::lock");
+ const int n_threads;
+ bool stop;
+
+ Message::Queue mqueue;
+
+ class QSThread : public Thread {
+ public:
+ bi::list_member_hook<> thread_q;
+ QueueStrategy *dq;
+ ceph::condition_variable cond;
+ explicit QSThread(QueueStrategy *dq) : thread_q(), dq(dq) {}
+ void* entry() {
+ dq->entry(this);
+ return NULL;
+ }
+
+ typedef bi::list< QSThread,
+ bi::member_hook< QSThread,
+ bi::list_member_hook<>,
+ &QSThread::thread_q > > Queue;
+ };
+
+ std::vector<std::unique_ptr<QSThread>> threads; //< all threads
+ QSThread::Queue disp_threads; //< waiting threads
+
+public:
+ explicit QueueStrategy(int n_threads);
+ void ds_dispatch(Message *m) override;
+ void shutdown() override;
+ void start() override;
+ void wait() override;
+ void entry(QSThread *thrd);
+ virtual ~QueueStrategy() {}
+};
+#endif /* QUEUE_STRATEGY_H */
diff --git a/src/test/direct_messenger/test_direct_messenger.cc b/src/test/direct_messenger/test_direct_messenger.cc
new file mode 100644
index 000000000..311fce161
--- /dev/null
+++ b/src/test/direct_messenger/test_direct_messenger.cc
@@ -0,0 +1,436 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+
+#include "DirectMessenger.h"
+#include "FastStrategy.h"
+#include "QueueStrategy.h"
+#include "messages/MPing.h"
+
+
+/// mock dispatcher that calls the given callback
+class MockDispatcher : public Dispatcher {
+ std::function<void(Message*)> callback;
+ public:
+ MockDispatcher(CephContext *cct, std::function<void(Message*)> callback)
+ : Dispatcher(cct), callback(std::move(callback)) {}
+ bool ms_handle_reset(Connection *con) override { return false; }
+ void ms_handle_remote_reset(Connection *con) override {}
+ bool ms_handle_refused(Connection *con) override { return false; }
+ bool ms_dispatch(Message *m) override {
+ callback(m);
+ m->put();
+ return true;
+ }
+};
+
+/// test synchronous dispatch of messenger and connection interfaces
+TEST(DirectMessenger, SyncDispatch)
+{
+ auto cct = g_ceph_context;
+
+ // use FastStrategy for synchronous dispatch
+ DirectMessenger client(cct, entity_name_t::CLIENT(1),
+ "client", 0, new FastStrategy());
+ DirectMessenger server(cct, entity_name_t::CLIENT(2),
+ "server", 0, new FastStrategy());
+
+ ASSERT_EQ(0, client.set_direct_peer(&server));
+ ASSERT_EQ(0, server.set_direct_peer(&client));
+
+ bool got_request = false;
+ bool got_reply = false;
+
+ MockDispatcher client_dispatcher(cct, [&] (Message *m) {
+ got_reply = true;
+ });
+ client.add_dispatcher_head(&client_dispatcher);
+
+ MockDispatcher server_dispatcher(cct, [&] (Message *m) {
+ got_request = true;
+ ASSERT_EQ(0, m->get_connection()->send_message(new MPing()));
+ });
+ server.add_dispatcher_head(&server_dispatcher);
+
+ ASSERT_EQ(0, client.start());
+ ASSERT_EQ(0, server.start());
+
+ // test DirectMessenger::send_message()
+ ASSERT_EQ(0, client.send_message(new MPing(), server.get_myinst()));
+ ASSERT_TRUE(got_request);
+ ASSERT_TRUE(got_reply);
+
+ // test DirectConnection::send_message()
+ {
+ got_request = false;
+ got_reply = false;
+ auto conn = client.get_connection(server.get_myinst());
+ ASSERT_EQ(0, conn->send_message(new MPing()));
+ ASSERT_TRUE(got_request);
+ ASSERT_TRUE(got_reply);
+ }
+
+ // test DirectMessenger::send_message() with loopback address
+ got_request = false;
+ got_reply = false;
+ ASSERT_EQ(0, client.send_message(new MPing(), client.get_myinst()));
+ ASSERT_FALSE(got_request); // server should never see this
+ ASSERT_TRUE(got_reply);
+
+ // test DirectConnection::send_message() with loopback address
+ {
+ got_request = false;
+ got_reply = false;
+ auto conn = client.get_connection(client.get_myinst());
+ ASSERT_EQ(0, conn->send_message(new MPing()));
+ ASSERT_FALSE(got_request); // server should never see this
+ ASSERT_TRUE(got_reply);
+ }
+
+ // test DirectConnection::send_message() with loopback connection
+ {
+ got_request = false;
+ got_reply = false;
+ auto conn = client.get_loopback_connection();
+ ASSERT_EQ(0, conn->send_message(new MPing()));
+ ASSERT_FALSE(got_request); // server should never see this
+ ASSERT_TRUE(got_reply);
+ }
+
+ ASSERT_EQ(0, client.shutdown());
+ client.wait();
+
+ ASSERT_EQ(0, server.shutdown());
+ server.wait();
+}
+
+/// test asynchronous dispatch of messenger and connection interfaces
+TEST(DirectMessenger, AsyncDispatch)
+{
+ auto cct = g_ceph_context;
+
+ // use QueueStrategy for async replies
+ DirectMessenger client(cct, entity_name_t::CLIENT(1),
+ "client", 0, new QueueStrategy(1));
+ DirectMessenger server(cct, entity_name_t::CLIENT(2),
+ "server", 0, new FastStrategy());
+
+ ASSERT_EQ(0, client.set_direct_peer(&server));
+ ASSERT_EQ(0, server.set_direct_peer(&client));
+
+ // condition variable to wait on ping reply
+ std::mutex mutex;
+ std::condition_variable cond;
+ bool done = false;
+
+ auto wait_for_reply = [&] {
+ std::unique_lock<std::mutex> lock(mutex);
+ while (!done) {
+ cond.wait(lock);
+ }
+ done = false; // clear for reuse
+ };
+
+ // client dispatcher signals the condition variable on reply
+ MockDispatcher client_dispatcher(cct, [&] (Message *m) {
+ std::lock_guard<std::mutex> lock(mutex);
+ done = true;
+ cond.notify_one();
+ });
+ client.add_dispatcher_head(&client_dispatcher);
+
+ MockDispatcher server_dispatcher(cct, [&] (Message *m) {
+ // hold the lock over the call to send_message() to prove that the client's
+ // dispatch is asynchronous. if it isn't, it will deadlock
+ std::lock_guard<std::mutex> lock(mutex);
+ ASSERT_EQ(0, m->get_connection()->send_message(new MPing()));
+ });
+ server.add_dispatcher_head(&server_dispatcher);
+
+ ASSERT_EQ(0, client.start());
+ ASSERT_EQ(0, server.start());
+
+ // test DirectMessenger::send_message()
+ ASSERT_EQ(0, client.send_message(new MPing(), server.get_myinst()));
+ wait_for_reply();
+
+ // test DirectConnection::send_message()
+ {
+ auto conn = client.get_connection(server.get_myinst());
+ ASSERT_EQ(0, conn->send_message(new MPing()));
+ }
+ wait_for_reply();
+
+ // test DirectMessenger::send_message() with loopback address
+ {
+ // hold the lock to test that loopback dispatch is asynchronous
+ std::lock_guard<std::mutex> lock(mutex);
+ ASSERT_EQ(0, client.send_message(new MPing(), client.get_myinst()));
+ }
+ wait_for_reply();
+
+ // test DirectConnection::send_message() with loopback address
+ {
+ auto conn = client.get_connection(client.get_myinst());
+ // hold the lock to test that loopback dispatch is asynchronous
+ std::lock_guard<std::mutex> lock(mutex);
+ ASSERT_EQ(0, conn->send_message(new MPing()));
+ }
+ wait_for_reply();
+
+ // test DirectConnection::send_message() with loopback connection
+ {
+ auto conn = client.get_loopback_connection();
+ // hold the lock to test that loopback dispatch is asynchronous
+ std::lock_guard<std::mutex> lock(mutex);
+ ASSERT_EQ(0, conn->send_message(new MPing()));
+ }
+ wait_for_reply();
+
+ ASSERT_EQ(0, client.shutdown());
+ client.wait();
+
+ ASSERT_EQ(0, server.shutdown());
+ server.wait();
+}
+
+/// test that wait() blocks until shutdown()
+TEST(DirectMessenger, WaitShutdown)
+{
+ auto cct = g_ceph_context;
+
+ // test wait() with both Queue- and FastStrategy
+ DirectMessenger client(cct, entity_name_t::CLIENT(1),
+ "client", 0, new QueueStrategy(1));
+ DirectMessenger server(cct, entity_name_t::CLIENT(2),
+ "server", 0, new FastStrategy());
+
+ ASSERT_EQ(0, client.set_direct_peer(&server));
+ ASSERT_EQ(0, server.set_direct_peer(&client));
+
+ ASSERT_EQ(0, client.start());
+ ASSERT_EQ(0, server.start());
+
+ std::atomic<bool> client_waiting{false};
+ std::atomic<bool> server_waiting{false};
+
+ // spawn threads to wait() on each of the messengers
+ std::thread client_thread([&] {
+ client_waiting = true;
+ client.wait();
+ client_waiting = false;
+ });
+ std::thread server_thread([&] {
+ server_waiting = true;
+ server.wait();
+ server_waiting = false;
+ });
+
+ // give them time to start
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+ ASSERT_TRUE(client_waiting);
+ ASSERT_TRUE(server_waiting);
+
+ // call shutdown to unblock the waiting threads
+ ASSERT_EQ(0, client.shutdown());
+ ASSERT_EQ(0, server.shutdown());
+
+ client_thread.join();
+ server_thread.join();
+
+ ASSERT_FALSE(client_waiting);
+ ASSERT_FALSE(server_waiting);
+}
+
+/// test connection and messenger interfaces after mark_down()
+TEST(DirectMessenger, MarkDown)
+{
+ auto cct = g_ceph_context;
+
+ DirectMessenger client(cct, entity_name_t::CLIENT(1),
+ "client", 0, new FastStrategy());
+ DirectMessenger server(cct, entity_name_t::CLIENT(2),
+ "server", 0, new FastStrategy());
+
+ ASSERT_EQ(0, client.set_direct_peer(&server));
+ ASSERT_EQ(0, server.set_direct_peer(&client));
+
+ ASSERT_EQ(0, client.start());
+ ASSERT_EQ(0, server.start());
+
+ auto client_to_server = client.get_connection(server.get_myinst());
+ auto server_to_client = server.get_connection(client.get_myinst());
+
+ ASSERT_TRUE(client_to_server->is_connected());
+ ASSERT_TRUE(server_to_client->is_connected());
+
+ // mark_down() breaks the connection on both sides
+ client_to_server->mark_down();
+
+ ASSERT_FALSE(client_to_server->is_connected());
+ ASSERT_EQ(-ENOTCONN, client_to_server->send_message(new MPing()));
+ ASSERT_EQ(-ENOTCONN, client.send_message(new MPing(), server.get_myinst()));
+
+ ASSERT_FALSE(server_to_client->is_connected());
+ ASSERT_EQ(-ENOTCONN, server_to_client->send_message(new MPing()));
+ ASSERT_EQ(-ENOTCONN, server.send_message(new MPing(), client.get_myinst()));
+
+ ASSERT_EQ(0, client.shutdown());
+ client.wait();
+
+ ASSERT_EQ(0, server.shutdown());
+ server.wait();
+}
+
+/// test connection and messenger interfaces after shutdown()
+TEST(DirectMessenger, SendShutdown)
+{
+ auto cct = g_ceph_context;
+
+ // put client on the heap so we can free it early
+ std::unique_ptr<DirectMessenger> client{
+ new DirectMessenger(cct, entity_name_t::CLIENT(1),
+ "client", 0, new FastStrategy())};
+ DirectMessenger server(cct, entity_name_t::CLIENT(2),
+ "server", 0, new FastStrategy());
+
+ ASSERT_EQ(0, client->set_direct_peer(&server));
+ ASSERT_EQ(0, server.set_direct_peer(client.get()));
+
+ ASSERT_EQ(0, client->start());
+ ASSERT_EQ(0, server.start());
+
+ const auto client_inst = client->get_myinst();
+ const auto server_inst = server.get_myinst();
+
+ auto client_to_server = client->get_connection(server_inst);
+ auto server_to_client = server.get_connection(client_inst);
+
+ ASSERT_TRUE(client_to_server->is_connected());
+ ASSERT_TRUE(server_to_client->is_connected());
+
+ // shut down the client to break connections
+ ASSERT_EQ(0, client->shutdown());
+ client->wait();
+
+ ASSERT_FALSE(client_to_server->is_connected());
+ ASSERT_EQ(-ENOTCONN, client_to_server->send_message(new MPing()));
+ ASSERT_EQ(-ENOTCONN, client->send_message(new MPing(), server_inst));
+
+ // free the client connection/messenger to test that calls to the server no
+ // longer try to dereference them
+ client_to_server.reset();
+ client.reset();
+
+ ASSERT_FALSE(server_to_client->is_connected());
+ ASSERT_EQ(-ENOTCONN, server_to_client->send_message(new MPing()));
+ ASSERT_EQ(-ENOTCONN, server.send_message(new MPing(), client_inst));
+
+ ASSERT_EQ(0, server.shutdown());
+ server.wait();
+}
+
+/// test connection and messenger interfaces after bind()
+TEST(DirectMessenger, Bind)
+{
+ auto cct = g_ceph_context;
+
+ DirectMessenger client(cct, entity_name_t::CLIENT(1),
+ "client", 0, new FastStrategy());
+ DirectMessenger server(cct, entity_name_t::CLIENT(2),
+ "server", 0, new FastStrategy());
+
+ entity_addr_t client_addr;
+ client_addr.set_family(AF_INET);
+ client_addr.set_port(1);
+
+ // client bind succeeds before set_direct_peer()
+ ASSERT_EQ(0, client.bind(client_addr));
+
+ ASSERT_EQ(0, client.set_direct_peer(&server));
+ ASSERT_EQ(0, server.set_direct_peer(&client));
+
+ // server bind fails after set_direct_peer()
+ entity_addr_t empty_addr;
+ ASSERT_EQ(-EINVAL, server.bind(empty_addr));
+
+ ASSERT_EQ(0, client.start());
+ ASSERT_EQ(0, server.start());
+
+ auto client_to_server = client.get_connection(server.get_myinst());
+ auto server_to_client = server.get_connection(client.get_myinst());
+
+ ASSERT_TRUE(client_to_server->is_connected());
+ ASSERT_TRUE(server_to_client->is_connected());
+
+ // no address in connection to server
+ ASSERT_EQ(empty_addr, client_to_server->get_peer_addr());
+ // bind address is reflected in connection to client
+ ASSERT_EQ(client_addr, server_to_client->get_peer_addr());
+
+ // mark_down() with bind address breaks the connection
+ server.mark_down(client_addr);
+
+ ASSERT_FALSE(client_to_server->is_connected());
+ ASSERT_FALSE(server_to_client->is_connected());
+
+ ASSERT_EQ(0, client.shutdown());
+ client.wait();
+
+ ASSERT_EQ(0, server.shutdown());
+ server.wait();
+}
+
+/// test connection and messenger interfaces before calls to set_direct_peer()
+TEST(DirectMessenger, StartWithoutPeer)
+{
+ auto cct = g_ceph_context;
+
+ DirectMessenger client(cct, entity_name_t::CLIENT(1),
+ "client", 0, new FastStrategy());
+ DirectMessenger server(cct, entity_name_t::CLIENT(2),
+ "server", 0, new FastStrategy());
+
+ // can't start until set_direct_peer()
+ ASSERT_EQ(-EINVAL, client.start());
+ ASSERT_EQ(-EINVAL, server.start());
+
+ ASSERT_EQ(0, client.set_direct_peer(&server));
+
+ // only client can start
+ ASSERT_EQ(0, client.start());
+ ASSERT_EQ(-EINVAL, server.start());
+
+ // client has a connection but can't send
+ auto conn = client.get_connection(server.get_myinst());
+ ASSERT_NE(nullptr, conn);
+ ASSERT_FALSE(conn->is_connected());
+ ASSERT_EQ(-ENOTCONN, conn->send_message(new MPing()));
+ ASSERT_EQ(-ENOTCONN, client.send_message(new MPing(), server.get_myinst()));
+
+ ASSERT_EQ(0, client.shutdown());
+ client.wait();
+}
+
+int main(int argc, char **argv)
+{
+ // command-line arguments
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_ANY,
+ CODE_ENVIRONMENT_DAEMON,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(cct.get());
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/docker-test-helper.sh b/src/test/docker-test-helper.sh
new file mode 100755
index 000000000..59c3b042f
--- /dev/null
+++ b/src/test/docker-test-helper.sh
@@ -0,0 +1,354 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2014, 2015 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+function get_image_name() {
+ local os_type=$1
+ local os_version=$2
+
+ echo ceph-$os_type-$os_version-$USER
+}
+
+function setup_container() {
+ local os_type=$1
+ local os_version=$2
+ local dockercmd=$3
+ local opts="$4"
+
+ # rm not valid here
+ opts=${opts//' --rm'};
+
+ local image=$(get_image_name $os_type $os_version)
+ local build=true
+ if $dockercmd images $image | grep --quiet "^$image " ; then
+ eval touch --date=$($dockercmd inspect $image | jq '.[0].Created') $image
+ found=$(find -L test/$os_type-$os_version/* -newer $image)
+ rm $image
+ if test -n "$found" ; then
+ $dockercmd rmi $image
+ else
+ build=false
+ fi
+ fi
+ if $build ; then
+ #
+ # In the dockerfile,
+ # replace environment variables %%FOO%% with their content
+ #
+ rm -fr dockerfile
+ cp --dereference --recursive test/$os_type-$os_version dockerfile
+ os_version=$os_version user_id=$(id -u) \
+ perl -p -e 's/%%(\w+)%%/$ENV{$1}/g' \
+ dockerfile/Dockerfile.in > dockerfile/Dockerfile
+ $dockercmd $opts build --tag=$image dockerfile
+ rm -fr dockerfile
+ fi
+}
+
+function get_upstream() {
+ git rev-parse --show-toplevel
+}
+
+function get_downstream() {
+ local os_type=$1
+ local os_version=$2
+
+ local image=$(get_image_name $os_type $os_version)
+ local upstream=$(get_upstream)
+ local dir=$(dirname $upstream)
+ echo "$dir/$image"
+}
+
+function setup_downstream() {
+ local os_type=$1
+ local os_version=$2
+ local ref=$3
+
+ local image=$(get_image_name $os_type $os_version)
+ local upstream=$(get_upstream)
+ local dir=$(dirname $upstream)
+ local downstream=$(get_downstream $os_type $os_version)
+
+ (
+ cd $dir
+ if ! test -d $downstream ; then
+ # Inspired by https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir
+ mkdir -p $downstream/.git || return 1
+ for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache
+ do
+ case $x in
+ */*)
+ mkdir -p "$downstream/.git/$x"
+ ;;
+ esac
+ ln -s "$upstream/.git/$x" "$downstream/.git/$x"
+ done
+ cp "$upstream/.git/HEAD" "$downstream/.git/HEAD"
+ fi
+ cd $downstream
+ git reset --hard $ref || return 1
+ git submodule sync --recursive || return 1
+ git submodule update --force --init --recursive || return 1
+ )
+}
+
+function run_in_docker() {
+ local os_type=$1
+ shift
+ local os_version=$1
+ shift
+ local ref=$1
+ shift
+ local dockercmd=$1
+ shift
+ local opts="$1"
+ shift
+ local script=$1
+
+ setup_downstream $os_type $os_version $ref || return 1
+ setup_container $os_type $os_version $dockercmd "$opts" || return 1
+ local downstream=$(get_downstream $os_type $os_version)
+ local image=$(get_image_name $os_type $os_version)
+ local upstream=$(get_upstream)
+ local ccache
+ mkdir -p $HOME/.ccache
+ ccache="--volume $HOME/.ccache:$HOME/.ccache"
+ user="--user $USER"
+ local cmd="$dockercmd run $opts --name $image --privileged $ccache"
+ cmd+=" --volume $downstream:$downstream"
+ cmd+=" --volume $upstream:$upstream"
+ if test "$dockercmd" = "podman" ; then
+ cmd+=" --userns=keep-id"
+ fi
+ local status=0
+ if test "$script" = "SHELL" ; then
+ echo Running: $cmd --tty --interactive --workdir $downstream $user $image bash
+ $cmd --tty --interactive --workdir $downstream $user $image bash
+ else
+ echo Running: $cmd --workdir $downstream $user $image "$@"
+ if ! $cmd --workdir $downstream $user $image "$@" ; then
+ status=1
+ fi
+ fi
+ return $status
+}
+
+function remove_all() {
+ local os_type=$1
+ local os_version=$2
+ local dockercmd=$3
+ local image=$(get_image_name $os_type $os_version)
+
+ $dockercmd rm $image
+ $dockercmd rmi $image
+}
+
+function usage() {
+ cat <<EOF
+Run commands within Ceph sources, in a container. Use podman if available,
+docker if not.
+$0 [options] command args ...
+
+ [-h|--help] display usage
+ [--verbose] trace all shell lines
+
+ [--os-type type] docker image repository (centos, ubuntu, etc.)
+ (defaults to ubuntu)
+ [--os-version version] docker image tag (7 for centos, 16.04 for ubuntu, etc.)
+ (defaults to 16.04)
+ [--ref gitref] git reset --hard gitref before running the command
+ (defaults to git rev-parse HEAD)
+ [--all types+versions] list of docker image repositories and tags
+
+ [--shell] run an interactive shell in the container
+ [--remove-all] remove the container and the image for the specified types+versions
+ [--no-rm] don't remove the container when finished
+
+ [--opts options] run the container with 'options'
+
+docker-test.sh must be run from a Ceph clone and it will run the
+command in a container, using a copy of the clone so that long running
+commands such as make check are not disturbed while development
+continues. Here is a sample use case including an interactive session
+and running a unit test:
+
+ $ grep PRETTY_NAME /etc/os-release
+ PRETTY_NAME="Ubuntu 16.04.7 LTS"
+ $ test/docker-test.sh --os-type centos --os-version 7 --shell
+ HEAD is now at 1caee81 autotools: add --enable-docker
+ bash-4.2$ pwd
+ /srv/ceph/ceph-centos-7
+ bash-4.2$ cat /etc/redhat-release
+ CentOS Linux release 7.6.1810 (Core)
+ bash-4.2$
+ $ time test/docker-test.sh --os-type centos --os-version 7 unittest_str_map
+ HEAD is now at 1caee81 autotools: add --enable-docker
+ Running main() from gtest_main.cc
+ [==========] Running 2 tests from 1 test case.
+ [----------] Global test environment set-up.
+ [----------] 2 tests from str_map
+ [ RUN ] str_map.json
+ [ OK ] str_map.json (1 ms)
+ [ RUN ] str_map.plaintext
+ [ OK ] str_map.plaintext (0 ms)
+ [----------] 2 tests from str_map (1 ms total)
+
+ [----------] Global test environment tear-down
+ [==========] 2 tests from 1 test case ran. (1 ms total)
+ [ PASSED ] 2 tests.
+
+ real 0m3.759s
+ user 0m0.074s
+ sys 0m0.051s
+
+The --all argument is a bash associative array literal listing the
+operating system version for each operating system type. For instance
+
+ docker-test.sh --all '([ubuntu]="16.04 17.04" [centos]="7")'
+
+is strictly equivalent to
+
+ docker-test.sh --os-type ubuntu --os-version 16.04
+ docker-test.sh --os-type ubuntu --os-version 17.04
+ docker-test.sh --os-type centos --os-version 7
+
+The --os-type and --os-version must be exactly as displayed by docker images:
+
+ $ docker images
+ REPOSITORY TAG IMAGE ID ...
+ centos 7 87e5b6b3ccc1 ...
+ ubuntu 16.04 6b4e8a7373fe ...
+
+The --os-type value can be any string in the REPOSITORY column, the --os-version
+can be any string in the TAG column.
+
+The --shell and --remove actions are mutually exclusive.
+
+Run make check in centos 7
+docker-test.sh --os-type centos --os-version 7 -- make check
+
+Run make check on a giant
+docker-test.sh --ref giant -- make check
+
+Run an interactive shell and set resolv.conf to use 172.17.42.1
+docker-test.sh --opts --dns=172.17.42.1 --shell
+
+Run make check on centos 7, ubuntu 16.04 and ubuntu 17.04
+docker-test.sh --all '([ubuntu]="16.04 17.04" [centos]="7")' -- make check
+EOF
+}
+
+function main_docker() {
+ local dockercmd="docker"
+ if type podman > /dev/null; then
+ dockercmd="podman"
+ fi
+
+ if ! $dockercmd ps > /dev/null 2>&1 ; then
+ echo "docker not available: $0"
+ return 0
+ fi
+
+ local temp
+ temp=$(getopt -o scht:v:o:a:r: --long remove-all,verbose,shell,no-rm,help,os-type:,os-version:,opts:,all:,ref: -n $0 -- "$@") || return 1
+
+ eval set -- "$temp"
+
+ local os_type=ubuntu
+ local os_version=16.04
+ local all
+ local remove=false
+ local shell=false
+ local opts
+ local ref=$(git rev-parse HEAD)
+ local no-rm=false
+
+ while true ; do
+ case "$1" in
+ --remove-all)
+ remove=true
+ shift
+ ;;
+ --verbose)
+ set -xe
+ PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: '
+ shift
+ ;;
+ -s|--shell)
+ shell=true
+ shift
+ ;;
+ -h|--help)
+ usage
+ return 0
+ ;;
+ -t|--os-type)
+ os_type=$2
+ shift 2
+ ;;
+ -v|--os-version)
+ os_version=$2
+ shift 2
+ ;;
+ -o|--opts)
+ opts="$2"
+ shift 2
+ ;;
+ -a|--all)
+ all="$2"
+ shift 2
+ ;;
+ -r|--ref)
+ ref="$2"
+ shift 2
+ ;;
+ --no-rm)
+ no-rm=true
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ echo "unexpected argument $1"
+ return 1
+ ;;
+ esac
+ done
+
+ if test -z "$all" ; then
+ all="([$os_type]=\"$os_version\")"
+ fi
+
+ declare -A os_type2versions
+ eval os_type2versions="$all"
+
+ if ! $no-rm ; then
+ opts+=" --rm"
+ fi
+
+ for os_type in ${!os_type2versions[@]} ; do
+ for os_version in ${os_type2versions[$os_type]} ; do
+ if $remove ; then
+ remove_all $os_type $os_version $dockercmd || return 1
+ elif $shell ; then
+ run_in_docker $os_type $os_version $ref $dockercmd "$opts" SHELL || return 1
+ else
+ run_in_docker $os_type $os_version $ref $dockercmd "$opts" "$@" || return 1
+ fi
+ done
+ done
+}
diff --git a/src/test/docker-test.sh b/src/test/docker-test.sh
new file mode 100755
index 000000000..6d0111367
--- /dev/null
+++ b/src/test/docker-test.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2014 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+source test/docker-test-helper.sh
+
+main_docker "$@"
diff --git a/src/test/dokan/CMakeLists.txt b/src/test/dokan/CMakeLists.txt
new file mode 100644
index 000000000..71029d386
--- /dev/null
+++ b/src/test/dokan/CMakeLists.txt
@@ -0,0 +1,13 @@
+if(WITH_DOKAN)
+ add_executable(ceph_test_dokan
+ dokan.cc
+ )
+ target_link_libraries(ceph_test_dokan
+ ceph-common
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_dokan
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(WITH_DOKAN)
diff --git a/src/test/dokan/dokan.cc b/src/test/dokan/dokan.cc
new file mode 100644
index 000000000..1a1d39580
--- /dev/null
+++ b/src/test/dokan/dokan.cc
@@ -0,0 +1,771 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Cloudbase Solutions
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <windows.h>
+#include <iostream>
+#include <fstream>
+#include <filesystem>
+#include <sys/socket.h>
+#include <direct.h>
+
+#include "gtest/gtest.h"
+
+#include "common/SubProcess.h"
+#include "common/run_cmd.h"
+#include "include/uuid.h"
+
+#define DEFAULT_MOUNTPOINT "X:\\"
+#define MOUNT_POLL_ATTEMPT 10
+#define MOUNT_POLL_INTERVAL_MS 1000
+#define TEST_VOL_SERIAL "1234567890"
+#define MByte 1048576
+
+namespace fs = std::filesystem;
+using namespace std::chrono_literals;
+
+std::string get_uuid() {
+ uuid_d suffix;
+ suffix.generate_random();
+
+ return suffix.to_string();
+}
+
+bool move_eof(HANDLE handle, LARGE_INTEGER offset) {
+
+ // Move file pointer to FILE_BEGIN + offset
+ if (!SetFilePointerEx(handle, offset, NULL, FILE_BEGIN)) {
+ std::cerr << "Setting file pointer failed. err: "
+ << GetLastError() << std::endl;
+ return false;
+ }
+
+ if (!SetEndOfFile(handle)) {
+ std::cerr << "Setting EOF failed. err: " << GetLastError() << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+void write_file(std::string file_path, std::string data) {
+ std::ofstream file;
+ file.open(file_path);
+
+ ASSERT_TRUE(file.is_open())
+ << "Failed to open file: " << file_path;
+ file << data;
+ file.flush();
+
+ file.close();
+}
+
+void expect_write_failure(std::string file_path) {
+ std::ofstream file;
+ file.open(file_path);
+
+ ASSERT_FALSE(file.is_open());
+}
+
+std::string read_file(std::string file_path) {
+ std::ifstream file;
+ file.open(file_path);
+ std::string content((std::istreambuf_iterator<char>(file)),
+ std::istreambuf_iterator<char>());
+ file.close();
+
+ return content;
+}
+
+void check_write_file(std::string file_path, std::string data) {
+ write_file(file_path, data);
+ ASSERT_EQ(read_file(file_path), data);
+}
+
+int wait_for_mount(std::string mount_path) {
+ std::cerr << "Waiting for mount: " << mount_path << std::endl;
+
+ int attempts = 0;
+ do {
+ attempts++;
+ if (attempts < MOUNT_POLL_ATTEMPT)
+ Sleep(MOUNT_POLL_INTERVAL_MS);
+ } while (!fs::exists(mount_path)
+ && attempts < MOUNT_POLL_ATTEMPT);
+
+ if (!fs::exists(mount_path)) {
+ std::cerr << "Timed out waiting for ceph-dokan mount: "
+ << mount_path << std::endl;
+ return -ETIMEDOUT;
+ }
+
+ std::cerr << "Successfully mounted: " << mount_path << std::endl;
+
+ return 0;
+}
+
+void map_dokan(SubProcess** mount, const char* mountpoint) {
+ SubProcess* new_mount = new SubProcess("ceph-dokan");
+
+ new_mount->add_cmd_args("map", "--win-vol-name", "TestCeph",
+ "--win-vol-serial", TEST_VOL_SERIAL,
+ "-l", mountpoint, NULL);
+
+ *mount = new_mount;
+ ASSERT_EQ(new_mount->spawn(), 0);
+ ASSERT_EQ(wait_for_mount(mountpoint), 0);
+}
+
+void map_dokan_read_only(
+ SubProcess** mount,
+ const char* mountpoint
+) {
+ SubProcess* new_mount = new SubProcess("ceph-dokan");
+ new_mount->add_cmd_args("map", "--win-vol-name", "TestCeph",
+ "--win-vol-serial", TEST_VOL_SERIAL,
+ "--read-only", "-l", mountpoint, NULL);
+
+ *mount = new_mount;
+ ASSERT_EQ(new_mount->spawn(), 0);
+ ASSERT_EQ(wait_for_mount(mountpoint), 0);
+ std::cerr << mountpoint << " mounted in read-only mode"
+ << std::endl;
+}
+
+void map_dokan_with_maxpath(
+ SubProcess** mount,
+ const char* mountpoint,
+ uint64_t max_path_len)
+{
+ SubProcess* new_mount = new SubProcess("ceph-dokan");
+ new_mount->add_cmd_args("map", "--debug", "--dokan-stderr",
+ "--win-vol-name", "TestCeph",
+ "--win-vol-serial", TEST_VOL_SERIAL,
+ "--max-path-len",
+ (std::to_string(max_path_len)).c_str(),
+ "-l", mountpoint, NULL);
+
+ *mount = new_mount;
+ ASSERT_EQ(new_mount->spawn(), 0);
+ if (256 <= max_path_len && max_path_len <= 4096) {
+ ASSERT_EQ(wait_for_mount(mountpoint), 0);
+ } else {
+ ASSERT_NE(wait_for_mount(mountpoint), 0);
+ }
+}
+
+void unmap_dokan(SubProcess* mount, const char* mountpoint) {
+ std::string ret = run_cmd("ceph-dokan", "unmap", "-l",
+ mountpoint, (char*)NULL);
+
+ ASSERT_EQ(ret, "") << "Failed unmapping: " << mountpoint;
+ std::cerr<< "Unmounted: " << mountpoint << std::endl;
+
+ ASSERT_EQ(mount->join(), 0);
+}
+
+int get_volume_max_path(std::string mountpoint){
+ char volume_name[MAX_PATH + 1] = { 0 };
+ char file_system_name[MAX_PATH + 1] = { 0 };
+ DWORD serial_number = 0;
+ DWORD max_component_len = 0;
+ DWORD file_system_flags = 0;
+ if (GetVolumeInformation(
+ mountpoint.c_str(),
+ volume_name,
+ sizeof(volume_name),
+ &serial_number,
+ &max_component_len,
+ &file_system_flags,
+ file_system_name,
+ sizeof(file_system_name)) != TRUE) {
+ std::cerr << "GetVolumeInformation() failed, error: "
+ << GetLastError() << std::endl;
+ }
+
+ return max_component_len;
+}
+
+static SubProcess* shared_mount = nullptr;
+
+class DokanTests : public testing::Test
+{
+protected:
+
+ static void SetUpTestSuite() {
+ map_dokan(&shared_mount, DEFAULT_MOUNTPOINT);
+ }
+
+ static void TearDownTestSuite() {
+ if (shared_mount) {
+ unmap_dokan(shared_mount, DEFAULT_MOUNTPOINT);
+ }
+ shared_mount = nullptr;
+ }
+};
+
+TEST_F(DokanTests, test_mount) {
+ std::string mountpoint = "Y:\\";
+ SubProcess* mount = nullptr;
+ map_dokan(&mount, mountpoint.c_str());
+ unmap_dokan(mount, mountpoint.c_str());
+}
+
+TEST_F(DokanTests, test_mount_read_only) {
+ std::string mountpoint = "Z:\\";
+ std::string data = "abc123";
+ std::string success_file_path = "ro_success_" + get_uuid();
+ std::string failed_file_path = "ro_fail_" + get_uuid();
+
+ SubProcess* mount = nullptr;
+ map_dokan(&mount, mountpoint.c_str());
+
+ check_write_file(mountpoint + success_file_path, data);
+ ASSERT_TRUE(fs::exists(mountpoint + success_file_path));
+
+ unmap_dokan(mount, mountpoint.c_str());
+
+ mount = nullptr;
+ map_dokan_read_only(&mount, mountpoint.c_str());
+
+ expect_write_failure(mountpoint + failed_file_path);
+ ASSERT_FALSE(fs::exists(mountpoint + failed_file_path));
+
+ ASSERT_TRUE(fs::exists(mountpoint + success_file_path));
+ ASSERT_EQ(read_file(mountpoint + success_file_path), data);
+
+ std::string exception_msg(
+ "filesystem error: cannot remove: No such device ["
+ + mountpoint + success_file_path + "]");
+ EXPECT_THROW({
+ try {
+ fs::remove(mountpoint + success_file_path);
+ } catch(const fs::filesystem_error &e) {
+ EXPECT_STREQ(e.what(), exception_msg.c_str());
+ throw;
+ }
+ }, fs::filesystem_error);
+ unmap_dokan(mount, mountpoint.c_str());
+
+ map_dokan(&mount, mountpoint.c_str());
+ ASSERT_TRUE(fs::exists(mountpoint + success_file_path));
+ ASSERT_TRUE(fs::remove(mountpoint + success_file_path));
+ unmap_dokan(mount, mountpoint.c_str());
+}
+
+TEST_F(DokanTests, test_delete_on_close) {
+ std::string file_path = DEFAULT_MOUNTPOINT"file_" + get_uuid();
+ HANDLE hFile = CreateFile(
+ file_path.c_str(),
+ GENERIC_WRITE, // open for writing
+ 0, // sharing mode, none in this case
+ 0, // use default security descriptor
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+ 0);
+
+ ASSERT_NE(hFile, INVALID_HANDLE_VALUE)
+ << "Could not open file: "
+ << DEFAULT_MOUNTPOINT"test_create.txt "
+ << "err: " << GetLastError() << std::endl;
+
+ ASSERT_NE(CloseHandle(hFile), 0);
+
+ // FILE_FLAG_DELETE_ON_CLOSE is used
+ ASSERT_FALSE(fs::exists(file_path));
+}
+
+TEST_F(DokanTests, test_io) {
+ std::string data = "abcdef";
+ std::string file_path = "test_io_" + get_uuid();
+
+ std::string mountpoint = "I:\\";
+ SubProcess* mount = nullptr;
+ map_dokan(&mount, mountpoint.c_str());
+
+ check_write_file(mountpoint + file_path, data);
+ ASSERT_TRUE(fs::exists(mountpoint + file_path));
+ unmap_dokan(mount, mountpoint.c_str());
+
+ mountpoint = "O:\\";
+ mount = nullptr;
+ map_dokan(&mount, mountpoint.c_str());
+
+ ASSERT_TRUE(fs::exists(mountpoint + file_path));
+ EXPECT_EQ(data, read_file(mountpoint + file_path));
+ ASSERT_TRUE(fs::remove((mountpoint + file_path).c_str()));
+ ASSERT_FALSE(fs::exists(mountpoint + file_path));
+ unmap_dokan(mount, mountpoint.c_str());
+}
+
+TEST_F(DokanTests, test_subfolders) {
+ std::string base_dir_path = DEFAULT_MOUNTPOINT"base_dir_"
+ + get_uuid() + "\\";
+ std::string sub_dir_path = base_dir_path
+ + "test_sub_dir" + get_uuid();
+ std::string base_dir_file = base_dir_path
+ + "file_" + get_uuid();
+ std::string sub_dir_file = sub_dir_path
+ + "file_" + get_uuid();
+
+ std::string data = "abc";
+
+ ASSERT_EQ(fs::create_directory(base_dir_path), true);
+ ASSERT_TRUE(fs::exists(base_dir_path));
+
+ ASSERT_EQ(fs::create_directory(sub_dir_path), true);
+ ASSERT_TRUE(fs::exists(sub_dir_path));
+
+ check_write_file(base_dir_file, data);
+ ASSERT_TRUE(fs::exists(base_dir_file));
+
+ check_write_file(sub_dir_file, data);
+ ASSERT_TRUE(fs::exists(sub_dir_file));
+
+ ASSERT_TRUE(fs::remove((sub_dir_file).c_str()))
+ << "Failed to remove file: " << sub_dir_file;
+ ASSERT_FALSE(fs::exists(sub_dir_file));
+
+ // Remove empty dir
+ ASSERT_TRUE(fs::remove((sub_dir_path).c_str()))
+ << "Failed to remove directory: " << sub_dir_path;
+ ASSERT_FALSE(fs::exists(sub_dir_file));
+
+ ASSERT_NE(fs::remove_all((base_dir_path).c_str()), 0)
+ << "Failed to remove directory: " << base_dir_path;
+ ASSERT_FALSE(fs::exists(sub_dir_file));
+}
+
+TEST_F(DokanTests, test_find_files) {
+ std::string basedir_path = "X:/find_" + get_uuid();
+ std::string subdir_path = basedir_path + "/dir_" + get_uuid();
+ std::string file1_path = basedir_path + "/file1_" + get_uuid();
+ std::string file2_path = subdir_path + "/file2_" + get_uuid();
+
+ ASSERT_TRUE(
+ fs::create_directories(subdir_path)
+ );
+
+ std::ofstream{file1_path};
+ std::ofstream{file2_path};
+
+ std::vector<std::string> paths;
+
+ for (const auto & entry :
+ fs::recursive_directory_iterator(basedir_path)
+ ) {
+ paths.push_back(entry.path().generic_string());
+ }
+
+ ASSERT_NE(std::find(begin(paths), end(paths), subdir_path), end(paths));
+ ASSERT_NE(std::find(begin(paths), end(paths), file1_path), end(paths));
+ ASSERT_NE(std::find(begin(paths), end(paths), file2_path), end(paths));
+
+ // clean-up
+ ASSERT_NE(fs::remove_all(basedir_path), 0);
+}
+
+TEST_F(DokanTests, test_move_file) {
+ std::string dir1_path = DEFAULT_MOUNTPOINT
+ "test_mv_1_" + get_uuid() + "\\";
+ std::string dir2_path = DEFAULT_MOUNTPOINT
+ "test_mv_2_" + get_uuid() + "\\";
+ std::string file_name = "mv_file_" + get_uuid();
+ std::string data = "abcd";
+
+ ASSERT_TRUE(fs::create_directory(dir1_path));
+ ASSERT_TRUE(fs::create_directory(dir2_path));
+
+ check_write_file(dir1_path + file_name, data);
+
+ fs::rename(dir1_path + file_name, dir2_path + file_name);
+
+ ASSERT_TRUE(fs::exists(dir2_path + file_name));
+ ASSERT_FALSE(fs::exists(dir1_path + file_name));
+
+ ASSERT_EQ(data, read_file(dir2_path + file_name));
+
+ // clean-up
+ ASSERT_NE(fs::remove_all(dir1_path),0);
+ ASSERT_NE(fs::remove_all(dir2_path),0);
+}
+
+TEST_F(DokanTests, test_max_path) {
+ std::string mountpoint = "P:\\";
+ std::string extended_mountpoint = "\\\\?\\" + mountpoint;
+ SubProcess* mount = nullptr;
+ char dir[200] = { 0 };
+ char file[200] = { 0 };
+ std::string data = "abcd1234";
+
+ memset(dir, 'd', sizeof(dir) - 1);
+ memset(file, 'f', sizeof(file) - 1);
+
+ uint64_t max_path_len = 4096;
+
+ map_dokan_with_maxpath(&mount,
+ mountpoint.c_str(),
+ max_path_len);
+ EXPECT_EQ(get_volume_max_path(extended_mountpoint),
+ max_path_len);
+
+ std::string long_dir_path = extended_mountpoint;
+
+ std::string dir_names[15];
+
+ for (int i = 0; i < 15; i++) {
+ std::string crt_dir = std::string(dir) + "_"
+ + get_uuid() + "\\";
+ long_dir_path.append(crt_dir);
+ int stat = _mkdir(long_dir_path.c_str());
+ ASSERT_EQ(stat, 0) << "Error creating directory " << i
+ << ": " << GetLastError() << std::endl;
+ dir_names[i] = crt_dir;
+ }
+ std::string file_path = long_dir_path + "\\" + std::string(file)
+ + "_" + get_uuid();
+
+ check_write_file(file_path, data);
+
+ // clean-up
+ // fs::remove is unable to handle long Windows paths
+ EXPECT_NE(DeleteFileA(file_path.c_str()), 0);
+
+ for (int i = 14; i >= 0; i--) {
+ std::string remove_dir = extended_mountpoint;
+ for (int j = 0; j <= i; j++) {
+ remove_dir.append(dir_names[j]);
+ }
+
+ EXPECT_NE(RemoveDirectoryA(remove_dir.c_str()), 0);
+ }
+
+ unmap_dokan(mount, mountpoint.c_str());
+
+ // value exceeds 32767, so a failure is expected
+ max_path_len = 32770;
+ map_dokan_with_maxpath(&mount,
+ mountpoint.c_str(),
+ max_path_len);
+ ASSERT_FALSE(fs::exists(mountpoint));
+
+ // value is below 256, so a failure is expected
+ max_path_len = 150;
+ map_dokan_with_maxpath(&mount,
+ mountpoint.c_str(),
+ max_path_len);
+ ASSERT_FALSE(fs::exists(mountpoint));
+
+ // default value
+ map_dokan(&mount, mountpoint.c_str());
+ EXPECT_EQ(get_volume_max_path(mountpoint.c_str()), 256);
+
+ unmap_dokan(mount, mountpoint.c_str());
+}
+
+TEST_F(DokanTests, test_set_eof) {
+ std::string file_path = DEFAULT_MOUNTPOINT"test_eof_"
+ + get_uuid();
+ HANDLE hFile = CreateFile(
+ file_path.c_str(),
+ GENERIC_WRITE, // open for writing
+ 0, // sharing mode, none in this case
+ 0, // use default security descriptor
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+ 0);
+
+ ASSERT_NE(hFile, INVALID_HANDLE_VALUE)
+ << "Could not open file: "
+ << DEFAULT_MOUNTPOINT"test_create.txt "
+ << "err: " << GetLastError() << std::endl;
+
+ LARGE_INTEGER offset;
+ offset.QuadPart = 2 * MByte; // 2MB
+
+ LARGE_INTEGER file_size;
+
+ ASSERT_TRUE(move_eof(hFile, offset));
+ ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0);
+ EXPECT_EQ(file_size.QuadPart, offset.QuadPart);
+
+ offset.QuadPart = MByte; // 1MB
+
+ ASSERT_TRUE(move_eof(hFile, offset));
+ ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0);
+ EXPECT_EQ(file_size.QuadPart, offset.QuadPart);
+
+ ASSERT_NE(CloseHandle(hFile), 0);
+
+ // FILE_FLAG_DELETE_ON_CLOSE is used
+ ASSERT_FALSE(fs::exists(file_path));
+}
+
+TEST_F(DokanTests, test_set_alloc_size) {
+ std::string file_path = DEFAULT_MOUNTPOINT"test_alloc_size_"
+ + get_uuid();
+ HANDLE hFile = CreateFile(
+ file_path.c_str(),
+ GENERIC_WRITE, // open for writing
+ 0, // sharing mode, none in this case
+ 0, // use default security descriptor
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+ 0);
+
+ ASSERT_NE(hFile, INVALID_HANDLE_VALUE)
+ << "Could not open file: "
+ << DEFAULT_MOUNTPOINT"test_create.txt "
+ << "err: " << GetLastError() << std::endl;
+
+ LARGE_INTEGER li;
+ li.QuadPart = MByte;
+ FILE_ALLOCATION_INFO fai;
+ fai.AllocationSize = li;
+
+ ASSERT_NE(SetFileInformationByHandle(
+ hFile,
+ FileAllocationInfo,
+ &fai,
+ sizeof(FILE_ALLOCATION_INFO)
+ ),0) << "Error: " << GetLastError();
+
+ LARGE_INTEGER offset;
+ offset.QuadPart = 2 * MByte;
+ LARGE_INTEGER file_size;
+
+ ASSERT_TRUE(move_eof(hFile, offset));
+ ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0);
+ EXPECT_EQ(file_size.QuadPart, offset.QuadPart);
+
+ offset.QuadPart = MByte;
+
+ ASSERT_TRUE(move_eof(hFile, offset));
+ ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0);
+ EXPECT_EQ(file_size.QuadPart, offset.QuadPart);
+
+ ASSERT_NE(CloseHandle(hFile), 0);
+
+ // FILE_FLAG_DELETE_ON_CLOSE is used
+ ASSERT_FALSE(fs::exists(file_path));
+}
+
+TEST_F(DokanTests, test_file_type) {
+ std::string test_dir = DEFAULT_MOUNTPOINT"test_info_"
+ + get_uuid() + "\\";
+ std::string file_path = test_dir + "file_"
+ + get_uuid();
+ std::string dir_path = test_dir + "dir_"
+ + get_uuid() + "\\";
+
+ ASSERT_TRUE(fs::create_directory(test_dir));
+
+ std::ofstream{file_path};
+ ASSERT_TRUE(fs::create_directory(dir_path));
+
+ ASSERT_TRUE(fs::is_regular_file(fs::status(file_path)));
+ ASSERT_TRUE(fs::is_directory(fs::status(dir_path)));
+
+ // clean-up
+ fs::remove_all(test_dir);
+
+}
+
+TEST_F(DokanTests, test_volume_info) {
+ char volume_name[MAX_PATH + 1] = { 0 };
+ char file_system_name[MAX_PATH + 1] = { 0 };
+ DWORD serial_number = 0;
+ DWORD max_component_len = 0;
+ DWORD file_system_flags = 0;
+
+ ASSERT_EQ(
+ GetVolumeInformation(
+ DEFAULT_MOUNTPOINT,
+ volume_name,
+ sizeof(volume_name),
+ &serial_number,
+ &max_component_len,
+ &file_system_flags,
+ file_system_name,
+ sizeof(file_system_name)),TRUE)
+ << "GetVolumeInformation() failed, error: "
+ << GetLastError() << std::endl;
+
+ ASSERT_STREQ(volume_name, "TestCeph")
+ << "Received: " << volume_name << std::endl;
+ ASSERT_STREQ(file_system_name, "Ceph")
+ << "Received: " << file_system_name << std::endl;
+ ASSERT_EQ(max_component_len, 256);
+ ASSERT_EQ(serial_number, std::stoi(TEST_VOL_SERIAL))
+ << "Received: " << serial_number << std::endl;
+
+ // Consider adding specific flags
+ // and check for them
+ // ASSERT_EQ(file_system_flags, 271);
+}
+
+TEST_F(DokanTests, test_get_free_space) {
+ std::error_code ec;
+ const std::filesystem::space_info si =
+ std::filesystem::space(DEFAULT_MOUNTPOINT, ec);
+ ASSERT_EQ(ec.value(), 0);
+
+ ASSERT_NE(static_cast<std::intmax_t>(si.capacity), 0);
+ ASSERT_NE(static_cast<std::intmax_t>(si.free), 0);
+ ASSERT_NE(static_cast<std::intmax_t>(si.available), 0);
+}
+
+TEST_F(DokanTests, test_file_timestamp) {
+ std::string file1 = DEFAULT_MOUNTPOINT"test_time1_"
+ + get_uuid();
+ std::string file2 = DEFAULT_MOUNTPOINT"test_time2_"
+ + get_uuid();
+ std::string file3 = DEFAULT_MOUNTPOINT"test_time3_"
+ + get_uuid();
+
+ std::ofstream{file1};
+ Sleep(1000);
+ std::ofstream{file2};
+ Sleep(1000);
+ std::ofstream{file3};
+
+ int64_t file1_creation = fs::last_write_time(file1)
+ .time_since_epoch().count();
+ int64_t file2_creation = fs::last_write_time(file2)
+ .time_since_epoch().count();
+ int64_t file3_creation = fs::last_write_time(file3)
+ .time_since_epoch().count();
+
+ EXPECT_LT(file1_creation, file2_creation);
+ EXPECT_LT(file2_creation, file3_creation);
+
+ // add 1h to file 1 creation time
+ fs::file_time_type file1_time = fs::last_write_time(file1);
+
+ fs::last_write_time(file1, file1_time + 1h);
+
+ int64_t file1_new_time = fs::last_write_time(file1)
+ .time_since_epoch().count();
+
+ EXPECT_EQ((file1_time + 1h).time_since_epoch().count(),
+ file1_new_time);
+ EXPECT_GT(file1_new_time, file2_creation);
+ EXPECT_GT(file1_new_time, file3_creation);
+
+ ASSERT_TRUE(fs::remove(file1));
+ ASSERT_TRUE(fs::remove(file2));
+ ASSERT_TRUE(fs::remove(file3));
+}
+
+TEST_F(DokanTests, test_delete_disposition) {
+ std::string file_path = DEFAULT_MOUNTPOINT"test_disp_"
+ + get_uuid();
+ HANDLE hFile = CreateFile(file_path.c_str(),
+ GENERIC_ALL, // required for delete
+ 0, // exclusive access
+ NULL,
+ CREATE_ALWAYS,
+ 0,
+ NULL);
+
+ ASSERT_NE(hFile, INVALID_HANDLE_VALUE)
+ << "Could not open file: " << file_path
+ << "err: " << GetLastError() << std::endl;
+
+ FILE_DISPOSITION_INFO fdi;
+ fdi.DeleteFile = TRUE; // marking for deletion
+
+ ASSERT_NE(
+ SetFileInformationByHandle(
+ hFile,
+ FileDispositionInfo,
+ &fdi,
+ sizeof(FILE_DISPOSITION_INFO)), 0);
+
+ ASSERT_NE(CloseHandle(hFile), 0);
+ ASSERT_FALSE(fs::exists(file_path));
+}
+
+bool check_create_disposition(std::string path, DWORD disposition) {
+ HANDLE hFile = CreateFile(path.c_str(),
+ GENERIC_WRITE,
+ 0, // exclusive access
+ NULL,
+ disposition,
+ 0,
+ NULL);
+
+ if(hFile == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ if(CloseHandle(hFile) == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+TEST_F(DokanTests, test_create_dispositions) {
+ std::string file_path = DEFAULT_MOUNTPOINT"test_create_"
+ + get_uuid();
+ std::string non_existant_file = DEFAULT_MOUNTPOINT
+ "test_create_" + get_uuid();
+
+ EXPECT_TRUE(
+ check_create_disposition(file_path, CREATE_NEW));
+
+ // CREATE_ALWAYS with existing file
+ EXPECT_TRUE(
+ check_create_disposition(file_path, CREATE_ALWAYS));
+ EXPECT_EQ(GetLastError(), ERROR_ALREADY_EXISTS);
+
+ // CREATE_NEW with existing file
+ EXPECT_FALSE(
+ check_create_disposition(file_path, CREATE_NEW));
+ EXPECT_EQ(GetLastError(), ERROR_FILE_EXISTS);
+
+ // OPEN_EXISTING with existing file
+ EXPECT_TRUE(
+ check_create_disposition(file_path, OPEN_EXISTING));
+
+ ASSERT_FALSE(fs::exists(non_existant_file));
+ // OPEN_EXISTING with non-existant file
+ EXPECT_FALSE(
+ check_create_disposition(non_existant_file, OPEN_EXISTING));
+ EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND);
+
+ // OPEN_ALWAYS with existing file
+ EXPECT_TRUE(
+ check_create_disposition(file_path, OPEN_ALWAYS));
+ EXPECT_EQ(GetLastError(), ERROR_ALREADY_EXISTS);
+
+ ASSERT_FALSE(fs::exists(non_existant_file));
+ // OPEN_ALWAYS with non-existant file
+ EXPECT_TRUE(
+ check_create_disposition(non_existant_file, OPEN_ALWAYS));
+ EXPECT_EQ(GetLastError(), 0);
+
+ ASSERT_TRUE(fs::remove(non_existant_file));
+
+ // TRUNCATE_EXISTING with existing file
+ EXPECT_TRUE(
+ check_create_disposition(file_path, TRUNCATE_EXISTING));
+
+ // TRUNCATE_EXISTING with non-existant file
+ EXPECT_FALSE(
+ check_create_disposition(non_existant_file,
+ TRUNCATE_EXISTING));
+ EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND);
+
+ // clean-up
+ ASSERT_TRUE(fs::remove(file_path));
+}
diff --git a/src/test/encoding.cc b/src/test/encoding.cc
new file mode 100644
index 000000000..6bc89491f
--- /dev/null
+++ b/src/test/encoding.cc
@@ -0,0 +1,543 @@
+#include "include/buffer.h"
+#include "include/encoding.h"
+
+#include <fmt/format.h>
+#include "gtest/gtest.h"
+
+using namespace std;
+
+template < typename T >
+static void test_encode_and_decode(const T& src)
+{
+ bufferlist bl(1000000);
+ encode(src, bl);
+ T dst;
+ auto i = bl.cbegin();
+ decode(dst, i);
+ ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst;
+}
+
+TEST(EncodingRoundTrip, StringSimple) {
+ string my_str("I am the very model of a modern major general");
+ test_encode_and_decode < std::string >(my_str);
+}
+
+TEST(EncodingRoundTrip, StringEmpty) {
+ string my_str("");
+ test_encode_and_decode < std::string >(my_str);
+}
+
+TEST(EncodingRoundTrip, StringNewline) {
+ string my_str("foo bar baz\n");
+ test_encode_and_decode < std::string >(my_str);
+}
+
+template <typename Size, typename T>
+static void test_encode_and_nohead_nohead(Size len, const T& src)
+{
+ bufferlist bl(1000000);
+ encode(len, bl);
+ encode_nohead(src, bl);
+ T dst;
+ auto i = bl.cbegin();
+ decode(len, i);
+ decode_nohead(len, dst, i);
+ ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst;
+}
+
+TEST(EncodingRoundTrip, StringNoHead) {
+ const string str("The quick brown fox jumps over the lazy dog");
+ auto size = str.size();
+ test_encode_and_nohead_nohead(static_cast<int>(size), str);
+ test_encode_and_nohead_nohead(static_cast<unsigned>(size), str);
+ test_encode_and_nohead_nohead(static_cast<uint32_t>(size), str);
+ test_encode_and_nohead_nohead(static_cast<__u32>(size), str);
+ test_encode_and_nohead_nohead(static_cast<size_t>(size), str);
+}
+
+TEST(EncodingRoundTrip, BufferListNoHead) {
+ bufferlist bl;
+ bl.append("is this a dagger which i see before me?");
+ auto size = bl.length();
+ test_encode_and_nohead_nohead(static_cast<int>(size), bl);
+ test_encode_and_nohead_nohead(static_cast<unsigned>(size), bl);
+ test_encode_and_nohead_nohead(static_cast<uint32_t>(size), bl);
+ test_encode_and_nohead_nohead(static_cast<__u32>(size), bl);
+ test_encode_and_nohead_nohead(static_cast<size_t>(size), bl);
+}
+
+typedef std::multimap < int, std::string > multimap_t;
+typedef multimap_t::value_type my_val_ty;
+
+namespace std {
+static std::ostream& operator<<(std::ostream& oss, const multimap_t &multimap)
+{
+ for (multimap_t::const_iterator m = multimap.begin();
+ m != multimap.end();
+ ++m)
+ {
+ oss << m->first << "->" << m->second << " ";
+ }
+ return oss;
+}
+}
+
+TEST(EncodingRoundTrip, Multimap) {
+ multimap_t multimap;
+ multimap.insert( my_val_ty(1, "foo") );
+ multimap.insert( my_val_ty(2, "bar") );
+ multimap.insert( my_val_ty(2, "baz") );
+ multimap.insert( my_val_ty(3, "lucky number 3") );
+ multimap.insert( my_val_ty(10000, "large number") );
+
+ test_encode_and_decode < multimap_t >(multimap);
+}
+
+
+
+///////////////////////////////////////////////////////
+// ConstructorCounter
+///////////////////////////////////////////////////////
+template <typename T>
+class ConstructorCounter
+{
+public:
+ ConstructorCounter() : data(0)
+ {
+ default_ctor++;
+ }
+
+ explicit ConstructorCounter(const T& data_)
+ : data(data_)
+ {
+ one_arg_ctor++;
+ }
+
+ ConstructorCounter(const ConstructorCounter &rhs)
+ : data(rhs.data)
+ {
+ copy_ctor++;
+ }
+
+ ConstructorCounter &operator=(const ConstructorCounter &rhs)
+ {
+ data = rhs.data;
+ assigns++;
+ return *this;
+ }
+
+ static void init(void)
+ {
+ default_ctor = 0;
+ one_arg_ctor = 0;
+ copy_ctor = 0;
+ assigns = 0;
+ }
+
+ static int get_default_ctor(void)
+ {
+ return default_ctor;
+ }
+
+ static int get_one_arg_ctor(void)
+ {
+ return one_arg_ctor;
+ }
+
+ static int get_copy_ctor(void)
+ {
+ return copy_ctor;
+ }
+
+ static int get_assigns(void)
+ {
+ return assigns;
+ }
+
+ bool operator<(const ConstructorCounter &rhs) const
+ {
+ return data < rhs.data;
+ }
+
+ bool operator==(const ConstructorCounter &rhs) const
+ {
+ return data == rhs.data;
+ }
+
+ friend void decode(ConstructorCounter &s, bufferlist::const_iterator& p)
+ {
+ decode(s.data, p);
+ }
+
+ friend void encode(const ConstructorCounter &s, bufferlist& p)
+ {
+ encode(s.data, p);
+ }
+
+ friend ostream& operator<<(ostream &oss, const ConstructorCounter &cc)
+ {
+ oss << cc.data;
+ return oss;
+ }
+
+ T data;
+private:
+ static int default_ctor;
+ static int one_arg_ctor;
+ static int copy_ctor;
+ static int assigns;
+};
+
+template class ConstructorCounter <int32_t>;
+template class ConstructorCounter <int16_t>;
+
+typedef ConstructorCounter <int32_t> my_key_t;
+typedef ConstructorCounter <int16_t> my_val_t;
+typedef std::multimap < my_key_t, my_val_t > multimap2_t;
+typedef multimap2_t::value_type val2_ty;
+
+template <class T> int ConstructorCounter<T>::default_ctor = 0;
+template <class T> int ConstructorCounter<T>::one_arg_ctor = 0;
+template <class T> int ConstructorCounter<T>::copy_ctor = 0;
+template <class T> int ConstructorCounter<T>::assigns = 0;
+
+static std::ostream& operator<<(std::ostream& oss, const multimap2_t &multimap)
+{
+ for (multimap2_t::const_iterator m = multimap.begin();
+ m != multimap.end();
+ ++m)
+ {
+ oss << m->first << "->" << m->second << " ";
+ }
+ return oss;
+}
+
+TEST(EncodingRoundTrip, MultimapConstructorCounter) {
+ multimap2_t multimap2;
+ multimap2.insert( val2_ty(my_key_t(1), my_val_t(10)) );
+ multimap2.insert( val2_ty(my_key_t(2), my_val_t(20)) );
+ multimap2.insert( val2_ty(my_key_t(2), my_val_t(30)) );
+ multimap2.insert( val2_ty(my_key_t(3), my_val_t(40)) );
+ multimap2.insert( val2_ty(my_key_t(10000), my_val_t(1)) );
+
+ my_key_t::init();
+ my_val_t::init();
+ test_encode_and_decode < multimap2_t >(multimap2);
+
+ EXPECT_EQ(my_key_t::get_default_ctor(), 5);
+ EXPECT_EQ(my_key_t::get_one_arg_ctor(), 0);
+ EXPECT_EQ(my_key_t::get_copy_ctor(), 5);
+ EXPECT_EQ(my_key_t::get_assigns(), 0);
+
+ EXPECT_EQ(my_val_t::get_default_ctor(), 5);
+ EXPECT_EQ(my_val_t::get_one_arg_ctor(), 0);
+ EXPECT_EQ(my_val_t::get_copy_ctor(), 5);
+ EXPECT_EQ(my_val_t::get_assigns(), 0);
+}
+
+namespace ceph {
+// make sure that the legacy encode/decode methods are selected
+// over the ones defined using templates. the later is likely to
+// be slower, see also the definition of "WRITE_INT_DENC" in
+// include/denc.h
+template<>
+void encode<uint64_t, denc_traits<uint64_t>>(const uint64_t&,
+ bufferlist&,
+ uint64_t f) {
+ static_assert(denc_traits<uint64_t>::supported,
+ "should support new encoder");
+ static_assert(!denc_traits<uint64_t>::featured,
+ "should not be featured");
+ ASSERT_EQ(0UL, f);
+ // make sure the test fails if i get called
+ ASSERT_TRUE(false);
+}
+
+template<>
+void encode<ceph_le64, denc_traits<ceph_le64>>(const ceph_le64&,
+ bufferlist&,
+ uint64_t f) {
+ static_assert(denc_traits<ceph_le64>::supported,
+ "should support new encoder");
+ static_assert(!denc_traits<ceph_le64>::featured,
+ "should not be featured");
+ ASSERT_EQ(0UL, f);
+ // make sure the test fails if i get called
+ ASSERT_TRUE(false);
+}
+}
+
+namespace {
+ // search `underlying_type` in denc.h for supported underlying types
+ enum class Colour : int8_t { R,G,B };
+ ostream& operator<<(ostream& os, Colour c) {
+ switch (c) {
+ case Colour::R:
+ return os << "Colour::R";
+ case Colour::G:
+ return os << "Colour::G";
+ case Colour::B:
+ return os << "Colour::B";
+ default:
+ return os << "Colour::???";
+ }
+ }
+}
+
+TEST(EncodingRoundTrip, Integers) {
+ // int types
+ {
+ uint64_t i = 42;
+ test_encode_and_decode(i);
+ }
+ {
+ int16_t i = 42;
+ test_encode_and_decode(i);
+ }
+ {
+ bool b = true;
+ test_encode_and_decode(b);
+ }
+ {
+ bool b = false;
+ test_encode_and_decode(b);
+ }
+ // raw encoder
+ {
+ ceph_le64 i;
+ i = 42;
+ test_encode_and_decode(i);
+ }
+ // enum
+ {
+ test_encode_and_decode(Colour::R);
+ // this should not build, as the size of unsigned is not the same on
+ // different archs, that's why denc_traits<> intentionally leaves
+ // `int` and `unsigned int` out of supported types.
+ //
+ // enum E { R, G, B };
+ // test_encode_and_decode(R);
+ }
+}
+
+TEST(EncodingException, Macros) {
+ const struct {
+ buffer::malformed_input exc;
+ std::string expected_what;
+ } tests[] = {
+ {
+ DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, 100, 200),
+ fmt::format("{} no longer understand old encoding version 100 < 200: Malformed input",
+ __PRETTY_FUNCTION__)
+ },
+ {
+ DECODE_ERR_PAST(__PRETTY_FUNCTION__),
+ fmt::format("{} decode past end of struct encoding: Malformed input",
+ __PRETTY_FUNCTION__)
+ }
+ };
+ for (auto& [exec, expected_what] : tests) {
+ try {
+ throw exec;
+ } catch (const exception& e) {
+ ASSERT_NE(string(e.what()).find(expected_what), string::npos);
+ }
+ }
+}
+
+
+TEST(small_encoding, varint) {
+ uint32_t v[][4] = {
+ /* value, varint bytes, signed varint bytes, signed varint bytes (neg) */
+ {0, 1, 1, 1},
+ {1, 1, 1, 1},
+ {2, 1, 1, 1},
+ {31, 1, 1, 1},
+ {32, 1, 1, 1},
+ {0xff, 2, 2, 2},
+ {0x100, 2, 2, 2},
+ {0xfff, 2, 2, 2},
+ {0x1000, 2, 2, 2},
+ {0x2000, 2, 3, 3},
+ {0x3fff, 2, 3, 3},
+ {0x4000, 3, 3, 3},
+ {0x4001, 3, 3, 3},
+ {0x10001, 3, 3, 3},
+ {0x20001, 3, 3, 3},
+ {0x40001, 3, 3, 3},
+ {0x80001, 3, 3, 3},
+ {0x7f0001, 4, 4, 4},
+ {0xff00001, 4, 5, 5},
+ {0x1ff00001, 5, 5, 5},
+ {0xffff0001, 5, 5, 5},
+ {0xffffffff, 5, 5, 5},
+ {1074790401, 5, 5, 5},
+ {0, 0, 0, 0}
+ };
+ for (unsigned i=0; v[i][1]; ++i) {
+ {
+ bufferlist bl;
+ {
+ auto app = bl.get_contiguous_appender(16, true);
+ denc_varint(v[i][0], app);
+ }
+ cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t";
+ bl.hexdump(cout, false);
+ cout << std::endl;
+ ASSERT_EQ(bl.length(), v[i][1]);
+ uint32_t u;
+ auto p = bl.begin().get_current_ptr().cbegin();
+ denc_varint(u, p);
+ ASSERT_EQ(v[i][0], u);
+ }
+ {
+ bufferlist bl;
+ {
+ auto app = bl.get_contiguous_appender(16, true);
+ denc_signed_varint(v[i][0], app);
+ }
+ cout << std::hex << v[i][0] << "\t" << v[i][2] << "\t";
+ bl.hexdump(cout, false);
+ cout << std::endl;
+ ASSERT_EQ(bl.length(), v[i][2]);
+ int32_t u;
+ auto p = bl.begin().get_current_ptr().cbegin();
+ denc_signed_varint(u, p);
+ ASSERT_EQ((int32_t)v[i][0], u);
+ }
+ {
+ bufferlist bl;
+ int64_t x = -(int64_t)v[i][0];
+ {
+ auto app = bl.get_contiguous_appender(16, true);
+ denc_signed_varint(x, app);
+ }
+ cout << std::dec << x << std::hex << "\t" << v[i][3] << "\t";
+ bl.hexdump(cout, false);
+ cout << std::endl;
+ ASSERT_EQ(bl.length(), v[i][3]);
+ int64_t u;
+ auto p = bl.begin().get_current_ptr().cbegin();
+ denc_signed_varint(u, p);
+ ASSERT_EQ(x, u);
+ }
+ }
+}
+
+TEST(small_encoding, varint_lowz) {
+ uint32_t v[][4] = {
+ /* value, bytes encoded */
+ {0, 1, 1, 1},
+ {1, 1, 1, 1},
+ {2, 1, 1, 1},
+ {15, 1, 1, 1},
+ {16, 1, 1, 1},
+ {31, 1, 2, 2},
+ {63, 2, 2, 2},
+ {64, 1, 1, 1},
+ {0xff, 2, 2, 2},
+ {0x100, 1, 1, 1},
+ {0x7ff, 2, 2, 2},
+ {0xfff, 2, 3, 3},
+ {0x1000, 1, 1, 1},
+ {0x4000, 1, 1, 1},
+ {0x8000, 1, 1, 1},
+ {0x10000, 1, 2, 2},
+ {0x20000, 2, 2, 2},
+ {0x40000, 2, 2, 2},
+ {0x80000, 2, 2, 2},
+ {0x7f0000, 2, 2, 2},
+ {0xffff0000, 4, 4, 4},
+ {0xffffffff, 5, 5, 5},
+ {0x41000000, 3, 4, 4},
+ {0, 0, 0, 0}
+ };
+ for (unsigned i=0; v[i][1]; ++i) {
+ {
+ bufferlist bl;
+ {
+ auto app = bl.get_contiguous_appender(16, true);
+ denc_varint_lowz(v[i][0], app);
+ }
+ cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t";
+ bl.hexdump(cout, false);
+ cout << std::endl;
+ ASSERT_EQ(bl.length(), v[i][1]);
+ uint32_t u;
+ auto p = bl.begin().get_current_ptr().cbegin();
+ denc_varint_lowz(u, p);
+ ASSERT_EQ(v[i][0], u);
+ }
+ {
+ bufferlist bl;
+ int64_t x = v[i][0];
+ {
+ auto app = bl.get_contiguous_appender(16, true);
+ denc_signed_varint_lowz(x, app);
+ }
+ cout << std::hex << x << "\t" << v[i][1] << "\t";
+ bl.hexdump(cout, false);
+ cout << std::endl;
+ ASSERT_EQ(bl.length(), v[i][2]);
+ int64_t u;
+ auto p = bl.begin().get_current_ptr().cbegin();
+ denc_signed_varint_lowz(u, p);
+ ASSERT_EQ(x, u);
+ }
+ {
+ bufferlist bl;
+ int64_t x = -(int64_t)v[i][0];
+ {
+ auto app = bl.get_contiguous_appender(16, true);
+ denc_signed_varint_lowz(x, app);
+ }
+ cout << std::dec << x << "\t" << v[i][1] << "\t";
+ bl.hexdump(cout, false);
+ cout << std::endl;
+ ASSERT_EQ(bl.length(), v[i][3]);
+ int64_t u;
+ auto p = bl.begin().get_current_ptr().cbegin();
+ denc_signed_varint_lowz(u, p);
+ ASSERT_EQ(x, u);
+ }
+ }
+}
+
+TEST(small_encoding, lba) {
+ uint64_t v[][2] = {
+ /* value, bytes encoded */
+ {0, 4},
+ {1, 4},
+ {0xff, 4},
+ {0x10000, 4},
+ {0x7f0000, 4},
+ {0xffff0000, 4},
+ {0x0fffffff, 4},
+ {0x1fffffff, 5},
+ {0xffffffff, 5},
+ {0x3fffffff000, 4},
+ {0x7fffffff000, 5},
+ {0x1fffffff0000, 4},
+ {0x3fffffff0000, 5},
+ {0xfffffff00000, 4},
+ {0x1fffffff00000, 5},
+ {0x41000000, 4},
+ {0, 0}
+ };
+ for (unsigned i=0; v[i][1]; ++i) {
+ bufferlist bl;
+ {
+ auto app = bl.get_contiguous_appender(16, true);
+ denc_lba(v[i][0], app);
+ }
+ cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t";
+ bl.hexdump(cout, false);
+ cout << std::endl;
+ ASSERT_EQ(bl.length(), v[i][1]);
+ uint64_t u;
+ auto p = bl.begin().get_current_ptr().cbegin();
+ denc_lba(u, p);
+ ASSERT_EQ(v[i][0], u);
+ }
+
+}
diff --git a/src/test/encoding/CMakeLists.txt b/src/test/encoding/CMakeLists.txt
new file mode 100644
index 000000000..e8c57a042
--- /dev/null
+++ b/src/test/encoding/CMakeLists.txt
@@ -0,0 +1,3 @@
+# scripts
+add_ceph_test(check-generated.sh ${CMAKE_CURRENT_SOURCE_DIR}/check-generated.sh)
+add_ceph_test(readable.sh ${CMAKE_CURRENT_SOURCE_DIR}/readable.sh)
diff --git a/src/test/encoding/check-generated.sh b/src/test/encoding/check-generated.sh
new file mode 100755
index 000000000..2569bc1a5
--- /dev/null
+++ b/src/test/encoding/check-generated.sh
@@ -0,0 +1,101 @@
+#!/usr/bin/env bash
+set -e
+
+source $(dirname $0)/../detect-build-env-vars.sh
+source $CEPH_ROOT/qa/standalone/ceph-helpers.sh
+
+dir=$1
+
+tmp1=`mktemp /tmp/typ-XXXXXXXXX`
+tmp2=`mktemp /tmp/typ-XXXXXXXXX`
+tmp3=`mktemp /tmp/typ-XXXXXXXXX`
+tmp4=`mktemp /tmp/typ-XXXXXXXXX`
+
+failed=0
+numtests=0
+echo "checking ceph-dencoder generated test instances..."
+echo "numgen type"
+while read type; do
+ num=`ceph-dencoder type $type count_tests`
+ echo "$num $type"
+ for n in `seq 1 1 $num 2>/dev/null`; do
+
+ pids=""
+ run_in_background pids save_stdout "$tmp1" ceph-dencoder type "$type" select_test "$n" dump_json
+ run_in_background pids save_stdout "$tmp2" ceph-dencoder type "$type" select_test "$n" encode decode dump_json
+ run_in_background pids save_stdout "$tmp3" ceph-dencoder type "$type" select_test "$n" copy dump_json
+ run_in_background pids save_stdout "$tmp4" ceph-dencoder type "$type" select_test "$n" copy_ctor dump_json
+ wait_background pids
+
+ if [ $? -ne 0 ]; then
+ echo "**** $type test $n encode+decode check failed ****"
+ echo " ceph-dencoder type $type select_test $n encode decode"
+ failed=$(($failed + 3))
+ continue
+ fi
+
+ # nondeterministic classes may dump nondeterministically. compare
+ # the sorted json output. this is a weaker test, but is better
+ # than nothing.
+ deterministic=0
+ if ceph-dencoder type "$type" is_deterministic; then
+ deterministic=1
+ fi
+
+ if [ $deterministic -eq 0 ]; then
+ echo " sorting json output for nondeterministic object"
+ for f in $tmp1 $tmp2 $tmp3 $tmp4; do
+ sort $f | sed 's/,$//' > $f.new
+ mv $f.new $f
+ done
+ fi
+
+ if ! cmp $tmp1 $tmp2; then
+ echo "**** $type test $n dump_json check failed ****"
+ echo " ceph-dencoder type $type select_test $n dump_json > $tmp1"
+ echo " ceph-dencoder type $type select_test $n encode decode dump_json > $tmp2"
+ diff $tmp1 $tmp2
+ failed=$(($failed + 1))
+ fi
+
+ if ! cmp $tmp1 $tmp3; then
+ echo "**** $type test $n copy dump_json check failed ****"
+ echo " ceph-dencoder type $type select_test $n dump_json > $tmp1"
+ echo " ceph-dencoder type $type select_test $n copy dump_json > $tmp2"
+ diff $tmp1 $tmp2
+ failed=$(($failed + 1))
+ fi
+
+ if ! cmp $tmp1 $tmp4; then
+ echo "**** $type test $n copy_ctor dump_json check failed ****"
+ echo " ceph-dencoder type $type select_test $n dump_json > $tmp1"
+ echo " ceph-dencoder type $type select_test $n copy_ctor dump_json > $tmp2"
+ diff $tmp1 $tmp2
+ failed=$(($failed + 1))
+ fi
+
+ if [ $deterministic -ne 0 ]; then
+ run_in_background pids ceph-dencoder type "$type" select_test $n encode export "$tmp1"
+ run_in_background pids ceph-dencoder type "$type" select_test $n encode decode encode export "$tmp2"
+ wait_background pids
+
+ if ! cmp $tmp1 $tmp2; then
+ echo "**** $type test $n binary reencode check failed ****"
+ echo " ceph-dencoder type $type select_test $n encode export $tmp1"
+ echo " ceph-dencoder type $type select_test $n encode decode encode export $tmp2"
+ diff <(hexdump -C $tmp1) <(hexdump -C $tmp2)
+ failed=$(($failed + 1))
+ fi
+ fi
+
+ numtests=$(($numtests + 3))
+ done
+done < <(ceph-dencoder list_types)
+
+rm -f $tmp1 $tmp2 $tmp3 $tmp4
+
+if [ $failed -gt 0 ]; then
+ echo "FAILED $failed / $numtests tests."
+ exit 1
+fi
+echo "passed $numtests tests."
diff --git a/src/test/encoding/generate-corpus-objects.sh b/src/test/encoding/generate-corpus-objects.sh
new file mode 100755
index 000000000..559ac524d
--- /dev/null
+++ b/src/test/encoding/generate-corpus-objects.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+set -ex
+
+BDIR=`pwd`
+
+p=$1
+echo path $p
+test ! -d $p
+mkdir $p
+strings bin/ceph-osd | grep "^$p/%s__%d.%x"
+
+v=`git describe | cut -c 2-`
+echo version $v
+
+echo 'binaries look ok, vstarting'
+echo
+
+MON=3 MDS=3 OSD=5 MDS=3 MGR=2 RGW=1 ../src/vstart.sh -x -n -l --bluestore -e
+
+export PATH=bin:$PATH
+
+# do some work to generate a hopefully braod set of object instances
+
+echo 'starting some background work'
+../qa/workunits/rados/test.sh &
+../qa/workunits/rbd/test_librbd.sh &
+../qa/workunits/libcephfs/test.sh &
+../qa/workunits/rgw/run-s3tests.sh &
+ceph-syn --syn makedirs 3 3 3 &
+
+echo 'waiting a bit'
+
+sleep 10
+echo 'triggering some recovery'
+
+kill -9 `cat out/osd.0.pid`
+sleep 10
+ceph osd out 0
+sleep 10
+init-ceph start osd.0
+ceph osd in 0
+
+sleep 5
+echo 'triggering mds work'
+bin/ceph mds fail 0
+
+echo 'waiting for worker to join (and ignoring errors)'
+wait || true
+
+echo 'importing'
+../src/test/encoding/import.sh $p $v ../ceph-object-corpus/archive
+
+for d in ../ceph-object-corpus/archive/$v/objects/*
+do
+ echo prune $d
+ ../ceph-object-corpus/bin/prune.sh $d 25
+done
+
+echo 'done'
diff --git a/src/test/encoding/identity.sh b/src/test/encoding/identity.sh
new file mode 100755
index 000000000..67c803c9d
--- /dev/null
+++ b/src/test/encoding/identity.sh
@@ -0,0 +1,32 @@
+#!/bin/sh -e
+
+dir=$1
+
+set -e
+
+tmp1=`mktemp /tmp/typ-XXXXXXXXX`
+tmp2=`mktemp /tmp/typ-XXXXXXXXX`
+
+for type in `ls $dir`
+do
+ if ./ceph-dencoder type $type 2>/dev/null; then
+ echo "type $type"
+ for o in `ls $dir/$type`; do
+ f="$dir/$type/$o"
+ echo "\t$f"
+
+ ./ceph-dencoder type $type import $f decode dump_json > $tmp1
+ ./ceph-dencoder type $type import $f decode encode decode dump_json > $tmp2
+ cmp $tmp1 $tmp2 || exit 1
+
+ ./ceph-dencoder type $type import $f decode encode export $tmp1
+ cmp $tmp1 $f || exit 1
+ done
+ else
+ echo "skip $type"
+ fi
+done
+
+rm -f $tmp1 $tmp2
+
+echo OK
diff --git a/src/test/encoding/import-generated.sh b/src/test/encoding/import-generated.sh
new file mode 100755
index 000000000..c328e6fd8
--- /dev/null
+++ b/src/test/encoding/import-generated.sh
@@ -0,0 +1,30 @@
+#!/bin/sh -e
+
+archive=$1
+
+[ -d "$archive" ] || echo "usage: $0 <archive>"
+
+ver=`bin/ceph-dencoder version`
+echo "version $ver"
+
+[ -d "$archive/$ver" ] || mkdir "$archive/$ver"
+
+tmp1=`mktemp /tmp/typ-XXXXXXXXX`
+
+echo "numgen\ttype"
+for type in `bin/ceph-dencoder list_types`; do
+
+ [ -d "$archive/$ver/objects/$type" ] || mkdir -p "$archive/$ver/objects/$type"
+
+ num=`bin/ceph-dencoder type $type count_tests`
+ echo "$num\t$type"
+ max=$(($num - 1))
+ for n in `seq 0 $max`; do
+ bin/ceph-dencoder type $type select_test $n encode export $tmp1
+ md=`md5sum $tmp1 | awk '{print $1}'`
+ echo "\t$md"
+ [ -e "$archive/$ver/objects/$type/$md" ] || cp $tmp1 $archive/$ver/objects/$type/$md
+ done
+done
+
+rm $tmp1
diff --git a/src/test/encoding/import.sh b/src/test/encoding/import.sh
new file mode 100755
index 000000000..eea96e353
--- /dev/null
+++ b/src/test/encoding/import.sh
@@ -0,0 +1,23 @@
+#!/bin/sh -e
+
+src=$1
+ver=$2
+archive=$3
+
+[ -d "$archive" ] && [ -d "$src" ] || echo "usage: $0 <srcdir> <version> <archive>"
+
+[ -d "$archive/$ver" ] || mkdir "$archive/$ver"
+
+dest_dir="$archive/$ver/objects"
+
+[ -d "$dest_dir" ] || mkdir "$dest_dir"
+
+for f in `find $src -type f`
+do
+ n=`basename $f`
+ type=`echo $n | sed 's/__.*//'`
+ md=`md5sum $f | awk '{print $1}'`
+
+ [ -d "$dest_dir/$type" ] || mkdir $dest_dir/$type
+ [ -e "$dest_dir/$type/$md" ] || cp $f $dest_dir/$type/$md
+done
diff --git a/src/test/encoding/readable.sh b/src/test/encoding/readable.sh
new file mode 100755
index 000000000..81545852c
--- /dev/null
+++ b/src/test/encoding/readable.sh
@@ -0,0 +1,241 @@
+#!/usr/bin/env bash
+set -e
+
+source $(dirname $0)/../detect-build-env-vars.sh
+
+[ -z "$CEPH_ROOT" ] && CEPH_ROOT=..
+
+dir=$CEPH_ROOT/ceph-object-corpus
+
+failed=0
+numtests=0
+pids=""
+
+if [ -x ./ceph-dencoder ]; then
+ CEPH_DENCODER=./ceph-dencoder
+else
+ CEPH_DENCODER=ceph-dencoder
+fi
+
+myversion=`$CEPH_DENCODER version`
+DEBUG=0
+WAITALL_DELAY=.1
+debug() { if [ "$DEBUG" -gt 0 ]; then echo "DEBUG: $*" >&2; fi }
+
+test_object() {
+ local type=$1
+ local output_file=$2
+ local failed=0
+ local numtests=0
+
+ tmp1=`mktemp /tmp/test_object_1-XXXXXXXXX`
+ tmp2=`mktemp /tmp/test_object_2-XXXXXXXXX`
+
+ rm -f $output_file
+ if $CEPH_DENCODER type $type 2>/dev/null; then
+ #echo "type $type";
+ echo " $vdir/objects/$type"
+
+ # is there a fwd incompat change between $arversion and $version?
+ incompat=""
+ incompat_paths=""
+ sawarversion=0
+ for iv in `ls $dir/archive | sort -n`; do
+ if [ "$iv" = "$arversion" ]; then
+ sawarversion=1
+ fi
+
+ if [ $sawarversion -eq 1 ] && [ -e "$dir/archive/$iv/forward_incompat/$type" ]; then
+ incompat="$iv"
+
+ # Check if we'll be ignoring only specified objects, not whole type. If so, remember
+ # all paths for this type into variable. Assuming that this path won't contain any
+ # whitechars (implication of above for loop).
+ if [ -d "$dir/archive/$iv/forward_incompat/$type" ]; then
+ if [ -n "`ls $dir/archive/$iv/forward_incompat/$type/ | sort -n`" ]; then
+ incompat_paths="$incompat_paths $dir/archive/$iv/forward_incompat/$type"
+ else
+ echo "type $type directory empty, ignoring whole type instead of single objects"
+ fi;
+ fi
+ fi
+
+ if [ "$iv" = "$version" ]; then
+ rm -rf $tmp1 $tmp2
+ break
+ fi
+ done
+
+ if [ -n "$incompat" ]; then
+ if [ -z "$incompat_paths" ]; then
+ echo "skipping incompat $type version $arversion, changed at $incompat < code $myversion"
+ rm -rf $tmp1 $tmp2
+ return
+ else
+ # If we are ignoring not whole type, but objects that are in $incompat_path,
+ # we don't skip here, just give info.
+ echo "postponed skip one of incompact $type version $arversion, changed at $incompat < code $myversion"
+ fi;
+ fi
+
+ for f in `ls $vdir/objects/$type`; do
+
+ skip=0;
+ # Check if processed object $f of $type should be skipped (postponed skip)
+ if [ -n "$incompat_paths" ]; then
+ for i_path in $incompat_paths; do
+ # Check if $f is a symbolic link and if it's pointing to existing target
+ if [ -L "$i_path/$f" ]; then
+ echo "skipping object $f of type $type"
+ skip=1
+ break
+ fi;
+ done;
+ fi;
+
+ if [ $skip -ne 0 ]; then
+ continue
+ fi;
+
+ $CEPH_DENCODER type $type import $vdir/objects/$type/$f decode dump_json > $tmp1 &
+ pid1="$!"
+ $CEPH_DENCODER type $type import $vdir/objects/$type/$f decode encode decode dump_json > $tmp2 &
+ pid2="$!"
+ #echo "\t$vdir/$type/$f"
+ if ! wait $pid1; then
+ echo "**** failed to decode $vdir/objects/$type/$f ****"
+ failed=$(($failed + 1))
+ rm -f $tmp1 $tmp2
+ continue
+ fi
+ if ! wait $pid2; then
+ echo "**** failed to decode+encode+decode $vdir/objects/$type/$f ****"
+ failed=$(($failed + 1))
+ rm -f $tmp1 $tmp2
+ continue
+ fi
+
+ # nondeterministic classes may dump
+ # nondeterministically. compare the sorted json
+ # output. this is a weaker test, but is better than
+ # nothing.
+ if ! $CEPH_DENCODER type $type is_deterministic; then
+ echo " sorting json output for nondeterministic object"
+ for f in $tmp1 $tmp2; do
+ sort $f | sed 's/,$//' > $f.new
+ mv $f.new $f
+ done
+ fi
+
+ if ! cmp $tmp1 $tmp2; then
+ echo "**** reencode of $vdir/objects/$type/$f resulted in a different dump ****"
+ diff $tmp1 $tmp2
+ failed=$(($failed + 1))
+ fi
+ numtests=$(($numtests + 1))
+ rm -f $tmp1 $tmp2
+ done
+ else
+ echo "skipping unrecognized type $type"
+ rm -f $tmp1 $tmp2
+ fi
+
+ echo "failed=$failed" > $output_file
+ echo "numtests=$numtests" >> $output_file
+}
+
+waitall() { # PID...
+ ## Wait for children to exit and indicate whether all exited with 0 status.
+ local errors=0
+ while :; do
+ debug "Processes remaining: $*"
+ for pid in "$@"; do
+ shift
+ if kill -0 "$pid" 2>/dev/null; then
+ debug "$pid is still alive."
+ set -- "$@" "$pid"
+ elif wait "$pid"; then
+ debug "$pid exited with zero exit status."
+ else
+ debug "$pid exited with non-zero exit status."
+ errors=$(($errors + 1))
+ fi
+ done
+ [ $# -eq 0 ] && break
+ sleep ${WAITALL_DELAY:-1}
+ done
+ [ $errors -eq 0 ]
+}
+
+######
+# MAIN
+######
+
+do_join() {
+ waitall $pids
+ pids=""
+ # Reading the output of jobs to compute failed & numtests
+ # Tests are run in parallel but sum should be done sequentialy to avoid
+ # races between threads
+ while [ "$running_jobs" -ge 0 ]; do
+ if [ -f $output_file.$running_jobs ]; then
+ read_failed=$(grep "^failed=" $output_file.$running_jobs | cut -d "=" -f 2)
+ read_numtests=$(grep "^numtests=" $output_file.$running_jobs | cut -d "=" -f 2)
+ rm -f $output_file.$running_jobs
+ failed=$(($failed + $read_failed))
+ numtests=$(($numtests + $read_numtests))
+ fi
+ running_jobs=$(($running_jobs - 1))
+ done
+ running_jobs=0
+}
+
+# Using $MAX_PARALLEL_JOBS jobs if defined, unless the number of logical
+# processors
+if [ `uname` == FreeBSD -o `uname` == Darwin ]; then
+ NPROC=`sysctl -n hw.ncpu`
+ max_parallel_jobs=${MAX_PARALLEL_JOBS:-${NPROC}}
+else
+ max_parallel_jobs=${MAX_PARALLEL_JOBS:-$(nproc)}
+fi
+
+output_file=`mktemp /tmp/output_file-XXXXXXXXX`
+running_jobs=0
+
+for arversion in `ls $dir/archive | sort -n`; do
+ vdir="$dir/archive/$arversion"
+ #echo $vdir
+
+ if [ ! -d "$vdir/objects" ]; then
+ continue;
+ fi
+
+ for type in `ls $vdir/objects`; do
+ test_object $type $output_file.$running_jobs &
+ pids="$pids $!"
+ running_jobs=$(($running_jobs + 1))
+
+ # Once we spawned enough jobs, let's wait them to complete
+ # Every spawned job have almost the same execution time so
+ # it's not a big deal having them not ending at the same time
+ if [ "$running_jobs" -eq "$max_parallel_jobs" ]; then
+ do_join
+ fi
+ rm -f ${output_file}*
+ done
+done
+
+do_join
+
+if [ $failed -gt 0 ]; then
+ echo "FAILED $failed / $numtests tests."
+ exit 1
+fi
+
+if [ $numtests -eq 0 ]; then
+ echo "FAILED: no tests found to run!"
+ exit 1
+fi
+
+echo "passed $numtests tests."
+
diff --git a/src/test/erasure-code/CMakeLists.txt b/src/test/erasure-code/CMakeLists.txt
new file mode 100644
index 000000000..ab7328eae
--- /dev/null
+++ b/src/test/erasure-code/CMakeLists.txt
@@ -0,0 +1,259 @@
+
+add_executable(ceph_erasure_code_benchmark
+ ${CMAKE_SOURCE_DIR}/src/erasure-code/ErasureCode.cc
+ ceph_erasure_code_benchmark.cc)
+target_link_libraries(ceph_erasure_code_benchmark ceph-common Boost::program_options global ${CMAKE_DL_LIBS})
+install(TARGETS ceph_erasure_code_benchmark
+ DESTINATION bin)
+
+add_executable(ceph_erasure_code_non_regression ceph_erasure_code_non_regression.cc)
+target_link_libraries(ceph_erasure_code_non_regression ceph-common Boost::program_options global ${CMAKE_DL_LIBS})
+
+add_library(ec_example SHARED
+ ErasureCodePluginExample.cc
+ $<TARGET_OBJECTS:erasure_code_objs>)
+target_link_libraries(ec_example pthread ${EXTRALIBS})
+
+add_library(ec_missing_entry_point SHARED ErasureCodePluginMissingEntryPoint.cc)
+target_link_libraries(ec_missing_entry_point pthread ${EXTRALIBS})
+
+add_library(ec_missing_version SHARED ErasureCodePluginMissingVersion.cc)
+target_link_libraries(ec_missing_version pthread ${EXTRALIBS})
+
+add_library(ec_hangs SHARED ErasureCodePluginHangs.cc)
+target_link_libraries(ec_hangs pthread ${EXTRALIBS})
+
+add_library(ec_fail_to_initialize SHARED ErasureCodePluginFailToInitialize.cc)
+target_link_libraries(ec_fail_to_initialize pthread ${EXTRALIBS})
+
+add_library(ec_fail_to_register SHARED ErasureCodePluginFailToRegister.cc)
+target_link_libraries(ec_fail_to_register pthread ${EXTRALIBS})
+
+# unittest_erasure_code_plugin
+add_executable(unittest_erasure_code_plugin
+ ${CMAKE_SOURCE_DIR}/src/erasure-code/ErasureCode.cc
+ TestErasureCodePlugin.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_plugin)
+target_link_libraries(unittest_erasure_code_plugin
+ global
+ ${CMAKE_DL_LIBS}
+ ec_example
+ ceph-common
+ )
+add_dependencies(unittest_erasure_code_plugin
+ ec_example
+ ec_missing_entry_point
+ ec_missing_version
+ ec_hangs
+ ec_fail_to_initialize
+ ec_fail_to_register)
+
+# unittest_erasure_code
+add_executable(unittest_erasure_code
+ ${CMAKE_SOURCE_DIR}/src/erasure-code/ErasureCode.cc
+ TestErasureCode.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code)
+target_link_libraries(unittest_erasure_code
+ global
+ ceph-common
+ )
+
+# unittest_erasure_code_plugin_jerasure
+add_executable(unittest_erasure_code_plugin_jerasure
+ TestErasureCodePluginJerasure.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_plugin_jerasure)
+target_link_libraries(unittest_erasure_code_plugin_jerasure
+ global
+ ceph-common)
+add_dependencies(unittest_erasure_code_plugin_jerasure
+ ec_jerasure)
+
+if(WITH_EC_ISA_PLUGIN)
+
+#unittest_erasure_code_isa
+add_executable(unittest_erasure_code_isa
+ ${CMAKE_SOURCE_DIR}/src/erasure-code/ErasureCode.cc
+ TestErasureCodeIsa.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_isa)
+target_link_libraries(unittest_erasure_code_isa
+ global
+ ceph-common
+ ec_isa
+ erasure_code
+ )
+
+#unittest_erasure_code_plugin_isa
+add_executable(unittest_erasure_code_plugin_isa
+ ${CMAKE_SOURCE_DIR}/src/erasure-code/ErasureCode.cc
+ TestErasureCodePluginIsa.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_plugin_isa)
+target_link_libraries(unittest_erasure_code_plugin_isa
+ global
+ ceph-common
+ ${CMAKE_DL_LIBS}
+ erasure_code
+ )
+add_dependencies(unittest_erasure_code_plugin_isa
+ ec_isa)
+endif(WITH_EC_ISA_PLUGIN)
+
+# unittest_erasure_code_lrc
+add_executable(unittest_erasure_code_lrc
+ TestErasureCodeLrc.cc
+ $<TARGET_OBJECTS:unit-main>)
+add_ceph_unittest(unittest_erasure_code_lrc)
+target_link_libraries(unittest_erasure_code_lrc
+ global
+ ${CMAKE_DL_LIBS}
+ ec_lrc
+ ceph-common
+ )
+
+# unittest_erasure_code_plugin_lrc
+add_executable(unittest_erasure_code_plugin_lrc
+ TestErasureCodePluginLrc.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_plugin_lrc)
+add_dependencies(unittest_erasure_code_plugin_lrc
+ ec_lrc
+ ec_jerasure)
+target_link_libraries(unittest_erasure_code_plugin_lrc
+ global
+ ${CMAKE_DL_LIBS}
+ ceph-common)
+
+# unittest_erasure_code_plugin_shec
+add_executable(unittest_erasure_code_plugin_shec
+ TestErasureCodePluginShec.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_plugin_shec)
+target_link_libraries(unittest_erasure_code_plugin_shec
+ global
+ ${CMAKE_DL_LIBS}
+ ceph-common)
+add_dependencies(unittest_erasure_code_plugin_shec
+ ec_shec)
+
+# unittest_erasure_code_example
+add_executable(unittest_erasure_code_example
+ ${CMAKE_SOURCE_DIR}/src/erasure-code/ErasureCode.cc
+ TestErasureCodeExample.cc
+ $<TARGET_OBJECTS:unit-main>
+)
+add_ceph_unittest(unittest_erasure_code_example)
+target_link_libraries(unittest_erasure_code_example
+ global
+ ${CMAKE_DL_LIBS}
+ ceph-common
+ erasure_code
+ ${UNITTEST_LIBS}
+ )
+
+include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/src/erasure-code/jerasure/jerasure/include)
+include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/src/erasure-code//jerasure/gf-complete/include)
+
+# unittest_erasure_code_jerasure
+add_executable(unittest_erasure_code_jerasure
+ TestErasureCodeJerasure.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_jerasure)
+target_link_libraries(unittest_erasure_code_jerasure
+ global
+ ceph-common
+ ec_jerasure
+ )
+
+include_directories(${CMAKE_SOURCE_DIR}/src/erasure-code/jerasure)
+include_directories(${CMAKE_SOURCE_DIR}/src/erasure-code/shec)
+
+# unittest_erasure_code_shec
+add_executable(unittest_erasure_code_shec
+ TestErasureCodeShec.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_shec)
+target_link_libraries(unittest_erasure_code_shec
+ global
+ ${CMAKE_DL_LIBS}
+ ceph-common
+ ec_shec
+ )
+
+# unittest_erasure_code_shec_all
+add_executable(unittest_erasure_code_shec_all
+ TestErasureCodeShec_all.cc
+ )
+add_ceph_unittest(unittest_erasure_code_shec_all PARALLEL)
+target_link_libraries(unittest_erasure_code_shec_all
+ global
+ ${CMAKE_DL_LIBS}
+ ceph-common
+ ec_shec
+ )
+
+# unittest_erasure_code_shec_thread
+add_executable(unittest_erasure_code_shec_thread
+ TestErasureCodeShec_thread.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_erasure_code_shec_thread)
+target_link_libraries(unittest_erasure_code_shec_thread
+ global
+ ${CMAKE_DL_LIBS}
+ ceph-common
+ ec_shec
+ )
+
+
+# unittest_erasure_code_shec_arguments
+add_executable(unittest_erasure_code_shec_arguments
+ TestErasureCodeShec_arguments.cc
+ )
+add_ceph_unittest(unittest_erasure_code_shec_arguments)
+target_link_libraries(unittest_erasure_code_shec_arguments
+ global
+ ${CMAKE_DL_LIBS}
+ ceph-common
+ ec_shec
+ )
+
+#unitest_erasure_code_clay
+add_executable(unittest_erasure_code_clay
+ TestErasureCodeClay.cc
+ $<TARGET_OBJECTS:unit-main>)
+add_ceph_unittest(unittest_erasure_code_clay)
+target_link_libraries(unittest_erasure_code_clay
+ global
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ceph-common
+ ec_clay
+ )
+
+# unittest_erasure_code_plugin_clay
+add_executable(unittest_erasure_code_plugin_clay
+ TestErasureCodePluginClay.cc
+ $<TARGET_OBJECTS:unit-main>)
+add_ceph_unittest(unittest_erasure_code_plugin_clay)
+add_dependencies(unittest_erasure_code_plugin_clay
+ ec_clay)
+target_link_libraries(unittest_erasure_code_plugin_clay
+ GTest::Main
+ global
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ceph-common)
+
diff --git a/src/test/erasure-code/ErasureCodeExample.h b/src/test/erasure-code/ErasureCodeExample.h
new file mode 100644
index 000000000..4226361c4
--- /dev/null
+++ b/src/test/erasure-code/ErasureCodeExample.h
@@ -0,0 +1,195 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef CEPH_ERASURE_CODE_EXAMPLE_H
+#define CEPH_ERASURE_CODE_EXAMPLE_H
+
+#include <unistd.h>
+#include <errno.h>
+#include <algorithm>
+#include <sstream>
+
+#include "crush/CrushWrapper.h"
+#include "osd/osd_types.h"
+#include "erasure-code/ErasureCode.h"
+
+#define FIRST_DATA_CHUNK 0
+#define SECOND_DATA_CHUNK 1
+#define DATA_CHUNKS 2u
+
+#define CODING_CHUNK 2
+#define CODING_CHUNKS 1u
+
+#define MINIMUM_TO_RECOVER 2u
+
+class ErasureCodeExample final : public ErasureCode {
+public:
+ ~ErasureCodeExample() override {}
+
+ int create_rule(const std::string &name,
+ CrushWrapper &crush,
+ std::ostream *ss) const override {
+ return crush.add_simple_rule(name, "default", "host", "",
+ "indep", pg_pool_t::TYPE_ERASURE, ss);
+ }
+
+ int minimum_to_decode_with_cost(const std::set<int> &want_to_read,
+ const std::map<int, int> &available,
+ std::set<int> *minimum) override {
+ //
+ // If one chunk is more expensive to fetch than the others,
+ // recover it instead. For instance, if the cost reflects the
+ // time it takes for a chunk to be retrieved from a remote
+ // OSD and if CPU is cheap, it could make sense to recover
+ // instead of fetching the chunk.
+ //
+ std::map<int, int> c2c(available);
+ if (c2c.size() > DATA_CHUNKS) {
+ if (c2c[FIRST_DATA_CHUNK] > c2c[SECOND_DATA_CHUNK] &&
+ c2c[FIRST_DATA_CHUNK] > c2c[CODING_CHUNK])
+ c2c.erase(FIRST_DATA_CHUNK);
+ else if(c2c[SECOND_DATA_CHUNK] > c2c[FIRST_DATA_CHUNK] &&
+ c2c[SECOND_DATA_CHUNK] > c2c[CODING_CHUNK])
+ c2c.erase(SECOND_DATA_CHUNK);
+ else if(c2c[CODING_CHUNK] > c2c[FIRST_DATA_CHUNK] &&
+ c2c[CODING_CHUNK] > c2c[SECOND_DATA_CHUNK])
+ c2c.erase(CODING_CHUNK);
+ }
+ std::set <int> available_chunks;
+ for (std::map<int, int>::const_iterator i = c2c.begin();
+ i != c2c.end();
+ ++i)
+ available_chunks.insert(i->first);
+ return _minimum_to_decode(want_to_read, available_chunks, minimum);
+ }
+
+ unsigned int get_chunk_count() const override {
+ return DATA_CHUNKS + CODING_CHUNKS;
+ }
+
+ unsigned int get_data_chunk_count() const override {
+ return DATA_CHUNKS;
+ }
+
+ unsigned int get_chunk_size(unsigned int object_size) const override {
+ return ( object_size / DATA_CHUNKS ) + 1;
+ }
+
+ int encode(const std::set<int> &want_to_encode,
+ const bufferlist &in,
+ std::map<int, bufferlist> *encoded) override {
+ //
+ // make sure all data chunks have the same length, allocating
+ // padding if necessary.
+ //
+ unsigned int chunk_length = get_chunk_size(in.length());
+ bufferlist out(in);
+ unsigned int width = get_chunk_count() * get_chunk_size(in.length());
+ bufferptr pad(width - in.length());
+ pad.zero(0, get_data_chunk_count());
+ out.push_back(pad);
+ //
+ // compute the coding chunk with first chunk ^ second chunk
+ //
+ char *p = out.c_str();
+ for (unsigned i = 0; i < chunk_length; i++)
+ p[i + CODING_CHUNK * chunk_length] =
+ p[i + FIRST_DATA_CHUNK * chunk_length] ^
+ p[i + SECOND_DATA_CHUNK * chunk_length];
+ //
+ // populate the bufferlist with bufferptr pointing
+ // to chunk boundaries
+ //
+ const bufferptr &ptr = out.front();
+ for (auto j = want_to_encode.begin();
+ j != want_to_encode.end();
+ ++j) {
+ bufferlist tmp;
+ bufferptr chunk(ptr, (*j) * chunk_length, chunk_length);
+ tmp.push_back(chunk);
+ tmp.claim_append((*encoded)[*j]);
+ (*encoded)[*j].swap(tmp);
+ }
+ return 0;
+ }
+
+ int encode_chunks(const std::set<int> &want_to_encode,
+ std::map<int, bufferlist> *encoded) override {
+ ceph_abort();
+ return 0;
+ }
+
+ int _decode(const std::set<int> &want_to_read,
+ const std::map<int, bufferlist> &chunks,
+ std::map<int, bufferlist> *decoded) override {
+ //
+ // All chunks have the same size
+ //
+ unsigned chunk_length = (*chunks.begin()).second.length();
+ for (std::set<int>::iterator i = want_to_read.begin();
+ i != want_to_read.end();
+ ++i) {
+ if (chunks.find(*i) != chunks.end()) {
+ //
+ // If the chunk is available, just copy the bufferptr pointer
+ // to the decoded argument.
+ //
+ (*decoded)[*i] = chunks.find(*i)->second;
+ } else if(chunks.size() != 2) {
+ //
+ // If a chunk is missing and there are not enough chunks
+ // to recover, abort.
+ //
+ return -ERANGE;
+ } else {
+ //
+ // No matter what the missing chunk is, XOR of the other
+ // two recovers it.
+ //
+ std::map<int, bufferlist>::const_iterator k = chunks.begin();
+ const char *a = k->second.front().c_str();
+ ++k;
+ const char *b = k->second.front().c_str();
+ bufferptr chunk(chunk_length);
+ char *c = chunk.c_str();
+ for (unsigned j = 0; j < chunk_length; j++) {
+ c[j] = a[j] ^ b[j];
+ }
+
+ bufferlist tmp;
+ tmp.append(chunk);
+ tmp.claim_append((*decoded)[*i]);
+ (*decoded)[*i].swap(tmp);
+ }
+ }
+ return 0;
+ }
+
+ int decode_chunks(const std::set<int> &want_to_read,
+ const std::map<int, bufferlist> &chunks,
+ std::map<int, bufferlist> *decoded) override {
+ ceph_abort();
+ return 0;
+ }
+
+ const std::vector<int> &get_chunk_mapping() const override {
+ static std::vector<int> mapping;
+ return mapping;
+ }
+
+};
+
+#endif
diff --git a/src/test/erasure-code/ErasureCodePluginExample.cc b/src/test/erasure-code/ErasureCodePluginExample.cc
new file mode 100644
index 000000000..697b77d94
--- /dev/null
+++ b/src/test/erasure-code/ErasureCodePluginExample.cc
@@ -0,0 +1,45 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <unistd.h>
+
+#include "ceph_ver.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "ErasureCodeExample.h"
+
+using namespace std;
+
+class ErasureCodePluginExample : public ErasureCodePlugin {
+public:
+ int factory(const std::string &directory,
+ ErasureCodeProfile &profile,
+ ErasureCodeInterfaceRef *erasure_code,
+ ostream *ss) override
+ {
+ *erasure_code = ErasureCodeInterfaceRef(new ErasureCodeExample());
+ (*erasure_code)->init(profile, ss);
+ return 0;
+ }
+};
+
+const char *__erasure_code_version() { return CEPH_GIT_NICE_VER; }
+
+int __erasure_code_init(char *plugin_name, char *directory)
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ return instance.add(plugin_name, new ErasureCodePluginExample());
+}
diff --git a/src/test/erasure-code/ErasureCodePluginFailToInitialize.cc b/src/test/erasure-code/ErasureCodePluginFailToInitialize.cc
new file mode 100644
index 000000000..f1219fab4
--- /dev/null
+++ b/src/test/erasure-code/ErasureCodePluginFailToInitialize.cc
@@ -0,0 +1,26 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include "ceph_ver.h"
+
+extern "C" const char *__erasure_code_version() { return CEPH_GIT_NICE_VER; }
+
+extern "C" int __erasure_code_init(char *plugin_name, char *directory)
+{
+ return -ESRCH;
+}
diff --git a/src/test/erasure-code/ErasureCodePluginFailToRegister.cc b/src/test/erasure-code/ErasureCodePluginFailToRegister.cc
new file mode 100644
index 000000000..9e8e0161e
--- /dev/null
+++ b/src/test/erasure-code/ErasureCodePluginFailToRegister.cc
@@ -0,0 +1,25 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include "ceph_ver.h"
+
+extern "C" const char *__erasure_code_version() { return CEPH_GIT_NICE_VER; }
+
+extern "C" int __erasure_code_init(char *plugin_name, char *directory)
+{
+ return 0;
+}
diff --git a/src/test/erasure-code/ErasureCodePluginHangs.cc b/src/test/erasure-code/ErasureCodePluginHangs.cc
new file mode 100644
index 000000000..037fff1bb
--- /dev/null
+++ b/src/test/erasure-code/ErasureCodePluginHangs.cc
@@ -0,0 +1,27 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <unistd.h>
+#include "ceph_ver.h"
+
+extern "C" const char *__erasure_code_version() { return CEPH_GIT_NICE_VER; }
+
+extern "C" int __erasure_code_init(char *plugin_name, char *directory)
+{
+ sleep(10);
+ return 0;
+}
diff --git a/src/test/erasure-code/ErasureCodePluginMissingEntryPoint.cc b/src/test/erasure-code/ErasureCodePluginMissingEntryPoint.cc
new file mode 100644
index 000000000..8a55214b1
--- /dev/null
+++ b/src/test/erasure-code/ErasureCodePluginMissingEntryPoint.cc
@@ -0,0 +1,6 @@
+#include "ceph_ver.h"
+
+// missing int __erasure_code_init(char *plugin_name, char *directory) {}
+
+extern "C" const char *__erasure_code_version() { return CEPH_GIT_NICE_VER; }
+
diff --git a/src/test/erasure-code/ErasureCodePluginMissingVersion.cc b/src/test/erasure-code/ErasureCodePluginMissingVersion.cc
new file mode 100644
index 000000000..da4ed0e48
--- /dev/null
+++ b/src/test/erasure-code/ErasureCodePluginMissingVersion.cc
@@ -0,0 +1,3 @@
+// missing __erasure_code_version
+
+int __this_is_an_used_variable_to_avoid_warnings;
diff --git a/src/test/erasure-code/TestErasureCode.cc b/src/test/erasure-code/TestErasureCode.cc
new file mode 100644
index 000000000..05b95ded4
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCode.cc
@@ -0,0 +1,169 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "erasure-code/ErasureCode.h"
+#include "global/global_context.h"
+#include "common/config.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+class ErasureCodeTest : public ErasureCode {
+public:
+ map<int, bufferlist> encode_chunks_encoded;
+ unsigned int k;
+ unsigned int m;
+ unsigned int chunk_size;
+
+ ErasureCodeTest(unsigned int _k, unsigned int _m, unsigned int _chunk_size) :
+ k(_k), m(_m), chunk_size(_chunk_size) {}
+ ~ErasureCodeTest() override {}
+
+ int init(ErasureCodeProfile &profile, ostream *ss) override {
+ return 0;
+ }
+
+ unsigned int get_chunk_count() const override { return k + m; }
+ unsigned int get_data_chunk_count() const override { return k; }
+ unsigned int get_chunk_size(unsigned int object_size) const override {
+ return chunk_size;
+ }
+ int encode_chunks(const set<int> &want_to_encode,
+ map<int, bufferlist> *encoded) override {
+ encode_chunks_encoded = *encoded;
+ return 0;
+ }
+ int decode_chunks(const set<int> &want_to_read,
+ const map<int, bufferlist> &chunks,
+ map<int, bufferlist> *decoded) override {
+ ceph_abort_msg("ErasureCode::decode_chunks not implemented");
+ }
+
+ int create_rule(const string &name,
+ CrushWrapper &crush,
+ ostream *ss) const override { return 0; }
+};
+
+/*
+ * If we have a buffer of 5 bytes (X below) and a chunk size of 3
+ * bytes, for k=3, m=1 an additional 7 bytes (P and C below) will
+ * need to be allocated for padding (P) and the 3 coding bytes (C).
+ *
+ * X -+ +----------+ +-X
+ * X | | data 0 | | X
+ * X | +----------+ | X
+ * X | +----------+ | X -> +-X
+ * X -+ | data 1 | +-X -> | X
+ * P -+ +----------+ | P
+ * P | +----------+ | P
+ * P | | data 2 | | P
+ * P | +----------+ | P
+ * C | +----------+ | C
+ * C | | coding 3 | | C
+ * C -+ +----------+ +-C
+ *
+ * The data chunks 1 and 2 (data 1 and data 2 above) overflow the
+ * original buffer because it needs padding. A new buffer will
+ * be allocated to contain the chunk that overflows and all other
+ * chunks after it, including the coding chunk(s).
+ *
+ * The following test creates a siguation where the buffer provided
+ * for encoding is not memory aligned. After encoding it asserts that:
+ *
+ * a) each chunk is SIMD aligned
+ * b) the data 1 chunk content is as expected which implies that its
+ * content has been copied over.
+ *
+ * It is possible for a flawed implementation to pas the test because the
+ * underlying allocation function enforces it.
+ */
+TEST(ErasureCodeTest, encode_memory_align)
+{
+ int k = 3;
+ int m = 1;
+ unsigned chunk_size = ErasureCode::SIMD_ALIGN * 7;
+ ErasureCodeTest erasure_code(k, m, chunk_size);
+
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < erasure_code.get_chunk_count(); i++)
+ want_to_encode.insert(i);
+ string data(chunk_size + chunk_size / 2, 'X'); // uses 1.5 chunks out of 3
+ // make sure nothing is memory aligned
+ bufferptr ptr(buffer::create_aligned(data.length() + 1, ErasureCode::SIMD_ALIGN));
+ ptr.copy_in(1, data.length(), data.c_str());
+ ptr.set_offset(1);
+ ptr.set_length(data.length());
+ bufferlist in;
+ in.append(ptr);
+ map<int, bufferlist> encoded;
+
+ ASSERT_FALSE(in.is_aligned(ErasureCode::SIMD_ALIGN));
+ ASSERT_EQ(0, erasure_code.encode(want_to_encode, in, &encoded));
+ for (unsigned int i = 0; i < erasure_code.get_chunk_count(); i++)
+ ASSERT_TRUE(encoded[i].is_aligned(ErasureCode::SIMD_ALIGN));
+ for (unsigned i = 0; i < chunk_size / 2; i++)
+ ASSERT_EQ(encoded[1][i], 'X');
+ ASSERT_NE(encoded[1][chunk_size / 2], 'X');
+}
+
+TEST(ErasureCodeTest, encode_misaligned_non_contiguous)
+{
+ int k = 3;
+ int m = 1;
+ unsigned chunk_size = ErasureCode::SIMD_ALIGN * 7;
+ ErasureCodeTest erasure_code(k, m, chunk_size);
+
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < erasure_code.get_chunk_count(); i++)
+ want_to_encode.insert(i);
+ string data(chunk_size, 'X');
+ // create a non contiguous bufferlist where the frist and the second
+ // bufferptr are not size aligned although they are memory aligned
+ bufferlist in;
+ {
+ bufferptr ptr(buffer::create_aligned(data.length() - 1, ErasureCode::SIMD_ALIGN));
+ in.append(ptr);
+ }
+ {
+ bufferptr ptr(buffer::create_aligned(data.length() + 1, ErasureCode::SIMD_ALIGN));
+ in.append(ptr);
+ }
+ map<int, bufferlist> encoded;
+
+ ASSERT_FALSE(in.is_contiguous());
+ ASSERT_TRUE(in.front().is_aligned(ErasureCode::SIMD_ALIGN));
+ ASSERT_FALSE(in.front().is_n_align_sized(chunk_size));
+ ASSERT_TRUE(in.back().is_aligned(ErasureCode::SIMD_ALIGN));
+ ASSERT_FALSE(in.back().is_n_align_sized(chunk_size));
+ ASSERT_EQ(0, erasure_code.encode(want_to_encode, in, &encoded));
+ for (unsigned int i = 0; i < erasure_code.get_chunk_count(); i++) {
+ ASSERT_TRUE(encoded[i].is_aligned(ErasureCode::SIMD_ALIGN));
+ ASSERT_TRUE(encoded[i].is_n_align_sized(chunk_size));
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make -j4 unittest_erasure_code &&
+ * valgrind --tool=memcheck --leak-check=full \
+ * ./unittest_erasure_code \
+ * --gtest_filter=*.* --log-to-stderr=true"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodeClay.cc b/src/test/erasure-code/TestErasureCodeClay.cc
new file mode 100644
index 000000000..cb4740948
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeClay.cc
@@ -0,0 +1,596 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2018 Indian Institute of Science <office.ece@iisc.ac.in>
+ *
+ * Author: Myna Vajha <mynaramana@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "crush/CrushWrapper.h"
+#include "include/stringify.h"
+#include "erasure-code/clay/ErasureCodeClay.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ErasureCodeClay, sanity_check_k)
+{
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "1";
+ profile["m"] = "1";
+ ostringstream errors;
+ EXPECT_EQ(-EINVAL, clay.init(profile, &errors));
+ EXPECT_NE(std::string::npos, errors.str().find("must be >= 2"));
+}
+
+TEST(ErasureCodeClay, encode_decode)
+{
+ ostringstream errors;
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ int r= clay.init(profile, &cerr);
+ EXPECT_EQ(0, r);
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+ int want_to_encode[] = { 0, 1, 2, 3 };
+ map<int, bufferlist> encoded;
+ EXPECT_EQ(0, clay.encode(set<int>(want_to_encode, want_to_encode+4),
+ in,
+ &encoded));
+ EXPECT_EQ(4u, encoded.size());
+ unsigned length = encoded[0].length();
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), length));
+ EXPECT_EQ(0, memcmp(encoded[1].c_str(), in.c_str() + length,
+ in.length() - length));
+
+
+ // all chunks are available
+ {
+ int want_to_decode[] = { 0, 1 };
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay._decode(set<int>(want_to_decode, want_to_decode+2),
+ encoded,
+ &decoded));
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(length, decoded[0].length());
+ EXPECT_EQ(0, memcmp(decoded[0].c_str(), in.c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[1].c_str(), in.c_str() + length,
+ in.length() - length));
+ }
+
+ // check all two chunks missing possibilities and recover them
+ for (int i=1; i<4; i++) {
+ for (int j=0; j<i; j++) {
+ map<int, bufferlist> degraded = encoded;
+ degraded.erase(j);
+ degraded.erase(i);
+ EXPECT_EQ(2u, degraded.size());
+ int want_to_decode[] = {j,i};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay._decode(set<int>(want_to_decode, want_to_decode+2),
+ degraded,
+ &decoded));
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(length, decoded[j].length());
+ EXPECT_EQ(0, memcmp(decoded[j].c_str(), encoded[j].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[i].c_str(), encoded[i].c_str(), length));
+ }
+ }
+ //check for all one chunk missing possibilities
+ int sc_size = length/clay.sub_chunk_no;
+ int avail[] = {0,1,2,3};
+ for (int i=0; i < 4; i++) {
+ set<int> want_to_read;
+ want_to_read.insert(i);
+ set<int> available(avail, avail+4);
+ available.erase(i);
+ map<int, vector<pair<int,int>>> minimum;
+ EXPECT_EQ(0, clay.minimum_to_decode(want_to_read, available, &minimum));
+ map<int, bufferlist> helper;
+ for (map<int, vector<pair<int,int>>>::iterator h=minimum.begin(); h!= minimum.end(); ++h) {
+ for(vector<pair<int,int>>::iterator ind=h->second.begin(); ind != h->second.end(); ++ind) {
+ bufferlist temp;
+ temp.substr_of(encoded[h->first], ind->first*sc_size, ind->second*sc_size);
+ helper[h->first].append(temp);
+ }
+ }
+ for (map<int, vector<pair<int,int>>>::iterator h=minimum.begin(); h!= minimum.end(); ++h) {
+ EXPECT_EQ(length/clay.q, helper[h->first].length());
+ }
+ EXPECT_EQ(3u, helper.size());
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay.decode(want_to_read, helper, &decoded, length));
+ EXPECT_EQ(1u, decoded.size());
+ EXPECT_EQ(0, memcmp(decoded[i].c_str(), encoded[i].c_str(), length));
+ }
+}
+
+
+TEST(ErasureCodeClay, encode_decode_aloof_nodes)
+{
+ ostringstream errors;
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "3";
+ profile["m"] = "3";
+ profile["d"] = "4";
+ int r= clay.init(profile, &cerr);
+ EXPECT_EQ(0, r);
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+ int want_to_encode[] = { 0, 1, 2, 3, 4, 5 };
+ map<int, bufferlist> encoded;
+ EXPECT_EQ(0, clay.encode(set<int>(want_to_encode, want_to_encode+6),
+ in,
+ &encoded));
+ EXPECT_EQ(6u, encoded.size());
+ unsigned length = encoded[0].length();
+ if (in.length() < length) {
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), in.length()));
+ } else if (in.length() <= 2*length ) {
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), in.length()));
+ EXPECT_EQ(0, memcmp(encoded[1].c_str(), in.c_str()+length, in.length()-length));
+ } else {
+ EXPECT_EQ(1, in.length() <= 3*length);
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), in.length()));
+ EXPECT_EQ(0, memcmp(encoded[1].c_str(), in.c_str()+length, length));
+ EXPECT_EQ(0, memcmp(encoded[2].c_str(), in.c_str()+2*length, in.length()-2*length));
+ }
+
+ // all chunks are available
+ {
+ int want_to_decode[] = { 0, 1, 2 };
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay._decode(set<int>(want_to_decode, want_to_decode+3),
+ encoded,
+ &decoded));
+ EXPECT_EQ(3u, decoded.size());
+ EXPECT_EQ(length, decoded[0].length());
+ EXPECT_EQ(0, memcmp(decoded[0].c_str(), encoded[0].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[1].c_str(), encoded[1].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[2].c_str(), encoded[2].c_str(), length));
+ }
+
+ // check all three chunks missing possibilities and recover them
+ for (int i=2; i<6; i++) {
+ for (int j=1; j<i; j++) {
+ for(int k=0; k<j; k++) {
+ map<int, bufferlist> degraded = encoded;
+ degraded.erase(k);
+ degraded.erase(j);
+ degraded.erase(i);
+ EXPECT_EQ(3u, degraded.size());
+ int want_to_decode[] = {k,j,i};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay._decode(set<int>(want_to_decode, want_to_decode+3),
+ degraded,
+ &decoded));
+ EXPECT_EQ(6u, decoded.size());
+ EXPECT_EQ(length, decoded[j].length());
+ EXPECT_EQ(0, memcmp(decoded[k].c_str(), encoded[k].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[j].c_str(), encoded[j].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[i].c_str(), encoded[i].c_str(), length));
+ }
+ }
+ }
+ //check for all one chunk missing possibilities
+ int sc_size = length/clay.sub_chunk_no;
+ int avail[] = {0,1,2,3,4,5};
+ for (int i=0; i < 6; i++) {
+ vector<pair<int,int>> repair_subchunks;
+ map<int, vector<pair<int,int>>> minimum;
+ set<int> want_to_read;
+ want_to_read.insert(i);
+ set<int> available(avail, avail+6);
+ available.erase(i);
+ clay.minimum_to_decode(want_to_read, available, &minimum);
+ map<int, bufferlist> helper;
+ for (map<int, vector<pair<int,int>>>::iterator h=minimum.begin(); h!= minimum.end(); ++h) {
+ for(vector<pair<int,int>>::iterator ind=h->second.begin(); ind != h->second.end(); ++ind) {
+ bufferlist temp;
+ temp.substr_of(encoded[h->first], ind->first*sc_size, ind->second*sc_size);
+ helper[h->first].append(temp);
+ }
+ }
+ for (map<int, vector<pair<int,int>>>::iterator h=minimum.begin(); h!= minimum.end(); ++h) {
+ EXPECT_EQ(length/clay.q, helper[h->first].length());
+ }
+ EXPECT_EQ((unsigned)clay.d, helper.size());
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay.decode(want_to_read, helper, &decoded, length));
+ EXPECT_EQ(1u, decoded.size());
+ EXPECT_EQ(0, memcmp(decoded[i].c_str(), encoded[i].c_str(), length));
+ }
+}
+
+TEST(ErasureCodeClay, encode_decode_shortening_case)
+{
+ ostringstream errors;
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "4";
+ profile["m"] = "3";
+ profile["d"] = "5";
+ int r= clay.init(profile, &cerr);
+ EXPECT_EQ(0, r);
+
+ EXPECT_EQ(2, clay.q);
+ EXPECT_EQ(4, clay.t);
+ EXPECT_EQ(1, clay.nu);
+
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+ int want_to_encode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> encoded;
+ EXPECT_EQ(0, clay.encode(set<int>(want_to_encode, want_to_encode+7),
+ in,
+ &encoded));
+ EXPECT_EQ(7u, encoded.size());
+ unsigned length = encoded[0].length();
+ if (in.length() < length) {
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), in.length()));
+ } else if (in.length() <= 2*length) {
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), in.length()));
+ EXPECT_EQ(0, memcmp(encoded[1].c_str(), in.c_str()+length, in.length()-length));
+ } else if (in.length() <= 3*length) {
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), in.length()));
+ EXPECT_EQ(0, memcmp(encoded[1].c_str(), in.c_str()+length, length));
+ EXPECT_EQ(0, memcmp(encoded[2].c_str(), in.c_str()+2*length, in.length()-2*length));
+ } else {
+ EXPECT_EQ(1, in.length() <= 4*length);
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), in.length()));
+ EXPECT_EQ(0, memcmp(encoded[1].c_str(), in.c_str()+length, length));
+ EXPECT_EQ(0, memcmp(encoded[2].c_str(), in.c_str()+2*length, length));
+ EXPECT_EQ(0, memcmp(encoded[3].c_str(), in.c_str()+3*length, in.length()-3*length));
+ }
+
+ // all chunks are available
+ {
+ int want_to_decode[] = { 0, 1, 2, 3 };
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay._decode(set<int>(want_to_decode, want_to_decode+4),
+ encoded,
+ &decoded));
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(length, decoded[0].length());
+ EXPECT_EQ(0, memcmp(decoded[0].c_str(), encoded[0].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[1].c_str(), encoded[1].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[2].c_str(), encoded[2].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[3].c_str(), encoded[3].c_str(), length));
+ }
+
+ // check all three chunks missing possibilities and recover them
+ for (int i=2; i<7; i++) {
+ for (int j=1; j<i; j++) {
+ for(int k=0; k<j; k++) {
+ map<int, bufferlist> degraded = encoded;
+ degraded.erase(k);
+ degraded.erase(j);
+ degraded.erase(i);
+ EXPECT_EQ(4u, degraded.size());
+ int want_to_decode[] = {k,j,i};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay._decode(set<int>(want_to_decode, want_to_decode+3),
+ degraded,
+ &decoded));
+ EXPECT_EQ(7u, decoded.size());
+ EXPECT_EQ(length, decoded[j].length());
+ EXPECT_EQ(0, memcmp(decoded[k].c_str(), encoded[k].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[j].c_str(), encoded[j].c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[i].c_str(), encoded[i].c_str(), length));
+ }
+ }
+ }
+ //check for all one chunk missing possibilities
+ int sc_size = length/clay.sub_chunk_no;
+ int avail[] = {0,1,2,3,4,5,6};
+ for (int i=0; i < 7; i++) {
+ vector<pair<int,int>> repair_subchunks;
+ map<int, vector<pair<int,int>>> minimum;
+ set<int> want_to_read;
+ want_to_read.insert(i);
+ set<int> available(avail, avail+7);
+ available.erase(i);
+ clay.minimum_to_decode(want_to_read, available, &minimum);
+ map<int, bufferlist> helper;
+ for (map<int, vector<pair<int,int>>>::iterator h=minimum.begin(); h!= minimum.end(); ++h) {
+ for(vector<pair<int,int>>::iterator ind=h->second.begin(); ind != h->second.end(); ++ind) {
+ bufferlist temp;
+ temp.substr_of(encoded[h->first], ind->first*sc_size, ind->second*sc_size);
+ helper[h->first].append(temp);
+ }
+ }
+ for (map<int, vector<pair<int,int>>>::iterator h=minimum.begin(); h!= minimum.end(); ++h) {
+ EXPECT_EQ(length/clay.q, helper[h->first].length());
+ }
+ EXPECT_EQ(static_cast<size_t>(clay.d), helper.size());
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, clay.decode(want_to_read, helper, &decoded, length));
+ EXPECT_EQ(1u, decoded.size());
+ EXPECT_EQ(length, decoded[i].length());
+ EXPECT_EQ(0, memcmp(decoded[i].c_str(), encoded[i].c_str(), length));
+ }
+}
+
+TEST(ErasureCodeClay, minimum_to_decode)
+{
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ EXPECT_EQ(0, clay.init(profile, &cerr));
+
+ //
+ // If trying to read nothing, the minimum is empty.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ EXPECT_EQ(0, clay._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_TRUE(minimum.empty());
+ }
+ //
+ // There is no way to read a chunk if none are available.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+
+ EXPECT_EQ(-EIO, clay._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ }
+ //
+ // Reading a subset of the available chunks is always possible.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+ available_chunks.insert(0);
+
+ EXPECT_EQ(0, clay._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(want_to_read, minimum);
+ }
+ //
+ // There is no way to read a missing chunk if there is less than k
+ // chunks available.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+ want_to_read.insert(1);
+ available_chunks.insert(0);
+
+ EXPECT_EQ(-EIO, clay._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ }
+ //
+ // When chunks are not available, the minimum can be made of any
+ // chunks. For instance, to read 1 and 3 below the minimum could be
+ // 2 and 3 which may seem better because it contains one of the
+ // chunks to be read. But it won't be more efficient than retrieving
+ // 0 and 2 instead because, in both cases, the decode function will
+ // need to run the same recovery operation and use the same amount
+ // of CPU and memory.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(1);
+ want_to_read.insert(3);
+ available_chunks.insert(0);
+ available_chunks.insert(2);
+ available_chunks.insert(3);
+
+ EXPECT_EQ(0, clay._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(2u, minimum.size());
+ EXPECT_EQ(0u, minimum.count(3));
+ }
+}
+
+TEST(ErasureCodeClay, encode)
+{
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ EXPECT_EQ(0, clay.init(profile, &cerr));
+
+ unsigned aligned_object_size = clay.get_chunk_size(1) * 2 * 2;
+ {
+ //
+ // When the input bufferlist needs to be padded because
+ // it is not properly aligned, it is padded with zeros.
+ //
+ bufferlist in;
+ map<int,bufferlist> encoded;
+ int want_to_encode[] = { 0, 1, 2, 3 };
+ int trail_length = 1;
+ in.append(string(aligned_object_size + trail_length, 'X'));
+ EXPECT_EQ(0, clay.encode(set<int>(want_to_encode, want_to_encode+4),
+ in,
+ &encoded));
+ EXPECT_EQ(4u, encoded.size());
+ char *last_chunk = encoded[1].c_str();
+ int length =encoded[1].length();
+ EXPECT_EQ('X', last_chunk[0]);
+ EXPECT_EQ('\0', last_chunk[length - trail_length]);
+ }
+
+ {
+ //
+ // When only the first chunk is required, the encoded map only
+ // contains the first chunk. Although the clay encode
+ // internally allocated a buffer because of padding requirements
+ // and also computes the coding chunks, they are released before
+ // the return of the method, as shown when running the tests thru
+ // valgrind (there is no leak).
+ //
+ bufferlist in;
+ map<int,bufferlist> encoded;
+ set<int> want_to_encode;
+ want_to_encode.insert(0);
+ int trail_length = 1;
+ in.append(string(aligned_object_size + trail_length, 'X'));
+ EXPECT_EQ(0, clay.encode(want_to_encode, in, &encoded));
+ EXPECT_EQ(1u, encoded.size());
+ }
+}
+
+TEST(ErasureCodeClay, create_rule)
+{
+ std::unique_ptr<CrushWrapper> c = std::make_unique<CrushWrapper>();
+ c->create();
+ int root_type = 2;
+ c->set_type_name(root_type, "root");
+ int host_type = 1;
+ c->set_type_name(host_type, "host");
+ int osd_type = 0;
+ c->set_type_name(osd_type, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ root_type, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int num_host = 4;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h=0; h<num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o=0; o<num_osd; ++o, ++osd) {
+ c->insert_item(g_ceph_context, osd, 1.0, string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ c->finalize();
+
+ {
+ stringstream ss;
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ EXPECT_EQ(0, clay.init(profile, &cerr));
+ int ruleid = clay.create_rule("myrule", *c, &ss);
+ EXPECT_EQ(0, ruleid);
+ EXPECT_EQ(-EEXIST, clay.create_rule("myrule", *c, &ss));
+ //
+ // the minimum that is expected from the created rule is to
+ // successfully map get_chunk_count() devices from the crushmap,
+ // at least once.
+ //
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+ vector<int> out;
+ int x = 0;
+ c->do_rule(ruleid, x, out, clay.get_chunk_count(), weight, 0);
+ ASSERT_EQ(out.size(), clay.get_chunk_count());
+ for (unsigned i=0; i<out.size(); ++i)
+ ASSERT_NE(CRUSH_ITEM_NONE, out[i]);
+ }
+ {
+ stringstream ss;
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["crush-root"] = "BAD";
+ EXPECT_EQ(0, clay.init(profile, &cerr));
+ EXPECT_EQ(-ENOENT, clay.create_rule("otherrule", *c, &ss));
+ EXPECT_EQ("root item BAD does not exist", ss.str());
+ }
+ {
+ stringstream ss;
+ ErasureCodeClay clay(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["crush-failure-domain"] = "WORSE";
+ EXPECT_EQ(0, clay.init(profile, &cerr));
+ EXPECT_EQ(-EINVAL, clay.create_rule("otherrule", *c, &ss));
+ EXPECT_EQ("unknown type WORSE", ss.str());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make -j4 unittest_erasure_code_clay &&
+ * valgrind --tool=memcheck \
+ * ./unittest_erasure_code_clay \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodeExample.cc b/src/test/erasure-code/TestErasureCodeExample.cc
new file mode 100644
index 000000000..b488a604b
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeExample.cc
@@ -0,0 +1,248 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+#include <stdlib.h>
+
+#include "include/stringify.h"
+#include "ErasureCodeExample.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ErasureCodeExample, chunk_size)
+{
+ ErasureCodeExample example;
+ EXPECT_EQ(3u, example.get_chunk_count());
+ EXPECT_EQ(11u, example.get_chunk_size(20));
+}
+
+TEST(ErasureCodeExample, minimum_to_decode)
+{
+ ErasureCodeExample example;
+ set<int> available_chunks;
+ set<int> want_to_read;
+ want_to_read.insert(1);
+ {
+ set<int> minimum;
+ EXPECT_EQ(-EIO, example._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ }
+ available_chunks.insert(0);
+ available_chunks.insert(2);
+ {
+ set<int> minimum;
+ EXPECT_EQ(0, example._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(available_chunks, minimum);
+ EXPECT_EQ(2u, minimum.size());
+ EXPECT_EQ(1u, minimum.count(0));
+ EXPECT_EQ(1u, minimum.count(2));
+ }
+ {
+ set<int> minimum;
+ available_chunks.insert(1);
+ EXPECT_EQ(0, example._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(1u, minimum.size());
+ EXPECT_EQ(1u, minimum.count(1));
+ }
+}
+
+TEST(ErasureCodeExample, minimum_to_decode_with_cost)
+{
+ ErasureCodeExample example;
+ map<int,int> available;
+ set<int> want_to_read;
+ want_to_read.insert(1);
+ {
+ set<int> minimum;
+ EXPECT_EQ(-EIO, example.minimum_to_decode_with_cost(want_to_read,
+ available,
+ &minimum));
+ }
+ available[0] = 1;
+ available[2] = 1;
+ {
+ set<int> minimum;
+ EXPECT_EQ(0, example.minimum_to_decode_with_cost(want_to_read,
+ available,
+ &minimum));
+ EXPECT_EQ(2u, minimum.size());
+ EXPECT_EQ(1u, minimum.count(0));
+ EXPECT_EQ(1u, minimum.count(2));
+ }
+ {
+ set<int> minimum;
+ available[1] = 1;
+ EXPECT_EQ(0, example.minimum_to_decode_with_cost(want_to_read,
+ available,
+ &minimum));
+ EXPECT_EQ(1u, minimum.size());
+ EXPECT_EQ(1u, minimum.count(1));
+ }
+ {
+ set<int> minimum;
+ available[1] = 2;
+ EXPECT_EQ(0, example.minimum_to_decode_with_cost(want_to_read,
+ available,
+ &minimum));
+ EXPECT_EQ(2u, minimum.size());
+ EXPECT_EQ(1u, minimum.count(0));
+ EXPECT_EQ(1u, minimum.count(2));
+ }
+}
+
+TEST(ErasureCodeExample, encode_decode)
+{
+ ErasureCodeExample example;
+
+ bufferlist in;
+ in.append("ABCDE");
+ set<int> want_to_encode;
+ for(unsigned int i = 0; i < example.get_chunk_count(); i++)
+ want_to_encode.insert(i);
+ map<int, bufferlist> encoded;
+ EXPECT_EQ(0, example.encode(want_to_encode, in, &encoded));
+ EXPECT_EQ(example.get_chunk_count(), encoded.size());
+ EXPECT_EQ(example.get_chunk_size(in.length()), encoded[0].length());
+ EXPECT_EQ('A', encoded[0][0]);
+ EXPECT_EQ('B', encoded[0][1]);
+ EXPECT_EQ('C', encoded[0][2]);
+ EXPECT_EQ('D', encoded[1][0]);
+ EXPECT_EQ('E', encoded[1][1]);
+ EXPECT_EQ('A'^'D', encoded[2][0]);
+ EXPECT_EQ('B'^'E', encoded[2][1]);
+ EXPECT_EQ('C'^0, encoded[2][2]);
+
+ // all chunks are available
+ {
+ int want_to_decode[] = { 0, 1 };
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, example._decode(set<int>(want_to_decode, want_to_decode+2),
+ encoded,
+ &decoded));
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(3u, decoded[0].length());
+ EXPECT_EQ('A', decoded[0][0]);
+ EXPECT_EQ('B', decoded[0][1]);
+ EXPECT_EQ('C', decoded[0][2]);
+ EXPECT_EQ('D', decoded[1][0]);
+ EXPECT_EQ('E', decoded[1][1]);
+ }
+
+ // one chunk is missing
+ {
+ map<int, bufferlist> degraded = encoded;
+ degraded.erase(0);
+ EXPECT_EQ(2u, degraded.size());
+ int want_to_decode[] = { 0, 1 };
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, example._decode(set<int>(want_to_decode, want_to_decode+2),
+ degraded,
+ &decoded));
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(3u, decoded[0].length());
+ EXPECT_EQ('A', decoded[0][0]);
+ EXPECT_EQ('B', decoded[0][1]);
+ EXPECT_EQ('C', decoded[0][2]);
+ EXPECT_EQ('D', decoded[1][0]);
+ EXPECT_EQ('E', decoded[1][1]);
+ }
+}
+
+TEST(ErasureCodeExample, decode)
+{
+ ErasureCodeExample example;
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+ int want_to_encode[] = { 0, 1, 2 };
+ map<int, bufferlist> encoded;
+ EXPECT_EQ(0, example.encode(set<int>(want_to_encode, want_to_encode+3),
+ in,
+ &encoded));
+ EXPECT_EQ(3u, encoded.size());
+
+ // successfull decode
+ bufferlist out;
+ EXPECT_EQ(0, example.decode_concat(encoded, &out));
+ bufferlist usable;
+ usable.substr_of(out, 0, in.length());
+ EXPECT_TRUE(usable == in);
+
+ // cannot recover
+ map<int, bufferlist> degraded;
+ degraded[0] = encoded[0];
+ EXPECT_EQ(-ERANGE, example.decode_concat(degraded, &out));
+}
+
+TEST(ErasureCodeExample, create_rule)
+{
+ std::unique_ptr<CrushWrapper> c = std::make_unique<CrushWrapper>();
+ c->create();
+ c->set_type_name(2, "root");
+ c->set_type_name(1, "host");
+ c->set_type_name(0, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ 5, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int num_host = 2;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h=0; h<num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o=0; o<num_osd; ++o, ++osd) {
+ c->insert_item(g_ceph_context, osd, 1.0, string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ stringstream ss;
+ ErasureCodeExample example;
+ EXPECT_EQ(0, example.create_rule("myrule", *c, &ss));
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make -j4 &&
+ * make unittest_erasure_code_example &&
+ * valgrind --leak-check=full --tool=memcheck \
+ * ./unittest_erasure_code_example --gtest_filter=*.* \
+ * --log-to-stderr=true --debug-osd=20
+ * "
+ * End:
+ */
+
diff --git a/src/test/erasure-code/TestErasureCodeIsa.cc b/src/test/erasure-code/TestErasureCodeIsa.cc
new file mode 100644
index 000000000..bbd4441fc
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeIsa.cc
@@ -0,0 +1,967 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 CERN (Switzerland)
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Andreas-Joachim Peters <Andreas.Joachim.Peters@cern.ch>
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "crush/CrushWrapper.h"
+#include "include/stringify.h"
+#include "erasure-code/isa/ErasureCodeIsa.h"
+#include "erasure-code/isa/xor_op.h"
+#include "global/global_context.h"
+#include "common/config.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+ErasureCodeIsaTableCache tcache;
+
+class IsaErasureCodeTest : public ::testing::Test {
+public:
+ void compare_chunks(bufferlist &in, map<int, bufferlist> &encoded);
+ void encode_decode(unsigned object_size);
+};
+
+void IsaErasureCodeTest::compare_chunks(bufferlist &in, map<int, bufferlist> &encoded)
+{
+ unsigned object_size = in.length();
+ unsigned chunk_size = encoded[0].length();
+ for (unsigned i = 0; i < encoded.size(); i++) {
+ if (i * chunk_size >= object_size)
+ break;
+ int chunk_length = object_size > (i + 1) * chunk_size ? chunk_size : object_size - i * chunk_size;
+ EXPECT_EQ(0, memcmp(encoded[i].c_str(), in.c_str() + i * chunk_size, chunk_length));
+ }
+}
+
+void IsaErasureCodeTest::encode_decode(unsigned object_size)
+{
+ ErasureCodeIsaDefault Isa(tcache);
+
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ Isa.init(profile, &cerr);
+
+ string payload(object_size, 'X');
+ bufferlist in;
+ // may be multiple bufferptr if object_size is larger than CEPH_PAGE_SIZE
+ in.append(payload.c_str(), payload.length());
+ int want_to_encode[] = {0, 1, 2, 3};
+ map<int, bufferlist> encoded;
+ EXPECT_EQ(0, Isa.encode(set<int>(want_to_encode, want_to_encode + 4),
+ in,
+ &encoded));
+ EXPECT_EQ(4u, encoded.size());
+ unsigned chunk_size = encoded[0].length();
+ EXPECT_EQ(chunk_size, Isa.get_chunk_size(object_size));
+ compare_chunks(in, encoded);
+
+ // all chunks are available
+ {
+ int want_to_decode[] = {0, 1};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, Isa._decode(set<int>(want_to_decode, want_to_decode + 2),
+ encoded,
+ &decoded));
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(chunk_size, decoded[0].length());
+ compare_chunks(in, decoded);
+ }
+
+ // one data chunk is missing
+ {
+ map<int, bufferlist> degraded = encoded;
+
+ string enc1(encoded[1].c_str(), chunk_size);
+
+ degraded.erase(1);
+ EXPECT_EQ(3u, degraded.size());
+ int want_to_decode[] = {1};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, Isa._decode(set<int>(want_to_decode, want_to_decode + 1),
+ degraded,
+ &decoded));
+ // always decode all, regardless of want_to_decode
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(chunk_size, decoded[1].length());
+ EXPECT_EQ(0, memcmp(decoded[1].c_str(), enc1.c_str(), chunk_size));
+ }
+
+ // non-xor coding chunk is missing
+ {
+ map<int, bufferlist> degraded = encoded;
+
+ string enc3(encoded[3].c_str(), chunk_size);
+
+ degraded.erase(3);
+ EXPECT_EQ(3u, degraded.size());
+ int want_to_decode[] = {3};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, Isa._decode(set<int>(want_to_decode, want_to_decode + 1),
+ degraded,
+ &decoded));
+ // always decode all, regardless of want_to_decode
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(chunk_size, decoded[3].length());
+ EXPECT_EQ(0, memcmp(decoded[3].c_str(), enc3.c_str(), chunk_size));
+ }
+
+ // xor coding chunk is missing
+ {
+ map<int, bufferlist> degraded = encoded;
+
+ string enc2(encoded[2].c_str(), chunk_size);
+
+ degraded.erase(2);
+ EXPECT_EQ(3u, degraded.size());
+ int want_to_decode[] = {2};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, Isa._decode(set<int>(want_to_decode, want_to_decode + 1),
+ degraded,
+ &decoded));
+ // always decode all, regardless of want_to_decode
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(chunk_size, decoded[2].length());
+ EXPECT_EQ(0, memcmp(decoded[2].c_str(), enc2.c_str(), chunk_size));
+ }
+
+ // one data and one coding chunk is missing
+ {
+ map<int, bufferlist> degraded = encoded;
+
+ string enc3(encoded[3].c_str(), chunk_size);
+
+ degraded.erase(1);
+ degraded.erase(3);
+ EXPECT_EQ(2u, degraded.size());
+ int want_to_decode[] = {1, 3};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, Isa._decode(set<int>(want_to_decode, want_to_decode + 2),
+ degraded,
+ &decoded));
+ // always decode all, regardless of want_to_decode
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(chunk_size, decoded[1].length());
+ EXPECT_EQ(0, memcmp(decoded[3].c_str(), enc3.c_str(), chunk_size));
+ }
+
+ // two data chunks are missing
+ {
+ map<int, bufferlist> degraded = encoded;
+ degraded.erase(0);
+ degraded.erase(1);
+ EXPECT_EQ(2u, degraded.size());
+ int want_to_decode[] = {0, 1};
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, Isa._decode(set<int>(want_to_decode, want_to_decode + 2),
+ degraded,
+ &decoded));
+ // always decode all, regardless of want_to_decode
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(chunk_size, decoded[0].length());
+ compare_chunks(in, decoded);
+ }
+
+}
+
+TEST_F(IsaErasureCodeTest, encode_decode)
+{
+ encode_decode(1);
+ encode_decode(EC_ISA_ADDRESS_ALIGNMENT);
+ encode_decode(EC_ISA_ADDRESS_ALIGNMENT + 1);
+ encode_decode(2048);
+ encode_decode(4096);
+ encode_decode(4096 + 1);
+}
+
+TEST_F(IsaErasureCodeTest, minimum_to_decode)
+{
+ ErasureCodeIsaDefault Isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ Isa.init(profile, &cerr);
+
+ //
+ // If trying to read nothing, the minimum is empty.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ EXPECT_EQ(0, Isa._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_TRUE(minimum.empty());
+ }
+ //
+ // There is no way to read a chunk if none are available.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+
+ EXPECT_EQ(-EIO, Isa._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ }
+ //
+ // Reading a subset of the available chunks is always possible.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+ available_chunks.insert(0);
+
+ EXPECT_EQ(0, Isa._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(want_to_read, minimum);
+ }
+ //
+ // There is no way to read a missing chunk if there is less than k
+ // chunks available.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+ want_to_read.insert(1);
+ available_chunks.insert(0);
+
+ EXPECT_EQ(-EIO, Isa._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ }
+ //
+ // When chunks are not available, the minimum can be made of any
+ // chunks. For instance, to read 1 and 3 below the minimum could be
+ // 2 and 3 which may seem better because it contains one of the
+ // chunks to be read. But it won't be more efficient than retrieving
+ // 0 and 2 instead because, in both cases, the decode function will
+ // need to run the same recovery operation and use the same amount
+ // of CPU and memory.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(1);
+ want_to_read.insert(3);
+ available_chunks.insert(0);
+ available_chunks.insert(2);
+ available_chunks.insert(3);
+
+ EXPECT_EQ(0, Isa._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(2u, minimum.size());
+ EXPECT_EQ(0u, minimum.count(3));
+ }
+}
+
+TEST_F(IsaErasureCodeTest, chunk_size)
+{
+ {
+ ErasureCodeIsaDefault Isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "1";
+ Isa.init(profile, &cerr);
+ const int k = 2;
+
+ ASSERT_EQ(EC_ISA_ADDRESS_ALIGNMENT, Isa.get_chunk_size(1));
+ ASSERT_EQ(EC_ISA_ADDRESS_ALIGNMENT, Isa.get_chunk_size(EC_ISA_ADDRESS_ALIGNMENT * k - 1));
+ ASSERT_EQ(EC_ISA_ADDRESS_ALIGNMENT * 2, Isa.get_chunk_size(EC_ISA_ADDRESS_ALIGNMENT * k + 1));
+ }
+ {
+ ErasureCodeIsaDefault Isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "3";
+ profile["m"] = "1";
+ Isa.init(profile, &cerr);
+ const int k = 3;
+
+ ASSERT_EQ(EC_ISA_ADDRESS_ALIGNMENT, Isa.get_chunk_size(1));
+ ASSERT_EQ(EC_ISA_ADDRESS_ALIGNMENT, Isa.get_chunk_size(EC_ISA_ADDRESS_ALIGNMENT * k - 1));
+ ASSERT_EQ(EC_ISA_ADDRESS_ALIGNMENT * 2, Isa.get_chunk_size(EC_ISA_ADDRESS_ALIGNMENT * k + 1));
+ unsigned object_size = EC_ISA_ADDRESS_ALIGNMENT * k * 1024 + 1;
+ ASSERT_NE(0u, object_size % k);
+ ASSERT_NE(0u, object_size % EC_ISA_ADDRESS_ALIGNMENT);
+ unsigned chunk_size = Isa.get_chunk_size(object_size);
+ ASSERT_EQ(0u, chunk_size % EC_ISA_ADDRESS_ALIGNMENT);
+ ASSERT_GT(chunk_size, (chunk_size * k) - object_size);
+ }
+}
+
+TEST_F(IsaErasureCodeTest, encode)
+{
+ ErasureCodeIsaDefault Isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ Isa.init(profile, &cerr);
+
+ unsigned aligned_object_size = Isa.get_alignment() * 2;
+ {
+ //
+ // When the input bufferlist needs to be padded because
+ // it is not properly aligned, it is padded with zeros.
+ //
+ bufferlist in;
+ map<int,bufferlist> encoded;
+ int want_to_encode[] = { 0, 1, 2, 3 };
+ int trail_length = 1;
+ in.append(string(aligned_object_size + trail_length, 'X'));
+ EXPECT_EQ(0, Isa.encode(set<int>(want_to_encode, want_to_encode+4),
+ in,
+ &encoded));
+ EXPECT_EQ(4u, encoded.size());
+ char *last_chunk = encoded[1].c_str();
+ int length =encoded[1].length();
+ EXPECT_EQ('X', last_chunk[0]);
+ EXPECT_EQ('\0', last_chunk[length - trail_length]);
+ }
+
+ {
+ //
+ // When only the first chunk is required, the encoded map only
+ // contains the first chunk. Although the Isa encode
+ // internally allocated a buffer because of padding requirements
+ // and also computes the coding chunks, they are released before
+ // the return of the method, as shown when running the tests thru
+ // valgrind (there is no leak).
+ //
+ bufferlist in;
+ map<int,bufferlist> encoded;
+ set<int> want_to_encode;
+ want_to_encode.insert(0);
+ int trail_length = 1;
+ in.append(string(aligned_object_size + trail_length, 'X'));
+ EXPECT_EQ(0, Isa.encode(want_to_encode, in, &encoded));
+ EXPECT_EQ(1u, encoded.size());
+ }
+}
+
+TEST_F(IsaErasureCodeTest, sanity_check_k)
+{
+ ErasureCodeIsaDefault Isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "1";
+ profile["m"] = "1";
+ ostringstream errors;
+ EXPECT_EQ(-EINVAL, Isa.init(profile, &errors));
+ EXPECT_NE(std::string::npos, errors.str().find("must be >= 2"));
+}
+
+bool
+DecodeAndVerify(ErasureCodeIsaDefault& Isa, map<int, bufferlist> &degraded, set<int> want_to_decode, buffer::ptr* enc, int length)
+{
+ map<int, bufferlist> decoded;
+ bool ok;
+
+ // decode as requested
+ ok = Isa._decode(want_to_decode,
+ degraded,
+ &decoded);
+
+ for (int i = 0; i < (int) decoded.size(); i++) {
+ // compare all the buffers with their original
+ ok |= memcmp(decoded[i].c_str(), enc[i].c_str(), length);
+ }
+
+ return ok;
+}
+
+TEST_F(IsaErasureCodeTest, isa_vandermonde_exhaustive)
+{
+ // Test all possible failure scenarios and reconstruction cases for
+ // a (12,4) configuration using the vandermonde matrix
+
+ ErasureCodeIsaDefault Isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "12";
+ profile["m"] = "4";
+ Isa.init(profile, &cerr);
+
+ const int k = 12;
+ const int m = 4;
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+
+ set<int>want_to_encode;
+
+ map<int, bufferlist> encoded;
+ for (int i = 0; i < (k + m); i++) {
+ want_to_encode.insert(i);
+ }
+
+
+ EXPECT_EQ(0, Isa.encode(want_to_encode,
+ in,
+ &encoded));
+
+ EXPECT_EQ((unsigned) (k + m), encoded.size());
+
+ unsigned length = encoded[0].length();
+
+ for (int i = 0; i < k; i++) {
+ EXPECT_EQ(0, memcmp(encoded[i].c_str(), in.c_str() + (i * length), length));
+ }
+
+ buffer::ptr enc[k + m];
+ // create buffers with a copy of the original data to be able to compare it after decoding
+ {
+ for (int i = 0; i < (k + m); i++) {
+ buffer::ptr newenc(buffer::create_page_aligned(LARGE_ENOUGH));
+ enc[i] = newenc;
+ enc[i].zero();
+ enc[i].set_length(0);
+ enc[i].append(encoded[i].c_str(), length);
+ }
+ }
+
+ // loop through all possible loss scenarios
+ int cnt_cf = 0;
+
+ for (int l1 = 0; l1 < (k + m); l1++) {
+ map<int, bufferlist> degraded = encoded;
+ set<int> want_to_decode;
+ bool err;
+ degraded.erase(l1);
+ want_to_decode.insert(l1);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l2 = l1 + 1; l2 < (k + m); l2++) {
+ degraded.erase(l2);
+ want_to_decode.insert(l2);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l3 = l2 + 1; l3 < (k + m); l3++) {
+ degraded.erase(l3);
+ want_to_decode.insert(l3);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l4 = l3 + 1; l4 < (k + m); l4++) {
+ degraded.erase(l4);
+ want_to_decode.insert(l4);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ degraded[l4] = encoded[l4];
+ want_to_decode.erase(l4);
+ cnt_cf++;
+ }
+ degraded[l3] = encoded[l3];
+ want_to_decode.erase(l3);
+ }
+ degraded[l2] = encoded[l2];
+ want_to_decode.erase(l2);
+ }
+ degraded[l1] = encoded[l1];
+ want_to_decode.erase(l1);
+ }
+ EXPECT_EQ(2516, cnt_cf);
+ EXPECT_EQ(2506, tcache.getDecodingTableCacheSize()); // 3 entries from (2,2) test and 2503 from (12,4)
+}
+
+TEST_F(IsaErasureCodeTest, isa_cauchy_exhaustive)
+{
+ // Test all possible failure scenarios and reconstruction cases for
+ // a (12,4) configuration using the cauchy matrix
+ ErasureCodeIsaDefault Isa(tcache,ErasureCodeIsaDefault::kCauchy);
+ ErasureCodeProfile profile;
+ profile["k"] = "12";
+ profile["m"] = "4";
+ profile["technique"] = "cauchy";
+
+ Isa.init(profile, &cerr);
+
+ const int k = 12;
+ const int m = 4;
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+
+ set<int>want_to_encode;
+
+ map<int, bufferlist> encoded;
+ for (int i = 0; i < (k + m); i++) {
+ want_to_encode.insert(i);
+ }
+
+
+ EXPECT_EQ(0, Isa.encode(want_to_encode,
+ in,
+ &encoded));
+
+ EXPECT_EQ((unsigned) (k + m), encoded.size());
+
+ unsigned length = encoded[0].length();
+
+ for (int i = 0; i < k; i++) {
+ EXPECT_EQ(0, memcmp(encoded[i].c_str(), in.c_str() + (i * length), length));
+ }
+
+ buffer::ptr enc[k + m];
+ // create buffers with a copy of the original data to be able to compare it after decoding
+ {
+ for (int i = 0; i < (k + m); i++) {
+ buffer::ptr newenc(buffer::create_page_aligned(LARGE_ENOUGH));
+ enc[i] = newenc;
+ enc[i].zero();
+ enc[i].set_length(0);
+ enc[i].append(encoded[i].c_str(), length);
+ }
+ }
+
+ // loop through all possible loss scenarios
+ int cnt_cf = 0;
+
+ for (int l1 = 0; l1 < (k + m); l1++) {
+ map<int, bufferlist> degraded = encoded;
+ set<int> want_to_decode;
+ bool err;
+ degraded.erase(l1);
+ want_to_decode.insert(l1);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l2 = l1 + 1; l2 < (k + m); l2++) {
+ degraded.erase(l2);
+ want_to_decode.insert(l2);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l3 = l2 + 1; l3 < (k + m); l3++) {
+ degraded.erase(l3);
+ want_to_decode.insert(l3);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l4 = l3 + 1; l4 < (k + m); l4++) {
+ degraded.erase(l4);
+ want_to_decode.insert(l4);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ degraded[l4] = encoded[l4];
+ want_to_decode.erase(l4);
+ cnt_cf++;
+ }
+ degraded[l3] = encoded[l3];
+ want_to_decode.erase(l3);
+ }
+ degraded[l2] = encoded[l2];
+ want_to_decode.erase(l2);
+ }
+ degraded[l1] = encoded[l1];
+ want_to_decode.erase(l1);
+ }
+ EXPECT_EQ(2516, cnt_cf);
+ EXPECT_EQ(2516, tcache.getDecodingTableCacheSize(ErasureCodeIsaDefault::kCauchy));
+}
+
+TEST_F(IsaErasureCodeTest, isa_cauchy_cache_trash)
+{
+ // Test all possible failure scenarios and reconstruction cases for
+ // a (12,4) configuration using the cauchy matrix
+ ErasureCodeIsaDefault Isa(tcache,ErasureCodeIsaDefault::kCauchy);
+ ErasureCodeProfile profile;
+ profile["k"] = "16";
+ profile["m"] = "4";
+ profile["technique"] = "cauchy";
+
+ Isa.init(profile, &cerr);
+
+ const int k = 16;
+ const int m = 4;
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+
+ set<int>want_to_encode;
+
+ map<int, bufferlist> encoded;
+ for (int i = 0; i < (k + m); i++) {
+ want_to_encode.insert(i);
+ }
+
+
+ EXPECT_EQ(0, Isa.encode(want_to_encode,
+ in,
+ &encoded));
+
+ EXPECT_EQ((unsigned) (k + m), encoded.size());
+
+ unsigned length = encoded[0].length();
+
+ for (int i = 0; i < k; i++) {
+ EXPECT_EQ(0, memcmp(encoded[i].c_str(), in.c_str() + (i * length), length));
+ }
+
+ buffer::ptr enc[k + m];
+ // create buffers with a copy of the original data to be able to compare it after decoding
+ {
+ for (int i = 0; i < (k + m); i++) {
+ buffer::ptr newenc(buffer::create_page_aligned(LARGE_ENOUGH));
+ enc[i] = newenc;
+ enc[i].zero();
+ enc[i].set_length(0);
+ enc[i].append(encoded[i].c_str(), length);
+ }
+ }
+
+ // loop through all possible loss scenarios
+ int cnt_cf = 0;
+
+ for (int l1 = 0; l1 < (k + m); l1++) {
+ map<int, bufferlist> degraded = encoded;
+ set<int> want_to_decode;
+ bool err;
+ degraded.erase(l1);
+ want_to_decode.insert(l1);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l2 = l1 + 1; l2 < (k + m); l2++) {
+ degraded.erase(l2);
+ want_to_decode.insert(l2);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l3 = l2 + 1; l3 < (k + m); l3++) {
+ degraded.erase(l3);
+ want_to_decode.insert(l3);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ for (int l4 = l3 + 1; l4 < (k + m); l4++) {
+ degraded.erase(l4);
+ want_to_decode.insert(l4);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ degraded[l4] = encoded[l4];
+ want_to_decode.erase(l4);
+ cnt_cf++;
+ }
+ degraded[l3] = encoded[l3];
+ want_to_decode.erase(l3);
+ }
+ degraded[l2] = encoded[l2];
+ want_to_decode.erase(l2);
+ }
+ degraded[l1] = encoded[l1];
+ want_to_decode.erase(l1);
+ }
+ EXPECT_EQ(6195, cnt_cf);
+ EXPECT_EQ(2516, tcache.getDecodingTableCacheSize(ErasureCodeIsaDefault::kCauchy));
+}
+
+TEST_F(IsaErasureCodeTest, isa_xor_codec)
+{
+ // Test all possible failure scenarios and reconstruction cases for
+ // a (4,1) RAID-5 like configuration
+
+ ErasureCodeIsaDefault Isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "4";
+ profile["m"] = "1";
+ Isa.init(profile, &cerr);
+
+ const int k = 4;
+ const int m = 1;
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+
+ set<int>want_to_encode;
+
+ map<int, bufferlist> encoded;
+ for (int i = 0; i < (k + m); i++) {
+ want_to_encode.insert(i);
+ }
+
+
+ EXPECT_EQ(0, Isa.encode(want_to_encode,
+ in,
+ &encoded));
+
+ EXPECT_EQ((unsigned) (k + m), encoded.size());
+
+ unsigned length = encoded[0].length();
+
+ for (int i = 0; i < k; i++) {
+ EXPECT_EQ(0, memcmp(encoded[i].c_str(), in.c_str() + (i * length), length));
+ }
+
+ buffer::ptr enc[k + m];
+ // create buffers with a copy of the original data to be able to compare it after decoding
+ {
+ for (int i = 0; i < (k + m); i++) {
+ buffer::ptr newenc(buffer::create_page_aligned(LARGE_ENOUGH));
+ enc[i] = newenc;
+ enc[i].zero();
+ enc[i].set_length(0);
+ enc[i].append(encoded[i].c_str(), length);
+ }
+ }
+
+ // loop through all possible loss scenarios
+ int cnt_cf = 0;
+
+ for (int l1 = 0; l1 < (k + m); l1++) {
+ map<int, bufferlist> degraded = encoded;
+ set<int> want_to_decode;
+ bool err;
+ degraded.erase(l1);
+ want_to_decode.insert(l1);
+ err = DecodeAndVerify(Isa, degraded, want_to_decode, enc, length);
+ EXPECT_EQ(0, err);
+ cnt_cf++;
+ degraded[l1] = encoded[l1];
+ want_to_decode.erase(l1);
+ }
+ EXPECT_EQ(5, cnt_cf);
+}
+
+TEST_F(IsaErasureCodeTest, create_rule)
+{
+ std::unique_ptr<CrushWrapper> c = std::make_unique<CrushWrapper>();
+ c->create();
+ int root_type = 2;
+ c->set_type_name(root_type, "root");
+ int host_type = 1;
+ c->set_type_name(host_type, "host");
+ int osd_type = 0;
+ c->set_type_name(osd_type, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ root_type, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int num_host = 4;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h=0; h<num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o=0; o<num_osd; ++o, ++osd) {
+ c->insert_item(g_ceph_context, osd, 1.0, string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ c->finalize();
+
+ {
+ stringstream ss;
+ ErasureCodeIsaDefault isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "8";
+ isa.init(profile, &cerr);
+ int rule = isa.create_rule("myrule", *c, &ss);
+ EXPECT_EQ(0, rule);
+ EXPECT_EQ(-EEXIST, isa.create_rule("myrule", *c, &ss));
+ //
+ // the minimum that is expected from the created rule is to
+ // successfully map get_chunk_count() devices from the crushmap,
+ // at least once.
+ //
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+ vector<int> out;
+ int x = 0;
+ c->do_rule(rule, x, out, isa.get_chunk_count(), weight, 0);
+ ASSERT_EQ(out.size(), isa.get_chunk_count());
+ for (unsigned i=0; i<out.size(); ++i)
+ ASSERT_NE(CRUSH_ITEM_NONE, out[i]);
+ }
+ {
+ stringstream ss;
+ ErasureCodeIsaDefault isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "8";
+ profile["crush-root"] = "BAD";
+ isa.init(profile, &cerr);
+ EXPECT_EQ(-ENOENT, isa.create_rule("otherrule", *c, &ss));
+ EXPECT_EQ("root item BAD does not exist", ss.str());
+ }
+ {
+ stringstream ss;
+ ErasureCodeIsaDefault isa(tcache);
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "8";
+ profile["crush-failure-domain"] = "WORSE";
+ isa.init(profile, &cerr);
+ EXPECT_EQ(-EINVAL, isa.create_rule("otherrule", *c, &ss));
+ EXPECT_EQ("unknown type WORSE", ss.str());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 unittest_erasure_code_isa &&
+ * libtool --mode=execute valgrind --tool=memcheck \
+ * ./unittest_erasure_code_isa \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodeJerasure.cc b/src/test/erasure-code/TestErasureCodeJerasure.cc
new file mode 100644
index 000000000..835f3c7b6
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeJerasure.cc
@@ -0,0 +1,370 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "crush/CrushWrapper.h"
+#include "include/stringify.h"
+#include "erasure-code/jerasure/ErasureCodeJerasure.h"
+#include "global/global_context.h"
+#include "common/config.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+template <typename T>
+class ErasureCodeTest : public ::testing::Test {
+ public:
+};
+
+typedef ::testing::Types<
+ ErasureCodeJerasureReedSolomonVandermonde,
+ ErasureCodeJerasureReedSolomonRAID6,
+ ErasureCodeJerasureCauchyOrig,
+ ErasureCodeJerasureCauchyGood,
+ ErasureCodeJerasureLiberation,
+ ErasureCodeJerasureBlaumRoth,
+ ErasureCodeJerasureLiber8tion
+> JerasureTypes;
+TYPED_TEST_SUITE(ErasureCodeTest, JerasureTypes);
+
+TYPED_TEST(ErasureCodeTest, sanity_check_k)
+{
+ TypeParam jerasure;
+ ErasureCodeProfile profile;
+ profile["k"] = "1";
+ profile["m"] = "1";
+ profile["packetsize"] = "8";
+ ostringstream errors;
+ EXPECT_EQ(-EINVAL, jerasure.init(profile, &errors));
+ EXPECT_NE(std::string::npos, errors.str().find("must be >= 2"));
+}
+
+TYPED_TEST(ErasureCodeTest, encode_decode)
+{
+ const char *per_chunk_alignments[] = { "false", "true" };
+ for (int per_chunk_alignment = 0 ;
+ per_chunk_alignment < 2;
+ per_chunk_alignment++) {
+ TypeParam jerasure;
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["packetsize"] = "8";
+ profile["jerasure-per-chunk-alignment"] =
+ per_chunk_alignments[per_chunk_alignment];
+ jerasure.init(profile, &cerr);
+
+#define LARGE_ENOUGH 2048
+ bufferptr in_ptr(buffer::create_page_aligned(LARGE_ENOUGH));
+ in_ptr.zero();
+ in_ptr.set_length(0);
+ const char *payload =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ in_ptr.append(payload, strlen(payload));
+ bufferlist in;
+ in.push_back(in_ptr);
+ int want_to_encode[] = { 0, 1, 2, 3 };
+ map<int, bufferlist> encoded;
+ EXPECT_EQ(0, jerasure.encode(set<int>(want_to_encode, want_to_encode+4),
+ in,
+ &encoded));
+ EXPECT_EQ(4u, encoded.size());
+ unsigned length = encoded[0].length();
+ EXPECT_EQ(0, memcmp(encoded[0].c_str(), in.c_str(), length));
+ EXPECT_EQ(0, memcmp(encoded[1].c_str(), in.c_str() + length,
+ in.length() - length));
+
+
+ // all chunks are available
+ {
+ int want_to_decode[] = { 0, 1 };
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, jerasure._decode(set<int>(want_to_decode, want_to_decode+2),
+ encoded,
+ &decoded));
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(length, decoded[0].length());
+ EXPECT_EQ(0, memcmp(decoded[0].c_str(), in.c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[1].c_str(), in.c_str() + length,
+ in.length() - length));
+ }
+
+ // two chunks are missing
+ {
+ map<int, bufferlist> degraded = encoded;
+ degraded.erase(0);
+ degraded.erase(1);
+ EXPECT_EQ(2u, degraded.size());
+ int want_to_decode[] = { 0, 1 };
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, jerasure._decode(set<int>(want_to_decode, want_to_decode+2),
+ degraded,
+ &decoded));
+ // always decode all, regardless of want_to_decode
+ EXPECT_EQ(4u, decoded.size());
+ EXPECT_EQ(length, decoded[0].length());
+ EXPECT_EQ(0, memcmp(decoded[0].c_str(), in.c_str(), length));
+ EXPECT_EQ(0, memcmp(decoded[1].c_str(), in.c_str() + length,
+ in.length() - length));
+ }
+ }
+}
+
+TYPED_TEST(ErasureCodeTest, minimum_to_decode)
+{
+ TypeParam jerasure;
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "7";
+ profile["packetsize"] = "8";
+ jerasure.init(profile, &cerr);
+
+ //
+ // If trying to read nothing, the minimum is empty.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ EXPECT_EQ(0, jerasure._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_TRUE(minimum.empty());
+ }
+ //
+ // There is no way to read a chunk if none are available.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+
+ EXPECT_EQ(-EIO, jerasure._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ }
+ //
+ // Reading a subset of the available chunks is always possible.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+ available_chunks.insert(0);
+
+ EXPECT_EQ(0, jerasure._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(want_to_read, minimum);
+ }
+ //
+ // There is no way to read a missing chunk if there is less than k
+ // chunks available.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(0);
+ want_to_read.insert(1);
+ available_chunks.insert(0);
+
+ EXPECT_EQ(-EIO, jerasure._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ }
+ //
+ // When chunks are not available, the minimum can be made of any
+ // chunks. For instance, to read 1 and 3 below the minimum could be
+ // 2 and 3 which may seem better because it contains one of the
+ // chunks to be read. But it won't be more efficient than retrieving
+ // 0 and 2 instead because, in both cases, the decode function will
+ // need to run the same recovery operation and use the same amount
+ // of CPU and memory.
+ //
+ {
+ set<int> want_to_read;
+ set<int> available_chunks;
+ set<int> minimum;
+
+ want_to_read.insert(1);
+ want_to_read.insert(3);
+ available_chunks.insert(0);
+ available_chunks.insert(2);
+ available_chunks.insert(3);
+
+ EXPECT_EQ(0, jerasure._minimum_to_decode(want_to_read,
+ available_chunks,
+ &minimum));
+ EXPECT_EQ(2u, minimum.size());
+ EXPECT_EQ(0u, minimum.count(3));
+ }
+}
+
+TEST(ErasureCodeTest, encode)
+{
+ ErasureCodeJerasureReedSolomonVandermonde jerasure;
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "8";
+ jerasure.init(profile, &cerr);
+
+ unsigned aligned_object_size = jerasure.get_alignment() * 2;
+ {
+ //
+ // When the input bufferlist needs to be padded because
+ // it is not properly aligned, it is padded with zeros.
+ //
+ bufferlist in;
+ map<int,bufferlist> encoded;
+ int want_to_encode[] = { 0, 1, 2, 3 };
+ int trail_length = 1;
+ in.append(string(aligned_object_size + trail_length, 'X'));
+ EXPECT_EQ(0, jerasure.encode(set<int>(want_to_encode, want_to_encode+4),
+ in,
+ &encoded));
+ EXPECT_EQ(4u, encoded.size());
+ char *last_chunk = encoded[1].c_str();
+ int length =encoded[1].length();
+ EXPECT_EQ('X', last_chunk[0]);
+ EXPECT_EQ('\0', last_chunk[length - trail_length]);
+ }
+
+ {
+ //
+ // When only the first chunk is required, the encoded map only
+ // contains the first chunk. Although the jerasure encode
+ // internally allocated a buffer because of padding requirements
+ // and also computes the coding chunks, they are released before
+ // the return of the method, as shown when running the tests thru
+ // valgrind (there is no leak).
+ //
+ bufferlist in;
+ map<int,bufferlist> encoded;
+ set<int> want_to_encode;
+ want_to_encode.insert(0);
+ int trail_length = 1;
+ in.append(string(aligned_object_size + trail_length, 'X'));
+ EXPECT_EQ(0, jerasure.encode(want_to_encode, in, &encoded));
+ EXPECT_EQ(1u, encoded.size());
+ }
+}
+
+TEST(ErasureCodeTest, create_rule)
+{
+ std::unique_ptr<CrushWrapper> c = std::make_unique<CrushWrapper>();
+ c->create();
+ int root_type = 2;
+ c->set_type_name(root_type, "root");
+ int host_type = 1;
+ c->set_type_name(host_type, "host");
+ int osd_type = 0;
+ c->set_type_name(osd_type, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ root_type, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int num_host = 4;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h=0; h<num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o=0; o<num_osd; ++o, ++osd) {
+ c->insert_item(g_ceph_context, osd, 1.0, string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ c->finalize();
+
+ {
+ stringstream ss;
+ ErasureCodeJerasureReedSolomonVandermonde jerasure;
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "8";
+ jerasure.init(profile, &cerr);
+ int rule = jerasure.create_rule("myrule", *c, &ss);
+ EXPECT_EQ(0, rule);
+ EXPECT_EQ(-EEXIST, jerasure.create_rule("myrule", *c, &ss));
+ //
+ // the minimum that is expected from the created rule is to
+ // successfully map get_chunk_count() devices from the crushmap,
+ // at least once.
+ //
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+ vector<int> out;
+ int x = 0;
+ c->do_rule(rule, x, out, jerasure.get_chunk_count(), weight, 0);
+ ASSERT_EQ(out.size(), jerasure.get_chunk_count());
+ for (unsigned i=0; i<out.size(); ++i)
+ ASSERT_NE(CRUSH_ITEM_NONE, out[i]);
+ }
+ {
+ stringstream ss;
+ ErasureCodeJerasureReedSolomonVandermonde jerasure;
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "8";
+ profile["crush-root"] = "BAD";
+ jerasure.init(profile, &cerr);
+ EXPECT_EQ(-ENOENT, jerasure.create_rule("otherrule", *c, &ss));
+ EXPECT_EQ("root item BAD does not exist", ss.str());
+ }
+ {
+ stringstream ss;
+ ErasureCodeJerasureReedSolomonVandermonde jerasure;
+ ErasureCodeProfile profile;
+ profile["k"] = "2";
+ profile["m"] = "2";
+ profile["w"] = "8";
+ profile["crush-failure-domain"] = "WORSE";
+ jerasure.init(profile, &cerr);
+ EXPECT_EQ(-EINVAL, jerasure.create_rule("otherrule", *c, &ss));
+ EXPECT_EQ("unknown type WORSE", ss.str());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make -j4 unittest_erasure_code_jerasure &&
+ * valgrind --tool=memcheck \
+ * ./unittest_erasure_code_jerasure \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodeLrc.cc b/src/test/erasure-code/TestErasureCodeLrc.cc
new file mode 100644
index 000000000..aca6ccae9
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeLrc.cc
@@ -0,0 +1,925 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "crush/CrushWrapper.h"
+#include "include/stringify.h"
+#include "erasure-code/lrc/ErasureCodeLrc.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ErasureCodeLrc, parse_rule)
+{
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ EXPECT_EQ("default", lrc.rule_root);
+ EXPECT_EQ("host", lrc.rule_steps.front().type);
+
+ ErasureCodeProfile profile;
+ profile["crush-root"] = "other";
+ EXPECT_EQ(0, lrc.parse_rule(profile, &cerr));
+ EXPECT_EQ("other", lrc.rule_root);
+
+ profile["crush-steps"] = "[]";
+ EXPECT_EQ(0, lrc.parse_rule(profile, &cerr));
+ EXPECT_TRUE(lrc.rule_steps.empty());
+
+ profile["crush-steps"] = "0";
+ EXPECT_EQ(ERROR_LRC_ARRAY, lrc.parse_rule(profile, &cerr));
+
+ profile["crush-steps"] = "{";
+ EXPECT_EQ(ERROR_LRC_PARSE_JSON, lrc.parse_rule(profile, &cerr));
+
+ profile["crush-steps"] = "[0]";
+ EXPECT_EQ(ERROR_LRC_ARRAY, lrc.parse_rule(profile, &cerr));
+
+ profile["crush-steps"] = "[[0]]";
+ EXPECT_EQ(ERROR_LRC_RULE_OP, lrc.parse_rule(profile, &cerr));
+
+ profile["crush-steps"] = "[[\"choose\", 0]]";
+ EXPECT_EQ(ERROR_LRC_RULE_TYPE, lrc.parse_rule(profile, &cerr));
+
+ profile["crush-steps"] = "[[\"choose\", \"host\", []]]";
+ EXPECT_EQ(ERROR_LRC_RULE_N, lrc.parse_rule(profile, &cerr));
+
+ profile["crush-steps"] = "[[\"choose\", \"host\", 2]]";
+ EXPECT_EQ(0, lrc.parse_rule(profile, &cerr));
+
+ const ErasureCodeLrc::Step &step = lrc.rule_steps.front();
+ EXPECT_EQ("choose", step.op);
+ EXPECT_EQ("host", step.type);
+ EXPECT_EQ(2, step.n);
+
+ profile["crush-steps"] =
+ "["
+ " [\"choose\", \"rack\", 2], "
+ " [\"chooseleaf\", \"host\", 5], "
+ "]";
+ EXPECT_EQ(0, lrc.parse_rule(profile, &cerr));
+ EXPECT_EQ(2U, lrc.rule_steps.size());
+ {
+ const ErasureCodeLrc::Step &step = lrc.rule_steps[0];
+ EXPECT_EQ("choose", step.op);
+ EXPECT_EQ("rack", step.type);
+ EXPECT_EQ(2, step.n);
+ }
+ {
+ const ErasureCodeLrc::Step &step = lrc.rule_steps[1];
+ EXPECT_EQ("chooseleaf", step.op);
+ EXPECT_EQ("host", step.type);
+ EXPECT_EQ(5, step.n);
+ }
+}
+
+TEST(ErasureCodeTest, create_rule)
+{
+ CrushWrapper *c = new CrushWrapper;
+ c->create();
+ int root_type = 3;
+ c->set_type_name(root_type, "root");
+ int rack_type = 2;
+ c->set_type_name(rack_type, "rack");
+ int host_type = 1;
+ c->set_type_name(host_type, "host");
+ int osd_type = 0;
+ c->set_type_name(osd_type, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ root_type, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ //
+ // Set all to 10 so that the item number it trivial to decompose
+ // into rack/host/osd.
+ //
+ int num_rack;
+ int num_host;
+ int num_osd;
+ num_rack = num_host = num_osd = 10;
+ int osd = 0;
+ for (int r=0; r<num_rack; ++r) {
+ loc["rack"] = string("rack-") + stringify(r);
+ for (int h=0; h<num_host; ++h) {
+ loc["host"] = string("host-") + stringify(r) + string("-") + stringify(h);
+ for (int o=0; o<num_osd; ++o, ++osd) {
+ c->insert_item(g_ceph_context, osd, 1.0, string("osd.") + stringify(osd), loc);
+ }
+ }
+ }
+
+ c->finalize();
+
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ EXPECT_EQ(0, lrc.create_rule("rule1", *c, &cerr));
+
+ ErasureCodeProfile profile;
+ unsigned int racks = 2;
+ unsigned int hosts = 5;
+ profile["crush-steps"] =
+ "["
+ " [\"choose\", \"rack\", " + stringify(racks) + "], "
+ " [\"chooseleaf\", \"host\", " + stringify(hosts) + "], "
+ "]";
+ const char *rule_name = "rule2";
+ EXPECT_EQ(0, lrc.parse_rule(profile, &cerr));
+ EXPECT_EQ(1, lrc.create_rule(rule_name, *c, &cerr));
+
+ vector<__u32> weight;
+ for (int o = 0; o < c->get_max_devices(); o++)
+ weight.push_back(0x10000);
+ int rule = c->get_rule_id(rule_name);
+ vector<int> out;
+ unsigned int n = racks * hosts;
+ c->do_rule(rule, 1, out, n, weight, 0);
+ EXPECT_EQ(n, out.size());
+ //
+ // check that the first five are in the same rack and the next five
+ // in the same rack
+ //
+ int first_rack = out[0] / num_host / num_osd;
+ EXPECT_EQ(first_rack, out[1] / num_host / num_osd);
+ EXPECT_EQ(first_rack, out[2] / num_host / num_osd);
+ EXPECT_EQ(first_rack, out[3] / num_host / num_osd);
+ EXPECT_EQ(first_rack, out[4] / num_host / num_osd);
+ int second_rack = out[5] / num_host / num_osd;
+ EXPECT_EQ(second_rack, out[6] / num_host / num_osd);
+ EXPECT_EQ(second_rack, out[7] / num_host / num_osd);
+ EXPECT_EQ(second_rack, out[8] / num_host / num_osd);
+ EXPECT_EQ(second_rack, out[9] / num_host / num_osd);
+}
+
+TEST(ErasureCodeLrc, parse_kml)
+{
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ EXPECT_EQ(0, lrc.parse_kml(profile, &cerr));
+ profile["k"] = "4";
+ EXPECT_EQ(ERROR_LRC_ALL_OR_NOTHING, lrc.parse_kml(profile, &cerr));
+ const char *generated[] = { "mapping",
+ "layers",
+ "crush-steps" };
+ profile["m"] = "2";
+ profile["l"] = "3";
+
+ for (int i = 0; i < 3; i++) {
+ profile[generated[i]] = "SET";
+ EXPECT_EQ(ERROR_LRC_GENERATED, lrc.parse_kml(profile, &cerr));
+ profile.erase(profile.find(generated[i]));
+ }
+
+ profile["k"] = "4";
+ profile["m"] = "2";
+ profile["l"] = "7";
+ EXPECT_EQ(ERROR_LRC_K_M_MODULO, lrc.parse_kml(profile, &cerr));
+
+ profile["k"] = "3";
+ profile["m"] = "3";
+ profile["l"] = "3";
+ EXPECT_EQ(ERROR_LRC_K_MODULO, lrc.parse_kml(profile, &cerr));
+
+ profile["k"] = "4";
+ profile["m"] = "2";
+ profile["l"] = "3";
+ EXPECT_EQ(0, lrc.parse_kml(profile, &cerr));
+ EXPECT_EQ("[ "
+ " [ \"DDc_DDc_\", \"\" ],"
+ " [ \"DDDc____\", \"\" ],"
+ " [ \"____DDDc\", \"\" ],"
+ "]", profile["layers"]);
+ EXPECT_EQ("DD__DD__", profile["mapping"]);
+ EXPECT_EQ("chooseleaf", lrc.rule_steps[0].op);
+ EXPECT_EQ("host", lrc.rule_steps[0].type);
+ EXPECT_EQ(0, lrc.rule_steps[0].n);
+ EXPECT_EQ(1U, lrc.rule_steps.size());
+ profile.erase(profile.find("mapping"));
+ profile.erase(profile.find("layers"));
+
+ profile["k"] = "4";
+ profile["m"] = "2";
+ profile["l"] = "3";
+ profile["crush-failure-domain"] = "osd";
+ EXPECT_EQ(0, lrc.parse_kml(profile, &cerr));
+ EXPECT_EQ("chooseleaf", lrc.rule_steps[0].op);
+ EXPECT_EQ("osd", lrc.rule_steps[0].type);
+ EXPECT_EQ(0, lrc.rule_steps[0].n);
+ EXPECT_EQ(1U, lrc.rule_steps.size());
+ profile.erase(profile.find("mapping"));
+ profile.erase(profile.find("layers"));
+
+ profile["k"] = "4";
+ profile["m"] = "2";
+ profile["l"] = "3";
+ profile["crush-failure-domain"] = "osd";
+ profile["crush-locality"] = "rack";
+ EXPECT_EQ(0, lrc.parse_kml(profile, &cerr));
+ EXPECT_EQ("choose", lrc.rule_steps[0].op);
+ EXPECT_EQ("rack", lrc.rule_steps[0].type);
+ EXPECT_EQ(2, lrc.rule_steps[0].n);
+ EXPECT_EQ("chooseleaf", lrc.rule_steps[1].op);
+ EXPECT_EQ("osd", lrc.rule_steps[1].type);
+ EXPECT_EQ(4, lrc.rule_steps[1].n);
+ EXPECT_EQ(2U, lrc.rule_steps.size());
+ profile.erase(profile.find("mapping"));
+ profile.erase(profile.find("layers"));
+}
+
+TEST(ErasureCodeLrc, layers_description)
+{
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+
+ json_spirit::mArray description;
+ EXPECT_EQ(ERROR_LRC_DESCRIPTION,
+ lrc.layers_description(profile, &description, &cerr));
+
+ {
+ const char *description_string = "\"not an array\"";
+ profile["layers"] = description_string;
+ EXPECT_EQ(ERROR_LRC_ARRAY,
+ lrc.layers_description(profile, &description, &cerr));
+ }
+ {
+ const char *description_string = "invalid json";
+ profile["layers"] = description_string;
+ EXPECT_EQ(ERROR_LRC_PARSE_JSON,
+ lrc.layers_description(profile, &description, &cerr));
+ }
+ {
+ const char *description_string = "[]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.layers_description(profile, &description, &cerr));
+ }
+}
+
+TEST(ErasureCodeLrc, layers_parse)
+{
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+
+ const char *description_string ="[ 0 ]";
+ profile["layers"] = description_string;
+ json_spirit::mArray description;
+ EXPECT_EQ(0, lrc.layers_description(profile, &description, &cerr));
+ EXPECT_EQ(ERROR_LRC_ARRAY,
+ lrc.layers_parse(description_string, description, &cerr));
+ }
+
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+
+ const char *description_string ="[ [ 0 ] ]";
+ profile["layers"] = description_string;
+ json_spirit::mArray description;
+ EXPECT_EQ(0, lrc.layers_description(profile, &description, &cerr));
+ EXPECT_EQ(ERROR_LRC_STR,
+ lrc.layers_parse(description_string, description, &cerr));
+ }
+
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+
+ const char *description_string ="[ [ \"\", 0 ] ]";
+ profile["layers"] = description_string;
+ json_spirit::mArray description;
+ EXPECT_EQ(0, lrc.layers_description(profile, &description, &cerr));
+ EXPECT_EQ(ERROR_LRC_CONFIG_OPTIONS,
+ lrc.layers_parse(description_string, description, &cerr));
+ }
+
+ //
+ // The second element can be an object describing the plugin
+ // profile.
+ //
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+
+ const char *description_string ="[ [ \"\", { \"a\": \"b\" }, \"ignored\" ] ]";
+ profile["layers"] = description_string;
+ json_spirit::mArray description;
+ EXPECT_EQ(0, lrc.layers_description(profile, &description, &cerr));
+ EXPECT_EQ(0, lrc.layers_parse(description_string, description, &cerr));
+ EXPECT_EQ("b", lrc.layers.front().profile["a"]);
+ }
+
+ //
+ // The second element can be a str_map parseable string describing the plugin
+ // profile.
+ //
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+
+ const char *description_string ="[ [ \"\", \"a=b c=d\" ] ]";
+ profile["layers"] = description_string;
+ json_spirit::mArray description;
+ EXPECT_EQ(0, lrc.layers_description(profile, &description, &cerr));
+ EXPECT_EQ(0, lrc.layers_parse(description_string, description, &cerr));
+ EXPECT_EQ("b", lrc.layers.front().profile["a"]);
+ EXPECT_EQ("d", lrc.layers.front().profile["c"]);
+ }
+
+}
+
+TEST(ErasureCodeLrc, layers_sanity_checks)
+{
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "__DDD__DD";
+ const char *description_string =
+ "[ "
+ " [ \"_cDDD_cDD\", \"\" ],"
+ " [ \"c_DDD____\", \"\" ],"
+ " [ \"_____cDDD\", \"\" ],"
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+ }
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ const char *description_string =
+ "[ "
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(ERROR_LRC_MAPPING, lrc.init(profile, &cerr));
+ }
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] = "";
+ const char *description_string =
+ "[ "
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(ERROR_LRC_LAYERS_COUNT, lrc.init(profile, &cerr));
+ }
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "DD";
+ const char *description_string =
+ "[ "
+ " [ \"DD??\", \"\" ], "
+ " [ \"DD\", \"\" ], "
+ " [ \"DD\", \"\" ], "
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(-EINVAL, lrc.init(profile, &cerr));
+ }
+}
+
+TEST(ErasureCodeLrc, layers_init)
+{
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+
+ const char* env = getenv("CEPH_LIB");
+ string directory(env ? env : "lib");
+ string description_string =
+ "[ "
+ " [ \"_cDDD_cDD_\", \"directory=" + directory + "\" ],"
+ "]";
+ profile["layers"] = description_string;
+ json_spirit::mArray description;
+ EXPECT_EQ(0, lrc.layers_description(profile, &description, &cerr));
+ EXPECT_EQ(0, lrc.layers_parse(description_string, description, &cerr));
+ EXPECT_EQ(0, lrc.layers_init(&cerr));
+ EXPECT_EQ("5", lrc.layers.front().profile["k"]);
+ EXPECT_EQ("2", lrc.layers.front().profile["m"]);
+ EXPECT_EQ("jerasure", lrc.layers.front().profile["plugin"]);
+ EXPECT_EQ("reed_sol_van", lrc.layers.front().profile["technique"]);
+ }
+}
+
+TEST(ErasureCodeLrc, init)
+{
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "__DDD__DD";
+ const char *description_string =
+ "[ "
+ " [ \"_cDDD_cDD\", \"\" ],"
+ " [ \"c_DDD____\", \"\" ],"
+ " [ \"_____cDDD\", \"\" ],"
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+}
+
+TEST(ErasureCodeLrc, init_kml)
+{
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["k"] = "4";
+ profile["m"] = "2";
+ profile["l"] = "3";
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+ EXPECT_EQ((unsigned int)(4 + 2 + (4 + 2) / 3), lrc.get_chunk_count());
+}
+
+TEST(ErasureCodeLrc, minimum_to_decode)
+{
+ // trivial : no erasures, the minimum is want_to_read
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "__DDD__DD";
+ const char *description_string =
+ "[ "
+ " [ \"_cDDD_cDD\", \"\" ],"
+ " [ \"c_DDD____\", \"\" ],"
+ " [ \"_____cDDD\", \"\" ],"
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+ set<int> want_to_read;
+ want_to_read.insert(1);
+ set<int> available_chunks;
+ available_chunks.insert(1);
+ available_chunks.insert(2);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(want_to_read, minimum);
+ }
+ // locally repairable erasure
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "__DDD__DD_";
+ const char *description_string =
+ "[ "
+ " [ \"_cDDD_cDD_\", \"\" ],"
+ " [ \"c_DDD_____\", \"\" ],"
+ " [ \"_____cDDD_\", \"\" ],"
+ " [ \"_____DDDDc\", \"\" ],"
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+ EXPECT_EQ(profile["mapping"].length(),
+ lrc.get_chunk_count());
+ {
+ // want to read the last chunk
+ set<int> want_to_read;
+ want_to_read.insert(lrc.get_chunk_count() - 1);
+ // all chunks are available except the last chunk
+ set<int> available_chunks;
+ for (int i = 0; i < (int)lrc.get_chunk_count() - 1; i++)
+ available_chunks.insert(i);
+ // _____DDDDc can recover c
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ set<int> expected_minimum;
+ expected_minimum.insert(5);
+ expected_minimum.insert(6);
+ expected_minimum.insert(7);
+ expected_minimum.insert(8);
+ EXPECT_EQ(expected_minimum, minimum);
+ }
+ {
+ set<int> want_to_read;
+ want_to_read.insert(0);
+ set<int> available_chunks;
+ for (int i = 1; i < (int)lrc.get_chunk_count(); i++)
+ available_chunks.insert(i);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ set<int> expected_minimum;
+ expected_minimum.insert(2);
+ expected_minimum.insert(3);
+ expected_minimum.insert(4);
+ EXPECT_EQ(expected_minimum, minimum);
+ }
+ }
+ // implicit parity required
+ {
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "__DDD__DD";
+ const char *description_string =
+ "[ "
+ " [ \"_cDDD_cDD\", \"\" ],"
+ " [ \"c_DDD____\", \"\" ],"
+ " [ \"_____cDDD\", \"\" ],"
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+ EXPECT_EQ(profile["mapping"].length(),
+ lrc.get_chunk_count());
+ set<int> want_to_read;
+ want_to_read.insert(8);
+ //
+ // unable to recover, too many chunks missing
+ //
+ {
+ set<int> available_chunks;
+ available_chunks.insert(0);
+ available_chunks.insert(1);
+ // missing (2)
+ // missing (3)
+ available_chunks.insert(4);
+ available_chunks.insert(5);
+ available_chunks.insert(6);
+ // missing (7)
+ // missing (8)
+ set<int> minimum;
+ EXPECT_EQ(-EIO, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ }
+ //
+ // We want to read chunk 8 and encoding was done with
+ //
+ // _cDDD_cDD
+ // c_DDD____
+ // _____cDDD
+ //
+ // First strategy fails:
+ //
+ // 012345678
+ // xxXXXxxXX initial chunks
+ // xx.XXxx.. missing (2, 7, 8)
+ // _____cDDD fail : can recover 1 but 2 are missing
+ // c_DDD____ ignored because 8 is not used (i.e. _)
+ // _cDDD_cDD fail : can recover 2 but 3 are missing
+ //
+ // Second strategy succeeds:
+ //
+ // 012345678
+ // xxXXXxxXX initial chunks
+ // xx.XXxx.. missing (2, 7, 8)
+ // _____cDDD fail : can recover 1 but 2 are missing
+ // c_DDD____ success: recovers chunk 2
+ // _cDDD_cDD success: recovers chunk 7, 8
+ //
+ {
+ set<int> available_chunks;
+ available_chunks.insert(0);
+ available_chunks.insert(1);
+ // missing (2)
+ available_chunks.insert(3);
+ available_chunks.insert(4);
+ available_chunks.insert(5);
+ available_chunks.insert(6);
+ // missing (7)
+ // missing (8)
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(available_chunks, minimum);
+ }
+ }
+}
+
+TEST(ErasureCodeLrc, encode_decode)
+{
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "__DD__DD";
+ const char *description_string =
+ "[ "
+ " [ \"_cDD_cDD\", \"\" ]," // global layer
+ " [ \"c_DD____\", \"\" ]," // first local layer
+ " [ \"____cDDD\", \"\" ]," // second local layer
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+ EXPECT_EQ(4U, lrc.get_data_chunk_count());
+ unsigned int chunk_size = g_conf().get_val<Option::size_t>("osd_pool_erasure_code_stripe_unit");
+ unsigned int stripe_width = lrc.get_data_chunk_count() * chunk_size;
+ EXPECT_EQ(chunk_size, lrc.get_chunk_size(stripe_width));
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+ for (unsigned int i = 0; i < lrc.get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ bufferptr ptr(buffer::create_page_aligned(chunk_size));
+ bufferlist tmp;
+ tmp.push_back(ptr);
+ tmp.claim_append(encoded[i]);
+ encoded[i].swap(tmp);
+ }
+ const vector<int> &mapping = lrc.get_chunk_mapping();
+ char c = 'A';
+ for (unsigned int i = 0; i < lrc.get_data_chunk_count(); i++) {
+ int j = mapping[i];
+ string s(chunk_size, c);
+ encoded[j].clear();
+ encoded[j].append(s);
+ c++;
+ }
+ EXPECT_EQ(0, lrc.encode_chunks(want_to_encode, &encoded));
+
+ {
+ map<int, bufferlist> chunks;
+ chunks[4] = encoded[4];
+ chunks[5] = encoded[5];
+ chunks[6] = encoded[6];
+ set<int> want_to_read;
+ want_to_read.insert(7);
+ set<int> available_chunks;
+ available_chunks.insert(4);
+ available_chunks.insert(5);
+ available_chunks.insert(6);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ // only need three chunks from the second local layer
+ EXPECT_EQ(3U, minimum.size());
+ EXPECT_EQ(1U, minimum.count(4));
+ EXPECT_EQ(1U, minimum.count(5));
+ EXPECT_EQ(1U, minimum.count(6));
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, lrc._decode(want_to_read, chunks, &decoded));
+ string s(chunk_size, 'D');
+ EXPECT_EQ(s, string(decoded[7].c_str(), chunk_size));
+ }
+ {
+ set<int> want_to_read;
+ want_to_read.insert(2);
+ map<int, bufferlist> chunks;
+ chunks[1] = encoded[1];
+ chunks[3] = encoded[3];
+ chunks[5] = encoded[5];
+ chunks[6] = encoded[6];
+ chunks[7] = encoded[7];
+ set<int> available_chunks;
+ available_chunks.insert(1);
+ available_chunks.insert(3);
+ available_chunks.insert(5);
+ available_chunks.insert(6);
+ available_chunks.insert(7);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(5U, minimum.size());
+ EXPECT_EQ(available_chunks, minimum);
+
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, lrc._decode(want_to_read, encoded, &decoded));
+ string s(chunk_size, 'A');
+ EXPECT_EQ(s, string(decoded[2].c_str(), chunk_size));
+ }
+ {
+ set<int> want_to_read;
+ want_to_read.insert(3);
+ want_to_read.insert(6);
+ want_to_read.insert(7);
+ set<int> available_chunks;
+ available_chunks.insert(0);
+ available_chunks.insert(1);
+ available_chunks.insert(2);
+ // available_chunks.insert(3);
+ available_chunks.insert(4);
+ available_chunks.insert(5);
+ // available_chunks.insert(6);
+ // available_chunks.insert(7);
+ encoded.erase(3);
+ encoded.erase(6);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(4U, minimum.size());
+ // only need two chunks from the first local layer
+ EXPECT_EQ(1U, minimum.count(0));
+ EXPECT_EQ(1U, minimum.count(2));
+ // the above chunks will rebuild chunk 3 and the global layer only needs
+ // three more chunks to reach the required amount of chunks (4) to recover
+ // the last two
+ EXPECT_EQ(1U, minimum.count(1));
+ EXPECT_EQ(1U, minimum.count(2));
+ EXPECT_EQ(1U, minimum.count(5));
+
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, lrc._decode(want_to_read, encoded, &decoded));
+ {
+ string s(chunk_size, 'B');
+ EXPECT_EQ(s, string(decoded[3].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'C');
+ EXPECT_EQ(s, string(decoded[6].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'D');
+ EXPECT_EQ(s, string(decoded[7].c_str(), chunk_size));
+ }
+ }
+}
+
+TEST(ErasureCodeLrc, encode_decode_2)
+{
+ ErasureCodeLrc lrc(g_conf().get_val<std::string>("erasure_code_dir"));
+ ErasureCodeProfile profile;
+ profile["mapping"] =
+ "DD__DD__";
+ const char *description_string =
+ "[ "
+ " [ \"DDc_DDc_\", \"\" ],"
+ " [ \"DDDc____\", \"\" ],"
+ " [ \"____DDDc\", \"\" ],"
+ "]";
+ profile["layers"] = description_string;
+ EXPECT_EQ(0, lrc.init(profile, &cerr));
+ EXPECT_EQ(4U, lrc.get_data_chunk_count());
+ unsigned int chunk_size = g_conf().get_val<Option::size_t>("osd_pool_erasure_code_stripe_unit");
+ unsigned int stripe_width = lrc.get_data_chunk_count() * chunk_size;
+ EXPECT_EQ(chunk_size, lrc.get_chunk_size(stripe_width));
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+ for (unsigned int i = 0; i < lrc.get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ bufferptr ptr(buffer::create_page_aligned(chunk_size));
+ bufferlist tmp;
+ tmp.push_back(ptr);
+ tmp.claim_append(encoded[i]);
+ encoded[i].swap(tmp);
+ }
+ const vector<int> &mapping = lrc.get_chunk_mapping();
+ char c = 'A';
+ for (unsigned int i = 0; i < lrc.get_data_chunk_count(); i++) {
+ int j = mapping[i];
+ string s(chunk_size, c);
+ encoded[j].clear();
+ encoded[j].append(s);
+ c++;
+ }
+ EXPECT_EQ(0, lrc.encode_chunks(want_to_encode, &encoded));
+
+ {
+ set<int> want_to_read;
+ want_to_read.insert(0);
+ map<int, bufferlist> chunks;
+ chunks[1] = encoded[1];
+ chunks[3] = encoded[3];
+ chunks[4] = encoded[4];
+ chunks[5] = encoded[5];
+ chunks[6] = encoded[6];
+ chunks[7] = encoded[7];
+ set<int> available_chunks;
+ available_chunks.insert(1);
+ available_chunks.insert(3);
+ available_chunks.insert(4);
+ available_chunks.insert(5);
+ available_chunks.insert(6);
+ available_chunks.insert(7);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(4U, minimum.size());
+ EXPECT_EQ(1U, minimum.count(1));
+ EXPECT_EQ(1U, minimum.count(4));
+ EXPECT_EQ(1U, minimum.count(5));
+ EXPECT_EQ(1U, minimum.count(6));
+
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, lrc._decode(want_to_read, chunks, &decoded));
+ string s(chunk_size, 'A');
+ EXPECT_EQ(s, string(decoded[0].c_str(), chunk_size));
+ }
+ {
+ set<int> want_to_read;
+ for (unsigned int i = 0; i < lrc.get_chunk_count(); i++)
+ want_to_read.insert(i);
+ map<int, bufferlist> chunks;
+ chunks[1] = encoded[1];
+ chunks[3] = encoded[3];
+ chunks[5] = encoded[5];
+ chunks[6] = encoded[6];
+ chunks[7] = encoded[7];
+ set<int> available_chunks;
+ available_chunks.insert(1);
+ available_chunks.insert(3);
+ available_chunks.insert(5);
+ available_chunks.insert(6);
+ available_chunks.insert(7);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(5U, minimum.size());
+ EXPECT_EQ(1U, minimum.count(1));
+ EXPECT_EQ(1U, minimum.count(3));
+ EXPECT_EQ(1U, minimum.count(5));
+ EXPECT_EQ(1U, minimum.count(6));
+ EXPECT_EQ(1U, minimum.count(7));
+
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, lrc._decode(want_to_read, chunks, &decoded));
+ {
+ string s(chunk_size, 'A');
+ EXPECT_EQ(s, string(decoded[0].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'B');
+ EXPECT_EQ(s, string(decoded[1].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'C');
+ EXPECT_EQ(s, string(decoded[4].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'D');
+ EXPECT_EQ(s, string(decoded[5].c_str(), chunk_size));
+ }
+ }
+ {
+ set<int> want_to_read;
+ for (unsigned int i = 0; i < lrc.get_chunk_count(); i++)
+ want_to_read.insert(i);
+ map<int, bufferlist> chunks;
+ chunks[1] = encoded[1];
+ chunks[3] = encoded[3];
+ chunks[5] = encoded[5];
+ chunks[6] = encoded[6];
+ chunks[7] = encoded[7];
+ set<int> available_chunks;
+ available_chunks.insert(1);
+ available_chunks.insert(3);
+ available_chunks.insert(5);
+ available_chunks.insert(6);
+ available_chunks.insert(7);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(5U, minimum.size());
+ EXPECT_EQ(1U, minimum.count(1));
+ EXPECT_EQ(1U, minimum.count(3));
+ EXPECT_EQ(1U, minimum.count(5));
+ EXPECT_EQ(1U, minimum.count(6));
+ EXPECT_EQ(1U, minimum.count(7));
+
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, lrc._decode(want_to_read, chunks, &decoded));
+ {
+ string s(chunk_size, 'A');
+ EXPECT_EQ(s, string(decoded[0].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'B');
+ EXPECT_EQ(s, string(decoded[1].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'C');
+ EXPECT_EQ(s, string(decoded[4].c_str(), chunk_size));
+ }
+ {
+ string s(chunk_size, 'D');
+ EXPECT_EQ(s, string(decoded[5].c_str(), chunk_size));
+ }
+ }
+ {
+ set<int> want_to_read;
+ want_to_read.insert(6);
+ map<int, bufferlist> chunks;
+ chunks[0] = encoded[0];
+ chunks[1] = encoded[1];
+ chunks[3] = encoded[3];
+ chunks[5] = encoded[5];
+ chunks[7] = encoded[7];
+ set<int> available_chunks;
+ available_chunks.insert(0);
+ available_chunks.insert(1);
+ available_chunks.insert(3);
+ available_chunks.insert(5);
+ available_chunks.insert(7);
+ set<int> minimum;
+ EXPECT_EQ(0, lrc._minimum_to_decode(want_to_read, available_chunks, &minimum));
+ EXPECT_EQ(available_chunks, minimum);
+
+ map<int, bufferlist> decoded;
+ EXPECT_EQ(0, lrc._decode(want_to_read, chunks, &decoded));
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make -j4 unittest_erasure_code_lrc && valgrind --tool=memcheck \
+ * ./unittest_erasure_code_lrc \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodePlugin.cc b/src/test/erasure-code/TestErasureCodePlugin.cc
new file mode 100644
index 000000000..e396d9467
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodePlugin.cc
@@ -0,0 +1,131 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include "common/Thread.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+class ErasureCodePluginRegistryTest : public ::testing::Test {};
+
+TEST_F(ErasureCodePluginRegistryTest, factory_mutex) {
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+
+ {
+ unique_lock l{instance.lock, std::try_to_lock};
+ EXPECT_TRUE(l.owns_lock());
+ }
+ //
+ // Test that the loading of a plugin is protected by a mutex.
+
+ std::thread sleep_for_10_secs([] {
+ ErasureCodeProfile profile;
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeInterfaceRef erasure_code;
+ instance.factory("hangs",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &cerr);
+ });
+ auto wait_until = [&instance](bool loading, unsigned max_secs) {
+ auto delay = 0ms;
+ const auto DELAY_MAX = std::chrono::seconds(max_secs);
+ for (; delay < DELAY_MAX; delay = (delay + 1ms) * 2) {
+ cout << "Trying (1) with delay " << delay << "us\n";
+ if (delay.count() > 0) {
+ std::this_thread::sleep_for(delay);
+ }
+ if (instance.loading == loading) {
+ return true;
+ }
+ }
+ return false;
+ };
+ // should be loading in 5 seconds
+ ASSERT_TRUE(wait_until(true, 5));
+ {
+ unique_lock l{instance.lock, std::try_to_lock};
+ EXPECT_TRUE(!l.owns_lock());
+ }
+ // should finish loading in 15 seconds
+ ASSERT_TRUE(wait_until(false, 15));
+ {
+ unique_lock l{instance.lock, std::try_to_lock};
+ EXPECT_TRUE(l.owns_lock());
+ }
+ sleep_for_10_secs.join();
+}
+
+TEST_F(ErasureCodePluginRegistryTest, all)
+{
+ ErasureCodeProfile profile;
+ string directory = g_conf().get_val<std::string>("erasure_code_dir");
+ ErasureCodeInterfaceRef erasure_code;
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(-EIO, instance.factory("invalid",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &cerr));
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(-EXDEV, instance.factory("missing_version",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(-ENOENT, instance.factory("missing_entry_point",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(-ESRCH, instance.factory("fail_to_initialize",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(-EBADF, instance.factory("fail_to_register",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("example",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ ErasureCodePlugin *plugin = 0;
+ {
+ std::lock_guard l{instance.lock};
+ EXPECT_EQ(-EEXIST, instance.load("example", directory, &plugin, &cerr));
+ EXPECT_EQ(-ENOENT, instance.remove("does not exist"));
+ EXPECT_EQ(0, instance.remove("example"));
+ EXPECT_EQ(0, instance.load("example", directory, &plugin, &cerr));
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../../../build ; make -j4 &&
+ * make unittest_erasure_code_plugin &&
+ * valgrind --tool=memcheck \
+ * ./bin/unittest_erasure_code_plugin \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodePluginClay.cc b/src/test/erasure-code/TestErasureCodePluginClay.cc
new file mode 100644
index 000000000..cbf6566dd
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodePluginClay.cc
@@ -0,0 +1,114 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2018 Indian Institute of Science <office.ece@iisc.ac.in>
+ *
+ * Author: Myna Vajha <mynaramana@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include "erasure-code/ErasureCodePlugin.h"
+#include "log/Log.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ErasureCodePlugin, factory)
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeProfile profile;
+ {
+ ErasureCodeInterfaceRef erasure_code;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("clay",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code);
+ }
+ //check clay plugin with scalar_mds=jerasure
+ {
+ const char *techniques[] = {
+ "reed_sol_van",
+ "reed_sol_r6_op",
+ "cauchy_orig",
+ "cauchy_good",
+ "liber8tion",
+ 0
+ };
+ for(const char **technique = techniques; *technique; technique++) {
+ ErasureCodeInterfaceRef erasure_code;
+ ErasureCodeProfile profile;
+ profile["scalar_mds"] = "jerasure";
+ profile["technique"] = *technique;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("clay",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ }
+ }
+#ifdef WITH_EC_ISA_PLUGIN
+ //check clay plugin with scalar_mds=isa
+ {
+ const char *techniques[] = {
+ "reed_sol_van",
+ "cauchy",
+ 0
+ };
+ for(const char **technique = techniques; *technique; technique++) {
+ ErasureCodeInterfaceRef erasure_code;
+ ErasureCodeProfile profile;
+ profile["scalar_mds"] = "isa";
+ profile["technique"] = *technique;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("clay",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ }
+ }
+#endif
+ //check clay plugin with scalar_mds=shec
+ {
+ const char *techniques[] = {
+ "single",
+ "multiple",
+ 0
+ };
+ for(const char **technique = techniques; *technique; technique++) {
+ ErasureCodeInterfaceRef erasure_code;
+ ErasureCodeProfile profile;
+ profile["scalar_mds"] = "shec";
+ profile["technique"] = *technique;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("clay",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ }
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_erasure_code_plugin_clay &&
+ * valgrind --tool=memcheck ./unittest_erasure_code_plugin_clay \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodePluginIsa.cc b/src/test/erasure-code/TestErasureCodePluginIsa.cc
new file mode 100644
index 000000000..86bac636d
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodePluginIsa.cc
@@ -0,0 +1,62 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 CERN (Switzerland)
+ *
+ * Author: Andreas-Joachim Peters <Andreas.Joachim.Peters@cern.ch>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include "arch/probe.h"
+#include "arch/intel.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ErasureCodePlugin, factory)
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeProfile profile;
+ {
+ ErasureCodeInterfaceRef erasure_code;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(-EIO, instance.factory("no-isa",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_FALSE(erasure_code);
+ }
+ const char *techniques[] = {
+ "reed_sol_van",
+ 0
+ };
+ for(const char **technique = techniques; *technique; technique++) {
+ ErasureCodeInterfaceRef erasure_code;
+ profile["technique"] = *technique;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("isa",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_erasure_code_plugin_isa &&
+ * valgrind --tool=memcheck ./unittest_erasure_code_plugin_isa \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodePluginJerasure.cc b/src/test/erasure-code/TestErasureCodePluginJerasure.cc
new file mode 100644
index 000000000..ffe4183bf
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodePluginJerasure.cc
@@ -0,0 +1,71 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include "erasure-code/ErasureCodePlugin.h"
+#include "log/Log.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ErasureCodePlugin, factory)
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeProfile profile;
+ {
+ ErasureCodeInterfaceRef erasure_code;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(-ENOENT, instance.factory("jerasure",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_FALSE(erasure_code);
+ }
+ const char *techniques[] = {
+ "reed_sol_van",
+ "reed_sol_r6_op",
+ "cauchy_orig",
+ "cauchy_good",
+ "liberation",
+ "blaum_roth",
+ "liber8tion",
+ 0
+ };
+ for(const char **technique = techniques; *technique; technique++) {
+ ErasureCodeInterfaceRef erasure_code;
+ ErasureCodeProfile profile;
+ profile["technique"] = *technique;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("jerasure",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_erasure_code_plugin_jerasure &&
+ * valgrind --tool=memcheck ./unittest_erasure_code_plugin_jerasure \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodePluginLrc.cc b/src/test/erasure-code/TestErasureCodePluginLrc.cc
new file mode 100644
index 000000000..9be2c336d
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodePluginLrc.cc
@@ -0,0 +1,50 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include "arch/probe.h"
+#include "arch/intel.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ErasureCodePlugin, factory)
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeProfile profile;
+ profile["mapping"] = "DD_";
+ profile["layers"] = "[ [ \"DDc\", \"\" ] ]";
+ ErasureCodeInterfaceRef erasure_code;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("lrc",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_erasure_code_plugin_lrc &&
+ * valgrind --tool=memcheck ./unittest_erasure_code_plugin_lrc \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodePluginShec.cc b/src/test/erasure-code/TestErasureCodePluginShec.cc
new file mode 100644
index 000000000..fae1b559e
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodePluginShec.cc
@@ -0,0 +1,65 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2015 FUJITSU LIMITED
+ *
+ * Author: Shotaro Kawaguchi <kawaguchi.s@jp.fujitsu.com>
+ * Author: Takanori Nakao <nakao.takanori@jp.fujitsu.com>
+ * Author: Takeshi Miyamae <miyamae.takeshi@jp.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+#include "common/config_proxy.h"
+
+using namespace std;
+
+TEST(ErasureCodePlugin, factory)
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ map<std::string,std::string> profile;
+ {
+ ErasureCodeInterfaceRef erasure_code;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("shec",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ }
+ const char *techniques[] = {
+ "single",
+ "multiple",
+ 0
+ };
+ for(const char **technique = techniques; *technique; technique++) {
+ ErasureCodeInterfaceRef erasure_code;
+ profile["technique"] = *technique;
+ EXPECT_FALSE(erasure_code);
+ EXPECT_EQ(0, instance.factory("shec",
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile,
+ &erasure_code, &cerr));
+ EXPECT_TRUE(erasure_code.get());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make unittest_erasure_code_plugin_shec &&
+ * valgrind --tool=memcheck ./unittest_erasure_code_plugin_shec \
+ * --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
diff --git a/src/test/erasure-code/TestErasureCodeShec.cc b/src/test/erasure-code/TestErasureCodeShec.cc
new file mode 100644
index 000000000..6b901dc6f
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeShec.cc
@@ -0,0 +1,2823 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014,2015 FUJITSU LIMITED
+ *
+ * Author: Shotaro Kawaguchi <kawaguchi.s@jp.fujitsu.com>
+ * Author: Takanori Nakao <nakao.takanori@jp.fujitsu.com>
+ * Author: Takeshi Miyamae <miyamae.takeshi@jp.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+//SUMMARY: TestErasureCodeShec
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "crush/CrushWrapper.h"
+#include "osd/osd_types.h"
+#include "include/stringify.h"
+#include "erasure-code/shec/ErasureCodeShec.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+void* thread1(void* pParam);
+void* thread2(void* pParam);
+void* thread3(void* pParam);
+void* thread4(void* pParam);
+void* thread5(void* pParam);
+
+static int g_flag = 0;
+
+TEST(ErasureCodeShec, init_1)
+{
+ //all parameters are normal values
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ //check profile
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_2)
+{
+ //all parameters are normal values
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-root"] = "test";
+ (*profile)["crush-failure-domain"] = "host";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "8";
+
+ int r = shec->init(*profile, &cerr);
+
+ //check profile
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("test", shec->rule_root.c_str());
+ EXPECT_STREQ("host", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_3)
+{
+ //all parameters are normal values
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "16";
+
+ int r = shec->init(*profile, &cerr);
+
+ //check profile
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(16, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_4)
+{
+ //all parameters are normal values
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "32";
+
+ int r = shec->init(*profile, &cerr);
+
+ //check profile
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(32, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_5)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ //plugin is not specified
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_6)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "jerasure"; //unexpected value
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_7)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "abc"; //unexpected value
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_8)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_9)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-root"] = "abc"; //unexpected value
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_10)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "abc"; //unexpected value
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_11)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "abc"; //unexpected value
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_12)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "-1"; //unexpected value
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_13)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "abc";
+ (*profile)["k"] = "0.1"; //unexpected value
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_14)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "a"; //unexpected value
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_15)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ //k is not specified
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_16)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "-1"; //unexpected value
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_17)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "0.1"; //unexpected value
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_18)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "a"; //unexpected value
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_19)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ //m is not specified
+ (*profile)["c"] = "2";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_20)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "-1"; //unexpected value
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_21)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "0.1"; //unexpected value
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_22)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "a"; //unexpected value
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_23)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ //c is not specified
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_24)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "1"; //unexpected value
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ //w is default value
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_25)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "-1"; //unexpected value
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ //w is default value
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_26)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "0.1"; //unexpected value
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ //w is default value
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_27)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "a"; //unexpected value
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ //w is default value
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_28)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "10"; //c > m
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_29)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ //k is not specified
+ //m is not specified
+ //c is not specified
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ //k,m,c are default values
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_30)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "12";
+ (*profile)["m"] = "8";
+ (*profile)["c"] = "8";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(12, shec->k);
+ EXPECT_EQ(8, shec->m);
+ EXPECT_EQ(8, shec->c);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_31)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "13";
+ (*profile)["m"] = "7";
+ (*profile)["c"] = "7";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_32)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "7";
+ (*profile)["m"] = "13";
+ (*profile)["c"] = "13";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_33)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "12";
+ (*profile)["m"] = "9";
+ (*profile)["c"] = "8";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init_34)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "8";
+ (*profile)["m"] = "12";
+ (*profile)["c"] = "12";
+
+ int r = shec->init(*profile, &cerr);
+
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init2_4)
+{
+ //all parameters are normal values
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+ int r = shec->init(*profile, &cerr); //init executed twice
+
+ //check profile
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, init2_5)
+{
+ //all parameters are normal values
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ ErasureCodeProfile *profile2 = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "host";
+ (*profile)["k"] = "10";
+ (*profile)["m"] = "6";
+ (*profile)["c"] = "5";
+ (*profile)["w"] = "16";
+
+ int r = shec->init(*profile, &cerr);
+
+ //reexecute init
+ (*profile2)["plugin"] = "shec";
+ (*profile2)["technique"] = "";
+ (*profile2)["crush-failure-domain"] = "osd";
+ (*profile2)["k"] = "4";
+ (*profile2)["m"] = "3";
+ (*profile2)["c"] = "2";
+ shec->init(*profile2, &cerr);
+
+ EXPECT_EQ(4, shec->k);
+ EXPECT_EQ(3, shec->m);
+ EXPECT_EQ(2, shec->c);
+ EXPECT_EQ(8, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+ delete profile2;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_8)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks;
+
+ for (int i = 0; i < 8; ++i) {
+ want_to_decode.insert(i);
+ }
+ for (int i = 0; i < 5; ++i) {
+ available_chunks.insert(i);
+ }
+
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_9)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks;
+
+ for (int i = 0; i < 4; ++i) {
+ want_to_decode.insert(i);
+ }
+ for (int i = 0; i < 8; ++i) {
+ available_chunks.insert(i);
+ }
+
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_10)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks;
+
+ for (int i = 0; i < 7; ++i) {
+ want_to_decode.insert(i);
+ }
+ for (int i = 4; i < 7; ++i) {
+ available_chunks.insert(i);
+ }
+
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_EQ(-EIO, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_11)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks;
+
+ for (int i = 0; i < 5; ++i) {
+ want_to_decode.insert(i);
+ }
+ for (int i = 4; i < 7; ++i) {
+ available_chunks.insert(i);
+ }
+
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_EQ(-EIO, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_12)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ //minimum_chunks is NULL
+
+ for (int i = 0; i < 7; ++i) {
+ want_to_decode.insert(i);
+ available_chunks.insert(i);
+ }
+
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks, NULL);
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_13)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks, minimum;
+
+ for (int i = 0; i < 7; ++i) {
+ want_to_decode.insert(i);
+ available_chunks.insert(i);
+ }
+ shec->_minimum_to_decode(want_to_decode, available_chunks, &minimum_chunks);
+ minimum = minimum_chunks; //normal value
+ for (int i = 100; i < 120; ++i) {
+ minimum_chunks.insert(i); //insert extra data
+ }
+
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(minimum, minimum_chunks);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode2_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks;
+
+ want_to_decode.insert(0);
+ available_chunks.insert(0);
+ available_chunks.insert(1);
+ available_chunks.insert(2);
+
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_TRUE(minimum_chunks.size());
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode2_3)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks;
+
+ want_to_decode.insert(0);
+ want_to_decode.insert(2);
+ available_chunks.insert(0);
+ available_chunks.insert(1);
+ available_chunks.insert(2);
+ available_chunks.insert(3);
+
+ pthread_t tid;
+ g_flag = 0;
+ pthread_create(&tid, NULL, thread1, shec);
+ while (g_flag == 0) {
+ usleep(1);
+ }
+ sleep(1);
+ printf("*** test start ***\n");
+ int r = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(want_to_decode, minimum_chunks);
+ printf("*** test end ***\n");
+ g_flag = 0;
+ pthread_join(tid, NULL);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_with_cost_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode_with_cost
+ set<int> want_to_decode;
+ map<int, int> available_chunks;
+ set<int> minimum_chunks;
+
+ for (int i = 0; i < 7; ++i) {
+ want_to_decode.insert(i);
+ available_chunks.insert(make_pair(i, i));
+ }
+
+ int r = shec->minimum_to_decode_with_cost(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_TRUE(minimum_chunks.size());
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, minimum_to_decode_with_cost_2_3)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //minimum_to_decode_with_cost
+ set<int> want_to_decode;
+ map<int, int> available_chunks;
+ set<int> minimum_chunks;
+
+ want_to_decode.insert(0);
+ want_to_decode.insert(2);
+ available_chunks[0] = 0;
+ available_chunks[1] = 1;
+ available_chunks[2] = 2;
+ available_chunks[3] = 3;
+
+ pthread_t tid;
+ g_flag = 0;
+ pthread_create(&tid, NULL, thread2, shec);
+ while (g_flag == 0) {
+ usleep(1);
+ }
+ sleep(1);
+ printf("*** test start ***\n");
+ int r = shec->minimum_to_decode_with_cost(want_to_decode, available_chunks,
+ &minimum_chunks);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(want_to_decode, minimum_chunks);
+ printf("*** test end ***\n");
+ g_flag = 0;
+ pthread_join(tid, NULL);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "0123"//128
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+ decoded.clear();
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2),
+ encoded,
+ &decoded);
+ EXPECT_NE(nullptr, shec->matrix);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(32u, decoded[0].length());
+
+ bufferlist out1, out2, usable;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i) {
+ out1.append(encoded[i]);
+ }
+ //out2 is "decoded"
+ r = shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode_2)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(32u, decoded[0].length());
+
+ bufferlist out1, out2, usable;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i)
+ out1.append(encoded[i]);
+ //out2 is "decoded"
+ shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode_3)
+{
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ bufferlist in;
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ );
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+ want_to_encode.insert(10);
+ want_to_encode.insert(11);
+ map<int, bufferlist> encoded;
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), decoded[0].length());
+
+ bufferlist out1, out2, usable;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i) {
+ out1.append(encoded[i]);
+ }
+ //out2 is "decoded"
+ shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode_4)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count() - 1; ++i) {
+ want_to_encode.insert(i);
+ }
+ want_to_encode.insert(100);
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count()-1, encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), decoded[0].length());
+
+ bufferlist out1, out2, usable;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i) {
+ out1.append(encoded[i]);
+ }
+ //out2 is "decoded"
+ shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode_8)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, NULL); //encoded = NULL
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode_9)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+ for (int i = 0; i < 100; ++i) {
+ encoded[i].append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(-EINVAL, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode2_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "0123"//128
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(32u, decoded[0].length());
+
+ bufferlist out1, out2, usable;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i) {
+ out1.append(encoded[i]);
+ }
+ //out2 is "decoded"
+ shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, encode2_3)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "0123"//128
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ pthread_t tid;
+ g_flag = 0;
+ pthread_create(&tid, NULL, thread4, shec);
+ while (g_flag == 0) {
+ usleep(1);
+ }
+ sleep(1);
+ printf("*** test start ***\n");
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+ printf("*** test end ***\n");
+ g_flag = 0;
+ pthread_join(tid, NULL);
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(32u, decoded[0].length());
+
+ bufferlist out1, out2, usable;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i) {
+ out1.append(encoded[i]);
+ }
+ //out2 is "decoded"
+ shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ // all chunks are available
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 7), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(7u, decoded.size());
+
+ bufferlist usable;
+ int cmp;
+ unsigned int c_size = shec->get_chunk_size(in.length());
+ for (unsigned int i = 0; i < shec->get_data_chunk_count(); ++i) {
+ usable.clear();
+ EXPECT_EQ(c_size, decoded[i].length());
+ if ( c_size * (i+1) <= in.length() ) {
+ usable.substr_of(in, c_size * i, c_size);
+ cmp = memcmp(decoded[i].c_str(), usable.c_str(), c_size);
+ } else {
+ usable.substr_of(in, c_size * i, in.length() % c_size);
+ cmp = memcmp(decoded[i].c_str(), usable.c_str(), in.length() % c_size);
+ }
+ EXPECT_EQ(0, cmp);
+ }
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode_8)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ // all chunks are available
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; //more than k+m
+ map<int, bufferlist> decoded;
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 8), encoded,
+ &decoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(7u, decoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ bufferlist usable;
+ int cmp;
+ unsigned int c_size = shec->get_chunk_size(in.length());
+ for (unsigned int i = 0; i < shec->get_data_chunk_count(); ++i) {
+ usable.clear();
+ EXPECT_EQ(c_size, decoded[i].length());
+ if ( c_size * (i+1) <= in.length() ) {
+ usable.substr_of(in, c_size * i, c_size);
+ cmp = memcmp(decoded[i].c_str(), usable.c_str(), c_size);
+ } else {
+ usable.substr_of(in, c_size * i, in.length() % c_size);
+ cmp = memcmp(decoded[i].c_str(), usable.c_str(), in.length() % c_size);
+ }
+ EXPECT_EQ(0, cmp);
+ }
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode_9)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ // all chunks are available
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ map<int, bufferlist> decoded;
+
+ //extra data
+ bufferlist buf;
+ buf.append("abc");
+ encoded[100] = buf;
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 10), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(7u, decoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), decoded[0].length());
+
+ bufferlist out1, usable;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i) {
+ out1.append(encoded[i]);
+ }
+ EXPECT_FALSE(out1 == in);
+ //usable is "decoded"
+ int cmp;
+ unsigned int c_size = shec->get_chunk_size(in.length());
+ for (unsigned int i = 0; i < shec->get_data_chunk_count(); ++i) {
+ usable.clear();
+ EXPECT_EQ(c_size, decoded[i].length());
+ if ( c_size * (i+1) <= in.length() ) {
+ usable.substr_of(in, c_size * i, c_size);
+ cmp = memcmp(decoded[i].c_str(), usable.c_str(), c_size);
+ } else {
+ usable.substr_of(in, c_size * i, in.length() % c_size);
+ cmp = memcmp(decoded[i].c_str(), usable.c_str(), in.length() % c_size);
+ }
+ EXPECT_EQ(0, cmp);
+ }
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode_10)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 }; //more than k+m
+ map<int, bufferlist> decoded, inchunks;
+
+ for ( unsigned int i = 0; i < 3; ++i) {
+ inchunks.insert(make_pair(i, encoded[i]));
+ }
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 7), inchunks,
+ &decoded);
+ EXPECT_EQ(-1, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode_11)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCD"//128
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4 };
+ map<int, bufferlist> decoded, inchunks;
+
+ for ( unsigned int i = 4; i < 7; ++i) {
+ inchunks.insert(make_pair(i, encoded[i]));
+ }
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 5), inchunks,
+ &decoded);
+ EXPECT_EQ(-1, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode_12)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ // all chunks are available
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+
+ //decoded = NULL
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 7), encoded,
+ NULL);
+ EXPECT_NE(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode_13)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ // all chunks are available
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6 };
+ map<int, bufferlist> decoded;
+
+ //extra data
+ bufferlist buf;
+ buf.append("a");
+ for (int i = 0; i < 100; ++i) {
+ decoded[i] = buf;
+ }
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 7), encoded,
+ &decoded);
+ EXPECT_NE(0, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode2_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ // all chunks are available
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ map<int, bufferlist> decoded;
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+
+ bufferlist out;
+ shec->decode_concat(encoded, &out);
+ bufferlist usable;
+ usable.substr_of(out, 0, in.length());
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode2_3)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ // all chunks are available
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ map<int, bufferlist> decoded;
+
+ pthread_t tid;
+ g_flag = 0;
+ pthread_create(&tid, NULL, thread4, shec);
+ while (g_flag == 0) {
+ usleep(1);
+ }
+ sleep(1);
+ printf("*** test start ***\n");
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ printf("*** test end ***\n");
+ g_flag = 0;
+ pthread_join(tid, NULL);
+
+ bufferlist out;
+ shec->decode_concat(encoded, &out);
+ bufferlist usable;
+ usable.substr_of(out, 0, in.length());
+ EXPECT_TRUE(usable == in);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, decode2_4)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ int r = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ map<int, bufferlist> decoded;
+
+ // cannot recover
+ bufferlist out;
+ map<int, bufferlist> degraded;
+ degraded[0] = encoded[0];
+
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2), degraded,
+ &decoded);
+ EXPECT_EQ(-1, r);
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, create_rule_1_2)
+{
+ //create rule
+ CrushWrapper *crush = new CrushWrapper;
+ crush->create();
+ crush->set_type_name(2, "root");
+ crush->set_type_name(1, "host");
+ crush->set_type_name(0, "osd");
+
+ int rootno;
+ crush->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, 2, 0, NULL,
+ NULL, &rootno);
+ crush->set_item_name(rootno, "default");
+
+ map < string, string > loc;
+ loc["root"] = "default";
+
+ int num_host = 2;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h = 0; h < num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o = 0; o < num_osd; ++o, ++osd) {
+ crush->insert_item(g_ceph_context, osd, 1.0,
+ string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //create_rule
+ stringstream ss;
+
+ int r = shec->create_rule("myrule", *crush, &ss);
+ EXPECT_EQ(0, r);
+ EXPECT_STREQ("myrule", crush->rule_name_map[0].c_str());
+
+ //reexecute create_rule
+ r = shec->create_rule("myrule", *crush, &ss);
+ EXPECT_EQ(-EEXIST, r);
+
+ delete shec;
+ delete profile;
+ delete crush;
+}
+
+TEST(ErasureCodeShec, create_rule_4)
+{
+ //create rule
+ CrushWrapper *crush = new CrushWrapper;
+ crush->create();
+ crush->set_type_name(2, "root");
+ crush->set_type_name(1, "host");
+ crush->set_type_name(0, "osd");
+
+ int rootno;
+ crush->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, 2, 0, NULL,
+ NULL, &rootno);
+ crush->set_item_name(rootno, "default");
+
+ map < string, string > loc;
+ loc["root"] = "default";
+
+ int num_host = 2;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h = 0; h < num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o = 0; o < num_osd; ++o, ++osd) {
+ crush->insert_item(g_ceph_context, osd, 1.0,
+ string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //create_rule
+ int r = shec->create_rule("myrule", *crush, NULL); //ss = NULL
+ EXPECT_EQ(0, r);
+
+ delete shec;
+ delete profile;
+ delete crush;
+}
+
+TEST(ErasureCodeShec, create_rule2_1)
+{
+ //create rule
+ CrushWrapper *crush = new CrushWrapper;
+ crush->create();
+ crush->set_type_name(2, "root");
+ crush->set_type_name(1, "host");
+ crush->set_type_name(0, "osd");
+
+ int rootno;
+ crush->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, 2, 0, NULL,
+ NULL, &rootno);
+ crush->set_item_name(rootno, "default");
+
+ map < string, string > loc;
+ loc["root"] = "default";
+
+ int num_host = 2;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h = 0; h < num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o = 0; o < num_osd; ++o, ++osd) {
+ crush->insert_item(g_ceph_context, osd, 1.0,
+ string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //create_rule
+ stringstream ss;
+
+ int r = shec->create_rule("myrule", *crush, &ss);
+ EXPECT_EQ(0, r);
+ EXPECT_STREQ("myrule", crush->rule_name_map[0].c_str());
+
+ delete shec;
+ delete profile;
+ delete crush;
+}
+
+struct CreateRuleset2_3_Param_d {
+ ErasureCodeShec *shec;
+ CrushWrapper *crush;
+};
+
+TEST(ErasureCodeShec, create_rule2_3)
+{
+ //create rule
+ CrushWrapper *crush = new CrushWrapper;
+ crush->create();
+ crush->set_type_name(2, "root");
+ crush->set_type_name(1, "host");
+ crush->set_type_name(0, "osd");
+
+ int rootno;
+ crush->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, 2, 0, NULL,
+ NULL, &rootno);
+ crush->set_item_name(rootno, "default");
+
+ map < string, string > loc;
+ loc["root"] = "default";
+
+ int num_host = 2;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h = 0; h < num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o = 0; o < num_osd; ++o, ++osd) {
+ crush->insert_item(g_ceph_context, osd, 1.0,
+ string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //create_rule
+ stringstream ss;
+
+ pthread_t tid;
+ g_flag = 0;
+ pthread_create(&tid, NULL, thread3, shec);
+ while (g_flag == 0) {
+ usleep(1);
+ }
+ sleep(1);
+ printf("*** test start ***\n");
+ int r = (shec->create_rule("myrule", *crush, &ss));
+ EXPECT_TRUE(r >= 0);
+ printf("*** test end ***\n");
+ g_flag = 0;
+ pthread_join(tid, NULL);
+
+ delete shec;
+ delete profile;
+ delete crush;
+}
+
+TEST(ErasureCodeShec, get_chunk_count_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //get_chunk_count
+ EXPECT_EQ(7u, shec->get_chunk_count());
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, get_data_chunk_count_1)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ shec->init(*profile, &cerr);
+
+ //get_data_chunk_count
+ EXPECT_EQ(4u, shec->get_data_chunk_count());
+
+ delete shec;
+ delete profile;
+}
+
+TEST(ErasureCodeShec, get_chunk_size_1_2)
+{
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = "4";
+ (*profile)["m"] = "3";
+ (*profile)["c"] = "2";
+ (*profile)["w"] = "8";
+ shec->init(*profile, &cerr);
+
+ //when there is no padding(128=k*w*4)
+ EXPECT_EQ(32u, shec->get_chunk_size(128));
+ //when there is padding(126=k*w*4-2)
+ EXPECT_EQ(32u, shec->get_chunk_size(126));
+
+ delete shec;
+ delete profile;
+}
+
+void* thread1(void* pParam)
+{
+ ErasureCodeShec* shec = (ErasureCodeShec*) pParam;
+ set<int> want_to_decode;
+ set<int> available_chunks;
+ set<int> minimum_chunks;
+
+ want_to_decode.insert(0);
+ want_to_decode.insert(1);
+ available_chunks.insert(0);
+ available_chunks.insert(1);
+ available_chunks.insert(2);
+
+ printf("*** thread loop start ***\n");
+ g_flag = 1;
+ while (g_flag == 1) {
+ shec->_minimum_to_decode(want_to_decode, available_chunks, &minimum_chunks);
+ }
+ printf("*** thread loop end ***\n");
+
+ return NULL;
+}
+
+void* thread2(void* pParam)
+{
+ ErasureCodeShec* shec = (ErasureCodeShec*) pParam;
+ set<int> want_to_decode;
+ map<int, int> available_chunks;
+ set<int> minimum_chunks;
+
+ want_to_decode.insert(0);
+ want_to_decode.insert(1);
+ available_chunks[0] = 0;
+ available_chunks[1] = 1;
+ available_chunks[2] = 2;
+
+ printf("*** thread loop start ***\n");
+ g_flag = 1;
+ while (g_flag == 1) {
+ shec->minimum_to_decode_with_cost(want_to_decode, available_chunks,
+ &minimum_chunks);
+ minimum_chunks.clear();
+ }
+ printf("*** thread loop end ***\n");
+
+ return NULL;
+}
+
+void* thread3(void* pParam)
+{
+ ErasureCodeShec* shec = (ErasureCodeShec*) pParam;
+
+ std::unique_ptr<CrushWrapper> crush = std::make_unique<CrushWrapper>();
+ crush->create();
+ crush->set_type_name(2, "root");
+ crush->set_type_name(1, "host");
+ crush->set_type_name(0, "osd");
+
+ int rootno;
+ crush->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, 2, 0, NULL,
+ NULL, &rootno);
+ crush->set_item_name(rootno, "default");
+
+ map < string, string > loc;
+ loc["root"] = "default";
+
+ int num_host = 2;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h = 0; h < num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o = 0; o < num_osd; ++o, ++osd) {
+ crush->insert_item(g_ceph_context, osd, 1.0,
+ string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ stringstream ss;
+ int i = 0;
+ char name[30];
+
+ printf("*** thread loop start ***\n");
+ g_flag = 1;
+ while (g_flag == 1) {
+ sprintf(name, "myrule%d", i);
+ shec->create_rule(name, *crush, &ss);
+ ++i;
+ }
+ printf("*** thread loop end ***\n");
+
+ return NULL;
+}
+
+void* thread4(void* pParam)
+{
+ ErasureCodeShec* shec = (ErasureCodeShec*) pParam;
+
+ bufferlist in;
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ );
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ map<int, bufferlist> encoded;
+
+ printf("*** thread loop start ***\n");
+ g_flag = 1;
+ while (g_flag == 1) {
+ shec->encode(want_to_encode, in, &encoded);
+ encoded.clear();
+ }
+ printf("*** thread loop end ***\n");
+
+ return NULL;
+}
+
+void* thread5(void* pParam)
+{
+ ErasureCodeShec* shec = (ErasureCodeShec*) pParam;
+
+ bufferlist in;
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//248
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//310
+ );
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+ map<int, bufferlist> encoded;
+ shec->encode(want_to_encode, in, &encoded);
+
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5 };
+ map<int, bufferlist> decoded;
+
+ printf("*** thread loop start ***\n");
+ g_flag = 1;
+ while (g_flag == 1) {
+ shec->_decode(set<int>(want_to_decode, want_to_decode + 2), encoded,
+ &decoded);
+ decoded.clear();
+ }
+ printf("*** thread loop end ***\n");
+
+ return NULL;
+}
diff --git a/src/test/erasure-code/TestErasureCodeShec_all.cc b/src/test/erasure-code/TestErasureCodeShec_all.cc
new file mode 100644
index 000000000..401b8affc
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeShec_all.cc
@@ -0,0 +1,332 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014,2015 FUJITSU LIMITED
+ *
+ * Author: Shotaro Kawaguchi <kawaguchi.s@jp.fujitsu.com>
+ * Author: Takanori Nakao <nakao.takanori@jp.fujitsu.com>
+ * Author: Takeshi Miyamae <miyamae.takeshi@jp.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+// SUMMARY: TestErasureCodeShec combination of k,m,c by 301 patterns
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "crush/CrushWrapper.h"
+#include "osd/osd_types.h"
+#include "include/stringify.h"
+#include "global/global_init.h"
+#include "erasure-code/shec/ErasureCodeShec.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "common/ceph_argparse.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+struct Param_d {
+ char* k;
+ char* m;
+ char* c;
+ int ch_size;
+ char sk[16];
+ char sm[16];
+ char sc[16];
+};
+struct Param_d param[301];
+
+unsigned int g_recover = 0;
+unsigned int g_cannot_recover = 0;
+struct Recover_d {
+ int k;
+ int m;
+ int c;
+ set<int> want;
+ set<int> avail;
+};
+struct std::vector<Recover_d> cannot_recover;
+
+class ParameterTest : public ::testing::TestWithParam<struct Param_d> {
+
+};
+
+TEST_P(ParameterTest, parameter_all)
+{
+ int result;
+ //get parameters
+ char* k = GetParam().k;
+ char* m = GetParam().m;
+ char* c = GetParam().c;
+ unsigned c_size = GetParam().ch_size;
+ int i_k = atoi(k);
+ int i_m = atoi(m);
+ int i_c = atoi(c);
+
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = k;
+ (*profile)["m"] = m;
+ (*profile)["c"] = c;
+
+ result = shec->init(*profile, &cerr);
+
+ //check profile
+ EXPECT_EQ(i_k, shec->k);
+ EXPECT_EQ(i_m, shec->m);
+ EXPECT_EQ(i_c, shec->c);
+ EXPECT_EQ(8, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, result);
+
+ //minimum_to_decode
+ //want_to_decode will be a combination that chooses 1~c from k+m
+ set<int> want_to_decode, available_chunks, minimum_chunks;
+ int array_want_to_decode[shec->get_chunk_count()];
+ struct Recover_d comb;
+
+ for (int w = 1; w <= i_c; w++) {
+ const unsigned int r = w; // combination(k+m,r)
+
+ for (unsigned int i = 0; i < r; ++i) {
+ array_want_to_decode[i] = 1;
+ }
+ for (unsigned int i = r; i < shec->get_chunk_count(); ++i) {
+ array_want_to_decode[i] = 0;
+ }
+
+ do {
+ for (unsigned int i = 0; i < shec->get_chunk_count(); i++) {
+ available_chunks.insert(i);
+ }
+ for (unsigned int i = 0; i < shec->get_chunk_count(); i++) {
+ if (array_want_to_decode[i]) {
+ want_to_decode.insert(i);
+ available_chunks.erase(i);
+ }
+ }
+
+ result = shec->_minimum_to_decode(want_to_decode, available_chunks,
+ &minimum_chunks);
+
+ if (result == 0){
+ EXPECT_EQ(0, result);
+ EXPECT_TRUE(minimum_chunks.size());
+ g_recover++;
+ } else {
+ EXPECT_EQ(-EIO, result);
+ EXPECT_EQ(0u, minimum_chunks.size());
+ g_cannot_recover++;
+ comb.k = shec->k;
+ comb.m = shec->m;
+ comb.c = shec->c;
+ comb.want = want_to_decode;
+ comb.avail = available_chunks;
+ cannot_recover.push_back(comb);
+ }
+
+ want_to_decode.clear();
+ available_chunks.clear();
+ minimum_chunks.clear();
+ } while (std::prev_permutation(
+ array_want_to_decode,
+ array_want_to_decode + shec->get_chunk_count()));
+ }
+
+ //minimum_to_decode_with_cost
+ set<int> want_to_decode_with_cost, minimum_chunks_with_cost;
+ map<int, int> available_chunks_with_cost;
+
+ for (unsigned int i = 0; i < 1; i++) {
+ want_to_decode_with_cost.insert(i);
+ }
+ for (unsigned int i = 0; i < shec->get_chunk_count(); i++) {
+ available_chunks_with_cost[i] = i;
+ }
+
+ result = shec->minimum_to_decode_with_cost(
+ want_to_decode_with_cost,
+ available_chunks_with_cost,
+ &minimum_chunks_with_cost);
+ EXPECT_EQ(0, result);
+ EXPECT_TRUE(minimum_chunks_with_cost.size());
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "012345"//192
+ );
+ for (unsigned int i = 0; i < shec->get_chunk_count(); i++) {
+ want_to_encode.insert(i);
+ }
+
+ result = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, result);
+ EXPECT_EQ(i_k+i_m, (int)encoded.size());
+ EXPECT_EQ(c_size, encoded[0].length());
+
+ //decode
+ int want_to_decode2[i_k + i_m];
+ map<int, bufferlist> decoded;
+
+ for (unsigned int i = 0; i < shec->get_chunk_count(); i++) {
+ want_to_decode2[i] = i;
+ }
+
+ result = shec->_decode(set<int>(want_to_decode2, want_to_decode2 + 2),
+ encoded, &decoded);
+ EXPECT_EQ(0, result);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(c_size, decoded[0].length());
+
+ //check encoded,decoded
+ bufferlist out1, out2, usable;
+
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); i++) {
+ out1.append(encoded[i]);
+ }
+
+ //out2 is "decoded"
+ shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+
+ //create_rule
+ stringstream ss;
+ CrushWrapper *crush = new CrushWrapper;
+ crush->create();
+ crush->set_type_name(2, "root");
+ crush->set_type_name(1, "host");
+ crush->set_type_name(0, "osd");
+
+ int rootno;
+ crush->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, 2, 0, NULL,
+ NULL, &rootno);
+ crush->set_item_name(rootno, "default");
+
+ map < string, string > loc;
+ loc["root"] = "default";
+
+ int num_host = 2;
+ int num_osd = 5;
+ int osd = 0;
+ for (int h = 0; h < num_host; ++h) {
+ loc["host"] = string("host-") + stringify(h);
+ for (int o = 0; o < num_osd; ++o, ++osd) {
+ crush->insert_item(g_ceph_context, osd, 1.0,
+ string("osd.") + stringify(osd), loc);
+ }
+ }
+
+ result = shec->create_rule("myrule", *crush, &ss);
+ EXPECT_EQ(0, result);
+ EXPECT_STREQ("myrule", crush->rule_name_map[0].c_str());
+
+ //get_chunk_count
+ EXPECT_EQ(i_k+i_m, (int)shec->get_chunk_count());
+
+ //get_data_chunk_count
+ EXPECT_EQ(i_k, (int)shec->get_data_chunk_count());
+
+ //get_chunk_size
+ EXPECT_EQ(c_size, shec->get_chunk_size(192));
+
+ delete shec;
+ delete profile;
+ delete crush;
+}
+
+INSTANTIATE_TEST_SUITE_P(Test, ParameterTest, ::testing::ValuesIn(param));
+
+int main(int argc, char **argv)
+{
+ int i = 0;
+ int r;
+ const int kObjectSize = 192;
+ unsigned alignment, tail, padded_length;
+ float recovery_percentage;
+
+ //make_kmc
+ for (unsigned int k = 1; k <= 12; k++) {
+ for (unsigned int m = 1; (m <= k) && (k + m <= 20); m++) {
+ for (unsigned int c = 1; c <= m; c++) {
+ sprintf(param[i].sk, "%u", k);
+ sprintf(param[i].sm, "%u", m);
+ sprintf(param[i].sc, "%u", c);
+
+ param[i].k = param[i].sk;
+ param[i].m = param[i].sm;
+ param[i].c = param[i].sc;
+
+ alignment = k * 8 * sizeof(int);
+ tail = kObjectSize % alignment;
+ padded_length = kObjectSize + (tail ? (alignment - tail) : 0);
+ param[i].ch_size = padded_length / k;
+ i++;
+ }
+ }
+ }
+
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ r = RUN_ALL_TESTS();
+
+ std::cout << "minimum_to_decode:recover_num = " << g_recover << std::endl;
+ std::cout << "minimum_to_decode:cannot_recover_num = " << g_cannot_recover
+ << std::endl;
+ recovery_percentage = 100.0
+ - (float) (100.0 * g_cannot_recover / (g_recover + g_cannot_recover));
+ printf("recovery_percentage:%f\n",recovery_percentage);
+ if (recovery_percentage > 99.0) {
+ std::cout << "[ OK ] Recovery percentage is more than 99.0%"
+ << std::endl;
+ } else {
+ std::cout << "[ NG ] Recovery percentage is less than 99.0%"
+ << std::endl;
+ }
+ std::cout << "cannot recovery patterns:" << std::endl;
+ for (std::vector<Recover_d>::const_iterator i = cannot_recover.begin();
+ i != cannot_recover.end(); ++i) {
+ std::cout << "---" << std::endl;
+ std::cout << "k = " << i->k << ", m = " << i->m << ", c = " << i->c
+ << std::endl;
+ std::cout << "want_to_decode :" << i->want << std::endl;
+ std::cout << "available_chunks:" << i->avail << std::endl;
+ }
+ std::cout << "---" << std::endl;
+
+ return r;
+}
diff --git a/src/test/erasure-code/TestErasureCodeShec_arguments.cc b/src/test/erasure-code/TestErasureCodeShec_arguments.cc
new file mode 100644
index 000000000..075c6383e
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeShec_arguments.cc
@@ -0,0 +1,370 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 FUJITSU LIMITED
+ *
+ * Author: Shotaro Kawaguchi <kawaguchi.s@jp.fujitsu.com>
+ * Author: Takanori Nakao <nakao.takanori@jp.fujitsu.com>
+ * Author: Takeshi Miyamae <miyamae.takeshi@jp.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+// SUMMARY: shec's gtest for each argument of minimum_to_decode()/decode()
+
+#include <algorithm>
+#include <bit>
+#include <cerrno>
+#include <cstdlib>
+
+#include "crush/CrushWrapper.h"
+#include "osd/osd_types.h"
+#include "include/stringify.h"
+#include "global/global_init.h"
+#include "erasure-code/shec/ErasureCodeShec.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "common/ceph_argparse.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+unsigned int count_num = 0;
+unsigned int unexpected_count = 0;
+unsigned int value_count = 0;
+
+map<set<int>,set<set<int> > > shec_table;
+
+constexpr int getint(std::initializer_list<int> is) {
+ int a = 0;
+ for (const auto i : is) {
+ a |= 1 << i;
+ }
+ return a;
+}
+
+void create_table_shec432() {
+ set<int> table_key,vec_avails;
+ set<set<int> > table_value;
+
+ for (int want_count = 0; want_count < 7; ++want_count) {
+ for (unsigned want = 1; want < (1<<7); ++want) {
+ table_key.clear();
+ table_value.clear();
+ if (std::popcount(want) != want_count) {
+ continue;
+ }
+ {
+ for (int i = 0; i < 7; ++i) {
+ if (want & (1 << i)) {
+ table_key.insert(i);
+ }
+ }
+ }
+ vector<int> vec;
+ for (unsigned avails = 0; avails < (1<<7); ++avails) {
+ if (want & avails) {
+ continue;
+ }
+ if (std::popcount(avails) == 2 &&
+ std::popcount(want) == 1) {
+ if (std::cmp_equal(want | avails, getint({0,1,5})) ||
+ std::cmp_equal(want | avails, getint({2,3,6}))) {
+ vec.push_back(avails);
+ }
+ }
+ }
+
+ for (unsigned avails = 0; avails < (1<<7); ++avails) {
+ if (want & avails) {
+ continue;
+ }
+ if (std::popcount(avails) == 4) {
+ auto a = to_array<std::initializer_list<int>>({
+ {0,1,2,3}, {0,1,2,4}, {0,1,2,6}, {0,1,3,4}, {0,1,3,6}, {0,1,4,6},
+ {0,2,3,4}, {0,2,3,5}, {0,2,4,5}, {0,2,4,6}, {0,2,5,6}, {0,3,4,5},
+ {0,3,4,6}, {0,3,5,6}, {0,4,5,6}, {1,2,3,4}, {1,2,3,5}, {1,2,4,5},
+ {1,2,4,6}, {1,2,5,6}, {1,3,4,5}, {1,3,4,6}, {1,3,5,6}, {1,4,5,6},
+ {2,3,4,5}, {2,4,5,6}, {3,4,5,6}});
+ if (ranges::any_of(a, std::bind_front(cmp_equal<uint, int>, avails),
+ getint)) {
+ vec.push_back(avails);
+ }
+ }
+ }
+ for (int i = 0; i < (int)vec.size(); ++i) {
+ for (int j = i + 1; j < (int)vec.size(); ++j) {
+ if ((vec[i] & vec[j]) == vec[i]) {
+ vec.erase(vec.begin() + j);
+ --j;
+ }
+ }
+ }
+ for (int i = 0; i < (int)vec.size(); ++i) {
+ vec_avails.clear();
+ for (int j = 0; j < 7; ++j) {
+ if (vec[i] & (1 << j)) {
+ vec_avails.insert(j);
+ }
+ }
+ table_value.insert(vec_avails);
+ }
+ shec_table.insert(std::make_pair(table_key,table_value));
+ }
+ }
+}
+
+bool search_table_shec432(set<int> want_to_read, set<int> available_chunks) {
+ set<set<int> > tmp;
+ set<int> settmp;
+ bool found;
+
+ tmp = shec_table.find(want_to_read)->second;
+ for (set<set<int> >::iterator itr = tmp.begin();itr != tmp.end(); ++itr) {
+ found = true;
+ value_count = 0;
+ settmp = *itr;
+ for (set<int>::iterator setitr = settmp.begin();setitr != settmp.end(); ++setitr) {
+ if (!available_chunks.count(*setitr)) {
+ found = false;
+ }
+ ++value_count;
+ }
+ if (found) {
+ return true;
+ }
+ }
+ return false;
+}
+
+TEST(ParameterTest, combination_all)
+{
+ const unsigned int kObjectSize = 128;
+
+ //get profile
+ char* k = (char*)"4";
+ char* m = (char*)"3";
+ char* c = (char*)"2";
+ int i_k = atoi(k);
+ int i_m = atoi(m);
+ int i_c = atoi(c);
+ const unsigned alignment = i_k * 8 * sizeof(int);
+ const unsigned tail = kObjectSize % alignment;
+ const unsigned padded_length = kObjectSize + (tail ? (alignment - tail) : 0);
+ const unsigned c_size = padded_length / i_k;
+
+ //init
+ ErasureCodeShecTableCache tcache;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ map < std::string, std::string > *profile = new map<std::string,
+ std::string>();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = k;
+ (*profile)["m"] = m;
+ (*profile)["c"] = c;
+
+ int result = shec->init(*profile, &cerr);
+
+ //check profile
+ EXPECT_EQ(i_k, shec->k);
+ EXPECT_EQ(i_m, shec->m);
+ EXPECT_EQ(i_c, shec->c);
+ EXPECT_EQ(8, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ EXPECT_EQ(0, result);
+
+ //encode
+ bufferlist in;
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "0123"//128
+ );
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ want_to_encode.insert(i);
+ }
+
+ map<int, bufferlist> encoded;
+ result = shec->encode(want_to_encode, in, &encoded);
+ EXPECT_EQ(0, result);
+ EXPECT_EQ(i_k+i_m, (int)encoded.size());
+ EXPECT_EQ(c_size, encoded[0].length());
+ bufferlist out1;
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); ++i) {
+ out1.append(encoded[i]);
+ }
+ EXPECT_FALSE(out1 == in);
+
+ for (unsigned int w1 = 0; w1 <= shec->get_chunk_count(); ++w1) {
+ // combination(k+m,w1)
+ int array_want_to_read[shec->get_chunk_count()];
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ array_want_to_read[i] = i < w1 ? 1 : 0;
+ }
+
+ for (unsigned w2 = 0; w2 <= shec->get_chunk_count(); ++w2) {
+ // combination(k+m,w2)
+ int array_available_chunks[shec->get_chunk_count()];
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i ) {
+ array_available_chunks[i] = i < w2 ? 1 : 0;
+ }
+
+ do {
+ do {
+ set<int> want_to_read, available_chunks;
+ map<int, bufferlist> inchunks;
+ for (unsigned int i = 0; i < shec->get_chunk_count(); ++i) {
+ if (array_want_to_read[i]) {
+ want_to_read.insert(i);
+ }
+ if (array_available_chunks[i]) {
+ available_chunks.insert(i);
+ inchunks.insert(make_pair(i,encoded[i]));
+ }
+ }
+
+ map<int, vector<pair<int,int>>> minimum_chunks;
+ map<int, bufferlist> decoded;
+ result = shec->minimum_to_decode(want_to_read, available_chunks,
+ &minimum_chunks);
+ int dresult = shec->decode(want_to_read, inchunks, &decoded,
+ shec->get_chunk_size(kObjectSize));
+ ++count_num;
+ unsigned int minimum_count = 0;
+
+ if (want_to_read.size() == 0) {
+ EXPECT_EQ(0, result);
+ EXPECT_EQ(0u, minimum_chunks.size());
+ EXPECT_EQ(0, dresult);
+ EXPECT_EQ(0u, decoded.size());
+ EXPECT_EQ(0u, decoded[0].length());
+ if (result != 0 || dresult != 0) {
+ ++unexpected_count;
+ }
+ } else {
+ // want - avail
+ set<int> want_to_read_without_avails;
+ for (auto chunk : want_to_read) {
+ if (!available_chunks.count(chunk)) {
+ want_to_read_without_avails.insert(chunk);
+ } else {
+ ++minimum_count;
+ }
+ }
+
+ if (want_to_read_without_avails.size() == 0) {
+ EXPECT_EQ(0, result);
+ EXPECT_LT(0u, minimum_chunks.size());
+ EXPECT_GE(minimum_count, minimum_chunks.size());
+ EXPECT_EQ(0, dresult);
+ EXPECT_NE(0u, decoded.size());
+ for (unsigned int i = 0; i < shec->get_data_chunk_count(); ++i) {
+ if (array_want_to_read[i]) {
+ bufferlist usable;
+ usable.substr_of(in, c_size * i, c_size);
+ int cmp = memcmp(decoded[i].c_str(), usable.c_str(), c_size);
+ EXPECT_EQ(c_size, decoded[i].length());
+ EXPECT_EQ(0, cmp);
+ if (cmp != 0) {
+ ++unexpected_count;
+ }
+ }
+ }
+ if (result != 0 || dresult != 0) {
+ ++unexpected_count;
+ }
+ } else if (want_to_read_without_avails.size() > 3) {
+ EXPECT_EQ(-EIO, result);
+ EXPECT_EQ(0u, minimum_chunks.size());
+ EXPECT_EQ(-1, dresult);
+ if (result != -EIO || dresult != -1) {
+ ++unexpected_count;
+ }
+ } else {
+ // search
+ if (search_table_shec432(want_to_read_without_avails,available_chunks)) {
+ EXPECT_EQ(0, result);
+ EXPECT_LT(0u, minimum_chunks.size());
+ EXPECT_GE(value_count + minimum_count, minimum_chunks.size());
+ EXPECT_EQ(0, dresult);
+ EXPECT_NE(0u, decoded.size());
+ for (unsigned int i = 0; i < shec->get_data_chunk_count(); ++i) {
+ if (array_want_to_read[i]) {
+ bufferlist usable;
+ usable.substr_of(in, c_size * i, c_size);
+ int cmp = memcmp(decoded[i].c_str(), usable.c_str(), c_size);
+ EXPECT_EQ(c_size, decoded[i].length());
+ EXPECT_EQ(0, cmp);
+ if (cmp != 0) {
+ ++unexpected_count;
+ std::cout << "decoded[" << i << "] = " << decoded[i].c_str() << std::endl;
+ std::cout << "usable = " << usable.c_str() << std::endl;
+ std::cout << "want_to_read :" << want_to_read << std::endl;
+ std::cout << "available_chunks:" << available_chunks << std::endl;
+ std::cout << "minimum_chunks :" << minimum_chunks << std::endl;
+ }
+ }
+ }
+ if (result != 0 || dresult != 0) {
+ ++unexpected_count;
+ }
+ } else {
+ EXPECT_EQ(-EIO, result);
+ EXPECT_EQ(0u, minimum_chunks.size());
+ EXPECT_EQ(-1, dresult);
+ if (result != -EIO || dresult != -1) {
+ ++unexpected_count;
+ }
+ }
+ }
+ }
+ } while (std::prev_permutation(
+ array_want_to_read,
+ array_want_to_read + shec->get_chunk_count()));
+
+ } while (std::prev_permutation(
+ array_available_chunks,
+ array_available_chunks + shec->get_chunk_count()));
+ }
+ }
+
+ delete shec;
+ delete profile;
+}
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ create_table_shec432();
+
+ int r = RUN_ALL_TESTS();
+
+ std::cout << "minimum_to_decode:total_num = " << count_num
+ << std::endl;
+ std::cout << "minimum_to_decode:unexpected_num = " << unexpected_count
+ << std::endl;
+
+ return r;
+}
diff --git a/src/test/erasure-code/TestErasureCodeShec_thread.cc b/src/test/erasure-code/TestErasureCodeShec_thread.cc
new file mode 100644
index 000000000..c8d7bbb1e
--- /dev/null
+++ b/src/test/erasure-code/TestErasureCodeShec_thread.cc
@@ -0,0 +1,219 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014,2015 FUJITSU LIMITED
+ *
+ * Author: Shotaro Kawaguchi <kawaguchi.s@jp.fujitsu.com>
+ * Author: Takanori Nakao <nakao.takanori@jp.fujitsu.com>
+ * Author: Takeshi Miyamae <miyamae.takeshi@jp.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+// SUMMARY: TestErasureCodeShec executes some threads at the same time
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "crush/CrushWrapper.h"
+#include "osd/osd_types.h"
+#include "include/stringify.h"
+#include "erasure-code/shec/ErasureCodeShec.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+void* thread1(void* pParam);
+
+class TestParam {
+public:
+ string k, m, c, w;
+};
+
+TEST(ErasureCodeShec, thread)
+{
+ TestParam param1, param2, param3, param4, param5;
+ param1.k = "6";
+ param1.m = "4";
+ param1.c = "3";
+ param1.w = "8";
+
+ param2.k = "4";
+ param2.m = "3";
+ param2.c = "2";
+ param2.w = "16";
+
+ param3.k = "10";
+ param3.m = "8";
+ param3.c = "4";
+ param3.w = "32";
+
+ param4.k = "5";
+ param4.m = "5";
+ param4.c = "5";
+ param4.w = "8";
+
+ param5.k = "9";
+ param5.m = "9";
+ param5.c = "6";
+ param5.w = "16";
+
+ pthread_t tid1, tid2, tid3, tid4, tid5;
+ pthread_create(&tid1, NULL, thread1, (void*) &param1);
+ std::cout << "thread1 start " << std::endl;
+ pthread_create(&tid2, NULL, thread1, (void*) &param2);
+ std::cout << "thread2 start " << std::endl;
+ pthread_create(&tid3, NULL, thread1, (void*) &param3);
+ std::cout << "thread3 start " << std::endl;
+ pthread_create(&tid4, NULL, thread1, (void*) &param4);
+ std::cout << "thread4 start " << std::endl;
+ pthread_create(&tid5, NULL, thread1, (void*) &param5);
+ std::cout << "thread5 start " << std::endl;
+
+ pthread_join(tid1, NULL);
+ pthread_join(tid2, NULL);
+ pthread_join(tid3, NULL);
+ pthread_join(tid4, NULL);
+ pthread_join(tid5, NULL);
+}
+
+void* thread1(void* pParam)
+{
+ TestParam* param = static_cast<TestParam*>(pParam);
+
+ time_t start, end;
+
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+
+ instance.disable_dlclose = true;
+ {
+ std::lock_guard l{instance.lock};
+ __erasure_code_init((char*) "shec", (char*) "");
+ }
+ std::cout << "__erasure_code_init finish " << std::endl;
+
+ //encode
+ bufferlist in;
+ set<int> want_to_encode;
+ map<int, bufferlist> encoded;
+
+ in.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" //length = 62
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//124
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"//186
+ "012345"//192
+ );
+
+ //decode
+ int want_to_decode[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ map<int, bufferlist> decoded;
+ bufferlist out1, out2, usable;
+
+ time(&start);
+ time(&end);
+ const int kTestSec = 60;
+ ErasureCodeShecTableCache tcache;
+
+ while (kTestSec >= (end - start)) {
+ //init
+ int r;
+ ErasureCodeShec* shec = new ErasureCodeShecReedSolomonVandermonde(
+ tcache,
+ ErasureCodeShec::MULTIPLE);
+ ErasureCodeProfile *profile = new ErasureCodeProfile();
+ (*profile)["plugin"] = "shec";
+ (*profile)["technique"] = "multiple";
+ (*profile)["crush-failure-domain"] = "osd";
+ (*profile)["k"] = param->k;
+ (*profile)["m"] = param->m;
+ (*profile)["c"] = param->c;
+ (*profile)["w"] = param->w;
+ r = shec->init(*profile, &cerr);
+
+ int i_k = std::atoi(param->k.c_str());
+ int i_m = std::atoi(param->m.c_str());
+ int i_c = std::atoi(param->c.c_str());
+ int i_w = std::atoi(param->w.c_str());
+
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(i_k, shec->k);
+ EXPECT_EQ(i_m, shec->m);
+ EXPECT_EQ(i_c, shec->c);
+ EXPECT_EQ(i_w, shec->w);
+ EXPECT_EQ(ErasureCodeShec::MULTIPLE, shec->technique);
+ EXPECT_STREQ("default", shec->rule_root.c_str());
+ EXPECT_STREQ("osd", shec->rule_failure_domain.c_str());
+ EXPECT_TRUE(shec->matrix != NULL);
+ if ((shec->matrix == NULL)) {
+ std::cout << "matrix is null" << std::endl;
+ // error
+ break;
+ }
+
+ //encode
+ for (unsigned int i = 0; i < shec->get_chunk_count(); i++) {
+ want_to_encode.insert(i);
+ }
+ r = shec->encode(want_to_encode, in, &encoded);
+
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(shec->get_chunk_count(), encoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), encoded[0].length());
+
+ if (r != 0) {
+ std::cout << "error in encode" << std::endl;
+ //error
+ break;
+ }
+
+ //decode
+ r = shec->_decode(set<int>(want_to_decode, want_to_decode + 2),
+ encoded,
+ &decoded);
+
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(2u, decoded.size());
+ EXPECT_EQ(shec->get_chunk_size(in.length()), decoded[0].length());
+
+ if (r != 0) {
+ std::cout << "error in decode" << std::endl;
+ //error
+ break;
+ }
+
+ //out1 is "encoded"
+ for (unsigned int i = 0; i < encoded.size(); i++) {
+ out1.append(encoded[i]);
+ }
+ //out2 is "decoded"
+ shec->decode_concat(encoded, &out2);
+ usable.substr_of(out2, 0, in.length());
+ EXPECT_FALSE(out1 == in);
+ EXPECT_TRUE(usable == in);
+ if (out1 == in || !(usable == in)) {
+ std::cout << "encode(decode) result is not correct" << std::endl;
+ break;
+ }
+
+ delete shec;
+ delete profile;
+ want_to_encode.clear();
+ encoded.clear();
+ decoded.clear();
+ out1.clear();
+ out2.clear();
+ usable.clear();
+
+ time(&end);
+ }
+
+ return NULL;
+}
diff --git a/src/test/erasure-code/ceph_erasure_code_benchmark.cc b/src/test/erasure-code/ceph_erasure_code_benchmark.cc
new file mode 100644
index 000000000..c86e58697
--- /dev/null
+++ b/src/test/erasure-code/ceph_erasure_code_benchmark.cc
@@ -0,0 +1,354 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/program_options/option.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+#include <boost/program_options/parsers.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "common/Clock.h"
+#include "include/utime.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "erasure-code/ErasureCode.h"
+#include "ceph_erasure_code_benchmark.h"
+
+using std::endl;
+using std::cerr;
+using std::cout;
+using std::map;
+using std::set;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+namespace po = boost::program_options;
+
+int ErasureCodeBench::setup(int argc, char** argv) {
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help,h", "produce help message")
+ ("verbose,v", "explain what happens")
+ ("size,s", po::value<int>()->default_value(1024 * 1024),
+ "size of the buffer to be encoded")
+ ("iterations,i", po::value<int>()->default_value(1),
+ "number of encode/decode runs")
+ ("plugin,p", po::value<string>()->default_value("jerasure"),
+ "erasure code plugin name")
+ ("workload,w", po::value<string>()->default_value("encode"),
+ "run either encode or decode")
+ ("erasures,e", po::value<int>()->default_value(1),
+ "number of erasures when decoding")
+ ("erased", po::value<vector<int> >(),
+ "erased chunk (repeat if more than one chunk is erased)")
+ ("erasures-generation,E", po::value<string>()->default_value("random"),
+ "If set to 'random', pick the number of chunks to recover (as specified by "
+ " --erasures) at random. If set to 'exhaustive' try all combinations of erasures "
+ " (i.e. k=4,m=3 with one erasure will try to recover from the erasure of "
+ " the first chunk, then the second etc.)")
+ ("parameter,P", po::value<vector<string> >(),
+ "add a parameter to the erasure code profile")
+ ;
+
+ po::variables_map vm;
+ po::parsed_options parsed =
+ po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
+ po::store(
+ parsed,
+ vm);
+ po::notify(vm);
+
+ vector<const char *> ceph_options;
+ vector<string> ceph_option_strings = po::collect_unrecognized(
+ parsed.options, po::include_positional);
+ ceph_options.reserve(ceph_option_strings.size());
+ for (vector<string>::iterator i = ceph_option_strings.begin();
+ i != ceph_option_strings.end();
+ ++i) {
+ ceph_options.push_back(i->c_str());
+ }
+
+ cct = global_init(
+ NULL, ceph_options, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ if (vm.count("help")) {
+ cout << desc << std::endl;
+ return 1;
+ }
+
+ if (vm.count("parameter")) {
+ const vector<string> &p = vm["parameter"].as< vector<string> >();
+ for (vector<string>::const_iterator i = p.begin();
+ i != p.end();
+ ++i) {
+ std::vector<std::string> strs;
+ boost::split(strs, *i, boost::is_any_of("="));
+ if (strs.size() != 2) {
+ cerr << "--parameter " << *i << " ignored because it does not contain exactly one =" << endl;
+ } else {
+ profile[strs[0]] = strs[1];
+ }
+ }
+ }
+
+ in_size = vm["size"].as<int>();
+ max_iterations = vm["iterations"].as<int>();
+ plugin = vm["plugin"].as<string>();
+ workload = vm["workload"].as<string>();
+ erasures = vm["erasures"].as<int>();
+ if (vm.count("erasures-generation") > 0 &&
+ vm["erasures-generation"].as<string>() == "exhaustive")
+ exhaustive_erasures = true;
+ else
+ exhaustive_erasures = false;
+ if (vm.count("erased") > 0)
+ erased = vm["erased"].as<vector<int> >();
+
+ try {
+ k = stoi(profile["k"]);
+ m = stoi(profile["m"]);
+ } catch (const std::logic_error& e) {
+ cout << "Invalid k and/or m: k=" << profile["k"] << ", m=" << profile["m"]
+ << " (" << e.what() << ")" << endl;
+ return -EINVAL;
+ }
+ if (k <= 0) {
+ cout << "parameter k is " << k << ". But k needs to be > 0." << endl;
+ return -EINVAL;
+ } else if ( m < 0 ) {
+ cout << "parameter m is " << m << ". But m needs to be >= 0." << endl;
+ return -EINVAL;
+ }
+
+ verbose = vm.count("verbose") > 0 ? true : false;
+
+ return 0;
+}
+
+int ErasureCodeBench::run() {
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ instance.disable_dlclose = true;
+
+ if (workload == "encode")
+ return encode();
+ else
+ return decode();
+}
+
+int ErasureCodeBench::encode()
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeInterfaceRef erasure_code;
+ stringstream messages;
+ int code = instance.factory(plugin,
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &messages);
+ if (code) {
+ cerr << messages.str() << endl;
+ return code;
+ }
+
+ bufferlist in;
+ in.append(string(in_size, 'X'));
+ in.rebuild_aligned(ErasureCode::SIMD_ALIGN);
+ set<int> want_to_encode;
+ for (int i = 0; i < k + m; i++) {
+ want_to_encode.insert(i);
+ }
+ utime_t begin_time = ceph_clock_now();
+ for (int i = 0; i < max_iterations; i++) {
+ std::map<int,bufferlist> encoded;
+ code = erasure_code->encode(want_to_encode, in, &encoded);
+ if (code)
+ return code;
+ }
+ utime_t end_time = ceph_clock_now();
+ cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl;
+ return 0;
+}
+
+static void display_chunks(const map<int,bufferlist> &chunks,
+ unsigned int chunk_count) {
+ cout << "chunks ";
+ for (unsigned int chunk = 0; chunk < chunk_count; chunk++) {
+ if (chunks.count(chunk) == 0) {
+ cout << "(" << chunk << ")";
+ } else {
+ cout << " " << chunk << " ";
+ }
+ cout << " ";
+ }
+ cout << "(X) is an erased chunk" << endl;
+}
+
+int ErasureCodeBench::decode_erasures(const map<int,bufferlist> &all_chunks,
+ const map<int,bufferlist> &chunks,
+ unsigned i,
+ unsigned want_erasures,
+ ErasureCodeInterfaceRef erasure_code)
+{
+ int code = 0;
+
+ if (want_erasures == 0) {
+ if (verbose)
+ display_chunks(chunks, erasure_code->get_chunk_count());
+ set<int> want_to_read;
+ for (unsigned int chunk = 0; chunk < erasure_code->get_chunk_count(); chunk++)
+ if (chunks.count(chunk) == 0)
+ want_to_read.insert(chunk);
+
+ map<int,bufferlist> decoded;
+ code = erasure_code->decode(want_to_read, chunks, &decoded, 0);
+ if (code)
+ return code;
+ for (set<int>::iterator chunk = want_to_read.begin();
+ chunk != want_to_read.end();
+ ++chunk) {
+ if (all_chunks.find(*chunk)->second.length() != decoded[*chunk].length()) {
+ cerr << "chunk " << *chunk << " length=" << all_chunks.find(*chunk)->second.length()
+ << " decoded with length=" << decoded[*chunk].length() << endl;
+ return -1;
+ }
+ bufferlist tmp = all_chunks.find(*chunk)->second;
+ if (!tmp.contents_equal(decoded[*chunk])) {
+ cerr << "chunk " << *chunk
+ << " content and recovered content are different" << endl;
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ for (; i < erasure_code->get_chunk_count(); i++) {
+ map<int,bufferlist> one_less = chunks;
+ one_less.erase(i);
+ code = decode_erasures(all_chunks, one_less, i + 1, want_erasures - 1, erasure_code);
+ if (code)
+ return code;
+ }
+
+ return 0;
+}
+
+int ErasureCodeBench::decode()
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeInterfaceRef erasure_code;
+ stringstream messages;
+ int code = instance.factory(plugin,
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &messages);
+ if (code) {
+ cerr << messages.str() << endl;
+ return code;
+ }
+
+ bufferlist in;
+ in.append(string(in_size, 'X'));
+ in.rebuild_aligned(ErasureCode::SIMD_ALIGN);
+
+ set<int> want_to_encode;
+ for (int i = 0; i < k + m; i++) {
+ want_to_encode.insert(i);
+ }
+
+ map<int,bufferlist> encoded;
+ code = erasure_code->encode(want_to_encode, in, &encoded);
+ if (code)
+ return code;
+
+ set<int> want_to_read = want_to_encode;
+
+ if (erased.size() > 0) {
+ for (vector<int>::const_iterator i = erased.begin();
+ i != erased.end();
+ ++i)
+ encoded.erase(*i);
+ display_chunks(encoded, erasure_code->get_chunk_count());
+ }
+
+ utime_t begin_time = ceph_clock_now();
+ for (int i = 0; i < max_iterations; i++) {
+ if (exhaustive_erasures) {
+ code = decode_erasures(encoded, encoded, 0, erasures, erasure_code);
+ if (code)
+ return code;
+ } else if (erased.size() > 0) {
+ map<int,bufferlist> decoded;
+ code = erasure_code->decode(want_to_read, encoded, &decoded, 0);
+ if (code)
+ return code;
+ } else {
+ map<int,bufferlist> chunks = encoded;
+ for (int j = 0; j < erasures; j++) {
+ int erasure;
+ do {
+ erasure = rand() % ( k + m );
+ } while(chunks.count(erasure) == 0);
+ chunks.erase(erasure);
+ }
+ map<int,bufferlist> decoded;
+ code = erasure_code->decode(want_to_read, chunks, &decoded, 0);
+ if (code)
+ return code;
+ }
+ }
+ utime_t end_time = ceph_clock_now();
+ cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl;
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ ErasureCodeBench ecbench;
+ try {
+ int err = ecbench.setup(argc, argv);
+ if (err)
+ return err;
+ return ecbench.run();
+ } catch(po::error &e) {
+ cerr << e.what() << endl;
+ return 1;
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../../../build ; make -j4 ceph_erasure_code_benchmark &&
+ * valgrind --tool=memcheck --leak-check=full \
+ * ./bin/ceph_erasure_code_benchmark \
+ * --plugin jerasure \
+ * --parameter directory=lib \
+ * --parameter technique=reed_sol_van \
+ * --parameter k=2 \
+ * --parameter m=2 \
+ * --iterations 1
+ * "
+ * End:
+ */
diff --git a/src/test/erasure-code/ceph_erasure_code_benchmark.h b/src/test/erasure-code/ceph_erasure_code_benchmark.h
new file mode 100644
index 000000000..59149a74c
--- /dev/null
+++ b/src/test/erasure-code/ceph_erasure_code_benchmark.h
@@ -0,0 +1,62 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef CEPH_ERASURE_CODE_BENCHMARK_H
+#define CEPH_ERASURE_CODE_BENCHMARK_H
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include <boost/intrusive_ptr.hpp>
+
+#include "include/buffer.h"
+
+#include "common/ceph_context.h"
+
+#include "erasure-code/ErasureCodeInterface.h"
+
+class ErasureCodeBench {
+ int in_size;
+ int max_iterations;
+ int erasures;
+ int k;
+ int m;
+
+ std::string plugin;
+
+ bool exhaustive_erasures;
+ std::vector<int> erased;
+ std::string workload;
+
+ ceph::ErasureCodeProfile profile;
+
+ bool verbose;
+ boost::intrusive_ptr<CephContext> cct;
+public:
+ int setup(int argc, char** argv);
+ int run();
+ int decode_erasures(const std::map<int, ceph::buffer::list> &all_chunks,
+ const std::map<int, ceph::buffer::list> &chunks,
+ unsigned i,
+ unsigned want_erasures,
+ ErasureCodeInterfaceRef erasure_code);
+ int decode();
+ int encode();
+};
+
+#endif
diff --git a/src/test/erasure-code/ceph_erasure_code_non_regression.cc b/src/test/erasure-code/ceph_erasure_code_non_regression.cc
new file mode 100644
index 000000000..3ce31b243
--- /dev/null
+++ b/src/test/erasure-code/ceph_erasure_code_non_regression.cc
@@ -0,0 +1,327 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Red Hat (C) 2014, 2015 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/program_options/option.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+#include <boost/program_options/parsers.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/errno.h"
+#include "common/ceph_context.h"
+#include "common/ceph_argparse.h"
+#include "common/config.h"
+#include "erasure-code/ErasureCodePlugin.h"
+
+namespace po = boost::program_options;
+using namespace std;
+
+class ErasureCodeNonRegression {
+ unsigned stripe_width;
+ string plugin;
+ bool create;
+ bool check;
+ string base;
+ string directory;
+ ErasureCodeProfile profile;
+ boost::intrusive_ptr<CephContext> cct;
+public:
+ int setup(int argc, char** argv);
+ int run();
+ int run_create();
+ int run_check();
+ int decode_erasures(ErasureCodeInterfaceRef erasure_code,
+ set<int> erasures,
+ map<int,bufferlist> chunks);
+ string content_path();
+ string chunk_path(unsigned int chunk);
+};
+
+int ErasureCodeNonRegression::setup(int argc, char** argv) {
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help,h", "produce help message")
+ ("stripe-width,s", po::value<int>()->default_value(4 * 1024),
+ "stripe_width, i.e. the size of the buffer to be encoded")
+ ("plugin,p", po::value<string>()->default_value("jerasure"),
+ "erasure code plugin name")
+ ("base", po::value<string>()->default_value("."),
+ "prefix all paths with base")
+ ("parameter,P", po::value<vector<string> >(),
+ "add a parameter to the erasure code profile")
+ ("create", "create the erasure coded content in the directory")
+ ("check", "check the content in the directory matches the chunks and vice versa")
+ ;
+
+ po::variables_map vm;
+ po::parsed_options parsed =
+ po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
+ po::store(
+ parsed,
+ vm);
+ po::notify(vm);
+
+ vector<const char *> ceph_options;
+ vector<string> ceph_option_strings = po::collect_unrecognized(
+ parsed.options, po::include_positional);
+ ceph_options.reserve(ceph_option_strings.size());
+ for (vector<string>::iterator i = ceph_option_strings.begin();
+ i != ceph_option_strings.end();
+ ++i) {
+ ceph_options.push_back(i->c_str());
+ }
+
+ cct = global_init(NULL, ceph_options, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ if (vm.count("help")) {
+ cout << desc << std::endl;
+ return 1;
+ }
+
+ stripe_width = vm["stripe-width"].as<int>();
+ plugin = vm["plugin"].as<string>();
+ base = vm["base"].as<string>();
+ check = vm.count("check") > 0;
+ create = vm.count("create") > 0;
+
+ if (!check && !create) {
+ cerr << "must specifify either --check, or --create" << endl;
+ return 1;
+ }
+
+ {
+ stringstream path;
+ path << base << "/" << "plugin=" << plugin << " stripe-width=" << stripe_width;
+ directory = path.str();
+ }
+
+ if (vm.count("parameter")) {
+ const vector<string> &p = vm["parameter"].as< vector<string> >();
+ for (vector<string>::const_iterator i = p.begin();
+ i != p.end();
+ ++i) {
+ std::vector<std::string> strs;
+ boost::split(strs, *i, boost::is_any_of("="));
+ if (strs.size() != 2) {
+ cerr << "--parameter " << *i << " ignored because it does not contain exactly one =" << endl;
+ } else {
+ profile[strs[0]] = strs[1];
+ }
+ directory += " " + *i;
+ }
+ }
+
+ return 0;
+}
+
+int ErasureCodeNonRegression::run()
+ {
+ int ret = 0;
+ if(create && (ret = run_create()))
+ return ret;
+ if(check && (ret = run_check()))
+ return ret;
+ return ret;
+}
+
+int ErasureCodeNonRegression::run_create()
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeInterfaceRef erasure_code;
+ stringstream messages;
+ int code = instance.factory(plugin,
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &messages);
+ if (code) {
+ cerr << messages.str() << endl;
+ return code;
+ }
+
+ if (::mkdir(directory.c_str(), 0755)) {
+ cerr << "mkdir(" << directory << "): " << cpp_strerror(errno) << endl;
+ return 1;
+ }
+ unsigned payload_chunk_size = 37;
+ string payload;
+ for (unsigned j = 0; j < payload_chunk_size; ++j)
+ payload.push_back('a' + (rand() % 26));
+ bufferlist in;
+ for (unsigned j = 0; j < stripe_width; j += payload_chunk_size)
+ in.append(payload);
+ if (stripe_width < in.length())
+ in.splice(stripe_width, in.length() - stripe_width);
+ if (in.write_file(content_path().c_str()))
+ return 1;
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < erasure_code->get_chunk_count(); i++) {
+ want_to_encode.insert(i);
+ }
+ map<int,bufferlist> encoded;
+ code = erasure_code->encode(want_to_encode, in, &encoded);
+ if (code)
+ return code;
+ for (map<int,bufferlist>::iterator chunk = encoded.begin();
+ chunk != encoded.end();
+ ++chunk) {
+ if (chunk->second.write_file(chunk_path(chunk->first).c_str()))
+ return 1;
+ }
+ return 0;
+}
+
+int ErasureCodeNonRegression::decode_erasures(ErasureCodeInterfaceRef erasure_code,
+ set<int> erasures,
+ map<int,bufferlist> chunks)
+{
+ map<int,bufferlist> available;
+ for (map<int,bufferlist>::iterator chunk = chunks.begin();
+ chunk != chunks.end();
+ ++chunk) {
+ if (erasures.count(chunk->first) == 0)
+ available[chunk->first] = chunk->second;
+
+ }
+ map<int,bufferlist> decoded;
+ int code = erasure_code->decode(erasures, available, &decoded, available.begin()->second.length());
+ if (code)
+ return code;
+ for (set<int>::iterator erasure = erasures.begin();
+ erasure != erasures.end();
+ ++erasure) {
+ if (!chunks[*erasure].contents_equal(decoded[*erasure])) {
+ cerr << "chunk " << *erasure << " incorrectly recovered" << endl;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int ErasureCodeNonRegression::run_check()
+{
+ ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+ ErasureCodeInterfaceRef erasure_code;
+ stringstream messages;
+ int code = instance.factory(plugin,
+ g_conf().get_val<std::string>("erasure_code_dir"),
+ profile, &erasure_code, &messages);
+ if (code) {
+ cerr << messages.str() << endl;
+ return code;
+ }
+ string errors;
+ bufferlist in;
+ if (in.read_file(content_path().c_str(), &errors)) {
+ cerr << errors << endl;
+ return 1;
+ }
+ set<int> want_to_encode;
+ for (unsigned int i = 0; i < erasure_code->get_chunk_count(); i++) {
+ want_to_encode.insert(i);
+ }
+
+ map<int,bufferlist> encoded;
+ code = erasure_code->encode(want_to_encode, in, &encoded);
+ if (code)
+ return code;
+
+ for (map<int,bufferlist>::iterator chunk = encoded.begin();
+ chunk != encoded.end();
+ ++chunk) {
+ bufferlist existing;
+ if (existing.read_file(chunk_path(chunk->first).c_str(), &errors)) {
+ cerr << errors << endl;
+ return 1;
+ }
+ bufferlist &old = chunk->second;
+ if (existing.length() != old.length() ||
+ memcmp(existing.c_str(), old.c_str(), old.length())) {
+ cerr << "chunk " << chunk->first << " encodes differently" << endl;
+ return 1;
+ }
+ }
+
+ // erasing a single chunk is likely to use a specific code path in every plugin
+ set<int> erasures;
+ erasures.clear();
+ erasures.insert(0);
+ code = decode_erasures(erasure_code, erasures, encoded);
+ if (code)
+ return code;
+
+ if (erasure_code->get_chunk_count() - erasure_code->get_data_chunk_count() > 1) {
+ // erasing two chunks is likely to be the general case
+ erasures.clear();
+ erasures.insert(0);
+ erasures.insert(erasure_code->get_chunk_count() - 1);
+ code = decode_erasures(erasure_code, erasures, encoded);
+ if (code)
+ return code;
+ }
+
+ return 0;
+}
+
+string ErasureCodeNonRegression::content_path()
+{
+ stringstream path;
+ path << directory << "/content";
+ return path.str();
+}
+
+string ErasureCodeNonRegression::chunk_path(unsigned int chunk)
+{
+ stringstream path;
+ path << directory << "/" << chunk;
+ return path.str();
+}
+
+int main(int argc, char** argv) {
+ ErasureCodeNonRegression non_regression;
+ int err = non_regression.setup(argc, argv);
+ if (err)
+ return err;
+ return non_regression.run();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 &&
+ * make ceph_erasure_code_non_regression &&
+ * libtool --mode=execute valgrind --tool=memcheck --leak-check=full \
+ * ./ceph_erasure_code_non_regression \
+ * --plugin jerasure \
+ * --parameter technique=reed_sol_van \
+ * --parameter k=2 \
+ * --parameter m=2 \
+ * --directory /tmp/ceph_erasure_code_non_regression \
+ * --stripe-width 3181 \
+ * --create \
+ * --check
+ * "
+ * End:
+ */
diff --git a/src/test/escape.cc b/src/test/escape.cc
new file mode 100644
index 000000000..cab87043f
--- /dev/null
+++ b/src/test/escape.cc
@@ -0,0 +1,128 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "common/escape.h"
+#include "gtest/gtest.h"
+#include <stdint.h>
+
+static std::string escape_xml_attrs(const char *str)
+{
+ int len = escape_xml_attr_len(str);
+ char out[len];
+ escape_xml_attr(str, out);
+ return out;
+}
+static std::string escape_xml_stream(const char *str)
+{
+ std::stringstream ss;
+ ss << xml_stream_escaper(str);
+ return ss.str();
+}
+
+TEST(EscapeXml, PassThrough) {
+ ASSERT_EQ(escape_xml_attrs("simplicity itself"), "simplicity itself");
+ ASSERT_EQ(escape_xml_stream("simplicity itself"), "simplicity itself");
+ ASSERT_EQ(escape_xml_attrs(""), "");
+ ASSERT_EQ(escape_xml_stream(""), "");
+ ASSERT_EQ(escape_xml_attrs("simple examples please!"), "simple examples please!");
+ ASSERT_EQ(escape_xml_stream("simple examples please!"), "simple examples please!");
+}
+
+TEST(EscapeXml, EntityRefs1) {
+ ASSERT_EQ(escape_xml_attrs("The \"scare quotes\""), "The &quot;scare quotes&quot;");
+ ASSERT_EQ(escape_xml_stream("The \"scare quotes\""), "The &quot;scare quotes&quot;");
+ ASSERT_EQ(escape_xml_attrs("I <3 XML"), "I &lt;3 XML");
+ ASSERT_EQ(escape_xml_stream("I <3 XML"), "I &lt;3 XML");
+ ASSERT_EQ(escape_xml_attrs("Some 'single' \"quotes\" here"),
+ "Some &apos;single&apos; &quot;quotes&quot; here");
+ ASSERT_EQ(escape_xml_stream("Some 'single' \"quotes\" here"),
+ "Some &apos;single&apos; &quot;quotes&quot; here");
+}
+
+TEST(EscapeXml, ControlChars) {
+ ASSERT_EQ(escape_xml_attrs("\x01\x02\x03"), "&#x01;&#x02;&#x03;");
+ ASSERT_EQ(escape_xml_stream("\x01\x02\x03"), "&#x01;&#x02;&#x03;");
+
+ ASSERT_EQ(escape_xml_attrs("abc\x7f"), "abc&#x7f;");
+ ASSERT_EQ(escape_xml_stream("abc\x7f"), "abc&#x7f;");
+}
+
+TEST(EscapeXml, Utf8) {
+ const char *cc1 = "\xe6\xb1\x89\xe5\xad\x97\n";
+ ASSERT_EQ(escape_xml_attrs(cc1), cc1);
+ ASSERT_EQ(escape_xml_stream(cc1), cc1);
+
+ ASSERT_EQ(escape_xml_attrs("<\xe6\xb1\x89\xe5\xad\x97>\n"), "&lt;\xe6\xb1\x89\xe5\xad\x97&gt;\n");
+ ASSERT_EQ(escape_xml_stream("<\xe6\xb1\x89\xe5\xad\x97>\n"), "&lt;\xe6\xb1\x89\xe5\xad\x97&gt;\n");
+}
+
+static std::string escape_json_attrs(const char *str, size_t src_len = 0)
+{
+ if (!src_len)
+ src_len = strlen(str);
+ int len = escape_json_attr_len(str, src_len);
+ char out[len];
+ escape_json_attr(str, src_len, out);
+ return out;
+}
+static std::string escape_json_stream(const char *str, size_t src_len = 0)
+{
+ if (!src_len)
+ src_len = strlen(str);
+ std::stringstream ss;
+ ss << json_stream_escaper(std::string_view(str, src_len));
+ return ss.str();
+}
+
+TEST(EscapeJson, PassThrough) {
+ ASSERT_EQ(escape_json_attrs("simplicity itself"), "simplicity itself");
+ ASSERT_EQ(escape_json_stream("simplicity itself"), "simplicity itself");
+ ASSERT_EQ(escape_json_attrs(""), "");
+ ASSERT_EQ(escape_json_stream(""), "");
+ ASSERT_EQ(escape_json_attrs("simple examples please!"), "simple examples please!");
+ ASSERT_EQ(escape_json_stream("simple examples please!"), "simple examples please!");
+}
+
+TEST(EscapeJson, Escapes1) {
+ ASSERT_EQ(escape_json_attrs("The \"scare quotes\""),
+ "The \\\"scare quotes\\\"");
+ ASSERT_EQ(escape_json_stream("The \"scare quotes\""),
+ "The \\\"scare quotes\\\"");
+ ASSERT_EQ(escape_json_attrs("I <3 JSON"), "I <3 JSON");
+ ASSERT_EQ(escape_json_stream("I <3 JSON"), "I <3 JSON");
+ ASSERT_EQ(escape_json_attrs("Some 'single' \"quotes\" here"),
+ "Some 'single' \\\"quotes\\\" here");
+ ASSERT_EQ(escape_json_stream("Some 'single' \"quotes\" here"),
+ "Some 'single' \\\"quotes\\\" here");
+ ASSERT_EQ(escape_json_attrs("tabs\tand\tnewlines\n, oh my"),
+ "tabs\\tand\\tnewlines\\n, oh my");
+ ASSERT_EQ(escape_json_stream("tabs\tand\tnewlines\n, oh my"),
+ "tabs\\tand\\tnewlines\\n, oh my");
+}
+
+TEST(EscapeJson, ControlChars) {
+ ASSERT_EQ(escape_json_attrs("\x01\x02\x03"), "\\u0001\\u0002\\u0003");
+ ASSERT_EQ(escape_json_stream("\x01\x02\x03"), "\\u0001\\u0002\\u0003");
+ ASSERT_EQ(escape_json_stream("\x00\x02\x03", 3), "\\u0000\\u0002\\u0003");
+
+ // json can't print binary data!
+ ASSERT_EQ(escape_json_stream("\x00\x7f\xff", 3), "\\u0000\\u007f\xff");
+
+ ASSERT_EQ(escape_json_attrs("abc\x7f"), "abc\\u007f");
+ ASSERT_EQ(escape_json_stream("abc\x7f"), "abc\\u007f");
+}
+
+TEST(EscapeJson, Utf8) {
+ EXPECT_EQ(escape_json_attrs("\xe6\xb1\x89\xe5\xad\x97\n"), "\xe6\xb1\x89\xe5\xad\x97\\n");
+ EXPECT_EQ(escape_json_stream("\xe6\xb1\x89\xe5\xad\x97\n"), "\xe6\xb1\x89\xe5\xad\x97\\n");
+}
diff --git a/src/test/exporter/CMakeLists.txt b/src/test/exporter/CMakeLists.txt
new file mode 100644
index 000000000..7ef5631f8
--- /dev/null
+++ b/src/test/exporter/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_executable(unittest_exporter
+ test_exporter.cc
+ "${CMAKE_SOURCE_DIR}/src/exporter/util.cc"
+ "${CMAKE_SOURCE_DIR}/src/exporter/DaemonMetricCollector.cc"
+ )
+
+target_link_libraries(unittest_exporter
+ global
+ ${UNITTEST_LIBS}
+ )
+add_ceph_unittest(unittest_exporter)
+
diff --git a/src/test/exporter/test_exporter.cc b/src/test/exporter/test_exporter.cc
new file mode 100644
index 000000000..b607d1aff
--- /dev/null
+++ b/src/test/exporter/test_exporter.cc
@@ -0,0 +1,694 @@
+#include "gtest/gtest.h"
+#include "exporter/util.h"
+#include "exporter/DaemonMetricCollector.h"
+
+#include <string>
+#include <vector>
+#include <utility>
+
+typedef std::map<std::string, std::string> labels_t;
+
+// 17.2.6's memento mori:
+// This data was gathered from the python implementation of the promethize method
+// where we transform the path of a counter to a valid prometheus name.
+static std::vector<std::pair<std::string, std::string>> promethize_data = {
+ {"bluefs.alloc_slow_fallback", "ceph_bluefs_alloc_slow_fallback"},
+ {"bluefs.alloc_slow_size_fallback", "ceph_bluefs_alloc_slow_size_fallback"},
+ {"bluefs.alloc_unit_db", "ceph_bluefs_alloc_unit_db"},
+ {"bluefs.alloc_unit_main", "ceph_bluefs_alloc_unit_main"},
+ {"bluefs.alloc_unit_wal", "ceph_bluefs_alloc_unit_wal"},
+ {"bluefs.bytes_written_slow", "ceph_bluefs_bytes_written_slow"},
+ {"bluefs.bytes_written_sst", "ceph_bluefs_bytes_written_sst"},
+ {"bluefs.bytes_written_wal", "ceph_bluefs_bytes_written_wal"},
+ {"bluefs.compact_lat_count", "ceph_bluefs_compact_lat_count"},
+ {"bluefs.compact_lat_sum", "ceph_bluefs_compact_lat_sum"},
+ {"bluefs.compact_lock_lat_count", "ceph_bluefs_compact_lock_lat_count"},
+ {"bluefs.compact_lock_lat_sum", "ceph_bluefs_compact_lock_lat_sum"},
+ {"bluefs.db_total_bytes", "ceph_bluefs_db_total_bytes"},
+ {"bluefs.db_used_bytes", "ceph_bluefs_db_used_bytes"},
+ {"bluefs.log_bytes", "ceph_bluefs_log_bytes"},
+ {"bluefs.logged_bytes", "ceph_bluefs_logged_bytes"},
+ {"bluefs.max_bytes_db", "ceph_bluefs_max_bytes_db"},
+ {"bluefs.max_bytes_slow", "ceph_bluefs_max_bytes_slow"},
+ {"bluefs.max_bytes_wal", "ceph_bluefs_max_bytes_wal"},
+ {"bluefs.num_files", "ceph_bluefs_num_files"},
+ {"bluefs.read_bytes", "ceph_bluefs_read_bytes"},
+ {"bluefs.read_count", "ceph_bluefs_read_count"},
+ {"bluefs.read_disk_bytes", "ceph_bluefs_read_disk_bytes"},
+ {"bluefs.read_disk_bytes_db", "ceph_bluefs_read_disk_bytes_db"},
+ {"bluefs.read_disk_bytes_slow", "ceph_bluefs_read_disk_bytes_slow"},
+ {"bluefs.read_disk_bytes_wal", "ceph_bluefs_read_disk_bytes_wal"},
+ {"bluefs.read_disk_count", "ceph_bluefs_read_disk_count"},
+ {"bluefs.read_prefetch_bytes", "ceph_bluefs_read_prefetch_bytes"},
+ {"bluefs.read_prefetch_count", "ceph_bluefs_read_prefetch_count"},
+ {"bluefs.read_random_buffer_bytes", "ceph_bluefs_read_random_buffer_bytes"},
+ {"bluefs.read_random_buffer_count", "ceph_bluefs_read_random_buffer_count"},
+ {"bluefs.read_random_bytes", "ceph_bluefs_read_random_bytes"},
+ {"bluefs.read_random_count", "ceph_bluefs_read_random_count"},
+ {"bluefs.read_random_disk_bytes", "ceph_bluefs_read_random_disk_bytes"},
+ {"bluefs.read_random_disk_bytes_db", "ceph_bluefs_read_random_disk_bytes_db"},
+ {"bluefs.read_random_disk_bytes_slow", "ceph_bluefs_read_random_disk_bytes_slow"},
+ {"bluefs.read_random_disk_bytes_wal", "ceph_bluefs_read_random_disk_bytes_wal"},
+ {"bluefs.read_random_disk_count", "ceph_bluefs_read_random_disk_count"},
+ {"bluefs.slow_total_bytes", "ceph_bluefs_slow_total_bytes"},
+ {"bluefs.slow_used_bytes", "ceph_bluefs_slow_used_bytes"},
+ {"bluefs.wal_total_bytes", "ceph_bluefs_wal_total_bytes"},
+ {"bluefs.wal_used_bytes", "ceph_bluefs_wal_used_bytes"},
+ {"bluestore-pricache.cache_bytes", "ceph_bluestore_pricache_cache_bytes"},
+ {"bluestore-pricache.heap_bytes", "ceph_bluestore_pricache_heap_bytes"},
+ {"bluestore-pricache.mapped_bytes", "ceph_bluestore_pricache_mapped_bytes"},
+ {"bluestore-pricache.target_bytes", "ceph_bluestore_pricache_target_bytes"},
+ {"bluestore-pricache.unmapped_bytes", "ceph_bluestore_pricache_unmapped_bytes"},
+ {"bluestore-pricache:data.committed_bytes", "ceph_bluestore_pricache:data_committed_bytes"},
+ {"bluestore-pricache:data.pri0_bytes", "ceph_bluestore_pricache:data_pri0_bytes"},
+ {"bluestore-pricache:data.pri10_bytes", "ceph_bluestore_pricache:data_pri10_bytes"},
+ {"bluestore-pricache:data.pri11_bytes", "ceph_bluestore_pricache:data_pri11_bytes"},
+ {"bluestore-pricache:data.pri1_bytes", "ceph_bluestore_pricache:data_pri1_bytes"},
+ {"bluestore-pricache:data.pri2_bytes", "ceph_bluestore_pricache:data_pri2_bytes"},
+ {"bluestore-pricache:data.pri3_bytes", "ceph_bluestore_pricache:data_pri3_bytes"},
+ {"bluestore-pricache:data.pri4_bytes", "ceph_bluestore_pricache:data_pri4_bytes"},
+ {"bluestore-pricache:data.pri5_bytes", "ceph_bluestore_pricache:data_pri5_bytes"},
+ {"bluestore-pricache:data.pri6_bytes", "ceph_bluestore_pricache:data_pri6_bytes"},
+ {"bluestore-pricache:data.pri7_bytes", "ceph_bluestore_pricache:data_pri7_bytes"},
+ {"bluestore-pricache:data.pri8_bytes", "ceph_bluestore_pricache:data_pri8_bytes"},
+ {"bluestore-pricache:data.pri9_bytes", "ceph_bluestore_pricache:data_pri9_bytes"},
+ {"bluestore-pricache:data.reserved_bytes", "ceph_bluestore_pricache:data_reserved_bytes"},
+ {"bluestore-pricache:kv.committed_bytes", "ceph_bluestore_pricache:kv_committed_bytes"},
+ {"bluestore-pricache:kv.pri0_bytes", "ceph_bluestore_pricache:kv_pri0_bytes"},
+ {"bluestore-pricache:kv.pri10_bytes", "ceph_bluestore_pricache:kv_pri10_bytes"},
+ {"bluestore-pricache:kv.pri11_bytes", "ceph_bluestore_pricache:kv_pri11_bytes"},
+ {"bluestore-pricache:kv.pri1_bytes", "ceph_bluestore_pricache:kv_pri1_bytes"},
+ {"bluestore-pricache:kv.pri2_bytes", "ceph_bluestore_pricache:kv_pri2_bytes"},
+ {"bluestore-pricache:kv.pri3_bytes", "ceph_bluestore_pricache:kv_pri3_bytes"},
+ {"bluestore-pricache:kv.pri4_bytes", "ceph_bluestore_pricache:kv_pri4_bytes"},
+ {"bluestore-pricache:kv.pri5_bytes", "ceph_bluestore_pricache:kv_pri5_bytes"},
+ {"bluestore-pricache:kv.pri6_bytes", "ceph_bluestore_pricache:kv_pri6_bytes"},
+ {"bluestore-pricache:kv.pri7_bytes", "ceph_bluestore_pricache:kv_pri7_bytes"},
+ {"bluestore-pricache:kv.pri8_bytes", "ceph_bluestore_pricache:kv_pri8_bytes"},
+ {"bluestore-pricache:kv.pri9_bytes", "ceph_bluestore_pricache:kv_pri9_bytes"},
+ {"bluestore-pricache:kv.reserved_bytes", "ceph_bluestore_pricache:kv_reserved_bytes"},
+ {"bluestore-pricache:kv_onode.committed_bytes", "ceph_bluestore_pricache:kv_onode_committed_bytes"},
+ {"bluestore-pricache:kv_onode.pri0_bytes", "ceph_bluestore_pricache:kv_onode_pri0_bytes"},
+ {"bluestore-pricache:kv_onode.pri10_bytes", "ceph_bluestore_pricache:kv_onode_pri10_bytes"},
+ {"bluestore-pricache:kv_onode.pri11_bytes", "ceph_bluestore_pricache:kv_onode_pri11_bytes"},
+ {"bluestore-pricache:kv_onode.pri1_bytes", "ceph_bluestore_pricache:kv_onode_pri1_bytes"},
+ {"bluestore-pricache:kv_onode.pri2_bytes", "ceph_bluestore_pricache:kv_onode_pri2_bytes"},
+ {"bluestore-pricache:kv_onode.pri3_bytes", "ceph_bluestore_pricache:kv_onode_pri3_bytes"},
+ {"bluestore-pricache:kv_onode.pri4_bytes", "ceph_bluestore_pricache:kv_onode_pri4_bytes"},
+ {"bluestore-pricache:kv_onode.pri5_bytes", "ceph_bluestore_pricache:kv_onode_pri5_bytes"},
+ {"bluestore-pricache:kv_onode.pri6_bytes", "ceph_bluestore_pricache:kv_onode_pri6_bytes"},
+ {"bluestore-pricache:kv_onode.pri7_bytes", "ceph_bluestore_pricache:kv_onode_pri7_bytes"},
+ {"bluestore-pricache:kv_onode.pri8_bytes", "ceph_bluestore_pricache:kv_onode_pri8_bytes"},
+ {"bluestore-pricache:kv_onode.pri9_bytes", "ceph_bluestore_pricache:kv_onode_pri9_bytes"},
+ {"bluestore-pricache:kv_onode.reserved_bytes", "ceph_bluestore_pricache:kv_onode_reserved_bytes"},
+ {"bluestore-pricache:meta.committed_bytes", "ceph_bluestore_pricache:meta_committed_bytes"},
+ {"bluestore-pricache:meta.pri0_bytes", "ceph_bluestore_pricache:meta_pri0_bytes"},
+ {"bluestore-pricache:meta.pri10_bytes", "ceph_bluestore_pricache:meta_pri10_bytes"},
+ {"bluestore-pricache:meta.pri11_bytes", "ceph_bluestore_pricache:meta_pri11_bytes"},
+ {"bluestore-pricache:meta.pri1_bytes", "ceph_bluestore_pricache:meta_pri1_bytes"},
+ {"bluestore-pricache:meta.pri2_bytes", "ceph_bluestore_pricache:meta_pri2_bytes"},
+ {"bluestore-pricache:meta.pri3_bytes", "ceph_bluestore_pricache:meta_pri3_bytes"},
+ {"bluestore-pricache:meta.pri4_bytes", "ceph_bluestore_pricache:meta_pri4_bytes"},
+ {"bluestore-pricache:meta.pri5_bytes", "ceph_bluestore_pricache:meta_pri5_bytes"},
+ {"bluestore-pricache:meta.pri6_bytes", "ceph_bluestore_pricache:meta_pri6_bytes"},
+ {"bluestore-pricache:meta.pri7_bytes", "ceph_bluestore_pricache:meta_pri7_bytes"},
+ {"bluestore-pricache:meta.pri8_bytes", "ceph_bluestore_pricache:meta_pri8_bytes"},
+ {"bluestore-pricache:meta.pri9_bytes", "ceph_bluestore_pricache:meta_pri9_bytes"},
+ {"bluestore-pricache:meta.reserved_bytes", "ceph_bluestore_pricache:meta_reserved_bytes"},
+ {"bluestore.alloc_unit", "ceph_bluestore_alloc_unit"},
+ {"bluestore.allocated", "ceph_bluestore_allocated"},
+ {"bluestore.clist_lat_count", "ceph_bluestore_clist_lat_count"},
+ {"bluestore.clist_lat_sum", "ceph_bluestore_clist_lat_sum"},
+ {"bluestore.compress_lat_count", "ceph_bluestore_compress_lat_count"},
+ {"bluestore.compress_lat_sum", "ceph_bluestore_compress_lat_sum"},
+ {"bluestore.compressed", "ceph_bluestore_compressed"},
+ {"bluestore.compressed_allocated", "ceph_bluestore_compressed_allocated"},
+ {"bluestore.compressed_original", "ceph_bluestore_compressed_original"},
+ {"bluestore.csum_lat_count", "ceph_bluestore_csum_lat_count"},
+ {"bluestore.csum_lat_sum", "ceph_bluestore_csum_lat_sum"},
+ {"bluestore.decompress_lat_count", "ceph_bluestore_decompress_lat_count"},
+ {"bluestore.decompress_lat_sum", "ceph_bluestore_decompress_lat_sum"},
+ {"bluestore.kv_commit_lat_count", "ceph_bluestore_kv_commit_lat_count"},
+ {"bluestore.kv_commit_lat_sum", "ceph_bluestore_kv_commit_lat_sum"},
+ {"bluestore.kv_final_lat_count", "ceph_bluestore_kv_final_lat_count"},
+ {"bluestore.kv_final_lat_sum", "ceph_bluestore_kv_final_lat_sum"},
+ {"bluestore.kv_flush_lat_count", "ceph_bluestore_kv_flush_lat_count"},
+ {"bluestore.kv_flush_lat_sum", "ceph_bluestore_kv_flush_lat_sum"},
+ {"bluestore.kv_sync_lat_count", "ceph_bluestore_kv_sync_lat_count"},
+ {"bluestore.kv_sync_lat_sum", "ceph_bluestore_kv_sync_lat_sum"},
+ {"bluestore.omap_get_keys_lat_count", "ceph_bluestore_omap_get_keys_lat_count"},
+ {"bluestore.omap_get_keys_lat_sum", "ceph_bluestore_omap_get_keys_lat_sum"},
+ {"bluestore.omap_get_values_lat_count", "ceph_bluestore_omap_get_values_lat_count"},
+ {"bluestore.omap_get_values_lat_sum", "ceph_bluestore_omap_get_values_lat_sum"},
+ {"bluestore.omap_lower_bound_lat_count", "ceph_bluestore_omap_lower_bound_lat_count"},
+ {"bluestore.omap_lower_bound_lat_sum", "ceph_bluestore_omap_lower_bound_lat_sum"},
+ {"bluestore.omap_next_lat_count", "ceph_bluestore_omap_next_lat_count"},
+ {"bluestore.omap_next_lat_sum", "ceph_bluestore_omap_next_lat_sum"},
+ {"bluestore.omap_seek_to_first_lat_count", "ceph_bluestore_omap_seek_to_first_lat_count"},
+ {"bluestore.omap_seek_to_first_lat_sum", "ceph_bluestore_omap_seek_to_first_lat_sum"},
+ {"bluestore.omap_upper_bound_lat_count", "ceph_bluestore_omap_upper_bound_lat_count"},
+ {"bluestore.omap_upper_bound_lat_sum", "ceph_bluestore_omap_upper_bound_lat_sum"},
+ {"bluestore.onode_hits", "ceph_bluestore_onode_hits"},
+ {"bluestore.onode_misses", "ceph_bluestore_onode_misses"},
+ {"bluestore.read_lat_count", "ceph_bluestore_read_lat_count"},
+ {"bluestore.read_lat_sum", "ceph_bluestore_read_lat_sum"},
+ {"bluestore.read_onode_meta_lat_count", "ceph_bluestore_read_onode_meta_lat_count"},
+ {"bluestore.read_onode_meta_lat_sum", "ceph_bluestore_read_onode_meta_lat_sum"},
+ {"bluestore.read_wait_aio_lat_count", "ceph_bluestore_read_wait_aio_lat_count"},
+ {"bluestore.read_wait_aio_lat_sum", "ceph_bluestore_read_wait_aio_lat_sum"},
+ {"bluestore.reads_with_retries", "ceph_bluestore_reads_with_retries"},
+ {"bluestore.remove_lat_count", "ceph_bluestore_remove_lat_count"},
+ {"bluestore.remove_lat_sum", "ceph_bluestore_remove_lat_sum"},
+ {"bluestore.state_aio_wait_lat_count", "ceph_bluestore_state_aio_wait_lat_count"},
+ {"bluestore.state_aio_wait_lat_sum", "ceph_bluestore_state_aio_wait_lat_sum"},
+ {"bluestore.state_deferred_aio_wait_lat_count", "ceph_bluestore_state_deferred_aio_wait_lat_count"},
+ {"bluestore.state_deferred_aio_wait_lat_sum", "ceph_bluestore_state_deferred_aio_wait_lat_sum"},
+ {"bluestore.state_deferred_cleanup_lat_count", "ceph_bluestore_state_deferred_cleanup_lat_count"},
+ {"bluestore.state_deferred_cleanup_lat_sum", "ceph_bluestore_state_deferred_cleanup_lat_sum"},
+ {"bluestore.state_deferred_queued_lat_count", "ceph_bluestore_state_deferred_queued_lat_count"},
+ {"bluestore.state_deferred_queued_lat_sum", "ceph_bluestore_state_deferred_queued_lat_sum"},
+ {"bluestore.state_done_lat_count", "ceph_bluestore_state_done_lat_count"},
+ {"bluestore.state_done_lat_sum", "ceph_bluestore_state_done_lat_sum"},
+ {"bluestore.state_finishing_lat_count", "ceph_bluestore_state_finishing_lat_count"},
+ {"bluestore.state_finishing_lat_sum", "ceph_bluestore_state_finishing_lat_sum"},
+ {"bluestore.state_io_done_lat_count", "ceph_bluestore_state_io_done_lat_count"},
+ {"bluestore.state_io_done_lat_sum", "ceph_bluestore_state_io_done_lat_sum"},
+ {"bluestore.state_kv_commiting_lat_count", "ceph_bluestore_state_kv_commiting_lat_count"},
+ {"bluestore.state_kv_commiting_lat_sum", "ceph_bluestore_state_kv_commiting_lat_sum"},
+ {"bluestore.state_kv_done_lat_count", "ceph_bluestore_state_kv_done_lat_count"},
+ {"bluestore.state_kv_done_lat_sum", "ceph_bluestore_state_kv_done_lat_sum"},
+ {"bluestore.state_kv_queued_lat_count", "ceph_bluestore_state_kv_queued_lat_count"},
+ {"bluestore.state_kv_queued_lat_sum", "ceph_bluestore_state_kv_queued_lat_sum"},
+ {"bluestore.state_prepare_lat_count", "ceph_bluestore_state_prepare_lat_count"},
+ {"bluestore.state_prepare_lat_sum", "ceph_bluestore_state_prepare_lat_sum"},
+ {"bluestore.stored", "ceph_bluestore_stored"},
+ {"bluestore.truncate_lat_count", "ceph_bluestore_truncate_lat_count"},
+ {"bluestore.truncate_lat_sum", "ceph_bluestore_truncate_lat_sum"},
+ {"bluestore.txc_commit_lat_count", "ceph_bluestore_txc_commit_lat_count"},
+ {"bluestore.txc_commit_lat_sum", "ceph_bluestore_txc_commit_lat_sum"},
+ {"bluestore.txc_submit_lat_count", "ceph_bluestore_txc_submit_lat_count"},
+ {"bluestore.txc_submit_lat_sum", "ceph_bluestore_txc_submit_lat_sum"},
+ {"bluestore.txc_throttle_lat_count", "ceph_bluestore_txc_throttle_lat_count"},
+ {"bluestore.txc_throttle_lat_sum", "ceph_bluestore_txc_throttle_lat_sum"},
+ {"cluster_by_class_total_bytes", "ceph_cluster_by_class_total_bytes"},
+ {"cluster_by_class_total_used_bytes", "ceph_cluster_by_class_total_used_bytes"},
+ {"cluster_by_class_total_used_raw_bytes", "ceph_cluster_by_class_total_used_raw_bytes"},
+ {"cluster_osd_blocklist_count", "ceph_cluster_osd_blocklist_count"},
+ {"cluster_total_bytes", "ceph_cluster_total_bytes"},
+ {"cluster_total_used_bytes", "ceph_cluster_total_used_bytes"},
+ {"cluster_total_used_raw_bytes", "ceph_cluster_total_used_raw_bytes"},
+ {"daemon_health_metrics", "ceph_daemon_health_metrics"},
+ {"disk_occupation", "ceph_disk_occupation"},
+ {"disk_occupation_human", "ceph_disk_occupation_human"},
+ {"fs_metadata", "ceph_fs_metadata"},
+ {"health_detail", "ceph_health_detail"},
+ {"health_status", "ceph_health_status"},
+ {"healthcheck_slow_ops", "ceph_healthcheck_slow_ops"},
+ {"mds.caps", "ceph_mds_caps"},
+ {"mds.ceph_cap_op_flush_ack", "ceph_mds_ceph_cap_op_flush_ack"},
+ {"mds.ceph_cap_op_flushsnap_ack", "ceph_mds_ceph_cap_op_flushsnap_ack"},
+ {"mds.ceph_cap_op_grant", "ceph_mds_ceph_cap_op_grant"},
+ {"mds.ceph_cap_op_revoke", "ceph_mds_ceph_cap_op_revoke"},
+ {"mds.ceph_cap_op_trunc", "ceph_mds_ceph_cap_op_trunc"},
+ {"mds.dir_commit", "ceph_mds_dir_commit"},
+ {"mds.dir_fetch_complete", "ceph_mds_dir_fetch_complete"},
+ {"mds.dir_fetch_keys", "ceph_mds_dir_fetch_keys"},
+ {"mds.dir_merge", "ceph_mds_dir_merge"},
+ {"mds.dir_split", "ceph_mds_dir_split"},
+ {"mds.exported_inodes", "ceph_mds_exported_inodes"},
+ {"mds.forward", "ceph_mds_forward"},
+ {"mds.handle_client_cap_release", "ceph_mds_handle_client_cap_release"},
+ {"mds.handle_client_caps", "ceph_mds_handle_client_caps"},
+ {"mds.handle_client_caps_dirty", "ceph_mds_handle_client_caps_dirty"},
+ {"mds.handle_inode_file_caps", "ceph_mds_handle_inode_file_caps"},
+ {"mds.imported_inodes", "ceph_mds_imported_inodes"},
+ {"mds.inodes", "ceph_mds_inodes"},
+ {"mds.inodes_expired", "ceph_mds_inodes_expired"},
+ {"mds.inodes_pinned", "ceph_mds_inodes_pinned"},
+ {"mds.inodes_with_caps", "ceph_mds_inodes_with_caps"},
+ {"mds.load_cent", "ceph_mds_load_cent"},
+ {"mds.openino_dir_fetch", "ceph_mds_openino_dir_fetch"},
+ {"mds.process_request_cap_release", "ceph_mds_process_request_cap_release"},
+ {"mds.reply_latency_count", "ceph_mds_reply_latency_count"},
+ {"mds.reply_latency_sum", "ceph_mds_reply_latency_sum"},
+ {"mds.request", "ceph_mds_request"},
+ {"mds.root_rbytes", "ceph_mds_root_rbytes"},
+ {"mds.root_rfiles", "ceph_mds_root_rfiles"},
+ {"mds.root_rsnaps", "ceph_mds_root_rsnaps"},
+ {"mds.slow_reply", "ceph_mds_slow_reply"},
+ {"mds.subtrees", "ceph_mds_subtrees"},
+ {"mds_cache.ireq_enqueue_scrub", "ceph_mds_cache_ireq_enqueue_scrub"},
+ {"mds_cache.ireq_exportdir", "ceph_mds_cache_ireq_exportdir"},
+ {"mds_cache.ireq_flush", "ceph_mds_cache_ireq_flush"},
+ {"mds_cache.ireq_fragmentdir", "ceph_mds_cache_ireq_fragmentdir"},
+ {"mds_cache.ireq_fragstats", "ceph_mds_cache_ireq_fragstats"},
+ {"mds_cache.ireq_inodestats", "ceph_mds_cache_ireq_inodestats"},
+ {"mds_cache.num_recovering_enqueued", "ceph_mds_cache_num_recovering_enqueued"},
+ {"mds_cache.num_recovering_prioritized", "ceph_mds_cache_num_recovering_prioritized"},
+ {"mds_cache.num_recovering_processing", "ceph_mds_cache_num_recovering_processing"},
+ {"mds_cache.num_strays", "ceph_mds_cache_num_strays"},
+ {"mds_cache.num_strays_delayed", "ceph_mds_cache_num_strays_delayed"},
+ {"mds_cache.num_strays_enqueuing", "ceph_mds_cache_num_strays_enqueuing"},
+ {"mds_cache.recovery_completed", "ceph_mds_cache_recovery_completed"},
+ {"mds_cache.recovery_started", "ceph_mds_cache_recovery_started"},
+ {"mds_cache.strays_created", "ceph_mds_cache_strays_created"},
+ {"mds_cache.strays_enqueued", "ceph_mds_cache_strays_enqueued"},
+ {"mds_cache.strays_migrated", "ceph_mds_cache_strays_migrated"},
+ {"mds_cache.strays_reintegrated", "ceph_mds_cache_strays_reintegrated"},
+ {"mds_log.ev", "ceph_mds_log_ev"},
+ {"mds_log.evadd", "ceph_mds_log_evadd"},
+ {"mds_log.evex", "ceph_mds_log_evex"},
+ {"mds_log.evexd", "ceph_mds_log_evexd"},
+ {"mds_log.evexg", "ceph_mds_log_evexg"},
+ {"mds_log.evtrm", "ceph_mds_log_evtrm"},
+ {"mds_log.jlat_count", "ceph_mds_log_jlat_count"},
+ {"mds_log.jlat_sum", "ceph_mds_log_jlat_sum"},
+ {"mds_log.replayed", "ceph_mds_log_replayed"},
+ {"mds_log.seg", "ceph_mds_log_seg"},
+ {"mds_log.segadd", "ceph_mds_log_segadd"},
+ {"mds_log.segex", "ceph_mds_log_segex"},
+ {"mds_log.segexd", "ceph_mds_log_segexd"},
+ {"mds_log.segexg", "ceph_mds_log_segexg"},
+ {"mds_log.segtrm", "ceph_mds_log_segtrm"},
+ {"mds_mem.cap", "ceph_mds_mem_cap"},
+ {"mds_mem.cap+", "ceph_mds_mem_cap_plus"},
+ {"mds_mem.cap-", "ceph_mds_mem_cap_minus"},
+ {"mds_mem.dir", "ceph_mds_mem_dir"},
+ {"mds_mem.dir+", "ceph_mds_mem_dir_plus"},
+ {"mds_mem.dir-", "ceph_mds_mem_dir_minus"},
+ {"mds_mem.dn", "ceph_mds_mem_dn"},
+ {"mds_mem.dn+", "ceph_mds_mem_dn_plus"},
+ {"mds_mem.dn-", "ceph_mds_mem_dn_minus"},
+ {"mds_mem.heap", "ceph_mds_mem_heap"},
+ {"mds_mem.ino", "ceph_mds_mem_ino"},
+ {"mds_mem.ino+", "ceph_mds_mem_ino_plus"},
+ {"mds_mem.ino-", "ceph_mds_mem_ino_minus"},
+ {"mds_metadata", "ceph_mds_metadata"},
+ {"mds_server.cap_acquisition_throttle", "ceph_mds_server_cap_acquisition_throttle"},
+ {"mds_server.cap_revoke_eviction", "ceph_mds_server_cap_revoke_eviction"},
+ {"mds_server.handle_client_request", "ceph_mds_server_handle_client_request"},
+ {"mds_server.handle_client_session", "ceph_mds_server_handle_client_session"},
+ {"mds_server.handle_peer_request", "ceph_mds_server_handle_peer_request"},
+ {"mds_server.req_create_latency_count", "ceph_mds_server_req_create_latency_count"},
+ {"mds_server.req_create_latency_sum", "ceph_mds_server_req_create_latency_sum"},
+ {"mds_server.req_getattr_latency_count", "ceph_mds_server_req_getattr_latency_count"},
+ {"mds_server.req_getattr_latency_sum", "ceph_mds_server_req_getattr_latency_sum"},
+ {"mds_server.req_getfilelock_latency_count", "ceph_mds_server_req_getfilelock_latency_count"},
+ {"mds_server.req_getfilelock_latency_sum", "ceph_mds_server_req_getfilelock_latency_sum"},
+ {"mds_server.req_getvxattr_latency_count", "ceph_mds_server_req_getvxattr_latency_count"},
+ {"mds_server.req_getvxattr_latency_sum", "ceph_mds_server_req_getvxattr_latency_sum"},
+ {"mds_server.req_link_latency_count", "ceph_mds_server_req_link_latency_count"},
+ {"mds_server.req_link_latency_sum", "ceph_mds_server_req_link_latency_sum"},
+ {"mds_server.req_lookup_latency_count", "ceph_mds_server_req_lookup_latency_count"},
+ {"mds_server.req_lookup_latency_sum", "ceph_mds_server_req_lookup_latency_sum"},
+ {"mds_server.req_lookuphash_latency_count", "ceph_mds_server_req_lookuphash_latency_count"},
+ {"mds_server.req_lookuphash_latency_sum", "ceph_mds_server_req_lookuphash_latency_sum"},
+ {"mds_server.req_lookupino_latency_count", "ceph_mds_server_req_lookupino_latency_count"},
+ {"mds_server.req_lookupino_latency_sum", "ceph_mds_server_req_lookupino_latency_sum"},
+ {"mds_server.req_lookupname_latency_count", "ceph_mds_server_req_lookupname_latency_count"},
+ {"mds_server.req_lookupname_latency_sum", "ceph_mds_server_req_lookupname_latency_sum"},
+ {"mds_server.req_lookupparent_latency_count", "ceph_mds_server_req_lookupparent_latency_count"},
+ {"mds_server.req_lookupparent_latency_sum", "ceph_mds_server_req_lookupparent_latency_sum"},
+ {"mds_server.req_lookupsnap_latency_count", "ceph_mds_server_req_lookupsnap_latency_count"},
+ {"mds_server.req_lookupsnap_latency_sum", "ceph_mds_server_req_lookupsnap_latency_sum"},
+ {"mds_server.req_lssnap_latency_count", "ceph_mds_server_req_lssnap_latency_count"},
+ {"mds_server.req_lssnap_latency_sum", "ceph_mds_server_req_lssnap_latency_sum"},
+ {"mds_server.req_mkdir_latency_count", "ceph_mds_server_req_mkdir_latency_count"},
+ {"mds_server.req_mkdir_latency_sum", "ceph_mds_server_req_mkdir_latency_sum"},
+ {"mds_server.req_mknod_latency_count", "ceph_mds_server_req_mknod_latency_count"},
+ {"mds_server.req_mknod_latency_sum", "ceph_mds_server_req_mknod_latency_sum"},
+ {"mds_server.req_mksnap_latency_count", "ceph_mds_server_req_mksnap_latency_count"},
+ {"mds_server.req_mksnap_latency_sum", "ceph_mds_server_req_mksnap_latency_sum"},
+ {"mds_server.req_open_latency_count", "ceph_mds_server_req_open_latency_count"},
+ {"mds_server.req_open_latency_sum", "ceph_mds_server_req_open_latency_sum"},
+ {"mds_server.req_readdir_latency_count", "ceph_mds_server_req_readdir_latency_count"},
+ {"mds_server.req_readdir_latency_sum", "ceph_mds_server_req_readdir_latency_sum"},
+ {"mds_server.req_rename_latency_count", "ceph_mds_server_req_rename_latency_count"},
+ {"mds_server.req_rename_latency_sum", "ceph_mds_server_req_rename_latency_sum"},
+ {"mds_server.req_renamesnap_latency_count", "ceph_mds_server_req_renamesnap_latency_count"},
+ {"mds_server.req_renamesnap_latency_sum", "ceph_mds_server_req_renamesnap_latency_sum"},
+ {"mds_server.req_rmdir_latency_count", "ceph_mds_server_req_rmdir_latency_count"},
+ {"mds_server.req_rmdir_latency_sum", "ceph_mds_server_req_rmdir_latency_sum"},
+ {"mds_server.req_rmsnap_latency_count", "ceph_mds_server_req_rmsnap_latency_count"},
+ {"mds_server.req_rmsnap_latency_sum", "ceph_mds_server_req_rmsnap_latency_sum"},
+ {"mds_server.req_rmxattr_latency_count", "ceph_mds_server_req_rmxattr_latency_count"},
+ {"mds_server.req_rmxattr_latency_sum", "ceph_mds_server_req_rmxattr_latency_sum"},
+ {"mds_server.req_setattr_latency_count", "ceph_mds_server_req_setattr_latency_count"},
+ {"mds_server.req_setattr_latency_sum", "ceph_mds_server_req_setattr_latency_sum"},
+ {"mds_server.req_setdirlayout_latency_count", "ceph_mds_server_req_setdirlayout_latency_count"},
+ {"mds_server.req_setdirlayout_latency_sum", "ceph_mds_server_req_setdirlayout_latency_sum"},
+ {"mds_server.req_setfilelock_latency_count", "ceph_mds_server_req_setfilelock_latency_count"},
+ {"mds_server.req_setfilelock_latency_sum", "ceph_mds_server_req_setfilelock_latency_sum"},
+ {"mds_server.req_setlayout_latency_count", "ceph_mds_server_req_setlayout_latency_count"},
+ {"mds_server.req_setlayout_latency_sum", "ceph_mds_server_req_setlayout_latency_sum"},
+ {"mds_server.req_setxattr_latency_count", "ceph_mds_server_req_setxattr_latency_count"},
+ {"mds_server.req_setxattr_latency_sum", "ceph_mds_server_req_setxattr_latency_sum"},
+ {"mds_server.req_symlink_latency_count", "ceph_mds_server_req_symlink_latency_count"},
+ {"mds_server.req_symlink_latency_sum", "ceph_mds_server_req_symlink_latency_sum"},
+ {"mds_server.req_unlink_latency_count", "ceph_mds_server_req_unlink_latency_count"},
+ {"mds_server.req_unlink_latency_sum", "ceph_mds_server_req_unlink_latency_sum"},
+ {"mds_sessions.average_load", "ceph_mds_sessions_average_load"},
+ {"mds_sessions.avg_session_uptime", "ceph_mds_sessions_avg_session_uptime"},
+ {"mds_sessions.session_add", "ceph_mds_sessions_session_add"},
+ {"mds_sessions.session_count", "ceph_mds_sessions_session_count"},
+ {"mds_sessions.session_remove", "ceph_mds_sessions_session_remove"},
+ {"mds_sessions.sessions_open", "ceph_mds_sessions_sessions_open"},
+ {"mds_sessions.sessions_stale", "ceph_mds_sessions_sessions_stale"},
+ {"mds_sessions.total_load", "ceph_mds_sessions_total_load"},
+ {"mgr_metadata", "ceph_mgr_metadata"},
+ {"mgr_module_can_run", "ceph_mgr_module_can_run"},
+ {"mgr_module_status", "ceph_mgr_module_status"},
+ {"mgr_status", "ceph_mgr_status"},
+ {"mon.election_call", "ceph_mon_election_call"},
+ {"mon.election_lose", "ceph_mon_election_lose"},
+ {"mon.election_win", "ceph_mon_election_win"},
+ {"mon.num_elections", "ceph_mon_num_elections"},
+ {"mon.num_sessions", "ceph_mon_num_sessions"},
+ {"mon.session_add", "ceph_mon_session_add"},
+ {"mon.session_rm", "ceph_mon_session_rm"},
+ {"mon.session_trim", "ceph_mon_session_trim"},
+ {"mon_metadata", "ceph_mon_metadata"},
+ {"mon_quorum_status", "ceph_mon_quorum_status"},
+ {"num_objects_degraded", "ceph_num_objects_degraded"},
+ {"num_objects_misplaced", "ceph_num_objects_misplaced"},
+ {"num_objects_unfound", "ceph_num_objects_unfound"},
+ {"objecter-0x5591781656c0.op_active", "ceph_objecter_0x5591781656c0_op_active"},
+ {"objecter-0x5591781656c0.op_r", "ceph_objecter_0x5591781656c0_op_r"},
+ {"objecter-0x5591781656c0.op_rmw", "ceph_objecter_0x5591781656c0_op_rmw"},
+ {"objecter-0x5591781656c0.op_w", "ceph_objecter_0x5591781656c0_op_w"},
+ {"objecter-0x559178165930.op_active", "ceph_objecter_0x559178165930_op_active"},
+ {"objecter-0x559178165930.op_r", "ceph_objecter_0x559178165930_op_r"},
+ {"objecter-0x559178165930.op_rmw", "ceph_objecter_0x559178165930_op_rmw"},
+ {"objecter-0x559178165930.op_w", "ceph_objecter_0x559178165930_op_w"},
+ {"objecter.op_active", "ceph_objecter_op_active"},
+ {"objecter.op_r", "ceph_objecter_op_r"},
+ {"objecter.op_rmw", "ceph_objecter_op_rmw"},
+ {"objecter.op_w", "ceph_objecter_op_w"},
+ {"osd.numpg", "ceph_osd_numpg"},
+ {"osd.numpg_removing", "ceph_osd_numpg_removing"},
+ {"osd.op", "ceph_osd_op"},
+ {"osd.op_in_bytes", "ceph_osd_op_in_bytes"},
+ {"osd.op_latency_count", "ceph_osd_op_latency_count"},
+ {"osd.op_latency_sum", "ceph_osd_op_latency_sum"},
+ {"osd.op_out_bytes", "ceph_osd_op_out_bytes"},
+ {"osd.op_prepare_latency_count", "ceph_osd_op_prepare_latency_count"},
+ {"osd.op_prepare_latency_sum", "ceph_osd_op_prepare_latency_sum"},
+ {"osd.op_process_latency_count", "ceph_osd_op_process_latency_count"},
+ {"osd.op_process_latency_sum", "ceph_osd_op_process_latency_sum"},
+ {"osd.op_r", "ceph_osd_op_r"},
+ {"osd.op_r_latency_count", "ceph_osd_op_r_latency_count"},
+ {"osd.op_r_latency_sum", "ceph_osd_op_r_latency_sum"},
+ {"osd.op_r_out_bytes", "ceph_osd_op_r_out_bytes"},
+ {"osd.op_r_prepare_latency_count", "ceph_osd_op_r_prepare_latency_count"},
+ {"osd.op_r_prepare_latency_sum", "ceph_osd_op_r_prepare_latency_sum"},
+ {"osd.op_r_process_latency_count", "ceph_osd_op_r_process_latency_count"},
+ {"osd.op_r_process_latency_sum", "ceph_osd_op_r_process_latency_sum"},
+ {"osd.op_rw", "ceph_osd_op_rw"},
+ {"osd.op_rw_in_bytes", "ceph_osd_op_rw_in_bytes"},
+ {"osd.op_rw_latency_count", "ceph_osd_op_rw_latency_count"},
+ {"osd.op_rw_latency_sum", "ceph_osd_op_rw_latency_sum"},
+ {"osd.op_rw_out_bytes", "ceph_osd_op_rw_out_bytes"},
+ {"osd.op_rw_prepare_latency_count", "ceph_osd_op_rw_prepare_latency_count"},
+ {"osd.op_rw_prepare_latency_sum", "ceph_osd_op_rw_prepare_latency_sum"},
+ {"osd.op_rw_process_latency_count", "ceph_osd_op_rw_process_latency_count"},
+ {"osd.op_rw_process_latency_sum", "ceph_osd_op_rw_process_latency_sum"},
+ {"osd.op_w", "ceph_osd_op_w"},
+ {"osd.op_w_in_bytes", "ceph_osd_op_w_in_bytes"},
+ {"osd.op_w_latency_count", "ceph_osd_op_w_latency_count"},
+ {"osd.op_w_latency_sum", "ceph_osd_op_w_latency_sum"},
+ {"osd.op_w_prepare_latency_count", "ceph_osd_op_w_prepare_latency_count"},
+ {"osd.op_w_prepare_latency_sum", "ceph_osd_op_w_prepare_latency_sum"},
+ {"osd.op_w_process_latency_count", "ceph_osd_op_w_process_latency_count"},
+ {"osd.op_w_process_latency_sum", "ceph_osd_op_w_process_latency_sum"},
+ {"osd.op_wip", "ceph_osd_op_wip"},
+ {"osd.recovery_bytes", "ceph_osd_recovery_bytes"},
+ {"osd.recovery_ops", "ceph_osd_recovery_ops"},
+ {"osd.stat_bytes", "ceph_osd_stat_bytes"},
+ {"osd.stat_bytes_used", "ceph_osd_stat_bytes_used"},
+ {"osd_apply_latency_ms", "ceph_osd_apply_latency_ms"},
+ {"osd_commit_latency_ms", "ceph_osd_commit_latency_ms"},
+ {"osd_flag_nobackfill", "ceph_osd_flag_nobackfill"},
+ {"osd_flag_nodeep-scrub", "ceph_osd_flag_nodeep_scrub"},
+ {"osd_flag_nodown", "ceph_osd_flag_nodown"},
+ {"osd_flag_noin", "ceph_osd_flag_noin"},
+ {"osd_flag_noout", "ceph_osd_flag_noout"},
+ {"osd_flag_norebalance", "ceph_osd_flag_norebalance"},
+ {"osd_flag_norecover", "ceph_osd_flag_norecover"},
+ {"osd_flag_noscrub", "ceph_osd_flag_noscrub"},
+ {"osd_flag_noup", "ceph_osd_flag_noup"},
+ {"osd_in", "ceph_osd_in"},
+ {"osd_metadata", "ceph_osd_metadata"},
+ {"osd_up", "ceph_osd_up"},
+ {"osd_weight", "ceph_osd_weight"},
+ {"paxos.accept_timeout", "ceph_paxos_accept_timeout"},
+ {"paxos.begin", "ceph_paxos_begin"},
+ {"paxos.begin_bytes_count", "ceph_paxos_begin_bytes_count"},
+ {"paxos.begin_bytes_sum", "ceph_paxos_begin_bytes_sum"},
+ {"paxos.begin_keys_count", "ceph_paxos_begin_keys_count"},
+ {"paxos.begin_keys_sum", "ceph_paxos_begin_keys_sum"},
+ {"paxos.begin_latency_count", "ceph_paxos_begin_latency_count"},
+ {"paxos.begin_latency_sum", "ceph_paxos_begin_latency_sum"},
+ {"paxos.collect", "ceph_paxos_collect"},
+ {"paxos.collect_bytes_count", "ceph_paxos_collect_bytes_count"},
+ {"paxos.collect_bytes_sum", "ceph_paxos_collect_bytes_sum"},
+ {"paxos.collect_keys_count", "ceph_paxos_collect_keys_count"},
+ {"paxos.collect_keys_sum", "ceph_paxos_collect_keys_sum"},
+ {"paxos.collect_latency_count", "ceph_paxos_collect_latency_count"},
+ {"paxos.collect_latency_sum", "ceph_paxos_collect_latency_sum"},
+ {"paxos.collect_timeout", "ceph_paxos_collect_timeout"},
+ {"paxos.collect_uncommitted", "ceph_paxos_collect_uncommitted"},
+ {"paxos.commit", "ceph_paxos_commit"},
+ {"paxos.commit_bytes_count", "ceph_paxos_commit_bytes_count"},
+ {"paxos.commit_bytes_sum", "ceph_paxos_commit_bytes_sum"},
+ {"paxos.commit_keys_count", "ceph_paxos_commit_keys_count"},
+ {"paxos.commit_keys_sum", "ceph_paxos_commit_keys_sum"},
+ {"paxos.commit_latency_count", "ceph_paxos_commit_latency_count"},
+ {"paxos.commit_latency_sum", "ceph_paxos_commit_latency_sum"},
+ {"paxos.lease_ack_timeout", "ceph_paxos_lease_ack_timeout"},
+ {"paxos.lease_timeout", "ceph_paxos_lease_timeout"},
+ {"paxos.new_pn", "ceph_paxos_new_pn"},
+ {"paxos.new_pn_latency_count", "ceph_paxos_new_pn_latency_count"},
+ {"paxos.new_pn_latency_sum", "ceph_paxos_new_pn_latency_sum"},
+ {"paxos.refresh", "ceph_paxos_refresh"},
+ {"paxos.refresh_latency_count", "ceph_paxos_refresh_latency_count"},
+ {"paxos.refresh_latency_sum", "ceph_paxos_refresh_latency_sum"},
+ {"paxos.restart", "ceph_paxos_restart"},
+ {"paxos.share_state", "ceph_paxos_share_state"},
+ {"paxos.share_state_bytes_count", "ceph_paxos_share_state_bytes_count"},
+ {"paxos.share_state_bytes_sum", "ceph_paxos_share_state_bytes_sum"},
+ {"paxos.share_state_keys_count", "ceph_paxos_share_state_keys_count"},
+ {"paxos.share_state_keys_sum", "ceph_paxos_share_state_keys_sum"},
+ {"paxos.start_leader", "ceph_paxos_start_leader"},
+ {"paxos.start_peon", "ceph_paxos_start_peon"},
+ {"paxos.store_state", "ceph_paxos_store_state"},
+ {"paxos.store_state_bytes_count", "ceph_paxos_store_state_bytes_count"},
+ {"paxos.store_state_bytes_sum", "ceph_paxos_store_state_bytes_sum"},
+ {"paxos.store_state_keys_count", "ceph_paxos_store_state_keys_count"},
+ {"paxos.store_state_keys_sum", "ceph_paxos_store_state_keys_sum"},
+ {"paxos.store_state_latency_count", "ceph_paxos_store_state_latency_count"},
+ {"paxos.store_state_latency_sum", "ceph_paxos_store_state_latency_sum"},
+ {"pg_activating", "ceph_pg_activating"},
+ {"pg_active", "ceph_pg_active"},
+ {"pg_backfill_toofull", "ceph_pg_backfill_toofull"},
+ {"pg_backfill_unfound", "ceph_pg_backfill_unfound"},
+ {"pg_backfill_wait", "ceph_pg_backfill_wait"},
+ {"pg_backfilling", "ceph_pg_backfilling"},
+ {"pg_clean", "ceph_pg_clean"},
+ {"pg_creating", "ceph_pg_creating"},
+ {"pg_deep", "ceph_pg_deep"},
+ {"pg_degraded", "ceph_pg_degraded"},
+ {"pg_down", "ceph_pg_down"},
+ {"pg_failed_repair", "ceph_pg_failed_repair"},
+ {"pg_forced_backfill", "ceph_pg_forced_backfill"},
+ {"pg_forced_recovery", "ceph_pg_forced_recovery"},
+ {"pg_incomplete", "ceph_pg_incomplete"},
+ {"pg_inconsistent", "ceph_pg_inconsistent"},
+ {"pg_laggy", "ceph_pg_laggy"},
+ {"pg_peered", "ceph_pg_peered"},
+ {"pg_peering", "ceph_pg_peering"},
+ {"pg_premerge", "ceph_pg_premerge"},
+ {"pg_recovering", "ceph_pg_recovering"},
+ {"pg_recovery_toofull", "ceph_pg_recovery_toofull"},
+ {"pg_recovery_unfound", "ceph_pg_recovery_unfound"},
+ {"pg_recovery_wait", "ceph_pg_recovery_wait"},
+ {"pg_remapped", "ceph_pg_remapped"},
+ {"pg_repair", "ceph_pg_repair"},
+ {"pg_scrubbing", "ceph_pg_scrubbing"},
+ {"pg_snaptrim", "ceph_pg_snaptrim"},
+ {"pg_snaptrim_error", "ceph_pg_snaptrim_error"},
+ {"pg_snaptrim_wait", "ceph_pg_snaptrim_wait"},
+ {"pg_stale", "ceph_pg_stale"},
+ {"pg_total", "ceph_pg_total"},
+ {"pg_undersized", "ceph_pg_undersized"},
+ {"pg_unknown", "ceph_pg_unknown"},
+ {"pg_wait", "ceph_pg_wait"},
+ {"pool_avail_raw", "ceph_pool_avail_raw"},
+ {"pool_bytes_used", "ceph_pool_bytes_used"},
+ {"pool_compress_bytes_used", "ceph_pool_compress_bytes_used"},
+ {"pool_compress_under_bytes", "ceph_pool_compress_under_bytes"},
+ {"pool_dirty", "ceph_pool_dirty"},
+ {"pool_max_avail", "ceph_pool_max_avail"},
+ {"pool_metadata", "ceph_pool_metadata"},
+ {"pool_num_bytes_recovered", "ceph_pool_num_bytes_recovered"},
+ {"pool_num_objects_recovered", "ceph_pool_num_objects_recovered"},
+ {"pool_objects", "ceph_pool_objects"},
+ {"pool_objects_repaired", "ceph_pool_objects_repaired"},
+ {"pool_percent_used", "ceph_pool_percent_used"},
+ {"pool_quota_bytes", "ceph_pool_quota_bytes"},
+ {"pool_quota_objects", "ceph_pool_quota_objects"},
+ {"pool_rd", "ceph_pool_rd"},
+ {"pool_rd_bytes", "ceph_pool_rd_bytes"},
+ {"pool_recovering_bytes_per_sec", "ceph_pool_recovering_bytes_per_sec"},
+ {"pool_recovering_keys_per_sec", "ceph_pool_recovering_keys_per_sec"},
+ {"pool_recovering_objects_per_sec", "ceph_pool_recovering_objects_per_sec"},
+ {"pool_stored", "ceph_pool_stored"},
+ {"pool_stored_raw", "ceph_pool_stored_raw"},
+ {"pool_wr", "ceph_pool_wr"},
+ {"pool_wr_bytes", "ceph_pool_wr_bytes"},
+ {"prioritycache.cache_bytes", "ceph_prioritycache_cache_bytes"},
+ {"prioritycache.heap_bytes", "ceph_prioritycache_heap_bytes"},
+ {"prioritycache.mapped_bytes", "ceph_prioritycache_mapped_bytes"},
+ {"prioritycache.target_bytes", "ceph_prioritycache_target_bytes"},
+ {"prioritycache.unmapped_bytes", "ceph_prioritycache_unmapped_bytes"},
+ {"prioritycache:full.committed_bytes", "ceph_prioritycache:full_committed_bytes"},
+ {"prioritycache:full.pri0_bytes", "ceph_prioritycache:full_pri0_bytes"},
+ {"prioritycache:full.pri10_bytes", "ceph_prioritycache:full_pri10_bytes"},
+ {"prioritycache:full.pri11_bytes", "ceph_prioritycache:full_pri11_bytes"},
+ {"prioritycache:full.pri1_bytes", "ceph_prioritycache:full_pri1_bytes"},
+ {"prioritycache:full.pri2_bytes", "ceph_prioritycache:full_pri2_bytes"},
+ {"prioritycache:full.pri3_bytes", "ceph_prioritycache:full_pri3_bytes"},
+ {"prioritycache:full.pri4_bytes", "ceph_prioritycache:full_pri4_bytes"},
+ {"prioritycache:full.pri5_bytes", "ceph_prioritycache:full_pri5_bytes"},
+ {"prioritycache:full.pri6_bytes", "ceph_prioritycache:full_pri6_bytes"},
+ {"prioritycache:full.pri7_bytes", "ceph_prioritycache:full_pri7_bytes"},
+ {"prioritycache:full.pri8_bytes", "ceph_prioritycache:full_pri8_bytes"},
+ {"prioritycache:full.pri9_bytes", "ceph_prioritycache:full_pri9_bytes"},
+ {"prioritycache:full.reserved_bytes", "ceph_prioritycache:full_reserved_bytes"},
+ {"prioritycache:inc.committed_bytes", "ceph_prioritycache:inc_committed_bytes"},
+ {"prioritycache:inc.pri0_bytes", "ceph_prioritycache:inc_pri0_bytes"},
+ {"prioritycache:inc.pri10_bytes", "ceph_prioritycache:inc_pri10_bytes"},
+ {"prioritycache:inc.pri11_bytes", "ceph_prioritycache:inc_pri11_bytes"},
+ {"prioritycache:inc.pri1_bytes", "ceph_prioritycache:inc_pri1_bytes"},
+ {"prioritycache:inc.pri2_bytes", "ceph_prioritycache:inc_pri2_bytes"},
+ {"prioritycache:inc.pri3_bytes", "ceph_prioritycache:inc_pri3_bytes"},
+ {"prioritycache:inc.pri4_bytes", "ceph_prioritycache:inc_pri4_bytes"},
+ {"prioritycache:inc.pri5_bytes", "ceph_prioritycache:inc_pri5_bytes"},
+ {"prioritycache:inc.pri6_bytes", "ceph_prioritycache:inc_pri6_bytes"},
+ {"prioritycache:inc.pri7_bytes", "ceph_prioritycache:inc_pri7_bytes"},
+ {"prioritycache:inc.pri8_bytes", "ceph_prioritycache:inc_pri8_bytes"},
+ {"prioritycache:inc.pri9_bytes", "ceph_prioritycache:inc_pri9_bytes"},
+ {"prioritycache:inc.reserved_bytes", "ceph_prioritycache:inc_reserved_bytes"},
+ {"prioritycache:kv.committed_bytes", "ceph_prioritycache:kv_committed_bytes"},
+ {"prioritycache:kv.pri0_bytes", "ceph_prioritycache:kv_pri0_bytes"},
+ {"prioritycache:kv.pri10_bytes", "ceph_prioritycache:kv_pri10_bytes"},
+ {"prioritycache:kv.pri11_bytes", "ceph_prioritycache:kv_pri11_bytes"},
+ {"prioritycache:kv.pri1_bytes", "ceph_prioritycache:kv_pri1_bytes"},
+ {"prioritycache:kv.pri2_bytes", "ceph_prioritycache:kv_pri2_bytes"},
+ {"prioritycache:kv.pri3_bytes", "ceph_prioritycache:kv_pri3_bytes"},
+ {"prioritycache:kv.pri4_bytes", "ceph_prioritycache:kv_pri4_bytes"},
+ {"prioritycache:kv.pri5_bytes", "ceph_prioritycache:kv_pri5_bytes"},
+ {"prioritycache:kv.pri6_bytes", "ceph_prioritycache:kv_pri6_bytes"},
+ {"prioritycache:kv.pri7_bytes", "ceph_prioritycache:kv_pri7_bytes"},
+ {"prioritycache:kv.pri8_bytes", "ceph_prioritycache:kv_pri8_bytes"},
+ {"prioritycache:kv.pri9_bytes", "ceph_prioritycache:kv_pri9_bytes"},
+ {"prioritycache:kv.reserved_bytes", "ceph_prioritycache:kv_reserved_bytes"},
+ {"prometheus_collect_duration_seconds_count", "ceph_prometheus_collect_duration_seconds_count"},
+ {"prometheus_collect_duration_seconds_sum", "ceph_prometheus_collect_duration_seconds_sum"},
+ {"purge_queue.pq_executed", "ceph_purge_queue_pq_executed"},
+ {"purge_queue.pq_executing", "ceph_purge_queue_pq_executing"},
+ {"purge_queue.pq_executing_high_water", "ceph_purge_queue_pq_executing_high_water"},
+ {"purge_queue.pq_executing_ops", "ceph_purge_queue_pq_executing_ops"},
+ {"purge_queue.pq_executing_ops_high_water", "ceph_purge_queue_pq_executing_ops_high_water"},
+ {"purge_queue.pq_item_in_journal", "ceph_purge_queue_pq_item_in_journal"},
+ {"rbd_mirror_metadata", "ceph_rbd_mirror_metadata"},
+ {"rgw.cache_hit", "ceph_rgw_cache_hit"},
+ {"rgw.cache_miss", "ceph_rgw_cache_miss"},
+ {"rgw.failed_req", "ceph_rgw_failed_req"},
+ {"rgw.gc_retire_object", "ceph_rgw_gc_retire_object"},
+ {"rgw.get", "ceph_rgw_get"},
+ {"rgw.get_b", "ceph_rgw_get_b"},
+ {"rgw.get_initial_lat_count", "ceph_rgw_get_initial_lat_count"},
+ {"rgw.get_initial_lat_sum", "ceph_rgw_get_initial_lat_sum"},
+ {"rgw.keystone_token_cache_hit", "ceph_rgw_keystone_token_cache_hit"},
+ {"rgw.keystone_token_cache_miss", "ceph_rgw_keystone_token_cache_miss"},
+ {"rgw.lc_abort_mpu", "ceph_rgw_lc_abort_mpu"},
+ {"rgw.lc_expire_current", "ceph_rgw_lc_expire_current"},
+ {"rgw.lc_expire_dm", "ceph_rgw_lc_expire_dm"},
+ {"rgw.lc_expire_noncurrent", "ceph_rgw_lc_expire_noncurrent"},
+ {"rgw.lc_transition_current", "ceph_rgw_lc_transition_current"},
+ {"rgw.lc_transition_noncurrent", "ceph_rgw_lc_transition_noncurrent"},
+ {"rgw.lua_current_vms", "ceph_rgw_lua_current_vms"},
+ {"rgw.lua_script_fail", "ceph_rgw_lua_script_fail"},
+ {"rgw.lua_script_ok", "ceph_rgw_lua_script_ok"},
+ {"rgw.pubsub_event_lost", "ceph_rgw_pubsub_event_lost"},
+ {"rgw.pubsub_event_triggered", "ceph_rgw_pubsub_event_triggered"},
+ {"rgw.pubsub_events", "ceph_rgw_pubsub_events"},
+ {"rgw.pubsub_missing_conf", "ceph_rgw_pubsub_missing_conf"},
+ {"rgw.pubsub_push_failed", "ceph_rgw_pubsub_push_failed"},
+ {"rgw.pubsub_push_ok", "ceph_rgw_pubsub_push_ok"},
+ {"rgw.pubsub_push_pending", "ceph_rgw_pubsub_push_pending"},
+ {"rgw.pubsub_store_fail", "ceph_rgw_pubsub_store_fail"},
+ {"rgw.pubsub_store_ok", "ceph_rgw_pubsub_store_ok"},
+ {"rgw.put", "ceph_rgw_put"},
+ {"rgw.put_b", "ceph_rgw_put_b"},
+ {"rgw.put_initial_lat_count", "ceph_rgw_put_initial_lat_count"},
+ {"rgw.put_initial_lat_sum", "ceph_rgw_put_initial_lat_sum"},
+ {"rgw.qactive", "ceph_rgw_qactive"},
+ {"rgw.qlen", "ceph_rgw_qlen"},
+ {"rgw.req", "ceph_rgw_req"},
+ {"rgw_metadata", "ceph_rgw_metadata"},
+ {"rocksdb.compact", "ceph_rocksdb_compact"},
+ {"rocksdb.compact_queue_len", "ceph_rocksdb_compact_queue_len"},
+ {"rocksdb.compact_queue_merge", "ceph_rocksdb_compact_queue_merge"},
+ {"rocksdb.compact_range", "ceph_rocksdb_compact_range"},
+ {"rocksdb.get_latency_count", "ceph_rocksdb_get_latency_count"},
+ {"rocksdb.get_latency_sum", "ceph_rocksdb_get_latency_sum"},
+ {"rocksdb.rocksdb_write_delay_time_count", "ceph_rocksdb_rocksdb_write_delay_time_count"},
+ {"rocksdb.rocksdb_write_delay_time_sum", "ceph_rocksdb_rocksdb_write_delay_time_sum"},
+ {"rocksdb.rocksdb_write_memtable_time_count", "ceph_rocksdb_rocksdb_write_memtable_time_count"},
+ {"rocksdb.rocksdb_write_memtable_time_sum", "ceph_rocksdb_rocksdb_write_memtable_time_sum"},
+ {"rocksdb.rocksdb_write_pre_and_post_time_count", "ceph_rocksdb_rocksdb_write_pre_and_post_time_count"},
+ {"rocksdb.rocksdb_write_pre_and_post_time_sum", "ceph_rocksdb_rocksdb_write_pre_and_post_time_sum"},
+ {"rocksdb.rocksdb_write_wal_time_count", "ceph_rocksdb_rocksdb_write_wal_time_count"},
+ {"rocksdb.rocksdb_write_wal_time_sum", "ceph_rocksdb_rocksdb_write_wal_time_sum"},
+ {"rocksdb.submit_latency_count", "ceph_rocksdb_submit_latency_count"},
+ {"rocksdb.submit_latency_sum", "ceph_rocksdb_submit_latency_sum"},
+ {"rocksdb.submit_sync_latency_count", "ceph_rocksdb_submit_sync_latency_count"},
+ {"rocksdb.submit_sync_latency_sum", "ceph_rocksdb_submit_sync_latency_sum"}
+};
+
+TEST(Exporter, promethize) {
+ for (auto &test_case : promethize_data) {
+ std::string path = test_case.first;
+ promethize(path);
+ ASSERT_EQ(path, test_case.second);
+ }
+}
+
+TEST(Exporter, check_labels_and_metric_name) {
+ static std::vector<std::pair<std::string, std::string>> counters_data;
+ counters_data.emplace_back("ceph-osd.0", "ceph_osd_numpg");
+ counters_data.emplace_back("ceph-client.rgw.foo.ceph-node-00.hrgsea.2.94739968030880", "ceph_rgw_get");
+
+ static std::vector<labels_t> labels_vec;
+ labels_vec.emplace_back(labels_t{{"ceph_daemon", "\"osd.0\""}});
+ labels_vec.emplace_back(labels_t{{"instance_id", "\"hrgsea\""}});
+ auto counter_data_itr = counters_data.begin();
+ auto labels_vec_itr = labels_vec.begin();
+ for (; counter_data_itr != counters_data.end() && labels_vec_itr != labels_vec.end();
+ ++counter_data_itr, ++labels_vec_itr) {
+ std::string daemon_name = counter_data_itr->first;
+ std::string counter_name = counter_data_itr->second;
+ DaemonMetricCollector &collector = collector_instance();
+ labels_t result = collector.get_extra_labels(daemon_name);
+ ASSERT_EQ(result, *labels_vec_itr);
+ }
+ // test for fail case with daemon_name.size() < 4
+ std::string short_daemon_name = "ceph-client.rgw.foo";
+ std::string counter_name = "ceph_rgw_get";
+ DaemonMetricCollector &collector = collector_instance();
+ labels_t fail_result = collector.get_extra_labels(short_daemon_name);
+ // This is a special case, the daemon name is not of the required size for fetching instance_id.
+ // So no labels should be added.
+ ASSERT_TRUE(fail_result.empty());
+}
diff --git a/src/test/fedora-33/Dockerfile.in b/src/test/fedora-33/Dockerfile.in
new file mode 100644
index 000000000..3598271f6
--- /dev/null
+++ b/src/test/fedora-33/Dockerfile.in
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2015 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+# Environment variables are substituted via envsubst(1)
+#
+# user_id=$(id -u)
+# os_version= the desired REPOSITORY TAG
+#
+FROM fedora:%%os_version%%
+COPY install-deps.sh /root/
+COPY ceph.spec.in /root/
+# build dependencies
+RUN dnf install -y which gdb git sudo; cd /root ; ./install-deps.sh
+RUN if test %%USER%% != root ; then useradd -M --uid %%user_id%% %%USER%% && echo '%%USER%% ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ; fi
diff --git a/src/test/fedora-33/ceph.spec.in b/src/test/fedora-33/ceph.spec.in
new file mode 120000
index 000000000..9abcafd12
--- /dev/null
+++ b/src/test/fedora-33/ceph.spec.in
@@ -0,0 +1 @@
+../../../ceph.spec.in \ No newline at end of file
diff --git a/src/test/fedora-33/install-deps.sh b/src/test/fedora-33/install-deps.sh
new file mode 120000
index 000000000..fc9c78b27
--- /dev/null
+++ b/src/test/fedora-33/install-deps.sh
@@ -0,0 +1 @@
+../../../install-deps.sh \ No newline at end of file
diff --git a/src/test/fedora-34 b/src/test/fedora-34
new file mode 120000
index 000000000..b2efdfaff
--- /dev/null
+++ b/src/test/fedora-34
@@ -0,0 +1 @@
+fedora-33 \ No newline at end of file
diff --git a/src/test/fio/CMakeLists.txt b/src/test/fio/CMakeLists.txt
new file mode 100644
index 000000000..60731a0c5
--- /dev/null
+++ b/src/test/fio/CMakeLists.txt
@@ -0,0 +1,21 @@
+# ObjectStore
+add_library(fio_ceph_objectstore SHARED fio_ceph_objectstore.cc)
+target_link_libraries(fio_ceph_objectstore fio)
+
+# Messenger
+add_library(fio_ceph_messenger SHARED fio_ceph_messenger.cc)
+target_link_libraries(fio_ceph_messenger fio)
+
+# librgw
+add_library(fio_librgw SHARED fio_librgw.cc)
+target_link_libraries(fio_librgw rgw fio)
+
+target_link_libraries(fio_ceph_objectstore os global)
+install(TARGETS fio_ceph_objectstore DESTINATION lib)
+
+target_link_libraries(fio_ceph_messenger os global)
+install(TARGETS fio_ceph_messenger DESTINATION lib)
+
+target_link_libraries(fio_librgw os global rgw)
+install(TARGETS fio_librgw DESTINATION lib)
+
diff --git a/src/test/fio/README.md b/src/test/fio/README.md
new file mode 100644
index 000000000..91a98af9e
--- /dev/null
+++ b/src/test/fio/README.md
@@ -0,0 +1,116 @@
+FIO
+===
+
+Ceph uses the fio workload generator and benchmarking utility.
+(https://github.com/axboe/fio.git)
+
+FIO tool is automatically fetched to build/src/fio, and build if necessary.
+
+RBD
+---
+
+The fio engine for rbd is located in the fio tree itself, so you'll need to
+build it from source.
+
+If you install the ceph libraries to a location that isn't in your
+LD_LIBRARY_PATH, be sure to add it:
+
+ export LD_LIBRARY_PATH=/path/to/install/lib
+
+To build fio with rbd:
+
+ ./configure --extra-cflags="-I/path/to/install/include -L/path/to/install/lib"
+ make
+
+If configure fails with "Rados Block Device engine no", see config.log for
+details and adjust the cflags as necessary.
+
+If ceph was compiled with tcmalloc, it may be necessary to compile fio with:
+ make EXTLIBS=tcmalloc
+Otherwise fio might crash in malloc_usable_size().
+
+To view the fio options specific to the rbd engine:
+
+ ./fio --enghelp=rbd
+
+See examples/rbd.fio for an example job file. To run:
+
+ ./fio examples/rbd.fio
+
+ObjectStore
+-----------
+
+This fio engine allows you to mount and use a ceph object store directly,
+without having to build a ceph cluster or start any daemons.
+
+Because the ObjectStore is not a public-facing interface, we build it inside
+of the ceph tree and load libfio_ceph_objectstore.so into fio as an external
+engine.
+
+To build fio_ceph_objectstore run:
+```
+ ./do_cmake.sh -DWITH_FIO=ON
+ cd build
+ make fio_ceph_objectstore
+```
+This will fetch FIO to build/src/fio directory,
+compile fio tool and libfio_ceph_objectstore.so.
+
+If you install the ceph libraries to a location that isn't in your
+LD_LIBRARY_PATH, be sure to add it:
+
+ export LD_LIBRARY_PATH=/path/to/install/lib
+
+To view the fio options specific to the objectstore engine:
+
+ ./fio --enghelp=libfio_ceph_objectstore.so
+
+The conf= option requires a ceph configuration file (ceph.conf). Example job
+and conf files for each object store are provided in the same directory as
+this README.
+
+To run:
+
+ ./fio /path/to/job.fio
+
+RADOS
+-----
+
+By default FIO can be compiled with support for RADOS.
+When ceph is installed in your system default compilation of FIO includes RADOS ioengine.
+If you installed ceph in any other place (cmake -DCMAKE_INSTALL_PREFIX=${CEPH_INSTALL_ROOT} ..) you can build FIO following way:
+
+ LIBS="-lrados -ltcmalloc" LDFLAGS="-L${CEPH_INSTALL_ROOT}/lib" EXTFLAGS="-I${CEPH_INSTALL_ROOT}/include" \
+ rados=yes ./configure
+ LIBS="-lrados -ltcmalloc" LDFLAGS="-L${CEPH_INSTALL_ROOT}/lib" EXTFLAGS="-I${CEPH_INSTALL_ROOT}/include" \
+ rados=yes make
+
+"-ltcmalloc" is necessary if ceph was compiled with tcmalloc.
+
+Messenger
+---------
+
+This fio engine allows you to test CEPH messenger transport layer, without
+any disk activities involved.
+
+To build fio_ceph_messenger:
+```
+ ./do_cmake.sh -DWITH_FIO=ON
+ cd build
+ make fio_ceph_messenger
+```
+If you install the ceph libraries to a location that isn't in your
+LD_LIBRARY_PATH, be sure to add it:
+
+ export LD_LIBRARY_PATH=/path/to/install/lib
+
+To view the fio options specific to the messenger engine:
+
+ ./fio --enghelp=libfio_ceph_messenger.so
+
+The ceph_conf_file= option requires a ceph configuration file (ceph.conf),
+see ceph-messenger.conf and ceph-messenger.fio for details.
+
+To run:
+
+ ./fio ./ceph-messenger.fio
diff --git a/src/test/fio/ceph-bluestore.conf b/src/test/fio/ceph-bluestore.conf
new file mode 100644
index 000000000..6dd4f1afa
--- /dev/null
+++ b/src/test/fio/ceph-bluestore.conf
@@ -0,0 +1,20 @@
+# example configuration file for ceph-bluestore.fio
+
+[global]
+ debug bluestore = 0/0
+ debug bluefs = 0/0
+ debug bdev = 0/0
+ debug rocksdb = 0/0
+ # spread objects over 8 collections
+ osd pool default pg num = 8
+ # increasing shards can help when scaling number of collections
+ osd op num shards = 5
+
+[osd]
+ osd objectstore = bluestore
+
+ # use directory= option from fio job file
+ osd data = ${fio_dir}
+
+ # log inside fio_dir
+ log file = ${fio_dir}/log
diff --git a/src/test/fio/ceph-bluestore.fio b/src/test/fio/ceph-bluestore.fio
new file mode 100644
index 000000000..dbadf701a
--- /dev/null
+++ b/src/test/fio/ceph-bluestore.fio
@@ -0,0 +1,39 @@
+# Runs a 64k random write test against the ceph BlueStore.
+[global]
+ioengine=libfio_ceph_objectstore.so # must be found in your LD_LIBRARY_PATH
+
+conf=ceph-bluestore.conf # must point to a valid ceph configuration file
+directory=/mnt/fio-bluestore # directory for osd_data
+
+#oi_attr_len=350-4000 # specifies OI(aka '_') attribute length range to couple
+ # writes with. Default: 0 (disabled)
+
+#snapset_attr_len=35 # specifies snapset attribute length range to couple
+ # writes with. Default: 0 (disabled)
+
+#_fastinfo_omap_len=186 # specifies _fastinfo omap entry length range to
+ # couple writes with. Default: 0 (disabled)
+
+#pglog_simulation=1 # couples write and omap generation in OSD PG log manner.
+ # Ceph's osd_min_pg_log_entries, osd_pg_log_trim_min,
+ # osd_pg_log_dups_tracked settings control cyclic
+ # omap keys creation/removal.
+ # Following additional FIO pglog_ settings to apply too:
+
+#pglog_omap_len=173 # specifies PG log entry length range to couple
+ # writes with. Default: 0 (disabled)
+
+#pglog_dup_omap_len=57 # specifies duplicate PG log entry length range
+ # to couple writes with. Default: 0 (disabled)
+#single_pool_mode=0 # Enables the mode when all jobs run against for the same pool.
+
+rw=randwrite
+iodepth=16
+
+time_based=1
+runtime=20s
+
+[bluestore]
+nr_files=64
+size=256m
+bs=64k
diff --git a/src/test/fio/ceph-filestore.conf b/src/test/fio/ceph-filestore.conf
new file mode 100644
index 000000000..06266656a
--- /dev/null
+++ b/src/test/fio/ceph-filestore.conf
@@ -0,0 +1,26 @@
+# example configuration file for ceph-filestore.fio
+
+[global]
+ debug filestore = 0/0
+ debug journal = 0/0
+
+ # spread objects over 8 collections
+ osd pool default pg num = 8
+ # increasing shards can help when scaling number of collections
+ osd op num shards = 5
+
+ filestore fd cache size = 32
+
+[osd]
+ osd objectstore = filestore
+
+ # use directory= option from fio job file
+ osd data = ${fio_dir}
+
+ # journal inside fio_dir
+ osd journal = ${fio_dir}/journal
+ osd journal size = 500
+ journal force aio = 1
+
+ # log outside fio_dir
+ log file = ${fio_dir}.log
diff --git a/src/test/fio/ceph-filestore.fio b/src/test/fio/ceph-filestore.fio
new file mode 100644
index 000000000..bb93c8df6
--- /dev/null
+++ b/src/test/fio/ceph-filestore.fio
@@ -0,0 +1,17 @@
+# Runs a 64k random write test against the ceph FileStore.
+[global]
+ioengine=libfio_ceph_objectstore.so # must be found in your LD_LIBRARY_PATH
+
+conf=ceph-filestore.conf # must point to a valid ceph configuration file
+directory=/mnt/fio-filestore # directory for osd_data
+
+rw=randwrite
+iodepth=16
+
+time_based=1
+runtime=20s
+
+[filestore]
+nr_files=64
+size=256m
+bs=64k
diff --git a/src/test/fio/ceph-librgw.fio b/src/test/fio/ceph-librgw.fio
new file mode 100644
index 000000000..fefca6f84
--- /dev/null
+++ b/src/test/fio/ceph-librgw.fio
@@ -0,0 +1,28 @@
+#
+# example jobfile, e.g.:
+# fio --max-jobs=20 /lv2tb/ceph-cp/src/test/fio/ceph-librgw.fio
+#
+[global]
+ioengine=external:/home/mbenjamin/ceph-cp/build/lib/libfio_librgw.so
+name=fiotest
+direct=0
+access_key=${AWS_ACCESS_KEY_ID}
+secret_key=${AWS_SECRET_ACCESS_KEY}
+userid=testuser
+ceph_cluster=ceph
+ceph_conf=/home/mbenjamin/ceph-cp/build/ceph.conf
+#in current impl, there is only one, global bucket
+bucket_name=fiotest
+thread=1
+nr_files=8
+bs=256k
+size=256k
+
+[rgw_randwrite]
+numjobs=8
+rw=rw
+rwmixread=70
+rwmixwrite=30
+offset=0
+time_based=1
+runtime=30s
diff --git a/src/test/fio/ceph-memstore.conf b/src/test/fio/ceph-memstore.conf
new file mode 100644
index 000000000..3553d1bcf
--- /dev/null
+++ b/src/test/fio/ceph-memstore.conf
@@ -0,0 +1,18 @@
+# example configuration file for ceph-memstore.fio
+
+[global]
+ debug filestore = 0
+
+ # spread objects over 8 collections
+ osd pool default pg num = 8
+ # increasing shards can help when scaling number of collections
+ osd op num shards = 5
+
+[osd]
+ osd objectstore = memstore
+
+ # use directory= option from fio job file
+ osd data = ${fio_dir}
+
+ # log inside fio_dir
+ log file = ${fio_dir}/log
diff --git a/src/test/fio/ceph-memstore.fio b/src/test/fio/ceph-memstore.fio
new file mode 100644
index 000000000..ceb6671d7
--- /dev/null
+++ b/src/test/fio/ceph-memstore.fio
@@ -0,0 +1,17 @@
+# Runs a 64k random write test against the ceph MemStore.
+[global]
+ioengine=libfio_ceph_objectstore.so # must be found in your LD_LIBRARY_PATH
+
+conf=ceph-memstore.conf # must point to a valid ceph configuration file
+directory=/mnt/fio-memstore # directory for osd_data
+
+rw=randwrite
+iodepth=16
+
+time_based=1
+runtime=20s
+
+[memstore]
+nr_files=64
+size=256m
+bs=64k
diff --git a/src/test/fio/ceph-messenger.conf b/src/test/fio/ceph-messenger.conf
new file mode 100644
index 000000000..8d83d3613
--- /dev/null
+++ b/src/test/fio/ceph-messenger.conf
@@ -0,0 +1,7 @@
+[global]
+
+ms_type=async+posix
+ms_crc_data=false
+ms_crc_header=false
+ms_dispatch_throttle_bytes=0
+debug_ms=0/0
diff --git a/src/test/fio/ceph-messenger.fio b/src/test/fio/ceph-messenger.fio
new file mode 100644
index 000000000..20115cb8d
--- /dev/null
+++ b/src/test/fio/ceph-messenger.fio
@@ -0,0 +1,22 @@
+[global]
+bs=4k
+size=1g
+iodepth=128
+
+ioengine=libfio_ceph_messenger.so
+#ceph_conf_file=ceph-messenger.conf
+
+# In order to select protocol explicitly add 'v1:' or 'v2:' prefix.
+# By default v2 is used.
+hostname=127.0.0.1
+port=5555
+
+ms_type=async+posix # or async+dpdk or async+rdma
+
+[client]
+receiver=0
+rw=write
+
+[server]
+receiver=1
+rw=read
diff --git a/src/test/fio/fio_ceph_messenger.cc b/src/test/fio/fio_ceph_messenger.cc
new file mode 100644
index 000000000..81680f102
--- /dev/null
+++ b/src/test/fio/fio_ceph_messenger.cc
@@ -0,0 +1,700 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * CEPH messenger engine
+ *
+ * FIO engine which uses ceph messenger as a transport. See corresponding
+ * FIO client and server jobs for details.
+ */
+
+#include "global/global_init.h"
+#include "msg/Messenger.h"
+#include "messages/MOSDOp.h"
+#include "messages/MOSDOpReply.h"
+#include "common/perf_counters.h"
+#include "auth/DummyAuth.h"
+#include "ring_buffer.h"
+
+#include <fio.h>
+#include <flist.h>
+#include <optgroup.h>
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_
+
+using namespace std;
+
+enum ceph_msgr_type {
+ CEPH_MSGR_TYPE_UNDEF,
+ CEPH_MSGR_TYPE_POSIX,
+ CEPH_MSGR_TYPE_DPDK,
+ CEPH_MSGR_TYPE_RDMA,
+};
+
+const char *ceph_msgr_types[] = { "undef", "async+posix",
+ "async+dpdk", "async+rdma" };
+
+struct ceph_msgr_options {
+ struct thread_data *td__;
+ unsigned int is_receiver;
+ unsigned int is_single;
+ unsigned int port;
+ const char *hostname;
+ const char *conffile;
+ enum ceph_msgr_type ms_type;
+};
+
+class FioDispatcher;
+
+struct ceph_msgr_data {
+ ceph_msgr_data(struct ceph_msgr_options *o_, unsigned iodepth) :
+ o(o_) {
+ INIT_FLIST_HEAD(&io_inflight_list);
+ INIT_FLIST_HEAD(&io_pending_list);
+ ring_buffer_init(&io_completed_q, iodepth);
+ pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE);
+ }
+
+ struct ceph_msgr_options *o;
+ Messenger *msgr = NULL;
+ FioDispatcher *disp = NULL;
+ pthread_spinlock_t spin;
+ struct ring_buffer io_completed_q;
+ struct flist_head io_inflight_list;
+ struct flist_head io_pending_list;
+ unsigned int io_inflight_nr = 0;
+ unsigned int io_pending_nr = 0;
+};
+
+struct ceph_msgr_io {
+ struct flist_head list;
+ struct ceph_msgr_data *data;
+ struct io_u *io_u;
+ MOSDOp *req_msg; /** Cached request, valid only for sender */
+};
+
+struct ceph_msgr_reply_io {
+ struct flist_head list;
+ MOSDOpReply *rep;
+};
+
+static void *str_to_ptr(const std::string &str)
+{
+ // str is assumed to be a valid ptr string
+ return reinterpret_cast<void*>(ceph::parse<uintptr_t>(str, 16).value());
+}
+
+static std::string ptr_to_str(void *ptr)
+{
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%llx", (unsigned long long)ptr);
+ return std::string(buf);
+}
+
+/*
+ * Used for refcounters print on the last context put, almost duplicates
+ * global context refcounter, sigh.
+ */
+static std::atomic<int> ctx_ref(1);
+static DummyAuthClientServer *g_dummy_auth;
+
+static void create_or_get_ceph_context(struct ceph_msgr_options *o)
+{
+ if (g_ceph_context) {
+ g_ceph_context->get();
+ ctx_ref++;
+ return;
+ }
+
+ boost::intrusive_ptr<CephContext> cct;
+ vector<const char*> args;
+
+ if (o->conffile)
+ args = { "--conf", o->conffile };
+
+ cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ /* Will use g_ceph_context instead */
+ cct.detach();
+
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(NULL);
+ g_dummy_auth = new DummyAuthClientServer(g_ceph_context);
+ g_dummy_auth->auth_registry.refresh_config();
+}
+
+static void put_ceph_context(void)
+{
+ if (--ctx_ref == 0) {
+ ostringstream ostr;
+ Formatter* f;
+
+ f = Formatter::create("json-pretty");
+ g_ceph_context->get_perfcounters_collection()->dump_formatted(f, false, false);
+ ostr << ">>>>>>>>>>>>> PERFCOUNTERS BEGIN <<<<<<<<<<<<" << std::endl;
+ f->flush(ostr);
+ ostr << ">>>>>>>>>>>>> PERFCOUNTERS END <<<<<<<<<<<<" << std::endl;
+
+ delete f;
+ delete g_dummy_auth;
+ dout(0) << ostr.str() << dendl;
+ }
+
+ g_ceph_context->put();
+}
+
+static void ceph_msgr_sender_on_reply(const object_t &oid)
+{
+ struct ceph_msgr_data *data;
+ struct ceph_msgr_io *io;
+
+ /*
+ * Here we abuse object and use it as a raw pointer. Since this is
+ * only for benchmarks and testing we do not care about anything
+ * but performance. So no need to use global structure in order
+ * to search for reply, just send a pointer and get it back.
+ */
+
+ io = (decltype(io))str_to_ptr(oid.name);
+ data = io->data;
+ ring_buffer_enqueue(&data->io_completed_q, (void *)io);
+}
+
+
+class ReplyCompletion : public Message::CompletionHook {
+ struct ceph_msgr_io *m_io;
+
+public:
+ ReplyCompletion(MOSDOpReply *rep, struct ceph_msgr_io *io) :
+ Message::CompletionHook(rep),
+ m_io(io) {
+ }
+ void finish(int err) override {
+ struct ceph_msgr_data *data = m_io->data;
+
+ ring_buffer_enqueue(&data->io_completed_q, (void *)m_io);
+ }
+};
+
+static void ceph_msgr_receiver_on_request(struct ceph_msgr_data *data,
+ MOSDOp *req)
+{
+ MOSDOpReply *rep;
+
+ rep = new MOSDOpReply(req, 0, 0, 0, false);
+ rep->set_connection(req->get_connection());
+
+ pthread_spin_lock(&data->spin);
+ if (data->io_inflight_nr) {
+ struct ceph_msgr_io *io;
+
+ data->io_inflight_nr--;
+ io = flist_first_entry(&data->io_inflight_list,
+ struct ceph_msgr_io, list);
+ flist_del(&io->list);
+ pthread_spin_unlock(&data->spin);
+
+ rep->set_completion_hook(new ReplyCompletion(rep, io));
+ rep->get_connection()->send_message(rep);
+ } else {
+ struct ceph_msgr_reply_io *rep_io;
+
+ rep_io = (decltype(rep_io))malloc(sizeof(*rep_io));
+ rep_io->rep = rep;
+
+ data->io_pending_nr++;
+ flist_add_tail(&rep_io->list, &data->io_pending_list);
+ pthread_spin_unlock(&data->spin);
+ }
+}
+
+class FioDispatcher : public Dispatcher {
+ struct ceph_msgr_data *m_data;
+
+public:
+ FioDispatcher(struct ceph_msgr_data *data):
+ Dispatcher(g_ceph_context),
+ m_data(data) {
+ }
+ bool ms_can_fast_dispatch_any() const override {
+ return true;
+ }
+ bool ms_can_fast_dispatch(const Message *m) const override {
+ switch (m->get_type()) {
+ case CEPH_MSG_OSD_OP:
+ return m_data->o->is_receiver;
+ case CEPH_MSG_OSD_OPREPLY:
+ return !m_data->o->is_receiver;
+ default:
+ return false;
+ }
+ }
+ void ms_handle_fast_connect(Connection *con) override {
+ }
+ void ms_handle_fast_accept(Connection *con) override {
+ }
+ bool ms_dispatch(Message *m) override {
+ return true;
+ }
+ void ms_fast_dispatch(Message *m) override {
+ if (m_data->o->is_receiver) {
+ MOSDOp *req;
+
+ /*
+ * Server side, handle request.
+ */
+
+ req = static_cast<MOSDOp*>(m);
+ req->finish_decode();
+
+ ceph_msgr_receiver_on_request(m_data, req);
+ } else {
+ MOSDOpReply *rep;
+
+ /*
+ * Client side, get reply, extract objid and mark
+ * IO as completed.
+ */
+
+ rep = static_cast<MOSDOpReply*>(m);
+ ceph_msgr_sender_on_reply(rep->get_oid());
+ }
+ m->put();
+ }
+ bool ms_handle_reset(Connection *con) override {
+ return true;
+ }
+ void ms_handle_remote_reset(Connection *con) override {
+ }
+ bool ms_handle_refused(Connection *con) override {
+ return false;
+ }
+ int ms_handle_fast_authentication(Connection *con) override {
+ return 1;
+ }
+};
+
+static entity_addr_t hostname_to_addr(struct ceph_msgr_options *o)
+{
+ entity_addr_t addr;
+
+ addr.parse(o->hostname);
+ addr.set_port(o->port);
+ addr.set_nonce(0);
+
+ return addr;
+}
+
+static Messenger *create_messenger(struct ceph_msgr_options *o)
+{
+ entity_name_t ename = o->is_receiver ?
+ entity_name_t::OSD(0) : entity_name_t::CLIENT(0);
+ std::string lname = o->is_receiver ?
+ "receiver" : "sender";
+
+ std::string ms_type = o->ms_type != CEPH_MSGR_TYPE_UNDEF ?
+ ceph_msgr_types[o->ms_type] :
+ g_ceph_context->_conf.get_val<std::string>("ms_type");
+
+ /* o->td__>pid doesn't set value, so use getpid() instead*/
+ auto nonce = o->is_receiver ? 0 : (getpid() + o->td__->thread_number);
+ Messenger *msgr = Messenger::create(g_ceph_context, ms_type.c_str(),
+ ename, lname, nonce);
+ if (o->is_receiver) {
+ msgr->set_default_policy(Messenger::Policy::stateless_server(0));
+ msgr->bind(hostname_to_addr(o));
+ } else {
+ msgr->set_default_policy(Messenger::Policy::lossless_client(0));
+ }
+ msgr->set_auth_client(g_dummy_auth);
+ msgr->set_auth_server(g_dummy_auth);
+ msgr->set_require_authorizer(false);
+ msgr->start();
+
+ return msgr;
+}
+
+static Messenger *single_msgr;
+static std::atomic<int> single_msgr_ref;
+static vector<FioDispatcher *> single_msgr_disps;
+
+static void init_messenger(struct ceph_msgr_data *data)
+{
+ struct ceph_msgr_options *o = data->o;
+ FioDispatcher *disp;
+ Messenger *msgr;
+
+ disp = new FioDispatcher(data);
+ if (o->is_single) {
+ /*
+ * Single messenger instance for the whole FIO
+ */
+
+ if (!single_msgr) {
+ msgr = create_messenger(o);
+ single_msgr = msgr;
+ } else {
+ msgr = single_msgr;
+ }
+ single_msgr_disps.push_back(disp);
+ single_msgr_ref++;
+ } else {
+ /*
+ * Messenger instance per FIO thread
+ */
+ msgr = create_messenger(o);
+ }
+ msgr->add_dispatcher_head(disp);
+
+ data->disp = disp;
+ data->msgr = msgr;
+}
+
+static void free_messenger(struct ceph_msgr_data *data)
+{
+ data->msgr->shutdown();
+ data->msgr->wait();
+ delete data->msgr;
+}
+
+static void put_messenger(struct ceph_msgr_data *data)
+{
+ struct ceph_msgr_options *o = data->o;
+
+ if (o->is_single) {
+ if (--single_msgr_ref == 0) {
+ free_messenger(data);
+ /*
+ * In case of a single messenger instance we have to
+ * free dispatchers after actual messenger destruction.
+ */
+ for (auto disp : single_msgr_disps)
+ delete disp;
+ single_msgr = NULL;
+ }
+ } else {
+ free_messenger(data);
+ delete data->disp;
+ }
+ data->disp = NULL;
+ data->msgr = NULL;
+}
+
+static int fio_ceph_msgr_setup(struct thread_data *td)
+{
+ struct ceph_msgr_options *o = (decltype(o))td->eo;
+ o->td__ = td;
+ ceph_msgr_data *data;
+
+ /* We have to manage global resources so we use threads */
+ td->o.use_thread = 1;
+
+ create_or_get_ceph_context(o);
+
+ if (!td->io_ops_data) {
+ data = new ceph_msgr_data(o, td->o.iodepth);
+ init_messenger(data);
+ td->io_ops_data = (void *)data;
+ }
+
+ return 0;
+}
+
+static void fio_ceph_msgr_cleanup(struct thread_data *td)
+{
+ struct ceph_msgr_data *data;
+ unsigned nr;
+
+ data = (decltype(data))td->io_ops_data;
+ put_messenger(data);
+
+ nr = ring_buffer_used_size(&data->io_completed_q);
+ if (nr)
+ fprintf(stderr, "fio: io_completed_nr==%d, but should be zero\n",
+ nr);
+ if (data->io_inflight_nr)
+ fprintf(stderr, "fio: io_inflight_nr==%d, but should be zero\n",
+ data->io_inflight_nr);
+ if (data->io_pending_nr)
+ fprintf(stderr, "fio: io_pending_nr==%d, but should be zero\n",
+ data->io_pending_nr);
+ if (!flist_empty(&data->io_inflight_list))
+ fprintf(stderr, "fio: io_inflight_list is not empty\n");
+ if (!flist_empty(&data->io_pending_list))
+ fprintf(stderr, "fio: io_pending_list is not empty\n");
+
+ ring_buffer_deinit(&data->io_completed_q);
+ delete data;
+ put_ceph_context();
+}
+
+static int fio_ceph_msgr_io_u_init(struct thread_data *td, struct io_u *io_u)
+{
+ struct ceph_msgr_options *o = (decltype(o))td->eo;
+ struct ceph_msgr_io *io;
+ MOSDOp *req_msg = NULL;
+
+ io = (decltype(io))malloc(sizeof(*io));
+ io->io_u = io_u;
+ io->data = (decltype(io->data))td->io_ops_data;
+
+ if (!o->is_receiver) {
+ object_t oid(ptr_to_str(io));
+ pg_t pgid;
+ object_locator_t oloc;
+ hobject_t hobj(oid, oloc.key, CEPH_NOSNAP, pgid.ps(),
+ pgid.pool(), oloc.nspace);
+ spg_t spgid(pgid);
+ entity_inst_t dest(entity_name_t::OSD(0), hostname_to_addr(o));
+
+ Messenger *msgr = io->data->msgr;
+ ConnectionRef con = msgr->connect_to(dest.name.type(),
+ entity_addrvec_t(dest.addr));
+
+ req_msg = new MOSDOp(0, 0, hobj, spgid, 0, 0, 0);
+ req_msg->set_connection(con);
+ }
+
+ io->req_msg = req_msg;
+ io_u->engine_data = (void *)io;
+
+ return 0;
+}
+
+static void fio_ceph_msgr_io_u_free(struct thread_data *td, struct io_u *io_u)
+{
+ struct ceph_msgr_io *io;
+
+ io = (decltype(io))io_u->engine_data;
+ if (io) {
+ io_u->engine_data = NULL;
+ if (io->req_msg)
+ io->req_msg->put();
+ free(io);
+ }
+}
+
+static enum fio_q_status ceph_msgr_sender_queue(struct thread_data *td,
+ struct io_u *io_u)
+{
+ struct ceph_msgr_data *data;
+ struct ceph_msgr_io *io;
+
+ bufferlist buflist = bufferlist::static_from_mem(
+ (char *)io_u->buf, io_u->buflen);
+
+ io = (decltype(io))io_u->engine_data;
+ data = (decltype(data))td->io_ops_data;
+
+ /* No handy method to clear ops before reusage? Ok */
+ io->req_msg->ops.clear();
+
+ /* Here we do not care about direction, always send as write */
+ io->req_msg->write(0, io_u->buflen, buflist);
+ /* Keep message alive */
+ io->req_msg->get();
+ io->req_msg->get_connection()->send_message(io->req_msg);
+
+ return FIO_Q_QUEUED;
+}
+
+static int fio_ceph_msgr_getevents(struct thread_data *td, unsigned int min,
+ unsigned int max, const struct timespec *ts)
+{
+ struct ceph_msgr_data *data;
+ unsigned int nr;
+
+ data = (decltype(data))td->io_ops_data;
+
+ /*
+ * Check io_u.c : if min == 0 -> ts is valid and equal to zero,
+ * if min != 0 -> ts is NULL.
+ */
+ assert(!min ^ !ts);
+
+ nr = ring_buffer_used_size(&data->io_completed_q);
+ if (nr >= min)
+ /* We got something */
+ return min(nr, max);
+
+ /* Here we are only if min != 0 and ts == NULL */
+ assert(min && !ts);
+
+ while ((nr = ring_buffer_used_size(&data->io_completed_q)) < min &&
+ !td->terminate) {
+ /* Poll, no disk IO, so we expect response immediately. */
+ usleep(10);
+ }
+
+ return min(nr, max);
+}
+
+static struct io_u *fio_ceph_msgr_event(struct thread_data *td, int event)
+{
+ struct ceph_msgr_data *data;
+ struct ceph_msgr_io *io;
+
+ data = (decltype(data))td->io_ops_data;
+ io = (decltype(io))ring_buffer_dequeue(&data->io_completed_q);
+
+ return io->io_u;
+}
+
+static enum fio_q_status ceph_msgr_receiver_queue(struct thread_data *td,
+ struct io_u *io_u)
+{
+ struct ceph_msgr_data *data;
+ struct ceph_msgr_io *io;
+
+ io = (decltype(io))io_u->engine_data;
+ data = io->data;
+ pthread_spin_lock(&data->spin);
+ if (data->io_pending_nr) {
+ struct ceph_msgr_reply_io *rep_io;
+ MOSDOpReply *rep;
+
+ data->io_pending_nr--;
+ rep_io = flist_first_entry(&data->io_pending_list,
+ struct ceph_msgr_reply_io,
+ list);
+ flist_del(&rep_io->list);
+ rep = rep_io->rep;
+ pthread_spin_unlock(&data->spin);
+ free(rep_io);
+
+ rep->set_completion_hook(new ReplyCompletion(rep, io));
+ rep->get_connection()->send_message(rep);
+ } else {
+ data->io_inflight_nr++;
+ flist_add_tail(&io->list, &data->io_inflight_list);
+ pthread_spin_unlock(&data->spin);
+ }
+
+ return FIO_Q_QUEUED;
+}
+
+static enum fio_q_status fio_ceph_msgr_queue(struct thread_data *td,
+ struct io_u *io_u)
+{
+ struct ceph_msgr_options *o = (decltype(o))td->eo;
+
+ if (o->is_receiver)
+ return ceph_msgr_receiver_queue(td, io_u);
+ else
+ return ceph_msgr_sender_queue(td, io_u);
+}
+
+static int fio_ceph_msgr_open_file(struct thread_data *td, struct fio_file *f)
+{
+ return 0;
+}
+
+static int fio_ceph_msgr_close_file(struct thread_data *, struct fio_file *)
+{
+ return 0;
+}
+
+template <class Func>
+fio_option make_option(Func&& func)
+{
+ auto o = fio_option{};
+ o.category = FIO_OPT_C_ENGINE;
+ func(std::ref(o));
+ return o;
+}
+
+static std::vector<fio_option> options {
+ make_option([] (fio_option& o) {
+ o.name = "receiver";
+ o.lname = "CEPH messenger is receiver";
+ o.type = FIO_OPT_BOOL;
+ o.off1 = offsetof(struct ceph_msgr_options, is_receiver);
+ o.help = "CEPH messenger is sender or receiver";
+ o.def = "0";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "single_instance";
+ o.lname = "Single instance of CEPH messenger ";
+ o.type = FIO_OPT_BOOL;
+ o.off1 = offsetof(struct ceph_msgr_options, is_single);
+ o.help = "CEPH messenger is a created once for all threads";
+ o.def = "0";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "hostname";
+ o.lname = "CEPH messenger hostname";
+ o.type = FIO_OPT_STR_STORE;
+ o.off1 = offsetof(struct ceph_msgr_options, hostname);
+ o.help = "Hostname for CEPH messenger engine";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "port";
+ o.lname = "CEPH messenger engine port";
+ o.type = FIO_OPT_INT;
+ o.off1 = offsetof(struct ceph_msgr_options, port);
+ o.maxval = 65535;
+ o.minval = 1;
+ o.help = "Port to use for CEPH messenger";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "ms_type";
+ o.lname = "CEPH messenger transport type: async+posix, async+dpdk, async+rdma";
+ o.type = FIO_OPT_STR;
+ o.off1 = offsetof(struct ceph_msgr_options, ms_type);
+ o.help = "Transport type for CEPH messenger, see 'ms async transport type' corresponding CEPH documentation page";
+ o.def = "undef";
+
+ o.posval[0].ival = "undef";
+ o.posval[0].oval = CEPH_MSGR_TYPE_UNDEF;
+
+ o.posval[1].ival = "async+posix";
+ o.posval[1].oval = CEPH_MSGR_TYPE_POSIX;
+ o.posval[1].help = "POSIX API";
+
+ o.posval[2].ival = "async+dpdk";
+ o.posval[2].oval = CEPH_MSGR_TYPE_DPDK;
+ o.posval[2].help = "DPDK";
+
+ o.posval[3].ival = "async+rdma";
+ o.posval[3].oval = CEPH_MSGR_TYPE_RDMA;
+ o.posval[3].help = "RDMA";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "ceph_conf_file";
+ o.lname = "CEPH configuration file";
+ o.type = FIO_OPT_STR_STORE;
+ o.off1 = offsetof(struct ceph_msgr_options, conffile);
+ o.help = "Path to CEPH configuration file";
+ }),
+ {} /* Last NULL */
+};
+
+static struct ioengine_ops ioengine;
+
+extern "C" {
+
+void get_ioengine(struct ioengine_ops** ioengine_ptr)
+{
+ /*
+ * Main ioengine structure
+ */
+ ioengine.name = "ceph-msgr";
+ ioengine.version = FIO_IOOPS_VERSION;
+ ioengine.flags = FIO_DISKLESSIO | FIO_UNIDIR | FIO_PIPEIO;
+ ioengine.setup = fio_ceph_msgr_setup;
+ ioengine.queue = fio_ceph_msgr_queue;
+ ioengine.getevents = fio_ceph_msgr_getevents;
+ ioengine.event = fio_ceph_msgr_event;
+ ioengine.cleanup = fio_ceph_msgr_cleanup;
+ ioengine.open_file = fio_ceph_msgr_open_file;
+ ioengine.close_file = fio_ceph_msgr_close_file;
+ ioengine.io_u_init = fio_ceph_msgr_io_u_init;
+ ioengine.io_u_free = fio_ceph_msgr_io_u_free;
+ ioengine.option_struct_size = sizeof(struct ceph_msgr_options);
+ ioengine.options = options.data();
+
+ *ioengine_ptr = &ioengine;
+}
+} // extern "C"
diff --git a/src/test/fio/fio_ceph_objectstore.cc b/src/test/fio/fio_ceph_objectstore.cc
new file mode 100644
index 000000000..ade043f0c
--- /dev/null
+++ b/src/test/fio/fio_ceph_objectstore.cc
@@ -0,0 +1,942 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph ObjectStore engine
+ *
+ * IO engine using Ceph's ObjectStore class to test low-level performance of
+ * Ceph OSDs.
+ *
+ */
+
+#include <memory>
+#include <system_error>
+#include <vector>
+#include <fstream>
+
+#include "os/ObjectStore.h"
+#include "global/global_init.h"
+#include "common/errno.h"
+#include "include/intarith.h"
+#include "include/stringify.h"
+#include "include/random.h"
+#include "include/str_list.h"
+#include "common/perf_counters.h"
+#include "common/TracepointProvider.h"
+
+#include <fio.h>
+#include <optgroup.h>
+
+#include "include/ceph_assert.h" // fio.h clobbers our assert.h
+#include <algorithm>
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_
+
+using namespace std;
+
+namespace {
+
+/// fio configuration options read from the job file
+struct Options {
+ thread_data* td;
+ char* conf;
+ char* perf_output_file;
+ char* throttle_values;
+ char* deferred_throttle_values;
+ unsigned long long
+ cycle_throttle_period,
+ oi_attr_len_low,
+ oi_attr_len_high,
+ snapset_attr_len_low,
+ snapset_attr_len_high,
+ pglog_omap_len_low,
+ pglog_omap_len_high,
+ pglog_dup_omap_len_low,
+ pglog_dup_omap_len_high,
+ _fastinfo_omap_len_low,
+ _fastinfo_omap_len_high;
+ unsigned simulate_pglog;
+ unsigned single_pool_mode;
+ unsigned preallocate_files;
+ unsigned check_files;
+};
+
+template <class Func> // void Func(fio_option&)
+fio_option make_option(Func&& func)
+{
+ // zero-initialize and set common defaults
+ auto o = fio_option{};
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_RBD;
+ func(std::ref(o));
+ return o;
+}
+
+static std::vector<fio_option> ceph_options{
+ make_option([] (fio_option& o) {
+ o.name = "conf";
+ o.lname = "ceph configuration file";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "Path to a ceph configuration file";
+ o.off1 = offsetof(Options, conf);
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "perf_output_file";
+ o.lname = "perf output target";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "Path to which to write json formatted perf output";
+ o.off1 = offsetof(Options, perf_output_file);
+ o.def = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "oi_attr_len";
+ o.lname = "OI Attr length";
+ o.type = FIO_OPT_STR_VAL;
+ o.help = "Set OI(aka '_') attribute to specified length";
+ o.off1 = offsetof(Options, oi_attr_len_low);
+ o.off2 = offsetof(Options, oi_attr_len_high);
+ o.def = 0;
+ o.minval = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "snapset_attr_len";
+ o.lname = "Attr 'snapset' length";
+ o.type = FIO_OPT_STR_VAL;
+ o.help = "Set 'snapset' attribute to specified length";
+ o.off1 = offsetof(Options, snapset_attr_len_low);
+ o.off2 = offsetof(Options, snapset_attr_len_high);
+ o.def = 0;
+ o.minval = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "_fastinfo_omap_len";
+ o.lname = "'_fastinfo' omap entry length";
+ o.type = FIO_OPT_STR_VAL;
+ o.help = "Set '_fastinfo' OMAP attribute to specified length";
+ o.off1 = offsetof(Options, _fastinfo_omap_len_low);
+ o.off2 = offsetof(Options, _fastinfo_omap_len_high);
+ o.def = 0;
+ o.minval = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "pglog_simulation";
+ o.lname = "pglog behavior simulation";
+ o.type = FIO_OPT_BOOL;
+ o.help = "Enables PG Log simulation behavior";
+ o.off1 = offsetof(Options, simulate_pglog);
+ o.def = "0";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "pglog_omap_len";
+ o.lname = "pglog omap entry length";
+ o.type = FIO_OPT_STR_VAL;
+ o.help = "Set pglog omap entry to specified length";
+ o.off1 = offsetof(Options, pglog_omap_len_low);
+ o.off2 = offsetof(Options, pglog_omap_len_high);
+ o.def = 0;
+ o.minval = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "pglog_dup_omap_len";
+ o.lname = "uplicate pglog omap entry length";
+ o.type = FIO_OPT_STR_VAL;
+ o.help = "Set duplicate pglog omap entry to specified length";
+ o.off1 = offsetof(Options, pglog_dup_omap_len_low);
+ o.off2 = offsetof(Options, pglog_dup_omap_len_high);
+ o.def = 0;
+ o.minval = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "single_pool_mode";
+ o.lname = "single(shared among jobs) pool mode";
+ o.type = FIO_OPT_BOOL;
+ o.help = "Enables the mode when all jobs run against the same pool";
+ o.off1 = offsetof(Options, single_pool_mode);
+ o.def = "0";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "preallocate_files";
+ o.lname = "preallocate files on init";
+ o.type = FIO_OPT_BOOL;
+ o.help = "Enables/disables file preallocation (touch and resize) on init";
+ o.off1 = offsetof(Options, preallocate_files);
+ o.def = "1";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "check_files";
+ o.lname = "ensure files exist and are correct on init";
+ o.type = FIO_OPT_BOOL;
+ o.help = "Enables/disables checking of files on init";
+ o.off1 = offsetof(Options, check_files);
+ o.def = "0";
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "bluestore_throttle";
+ o.lname = "set bluestore throttle";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "comma delimited list of throttle values",
+ o.off1 = offsetof(Options, throttle_values);
+ o.def = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "bluestore_deferred_throttle";
+ o.lname = "set bluestore deferred throttle";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "comma delimited list of throttle values",
+ o.off1 = offsetof(Options, deferred_throttle_values);
+ o.def = 0;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "vary_bluestore_throttle_period";
+ o.lname = "period between different throttle values";
+ o.type = FIO_OPT_STR_VAL;
+ o.help = "set to non-zero value to periodically cycle through throttle options";
+ o.off1 = offsetof(Options, cycle_throttle_period);
+ o.def = "0";
+ o.minval = 0;
+ }),
+ {} // fio expects a 'null'-terminated list
+};
+
+
+struct Collection {
+ spg_t pg;
+ coll_t cid;
+ ObjectStore::CollectionHandle ch;
+ // Can't use mutex directly in vectors hence dynamic allocation
+
+ std::unique_ptr<std::mutex> lock;
+ uint64_t pglog_ver_head = 1;
+ uint64_t pglog_ver_tail = 1;
+ uint64_t pglog_dup_ver_tail = 1;
+
+ // use big pool ids to avoid clashing with existing collections
+ static constexpr int64_t MIN_POOL_ID = 0x0000ffffffffffff;
+
+ Collection(const spg_t& pg, ObjectStore::CollectionHandle _ch)
+ : pg(pg), cid(pg), ch(_ch),
+ lock(new std::mutex) {
+ }
+};
+
+int destroy_collections(
+ std::unique_ptr<ObjectStore>& os,
+ std::vector<Collection>& collections)
+{
+ ObjectStore::Transaction t;
+ bool failed = false;
+ // remove our collections
+ for (auto& coll : collections) {
+ ghobject_t pgmeta_oid(coll.pg.make_pgmeta_oid());
+ t.remove(coll.cid, pgmeta_oid);
+ t.remove_collection(coll.cid);
+ int r = os->queue_transaction(coll.ch, std::move(t));
+ if (r && !failed) {
+ derr << "Engine cleanup failed with " << cpp_strerror(-r) << dendl;
+ failed = true;
+ }
+ }
+ return 0;
+}
+
+int init_collections(std::unique_ptr<ObjectStore>& os,
+ uint64_t pool,
+ std::vector<Collection>& collections,
+ uint64_t count)
+{
+ ceph_assert(count > 0);
+ collections.reserve(count);
+
+ const int split_bits = cbits(count - 1);
+
+ {
+ // propagate Superblock object to ensure proper functioning of tools that
+ // need it. E.g. ceph-objectstore-tool
+ coll_t cid(coll_t::meta());
+ bool exists = os->collection_exists(cid);
+ if (!exists) {
+ auto ch = os->create_new_collection(cid);
+
+ OSDSuperblock superblock;
+ bufferlist bl;
+ encode(superblock, bl);
+
+ ObjectStore::Transaction t;
+ t.create_collection(cid, split_bits);
+ t.write(cid, OSD_SUPERBLOCK_GOBJECT, 0, bl.length(), bl);
+ int r = os->queue_transaction(ch, std::move(t));
+
+ if (r < 0) {
+ derr << "Failure to write OSD superblock: " << cpp_strerror(-r) << dendl;
+ return r;
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ auto pg = spg_t{pg_t{i, pool}};
+ coll_t cid(pg);
+
+ bool exists = os->collection_exists(cid);
+ auto ch = exists ?
+ os->open_collection(cid) :
+ os->create_new_collection(cid) ;
+
+ collections.emplace_back(pg, ch);
+
+ ObjectStore::Transaction t;
+ auto& coll = collections.back();
+ if (!exists) {
+ t.create_collection(coll.cid, split_bits);
+ ghobject_t pgmeta_oid(coll.pg.make_pgmeta_oid());
+ t.touch(coll.cid, pgmeta_oid);
+ int r = os->queue_transaction(coll.ch, std::move(t));
+ if (r) {
+ derr << "Engine init failed with " << cpp_strerror(-r) << dendl;
+ destroy_collections(os, collections);
+ return r;
+ }
+ }
+ }
+ return 0;
+}
+
+/// global engine state shared between all jobs within the process. this
+/// includes g_ceph_context and the ObjectStore instance
+struct Engine {
+ /// the initial g_ceph_context reference to be dropped on destruction
+ boost::intrusive_ptr<CephContext> cct;
+ std::unique_ptr<ObjectStore> os;
+
+ std::vector<Collection> collections; //< shared collections to spread objects over
+
+ std::mutex lock;
+ int ref_count;
+ const bool unlink; //< unlink objects on destruction
+
+ // file to which to output formatted perf information
+ const std::optional<std::string> perf_output_file;
+
+ explicit Engine(thread_data* td);
+ ~Engine();
+
+ static Engine* get_instance(thread_data* td) {
+ // note: creates an Engine with the options associated with the first job
+ static Engine engine(td);
+ return &engine;
+ }
+
+ void ref() {
+ std::lock_guard<std::mutex> l(lock);
+ ++ref_count;
+ }
+ void deref() {
+ std::lock_guard<std::mutex> l(lock);
+ --ref_count;
+ if (!ref_count) {
+ ostringstream ostr;
+ Formatter* f = Formatter::create(
+ "json-pretty", "json-pretty", "json-pretty");
+ f->open_object_section("perf_output");
+ cct->get_perfcounters_collection()->dump_formatted(f, false, false);
+ if (g_conf()->rocksdb_perf) {
+ f->open_object_section("rocksdb_perf");
+ os->get_db_statistics(f);
+ f->close_section();
+ }
+ mempool::dump(f);
+ {
+ f->open_object_section("db_histogram");
+ os->generate_db_histogram(f);
+ f->close_section();
+ }
+ f->close_section();
+
+ f->flush(ostr);
+ delete f;
+
+ if (unlink) {
+ destroy_collections(os, collections);
+ }
+ os->umount();
+ dout(0) << "FIO plugin perf dump:" << dendl;
+ dout(0) << ostr.str() << dendl;
+ if (perf_output_file) {
+ try {
+ std::ofstream foutput(*perf_output_file);
+ foutput << ostr.str() << std::endl;
+ } catch (std::exception &e) {
+ std::cerr << "Unable to write formatted output to "
+ << *perf_output_file
+ << ", exception: " << e.what()
+ << std::endl;
+ }
+ }
+ }
+ }
+};
+
+TracepointProvider::Traits bluestore_tracepoint_traits("libbluestore_tp.so",
+ "bluestore_tracing");
+
+Engine::Engine(thread_data* td)
+ : ref_count(0),
+ unlink(td->o.unlink),
+ perf_output_file(
+ static_cast<Options*>(td->eo)->perf_output_file ?
+ std::make_optional(static_cast<Options*>(td->eo)->perf_output_file) :
+ std::nullopt)
+{
+ // add the ceph command line arguments
+ auto o = static_cast<Options*>(td->eo);
+ if (!o->conf) {
+ throw std::runtime_error("missing conf option for ceph configuration file");
+ }
+ std::vector<const char*> args{
+ "-i", "0", // identify as osd.0 for osd_data and osd_journal
+ "--conf", o->conf, // use the requested conf file
+ };
+ if (td->o.directory) { // allow conf files to use ${fio_dir} for data
+ args.emplace_back("--fio_dir");
+ args.emplace_back(td->o.directory);
+ }
+
+ // claim the g_ceph_context reference and release it on destruction
+ cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_OSD,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ TracepointProvider::initialize<bluestore_tracepoint_traits>(g_ceph_context);
+
+ // create the ObjectStore
+ os = ObjectStore::create(g_ceph_context,
+ g_conf().get_val<std::string>("osd objectstore"),
+ g_conf().get_val<std::string>("osd data"),
+ g_conf().get_val<std::string>("osd journal"));
+ if (!os)
+ throw std::runtime_error("bad objectstore type " + g_conf()->osd_objectstore);
+
+ unsigned num_shards;
+ if(g_conf()->osd_op_num_shards)
+ num_shards = g_conf()->osd_op_num_shards;
+ else if(os->is_rotational())
+ num_shards = g_conf()->osd_op_num_shards_hdd;
+ else
+ num_shards = g_conf()->osd_op_num_shards_ssd;
+ os->set_cache_shards(num_shards);
+
+ //normalize options
+ o->oi_attr_len_high = max(o->oi_attr_len_low, o->oi_attr_len_high);
+ o->snapset_attr_len_high = max(o->snapset_attr_len_low,
+ o->snapset_attr_len_high);
+ o->pglog_omap_len_high = max(o->pglog_omap_len_low,
+ o->pglog_omap_len_high);
+ o->pglog_dup_omap_len_high = max(o->pglog_dup_omap_len_low,
+ o->pglog_dup_omap_len_high);
+ o->_fastinfo_omap_len_high = max(o->_fastinfo_omap_len_low,
+ o->_fastinfo_omap_len_high);
+
+ int r = os->mkfs();
+ if (r < 0)
+ throw std::system_error(-r, std::system_category(), "mkfs failed");
+
+ r = os->mount();
+ if (r < 0)
+ throw std::system_error(-r, std::system_category(), "mount failed");
+
+ // create shared collections up to osd_pool_default_pg_num
+ if (o->single_pool_mode) {
+ uint64_t count = g_conf().get_val<uint64_t>("osd_pool_default_pg_num");
+ if (count > td->o.nr_files)
+ count = td->o.nr_files;
+ init_collections(os, Collection::MIN_POOL_ID, collections, count);
+ }
+}
+
+Engine::~Engine()
+{
+ ceph_assert(!ref_count);
+}
+
+struct Object {
+ ghobject_t oid;
+ Collection& coll;
+
+ Object(const char* name, Collection& coll)
+ : oid(hobject_t(name, "", CEPH_NOSNAP, coll.pg.ps(), coll.pg.pool(), "")),
+ coll(coll) {}
+};
+
+/// treat each fio job either like a separate pool with its own collections and objects
+/// or just a client using its own objects from the shared pool
+struct Job {
+ Engine* engine; //< shared ptr to the global Engine
+ const unsigned subjob_number; //< subjob num
+ std::vector<Collection> collections; //< job's private collections to spread objects over
+ std::vector<Object> objects; //< associate an object with each fio_file
+ std::vector<io_u*> events; //< completions for fio_ceph_os_event()
+ const bool unlink; //< unlink objects on destruction
+
+ bufferptr one_for_all_data; //< preallocated buffer long enough
+ //< to use for vairious operations
+ std::mutex throttle_lock;
+ const vector<unsigned> throttle_values;
+ const vector<unsigned> deferred_throttle_values;
+ std::chrono::duration<double> cycle_throttle_period;
+ mono_clock::time_point last = ceph::mono_clock::zero();
+ unsigned index = 0;
+
+ static vector<unsigned> parse_throttle_str(const char *p) {
+ vector<unsigned> ret;
+ if (p == nullptr) {
+ return ret;
+ }
+ ceph::for_each_substr(p, ",\"", [&ret] (auto &&s) mutable {
+ if (s.size() > 0) {
+ ret.push_back(std::stoul(std::string(s)));
+ }
+ });
+ return ret;
+ }
+ void check_throttle();
+
+ Job(Engine* engine, const thread_data* td);
+ ~Job();
+};
+
+Job::Job(Engine* engine, const thread_data* td)
+ : engine(engine),
+ subjob_number(td->subjob_number),
+ events(td->o.iodepth),
+ unlink(td->o.unlink),
+ throttle_values(
+ parse_throttle_str(static_cast<Options*>(td->eo)->throttle_values)),
+ deferred_throttle_values(
+ parse_throttle_str(static_cast<Options*>(td->eo)->deferred_throttle_values)),
+ cycle_throttle_period(
+ static_cast<Options*>(td->eo)->cycle_throttle_period)
+{
+ engine->ref();
+ auto o = static_cast<Options*>(td->eo);
+ unsigned long long max_data = max(o->oi_attr_len_high,
+ o->snapset_attr_len_high);
+ max_data = max(max_data, o->pglog_omap_len_high);
+ max_data = max(max_data, o->pglog_dup_omap_len_high);
+ max_data = max(max_data, o->_fastinfo_omap_len_high);
+ one_for_all_data = buffer::create(max_data);
+
+ std::vector<Collection>* colls;
+ // create private collections up to osd_pool_default_pg_num
+ if (!o->single_pool_mode) {
+ uint64_t count = g_conf().get_val<uint64_t>("osd_pool_default_pg_num");
+ if (count > td->o.nr_files)
+ count = td->o.nr_files;
+ // use the fio thread_number for our unique pool id
+ const uint64_t pool = Collection::MIN_POOL_ID + td->thread_number + 1;
+ init_collections(engine->os, pool, collections, count);
+ colls = &collections;
+ } else {
+ colls = &engine->collections;
+ }
+ const uint64_t file_size = td->o.size / max(1u, td->o.nr_files);
+ ObjectStore::Transaction t;
+
+ // create an object for each file in the job
+ objects.reserve(td->o.nr_files);
+ unsigned checked_or_preallocated = 0;
+ for (uint32_t i = 0; i < td->o.nr_files; i++) {
+ auto f = td->files[i];
+ f->real_file_size = file_size;
+ f->engine_pos = i;
+
+ // associate each object with a collection in a round-robin fashion.
+ auto& coll = (*colls)[i % colls->size()];
+
+ objects.emplace_back(f->file_name, coll);
+ if (o->preallocate_files) {
+ auto& oid = objects.back().oid;
+ t.touch(coll.cid, oid);
+ t.truncate(coll.cid, oid, file_size);
+ int r = engine->os->queue_transaction(coll.ch, std::move(t));
+ if (r) {
+ engine->deref();
+ throw std::system_error(r, std::system_category(), "job init");
+ }
+ }
+ if (o->check_files) {
+ auto& oid = objects.back().oid;
+ struct stat st;
+ int r = engine->os->stat(coll.ch, oid, &st);
+ if (r || ((unsigned)st.st_size) != file_size) {
+ derr << "Problem checking " << oid << ", r=" << r
+ << ", st.st_size=" << st.st_size
+ << ", file_size=" << file_size
+ << ", nr_files=" << td->o.nr_files << dendl;
+ engine->deref();
+ throw std::system_error(
+ r, std::system_category(), "job init -- cannot check file");
+ }
+ }
+ if (o->check_files || o->preallocate_files) {
+ ++checked_or_preallocated;
+ }
+ }
+ if (o->check_files) {
+ derr << "fio_ceph_objectstore checked " << checked_or_preallocated
+ << " files"<< dendl;
+ }
+ if (o->preallocate_files ){
+ derr << "fio_ceph_objectstore preallocated " << checked_or_preallocated
+ << " files"<< dendl;
+ }
+}
+
+Job::~Job()
+{
+ if (unlink) {
+ ObjectStore::Transaction t;
+ bool failed = false;
+ // remove our objects
+ for (auto& obj : objects) {
+ t.remove(obj.coll.cid, obj.oid);
+ int r = engine->os->queue_transaction(obj.coll.ch, std::move(t));
+ if (r && !failed) {
+ derr << "job cleanup failed with " << cpp_strerror(-r) << dendl;
+ failed = true;
+ }
+ }
+ destroy_collections(engine->os, collections);
+ }
+ engine->deref();
+}
+
+void Job::check_throttle()
+{
+ if (subjob_number != 0)
+ return;
+
+ std::lock_guard<std::mutex> l(throttle_lock);
+ if (throttle_values.empty() && deferred_throttle_values.empty())
+ return;
+
+ if (ceph::mono_clock::is_zero(last) ||
+ ((cycle_throttle_period != cycle_throttle_period.zero()) &&
+ (ceph::mono_clock::now() - last) > cycle_throttle_period)) {
+ unsigned tvals = throttle_values.size() ? throttle_values.size() : 1;
+ unsigned dtvals = deferred_throttle_values.size() ? deferred_throttle_values.size() : 1;
+ if (!throttle_values.empty()) {
+ std::string val = std::to_string(throttle_values[index % tvals]);
+ std::cerr << "Setting bluestore_throttle_bytes to " << val << std::endl;
+ int r = engine->cct->_conf.set_val(
+ "bluestore_throttle_bytes",
+ val,
+ nullptr);
+ ceph_assert(r == 0);
+ }
+ if (!deferred_throttle_values.empty()) {
+ std::string val = std::to_string(deferred_throttle_values[(index / tvals) % dtvals]);
+ std::cerr << "Setting bluestore_deferred_throttle_bytes to " << val << std::endl;
+ int r = engine->cct->_conf.set_val(
+ "bluestore_throttle_deferred_bytes",
+ val,
+ nullptr);
+ ceph_assert(r == 0);
+ }
+ engine->cct->_conf.apply_changes(nullptr);
+ index++;
+ index %= tvals * dtvals;
+ last = ceph::mono_clock::now();
+ }
+}
+
+int fio_ceph_os_setup(thread_data* td)
+{
+ // if there are multiple jobs, they must run in the same process against a
+ // single instance of the ObjectStore. explicitly disable fio's default
+ // job-per-process configuration
+ td->o.use_thread = 1;
+
+ try {
+ // get or create the global Engine instance
+ auto engine = Engine::get_instance(td);
+ // create a Job for this thread
+ td->io_ops_data = new Job(engine, td);
+ } catch (std::exception& e) {
+ std::cerr << "setup failed with " << e.what() << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+void fio_ceph_os_cleanup(thread_data* td)
+{
+ auto job = static_cast<Job*>(td->io_ops_data);
+ td->io_ops_data = nullptr;
+ delete job;
+}
+
+
+io_u* fio_ceph_os_event(thread_data* td, int event)
+{
+ // return the requested event from fio_ceph_os_getevents()
+ auto job = static_cast<Job*>(td->io_ops_data);
+ return job->events[event];
+}
+
+int fio_ceph_os_getevents(thread_data* td, unsigned int min,
+ unsigned int max, const timespec* t)
+{
+ auto job = static_cast<Job*>(td->io_ops_data);
+ unsigned int events = 0;
+ io_u* u = NULL;
+ unsigned int i = 0;
+
+ // loop through inflight ios until we find 'min' completions
+ do {
+ io_u_qiter(&td->io_u_all, u, i) {
+ if (!(u->flags & IO_U_F_FLIGHT))
+ continue;
+
+ if (u->engine_data) {
+ u->engine_data = nullptr;
+ job->events[events] = u;
+ events++;
+ }
+ }
+ if (events >= min)
+ break;
+ usleep(100);
+ } while (1);
+
+ return events;
+}
+
+/// completion context for ObjectStore::queue_transaction()
+class UnitComplete : public Context {
+ io_u* u;
+ public:
+ explicit UnitComplete(io_u* u) : u(u) {}
+ void finish(int r) {
+ // mark the pointer to indicate completion for fio_ceph_os_getevents()
+ u->engine_data = reinterpret_cast<void*>(1ull);
+ }
+};
+
+enum fio_q_status fio_ceph_os_queue(thread_data* td, io_u* u)
+{
+ fio_ro_check(td, u);
+
+
+
+ auto o = static_cast<const Options*>(td->eo);
+ auto job = static_cast<Job*>(td->io_ops_data);
+ auto& object = job->objects[u->file->engine_pos];
+ auto& coll = object.coll;
+ auto& os = job->engine->os;
+
+ job->check_throttle();
+
+ if (u->ddir == DDIR_WRITE) {
+ // provide a hint if we're likely to read this data back
+ const int flags = td_rw(td) ? CEPH_OSD_OP_FLAG_FADVISE_WILLNEED : 0;
+
+ bufferlist bl;
+ bl.push_back(buffer::copy(reinterpret_cast<char*>(u->xfer_buf),
+ u->xfer_buflen ) );
+
+ map<string,bufferptr,less<>> attrset;
+ map<string, bufferlist> omaps;
+ // enqueue a write transaction on the collection's handle
+ ObjectStore::Transaction t;
+ char ver_key[64];
+
+ // fill attrs if any
+ if (o->oi_attr_len_high) {
+ ceph_assert(o->oi_attr_len_high >= o->oi_attr_len_low);
+ // fill with the garbage as we do not care of the actual content...
+ job->one_for_all_data.set_length(
+ ceph::util::generate_random_number(
+ o->oi_attr_len_low, o->oi_attr_len_high));
+ attrset["_"] = job->one_for_all_data;
+ }
+ if (o->snapset_attr_len_high) {
+ ceph_assert(o->snapset_attr_len_high >= o->snapset_attr_len_low);
+ job->one_for_all_data.set_length(
+ ceph::util::generate_random_number
+ (o->snapset_attr_len_low, o->snapset_attr_len_high));
+ attrset["snapset"] = job->one_for_all_data;
+
+ }
+ if (o->_fastinfo_omap_len_high) {
+ ceph_assert(o->_fastinfo_omap_len_high >= o->_fastinfo_omap_len_low);
+ // fill with the garbage as we do not care of the actual content...
+ job->one_for_all_data.set_length(
+ ceph::util::generate_random_number(
+ o->_fastinfo_omap_len_low, o->_fastinfo_omap_len_high));
+ omaps["_fastinfo"].append(job->one_for_all_data);
+ }
+
+ uint64_t pglog_trim_head = 0, pglog_trim_tail = 0;
+ uint64_t pglog_dup_trim_head = 0, pglog_dup_trim_tail = 0;
+ if (o->simulate_pglog) {
+
+ uint64_t pglog_ver_cnt = 0;
+ {
+ std::lock_guard<std::mutex> l(*coll.lock);
+ pglog_ver_cnt = coll.pglog_ver_head++;
+ if (o->pglog_omap_len_high &&
+ pglog_ver_cnt >=
+ coll.pglog_ver_tail +
+ g_conf()->osd_min_pg_log_entries + g_conf()->osd_pg_log_trim_min) {
+ pglog_trim_tail = coll.pglog_ver_tail;
+ coll.pglog_ver_tail = pglog_trim_head =
+ pglog_trim_tail + g_conf()->osd_pg_log_trim_min;
+
+ if (o->pglog_dup_omap_len_high &&
+ pglog_ver_cnt >=
+ coll.pglog_dup_ver_tail + g_conf()->osd_pg_log_dups_tracked +
+ g_conf()->osd_pg_log_trim_min) {
+ pglog_dup_trim_tail = coll.pglog_dup_ver_tail;
+ coll.pglog_dup_ver_tail = pglog_dup_trim_head =
+ pglog_dup_trim_tail + g_conf()->osd_pg_log_trim_min;
+ }
+ }
+ }
+
+ if (o->pglog_omap_len_high) {
+ ceph_assert(o->pglog_omap_len_high >= o->pglog_omap_len_low);
+ snprintf(ver_key, sizeof(ver_key),
+ "0000000011.%020llu", (unsigned long long)pglog_ver_cnt);
+ // fill with the garbage as we do not care of the actual content...
+ job->one_for_all_data.set_length(
+ ceph::util::generate_random_number(
+ o->pglog_omap_len_low, o->pglog_omap_len_high));
+ omaps[ver_key].append(job->one_for_all_data);
+ }
+ if (o->pglog_dup_omap_len_high) {
+ //insert dup
+ ceph_assert(o->pglog_dup_omap_len_high >= o->pglog_dup_omap_len_low);
+ for( auto i = pglog_trim_tail; i < pglog_trim_head; ++i) {
+ snprintf(ver_key, sizeof(ver_key),
+ "dup_0000000011.%020llu", (unsigned long long)i);
+ // fill with the garbage as we do not care of the actual content...
+ job->one_for_all_data.set_length(
+ ceph::util::generate_random_number(
+ o->pglog_dup_omap_len_low, o->pglog_dup_omap_len_high));
+ omaps[ver_key].append(job->one_for_all_data);
+ }
+ }
+ }
+
+ if (!attrset.empty()) {
+ t.setattrs(coll.cid, object.oid, attrset);
+ }
+ t.write(coll.cid, object.oid, u->offset, u->xfer_buflen, bl, flags);
+
+ set<string> rmkeys;
+ for( auto i = pglog_trim_tail; i < pglog_trim_head; ++i) {
+ snprintf(ver_key, sizeof(ver_key),
+ "0000000011.%020llu", (unsigned long long)i);
+ rmkeys.emplace(ver_key);
+ }
+ for( auto i = pglog_dup_trim_tail; i < pglog_dup_trim_head; ++i) {
+ snprintf(ver_key, sizeof(ver_key),
+ "dup_0000000011.%020llu", (unsigned long long)i);
+ rmkeys.emplace(ver_key);
+ }
+
+ if (rmkeys.size()) {
+ ghobject_t pgmeta_oid(coll.pg.make_pgmeta_oid());
+ t.omap_rmkeys(coll.cid, pgmeta_oid, rmkeys);
+ }
+
+ if (omaps.size()) {
+ ghobject_t pgmeta_oid(coll.pg.make_pgmeta_oid());
+ t.omap_setkeys(coll.cid, pgmeta_oid, omaps);
+ }
+ t.register_on_commit(new UnitComplete(u));
+ os->queue_transaction(coll.ch,
+ std::move(t));
+ return FIO_Q_QUEUED;
+ }
+
+ if (u->ddir == DDIR_READ) {
+ // ObjectStore reads are synchronous, so make the call and return COMPLETED
+ bufferlist bl;
+ int r = os->read(coll.ch, object.oid, u->offset, u->xfer_buflen, bl);
+ if (r < 0) {
+ u->error = r;
+ td_verror(td, u->error, "xfer");
+ } else {
+ bl.begin().copy(bl.length(), static_cast<char*>(u->xfer_buf));
+ u->resid = u->xfer_buflen - r;
+ }
+ return FIO_Q_COMPLETED;
+ }
+
+ derr << "WARNING: Only DDIR_READ and DDIR_WRITE are supported!" << dendl;
+ u->error = -EINVAL;
+ td_verror(td, u->error, "xfer");
+ return FIO_Q_COMPLETED;
+}
+
+int fio_ceph_os_commit(thread_data* td)
+{
+ // commit() allows the engine to batch up queued requests to be submitted all
+ // at once. it would be natural for queue() to collect transactions in a list,
+ // and use commit() to pass them all to ObjectStore::queue_transactions(). but
+ // because we spread objects over multiple collections, we a) need to use a
+ // different sequencer for each collection, and b) are less likely to see a
+ // benefit from batching requests within a collection
+ return 0;
+}
+
+// open/close are noops. we set the FIO_DISKLESSIO flag in ioengine_ops to
+// prevent fio from creating the files
+int fio_ceph_os_open(thread_data* td, fio_file* f) { return 0; }
+int fio_ceph_os_close(thread_data* td, fio_file* f) { return 0; }
+
+int fio_ceph_os_io_u_init(thread_data* td, io_u* u)
+{
+ // no data is allocated, we just use the pointer as a boolean 'completed' flag
+ u->engine_data = nullptr;
+ return 0;
+}
+
+void fio_ceph_os_io_u_free(thread_data* td, io_u* u)
+{
+ u->engine_data = nullptr;
+}
+
+
+// ioengine_ops for get_ioengine()
+struct ceph_ioengine : public ioengine_ops {
+ ceph_ioengine() : ioengine_ops({}) {
+ name = "ceph-os";
+ version = FIO_IOOPS_VERSION;
+ flags = FIO_DISKLESSIO;
+ setup = fio_ceph_os_setup;
+ queue = fio_ceph_os_queue;
+ commit = fio_ceph_os_commit;
+ getevents = fio_ceph_os_getevents;
+ event = fio_ceph_os_event;
+ cleanup = fio_ceph_os_cleanup;
+ open_file = fio_ceph_os_open;
+ close_file = fio_ceph_os_close;
+ io_u_init = fio_ceph_os_io_u_init;
+ io_u_free = fio_ceph_os_io_u_free;
+ options = ceph_options.data();
+ option_struct_size = sizeof(struct Options);
+ }
+};
+
+} // anonymous namespace
+
+extern "C" {
+// the exported fio engine interface
+void get_ioengine(struct ioengine_ops** ioengine_ptr) {
+ static ceph_ioengine ioengine;
+ *ioengine_ptr = &ioengine;
+}
+} // extern "C"
diff --git a/src/test/fio/fio_librgw.cc b/src/test/fio/fio_librgw.cc
new file mode 100644
index 000000000..bac4ff2da
--- /dev/null
+++ b/src/test/fio/fio_librgw.cc
@@ -0,0 +1,540 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <vector>
+#include <functional>
+#include <iostream>
+
+#include <semaphore.h> // XXX kill this?
+
+#include "fmt/include/fmt/format.h"
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+//#include "rgw/rgw_file.h"
+//#include "rgw/rgw_lib_frontend.h" // direct requests
+
+/* naughty fio.h leaks min and max as C macros--include it last */
+#include <fio.h>
+#include <optgroup.h>
+#undef min
+#undef max
+
+namespace {
+
+ struct librgw_iou {
+ struct io_u *io_u;
+ int io_complete;
+ };
+
+ struct librgw_data {
+ io_u** aio_events;
+ librgw_t rgw_h;
+ rgw_fs* fs;
+ rgw_file_handle* bucket_fh;
+
+ std::vector<rgw_file_handle*> fh_vec;
+
+ librgw_data(thread_data* td)
+ : rgw_h(nullptr), fs(nullptr), bucket_fh(nullptr)
+ {
+ auto size = td->o.iodepth * sizeof(io_u*);
+ aio_events = static_cast<io_u**>(malloc(size));
+ memset(aio_events, 0, size);
+ }
+
+ void save_handle(rgw_file_handle* fh) {
+ fh_vec.push_back(fh);
+ }
+
+ void release_handles() {
+ for (auto object_fh : fh_vec) {
+ rgw_fh_rele(fs, object_fh, RGW_FH_RELE_FLAG_NONE);
+ }
+ fh_vec.clear();
+ }
+
+ ~librgw_data() {
+ free(aio_events);
+ }
+ };
+
+ struct opt_struct {
+ struct thread_data *td;
+
+ const char* config; /* can these be std::strings? */
+ const char* cluster;
+ const char* name; // instance?
+ const char* init_args;
+ const char* access_key;
+ const char* secret_key;
+ const char* userid;
+ const char* bucket_name;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+ };
+
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+/* borrowed from fio_ceph_objectstore */
+ template <class F>
+ fio_option make_option(F&& func)
+ {
+ // zero-initialize and set common defaults
+ auto o = fio_option{};
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ func(std::ref(o));
+ return o;
+ }
+
+ static std::vector<fio_option> options = {
+ make_option([] (fio_option& o) {
+ o.name = "ceph_conf";
+ o.lname = "ceph configuration file";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "Path to ceph.conf file";
+ o.off1 = offsetof(opt_struct, config);
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "ceph_name";
+ o.lname = "ceph instance name";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "Name of this program instance";
+ o.off1 = offsetof(opt_struct, name);
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "ceph_cluster";
+ o.lname = "ceph cluster name";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "Name of ceph cluster (default=ceph)";
+ o.off1 = offsetof(opt_struct, cluster);
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "ceph_init_args";
+ o.lname = "ceph init args";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "Extra ceph arguments (e.g., -d --debug-rgw=16)";
+ o.off1 = offsetof(opt_struct, init_args);
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "access_key";
+ o.lname = "AWS access key";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "AWS access key";
+ o.off1 = offsetof(opt_struct, access_key);
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "secret_key";
+ o.lname = "AWS secret key";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "AWS secret key";
+ o.off1 = offsetof(opt_struct, secret_key);
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "userid";
+ o.lname = "userid";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "userid corresponding to access key";
+ o.off1 = offsetof(opt_struct, userid);
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ }),
+ make_option([] (fio_option& o) {
+ o.name = "bucket_name";
+ o.lname = "S3 bucket";
+ o.type = FIO_OPT_STR_STORE;
+ o.help = "S3 bucket to operate on";
+ o.off1 = offsetof(opt_struct, bucket_name);
+ o.category = FIO_OPT_C_ENGINE;
+ o.group = FIO_OPT_G_INVALID;
+ }),
+ {} // fio expects a 'null'-terminated list
+ };
+
+ struct save_args {
+ int argc;
+ char *argv[8];
+ save_args() : argc(1)
+ {
+ argv[0] = strdup("librgw");
+ for (int ix = 1; ix < 8; ++ix) {
+ argv[ix] = nullptr;
+ }
+ }
+
+ void push_arg(const std::string sarg) {
+ argv[argc++] = strdup(sarg.c_str());
+ }
+
+ ~save_args() {
+ for (int ix = 0; ix < argc; ++ix) {
+ argv[ix] = nullptr;
+ }
+ }
+ } args;
+
+/*
+ * It looks like the setup function is called once, on module load.
+ * It's not documented in the skeleton driver.
+ */
+ static int fio_librgw_setup(struct thread_data* td)
+ {
+ opt_struct& o = *(reinterpret_cast<opt_struct*>(td->eo));
+ librgw_data* data = nullptr;
+ int r = 0;
+
+ dprint(FD_IO, "fio_librgw_setup\n");
+
+ if (! td->io_ops_data) {
+ data = new librgw_data(td);
+
+ /* init args */
+ std::string sopt;
+ if (o.config) {
+ sopt = fmt::format("--conf={}", o.config);
+ args.push_arg(sopt);
+ }
+ std::cout << o.name << std::endl;
+ if (o.name) {
+ sopt = fmt::format("--name={}", o.name);
+ args.push_arg(sopt);
+ }
+ if (o.cluster) {
+ sopt = fmt::format("--cluster={}", o.cluster);
+ args.push_arg(sopt);
+ }
+ if (o.init_args) {
+ args.push_arg(std::string(o.init_args));
+ }
+
+ r = librgw_create(&data->rgw_h, args.argc, args.argv);
+ if (!! r) {
+ dprint(FD_IO, "librgw_create failed\n");
+ return r;
+ }
+
+ r = rgw_mount2(data->rgw_h, o.userid, o.access_key, o.secret_key, "/",
+ &data->fs, RGW_MOUNT_FLAG_NONE);
+ if (!! r) {
+ dprint(FD_IO, "rgw_mount2 failed\n");
+ return r;
+ }
+
+ /* go ahead and lookup the bucket as well */
+ r = rgw_lookup(data->fs, data->fs->root_fh, o.bucket_name,
+ &data->bucket_fh, nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! data->bucket_fh) {
+ dprint(FD_IO, "rgw_lookup on bucket %s failed, will create\n",
+ o.bucket_name);
+
+ struct stat st;
+ st.st_uid = o.owner_uid;
+ st.st_gid = o.owner_gid;
+ st.st_mode = 755;
+
+ r = rgw_mkdir(data->fs, data->fs->root_fh, o.bucket_name,
+ &st, create_mask, &data->bucket_fh, RGW_MKDIR_FLAG_NONE);
+ if (! data->bucket_fh) {
+ dprint(FD_IO, "rgw_mkdir for bucket %s failed\n", o.bucket_name);
+ return EINVAL;
+ }
+ }
+
+ td->io_ops_data = data;
+ }
+
+ td->o.use_thread = 1;
+
+ if (r != 0) {
+ abort();
+ }
+
+ return r;
+ }
+
+/*
+ * The init function is called once per thread/process, and should set up
+ * any structures that this io engine requires to keep track of io. Not
+ * required.
+ */
+ static int fio_librgw_init(struct thread_data *td)
+ {
+ dprint(FD_IO, "fio_librgw_init\n");
+ return 0;
+ }
+
+/*
+ * This is paired with the ->init() function and is called when a thread is
+ * done doing io. Should tear down anything setup by the ->init() function.
+ * Not required.
+ *
+ * N.b., the cohort driver made this idempotent by allocating data in
+ * setup, clearing data here if present, and doing nothing in the
+ * subsequent per-thread invocations.
+ */
+ static void fio_librgw_cleanup(struct thread_data *td)
+ {
+ int r = 0;
+
+ dprint(FD_IO, "fio_librgw_cleanup\n");
+
+ /* cleanup specific data */
+ librgw_data* data = static_cast<librgw_data*>(td->io_ops_data);
+ if (data) {
+
+ /* release active handles */
+ data->release_handles();
+
+ if (data->bucket_fh) {
+ r = rgw_fh_rele(data->fs, data->bucket_fh, 0 /* flags */);
+ }
+ r = rgw_umount(data->fs, RGW_UMOUNT_FLAG_NONE);
+ librgw_shutdown(data->rgw_h);
+ td->io_ops_data = nullptr;
+ delete data;
+ }
+ }
+
+/*
+ * The ->prep() function is called for each io_u prior to being submitted
+ * with ->queue(). This hook allows the io engine to perform any
+ * preparatory actions on the io_u, before being submitted. Not required.
+ */
+ static int fio_librgw_prep(struct thread_data *td, struct io_u *io_u)
+ {
+ return 0;
+ }
+
+/*
+ * The ->event() hook is called to match an event number with an io_u.
+ * After the core has called ->getevents() and it has returned eg 3,
+ * the ->event() hook must return the 3 events that have completed for
+ * subsequent calls to ->event() with [0-2]. Required.
+ */
+ static struct io_u *fio_librgw_event(struct thread_data *td, int event)
+ {
+ return NULL;
+ }
+
+/*
+ * The ->getevents() hook is used to reap completion events from an async
+ * io engine. It returns the number of completed events since the last call,
+ * which may then be retrieved by calling the ->event() hook with the event
+ * numbers. Required.
+ */
+ static int fio_librgw_getevents(struct thread_data *td, unsigned int min,
+ unsigned int max, const struct timespec *t)
+ {
+ return 0;
+ }
+
+/*
+ * The ->cancel() hook attempts to cancel the io_u. Only relevant for
+ * async io engines, and need not be supported.
+ */
+ static int fio_librgw_cancel(struct thread_data *td, struct io_u *io_u)
+ {
+ return 0;
+ }
+
+/*
+ * The ->queue() hook is responsible for initiating io on the io_u
+ * being passed in. If the io engine is a synchronous one, io may complete
+ * before ->queue() returns. Required.
+ *
+ * The io engine must transfer in the direction noted by io_u->ddir
+ * to the buffer pointed to by io_u->xfer_buf for as many bytes as
+ * io_u->xfer_buflen. Residual data count may be set in io_u->resid
+ * for a short read/write.
+ */
+ static enum fio_q_status fio_librgw_queue(struct thread_data *td,
+ struct io_u *io_u)
+ {
+ librgw_data* data = static_cast<librgw_data*>(td->io_ops_data);
+ const char* object = io_u->file->file_name;
+ struct rgw_file_handle* object_fh = nullptr;
+ size_t nbytes;
+ int r = 0;
+
+ /*
+ * Double sanity check to catch errant write on a readonly setup
+ */
+ fio_ro_check(td, io_u);
+
+ if (io_u->ddir == DDIR_WRITE) {
+ /* Do full write cycle */
+ r = rgw_lookup(data->fs, data->bucket_fh, object, &object_fh, nullptr, 0,
+ RGW_LOOKUP_FLAG_CREATE);
+ if (!! r) {
+ dprint(FD_IO, "rgw_lookup failed to create filehandle for %s\n",
+ object);
+ goto out;
+ }
+
+ r = rgw_open(data->fs, object_fh, 0 /* posix flags */, 0 /* flags */);
+ if (!! r) {
+ dprint(FD_IO, "rgw_open failed to create filehandle for %s\n",
+ object);
+ rgw_fh_rele(data->fs, object_fh, RGW_FH_RELE_FLAG_NONE);
+ goto out;
+ }
+
+ /* librgw can write at any offset, but only sequentially
+ * starting at 0, in one open/write/close cycle */
+ r = rgw_write(data->fs, object_fh, 0, io_u->xfer_buflen, &nbytes,
+ (void*) io_u->xfer_buf, RGW_WRITE_FLAG_NONE);
+ if (!! r) {
+ dprint(FD_IO, "rgw_write failed for %s\n",
+ object);
+ }
+
+ r = rgw_close(data->fs, object_fh, 0 /* flags */);
+
+ /* object_fh is closed but still reachable, save it */
+ data->save_handle(object_fh);
+ } else if (io_u->ddir == DDIR_READ) {
+
+ r = rgw_lookup(data->fs, data->bucket_fh, object, &object_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (!! r) {
+ dprint(FD_IO, "rgw_lookup failed to create filehandle for %s\n",
+ object);
+ goto out;
+ }
+
+ r = rgw_open(data->fs, object_fh, 0 /* posix flags */, 0 /* flags */);
+ if (!! r) {
+ dprint(FD_IO, "rgw_open failed to create filehandle for %s\n",
+ object);
+ rgw_fh_rele(data->fs, object_fh, RGW_FH_RELE_FLAG_NONE);
+ goto out;
+ }
+
+ r = rgw_read(data->fs, object_fh, io_u->offset, io_u->xfer_buflen,
+ &nbytes, io_u->xfer_buf, RGW_READ_FLAG_NONE);
+ if (!! r) {
+ dprint(FD_IO, "rgw_read failed for %s\n",
+ object);
+ }
+ } else {
+ dprint(FD_IO, "%s: Warning: unhandled ddir: %d\n", __func__,
+ io_u->ddir);
+ }
+
+ if (object_fh) {
+ r = rgw_close(data->fs, object_fh, 0 /* flags */);
+
+ /* object_fh is closed but still reachable, save it */
+ data->save_handle(object_fh);
+ }
+
+ out:
+ /*
+ * Could return FIO_Q_QUEUED for a queued request,
+ * FIO_Q_COMPLETED for a completed request, and FIO_Q_BUSY
+ * if we could queue no more at this point (you'd have to
+ * define ->commit() to handle that.
+ */
+ return FIO_Q_COMPLETED;
+ }
+
+ int fio_librgw_commit(thread_data* td)
+ {
+ // commit() allows the engine to batch up queued requests to be submitted all
+ // at once. it would be natural for queue() to collect transactions in a list,
+ // and use commit() to pass them all to ObjectStore::queue_transactions(). but
+ // because we spread objects over multiple collections, we a) need to use a
+ // different sequencer for each collection, and b) are less likely to see a
+ // benefit from batching requests within a collection
+ return 0;
+ }
+
+/*
+ * Hook for opening the given file. Unless the engine has special
+ * needs, it usually just provides generic_open_file() as the handler.
+ */
+ static int fio_librgw_open(struct thread_data *td, struct fio_file *f)
+ {
+ /* for now, let's try to avoid doing open/close in these hooks */
+ return 0;
+ }
+
+/*
+ * Hook for closing a file. See fio_librgw_open().
+ */
+ static int fio_librgw_close(struct thread_data *td, struct fio_file *f)
+ {
+ /* for now, let's try to avoid doing open/close in these hooks */
+ return 0;
+ }
+
+/* XXX next two probably not needed */
+ int fio_librgw_io_u_init(thread_data* td, io_u* u)
+ {
+ // no data is allocated, we just use the pointer as a boolean 'completed' flag
+ u->engine_data = nullptr;
+ return 0;
+ }
+
+ void fio_librgw_io_u_free(thread_data* td, io_u* u)
+ {
+ u->engine_data = nullptr;
+ }
+
+ struct librgw_ioengine : public ioengine_ops
+ {
+ librgw_ioengine() : ioengine_ops({}) {
+ name = "librgw";
+ version = FIO_IOOPS_VERSION;
+ flags = FIO_DISKLESSIO;
+ setup = fio_librgw_setup;
+ init = fio_librgw_init;
+ queue = fio_librgw_queue;
+ commit = fio_librgw_commit;
+ getevents = fio_librgw_getevents;
+ event = fio_librgw_event;
+ cleanup = fio_librgw_cleanup;
+ open_file = fio_librgw_open;
+ close_file = fio_librgw_close;
+ io_u_init = fio_librgw_io_u_init;
+ io_u_free = fio_librgw_io_u_free;
+ options = ::options.data();
+ option_struct_size = sizeof(opt_struct);
+ }
+ };
+
+} // namespace
+
+extern "C" {
+// the exported fio engine interface
+ void get_ioengine(struct ioengine_ops** ioengine_ptr) {
+ static librgw_ioengine ioengine;
+ *ioengine_ptr = &ioengine;
+ }
+} // extern "C"
diff --git a/src/test/fio/ring_buffer.h b/src/test/fio/ring_buffer.h
new file mode 100644
index 000000000..0e1eb62be
--- /dev/null
+++ b/src/test/fio/ring_buffer.h
@@ -0,0 +1,102 @@
+/*
+ * Very simple and fast lockless ring buffer implementatation for
+ * one producer and one consumer.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Do not overcomplicate, choose generic x86 case */
+#define L1_CACHE_BYTES 64
+#define __cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES)))
+
+struct ring_buffer
+{
+ unsigned int read_idx __cacheline_aligned;
+ unsigned int write_idx __cacheline_aligned;
+ unsigned int size;
+ unsigned int low_mask;
+ unsigned int high_mask;
+ unsigned int bit_shift;
+ void *data_ptr;
+};
+
+static inline unsigned int upper_power_of_two(unsigned int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+
+ return v;
+}
+
+static inline int ring_buffer_init(struct ring_buffer* rbuf, unsigned int size)
+{
+ /* Must be pow2 */
+ if (((size-1) & size))
+ size = upper_power_of_two(size);
+
+ size *= sizeof(void *);
+ rbuf->data_ptr = malloc(size);
+ rbuf->size = size;
+ rbuf->read_idx = 0;
+ rbuf->write_idx = 0;
+ rbuf->bit_shift = __builtin_ffs(sizeof(void *))-1;
+ rbuf->low_mask = rbuf->size - 1;
+ rbuf->high_mask = rbuf->size * 2 - 1;
+
+ return 0;
+}
+
+static inline void ring_buffer_deinit(struct ring_buffer* rbuf)
+{
+ free(rbuf->data_ptr);
+}
+
+static inline unsigned int ring_buffer_used_size(const struct ring_buffer* rbuf)
+{
+ __sync_synchronize();
+ return ((rbuf->write_idx - rbuf->read_idx) & rbuf->high_mask) >>
+ rbuf->bit_shift;
+}
+
+static inline void ring_buffer_enqueue(struct ring_buffer* rbuf, void *ptr)
+{
+
+ unsigned int idx;
+
+ /*
+ * Be aware: we do not check that buffer can be full,
+ * assume user of the ring buffer can't submit more.
+ */
+
+ idx = rbuf->write_idx & rbuf->low_mask;
+ *(void **)((uintptr_t)rbuf->data_ptr + idx) = ptr;
+ /* Barrier to be sure stored pointer will be seen properly */
+ __sync_synchronize();
+ rbuf->write_idx = (rbuf->write_idx + sizeof(ptr)) & rbuf->high_mask;
+}
+
+static inline void *ring_buffer_dequeue(struct ring_buffer* rbuf)
+{
+
+ unsigned idx;
+ void *ptr;
+
+ /*
+ * Be aware: we do not check that buffer can be empty,
+ * assume user of the ring buffer called ring_buffer_used_size(),
+ * which returns actual used size and introduces memory barrier
+ * explicitly.
+ */
+
+ idx = rbuf->read_idx & rbuf->low_mask;
+ ptr = *(void **)((uintptr_t)rbuf->data_ptr + idx);
+ rbuf->read_idx = (rbuf->read_idx + sizeof(ptr)) & rbuf->high_mask;
+
+ return ptr;
+}
diff --git a/src/test/fooclass.cc b/src/test/fooclass.cc
new file mode 100644
index 000000000..2db2d815b
--- /dev/null
+++ b/src/test/fooclass.cc
@@ -0,0 +1,48 @@
+
+
+
+#include <iostream>
+#include <string.h>
+#include <stdlib.h>
+
+#include "objclass/objclass.h"
+
+CLS_VER(1,0)
+CLS_NAME(foo)
+
+cls_handle_t h_class;
+
+cls_method_handle_t h_foo;
+
+int foo_method(cls_method_context_t ctx, char *indata, int datalen,
+ char **outdata, int *outdatalen)
+{
+ int i;
+
+ cls_log("hello world, this is foo");
+ cls_log("indata=%s", indata);
+
+ *outdata = (char *)malloc(128);
+ for (i=0; i<strlen(indata) + 1; i++) {
+ if (indata[i] == '1') {
+ (*outdata)[i] = 'I';
+ } else {
+ (*outdata)[i] = indata[i];
+ }
+ }
+ *outdatalen = strlen(*outdata) + 1;
+ cls_log("outdata=%s", *outdata);
+
+ return 0;
+}
+
+void class_init()
+{
+ cls_log("Loaded foo class!");
+
+ cls_register("foo", &h_class);
+ cls_register_method(h_class, "foo", foo_method, &h_foo);
+
+ return;
+}
+
diff --git a/src/test/formatter.cc b/src/test/formatter.cc
new file mode 100644
index 000000000..1e78a18a0
--- /dev/null
+++ b/src/test/formatter.cc
@@ -0,0 +1,354 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "common/Formatter.h"
+#include "common/HTMLFormatter.h"
+
+#include <sstream>
+#include <string>
+
+using namespace ceph;
+using std::ostringstream;
+
+TEST(JsonFormatter, Simple1) {
+ ostringstream oss;
+ JSONFormatter fmt(false);
+ fmt.open_object_section("foo");
+ fmt.dump_int("a", 1);
+ fmt.dump_int("b", 2);
+ fmt.dump_int("c", 3);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "{\"a\":1,\"b\":2,\"c\":3}");
+}
+
+TEST(JsonFormatter, Simple2) {
+ ostringstream oss;
+ JSONFormatter fmt(false);
+ fmt.open_object_section("foo");
+ fmt.open_object_section("bar");
+ fmt.dump_int("int", 0xf00000000000ll);
+ fmt.dump_unsigned("unsigned", 0x8000000000000001llu);
+ fmt.dump_float("float", 1.234);
+ fmt.close_section();
+ fmt.dump_string("string", "str");
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "{\"bar\":{\"int\":263882790666240,\
+\"unsigned\":9223372036854775809,\"float\":1.234},\
+\"string\":\"str\"}");
+}
+
+TEST(JsonFormatter, CunningFloats) {
+ ostringstream oss;
+ JSONFormatter fmt(false);
+ fmt.open_object_section("foo");
+ fmt.dump_float("long", 1.0 / 7);
+ fmt.dump_float("big", 12345678901234567890.0);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "{\"long\":0.14285714285714285,\"big\":1.2345678901234567e+19}");
+}
+
+TEST(JsonFormatter, Empty) {
+ ostringstream oss;
+ JSONFormatter fmt(false);
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "");
+}
+
+TEST(XmlFormatter, Simple1) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+ fmt.open_object_section("foo");
+ fmt.dump_int("a", 1);
+ fmt.dump_int("b", 2);
+ fmt.dump_int("c", 3);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><a>1</a><b>2</b><c>3</c></foo>");
+}
+
+TEST(XmlFormatter, Simple2) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+ fmt.open_object_section("foo");
+ fmt.open_object_section("bar");
+ fmt.dump_int("int", 0xf00000000000ll);
+ fmt.dump_unsigned("unsigned", 0x8000000000000001llu);
+ fmt.dump_float("float", 1.234);
+ fmt.close_section();
+ fmt.dump_string("string", "str");
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><bar>\
+<int>263882790666240</int>\
+<unsigned>9223372036854775809</unsigned>\
+<float>1.234</float>\
+</bar><string>str</string>\
+</foo>");
+}
+
+TEST(XmlFormatter, Empty) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "");
+}
+
+TEST(XmlFormatter, DumpStream1) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+ fmt.dump_stream("blah") << "hithere";
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<blah>hithere</blah>");
+}
+
+TEST(XmlFormatter, DumpStream2) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><blah>hithere</blah></foo>");
+}
+
+TEST(XmlFormatter, DumpStream3) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><blah>hithere</blah><pi>0.128</pi></foo>");
+}
+
+TEST(XmlFormatter, DTD) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+
+ fmt.output_header();
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<foo><blah>hithere</blah><pi>0.128</pi></foo>");
+}
+
+TEST(XmlFormatter, Clear) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+
+ fmt.output_header();
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<foo><blah>hithere</blah><pi>0.128</pi></foo>");
+
+ ostringstream oss2;
+ fmt.flush(oss2);
+ ASSERT_EQ(oss2.str(), "");
+
+ ostringstream oss3;
+ fmt.reset();
+ fmt.flush(oss3);
+ ASSERT_EQ(oss3.str(), "");
+}
+
+TEST(XmlFormatter, NamespaceTest) {
+ ostringstream oss;
+ XMLFormatter fmt(false);
+
+ fmt.output_header();
+ fmt.open_array_section_in_ns("foo",
+ "http://s3.amazonaws.com/doc/2006-03-01/");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<foo xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
+ "<blah>hithere</blah><pi>0.128</pi></foo>");
+}
+
+TEST(XmlFormatter, DumpFormatNameSpaceTest) {
+ ostringstream oss1;
+ XMLFormatter fmt(false);
+
+ fmt.dump_format_ns("foo",
+ "http://s3.amazonaws.com/doc/2006-03-01/",
+ "%s","bar");
+ fmt.flush(oss1);
+ ASSERT_EQ(oss1.str(),
+ "<foo xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">bar</foo>");
+
+ // Testing with a null ns..should be same as dump format
+ ostringstream oss2;
+ fmt.reset();
+ fmt.dump_format_ns("foo",NULL,"%s","bar");
+ fmt.flush(oss2);
+ ASSERT_EQ(oss2.str(),"<foo>bar</foo>");
+}
+
+TEST(HtmlFormatter, Simple1) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+ fmt.open_object_section("foo");
+ fmt.dump_int("a", 1);
+ fmt.dump_int("b", 2);
+ fmt.dump_int("c", 3);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><li>a: 1</li><li>b: 2</li><li>c: 3</li></foo>");
+}
+
+TEST(HtmlFormatter, Simple2) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+ fmt.open_object_section("foo");
+ fmt.open_object_section("bar");
+ fmt.dump_int("int", 0xf00000000000ll);
+ fmt.dump_unsigned("unsigned", 0x8000000000000001llu);
+ fmt.dump_float("float", 1.234);
+ fmt.close_section();
+ fmt.dump_string("string", "str");
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><bar>\
+<li>int: 263882790666240</li>\
+<li>unsigned: 9223372036854775809</li>\
+<li>float: 1.234</li>\
+</bar><li>string: str</li>\
+</foo>");
+}
+
+TEST(HtmlFormatter, Empty) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "");
+}
+
+TEST(HtmlFormatter, DumpStream1) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+ fmt.dump_stream("blah") << "hithere";
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<li>blah: hithere</li>");
+}
+
+TEST(HtmlFormatter, DumpStream2) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><li>blah: hithere</li></foo>");
+}
+
+TEST(HtmlFormatter, DumpStream3) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<foo><li>blah: hithere</li><li>pi: 0.128</li></foo>");
+}
+
+TEST(HtmlFormatter, DTD) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+
+ fmt.write_raw_data(HTMLFormatter::XML_1_DTD);
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<foo><li>blah: hithere</li><li>pi: 0.128</li></foo>");
+}
+
+TEST(HtmlFormatter, Clear) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+
+ fmt.write_raw_data(HTMLFormatter::XML_1_DTD);
+ fmt.open_array_section("foo");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<foo><li>blah: hithere</li><li>pi: 0.128</li></foo>");
+
+ ostringstream oss2;
+ fmt.flush(oss2);
+ ASSERT_EQ(oss2.str(), "");
+
+ ostringstream oss3;
+ fmt.reset();
+ fmt.flush(oss3);
+ ASSERT_EQ(oss3.str(), "");
+}
+
+TEST(HtmlFormatter, NamespaceTest) {
+ ostringstream oss;
+ HTMLFormatter fmt(false);
+
+ fmt.write_raw_data(HTMLFormatter::XML_1_DTD);
+ fmt.open_array_section_in_ns("foo",
+ "http://s3.amazonaws.com/doc/2006-03-01/");
+ fmt.dump_stream("blah") << "hithere";
+ fmt.dump_float("pi", 0.128);
+ fmt.close_section();
+ fmt.flush(oss);
+ ASSERT_EQ(oss.str(), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<foo xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
+ "<li>blah: hithere</li><li>pi: 0.128</li></foo>");
+}
+
+TEST(HtmlFormatter, DumpFormatNameSpaceTest) {
+ ostringstream oss1;
+ HTMLFormatter fmt(false);
+
+ fmt.dump_format_ns("foo",
+ "http://s3.amazonaws.com/doc/2006-03-01/",
+ "%s","bar");
+ fmt.flush(oss1);
+ ASSERT_EQ(oss1.str(),
+ "<li xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">foo: bar</li>");
+
+ // Testing with a null ns..should be same as dump format
+ ostringstream oss2;
+ fmt.reset();
+ fmt.dump_format_ns("foo",NULL,"%s","bar");
+ fmt.flush(oss2);
+ ASSERT_EQ(oss2.str(),"<li>foo: bar</li>");
+}
diff --git a/src/test/fs/CMakeLists.txt b/src/test/fs/CMakeLists.txt
new file mode 100644
index 000000000..70ff64afd
--- /dev/null
+++ b/src/test/fs/CMakeLists.txt
@@ -0,0 +1,20 @@
+if(${WITH_CEPHFS})
+ # unittest_mds_types
+ add_executable(unittest_mds_types
+ mds_types.cc
+ )
+ add_ceph_unittest(unittest_mds_types)
+ target_link_libraries(unittest_mds_types global)
+
+ add_executable(ceph_test_trim_caps
+ test_trim_caps.cc
+ )
+ target_link_libraries(ceph_test_trim_caps ceph-common cephfs)
+ install(TARGETS ceph_test_trim_caps DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+ add_executable(ceph_test_ino_release_cb
+ test_ino_release_cb.cc
+ )
+ target_link_libraries(ceph_test_ino_release_cb ceph-common cephfs)
+ install(TARGETS ceph_test_ino_release_cb DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(${WITH_CEPHFS})
diff --git a/src/test/fs/mds_types.cc b/src/test/fs/mds_types.cc
new file mode 100644
index 000000000..cef54a799
--- /dev/null
+++ b/src/test/fs/mds_types.cc
@@ -0,0 +1,252 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Red Hat
+ *
+ * Author: Greg Farnum <greg@inktank.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "mds/mdstypes.h"
+#include "mds/inode_backtrace.h"
+
+TEST(inode_t, compare_equal)
+{
+ inode_t foo{};
+ inode_t bar{};
+ int compare_r;
+ bool divergent;
+ compare_r = foo.compare(bar, &divergent);
+ EXPECT_EQ(0, compare_r);
+ EXPECT_FALSE(divergent);
+ compare_r = bar.compare(foo, &divergent);
+ EXPECT_EQ(0, compare_r);
+ EXPECT_FALSE(divergent);
+
+
+ foo.ino = 1234;
+ foo.ctime.set_from_double(10.0);
+ foo.mode = 0777;
+ foo.uid = 42;
+ foo.gid = 43;
+ foo.nlink = 3;
+ foo.version = 3;
+
+ bar = foo;
+ compare_r = foo.compare(bar, &divergent);
+ EXPECT_EQ(0, compare_r);
+ EXPECT_FALSE(divergent);
+ compare_r = bar.compare(foo, &divergent);
+ EXPECT_EQ(0, compare_r);
+ EXPECT_FALSE(divergent);
+}
+
+TEST(inode_t, compare_aged)
+{
+ inode_t foo{};
+ inode_t bar{};
+
+ foo.ino = 1234;
+ foo.ctime.set_from_double(10.0);
+ foo.mode = 0777;
+ foo.uid = 42;
+ foo.gid = 43;
+ foo.nlink = 3;
+ foo.version = 3;
+ foo.rstat.version = 1;
+
+ bar = foo;
+ bar.version = 2;
+
+ int compare_r;
+ bool divergent;
+ compare_r = foo.compare(bar, &divergent);
+ EXPECT_EQ(1, compare_r);
+ EXPECT_FALSE(divergent);
+ compare_r = bar.compare(foo, &divergent);
+ EXPECT_EQ(-1, compare_r);
+ EXPECT_FALSE(divergent);
+}
+
+TEST(inode_t, compare_divergent)
+{
+ inode_t foo{};
+ inode_t bar{};
+
+ foo.ino = 1234;
+ foo.ctime.set_from_double(10.0);
+ foo.mode = 0777;
+ foo.uid = 42;
+ foo.gid = 43;
+ foo.nlink = 3;
+ foo.version = 3;
+ foo.rstat.version = 1;
+
+ bar = foo;
+ bar.version = 2;
+ bar.rstat.version = 2;
+
+ int compare_r;
+ bool divergent;
+ compare_r = foo.compare(bar, &divergent);
+ EXPECT_EQ(1, compare_r);
+ EXPECT_TRUE(divergent);
+ compare_r = bar.compare(foo, &divergent);
+ EXPECT_EQ(-1, compare_r);
+ EXPECT_TRUE(divergent);
+}
+
+TEST(inode_backtrace_t, compare_equal)
+{
+ inode_backtrace_t foo;
+ inode_backtrace_t bar;
+
+ foo.ino = 1234;
+ foo.pool = 12;
+ foo.old_pools.push_back(10);
+ foo.old_pools.push_back(5);
+
+ inode_backpointer_t foop;
+ foop.dirino = 3;
+ foop.dname = "l3";
+ foop.version = 15;
+ foo.ancestors.push_back(foop);
+ foop.dirino = 2;
+ foop.dname = "l2";
+ foop.version = 10;
+ foo.ancestors.push_back(foop);
+ foop.dirino = 1;
+ foop.dname = "l1";
+ foop.version = 25;
+ foo.ancestors.push_back(foop);
+
+ bar = foo;
+
+ int compare_r;
+ bool equivalent;
+ bool divergent;
+
+ compare_r = foo.compare(bar, &equivalent, &divergent);
+ EXPECT_EQ(0, compare_r);
+ EXPECT_TRUE(equivalent);
+ EXPECT_FALSE(divergent);
+}
+
+TEST(inode_backtrace_t, compare_newer)
+{
+ inode_backtrace_t foo;
+ inode_backtrace_t bar;
+
+ foo.ino = 1234;
+ foo.pool = 12;
+ foo.old_pools.push_back(10);
+ foo.old_pools.push_back(5);
+
+ bar.ino = 1234;
+ bar.pool = 12;
+ bar.old_pools.push_back(10);
+
+ inode_backpointer_t foop;
+ foop.dirino = 3;
+ foop.dname = "l3";
+ foop.version = 15;
+ foo.ancestors.push_back(foop);
+ foop.version = 14;
+ bar.ancestors.push_back(foop);
+
+ foop.dirino = 2;
+ foop.dname = "l2";
+ foop.version = 10;
+ foo.ancestors.push_back(foop);
+ foop.version = 9;
+ bar.ancestors.push_back(foop);
+
+ foop.dirino = 1;
+ foop.dname = "l1";
+ foop.version = 25;
+ foo.ancestors.push_back(foop);
+ bar.ancestors.push_back(foop);
+
+ int compare_r;
+ bool equivalent;
+ bool divergent;
+
+ compare_r = foo.compare(bar, &equivalent, &divergent);
+ EXPECT_EQ(1, compare_r);
+ EXPECT_TRUE(equivalent);
+ EXPECT_FALSE(divergent);
+
+ compare_r = bar.compare(foo, &equivalent, &divergent);
+ EXPECT_EQ(-1, compare_r);
+ EXPECT_TRUE(equivalent);
+ EXPECT_FALSE(divergent);
+
+ bar.ancestors.back().dirino = 75;
+ bar.ancestors.back().dname = "l1-old";
+ bar.ancestors.back().version = 70;
+
+ compare_r = foo.compare(bar, &equivalent, &divergent);
+ EXPECT_EQ(1, compare_r);
+ EXPECT_FALSE(equivalent);
+ EXPECT_FALSE(divergent);
+
+ compare_r = bar.compare(foo, &equivalent, &divergent);
+ EXPECT_EQ(-1, compare_r);
+ EXPECT_FALSE(equivalent);
+ EXPECT_FALSE(divergent);
+}
+
+TEST(inode_backtrace_t, compare_divergent)
+{
+ inode_backtrace_t foo;
+ inode_backtrace_t bar;
+
+ foo.ino = 1234;
+ foo.pool = 12;
+ foo.old_pools.push_back(10);
+ foo.old_pools.push_back(5);
+
+ bar.ino = 1234;
+ bar.pool = 12;
+ bar.old_pools.push_back(10);
+
+ inode_backpointer_t foop;
+ foop.dirino = 3;
+ foop.dname = "l3";
+ foop.version = 15;
+ foo.ancestors.push_back(foop);
+ foop.version = 17;
+ bar.ancestors.push_back(foop);
+
+ foop.dirino = 2;
+ foop.dname = "l2";
+ foop.version = 10;
+ foo.ancestors.push_back(foop);
+ foop.version = 9;
+ bar.ancestors.push_back(foop);
+
+ foop.dirino = 1;
+ foop.dname = "l1";
+ foop.version = 25;
+ foo.ancestors.push_back(foop);
+ bar.ancestors.push_back(foop);
+
+ int compare_r;
+ bool equivalent;
+ bool divergent;
+
+ compare_r = foo.compare(bar, &equivalent, &divergent);
+ EXPECT_EQ(1, compare_r);
+ EXPECT_TRUE(divergent);
+ compare_r = bar.compare(foo, &equivalent, &divergent);
+ EXPECT_EQ(-1, compare_r);
+ EXPECT_TRUE(divergent);
+}
diff --git a/src/test/fs/test_ino_release_cb.cc b/src/test/fs/test_ino_release_cb.cc
new file mode 100644
index 000000000..b442fb991
--- /dev/null
+++ b/src/test/fs/test_ino_release_cb.cc
@@ -0,0 +1,84 @@
+#include <string>
+#include <unistd.h>
+#include <include/fs_types.h>
+#include <mds/mdstypes.h>
+#include <include/cephfs/libcephfs.h>
+
+#define MAX_CEPH_FILES 1000
+#define DIRNAME "ino_release_cb"
+
+using namespace std;
+
+static std::atomic<bool> cb_done = false;
+
+static void cb(void *hdl, vinodeno_t vino)
+{
+ cb_done = true;
+}
+
+int main(int argc, char *argv[])
+{
+ inodeno_t inos[MAX_CEPH_FILES];
+ struct ceph_mount_info *cmount = NULL;
+
+ ceph_create(&cmount, "admin");
+ ceph_conf_read_file(cmount, NULL);
+ ceph_init(cmount);
+
+ [[maybe_unused]] int ret = ceph_mount(cmount, NULL);
+ assert(ret >= 0);
+ ret = ceph_mkdir(cmount, DIRNAME, 0755);
+ assert(ret >= 0);
+ ret = ceph_chdir(cmount, DIRNAME);
+ assert(ret >= 0);
+
+ /* Create a bunch of files, get their inode numbers and close them */
+ int i;
+ for (i = 0; i < MAX_CEPH_FILES; ++i) {
+ int fd;
+ struct ceph_statx stx;
+
+ string name = std::to_string(i);
+
+ fd = ceph_open(cmount, name.c_str(), O_RDWR|O_CREAT, 0644);
+ assert(fd >= 0);
+
+ ret = ceph_fstatx(cmount, fd, &stx, CEPH_STATX_INO, 0);
+ assert(ret >= 0);
+
+ inos[i] = stx.stx_ino;
+ ceph_close(cmount, fd);
+ }
+
+ /* Remount */
+ ceph_unmount(cmount);
+ ceph_release(cmount);
+ ceph_create(&cmount, "admin");
+ ceph_conf_read_file(cmount, NULL);
+ ceph_init(cmount);
+
+ struct ceph_client_callback_args args = { 0 };
+ args.ino_release_cb = cb;
+ ret = ceph_ll_register_callbacks2(cmount, &args);
+ assert(ret == 0);
+
+ ret = ceph_mount(cmount, NULL);
+ assert(ret >= 0);
+
+ Inode *inodes[MAX_CEPH_FILES];
+
+ for (i = 0; i < MAX_CEPH_FILES; ++i) {
+ /* We can stop if we got a callback */
+ if (cb_done)
+ break;
+
+ ret = ceph_ll_lookup_inode(cmount, inos[i], &inodes[i]);
+ assert(ret >= 0);
+ }
+ sleep(45);
+
+ assert(cb_done);
+ ceph_unmount(cmount);
+ ceph_release(cmount);
+ return 0;
+}
diff --git a/src/test/fs/test_trim_caps.cc b/src/test/fs/test_trim_caps.cc
new file mode 100644
index 000000000..11acabb0a
--- /dev/null
+++ b/src/test/fs/test_trim_caps.cc
@@ -0,0 +1,85 @@
+#define _FILE_OFFSET_BITS 64
+#if defined(__linux__)
+#include <features.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <include/cephfs/libcephfs.h>
+
+int main(int argc, char *argv[])
+{
+ char buf;
+ int pipefd[2];
+ int rc [[maybe_unused]] = pipe(pipefd);
+ assert(rc >= 0);
+
+ pid_t pid = fork();
+ assert(pid >= 0);
+ if (pid == 0)
+ close(pipefd[1]);
+ else
+ close(pipefd[0]);
+
+ struct ceph_mount_info *cmount = NULL;
+
+ ceph_create(&cmount, "admin");
+ ceph_conf_read_file(cmount, NULL);
+
+ int ret [[maybe_unused]] = ceph_mount(cmount, NULL);
+ assert(ret >= 0);
+
+ if (pid == 0) {
+ ret = read(pipefd[0], &buf, 1);
+ assert(ret == 1);
+
+ ret = ceph_rename(cmount, "1", "3");
+ assert(ret >= 0);
+
+ ret = ceph_rename(cmount, "2", "1");
+ assert(ret >= 0);
+
+ ceph_unmount(cmount);
+ printf("child exits\n");
+ } else {
+ ret = ceph_mkdirs(cmount, "1/2", 0755);
+ assert(ret >= 0);
+
+ struct ceph_statx stx;
+ ret = ceph_statx(cmount, "1", &stx, 0, 0);
+ assert(ret >= 0);
+ uint64_t orig_ino [[maybe_unused]] = stx.stx_ino;
+
+
+ ret = ceph_mkdir(cmount, "2", 0755);
+ assert(ret >= 0);
+
+ ret = write(pipefd[1], &buf, 1);
+ assert(ret == 1);
+
+ int wstatus;
+ ret = waitpid(pid, &wstatus, 0);
+ assert(ret >= 0);
+ assert(wstatus == 0);
+
+ // make origin '1' no parent dentry
+ ret = ceph_statx(cmount, "1", &stx, 0, 0);
+ assert(ret >= 0);
+ assert(orig_ino != stx.stx_ino);
+
+ // move root inode's cap_item to tail of session->caps
+ ret = ceph_statx(cmount, ".", &stx, 0, 0);
+ assert(ret >= 0);
+
+ printf("waiting for crash\n");
+ sleep(60);
+ }
+ return 0;
+}
diff --git a/src/test/gather.cc b/src/test/gather.cc
new file mode 100644
index 000000000..496403625
--- /dev/null
+++ b/src/test/gather.cc
@@ -0,0 +1,103 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 Greg Farnum <gregory.farnum@dreamhost.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "include/Context.h"
+#include "gtest/gtest.h"
+
+class C_Checker : public Context {
+public:
+ bool *finish_called;
+ int *result;
+ C_Checker(bool* _finish_called, int *r) :
+ finish_called(_finish_called), result(r) {}
+ void finish(int r) override { *finish_called = true; *result = r; }
+};
+
+TEST(ContextGather, Constructor) {
+ C_GatherBuilder gather(g_ceph_context);
+ EXPECT_FALSE(gather.has_subs());
+ EXPECT_TRUE(gather.get() == NULL);
+}
+
+TEST(ContextGather, OneSub) {
+ C_GatherBuilder gather(g_ceph_context);
+ Context *sub = gather.new_sub();
+ EXPECT_EQ(1, gather.num_subs_created());
+ EXPECT_EQ(1, gather.num_subs_remaining());
+
+ bool finish_called = false;
+ int result = 0;
+ C_Checker *checker = new C_Checker(&finish_called, &result);
+ gather.set_finisher(checker);
+ gather.activate();
+ sub->complete(0);
+ EXPECT_TRUE(finish_called);
+ EXPECT_EQ(0, result);
+}
+
+TEST(ContextGather, ManySubs) {
+ bool finish_called = false;
+ int result = 0;
+ C_GatherBuilder gather(g_ceph_context, new C_Checker(&finish_called, &result));
+ int sub_count = 8;
+ Context* subs[sub_count];
+ //create subs and test
+ for (int i = 0; i < sub_count; ++i) {
+ subs[i] = gather.new_sub();
+ EXPECT_EQ(i+1, gather.num_subs_created());
+ EXPECT_EQ(i+1, gather.num_subs_remaining());
+ }
+ EXPECT_TRUE(gather.has_subs());
+ gather.activate();
+
+ //finish all except one sub
+ for (int j = 0; j < sub_count - 1; ++j) {
+ subs[j]->complete(0);
+ EXPECT_FALSE(finish_called);
+ }
+
+ //finish last one and check asserts
+ subs[sub_count-1]->complete(0);
+ EXPECT_TRUE(finish_called);
+}
+
+TEST(ContextGather, AlternatingSubCreateFinish) {
+ C_GatherBuilder gather(g_ceph_context);
+ int sub_count = 8;
+ bool finish_called = false;
+ int result = 0;
+ C_Checker *checker = new C_Checker(&finish_called, &result);
+ gather.set_finisher(checker);
+ Context* subs[sub_count];
+
+ //create half the subs
+ for (int i = 0; i < sub_count / 2; ++i) {
+ subs[i] = gather.new_sub();
+ EXPECT_EQ(i + 1, gather.num_subs_created());
+ EXPECT_EQ(i + 1, gather.num_subs_remaining());
+ }
+
+ //alternate finishing first half of subs and creating last half of subs
+ for (int j = 0; j < sub_count / 2; ++j) {
+ subs[j]->complete(0);
+ subs[sub_count / 2 + j] = gather.new_sub();
+ }
+ gather.activate();
+
+ //finish last half of subs
+ for (int k = sub_count / 2; k < sub_count; ++k) {
+ subs[k]->complete(0);
+ }
+
+ EXPECT_TRUE(finish_called);
+}
diff --git a/src/test/gprof-helper.c b/src/test/gprof-helper.c
new file mode 100644
index 000000000..a64c406ec
--- /dev/null
+++ b/src/test/gprof-helper.c
@@ -0,0 +1,119 @@
+/* gprof-helper.c -- preload library to profile pthread-enabled programs
+ *
+ * Authors: Sam Hocevar <sam at zoy dot org>
+ * Daniel Jönsson <danieljo at fagotten dot org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Do What The Fuck You Want To
+ * Public License as published by Banlu Kemiyatorn. See
+ * http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+ *
+ * Compilation example:
+ * gcc -shared -fPIC gprof-helper.c -o gprof-helper.so -lpthread -ldl
+ *
+ * Usage example:
+ * LD_PRELOAD=./gprof-helper.so your_program
+ */
+
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
+static void * wrapper_routine(void *);
+
+/* Original pthread function */
+static int (*pthread_create_orig)(pthread_t *__restrict,
+ __const pthread_attr_t *__restrict,
+ void *(*)(void *),
+ void *__restrict) = NULL;
+
+/* Library initialization function */
+void wooinit(void) __attribute__((constructor));
+
+void wooinit(void)
+{
+ pthread_create_orig = dlsym(RTLD_NEXT, "pthread_create");
+ fprintf(stderr, "pthreads: using profiling hooks for gprof\n");
+ if(pthread_create_orig == NULL)
+ {
+ char *error = dlerror();
+ if(error == NULL)
+ {
+ error = "pthread_create is NULL";
+ }
+ fprintf(stderr, "%s\n", error);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* Our data structure passed to the wrapper */
+typedef struct wrapper_s
+{
+ void * (*start_routine)(void *);
+ void * arg;
+
+ pthread_mutex_t lock;
+ pthread_cond_t wait;
+
+ struct itimerval itimer;
+
+} wrapper_t;
+
+/* The wrapper function in charge for setting the itimer value */
+static void * wrapper_routine(void * data)
+{
+ /* Put user data in thread-local variables */
+ void * (*start_routine)(void *) = ((wrapper_t*)data)->start_routine;
+ void * arg = ((wrapper_t*)data)->arg;
+
+ /* Set the profile timer value */
+ setitimer(ITIMER_PROF, &((wrapper_t*)data)->itimer, NULL);
+
+ /* Tell the calling thread that we don't need its data anymore */
+ pthread_mutex_lock(&((wrapper_t*)data)->lock);
+ pthread_cond_signal(&((wrapper_t*)data)->wait);
+ pthread_mutex_unlock(&((wrapper_t*)data)->lock);
+
+ /* Call the real function */
+ return start_routine(arg);
+}
+
+/* Our wrapper function for the real pthread_create() */
+int pthread_create(pthread_t *__restrict thread,
+ __const pthread_attr_t *__restrict attr,
+ void * (*start_routine)(void *),
+ void *__restrict arg)
+{
+ wrapper_t wrapper_data;
+ int i_return;
+
+ /* Initialize the wrapper structure */
+ wrapper_data.start_routine = start_routine;
+ wrapper_data.arg = arg;
+ getitimer(ITIMER_PROF, &wrapper_data.itimer);
+ pthread_cond_init(&wrapper_data.wait, NULL);
+ pthread_mutex_init(&wrapper_data.lock, NULL);
+ pthread_mutex_lock(&wrapper_data.lock);
+
+ /* The real pthread_create call */
+ i_return = pthread_create_orig(thread,
+ attr,
+ &wrapper_routine,
+ &wrapper_data);
+
+ /* If the thread was successfully spawned, wait for the data
+ * to be released */
+ if(i_return == 0)
+ {
+ pthread_cond_wait(&wrapper_data.wait, &wrapper_data.lock);
+ }
+
+ pthread_mutex_unlock(&wrapper_data.lock);
+ pthread_mutex_destroy(&wrapper_data.lock);
+ pthread_cond_destroy(&wrapper_data.wait);
+
+ return i_return;
+}
+
diff --git a/src/test/heartbeat_map.cc b/src/test/heartbeat_map.cc
new file mode 100644
index 000000000..27f1fe721
--- /dev/null
+++ b/src/test/heartbeat_map.cc
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/HeartbeatMap.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+using namespace ceph;
+
+TEST(HeartbeatMap, Healthy) {
+ HeartbeatMap hm(g_ceph_context);
+ heartbeat_handle_d *h = hm.add_worker("one", pthread_self());
+
+ hm.reset_timeout(h, ceph::make_timespan(9), ceph::make_timespan(18));
+ bool healthy = hm.is_healthy();
+ ASSERT_TRUE(healthy);
+
+ hm.remove_worker(h);
+}
+
+TEST(HeartbeatMap, Unhealth) {
+ HeartbeatMap hm(g_ceph_context);
+ heartbeat_handle_d *h = hm.add_worker("one", pthread_self());
+
+ hm.reset_timeout(h, ceph::make_timespan(1), ceph::make_timespan(3));
+ sleep(2);
+ bool healthy = hm.is_healthy();
+ ASSERT_FALSE(healthy);
+
+ hm.remove_worker(h);
+}
diff --git a/src/test/immutable_object_cache/CMakeLists.txt b/src/test/immutable_object_cache/CMakeLists.txt
new file mode 100644
index 000000000..e4ed3d459
--- /dev/null
+++ b/src/test/immutable_object_cache/CMakeLists.txt
@@ -0,0 +1,37 @@
+
+add_executable(unittest_ceph_immutable_obj_cache
+ test_main.cc
+ test_SimplePolicy.cc
+ test_DomainSocket.cc
+ test_multi_session.cc
+ test_object_store.cc
+ test_message.cc
+ )
+add_ceph_unittest(unittest_ceph_immutable_obj_cache)
+
+
+target_link_libraries(unittest_ceph_immutable_obj_cache
+ ceph_immutable_object_cache_lib
+ rados_test_stub
+ librados
+ global
+ radostest-cxx
+ StdFilesystem::filesystem
+ GTest::GTest
+ )
+
+
+add_executable(ceph_test_immutable_obj_cache
+ test_main.cc
+ )
+
+target_link_libraries(ceph_test_immutable_obj_cache
+ librados
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ )
+
+
+install(TARGETS
+ ceph_test_immutable_obj_cache
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/immutable_object_cache/MockCacheDaemon.h b/src/test/immutable_object_cache/MockCacheDaemon.h
new file mode 100644
index 000000000..02e86acb2
--- /dev/null
+++ b/src/test/immutable_object_cache/MockCacheDaemon.h
@@ -0,0 +1,45 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef IMMUTABLE_OBJECT_CACHE_MOCK_DAEMON
+#define IMMUTABLE_OBJECT_CACHE_MOCK_DAEMON
+
+#include <iostream>
+#include <unistd.h>
+
+#include "gmock/gmock.h"
+
+#include "include/Context.h"
+#include "tools/immutable_object_cache/CacheClient.h"
+
+namespace ceph {
+namespace immutable_obj_cache {
+
+class MockCacheClient {
+ public:
+ MockCacheClient(const std::string& file, CephContext* ceph_ctx) {}
+ MOCK_METHOD0(run, void());
+ MOCK_METHOD0(is_session_work, bool());
+ MOCK_METHOD0(close, void());
+ MOCK_METHOD0(stop, void());
+ MOCK_METHOD0(connect, int());
+ MOCK_METHOD1(connect, void(Context*));
+ MOCK_METHOD6(lookup_object, void(std::string, uint64_t, uint64_t, uint64_t,
+ std::string, CacheGenContextURef));
+ MOCK_METHOD1(register_client, int(Context*));
+};
+
+class MockCacheServer {
+ public:
+ MockCacheServer(CephContext* cct, const std::string& file,
+ ProcessMsg processmsg) {
+ }
+ MOCK_METHOD0(run, int());
+ MOCK_METHOD0(start_accept, int());
+ MOCK_METHOD0(stop, int());
+};
+
+} // namespace immutable_obj_cach3
+} // namespace ceph
+
+#endif // IMMUTABLE_OBJECT_CACHE_MOCK_DAEMON
diff --git a/src/test/immutable_object_cache/test_DomainSocket.cc b/src/test/immutable_object_cache/test_DomainSocket.cc
new file mode 100644
index 000000000..31d1b9adc
--- /dev/null
+++ b/src/test/immutable_object_cache/test_DomainSocket.cc
@@ -0,0 +1,177 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "include/Context.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+
+#include "test/immutable_object_cache/test_common.h"
+#include "tools/immutable_object_cache/CacheClient.h"
+#include "tools/immutable_object_cache/CacheServer.h"
+
+using namespace ceph::immutable_obj_cache;
+
+class TestCommunication :public ::testing::Test {
+public:
+ CacheServer* m_cache_server;
+ std::thread* srv_thd;
+ CacheClient* m_cache_client;
+ std::string m_local_path;
+ pthread_mutex_t m_mutex;
+ pthread_cond_t m_cond;
+ std::atomic<uint64_t> m_send_request_index;
+ std::atomic<uint64_t> m_recv_ack_index;
+ WaitEvent m_wait_event;
+ unordered_set<std::string> m_hit_entry_set;
+
+ TestCommunication()
+ : m_cache_server(nullptr), m_cache_client(nullptr),
+ m_local_path("/tmp/ceph_test_domain_socket"),
+ m_send_request_index(0), m_recv_ack_index(0)
+ {}
+
+ ~TestCommunication() {}
+
+ static void SetUpTestCase() {}
+ static void TearDownTestCase() {}
+
+ void SetUp() override {
+ std::remove(m_local_path.c_str());
+ m_cache_server = new CacheServer(g_ceph_context, m_local_path,
+ [this](CacheSession* sid, ObjectCacheRequest* req){
+ handle_request(sid, req);
+ });
+ ASSERT_TRUE(m_cache_server != nullptr);
+ srv_thd = new std::thread([this]() {m_cache_server->run();});
+
+ m_cache_client = new CacheClient(m_local_path, g_ceph_context);
+ ASSERT_TRUE(m_cache_client != nullptr);
+ m_cache_client->run();
+
+ while (true) {
+ if (0 == m_cache_client->connect()) {
+ break;
+ }
+ }
+
+ auto ctx = new LambdaContext([](int reg) {
+ ASSERT_TRUE(reg == 0);
+ });
+ m_cache_client->register_client(ctx);
+ ASSERT_TRUE(m_cache_client->is_session_work());
+ }
+
+ void TearDown() override {
+
+ delete m_cache_client;
+ m_cache_server->stop();
+ if (srv_thd->joinable()) {
+ srv_thd->join();
+ }
+ delete m_cache_server;
+ std::remove(m_local_path.c_str());
+ delete srv_thd;
+ }
+
+ void handle_request(CacheSession* session_id, ObjectCacheRequest* req) {
+
+ switch (req->get_request_type()) {
+ case RBDSC_REGISTER: {
+ ObjectCacheRequest* reply = new ObjectCacheRegReplyData(RBDSC_REGISTER_REPLY, req->seq);
+ session_id->send(reply);
+ break;
+ }
+ case RBDSC_READ: {
+ ObjectCacheReadData* read_req = (ObjectCacheReadData*)req;
+ ObjectCacheRequest* reply = nullptr;
+ if (m_hit_entry_set.find(read_req->oid) == m_hit_entry_set.end()) {
+ reply = new ObjectCacheReadRadosData(RBDSC_READ_RADOS, req->seq);
+ } else {
+ reply = new ObjectCacheReadReplyData(RBDSC_READ_REPLY, req->seq, "/fakepath");
+ }
+ session_id->send(reply);
+ break;
+ }
+ }
+ }
+
+ // times: message number
+ // queue_depth : imitate message queue depth
+ // thinking : imitate handing message time
+ void startup_pingpong_testing(uint64_t times, uint64_t queue_depth, int thinking) {
+ m_send_request_index.store(0);
+ m_recv_ack_index.store(0);
+ for (uint64_t index = 0; index < times; index++) {
+ auto ctx = make_gen_lambda_context<ObjectCacheRequest*, std::function<void(ObjectCacheRequest*)>>
+ ([this, thinking, times](ObjectCacheRequest* ack){
+ if (thinking != 0) {
+ usleep(thinking); // handling message
+ }
+ m_recv_ack_index++;
+ if (m_recv_ack_index == times) {
+ m_wait_event.signal();
+ }
+ });
+
+ // simple queue depth
+ while (m_send_request_index - m_recv_ack_index > queue_depth) {
+ usleep(1);
+ }
+
+ m_cache_client->lookup_object("pool_nspace", 1, 2, 3, "object_name", std::move(ctx));
+ m_send_request_index++;
+ }
+ m_wait_event.wait();
+ }
+
+ bool startup_lookupobject_testing(std::string pool_nspace, std::string object_id) {
+ bool hit;
+ auto ctx = make_gen_lambda_context<ObjectCacheRequest*, std::function<void(ObjectCacheRequest*)>>
+ ([this, &hit](ObjectCacheRequest* ack){
+ hit = ack->type == RBDSC_READ_REPLY;
+ m_wait_event.signal();
+ });
+ m_cache_client->lookup_object(pool_nspace, 1, 2, 3, object_id, std::move(ctx));
+ m_wait_event.wait();
+ return hit;
+ }
+
+ void set_hit_entry_in_fake_lru(std::string cache_file_name) {
+ if (m_hit_entry_set.find(cache_file_name) == m_hit_entry_set.end()) {
+ m_hit_entry_set.insert(cache_file_name);
+ }
+ }
+};
+
+TEST_F(TestCommunication, test_pingpong) {
+
+ startup_pingpong_testing(64, 16, 0);
+ ASSERT_TRUE(m_send_request_index == m_recv_ack_index);
+ startup_pingpong_testing(200, 128, 0);
+ ASSERT_TRUE(m_send_request_index == m_recv_ack_index);
+}
+
+TEST_F(TestCommunication, test_lookup_object) {
+
+ m_hit_entry_set.clear();
+
+ srand(time(0));
+ uint64_t random_hit = random();
+
+ for (uint64_t i = 50; i < 100; i++) {
+ if ((random_hit % i) == 0) {
+ set_hit_entry_in_fake_lru(std::to_string(i));
+ }
+ }
+ for (uint64_t i = 50; i < 100; i++) {
+ if ((random_hit % i) != 0) {
+ ASSERT_FALSE(startup_lookupobject_testing("test_nspace", std::to_string(i)));
+ } else {
+ ASSERT_TRUE(startup_lookupobject_testing("test_nspace", std::to_string(i)));
+ }
+ }
+}
diff --git a/src/test/immutable_object_cache/test_SimplePolicy.cc b/src/test/immutable_object_cache/test_SimplePolicy.cc
new file mode 100644
index 000000000..26f503be4
--- /dev/null
+++ b/src/test/immutable_object_cache/test_SimplePolicy.cc
@@ -0,0 +1,235 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <sstream>
+#include <list>
+#include <gtest/gtest.h>
+
+#include "include/Context.h"
+#include "tools/immutable_object_cache/SimplePolicy.h"
+
+using namespace ceph::immutable_obj_cache;
+
+std::string generate_file_name(uint64_t index) {
+ std::string pre_name("object_cache_file_");
+ std::ostringstream oss;
+ oss << index;
+ return pre_name + oss.str();
+}
+
+class TestSimplePolicy :public ::testing::Test {
+public:
+ SimplePolicy* m_simple_policy;
+ const uint64_t m_cache_size;
+ uint64_t m_entry_index;
+ std::vector<std::string> m_promoted_lru;
+ std::vector<std::string> m_promoting_lru;
+
+ TestSimplePolicy() : m_cache_size(100), m_entry_index(0) {}
+ ~TestSimplePolicy() {}
+ static void SetUpTestCase() {}
+ static void TearDownTestCase() {}
+ void SetUp() override {
+ m_simple_policy = new SimplePolicy(g_ceph_context, m_cache_size, 128, 0.9);
+ // populate 50 entries
+ for (uint64_t i = 0; i < m_cache_size / 2; i++, m_entry_index++) {
+ insert_entry_into_promoted_lru(generate_file_name(m_entry_index));
+ }
+ }
+ void TearDown() override {
+ while(m_promoted_lru.size()) {
+ ASSERT_TRUE(m_simple_policy->get_evict_entry() == m_promoted_lru.front());
+ m_simple_policy->evict_entry(m_simple_policy->get_evict_entry());
+ m_promoted_lru.erase(m_promoted_lru.begin());
+ }
+ delete m_simple_policy;
+ }
+
+ void insert_entry_into_promoted_lru(std::string cache_file_name) {
+ ASSERT_EQ(m_cache_size - m_promoted_lru.size(), m_simple_policy->get_free_size());
+ ASSERT_EQ(m_promoting_lru.size(), m_simple_policy->get_promoting_entry_num());
+ ASSERT_EQ(m_promoted_lru.size(), m_simple_policy->get_promoted_entry_num());
+ ASSERT_EQ(OBJ_CACHE_NONE, m_simple_policy->get_status(cache_file_name));
+
+ m_simple_policy->lookup_object(cache_file_name);
+ ASSERT_EQ(OBJ_CACHE_SKIP, m_simple_policy->get_status(cache_file_name));
+ ASSERT_EQ(m_cache_size - m_promoted_lru.size(), m_simple_policy->get_free_size());
+ ASSERT_EQ(m_promoting_lru.size() + 1, m_simple_policy->get_promoting_entry_num());
+ ASSERT_EQ(m_promoted_lru.size(), m_simple_policy->get_promoted_entry_num());
+
+ m_simple_policy->update_status(cache_file_name, OBJ_CACHE_PROMOTED, 1);
+ m_promoted_lru.push_back(cache_file_name);
+ ASSERT_EQ(OBJ_CACHE_PROMOTED, m_simple_policy->get_status(cache_file_name));
+
+ ASSERT_EQ(m_cache_size - m_promoted_lru.size(), m_simple_policy->get_free_size());
+ ASSERT_EQ(m_promoting_lru.size(), m_simple_policy->get_promoting_entry_num());
+ ASSERT_EQ(m_promoted_lru.size(), m_simple_policy->get_promoted_entry_num());
+ }
+
+ void insert_entry_into_promoting_lru(std::string cache_file_name) {
+ ASSERT_EQ(m_cache_size - m_promoted_lru.size(), m_simple_policy->get_free_size());
+ ASSERT_EQ(m_promoting_lru.size(), m_simple_policy->get_promoting_entry_num());
+ ASSERT_EQ(m_promoted_lru.size(), m_simple_policy->get_promoted_entry_num());
+ ASSERT_EQ(OBJ_CACHE_NONE, m_simple_policy->get_status(cache_file_name));
+
+ m_simple_policy->lookup_object(cache_file_name);
+ m_promoting_lru.push_back(cache_file_name);
+ ASSERT_EQ(OBJ_CACHE_SKIP, m_simple_policy->get_status(cache_file_name));
+ ASSERT_EQ(m_cache_size - m_promoted_lru.size(), m_simple_policy->get_free_size());
+ ASSERT_EQ(m_promoting_lru.size(), m_simple_policy->get_promoting_entry_num());
+ ASSERT_EQ(m_promoted_lru.size(), m_simple_policy->get_promoted_entry_num());
+ }
+};
+
+TEST_F(TestSimplePolicy, test_lookup_miss_and_no_free) {
+ // exhaust cache space
+ uint64_t left_entry_num = m_cache_size - m_promoted_lru.size();
+ for (uint64_t i = 0; i < left_entry_num; i++, ++m_entry_index) {
+ insert_entry_into_promoted_lru(generate_file_name(m_entry_index));
+ }
+ ASSERT_TRUE(0 == m_simple_policy->get_free_size());
+ ASSERT_TRUE(m_simple_policy->lookup_object("no_this_cache_file_name") == OBJ_CACHE_SKIP);
+}
+
+TEST_F(TestSimplePolicy, test_lookup_miss_and_have_free) {
+ ASSERT_TRUE(m_cache_size - m_promoted_lru.size() == m_simple_policy->get_free_size());
+ ASSERT_TRUE(m_simple_policy->lookup_object("miss_but_have_free_space_file_name") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("miss_but_have_free_space_file_name") == OBJ_CACHE_SKIP);
+}
+
+TEST_F(TestSimplePolicy, test_lookup_hit_and_promoting) {
+ ASSERT_TRUE(m_cache_size - m_promoted_lru.size() == m_simple_policy->get_free_size());
+ insert_entry_into_promoting_lru("promoting_file_1");
+ insert_entry_into_promoting_lru("promoting_file_2");
+ insert_entry_into_promoted_lru(generate_file_name(++m_entry_index));
+ insert_entry_into_promoted_lru(generate_file_name(++m_entry_index));
+ insert_entry_into_promoting_lru("promoting_file_3");
+ insert_entry_into_promoting_lru("promoting_file_4");
+
+ ASSERT_TRUE(m_simple_policy->get_promoting_entry_num() == 4);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_file_1") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_file_2") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_file_3") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_file_4") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->lookup_object("promoting_file_1") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->lookup_object("promoting_file_2") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->lookup_object("promoting_file_3") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->lookup_object("promoting_file_4") == OBJ_CACHE_SKIP);
+}
+
+TEST_F(TestSimplePolicy, test_lookup_hit_and_promoted) {
+ ASSERT_TRUE(m_promoted_lru.size() == m_simple_policy->get_promoted_entry_num());
+ for (uint64_t index = 0; index < m_entry_index; index++) {
+ ASSERT_TRUE(m_simple_policy->get_status(generate_file_name(index)) == OBJ_CACHE_PROMOTED);
+ }
+}
+
+TEST_F(TestSimplePolicy, test_update_state_from_promoting_to_none) {
+ ASSERT_TRUE(m_cache_size - m_promoted_lru.size() == m_simple_policy->get_free_size());
+ insert_entry_into_promoting_lru("promoting_to_none_file_1");
+ insert_entry_into_promoting_lru("promoting_to_none_file_2");
+ insert_entry_into_promoted_lru(generate_file_name(++m_entry_index));
+ insert_entry_into_promoting_lru("promoting_to_none_file_3");
+ insert_entry_into_promoting_lru("promoting_to_none_file_4");
+
+ ASSERT_TRUE(m_simple_policy->get_promoting_entry_num() == 4);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_1") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_2") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_3") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_4") == OBJ_CACHE_SKIP);
+
+ m_simple_policy->update_status("promoting_to_none_file_1", OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_promoting_entry_num() == 3);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_1") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_2") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_3") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_4") == OBJ_CACHE_SKIP);
+
+ m_simple_policy->update_status("promoting_to_none_file_2", OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_promoting_entry_num() == 2);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_1") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_2") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_3") == OBJ_CACHE_SKIP);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_4") == OBJ_CACHE_SKIP);
+
+ m_simple_policy->update_status("promoting_to_none_file_3", OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_promoting_entry_num() == 1);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_1") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_2") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_3") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_4") == OBJ_CACHE_SKIP);
+
+ m_simple_policy->update_status("promoting_to_none_file_4", OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_promoting_entry_num() == 0);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_1") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_2") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_3") == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_none_file_4") == OBJ_CACHE_NONE);
+}
+
+TEST_F(TestSimplePolicy, test_update_state_from_promoted_to_none) {
+ ASSERT_TRUE(m_promoted_lru.size() == m_simple_policy->get_promoted_entry_num());
+ for (uint64_t index = 0; index < m_entry_index; index++) {
+ ASSERT_TRUE(m_simple_policy->get_status(generate_file_name(index)) == OBJ_CACHE_PROMOTED);
+ m_simple_policy->update_status(generate_file_name(index), OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_status(generate_file_name(index)) == OBJ_CACHE_NONE);
+ ASSERT_TRUE(m_simple_policy->get_promoted_entry_num() == m_promoted_lru.size() - index - 1);
+ }
+ m_promoted_lru.clear();
+}
+
+TEST_F(TestSimplePolicy, test_update_state_from_promoting_to_promoted) {
+ ASSERT_TRUE(m_cache_size - m_promoted_lru.size() == m_simple_policy->get_free_size());
+ insert_entry_into_promoting_lru("promoting_to_promoted_file_1");
+ insert_entry_into_promoting_lru("promoting_to_promoted_file_2");
+ insert_entry_into_promoting_lru("promoting_to_promoted_file_3");
+ insert_entry_into_promoting_lru("promoting_to_promoted_file_4");
+ ASSERT_TRUE(4 == m_simple_policy->get_promoting_entry_num());
+
+ m_simple_policy->update_status("promoting_to_promoted_file_1", OBJ_CACHE_PROMOTED);
+ ASSERT_TRUE(3 == m_simple_policy->get_promoting_entry_num());
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_promoted_file_1") == OBJ_CACHE_PROMOTED);
+
+ m_simple_policy->update_status("promoting_to_promoted_file_2", OBJ_CACHE_PROMOTED);
+ ASSERT_TRUE(2 == m_simple_policy->get_promoting_entry_num());
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_promoted_file_2") == OBJ_CACHE_PROMOTED);
+
+ m_simple_policy->update_status("promoting_to_promoted_file_3", OBJ_CACHE_PROMOTED);
+ ASSERT_TRUE(1 == m_simple_policy->get_promoting_entry_num());
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_promoted_file_3") == OBJ_CACHE_PROMOTED);
+
+ m_simple_policy->update_status("promoting_to_promoted_file_4", OBJ_CACHE_PROMOTED);
+ ASSERT_TRUE(0 == m_simple_policy->get_promoting_entry_num());
+ ASSERT_TRUE(m_simple_policy->get_status("promoting_to_promoted_file_4") == OBJ_CACHE_PROMOTED);
+
+ m_promoted_lru.push_back("promoting_to_promoted_file_1");
+ m_promoted_lru.push_back("promoting_to_promoted_file_2");
+ m_promoted_lru.push_back("promoting_to_promoted_file_3");
+ m_promoted_lru.push_back("promoting_to_promoted_file_4");
+}
+
+TEST_F(TestSimplePolicy, test_evict_list_0) {
+ std::list<std::string> evict_entry_list;
+ // the default water mark is 0.9
+ ASSERT_TRUE((float)m_simple_policy->get_free_size() > m_cache_size*0.1);
+ m_simple_policy->get_evict_list(&evict_entry_list);
+ ASSERT_TRUE(evict_entry_list.size() == 0);
+}
+
+TEST_F(TestSimplePolicy, test_evict_list_10) {
+ uint64_t left_entry_num = m_cache_size - m_promoted_lru.size();
+ for (uint64_t i = 0; i < left_entry_num; i++, ++m_entry_index) {
+ insert_entry_into_promoted_lru(generate_file_name(m_entry_index));
+ }
+ ASSERT_TRUE(0 == m_simple_policy->get_free_size());
+ std::list<std::string> evict_entry_list;
+ m_simple_policy->get_evict_list(&evict_entry_list);
+ // evict 10% of old entries
+ ASSERT_TRUE(m_cache_size*0.1 == evict_entry_list.size());
+ ASSERT_TRUE(m_cache_size - m_cache_size*0.1 == m_simple_policy->get_promoted_entry_num());
+
+ for (auto it = evict_entry_list.begin(); it != evict_entry_list.end(); it++) {
+ ASSERT_TRUE(*it == m_promoted_lru.front());
+ m_promoted_lru.erase(m_promoted_lru.begin());
+ }
+}
diff --git a/src/test/immutable_object_cache/test_common.h b/src/test/immutable_object_cache/test_common.h
new file mode 100644
index 000000000..9d6fd14c7
--- /dev/null
+++ b/src/test/immutable_object_cache/test_common.h
@@ -0,0 +1,41 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CACHE_TEST_COMMON_H
+#define CACHE_TEST_COMMON_H
+
+#include <pthread.h>
+
+class WaitEvent {
+public:
+ WaitEvent() : m_signaled(false) {
+ pthread_mutex_init(&m_lock, NULL);
+ pthread_cond_init(&m_cond, NULL);
+ }
+
+ ~WaitEvent() {
+ pthread_mutex_destroy(&m_lock);
+ pthread_cond_destroy(&m_cond);
+ }
+
+ void wait() {
+ pthread_mutex_lock(&m_lock);
+ while (!m_signaled) {
+ pthread_cond_wait(&m_cond, &m_lock);
+ }
+ m_signaled = false;
+ pthread_mutex_unlock(&m_lock);
+ }
+
+ void signal() {
+ pthread_mutex_lock(&m_lock);
+ m_signaled = true;
+ pthread_cond_signal(&m_cond);
+ pthread_mutex_unlock(&m_lock);
+ }
+private:
+ pthread_mutex_t m_lock;
+ pthread_cond_t m_cond;
+ bool m_signaled;
+};
+
+#endif
diff --git a/src/test/immutable_object_cache/test_main.cc b/src/test/immutable_object_cache/test_main.cc
new file mode 100644
index 000000000..571627e0b
--- /dev/null
+++ b/src/test/immutable_object_cache/test_main.cc
@@ -0,0 +1,29 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "global/global_context.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+#include <iostream>
+#include <string>
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ librados::Rados rados;
+ std::string result = connect_cluster_pp(rados);
+ if (result != "" ) {
+ std::cerr << result << std::endl;
+ return 1;
+ }
+
+ g_ceph_context = reinterpret_cast<CephContext*>(rados.cct());
+
+ int r = rados.conf_set("lockdep", "true");
+ if (r < 0) {
+ std::cerr << "warning: failed to enable lockdep" << std::endl;
+ }
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/immutable_object_cache/test_message.cc b/src/test/immutable_object_cache/test_message.cc
new file mode 100644
index 000000000..bbd6ad165
--- /dev/null
+++ b/src/test/immutable_object_cache/test_message.cc
@@ -0,0 +1,50 @@
+#include "gtest/gtest.h"
+#include "tools/immutable_object_cache/Types.h"
+#include "tools/immutable_object_cache/SocketCommon.h"
+
+using namespace ceph::immutable_obj_cache;
+
+TEST(test_for_message, test_1)
+{
+ std::string pool_nspace("this is a pool namespace");
+ std::string oid_name("this is a oid name");
+ std::string cache_file_path("/temp/ceph_immutable_object_cache");
+
+ uint16_t type = RBDSC_READ;
+ uint64_t seq = 123456UL;
+ uint64_t read_offset = 222222UL;
+ uint64_t read_len = 333333UL;
+ uint64_t pool_id = 444444UL;
+ uint64_t snap_id = 555555UL;
+ uint64_t object_size = 666666UL;
+
+ // ObjectRequest --> bufferlist
+ ObjectCacheRequest* req = new ObjectCacheReadData(type, seq, read_offset, read_len,
+ pool_id, snap_id, object_size, oid_name, pool_nspace);
+ req->encode();
+ auto payload_bl = req->get_payload_bufferlist();
+
+ uint32_t data_len = get_data_len(payload_bl.c_str());
+ ASSERT_EQ(payload_bl.length(), data_len + get_header_size());
+ ASSERT_TRUE(payload_bl.c_str() != nullptr);
+
+ // bufferlist --> ObjectCacheRequest
+ ObjectCacheRequest* req_decode = decode_object_cache_request(payload_bl);
+
+ ASSERT_EQ(req_decode->get_request_type(), RBDSC_READ);
+
+ ASSERT_EQ(req_decode->type, RBDSC_READ);
+ ASSERT_EQ(req_decode->seq, 123456UL);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->type, RBDSC_READ);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->seq, 123456UL);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->read_offset, 222222UL);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->read_len, 333333UL);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->pool_id, 444444UL);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->snap_id, 555555UL);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->oid, oid_name);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->pool_namespace, pool_nspace);
+ ASSERT_EQ(((ObjectCacheReadData*)req_decode)->object_size, 666666UL);
+
+ delete req;
+ delete req_decode;
+}
diff --git a/src/test/immutable_object_cache/test_multi_session.cc b/src/test/immutable_object_cache/test_multi_session.cc
new file mode 100644
index 000000000..a8ccbffe2
--- /dev/null
+++ b/src/test/immutable_object_cache/test_multi_session.cc
@@ -0,0 +1,162 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <iostream>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "include/Context.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+
+#include "test/immutable_object_cache/test_common.h"
+#include "tools/immutable_object_cache/CacheClient.h"
+#include "tools/immutable_object_cache/CacheServer.h"
+
+using namespace std;
+using namespace ceph::immutable_obj_cache;
+
+class TestMultiSession : public ::testing::Test {
+public:
+ std::string m_local_path;
+ CacheServer* m_cache_server;
+ std::thread* m_cache_server_thread;
+ std::vector<CacheClient*> m_cache_client_vec;
+ WaitEvent m_wait_event;
+ std::atomic<uint64_t> m_send_request_index;
+ std::atomic<uint64_t> m_recv_ack_index;
+ uint64_t m_session_num = 110;
+
+ TestMultiSession() : m_local_path("/tmp/ceph_test_multisession_socket"),
+ m_cache_server_thread(nullptr), m_send_request_index(0),
+ m_recv_ack_index(0) {
+ m_cache_client_vec.resize(m_session_num + 1, nullptr);
+ }
+
+ ~TestMultiSession() {}
+
+ static void SetUpTestCase() {}
+ static void TearDownTestCase() {}
+
+ void SetUp() override {
+ std::remove(m_local_path.c_str());
+ m_cache_server = new CacheServer(g_ceph_context, m_local_path,
+ [this](CacheSession* session_id, ObjectCacheRequest* req){
+ server_handle_request(session_id, req);
+ });
+ ASSERT_TRUE(m_cache_server != nullptr);
+
+ m_cache_server_thread = new std::thread(([this]() {
+ m_wait_event.signal();
+ m_cache_server->run();
+ }));
+
+ // waiting for thread running.
+ m_wait_event.wait();
+
+ // waiting for io_service run.
+ usleep(2);
+ }
+
+ void TearDown() override {
+ for (uint64_t i = 0; i < m_session_num; i++) {
+ if (m_cache_client_vec[i] != nullptr) {
+ m_cache_client_vec[i]->close();
+ delete m_cache_client_vec[i];
+ }
+ }
+ m_cache_server->stop();
+ if (m_cache_server_thread->joinable()) {
+ m_cache_server_thread->join();
+ }
+ delete m_cache_server;
+ delete m_cache_server_thread;
+
+ std::remove(m_local_path.c_str());
+ }
+
+ CacheClient* create_session(uint64_t random_index) {
+ CacheClient* cache_client = new CacheClient(m_local_path, g_ceph_context);
+ cache_client->run();
+ while (true) {
+ if (0 == cache_client->connect()) {
+ break;
+ }
+ }
+ m_cache_client_vec[random_index] = cache_client;
+ return cache_client;
+ }
+
+ void server_handle_request(CacheSession* session_id, ObjectCacheRequest* req) {
+
+ switch (req->get_request_type()) {
+ case RBDSC_REGISTER: {
+ ObjectCacheRequest* reply = new ObjectCacheRegReplyData(RBDSC_REGISTER_REPLY,
+ req->seq);
+ session_id->send(reply);
+ break;
+ }
+ case RBDSC_READ: {
+ ObjectCacheRequest* reply = new ObjectCacheReadReplyData(RBDSC_READ_REPLY,
+ req->seq);
+ session_id->send(reply);
+ break;
+ }
+ }
+ }
+
+ void test_register_client(uint64_t random_index) {
+ ASSERT_TRUE(m_cache_client_vec[random_index] == nullptr);
+
+ auto ctx = new LambdaContext([](int ret){
+ ASSERT_TRUE(ret == 0);
+ });
+ auto session = create_session(random_index);
+ session->register_client(ctx);
+
+ ASSERT_TRUE(m_cache_client_vec[random_index] != nullptr);
+ ASSERT_TRUE(session->is_session_work());
+ }
+
+ void test_lookup_object(std::string pool_nspace, uint64_t index,
+ uint64_t request_num, bool is_last) {
+
+ for (uint64_t i = 0; i < request_num; i++) {
+ auto ctx = make_gen_lambda_context<ObjectCacheRequest*,
+ std::function<void(ObjectCacheRequest*)>>([this](ObjectCacheRequest* ack) {
+ m_recv_ack_index++;
+ });
+ m_send_request_index++;
+ // here just for concurrently testing register + lookup, so fix object id.
+ m_cache_client_vec[index]->lookup_object(pool_nspace, 1, 2, 3, "1234", std::move(ctx));
+ }
+
+ if (is_last) {
+ while(m_send_request_index != m_recv_ack_index) {
+ usleep(1);
+ }
+ m_wait_event.signal();
+ }
+ }
+};
+
+// test concurrent : multi-session + register_client + lookup_request
+TEST_F(TestMultiSession, test_multi_session) {
+
+ uint64_t test_times = 1000;
+ uint64_t test_session_num = 100;
+
+ for (uint64_t i = 0; i <= test_times; i++) {
+ uint64_t random_index = random() % test_session_num;
+ if (m_cache_client_vec[random_index] == nullptr) {
+ test_register_client(random_index);
+ } else {
+ test_lookup_object(string("test_nspace") + std::to_string(random_index),
+ random_index, 4, i == test_times ? true : false);
+ }
+ }
+
+ // make sure all ack will be received.
+ m_wait_event.wait();
+
+ ASSERT_TRUE(m_send_request_index == m_recv_ack_index);
+}
diff --git a/src/test/immutable_object_cache/test_object_store.cc b/src/test/immutable_object_cache/test_object_store.cc
new file mode 100644
index 000000000..f4d75274e
--- /dev/null
+++ b/src/test/immutable_object_cache/test_object_store.cc
@@ -0,0 +1,99 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <filesystem>
+#include <iostream>
+#include <unistd.h>
+
+
+#include "gtest/gtest.h"
+#include "include/Context.h"
+#include "include/rados/librados.hpp"
+#include "include/rbd/librbd.hpp"
+#include "librbd/ImageCtx.h"
+#include "test/librados/test.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+#include "test/librados/test_cxx.h"
+
+#include "tools/immutable_object_cache/ObjectCacheStore.h"
+
+namespace fs = std::filesystem;
+
+using namespace ceph::immutable_obj_cache;
+
+std::string test_cache_path("/tmp/test_ceph_immutable_shared_cache");
+
+class TestObjectStore : public ::testing::Test {
+public:
+ ObjectCacheStore* m_object_cache_store;
+ librados::Rados* m_test_rados;
+ CephContext* m_ceph_context;
+ librados::IoCtx m_local_io_ctx;
+ std::string m_temp_pool_name;
+ std::string m_temp_volume_name;
+
+ TestObjectStore(): m_object_cache_store(nullptr), m_test_rados(nullptr), m_ceph_context(nullptr){}
+
+ ~TestObjectStore(){}
+
+ static void SetUpTestCase() {}
+ static void TearDownTestCase() {}
+
+ void SetUp() override {
+ m_test_rados = new librados::Rados();
+ ASSERT_EQ("", connect_cluster_pp(*m_test_rados));
+ ASSERT_EQ(0, m_test_rados->conf_set("rbd_cache", "false"));
+ ASSERT_EQ(0, m_test_rados->conf_set("immutable_object_cache_max_size", "1024"));
+ ASSERT_EQ(0, m_test_rados->conf_set("immutable_object_cache_path", test_cache_path.c_str()));
+
+ }
+
+ void create_object_cache_store(uint64_t entry_num) {
+ m_temp_pool_name = get_temp_pool_name("test_pool_");
+ ASSERT_EQ(0, m_test_rados->pool_create(m_temp_pool_name.c_str()));
+ ASSERT_EQ(0, m_test_rados->ioctx_create(m_temp_pool_name.c_str(), m_local_io_ctx));
+ m_temp_volume_name = "test_volume";
+ m_ceph_context = reinterpret_cast<CephContext*>(m_test_rados->cct());
+ m_object_cache_store = new ObjectCacheStore(m_ceph_context);
+ }
+
+ void init_object_cache_store(std::string pool_name, std::string vol_name,
+ uint64_t vol_size, bool reset) {
+ ASSERT_EQ(0, m_object_cache_store->init(reset));
+ ASSERT_EQ(0, m_object_cache_store->init_cache());
+ }
+
+ void shutdown_object_cache_store() {
+ ASSERT_EQ(0, m_object_cache_store->shutdown());
+ }
+
+ void lookup_object_cache_store(std::string pool_name, std::string vol_name,
+ std::string obj_name, int& ret) {
+ std::string cache_path;
+ ret = m_object_cache_store->lookup_object(pool_name, 1, 2, 3,
+ obj_name, true, cache_path);
+ }
+
+ void TearDown() override {
+ if(m_test_rados)
+ delete m_test_rados;
+ if(m_object_cache_store)
+ delete m_object_cache_store;
+ }
+};
+
+TEST_F(TestObjectStore, test_1) {
+ create_object_cache_store(1000);
+
+ std::string cache_path(test_cache_path);
+
+ fs::remove_all(test_cache_path);
+
+ init_object_cache_store(m_temp_pool_name, m_temp_volume_name, 1000, true);
+
+
+ // TODO add lookup interface testing
+
+ shutdown_object_cache_store();
+}
diff --git a/src/test/journal/CMakeLists.txt b/src/test/journal/CMakeLists.txt
new file mode 100644
index 000000000..99e0f8ae6
--- /dev/null
+++ b/src/test/journal/CMakeLists.txt
@@ -0,0 +1,32 @@
+add_library(journal_test_mock STATIC mock/MockJournaler.cc)
+target_link_libraries(journal_test_mock
+ PUBLIC GMock::GMock)
+
+# unittest_journal
+set(unittest_journal_srcs
+ test_main.cc
+ test_Entry.cc
+ test_FutureImpl.cc
+ test_Journaler.cc
+ test_JournalMetadata.cc
+ test_JournalPlayer.cc
+ test_JournalRecorder.cc
+ test_JournalTrimmer.cc
+ test_ObjectPlayer.cc
+ test_ObjectRecorder.cc
+ RadosTestFixture.cc
+ )
+
+add_executable(unittest_journal
+ ${unittest_journal_srcs}
+ )
+add_ceph_unittest(unittest_journal)
+target_link_libraries(unittest_journal
+ journal
+ cls_journal
+ cls_journal_client
+ rados_test_stub
+ librados
+ radostest-cxx
+ global
+ )
diff --git a/src/test/journal/RadosTestFixture.cc b/src/test/journal/RadosTestFixture.cc
new file mode 100644
index 000000000..0da22ba2b
--- /dev/null
+++ b/src/test/journal/RadosTestFixture.cc
@@ -0,0 +1,135 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados/test_cxx.h"
+#include "test/journal/RadosTestFixture.h"
+#include "cls/journal/cls_journal_client.h"
+#include "include/stringify.h"
+#include "common/WorkQueue.h"
+#include "journal/Settings.h"
+
+using namespace std::chrono_literals;
+
+RadosTestFixture::RadosTestFixture()
+ : m_timer_lock(ceph::make_mutex("m_timer_lock")),
+ m_listener(this) {
+}
+
+void RadosTestFixture::SetUpTestCase() {
+ _pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(_pool_name, _rados));
+
+ CephContext* cct = reinterpret_cast<CephContext*>(_rados.cct());
+ _thread_pool = new ThreadPool(cct, "RadosTestFixture::_thread_pool",
+ "tp_test", 1);
+ _thread_pool->start();
+}
+
+void RadosTestFixture::TearDownTestCase() {
+ _thread_pool->stop();
+ delete _thread_pool;
+
+ ASSERT_EQ(0, destroy_one_pool_pp(_pool_name, _rados));
+}
+
+std::string RadosTestFixture::get_temp_oid() {
+ ++_oid_number;
+ return "oid" + stringify(_oid_number);
+}
+
+void RadosTestFixture::SetUp() {
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), m_ioctx));
+
+ CephContext* cct = reinterpret_cast<CephContext*>(m_ioctx.cct());
+ m_work_queue = new ContextWQ("RadosTestFixture::m_work_queue",
+ ceph::make_timespan(60),
+ _thread_pool);
+
+ m_timer = new SafeTimer(cct, m_timer_lock, true);
+ m_timer->init();
+}
+
+void RadosTestFixture::TearDown() {
+ for (auto metadata : m_metadatas) {
+ C_SaferCond ctx;
+ metadata->shut_down(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ {
+ std::lock_guard locker{m_timer_lock};
+ m_timer->shutdown();
+ }
+ delete m_timer;
+
+ m_work_queue->drain();
+ delete m_work_queue;
+}
+
+int RadosTestFixture::create(const std::string &oid, uint8_t order,
+ uint8_t splay_width) {
+ return cls::journal::client::create(m_ioctx, oid, order, splay_width, -1);
+}
+
+ceph::ref_t<journal::JournalMetadata> RadosTestFixture::create_metadata(
+ const std::string &oid, const std::string &client_id,
+ double commit_interval, int max_concurrent_object_sets) {
+ journal::Settings settings;
+ settings.commit_interval = commit_interval;
+ settings.max_concurrent_object_sets = max_concurrent_object_sets;
+
+ auto metadata = ceph::make_ref<journal::JournalMetadata>(
+ m_work_queue, m_timer, &m_timer_lock, m_ioctx, oid, client_id, settings);
+ m_metadatas.push_back(metadata);
+ return metadata;
+}
+
+int RadosTestFixture::append(const std::string &oid, const bufferlist &bl) {
+ librados::ObjectWriteOperation op;
+ op.append(bl);
+ return m_ioctx.operate(oid, &op);
+}
+
+int RadosTestFixture::client_register(const std::string &oid,
+ const std::string &id,
+ const std::string &description) {
+ bufferlist data;
+ data.append(description);
+ return cls::journal::client::client_register(m_ioctx, oid, id, data);
+}
+
+int RadosTestFixture::client_commit(const std::string &oid,
+ const std::string &id,
+ const cls::journal::ObjectSetPosition &commit_position) {
+ librados::ObjectWriteOperation op;
+ cls::journal::client::client_commit(&op, id, commit_position);
+ return m_ioctx.operate(oid, &op);
+}
+
+bufferlist RadosTestFixture::create_payload(const std::string &payload) {
+ bufferlist bl;
+ bl.append(payload);
+ return bl;
+}
+
+int RadosTestFixture::init_metadata(const ceph::ref_t<journal::JournalMetadata>& metadata) {
+ C_SaferCond cond;
+ metadata->init(&cond);
+ return cond.wait();
+}
+
+bool RadosTestFixture::wait_for_update(const ceph::ref_t<journal::JournalMetadata>& metadata) {
+ std::unique_lock locker{m_listener.mutex};
+ while (m_listener.updates[metadata.get()] == 0) {
+ if (m_listener.cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ --m_listener.updates[metadata.get()];
+ return true;
+}
+
+std::string RadosTestFixture::_pool_name;
+librados::Rados RadosTestFixture::_rados;
+uint64_t RadosTestFixture::_oid_number = 0;
+ThreadPool *RadosTestFixture::_thread_pool = nullptr;
diff --git a/src/test/journal/RadosTestFixture.h b/src/test/journal/RadosTestFixture.h
new file mode 100644
index 000000000..8ec662931
--- /dev/null
+++ b/src/test/journal/RadosTestFixture.h
@@ -0,0 +1,74 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados/test.h"
+#include "common/ceph_mutex.h"
+#include "common/Timer.h"
+#include "journal/JournalMetadata.h"
+#include "cls/journal/cls_journal_types.h"
+#include "gtest/gtest.h"
+
+class ThreadPool;
+
+class RadosTestFixture : public ::testing::Test {
+public:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ static std::string get_temp_oid();
+
+ RadosTestFixture();
+ void SetUp() override;
+ void TearDown() override;
+
+ int create(const std::string &oid, uint8_t order = 14,
+ uint8_t splay_width = 2);
+ ceph::ref_t<journal::JournalMetadata> create_metadata(const std::string &oid,
+ const std::string &client_id = "client",
+ double commit_internal = 0.1,
+ int max_concurrent_object_sets = 0);
+ int append(const std::string &oid, const bufferlist &bl);
+
+ int client_register(const std::string &oid, const std::string &id = "client",
+ const std::string &description = "");
+ int client_commit(const std::string &oid, const std::string &id,
+ const cls::journal::ObjectSetPosition &commit_position);
+
+ bufferlist create_payload(const std::string &payload);
+
+ struct Listener : public journal::JournalMetadataListener {
+ RadosTestFixture *test_fixture;
+ ceph::mutex mutex = ceph::make_mutex("mutex");
+ ceph::condition_variable cond;
+ std::map<journal::JournalMetadata*, uint32_t> updates;
+
+ Listener(RadosTestFixture *_test_fixture)
+ : test_fixture(_test_fixture) {}
+
+ void handle_update(journal::JournalMetadata *metadata) override {
+ std::lock_guard locker{mutex};
+ ++updates[metadata];
+ cond.notify_all();
+ }
+ };
+
+ int init_metadata(const ceph::ref_t<journal::JournalMetadata>& metadata);
+
+ bool wait_for_update(const ceph::ref_t<journal::JournalMetadata>& metadata);
+
+ static std::string _pool_name;
+ static librados::Rados _rados;
+ static uint64_t _oid_number;
+ static ThreadPool *_thread_pool;
+
+ librados::IoCtx m_ioctx;
+
+ ContextWQ *m_work_queue = nullptr;
+
+ ceph::mutex m_timer_lock;
+ SafeTimer *m_timer = nullptr;
+
+ Listener m_listener;
+
+ std::list<ceph::ref_t<journal::JournalMetadata>> m_metadatas;
+};
diff --git a/src/test/journal/mock/MockJournaler.cc b/src/test/journal/mock/MockJournaler.cc
new file mode 100644
index 000000000..90649440d
--- /dev/null
+++ b/src/test/journal/mock/MockJournaler.cc
@@ -0,0 +1,16 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "MockJournaler.h"
+
+namespace journal {
+
+MockFuture *MockFuture::s_instance = nullptr;
+MockReplayEntry *MockReplayEntry::s_instance = nullptr;
+MockJournaler *MockJournaler::s_instance = nullptr;
+
+std::ostream &operator<<(std::ostream &os, const MockJournalerProxy &) {
+ return os;
+}
+
+} // namespace journal
diff --git a/src/test/journal/mock/MockJournaler.h b/src/test/journal/mock/MockJournaler.h
new file mode 100644
index 000000000..d4e0f6c2a
--- /dev/null
+++ b/src/test/journal/mock/MockJournaler.h
@@ -0,0 +1,313 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef TEST_RBD_MIRROR_MOCK_JOURNALER_H
+#define TEST_RBD_MIRROR_MOCK_JOURNALER_H
+
+#include <gmock/gmock.h>
+#include "include/int_types.h"
+#include "include/rados/librados.hpp"
+#include "cls/journal/cls_journal_types.h"
+#include "journal/Journaler.h"
+#include <iosfwd>
+#include <string>
+
+class Context;
+
+namespace journal {
+
+struct ReplayHandler;
+struct Settings;
+
+struct MockFuture {
+ static MockFuture *s_instance;
+ static MockFuture &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockFuture() {
+ s_instance = this;
+ }
+
+ MOCK_CONST_METHOD0(is_valid, bool());
+ MOCK_METHOD1(flush, void(Context *));
+ MOCK_METHOD1(wait, void(Context *));
+};
+
+struct MockFutureProxy {
+ bool is_valid() const {
+ return MockFuture::get_instance().is_valid();
+ }
+
+ void flush(Context *on_safe) {
+ MockFuture::get_instance().flush(on_safe);
+ }
+
+ void wait(Context *on_safe) {
+ MockFuture::get_instance().wait(on_safe);
+ }
+};
+
+struct MockReplayEntry {
+ static MockReplayEntry *s_instance;
+ static MockReplayEntry &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockReplayEntry() {
+ s_instance = this;
+ }
+
+ MOCK_CONST_METHOD0(get_commit_tid, uint64_t());
+ MOCK_CONST_METHOD0(get_data, bufferlist());
+};
+
+struct MockReplayEntryProxy {
+ uint64_t get_commit_tid() const {
+ return MockReplayEntry::get_instance().get_commit_tid();
+ }
+
+ bufferlist get_data() const {
+ return MockReplayEntry::get_instance().get_data();
+ }
+};
+
+struct MockJournaler {
+ static MockJournaler *s_instance;
+ static MockJournaler &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockJournaler() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(construct, void());
+
+ MOCK_METHOD1(init, void(Context *));
+ MOCK_METHOD0(shut_down, void());
+ MOCK_METHOD1(shut_down, void(Context *));
+ MOCK_CONST_METHOD0(is_initialized, bool());
+
+ MOCK_METHOD3(get_metadata, void(uint8_t *order, uint8_t *splay_width,
+ int64_t *pool_id));
+ MOCK_METHOD4(get_mutable_metadata, void(uint64_t*, uint64_t*,
+ std::set<cls::journal::Client> *,
+ Context*));
+
+ MOCK_METHOD2(register_client, void(const bufferlist &, Context *));
+ MOCK_METHOD1(unregister_client, void(Context *));
+ MOCK_METHOD3(get_client, void(const std::string &, cls::journal::Client *,
+ Context *));
+ MOCK_METHOD2(get_cached_client, int(const std::string&, cls::journal::Client*));
+ MOCK_METHOD2(update_client, void(const bufferlist &, Context *));
+
+ MOCK_METHOD4(allocate_tag, void(uint64_t, const bufferlist &,
+ cls::journal::Tag*, Context *));
+ MOCK_METHOD3(get_tag, void(uint64_t, cls::journal::Tag *, Context *));
+ MOCK_METHOD3(get_tags, void(uint64_t, journal::Journaler::Tags*, Context*));
+ MOCK_METHOD4(get_tags, void(uint64_t, uint64_t, journal::Journaler::Tags*,
+ Context*));
+
+ MOCK_METHOD1(start_replay, void(::journal::ReplayHandler *replay_handler));
+ MOCK_METHOD2(start_live_replay, void(ReplayHandler *, double));
+ MOCK_METHOD1(try_pop_front, bool(MockReplayEntryProxy *));
+ MOCK_METHOD2(try_pop_front, bool(MockReplayEntryProxy *, uint64_t *));
+ MOCK_METHOD0(stop_replay, void());
+ MOCK_METHOD1(stop_replay, void(Context *on_finish));
+
+ MOCK_METHOD1(start_append, void(uint64_t));
+ MOCK_METHOD3(set_append_batch_options, void(int, uint64_t, double));
+ MOCK_CONST_METHOD0(get_max_append_size, uint64_t());
+ MOCK_METHOD2(append, MockFutureProxy(uint64_t tag_id,
+ const bufferlist &bl));
+ MOCK_METHOD1(flush, void(Context *on_safe));
+ MOCK_METHOD1(stop_append, void(Context *on_safe));
+
+ MOCK_METHOD1(committed, void(const MockReplayEntryProxy &));
+ MOCK_METHOD1(committed, void(const MockFutureProxy &future));
+ MOCK_METHOD1(flush_commit_position, void(Context*));
+
+ MOCK_METHOD1(add_listener, void(JournalMetadataListener *));
+ MOCK_METHOD1(remove_listener, void(JournalMetadataListener *));
+
+};
+
+struct MockJournalerProxy {
+ MockJournalerProxy() {
+ MockJournaler::get_instance().construct();
+ }
+
+ template <typename IoCtxT>
+ MockJournalerProxy(IoCtxT &header_ioctx, const std::string &,
+ const std::string &, const Settings&,
+ journal::CacheManagerHandler *) {
+ MockJournaler::get_instance().construct();
+ }
+
+ template <typename WorkQueue, typename Timer>
+ MockJournalerProxy(WorkQueue *work_queue, Timer *timer, ceph::mutex *timer_lock,
+ librados::IoCtx &header_ioctx,
+ const std::string &journal_id,
+ const std::string &client_id, const Settings&,
+ journal::CacheManagerHandler *) {
+ MockJournaler::get_instance().construct();
+ }
+
+ void exists(Context *on_finish) const {
+ on_finish->complete(-EINVAL);
+ }
+ void create(uint8_t order, uint8_t splay_width, int64_t pool_id, Context *on_finish) {
+ on_finish->complete(-EINVAL);
+ }
+ void remove(bool force, Context *on_finish) {
+ on_finish->complete(-EINVAL);
+ }
+ int register_client(const bufferlist &data) {
+ return -EINVAL;
+ }
+
+ void allocate_tag(uint64_t tag_class, const bufferlist &tag_data,
+ cls::journal::Tag* tag, Context *on_finish) {
+ MockJournaler::get_instance().allocate_tag(tag_class, tag_data, tag,
+ on_finish);
+ }
+
+ void init(Context *on_finish) {
+ MockJournaler::get_instance().init(on_finish);
+ }
+ void shut_down() {
+ MockJournaler::get_instance().shut_down();
+ }
+ void shut_down(Context *on_finish) {
+ MockJournaler::get_instance().shut_down(on_finish);
+ }
+ bool is_initialized() const {
+ return MockJournaler::get_instance().is_initialized();
+ }
+
+ void get_metadata(uint8_t *order, uint8_t *splay_width, int64_t *pool_id) {
+ MockJournaler::get_instance().get_metadata(order, splay_width, pool_id);
+ }
+
+ void get_mutable_metadata(uint64_t *min, uint64_t *active,
+ std::set<cls::journal::Client> *clients,
+ Context *on_finish) {
+ MockJournaler::get_instance().get_mutable_metadata(min, active, clients,
+ on_finish);
+ }
+
+ void register_client(const bufferlist &data, Context *on_finish) {
+ MockJournaler::get_instance().register_client(data, on_finish);
+ }
+
+ void unregister_client(Context *on_finish) {
+ MockJournaler::get_instance().unregister_client(on_finish);
+ }
+
+ void get_client(const std::string &client_id, cls::journal::Client *client,
+ Context *on_finish) {
+ MockJournaler::get_instance().get_client(client_id, client, on_finish);
+ }
+
+ int get_cached_client(const std::string& client_id,
+ cls::journal::Client* client) {
+ return MockJournaler::get_instance().get_cached_client(client_id, client);
+ }
+
+ void update_client(const bufferlist &client_data, Context *on_finish) {
+ MockJournaler::get_instance().update_client(client_data, on_finish);
+ }
+
+ void get_tag(uint64_t tag_tid, cls::journal::Tag *tag, Context *on_finish) {
+ MockJournaler::get_instance().get_tag(tag_tid, tag, on_finish);
+ }
+
+ void get_tags(uint64_t tag_class, journal::Journaler::Tags *tags,
+ Context *on_finish) {
+ MockJournaler::get_instance().get_tags(tag_class, tags, on_finish);
+ }
+ void get_tags(uint64_t start_after_tag_tid, uint64_t tag_class,
+ journal::Journaler::Tags *tags, Context *on_finish) {
+ MockJournaler::get_instance().get_tags(start_after_tag_tid, tag_class, tags,
+ on_finish);
+ }
+
+ void start_replay(::journal::ReplayHandler *replay_handler) {
+ MockJournaler::get_instance().start_replay(replay_handler);
+ }
+
+ void start_live_replay(ReplayHandler *handler, double interval) {
+ MockJournaler::get_instance().start_live_replay(handler, interval);
+ }
+
+ bool try_pop_front(MockReplayEntryProxy *replay_entry) {
+ return MockJournaler::get_instance().try_pop_front(replay_entry);
+ }
+
+ bool try_pop_front(MockReplayEntryProxy *entry, uint64_t *tag_tid) {
+ return MockJournaler::get_instance().try_pop_front(entry, tag_tid);
+ }
+
+ void stop_replay() {
+ MockJournaler::get_instance().stop_replay();
+ }
+ void stop_replay(Context *on_finish) {
+ MockJournaler::get_instance().stop_replay(on_finish);
+ }
+
+ void start_append(uint64_t max_in_flight_appends) {
+ MockJournaler::get_instance().start_append(max_in_flight_appends);
+ }
+
+ void set_append_batch_options(int flush_interval, uint64_t flush_bytes,
+ double flush_age) {
+ MockJournaler::get_instance().set_append_batch_options(
+ flush_interval, flush_bytes, flush_age);
+ }
+
+ uint64_t get_max_append_size() const {
+ return MockJournaler::get_instance().get_max_append_size();
+ }
+
+ MockFutureProxy append(uint64_t tag_id, const bufferlist &bl) {
+ return MockJournaler::get_instance().append(tag_id, bl);
+ }
+
+ void flush(Context *on_safe) {
+ MockJournaler::get_instance().flush(on_safe);
+ }
+
+ void stop_append(Context *on_safe) {
+ MockJournaler::get_instance().stop_append(on_safe);
+ }
+
+ void committed(const MockReplayEntryProxy &entry) {
+ MockJournaler::get_instance().committed(entry);
+ }
+
+ void committed(const MockFutureProxy &future) {
+ MockJournaler::get_instance().committed(future);
+ }
+
+ void flush_commit_position(Context *on_finish) {
+ MockJournaler::get_instance().flush_commit_position(on_finish);
+ }
+
+ void add_listener(JournalMetadataListener *listener) {
+ MockJournaler::get_instance().add_listener(listener);
+ }
+
+ void remove_listener(JournalMetadataListener *listener) {
+ MockJournaler::get_instance().remove_listener(listener);
+ }
+};
+
+std::ostream &operator<<(std::ostream &os, const MockJournalerProxy &);
+
+} // namespace journal
+
+#endif // TEST_RBD_MIRROR_MOCK_JOURNALER_H
diff --git a/src/test/journal/test_Entry.cc b/src/test/journal/test_Entry.cc
new file mode 100644
index 000000000..1fa3136f7
--- /dev/null
+++ b/src/test/journal/test_Entry.cc
@@ -0,0 +1,96 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/Entry.h"
+#include "gtest/gtest.h"
+
+class TestEntry : public ::testing::Test {
+};
+
+TEST_F(TestEntry, DefaultConstructor) {
+ journal::Entry entry;
+ ASSERT_EQ(0U, entry.get_entry_tid());
+ ASSERT_EQ(0U, entry.get_tag_tid());
+
+ bufferlist data(entry.get_data());
+ bufferlist expected_data;
+ ASSERT_TRUE(data.contents_equal(expected_data));
+}
+
+TEST_F(TestEntry, Constructor) {
+ bufferlist data;
+ data.append("data");
+ journal::Entry entry(234, 123, data);
+
+ data.clear();
+ data = entry.get_data();
+
+ bufferlist expected_data;
+ expected_data.append("data");
+
+ ASSERT_EQ(123U, entry.get_entry_tid());
+ ASSERT_EQ(234U, entry.get_tag_tid());
+ ASSERT_TRUE(data.contents_equal(expected_data));
+}
+
+TEST_F(TestEntry, IsReadable) {
+ bufferlist data;
+ data.append("data");
+ journal::Entry entry(234, 123, data);
+
+ bufferlist full_bl;
+ encode(entry, full_bl);
+
+ uint32_t bytes_needed;
+ for (size_t i = 0; i < full_bl.length() - 1; ++i) {
+ bufferlist partial_bl;
+ if (i > 0) {
+ partial_bl.substr_of(full_bl, 0, i);
+ }
+ ASSERT_FALSE(journal::Entry::is_readable(partial_bl.begin(),
+ &bytes_needed));
+ ASSERT_GT(bytes_needed, 0U);
+ }
+ ASSERT_TRUE(journal::Entry::is_readable(full_bl.begin(), &bytes_needed));
+ ASSERT_EQ(0U, bytes_needed);
+}
+
+TEST_F(TestEntry, IsReadableBadPreamble) {
+ bufferlist data;
+ data.append("data");
+ journal::Entry entry(234, 123, data);
+
+ uint64_t stray_bytes = 0x1122334455667788;
+ bufferlist full_bl;
+ encode(stray_bytes, full_bl);
+ encode(entry, full_bl);
+
+ uint32_t bytes_needed;
+ bufferlist::iterator it = full_bl.begin();
+ ASSERT_FALSE(journal::Entry::is_readable(it, &bytes_needed));
+ ASSERT_EQ(0U, bytes_needed);
+
+ it += sizeof(stray_bytes);
+ ASSERT_TRUE(journal::Entry::is_readable(it, &bytes_needed));
+ ASSERT_EQ(0U, bytes_needed);
+}
+
+TEST_F(TestEntry, IsReadableBadCRC) {
+ bufferlist data;
+ data.append("data");
+ journal::Entry entry(234, 123, data);
+
+ bufferlist full_bl;
+ encode(entry, full_bl);
+
+ bufferlist bad_bl;
+ bad_bl.substr_of(full_bl, 0, full_bl.length() - 4);
+ encode(full_bl.crc32c(1), bad_bl);
+
+ uint32_t bytes_needed;
+ ASSERT_FALSE(journal::Entry::is_readable(bad_bl.begin(), &bytes_needed));
+ ASSERT_EQ(0U, bytes_needed);
+
+
+
+}
diff --git a/src/test/journal/test_FutureImpl.cc b/src/test/journal/test_FutureImpl.cc
new file mode 100644
index 000000000..1ff346dcf
--- /dev/null
+++ b/src/test/journal/test_FutureImpl.cc
@@ -0,0 +1,268 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/FutureImpl.h"
+#include "common/Cond.h"
+#include "gtest/gtest.h"
+#include "test/journal/RadosTestFixture.h"
+
+class TestFutureImpl : public RadosTestFixture {
+public:
+ struct FlushHandler : public journal::FutureImpl::FlushHandler {
+ uint64_t flushes = 0;
+ void flush(const ceph::ref_t<journal::FutureImpl>& future) override {
+ ++flushes;
+ }
+ FlushHandler() = default;
+ };
+
+ TestFutureImpl() {
+ m_flush_handler = std::make_shared<FlushHandler>();
+ }
+
+ auto create_future(uint64_t tag_tid, uint64_t entry_tid,
+ uint64_t commit_tid,
+ ceph::ref_t<journal::FutureImpl> prev = nullptr) {
+ auto future = ceph::make_ref<journal::FutureImpl>(tag_tid, entry_tid, commit_tid);
+ future->init(prev);
+ return future;
+ }
+
+ void flush(const ceph::ref_t<journal::FutureImpl>& future) {
+ }
+
+ std::shared_ptr<FlushHandler> m_flush_handler;
+};
+
+TEST_F(TestFutureImpl, Getters) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ ASSERT_EQ(234U, future->get_tag_tid());
+ ASSERT_EQ(123U, future->get_entry_tid());
+ ASSERT_EQ(456U, future->get_commit_tid());
+}
+
+TEST_F(TestFutureImpl, Attach) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ ASSERT_FALSE(future->attach(m_flush_handler));
+ ASSERT_EQ(2U, m_flush_handler.use_count());
+}
+
+TEST_F(TestFutureImpl, AttachWithPendingFlush) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ future->flush(NULL);
+
+ ASSERT_TRUE(future->attach(m_flush_handler));
+ ASSERT_EQ(2U, m_flush_handler.use_count());
+}
+
+TEST_F(TestFutureImpl, Detach) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ ASSERT_FALSE(future->attach(m_flush_handler));
+ future->detach();
+ ASSERT_EQ(1U, m_flush_handler.use_count());
+}
+
+TEST_F(TestFutureImpl, DetachImplicit) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ ASSERT_FALSE(future->attach(m_flush_handler));
+ future.reset();
+ ASSERT_EQ(1U, m_flush_handler.use_count());
+}
+
+TEST_F(TestFutureImpl, Flush) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ ASSERT_FALSE(future->attach(m_flush_handler));
+
+ C_SaferCond cond;
+ future->flush(&cond);
+
+ ASSERT_EQ(1U, m_flush_handler->flushes);
+ future->safe(-EIO);
+ ASSERT_EQ(-EIO, cond.wait());
+}
+
+TEST_F(TestFutureImpl, FlushWithoutContext) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ ASSERT_FALSE(future->attach(m_flush_handler));
+
+ future->flush(NULL);
+ ASSERT_EQ(1U, m_flush_handler->flushes);
+ future->safe(-EIO);
+ ASSERT_TRUE(future->is_complete());
+ ASSERT_EQ(-EIO, future->get_return_value());
+}
+
+TEST_F(TestFutureImpl, FlushChain) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future1 = create_future(234, 123, 456);
+ auto future2 = create_future(234, 124, 457, future1);
+ auto future3 = create_future(235, 1, 458, future2);
+
+ auto flush_handler = std::make_shared<FlushHandler>();
+ ASSERT_FALSE(future1->attach(m_flush_handler));
+ ASSERT_FALSE(future2->attach(flush_handler));
+ ASSERT_FALSE(future3->attach(m_flush_handler));
+
+ C_SaferCond cond;
+ future3->flush(&cond);
+
+ ASSERT_EQ(1U, m_flush_handler->flushes);
+ ASSERT_EQ(1U, flush_handler->flushes);
+
+ future3->safe(0);
+ ASSERT_FALSE(future3->is_complete());
+
+ future1->safe(0);
+ ASSERT_FALSE(future3->is_complete());
+
+ future2->safe(-EIO);
+ ASSERT_TRUE(future3->is_complete());
+ ASSERT_EQ(-EIO, future3->get_return_value());
+ ASSERT_EQ(-EIO, cond.wait());
+ ASSERT_EQ(0, future1->get_return_value());
+}
+
+TEST_F(TestFutureImpl, FlushInProgress) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future1 = create_future(234, 123, 456);
+ auto future2 = create_future(234, 124, 457, future1);
+ ASSERT_FALSE(future1->attach(m_flush_handler));
+ ASSERT_FALSE(future2->attach(m_flush_handler));
+
+ future1->set_flush_in_progress();
+ ASSERT_TRUE(future1->is_flush_in_progress());
+
+ future1->flush(NULL);
+ ASSERT_EQ(0U, m_flush_handler->flushes);
+
+ future1->safe(0);
+}
+
+TEST_F(TestFutureImpl, FlushAlreadyComplete) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 123, 456);
+ future->safe(-EIO);
+
+ C_SaferCond cond;
+ future->flush(&cond);
+ ASSERT_EQ(-EIO, cond.wait());
+}
+
+TEST_F(TestFutureImpl, Wait) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 1, 456);
+
+ C_SaferCond cond;
+ future->wait(&cond);
+ future->safe(-EEXIST);
+ ASSERT_EQ(-EEXIST, cond.wait());
+}
+
+TEST_F(TestFutureImpl, WaitAlreadyComplete) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future = create_future(234, 1, 456);
+ future->safe(-EEXIST);
+
+ C_SaferCond cond;
+ future->wait(&cond);
+ ASSERT_EQ(-EEXIST, cond.wait());
+}
+
+TEST_F(TestFutureImpl, SafePreservesError) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future1 = create_future(234, 123, 456);
+ auto future2 = create_future(234, 124, 457, future1);
+
+ future1->safe(-EIO);
+ future2->safe(-EEXIST);
+ ASSERT_TRUE(future2->is_complete());
+ ASSERT_EQ(-EIO, future2->get_return_value());
+}
+
+TEST_F(TestFutureImpl, ConsistentPreservesError) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ auto future1 = create_future(234, 123, 456);
+ auto future2 = create_future(234, 124, 457, future1);
+
+ future2->safe(-EEXIST);
+ future1->safe(-EIO);
+ ASSERT_TRUE(future2->is_complete());
+ ASSERT_EQ(-EEXIST, future2->get_return_value());
+}
diff --git a/src/test/journal/test_JournalMetadata.cc b/src/test/journal/test_JournalMetadata.cc
new file mode 100644
index 000000000..4108d4da3
--- /dev/null
+++ b/src/test/journal/test_JournalMetadata.cc
@@ -0,0 +1,210 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/JournalMetadata.h"
+#include "test/journal/RadosTestFixture.h"
+#include "common/Cond.h"
+#include <map>
+
+class TestJournalMetadata : public RadosTestFixture {
+public:
+ void TearDown() override {
+ for (MetadataList::iterator it = m_metadata_list.begin();
+ it != m_metadata_list.end(); ++it) {
+ (*it)->remove_listener(&m_listener);
+ }
+ m_metadata_list.clear();
+
+ RadosTestFixture::TearDown();
+ }
+
+ auto create_metadata(const std::string &oid,
+ const std::string &client_id,
+ double commit_interval = 0.1,
+ int max_concurrent_object_sets = 0) {
+ auto metadata = RadosTestFixture::create_metadata(
+ oid, client_id, commit_interval, max_concurrent_object_sets);
+ m_metadata_list.push_back(metadata);
+ metadata->add_listener(&m_listener);
+ return metadata;
+ }
+
+ typedef std::list<ceph::ref_t<journal::JournalMetadata>> MetadataList;
+ MetadataList m_metadata_list;
+};
+
+TEST_F(TestJournalMetadata, JournalDNE) {
+ std::string oid = get_temp_oid();
+
+ auto metadata1 = create_metadata(oid, "client1");
+ ASSERT_EQ(-ENOENT, init_metadata(metadata1));
+}
+
+TEST_F(TestJournalMetadata, ClientDNE) {
+ std::string oid = get_temp_oid();
+
+ ASSERT_EQ(0, create(oid, 14, 2));
+ ASSERT_EQ(0, client_register(oid, "client1", ""));
+
+ auto metadata1 = create_metadata(oid, "client1");
+ ASSERT_EQ(0, init_metadata(metadata1));
+
+ auto metadata2 = create_metadata(oid, "client2");
+ ASSERT_EQ(-ENOENT, init_metadata(metadata2));
+}
+
+TEST_F(TestJournalMetadata, Committed) {
+ std::string oid = get_temp_oid();
+
+ ASSERT_EQ(0, create(oid, 14, 2));
+ ASSERT_EQ(0, client_register(oid, "client1", ""));
+
+ auto metadata1 = create_metadata(oid, "client1", 600);
+ ASSERT_EQ(0, init_metadata(metadata1));
+
+ auto metadata2 = create_metadata(oid, "client1");
+ ASSERT_EQ(0, init_metadata(metadata2));
+ ASSERT_TRUE(wait_for_update(metadata2));
+
+ journal::JournalMetadata::ObjectSetPosition expect_commit_position;
+ journal::JournalMetadata::ObjectSetPosition read_commit_position;
+ metadata1->get_commit_position(&read_commit_position);
+ ASSERT_EQ(expect_commit_position, read_commit_position);
+
+ uint64_t commit_tid1 = metadata1->allocate_commit_tid(0, 0, 0);
+ uint64_t commit_tid2 = metadata1->allocate_commit_tid(0, 1, 0);
+ uint64_t commit_tid3 = metadata1->allocate_commit_tid(1, 0, 1);
+ uint64_t commit_tid4 = metadata1->allocate_commit_tid(0, 0, 2);
+
+ // cannot commit until tid1 + 2 committed
+ metadata1->committed(commit_tid2, []() { return nullptr; });
+ metadata1->committed(commit_tid3, []() { return nullptr; });
+
+ C_SaferCond cond1;
+ metadata1->committed(commit_tid1, [&cond1]() { return &cond1; });
+
+ // given our 10 minute commit internal, this should override the
+ // in-flight commit
+ C_SaferCond cond2;
+ metadata1->committed(commit_tid4, [&cond2]() { return &cond2; });
+
+ ASSERT_EQ(-ESTALE, cond1.wait());
+ metadata1->flush_commit_position();
+ ASSERT_EQ(0, cond2.wait());
+
+ ASSERT_TRUE(wait_for_update(metadata2));
+ metadata2->get_commit_position(&read_commit_position);
+ expect_commit_position = {{{0, 0, 2}, {1, 0, 1}}};
+ ASSERT_EQ(expect_commit_position, read_commit_position);
+}
+
+TEST_F(TestJournalMetadata, UpdateActiveObject) {
+ std::string oid = get_temp_oid();
+
+ ASSERT_EQ(0, create(oid, 14, 2));
+ ASSERT_EQ(0, client_register(oid, "client1", ""));
+
+ auto metadata1 = create_metadata(oid, "client1");
+ ASSERT_EQ(0, init_metadata(metadata1));
+ ASSERT_TRUE(wait_for_update(metadata1));
+
+ ASSERT_EQ(0U, metadata1->get_active_set());
+
+ ASSERT_EQ(0, metadata1->set_active_set(123));
+ ASSERT_TRUE(wait_for_update(metadata1));
+
+ ASSERT_EQ(123U, metadata1->get_active_set());
+}
+
+TEST_F(TestJournalMetadata, DisconnectLaggyClient) {
+ std::string oid = get_temp_oid();
+
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid, "client1", ""));
+ ASSERT_EQ(0, client_register(oid, "client2", "laggy"));
+
+ int max_concurrent_object_sets = 100;
+ auto metadata =
+ create_metadata(oid, "client1", 0.1, max_concurrent_object_sets);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ ASSERT_EQ(0U, metadata->get_active_set());
+
+ journal::JournalMetadata::RegisteredClients clients;
+
+#define ASSERT_CLIENT_STATES(s1, s2) \
+ ASSERT_EQ(2U, clients.size()); \
+ for (auto &c : clients) { \
+ if (c.id == "client1") { \
+ ASSERT_EQ(c.state, s1); \
+ } else if (c.id == "client2") { \
+ ASSERT_EQ(c.state, s2); \
+ } else { \
+ ASSERT_TRUE(false); \
+ } \
+ }
+
+ metadata->get_registered_clients(&clients);
+ ASSERT_CLIENT_STATES(cls::journal::CLIENT_STATE_CONNECTED,
+ cls::journal::CLIENT_STATE_CONNECTED);
+
+ // client2 is connected when active set <= max_concurrent_object_sets
+ ASSERT_EQ(0, metadata->set_active_set(max_concurrent_object_sets));
+ ASSERT_TRUE(wait_for_update(metadata));
+ uint64_t commit_tid = metadata->allocate_commit_tid(0, 0, 0);
+ C_SaferCond cond1;
+ metadata->committed(commit_tid, [&cond1]() { return &cond1; });
+ ASSERT_EQ(0, cond1.wait());
+ metadata->flush_commit_position();
+ ASSERT_TRUE(wait_for_update(metadata));
+ ASSERT_EQ(100U, metadata->get_active_set());
+ clients.clear();
+ metadata->get_registered_clients(&clients);
+ ASSERT_CLIENT_STATES(cls::journal::CLIENT_STATE_CONNECTED,
+ cls::journal::CLIENT_STATE_CONNECTED);
+
+ // client2 is disconnected when active set > max_concurrent_object_sets
+ ASSERT_EQ(0, metadata->set_active_set(max_concurrent_object_sets + 1));
+ ASSERT_TRUE(wait_for_update(metadata));
+ commit_tid = metadata->allocate_commit_tid(0, 0, 1);
+ C_SaferCond cond2;
+ metadata->committed(commit_tid, [&cond2]() { return &cond2; });
+ ASSERT_EQ(0, cond2.wait());
+ metadata->flush_commit_position();
+ ASSERT_TRUE(wait_for_update(metadata));
+ ASSERT_EQ(101U, metadata->get_active_set());
+ clients.clear();
+ metadata->get_registered_clients(&clients);
+ ASSERT_CLIENT_STATES(cls::journal::CLIENT_STATE_CONNECTED,
+ cls::journal::CLIENT_STATE_DISCONNECTED);
+}
+
+TEST_F(TestJournalMetadata, AssertActiveTag) {
+ std::string oid = get_temp_oid();
+
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid, "client1", ""));
+
+ auto metadata = create_metadata(oid, "client1");
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ C_SaferCond ctx1;
+ cls::journal::Tag tag1;
+ metadata->allocate_tag(cls::journal::Tag::TAG_CLASS_NEW, {}, &tag1, &ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ metadata->assert_active_tag(tag1.tid, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ C_SaferCond ctx3;
+ cls::journal::Tag tag2;
+ metadata->allocate_tag(tag1.tag_class, {}, &tag2, &ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+
+ C_SaferCond ctx4;
+ metadata->assert_active_tag(tag1.tid, &ctx4);
+ ASSERT_EQ(-ESTALE, ctx4.wait());
+}
diff --git a/src/test/journal/test_JournalPlayer.cc b/src/test/journal/test_JournalPlayer.cc
new file mode 100644
index 000000000..63d2d0a30
--- /dev/null
+++ b/src/test/journal/test_JournalPlayer.cc
@@ -0,0 +1,995 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/JournalPlayer.h"
+#include "journal/Entry.h"
+#include "journal/JournalMetadata.h"
+#include "journal/ReplayHandler.h"
+#include "include/stringify.h"
+#include "common/ceph_mutex.h"
+#include "gtest/gtest.h"
+#include "test/journal/RadosTestFixture.h"
+#include <list>
+#include <boost/scope_exit.hpp>
+
+using namespace std::chrono_literals;
+typedef std::list<journal::Entry> Entries;
+
+template <typename T>
+class TestJournalPlayer : public RadosTestFixture {
+public:
+ typedef std::list<journal::JournalPlayer *> JournalPlayers;
+
+ static const uint64_t max_fetch_bytes = T::max_fetch_bytes;
+
+ struct ReplayHandler : public journal::ReplayHandler {
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cond;
+ bool entries_available;
+ bool complete;
+ int complete_result;
+
+ ReplayHandler()
+ : entries_available(false), complete(false),
+ complete_result(0) {}
+
+ void handle_entries_available() override {
+ std::lock_guard locker{lock};
+ entries_available = true;
+ cond.notify_all();
+ }
+
+ void handle_complete(int r) override {
+ std::lock_guard locker{lock};
+ complete = true;
+ complete_result = r;
+ cond.notify_all();
+ }
+ };
+
+ void TearDown() override {
+ for (JournalPlayers::iterator it = m_players.begin();
+ it != m_players.end(); ++it) {
+ delete *it;
+ }
+ RadosTestFixture::TearDown();
+ }
+
+ auto create_metadata(const std::string &oid) {
+ return RadosTestFixture::create_metadata(oid, "client", 0.1,
+ max_fetch_bytes);
+ }
+
+ int client_commit(const std::string &oid,
+ journal::JournalPlayer::ObjectSetPosition position) {
+ return RadosTestFixture::client_commit(oid, "client", position);
+ }
+
+ journal::Entry create_entry(uint64_t tag_tid, uint64_t entry_tid) {
+ std::string payload(128, '0');
+ bufferlist payload_bl;
+ payload_bl.append(payload);
+ return journal::Entry(tag_tid, entry_tid, payload_bl);
+ }
+
+ journal::JournalPlayer *create_player(const std::string &oid,
+ const ceph::ref_t<journal::JournalMetadata>& metadata) {
+ journal::JournalPlayer *player(new journal::JournalPlayer(
+ m_ioctx, oid + ".", metadata, &m_replay_hander, nullptr));
+ m_players.push_back(player);
+ return player;
+ }
+
+ bool wait_for_entries(journal::JournalPlayer *player, uint32_t count,
+ Entries *entries) {
+ entries->clear();
+ while (entries->size() < count) {
+ journal::Entry entry;
+ uint64_t commit_tid;
+ while (entries->size() < count &&
+ player->try_pop_front(&entry, &commit_tid)) {
+ entries->push_back(entry);
+ }
+ if (entries->size() == count) {
+ break;
+ }
+
+ std::unique_lock locker{m_replay_hander.lock};
+ if (m_replay_hander.entries_available) {
+ m_replay_hander.entries_available = false;
+ } else if (m_replay_hander.cond.wait_for(locker, 10s) ==
+ std::cv_status::timeout) {
+ break;
+ }
+ }
+ return entries->size() == count;
+ }
+
+ bool wait_for_complete(journal::JournalPlayer *player) {
+ std::unique_lock locker{m_replay_hander.lock};
+ while (!m_replay_hander.complete) {
+ journal::Entry entry;
+ uint64_t commit_tid;
+ player->try_pop_front(&entry, &commit_tid);
+
+ if (m_replay_hander.cond.wait_for(locker, 10s) ==
+ std::cv_status::timeout) {
+ return false;
+ }
+ }
+ m_replay_hander.complete = false;
+ return true;
+ }
+
+ int write_entry(const std::string &oid, uint64_t object_num,
+ uint64_t tag_tid, uint64_t entry_tid) {
+ bufferlist bl;
+ encode(create_entry(tag_tid, entry_tid), bl);
+ return append(oid + "." + stringify(object_num), bl);
+ }
+
+ JournalPlayers m_players;
+ ReplayHandler m_replay_hander;
+};
+
+template <uint64_t _max_fetch_bytes>
+class TestJournalPlayerParams {
+public:
+ static const uint64_t max_fetch_bytes = _max_fetch_bytes;
+};
+
+typedef ::testing::Types<TestJournalPlayerParams<0>,
+ TestJournalPlayerParams<16> > TestJournalPlayerTypes;
+TYPED_TEST_SUITE(TestJournalPlayer, TestJournalPlayerTypes);
+
+TYPED_TEST(TestJournalPlayer, Prefetch) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions;
+ positions = {
+ cls::journal::ObjectPosition(0, 234, 122) };
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 122));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 123));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 124));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 125));
+
+ player->prefetch();
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+ ASSERT_TRUE(this->wait_for_complete(player));
+
+ Entries expected_entries;
+ expected_entries = {
+ this->create_entry(234, 123),
+ this->create_entry(234, 124),
+ this->create_entry(234, 125)};
+ ASSERT_EQ(expected_entries, entries);
+
+ uint64_t last_tid;
+ ASSERT_TRUE(metadata->get_last_allocated_entry_tid(234, &last_tid));
+ ASSERT_EQ(125U, last_tid);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchSkip) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions;
+ positions = {
+ cls::journal::ObjectPosition(0, 234, 125),
+ cls::journal::ObjectPosition(1, 234, 124) };
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 122));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 123));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 124));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 125));
+
+ player->prefetch();
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 0, &entries));
+ ASSERT_TRUE(this->wait_for_complete(player));
+
+ uint64_t last_tid;
+ ASSERT_TRUE(metadata->get_last_allocated_entry_tid(234, &last_tid));
+ ASSERT_EQ(125U, last_tid);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchWithoutCommit) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 122));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 123));
+
+ player->prefetch();
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 2, &entries));
+ ASSERT_TRUE(this->wait_for_complete(player));
+
+ Entries expected_entries;
+ expected_entries = {
+ this->create_entry(234, 122),
+ this->create_entry(234, 123)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchMultipleTags) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions;
+ positions = {
+ cls::journal::ObjectPosition(2, 234, 122),
+ cls::journal::ObjectPosition(1, 234, 121),
+ cls::journal::ObjectPosition(0, 234, 120)};
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid, 14, 3));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 120));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 121));
+ ASSERT_EQ(0, this->write_entry(oid, 2, 234, 122));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 123));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 124));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 236, 0)); // new tag allocated
+
+ player->prefetch();
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+ ASSERT_TRUE(this->wait_for_complete(player));
+
+ uint64_t last_tid;
+ ASSERT_TRUE(metadata->get_last_allocated_entry_tid(234, &last_tid));
+ ASSERT_EQ(124U, last_tid);
+ ASSERT_TRUE(metadata->get_last_allocated_entry_tid(236, &last_tid));
+ ASSERT_EQ(0U, last_tid);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchCorruptSequence) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 120));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 121));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 124));
+
+ player->prefetch();
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 2, &entries));
+
+ journal::Entry entry;
+ uint64_t commit_tid;
+ ASSERT_FALSE(player->try_pop_front(&entry, &commit_tid));
+ ASSERT_TRUE(this->wait_for_complete(player));
+ ASSERT_EQ(-ENOMSG, this->m_replay_hander.complete_result);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchMissingSequence) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid, 14, 4));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, metadata->set_active_set(1));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 2, 852));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 2, 856));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 2, 860));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 2, 853));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 2, 857));
+ ASSERT_EQ(0, this->write_entry(oid, 5, 2, 861));
+ ASSERT_EQ(0, this->write_entry(oid, 2, 2, 854));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 3, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 5, 3, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 2, 3, 2));
+ ASSERT_EQ(0, this->write_entry(oid, 3, 3, 3));
+
+ player->prefetch();
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 7, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(2, 852),
+ this->create_entry(2, 853),
+ this->create_entry(2, 854),
+ this->create_entry(3, 0),
+ this->create_entry(3, 1),
+ this->create_entry(3, 2),
+ this->create_entry(3, 3)};
+ ASSERT_EQ(expected_entries, entries);
+
+ ASSERT_TRUE(this->wait_for_complete(player));
+ ASSERT_EQ(0, this->m_replay_hander.complete_result);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchLargeMissingSequence) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, metadata->set_active_set(2));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 3, 0, 3));
+ ASSERT_EQ(0, this->write_entry(oid, 4, 1, 0));
+
+ player->prefetch();
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(0, 0),
+ this->create_entry(0, 1),
+ this->create_entry(1, 0)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchBlockedNewTag) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 2));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 4));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 1, 0));
+
+ player->prefetch();
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 4, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(0, 0),
+ this->create_entry(0, 1),
+ this->create_entry(0, 2),
+ this->create_entry(1, 0)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchStaleEntries) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions = {
+ cls::journal::ObjectPosition(0, 1, 0) };
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 3));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 1, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 1, 1));
+
+ player->prefetch();
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(1, 1)};
+ ASSERT_EQ(expected_entries, entries);
+
+ ASSERT_TRUE(this->wait_for_complete(player));
+ ASSERT_EQ(0, this->m_replay_hander.complete_result);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchUnexpectedTag) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 120));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 235, 121));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 124));
+
+ player->prefetch();
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ journal::Entry entry;
+ uint64_t commit_tid;
+ ASSERT_FALSE(player->try_pop_front(&entry, &commit_tid));
+ ASSERT_TRUE(this->wait_for_complete(player));
+ ASSERT_EQ(0, this->m_replay_hander.complete_result);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchAndWatch) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions;
+ positions = {
+ cls::journal::ObjectPosition(0, 234, 122)};
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 122));
+
+ player->prefetch_and_watch(0.25);
+
+ Entries entries;
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 123));
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ Entries expected_entries;
+ expected_entries = {this->create_entry(234, 123)};
+ ASSERT_EQ(expected_entries, entries);
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 124));
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ expected_entries = {this->create_entry(234, 124)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefetchSkippedObject) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid, 14, 3));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+ ASSERT_EQ(0, metadata->set_active_set(2));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 234, 122));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 234, 123));
+ ASSERT_EQ(0, this->write_entry(oid, 5, 234, 124));
+ ASSERT_EQ(0, this->write_entry(oid, 6, 234, 125));
+ ASSERT_EQ(0, this->write_entry(oid, 7, 234, 126));
+
+ player->prefetch();
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 5, &entries));
+ ASSERT_TRUE(this->wait_for_complete(player));
+
+ Entries expected_entries;
+ expected_entries = {
+ this->create_entry(234, 122),
+ this->create_entry(234, 123),
+ this->create_entry(234, 124),
+ this->create_entry(234, 125),
+ this->create_entry(234, 126)};
+ ASSERT_EQ(expected_entries, entries);
+
+ uint64_t last_tid;
+ ASSERT_TRUE(metadata->get_last_allocated_entry_tid(234, &last_tid));
+ ASSERT_EQ(126U, last_tid);
+}
+
+TYPED_TEST(TestJournalPlayer, ImbalancedJournal) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions = {
+ cls::journal::ObjectPosition(9, 300, 1),
+ cls::journal::ObjectPosition(8, 300, 0),
+ cls::journal::ObjectPosition(10, 200, 4334),
+ cls::journal::ObjectPosition(11, 200, 4331) };
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid, 14, 4));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+ ASSERT_EQ(0, metadata->set_active_set(2));
+ metadata->set_minimum_set(2);
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 8, 300, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 8, 301, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 9, 300, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 9, 301, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 10, 200, 4334));
+ ASSERT_EQ(0, this->write_entry(oid, 10, 301, 2));
+ ASSERT_EQ(0, this->write_entry(oid, 11, 200, 4331));
+ ASSERT_EQ(0, this->write_entry(oid, 11, 301, 3));
+
+ player->prefetch();
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 4, &entries));
+ ASSERT_TRUE(this->wait_for_complete(player));
+
+ Entries expected_entries;
+ expected_entries = {
+ this->create_entry(301, 0),
+ this->create_entry(301, 1),
+ this->create_entry(301, 2),
+ this->create_entry(301, 3)};
+ ASSERT_EQ(expected_entries, entries);
+
+ uint64_t last_tid;
+ ASSERT_TRUE(metadata->get_last_allocated_entry_tid(301, &last_tid));
+ ASSERT_EQ(3U, last_tid);
+}
+
+TYPED_TEST(TestJournalPlayer, LiveReplayLaggyAppend) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 2));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 4));
+ ASSERT_EQ(0, this->write_entry(oid, 3, 0, 5)); // laggy entry 0/3 in object 1
+ player->prefetch_and_watch(0.25);
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(0, 0),
+ this->create_entry(0, 1),
+ this->create_entry(0, 2)};
+ ASSERT_EQ(expected_entries, entries);
+
+ journal::Entry entry;
+ uint64_t commit_tid;
+ ASSERT_FALSE(player->try_pop_front(&entry, &commit_tid));
+
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 3));
+ ASSERT_EQ(0, metadata->set_active_set(1));
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+
+ expected_entries = {
+ this->create_entry(0, 3),
+ this->create_entry(0, 4),
+ this->create_entry(0, 5)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, LiveReplayMissingSequence) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid, 14, 4));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, 2, 852));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 2, 856));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 2, 860));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 2, 853));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 2, 857));
+ ASSERT_EQ(0, this->write_entry(oid, 2, 2, 854));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 2, 856));
+ player->prefetch_and_watch(0.25);
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(2, 852),
+ this->create_entry(2, 853),
+ this->create_entry(2, 854)};
+ ASSERT_EQ(expected_entries, entries);
+
+ journal::Entry entry;
+ uint64_t commit_tid;
+ ASSERT_FALSE(player->try_pop_front(&entry, &commit_tid));
+
+ ASSERT_EQ(0, this->write_entry(oid, 3, 3, 3));
+ ASSERT_EQ(0, this->write_entry(oid, 2, 3, 2));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 3, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 3, 0));
+ ASSERT_TRUE(this->wait_for_entries(player, 4, &entries));
+
+ expected_entries = {
+ this->create_entry(3, 0),
+ this->create_entry(3, 1),
+ this->create_entry(3, 2),
+ this->create_entry(3, 3)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, LiveReplayLargeMissingSequence) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, metadata->set_active_set(2));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 3, 0, 3));
+ ASSERT_EQ(0, this->write_entry(oid, 4, 1, 0));
+ player->prefetch_and_watch(0.25);
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(0, 0),
+ this->create_entry(0, 1),
+ this->create_entry(1, 0)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, LiveReplayBlockedNewTag) {
+ std::string oid = this->get_temp_oid();
+
+ cls::journal::ObjectSetPosition commit_position;
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ C_SaferCond ctx1;
+ cls::journal::Tag tag1;
+ metadata->allocate_tag(cls::journal::Tag::TAG_CLASS_NEW, {}, &tag1, &ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ ASSERT_EQ(0, metadata->set_active_set(0));
+ ASSERT_EQ(0, this->write_entry(oid, 0, tag1.tid, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, tag1.tid, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 0, tag1.tid, 2));
+ ASSERT_EQ(0, this->write_entry(oid, 0, tag1.tid, 4));
+ player->prefetch_and_watch(0.25);
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 3, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(tag1.tid, 0),
+ this->create_entry(tag1.tid, 1),
+ this->create_entry(tag1.tid, 2)};
+ ASSERT_EQ(expected_entries, entries);
+
+ journal::Entry entry;
+ uint64_t commit_tid;
+ ASSERT_FALSE(player->try_pop_front(&entry, &commit_tid));
+
+ C_SaferCond ctx2;
+ cls::journal::Tag tag2;
+ metadata->allocate_tag(tag1.tag_class, {}, &tag2, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ ASSERT_EQ(0, this->write_entry(oid, 0, tag2.tid, 0));
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ expected_entries = {
+ this->create_entry(tag2.tid, 0)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, LiveReplayStaleEntries) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions = {
+ cls::journal::ObjectPosition(0, 1, 0) };
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 3));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 1, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 1, 1));
+ player->prefetch_and_watch(0.25);
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(1, 1)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, LiveReplayRefetchRemoveEmpty) {
+ std::string oid = this->get_temp_oid();
+
+ journal::JournalPlayer::ObjectPositions positions = {
+ cls::journal::ObjectPosition(1, 0, 1),
+ cls::journal::ObjectPosition(0, 0, 0)};
+ cls::journal::ObjectSetPosition commit_position(positions);
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, commit_position));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+
+ ASSERT_EQ(0, metadata->set_active_set(1));
+ ASSERT_EQ(0, this->write_entry(oid, 0, 0, 0));
+ ASSERT_EQ(0, this->write_entry(oid, 1, 0, 1));
+ ASSERT_EQ(0, this->write_entry(oid, 3, 0, 3));
+ ASSERT_EQ(0, this->write_entry(oid, 2, 1, 0));
+ player->prefetch_and_watch(0.25);
+
+ Entries entries;
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ Entries expected_entries = {
+ this->create_entry(1, 0)};
+ ASSERT_EQ(expected_entries, entries);
+
+ // should remove player for offset 3 after refetching
+ ASSERT_EQ(0, metadata->set_active_set(3));
+ ASSERT_EQ(0, this->write_entry(oid, 7, 1, 1));
+
+ ASSERT_TRUE(this->wait_for_entries(player, 1, &entries));
+
+ expected_entries = {
+ this->create_entry(1, 1)};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestJournalPlayer, PrefechShutDown) {
+ std::string oid = this->get_temp_oid();
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, {}));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+ player->prefetch();
+}
+
+TYPED_TEST(TestJournalPlayer, LiveReplayShutDown) {
+ std::string oid = this->get_temp_oid();
+
+ ASSERT_EQ(0, this->create(oid));
+ ASSERT_EQ(0, this->client_register(oid));
+ ASSERT_EQ(0, this->client_commit(oid, {}));
+
+ auto metadata = this->create_metadata(oid);
+ ASSERT_EQ(0, this->init_metadata(metadata));
+
+ journal::JournalPlayer *player = this->create_player(oid, metadata);
+ BOOST_SCOPE_EXIT_ALL( (player) ) {
+ C_SaferCond unwatch_ctx;
+ player->shut_down(&unwatch_ctx);
+ ASSERT_EQ(0, unwatch_ctx.wait());
+ };
+ player->prefetch_and_watch(0.25);
+}
+
diff --git a/src/test/journal/test_JournalRecorder.cc b/src/test/journal/test_JournalRecorder.cc
new file mode 100644
index 000000000..466ee2741
--- /dev/null
+++ b/src/test/journal/test_JournalRecorder.cc
@@ -0,0 +1,174 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/JournalRecorder.h"
+#include "journal/Entry.h"
+#include "journal/JournalMetadata.h"
+#include "test/journal/RadosTestFixture.h"
+#include <limits>
+#include <list>
+#include <memory>
+
+class TestJournalRecorder : public RadosTestFixture {
+public:
+ using JournalRecorderPtr = std::unique_ptr<journal::JournalRecorder,
+ std::function<void(journal::JournalRecorder*)>>;
+ JournalRecorderPtr create_recorder(
+ const std::string &oid, const ceph::ref_t<journal::JournalMetadata>& metadata) {
+ JournalRecorderPtr recorder{
+ new journal::JournalRecorder(m_ioctx, oid + ".", metadata, 0),
+ [](journal::JournalRecorder* recorder) {
+ C_SaferCond cond;
+ recorder->shut_down(&cond);
+ cond.wait();
+ delete recorder;
+ }
+ };
+ recorder->set_append_batch_options(0, std::numeric_limits<uint32_t>::max(), 0);
+ return recorder;
+ }
+};
+
+TEST_F(TestJournalRecorder, Append) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ JournalRecorderPtr recorder = create_recorder(oid, metadata);
+
+ journal::Future future1 = recorder->append(123, create_payload("payload"));
+
+ C_SaferCond cond;
+ future1.flush(&cond);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestJournalRecorder, AppendKnownOverflow) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_EQ(0U, metadata->get_active_set());
+
+ JournalRecorderPtr recorder = create_recorder(oid, metadata);
+
+ recorder->append(123, create_payload(std::string(metadata->get_object_size() -
+ journal::Entry::get_fixed_size(), '1')));
+ journal::Future future2 = recorder->append(123, create_payload(std::string(1, '2')));
+
+ C_SaferCond cond;
+ future2.flush(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ ASSERT_EQ(1U, metadata->get_active_set());
+}
+
+TEST_F(TestJournalRecorder, AppendDelayedOverflow) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_EQ(0U, metadata->get_active_set());
+
+ JournalRecorderPtr recorder1 = create_recorder(oid, metadata);
+ JournalRecorderPtr recorder2 = create_recorder(oid, metadata);
+
+ recorder1->append(234, create_payload(std::string(1, '1')));
+ recorder2->append(123, create_payload(std::string(metadata->get_object_size() -
+ journal::Entry::get_fixed_size(), '2')));
+
+ journal::Future future = recorder2->append(123, create_payload(std::string(1, '3')));
+
+ C_SaferCond cond;
+ future.flush(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ ASSERT_EQ(1U, metadata->get_active_set());
+}
+
+TEST_F(TestJournalRecorder, FutureFlush) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ JournalRecorderPtr recorder = create_recorder(oid, metadata);
+
+ journal::Future future1 = recorder->append(123, create_payload("payload1"));
+ journal::Future future2 = recorder->append(123, create_payload("payload2"));
+
+ C_SaferCond cond;
+ future2.flush(&cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_TRUE(future1.is_complete());
+ ASSERT_TRUE(future2.is_complete());
+}
+
+TEST_F(TestJournalRecorder, Flush) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ JournalRecorderPtr recorder = create_recorder(oid, metadata);
+
+ journal::Future future1 = recorder->append(123, create_payload("payload1"));
+ journal::Future future2 = recorder->append(123, create_payload("payload2"));
+
+ C_SaferCond cond1;
+ recorder->flush(&cond1);
+ ASSERT_EQ(0, cond1.wait());
+
+ C_SaferCond cond2;
+ future2.wait(&cond2);
+ ASSERT_EQ(0, cond2.wait());
+ ASSERT_TRUE(future1.is_complete());
+ ASSERT_TRUE(future2.is_complete());
+}
+
+TEST_F(TestJournalRecorder, OverflowCommitObjectNumber) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_EQ(0U, metadata->get_active_set());
+
+ JournalRecorderPtr recorder = create_recorder(oid, metadata);
+
+ recorder->append(123, create_payload(std::string(metadata->get_object_size() -
+ journal::Entry::get_fixed_size(), '1')));
+ journal::Future future2 = recorder->append(124, create_payload(std::string(1, '2')));
+
+ C_SaferCond cond;
+ future2.flush(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ ASSERT_EQ(1U, metadata->get_active_set());
+
+ uint64_t object_num;
+ uint64_t tag_tid;
+ uint64_t entry_tid;
+ metadata->get_commit_entry(1, &object_num, &tag_tid, &entry_tid);
+ ASSERT_EQ(0U, object_num);
+ ASSERT_EQ(123U, tag_tid);
+ ASSERT_EQ(0U, entry_tid);
+
+ metadata->get_commit_entry(2, &object_num, &tag_tid, &entry_tid);
+ ASSERT_EQ(2U, object_num);
+ ASSERT_EQ(124U, tag_tid);
+ ASSERT_EQ(0U, entry_tid);
+}
+
diff --git a/src/test/journal/test_JournalTrimmer.cc b/src/test/journal/test_JournalTrimmer.cc
new file mode 100644
index 000000000..aaf10979f
--- /dev/null
+++ b/src/test/journal/test_JournalTrimmer.cc
@@ -0,0 +1,197 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/JournalTrimmer.h"
+#include "journal/JournalMetadata.h"
+#include "include/stringify.h"
+#include "test/journal/RadosTestFixture.h"
+#include <limits>
+#include <list>
+
+class TestJournalTrimmer : public RadosTestFixture {
+public:
+
+ void TearDown() override {
+ for (MetadataList::iterator it = m_metadata_list.begin();
+ it != m_metadata_list.end(); ++it) {
+ (*it)->remove_listener(&m_listener);
+ }
+ m_metadata_list.clear();
+
+ for (std::list<journal::JournalTrimmer*>::iterator it = m_trimmers.begin();
+ it != m_trimmers.end(); ++it) {
+ C_SaferCond ctx;
+ (*it)->shut_down(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ delete *it;
+ }
+ RadosTestFixture::TearDown();
+ }
+
+ int append_payload(const ceph::ref_t<journal::JournalMetadata>& metadata,
+ const std::string &oid, uint64_t object_num,
+ const std::string &payload, uint64_t *commit_tid) {
+ int r = append(oid + "." + stringify(object_num), create_payload(payload));
+ uint64_t tid = metadata->allocate_commit_tid(object_num, 234, 123);
+ if (commit_tid != NULL) {
+ *commit_tid = tid;
+ }
+ return r;
+ }
+
+ auto create_metadata(const std::string &oid) {
+ auto metadata = RadosTestFixture::create_metadata(oid);
+ m_metadata_list.push_back(metadata);
+ metadata->add_listener(&m_listener);
+ return metadata;
+ }
+
+ journal::JournalTrimmer *create_trimmer(const std::string &oid,
+ const ceph::ref_t<journal::JournalMetadata>& metadata) {
+ journal::JournalTrimmer *trimmer(new journal::JournalTrimmer(
+ m_ioctx, oid + ".", metadata));
+ m_trimmers.push_back(trimmer);
+ return trimmer;
+ }
+
+ int assert_exists(const std::string &oid) {
+ librados::ObjectWriteOperation op;
+ op.assert_exists();
+ return m_ioctx.operate(oid, &op);
+ }
+
+ typedef std::list<ceph::ref_t<journal::JournalMetadata>> MetadataList;
+ MetadataList m_metadata_list;
+ std::list<journal::JournalTrimmer*> m_trimmers;
+};
+
+TEST_F(TestJournalTrimmer, Committed) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ ASSERT_EQ(0, metadata->set_active_set(10));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ uint64_t commit_tid1;
+ uint64_t commit_tid2;
+ uint64_t commit_tid3;
+ uint64_t commit_tid4;
+ uint64_t commit_tid5;
+ uint64_t commit_tid6;
+ ASSERT_EQ(0, append_payload(metadata, oid, 0, "payload", &commit_tid1));
+ ASSERT_EQ(0, append_payload(metadata, oid, 4, "payload", &commit_tid2));
+ ASSERT_EQ(0, append_payload(metadata, oid, 5, "payload", &commit_tid3));
+ ASSERT_EQ(0, append_payload(metadata, oid, 0, "payload", &commit_tid4));
+ ASSERT_EQ(0, append_payload(metadata, oid, 4, "payload", &commit_tid5));
+ ASSERT_EQ(0, append_payload(metadata, oid, 5, "payload", &commit_tid6));
+
+ journal::JournalTrimmer *trimmer = create_trimmer(oid, metadata);
+
+ trimmer->committed(commit_tid4);
+ trimmer->committed(commit_tid6);
+ trimmer->committed(commit_tid2);
+ trimmer->committed(commit_tid5);
+ trimmer->committed(commit_tid3);
+ trimmer->committed(commit_tid1);
+ while (metadata->get_minimum_set() != 2U) {
+ ASSERT_TRUE(wait_for_update(metadata));
+ }
+
+ ASSERT_EQ(-ENOENT, assert_exists(oid + ".0"));
+ ASSERT_EQ(-ENOENT, assert_exists(oid + ".2"));
+ ASSERT_EQ(0, assert_exists(oid + ".5"));
+}
+
+TEST_F(TestJournalTrimmer, CommittedWithOtherClient) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+ ASSERT_EQ(0, client_register(oid, "client2", "slow client"));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ ASSERT_EQ(0, metadata->set_active_set(10));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ uint64_t commit_tid1;
+ uint64_t commit_tid2;
+ uint64_t commit_tid3;
+ uint64_t commit_tid4;
+ ASSERT_EQ(0, append_payload(metadata, oid, 0, "payload", &commit_tid1));
+ ASSERT_EQ(0, append_payload(metadata, oid, 2, "payload", &commit_tid2));
+ ASSERT_EQ(0, append_payload(metadata, oid, 3, "payload", &commit_tid3));
+ ASSERT_EQ(0, append_payload(metadata, oid, 5, "payload", &commit_tid4));
+
+ journal::JournalTrimmer *trimmer = create_trimmer(oid, metadata);
+
+ trimmer->committed(commit_tid1);
+ trimmer->committed(commit_tid2);
+ trimmer->committed(commit_tid3);
+ trimmer->committed(commit_tid4);
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ ASSERT_EQ(0, assert_exists(oid + ".0"));
+ ASSERT_EQ(0, assert_exists(oid + ".2"));
+ ASSERT_EQ(0, assert_exists(oid + ".3"));
+ ASSERT_EQ(0, assert_exists(oid + ".5"));
+}
+
+TEST_F(TestJournalTrimmer, RemoveObjects) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ ASSERT_EQ(0, metadata->set_active_set(10));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ ASSERT_EQ(0, append(oid + ".0", create_payload("payload")));
+ ASSERT_EQ(0, append(oid + ".2", create_payload("payload")));
+ ASSERT_EQ(0, append(oid + ".3", create_payload("payload")));
+ ASSERT_EQ(0, append(oid + ".5", create_payload("payload")));
+
+ journal::JournalTrimmer *trimmer = create_trimmer(oid, metadata);
+
+ C_SaferCond cond;
+ trimmer->remove_objects(false, &cond);
+ ASSERT_EQ(0, cond.wait());
+
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ ASSERT_EQ(-ENOENT, assert_exists(oid + ".0"));
+ ASSERT_EQ(-ENOENT, assert_exists(oid + ".2"));
+ ASSERT_EQ(-ENOENT, assert_exists(oid + ".3"));
+ ASSERT_EQ(-ENOENT, assert_exists(oid + ".5"));
+}
+
+TEST_F(TestJournalTrimmer, RemoveObjectsWithOtherClient) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid, 12, 2));
+ ASSERT_EQ(0, client_register(oid));
+ ASSERT_EQ(0, client_register(oid, "client2", "other client"));
+
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+ ASSERT_TRUE(wait_for_update(metadata));
+
+ journal::JournalTrimmer *trimmer = create_trimmer(oid, metadata);
+
+ C_SaferCond ctx1;
+ trimmer->remove_objects(false, &ctx1);
+ ASSERT_EQ(-EBUSY, ctx1.wait());
+
+ C_SaferCond ctx2;
+ trimmer->remove_objects(true, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
diff --git a/src/test/journal/test_Journaler.cc b/src/test/journal/test_Journaler.cc
new file mode 100644
index 000000000..836816581
--- /dev/null
+++ b/src/test/journal/test_Journaler.cc
@@ -0,0 +1,198 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/stringify.h"
+
+#include "journal/Journaler.h"
+#include "journal/Settings.h"
+
+#include "test/librados/test.h"
+#include "test/journal/RadosTestFixture.h"
+
+#include "gtest/gtest.h"
+
+// reinclude our assert to clobber the system one
+#include "include/ceph_assert.h"
+
+class TestJournaler : public RadosTestFixture {
+public:
+
+ static const std::string CLIENT_ID;
+
+ static std::string get_temp_journal_id() {
+ return stringify(++_journal_id);
+ }
+
+ void SetUp() override {
+ RadosTestFixture::SetUp();
+ m_journal_id = get_temp_journal_id();
+ m_journaler = new journal::Journaler(m_work_queue, m_timer, &m_timer_lock,
+ m_ioctx, m_journal_id, CLIENT_ID, {},
+ nullptr);
+ }
+
+ void TearDown() override {
+ delete m_journaler;
+ RadosTestFixture::TearDown();
+ }
+
+ int create_journal(uint8_t order, uint8_t splay_width) {
+ C_SaferCond cond;
+ m_journaler->create(order, splay_width, -1, &cond);
+ return cond.wait();
+ }
+
+ int init_journaler() {
+ C_SaferCond cond;
+ m_journaler->init(&cond);
+ return cond.wait();
+ }
+
+ int shut_down_journaler() {
+ C_SaferCond ctx;
+ m_journaler->shut_down(&ctx);
+ return ctx.wait();
+ }
+
+ int register_client(const std::string &client_id, const std::string &desc) {
+ journal::Journaler journaler(m_work_queue, m_timer, &m_timer_lock, m_ioctx,
+ m_journal_id, client_id, {}, nullptr);
+ bufferlist data;
+ data.append(desc);
+ C_SaferCond cond;
+ journaler.register_client(data, &cond);
+ return cond.wait();
+ }
+
+ int update_client(const std::string &client_id, const std::string &desc) {
+ journal::Journaler journaler(m_work_queue, m_timer, &m_timer_lock, m_ioctx,
+ m_journal_id, client_id, {}, nullptr);
+ bufferlist data;
+ data.append(desc);
+ C_SaferCond cond;
+ journaler.update_client(data, &cond);
+ return cond.wait();
+ }
+
+ int unregister_client(const std::string &client_id) {
+ journal::Journaler journaler(m_work_queue, m_timer, &m_timer_lock, m_ioctx,
+ m_journal_id, client_id, {}, nullptr);
+ C_SaferCond cond;
+ journaler.unregister_client(&cond);
+ return cond.wait();
+ }
+
+ static uint64_t _journal_id;
+
+ std::string m_journal_id;
+ journal::Journaler *m_journaler;
+};
+
+const std::string TestJournaler::CLIENT_ID = "client1";
+uint64_t TestJournaler::_journal_id = 0;
+
+TEST_F(TestJournaler, Create) {
+ ASSERT_EQ(0, create_journal(12, 8));
+}
+
+TEST_F(TestJournaler, CreateDuplicate) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(-EEXIST, create_journal(12, 8));
+}
+
+TEST_F(TestJournaler, CreateInvalidParams) {
+ ASSERT_EQ(-EDOM, create_journal(1, 8));
+ ASSERT_EQ(-EDOM, create_journal(123, 8));
+ ASSERT_EQ(-EINVAL, create_journal(12, 0));
+}
+
+TEST_F(TestJournaler, Init) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(0, register_client(CLIENT_ID, "foo"));
+ ASSERT_EQ(0, init_journaler());
+ ASSERT_EQ(0, shut_down_journaler());
+}
+
+TEST_F(TestJournaler, InitDNE) {
+ ASSERT_EQ(-ENOENT, init_journaler());
+ ASSERT_EQ(0, shut_down_journaler());
+}
+
+TEST_F(TestJournaler, RegisterClientDuplicate) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(0, register_client(CLIENT_ID, "foo"));
+ ASSERT_EQ(-EEXIST, register_client(CLIENT_ID, "foo2"));
+}
+
+TEST_F(TestJournaler, UpdateClient) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(0, register_client(CLIENT_ID, "foo"));
+ ASSERT_EQ(0, update_client(CLIENT_ID, "foo2"));
+}
+
+TEST_F(TestJournaler, UpdateClientDNE) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(-ENOENT, update_client(CLIENT_ID, "foo"));
+}
+
+TEST_F(TestJournaler, UnregisterClient) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(0, register_client(CLIENT_ID, "foo"));
+ ASSERT_EQ(0, unregister_client(CLIENT_ID));
+ // Test it does not exist and can be registered again
+ ASSERT_EQ(-ENOENT, update_client(CLIENT_ID, "foo"));
+ ASSERT_EQ(0, register_client(CLIENT_ID, "foo"));
+}
+
+TEST_F(TestJournaler, UnregisterClientDNE) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(-ENOENT, unregister_client(CLIENT_ID));
+}
+
+TEST_F(TestJournaler, AllocateTag) {
+ ASSERT_EQ(0, create_journal(12, 8));
+
+ cls::journal::Tag tag;
+
+ bufferlist data;
+ data.append(std::string(128, '1'));
+
+ // allocate a new tag class
+ C_SaferCond ctx1;
+ m_journaler->allocate_tag(data, &tag, &ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+ ASSERT_EQ(cls::journal::Tag(0, 0, data), tag);
+
+ // re-use an existing tag class
+ C_SaferCond ctx2;
+ m_journaler->allocate_tag(tag.tag_class, bufferlist(), &tag, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(cls::journal::Tag(1, 0, bufferlist()), tag);
+}
+
+TEST_F(TestJournaler, GetTags) {
+ ASSERT_EQ(0, create_journal(12, 8));
+ ASSERT_EQ(0, register_client(CLIENT_ID, "foo"));
+
+ std::list<cls::journal::Tag> expected_tags;
+ for (size_t i = 0; i < 256; ++i) {
+ C_SaferCond ctx;
+ cls::journal::Tag tag;
+ if (i < 2) {
+ m_journaler->allocate_tag(bufferlist(), &tag, &ctx);
+ } else {
+ m_journaler->allocate_tag(i % 2, bufferlist(), &tag, &ctx);
+ }
+ ASSERT_EQ(0, ctx.wait());
+
+ if (i % 2 == 0) {
+ expected_tags.push_back(tag);
+ }
+ }
+
+ std::list<cls::journal::Tag> tags;
+ C_SaferCond ctx;
+ m_journaler->get_tags(0, &tags, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(expected_tags, tags);
+}
diff --git a/src/test/journal/test_ObjectPlayer.cc b/src/test/journal/test_ObjectPlayer.cc
new file mode 100644
index 000000000..5ac3d8b12
--- /dev/null
+++ b/src/test/journal/test_ObjectPlayer.cc
@@ -0,0 +1,281 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/ObjectPlayer.h"
+#include "journal/Entry.h"
+#include "include/stringify.h"
+#include "common/Timer.h"
+#include "gtest/gtest.h"
+#include "test/librados/test.h"
+#include "test/journal/RadosTestFixture.h"
+
+template <typename T>
+class TestObjectPlayer : public RadosTestFixture, public T {
+public:
+ auto create_object(const std::string &oid, uint8_t order) {
+ auto object = ceph::make_ref<journal::ObjectPlayer>(
+ m_ioctx, oid + ".", 0, *m_timer, m_timer_lock, order,
+ T::max_fetch_bytes);
+ return object;
+ }
+
+ int fetch(const ceph::ref_t<journal::ObjectPlayer>& object_player) {
+ while (true) {
+ C_SaferCond ctx;
+ object_player->set_refetch_state(
+ journal::ObjectPlayer::REFETCH_STATE_NONE);
+ object_player->fetch(&ctx);
+ int r = ctx.wait();
+ if (r < 0 || !object_player->refetch_required()) {
+ return r;
+ }
+ }
+ return 0;
+ }
+
+ int watch_and_wait_for_entries(const ceph::ref_t<journal::ObjectPlayer>& object_player,
+ journal::ObjectPlayer::Entries *entries,
+ size_t count) {
+ for (size_t i = 0; i < 50; ++i) {
+ object_player->get_entries(entries);
+ if (entries->size() == count) {
+ break;
+ }
+
+ C_SaferCond ctx;
+ object_player->watch(&ctx, 0.1);
+
+ int r = ctx.wait();
+ if (r < 0) {
+ return r;
+ }
+ }
+ return 0;
+ }
+
+ std::string get_object_name(const std::string &oid) {
+ return oid + ".0";
+ }
+};
+
+template <uint32_t _max_fetch_bytes>
+struct TestObjectPlayerParams {
+ static inline const uint32_t max_fetch_bytes = _max_fetch_bytes;
+};
+
+typedef ::testing::Types<TestObjectPlayerParams<0>,
+ TestObjectPlayerParams<10> > TestObjectPlayerTypes;
+TYPED_TEST_SUITE(TestObjectPlayer, TestObjectPlayerTypes);
+
+TYPED_TEST(TestObjectPlayer, Fetch) {
+ std::string oid = this->get_temp_oid();
+
+ journal::Entry entry1(234, 123, this->create_payload(std::string(24, '1')));
+ journal::Entry entry2(234, 124, this->create_payload(std::string(24, '1')));
+
+ bufferlist bl;
+ encode(entry1, bl);
+ encode(entry2, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+
+ auto object = this->create_object(oid, 14);
+ ASSERT_LE(0, this->fetch(object));
+
+ journal::ObjectPlayer::Entries entries;
+ object->get_entries(&entries);
+ ASSERT_EQ(2U, entries.size());
+
+ journal::ObjectPlayer::Entries expected_entries = {entry1, entry2};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestObjectPlayer, FetchLarge) {
+ std::string oid = this->get_temp_oid();
+
+ journal::Entry entry1(234, 123,
+ this->create_payload(std::string(8192 - 32, '1')));
+ journal::Entry entry2(234, 124, this->create_payload(""));
+
+ bufferlist bl;
+ encode(entry1, bl);
+ encode(entry2, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+
+ auto object = this->create_object(oid, 12);
+ ASSERT_LE(0, this->fetch(object));
+
+ journal::ObjectPlayer::Entries entries;
+ object->get_entries(&entries);
+ ASSERT_EQ(2U, entries.size());
+
+ journal::ObjectPlayer::Entries expected_entries = {entry1, entry2};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestObjectPlayer, FetchDeDup) {
+ std::string oid = this->get_temp_oid();
+
+ journal::Entry entry1(234, 123, this->create_payload(std::string(24, '1')));
+ journal::Entry entry2(234, 123, this->create_payload(std::string(24, '2')));
+
+ bufferlist bl;
+ encode(entry1, bl);
+ encode(entry2, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+
+ auto object = this->create_object(oid, 14);
+ ASSERT_LE(0, this->fetch(object));
+
+ journal::ObjectPlayer::Entries entries;
+ object->get_entries(&entries);
+ ASSERT_EQ(1U, entries.size());
+
+ journal::ObjectPlayer::Entries expected_entries = {entry2};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestObjectPlayer, FetchEmpty) {
+ std::string oid = this->get_temp_oid();
+
+ bufferlist bl;
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+
+ auto object = this->create_object(oid, 14);
+
+ ASSERT_EQ(0, this->fetch(object));
+ ASSERT_TRUE(object->empty());
+}
+
+TYPED_TEST(TestObjectPlayer, FetchCorrupt) {
+ std::string oid = this->get_temp_oid();
+
+ journal::Entry entry1(234, 123, this->create_payload(std::string(24, '1')));
+ journal::Entry entry2(234, 124, this->create_payload(std::string(24, '2')));
+
+ bufferlist bl;
+ encode(entry1, bl);
+ encode(this->create_payload("corruption" + std::string(1024, 'X')), bl);
+ encode(entry2, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+
+ auto object = this->create_object(oid, 14);
+ ASSERT_EQ(-EBADMSG, this->fetch(object));
+
+ journal::ObjectPlayer::Entries entries;
+ object->get_entries(&entries);
+ ASSERT_EQ(1U, entries.size());
+
+ journal::ObjectPlayer::Entries expected_entries = {entry1};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestObjectPlayer, FetchAppend) {
+ std::string oid = this->get_temp_oid();
+
+ journal::Entry entry1(234, 123, this->create_payload(std::string(24, '1')));
+ journal::Entry entry2(234, 124, this->create_payload(std::string(24, '2')));
+
+ bufferlist bl;
+ encode(entry1, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+
+ auto object = this->create_object(oid, 14);
+ ASSERT_LE(0, this->fetch(object));
+
+ journal::ObjectPlayer::Entries entries;
+ object->get_entries(&entries);
+ ASSERT_EQ(1U, entries.size());
+
+ journal::ObjectPlayer::Entries expected_entries = {entry1};
+ ASSERT_EQ(expected_entries, entries);
+
+ bl.clear();
+ encode(entry2, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+ ASSERT_LE(0, this->fetch(object));
+
+ object->get_entries(&entries);
+ ASSERT_EQ(2U, entries.size());
+
+ expected_entries = {entry1, entry2};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestObjectPlayer, PopEntry) {
+ std::string oid = this->get_temp_oid();
+
+ journal::Entry entry1(234, 123, this->create_payload(std::string(24, '1')));
+ journal::Entry entry2(234, 124, this->create_payload(std::string(24, '1')));
+
+ bufferlist bl;
+ encode(entry1, bl);
+ encode(entry2, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+
+ auto object = this->create_object(oid, 14);
+ ASSERT_LE(0, this->fetch(object));
+
+ journal::ObjectPlayer::Entries entries;
+ object->get_entries(&entries);
+ ASSERT_EQ(2U, entries.size());
+
+ journal::Entry entry;
+ object->front(&entry);
+ object->pop_front();
+ ASSERT_EQ(entry1, entry);
+ object->front(&entry);
+ object->pop_front();
+ ASSERT_EQ(entry2, entry);
+ ASSERT_TRUE(object->empty());
+}
+
+TYPED_TEST(TestObjectPlayer, Watch) {
+ std::string oid = this->get_temp_oid();
+ auto object = this->create_object(oid, 14);
+
+ C_SaferCond cond1;
+ object->watch(&cond1, 0.1);
+
+ journal::Entry entry1(234, 123, this->create_payload(std::string(24, '1')));
+ journal::Entry entry2(234, 124, this->create_payload(std::string(24, '1')));
+
+ bufferlist bl;
+ encode(entry1, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+ ASSERT_LE(0, cond1.wait());
+
+ journal::ObjectPlayer::Entries entries;
+ ASSERT_EQ(0, this->watch_and_wait_for_entries(object, &entries, 1U));
+ ASSERT_EQ(1U, entries.size());
+
+ journal::ObjectPlayer::Entries expected_entries;
+ expected_entries = {entry1};
+ ASSERT_EQ(expected_entries, entries);
+
+ C_SaferCond cond2;
+ object->watch(&cond2, 0.1);
+
+ bl.clear();
+ encode(entry2, bl);
+ ASSERT_EQ(0, this->append(this->get_object_name(oid), bl));
+ ASSERT_LE(0, cond2.wait());
+
+ ASSERT_EQ(0, this->watch_and_wait_for_entries(object, &entries, 2U));
+ ASSERT_EQ(2U, entries.size());
+
+ expected_entries = {entry1, entry2};
+ ASSERT_EQ(expected_entries, entries);
+}
+
+TYPED_TEST(TestObjectPlayer, Unwatch) {
+ std::string oid = this->get_temp_oid();
+ auto object = this->create_object(oid, 14);
+
+ C_SaferCond watch_ctx;
+ object->watch(&watch_ctx, 600);
+
+ usleep(200000);
+
+ object->unwatch();
+ ASSERT_EQ(-ECANCELED, watch_ctx.wait());
+}
diff --git a/src/test/journal/test_ObjectRecorder.cc b/src/test/journal/test_ObjectRecorder.cc
new file mode 100644
index 000000000..e4ab8a141
--- /dev/null
+++ b/src/test/journal/test_ObjectRecorder.cc
@@ -0,0 +1,464 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "journal/ObjectRecorder.h"
+#include "common/Cond.h"
+#include "common/ceph_mutex.h"
+#include "common/Timer.h"
+#include "gtest/gtest.h"
+#include "test/librados/test.h"
+#include "test/journal/RadosTestFixture.h"
+#include <limits>
+
+using namespace std::chrono_literals;
+using std::shared_ptr;
+
+class TestObjectRecorder : public RadosTestFixture {
+public:
+ TestObjectRecorder() = default;
+
+ struct Handler : public journal::ObjectRecorder::Handler {
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::mutex* object_lock = nullptr;
+ ceph::condition_variable cond;
+ bool is_closed = false;
+ uint32_t overflows = 0;
+
+ Handler() = default;
+
+ void closed(journal::ObjectRecorder *object_recorder) override {
+ std::lock_guard locker{lock};
+ is_closed = true;
+ cond.notify_all();
+ }
+ void overflow(journal::ObjectRecorder *object_recorder) override {
+ std::lock_guard locker{lock};
+ journal::AppendBuffers append_buffers;
+ object_lock->lock();
+ object_recorder->claim_append_buffers(&append_buffers);
+ object_lock->unlock();
+
+ ++overflows;
+ cond.notify_all();
+ }
+ };
+
+ // flush the pending buffers in dtor
+ class ObjectRecorderFlusher {
+ public:
+ ObjectRecorderFlusher(librados::IoCtx& ioctx,
+ ContextWQ* work_queue)
+ : m_ioctx{ioctx},
+ m_work_queue{work_queue}
+ {}
+ ObjectRecorderFlusher(librados::IoCtx& ioctx,
+ ContextWQ* work_queue,
+ uint32_t flush_interval,
+ uint16_t flush_bytes,
+ double flush_age,
+ int max_in_flight)
+ : m_ioctx{ioctx},
+ m_work_queue{work_queue},
+ m_flush_interval{flush_interval},
+ m_flush_bytes{flush_bytes},
+ m_flush_age{flush_age},
+ m_max_in_flight_appends{max_in_flight < 0 ?
+ std::numeric_limits<uint64_t>::max() :
+ static_cast<uint64_t>(max_in_flight)}
+ {}
+ ~ObjectRecorderFlusher() {
+ for (auto& [object_recorder, m] : m_object_recorders) {
+ C_SaferCond cond;
+ object_recorder->flush(&cond);
+ cond.wait();
+ std::scoped_lock l{*m};
+ if (!object_recorder->is_closed()) {
+ object_recorder->close();
+ }
+ }
+ }
+ auto create_object(std::string_view oid, uint8_t order, ceph::mutex* lock) {
+ auto object = ceph::make_ref<journal::ObjectRecorder>(
+ m_ioctx, oid, 0, lock, m_work_queue, &m_handler,
+ order, m_max_in_flight_appends);
+ {
+ std::lock_guard locker{*lock};
+ object->set_append_batch_options(m_flush_interval,
+ m_flush_bytes,
+ m_flush_age);
+ }
+ m_object_recorders.emplace_back(object, lock);
+ m_handler.object_lock = lock;
+ return object;
+ }
+ bool wait_for_closed() {
+ std::unique_lock locker{m_handler.lock};
+ return m_handler.cond.wait_for(locker, 10s,
+ [this] { return m_handler.is_closed; });
+ }
+ bool wait_for_overflow() {
+ std::unique_lock locker{m_handler.lock};
+ if (m_handler.cond.wait_for(locker, 10s,
+ [this] { return m_handler.overflows > 0; })) {
+ m_handler.overflows = 0;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ private:
+ librados::IoCtx& m_ioctx;
+ ContextWQ *m_work_queue;
+ uint32_t m_flush_interval = std::numeric_limits<uint32_t>::max();
+ uint64_t m_flush_bytes = std::numeric_limits<uint64_t>::max();
+ double m_flush_age = 600;
+ uint64_t m_max_in_flight_appends = 0;
+ using ObjectRecorders =
+ std::list<std::pair<ceph::ref_t<journal::ObjectRecorder>, ceph::mutex*>>;
+ ObjectRecorders m_object_recorders;
+ Handler m_handler;
+ };
+
+ journal::AppendBuffer create_append_buffer(uint64_t tag_tid,
+ uint64_t entry_tid,
+ const std::string &payload) {
+ auto future = ceph::make_ref<journal::FutureImpl>(tag_tid, entry_tid, 456);
+ future->init(ceph::ref_t<journal::FutureImpl>());
+
+ bufferlist bl;
+ bl.append(payload);
+ return std::make_pair(future, bl);
+ }
+};
+
+TEST_F(TestObjectRecorder, Append) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 0, 0, 0, 0);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ "payload");
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(0U, object->get_pending_appends());
+
+ journal::AppendBuffer append_buffer2 = create_append_buffer(234, 124,
+ "payload");
+ append_buffers = {append_buffer2};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(0U, object->get_pending_appends());
+
+ C_SaferCond cond;
+ append_buffer2.first->flush(&cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(0U, object->get_pending_appends());
+}
+
+TEST_F(TestObjectRecorder, AppendFlushByCount) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 2, 0, 0, -1);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ "payload");
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(1U, object->get_pending_appends());
+
+ journal::AppendBuffer append_buffer2 = create_append_buffer(234, 124,
+ "payload");
+ append_buffers = {append_buffer2};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(0U, object->get_pending_appends());
+
+ C_SaferCond cond;
+ append_buffer2.first->wait(&cond);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestObjectRecorder, AppendFlushByBytes) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 0, 10, 0, -1);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ "payload");
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(1U, object->get_pending_appends());
+
+ journal::AppendBuffer append_buffer2 = create_append_buffer(234, 124,
+ "payload");
+ append_buffers = {append_buffer2};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(0U, object->get_pending_appends());
+
+ C_SaferCond cond;
+ append_buffer2.first->wait(&cond);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestObjectRecorder, AppendFlushByAge) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 0, 0, 0.0005, -1);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ "payload");
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+
+ uint32_t offset = 0;
+ journal::AppendBuffer append_buffer2;
+ while (!append_buffer1.first->is_flush_in_progress() &&
+ !append_buffer1.first->is_complete()) {
+ usleep(1000);
+
+ append_buffer2 = create_append_buffer(234, 124 + offset, "payload");
+ ++offset;
+ append_buffers = {append_buffer2};
+
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ }
+
+ C_SaferCond cond;
+ append_buffer2.first->wait(&cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(0U, object->get_pending_appends());
+}
+
+TEST_F(TestObjectRecorder, AppendFilledObject) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 0, 0, 0.0, -1);
+ auto object = flusher.create_object(oid, 12, &lock);
+
+ std::string payload(2048, '1');
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ payload);
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+
+ journal::AppendBuffer append_buffer2 = create_append_buffer(234, 124,
+ payload);
+ append_buffers = {append_buffer2};
+ lock.lock();
+ ASSERT_TRUE(object->append(std::move(append_buffers)));
+ lock.unlock();
+
+ C_SaferCond cond;
+ append_buffer2.first->wait(&cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(0U, object->get_pending_appends());
+}
+
+TEST_F(TestObjectRecorder, Flush) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 0, 10, 0, -1);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ "payload");
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(1U, object->get_pending_appends());
+
+ C_SaferCond cond1;
+ object->flush(&cond1);
+ ASSERT_EQ(0, cond1.wait());
+
+ C_SaferCond cond2;
+ append_buffer1.first->wait(&cond2);
+ ASSERT_EQ(0, cond2.wait());
+ ASSERT_EQ(0U, object->get_pending_appends());
+}
+
+TEST_F(TestObjectRecorder, FlushFuture) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 0, 10, 0, -1);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer = create_append_buffer(234, 123,
+ "payload");
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(1U, object->get_pending_appends());
+
+ C_SaferCond cond;
+ append_buffer.first->wait(&cond);
+ object->flush(append_buffer.first);
+ ASSERT_TRUE(append_buffer.first->is_flush_in_progress() ||
+ append_buffer.first->is_complete());
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestObjectRecorder, FlushDetachedFuture) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer = create_append_buffer(234, 123,
+ "payload");
+
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer};
+
+ object->flush(append_buffer.first);
+ ASSERT_FALSE(append_buffer.first->is_flush_in_progress());
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+
+ // should automatically flush once its attached to the object
+ C_SaferCond cond;
+ append_buffer.first->wait(&cond);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestObjectRecorder, Close) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock = ceph::make_mutex("object_recorder_lock");
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue, 2, 0, 0, -1);
+ auto object = flusher.create_object(oid, 24, &lock);
+
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ "payload");
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1};
+ lock.lock();
+ ASSERT_FALSE(object->append(std::move(append_buffers)));
+ lock.unlock();
+ ASSERT_EQ(1U, object->get_pending_appends());
+
+ lock.lock();
+ ASSERT_FALSE(object->close());
+ ASSERT_TRUE(ceph_mutex_is_locked(lock));
+ lock.unlock();
+
+ ASSERT_TRUE(flusher.wait_for_closed());
+
+ ASSERT_EQ(0U, object->get_pending_appends());
+}
+
+TEST_F(TestObjectRecorder, Overflow) {
+ std::string oid = get_temp_oid();
+ ASSERT_EQ(0, create(oid));
+ ASSERT_EQ(0, client_register(oid));
+ auto metadata = create_metadata(oid);
+ ASSERT_EQ(0, init_metadata(metadata));
+
+ ceph::mutex lock1 = ceph::make_mutex("object_recorder_lock_1");
+ ceph::mutex lock2 = ceph::make_mutex("object_recorder_lock_2");
+
+ ObjectRecorderFlusher flusher(m_ioctx, m_work_queue);
+ auto object1 = flusher.create_object(oid, 12, &lock1);
+
+ std::string payload(1 << 11, '1');
+ journal::AppendBuffer append_buffer1 = create_append_buffer(234, 123,
+ payload);
+ journal::AppendBuffer append_buffer2 = create_append_buffer(234, 124,
+ payload);
+ journal::AppendBuffers append_buffers;
+ append_buffers = {append_buffer1, append_buffer2};
+ lock1.lock();
+ ASSERT_TRUE(object1->append(std::move(append_buffers)));
+ lock1.unlock();
+
+ C_SaferCond cond;
+ append_buffer2.first->wait(&cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(0U, object1->get_pending_appends());
+
+ auto object2 = flusher.create_object(oid, 12, &lock2);
+
+ journal::AppendBuffer append_buffer3 = create_append_buffer(456, 123,
+ payload);
+ append_buffers = {append_buffer3};
+ lock2.lock();
+ ASSERT_FALSE(object2->append(std::move(append_buffers)));
+ lock2.unlock();
+ append_buffer3.first->flush(NULL);
+
+ ASSERT_TRUE(flusher.wait_for_overflow());
+}
diff --git a/src/test/journal/test_main.cc b/src/test/journal/test_main.cc
new file mode 100644
index 000000000..9ed2e4152
--- /dev/null
+++ b/src/test/journal/test_main.cc
@@ -0,0 +1,26 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_crypto.h"
+#include "common/config_proxy.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include <vector>
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_OSD,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+ g_conf().set_val("lockdep", "true");
+ common_init_finish(g_ceph_context);
+
+ int r = RUN_ALL_TESTS();
+ return r;
+}
diff --git a/src/test/kv_store_bench.cc b/src/test/kv_store_bench.cc
new file mode 100644
index 000000000..6a9c9229e
--- /dev/null
+++ b/src/test/kv_store_bench.cc
@@ -0,0 +1,564 @@
+/*
+ * KvStoreBench.cc
+ *
+ * Created on: Aug 23, 2012
+ * Author: eleanor
+ */
+
+#include "test/kv_store_bench.h"
+#include "key_value_store/key_value_structure.h"
+#include "key_value_store/kv_flat_btree_async.h"
+#include "include/rados/librados.hpp"
+#include "test/omap_bench.h"
+#include "common/ceph_argparse.h"
+
+
+#include <string>
+#include <climits>
+#include <iostream>
+#include <sstream>
+#include <cmath>
+
+using std::stringstream;
+using std::cout;
+using std::string;
+using std::pair;
+using std::make_pair;
+using std::map;
+using std::cerr;
+
+KvStoreBench::KvStoreBench()
+: entries(30),
+ ops(100),
+ clients(5),
+ key_size(5),
+ val_size(7),
+ max_ops_in_flight(8),
+ clear_first(false),
+ k(2),
+ cache_size(10),
+ cache_refresh(1),
+ client_name("admin"),
+ verbose(false),
+ kvs(NULL),
+ ops_in_flight(0),
+ rados_id("admin"),
+ pool_name("rbd"),
+ io_ctx_ready(false)
+{
+ probs[25] = 'i';
+ probs[50] = 'u';
+ probs[75] = 'd';
+ probs[100] = 'r';
+}
+
+KvStoreBench::~KvStoreBench()
+{
+ if (io_ctx_ready) {
+ librados::ObjectWriteOperation owo;
+ owo.remove();
+ io_ctx.operate(client_name + ".done-setting", &owo);
+ }
+ delete kvs;
+}
+
+int KvStoreBench::setup(int argc, const char** argv) {
+ auto args = argv_to_vec(argc, argv);
+ srand(time(NULL));
+
+ stringstream help;
+ help
+ << "Usage: KvStoreBench [options]\n"
+ << "Generate latency and throughput statistics for the key value store\n"
+ << "\n"
+ << "There are two sets of options - workload options affect the kind of\n"
+ << "test to run, while algorithm options affect how the key value\n"
+ << "store handles the workload.\n"
+ << "\n"
+ << "There are about entries / k objects in the store to begin with.\n"
+ << "Higher k values reduce the likelihood of splits and the likelihood\n"
+ << "multiple writers simultaneously faling to write because an object \n"
+ << "is full, but having a high k also means there will be more object\n"
+ << "contention.\n"
+ << "\n"
+ << "WORKLOAD OPTIONS\n"
+ << " --name <client name> client name (default admin)\n"
+ << " --entries <number> number of key/value pairs to store initially\n"
+ << " (default " << entries << ")\n"
+ << " --ops <number> number of operations to run\n"
+ << " --keysize <number> number of characters per key (default " << key_size << ")\n"
+ << " --valsize <number> number of characters per value (default " << val_size << ")\n"
+ << " -t <number> number of operations in flight concurrently\n"
+ << " (default " << max_ops_in_flight << ")\n"
+ << " --clients <number> tells this instance how many total clients are. Note that\n"
+ << " changing this does not change the number of clients."
+ << " -d <insert> <update> <delete> <read> percent (1-100) of operations that should be of each type\n"
+ << " (default 25 25 25 25)\n"
+ << " -r <number> random seed to use (default time(0))\n"
+ << "ALGORITHM OPTIONS\n"
+ << " --kval k, where each object has a number of entries\n"
+ << " >= k and <= 2k.\n"
+ << " --cache-size number of index entries to keep in cache\n"
+ << " (default " << cache_size << ")\n"
+ << " --cache-refresh percent (1-100) of cache-size to read each \n"
+ << " time the index is read\n"
+ << "OTHER OPTIONS\n"
+ << " --verbosity-on display debug output\n"
+ << " --clear-first delete all existing objects in the pool before running tests\n";
+ for (unsigned i = 0; i < args.size(); i++) {
+ if(i < args.size() - 1) {
+ if (strcmp(args[i], "--ops") == 0) {
+ ops = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--entries") == 0) {
+ entries = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--kval") == 0) {
+ k = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--keysize") == 0) {
+ key_size = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--valsize") == 0) {
+ val_size = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--cache-size") == 0) {
+ cache_size = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--cache-refresh") == 0) {
+ auto temp = atoi(args[i+1]);
+ assert (temp != 0);
+ cache_refresh = 100 / (double)temp;
+ } else if (strcmp(args[i], "-t") == 0) {
+ max_ops_in_flight = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--clients") == 0) {
+ clients = atoi(args[i+1]);
+ } else if (strcmp(args[i], "-d") == 0) {
+ if (i + 4 >= args.size()) {
+ cout << "Invalid arguments after -d: there must be 4 of them."
+ << std::endl;
+ continue;
+ } else {
+ probs.clear();
+ int sum = atoi(args[i + 1]);
+ probs[sum] = 'i';
+ sum += atoi(args[i + 2]);
+ probs[sum] = 'u';
+ sum += atoi(args[i + 3]);
+ probs[sum] = 'd';
+ sum += atoi(args[i + 4]);
+ probs[sum] = 'r';
+ if (sum != 100) {
+ cout << "Invalid arguments after -d: they must add to 100."
+ << std::endl;
+ }
+ }
+ } else if (strcmp(args[i], "--name") == 0) {
+ client_name = args[i+1];
+ } else if (strcmp(args[i], "-r") == 0) {
+ srand(atoi(args[i+1]));
+ }
+ } else if (strcmp(args[i], "--verbosity-on") == 0) {
+ verbose = true;
+ } else if (strcmp(args[i], "--clear-first") == 0) {
+ clear_first = true;
+ } else if (strcmp(args[i], "--help") == 0) {
+ cout << help.str() << std::endl;
+ exit(1);
+ }
+ }
+
+ KvFlatBtreeAsync * kvba = new KvFlatBtreeAsync(k, client_name, cache_size,
+ cache_refresh, verbose);
+ kvs = kvba;
+
+ int r = rados.init(rados_id.c_str());
+ if (r < 0) {
+ cout << "error during init" << std::endl;
+ return r;
+ }
+ r = rados.conf_parse_argv(argc, argv);
+ if (r < 0) {
+ cout << "error during parsing args" << std::endl;
+ return r;
+ }
+ r = rados.conf_parse_env(NULL);
+ if (r < 0) {
+ cout << "error during parsing env" << std::endl;
+ return r;
+ }
+ r = rados.conf_read_file(NULL);
+ if (r < 0) {
+ cout << "error during read file" << std::endl;
+ return r;
+ }
+ r = rados.connect();
+ if (r < 0) {
+ cout << "error during connect: " << r << std::endl;
+ return r;
+ }
+ r = rados.ioctx_create(pool_name.c_str(), io_ctx);
+ if (r < 0) {
+ cout << "error creating io ctx" << std::endl;
+ rados.shutdown();
+ return r;
+ }
+ io_ctx_ready = true;
+
+ if (clear_first) {
+ librados::NObjectIterator it;
+ for (it = io_ctx.nobjects_begin(); it != io_ctx.nobjects_end(); ++it) {
+ librados::ObjectWriteOperation rm;
+ rm.remove();
+ io_ctx.operate(it->get_oid(), &rm);
+ }
+ }
+
+ int err = kvs->setup(argc, argv);
+ if (err < 0 && err != -17) {
+ cout << "error during setup of kvs: " << err << std::endl;
+ return err;
+ }
+
+ return 0;
+}
+
+string KvStoreBench::random_string(int len) {
+ string ret;
+ string alphanum = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ for (int i = 0; i < len; ++i) {
+ ret.push_back(alphanum[rand() % (alphanum.size() - 1)]);
+ }
+
+ return ret;
+}
+
+pair<string, bufferlist> KvStoreBench::rand_distr(bool new_elem) {
+ pair<string, bufferlist> ret;
+ if (new_elem) {
+ ret = make_pair(random_string(key_size),
+ KvFlatBtreeAsync::to_bl(random_string(val_size)));
+ key_set.insert(ret.first);
+ } else {
+ if (key_set.size() == 0) {
+ return make_pair("",KvFlatBtreeAsync::to_bl(""));
+ }
+ string get_string = random_string(key_size);
+ std::set<string>::iterator it = key_set.lower_bound(get_string);
+ if (it == key_set.end()) {
+ ret.first = *(key_set.rbegin());
+ } else {
+ ret.first = *it;
+ }
+ ret.second = KvFlatBtreeAsync::to_bl(random_string(val_size));
+ }
+ return ret;
+}
+
+int KvStoreBench::test_random_insertions() {
+ int err;
+ if (entries == 0) {
+ return 0;
+ }
+ stringstream prev_ss;
+ prev_ss << (atoi(client_name.c_str()) - 1);
+ string prev_rid = prev_ss.str();
+ stringstream last_ss;
+ if (client_name.size() > 1) {
+ last_ss << client_name.substr(0,client_name.size() - 2);
+ }
+ last_ss << clients - 1;
+ string last_rid = client_name == "admin" ? "admin" : last_ss.str();
+
+ map<string, bufferlist> big_map;
+ for (int i = 0; i < entries; i++) {
+ bufferlist bfr;
+ bfr.append(random_string(7));
+ big_map[random_string(5)] = bfr;
+ }
+
+ uint64_t uint;
+ time_t t;
+ if (client_name[client_name.size() - 1] != '0' && client_name != "admin") {
+ do {
+ librados::ObjectReadOperation oro;
+ oro.stat(&uint, &t, &err);
+ err = io_ctx.operate(prev_rid + ".done-setting", &oro, NULL);
+ if (verbose) cout << "reading " << prev_rid << ": err = " << err
+ << std::endl;
+ } while (err != 0);
+ cout << "detected " << prev_rid << ".done-setting" << std::endl;
+ }
+
+ cout << "testing random insertions";
+ err = kvs->set_many(big_map);
+ if (err < 0) {
+ cout << "error setting things" << std::endl;
+ return err;
+ }
+
+ librados::ObjectWriteOperation owo;
+ owo.create(true);
+ io_ctx.operate(client_name + ".done-setting", &owo);
+ cout << "created " << client_name + ".done-setting. waiting for "
+ << last_rid << ".done-setting" << std::endl;
+
+ do {
+ librados::ObjectReadOperation oro;
+ oro.stat(&uint, &t, &err);
+ err = io_ctx.operate(last_rid + ".done-setting", &oro, NULL);
+ } while (err != 0);
+ cout << "detected " << last_rid << ".done-setting" << std::endl;
+
+ return err;
+}
+
+void KvStoreBench::aio_callback_timed(int * err, void *arg) {
+ timed_args *args = reinterpret_cast<timed_args *>(arg);
+ ceph::mutex * ops_in_flight_lock = &args->kvsb->ops_in_flight_lock;
+ ceph::mutex * data_lock = &args->kvsb->data_lock;
+ ceph::condition_variable * op_avail = &args->kvsb->op_avail;
+ int *ops_in_flight = &args->kvsb->ops_in_flight;
+ if (*err < 0 && *err != -61) {
+ cerr << "Error during " << args->op << " operation: " << *err << std::endl;
+ }
+
+ args->sw.stop_time();
+ double time = args->sw.get_time();
+ args->sw.clear();
+
+ data_lock->lock();
+ //latency
+ args->kvsb->data.latency_jf.open_object_section("latency");
+ args->kvsb->data.latency_jf.dump_float(string(1, args->op).c_str(),
+ time);
+ args->kvsb->data.latency_jf.close_section();
+
+ //throughput
+ args->kvsb->data.throughput_jf.open_object_section("throughput");
+ args->kvsb->data.throughput_jf.dump_unsigned(string(1, args->op).c_str(),
+ ceph_clock_now());
+ args->kvsb->data.throughput_jf.close_section();
+
+ data_lock->unlock();
+
+ ops_in_flight_lock->lock();
+ (*ops_in_flight)--;
+ op_avail->notify_all();
+ ops_in_flight_lock->unlock();
+
+ delete args;
+}
+
+int KvStoreBench::test_teuthology_aio(next_gen_t distr,
+ const map<int, char> &probs)
+{
+ int err = 0;
+ cout << "inserting initial entries..." << std::endl;
+ err = test_random_insertions();
+ if (err < 0) {
+ return err;
+ }
+ cout << "finished inserting initial entries. Waiting 10 seconds for everyone"
+ << " to catch up..." << std::endl;
+
+ sleep(10);
+
+ cout << "done waiting. Starting random operations..." << std::endl;
+
+ std::unique_lock l{ops_in_flight_lock};
+ for (int i = 0; i < ops; i++) {
+ ceph_assert(ops_in_flight <= max_ops_in_flight);
+ if (ops_in_flight == max_ops_in_flight) {
+ op_avail.wait(l);
+ ceph_assert(ops_in_flight < max_ops_in_flight);
+ }
+ cout << "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" << i + 1 << " / "
+ << ops << std::endl;
+ timed_args * cb_args = new timed_args(this);
+ pair<string, bufferlist> kv;
+ int random = (rand() % 100);
+ cb_args->op = probs.lower_bound(random)->second;
+ switch (cb_args->op) {
+ case 'i':
+ kv = (((KvStoreBench *)this)->*distr)(true);
+ if (kv.first == "") {
+ i--;
+ delete cb_args;
+ continue;
+ }
+ ops_in_flight++;
+ cb_args->sw.start_time();
+ kvs->aio_set(kv.first, kv.second, false, aio_callback_timed,
+ cb_args, &cb_args->err);
+ break;
+ case 'u':
+ kv = (((KvStoreBench *)this)->*distr)(false);
+ if (kv.first == "") {
+ i--;
+ delete cb_args;
+ continue;
+ }
+ ops_in_flight++;
+ cb_args->sw.start_time();
+ kvs->aio_set(kv.first, kv.second, true, aio_callback_timed,
+ cb_args, &cb_args->err);
+ break;
+ case 'd':
+ kv = (((KvStoreBench *)this)->*distr)(false);
+ if (kv.first == "") {
+ i--;
+ delete cb_args;
+ continue;
+ }
+ key_set.erase(kv.first);
+ ops_in_flight++;
+ cb_args->sw.start_time();
+ kvs->aio_remove(kv.first, aio_callback_timed, cb_args, &cb_args->err);
+ break;
+ case 'r':
+ kv = (((KvStoreBench *)this)->*distr)(false);
+ if (kv.first == "") {
+ i--;
+ delete cb_args;
+ continue;
+ }
+ ops_in_flight++;
+ cb_args->sw.start_time();
+ kvs->aio_get(kv.first, &cb_args->val, aio_callback_timed,
+ cb_args, &cb_args->err);
+ break;
+ default:
+ // shouldn't happen here
+ assert(false);
+ }
+
+ }
+
+ op_avail.wait(l, [this] { return ops_in_flight <= 0; });
+
+ print_time_data();
+ return err;
+}
+
+int KvStoreBench::test_teuthology_sync(next_gen_t distr,
+ const map<int, char> &probs)
+{
+ int err = 0;
+ err = test_random_insertions();
+ if (err < 0) {
+ return err;
+ }
+ sleep(10);
+ for (int i = 0; i < ops; i++) {
+ StopWatch sw;
+ pair<char, double> d;
+ cout << "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" << i + 1 << " / "
+ << ops << std::endl;
+ pair<string, bufferlist> kv;
+ int random = (rand() % 100);
+ d.first = probs.lower_bound(random)->second;
+ switch (d.first) {
+ case 'i':
+ kv = (((KvStoreBench *)this)->*distr)(true);
+ if (kv.first == "") {
+ i--;
+ continue;
+ }
+ sw.start_time();
+ err = kvs->set(kv.first, kv.second, true);
+ sw.stop_time();
+ if (err < 0) {
+ cout << "Error setting " << kv << ": " << err << std::endl;
+ return err;
+ }
+ break;
+ case 'u':
+ kv = (((KvStoreBench *)this)->*distr)(false);
+ if (kv.first == "") {
+ i--;
+ continue;
+ }
+ sw.start_time();
+ err = kvs->set(kv.first, kv.second, true);
+ sw.stop_time();
+ if (err < 0 && err != -61) {
+ cout << "Error updating " << kv << ": " << err << std::endl;
+ return err;
+ }
+ break;
+ case 'd':
+ kv = (((KvStoreBench *)this)->*distr)(false);
+ if (kv.first == "") {
+ i--;
+ continue;
+ }
+ key_set.erase(kv.first);
+ sw.start_time();
+ err = kvs->remove(kv.first);
+ sw.stop_time();
+ if (err < 0 && err != -61) {
+ cout << "Error removing " << kv << ": " << err << std::endl;
+ return err;
+ }
+ break;
+ case 'r':
+ kv = (((KvStoreBench *)this)->*distr)(false);
+ if (kv.first == "") {
+ i--;
+ continue;
+ }
+ bufferlist val;
+ sw.start_time();
+ err = kvs->get(kv.first, &kv.second);
+ sw.stop_time();
+ if (err < 0 && err != -61) {
+ cout << "Error getting " << kv << ": " << err << std::endl;
+ return err;
+ }
+ break;
+ }
+
+ double time = sw.get_time();
+ d.second = time;
+ sw.clear();
+ //latency
+ data.latency_jf.open_object_section("latency");
+ data.latency_jf.dump_float(string(1, d.first).c_str(),
+ time);
+ data.latency_jf.close_section();
+ }
+
+ print_time_data();
+ return err;
+}
+
+void KvStoreBench::print_time_data() {
+ cout << "========================================================\n";
+ cout << "latency:" << std::endl;
+ data.latency_jf.flush(cout);
+ cout << std::endl;
+ cout << "throughput:" << std::endl;
+ data.throughput_jf.flush(cout);
+ cout << "\n========================================================"
+ << std::endl;
+}
+
+int KvStoreBench::teuthology_tests() {
+ int err = 0;
+ if (max_ops_in_flight > 1) {
+ err = test_teuthology_aio(&KvStoreBench::rand_distr, probs);
+ } else {
+ err = test_teuthology_sync(&KvStoreBench::rand_distr, probs);
+ }
+ return err;
+}
+
+int main(int argc, const char** argv) {
+ KvStoreBench kvsb;
+ int err = kvsb.setup(argc, argv);
+ if (err == 0) cout << "setup successful" << std::endl;
+ else{
+ cout << "error " << err << std::endl;
+ return err;
+ }
+ err = kvsb.teuthology_tests();
+ if (err < 0) return err;
+ return 0;
+};
diff --git a/src/test/kv_store_bench.h b/src/test/kv_store_bench.h
new file mode 100644
index 000000000..689c769ce
--- /dev/null
+++ b/src/test/kv_store_bench.h
@@ -0,0 +1,192 @@
+/*
+ * Benchmarking suite for key-value store
+ *
+ * September 2, 2012
+ * Eleanor Cawthon
+ * eleanor.cawthon@inktank.com
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#ifndef KVSTOREBENCH_H_
+#define KVSTOREBENCH_H_
+
+#include "key_value_store/key_value_structure.h"
+#include "key_value_store/kv_flat_btree_async.h"
+#include "common/Clock.h"
+#include "global/global_context.h"
+#include "common/Cond.h"
+
+#include <string>
+#include <climits>
+#include <cfloat>
+#include <iostream>
+
+/**
+ * stores pairings from op type to time taken for that op (for latency), and to
+ * time that op completed to the nearest second (for throughput).
+ */
+struct kv_bench_data {
+ JSONFormatter throughput_jf;
+
+ JSONFormatter latency_jf;
+};
+
+class KvStoreBench;
+
+/**
+ * keeps track of the number of milliseconds between two events - used to
+ * measure latency
+ */
+struct StopWatch {
+ utime_t begin_time;
+ utime_t end_time;
+
+ void start_time() {
+ begin_time = ceph_clock_now();
+ }
+ void stop_time() {
+ end_time = ceph_clock_now();
+ }
+ double get_time() {
+ return (end_time - begin_time) * 1000;
+ }
+ void clear() {
+ begin_time = end_time = utime_t();
+ }
+};
+
+/**
+ * arguments passed to the callback method when the op is being timed
+ */
+struct timed_args {
+ StopWatch sw;
+ //kv_bench_data data;
+ KvStoreBench * kvsb;
+ ceph::buffer::list val;
+ int err;
+ char op;
+
+ timed_args ()
+ : kvsb(NULL),
+ err(0),
+ op(' ')
+ {};
+
+ timed_args (KvStoreBench * k)
+ : kvsb(k),
+ err(0),
+ op(' ')
+ {}
+};
+
+typedef std::pair<std::string, ceph::buffer::list> (KvStoreBench::*next_gen_t)(bool new_elem);
+
+class KvStoreBench {
+
+protected:
+
+ //test setup variables set from command line
+ int entries; //the number of entries to write initially
+ int ops; //the number of operations to time
+ int clients; //the total number of clients running this test - used
+ //in the aio test to coordinate the end of the initial sets
+ int key_size;//number of characters in keys to write
+ int val_size;//number of characters in values to write
+ int max_ops_in_flight;
+ bool clear_first;//if true, remove all objects in pool before starting tests
+
+ //variables passed to KeyValueStructure
+ int k;
+ int cache_size; //number of index entries to store in cache
+ double cache_refresh; //cache_size / cache_refresh entries are read each time
+ //the index is read
+ std::string client_name;
+ bool verbose;//if true, display debug output
+
+ //internal
+ std::map<int, char> probs;//map of numbers from 1 to 100 to chars representing
+ //operation types - used to generate random operations
+ std::set<std::string> key_set;//set of keys already in the data set
+ KeyValueStructure * kvs;
+ kv_bench_data data;//stores throughput and latency from completed tests
+ ceph::mutex data_lock = ceph::make_mutex("data lock");
+ ceph::condition_variable op_avail; // signaled when an op completes
+ int ops_in_flight;//number of operations currently in progress
+ ceph::mutex ops_in_flight_lock =
+ ceph::make_mutex("KvStoreBench::ops_in_flight_lock");
+ //these are used for cleanup and setup purposes - they are NOT passed to kvs!
+ librados::Rados rados;
+ std::string rados_id;
+ std::string pool_name;
+ bool io_ctx_ready;
+ librados::IoCtx io_ctx;
+
+ /**
+ * Prints JSON-formatted throughput and latency data.
+ *
+ * Throughput data is {'char representing the operation type':time the op
+ * completed to the nearest second}
+ * Latency is {'char representing the operation type':time taken by the op}
+ */
+ void print_time_data();
+
+public:
+
+ KvStoreBench();
+
+ //after this is called, objects created by the KeyValueStructure remain.
+ ~KvStoreBench();
+
+ /**
+ * parses command line arguments, sets up this rados instance, clears the
+ * pool if clear_first is true and calls kvs->setup.
+ */
+ int setup(int argc, const char** argv);
+
+ /**
+ * Returns a string of random characters of length len
+ */
+ std::string random_string(int len);
+
+ /**
+ * Inserts entries random keys and values asynchronously.
+ */
+ int test_random_insertions();
+
+ /**
+ * calls test_random_insertions, then does ops randomly chosen operations
+ * asynchronously, with max_ops_in_flight operations at a time.
+ */
+ int test_teuthology_aio(next_gen_t distr, const std::map<int, char> &probs);
+
+ /**
+ * calls test_random_insertions, then does ops randomly chosen operations
+ * synchronously.
+ */
+ int test_teuthology_sync(next_gen_t distr, const std::map<int, char> &probs);
+
+ /**
+ * returns a key-value pair. If new_elem is true, the key is randomly
+ * generated. If it is false, the key is selected from the keys currently in
+ * the key set.
+ */
+ std::pair<std::string, ceph::buffer::list> rand_distr(bool new_elem);
+
+ /**
+ * Called when aio operations complete. Updates data.
+ */
+ static void aio_callback_timed(int * err, void *arg);
+
+ /**
+ * Calls test_ methods. Change to call, for example, multiple runs of a test
+ * with different settings. Currently just calls test_teuthology_aio.
+ */
+ int teuthology_tests();
+
+};
+
+#endif /* KVSTOREBENCH_H_ */
diff --git a/src/test/lazy-omap-stats/CMakeLists.txt b/src/test/lazy-omap-stats/CMakeLists.txt
new file mode 100644
index 000000000..bddd074c5
--- /dev/null
+++ b/src/test/lazy-omap-stats/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Lazy omap stat collection tests
+
+add_executable(ceph_test_lazy_omap_stats
+ main.cc
+ lazy_omap_stats_test.cc)
+target_link_libraries(ceph_test_lazy_omap_stats
+ librados Boost::system ceph-common ${UNITTEST_LIBS})
+install(TARGETS
+ ceph_test_lazy_omap_stats
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/lazy-omap-stats/lazy_omap_stats_test.cc b/src/test/lazy-omap-stats/lazy_omap_stats_test.cc
new file mode 100644
index 000000000..d0b8bd734
--- /dev/null
+++ b/src/test/lazy-omap-stats/lazy_omap_stats_test.cc
@@ -0,0 +1,621 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#include <algorithm>
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/uuid/uuid.hpp> // uuid class
+#include <boost/uuid/uuid_generators.hpp> // generators
+#include <boost/uuid/uuid_io.hpp> // streaming operators etc.
+#include <chrono>
+#include <iostream>
+#include <thread>
+#include <vector>
+
+#include "common/ceph_json.h"
+#include "global/global_init.h"
+#include "include/compat.h"
+
+#include "lazy_omap_stats_test.h"
+
+using namespace std;
+
+void LazyOmapStatsTest::init(const int argc, const char** argv)
+{
+ int ret = rados.init("admin");
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to initialise rados! Error: " << ret << " " << strerror(ret)
+ << endl;
+ exit(ret);
+ }
+
+ ret = rados.conf_parse_argv(argc, argv);
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to parse command line config options! Error: " << ret << " "
+ << strerror(ret) << endl;
+ exit(ret);
+ }
+
+ rados.conf_parse_env(NULL);
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to parse environment! Error: " << ret << " "
+ << strerror(ret) << endl;
+ exit(ret);
+ }
+
+ rados.conf_read_file(NULL);
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to read config file! Error: " << ret << " " << strerror(ret)
+ << endl;
+ exit(ret);
+ }
+
+ ret = rados.connect();
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to connect to running cluster! Error: " << ret << " "
+ << strerror(ret) << endl;
+ exit(ret);
+ }
+
+ string command = R"(
+ {
+ "prefix": "osd pool create",
+ "pool": ")" + conf.pool_name +
+ R"(",
+ "pool_type": "replicated",
+ "size": )" + to_string(conf.replica_count) +
+ R"(
+ })";
+ librados::bufferlist inbl;
+ string output;
+ ret = rados.mon_command(command, inbl, nullptr, &output);
+ if (output.length()) cout << output << endl;
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to create pool! Error: " << ret << " " << strerror(ret)
+ << endl;
+ exit(ret);
+ }
+
+ ret = rados.ioctx_create(conf.pool_name.c_str(), io_ctx);
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to create ioctx! Error: " << ret << " " << strerror(ret)
+ << endl;
+ exit(ret);
+ }
+
+ get_pool_id(conf.pool_name);
+}
+
+void LazyOmapStatsTest::shutdown()
+{
+ rados.pool_delete(conf.pool_name.c_str());
+ rados.shutdown();
+}
+
+void LazyOmapStatsTest::write_omap(const string& object_name)
+{
+ librados::bufferlist bl;
+ int ret = io_ctx.write_full(object_name, bl);
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to create object! Error: " << ret << " " << strerror(ret)
+ << endl;
+ exit(ret);
+ }
+ ret = io_ctx.omap_set(object_name, payload);
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to write omap payload! Error: " << ret << " "
+ << strerror(ret) << endl;
+ exit(ret);
+ }
+ cout << "Wrote " << conf.keys << " omap keys of " << conf.payload_size
+ << " bytes to "
+ << "the " << object_name << " object" << endl;
+}
+
+const string LazyOmapStatsTest::get_name() const
+{
+ boost::uuids::uuid uuid = boost::uuids::random_generator()();
+ return boost::uuids::to_string(uuid);
+}
+
+void LazyOmapStatsTest::write_many(uint how_many)
+{
+ for (uint i = 0; i < how_many; i++) {
+ write_omap(get_name());
+ }
+}
+
+void LazyOmapStatsTest::create_payload()
+{
+ librados::bufferlist Lorem;
+ Lorem.append(
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
+ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
+ "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
+ "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
+ "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
+ "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
+ "sunt in culpa qui officia deserunt mollit anim id est laborum.");
+ conf.payload_size = Lorem.length();
+ conf.total_bytes = conf.keys * conf.payload_size * conf.how_many;
+ conf.total_keys = conf.keys * conf.how_many;
+ uint i = 0;
+ for (i = 1; i < conf.keys + 1; ++i) {
+ payload[get_name()] = Lorem;
+ }
+ cout << "Created payload with " << conf.keys << " keys of "
+ << conf.payload_size
+ << " bytes each. Total size in bytes = " << conf.keys * conf.payload_size
+ << endl;
+}
+
+void LazyOmapStatsTest::scrub()
+{
+ cout << "Scrubbing" << endl;
+
+ cout << "Before scrub stamps:" << endl;
+ string target_pool(conf.pool_id);
+ target_pool.append(".");
+ bool target_pool_found = false;
+ map<string, string> before_scrub = get_scrub_stamps();
+ for (auto [pg, stamp] : before_scrub) {
+ cout << "pg = " << pg << " stamp = " << stamp << endl;
+ if (pg.rfind(target_pool, 0) == 0) {
+ target_pool_found = true;
+ }
+ }
+ if (!target_pool_found) {
+ cout << "Error: Target pool " << conf.pool_name << ":" << conf.pool_id
+ << " not found!" << endl;
+ exit(2); // ENOENT
+ }
+ cout << endl;
+
+ // Short sleep to make sure the new pool is visible
+ sleep(5);
+
+ string command = R"({"prefix": "osd deep-scrub", "who": "all"})";
+ auto output = get_output(command);
+ cout << output << endl;
+
+ cout << "Waiting for deep-scrub to complete..." << endl;
+ while (sleep(1) == 0) {
+ cout << "Current scrub stamps:" << endl;
+ bool complete = true;
+ map<string, string> current_stamps = get_scrub_stamps();
+ for (auto [pg, stamp] : current_stamps) {
+ cout << "pg = " << pg << " stamp = " << stamp << endl;
+ if (stamp == before_scrub[pg]) {
+ // See if stamp for each pg has changed
+ // If not, we haven't completed the deep-scrub
+ complete = false;
+ }
+ }
+ cout << endl;
+ if (complete) {
+ break;
+ }
+ }
+ cout << "Scrubbing complete" << endl;
+}
+
+const int LazyOmapStatsTest::find_matches(string& output, regex& reg) const
+{
+ sregex_iterator cur(output.begin(), output.end(), reg);
+ uint x = 0;
+ for (auto end = std::sregex_iterator(); cur != end; ++cur) {
+ cout << (*cur)[1].str() << endl;
+ x++;
+ }
+ return x;
+}
+
+const string LazyOmapStatsTest::get_output(const string command,
+ const bool silent,
+ const CommandTarget target)
+{
+ librados::bufferlist inbl, outbl;
+ string output;
+ int ret = 0;
+ if (target == CommandTarget::TARGET_MON) {
+ ret = rados.mon_command(command, inbl, &outbl, &output);
+ } else {
+ ret = rados.mgr_command(command, inbl, &outbl, &output);
+ }
+ if (output.length() && !silent) {
+ cout << output << endl;
+ }
+ if (ret < 0) {
+ ret = -ret;
+ cerr << "Failed to get " << command << "! Error: " << ret << " "
+ << strerror(ret) << endl;
+ exit(ret);
+ }
+ return string(outbl.c_str(), outbl.length());
+}
+
+void LazyOmapStatsTest::get_pool_id(const string& pool)
+{
+ cout << R"(Querying pool id)" << endl;
+
+ string command = R"({"prefix": "osd pool ls", "detail": "detail", "format": "json"})";
+ librados::bufferlist inbl, outbl;
+ auto output = get_output(command, false, CommandTarget::TARGET_MON);
+ JSONParser parser;
+ parser.parse(output.c_str(), output.size());
+ for (const auto& pool : parser.get_array_elements()) {
+ JSONParser parser2;
+ parser2.parse(pool.c_str(), static_cast<int>(pool.size()));
+ auto* obj = parser2.find_obj("pool_name");
+ if (obj->get_data().compare(conf.pool_name) == 0) {
+ obj = parser2.find_obj("pool_id");
+ conf.pool_id = obj->get_data();
+ }
+ }
+ if (conf.pool_id.empty()) {
+ cout << "Failed to find pool ID for pool " << conf.pool_name << "!" << endl;
+ exit(2); // ENOENT
+ } else {
+ cout << "Found pool ID: " << conf.pool_id << endl;
+ }
+}
+
+map<string, string> LazyOmapStatsTest::get_scrub_stamps() {
+ map<string, string> stamps;
+ string command = R"({"prefix": "pg dump", "format": "json"})";
+ auto output = get_output(command);
+ JSONParser parser;
+ parser.parse(output.c_str(), output.size());
+ auto* obj = parser.find_obj("pg_map")->find_obj("pg_stats");
+ for (auto pg = obj->find_first(); !pg.end(); ++pg) {
+ stamps.insert({(*pg)->find_obj("pgid")->get_data(),
+ (*pg)->find_obj("last_deep_scrub_stamp")->get_data()});
+ }
+ return stamps;
+}
+
+void LazyOmapStatsTest::check_one()
+{
+ string full_output = get_output();
+ cout << full_output << endl;
+ regex reg(
+ "\n"
+ R"((PG_STAT[\s\S]*)"
+ "\n)OSD_STAT"); // Strip OSD_STAT table so we don't find matches there
+ smatch match;
+ regex_search(full_output, match, reg);
+ auto truncated_output = match[1].str();
+ cout << truncated_output << endl;
+ reg = regex(
+ "\n"
+ R"(([0-9,s].*\s)" +
+ to_string(conf.keys) +
+ R"(\s.*))"
+ "\n");
+
+ cout << "Checking number of keys " << conf.keys << endl;
+ cout << "Found the following lines" << endl;
+ cout << "*************************" << endl;
+ uint result = find_matches(truncated_output, reg);
+ cout << "**********************" << endl;
+ cout << "Found " << result << " matching line(s)" << endl;
+ uint total = result;
+
+ reg = regex(
+ "\n"
+ R"(([0-9,s].*\s)" +
+ to_string(conf.payload_size * conf.keys) +
+ R"(\s.*))"
+ "\n");
+ cout << "Checking number of bytes "
+ << conf.payload_size * conf.keys << endl;
+ cout << "Found the following lines" << endl;
+ cout << "*************************" << endl;
+ result = find_matches(truncated_output, reg);
+ cout << "**********************" << endl;
+ cout << "Found " << result << " matching line(s)" << endl;
+
+ total += result;
+ if (total != 6) {
+ cout << "Error: Found " << total << " matches, expected 6! Exiting..."
+ << endl;
+ exit(22); // EINVAL
+ }
+ cout << "check_one successful. Found " << total << " matches as expected"
+ << endl;
+}
+
+const int LazyOmapStatsTest::find_index(string& haystack, regex& needle,
+ string label) const
+{
+ smatch match;
+ regex_search(haystack, match, needle);
+ auto line = match[1].str();
+ boost::algorithm::trim(line);
+ boost::char_separator<char> sep{" "};
+ boost::tokenizer<boost::char_separator<char>> tok(line, sep);
+ vector<string> tokens(tok.begin(), tok.end());
+ auto it = find(tokens.begin(), tokens.end(), label);
+ if (it != tokens.end()) {
+ return distance(tokens.begin(), it);
+ }
+
+ cerr << "find_index failed to find index for " << label << endl;
+ exit(2); // ENOENT
+ return -1; // Unreachable
+}
+
+const uint LazyOmapStatsTest::tally_column(const uint omap_bytes_index,
+ const string& table,
+ bool header) const
+{
+ istringstream buffer(table);
+ string line;
+ uint64_t total = 0;
+ while (std::getline(buffer, line)) {
+ if (header) {
+ header = false;
+ continue;
+ }
+ boost::char_separator<char> sep{" "};
+ boost::tokenizer<boost::char_separator<char>> tok(line, sep);
+ vector<string> tokens(tok.begin(), tok.end());
+ total += stoi(tokens.at(omap_bytes_index));
+ }
+
+ return total;
+}
+
+void LazyOmapStatsTest::check_column(const int index, const string& table,
+ const string& type, bool header) const
+{
+ uint expected;
+ string errormsg;
+ if (type.compare("bytes") == 0) {
+ expected = conf.total_bytes;
+ errormsg = "Error. Got unexpected byte count!";
+ } else {
+ expected = conf.total_keys;
+ errormsg = "Error. Got unexpected key count!";
+ }
+ uint sum = tally_column(index, table, header);
+ cout << "Got: " << sum << " Expected: " << expected << endl;
+ if (sum != expected) {
+ cout << errormsg << endl;
+ exit(22); // EINVAL
+ }
+}
+
+index_t LazyOmapStatsTest::get_indexes(regex& reg, string& output) const
+{
+ index_t indexes;
+ indexes.byte_index = find_index(output, reg, "OMAP_BYTES*");
+ indexes.key_index = find_index(output, reg, "OMAP_KEYS*");
+
+ return indexes;
+}
+
+void LazyOmapStatsTest::check_pg_dump()
+{
+ cout << R"(Checking "pg dump" output)" << endl;
+
+ string dump_output = get_output();
+ cout << dump_output << endl;
+
+ regex reg(
+ "\n"
+ R"((PG_STAT\s.*))"
+ "\n");
+ index_t indexes = get_indexes(reg, dump_output);
+
+ reg =
+ "\n"
+ R"((PG_STAT[\s\S]*))"
+ "\n +\n[0-9]";
+ smatch match;
+ regex_search(dump_output, match, reg);
+ auto table = match[1].str();
+
+ cout << "Checking bytes" << endl;
+ check_column(indexes.byte_index, table, string("bytes"));
+
+ cout << "Checking keys" << endl;
+ check_column(indexes.key_index, table, string("keys"));
+
+ cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_dump_summary()
+{
+ cout << R"(Checking "pg dump summary" output)" << endl;
+
+ string command = R"({"prefix": "pg dump", "dumpcontents": ["summary"]})";
+ string dump_output = get_output(command);
+ cout << dump_output << endl;
+
+ regex reg(
+ "\n"
+ R"((PG_STAT\s.*))"
+ "\n");
+ index_t indexes = get_indexes(reg, dump_output);
+
+ reg =
+ "\n"
+ R"((sum\s.*))"
+ "\n";
+ smatch match;
+ regex_search(dump_output, match, reg);
+ auto table = match[1].str();
+
+ cout << "Checking bytes" << endl;
+ check_column(indexes.byte_index, table, string("bytes"), false);
+
+ cout << "Checking keys" << endl;
+ check_column(indexes.key_index, table, string("keys"), false);
+ cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_dump_pgs()
+{
+ cout << R"(Checking "pg dump pgs" output)" << endl;
+
+ string command = R"({"prefix": "pg dump", "dumpcontents": ["pgs"]})";
+ string dump_output = get_output(command);
+ cout << dump_output << endl;
+
+ regex reg(R"(^(PG_STAT\s.*))"
+ "\n");
+ index_t indexes = get_indexes(reg, dump_output);
+
+ reg = R"(^(PG_STAT[\s\S]*))"
+ "\n\n";
+ smatch match;
+ regex_search(dump_output, match, reg);
+ auto table = match[1].str();
+
+ cout << "Checking bytes" << endl;
+ check_column(indexes.byte_index, table, string("bytes"));
+
+ cout << "Checking keys" << endl;
+ check_column(indexes.key_index, table, string("keys"));
+ cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_dump_pools()
+{
+ cout << R"(Checking "pg dump pools" output)" << endl;
+
+ string command = R"({"prefix": "pg dump", "dumpcontents": ["pools"]})";
+ string dump_output = get_output(command);
+ cout << dump_output << endl;
+
+ regex reg(R"(^(POOLID\s.*))"
+ "\n");
+ index_t indexes = get_indexes(reg, dump_output);
+
+ reg =
+ "\n"
+ R"(()" +
+ conf.pool_id +
+ R"(\s.*))"
+ "\n";
+ smatch match;
+ regex_search(dump_output, match, reg);
+ auto line = match[1].str();
+
+ cout << "Checking bytes" << endl;
+ check_column(indexes.byte_index, line, string("bytes"), false);
+
+ cout << "Checking keys" << endl;
+ check_column(indexes.key_index, line, string("keys"), false);
+ cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_ls()
+{
+ cout << R"(Checking "pg ls" output)" << endl;
+
+ string command = R"({"prefix": "pg ls"})";
+ string dump_output = get_output(command);
+ cout << dump_output << endl;
+
+ regex reg(R"(^(PG\s.*))"
+ "\n");
+ index_t indexes = get_indexes(reg, dump_output);
+
+ reg = R"(^(PG[\s\S]*))"
+ "\n\n";
+ smatch match;
+ regex_search(dump_output, match, reg);
+ auto table = match[1].str();
+
+ cout << "Checking bytes" << endl;
+ check_column(indexes.byte_index, table, string("bytes"));
+
+ cout << "Checking keys" << endl;
+ check_column(indexes.key_index, table, string("keys"));
+ cout << endl;
+}
+
+void LazyOmapStatsTest::wait_for_active_clean()
+{
+ cout << "Waiting for active+clean" << endl;
+
+ int index = -1;
+ regex reg(
+ "\n"
+ R"((PG_STAT[\s\S]*))"
+ "\n +\n[0-9]");
+ string command = R"({"prefix": "pg dump"})";
+ int num_not_clean;
+ do {
+ string dump_output = get_output(command, true);
+ if (index == -1) {
+ regex ireg(
+ "\n"
+ R"((PG_STAT\s.*))"
+ "\n");
+ index = find_index(dump_output, ireg, "STATE");
+ }
+ smatch match;
+ regex_search(dump_output, match, reg);
+ istringstream buffer(match[1].str());
+ string line;
+ num_not_clean = 0;
+ while (std::getline(buffer, line)) {
+ if (line.compare(0, 1, "P") == 0) continue;
+ boost::char_separator<char> sep{" "};
+ boost::tokenizer<boost::char_separator<char>> tok(line, sep);
+ vector<string> tokens(tok.begin(), tok.end());
+ num_not_clean += tokens.at(index).compare("active+clean");
+ }
+ cout << "." << flush;
+ this_thread::sleep_for(chrono::milliseconds(250));
+ } while (num_not_clean);
+
+ cout << endl;
+}
+
+const int LazyOmapStatsTest::run(const int argc, const char** argv)
+{
+ init(argc, argv);
+ create_payload();
+ wait_for_active_clean();
+ write_omap(get_name());
+ scrub();
+ check_one();
+
+ write_many(conf.how_many - 1); // Since we already wrote one
+ scrub();
+ check_pg_dump();
+ check_pg_dump_summary();
+ check_pg_dump_pgs();
+ check_pg_dump_pools();
+ check_pg_ls();
+ cout << "All tests passed. Success!" << endl;
+
+ shutdown();
+
+ return 0;
+}
diff --git a/src/test/lazy-omap-stats/lazy_omap_stats_test.h b/src/test/lazy-omap-stats/lazy_omap_stats_test.h
new file mode 100644
index 000000000..57cbe6e32
--- /dev/null
+++ b/src/test/lazy-omap-stats/lazy_omap_stats_test.h
@@ -0,0 +1,88 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_LAZY_OMAP_STATS_TEST_H
+#define CEPH_LAZY_OMAP_STATS_TEST_H
+
+#include <map>
+#include <regex>
+#include <string>
+
+#include "include/compat.h"
+#include "include/rados/librados.hpp"
+
+struct index_t {
+ unsigned byte_index = 0;
+ unsigned key_index = 0;
+};
+
+class LazyOmapStatsTest
+{
+ librados::IoCtx io_ctx;
+ librados::Rados rados;
+ std::map<std::string, librados::bufferlist> payload;
+
+ struct lazy_omap_test_t {
+ unsigned payload_size = 0;
+ unsigned replica_count = 3;
+ unsigned keys = 2000;
+ unsigned how_many = 50;
+ std::string pool_name = "lazy_omap_test_pool";
+ std::string pool_id;
+ unsigned total_bytes = 0;
+ unsigned total_keys = 0;
+ } conf;
+
+ typedef enum {
+ TARGET_MON,
+ TARGET_MGR
+ } CommandTarget;
+
+ LazyOmapStatsTest(LazyOmapStatsTest&) = delete;
+ void operator=(LazyOmapStatsTest) = delete;
+ void init(const int argc, const char** argv);
+ void shutdown();
+ void write_omap(const std::string& object_name);
+ const std::string get_name() const;
+ void create_payload();
+ void write_many(const unsigned how_many);
+ void scrub();
+ const int find_matches(std::string& output, std::regex& reg) const;
+ void check_one();
+ const int find_index(std::string& haystack, std::regex& needle,
+ std::string label) const;
+ const unsigned tally_column(const unsigned omap_bytes_index,
+ const std::string& table, bool header) const;
+ void check_column(const int index, const std::string& table,
+ const std::string& type, bool header = true) const;
+ index_t get_indexes(std::regex& reg, std::string& output) const;
+ void check_pg_dump();
+ void check_pg_dump_summary();
+ void check_pg_dump_pgs();
+ void check_pg_dump_pools();
+ void check_pg_ls();
+ const std::string get_output(
+ const std::string command = R"({"prefix": "pg dump"})",
+ const bool silent = false,
+ const CommandTarget target = CommandTarget::TARGET_MGR);
+ void get_pool_id(const std::string& pool);
+ std::map<std::string, std::string> get_scrub_stamps();
+ void wait_for_active_clean();
+
+ public:
+ LazyOmapStatsTest() = default;
+ const int run(const int argc, const char** argv);
+};
+
+#endif // CEPH_LAZY_OMAP_STATS_TEST_H
diff --git a/src/test/lazy-omap-stats/main.cc b/src/test/lazy-omap-stats/main.cc
new file mode 100644
index 000000000..d379e8fbd
--- /dev/null
+++ b/src/test/lazy-omap-stats/main.cc
@@ -0,0 +1,21 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "lazy_omap_stats_test.h"
+
+int main(const int argc, const char** argv)
+{
+ LazyOmapStatsTest app;
+ return app.run(argc, argv);
+}
diff --git a/src/test/libcephfs/CMakeLists.txt b/src/test/libcephfs/CMakeLists.txt
new file mode 100644
index 000000000..672e6dd8f
--- /dev/null
+++ b/src/test/libcephfs/CMakeLists.txt
@@ -0,0 +1,96 @@
+if(WITH_LIBCEPHFS)
+ add_executable(ceph_test_libcephfs
+ test.cc
+ readdir_r_cb.cc
+ caps.cc
+ multiclient.cc
+ flock.cc
+ recordlock.cc
+ acl.cc
+ main.cc
+ deleg.cc
+ monconfig.cc
+ vxattr.cc
+ snapdiff.cc
+ )
+ target_link_libraries(ceph_test_libcephfs
+ ceph-common
+ cephfs
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_libcephfs
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+ add_executable(ceph_test_libcephfs_suidsgid
+ suidsgid.cc
+ )
+ target_link_libraries(ceph_test_libcephfs_suidsgid
+ ceph-common
+ cephfs
+ librados
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_libcephfs_suidsgid
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+ add_executable(ceph_test_libcephfs_newops
+ main.cc
+ newops.cc
+ )
+ target_link_libraries(ceph_test_libcephfs_newops
+ ceph-common
+ cephfs
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_libcephfs_newops
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+ # uses fork, not available on Windows
+ if(NOT WIN32)
+ add_executable(ceph_test_libcephfs_reclaim
+ reclaim.cc
+ )
+ target_link_libraries(ceph_test_libcephfs_reclaim
+ cephfs
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_libcephfs_reclaim
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+ endif(NOT WIN32)
+
+ add_executable(ceph_test_libcephfs_lazyio
+ lazyio.cc
+ )
+ target_link_libraries(ceph_test_libcephfs_lazyio
+ cephfs
+ librados
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_libcephfs_lazyio
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+ add_executable(ceph_test_libcephfs_access
+ test.cc
+ access.cc
+ )
+ target_link_libraries(ceph_test_libcephfs_access
+ ceph-common
+ cephfs
+ librados
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_libcephfs_access
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(WITH_LIBCEPHFS)
diff --git a/src/test/libcephfs/access.cc b/src/test/libcephfs/access.cc
new file mode 100644
index 000000000..57b1a89fa
--- /dev/null
+++ b/src/test/libcephfs/access.cc
@@ -0,0 +1,399 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "include/buffer.h"
+#include "include/stringify.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include "include/rados/librados.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/uio.h>
+#include <iostream>
+#include <vector>
+#include "json_spirit/json_spirit.h"
+
+#include "include/fs_types.h"
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#endif
+
+using namespace std;
+
+rados_t cluster;
+
+string key;
+
+int do_mon_command(string s, string *key)
+{
+ char *outs, *outbuf;
+ size_t outs_len, outbuf_len;
+ const char *ss = s.c_str();
+ int r = rados_mon_command(cluster, (const char **)&ss, 1,
+ 0, 0,
+ &outbuf, &outbuf_len,
+ &outs, &outs_len);
+ if (outbuf_len) {
+ string s(outbuf, outbuf_len);
+ std::cout << "out: " << s << std::endl;
+
+ // parse out the key
+ json_spirit::mValue v, k;
+ json_spirit::read_or_throw(s, v);
+ k = v.get_array()[0].get_obj().find("key")->second;
+ *key = k.get_str();
+ std::cout << "key: " << *key << std::endl;
+ free(outbuf);
+ } else {
+ return -CEPHFS_EINVAL;
+ }
+ if (outs_len) {
+ string s(outs, outs_len);
+ std::cout << "outs: " << s << std::endl;
+ free(outs);
+ }
+ return r;
+}
+
+string get_unique_dir()
+{
+ return string("/ceph_test_libcephfs_access.") + stringify(rand());
+}
+
+TEST(AccessTest, Foo) {
+ string dir = get_unique_dir();
+ string user = "libcephfs_foo_test." + stringify(rand());
+ // admin mount to set up test
+ struct ceph_mount_info *admin;
+ ASSERT_EQ(0, ceph_create(&admin, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(admin, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(admin, NULL));
+ ASSERT_EQ(0, ceph_mount(admin, "/"));
+ ASSERT_EQ(0, ceph_mkdir(admin, dir.c_str(), 0755));
+
+ // create access key
+ string key;
+ ASSERT_EQ(0, do_mon_command(
+ "{\"prefix\": \"auth get-or-create\", \"entity\": \"client." + user + "\", "
+ "\"caps\": [\"mon\", \"allow *\", \"osd\", \"allow rw\", "
+ "\"mds\", \"allow rw\""
+ "], \"format\": \"json\"}", &key));
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, user.c_str()));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "key", key.c_str()));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ceph_shutdown(cmount);
+
+ // clean up
+ ASSERT_EQ(0, ceph_rmdir(admin, dir.c_str()));
+ ceph_shutdown(admin);
+}
+
+TEST(AccessTest, Path) {
+ string good = get_unique_dir();
+ string bad = get_unique_dir();
+ string user = "libcephfs_path_test." + stringify(rand());
+ struct ceph_mount_info *admin;
+ ASSERT_EQ(0, ceph_create(&admin, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(admin, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(admin, NULL));
+ ASSERT_EQ(0, ceph_mount(admin, "/"));
+ ASSERT_EQ(0, ceph_mkdir(admin, good.c_str(), 0755));
+ ASSERT_EQ(0, ceph_mkdir(admin, string(good + "/p").c_str(), 0755));
+ ASSERT_EQ(0, ceph_mkdir(admin, bad.c_str(), 0755));
+ ASSERT_EQ(0, ceph_mkdir(admin, string(bad + "/p").c_str(), 0755));
+ int fd = ceph_open(admin, string(good + "/q").c_str(), O_CREAT|O_WRONLY, 0755);
+ ceph_close(admin, fd);
+ fd = ceph_open(admin, string(bad + "/q").c_str(), O_CREAT|O_WRONLY, 0755);
+ ceph_close(admin, fd);
+ fd = ceph_open(admin, string(bad + "/z").c_str(), O_CREAT|O_WRONLY, 0755);
+ ceph_write(admin, fd, "TEST FAILED", 11, 0);
+ ceph_close(admin, fd);
+
+ string key;
+ ASSERT_EQ(0, do_mon_command(
+ "{\"prefix\": \"auth get-or-create\", \"entity\": \"client." + user + "\", "
+ "\"caps\": [\"mon\", \"allow r\", \"osd\", \"allow rwx\", "
+ "\"mds\", \"allow r, allow rw path=" + good + "\""
+ "], \"format\": \"json\"}", &key));
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, user.c_str()));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "key", key.c_str()));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ // allowed
+ ASSERT_GE(ceph_mkdir(cmount, string(good + "/x").c_str(), 0755), 0);
+ ASSERT_GE(ceph_rmdir(cmount, string(good + "/p").c_str()), 0);
+ ASSERT_GE(ceph_unlink(cmount, string(good + "/q").c_str()), 0);
+ fd = ceph_open(cmount, string(good + "/y").c_str(), O_CREAT|O_WRONLY, 0755);
+ ASSERT_GE(fd, 0);
+ ceph_write(cmount, fd, "bar", 3, 0);
+ ceph_close(cmount, fd);
+ ASSERT_GE(ceph_unlink(cmount, string(good + "/y").c_str()), 0);
+ ASSERT_GE(ceph_rmdir(cmount, string(good + "/x").c_str()), 0);
+
+ fd = ceph_open(cmount, string(bad + "/z").c_str(), O_RDONLY, 0644);
+ ASSERT_GE(fd, 0);
+ ceph_close(cmount, fd);
+
+ // not allowed
+ ASSERT_LT(ceph_mkdir(cmount, string(bad + "/x").c_str(), 0755), 0);
+ ASSERT_LT(ceph_rmdir(cmount, string(bad + "/p").c_str()), 0);
+ ASSERT_LT(ceph_unlink(cmount, string(bad + "/q").c_str()), 0);
+ fd = ceph_open(cmount, string(bad + "/y").c_str(), O_CREAT|O_WRONLY, 0755);
+ ASSERT_LT(fd, 0);
+
+ // unlink open file
+ fd = ceph_open(cmount, string(good + "/unlinkme").c_str(), O_CREAT|O_WRONLY, 0755);
+ ceph_unlink(cmount, string(good + "/unlinkme").c_str());
+ ASSERT_GE(ceph_write(cmount, fd, "foo", 3, 0), 0);
+ ASSERT_GE(ceph_fchmod(cmount, fd, 0777), 0);
+ ASSERT_GE(ceph_ftruncate(cmount, fd, 0), 0);
+ ASSERT_GE(ceph_fsetxattr(cmount, fd, "user.any", "bar", 3, 0), 0);
+ ceph_close(cmount, fd);
+
+ // rename open file
+ fd = ceph_open(cmount, string(good + "/renameme").c_str(), O_CREAT|O_WRONLY, 0755);
+ ASSERT_EQ(ceph_rename(admin, string(good + "/renameme").c_str(),
+ string(bad + "/asdf").c_str()), 0);
+ ASSERT_GE(ceph_write(cmount, fd, "foo", 3, 0), 0);
+ ASSERT_GE(ceph_fchmod(cmount, fd, 0777), -CEPHFS_EACCES);
+ ASSERT_GE(ceph_ftruncate(cmount, fd, 0), -CEPHFS_EACCES);
+ ASSERT_GE(ceph_fsetxattr(cmount, fd, "user.any", "bar", 3, 0), -CEPHFS_EACCES);
+ ceph_close(cmount, fd);
+
+ ceph_shutdown(cmount);
+ ASSERT_EQ(0, ceph_unlink(admin, string(bad + "/q").c_str()));
+ ASSERT_EQ(0, ceph_unlink(admin, string(bad + "/z").c_str()));
+ ASSERT_EQ(0, ceph_rmdir(admin, string(bad + "/p").c_str()));
+ ASSERT_EQ(0, ceph_unlink(admin, string(bad + "/asdf").c_str()));
+ ASSERT_EQ(0, ceph_rmdir(admin, good.c_str()));
+ ASSERT_EQ(0, ceph_rmdir(admin, bad.c_str()));
+ ceph_shutdown(admin);
+}
+
+TEST(AccessTest, ReadOnly) {
+ string dir = get_unique_dir();
+ string dir2 = get_unique_dir();
+ string user = "libcephfs_readonly_test." + stringify(rand());
+ struct ceph_mount_info *admin;
+ ASSERT_EQ(0, ceph_create(&admin, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(admin, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(admin, NULL));
+ ASSERT_EQ(0, ceph_mount(admin, "/"));
+ ASSERT_EQ(0, ceph_mkdir(admin, dir.c_str(), 0755));
+ int fd = ceph_open(admin, string(dir + "/out").c_str(), O_CREAT|O_WRONLY, 0755);
+ ceph_write(admin, fd, "foo", 3, 0);
+ ceph_close(admin,fd);
+
+ string key;
+ ASSERT_EQ(0, do_mon_command(
+ "{\"prefix\": \"auth get-or-create\", \"entity\": \"client." + user + "\", "
+ "\"caps\": [\"mon\", \"allow r\", \"osd\", \"allow rw\", "
+ "\"mds\", \"allow r\""
+ "], \"format\": \"json\"}", &key));
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, user.c_str()));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "key", key.c_str()));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ // allowed
+ fd = ceph_open(cmount, string(dir + "/out").c_str(), O_RDONLY, 0644);
+ ASSERT_GE(fd, 0);
+ ceph_close(cmount,fd);
+
+ // not allowed
+ fd = ceph_open(cmount, string(dir + "/bar").c_str(), O_CREAT|O_WRONLY, 0755);
+ ASSERT_LT(fd, 0);
+ ASSERT_LT(ceph_mkdir(cmount, dir2.c_str(), 0755), 0);
+
+ ceph_shutdown(cmount);
+ ASSERT_EQ(0, ceph_unlink(admin, string(dir + "/out").c_str()));
+ ASSERT_EQ(0, ceph_rmdir(admin, dir.c_str()));
+ ceph_shutdown(admin);
+}
+
+TEST(AccessTest, User) {
+ string dir = get_unique_dir();
+ string user = "libcephfs_user_test." + stringify(rand());
+
+ // admin mount to set up test
+ struct ceph_mount_info *admin;
+ ASSERT_EQ(0, ceph_create(&admin, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(admin, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(admin, NULL));
+ ASSERT_EQ(0, ceph_conf_set(admin, "client_permissions", "0"));
+ ASSERT_EQ(0, ceph_mount(admin, "/"));
+ ASSERT_EQ(0, ceph_mkdir(admin, dir.c_str(), 0755));
+
+ // create access key
+ string key;
+ ASSERT_EQ(0, do_mon_command(
+ "{\"prefix\": \"auth get-or-create\", \"entity\": \"client." + user + "\", "
+ "\"caps\": [\"mon\", \"allow *\", \"osd\", \"allow rw\", "
+ "\"mds\", \"allow rw uid=123 gids=456,789\""
+ "], \"format\": \"json\"}", &key));
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, user.c_str()));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "key", key.c_str()));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_init(cmount));
+
+ UserPerm *perms = ceph_userperm_new(123, 456, 0, NULL);
+ ASSERT_NE(nullptr, perms);
+ ASSERT_EQ(0, ceph_mount_perms_set(cmount, perms));
+ ceph_userperm_destroy(perms);
+
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "0"));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ // user bits
+ ASSERT_EQ(0, ceph_chmod(admin, dir.c_str(), 0700));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 123, 456));
+ ASSERT_EQ(0, ceph_mkdir(cmount, string(dir + "/u1").c_str(), 0755));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 1, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mkdir(cmount, string(dir + "/no").c_str(), 0755));
+
+ // group bits
+ ASSERT_EQ(0, ceph_chmod(admin, dir.c_str(), 0770));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 1, 456));
+ ASSERT_EQ(0, ceph_mkdir(cmount, string(dir + "/u2").c_str(), 0755));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 1, 2));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mkdir(cmount, string(dir + "/no").c_str(), 0755));
+
+ // user overrides group
+ ASSERT_EQ(0, ceph_chmod(admin, dir.c_str(), 0470));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 123, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mkdir(cmount, string(dir + "/no").c_str(), 0755));
+
+ // other
+ ASSERT_EQ(0, ceph_chmod(admin, dir.c_str(), 0777));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 1, 1));
+ ASSERT_EQ(0, ceph_mkdir(cmount, string(dir + "/u3").c_str(), 0755));
+ ASSERT_EQ(0, ceph_chmod(admin, dir.c_str(), 0770));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mkdir(cmount, string(dir + "/no").c_str(), 0755));
+
+ // user and group overrides other
+ ASSERT_EQ(0, ceph_chmod(admin, dir.c_str(), 07));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 1, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mkdir(cmount, string(dir + "/no").c_str(), 0755));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 123, 1));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mkdir(cmount, string(dir + "/no").c_str(), 0755));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 123, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_mkdir(cmount, string(dir + "/no").c_str(), 0755));
+
+ // chown and chgrp
+ ASSERT_EQ(0, ceph_chmod(admin, dir.c_str(), 0700));
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 123, 456));
+ // FIXME: Re-enable these 789 tests once we can set multiple GIDs via libcephfs/config
+ // ASSERT_EQ(0, ceph_chown(cmount, dir.c_str(), 123, 789));
+ ASSERT_EQ(0, ceph_chown(cmount, dir.c_str(), 123, 456));
+ // ASSERT_EQ(0, ceph_chown(cmount, dir.c_str(), -1, 789));
+ ASSERT_EQ(0, ceph_chown(cmount, dir.c_str(), -1, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), 123, 1));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), 1, 456));
+
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 1, 1));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), 123, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), 123, -1));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), -1, 456));
+
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 1, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), 123, 456));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), 123, -1));
+ ASSERT_EQ(-CEPHFS_EACCES, ceph_chown(cmount, dir.c_str(), -1, 456));
+
+ ASSERT_EQ(0, ceph_chown(admin, dir.c_str(), 123, 1));
+ ASSERT_EQ(0, ceph_chown(cmount, dir.c_str(), -1, 456));
+ // ASSERT_EQ(0, ceph_chown(cmount, dir.c_str(), 123, 789));
+
+ ceph_shutdown(cmount);
+
+ // clean up
+ ASSERT_EQ(0, ceph_rmdir(admin, string(dir + "/u1").c_str()));
+ ASSERT_EQ(0, ceph_rmdir(admin, string(dir + "/u2").c_str()));
+ ASSERT_EQ(0, ceph_rmdir(admin, string(dir + "/u3").c_str()));
+ ASSERT_EQ(0, ceph_rmdir(admin, dir.c_str()));
+ ceph_shutdown(admin);
+}
+
+static int update_root_mode()
+{
+ struct ceph_mount_info *admin;
+ int r = ceph_create(&admin, NULL);
+ if (r < 0)
+ return r;
+ ceph_conf_read_file(admin, NULL);
+ ceph_conf_parse_env(admin, NULL);
+ ceph_conf_set(admin, "client_permissions", "false");
+ r = ceph_mount(admin, "/");
+ if (r < 0)
+ goto out;
+ r = ceph_chmod(admin, "/", 0777);
+out:
+ ceph_shutdown(admin);
+ return r;
+}
+
+
+int main(int argc, char **argv)
+{
+ int r = update_root_mode();
+ if (r < 0)
+ exit(1);
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ srand(getpid());
+
+ r = rados_create(&cluster, NULL);
+ if (r < 0)
+ exit(1);
+
+ r = rados_conf_read_file(cluster, NULL);
+ if (r < 0)
+ exit(1);
+
+ rados_conf_parse_env(cluster, NULL);
+ r = rados_connect(cluster);
+ if (r < 0)
+ exit(1);
+
+ r = RUN_ALL_TESTS();
+
+ rados_shutdown(cluster);
+
+ return r;
+}
diff --git a/src/test/libcephfs/acl.cc b/src/test/libcephfs/acl.cc
new file mode 100644
index 000000000..e263ef2fb
--- /dev/null
+++ b/src/test/libcephfs/acl.cc
@@ -0,0 +1,367 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "include/types.h"
+#include "gtest/gtest.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include "include/ceph_fs.h"
+#include "client/posix_acl.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __linux__
+#include <sys/xattr.h>
+#endif
+
+static size_t acl_ea_size(int count)
+{
+ return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry);
+}
+
+static int acl_ea_count(size_t size)
+{
+ if (size < sizeof(acl_ea_header))
+ return -1;
+ size -= sizeof(acl_ea_header);
+ if (size % sizeof(acl_ea_entry))
+ return -1;
+ return size / sizeof(acl_ea_entry);
+}
+
+static int check_acl_and_mode(const void *buf, size_t size, mode_t mode)
+{
+ const acl_ea_entry *group_entry = NULL, *mask_entry = NULL;
+ const acl_ea_header *header = reinterpret_cast<const acl_ea_header*>(buf);
+ const acl_ea_entry *entry = header->a_entries;
+ int count = (size - sizeof(*header)) / sizeof(*entry);
+ for (int i = 0; i < count; ++i) {
+ __u16 tag = entry->e_tag;
+ __u16 perm = entry->e_perm;
+ switch(tag) {
+ case ACL_USER_OBJ:
+ if (perm != ((mode >> 6) & 7))
+ return -CEPHFS_EINVAL;
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ break;
+ case ACL_GROUP_OBJ:
+ group_entry = entry;
+ break;
+ case ACL_OTHER:
+ if (perm != (mode & 7))
+ return -CEPHFS_EINVAL;
+ break;
+ case ACL_MASK:
+ mask_entry = entry;
+ break;
+ default:
+ return -CEPHFS_EIO;
+ }
+ ++entry;
+ }
+ if (mask_entry) {
+ __u16 perm = mask_entry->e_perm;
+ if (perm != ((mode >> 3) & 7))
+ return -CEPHFS_EINVAL;
+ } else {
+ if (!group_entry)
+ return -CEPHFS_EIO;
+ __u16 perm = group_entry->e_perm;
+ if (perm != ((mode >> 3) & 7))
+ return -CEPHFS_EINVAL;
+ }
+ return 0;
+}
+
+static int generate_test_acl(void *buf, size_t size, mode_t mode)
+{
+ if (acl_ea_count(size) != 5)
+ return -1;
+ acl_ea_header *header = reinterpret_cast<acl_ea_header*>(buf);
+ header->a_version = (__u32)ACL_EA_VERSION;
+ acl_ea_entry *entry = header->a_entries;
+ entry->e_tag = ACL_USER_OBJ;
+ entry->e_perm = (mode >> 6) & 7;
+ ++entry;
+ entry->e_tag = ACL_USER;
+ entry->e_perm = 7;
+ entry->e_id = getuid();
+ ++entry;
+ entry->e_tag = ACL_GROUP_OBJ;
+ entry->e_perm = (mode >> 3) & 7;
+ ++entry;
+ entry->e_tag = ACL_MASK;
+ entry->e_perm = 7;
+ ++entry;
+ entry->e_tag = ACL_OTHER;
+ entry->e_perm = mode & 7;
+ return 0;
+}
+
+static int generate_empty_acl(void *buf, size_t size, mode_t mode)
+{
+
+ if (acl_ea_count(size) != 3)
+ return -1;
+ acl_ea_header *header = reinterpret_cast<acl_ea_header*>(buf);
+ header->a_version = (__u32)ACL_EA_VERSION;
+ acl_ea_entry *entry = header->a_entries;
+ entry->e_tag = ACL_USER_OBJ;
+ entry->e_perm = (mode >> 6) & 7;
+ ++entry;
+ entry->e_tag = ACL_GROUP_OBJ;
+ entry->e_perm = (mode >> 3) & 7;
+ ++entry;
+ entry->e_tag = ACL_OTHER;
+ entry->e_perm = mode & 7;
+ return 0;
+}
+
+TEST(ACL, SetACL) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl"));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "0"));
+
+ char test_file[256];
+ sprintf(test_file, "file1_setacl_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600);
+ ASSERT_GT(fd, 0);
+ // change ownership to nobody -- we assume nobody exists and id is always 65534
+ ASSERT_EQ(ceph_fchown(cmount, fd, 65534, 65534), 0);
+
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "1"));
+ // "nobody" will be ignored on Windows
+ #ifndef _WIN32
+ ASSERT_EQ(ceph_open(cmount, test_file, O_RDWR, 0), -CEPHFS_EACCES);
+ #endif
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "0"));
+
+ size_t acl_buf_size = acl_ea_size(5);
+ void *acl_buf = malloc(acl_buf_size);
+ ASSERT_EQ(generate_test_acl(acl_buf, acl_buf_size, 0750), 0);
+
+ // can't set default acl for non-directory
+ ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_DEFAULT, acl_buf, acl_buf_size, 0), -CEPHFS_EACCES);
+ ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0);
+
+ int tmpfd = ceph_open(cmount, test_file, O_RDWR, 0);
+ ASSERT_GT(tmpfd, 0);
+ ceph_close(cmount, tmpfd);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0);
+ // mode was modified according to ACL
+ ASSERT_EQ(stx.stx_mode & 0777u, 0770u);
+ ASSERT_EQ(check_acl_and_mode(acl_buf, acl_buf_size, stx.stx_mode), 0);
+
+ acl_buf_size = acl_ea_size(3);
+ // setting ACL that is equivalent to file mode
+ ASSERT_EQ(generate_empty_acl(acl_buf, acl_buf_size, 0600), 0);
+ ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0);
+ // ACL was deleted
+ ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, NULL, 0), -CEPHFS_ENODATA);
+
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0);
+ // mode was modified according to ACL
+ ASSERT_EQ(stx.stx_mode & 0777u, 0600u);
+
+ free(acl_buf);
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(ACL, Chmod) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl"));
+
+ char test_file[256];
+ sprintf(test_file, "file1_acl_chmod_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600);
+ ASSERT_GT(fd, 0);
+
+ int acl_buf_size = acl_ea_size(5);
+ void *acl_buf = malloc(acl_buf_size);
+ ASSERT_EQ(generate_test_acl(acl_buf, acl_buf_size, 0775), 0);
+ ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0);
+ // mode was updated according to ACL
+ ASSERT_EQ(stx.stx_mode & 0777u, 0775u);
+
+ // change mode
+ ASSERT_EQ(ceph_fchmod(cmount, fd, 0640), 0);
+
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0);
+ ASSERT_EQ(stx.stx_mode & 0777u, 0640u);
+
+ // ACL was updated according to mode
+ ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size), acl_buf_size);
+ ASSERT_EQ(check_acl_and_mode(acl_buf, acl_buf_size, stx.stx_mode), 0);
+
+ free(acl_buf);
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(ACL, DefaultACL) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl"));
+
+ int acl_buf_size = acl_ea_size(5);
+ void *acl1_buf = malloc(acl_buf_size);
+ void *acl2_buf = malloc(acl_buf_size);
+
+ ASSERT_EQ(generate_test_acl(acl1_buf, acl_buf_size, 0750), 0);
+
+ char test_dir1[256];
+ sprintf(test_dir1, "dir1_acl_default_%d", getpid());
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0750), 0);
+
+ // set default acl
+ ASSERT_EQ(ceph_setxattr(cmount, test_dir1, ACL_EA_DEFAULT, acl1_buf, acl_buf_size, 0), 0);
+
+ char test_dir2[262];
+ sprintf(test_dir2, "%s/dir2", test_dir1);
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0755), 0);
+
+ // inherit default acl
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_DEFAULT, acl2_buf, acl_buf_size), acl_buf_size);
+ ASSERT_EQ(memcmp(acl1_buf, acl2_buf, acl_buf_size), 0);
+
+ // mode and ACL are updated
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size);
+ {
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, test_dir2, &stx, CEPH_STATX_MODE, 0), 0);
+ // other bits of mode &= acl other perm
+ ASSERT_EQ(stx.stx_mode & 0777u, 0750u);
+ ASSERT_EQ(check_acl_and_mode(acl2_buf, acl_buf_size, stx.stx_mode), 0);
+ }
+
+ char test_file1[262];
+ sprintf(test_file1, "%s/file1", test_dir1);
+ int fd = ceph_open(cmount, test_file1, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ // no default acl
+ ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_DEFAULT, NULL, 0), -CEPHFS_ENODATA);
+
+ // mode and ACL are updated
+ ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size);
+ {
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, test_file1, &stx, CEPH_STATX_MODE, 0), 0);
+ // other bits of mode &= acl other perm
+ ASSERT_EQ(stx.stx_mode & 0777u, 0660u);
+ ASSERT_EQ(check_acl_and_mode(acl2_buf, acl_buf_size, stx.stx_mode), 0);
+ }
+
+ free(acl1_buf);
+ free(acl2_buf);
+ ASSERT_EQ(ceph_unlink(cmount, test_file1), 0);
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir2), 0);
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir1), 0);
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(ACL, Disabled) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", ""));
+
+ size_t acl_buf_size = acl_ea_size(3);
+ void *acl_buf = malloc(acl_buf_size);
+ ASSERT_EQ(generate_empty_acl(acl_buf, acl_buf_size, 0755), 0);
+
+ char test_dir[256];
+ sprintf(test_dir, "dir1_acl_disabled_%d", getpid());
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir, 0750), 0);
+
+ ASSERT_EQ(ceph_setxattr(cmount, test_dir, ACL_EA_DEFAULT, acl_buf, acl_buf_size, 0), -CEPHFS_EOPNOTSUPP);
+ ASSERT_EQ(ceph_setxattr(cmount, test_dir, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), -CEPHFS_EOPNOTSUPP);
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir, ACL_EA_DEFAULT, acl_buf, acl_buf_size), -CEPHFS_EOPNOTSUPP);
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir, ACL_EA_ACCESS, acl_buf, acl_buf_size), -CEPHFS_EOPNOTSUPP);
+
+ free(acl_buf);
+ ceph_shutdown(cmount);
+}
+
+TEST(ACL, SnapdirACL) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl"));
+
+ int acl_buf_size = acl_ea_size(5);
+ void *acl1_buf = malloc(acl_buf_size);
+ void *acl2_buf = malloc(acl_buf_size);
+ void *acl3_buf = malloc(acl_buf_size);
+
+ ASSERT_EQ(generate_test_acl(acl1_buf, acl_buf_size, 0750), 0);
+
+ char test_dir1[256];
+ sprintf(test_dir1, "dir1_acl_default_%d", getpid());
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0750), 0);
+
+ // set default acl
+ ASSERT_EQ(ceph_setxattr(cmount, test_dir1, ACL_EA_DEFAULT, acl1_buf, acl_buf_size, 0), 0);
+
+ char test_dir2[262];
+ sprintf(test_dir2, "%s/dir2", test_dir1);
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0755), 0);
+
+ // inherit default acl
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_DEFAULT, acl2_buf, acl_buf_size), acl_buf_size);
+ ASSERT_EQ(memcmp(acl1_buf, acl2_buf, acl_buf_size), 0);
+
+ char test_dir2_snapdir[512];
+ sprintf(test_dir2_snapdir, "%s/dir2/.snap", test_dir1);
+
+ // inherit default acl
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir2_snapdir, ACL_EA_DEFAULT, acl3_buf, acl_buf_size), acl_buf_size);
+ ASSERT_EQ(memcmp(acl2_buf, acl3_buf, acl_buf_size), 0);
+
+ memset(acl2_buf, 0, acl_buf_size);
+ memset(acl3_buf, 0, acl_buf_size);
+
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size);
+ ASSERT_EQ(ceph_getxattr(cmount, test_dir2_snapdir, ACL_EA_ACCESS, acl3_buf, acl_buf_size), acl_buf_size);
+ ASSERT_EQ(memcmp(acl2_buf, acl3_buf, acl_buf_size), 0);
+
+ free(acl1_buf);
+ free(acl2_buf);
+ free(acl3_buf);
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir2), 0);
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir1), 0);
+ ceph_shutdown(cmount);
+}
diff --git a/src/test/libcephfs/caps.cc b/src/test/libcephfs/caps.cc
new file mode 100644
index 000000000..9141f9a6b
--- /dev/null
+++ b/src/test/libcephfs/caps.cc
@@ -0,0 +1,95 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "include/int_types.h"
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/ceph_fs.h"
+#include "include/cephfs/libcephfs.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#ifdef __linux__
+#include <sys/xattr.h>
+#endif
+#include <signal.h>
+
+TEST(Caps, ReadZero) {
+
+ int mypid = getpid();
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ int i = 0;
+ for(; i < 30; ++i) {
+
+ char c_path[1024];
+ sprintf(c_path, "/caps_rzfile_%d_%d", mypid, i);
+ int fd = ceph_open(cmount, c_path, O_CREAT|O_TRUNC|O_WRONLY, 0644);
+ ASSERT_LT(0, fd);
+
+ int expect = CEPH_CAP_FILE_EXCL | CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER;
+ int caps = ceph_debug_get_fd_caps(cmount, fd);
+
+ ASSERT_EQ(expect, caps & expect);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ caps = ceph_debug_get_file_caps(cmount, c_path);
+ ASSERT_EQ(expect, caps & expect);
+
+ char cw_path[1024];
+ sprintf(cw_path, "/caps_wzfile_%d_%d", mypid, i);
+ int wfd = ceph_open(cmount, cw_path, O_CREAT|O_TRUNC|O_WRONLY, 0644);
+ ASSERT_LT(0, wfd);
+
+ char wbuf[4096];
+ ASSERT_EQ(4096, ceph_write(cmount, wfd, wbuf, 4096, 0));
+
+ ASSERT_EQ(0, ceph_close(cmount, wfd));
+
+ struct ceph_statx stx;
+ ASSERT_EQ(0, ceph_statx(cmount, c_path, &stx, CEPH_STATX_MTIME, 0));
+
+ caps = ceph_debug_get_file_caps(cmount, c_path);
+ ASSERT_EQ(expect, caps & expect);
+ }
+
+ ASSERT_EQ(0, ceph_conf_set(cmount, "client_debug_inject_tick_delay", "20"));
+
+ for(i = 0; i < 30; ++i) {
+
+ char c_path[1024];
+ sprintf(c_path, "/caps_rzfile_%d_%d", mypid, i);
+
+ int fd = ceph_open(cmount, c_path, O_RDONLY, 0);
+ ASSERT_LT(0, fd);
+ char buf[256];
+
+ int expect = CEPH_CAP_FILE_RD | CEPH_STAT_CAP_SIZE | CEPH_CAP_FILE_CACHE;
+ int caps = ceph_debug_get_fd_caps(cmount, fd);
+ ASSERT_EQ(expect, caps & expect);
+ ASSERT_EQ(0, ceph_read(cmount, fd, buf, 256, 0));
+
+ caps = ceph_debug_get_fd_caps(cmount, fd);
+ ASSERT_EQ(expect, caps & expect);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ }
+ ceph_shutdown(cmount);
+}
diff --git a/src/test/libcephfs/ceph_pthread_self.h b/src/test/libcephfs/ceph_pthread_self.h
new file mode 100644
index 000000000..9e3cdfa99
--- /dev/null
+++ b/src/test/libcephfs/ceph_pthread_self.h
@@ -0,0 +1,31 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBCEPHFS_PTHREAD_SELF
+#define CEPH_TEST_LIBCEPHFS_PTHREAD_SELF
+
+#include <pthread.h>
+
+#include <type_traits>
+
+/*
+ * There is a difference between libc shipped with FreeBSD and
+ * glibc shipped with GNU/Linux for the return type of pthread_self().
+ *
+ * Introduced a conversion function in include/compat.h
+ * (uint64_t)ceph_pthread_self()
+ *
+ * libc returns an opague pthread_t that is not default convertable
+ * to a uint64_t, which is what gtest expects.
+ * And tests using gtest will not compile because of this difference.
+ *
+ */
+static uint64_t ceph_pthread_self() {
+ auto me = pthread_self();
+ static_assert(std::is_convertible_v<decltype(me), uint64_t> ||
+ std::is_pointer_v<decltype(me)>,
+ "we need to use pthread_self() for the owner parameter");
+ return static_cast<uint64_t>(me);
+}
+
+#endif
diff --git a/src/test/libcephfs/deleg.cc b/src/test/libcephfs/deleg.cc
new file mode 100644
index 000000000..061e13763
--- /dev/null
+++ b/src/test/libcephfs/deleg.cc
@@ -0,0 +1,401 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Tests for Ceph delegation handling
+ *
+ * (c) 2017, Jeff Layton <jlayton@redhat.com>
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include "include/stat.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/uio.h>
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#endif
+
+#include <map>
+#include <vector>
+#include <thread>
+#include <atomic>
+
+#include "include/ceph_assert.h"
+
+/* in ms -- 1 minute */
+#define MAX_WAIT (60 * 1000)
+
+static void wait_for_atomic_bool(std::atomic_bool &recalled)
+{
+ int i = 0;
+
+ while (!recalled.load()) {
+ ASSERT_LT(i++, MAX_WAIT);
+ usleep(1000);
+ }
+}
+
+static int ceph_ll_delegation_wait(struct ceph_mount_info *cmount, Fh *fh,
+ unsigned cmd, ceph_deleg_cb_t cb, void *priv)
+{
+ int ret, retry = 0;
+
+ /* Wait 10s at most */
+ do {
+ ret = ceph_ll_delegation(cmount, fh, cmd, cb, priv);
+ usleep(10000);
+ } while (ret == -CEPHFS_EAGAIN && retry++ < 1000);
+
+ return ret;
+}
+
+static int set_default_deleg_timeout(struct ceph_mount_info *cmount)
+{
+ uint32_t session_timeout = ceph_get_cap_return_timeout(cmount);
+ return ceph_set_deleg_timeout(cmount, session_timeout - 1);
+}
+
+static void dummy_deleg_cb(Fh *fh, void *priv)
+{
+ std::atomic_bool *recalled = (std::atomic_bool *)priv;
+ recalled->store(true);
+}
+
+static void open_breaker_func(struct ceph_mount_info *cmount, const char *filename, int flags, std::atomic_bool *opened)
+{
+ bool do_shutdown = false;
+
+ if (!cmount) {
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0);
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+ ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
+ do_shutdown = true;
+ }
+
+ Inode *root, *file;
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+
+ Fh *fh;
+ struct ceph_statx stx;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ ASSERT_EQ(ceph_ll_lookup(cmount, root, filename, &file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
+ int ret, i = 0;
+ for (;;) {
+ ASSERT_EQ(ceph_ll_getattr(cmount, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
+ ret = ceph_ll_open(cmount, file, flags, &fh, perms);
+ if (ret != -CEPHFS_EAGAIN)
+ break;
+ ASSERT_LT(i++, MAX_WAIT);
+ usleep(1000);
+ }
+ ASSERT_EQ(ret, 0);
+ opened->store(true);
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+
+ if (do_shutdown)
+ ceph_shutdown(cmount);
+}
+
+enum {
+ DelegTestLink,
+ DelegTestRename,
+ DelegTestUnlink
+};
+
+static void namespace_breaker_func(struct ceph_mount_info *cmount, int cmd, const char *oldname, const char *newname)
+{
+ bool do_shutdown = false;
+
+ if (!cmount) {
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+ ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
+ do_shutdown = true;
+ }
+
+ Inode *root, *file = nullptr;
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+
+ struct ceph_statx stx;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ int ret, i = 0;
+ for (;;) {
+ switch (cmd) {
+ case DelegTestRename:
+ ret = ceph_ll_rename(cmount, root, oldname, root, newname, perms);
+ break;
+ case DelegTestLink:
+ if (!file) {
+ ASSERT_EQ(ceph_ll_lookup(cmount, root, oldname, &file, &stx, 0, 0, perms), 0);
+ }
+ ret = ceph_ll_link(cmount, file, root, newname, perms);
+ break;
+ case DelegTestUnlink:
+ ret = ceph_ll_unlink(cmount, root, oldname, perms);
+ break;
+ default:
+ // Bad command
+ ceph_abort();
+ }
+ if (ret != -CEPHFS_EAGAIN)
+ break;
+ ASSERT_LT(i++, MAX_WAIT);
+ usleep(1000);
+ }
+ ASSERT_EQ(ret, 0);
+
+ if (do_shutdown)
+ ceph_shutdown(cmount);
+}
+
+static void simple_deleg_test(struct ceph_mount_info *cmount, struct ceph_mount_info *tcmount)
+{
+ Inode *root, *file;
+
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+
+ char filename[32];
+
+ Fh *fh;
+ struct ceph_statx stx;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ std::atomic_bool recalled(false);
+ std::atomic_bool opened(false);
+
+ // ensure r/w open breaks a r/w delegation
+ sprintf(filename, "deleg.rwrw.%x", getpid());
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
+ std::thread breaker1(open_breaker_func, tcmount, filename, O_RDWR, &opened);
+
+ wait_for_atomic_bool(recalled);
+ ASSERT_EQ(opened.load(), false);
+ ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
+ breaker1.join();
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
+
+ // ensure r/o open breaks a r/w delegation
+ recalled.store(false);
+ opened.store(false);
+ sprintf(filename, "deleg.rorw.%x", getpid());
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
+ std::thread breaker2(open_breaker_func, tcmount, filename, O_RDONLY, &opened);
+ wait_for_atomic_bool(recalled);
+ ASSERT_EQ(opened.load(), false);
+ ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
+ breaker2.join();
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
+
+ // ensure r/o open does not break a r/o delegation
+ sprintf(filename, "deleg.rwro.%x", getpid());
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDONLY|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+ recalled.store(false);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount, fh, CEPH_DELEGATION_RD, dummy_deleg_cb, &recalled), 0);
+ std::thread breaker3(open_breaker_func, tcmount, filename, O_RDONLY, &opened);
+ breaker3.join();
+ ASSERT_EQ(recalled.load(), false);
+
+ // ensure that r/w open breaks r/o delegation
+ opened.store(false);
+ std::thread breaker4(open_breaker_func, tcmount, filename, O_WRONLY, &opened);
+ wait_for_atomic_bool(recalled);
+ usleep(1000);
+ ASSERT_EQ(opened.load(), false);
+ ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
+ breaker4.join();
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
+
+ // ensure hardlinking breaks a r/w delegation
+ recalled.store(false);
+ char newname[32];
+ sprintf(filename, "deleg.old.%x", getpid());
+ sprintf(newname, "deleg.new.%x", getpid());
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
+ std::thread breaker5(namespace_breaker_func, tcmount, DelegTestLink, filename, newname);
+ wait_for_atomic_bool(recalled);
+ ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
+ breaker5.join();
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
+ ASSERT_EQ(ceph_ll_unlink(cmount, root, newname, perms), 0);
+
+ // ensure renaming breaks a r/w delegation
+ recalled.store(false);
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
+ std::thread breaker6(namespace_breaker_func, tcmount, DelegTestRename, filename, newname);
+ wait_for_atomic_bool(recalled);
+ ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
+ breaker6.join();
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ASSERT_EQ(ceph_ll_unlink(cmount, root, newname, perms), 0);
+
+ // ensure unlinking breaks a r/w delegation
+ recalled.store(false);
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
+ std::thread breaker7(namespace_breaker_func, tcmount, DelegTestUnlink, filename, nullptr);
+ wait_for_atomic_bool(recalled);
+ ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
+ breaker7.join();
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+}
+
+TEST(LibCephFS, DelegMultiClient) {
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+ ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
+
+ simple_deleg_test(cmount, nullptr);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DelegSingleClient) {
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+ ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
+
+ simple_deleg_test(cmount, cmount);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DelegTimeout) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+ // tweak timeout to run quickly, since we don't plan to return it anyway
+ ASSERT_EQ(ceph_set_deleg_timeout(cmount, 2), 0);
+
+ Inode *root, *file;
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+
+ char filename[32];
+ sprintf(filename, "delegtimeo%x", getpid());
+
+ Fh *fh;
+ struct ceph_statx stx;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+
+ /* Reopen read-only */
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ASSERT_EQ(ceph_ll_open(cmount, file, O_RDONLY, &fh, perms), 0);
+
+ std::atomic_bool recalled(false);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount, fh, CEPH_DELEGATION_RD, dummy_deleg_cb, &recalled), 0);
+ std::atomic_bool opened(false);
+ std::thread breaker1(open_breaker_func, nullptr, filename, O_RDWR, &opened);
+ breaker1.join();
+ ASSERT_EQ(recalled.load(), true);
+ ASSERT_EQ(ceph_ll_getattr(cmount, root, &stx, 0, 0, perms), -CEPHFS_ENOTCONN);
+ ceph_release(cmount);
+}
+
+TEST(LibCephFS, RecalledGetattr) {
+ struct ceph_mount_info *cmount1;
+ ASSERT_EQ(ceph_create(&cmount1, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount1, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount1, NULL));
+ ASSERT_EQ(ceph_mount(cmount1, "/"), 0);
+ ASSERT_EQ(set_default_deleg_timeout(cmount1), 0);
+
+ Inode *root, *file;
+ ASSERT_EQ(ceph_ll_lookup_root(cmount1, &root), 0);
+
+ char filename[32];
+ sprintf(filename, "recalledgetattr%x", getpid());
+
+ Fh *fh;
+ struct ceph_statx stx;
+ UserPerm *perms = ceph_mount_perms(cmount1);
+
+ ASSERT_EQ(ceph_ll_create(cmount1, root, filename, 0666,
+ O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_write(cmount1, fh, 0, sizeof(filename), filename),
+ static_cast<int>(sizeof(filename)));
+ ASSERT_EQ(ceph_ll_close(cmount1, fh), 0);
+
+ /* New mount for read delegation */
+ struct ceph_mount_info *cmount2;
+ ASSERT_EQ(ceph_create(&cmount2, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount2, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount2, NULL));
+ ASSERT_EQ(ceph_mount(cmount2, "/"), 0);
+ ASSERT_EQ(set_default_deleg_timeout(cmount2), 0);
+
+ ASSERT_EQ(ceph_ll_lookup_root(cmount2, &root), 0);
+ perms = ceph_mount_perms(cmount2);
+ ASSERT_EQ(ceph_ll_lookup(cmount2, root, filename, &file, &stx, 0, 0, perms), 0);
+
+ ASSERT_EQ(ceph_ll_open(cmount2, file, O_WRONLY, &fh, perms), 0);
+ ASSERT_EQ(ceph_ll_write(cmount2, fh, 0, sizeof(filename), filename),
+ static_cast<int>(sizeof(filename)));
+ ASSERT_EQ(ceph_ll_close(cmount2, fh), 0);
+
+ ASSERT_EQ(ceph_ll_open(cmount2, file, O_RDONLY, &fh, perms), 0);
+
+ /* Break delegation */
+ std::atomic_bool recalled(false);
+ ASSERT_EQ(ceph_ll_delegation_wait(cmount2, fh, CEPH_DELEGATION_RD, dummy_deleg_cb, &recalled), 0);
+ ASSERT_EQ(ceph_ll_read(cmount2, fh, 0, sizeof(filename), filename),
+ static_cast<int>(sizeof(filename)));
+ ASSERT_EQ(ceph_ll_getattr(cmount2, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
+ std::atomic_bool opened(false);
+ std::thread breaker1(open_breaker_func, cmount1, filename, O_WRONLY, &opened);
+ int i = 0;
+ do {
+ ASSERT_EQ(ceph_ll_getattr(cmount2, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
+ ASSERT_LT(i++, MAX_WAIT);
+ usleep(1000);
+ } while (!recalled.load());
+ ASSERT_EQ(opened.load(), false);
+ ASSERT_EQ(ceph_ll_getattr(cmount2, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_delegation(cmount2, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, nullptr), 0);
+ breaker1.join();
+ ASSERT_EQ(ceph_ll_close(cmount2, fh), 0);
+ ceph_unmount(cmount2);
+ ceph_release(cmount2);
+ ceph_unmount(cmount1);
+ ceph_release(cmount1);
+}
diff --git a/src/test/libcephfs/flock.cc b/src/test/libcephfs/flock.cc
new file mode 100644
index 000000000..367483d07
--- /dev/null
+++ b/src/test/libcephfs/flock.cc
@@ -0,0 +1,654 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <pthread.h>
+#include "gtest/gtest.h"
+#ifndef GTEST_IS_THREADSAFE
+#error "!GTEST_IS_THREADSAFE"
+#endif
+
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <semaphore.h>
+#include <time.h>
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#elif __FreeBSD__
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+#include "include/ceph_assert.h"
+#include "ceph_pthread_self.h"
+
+// Startup common: create and mount ceph fs
+#define STARTUP_CEPH() do { \
+ ASSERT_EQ(0, ceph_create(&cmount, NULL)); \
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); \
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL)); \
+ ASSERT_EQ(0, ceph_mount(cmount, NULL)); \
+ } while(0)
+
+// Cleanup common: unmount and release ceph fs
+#define CLEANUP_CEPH() do { \
+ ASSERT_EQ(0, ceph_unmount(cmount)); \
+ ASSERT_EQ(0, ceph_release(cmount)); \
+ } while(0)
+
+static const mode_t fileMode = S_IRWXU | S_IRWXG | S_IRWXO;
+
+// Default wait time for normal and "slow" operations
+// (5" should be enough in case of network congestion)
+static const long waitMs = 10;
+static const long waitSlowMs = 5000;
+
+// Get the absolute struct timespec reference from now + 'ms' milliseconds
+static const struct timespec* abstime(struct timespec &ts, long ms) {
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
+ ceph_abort();
+ }
+ ts.tv_nsec += ms * 1000000;
+ ts.tv_sec += ts.tv_nsec / 1000000000;
+ ts.tv_nsec %= 1000000000;
+ return &ts;
+}
+
+/* Basic locking */
+TEST(LibCephFS, BasicLocking) {
+ struct ceph_mount_info *cmount = NULL;
+ STARTUP_CEPH();
+
+ char c_file[1024];
+ sprintf(c_file, "/flock_test_%d", getpid());
+ const int fd = ceph_open(cmount, c_file, O_RDWR | O_CREAT, fileMode);
+ ASSERT_GE(fd, 0);
+
+ // Lock exclusively twice
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, 42));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 43));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 44));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 42));
+
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 43));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 44));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 43));
+
+ // Lock shared three times
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH, 42));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH, 43));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH, 44));
+ // And then attempt to lock exclusively
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 45));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 42));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 45));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 44));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 45));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 43));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, 45));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, 42));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 45));
+
+ // Lock shared with upgrade to exclusive (POSIX)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH, 42));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, 42));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 42));
+
+ // Lock exclusive with downgrade to shared (POSIX)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, 42));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH, 42));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, 42));
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unlink(cmount, c_file));
+ CLEANUP_CEPH();
+}
+
+/* Locking in different threads */
+
+// Used by ConcurrentLocking test
+struct str_ConcurrentLocking {
+ const char *file;
+ struct ceph_mount_info *cmount; // !NULL if shared
+ sem_t sem[2];
+ sem_t semReply[2];
+ void sem_init(int pshared) {
+ ASSERT_EQ(0, ::sem_init(&sem[0], pshared, 0));
+ ASSERT_EQ(0, ::sem_init(&sem[1], pshared, 0));
+ ASSERT_EQ(0, ::sem_init(&semReply[0], pshared, 0));
+ ASSERT_EQ(0, ::sem_init(&semReply[1], pshared, 0));
+ }
+ void sem_destroy() {
+ ASSERT_EQ(0, ::sem_destroy(&sem[0]));
+ ASSERT_EQ(0, ::sem_destroy(&sem[1]));
+ ASSERT_EQ(0, ::sem_destroy(&semReply[0]));
+ ASSERT_EQ(0, ::sem_destroy(&semReply[1]));
+ }
+};
+
+// Wakeup main (for (N) steps)
+#define PING_MAIN(n) ASSERT_EQ(0, sem_post(&s.sem[n%2]))
+// Wait for main to wake us up (for (RN) steps)
+#define WAIT_MAIN(n) \
+ ASSERT_EQ(0, sem_timedwait(&s.semReply[n%2], abstime(ts, waitSlowMs)))
+
+// Wakeup worker (for (RN) steps)
+#define PING_WORKER(n) ASSERT_EQ(0, sem_post(&s.semReply[n%2]))
+// Wait for worker to wake us up (for (N) steps)
+#define WAIT_WORKER(n) \
+ ASSERT_EQ(0, sem_timedwait(&s.sem[n%2], abstime(ts, waitSlowMs)))
+// Worker shall not wake us up (for (N) steps)
+#define NOT_WAIT_WORKER(n) \
+ ASSERT_EQ(-1, sem_timedwait(&s.sem[n%2], abstime(ts, waitMs)))
+
+// Do twice an operation
+#define TWICE(EXPR) do { \
+ EXPR; \
+ EXPR; \
+ } while(0)
+
+/* Locking in different threads */
+
+// Used by ConcurrentLocking test
+static void thread_ConcurrentLocking(str_ConcurrentLocking& s) {
+ struct ceph_mount_info *const cmount = s.cmount;
+ struct timespec ts;
+
+ const int fd = ceph_open(cmount, s.file, O_RDWR | O_CREAT, fileMode);
+ ASSERT_GE(fd, 0);
+
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, ceph_pthread_self()));
+ PING_MAIN(1); // (1)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, ceph_pthread_self()));
+ PING_MAIN(2); // (2)
+
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+ PING_MAIN(3); // (3)
+
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH, ceph_pthread_self()));
+ PING_MAIN(4); // (4)
+
+ WAIT_MAIN(1); // (R1)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+ PING_MAIN(5); // (5)
+
+ WAIT_MAIN(2); // (R2)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, ceph_pthread_self()));
+ PING_MAIN(6); // (6)
+
+ WAIT_MAIN(3); // (R3)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+ PING_MAIN(7); // (7)
+}
+
+// Used by ConcurrentLocking test
+static void* thread_ConcurrentLocking_(void *arg) {
+ str_ConcurrentLocking *const s =
+ reinterpret_cast<str_ConcurrentLocking*>(arg);
+ thread_ConcurrentLocking(*s);
+ return NULL;
+}
+
+TEST(LibCephFS, ConcurrentLocking) {
+ const pid_t mypid = getpid();
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+
+ char c_file[1024];
+ sprintf(c_file, "/flock_test_%d", mypid);
+ const int fd = ceph_open(cmount, c_file, O_RDWR | O_CREAT, fileMode);
+ ASSERT_GE(fd, 0);
+
+ // Lock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, ceph_pthread_self()));
+
+ // Start locker thread
+ pthread_t thread;
+ struct timespec ts;
+ str_ConcurrentLocking s = { c_file, cmount };
+ s.sem_init(0);
+ ASSERT_EQ(0, pthread_create(&thread, NULL, thread_ConcurrentLocking_, &s));
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(1); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+
+ // Shall have lock
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(3); // (3)
+
+ // Wait for thread to share lock
+ WAIT_WORKER(4); // (4)
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, ceph_pthread_self()));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, ceph_pthread_self()));
+
+ // Wake up thread to unlock shared lock
+ PING_WORKER(1); // (R1)
+ WAIT_WORKER(5); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, ceph_pthread_self()));
+
+ // Wake up thread to lock shared lock
+ PING_WORKER(2); // (R2)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6); // (6)
+
+ // Release lock ; thread will get it
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+ WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, ceph_pthread_self()));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, ceph_pthread_self()));
+
+ // Wake up thread to unlock exclusive lock
+ PING_WORKER(3); // (R3)
+ WAIT_WORKER(7); // (7)
+
+ // We can lock it again
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, ceph_pthread_self()));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+
+ // Cleanup
+ void *retval = (void*) (uintptr_t) -1;
+ ASSERT_EQ(0, pthread_join(thread, &retval));
+ ASSERT_EQ(NULL, retval);
+ s.sem_destroy();
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unlink(cmount, c_file));
+ CLEANUP_CEPH();
+}
+
+TEST(LibCephFS, ThreesomeLocking) {
+ const pid_t mypid = getpid();
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+
+ char c_file[1024];
+ sprintf(c_file, "/flock_test_%d", mypid);
+ const int fd = ceph_open(cmount, c_file, O_RDWR | O_CREAT, fileMode);
+ ASSERT_GE(fd, 0);
+
+ // Lock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, ceph_pthread_self()));
+
+ // Start locker thread
+ pthread_t thread[2];
+ struct timespec ts;
+ str_ConcurrentLocking s = { c_file, cmount };
+ s.sem_init(0);
+ ASSERT_EQ(0, pthread_create(&thread[0], NULL, thread_ConcurrentLocking_, &s));
+ ASSERT_EQ(0, pthread_create(&thread[1], NULL, thread_ConcurrentLocking_, &s));
+ // Synchronization point with thread (failure: thread is dead)
+ TWICE(WAIT_WORKER(1)); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+
+ // Shall have lock
+ TWICE(// Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(3)); // (3)
+
+ // Wait for thread to share lock
+ TWICE(WAIT_WORKER(4)); // (4)
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, ceph_pthread_self()));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, ceph_pthread_self()));
+
+ // Wake up thread to unlock shared lock
+ TWICE(PING_WORKER(1); // (R1)
+ WAIT_WORKER(5)); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, ceph_pthread_self()));
+
+ TWICE( // Wake up thread to lock shared lock
+ PING_WORKER(2); // (R2)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6)); // (6)
+
+ // Release lock ; thread will get it
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+ TWICE(WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, ceph_pthread_self()));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, ceph_pthread_self()));
+
+ // Wake up thread to unlock exclusive lock
+ PING_WORKER(3); // (R3)
+ WAIT_WORKER(7); // (7)
+ );
+
+ // We can lock it again
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, ceph_pthread_self()));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, ceph_pthread_self()));
+
+ // Cleanup
+ void *retval = (void*) (uintptr_t) -1;
+ ASSERT_EQ(0, pthread_join(thread[0], &retval));
+ ASSERT_EQ(NULL, retval);
+ ASSERT_EQ(0, pthread_join(thread[1], &retval));
+ ASSERT_EQ(NULL, retval);
+ s.sem_destroy();
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unlink(cmount, c_file));
+ CLEANUP_CEPH();
+}
+
+/* Locking in different processes */
+
+#define PROCESS_SLOW_MS() \
+ static const long waitMs = 100; \
+ (void) waitMs
+
+// Used by ConcurrentLocking test
+static void process_ConcurrentLocking(str_ConcurrentLocking& s) {
+ const pid_t mypid = getpid();
+ PROCESS_SLOW_MS();
+
+ struct ceph_mount_info *cmount = NULL;
+ struct timespec ts;
+
+ STARTUP_CEPH();
+ s.cmount = cmount;
+
+ const int fd = ceph_open(cmount, s.file, O_RDWR | O_CREAT, fileMode);
+ ASSERT_GE(fd, 0);
+ WAIT_MAIN(1); // (R1)
+
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, mypid));
+ PING_MAIN(1); // (1)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, mypid));
+ PING_MAIN(2); // (2)
+
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+ PING_MAIN(3); // (3)
+
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH, mypid));
+ PING_MAIN(4); // (4)
+
+ WAIT_MAIN(2); // (R2)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+ PING_MAIN(5); // (5)
+
+ WAIT_MAIN(3); // (R3)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, mypid));
+ PING_MAIN(6); // (6)
+
+ WAIT_MAIN(4); // (R4)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+ PING_MAIN(7); // (7)
+
+ CLEANUP_CEPH();
+
+ s.sem_destroy();
+ exit(EXIT_SUCCESS);
+}
+
+#ifndef _WIN32
+// Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
+TEST(LibCephFS, DISABLED_InterProcessLocking) {
+ PROCESS_SLOW_MS();
+ // Process synchronization
+ char c_file[1024];
+ const pid_t mypid = getpid();
+ sprintf(c_file, "/flock_test_%d", mypid);
+
+ // Note: the semaphores MUST be on a shared memory segment
+ str_ConcurrentLocking *const shs =
+ reinterpret_cast<str_ConcurrentLocking*>
+ (mmap(0, sizeof(*shs), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
+ -1, 0));
+ str_ConcurrentLocking &s = *shs;
+ s.file = c_file;
+ s.sem_init(1);
+
+ // Start locker process
+ const pid_t pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0) {
+ process_ConcurrentLocking(s);
+ exit(EXIT_FAILURE);
+ }
+
+ struct timespec ts;
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+
+ const int fd = ceph_open(cmount, c_file, O_RDWR | O_CREAT, fileMode);
+ ASSERT_GE(fd, 0);
+
+ // Lock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, mypid));
+
+ // Synchronization point with process (failure: process is dead)
+ PING_WORKER(1); // (R1)
+ WAIT_WORKER(1); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+
+ // Shall have lock
+ // Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(3); // (3)
+
+ // Wait for process to share lock
+ WAIT_WORKER(4); // (4)
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, mypid));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, mypid));
+
+ // Wake up process to unlock shared lock
+ PING_WORKER(2); // (R2)
+ WAIT_WORKER(5); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, mypid));
+
+ // Wake up process to lock shared lock
+ PING_WORKER(3); // (R3)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6); // (6)
+
+ // Release lock ; process will get it
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+ WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, mypid));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK, ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, mypid));
+
+ // Wake up process to unlock exclusive lock
+ PING_WORKER(4); // (R4)
+ WAIT_WORKER(7); // (7)
+
+ // We can lock it again
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, mypid));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+
+ // Wait pid
+ int status;
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_EQ(EXIT_SUCCESS, status);
+
+ // Cleanup
+ s.sem_destroy();
+ ASSERT_EQ(0, munmap(shs, sizeof(*shs)));
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unlink(cmount, c_file));
+ CLEANUP_CEPH();
+}
+#endif
+
+#ifndef _WIN32
+// Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
+TEST(LibCephFS, DISABLED_ThreesomeInterProcessLocking) {
+ PROCESS_SLOW_MS();
+ // Process synchronization
+ char c_file[1024];
+ const pid_t mypid = getpid();
+ sprintf(c_file, "/flock_test_%d", mypid);
+
+ // Note: the semaphores MUST be on a shared memory segment
+ str_ConcurrentLocking *const shs =
+ reinterpret_cast<str_ConcurrentLocking*>
+ (mmap(0, sizeof(*shs), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
+ -1, 0));
+ str_ConcurrentLocking &s = *shs;
+ s.file = c_file;
+ s.sem_init(1);
+
+ // Start locker processes
+ pid_t pid[2];
+ pid[0] = fork();
+ ASSERT_GE(pid[0], 0);
+ if (pid[0] == 0) {
+ process_ConcurrentLocking(s);
+ exit(EXIT_FAILURE);
+ }
+ pid[1] = fork();
+ ASSERT_GE(pid[1], 0);
+ if (pid[1] == 0) {
+ process_ConcurrentLocking(s);
+ exit(EXIT_FAILURE);
+ }
+
+ struct timespec ts;
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+
+ const int fd = ceph_open(cmount, c_file, O_RDWR | O_CREAT, fileMode);
+ ASSERT_GE(fd, 0);
+
+ // Lock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, mypid));
+
+ // Synchronization point with process (failure: process is dead)
+ TWICE(PING_WORKER(1)); // (R1)
+ TWICE(WAIT_WORKER(1)); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+
+ // Shall have lock
+ TWICE(// Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(3)); // (3)
+
+ // Wait for process to share lock
+ TWICE(WAIT_WORKER(4)); // (4)
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, mypid));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, mypid));
+
+ // Wake up process to unlock shared lock
+ TWICE(PING_WORKER(2); // (R2)
+ WAIT_WORKER(5)); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX, mypid));
+
+ TWICE( // Wake up process to lock shared lock
+ PING_WORKER(3); // (R3)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6)); // (6)
+
+ // Release lock ; process will get it
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+ TWICE(WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, mypid));
+ ASSERT_EQ(-CEPHFS_EWOULDBLOCK,
+ ceph_flock(cmount, fd, LOCK_SH | LOCK_NB, mypid));
+
+ // Wake up process to unlock exclusive lock
+ PING_WORKER(4); // (R4)
+ WAIT_WORKER(7); // (7)
+ );
+
+ // We can lock it again
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_EX | LOCK_NB, mypid));
+ ASSERT_EQ(0, ceph_flock(cmount, fd, LOCK_UN, mypid));
+
+ // Wait pids
+ int status;
+ ASSERT_EQ(pid[0], waitpid(pid[0], &status, 0));
+ ASSERT_EQ(EXIT_SUCCESS, status);
+ ASSERT_EQ(pid[1], waitpid(pid[1], &status, 0));
+ ASSERT_EQ(EXIT_SUCCESS, status);
+
+ // Cleanup
+ s.sem_destroy();
+ ASSERT_EQ(0, munmap(shs, sizeof(*shs)));
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unlink(cmount, c_file));
+ CLEANUP_CEPH();
+}
+#endif
diff --git a/src/test/libcephfs/lazyio.cc b/src/test/libcephfs/lazyio.cc
new file mode 100644
index 000000000..0c17f1f2b
--- /dev/null
+++ b/src/test/libcephfs/lazyio.cc
@@ -0,0 +1,356 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat Ltd
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/rados/librados.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#if defined(__linux__)
+#include <sys/xattr.h>
+#endif
+
+rados_t cluster;
+
+TEST(LibCephFS, LazyIOOneWriterMulipleReaders) {
+ struct ceph_mount_info *ca, *cb;
+ ASSERT_EQ(ceph_create(&ca, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(ca, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ ASSERT_EQ(ceph_mount(ca, NULL), 0);
+
+ ASSERT_EQ(ceph_create(&cb, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cb, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cb, NULL));
+ ASSERT_EQ(ceph_mount(cb, NULL), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo.%d", getpid());
+
+ int fda = ceph_open(ca, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fda);
+
+ int fdb = ceph_open(cb, name, O_RDONLY, 0644);
+ ASSERT_LE(0, fdb);
+
+ ASSERT_EQ(0, ceph_lazyio(ca, fda, 1));
+ ASSERT_EQ(0, ceph_lazyio(cb, fdb, 1));
+
+ char out_buf[] = "fooooooooo";
+
+ /* Client a issues a write and propagates/flushes the buffer */
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(ca, fda, out_buf, sizeof(out_buf), 0));
+ ASSERT_EQ(0, ceph_lazyio_propagate(ca, fda, 0, 0));
+
+ /* Client a issues a write and propagates/flushes the buffer */
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(ca, fda, out_buf, sizeof(out_buf), 10));
+ ASSERT_EQ(0, ceph_lazyio_propagate(ca, fda, 0, 0));
+
+ char in_buf[40];
+ /* Calling ceph_lazyio_synchronize here will invalidate client b's cache and hence enable client a to fetch the propagated write of client a in the subsequent read */
+ ASSERT_EQ(0, ceph_lazyio_synchronize(cb, fdb, 0, 0));
+ ASSERT_EQ(ceph_read(cb, fdb, in_buf, sizeof(in_buf), 0), 2*strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooo");
+
+ /* Client a does not need to call ceph_lazyio_synchronize here because it is the latest writer and fda holds the updated inode*/
+ ASSERT_EQ(ceph_read(ca, fda, in_buf, sizeof(in_buf), 0), 2*strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooo");
+
+ ceph_close(ca, fda);
+ ceph_close(cb, fdb);
+
+ ceph_shutdown(ca);
+ ceph_shutdown(cb);
+}
+
+TEST(LibCephFS, LazyIOMultipleWritersMulipleReaders) {
+ struct ceph_mount_info *ca, *cb;
+ ASSERT_EQ(ceph_create(&ca, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(ca, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ ASSERT_EQ(ceph_mount(ca, NULL), 0);
+
+ ASSERT_EQ(ceph_create(&cb, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cb, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cb, NULL));
+ ASSERT_EQ(ceph_mount(cb, NULL), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo2.%d", getpid());
+
+ int fda = ceph_open(ca, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fda);
+
+ int fdb = ceph_open(cb, name, O_RDWR, 0644);
+ ASSERT_LE(0, fdb);
+
+ ASSERT_EQ(0, ceph_lazyio(ca, fda, 1));
+ ASSERT_EQ(0, ceph_lazyio(cb, fdb, 1));
+
+ char out_buf[] = "fooooooooo";
+ /* Client a issues a write and propagates/flushes the buffer */
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(ca, fda, out_buf, sizeof(out_buf), 0));
+ ASSERT_EQ(0, ceph_lazyio_propagate(ca, fda, 0, 0));
+
+ /* Client b issues a write and propagates/flushes the buffer*/
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(cb, fdb, out_buf, sizeof(out_buf), 10));
+ ASSERT_EQ(0, ceph_lazyio_propagate(cb, fdb, 0, 0));
+
+ char in_buf[40];
+ /* Calling ceph_lazyio_synchronize here will invalidate client a's cache and hence enable client a to fetch the propagated writes of client b in the subsequent read */
+ ASSERT_EQ(0, ceph_lazyio_synchronize(ca, fda, 0, 0));
+ ASSERT_EQ(ceph_read(ca, fda, in_buf, sizeof(in_buf), 0), 2*strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooo");
+
+ /* Client b does not need to call ceph_lazyio_synchronize here because it is the latest writer and the writes before it have already been propagated*/
+ ASSERT_EQ(ceph_read(cb, fdb, in_buf, sizeof(in_buf), 0), 2*strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooo");
+
+ /* Client a issues a write */
+ char wait_out_buf[] = "foobarbars";
+ ASSERT_EQ((int)sizeof(wait_out_buf), ceph_write(ca, fda, wait_out_buf, sizeof(wait_out_buf), 20));
+ ASSERT_EQ(0, ceph_lazyio_propagate(ca, fda, 0, 0));
+
+ /* Client a does not need to call ceph_lazyio_synchronize here because it is the latest writer and the writes before it have already been propagated*/
+ ASSERT_EQ(ceph_read(ca, fda, in_buf, sizeof(in_buf), 0), (2*(strlen(out_buf)))+strlen(wait_out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooofoobarbars");
+
+ /* Calling ceph_lazyio_synchronize here will invalidate client b's cache and hence enable client a to fetch the propagated write of client a in the subsequent read */
+ ASSERT_EQ(0, ceph_lazyio_synchronize(cb, fdb, 0, 0));
+ ASSERT_EQ(ceph_read(cb, fdb, in_buf, sizeof(in_buf), 0), (2*(strlen(out_buf)))+strlen(wait_out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooofoobarbars");
+
+ ceph_close(ca, fda);
+ ceph_close(cb, fdb);
+
+ ceph_shutdown(ca);
+ ceph_shutdown(cb);
+}
+
+TEST(LibCephFS, LazyIOMultipleWritersOneReader) {
+ struct ceph_mount_info *ca, *cb;
+ ASSERT_EQ(ceph_create(&ca, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(ca, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ ASSERT_EQ(ceph_mount(ca, NULL), 0);
+
+ ASSERT_EQ(ceph_create(&cb, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cb, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cb, NULL));
+ ASSERT_EQ(ceph_mount(cb, NULL), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo3.%d", getpid());
+
+ int fda = ceph_open(ca, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fda);
+
+ int fdb = ceph_open(cb, name, O_RDWR, 0644);
+ ASSERT_LE(0, fdb);
+
+ ASSERT_EQ(0, ceph_lazyio(ca, fda, 1));
+ ASSERT_EQ(0, ceph_lazyio(cb, fdb, 1));
+
+ char out_buf[] = "fooooooooo";
+ /* Client a issues a write and propagates/flushes the buffer */
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(ca, fda, out_buf, sizeof(out_buf), 0));
+ ASSERT_EQ(0, ceph_lazyio_propagate(ca, fda, 0, 0));
+
+ /* Client b issues a write and propagates/flushes the buffer*/
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(cb, fdb, out_buf, sizeof(out_buf), 10));
+ ASSERT_EQ(0, ceph_lazyio_propagate(cb, fdb, 0, 0));
+
+ char in_buf[40];
+ /* Client a reads the file and verifies that it only reads it's propagated writes and not Client b's*/
+ ASSERT_EQ(ceph_read(ca, fda, in_buf, sizeof(in_buf), 0), strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooo");
+
+ /* Client a reads the file again, this time with a lazyio_synchronize to check if the cache gets invalidated and data is refetched i.e all the propagated writes are being read*/
+ ASSERT_EQ(0, ceph_lazyio_synchronize(ca, fda, 0, 0));
+ ASSERT_EQ(ceph_read(ca, fda, in_buf, sizeof(in_buf), 0), 2*strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooo");
+
+ ceph_close(ca, fda);
+ ceph_close(cb, fdb);
+
+ ceph_shutdown(ca);
+ ceph_shutdown(cb);
+}
+
+TEST(LibCephFS, LazyIOSynchronizeFlush) {
+ /* Test to make sure lazyio_synchronize flushes dirty buffers */
+ struct ceph_mount_info *ca, *cb;
+ ASSERT_EQ(ceph_create(&ca, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(ca, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ ASSERT_EQ(ceph_mount(ca, NULL), 0);
+
+ ASSERT_EQ(ceph_create(&cb, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cb, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cb, NULL));
+ ASSERT_EQ(ceph_mount(cb, NULL), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo4.%d", getpid());
+
+ int fda = ceph_open(ca, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fda);
+
+ int fdb = ceph_open(cb, name, O_RDWR, 0644);
+ ASSERT_LE(0, fdb);
+
+ ASSERT_EQ(0, ceph_lazyio(ca, fda, 1));
+ ASSERT_EQ(0, ceph_lazyio(cb, fdb, 1));
+
+ char out_buf[] = "fooooooooo";
+
+ /* Client a issues a write and propagates it*/
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(ca, fda, out_buf, sizeof(out_buf), 0));
+ ASSERT_EQ(0, ceph_lazyio_propagate(ca, fda, 0, 0));
+
+ /* Client b issues writes and without lazyio_propagate*/
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(cb, fdb, out_buf, sizeof(out_buf), 10));
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(cb, fdb, out_buf, sizeof(out_buf), 20));
+
+ char in_buf[40];
+ /* Calling ceph_lazyio_synchronize here will first flush the possibly pending buffered write of client b and invalidate client b's cache and hence enable client b to fetch all the propagated writes */
+ ASSERT_EQ(0, ceph_lazyio_synchronize(cb, fdb, 0, 0));
+ ASSERT_EQ(ceph_read(cb, fdb, in_buf, sizeof(in_buf), 0), 3*strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooofooooooooo");
+
+ /* Required to call ceph_lazyio_synchronize here since client b is the latest writer and client a is out of sync with updated file*/
+ ASSERT_EQ(0, ceph_lazyio_synchronize(ca, fda, 0, 0));
+ ASSERT_EQ(ceph_read(ca, fda, in_buf, sizeof(in_buf), 0), 3*strlen(out_buf)+1);
+ ASSERT_STREQ(in_buf, "fooooooooofooooooooofooooooooo");
+
+ ceph_close(ca, fda);
+ ceph_close(cb, fdb);
+
+ ceph_shutdown(ca);
+ ceph_shutdown(cb);
+}
+
+TEST(LibCephFS, WithoutandWithLazyIO) {
+ struct ceph_mount_info *ca, *cb;
+ ASSERT_EQ(ceph_create(&ca, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(ca, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ ASSERT_EQ(ceph_mount(ca, NULL), 0);
+
+ ASSERT_EQ(ceph_create(&cb, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cb, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cb, NULL));
+ ASSERT_EQ(ceph_mount(cb, NULL), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo5.%d", getpid());
+
+ int fda = ceph_open(ca, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fda);
+
+ int fdb = ceph_open(cb, name, O_RDWR, 0644);
+ ASSERT_LE(0, fdb);
+
+ char out_buf_w[] = "1234567890";
+ /* Doing some non lazyio writes and read*/
+ ASSERT_EQ((int)sizeof(out_buf_w), ceph_write(ca, fda, out_buf_w, sizeof(out_buf_w), 0));
+
+ ASSERT_EQ((int)sizeof(out_buf_w), ceph_write(cb, fdb, out_buf_w, sizeof(out_buf_w), 10));
+
+ char in_buf_w[30];
+ ASSERT_EQ(ceph_read(ca, fda, in_buf_w, sizeof(in_buf_w), 0), 2*strlen(out_buf_w)+1);
+
+ /* Enable lazyio*/
+ ASSERT_EQ(0, ceph_lazyio(ca, fda, 1));
+ ASSERT_EQ(0, ceph_lazyio(cb, fdb, 1));
+
+ char out_buf[] = "fooooooooo";
+
+ /* Client a issues a write and propagates/flushes the buffer*/
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(ca, fda, out_buf, sizeof(out_buf), 20));
+ ASSERT_EQ(0, ceph_lazyio_propagate(ca, fda, 0, 0));
+
+ /* Client b issues a write and propagates/flushes the buffer*/
+ ASSERT_EQ((int)sizeof(out_buf), ceph_write(cb, fdb, out_buf, sizeof(out_buf), 30));
+ ASSERT_EQ(0, ceph_lazyio_propagate(cb, fdb, 0, 0));
+
+ char in_buf[50];
+ /* Calling ceph_lazyio_synchronize here will invalidate client a's cache and hence enable client a to fetch the propagated writes of client b in the subsequent read */
+ ASSERT_EQ(0, ceph_lazyio_synchronize(ca, fda, 0, 0));
+ ASSERT_EQ(ceph_read(ca, fda, in_buf, sizeof(in_buf), 0), (2*(strlen(out_buf)))+(2*(strlen(out_buf_w)))+1);
+ ASSERT_STREQ(in_buf, "12345678901234567890fooooooooofooooooooo");
+
+ /* Client b does not need to call ceph_lazyio_synchronize here because it is the latest writer and the writes before it have already been propagated*/
+ ASSERT_EQ(ceph_read(cb, fdb, in_buf, sizeof(in_buf), 0), (2*(strlen(out_buf)))+(2*(strlen(out_buf_w)))+1);
+ ASSERT_STREQ(in_buf, "12345678901234567890fooooooooofooooooooo");
+
+ ceph_close(ca, fda);
+ ceph_close(cb, fdb);
+
+ ceph_shutdown(ca);
+ ceph_shutdown(cb);
+}
+
+static int update_root_mode()
+{
+ struct ceph_mount_info *admin;
+ int r = ceph_create(&admin, NULL);
+ if (r < 0)
+ return r;
+ ceph_conf_read_file(admin, NULL);
+ ceph_conf_parse_env(admin, NULL);
+ ceph_conf_set(admin, "client_permissions", "false");
+ r = ceph_mount(admin, "/");
+ if (r < 0)
+ goto out;
+ r = ceph_chmod(admin, "/", 0777);
+out:
+ ceph_shutdown(admin);
+ return r;
+}
+
+int main(int argc, char **argv)
+{
+ int r = update_root_mode();
+ if (r < 0)
+ exit(1);
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ srand(getpid());
+
+ r = rados_create(&cluster, NULL);
+ if (r < 0)
+ exit(1);
+
+ r = rados_conf_read_file(cluster, NULL);
+ if (r < 0)
+ exit(1);
+
+ rados_conf_parse_env(cluster, NULL);
+ r = rados_connect(cluster);
+ if (r < 0)
+ exit(1);
+
+ r = RUN_ALL_TESTS();
+
+ rados_shutdown(cluster);
+
+ return r;
+}
diff --git a/src/test/libcephfs/main.cc b/src/test/libcephfs/main.cc
new file mode 100644
index 000000000..5def83c43
--- /dev/null
+++ b/src/test/libcephfs/main.cc
@@ -0,0 +1,50 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ * Copyright (C) 2016 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+
+static int update_root_mode()
+{
+ struct ceph_mount_info *admin;
+ int r = ceph_create(&admin, NULL);
+ if (r < 0)
+ return r;
+ ceph_conf_read_file(admin, NULL);
+ ceph_conf_parse_env(admin, NULL);
+ ceph_conf_set(admin, "client_permissions", "false");
+ r = ceph_mount(admin, "/");
+ if (r < 0)
+ goto out;
+ r = ceph_chmod(admin, "/", 01777);
+out:
+ ceph_shutdown(admin);
+ return r;
+}
+
+
+int main(int argc, char **argv)
+{
+ int r = update_root_mode();
+ if (r < 0)
+ exit(1);
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ srand(getpid());
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/libcephfs/monconfig.cc b/src/test/libcephfs/monconfig.cc
new file mode 100644
index 000000000..178278c6b
--- /dev/null
+++ b/src/test/libcephfs/monconfig.cc
@@ -0,0 +1,101 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include "common/ceph_context.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+class MonConfig : public ::testing::Test
+{
+ protected:
+ struct ceph_mount_info *ca;
+
+ void SetUp() override {
+ ASSERT_EQ(0, ceph_create(&ca, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(ca, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ }
+
+ void TearDown() override {
+ ceph_shutdown(ca);
+ }
+
+ // Helper to remove/unset all possible mon information from ConfigProxy
+ void clear_mon_config(CephContext *cct) {
+ auto& conf = cct->_conf;
+ // Clear safe_to_start_threads, allowing updates to config values
+ conf._clear_safe_to_start_threads();
+ ASSERT_EQ(0, conf.set_val("monmap", "", nullptr));
+ ASSERT_EQ(0, conf.set_val("mon_host", "", nullptr));
+ ASSERT_EQ(0, conf.set_val("mon_dns_srv_name", "", nullptr));
+ conf.set_safe_to_start_threads();
+ }
+
+ // Helper to test basic operation on a mount
+ void use_mount(struct ceph_mount_info *mnt, std::string name_prefix) {
+ char name[20];
+ snprintf(name, sizeof(name), "%s.%d", name_prefix.c_str(), getpid());
+ int fd = ceph_open(mnt, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fd);
+
+ ceph_close(mnt, fd);
+ }
+};
+
+TEST_F(MonConfig, MonAddrsMissing) {
+ CephContext *cct;
+
+ // Test mount failure when there is no known mon config source
+ cct = ceph_get_mount_context(ca);
+ ASSERT_NE(nullptr, cct);
+ clear_mon_config(cct);
+
+ ASSERT_EQ(-CEPHFS_ENOENT, ceph_mount(ca, NULL));
+}
+
+TEST_F(MonConfig, MonAddrsInConfigProxy) {
+ // Test a successful mount with default mon config source in ConfigProxy
+ ASSERT_EQ(0, ceph_mount(ca, NULL));
+
+ use_mount(ca, "foo");
+}
+
+TEST_F(MonConfig, MonAddrsInCct) {
+ struct ceph_mount_info *cb;
+ CephContext *cct;
+
+ // Perform mount to bootstrap mon addrs in CephContext
+ ASSERT_EQ(0, ceph_mount(ca, NULL));
+
+ // Reuse bootstrapped CephContext, clearing ConfigProxy mon addr sources
+ cct = ceph_get_mount_context(ca);
+ ASSERT_NE(nullptr, cct);
+ clear_mon_config(cct);
+ ASSERT_EQ(0, ceph_create_with_context(&cb, cct));
+
+ // Test a successful mount with only mon values in CephContext
+ ASSERT_EQ(0, ceph_mount(cb, NULL));
+
+ use_mount(ca, "bar");
+ use_mount(cb, "bar");
+
+ ceph_shutdown(cb);
+}
diff --git a/src/test/libcephfs/multiclient.cc b/src/test/libcephfs/multiclient.cc
new file mode 100644
index 000000000..c9fc038bc
--- /dev/null
+++ b/src/test/libcephfs/multiclient.cc
@@ -0,0 +1,180 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <thread>
+#ifdef __linux__
+#include <sys/xattr.h>
+#endif
+
+TEST(LibCephFS, MulticlientSimple) {
+ struct ceph_mount_info *ca, *cb;
+ ASSERT_EQ(ceph_create(&ca, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(ca, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ ASSERT_EQ(ceph_mount(ca, NULL), 0);
+
+ ASSERT_EQ(ceph_create(&cb, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cb, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cb, NULL));
+ ASSERT_EQ(ceph_mount(cb, NULL), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo.%d", getpid());
+ int fda = ceph_open(ca, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fda);
+ int fdb = ceph_open(cb, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fdb);
+
+ char bufa[4] = "foo";
+ char bufb[4];
+
+ for (int i=0; i<10; i++) {
+ strcpy(bufa, "foo");
+ ASSERT_EQ((int)sizeof(bufa), ceph_write(ca, fda, bufa, sizeof(bufa), i*6));
+ ASSERT_EQ((int)sizeof(bufa), ceph_read(cb, fdb, bufb, sizeof(bufa), i*6));
+ ASSERT_EQ(0, memcmp(bufa, bufb, sizeof(bufa)));
+ strcpy(bufb, "bar");
+ ASSERT_EQ((int)sizeof(bufb), ceph_write(cb, fdb, bufb, sizeof(bufb), i*6+3));
+ ASSERT_EQ((int)sizeof(bufb), ceph_read(ca, fda, bufa, sizeof(bufb), i*6+3));
+ ASSERT_EQ(0, memcmp(bufa, bufb, sizeof(bufa)));
+ }
+
+ ceph_close(ca, fda);
+ ceph_close(cb, fdb);
+
+ ceph_shutdown(ca);
+ ceph_shutdown(cb);
+}
+
+TEST(LibCephFS, MulticlientHoleEOF) {
+ struct ceph_mount_info *ca, *cb;
+ ASSERT_EQ(ceph_create(&ca, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(ca, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(ca, NULL));
+ ASSERT_EQ(ceph_mount(ca, NULL), 0);
+
+ ASSERT_EQ(ceph_create(&cb, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cb, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cb, NULL));
+ ASSERT_EQ(ceph_mount(cb, NULL), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo.%d", getpid());
+ int fda = ceph_open(ca, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fda);
+ int fdb = ceph_open(cb, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fdb);
+
+ ASSERT_EQ(3, ceph_write(ca, fda, "foo", 3, 0));
+ ASSERT_EQ(0, ceph_ftruncate(ca, fda, 1000000));
+
+ char buf[4];
+ ASSERT_EQ(2, ceph_read(cb, fdb, buf, sizeof(buf), 1000000-2));
+ ASSERT_EQ(0, buf[0]);
+ ASSERT_EQ(0, buf[1]);
+
+ ceph_close(ca, fda);
+ ceph_close(cb, fdb);
+
+ ceph_shutdown(ca);
+ ceph_shutdown(cb);
+}
+
+static void write_func(bool *stop)
+{
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo.%d", getpid());
+ int fd = ceph_open(cmount, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fd);
+
+ int buf_size = 4096;
+ char *buf = (char *)malloc(buf_size);
+ if (!buf) {
+ *stop = true;
+ printf("write_func failed to allocate buffer!");
+ return;
+ }
+ memset(buf, 1, buf_size);
+
+ while (!(*stop)) {
+ int i;
+
+ // truncate the file size to 4096 will set the max_size to 4MB.
+ ASSERT_EQ(0, ceph_ftruncate(cmount, fd, 4096));
+
+ // write 4MB + extra 64KB data will make client to trigger to
+ // call check_cap() to report new size. And if MDS is revoking
+ // the Fsxrw caps and we are still holding the Fw caps and will
+ // trigger tracker#57244.
+ for (i = 0; i < 1040; i++) {
+ ASSERT_EQ(ceph_write(cmount, fd, buf, buf_size, 0), buf_size);
+ }
+ }
+
+ ceph_shutdown(cmount);
+}
+
+static void setattr_func(bool *stop)
+{
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char name[20];
+ snprintf(name, sizeof(name), "foo.%d", getpid());
+ int fd = ceph_open(cmount, name, O_CREAT|O_RDWR, 0644);
+ ASSERT_LE(0, fd);
+
+ while (!(*stop)) {
+ // setattr will make the MDS to acquire xlock for the filelock and
+ // force to revoke caps from clients
+ struct ceph_statx stx = {.stx_size = 0};
+ ASSERT_EQ(ceph_fsetattrx(cmount, fd, &stx, CEPH_SETATTR_SIZE), 0);
+ }
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, MulticlientRevokeCaps) {
+ std::thread thread1, thread2;
+ bool stop = false;
+ int wait = 60; // in second
+
+ thread1 = std::thread(write_func, &stop);
+ thread2 = std::thread(setattr_func, &stop);
+
+ printf(" Will run test for %d seconds!\n", wait);
+ sleep(wait);
+ stop = true;
+
+ thread1.join();
+ thread2.join();
+}
diff --git a/src/test/libcephfs/newops.cc b/src/test/libcephfs/newops.cc
new file mode 100644
index 000000000..2a4573b9f
--- /dev/null
+++ b/src/test/libcephfs/newops.cc
@@ -0,0 +1,87 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2021 Red Hat Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "gtest/gtest-spi.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-more-matchers.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include "mds/mdstypes.h"
+#include "include/stat.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#endif
+
+#include <fmt/format.h>
+#include <map>
+#include <vector>
+#include <thread>
+#include <regex>
+#include <string>
+
+using ::testing::AnyOf;
+using ::testing::Gt;
+using ::testing::Eq;
+using namespace std;
+
+/*
+ * Test this with different ceph versions
+ */
+
+TEST(LibCephFS, NewOPs)
+{
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ const char *test_path = "test_newops_dir";
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, test_path, 0777));
+
+ {
+ char value[1024] = "";
+ int r = ceph_getxattr(cmount, test_path, "ceph.dir.pin.random", (void*)value, sizeof(value));
+ // Clients will return -CEPHFS_ENODATA if new getvxattr op not support yet.
+ EXPECT_THAT(r, AnyOf(Gt(0), Eq(-CEPHFS_ENODATA)));
+ }
+
+ {
+ double val = (double)1.0/(double)128.0;
+ std::stringstream ss;
+ ss << val;
+ int r = ceph_setxattr(cmount, test_path, "ceph.dir.pin.random", (void*)ss.str().c_str(), strlen(ss.str().c_str()), XATTR_CREATE);
+ // Old cephs will return -CEPHFS_EINVAL if not support "ceph.dir.pin.random" yet.
+ EXPECT_THAT(r, AnyOf(Eq(0), Eq(-CEPHFS_EINVAL)));
+
+ char value[1024] = "";
+ r = ceph_getxattr(cmount, test_path, "ceph.dir.pin.random", (void*)value, sizeof(value));
+ // Clients will return -CEPHFS_ENODATA if new getvxattr op not support yet.
+ EXPECT_THAT(r, AnyOf(Gt(0), Eq(-CEPHFS_ENODATA)));
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, test_path));
+
+ ceph_shutdown(cmount);
+}
diff --git a/src/test/libcephfs/quota.cc b/src/test/libcephfs/quota.cc
new file mode 100644
index 000000000..00afb3d09
--- /dev/null
+++ b/src/test/libcephfs/quota.cc
@@ -0,0 +1,167 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/compat.h"
+#include "gtest/gtest.h"
+#include "include/cephfs/libcephfs.h"
+#include "mds/mdstypes.h"
+#include "include/stat.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#endif
+
+TEST(LibCephFS, SnapQuota) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_snap_dir_quota_xattr[256];
+ char test_snap_subdir_quota_xattr[256];
+ char test_snap_subdir_noquota_xattr[256];
+ char xattrk[128];
+ char xattrv[128];
+ char c_temp[PATH_MAX];
+ char gxattrv[128];
+ int xbuflen = sizeof(gxattrv);
+ pid_t mypid = getpid();
+
+ // create dir and set quota
+ sprintf(test_snap_dir_quota_xattr, "test_snap_dir_quota_xattr_%d", mypid);
+ ASSERT_EQ(0, ceph_mkdir(cmount, test_snap_dir_quota_xattr, 0777));
+
+ sprintf(xattrk, "ceph.quota.max_bytes");
+ sprintf(xattrv, "65536");
+ ASSERT_EQ(0, ceph_setxattr(cmount, test_snap_dir_quota_xattr, xattrk, (void *)xattrv, 5, XATTR_CREATE));
+
+ // create subdir and set quota
+ sprintf(test_snap_subdir_quota_xattr, "test_snap_dir_quota_xattr_%d/subdir_quota", mypid);
+ ASSERT_EQ(0, ceph_mkdirs(cmount, test_snap_subdir_quota_xattr, 0777));
+
+ sprintf(xattrk, "ceph.quota.max_bytes");
+ sprintf(xattrv, "32768");
+ ASSERT_EQ(0, ceph_setxattr(cmount, test_snap_subdir_quota_xattr, xattrk, (void *)xattrv, 5, XATTR_CREATE));
+
+ // create subdir with no quota
+ sprintf(test_snap_subdir_noquota_xattr, "test_snap_dir_quota_xattr_%d/subdir_noquota", mypid);
+ ASSERT_EQ(0, ceph_mkdirs(cmount, test_snap_subdir_noquota_xattr, 0777));
+
+ // snapshot dir
+ sprintf(c_temp, "/.snap/test_snap_dir_quota_xattr_snap_%d", mypid);
+ ASSERT_EQ(0, ceph_mkdirs(cmount, c_temp, 0777));
+
+ // check dir quota under snap
+ sprintf(c_temp, "/.snap/test_snap_dir_quota_xattr_snap_%d/test_snap_dir_quota_xattr_%d", mypid, mypid);
+ int alen = ceph_getxattr(cmount, c_temp, "ceph.quota.max_bytes", (void *)gxattrv, xbuflen);
+ ASSERT_LT(0, alen);
+ ASSERT_LT(alen, xbuflen);
+ gxattrv[alen] = '\0';
+ ASSERT_STREQ(gxattrv, "65536");
+
+ // check subdir quota under snap
+ sprintf(c_temp, "/.snap/test_snap_dir_quota_xattr_snap_%d/test_snap_dir_quota_xattr_%d/subdir_quota", mypid, mypid);
+ alen = ceph_getxattr(cmount, c_temp, "ceph.quota.max_bytes", (void *)gxattrv, xbuflen);
+ ASSERT_LT(0, alen);
+ ASSERT_LT(alen, xbuflen);
+ gxattrv[alen] = '\0';
+ ASSERT_STREQ(gxattrv, "32768");
+
+ // ensure subdir noquota xattr under snap
+ sprintf(c_temp, "/.snap/test_snap_dir_quota_xattr_snap_%d/test_snap_dir_quota_xattr_%d/subdir_noquota", mypid, mypid);
+ EXPECT_EQ(-CEPHFS_ENODATA, ceph_getxattr(cmount, c_temp, "ceph.quota.max_bytes", (void *)gxattrv, xbuflen));
+
+ // listxattr() shouldn't return ceph.quota.max_bytes vxattr
+ sprintf(c_temp, "/.snap/test_snap_dir_quota_xattr_snap_%d/test_snap_dir_quota_xattr_%d", mypid, mypid);
+ char xattrlist[512];
+ int len = ceph_listxattr(cmount, c_temp, xattrlist, sizeof(xattrlist));
+ ASSERT_GE(sizeof(xattrlist), (size_t)len);
+ char *p = xattrlist;
+ int found = 0;
+ while (len > 0) {
+ if (strcmp(p, "ceph.quota.max_bytes") == 0)
+ found++;
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ }
+ ASSERT_EQ(found, 0);
+
+ ceph_shutdown(cmount);
+}
+
+void statfs_quota_size_check(struct ceph_mount_info *cmount, const char *path,
+ int blocks, int bsize)
+{
+ struct statvfs stvfs;
+
+ ASSERT_EQ(0, ceph_statfs(cmount, path, &stvfs));
+ ASSERT_EQ(blocks, stvfs.f_blocks);
+ ASSERT_EQ(bsize, stvfs.f_bsize);
+ ASSERT_EQ(bsize, stvfs.f_frsize);
+}
+
+TEST(LibCephFS, QuotaRealm) {
+ struct ceph_mount_info *cmount, *pmount1, *pmount2;
+ char test_quota_realm_pdir[128];
+ char test_quota_realm_cdir[256];
+ char xattrk[32];
+ char xattrv[16];
+
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ pid_t mypid = getpid();
+
+ // create parent directory and set quota size
+ sprintf(test_quota_realm_pdir, "/test_quota_realm_pdir_%d", mypid);
+ ASSERT_EQ(0, ceph_mkdir(cmount, test_quota_realm_pdir, 0777));
+ sprintf(xattrk, "ceph.quota.max_bytes");
+ sprintf(xattrv, "8388608"); // 8MB
+ ASSERT_EQ(0, ceph_setxattr(cmount, test_quota_realm_pdir, xattrk, (void *)xattrv, 7, XATTR_CREATE));
+
+ // create child directory and set quota file
+ sprintf(test_quota_realm_cdir, "%s/test_quota_realm_cdir", test_quota_realm_pdir);
+ ASSERT_EQ(0, ceph_mkdir(cmount, test_quota_realm_cdir, 0777));
+ sprintf(xattrk, "ceph.quota.max_files");
+ sprintf(xattrv, "1024"); // 1K files
+ ASSERT_EQ(0, ceph_setxattr(cmount, test_quota_realm_cdir, xattrk, (void *)xattrv, 4, XATTR_CREATE));
+
+ ASSERT_EQ(ceph_create(&pmount1, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(pmount1, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(pmount1, NULL));
+ ASSERT_EQ(ceph_mount(pmount1, test_quota_realm_pdir), 0);
+ statfs_quota_size_check(pmount1, "/", 2, 4194304); // 8MB
+
+ ASSERT_EQ(ceph_create(&pmount2, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(pmount2, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(pmount2, NULL));
+ ASSERT_EQ(ceph_mount(pmount2, test_quota_realm_cdir), 0);
+ statfs_quota_size_check(pmount2, "/", 2, 4194304); // 8MB
+
+ ceph_shutdown(pmount1);
+ ceph_shutdown(pmount2);
+ ceph_shutdown(cmount);
+}
diff --git a/src/test/libcephfs/readdir_r_cb.cc b/src/test/libcephfs/readdir_r_cb.cc
new file mode 100644
index 000000000..959c276f1
--- /dev/null
+++ b/src/test/libcephfs/readdir_r_cb.cc
@@ -0,0 +1,65 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include <errno.h>
+#include <fcntl.h>
+
+TEST(LibCephFS, ReaddirRCB) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ char c_dir[256];
+ sprintf(c_dir, "/readdir_r_cb_tests_%d", getpid());
+ struct ceph_dir_result *dirp;
+ ASSERT_EQ(0, ceph_mkdirs(cmount, c_dir, 0777));
+ ASSERT_LE(0, ceph_opendir(cmount, c_dir, &dirp));
+
+ // dir is empty, check that it only contains . and ..
+ int buflen = 100;
+ char *buf = new char[buflen];
+ // . is 2, .. is 3 (for null terminators)
+ ASSERT_EQ(5, ceph_getdnames(cmount, dirp, buf, buflen));
+ char c_file[256];
+ sprintf(c_file, "/readdir_r_cb_tests_%d/foo", getpid());
+ int fd = ceph_open(cmount, c_file, O_CREAT, 0777);
+ ASSERT_LT(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ // check correctness with one entry
+ ASSERT_LE(0, ceph_closedir(cmount, dirp));
+ ASSERT_LE(0, ceph_opendir(cmount, c_dir, &dirp));
+ ASSERT_EQ(9, ceph_getdnames(cmount, dirp, buf, buflen)); // ., .., foo
+
+ // check correctness if buffer is too small
+ ASSERT_LE(0, ceph_closedir(cmount, dirp));
+ ASSERT_GE(0, ceph_opendir(cmount, c_dir, &dirp));
+ ASSERT_EQ(-CEPHFS_ERANGE, ceph_getdnames(cmount, dirp, buf, 1));
+
+ //check correctness if it needs to split listing
+ ASSERT_LE(0, ceph_closedir(cmount, dirp));
+ ASSERT_LE(0, ceph_opendir(cmount, c_dir, &dirp));
+ ASSERT_EQ(5, ceph_getdnames(cmount, dirp, buf, 6));
+ ASSERT_EQ(4, ceph_getdnames(cmount, dirp, buf, 6));
+
+ // free cmount after finishing testing
+ ASSERT_LE(0, ceph_closedir(cmount, dirp));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ASSERT_EQ(0, ceph_release(cmount));
+}
diff --git a/src/test/libcephfs/reclaim.cc b/src/test/libcephfs/reclaim.cc
new file mode 100644
index 000000000..736ed5759
--- /dev/null
+++ b/src/test/libcephfs/reclaim.cc
@@ -0,0 +1,163 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Tests for Ceph delegation handling
+ *
+ * (c) 2017, Jeff Layton <jlayton@redhat.com>
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include "include/stat.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/uio.h>
+#include <libgen.h>
+#include <stdlib.h>
+
+#ifdef __linux__
+#include <sys/xattr.h>
+#include <limits.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/wait.h>
+#endif
+
+
+#include <map>
+#include <vector>
+#include <thread>
+#include <atomic>
+
+#define CEPHFS_RECLAIM_TIMEOUT 60
+
+static int dying_client(int argc, char **argv)
+{
+ struct ceph_mount_info *cmount;
+
+ /* Caller must pass in the uuid */
+ if (argc < 2)
+ return 1;
+
+ if (ceph_create(&cmount, nullptr) != 0)
+ return 1;
+
+ if (ceph_conf_read_file(cmount, nullptr) != 0)
+ return 1;
+
+ if (ceph_conf_parse_env(cmount, nullptr) != 0)
+ return 1;
+
+ if (ceph_init(cmount) != 0)
+ return 1;
+
+ ceph_set_session_timeout(cmount, CEPHFS_RECLAIM_TIMEOUT);
+
+ if (ceph_start_reclaim(cmount, argv[1], CEPH_RECLAIM_RESET) != -CEPHFS_ENOENT)
+ return 1;
+
+ ceph_set_uuid(cmount, argv[1]);
+
+ if (ceph_mount(cmount, "/") != 0)
+ return 1;
+
+ Inode *root, *file;
+ if (ceph_ll_lookup_root(cmount, &root) != 0)
+ return 1;
+
+ Fh *fh;
+ struct ceph_statx stx;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ if (ceph_ll_create(cmount, root, argv[1], 0666, O_RDWR|O_CREAT|O_EXCL,
+ &file, &fh, &stx, 0, 0, perms) != 0)
+ return 1;
+
+ return 0;
+}
+
+TEST(LibCephFS, ReclaimReset) {
+ pid_t pid;
+ char uuid[256];
+ const char *exe = "/proc/self/exe";
+
+ sprintf(uuid, "simplereclaim:%x", getpid());
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0) {
+ errno = 0;
+ execl(exe, exe, uuid, nullptr);
+ /* It won't be zero of course, which is sort of the point... */
+ ASSERT_EQ(errno, 0);
+ }
+
+ /* parent - wait for child to exit */
+ int ret;
+ pid_t wp = wait(&ret);
+ ASSERT_GE(wp, 0);
+ ASSERT_EQ(WIFEXITED(ret), true);
+ ASSERT_EQ(WEXITSTATUS(ret), 0);
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, nullptr), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, nullptr), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, nullptr));
+ ASSERT_EQ(ceph_init(cmount), 0);
+ ceph_set_session_timeout(cmount, CEPHFS_RECLAIM_TIMEOUT);
+ ASSERT_EQ(ceph_start_reclaim(cmount, uuid, CEPH_RECLAIM_RESET), 0);
+ ceph_set_uuid(cmount, uuid);
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ Inode *root, *file;
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+ UserPerm *perms = ceph_mount_perms(cmount);
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_ll_lookup(cmount, root, uuid, &file, &stx, 0, 0, perms), 0);
+ Fh *fh;
+ ASSERT_EQ(ceph_ll_open(cmount, file, O_WRONLY, &fh, perms), 0);
+
+ ceph_unmount(cmount);
+ ceph_release(cmount);
+}
+
+static int update_root_mode()
+{
+ struct ceph_mount_info *admin;
+ int r = ceph_create(&admin, nullptr);
+ if (r < 0)
+ return r;
+ ceph_conf_read_file(admin, nullptr);
+ ceph_conf_parse_env(admin, nullptr);
+ ceph_conf_set(admin, "client_permissions", "false");
+ r = ceph_mount(admin, "/");
+ if (r < 0)
+ goto out;
+ r = ceph_chmod(admin, "/", 01777);
+out:
+ ceph_shutdown(admin);
+ return r;
+}
+
+int main(int argc, char **argv)
+{
+ int r = update_root_mode();
+ if (r < 0)
+ exit(1);
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (argc > 1)
+ return dying_client(argc, argv);
+
+ srand(getpid());
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/libcephfs/recordlock.cc b/src/test/libcephfs/recordlock.cc
new file mode 100644
index 000000000..ad108b69c
--- /dev/null
+++ b/src/test/libcephfs/recordlock.cc
@@ -0,0 +1,1105 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ * 2016 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <pthread.h>
+#include "gtest/gtest.h"
+#ifndef GTEST_IS_THREADSAFE
+#error "!GTEST_IS_THREADSAFE"
+#endif
+
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <semaphore.h>
+#include <time.h>
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#elif __FreeBSD__
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+#include "include/ceph_assert.h"
+#include "ceph_pthread_self.h"
+
+// Startup common: create and mount ceph fs
+#define STARTUP_CEPH() do { \
+ ASSERT_EQ(0, ceph_create(&cmount, NULL)); \
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); \
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL)); \
+ ASSERT_EQ(0, ceph_mount(cmount, NULL)); \
+ } while(0)
+
+// Cleanup common: unmount and release ceph fs
+#define CLEANUP_CEPH() do { \
+ ASSERT_EQ(0, ceph_unmount(cmount)); \
+ ASSERT_EQ(0, ceph_release(cmount)); \
+ } while(0)
+
+static const mode_t fileMode = S_IRWXU | S_IRWXG | S_IRWXO;
+
+// Default wait time for normal and "slow" operations
+// (5" should be enough in case of network congestion)
+static const long waitMs = 10;
+static const long waitSlowMs = 5000;
+
+// Get the absolute struct timespec reference from now + 'ms' milliseconds
+static const struct timespec* abstime(struct timespec &ts, long ms) {
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
+ ceph_abort();
+ }
+ ts.tv_nsec += ms * 1000000;
+ ts.tv_sec += ts.tv_nsec / 1000000000;
+ ts.tv_nsec %= 1000000000;
+ return &ts;
+}
+
+/* Basic locking */
+
+TEST(LibCephFS, BasicRecordLocking) {
+ struct ceph_mount_info *cmount = NULL;
+ STARTUP_CEPH();
+
+ char c_file[1024];
+ sprintf(c_file, "recordlock_test_%d", getpid());
+ Fh *fh = NULL;
+ Inode *root = NULL, *inode = NULL;
+ struct ceph_statx stx;
+ int rc;
+ struct flock lock1, lock2;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ // Get the root inode
+ rc = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(rc, 0);
+
+ // Get the inode and Fh corresponding to c_file
+ rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
+ &inode, &fh, &stx, 0, 0, perms);
+ ASSERT_EQ(rc, 0);
+
+ // write lock twice
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
+
+ lock2.l_type = F_WRLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 0;
+ lock2.l_len = 1024;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock2, 43, false));
+
+ // Now try a conflicting read lock
+ lock2.l_type = F_RDLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 100;
+ lock2.l_len = 100;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock2, 43, false));
+
+ // Now do a getlk
+ ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
+ ASSERT_EQ(lock2.l_type, F_WRLCK);
+ ASSERT_EQ(lock2.l_start, 0);
+ ASSERT_EQ(lock2.l_len, 1024);
+ ASSERT_EQ(lock2.l_pid, getpid());
+
+ // Extend the range of the write lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 1024;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
+
+ // Now do a getlk
+ lock2.l_type = F_RDLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 100;
+ lock2.l_len = 100;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
+ ASSERT_EQ(lock2.l_type, F_WRLCK);
+ ASSERT_EQ(lock2.l_start, 0);
+ ASSERT_EQ(lock2.l_len, 2048);
+ ASSERT_EQ(lock2.l_pid, getpid());
+
+ // Now release part of the range
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 512;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
+
+ // Now do a getlk to check 1st part
+ lock2.l_type = F_RDLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 100;
+ lock2.l_len = 100;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
+ ASSERT_EQ(lock2.l_type, F_WRLCK);
+ ASSERT_EQ(lock2.l_start, 0);
+ ASSERT_EQ(lock2.l_len, 512);
+ ASSERT_EQ(lock2.l_pid, getpid());
+
+ // Now do a getlk to check 2nd part
+ lock2.l_type = F_RDLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 2000;
+ lock2.l_len = 100;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
+ ASSERT_EQ(lock2.l_type, F_WRLCK);
+ ASSERT_EQ(lock2.l_start, 1536);
+ ASSERT_EQ(lock2.l_len, 512);
+ ASSERT_EQ(lock2.l_pid, getpid());
+
+ // Now do a getlk to check released part
+ lock2.l_type = F_RDLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 512;
+ lock2.l_len = 1024;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
+ ASSERT_EQ(lock2.l_type, F_UNLCK);
+ ASSERT_EQ(lock2.l_start, 512);
+ ASSERT_EQ(lock2.l_len, 1024);
+ ASSERT_EQ(lock2.l_pid, getpid());
+
+ // Now downgrade the 1st part of the lock
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 512;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
+
+ // Now do a getlk to check 1st part
+ lock2.l_type = F_WRLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 100;
+ lock2.l_len = 100;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
+ ASSERT_EQ(lock2.l_type, F_RDLCK);
+ ASSERT_EQ(lock2.l_start, 0);
+ ASSERT_EQ(lock2.l_len, 512);
+ ASSERT_EQ(lock2.l_pid, getpid());
+
+ // Now upgrade the 1st part of the lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 512;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
+
+ // Now do a getlk to check 1st part
+ lock2.l_type = F_WRLCK;
+ lock2.l_whence = SEEK_SET;
+ lock2.l_start = 100;
+ lock2.l_len = 100;
+ lock2.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
+ ASSERT_EQ(lock2.l_type, F_WRLCK);
+ ASSERT_EQ(lock2.l_start, 0);
+ ASSERT_EQ(lock2.l_len, 512);
+ ASSERT_EQ(lock2.l_pid, getpid());
+
+ ASSERT_EQ(0, ceph_ll_close(cmount, fh));
+ ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
+ CLEANUP_CEPH();
+}
+
+/* Locking in different threads */
+
+// Used by ConcurrentLocking test
+struct str_ConcurrentRecordLocking {
+ const char *file;
+ struct ceph_mount_info *cmount; // !NULL if shared
+ sem_t sem[2];
+ sem_t semReply[2];
+ void sem_init(int pshared) {
+ ASSERT_EQ(0, ::sem_init(&sem[0], pshared, 0));
+ ASSERT_EQ(0, ::sem_init(&sem[1], pshared, 0));
+ ASSERT_EQ(0, ::sem_init(&semReply[0], pshared, 0));
+ ASSERT_EQ(0, ::sem_init(&semReply[1], pshared, 0));
+ }
+ void sem_destroy() {
+ ASSERT_EQ(0, ::sem_destroy(&sem[0]));
+ ASSERT_EQ(0, ::sem_destroy(&sem[1]));
+ ASSERT_EQ(0, ::sem_destroy(&semReply[0]));
+ ASSERT_EQ(0, ::sem_destroy(&semReply[1]));
+ }
+};
+
+// Wakeup main (for (N) steps)
+#define PING_MAIN(n) ASSERT_EQ(0, sem_post(&s.sem[n%2]))
+// Wait for main to wake us up (for (RN) steps)
+#define WAIT_MAIN(n) \
+ ASSERT_EQ(0, sem_timedwait(&s.semReply[n%2], abstime(ts, waitSlowMs)))
+
+// Wakeup worker (for (RN) steps)
+#define PING_WORKER(n) ASSERT_EQ(0, sem_post(&s.semReply[n%2]))
+// Wait for worker to wake us up (for (N) steps)
+#define WAIT_WORKER(n) \
+ ASSERT_EQ(0, sem_timedwait(&s.sem[n%2], abstime(ts, waitSlowMs)))
+// Worker shall not wake us up (for (N) steps)
+#define NOT_WAIT_WORKER(n) \
+ ASSERT_EQ(-1, sem_timedwait(&s.sem[n%2], abstime(ts, waitMs)))
+
+// Do twice an operation
+#define TWICE(EXPR) do { \
+ EXPR; \
+ EXPR; \
+ } while(0)
+
+/* Locking in different threads */
+
+// Used by ConcurrentLocking test
+static void thread_ConcurrentRecordLocking(str_ConcurrentRecordLocking& s) {
+ struct ceph_mount_info *const cmount = s.cmount;
+ Fh *fh = NULL;
+ Inode *root = NULL, *inode = NULL;
+ struct ceph_statx stx;
+ struct flock lock1;
+ int rc;
+ struct timespec ts;
+
+ // Get the root inode
+ rc = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(rc, 0);
+
+ // Get the inode and Fh corresponding to c_file
+ rc = ceph_ll_create(cmount, root, s.file, fileMode, O_RDWR | O_CREAT,
+ &inode, &fh, &stx, 0, 0, ceph_mount_perms(cmount));
+ ASSERT_EQ(rc, 0);
+
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ PING_MAIN(1); // (1)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), true));
+ PING_MAIN(2); // (2)
+
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ PING_MAIN(3); // (3)
+
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), true));
+ PING_MAIN(4); // (4)
+
+ WAIT_MAIN(1); // (R1)
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ PING_MAIN(5); // (5)
+
+ WAIT_MAIN(2); // (R2)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), true));
+ PING_MAIN(6); // (6)
+
+ WAIT_MAIN(3); // (R3)
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ PING_MAIN(7); // (7)
+
+ ASSERT_EQ(0, ceph_ll_close(cmount, fh));
+}
+
+// Used by ConcurrentRecordLocking test
+static void* thread_ConcurrentRecordLocking_(void *arg) {
+ str_ConcurrentRecordLocking *const s =
+ reinterpret_cast<str_ConcurrentRecordLocking*>(arg);
+ thread_ConcurrentRecordLocking(*s);
+ return NULL;
+}
+
+TEST(LibCephFS, ConcurrentRecordLocking) {
+ const pid_t mypid = getpid();
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+
+ char c_file[1024];
+ sprintf(c_file, "recordlock_test_%d", mypid);
+ Fh *fh = NULL;
+ Inode *root = NULL, *inode = NULL;
+ struct ceph_statx stx;
+ struct flock lock1;
+ int rc;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ // Get the root inode
+ rc = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(rc, 0);
+
+ // Get the inode and Fh corresponding to c_file
+ rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
+ &inode, &fh, &stx, 0, 0, perms);
+ ASSERT_EQ(rc, 0);
+
+ // Lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), true));
+
+ // Start locker thread
+ pthread_t thread;
+ struct timespec ts;
+ str_ConcurrentRecordLocking s = { c_file, cmount };
+ s.sem_init(0);
+ ASSERT_EQ(0, pthread_create(&thread, NULL, thread_ConcurrentRecordLocking_, &s));
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(1); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Shall have lock
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(3); // (3)
+
+ // Wait for thread to share lock
+ WAIT_WORKER(4); // (4)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Wake up thread to unlock shared lock
+ PING_WORKER(1); // (R1)
+ WAIT_WORKER(5); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), true));
+
+ // Wake up thread to lock shared lock
+ PING_WORKER(2); // (R2)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6); // (6)
+
+ // Release lock ; thread will get it
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Wake up thread to unlock exclusive lock
+ PING_WORKER(3); // (R3)
+ WAIT_WORKER(7); // (7)
+
+ // We can lock it again
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Cleanup
+ void *retval = (void*) (uintptr_t) -1;
+ ASSERT_EQ(0, pthread_join(thread, &retval));
+ ASSERT_EQ(NULL, retval);
+ s.sem_destroy();
+ ASSERT_EQ(0, ceph_ll_close(cmount, fh));
+ ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
+ CLEANUP_CEPH();
+}
+
+TEST(LibCephFS, ThreesomeRecordLocking) {
+ const pid_t mypid = getpid();
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+
+ char c_file[1024];
+ sprintf(c_file, "recordlock_test_%d", mypid);
+ Fh *fh = NULL;
+ Inode *root = NULL, *inode = NULL;
+ struct ceph_statx stx;
+ struct flock lock1;
+ int rc;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ // Get the root inode
+ rc = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(rc, 0);
+
+ // Get the inode and Fh corresponding to c_file
+ rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
+ &inode, &fh, &stx, 0, 0, perms);
+ ASSERT_EQ(rc, 0);
+
+ // Lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), true));
+
+ // Start locker thread
+ pthread_t thread[2];
+ struct timespec ts;
+ str_ConcurrentRecordLocking s = { c_file, cmount };
+ s.sem_init(0);
+ ASSERT_EQ(0, pthread_create(&thread[0], NULL, thread_ConcurrentRecordLocking_, &s));
+ ASSERT_EQ(0, pthread_create(&thread[1], NULL, thread_ConcurrentRecordLocking_, &s));
+ // Synchronization point with thread (failure: thread is dead)
+ TWICE(WAIT_WORKER(1)); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Shall have lock
+ TWICE(// Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with thread (failure: thread is dead)
+ WAIT_WORKER(3)); // (3)
+
+ // Wait for thread to share lock
+ TWICE(WAIT_WORKER(4)); // (4)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Wake up thread to unlock shared lock
+ TWICE(PING_WORKER(1); // (R1)
+ WAIT_WORKER(5)); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), true));
+
+ TWICE( // Wake up thread to lock shared lock
+ PING_WORKER(2); // (R2)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6)); // (6)
+
+ // Release lock ; thread will get it
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ TWICE(WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Wake up thread to unlock exclusive lock
+ PING_WORKER(3); // (R3)
+ WAIT_WORKER(7); // (7)
+ );
+
+ // We can lock it again
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Cleanup
+ void *retval = (void*) (uintptr_t) -1;
+ ASSERT_EQ(0, pthread_join(thread[0], &retval));
+ ASSERT_EQ(NULL, retval);
+ ASSERT_EQ(0, pthread_join(thread[1], &retval));
+ ASSERT_EQ(NULL, retval);
+ s.sem_destroy();
+ ASSERT_EQ(0, ceph_ll_close(cmount, fh));
+ ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
+ CLEANUP_CEPH();
+}
+
+/* Locking in different processes */
+
+#define PROCESS_SLOW_MS() \
+ static const long waitMs = 100; \
+ (void) waitMs
+
+// Used by ConcurrentLocking test
+static void process_ConcurrentRecordLocking(str_ConcurrentRecordLocking& s) {
+ const pid_t mypid = getpid();
+ PROCESS_SLOW_MS();
+
+ struct ceph_mount_info *cmount = NULL;
+ struct timespec ts;
+ Fh *fh = NULL;
+ Inode *root = NULL, *inode = NULL;
+ struct ceph_statx stx;
+ int rc;
+ struct flock lock1;
+
+ STARTUP_CEPH();
+ s.cmount = cmount;
+
+ // Get the root inode
+ rc = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(rc, 0);
+
+ // Get the inode and Fh corresponding to c_file
+ rc = ceph_ll_create(cmount, root, s.file, fileMode, O_RDWR | O_CREAT,
+ &inode, &fh, &stx, 0, 0, ceph_mount_perms(cmount));
+ ASSERT_EQ(rc, 0);
+
+ WAIT_MAIN(1); // (R1)
+
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ PING_MAIN(1); // (1)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
+ PING_MAIN(2); // (2)
+
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ PING_MAIN(3); // (3)
+
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
+ PING_MAIN(4); // (4)
+
+ WAIT_MAIN(2); // (R2)
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ PING_MAIN(5); // (5)
+
+ WAIT_MAIN(3); // (R3)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
+ PING_MAIN(6); // (6)
+
+ WAIT_MAIN(4); // (R4)
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ PING_MAIN(7); // (7)
+
+ ASSERT_EQ(0, ceph_ll_close(cmount, fh));
+ CLEANUP_CEPH();
+
+ s.sem_destroy();
+ exit(EXIT_SUCCESS);
+}
+
+// Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
+#ifndef _WIN32
+TEST(LibCephFS, DISABLED_InterProcessRecordLocking) {
+ PROCESS_SLOW_MS();
+ // Process synchronization
+ char c_file[1024];
+ const pid_t mypid = getpid();
+ sprintf(c_file, "recordlock_test_%d", mypid);
+ Fh *fh = NULL;
+ Inode *root = NULL, *inode = NULL;
+ struct ceph_statx stx;
+ struct flock lock1;
+ int rc;
+
+ // Note: the semaphores MUST be on a shared memory segment
+ str_ConcurrentRecordLocking *const shs =
+ reinterpret_cast<str_ConcurrentRecordLocking*>
+ (mmap(0, sizeof(*shs), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
+ -1, 0));
+ str_ConcurrentRecordLocking &s = *shs;
+ s.file = c_file;
+ s.sem_init(1);
+
+ // Start locker process
+ const pid_t pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0) {
+ process_ConcurrentRecordLocking(s);
+ exit(EXIT_FAILURE);
+ }
+
+ struct timespec ts;
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ // Get the root inode
+ rc = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(rc, 0);
+
+ // Get the inode and Fh corresponding to c_file
+ rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
+ &inode, &fh, &stx, 0, 0, perms);
+ ASSERT_EQ(rc, 0);
+
+ // Lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
+
+ // Synchronization point with process (failure: process is dead)
+ PING_WORKER(1); // (R1)
+ WAIT_WORKER(1); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+
+ // Shall have lock
+ // Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(3); // (3)
+
+ // Wait for process to share lock
+ WAIT_WORKER(4); // (4)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+
+ // Wake up process to unlock shared lock
+ PING_WORKER(2); // (R2)
+ WAIT_WORKER(5); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
+
+ // Wake up process to lock shared lock
+ PING_WORKER(3); // (R3)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6); // (6)
+
+ // Release lock ; process will get it
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+
+ // Wake up process to unlock exclusive lock
+ PING_WORKER(4); // (R4)
+ WAIT_WORKER(7); // (7)
+
+ // We can lock it again
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+
+ // Wait pid
+ int status;
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_EQ(EXIT_SUCCESS, status);
+
+ // Cleanup
+ s.sem_destroy();
+ ASSERT_EQ(0, munmap(shs, sizeof(*shs)));
+ ASSERT_EQ(0, ceph_ll_close(cmount, fh));
+ ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
+ CLEANUP_CEPH();
+}
+#endif
+
+#ifndef _WIN32
+// Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
+TEST(LibCephFS, DISABLED_ThreesomeInterProcessRecordLocking) {
+ PROCESS_SLOW_MS();
+ // Process synchronization
+ char c_file[1024];
+ const pid_t mypid = getpid();
+ sprintf(c_file, "recordlock_test_%d", mypid);
+ Fh *fh = NULL;
+ Inode *root = NULL, *inode = NULL;
+ struct ceph_statx stx;
+ struct flock lock1;
+ int rc;
+
+ // Note: the semaphores MUST be on a shared memory segment
+ str_ConcurrentRecordLocking *const shs =
+ reinterpret_cast<str_ConcurrentRecordLocking*>
+ (mmap(0, sizeof(*shs), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
+ -1, 0));
+ str_ConcurrentRecordLocking &s = *shs;
+ s.file = c_file;
+ s.sem_init(1);
+
+ // Start locker processes
+ pid_t pid[2];
+ pid[0] = fork();
+ ASSERT_GE(pid[0], 0);
+ if (pid[0] == 0) {
+ process_ConcurrentRecordLocking(s);
+ exit(EXIT_FAILURE);
+ }
+ pid[1] = fork();
+ ASSERT_GE(pid[1], 0);
+ if (pid[1] == 0) {
+ process_ConcurrentRecordLocking(s);
+ exit(EXIT_FAILURE);
+ }
+
+ struct timespec ts;
+ struct ceph_mount_info *cmount;
+ STARTUP_CEPH();
+
+ // Get the root inode
+ rc = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(rc, 0);
+
+ // Get the inode and Fh corresponding to c_file
+ UserPerm *perms = ceph_mount_perms(cmount);
+ rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
+ &inode, &fh, &stx, 0, 0, perms);
+ ASSERT_EQ(rc, 0);
+
+ // Lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
+
+ // Synchronization point with process (failure: process is dead)
+ TWICE(PING_WORKER(1)); // (R1)
+ TWICE(WAIT_WORKER(1)); // (1)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(2); // (2)
+
+ // Unlock
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+
+ // Shall have lock
+ TWICE(// Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(2); // (2)
+
+ // Synchronization point with process (failure: process is dead)
+ WAIT_WORKER(3)); // (3)
+
+ // Wait for process to share lock
+ TWICE(WAIT_WORKER(4)); // (4)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+
+ // Wake up process to unlock shared lock
+ TWICE(PING_WORKER(2); // (R2)
+ WAIT_WORKER(5)); // (5)
+
+ // Now we can lock exclusively
+ // Upgrade to exclusive lock (as per POSIX)
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
+
+ TWICE( // Wake up process to lock shared lock
+ PING_WORKER(3); // (R3)
+
+ // Shall not have lock immediately
+ NOT_WAIT_WORKER(6)); // (6)
+
+ // Release lock ; process will get it
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ TWICE(WAIT_WORKER(6); // (6)
+
+ // We no longer have the lock
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+ lock1.l_type = F_RDLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(-CEPHFS_EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, ceph_pthread_self(), false));
+
+ // Wake up process to unlock exclusive lock
+ PING_WORKER(4); // (R4)
+ WAIT_WORKER(7); // (7)
+ );
+
+ // We can lock it again
+ lock1.l_type = F_WRLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+ lock1.l_type = F_UNLCK;
+ lock1.l_whence = SEEK_SET;
+ lock1.l_start = 0;
+ lock1.l_len = 1024;
+ lock1.l_pid = getpid();
+ ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
+
+ // Wait pids
+ int status;
+ ASSERT_EQ(pid[0], waitpid(pid[0], &status, 0));
+ ASSERT_EQ(EXIT_SUCCESS, status);
+ ASSERT_EQ(pid[1], waitpid(pid[1], &status, 0));
+ ASSERT_EQ(EXIT_SUCCESS, status);
+
+ // Cleanup
+ s.sem_destroy();
+ ASSERT_EQ(0, munmap(shs, sizeof(*shs)));
+ ASSERT_EQ(0, ceph_ll_close(cmount, fh));
+ ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
+ CLEANUP_CEPH();
+}
+#endif
diff --git a/src/test/libcephfs/snapdiff.cc b/src/test/libcephfs/snapdiff.cc
new file mode 100644
index 000000000..2320bf58b
--- /dev/null
+++ b/src/test/libcephfs/snapdiff.cc
@@ -0,0 +1,1684 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/stat.h"
+#include "include/ceph_assert.h"
+#include "include/object.h"
+#include "include/stringify.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <limits.h>
+#include <dirent.h>
+
+using namespace std;
+class TestMount {
+ ceph_mount_info* cmount = nullptr;
+ char dir_path[64];
+
+public:
+ TestMount( const char* root_dir_name = "dir0") {
+ ceph_create(&cmount, NULL);
+ ceph_conf_read_file(cmount, NULL);
+ ceph_conf_parse_env(cmount, NULL);
+ ceph_assert(0 == ceph_mount(cmount, NULL));
+
+ sprintf(dir_path, "/%s_%d", root_dir_name, getpid());
+ ceph_assert(0 == ceph_mkdir(cmount, dir_path, 0777));
+ }
+ ~TestMount()
+ {
+ if (cmount) {
+ ceph_assert(0 == purge_dir(""));
+ }
+ ceph_rmdir(cmount, dir_path);
+ ceph_shutdown(cmount);
+ }
+
+ int conf_get(const char *option, char *buf, size_t len) {
+ return ceph_conf_get(cmount, option, buf, len);
+ }
+
+ string make_file_path(const char* relpath) {
+ char path[PATH_MAX];
+ sprintf(path, "%s/%s", dir_path, relpath);
+ return path;
+ }
+
+ string make_snap_name(const char* name) {
+ char snap_name[64];
+ if (name && *name) {
+ sprintf(snap_name, "%s_%d", name, getpid());
+ } else {
+ // just simulate empty snapname
+ snap_name[0] = 0;
+ }
+ return snap_name;
+ }
+ string make_snap_path(const char* sname, const char* subdir = nullptr) {
+ char snap_path[PATH_MAX];
+ string snap_name = subdir ?
+ concat_path(make_snap_name(sname), subdir) :
+ make_snap_name(sname);
+ sprintf(snap_path, ".snap/%s", snap_name.c_str());
+ return snap_path;
+ }
+
+ int mksnap(const char* name) {
+ string snap_name = make_snap_name(name);
+ return ceph_mksnap(cmount, dir_path, snap_name.c_str(),
+ 0755, nullptr, 0);
+ }
+ int rmsnap(const char* name) {
+ string snap_name = make_snap_name(name);
+ return ceph_rmsnap(cmount, dir_path, snap_name.c_str());
+ }
+ int get_snapid(const char* name, uint64_t* res)
+ {
+ ceph_assert(res);
+ snap_info snap_info;
+
+ char snap_path[PATH_MAX];
+ string snap_name = make_snap_name(name);
+ sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name.c_str());
+ int r = ceph_get_snap_info(cmount, snap_path, &snap_info);
+ if (r >= 0) {
+ *res = snap_info.id;
+ r = 0;
+ }
+ return r;
+ }
+
+ int write_full(const char* relpath, const string& data)
+ {
+ auto file_path = make_file_path(relpath);
+ int fd = ceph_open(cmount, file_path.c_str(), O_WRONLY | O_CREAT, 0666);
+ if (fd < 0) {
+ return -EACCES;
+ }
+ int r = ceph_write(cmount, fd, data.c_str(), data.size(), 0);
+ if (r >= 0) {
+ ceph_truncate(cmount, file_path.c_str(), data.size());
+ ceph_fsync(cmount, fd, 0);
+ }
+ ceph_close(cmount, fd);
+ return r;
+ }
+ string concat_path(string_view path, string_view name) {
+ string s(path);
+ if (s.empty() || s.back() != '/') {
+ s += '/';
+ }
+ s += name;
+ return s;
+ }
+ int unlink(const char* relpath)
+ {
+ auto file_path = make_file_path(relpath);
+ return ceph_unlink(cmount, file_path.c_str());
+ }
+
+ int test_open(const char* relpath)
+ {
+ auto subdir_path = make_file_path(relpath);
+ struct ceph_dir_result* ls_dir;
+ int r = ceph_opendir(cmount, subdir_path.c_str(), &ls_dir);
+ if (r != 0) {
+ return r;
+ }
+ ceph_assert(0 == ceph_closedir(cmount, ls_dir));
+ return r;
+ }
+
+ int for_each_readdir(const char* relpath,
+ std::function<bool(const dirent*, const struct ceph_statx*)> fn)
+ {
+ auto subdir_path = make_file_path(relpath);
+ struct ceph_dir_result* ls_dir;
+ int r = ceph_opendir(cmount, subdir_path.c_str(), &ls_dir);
+ if (r != 0) {
+ return r;
+ }
+
+ while (1) {
+ struct dirent result;
+ struct ceph_statx stx;
+
+ r = ceph_readdirplus_r(
+ cmount, ls_dir, &result, &stx, CEPH_STATX_BASIC_STATS,
+ 0,
+ NULL);
+ if (!r)
+ break;
+ if (r < 0) {
+ std::cerr << "ceph_readdirplus_r failed, error: "
+ << r << std::endl;
+ return r;
+ }
+
+ if (strcmp(result.d_name, ".") == 0 ||
+ strcmp(result.d_name, "..") == 0) {
+ continue;
+ }
+ if (!fn(&result, &stx)) {
+ r = -EINTR;
+ break;
+ }
+ }
+ ceph_assert(0 == ceph_closedir(cmount, ls_dir));
+ return r;
+ }
+ int readdir_and_compare(const char* relpath,
+ const vector<string>& expected0)
+ {
+ vector<string> expected(expected0);
+ auto end = expected.end();
+ int r = for_each_readdir(relpath,
+ [&](const dirent* dire, const struct ceph_statx* stx) {
+
+ std::string name(dire->d_name);
+ auto it = std::find(expected.begin(), end, name);
+ if (it == end) {
+ std::cerr << "readdir_and_compare error: unexpected name:"
+ << name << std::endl;
+ return false;
+ }
+ expected.erase(it);
+ return true;
+ });
+ if (r == 0 && !expected.empty()) {
+ std::cerr << __func__ << " error: left entries:" << std::endl;
+ for (auto& e : expected) {
+ std::cerr << e << std::endl;
+ }
+ std::cerr << __func__ << " ************" << std::endl;
+ r = -ENOTEMPTY;
+ }
+ return r;
+ }
+ int for_each_readdir_snapdiff(const char* relpath,
+ const char* snap1,
+ const char* snap2,
+ std::function<bool(const dirent*, uint64_t)> fn)
+ {
+ auto s1 = make_snap_name(snap1);
+ auto s2 = make_snap_name(snap2);
+ ceph_snapdiff_info info;
+ ceph_snapdiff_entry_t res_entry;
+ int r = ceph_open_snapdiff(cmount,
+ dir_path,
+ relpath,
+ s1.c_str(),
+ s2.c_str(),
+ &info);
+ if (r != 0) {
+ std::cerr << " Failed to open snapdiff, ret:" << r << std::endl;
+ return r;
+ }
+ while (0 < (r = ceph_readdir_snapdiff(&info,
+ &res_entry))) {
+ if (strcmp(res_entry.dir_entry.d_name, ".") == 0 ||
+ strcmp(res_entry.dir_entry.d_name, "..") == 0) {
+ continue;
+ }
+ if (!fn(&res_entry.dir_entry, res_entry.snapid)) {
+ r = -EINTR;
+ break;
+ }
+ }
+ ceph_assert(0 == ceph_close_snapdiff(&info));
+ if (r != 0) {
+ std::cerr << " Failed to readdir snapdiff, ret:" << r
+ << " " << relpath << ", " << snap1 << " vs. " << snap2
+ << std::endl;
+ }
+ return r;
+ }
+ int readdir_snapdiff_and_compare(const char* relpath,
+ const char* snap1,
+ const char* snap2,
+ const vector<pair<string, uint64_t>>& expected0)
+ {
+ vector<pair<string, uint64_t>> expected(expected0);
+ auto end = expected.end();
+ int r = for_each_readdir_snapdiff(relpath, snap1, snap2,
+ [&](const dirent* dire, uint64_t snapid) {
+
+ pair<string, uint64_t> p = std::make_pair(dire->d_name, snapid);
+ auto it = std::find(expected.begin(), end, p);
+ if (it == end) {
+ std::cerr << "readdir_snapdiff_and_compare error: unexpected name:"
+ << dire->d_name << "/" << snapid << std::endl;
+ return false;
+ }
+ expected.erase(it);
+ return true;
+ });
+ if (r == 0 && !expected.empty()) {
+ std::cerr << __func__ << " error: left entries:" << std::endl;
+ for (auto& e : expected) {
+ std::cerr << e.first << "/" << e.second << std::endl;
+ }
+ std::cerr << __func__ << " ************" << std::endl;
+ r = -ENOTEMPTY;
+ }
+ return r;
+ }
+
+ int mkdir(const char* relpath)
+ {
+ auto path = make_file_path(relpath);
+ return ceph_mkdir(cmount, path.c_str(), 0777);
+ }
+ int rmdir(const char* relpath)
+ {
+ auto path = make_file_path(relpath);
+ return ceph_rmdir(cmount, path.c_str());
+ }
+ int purge_dir(const char* relpath0, bool inclusive = true)
+ {
+ int r =
+ for_each_readdir(relpath0,
+ [&](const dirent* dire, const struct ceph_statx* stx) {
+ string relpath = concat_path(relpath0, dire->d_name);
+ if (S_ISDIR(stx->stx_mode)) {
+ purge_dir(relpath.c_str());
+ rmdir(relpath.c_str());
+ } else {
+ unlink(relpath.c_str());
+ }
+ return true;
+ });
+ if (r != 0) {
+ return r;
+ }
+ if (*relpath0 != 0) {
+ r = rmdir(relpath0);
+ }
+ return r;
+ }
+
+ void remove_all() {
+ purge_dir("/", false);
+ }
+
+ ceph_mount_info* get_cmount() {
+ return cmount;
+ }
+
+ void verify_snap_diff(vector<pair<string, uint64_t>>& expected,
+ const char* relpath,
+ const char* snap1,
+ const char* snap2);
+ void print_snap_diff(const char* relpath,
+ const char* snap1,
+ const char* snap2);
+
+ void prepareSnapDiffLib1Cases();
+ void prepareSnapDiffLib2Cases();
+ void prepareSnapDiffLib3Cases();
+ void prepareHugeSnapDiff(const std::string& name_prefix_start,
+ const std::string& name_prefix_bulk,
+ const std::string& name_prefix_end,
+ size_t file_count,
+ bool bulk_diff);
+};
+
+// Helper function to verify readdir_snapdiff returns expected results
+void TestMount::verify_snap_diff(vector<pair<string, uint64_t>>& expected,
+ const char* relpath,
+ const char* snap1,
+ const char* snap2)
+{
+ std::cout << "---------" << snap1 << " vs. " << snap2
+ << " diff listing verification for /" << (relpath ? relpath : "")
+ << std::endl;
+ ASSERT_EQ(0,
+ readdir_snapdiff_and_compare(relpath, snap1, snap2, expected));
+};
+
+// Helper function to print readdir_snapdiff results
+void TestMount::print_snap_diff(const char* relpath,
+ const char* snap1,
+ const char* snap2)
+{
+ std::cout << "---------" << snap1 << " vs. " << snap2
+ << " diff listing for /" << (relpath ? relpath : "")
+ << std::endl;
+ ASSERT_EQ(0, for_each_readdir_snapdiff(relpath, snap1, snap2,
+ [&](const dirent* dire, uint64_t snapid) {
+ std::cout << dire->d_name << " snap " << snapid << std::endl;
+ return true;
+ }));
+};
+
+
+/* The following method creates some files/folders/snapshots layout,
+ described in the sheet below.
+ We're to test SnapDiff readdir API against that structure.
+
+* where:
+ - xN denotes file 'x' version N.
+ - X denotes folder name
+ - * denotes no/removed file/folder
+
+# snap1 snap2
+# fileA1 | fileA2 |
+# * | fileB2 |
+# fileC1 | * |
+# fileD1 | fileD1 |
+# dirA | dirA |
+# dirA/fileA1 | dirA/fileA2 |
+# * | dirB |
+# * | dirB/fileb2 |
+# dirC | * |
+# dirC/filec1 | * |
+# dirD | dirD |
+# dirD/fileD1 | dirD/fileD1 |
+*/
+void TestMount::prepareSnapDiffLib1Cases()
+{
+ //************ snap1 *************
+ ASSERT_LE(0, write_full("fileA", "hello world"));
+ ASSERT_LE(0, write_full("fileC", "hello world to be removed"));
+ ASSERT_LE(0, write_full("fileD", "hello world unmodified"));
+ ASSERT_EQ(0, mkdir("dirA"));
+ ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v1"));
+ ASSERT_EQ(0, mkdir("dirC"));
+ ASSERT_LE(0, write_full("dirC/filec", "file 'C/c' v1"));
+ ASSERT_EQ(0, mkdir("dirD"));
+ ASSERT_LE(0, write_full("dirD/filed", "file 'D/d' v1"));
+
+ ASSERT_EQ(0, mksnap("snap1"));
+
+ //************ snap2 *************
+ ASSERT_LE(0, write_full("fileA", "hello world again in A"));
+ ASSERT_LE(0, write_full("fileB", "hello world in B"));
+ ASSERT_EQ(0, unlink("fileC"));
+
+ ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v2"));
+ ASSERT_EQ(0, purge_dir("dirC"));
+ ASSERT_EQ(0, mkdir("dirB"));
+ ASSERT_LE(0, write_full("dirB/fileb", "file 'B/b' v2"));
+
+ ASSERT_EQ(0, mksnap("snap2"));
+}
+
+/*
+* Basic functionality testing for the SnapDiff readdir API
+*/
+TEST(LibCephFS, SnapDiffLib)
+{
+ TestMount test_mount;
+
+ // Create simple directory tree with a couple of snapshots
+ // to test against
+ test_mount.prepareSnapDiffLib1Cases();
+
+ uint64_t snapid1;
+ uint64_t snapid2;
+
+ // learn snapshot ids and do basic verification
+ ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1));
+ ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2));
+ ASSERT_GT(snapid1, 0);
+ ASSERT_GT(snapid2, 0);
+ ASSERT_GT(snapid2, snapid1);
+ std::cout << snapid1 << " vs. " << snapid2 << std::endl;
+
+ //
+ // Make sure root listing for snapshot snap1 is as expected
+ //
+ {
+ std::cout << "---------snap1 listing verification---------" << std::endl;
+ string snap_path = test_mount.make_snap_path("snap1");
+ vector<string> expected;
+ expected.push_back("fileA");
+ expected.push_back("fileC");
+ expected.push_back("fileD");
+ expected.push_back("dirA");
+ expected.push_back("dirC");
+ expected.push_back("dirD");
+ ASSERT_EQ(0,
+ test_mount.readdir_and_compare(snap_path.c_str(), expected));
+ }
+
+ //
+ // Make sure root listing for snapshot snap2 is as expected
+ //
+ {
+ std::cout << "---------snap2 listing verification---------" << std::endl;
+ string snap_path = test_mount.make_snap_path("snap2");
+ vector<string> expected;
+ expected.push_back("fileA");
+ expected.push_back("fileB");
+ expected.push_back("fileD");
+ expected.push_back("dirA");
+ expected.push_back("dirB");
+ expected.push_back("dirD");
+ ASSERT_EQ(0,
+ test_mount.readdir_and_compare(snap_path.c_str(), expected));
+ }
+
+ //
+ // Print snap1 vs. snap2 delta for the root
+ //
+ test_mount.print_snap_diff("", "snap1", "snap2");
+
+ //
+ // Make sure snap1 vs. snap2 delta for the root is as expected
+ //
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back("fileA", snapid2);
+ expected.emplace_back("fileB", snapid2);
+ expected.emplace_back("fileC", snapid1);
+ expected.emplace_back("dirA", snapid2);
+ expected.emplace_back("dirB", snapid2);
+ expected.emplace_back("dirC", snapid1);
+ expected.emplace_back("dirD", snapid2);
+ test_mount.verify_snap_diff(expected, "", "snap1", "snap2");
+ }
+
+ //
+ // Make sure snap1 vs. snap2 delta for /dirA is as expected
+ //
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back("fileA", snapid2);
+ test_mount.verify_snap_diff(expected, "dirA", "snap1", "snap2");
+ }
+
+ //
+ // Make sure snap1 vs. snap2 delta for /dirB is as expected
+ //
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back("fileb", snapid2);
+ test_mount.verify_snap_diff(expected, "dirB", "snap1", "snap2");
+ }
+
+ //
+ // Make sure snap1 vs. snap2 delta for /dirC is as expected
+ //
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back("filec", snapid1);
+ test_mount.verify_snap_diff(expected, "dirC", "snap2", "snap1");
+ }
+
+ //
+ // Make sure snap1 vs. snap2 delta for /dirD is as expected
+ //
+ {
+ vector<pair<string, uint64_t>> expected;
+ test_mount.verify_snap_diff(expected, "dirD", "snap1", "snap2");
+ }
+
+ // Make sure SnapDiff returns an error when provided with the same
+ // snapshot name for both parties A and B.
+ {
+ string snap_path = test_mount.make_snap_path("snap2");
+ string snap_other_path = snap_path;
+ std::cout << "---------invalid snapdiff params, the same snaps---------" << std::endl;
+ ASSERT_EQ(-EINVAL, test_mount.for_each_readdir_snapdiff(
+ "",
+ "snap2",
+ "snap2",
+ [&](const dirent* dire, uint64_t snapid) {
+ return true;
+ }));
+ }
+ // Make sure SnapDiff returns an error when provided with an empty
+ // snapshot name for one of the parties
+ {
+ std::cout << "---------invalid snapdiff params, no snap_other ---------" << std::endl;
+ string snap_path = test_mount.make_snap_path("snap2");
+ string snap_other_path;
+ ASSERT_EQ(-EINVAL, test_mount.for_each_readdir_snapdiff(
+ "",
+ "snap2",
+ "",
+ [&](const dirent* dire, uint64_t snapid) {
+ return true;
+ }));
+ }
+
+ std::cout << "------------- closing -------------" << std::endl;
+ ASSERT_EQ(0, test_mount.purge_dir(""));
+ ASSERT_EQ(0, test_mount.rmsnap("snap1"));
+ ASSERT_EQ(0, test_mount.rmsnap("snap2"));
+}
+
+/* The following method creates some files/folders/snapshots layout,
+ described in the sheet below.
+ We're to test SnapDiff readdir API against that structure.
+
+* where:
+ - xN denotes file 'x' version N.
+ - X denotes folder name
+ - * denotes no/removed file/folder
+
+# snap1 snap2 snap3 head
+# fileA1 | fileA2 | fileA2
+# * | fileB2 | fileB2
+# fileC1 | * | fileC3
+# fileD1 | fileD1 | fileD3
+# * | * | fileE3
+# fileF1 | * | *
+# fileG1 | fileG2 | *
+# dirA | dirA | *
+# dirA/fileA1 | dirA/fileA2 | *
+# * | dirB | *
+# * | dirB/fileb2 | *
+# dirC | * | *
+# dirC/filec1 | * | *
+# dirD | dirD | dirD
+# dirD/filed1 | dirD/filed1 | dirD/filed1
+*/
+void TestMount::prepareSnapDiffLib2Cases()
+{
+ //************ snap1 *************
+ ASSERT_LE(0, write_full("fileA", "hello world"));
+ ASSERT_LE(0, write_full("fileC", "hello world to be removed temporarily"));
+ ASSERT_LE(0, write_full("fileD", "hello world unmodified"));
+ ASSERT_LE(0, write_full("fileF", "hello world to be removed completely"));
+ ASSERT_LE(0, write_full("fileG", "hello world to be overwritten at snap2"));
+ ASSERT_EQ(0, mkdir("dirA"));
+ ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v1"));
+ ASSERT_EQ(0, mkdir("dirC"));
+ ASSERT_LE(0, write_full("dirC/filec", "file 'C/c' v1"));
+ ASSERT_EQ(0, mkdir("dirD"));
+ ASSERT_LE(0, write_full("dirD/filed", "file 'D/d' v1"));
+
+ ASSERT_EQ(0, mksnap("snap1"));
+
+ //************ snap2 *************
+ ASSERT_LE(0, write_full("fileA", "hello world again in A"));
+ ASSERT_LE(0, write_full("fileB", "hello world in B"));
+ ASSERT_LE(0, write_full("fileG", "hello world to be removed at snap3"));
+ ASSERT_EQ(0, unlink("fileC"));
+ ASSERT_EQ(0, unlink("fileF"));
+
+ ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v2"));
+ ASSERT_EQ(0, mkdir("dirB"));
+ ASSERT_LE(0, write_full("dirB/fileb", "file 'B/b' v2"));
+ ASSERT_EQ(0, purge_dir("dirC"));
+
+ ASSERT_EQ(0, mksnap("snap2"));
+
+ //************ snap3 *************
+ ASSERT_LE(0, write_full("fileC", "hello world in C recovered"));
+ ASSERT_LE(0, write_full("fileD", "hello world in D now modified"));
+ ASSERT_LE(0, write_full("fileE", "file 'E' created at snap3"));
+ ASSERT_EQ(0, unlink("fileG"));
+ ASSERT_EQ(0, purge_dir("dirA"));
+ ASSERT_EQ(0, purge_dir("dirB"));
+ ASSERT_EQ(0, mksnap("snap3"));
+}
+
+/* The following method creates a folder with tons of file
+ updated between two snapshots
+ We're to test SnapDiff readdir API against that structure.
+
+* where:
+ - xN denotes file 'x' version N.
+ - X denotes folder name
+ - * denotes no/removed file/folder
+
+# snap1 snap2
+* aaaaA1 | aaaaA1 |
+* aaaaB1 | * |
+* * | aaaaC2 |
+* aaaaD1 | aaaaD2 |
+# file<NNN>1 | file<NNN>2|
+* fileZ1 | fileA1 |
+* zzzzA1 | zzzzA1 |
+* zzzzB1 | * |
+* * | zzzzC2 |
+* zzzzD1 | zzzzD2 |
+*/
+
+void TestMount::prepareHugeSnapDiff(const std::string& name_prefix_start,
+ const std::string& name_prefix_bulk,
+ const std::string& name_prefix_end,
+ size_t file_count,
+ bool bulk_diff)
+{
+ //************ snap1 *************
+ std::string startA = name_prefix_start + "A";
+ std::string startB = name_prefix_start + "B";
+ std::string startC = name_prefix_start + "C";
+ std::string startD = name_prefix_start + "D";
+ std::string endA = name_prefix_end + "A";
+ std::string endB = name_prefix_end + "B";
+ std::string endC = name_prefix_end + "C";
+ std::string endD = name_prefix_end + "D";
+
+ ASSERT_LE(0, write_full(startA.c_str(), "hello world"));
+ ASSERT_LE(0, write_full(startB.c_str(), "hello world"));
+ ASSERT_LE(0, write_full(startD.c_str(), "hello world"));
+ for(size_t i = 0; i < file_count; i++) {
+ auto s = name_prefix_bulk + stringify(i);
+ ASSERT_LE(0, write_full(s.c_str(), "hello world"));
+ }
+ ASSERT_LE(0, write_full(endA.c_str(), "hello world"));
+ ASSERT_LE(0, write_full(endB.c_str(), "hello world"));
+ ASSERT_LE(0, write_full(endD.c_str(), "hello world"));
+
+ ASSERT_EQ(0, mksnap("snap1"));
+
+ ASSERT_LE(0, unlink(startB.c_str()));
+ ASSERT_LE(0, write_full(startC.c_str(), "hello world2"));
+ ASSERT_LE(0, write_full(startD.c_str(), "hello world2"));
+ if (bulk_diff) {
+ for(size_t i = 0; i < file_count; i++) {
+ auto s = std::string(name_prefix_bulk) + stringify(i);
+ ASSERT_LE(0, write_full(s.c_str(), "hello world2"));
+ }
+ }
+ ASSERT_LE(0, unlink(endB.c_str()));
+ ASSERT_LE(0, write_full(endC.c_str(), "hello world2"));
+ ASSERT_LE(0, write_full(endD.c_str(), "hello world2"));
+ ASSERT_EQ(0, mksnap("snap2"));
+}
+
+/*
+* More versatile SnapDiff readdir API verification,
+* includes 3 different snapshots and interleaving/repetitive calls to make sure
+* the results aren't spoiled due to caching.
+*/
+TEST(LibCephFS, SnapDiffLib2)
+{
+ TestMount test_mount;
+
+ test_mount.prepareSnapDiffLib2Cases();
+
+ // Create simple directory tree with a couple of snapshots to test against
+ uint64_t snapid1;
+ uint64_t snapid2;
+ uint64_t snapid3;
+ ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1));
+ ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2));
+ ASSERT_EQ(0, test_mount.get_snapid("snap3", &snapid3));
+ std::cout << snapid1 << " vs. " << snapid2 << " vs. " << snapid3 << std::endl;
+ ASSERT_GT(snapid1, 0);
+ ASSERT_GT(snapid2, 0);
+ ASSERT_GT(snapid3, 0);
+ ASSERT_GT(snapid2, snapid1);
+ ASSERT_GT(snapid3, snapid2);
+
+ // define a labda which verifies snap1/snap2/snap3 listings
+ auto verify_snap_listing = [&]()
+ {
+ {
+ string snap_path = test_mount.make_snap_path("snap1");
+
+ std::cout << "---------snap1 listing verification---------" << std::endl;
+ vector<string> expected;
+ expected.push_back("fileA");
+ expected.push_back("fileC");
+ expected.push_back("fileD");
+ expected.push_back("fileF");
+ expected.push_back("fileG");
+ expected.push_back("dirA");
+ expected.push_back("dirC");
+ expected.push_back("dirD");
+ ASSERT_EQ(0,
+ test_mount.readdir_and_compare(snap_path.c_str(), expected));
+ }
+ {
+ std::cout << "---------snap2 listing verification---------" << std::endl;
+ string snap_path = test_mount.make_snap_path("snap2");
+ vector<string> expected;
+ expected.push_back("fileA");
+ expected.push_back("fileB");
+ expected.push_back("fileD");
+ expected.push_back("fileG");
+ expected.push_back("dirA");
+ expected.push_back("dirB");
+ expected.push_back("dirD");
+ ASSERT_EQ(0,
+ test_mount.readdir_and_compare(snap_path.c_str(), expected));
+ }
+ {
+ std::cout << "---------snap3 listing verification---------" << std::endl;
+ string snap_path = test_mount.make_snap_path("snap3");
+ vector<string> expected;
+ expected.push_back("fileA");
+ expected.push_back("fileB");
+ expected.push_back("fileC");
+ expected.push_back("fileD");
+ expected.push_back("fileE");
+ expected.push_back("dirD");
+ ASSERT_EQ(0,
+ test_mount.readdir_and_compare(snap_path.c_str(), expected));
+ }
+ };
+ // Prepare expected delta for snap1 vs. snap2
+ vector<pair<string, uint64_t>> snap1_2_diff_expected;
+ snap1_2_diff_expected.emplace_back("fileA", snapid2);
+ snap1_2_diff_expected.emplace_back("fileB", snapid2);
+ snap1_2_diff_expected.emplace_back("fileC", snapid1);
+ snap1_2_diff_expected.emplace_back("fileF", snapid1);
+ snap1_2_diff_expected.emplace_back("fileG", snapid2);
+ snap1_2_diff_expected.emplace_back("dirA", snapid2);
+ snap1_2_diff_expected.emplace_back("dirB", snapid2);
+ snap1_2_diff_expected.emplace_back("dirC", snapid1);
+ snap1_2_diff_expected.emplace_back("dirD", snapid2);
+
+ // Prepare expected delta for snap1 vs. snap3
+ vector<pair<string, uint64_t>> snap1_3_diff_expected;
+ snap1_3_diff_expected.emplace_back("fileA", snapid3);
+ snap1_3_diff_expected.emplace_back("fileB", snapid3);
+ snap1_3_diff_expected.emplace_back("fileC", snapid3);
+ snap1_3_diff_expected.emplace_back("fileD", snapid3);
+ snap1_3_diff_expected.emplace_back("fileE", snapid3);
+ snap1_3_diff_expected.emplace_back("fileF", snapid1);
+ snap1_3_diff_expected.emplace_back("fileG", snapid1);
+ snap1_3_diff_expected.emplace_back("dirA", snapid1);
+ snap1_3_diff_expected.emplace_back("dirC", snapid1);
+ snap1_3_diff_expected.emplace_back("dirD", snapid3);
+
+ // Prepare expected delta for snap2 vs. snap3
+ vector<pair<string, uint64_t>> snap2_3_diff_expected;
+ snap2_3_diff_expected.emplace_back("fileC", snapid3);
+ snap2_3_diff_expected.emplace_back("fileD", snapid3);
+ snap2_3_diff_expected.emplace_back("fileE", snapid3);
+ snap2_3_diff_expected.emplace_back("fileG", snapid2);
+ snap2_3_diff_expected.emplace_back("dirA", snapid2);
+ snap2_3_diff_expected.emplace_back("dirB", snapid2);
+ snap2_3_diff_expected.emplace_back("dirD", snapid3);
+
+ // Check snapshot listings on a cold cache
+ verify_snap_listing();
+
+ // Check snapshot listings on a warm cache
+ verify_snap_listing(); // served from cache
+
+ // Print snap1 vs. snap2 delta against the root folder
+ test_mount.print_snap_diff("", "snap1", "snap2");
+
+ // Verify snap1 vs. snap2 delta for the root
+ test_mount.verify_snap_diff(snap1_2_diff_expected, "", "snap1", "snap2");
+
+ // Check snapshot listings on a warm cache once again
+ // to make sure it wasn't spoiled by SnapDiff
+ verify_snap_listing(); // served from cache
+
+ // Verify snap2 vs. snap1 delta
+ test_mount.verify_snap_diff(snap1_2_diff_expected, "", "snap2", "snap1");
+
+ // Check snapshot listings on a warm cache once again
+ // to make sure it wasn't spoiled by SnapDiff
+ verify_snap_listing(); // served from cache
+
+ // Verify snap1 vs. snap3 delta for the root
+ test_mount.verify_snap_diff(snap1_3_diff_expected, "", "snap1", "snap3");
+
+ // Verify snap2 vs. snap3 delta for the root
+ test_mount.verify_snap_diff(snap2_3_diff_expected, "", "snap2", "snap3");
+
+ // Check snapshot listings on a warm cache once again
+ // to make sure it wasn't spoiled by SnapDiff
+ verify_snap_listing(); // served from cache
+
+ // Print snap1 vs. snap2 delta against /dirA folder
+ test_mount.print_snap_diff("dirA", "snap1", "snap2");
+
+ // Verify snap1 vs. snap2 delta for /dirA
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back("fileA", snapid2);
+ test_mount.verify_snap_diff(expected, "dirA", "snap1", "snap2");
+ }
+
+ // Print snap1 vs. snap2 delta against /dirB folder
+ test_mount.print_snap_diff("dirB", "snap1", "snap2");
+
+ // Verify snap1 vs. snap2 delta for /dirB
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back("fileb", snapid2);
+ test_mount.verify_snap_diff(expected, "dirB", "snap1", "snap2");
+ }
+
+ // Print snap1 vs. snap2 delta against /dirD folder
+ test_mount.print_snap_diff("dirD", "snap1", "snap2");
+
+ // Verify snap1 vs. snap2 delta for /dirD
+ {
+ vector<pair<string, uint64_t>> expected;
+ test_mount.verify_snap_diff(expected, "dirD", "snap1", "snap2");
+ }
+
+ // Check snapshot listings on a warm cache once again
+ // to make sure it wasn't spoiled by SnapDiff
+ verify_snap_listing(); // served from cache
+
+ // Verify snap1 vs. snap2 delta for the root once again
+ test_mount.verify_snap_diff(snap1_2_diff_expected, "", "snap1", "snap2");
+
+ // Verify snap2 vs. snap3 delta for the root once again
+ test_mount.verify_snap_diff(snap2_3_diff_expected, "", "snap3", "snap2");
+
+ // Verify snap1 vs. snap3 delta for the root once again
+ test_mount.verify_snap_diff(snap1_3_diff_expected, "", "snap1", "snap3");
+
+ std::cout << "------------- closing -------------" << std::endl;
+ ASSERT_EQ(0, test_mount.purge_dir(""));
+ ASSERT_EQ(0, test_mount.rmsnap("snap1"));
+ ASSERT_EQ(0, test_mount.rmsnap("snap2"));
+ ASSERT_EQ(0, test_mount.rmsnap("snap3"));
+}
+
+/* The following method creates some files/folders/snapshots layout,
+ described in the sheet below.
+ We're to test SnapDiff against that structure.
+
+* where:
+ - xN denotes file 'x' version N.
+ - X denotes folder name
+ - * denotes no/removed file/folder
+
+# snap1 snap2 snap3 head
+# a1 | a1 | a3 | a4
+# b1 | b2 | b3 | b3
+# c1 | * | * | *
+# * | d2 | d3 | d3
+# f1 | f2 | * | *
+# ff1 | ff1 | * | *
+# g1 | * | g3 | g3
+# * | * | * | h4
+# i1 | i1 | i1 | i1
+# S | S | S | S
+# S/sa1 | S/sa2 | S/sa3 | S/sa3
+# * | * | * | S/sh4
+# * | T | T | T
+# * | T/td2 | T/td3 | T/td3
+# C | * | * | *
+# C/cc1 | * | * | *
+# C/C1 | * | * | *
+# C/C1/c1| * | * | *
+# G | * | G | G
+# G/gg1 | * | G/gg3 | G/gg3
+# * | k2 | * | *
+# * | l2 | l2 | *
+# * | K | * | *
+# * | K/kk2 | * | *
+# * | * | H | H
+# * | * | H/hh3 | H/hh3
+# I | I | I | *
+# I/ii1 | I/ii2 | I/ii3 | *
+# I/iii1 | I/iii1 | I/iii3| *
+# * | * | I/iiii3| *
+# * | I/J | I/J | *
+# * | I/J/i2 | I/J/i3 | *
+# * | I/J/j2 | I/J/j2 | *
+# * | I/J/k2 | * | *
+# * | * | I/J/l3 | *
+# L | L | L | L
+# L/ll1 | L/ll1 | L/ll3 | L/ll3
+# L/LL | L/LL | L/LL | L/LL
+# * | L/LL/ll2| L/LL/ll3| L/LL/ll4
+# * | L/LM | * | *
+# * | L/LM/lm2| * | *
+# * | L/LN | L/LN | *
+*/
+void TestMount::prepareSnapDiffLib3Cases()
+{
+ //************ snap1 *************
+ ASSERT_LE(0, write_full("a", "file 'a' v1"));
+ ASSERT_LE(0, write_full("b", "file 'b' v1"));
+ ASSERT_LE(0, write_full("c", "file 'c' v1"));
+ ASSERT_LE(0, write_full("e", "file 'e' v1"));
+ ASSERT_LE(0, write_full("~e", "file '~e' v1"));
+ ASSERT_LE(0, write_full("f", "file 'f' v1"));
+ ASSERT_LE(0, write_full("ff", "file 'ff' v1"));
+ ASSERT_LE(0, write_full("g", "file 'g' v1"));
+ ASSERT_LE(0, write_full("i", "file 'i' v1"));
+
+ ASSERT_EQ(0, mkdir("S"));
+ ASSERT_LE(0, write_full("S/sa", "file 'S/sa' v1"));
+
+ ASSERT_EQ(0, mkdir("C"));
+ ASSERT_LE(0, write_full("C/cc", "file 'C/cc' v1"));
+
+ ASSERT_EQ(0, mkdir("C/CC"));
+ ASSERT_LE(0, write_full("C/CC/c", "file 'C/CC/c' v1"));
+
+ ASSERT_EQ(0, mkdir("G"));
+ ASSERT_LE(0, write_full("G/gg", "file 'G/gg' v1"));
+
+ ASSERT_EQ(0, mkdir("I"));
+ ASSERT_LE(0, write_full("I/ii", "file 'I/ii' v1"));
+ ASSERT_LE(0, write_full("I/iii", "file 'I/iii' v1"));
+
+ ASSERT_EQ(0, mkdir("L"));
+ ASSERT_LE(0, write_full("L/ll", "file 'L/ll' v1"));
+ ASSERT_EQ(0, mkdir("L/LL"));
+
+ ASSERT_EQ(0, mksnap("snap1"));
+ //************ snap2 *************
+
+ ASSERT_LE(0, write_full("b", "file 'b' v2"));
+ ASSERT_EQ(0, unlink("c"));
+ ASSERT_LE(0, write_full("d", "file 'd' v2"));
+ ASSERT_LE(0, write_full("e", "file 'e' v2"));
+ ASSERT_LE(0, write_full("~e", "file '~e' v2"));
+ ASSERT_LE(0, write_full("f", "file 'f' v2"));
+ ASSERT_EQ(0, unlink("g"));
+
+ ASSERT_LE(0, write_full("S/sa", "file 'S/sa' v2"));
+
+ ASSERT_EQ(0, mkdir("T"));
+ ASSERT_LE(0, write_full("T/td", "file 'T/td' v2"));
+
+ ASSERT_EQ(0, purge_dir("C"));
+ ASSERT_EQ(0, purge_dir("G"));
+
+ ASSERT_LE(0, write_full("k", "file 'k' v2"));
+ ASSERT_LE(0, write_full("l", "file 'l' v2"));
+
+ ASSERT_EQ(0, mkdir("K"));
+ ASSERT_LE(0, write_full("K/kk", "file 'K/kk' v2"));
+
+ ASSERT_LE(0, write_full("I/ii", "file 'I/ii' v2"));
+
+ ASSERT_EQ(0, mkdir("I/J"));
+ ASSERT_LE(0, write_full("I/J/i", "file 'I/J/i' v2"));
+ ASSERT_LE(0, write_full("I/J/j", "file 'I/J/j' v2"));
+ ASSERT_LE(0, write_full("I/J/k", "file 'I/J/k' v2"));
+
+ ASSERT_LE(0, write_full("L/LL/ll", "file 'L/LL/ll' v2"));
+
+ ASSERT_EQ(0, mkdir("L/LM"));
+ ASSERT_LE(0, write_full("L/LM/lm", "file 'L/LM/lm' v2"));
+
+ ASSERT_EQ(0, mkdir("L/LN"));
+
+ ASSERT_EQ(0, mksnap("snap2"));
+ //************ snap3 *************
+
+ ASSERT_LE(0, write_full("a", "file 'a' v3"));
+ ASSERT_LE(0, write_full("b", "file 'b' v3"));
+ ASSERT_LE(0, write_full("d", "file 'd' v3"));
+ ASSERT_EQ(0, unlink("e"));
+ ASSERT_EQ(0, unlink("~e"));
+ ASSERT_EQ(0, unlink("f"));
+ ASSERT_EQ(0, unlink("ff"));
+ ASSERT_LE(0, write_full("g", "file 'g' v3"));
+
+ ASSERT_LE(0, write_full("S/sa", "file 'S/sa' v3"));
+
+ ASSERT_LE(0, write_full("T/td", "file 'T/td' v3"));
+
+ ASSERT_EQ(0, mkdir("G"));
+ ASSERT_LE(0, write_full("G/gg", "file 'G/gg' v3"));
+
+ ASSERT_EQ(0, unlink("k"));
+
+ ASSERT_EQ(0, purge_dir("K"));
+
+ ASSERT_EQ(0, mkdir("H"));
+ ASSERT_LE(0, write_full("H/hh", "file 'H/hh' v3"));
+
+ ASSERT_LE(0, write_full("I/ii", "file 'I/ii' v3"));
+ ASSERT_LE(0, write_full("I/iii", "file 'I/iii' v3"));
+ ASSERT_LE(0, write_full("I/iiii", "file 'I/iiii' v3"));
+
+ ASSERT_LE(0, write_full("I/J/i", "file 'I/J/i' v3"));
+ ASSERT_EQ(0, unlink("I/J/k"));
+ ASSERT_LE(0, write_full("I/J/l", "file 'I/J/l' v3"));
+
+ ASSERT_LE(0, write_full("L/ll", "file 'L/ll' v3"));
+
+ ASSERT_LE(0, write_full("L/LL/ll", "file 'L/LL/ll' v3"));
+
+ ASSERT_EQ(0, purge_dir("L/LM"));
+
+ ASSERT_EQ(0, mksnap("snap3"));
+ //************ head *************
+ ASSERT_LE(0, write_full("a", "file 'a' head"));
+
+ ASSERT_LE(0, write_full("h", "file 'h' head"));
+
+ ASSERT_LE(0, write_full("S/sh", "file 'S/sh' head"));
+
+ ASSERT_EQ(0, unlink("l"));
+
+ ASSERT_EQ(0, purge_dir("I"));
+
+ ASSERT_LE(0, write_full("L/LL/ll", "file 'L/LL/ll' head"));
+
+ ASSERT_EQ(0, purge_dir("L/LN"));
+}
+
+//
+// This case tests SnapDiff functionality for snap1/snap2 snapshot delta
+// It operates against FS layout created by prepareSnapDiffCases() method,
+// see relevant table before that function for FS state overview.
+//
+TEST(LibCephFS, SnapDiffCases1_2)
+{
+ TestMount test_mount;
+
+ // Create directory tree evolving through a bunch of snapshots
+ test_mount.prepareSnapDiffLib3Cases();
+
+ uint64_t snapid1;
+ uint64_t snapid2;
+ ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1));
+ ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2));
+ std::cout << snapid1 << " vs. " << snapid2 << std::endl;
+ ASSERT_GT(snapid1, 0);
+ ASSERT_GT(snapid2, 0);
+ ASSERT_GT(snapid2, snapid1);
+
+ // Print snapshot delta (snap1 vs. snap2) results for root in a
+ // human-readable form.
+ test_mount.print_snap_diff("", "snap1", "snap2");
+
+ {
+ // Make sure the root delta is as expected
+ // One should use columns snap1 and snap2 from
+ // the table preceeding prepareSnapDiffCases() function
+ // to learn which names to expect in the delta.
+ //
+ // - file 'a' is unchanged hence not present in delta
+ // - file 'ff' is unchanged hence not present in delta
+ // - file 'i' is unchanged hence not present in delta
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("b", snapid2); // file 'b' is updated in snap2
+ expected.emplace_back("c", snapid1); // file 'c' is removed in snap2
+ expected.emplace_back("d", snapid2); // file 'd' is created in snap2
+ expected.emplace_back("e", snapid2); // file 'e' is updated in snap2
+ expected.emplace_back("~e", snapid2); // file '~e' is updated in snap2
+ expected.emplace_back("f", snapid2); // file 'f' is updated in snap2
+ expected.emplace_back("g", snapid1); // file 'g' is removed in snap2
+ expected.emplace_back("S", snapid2); // folder 'S' is present in snap2 hence reported
+ expected.emplace_back("T", snapid2); // folder 'T' is created in snap2
+ expected.emplace_back("C", snapid1); // folder 'C' is removed in snap2
+ expected.emplace_back("G", snapid1); // folder 'G' is removed in snap2
+ expected.emplace_back("k", snapid2); // file 'k' is created in snap2
+ expected.emplace_back("l", snapid2); // file 'l' is created in snap2
+ expected.emplace_back("K", snapid2); // folder 'K' is created in snap2
+ expected.emplace_back("I", snapid2); // folder 'I' is created in snap2
+ expected.emplace_back("L", snapid2); // folder 'L' is present in snap2 but got more
+ // subfolders
+ test_mount.verify_snap_diff(expected, "", "snap1", "snap2");
+ }
+ {
+
+ //
+ // Make sure snapshot delta for /S (existed at both snap1 and snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("sa", snapid2);
+ test_mount.verify_snap_diff(expected, "S", "snap1", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /T (created at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("td", snapid2);
+ test_mount.verify_snap_diff(expected, "T", "snap1", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /C (removed at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("cc", snapid1);
+ expected.emplace_back("CC", snapid1);
+ test_mount.verify_snap_diff(expected, "C", "snap2", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /C/CC (removed at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("c", snapid1);
+ test_mount.verify_snap_diff(expected, "C/CC", "snap2", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /I (created at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ii", snapid2);
+ expected.emplace_back("J", snapid2);
+ test_mount.verify_snap_diff(expected, "I", "snap1", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /I/J (created at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("i", snapid2);
+ expected.emplace_back("j", snapid2);
+ expected.emplace_back("k", snapid2);
+ test_mount.verify_snap_diff(expected, "I/J", "snap1", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L (extended at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("LL", snapid2);
+ expected.emplace_back("LM", snapid2);
+ expected.emplace_back("LN", snapid2);
+ test_mount.verify_snap_diff(expected, "L", "snap1", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L/LL (updated at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ll", snapid2);
+ test_mount.verify_snap_diff(expected, "L/LL", "snap1", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L/LN (created empty at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ test_mount.verify_snap_diff(expected, "L/LN", "snap1", "snap2");
+ }
+
+ {
+ // Make sure snapshot delta for /L/LM (created at snap2)
+ // is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("lm", snapid2);
+ test_mount.verify_snap_diff(expected, "L/LM", "snap1", "snap2");
+ }
+ std::cout << "-------------" << std::endl;
+
+ test_mount.remove_all();
+ test_mount.rmsnap("snap1");
+ test_mount.rmsnap("snap2");
+ test_mount.rmsnap("snap3");
+}
+
+//
+// This case tests SnapDiff functionality for snap2/snap3 snapshot delta
+// retrieved through .snap path-based query API.
+// It operates against FS layout created by prepareSnapDiffCases() method,
+// see relevant table before that function for FS state overview.
+//
+TEST(LibCephFS, SnapDiffCases2_3)
+{
+ TestMount test_mount;
+
+ // Create directory tree evolving through a bunch of snapshots
+ test_mount.prepareSnapDiffLib3Cases();
+
+ uint64_t snapid2;
+ uint64_t snapid3;
+ ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2));
+ ASSERT_EQ(0, test_mount.get_snapid("snap3", &snapid3));
+ std::cout << snapid2 << " vs. " << snapid3 << std::endl;
+ ASSERT_GT(snapid3, 0);
+ ASSERT_GT(snapid3, 0);
+ ASSERT_GT(snapid3, snapid2);
+
+ // Print snapshot delta (snap2 vs. snap3) results for root in a
+ // human-readable form.
+ test_mount.print_snap_diff("", "snap2", "snap3");
+
+ {
+ // Make sure the root delta is as expected
+ // One should use columns snap1 and snap2 from
+ // the table preceeding prepareSnapDiffCases() function
+ // to learn which names to expect in the delta.
+ //
+ // - file 'c' is removed since snap1 hence not present in delta
+ // - file 'l' is unchanged hence not present in delta
+ // - file 'i' is unchanged hence not present in delta
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("a", snapid3); // file 'a' is updated in snap3
+ expected.emplace_back("b", snapid3); // file 'b' is updated in snap3
+ expected.emplace_back("d", snapid3); // file 'd' is updated in snap3
+ expected.emplace_back("~e", snapid2); // file '~e' is removed in snap3
+ expected.emplace_back("e", snapid2); // file 'e' is removed in snap3
+ expected.emplace_back("f", snapid2); // file 'f' is removed in snap3
+ expected.emplace_back("ff", snapid2); // file 'ff' is removed in snap3
+ expected.emplace_back("g", snapid3); // file 'g' re-appeared in snap3
+ expected.emplace_back("S", snapid3); // folder 'S' is present in snap3 hence reported
+ expected.emplace_back("T", snapid3); // folder 'T' is present in snap3 hence reported
+ expected.emplace_back("G", snapid3); // folder 'G' re-appeared in snap3 hence reported
+ expected.emplace_back("k", snapid2); // file 'k' is removed in snap3
+ expected.emplace_back("K", snapid2); // folder 'K' is removed in snap3
+ expected.emplace_back("H", snapid3); // folder 'H' is created in snap3 hence reported
+ expected.emplace_back("I", snapid3); // folder 'I' is present in snap3 hence reported
+ expected.emplace_back("L", snapid3); // folder 'L' is present in snap3 hence reported
+ test_mount.verify_snap_diff(expected, "", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /S (children updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("sa", snapid3);
+ test_mount.verify_snap_diff(expected, "S", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /T (children updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("td", snapid3);
+ test_mount.verify_snap_diff(expected, "T", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /G (re-appeared) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("gg", snapid3);
+ test_mount.verify_snap_diff(expected, "G", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /K (removed) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("kk", snapid2);
+ test_mount.verify_snap_diff(expected, "K", "snap3", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /H (created) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("hh", snapid3);
+ test_mount.verify_snap_diff(expected, "H", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /I (children updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ii", snapid3);
+ expected.emplace_back("iii", snapid3);
+ expected.emplace_back("iiii", snapid3);
+ expected.emplace_back("J", snapid3);
+ test_mount.verify_snap_diff(expected, "I", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /I/J (children updated/removed) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("i", snapid3);
+ expected.emplace_back("k", snapid2);
+ expected.emplace_back("l", snapid3);
+ test_mount.verify_snap_diff(expected, "I/J", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L (children updated/removed) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ll", snapid3);
+ expected.emplace_back("LL", snapid3);
+ expected.emplace_back("LM", snapid2);
+ expected.emplace_back("LN", snapid3);
+ test_mount.verify_snap_diff(expected, "L", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L/LL (children updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ll", snapid3);
+ test_mount.verify_snap_diff(expected, "L/LL", "snap2", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L/LM (removed) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("lm", snapid2);
+ test_mount.verify_snap_diff(expected, "L/LM", "snap3", "snap2");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L/LN (created empty) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ test_mount.verify_snap_diff(expected, "L/LN", "snap2", "snap3");
+ }
+ test_mount.remove_all();
+ test_mount.rmsnap("snap1");
+ test_mount.rmsnap("snap2");
+ test_mount.rmsnap("snap3");
+}
+
+//
+// This case tests SnapDiff functionality for snap1/snap3 snapshot delta
+// retrieved through .snap path-based query API.
+// It operates against FS layout created by prepareSnapDiffCases() method,
+// see relevant table before that function for FS state overview.
+//
+TEST(LibCephFS, SnapDiffCases1_3)
+{
+ TestMount test_mount;
+
+ // Create directory tree evolving through a bunch of snapshots
+ test_mount.prepareSnapDiffLib3Cases();
+
+ uint64_t snapid1;
+ uint64_t snapid3;
+ ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1));
+ ASSERT_EQ(0, test_mount.get_snapid("snap3", &snapid3));
+ std::cout << snapid1 << " vs. " << snapid3 << std::endl;
+ ASSERT_GT(snapid3, 0);
+ ASSERT_GT(snapid3, 0);
+ ASSERT_GT(snapid3, snapid1);
+
+ // Print snapshot delta (snap2 vs. snap3) results for root in a
+ // human-readable form.
+ test_mount.print_snap_diff("", "snap1", "snap3");
+
+ {
+ // Make sure the root delta is as expected
+ // One should use columns snap1 and snap3 from
+ // the table preceeding prepareSnapDiffCases() function
+ // to learn which names to expect in the delta.
+ //
+ // - file 'i' is unchanged hence not present in delta
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("a", snapid3); // file 'a' is updated in snap3
+ expected.emplace_back("b", snapid3); // file 'b' is updated in snap3
+ expected.emplace_back("c", snapid1); // file 'c' is removed in snap2
+ expected.emplace_back("d", snapid3); // file 'd' is updated in snap3
+ expected.emplace_back("~e", snapid1); // file '~e' is removed in snap3
+ expected.emplace_back("e", snapid1); // file 'e' is removed in snap3
+ expected.emplace_back("f", snapid1); // file 'f' is removed in snap3
+ expected.emplace_back("ff", snapid1); // file 'ff' is removed in snap3
+ expected.emplace_back("g", snapid3); // file 'g' removed in snap2 and
+ // re-appeared in snap3
+ expected.emplace_back("S", snapid3); // folder 'S' is present in snap3 hence reported
+ expected.emplace_back("T", snapid3); // folder 'T' is present in snap3 hence reported
+ expected.emplace_back("C", snapid1); // folder 'C' is removed in snap2
+
+ // folder 'G' is removed in snap2 and re-appeared in snap3
+ // hence reporting it twice under different snapid
+ expected.emplace_back("G", snapid1);
+ expected.emplace_back("G", snapid3);
+
+ expected.emplace_back("l", snapid3); // file 'l' is created in snap2
+ expected.emplace_back("H", snapid3); // folder 'H' is created in snap3 hence reported
+ expected.emplace_back("I", snapid3); // folder 'I' is created in snap3 hence reported
+ expected.emplace_back("L", snapid3); // folder 'L' is created in snap3 hence reported
+ test_mount.verify_snap_diff(expected, "", "snap3", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /S (children updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("sa", snapid3);
+ test_mount.verify_snap_diff(expected, "S", "snap3", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /T (created and children updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("td", snapid3);
+ test_mount.verify_snap_diff(expected, "T", "snap3", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /C (removed) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("cc", snapid1);
+ expected.emplace_back("CC", snapid1);
+ test_mount.verify_snap_diff(expected, "C", "snap3", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /C/CC (removed) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("c", snapid1);
+ test_mount.verify_snap_diff(expected, "C/CC", "snap3", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /G (removed) is as expected
+ // For this case (G@snap1 and G@snap3 are different entries)
+ // the order in which snapshot names are provided is crucial.
+ // Making G@snap1 vs. snap3 delta returns everything from G@snap1
+ // but omits any entries from G/snap3 (since it's a different entry).
+ // And making G@snap3 vs. snap1 delta returns everything from G@snap3
+ // but nothing from snap1,
+
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("gg", snapid1);
+ test_mount.verify_snap_diff(expected, "G", "snap1", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /G (re-created) is as expected
+ // The snapshot names order is important, see above.
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("gg", snapid3);
+ test_mount.verify_snap_diff(expected, "G", "snap3", "snap1");
+ }
+ {
+ //
+ // Make sure snapshot delta for /H (created) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("hh", snapid3);
+ test_mount.verify_snap_diff(expected, "H", "snap1", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /I (chinldren updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ii", snapid3);
+ expected.emplace_back("iii", snapid3);
+ expected.emplace_back("iiii", snapid3);
+ expected.emplace_back("J", snapid3);
+ test_mount.verify_snap_diff(expected, "I", "snap1", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /I/J (created at snap2) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("i", snapid3);
+ expected.emplace_back("j", snapid3);
+ expected.emplace_back("l", snapid3);
+ test_mount.verify_snap_diff(expected, "I/J", "snap1", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ll", snapid3);
+ expected.emplace_back("LL", snapid3);
+ expected.emplace_back("LN", snapid3);
+ test_mount.verify_snap_diff(expected, "L", "snap1", "snap3");
+ }
+ {
+ //
+ // Make sure snapshot delta for /L/LL (children updated) is as expected
+ //
+ vector<std::pair<string, uint64_t>> expected;
+ expected.emplace_back("ll", snapid3);
+ test_mount.verify_snap_diff(expected, "L/LL", "snap1", "snap3");
+ }
+ {
+ vector<std::pair<string, uint64_t>> expected;
+ test_mount.verify_snap_diff(expected, "L/LN", "snap1", "snap3");
+ }
+ std::cout << "-------------" << std::endl;
+
+ test_mount.remove_all();
+ test_mount.rmsnap("snap1");
+ test_mount.rmsnap("snap2");
+ test_mount.rmsnap("snap3");
+}
+
+/*
+* SnapDiff readdir API testing for huge dir
+* when delta is minor.
+*/
+TEST(LibCephFS, HugeSnapDiffSmallDelta)
+{
+ TestMount test_mount;
+
+ long int file_count = 10000;
+ printf("Seeding %ld files...\n", file_count);
+
+ // Create simple directory tree with a couple of snapshots
+ // to test against.
+ string name_prefix_start = "aaaa";
+ string name_prefix_bulk = "file";
+ string name_prefix_end = "zzzz";
+ test_mount.prepareHugeSnapDiff(name_prefix_start,
+ name_prefix_bulk,
+ name_prefix_end,
+ file_count,
+ false);
+
+ uint64_t snapid1;
+ uint64_t snapid2;
+
+ // learn snapshot ids and do basic verification
+ ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1));
+ ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2));
+ ASSERT_GT(snapid1, 0);
+ ASSERT_GT(snapid2, 0);
+ ASSERT_GT(snapid2, snapid1);
+ std::cout << snapid1 << " vs. " << snapid2 << std::endl;
+
+ //
+ // Make sure snap1 vs. snap2 delta for the root is as expected
+ //
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back(name_prefix_start + "B", snapid1);
+ expected.emplace_back(name_prefix_start + "C", snapid2);
+ expected.emplace_back(name_prefix_start + "D", snapid2);
+
+ expected.emplace_back(name_prefix_end + "B", snapid1);
+ expected.emplace_back(name_prefix_end + "C", snapid2);
+ expected.emplace_back(name_prefix_end + "D", snapid2);
+ test_mount.verify_snap_diff(expected, "", "snap1", "snap2");
+ }
+
+ std::cout << "------------- closing -------------" << std::endl;
+ ASSERT_EQ(0, test_mount.purge_dir(""));
+ ASSERT_EQ(0, test_mount.rmsnap("snap1"));
+ ASSERT_EQ(0, test_mount.rmsnap("snap2"));
+}
+
+/*
+* SnapDiff readdir API testing for huge dir
+* when delta is large
+*/
+TEST(LibCephFS, HugeSnapDiffLargeDelta)
+{
+ TestMount test_mount;
+
+ // Calculate amount of files required to have multiple directory fragments
+ // using relevant config parameters.
+ // file_count = mds_bal_spli_size * mds_bal_fragment_fast_factor + 100
+ char buf[256];
+ int r = test_mount.conf_get("mds_bal_split_size", buf, sizeof(buf));
+ ASSERT_TRUE(r >= 0);
+ long int file_count = strtol(buf, nullptr, 10);
+ r = test_mount.conf_get("mds_bal_fragment_fast_factor ", buf, sizeof(buf));
+ ASSERT_TRUE(r >= 0);
+ double factor = strtod(buf, nullptr);
+ file_count *= factor;
+ file_count += 100;
+ printf("Seeding %ld files...\n", file_count);
+
+ // Create simple directory tree with a couple of snapshots
+ // to test against.
+
+ string name_prefix_start = "aaaa";
+ string name_prefix_bulk = "file";
+ string name_prefix_end = "zzzz";
+
+ test_mount.prepareHugeSnapDiff(name_prefix_start,
+ name_prefix_bulk,
+ name_prefix_end,
+ file_count,
+ true);
+ uint64_t snapid1;
+ uint64_t snapid2;
+
+ // learn snapshot ids and do basic verification
+ ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1));
+ ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2));
+ ASSERT_GT(snapid1, 0);
+ ASSERT_GT(snapid2, 0);
+ ASSERT_GT(snapid2, snapid1);
+ std::cout << snapid1 << " vs. " << snapid2 << std::endl;
+
+ //
+ // Make sure snap1 vs. snap2 delta for the root is as expected
+ //
+ {
+ vector<pair<string, uint64_t>> expected;
+ expected.emplace_back(name_prefix_start + "B", snapid1);
+ expected.emplace_back(name_prefix_start + "C", snapid2);
+ expected.emplace_back(name_prefix_start + "D", snapid2);
+ for (size_t i = 0; i < (size_t)file_count; i++) {
+ expected.emplace_back(name_prefix_bulk + stringify(i), snapid2);
+ }
+ expected.emplace_back(name_prefix_end + "B", snapid1);
+ expected.emplace_back(name_prefix_end + "C", snapid2);
+ expected.emplace_back(name_prefix_end + "D", snapid2);
+ test_mount.verify_snap_diff(expected, "", "snap1", "snap2");
+ }
+
+ std::cout << "------------- closing -------------" << std::endl;
+ ASSERT_EQ(0, test_mount.purge_dir(""));
+ ASSERT_EQ(0, test_mount.rmsnap("snap1"));
+ ASSERT_EQ(0, test_mount.rmsnap("snap2"));
+}
diff --git a/src/test/libcephfs/suidsgid.cc b/src/test/libcephfs/suidsgid.cc
new file mode 100644
index 000000000..d750613eb
--- /dev/null
+++ b/src/test/libcephfs/suidsgid.cc
@@ -0,0 +1,331 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "include/buffer.h"
+#include "include/fs_types.h"
+#include "include/stringify.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/rados/librados.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/uio.h>
+#include <iostream>
+#include <vector>
+#include "json_spirit/json_spirit.h"
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#endif
+
+using namespace std;
+struct ceph_mount_info *admin;
+struct ceph_mount_info *cmount;
+char filename[128];
+
+void run_fallocate_test_case(int mode, int result, bool with_admin=false)
+{
+ struct ceph_statx stx;
+ int flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE;
+
+ ASSERT_EQ(0, ceph_chmod(admin, filename, mode));
+
+ struct ceph_mount_info *_cmount = cmount;
+ if (with_admin) {
+ _cmount = admin;
+ }
+ int fd = ceph_open(_cmount, filename, O_RDWR, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_fallocate(_cmount, fd, flags, 1024, 40960));
+ ASSERT_EQ(ceph_statx(_cmount, filename, &stx, CEPH_STATX_MODE, 0), 0);
+ std::cout << "After ceph_fallocate, mode: 0" << oct << mode << " -> 0"
+ << (stx.stx_mode & 07777) << dec << std::endl;
+ ASSERT_EQ(stx.stx_mode & (S_ISUID|S_ISGID), result);
+ ceph_close(_cmount, fd);
+}
+
+rados_t cluster;
+
+int do_mon_command(string s, string *key)
+{
+ char *outs, *outbuf;
+ size_t outs_len, outbuf_len;
+ const char *ss = s.c_str();
+ int r = rados_mon_command(cluster, (const char **)&ss, 1,
+ 0, 0,
+ &outbuf, &outbuf_len,
+ &outs, &outs_len);
+ if (outbuf_len) {
+ string s(outbuf, outbuf_len);
+ std::cout << "out: " << s << std::endl;
+
+ // parse out the key
+ json_spirit::mValue v, k;
+ json_spirit::read_or_throw(s, v);
+ k = v.get_array()[0].get_obj().find("key")->second;
+ *key = k.get_str();
+ std::cout << "key: " << *key << std::endl;
+ free(outbuf);
+ } else {
+ return -CEPHFS_EINVAL;
+ }
+ if (outs_len) {
+ string s(outs, outs_len);
+ std::cout << "outs: " << s << std::endl;
+ free(outs);
+ }
+ return r;
+}
+
+void run_write_test_case(int mode, int result, bool with_admin=false)
+{
+ struct ceph_statx stx;
+
+ ASSERT_EQ(0, ceph_chmod(admin, filename, mode));
+
+ struct ceph_mount_info *_cmount = cmount;
+ if (with_admin) {
+ _cmount = admin;
+ }
+ int fd = ceph_open(_cmount, filename, O_RDWR, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(ceph_write(_cmount, fd, "foo", 3, 0), 3);
+ ASSERT_EQ(ceph_statx(_cmount, filename, &stx, CEPH_STATX_MODE, 0), 0);
+ std::cout << "After ceph_write, mode: 0" << oct << mode << " -> 0"
+ << (stx.stx_mode & 07777) << dec << std::endl;
+ ASSERT_EQ(stx.stx_mode & (S_ISUID|S_ISGID), result);
+ ceph_close(_cmount, fd);
+}
+
+void run_truncate_test_case(int mode, int result, size_t size, bool with_admin=false)
+{
+ struct ceph_statx stx;
+
+ ASSERT_EQ(0, ceph_chmod(admin, filename, mode));
+
+ struct ceph_mount_info *_cmount = cmount;
+ if (with_admin) {
+ _cmount = admin;
+ }
+ int fd = ceph_open(_cmount, filename, O_RDWR, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_GE(ceph_ftruncate(_cmount, fd, size), 0);
+ ASSERT_EQ(ceph_statx(_cmount, filename, &stx, CEPH_STATX_MODE, 0), 0);
+ std::cout << "After ceph_truncate size " << size << " mode: 0" << oct
+ << mode << " -> 0" << (stx.stx_mode & 07777) << dec << std::endl;
+ ASSERT_EQ(stx.stx_mode & (S_ISUID|S_ISGID), result);
+ ceph_close(_cmount, fd);
+}
+
+TEST(SuidsgidTest, WriteClearSetuid) {
+ ASSERT_EQ(0, ceph_create(&admin, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(admin, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(admin, NULL));
+ ASSERT_EQ(0, ceph_mount(admin, "/"));
+
+ sprintf(filename, "/clear_suidsgid_file_%d", getpid());
+ int fd = ceph_open(admin, filename, O_CREAT|O_RDWR, 0766);
+ ASSERT_GE(ceph_ftruncate(admin, fd, 10000000), 0);
+ ceph_close(admin, fd);
+
+ string user = "clear_suidsgid_" + stringify(rand());
+ // create access key
+ string key;
+ ASSERT_EQ(0, do_mon_command(
+ "{\"prefix\": \"auth get-or-create\", \"entity\": \"client." + user + "\", "
+ "\"caps\": [\"mon\", \"allow *\", \"osd\", \"allow *\", \"mgr\", \"allow *\", "
+ "\"mds\", \"allow *\"], \"format\": \"json\"}", &key));
+
+ ASSERT_EQ(0, ceph_create(&cmount, user.c_str()));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_set(cmount, "key", key.c_str()));
+ ASSERT_EQ(ceph_init(cmount), 0);
+ UserPerm *perms = ceph_userperm_new(123, 456, 0, NULL);
+ ASSERT_NE(nullptr, perms);
+ ASSERT_EQ(0, ceph_mount_perms_set(cmount, perms));
+ ceph_userperm_destroy(perms);
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ // 1, Commit to a non-exec file by an unprivileged user clears suid and sgid.
+ run_fallocate_test_case(06666, 0); // a+rws
+
+ // 2, Commit to a group-exec file by an unprivileged user clears suid and sgid.
+ run_fallocate_test_case(06676, 0); // g+x,a+rws
+
+ // 3, Commit to a user-exec file by an unprivileged user clears suid and sgid.
+ run_fallocate_test_case(06766, 0); // u+x,a+rws,g-x
+
+ // 4, Commit to a all-exec file by an unprivileged user clears suid and sgid.
+ run_fallocate_test_case(06777, 0); // a+rwxs
+
+ // 5, Commit to a non-exec file by root leaves suid and sgid.
+ run_fallocate_test_case(06666, S_ISUID|S_ISGID, true); // a+rws
+
+ // 6, Commit to a group-exec file by root leaves suid and sgid.
+ run_fallocate_test_case(06676, S_ISUID|S_ISGID, true); // g+x,a+rws
+
+ // 7, Commit to a user-exec file by root leaves suid and sgid.
+ run_fallocate_test_case(06766, S_ISUID|S_ISGID, true); // u+x,a+rws,g-x
+
+ // 8, Commit to a all-exec file by root leaves suid and sgid.
+ run_fallocate_test_case(06777, S_ISUID|S_ISGID, true); // a+rwxs
+
+ // 9, Commit to a group-exec file by an unprivileged user clears sgid
+ run_fallocate_test_case(02676, 0); // a+rw,g+rwxs
+
+ // 10, Commit to a all-exec file by an unprivileged user clears sgid.
+ run_fallocate_test_case(02777, 0); // a+rwx,g+rwxs
+
+ // 11, Write by privileged user leaves the suid and sgid
+ run_write_test_case(06766, S_ISUID | S_ISGID, true);
+
+ // 12, Write by unprivileged user clears the suid and sgid
+ run_write_test_case(06766, 0);
+
+ // 13, Truncate by privileged user leaves the suid and sgid
+ run_truncate_test_case(06766, S_ISUID | S_ISGID, 10000, true);
+
+ // 14, Truncate by unprivileged user clears the suid and sgid
+ run_truncate_test_case(06766, 0, 100);
+
+ // clean up
+ ceph_shutdown(cmount);
+ ceph_shutdown(admin);
+}
+
+TEST(LibCephFS, ChownClearSetuid) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ Inode *root;
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+
+ char filename[32];
+ sprintf(filename, "clearsetuid%x", getpid());
+
+ Fh *fh;
+ Inode *in;
+ struct ceph_statx stx;
+ const mode_t after_mode = S_IRWXU;
+ const mode_t before_mode = S_IRWXU | S_ISUID | S_ISGID;
+ const unsigned want = CEPH_STATX_UID|CEPH_STATX_GID|CEPH_STATX_MODE;
+ UserPerm *usercred = ceph_mount_perms(cmount);
+
+ ceph_ll_unlink(cmount, root, filename, usercred);
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, before_mode,
+ O_RDWR|O_CREAT|O_EXCL, &in, &fh, &stx, want, 0,
+ usercred), 0);
+
+ ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, before_mode);
+
+ // chown -- for this we need to be "root"
+ UserPerm *rootcred = ceph_userperm_new(0, 0, 0, NULL);
+ ASSERT_TRUE(rootcred);
+ stx.stx_uid++;
+ stx.stx_gid++;
+ ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_UID|CEPH_SETATTR_GID, rootcred), 0);
+ ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, usercred), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_MODE);
+ ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, after_mode);
+
+ /* test chown with supplementary groups, and chown with/without exe bit */
+ uid_t u = 65534;
+ gid_t g = 65534;
+ gid_t gids[] = {65533,65532};
+ UserPerm *altcred = ceph_userperm_new(u, g, sizeof gids / sizeof gids[0], gids);
+ stx.stx_uid = u;
+ stx.stx_gid = g;
+ mode_t m = S_ISGID|S_ISUID|S_IRUSR|S_IWUSR;
+ stx.stx_mode = m;
+ ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_MODE|CEPH_SETATTR_UID|CEPH_SETATTR_GID, rootcred), 0);
+ ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
+ ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m);
+ /* not dropped without exe bit */
+ stx.stx_gid = gids[0];
+ ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_GID, altcred), 0);
+ ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
+ ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m);
+ /* now check dropped with exe bit */
+ m = S_ISGID|S_ISUID|S_IRWXU;
+ stx.stx_mode = m;
+ ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_STATX_MODE, altcred), 0);
+ ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
+ ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m);
+ stx.stx_gid = gids[1];
+ ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_GID, altcred), 0);
+ ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
+ ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m&(S_IRWXU|S_IRWXG|S_IRWXO));
+ ceph_userperm_destroy(altcred);
+
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ceph_shutdown(cmount);
+}
+
+static int update_root_mode()
+{
+ struct ceph_mount_info *admin;
+ int r = ceph_create(&admin, NULL);
+ if (r < 0)
+ return r;
+ ceph_conf_read_file(admin, NULL);
+ ceph_conf_parse_env(admin, NULL);
+ ceph_conf_set(admin, "client_permissions", "false");
+ r = ceph_mount(admin, "/");
+ if (r < 0)
+ goto out;
+ r = ceph_chmod(admin, "/", 0777);
+out:
+ ceph_shutdown(admin);
+ return r;
+}
+
+int main(int argc, char **argv)
+{
+ int r = update_root_mode();
+ if (r < 0)
+ exit(1);
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ srand(getpid());
+
+ r = rados_create(&cluster, NULL);
+ if (r < 0)
+ exit(1);
+
+ r = rados_conf_read_file(cluster, NULL);
+ if (r < 0)
+ exit(1);
+
+ rados_conf_parse_env(cluster, NULL);
+ r = rados_connect(cluster);
+ if (r < 0)
+ exit(1);
+
+ r = RUN_ALL_TESTS();
+
+ rados_shutdown(cluster);
+
+ return r;
+}
diff --git a/src/test/libcephfs/test.cc b/src/test/libcephfs/test.cc
new file mode 100644
index 000000000..c83ddccf9
--- /dev/null
+++ b/src/test/libcephfs/test.cc
@@ -0,0 +1,3775 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/compat.h"
+#include "gtest/gtest.h"
+#include "include/cephfs/libcephfs.h"
+#include "mds/mdstypes.h"
+#include "include/stat.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#ifndef _WIN32
+#include <sys/resource.h>
+#endif
+
+#include "common/Clock.h"
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#endif
+
+#include <fmt/format.h>
+#include <map>
+#include <vector>
+#include <thread>
+#include <regex>
+
+using namespace std;
+
+TEST(LibCephFS, OpenEmptyComponent) {
+
+ pid_t mypid = getpid();
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ char c_dir[1024];
+ sprintf(c_dir, "/open_test_%d", mypid);
+ struct ceph_dir_result *dirp;
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, c_dir, 0777));
+
+ ASSERT_EQ(0, ceph_opendir(cmount, c_dir, &dirp));
+
+ char c_path[1024];
+ sprintf(c_path, "/open_test_%d//created_file_%d", mypid, mypid);
+ int fd = ceph_open(cmount, c_path, O_RDONLY|O_CREAT, 0666);
+ ASSERT_LT(0, fd);
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_closedir(cmount, dirp));
+ ceph_shutdown(cmount);
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ fd = ceph_open(cmount, c_path, O_RDONLY, 0666);
+ ASSERT_LT(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ // cleanup
+ ASSERT_EQ(0, ceph_unlink(cmount, c_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, c_dir));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, OpenReadTruncate) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ auto path = fmt::format("test_open_rdt_{}", getpid());
+ int fd = ceph_open(cmount, path.c_str(), O_WRONLY|O_CREAT, 0666);
+ ASSERT_LE(0, fd);
+
+ auto data = std::string("hello world");
+ ASSERT_EQ(ceph_write(cmount, fd, data.c_str(), data.size(), 0), (int)data.size());
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, path.c_str(), O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(ceph_ftruncate(cmount, fd, 0), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_ftruncate(cmount, fd, 1), -CEPHFS_EBADF);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, OpenReadWrite) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ char c_path[1024];
+ sprintf(c_path, "test_open_rdwr_%d", getpid());
+ int fd = ceph_open(cmount, c_path, O_WRONLY|O_CREAT, 0666);
+ ASSERT_LT(0, fd);
+
+ const char *out_buf = "hello world";
+ size_t size = strlen(out_buf);
+ char in_buf[100];
+ ASSERT_EQ(ceph_write(cmount, fd, out_buf, size, 0), (int)size);
+ ASSERT_EQ(ceph_read(cmount, fd, in_buf, sizeof(in_buf), 0), -CEPHFS_EBADF);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, c_path, O_RDONLY, 0);
+ ASSERT_LT(0, fd);
+ ASSERT_EQ(ceph_write(cmount, fd, out_buf, size, 0), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_read(cmount, fd, in_buf, sizeof(in_buf), 0), (int)size);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, c_path, O_RDWR, 0);
+ ASSERT_LT(0, fd);
+ ASSERT_EQ(ceph_write(cmount, fd, out_buf, size, 0), (int)size);
+ ASSERT_EQ(ceph_read(cmount, fd, in_buf, sizeof(in_buf), 0), (int)size);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, MountNonExist) {
+
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_NE(0, ceph_mount(cmount, "/non-exist"));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, MountDouble) {
+
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(-CEPHFS_EISCONN, ceph_mount(cmount, "/"));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, MountRemount) {
+
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+
+ CephContext *cct = ceph_get_mount_context(cmount);
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(cct, ceph_get_mount_context(cmount));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, UnmountUnmounted) {
+
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(-CEPHFS_ENOTCONN, ceph_unmount(cmount));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, ReleaseUnmounted) {
+
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_release(cmount));
+}
+
+TEST(LibCephFS, ReleaseMounted) {
+
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(-CEPHFS_EISCONN, ceph_release(cmount));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ASSERT_EQ(0, ceph_release(cmount));
+}
+
+TEST(LibCephFS, UnmountRelease) {
+
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ASSERT_EQ(0, ceph_release(cmount));
+}
+
+TEST(LibCephFS, Mount) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+ ceph_shutdown(cmount);
+
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, OpenLayout) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ /* valid layout */
+ char test_layout_file[256];
+ sprintf(test_layout_file, "test_layout_%d_b", getpid());
+ int fd = ceph_open_layout(cmount, test_layout_file, O_CREAT|O_WRONLY, 0666, (1<<20), 7, (1<<20), NULL);
+ ASSERT_GT(fd, 0);
+ char poolname[80];
+ ASSERT_LT(0, ceph_get_file_pool_name(cmount, fd, poolname, sizeof(poolname)));
+ ASSERT_LT(0, ceph_get_file_pool_name(cmount, fd, poolname, 0));
+
+ /* on already-written file (CEPHFS_ENOTEMPTY) */
+ ceph_write(cmount, fd, "hello world", 11, 0);
+ ceph_close(cmount, fd);
+
+ char xattrk[128];
+ char xattrv[128];
+ sprintf(xattrk, "ceph.file.layout.stripe_unit");
+ sprintf(xattrv, "65536");
+ ASSERT_EQ(-CEPHFS_ENOTEMPTY, ceph_setxattr(cmount, test_layout_file, xattrk, (void *)xattrv, 5, 0));
+
+ /* invalid layout */
+ sprintf(test_layout_file, "test_layout_%d_c", getpid());
+ fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 1, 19, NULL);
+ ASSERT_EQ(fd, -CEPHFS_EINVAL);
+
+ /* with data pool */
+ sprintf(test_layout_file, "test_layout_%d_d", getpid());
+ fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 7, (1<<20), poolname);
+ ASSERT_GT(fd, 0);
+ ceph_close(cmount, fd);
+
+ /* with metadata pool (invalid) */
+ sprintf(test_layout_file, "test_layout_%d_e", getpid());
+ fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 7, (1<<20), "metadata");
+ ASSERT_EQ(fd, -CEPHFS_EINVAL);
+
+ /* with metadata pool (does not exist) */
+ sprintf(test_layout_file, "test_layout_%d_f", getpid());
+ fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 7, (1<<20), "asdfjasdfjasdf");
+ ASSERT_EQ(fd, -CEPHFS_EINVAL);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DirLs) {
+
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ struct ceph_dir_result *ls_dir = NULL;
+ char foostr[256];
+ sprintf(foostr, "dir_ls%d", mypid);
+ ASSERT_EQ(ceph_opendir(cmount, foostr, &ls_dir), -CEPHFS_ENOENT);
+
+ ASSERT_EQ(ceph_mkdir(cmount, foostr, 0777), 0);
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, foostr, &stx, 0, 0), 0);
+ ASSERT_NE(S_ISDIR(stx.stx_mode), 0);
+
+ char barstr[256];
+ sprintf(barstr, "dir_ls2%d", mypid);
+ ASSERT_EQ(ceph_statx(cmount, barstr, &stx, 0, AT_SYMLINK_NOFOLLOW), -CEPHFS_ENOENT);
+
+ // insert files into directory and test open
+ char bazstr[256];
+ int i = 0, r = rand() % 4096;
+ if (getenv("LIBCEPHFS_RAND")) {
+ r = atoi(getenv("LIBCEPHFS_RAND"));
+ }
+ printf("rand: %d\n", r);
+ for(; i < r; ++i) {
+
+ sprintf(bazstr, "dir_ls%d/dirf%d", mypid, i);
+ int fd = ceph_open(cmount, bazstr, O_CREAT|O_RDONLY, 0666);
+ ASSERT_GT(fd, 0);
+ ASSERT_EQ(ceph_close(cmount, fd), 0);
+
+ // set file sizes for readdirplus
+ ceph_truncate(cmount, bazstr, i);
+ }
+
+ ASSERT_EQ(ceph_opendir(cmount, foostr, &ls_dir), 0);
+
+ // not guaranteed to get . and .. first, but its a safe assumption in this case
+ struct dirent *result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+
+ std::vector<std::string> entries;
+ std::map<std::string, int64_t> offset_map;
+ int64_t offset = ceph_telldir(cmount, ls_dir);
+ for(i = 0; i < r; ++i) {
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ entries.push_back(result->d_name);
+ offset_map[result->d_name] = offset;
+ offset = ceph_telldir(cmount, ls_dir);
+ }
+
+ ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
+ offset = ceph_telldir(cmount, ls_dir);
+
+ ASSERT_EQ(offset_map.size(), entries.size());
+ for(i = 0; i < r; ++i) {
+ sprintf(bazstr, "dirf%d", i);
+ ASSERT_TRUE(offset_map.count(bazstr) == 1);
+ }
+
+ // test seekdir
+ ceph_seekdir(cmount, ls_dir, offset);
+ ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
+
+ for (auto p = offset_map.begin(); p != offset_map.end(); ++p) {
+ ceph_seekdir(cmount, ls_dir, p->second);
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ std::string d_name(result->d_name);
+ ASSERT_EQ(p->first, d_name);
+ }
+
+ // test rewinddir
+ ceph_rewinddir(cmount, ls_dir);
+
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+
+ ceph_rewinddir(cmount, ls_dir);
+
+ int t = ceph_telldir(cmount, ls_dir);
+ ASSERT_GT(t, -1);
+
+ ASSERT_TRUE(ceph_readdir(cmount, ls_dir) != NULL);
+
+ // test seekdir - move back to the beginning
+ ceph_seekdir(cmount, ls_dir, t);
+
+ // test getdents
+ struct dirent *getdents_entries;
+ size_t getdents_entries_len = (r + 2) * sizeof(*getdents_entries);
+ getdents_entries = (struct dirent *)malloc(getdents_entries_len);
+
+ int count = 0;
+ std::vector<std::string> found;
+ while (true) {
+ int len = ceph_getdents(cmount, ls_dir, (char *)getdents_entries, getdents_entries_len);
+ if (len == 0)
+ break;
+ ASSERT_GT(len, 0);
+ ASSERT_TRUE((len % sizeof(*getdents_entries)) == 0);
+ int n = len / sizeof(*getdents_entries);
+ int j;
+ if (count == 0) {
+ ASSERT_STREQ(getdents_entries[0].d_name, ".");
+ ASSERT_STREQ(getdents_entries[1].d_name, "..");
+ j = 2;
+ } else {
+ j = 0;
+ }
+ count += n;
+ for(; j < n; ++i, ++j) {
+ const char *name = getdents_entries[j].d_name;
+ found.push_back(name);
+ }
+ }
+ ASSERT_EQ(found, entries);
+ free(getdents_entries);
+
+ // test readdir_r
+ ceph_rewinddir(cmount, ls_dir);
+
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+
+ found.clear();
+ while (true) {
+ struct dirent rdent;
+ int len = ceph_readdir_r(cmount, ls_dir, &rdent);
+ if (len == 0)
+ break;
+ ASSERT_EQ(len, 1);
+ found.push_back(rdent.d_name);
+ }
+ ASSERT_EQ(found, entries);
+
+ // test readdirplus
+ ceph_rewinddir(cmount, ls_dir);
+
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+
+ found.clear();
+ while (true) {
+ struct dirent rdent;
+ struct ceph_statx stx;
+ int len = ceph_readdirplus_r(cmount, ls_dir, &rdent, &stx,
+ CEPH_STATX_SIZE, AT_STATX_DONT_SYNC, NULL);
+ if (len == 0)
+ break;
+ ASSERT_EQ(len, 1);
+ const char *name = rdent.d_name;
+ found.push_back(name);
+ int size;
+ sscanf(name, "dirf%d", &size);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_SIZE);
+ ASSERT_EQ(stx.stx_size, (size_t)size);
+ // On Windows, dirent uses long (4B) inodes, which get trimmed
+ // and can't be used.
+ // TODO: consider defining ceph_dirent.
+ #ifndef _WIN32
+ ASSERT_EQ(stx.stx_ino, rdent.d_ino);
+ #endif
+ //ASSERT_EQ(st.st_mode, (mode_t)0666);
+ }
+ ASSERT_EQ(found, entries);
+
+ ASSERT_EQ(ceph_closedir(cmount, ls_dir), 0);
+
+ // cleanup
+ for(i = 0; i < r; ++i) {
+ sprintf(bazstr, "dir_ls%d/dirf%d", mypid, i);
+ ASSERT_EQ(0, ceph_unlink(cmount, bazstr));
+ }
+ ASSERT_EQ(0, ceph_rmdir(cmount, foostr));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, ManyNestedDirs) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ const char *many_path = "a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a";
+ ASSERT_EQ(ceph_mkdirs(cmount, many_path, 0755), 0);
+
+ int i = 0;
+
+ for(; i < 39; ++i) {
+ ASSERT_EQ(ceph_chdir(cmount, "a"), 0);
+
+ struct ceph_dir_result *dirp;
+ ASSERT_EQ(ceph_opendir(cmount, "a", &dirp), 0);
+ struct dirent *dent = ceph_readdir(cmount, dirp);
+ ASSERT_TRUE(dent != NULL);
+ ASSERT_STREQ(dent->d_name, ".");
+ dent = ceph_readdir(cmount, dirp);
+ ASSERT_TRUE(dent != NULL);
+ ASSERT_STREQ(dent->d_name, "..");
+ dent = ceph_readdir(cmount, dirp);
+ ASSERT_TRUE(dent != NULL);
+ ASSERT_STREQ(dent->d_name, "a");
+ ASSERT_EQ(ceph_closedir(cmount, dirp), 0);
+ }
+
+ ASSERT_STREQ(ceph_getcwd(cmount), "/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a");
+
+ ASSERT_EQ(ceph_chdir(cmount, "a/a/a"), 0);
+
+ for(i = 0; i < 39; ++i) {
+ ASSERT_EQ(ceph_chdir(cmount, ".."), 0);
+ ASSERT_EQ(ceph_rmdir(cmount, "a"), 0);
+ }
+
+ ASSERT_EQ(ceph_chdir(cmount, "/"), 0);
+
+ ASSERT_EQ(ceph_rmdir(cmount, "a/a/a"), 0);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Xattrs) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_xattr_file[256];
+ sprintf(test_xattr_file, "test_xattr_%d", getpid());
+ int fd = ceph_open(cmount, test_xattr_file, O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+
+ // test removing non-existent xattr
+ ASSERT_EQ(-CEPHFS_ENODATA, ceph_removexattr(cmount, test_xattr_file, "user.nosuchxattr"));
+
+ char i = 'a';
+ char xattrk[128];
+ char xattrv[128];
+ for(; i < 'a'+26; ++i) {
+ sprintf(xattrk, "user.test_xattr_%c", i);
+ int len = sprintf(xattrv, "testxattr%c", i);
+ ASSERT_EQ(ceph_setxattr(cmount, test_xattr_file, xattrk, (void *) xattrv, len, XATTR_CREATE), 0);
+ }
+
+ // zero size should return required buffer length
+ int len_needed = ceph_listxattr(cmount, test_xattr_file, NULL, 0);
+ ASSERT_GT(len_needed, 0);
+
+ // buffer size smaller than needed should fail
+ char xattrlist[128*26];
+ ASSERT_GT(sizeof(xattrlist), (size_t)len_needed);
+ int len = ceph_listxattr(cmount, test_xattr_file, xattrlist, len_needed - 1);
+ ASSERT_EQ(-CEPHFS_ERANGE, len);
+
+ len = ceph_listxattr(cmount, test_xattr_file, xattrlist, sizeof(xattrlist));
+ ASSERT_EQ(len, len_needed);
+ char *p = xattrlist;
+ char *n;
+ i = 'a';
+ while (len > 0) {
+ // ceph.* xattrs should not be listed
+ ASSERT_NE(strncmp(p, "ceph.", 5), 0);
+
+ sprintf(xattrk, "user.test_xattr_%c", i);
+ ASSERT_STREQ(p, xattrk);
+
+ char gxattrv[128];
+ std::cout << "getting attr " << p << std::endl;
+ int alen = ceph_getxattr(cmount, test_xattr_file, p, (void *) gxattrv, 128);
+ ASSERT_GT(alen, 0);
+ sprintf(xattrv, "testxattr%c", i);
+ ASSERT_TRUE(!strncmp(xattrv, gxattrv, alen));
+
+ n = strchr(p, '\0');
+ n++;
+ len -= (n - p);
+ p = n;
+ ++i;
+ }
+
+ i = 'a';
+ for(i = 'a'; i < 'a'+26; ++i) {
+ sprintf(xattrk, "user.test_xattr_%c", i);
+ ASSERT_EQ(ceph_removexattr(cmount, test_xattr_file, xattrk), 0);
+ }
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+
+}
+
+TEST(LibCephFS, Xattrs_ll) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_xattr_file[256];
+ sprintf(test_xattr_file, "test_xattr_%d", getpid());
+ int fd = ceph_open(cmount, test_xattr_file, O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+ ceph_close(cmount, fd);
+
+ Inode *root = NULL;
+ Inode *existent_file_handle = NULL;
+
+ int res = ceph_ll_lookup_root(cmount, &root);
+ ASSERT_EQ(res, 0);
+
+ UserPerm *perms = ceph_mount_perms(cmount);
+ struct ceph_statx stx;
+
+ res = ceph_ll_lookup(cmount, root, test_xattr_file, &existent_file_handle,
+ &stx, 0, 0, perms);
+ ASSERT_EQ(res, 0);
+
+ const char *valid_name = "user.attrname";
+ const char *value = "attrvalue";
+ char value_buf[256] = { 0 };
+
+ res = ceph_ll_setxattr(cmount, existent_file_handle, valid_name, value, strlen(value), 0, perms);
+ ASSERT_EQ(res, 0);
+
+ res = ceph_ll_getxattr(cmount, existent_file_handle, valid_name, value_buf, 256, perms);
+ ASSERT_EQ(res, (int)strlen(value));
+
+ value_buf[res] = '\0';
+ ASSERT_STREQ(value_buf, value);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LstatSlashdot) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, "/.", &stx, 0, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(ceph_statx(cmount, ".", &stx, 0, AT_SYMLINK_NOFOLLOW), 0);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, StatDirNlink) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_dir1[256];
+ sprintf(test_dir1, "dir1_symlinks_%d", getpid());
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0700), 0);
+
+ int fd = ceph_open(cmount, test_dir1, O_DIRECTORY|O_RDONLY, 0);
+ ASSERT_GT(fd, 0);
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 2u);
+
+ {
+ char test_dir2[296];
+ sprintf(test_dir2, "%s/.", test_dir1);
+ ASSERT_EQ(ceph_statx(cmount, test_dir2, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 2u);
+ }
+
+ {
+ char test_dir2[296];
+ sprintf(test_dir2, "%s/1", test_dir1);
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0700), 0);
+ ASSERT_EQ(ceph_statx(cmount, test_dir2, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 2u);
+ ASSERT_EQ(ceph_statx(cmount, test_dir1, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 3u);
+ sprintf(test_dir2, "%s/2", test_dir1);
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0700), 0);
+ ASSERT_EQ(ceph_statx(cmount, test_dir1, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 4u);
+ sprintf(test_dir2, "%s/1/1", test_dir1);
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0700), 0);
+ ASSERT_EQ(ceph_statx(cmount, test_dir1, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 4u);
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir2), 0);
+ ASSERT_EQ(ceph_statx(cmount, test_dir1, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 4u);
+ sprintf(test_dir2, "%s/1", test_dir1);
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir2), 0);
+ ASSERT_EQ(ceph_statx(cmount, test_dir1, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 3u);
+ sprintf(test_dir2, "%s/2", test_dir1);
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir2), 0);
+ ASSERT_EQ(ceph_statx(cmount, test_dir1, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 2u);
+ }
+
+ ASSERT_EQ(ceph_rmdir(cmount, test_dir1), 0);
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_NLINK, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_nlink, 0u);
+
+ ceph_close(cmount, fd);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DoubleChmod) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+ sprintf(test_file, "test_perms_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ // write some stuff
+ const char *bytes = "foobarbaz";
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+
+ ceph_close(cmount, fd);
+
+ // set perms to read but can't write
+ ASSERT_EQ(ceph_chmod(cmount, test_file, 0400), 0);
+
+ fd = ceph_open(cmount, test_file, O_RDWR, 0);
+ ASSERT_EQ(fd, -CEPHFS_EACCES);
+
+ fd = ceph_open(cmount, test_file, O_RDONLY, 0);
+ ASSERT_GT(fd, -1);
+
+ char buf[100];
+ int ret = ceph_read(cmount, fd, buf, 100, 0);
+ ASSERT_EQ(ret, (int)strlen(bytes));
+ buf[ret] = '\0';
+ ASSERT_STREQ(buf, bytes);
+
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), -CEPHFS_EBADF);
+
+ ceph_close(cmount, fd);
+
+ // reset back to writeable
+ ASSERT_EQ(ceph_chmod(cmount, test_file, 0600), 0);
+
+ // ensure perms are correct
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx, CEPH_STATX_MODE, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(stx.stx_mode, 0100600U);
+
+ fd = ceph_open(cmount, test_file, O_RDWR, 0);
+ ASSERT_GT(fd, 0);
+
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+ ceph_close(cmount, fd);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Fchmod) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+ sprintf(test_file, "test_perms_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ // write some stuff
+ const char *bytes = "foobarbaz";
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+
+ // set perms to read but can't write
+ ASSERT_EQ(ceph_fchmod(cmount, fd, 0400), 0);
+
+ char buf[100];
+ int ret = ceph_read(cmount, fd, buf, 100, 0);
+ ASSERT_EQ(ret, (int)strlen(bytes));
+ buf[ret] = '\0';
+ ASSERT_STREQ(buf, bytes);
+
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+
+ ceph_close(cmount, fd);
+
+ ASSERT_EQ(ceph_open(cmount, test_file, O_RDWR, 0), -CEPHFS_EACCES);
+
+ // reset back to writeable
+ ASSERT_EQ(ceph_chmod(cmount, test_file, 0600), 0);
+
+ fd = ceph_open(cmount, test_file, O_RDWR, 0);
+ ASSERT_GT(fd, 0);
+
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+ ceph_close(cmount, fd);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Lchmod) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+ sprintf(test_file, "test_perms_lchmod_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ // write some stuff
+ const char *bytes = "foobarbaz";
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+ ceph_close(cmount, fd);
+
+ // Create symlink
+ char test_symlink[256];
+ sprintf(test_symlink, "test_lchmod_sym_%d", getpid());
+ ASSERT_EQ(ceph_symlink(cmount, test_file, test_symlink), 0);
+
+ // get symlink stat - lstat
+ struct ceph_statx stx_orig1;
+ ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_orig1, CEPH_STATX_ALL_STATS, AT_SYMLINK_NOFOLLOW), 0);
+
+ // Change mode on symlink file
+ ASSERT_EQ(ceph_lchmod(cmount, test_symlink, 0400), 0);
+ struct ceph_statx stx_orig2;
+ ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_orig2, CEPH_STATX_ALL_STATS, AT_SYMLINK_NOFOLLOW), 0);
+
+ // Compare modes
+ ASSERT_NE(stx_orig1.stx_mode, stx_orig2.stx_mode);
+ static const int permbits = S_IRWXU|S_IRWXG|S_IRWXO;
+ ASSERT_EQ(permbits&stx_orig1.stx_mode, 0777);
+ ASSERT_EQ(permbits&stx_orig2.stx_mode, 0400);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Fchown) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+ sprintf(test_file, "test_fchown_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ // set perms to readable and writeable only by owner
+ ASSERT_EQ(ceph_fchmod(cmount, fd, 0600), 0);
+
+ // change ownership to nobody -- we assume nobody exists and id is always 65534
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "0"), 0);
+ ASSERT_EQ(ceph_fchown(cmount, fd, 65534, 65534), 0);
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "1"), 0);
+
+ ceph_close(cmount, fd);
+
+ // "nobody" will be ignored on Windows
+ #ifndef _WIN32
+ fd = ceph_open(cmount, test_file, O_RDWR, 0);
+ ASSERT_EQ(fd, -CEPHFS_EACCES);
+ #endif
+
+ ceph_shutdown(cmount);
+}
+
+#if defined(__linux__) && defined(O_PATH)
+TEST(LibCephFS, FlagO_PATH) {
+ struct ceph_mount_info *cmount;
+
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, NULL));
+
+ char test_file[PATH_MAX];
+ sprintf(test_file, "test_oflag_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR|O_PATH, 0666);
+ ASSERT_EQ(-CEPHFS_ENOENT, fd);
+
+ fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ // ok, the file has been created. perform real checks now
+ fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR|O_PATH, 0666);
+ ASSERT_GT(fd, 0);
+
+ char buf[128];
+ ASSERT_EQ(-CEPHFS_EBADF, ceph_read(cmount, fd, buf, sizeof(buf), 0));
+ ASSERT_EQ(-CEPHFS_EBADF, ceph_write(cmount, fd, buf, sizeof(buf), 0));
+
+ // set perms to readable and writeable only by owner
+ ASSERT_EQ(-CEPHFS_EBADF, ceph_fchmod(cmount, fd, 0600));
+
+ // change ownership to nobody -- we assume nobody exists and id is always 65534
+ ASSERT_EQ(-CEPHFS_EBADF, ceph_fchown(cmount, fd, 65534, 65534));
+
+ // try to sync
+ ASSERT_EQ(-CEPHFS_EBADF, ceph_fsync(cmount, fd, false));
+
+ struct ceph_statx stx;
+ ASSERT_EQ(0, ceph_fstatx(cmount, fd, &stx, 0, 0));
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ceph_shutdown(cmount);
+}
+#endif /* __linux */
+
+TEST(LibCephFS, Symlinks) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+ sprintf(test_file, "test_symlinks_%d", getpid());
+
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ ceph_close(cmount, fd);
+
+ char test_symlink[256];
+ sprintf(test_symlink, "test_symlinks_sym_%d", getpid());
+
+ ASSERT_EQ(ceph_symlink(cmount, test_file, test_symlink), 0);
+
+ // test the O_NOFOLLOW case
+ fd = ceph_open(cmount, test_symlink, O_NOFOLLOW, 0);
+ ASSERT_EQ(fd, -CEPHFS_ELOOP);
+
+ // stat the original file
+ struct ceph_statx stx_orig;
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx_orig, CEPH_STATX_ALL_STATS, 0), 0);
+ // stat the symlink
+ struct ceph_statx stx_symlink_orig;
+ ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_symlink_orig, CEPH_STATX_ALL_STATS, 0), 0);
+ // ensure the statx bufs are equal
+ ASSERT_EQ(memcmp(&stx_orig, &stx_symlink_orig, sizeof(stx_orig)), 0);
+
+ sprintf(test_file, "/test_symlinks_abs_%d", getpid());
+
+ fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ ceph_close(cmount, fd);
+
+ sprintf(test_symlink, "/test_symlinks_abs_sym_%d", getpid());
+
+ ASSERT_EQ(ceph_symlink(cmount, test_file, test_symlink), 0);
+
+ // stat the original file
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx_orig, CEPH_STATX_ALL_STATS, 0), 0);
+ // stat the symlink
+ ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_symlink_orig, CEPH_STATX_ALL_STATS, 0), 0);
+ // ensure the statx bufs are equal
+ ASSERT_TRUE(!memcmp(&stx_orig, &stx_symlink_orig, sizeof(stx_orig)));
+
+ // test lstat
+ ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_orig, CEPH_STATX_ALL_STATS, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_TRUE(S_ISLNK(stx_orig.stx_mode));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DirSyms) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_dir1[256];
+ sprintf(test_dir1, "dir1_symlinks_%d", getpid());
+
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0700), 0);
+
+ char test_symdir[256];
+ sprintf(test_symdir, "symdir_symlinks_%d", getpid());
+
+ ASSERT_EQ(ceph_symlink(cmount, test_dir1, test_symdir), 0);
+
+ char test_file[256];
+ sprintf(test_file, "/symdir_symlinks_%d/test_symdir_file", getpid());
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600);
+ ASSERT_GT(fd, 0);
+ ceph_close(cmount, fd);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx, 0, AT_SYMLINK_NOFOLLOW), 0);
+
+ // ensure that its a file not a directory we get back
+ ASSERT_TRUE(S_ISREG(stx.stx_mode));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LoopSyms) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_dir1[256];
+ sprintf(test_dir1, "dir1_loopsym_%d", getpid());
+
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0700), 0);
+
+ char test_dir2[256];
+ sprintf(test_dir2, "/dir1_loopsym_%d/loop_dir", getpid());
+
+ ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0700), 0);
+
+ // symlink it itself: /path/to/mysym -> /path/to/mysym
+ char test_symdir[256];
+ sprintf(test_symdir, "/dir1_loopsym_%d/loop_dir/symdir", getpid());
+
+ ASSERT_EQ(ceph_symlink(cmount, test_symdir, test_symdir), 0);
+
+ char test_file[256];
+ sprintf(test_file, "/dir1_loopsym_%d/loop_dir/symdir/test_loopsym_file", getpid());
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600);
+ ASSERT_EQ(fd, -CEPHFS_ELOOP);
+
+ // loop: /a -> /b, /b -> /c, /c -> /a
+ char a[264], b[264], c[264];
+ sprintf(a, "/%s/a", test_dir1);
+ sprintf(b, "/%s/b", test_dir1);
+ sprintf(c, "/%s/c", test_dir1);
+ ASSERT_EQ(ceph_symlink(cmount, a, b), 0);
+ ASSERT_EQ(ceph_symlink(cmount, b, c), 0);
+ ASSERT_EQ(ceph_symlink(cmount, c, a), 0);
+ ASSERT_EQ(ceph_open(cmount, a, O_RDWR, 0), -CEPHFS_ELOOP);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, HardlinkNoOriginal) {
+
+ int mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir[256];
+ sprintf(dir, "/test_rmdirfail%d", mypid);
+ ASSERT_EQ(ceph_mkdir(cmount, dir, 0777), 0);
+
+ ASSERT_EQ(ceph_chdir(cmount, dir), 0);
+
+ int fd = ceph_open(cmount, "f1", O_CREAT, 0644);
+ ASSERT_GT(fd, 0);
+
+ ceph_close(cmount, fd);
+
+ // create hard link
+ ASSERT_EQ(ceph_link(cmount, "f1", "hardl1"), 0);
+
+ // remove file link points to
+ ASSERT_EQ(ceph_unlink(cmount, "f1"), 0);
+
+ ceph_shutdown(cmount);
+
+ // now cleanup
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+ ASSERT_EQ(ceph_chdir(cmount, dir), 0);
+ ASSERT_EQ(ceph_unlink(cmount, "hardl1"), 0);
+ ASSERT_EQ(ceph_rmdir(cmount, dir), 0);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, BadArgument) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ int fd = ceph_open(cmount, "test_file", O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+ char buf[100];
+ ASSERT_EQ(ceph_write(cmount, fd, buf, sizeof(buf), 0), (int)sizeof(buf));
+ ASSERT_EQ(ceph_read(cmount, fd, buf, 0, 5), 0);
+ ceph_close(cmount, fd);
+ ASSERT_EQ(ceph_unlink(cmount, "test_file"), 0);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, BadFileDesc) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ ASSERT_EQ(ceph_fchmod(cmount, -1, 0655), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_close(cmount, -1), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_lseek(cmount, -1, 0, SEEK_SET), -CEPHFS_EBADF);
+
+ char buf[0];
+ ASSERT_EQ(ceph_read(cmount, -1, buf, 0, 0), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_write(cmount, -1, buf, 0, 0), -CEPHFS_EBADF);
+
+ ASSERT_EQ(ceph_ftruncate(cmount, -1, 0), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_fsync(cmount, -1, 0), -CEPHFS_EBADF);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_fstatx(cmount, -1, &stx, 0, 0), -CEPHFS_EBADF);
+
+ struct sockaddr_storage addr;
+ ASSERT_EQ(ceph_get_file_stripe_address(cmount, -1, 0, &addr, 1), -CEPHFS_EBADF);
+
+ ASSERT_EQ(ceph_get_file_stripe_unit(cmount, -1), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_get_file_pool(cmount, -1), -CEPHFS_EBADF);
+ char poolname[80];
+ ASSERT_EQ(ceph_get_file_pool_name(cmount, -1, poolname, sizeof(poolname)), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_get_file_replication(cmount, -1), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_get_file_object_size(cmount, -1), -CEPHFS_EBADF);
+ int stripe_unit, stripe_count, object_size, pg_pool;
+ ASSERT_EQ(ceph_get_file_layout(cmount, -1, &stripe_unit, &stripe_count, &object_size, &pg_pool), -CEPHFS_EBADF);
+ ASSERT_EQ(ceph_get_file_stripe_count(cmount, -1), -CEPHFS_EBADF);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, ReadEmptyFile) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ // test the read_sync path in the client for zero files
+ ASSERT_EQ(ceph_conf_set(cmount, "client_debug_force_sync_read", "true"), 0);
+
+ int mypid = getpid();
+ char testf[256];
+
+ sprintf(testf, "test_reademptyfile%d", mypid);
+ int fd = ceph_open(cmount, testf, O_CREAT|O_TRUNC|O_WRONLY, 0644);
+ ASSERT_GT(fd, 0);
+
+ ceph_close(cmount, fd);
+
+ fd = ceph_open(cmount, testf, O_RDONLY, 0);
+ ASSERT_GT(fd, 0);
+
+ char buf[4096];
+ ASSERT_EQ(ceph_read(cmount, fd, buf, 4096, 0), 0);
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, PreadvPwritev) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ int mypid = getpid();
+ char testf[256];
+
+ sprintf(testf, "test_preadvpwritevfile%d", mypid);
+ int fd = ceph_open(cmount, testf, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ char out0[] = "hello ";
+ char out1[] = "world\n";
+ struct iovec iov_out[2] = {
+ {out0, sizeof(out0)},
+ {out1, sizeof(out1)},
+ };
+ char in0[sizeof(out0)];
+ char in1[sizeof(out1)];
+ struct iovec iov_in[2] = {
+ {in0, sizeof(in0)},
+ {in1, sizeof(in1)},
+ };
+ ssize_t nwritten = iov_out[0].iov_len + iov_out[1].iov_len;
+ ssize_t nread = iov_in[0].iov_len + iov_in[1].iov_len;
+
+ ASSERT_EQ(ceph_pwritev(cmount, fd, iov_out, 2, 0), nwritten);
+ ASSERT_EQ(ceph_preadv(cmount, fd, iov_in, 2, 0), nread);
+ ASSERT_EQ(0, strncmp((const char*)iov_in[0].iov_base, (const char*)iov_out[0].iov_base, iov_out[0].iov_len));
+ ASSERT_EQ(0, strncmp((const char*)iov_in[1].iov_base, (const char*)iov_out[1].iov_base, iov_out[1].iov_len));
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LlreadvLlwritev) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ int mypid = getpid();
+ char filename[256];
+
+ sprintf(filename, "test_llreadvllwritevfile%u", mypid);
+
+ Inode *root, *file;
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+
+ Fh *fh;
+ struct ceph_statx stx;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
+ O_RDWR|O_CREAT|O_TRUNC, &file, &fh, &stx, 0, 0, perms), 0);
+
+ /* Reopen read-only */
+ char out0[] = "hello ";
+ char out1[] = "world\n";
+ struct iovec iov_out[2] = {
+ {out0, sizeof(out0)},
+ {out1, sizeof(out1)},
+ };
+ char in0[sizeof(out0)];
+ char in1[sizeof(out1)];
+ struct iovec iov_in[2] = {
+ {in0, sizeof(in0)},
+ {in1, sizeof(in1)},
+ };
+ ssize_t nwritten = iov_out[0].iov_len + iov_out[1].iov_len;
+ ssize_t nread = iov_in[0].iov_len + iov_in[1].iov_len;
+
+ ASSERT_EQ(ceph_ll_writev(cmount, fh, iov_out, 2, 0), nwritten);
+ ASSERT_EQ(ceph_ll_readv(cmount, fh, iov_in, 2, 0), nread);
+ ASSERT_EQ(0, strncmp((const char*)iov_in[0].iov_base, (const char*)iov_out[0].iov_base, iov_out[0].iov_len));
+ ASSERT_EQ(0, strncmp((const char*)iov_in[1].iov_base, (const char*)iov_out[1].iov_base, iov_out[1].iov_len));
+
+ ceph_ll_close(cmount, fh);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, StripeUnitGran) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+ ASSERT_GT(ceph_get_stripe_unit_granularity(cmount), 0);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Rename) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ int mypid = getpid();
+ char path_src[256];
+ char path_dst[256];
+
+ /* make a source file */
+ sprintf(path_src, "test_rename_src%d", mypid);
+ int fd = ceph_open(cmount, path_src, O_CREAT|O_TRUNC|O_WRONLY, 0777);
+ ASSERT_GT(fd, 0);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ /* rename to a new dest path */
+ sprintf(path_dst, "test_rename_dst%d", mypid);
+ ASSERT_EQ(0, ceph_rename(cmount, path_src, path_dst));
+
+ /* test that dest path exists */
+ struct ceph_statx stx;
+ ASSERT_EQ(0, ceph_statx(cmount, path_dst, &stx, 0, 0));
+
+ /* test that src path doesn't exist */
+ ASSERT_EQ(-CEPHFS_ENOENT, ceph_statx(cmount, path_src, &stx, 0, AT_SYMLINK_NOFOLLOW));
+
+ /* rename with non-existent source path */
+ ASSERT_EQ(-CEPHFS_ENOENT, ceph_rename(cmount, path_src, path_dst));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, path_dst));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, UseUnmounted) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+
+ struct statvfs stvfs;
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_statfs(cmount, "/", &stvfs));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_local_osd(cmount));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_chdir(cmount, "/"));
+
+ struct ceph_dir_result *dirp;
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_opendir(cmount, "/", &dirp));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_closedir(cmount, dirp));
+
+ ceph_readdir(cmount, dirp);
+ EXPECT_EQ(CEPHFS_ENOTCONN, errno);
+
+ struct dirent rdent;
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_readdir_r(cmount, dirp, &rdent));
+
+ struct ceph_statx stx;
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_readdirplus_r(cmount, dirp, &rdent, &stx, 0, 0, NULL));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_getdents(cmount, dirp, NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_getdnames(cmount, dirp, NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_telldir(cmount, dirp));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_link(cmount, "/", "/link"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_unlink(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_rename(cmount, "/path", "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_mkdir(cmount, "/", 0655));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_mkdirs(cmount, "/", 0655));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_rmdir(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_readlink(cmount, "/path", NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_symlink(cmount, "/path", "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_statx(cmount, "/path", &stx, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_setattrx(cmount, "/path", &stx, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_getxattr(cmount, "/path", "name", NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_lgetxattr(cmount, "/path", "name", NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_listxattr(cmount, "/path", NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_llistxattr(cmount, "/path", NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_removexattr(cmount, "/path", "name"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_lremovexattr(cmount, "/path", "name"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_setxattr(cmount, "/path", "name", NULL, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_lsetxattr(cmount, "/path", "name", NULL, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_fsetattrx(cmount, 0, &stx, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_chmod(cmount, "/path", 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_fchmod(cmount, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_chown(cmount, "/path", 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_lchown(cmount, "/path", 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_fchown(cmount, 0, 0, 0));
+
+ struct utimbuf utb;
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_utime(cmount, "/path", &utb));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_truncate(cmount, "/path", 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_mknod(cmount, "/path", 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_open(cmount, "/path", 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_open_layout(cmount, "/path", 0, 0, 0, 0, 0, "pool"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_close(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_lseek(cmount, 0, 0, SEEK_SET));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_read(cmount, 0, NULL, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_write(cmount, 0, NULL, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_ftruncate(cmount, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_fsync(cmount, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_fstatx(cmount, 0, &stx, 0, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_sync_fs(cmount));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_stripe_unit(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_stripe_count(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_layout(cmount, 0, NULL, NULL ,NULL ,NULL));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_object_size(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_pool(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_pool_name(cmount, 0, NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_replication(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_path_replication(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_path_layout(cmount, "/path", NULL, NULL, NULL, NULL));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_path_object_size(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_path_stripe_count(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_path_stripe_unit(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_path_pool(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_path_pool_name(cmount, "/path", NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_pool_name(cmount, 0, NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_stripe_address(cmount, 0, 0, NULL, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_localize_reads(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_debug_get_fd_caps(cmount, 0));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_debug_get_file_caps(cmount, "/path"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_stripe_unit_granularity(cmount));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_pool_id(cmount, "data"));
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_pool_replication(cmount, 1));
+
+ ceph_release(cmount);
+}
+
+TEST(LibCephFS, GetPoolId) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char name[80];
+ memset(name, 0, sizeof(name));
+ ASSERT_LE(0, ceph_get_path_pool_name(cmount, "/", name, sizeof(name)));
+ ASSERT_GE(ceph_get_pool_id(cmount, name), 0);
+ ASSERT_EQ(ceph_get_pool_id(cmount, "weflkjwelfjwlkejf"), -CEPHFS_ENOENT);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, GetPoolReplication) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ /* negative pools */
+ ASSERT_EQ(ceph_get_pool_replication(cmount, -10), -CEPHFS_ENOENT);
+
+ /* valid pool */
+ int pool_id;
+ int stripe_unit, stripe_count, object_size;
+ ASSERT_EQ(0, ceph_get_path_layout(cmount, "/", &stripe_unit, &stripe_count,
+ &object_size, &pool_id));
+ ASSERT_GE(pool_id, 0);
+ ASSERT_GT(ceph_get_pool_replication(cmount, pool_id), 0);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, GetExtentOsds) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_file_extent_osds(cmount, 0, 0, NULL, NULL, 0));
+
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ int stripe_unit = (1<<18);
+
+ /* make a file! */
+ char test_file[256];
+ sprintf(test_file, "test_extent_osds_%d", getpid());
+ int fd = ceph_open_layout(cmount, test_file, O_CREAT|O_RDWR, 0666,
+ stripe_unit, 2, stripe_unit*2, NULL);
+ ASSERT_GT(fd, 0);
+
+ /* get back how many osds > 0 */
+ int ret = ceph_get_file_extent_osds(cmount, fd, 0, NULL, NULL, 0);
+ EXPECT_GT(ret, 0);
+
+ int64_t len;
+ int osds[ret];
+
+ /* full stripe extent */
+ EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 0, &len, osds, ret));
+ EXPECT_EQ(len, (int64_t)stripe_unit);
+
+ /* half stripe extent */
+ EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, stripe_unit/2, &len, osds, ret));
+ EXPECT_EQ(len, (int64_t)stripe_unit/2);
+
+ /* 1.5 stripe unit offset -1 byte */
+ EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 3*stripe_unit/2-1, &len, osds, ret));
+ EXPECT_EQ(len, (int64_t)stripe_unit/2+1);
+
+ /* 1.5 stripe unit offset +1 byte */
+ EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 3*stripe_unit/2+1, &len, osds, ret));
+ EXPECT_EQ(len, (int64_t)stripe_unit/2-1);
+
+ /* only when more than 1 osd */
+ if (ret > 1) {
+ EXPECT_EQ(-CEPHFS_ERANGE, ceph_get_file_extent_osds(cmount, fd, 0, NULL, osds, 1));
+ }
+
+ ceph_close(cmount, fd);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, GetOsdCrushLocation) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_osd_crush_location(cmount, 0, NULL, 0));
+
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ ASSERT_EQ(ceph_get_osd_crush_location(cmount, 0, NULL, 1), -CEPHFS_EINVAL);
+
+ char path[256];
+ ASSERT_EQ(ceph_get_osd_crush_location(cmount, 9999999, path, 0), -CEPHFS_ENOENT);
+ ASSERT_EQ(ceph_get_osd_crush_location(cmount, -1, path, 0), -CEPHFS_EINVAL);
+
+ char test_file[256];
+ sprintf(test_file, "test_osds_loc_%d", getpid());
+ int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+
+ /* get back how many osds > 0 */
+ int ret = ceph_get_file_extent_osds(cmount, fd, 0, NULL, NULL, 0);
+ EXPECT_GT(ret, 0);
+
+ /* full stripe extent */
+ int osds[ret];
+ EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 0, NULL, osds, ret));
+
+ ASSERT_GT(ceph_get_osd_crush_location(cmount, 0, path, 0), 0);
+ ASSERT_EQ(ceph_get_osd_crush_location(cmount, 0, path, 1), -CEPHFS_ERANGE);
+
+ for (int i = 0; i < ret; i++) {
+ int len = ceph_get_osd_crush_location(cmount, osds[i], path, sizeof(path));
+ ASSERT_GT(len, 0);
+ int pos = 0;
+ while (pos < len) {
+ std::string type(path + pos);
+ ASSERT_GT((int)type.size(), 0);
+ pos += type.size() + 1;
+
+ std::string name(path + pos);
+ ASSERT_GT((int)name.size(), 0);
+ pos += name.size() + 1;
+ }
+ }
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, GetOsdAddr) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+
+ EXPECT_EQ(-CEPHFS_ENOTCONN, ceph_get_osd_addr(cmount, 0, NULL));
+
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ ASSERT_EQ(-CEPHFS_EINVAL, ceph_get_osd_addr(cmount, 0, NULL));
+
+ struct sockaddr_storage addr;
+ ASSERT_EQ(-CEPHFS_ENOENT, ceph_get_osd_addr(cmount, -1, &addr));
+ ASSERT_EQ(-CEPHFS_ENOENT, ceph_get_osd_addr(cmount, 9999999, &addr));
+
+ ASSERT_EQ(0, ceph_get_osd_addr(cmount, 0, &addr));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, OpenNoClose) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ pid_t mypid = getpid();
+ char str_buf[256];
+ sprintf(str_buf, "open_no_close_dir%d", mypid);
+ ASSERT_EQ(0, ceph_mkdirs(cmount, str_buf, 0777));
+
+ struct ceph_dir_result *ls_dir = NULL;
+ ASSERT_EQ(ceph_opendir(cmount, str_buf, &ls_dir), 0);
+
+ sprintf(str_buf, "open_no_close_file%d", mypid);
+ int fd = ceph_open(cmount, str_buf, O_RDONLY|O_CREAT, 0666);
+ ASSERT_LT(0, fd);
+
+ // shutdown should force close opened file/dir
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Nlink) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ Inode *root, *dir, *file;
+
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+
+ char dirname[32], filename[32], linkname[32];
+ sprintf(dirname, "nlinkdir%x", getpid());
+ sprintf(filename, "nlinkorig%x", getpid());
+ sprintf(linkname, "nlinklink%x", getpid());
+
+ struct ceph_statx stx;
+ Fh *fh;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ ASSERT_EQ(ceph_ll_mkdir(cmount, root, dirname, 0755, &dir, &stx, 0, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_create(cmount, dir, filename, 0666, O_RDWR|O_CREAT|O_EXCL,
+ &file, &fh, &stx, CEPH_STATX_NLINK, 0, perms), 0);
+ ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
+ ASSERT_EQ(stx.stx_nlink, (nlink_t)1);
+
+ ASSERT_EQ(ceph_ll_link(cmount, file, dir, linkname, perms), 0);
+ ASSERT_EQ(ceph_ll_getattr(cmount, file, &stx, CEPH_STATX_NLINK, 0, perms), 0);
+ ASSERT_EQ(stx.stx_nlink, (nlink_t)2);
+
+ ASSERT_EQ(ceph_ll_unlink(cmount, dir, linkname, perms), 0);
+ ASSERT_EQ(ceph_ll_lookup(cmount, dir, filename, &file, &stx,
+ CEPH_STATX_NLINK, 0, perms), 0);
+ ASSERT_EQ(stx.stx_nlink, (nlink_t)1);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SlashDotDot) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, "/.", &stx, CEPH_STATX_INO, 0), 0);
+
+ ino_t ino = stx.stx_ino;
+ ASSERT_EQ(ceph_statx(cmount, "/..", &stx, CEPH_STATX_INO, 0), 0);
+
+ /* At root, "." and ".." should be the same inode */
+ ASSERT_EQ(ino, stx.stx_ino);
+
+ /* Test accessing the parent of an unlinked directory */
+ char dir1[32], dir2[56];
+ sprintf(dir1, "/sldotdot%x", getpid());
+ sprintf(dir2, "%s/sub%x", dir1, getpid());
+
+ ASSERT_EQ(ceph_mkdir(cmount, dir1, 0755), 0);
+ ASSERT_EQ(ceph_mkdir(cmount, dir2, 0755), 0);
+
+ ASSERT_EQ(ceph_chdir(cmount, dir2), 0);
+
+ /* Test behavior when unlinking cwd */
+ struct ceph_dir_result *rdir;
+ ASSERT_EQ(ceph_opendir(cmount, ".", &rdir), 0);
+ ASSERT_EQ(ceph_rmdir(cmount, dir2), 0);
+
+ /* get "." entry */
+ struct dirent *result = ceph_readdir(cmount, rdir);
+ ino = result->d_ino;
+
+ /* get ".." entry */
+ result = ceph_readdir(cmount, rdir);
+ ASSERT_EQ(ino, result->d_ino);
+ ceph_closedir(cmount, rdir);
+
+ /* Make sure it works same way when mounting subtree */
+ ASSERT_EQ(ceph_unmount(cmount), 0);
+ ASSERT_EQ(ceph_mount(cmount, dir1), 0);
+ ASSERT_EQ(ceph_statx(cmount, "/..", &stx, CEPH_STATX_INO, 0), 0);
+
+ /* Test readdir behavior */
+ ASSERT_EQ(ceph_opendir(cmount, "/", &rdir), 0);
+ result = ceph_readdir(cmount, rdir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ ino = result->d_ino;
+ result = ceph_readdir(cmount, rdir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+ ASSERT_EQ(ino, result->d_ino);
+
+ ceph_shutdown(cmount);
+}
+
+static inline bool
+timespec_eq(timespec const& lhs, timespec const& rhs)
+{
+ return lhs.tv_sec == rhs.tv_sec && lhs.tv_nsec == rhs.tv_nsec;
+}
+
+TEST(LibCephFS, Btime) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char filename[32];
+ sprintf(filename, "/getattrx%x", getpid());
+
+ ceph_unlink(cmount, filename);
+ int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
+ ASSERT_LT(0, fd);
+
+ /* make sure fstatx works */
+ struct ceph_statx stx;
+
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_CTIME|CEPH_STATX_BTIME, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & (CEPH_STATX_CTIME|CEPH_STATX_BTIME));
+ ASSERT_TRUE(timespec_eq(stx.stx_ctime, stx.stx_btime));
+ ceph_close(cmount, fd);
+
+ ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_CTIME|CEPH_STATX_BTIME, 0), 0);
+ ASSERT_TRUE(timespec_eq(stx.stx_ctime, stx.stx_btime));
+ ASSERT_TRUE(stx.stx_mask & (CEPH_STATX_CTIME|CEPH_STATX_BTIME));
+
+ struct timespec old_btime = stx.stx_btime;
+
+ /* Now sleep, do a chmod and verify that the ctime changed, but btime didn't */
+ sleep(1);
+ ASSERT_EQ(ceph_chmod(cmount, filename, 0644), 0);
+ ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_CTIME|CEPH_STATX_BTIME, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_BTIME);
+ ASSERT_TRUE(timespec_eq(stx.stx_btime, old_btime));
+ ASSERT_FALSE(timespec_eq(stx.stx_ctime, stx.stx_btime));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SetBtime) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char filename[32];
+ sprintf(filename, "/setbtime%x", getpid());
+
+ ceph_unlink(cmount, filename);
+ int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
+ ASSERT_LT(0, fd);
+ ceph_close(cmount, fd);
+
+ struct ceph_statx stx;
+ struct timespec old_btime = { 1, 2 };
+
+ stx.stx_btime = old_btime;
+
+ ASSERT_EQ(ceph_setattrx(cmount, filename, &stx, CEPH_SETATTR_BTIME, 0), 0);
+
+ ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_BTIME, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_BTIME);
+ ASSERT_TRUE(timespec_eq(stx.stx_btime, old_btime));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LazyStatx) {
+ struct ceph_mount_info *cmount1, *cmount2;
+ ASSERT_EQ(ceph_create(&cmount1, NULL), 0);
+ ASSERT_EQ(ceph_create(&cmount2, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount1, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount2, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount1, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount2, NULL));
+ ASSERT_EQ(ceph_mount(cmount1, "/"), 0);
+ ASSERT_EQ(ceph_mount(cmount2, "/"), 0);
+
+ char filename[32];
+ sprintf(filename, "lazystatx%x", getpid());
+
+ Inode *root1, *file1, *root2, *file2;
+ struct ceph_statx stx;
+ Fh *fh;
+ UserPerm *perms1 = ceph_mount_perms(cmount1);
+ UserPerm *perms2 = ceph_mount_perms(cmount2);
+
+ ASSERT_EQ(ceph_ll_lookup_root(cmount1, &root1), 0);
+ ceph_ll_unlink(cmount1, root1, filename, perms1);
+ ASSERT_EQ(ceph_ll_create(cmount1, root1, filename, 0666, O_RDWR|O_CREAT|O_EXCL,
+ &file1, &fh, &stx, 0, 0, perms1), 0);
+ ASSERT_EQ(ceph_ll_close(cmount1, fh), 0);
+
+ ASSERT_EQ(ceph_ll_lookup_root(cmount2, &root2), 0);
+
+ ASSERT_EQ(ceph_ll_lookup(cmount2, root2, filename, &file2, &stx, CEPH_STATX_CTIME, 0, perms2), 0);
+
+ struct timespec old_ctime = stx.stx_ctime;
+
+ /*
+ * Now sleep, do a chmod on the first client and the see whether we get a
+ * different ctime with a statx that uses AT_STATX_DONT_SYNC
+ */
+ sleep(1);
+ stx.stx_mode = 0644;
+ ASSERT_EQ(ceph_ll_setattr(cmount1, file1, &stx, CEPH_SETATTR_MODE, perms1), 0);
+
+ ASSERT_EQ(ceph_ll_getattr(cmount2, file2, &stx, CEPH_STATX_CTIME, AT_STATX_DONT_SYNC, perms2), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_CTIME);
+ ASSERT_TRUE(stx.stx_ctime.tv_sec == old_ctime.tv_sec &&
+ stx.stx_ctime.tv_nsec == old_ctime.tv_nsec);
+
+ ceph_shutdown(cmount1);
+ ceph_shutdown(cmount2);
+}
+
+TEST(LibCephFS, ChangeAttr) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char filename[32];
+ sprintf(filename, "/changeattr%x", getpid());
+
+ ceph_unlink(cmount, filename);
+ int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
+ ASSERT_LT(0, fd);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+
+ uint64_t old_change_attr = stx.stx_version;
+
+ /* do chmod, and check whether change_attr changed */
+ ASSERT_EQ(ceph_chmod(cmount, filename, 0644), 0);
+ ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_NE(stx.stx_version, old_change_attr);
+ old_change_attr = stx.stx_version;
+
+ /* now do a write and see if it changed again */
+ ASSERT_EQ(3, ceph_write(cmount, fd, "foo", 3, 0));
+ ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_NE(stx.stx_version, old_change_attr);
+ old_change_attr = stx.stx_version;
+
+ /* Now truncate and check again */
+ ASSERT_EQ(0, ceph_ftruncate(cmount, fd, 0));
+ ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_NE(stx.stx_version, old_change_attr);
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DirChangeAttrCreateFile) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dirpath[32], filepath[56];
+ sprintf(dirpath, "/dirchange%x", getpid());
+ sprintf(filepath, "%s/foo", dirpath);
+
+ ASSERT_EQ(ceph_mkdir(cmount, dirpath, 0755), 0);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+
+ uint64_t old_change_attr = stx.stx_version;
+
+ /* Important: Follow an operation that changes the directory's ctime (setxattr)
+ * with one that changes the directory's mtime and ctime (create).
+ * Check that directory's change_attr is updated everytime ctime changes.
+ */
+
+ /* set xattr on dir, and check whether dir's change_attr is incremented */
+ ASSERT_EQ(ceph_setxattr(cmount, dirpath, "user.name", (void*)"bob", 3, XATTR_CREATE), 0);
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_GT(stx.stx_version, old_change_attr);
+ old_change_attr = stx.stx_version;
+
+ /* create a file within dir, and check whether dir's change_attr is incremented */
+ int fd = ceph_open(cmount, filepath, O_RDWR|O_CREAT|O_EXCL, 0666);
+ ASSERT_LT(0, fd);
+ ceph_close(cmount, fd);
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_GT(stx.stx_version, old_change_attr);
+
+ ASSERT_EQ(0, ceph_unlink(cmount, filepath));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dirpath));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DirChangeAttrRenameFile) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dirpath[32], filepath[56], newfilepath[56];
+ sprintf(dirpath, "/dirchange%x", getpid());
+ sprintf(filepath, "%s/foo", dirpath);
+
+ ASSERT_EQ(ceph_mkdir(cmount, dirpath, 0755), 0);
+
+ int fd = ceph_open(cmount, filepath, O_RDWR|O_CREAT|O_EXCL, 0666);
+ ASSERT_LT(0, fd);
+ ceph_close(cmount, fd);
+
+ /* Important: Follow an operation that changes the directory's ctime (setattr)
+ * with one that changes the directory's mtime and ctime (rename).
+ * Check that directory's change_attr is updated everytime ctime changes.
+ */
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+
+ uint64_t old_change_attr = stx.stx_version;
+
+ /* chmod dir, and check whether dir's change_attr is incremented */
+ ASSERT_EQ(ceph_chmod(cmount, dirpath, 0777), 0);
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_GT(stx.stx_version, old_change_attr);
+ old_change_attr = stx.stx_version;
+
+ /* rename a file within dir, and check whether dir's change_attr is incremented */
+ sprintf(newfilepath, "%s/bar", dirpath);
+ ASSERT_EQ(ceph_rename(cmount, filepath, newfilepath), 0);
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_GT(stx.stx_version, old_change_attr);
+
+ ASSERT_EQ(0, ceph_unlink(cmount, newfilepath));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dirpath));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, DirChangeAttrRemoveFile) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dirpath[32], filepath[56];
+ sprintf(dirpath, "/dirchange%x", getpid());
+ sprintf(filepath, "%s/foo", dirpath);
+
+ ASSERT_EQ(ceph_mkdir(cmount, dirpath, 0755), 0);
+
+ ASSERT_EQ(ceph_setxattr(cmount, dirpath, "user.name", (void*)"bob", 3, XATTR_CREATE), 0);
+
+ int fd = ceph_open(cmount, filepath, O_RDWR|O_CREAT|O_EXCL, 0666);
+ ASSERT_LT(0, fd);
+ ceph_close(cmount, fd);
+
+ /* Important: Follow an operation that changes the directory's ctime (removexattr)
+ * with one that changes the directory's mtime and ctime (remove a file).
+ * Check that directory's change_attr is updated everytime ctime changes.
+ */
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+
+ uint64_t old_change_attr = stx.stx_version;
+
+ /* remove xattr, and check whether dir's change_attr is incremented */
+ ASSERT_EQ(ceph_removexattr(cmount, dirpath, "user.name"), 0);
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_GT(stx.stx_version, old_change_attr);
+ old_change_attr = stx.stx_version;
+
+ /* remove a file within dir, and check whether dir's change_attr is incremented */
+ ASSERT_EQ(0, ceph_unlink(cmount, filepath));
+ ASSERT_EQ(ceph_statx(cmount, dirpath, &stx, CEPH_STATX_VERSION, 0), 0);
+ ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
+ ASSERT_GT(stx.stx_version, old_change_attr);
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, dirpath));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SetSize) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char filename[32];
+ sprintf(filename, "/setsize%x", getpid());
+
+ ceph_unlink(cmount, filename);
+ int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
+ ASSERT_LT(0, fd);
+
+ struct ceph_statx stx;
+ uint64_t size = 8388608;
+ stx.stx_size = size;
+ ASSERT_EQ(ceph_fsetattrx(cmount, fd, &stx, CEPH_SETATTR_SIZE), 0);
+ ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_SIZE, 0), 0);
+ ASSERT_EQ(stx.stx_size, size);
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, OperationsOnRoot)
+{
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dirname[32];
+ sprintf(dirname, "/somedir%x", getpid());
+
+ ASSERT_EQ(ceph_mkdir(cmount, dirname, 0755), 0);
+
+ ASSERT_EQ(ceph_rmdir(cmount, "/"), -CEPHFS_EBUSY);
+
+ ASSERT_EQ(ceph_link(cmount, "/", "/"), -CEPHFS_EEXIST);
+ ASSERT_EQ(ceph_link(cmount, dirname, "/"), -CEPHFS_EEXIST);
+ ASSERT_EQ(ceph_link(cmount, "nonExisitingDir", "/"), -CEPHFS_ENOENT);
+
+ ASSERT_EQ(ceph_unlink(cmount, "/"), -CEPHFS_EISDIR);
+
+ ASSERT_EQ(ceph_rename(cmount, "/", "/"), -CEPHFS_EBUSY);
+ ASSERT_EQ(ceph_rename(cmount, dirname, "/"), -CEPHFS_EBUSY);
+ ASSERT_EQ(ceph_rename(cmount, "nonExistingDir", "/"), -CEPHFS_EBUSY);
+ ASSERT_EQ(ceph_rename(cmount, "/", dirname), -CEPHFS_EBUSY);
+ ASSERT_EQ(ceph_rename(cmount, "/", "nonExistingDir"), -CEPHFS_EBUSY);
+
+ ASSERT_EQ(ceph_mkdir(cmount, "/", 0777), -CEPHFS_EEXIST);
+
+ ASSERT_EQ(ceph_mknod(cmount, "/", 0, 0), -CEPHFS_EEXIST);
+
+ ASSERT_EQ(ceph_symlink(cmount, "/", "/"), -CEPHFS_EEXIST);
+ ASSERT_EQ(ceph_symlink(cmount, dirname, "/"), -CEPHFS_EEXIST);
+ ASSERT_EQ(ceph_symlink(cmount, "nonExistingDir", "/"), -CEPHFS_EEXIST);
+
+ ceph_shutdown(cmount);
+}
+
+// no rlimits on Windows
+#ifndef _WIN32
+static void shutdown_racer_func()
+{
+ const int niter = 32;
+ struct ceph_mount_info *cmount;
+ int i;
+
+ for (i = 0; i < niter; ++i) {
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+ ceph_shutdown(cmount);
+ }
+}
+
+// See tracker #20988
+TEST(LibCephFS, ShutdownRace)
+{
+ const int nthreads = 32;
+ std::thread threads[nthreads];
+
+ // Need a bunch of fd's for this test
+ struct rlimit rold, rnew;
+ ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &rold), 0);
+ rnew = rold;
+ rnew.rlim_cur = rnew.rlim_max;
+
+ cout << "Setting RLIMIT_NOFILE from " << rold.rlim_cur <<
+ " to " << rnew.rlim_cur << std::endl;
+
+ ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &rnew), 0);
+
+ for (int i = 0; i < nthreads; ++i)
+ threads[i] = std::thread(shutdown_racer_func);
+
+ for (int i = 0; i < nthreads; ++i)
+ threads[i].join();
+ /*
+ * Let's just ignore restoring the open files limit,
+ * the kernel will defer releasing the file descriptors
+ * and then the process will be possibly reachthe open
+ * files limit. More detail, please see tracer#43039
+ */
+// ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &rold), 0);
+}
+#endif
+
+static void get_current_time_utimbuf(struct utimbuf *utb)
+{
+ utime_t t = ceph_clock_now();
+ utb->actime = t.sec();
+ utb->modtime = t.sec();
+}
+
+static void get_current_time_timeval(struct timeval tv[2])
+{
+ utime_t t = ceph_clock_now();
+ t.copy_to_timeval(&tv[0]);
+ t.copy_to_timeval(&tv[1]);
+}
+
+static void get_current_time_timespec(struct timespec ts[2])
+{
+ utime_t t = ceph_clock_now();
+ t.to_timespec(&ts[0]);
+ t.to_timespec(&ts[1]);
+}
+
+TEST(LibCephFS, TestUtime) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+ sprintf(test_file, "test_utime_file_%d", getpid());
+ int fd = ceph_open(cmount, test_file, O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+
+ struct utimbuf utb;
+ struct ceph_statx stx;
+
+ get_current_time_utimbuf(&utb);
+
+ // ceph_utime()
+ EXPECT_EQ(0, ceph_utime(cmount, test_file, &utb));
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, 0), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(utb.actime, 0));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(utb.modtime, 0));
+
+ get_current_time_utimbuf(&utb);
+
+ // ceph_futime()
+ EXPECT_EQ(0, ceph_futime(cmount, fd, &utb));
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, 0), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(utb.actime, 0));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(utb.modtime, 0));
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, TestUtimes) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+ char test_symlink[256];
+
+ sprintf(test_file, "test_utimes_file_%d", getpid());
+ sprintf(test_symlink, "test_utimes_symlink_%d", getpid());
+ int fd = ceph_open(cmount, test_file, O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+
+ ASSERT_EQ(ceph_symlink(cmount, test_file, test_symlink), 0);
+
+ struct timeval times[2];
+ struct ceph_statx stx;
+
+ get_current_time_timeval(times);
+
+ // ceph_utimes() on symlink, validate target file time
+ EXPECT_EQ(0, ceph_utimes(cmount, test_symlink, times));
+ ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, 0), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(times[0]));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(times[1]));
+
+ get_current_time_timeval(times);
+
+ // ceph_lutimes() on symlink, validate symlink time
+ EXPECT_EQ(0, ceph_lutimes(cmount, test_symlink, times));
+ ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, AT_SYMLINK_NOFOLLOW), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(times[0]));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(times[1]));
+
+ get_current_time_timeval(times);
+
+ // ceph_futimes()
+ EXPECT_EQ(0, ceph_futimes(cmount, fd, times));
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, 0), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(times[0]));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(times[1]));
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, TestFutimens) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_file[256];
+
+ sprintf(test_file, "test_futimens_file_%d", getpid());
+ int fd = ceph_open(cmount, test_file, O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+
+ struct timespec times[2];
+ struct ceph_statx stx;
+
+ get_current_time_timespec(times);
+
+ // ceph_futimens()
+ EXPECT_EQ(0, ceph_futimens(cmount, fd, times));
+ ASSERT_EQ(ceph_statx(cmount, test_file, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, 0), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(times[0]));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(times[1]));
+
+ ceph_close(cmount, fd);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, OperationsOnDotDot) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char c_dir[512], c_dir_dot[1024], c_dir_dotdot[1024];
+ char c_non_existent_dir[1024], c_non_existent_dirs[1024];
+ char c_temp[1024];
+
+ pid_t mypid = getpid();
+ sprintf(c_dir, "/oodd_dir_%d", mypid);
+ sprintf(c_dir_dot, "/%s/.", c_dir);
+ sprintf(c_dir_dotdot, "/%s/..", c_dir);
+ sprintf(c_non_existent_dir, "/%s/../oodd_nonexistent/..", c_dir);
+ sprintf(c_non_existent_dirs,
+ "/%s/../ood_nonexistent1_%d/oodd_nonexistent2_%d", c_dir, mypid, mypid);
+ sprintf(c_temp, "/oodd_temp_%d", mypid);
+
+ ASSERT_EQ(0, ceph_mkdir(cmount, c_dir, 0777));
+ ASSERT_EQ(-CEPHFS_EEXIST, ceph_mkdir(cmount, c_dir_dot, 0777));
+ ASSERT_EQ(-CEPHFS_EEXIST, ceph_mkdir(cmount, c_dir_dotdot, 0777));
+ ASSERT_EQ(0, ceph_mkdirs(cmount, c_non_existent_dirs, 0777));
+
+ ASSERT_EQ(-CEPHFS_ENOTEMPTY, ceph_rmdir(cmount, c_dir_dot));
+ ASSERT_EQ(-CEPHFS_ENOTEMPTY, ceph_rmdir(cmount, c_dir_dotdot));
+ // non existent directory should return -CEPHFS_ENOENT
+ ASSERT_EQ(-CEPHFS_ENOENT, ceph_rmdir(cmount, c_non_existent_dir));
+
+ ASSERT_EQ(-CEPHFS_EBUSY, ceph_rename(cmount, c_dir_dot, c_temp));
+ ASSERT_EQ(0, ceph_chdir(cmount, c_dir));
+ ASSERT_EQ(0, ceph_mkdir(cmount, c_temp, 0777));
+ ASSERT_EQ(-CEPHFS_EBUSY, ceph_rename(cmount, c_temp, ".."));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Caps_vxattr) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_caps_vxattr_file[256];
+ char gxattrv[128];
+ int xbuflen = sizeof(gxattrv);
+ pid_t mypid = getpid();
+
+ sprintf(test_caps_vxattr_file, "test_caps_vxattr_%d", mypid);
+ int fd = ceph_open(cmount, test_caps_vxattr_file, O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+ ceph_close(cmount, fd);
+
+ int alen = ceph_getxattr(cmount, test_caps_vxattr_file, "ceph.caps", (void *)gxattrv, xbuflen);
+ ASSERT_GT(alen, 0);
+ gxattrv[alen] = '\0';
+
+ char caps_regex[] = "pA[sx]*L[sx]*X[sx]*F[sxcrwbal]*/0x[0-9a-fA-f]+";
+ ASSERT_TRUE(regex_match(gxattrv, regex(caps_regex)) == 1);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SnapXattrs) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_snap_xattr_file[256];
+ char c_temp[PATH_MAX];
+ char gxattrv[128];
+ char gxattrv2[128];
+ int xbuflen = sizeof(gxattrv);
+ pid_t mypid = getpid();
+
+ sprintf(test_snap_xattr_file, "test_snap_xattr_%d", mypid);
+ int fd = ceph_open(cmount, test_snap_xattr_file, O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+ ceph_close(cmount, fd);
+
+ sprintf(c_temp, "/.snap/test_snap_xattr_snap_%d", mypid);
+ ASSERT_EQ(0, ceph_mkdir(cmount, c_temp, 0777));
+
+ int alen = ceph_getxattr(cmount, c_temp, "ceph.snap.btime", (void *)gxattrv, xbuflen);
+ // xattr value is secs.nsecs (don't assume zero-term)
+ ASSERT_LT(0, alen);
+ ASSERT_LT(alen, xbuflen);
+ gxattrv[alen] = '\0';
+ char *s = strchr(gxattrv, '.');
+ char *q = NULL;
+ ASSERT_NE(q, s);
+ ASSERT_LT(s, gxattrv + alen);
+ ASSERT_EQ('.', *s);
+ *s = '\0';
+ utime_t btime = utime_t(strtoull(gxattrv, NULL, 10), strtoull(s + 1, NULL, 10));
+ *s = '.'; // restore for later strcmp
+
+ // file within the snapshot should carry the same btime
+ sprintf(c_temp, "/.snap/test_snap_xattr_snap_%d/%s", mypid, test_snap_xattr_file);
+
+ int alen2 = ceph_getxattr(cmount, c_temp, "ceph.snap.btime", (void *)gxattrv2, xbuflen);
+ ASSERT_EQ(alen, alen2);
+ ASSERT_EQ(0, strncmp(gxattrv, gxattrv2, alen));
+
+ // non-snap file shouldn't carry the xattr
+ alen = ceph_getxattr(cmount, test_snap_xattr_file, "ceph.snap.btime", (void *)gxattrv2, xbuflen);
+ ASSERT_EQ(-CEPHFS_ENODATA, alen);
+
+ // create a second snapshot
+ sprintf(c_temp, "/.snap/test_snap_xattr_snap2_%d", mypid);
+ ASSERT_EQ(0, ceph_mkdir(cmount, c_temp, 0777));
+
+ // check that the btime for the newer snapshot is > older
+ alen = ceph_getxattr(cmount, c_temp, "ceph.snap.btime", (void *)gxattrv2, xbuflen);
+ ASSERT_LT(0, alen);
+ ASSERT_LT(alen, xbuflen);
+ gxattrv2[alen] = '\0';
+ s = strchr(gxattrv2, '.');
+ ASSERT_NE(q, s);
+ ASSERT_LT(s, gxattrv2 + alen);
+ ASSERT_EQ('.', *s);
+ *s = '\0';
+ utime_t new_btime = utime_t(strtoull(gxattrv2, NULL, 10), strtoull(s + 1, NULL, 10));
+ #ifndef _WIN32
+ // This assertion sometimes fails on Windows, possibly due to the clock precision.
+ ASSERT_LT(btime, new_btime);
+ #endif
+
+ // listxattr() shouldn't return snap.btime vxattr
+ char xattrlist[512];
+ int len = ceph_listxattr(cmount, test_snap_xattr_file, xattrlist, sizeof(xattrlist));
+ ASSERT_GE(sizeof(xattrlist), (size_t)len);
+ char *p = xattrlist;
+ int found = 0;
+ while (len > 0) {
+ if (strcmp(p, "ceph.snap.btime") == 0)
+ found++;
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ }
+ ASSERT_EQ(found, 0);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Lseek) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ char c_path[1024];
+ sprintf(c_path, "test_lseek_%d", getpid());
+ int fd = ceph_open(cmount, c_path, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ ASSERT_LT(0, fd);
+
+ const char *out_buf = "hello world";
+ size_t size = strlen(out_buf);
+ ASSERT_EQ(ceph_write(cmount, fd, out_buf, size, 0), (int)size);
+
+ /* basic SEEK_SET/END/CUR tests */
+ ASSERT_EQ(0, ceph_lseek(cmount, fd, 0, SEEK_SET));
+ ASSERT_EQ(size, ceph_lseek(cmount, fd, 0, SEEK_END));
+ ASSERT_EQ(0, ceph_lseek(cmount, fd, -size, SEEK_CUR));
+
+ /* Test basic functionality and out of bounds conditions for SEEK_HOLE/DATA */
+#ifdef SEEK_HOLE
+ ASSERT_EQ(size, ceph_lseek(cmount, fd, 0, SEEK_HOLE));
+ ASSERT_EQ(-CEPHFS_ENXIO, ceph_lseek(cmount, fd, -1, SEEK_HOLE));
+ ASSERT_EQ(-CEPHFS_ENXIO, ceph_lseek(cmount, fd, size + 1, SEEK_HOLE));
+#endif
+#ifdef SEEK_DATA
+ ASSERT_EQ(0, ceph_lseek(cmount, fd, 0, SEEK_DATA));
+ ASSERT_EQ(-CEPHFS_ENXIO, ceph_lseek(cmount, fd, -1, SEEK_DATA));
+ ASSERT_EQ(-CEPHFS_ENXIO, ceph_lseek(cmount, fd, size + 1, SEEK_DATA));
+#endif
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SnapInfoOnNonSnapshot) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ struct snap_info info;
+ ASSERT_EQ(-CEPHFS_EINVAL, ceph_get_snap_info(cmount, "/", &info));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, EmptySnapInfo) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_path[64];
+ char snap_path[PATH_MAX];
+ sprintf(dir_path, "/dir0_%d", getpid());
+ sprintf(snap_path, "%s/.snap/snap0_%d", dir_path, getpid());
+
+ ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755));
+ // snapshot without custom metadata
+ ASSERT_EQ(0, ceph_mkdir(cmount, snap_path, 0755));
+
+ struct snap_info info;
+ ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info));
+ ASSERT_GT(info.id, 0);
+ ASSERT_EQ(info.nr_snap_metadata, 0);
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, snap_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SnapInfo) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_path[64];
+ char snap_name[64];
+ char snap_path[PATH_MAX];
+ sprintf(dir_path, "/dir0_%d", getpid());
+ sprintf(snap_name, "snap0_%d", getpid());
+ sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name);
+
+ ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755));
+ // snapshot with custom metadata
+ struct snap_metadata snap_meta[] = {{"foo", "bar"},{"this", "that"},{"abcdefg", "12345"}};
+ ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, std::size(snap_meta)));
+
+ struct snap_info info;
+ ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info));
+ ASSERT_GT(info.id, 0);
+ ASSERT_EQ(info.nr_snap_metadata, std::size(snap_meta));
+ for (size_t i = 0; i < info.nr_snap_metadata; ++i) {
+ auto &k1 = info.snap_metadata[i].key;
+ auto &v1 = info.snap_metadata[i].value;
+ bool found = false;
+ for (size_t j = 0; j < info.nr_snap_metadata; ++j) {
+ auto &k2 = snap_meta[j].key;
+ auto &v2 = snap_meta[j].value;
+ if (strncmp(k1, k2, strlen(k1)) == 0 && strncmp(v1, v2, strlen(v1)) == 0) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ }
+ ceph_free_snap_info_buffer(&info);
+
+ ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LookupInoMDSDir) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ Inode *inode;
+ auto ino = inodeno_t(0x100); /* rank 0 ~mdsdir */
+ ASSERT_EQ(-CEPHFS_ESTALE, ceph_ll_lookup_inode(cmount, ino, &inode));
+ ino = inodeno_t(0x600); /* rank 0 first stray dir */
+ ASSERT_EQ(-CEPHFS_ESTALE, ceph_ll_lookup_inode(cmount, ino, &inode));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LookupVino) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_path[64];
+ char snap_name[64];
+ char snapdir_path[128];
+ char snap_path[256];
+ char file_path[PATH_MAX];
+ char snap_file[PATH_MAX];
+ sprintf(dir_path, "/dir0_%d", getpid());
+ sprintf(snap_name, "snap0_%d", getpid());
+ sprintf(file_path, "%s/file_%d", dir_path, getpid());
+ sprintf(snapdir_path, "%s/.snap", dir_path);
+ sprintf(snap_path, "%s/%s", snapdir_path, snap_name);
+ sprintf(snap_file, "%s/file_%d", snap_path, getpid());
+
+ ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755));
+ int fd = ceph_open(cmount, file_path, O_WRONLY|O_CREAT, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, nullptr, 0));
+
+ // record vinos for all of them
+ struct ceph_statx stx;
+ ASSERT_EQ(0, ceph_statx(cmount, dir_path, &stx, CEPH_STATX_INO, 0));
+ vinodeno_t dir_vino(stx.stx_ino, stx.stx_dev);
+
+ ASSERT_EQ(0, ceph_statx(cmount, file_path, &stx, CEPH_STATX_INO, 0));
+ vinodeno_t file_vino(stx.stx_ino, stx.stx_dev);
+
+ ASSERT_EQ(0, ceph_statx(cmount, snapdir_path, &stx, CEPH_STATX_INO, 0));
+ vinodeno_t snapdir_vino(stx.stx_ino, stx.stx_dev);
+
+ ASSERT_EQ(0, ceph_statx(cmount, snap_path, &stx, CEPH_STATX_INO, 0));
+ vinodeno_t snap_vino(stx.stx_ino, stx.stx_dev);
+
+ ASSERT_EQ(0, ceph_statx(cmount, snap_file, &stx, CEPH_STATX_INO, 0));
+ vinodeno_t snap_file_vino(stx.stx_ino, stx.stx_dev);
+
+ // Remount
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, NULL));
+
+ // Find them all
+ Inode *inode;
+ ASSERT_EQ(0, ceph_ll_lookup_vino(cmount, dir_vino, &inode));
+ ceph_ll_put(cmount, inode);
+ ASSERT_EQ(0, ceph_ll_lookup_vino(cmount, file_vino, &inode));
+ ceph_ll_put(cmount, inode);
+ ASSERT_EQ(0, ceph_ll_lookup_vino(cmount, snapdir_vino, &inode));
+ ceph_ll_put(cmount, inode);
+ ASSERT_EQ(0, ceph_ll_lookup_vino(cmount, snap_vino, &inode));
+ ceph_ll_put(cmount, inode);
+ ASSERT_EQ(0, ceph_ll_lookup_vino(cmount, snap_file_vino, &inode));
+ ceph_ll_put(cmount, inode);
+
+ // cleanup
+ ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name));
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Openat) {
+ pid_t mypid = getpid();
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ char c_rel_dir[64];
+ char c_dir[128];
+ sprintf(c_rel_dir, "open_test_%d", mypid);
+ sprintf(c_dir, "/%s", c_rel_dir);
+ ASSERT_EQ(0, ceph_mkdir(cmount, c_dir, 0777));
+
+ int root_fd = ceph_open(cmount, "/", O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, root_fd);
+
+ int dir_fd = ceph_openat(cmount, root_fd, c_rel_dir, O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, dir_fd);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statxat(cmount, root_fd, c_rel_dir, &stx, 0, 0), 0);
+ ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFDIR);
+
+ char c_rel_path[64];
+ char c_path[256];
+ sprintf(c_rel_path, "created_file_%d", mypid);
+ sprintf(c_path, "%s/created_file_%d", c_dir, mypid);
+ int file_fd = ceph_openat(cmount, dir_fd, c_rel_path, O_RDONLY | O_CREAT, 0666);
+ ASSERT_LE(0, file_fd);
+
+ ASSERT_EQ(ceph_statxat(cmount, dir_fd, c_rel_path, &stx, 0, 0), 0);
+ ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
+
+ ASSERT_EQ(0, ceph_close(cmount, file_fd));
+ ASSERT_EQ(0, ceph_close(cmount, dir_fd));
+ ASSERT_EQ(0, ceph_close(cmount, root_fd));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, c_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, c_dir));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Statxat) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[64];
+ char rel_file_name_1[128];
+ char rel_file_name_2[256];
+
+ char dir_path[512];
+ char file_path[1024];
+
+ // relative paths for *at() calls
+ sprintf(dir_name, "dir0_%d", getpid());
+ sprintf(rel_file_name_1, "file_%d", getpid());
+ sprintf(rel_file_name_2, "%s/%s", dir_name, rel_file_name_1);
+
+ sprintf(dir_path, "/%s", dir_name);
+ sprintf(file_path, "%s/%s", dir_path, rel_file_name_1);
+
+ ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755));
+ int fd = ceph_open(cmount, file_path, O_WRONLY|O_CREAT, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ struct ceph_statx stx;
+
+ // test relative to root
+ fd = ceph_open(cmount, "/", O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(ceph_statxat(cmount, fd, dir_name, &stx, 0, 0), 0);
+ ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFDIR);
+ ASSERT_EQ(ceph_statxat(cmount, fd, rel_file_name_2, &stx, 0, 0), 0);
+ ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ // test relative to dir
+ fd = ceph_open(cmount, dir_path, O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(ceph_statxat(cmount, fd, rel_file_name_1, &stx, 0, 0), 0);
+ ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
+
+ // delete the dirtree, recreate and verify
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755));
+ int fd1 = ceph_open(cmount, file_path, O_WRONLY|O_CREAT, 0666);
+ ASSERT_LE(0, fd1);
+ ASSERT_EQ(0, ceph_close(cmount, fd1));
+ ASSERT_EQ(ceph_statxat(cmount, fd, rel_file_name_1, &stx, 0, 0), -CEPHFS_ENOENT);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, StatxatATFDCWD) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[64];
+ char rel_file_name_1[128];
+
+ char dir_path[512];
+ char file_path[1024];
+
+ // relative paths for *at() calls
+ sprintf(dir_name, "dir0_%d", getpid());
+ sprintf(rel_file_name_1, "file_%d", getpid());
+
+ sprintf(dir_path, "/%s", dir_name);
+ sprintf(file_path, "%s/%s", dir_path, rel_file_name_1);
+
+ ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755));
+ int fd = ceph_open(cmount, file_path, O_WRONLY|O_CREAT, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ struct ceph_statx stx;
+ // chdir and test with CEPHFS_AT_FDCWD
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path));
+ ASSERT_EQ(ceph_statxat(cmount, CEPHFS_AT_FDCWD, rel_file_name_1, &stx, 0, 0), 0);
+ ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
+
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Fdopendir) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char foostr[256];
+ sprintf(foostr, "/dir_ls%d", mypid);
+ ASSERT_EQ(ceph_mkdir(cmount, foostr, 0777), 0);
+
+ char bazstr[512];
+ sprintf(bazstr, "%s/elif", foostr);
+ int fd = ceph_open(cmount, bazstr, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, foostr, O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ struct ceph_dir_result *ls_dir = NULL;
+ ASSERT_EQ(ceph_fdopendir(cmount, fd, &ls_dir), 0);
+
+ // not guaranteed to get . and .. first, but its a safe assumption in this case
+ struct dirent *result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "elif");
+
+ ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_closedir(cmount, ls_dir));
+ ASSERT_EQ(0, ceph_unlink(cmount, bazstr));
+ ASSERT_EQ(0, ceph_rmdir(cmount, foostr));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, FdopendirATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char foostr[256];
+ sprintf(foostr, "/dir_ls%d", mypid);
+ ASSERT_EQ(ceph_mkdir(cmount, foostr, 0777), 0);
+
+ char bazstr[512];
+ sprintf(bazstr, "%s/elif", foostr);
+ int fd = ceph_open(cmount, bazstr, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ASSERT_EQ(0, ceph_chdir(cmount, foostr));
+ struct ceph_dir_result *ls_dir = NULL;
+ ASSERT_EQ(ceph_fdopendir(cmount, CEPHFS_AT_FDCWD, &ls_dir), 0);
+
+ // not guaranteed to get . and .. first, but its a safe assumption in this case
+ struct dirent *result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "elif");
+
+ ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
+
+ ASSERT_EQ(0, ceph_closedir(cmount, ls_dir));
+ ASSERT_EQ(0, ceph_unlink(cmount, bazstr));
+ ASSERT_EQ(0, ceph_rmdir(cmount, foostr));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, FdopendirReaddirTestWithDelete) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char foostr[256];
+ sprintf(foostr, "/dir_ls%d", mypid);
+ ASSERT_EQ(ceph_mkdir(cmount, foostr, 0777), 0);
+
+ char bazstr[512];
+ sprintf(bazstr, "%s/elif", foostr);
+ int fd = ceph_open(cmount, bazstr, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, foostr, O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ struct ceph_dir_result *ls_dir = NULL;
+ ASSERT_EQ(ceph_fdopendir(cmount, fd, &ls_dir), 0);
+
+ ASSERT_EQ(0, ceph_unlink(cmount, bazstr));
+ ASSERT_EQ(0, ceph_rmdir(cmount, foostr));
+
+ // not guaranteed to get . and .. first, but its a safe assumption
+ // in this case. also, note that we may or may not get other
+ // entries.
+ struct dirent *result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, ".");
+ result = ceph_readdir(cmount, ls_dir);
+ ASSERT_TRUE(result != NULL);
+ ASSERT_STREQ(result->d_name, "..");
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_closedir(cmount, ls_dir));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, FdopendirOnNonDir) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char foostr[256];
+ sprintf(foostr, "/dir_ls%d", mypid);
+ ASSERT_EQ(ceph_mkdir(cmount, foostr, 0777), 0);
+
+ char bazstr[512];
+ sprintf(bazstr, "%s/file", foostr);
+ int fd = ceph_open(cmount, bazstr, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+
+ struct ceph_dir_result *ls_dir = NULL;
+ ASSERT_EQ(ceph_fdopendir(cmount, fd, &ls_dir), -CEPHFS_ENOTDIR);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, bazstr));
+ ASSERT_EQ(0, ceph_rmdir(cmount, foostr));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Mkdirat) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path1[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path1, "/%s", dir_name);
+
+ char dir_path2[512];
+ char rel_dir_path2[512];
+ sprintf(dir_path2, "%s/dir_%d", dir_path1, mypid);
+ sprintf(rel_dir_path2, "%s/dir_%d", dir_name, mypid);
+
+ int fd = ceph_open(cmount, "/", O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+
+ ASSERT_EQ(0, ceph_mkdirat(cmount, fd, dir_name, 0777));
+ ASSERT_EQ(0, ceph_mkdirat(cmount, fd, rel_dir_path2, 0666));
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path2));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path1));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, MkdiratATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path1[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path1, "/%s", dir_name);
+
+ char dir_path2[512];
+ sprintf(dir_path2, "%s/dir_%d", dir_path1, mypid);
+
+ ASSERT_EQ(0, ceph_mkdirat(cmount, CEPHFS_AT_FDCWD, dir_name, 0777));
+
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path1));
+ ASSERT_EQ(0, ceph_mkdirat(cmount, CEPHFS_AT_FDCWD, dir_name, 0666));
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path2));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path1));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Readlinkat) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512];
+ sprintf(rel_file_path, "%s/elif", dir_name);
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ char link_path[128];
+ char rel_link_path[64];
+ sprintf(rel_link_path, "linkfile_%d", mypid);
+ sprintf(link_path, "/%s", rel_link_path);
+ ASSERT_EQ(0, ceph_symlink(cmount, rel_file_path, link_path));
+
+ fd = ceph_open(cmount, "/", O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ size_t target_len = strlen(rel_file_path);
+ char target[target_len+1];
+ ASSERT_EQ(target_len, ceph_readlinkat(cmount, fd, rel_link_path, target, target_len));
+ target[target_len] = '\0';
+ ASSERT_EQ(0, memcmp(target, rel_file_path, target_len));
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unlink(cmount, link_path));
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, ReadlinkatATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "./elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ char link_path[PATH_MAX];
+ char rel_link_path[1024];
+ sprintf(rel_link_path, "./linkfile_%d", mypid);
+ sprintf(link_path, "%s/%s", dir_path, rel_link_path);
+ ASSERT_EQ(0, ceph_symlink(cmount, rel_file_path, link_path));
+
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path));
+ size_t target_len = strlen(rel_file_path);
+ char target[target_len+1];
+ ASSERT_EQ(target_len, ceph_readlinkat(cmount, CEPHFS_AT_FDCWD, rel_link_path, target, target_len));
+ target[target_len] = '\0';
+ ASSERT_EQ(0, memcmp(target, rel_file_path, target_len));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, link_path));
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Symlinkat) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512];
+ sprintf(rel_file_path, "%s/elif", dir_name);
+ sprintf(file_path, "%s/elif", dir_path);
+
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ char link_path[128];
+ char rel_link_path[64];
+ sprintf(rel_link_path, "linkfile_%d", mypid);
+ sprintf(link_path, "/%s", rel_link_path);
+
+ fd = ceph_open(cmount, "/", O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_symlinkat(cmount, rel_file_path, fd, rel_link_path));
+
+ size_t target_len = strlen(rel_file_path);
+ char target[target_len+1];
+ ASSERT_EQ(target_len, ceph_readlinkat(cmount, fd, rel_link_path, target, target_len));
+ target[target_len] = '\0';
+ ASSERT_EQ(0, memcmp(target, rel_file_path, target_len));
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unlink(cmount, link_path));
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SymlinkatATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "./elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ char link_path[PATH_MAX];
+ char rel_link_path[1024];
+ sprintf(rel_link_path, "./linkfile_%d", mypid);
+ sprintf(link_path, "%s/%s", dir_path, rel_link_path);
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path));
+ ASSERT_EQ(0, ceph_symlinkat(cmount, rel_file_path, CEPHFS_AT_FDCWD, rel_link_path));
+
+ size_t target_len = strlen(rel_file_path);
+ char target[target_len+1];
+ ASSERT_EQ(target_len, ceph_readlinkat(cmount, CEPHFS_AT_FDCWD, rel_link_path, target, target_len));
+ target[target_len] = '\0';
+ ASSERT_EQ(0, memcmp(target, rel_file_path, target_len));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, link_path));
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Unlinkat) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, dir_path, O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(-CEPHFS_ENOTDIR, ceph_unlinkat(cmount, fd, rel_file_path, AT_REMOVEDIR));
+ ASSERT_EQ(0, ceph_unlinkat(cmount, fd, rel_file_path, 0));
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, "/", O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_EQ(-CEPHFS_EISDIR, ceph_unlinkat(cmount, fd, dir_name, 0));
+ ASSERT_EQ(0, ceph_unlinkat(cmount, fd, dir_name, AT_REMOVEDIR));
+ ASSERT_LE(0, fd);
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, UnlinkatATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDONLY, 0666);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path));
+ ASSERT_EQ(-CEPHFS_ENOTDIR, ceph_unlinkat(cmount, CEPHFS_AT_FDCWD, rel_file_path, AT_REMOVEDIR));
+ ASSERT_EQ(0, ceph_unlinkat(cmount, CEPHFS_AT_FDCWD, rel_file_path, 0));
+
+ ASSERT_EQ(0, ceph_chdir(cmount, "/"));
+ ASSERT_EQ(-CEPHFS_EISDIR, ceph_unlinkat(cmount, CEPHFS_AT_FDCWD, dir_name, 0));
+ ASSERT_EQ(0, ceph_unlinkat(cmount, CEPHFS_AT_FDCWD, dir_name, AT_REMOVEDIR));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Chownat) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDWR, 0666);
+ ASSERT_LE(0, fd);
+
+ // set perms to readable and writeable only by owner
+ ASSERT_EQ(ceph_fchmod(cmount, fd, 0600), 0);
+ ceph_close(cmount, fd);
+
+ fd = ceph_open(cmount, dir_path, O_DIRECTORY | O_RDONLY, 0);
+ // change ownership to nobody -- we assume nobody exists and id is always 65534
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "0"), 0);
+ ASSERT_EQ(ceph_chownat(cmount, fd, rel_file_path, 65534, 65534, 0), 0);
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "1"), 0);
+ ceph_close(cmount, fd);
+
+ // "nobody" will be ignored on Windows
+ #ifndef _WIN32
+ fd = ceph_open(cmount, file_path, O_RDWR, 0);
+ ASSERT_EQ(fd, -CEPHFS_EACCES);
+ #endif
+
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "0"), 0);
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "1"), 0);
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, ChownatATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDWR, 0666);
+ ASSERT_LE(0, fd);
+
+ // set perms to readable and writeable only by owner
+ ASSERT_EQ(ceph_fchmod(cmount, fd, 0600), 0);
+ ceph_close(cmount, fd);
+
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path));
+ // change ownership to nobody -- we assume nobody exists and id is always 65534
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "0"), 0);
+ ASSERT_EQ(ceph_chownat(cmount, CEPHFS_AT_FDCWD, rel_file_path, 65534, 65534, 0), 0);
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "1"), 0);
+
+ // "nobody" will be ignored on Windows
+ #ifndef _WIN32
+ fd = ceph_open(cmount, file_path, O_RDWR, 0);
+ ASSERT_EQ(fd, -CEPHFS_EACCES);
+ #endif
+
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "0"), 0);
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "1"), 0);
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Chmodat) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDWR, 0666);
+ ASSERT_LE(0, fd);
+ const char *bytes = "foobarbaz";
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ fd = ceph_open(cmount, dir_path, O_DIRECTORY | O_RDONLY, 0);
+
+ // set perms to read but can't write
+ ASSERT_EQ(ceph_chmodat(cmount, fd, rel_file_path, 0400, 0), 0);
+ ASSERT_EQ(ceph_open(cmount, file_path, O_RDWR, 0), -CEPHFS_EACCES);
+
+ // reset back to writeable
+ ASSERT_EQ(ceph_chmodat(cmount, fd, rel_file_path, 0600, 0), 0);
+ int fd2 = ceph_open(cmount, file_path, O_RDWR, 0);
+ ASSERT_LE(0, fd2);
+
+ ASSERT_EQ(0, ceph_close(cmount, fd2));
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, ChmodatATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDWR, 0666);
+ ASSERT_LE(0, fd);
+ const char *bytes = "foobarbaz";
+ ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ // set perms to read but can't write
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path));
+ ASSERT_EQ(ceph_chmodat(cmount, CEPHFS_AT_FDCWD, rel_file_path, 0400, 0), 0);
+ ASSERT_EQ(ceph_open(cmount, file_path, O_RDWR, 0), -CEPHFS_EACCES);
+
+ // reset back to writeable
+ ASSERT_EQ(ceph_chmodat(cmount, CEPHFS_AT_FDCWD, rel_file_path, 0600, 0), 0);
+ int fd2 = ceph_open(cmount, file_path, O_RDWR, 0);
+ ASSERT_LE(0, fd2);
+ ASSERT_EQ(0, ceph_close(cmount, fd2));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Utimensat) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDWR, 0666);
+ ASSERT_LE(0, fd);
+
+ struct timespec times[2];
+ get_current_time_timespec(times);
+
+ fd = ceph_open(cmount, dir_path, O_DIRECTORY | O_RDONLY, 0);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(0, ceph_utimensat(cmount, fd, rel_file_path, times, 0));
+ ceph_close(cmount, fd);
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, file_path, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, 0), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(times[0]));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(times[1]));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, UtimensatATFDCWD) {
+ pid_t mypid = getpid();
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ char file_path[512];
+ char rel_file_path[512] = "elif";
+ sprintf(file_path, "%s/elif", dir_path);
+ int fd = ceph_open(cmount, file_path, O_CREAT|O_RDWR, 0666);
+ ASSERT_LE(0, fd);
+
+ struct timespec times[2];
+ get_current_time_timespec(times);
+
+ ASSERT_EQ(0, ceph_chdir(cmount, dir_path));
+ EXPECT_EQ(0, ceph_utimensat(cmount, CEPHFS_AT_FDCWD, rel_file_path, times, 0));
+
+ struct ceph_statx stx;
+ ASSERT_EQ(ceph_statx(cmount, file_path, &stx,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME, 0), 0);
+ ASSERT_EQ(utime_t(stx.stx_atime), utime_t(times[0]));
+ ASSERT_EQ(utime_t(stx.stx_mtime), utime_t(times[1]));
+
+ ASSERT_EQ(0, ceph_unlink(cmount, file_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LookupMdsPrivateInos) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ Inode *inode;
+ for (int ino = 0; ino < MDS_INO_SYSTEM_BASE; ino++) {
+ if (MDS_IS_PRIVATE_INO(ino)) {
+ ASSERT_EQ(-CEPHFS_ESTALE, ceph_ll_lookup_inode(cmount, ino, &inode));
+ } else if (ino == CEPH_INO_ROOT || ino == CEPH_INO_GLOBAL_SNAPREALM) {
+ ASSERT_EQ(0, ceph_ll_lookup_inode(cmount, ino, &inode));
+ ceph_ll_put(cmount, inode);
+ } else if (ino == CEPH_INO_LOST_AND_FOUND) {
+ // the ino 3 will only exists after the recovery tool ran, so
+ // it may return -CEPHFS_ESTALE with a fresh fs cluster
+ int r = ceph_ll_lookup_inode(cmount, ino, &inode);
+ if (r == 0) {
+ ceph_ll_put(cmount, inode);
+ } else {
+ ASSERT_TRUE(r == -CEPHFS_ESTALE);
+ }
+ } else {
+ // currently the ino 0 and 4~99 is not useded yet.
+ ASSERT_EQ(-CEPHFS_ESTALE, ceph_ll_lookup_inode(cmount, ino, &inode));
+ }
+ }
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SetMountTimeoutPostMount) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ ASSERT_EQ(-CEPHFS_EINVAL, ceph_set_mount_timeout(cmount, 5));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SetMountTimeout) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_set_mount_timeout(cmount, 5));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, FsCrypt) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char test_xattr_file[NAME_MAX];
+ sprintf(test_xattr_file, "test_fscrypt_%d", getpid());
+ int fd = ceph_open(cmount, test_xattr_file, O_RDWR|O_CREAT, 0666);
+ ASSERT_GT(fd, 0);
+
+ ASSERT_EQ(0, ceph_fsetxattr(cmount, fd, "ceph.fscrypt.auth", "foo", 3, CEPH_XATTR_CREATE));
+ ASSERT_EQ(0, ceph_fsetxattr(cmount, fd, "ceph.fscrypt.file", "foo", 3, CEPH_XATTR_CREATE));
+
+ char buf[64];
+ ASSERT_EQ(3, ceph_fgetxattr(cmount, fd, "ceph.fscrypt.auth", buf, sizeof(buf)));
+ ASSERT_EQ(3, ceph_fgetxattr(cmount, fd, "ceph.fscrypt.file", buf, sizeof(buf)));
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ASSERT_EQ(0, ceph_mount(cmount, NULL));
+
+ fd = ceph_open(cmount, test_xattr_file, O_RDWR, 0666);
+ ASSERT_GT(fd, 0);
+ ASSERT_EQ(3, ceph_fgetxattr(cmount, fd, "ceph.fscrypt.auth", buf, sizeof(buf)));
+ ASSERT_EQ(3, ceph_fgetxattr(cmount, fd, "ceph.fscrypt.file", buf, sizeof(buf)));
+
+ ASSERT_EQ(0, ceph_close(cmount, fd));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SnapdirAttrs) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ char snap_dir_path[512];
+
+ pid_t mypid = getpid();
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ sprintf(snap_dir_path, "%s/.snap", dir_path);
+
+ Inode *dir, *root;
+ struct ceph_statx stx_dir;
+ struct ceph_statx stx_snap_dir;
+ struct ceph_statx stx_root_snap_dir;
+ UserPerm *perms = ceph_mount_perms(cmount);
+
+ ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
+ ASSERT_EQ(ceph_ll_mkdir(cmount, root, dir_name, 0755, &dir, &stx_dir, 0, 0, perms), 0);
+
+ ASSERT_EQ(ceph_statx(cmount, dir_path, &stx_dir,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME|CEPH_STATX_MODE|CEPH_STATX_MODE|CEPH_STATX_GID|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME|CEPH_STATX_MODE|CEPH_STATX_MODE|CEPH_STATX_GID|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, "/.snap", &stx_root_snap_dir,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME|CEPH_STATX_MODE|CEPH_STATX_MODE|CEPH_STATX_GID|CEPH_STATX_VERSION, 0), 0);
+
+ // these should match the parent directories attrs
+ ASSERT_EQ(stx_dir.stx_mode, stx_snap_dir.stx_mode);
+ ASSERT_EQ(stx_dir.stx_uid, stx_snap_dir.stx_uid);
+ ASSERT_EQ(stx_dir.stx_gid, stx_snap_dir.stx_gid);
+ ASSERT_EQ(utime_t(stx_dir.stx_atime), utime_t(stx_snap_dir.stx_atime));
+ // these should match the closest snaprealm ancestor (root in this
+ // case) attrs
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir.stx_mtime));
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir.stx_ctime));
+ ASSERT_EQ(stx_root_snap_dir.stx_version, stx_snap_dir.stx_version);
+
+ // chown -- for this we need to be "root"
+ UserPerm *rootcred = ceph_userperm_new(0, 0, 0, NULL);
+ ASSERT_TRUE(rootcred);
+ stx_dir.stx_uid++;
+ stx_dir.stx_gid++;
+ ASSERT_EQ(ceph_ll_setattr(cmount, dir, &stx_dir, CEPH_SETATTR_UID|CEPH_SETATTR_GID, rootcred), 0);
+
+ memset(&stx_dir, 0, sizeof(stx_dir));
+ memset(&stx_snap_dir, 0, sizeof(stx_snap_dir));
+
+ ASSERT_EQ(ceph_statx(cmount, dir_path, &stx_dir,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME|CEPH_STATX_MODE|CEPH_STATX_MODE|CEPH_STATX_GID|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir,
+ CEPH_STATX_MTIME|CEPH_STATX_ATIME|CEPH_STATX_MODE|CEPH_STATX_MODE|CEPH_STATX_GID|CEPH_STATX_VERSION, 0), 0);
+
+ ASSERT_EQ(stx_dir.stx_mode, stx_snap_dir.stx_mode);
+ ASSERT_EQ(stx_dir.stx_uid, stx_snap_dir.stx_uid);
+ ASSERT_EQ(stx_dir.stx_gid, stx_snap_dir.stx_gid);
+ ASSERT_EQ(utime_t(stx_dir.stx_atime), utime_t(stx_snap_dir.stx_atime));
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir.stx_mtime));
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir.stx_ctime));
+ ASSERT_EQ(stx_root_snap_dir.stx_version, stx_snap_dir.stx_version);
+
+ ASSERT_EQ(ceph_ll_rmdir(cmount, root, dir_name, rootcred), 0);
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SnapdirAttrsOnSnapCreate) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ char snap_dir_path[512];
+
+ pid_t mypid = getpid();
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ sprintf(snap_dir_path, "%s/.snap", dir_path);
+
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ struct ceph_statx stx_dir;
+ struct ceph_statx stx_snap_dir;
+ struct ceph_statx stx_root_snap_dir;
+ ASSERT_EQ(ceph_statx(cmount, dir_path, &stx_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, "/.snap", &stx_root_snap_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir.stx_mtime));
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir.stx_ctime));
+ ASSERT_EQ(stx_root_snap_dir.stx_version, stx_snap_dir.stx_version);
+
+ char snap_path[1024];
+ sprintf(snap_path, "%s/snap_a", snap_dir_path);
+ ASSERT_EQ(ceph_mkdir(cmount, snap_path, 0777), 0);
+
+ struct ceph_statx stx_snap_dir_1;
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir_1, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_LT(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir_1.stx_mtime));
+ ASSERT_LT(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir_1.stx_ctime));
+ ASSERT_LT(stx_root_snap_dir.stx_version, stx_snap_dir_1.stx_version);
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, snap_path));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ceph_shutdown(cmount);
+}
+
+
+TEST(LibCephFS, SnapdirAttrsOnSnapDelete) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ char snap_dir_path[512];
+
+ pid_t mypid = getpid();
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ sprintf(snap_dir_path, "%s/.snap", dir_path);
+
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ struct ceph_statx stx_dir;
+ struct ceph_statx stx_snap_dir;
+ struct ceph_statx stx_root_snap_dir;
+ ASSERT_EQ(ceph_statx(cmount, dir_path, &stx_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, "/.snap", &stx_root_snap_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir.stx_mtime));
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir.stx_mtime));
+ ASSERT_EQ(stx_root_snap_dir.stx_version, stx_snap_dir.stx_version);
+
+ char snap_path[1024];
+ sprintf(snap_path, "%s/snap_a", snap_dir_path);
+ ASSERT_EQ(ceph_mkdir(cmount, snap_path, 0777), 0);
+
+ struct ceph_statx stx_snap_dir_1;
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir_1, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_LT(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir_1.stx_mtime));
+ ASSERT_LT(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir_1.stx_ctime));
+ ASSERT_LT(stx_root_snap_dir.stx_version, stx_snap_dir_1.stx_version);
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, snap_path));
+
+ struct ceph_statx stx_snap_dir_2;
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir_2, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_LT(utime_t(stx_snap_dir_1.stx_mtime), utime_t(stx_snap_dir_2.stx_mtime));
+ ASSERT_LT(utime_t(stx_snap_dir_1.stx_ctime), utime_t(stx_snap_dir_2.stx_ctime));
+ ASSERT_LT(stx_snap_dir_1.stx_version, stx_snap_dir_2.stx_version);
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, SnapdirAttrsOnSnapRename) {
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+ ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+ char dir_name[128];
+ char dir_path[256];
+ char snap_dir_path[512];
+
+ pid_t mypid = getpid();
+ sprintf(dir_name, "dir_%d", mypid);
+ sprintf(dir_path, "/%s", dir_name);
+ sprintf(snap_dir_path, "%s/.snap", dir_path);
+
+ ASSERT_EQ(ceph_mkdir(cmount, dir_path, 0777), 0);
+
+ struct ceph_statx stx_dir;
+ struct ceph_statx stx_snap_dir;
+ struct ceph_statx stx_root_snap_dir;
+ ASSERT_EQ(ceph_statx(cmount, dir_path, &stx_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(ceph_statx(cmount, "/.snap", &stx_root_snap_dir, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir.stx_mtime));
+ ASSERT_EQ(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir.stx_ctime));
+ ASSERT_EQ(stx_root_snap_dir.stx_version, stx_snap_dir.stx_version);
+
+ char snap_path[1024];
+ sprintf(snap_path, "%s/snap_a", snap_dir_path);
+ ASSERT_EQ(ceph_mkdir(cmount, snap_path, 0777), 0);
+
+ struct ceph_statx stx_snap_dir_1;
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir_1, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_LT(utime_t(stx_root_snap_dir.stx_mtime), utime_t(stx_snap_dir_1.stx_mtime));
+ ASSERT_LT(utime_t(stx_root_snap_dir.stx_ctime), utime_t(stx_snap_dir_1.stx_ctime));
+ ASSERT_LT(stx_root_snap_dir.stx_version, stx_snap_dir_1.stx_version);
+
+ char snap_path_r[1024];
+ sprintf(snap_path_r, "%s/snap_b", snap_dir_path);
+ ASSERT_EQ(ceph_rename(cmount, snap_path, snap_path_r), 0);
+
+ struct ceph_statx stx_snap_dir_2;
+ ASSERT_EQ(ceph_statx(cmount, snap_dir_path, &stx_snap_dir_2, CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_VERSION, 0), 0);
+ ASSERT_LT(utime_t(stx_snap_dir_1.stx_mtime), utime_t(stx_snap_dir_2.stx_mtime));
+ ASSERT_LT(utime_t(stx_snap_dir_1.stx_ctime), utime_t(stx_snap_dir_2.stx_ctime));
+ ASSERT_LT(stx_snap_dir_1.stx_version, stx_snap_dir_2.stx_version);
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, snap_path_r));
+ ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));
+ ASSERT_EQ(0, ceph_unmount(cmount));
+ ceph_shutdown(cmount);
+}
diff --git a/src/test/libcephfs/vxattr.cc b/src/test/libcephfs/vxattr.cc
new file mode 100644
index 000000000..4d9eaf5e4
--- /dev/null
+++ b/src/test/libcephfs/vxattr.cc
@@ -0,0 +1,385 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2021 Red Hat Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/compat.h"
+#include "gtest/gtest.h"
+#include "include/cephfs/libcephfs.h"
+#include "include/fs_types.h"
+#include "mds/mdstypes.h"
+#include "include/stat.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <string.h>
+
+#ifndef _WIN32
+#include <sys/resource.h>
+#endif
+
+#include "common/Clock.h"
+#include "common/ceph_json.h"
+
+#ifdef __linux__
+#include <limits.h>
+#include <sys/xattr.h>
+#endif
+
+#include <fmt/format.h>
+#include <map>
+#include <vector>
+#include <thread>
+#include <regex>
+#include <string>
+
+using namespace std;
+
+TEST(LibCephFS, LayoutVerifyDefaultLayout) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d0/subdir", 0777));
+
+ {
+ char value[1024] = "";
+ int r = 0;
+
+ // check for default layout
+ r = ceph_getxattr(cmount, "/", "ceph.dir.layout.json", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ std::clog << "layout:" << value << std::endl;
+ ASSERT_STRNE((char*)NULL, strstr(value, "\"inheritance\": \"@default\""));
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0/subdir"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LayoutSetAndVerifyNewAndInheritedLayout) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d0/subdir", 0777));
+
+ std::string pool_name_set;
+
+ {
+ char value[1024] = "";
+ int r = 0;
+
+ r = ceph_getxattr(cmount, "/", "ceph.dir.layout.json", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+
+ JSONParser json_parser;
+ ASSERT_EQ(json_parser.parse(value, r), 1);
+ ASSERT_EQ(json_parser.is_object(), 1);
+
+ std::string pool_name;
+
+ JSONDecoder::decode_json("pool_name", pool_name, &json_parser, true);
+
+ pool_name_set = pool_name;
+
+ // set a new layout
+ std::string new_layout;
+ new_layout += "{";
+ new_layout += "\"stripe_unit\": 65536, ";
+ new_layout += "\"stripe_count\": 1, ";
+ new_layout += "\"object_size\": 65536, ";
+ new_layout += "\"pool_name\": \"" + pool_name + "\"";
+ new_layout += "}";
+
+ ASSERT_EQ(0, ceph_setxattr(cmount, "test/d0", "ceph.dir.layout.json", (void*)new_layout.c_str(), new_layout.length(), XATTR_CREATE));
+ }
+
+ {
+ char value[1024] = "";
+ int r = 0;
+
+ r = ceph_getxattr(cmount, "test/d0", "ceph.dir.layout.json", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ std::clog << "layout:" << value << std::endl;
+
+ JSONParser json_parser;
+ ASSERT_EQ(json_parser.parse(value, r), 1);
+ ASSERT_EQ(json_parser.is_object(), 1);
+
+ int64_t object_size;
+ int64_t stripe_unit;
+ int64_t stripe_count;
+ std::string pool_name;
+ std::string inheritance;
+
+ JSONDecoder::decode_json("pool_name", pool_name, &json_parser, true);
+ JSONDecoder::decode_json("object_size", object_size, &json_parser, true);
+ JSONDecoder::decode_json("stripe_unit", stripe_unit, &json_parser, true);
+ JSONDecoder::decode_json("stripe_count", stripe_count, &json_parser, true);
+ JSONDecoder::decode_json("inheritance", inheritance, &json_parser, true);
+
+ // now verify the layout
+ ASSERT_EQ(pool_name.compare(pool_name_set), 0);
+ ASSERT_EQ(object_size, 65536);
+ ASSERT_EQ(stripe_unit, 65536);
+ ASSERT_EQ(stripe_count, 1);
+ ASSERT_EQ(inheritance.compare("@set"), 0);
+ }
+
+ {
+ char value[1024] = "";
+ int r = 0;
+
+ JSONParser json_parser;
+ std::string inheritance;
+
+ // now check that the subdir layout is inherited
+ r = ceph_getxattr(cmount, "test/d0/subdir", "ceph.dir.layout.json", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ std::clog << "layout:" << value << std::endl;
+ ASSERT_EQ(json_parser.parse(value, r), 1);
+ ASSERT_EQ(json_parser.is_object(), 1);
+ JSONDecoder::decode_json("inheritance", inheritance, &json_parser, true);
+ ASSERT_EQ(inheritance.compare("@inherited"), 0);
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0/subdir"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LayoutSetBadJSON) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d0/subdir", 0777));
+
+ {
+ // set a new layout and verify the same
+ const char *new_layout = "" // bad json without starting brace
+ "\"stripe_unit\": 65536, "
+ "\"stripe_count\": 1, "
+ "\"object_size\": 65536, "
+ "\"pool_name\": \"cephfs.a.data\", "
+ "}";
+ // try to set a malformed JSON, eg. without an open brace
+ ASSERT_EQ(-CEPHFS_EINVAL, ceph_setxattr(cmount, "test/d0", "ceph.dir.layout.json", (void*)new_layout, strlen(new_layout), XATTR_CREATE));
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0/subdir"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LayoutSetBadPoolName) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d0/subdir", 0777));
+
+ {
+ // try setting a bad pool name
+ ASSERT_EQ(-CEPHFS_EINVAL, ceph_setxattr(cmount, "test/d0", "ceph.dir.layout.pool_name", (void*)"UglyPoolName", 12, XATTR_CREATE));
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0/subdir"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LayoutSetBadPoolId) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d0/subdir", 0777));
+
+ {
+ // try setting a bad pool id
+ ASSERT_EQ(-CEPHFS_EINVAL, ceph_setxattr(cmount, "test/d0", "ceph.dir.layout.pool_id", (void*)"300", 3, XATTR_CREATE));
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0/subdir"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, LayoutSetInvalidFieldName) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d0/subdir", 0777));
+
+ {
+ // try to set in invalid field
+ ASSERT_EQ(-CEPHFS_ENODATA, ceph_setxattr(cmount, "test/d0", "ceph.dir.layout.bad_field", (void*)"300", 3, XATTR_CREATE));
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0/subdir"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d0"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, GetAndSetDirPin) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d1", 0777));
+
+ {
+ char value[1024] = "";
+ int r = ceph_getxattr(cmount, "test/d1", "ceph.dir.pin", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ ASSERT_STREQ("-1", value);
+ }
+
+ {
+ char value[1024] = "";
+ int r = -1;
+
+ ASSERT_EQ(0, ceph_setxattr(cmount, "test/d1", "ceph.dir.pin", (void*)"1", 1, XATTR_CREATE));
+
+ r = ceph_getxattr(cmount, "test/d1", "ceph.dir.pin", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ ASSERT_STREQ("1", value);
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d1"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, GetAndSetDirDistribution) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d2", 0777));
+
+ {
+ char value[1024] = "";
+ int r = ceph_getxattr(cmount, "test/d2", "ceph.dir.pin.distributed", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ ASSERT_STREQ("0", value);
+ }
+
+ {
+ char value[1024] = "";
+ int r = -1;
+
+ ASSERT_EQ(0, ceph_setxattr(cmount, "test/d2", "ceph.dir.pin.distributed", (void*)"1", 1, XATTR_CREATE));
+
+ r = ceph_getxattr(cmount, "test/d2", "ceph.dir.pin.distributed", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ ASSERT_STREQ("1", value);
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d2"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, GetAndSetDirRandom) {
+
+ struct ceph_mount_info *cmount;
+ ASSERT_EQ(0, ceph_create(&cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
+ ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+ ASSERT_EQ(0, ceph_mount(cmount, "/"));
+
+ ASSERT_EQ(0, ceph_mkdirs(cmount, "test/d3", 0777));
+
+ {
+ char value[1024] = "";
+ int r = ceph_getxattr(cmount, "test/d3", "ceph.dir.pin.random", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ ASSERT_STREQ("0", value);
+ }
+
+ {
+ double val = (double)1.0/(double)128.0;
+ std::stringstream ss;
+ ss << val;
+ ASSERT_EQ(0, ceph_setxattr(cmount, "test/d3", "ceph.dir.pin.random", (void*)ss.str().c_str(), strlen(ss.str().c_str()), XATTR_CREATE));
+
+ char value[1024] = "";
+ int r = -1;
+
+ r = ceph_getxattr(cmount, "test/d3", "ceph.dir.pin.random", (void*)value, sizeof(value));
+ ASSERT_GT(r, 0);
+ ASSERT_LT(r, sizeof value);
+ ASSERT_STREQ(ss.str().c_str(), value);
+ }
+
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test/d3"));
+ ASSERT_EQ(0, ceph_rmdir(cmount, "test"));
+
+ ceph_shutdown(cmount);
+}
diff --git a/src/test/libcephfs_config.cc b/src/test/libcephfs_config.cc
new file mode 100644
index 000000000..a54bb4ddf
--- /dev/null
+++ b/src/test/libcephfs_config.cc
@@ -0,0 +1,64 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/compat.h"
+#include "include/cephfs/libcephfs.h"
+
+#include <sstream>
+#include <string>
+#include <string.h>
+
+using std::string;
+
+TEST(LibCephConfig, SimpleSet) {
+ struct ceph_mount_info *cmount;
+ int ret = ceph_create(&cmount, NULL);
+ ASSERT_EQ(ret, 0);
+
+ ret = ceph_conf_set(cmount, "leveldb_max_open_files", "21");
+ ASSERT_EQ(ret, 0);
+
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ ret = ceph_conf_get(cmount, "leveldb_max_open_files", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(string("21"), string(buf));
+
+ ceph_shutdown(cmount);
+}
+
+TEST(LibCephConfig, ArgV) {
+ struct ceph_mount_info *cmount;
+ int ret = ceph_create(&cmount, NULL);
+ ASSERT_EQ(ret, 0);
+
+ const char *argv[] = { "foo", "--leveldb-max-open-files", "2",
+ "--key", "my-key", NULL };
+ size_t argc = (sizeof(argv) / sizeof(argv[0])) - 1;
+ ceph_conf_parse_argv(cmount, argc, argv);
+
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ ret = ceph_conf_get(cmount, "key", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(string("my-key"), string(buf));
+
+ memset(buf, 0, sizeof(buf));
+ ret = ceph_conf_get(cmount, "leveldb_max_open_files", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(string("2"), string(buf));
+
+ ceph_shutdown(cmount);
+}
diff --git a/src/test/libcephsqlite/CMakeLists.txt b/src/test/libcephsqlite/CMakeLists.txt
new file mode 100644
index 000000000..461c18476
--- /dev/null
+++ b/src/test/libcephsqlite/CMakeLists.txt
@@ -0,0 +1,15 @@
+if(WITH_LIBCEPHSQLITE)
+ add_executable(ceph_test_libcephsqlite
+ main.cc
+ )
+ target_link_libraries(ceph_test_libcephsqlite
+ cephsqlite
+ librados
+ ceph-common
+ SQLite3::SQLite3
+ ${UNITTEST_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+ install(TARGETS ceph_test_libcephsqlite DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif(WITH_LIBCEPHSQLITE)
diff --git a/src/test/libcephsqlite/main.cc b/src/test/libcephsqlite/main.cc
new file mode 100644
index 000000000..40bab659c
--- /dev/null
+++ b/src/test/libcephsqlite/main.cc
@@ -0,0 +1,1129 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2021 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License version 2.1, as published by
+ * the Free Software Foundation. See file COPYING.
+ *
+ */
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <string_view>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <sqlite3.h>
+#include <fmt/format.h>
+#include "gtest/gtest.h"
+
+#include "include/uuid.h"
+#include "include/rados/librados.hpp"
+#include "include/libcephsqlite.h"
+#include "SimpleRADOSStriper.h"
+
+#include "common/ceph_argparse.h"
+#include "common/ceph_crypto.h"
+#include "common/ceph_time.h"
+#include "common/common_init.h"
+#include "common/debug.h"
+
+#define dout_subsys ceph_subsys_client
+#undef dout_prefix
+#define dout_prefix *_dout << "unittest_libcephsqlite: "
+
+#define sqlcatchcode(S, code) \
+do {\
+ rc = S;\
+ if (rc != code) {\
+ std::cout << "[" << __FILE__ << ":" << __LINE__ << "]"\
+ << " sqlite3 error: " << rc << " `" << sqlite3_errstr(rc)\
+ << "': " << sqlite3_errmsg(db) << std::endl;\
+ sqlite3_finalize(stmt);\
+ stmt = NULL;\
+ goto out;\
+ }\
+} while (0)
+
+#define sqlcatch(S) sqlcatchcode(S, SQLITE_OK)
+
+static boost::intrusive_ptr<CephContext> cct;
+
+class CephSQLiteTest : public ::testing::Test {
+public:
+ inline static const std::string pool = "cephsqlite";
+
+ static void SetUpTestSuite() {
+ librados::Rados cluster;
+ ASSERT_LE(0, cluster.init_with_context(cct.get()));
+ ASSERT_LE(0, cluster.connect());
+ if (int rc = cluster.pool_create(pool.c_str()); rc < 0 && rc != -EEXIST) {
+ ASSERT_EQ(0, rc);
+ }
+ cluster.shutdown();
+ sleep(5);
+ }
+ void SetUp() override {
+ uuid.generate_random();
+ ASSERT_LE(0, cluster.init_with_context(cct.get()));
+ ASSERT_LE(0, cluster.connect());
+ ASSERT_LE(0, cluster.wait_for_latest_osdmap());
+ ASSERT_EQ(0, db_open());
+ }
+ void TearDown() override {
+ ASSERT_EQ(SQLITE_OK, sqlite3_close(db));
+ db = nullptr;
+ cluster.shutdown();
+ /* Leave database behind for inspection. */
+ }
+
+protected:
+ int db_open()
+ {
+ static const char SQL[] =
+ "PRAGMA journal_mode = PERSIST;"
+ "PRAGMA page_size = 65536;"
+ "PRAGMA cache_size = 32768;"
+ "PRAGMA temp_store = memory;"
+ "CREATE TEMPORARY TABLE perf (i INTEGER PRIMARY KEY, v TEXT);"
+ "CREATE TEMPORARY VIEW p AS"
+ " SELECT perf.i, J.*"
+ " FROM perf, json_tree(perf.v) AS J;"
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ ;
+
+ sqlite3_stmt *stmt = NULL;
+ const char *current = SQL;
+ int rc;
+
+ auto&& name = get_uri();
+ sqlcatch(sqlite3_open_v2(name.c_str(), &db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_URI, "ceph"));
+ std::cout << "using database: " << name << std::endl;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_exec(db, current, NULL, NULL, NULL));
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ return rc;
+ }
+
+ virtual std::string get_uri() const {
+ auto uri = fmt::format("file:{}:/{}?vfs=ceph", pool, get_name());
+ return uri;
+ }
+ virtual std::string get_name() const {
+ auto name = fmt::format("{}.db", uuid.to_string());
+ return name;
+ }
+
+ sqlite3* db = nullptr;
+ uuid_d uuid;
+ librados::Rados cluster;
+};
+
+TEST_F(CephSQLiteTest, Create) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a INT);"
+ ;
+
+ sqlite3_stmt *stmt = NULL;
+ const char *current = SQL;
+ int rc;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, InsertBulk4096) {
+ static const char SQL[] =
+ "PRAGMA page_size = 4096;"
+ "CREATE TABLE foo (a INT);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO foo (a)"
+ " SELECT RANDOM()"
+ " FROM c"
+ " LIMIT 1000000;"
+ "PRAGMA page_size;"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 0), 4096);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, InsertBulk) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a INT);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO foo (a)"
+ " SELECT RANDOM()"
+ " FROM c"
+ " LIMIT 1000000;"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_exec(db, current, NULL, NULL, NULL));
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, UpdateBulk) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a INT);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO foo (a)"
+ " SELECT x"
+ " FROM c"
+ " LIMIT 1000000;"
+ "SELECT SUM(a) FROM foo;"
+ "UPDATE foo"
+ " SET a = a+a;"
+ "SELECT SUM(a) FROM foo;"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ uint64_t sum, sum2;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sum = sqlite3_column_int64(stmt, 0);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sum2 = sqlite3_column_int64(stmt, 0);
+ ASSERT_EQ(sum*2, sum2);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, InsertRate) {
+ using clock = ceph::coarse_mono_clock;
+ using time = ceph::coarse_mono_time;
+
+ static const char SQL[] =
+ "CREATE TABLE foo (a INT);"
+ "INSERT INTO foo (a) VALUES (RANDOM());"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ time t1, t2;
+ int count = 100;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ t1 = clock::now();
+ for (int i = 0; i < count; ++i) {
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ }
+ t2 = clock::now();
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ {
+ auto diff = std::chrono::duration<double>(t2-t1);
+ std::cout << "transactions per second: " << count/diff.count() << std::endl;
+ }
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, DatabaseShrink) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a INT);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO foo (a)"
+ " SELECT x"
+ " FROM c"
+ " LIMIT 1000000;"
+ "DELETE FROM foo"
+ " WHERE RANDOM()%4 < 3;"
+ "VACUUM;"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ librados::IoCtx ioctx;
+ std::unique_ptr<SimpleRADOSStriper> rs;
+ uint64_t size1, size2;
+
+ std::cout << SQL << std::endl;
+
+ ASSERT_EQ(0, cluster.ioctx_create(pool.c_str(), ioctx));
+ rs = std::make_unique<SimpleRADOSStriper>(ioctx, get_name());
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ ASSERT_EQ(0, rs->lock(1000));
+ ASSERT_EQ(0, rs->stat(&size1));
+ ASSERT_EQ(0, rs->unlock());
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+ ASSERT_EQ(0, rs->lock(1000));
+ ASSERT_EQ(0, rs->stat(&size2));
+ ASSERT_EQ(0, rs->unlock());
+ ASSERT_LT(size2, size1/2);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, InsertExclusiveRate) {
+ using clock = ceph::coarse_mono_clock;
+ using time = ceph::coarse_mono_time;
+
+ static const char SQL[] =
+ "PRAGMA locking_mode=EXCLUSIVE;"
+ "CREATE TABLE foo (a INT);"
+ "INSERT INTO foo (a) VALUES (RANDOM());"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ time t1, t2;
+ int count = 100;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ t1 = clock::now();
+ for (int i = 0; i < count; ++i) {
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ }
+ t2 = clock::now();
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ {
+ auto diff = std::chrono::duration<double>(t2-t1);
+ std::cout << "transactions per second: " << count/diff.count() << std::endl;
+ }
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, InsertExclusiveWALRate) {
+ using clock = ceph::coarse_mono_clock;
+ using time = ceph::coarse_mono_time;
+
+ static const char SQL[] =
+ "PRAGMA locking_mode=EXCLUSIVE;"
+ "PRAGMA journal_mode=WAL;"
+ "CREATE TABLE foo (a INT);"
+ "INSERT INTO foo (a) VALUES (RANDOM());"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ time t1, t2;
+ int count = 100;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ t1 = clock::now();
+ for (int i = 0; i < count; ++i) {
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ }
+ t2 = clock::now();
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ {
+ auto diff = std::chrono::duration<double>(t2-t1);
+ std::cout << "transactions per second: " << count/diff.count() << std::endl;
+ }
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, WALTransactionSync) {
+ static const char SQL[] =
+ "PRAGMA locking_mode=EXCLUSIVE;"
+ "PRAGMA journal_mode=WAL;"
+ "CREATE TABLE foo (a INT);" /* sets up the -wal journal */
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ "BEGIN TRANSACTION;"
+ "INSERT INTO foo (a) VALUES (RANDOM());"
+ "END TRANSACTION;"
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ "SELECT a.atom-b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_vfs.opf_sync.avgcount' AND"
+ " b.fullkey = '$.libcephsqlite_vfs.opf_sync.avgcount';"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ uint64_t id;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ for (int i = 0; i < 10; i++) {
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ }
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ id = sqlite3_last_insert_rowid(db);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 0), 1);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, PersistTransactionSync) {
+ static const char SQL[] =
+ "BEGIN TRANSACTION;"
+ "CREATE TABLE foo (a INT);"
+ "INSERT INTO foo (a) VALUES (RANDOM());"
+ "END TRANSACTION;"
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ "SELECT a.atom-b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_vfs.opf_sync.avgcount' AND"
+ " b.fullkey = '$.libcephsqlite_vfs.opf_sync.avgcount';"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ uint64_t id;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ id = sqlite3_last_insert_rowid(db);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 0), 3); /* journal, db, journal header (PERIST) */
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, InsertExclusiveLock) {
+ static const char SQL[] =
+ "PRAGMA locking_mode=EXCLUSIVE;"
+ "CREATE TABLE foo (a INT);"
+ "INSERT INTO foo (a) VALUES (RANDOM());"
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ "SELECT a.atom, b.atom, a.atom-b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_vfs.opf_lock.avgcount' AND"
+ " b.fullkey = '$.libcephsqlite_vfs.opf_lock.avgcount';"
+ "SELECT a.atom, b.atom, a.atom-b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_striper.lock' AND"
+ " b.fullkey = '$.libcephsqlite_striper.lock';"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ uint64_t id;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ id = sqlite3_last_insert_rowid(db);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_GT(sqlite3_column_int64(stmt, 0), 0);
+ ASSERT_GT(sqlite3_column_int64(stmt, 1), 0);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 2), 3); /* NONE -> SHARED; SHARED -> RESERVED; RESERVED -> EXCLUSIVE */
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_GT(sqlite3_column_int64(stmt, 0), 0);
+ ASSERT_GT(sqlite3_column_int64(stmt, 1), 0);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 2), 1); /* one actual lock on the striper */
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, TransactionSizeUpdate) {
+ static const char SQL[] =
+ "BEGIN TRANSACTION;"
+ "CREATE TABLE foo (a INT);"
+ "INSERT INTO foo (a) VALUES (RANDOM());"
+ "END TRANSACTION;"
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ "SELECT a.atom, b.atom, a.atom-b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_striper.update_size' AND"
+ " b.fullkey = '$.libcephsqlite_striper.update_size';"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ uint64_t id;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ id = sqlite3_last_insert_rowid(db);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_GT(sqlite3_column_int64(stmt, 0), 0);
+ ASSERT_GT(sqlite3_column_int64(stmt, 1), 0);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 2), 2); /* once for journal write and db write (but not journal header clear!) */
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, AllocatedGrowth) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a BLOB);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO foo (a)"
+ " SELECT RANDOMBLOB(1<<20)"
+ " FROM c"
+ " LIMIT 1024;"
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ "SELECT a.atom, b.atom, a.atom-b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_striper.update_allocated' AND"
+ " b.fullkey = '$.libcephsqlite_striper.update_allocated';"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ uint64_t id;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ id = sqlite3_last_insert_rowid(db);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_GT(sqlite3_column_int64(stmt, 2), 8); /* max_growth = 128MB, 1024MB of data */
+ ASSERT_LT(sqlite3_column_int64(stmt, 2), 12);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+
+TEST_F(CephSQLiteTest, DeleteBulk) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a INT);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO foo (a)"
+ " SELECT x"
+ " FROM c"
+ " LIMIT 1000000;"
+ "DELETE FROM foo"
+ " WHERE RANDOM()%2 == 0;"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, DropMassive) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a BLOB);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO foo (a)"
+ " SELECT RANDOMBLOB(1<<20)"
+ " FROM c"
+ " LIMIT 1024;"
+ "DROP TABLE foo;"
+ "VACUUM;"
+ "INSERT INTO perf (v)"
+ " VALUES (ceph_perf());"
+ "SELECT a.atom, b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_striper.shrink' AND"
+ " b.fullkey = '$.libcephsqlite_striper.shrink';"
+ "SELECT a.atom-b.atom"
+ " FROM p AS a, p AS b"
+ " WHERE a.i = ? AND"
+ " b.i = ? AND"
+ " a.fullkey = '$.libcephsqlite_striper.shrink_bytes' AND"
+ " b.fullkey = '$.libcephsqlite_striper.shrink_bytes';"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ uint64_t id;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ id = sqlite3_last_insert_rowid(db);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_GT(sqlite3_column_int64(stmt, 0), sqlite3_column_int64(stmt, 1));
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatch(sqlite3_bind_int64(stmt, 1, id));
+ sqlcatch(sqlite3_bind_int64(stmt, 2, id-1));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_LT(512*(1<<20), sqlite3_column_int64(stmt, 0));
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, InsertMassiveVerify) {
+ static const char SQL[] =
+ "CREATE TABLE foo (a BLOB);"
+ "CREATE TEMPORARY TABLE bar (a BLOB);"
+ "WITH RECURSIVE c(x) AS"
+ " ("
+ " VALUES(1)"
+ " UNION ALL"
+ " SELECT x+1"
+ " FROM c"
+ " )"
+ "INSERT INTO bar (a)"
+ " SELECT RANDOMBLOB(1<<20)"
+ " FROM c"
+ " LIMIT 1024;"
+ "SELECT a FROM bar;"
+ "INSERT INTO foo (a)"
+ " SELECT a FROM bar;"
+ "SELECT a FROM foo;"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+ std::vector<std::string> hashes1, hashes2;
+
+ std::cout << SQL << std::endl;
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ const void* blob = sqlite3_column_blob(stmt, 0);
+ ceph::bufferlist bl;
+ bl.append(std::string_view((const char*)blob, (size_t)sqlite3_column_bytes(stmt, 0)));
+ auto digest = ceph::crypto::digest<ceph::crypto::SHA1>(bl);
+ hashes1.emplace_back(digest.to_str());
+ }
+ sqlcatchcode(rc, SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ const void* blob = sqlite3_column_blob(stmt, 0);
+ ceph::bufferlist bl;
+ bl.append(std::string_view((const char*)blob, (size_t)sqlite3_column_bytes(stmt, 0)));
+ auto digest = ceph::crypto::digest<ceph::crypto::SHA1>(bl);
+ hashes2.emplace_back(digest.to_str());
+ }
+ sqlcatchcode(rc, SQLITE_DONE);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ ASSERT_EQ(hashes1, hashes2);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, PerfValid) {
+ static const char SQL[] =
+ "SELECT json_valid(ceph_perf());"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 0), 1);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, StatusValid) {
+ static const char SQL[] =
+ "SELECT json_valid(ceph_status());"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(stmt, 0), 1);
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CephSQLiteTest, CurrentTime) {
+ static const char SQL[] =
+ "SELECT strftime('%s', 'now');"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ {
+ time_t now = time(0);
+ auto t = sqlite3_column_int64(stmt, 0);
+ ASSERT_LT(abs(now-t), 5);
+ }
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+
+TEST_F(CephSQLiteTest, StatusFields) {
+ static const char SQL[] =
+ "SELECT json_extract(ceph_status(), '$.addr');"
+ "SELECT json_extract(ceph_status(), '$.id');"
+ ;
+
+ int rc;
+ const char *current = SQL;
+ sqlite3_stmt *stmt = NULL;
+
+ std::cout << SQL << std::endl;
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ {
+ auto addr = sqlite3_column_text(stmt, 0);
+ std::cout << addr << std::endl;
+ }
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ sqlcatch(sqlite3_prepare_v2(db, current, -1, &stmt, &current));
+ sqlcatchcode(sqlite3_step(stmt), SQLITE_ROW);
+ {
+ auto id = sqlite3_column_int64(stmt, 0);
+ std::cout << id << std::endl;
+ ASSERT_GT(id, 0);
+ }
+ sqlcatch(sqlite3_finalize(stmt); stmt = NULL);
+
+ rc = 0;
+out:
+ sqlite3_finalize(stmt);
+ ASSERT_EQ(0, rc);
+}
+
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+
+ std::string conf_file_list;
+ std::string cluster;
+ CephInitParameters iparams = ceph_argparse_early_args(args, CEPH_ENTITY_TYPE_CLIENT, &cluster, &conf_file_list);
+ cct = boost::intrusive_ptr<CephContext>(common_preinit(iparams, CODE_ENVIRONMENT_UTILITY, 0), false);
+ cct->_conf.parse_config_files(conf_file_list.empty() ? nullptr : conf_file_list.c_str(), &std::cerr, 0);
+ cct->_conf.parse_env(cct->get_module_type()); // environment variables override
+ cct->_conf.parse_argv(args);
+ cct->_conf.apply_changes(nullptr);
+ common_init_finish(cct.get());
+
+ ldout(cct, 1) << "sqlite3 version: " << sqlite3_libversion() << dendl;
+ if (int rc = sqlite3_config(SQLITE_CONFIG_URI, 1); rc) {
+ lderr(cct) << "sqlite3 config failed: " << rc << dendl;
+ exit(EXIT_FAILURE);
+ }
+
+ sqlite3_auto_extension((void (*)())sqlite3_cephsqlite_init);
+ sqlite3* db = nullptr;
+ if (int rc = sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_READWRITE, nullptr); rc == SQLITE_OK) {
+ sqlite3_close(db);
+ } else {
+ lderr(cct) << "could not open sqlite3: " << rc << dendl;
+ exit(EXIT_FAILURE);
+ }
+ if (int rc = cephsqlite_setcct(cct.get(), nullptr); rc < 0) {
+ lderr(cct) << "could not set cct: " << rc << dendl;
+ exit(EXIT_FAILURE);
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librados/CMakeLists.txt b/src/test/librados/CMakeLists.txt
new file mode 100644
index 000000000..5d5623f06
--- /dev/null
+++ b/src/test/librados/CMakeLists.txt
@@ -0,0 +1,217 @@
+# radostest
+add_library(radostest_shared OBJECT test_shared.cc)
+target_include_directories(radostest_shared PRIVATE
+ $<TARGET_PROPERTY:GTest::GTest,INTERFACE_INCLUDE_DIRECTORIES>)
+
+add_library(radostest STATIC
+ test_common.cc
+ TestCase.cc
+ test.cc
+ $<TARGET_OBJECTS:radostest_shared>)
+target_link_libraries(radostest PUBLIC
+ GTest::GTest
+ ceph-common
+ json_spirit
+ ${GSSAPI_LIBRARIES} ${EXTRALIBS})
+add_library(radostest-cxx STATIC
+ testcase_cxx.cc
+ test_cxx.cc
+ $<TARGET_OBJECTS:radostest_shared>)
+target_link_libraries(radostest-cxx PUBLIC
+ GTest::GTest
+ ceph-common)
+
+add_executable(ceph_test_rados_api_cmd
+ cmd.cc)
+target_link_libraries(ceph_test_rados_api_cmd
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_cmd_pp
+ cmd_cxx.cc)
+target_link_libraries(ceph_test_rados_api_cmd_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_io
+ io.cc)
+target_link_libraries(ceph_test_rados_api_io
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_io_pp
+ io_cxx.cc)
+target_link_libraries(ceph_test_rados_api_io_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_c_write_operations
+ c_write_operations.cc)
+target_link_libraries(ceph_test_rados_api_c_write_operations
+ librados ${UNITTEST_LIBS} radostest)
+
+add_executable(ceph_test_rados_api_c_read_operations
+ c_read_operations.cc)
+target_link_libraries(ceph_test_rados_api_c_read_operations
+ librados ${UNITTEST_LIBS} radostest)
+
+add_executable(ceph_test_rados_api_aio
+ aio.cc)
+target_link_libraries(ceph_test_rados_api_aio
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_aio_pp
+ aio_cxx.cc)
+target_link_libraries(ceph_test_rados_api_aio_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_asio asio.cc)
+target_link_libraries(ceph_test_rados_api_asio global
+ librados ${UNITTEST_LIBS} spawn)
+
+add_executable(ceph_test_rados_api_list
+ list.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(ceph_test_rados_api_list
+ librados global ${UNITTEST_LIBS} radostest)
+
+add_executable(ceph_test_rados_api_pool
+ pool.cc)
+target_link_libraries(ceph_test_rados_api_pool
+ librados ${UNITTEST_LIBS} radostest)
+
+add_executable(ceph_test_rados_api_stat
+ stat.cc)
+target_link_libraries(ceph_test_rados_api_stat
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_stat_pp
+ stat_cxx.cc)
+target_link_libraries(ceph_test_rados_api_stat_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_watch_notify
+ watch_notify.cc)
+target_link_libraries(ceph_test_rados_api_watch_notify
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_watch_notify_pp
+ watch_notify_cxx.cc)
+target_link_libraries(ceph_test_rados_api_watch_notify_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_cls
+ cls.cc)
+target_link_libraries(ceph_test_rados_api_cls
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_misc
+ misc.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(ceph_test_rados_api_misc
+ librados global ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_misc_pp
+ misc_cxx.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(ceph_test_rados_api_misc_pp
+ librados global ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_lock
+ lock.cc)
+target_link_libraries(ceph_test_rados_api_lock
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_lock_pp
+ lock_cxx.cc)
+target_link_libraries(ceph_test_rados_api_lock_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_service
+ service.cc)
+target_link_libraries(ceph_test_rados_api_service
+ librados global ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_service_pp
+ service_cxx.cc)
+target_link_libraries(ceph_test_rados_api_service_pp
+ librados global ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_tier_pp
+ tier_cxx.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_include_directories(ceph_test_rados_api_tier_pp
+ PUBLIC "${CMAKE_SOURCE_DIR}/src/rgw/driver/rados"
+ PUBLIC "${CMAKE_SOURCE_DIR}/src/rgw")
+target_link_libraries(ceph_test_rados_api_tier_pp
+ librados global ${UNITTEST_LIBS} Boost::system radostest-cxx cls_cas_internal
+ cls_cas_client spawn)
+
+add_executable(ceph_test_rados_api_snapshots
+ snapshots.cc)
+target_link_libraries(ceph_test_rados_api_snapshots
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_snapshots_pp
+ snapshots_cxx.cc)
+target_link_libraries(ceph_test_rados_api_snapshots_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+add_executable(ceph_test_rados_api_snapshots_stats
+ snapshots_stats.cc)
+target_link_libraries(ceph_test_rados_api_snapshots_stats
+ librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_snapshots_stats_pp
+ snapshots_stats_cxx.cc)
+target_link_libraries(ceph_test_rados_api_snapshots_stats_pp
+ librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_api_cls_remote_reads
+ cls_remote_reads.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(ceph_test_rados_api_cls_remote_reads
+ librados global ${UNITTEST_LIBS} radostest-cxx)
+
+install(TARGETS
+ ceph_test_rados_api_aio
+ ceph_test_rados_api_aio_pp
+ ceph_test_rados_api_asio
+ ceph_test_rados_api_c_read_operations
+ ceph_test_rados_api_c_write_operations
+ ceph_test_rados_api_cmd
+ ceph_test_rados_api_cmd_pp
+ ceph_test_rados_api_io
+ ceph_test_rados_api_io_pp
+ ceph_test_rados_api_list
+ ceph_test_rados_api_lock
+ ceph_test_rados_api_lock_pp
+ ceph_test_rados_api_misc
+ ceph_test_rados_api_misc_pp
+ ceph_test_rados_api_pool
+ ceph_test_rados_api_service
+ ceph_test_rados_api_service_pp
+ ceph_test_rados_api_snapshots
+ ceph_test_rados_api_snapshots_pp
+ ceph_test_rados_api_stat
+ ceph_test_rados_api_stat_pp
+ ceph_test_rados_api_tier_pp
+ ceph_test_rados_api_watch_notify
+ ceph_test_rados_api_watch_notify_pp
+ ceph_test_rados_api_cls_remote_reads
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# unittest_librados
+add_executable(unittest_librados
+ librados.cc
+ )
+add_ceph_unittest(unittest_librados)
+target_link_libraries(unittest_librados librados ${BLKID_LIBRARIES}
+ ${GSSAPI_LIBRARIES})
+
+# unittest_librados_config
+add_executable(unittest_librados_config
+ librados_config.cc
+ )
+add_ceph_unittest(unittest_librados_config)
+target_link_libraries(unittest_librados_config
+ librados
+ ${BLKID_LIBRARIES} ${GSSAPI_LIBRARIES})
+
+# Removing this test. We can't shove it into Finisher as it's not a
+# Context any more, and wrapping it to adapt it would be less fair.
+
+#add_executable(ceph_test_rados_completion_speed
+# completion_speed.cc)
+#target_link_libraries(ceph_test_rados_completion_speed
+# librados ${UNITTEST_LIBS} radostest-cxx)
+
+add_executable(ceph_test_rados_op_speed
+ op_speed.cc)
+target_link_libraries(ceph_test_rados_op_speed
+ librados ${UNITTEST_LIBS} radostest-cxx)
diff --git a/src/test/librados/TestCase.cc b/src/test/librados/TestCase.cc
new file mode 100644
index 000000000..e99c19273
--- /dev/null
+++ b/src/test/librados/TestCase.cc
@@ -0,0 +1,203 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+#include <fmt/format.h>
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+#include "include/scope_guard.h"
+#include "crimson_utils.h"
+
+std::string RadosTestNS::pool_name;
+rados_t RadosTestNS::s_cluster = NULL;
+
+
+void RadosTestNS::SetUpTestCase()
+{
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool(pool_name, &s_cluster));
+}
+
+void RadosTestNS::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &s_cluster));
+}
+
+void RadosTestNS::SetUp()
+{
+ cluster = RadosTestNS::s_cluster;
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ int req;
+ ASSERT_EQ(0, rados_ioctx_pool_requires_alignment2(ioctx, &req));
+ ASSERT_FALSE(req);
+}
+
+void RadosTestNS::TearDown()
+{
+ if (cleanup)
+ cleanup_all_objects(ioctx);
+ rados_ioctx_destroy(ioctx);
+}
+
+void RadosTestNS::cleanup_all_objects(rados_ioctx_t ioctx)
+{
+ // remove all objects to avoid polluting other tests
+ rados_ioctx_snap_set_read(ioctx, LIBRADOS_SNAP_HEAD);
+ rados_ioctx_set_namespace(ioctx, LIBRADOS_ALL_NSPACES);
+ rados_list_ctx_t list_ctx;
+
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &list_ctx));
+ auto sg = make_scope_guard([&] { rados_nobjects_list_close(list_ctx); });
+
+ int r;
+ const char *entry = NULL;
+ const char *key = NULL;
+ const char *nspace = NULL;
+ while ((r = rados_nobjects_list_next(list_ctx, &entry, &key, &nspace)) != -ENOENT) {
+ ASSERT_EQ(0, r);
+ rados_ioctx_locator_set_key(ioctx, key);
+ rados_ioctx_set_namespace(ioctx, nspace);
+ ASSERT_EQ(0, rados_remove(ioctx, entry));
+ }
+}
+
+std::string RadosTestECNS::pool_name;
+rados_t RadosTestECNS::s_cluster = NULL;
+
+void RadosTestECNS::SetUpTestCase()
+{
+ SKIP_IF_CRIMSON();
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_ec_pool(pool_name, &s_cluster));
+}
+
+void RadosTestECNS::TearDownTestCase()
+{
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, destroy_one_ec_pool(pool_name, &s_cluster));
+}
+
+void RadosTestECNS::SetUp()
+{
+ SKIP_IF_CRIMSON();
+ cluster = RadosTestECNS::s_cluster;
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ int req;
+ ASSERT_EQ(0, rados_ioctx_pool_requires_alignment2(ioctx, &req));
+ ASSERT_TRUE(req);
+ ASSERT_EQ(0, rados_ioctx_pool_required_alignment2(ioctx, &alignment));
+ ASSERT_NE(0U, alignment);
+}
+
+void RadosTestECNS::TearDown()
+{
+ SKIP_IF_CRIMSON();
+ if (cleanup)
+ cleanup_all_objects(ioctx);
+ rados_ioctx_destroy(ioctx);
+}
+
+std::string RadosTest::pool_name;
+rados_t RadosTest::s_cluster = NULL;
+
+void RadosTest::SetUpTestCase()
+{
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool(pool_name, &s_cluster));
+}
+
+void RadosTest::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &s_cluster));
+}
+
+void RadosTest::SetUp()
+{
+ cluster = RadosTest::s_cluster;
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ nspace = get_temp_pool_name();
+ rados_ioctx_set_namespace(ioctx, nspace.c_str());
+ int req;
+ ASSERT_EQ(0, rados_ioctx_pool_requires_alignment2(ioctx, &req));
+ ASSERT_FALSE(req);
+}
+
+void RadosTest::TearDown()
+{
+ if (cleanup) {
+ cleanup_default_namespace(ioctx);
+ cleanup_namespace(ioctx, nspace);
+ }
+ rados_ioctx_destroy(ioctx);
+}
+
+void RadosTest::cleanup_default_namespace(rados_ioctx_t ioctx)
+{
+ // remove all objects from the default namespace to avoid polluting
+ // other tests
+ cleanup_namespace(ioctx, "");
+}
+
+void RadosTest::cleanup_namespace(rados_ioctx_t ioctx, std::string ns)
+{
+ rados_ioctx_snap_set_read(ioctx, LIBRADOS_SNAP_HEAD);
+ rados_ioctx_set_namespace(ioctx, ns.c_str());
+ rados_list_ctx_t list_ctx;
+
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &list_ctx));
+ auto sg = make_scope_guard([&] { rados_nobjects_list_close(list_ctx); });
+
+ int r;
+ const char *entry = NULL;
+ const char *key = NULL;
+ while ((r = rados_nobjects_list_next(list_ctx, &entry, &key, NULL)) != -ENOENT) {
+ ASSERT_EQ(0, r);
+ rados_ioctx_locator_set_key(ioctx, key);
+ ASSERT_EQ(0, rados_remove(ioctx, entry));
+ }
+}
+
+std::string RadosTestEC::pool_name;
+rados_t RadosTestEC::s_cluster = NULL;
+
+void RadosTestEC::SetUpTestCase()
+{
+ SKIP_IF_CRIMSON();
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_ec_pool(pool_name, &s_cluster));
+}
+
+void RadosTestEC::TearDownTestCase()
+{
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, destroy_one_ec_pool(pool_name, &s_cluster));
+}
+
+void RadosTestEC::SetUp()
+{
+ SKIP_IF_CRIMSON();
+ cluster = RadosTestEC::s_cluster;
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ nspace = get_temp_pool_name();
+ rados_ioctx_set_namespace(ioctx, nspace.c_str());
+ int req;
+ ASSERT_EQ(0, rados_ioctx_pool_requires_alignment2(ioctx, &req));
+ ASSERT_TRUE(req);
+ ASSERT_EQ(0, rados_ioctx_pool_required_alignment2(ioctx, &alignment));
+ ASSERT_NE(0U, alignment);
+}
+
+void RadosTestEC::TearDown()
+{
+ SKIP_IF_CRIMSON();
+ if (cleanup) {
+ cleanup_default_namespace(ioctx);
+ cleanup_namespace(ioctx, nspace);
+ }
+ rados_ioctx_destroy(ioctx);
+}
+
diff --git a/src/test/librados/TestCase.h b/src/test/librados/TestCase.h
new file mode 100644
index 000000000..15fcfeb73
--- /dev/null
+++ b/src/test/librados/TestCase.h
@@ -0,0 +1,124 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_RADOS_TESTCASE_H
+#define CEPH_TEST_RADOS_TESTCASE_H
+
+#include "include/rados/librados.h"
+#include "gtest/gtest.h"
+
+#include <string>
+
+/**
+ * These test cases create a temporary pool that lives as long as the
+ * test case. We initially use the default namespace and assume
+ * test will whatever namespaces it wants. After each test all objects
+ * are removed.
+ *
+ * Since pool creation and deletion is slow, this allows many tests to
+ * run faster.
+ */
+class RadosTestNS : public ::testing::Test {
+public:
+ RadosTestNS(bool c=false) : cleanup(c) {}
+ ~RadosTestNS() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static void cleanup_all_objects(rados_ioctx_t ioctx);
+ static rados_t s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ rados_t cluster = nullptr;
+ rados_ioctx_t ioctx = nullptr;
+ bool cleanup;
+};
+
+struct RadosTestNSCleanup : public RadosTestNS {
+ RadosTestNSCleanup() : RadosTestNS(true) {}
+};
+
+class RadosTestECNS : public RadosTestNS {
+public:
+ RadosTestECNS(bool c=false) : cleanup(c) {}
+ ~RadosTestECNS() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static rados_t s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ rados_t cluster = nullptr;
+ rados_ioctx_t ioctx = nullptr;
+ uint64_t alignment = 0;
+ bool cleanup;
+};
+
+struct RadosTestECNSCleanup : public RadosTestECNS {
+ RadosTestECNSCleanup() : RadosTestECNS(true) {}
+};
+
+/**
+ * These test cases create a temporary pool that lives as long as the
+ * test case. Each test within a test case gets a new ioctx set to a
+ * unique namespace within the pool.
+ *
+ * Since pool creation and deletion is slow, this allows many tests to
+ * run faster.
+ */
+class RadosTest : public ::testing::Test {
+public:
+ RadosTest(bool c=false) : cleanup(c) {}
+ ~RadosTest() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static void cleanup_default_namespace(rados_ioctx_t ioctx);
+ static void cleanup_namespace(rados_ioctx_t ioctx, std::string ns);
+ static rados_t s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ rados_t cluster = nullptr;
+ rados_ioctx_t ioctx = nullptr;
+ std::string nspace;
+ bool cleanup;
+};
+
+class RadosTestEC : public RadosTest {
+public:
+ RadosTestEC(bool c=false) : cleanup(c) {}
+ ~RadosTestEC() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static rados_t s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ rados_t cluster = nullptr;
+ rados_ioctx_t ioctx = nullptr;
+ bool cleanup;
+ std::string nspace;
+ uint64_t alignment = 0;
+};
+
+/**
+ * Test case without creating a temporary pool in advance.
+ * This is necessary for scenarios such that we need to
+ * manually create a pool, start some long-runing tasks and
+ * then the related pool is suddenly gone.
+ */
+class RadosTestNP: public ::testing::Test {
+public:
+ RadosTestNP() {}
+ ~RadosTestNP() override {}
+};
+
+#endif
diff --git a/src/test/librados/aio.cc b/src/test/librados/aio.cc
new file mode 100644
index 000000000..68587fe87
--- /dev/null
+++ b/src/test/librados/aio.cc
@@ -0,0 +1,1724 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <boost/scoped_ptr.hpp>
+#include <fmt/format.h>
+
+#include "include/err.h"
+#include "include/rados/librados.h"
+#include "include/types.h"
+#include "include/stringify.h"
+#include "include/scope_guard.h"
+
+#include "common/errno.h"
+
+#include "gtest/gtest.h"
+
+#include "test.h"
+#include "crimson_utils.h"
+
+using std::ostringstream;
+
+class AioTestData
+{
+public:
+ AioTestData()
+ : m_cluster(NULL),
+ m_ioctx(NULL),
+ m_init(false)
+ {
+ }
+
+ ~AioTestData()
+ {
+ if (m_init) {
+ rados_ioctx_destroy(m_ioctx);
+ destroy_one_pool(m_pool_name, &m_cluster);
+ }
+ }
+
+ std::string init()
+ {
+ int ret;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ m_pool_name = get_temp_pool_name(pool_prefix);
+ std::string err = create_one_pool(m_pool_name, &m_cluster);
+ if (!err.empty()) {
+ ostringstream oss;
+ oss << "create_one_pool(" << m_pool_name << ") failed: error " << err;
+ return oss.str();
+ }
+ ret = rados_ioctx_create(m_cluster, m_pool_name.c_str(), &m_ioctx);
+ if (ret) {
+ destroy_one_pool(m_pool_name, &m_cluster);
+ ostringstream oss;
+ oss << "rados_ioctx_create failed: error " << ret;
+ return oss.str();
+ }
+ m_init = true;
+ return "";
+ }
+
+ rados_t m_cluster;
+ rados_ioctx_t m_ioctx;
+ std::string m_pool_name;
+ bool m_init;
+};
+
+TEST(LibRadosAio, TooBig) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(-E2BIG, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, UINT_MAX, 0));
+ ASSERT_EQ(-E2BIG, rados_aio_write_full(test_data.m_ioctx, "foo",
+ my_completion, buf, UINT_MAX));
+ ASSERT_EQ(-E2BIG, rados_aio_append(test_data.m_ioctx, "foo",
+ my_completion, buf, UINT_MAX));
+ rados_aio_release(my_completion);
+}
+
+TEST(LibRadosAio, SimpleWrite) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ auto sg = make_scope_guard([&] { rados_aio_release(my_completion); });
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+
+ rados_ioctx_set_namespace(test_data.m_ioctx, "nspace");
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ auto sg2 = make_scope_guard([&] { rados_aio_release(my_completion2); });
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion2, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+}
+
+TEST(LibRadosAio, WaitForSafe) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+}
+
+TEST(LibRadosAio, RoundTrip) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[256];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAio, RoundTrip2) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAio, RoundTrip3) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+
+ rados_write_op_t op1 = rados_create_write_op();
+ rados_write_op_write(op1, buf, sizeof(buf), 0);
+ rados_write_op_set_alloc_hint2(op1, 0, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, rados_aio_write_op_operate(op1, test_data.m_ioctx, my_completion,
+ "foo", NULL, 0));
+ rados_release_write_op(op1);
+
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+
+ rados_read_op_t op2 = rados_create_read_op();
+ rados_read_op_read(op2, 0, sizeof(buf2), buf2, NULL, NULL);
+ rados_read_op_set_flags(op2, LIBRADOS_OP_FLAG_FADVISE_NOCACHE |
+ LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ceph_le32 init_value(-1);
+ ceph_le32 checksum[2];
+ rados_read_op_checksum(op2, LIBRADOS_CHECKSUM_TYPE_CRC32C,
+ reinterpret_cast<char *>(&init_value),
+ sizeof(init_value), 0, 0, 0,
+ reinterpret_cast<char *>(&checksum),
+ sizeof(checksum), NULL);
+ ASSERT_EQ(0, rados_aio_read_op_operate(op2, test_data.m_ioctx, my_completion2,
+ "foo", 0));
+ rados_release_read_op(op2);
+
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion2);
+
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(1U, checksum[0]);
+ ASSERT_EQ(bl.crc32c(-1), checksum[1]);
+}
+
+TEST(LibRadosAio, RoundTripAppend) {
+ AioTestData test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_append(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_append(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ char buf3[sizeof(buf) + sizeof(buf2)];
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion3, buf3, sizeof(buf3), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ((int)sizeof(buf3), rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST(LibRadosAio, RemoveTest) {
+ char buf[128];
+ char buf2[sizeof(buf)];
+ rados_completion_t my_completion;
+ AioTestData test_data;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_aio_remove(test_data.m_ioctx, "foo", my_completion));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ(-ENOENT, rados_read(test_data.m_ioctx, "foo", buf2, sizeof(buf2), 0));
+ rados_aio_release(my_completion);
+}
+
+TEST(LibRadosAio, XattrsRoundTrip) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ // append
+ AioTestData test_data;
+ ASSERT_EQ("", test_data.init());
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));
+ // async getxattr
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ ASSERT_EQ(0, rados_aio_getxattr(test_data.m_ioctx, "foo", my_completion, attr1, buf, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(-ENODATA, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+ // async setxattr
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_setxattr(test_data.m_ioctx, "foo", my_completion2, attr1, attr1_buf, sizeof(attr1_buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ rados_aio_release(my_completion2);
+ // async getxattr
+ rados_completion_t my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_getxattr(test_data.m_ioctx, "foo", my_completion3, attr1, buf, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ((int)sizeof(attr1_buf), rados_aio_get_return_value(my_completion3));
+ rados_aio_release(my_completion3);
+ // check content of attribute
+ ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
+}
+
+TEST(LibRadosAio, RmXattr) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ // append
+ memset(buf, 0xaa, sizeof(buf));
+ AioTestData test_data;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));
+ // async setxattr
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ ASSERT_EQ(0, rados_aio_setxattr(test_data.m_ioctx, "foo", my_completion, attr1, attr1_buf, sizeof(attr1_buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+ // async rmxattr
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_rmxattr(test_data.m_ioctx, "foo", my_completion2, attr1));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ rados_aio_release(my_completion2);
+ // async getxattr after deletion
+ rados_completion_t my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_getxattr(test_data.m_ioctx, "foo", my_completion3, attr1, buf, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ(-ENODATA, rados_aio_get_return_value(my_completion3));
+ rados_aio_release(my_completion3);
+ // Test rmxattr on a removed object
+ char buf2[128];
+ char attr2[] = "attr2";
+ char attr2_buf[] = "foo bar baz";
+ memset(buf2, 0xbb, sizeof(buf2));
+ ASSERT_EQ(0, rados_write(test_data.m_ioctx, "foo_rmxattr", buf2, sizeof(buf2), 0));
+ // asynx setxattr
+ rados_completion_t my_completion4;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion4));
+ ASSERT_EQ(0, rados_aio_setxattr(test_data.m_ioctx, "foo_rmxattr", my_completion4, attr2, attr2_buf, sizeof(attr2_buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion4));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion4));
+ rados_aio_release(my_completion4);
+ // remove object
+ ASSERT_EQ(0, rados_remove(test_data.m_ioctx, "foo_rmxattr"));
+ // async rmxattr on non existing object
+ rados_completion_t my_completion5;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion5));
+ ASSERT_EQ(0, rados_aio_rmxattr(test_data.m_ioctx, "foo_rmxattr", my_completion5, attr2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion5));
+ }
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(my_completion5));
+ rados_aio_release(my_completion5);
+}
+
+TEST(LibRadosAio, XattrIter) {
+ AioTestData test_data;
+ ASSERT_EQ("", test_data.init());
+ // Create an object with 2 attributes
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ char attr2[] = "attr2";
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_setxattr(test_data.m_ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ(0, rados_setxattr(test_data.m_ioctx, "foo", attr2, attr2_buf, sizeof(attr2_buf)));
+ // call async version of getxattrs and wait for completion
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2((void*)&test_data,
+ nullptr, &my_completion));
+ rados_xattrs_iter_t iter;
+ ASSERT_EQ(0, rados_aio_getxattrs(test_data.m_ioctx, "foo", my_completion, &iter));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ // loop over attributes
+ int num_seen = 0;
+ while (true) {
+ const char *name;
+ const char *val;
+ size_t len;
+ ASSERT_EQ(0, rados_getxattrs_next(iter, &name, &val, &len));
+ if (name == NULL) {
+ break;
+ }
+ ASSERT_LT(num_seen, 2);
+ if ((strcmp(name, attr1) == 0) && (val != NULL) && (memcmp(val, attr1_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else if ((strcmp(name, attr2) == 0) && (val != NULL) && (memcmp(val, attr2_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else {
+ ASSERT_EQ(0, 1);
+ }
+ }
+ rados_getxattrs_end(iter);
+}
+
+TEST(LibRadosAio, IsComplete) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_complete.
+ while (true) {
+ int is_complete = rados_aio_is_complete(my_completion2);
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAio, IsSafe) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
+ while (true) {
+ int is_safe = rados_aio_is_safe(my_completion);
+ if (is_safe)
+ break;
+ }
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAio, ReturnValue) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "nonexistent",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+}
+
+TEST(LibRadosAio, Flush) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_aio_flush(test_data.m_ioctx));
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf2), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAio, FlushAsync) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ rados_completion_t flush_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &flush_completion));
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_aio_flush_async(test_data.m_ioctx, flush_completion));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(flush_completion));
+ }
+ ASSERT_EQ(1, rados_aio_is_complete(my_completion));
+ ASSERT_EQ(1, rados_aio_is_complete(flush_completion));
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf2), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(flush_completion);
+}
+
+TEST(LibRadosAio, RoundTripWriteFull) {
+ AioTestData test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_write_full(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ char buf3[sizeof(buf) + sizeof(buf2)];
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion3, buf3, sizeof(buf3), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ((int)sizeof(buf2), rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST(LibRadosAio, RoundTripWriteSame) {
+ AioTestData test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char full[128];
+ memset(full, 0xcc, sizeof(full));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, full, sizeof(full), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ /* write the same buf four times */
+ char buf[32];
+ size_t ws_write_len = sizeof(full);
+ memset(buf, 0xdd, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_writesame(test_data.m_ioctx, "foo",
+ my_completion2, buf, sizeof(buf),
+ ws_write_len, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion3, full, sizeof(full), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ((int)sizeof(full), rados_aio_get_return_value(my_completion3));
+ for (char *cmp = full; cmp < full + sizeof(full); cmp += sizeof(buf)) {
+ ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+ }
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST(LibRadosAio, SimpleStat) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ uint64_t psize;
+ time_t pmtime;
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion2, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(sizeof(buf), psize);
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAio, OperateMtime)
+{
+ AioTestData test_data;
+ ASSERT_EQ("", test_data.init());
+
+ time_t set_mtime = 1457129052;
+ {
+ rados_write_op_t op = rados_create_write_op();
+ rados_write_op_create(op, LIBRADOS_CREATE_IDEMPOTENT, nullptr);
+ rados_completion_t completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &completion));
+ ASSERT_EQ(0, rados_aio_write_op_operate(op, test_data.m_ioctx, completion,
+ "foo", &set_mtime, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(completion));
+ rados_aio_release(completion);
+ rados_release_write_op(op);
+ }
+ {
+ uint64_t size;
+ timespec mtime;
+ ASSERT_EQ(0, rados_stat2(test_data.m_ioctx, "foo", &size, &mtime));
+ EXPECT_EQ(0, size);
+ EXPECT_EQ(set_mtime, mtime.tv_sec);
+ EXPECT_EQ(0, mtime.tv_nsec);
+ }
+}
+
+TEST(LibRadosAio, Operate2Mtime)
+{
+ AioTestData test_data;
+ ASSERT_EQ("", test_data.init());
+
+ timespec set_mtime{1457129052, 123456789};
+ {
+ rados_write_op_t op = rados_create_write_op();
+ rados_write_op_create(op, LIBRADOS_CREATE_IDEMPOTENT, nullptr);
+ rados_completion_t completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &completion));
+ ASSERT_EQ(0, rados_aio_write_op_operate2(op, test_data.m_ioctx, completion,
+ "foo", &set_mtime, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(completion));
+ rados_aio_release(completion);
+ rados_release_write_op(op);
+ }
+ {
+ uint64_t size;
+ timespec mtime;
+ ASSERT_EQ(0, rados_stat2(test_data.m_ioctx, "foo", &size, &mtime));
+ EXPECT_EQ(0, size);
+ EXPECT_EQ(set_mtime.tv_sec, mtime.tv_sec);
+ EXPECT_EQ(set_mtime.tv_nsec, mtime.tv_nsec);
+ }
+}
+
+TEST(LibRadosAio, SimpleStatNS) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_ioctx_set_namespace(test_data.m_ioctx, "nspace");
+ char buf2[64];
+ memset(buf2, 0xbb, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ uint64_t psize;
+ time_t pmtime;
+ rados_completion_t my_completion2;
+ rados_ioctx_set_namespace(test_data.m_ioctx, "");
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion2, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(sizeof(buf), psize);
+
+ rados_ioctx_set_namespace(test_data.m_ioctx, "nspace");
+ rados_completion_t my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion3, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(sizeof(buf2), psize);
+
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST(LibRadosAio, StatRemove) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ uint64_t psize;
+ time_t pmtime;
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion2, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(sizeof(buf), psize);
+ rados_completion_t my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_remove(test_data.m_ioctx, "foo", my_completion3));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion3));
+ uint64_t psize2;
+ time_t pmtime2;
+ rados_completion_t my_completion4;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion4));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion4, &psize2, &pmtime2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion4));
+ }
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(my_completion4));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+ rados_aio_release(my_completion4);
+}
+
+TEST(LibRadosAio, ExecuteClass) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ }
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ char out[128];
+ ASSERT_EQ(0, rados_aio_exec(test_data.m_ioctx, "foo", my_completion2,
+ "hello", "say_hello", NULL, 0, out, sizeof(out)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(13, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, strncmp("Hello, world!", out, 13));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+using std::string;
+using std::map;
+using std::set;
+
+TEST(LibRadosAio, MultiWrite) {
+ AioTestData test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+
+ char buf3[(sizeof(buf) + sizeof(buf2)) * 3];
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion3, buf3, sizeof(buf3), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)), rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST(LibRadosAio, AioUnlock) {
+ AioTestData test_data;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_lock_exclusive(test_data.m_ioctx, "foo", "TestLock", "Cookie", "", NULL, 0));
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ ASSERT_EQ(0, rados_aio_unlock(test_data.m_ioctx, "foo", "TestLock", "Cookie", my_completion));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ ASSERT_EQ(0, rados_lock_exclusive(test_data.m_ioctx, "foo", "TestLock", "Cookie", "", NULL, 0));
+}
+
+// EC test cases
+class AioTestDataEC
+{
+public:
+ AioTestDataEC()
+ : m_cluster(NULL),
+ m_ioctx(NULL),
+ m_init(false)
+ {
+ }
+
+ ~AioTestDataEC()
+ {
+ if (m_init) {
+ rados_ioctx_destroy(m_ioctx);
+ destroy_one_ec_pool(m_pool_name, &m_cluster);
+ }
+ }
+
+ std::string init()
+ {
+ int ret;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ m_pool_name = get_temp_pool_name(pool_prefix);
+ std::string err = create_one_ec_pool(m_pool_name, &m_cluster);
+ if (!err.empty()) {
+ ostringstream oss;
+ oss << "create_one_ec_pool(" << m_pool_name << ") failed: error " << err;
+ return oss.str();
+ }
+ ret = rados_ioctx_create(m_cluster, m_pool_name.c_str(), &m_ioctx);
+ if (ret) {
+ destroy_one_ec_pool(m_pool_name, &m_cluster);
+ ostringstream oss;
+ oss << "rados_ioctx_create failed: error " << ret;
+ return oss.str();
+ }
+ m_init = true;
+ return "";
+ }
+
+ rados_t m_cluster;
+ rados_ioctx_t m_ioctx;
+ std::string m_pool_name;
+ bool m_init;
+};
+
+TEST(LibRadosAioEC, SimpleWrite) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ auto sg = make_scope_guard([&] { rados_aio_release(my_completion); });
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+
+ rados_ioctx_set_namespace(test_data.m_ioctx, "nspace");
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ auto sg2 = make_scope_guard([&] { rados_aio_release(my_completion2); });
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion2, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+}
+
+TEST(LibRadosAioEC, WaitForComplete) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+}
+
+TEST(LibRadosAioEC, RoundTrip) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[256];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAioEC, RoundTrip2) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAioEC, RoundTripAppend) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3, my_completion4;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ int req;
+ ASSERT_EQ(0, rados_ioctx_pool_requires_alignment2(test_data.m_ioctx, &req));
+ ASSERT_NE(0, req);
+ uint64_t alignment;
+ ASSERT_EQ(0, rados_ioctx_pool_required_alignment2(test_data.m_ioctx, &alignment));
+ ASSERT_NE(0U, alignment);
+
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ ASSERT_EQ(0, rados_aio_append(test_data.m_ioctx, "foo",
+ my_completion, buf, bsize));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+
+ int hbsize = bsize / 2;
+ char *buf2 = (char *)new char[hbsize];
+ memset(buf2, 0xdd, hbsize);
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_append(test_data.m_ioctx, "foo",
+ my_completion2, buf2, hbsize));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_append(test_data.m_ioctx, "foo",
+ my_completion3, buf2, hbsize));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ EXPECT_EQ(-EOPNOTSUPP, rados_aio_get_return_value(my_completion3));
+
+ int tbsize = bsize + hbsize;
+ char *buf3 = (char *)new char[tbsize];
+ memset(buf3, 0, tbsize);
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion4));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion4, buf3, bsize * 3, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion4));
+ }
+ ASSERT_EQ(tbsize, rados_aio_get_return_value(my_completion4));
+ ASSERT_EQ(0, memcmp(buf3, buf, bsize));
+ ASSERT_EQ(0, memcmp(buf3 + bsize, buf2, hbsize));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+ rados_aio_release(my_completion4);
+ delete[] buf;
+ delete[] buf2;
+ delete[] buf3;
+}
+
+TEST(LibRadosAioEC, IsComplete) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_complete.
+ while (true) {
+ int is_complete = rados_aio_is_complete(my_completion2);
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAioEC, IsSafe) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
+ while (true) {
+ int is_safe = rados_aio_is_safe(my_completion);
+ if (is_safe)
+ break;
+ }
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAioEC, ReturnValue) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "nonexistent",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+}
+
+TEST(LibRadosAioEC, Flush) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_aio_flush(test_data.m_ioctx));
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf2), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAioEC, FlushAsync) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ rados_completion_t flush_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &flush_completion));
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_aio_flush_async(test_data.m_ioctx, flush_completion));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(flush_completion));
+ }
+ ASSERT_EQ(1, rados_aio_is_complete(my_completion));
+ ASSERT_EQ(1, rados_aio_is_complete(flush_completion));
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ((int)sizeof(buf2), rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(flush_completion);
+}
+
+TEST(LibRadosAioEC, RoundTripWriteFull) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_write_full(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ char buf3[sizeof(buf) + sizeof(buf2)];
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion3, buf3, sizeof(buf3), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ((int)sizeof(buf2), rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST(LibRadosAioEC, SimpleStat) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ uint64_t psize;
+ time_t pmtime;
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion2, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(sizeof(buf), psize);
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+
+TEST(LibRadosAioEC, SimpleStatNS) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_ioctx_set_namespace(test_data.m_ioctx, "nspace");
+ char buf2[64];
+ memset(buf2, 0xbb, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ uint64_t psize;
+ time_t pmtime;
+ rados_completion_t my_completion2;
+ rados_ioctx_set_namespace(test_data.m_ioctx, "");
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion2, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(sizeof(buf), psize);
+
+ rados_ioctx_set_namespace(test_data.m_ioctx, "nspace");
+ rados_completion_t my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion3, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(sizeof(buf2), psize);
+
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST(LibRadosAioEC, StatRemove) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ uint64_t psize;
+ time_t pmtime;
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion2, &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(sizeof(buf), psize);
+ rados_completion_t my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_remove(test_data.m_ioctx, "foo", my_completion3));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion3));
+ uint64_t psize2;
+ time_t pmtime2;
+ rados_completion_t my_completion4;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion4));
+ ASSERT_EQ(0, rados_aio_stat(test_data.m_ioctx, "foo",
+ my_completion4, &psize2, &pmtime2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion4));
+ }
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(my_completion4));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+ rados_aio_release(my_completion4);
+}
+
+TEST(LibRadosAioEC, ExecuteClass) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ char out[128];
+ ASSERT_EQ(0, rados_aio_exec(test_data.m_ioctx, "foo", my_completion2,
+ "hello", "say_hello", NULL, 0, out, sizeof(out)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(13, rados_aio_get_return_value(my_completion2));
+ ASSERT_EQ(0, strncmp("Hello, world!", out, 13));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST(LibRadosAioEC, MultiWrite) {
+ SKIP_IF_CRIMSON();
+ AioTestDataEC test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion2));
+ ASSERT_EQ(0, rados_aio_write(test_data.m_ioctx, "foo",
+ my_completion2, buf2, sizeof(buf2), sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+ }
+ ASSERT_EQ(-EOPNOTSUPP, rados_aio_get_return_value(my_completion2));
+
+ char buf3[(sizeof(buf) + sizeof(buf2)) * 3];
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr,
+ nullptr, &my_completion3));
+ ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+ my_completion3, buf3, sizeof(buf3), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+ }
+ ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
diff --git a/src/test/librados/aio_cxx.cc b/src/test/librados/aio_cxx.cc
new file mode 100644
index 000000000..5647bd9c0
--- /dev/null
+++ b/src/test/librados/aio_cxx.cc
@@ -0,0 +1,2467 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <boost/scoped_ptr.hpp>
+#include <fmt/format.h>
+
+#include "gtest/gtest.h"
+
+#include "common/errno.h"
+#include "include/err.h"
+#include "include/rados/librados.hpp"
+#include "include/types.h"
+#include "include/stringify.h"
+#include "include/scope_guard.h"
+#include "common/ceph_mutex.h"
+#include <fmt/format.h>
+
+#include "test_cxx.h"
+#include "crimson_utils.h"
+
+using namespace std;
+using namespace librados;
+
+class AioTestDataPP
+{
+public:
+ AioTestDataPP()
+ : m_init(false),
+ m_oid("foo")
+ {
+ }
+
+ ~AioTestDataPP()
+ {
+ if (m_init) {
+ m_ioctx.close();
+ destroy_one_pool_pp(m_pool_name, m_cluster);
+ }
+ }
+
+ std::string init()
+ {
+ return init({});
+ }
+
+ std::string init(const std::map<std::string, std::string> &config)
+ {
+ int ret;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ m_pool_name = get_temp_pool_name(pool_prefix);
+ std::string err = create_one_pool_pp(m_pool_name, m_cluster, config);
+ if (!err.empty()) {
+ ostringstream oss;
+ oss << "create_one_pool(" << m_pool_name << ") failed: error " << err;
+ return oss.str();
+ }
+ ret = m_cluster.ioctx_create(m_pool_name.c_str(), m_ioctx);
+ if (ret) {
+ destroy_one_pool_pp(m_pool_name, m_cluster);
+ ostringstream oss;
+ oss << "rados_ioctx_create failed: error " << ret;
+ return oss.str();
+ }
+ m_oid = fmt::format("oid_{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ m_init = true;
+ return "";
+ }
+
+ Rados m_cluster;
+ IoCtx m_ioctx;
+ std::string m_pool_name;
+ bool m_init;
+ std::string m_oid;
+};
+
+TEST(LibRadosAio, TooBigPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+
+ bufferlist bl;
+ auto aio_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(-E2BIG, test_data.m_ioctx.aio_write(test_data.m_oid, aio_completion.get(), bl, UINT_MAX, 0));
+ ASSERT_EQ(-E2BIG, test_data.m_ioctx.aio_append(test_data.m_oid, aio_completion.get(), bl, UINT_MAX));
+ // ioctx.aio_write_full no way to overflow bl.length()
+}
+
+TEST(LibRadosAio, PoolQuotaPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ string p = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ(0, test_data.m_cluster.pool_create(p.c_str()));
+ IoCtx ioctx;
+ ASSERT_EQ(0, test_data.m_cluster.ioctx_create(p.c_str(), ioctx));
+ ioctx.application_enable("rados", true);
+
+ bufferlist inbl;
+ ASSERT_EQ(0, test_data.m_cluster.mon_command(
+ "{\"prefix\": \"osd pool set-quota\", \"pool\": \"" + p +
+ "\", \"field\": \"max_bytes\", \"val\": \"4096\"}",
+ inbl, NULL, NULL));
+
+ bufferlist bl;
+ bufferptr z(4096);
+ bl.append(z);
+ int n;
+ for (n = 0; n < 1024; ++n) {
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ auto completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, ioctx.aio_operate(test_data.m_oid + stringify(n),
+ completion.get(), &op,
+ librados::OPERATION_FULL_TRY));
+ completion->wait_for_complete();
+ int r = completion->get_return_value();
+ if (r == -EDQUOT)
+ break;
+ ASSERT_EQ(0, r);
+ sleep(1);
+ }
+ ASSERT_LT(n, 1024);
+
+ // make sure we have latest map that marked the pool full
+ test_data.m_cluster.wait_for_latest_osdmap();
+
+ // make sure we block without FULL_TRY
+ {
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ auto completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, ioctx.aio_operate("bar", completion.get(), &op, 0));
+ sleep(5);
+ ASSERT_FALSE(completion->is_complete());
+ }
+
+ ioctx.close();
+ ASSERT_EQ(0, test_data.m_cluster.pool_delete(p.c_str()));
+}
+
+TEST(LibRadosAio, SimpleWritePP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ }
+
+ {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ test_data.m_ioctx.set_namespace("nspace");
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ }
+}
+
+TEST(LibRadosAio, WaitForSafePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ ASSERT_EQ(0, my_completion->get_return_value());
+}
+
+TEST(LibRadosAio, RoundTripPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAio, RoundTripPP2) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAio, RoundTripPP3)
+{
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ auto my_completion1 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ObjectWriteOperation op;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ op.write(0, bl);
+ op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion1->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion1->get_return_value());
+
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ bl.clear();
+ ObjectReadOperation op1;
+ op1.read(0, sizeof(buf), &bl, NULL);
+ op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ bufferlist init_value_bl;
+ encode(static_cast<int32_t>(-1), init_value_bl);
+ bufferlist csum_bl;
+ op1.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl,
+ 0, 0, 0, &csum_bl, nullptr);
+ ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+ ASSERT_EQ(8U, csum_bl.length());
+ auto csum_bl_it = csum_bl.cbegin();
+ uint32_t csum_count;
+ uint32_t csum;
+ decode(csum_count, csum_bl_it);
+ ASSERT_EQ(1U, csum_count);
+ decode(csum, csum_bl_it);
+ ASSERT_EQ(bl.crc32c(-1), csum);
+ ioctx.remove("test_obj");
+ destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, RoundTripSparseReadPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ std::map<uint64_t, uint64_t> extents;
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_sparse_read(test_data.m_oid, my_completion2.get(),
+ &extents, &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ assert_eq_sparse(bl1, extents, bl2);
+}
+
+TEST(LibRadosAioPP, ReadIntoBufferlist) {
+
+ // here we test reading into a non-empty bufferlist referencing existing
+ // buffers
+
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+
+ bufferlist bl2;
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xbb, sizeof(buf2));
+ bl2.append(buffer::create_static(sizeof(buf2), buf2));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+}
+
+TEST(LibRadosAioPP, XattrsRoundTripPP) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, test_data.m_ioctx.append(test_data.m_oid, bl1, sizeof(buf)));
+ bufferlist bl2;
+ // async getxattr
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr(test_data.m_oid, my_completion.get(), attr1, bl2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(-ENODATA, my_completion->get_return_value());
+ // append
+ bufferlist bl3;
+ bl3.append(attr1_buf, sizeof(attr1_buf));
+ // async setxattr
+ AioTestDataPP test_data2;
+ ASSERT_EQ("", test_data2.init());
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr(test_data.m_oid, my_completion2.get(), attr1, bl3));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ // async getxattr
+ bufferlist bl4;
+ AioTestDataPP test_data3;
+ ASSERT_EQ("", test_data3.init());
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr(test_data.m_oid, my_completion3.get(), attr1, bl4));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(attr1_buf), my_completion3->get_return_value());
+ // check content of attribute
+ ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST(LibRadosAioPP, RmXattrPP) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, test_data.m_ioctx.append(test_data.m_oid, bl1, sizeof(buf)));
+ // async setxattr
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr(test_data.m_oid, my_completion.get(), attr1, bl2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ // async rmxattr
+ AioTestDataPP test_data2;
+ ASSERT_EQ("", test_data2.init());
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr(test_data.m_oid, my_completion2.get(), attr1));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ // async getxattr
+ AioTestDataPP test_data3;
+ ASSERT_EQ("", test_data3.init());
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ bufferlist bl3;
+ ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr(test_data.m_oid, my_completion3.get(), attr1, bl3));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ(-ENODATA, my_completion3->get_return_value());
+ // Test rmxattr on a removed object
+ char buf2[128];
+ char attr2[] = "attr2";
+ char attr2_buf[] = "foo bar baz";
+ memset(buf2, 0xbb, sizeof(buf2));
+ bufferlist bl21;
+ bl21.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
+ bufferlist bl22;
+ bl22.append(attr2_buf, sizeof(attr2_buf));
+ // async setxattr
+ AioTestDataPP test_data4;
+ ASSERT_EQ("", test_data4.init());
+ auto my_completion4 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo_rmxattr", my_completion4.get(), attr2, bl22));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion4->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion4->get_return_value());
+ // remove object
+ ASSERT_EQ(0, test_data.m_ioctx.remove("foo_rmxattr"));
+ // async rmxattr on non existing object
+ AioTestDataPP test_data5;
+ ASSERT_EQ("", test_data5.init());
+ auto my_completion5 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr("foo_rmxattr", my_completion5.get(), attr2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion5->wait_for_complete());
+ }
+ ASSERT_EQ(-ENOENT, my_completion5->get_return_value());
+}
+
+TEST(LibRadosIoPP, XattrListPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ // create an object with 2 attributes
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ char attr2[] = "attr2";
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.append(test_data.m_oid, bl1, sizeof(buf)));
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, test_data.m_ioctx.setxattr(test_data.m_oid, attr1, bl2));
+ bufferlist bl3;
+ bl3.append(attr2_buf, sizeof(attr2_buf));
+ ASSERT_EQ(0, test_data.m_ioctx.setxattr(test_data.m_oid, attr2, bl3));
+ // call async version of getxattrs
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ std::map<std::string, bufferlist> attrset;
+ ASSERT_EQ(0, test_data.m_ioctx.aio_getxattrs(test_data.m_oid, my_completion.get(), attrset));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+ i != attrset.end(); ++i) {
+ if (i->first == string(attr1)) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+ }
+ else if (i->first == string(attr2)) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+ }
+ else {
+ ASSERT_EQ(0, 1);
+ }
+ }
+}
+
+TEST(LibRadosAio, IsCompletePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test is_complete.
+ while (true) {
+ int is_complete = my_completion2->is_complete();
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAio, IsSafePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
+ while (true) {
+ int is_complete = my_completion->is_complete();
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ bufferlist bl2;
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAio, ReturnValuePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ bufferlist bl1;
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read("nonexistent", my_completion.get(),
+ &bl1, 128, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(-ENOENT, my_completion->get_return_value());
+}
+
+TEST(LibRadosAio, FlushPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_flush());
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAio, FlushAsyncPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ auto flush_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_flush_async(flush_completion.get()));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, flush_completion->wait_for_complete());
+ }
+ ASSERT_EQ(1, my_completion->is_complete());
+ ASSERT_EQ(1, flush_completion->is_complete());
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAio, RoundTripWriteFullPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write_full(test_data.m_oid, my_completion2.get(), bl2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ bufferlist bl3;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion3.get(),
+ &bl3, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf2), my_completion3->get_return_value());
+ ASSERT_EQ(sizeof(buf2), bl3.length());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAio, RoundTripWriteFullPP2)
+{
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ auto my_completion1 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ObjectWriteOperation op;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf);
+
+ op.write_full(bl);
+ op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion1->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion1->get_return_value());
+
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ bl.clear();
+ ObjectReadOperation op1;
+ op1.read(0, sizeof(buf), &bl, NULL);
+ op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+ ioctx.remove("test_obj");
+ destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, RoundTripWriteSamePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char full[128];
+ memset(full, 0xcc, sizeof(full));
+ bufferlist bl1;
+ bl1.append(full, sizeof(full));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(full), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ /* write the same buf four times */
+ char buf[32];
+ size_t ws_write_len = sizeof(full);
+ memset(buf, 0xdd, sizeof(buf));
+ bufferlist bl2;
+ bl2.append(buf, sizeof(buf));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_writesame(test_data.m_oid, my_completion2.get(), bl2,
+ ws_write_len, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ bufferlist bl3;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion3.get(),
+ &bl3, sizeof(full), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(full), my_completion3->get_return_value());
+ ASSERT_EQ(sizeof(full), bl3.length());
+ for (char *cmp = bl3.c_str(); cmp < bl3.c_str() + bl3.length();
+ cmp += sizeof(buf)) {
+ ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+ }
+}
+
+TEST(LibRadosAio, RoundTripWriteSamePP2)
+{
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ auto wr_cmpl = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ObjectWriteOperation op;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ op.writesame(0, sizeof(buf) * 4, bl);
+ op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", wr_cmpl.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, wr_cmpl->wait_for_complete());
+ }
+ EXPECT_EQ(0, wr_cmpl->get_return_value());
+
+ boost::scoped_ptr<AioCompletion>
+ rd_cmpl(cluster.aio_create_completion(0, 0));
+ char *cmp;
+ char full[sizeof(buf) * 4];
+ memset(full, 0, sizeof(full));
+ bufferlist fl;
+ fl.append(full, sizeof(full));
+ ObjectReadOperation op1;
+ op1.read(0, sizeof(full), &fl, NULL);
+ op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", rd_cmpl.get(), &op1, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rd_cmpl->wait_for_complete());
+ }
+ EXPECT_EQ(0, rd_cmpl->get_return_value());
+ for (cmp = fl.c_str(); cmp < fl.c_str() + fl.length(); cmp += sizeof(buf)) {
+ ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+ }
+
+ ioctx.remove("test_obj");
+ destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, SimpleStatPPNS) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ uint64_t psize;
+ time_t pmtime;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion2.get(),
+ &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), psize);
+}
+
+TEST(LibRadosAio, SimpleStatPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ uint64_t psize;
+ time_t pmtime;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion2.get(),
+ &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), psize);
+}
+
+TEST(LibRadosAio, OperateMtime)
+{
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+
+ time_t set_mtime = 1457129052;
+ {
+ auto c = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ librados::ObjectWriteOperation op;
+ op.mtime(&set_mtime);
+ op.create(false);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_operate(test_data.m_oid, c.get(), &op));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, c->wait_for_complete());
+ }
+ ASSERT_EQ(0, c->get_return_value());
+ }
+ {
+ uint64_t size;
+ timespec mtime;
+ ASSERT_EQ(0, test_data.m_ioctx.stat2(test_data.m_oid, &size, &mtime));
+ EXPECT_EQ(0, size);
+ EXPECT_EQ(set_mtime, mtime.tv_sec);
+ EXPECT_EQ(0, mtime.tv_nsec);
+ }
+}
+
+TEST(LibRadosAio, OperateMtime2)
+{
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+
+ timespec set_mtime{1457129052, 123456789};
+ {
+ auto c = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ librados::ObjectWriteOperation op;
+ op.mtime2(&set_mtime);
+ op.create(false);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_operate(test_data.m_oid, c.get(), &op));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, c->wait_for_complete());
+ }
+ ASSERT_EQ(0, c->get_return_value());
+ }
+ {
+ uint64_t size;
+ timespec mtime;
+ ASSERT_EQ(0, test_data.m_ioctx.stat2(test_data.m_oid, &size, &mtime));
+ EXPECT_EQ(0, size);
+ EXPECT_EQ(set_mtime.tv_sec, mtime.tv_sec);
+ EXPECT_EQ(set_mtime.tv_nsec, mtime.tv_nsec);
+ }
+}
+
+TEST(LibRadosAio, StatRemovePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ uint64_t psize;
+ time_t pmtime;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion2.get(),
+ &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), psize);
+ uint64_t psize2;
+ time_t pmtime2;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_remove(test_data.m_oid, my_completion3.get()));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion3->get_return_value());
+
+ auto my_completion4 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion4);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion4.get(),
+ &psize2, &pmtime2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion4->wait_for_complete());
+ }
+ ASSERT_EQ(-ENOENT, my_completion4->get_return_value());
+}
+
+TEST(LibRadosAio, ExecuteClassPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ bufferlist in, out;
+ ASSERT_EQ(0, test_data.m_ioctx.aio_exec(test_data.m_oid, my_completion2.get(),
+ "hello", "say_hello", in, &out));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
+}
+
+using std::string;
+using std::map;
+using std::set;
+
+TEST(LibRadosAio, OmapPP) {
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string header_str = "baz";
+ bufferptr bp(header_str.c_str(), header_str.size() + 1);
+ bufferlist header_to_set;
+ header_to_set.push_back(bp);
+ map<string, bufferlist> to_set;
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectWriteOperation op;
+ to_set["foo"] = header_to_set;
+ to_set["foo2"] = header_to_set;
+ to_set["qfoo3"] = header_to_set;
+ op.omap_set(to_set);
+
+ op.omap_set_header(header_to_set);
+
+ ioctx.aio_operate("test_obj", my_completion.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ }
+
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectReadOperation op;
+ map<string, pair<bufferlist, int> > assertions;
+ bufferlist val;
+ val.append(string("bar"));
+ assertions["foo"] = pair<bufferlist, int>(val, CEPH_OSD_CMPXATTR_OP_EQ);
+
+ int r;
+ op.omap_cmp(assertions, &r);
+
+ ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(-ECANCELED, my_completion->get_return_value());
+ ASSERT_EQ(-ECANCELED, r);
+ }
+
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectReadOperation op;
+
+ set<string> set_got;
+ map<string, bufferlist> map_got;
+
+ set<string> to_get;
+ map<string, bufferlist> got3;
+
+ map<string, bufferlist> got4;
+
+ bufferlist header;
+
+ op.omap_get_keys2("", 1, &set_got, nullptr, 0);
+ op.omap_get_vals2("foo", 1, &map_got, nullptr, 0);
+
+ to_get.insert("foo");
+ to_get.insert("qfoo3");
+ op.omap_get_vals_by_keys(to_get, &got3, 0);
+
+ op.omap_get_header(&header, 0);
+
+ op.omap_get_vals2("foo2", "q", 1, &got4, nullptr, 0);
+
+ ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+
+ ASSERT_EQ(header.length(), header_to_set.length());
+ ASSERT_EQ(set_got.size(), (unsigned)1);
+ ASSERT_EQ(*set_got.begin(), "foo");
+ ASSERT_EQ(map_got.size(), (unsigned)1);
+ ASSERT_EQ(map_got.begin()->first, "foo2");
+ ASSERT_EQ(got3.size(), (unsigned)2);
+ ASSERT_EQ(got3.begin()->first, "foo");
+ ASSERT_EQ(got3.rbegin()->first, "qfoo3");
+ ASSERT_EQ(got4.size(), (unsigned)1);
+ ASSERT_EQ(got4.begin()->first, "qfoo3");
+ }
+
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectWriteOperation op;
+ set<string> to_remove;
+ to_remove.insert("foo2");
+ op.omap_rm_keys(to_remove);
+ ioctx.aio_operate("test_obj", my_completion.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ }
+
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectReadOperation op;
+
+ set<string> set_got;
+ op.omap_get_keys2("", -1, &set_got, nullptr, 0);
+ ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ ASSERT_EQ(set_got.size(), (unsigned)2);
+ }
+
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectWriteOperation op;
+ op.omap_clear();
+ ioctx.aio_operate("test_obj", my_completion.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ }
+
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectReadOperation op;
+
+ set<string> set_got;
+ op.omap_get_keys2("", -1, &set_got, nullptr, 0);
+ ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ ASSERT_EQ(set_got.size(), (unsigned)0);
+ }
+
+ // omap_clear clears header *and* keys
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("some data");
+ map<string,bufferlist> to_set;
+ to_set["foo"] = bl;
+ to_set["foo2"] = bl;
+ to_set["qfoo3"] = bl;
+ op.omap_set(to_set);
+ op.omap_set_header(bl);
+ ioctx.aio_operate("foo3", my_completion.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ }
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectWriteOperation op;
+ op.omap_clear();
+ ioctx.aio_operate("foo3", my_completion.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ }
+ {
+ boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0));
+ ObjectReadOperation op;
+ set<string> set_got;
+ bufferlist hdr;
+ op.omap_get_keys2("", -1, &set_got, nullptr, 0);
+ op.omap_get_header(&hdr, NULL);
+ ioctx.aio_operate("foo3", my_completion.get(), &op, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion->get_return_value());
+ ASSERT_EQ(set_got.size(), (unsigned)0);
+ ASSERT_EQ(hdr.length(), 0u);
+ }
+
+ ioctx.remove("test_obj");
+ destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, MultiWritePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion2.get(),
+ bl2, sizeof(buf2), sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+
+ bufferlist bl3;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion3.get(),
+ &bl3, (sizeof(buf) + sizeof(buf2) * 3), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)), my_completion3->get_return_value());
+ ASSERT_EQ(sizeof(buf) + sizeof(buf2), bl3.length());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2)));
+}
+
+TEST(LibRadosAio, AioUnlockPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive(test_data.m_oid, "TestLock", "Cookie", "", NULL, 0));
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_unlock(test_data.m_oid, "TestLock", "Cookie", my_completion.get()));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive(test_data.m_oid, "TestLock", "Cookie", "", NULL, 0));
+}
+
+class AioTestDataECPP
+{
+public:
+ AioTestDataECPP()
+ : m_init(false),
+ m_oid("foo")
+ {}
+
+ ~AioTestDataECPP()
+ {
+ if (m_init) {
+ m_ioctx.close();
+ destroy_one_ec_pool_pp(m_pool_name, m_cluster);
+ }
+ }
+
+ std::string init()
+ {
+ int ret;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ m_pool_name = get_temp_pool_name(pool_prefix);
+ std::string err = create_one_ec_pool_pp(m_pool_name, m_cluster);
+ if (!err.empty()) {
+ ostringstream oss;
+ oss << "create_one_ec_pool(" << m_pool_name << ") failed: error " << err;
+ return oss.str();
+ }
+ ret = m_cluster.ioctx_create(m_pool_name.c_str(), m_ioctx);
+ if (ret) {
+ destroy_one_ec_pool_pp(m_pool_name, m_cluster);
+ ostringstream oss;
+ oss << "rados_ioctx_create failed: error " << ret;
+ return oss.str();
+ }
+ m_oid = fmt::format("oid_{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ m_init = true;
+ return "";
+ }
+
+ Rados m_cluster;
+ IoCtx m_ioctx;
+ std::string m_pool_name;
+ bool m_init;
+ std::string m_oid;
+};
+
+// EC test cases
+TEST(LibRadosAioEC, SimpleWritePP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ {
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ }
+
+ {
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ test_data.m_ioctx.set_namespace("nspace");
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ }
+}
+
+TEST(LibRadosAioEC, WaitForSafePP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ ASSERT_EQ(0, my_completion->get_return_value());
+}
+
+TEST(LibRadosAioEC, RoundTripPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAioEC, RoundTripPP2) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAioEC, RoundTripPP3)
+{
+ SKIP_IF_CRIMSON();
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ auto my_completion1 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};;
+ ObjectWriteOperation op;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf);
+
+ op.write(0, bl);
+ op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion1->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion1->get_return_value());
+
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ bl.clear();
+ ObjectReadOperation op1;
+ op1.read(0, sizeof(buf), &bl, NULL);
+ op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+ ioctx.remove("test_obj");
+ destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, RoundTripAppendPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_append(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ char buf2[128];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_append(test_data.m_oid, my_completion2.get(),
+ bl2, sizeof(buf2)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ bufferlist bl3;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion3.get(),
+ &bl3, 2 * sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ((int)(sizeof(buf) * 2), my_completion3->get_return_value());
+ ASSERT_EQ(sizeof(buf) * 2, bl3.length());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2)));
+}
+
+TEST(LibRadosAioPP, RemoveTestPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ ASSERT_EQ(0, test_data.m_ioctx.append(test_data.m_oid, bl1, sizeof(buf)));
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_remove(test_data.m_oid, my_completion.get()));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ ASSERT_EQ(-ENOENT, test_data.m_ioctx.read(test_data.m_oid, bl2, sizeof(buf), 0));
+}
+
+TEST(LibRadosAioEC, RoundTripSparseReadPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+
+ map<uint64_t, uint64_t> extents;
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_sparse_read(test_data.m_oid, my_completion2.get(),
+ &extents, &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ assert_eq_sparse(bl1, extents, bl2);
+}
+
+TEST(LibRadosAioEC, RoundTripAppendPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ bool req;
+ ASSERT_EQ(0, test_data.m_ioctx.pool_requires_alignment2(&req));
+ ASSERT_TRUE(req);
+ uint64_t alignment;
+ ASSERT_EQ(0, test_data.m_ioctx.pool_required_alignment2(&alignment));
+ ASSERT_NE((unsigned)0, alignment);
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ bufferlist bl1;
+ bl1.append(buf, bsize);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_append(test_data.m_oid, my_completion.get(),
+ bl1, bsize));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+
+ int hbsize = bsize / 2;
+ char *buf2 = (char *)new char[hbsize];
+ memset(buf2, 0xdd, hbsize);
+ bufferlist bl2;
+ bl2.append(buf2, hbsize);
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_append(test_data.m_oid, my_completion2.get(),
+ bl2, hbsize));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_append(test_data.m_oid, my_completion3.get(),
+ bl2, hbsize));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ EXPECT_EQ(-EOPNOTSUPP, my_completion3->get_return_value());
+
+ bufferlist bl3;
+ auto my_completion4 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion4);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion4.get(),
+ &bl3, bsize * 3, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion4->wait_for_complete());
+ }
+ int tbsize = bsize + hbsize;
+ ASSERT_EQ(tbsize, my_completion4->get_return_value());
+ ASSERT_EQ((unsigned)tbsize, bl3.length());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+ ASSERT_EQ(0, memcmp(bl3.c_str() + bsize, buf2, hbsize));
+ delete[] buf;
+ delete[] buf2;
+}
+
+TEST(LibRadosAioEC, IsCompletePP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test is_complete.
+ while (true) {
+ int is_complete = my_completion2->is_complete();
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+TEST(LibRadosAioEC, IsSafePP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
+ while (true) {
+ int is_complete = my_completion->is_complete();
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ bufferlist bl2;
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAioEC, ReturnValuePP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ bufferlist bl1;
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read("nonexistent", my_completion.get(),
+ &bl1, 128, 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(-ENOENT, my_completion->get_return_value());
+}
+
+TEST(LibRadosAioEC, FlushPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_flush());
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAioEC, FlushAsyncPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ auto flush_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_flush_async(flush_completion.get()));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, flush_completion->wait_for_complete());
+ }
+ ASSERT_EQ(1, my_completion->is_complete());
+ ASSERT_EQ(1, flush_completion->is_complete());
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion2.get(),
+ &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl2.length());
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST(LibRadosAioEC, RoundTripWriteFullPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write_full(test_data.m_oid, my_completion2.get(), bl2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ bufferlist bl3;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion3.get(),
+ &bl3, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf2), my_completion3->get_return_value());
+ ASSERT_EQ(sizeof(buf2), bl3.length());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAioEC, RoundTripWriteFullPP2)
+{
+ SKIP_IF_CRIMSON();
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ auto my_completion1 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ObjectWriteOperation op;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf);
+
+ op.write_full(bl);
+ op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion1->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion1->get_return_value());
+
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ bl.clear();
+ ObjectReadOperation op1;
+ op1.read(0, sizeof(buf), &bl, NULL);
+ op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ EXPECT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+ ioctx.remove("test_obj");
+ destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAioEC, SimpleStatPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ uint64_t psize;
+ time_t pmtime;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion2.get(),
+ &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), psize);
+}
+
+TEST(LibRadosAioEC, SimpleStatPPNS) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ uint64_t psize;
+ time_t pmtime;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion2.get(),
+ &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), psize);
+}
+
+TEST(LibRadosAioEC, StatRemovePP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ uint64_t psize;
+ time_t pmtime;
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion2.get(),
+ &psize, &pmtime));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(sizeof(buf), psize);
+ uint64_t psize2;
+ time_t pmtime2;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_remove(test_data.m_oid, my_completion3.get()));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion3->get_return_value());
+
+ auto my_completion4 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion4);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_stat(test_data.m_oid, my_completion4.get(),
+ &psize2, &pmtime2));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion4->wait_for_complete());
+ }
+ ASSERT_EQ(-ENOENT, my_completion4->get_return_value());
+}
+
+TEST(LibRadosAioEC, ExecuteClassPP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ bufferlist in, out;
+ ASSERT_EQ(0, test_data.m_ioctx.aio_exec(test_data.m_oid, my_completion2.get(),
+ "hello", "say_hello", in, &out));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+ ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
+}
+
+TEST(LibRadosAioEC, OmapPP) {
+ SKIP_IF_CRIMSON();
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string header_str = "baz";
+ bufferptr bp(header_str.c_str(), header_str.size() + 1);
+ bufferlist header_to_set;
+ header_to_set.push_back(bp);
+ map<string, bufferlist> to_set;
+ {
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ObjectWriteOperation op;
+ to_set["foo"] = header_to_set;
+ to_set["foo2"] = header_to_set;
+ to_set["qfoo3"] = header_to_set;
+ op.omap_set(to_set);
+
+ op.omap_set_header(header_to_set);
+
+ ioctx.aio_operate("test_obj", my_completion.get(), &op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ EXPECT_EQ(-EOPNOTSUPP, my_completion->get_return_value());
+ }
+ ioctx.remove("test_obj");
+ destroy_one_ec_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAioEC, MultiWritePP) {
+ SKIP_IF_CRIMSON();
+ AioTestDataECPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion2.get(),
+ bl2, sizeof(buf2), sizeof(buf)));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(-EOPNOTSUPP, my_completion2->get_return_value());
+
+ bufferlist bl3;
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion3);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read(test_data.m_oid, my_completion3.get(),
+ &bl3, (sizeof(buf) + sizeof(buf2) * 3), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), my_completion3->get_return_value());
+ ASSERT_EQ(sizeof(buf), bl3.length());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+
+}
+
+TEST(LibRadosAio, RacingRemovePP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init({{"objecter_retry_writes_after_first_reply", "true"}}));
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion2);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_remove(test_data.m_oid, my_completion2.get()));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ my_completion2->wait_for_complete();
+ my_completion->wait_for_complete();
+ }
+ ASSERT_EQ(-ENOENT, my_completion2->get_return_value());
+ ASSERT_EQ(0, my_completion->get_return_value());
+ ASSERT_EQ(0, test_data.m_ioctx.stat(test_data.m_oid, nullptr, nullptr));
+}
+
+TEST(LibRadosAio, RoundTripCmpExtPP) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char full[128];
+ memset(full, 0xcc, sizeof(full));
+ bufferlist bl1;
+ bl1.append(full, sizeof(full));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write(test_data.m_oid, my_completion.get(),
+ bl1, sizeof(full), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+
+ /* compare with match */
+ bufferlist cbl;
+ cbl.append(full, sizeof(full));
+ auto my_completion2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_cmpext(test_data.m_oid, my_completion2.get(), 0, cbl));
+
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion2->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion2->get_return_value());
+
+ /* compare with mismatch */
+ memset(full, 0xdd, sizeof(full));
+ cbl.clear();
+ cbl.append(full, sizeof(full));
+ auto my_completion3 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_EQ(0, test_data.m_ioctx.aio_cmpext(test_data.m_oid, my_completion3.get(), 0, cbl));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion3->wait_for_complete());
+ }
+ ASSERT_EQ(-MAX_ERRNO, my_completion3->get_return_value());
+}
+
+TEST(LibRadosAio, RoundTripCmpExtPP2)
+{
+ int ret;
+ char buf[128];
+ char miscmp_buf[128];
+ bufferlist cbl;
+ Rados cluster;
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_info()->name());
+ std::string pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ auto wr_cmpl = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ObjectWriteOperation wr_op;
+ memset(buf, 0xcc, sizeof(buf));
+ memset(miscmp_buf, 0xdd, sizeof(miscmp_buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ wr_op.write_full(bl);
+ wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", wr_cmpl.get(), &wr_op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, wr_cmpl->wait_for_complete());
+ }
+ EXPECT_EQ(0, wr_cmpl->get_return_value());
+
+ /* cmpext as write op. first match then mismatch */
+ auto wr_cmpext_cmpl = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ cbl.append(buf, sizeof(buf));
+ ret = 0;
+
+ wr_op.cmpext(0, cbl, &ret);
+ wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", wr_cmpext_cmpl.get(), &wr_op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, wr_cmpext_cmpl->wait_for_complete());
+ }
+ EXPECT_EQ(0, wr_cmpext_cmpl->get_return_value());
+ EXPECT_EQ(0, ret);
+
+ auto wr_cmpext_cmpl2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ cbl.clear();
+ cbl.append(miscmp_buf, sizeof(miscmp_buf));
+ ret = 0;
+
+ wr_op.cmpext(0, cbl, &ret);
+ wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", wr_cmpext_cmpl2.get(), &wr_op);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, wr_cmpext_cmpl2->wait_for_complete());
+ }
+ EXPECT_EQ(-MAX_ERRNO, wr_cmpext_cmpl2->get_return_value());
+ EXPECT_EQ(-MAX_ERRNO, ret);
+
+ /* cmpext as read op */
+ auto rd_cmpext_cmpl = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ObjectReadOperation rd_op;
+ cbl.clear();
+ cbl.append(buf, sizeof(buf));
+ ret = 0;
+ rd_op.cmpext(0, cbl, &ret);
+ rd_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", rd_cmpext_cmpl.get(), &rd_op, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rd_cmpext_cmpl->wait_for_complete());
+ }
+ EXPECT_EQ(0, rd_cmpext_cmpl->get_return_value());
+ EXPECT_EQ(0, ret);
+
+ auto rd_cmpext_cmpl2 = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ cbl.clear();
+ cbl.append(miscmp_buf, sizeof(miscmp_buf));
+ ret = 0;
+
+ rd_op.cmpext(0, cbl, &ret);
+ rd_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ioctx.aio_operate("test_obj", rd_cmpext_cmpl2.get(), &rd_op, 0);
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rd_cmpext_cmpl2->wait_for_complete());
+ }
+ EXPECT_EQ(-MAX_ERRNO, rd_cmpext_cmpl2->get_return_value());
+ EXPECT_EQ(-MAX_ERRNO, ret);
+
+ ioctx.remove("test_obj");
+ destroy_one_pool_pp(pool_name, cluster);
+}
+
+ceph::mutex my_lock = ceph::make_mutex("my_lock");
+set<unsigned> inflight;
+unsigned max_success = 0;
+unsigned min_failed = 0;
+
+struct io_info {
+ unsigned i;
+ AioCompletion *c;
+};
+
+void pool_io_callback(completion_t cb, void *arg /* Actually AioCompletion* */)
+{
+ io_info *info = (io_info *)arg;
+ unsigned long i = info->i;
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, info->c->wait_for_complete());
+ }
+ int r = info->c->get_return_value();
+ //cout << "finish " << i << " r = " << r << std::endl;
+
+ std::scoped_lock l(my_lock);
+ inflight.erase(i);
+ if (r == 0) {
+ if (i > max_success) {
+ max_success = i;
+ }
+ } else {
+ if (!min_failed || i < min_failed) {
+ min_failed = i;
+ }
+ }
+}
+
+TEST(LibRadosAio, PoolEIOFlag) {
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+
+ bufferlist bl;
+ bl.append("some data");
+ std::thread *t = nullptr;
+
+ unsigned max = 100;
+ unsigned timeout = max * 10;
+ unsigned long i = 1;
+ my_lock.lock();
+ for (; min_failed == 0 && i <= timeout; ++i) {
+ io_info *info = new io_info;
+ info->i = i;
+ info->c = Rados::aio_create_completion();
+ info->c->set_complete_callback((void*)info, pool_io_callback);
+ inflight.insert(i);
+ my_lock.unlock();
+ int r = test_data.m_ioctx.aio_write(test_data.m_oid, info->c, bl, bl.length(), 0);
+ //cout << "start " << i << " r = " << r << std::endl;
+
+ if (i == max / 2) {
+ cout << "setting pool EIO" << std::endl;
+ t = new std::thread(
+ [&] {
+ bufferlist empty;
+ ASSERT_EQ(0, test_data.m_cluster.mon_command(
+ fmt::format(R"({{
+ "prefix": "osd pool set",
+ "pool": "{}",
+ "var": "eio",
+ "val": "true"
+ }})", test_data.m_pool_name),
+ empty, nullptr, nullptr));
+ });
+ }
+
+ std::this_thread::sleep_for(10ms);
+ my_lock.lock();
+ if (r < 0) {
+ inflight.erase(i);
+ break;
+ }
+ }
+ t->join();
+ delete t;
+
+ // wait for ios to finish
+ for (; !inflight.empty(); ++i) {
+ cout << "waiting for " << inflight.size() << std::endl;
+ my_lock.unlock();
+ sleep(1);
+ my_lock.lock();
+ }
+
+ cout << "max_success " << max_success << ", min_failed " << min_failed << std::endl;
+ ASSERT_TRUE(max_success + 1 == min_failed);
+ my_lock.unlock();
+}
+
+// This test case reproduces https://tracker.ceph.com/issues/57152
+TEST(LibRadosAio, MultiReads) {
+
+ // here we test multithreaded aio reads
+
+ AioTestDataPP test_data;
+ ASSERT_EQ("", test_data.init());
+ auto my_completion = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(my_completion);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion.get(),
+ bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+
+ // Don't use std::vector to store bufferlists (e.g for parallelizing aio_reads),
+ // as they are being moved whenever the vector resizes
+ // and will cause invalidated references.
+ std::deque<std::pair<bufferlist, std::unique_ptr<AioCompletion>>> reads;
+ for (int i = 0; i < 100; i++) {
+ // std::deque is appropriate here as emplace_back() is obliged to
+ // preserve the referenced inserted element. (Unlike insert() or erase())
+ auto& [bl, aiocp] = reads.emplace_back();
+ aiocp = std::unique_ptr<AioCompletion>{Rados::aio_create_completion()};
+ ASSERT_TRUE(aiocp);
+ ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", aiocp.get(),
+ &bl, sizeof(buf), 0));
+ }
+ for (auto& [bl, aiocp] : reads) {
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, aiocp->wait_for_complete());
+ }
+ ASSERT_EQ((int)sizeof(buf), aiocp->get_return_value());
+ ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+ }
+}
diff --git a/src/test/librados/asio.cc b/src/test/librados/asio.cc
new file mode 100644
index 000000000..9f86b4472
--- /dev/null
+++ b/src/test/librados/asio.cc
@@ -0,0 +1,369 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include "librados/librados_asio.h"
+#include <gtest/gtest.h>
+
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "common/errno.h"
+#include "global/global_init.h"
+
+#include <boost/range/begin.hpp>
+#include <boost/range/end.hpp>
+#include <spawn/spawn.hpp>
+#include <boost/asio/use_future.hpp>
+
+#define dout_subsys ceph_subsys_rados
+#define dout_context g_ceph_context
+
+using namespace std;
+
+// test fixture for global setup/teardown
+class AsioRados : public ::testing::Test {
+ static constexpr auto poolname = "ceph_test_rados_api_asio";
+
+ protected:
+ static librados::Rados rados;
+ static librados::IoCtx io;
+ // writes to snapio fail immediately with -EROFS. this is used to test errors
+ // that come from inside the initiating function, rather than passed to the
+ // AioCompletion callback
+ static librados::IoCtx snapio;
+
+ public:
+ static void SetUpTestCase() {
+ ASSERT_EQ(0, rados.init_with_context(g_ceph_context));
+ ASSERT_EQ(0, rados.connect());
+ // open/create test pool
+ int r = rados.ioctx_create(poolname, io);
+ if (r == -ENOENT) {
+ r = rados.pool_create(poolname);
+ if (r == -EEXIST) {
+ r = 0;
+ } else if (r == 0) {
+ r = rados.ioctx_create(poolname, io);
+ }
+ }
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(0, rados.ioctx_create(poolname, snapio));
+ snapio.snap_set_read(1);
+ // initialize the "exist" object
+ bufferlist bl;
+ bl.append("hello");
+ ASSERT_EQ(0, io.write_full("exist", bl));
+ }
+
+ static void TearDownTestCase() {
+ rados.shutdown();
+ }
+};
+librados::Rados AsioRados::rados;
+librados::IoCtx AsioRados::io;
+librados::IoCtx AsioRados::snapio;
+
+TEST_F(AsioRados, AsyncReadCallback)
+{
+ boost::asio::io_service service;
+
+ auto success_cb = [&] (boost::system::error_code ec, bufferlist bl) {
+ EXPECT_FALSE(ec);
+ EXPECT_EQ("hello", bl.to_str());
+ };
+ librados::async_read(service, io, "exist", 256, 0, success_cb);
+
+ auto failure_cb = [&] (boost::system::error_code ec, bufferlist bl) {
+ EXPECT_EQ(boost::system::errc::no_such_file_or_directory, ec);
+ };
+ librados::async_read(service, io, "noexist", 256, 0, failure_cb);
+
+ service.run();
+}
+
+TEST_F(AsioRados, AsyncReadFuture)
+{
+ boost::asio::io_service service;
+
+ std::future<bufferlist> f1 = librados::async_read(service, io, "exist", 256,
+ 0, boost::asio::use_future);
+ std::future<bufferlist> f2 = librados::async_read(service, io, "noexist", 256,
+ 0, boost::asio::use_future);
+
+ service.run();
+
+ EXPECT_NO_THROW({
+ auto bl = f1.get();
+ EXPECT_EQ("hello", bl.to_str());
+ });
+ EXPECT_THROW(f2.get(), boost::system::system_error);
+}
+
+TEST_F(AsioRados, AsyncReadYield)
+{
+ boost::asio::io_service service;
+
+ auto success_cr = [&] (spawn::yield_context yield) {
+ boost::system::error_code ec;
+ auto bl = librados::async_read(service, io, "exist", 256, 0, yield[ec]);
+ EXPECT_FALSE(ec);
+ EXPECT_EQ("hello", bl.to_str());
+ };
+ spawn::spawn(service, success_cr);
+
+ auto failure_cr = [&] (spawn::yield_context yield) {
+ boost::system::error_code ec;
+ auto bl = librados::async_read(service, io, "noexist", 256, 0, yield[ec]);
+ EXPECT_EQ(boost::system::errc::no_such_file_or_directory, ec);
+ };
+ spawn::spawn(service, failure_cr);
+
+ service.run();
+}
+
+TEST_F(AsioRados, AsyncWriteCallback)
+{
+ boost::asio::io_service service;
+
+ bufferlist bl;
+ bl.append("hello");
+
+ auto success_cb = [&] (boost::system::error_code ec) {
+ EXPECT_FALSE(ec);
+ };
+ librados::async_write(service, io, "exist", bl, bl.length(), 0,
+ success_cb);
+
+ auto failure_cb = [&] (boost::system::error_code ec) {
+ EXPECT_EQ(boost::system::errc::read_only_file_system, ec);
+ };
+ librados::async_write(service, snapio, "exist", bl, bl.length(), 0,
+ failure_cb);
+
+ service.run();
+}
+
+TEST_F(AsioRados, AsyncWriteFuture)
+{
+ boost::asio::io_service service;
+
+ bufferlist bl;
+ bl.append("hello");
+
+ auto f1 = librados::async_write(service, io, "exist", bl, bl.length(), 0,
+ boost::asio::use_future);
+ auto f2 = librados::async_write(service, snapio, "exist", bl, bl.length(), 0,
+ boost::asio::use_future);
+
+ service.run();
+
+ EXPECT_NO_THROW(f1.get());
+ EXPECT_THROW(f2.get(), boost::system::system_error);
+}
+
+TEST_F(AsioRados, AsyncWriteYield)
+{
+ boost::asio::io_service service;
+
+ bufferlist bl;
+ bl.append("hello");
+
+ auto success_cr = [&] (spawn::yield_context yield) {
+ boost::system::error_code ec;
+ librados::async_write(service, io, "exist", bl, bl.length(), 0,
+ yield[ec]);
+ EXPECT_FALSE(ec);
+ EXPECT_EQ("hello", bl.to_str());
+ };
+ spawn::spawn(service, success_cr);
+
+ auto failure_cr = [&] (spawn::yield_context yield) {
+ boost::system::error_code ec;
+ librados::async_write(service, snapio, "exist", bl, bl.length(), 0,
+ yield[ec]);
+ EXPECT_EQ(boost::system::errc::read_only_file_system, ec);
+ };
+ spawn::spawn(service, failure_cr);
+
+ service.run();
+}
+
+TEST_F(AsioRados, AsyncReadOperationCallback)
+{
+ boost::asio::io_service service;
+ {
+ librados::ObjectReadOperation op;
+ op.read(0, 0, nullptr, nullptr);
+ auto success_cb = [&] (boost::system::error_code ec, bufferlist bl) {
+ EXPECT_FALSE(ec);
+ EXPECT_EQ("hello", bl.to_str());
+ };
+ librados::async_operate(service, io, "exist", &op, 0, success_cb);
+ }
+ {
+ librados::ObjectReadOperation op;
+ op.read(0, 0, nullptr, nullptr);
+ auto failure_cb = [&] (boost::system::error_code ec, bufferlist bl) {
+ EXPECT_EQ(boost::system::errc::no_such_file_or_directory, ec);
+ };
+ librados::async_operate(service, io, "noexist", &op, 0, failure_cb);
+ }
+ service.run();
+}
+
+TEST_F(AsioRados, AsyncReadOperationFuture)
+{
+ boost::asio::io_service service;
+ std::future<bufferlist> f1;
+ {
+ librados::ObjectReadOperation op;
+ op.read(0, 0, nullptr, nullptr);
+ f1 = librados::async_operate(service, io, "exist", &op, 0,
+ boost::asio::use_future);
+ }
+ std::future<bufferlist> f2;
+ {
+ librados::ObjectReadOperation op;
+ op.read(0, 0, nullptr, nullptr);
+ f2 = librados::async_operate(service, io, "noexist", &op, 0,
+ boost::asio::use_future);
+ }
+ service.run();
+
+ EXPECT_NO_THROW({
+ auto bl = f1.get();
+ EXPECT_EQ("hello", bl.to_str());
+ });
+ EXPECT_THROW(f2.get(), boost::system::system_error);
+}
+
+TEST_F(AsioRados, AsyncReadOperationYield)
+{
+ boost::asio::io_service service;
+
+ auto success_cr = [&] (spawn::yield_context yield) {
+ librados::ObjectReadOperation op;
+ op.read(0, 0, nullptr, nullptr);
+ boost::system::error_code ec;
+ auto bl = librados::async_operate(service, io, "exist", &op, 0,
+ yield[ec]);
+ EXPECT_FALSE(ec);
+ EXPECT_EQ("hello", bl.to_str());
+ };
+ spawn::spawn(service, success_cr);
+
+ auto failure_cr = [&] (spawn::yield_context yield) {
+ librados::ObjectReadOperation op;
+ op.read(0, 0, nullptr, nullptr);
+ boost::system::error_code ec;
+ auto bl = librados::async_operate(service, io, "noexist", &op, 0,
+ yield[ec]);
+ EXPECT_EQ(boost::system::errc::no_such_file_or_directory, ec);
+ };
+ spawn::spawn(service, failure_cr);
+
+ service.run();
+}
+
+TEST_F(AsioRados, AsyncWriteOperationCallback)
+{
+ boost::asio::io_service service;
+
+ bufferlist bl;
+ bl.append("hello");
+
+ {
+ librados::ObjectWriteOperation op;
+ op.write_full(bl);
+ auto success_cb = [&] (boost::system::error_code ec) {
+ EXPECT_FALSE(ec);
+ };
+ librados::async_operate(service, io, "exist", &op, 0, success_cb);
+ }
+ {
+ librados::ObjectWriteOperation op;
+ op.write_full(bl);
+ auto failure_cb = [&] (boost::system::error_code ec) {
+ EXPECT_EQ(boost::system::errc::read_only_file_system, ec);
+ };
+ librados::async_operate(service, snapio, "exist", &op, 0, failure_cb);
+ }
+ service.run();
+}
+
+TEST_F(AsioRados, AsyncWriteOperationFuture)
+{
+ boost::asio::io_service service;
+
+ bufferlist bl;
+ bl.append("hello");
+
+ std::future<void> f1;
+ {
+ librados::ObjectWriteOperation op;
+ op.write_full(bl);
+ f1 = librados::async_operate(service, io, "exist", &op, 0,
+ boost::asio::use_future);
+ }
+ std::future<void> f2;
+ {
+ librados::ObjectWriteOperation op;
+ op.write_full(bl);
+ f2 = librados::async_operate(service, snapio, "exist", &op, 0,
+ boost::asio::use_future);
+ }
+ service.run();
+
+ EXPECT_NO_THROW(f1.get());
+ EXPECT_THROW(f2.get(), boost::system::system_error);
+}
+
+TEST_F(AsioRados, AsyncWriteOperationYield)
+{
+ boost::asio::io_service service;
+
+ bufferlist bl;
+ bl.append("hello");
+
+ auto success_cr = [&] (spawn::yield_context yield) {
+ librados::ObjectWriteOperation op;
+ op.write_full(bl);
+ boost::system::error_code ec;
+ librados::async_operate(service, io, "exist", &op, 0, yield[ec]);
+ EXPECT_FALSE(ec);
+ };
+ spawn::spawn(service, success_cr);
+
+ auto failure_cr = [&] (spawn::yield_context yield) {
+ librados::ObjectWriteOperation op;
+ op.write_full(bl);
+ boost::system::error_code ec;
+ librados::async_operate(service, snapio, "exist", &op, 0, yield[ec]);
+ EXPECT_EQ(boost::system::errc::read_only_file_system, ec);
+ };
+ spawn::spawn(service, failure_cr);
+
+ service.run();
+}
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(cct.get());
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librados/c_read_operations.cc b/src/test/librados/c_read_operations.cc
new file mode 100644
index 000000000..a8bad0748
--- /dev/null
+++ b/src/test/librados/c_read_operations.cc
@@ -0,0 +1,895 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// Tests for the C API coverage of atomic read operations
+
+#include <cstring> // For memcpy
+#include <errno.h>
+#include <string>
+
+#include "include/buffer.h"
+#include "include/denc.h"
+#include "include/err.h"
+#include "include/rados/librados.h"
+#include "include/rbd/features.h" // For RBD_FEATURES_ALL
+#include "include/scope_guard.h"
+#include "test/librados/TestCase.h"
+#include "test/librados/test.h"
+
+const char *data = "testdata";
+const char *obj = "testobj";
+const size_t len = strlen(data);
+
+class CReadOpsTest : public RadosTest {
+protected:
+ void write_object() {
+ // Create an object and write to it
+ ASSERT_EQ(0, rados_write(ioctx, obj, data, len, 0));
+ }
+ void remove_object() {
+ ASSERT_EQ(0, rados_remove(ioctx, obj));
+ }
+ int cmp_xattr(const char *xattr, const char *value, size_t value_len,
+ uint8_t cmp_op)
+ {
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_cmpxattr(op, xattr, cmp_op, value, value_len);
+ int r = rados_read_op_operate(op, ioctx, obj, 0);
+ rados_release_read_op(op);
+ return r;
+ }
+
+ void fetch_and_verify_omap_vals(char const* const* keys,
+ char const* const* vals,
+ const size_t *lens,
+ size_t len)
+ {
+ rados_omap_iter_t iter_vals, iter_keys, iter_vals_by_key;
+ int r_vals, r_keys, r_vals_by_key;
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_omap_get_vals2(op, NULL, NULL, 100, &iter_vals, NULL, &r_vals);
+ rados_read_op_omap_get_keys2(op, NULL, 100, &iter_keys, NULL, &r_keys);
+ rados_read_op_omap_get_vals_by_keys(op, keys, len,
+ &iter_vals_by_key, &r_vals_by_key);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+ ASSERT_EQ(0, r_vals);
+ ASSERT_EQ(0, r_keys);
+ ASSERT_EQ(0, r_vals_by_key);
+
+ const char *zeros[len];
+ size_t zero_lens[len];
+ memset(zeros, 0, sizeof(zeros));
+ memset(zero_lens, 0, sizeof(zero_lens));
+ compare_omap_vals(keys, vals, lens, len, iter_vals);
+ compare_omap_vals(keys, zeros, zero_lens, len, iter_keys);
+ compare_omap_vals(keys, vals, lens, len, iter_vals_by_key);
+ }
+
+ void compare_omap_vals(char const* const* keys,
+ char const* const* vals,
+ const size_t *lens,
+ size_t len,
+ rados_omap_iter_t iter)
+ {
+ size_t i = 0;
+ char *key = NULL;
+ char *val = NULL;
+ size_t val_len = 0;
+ ASSERT_EQ(len, rados_omap_iter_size(iter));
+ while (i < len) {
+ ASSERT_EQ(0, rados_omap_get_next(iter, &key, &val, &val_len));
+ if (val_len == 0 && key == NULL && val == NULL)
+ break;
+ if (key)
+ EXPECT_EQ(std::string(keys[i]), std::string(key));
+ else
+ EXPECT_EQ(keys[i], key);
+ ASSERT_EQ(0, memcmp(vals[i], val, val_len));
+ ASSERT_EQ(lens[i], val_len);
+ ++i;
+ }
+ ASSERT_EQ(i, len);
+ ASSERT_EQ(0, rados_omap_get_next(iter, &key, &val, &val_len));
+ ASSERT_EQ((char*)NULL, key);
+ ASSERT_EQ((char*)NULL, val);
+ ASSERT_EQ(0u, val_len);
+ rados_omap_get_end(iter);
+ }
+
+ // these two used to test omap funcs that accept length for both keys and vals
+ void fetch_and_verify_omap_vals2(char const* const* keys,
+ char const* const* vals,
+ const size_t *keylens,
+ const size_t *vallens,
+ size_t len)
+ {
+ rados_omap_iter_t iter_vals_by_key;
+ int r_vals_by_key;
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_omap_get_vals_by_keys2(op, keys, len, keylens,
+ &iter_vals_by_key, &r_vals_by_key);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+ ASSERT_EQ(0, r_vals_by_key);
+
+ compare_omap_vals2(keys, vals, keylens, vallens, len, iter_vals_by_key);
+ }
+
+ void compare_omap_vals2(char const* const* keys,
+ char const* const* vals,
+ const size_t *keylens,
+ const size_t *vallens,
+ size_t len,
+ rados_omap_iter_t iter)
+ {
+ size_t i = 0;
+ char *key = NULL;
+ char *val = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ ASSERT_EQ(len, rados_omap_iter_size(iter));
+ while (i < len) {
+ ASSERT_EQ(0, rados_omap_get_next2(iter, &key, &val, &key_len, &val_len));
+ if (key_len == 0 && val_len == 0 && key == NULL && val == NULL)
+ break;
+ if (key)
+ EXPECT_EQ(std::string(keys[i], keylens[i]), std::string(key, key_len));
+ else
+ EXPECT_EQ(keys[i], key);
+ ASSERT_EQ(val_len, vallens[i]);
+ ASSERT_EQ(key_len, keylens[i]);
+ ASSERT_EQ(0, memcmp(vals[i], val, val_len));
+ ++i;
+ }
+ ASSERT_EQ(i, len);
+ ASSERT_EQ(0, rados_omap_get_next2(iter, &key, &val, &key_len, &val_len));
+ ASSERT_EQ((char*)NULL, key);
+ ASSERT_EQ((char*)NULL, val);
+ ASSERT_EQ(0u, key_len);
+ ASSERT_EQ(0u, val_len);
+ rados_omap_get_end(iter);
+ }
+
+ void compare_xattrs(char const* const* keys,
+ char const* const* vals,
+ const size_t *lens,
+ size_t len,
+ rados_xattrs_iter_t iter)
+ {
+ size_t i = 0;
+ char *key = NULL;
+ char *val = NULL;
+ size_t val_len = 0;
+ while (i < len) {
+ ASSERT_EQ(0, rados_getxattrs_next(iter, (const char**) &key,
+ (const char**) &val, &val_len));
+ if (key == NULL)
+ break;
+ EXPECT_EQ(std::string(keys[i]), std::string(key));
+ if (val != NULL) {
+ EXPECT_EQ(0, memcmp(vals[i], val, val_len));
+ }
+ EXPECT_EQ(lens[i], val_len);
+ ++i;
+ }
+ ASSERT_EQ(i, len);
+ ASSERT_EQ(0, rados_getxattrs_next(iter, (const char**)&key,
+ (const char**)&val, &val_len));
+ ASSERT_EQ((char*)NULL, key);
+ ASSERT_EQ((char*)NULL, val);
+ ASSERT_EQ(0u, val_len);
+ rados_getxattrs_end(iter);
+ }
+};
+
+TEST_F(CReadOpsTest, NewDelete) {
+ rados_read_op_t op = rados_create_read_op();
+ ASSERT_TRUE(op);
+ rados_release_read_op(op);
+}
+
+TEST_F(CReadOpsTest, SetOpFlags) {
+ write_object();
+
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ char *out = NULL;
+ int rval = 0;
+ rados_read_op_exec(op, "rbd", "get_id", NULL, 0, &out,
+ &bytes_read, &rval);
+ rados_read_op_set_flags(op, LIBRADOS_OP_FLAG_FAILOK);
+ EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ EXPECT_EQ(-EIO, rval);
+ EXPECT_EQ(0u, bytes_read);
+ EXPECT_EQ((char*)NULL, out);
+ rados_release_read_op(op);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, AssertExists) {
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_assert_exists(op);
+
+ ASSERT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ op = rados_create_read_op();
+ rados_read_op_assert_exists(op);
+
+ rados_completion_t completion;
+ ASSERT_EQ(0, rados_aio_create_completion(NULL, NULL, NULL, &completion));
+ auto sg = make_scope_guard([&] { rados_aio_release(completion); });
+ ASSERT_EQ(0, rados_aio_read_op_operate(op, ioctx, completion, obj, 0));
+ rados_aio_wait_for_complete(completion);
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(completion));
+ rados_release_read_op(op);
+
+ write_object();
+
+ op = rados_create_read_op();
+ rados_read_op_assert_exists(op);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, AssertVersion) {
+ write_object();
+ // Write to the object a second time to guarantee that its
+ // version number is greater than 0
+ write_object();
+ uint64_t v = rados_get_last_version(ioctx);
+
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_assert_version(op, v+1);
+ ASSERT_EQ(-EOVERFLOW, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ op = rados_create_read_op();
+ rados_read_op_assert_version(op, v-1);
+ ASSERT_EQ(-ERANGE, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ op = rados_create_read_op();
+ rados_read_op_assert_version(op, v);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, CmpXattr) {
+ write_object();
+
+ char buf[len];
+ memset(buf, 0xcc, sizeof(buf));
+
+ const char *xattr = "test";
+ rados_setxattr(ioctx, obj, xattr, buf, sizeof(buf));
+
+ // equal value
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_EQ));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_NE));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GT));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GTE));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LT));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LTE));
+
+ // < value
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_EQ));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_NE));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_GT));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_GTE));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_LT));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf) - 1, LIBRADOS_CMPXATTR_OP_LTE));
+
+ // > value
+ memset(buf, 0xcd, sizeof(buf));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_EQ));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_NE));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GT));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_GTE));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LT));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, sizeof(buf), LIBRADOS_CMPXATTR_OP_LTE));
+
+ // check that null bytes are compared correctly
+ rados_setxattr(ioctx, obj, xattr, "\0\0", 2);
+ buf[0] = '\0';
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_EQ));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_NE));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GT));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GTE));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LT));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LTE));
+
+ buf[1] = '\0';
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_EQ));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_NE));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GT));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_GTE));
+ EXPECT_EQ(-ECANCELED, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LT));
+ EXPECT_EQ(1, cmp_xattr(xattr, buf, 2, LIBRADOS_CMPXATTR_OP_LTE));
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, Read) {
+ write_object();
+
+ char buf[len];
+ // check that using read_ops returns the same data with
+ // or without bytes_read and rval out params
+ {
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_read(op, 0, len, buf, NULL, NULL);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ int rval;
+ rados_read_op_read(op, 0, len, buf, NULL, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ rados_read_op_read(op, 0, len, buf, &bytes_read, NULL);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(len, bytes_read);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ int rval;
+ rados_read_op_read(op, 0, len, buf, &bytes_read, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(len, bytes_read);
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ int rval;
+ rados_read_op_read(op, 0, len, buf, &bytes_read, &rval);
+ rados_read_op_set_flags(op, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(len, bytes_read);
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, Checksum) {
+ write_object();
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ ceph_le64 init_value(-1);
+ rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_XXHASH64,
+ reinterpret_cast<char *>(&init_value),
+ sizeof(init_value), 0, len, 0, NULL, 0, NULL);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+ }
+
+ {
+ ceph_le32 init_value(-1);
+ ceph_le32 crc[2];
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_CRC32C,
+ reinterpret_cast<char *>(&init_value),
+ sizeof(init_value), 0, len, 0,
+ reinterpret_cast<char *>(&crc), sizeof(crc),
+ nullptr);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(1U, crc[0]);
+ uint32_t expected_crc = ceph_crc32c(
+ -1, reinterpret_cast<const uint8_t*>(data), static_cast<uint32_t>(len));
+ ASSERT_EQ(expected_crc, crc[1]);
+ rados_release_read_op(op);
+ }
+
+ {
+ ceph_le32 init_value(-1);
+ int rval;
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_XXHASH32,
+ reinterpret_cast<char *>(&init_value),
+ sizeof(init_value), 0, len, 0, nullptr, 0, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(0, rval);
+ rados_release_read_op(op);
+ }
+
+ {
+ ceph_le32 init_value(-1);
+ ceph_le32 crc[3];
+ int rval;
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_CRC32C,
+ reinterpret_cast<char *>(&init_value),
+ sizeof(init_value), 0, len, 4,
+ reinterpret_cast<char *>(&crc), sizeof(crc), &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(2U, crc[0]);
+ uint32_t expected_crc[2];
+ expected_crc[0] = ceph_crc32c(
+ -1, reinterpret_cast<const uint8_t*>(data), 4U);
+ expected_crc[1] = ceph_crc32c(
+ -1, reinterpret_cast<const uint8_t*>(data + 4), 4U);
+ ASSERT_EQ(expected_crc[0], crc[1]);
+ ASSERT_EQ(expected_crc[1], crc[2]);
+ ASSERT_EQ(0, rval);
+ rados_release_read_op(op);
+ }
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, RWOrderedRead) {
+ write_object();
+
+ char buf[len];
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ int rval;
+ rados_read_op_read(op, 0, len, buf, &bytes_read, &rval);
+ rados_read_op_set_flags(op, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj,
+ LIBRADOS_OPERATION_ORDER_READS_WRITES));
+ ASSERT_EQ(len, bytes_read);
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, ShortRead) {
+ write_object();
+
+ char buf[len * 2];
+ // check that using read_ops returns the same data with
+ // or without bytes_read and rval out params
+ {
+ rados_read_op_t op = rados_create_read_op();
+ rados_read_op_read(op, 0, len * 2, buf, NULL, NULL);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ int rval;
+ rados_read_op_read(op, 0, len * 2, buf, NULL, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ rados_read_op_read(op, 0, len * 2, buf, &bytes_read, NULL);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(len, bytes_read);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ {
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ int rval;
+ rados_read_op_read(op, 0, len * 2, buf, &bytes_read, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(len, bytes_read);
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ rados_release_read_op(op);
+ }
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, Exec) {
+ // create object so we don't get -ENOENT
+ write_object();
+
+ rados_read_op_t op = rados_create_read_op();
+ ASSERT_TRUE(op);
+ size_t bytes_read = 0;
+ char *out = NULL;
+ int rval = 0;
+ rados_read_op_exec(op, "rbd", "get_all_features", NULL, 0, &out,
+ &bytes_read, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+ EXPECT_EQ(0, rval);
+ EXPECT_TRUE(out);
+ uint64_t features;
+ EXPECT_EQ(sizeof(features), bytes_read);
+ // make sure buffer is at least as long as it claims
+ bufferlist bl;
+ bl.append(out, bytes_read);
+ auto it = bl.cbegin();
+ ceph::decode(features, it);
+ ASSERT_EQ(RBD_FEATURES_ALL, features);
+ rados_buffer_free(out);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, ExecUserBuf) {
+ // create object so we don't get -ENOENT
+ write_object();
+
+ rados_read_op_t op = rados_create_read_op();
+ size_t bytes_read = 0;
+ uint64_t features;
+ char out[sizeof(features)];
+ int rval = 0;
+ rados_read_op_exec_user_buf(op, "rbd", "get_all_features", NULL, 0, out,
+ sizeof(out), &bytes_read, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+ EXPECT_EQ(0, rval);
+ EXPECT_EQ(sizeof(features), bytes_read);
+
+ // buffer too short
+ bytes_read = 1024;
+ op = rados_create_read_op();
+ rados_read_op_exec_user_buf(op, "rbd", "get_all_features", NULL, 0, out,
+ sizeof(features) - 1, &bytes_read, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+ EXPECT_EQ(0u, bytes_read);
+ EXPECT_EQ(-ERANGE, rval);
+
+ // input buffer and no rval or bytes_read
+ op = rados_create_read_op();
+ rados_read_op_exec_user_buf(op, "rbd", "get_all_features", out, sizeof(out),
+ out, sizeof(out), NULL, NULL);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, Stat) {
+ rados_read_op_t op = rados_create_read_op();
+ uint64_t size = 1;
+ int rval = 0;
+ rados_read_op_stat(op, &size, NULL, &rval);
+ EXPECT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0));
+ EXPECT_EQ(-EIO, rval);
+ EXPECT_EQ(1u, size);
+ rados_release_read_op(op);
+
+ time_t ts = 1457129052;
+ rados_write_op_t wop = rados_create_write_op();
+ rados_write_op_write(wop, data, len, 0);
+ ASSERT_EQ(0, rados_write_op_operate(wop, ioctx, obj, &ts, 0));
+ rados_release_write_op(wop);
+
+ time_t ts2;
+ op = rados_create_read_op();
+ rados_read_op_stat(op, &size, &ts2, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ EXPECT_EQ(0, rval);
+ EXPECT_EQ(len, size);
+ EXPECT_EQ(ts2, ts);
+ rados_release_read_op(op);
+
+ op = rados_create_read_op();
+ rados_read_op_stat(op, NULL, NULL, NULL);
+ EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ remove_object();
+
+ op = rados_create_read_op();
+ rados_read_op_stat(op, NULL, NULL, NULL);
+ EXPECT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+}
+
+TEST_F(CReadOpsTest, Stat2) {
+ rados_read_op_t op = rados_create_read_op();
+ uint64_t size = 1;
+ int rval = 0;
+ rados_read_op_stat2(op, &size, NULL, &rval);
+ EXPECT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0));
+ EXPECT_EQ(-EIO, rval);
+ EXPECT_EQ(1u, size);
+ rados_release_read_op(op);
+
+ struct timespec ts;
+ ts.tv_sec = 1457129052;
+ ts.tv_nsec = 123456789;
+ rados_write_op_t wop = rados_create_write_op();
+ rados_write_op_write(wop, data, len, 0);
+ ASSERT_EQ(0, rados_write_op_operate2(wop, ioctx, obj, &ts, 0));
+ rados_release_write_op(wop);
+
+ struct timespec ts2 = {};
+ op = rados_create_read_op();
+ rados_read_op_stat2(op, &size, &ts2, &rval);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ EXPECT_EQ(0, rval);
+ EXPECT_EQ(len, size);
+ EXPECT_EQ(ts2.tv_sec, ts.tv_sec);
+ EXPECT_EQ(ts2.tv_nsec, ts.tv_nsec);
+ rados_release_read_op(op);
+
+ op = rados_create_read_op();
+ rados_read_op_stat2(op, NULL, NULL, NULL);
+ EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ remove_object();
+
+ op = rados_create_read_op();
+ rados_read_op_stat2(op, NULL, NULL, NULL);
+ EXPECT_EQ(-ENOENT, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+}
+
+TEST_F(CReadOpsTest, Omap) {
+ char *keys[] = {(char*)"bar",
+ (char*)"foo",
+ (char*)"test1",
+ (char*)"test2"};
+ char *vals[] = {(char*)"",
+ (char*)"\0",
+ (char*)"abc",
+ (char*)"va\0lue"};
+ size_t lens[] = {0, 1, 3, 6};
+
+ // check for -ENOENT before the object exists and when it exists
+ // with no omap entries
+ rados_omap_iter_t iter_vals;
+ rados_read_op_t rop = rados_create_read_op();
+ rados_read_op_omap_get_vals2(rop, "", "", 10, &iter_vals, NULL, NULL);
+ ASSERT_EQ(-ENOENT, rados_read_op_operate(rop, ioctx, obj, 0));
+ rados_release_read_op(rop);
+ compare_omap_vals(NULL, NULL, NULL, 0, iter_vals);
+
+ write_object();
+
+ fetch_and_verify_omap_vals(NULL, NULL, NULL, 0);
+
+ // write and check for the k/v pairs
+ rados_write_op_t op = rados_create_write_op();
+ rados_write_op_omap_set(op, keys, vals, lens, 4);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ fetch_and_verify_omap_vals(keys, vals, lens, 4);
+
+ rados_omap_iter_t iter_keys;
+ int r_vals = -1, r_keys = -1;
+ rop = rados_create_read_op();
+ rados_read_op_omap_get_vals2(rop, "", "test", 1, &iter_vals, NULL, &r_vals);
+ rados_read_op_omap_get_keys2(rop, "test", 1, &iter_keys, NULL, &r_keys);
+ ASSERT_EQ(0, rados_read_op_operate(rop, ioctx, obj, 0));
+ rados_release_read_op(rop);
+ EXPECT_EQ(0, r_vals);
+ EXPECT_EQ(0, r_keys);
+ EXPECT_EQ(1u, rados_omap_iter_size(iter_vals));
+ EXPECT_EQ(1u, rados_omap_iter_size(iter_keys));
+
+ compare_omap_vals(&keys[2], &vals[2], &lens[2], 1, iter_vals);
+ compare_omap_vals(&keys[2], &vals[0], &lens[0], 1, iter_keys);
+
+ // check omap_cmp finds all expected values
+ rop = rados_create_read_op();
+ int rvals[4];
+ for (int i = 0; i < 4; ++i)
+ rados_read_op_omap_cmp(rop, keys[i], LIBRADOS_CMPXATTR_OP_EQ,
+ vals[i], lens[i], &rvals[i]);
+ EXPECT_EQ(0, rados_read_op_operate(rop, ioctx, obj, 0));
+ rados_release_read_op(rop);
+ for (int i = 0; i < 4; ++i)
+ EXPECT_EQ(0, rvals[i]);
+
+ // try to remove keys with a guard that should fail
+ op = rados_create_write_op();
+ rados_write_op_omap_cmp(op, keys[2], LIBRADOS_CMPXATTR_OP_LT,
+ vals[2], lens[2], &r_vals);
+ rados_write_op_omap_rm_keys(op, keys, 2);
+ EXPECT_EQ(-ECANCELED, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ // see http://tracker.ceph.com/issues/19518
+ //ASSERT_EQ(-ECANCELED, r_vals);
+
+ // verifying the keys are still there, and then remove them
+ op = rados_create_write_op();
+ rados_write_op_omap_cmp(op, keys[0], LIBRADOS_CMPXATTR_OP_EQ,
+ vals[0], lens[0], NULL);
+ rados_write_op_omap_cmp(op, keys[1], LIBRADOS_CMPXATTR_OP_EQ,
+ vals[1], lens[1], NULL);
+ rados_write_op_omap_rm_keys(op, keys, 2);
+ EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ fetch_and_verify_omap_vals(&keys[2], &vals[2], &lens[2], 2);
+
+ // clear the rest and check there are none left
+ op = rados_create_write_op();
+ rados_write_op_omap_clear(op);
+ EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ fetch_and_verify_omap_vals(NULL, NULL, NULL, 0);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, OmapNuls) {
+ char *keys[] = {(char*)"1\0bar",
+ (char*)"2baar\0",
+ (char*)"3baa\0rr"};
+ char *vals[] = {(char*)"_\0var",
+ (char*)"_vaar\0",
+ (char*)"__vaa\0rr"};
+ size_t nklens[] = {5, 6, 7};
+ size_t nvlens[] = {5, 6, 8};
+ const int paircount = 3;
+
+ // check for -ENOENT before the object exists and when it exists
+ // with no omap entries
+ rados_omap_iter_t iter_vals;
+ rados_read_op_t rop = rados_create_read_op();
+ rados_read_op_omap_get_vals2(rop, "", "", 10, &iter_vals, NULL, NULL);
+ ASSERT_EQ(-ENOENT, rados_read_op_operate(rop, ioctx, obj, 0));
+ rados_release_read_op(rop);
+ compare_omap_vals(NULL, NULL, NULL, 0, iter_vals);
+
+ write_object();
+
+ fetch_and_verify_omap_vals(NULL, NULL, NULL, 0);
+
+ // write and check for the k/v pairs
+ rados_write_op_t op = rados_create_write_op();
+ rados_write_op_omap_set2(op, keys, vals, nklens, nvlens, paircount);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ fetch_and_verify_omap_vals2(keys, vals, nklens, nvlens, paircount);
+
+ // check omap_cmp finds all expected values
+ rop = rados_create_read_op();
+ int rvals[4];
+ for (int i = 0; i < paircount; ++i)
+ rados_read_op_omap_cmp2(rop, keys[i], LIBRADOS_CMPXATTR_OP_EQ,
+ vals[i], nklens[i], nvlens[i], &rvals[i]);
+ EXPECT_EQ(0, rados_read_op_operate(rop, ioctx, obj, 0));
+ rados_release_read_op(rop);
+ for (int i = 0; i < paircount; ++i)
+ EXPECT_EQ(0, rvals[i]);
+
+ // try to remove keys with a guard that should fail
+ int r_vals = -1;
+ op = rados_create_write_op();
+ rados_write_op_omap_cmp2(op, keys[2], LIBRADOS_CMPXATTR_OP_LT,
+ vals[2], nklens[2], nvlens[2], &r_vals);
+ rados_write_op_omap_rm_keys(op, keys, 2);
+ EXPECT_EQ(-ECANCELED, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ // verifying the keys are still there, and then remove them
+ op = rados_create_write_op();
+ rados_write_op_omap_cmp2(op, keys[0], LIBRADOS_CMPXATTR_OP_EQ,
+ vals[0], nklens[0], nvlens[0], NULL);
+ rados_write_op_omap_cmp2(op, keys[1], LIBRADOS_CMPXATTR_OP_EQ,
+ vals[1], nklens[1], nvlens[1], NULL);
+ rados_write_op_omap_rm_keys2(op, keys, nklens, 2);
+ EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ fetch_and_verify_omap_vals2(&keys[2], &vals[2], &nklens[2], &nvlens[2], 1);
+
+ // clear the rest and check there are none left
+ op = rados_create_write_op();
+ rados_write_op_omap_clear(op);
+ EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+ rados_release_write_op(op);
+
+ fetch_and_verify_omap_vals(NULL, NULL, NULL, 0);
+
+ remove_object();
+}
+TEST_F(CReadOpsTest, GetXattrs) {
+ write_object();
+
+ char *keys[] = {(char*)"bar",
+ (char*)"foo",
+ (char*)"test1",
+ (char*)"test2"};
+ char *vals[] = {(char*)"",
+ (char*)"\0",
+ (char*)"abc",
+ (char*)"va\0lue"};
+ size_t lens[] = {0, 1, 3, 6};
+
+ int rval = 1;
+ rados_read_op_t op = rados_create_read_op();
+ rados_xattrs_iter_t it;
+ rados_read_op_getxattrs(op, &it, &rval);
+ EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ EXPECT_EQ(0, rval);
+ rados_release_read_op(op);
+ compare_xattrs(keys, vals, lens, 0, it);
+
+ for (int i = 0; i < 4; ++i)
+ rados_setxattr(ioctx, obj, keys[i], vals[i], lens[i]);
+
+ rval = 1;
+ op = rados_create_read_op();
+ rados_read_op_getxattrs(op, &it, &rval);
+ EXPECT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ EXPECT_EQ(0, rval);
+ rados_release_read_op(op);
+ compare_xattrs(keys, vals, lens, 4, it);
+
+ remove_object();
+}
+
+TEST_F(CReadOpsTest, CmpExt) {
+ char buf[len];
+ size_t bytes_read = 0;
+ int cmpext_val = 0;
+ int read_val = 0;
+
+ write_object();
+
+ // cmpext with match should ensure that the following read is successful
+ rados_read_op_t op = rados_create_read_op();
+ ASSERT_TRUE(op);
+ // @obj, @data and @len correspond to object initialised by write_object()
+ rados_read_op_cmpext(op, data, len, 0, &cmpext_val);
+ rados_read_op_read(op, 0, len, buf, &bytes_read, &read_val);
+ ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+ ASSERT_EQ(len, bytes_read);
+ ASSERT_EQ(0, memcmp(data, buf, len));
+ ASSERT_EQ(cmpext_val, 0);
+ rados_release_read_op(op);
+
+ // cmpext with mismatch should fail and fill mismatch_buf accordingly
+ memset(buf, 0, sizeof(buf));
+ bytes_read = 0;
+ cmpext_val = 0;
+ read_val = 0;
+ op = rados_create_read_op();
+ ASSERT_TRUE(op);
+ // @obj, @data and @len correspond to object initialised by write_object()
+ rados_read_op_cmpext(op, "mismatch", strlen("mismatch"), 0, &cmpext_val);
+ rados_read_op_read(op, 0, len, buf, &bytes_read, &read_val);
+ ASSERT_EQ(-MAX_ERRNO, rados_read_op_operate(op, ioctx, obj, 0));
+ rados_release_read_op(op);
+
+ ASSERT_EQ(-MAX_ERRNO, cmpext_val);
+
+ remove_object();
+}
diff --git a/src/test/librados/c_write_operations.cc b/src/test/librados/c_write_operations.cc
new file mode 100644
index 000000000..558d0942f
--- /dev/null
+++ b/src/test/librados/c_write_operations.cc
@@ -0,0 +1,279 @@
+// Tests for the C API coverage of atomic write operations
+
+#include <errno.h>
+#include "gtest/gtest.h"
+#include "include/err.h"
+#include "include/rados/librados.h"
+#include "test/librados/test.h"
+
+TEST(LibradosCWriteOps, NewDelete) {
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_release_write_op(op);
+}
+
+TEST(LibRadosCWriteOps, assertExists) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_assert_exists(op);
+
+ // -2, ENOENT
+ ASSERT_EQ(-2, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ rados_write_op_t op2 = rados_create_write_op();
+ ASSERT_TRUE(op2);
+ rados_write_op_assert_exists(op2);
+
+ rados_completion_t completion;
+ ASSERT_EQ(0, rados_aio_create_completion(NULL, NULL, NULL, &completion));
+ ASSERT_EQ(0, rados_aio_write_op_operate(op2, ioctx, completion, "test", NULL, 0));
+ rados_aio_wait_for_complete(completion);
+ ASSERT_EQ(-2, rados_aio_get_return_value(completion));
+ rados_aio_release(completion);
+
+ rados_ioctx_destroy(ioctx);
+ rados_release_write_op(op2);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, WriteOpAssertVersion) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ // Write to the object a second time to guarantee that its
+ // version number is greater than 0
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_write_full(op, "hi", 2);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ uint64_t v = rados_get_last_version(ioctx);
+
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_assert_version(op, v+1);
+ ASSERT_EQ(-EOVERFLOW, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_assert_version(op, v-1);
+ ASSERT_EQ(-ERANGE, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_assert_version(op, v);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, Xattrs) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ // Create an object with an xattr
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+ rados_write_op_setxattr(op, "key", "value", 5);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ // Check that xattr exists, if it does, delete it.
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_IDEMPOTENT, NULL);
+ rados_write_op_cmpxattr(op, "key", LIBRADOS_CMPXATTR_OP_EQ, "value", 5);
+ rados_write_op_rmxattr(op, "key");
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ // Check the xattr exits, if it does, add it again (will fail) with -125
+ // (ECANCELED)
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_cmpxattr(op, "key", LIBRADOS_CMPXATTR_OP_EQ, "value", 5);
+ rados_write_op_setxattr(op, "key", "value", 5);
+ ASSERT_EQ(-ECANCELED, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+
+ rados_release_write_op(op);
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, Write) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ // Create an object, write and write full to it
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+ rados_write_op_write(op, "four", 4, 0);
+ rados_write_op_write_full(op, "hi", 2);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ char hi[4];
+ ASSERT_EQ(2, rados_read(ioctx, "test", hi, 4, 0));
+ rados_release_write_op(op);
+
+ //create write op with iohint
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_write_full(op, "ceph", 4);
+ rados_write_op_set_flags(op, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ ASSERT_EQ(4, rados_read(ioctx, "test", hi, 4, 0));
+ rados_release_write_op(op);
+
+ // Truncate and append
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_truncate(op, 1);
+ rados_write_op_append(op, "hi", 2);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ ASSERT_EQ(3, rados_read(ioctx, "test", hi, 4, 0));
+ rados_release_write_op(op);
+
+ // zero and remove
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_zero(op, 0, 3);
+ rados_write_op_remove(op);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ // ENOENT
+ ASSERT_EQ(-2, rados_read(ioctx, "test", hi, 4, 0));
+ rados_release_write_op(op);
+
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, Exec) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ int rval = 1;
+ rados_write_op_t op = rados_create_write_op();
+ rados_write_op_exec(op, "hello", "record_hello", "test", 4, &rval);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+ ASSERT_EQ(0, rval);
+
+ char hi[100];
+ ASSERT_EQ(12, rados_read(ioctx, "test", hi, 100, 0));
+ hi[12] = '\0';
+ ASSERT_EQ(0, strcmp("Hello, test!", hi));
+
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, WriteSame) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ // Create an object, write to it using writesame
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+ rados_write_op_writesame(op, "four", 4, 4 * 4, 0);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ char hi[4 * 4];
+ ASSERT_EQ(sizeof(hi), static_cast<std::size_t>(
+ rados_read(ioctx, "test", hi,sizeof(hi), 0)));
+ rados_release_write_op(op);
+ ASSERT_EQ(0, memcmp("fourfourfourfour", hi, sizeof(hi)));
+
+ // cleanup
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_remove(op);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, CmpExt) {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+ // create an object, write to it using writesame
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+ rados_write_op_write(op, "four", 4, 0);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ rados_release_write_op(op);
+ char hi[4];
+ ASSERT_EQ(sizeof(hi), static_cast<std::size_t>(rados_read(ioctx, "test", hi, sizeof(hi), 0)));
+ ASSERT_EQ(0, memcmp("four", hi, sizeof(hi)));
+
+ // compare and overwrite on (expected) match
+ int val = 0;
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_cmpext(op, "four", 4, 0, &val);
+ rados_write_op_write(op, "five", 4, 0);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+ ASSERT_EQ(0, val);
+ rados_release_write_op(op);
+ ASSERT_EQ(sizeof(hi), static_cast<std::size_t>(rados_read(ioctx, "test", hi, sizeof(hi), 0)));
+ ASSERT_EQ(0, memcmp("five", hi, sizeof(hi)));
+
+ // compare and bail before write due to mismatch
+ val = 0;
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_cmpext(op, "four", 4, 0, &val);
+ rados_write_op_write(op, "six ", 4, 0);
+ ASSERT_EQ(-MAX_ERRNO - 1, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+
+ ASSERT_EQ(-MAX_ERRNO - 1, val);
+
+ // cleanup
+ op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_remove(op);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL, 0));
+
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
diff --git a/src/test/librados/cls.cc b/src/test/librados/cls.cc
new file mode 100644
index 000000000..c4f24954d
--- /dev/null
+++ b/src/test/librados/cls.cc
@@ -0,0 +1,36 @@
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "test/librados/test_cxx.h"
+
+using namespace librados;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+TEST(LibRadosCls, DNE) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ // create an object
+ string oid = "foo";
+ bufferlist bl;
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ // call a bogus class
+ ASSERT_EQ(-EOPNOTSUPP, ioctx.exec(oid, "doesnotexistasdfasdf", "method", bl, bl));
+
+ // call a bogus method on existent class
+ ASSERT_EQ(-EOPNOTSUPP, ioctx.exec(oid, "lock", "doesnotexistasdfasdfasdf", bl, bl));
+
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
diff --git a/src/test/librados/cls_remote_reads.cc b/src/test/librados/cls_remote_reads.cc
new file mode 100644
index 000000000..4256c072f
--- /dev/null
+++ b/src/test/librados/cls_remote_reads.cc
@@ -0,0 +1,55 @@
+#include <set>
+#include <string>
+
+#include "common/ceph_json.h"
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+
+#include "crimson_utils.h"
+
+using namespace librados;
+
+TEST(ClsTestRemoteReads, TestGather) {
+ SKIP_IF_CRIMSON();
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist in, out;
+ int object_size = 4096;
+ char buf[object_size];
+ memset(buf, 1, sizeof(buf));
+
+ // create source objects from which data are gathered
+ in.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write_full("src_object.1", in));
+ in.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write_full("src_object.2", in));
+ in.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write_full("src_object.3", in));
+
+ // construct JSON request passed to "test_gather" method, and in turn, to "test_read" method
+ JSONFormatter *formatter = new JSONFormatter(true);
+ formatter->open_object_section("foo");
+ std::set<std::string> src_objects;
+ src_objects.insert("src_object.1");
+ src_objects.insert("src_object.2");
+ src_objects.insert("src_object.3");
+ encode_json("src_objects", src_objects, formatter);
+ encode_json("cls", "test_remote_reads", formatter);
+ encode_json("method", "test_read", formatter);
+ encode_json("pool", pool_name, formatter);
+ formatter->close_section();
+ in.clear();
+ formatter->flush(in);
+
+ // create target object by combining data gathered from source objects using "test_read" method
+ ASSERT_EQ(0, ioctx.exec("tgt_object", "test_remote_reads", "test_gather", in, out));
+
+ // read target object and check its size
+ ASSERT_EQ(3*object_size, ioctx.read("tgt_object", out, 0, 0));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
diff --git a/src/test/librados/cmd.cc b/src/test/librados/cmd.cc
new file mode 100644
index 000000000..1d110f73b
--- /dev/null
+++ b/src/test/librados/cmd.cc
@@ -0,0 +1,229 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "test/librados/test.h"
+
+#include "gtest/gtest.h"
+#include <errno.h>
+#include <condition_variable>
+#include <map>
+#include <sstream>
+#include <string>
+
+using std::cout;
+using std::list;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+TEST(LibRadosCmd, MonDescribe) {
+ rados_t cluster;
+ ASSERT_EQ("", connect_cluster(&cluster));
+
+ char *buf, *st;
+ size_t buflen, stlen;
+ char *cmd[2];
+
+ cmd[1] = NULL;
+
+ cmd[0] = (char *)"{\"prefix\":\"get_command_descriptions\"}";
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ ASSERT_LT(0u, buflen);
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"get_command_descriptions";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"asdfqwer";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "{}", 2, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "{}", 2, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"{}";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"{\"abc\":\"something\"}";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"{\"prefix\":\"\"}";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"{\"prefix\":\" \"}";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"{\"prefix\":\";;;,,,;;,,\"}";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"{\"prefix\":\"extra command\"}";
+ ASSERT_EQ(-EINVAL, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ cmd[0] = (char *)"{\"prefix\":\"quorum_status\"}";
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ ASSERT_LT(0u, buflen);
+ //ASSERT_LT(0u, stlen);
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+ rados_shutdown(cluster);
+}
+
+TEST(LibRadosCmd, OSDCmd) {
+ rados_t cluster;
+ ASSERT_EQ("", connect_cluster(&cluster));
+ int r;
+ char *buf, *st;
+ size_t buflen, stlen;
+ char *cmd[2];
+ cmd[1] = NULL;
+
+ // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
+ cmd[0] = (char *)"asdfasdf";
+ r = rados_osd_command(cluster, 0, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen);
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+ ASSERT_TRUE(r == -22 || r == -ENXIO);
+ cmd[0] = (char *)"version";
+ r = rados_osd_command(cluster, 0, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen);
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+ ASSERT_TRUE(r == -22 || r == -ENXIO);
+ cmd[0] = (char *)"{\"prefix\":\"version\"}";
+ r = rados_osd_command(cluster, 0, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen);
+ ASSERT_TRUE((r == 0 && buflen > 0) || (r == -ENXIO && buflen == 0));
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+ rados_shutdown(cluster);
+}
+
+TEST(LibRadosCmd, PGCmd) {
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+
+ char *buf, *st;
+ size_t buflen, stlen;
+ char *cmd[2];
+ cmd[1] = NULL;
+
+ int64_t poolid = rados_pool_lookup(cluster, pool_name.c_str());
+ ASSERT_LT(0, poolid);
+
+ string pgid = stringify(poolid) + ".0";
+
+ cmd[0] = (char *)"asdfasdf";
+ // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
+ int r = rados_pg_command(cluster, pgid.c_str(), (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen);
+ ASSERT_TRUE(r == -22 || r == -ENXIO);
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ // make sure the pg exists on the osd before we query it
+ rados_ioctx_t io;
+ rados_ioctx_create(cluster, pool_name.c_str(), &io);
+ for (int i=0; i<100; i++) {
+ string oid = "obj" + stringify(i);
+ ASSERT_EQ(-ENOENT, rados_stat(io, oid.c_str(), NULL, NULL));
+ }
+ rados_ioctx_destroy(io);
+
+ string qstr = "{\"prefix\":\"pg\", \"cmd\":\"query\", \"pgid\":\"" + pgid + "\"}";
+ cmd[0] = (char *)qstr.c_str();
+ // note: tolerate ENOENT/ENXIO here if hte osd is thrashing out underneath us
+ r = rados_pg_command(cluster, pgid.c_str(), (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen);
+ ASSERT_TRUE(r == 0 || r == -ENOENT || r == -ENXIO);
+
+ ASSERT_LT(0u, buflen);
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+struct Log {
+ list<string> log;
+ std::condition_variable cond;
+ std::mutex lock;
+
+ bool contains(const string& str) {
+ std::lock_guard<std::mutex> l(lock);
+ for (list<string>::iterator p = log.begin(); p != log.end(); ++p) {
+ if (p->find(str) != std::string::npos)
+ return true;
+ }
+ return false;
+ }
+};
+
+void log_cb(void *arg,
+ const char *line,
+ const char *who, uint64_t stampsec, uint64_t stamp_nsec,
+ uint64_t seq, const char *level,
+ const char *msg) {
+ Log *l = static_cast<Log *>(arg);
+ std::lock_guard<std::mutex> locker(l->lock);
+ l->log.push_back(line);
+ l->cond.notify_all();
+ cout << "got: " << line << std::endl;
+}
+
+TEST(LibRadosCmd, WatchLog) {
+ rados_t cluster;
+ ASSERT_EQ("", connect_cluster(&cluster));
+ char *buf, *st;
+ char *cmd[2];
+ cmd[1] = NULL;
+ size_t buflen, stlen;
+ Log l;
+
+ ASSERT_EQ(0, rados_monitor_log(cluster, "info", log_cb, &l));
+ cmd[0] = (char *)"{\"prefix\":\"log\", \"logtext\":[\"onexx\"]}";
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ for (int i=0; !l.contains("onexx"); i++) {
+ ASSERT_TRUE(i<100);
+ sleep(1);
+ }
+ ASSERT_TRUE(l.contains("onexx"));
+
+ cmd[0] = (char *)"{\"prefix\":\"log\", \"logtext\":[\"twoxx\"]}";
+ ASSERT_EQ(0, rados_monitor_log(cluster, "err", log_cb, &l));
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ sleep(2);
+ ASSERT_FALSE(l.contains("twoxx"));
+
+ ASSERT_EQ(0, rados_monitor_log(cluster, "info", log_cb, &l));
+ cmd[0] = (char *)"{\"prefix\":\"log\", \"logtext\":[\"threexx\"]}";
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ for (int i=0; !l.contains("threexx"); i++) {
+ ASSERT_TRUE(i<100);
+ sleep(1);
+ }
+
+ ASSERT_EQ(0, rados_monitor_log(cluster, "info", NULL, NULL));
+ cmd[0] = (char *)"{\"prefix\":\"log\", \"logtext\":[\"fourxx\"]}";
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ sleep(2);
+ ASSERT_FALSE(l.contains("fourxx"));
+ rados_shutdown(cluster);
+}
diff --git a/src/test/librados/cmd_cxx.cc b/src/test/librados/cmd_cxx.cc
new file mode 100644
index 000000000..d67e2613b
--- /dev/null
+++ b/src/test/librados/cmd_cxx.cc
@@ -0,0 +1,92 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+#include <condition_variable>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "test/librados/test_cxx.h"
+
+using namespace librados;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+TEST(LibRadosCmd, MonDescribePP) {
+ Rados cluster;
+ ASSERT_EQ("", connect_cluster_pp(cluster));
+ bufferlist inbl, outbl;
+ string outs;
+ ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"get_command_descriptions\"}",
+ inbl, &outbl, &outs));
+ ASSERT_LT(0u, outbl.length());
+ ASSERT_LE(0u, outs.length());
+ cluster.shutdown();
+}
+
+TEST(LibRadosCmd, OSDCmdPP) {
+ Rados cluster;
+ ASSERT_EQ("", connect_cluster_pp(cluster));
+ int r;
+ bufferlist inbl, outbl;
+ string outs;
+ string cmd;
+
+ // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
+ cmd = "asdfasdf";
+ r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
+ ASSERT_TRUE(r == -22 || r == -ENXIO);
+ cmd = "version";
+ r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
+ ASSERT_TRUE(r == -22 || r == -ENXIO);
+ cmd = "{\"prefix\":\"version\"}";
+ r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
+ ASSERT_TRUE((r == 0 && outbl.length() > 0) || (r == -ENXIO && outbl.length() == 0));
+ cluster.shutdown();
+}
+
+TEST(LibRadosCmd, PGCmdPP) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+
+ int r;
+ bufferlist inbl, outbl;
+ string outs;
+ string cmd;
+
+ int64_t poolid = cluster.pool_lookup(pool_name.c_str());
+ ASSERT_LT(0, poolid);
+
+ string pgid = stringify(poolid) + ".0";
+
+ cmd = "asdfasdf";
+ // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
+ r = cluster.pg_command(pgid.c_str(), cmd, inbl, &outbl, &outs);
+ ASSERT_TRUE(r == -22 || r == -ENXIO);
+
+ // make sure the pg exists on the osd before we query it
+ IoCtx io;
+ cluster.ioctx_create(pool_name.c_str(), io);
+ for (int i=0; i<100; i++) {
+ string oid = "obj" + stringify(i);
+ ASSERT_EQ(-ENOENT, io.stat(oid, NULL, NULL));
+ }
+ io.close();
+
+ cmd = "{\"prefix\":\"pg\", \"cmd\":\"query\", \"pgid\":\"" + pgid + "\"}";
+ // note: tolerate ENOENT/ENXIO here if hte osd is thrashing out underneath us
+ r = cluster.pg_command(pgid.c_str(), cmd, inbl, &outbl, &outs);
+ ASSERT_TRUE(r == 0 || r == -ENOENT || r == -ENXIO);
+
+ ASSERT_LT(0u, outbl.length());
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
diff --git a/src/test/librados/completion_speed.cc b/src/test/librados/completion_speed.cc
new file mode 100644
index 000000000..708a58425
--- /dev/null
+++ b/src/test/librados/completion_speed.cc
@@ -0,0 +1,38 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "common/ceph_context.h"
+#include "common/Finisher.h"
+#include "librados/AioCompletionImpl.h"
+
+
+constexpr int max_completions = 10'000'000;
+int completed = 0;
+auto cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+Finisher f(cct);
+
+void completion_cb(librados::completion_t cb, void* arg) {
+ auto c = static_cast<librados::AioCompletion*>(arg);
+ delete c;
+ if (++completed < max_completions) {
+ auto aio = librados::Rados::aio_create_completion();
+ aio->set_complete_callback(static_cast<void*>(aio), &completion_cb);
+ f.queue(new librados::C_AioComplete(aio->pc));
+ }
+}
+
+int main(void) {
+ auto aio = librados::Rados::aio_create_completion();
+ aio->set_complete_callback(static_cast<void*>(aio), &completion_cb);
+ f.queue(new librados::C_AioComplete(aio->pc));
+ f.start();
+
+ while (completed < max_completions)
+ f.wait_for_empty();
+
+ f.stop();
+
+ assert(completed == max_completions);
+ cct->put();
+}
diff --git a/src/test/librados/crimson_utils.h b/src/test/librados/crimson_utils.h
new file mode 100644
index 000000000..4adcb69d9
--- /dev/null
+++ b/src/test/librados/crimson_utils.h
@@ -0,0 +1,15 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <cstdlib>
+
+static inline bool is_crimson_cluster() {
+ return getenv("CRIMSON_COMPAT") != nullptr;
+}
+
+#define SKIP_IF_CRIMSON() \
+ if (is_crimson_cluster()) { \
+ GTEST_SKIP() << "Not supported by crimson yet. Skipped"; \
+ }
diff --git a/src/test/librados/io.cc b/src/test/librados/io.cc
new file mode 100644
index 000000000..7814463d4
--- /dev/null
+++ b/src/test/librados/io.cc
@@ -0,0 +1,461 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include <climits>
+
+#include "include/rados/librados.h"
+#include "include/encoding.h"
+#include "include/err.h"
+#include "include/scope_guard.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+
+#include <errno.h>
+#include "gtest/gtest.h"
+#include "crimson_utils.h"
+
+using std::string;
+
+typedef RadosTest LibRadosIo;
+typedef RadosTestEC LibRadosIoEC;
+
+TEST_F(LibRadosIo, SimpleWrite) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "nspace");
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIo, TooBig) {
+ char buf[1] = { 0 };
+ ASSERT_EQ(-E2BIG, rados_write(ioctx, "A", buf, UINT_MAX, 0));
+ ASSERT_EQ(-E2BIG, rados_append(ioctx, "A", buf, UINT_MAX));
+ ASSERT_EQ(-E2BIG, rados_write_full(ioctx, "A", buf, UINT_MAX));
+ ASSERT_EQ(-E2BIG, rados_writesame(ioctx, "A", buf, sizeof(buf), UINT_MAX, 0));
+}
+
+TEST_F(LibRadosIo, ReadTimeout) {
+ char buf[128];
+ memset(buf, 'a', sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+
+ {
+ // set up a second client
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_create(&cluster, "admin"));
+ ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_set(cluster, "rados_osd_op_timeout", "1")); // use any small value that will result in a timeout
+ ASSERT_EQ(0, rados_conf_set(cluster, "ms_inject_internal_delays", "2")); // create a 2 second delay
+ ASSERT_EQ(0, rados_connect(cluster));
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ rados_ioctx_set_namespace(ioctx, nspace.c_str());
+
+ // then we show that the buffer is changed after rados_read returned
+ // with a timeout
+ for (int i=0; i<5; i++) {
+ char buf2[sizeof(buf)];
+ memset(buf2, 0, sizeof(buf2));
+ int err = rados_read(ioctx, "foo", buf2, sizeof(buf2), 0);
+ if (err == -110) {
+ int startIndex = 0;
+ // find the index until which librados already read the object before the timeout occurred
+ for (unsigned b=0; b<sizeof(buf); b++) {
+ if (buf2[b] != buf[b]) {
+ startIndex = b;
+ break;
+ }
+ }
+
+ // wait some time to give librados a change to do something
+ sleep(1);
+
+ // then check if the buffer was changed after the call
+ if (buf2[startIndex] == 'a') {
+ printf("byte at index %d was changed after the timeout to %d\n",
+ startIndex, (int)buf[startIndex]);
+ ASSERT_TRUE(0);
+ break;
+ }
+ } else {
+ printf("no timeout :/\n");
+ }
+ }
+ rados_ioctx_destroy(ioctx);
+ rados_shutdown(cluster);
+ }
+}
+
+
+TEST_F(LibRadosIo, RoundTrip) {
+ char buf[128];
+ char buf2[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+
+ uint64_t off = 19;
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "bar", buf, sizeof(buf), off));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "bar", buf2, sizeof(buf2), off));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+}
+
+TEST_F(LibRadosIo, Checksum) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+
+ uint32_t expected_crc = ceph_crc32c(-1, reinterpret_cast<const uint8_t*>(buf),
+ sizeof(buf));
+ ceph_le32 init_value(-1);
+ ceph_le32 crc[2];
+ ASSERT_EQ(0, rados_checksum(ioctx, "foo", LIBRADOS_CHECKSUM_TYPE_CRC32C,
+ reinterpret_cast<char*>(&init_value),
+ sizeof(init_value), sizeof(buf), 0, 0,
+ reinterpret_cast<char*>(&crc), sizeof(crc)));
+ ASSERT_EQ(1U, crc[0]);
+ ASSERT_EQ(expected_crc, crc[1]);
+}
+
+TEST_F(LibRadosIo, OverlappingWriteRoundTrip) {
+ char buf[128];
+ char buf2[64];
+ char buf3[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
+ memset(buf3, 0xdd, sizeof(buf3));
+ ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
+ ASSERT_EQ(0, memcmp(buf3 + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
+}
+
+TEST_F(LibRadosIo, WriteFullRoundTrip) {
+ char buf[128];
+ char buf2[64];
+ char buf3[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_write_full(ioctx, "foo", buf2, sizeof(buf2)));
+ memset(buf3, 0x00, sizeof(buf3));
+ ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf2, buf3, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIo, AppendRoundTrip) {
+ char buf[64];
+ char buf2[64];
+ char buf3[sizeof(buf) + sizeof(buf2)];
+ memset(buf, 0xde, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ memset(buf2, 0xad, sizeof(buf2));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf2, sizeof(buf2)));
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIo, ZeroLenZero) {
+ rados_write_op_t op = rados_create_write_op();
+ ASSERT_TRUE(op);
+ rados_write_op_zero(op, 0, 0);
+ ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "foo", NULL, 0));
+ rados_release_write_op(op);
+}
+
+TEST_F(LibRadosIo, TruncTest) {
+ char buf[128];
+ char buf2[sizeof(buf)];
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_trunc(ioctx, "foo", sizeof(buf) / 2));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ((int)(sizeof(buf)/2), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)/2));
+}
+
+TEST_F(LibRadosIo, RemoveTest) {
+ char buf[128];
+ char buf2[sizeof(buf)];
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_remove(ioctx, "foo"));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
+}
+
+TEST_F(LibRadosIo, XattrsRoundTrip) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(-ENODATA, rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_setxattr(ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ((int)sizeof(attr1_buf),
+ rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
+}
+
+TEST_F(LibRadosIo, RmXattr) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0,
+ rados_setxattr(ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ(0, rados_rmxattr(ioctx, "foo", attr1));
+ ASSERT_EQ(-ENODATA, rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
+
+ // Test rmxattr on a removed object
+ char buf2[128];
+ char attr2[] = "attr2";
+ char attr2_buf[] = "foo bar baz";
+ memset(buf2, 0xbb, sizeof(buf2));
+ ASSERT_EQ(0, rados_write(ioctx, "foo_rmxattr", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0,
+ rados_setxattr(ioctx, "foo_rmxattr", attr2, attr2_buf, sizeof(attr2_buf)));
+ ASSERT_EQ(0, rados_remove(ioctx, "foo_rmxattr"));
+ ASSERT_EQ(-ENOENT, rados_rmxattr(ioctx, "foo_rmxattr", attr2));
+}
+
+TEST_F(LibRadosIo, XattrIter) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ char attr2[] = "attr2";
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_setxattr(ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ(0, rados_setxattr(ioctx, "foo", attr2, attr2_buf, sizeof(attr2_buf)));
+ rados_xattrs_iter_t iter;
+ ASSERT_EQ(0, rados_getxattrs(ioctx, "foo", &iter));
+ int num_seen = 0;
+ while (true) {
+ const char *name;
+ const char *val;
+ size_t len;
+ ASSERT_EQ(0, rados_getxattrs_next(iter, &name, &val, &len));
+ if (name == NULL) {
+ break;
+ }
+ ASSERT_LT(num_seen, 2);
+ if ((strcmp(name, attr1) == 0) && (val != NULL) && (memcmp(val, attr1_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else if ((strcmp(name, attr2) == 0) && (val != NULL) && (memcmp(val, attr2_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else {
+ ASSERT_EQ(0, 1);
+ }
+ }
+ rados_getxattrs_end(iter);
+}
+
+TEST_F(LibRadosIoEC, SimpleWrite) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "nspace");
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoEC, RoundTrip) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char buf2[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+
+ uint64_t off = 19;
+ ASSERT_EQ(-EOPNOTSUPP, rados_write(ioctx, "bar", buf, sizeof(buf), off));
+}
+
+TEST_F(LibRadosIoEC, OverlappingWriteRoundTrip) {
+ SKIP_IF_CRIMSON();
+ int bsize = alignment;
+ int dbsize = bsize * 2;
+ char *buf = (char *)new char[dbsize];
+ char *buf2 = (char *)new char[bsize];
+ char *buf3 = (char *)new char[dbsize];
+ auto cleanup = [&] {
+ delete[] buf;
+ delete[] buf2;
+ delete[] buf3;
+ };
+ scope_guard<decltype(cleanup)> sg(std::move(cleanup));
+ memset(buf, 0xcc, dbsize);
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, dbsize, 0));
+ memset(buf2, 0xdd, bsize);
+ ASSERT_EQ(-EOPNOTSUPP, rados_write(ioctx, "foo", buf2, bsize, 0));
+ memset(buf3, 0xdd, dbsize);
+ ASSERT_EQ(dbsize, rados_read(ioctx, "foo", buf3, dbsize, 0));
+ // Read the same as first write
+ ASSERT_EQ(0, memcmp(buf3, buf, dbsize));
+}
+
+TEST_F(LibRadosIoEC, WriteFullRoundTrip) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char buf2[64];
+ char buf3[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_write_full(ioctx, "foo", buf2, sizeof(buf2)));
+ memset(buf3, 0xee, sizeof(buf3));
+ ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoEC, AppendRoundTrip) {
+ SKIP_IF_CRIMSON();
+ char *buf = (char *)new char[alignment];
+ char *buf2 = (char *)new char[alignment];
+ char *buf3 = (char *)new char[alignment *2];
+ int uasize = alignment/2;
+ char *unalignedbuf = (char *)new char[uasize];
+ auto cleanup = [&] {
+ delete[] buf;
+ delete[] buf2;
+ delete[] buf3;
+ delete[] unalignedbuf;
+ };
+ scope_guard<decltype(cleanup)> sg(std::move(cleanup));
+ memset(buf, 0xde, alignment);
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, alignment));
+ memset(buf2, 0xad, alignment);
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf2, alignment));
+ memset(buf3, 0, alignment*2);
+ ASSERT_EQ((int)alignment*2, rados_read(ioctx, "foo", buf3, alignment*2, 0));
+ ASSERT_EQ(0, memcmp(buf3, buf, alignment));
+ ASSERT_EQ(0, memcmp(buf3 + alignment, buf2, alignment));
+ memset(unalignedbuf, 0, uasize);
+ ASSERT_EQ(0, rados_append(ioctx, "foo", unalignedbuf, uasize));
+ ASSERT_EQ(-EOPNOTSUPP, rados_append(ioctx, "foo", unalignedbuf, uasize));
+}
+
+TEST_F(LibRadosIoEC, TruncTest) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char buf2[sizeof(buf)];
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(-EOPNOTSUPP, rados_trunc(ioctx, "foo", sizeof(buf) / 2));
+ memset(buf2, 0, sizeof(buf2));
+ // Same size
+ ASSERT_EQ((int)sizeof(buf), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
+ // No change
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+}
+
+TEST_F(LibRadosIoEC, RemoveTest) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char buf2[sizeof(buf)];
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_remove(ioctx, "foo"));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
+}
+
+TEST_F(LibRadosIoEC, XattrsRoundTrip) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(-ENODATA, rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_setxattr(ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ((int)sizeof(attr1_buf),
+ rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
+}
+
+TEST_F(LibRadosIoEC, RmXattr) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0,
+ rados_setxattr(ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ(0, rados_rmxattr(ioctx, "foo", attr1));
+ ASSERT_EQ(-ENODATA, rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
+
+ // Test rmxattr on a removed object
+ char buf2[128];
+ char attr2[] = "attr2";
+ char attr2_buf[] = "foo bar baz";
+ memset(buf2, 0xbb, sizeof(buf2));
+ ASSERT_EQ(0, rados_write(ioctx, "foo_rmxattr", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0,
+ rados_setxattr(ioctx, "foo_rmxattr", attr2, attr2_buf, sizeof(attr2_buf)));
+ ASSERT_EQ(0, rados_remove(ioctx, "foo_rmxattr"));
+ ASSERT_EQ(-ENOENT, rados_rmxattr(ioctx, "foo_rmxattr", attr2));
+}
+
+TEST_F(LibRadosIoEC, XattrIter) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ char attr2[] = "attr2";
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_append(ioctx, "foo", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_setxattr(ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ(0, rados_setxattr(ioctx, "foo", attr2, attr2_buf, sizeof(attr2_buf)));
+ rados_xattrs_iter_t iter;
+ ASSERT_EQ(0, rados_getxattrs(ioctx, "foo", &iter));
+ int num_seen = 0;
+ while (true) {
+ const char *name;
+ const char *val;
+ size_t len;
+ ASSERT_EQ(0, rados_getxattrs_next(iter, &name, &val, &len));
+ if (name == NULL) {
+ break;
+ }
+ ASSERT_LT(num_seen, 2);
+ if ((strcmp(name, attr1) == 0) && (val != NULL) && (memcmp(val, attr1_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else if ((strcmp(name, attr2) == 0) && (val != NULL) && (memcmp(val, attr2_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else {
+ ASSERT_EQ(0, 1);
+ }
+ }
+ rados_getxattrs_end(iter);
+}
diff --git a/src/test/librados/io_cxx.cc b/src/test/librados/io_cxx.cc
new file mode 100644
index 000000000..d9606d16b
--- /dev/null
+++ b/src/test/librados/io_cxx.cc
@@ -0,0 +1,986 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include <climits>
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/encoding.h"
+#include "include/err.h"
+#include "include/scope_guard.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+#include "crimson_utils.h"
+
+using namespace librados;
+using std::string;
+
+typedef RadosTestPP LibRadosIoPP;
+typedef RadosTestECPP LibRadosIoECPP;
+
+TEST_F(LibRadosIoPP, TooBigPP) {
+ IoCtx ioctx;
+ bufferlist bl;
+ ASSERT_EQ(-E2BIG, ioctx.write("foo", bl, UINT_MAX, 0));
+ ASSERT_EQ(-E2BIG, ioctx.append("foo", bl, UINT_MAX));
+ // ioctx.write_full no way to overflow bl.length()
+ ASSERT_EQ(-E2BIG, ioctx.writesame("foo", bl, UINT_MAX, 0));
+}
+
+TEST_F(LibRadosIoPP, SimpleWritePP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ ioctx.set_namespace("nspace");
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoPP, ReadOpPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+ {
+ bufferlist op_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist op_bl;
+ ObjectReadOperation op;
+ op.read(0, 0, NULL, NULL); //len=0 mean read the whole object data.
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl, op_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist op_bl;
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl, op_bl;
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl1, read_bl2, op_bl;
+ int rval1 = 1000, rval2 = 1002;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl1, &rval1);
+ op.read(0, sizeof(buf), &read_bl2, &rval2);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), read_bl1.length());
+ ASSERT_EQ(sizeof(buf), read_bl2.length());
+ ASSERT_EQ(sizeof(buf) * 2, op_bl.length());
+ ASSERT_EQ(0, rval1);
+ ASSERT_EQ(0, rval2);
+ ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(op_bl.c_str() + sizeof(buf), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist op_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(0, rval);
+ }
+
+ {
+ bufferlist read_bl;
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl1, read_bl2;
+ int rval1 = 1000, rval2 = 1002;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl1, &rval1);
+ op.read(0, sizeof(buf), &read_bl2, &rval2);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(sizeof(buf), read_bl1.length());
+ ASSERT_EQ(sizeof(buf), read_bl2.length());
+ ASSERT_EQ(0, rval1);
+ ASSERT_EQ(0, rval2);
+ ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+ }
+
+ // read into a preallocated buffer with a cached crc
+ {
+ bufferlist op_bl;
+ op_bl.append(std::string(sizeof(buf), 'x'));
+ ASSERT_NE(op_bl.crc32c(0), bl.crc32c(0)); // cache 'x' crc
+
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(op_bl.crc32c(0), bl.crc32c(0));
+ }
+}
+
+TEST_F(LibRadosIoPP, SparseReadOpPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+ {
+ std::map<uint64_t, uint64_t> extents;
+ bufferlist read_bl;
+ int rval = -1;
+ ObjectReadOperation op;
+ op.sparse_read(0, sizeof(buf), &extents, &read_bl, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, nullptr));
+ ASSERT_EQ(0, rval);
+ assert_eq_sparse(bl, extents, read_bl);
+ }
+ {
+ bufferlist bl;
+ bl.append(buf, sizeof(buf) / 2);
+
+ std::map<uint64_t, uint64_t> extents;
+ bufferlist read_bl;
+ int rval = -1;
+ ObjectReadOperation op;
+ op.sparse_read(0, sizeof(buf), &extents, &read_bl, &rval, sizeof(buf) / 2, 1);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, nullptr));
+ ASSERT_EQ(0, rval);
+ assert_eq_sparse(bl, extents, read_bl);
+ }
+}
+
+TEST_F(LibRadosIoPP, RoundTripPP) {
+ char buf[128];
+ Rados cluster;
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ bufferlist cl;
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", cl, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
+}
+
+TEST_F(LibRadosIoPP, RoundTripPP2)
+{
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write;
+ write.write(0, bl);
+ write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoPP, Checksum) {
+ char buf[128];
+ Rados cluster;
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ bufferlist init_value_bl;
+ encode(static_cast<uint32_t>(-1), init_value_bl);
+ bufferlist csum_bl;
+ ASSERT_EQ(0, ioctx.checksum("foo", LIBRADOS_CHECKSUM_TYPE_CRC32C,
+ init_value_bl, sizeof(buf), 0, 0, &csum_bl));
+ auto csum_bl_it = csum_bl.cbegin();
+ uint32_t csum_count;
+ decode(csum_count, csum_bl_it);
+ ASSERT_EQ(1U, csum_count);
+ uint32_t csum;
+ decode(csum, csum_bl_it);
+ ASSERT_EQ(bl.crc32c(-1), csum);
+}
+
+TEST_F(LibRadosIoPP, ReadIntoBufferlist) {
+
+ // here we test reading into a non-empty bufferlist referencing existing
+ // buffers
+
+ char buf[128];
+ Rados cluster;
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ bufferlist bl2;
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xbb, sizeof(buf2));
+ bl2.append(buffer::create_static(sizeof(buf2), buf2));
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl2, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+}
+
+TEST_F(LibRadosIoPP, OverlappingWriteRoundTripPP) {
+ char buf[128];
+ char buf2[64];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+ bufferlist bl3;
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+ ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoPP, WriteFullRoundTripPP) {
+ char buf[128];
+ char buf2[64];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, ioctx.write_full("foo", bl2));
+ bufferlist bl3;
+ ASSERT_EQ((int)sizeof(buf2), ioctx.read("foo", bl3, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoPP, WriteFullRoundTripPP2)
+{
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write;
+ write.write_full(bl);
+ write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoPP, AppendRoundTripPP) {
+ char buf[64];
+ char buf2[64];
+ memset(buf, 0xde, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ memset(buf2, 0xad, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, ioctx.append("foo", bl2, sizeof(buf2)));
+ bufferlist bl3;
+ ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)),
+ ioctx.read("foo", bl3, (sizeof(buf) + sizeof(buf2)), 0));
+ const char *bl3_str = bl3.c_str();
+ ASSERT_EQ(0, memcmp(bl3_str, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(bl3_str + sizeof(buf), buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoPP, TruncTestPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl, sizeof(buf)));
+ ASSERT_EQ(0, ioctx.trunc("foo", sizeof(buf) / 2));
+ bufferlist bl2;
+ ASSERT_EQ((int)(sizeof(buf)/2), ioctx.read("foo", bl2, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)/2));
+}
+
+TEST_F(LibRadosIoPP, RemoveTestPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ ASSERT_EQ(0, ioctx.remove("foo"));
+ bufferlist bl2;
+ ASSERT_EQ(-ENOENT, ioctx.read("foo", bl2, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoPP, XattrsRoundTripPP) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ bufferlist bl2;
+ ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl2));
+ bufferlist bl3;
+ bl3.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl3));
+ bufferlist bl4;
+ ASSERT_EQ((int)sizeof(attr1_buf),
+ ioctx.getxattr("foo", attr1, bl4));
+ ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST_F(LibRadosIoPP, RmXattrPP) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+ ASSERT_EQ(0, ioctx.rmxattr("foo", attr1));
+ bufferlist bl3;
+ ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl3));
+
+ // Test rmxattr on a removed object
+ char buf2[128];
+ char attr2[] = "attr2";
+ char attr2_buf[] = "foo bar baz";
+ memset(buf2, 0xbb, sizeof(buf2));
+ bufferlist bl21;
+ bl21.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
+ bufferlist bl22;
+ bl22.append(attr2_buf, sizeof(attr2_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo_rmxattr", attr2, bl22));
+ ASSERT_EQ(0, ioctx.remove("foo_rmxattr"));
+ ASSERT_EQ(-ENOENT, ioctx.rmxattr("foo_rmxattr", attr2));
+}
+
+TEST_F(LibRadosIoPP, XattrListPP) {
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ char attr2[] = "attr2";
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+ bufferlist bl3;
+ bl3.append(attr2_buf, sizeof(attr2_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr2, bl3));
+ std::map<std::string, bufferlist> attrset;
+ ASSERT_EQ(0, ioctx.getxattrs("foo", attrset));
+ for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+ i != attrset.end(); ++i) {
+ if (i->first == string(attr1)) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+ }
+ else if (i->first == string(attr2)) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+ }
+ else {
+ ASSERT_EQ(0, 1);
+ }
+ }
+}
+
+TEST_F(LibRadosIoECPP, SimpleWritePP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ ioctx.set_namespace("nspace");
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoECPP, ReadOpPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+ {
+ bufferlist op_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist op_bl;
+ ObjectReadOperation op;
+ op.read(0, 0, NULL, NULL); //len=0 mean read the whole object data
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl, op_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist op_bl;
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl, op_bl;
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl1, read_bl2, op_bl;
+ int rval1 = 1000, rval2 = 1002;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl1, &rval1);
+ op.read(0, sizeof(buf), &read_bl2, &rval2);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), read_bl1.length());
+ ASSERT_EQ(sizeof(buf), read_bl2.length());
+ ASSERT_EQ(sizeof(buf) * 2, op_bl.length());
+ ASSERT_EQ(0, rval1);
+ ASSERT_EQ(0, rval2);
+ ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(op_bl.c_str() + sizeof(buf), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist op_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(0, rval);
+ }
+
+ {
+ bufferlist read_bl;
+ int rval = 1000;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(sizeof(buf), read_bl.length());
+ ASSERT_EQ(0, rval);
+ ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+ }
+
+ {
+ bufferlist read_bl1, read_bl2;
+ int rval1 = 1000, rval2 = 1002;
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), &read_bl1, &rval1);
+ op.read(0, sizeof(buf), &read_bl2, &rval2);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(sizeof(buf), read_bl1.length());
+ ASSERT_EQ(sizeof(buf), read_bl2.length());
+ ASSERT_EQ(0, rval1);
+ ASSERT_EQ(0, rval2);
+ ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+ }
+
+ // read into a preallocated buffer with a cached crc
+ {
+ bufferlist op_bl;
+ op_bl.append(std::string(sizeof(buf), 'x'));
+ ASSERT_NE(op_bl.crc32c(0), bl.crc32c(0)); // cache 'x' crc
+
+ ObjectReadOperation op;
+ op.read(0, sizeof(buf), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+
+ ASSERT_EQ(sizeof(buf), op_bl.length());
+ ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(op_bl.crc32c(0), bl.crc32c(0));
+ }
+}
+
+TEST_F(LibRadosIoECPP, SparseReadOpPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+ {
+ std::map<uint64_t, uint64_t> extents;
+ bufferlist read_bl;
+ int rval = -1;
+ ObjectReadOperation op;
+ op.sparse_read(0, sizeof(buf), &extents, &read_bl, &rval);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, nullptr));
+ ASSERT_EQ(0, rval);
+ assert_eq_sparse(bl, extents, read_bl);
+ }
+}
+
+TEST_F(LibRadosIoECPP, RoundTripPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ Rados cluster;
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ bufferlist cl;
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", cl, sizeof(buf) * 3, 0));
+ ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
+}
+
+TEST_F(LibRadosIoECPP, RoundTripPP2)
+{
+ SKIP_IF_CRIMSON();
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write;
+ write.write(0, bl);
+ write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoECPP, OverlappingWriteRoundTripPP) {
+ SKIP_IF_CRIMSON();
+ int bsize = alignment;
+ int dbsize = bsize * 2;
+ char *buf = (char *)new char[dbsize];
+ char *buf2 = (char *)new char[bsize];
+ auto cleanup = [&] {
+ delete[] buf;
+ delete[] buf2;
+ };
+ scope_guard<decltype(cleanup)> sg(std::move(cleanup));
+ memset(buf, 0xcc, dbsize);
+ bufferlist bl1;
+ bl1.append(buf, dbsize);
+ ASSERT_EQ(0, ioctx.write("foo", bl1, dbsize, 0));
+ memset(buf2, 0xdd, bsize);
+ bufferlist bl2;
+ bl2.append(buf2, bsize);
+ ASSERT_EQ(-EOPNOTSUPP, ioctx.write("foo", bl2, bsize, 0));
+ bufferlist bl3;
+ ASSERT_EQ(dbsize, ioctx.read("foo", bl3, dbsize, 0));
+ // Read the same as first write
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, dbsize));
+}
+
+TEST_F(LibRadosIoECPP, WriteFullRoundTripPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char buf2[64];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, ioctx.write_full("foo", bl2));
+ bufferlist bl3;
+ ASSERT_EQ((int)sizeof(buf2), ioctx.read("foo", bl3, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoECPP, WriteFullRoundTripPP2)
+{
+ SKIP_IF_CRIMSON();
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write;
+ write.write_full(bl);
+ write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoECPP, AppendRoundTripPP) {
+ SKIP_IF_CRIMSON();
+ char *buf = (char *)new char[alignment];
+ char *buf2 = (char *)new char[alignment];
+ auto cleanup = [&] {
+ delete[] buf;
+ delete[] buf2;
+ };
+ scope_guard<decltype(cleanup)> sg(std::move(cleanup));
+ memset(buf, 0xde, alignment);
+ bufferlist bl1;
+ bl1.append(buf, alignment);
+ ASSERT_EQ(0, ioctx.append("foo", bl1, alignment));
+ memset(buf2, 0xad, alignment);
+ bufferlist bl2;
+ bl2.append(buf2, alignment);
+ ASSERT_EQ(0, ioctx.append("foo", bl2, alignment));
+ bufferlist bl3;
+ ASSERT_EQ((int)(alignment * 2),
+ ioctx.read("foo", bl3, (alignment * 4), 0));
+ const char *bl3_str = bl3.c_str();
+ ASSERT_EQ(0, memcmp(bl3_str, buf, alignment));
+ ASSERT_EQ(0, memcmp(bl3_str + alignment, buf2, alignment));
+}
+
+TEST_F(LibRadosIoECPP, TruncTestPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl, sizeof(buf)));
+ ASSERT_EQ(-EOPNOTSUPP, ioctx.trunc("foo", sizeof(buf) / 2));
+ bufferlist bl2;
+ // Same size
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl2, sizeof(buf), 0));
+ // No change
+ ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)));
+}
+
+TEST_F(LibRadosIoECPP, RemoveTestPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ ASSERT_EQ(0, ioctx.remove("foo"));
+ bufferlist bl2;
+ ASSERT_EQ(-ENOENT, ioctx.read("foo", bl2, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoECPP, XattrsRoundTripPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ bufferlist bl2;
+ ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl2));
+ bufferlist bl3;
+ bl3.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl3));
+ bufferlist bl4;
+ ASSERT_EQ((int)sizeof(attr1_buf),
+ ioctx.getxattr("foo", attr1, bl4));
+ ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST_F(LibRadosIoECPP, RmXattrPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+ ASSERT_EQ(0, ioctx.rmxattr("foo", attr1));
+ bufferlist bl3;
+ ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl3));
+
+ // Test rmxattr on a removed object
+ char buf2[128];
+ char attr2[] = "attr2";
+ char attr2_buf[] = "foo bar baz";
+ memset(buf2, 0xbb, sizeof(buf2));
+ bufferlist bl21;
+ bl21.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
+ bufferlist bl22;
+ bl22.append(attr2_buf, sizeof(attr2_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo_rmxattr", attr2, bl22));
+ ASSERT_EQ(0, ioctx.remove("foo_rmxattr"));
+ ASSERT_EQ(-ENOENT, ioctx.rmxattr("foo_rmxattr", attr2));
+}
+
+TEST_F(LibRadosIoECPP, XattrListPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char attr1[] = "attr1";
+ char attr1_buf[] = "foo bar baz";
+ char attr2[] = "attr2";
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+ bufferlist bl3;
+ bl3.append(attr2_buf, sizeof(attr2_buf));
+ ASSERT_EQ(0, ioctx.setxattr("foo", attr2, bl3));
+ std::map<std::string, bufferlist> attrset;
+ ASSERT_EQ(0, ioctx.getxattrs("foo", attrset));
+ for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+ i != attrset.end(); ++i) {
+ if (i->first == string(attr1)) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+ }
+ else if (i->first == string(attr2)) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+ }
+ else {
+ ASSERT_EQ(0, 1);
+ }
+ }
+}
+
+TEST_F(LibRadosIoPP, CmpExtPP) {
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write1;
+ write1.write(0, bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+ bufferlist new_bl;
+ new_bl.append("CEPH");
+ ObjectWriteOperation write2;
+ write2.cmpext(0, bl, nullptr);
+ write2.write(0, new_bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write2));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoPP, CmpExtDNEPP) {
+ bufferlist bl;
+ bl.append(std::string(4, '\0'));
+
+ bufferlist new_bl;
+ new_bl.append("CEPH");
+ ObjectWriteOperation write;
+ write.cmpext(0, bl, nullptr);
+ write.write(0, new_bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoPP, CmpExtMismatchPP) {
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write1;
+ write1.write(0, bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+ bufferlist new_bl;
+ new_bl.append("CEPH");
+ ObjectWriteOperation write2;
+ write2.cmpext(0, new_bl, nullptr);
+ write2.write(0, new_bl);
+ ASSERT_EQ(-MAX_ERRNO, ioctx.operate("foo", &write2));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoECPP, CmpExtPP) {
+ SKIP_IF_CRIMSON();
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write1;
+ write1.write(0, bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+ bufferlist new_bl;
+ new_bl.append("CEPH");
+ ObjectWriteOperation write2;
+ write2.cmpext(0, bl, nullptr);
+ write2.write_full(new_bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write2));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoECPP, CmpExtDNEPP) {
+ SKIP_IF_CRIMSON();
+ bufferlist bl;
+ bl.append(std::string(4, '\0'));
+
+ bufferlist new_bl;
+ new_bl.append("CEPH");
+ ObjectWriteOperation write;
+ write.cmpext(0, bl, nullptr);
+ write.write_full(new_bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoECPP, CmpExtMismatchPP) {
+ SKIP_IF_CRIMSON();
+ bufferlist bl;
+ bl.append("ceph");
+ ObjectWriteOperation write1;
+ write1.write(0, bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+ bufferlist new_bl;
+ new_bl.append("CEPH");
+ ObjectWriteOperation write2;
+ write2.cmpext(0, new_bl, nullptr);
+ write2.write_full(new_bl);
+ ASSERT_EQ(-MAX_ERRNO, ioctx.operate("foo", &write2));
+
+ ObjectReadOperation read;
+ read.read(0, bl.length(), NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+ ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
diff --git a/src/test/librados/librados.cc b/src/test/librados/librados.cc
new file mode 100644
index 000000000..c688724da
--- /dev/null
+++ b/src/test/librados/librados.cc
@@ -0,0 +1,13 @@
+//#include "common/config.h"
+#include "include/rados/librados.h"
+
+#include "gtest/gtest.h"
+
+TEST(Librados, CreateShutdown) {
+ rados_t cluster;
+ int err;
+ err = rados_create(&cluster, "someid");
+ EXPECT_EQ(err, 0);
+
+ rados_shutdown(cluster);
+}
diff --git a/src/test/librados/librados_config.cc b/src/test/librados/librados_config.cc
new file mode 100644
index 000000000..d30fb30ef
--- /dev/null
+++ b/src/test/librados/librados_config.cc
@@ -0,0 +1,98 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "include/rados/librados.h"
+
+#include <sstream>
+#include <string>
+#include <string.h>
+#include <errno.h>
+
+using std::string;
+
+TEST(LibRadosConfig, SimpleSet) {
+ rados_t cl;
+ int ret = rados_create(&cl, NULL);
+ ASSERT_EQ(ret, 0);
+
+ ret = rados_conf_set(cl, "leveldb_max_open_files", "21");
+ ASSERT_EQ(ret, 0);
+
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ ret = rados_conf_get(cl, "leveldb_max_open_files", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(string("21"), string(buf));
+
+ rados_shutdown(cl);
+}
+
+TEST(LibRadosConfig, ArgV) {
+ rados_t cl;
+ int ret = rados_create(&cl, NULL);
+ ASSERT_EQ(ret, 0);
+
+ const char *argv[] = { "foo", "--leveldb-max-open-files", "2",
+ "--key", "my-key", NULL };
+ size_t argc = (sizeof(argv) / sizeof(argv[0])) - 1;
+ rados_conf_parse_argv(cl, argc, argv);
+
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ ret = rados_conf_get(cl, "key", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(string("my-key"), string(buf));
+
+ memset(buf, 0, sizeof(buf));
+ ret = rados_conf_get(cl, "leveldb_max_open_files", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(string("2"), string(buf));
+
+ rados_shutdown(cl);
+}
+
+TEST(LibRadosConfig, DebugLevels) {
+ rados_t cl;
+ int ret = rados_create(&cl, NULL);
+ ASSERT_EQ(ret, 0);
+
+ ret = rados_conf_set(cl, "debug_rados", "3");
+ ASSERT_EQ(ret, 0);
+
+ char buf[128];
+ memset(buf, 0, sizeof(buf));
+ ret = rados_conf_get(cl, "debug_rados", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(0, strcmp("3/3", buf));
+
+ ret = rados_conf_set(cl, "debug_rados", "7/8");
+ ASSERT_EQ(ret, 0);
+
+ memset(buf, 0, sizeof(buf));
+ ret = rados_conf_get(cl, "debug_rados", buf, sizeof(buf));
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(0, strcmp("7/8", buf));
+
+ ret = rados_conf_set(cl, "debug_rados", "foo");
+ ASSERT_EQ(ret, -EINVAL);
+
+ ret = rados_conf_set(cl, "debug_asdkfasdjfajksdf", "foo");
+ ASSERT_EQ(ret, -ENOENT);
+
+ ret = rados_conf_get(cl, "debug_radfjadfsdados", buf, sizeof(buf));
+ ASSERT_EQ(ret, -ENOENT);
+
+ rados_shutdown(cl);
+}
diff --git a/src/test/librados/list.cc b/src/test/librados/list.cc
new file mode 100644
index 000000000..ef3488c0b
--- /dev/null
+++ b/src/test/librados/list.cc
@@ -0,0 +1,555 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "test/librados/test.h"
+#include "test/librados/test_common.h"
+#include "test/librados/TestCase.h"
+#include "global/global_context.h"
+
+#include "include/types.h"
+#include "common/hobject.h"
+#include "gtest/gtest.h"
+#include <errno.h>
+#include <string>
+#include <stdexcept>
+
+#include "crimson_utils.h"
+
+using namespace std;
+using namespace librados;
+
+typedef RadosTestNSCleanup LibRadosList;
+typedef RadosTestECNSCleanup LibRadosListEC;
+typedef RadosTestNP LibRadosListNP;
+
+
+TEST_F(LibRadosList, ListObjects) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ rados_list_ctx_t ctx;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ const char *entry;
+ bool foundit = false;
+ while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) != -ENOENT) {
+ foundit = true;
+ ASSERT_EQ(std::string(entry), "foo");
+ }
+ ASSERT_TRUE(foundit);
+ rados_nobjects_list_close(ctx);
+}
+
+TEST_F(LibRadosList, ListObjectsZeroInName) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo\0bar", buf, sizeof(buf), 0));
+ rados_list_ctx_t ctx;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ const char *entry;
+ size_t entry_size;
+ bool foundit = false;
+ while (rados_nobjects_list_next2(ctx, &entry, NULL, NULL,
+ &entry_size, NULL, NULL) != -ENOENT) {
+ foundit = true;
+ ASSERT_EQ(std::string(entry, entry_size), "foo\0bar");
+ }
+ ASSERT_TRUE(foundit);
+ rados_nobjects_list_close(ctx);
+}
+
+static void check_list(
+ std::set<std::string>& myset,
+ rados_list_ctx_t& ctx,
+ const std::string &check_nspace)
+{
+ const char *entry, *nspace;
+ cout << "myset " << myset << std::endl;
+ // we should see every item exactly once.
+ int ret;
+ while ((ret = rados_nobjects_list_next(ctx, &entry, NULL, &nspace)) == 0) {
+ std::string test_name;
+ if (check_nspace == all_nspaces) {
+ test_name = std::string(nspace) + ":" + std::string(entry);
+ } else {
+ ASSERT_TRUE(std::string(nspace) == check_nspace);
+ test_name = std::string(entry);
+ }
+ cout << test_name << std::endl;
+
+ ASSERT_TRUE(myset.end() != myset.find(test_name));
+ myset.erase(test_name);
+ }
+ ASSERT_EQ(-ENOENT, ret);
+ ASSERT_TRUE(myset.empty());
+}
+
+TEST_F(LibRadosList, ListObjectsNS) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "ns1");
+ ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_write(ioctx, "foo2", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo3", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "ns1");
+ ASSERT_EQ(0, rados_write(ioctx, "foo4", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo5", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "ns2");
+ ASSERT_EQ(0, rados_write(ioctx, "foo6", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo7", buf, sizeof(buf), 0));
+
+ char nspace[4];
+ ASSERT_EQ(-ERANGE, rados_ioctx_get_namespace(ioctx, nspace, 3));
+ ASSERT_EQ(static_cast<int>(strlen("ns2")),
+ rados_ioctx_get_namespace(ioctx, nspace, sizeof(nspace)));
+ ASSERT_EQ(0, strcmp("ns2", nspace));
+
+ std::set<std::string> def, ns1, ns2, all;
+ def.insert(std::string("foo1"));
+ def.insert(std::string("foo2"));
+ def.insert(std::string("foo3"));
+ ns1.insert(std::string("foo1"));
+ ns1.insert(std::string("foo4"));
+ ns1.insert(std::string("foo5"));
+ ns2.insert(std::string("foo6"));
+ ns2.insert(std::string("foo7"));
+ all.insert(std::string(":foo1"));
+ all.insert(std::string(":foo2"));
+ all.insert(std::string(":foo3"));
+ all.insert(std::string("ns1:foo1"));
+ all.insert(std::string("ns1:foo4"));
+ all.insert(std::string("ns1:foo5"));
+ all.insert(std::string("ns2:foo6"));
+ all.insert(std::string("ns2:foo7"));
+
+ rados_list_ctx_t ctx;
+ // Check default namespace ""
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(def, ctx, "");
+ rados_nobjects_list_close(ctx);
+
+ // Check namespace "ns1"
+ rados_ioctx_set_namespace(ioctx, "ns1");
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(ns1, ctx, "ns1");
+ rados_nobjects_list_close(ctx);
+
+ // Check namespace "ns2"
+ rados_ioctx_set_namespace(ioctx, "ns2");
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(ns2, ctx, "ns2");
+ rados_nobjects_list_close(ctx);
+
+ // Check ALL namespaces
+ rados_ioctx_set_namespace(ioctx, LIBRADOS_ALL_NSPACES);
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(all, ctx, all_nspaces);
+ rados_nobjects_list_close(ctx);
+}
+
+
+TEST_F(LibRadosList, ListObjectsStart) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+
+ for (int i=0; i<16; ++i) {
+ string n = stringify(i);
+ ASSERT_EQ(0, rados_write(ioctx, n.c_str(), buf, sizeof(buf), 0));
+ }
+
+ rados_list_ctx_t ctx;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ std::map<int, std::set<std::string> > pg_to_obj;
+ const char *entry;
+ while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
+ uint32_t pos = rados_nobjects_list_get_pg_hash_position(ctx);
+ std::cout << entry << " " << pos << std::endl;
+ pg_to_obj[pos].insert(entry);
+ }
+ rados_nobjects_list_close(ctx);
+
+ std::map<int, std::set<std::string> >::reverse_iterator p =
+ pg_to_obj.rbegin();
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ while (p != pg_to_obj.rend()) {
+ ASSERT_EQ((uint32_t)p->first, rados_nobjects_list_seek(ctx, p->first));
+ ASSERT_EQ(0, rados_nobjects_list_next(ctx, &entry, NULL, NULL));
+ std::cout << "have " << entry << " expect one of " << p->second << std::endl;
+ ASSERT_TRUE(p->second.count(entry));
+ ++p;
+ }
+ rados_nobjects_list_close(ctx);
+}
+
+// this function replicates
+// librados::operator<<(std::ostream& os, const librados::ObjectCursor& oc)
+// because we don't want to use librados in librados client.
+std::ostream& operator<<(std::ostream&os, const rados_object_list_cursor& oc)
+{
+ if (oc) {
+ os << *(hobject_t *)oc;
+ } else {
+ os << hobject_t{};
+ }
+ return os;
+}
+
+TEST_F(LibRadosList, ListObjectsCursor) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+
+ const int max_objs = 16;
+
+ for (int i=0; i<max_objs; ++i) {
+ string n = stringify(i);
+ ASSERT_EQ(0, rados_write(ioctx, n.c_str(), buf, sizeof(buf), 0));
+ }
+
+ {
+ rados_list_ctx_t ctx;
+ const char *entry;
+ rados_object_list_cursor cursor;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
+ rados_object_list_cursor first_cursor = cursor;
+ cout << "x cursor=" << cursor << std::endl;
+ while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
+ string oid = entry;
+ ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
+ cout << "> oid=" << oid << " cursor=" << cursor << std::endl;
+ }
+ rados_nobjects_list_seek_cursor(ctx, first_cursor);
+ ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
+ cout << "FIRST> seek to " << first_cursor << " oid=" << string(entry) << std::endl;
+ }
+ rados_list_ctx_t ctx;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+
+ std::map<rados_object_list_cursor, string> cursor_to_obj;
+ int count = 0;
+
+ const char *entry;
+ while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
+ rados_object_list_cursor cursor;
+ ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
+ string oid = entry;
+ cout << ": oid=" << oid << " cursor=" << cursor << std::endl;
+ cursor_to_obj[cursor] = oid;
+
+ rados_nobjects_list_seek_cursor(ctx, cursor);
+ cout << ": seek to " << cursor << std::endl;
+ ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
+ cout << "> " << cursor << " -> " << entry << std::endl;
+ ASSERT_EQ(string(entry), oid);
+ ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
+
+ ++count;
+ }
+
+ ASSERT_EQ(count, max_objs);
+
+ auto p = cursor_to_obj.rbegin();
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ while (p != cursor_to_obj.rend()) {
+ cout << ": seek to " << p->first << std::endl;
+ rados_object_list_cursor cursor;
+ rados_object_list_cursor oid(p->first);
+ rados_nobjects_list_seek_cursor(ctx, oid);
+ ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
+ cout << ": cursor()=" << cursor << " expected=" << oid << std::endl;
+ // ASSERT_EQ(ObjectCursor(oid), ObjectCursor(cursor));
+ ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
+ cout << "> " << cursor << " -> " << entry << std::endl;
+ cout << ": entry=" << entry << " expected=" << p->second << std::endl;
+ ASSERT_EQ(p->second, string(entry));
+
+ ++p;
+
+ rados_object_list_cursor_free(ctx, cursor);
+ }
+}
+
+TEST_F(LibRadosListEC, ListObjects) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ rados_list_ctx_t ctx;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ const char *entry;
+ bool foundit = false;
+ while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) != -ENOENT) {
+ foundit = true;
+ ASSERT_EQ(std::string(entry), "foo");
+ }
+ ASSERT_TRUE(foundit);
+ rados_nobjects_list_close(ctx);
+}
+
+TEST_F(LibRadosListEC, ListObjectsNS) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "ns1");
+ ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_write(ioctx, "foo2", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo3", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "ns1");
+ ASSERT_EQ(0, rados_write(ioctx, "foo4", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo5", buf, sizeof(buf), 0));
+ rados_ioctx_set_namespace(ioctx, "ns2");
+ ASSERT_EQ(0, rados_write(ioctx, "foo6", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo7", buf, sizeof(buf), 0));
+
+ std::set<std::string> def, ns1, ns2, all;
+ def.insert(std::string("foo1"));
+ def.insert(std::string("foo2"));
+ def.insert(std::string("foo3"));
+ ns1.insert(std::string("foo1"));
+ ns1.insert(std::string("foo4"));
+ ns1.insert(std::string("foo5"));
+ ns2.insert(std::string("foo6"));
+ ns2.insert(std::string("foo7"));
+ all.insert(std::string(":foo1"));
+ all.insert(std::string(":foo2"));
+ all.insert(std::string(":foo3"));
+ all.insert(std::string("ns1:foo1"));
+ all.insert(std::string("ns1:foo4"));
+ all.insert(std::string("ns1:foo5"));
+ all.insert(std::string("ns2:foo6"));
+ all.insert(std::string("ns2:foo7"));
+
+ rados_list_ctx_t ctx;
+ // Check default namespace ""
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(def, ctx, "");
+ rados_nobjects_list_close(ctx);
+
+ // Check default namespace "ns1"
+ rados_ioctx_set_namespace(ioctx, "ns1");
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(ns1, ctx, "ns1");
+ rados_nobjects_list_close(ctx);
+
+ // Check default namespace "ns2"
+ rados_ioctx_set_namespace(ioctx, "ns2");
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(ns2, ctx, "ns2");
+ rados_nobjects_list_close(ctx);
+
+ // Check all namespaces
+ rados_ioctx_set_namespace(ioctx, LIBRADOS_ALL_NSPACES);
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ check_list(all, ctx, all_nspaces);
+ rados_nobjects_list_close(ctx);
+}
+
+
+TEST_F(LibRadosListEC, ListObjectsStart) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+
+ for (int i=0; i<16; ++i) {
+ string n = stringify(i);
+ ASSERT_EQ(0, rados_write(ioctx, n.c_str(), buf, sizeof(buf), 0));
+ }
+
+ rados_list_ctx_t ctx;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ std::map<int, std::set<std::string> > pg_to_obj;
+ const char *entry;
+ while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
+ uint32_t pos = rados_nobjects_list_get_pg_hash_position(ctx);
+ std::cout << entry << " " << pos << std::endl;
+ pg_to_obj[pos].insert(entry);
+ }
+ rados_nobjects_list_close(ctx);
+
+ std::map<int, std::set<std::string> >::reverse_iterator p =
+ pg_to_obj.rbegin();
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ while (p != pg_to_obj.rend()) {
+ ASSERT_EQ((uint32_t)p->first, rados_nobjects_list_seek(ctx, p->first));
+ ASSERT_EQ(0, rados_nobjects_list_next(ctx, &entry, NULL, NULL));
+ std::cout << "have " << entry << " expect one of " << p->second << std::endl;
+ ASSERT_TRUE(p->second.count(entry));
+ ++p;
+ }
+ rados_nobjects_list_close(ctx);
+}
+
+TEST_F(LibRadosListNP, ListObjectsError) {
+ std::string pool_name;
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+
+ //ASSERT_EQ(0, rados_pool_delete(cluster, pool_name.c_str()));
+ {
+ char *buf, *st;
+ size_t buflen, stlen;
+ string c = "{\"prefix\":\"osd pool rm\",\"pool\": \"" + pool_name +
+ "\",\"pool2\":\"" + pool_name +
+ "\",\"yes_i_really_really_mean_it_not_faking\": true}";
+ const char *cmd[2] = { c.c_str(), 0 };
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
+ ASSERT_EQ(0, rados_wait_for_latest_osdmap(cluster));
+ }
+
+ rados_list_ctx_t ctx;
+ ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
+ const char *entry;
+ ASSERT_EQ(-ENOENT, rados_nobjects_list_next(ctx, &entry, NULL, NULL));
+ rados_nobjects_list_close(ctx);
+ rados_ioctx_destroy(ioctx);
+ rados_shutdown(cluster);
+}
+
+
+
+// ---------------------------------------------
+
+TEST_F(LibRadosList, EnumerateObjects) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+
+ const uint32_t n_objects = 16;
+ for (unsigned i=0; i<n_objects; ++i) {
+ ASSERT_EQ(0, rados_write(ioctx, stringify(i).c_str(), buf, sizeof(buf), 0));
+ }
+
+ // Ensure a non-power-of-two PG count to avoid only
+ // touching the easy path.
+ if (!is_crimson_cluster()) {
+ ASSERT_TRUE(set_pg_num(&s_cluster, pool_name, 11).empty());
+ ASSERT_TRUE(set_pgp_num(&s_cluster, pool_name, 11).empty());
+ }
+ std::set<std::string> saw_obj;
+ rados_object_list_cursor c = rados_object_list_begin(ioctx);
+ rados_object_list_cursor end = rados_object_list_end(ioctx);
+ while(!rados_object_list_is_end(ioctx, c))
+ {
+ rados_object_list_item results[12];
+ memset(results, 0, sizeof(rados_object_list_item) * 12);
+ rados_object_list_cursor temp_end = rados_object_list_end(ioctx);
+ int r = rados_object_list(ioctx, c, temp_end,
+ 12, NULL, 0, results, &c);
+ rados_object_list_cursor_free(ioctx, temp_end);
+ ASSERT_GE(r, 0);
+ for (int i = 0; i < r; ++i) {
+ std::string oid(results[i].oid, results[i].oid_length);
+ if (saw_obj.count(oid)) {
+ std::cerr << "duplicate obj " << oid << std::endl;
+ }
+ ASSERT_FALSE(saw_obj.count(oid));
+ saw_obj.insert(oid);
+ }
+ rados_object_list_free(12, results);
+ }
+ rados_object_list_cursor_free(ioctx, c);
+ rados_object_list_cursor_free(ioctx, end);
+
+ for (unsigned i=0; i<n_objects; ++i) {
+ if (!saw_obj.count(stringify(i))) {
+ std::cerr << "missing object " << i << std::endl;
+ }
+ ASSERT_TRUE(saw_obj.count(stringify(i)));
+ }
+ ASSERT_EQ(n_objects, saw_obj.size());
+}
+
+TEST_F(LibRadosList, EnumerateObjectsSplit) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+
+ const uint32_t n_objects = 16;
+ for (unsigned i=0; i<n_objects; ++i) {
+ ASSERT_EQ(0, rados_write(ioctx, stringify(i).c_str(), buf, sizeof(buf), 0));
+ }
+
+ // Ensure a non-power-of-two PG count to avoid only
+ // touching the easy path.
+ if (!is_crimson_cluster()) {
+ if (auto error = set_pg_num(&s_cluster, pool_name, 11); !error.empty()) {
+ GTEST_FAIL() << error;
+ }
+ if (auto error = set_pgp_num(&s_cluster, pool_name, 11); !error.empty()) {
+ GTEST_FAIL() << error;
+ }
+ }
+
+ rados_object_list_cursor begin = rados_object_list_begin(ioctx);
+ rados_object_list_cursor end = rados_object_list_end(ioctx);
+
+ // Step through an odd number of shards
+ unsigned m = 5;
+ std::set<std::string> saw_obj;
+ for (unsigned n = 0; n < m; ++n) {
+ rados_object_list_cursor shard_start = rados_object_list_begin(ioctx);;
+ rados_object_list_cursor shard_end = rados_object_list_end(ioctx);;
+
+ rados_object_list_slice(
+ ioctx,
+ begin,
+ end,
+ n,
+ m,
+ &shard_start,
+ &shard_end);
+ std::cout << "split " << n << "/" << m << " -> "
+ << *(hobject_t*)shard_start << " "
+ << *(hobject_t*)shard_end << std::endl;
+
+ rados_object_list_cursor c = shard_start;
+ //while(c < shard_end)
+ while(rados_object_list_cursor_cmp(ioctx, c, shard_end) == -1)
+ {
+ rados_object_list_item results[12];
+ memset(results, 0, sizeof(rados_object_list_item) * 12);
+ int r = rados_object_list(ioctx,
+ c, shard_end,
+ 12, NULL, 0, results, &c);
+ ASSERT_GE(r, 0);
+ for (int i = 0; i < r; ++i) {
+ std::string oid(results[i].oid, results[i].oid_length);
+ if (saw_obj.count(oid)) {
+ std::cerr << "duplicate obj " << oid << std::endl;
+ }
+ ASSERT_FALSE(saw_obj.count(oid));
+ saw_obj.insert(oid);
+ }
+ rados_object_list_free(12, results);
+ }
+ rados_object_list_cursor_free(ioctx, shard_start);
+ rados_object_list_cursor_free(ioctx, shard_end);
+ }
+
+ rados_object_list_cursor_free(ioctx, begin);
+ rados_object_list_cursor_free(ioctx, end);
+
+ for (unsigned i=0; i<n_objects; ++i) {
+ if (!saw_obj.count(stringify(i))) {
+ std::cerr << "missing object " << i << std::endl;
+ }
+ ASSERT_TRUE(saw_obj.count(stringify(i)));
+ }
+ ASSERT_EQ(n_objects, saw_obj.size());
+}
diff --git a/src/test/librados/list_cxx.cc b/src/test/librados/list_cxx.cc
new file mode 100644
index 000000000..112231302
--- /dev/null
+++ b/src/test/librados/list_cxx.cc
@@ -0,0 +1,782 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <errno.h>
+#include <string>
+#include <stdexcept>
+
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "include/types.h"
+#include "common/hobject.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/test_common.h"
+#include "test/librados/testcase_cxx.h"
+#include "global/global_context.h"
+
+#include "crimson_utils.h"
+
+using namespace librados;
+
+typedef RadosTestPPNSCleanup LibRadosListPP;
+typedef RadosTestECPPNSCleanup LibRadosListECPP;
+
+TEST_F(LibRadosListPP, ListObjectsPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ NObjectIterator iter(ioctx.nobjects_begin());
+ bool foundit = false;
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ }
+ ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListPP, ListObjectsTwicePP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ NObjectIterator iter(ioctx.nobjects_begin());
+ bool foundit = false;
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ }
+ ASSERT_TRUE(foundit);
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+ foundit = false;
+ iter.seek(0);
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ }
+ ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListPP, ListObjectsCopyIterPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ // make sure this is still valid after the original iterators are gone
+ NObjectIterator iter3;
+ {
+ NObjectIterator iter(ioctx.nobjects_begin());
+ NObjectIterator iter2(iter);
+ iter3 = iter2;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+
+ ASSERT_EQ(iter2->get_oid(), "foo");
+ ASSERT_EQ(iter3->get_oid(), "foo");
+ ++iter2;
+ ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+ }
+
+ ASSERT_EQ(iter3->get_oid(), "foo");
+ iter3 = iter3;
+ ASSERT_EQ(iter3->get_oid(), "foo");
+ ++iter3;
+ ASSERT_TRUE(iter3 == ioctx.nobjects_end());
+}
+
+TEST_F(LibRadosListPP, ListObjectsEndIter) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ NObjectIterator iter(ioctx.nobjects_begin());
+ NObjectIterator iter_end(ioctx.nobjects_end());
+ NObjectIterator iter_end2 = ioctx.nobjects_end();
+ ASSERT_TRUE(iter_end == iter_end2);
+ ASSERT_TRUE(iter_end == ioctx.nobjects_end());
+ ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
+
+ ASSERT_EQ(iter->get_oid(), "foo");
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+ ASSERT_TRUE(iter == iter_end);
+ ASSERT_TRUE(iter == iter_end2);
+ NObjectIterator iter2 = iter;
+ ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+ ASSERT_TRUE(iter2 == iter_end);
+ ASSERT_TRUE(iter2 == iter_end2);
+}
+
+static void check_listpp(std::set<std::string>& myset, IoCtx& ioctx, const std::string &check_nspace)
+{
+ NObjectIterator iter(ioctx.nobjects_begin());
+ std::set<std::string> orig_set(myset);
+ /**
+ * During splitting, we might see duplicate items.
+ * We assert that every object returned is in myset and that
+ * we don't hit ENOENT until we have hit every item in myset
+ * at least once.
+ */
+ while (iter != ioctx.nobjects_end()) {
+ std::string test_name;
+ if (check_nspace == all_nspaces) {
+ test_name = iter->get_nspace() + ":" + iter->get_oid();
+ } else {
+ ASSERT_TRUE(iter->get_nspace() == check_nspace);
+ test_name = iter->get_oid();
+ }
+ ASSERT_TRUE(orig_set.end() != orig_set.find(test_name));
+ myset.erase(test_name);
+ ++iter;
+ }
+ ASSERT_TRUE(myset.empty());
+}
+
+TEST_F(LibRadosListPP, ListObjectsPPNS) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("ns1");
+ ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("ns1");
+ ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("ns2");
+ ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
+ ASSERT_EQ(std::string("ns2"), ioctx.get_namespace());
+
+ std::set<std::string> def, ns1, ns2, all;
+ def.insert(std::string("foo1"));
+ def.insert(std::string("foo2"));
+ def.insert(std::string("foo3"));
+ ns1.insert(std::string("foo1"));
+ ns1.insert(std::string("foo4"));
+ ns1.insert(std::string("foo5"));
+ ns2.insert(std::string("foo6"));
+ ns2.insert(std::string("foo7"));
+ all.insert(std::string(":foo1"));
+ all.insert(std::string(":foo2"));
+ all.insert(std::string(":foo3"));
+ all.insert(std::string("ns1:foo1"));
+ all.insert(std::string("ns1:foo4"));
+ all.insert(std::string("ns1:foo5"));
+ all.insert(std::string("ns2:foo6"));
+ all.insert(std::string("ns2:foo7"));
+
+ ioctx.set_namespace("");
+ check_listpp(def, ioctx, "");
+
+ ioctx.set_namespace("ns1");
+ check_listpp(ns1, ioctx, "ns1");
+
+ ioctx.set_namespace("ns2");
+ check_listpp(ns2, ioctx, "ns2");
+
+ ioctx.set_namespace(all_nspaces);
+ check_listpp(all, ioctx, all_nspaces);
+}
+
+TEST_F(LibRadosListPP, ListObjectsManyPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ for (int i=0; i<256; ++i) {
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+ }
+
+ librados::NObjectIterator it = ioctx.nobjects_begin();
+ std::set<std::string> saw_obj;
+ std::set<int> saw_pg;
+ for (; it != ioctx.nobjects_end(); ++it) {
+ std::cout << it->get_oid()
+ << " " << it.get_pg_hash_position() << std::endl;
+ saw_obj.insert(it->get_oid());
+ saw_pg.insert(it.get_pg_hash_position());
+ }
+ std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
+
+ // make sure they are 0..n
+ for (unsigned i = 0; i < saw_pg.size(); ++i)
+ ASSERT_TRUE(saw_pg.count(i));
+}
+
+TEST_F(LibRadosListPP, ListObjectsStartPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ for (int i=0; i<16; ++i) {
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+ }
+
+ librados::NObjectIterator it = ioctx.nobjects_begin();
+ std::map<int, std::set<std::string> > pg_to_obj;
+ for (; it != ioctx.nobjects_end(); ++it) {
+ std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
+ pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
+ }
+
+ std::map<int, std::set<std::string> >::reverse_iterator p =
+ pg_to_obj.rbegin();
+ it = ioctx.nobjects_begin(p->first);
+ while (p != pg_to_obj.rend()) {
+ ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
+ std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
+ ASSERT_TRUE(p->second.count(it->get_oid()));
+ ++p;
+ }
+}
+
+TEST_F(LibRadosListPP, ListObjectsCursorNSPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ const int max_objs = 16;
+
+ map<string, string> oid_to_ns;
+
+ for (int i=0; i<max_objs; ++i) {
+ stringstream ss;
+ ss << "ns" << i / 4;
+ ioctx.set_namespace(ss.str());
+ string oid = stringify(i);
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ oid_to_ns[oid] = ss.str();
+ }
+
+ ioctx.set_namespace(all_nspaces);
+
+ librados::NObjectIterator it = ioctx.nobjects_begin();
+ std::map<librados::ObjectCursor, string> cursor_to_obj;
+
+ int count = 0;
+
+ librados::ObjectCursor seek_cursor;
+
+ map<string, list<librados::ObjectCursor> > ns_to_cursors;
+
+ for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it) {
+ librados::ObjectCursor cursor = it.get_cursor();
+ string oid = it->get_oid();
+ cout << "> oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
+ }
+
+ vector<string> objs_order;
+
+ for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it, ++count) {
+ librados::ObjectCursor cursor = it.get_cursor();
+ string oid = it->get_oid();
+ std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
+ cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
+ cursor_to_obj[cursor] = oid;
+
+ ASSERT_EQ(oid_to_ns[oid], it->get_nspace());
+
+ it.seek(cursor);
+ cout << ": seek to " << cursor << " it.cursor=" << it.get_cursor() << std::endl;
+ ASSERT_EQ(oid, it->get_oid());
+ ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
+
+ ns_to_cursors[it->get_nspace()].push_back(cursor);
+
+ if (count == max_objs/2) {
+ seek_cursor = cursor;
+ }
+ objs_order.push_back(it->get_oid());
+ }
+
+ ASSERT_EQ(count, max_objs);
+
+ /* check that reading past seek also works */
+ cout << "seek_cursor=" << seek_cursor << std::endl;
+ it.seek(seek_cursor);
+ for (count = max_objs/2; count < max_objs; ++count, ++it) {
+ ASSERT_EQ(objs_order[count], it->get_oid());
+ }
+
+ /* seek to all cursors, check that we get expected obj */
+ for (auto& niter : ns_to_cursors) {
+ const string& ns = niter.first;
+ list<librados::ObjectCursor>& cursors = niter.second;
+
+ for (auto& cursor : cursors) {
+ cout << ": seek to " << cursor << std::endl;
+ it.seek(cursor);
+ ASSERT_EQ(cursor, it.get_cursor());
+ string& expected_oid = cursor_to_obj[cursor];
+ cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << cursor << std::endl;
+ cout << ": it->get_oid()=" << it->get_oid() << " expected=" << expected_oid << std::endl;
+ cout << ": it->get_nspace()=" << it->get_oid() << " expected=" << ns << std::endl;
+ ASSERT_EQ(expected_oid, it->get_oid());
+ ASSERT_EQ(it->get_nspace(), ns);
+ }
+ }
+}
+
+TEST_F(LibRadosListPP, ListObjectsCursorPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ const int max_objs = 16;
+
+ for (int i=0; i<max_objs; ++i) {
+ stringstream ss;
+ ss << "ns" << i / 4;
+ ioctx.set_namespace(ss.str());
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+ }
+
+ ioctx.set_namespace(all_nspaces);
+
+ librados::NObjectIterator it = ioctx.nobjects_begin();
+ std::map<librados::ObjectCursor, string> cursor_to_obj;
+
+ int count = 0;
+
+ for (; it != ioctx.nobjects_end(); ++it, ++count) {
+ librados::ObjectCursor cursor = it.get_cursor();
+ string oid = it->get_oid();
+ std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
+ cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
+ cursor_to_obj[cursor] = oid;
+
+ it.seek(cursor);
+ cout << ": seek to " << cursor << std::endl;
+ ASSERT_EQ(oid, it->get_oid());
+ ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
+ }
+
+ ASSERT_EQ(count, max_objs);
+
+ auto p = cursor_to_obj.rbegin();
+ it = ioctx.nobjects_begin();
+ while (p != cursor_to_obj.rend()) {
+ cout << ": seek to " << p->first << std::endl;
+ it.seek(p->first);
+ ASSERT_EQ(p->first, it.get_cursor());
+ cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << p->first << std::endl;
+ cout << ": it->get_oid()=" << it->get_oid() << " expected=" << p->second << std::endl;
+ ASSERT_EQ(p->second, it->get_oid());
+
+ librados::NObjectIterator it2 = ioctx.nobjects_begin(it.get_cursor());
+ ASSERT_EQ(it2->get_oid(), it->get_oid());
+
+ ++p;
+ }
+}
+
+TEST_F(LibRadosListECPP, ListObjectsPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ NObjectIterator iter(ioctx.nobjects_begin());
+ bool foundit = false;
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ }
+ ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListECPP, ListObjectsTwicePP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ NObjectIterator iter(ioctx.nobjects_begin());
+ bool foundit = false;
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ }
+ ASSERT_TRUE(foundit);
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+ foundit = false;
+ iter.seek(0);
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ }
+ ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListECPP, ListObjectsCopyIterPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ // make sure this is still valid after the original iterators are gone
+ NObjectIterator iter3;
+ {
+ NObjectIterator iter(ioctx.nobjects_begin());
+ NObjectIterator iter2(iter);
+ iter3 = iter2;
+ ASSERT_EQ((*iter).get_oid(), "foo");
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+
+ ASSERT_EQ(iter2->get_oid(), "foo");
+ ASSERT_EQ(iter3->get_oid(), "foo");
+ ++iter2;
+ ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+ }
+
+ ASSERT_EQ(iter3->get_oid(), "foo");
+ iter3 = iter3;
+ ASSERT_EQ(iter3->get_oid(), "foo");
+ ++iter3;
+ ASSERT_TRUE(iter3 == ioctx.nobjects_end());
+}
+
+TEST_F(LibRadosListECPP, ListObjectsEndIter) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ NObjectIterator iter(ioctx.nobjects_begin());
+ NObjectIterator iter_end(ioctx.nobjects_end());
+ NObjectIterator iter_end2 = ioctx.nobjects_end();
+ ASSERT_TRUE(iter_end == iter_end2);
+ ASSERT_TRUE(iter_end == ioctx.nobjects_end());
+ ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
+
+ ASSERT_EQ(iter->get_oid(), "foo");
+ ++iter;
+ ASSERT_TRUE(iter == ioctx.nobjects_end());
+ ASSERT_TRUE(iter == iter_end);
+ ASSERT_TRUE(iter == iter_end2);
+ NObjectIterator iter2 = iter;
+ ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+ ASSERT_TRUE(iter2 == iter_end);
+ ASSERT_TRUE(iter2 == iter_end2);
+}
+
+TEST_F(LibRadosListECPP, ListObjectsPPNS) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("ns1");
+ ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("ns1");
+ ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
+ ioctx.set_namespace("ns2");
+ ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
+
+ std::set<std::string> def, ns1, ns2;
+ def.insert(std::string("foo1"));
+ def.insert(std::string("foo2"));
+ def.insert(std::string("foo3"));
+ ns1.insert(std::string("foo1"));
+ ns1.insert(std::string("foo4"));
+ ns1.insert(std::string("foo5"));
+ ns2.insert(std::string("foo6"));
+ ns2.insert(std::string("foo7"));
+
+ ioctx.set_namespace("");
+ check_listpp(def, ioctx, "");
+
+ ioctx.set_namespace("ns1");
+ check_listpp(ns1, ioctx, "ns1");
+
+ ioctx.set_namespace("ns2");
+ check_listpp(ns2, ioctx, "ns2");
+}
+
+TEST_F(LibRadosListECPP, ListObjectsManyPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ for (int i=0; i<256; ++i) {
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+ }
+
+ librados::NObjectIterator it = ioctx.nobjects_begin();
+ std::set<std::string> saw_obj;
+ std::set<int> saw_pg;
+ for (; it != ioctx.nobjects_end(); ++it) {
+ std::cout << it->get_oid()
+ << " " << it.get_pg_hash_position() << std::endl;
+ saw_obj.insert(it->get_oid());
+ saw_pg.insert(it.get_pg_hash_position());
+ }
+ std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
+
+ // make sure they are 0..n
+ for (unsigned i = 0; i < saw_pg.size(); ++i)
+ ASSERT_TRUE(saw_pg.count(i));
+}
+
+TEST_F(LibRadosListECPP, ListObjectsStartPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ for (int i=0; i<16; ++i) {
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+ }
+
+ librados::NObjectIterator it = ioctx.nobjects_begin();
+ std::map<int, std::set<std::string> > pg_to_obj;
+ for (; it != ioctx.nobjects_end(); ++it) {
+ std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
+ pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
+ }
+
+ std::map<int, std::set<std::string> >::reverse_iterator p =
+ pg_to_obj.rbegin();
+ it = ioctx.nobjects_begin(p->first);
+ while (p != pg_to_obj.rend()) {
+ ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
+ std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
+ ASSERT_TRUE(p->second.count(it->get_oid()));
+ ++p;
+ }
+}
+
+TEST_F(LibRadosListPP, ListObjectsFilterPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist obj_content;
+ obj_content.append(buf, sizeof(buf));
+
+ std::string target_str = "content";
+
+ // Write xattr bare, no ::encod'ing
+ bufferlist target_val;
+ target_val.append(target_str);
+ bufferlist nontarget_val;
+ nontarget_val.append("rhubarb");
+
+ ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
+ ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
+ ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
+
+ ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
+ ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
+
+ bufferlist filter_bl;
+ std::string filter_name = "plain";
+ encode(filter_name, filter_bl);
+ encode("_theattr", filter_bl);
+ encode(target_str, filter_bl);
+
+ NObjectIterator iter(ioctx.nobjects_begin(filter_bl));
+ bool foundit = false;
+ int k = 0;
+ while (iter != ioctx.nobjects_end()) {
+ foundit = true;
+ // We should only see the object that matches the filter
+ ASSERT_EQ((*iter).get_oid(), "has_xattr");
+ // We should only see it once
+ ASSERT_EQ(k, 0);
+ ++iter;
+ ++k;
+ }
+ ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListPP, EnumerateObjectsPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ const uint32_t n_objects = 16;
+ for (unsigned i=0; i<n_objects; ++i) {
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
+ }
+
+ std::set<std::string> saw_obj;
+ ObjectCursor c = ioctx.object_list_begin();
+ ObjectCursor end = ioctx.object_list_end();
+ while(!ioctx.object_list_is_end(c))
+ {
+ std::vector<ObjectItem> result;
+ int r = ioctx.object_list(c, end, 12, {}, &result, &c);
+ ASSERT_GE(r, 0);
+ ASSERT_EQ(r, (int)result.size());
+ for (int i = 0; i < r; ++i) {
+ auto oid = result[i].oid;
+ if (saw_obj.count(oid)) {
+ std::cerr << "duplicate obj " << oid << std::endl;
+ }
+ ASSERT_FALSE(saw_obj.count(oid));
+ saw_obj.insert(oid);
+ }
+ }
+
+ for (unsigned i=0; i<n_objects; ++i) {
+ if (!saw_obj.count(stringify(i))) {
+ std::cerr << "missing object " << i << std::endl;
+ }
+ ASSERT_TRUE(saw_obj.count(stringify(i)));
+ }
+ ASSERT_EQ(n_objects, saw_obj.size());
+}
+
+TEST_F(LibRadosListPP, EnumerateObjectsSplitPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ const uint32_t n_objects = 16;
+ for (unsigned i=0; i<n_objects; ++i) {
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
+ }
+
+ ObjectCursor begin = ioctx.object_list_begin();
+ ObjectCursor end = ioctx.object_list_end();
+
+ // Step through an odd number of shards
+ unsigned m = 5;
+ std::set<std::string> saw_obj;
+ for (unsigned n = 0; n < m; ++n) {
+ ObjectCursor shard_start;
+ ObjectCursor shard_end;
+
+ ioctx.object_list_slice(
+ begin,
+ end,
+ n,
+ m,
+ &shard_start,
+ &shard_end);
+
+ ObjectCursor c(shard_start);
+ while(c < shard_end)
+ {
+ std::vector<ObjectItem> result;
+ int r = ioctx.object_list(c, shard_end, 12, {}, &result, &c);
+ ASSERT_GE(r, 0);
+
+ for (const auto & i : result) {
+ const auto &oid = i.oid;
+ if (saw_obj.count(oid)) {
+ std::cerr << "duplicate obj " << oid << std::endl;
+ }
+ ASSERT_FALSE(saw_obj.count(oid));
+ saw_obj.insert(oid);
+ }
+ }
+ }
+
+ for (unsigned i=0; i<n_objects; ++i) {
+ if (!saw_obj.count(stringify(i))) {
+ std::cerr << "missing object " << i << std::endl;
+ }
+ ASSERT_TRUE(saw_obj.count(stringify(i)));
+ }
+ ASSERT_EQ(n_objects, saw_obj.size());
+}
+
+
+TEST_F(LibRadosListPP, EnumerateObjectsFilterPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist obj_content;
+ obj_content.append(buf, sizeof(buf));
+
+ std::string target_str = "content";
+
+ // Write xattr bare, no ::encod'ing
+ bufferlist target_val;
+ target_val.append(target_str);
+ bufferlist nontarget_val;
+ nontarget_val.append("rhubarb");
+
+ ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
+ ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
+ ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
+
+ ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
+ ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
+
+ bufferlist filter_bl;
+ std::string filter_name = "plain";
+ encode(filter_name, filter_bl);
+ encode("_theattr", filter_bl);
+ encode(target_str, filter_bl);
+
+ ObjectCursor c = ioctx.object_list_begin();
+ ObjectCursor end = ioctx.object_list_end();
+ bool foundit = false;
+ while(!ioctx.object_list_is_end(c))
+ {
+ std::vector<ObjectItem> result;
+ int r = ioctx.object_list(c, end, 12, filter_bl, &result, &c);
+ ASSERT_GE(r, 0);
+ ASSERT_EQ(r, (int)result.size());
+ for (int i = 0; i < r; ++i) {
+ auto oid = result[i].oid;
+ // We should only see the object that matches the filter
+ ASSERT_EQ(oid, "has_xattr");
+ // We should only see it once
+ ASSERT_FALSE(foundit);
+ foundit = true;
+ }
+ }
+ ASSERT_TRUE(foundit);
+}
diff --git a/src/test/librados/lock.cc b/src/test/librados/lock.cc
new file mode 100644
index 000000000..a6ac36365
--- /dev/null
+++ b/src/test/librados/lock.cc
@@ -0,0 +1,237 @@
+#include "include/rados/librados.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+#include "cls/lock/cls_lock_client.h"
+
+#include <algorithm>
+#include <chrono>
+#include <thread>
+#include <errno.h>
+#include "gtest/gtest.h"
+#include <sys/time.h>
+
+#include "crimson_utils.h"
+
+using namespace std::chrono_literals;
+
+typedef RadosTest LibRadosLock;
+typedef RadosTestEC LibRadosLockEC;
+
+
+TEST_F(LibRadosLock, LockExclusive) {
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock1", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, rados_lock_exclusive(ioctx, "foo", "TestLock1", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLock, LockShared) {
+ ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLock2", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, rados_lock_shared(ioctx, "foo", "TestLock2", "Cookie", "Tag", "", NULL, 0));
+}
+
+TEST_F(LibRadosLock, LockExclusiveDur) {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_exclusive = [this](timeval* tv) {
+ return rados_lock_exclusive(ioctx, "foo", "TestLock3", "Cookie", "", tv, 0);
+ };
+ constexpr int expected = 0;
+ ASSERT_EQ(expected, lock_exclusive(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
+}
+
+TEST_F(LibRadosLock, LockSharedDur) {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_shared = [this](timeval* tv) {
+ return rados_lock_shared(ioctx, "foo", "TestLock4", "Cookie", "Tag", "", tv, 0);
+ };
+ constexpr int expected = 0;
+ ASSERT_EQ(expected, lock_shared(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
+}
+
+
+TEST_F(LibRadosLock, LockMayRenew) {
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, rados_lock_exclusive(ioctx, "foo", "TestLock5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
+}
+
+TEST_F(LibRadosLock, Unlock) {
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock6", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, rados_unlock(ioctx, "foo", "TestLock6", "Cookie"));
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock6", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLock, ListLockers) {
+ int exclusive;
+ char tag[1024];
+ char clients[1024];
+ char cookies[1024];
+ char addresses[1024];
+ size_t tag_len = 1024;
+ size_t clients_len = 1024;
+ size_t cookies_len = 1024;
+ size_t addresses_len = 1024;
+ std::stringstream sstm;
+ sstm << "client." << rados_get_instance_id(cluster);
+ std::string me = sstm.str();
+ ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLock7", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(0, rados_unlock(ioctx, "foo", "TestLock7", "Cookie"));
+ ASSERT_EQ(0, rados_list_lockers(ioctx, "foo", "TestLock7", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLock7", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(-34, rados_list_lockers(ioctx, "foo", "TestLock7", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ tag_len = 1024;
+ clients_len = 1024;
+ cookies_len = 1024;
+ addresses_len = 1024;
+ ASSERT_EQ(1, rados_list_lockers(ioctx, "foo", "TestLock7", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ ASSERT_EQ(0, exclusive);
+ ASSERT_EQ(0, strcmp(tag, "Tag"));
+ ASSERT_EQ(strlen("Tag") + 1, tag_len);
+ ASSERT_EQ(0, strcmp(me.c_str(), clients));
+ ASSERT_EQ(me.size() + 1, clients_len);
+ ASSERT_EQ(0, strcmp(cookies, "Cookie"));
+ ASSERT_EQ(strlen("Cookie") + 1, cookies_len);
+}
+
+TEST_F(LibRadosLock, BreakLock) {
+ int exclusive;
+ char tag[1024];
+ char clients[1024];
+ char cookies[1024];
+ char addresses[1024];
+ size_t tag_len = 1024;
+ size_t clients_len = 1024;
+ size_t cookies_len = 1024;
+ size_t addresses_len = 1024;
+ std::stringstream sstm;
+ sstm << "client." << rados_get_instance_id(cluster);
+ std::string me = sstm.str();
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock8", "Cookie", "", NULL, 0));
+ ASSERT_EQ(1, rados_list_lockers(ioctx, "foo", "TestLock8", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ ASSERT_EQ(1, exclusive);
+ ASSERT_EQ(0, strcmp(tag, ""));
+ ASSERT_EQ(1U, tag_len);
+ ASSERT_EQ(0, strcmp(me.c_str(), clients));
+ ASSERT_EQ(me.size() + 1, clients_len);
+ ASSERT_EQ(0, strcmp(cookies, "Cookie"));
+ ASSERT_EQ(strlen("Cookie") + 1, cookies_len);
+ ASSERT_EQ(0, rados_break_lock(ioctx, "foo", "TestLock8", clients, "Cookie"));
+}
+
+// EC testing
+TEST_F(LibRadosLockEC, LockExclusive) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC1", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, rados_lock_exclusive(ioctx, "foo", "TestLockEC1", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockEC, LockShared) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLockEC2", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, rados_lock_shared(ioctx, "foo", "TestLockEC2", "Cookie", "Tag", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockEC, LockExclusiveDur) {
+ SKIP_IF_CRIMSON();
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_exclusive = [this](timeval* tv) {
+ return rados_lock_exclusive(ioctx, "foo", "TestLockEC3", "Cookie", "", tv, 0);
+ };
+ constexpr int expected = 0;
+ ASSERT_EQ(expected, lock_exclusive(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
+}
+
+TEST_F(LibRadosLockEC, LockSharedDur) {
+ SKIP_IF_CRIMSON();
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_shared = [this](timeval* tv) {
+ return rados_lock_shared(ioctx, "foo", "TestLockEC4", "Cookie", "Tag", "", tv, 0);
+ };
+ constexpr int expected = 0;
+ ASSERT_EQ(expected, lock_shared(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
+}
+
+
+TEST_F(LibRadosLockEC, LockMayRenew) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, rados_lock_exclusive(ioctx, "foo", "TestLockEC5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
+}
+
+TEST_F(LibRadosLockEC, Unlock) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC6", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, rados_unlock(ioctx, "foo", "TestLockEC6", "Cookie"));
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC6", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockEC, ListLockers) {
+ SKIP_IF_CRIMSON();
+ int exclusive;
+ char tag[1024];
+ char clients[1024];
+ char cookies[1024];
+ char addresses[1024];
+ size_t tag_len = 1024;
+ size_t clients_len = 1024;
+ size_t cookies_len = 1024;
+ size_t addresses_len = 1024;
+ std::stringstream sstm;
+ sstm << "client." << rados_get_instance_id(cluster);
+ std::string me = sstm.str();
+ ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLockEC7", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(0, rados_unlock(ioctx, "foo", "TestLockEC7", "Cookie"));
+ ASSERT_EQ(0, rados_list_lockers(ioctx, "foo", "TestLockEC7", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLockEC7", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(-34, rados_list_lockers(ioctx, "foo", "TestLockEC7", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ tag_len = 1024;
+ clients_len = 1024;
+ cookies_len = 1024;
+ addresses_len = 1024;
+ ASSERT_EQ(1, rados_list_lockers(ioctx, "foo", "TestLockEC7", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ ASSERT_EQ(0, exclusive);
+ ASSERT_EQ(0, strcmp(tag, "Tag"));
+ ASSERT_EQ(strlen("Tag") + 1, tag_len);
+ ASSERT_EQ(0, strcmp(me.c_str(), clients));
+ ASSERT_EQ(me.size() + 1, clients_len);
+ ASSERT_EQ(0, strcmp(cookies, "Cookie"));
+ ASSERT_EQ(strlen("Cookie") + 1, cookies_len);
+}
+
+TEST_F(LibRadosLockEC, BreakLock) {
+ SKIP_IF_CRIMSON();
+ int exclusive;
+ char tag[1024];
+ char clients[1024];
+ char cookies[1024];
+ char addresses[1024];
+ size_t tag_len = 1024;
+ size_t clients_len = 1024;
+ size_t cookies_len = 1024;
+ size_t addresses_len = 1024;
+ std::stringstream sstm;
+ sstm << "client." << rados_get_instance_id(cluster);
+ std::string me = sstm.str();
+ ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC8", "Cookie", "", NULL, 0));
+ ASSERT_EQ(1, rados_list_lockers(ioctx, "foo", "TestLockEC8", &exclusive, tag, &tag_len, clients, &clients_len, cookies, &cookies_len, addresses, &addresses_len ));
+ ASSERT_EQ(1, exclusive);
+ ASSERT_EQ(0, strcmp(tag, ""));
+ ASSERT_EQ(1U, tag_len);
+ ASSERT_EQ(0, strcmp(me.c_str(), clients));
+ ASSERT_EQ(me.size() + 1, clients_len);
+ ASSERT_EQ(0, strcmp(cookies, "Cookie"));
+ ASSERT_EQ(strlen("Cookie") + 1, cookies_len);
+ ASSERT_EQ(0, rados_break_lock(ioctx, "foo", "TestLockEC8", clients, "Cookie"));
+}
+
diff --git a/src/test/librados/lock_cxx.cc b/src/test/librados/lock_cxx.cc
new file mode 100644
index 000000000..0267ea938
--- /dev/null
+++ b/src/test/librados/lock_cxx.cc
@@ -0,0 +1,203 @@
+#include <algorithm>
+#include <chrono>
+#include <thread>
+#include <errno.h>
+#include <sys/time.h>
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "cls/lock/cls_lock_client.h"
+
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+#include "crimson_utils.h"
+
+using namespace std::chrono_literals;
+using namespace librados;
+
+typedef RadosTestPP LibRadosLockPP;
+typedef RadosTestECPP LibRadosLockECPP;
+
+TEST_F(LibRadosLockPP, LockExclusivePP) {
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP1", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockPP1", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockPP, LockSharedPP) {
+ ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP2", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, ioctx.lock_shared("foo", "TestLockPP2", "Cookie", "Tag", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockPP, LockExclusiveDurPP) {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_exclusive = [this](timeval* tv) {
+ return ioctx.lock_exclusive("foo", "TestLockPP3", "Cookie", "", tv, 0);
+ };
+ constexpr int expected = 0;
+ ASSERT_EQ(expected, lock_exclusive(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
+}
+
+TEST_F(LibRadosLockPP, LockSharedDurPP) {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_shared = [this](timeval* tv) {
+ return ioctx.lock_shared("foo", "TestLockPP4", "Cookie", "Tag", "", tv, 0);
+ };
+ constexpr int expected = 0;
+ ASSERT_EQ(expected, lock_shared(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
+}
+
+TEST_F(LibRadosLockPP, LockMayRenewPP) {
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
+}
+
+TEST_F(LibRadosLockPP, UnlockPP) {
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP6", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, ioctx.unlock("foo", "TestLockPP6", "Cookie"));
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP6", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockPP, ListLockersPP) {
+ std::stringstream sstm;
+ sstm << "client." << cluster.get_instance_id();
+ std::string me = sstm.str();
+ ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP7", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(0, ioctx.unlock("foo", "TestLockPP7", "Cookie"));
+ {
+ int exclusive;
+ std::string tag;
+ std::list<librados::locker_t> lockers;
+ ASSERT_EQ(0, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers));
+ }
+ ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP7", "Cookie", "Tag", "", NULL, 0));
+ {
+ int exclusive;
+ std::string tag;
+ std::list<librados::locker_t> lockers;
+ ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers));
+ std::list<librados::locker_t>::iterator it = lockers.begin();
+ ASSERT_FALSE(lockers.end() == it);
+ ASSERT_EQ(me, it->client);
+ ASSERT_EQ("Cookie", it->cookie);
+ }
+}
+
+TEST_F(LibRadosLockPP, BreakLockPP) {
+ int exclusive;
+ std::string tag;
+ std::list<librados::locker_t> lockers;
+ std::stringstream sstm;
+ sstm << "client." << cluster.get_instance_id();
+ std::string me = sstm.str();
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP8", "Cookie", "", NULL, 0));
+ ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP8", &exclusive, &tag, &lockers));
+ std::list<librados::locker_t>::iterator it = lockers.begin();
+ ASSERT_FALSE(lockers.end() == it);
+ ASSERT_EQ(me, it->client);
+ ASSERT_EQ("Cookie", it->cookie);
+ ASSERT_EQ(0, ioctx.break_lock("foo", "TestLockPP8", it->client, "Cookie"));
+}
+
+// EC testing
+TEST_F(LibRadosLockECPP, LockExclusivePP) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockECPP, LockSharedPP) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockECPP, LockExclusiveDurPP) {
+ SKIP_IF_CRIMSON();
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_exclusive = [this](timeval* tv) {
+ return ioctx.lock_exclusive("foo", "TestLockECPP3", "Cookie", "", tv, 0);
+ };
+ constexpr int expected = 0;
+ ASSERT_EQ(expected, lock_exclusive(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
+}
+
+TEST_F(LibRadosLockECPP, LockSharedDurPP) {
+ SKIP_IF_CRIMSON();
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ auto lock_shared = [this](timeval* tv) {
+ return ioctx.lock_shared("foo", "TestLockECPP4", "Cookie", "Tag", "", tv, 0);
+ };
+ const int expected = 0;
+ ASSERT_EQ(expected, lock_shared(&tv));
+ ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
+}
+
+TEST_F(LibRadosLockECPP, LockMayRenewPP) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
+}
+
+TEST_F(LibRadosLockECPP, UnlockPP) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
+ ASSERT_EQ(0, ioctx.unlock("foo", "TestLockECPP6", "Cookie"));
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockECPP, ListLockersPP) {
+ SKIP_IF_CRIMSON();
+ std::stringstream sstm;
+ sstm << "client." << cluster.get_instance_id();
+ std::string me = sstm.str();
+ ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP7", "Cookie", "Tag", "", NULL, 0));
+ ASSERT_EQ(0, ioctx.unlock("foo", "TestLockECPP7", "Cookie"));
+ {
+ int exclusive;
+ std::string tag;
+ std::list<librados::locker_t> lockers;
+ ASSERT_EQ(0, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers));
+ }
+ ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP7", "Cookie", "Tag", "", NULL, 0));
+ {
+ int exclusive;
+ std::string tag;
+ std::list<librados::locker_t> lockers;
+ ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers));
+ std::list<librados::locker_t>::iterator it = lockers.begin();
+ ASSERT_FALSE(lockers.end() == it);
+ ASSERT_EQ(me, it->client);
+ ASSERT_EQ("Cookie", it->cookie);
+ }
+}
+
+TEST_F(LibRadosLockECPP, BreakLockPP) {
+ SKIP_IF_CRIMSON();
+ int exclusive;
+ std::string tag;
+ std::list<librados::locker_t> lockers;
+ std::stringstream sstm;
+ sstm << "client." << cluster.get_instance_id();
+ std::string me = sstm.str();
+ ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP8", "Cookie", "", NULL, 0));
+ ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP8", &exclusive, &tag, &lockers));
+ std::list<librados::locker_t>::iterator it = lockers.begin();
+ ASSERT_FALSE(lockers.end() == it);
+ ASSERT_EQ(me, it->client);
+ ASSERT_EQ("Cookie", it->cookie);
+ ASSERT_EQ(0, ioctx.break_lock("foo", "TestLockECPP8", it->client, "Cookie"));
+}
diff --git a/src/test/librados/misc.cc b/src/test/librados/misc.cc
new file mode 100644
index 000000000..d9cb1c5b8
--- /dev/null
+++ b/src/test/librados/misc.cc
@@ -0,0 +1,358 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "mds/mdstypes.h"
+#include "include/err.h"
+#include "include/buffer.h"
+#include "include/rbd_types.h"
+#include "include/rados.h"
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/scope_guard.h"
+#include "include/stringify.h"
+#include "common/Checksummer.h"
+#include "global/global_context.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+#include "gtest/gtest.h"
+#include <sys/time.h>
+#ifndef _WIN32
+#include <sys/resource.h>
+#endif
+
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+#include <regex>
+
+using namespace std;
+using namespace librados;
+
+typedef RadosTest LibRadosMisc;
+
+TEST(LibRadosMiscVersion, Version) {
+ int major, minor, extra;
+ rados_version(&major, &minor, &extra);
+}
+
+static void test_rados_log_cb(void *arg,
+ const char *line,
+ const char *who,
+ uint64_t sec, uint64_t nsec,
+ uint64_t seq, const char *level,
+ const char *msg)
+{
+ std::cerr << "monitor log callback invoked" << std::endl;
+}
+
+TEST(LibRadosMiscConnectFailure, ConnectFailure) {
+ rados_t cluster;
+
+ char *id = getenv("CEPH_CLIENT_ID");
+ if (id)
+ std::cerr << "Client id is: " << id << std::endl;
+
+ ASSERT_EQ(0, rados_create(&cluster, NULL));
+ ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
+
+ ASSERT_EQ(-ENOTCONN, rados_monitor_log(cluster, "error",
+ test_rados_log_cb, NULL));
+
+ ASSERT_EQ(0, rados_connect(cluster));
+ rados_shutdown(cluster);
+
+ ASSERT_EQ(0, rados_create(&cluster, NULL));
+ ASSERT_EQ(-ENOENT, rados_connect(cluster));
+ rados_shutdown(cluster);
+}
+
+TEST(LibRadosMiscConnectFailure, ConnectTimeout) {
+ rados_t cluster;
+
+ ASSERT_EQ(0, rados_create(&cluster, NULL));
+ ASSERT_EQ(0, rados_conf_set(cluster, "mon_host", "255.0.1.2:3456"));
+ ASSERT_EQ(0, rados_conf_set(cluster, "key",
+ "AQAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA=="));
+ ASSERT_EQ(0, rados_conf_set(cluster, "client_mount_timeout", "2s"));
+
+ utime_t start = ceph_clock_now();
+ ASSERT_EQ(-ETIMEDOUT, rados_connect(cluster));
+ utime_t end = ceph_clock_now();
+
+ utime_t dur = end - start;
+ ASSERT_GE(dur, utime_t(2, 0));
+ ASSERT_LT(dur, utime_t(4, 0));
+
+ rados_shutdown(cluster);
+}
+
+TEST(LibRadosMiscPool, PoolCreationRace) {
+ rados_t cluster_a, cluster_b;
+
+ char *id = getenv("CEPH_CLIENT_ID");
+ if (id)
+ std::cerr << "Client id is: " << id << std::endl;
+
+ ASSERT_EQ(0, rados_create(&cluster_a, NULL));
+ ASSERT_EQ(0, rados_conf_read_file(cluster_a, NULL));
+ // kludge: i want to --log-file foo and only get cluster b
+ //ASSERT_EQ(0, rados_conf_parse_env(cluster_a, NULL));
+ ASSERT_EQ(0, rados_conf_set(cluster_a,
+ "objecter_debug_inject_relock_delay", "true"));
+ ASSERT_EQ(0, rados_connect(cluster_a));
+
+ ASSERT_EQ(0, rados_create(&cluster_b, NULL));
+ ASSERT_EQ(0, rados_conf_read_file(cluster_b, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster_b, NULL));
+ ASSERT_EQ(0, rados_connect(cluster_b));
+
+ char poolname[80];
+ snprintf(poolname, sizeof(poolname), "poolrace.%d", rand());
+ rados_pool_create(cluster_a, poolname);
+ rados_ioctx_t a;
+ rados_ioctx_create(cluster_a, poolname, &a);
+
+ char pool2name[80];
+ snprintf(pool2name, sizeof(pool2name), "poolrace2.%d", rand());
+ rados_pool_create(cluster_b, pool2name);
+
+ list<rados_completion_t> cls;
+ // this should normally trigger pretty easily, but we need to bound
+ // the requests because if we get too many we'll get stuck by always
+ // sending enough messages that we hit the socket failure injection.
+ int max = 512;
+ while (max--) {
+ char buf[100];
+ rados_completion_t c;
+ rados_aio_create_completion2(nullptr, nullptr, &c);
+ cls.push_back(c);
+ rados_aio_read(a, "PoolCreationRaceObj", c, buf, 100, 0);
+ cout << "started " << (void*)c << std::endl;
+ if (rados_aio_is_complete(cls.front())) {
+ break;
+ }
+ }
+ while (!rados_aio_is_complete(cls.front())) {
+ cout << "waiting 1 sec" << std::endl;
+ sleep(1);
+ }
+
+ cout << " started " << cls.size() << " aios" << std::endl;
+ for (auto c : cls) {
+ cout << "waiting " << (void*)c << std::endl;
+ rados_aio_wait_for_complete_and_cb(c);
+ rados_aio_release(c);
+ }
+ cout << "done." << std::endl;
+
+ rados_ioctx_destroy(a);
+ rados_pool_delete(cluster_a, poolname);
+ rados_pool_delete(cluster_a, pool2name);
+ rados_shutdown(cluster_b);
+ rados_shutdown(cluster_a);
+}
+
+TEST_F(LibRadosMisc, ClusterFSID) {
+ char fsid[37];
+ ASSERT_EQ(-ERANGE, rados_cluster_fsid(cluster, fsid, sizeof(fsid) - 1));
+ ASSERT_EQ(sizeof(fsid) - 1,
+ (size_t)rados_cluster_fsid(cluster, fsid, sizeof(fsid)));
+}
+
+TEST_F(LibRadosMisc, Exec) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ char buf2[512];
+ int res = rados_exec(ioctx, "foo", "rbd", "get_all_features",
+ NULL, 0, buf2, sizeof(buf2));
+ ASSERT_GT(res, 0);
+ bufferlist bl;
+ bl.append(buf2, res);
+ auto iter = bl.cbegin();
+ uint64_t all_features;
+ decode(all_features, iter);
+ // make sure *some* features are specified; don't care which ones
+ ASSERT_NE(all_features, (unsigned)0);
+}
+
+TEST_F(LibRadosMisc, WriteSame) {
+ char buf[128];
+ char full[128 * 4];
+ char *cmp;
+
+ /* zero the full range before using writesame */
+ memset(full, 0, sizeof(full));
+ ASSERT_EQ(0, rados_write(ioctx, "ws", full, sizeof(full), 0));
+
+ memset(buf, 0xcc, sizeof(buf));
+ /* write the same buf four times */
+ ASSERT_EQ(0, rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(full), 0));
+
+ /* read back the full buffer and confirm that it matches */
+ ASSERT_EQ((int)sizeof(full), rados_read(ioctx, "ws", full, sizeof(full), 0));
+
+ for (cmp = full; cmp < full + sizeof(full); cmp += sizeof(buf)) {
+ ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+ }
+
+ /* write_len not a multiple of data_len should throw error */
+ ASSERT_EQ(-EINVAL, rados_writesame(ioctx, "ws", buf, sizeof(buf),
+ (sizeof(buf) * 4) - 1, 0));
+ ASSERT_EQ(-EINVAL,
+ rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(buf) / 2, 0));
+ ASSERT_EQ(-EINVAL,
+ rados_writesame(ioctx, "ws", buf, 0, sizeof(buf), 0));
+ /* write_len = data_len, i.e. same as rados_write() */
+ ASSERT_EQ(0, rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(buf), 0));
+}
+
+TEST_F(LibRadosMisc, CmpExt) {
+ bufferlist cmp_bl, bad_cmp_bl, write_bl;
+ char stored_str[] = "1234567891";
+ char mismatch_str[] = "1234577777";
+
+ ASSERT_EQ(0,
+ rados_write(ioctx, "cmpextpp", stored_str, sizeof(stored_str), 0));
+
+ ASSERT_EQ(0,
+ rados_cmpext(ioctx, "cmpextpp", stored_str, sizeof(stored_str), 0));
+
+ ASSERT_EQ(-MAX_ERRNO - 5,
+ rados_cmpext(ioctx, "cmpextpp", mismatch_str, sizeof(mismatch_str), 0));
+}
+
+TEST_F(LibRadosMisc, Applications) {
+ const char *cmd[] = {"{\"prefix\":\"osd dump\"}", nullptr};
+ char *buf, *st;
+ size_t buflen, stlen;
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf,
+ &buflen, &st, &stlen));
+ ASSERT_LT(0u, buflen);
+ string result(buf);
+ rados_buffer_free(buf);
+ rados_buffer_free(st);
+ if (!std::regex_search(result, std::regex("require_osd_release [l-z]"))) {
+ std::cout << "SKIPPING";
+ return;
+ }
+
+ char apps[128];
+ size_t app_len;
+
+ app_len = sizeof(apps);
+ ASSERT_EQ(0, rados_application_list(ioctx, apps, &app_len));
+ ASSERT_EQ(6U, app_len);
+ ASSERT_EQ(0, memcmp("rados\0", apps, app_len));
+
+ ASSERT_EQ(0, rados_application_enable(ioctx, "app1", 1));
+ ASSERT_EQ(-EPERM, rados_application_enable(ioctx, "app2", 0));
+ ASSERT_EQ(0, rados_application_enable(ioctx, "app2", 1));
+
+ ASSERT_EQ(-ERANGE, rados_application_list(ioctx, apps, &app_len));
+ ASSERT_EQ(16U, app_len);
+ ASSERT_EQ(0, rados_application_list(ioctx, apps, &app_len));
+ ASSERT_EQ(16U, app_len);
+ ASSERT_EQ(0, memcmp("app1\0app2\0rados\0", apps, app_len));
+
+ char keys[128];
+ char vals[128];
+ size_t key_len;
+ size_t val_len;
+
+ key_len = sizeof(keys);
+ val_len = sizeof(vals);
+ ASSERT_EQ(-ENOENT, rados_application_metadata_list(ioctx, "dne", keys,
+ &key_len, vals, &val_len));
+ ASSERT_EQ(0, rados_application_metadata_list(ioctx, "app1", keys, &key_len,
+ vals, &val_len));
+ ASSERT_EQ(0U, key_len);
+ ASSERT_EQ(0U, val_len);
+
+ ASSERT_EQ(-ENOENT, rados_application_metadata_set(ioctx, "dne", "key",
+ "value"));
+ ASSERT_EQ(0, rados_application_metadata_set(ioctx, "app1", "key1", "value1"));
+ ASSERT_EQ(0, rados_application_metadata_set(ioctx, "app1", "key2", "value2"));
+
+ ASSERT_EQ(-ERANGE, rados_application_metadata_list(ioctx, "app1", keys,
+ &key_len, vals, &val_len));
+ ASSERT_EQ(10U, key_len);
+ ASSERT_EQ(14U, val_len);
+ ASSERT_EQ(0, rados_application_metadata_list(ioctx, "app1", keys, &key_len,
+ vals, &val_len));
+ ASSERT_EQ(10U, key_len);
+ ASSERT_EQ(14U, val_len);
+ ASSERT_EQ(0, memcmp("key1\0key2\0", keys, key_len));
+ ASSERT_EQ(0, memcmp("value1\0value2\0", vals, val_len));
+
+ ASSERT_EQ(0, rados_application_metadata_remove(ioctx, "app1", "key1"));
+ ASSERT_EQ(0, rados_application_metadata_list(ioctx, "app1", keys, &key_len,
+ vals, &val_len));
+ ASSERT_EQ(5U, key_len);
+ ASSERT_EQ(7U, val_len);
+ ASSERT_EQ(0, memcmp("key2\0", keys, key_len));
+ ASSERT_EQ(0, memcmp("value2\0", vals, val_len));
+}
+
+TEST_F(LibRadosMisc, MinCompatOSD) {
+ int8_t require_osd_release;
+ ASSERT_EQ(0, rados_get_min_compatible_osd(cluster, &require_osd_release));
+ ASSERT_LE(-1, require_osd_release);
+ ASSERT_GT(CEPH_RELEASE_MAX, require_osd_release);
+}
+
+TEST_F(LibRadosMisc, MinCompatClient) {
+ int8_t min_compat_client;
+ int8_t require_min_compat_client;
+ ASSERT_EQ(0, rados_get_min_compatible_client(cluster,
+ &min_compat_client,
+ &require_min_compat_client));
+ ASSERT_LE(-1, min_compat_client);
+ ASSERT_GT(CEPH_RELEASE_MAX, min_compat_client);
+
+ ASSERT_LE(-1, require_min_compat_client);
+ ASSERT_GT(CEPH_RELEASE_MAX, require_min_compat_client);
+}
+
+static void shutdown_racer_func()
+{
+ const int niter = 32;
+ rados_t rad;
+ int i;
+
+ for (i = 0; i < niter; ++i) {
+ auto r = connect_cluster(&rad);
+ if (getenv("ALLOW_TIMEOUTS")) {
+ ASSERT_TRUE(r == "" || r == "rados_connect failed with error -110");
+ } else {
+ ASSERT_EQ("", r);
+ }
+ rados_shutdown(rad);
+ }
+}
+
+#ifndef _WIN32
+// See trackers #20988 and #42026
+TEST_F(LibRadosMisc, ShutdownRace)
+{
+ const int nthreads = 128;
+ std::thread threads[nthreads];
+
+ // Need a bunch of fd's for this test
+ struct rlimit rold, rnew;
+ ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &rold), 0);
+ rnew = rold;
+ rnew.rlim_cur = rnew.rlim_max;
+ ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &rnew), 0);
+
+ for (int i = 0; i < nthreads; ++i)
+ threads[i] = std::thread(shutdown_racer_func);
+
+ for (int i = 0; i < nthreads; ++i)
+ threads[i].join();
+ ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &rold), 0);
+}
+#endif /* _WIN32 */
diff --git a/src/test/librados/misc_cxx.cc b/src/test/librados/misc_cxx.cc
new file mode 100644
index 000000000..545d5e57b
--- /dev/null
+++ b/src/test/librados/misc_cxx.cc
@@ -0,0 +1,923 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+#include <regex>
+
+#include "gtest/gtest.h"
+
+#include "include/err.h"
+#include "include/buffer.h"
+#include "include/rbd_types.h"
+#include "include/rados.h"
+#include "include/rados/librados.hpp"
+#include "include/scope_guard.h"
+#include "include/stringify.h"
+#include "common/Checksummer.h"
+#include "mds/mdstypes.h"
+#include "global/global_context.h"
+#include "test/librados/testcase_cxx.h"
+#include "test/librados/test_cxx.h"
+
+#include "crimson_utils.h"
+
+using namespace std;
+using namespace librados;
+
+typedef RadosTestPP LibRadosMiscPP;
+typedef RadosTestECPP LibRadosMiscECPP;
+
+TEST(LibRadosMiscVersion, VersionPP) {
+ int major, minor, extra;
+ Rados::version(&major, &minor, &extra);
+}
+
+TEST_F(LibRadosMiscPP, WaitOSDMapPP) {
+ ASSERT_EQ(0, cluster.wait_for_latest_osdmap());
+}
+
+TEST_F(LibRadosMiscPP, LongNamePP) {
+ bufferlist bl;
+ bl.append("content");
+ int maxlen = g_conf()->osd_max_object_name_len;
+ ASSERT_EQ(0, ioctx.write(string(maxlen/2, 'a').c_str(), bl, bl.length(), 0));
+ ASSERT_EQ(0, ioctx.write(string(maxlen-1, 'a').c_str(), bl, bl.length(), 0));
+ ASSERT_EQ(0, ioctx.write(string(maxlen, 'a').c_str(), bl, bl.length(), 0));
+ ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen+1, 'a').c_str(), bl, bl.length(), 0));
+ ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen*2, 'a').c_str(), bl, bl.length(), 0));
+}
+
+TEST_F(LibRadosMiscPP, LongLocatorPP) {
+ bufferlist bl;
+ bl.append("content");
+ int maxlen = g_conf()->osd_max_object_name_len;
+ ioctx.locator_set_key(
+ string((maxlen/2), 'a'));
+ ASSERT_EQ(
+ 0,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.locator_set_key(
+ string(maxlen - 1, 'a'));
+ ASSERT_EQ(
+ 0,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.locator_set_key(
+ string(maxlen, 'a'));
+ ASSERT_EQ(
+ 0,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.locator_set_key(
+ string(maxlen+1, 'a'));
+ ASSERT_EQ(
+ -ENAMETOOLONG,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.locator_set_key(
+ string((maxlen*2), 'a'));
+ ASSERT_EQ(
+ -ENAMETOOLONG,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+}
+
+TEST_F(LibRadosMiscPP, LongNSpacePP) {
+ bufferlist bl;
+ bl.append("content");
+ int maxlen = g_conf()->osd_max_object_namespace_len;
+ ioctx.set_namespace(
+ string((maxlen/2), 'a'));
+ ASSERT_EQ(
+ 0,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.set_namespace(
+ string(maxlen - 1, 'a'));
+ ASSERT_EQ(
+ 0,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.set_namespace(
+ string(maxlen, 'a'));
+ ASSERT_EQ(
+ 0,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.set_namespace(
+ string(maxlen+1, 'a'));
+ ASSERT_EQ(
+ -ENAMETOOLONG,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+ ioctx.set_namespace(
+ string((maxlen*2), 'a'));
+ ASSERT_EQ(
+ -ENAMETOOLONG,
+ ioctx.write(
+ string("a").c_str(),
+ bl, bl.length(), 0));
+}
+
+TEST_F(LibRadosMiscPP, LongAttrNamePP) {
+ bufferlist bl;
+ bl.append("content");
+ int maxlen = g_conf()->osd_max_attr_name_len;
+ ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen/2, 'a').c_str(), bl));
+ ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen-1, 'a').c_str(), bl));
+ ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen, 'a').c_str(), bl));
+ ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen+1, 'a').c_str(), bl));
+ ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen*2, 'a').c_str(), bl));
+}
+
+TEST_F(LibRadosMiscPP, ExecPP) {
+ bufferlist bl;
+ ASSERT_EQ(0, ioctx.write("foo", bl, 0, 0));
+ bufferlist bl2, out;
+ int r = ioctx.exec("foo", "rbd", "get_all_features", bl2, out);
+ ASSERT_EQ(0, r);
+ auto iter = out.cbegin();
+ uint64_t all_features;
+ decode(all_features, iter);
+ // make sure *some* features are specified; don't care which ones
+ ASSERT_NE(all_features, (unsigned)0);
+}
+
+void set_completion_complete(rados_completion_t cb, void *arg)
+{
+ bool *my_aio_complete = (bool*)arg;
+ *my_aio_complete = true;
+}
+
+TEST_F(LibRadosMiscPP, BadFlagsPP) {
+ unsigned badflags = CEPH_OSD_FLAG_PARALLELEXEC;
+ {
+ bufferlist bl;
+ bl.append("data");
+ ASSERT_EQ(0, ioctx.write("badfoo", bl, bl.length(), 0));
+ }
+ {
+ ASSERT_EQ(-EINVAL, ioctx.remove("badfoo", badflags));
+ }
+}
+
+TEST_F(LibRadosMiscPP, Operate1PP) {
+ ObjectWriteOperation o;
+ {
+ bufferlist bl;
+ o.write(0, bl);
+ }
+ std::string val1("val1");
+ {
+ bufferlist bl;
+ bl.append(val1.c_str(), val1.size() + 1);
+ o.setxattr("key1", bl);
+ o.omap_clear(); // shouldn't affect attrs!
+ }
+ ASSERT_EQ(0, ioctx.operate("foo", &o));
+
+ ObjectWriteOperation empty;
+ ASSERT_EQ(0, ioctx.operate("foo", &empty));
+
+ {
+ bufferlist bl;
+ ASSERT_GT(ioctx.getxattr("foo", "key1", bl), 0);
+ ASSERT_EQ(0, strcmp(bl.c_str(), val1.c_str()));
+ }
+ ObjectWriteOperation o2;
+ {
+ bufferlist bl;
+ bl.append(val1);
+ o2.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
+ o2.rmxattr("key1");
+ }
+ ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o2));
+ ObjectWriteOperation o3;
+ {
+ bufferlist bl;
+ bl.append(val1);
+ o3.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
+ }
+ ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o3));
+}
+
+TEST_F(LibRadosMiscPP, Operate2PP) {
+ ObjectWriteOperation o;
+ {
+ bufferlist bl;
+ bl.append("abcdefg");
+ o.write(0, bl);
+ }
+ std::string val1("val1");
+ {
+ bufferlist bl;
+ bl.append(val1.c_str(), val1.size() + 1);
+ o.setxattr("key1", bl);
+ o.truncate(0);
+ }
+ ASSERT_EQ(0, ioctx.operate("foo", &o));
+ uint64_t size;
+ time_t mtime;
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(0U, size);
+}
+
+TEST_F(LibRadosMiscPP, BigObjectPP) {
+ bufferlist bl;
+ bl.append("abcdefg");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+
+ {
+ ObjectWriteOperation o;
+ o.truncate(500000000000ull);
+ ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+ }
+ {
+ ObjectWriteOperation o;
+ o.zero(500000000000ull, 1);
+ ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+ }
+ {
+ ObjectWriteOperation o;
+ o.zero(1, 500000000000ull);
+ ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+ }
+ {
+ ObjectWriteOperation o;
+ o.zero(500000000000ull, 500000000000ull);
+ ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+ }
+
+#ifdef __LP64__
+ // this test only works on 64-bit platforms
+ ASSERT_EQ(-EFBIG, ioctx.write("foo", bl, bl.length(), 500000000000ull));
+#endif
+}
+
+TEST_F(LibRadosMiscPP, AioOperatePP) {
+ bool my_aio_complete = false;
+ AioCompletion *my_completion = cluster.aio_create_completion(
+ (void*)&my_aio_complete, set_completion_complete);
+ AioCompletion *my_completion_null = NULL;
+ ASSERT_NE(my_completion, my_completion_null);
+
+ ObjectWriteOperation o;
+ {
+ bufferlist bl;
+ o.write(0, bl);
+ }
+ std::string val1("val1");
+ {
+ bufferlist bl;
+ bl.append(val1.c_str(), val1.size() + 1);
+ o.setxattr("key1", bl);
+ bufferlist bl2;
+ char buf2[1024];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bl2.append(buf2, sizeof(buf2));
+ o.append(bl2);
+ }
+ ASSERT_EQ(0, ioctx.aio_operate("foo", my_completion, &o));
+ ASSERT_EQ(0, my_completion->wait_for_complete_and_cb());
+ ASSERT_EQ(my_aio_complete, true);
+ my_completion->release();
+
+ uint64_t size;
+ time_t mtime;
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(1024U, size);
+}
+
+TEST_F(LibRadosMiscPP, AssertExistsPP) {
+ char buf[64];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ ObjectWriteOperation op;
+ op.assert_exists();
+ op.write(0, bl);
+ ASSERT_EQ(-ENOENT, ioctx.operate("asdffoo", &op));
+ ASSERT_EQ(0, ioctx.create("asdffoo", true));
+ ASSERT_EQ(0, ioctx.operate("asdffoo", &op));
+ ASSERT_EQ(-EEXIST, ioctx.create("asdffoo", true));
+}
+
+TEST_F(LibRadosMiscPP, AssertVersionPP) {
+ char buf[64];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ // Create test object...
+ ASSERT_EQ(0, ioctx.create("asdfbar", true));
+ // ...then write it again to guarantee that the
+ // (unsigned) version must be at least 1 (not 0)
+ // since we want to decrement it by 1 later.
+ ASSERT_EQ(0, ioctx.write_full("asdfbar", bl));
+
+ uint64_t v = ioctx.get_last_version();
+ ObjectWriteOperation op1;
+ op1.assert_version(v+1);
+ op1.write(0, bl);
+ ASSERT_EQ(-EOVERFLOW, ioctx.operate("asdfbar", &op1));
+ ObjectWriteOperation op2;
+ op2.assert_version(v-1);
+ op2.write(0, bl);
+ ASSERT_EQ(-ERANGE, ioctx.operate("asdfbar", &op2));
+ ObjectWriteOperation op3;
+ op3.assert_version(v);
+ op3.write(0, bl);
+ ASSERT_EQ(0, ioctx.operate("asdfbar", &op3));
+}
+
+TEST_F(LibRadosMiscPP, BigAttrPP) {
+ char buf[64];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ ASSERT_EQ(0, ioctx.create("foo", true));
+
+ bufferlist got;
+
+ cout << "osd_max_attr_size = " << g_conf()->osd_max_attr_size << std::endl;
+ if (g_conf()->osd_max_attr_size) {
+ bl.clear();
+ got.clear();
+ bl.append(buffer::create(g_conf()->osd_max_attr_size));
+ ASSERT_EQ(0, ioctx.setxattr("foo", "one", bl));
+ ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", "one", got));
+ ASSERT_TRUE(bl.contents_equal(got));
+
+ bl.clear();
+ bl.append(buffer::create(g_conf()->osd_max_attr_size+1));
+ ASSERT_EQ(-EFBIG, ioctx.setxattr("foo", "one", bl));
+ } else {
+ cout << "osd_max_attr_size == 0; skipping test" << std::endl;
+ }
+
+ for (int i=0; i<1000; i++) {
+ bl.clear();
+ got.clear();
+ bl.append(buffer::create(std::min<uint64_t>(g_conf()->osd_max_attr_size,
+ 1024)));
+ char n[10];
+ snprintf(n, sizeof(n), "a%d", i);
+ ASSERT_EQ(0, ioctx.setxattr("foo", n, bl));
+ ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", n, got));
+ ASSERT_TRUE(bl.contents_equal(got));
+ }
+}
+
+TEST_F(LibRadosMiscPP, CopyPP) {
+ SKIP_IF_CRIMSON();
+ bufferlist bl, x;
+ bl.append("hi there");
+ x.append("bar");
+
+ // small object
+ bufferlist blc = bl;
+ bufferlist xc = x;
+ ASSERT_EQ(0, ioctx.write_full("foo", blc));
+ ASSERT_EQ(0, ioctx.setxattr("foo", "myattr", xc));
+
+ version_t uv = ioctx.get_last_version();
+ {
+ // pass future version
+ ObjectWriteOperation op;
+ op.copy_from("foo", ioctx, uv + 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(-EOVERFLOW, ioctx.operate("foo.copy", &op));
+ }
+ {
+ // pass old version
+ ObjectWriteOperation op;
+ op.copy_from("foo", ioctx, uv - 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(-ERANGE, ioctx.operate("foo.copy", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.copy_from("foo", ioctx, uv, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, ioctx.operate("foo.copy", &op));
+
+ bufferlist bl2, x2;
+ ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy", bl2, 10000, 0));
+ ASSERT_TRUE(bl.contents_equal(bl2));
+ ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
+ ASSERT_TRUE(x.contents_equal(x2));
+ }
+
+ // small object without a version
+ {
+ ObjectWriteOperation op;
+ op.copy_from("foo", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, ioctx.operate("foo.copy2", &op));
+
+ bufferlist bl2, x2;
+ ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy2", bl2, 10000, 0));
+ ASSERT_TRUE(bl.contents_equal(bl2));
+ ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
+ ASSERT_TRUE(x.contents_equal(x2));
+ }
+
+ // do a big object
+ bl.append(buffer::create(g_conf()->osd_copyfrom_max_chunk * 3));
+ bl.zero();
+ bl.append("tail");
+ blc = bl;
+ xc = x;
+ ASSERT_EQ(0, ioctx.write_full("big", blc));
+ ASSERT_EQ(0, ioctx.setxattr("big", "myattr", xc));
+
+ {
+ ObjectWriteOperation op;
+ op.copy_from("big", ioctx, ioctx.get_last_version(), LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_EQ(0, ioctx.operate("big.copy", &op));
+
+ bufferlist bl2, x2;
+ ASSERT_EQ((int)bl.length(), ioctx.read("big.copy", bl2, bl.length(), 0));
+ ASSERT_TRUE(bl.contents_equal(bl2));
+ ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
+ ASSERT_TRUE(x.contents_equal(x2));
+ }
+
+ {
+ ObjectWriteOperation op;
+ op.copy_from("big", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
+ ASSERT_EQ(0, ioctx.operate("big.copy2", &op));
+
+ bufferlist bl2, x2;
+ ASSERT_EQ((int)bl.length(), ioctx.read("big.copy2", bl2, bl.length(), 0));
+ ASSERT_TRUE(bl.contents_equal(bl2));
+ ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
+ ASSERT_TRUE(x.contents_equal(x2));
+ }
+}
+
+class LibRadosTwoPoolsECPP : public RadosTestECPP
+{
+public:
+ LibRadosTwoPoolsECPP() {};
+ ~LibRadosTwoPoolsECPP() override {};
+protected:
+ static void SetUpTestCase() {
+ SKIP_IF_CRIMSON();
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+ src_pool_name = get_temp_pool_name();
+ ASSERT_EQ(0, s_cluster.pool_create(src_pool_name.c_str()));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, s_cluster.ioctx_create(pool_name.c_str(), ioctx));
+ ioctx.application_enable("rados", true);
+
+ librados::IoCtx src_ioctx;
+ ASSERT_EQ(0, s_cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
+ src_ioctx.application_enable("rados", true);
+ }
+ static void TearDownTestCase() {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, s_cluster.pool_delete(src_pool_name.c_str()));
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+ }
+ static std::string src_pool_name;
+
+ void SetUp() override {
+ SKIP_IF_CRIMSON();
+ RadosTestECPP::SetUp();
+ ASSERT_EQ(0, cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
+ src_ioctx.set_namespace(nspace);
+ }
+ void TearDown() override {
+ SKIP_IF_CRIMSON();
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+
+ RadosTestECPP::TearDown();
+
+ cleanup_default_namespace(src_ioctx);
+ cleanup_namespace(src_ioctx, nspace);
+
+ src_ioctx.close();
+ }
+
+ librados::IoCtx src_ioctx;
+};
+std::string LibRadosTwoPoolsECPP::src_pool_name;
+
+//copy_from between ecpool and no-ecpool.
+TEST_F(LibRadosTwoPoolsECPP, CopyFrom) {
+ SKIP_IF_CRIMSON();
+ bufferlist z;
+ z.append_zero(4194304*2);
+ bufferlist b;
+ b.append("copyfrom");
+
+ // create big object w/ omapheader
+ {
+ ASSERT_EQ(0, src_ioctx.write_full("foo", z));
+ ASSERT_EQ(0, src_ioctx.omap_set_header("foo", b));
+ version_t uv = src_ioctx.get_last_version();
+ ObjectWriteOperation op;
+ op.copy_from("foo", src_ioctx, uv, 0);
+ ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("foo.copy", &op));
+ }
+
+ // same with small object
+ {
+ ASSERT_EQ(0, src_ioctx.omap_set_header("bar", b));
+ version_t uv = src_ioctx.get_last_version();
+ ObjectWriteOperation op;
+ op.copy_from("bar", src_ioctx, uv, 0);
+ ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("bar.copy", &op));
+ }
+}
+
+TEST_F(LibRadosMiscPP, CopyScrubPP) {
+ SKIP_IF_CRIMSON();
+ bufferlist inbl, bl, x;
+ for (int i=0; i<100; ++i)
+ x.append("barrrrrrrrrrrrrrrrrrrrrrrrrr");
+ bl.append(buffer::create(g_conf()->osd_copyfrom_max_chunk * 3));
+ bl.zero();
+ bl.append("tail");
+ bufferlist cbl;
+
+ map<string, bufferlist> to_set;
+ for (int i=0; i<1000; ++i)
+ to_set[string("foo") + stringify(i)] = x;
+
+ // small
+ cbl = x;
+ ASSERT_EQ(0, ioctx.write_full("small", cbl));
+ ASSERT_EQ(0, ioctx.setxattr("small", "myattr", x));
+
+ // big
+ cbl = bl;
+ ASSERT_EQ(0, ioctx.write_full("big", cbl));
+
+ // without header
+ cbl = bl;
+ ASSERT_EQ(0, ioctx.write_full("big2", cbl));
+ ASSERT_EQ(0, ioctx.setxattr("big2", "myattr", x));
+ ASSERT_EQ(0, ioctx.setxattr("big2", "myattr2", x));
+ ASSERT_EQ(0, ioctx.omap_set("big2", to_set));
+
+ // with header
+ cbl = bl;
+ ASSERT_EQ(0, ioctx.write_full("big3", cbl));
+ ASSERT_EQ(0, ioctx.omap_set_header("big3", x));
+ ASSERT_EQ(0, ioctx.omap_set("big3", to_set));
+
+ // deep scrub to ensure digests are in place
+ {
+ for (int i=0; i<10; ++i) {
+ ostringstream ss;
+ ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
+ << ioctx.get_id() << "." << i
+ << "\"}";
+ cluster.mon_command(ss.str(), inbl, NULL, NULL);
+ }
+
+ // give it a few seconds to go. this is sloppy but is usually enough time
+ cout << "waiting for initial deep scrubs..." << std::endl;
+ sleep(30);
+ cout << "done waiting, doing copies" << std::endl;
+ }
+
+ {
+ ObjectWriteOperation op;
+ op.copy_from("small", ioctx, 0, 0);
+ ASSERT_EQ(0, ioctx.operate("small.copy", &op));
+ }
+
+ {
+ ObjectWriteOperation op;
+ op.copy_from("big", ioctx, 0, 0);
+ ASSERT_EQ(0, ioctx.operate("big.copy", &op));
+ }
+
+ {
+ ObjectWriteOperation op;
+ op.copy_from("big2", ioctx, 0, 0);
+ ASSERT_EQ(0, ioctx.operate("big2.copy", &op));
+ }
+
+ {
+ ObjectWriteOperation op;
+ op.copy_from("big3", ioctx, 0, 0);
+ ASSERT_EQ(0, ioctx.operate("big3.copy", &op));
+ }
+
+ // deep scrub to ensure digests are correct
+ {
+ for (int i=0; i<10; ++i) {
+ ostringstream ss;
+ ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
+ << ioctx.get_id() << "." << i
+ << "\"}";
+ cluster.mon_command(ss.str(), inbl, NULL, NULL);
+ }
+
+ // give it a few seconds to go. this is sloppy but is usually enough time
+ cout << "waiting for final deep scrubs..." << std::endl;
+ sleep(30);
+ cout << "done waiting" << std::endl;
+ }
+}
+
+TEST_F(LibRadosMiscPP, WriteSamePP) {
+ bufferlist bl;
+ char buf[128];
+ bufferlist fl;
+ char full[128 * 4];
+ char *cmp;
+
+ /* zero the full range before using writesame */
+ memset(full, 0, sizeof(full));
+ fl.append(full, sizeof(full));
+ ASSERT_EQ(0, ioctx.write("ws", fl, fl.length(), 0));
+
+ memset(buf, 0xcc, sizeof(buf));
+ bl.clear();
+ bl.append(buf, sizeof(buf));
+ /* write the same buf four times */
+ ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(full), 0));
+
+ /* read back the full buffer and confirm that it matches */
+ fl.clear();
+ fl.append(full, sizeof(full));
+ ASSERT_EQ((int)fl.length(), ioctx.read("ws", fl, fl.length(), 0));
+
+ for (cmp = fl.c_str(); cmp < fl.c_str() + fl.length(); cmp += sizeof(buf)) {
+ ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+ }
+
+ /* write_len not a multiple of data_len should throw error */
+ bl.clear();
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(-EINVAL, ioctx.writesame("ws", bl, (sizeof(buf) * 4) - 1, 0));
+ ASSERT_EQ(-EINVAL,
+ ioctx.writesame("ws", bl, bl.length() / 2, 0));
+ /* write_len = data_len, i.e. same as write() */
+ ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(buf), 0));
+ bl.clear();
+ ASSERT_EQ(-EINVAL,
+ ioctx.writesame("ws", bl, sizeof(buf), 0));
+}
+
+template <typename T>
+class LibRadosChecksum : public LibRadosMiscPP {
+public:
+ typedef typename T::alg_t alg_t;
+ typedef typename T::value_t value_t;
+ typedef typename alg_t::init_value_t init_value_t;
+
+ static const rados_checksum_type_t type = T::type;
+
+ bufferlist content_bl;
+
+ using LibRadosMiscPP::SetUpTestCase;
+ using LibRadosMiscPP::TearDownTestCase;
+
+ void SetUp() override {
+ LibRadosMiscPP::SetUp();
+
+ std::string content(4096, '\0');
+ for (size_t i = 0; i < content.length(); ++i) {
+ content[i] = static_cast<char>(rand() % (126 - 33) + 33);
+ }
+ content_bl.append(content);
+ ASSERT_EQ(0, ioctx.write("foo", content_bl, content_bl.length(), 0));
+ }
+};
+
+template <rados_checksum_type_t _type, typename AlgT, typename ValueT>
+class LibRadosChecksumParams {
+public:
+ typedef AlgT alg_t;
+ typedef ValueT value_t;
+ static const rados_checksum_type_t type = _type;
+};
+
+typedef ::testing::Types<
+ LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH32,
+ Checksummer::xxhash32, ceph_le32>,
+ LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH64,
+ Checksummer::xxhash64, ceph_le64>,
+ LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_CRC32C,
+ Checksummer::crc32c, ceph_le32>
+ > LibRadosChecksumTypes;
+
+TYPED_TEST_SUITE(LibRadosChecksum, LibRadosChecksumTypes);
+
+TYPED_TEST(LibRadosChecksum, Subset) {
+ uint32_t chunk_size = 1024;
+ uint32_t csum_count = this->content_bl.length() / chunk_size;
+
+ typename TestFixture::init_value_t init_value = -1;
+ bufferlist init_value_bl;
+ encode(init_value, init_value_bl);
+
+ std::vector<bufferlist> checksum_bls(csum_count);
+ std::vector<int> checksum_rvals(csum_count);
+
+ // individual checksum ops for each chunk
+ ObjectReadOperation op;
+ for (uint32_t i = 0; i < csum_count; ++i) {
+ op.checksum(TestFixture::type, init_value_bl, i * chunk_size, chunk_size,
+ 0, &checksum_bls[i], &checksum_rvals[i]);
+ }
+ ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
+
+ for (uint32_t i = 0; i < csum_count; ++i) {
+ ASSERT_EQ(0, checksum_rvals[i]);
+
+ auto bl_it = checksum_bls[i].cbegin();
+ uint32_t count;
+ decode(count, bl_it);
+ ASSERT_EQ(1U, count);
+
+ typename TestFixture::value_t value;
+ decode(value, bl_it);
+
+ bufferlist content_sub_bl;
+ content_sub_bl.substr_of(this->content_bl, i * chunk_size, chunk_size);
+
+ typename TestFixture::value_t expected_value;
+ bufferptr expected_value_bp = buffer::create_static(
+ sizeof(expected_value), reinterpret_cast<char*>(&expected_value));
+ Checksummer::template calculate<typename TestFixture::alg_t>(
+ init_value, chunk_size, 0, chunk_size, content_sub_bl,
+ &expected_value_bp);
+ ASSERT_EQ(expected_value, value);
+ }
+}
+
+TYPED_TEST(LibRadosChecksum, Chunked) {
+ uint32_t chunk_size = 1024;
+ uint32_t csum_count = this->content_bl.length() / chunk_size;
+
+ typename TestFixture::init_value_t init_value = -1;
+ bufferlist init_value_bl;
+ encode(init_value, init_value_bl);
+
+ bufferlist checksum_bl;
+ int checksum_rval;
+
+ // single op with chunked checksum results
+ ObjectReadOperation op;
+ op.checksum(TestFixture::type, init_value_bl, 0, this->content_bl.length(),
+ chunk_size, &checksum_bl, &checksum_rval);
+ ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
+ ASSERT_EQ(0, checksum_rval);
+
+ auto bl_it = checksum_bl.cbegin();
+ uint32_t count;
+ decode(count, bl_it);
+ ASSERT_EQ(csum_count, count);
+
+ std::vector<typename TestFixture::value_t> expected_values(csum_count);
+ bufferptr expected_values_bp = buffer::create_static(
+ csum_count * sizeof(typename TestFixture::value_t),
+ reinterpret_cast<char*>(&expected_values[0]));
+
+ Checksummer::template calculate<typename TestFixture::alg_t>(
+ init_value, chunk_size, 0, this->content_bl.length(), this->content_bl,
+ &expected_values_bp);
+
+ for (uint32_t i = 0; i < csum_count; ++i) {
+ typename TestFixture::value_t value;
+ decode(value, bl_it);
+ ASSERT_EQ(expected_values[i], value);
+ }
+}
+
+TEST_F(LibRadosMiscPP, CmpExtPP) {
+ bufferlist cmp_bl, bad_cmp_bl, write_bl;
+ char stored_str[] = "1234567891";
+ char mismatch_str[] = "1234577777";
+
+ write_bl.append(stored_str);
+ ioctx.write("cmpextpp", write_bl, write_bl.length(), 0);
+ cmp_bl.append(stored_str);
+ ASSERT_EQ(0, ioctx.cmpext("cmpextpp", 0, cmp_bl));
+
+ bad_cmp_bl.append(mismatch_str);
+ ASSERT_EQ(-MAX_ERRNO - 5, ioctx.cmpext("cmpextpp", 0, bad_cmp_bl));
+}
+
+TEST_F(LibRadosMiscPP, Applications) {
+ bufferlist inbl, outbl;
+ string outs;
+ ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"osd dump\"}",
+ inbl, &outbl, &outs));
+ ASSERT_LT(0u, outbl.length());
+ ASSERT_LE(0u, outs.length());
+ if (!std::regex_search(outbl.to_str(),
+ std::regex("require_osd_release [l-z]"))) {
+ std::cout << "SKIPPING";
+ return;
+ }
+
+ std::set<std::string> expected_apps = {"rados"};
+ std::set<std::string> apps;
+ ASSERT_EQ(0, ioctx.application_list(&apps));
+ ASSERT_EQ(expected_apps, apps);
+
+ ASSERT_EQ(0, ioctx.application_enable("app1", true));
+ ASSERT_EQ(-EPERM, ioctx.application_enable("app2", false));
+ ASSERT_EQ(0, ioctx.application_enable("app2", true));
+
+ expected_apps = {"app1", "app2", "rados"};
+ ASSERT_EQ(0, ioctx.application_list(&apps));
+ ASSERT_EQ(expected_apps, apps);
+
+ std::map<std::string, std::string> expected_meta;
+ std::map<std::string, std::string> meta;
+ ASSERT_EQ(-ENOENT, ioctx.application_metadata_list("dne", &meta));
+ ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
+ ASSERT_EQ(expected_meta, meta);
+
+ ASSERT_EQ(-ENOENT, ioctx.application_metadata_set("dne", "key1", "value1"));
+ ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key1", "value1"));
+ ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key2", "value2"));
+
+ expected_meta = {{"key1", "value1"}, {"key2", "value2"}};
+ ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
+ ASSERT_EQ(expected_meta, meta);
+
+ ASSERT_EQ(0, ioctx.application_metadata_remove("app1", "key1"));
+
+ expected_meta = {{"key2", "value2"}};
+ ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
+ ASSERT_EQ(expected_meta, meta);
+}
+
+TEST_F(LibRadosMiscECPP, CompareExtentRange) {
+ SKIP_IF_CRIMSON();
+ bufferlist bl1;
+ bl1.append("ceph");
+ ObjectWriteOperation write;
+ write.write(0, bl1);
+ ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+ bufferlist bl2;
+ bl2.append("ph");
+ bl2.append(std::string(2, '\0'));
+ ObjectReadOperation read1;
+ read1.cmpext(2, bl2, nullptr);
+ ASSERT_EQ(0, ioctx.operate("foo", &read1, nullptr));
+
+ bufferlist bl3;
+ bl3.append(std::string(4, '\0'));
+ ObjectReadOperation read2;
+ read2.cmpext(2097152, bl3, nullptr);
+ ASSERT_EQ(0, ioctx.operate("foo", &read2, nullptr));
+}
+
+TEST_F(LibRadosMiscPP, MinCompatOSD) {
+ int8_t require_osd_release;
+ ASSERT_EQ(0, cluster.get_min_compatible_osd(&require_osd_release));
+ ASSERT_LE(-1, require_osd_release);
+ ASSERT_GT(CEPH_RELEASE_MAX, require_osd_release);
+}
+
+TEST_F(LibRadosMiscPP, MinCompatClient) {
+ int8_t min_compat_client;
+ int8_t require_min_compat_client;
+ ASSERT_EQ(0, cluster.get_min_compatible_client(&min_compat_client,
+ &require_min_compat_client));
+ ASSERT_LE(-1, min_compat_client);
+ ASSERT_GT(CEPH_RELEASE_MAX, min_compat_client);
+
+ ASSERT_LE(-1, require_min_compat_client);
+ ASSERT_GT(CEPH_RELEASE_MAX, require_min_compat_client);
+}
+
+TEST_F(LibRadosMiscPP, Conf) {
+ const char* const option = "bluestore_throttle_bytes";
+ size_t new_size = 1 << 20;
+ std::string original;
+ ASSERT_EQ(0, cluster.conf_get(option, original));
+ auto restore_setting = make_scope_guard([&] {
+ cluster.conf_set(option, original.c_str());
+ });
+ std::string expected = std::to_string(new_size);
+ ASSERT_EQ(0, cluster.conf_set(option, expected.c_str()));
+ std::string actual;
+ ASSERT_EQ(0, cluster.conf_get(option, actual));
+ ASSERT_EQ(expected, actual);
+}
diff --git a/src/test/librados/op_speed.cc b/src/test/librados/op_speed.cc
new file mode 100644
index 000000000..849a6566f
--- /dev/null
+++ b/src/test/librados/op_speed.cc
@@ -0,0 +1,24 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include <cstdint>
+
+#include "include/rados/librados.hpp"
+
+constexpr int to_create = 10'000'000;
+
+int main() {
+ for (int i = 0; i < to_create; ++i) {
+ librados::ObjectReadOperation op;
+ bufferlist bl;
+ std::uint64_t sz;
+ struct timespec tm;
+ std::map<std::string, ceph::buffer::list> xattrs;
+ std::map<std::string, ceph::buffer::list> omap;
+ bool more;
+ op.read(0, 0, &bl, nullptr);
+ op.stat2(&sz, &tm, nullptr);
+ op.getxattrs(&xattrs, nullptr);
+ op.omap_get_vals2({}, 1000, &omap, &more, nullptr);
+ }
+}
diff --git a/src/test/librados/pool.cc b/src/test/librados/pool.cc
new file mode 100644
index 000000000..a85d8f3a8
--- /dev/null
+++ b/src/test/librados/pool.cc
@@ -0,0 +1,186 @@
+#include <errno.h>
+#include <vector>
+#include "crimson_utils.h"
+#include "gtest/gtest.h"
+#include "include/rados/librados.h"
+#include "test/librados/test.h"
+
+#define POOL_LIST_BUF_SZ 32768
+
+TEST(LibRadosPools, PoolList) {
+ char pool_list_buf[POOL_LIST_BUF_SZ];
+ char *buf = pool_list_buf;
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ ASSERT_LT(rados_pool_list(cluster, buf, POOL_LIST_BUF_SZ), POOL_LIST_BUF_SZ);
+
+ // we can pass a null buffer too.
+ ASSERT_LT(rados_pool_list(cluster, NULL, POOL_LIST_BUF_SZ), POOL_LIST_BUF_SZ);
+
+ bool found_pool = false;
+ int firstlen = 0;
+ while (buf[0] != '\0') {
+ if ((found_pool == false) && (strcmp(buf, pool_name.c_str()) == 0)) {
+ found_pool = true;
+ }
+ if (!firstlen)
+ firstlen = strlen(buf) + 1;
+ buf += strlen(buf) + 1;
+ }
+ ASSERT_EQ(found_pool, true);
+
+ // make sure we honor the buffer size limit
+ buf = pool_list_buf;
+ memset(buf, 0, POOL_LIST_BUF_SZ);
+ ASSERT_LT(rados_pool_list(cluster, buf, firstlen), POOL_LIST_BUF_SZ);
+ ASSERT_NE(0, buf[0]); // include at least one pool name
+ ASSERT_EQ(0, buf[firstlen]); // but don't touch the stopping point
+
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+int64_t rados_pool_lookup(rados_t cluster, const char *pool_name);
+
+TEST(LibRadosPools, PoolLookup) {
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ ASSERT_LT(0, rados_pool_lookup(cluster, pool_name.c_str()));
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosPools, PoolLookup2) {
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ int64_t pool_id = rados_pool_lookup(cluster, pool_name.c_str());
+ ASSERT_GT(pool_id, 0);
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ int64_t pool_id2 = rados_ioctx_get_id(ioctx);
+ ASSERT_EQ(pool_id, pool_id2);
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosPools, PoolLookupOtherInstance) {
+ rados_t cluster1;
+ ASSERT_EQ("", connect_cluster(&cluster1));
+
+ rados_t cluster2;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster2));
+ int64_t pool_id = rados_pool_lookup(cluster2, pool_name.c_str());
+ ASSERT_GT(pool_id, 0);
+
+ ASSERT_EQ(pool_id, rados_pool_lookup(cluster1, pool_name.c_str()));
+
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster2));
+ rados_shutdown(cluster1);
+}
+
+TEST(LibRadosPools, PoolReverseLookupOtherInstance) {
+ rados_t cluster1;
+ ASSERT_EQ("", connect_cluster(&cluster1));
+
+ rados_t cluster2;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster2));
+ int64_t pool_id = rados_pool_lookup(cluster2, pool_name.c_str());
+ ASSERT_GT(pool_id, 0);
+
+ char buf[100];
+ ASSERT_LT(0, rados_pool_reverse_lookup(cluster1, pool_id, buf, 100));
+ ASSERT_EQ(0, strcmp(buf, pool_name.c_str()));
+
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster2));
+ rados_shutdown(cluster1);
+}
+
+TEST(LibRadosPools, PoolDelete) {
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ ASSERT_EQ(0, rados_pool_delete(cluster, pool_name.c_str()));
+ ASSERT_GT(0, rados_pool_lookup(cluster, pool_name.c_str()));
+ ASSERT_EQ(0, rados_pool_create(cluster, pool_name.c_str()));
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosPools, PoolCreateDelete) {
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+
+ std::string n = pool_name + "abc123";
+ ASSERT_EQ(0, rados_pool_create(cluster, n.c_str()));
+ ASSERT_EQ(-EEXIST, rados_pool_create(cluster, n.c_str()));
+ ASSERT_EQ(0, rados_pool_delete(cluster, n.c_str()));
+ ASSERT_EQ(-ENOENT, rados_pool_delete(cluster, n.c_str()));
+
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosPools, PoolCreateWithCrushRule) {
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+
+ std::string pool2_name = get_temp_pool_name();
+ ASSERT_EQ(0, rados_pool_create_with_crush_rule(cluster,
+ pool2_name.c_str(), 0));
+ ASSERT_EQ(0, rados_pool_delete(cluster, pool2_name.c_str()));
+
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosPools, PoolGetBaseTier) {
+ SKIP_IF_CRIMSON();
+ rados_t cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+ std::string tier_pool_name = pool_name + "-cache";
+ ASSERT_EQ(0, rados_pool_create(cluster, tier_pool_name.c_str()));
+
+ int64_t pool_id = rados_pool_lookup(cluster, pool_name.c_str());
+ ASSERT_GE(pool_id, 0);
+
+ int64_t tier_pool_id = rados_pool_lookup(cluster, tier_pool_name.c_str());
+ ASSERT_GE(tier_pool_id, 0);
+
+
+ int64_t base_tier = 0;
+ EXPECT_EQ(0, rados_pool_get_base_tier(cluster, pool_id, &base_tier));
+ EXPECT_EQ(pool_id, base_tier);
+
+ std::string cmdstr = "{\"prefix\": \"osd tier add\", \"pool\": \"" +
+ pool_name + "\", \"tierpool\":\"" + tier_pool_name + "\", \"force_nonempty\":\"\"}";
+ char *cmd[1];
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ cmdstr = "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" +
+ tier_pool_name + "\", \"mode\":\"readonly\"," +
+ " \"yes_i_really_mean_it\": true}";
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ EXPECT_EQ(0, rados_wait_for_latest_osdmap(cluster));
+
+ EXPECT_EQ(0, rados_pool_get_base_tier(cluster, pool_id, &base_tier));
+ EXPECT_EQ(pool_id, base_tier);
+
+ EXPECT_EQ(0, rados_pool_get_base_tier(cluster, tier_pool_id, &base_tier));
+ EXPECT_EQ(pool_id, base_tier);
+
+ int64_t nonexistent_pool_id = (int64_t)((-1ULL) >> 1);
+ EXPECT_EQ(-ENOENT, rados_pool_get_base_tier(cluster, nonexistent_pool_id, &base_tier));
+
+ cmdstr = "{\"prefix\": \"osd tier remove\", \"pool\": \"" +
+ pool_name + "\", \"tierpool\":\"" + tier_pool_name + "\"}";
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+ ASSERT_EQ(0, rados_pool_delete(cluster, tier_pool_name.c_str()));
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
diff --git a/src/test/librados/service.cc b/src/test/librados/service.cc
new file mode 100644
index 000000000..6d88f0da9
--- /dev/null
+++ b/src/test/librados/service.cc
@@ -0,0 +1,209 @@
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "common/config_proxy.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+#ifndef _WIN32
+#include <sys/resource.h>
+#endif
+
+#include <mutex>
+#include <condition_variable>
+#include <algorithm>
+#include <thread>
+#include <errno.h>
+#include "gtest/gtest.h"
+#include "test/unit.cc"
+
+using namespace std;
+using namespace librados;
+
+TEST(LibRadosService, RegisterEarly) {
+ rados_t cluster;
+ ASSERT_EQ(0, rados_create(&cluster, "admin"));
+ ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
+
+ string name = string("pid") + stringify(getpid());
+ ASSERT_EQ(0, rados_service_register(cluster, "laundry", name.c_str(),
+ "foo\0bar\0this\0that\0"));
+ ASSERT_EQ(-EEXIST, rados_service_register(cluster, "laundry", name.c_str(),
+ "foo\0bar\0this\0that\0"));
+
+ ASSERT_EQ(0, rados_connect(cluster));
+ sleep(5);
+ rados_shutdown(cluster);
+}
+
+TEST(LibRadosService, RegisterLate) {
+ rados_t cluster;
+ ASSERT_EQ(0, rados_create(&cluster, "admin"));
+ ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
+ ASSERT_EQ(0, rados_connect(cluster));
+
+ string name = string("pid") + stringify(getpid());
+ ASSERT_EQ(0, rados_service_register(cluster, "laundry", name.c_str(),
+ "foo\0bar\0this\0that\0"));
+ ASSERT_EQ(-EEXIST, rados_service_register(cluster, "laundry", name.c_str(),
+ "foo\0bar\0this\0that\0"));
+ rados_shutdown(cluster);
+}
+
+static void status_format_func(const int i, std::mutex &lock,
+ std::condition_variable &cond,
+ int &threads_started, bool &stopped)
+{
+ rados_t cluster;
+ char metadata_buf[4096];
+
+ ASSERT_EQ(0, rados_create(&cluster, "admin"));
+ ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
+
+ ASSERT_EQ(0, rados_connect(cluster));
+ if (i == 0) {
+ ASSERT_LT(0, sprintf(metadata_buf, "%s%c%s%c",
+ "foo", '\0', "bar", '\0'));
+ } else if (i == 1) {
+ ASSERT_LT(0, sprintf(metadata_buf, "%s%c%s%c",
+ "daemon_type", '\0', "portal", '\0'));
+ } else if (i == 2) {
+ ASSERT_LT(0, sprintf(metadata_buf, "%s%c%s%c",
+ "daemon_prefix", '\0', "gateway", '\0'));
+ } else {
+ string prefix = string("gw") + stringify(i % 4);
+ string zone = string("z") + stringify(i % 3);
+ ASSERT_LT(0, sprintf(metadata_buf, "%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s%c",
+ "daemon_type", '\0', "portal", '\0',
+ "daemon_prefix", '\0', prefix.c_str(), '\0',
+ "hostname", '\0', prefix.c_str(), '\0',
+ "zone_id", '\0', zone.c_str(), '\0'));
+ }
+ string name = string("rbd/image") + stringify(i);
+ ASSERT_EQ(0, rados_service_register(cluster, "foo", name.c_str(),
+ metadata_buf));
+
+ std::unique_lock<std::mutex> l(lock);
+ threads_started++;
+ cond.notify_all();
+ cond.wait(l, [&stopped] {
+ return stopped;
+ });
+
+ rados_shutdown(cluster);
+}
+
+TEST(LibRadosService, StatusFormat) {
+ const int nthreads = 16;
+ std::thread threads[nthreads];
+ std::mutex lock;
+ std::condition_variable cond;
+ bool stopped = false;
+ int threads_started = 0;
+
+ // no rlimits on Windows
+ #ifndef _WIN32
+ // Need a bunch of fd's for this test
+ struct rlimit rold, rnew;
+ ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &rold), 0);
+ rnew = rold;
+ rnew.rlim_cur = rnew.rlim_max;
+ ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &rnew), 0);
+ #endif
+
+ for (int i = 0; i < nthreads; ++i)
+ threads[i] = std::thread(status_format_func, i, std::ref(lock),
+ std::ref(cond), std::ref(threads_started),
+ std::ref(stopped));
+
+ {
+ std::unique_lock<std::mutex> l(lock);
+ cond.wait(l, [&threads_started] {
+ return nthreads == threads_started;
+ });
+ }
+
+ int retry = 60; // mon thrashing may make this take a long time
+ while (retry) {
+ rados_t cluster;
+
+ ASSERT_EQ(0, rados_create(&cluster, "admin"));
+ ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
+
+ ASSERT_EQ(0, rados_connect(cluster));
+ JSONFormatter cmd_f;
+ cmd_f.open_object_section("command");
+ cmd_f.dump_string("prefix", "status");
+ cmd_f.close_section();
+ std::ostringstream cmd_stream;
+ cmd_f.flush(cmd_stream);
+ const std::string serialized_cmd = cmd_stream.str();
+ const char *cmd[2];
+ cmd[1] = NULL;
+ cmd[0] = serialized_cmd.c_str();
+ char *outbuf = NULL;
+ size_t outlen = 0;
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0,
+ &outbuf, &outlen, NULL, NULL));
+ std::string out(outbuf, outlen);
+ cout << out << std::endl;
+ bool success = false;
+ auto r1 = out.find("16 portals active (1 hosts, 3 zones)");
+ if (std::string::npos != r1) {
+ success = true;
+ }
+ rados_buffer_free(outbuf);
+ rados_shutdown(cluster);
+
+ if (success || !retry) {
+ break;
+ }
+
+ // wait for 2 seconds to make sure all the
+ // services have been successfully updated
+ // to ceph mon, then retry it.
+ sleep(2);
+ retry--;
+ }
+
+ {
+ std::scoped_lock<std::mutex> l(lock);
+ stopped = true;
+ cond.notify_all();
+ }
+ for (int i = 0; i < nthreads; ++i)
+ threads[i].join();
+
+ ASSERT_NE(0, retry);
+ #ifndef _WIN32
+ ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &rold), 0);
+ #endif
+}
+
+TEST(LibRadosService, Status) {
+ rados_t cluster;
+ ASSERT_EQ(0, rados_create(&cluster, "admin"));
+ ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
+ ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
+
+ ASSERT_EQ(-ENOTCONN, rados_service_update_status(cluster,
+ "testing\0testing\0"));
+
+ ASSERT_EQ(0, rados_connect(cluster));
+ string name = string("pid") + stringify(getpid());
+ ASSERT_EQ(0, rados_service_register(cluster, "laundry", name.c_str(),
+ "foo\0bar\0this\0that\0"));
+
+ for (int i=0; i<20; ++i) {
+ char buffer[1024];
+ snprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c%d%c",
+ "testing", '\0', "testing", '\0',
+ "count", '\0', i, '\0');
+ ASSERT_EQ(0, rados_service_update_status(cluster, buffer));
+ sleep(1);
+ }
+ rados_shutdown(cluster);
+}
diff --git a/src/test/librados/service_cxx.cc b/src/test/librados/service_cxx.cc
new file mode 100644
index 000000000..1bf682af8
--- /dev/null
+++ b/src/test/librados/service_cxx.cc
@@ -0,0 +1,105 @@
+#include <algorithm>
+#include <thread>
+#include <errno.h>
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "common/config_proxy.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+#include "test/unit.cc"
+
+using namespace std;
+using namespace librados;
+
+TEST(LibRadosServicePP, RegisterEarly) {
+ Rados cluster;
+ cluster.init("admin");
+ ASSERT_EQ(0, cluster.conf_read_file(NULL));
+ cluster.conf_parse_env(NULL);
+ string name = string("pid") + stringify(getpid());
+ ASSERT_EQ(0, cluster.service_daemon_register(
+ "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+ ASSERT_EQ(-EEXIST, cluster.service_daemon_register(
+ "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+ ASSERT_EQ(0, cluster.connect());
+ sleep(5);
+ cluster.shutdown();
+}
+
+TEST(LibRadosServicePP, RegisterLate) {
+ Rados cluster;
+ cluster.init("admin");
+ ASSERT_EQ(0, cluster.conf_read_file(NULL));
+ cluster.conf_parse_env(NULL);
+ ASSERT_EQ("", connect_cluster_pp(cluster));
+ string name = string("pid") + stringify(getpid());
+ ASSERT_EQ(0, cluster.service_daemon_register(
+ "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+}
+
+TEST(LibRadosServicePP, Status) {
+ Rados cluster;
+ cluster.init("admin");
+ ASSERT_EQ(0, cluster.conf_read_file(NULL));
+ cluster.conf_parse_env(NULL);
+ string name = string("pid") + stringify(getpid());
+ ASSERT_EQ(-ENOTCONN, cluster.service_daemon_update_status(
+ {{"testing", "starting"}}));
+ ASSERT_EQ(0, cluster.connect());
+ ASSERT_EQ(0, cluster.service_daemon_register(
+ "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+ for (int i=0; i<20; ++i) {
+ ASSERT_EQ(0, cluster.service_daemon_update_status({
+ {"testing", "running"},
+ {"count", stringify(i)}
+ }));
+ sleep(1);
+ }
+ cluster.shutdown();
+}
+
+TEST(LibRadosServicePP, Close) {
+ int tries = 20;
+ string name = string("close-test-pid") + stringify(getpid());
+ int i;
+ for (i = 0; i < tries; ++i) {
+ cout << "attempt " << i << " of " << tries << std::endl;
+ {
+ Rados cluster;
+ cluster.init("admin");
+ ASSERT_EQ(0, cluster.conf_read_file(NULL));
+ cluster.conf_parse_env(NULL);
+ ASSERT_EQ(0, cluster.connect());
+ ASSERT_EQ(0, cluster.service_daemon_register(
+ "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+ sleep(3); // let it register
+ cluster.shutdown();
+ }
+ // mgr updates servicemap every tick
+ //sleep(g_conf().get_val<int64_t>("mgr_tick_period"));
+ std::this_thread::sleep_for(g_conf().get_val<std::chrono::seconds>(
+ "mgr_tick_period"));
+ // make sure we are deregistered
+ {
+ Rados cluster;
+ cluster.init("admin");
+ ASSERT_EQ(0, cluster.conf_read_file(NULL));
+ cluster.conf_parse_env(NULL);
+ ASSERT_EQ(0, cluster.connect());
+ bufferlist inbl, outbl;
+ ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"service dump\"}",
+ inbl, &outbl, NULL));
+ string s = outbl.to_str();
+ cluster.shutdown();
+
+ if (s.find(name) != string::npos) {
+ cout << " failed to deregister:\n" << s << std::endl;
+ } else {
+ break;
+ }
+ }
+ }
+ ASSERT_LT(i, tries);
+}
diff --git a/src/test/librados/snapshots.cc b/src/test/librados/snapshots.cc
new file mode 100644
index 000000000..384447ca2
--- /dev/null
+++ b/src/test/librados/snapshots.cc
@@ -0,0 +1,356 @@
+#include "include/rados.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+#include "crimson_utils.h"
+
+#include <algorithm>
+#include <errno.h>
+#include "gtest/gtest.h"
+#include <string>
+
+using std::string;
+
+typedef RadosTest LibRadosSnapshots;
+typedef RadosTest LibRadosSnapshotsSelfManaged;
+typedef RadosTestEC LibRadosSnapshotsEC;
+typedef RadosTestEC LibRadosSnapshotsSelfManagedEC;
+
+const int bufsize = 128;
+
+TEST_F(LibRadosSnapshots, SnapList) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snap1"));
+ rados_snap_t snaps[10];
+ EXPECT_EQ(1, rados_ioctx_snap_list(ioctx, snaps,
+ sizeof(snaps) / sizeof(snaps[0])));
+ rados_snap_t rid;
+ EXPECT_EQ(0, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
+ EXPECT_EQ(rid, snaps[0]);
+ EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
+}
+
+TEST_F(LibRadosSnapshots, SnapRemove) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snap1"));
+ rados_snap_t rid;
+ ASSERT_EQ(0, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
+ ASSERT_EQ(-EEXIST, rados_ioctx_snap_create(ioctx, "snap1"));
+ ASSERT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
+ ASSERT_EQ(-ENOENT, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
+}
+
+TEST_F(LibRadosSnapshots, Rollback) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snap1"));
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ EXPECT_EQ(0, rados_write_full(ioctx, "foo", buf2, sizeof(buf2)));
+ EXPECT_EQ(0, rados_ioctx_snap_rollback(ioctx, "foo", "snap1"));
+ char buf3[sizeof(buf)];
+ EXPECT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ EXPECT_EQ(0, memcmp(buf, buf3, sizeof(buf)));
+ EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
+}
+
+TEST_F(LibRadosSnapshots, SnapGetName) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snapfoo"));
+ rados_snap_t rid;
+ EXPECT_EQ(0, rados_ioctx_snap_lookup(ioctx, "snapfoo", &rid));
+ EXPECT_EQ(-ENOENT, rados_ioctx_snap_lookup(ioctx, "snapbar", &rid));
+ char name[128];
+ memset(name, 0, sizeof(name));
+ EXPECT_EQ(0, rados_ioctx_snap_get_name(ioctx, rid, name, sizeof(name)));
+ time_t snaptime;
+ EXPECT_EQ(0, rados_ioctx_snap_get_stamp(ioctx, rid, &snaptime));
+ EXPECT_EQ(0, strcmp(name, "snapfoo"));
+ EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snapfoo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManaged, Snap) {
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+
+ my_snaps.push_back(-2);
+ rados_completion_t completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr,
+ &completion));
+ rados_aio_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back(), completion);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(completion));
+ rados_aio_release(completion);
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
+ rados_ioctx_snap_set_read(ioctx, my_snaps[1]-1);
+ char buf3[sizeof(buf)];
+ ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+
+ rados_ioctx_snap_set_read(ioctx, my_snaps[1]);
+ ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr,
+ &completion));
+ rados_aio_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back(), completion);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(completion));
+ rados_aio_release(completion);
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ rados_ioctx_snap_set_read(ioctx, LIBRADOS_SNAP_HEAD);
+ ASSERT_EQ(0, rados_remove(ioctx, "foo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManaged, Rollback) {
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ // First write
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ // Second write
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
+ // Rollback to my_snaps[1] - Object is expeceted to conatin the first write
+ rados_ioctx_selfmanaged_snap_rollback(ioctx, "foo", my_snaps[1]);
+ char buf3[sizeof(buf)];
+ ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_remove(ioctx, "foo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManaged, FutureSnapRollback) {
+ std::vector<uint64_t> my_snaps;
+ // Snapshot 1
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ // First write
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+
+ // Snapshot 2
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ // Second write
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
+ // Snapshot 3
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+
+ // Rollback to the last snap id - Object is expected to conatin
+ // latest write (head object)
+ rados_ioctx_selfmanaged_snap_rollback(ioctx, "foo", my_snaps[2]);
+ char buf3[sizeof(buf)];
+ ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf)));
+
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_remove(ioctx, "foo"));
+}
+
+
+
+// EC testing
+TEST_F(LibRadosSnapshotsEC, SnapList) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snap1"));
+ rados_snap_t snaps[10];
+ EXPECT_EQ(1, rados_ioctx_snap_list(ioctx, snaps,
+ sizeof(snaps) / sizeof(snaps[0])));
+ rados_snap_t rid;
+ EXPECT_EQ(0, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
+ EXPECT_EQ(rid, snaps[0]);
+ EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
+}
+
+TEST_F(LibRadosSnapshotsEC, SnapRemove) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snap1"));
+ rados_snap_t rid;
+ ASSERT_EQ(0, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
+ ASSERT_EQ(-EEXIST, rados_ioctx_snap_create(ioctx, "snap1"));
+ ASSERT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
+ ASSERT_EQ(-ENOENT, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
+}
+
+TEST_F(LibRadosSnapshotsEC, Rollback) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snap1"));
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ EXPECT_EQ(0, rados_write_full(ioctx, "foo", buf2, sizeof(buf2)));
+ EXPECT_EQ(0, rados_ioctx_snap_rollback(ioctx, "foo", "snap1"));
+ char buf3[sizeof(buf)];
+ EXPECT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
+ EXPECT_EQ(0, memcmp(buf, buf3, sizeof(buf)));
+ EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
+}
+
+TEST_F(LibRadosSnapshotsEC, SnapGetName) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_ioctx_snap_create(ioctx, "snapfoo"));
+ rados_snap_t rid;
+ EXPECT_EQ(0, rados_ioctx_snap_lookup(ioctx, "snapfoo", &rid));
+ EXPECT_EQ(-ENOENT, rados_ioctx_snap_lookup(ioctx, "snapbar", &rid));
+ char name[128];
+ memset(name, 0, sizeof(name));
+ EXPECT_EQ(0, rados_ioctx_snap_get_name(ioctx, rid, name, sizeof(name)));
+ time_t snaptime;
+ EXPECT_EQ(0, rados_ioctx_snap_get_stamp(ioctx, rid, &snaptime));
+ EXPECT_EQ(0, strcmp(name, "snapfoo"));
+ EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snapfoo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedEC, Snap) {
+ SKIP_IF_CRIMSON();
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, bsize, 0));
+
+ my_snaps.push_back(-2);
+ rados_completion_t completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr,
+ &completion));
+ rados_aio_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back(), completion);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(completion));
+ rados_aio_release(completion);
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char *buf2 = (char *)new char[bsize];
+ memset(buf2, 0xdd, bsize);
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, bsize, bsize));
+ rados_ioctx_snap_set_read(ioctx, my_snaps[1]-1);
+ char *buf3 = (char *)new char[bsize*2];
+ ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf3, bsize*2, 0));
+
+ rados_ioctx_snap_set_read(ioctx, my_snaps[1]);
+ ASSERT_EQ(bsize, rados_read(ioctx, "foo", buf3, bsize*2, 0));
+ ASSERT_EQ(0, memcmp(buf3, buf, bsize));
+
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr,
+ &completion));
+ rados_aio_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back(), completion);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(completion));
+ rados_aio_release(completion);
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ rados_ioctx_snap_set_read(ioctx, LIBRADOS_SNAP_HEAD);
+ ASSERT_EQ(0, rados_remove(ioctx, "foo"));
+ delete[] buf;
+ delete[] buf2;
+ delete[] buf3;
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedEC, Rollback) {
+ SKIP_IF_CRIMSON();
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, bsize, 0));
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char *buf2 = (char *)new char[bsize];
+ memset(buf2, 0xdd, bsize);
+
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, bsize, bsize));
+ rados_ioctx_selfmanaged_snap_rollback(ioctx, "foo", my_snaps[1]);
+ char *buf3 = (char *)new char[bsize*2];
+ ASSERT_EQ(bsize, rados_read(ioctx, "foo", buf3, bsize*2, 0));
+ ASSERT_EQ(0, memcmp(buf3, buf, bsize));
+
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, rados_remove(ioctx, "foo"));
+ delete[] buf;
+ delete[] buf2;
+ delete[] buf3;
+}
diff --git a/src/test/librados/snapshots_cxx.cc b/src/test/librados/snapshots_cxx.cc
new file mode 100644
index 000000000..8098b2cb7
--- /dev/null
+++ b/src/test/librados/snapshots_cxx.cc
@@ -0,0 +1,790 @@
+#include <algorithm>
+#include <errno.h>
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "include/rados.h"
+#include "include/rados/librados.hpp"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+#include "crimson_utils.h"
+
+using namespace librados;
+
+typedef RadosTestPP LibRadosSnapshotsPP;
+typedef RadosTestPP LibRadosSnapshotsSelfManagedPP;
+typedef RadosTestECPP LibRadosSnapshotsECPP;
+typedef RadosTestECPP LibRadosSnapshotsSelfManagedECPP;
+
+const int bufsize = 128;
+
+TEST_F(LibRadosSnapshotsPP, SnapListPP) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+ ASSERT_EQ(0, ioctx.snap_create("snap1"));
+ ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+ std::vector<snap_t> snaps;
+ EXPECT_EQ(0, ioctx.snap_list(&snaps));
+ EXPECT_EQ(1U, snaps.size());
+ snap_t rid;
+ EXPECT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+ EXPECT_EQ(rid, snaps[0]);
+ EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+ ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+}
+
+TEST_F(LibRadosSnapshotsPP, SnapRemovePP) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snap1"));
+ rados_snap_t rid;
+ ASSERT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+ ASSERT_EQ(0, ioctx.snap_remove("snap1"));
+ ASSERT_EQ(-ENOENT, ioctx.snap_lookup("snap1", &rid));
+}
+
+TEST_F(LibRadosSnapshotsPP, RollbackPP) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snap1"));
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ EXPECT_EQ(0, ioctx.write_full("foo", bl2));
+ EXPECT_EQ(0, ioctx.snap_rollback("foo", "snap1"));
+ bufferlist bl3;
+ EXPECT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+ EXPECT_EQ(0, memcmp(buf, bl3.c_str(), sizeof(buf)));
+ EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+}
+
+TEST_F(LibRadosSnapshotsPP, SnapGetNamePP) {
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+ rados_snap_t rid;
+ EXPECT_EQ(0, ioctx.snap_lookup("snapfoo", &rid));
+ EXPECT_EQ(-ENOENT, ioctx.snap_lookup("snapbar", &rid));
+ std::string name;
+ EXPECT_EQ(0, ioctx.snap_get_name(rid, &name));
+ time_t snaptime;
+ EXPECT_EQ(0, ioctx.snap_get_stamp(rid, &snaptime));
+ EXPECT_EQ(0, strcmp(name.c_str(), "snapfoo"));
+ EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+}
+
+TEST_F(LibRadosSnapshotsPP, SnapCreateRemovePP) {
+ // reproduces http://tracker.ceph.com/issues/10262
+ bufferlist bl;
+ bl.append("foo");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+ ASSERT_EQ(0, ioctx.remove("foo"));
+ ASSERT_EQ(0, ioctx.snap_create("snapbar"));
+
+ std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
+ op->create(false);
+ op->remove();
+ ASSERT_EQ(0, ioctx.operate("foo", op.get()));
+
+ EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+ EXPECT_EQ(0, ioctx.snap_remove("snapbar"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, SnapPP) {
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ASSERT_TRUE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ my_snaps.push_back(-2);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ioctx.aio_selfmanaged_snap_create(&my_snaps.back(), completion);
+ ASSERT_EQ(0, completion->wait_for_complete());
+ completion->release();
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+
+ ioctx.snap_set_read(my_snaps[1]);
+ bufferlist bl3;
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+
+ completion = cluster.aio_create_completion();
+ ioctx.aio_selfmanaged_snap_remove(my_snaps.back(), completion);
+ ASSERT_EQ(0, completion->wait_for_complete());
+ completion->release();
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+ ASSERT_TRUE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+ ASSERT_EQ(0, ioctx.remove("foo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, RollbackPP) {
+ SKIP_IF_CRIMSON();
+ std::vector<uint64_t> my_snaps;
+ IoCtx readioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
+ readioctx.set_namespace(nspace);
+ readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ //Write 3 consecutive buffers
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*2));
+
+ snap_set_t ss;
+
+ snap_t head = SNAP_HEAD;
+ ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+ ASSERT_EQ(1u, ss.clones.size());
+ ASSERT_EQ(head, ss.clones[0].cloneid);
+ ASSERT_EQ(0u, ss.clones[0].snaps.size());
+ ASSERT_EQ(0u, ss.clones[0].overlap.size());
+ ASSERT_EQ(384u, ss.clones[0].size);
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ //Change the middle buffer
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize));
+ //Add another after
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*3));
+
+ ASSERT_EQ(-EINVAL, ioctx.list_snaps("foo", &ss));
+ ObjectReadOperation o;
+ o.list_snaps(&ss, NULL);
+ ASSERT_EQ(-EINVAL, ioctx.operate("foo", &o, NULL));
+
+ ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+ ASSERT_EQ(2u, ss.clones.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+ ASSERT_EQ(1u, ss.clones[0].snaps.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+ ASSERT_EQ(2u, ss.clones[0].overlap.size());
+ ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
+ ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
+ ASSERT_EQ(384u, ss.clones[0].size);
+ ASSERT_EQ(head, ss.clones[1].cloneid);
+ ASSERT_EQ(0u, ss.clones[1].snaps.size());
+ ASSERT_EQ(0u, ss.clones[1].overlap.size());
+ ASSERT_EQ(512u, ss.clones[1].size);
+
+ ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]);
+
+ bufferlist bl3;
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), bufsize));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), bufsize*2));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ((int)0, ioctx.read("foo", bl3, sizeof(buf), bufsize*3));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ readioctx.close();
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, SnapOverlapPP) {
+ // WIP https://tracker.ceph.com/issues/58263
+ SKIP_IF_CRIMSON();
+ std::vector<uint64_t> my_snaps;
+ IoCtx readioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
+ readioctx.set_namespace(nspace);
+ readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*2));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*4));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*6));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*8));
+
+ snap_set_t ss;
+ snap_t head = SNAP_HEAD;
+ ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+ ASSERT_EQ(1u, ss.clones.size());
+ ASSERT_EQ(head, ss.clones[0].cloneid);
+ ASSERT_EQ(0u, ss.clones[0].snaps.size());
+ ASSERT_EQ(0u, ss.clones[0].overlap.size());
+ ASSERT_EQ(1152u, ss.clones[0].size);
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*1));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*3));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*5));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*7));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*9));
+
+ ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+ ASSERT_EQ(2u, ss.clones.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+ ASSERT_EQ(1u, ss.clones[0].snaps.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+ ASSERT_EQ(5u, ss.clones[0].overlap.size());
+ ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
+ ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
+ ASSERT_EQ(512u, ss.clones[0].overlap[2].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[2].second);
+ ASSERT_EQ(768u, ss.clones[0].overlap[3].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[3].second);
+ ASSERT_EQ(1024u, ss.clones[0].overlap[4].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[4].second);
+ ASSERT_EQ(1152u, ss.clones[0].size);
+ ASSERT_EQ(head, ss.clones[1].cloneid);
+ ASSERT_EQ(0u, ss.clones[1].snaps.size());
+ ASSERT_EQ(0u, ss.clones[1].overlap.size());
+ ASSERT_EQ(1280u, ss.clones[1].size);
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+ char buf3[sizeof(buf)];
+ memset(buf3, 0xee, sizeof(buf3));
+ bufferlist bl4;
+ bl4.append(buf3, sizeof(buf3));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*1));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*4));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*5));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*8));
+
+ ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+ ASSERT_EQ(3u, ss.clones.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+ ASSERT_EQ(1u, ss.clones[0].snaps.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+ ASSERT_EQ(5u, ss.clones[0].overlap.size());
+ ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
+ ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
+ ASSERT_EQ(512u, ss.clones[0].overlap[2].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[2].second);
+ ASSERT_EQ(768u, ss.clones[0].overlap[3].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[3].second);
+ ASSERT_EQ(1024u, ss.clones[0].overlap[4].first);
+ ASSERT_EQ(128u, ss.clones[0].overlap[4].second);
+ ASSERT_EQ(1152u, ss.clones[0].size);
+
+ ASSERT_EQ(my_snaps[2], ss.clones[1].cloneid);
+ ASSERT_EQ(1u, ss.clones[1].snaps.size());
+ ASSERT_EQ(my_snaps[2], ss.clones[1].snaps[0]);
+ ASSERT_EQ(4u, ss.clones[1].overlap.size());
+ ASSERT_EQ(0u, ss.clones[1].overlap[0].first);
+ ASSERT_EQ(128u, ss.clones[1].overlap[0].second);
+ ASSERT_EQ(256u, ss.clones[1].overlap[1].first);
+ ASSERT_EQ(256u, ss.clones[1].overlap[1].second);
+ ASSERT_EQ(768u, ss.clones[1].overlap[2].first);
+ ASSERT_EQ(256u, ss.clones[1].overlap[2].second);
+ ASSERT_EQ(1152u, ss.clones[1].overlap[3].first);
+ ASSERT_EQ(128u, ss.clones[1].overlap[3].second);
+ ASSERT_EQ(1280u, ss.clones[1].size);
+
+ ASSERT_EQ(head, ss.clones[2].cloneid);
+ ASSERT_EQ(0u, ss.clones[2].snaps.size());
+ ASSERT_EQ(0u, ss.clones[2].overlap.size());
+ ASSERT_EQ(1280u, ss.clones[2].size);
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ readioctx.close();
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, Bug11677) {
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+ int bsize = 1<<20;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ bufferlist bl1;
+ bl1.append(buf, bsize);
+ ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+ std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
+ op->assert_exists();
+ op->remove();
+ ASSERT_EQ(0, ioctx.operate("foo", op.get()));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+ delete[] buf;
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, OrderSnap) {
+ std::vector<uint64_t> my_snaps;
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ int flags = librados::OPERATION_ORDERSNAP;
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ObjectWriteOperation op1;
+ op1.write(0, bl);
+ librados::AioCompletion *comp1 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", comp1, &op1, flags));
+ ASSERT_EQ(0, comp1->wait_for_complete());
+ ASSERT_EQ(0, comp1->get_return_value());
+ comp1->release();
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ObjectWriteOperation op2;
+ op2.write(0, bl);
+ librados::AioCompletion *comp2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", comp2, &op2, flags));
+ ASSERT_EQ(0, comp2->wait_for_complete());
+ ASSERT_EQ(0, comp2->get_return_value());
+ comp2->release();
+
+ my_snaps.pop_back();
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ObjectWriteOperation op3;
+ op3.write(0, bl);
+ librados::AioCompletion *comp3 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", comp3, &op3, flags));
+ ASSERT_EQ(0, comp3->wait_for_complete());
+ ASSERT_EQ(-EOLDSNAPC, comp3->get_return_value());
+ comp3->release();
+
+ ObjectWriteOperation op4;
+ op4.write(0, bl);
+ librados::AioCompletion *comp4 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", comp4, &op4, 0));
+ ASSERT_EQ(0, comp4->wait_for_complete());
+ ASSERT_EQ(0, comp4->get_return_value());
+ comp4->release();
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, WriteRollback) {
+ // https://tracker.ceph.com/issues/59114
+ GTEST_SKIP();
+ uint64_t snapid = 5;
+
+ // buf1
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+
+ // buf2
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+
+ // First write
+ ObjectWriteOperation op_write1;
+ op_write1.write(0, bl);
+ // Operate
+ librados::AioCompletion *comp_write = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", comp_write, &op_write1, 0));
+ ASSERT_EQ(0, comp_write->wait_for_complete());
+ ASSERT_EQ(0, comp_write->get_return_value());
+ comp_write->release();
+
+ // Take Snapshot
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&snapid));
+
+ // Rollback + Second write in the same op
+ ObjectWriteOperation op_write2_snap_rollback;
+ op_write2_snap_rollback.write(0, bl2);
+ op_write2_snap_rollback.selfmanaged_snap_rollback(snapid);
+ // Operate
+ librados::AioCompletion *comp_write2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", comp_write2, &op_write2_snap_rollback, 0));
+ ASSERT_EQ(0, comp_write2->wait_for_complete());
+ ASSERT_EQ(0, comp_write2->get_return_value());
+ comp_write2->release();
+
+ // Resolved should be first write
+ bufferlist bl3;
+ EXPECT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+ EXPECT_EQ(0, memcmp(buf, bl3.c_str(), sizeof(buf)));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, ReusePurgedSnap) {
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ASSERT_TRUE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ my_snaps.push_back(-2);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ioctx.aio_selfmanaged_snap_create(&my_snaps.back(), completion);
+ ASSERT_EQ(0, completion->wait_for_complete());
+ completion->release();
+
+ std::cout << "deleting snap " << my_snaps.back() << " in pool "
+ << ioctx.get_pool_name() << std::endl;
+ completion = cluster.aio_create_completion();
+ ioctx.aio_selfmanaged_snap_remove(my_snaps.back(), completion);
+ ASSERT_EQ(0, completion->wait_for_complete());
+ completion->release();
+
+ std::cout << "waiting for snaps to purge" << std::endl;
+ sleep(15);
+
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+
+ // scrub it out?
+ //sleep(600);
+}
+
+// EC testing
+TEST_F(LibRadosSnapshotsECPP, SnapListPP) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snap1"));
+ std::vector<snap_t> snaps;
+ EXPECT_EQ(0, ioctx.snap_list(&snaps));
+ EXPECT_EQ(1U, snaps.size());
+ snap_t rid;
+ EXPECT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+ EXPECT_EQ(rid, snaps[0]);
+ EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+}
+
+TEST_F(LibRadosSnapshotsECPP, SnapRemovePP) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snap1"));
+ rados_snap_t rid;
+ ASSERT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+ ASSERT_EQ(0, ioctx.snap_remove("snap1"));
+ ASSERT_EQ(-ENOENT, ioctx.snap_lookup("snap1", &rid));
+}
+
+TEST_F(LibRadosSnapshotsECPP, RollbackPP) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snap1"));
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ EXPECT_EQ(0, ioctx.write_full("foo", bl2));
+ EXPECT_EQ(0, ioctx.snap_rollback("foo", "snap1"));
+ bufferlist bl3;
+ EXPECT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+ EXPECT_EQ(0, memcmp(buf, bl3.c_str(), sizeof(buf)));
+ EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+}
+
+TEST_F(LibRadosSnapshotsECPP, SnapGetNamePP) {
+ SKIP_IF_CRIMSON();
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+ rados_snap_t rid;
+ EXPECT_EQ(0, ioctx.snap_lookup("snapfoo", &rid));
+ EXPECT_EQ(-ENOENT, ioctx.snap_lookup("snapbar", &rid));
+ std::string name;
+ EXPECT_EQ(0, ioctx.snap_get_name(rid, &name));
+ time_t snaptime;
+ EXPECT_EQ(0, ioctx.snap_get_stamp(rid, &snaptime));
+ EXPECT_EQ(0, strcmp(name.c_str(), "snapfoo"));
+ EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedECPP, SnapPP) {
+ SKIP_IF_CRIMSON();
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ bufferlist bl1;
+ bl1.append(buf, bsize);
+ ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+
+ my_snaps.push_back(-2);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ioctx.aio_selfmanaged_snap_create(&my_snaps.back(), completion);
+ ASSERT_EQ(0, completion->wait_for_complete());
+ completion->release();
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char *buf2 = (char *)new char[bsize];
+ memset(buf2, 0xdd, bsize);
+ bufferlist bl2;
+ bl2.append(buf2, bsize);
+ // Add another aligned buffer
+ ASSERT_EQ(0, ioctx.write("foo", bl2, bsize, bsize));
+
+ ioctx.snap_set_read(my_snaps[1]);
+ bufferlist bl3;
+ ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize*3, 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+
+ completion = cluster.aio_create_completion();
+ ioctx.aio_selfmanaged_snap_remove(my_snaps.back(), completion);
+ ASSERT_EQ(0, completion->wait_for_complete());
+ completion->release();
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+ ASSERT_EQ(0, ioctx.remove("foo"));
+ delete[] buf;
+ delete[] buf2;
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedECPP, RollbackPP) {
+ SKIP_IF_CRIMSON();
+ std::vector<uint64_t> my_snaps;
+ IoCtx readioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
+ readioctx.set_namespace(nspace);
+ readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ bufferlist bl1;
+ bl1.append(buf, bsize);
+ //Write 3 consecutive buffers
+ ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, bsize));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, bsize*2));
+
+ snap_set_t ss;
+
+ snap_t head = SNAP_HEAD;
+ ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+ ASSERT_EQ(1u, ss.clones.size());
+ ASSERT_EQ(head, ss.clones[0].cloneid);
+ ASSERT_EQ(0u, ss.clones[0].snaps.size());
+ ASSERT_EQ(0u, ss.clones[0].overlap.size());
+ ASSERT_EQ((unsigned)(bsize*3), ss.clones[0].size);
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ char *buf2 = (char *)new char[bsize];
+ memset(buf2, 0xdd, bsize);
+ bufferlist bl2;
+ bl2.append(buf2, bsize);
+ //Change the middle buffer
+ //ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize));
+ //Add another after
+ ASSERT_EQ(0, ioctx.write("foo", bl2, bsize, bsize*3));
+
+ ASSERT_EQ(-EINVAL, ioctx.list_snaps("foo", &ss));
+ ObjectReadOperation o;
+ o.list_snaps(&ss, NULL);
+ ASSERT_EQ(-EINVAL, ioctx.operate("foo", &o, NULL));
+
+ ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+ ASSERT_EQ(2u, ss.clones.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+ ASSERT_EQ(1u, ss.clones[0].snaps.size());
+ ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+ ASSERT_EQ(1u, ss.clones[0].overlap.size());
+ ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+ ASSERT_EQ((unsigned)bsize*3, ss.clones[0].overlap[0].second);
+ ASSERT_EQ((unsigned)bsize*3, ss.clones[0].size);
+ ASSERT_EQ(head, ss.clones[1].cloneid);
+ ASSERT_EQ(0u, ss.clones[1].snaps.size());
+ ASSERT_EQ(0u, ss.clones[1].overlap.size());
+ ASSERT_EQ((unsigned)bsize*4, ss.clones[1].size);
+
+ ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]);
+
+ bufferlist bl3;
+ ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+ ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, bsize));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+ ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, bsize*2));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+ ASSERT_EQ(0, ioctx.read("foo", bl3, bsize, bsize*3));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ readioctx.close();
+
+ delete[] buf;
+ delete[] buf2;
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedECPP, Bug11677) {
+ SKIP_IF_CRIMSON();
+ std::vector<uint64_t> my_snaps;
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ bufferlist bl1;
+ bl1.append(buf, bsize);
+ ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+
+ my_snaps.push_back(-2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+ std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
+ op->assert_exists();
+ op->remove();
+ ASSERT_EQ(0, ioctx.operate("foo", op.get()));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+ my_snaps.pop_back();
+ ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+ delete[] buf;
+}
diff --git a/src/test/librados/snapshots_stats.cc b/src/test/librados/snapshots_stats.cc
new file mode 100644
index 000000000..143299ab1
--- /dev/null
+++ b/src/test/librados/snapshots_stats.cc
@@ -0,0 +1,332 @@
+#include "include/rados.h"
+#include "json_spirit/json_spirit.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+
+#include <algorithm>
+#include <errno.h>
+#include "gtest/gtest.h"
+#include <string>
+#include <vector>
+
+using std::string;
+
+class LibRadosSnapshotStatsSelfManaged : public RadosTest {
+public:
+ LibRadosSnapshotStatsSelfManaged() {};
+ ~LibRadosSnapshotStatsSelfManaged() override {};
+protected:
+ void SetUp() override {
+ // disable pg autoscaler for the tests
+ string c =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"off\""
+ "}";
+ char *cmd[1];
+ cmd[0] = (char *)c.c_str();
+ std::cout << "Setting pg_autoscaler to 'off'" << std::endl;
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL,
+ 0, NULL, 0));
+
+ // disable scrubs for the test
+ c = string("{\"prefix\": \"osd set\",\"key\":\"noscrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+ c = string("{\"prefix\": \"osd set\",\"key\":\"nodeep-scrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ RadosTest::SetUp();
+ }
+
+ void TearDown() override {
+ // re-enable pg autoscaler
+ string c =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"on\""
+ "}";
+ char *cmd[1];
+ cmd[0] = (char *)c.c_str();
+ std::cout << "Setting pg_autoscaler to 'on'" << std::endl;
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL,
+ 0, NULL, 0));
+
+ // re-enable scrubs
+ c = string("{\"prefix\": \"osd unset\",\"key\":\"noscrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+ c = string("{\"prefix\": \"osd unset\",\"key\":\"nodeep-scrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ RadosTest::TearDown();
+ }
+};
+
+class LibRadosSnapshotStatsSelfManagedEC : public RadosTestEC {
+public:
+ LibRadosSnapshotStatsSelfManagedEC() {};
+ ~LibRadosSnapshotStatsSelfManagedEC() override {};
+protected:
+ void SetUp() override {
+ // disable pg autoscaler for the tests
+ string c =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"off\""
+ "}";
+ char *cmd[1];
+ cmd[0] = (char *)c.c_str();
+ std::cout << "Setting pg_autoscaler to 'off'" << std::endl;
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL,
+ 0, NULL, 0));
+
+ // disable scrubs for the test
+ c = string("{\"prefix\": \"osd set\",\"key\":\"noscrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+ c = string("{\"prefix\": \"osd set\",\"key\":\"nodeep-scrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ RadosTestEC::SetUp();
+ }
+
+ void TearDown() override {
+ // re-enable pg autoscaler
+ string c =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"on\""
+ "}";
+ char *cmd[1];
+ cmd[0] = (char *)c.c_str();
+ std::cout << "Setting pg_autoscaler to 'on'" << std::endl;
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL,
+ 0, NULL, 0));
+
+ // re-enable scrubs
+ c = string("{\"prefix\": \"osd unset\",\"key\":\"noscrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+ c = string("{\"prefix\": \"osd unset\",\"key\":\"nodeep-scrub\"}");
+ cmd[0] = (char *)c.c_str();
+ ASSERT_EQ(0, rados_mon_command(s_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ RadosTestEC::TearDown();
+ }
+};
+
+void get_snaptrim_stats(json_spirit::Object& pg_dump,
+ int *objs_trimmed,
+ double *trim_duration) {
+ // pg_map
+ json_spirit::Object pgmap;
+ for (json_spirit::Object::size_type i = 0; i < pg_dump.size(); ++i) {
+ json_spirit::Pair& p = pg_dump[i];
+ if (p.name_ == "pg_map") {
+ pgmap = p.value_.get_obj();
+ break;
+ }
+ }
+
+ // pg_stats array
+ json_spirit::Array pgs;
+ for (json_spirit::Object::size_type i = 0; i < pgmap.size(); ++i) {
+ json_spirit::Pair& p = pgmap[i];
+ if (p.name_ == "pg_stats") {
+ pgs = p.value_.get_array();
+ break;
+ }
+ }
+
+ // snaptrim stats
+ for (json_spirit::Object::size_type j = 0; j < pgs.size(); ++j) {
+ json_spirit::Object& pg_stat = pgs[j].get_obj();
+ for(json_spirit::Object::size_type k = 0; k < pg_stat.size(); ++k) {
+ json_spirit::Pair& stats = pg_stat[k];
+ if (stats.name_ == "objects_trimmed") {
+ *objs_trimmed += stats.value_.get_int();
+ }
+ if (stats.name_ == "snaptrim_duration") {
+ *trim_duration += stats.value_.get_real();
+ }
+ }
+ }
+}
+
+const int bufsize = 128;
+
+TEST_F(LibRadosSnapshotStatsSelfManaged, SnaptrimStats) {
+ int num_objs = 10;
+
+ // create objects
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, rados_write(ioctx, obj.c_str(), buf, sizeof(buf), 0));
+ }
+
+ std::vector<uint64_t> my_snaps;
+ for (int snap = 0; snap < 1; ++snap) {
+ // create a snapshot, clone
+ std::vector<uint64_t> ns(1);
+ ns.insert(ns.end(), my_snaps.begin(), my_snaps.end());
+ my_snaps.swap(ns);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps[0]));
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, rados_write(ioctx, obj.c_str(), buf2, sizeof(buf2), 0));
+ }
+ }
+
+ // wait for maps to settle
+ ASSERT_EQ(0, rados_wait_for_latest_osdmap(cluster));
+
+ // remove snaps - should trigger snaptrim
+ rados_ioctx_snap_set_read(ioctx, LIBRADOS_SNAP_HEAD);
+ for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps[snap]));
+ }
+
+ // sleep for few secs for the trim stats to populate
+ std::cout << "Waiting for snaptrim stats to be generated" << std::endl;
+ sleep(30);
+
+ // Dump pg stats and determine if snaptrim stats are getting set
+ int objects_trimmed = 0;
+ double snaptrim_duration = 0.0;
+ int tries = 0;
+ do {
+ char *buf, *st;
+ size_t buflen, stlen;
+ string c = string("{\"prefix\": \"pg dump\",\"format\":\"json\"}");
+ const char *cmd = c.c_str();
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)&cmd, 1, "", 0,
+ &buf, &buflen, &st, &stlen));
+ string outstr(buf, buflen);
+ json_spirit::Value v;
+ ASSERT_NE(0, json_spirit::read(outstr, v)) << "unable to parse json."
+ << '\n' << outstr;
+
+ // pg dump object
+ json_spirit::Object& obj = v.get_obj();
+ get_snaptrim_stats(obj, &objects_trimmed, &snaptrim_duration);
+ if (objects_trimmed < num_objs) {
+ tries++;
+ objects_trimmed = 0;
+ std::cout << "Still waiting for all objects to be trimmed... " <<std::endl;
+ sleep(30);
+ }
+ } while(objects_trimmed < num_objs && tries < 5);
+
+ // final check for objects trimmed
+ ASSERT_EQ(objects_trimmed, num_objs);
+ std::cout << "Snaptrim duration: " << snaptrim_duration << std::endl;
+ ASSERT_GT(snaptrim_duration, 0.0);
+
+ // clean-up remaining objects
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, rados_remove(ioctx, obj.c_str()));
+ }
+}
+
+// EC testing
+TEST_F(LibRadosSnapshotStatsSelfManagedEC, SnaptrimStats) {
+ int num_objs = 10;
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ // create objects
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, rados_write(ioctx, obj.c_str(), buf, bsize, 0));
+ }
+
+ std::vector<uint64_t> my_snaps;
+ for (int snap = 0; snap < 1; ++snap) {
+ // create a snapshot, clone
+ std::vector<uint64_t> ns(1);
+ ns.insert(ns.end(), my_snaps.begin(), my_snaps.end());
+ my_snaps.swap(ns);
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_create(ioctx, &my_snaps[0]));
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_set_write_ctx(ioctx, my_snaps[0],
+ &my_snaps[0], my_snaps.size()));
+ char *buf2 = (char *)new char[bsize];
+ memset(buf2, 0xdd, bsize);
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, rados_write(ioctx, obj.c_str(), buf2, bsize, bsize));
+ }
+ delete[] buf2;
+ }
+
+ // wait for maps to settle
+ ASSERT_EQ(0, rados_wait_for_latest_osdmap(cluster));
+
+ // remove snaps - should trigger snaptrim
+ rados_ioctx_snap_set_read(ioctx, LIBRADOS_SNAP_HEAD);
+ for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+ ASSERT_EQ(0, rados_ioctx_selfmanaged_snap_remove(ioctx, my_snaps[snap]));
+ }
+
+ // sleep for few secs for the trim stats to populate
+ std::cout << "Waiting for snaptrim stats to be generated" << std::endl;
+ sleep(30);
+
+ // Dump pg stats and determine if snaptrim stats are getting set
+ int objects_trimmed = 0;
+ double snaptrim_duration = 0.0;
+ int tries = 0;
+ do {
+ char *buf, *st;
+ size_t buflen, stlen;
+ string c = string("{\"prefix\": \"pg dump\",\"format\":\"json\"}");
+ const char *cmd = c.c_str();
+ ASSERT_EQ(0, rados_mon_command(cluster, (const char **)&cmd, 1, 0, 0,
+ &buf, &buflen, &st, &stlen));
+ string outstr(buf, buflen);
+ json_spirit::Value v;
+ ASSERT_NE(0, json_spirit::read(outstr, v)) << "Unable tp parse json."
+ << '\n' << outstr;
+
+ // pg dump object
+ json_spirit::Object& obj = v.get_obj();
+ get_snaptrim_stats(obj, &objects_trimmed, &snaptrim_duration);
+ if (objects_trimmed != num_objs) {
+ tries++;
+ objects_trimmed = 0;
+ std::cout << "Still waiting for all objects to be trimmed... " <<std::endl;
+ sleep(30);
+ }
+ } while (objects_trimmed != num_objs && tries < 5);
+
+ // final check for objects trimmed
+ ASSERT_EQ(objects_trimmed, num_objs);
+ std::cout << "Snaptrim duration: " << snaptrim_duration << std::endl;
+ ASSERT_GT(snaptrim_duration, 0.0);
+
+ // clean-up remaining objects
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, rados_remove(ioctx, obj.c_str()));
+ }
+
+ delete[] buf;
+}
diff --git a/src/test/librados/snapshots_stats_cxx.cc b/src/test/librados/snapshots_stats_cxx.cc
new file mode 100644
index 000000000..f6be3a915
--- /dev/null
+++ b/src/test/librados/snapshots_stats_cxx.cc
@@ -0,0 +1,324 @@
+#include <algorithm>
+#include <errno.h>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "include/rados.h"
+#include "include/rados/librados.hpp"
+#include "json_spirit/json_spirit.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+using namespace librados;
+
+using std::string;
+
+class LibRadosSnapshotStatsSelfManagedPP : public RadosTestPP {
+public:
+ LibRadosSnapshotStatsSelfManagedPP() {};
+ ~LibRadosSnapshotStatsSelfManagedPP() override {};
+protected:
+ void SetUp() override {
+ // disable pg autoscaler for the tests
+ string cmd =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"off\""
+ "}";
+ std::cout << "Setting pg_autoscaler to 'off'" << std::endl;
+ bufferlist inbl;
+ bufferlist outbl;
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ // disable scrubs for the test
+ cmd = "{\"prefix\": \"osd set\",\"key\":\"noscrub\"}";
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+ cmd = "{\"prefix\": \"osd set\",\"key\":\"nodeep-scrub\"}";
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ RadosTestPP::SetUp();
+ }
+
+ void TearDown() override {
+ // re-enable pg autoscaler
+ string cmd =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"on\""
+ "}";
+ std::cout << "Setting pg_autoscaler to 'on'" << std::endl;
+ bufferlist inbl;
+ bufferlist outbl;
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ // re-enable scrubs
+ cmd = "{\"prefix\": \"osd unset\",\"key\":\"noscrub\"}";
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+ cmd = string("{\"prefix\": \"osd unset\",\"key\":\"nodeep-scrub\"}");
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ RadosTestPP::TearDown();
+ }
+};
+
+class LibRadosSnapshotStatsSelfManagedECPP : public RadosTestECPP {
+public:
+ LibRadosSnapshotStatsSelfManagedECPP() {};
+ ~LibRadosSnapshotStatsSelfManagedECPP() override {};
+protected:
+ void SetUp() override {
+ // disable pg autoscaler for the tests
+ string cmd =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"off\""
+ "}";
+ std::cout << "Setting pg_autoscaler to 'off'" << std::endl;
+ bufferlist inbl;
+ bufferlist outbl;
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ // disable scrubs for the test
+ cmd = string("{\"prefix\": \"osd set\",\"key\":\"noscrub\"}");
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+ cmd = string("{\"prefix\": \"osd set\",\"key\":\"nodeep-scrub\"}");
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ RadosTestECPP::SetUp();
+ }
+
+ void TearDown() override {
+ // re-enable pg autoscaler
+ string cmd =
+ "{"
+ "\"prefix\": \"config set\", "
+ "\"who\": \"global\", "
+ "\"name\": \"osd_pool_default_pg_autoscale_mode\", "
+ "\"value\": \"on\""
+ "}";
+ std::cout << "Setting pg_autoscaler to 'on'" << std::endl;
+ bufferlist inbl;
+ bufferlist outbl;
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ // re-enable scrubs
+ cmd = string("{\"prefix\": \"osd unset\",\"key\":\"noscrub\"}");
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+ cmd = string("{\"prefix\": \"osd unset\",\"key\":\"nodeep-scrub\"}");
+ ASSERT_EQ(0, s_cluster.mon_command(cmd, inbl, &outbl, NULL));
+
+ RadosTestECPP::TearDown();
+ }
+};
+
+void get_snaptrim_stats(json_spirit::Object& pg_dump,
+ int *objs_trimmed,
+ double *trim_duration) {
+ // pg_map
+ json_spirit::Object pgmap;
+ for (json_spirit::Object::size_type i = 0; i < pg_dump.size(); ++i) {
+ json_spirit::Pair& p = pg_dump[i];
+ if (p.name_ == "pg_map") {
+ pgmap = p.value_.get_obj();
+ break;
+ }
+ }
+
+ // pg_stats array
+ json_spirit::Array pgs;
+ for (json_spirit::Object::size_type i = 0; i < pgmap.size(); ++i) {
+ json_spirit::Pair& p = pgmap[i];
+ if (p.name_ == "pg_stats") {
+ pgs = p.value_.get_array();
+ break;
+ }
+ }
+
+ // snaptrim stats
+ for (json_spirit::Object::size_type j = 0; j < pgs.size(); ++j) {
+ json_spirit::Object& pg_stat = pgs[j].get_obj();
+ for(json_spirit::Object::size_type k = 0; k < pg_stat.size(); ++k) {
+ json_spirit::Pair& stats = pg_stat[k];
+ if (stats.name_ == "objects_trimmed") {
+ *objs_trimmed += stats.value_.get_int();
+ }
+ if (stats.name_ == "snaptrim_duration") {
+ *trim_duration += stats.value_.get_real();
+ }
+ }
+ }
+}
+const int bufsize = 128;
+
+TEST_F(LibRadosSnapshotStatsSelfManagedPP, SnaptrimStatsPP) {
+ int num_objs = 10;
+
+ // create objects
+ char buf[bufsize];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, ioctx.write(obj, bl, sizeof(buf), 0));
+ }
+
+ std::vector<uint64_t> my_snaps;
+ char buf2[sizeof(buf)];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ for (int snap = 0; snap < 1; ++snap) {
+ // create a snapshot, clone
+ std::vector<uint64_t> ns(1);
+ ns.insert(ns.end(), my_snaps.begin(), my_snaps.end());
+ my_snaps.swap(ns);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, ioctx.write(obj, bl2, sizeof(buf2), 0));
+ }
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // remove snaps - should trigger snaptrim
+ for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+ ioctx.selfmanaged_snap_remove(my_snaps[snap]);
+ }
+
+ // sleep for few secs for the trim stats to populate
+ std::cout << "Waiting for snaptrim stats to be generated" << std::endl;
+ sleep(30);
+
+ // Dump pg stats and determine if snaptrim stats are getting set
+ int objects_trimmed = 0;
+ double snaptrim_duration = 0.0;
+ int tries = 0;
+ do {
+ string cmd = string("{\"prefix\": \"pg dump\",\"format\":\"json\"}");
+ bufferlist inbl;
+ bufferlist outbl;
+ ASSERT_EQ(0, cluster.mon_command(cmd, inbl, &outbl, NULL));
+ string outstr(outbl.c_str(), outbl.length());
+ json_spirit::Value v;
+ ASSERT_NE(0, json_spirit::read(outstr, v)) << "unable to parse json." << '\n' << outstr;
+
+ // pg_map
+ json_spirit::Object& obj = v.get_obj();
+ get_snaptrim_stats(obj, &objects_trimmed, &snaptrim_duration);
+ if (objects_trimmed < num_objs) {
+ tries++;
+ objects_trimmed = 0;
+ std::cout << "Still waiting for all objects to be trimmed... " <<std::endl;
+ sleep(30);
+ }
+ } while(objects_trimmed < num_objs && tries < 5);
+
+ // final check for objects trimmed
+ ASSERT_EQ(objects_trimmed, num_objs);
+ std::cout << "Snaptrim duration: " << snaptrim_duration << std::endl;
+ ASSERT_GT(snaptrim_duration, 0.0);
+
+ // clean-up remaining objects
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, ioctx.remove(obj));
+ }
+}
+
+// EC testing
+TEST_F(LibRadosSnapshotStatsSelfManagedECPP, SnaptrimStatsECPP) {
+ int num_objs = 10;
+ int bsize = alignment;
+
+ // create objects
+ char *buf = (char *)new char[bsize];
+ memset(buf, 0xcc, bsize);
+ bufferlist bl;
+ bl.append(buf, bsize);
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, ioctx.write(obj, bl, bsize, 0));
+ }
+
+ std::vector<uint64_t> my_snaps;
+ char *buf2 = (char *)new char[bsize];
+ memset(buf2, 0xdd, bsize);
+ bufferlist bl2;
+ bl2.append(buf2, bsize);
+ for (int snap = 0; snap < 1; ++snap) {
+ // create a snapshot, clone
+ std::vector<uint64_t> ns(1);
+ ns.insert(ns.end(), my_snaps.begin(), my_snaps.end());
+ my_snaps.swap(ns);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, ioctx.write(obj, bl2, bsize, bsize));
+ }
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // remove snaps - should trigger snaptrim
+ for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+ ioctx.selfmanaged_snap_remove(my_snaps[snap]);
+ }
+
+ // sleep for few secs for the trim stats to populate
+ std::cout << "Waiting for snaptrim stats to be generated" << std::endl;
+ sleep(30);
+
+ // Dump pg stats and determine if snaptrim stats are getting set
+ int objects_trimmed = 0;
+ double snaptrim_duration = 0.0;
+ int tries = 0;
+ do {
+ string cmd = string("{\"prefix\": \"pg dump\",\"format\":\"json\"}");
+ bufferlist inbl;
+ bufferlist outbl;
+ ASSERT_EQ(0, cluster.mon_command(cmd, inbl, &outbl, NULL));
+ string outstr(outbl.c_str(), outbl.length());
+ json_spirit::Value v;
+ ASSERT_NE(0, json_spirit::read(outstr, v)) << "unable to parse json." << '\n' << outstr;
+
+ // pg_map
+ json_spirit::Object& obj = v.get_obj();
+ get_snaptrim_stats(obj, &objects_trimmed, &snaptrim_duration);
+ if (objects_trimmed < num_objs) {
+ tries++;
+ objects_trimmed = 0;
+ std::cout << "Still waiting for all objects to be trimmed... " <<std::endl;
+ sleep(30);
+ }
+ } while(objects_trimmed < num_objs && tries < 5);
+
+ // final check for objects trimmed
+ ASSERT_EQ(objects_trimmed, num_objs);
+ std::cout << "Snaptrim duration: " << snaptrim_duration << std::endl;
+ ASSERT_GT(snaptrim_duration, 0.0);
+
+ // clean-up remaining objects
+ ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+ for (int i = 0; i < num_objs; ++i) {
+ string obj = string("foo") + std::to_string(i);
+ ASSERT_EQ(0, ioctx.remove(obj));
+ }
+
+ delete[] buf;
+ delete[] buf2;
+}
diff --git a/src/test/librados/stat.cc b/src/test/librados/stat.cc
new file mode 100644
index 000000000..6ae14395e
--- /dev/null
+++ b/src/test/librados/stat.cc
@@ -0,0 +1,153 @@
+#include "include/rados/librados.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+
+#include "common/ceph_time.h"
+
+#include <algorithm>
+#include <errno.h>
+#include "gtest/gtest.h"
+#include "crimson_utils.h"
+
+typedef RadosTest LibRadosStat;
+typedef RadosTestEC LibRadosStatEC;
+
+TEST_F(LibRadosStat, Stat) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ uint64_t size = 0;
+ time_t mtime = 0;
+ ASSERT_EQ(0, rados_stat(ioctx, "foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
+}
+
+TEST_F(LibRadosStat, Stat2) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ rados_write_op_t op = rados_create_write_op();
+ rados_write_op_write(op, buf, sizeof(buf), 0);
+ struct timespec ts;
+ ts.tv_sec = 1457129052;
+ ts.tv_nsec = 123456789;
+ ASSERT_EQ(0, rados_write_op_operate2(op, ioctx, "foo", &ts, 0));
+ rados_release_write_op(op);
+
+ uint64_t size = 0;
+ time_t mtime = 0;
+ ASSERT_EQ(0, rados_stat(ioctx, "foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(mtime, ts.tv_sec);
+
+ struct timespec ts2 = {};
+ ASSERT_EQ(0, rados_stat2(ioctx, "foo", &size, &ts2));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(ts2.tv_sec, ts.tv_sec);
+ ASSERT_EQ(ts2.tv_nsec, ts.tv_nsec);
+
+ ASSERT_EQ(-ENOENT, rados_stat2(ioctx, "nonexistent", &size, &ts2));
+}
+
+TEST_F(LibRadosStat, StatNS) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo2", buf, sizeof(buf), 0));
+
+ char buf2[64];
+ memset(buf2, 0xcc, sizeof(buf2));
+ rados_ioctx_set_namespace(ioctx, "nspace");
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
+
+ uint64_t size = 0;
+ time_t mtime = 0;
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_stat(ioctx, "foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
+
+ rados_ioctx_set_namespace(ioctx, "nspace");
+ ASSERT_EQ(0, rados_stat(ioctx, "foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf2), size);
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "foo2", &size, &mtime));
+}
+
+TEST_F(LibRadosStat, ClusterStat) {
+ struct rados_cluster_stat_t result;
+ ASSERT_EQ(0, rados_cluster_stat(cluster, &result));
+}
+
+TEST_F(LibRadosStat, PoolStat) {
+ char buf[128];
+ char actual_pool_name[80];
+ unsigned l = rados_ioctx_get_pool_name(ioctx, actual_pool_name, sizeof(actual_pool_name));
+ ASSERT_EQ(strlen(actual_pool_name), l);
+ ASSERT_EQ(0, strcmp(actual_pool_name, pool_name.c_str()));
+ memset(buf, 0xff, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ struct rados_pool_stat_t stats;
+ memset(&stats, 0, sizeof(stats));
+ ASSERT_EQ(0, rados_ioctx_pool_stat(ioctx, &stats));
+}
+
+TEST_F(LibRadosStatEC, Stat) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ uint64_t size = 0;
+ time_t mtime = 0;
+ ASSERT_EQ(0, rados_stat(ioctx, "foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
+}
+
+TEST_F(LibRadosStatEC, StatNS) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_write(ioctx, "foo2", buf, sizeof(buf), 0));
+
+ char buf2[64];
+ memset(buf2, 0xcc, sizeof(buf2));
+ rados_ioctx_set_namespace(ioctx, "nspace");
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
+
+ uint64_t size = 0;
+ time_t mtime = 0;
+ rados_ioctx_set_namespace(ioctx, "");
+ ASSERT_EQ(0, rados_stat(ioctx, "foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
+
+ rados_ioctx_set_namespace(ioctx, "nspace");
+ ASSERT_EQ(0, rados_stat(ioctx, "foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf2), size);
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
+ ASSERT_EQ(-ENOENT, rados_stat(ioctx, "foo2", &size, &mtime));
+}
+
+TEST_F(LibRadosStatEC, ClusterStat) {
+ SKIP_IF_CRIMSON();
+ struct rados_cluster_stat_t result;
+ ASSERT_EQ(0, rados_cluster_stat(cluster, &result));
+}
+
+TEST_F(LibRadosStatEC, PoolStat) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ char actual_pool_name[80];
+ unsigned l = rados_ioctx_get_pool_name(ioctx, actual_pool_name, sizeof(actual_pool_name));
+ ASSERT_EQ(strlen(actual_pool_name), l);
+ ASSERT_EQ(0, strcmp(actual_pool_name, pool_name.c_str()));
+ memset(buf, 0xff, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ struct rados_pool_stat_t stats;
+ memset(&stats, 0, sizeof(stats));
+ ASSERT_EQ(0, rados_ioctx_pool_stat(ioctx, &stats));
+}
diff --git a/src/test/librados/stat_cxx.cc b/src/test/librados/stat_cxx.cc
new file mode 100644
index 000000000..07b480ad0
--- /dev/null
+++ b/src/test/librados/stat_cxx.cc
@@ -0,0 +1,168 @@
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+#include "crimson_utils.h"
+
+using namespace librados;
+
+typedef RadosTestPP LibRadosStatPP;
+typedef RadosTestECPP LibRadosStatECPP;
+
+TEST_F(LibRadosStatPP, StatPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ uint64_t size;
+ time_t mtime;
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+}
+
+TEST_F(LibRadosStatPP, Stat2Mtime2PP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ librados::ObjectWriteOperation op;
+ struct timespec ts;
+ ts.tv_sec = 1457129052;
+ ts.tv_nsec = 123456789;
+ op.mtime2(&ts);
+ op.write(0, bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+
+ /* XXX time comparison asserts could spuriously fail */
+
+ uint64_t size;
+ time_t mtime;
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(mtime, ts.tv_sec);
+
+ struct timespec ts2;
+ ASSERT_EQ(0, ioctx.stat2("foo", &size, &ts2));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(ts2.tv_sec, ts.tv_sec);
+ ASSERT_EQ(ts2.tv_nsec, ts.tv_nsec);
+
+ ASSERT_EQ(-ENOENT, ioctx.stat2("nonexistent", &size, &ts2));
+}
+
+TEST_F(LibRadosStatPP, ClusterStatPP) {
+ cluster_stat_t cstat;
+ ASSERT_EQ(0, cluster.cluster_stat(cstat));
+}
+
+TEST_F(LibRadosStatPP, PoolStatPP) {
+ std::string n = ioctx.get_pool_name();
+ ASSERT_EQ(n, pool_name);
+ char buf[128];
+ memset(buf, 0xff, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ std::list<std::string> v;
+ std::map<std::string,stats_map> stats;
+ ASSERT_EQ(0, cluster.get_pool_stats(v, stats));
+}
+
+TEST_F(LibRadosStatECPP, StatPP) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ uint64_t size;
+ time_t mtime;
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+}
+
+TEST_F(LibRadosStatECPP, ClusterStatPP) {
+ SKIP_IF_CRIMSON();
+ cluster_stat_t cstat;
+ ASSERT_EQ(0, cluster.cluster_stat(cstat));
+}
+
+TEST_F(LibRadosStatECPP, PoolStatPP) {
+ SKIP_IF_CRIMSON();
+ std::string n = ioctx.get_pool_name();
+ ASSERT_EQ(n, pool_name);
+ char buf[128];
+ memset(buf, 0xff, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ std::list<std::string> v;
+ std::map<std::string,stats_map> stats;
+ ASSERT_EQ(0, cluster.get_pool_stats(v, stats));
+}
+
+TEST_F(LibRadosStatPP, StatPPNS) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo2", bl, sizeof(buf), 0));
+
+ char buf2[64];
+ memset(buf2, 0xbb, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ioctx.set_namespace("nspace");
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+
+ uint64_t size;
+ time_t mtime;
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+
+ ioctx.set_namespace("nspace");
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf2), size);
+ ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+ ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
+}
+
+TEST_F(LibRadosStatECPP, StatPPNS) {
+ SKIP_IF_CRIMSON();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+ ASSERT_EQ(0, ioctx.write("foo2", bl, sizeof(buf), 0));
+
+ char buf2[64];
+ memset(buf2, 0xbb, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ioctx.set_namespace("nspace");
+ ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+
+ uint64_t size;
+ time_t mtime;
+ ioctx.set_namespace("");
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf), size);
+ ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+
+ ioctx.set_namespace("nspace");
+ ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+ ASSERT_EQ(sizeof(buf2), size);
+ ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+ ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
+}
diff --git a/src/test/librados/test.cc b/src/test/librados/test.cc
new file mode 100644
index 000000000..be1b4faf8
--- /dev/null
+++ b/src/test/librados/test.cc
@@ -0,0 +1,198 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "test/librados/test.h"
+
+#include "include/stringify.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+
+#include <errno.h>
+#include <sstream>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <unistd.h>
+#include <iostream>
+#include "gtest/gtest.h"
+
+std::string create_one_pool(
+ const std::string &pool_name, rados_t *cluster, uint32_t pg_num)
+{
+ std::string err_str = connect_cluster(cluster);
+ if (err_str.length())
+ return err_str;
+
+ int ret = rados_pool_create(*cluster, pool_name.c_str());
+ if (ret) {
+ rados_shutdown(*cluster);
+ std::ostringstream oss;
+ oss << "create_one_pool(" << pool_name << ") failed with error " << ret;
+ return oss.str();
+ }
+
+ rados_ioctx_t ioctx;
+ ret = rados_ioctx_create(*cluster, pool_name.c_str(), &ioctx);
+ if (ret < 0) {
+ rados_shutdown(*cluster);
+ std::ostringstream oss;
+ oss << "rados_ioctx_create(" << pool_name << ") failed with error " << ret;
+ return oss.str();
+ }
+
+ rados_application_enable(ioctx, "rados", 1);
+ rados_ioctx_destroy(ioctx);
+ return "";
+}
+
+int destroy_ec_profile(rados_t *cluster,
+ const std::string& pool_name,
+ std::ostream &oss)
+{
+ char buf[1000];
+ snprintf(buf, sizeof(buf),
+ "{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-%s\"}",
+ pool_name.c_str());
+ char *cmd[2];
+ cmd[0] = buf;
+ cmd[1] = NULL;
+ int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL,
+ 0, NULL, 0);
+ if (ret)
+ oss << "rados_mon_command: erasure-code-profile rm testprofile-"
+ << pool_name << " failed with error " << ret;
+ return ret;
+}
+
+int destroy_rule(rados_t *cluster,
+ const std::string &rule,
+ std::ostream &oss)
+{
+ char *cmd[2];
+ std::string tmp = ("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
+ rule + "\"}");
+ cmd[0] = (char*)tmp.c_str();
+ cmd[1] = NULL;
+ int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
+ if (ret)
+ oss << "rados_mon_command: osd crush rule rm " + rule + " failed with error " << ret;
+ return ret;
+}
+
+int destroy_ec_profile_and_rule(rados_t *cluster,
+ const std::string &rule,
+ std::ostream &oss)
+{
+ int ret;
+ ret = destroy_ec_profile(cluster, rule, oss);
+ if (ret)
+ return ret;
+ return destroy_rule(cluster, rule, oss);
+}
+
+
+std::string create_one_ec_pool(const std::string &pool_name, rados_t *cluster)
+{
+ std::string err = connect_cluster(cluster);
+ if (err.length())
+ return err;
+
+ std::ostringstream oss;
+ int ret = destroy_ec_profile_and_rule(cluster, pool_name, oss);
+ if (ret) {
+ rados_shutdown(*cluster);
+ return oss.str();
+ }
+
+ char *cmd[2];
+ cmd[1] = NULL;
+
+ std::string profile_create = "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"crush-failure-domain=osd\"]}";
+ cmd[0] = (char *)profile_create.c_str();
+ ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
+ if (ret) {
+ rados_shutdown(*cluster);
+ oss << "rados_mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
+ return oss.str();
+ }
+
+ std::string cmdstr = "{\"prefix\": \"osd pool create\", \"pool\": \"" +
+ pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}";
+ cmd[0] = (char *)cmdstr.c_str();
+ ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
+ if (ret) {
+ destroy_ec_profile(cluster, pool_name, oss);
+ rados_shutdown(*cluster);
+ oss << "rados_mon_command osd pool create failed with error " << ret;
+ return oss.str();
+ }
+
+ rados_wait_for_latest_osdmap(*cluster);
+ return "";
+}
+
+std::string connect_cluster(rados_t *cluster)
+{
+ char *id = getenv("CEPH_CLIENT_ID");
+ if (id) std::cerr << "Client id is: " << id << std::endl;
+
+ int ret;
+ ret = rados_create(cluster, NULL);
+ if (ret) {
+ std::ostringstream oss;
+ oss << "rados_create failed with error " << ret;
+ return oss.str();
+ }
+ ret = rados_conf_read_file(*cluster, NULL);
+ if (ret) {
+ rados_shutdown(*cluster);
+ std::ostringstream oss;
+ oss << "rados_conf_read_file failed with error " << ret;
+ return oss.str();
+ }
+ rados_conf_parse_env(*cluster, NULL);
+ ret = rados_connect(*cluster);
+ if (ret) {
+ rados_shutdown(*cluster);
+ std::ostringstream oss;
+ oss << "rados_connect failed with error " << ret;
+ return oss.str();
+ }
+ return "";
+}
+
+int destroy_one_pool(const std::string &pool_name, rados_t *cluster)
+{
+ int ret = rados_pool_delete(*cluster, pool_name.c_str());
+ if (ret) {
+ rados_shutdown(*cluster);
+ return ret;
+ }
+ rados_shutdown(*cluster);
+ return 0;
+}
+
+int destroy_one_ec_pool(const std::string &pool_name, rados_t *cluster)
+{
+ int ret = rados_pool_delete(*cluster, pool_name.c_str());
+ if (ret) {
+ rados_shutdown(*cluster);
+ return ret;
+ }
+
+ CephContext *cct = static_cast<CephContext*>(rados_cct(*cluster));
+ if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
+ std::ostringstream oss;
+ ret = destroy_ec_profile_and_rule(cluster, pool_name, oss);
+ if (ret) {
+ rados_shutdown(*cluster);
+ return ret;
+ }
+ }
+
+ rados_wait_for_latest_osdmap(*cluster);
+ rados_shutdown(*cluster);
+ return ret;
+}
diff --git a/src/test/librados/test.h b/src/test/librados/test.h
new file mode 100644
index 000000000..b3e0115fb
--- /dev/null
+++ b/src/test/librados/test.h
@@ -0,0 +1,32 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_TEST_RADOS_API_TEST_H
+#define CEPH_TEST_RADOS_API_TEST_H
+
+#include "include/rados/librados.h"
+#include "test/librados/test_shared.h"
+
+#include <map>
+#include <string>
+#include <unistd.h>
+
+std::string create_one_pool(const std::string &pool_name, rados_t *cluster,
+ uint32_t pg_num=0);
+std::string create_one_ec_pool(const std::string &pool_name, rados_t *cluster);
+std::string connect_cluster(rados_t *cluster);
+int destroy_one_pool(const std::string &pool_name, rados_t *cluster);
+int destroy_one_ec_pool(const std::string &pool_name, rados_t *cluster);
+
+#endif
diff --git a/src/test/librados/test_common.cc b/src/test/librados/test_common.cc
new file mode 100644
index 000000000..647a9ff48
--- /dev/null
+++ b/src/test/librados/test_common.cc
@@ -0,0 +1,167 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/Formatter.h"
+#include "include/stringify.h"
+#include "json_spirit/json_spirit.h"
+#include "test_common.h"
+
+using namespace std;
+
+namespace {
+
+using namespace ceph;
+
+int wait_for_healthy(rados_t *cluster)
+{
+ bool healthy = false;
+ // This timeout is very long because the tests are sometimes
+ // run on a thrashing cluster
+ int timeout = 3600;
+ int slept = 0;
+
+ while(!healthy) {
+ JSONFormatter cmd_f;
+ cmd_f.open_object_section("command");
+ cmd_f.dump_string("prefix", "status");
+ cmd_f.dump_string("format", "json");
+ cmd_f.close_section();
+ std::ostringstream cmd_stream;
+ cmd_f.flush(cmd_stream);
+ const std::string serialized_cmd = cmd_stream.str();
+
+ const char *cmd[2];
+ cmd[1] = NULL;
+ cmd[0] = serialized_cmd.c_str();
+
+ char *outbuf = NULL;
+ size_t outlen = 0;
+ int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0,
+ &outbuf, &outlen, NULL, NULL);
+ if (ret) {
+ return ret;
+ }
+
+ std::string out(outbuf, outlen);
+ rados_buffer_free(outbuf);
+
+ json_spirit::mValue root;
+ [[maybe_unused]] bool json_parse_success = json_spirit::read(out, root);
+ ceph_assert(json_parse_success);
+ json_spirit::mObject root_obj = root.get_obj();
+ json_spirit::mObject pgmap = root_obj["pgmap"].get_obj();
+ json_spirit::mArray pgs_by_state = pgmap["pgs_by_state"].get_array();
+
+ if (pgs_by_state.size() == 1) {
+ json_spirit::mObject state = pgs_by_state[0].get_obj();
+ std::string state_name = state["state_name"].get_str();
+ if (state_name != std::string("active+clean")) {
+ healthy = false;
+ } else {
+ healthy = true;
+ }
+ } else {
+ healthy = false;
+ }
+
+ if (slept >= timeout) {
+ return -ETIMEDOUT;
+ };
+
+ if (!healthy) {
+ sleep(1);
+ slept += 1;
+ }
+ }
+
+ return 0;
+}
+
+int rados_pool_set(
+ rados_t *cluster,
+ const std::string &pool_name,
+ const std::string &var,
+ const std::string &val)
+{
+ JSONFormatter cmd_f;
+ cmd_f.open_object_section("command");
+ cmd_f.dump_string("prefix", "osd pool set");
+ cmd_f.dump_string("pool", pool_name);
+ cmd_f.dump_string("var", var);
+ cmd_f.dump_string("val", val);
+ cmd_f.close_section();
+
+ std::ostringstream cmd_stream;
+ cmd_f.flush(cmd_stream);
+
+ const std::string serialized_cmd = cmd_stream.str();
+
+ const char *cmd[2];
+ cmd[1] = NULL;
+ cmd[0] = serialized_cmd.c_str();
+ int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL,
+ NULL, NULL, NULL);
+ return ret;
+}
+
+struct pool_op_error : std::exception {
+ string msg;
+ pool_op_error(const std::string& pool_name,
+ const std::string& func_name,
+ int err) {
+ std::ostringstream oss;
+ oss << func_name << "(" << pool_name << ") failed with error " << err;
+ msg = oss.str();
+ }
+ const char* what() const noexcept override {
+ return msg.c_str();
+ }
+};
+
+template<typename Func>
+std::string with_healthy_cluster(rados_t* cluster,
+ const std::string& pool_name,
+ Func&& func)
+{
+ try {
+ // Wait for 'creating/backfilling' to clear
+ if (int r = wait_for_healthy(cluster); r != 0) {
+ throw pool_op_error{pool_name, "wait_for_healthy", r};
+ }
+ func();
+ // Wait for 'creating/backfilling' to clear
+ if (int r = wait_for_healthy(cluster); r != 0) {
+ throw pool_op_error{pool_name, "wait_for_healthy", r};
+ }
+ } catch (const pool_op_error& e) {
+ return e.what();
+ }
+ return "";
+}
+}
+
+std::string set_pg_num(
+ rados_t *cluster, const std::string &pool_name, uint32_t pg_num)
+{
+ return with_healthy_cluster(cluster, pool_name, [&] {
+ // Adjust pg_num
+ if (int r = rados_pool_set(cluster, pool_name, "pg_num",
+ stringify(pg_num));
+ r != 0) {
+ throw pool_op_error{pool_name, "set_pg_num", r};
+ }
+ });
+}
+
+std::string set_pgp_num(
+ rados_t *cluster, const std::string &pool_name, uint32_t pgp_num)
+{
+ return with_healthy_cluster(cluster, pool_name, [&] {
+ // Adjust pgp_num
+ if (int r = rados_pool_set(cluster, pool_name, "pgp_num",
+ stringify(pgp_num));
+ r != 0) {
+ throw pool_op_error{pool_name, "set_pgp_num", r};
+ }
+ });
+}
diff --git a/src/test/librados/test_common.h b/src/test/librados/test_common.h
new file mode 100644
index 000000000..71ef9de2c
--- /dev/null
+++ b/src/test/librados/test_common.h
@@ -0,0 +1,9 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.h"
+
+std::string set_pg_num(
+ rados_t *cluster, const std::string &pool_name, uint32_t pg_num);
+std::string set_pgp_num(
+ rados_t *cluster, const std::string &pool_name, uint32_t pgp_num);
diff --git a/src/test/librados/test_cxx.cc b/src/test/librados/test_cxx.cc
new file mode 100644
index 000000000..6c7e353e4
--- /dev/null
+++ b/src/test/librados/test_cxx.cc
@@ -0,0 +1,203 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include "test_cxx.h"
+
+#include "include/stringify.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+
+#include <errno.h>
+#include <sstream>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <unistd.h>
+#include <iostream>
+#include "gtest/gtest.h"
+
+using namespace librados;
+
+std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+ return create_one_pool_pp(pool_name, cluster, {});
+}
+std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster,
+ const std::map<std::string, std::string> &config)
+{
+ std::string err = connect_cluster_pp(cluster, config);
+ if (err.length())
+ return err;
+ int ret = cluster.pool_create(pool_name.c_str());
+ if (ret) {
+ cluster.shutdown();
+ std::ostringstream oss;
+ oss << "cluster.pool_create(" << pool_name << ") failed with error " << ret;
+ return oss.str();
+ }
+
+ IoCtx ioctx;
+ ret = cluster.ioctx_create(pool_name.c_str(), ioctx);
+ if (ret < 0) {
+ cluster.shutdown();
+ std::ostringstream oss;
+ oss << "cluster.ioctx_create(" << pool_name << ") failed with error "
+ << ret;
+ return oss.str();
+ }
+ ioctx.application_enable("rados", true);
+ return "";
+}
+
+int destroy_rule_pp(Rados &cluster,
+ const std::string &rule,
+ std::ostream &oss)
+{
+ bufferlist inbl;
+ int ret = cluster.mon_command("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
+ rule + "\"}", inbl, NULL, NULL);
+ if (ret)
+ oss << "mon_command: osd crush rule rm " + rule + " failed with error " << ret << std::endl;
+ return ret;
+}
+
+int destroy_ec_profile_pp(Rados &cluster, const std::string& pool_name,
+ std::ostream &oss)
+{
+ bufferlist inbl;
+ int ret = cluster.mon_command("{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-" + pool_name + "\"}",
+ inbl, NULL, NULL);
+ if (ret)
+ oss << "mon_command: osd erasure-code-profile rm testprofile-" << pool_name << " failed with error " << ret << std::endl;
+ return ret;
+}
+
+int destroy_ec_profile_and_rule_pp(Rados &cluster,
+ const std::string &rule,
+ std::ostream &oss)
+{
+ int ret;
+ ret = destroy_ec_profile_pp(cluster, rule, oss);
+ if (ret)
+ return ret;
+ return destroy_rule_pp(cluster, rule, oss);
+}
+
+std::string create_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+ std::string err = connect_cluster_pp(cluster);
+ if (err.length())
+ return err;
+
+ std::ostringstream oss;
+ int ret = destroy_ec_profile_and_rule_pp(cluster, pool_name, oss);
+ if (ret) {
+ cluster.shutdown();
+ return oss.str();
+ }
+
+ bufferlist inbl;
+ ret = cluster.mon_command(
+ "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"crush-failure-domain=osd\"]}",
+ inbl, NULL, NULL);
+ if (ret) {
+ cluster.shutdown();
+ oss << "mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
+ return oss.str();
+ }
+
+ ret = cluster.mon_command(
+ "{\"prefix\": \"osd pool create\", \"pool\": \"" + pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}",
+ inbl, NULL, NULL);
+ if (ret) {
+ bufferlist inbl;
+ destroy_ec_profile_pp(cluster, pool_name, oss);
+ cluster.shutdown();
+ oss << "mon_command osd pool create pool:" << pool_name << " pool_type:erasure failed with error " << ret;
+ return oss.str();
+ }
+
+ cluster.wait_for_latest_osdmap();
+ return "";
+}
+
+std::string connect_cluster_pp(librados::Rados &cluster)
+{
+ return connect_cluster_pp(cluster, {});
+}
+
+std::string connect_cluster_pp(librados::Rados &cluster,
+ const std::map<std::string, std::string> &config)
+{
+ char *id = getenv("CEPH_CLIENT_ID");
+ if (id) std::cerr << "Client id is: " << id << std::endl;
+
+ int ret;
+ ret = cluster.init(id);
+ if (ret) {
+ std::ostringstream oss;
+ oss << "cluster.init failed with error " << ret;
+ return oss.str();
+ }
+ ret = cluster.conf_read_file(NULL);
+ if (ret) {
+ cluster.shutdown();
+ std::ostringstream oss;
+ oss << "cluster.conf_read_file failed with error " << ret;
+ return oss.str();
+ }
+ cluster.conf_parse_env(NULL);
+
+ for (auto &setting : config) {
+ ret = cluster.conf_set(setting.first.c_str(), setting.second.c_str());
+ if (ret) {
+ std::ostringstream oss;
+ oss << "failed to set config value " << setting.first << " to '"
+ << setting.second << "': " << strerror(-ret);
+ return oss.str();
+ }
+ }
+
+ ret = cluster.connect();
+ if (ret) {
+ cluster.shutdown();
+ std::ostringstream oss;
+ oss << "cluster.connect failed with error " << ret;
+ return oss.str();
+ }
+ return "";
+}
+
+int destroy_one_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+ int ret = cluster.pool_delete(pool_name.c_str());
+ if (ret) {
+ cluster.shutdown();
+ return ret;
+ }
+ cluster.shutdown();
+ return 0;
+}
+
+int destroy_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+ int ret = cluster.pool_delete(pool_name.c_str());
+ if (ret) {
+ cluster.shutdown();
+ return ret;
+ }
+
+ CephContext *cct = static_cast<CephContext*>(cluster.cct());
+ if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
+ std::ostringstream oss;
+ ret = destroy_ec_profile_and_rule_pp(cluster, pool_name, oss);
+ if (ret) {
+ cluster.shutdown();
+ return ret;
+ }
+ }
+
+ cluster.wait_for_latest_osdmap();
+ cluster.shutdown();
+ return ret;
+}
diff --git a/src/test/librados/test_cxx.h b/src/test/librados/test_cxx.h
new file mode 100644
index 000000000..1d11d6923
--- /dev/null
+++ b/src/test/librados/test_cxx.h
@@ -0,0 +1,19 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+
+#include "include/rados/librados.hpp"
+#include "test/librados/test_shared.h"
+
+std::string create_one_pool_pp(const std::string &pool_name,
+ librados::Rados &cluster);
+std::string create_one_pool_pp(const std::string &pool_name,
+ librados::Rados &cluster,
+ const std::map<std::string, std::string> &config);
+std::string create_one_ec_pool_pp(const std::string &pool_name,
+ librados::Rados &cluster);
+std::string connect_cluster_pp(librados::Rados &cluster);
+std::string connect_cluster_pp(librados::Rados &cluster,
+ const std::map<std::string, std::string> &config);
+int destroy_one_pool_pp(const std::string &pool_name, librados::Rados &cluster);
+int destroy_one_ec_pool_pp(const std::string &pool_name, librados::Rados &cluster);
diff --git a/src/test/librados/test_shared.cc b/src/test/librados/test_shared.cc
new file mode 100644
index 000000000..8b50d112e
--- /dev/null
+++ b/src/test/librados/test_shared.cc
@@ -0,0 +1,44 @@
+#include "test_shared.h"
+
+#include <cstring>
+#include "gtest/gtest.h"
+#include "include/buffer.h"
+
+using namespace ceph;
+
+std::string get_temp_pool_name(const std::string &prefix)
+{
+ char hostname[80];
+ char out[160];
+ memset(hostname, 0, sizeof(hostname));
+ memset(out, 0, sizeof(out));
+ gethostname(hostname, sizeof(hostname)-1);
+ static int num = 1;
+ snprintf(out, sizeof(out), "%s-%d-%d", hostname, getpid(), num);
+ num++;
+ return prefix + out;
+}
+
+void assert_eq_sparse(bufferlist& expected,
+ const std::map<uint64_t, uint64_t>& extents,
+ bufferlist& actual) {
+ auto i = expected.begin();
+ auto p = actual.begin();
+ uint64_t pos = 0;
+ for (auto extent : extents) {
+ const uint64_t start = extent.first;
+ const uint64_t end = start + extent.second;
+ for (; pos < end; ++i, ++pos) {
+ ASSERT_FALSE(i.end());
+ if (pos < start) {
+ // check the hole
+ ASSERT_EQ('\0', *i);
+ } else {
+ // then the extent
+ ASSERT_EQ(*i, *p);
+ ++p;
+ }
+ }
+ }
+ ASSERT_EQ(expected.length(), pos);
+}
diff --git a/src/test/librados/test_shared.h b/src/test/librados/test_shared.h
new file mode 100644
index 000000000..3a18e916e
--- /dev/null
+++ b/src/test/librados/test_shared.h
@@ -0,0 +1,58 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <unistd.h>
+#include <chrono>
+#include <map>
+#include <string>
+#include <thread>
+
+#include "include/buffer_fwd.h"
+
+// helpers shared by librados tests
+std::string get_temp_pool_name(const std::string &prefix = "test-rados-api-");
+void assert_eq_sparse(ceph::bufferlist& expected,
+ const std::map<uint64_t, uint64_t>& extents,
+ ceph::bufferlist& actual);
+class TestAlarm
+{
+public:
+ #ifndef _WIN32
+ TestAlarm() {
+ alarm(2400);
+ }
+ ~TestAlarm() {
+ alarm(0);
+ }
+ #else
+ // TODO: add a timeout mechanism for Windows as well, possibly by using
+ // CreateTimerQueueTimer.
+ TestAlarm() {
+ }
+ ~TestAlarm() {
+ }
+ #endif
+};
+
+template<class Rep, class Period, typename Func, typename... Args,
+ typename Return = std::result_of_t<Func&&(Args&&...)>>
+Return wait_until(const std::chrono::duration<Rep, Period>& rel_time,
+ const std::chrono::duration<Rep, Period>& step,
+ const Return& expected,
+ Func&& func, Args&&... args)
+{
+ std::this_thread::sleep_for(rel_time - step);
+ for (auto& s : {step, step}) {
+ if (!s.count()) {
+ break;
+ }
+ auto ret = func(std::forward<Args>(args)...);
+ if (ret == expected) {
+ return ret;
+ }
+ std::this_thread::sleep_for(s);
+ }
+ return func(std::forward<Args>(args)...);
+}
diff --git a/src/test/librados/testcase_cxx.cc b/src/test/librados/testcase_cxx.cc
new file mode 100644
index 000000000..407c59b55
--- /dev/null
+++ b/src/test/librados/testcase_cxx.cc
@@ -0,0 +1,407 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "testcase_cxx.h"
+
+#include <errno.h>
+#include <fmt/format.h>
+#include "test_cxx.h"
+#include "test_shared.h"
+#include "crimson_utils.h"
+#include "include/scope_guard.h"
+
+using namespace librados;
+
+namespace {
+
+void init_rand() {
+ static bool seeded = false;
+ if (!seeded) {
+ seeded = true;
+ int seed = getpid();
+ std::cout << "seed " << seed << std::endl;
+ srand(seed);
+ }
+}
+
+} // anonymous namespace
+
+std::string RadosTestPPNS::pool_name;
+Rados RadosTestPPNS::s_cluster;
+
+void RadosTestPPNS::SetUpTestCase()
+{
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPPNS::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPPNS::SetUp()
+{
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ bool req;
+ ASSERT_EQ(0, ioctx.pool_requires_alignment2(&req));
+ ASSERT_FALSE(req);
+}
+
+void RadosTestPPNS::TearDown()
+{
+ if (cleanup)
+ cleanup_all_objects(ioctx);
+ ioctx.close();
+}
+
+void RadosTestPPNS::cleanup_all_objects(librados::IoCtx ioctx)
+{
+ // remove all objects to avoid polluting other tests
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ ioctx.set_namespace(all_nspaces);
+ for (NObjectIterator it = ioctx.nobjects_begin();
+ it != ioctx.nobjects_end(); ++it) {
+ ioctx.locator_set_key(it->get_locator());
+ ioctx.set_namespace(it->get_nspace());
+ ASSERT_EQ(0, ioctx.remove(it->get_oid()));
+ }
+}
+
+std::string RadosTestParamPPNS::pool_name;
+std::string RadosTestParamPPNS::cache_pool_name;
+Rados RadosTestParamPPNS::s_cluster;
+
+void RadosTestParamPPNS::SetUpTestCase()
+{
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPPNS::TearDownTestCase()
+{
+ if (cache_pool_name.length()) {
+ // tear down tiers
+ bufferlist inbl;
+ ASSERT_EQ(0, s_cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, s_cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, s_cluster.mon_command(
+ "{\"prefix\": \"osd pool delete\", \"pool\": \"" + cache_pool_name +
+ "\", \"pool2\": \"" + cache_pool_name + "\", \"yes_i_really_really_mean_it\": true}",
+ inbl, NULL, NULL));
+ cache_pool_name = "";
+ }
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPPNS::SetUp()
+{
+ if (!is_crimson_cluster() && strcmp(GetParam(), "cache") == 0 &&
+ cache_pool_name.empty()) {
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ cache_pool_name = get_temp_pool_name();
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd pool create\", \"pool\": \"" + cache_pool_name +
+ "\", \"pg_num\": 4}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+ }
+
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ bool req;
+ ASSERT_EQ(0, ioctx.pool_requires_alignment2(&req));
+ ASSERT_FALSE(req);
+}
+
+void RadosTestParamPPNS::TearDown()
+{
+ if (cleanup)
+ cleanup_all_objects(ioctx);
+ ioctx.close();
+}
+
+void RadosTestParamPPNS::cleanup_all_objects(librados::IoCtx ioctx)
+{
+ // remove all objects to avoid polluting other tests
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ ioctx.set_namespace(all_nspaces);
+ for (NObjectIterator it = ioctx.nobjects_begin();
+ it != ioctx.nobjects_end(); ++it) {
+ ioctx.locator_set_key(it->get_locator());
+ ioctx.set_namespace(it->get_nspace());
+ ASSERT_EQ(0, ioctx.remove(it->get_oid()));
+ }
+}
+
+std::string RadosTestECPPNS::pool_name;
+Rados RadosTestECPPNS::s_cluster;
+
+void RadosTestECPPNS::SetUpTestCase()
+{
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPPNS::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPPNS::SetUp()
+{
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ bool req;
+ ASSERT_EQ(0, ioctx.pool_requires_alignment2(&req));
+ ASSERT_TRUE(req);
+ ASSERT_EQ(0, ioctx.pool_required_alignment2(&alignment));
+ ASSERT_NE(0U, alignment);
+}
+
+void RadosTestECPPNS::TearDown()
+{
+ if (cleanup)
+ cleanup_all_objects(ioctx);
+ ioctx.close();
+}
+
+std::string RadosTestPP::pool_name;
+Rados RadosTestPP::s_cluster;
+
+void RadosTestPP::SetUpTestCase()
+{
+ init_rand();
+
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPP::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPP::SetUp()
+{
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ nspace = get_temp_pool_name();
+ ioctx.set_namespace(nspace);
+ bool req;
+ ASSERT_EQ(0, ioctx.pool_requires_alignment2(&req));
+ ASSERT_FALSE(req);
+}
+
+void RadosTestPP::TearDown()
+{
+ if (cleanup) {
+ cleanup_default_namespace(ioctx);
+ cleanup_namespace(ioctx, nspace);
+ }
+ ioctx.close();
+}
+
+void RadosTestPP::cleanup_default_namespace(librados::IoCtx ioctx)
+{
+ // remove all objects from the default namespace to avoid polluting
+ // other tests
+ cleanup_namespace(ioctx, "");
+}
+
+void RadosTestPP::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
+{
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ ioctx.set_namespace(ns);
+ int tries = 20;
+ while (--tries) {
+ int got_enoent = 0;
+ for (NObjectIterator it = ioctx.nobjects_begin();
+ it != ioctx.nobjects_end(); ++it) {
+ ioctx.locator_set_key(it->get_locator());
+ ObjectWriteOperation op;
+ op.remove();
+ librados::AioCompletion *completion = s_cluster.aio_create_completion();
+ auto sg = make_scope_guard([&] { completion->release(); });
+ ASSERT_EQ(0, ioctx.aio_operate(it->get_oid(), completion, &op,
+ librados::OPERATION_IGNORE_CACHE));
+ completion->wait_for_complete();
+ if (completion->get_return_value() == -ENOENT) {
+ ++got_enoent;
+ std::cout << " got ENOENT removing " << it->get_oid()
+ << " in ns " << ns << std::endl;
+ } else {
+ ASSERT_EQ(0, completion->get_return_value());
+ }
+ }
+ if (!got_enoent) {
+ break;
+ }
+ std::cout << " got ENOENT on " << got_enoent
+ << " objects, waiting a bit for snap"
+ << " trimming before retrying " << tries << " more times..."
+ << std::endl;
+ sleep(1);
+ }
+ if (tries == 0) {
+ std::cout << "failed to clean up; probably need to scrub purged_snaps."
+ << std::endl;
+ }
+}
+
+std::string RadosTestParamPP::pool_name;
+std::string RadosTestParamPP::cache_pool_name;
+Rados RadosTestParamPP::s_cluster;
+
+void RadosTestParamPP::SetUpTestCase()
+{
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPP::TearDownTestCase()
+{
+ if (cache_pool_name.length()) {
+ // tear down tiers
+ bufferlist inbl;
+ ASSERT_EQ(0, s_cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, s_cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, s_cluster.mon_command(
+ "{\"prefix\": \"osd pool delete\", \"pool\": \"" + cache_pool_name +
+ "\", \"pool2\": \"" + cache_pool_name + "\", \"yes_i_really_really_mean_it\": true}",
+ inbl, NULL, NULL));
+ cache_pool_name = "";
+ }
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPP::SetUp()
+{
+ if (!is_crimson_cluster() && strcmp(GetParam(), "cache") == 0 &&
+ cache_pool_name.empty()) {
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ cache_pool_name = get_temp_pool_name();
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd pool create\", \"pool\": \"" + cache_pool_name +
+ "\", \"pg_num\": 4}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+ }
+
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ nspace = get_temp_pool_name();
+ ioctx.set_namespace(nspace);
+ bool req;
+ ASSERT_EQ(0, ioctx.pool_requires_alignment2(&req));
+ ASSERT_FALSE(req);
+}
+
+void RadosTestParamPP::TearDown()
+{
+ if (cleanup) {
+ cleanup_default_namespace(ioctx);
+ cleanup_namespace(ioctx, nspace);
+ }
+ ioctx.close();
+}
+
+void RadosTestParamPP::cleanup_default_namespace(librados::IoCtx ioctx)
+{
+ // remove all objects from the default namespace to avoid polluting
+ // other tests
+ cleanup_namespace(ioctx, "");
+}
+
+void RadosTestParamPP::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
+{
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ ioctx.set_namespace(ns);
+ for (NObjectIterator it = ioctx.nobjects_begin();
+ it != ioctx.nobjects_end(); ++it) {
+ ioctx.locator_set_key(it->get_locator());
+ ASSERT_EQ(0, ioctx.remove(it->get_oid()));
+ }
+}
+
+std::string RadosTestECPP::pool_name;
+Rados RadosTestECPP::s_cluster;
+
+void RadosTestECPP::SetUpTestCase()
+{
+ SKIP_IF_CRIMSON();
+ auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
+ pool_name = get_temp_pool_name(pool_prefix);
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPP::TearDownTestCase()
+{
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPP::SetUp()
+{
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ nspace = get_temp_pool_name();
+ ioctx.set_namespace(nspace);
+ bool req;
+ ASSERT_EQ(0, ioctx.pool_requires_alignment2(&req));
+ ASSERT_TRUE(req);
+ ASSERT_EQ(0, ioctx.pool_required_alignment2(&alignment));
+ ASSERT_NE(0U, alignment);
+}
+
+void RadosTestECPP::TearDown()
+{
+ SKIP_IF_CRIMSON();
+ if (cleanup) {
+ cleanup_default_namespace(ioctx);
+ cleanup_namespace(ioctx, nspace);
+ }
+ ioctx.close();
+}
+
diff --git a/src/test/librados/testcase_cxx.h b/src/test/librados/testcase_cxx.h
new file mode 100644
index 000000000..637ec11ee
--- /dev/null
+++ b/src/test/librados/testcase_cxx.h
@@ -0,0 +1,130 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include "gtest/gtest.h"
+#include "include/rados/librados.hpp"
+
+class RadosTestPPNS : public ::testing::Test {
+public:
+ RadosTestPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
+ ~RadosTestPPNS() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static void cleanup_all_objects(librados::IoCtx ioctx);
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ bool cleanup;
+};
+
+struct RadosTestPPNSCleanup : public RadosTestPPNS {
+ RadosTestPPNSCleanup() : RadosTestPPNS(true) {}
+};
+
+class RadosTestParamPPNS : public ::testing::TestWithParam<const char*> {
+public:
+ RadosTestParamPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
+ ~RadosTestParamPPNS() override {}
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+protected:
+ static void cleanup_all_objects(librados::IoCtx ioctx);
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+ static std::string cache_pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ bool cleanup;
+};
+
+class RadosTestECPPNS : public RadosTestPPNS {
+public:
+ RadosTestECPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
+ ~RadosTestECPPNS() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ uint64_t alignment = 0;
+ bool cleanup;
+};
+
+struct RadosTestECPPNSCleanup : public RadosTestECPPNS {
+ RadosTestECPPNSCleanup() : RadosTestECPPNS(true) {}
+};
+
+class RadosTestPP : public ::testing::Test {
+public:
+ RadosTestPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
+ ~RadosTestPP() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static void cleanup_default_namespace(librados::IoCtx ioctx);
+ static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ bool cleanup;
+ std::string nspace;
+};
+
+class RadosTestParamPP : public ::testing::TestWithParam<const char*> {
+public:
+ RadosTestParamPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
+ ~RadosTestParamPP() override {}
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+protected:
+ static void cleanup_default_namespace(librados::IoCtx ioctx);
+ static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+ static std::string cache_pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ bool cleanup;
+ std::string nspace;
+};
+
+class RadosTestECPP : public RadosTestPP {
+public:
+ RadosTestECPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
+ ~RadosTestECPP() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ bool cleanup;
+ std::string nspace;
+ uint64_t alignment = 0;
+};
diff --git a/src/test/librados/tier_cxx.cc b/src/test/librados/tier_cxx.cc
new file mode 100644
index 000000000..be1d411a9
--- /dev/null
+++ b/src/test/librados/tier_cxx.cc
@@ -0,0 +1,9304 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "mds/mdstypes.h"
+#include "include/buffer.h"
+#include "include/rbd_types.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "include/types.h"
+#include "global/global_context.h"
+#include "common/Cond.h"
+#include "common/ceph_crypto.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+#include "json_spirit/json_spirit.h"
+#include "cls/cas/cls_cas_ops.h"
+#include "cls/cas/cls_cas_internal.h"
+
+#include "osd/HitSet.h"
+
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "cls/cas/cls_cas_client.h"
+#include "cls/cas/cls_cas_internal.h"
+#include "crimson_utils.h"
+
+using namespace std;
+using namespace librados;
+using ceph::crypto::SHA1;
+
+typedef RadosTestPP LibRadosTierPP;
+typedef RadosTestECPP LibRadosTierECPP;
+
+void flush_evict_all(librados::Rados& cluster, librados::IoCtx& cache_ioctx)
+{
+ bufferlist inbl;
+ cache_ioctx.set_namespace(all_nspaces);
+ for (NObjectIterator it = cache_ioctx.nobjects_begin();
+ it != cache_ioctx.nobjects_end(); ++it) {
+ cache_ioctx.locator_set_key(it->get_locator());
+ cache_ioctx.set_namespace(it->get_nspace());
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ cache_ioctx.aio_operate(
+ it->get_oid(), completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL);
+ completion->wait_for_complete();
+ completion->get_return_value();
+ completion->release();
+ }
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ cache_ioctx.aio_operate(
+ it->get_oid(), completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL);
+ completion->wait_for_complete();
+ completion->get_return_value();
+ completion->release();
+ }
+ }
+}
+
+static string _get_required_osd_release(Rados& cluster)
+{
+ bufferlist inbl;
+ string cmd = string("{\"prefix\": \"osd dump\",\"format\":\"json\"}");
+ bufferlist outbl;
+ int r = cluster.mon_command(cmd, inbl, &outbl, NULL);
+ ceph_assert(r >= 0);
+ string outstr(outbl.c_str(), outbl.length());
+ json_spirit::Value v;
+ if (!json_spirit::read(outstr, v)) {
+ cerr <<" unable to parse json " << outstr << std::endl;
+ return "";
+ }
+
+ json_spirit::Object& o = v.get_obj();
+ for (json_spirit::Object::size_type i=0; i<o.size(); i++) {
+ json_spirit::Pair& p = o[i];
+ if (p.name_ == "require_osd_release") {
+ cout << "require_osd_release = " << p.value_.get_str() << std::endl;
+ return p.value_.get_str();
+ }
+ }
+ cerr << "didn't find require_osd_release in " << outstr << std::endl;
+ return "";
+}
+
+void manifest_set_chunk(Rados& cluster, librados::IoCtx& src_ioctx,
+ librados::IoCtx& tgt_ioctx,
+ uint64_t src_offset, uint64_t length,
+ std::string src_oid, std::string tgt_oid)
+{
+ ObjectReadOperation op;
+ op.set_chunk(src_offset, length, src_ioctx, src_oid, 0,
+ CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, tgt_ioctx.aio_operate(tgt_oid, completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+}
+
+static inline void buf_to_hex(const unsigned char *buf, int len, char *str)
+{
+ int i;
+ str[0] = '\0';
+ for (i = 0; i < len; i++) {
+ sprintf(&str[i*2], "%02x", (int)buf[i]);
+ }
+}
+
+void check_fp_oid_refcount(librados::IoCtx& ioctx, std::string foid, uint64_t count,
+ std::string fp_algo = NULL)
+{
+ bufferlist t;
+ int size = foid.length();
+ if (fp_algo == "sha1") {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ SHA1 sha1_gen;
+ sha1_gen.Update((const unsigned char *)foid.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ } else if (fp_algo.empty()) {
+ ioctx.getxattr(foid, CHUNK_REFCOUNT_ATTR, t);
+ } else if (!fp_algo.empty()) {
+ ceph_assert(0 == "unrecognized fingerprint algorithm");
+ }
+
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(count, refs.count());
+}
+
+string get_fp_oid(string oid, std::string fp_algo = NULL)
+{
+ if (fp_algo == "sha1") {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ SHA1 sha1_gen;
+ int size = oid.length();
+ sha1_gen.Update((const unsigned char *)oid.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ return string(p_str);
+ }
+
+ return string();
+}
+
+void is_intended_refcount_state(librados::IoCtx& src_ioctx,
+ std::string src_oid,
+ librados::IoCtx& dst_ioctx,
+ std::string dst_oid,
+ int expected_refcount)
+{
+ int src_refcount = 0, dst_refcount = 0;
+ bufferlist t;
+ int r = dst_ioctx.getxattr(dst_oid, CHUNK_REFCOUNT_ATTR, t);
+ if (r == -ENOENT) {
+ dst_refcount = 0;
+ } else {
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ceph_assert(0);
+ }
+ dst_refcount = refs.count();
+ }
+ int tries = 0;
+ for (; tries < 30; ++tries) {
+ r = cls_cas_references_chunk(src_ioctx, src_oid, dst_oid);
+ if (r == -ENOENT || r == -ENOLINK) {
+ src_refcount = 0;
+ } else if (r == -EBUSY) {
+ sleep(20);
+ continue;
+ } else {
+ src_refcount = r;
+ }
+ break;
+ }
+ ASSERT_TRUE(tries < 30);
+ ASSERT_TRUE(src_refcount >= 0);
+ ASSERT_TRUE(src_refcount == expected_refcount);
+ ASSERT_TRUE(src_refcount <= dst_refcount);
+}
+
+class LibRadosTwoPoolsPP : public RadosTestPP
+{
+public:
+ LibRadosTwoPoolsPP() {};
+ ~LibRadosTwoPoolsPP() override {};
+protected:
+ static void SetUpTestCase() {
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+ }
+ static void TearDownTestCase() {
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+ }
+ static std::string cache_pool_name;
+
+ void SetUp() override {
+ SKIP_IF_CRIMSON();
+ cache_pool_name = get_temp_pool_name();
+ ASSERT_EQ(0, s_cluster.pool_create(cache_pool_name.c_str()));
+ RadosTestPP::SetUp();
+
+ ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+ cache_ioctx.application_enable("rados", true);
+ cache_ioctx.set_namespace(nspace);
+ }
+ void TearDown() override {
+ SKIP_IF_CRIMSON();
+ // flush + evict cache
+ flush_evict_all(cluster, cache_ioctx);
+
+ bufferlist inbl;
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+
+ RadosTestPP::TearDown();
+
+ cleanup_default_namespace(cache_ioctx);
+ cleanup_namespace(cache_ioctx, nspace);
+
+ cache_ioctx.close();
+ ASSERT_EQ(0, s_cluster.pool_delete(cache_pool_name.c_str()));
+ }
+ librados::IoCtx cache_ioctx;
+};
+
+class Completions
+{
+public:
+ Completions() = default;
+ librados::AioCompletion* getCompletion() {
+ librados::AioCompletion* comp = librados::Rados::aio_create_completion();
+ m_completions.push_back(comp);
+ return comp;
+ }
+
+ ~Completions() {
+ for (auto& comp : m_completions) {
+ comp->release();
+ }
+ }
+
+private:
+ vector<librados::AioCompletion *> m_completions;
+};
+
+Completions completions;
+
+std::string LibRadosTwoPoolsPP::cache_pool_name;
+
+TEST_F(LibRadosTierPP, Dirty) {
+ SKIP_IF_CRIMSON();
+ {
+ ObjectWriteOperation op;
+ op.undirty();
+ ASSERT_EQ(0, ioctx.operate("foo", &op)); // still get 0 if it dne
+ }
+ {
+ ObjectWriteOperation op;
+ op.create(true);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_TRUE(dirty);
+ ASSERT_EQ(0, r);
+ }
+ {
+ ObjectWriteOperation op;
+ op.undirty();
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.undirty();
+ ASSERT_EQ(0, ioctx.operate("foo", &op)); // still 0 if already clean
+ }
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+ {
+ ObjectWriteOperation op;
+ op.truncate(0); // still a write even tho it is a no-op
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_TRUE(dirty);
+ ASSERT_EQ(0, r);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Overlay) {
+ SKIP_IF_CRIMSON();
+ // create objects
+ {
+ bufferlist bl;
+ bl.append("base");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("cache");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // by default, the overlay sends us to cache pool
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // unless we say otherwise
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(0, 1, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ ASSERT_EQ('b', bl[0]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Promote) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ }
+
+ // read, trigger a whiteout
+ {
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteSnap) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote on the head
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ ioctx.snap_set_read(my_snaps[0]);
+
+ // read foo snap
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // read bar snap
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // read baz snap
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+
+ // read foo
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // read bar
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // read baz
+ {
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, ioctx.read("baz", bl, 1, 0));
+ }
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteSnapScrub) {
+ SKIP_IF_CRIMSON();
+ int num = 100;
+
+ // create objects
+ for (int i=0; i<num; ++i) {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate(string("foo") + stringify(i), &op));
+ }
+
+ vector<uint64_t> my_snaps;
+ for (int snap=0; snap<4; ++snap) {
+ // create a snapshot, clone
+ vector<uint64_t> ns(1);
+ ns.insert(ns.end(), my_snaps.begin(), my_snaps.end());
+ my_snaps.swap(ns);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ cout << "my_snaps " << my_snaps << std::endl;
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ for (int i=0; i<num; ++i) {
+ bufferlist bl;
+ bl.append(string("ciao! snap") + stringify(snap));
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate(string("foo") + stringify(i), &op));
+ }
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote on _some_ heads to make sure we handle cases
+ // where snaps are present and where they are not.
+ cout << "promoting some heads" << std::endl;
+ for (int i=0; i<num; ++i) {
+ if (i % 5 == 0 || i > num - 3) {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read(string("foo") + stringify(i), bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ }
+
+ for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+ cout << "promoting from clones for snap " << my_snaps[snap] << std::endl;
+ ioctx.snap_set_read(my_snaps[snap]);
+
+ // read some snaps, semi-randomly
+ for (int i=0; i<50; ++i) {
+ bufferlist bl;
+ string o = string("foo") + stringify((snap * i * 137) % 80);
+ //cout << o << std::endl;
+ ASSERT_EQ(1, ioctx.read(o, bl, 1, 0));
+ }
+ }
+
+ // ok, stop and scrub this pool (to make sure scrub can handle
+ // missing clones in the cache tier).
+ {
+ IoCtx cache_ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+ for (int i=0; i<10; ++i) {
+ do {
+ ostringstream ss;
+ ss << "{\"prefix\": \"pg scrub\", \"pgid\": \""
+ << cache_ioctx.get_id() << "." << i
+ << "\"}";
+ int r = cluster.mon_command(ss.str(), inbl, NULL, NULL);
+ if (r == -ENOENT || // in case mgr osdmap is stale
+ r == -EAGAIN) {
+ sleep(5);
+ continue;
+ }
+ } while (false);
+ }
+
+ // give it a few seconds to go. this is sloppy but is usually enough time
+ cout << "waiting for scrubs..." << std::endl;
+ sleep(30);
+ cout << "done waiting" << std::endl;
+ }
+
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+
+ //cleanup
+ for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+ ioctx.selfmanaged_snap_remove(my_snaps[snap]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteSnapTrimRace) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // delete the snap
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps[0]));
+
+ ioctx.snap_set_read(my_snaps[0]);
+
+ // read foo snap. the OSD may or may not realize that this snap has
+ // been logically deleted; either response is valid.
+ {
+ bufferlist bl;
+ int r = ioctx.read("foo", bl, 1, 0);
+ ASSERT_TRUE(r == 1 || r == -ENOENT);
+ }
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsPP, Whiteout) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create some whiteouts, verify they behave
+ {
+ ObjectWriteOperation op;
+ op.assert_exists();
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ {
+ ObjectWriteOperation op;
+ op.assert_exists();
+ op.remove();
+ ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.assert_exists();
+ op.remove();
+ ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+ }
+
+ // verify the whiteouts are there in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // delete a whiteout and verify it goes away
+ ASSERT_EQ(-ENOENT, ioctx.remove("foo"));
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // recreate an object and verify we can read it
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, WhiteoutDeleteCreate) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create an object
+ {
+ bufferlist bl;
+ bl.append("foo");
+ ASSERT_EQ(0, ioctx.write_full("foo", bl));
+ }
+
+ // do delete + create operation
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ bufferlist bl;
+ bl.append("bar");
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify it still "exists" (w/ new content)
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('b', bl[0]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Evict) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ }
+
+ // read, trigger a whiteout, and a dirty object
+ {
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(0, ioctx.write("bar", bl, bl.length(), 0));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // pin
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // evict the pinned object with -EPERM
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE,
+ NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EPERM, completion->get_return_value());
+ completion->release();
+ }
+
+ // unpin
+ {
+ ObjectWriteOperation op;
+ op.cache_unpin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify clean
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // evict
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE,
+ NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, EvictSnap) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote on the head
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // evict bam
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "bam", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "bam", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-ENOENT, completion->get_return_value());
+ completion->release();
+ }
+
+ // read foo snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // evict foo snap
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // snap is gone...
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-ENOENT, completion->get_return_value());
+ completion->release();
+ }
+ // head is still there...
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // promote head + snap of bar
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // evict bar head (fail)
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+
+ // evict bar snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // ...and then head
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+// this test case reproduces http://tracker.ceph.com/issues/8629
+TEST_F(LibRadosTwoPoolsPP, EvictSnap2) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote on the head
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // evict
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify the snapdir is not present in the cache pool
+ {
+ ObjectReadOperation op;
+ librados::snap_set_t snapset;
+ op.list_snaps(&snapset, NULL);
+ ioctx.snap_set_read(librados::SNAP_DIR);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-ENOENT, completion->get_return_value());
+ completion->release();
+ }
+}
+
+//This test case reproduces http://tracker.ceph.com/issues/17445
+TEST_F(LibRadosTwoPoolsPP, ListSnap){
+ SKIP_IF_CRIMSON();
+ // Create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // Create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // Configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // Wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // Read, trigger a promote on the head
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // Read foo snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // Evict foo snap
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // Snap is gone...
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-ENOENT, completion->get_return_value());
+ completion->release();
+ }
+
+ // Do list-snaps
+ ioctx.snap_set_read(CEPH_SNAPDIR);
+ {
+ snap_set_t snap_set;
+ int snap_ret;
+ ObjectReadOperation op;
+ op.list_snaps(&snap_set, &snap_ret);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ 0, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, snap_ret);
+ ASSERT_LT(0u, snap_set.clones.size());
+ for (vector<librados::clone_info_t>::const_iterator r = snap_set.clones.begin();
+ r != snap_set.clones.end();
+ ++r) {
+ if (r->cloneid != librados::SNAP_HEAD) {
+ ASSERT_LT(0u, r->snaps.size());
+ }
+ }
+ }
+
+ // Cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+// This test case reproduces https://tracker.ceph.com/issues/49409
+TEST_F(LibRadosTwoPoolsPP, EvictSnapRollbackReadRace) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ int len = string("hi there").length() * 2;
+ // append more chrunk data make sure the second promote
+ // op coming before the first promote op finished
+ for (int i=0; i<4*1024*1024/len; ++i)
+ bl.append("hi therehi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // create two snapshot, a clone
+ vector<uint64_t> my_snaps(2);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[1]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote on the head
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // try more times
+ int retries = 50;
+ for (int i=0; i<retries; ++i)
+ {
+ {
+ librados::AioCompletion * completion = cluster.aio_create_completion();
+ librados::AioCompletion * completion1 = cluster.aio_create_completion();
+
+ // send a snap rollback op and a snap read op parallel
+ // trigger two promote(copy) to the same snap clone obj
+ // the second snap read op is read-ordered make sure
+ // op not wait for objects_blocked_on_snap_promotion
+ ObjectWriteOperation op;
+ op.selfmanaged_snap_rollback(my_snaps[0]);
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op));
+
+ ioctx.snap_set_read(my_snaps[1]);
+ std::map<uint64_t, uint64_t> extents;
+ bufferlist read_bl;
+ int rval = -1;
+ ObjectReadOperation op1;
+ op1.sparse_read(0, 8, &extents, &read_bl, &rval);
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion1, &op1, &read_bl));
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+
+ completion1->wait_for_complete();
+ ASSERT_EQ(0, completion1->get_return_value());
+ completion1->release();
+ }
+
+ // evict foo snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ }
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+ ioctx.selfmanaged_snap_remove(my_snaps[1]);
+}
+
+TEST_F(LibRadosTwoPoolsPP, TryFlush) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // verify the object is NOT present in the base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // verify dirty
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_TRUE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // pin
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush the pinned object with -EPERM
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EPERM, completion->get_return_value());
+ completion->release();
+ }
+
+ // unpin
+ {
+ ObjectWriteOperation op;
+ op.cache_unpin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify clean
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // verify in base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it != ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // evict it
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify no longer in cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Flush) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ uint64_t user_version = 0;
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // verify the object is NOT present in the base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // verify dirty
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_TRUE(dirty);
+ ASSERT_EQ(0, r);
+ user_version = cache_ioctx.get_last_version();
+ }
+
+ // pin
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush the pinned object with -EPERM
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EPERM, completion->get_return_value());
+ completion->release();
+ }
+
+ // unpin
+ {
+ ObjectWriteOperation op;
+ op.cache_unpin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify clean
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // verify in base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it != ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // evict it
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify no longer in cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // read it again and verify the version is consistent
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ(user_version, cache_ioctx.get_last_version());
+ }
+
+ // erase it
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush whiteout
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // evict
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify no longer in cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+ // or base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, FlushSnap) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("a");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("b");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // and another
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("c");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // verify the object is NOT present in the base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // flush on head (should fail)
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+ // flush on recent snap (should fail)
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+ // flush on oldest snap
+ ioctx.snap_set_read(my_snaps[1]);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // flush on next oldest snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // flush on head
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify i can read the snaps from the cache pool
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('b', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[1]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('a', bl[0]);
+ }
+
+ // remove overlay
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // verify i can read the snaps from the base pool
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('b', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[1]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('a', bl[0]);
+ }
+
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTierPP, FlushWriteRaces) {
+ SKIP_IF_CRIMSON();
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ std::string cache_pool_name = pool_name + "-cache";
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
+ IoCtx cache_ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+ cache_ioctx.application_enable("rados", true);
+ IoCtx ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create/dirty object
+ bufferlist bl;
+ bl.append("hi there");
+ {
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush + write
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ ObjectWriteOperation op2;
+ op2.write_full(bl);
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion2, &op2, 0));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+
+ int tries = 1000;
+ do {
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // try-flush + write
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ ObjectWriteOperation op2;
+ op2.write_full(bl);
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion2, &op2, 0));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ int r = completion->get_return_value();
+ ASSERT_TRUE(r == -EBUSY || r == 0);
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ if (r == -EBUSY)
+ break;
+ cout << "didn't get EBUSY, trying again" << std::endl;
+ }
+ ASSERT_TRUE(--tries);
+ } while (true);
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+
+ ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST_F(LibRadosTwoPoolsPP, FlushTryFlushRaces) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush + flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush + try-flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_try_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+
+ // create/dirty object
+ int tries = 1000;
+ do {
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // try-flush + flush
+ // (flush will not piggyback on try-flush)
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ int r = completion->get_return_value();
+ ASSERT_TRUE(r == -EBUSY || r == 0);
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ if (r == -EBUSY)
+ break;
+ cout << "didn't get EBUSY, trying again" << std::endl;
+ }
+ ASSERT_TRUE(--tries);
+ } while (true);
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // try-flush + try-flush
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_try_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+}
+
+
+IoCtx *read_ioctx = 0;
+ceph::mutex test_lock = ceph::make_mutex("FlushReadRaces::lock");
+ceph::condition_variable cond;
+int max_reads = 100;
+int num_reads = 0; // in progress
+
+void flush_read_race_cb(completion_t cb, void *arg);
+
+void start_flush_read()
+{
+ //cout << " starting read" << std::endl;
+ ObjectReadOperation op;
+ op.stat(NULL, NULL, NULL);
+ librados::AioCompletion *completion = completions.getCompletion();
+ completion->set_complete_callback(0, flush_read_race_cb);
+ read_ioctx->aio_operate("foo", completion, &op, NULL);
+}
+
+void flush_read_race_cb(completion_t cb, void *arg)
+{
+ //cout << " finished read" << std::endl;
+ std::lock_guard l{test_lock};
+ if (num_reads > max_reads) {
+ num_reads--;
+ cond.notify_all();
+ } else {
+ start_flush_read();
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, TryFlushReadRace) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ bufferptr bp(4000000); // make it big!
+ bp.zero();
+ bl.append(bp);
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // start a continuous stream of reads
+ read_ioctx = &ioctx;
+ test_lock.lock();
+ for (int i = 0; i < max_reads; ++i) {
+ start_flush_read();
+ num_reads++;
+ }
+ test_lock.unlock();
+
+ // try-flush
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+
+ // stop reads
+ std::unique_lock locker{test_lock};
+ max_reads = 0;
+ cond.wait(locker, [] { return num_reads == 0;});
+}
+
+TEST_F(LibRadosTierPP, HitSetNone) {
+ SKIP_IF_CRIMSON();
+ {
+ list< pair<time_t,time_t> > ls;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, ioctx.hit_set_list(123, c, &ls));
+ c->wait_for_complete();
+ ASSERT_EQ(0, c->get_return_value());
+ ASSERT_TRUE(ls.empty());
+ c->release();
+ }
+ {
+ bufferlist bl;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, ioctx.hit_set_get(123, c, 12345, &bl));
+ c->wait_for_complete();
+ ASSERT_EQ(-ENOENT, c->get_return_value());
+ c->release();
+ }
+}
+
+string set_pool_str(string pool, string var, string val)
+{
+ return string("{\"prefix\": \"osd pool set\",\"pool\":\"") + pool
+ + string("\",\"var\": \"") + var + string("\",\"val\": \"")
+ + val + string("\"}");
+}
+
+string set_pool_str(string pool, string var, int val)
+{
+ return string("{\"prefix\": \"osd pool set\",\"pool\":\"") + pool
+ + string("\",\"var\": \"") + var + string("\",\"val\": \"")
+ + stringify(val) + string("\"}");
+}
+
+TEST_F(LibRadosTwoPoolsPP, HitSetRead) {
+ SKIP_IF_CRIMSON();
+ // make it a tier
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+
+ // enable hitset tracking for this pool
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
+ "explicit_object"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ cache_ioctx.set_namespace("");
+
+ // keep reading until we see our object appear in the HitSet
+ utime_t start = ceph_clock_now();
+ utime_t hard_stop = start + utime_t(600, 0);
+
+ while (true) {
+ utime_t now = ceph_clock_now();
+ ASSERT_TRUE(now < hard_stop);
+
+ string name = "foo";
+ uint32_t hash;
+ ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+ hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash,
+ cluster.pool_lookup(cache_pool_name.c_str()), "");
+
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, cache_ioctx.read("foo", bl, 1, 0));
+
+ bufferlist hbl;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.hit_set_get(hash, c, now.sec(), &hbl));
+ c->wait_for_complete();
+ c->release();
+
+ if (hbl.length()) {
+ auto p = hbl.cbegin();
+ HitSet hs;
+ decode(hs, p);
+ if (hs.contains(oid)) {
+ cout << "ok, hit_set contains " << oid << std::endl;
+ break;
+ }
+ cout << "hmm, not in HitSet yet" << std::endl;
+ } else {
+ cout << "hmm, no HitSet yet" << std::endl;
+ }
+
+ sleep(1);
+ }
+}
+
+static int _get_pg_num(Rados& cluster, string pool_name)
+{
+ bufferlist inbl;
+ string cmd = string("{\"prefix\": \"osd pool get\",\"pool\":\"")
+ + pool_name
+ + string("\",\"var\": \"pg_num\",\"format\": \"json\"}");
+ bufferlist outbl;
+ int r = cluster.mon_command(cmd, inbl, &outbl, NULL);
+ ceph_assert(r >= 0);
+ string outstr(outbl.c_str(), outbl.length());
+ json_spirit::Value v;
+ if (!json_spirit::read(outstr, v)) {
+ cerr <<" unable to parse json " << outstr << std::endl;
+ return -1;
+ }
+
+ json_spirit::Object& o = v.get_obj();
+ for (json_spirit::Object::size_type i=0; i<o.size(); i++) {
+ json_spirit::Pair& p = o[i];
+ if (p.name_ == "pg_num") {
+ cout << "pg_num = " << p.value_.get_int() << std::endl;
+ return p.value_.get_int();
+ }
+ }
+ cerr << "didn't find pg_num in " << outstr << std::endl;
+ return -1;
+}
+
+int make_hitset(Rados& cluster, librados::IoCtx& cache_ioctx, int num_pg,
+ int num, std::map<int, HitSet>& hitsets, std::string& cache_pool_name)
+{
+ int pg = num_pg;
+ // do a bunch of writes
+ for (int i=0; i<num; ++i) {
+ bufferlist bl;
+ bl.append("a");
+ ceph_assert(0 == cache_ioctx.write(stringify(i), bl, 1, 0));
+ }
+
+ // get HitSets
+ for (int i=0; i<pg; ++i) {
+ list< pair<time_t,time_t> > ls;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ceph_assert(0 == cache_ioctx.hit_set_list(i, c, &ls));
+ c->wait_for_complete();
+ c->release();
+ std::cout << "pg " << i << " ls " << ls << std::endl;
+ ceph_assert(!ls.empty());
+
+ // get the latest
+ c = librados::Rados::aio_create_completion();
+ bufferlist bl;
+ ceph_assert(0 == cache_ioctx.hit_set_get(i, c, ls.back().first, &bl));
+ c->wait_for_complete();
+ c->release();
+
+ try {
+ auto p = bl.cbegin();
+ decode(hitsets[i], p);
+ }
+ catch (buffer::error& e) {
+ std::cout << "failed to decode hit set; bl len is " << bl.length() << "\n";
+ bl.hexdump(std::cout);
+ std::cout << std::endl;
+ throw e;
+ }
+
+ // cope with racing splits by refreshing pg_num
+ if (i == pg - 1)
+ pg = _get_pg_num(cluster, cache_pool_name);
+ }
+ return pg;
+}
+
+TEST_F(LibRadosTwoPoolsPP, HitSetWrite) {
+ SKIP_IF_CRIMSON();
+ int num_pg = _get_pg_num(cluster, pool_name);
+ ceph_assert(num_pg > 0);
+
+ // make it a tier
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+
+ // enable hitset tracking for this pool
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 8),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
+ "explicit_hash"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ cache_ioctx.set_namespace("");
+
+ int num = 200;
+
+ std::map<int,HitSet> hitsets;
+
+ num_pg = make_hitset(cluster, cache_ioctx, num_pg, num, hitsets, cache_pool_name);
+
+ int retry = 0;
+
+ for (int i=0; i<num; ++i) {
+ string n = stringify(i);
+ uint32_t hash;
+ ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(n, &hash));
+ hobject_t oid(sobject_t(n, CEPH_NOSNAP), "", hash,
+ cluster.pool_lookup(cache_pool_name.c_str()), "");
+ std::cout << "checking for " << oid << std::endl;
+ bool found = false;
+ for (int p=0; p<num_pg; ++p) {
+ if (hitsets[p].contains(oid)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found && retry < 5) {
+ num_pg = make_hitset(cluster, cache_ioctx, num_pg, num, hitsets, cache_pool_name);
+ i--;
+ retry++;
+ continue;
+ }
+ ASSERT_TRUE(found);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, HitSetTrim) {
+ SKIP_IF_CRIMSON();
+ unsigned count = 3;
+ unsigned period = 3;
+
+ // make it a tier
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+
+ // enable hitset tracking for this pool
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", count),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", period),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_fpp", ".01"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ cache_ioctx.set_namespace("");
+
+ // do a bunch of writes and make sure the hitsets rotate
+ utime_t start = ceph_clock_now();
+ utime_t hard_stop = start + utime_t(count * period * 50, 0);
+
+ time_t first = 0;
+ while (true) {
+ string name = "foo";
+ uint32_t hash;
+ ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+ hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash, -1, "");
+
+ bufferlist bl;
+ bl.append("f");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, 1, 0));
+
+ list<pair<time_t, time_t> > ls;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.hit_set_list(hash, c, &ls));
+ c->wait_for_complete();
+ c->release();
+
+ cout << " got ls " << ls << std::endl;
+ if (!ls.empty()) {
+ if (!first) {
+ first = ls.front().first;
+ cout << "first is " << first << std::endl;
+ } else {
+ if (ls.front().first != first) {
+ cout << "first now " << ls.front().first << ", trimmed" << std::endl;
+ break;
+ }
+ }
+ }
+
+ utime_t now = ceph_clock_now();
+ ASSERT_TRUE(now < hard_stop);
+
+ sleep(1);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteOn2ndRead) {
+ SKIP_IF_CRIMSON();
+ // create object
+ for (int i=0; i<20; ++i) {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo" + stringify(i), &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // enable hitset tracking for this pool
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_grade_decay_rate", 20),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_search_last_n", 1),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ int fake = 0; // set this to non-zero to test spurious promotion,
+ // e.g. from thrashing
+ int attempt = 0;
+ string obj;
+ while (true) {
+ // 1st read, don't trigger a promote
+ obj = "foo" + stringify(attempt);
+ cout << obj << std::endl;
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+ if (--fake >= 0) {
+ sleep(1);
+ ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+ sleep(1);
+ }
+ }
+
+ // verify the object is NOT present in the cache tier
+ {
+ bool found = false;
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ while (it != cache_ioctx.nobjects_end()) {
+ cout << " see " << it->get_oid() << std::endl;
+ if (it->get_oid() == string(obj.c_str())) {
+ found = true;
+ break;
+ }
+ ++it;
+ }
+ if (!found)
+ break;
+ }
+
+ ++attempt;
+ ASSERT_LE(attempt, 20);
+ cout << "hrm, object is present in cache on attempt " << attempt
+ << ", retrying" << std::endl;
+ }
+
+ // Read until the object is present in the cache tier
+ cout << "verifying " << obj << " is eventually promoted" << std::endl;
+ while (true) {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+
+ bool there = false;
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ while (it != cache_ioctx.nobjects_end()) {
+ if (it->get_oid() == string(obj.c_str())) {
+ there = true;
+ break;
+ }
+ ++it;
+ }
+ if (there)
+ break;
+
+ sleep(1);
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ProxyRead) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"readproxy\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read and verify the object
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // Verify 10 times the object is NOT present in the cache tier
+ uint32_t i = 0;
+ while (i++ < 10) {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ sleep(1);
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, CachePin) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger promote
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+ ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+ }
+
+ // verify the objects are present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ for (uint32_t i = 0; i < 4; i++) {
+ ASSERT_TRUE(it->get_oid() == string("foo") ||
+ it->get_oid() == string("bar") ||
+ it->get_oid() == string("baz") ||
+ it->get_oid() == string("bam"));
+ ++it;
+ }
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // pin objects
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("baz", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // enable agent
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "target_max_objects", 1),
+ inbl, NULL, NULL));
+
+ sleep(10);
+
+ // Verify the pinned object 'foo' is not flushed/evicted
+ uint32_t count = 0;
+ while (true) {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+
+ count = 0;
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ while (it != cache_ioctx.nobjects_end()) {
+ ASSERT_TRUE(it->get_oid() == string("foo") ||
+ it->get_oid() == string("bar") ||
+ it->get_oid() == string("baz") ||
+ it->get_oid() == string("bam"));
+ ++count;
+ ++it;
+ }
+ if (count == 2) {
+ ASSERT_TRUE(it->get_oid() == string("foo") ||
+ it->get_oid() == string("baz"));
+ break;
+ }
+
+ sleep(1);
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, SetRedirectRead) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ {
+ ObjectWriteOperation op;
+ op.set_redirect("bar", cache_ioctx, 0);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // read and verify the object
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('t', bl[0]);
+ }
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestPromoteRead) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet mimic
+ if (_get_required_osd_release(cluster) < "mimic") {
+ GTEST_SKIP() << "cluster is not yet mimic, skipping test";
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("base chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("CHUNK");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // set-redirect
+ {
+ ObjectWriteOperation op;
+ op.set_redirect("bar", cache_ioctx, 0);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 2, "bar-chunk", "foo-chunk");
+
+ // promote
+ {
+ ObjectWriteOperation op;
+ op.tier_promote();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // read and verify the object (redirect)
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('t', bl[0]);
+ }
+ // promote
+ {
+ ObjectWriteOperation op;
+ op.tier_promote();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // read and verify the object
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo-chunk", bl, 1, 0));
+ ASSERT_EQ('C', bl[0]);
+ }
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestRefRead) {
+ SKIP_IF_CRIMSON();
+ // note: require >= mimic
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("base chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("CHUNK");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // set-redirect
+ {
+ ObjectWriteOperation op;
+ op.set_redirect("bar", cache_ioctx, 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // set-chunk
+ {
+ ObjectReadOperation op;
+ op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // redirect's refcount
+ {
+ bufferlist t;
+ cache_ioctx.getxattr("bar", CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_EQ(1U, refs.count());
+ }
+ // chunk's refcount
+ {
+ bufferlist t;
+ cache_ioctx.getxattr("bar-chunk", CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_EQ(1u, refs.count());
+ }
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestUnset) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet nautilus
+ if (_get_required_osd_release(cluster) < "nautilus") {
+ GTEST_SKIP() << "cluster is not yet nautilus, skipping test";
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("base chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("CHUNK");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // set-redirect
+ {
+ ObjectWriteOperation op;
+ op.set_redirect("bar", cache_ioctx, 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // set-chunk
+ {
+ ObjectReadOperation op;
+ op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // redirect's refcount
+ {
+ bufferlist t;
+ cache_ioctx.getxattr("bar", CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_EQ(1u, refs.count());
+ }
+ // chunk's refcount
+ {
+ bufferlist t;
+ cache_ioctx.getxattr("bar-chunk", CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_EQ(1u, refs.count());
+ }
+
+ // unset-manifest for set-redirect
+ {
+ ObjectWriteOperation op;
+ op.unset_manifest();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // unset-manifest for set-chunk
+ {
+ ObjectWriteOperation op;
+ op.unset_manifest();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // redirect's refcount
+ {
+ bufferlist t;
+ cache_ioctx.getxattr("bar-chunk", CHUNK_REFCOUNT_ATTR, t);
+ if (t.length() != 0U) {
+ ObjectWriteOperation op;
+ op.unset_manifest();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EOPNOTSUPP, completion->get_return_value());
+ completion->release();
+ }
+ }
+ // chunk's refcount
+ {
+ bufferlist t;
+ cache_ioctx.getxattr("bar-chunk", CHUNK_REFCOUNT_ATTR, t);
+ if (t.length() != 0U) {
+ ObjectWriteOperation op;
+ op.unset_manifest();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EOPNOTSUPP, completion->get_return_value());
+ completion->release();
+ }
+ }
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestDedupRefRead) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet nautilus
+ if (_get_required_osd_release(cluster) < "nautilus") {
+ GTEST_SKIP() << "cluster is not yet nautilus, skipping test";
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+ string tgt_oid;
+
+ // get fp_oid
+ tgt_oid = get_fp_oid("There hi", "sha1");
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("There hi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("There hi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo-dedup", &op));
+ }
+
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("There hi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(tgt_oid, &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 8, tgt_oid, "foo-dedup");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 8, tgt_oid, "foo");
+ // chunk's refcount
+ {
+ bufferlist t;
+ cache_ioctx.getxattr(tgt_oid, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(2u, refs.count());
+ }
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestSnapRefcount) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("there hi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ string er_fp_oid, hi_fp_oid, bb_fp_oid;
+
+ // get fp_oid
+ er_fp_oid = get_fp_oid("er", "sha1");
+ hi_fp_oid = get_fp_oid("hi", "sha1");
+ bb_fp_oid = get_fp_oid("bb", "sha1");
+
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("er");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(er_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("hi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(hi_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("bb");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(bb_fp_oid, &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, er_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, hi_fp_oid, "foo");
+
+ // make all chunks dirty --> flush
+ // foo: [er] [hi]
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("er");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"er", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ cache_ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(1u, refs.count());
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // foo: [bb] [hi]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("Thbbe");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // make clean
+ {
+ bufferlist bl;
+ bl.append("Thbbe");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, bb_fp_oid, "foo");
+
+ // and another
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // foo: [er] [hi]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("There");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // make clean
+ {
+ bufferlist bl;
+ bl.append("There");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, er_fp_oid, "foo");
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("er");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"er", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ cache_ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(2u, refs.count());
+ }
+
+ // and another
+ my_snaps.resize(3);
+ my_snaps[2] = my_snaps[1];
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // foo: [bb] [hi]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("Thbbe");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // make clean
+ {
+ bufferlist bl;
+ bl.append("Thbbe");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, bb_fp_oid, "foo");
+
+ /*
+ * snap[2]: [er] [hi]
+ * snap[1]: [bb] [hi]
+ * snap[0]: [er] [hi]
+ * head: [bb] [hi]
+ */
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("hi");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"hi", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 1);
+ }
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("er");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"er", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ cache_ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(2u, refs.count());
+ }
+
+ // remove snap
+ ioctx.selfmanaged_snap_remove(my_snaps[2]);
+
+ /*
+ * snap[1]: [bb] [hi]
+ * snap[0]: [er] [hi]
+ * head: [bb] [hi]
+ */
+
+ sleep(10);
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("hi");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"hi", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 1);
+ }
+
+ // remove snap
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+
+ /*
+ * snap[1]: [bb] [hi]
+ * head: [bb] [hi]
+ */
+
+ sleep(10);
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("bb");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"bb", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 1);
+ }
+
+ // remove snap
+ ioctx.selfmanaged_snap_remove(my_snaps[1]);
+
+ /*
+ * snap[1]: [bb] [hi]
+ */
+
+ sleep(10);
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("bb");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"bb", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 1);
+ }
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("hi");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"hi", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 1);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestSnapRefcount2) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("Thabe cdHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+
+ string ab_fp_oid, cd_fp_oid, ef_fp_oid, BB_fp_oid;
+
+ // get fp_oid
+ ab_fp_oid = get_fp_oid("ab", "sha1");
+ cd_fp_oid = get_fp_oid("cd", "sha1");
+ ef_fp_oid = get_fp_oid("ef", "sha1");
+ BB_fp_oid = get_fp_oid("BB", "sha1");
+
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("ab");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(ab_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("cd");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(cd_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("ef");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(ef_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("BB");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(BB_fp_oid, &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, ab_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, cd_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, ef_fp_oid, "foo");
+
+
+ // make all chunks dirty --> flush
+ // foo: [ab] [cd] [ef]
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // foo: [BB] [BB] [ef]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("ThBBe BB");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // make clean
+ {
+ bufferlist bl;
+ bl.append("ThBBe BB");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, BB_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, BB_fp_oid, "foo");
+
+ // and another
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // foo: [ab] [cd] [ef]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("Thabe cd");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // make clean
+ {
+ bufferlist bl;
+ bl.append("Thabe cd");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, ab_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, cd_fp_oid, "foo");
+
+ /*
+ * snap[1]: [ab] [cd] [ef]
+ * snap[0]: [BB] [BB] [ef]
+ * head: [ab] [cd] [ef]
+ */
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("ab");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"ab", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ cache_ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(2u, refs.count());
+ }
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("cd");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"cd", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ cache_ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(2u, refs.count());
+ }
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("BB");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"BB", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ cache_ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(2u, refs.count());
+ }
+
+ // remove snap
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+
+ /*
+ * snap[1]: [ab] [cd] [ef]
+ * head: [ab] [cd] [ef]
+ */
+
+ sleep(10);
+
+ // check chunk's refcount
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("BB");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"BB", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 0);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestTestSnapCreate) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ GTEST_SKIP() << "cluster is not yet octopus, skipping test";
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("base chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("CHUNKS CHUNKS");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+
+ string ba_fp_oid, se_fp_oid, ch_fp_oid;
+
+ // get fp_oid
+ ba_fp_oid = get_fp_oid("ba", "sha1");
+ se_fp_oid = get_fp_oid("se", "sha1");
+ ch_fp_oid = get_fp_oid("ch", "sha1");
+
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("ba");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(ba_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("se");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(se_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("ch");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(ch_fp_oid, &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 2, ba_fp_oid, "foo");
+
+ // try to create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, se_fp_oid, "foo");
+
+ // check whether clone is created
+ ioctx.snap_set_read(librados::SNAP_DIR);
+ {
+ snap_set_t snap_set;
+ int snap_ret;
+ ObjectReadOperation op;
+ op.list_snaps(&snap_set, &snap_ret);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ 0, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, snap_ret);
+ ASSERT_LT(0u, snap_set.clones.size());
+ ASSERT_EQ(1, snap_set.clones.size());
+ }
+
+ // create a clone
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ bl.append("B");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 0));
+ }
+
+ ioctx.snap_set_read(my_snaps[0]);
+ // set-chunk to clone
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, ch_fp_oid, "foo");
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestRedirectAfterPromote) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ GTEST_SKIP() << "cluster is not yet octopus, skipping test";
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("base chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("BASE CHUNK");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+
+ // set-redirect
+ {
+ ObjectWriteOperation op;
+ op.set_redirect("bar", cache_ioctx, 0);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // promote
+ {
+ ObjectWriteOperation op;
+ op.tier_promote();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // write
+ {
+ bufferlist bl;
+ bl.append("a");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 0));
+ }
+
+ // read and verify the object (redirect)
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('a', bl[0]);
+ }
+
+ // read and verify the object (redirect)
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('B', bl[0]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestCheckRefcountWhenModification) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ GTEST_SKIP() << "cluster is not yet octopus, skipping test";
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ string er_fp_oid, hi_fp_oid, HI_fp_oid, ai_fp_oid, bi_fp_oid,
+ Er_fp_oid, Hi_fp_oid, Si_fp_oid;
+
+ // get fp_oid
+ er_fp_oid = get_fp_oid("er", "sha1");
+ hi_fp_oid = get_fp_oid("hi", "sha1");
+ HI_fp_oid = get_fp_oid("HI", "sha1");
+ ai_fp_oid = get_fp_oid("ai", "sha1");
+ bi_fp_oid = get_fp_oid("bi", "sha1");
+ Er_fp_oid = get_fp_oid("Er", "sha1");
+ Hi_fp_oid = get_fp_oid("Hi", "sha1");
+ Si_fp_oid = get_fp_oid("Si", "sha1");
+
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("er");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(er_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("hi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(hi_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("HI");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(HI_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("ai");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(ai_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("bi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(bi_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("Er");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(Er_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("Hi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(Hi_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("Si");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(Si_fp_oid, &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, er_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, hi_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, HI_fp_oid, "foo");
+
+ // foo head: [er] [hi] [HI]
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+
+ // foo snap[0]: [er] [hi] [HI]
+ // foo head : [er] [ai] [HI]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("a");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+ // write
+ {
+ bufferlist bl;
+ bl.append("a");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, ai_fp_oid, "foo");
+
+ // foo snap[0]: [er] [hi] [HI]
+ // foo head : [er] [bi] [HI]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("b");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+ // write
+ {
+ bufferlist bl;
+ bl.append("b");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, bi_fp_oid, "foo");
+
+ sleep(10);
+
+ // check chunk's refcount
+ // [ai]'s refcount should be 0
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("ai");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"ai", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 0);
+ }
+
+ // foo snap[0]: [er] [hi] [HI]
+ // foo head : [Er] [Hi] [Si]
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("thEre HiSi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ // write
+ {
+ bufferlist bl;
+ bl.append("thEre HiSi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, Er_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, Hi_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, Si_fp_oid, "foo");
+
+ // foo snap[0]: [er] [hi] [HI]
+ // foo head : [ER] [HI] [SI]
+ // write
+ {
+ bufferlist bl;
+ bl.append("thERe HISI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ sleep(10);
+
+ // check chunk's refcount
+ // [Er]'s refcount should be 0
+ {
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = strlen("Er");
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ sha1_gen.Update((const unsigned char *)"Er", size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, p_str, 0);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestSnapIncCount) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk1", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk2", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk3", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk4", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk1", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk4", "foo");
+ // foo snap[1]:
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk4]
+
+ ioctx.snap_set_read(my_snaps[1]);
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, "chunk2", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk4", "foo");
+ // foo snap[1]: [chunk2] [chunk4]
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk4]
+
+ ioctx.snap_set_read(my_snaps[0]);
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, "chunk2", "foo");
+ // foo snap[1]: [chunk2] [chunk4]
+ // foo snap[0]: [chunk2]
+ // foo head : [chunk1] [chunk4]
+
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk3", "foo");
+ // foo snap[1]: [chunk2] [chunk4]
+ // foo snap[0]: [chunk3] [chunk2]
+ // foo head : [chunk1] [chunk4]
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk4", "foo");
+ // foo snap[1]: [chunk2] [chunk4]
+ // foo snap[0]: [chunk3] [chunk2] [chunk4]
+ // foo head : [chunk1] [chunk4]
+
+ // check chunk's refcount
+ check_fp_oid_refcount(cache_ioctx, "chunk1", 1u, "");
+
+ // check chunk's refcount
+ check_fp_oid_refcount(cache_ioctx, "chunk2", 1u, "");
+
+ // check chunk's refcount
+ check_fp_oid_refcount(cache_ioctx, "chunk3", 1u, "");
+ sleep(10);
+
+ // check chunk's refcount
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk4", 1);
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestEvict) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk1", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk2", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk3", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk4", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk1", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk4", "foo");
+ // foo snap[1]:
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk4]
+
+ ioctx.snap_set_read(my_snaps[1]);
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 10, "chunk2", "foo");
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk4]
+
+ ioctx.snap_set_read(my_snaps[0]);
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, "chunk2", "foo");
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]: [chunk2]
+ // foo head : [chunk1] [chunk4]
+
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk3", "foo");
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]: [chunk3] [chunk2]
+ // foo head : [chunk1] [chunk4]
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk4", "foo");
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]: [chunk3] [chunk2] [chunk4]
+ // foo head : [chunk1] [chunk4]
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 2, "chunk4", "foo");
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]: [chunk4] [chunk3] [chunk2] [chunk4]
+ // foo head : [chunk1] [chunk4]
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 4, 2, "chunk1", "foo");
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]: [chunk4] [chunk3] [chunk1] [chunk2] [chunk4]
+ // foo head : [chunk1] [chunk4]
+
+ {
+ ObjectReadOperation op, stat_op;
+ uint64_t size;
+ op.tier_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+
+ stat_op.stat(&size, NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &stat_op, NULL));
+ ASSERT_EQ(10, size);
+ }
+
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op, stat_op;
+ uint64_t size;
+ op.tier_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+
+ stat_op.stat(&size, NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &stat_op, NULL));
+ ASSERT_EQ(strlen("there hiHI"), size);
+ }
+
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestEvictPromote) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("EREHT hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk1", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk2", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("THERE HIHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk3", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 2, "chunk1", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk2", "foo");
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk2]
+
+ ioctx.snap_set_read(my_snaps[0]);
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 10, "chunk3", "foo");
+ // foo snap[0]: [ chunk3 ]
+ // foo head : [chunk1] [chunk2]
+
+
+ {
+ ObjectReadOperation op, stat_op;
+ uint64_t size;
+ op.tier_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+
+ stat_op.stat(&size, NULL, NULL);
+ ASSERT_EQ(0, ioctx.operate("foo", &stat_op, NULL));
+ ASSERT_EQ(10, size);
+
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('T', bl[0]);
+ }
+
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(10, ioctx.read("foo", bl, 10, 0));
+ ASSERT_EQ('H', bl[8]);
+ }
+}
+
+
+TEST_F(LibRadosTwoPoolsPP, ManifestSnapSizeMismatch) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("chunk1", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there HIHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("chunk2", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("There hiHI");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("tHere hiHI");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // set-chunk
+ manifest_set_chunk(cluster, ioctx, cache_ioctx, 0, 10, "chunk1", "foo");
+
+ cache_ioctx.snap_set_read(my_snaps[1]);
+
+ // set-chunk
+ manifest_set_chunk(cluster, ioctx, cache_ioctx, 0, 10, "chunk2", "foo");
+
+ // evict
+ {
+ ObjectReadOperation op, stat_op;
+ op.tier_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ }
+
+ uint32_t hash;
+ ASSERT_EQ(0, cache_ioctx.get_object_pg_hash_position2("foo", &hash));
+
+ // scrub
+ {
+ for (int tries = 0; tries < 5; ++tries) {
+ bufferlist inbl;
+ ostringstream ss;
+ ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
+ << cache_ioctx.get_id() << "."
+ << std::hex << hash
+ << "\"}";
+ int r = cluster.mon_command(ss.str(), inbl, NULL, NULL);
+ if (r == -ENOENT ||
+ r == -EAGAIN) {
+ sleep(5);
+ continue;
+ }
+ ASSERT_EQ(0, r);
+ break;
+ }
+ cout << "waiting for scrubs..." << std::endl;
+ sleep(20);
+ cout << "done waiting" << std::endl;
+ }
+
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('t', bl[0]);
+ }
+}
+
+#include <common/CDC.h>
+TEST_F(LibRadosTwoPoolsPP, DedupFlushRead) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ GTEST_SKIP() << "cluster is not yet octopus, skipping test";
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_tier", pool_name),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_chunk_algorithm", "fastcdc"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 1024),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ bufferlist gbl;
+ {
+ generate_buffer(1024*8, &gbl);
+ ObjectWriteOperation op;
+ op.write_full(gbl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo-chunk", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("DDse chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar-chunk", &op));
+ }
+
+ // set-chunk to set manifest object
+ {
+ ObjectReadOperation op;
+ op.set_chunk(0, 2, ioctx, "bar-chunk", 0,
+ CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ std::unique_ptr<CDC> cdc = CDC::create("fastcdc", cbits(1024)-1);
+ vector<pair<uint64_t, uint64_t>> chunks;
+ bufferlist chunk;
+ cdc->calc_chunks(gbl, &chunks);
+ chunk.substr_of(gbl, chunks[1].first, chunks[1].second);
+ string tgt_oid;
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ SHA1 sha1_gen;
+ int size = chunk.length();
+ sha1_gen.Update((const unsigned char *)chunk.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ tgt_oid = string(p_str);
+ }
+
+ // read and verify the chunked object
+ {
+ bufferlist test_bl;
+ ASSERT_EQ(2, ioctx.read(tgt_oid, test_bl, 2, 0));
+ ASSERT_EQ(test_bl[1], chunk[1]);
+ }
+
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 512),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // make a dirty chunks
+ {
+ bufferlist bl;
+ bl.append("hi");
+ ASSERT_EQ(0, cache_ioctx.write("foo-chunk", bl, bl.length(), 0));
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ cdc = CDC::create("fastcdc", cbits(512)-1);
+ chunks.clear();
+ cdc->calc_chunks(gbl, &chunks);
+ bufferlist chunk_512;
+ chunk_512.substr_of(gbl, chunks[3].first, chunks[3].second);
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ SHA1 sha1_gen;
+ int size = chunk_512.length();
+ sha1_gen.Update((const unsigned char *)chunk_512.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ tgt_oid = string(p_str);
+ }
+
+ // read and verify the chunked object
+ {
+ bufferlist test_bl;
+ ASSERT_EQ(2, ioctx.read(tgt_oid, test_bl, 2, 0));
+ ASSERT_EQ(test_bl[1], chunk_512[1]);
+ }
+
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 16384),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // make a dirty chunks
+ {
+ bufferlist bl;
+ bl.append("hi");
+ ASSERT_EQ(0, cache_ioctx.write("foo-chunk", bl, bl.length(), 0));
+ gbl.begin(0).copy_in(bl.length(), bl);
+ }
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ cdc = CDC::create("fastcdc", cbits(16384)-1);
+ chunks.clear();
+ cdc->calc_chunks(gbl, &chunks);
+ bufferlist chunk_16384;
+ chunk_16384.substr_of(gbl, chunks[0].first, chunks[0].second);
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ SHA1 sha1_gen;
+ int size = chunk_16384.length();
+ sha1_gen.Update((const unsigned char *)chunk_16384.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ tgt_oid = string(p_str);
+ }
+ // read and verify the chunked object
+ {
+ bufferlist test_bl;
+ ASSERT_EQ(2, ioctx.read(tgt_oid, test_bl, 2, 0));
+ ASSERT_EQ(test_bl[0], chunk_16384[0]);
+ }
+
+ // less than object size
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 1024),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // make a dirty chunks
+ // a chunk_info is deleted by write, which converts the manifest object to non-manifest object
+ {
+ bufferlist bl;
+ bl.append("hi");
+ ASSERT_EQ(0, cache_ioctx.write("foo-chunk", bl, bl.length(), 0));
+ }
+
+ // reset set-chunk
+ {
+ bufferlist bl;
+ bl.append("DDse chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar-chunk", &op));
+ }
+ // set-chunk to set manifest object
+ {
+ ObjectReadOperation op;
+ op.set_chunk(0, 2, ioctx, "bar-chunk", 0,
+ CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo-chunk", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ cdc = CDC::create("fastcdc", cbits(1024)-1);
+ chunks.clear();
+ cdc->calc_chunks(gbl, &chunks);
+ bufferlist small_chunk;
+ small_chunk.substr_of(gbl, chunks[1].first, chunks[1].second);
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ SHA1 sha1_gen;
+ int size = small_chunk.length();
+ sha1_gen.Update((const unsigned char *)small_chunk.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ tgt_oid = string(p_str);
+ }
+ // read and verify the chunked object
+ {
+ bufferlist test_bl;
+ ASSERT_EQ(2, ioctx.read(tgt_oid, test_bl, 2, 0));
+ ASSERT_EQ(test_bl[0], small_chunk[0]);
+ }
+
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestFlushSnap) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_tier", pool_name),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_chunk_algorithm", "fastcdc"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 1024),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ bufferlist gbl;
+ {
+ //bufferlist bl;
+ //bl.append("there hi");
+ generate_buffer(1024*8, &gbl);
+ ObjectWriteOperation op;
+ op.write_full(gbl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, ioctx, cache_ioctx, 2, 2, "bar", "foo");
+ manifest_set_chunk(cluster, ioctx, cache_ioctx, 6, 2, "bar", "foo");
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // make a dirty chunks
+ {
+ bufferlist bl;
+ bl.append("Thbbe");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // and another
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // make a dirty chunks
+ {
+ bufferlist bl;
+ bl.append("Thcce");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // flush on head (should fail)
+ cache_ioctx.snap_set_read(librados::SNAP_HEAD);
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush on recent snap (should fail)
+ cache_ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush on oldest snap
+ cache_ioctx.snap_set_read(my_snaps[1]);
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush on oldest snap
+ cache_ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush on oldest snap
+ cache_ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // check chunk's refcount
+ std::unique_ptr<CDC> cdc = CDC::create("fastcdc", cbits(1024)-1);
+ vector<pair<uint64_t, uint64_t>> chunks;
+ bufferlist chunk;
+ cdc->calc_chunks(gbl, &chunks);
+ chunk.substr_of(gbl, chunks[1].first, chunks[1].second);
+ string tgt_oid;
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ SHA1 sha1_gen;
+ int size = chunk.length();
+ sha1_gen.Update((const unsigned char *)chunk.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ tgt_oid = string(p_str);
+ }
+ // read and verify the chunked object
+ {
+ bufferlist test_bl;
+ ASSERT_EQ(2, ioctx.read(tgt_oid, test_bl, 2, 0));
+ ASSERT_EQ(test_bl[1], chunk[1]);
+ }
+
+ cache_ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(4, cache_ioctx.read("foo", bl, 4, 0));
+ ASSERT_EQ('c', bl[2]);
+ }
+
+ cache_ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(4, cache_ioctx.read("foo", bl, 4, 0));
+ ASSERT_EQ('b', bl[2]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestFlushDupCount) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_tier", pool_name),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_chunk_algorithm", "fastcdc"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 1024),
+ inbl, NULL, NULL));
+
+ // create object
+ bufferlist gbl;
+ {
+ //bufferlist bl;
+ generate_buffer(1024*8, &gbl);
+ ObjectWriteOperation op;
+ op.write_full(gbl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // set-chunk to set manifest object
+ {
+ ObjectReadOperation op;
+ op.set_chunk(0, 2, ioctx, "bar", 0,
+ CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // make a dirty chunks
+ {
+ bufferlist bl;
+ bl.append("Thbbe hi");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // and another
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, cache_ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // make a dirty chunks
+ {
+ bufferlist bl;
+ bl.append("Thcce hi");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ //flush on oldest snap
+ cache_ioctx.snap_set_read(my_snaps[1]);
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush on oldest snap
+ cache_ioctx.snap_set_read(my_snaps[0]);
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ cache_ioctx.snap_set_read(librados::SNAP_HEAD);
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ std::unique_ptr<CDC> cdc = CDC::create("fastcdc", cbits(1024)-1);
+ vector<pair<uint64_t, uint64_t>> chunks;
+ bufferlist chunk;
+ cdc->calc_chunks(gbl, &chunks);
+ chunk.substr_of(gbl, chunks[1].first, chunks[1].second);
+ string tgt_oid;
+ // check chunk's refcount
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = chunk.length();
+ sha1_gen.Update((const unsigned char *)chunk.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ tgt_oid = string(p_str);
+ ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(1u, refs.count());
+ }
+
+ bufferlist chunk2;
+ chunk2.substr_of(gbl, chunks[0].first, chunks[0].second);
+ // check chunk's refcount
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = chunk2.length();
+ sha1_gen.Update((const unsigned char *)chunk2.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ tgt_oid = string(p_str);
+ ioctx.getxattr(p_str, CHUNK_REFCOUNT_ATTR, t);
+ chunk_refs_t refs;
+ try {
+ auto iter = t.cbegin();
+ decode(refs, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(0);
+ }
+ ASSERT_LE(1u, refs.count());
+ }
+
+ // make a dirty chunks
+ {
+ bufferlist bl;
+ bl.append("ThDDe hi");
+ ASSERT_EQ(0, cache_ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ bufferlist tmp;
+ tmp.append("Thcce hi");
+ gbl.begin(0).copy_in(tmp.length(), tmp);
+ bufferlist chunk3;
+ cdc->calc_chunks(gbl, &chunks);
+ chunk3.substr_of(gbl, chunks[0].first, chunks[0].second);
+ // check chunk's refcount
+ {
+ unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1] = {0};
+ char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+ bufferlist t;
+ SHA1 sha1_gen;
+ int size = chunk2.length();
+ sha1_gen.Update((const unsigned char *)chunk2.c_str(), size);
+ sha1_gen.Final(fingerprint);
+ buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+ is_intended_refcount_state(cache_ioctx, "foo", ioctx, p_str, 0);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, TierFlushDuringFlush) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ bufferlist inbl;
+
+ // create a new pool
+ std::string temp_pool_name = get_temp_pool_name() + "-test-flush";
+ ASSERT_EQ(0, cluster.pool_create(temp_pool_name.c_str()));
+
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_tier", temp_pool_name),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_chunk_algorithm", "fastcdc"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 1024),
+ inbl, NULL, NULL));
+
+ // create object
+ bufferlist gbl;
+ {
+ //bufferlist bl;
+ generate_buffer(1024*8, &gbl);
+ ObjectWriteOperation op;
+ op.write_full(gbl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // set-chunk to set manifest object
+ {
+ ObjectReadOperation op;
+ op.set_chunk(0, 2, ioctx, "bar", 0,
+ CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // delete temp pool, so flushing chunk will fail
+ ASSERT_EQ(0, s_cluster.pool_delete(temp_pool_name.c_str()));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // flush to check if proper error is returned
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-ENOENT, completion->get_return_value());
+ completion->release();
+ }
+
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestSnapHasChunk) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("there HIHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ string er_fp_oid, hi_fp_oid, HI_fp_oid, ai_fp_oid, bi_fp_oid,
+ Er_fp_oid, Hi_fp_oid, SI_fp_oid;
+
+ // get fp_oid
+ er_fp_oid = get_fp_oid("er", "sha1");
+ hi_fp_oid = get_fp_oid("hi", "sha1");
+ HI_fp_oid = get_fp_oid("HI", "sha1");
+ ai_fp_oid = get_fp_oid("ai", "sha1");
+ bi_fp_oid = get_fp_oid("bi", "sha1");
+ Er_fp_oid = get_fp_oid("Er", "sha1");
+ Hi_fp_oid = get_fp_oid("Hi", "sha1");
+ SI_fp_oid = get_fp_oid("SI", "sha1");
+
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("er");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(er_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("hi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(hi_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("HI");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(HI_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("ai");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(ai_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("bi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(bi_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("Er");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(Er_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("Hi");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(Hi_fp_oid, &op));
+ }
+ // write
+ {
+ ObjectWriteOperation op;
+ bufferlist bl;
+ bl.append("SI");
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate(SI_fp_oid, &op));
+ }
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, HI_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, HI_fp_oid, "foo");
+
+ // foo head: [hi] [HI]
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("a");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+ // write
+ {
+ bufferlist bl;
+ bl.append("a");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+ // write
+ {
+ bufferlist bl;
+ bl.append("S");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 8));
+ }
+
+ // foo snap[0]: [hi] [HI]
+ // foo head : [er] [ai] [SI]
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, er_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, ai_fp_oid, "foo");
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, SI_fp_oid, "foo");
+
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ // create a clone
+ {
+ bufferlist bl;
+ bl.append("b");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+ // write
+ {
+ bufferlist bl;
+ bl.append("b");
+ ASSERT_EQ(0, ioctx.write("foo", bl, 1, 6));
+ }
+
+ // foo snap[1]: [HI] [HI]
+ // foo snap[0]: [er] [ai] [SI]
+ // foo head : [er] [bi] [SI]
+
+ // set-chunk (dedup)
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, bi_fp_oid, "foo");
+
+ {
+ ASSERT_EQ(1, cls_cas_references_chunk(ioctx, "foo", SI_fp_oid));
+ ASSERT_EQ(1, cls_cas_references_chunk(ioctx, "foo", er_fp_oid));
+ ASSERT_EQ(1, cls_cas_references_chunk(ioctx, "foo", ai_fp_oid));
+ ASSERT_EQ(2, cls_cas_references_chunk(ioctx, "foo", HI_fp_oid));
+ ASSERT_EQ(-ENOLINK, cls_cas_references_chunk(ioctx, "foo", Hi_fp_oid));
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestRollback) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet pacific
+ if (_get_required_osd_release(cluster) < "pacific") {
+ cout << "cluster is not yet pacific, skipping test" << std::endl;
+ return;
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("CDere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ABere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk1", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("CDere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk2", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("EFere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk3", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("thABe hiEF");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk1", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk3", "foo");
+ // foo snap[1]:
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk3]
+
+ ioctx.snap_set_read(my_snaps[1]);
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 10, "chunk2", "foo");
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk3]
+
+ // foo snap[1]: [ chunk2 ]
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk3]
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_rollback("foo", my_snaps[0]));
+
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('t', bl[0]);
+ }
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]));
+
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('C', bl[0]);
+ }
+
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestRollbackRefcount) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet pacific
+ if (_get_required_osd_release(cluster) < "pacific") {
+ cout << "cluster is not yet pacific, skipping test" << std::endl;
+ return;
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("CDere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ABere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk1", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("CDere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk2", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("EFere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk3", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("DDDDD hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk4", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("EEEEE hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk5", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("thABe hiEF");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk1", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk3", "foo");
+ // foo snap[1]:
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk3]
+
+ ioctx.snap_set_read(my_snaps[1]);
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk4", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 6, 2, "chunk5", "foo");
+ // foo snap[1]: [chunk4] [chunk5]
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk3]
+
+ ioctx.snap_set_read(my_snaps[0]);
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 10, "chunk2", "foo");
+ // foo snap[1]: [chunk4] [chunk5]
+ // foo snap[0]: [ chunk2 ]
+ // foo head : [chunk1] [chunk3]
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]));
+ // foo snap[1]: [chunk4] [chunk5]
+ // foo snap[0]: [ chunk2 ]
+ // foo head : [chunk4] [chunk5] <-- will contain these contents
+
+ sleep(10);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk1", 0);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk3", 0);
+
+ ioctx.selfmanaged_snap_remove(my_snaps[1]);
+ sleep(10);
+ // foo snap[1]:
+ // foo snap[0]: [ chunk2 ]
+ // foo head : [chunk4] [chunk5]
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk4", 1);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk5", 1);
+
+ {
+ bufferlist bl;
+ bl.append("thABe hiEF");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+ // foo snap[1]:
+ // foo snap[0]: [ chunk2 ]
+ // foo head :
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk1", 0);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk3", 0);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk4", 0);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk5", 0);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk2", 1);
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestEvictRollback) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet pacific
+ if (_get_required_osd_release(cluster) < "pacific") {
+ cout << "cluster is not yet pacific, skipping test" << std::endl;
+ return;
+ }
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("CDere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ABere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk1", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("CDere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk2", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("EFere hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("chunk3", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+ }
+
+
+ // set-chunk
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 2, 2, "chunk1", "foo");
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 8, 2, "chunk3", "foo");
+ // foo snap[0]:
+ // foo head : [chunk1] [chunk3]
+
+ ioctx.snap_set_read(my_snaps[0]);
+ manifest_set_chunk(cluster, cache_ioctx, ioctx, 0, 10, "chunk2", "foo");
+ // foo snap[0]: [ chunk2 ]
+ // foo head : [chunk1] [chunk3]
+
+ sleep(10);
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk1", 1);
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk3", 1);
+
+
+ ioctx.snap_set_read(my_snaps[0]);
+ // evict--this makes the chunk missing state
+ {
+ ObjectReadOperation op, stat_op;
+ op.tier_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ }
+
+ // rollback to my_snaps[0]
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_rollback("foo", my_snaps[0]));
+
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('C', bl[0]);
+ }
+
+ is_intended_refcount_state(ioctx, "foo", cache_ioctx, "chunk2", 1);
+}
+
+class LibRadosTwoPoolsECPP : public RadosTestECPP
+{
+public:
+ LibRadosTwoPoolsECPP() {};
+ ~LibRadosTwoPoolsECPP() override {};
+protected:
+ static void SetUpTestCase() {
+ SKIP_IF_CRIMSON();
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+ }
+ static void TearDownTestCase() {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+ }
+ static std::string cache_pool_name;
+
+ void SetUp() override {
+ SKIP_IF_CRIMSON();
+ cache_pool_name = get_temp_pool_name();
+ ASSERT_EQ(0, s_cluster.pool_create(cache_pool_name.c_str()));
+ RadosTestECPP::SetUp();
+
+ ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+ cache_ioctx.application_enable("rados", true);
+ cache_ioctx.set_namespace(nspace);
+ }
+ void TearDown() override {
+ SKIP_IF_CRIMSON();
+ // flush + evict cache
+ flush_evict_all(cluster, cache_ioctx);
+
+ bufferlist inbl;
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+
+ RadosTestECPP::TearDown();
+
+ cleanup_default_namespace(cache_ioctx);
+ cleanup_namespace(cache_ioctx, nspace);
+
+ cache_ioctx.close();
+ ASSERT_EQ(0, s_cluster.pool_delete(cache_pool_name.c_str()));
+ }
+
+ librados::IoCtx cache_ioctx;
+};
+
+std::string LibRadosTwoPoolsECPP::cache_pool_name;
+
+TEST_F(LibRadosTierECPP, Dirty) {
+ SKIP_IF_CRIMSON();
+ {
+ ObjectWriteOperation op;
+ op.undirty();
+ ASSERT_EQ(0, ioctx.operate("foo", &op)); // still get 0 if it dne
+ }
+ {
+ ObjectWriteOperation op;
+ op.create(true);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_TRUE(dirty);
+ ASSERT_EQ(0, r);
+ }
+ {
+ ObjectWriteOperation op;
+ op.undirty();
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.undirty();
+ ASSERT_EQ(0, ioctx.operate("foo", &op)); // still 0 if already clean
+ }
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+ //{
+ // ObjectWriteOperation op;
+ // op.truncate(0); // still a write even tho it is a no-op
+ // ASSERT_EQ(0, ioctx.operate("foo", &op));
+ //}
+ //{
+ // bool dirty = false;
+ // int r = -1;
+ // ObjectReadOperation op;
+ // op.is_dirty(&dirty, &r);
+ // ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+ // ASSERT_TRUE(dirty);
+ // ASSERT_EQ(0, r);
+ //}
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Overlay) {
+ SKIP_IF_CRIMSON();
+ // create objects
+ {
+ bufferlist bl;
+ bl.append("base");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("cache");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // by default, the overlay sends us to cache pool
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // unless we say otherwise
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(0, 1, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ ASSERT_EQ('b', bl[0]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Promote) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ }
+
+ // read, trigger a whiteout
+ {
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, PromoteSnap) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote on the head
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ ioctx.snap_set_read(my_snaps[0]);
+
+ // stop and scrub this pg (to make sure scrub can handle missing
+ // clones in the cache tier)
+ // This test requires cache tier and base tier to have the same pg_num/pgp_num
+ {
+ for (int tries = 0; tries < 5; ++tries) {
+ IoCtx cache_ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+ uint32_t hash;
+ ASSERT_EQ(0, ioctx.get_object_pg_hash_position2("foo", &hash));
+ ostringstream ss;
+ ss << "{\"prefix\": \"pg scrub\", \"pgid\": \""
+ << cache_ioctx.get_id() << "."
+ << hash
+ << "\"}";
+ int r = cluster.mon_command(ss.str(), inbl, NULL, NULL);
+ if (r == -EAGAIN ||
+ r == -ENOENT) { // in case mgr osdmap is a bit stale
+ sleep(5);
+ continue;
+ }
+ ASSERT_EQ(0, r);
+ break;
+ }
+ // give it a few seconds to go. this is sloppy but is usually enough time
+ cout << "waiting for scrub..." << std::endl;
+ sleep(15);
+ cout << "done waiting" << std::endl;
+ }
+
+ // read foo snap
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // read bar snap
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // read baz snap
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+
+ // read foo
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // read bar
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // read baz
+ {
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, ioctx.read("baz", bl, 1, 0));
+ }
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsECPP, PromoteSnapTrimRace) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // delete the snap
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps[0]));
+
+ ioctx.snap_set_read(my_snaps[0]);
+
+ // read foo snap. the OSD may or may not realize that this snap has
+ // been logically deleted; either response is valid.
+ {
+ bufferlist bl;
+ int r = ioctx.read("foo", bl, 1, 0);
+ ASSERT_TRUE(r == 1 || r == -ENOENT);
+ }
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Whiteout) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create some whiteouts, verify they behave
+ {
+ ObjectWriteOperation op;
+ op.assert_exists();
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ {
+ ObjectWriteOperation op;
+ op.assert_exists();
+ op.remove();
+ ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.assert_exists();
+ op.remove();
+ ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+ }
+
+ // verify the whiteouts are there in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // delete a whiteout and verify it goes away
+ ASSERT_EQ(-ENOENT, ioctx.remove("foo"));
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // recreate an object and verify we can read it
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Evict) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ }
+
+ // read, trigger a whiteout, and a dirty object
+ {
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(0, ioctx.write("bar", bl, bl.length(), 0));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // pin
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // evict the pinned object with -EPERM
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE,
+ NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EPERM, completion->get_return_value());
+ completion->release();
+ }
+
+ // unpin
+ {
+ ObjectWriteOperation op;
+ op.cache_unpin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify clean
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // evict
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE,
+ NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, EvictSnap) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("ciao!");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger a promote on the head
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+
+ // evict bam
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "bam", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "bam", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-ENOENT, completion->get_return_value());
+ completion->release();
+ }
+
+ // read foo snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // evict foo snap
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // snap is gone...
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-ENOENT, completion->get_return_value());
+ completion->release();
+ }
+ // head is still there...
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // promote head + snap of bar
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // evict bar head (fail)
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+
+ // evict bar snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // ...and then head
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ObjectReadOperation op;
+ op.read(1, 0, &bl, NULL);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "bar", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsECPP, TryFlush) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // verify the object is NOT present in the base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // verify dirty
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_TRUE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // pin
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush the pinned object with -EPERM
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EPERM, completion->get_return_value());
+ completion->release();
+ }
+
+ // unpin
+ {
+ ObjectWriteOperation op;
+ op.cache_unpin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify clean
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // verify in base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it != ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // evict it
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify no longer in cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, FailedFlush) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // verify the object is NOT present in the base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // set omap
+ {
+ ObjectWriteOperation op;
+ std::map<std::string, bufferlist> omap;
+ omap["somekey"] = bufferlist();
+ op.omap_set(omap);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_NE(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // get omap
+ {
+ ObjectReadOperation op;
+ bufferlist bl;
+ int prval = 0;
+ std::set<std::string> keys;
+ keys.insert("somekey");
+ std::map<std::string, bufferlist> map;
+
+ op.omap_get_vals_by_keys(keys, &map, &prval);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op, &bl));
+ sleep(5);
+ bool completed = completion->is_complete();
+ if( !completed ) {
+ cache_ioctx.aio_cancel(completion);
+ std::cerr << "Most probably test case will hang here, please reset manually" << std::endl;
+ ASSERT_TRUE(completed); //in fact we are locked forever at test case shutdown unless fix for http://tracker.ceph.com/issues/14511 is applied. Seems there is no workaround for that
+ }
+ completion->release();
+ }
+ // verify still not in base tier
+ {
+ ASSERT_TRUE(ioctx.nobjects_begin() == ioctx.nobjects_end());
+ }
+ // erase it
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ // flush whiteout
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // evict
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify no longer in cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+ // or base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Flush) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ uint64_t user_version = 0;
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // verify the object is NOT present in the base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // verify dirty
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_TRUE(dirty);
+ ASSERT_EQ(0, r);
+ user_version = cache_ioctx.get_last_version();
+ }
+
+ // pin
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush the pinned object with -EPERM
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EPERM, completion->get_return_value());
+ completion->release();
+ }
+
+ // unpin
+ {
+ ObjectWriteOperation op;
+ op.cache_unpin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify clean
+ {
+ bool dirty = false;
+ int r = -1;
+ ObjectReadOperation op;
+ op.is_dirty(&dirty, &r);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+ ASSERT_FALSE(dirty);
+ ASSERT_EQ(0, r);
+ }
+
+ // verify in base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it != ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // evict it
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify no longer in cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // read it again and verify the version is consistent
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ(user_version, cache_ioctx.get_last_version());
+ }
+
+ // erase it
+ {
+ ObjectWriteOperation op;
+ op.remove();
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush whiteout
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // evict
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify no longer in cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+ // or base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, FlushSnap) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("a");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // create a snapshot, clone
+ vector<uint64_t> my_snaps(1);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("b");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // and another
+ my_snaps.resize(2);
+ my_snaps[1] = my_snaps[0];
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+ my_snaps));
+ {
+ bufferlist bl;
+ bl.append("c");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // verify the object is present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // verify the object is NOT present in the base tier
+ {
+ NObjectIterator it = ioctx.nobjects_begin();
+ ASSERT_TRUE(it == ioctx.nobjects_end());
+ }
+
+ // flush on head (should fail)
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+ // flush on recent snap (should fail)
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EBUSY, completion->get_return_value());
+ completion->release();
+ }
+ // flush on oldest snap
+ ioctx.snap_set_read(my_snaps[1]);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // flush on next oldest snap
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // flush on head
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // verify i can read the snaps from the cache pool
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('b', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[1]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('a', bl[0]);
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // verify i can read the snaps from the base pool
+ ioctx.snap_set_read(librados::SNAP_HEAD);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('c', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[0]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('b', bl[0]);
+ }
+ ioctx.snap_set_read(my_snaps[1]);
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('a', bl[0]);
+ }
+
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ cluster.wait_for_latest_osdmap();
+
+ // cleanup
+ ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTierECPP, FlushWriteRaces) {
+ SKIP_IF_CRIMSON();
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ std::string cache_pool_name = pool_name + "-cache";
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
+ IoCtx cache_ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+ cache_ioctx.application_enable("rados", true);
+ IoCtx ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create/dirty object
+ bufferlist bl;
+ bl.append("hi there");
+ {
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush + write
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ ObjectWriteOperation op2;
+ op2.write_full(bl);
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate(
+ "foo", completion2, &op2, 0));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+
+ int tries = 1000;
+ do {
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // try-flush + write
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ ObjectWriteOperation op2;
+ op2.write_full(bl);
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion2, &op2, 0));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ int r = completion->get_return_value();
+ ASSERT_TRUE(r == -EBUSY || r == 0);
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ if (r == -EBUSY)
+ break;
+ cout << "didn't get EBUSY, trying again" << std::endl;
+ }
+ ASSERT_TRUE(--tries);
+ } while (true);
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+
+ ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST_F(LibRadosTwoPoolsECPP, FlushTryFlushRaces) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush + flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush + try-flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_try_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+
+ // create/dirty object
+ int tries = 1000;
+ do {
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // try-flush + flush
+ // (flush will not piggyback on try-flush)
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ int r = completion->get_return_value();
+ ASSERT_TRUE(r == -EBUSY || r == 0);
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ if (r == -EBUSY)
+ break;
+ cout << "didn't get EBUSY, trying again" << std::endl;
+ }
+ ASSERT_TRUE(--tries);
+ } while (true);
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // try-flush + try-flush
+ {
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ ObjectReadOperation op2;
+ op2.cache_try_flush();
+ librados::AioCompletion *completion2 = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion2, &op2,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ completion->wait_for_complete();
+ completion2->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ ASSERT_EQ(0, completion2->get_return_value());
+ completion->release();
+ completion2->release();
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, TryFlushReadRace) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create/dirty object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ bufferptr bp(4000000); // make it big!
+ bp.zero();
+ bl.append(bp);
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // start a continuous stream of reads
+ read_ioctx = &ioctx;
+ test_lock.lock();
+ for (int i = 0; i < max_reads; ++i) {
+ start_flush_read();
+ num_reads++;
+ }
+ test_lock.unlock();
+
+ // try-flush
+ ObjectReadOperation op;
+ op.cache_try_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY |
+ librados::OPERATION_SKIPRWLOCKS, NULL));
+
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+
+ // stop reads
+ std::unique_lock locker{test_lock};
+ max_reads = 0;
+ cond.wait(locker, [] { return num_reads == 0;});
+}
+
+TEST_F(LibRadosTierECPP, CallForcesPromote) {
+ SKIP_IF_CRIMSON();
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ std::string cache_pool_name = pool_name + "-cache";
+ ASSERT_EQ("", create_one_ec_pool_pp(pool_name, cluster));
+ ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
+ IoCtx cache_ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+ cache_ioctx.application_enable("rados", true);
+ IoCtx ioctx;
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // set things up such that the op would normally be proxied
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_type",
+ "explicit_object"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "min_read_recency_for_promote",
+ "4"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // create/dirty object
+ bufferlist bl;
+ bl.append("hi there");
+ {
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // flush
+ {
+ ObjectReadOperation op;
+ op.cache_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op,
+ librados::OPERATION_IGNORE_OVERLAY, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // evict
+ {
+ ObjectReadOperation op;
+ op.cache_evict();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE,
+ NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // call
+ {
+ ObjectReadOperation op;
+ bufferlist bl;
+ op.exec("rbd", "get_id", bl);
+ bufferlist out;
+ // should get EIO (not an rbd object), not -EOPNOTSUPP (we didn't promote)
+ ASSERT_EQ(-5, ioctx.operate("foo", &op, &out));
+ }
+
+ // make sure foo is back in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ ASSERT_TRUE(it->get_oid() == string("foo"));
+ ++it;
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+
+ ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
+ ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, cluster));
+}
+
+TEST_F(LibRadosTierECPP, HitSetNone) {
+ SKIP_IF_CRIMSON();
+ {
+ list< pair<time_t,time_t> > ls;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, ioctx.hit_set_list(123, c, &ls));
+ c->wait_for_complete();
+ ASSERT_EQ(0, c->get_return_value());
+ ASSERT_TRUE(ls.empty());
+ c->release();
+ }
+ {
+ bufferlist bl;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, ioctx.hit_set_get(123, c, 12345, &bl));
+ c->wait_for_complete();
+ ASSERT_EQ(-ENOENT, c->get_return_value());
+ c->release();
+ }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, HitSetRead) {
+ SKIP_IF_CRIMSON();
+ // make it a tier
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+
+ // enable hitset tracking for this pool
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
+ "explicit_object"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ cache_ioctx.set_namespace("");
+
+ // keep reading until we see our object appear in the HitSet
+ utime_t start = ceph_clock_now();
+ utime_t hard_stop = start + utime_t(600, 0);
+
+ while (true) {
+ utime_t now = ceph_clock_now();
+ ASSERT_TRUE(now < hard_stop);
+
+ string name = "foo";
+ uint32_t hash;
+ ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+ hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash,
+ cluster.pool_lookup(cache_pool_name.c_str()), "");
+
+ bufferlist bl;
+ ASSERT_EQ(-ENOENT, cache_ioctx.read("foo", bl, 1, 0));
+
+ bufferlist hbl;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.hit_set_get(hash, c, now.sec(), &hbl));
+ c->wait_for_complete();
+ c->release();
+
+ if (hbl.length()) {
+ auto p = hbl.cbegin();
+ HitSet hs;
+ decode(hs, p);
+ if (hs.contains(oid)) {
+ cout << "ok, hit_set contains " << oid << std::endl;
+ break;
+ }
+ cout << "hmm, not in HitSet yet" << std::endl;
+ } else {
+ cout << "hmm, no HitSet yet" << std::endl;
+ }
+
+ sleep(1);
+ }
+}
+
+// disable this test until hitset-get reliably works on EC pools
+#if 0
+TEST_F(LibRadosTierECPP, HitSetWrite) {
+ int num_pg = _get_pg_num(cluster, pool_name);
+ ceph_assert(num_pg > 0);
+
+ // enable hitset tracking for this pool
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_count", 8),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_type",
+ "explicit_hash"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ ioctx.set_namespace("");
+
+ // do a bunch of writes
+ for (int i=0; i<1000; ++i) {
+ bufferlist bl;
+ bl.append("a");
+ ASSERT_EQ(0, ioctx.write(stringify(i), bl, 1, 0));
+ }
+
+ // get HitSets
+ std::map<int,HitSet> hitsets;
+ for (int i=0; i<num_pg; ++i) {
+ list< pair<time_t,time_t> > ls;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, ioctx.hit_set_list(i, c, &ls));
+ c->wait_for_complete();
+ c->release();
+ std::cout << "pg " << i << " ls " << ls << std::endl;
+ ASSERT_FALSE(ls.empty());
+
+ // get the latest
+ c = librados::Rados::aio_create_completion();
+ bufferlist bl;
+ ASSERT_EQ(0, ioctx.hit_set_get(i, c, ls.back().first, &bl));
+ c->wait_for_complete();
+ c->release();
+
+ //std::cout << "bl len is " << bl.length() << "\n";
+ //bl.hexdump(std::cout);
+ //std::cout << std::endl;
+
+ auto p = bl.cbegin();
+ decode(hitsets[i], p);
+
+ // cope with racing splits by refreshing pg_num
+ if (i == num_pg - 1)
+ num_pg = _get_pg_num(cluster, pool_name);
+ }
+
+ for (int i=0; i<1000; ++i) {
+ string n = stringify(i);
+ uint32_t hash = ioctx.get_object_hash_position(n);
+ hobject_t oid(sobject_t(n, CEPH_NOSNAP), "", hash,
+ cluster.pool_lookup(pool_name.c_str()), "");
+ std::cout << "checking for " << oid << std::endl;
+ bool found = false;
+ for (int p=0; p<num_pg; ++p) {
+ if (hitsets[p].contains(oid)) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ }
+}
+#endif
+
+TEST_F(LibRadosTwoPoolsECPP, HitSetTrim) {
+ SKIP_IF_CRIMSON();
+ unsigned count = 3;
+ unsigned period = 3;
+
+ // make it a tier
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+
+ // enable hitset tracking for this pool
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", count),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", period),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_fpp", ".01"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ cache_ioctx.set_namespace("");
+
+ // do a bunch of writes and make sure the hitsets rotate
+ utime_t start = ceph_clock_now();
+ utime_t hard_stop = start + utime_t(count * period * 50, 0);
+
+ time_t first = 0;
+ int bsize = alignment;
+ char *buf = (char *)new char[bsize];
+ memset(buf, 'f', bsize);
+
+ while (true) {
+ string name = "foo";
+ uint32_t hash;
+ ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+ hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash, -1, "");
+
+ bufferlist bl;
+ bl.append(buf, bsize);
+ ASSERT_EQ(0, cache_ioctx.append("foo", bl, bsize));
+
+ list<pair<time_t, time_t> > ls;
+ AioCompletion *c = librados::Rados::aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.hit_set_list(hash, c, &ls));
+ c->wait_for_complete();
+ c->release();
+
+ cout << " got ls " << ls << std::endl;
+ if (!ls.empty()) {
+ if (!first) {
+ first = ls.front().first;
+ cout << "first is " << first << std::endl;
+ } else {
+ if (ls.front().first != first) {
+ cout << "first now " << ls.front().first << ", trimmed" << std::endl;
+ break;
+ }
+ }
+ }
+
+ utime_t now = ceph_clock_now();
+ ASSERT_TRUE(now < hard_stop);
+
+ sleep(1);
+ }
+ delete[] buf;
+}
+
+TEST_F(LibRadosTwoPoolsECPP, PromoteOn2ndRead) {
+ SKIP_IF_CRIMSON();
+ // create object
+ for (int i=0; i<20; ++i) {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo" + stringify(i), &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // enable hitset tracking for this pool
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_grade_decay_rate", 20),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_search_last_n", 1),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ int fake = 0; // set this to non-zero to test spurious promotion,
+ // e.g. from thrashing
+ int attempt = 0;
+ string obj;
+ while (true) {
+ // 1st read, don't trigger a promote
+ obj = "foo" + stringify(attempt);
+ cout << obj << std::endl;
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+ if (--fake >= 0) {
+ sleep(1);
+ ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+ sleep(1);
+ }
+ }
+
+ // verify the object is NOT present in the cache tier
+ {
+ bool found = false;
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ while (it != cache_ioctx.nobjects_end()) {
+ cout << " see " << it->get_oid() << std::endl;
+ if (it->get_oid() == string(obj.c_str())) {
+ found = true;
+ break;
+ }
+ ++it;
+ }
+ if (!found)
+ break;
+ }
+
+ ++attempt;
+ ASSERT_LE(attempt, 20);
+ cout << "hrm, object is present in cache on attempt " << attempt
+ << ", retrying" << std::endl;
+ }
+
+ // Read until the object is present in the cache tier
+ cout << "verifying " << obj << " is eventually promoted" << std::endl;
+ while (true) {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+
+ bool there = false;
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ while (it != cache_ioctx.nobjects_end()) {
+ if (it->get_oid() == string(obj.c_str())) {
+ there = true;
+ break;
+ }
+ ++it;
+ }
+ if (there)
+ break;
+
+ sleep(1);
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, ProxyRead) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"readproxy\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read and verify the object
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('h', bl[0]);
+ }
+
+ // Verify 10 times the object is NOT present in the cache tier
+ uint32_t i = 0;
+ while (i++ < 10) {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ sleep(1);
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, CachePin) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("baz", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bam", &op));
+ }
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // read, trigger promote
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+ ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+ ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+ }
+
+ // verify the objects are present in the cache tier
+ {
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+ for (uint32_t i = 0; i < 4; i++) {
+ ASSERT_TRUE(it->get_oid() == string("foo") ||
+ it->get_oid() == string("bar") ||
+ it->get_oid() == string("baz") ||
+ it->get_oid() == string("bam"));
+ ++it;
+ }
+ ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+ }
+
+ // pin objects
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ {
+ ObjectWriteOperation op;
+ op.cache_pin();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("baz", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // enable agent
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "target_max_objects", 1),
+ inbl, NULL, NULL));
+
+ sleep(10);
+
+ // Verify the pinned object 'foo' is not flushed/evicted
+ uint32_t count = 0;
+ while (true) {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+
+ count = 0;
+ NObjectIterator it = cache_ioctx.nobjects_begin();
+ while (it != cache_ioctx.nobjects_end()) {
+ ASSERT_TRUE(it->get_oid() == string("foo") ||
+ it->get_oid() == string("bar") ||
+ it->get_oid() == string("baz") ||
+ it->get_oid() == string("bam"));
+ ++count;
+ ++it;
+ }
+ if (count == 2) {
+ ASSERT_TRUE(it->get_oid() == string("foo") ||
+ it->get_oid() == string("baz"));
+ break;
+ }
+
+ sleep(1);
+ }
+
+ // tear down tiers
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+TEST_F(LibRadosTwoPoolsECPP, SetRedirectRead) {
+ SKIP_IF_CRIMSON();
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hi there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+ }
+
+ // configure tier
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ {
+ ObjectWriteOperation op;
+ op.set_redirect("bar", cache_ioctx, 0);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // read and verify the object
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('t', bl[0]);
+ }
+
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, SetChunkRead) {
+ SKIP_IF_CRIMSON();
+ // note: require >= mimic
+
+ {
+ bufferlist bl;
+ bl.append("there hi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+
+ {
+ bufferlist bl;
+ bl.append("There hi");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // set_chunk
+ manifest_set_chunk(cluster, ioctx, cache_ioctx, 0, 4, "bar", "foo");
+
+ // promote
+ {
+ ObjectWriteOperation op;
+ op.tier_promote();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // read and verify the object
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('T', bl[0]);
+ }
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, ManifestPromoteRead) {
+ SKIP_IF_CRIMSON();
+ // note: require >= mimic
+
+ // create object
+ {
+ bufferlist bl;
+ bl.append("hiaa there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("base chunk");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo-chunk", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("HIaa there");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("BASE CHUNK");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar-chunk", &op));
+ }
+
+ // set-redirect
+ {
+ ObjectWriteOperation op;
+ op.set_redirect("bar", ioctx, 0);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // set-chunk
+ manifest_set_chunk(cluster, ioctx, cache_ioctx, 0, 10, "bar-chunk", "foo-chunk");
+ // promote
+ {
+ ObjectWriteOperation op;
+ op.tier_promote();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // read and verify the object (redirect)
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+ ASSERT_EQ('H', bl[0]);
+ }
+ // promote
+ {
+ ObjectWriteOperation op;
+ op.tier_promote();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo-chunk", completion, &op));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+ // read and verify the object
+ {
+ bufferlist bl;
+ ASSERT_EQ(1, cache_ioctx.read("foo-chunk", bl, 1, 0));
+ ASSERT_EQ('B', bl[0]);
+ }
+
+ // wait for maps to settle before next test
+ cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, TrySetDedupTier) {
+ SKIP_IF_CRIMSON();
+ // note: require >= mimic
+
+ bufferlist inbl;
+ ASSERT_EQ(-EOPNOTSUPP, cluster.mon_command(
+ set_pool_str(pool_name, "dedup_tier", cache_pool_name),
+ inbl, NULL, NULL));
+}
+
+TEST_F(LibRadosTwoPoolsPP, PropagateBaseTierError) {
+ SKIP_IF_CRIMSON();
+ // write object to base tier
+ bufferlist omap_bl;
+ encode(static_cast<uint32_t>(0U), omap_bl);
+
+ ObjectWriteOperation op1;
+ op1.omap_set({{"somekey", omap_bl}});
+ ASSERT_EQ(0, ioctx.operate("propagate-base-tier-error", &op1));
+
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_count", 1),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "target_max_objects", 250),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // guarded op should fail so expect error to propagate to cache tier
+ bufferlist test_omap_bl;
+ encode(static_cast<uint32_t>(1U), test_omap_bl);
+
+ ObjectWriteOperation op2;
+ op2.omap_cmp({{"somekey", {test_omap_bl, CEPH_OSD_CMPXATTR_OP_EQ}}}, nullptr);
+ op2.omap_set({{"somekey", test_omap_bl}});
+
+ ASSERT_EQ(-ECANCELED, ioctx.operate("propagate-base-tier-error", &op2));
+}
+
+TEST_F(LibRadosTwoPoolsPP, HelloWriteReturn) {
+ SKIP_IF_CRIMSON();
+ // configure cache
+ bufferlist inbl;
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+ "\", \"tierpool\": \"" + cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+ "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+
+ // set things up such that the op would normally be proxied
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_count", 2),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_period", 600),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "hit_set_type",
+ "explicit_object"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "min_read_recency_for_promote",
+ "10000"),
+ inbl, NULL, NULL));
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // this *will* return data due to the RETURNVEC flag
+ {
+ bufferlist in, out;
+ int rval;
+ ObjectWriteOperation o;
+ o.exec("hello", "write_return_data", in, &out, &rval);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &o,
+ librados::OPERATION_RETURNVEC));
+ completion->wait_for_complete();
+ ASSERT_EQ(42, completion->get_return_value());
+ ASSERT_EQ(42, rval);
+ out.hexdump(std::cout);
+ ASSERT_EQ("you might see this", std::string(out.c_str(), out.length()));
+ }
+
+ // this will overflow because the return data is too big
+ {
+ bufferlist in, out;
+ int rval;
+ ObjectWriteOperation o;
+ o.exec("hello", "write_too_much_return_data", in, &out, &rval);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &o,
+ librados::OPERATION_RETURNVEC));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EOVERFLOW, completion->get_return_value());
+ ASSERT_EQ(-EOVERFLOW, rval);
+ ASSERT_EQ("", std::string(out.c_str(), out.length()));
+ }
+}
+
+TEST_F(LibRadosTwoPoolsPP, TierFlushDuringUnsetDedupTier) {
+ SKIP_IF_CRIMSON();
+ // skip test if not yet octopus
+ if (_get_required_osd_release(cluster) < "octopus") {
+ cout << "cluster is not yet octopus, skipping test" << std::endl;
+ return;
+ }
+
+ bufferlist inbl;
+
+ // set dedup parameters without dedup_tier
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "fingerprint_algorithm", "sha1"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_chunk_algorithm", "fastcdc"),
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, cluster.mon_command(
+ set_pool_str(cache_pool_name, "dedup_cdc_chunk_size", 1024),
+ inbl, NULL, NULL));
+
+ // create object
+ bufferlist gbl;
+ {
+ generate_buffer(1024*8, &gbl);
+ ObjectWriteOperation op;
+ op.write_full(gbl);
+ ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+ }
+ {
+ bufferlist bl;
+ bl.append("there hiHI");
+ ObjectWriteOperation op;
+ op.write_full(bl);
+ ASSERT_EQ(0, ioctx.operate("bar", &op));
+ }
+
+ // wait for maps to settle
+ cluster.wait_for_latest_osdmap();
+
+ // set-chunk to set manifest object
+ {
+ ObjectReadOperation op;
+ op.set_chunk(0, 2, ioctx, "bar", 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+ librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(0, completion->get_return_value());
+ completion->release();
+ }
+
+ // flush to check if proper error is returned
+ {
+ ObjectReadOperation op;
+ op.tier_flush();
+ librados::AioCompletion *completion = cluster.aio_create_completion();
+ ASSERT_EQ(0, cache_ioctx.aio_operate(
+ "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+ completion->wait_for_complete();
+ ASSERT_EQ(-EINVAL, completion->get_return_value());
+ completion->release();
+ }
+}
+
diff --git a/src/test/librados/watch_notify.cc b/src/test/librados/watch_notify.cc
new file mode 100644
index 000000000..4d880f2c2
--- /dev/null
+++ b/src/test/librados/watch_notify.cc
@@ -0,0 +1,657 @@
+#include "include/rados/librados.h"
+#include "include/rados/rados_types.h"
+#include "test/librados/test.h"
+#include "test/librados/TestCase.h"
+#include "crimson_utils.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include "gtest/gtest.h"
+#include "include/encoding.h"
+#include <set>
+#include <map>
+
+typedef RadosTestEC LibRadosWatchNotifyEC;
+
+int notify_sleep = 0;
+
+// notify
+static sem_t sem;
+
+static void watch_notify_test_cb(uint8_t opcode, uint64_t ver, void *arg)
+{
+ std::cout << __func__ << std::endl;
+ sem_post(&sem);
+}
+
+class LibRadosWatchNotify : public RadosTest
+{
+protected:
+ // notify 2
+ bufferlist notify_bl;
+ std::set<uint64_t> notify_cookies;
+ rados_ioctx_t notify_io;
+ const char *notify_oid = nullptr;
+ int notify_err = 0;
+ rados_completion_t notify_comp;
+
+ static void watch_notify2_test_cb(void *arg,
+ uint64_t notify_id,
+ uint64_t cookie,
+ uint64_t notifier_gid,
+ void *data,
+ size_t data_len);
+ static void watch_notify2_test_errcb(void *arg, uint64_t cookie, int err);
+ static void watch_notify2_test_errcb_reconnect(void *arg, uint64_t cookie, int err);
+ static void watch_notify2_test_errcb_aio_reconnect(void *arg, uint64_t cookie, int err);
+};
+
+
+void LibRadosWatchNotify::watch_notify2_test_cb(void *arg,
+ uint64_t notify_id,
+ uint64_t cookie,
+ uint64_t notifier_gid,
+ void *data,
+ size_t data_len)
+{
+ std::cout << __func__ << " from " << notifier_gid << " notify_id " << notify_id
+ << " cookie " << cookie << std::endl;
+ ceph_assert(notifier_gid > 0);
+ auto thiz = reinterpret_cast<LibRadosWatchNotify*>(arg);
+ ceph_assert(thiz);
+ thiz->notify_cookies.insert(cookie);
+ thiz->notify_bl.clear();
+ thiz->notify_bl.append((char*)data, data_len);
+ if (notify_sleep)
+ sleep(notify_sleep);
+ thiz->notify_err = 0;
+ rados_notify_ack(thiz->notify_io, thiz->notify_oid, notify_id, cookie,
+ "reply", 5);
+}
+
+void LibRadosWatchNotify::watch_notify2_test_errcb(void *arg,
+ uint64_t cookie,
+ int err)
+{
+ std::cout << __func__ << " cookie " << cookie << " err " << err << std::endl;
+ ceph_assert(cookie > 1000);
+ auto thiz = reinterpret_cast<LibRadosWatchNotify*>(arg);
+ ceph_assert(thiz);
+ thiz->notify_err = err;
+}
+
+void LibRadosWatchNotify::watch_notify2_test_errcb_reconnect(void *arg,
+ uint64_t cookie,
+ int err)
+{
+ std::cout << __func__ << " cookie " << cookie << " err " << err << std::endl;
+ ceph_assert(cookie > 1000);
+ auto thiz = reinterpret_cast<LibRadosWatchNotify*>(arg);
+ ceph_assert(thiz);
+ thiz->notify_err = rados_unwatch2(thiz->ioctx, cookie);
+ thiz->notify_cookies.erase(cookie); //delete old cookie
+ thiz->notify_err = rados_watch2(thiz->ioctx, thiz->notify_oid, &cookie,
+ watch_notify2_test_cb, watch_notify2_test_errcb_reconnect, thiz);
+ if (thiz->notify_err < 0) {
+ std::cout << __func__ << " reconnect watch failed with error " << thiz->notify_err << std::endl;
+ return;
+ }
+ return;
+}
+
+
+void LibRadosWatchNotify::watch_notify2_test_errcb_aio_reconnect(void *arg,
+ uint64_t cookie,
+ int err)
+{
+ std::cout << __func__ << " cookie " << cookie << " err " << err << std::endl;
+ ceph_assert(cookie > 1000);
+ auto thiz = reinterpret_cast<LibRadosWatchNotify*>(arg);
+ ceph_assert(thiz);
+ thiz->notify_err = rados_aio_unwatch(thiz->ioctx, cookie, thiz->notify_comp);
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &thiz->notify_comp));
+ thiz->notify_cookies.erase(cookie); //delete old cookie
+ thiz->notify_err = rados_aio_watch(thiz->ioctx, thiz->notify_oid, thiz->notify_comp, &cookie,
+ watch_notify2_test_cb, watch_notify2_test_errcb_aio_reconnect, thiz);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(thiz->notify_comp));
+ ASSERT_EQ(0, rados_aio_get_return_value(thiz->notify_comp));
+ rados_aio_release(thiz->notify_comp);
+ if (thiz->notify_err < 0) {
+ std::cout << __func__ << " reconnect watch failed with error " << thiz->notify_err << std::endl;
+ return;
+ }
+ return;
+}
+
+class WatchNotifyTestCtx2;
+
+// --
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+TEST_F(LibRadosWatchNotify, WatchNotify) {
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ uint64_t handle;
+ ASSERT_EQ(0,
+ rados_watch(ioctx, "foo", 0, &handle, watch_notify_test_cb, NULL));
+ for (unsigned i=0; i<10; ++i) {
+ int r = rados_notify(ioctx, "foo", 0, NULL, 0);
+ if (r == 0) {
+ break;
+ }
+ if (!getenv("ALLOW_TIMEOUTS")) {
+ ASSERT_EQ(0, r);
+ }
+ }
+ TestAlarm alarm;
+ sem_wait(&sem);
+ rados_unwatch(ioctx, "foo", handle);
+
+ // when dne ...
+ ASSERT_EQ(-ENOENT,
+ rados_watch(ioctx, "dne", 0, &handle, watch_notify_test_cb, NULL));
+
+ sem_destroy(&sem);
+}
+
+TEST_F(LibRadosWatchNotifyEC, WatchNotify) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+ uint64_t handle;
+ ASSERT_EQ(0,
+ rados_watch(ioctx, "foo", 0, &handle, watch_notify_test_cb, NULL));
+ for (unsigned i=0; i<10; ++i) {
+ int r = rados_notify(ioctx, "foo", 0, NULL, 0);
+ if (r == 0) {
+ break;
+ }
+ if (!getenv("ALLOW_TIMEOUTS")) {
+ ASSERT_EQ(0, r);
+ }
+ }
+ TestAlarm alarm;
+ sem_wait(&sem);
+ rados_unwatch(ioctx, "foo", handle);
+ sem_destroy(&sem);
+}
+
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"
+
+
+// --
+
+TEST_F(LibRadosWatchNotify, Watch2Delete) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_err = 0;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+ uint64_t handle;
+ ASSERT_EQ(0,
+ rados_watch2(ioctx, notify_oid, &handle,
+ watch_notify2_test_cb,
+ watch_notify2_test_errcb, this));
+ ASSERT_EQ(0, rados_remove(ioctx, notify_oid));
+ int left = 300;
+ std::cout << "waiting up to " << left << " for disconnect notification ..."
+ << std::endl;
+ while (notify_err == 0 && --left) {
+ sleep(1);
+ }
+ ASSERT_TRUE(left > 0);
+ ASSERT_EQ(-ENOTCONN, notify_err);
+ int rados_watch_check_err = rados_watch_check(ioctx, handle);
+ // We may hit ENOENT due to socket failure and a forced reconnect
+ EXPECT_TRUE(rados_watch_check_err == -ENOTCONN || rados_watch_check_err == -ENOENT)
+ << "Where rados_watch_check_err = " << rados_watch_check_err;
+ rados_unwatch2(ioctx, handle);
+ rados_watch_flush(cluster);
+}
+
+TEST_F(LibRadosWatchNotify, AioWatchDelete) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_err = 0;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+
+
+ rados_completion_t comp;
+ uint64_t handle;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &comp));
+ rados_aio_watch(ioctx, notify_oid, comp, &handle,
+ watch_notify2_test_cb, watch_notify2_test_errcb, this);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rados_aio_get_return_value(comp));
+ rados_aio_release(comp);
+ ASSERT_EQ(0, rados_remove(ioctx, notify_oid));
+ int left = 300;
+ std::cout << "waiting up to " << left << " for disconnect notification ..."
+ << std::endl;
+ while (notify_err == 0 && --left) {
+ sleep(1);
+ }
+ ASSERT_TRUE(left > 0);
+ ASSERT_EQ(-ENOTCONN, notify_err);
+ int rados_watch_check_err = rados_watch_check(ioctx, handle);
+ // We may hit ENOENT due to socket failure injection and a forced reconnect
+ EXPECT_TRUE(rados_watch_check_err == -ENOTCONN || rados_watch_check_err == -ENOENT)
+ << "Where rados_watch_check_err = " << rados_watch_check_err;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &comp));
+ rados_aio_unwatch(ioctx, handle, comp);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(comp));
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(comp));
+ rados_aio_release(comp);
+}
+
+// --
+
+TEST_F(LibRadosWatchNotify, WatchNotify2) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+ uint64_t handle;
+ ASSERT_EQ(0,
+ rados_watch2(ioctx, notify_oid, &handle,
+ watch_notify2_test_cb,
+ watch_notify2_test_errcb_reconnect, this));
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+ char *reply_buf = 0;
+ size_t reply_buf_len;
+ ASSERT_EQ(0, rados_notify2(ioctx, notify_oid,
+ "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ bufferlist reply;
+ reply.append(reply_buf, reply_buf_len);
+ std::map<std::pair<uint64_t,uint64_t>, bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ auto reply_p = reply.cbegin();
+ decode(reply_map, reply_p);
+ decode(missed_map, reply_p);
+ ASSERT_EQ(1u, reply_map.size());
+ ASSERT_EQ(0u, missed_map.size());
+ ASSERT_EQ(1u, notify_cookies.size());
+ handle = *notify_cookies.begin();
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(5u, reply_map.begin()->second.length());
+ ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+ rados_buffer_free(reply_buf);
+
+ // try it on a non-existent object ... our buffer pointers
+ // should get zeroed.
+ ASSERT_EQ(-ENOENT, rados_notify2(ioctx, "doesnotexist",
+ "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ ASSERT_EQ((char*)0, reply_buf);
+ ASSERT_EQ(0u, reply_buf_len);
+
+ rados_unwatch2(ioctx, handle);
+ rados_watch_flush(cluster);
+}
+
+TEST_F(LibRadosWatchNotify, AioWatchNotify2) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+ uint64_t handle;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &notify_comp));
+ rados_aio_watch(ioctx, notify_oid, notify_comp, &handle,
+ watch_notify2_test_cb, watch_notify2_test_errcb_aio_reconnect, this);
+
+ ASSERT_EQ(0, rados_aio_wait_for_complete(notify_comp));
+ ASSERT_EQ(0, rados_aio_get_return_value(notify_comp));
+ rados_aio_release(notify_comp);
+
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+ char *reply_buf = 0;
+ size_t reply_buf_len;
+ ASSERT_EQ(0, rados_notify2(ioctx, notify_oid,
+ "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ bufferlist reply;
+ reply.append(reply_buf, reply_buf_len);
+ std::map<std::pair<uint64_t,uint64_t>, bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ auto reply_p = reply.cbegin();
+ decode(reply_map, reply_p);
+ decode(missed_map, reply_p);
+ ASSERT_EQ(1u, reply_map.size());
+ ASSERT_EQ(0u, missed_map.size());
+ ASSERT_EQ(1u, notify_cookies.size());
+ handle = *notify_cookies.begin();
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(5u, reply_map.begin()->second.length());
+ ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+ rados_buffer_free(reply_buf);
+
+ // try it on a non-existent object ... our buffer pointers
+ // should get zeroed.
+ ASSERT_EQ(-ENOENT, rados_notify2(ioctx, "doesnotexist",
+ "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ ASSERT_EQ((char*)0, reply_buf);
+ ASSERT_EQ(0u, reply_buf_len);
+
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &notify_comp));
+ rados_aio_unwatch(ioctx, handle, notify_comp);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(notify_comp));
+ ASSERT_EQ(0, rados_aio_get_return_value(notify_comp));
+ rados_aio_release(notify_comp);
+}
+
+TEST_F(LibRadosWatchNotify, AioNotify) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+ uint64_t handle;
+ ASSERT_EQ(0,
+ rados_watch2(ioctx, notify_oid, &handle,
+ watch_notify2_test_cb,
+ watch_notify2_test_errcb, this));
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+ char *reply_buf = 0;
+ size_t reply_buf_len;
+ rados_completion_t comp;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &comp));
+ ASSERT_EQ(0, rados_aio_notify(ioctx, "foo", comp, "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ ASSERT_EQ(0, rados_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rados_aio_get_return_value(comp));
+ rados_aio_release(comp);
+
+ size_t nr_acks, nr_timeouts;
+ notify_ack_t *acks = nullptr;
+ notify_timeout_t *timeouts = nullptr;
+ ASSERT_EQ(0, rados_decode_notify_response(reply_buf, reply_buf_len,
+ &acks, &nr_acks, &timeouts, &nr_timeouts));
+ ASSERT_EQ(1u, nr_acks);
+ ASSERT_EQ(0u, nr_timeouts);
+ ASSERT_EQ(1u, notify_cookies.size());
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(5u, acks[0].payload_len);
+ ASSERT_EQ(0, strncmp("reply", acks[0].payload, acks[0].payload_len));
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+ rados_free_notify_response(acks, nr_acks, timeouts);
+ rados_buffer_free(reply_buf);
+
+ // try it on a non-existent object ... our buffer pointers
+ // should get zeroed.
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &comp));
+ ASSERT_EQ(0, rados_aio_notify(ioctx, "doesnotexist", comp, "notify", 6,
+ 300000, &reply_buf, &reply_buf_len));
+ ASSERT_EQ(0, rados_aio_wait_for_complete(comp));
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(comp));
+ rados_aio_release(comp);
+ ASSERT_EQ((char*)0, reply_buf);
+ ASSERT_EQ(0u, reply_buf_len);
+
+ rados_unwatch2(ioctx, handle);
+ rados_watch_flush(cluster);
+}
+
+// --
+
+TEST_F(LibRadosWatchNotify, WatchNotify2Multi) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+ uint64_t handle1, handle2;
+ ASSERT_EQ(0,
+ rados_watch2(ioctx, notify_oid, &handle1,
+ watch_notify2_test_cb,
+ watch_notify2_test_errcb, this));
+ ASSERT_EQ(0,
+ rados_watch2(ioctx, notify_oid, &handle2,
+ watch_notify2_test_cb,
+ watch_notify2_test_errcb, this));
+ ASSERT_GT(rados_watch_check(ioctx, handle1), 0);
+ ASSERT_GT(rados_watch_check(ioctx, handle2), 0);
+ ASSERT_NE(handle1, handle2);
+ char *reply_buf = 0;
+ size_t reply_buf_len;
+ ASSERT_EQ(0, rados_notify2(ioctx, notify_oid,
+ "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ bufferlist reply;
+ reply.append(reply_buf, reply_buf_len);
+ std::map<std::pair<uint64_t,uint64_t>, bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ auto reply_p = reply.cbegin();
+ decode(reply_map, reply_p);
+ decode(missed_map, reply_p);
+ ASSERT_EQ(2u, reply_map.size());
+ ASSERT_EQ(5u, reply_map.begin()->second.length());
+ ASSERT_EQ(0u, missed_map.size());
+ ASSERT_EQ(2u, notify_cookies.size());
+ ASSERT_EQ(1u, notify_cookies.count(handle1));
+ ASSERT_EQ(1u, notify_cookies.count(handle2));
+ ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+ ASSERT_GT(rados_watch_check(ioctx, handle1), 0);
+ ASSERT_GT(rados_watch_check(ioctx, handle2), 0);
+ rados_buffer_free(reply_buf);
+ rados_unwatch2(ioctx, handle1);
+ rados_unwatch2(ioctx, handle2);
+ rados_watch_flush(cluster);
+}
+
+// --
+
+TEST_F(LibRadosWatchNotify, WatchNotify2Timeout) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_sleep = 3; // 3s
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+ uint64_t handle;
+ ASSERT_EQ(0,
+ rados_watch2(ioctx, notify_oid, &handle,
+ watch_notify2_test_cb,
+ watch_notify2_test_errcb, this));
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+ char *reply_buf = 0;
+ size_t reply_buf_len;
+ ASSERT_EQ(-ETIMEDOUT, rados_notify2(ioctx, notify_oid,
+ "notify", 6, 1000, // 1s
+ &reply_buf, &reply_buf_len));
+ ASSERT_EQ(1u, notify_cookies.size());
+ {
+ bufferlist reply;
+ reply.append(reply_buf, reply_buf_len);
+ std::map<std::pair<uint64_t,uint64_t>, bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ auto reply_p = reply.cbegin();
+ decode(reply_map, reply_p);
+ decode(missed_map, reply_p);
+ ASSERT_EQ(0u, reply_map.size());
+ ASSERT_EQ(1u, missed_map.size());
+ }
+ rados_buffer_free(reply_buf);
+
+ // we should get the next notify, though!
+ notify_sleep = 0;
+ notify_cookies.clear();
+ ASSERT_EQ(0, rados_notify2(ioctx, notify_oid,
+ "notify", 6, 300000, // 300s
+ &reply_buf, &reply_buf_len));
+ ASSERT_EQ(1u, notify_cookies.size());
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+
+ rados_unwatch2(ioctx, handle);
+
+ rados_completion_t comp;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &comp));
+ rados_aio_watch_flush(cluster, comp);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rados_aio_get_return_value(comp));
+ rados_aio_release(comp);
+ rados_buffer_free(reply_buf);
+
+}
+
+TEST_F(LibRadosWatchNotify, Watch3Timeout) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_cookies.clear();
+ notify_err = 0;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+ uint64_t handle;
+ time_t start = time(0);
+ const uint32_t timeout = 4;
+ {
+ // make sure i timeout before the messenger reconnects to the OSD,
+ // it will resend a watch request on behalf of the client, and the
+ // timer of timeout on OSD side will be reset by the new request.
+ char conf[128];
+ ASSERT_EQ(0, rados_conf_get(cluster,
+ "ms_connection_idle_timeout",
+ conf, sizeof(conf)));
+ auto connection_idle_timeout = std::stoll(conf);
+ ASSERT_LT(timeout, connection_idle_timeout);
+ }
+ ASSERT_EQ(0,
+ rados_watch3(ioctx, notify_oid, &handle,
+ watch_notify2_test_cb, watch_notify2_test_errcb,
+ timeout, this));
+ int age = rados_watch_check(ioctx, handle);
+ time_t age_bound = time(0) + 1 - start;
+ ASSERT_LT(age, age_bound * 1000);
+ ASSERT_GT(age, 0);
+ rados_conf_set(cluster, "objecter_inject_no_watch_ping", "true");
+ // allow a long time here since an osd peering event will renew our
+ // watch.
+ int left = 256 * timeout;
+ std::cout << "waiting up to " << left << " for osd to time us out ..."
+ << std::endl;
+ while (notify_err == 0 && --left) {
+ sleep(1);
+ }
+ ASSERT_GT(left, 0);
+ rados_conf_set(cluster, "objecter_inject_no_watch_ping", "false");
+ ASSERT_EQ(-ENOTCONN, notify_err);
+ ASSERT_EQ(-ENOTCONN, rados_watch_check(ioctx, handle));
+
+ // a subsequent notify should not reach us
+ char *reply_buf = nullptr;
+ size_t reply_buf_len;
+ ASSERT_EQ(0, rados_notify2(ioctx, notify_oid,
+ "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ {
+ bufferlist reply;
+ reply.append(reply_buf, reply_buf_len);
+ std::map<std::pair<uint64_t,uint64_t>, bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ auto reply_p = reply.cbegin();
+ decode(reply_map, reply_p);
+ decode(missed_map, reply_p);
+ ASSERT_EQ(0u, reply_map.size());
+ ASSERT_EQ(0u, missed_map.size());
+ }
+ ASSERT_EQ(0u, notify_cookies.size());
+ ASSERT_EQ(-ENOTCONN, rados_watch_check(ioctx, handle));
+ rados_buffer_free(reply_buf);
+
+ // re-watch
+ rados_unwatch2(ioctx, handle);
+ rados_watch_flush(cluster);
+
+ handle = 0;
+ ASSERT_EQ(0,
+ rados_watch2(ioctx, notify_oid, &handle,
+ watch_notify2_test_cb,
+ watch_notify2_test_errcb, this));
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+
+ // and now a notify will work.
+ ASSERT_EQ(0, rados_notify2(ioctx, notify_oid,
+ "notify", 6, 300000,
+ &reply_buf, &reply_buf_len));
+ {
+ bufferlist reply;
+ reply.append(reply_buf, reply_buf_len);
+ std::map<std::pair<uint64_t,uint64_t>, bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ auto reply_p = reply.cbegin();
+ decode(reply_map, reply_p);
+ decode(missed_map, reply_p);
+ ASSERT_EQ(1u, reply_map.size());
+ ASSERT_EQ(0u, missed_map.size());
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(5u, reply_map.begin()->second.length());
+ ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+ }
+ ASSERT_EQ(1u, notify_cookies.size());
+ ASSERT_GT(rados_watch_check(ioctx, handle), 0);
+
+ rados_buffer_free(reply_buf);
+ rados_unwatch2(ioctx, handle);
+ rados_watch_flush(cluster);
+}
+
+TEST_F(LibRadosWatchNotify, AioWatchDelete2) {
+ notify_io = ioctx;
+ notify_oid = "foo";
+ notify_err = 0;
+ char buf[128];
+ uint32_t timeout = 3;
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_write(ioctx, notify_oid, buf, sizeof(buf), 0));
+
+
+ rados_completion_t comp;
+ uint64_t handle;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &comp));
+ rados_aio_watch2(ioctx, notify_oid, comp, &handle,
+ watch_notify2_test_cb, watch_notify2_test_errcb, timeout, this);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rados_aio_get_return_value(comp));
+ rados_aio_release(comp);
+ ASSERT_EQ(0, rados_remove(ioctx, notify_oid));
+ int left = 30;
+ std::cout << "waiting up to " << left << " for disconnect notification ..."
+ << std::endl;
+ while (notify_err == 0 && --left) {
+ sleep(1);
+ }
+ ASSERT_TRUE(left > 0);
+ ASSERT_EQ(-ENOTCONN, notify_err);
+ int rados_watch_check_err = rados_watch_check(ioctx, handle);
+ // We may hit ENOENT due to socket failure injection and a forced reconnect
+ EXPECT_TRUE(rados_watch_check_err == -ENOTCONN || rados_watch_check_err == -ENOENT)
+ << "Where rados_watch_check_err = " << rados_watch_check_err;
+ ASSERT_EQ(0, rados_aio_create_completion2(nullptr, nullptr, &comp));
+ rados_aio_unwatch(ioctx, handle, comp);
+ ASSERT_EQ(0, rados_aio_wait_for_complete(comp));
+ ASSERT_EQ(-ENOENT, rados_aio_get_return_value(comp));
+ rados_aio_release(comp);
+}
diff --git a/src/test/librados/watch_notify_cxx.cc b/src/test/librados/watch_notify_cxx.cc
new file mode 100644
index 000000000..808384bcc
--- /dev/null
+++ b/src/test/librados/watch_notify_cxx.cc
@@ -0,0 +1,416 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <set>
+#include <map>
+
+#include "gtest/gtest.h"
+
+#include "include/encoding.h"
+#include "include/rados/librados.hpp"
+#include "include/rados/rados_types.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+#include "crimson_utils.h"
+
+using namespace librados;
+
+typedef RadosTestECPP LibRadosWatchNotifyECPP;
+
+int notify_sleep = 0;
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+class LibRadosWatchNotifyPP : public RadosTestParamPP
+{
+protected:
+ bufferlist notify_bl;
+ std::set<uint64_t> notify_cookies;
+ rados_ioctx_t notify_io;
+ const char *notify_oid = nullptr;
+ int notify_err = 0;
+
+ friend class WatchNotifyTestCtx2;
+ friend class WatchNotifyTestCtx2TimeOut;
+};
+
+IoCtx *notify_ioctx;
+
+class WatchNotifyTestCtx2 : public WatchCtx2
+{
+ LibRadosWatchNotifyPP *notify;
+
+public:
+ WatchNotifyTestCtx2(LibRadosWatchNotifyPP *notify)
+ : notify(notify)
+ {}
+
+ void handle_notify(uint64_t notify_id, uint64_t cookie, uint64_t notifier_gid,
+ bufferlist& bl) override {
+ std::cout << __func__ << " cookie " << cookie << " notify_id " << notify_id
+ << " notifier_gid " << notifier_gid << std::endl;
+ notify->notify_bl = bl;
+ notify->notify_cookies.insert(cookie);
+ bufferlist reply;
+ reply.append("reply", 5);
+ if (notify_sleep)
+ sleep(notify_sleep);
+ notify_ioctx->notify_ack(notify->notify_oid, notify_id, cookie, reply);
+ }
+
+ void handle_error(uint64_t cookie, int err) override {
+ std::cout << __func__ << " cookie " << cookie
+ << " err " << err << std::endl;
+ ceph_assert(cookie > 1000);
+ notify_ioctx->unwatch2(cookie);
+ notify->notify_cookies.erase(cookie);
+ notify->notify_err = notify_ioctx->watch2(notify->notify_oid, &cookie, this);
+ if (notify->notify_err < err ) {
+ std::cout << "reconnect notify_err " << notify->notify_err << " err " << err << std::endl;
+ }
+ }
+};
+
+class WatchNotifyTestCtx2TimeOut : public WatchCtx2
+{
+ LibRadosWatchNotifyPP *notify;
+
+public:
+ WatchNotifyTestCtx2TimeOut(LibRadosWatchNotifyPP *notify)
+ : notify(notify)
+ {}
+
+ void handle_notify(uint64_t notify_id, uint64_t cookie, uint64_t notifier_gid,
+ bufferlist& bl) override {
+ std::cout << __func__ << " cookie " << cookie << " notify_id " << notify_id
+ << " notifier_gid " << notifier_gid << std::endl;
+ notify->notify_bl = bl;
+ notify->notify_cookies.insert(cookie);
+ bufferlist reply;
+ reply.append("reply", 5);
+ if (notify_sleep)
+ sleep(notify_sleep);
+ notify_ioctx->notify_ack(notify->notify_oid, notify_id, cookie, reply);
+ }
+
+ void handle_error(uint64_t cookie, int err) override {
+ std::cout << __func__ << " cookie " << cookie
+ << " err " << err << std::endl;
+ ceph_assert(cookie > 1000);
+ notify->notify_err = err;
+ }
+};
+
+// notify
+static sem_t sem;
+
+class WatchNotifyTestCtx : public WatchCtx
+{
+public:
+ void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) override
+ {
+ std::cout << __func__ << std::endl;
+ sem_post(&sem);
+ }
+};
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotify) {
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ uint64_t handle;
+ WatchNotifyTestCtx ctx;
+ ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+ std::list<obj_watch_t> watches;
+ ASSERT_EQ(0, ioctx.list_watchers("foo", &watches));
+ ASSERT_EQ(1u, watches.size());
+ bufferlist bl2;
+ for (unsigned i=0; i<10; ++i) {
+ int r = ioctx.notify("foo", 0, bl2);
+ if (r == 0) {
+ break;
+ }
+ if (!getenv("ALLOW_TIMEOUTS")) {
+ ASSERT_EQ(0, r);
+ }
+ }
+ TestAlarm alarm;
+ sem_wait(&sem);
+ ioctx.unwatch("foo", handle);
+ sem_destroy(&sem);
+}
+
+TEST_F(LibRadosWatchNotifyECPP, WatchNotify) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+ uint64_t handle;
+ WatchNotifyTestCtx ctx;
+ ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+ std::list<obj_watch_t> watches;
+ ASSERT_EQ(0, ioctx.list_watchers("foo", &watches));
+ ASSERT_EQ(1u, watches.size());
+ bufferlist bl2;
+ for (unsigned i=0; i<10; ++i) {
+ int r = ioctx.notify("foo", 0, bl2);
+ if (r == 0) {
+ break;
+ }
+ if (!getenv("ALLOW_TIMEOUTS")) {
+ ASSERT_EQ(0, r);
+ }
+ }
+ TestAlarm alarm;
+ sem_wait(&sem);
+ ioctx.unwatch("foo", handle);
+ sem_destroy(&sem);
+}
+
+// --
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotifyTimeout) {
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+ ioctx.set_notify_timeout(1);
+ uint64_t handle;
+ WatchNotifyTestCtx ctx;
+
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+ sem_destroy(&sem);
+ ASSERT_EQ(0, ioctx.unwatch("foo", handle));
+}
+
+TEST_F(LibRadosWatchNotifyECPP, WatchNotifyTimeout) {
+ SKIP_IF_CRIMSON();
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+ ioctx.set_notify_timeout(1);
+ uint64_t handle;
+ WatchNotifyTestCtx ctx;
+
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+ ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+ sem_destroy(&sem);
+ ASSERT_EQ(0, ioctx.unwatch("foo", handle));
+}
+
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotify2) {
+ notify_oid = "foo";
+ notify_ioctx = &ioctx;
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+ uint64_t handle;
+ WatchNotifyTestCtx2 ctx(this);
+ ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ std::list<obj_watch_t> watches;
+ ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+ ASSERT_EQ(watches.size(), 1u);
+ bufferlist bl2, bl_reply;
+ ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
+ auto p = bl_reply.cbegin();
+ std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ decode(reply_map, p);
+ decode(missed_map, p);
+ ASSERT_EQ(1u, notify_cookies.size());
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(1u, reply_map.size());
+ ASSERT_EQ(5u, reply_map.begin()->second.length());
+ ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+ ASSERT_EQ(0u, missed_map.size());
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ ioctx.unwatch2(handle);
+}
+
+TEST_P(LibRadosWatchNotifyPP, AioWatchNotify2) {
+ notify_oid = "foo";
+ notify_ioctx = &ioctx;
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+
+ uint64_t handle;
+ WatchNotifyTestCtx2 ctx(this);
+ librados::AioCompletion *comp = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_watch(notify_oid, comp, &handle, &ctx));
+ ASSERT_EQ(0, comp->wait_for_complete());
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ std::list<obj_watch_t> watches;
+ ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+ ASSERT_EQ(watches.size(), 1u);
+ bufferlist bl2, bl_reply;
+ ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
+ auto p = bl_reply.cbegin();
+ std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ decode(reply_map, p);
+ decode(missed_map, p);
+ ASSERT_EQ(1u, notify_cookies.size());
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(1u, reply_map.size());
+ ASSERT_EQ(5u, reply_map.begin()->second.length());
+ ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+ ASSERT_EQ(0u, missed_map.size());
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+
+ comp = cluster.aio_create_completion();
+ ioctx.aio_unwatch(handle, comp);
+ ASSERT_EQ(0, comp->wait_for_complete());
+ comp->release();
+}
+
+
+TEST_P(LibRadosWatchNotifyPP, AioNotify) {
+ notify_oid = "foo";
+ notify_ioctx = &ioctx;
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+ uint64_t handle;
+ WatchNotifyTestCtx2 ctx(this);
+ ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ std::list<obj_watch_t> watches;
+ ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+ ASSERT_EQ(watches.size(), 1u);
+ bufferlist bl2, bl_reply;
+ librados::AioCompletion *comp = cluster.aio_create_completion();
+ ASSERT_EQ(0, ioctx.aio_notify(notify_oid, comp, bl2, 300000, &bl_reply));
+ ASSERT_EQ(0, comp->wait_for_complete());
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+ std::vector<librados::notify_ack_t> acks;
+ std::vector<librados::notify_timeout_t> timeouts;
+ ioctx.decode_notify_response(bl_reply, &acks, &timeouts);
+ ASSERT_EQ(1u, notify_cookies.size());
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(1u, acks.size());
+ ASSERT_EQ(5u, acks[0].payload_bl.length());
+ ASSERT_EQ(0, strncmp("reply", acks[0].payload_bl.c_str(), acks[0].payload_bl.length()));
+ ASSERT_EQ(0u, timeouts.size());
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ ioctx.unwatch2(handle);
+ cluster.watch_flush();
+}
+
+// --
+TEST_P(LibRadosWatchNotifyPP, WatchNotify2Timeout) {
+ notify_oid = "foo";
+ notify_ioctx = &ioctx;
+ notify_sleep = 3; // 3s
+ notify_cookies.clear();
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+ uint64_t handle;
+ WatchNotifyTestCtx2TimeOut ctx(this);
+ ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ std::list<obj_watch_t> watches;
+ ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+ ASSERT_EQ(watches.size(), 1u);
+ ASSERT_EQ(0u, notify_cookies.size());
+ bufferlist bl2, bl_reply;
+ std::cout << " trying..." << std::endl;
+ ASSERT_EQ(-ETIMEDOUT, ioctx.notify2(notify_oid, bl2, 1000 /* 1s */,
+ &bl_reply));
+ std::cout << " timed out" << std::endl;
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ ioctx.unwatch2(handle);
+
+ std::cout << " flushing" << std::endl;
+ librados::AioCompletion *comp = cluster.aio_create_completion();
+ cluster.aio_watch_flush(comp);
+ ASSERT_EQ(0, comp->wait_for_complete());
+ ASSERT_EQ(0, comp->get_return_value());
+ std::cout << " flushed" << std::endl;
+ comp->release();
+}
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotify3) {
+ notify_oid = "foo";
+ notify_ioctx = &ioctx;
+ notify_cookies.clear();
+ uint32_t timeout = 12; // configured timeout
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+ uint64_t handle;
+ WatchNotifyTestCtx2TimeOut ctx(this);
+ ASSERT_EQ(0, ioctx.watch3(notify_oid, &handle, &ctx, timeout));
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ std::list<obj_watch_t> watches;
+ ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+ ASSERT_EQ(watches.size(), 1u);
+ std::cout << "List watches" << std::endl;
+ for (std::list<obj_watch_t>::iterator it = watches.begin();
+ it != watches.end(); ++it) {
+ ASSERT_EQ(it->timeout_seconds, timeout);
+ }
+ bufferlist bl2, bl_reply;
+ std::cout << "notify2" << std::endl;
+ ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
+ std::cout << "notify2 done" << std::endl;
+ auto p = bl_reply.cbegin();
+ std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
+ std::set<std::pair<uint64_t,uint64_t> > missed_map;
+ decode(reply_map, p);
+ decode(missed_map, p);
+ ASSERT_EQ(1u, notify_cookies.size());
+ ASSERT_EQ(1u, notify_cookies.count(handle));
+ ASSERT_EQ(1u, reply_map.size());
+ ASSERT_EQ(5u, reply_map.begin()->second.length());
+ ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+ ASSERT_EQ(0u, missed_map.size());
+ std::cout << "watch_check" << std::endl;
+ ASSERT_GT(ioctx.watch_check(handle), 0);
+ std::cout << "unwatch2" << std::endl;
+ ioctx.unwatch2(handle);
+
+ std::cout << " flushing" << std::endl;
+ cluster.watch_flush();
+ std::cout << "done" << std::endl;
+}
+// --
+
+INSTANTIATE_TEST_SUITE_P(LibRadosWatchNotifyPPTests, LibRadosWatchNotifyPP,
+ ::testing::Values("", "cache"));
diff --git a/src/test/librados_test_stub/CMakeLists.txt b/src/test/librados_test_stub/CMakeLists.txt
new file mode 100644
index 000000000..f501d2da2
--- /dev/null
+++ b/src/test/librados_test_stub/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(librados_test_stub_srcs
+ LibradosTestStub.cc
+ NeoradosTestStub.cc
+ TestClassHandler.cc
+ TestIoCtxImpl.cc
+ TestMemCluster.cc
+ TestMemIoCtxImpl.cc
+ TestMemRadosClient.cc
+ TestRadosClient.cc
+ TestWatchNotify.cc)
+add_library(rados_test_stub STATIC ${librados_test_stub_srcs})
+
diff --git a/src/test/librados_test_stub/LibradosTestStub.cc b/src/test/librados_test_stub/LibradosTestStub.cc
new file mode 100644
index 000000000..238cffa19
--- /dev/null
+++ b/src/test/librados_test_stub/LibradosTestStub.cc
@@ -0,0 +1,1568 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_context.h"
+#include "common/common_init.h"
+#include "common/config.h"
+#include "common/debug.h"
+#include "common/snap_types.h"
+#include "librados/AioCompletionImpl.h"
+#include "log/Log.h"
+#include "test/librados_test_stub/TestClassHandler.h"
+#include "test/librados_test_stub/TestIoCtxImpl.h"
+#include "test/librados_test_stub/TestRadosClient.h"
+#include "test/librados_test_stub/TestMemCluster.h"
+#include "test/librados_test_stub/TestMemRadosClient.h"
+#include "objclass/objclass.h"
+#include "osd/osd_types.h"
+#include <arpa/inet.h>
+#include <boost/shared_ptr.hpp>
+#include <deque>
+#include <functional>
+#include <list>
+#include <vector>
+#include "include/ceph_assert.h"
+#include "include/compat.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rados
+
+using namespace std;
+
+namespace librados {
+
+MockTestMemIoCtxImpl &get_mock_io_ctx(IoCtx &ioctx) {
+ MockTestMemIoCtxImpl **mock =
+ reinterpret_cast<MockTestMemIoCtxImpl **>(&ioctx);
+ return **mock;
+}
+
+} // namespace librados
+
+namespace librados_test_stub {
+
+TestClusterRef &cluster() {
+ static TestClusterRef s_cluster;
+ return s_cluster;
+}
+
+void set_cluster(TestClusterRef cluster_ref) {
+ cluster() = cluster_ref;
+}
+
+TestClusterRef get_cluster() {
+ auto &cluster_ref = cluster();
+ if (cluster_ref.get() == nullptr) {
+ cluster_ref.reset(new librados::TestMemCluster());
+ }
+ return cluster_ref;
+}
+
+librados::TestClassHandler *get_class_handler() {
+ static boost::shared_ptr<librados::TestClassHandler> s_class_handler;
+ if (!s_class_handler) {
+ s_class_handler.reset(new librados::TestClassHandler());
+ s_class_handler->open_all_classes();
+ }
+ return s_class_handler.get();
+}
+
+} // namespace librados_test_stub
+
+namespace {
+
+void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen) {
+ if (outbuf) {
+ if (outbl.length() > 0) {
+ *outbuf = (char *)malloc(outbl.length());
+ memcpy(*outbuf, outbl.c_str(), outbl.length());
+ } else {
+ *outbuf = NULL;
+ }
+ }
+ if (outbuflen) {
+ *outbuflen = outbl.length();
+ }
+}
+
+void do_out_buffer(string& outbl, char **outbuf, size_t *outbuflen) {
+ if (outbuf) {
+ if (outbl.length() > 0) {
+ *outbuf = (char *)malloc(outbl.length());
+ memcpy(*outbuf, outbl.c_str(), outbl.length());
+ } else {
+ *outbuf = NULL;
+ }
+ }
+ if (outbuflen) {
+ *outbuflen = outbl.length();
+ }
+}
+
+librados::TestRadosClient *create_rados_client() {
+ CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT);
+ CephContext *cct = common_preinit(iparams, CODE_ENVIRONMENT_LIBRARY, 0);
+ cct->_conf.parse_env(cct->get_module_type());
+ cct->_conf.apply_changes(nullptr);
+ cct->_log->start();
+
+ auto rados_client =
+ librados_test_stub::get_cluster()->create_rados_client(cct);
+ cct->put();
+ return rados_client;
+}
+
+} // anonymous namespace
+
+extern "C" int rados_aio_create_completion2(void *cb_arg,
+ rados_callback_t cb_complete,
+ rados_completion_t *pc)
+{
+ librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
+ if (cb_complete) {
+ c->set_complete_callback(cb_arg, cb_complete);
+ }
+ *pc = c;
+ return 0;
+}
+
+extern "C" int rados_aio_get_return_value(rados_completion_t c) {
+ return reinterpret_cast<librados::AioCompletionImpl*>(c)->get_return_value();
+}
+
+extern "C" rados_config_t rados_cct(rados_t cluster)
+{
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ return reinterpret_cast<rados_config_t>(client->cct());
+}
+
+extern "C" int rados_conf_set(rados_t cluster, const char *option,
+ const char *value) {
+ librados::TestRadosClient *impl =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ CephContext *cct = impl->cct();
+ return cct->_conf.set_val(option, value);
+}
+
+extern "C" int rados_conf_parse_env(rados_t cluster, const char *var) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ auto& conf = client->cct()->_conf;
+ conf.parse_env(client->cct()->get_module_type(), var);
+ conf.apply_changes(NULL);
+ return 0;
+}
+
+extern "C" int rados_conf_read_file(rados_t cluster, const char *path) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ auto& conf = client->cct()->_conf;
+ int ret = conf.parse_config_files(path, NULL, 0);
+ if (ret == 0) {
+ conf.parse_env(client->cct()->get_module_type());
+ conf.apply_changes(NULL);
+ conf.complain_about_parse_error(client->cct());
+ } else if (ret == -ENOENT) {
+ // ignore missing client config
+ return 0;
+ }
+ return ret;
+}
+
+extern "C" int rados_connect(rados_t cluster) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ return client->connect();
+}
+
+extern "C" int rados_create(rados_t *cluster, const char * const id) {
+ *cluster = create_rados_client();
+ return 0;
+}
+
+extern "C" int rados_create_with_context(rados_t *cluster,
+ rados_config_t cct_) {
+ auto cct = reinterpret_cast<CephContext*>(cct_);
+ *cluster = librados_test_stub::get_cluster()->create_rados_client(cct);
+ return 0;
+}
+
+extern "C" rados_config_t rados_ioctx_cct(rados_ioctx_t ioctx)
+{
+ librados::TestIoCtxImpl *ctx =
+ reinterpret_cast<librados::TestIoCtxImpl*>(ioctx);
+ return reinterpret_cast<rados_config_t>(ctx->get_rados_client()->cct());
+}
+
+extern "C" int rados_ioctx_create(rados_t cluster, const char *pool_name,
+ rados_ioctx_t *ioctx) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+
+ int64_t pool_id = client->pool_lookup(pool_name);
+ if (pool_id < 0) {
+ return static_cast<int>(pool_id);
+ }
+
+ *ioctx = reinterpret_cast<rados_ioctx_t>(
+ client->create_ioctx(pool_id, pool_name));
+ return 0;
+}
+
+extern "C" int rados_ioctx_create2(rados_t cluster, int64_t pool_id,
+ rados_ioctx_t *ioctx)
+{
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+
+ std::list<std::pair<int64_t, std::string> > pools;
+ int r = client->pool_list(pools);
+ if (r < 0) {
+ return r;
+ }
+
+ for (std::list<std::pair<int64_t, std::string> >::iterator it =
+ pools.begin(); it != pools.end(); ++it) {
+ if (it->first == pool_id) {
+ *ioctx = reinterpret_cast<rados_ioctx_t>(
+ client->create_ioctx(pool_id, it->second));
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+extern "C" void rados_ioctx_destroy(rados_ioctx_t io) {
+ librados::TestIoCtxImpl *ctx =
+ reinterpret_cast<librados::TestIoCtxImpl*>(io);
+ ctx->put();
+}
+
+extern "C" rados_t rados_ioctx_get_cluster(rados_ioctx_t io) {
+ librados::TestIoCtxImpl *ctx =
+ reinterpret_cast<librados::TestIoCtxImpl*>(io);
+ return reinterpret_cast<rados_t>(ctx->get_rados_client());
+}
+
+extern "C" int rados_mon_command(rados_t cluster, const char **cmd,
+ size_t cmdlen, const char *inbuf,
+ size_t inbuflen, char **outbuf,
+ size_t *outbuflen, char **outs,
+ size_t *outslen) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+
+ vector<string> cmdvec;
+ for (size_t i = 0; i < cmdlen; i++) {
+ cmdvec.push_back(cmd[i]);
+ }
+
+ bufferlist inbl;
+ inbl.append(inbuf, inbuflen);
+
+ bufferlist outbl;
+ string outstring;
+ int ret = client->mon_command(cmdvec, inbl, &outbl, &outstring);
+
+ do_out_buffer(outbl, outbuf, outbuflen);
+ do_out_buffer(outstring, outs, outslen);
+ return ret;
+}
+
+extern "C" int rados_nobjects_list_open(rados_ioctx_t io,
+ rados_list_ctx_t *ctx) {
+ librados::TestIoCtxImpl *io_ctx =
+ reinterpret_cast<librados::TestIoCtxImpl*>(io);
+ librados::TestRadosClient *client = io_ctx->get_rados_client();
+
+ std::list<librados::TestRadosClient::Object> *list =
+ new std::list<librados::TestRadosClient::Object>();
+
+ client->object_list(io_ctx->get_id(), list);
+ list->push_front(librados::TestRadosClient::Object());
+ *ctx = reinterpret_cast<rados_list_ctx_t>(list);
+ return 0;
+}
+
+extern "C" int rados_nobjects_list_next(rados_list_ctx_t ctx,
+ const char **entry,
+ const char **key,
+ const char **nspace) {
+ std::list<librados::TestRadosClient::Object> *list =
+ reinterpret_cast<std::list<librados::TestRadosClient::Object> *>(ctx);
+ if (!list->empty()) {
+ list->pop_front();
+ }
+ if (list->empty()) {
+ return -ENOENT;
+ }
+
+ librados::TestRadosClient::Object &obj = list->front();
+ if (entry != NULL) {
+ *entry = obj.oid.c_str();
+ }
+ if (key != NULL) {
+ *key = obj.locator.c_str();
+ }
+ if (nspace != NULL) {
+ *nspace = obj.nspace.c_str();
+ }
+ return 0;
+}
+
+extern "C" void rados_nobjects_list_close(rados_list_ctx_t ctx) {
+ std::list<librados::TestRadosClient::Object> *list =
+ reinterpret_cast<std::list<librados::TestRadosClient::Object> *>(ctx);
+ delete list;
+}
+
+extern "C" int rados_pool_create(rados_t cluster, const char *pool_name) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ return client->pool_create(pool_name);
+}
+
+extern "C" int rados_pool_delete(rados_t cluster, const char *pool_name) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ return client->pool_delete(pool_name);
+}
+
+extern "C" void rados_shutdown(rados_t cluster) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ client->put();
+}
+
+extern "C" int rados_wait_for_latest_osdmap(rados_t cluster) {
+ librados::TestRadosClient *client =
+ reinterpret_cast<librados::TestRadosClient*>(cluster);
+ return client->wait_for_latest_osdmap();
+}
+
+using namespace std::placeholders;
+
+namespace librados {
+
+AioCompletion::~AioCompletion()
+{
+ auto c = reinterpret_cast<AioCompletionImpl *>(pc);
+ c->release();
+}
+
+void AioCompletion::release() {
+ delete this;
+}
+
+IoCtx::IoCtx() : io_ctx_impl(NULL) {
+}
+
+IoCtx::~IoCtx() {
+ close();
+}
+
+IoCtx::IoCtx(const IoCtx& rhs) {
+ io_ctx_impl = rhs.io_ctx_impl;
+ if (io_ctx_impl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->get();
+ }
+}
+
+IoCtx::IoCtx(IoCtx&& rhs) noexcept : io_ctx_impl(std::exchange(rhs.io_ctx_impl, nullptr))
+{
+}
+
+IoCtx& IoCtx::operator=(const IoCtx& rhs) {
+ if (io_ctx_impl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->put();
+ }
+
+ io_ctx_impl = rhs.io_ctx_impl;
+ if (io_ctx_impl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->get();
+ }
+ return *this;
+}
+
+librados::IoCtx& librados::IoCtx::operator=(IoCtx&& rhs) noexcept
+{
+ if (io_ctx_impl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->put();
+ }
+
+ io_ctx_impl = std::exchange(rhs.io_ctx_impl, nullptr);
+ return *this;
+}
+
+int IoCtx::aio_flush() {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->aio_flush();
+ return 0;
+}
+
+int IoCtx::aio_flush_async(AioCompletion *c) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->aio_flush_async(c->pc);
+ return 0;
+}
+
+int IoCtx::aio_notify(const std::string& oid, AioCompletion *c, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->aio_notify(oid, c->pc, bl, timeout_ms, pbl);
+ return 0;
+}
+
+int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectReadOperation *op, bufferlist *pbl) {
+ return aio_operate(oid, c, op, 0, pbl);
+}
+
+int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectReadOperation *op, int flags,
+ bufferlist *pbl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ TestObjectOperationImpl *ops = reinterpret_cast<TestObjectOperationImpl*>(op->impl);
+ return ctx->aio_operate_read(oid, *ops, c->pc, flags, pbl,
+ ctx->get_snap_read(), nullptr);
+}
+
+int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectReadOperation *op, int flags,
+ bufferlist *pbl, const blkin_trace_info *trace_info) {
+ return aio_operate(oid, c, op, flags, pbl);
+}
+
+int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectWriteOperation *op) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ TestObjectOperationImpl *ops = reinterpret_cast<TestObjectOperationImpl*>(op->impl);
+ return ctx->aio_operate(oid, *ops, c->pc, nullptr, nullptr, 0);
+}
+
+int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectWriteOperation *op, snap_t seq,
+ std::vector<snap_t>& snaps, int flags,
+ const blkin_trace_info *trace_info) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ TestObjectOperationImpl *ops = reinterpret_cast<TestObjectOperationImpl*>(op->impl);
+
+ std::vector<snapid_t> snv;
+ snv.resize(snaps.size());
+ for (size_t i = 0; i < snaps.size(); ++i)
+ snv[i] = snaps[i];
+ SnapContext snapc(seq, snv);
+
+ return ctx->aio_operate(oid, *ops, c->pc, &snapc, nullptr, flags);
+}
+
+int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectWriteOperation *op, snap_t seq,
+ std::vector<snap_t>& snaps) {
+ return aio_operate(oid, c, op, seq, snaps, 0, nullptr);
+}
+
+int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+ ObjectWriteOperation *op, snap_t seq,
+ std::vector<snap_t>& snaps,
+ const blkin_trace_info *trace_info) {
+ return aio_operate(oid, c, op, seq, snaps, 0, trace_info);
+}
+
+int IoCtx::aio_remove(const std::string& oid, AioCompletion *c) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->aio_remove(oid, c->pc);
+}
+
+int IoCtx::aio_remove(const std::string& oid, AioCompletion *c, int flags) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->aio_remove(oid, c->pc, flags);
+}
+
+int IoCtx::aio_watch(const std::string& o, AioCompletion *c, uint64_t *handle,
+ librados::WatchCtx2 *watch_ctx) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->aio_watch(o, c->pc, handle, watch_ctx);
+}
+
+int IoCtx::aio_unwatch(uint64_t handle, AioCompletion *c) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->aio_unwatch(handle, c->pc);
+}
+
+config_t IoCtx::cct() {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return reinterpret_cast<config_t>(ctx->get_rados_client()->cct());
+}
+
+void IoCtx::close() {
+ if (io_ctx_impl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->put();
+ }
+ io_ctx_impl = NULL;
+}
+
+int IoCtx::create(const std::string& oid, bool exclusive) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::create, _1, _2, exclusive,
+ ctx->get_snap_context()));
+}
+
+void IoCtx::dup(const IoCtx& rhs) {
+ close();
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(rhs.io_ctx_impl);
+ io_ctx_impl = reinterpret_cast<IoCtxImpl*>(ctx->clone());
+}
+
+int IoCtx::exec(const std::string& oid, const char *cls, const char *method,
+ bufferlist& inbl, bufferlist& outbl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::exec, _1, _2,
+ librados_test_stub::get_class_handler(), cls,
+ method, inbl, &outbl, ctx->get_snap_read(),
+ ctx->get_snap_context()));
+}
+
+void IoCtx::from_rados_ioctx_t(rados_ioctx_t p, IoCtx &io) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(p);
+ ctx->get();
+
+ io.close();
+ io.io_ctx_impl = reinterpret_cast<IoCtxImpl*>(ctx);
+}
+
+uint64_t IoCtx::get_instance_id() const {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->get_instance_id();
+}
+
+int64_t IoCtx::get_id() {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->get_id();
+}
+
+uint64_t IoCtx::get_last_version() {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->get_last_version();
+}
+
+std::string IoCtx::get_pool_name() {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->get_pool_name();
+}
+
+int IoCtx::list_snaps(const std::string& o, snap_set_t *out_snaps) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ o, std::bind(&TestIoCtxImpl::list_snaps, _1, _2, out_snaps));
+}
+
+int IoCtx::list_watchers(const std::string& o,
+ std::list<obj_watch_t> *out_watchers) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ o, std::bind(&TestIoCtxImpl::list_watchers, _1, _2, out_watchers));
+}
+
+int IoCtx::notify(const std::string& o, uint64_t ver, bufferlist& bl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->notify(o, bl, 0, NULL);
+}
+
+int IoCtx::notify2(const std::string& o, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->notify(o, bl, timeout_ms, pbl);
+}
+
+void IoCtx::notify_ack(const std::string& o, uint64_t notify_id,
+ uint64_t handle, bufferlist& bl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->notify_ack(o, notify_id, handle, bl);
+}
+
+int IoCtx::omap_get_vals(const std::string& oid,
+ const std::string& start_after,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::omap_get_vals, _1, _2, start_after, "",
+ max_return, out_vals));
+}
+
+int IoCtx::operate(const std::string& oid, ObjectWriteOperation *op) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ TestObjectOperationImpl *ops = reinterpret_cast<TestObjectOperationImpl*>(op->impl);
+ return ctx->operate(oid, *ops);
+}
+
+int IoCtx::operate(const std::string& oid, ObjectReadOperation *op,
+ bufferlist *pbl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ TestObjectOperationImpl *ops = reinterpret_cast<TestObjectOperationImpl*>(op->impl);
+ return ctx->operate_read(oid, *ops, pbl);
+}
+
+int IoCtx::read(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::read, _1, _2, len, off, &bl,
+ ctx->get_snap_read(), nullptr));
+}
+
+int IoCtx::remove(const std::string& oid) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::remove, _1, _2, ctx->get_snap_context()));
+}
+
+int IoCtx::selfmanaged_snap_create(uint64_t *snapid) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->selfmanaged_snap_create(snapid);
+}
+
+void IoCtx::aio_selfmanaged_snap_create(uint64_t *snapid, AioCompletion* c) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->aio_selfmanaged_snap_create(snapid, c->pc);
+}
+
+int IoCtx::selfmanaged_snap_remove(uint64_t snapid) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->selfmanaged_snap_remove(snapid);
+}
+
+void IoCtx::aio_selfmanaged_snap_remove(uint64_t snapid, AioCompletion* c) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->aio_selfmanaged_snap_remove(snapid, c->pc);
+}
+
+int IoCtx::selfmanaged_snap_rollback(const std::string& oid,
+ uint64_t snapid) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->selfmanaged_snap_rollback(oid, snapid);
+}
+
+int IoCtx::selfmanaged_snap_set_write_ctx(snap_t seq,
+ std::vector<snap_t>& snaps) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->selfmanaged_snap_set_write_ctx(seq, snaps);
+}
+
+void IoCtx::snap_set_read(snap_t seq) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->set_snap_read(seq);
+}
+
+int IoCtx::sparse_read(const std::string& oid, std::map<uint64_t,uint64_t>& m,
+ bufferlist& bl, size_t len, uint64_t off) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::sparse_read, _1, _2, off, len, &m, &bl,
+ ctx->get_snap_read()));
+}
+
+int IoCtx::stat(const std::string& oid, uint64_t *psize, time_t *pmtime) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::stat, _1, _2, psize, pmtime));
+}
+
+int IoCtx::tmap_update(const std::string& oid, bufferlist& cmdbl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::tmap_update, _1, _2, cmdbl));
+}
+
+int IoCtx::trunc(const std::string& oid, uint64_t off) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::truncate, _1, _2, off,
+ ctx->get_snap_context()));
+}
+
+int IoCtx::unwatch2(uint64_t handle) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->unwatch(handle);
+}
+
+int IoCtx::unwatch(const std::string& o, uint64_t handle) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->unwatch(handle);
+}
+
+int IoCtx::watch(const std::string& o, uint64_t ver, uint64_t *handle,
+ librados::WatchCtx *wctx) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->watch(o, handle, wctx, NULL);
+}
+
+int IoCtx::watch2(const std::string& o, uint64_t *handle,
+ librados::WatchCtx2 *wctx) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->watch(o, handle, NULL, wctx);
+}
+
+int IoCtx::write(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::write, _1, _2, bl, len, off,
+ ctx->get_snap_context()));
+}
+
+int IoCtx::write_full(const std::string& oid, bufferlist& bl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::write_full, _1, _2, bl,
+ ctx->get_snap_context()));
+}
+
+int IoCtx::writesame(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::writesame, _1, _2, bl, len, off,
+ ctx->get_snap_context()));
+}
+
+int IoCtx::cmpext(const std::string& oid, uint64_t off, bufferlist& cmp_bl) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->execute_operation(
+ oid, std::bind(&TestIoCtxImpl::cmpext, _1, _2, off, cmp_bl,
+ ctx->get_snap_read()));
+}
+
+int IoCtx::application_enable(const std::string& app_name, bool force) {
+ return 0;
+}
+
+int IoCtx::application_enable_async(const std::string& app_name,
+ bool force, PoolAsyncCompletion *c) {
+ return -EOPNOTSUPP;
+}
+
+int IoCtx::application_list(std::set<std::string> *app_names) {
+ return -EOPNOTSUPP;
+}
+
+int IoCtx::application_metadata_get(const std::string& app_name,
+ const std::string &key,
+ std::string *value) {
+ return -EOPNOTSUPP;
+}
+
+int IoCtx::application_metadata_set(const std::string& app_name,
+ const std::string &key,
+ const std::string& value) {
+ return -EOPNOTSUPP;
+}
+
+int IoCtx::application_metadata_remove(const std::string& app_name,
+ const std::string &key) {
+ return -EOPNOTSUPP;
+}
+
+int IoCtx::application_metadata_list(const std::string& app_name,
+ std::map<std::string, std::string> *values) {
+ return -EOPNOTSUPP;
+}
+
+void IoCtx::set_namespace(const std::string& nspace) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ ctx->set_namespace(nspace);
+}
+
+std::string IoCtx::get_namespace() const {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
+ return ctx->get_namespace();
+}
+
+void IoCtx::set_pool_full_try() {
+}
+
+bool IoCtx::get_pool_full_try() {
+ return false;
+}
+
+static int save_operation_result(int result, int *pval) {
+ if (pval != NULL) {
+ *pval = result;
+ }
+ return result;
+}
+
+ObjectOperation::ObjectOperation() {
+ TestObjectOperationImpl *o = new TestObjectOperationImpl();
+ o->get();
+ impl = reinterpret_cast<ObjectOperationImpl*>(o);
+}
+
+ObjectOperation::~ObjectOperation() {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ if (o) {
+ o->put();
+ o = NULL;
+ }
+}
+
+void ObjectOperation::assert_exists() {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::assert_exists, _1, _2, _4));
+}
+
+void ObjectOperation::assert_version(uint64_t ver) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::assert_version, _1, _2, ver));
+}
+
+void ObjectOperation::exec(const char *cls, const char *method,
+ bufferlist& inbl) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::exec, _1, _2,
+ librados_test_stub::get_class_handler(), cls,
+ method, inbl, _3, _4, _5));
+}
+
+void ObjectOperation::set_op_flags2(int flags) {
+}
+
+size_t ObjectOperation::size() {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ return o->ops.size();
+}
+
+void ObjectOperation::cmpext(uint64_t off, const bufferlist& cmp_bl,
+ int *prval) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ ObjectOperationTestImpl op = std::bind(&TestIoCtxImpl::cmpext, _1, _2, off,
+ cmp_bl, _4);
+ if (prval != NULL) {
+ op = std::bind(save_operation_result,
+ std::bind(op, _1, _2, _3, _4, _5, _6), prval);
+ }
+ o->ops.push_back(op);
+}
+
+void ObjectReadOperation::list_snaps(snap_set_t *out_snaps, int *prval) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+
+ ObjectOperationTestImpl op = std::bind(&TestIoCtxImpl::list_snaps, _1, _2,
+ out_snaps);
+ if (prval != NULL) {
+ op = std::bind(save_operation_result,
+ std::bind(op, _1, _2, _3, _4, _5, _6), prval);
+ }
+ o->ops.push_back(op);
+}
+
+void ObjectReadOperation::list_watchers(std::list<obj_watch_t> *out_watchers,
+ int *prval) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+
+ ObjectOperationTestImpl op = std::bind(&TestIoCtxImpl::list_watchers, _1,
+ _2, out_watchers);
+ if (prval != NULL) {
+ op = std::bind(save_operation_result,
+ std::bind(op, _1, _2, _3, _4, _5, _6), prval);
+ }
+ o->ops.push_back(op);
+}
+
+void ObjectReadOperation::read(size_t off, uint64_t len, bufferlist *pbl,
+ int *prval) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+
+ ObjectOperationTestImpl op;
+ if (pbl != NULL) {
+ op = std::bind(&TestIoCtxImpl::read, _1, _2, len, off, pbl, _4, nullptr);
+ } else {
+ op = std::bind(&TestIoCtxImpl::read, _1, _2, len, off, _3, _4, nullptr);
+ }
+
+ if (prval != NULL) {
+ op = std::bind(save_operation_result,
+ std::bind(op, _1, _2, _3, _4, _5, _6), prval);
+ }
+ o->ops.push_back(op);
+}
+
+void ObjectReadOperation::sparse_read(uint64_t off, uint64_t len,
+ std::map<uint64_t,uint64_t> *m,
+ bufferlist *pbl, int *prval,
+ uint64_t truncate_size,
+ uint32_t truncate_seq) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+
+ ObjectOperationTestImpl op;
+ if (pbl != NULL) {
+ op = std::bind(&TestIoCtxImpl::sparse_read, _1, _2, off, len, m, pbl, _4);
+ } else {
+ op = std::bind(&TestIoCtxImpl::sparse_read, _1, _2, off, len, m, _3, _4);
+ }
+
+ if (prval != NULL) {
+ op = std::bind(save_operation_result,
+ std::bind(op, _1, _2, _3, _4, _5, _6), prval);
+ }
+ o->ops.push_back(op);
+}
+
+void ObjectReadOperation::stat(uint64_t *psize, time_t *pmtime, int *prval) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+
+ ObjectOperationTestImpl op = std::bind(&TestIoCtxImpl::stat, _1, _2,
+ psize, pmtime);
+
+ if (prval != NULL) {
+ op = std::bind(save_operation_result,
+ std::bind(op, _1, _2, _3, _4, _5, _6), prval);
+ }
+ o->ops.push_back(op);
+}
+
+void ObjectWriteOperation::append(const bufferlist &bl) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::append, _1, _2, bl, _5));
+}
+
+void ObjectWriteOperation::create(bool exclusive) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::create, _1, _2, exclusive, _5));
+}
+
+void ObjectWriteOperation::omap_set(const std::map<std::string, bufferlist> &map) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::omap_set, _1, _2, boost::ref(map)));
+}
+
+void ObjectWriteOperation::remove() {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::remove, _1, _2, _5));
+}
+
+void ObjectWriteOperation::selfmanaged_snap_rollback(uint64_t snapid) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::selfmanaged_snap_rollback,
+ _1, _2, snapid));
+}
+
+void ObjectWriteOperation::set_alloc_hint(uint64_t expected_object_size,
+ uint64_t expected_write_size) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::set_alloc_hint, _1, _2,
+ expected_object_size, expected_write_size, 0,
+ _5));
+}
+
+void ObjectWriteOperation::set_alloc_hint2(uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::set_alloc_hint, _1, _2,
+ expected_object_size, expected_write_size, flags,
+ _5));
+}
+
+void ObjectWriteOperation::tmap_update(const bufferlist& cmdbl) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::tmap_update, _1, _2,
+ cmdbl));
+}
+
+void ObjectWriteOperation::truncate(uint64_t off) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::truncate, _1, _2, off, _5));
+}
+
+void ObjectWriteOperation::write(uint64_t off, const bufferlist& bl) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::write, _1, _2, bl, bl.length(),
+ off, _5));
+}
+
+void ObjectWriteOperation::write_full(const bufferlist& bl) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::write_full, _1, _2, bl, _5));
+}
+
+void ObjectWriteOperation::writesame(uint64_t off, uint64_t len,
+ const bufferlist& bl) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::writesame, _1, _2, bl, len,
+ off, _5));
+}
+
+void ObjectWriteOperation::zero(uint64_t off, uint64_t len) {
+ TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
+ o->ops.push_back(std::bind(&TestIoCtxImpl::zero, _1, _2, off, len, _5));
+}
+
+Rados::Rados() : client(NULL) {
+}
+
+Rados::Rados(IoCtx& ioctx) {
+ TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(ioctx.io_ctx_impl);
+ TestRadosClient *impl = ctx->get_rados_client();
+ impl->get();
+
+ client = reinterpret_cast<RadosClient*>(impl);
+ ceph_assert(client != NULL);
+}
+
+Rados::~Rados() {
+ shutdown();
+}
+
+void Rados::from_rados_t(rados_t p, Rados &rados) {
+ if (rados.client != nullptr) {
+ reinterpret_cast<TestRadosClient*>(rados.client)->put();
+ rados.client = nullptr;
+ }
+
+ auto impl = reinterpret_cast<TestRadosClient*>(p);
+ if (impl) {
+ impl->get();
+ rados.client = reinterpret_cast<RadosClient*>(impl);
+ }
+}
+
+AioCompletion *Rados::aio_create_completion(void *cb_arg,
+ callback_t cb_complete) {
+ AioCompletionImpl *c;
+ int r = rados_aio_create_completion2(cb_arg, cb_complete,
+ reinterpret_cast<void**>(&c));
+ ceph_assert(r == 0);
+ return new AioCompletion(c);
+}
+
+int Rados::aio_watch_flush(AioCompletion* c) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->aio_watch_flush(c->pc);
+}
+
+int Rados::blocklist_add(const std::string& client_address,
+ uint32_t expire_seconds) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->blocklist_add(client_address, expire_seconds);
+}
+
+config_t Rados::cct() {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return reinterpret_cast<config_t>(impl->cct());
+}
+
+int Rados::cluster_fsid(std::string* fsid) {
+ *fsid = "00000000-1111-2222-3333-444444444444";
+ return 0;
+}
+
+int Rados::conf_set(const char *option, const char *value) {
+ return rados_conf_set(reinterpret_cast<rados_t>(client), option, value);
+}
+
+int Rados::conf_get(const char *option, std::string &val) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ CephContext *cct = impl->cct();
+
+ char *str = NULL;
+ int ret = cct->_conf.get_val(option, &str, -1);
+ if (ret != 0) {
+ free(str);
+ return ret;
+ }
+
+ val = str;
+ free(str);
+ return 0;
+}
+
+int Rados::conf_parse_env(const char *env) const {
+ return rados_conf_parse_env(reinterpret_cast<rados_t>(client), env);
+}
+
+int Rados::conf_read_file(const char * const path) const {
+ return rados_conf_read_file(reinterpret_cast<rados_t>(client), path);
+}
+
+int Rados::connect() {
+ return rados_connect(reinterpret_cast<rados_t>(client));
+}
+
+uint64_t Rados::get_instance_id() {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->get_instance_id();
+}
+
+int Rados::get_min_compatible_osd(int8_t* require_osd_release) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->get_min_compatible_osd(require_osd_release);
+}
+
+int Rados::get_min_compatible_client(int8_t* min_compat_client,
+ int8_t* require_min_compat_client) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->get_min_compatible_client(min_compat_client,
+ require_min_compat_client);
+}
+
+int Rados::init(const char * const id) {
+ return rados_create(reinterpret_cast<rados_t *>(&client), id);
+}
+
+int Rados::init_with_context(config_t cct_) {
+ return rados_create_with_context(reinterpret_cast<rados_t *>(&client), cct_);
+}
+
+int Rados::ioctx_create(const char *name, IoCtx &io) {
+ rados_ioctx_t p;
+ int ret = rados_ioctx_create(reinterpret_cast<rados_t>(client), name, &p);
+ if (ret) {
+ return ret;
+ }
+
+ io.close();
+ io.io_ctx_impl = reinterpret_cast<IoCtxImpl*>(p);
+ return 0;
+}
+
+int Rados::ioctx_create2(int64_t pool_id, IoCtx &io)
+{
+ rados_ioctx_t p;
+ int ret = rados_ioctx_create2(reinterpret_cast<rados_t>(client), pool_id, &p);
+ if (ret) {
+ return ret;
+ }
+
+ io.close();
+ io.io_ctx_impl = reinterpret_cast<IoCtxImpl*>(p);
+ return 0;
+}
+
+int Rados::mon_command(std::string cmd, const bufferlist& inbl,
+ bufferlist *outbl, std::string *outs) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+
+ std::vector<std::string> cmds;
+ cmds.push_back(cmd);
+ return impl->mon_command(cmds, inbl, outbl, outs);
+}
+
+int Rados::service_daemon_register(const std::string& service,
+ const std::string& name,
+ const std::map<std::string,std::string>& metadata) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->service_daemon_register(service, name, metadata);
+}
+
+int Rados::service_daemon_update_status(std::map<std::string,std::string>&& status) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->service_daemon_update_status(std::move(status));
+}
+
+int Rados::pool_create(const char *name) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->pool_create(name);
+}
+
+int Rados::pool_delete(const char *name) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->pool_delete(name);
+}
+
+int Rados::pool_get_base_tier(int64_t pool, int64_t* base_tier) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->pool_get_base_tier(pool, base_tier);
+}
+
+int Rados::pool_list(std::list<std::string>& v) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ std::list<std::pair<int64_t, std::string> > pools;
+ int r = impl->pool_list(pools);
+ if (r < 0) {
+ return r;
+ }
+
+ v.clear();
+ for (std::list<std::pair<int64_t, std::string> >::iterator it = pools.begin();
+ it != pools.end(); ++it) {
+ v.push_back(it->second);
+ }
+ return 0;
+}
+
+int Rados::pool_list2(std::list<std::pair<int64_t, std::string> >& v)
+{
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->pool_list(v);
+}
+
+int64_t Rados::pool_lookup(const char *name) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->pool_lookup(name);
+}
+
+int Rados::pool_reverse_lookup(int64_t id, std::string *name) {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->pool_reverse_lookup(id, name);
+}
+
+void Rados::shutdown() {
+ if (client == NULL) {
+ return;
+ }
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ impl->put();
+ client = NULL;
+}
+
+void Rados::test_blocklist_self(bool set) {
+}
+
+int Rados::wait_for_latest_osdmap() {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->wait_for_latest_osdmap();
+}
+
+int Rados::watch_flush() {
+ TestRadosClient *impl = reinterpret_cast<TestRadosClient*>(client);
+ return impl->watch_flush();
+}
+
+WatchCtx::~WatchCtx() {
+}
+
+WatchCtx2::~WatchCtx2() {
+}
+
+} // namespace librados
+
+int cls_cxx_create(cls_method_context_t hctx, bool exclusive) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->create(ctx->oid, exclusive, ctx->snapc);
+}
+
+int cls_cxx_remove(cls_method_context_t hctx) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->remove(ctx->oid, ctx->io_ctx_impl->get_snap_context());
+}
+
+int cls_get_request_origin(cls_method_context_t hctx, entity_inst_t *origin) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+
+ librados::TestRadosClient *rados_client =
+ ctx->io_ctx_impl->get_rados_client();
+
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr);
+
+ entity_addr_t entity_addr(entity_addr_t::TYPE_DEFAULT,
+ rados_client->get_nonce());
+ entity_addr.in4_addr() = sin;
+
+ *origin = entity_inst_t(
+ entity_name_t::CLIENT(rados_client->get_instance_id()),
+ entity_addr);
+ return 0;
+}
+
+int cls_cxx_getxattr(cls_method_context_t hctx, const char *name,
+ bufferlist *outbl) {
+ std::map<string, bufferlist> attrs;
+ int r = cls_cxx_getxattrs(hctx, &attrs);
+ if (r < 0) {
+ return r;
+ }
+
+ std::map<string, bufferlist>::iterator it = attrs.find(name);
+ if (it == attrs.end()) {
+ return -ENODATA;
+ }
+ *outbl = it->second;
+ return 0;
+}
+
+int cls_cxx_getxattrs(cls_method_context_t hctx, std::map<string, bufferlist> *attrset) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->xattr_get(ctx->oid, attrset);
+}
+
+int cls_cxx_map_get_keys(cls_method_context_t hctx, const string &start_obj,
+ uint64_t max_to_get, std::set<string> *keys, bool *more) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+
+ keys->clear();
+ std::map<string, bufferlist> vals;
+ int r = ctx->io_ctx_impl->omap_get_vals2(ctx->oid, start_obj, "", max_to_get,
+ &vals, more);
+ if (r < 0) {
+ return r;
+ }
+
+ for (std::map<string, bufferlist>::iterator it = vals.begin();
+ it != vals.end(); ++it) {
+ keys->insert(it->first);
+ }
+ return keys->size();
+}
+
+int cls_cxx_map_get_val(cls_method_context_t hctx, const string &key,
+ bufferlist *outbl) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+
+ std::map<string, bufferlist> vals;
+ int r = ctx->io_ctx_impl->omap_get_vals(ctx->oid, "", key, 1024, &vals);
+ if (r < 0) {
+ return r;
+ }
+
+ std::map<string, bufferlist>::iterator it = vals.find(key);
+ if (it == vals.end()) {
+ return -ENOENT;
+ }
+
+ *outbl = it->second;
+ return 0;
+}
+
+int cls_cxx_map_get_vals(cls_method_context_t hctx, const string &start_obj,
+ const string &filter_prefix, uint64_t max_to_get,
+ std::map<string, bufferlist> *vals, bool *more) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ int r = ctx->io_ctx_impl->omap_get_vals2(ctx->oid, start_obj, filter_prefix,
+ max_to_get, vals, more);
+ if (r < 0) {
+ return r;
+ }
+ return vals->size();
+}
+
+int cls_cxx_map_remove_key(cls_method_context_t hctx, const string &key) {
+ std::set<std::string> keys;
+ keys.insert(key);
+
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->omap_rm_keys(ctx->oid, keys);
+}
+
+int cls_cxx_map_set_val(cls_method_context_t hctx, const string &key,
+ bufferlist *inbl) {
+ std::map<std::string, bufferlist> m;
+ m[key] = *inbl;
+ return cls_cxx_map_set_vals(hctx, &m);
+}
+
+int cls_cxx_map_set_vals(cls_method_context_t hctx,
+ const std::map<string, bufferlist> *map) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->omap_set(ctx->oid, *map);
+}
+
+int cls_cxx_read(cls_method_context_t hctx, int ofs, int len,
+ bufferlist *outbl) {
+ return cls_cxx_read2(hctx, ofs, len, outbl, 0);
+}
+
+int cls_cxx_read2(cls_method_context_t hctx, int ofs, int len,
+ bufferlist *outbl, uint32_t op_flags) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->read(
+ ctx->oid, len, ofs, outbl, ctx->snap_id, nullptr);
+}
+
+int cls_cxx_setxattr(cls_method_context_t hctx, const char *name,
+ bufferlist *inbl) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->xattr_set(ctx->oid, name, *inbl);
+}
+
+int cls_cxx_stat(cls_method_context_t hctx, uint64_t *size, time_t *mtime) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->stat(ctx->oid, size, mtime);
+}
+
+int cls_cxx_write(cls_method_context_t hctx, int ofs, int len,
+ bufferlist *inbl) {
+ return cls_cxx_write2(hctx, ofs, len, inbl, 0);
+}
+
+int cls_cxx_write2(cls_method_context_t hctx, int ofs, int len,
+ bufferlist *inbl, uint32_t op_flags) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->write(ctx->oid, *inbl, len, ofs, ctx->snapc);
+}
+
+int cls_cxx_write_full(cls_method_context_t hctx, bufferlist *inbl) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->write_full(ctx->oid, *inbl, ctx->snapc);
+}
+
+int cls_cxx_replace(cls_method_context_t hctx, int ofs, int len,
+ bufferlist *inbl) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ int r = ctx->io_ctx_impl->truncate(ctx->oid, 0, ctx->snapc);
+ if (r < 0) {
+ return r;
+ }
+ return ctx->io_ctx_impl->write(ctx->oid, *inbl, len, ofs, ctx->snapc);
+}
+
+int cls_cxx_truncate(cls_method_context_t hctx, int ofs) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->truncate(ctx->oid, ofs, ctx->snapc);
+}
+
+int cls_cxx_write_zero(cls_method_context_t hctx, int ofs, int len) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ return ctx->io_ctx_impl->zero(ctx->oid, len, ofs, ctx->snapc);
+}
+
+int cls_cxx_list_watchers(cls_method_context_t hctx,
+ obj_list_watch_response_t *watchers) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+
+ std::list<obj_watch_t> obj_watchers;
+ int r = ctx->io_ctx_impl->list_watchers(ctx->oid, &obj_watchers);
+ if (r < 0) {
+ return r;
+ }
+
+ for (auto &w : obj_watchers) {
+ watch_item_t watcher;
+ watcher.name = entity_name_t::CLIENT(w.watcher_id);
+ watcher.cookie = w.cookie;
+ watcher.timeout_seconds = w.timeout_seconds;
+ watcher.addr.parse(w.addr);
+ watchers->entries.push_back(watcher);
+ }
+
+ return 0;
+}
+
+uint64_t cls_get_features(cls_method_context_t hctx) {
+ return CEPH_FEATURES_SUPPORTED_DEFAULT;
+}
+
+uint64_t cls_get_client_features(cls_method_context_t hctx) {
+ return CEPH_FEATURES_SUPPORTED_DEFAULT;
+}
+
+int cls_get_snapset_seq(cls_method_context_t hctx, uint64_t *snap_seq) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ librados::snap_set_t snapset;
+ int r = ctx->io_ctx_impl->list_snaps(ctx->oid, &snapset);
+ if (r < 0) {
+ return r;
+ }
+
+ *snap_seq = snapset.seq;
+ return 0;
+}
+
+int cls_log(int level, const char *format, ...) {
+ int size = 256;
+ va_list ap;
+ while (1) {
+ char buf[size];
+ va_start(ap, format);
+ int n = vsnprintf(buf, size, format, ap);
+ va_end(ap);
+ if ((n > -1 && n < size) || size > 8196) {
+ dout(ceph::dout::need_dynamic(level)) << buf << dendl;
+ return n;
+ }
+ size *= 2;
+ }
+ return 0;
+}
+
+int cls_register(const char *name, cls_handle_t *handle) {
+ librados::TestClassHandler *cls = librados_test_stub::get_class_handler();
+ return cls->create(name, handle);
+}
+
+int cls_register_cxx_method(cls_handle_t hclass, const char *method,
+ int flags,
+ cls_method_cxx_call_t class_call,
+ cls_method_handle_t *handle) {
+ librados::TestClassHandler *cls = librados_test_stub::get_class_handler();
+ return cls->create_method(hclass, method, class_call, handle);
+}
+
+int cls_register_cxx_filter(cls_handle_t hclass,
+ const std::string &filter_name,
+ cls_cxx_filter_factory_t fn,
+ cls_filter_handle_t *)
+{
+ librados::TestClassHandler *cls = librados_test_stub::get_class_handler();
+ return cls->create_filter(hclass, filter_name, fn);
+}
+
+ceph_release_t cls_get_required_osd_release(cls_handle_t hclass) {
+ return ceph_release_t::nautilus;
+}
+
+ceph_release_t cls_get_min_compatible_client(cls_handle_t hclass) {
+ return ceph_release_t::nautilus;
+}
+
+// stubs to silence TestClassHandler::open_class()
+PGLSFilter::~PGLSFilter()
+{}
+
+int cls_gen_rand_base64(char *, int) {
+ return -ENOTSUP;
+}
+
+int cls_cxx_chunk_write_and_set(cls_method_handle_t, int,
+ int, bufferlist *,
+ uint32_t, bufferlist *, int) {
+ return -ENOTSUP;
+}
+
+int cls_cxx_map_read_header(cls_method_handle_t, bufferlist *) {
+ return -ENOTSUP;
+}
+
+uint64_t cls_get_osd_min_alloc_size(cls_method_context_t hctx) {
+ return 0;
+}
+
+uint64_t cls_get_pool_stripe_width(cls_method_context_t hctx) {
+ return 0;
+}
diff --git a/src/test/librados_test_stub/LibradosTestStub.h b/src/test/librados_test_stub/LibradosTestStub.h
new file mode 100644
index 000000000..5d335f488
--- /dev/null
+++ b/src/test/librados_test_stub/LibradosTestStub.h
@@ -0,0 +1,42 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef LIBRADOS_TEST_STUB_H
+#define LIBRADOS_TEST_STUB_H
+
+#include "include/rados/librados_fwd.hpp"
+#include <boost/shared_ptr.hpp>
+
+namespace neorados {
+struct IOContext;
+struct RADOS;
+} // namespace neorados
+
+namespace librados {
+
+class MockTestMemIoCtxImpl;
+class MockTestMemRadosClient;
+class TestCluster;
+class TestClassHandler;
+
+MockTestMemIoCtxImpl &get_mock_io_ctx(IoCtx &ioctx);
+MockTestMemIoCtxImpl &get_mock_io_ctx(neorados::RADOS& rados,
+ neorados::IOContext& io_context);
+
+MockTestMemRadosClient &get_mock_rados_client(neorados::RADOS& rados);
+
+} // namespace librados
+
+namespace librados_test_stub {
+
+typedef boost::shared_ptr<librados::TestCluster> TestClusterRef;
+
+void set_cluster(TestClusterRef cluster);
+TestClusterRef get_cluster();
+
+librados::TestClassHandler* get_class_handler();
+
+} // namespace librados_test_stub
+
+
+#endif // LIBRADOS_TEST_STUB_H
diff --git a/src/test/librados_test_stub/MockTestMemCluster.h b/src/test/librados_test_stub/MockTestMemCluster.h
new file mode 100644
index 000000000..4a6da0cc0
--- /dev/null
+++ b/src/test/librados_test_stub/MockTestMemCluster.h
@@ -0,0 +1,36 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef LIBRADOS_MOCK_TEST_MEM_CLUSTER_H
+#define LIBRADOS_MOCK_TEST_MEM_CLUSTER_H
+
+#include "include/common_fwd.h"
+#include "test/librados_test_stub/TestMemCluster.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "gmock/gmock.h"
+
+
+namespace librados {
+
+class TestRadosClient;
+
+class MockTestMemCluster : public TestMemCluster {
+public:
+ MockTestMemCluster() {
+ default_to_dispatch();
+ }
+
+ MOCK_METHOD1(create_rados_client, TestRadosClient*(CephContext*));
+ MockTestMemRadosClient* do_create_rados_client(CephContext *cct) {
+ return new ::testing::NiceMock<MockTestMemRadosClient>(cct, this);
+ }
+
+ void default_to_dispatch() {
+ using namespace ::testing;
+ ON_CALL(*this, create_rados_client(_)).WillByDefault(Invoke(this, &MockTestMemCluster::do_create_rados_client));
+ }
+};
+
+} // namespace librados
+
+#endif // LIBRADOS_MOCK_TEST_MEM_CLUSTER_H
diff --git a/src/test/librados_test_stub/MockTestMemIoCtxImpl.h b/src/test/librados_test_stub/MockTestMemIoCtxImpl.h
new file mode 100644
index 000000000..aed431c11
--- /dev/null
+++ b/src/test/librados_test_stub/MockTestMemIoCtxImpl.h
@@ -0,0 +1,252 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef LIBRADOS_TEST_STUB_MOCK_TEST_MEM_IO_CTX_IMPL_H
+#define LIBRADOS_TEST_STUB_MOCK_TEST_MEM_IO_CTX_IMPL_H
+
+#include "test/librados_test_stub/TestMemIoCtxImpl.h"
+#include "test/librados_test_stub/TestMemCluster.h"
+#include "gmock/gmock.h"
+
+namespace librados {
+
+class MockTestMemRadosClient;
+
+class MockTestMemIoCtxImpl : public TestMemIoCtxImpl {
+public:
+ MockTestMemIoCtxImpl(MockTestMemRadosClient *mock_client,
+ TestMemRadosClient *client, int64_t pool_id,
+ const std::string& pool_name,
+ TestMemCluster::Pool *pool)
+ : TestMemIoCtxImpl(client, pool_id, pool_name, pool),
+ m_mock_client(mock_client), m_client(client) {
+ default_to_parent();
+ }
+
+ MockTestMemRadosClient *get_mock_rados_client() {
+ return m_mock_client;
+ }
+
+ MOCK_METHOD0(clone, TestIoCtxImpl*());
+ TestIoCtxImpl *do_clone() {
+ TestIoCtxImpl *io_ctx_impl = new ::testing::NiceMock<MockTestMemIoCtxImpl>(
+ m_mock_client, m_client, get_pool_id(), get_pool_name(), get_pool());
+ io_ctx_impl->set_snap_read(get_snap_read());
+ io_ctx_impl->set_snap_context(get_snap_context());
+ return io_ctx_impl;
+ }
+
+ MOCK_METHOD5(aio_notify, void(const std::string& o, AioCompletionImpl *c,
+ bufferlist& bl, uint64_t timeout_ms,
+ bufferlist *pbl));
+ void do_aio_notify(const std::string& o, AioCompletionImpl *c, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl) {
+ return TestMemIoCtxImpl::aio_notify(o, c, bl, timeout_ms, pbl);
+ }
+
+ MOCK_METHOD6(aio_operate, int(const std::string&, TestObjectOperationImpl&,
+ AioCompletionImpl*, SnapContext*,
+ const ceph::real_time*, int));
+ int do_aio_operate(const std::string& o, TestObjectOperationImpl& ops,
+ AioCompletionImpl* c, SnapContext* snapc,
+ const ceph::real_time* pmtime, int flags) {
+ return TestMemIoCtxImpl::aio_operate(o, ops, c, snapc, pmtime, flags);
+ }
+
+ MOCK_METHOD4(aio_watch, int(const std::string& o, AioCompletionImpl *c,
+ uint64_t *handle, librados::WatchCtx2 *ctx));
+ int do_aio_watch(const std::string& o, AioCompletionImpl *c,
+ uint64_t *handle, librados::WatchCtx2 *ctx) {
+ return TestMemIoCtxImpl::aio_watch(o, c, handle, ctx);
+ }
+
+ MOCK_METHOD2(aio_unwatch, int(uint64_t handle, AioCompletionImpl *c));
+ int do_aio_unwatch(uint64_t handle, AioCompletionImpl *c) {
+ return TestMemIoCtxImpl::aio_unwatch(handle, c);
+ }
+
+ MOCK_METHOD2(assert_exists, int(const std::string &, uint64_t));
+ int do_assert_exists(const std::string &oid, uint64_t snap_id) {
+ return TestMemIoCtxImpl::assert_exists(oid, snap_id);
+ }
+
+ MOCK_METHOD2(assert_version, int(const std::string &, uint64_t));
+ int do_assert_version(const std::string &oid, uint64_t ver) {
+ return TestMemIoCtxImpl::assert_version(oid, ver);
+ }
+
+ MOCK_METHOD3(create, int(const std::string&, bool, const SnapContext &));
+ int do_create(const std::string& oid, bool exclusive,
+ const SnapContext &snapc) {
+ return TestMemIoCtxImpl::create(oid, exclusive, snapc);
+ }
+
+ MOCK_METHOD4(cmpext, int(const std::string&, uint64_t, bufferlist&,
+ uint64_t snap_id));
+ int do_cmpext(const std::string& oid, uint64_t off, bufferlist& cmp_bl,
+ uint64_t snap_id) {
+ return TestMemIoCtxImpl::cmpext(oid, off, cmp_bl, snap_id);
+ }
+
+ MOCK_METHOD8(exec, int(const std::string& oid,
+ TestClassHandler *handler,
+ const char *cls,
+ const char *method,
+ bufferlist& inbl,
+ bufferlist* outbl,
+ uint64_t snap_id,
+ const SnapContext &snapc));
+ int do_exec(const std::string& oid, TestClassHandler *handler,
+ const char *cls, const char *method, bufferlist& inbl,
+ bufferlist* outbl, uint64_t snap_id, const SnapContext &snapc) {
+ return TestMemIoCtxImpl::exec(oid, handler, cls, method, inbl, outbl,
+ snap_id, snapc);
+ }
+
+ MOCK_CONST_METHOD0(get_instance_id, uint64_t());
+ uint64_t do_get_instance_id() const {
+ return TestMemIoCtxImpl::get_instance_id();
+ }
+
+ MOCK_METHOD2(list_snaps, int(const std::string& o, snap_set_t *out_snaps));
+ int do_list_snaps(const std::string& o, snap_set_t *out_snaps) {
+ return TestMemIoCtxImpl::list_snaps(o, out_snaps);
+ }
+
+ MOCK_METHOD2(list_watchers, int(const std::string& o,
+ std::list<obj_watch_t> *out_watchers));
+ int do_list_watchers(const std::string& o,
+ std::list<obj_watch_t> *out_watchers) {
+ return TestMemIoCtxImpl::list_watchers(o, out_watchers);
+ }
+
+ MOCK_METHOD4(notify, int(const std::string& o, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl));
+ int do_notify(const std::string& o, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl) {
+ return TestMemIoCtxImpl::notify(o, bl, timeout_ms, pbl);
+ }
+
+ MOCK_METHOD1(set_snap_read, void(snap_t));
+ void do_set_snap_read(snap_t snap_id) {
+ return TestMemIoCtxImpl::set_snap_read(snap_id);
+ }
+ MOCK_METHOD6(sparse_read, int(const std::string& oid,
+ uint64_t off,
+ uint64_t len,
+ std::map<uint64_t, uint64_t> *m,
+ bufferlist *bl, uint64_t));
+ int do_sparse_read(const std::string& oid, uint64_t off, size_t len,
+ std::map<uint64_t, uint64_t> *m, bufferlist *bl,
+ uint64_t snap_id) {
+ return TestMemIoCtxImpl::sparse_read(oid, off, len, m, bl, snap_id);
+ }
+
+ MOCK_METHOD6(read, int(const std::string& oid,
+ size_t len,
+ uint64_t off,
+ bufferlist *bl, uint64_t snap_id, uint64_t* objver));
+ int do_read(const std::string& oid, size_t len, uint64_t off,
+ bufferlist *bl, uint64_t snap_id, uint64_t* objver) {
+ return TestMemIoCtxImpl::read(oid, len, off, bl, snap_id, objver);
+ }
+
+ MOCK_METHOD2(remove, int(const std::string& oid, const SnapContext &snapc));
+ int do_remove(const std::string& oid, const SnapContext &snapc) {
+ return TestMemIoCtxImpl::remove(oid, snapc);
+ }
+
+ MOCK_METHOD1(selfmanaged_snap_create, int(uint64_t *snap_id));
+ int do_selfmanaged_snap_create(uint64_t *snap_id) {
+ return TestMemIoCtxImpl::selfmanaged_snap_create(snap_id);
+ }
+
+ MOCK_METHOD1(selfmanaged_snap_remove, int(uint64_t snap_id));
+ int do_selfmanaged_snap_remove(uint64_t snap_id) {
+ return TestMemIoCtxImpl::selfmanaged_snap_remove(snap_id);
+ }
+
+ MOCK_METHOD2(selfmanaged_snap_rollback, int(const std::string& oid,
+ uint64_t snap_id));
+ int do_selfmanaged_snap_rollback(const std::string& oid, uint64_t snap_id) {
+ return TestMemIoCtxImpl::selfmanaged_snap_rollback(oid, snap_id);
+ }
+
+ MOCK_METHOD3(truncate, int(const std::string& oid,
+ uint64_t size,
+ const SnapContext &snapc));
+ int do_truncate(const std::string& oid, uint64_t size,
+ const SnapContext &snapc) {
+ return TestMemIoCtxImpl::truncate(oid, size, snapc);
+ }
+
+ MOCK_METHOD5(write, int(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off, const SnapContext &snapc));
+ int do_write(const std::string& oid, bufferlist& bl, size_t len, uint64_t off,
+ const SnapContext &snapc) {
+ return TestMemIoCtxImpl::write(oid, bl, len, off, snapc);
+ }
+
+ MOCK_METHOD3(write_full, int(const std::string& oid,
+ bufferlist& bl,
+ const SnapContext &snapc));
+ int do_write_full(const std::string& oid, bufferlist& bl,
+ const SnapContext &snapc) {
+ return TestMemIoCtxImpl::write_full(oid, bl, snapc);
+ }
+
+
+ MOCK_METHOD5(writesame, int(const std::string& oid, bufferlist& bl,
+ size_t len, uint64_t off,
+ const SnapContext &snapc));
+ int do_writesame(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off, const SnapContext &snapc) {
+ return TestMemIoCtxImpl::writesame(oid, bl, len, off, snapc);
+ }
+
+ MOCK_METHOD4(zero, int(const std::string& oid, uint64_t offset,
+ uint64_t length, const SnapContext &snapc));
+ int do_zero(const std::string& oid, uint64_t offset,
+ uint64_t length, const SnapContext &snapc) {
+ return TestMemIoCtxImpl::zero(oid, offset, length, snapc);
+ }
+
+ void default_to_parent() {
+ using namespace ::testing;
+
+ ON_CALL(*this, clone()).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_clone));
+ ON_CALL(*this, aio_notify(_, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_aio_notify));
+ ON_CALL(*this, aio_operate(_, _, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_aio_operate));
+ ON_CALL(*this, aio_watch(_, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_aio_watch));
+ ON_CALL(*this, aio_unwatch(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_aio_unwatch));
+ ON_CALL(*this, assert_exists(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_assert_exists));
+ ON_CALL(*this, assert_version(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_assert_version));
+ ON_CALL(*this, create(_, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_create));
+ ON_CALL(*this, cmpext(_, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_cmpext));
+ ON_CALL(*this, exec(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_exec));
+ ON_CALL(*this, get_instance_id()).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_get_instance_id));
+ ON_CALL(*this, list_snaps(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_list_snaps));
+ ON_CALL(*this, list_watchers(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_list_watchers));
+ ON_CALL(*this, notify(_, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_notify));
+ ON_CALL(*this, read(_, _, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_read));
+ ON_CALL(*this, set_snap_read(_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_set_snap_read));
+ ON_CALL(*this, sparse_read(_, _, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_sparse_read));
+ ON_CALL(*this, remove(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_remove));
+ ON_CALL(*this, selfmanaged_snap_create(_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_selfmanaged_snap_create));
+ ON_CALL(*this, selfmanaged_snap_remove(_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_selfmanaged_snap_remove));
+ ON_CALL(*this, selfmanaged_snap_rollback(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_selfmanaged_snap_rollback));
+ ON_CALL(*this, truncate(_,_,_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_truncate));
+ ON_CALL(*this, write(_, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_write));
+ ON_CALL(*this, write_full(_, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_write_full));
+ ON_CALL(*this, writesame(_, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_writesame));
+ ON_CALL(*this, zero(_,_,_,_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_zero));
+ }
+
+private:
+ MockTestMemRadosClient *m_mock_client;
+ TestMemRadosClient *m_client;
+};
+
+} // namespace librados
+
+#endif // LIBRADOS_TEST_STUB_MOCK_TEST_MEM_IO_CTX_IMPL_H
diff --git a/src/test/librados_test_stub/MockTestMemRadosClient.h b/src/test/librados_test_stub/MockTestMemRadosClient.h
new file mode 100644
index 000000000..65a1ac82e
--- /dev/null
+++ b/src/test/librados_test_stub/MockTestMemRadosClient.h
@@ -0,0 +1,103 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef LIBRADOS_TEST_STUB_MOCK_TEST_MEM_RADOS_CLIENT_H
+#define LIBRADOS_TEST_STUB_MOCK_TEST_MEM_RADOS_CLIENT_H
+
+#include "test/librados_test_stub/TestMemRadosClient.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "gmock/gmock.h"
+
+namespace librados {
+
+class TestMemCluster;
+
+class MockTestMemRadosClient : public TestMemRadosClient {
+public:
+ MockTestMemRadosClient(CephContext *cct, TestMemCluster *test_mem_cluster)
+ : TestMemRadosClient(cct, test_mem_cluster) {
+ default_to_dispatch();
+ }
+
+ MOCK_METHOD0(connect, int());
+ int do_connect() {
+ return TestMemRadosClient::connect();
+ }
+
+ MOCK_METHOD2(create_ioctx, TestIoCtxImpl *(int64_t pool_id,
+ const std::string &pool_name));
+ MockTestMemIoCtxImpl* do_create_ioctx(int64_t pool_id,
+ const std::string &pool_name) {
+ return new ::testing::NiceMock<MockTestMemIoCtxImpl>(
+ this, this, pool_id, pool_name,
+ get_mem_cluster()->get_pool(pool_name));
+ }
+
+ MOCK_METHOD2(blocklist_add, int(const std::string& client_address,
+ uint32_t expire_seconds));
+ int do_blocklist_add(const std::string& client_address,
+ uint32_t expire_seconds) {
+ return TestMemRadosClient::blocklist_add(client_address, expire_seconds);
+ }
+
+ MOCK_METHOD1(get_min_compatible_osd, int(int8_t*));
+ int do_get_min_compatible_osd(int8_t* require_osd_release) {
+ return TestMemRadosClient::get_min_compatible_osd(require_osd_release);
+ }
+
+ MOCK_METHOD2(get_min_compatible_client, int(int8_t*, int8_t*));
+ int do_get_min_compatible_client(int8_t* min_compat_client,
+ int8_t* require_min_compat_client) {
+ return TestMemRadosClient::get_min_compatible_client(
+ min_compat_client, require_min_compat_client);
+ }
+
+ MOCK_METHOD3(service_daemon_register,
+ int(const std::string&,
+ const std::string&,
+ const std::map<std::string,std::string>&));
+ int do_service_daemon_register(const std::string& service,
+ const std::string& name,
+ const std::map<std::string,std::string>& metadata) {
+ return TestMemRadosClient::service_daemon_register(service, name, metadata);
+ }
+
+ // workaround of https://github.com/google/googletest/issues/1155
+ MOCK_METHOD1(service_daemon_update_status_r,
+ int(const std::map<std::string,std::string>&));
+ int do_service_daemon_update_status_r(const std::map<std::string,std::string>& status) {
+ auto s = status;
+ return TestMemRadosClient::service_daemon_update_status(std::move(s));
+ }
+
+ MOCK_METHOD4(mon_command, int(const std::vector<std::string>&,
+ const bufferlist&, bufferlist*, std::string*));
+ int do_mon_command(const std::vector<std::string>& cmd,
+ const bufferlist &inbl, bufferlist *outbl,
+ std::string *outs) {
+ return mon_command(cmd, inbl, outbl, outs);
+ }
+
+ MOCK_METHOD0(wait_for_latest_osd_map, int());
+ int do_wait_for_latest_osd_map() {
+ return wait_for_latest_osd_map();
+ }
+
+ void default_to_dispatch() {
+ using namespace ::testing;
+
+ ON_CALL(*this, connect()).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_connect));
+ ON_CALL(*this, create_ioctx(_, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_create_ioctx));
+ ON_CALL(*this, blocklist_add(_, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_blocklist_add));
+ ON_CALL(*this, get_min_compatible_osd(_)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_get_min_compatible_osd));
+ ON_CALL(*this, get_min_compatible_client(_, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_get_min_compatible_client));
+ ON_CALL(*this, service_daemon_register(_, _, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_service_daemon_register));
+ ON_CALL(*this, service_daemon_update_status_r(_)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_service_daemon_update_status_r));
+ ON_CALL(*this, mon_command(_, _, _, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_mon_command));
+ ON_CALL(*this, wait_for_latest_osd_map()).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_wait_for_latest_osd_map));
+ }
+};
+
+} // namespace librados
+
+#endif // LIBRADOS_TEST_STUB_MOCK_TEST_MEM_RADOS_CLIENT_H
diff --git a/src/test/librados_test_stub/NeoradosTestStub.cc b/src/test/librados_test_stub/NeoradosTestStub.cc
new file mode 100644
index 000000000..0de2cd902
--- /dev/null
+++ b/src/test/librados_test_stub/NeoradosTestStub.cc
@@ -0,0 +1,601 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/neorados/RADOS.hpp"
+#include "include/rados/librados.hpp"
+#include "common/ceph_mutex.h"
+#include "common/hobject.h"
+#include "librados/AioCompletionImpl.h"
+#include "mon/error_code.h"
+#include "osd/error_code.h"
+#include "osd/osd_types.h"
+#include "osdc/error_code.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "test/librados_test_stub/TestClassHandler.h"
+#include "test/librados_test_stub/TestIoCtxImpl.h"
+#include "test/librados_test_stub/TestRadosClient.h"
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <functional>
+#include <boost/system/system_error.hpp>
+
+namespace bs = boost::system;
+using namespace std::literals;
+using namespace std::placeholders;
+
+namespace neorados {
+namespace detail {
+
+class Client {
+public:
+ ceph::mutex mutex = ceph::make_mutex("NeoradosTestStub::Client");
+
+ librados::TestRadosClient* test_rados_client;
+ boost::asio::io_context& io_context;
+
+ std::map<std::pair<int64_t, std::string>, librados::TestIoCtxImpl*> io_ctxs;
+
+ Client(librados::TestRadosClient* test_rados_client)
+ : test_rados_client(test_rados_client),
+ io_context(test_rados_client->get_io_context()) {
+ }
+
+ ~Client() {
+ for (auto& io_ctx : io_ctxs) {
+ io_ctx.second->put();
+ }
+ }
+
+ librados::TestIoCtxImpl* get_io_ctx(const IOContext& ioc) {
+ int64_t pool_id = ioc.pool();
+ std::string ns = std::string{ioc.ns()};
+
+ auto lock = std::scoped_lock{mutex};
+ auto key = make_pair(pool_id, ns);
+ auto it = io_ctxs.find(key);
+ if (it != io_ctxs.end()) {
+ return it->second;
+ }
+
+ std::list<std::pair<int64_t, std::string>> pools;
+ int r = test_rados_client->pool_list(pools);
+ if (r < 0) {
+ return nullptr;
+ }
+
+ for (auto& pool : pools) {
+ if (pool.first == pool_id) {
+ auto io_ctx = test_rados_client->create_ioctx(pool_id, pool.second);
+ io_ctx->set_namespace(ns);
+ io_ctxs[key] = io_ctx;
+ return io_ctx;
+ }
+ }
+ return nullptr;
+ }
+};
+
+} // namespace detail
+
+namespace {
+
+struct CompletionPayload {
+ std::unique_ptr<Op::Completion> c;
+};
+
+void completion_callback_adapter(rados_completion_t c, void *arg) {
+ auto impl = reinterpret_cast<librados::AioCompletionImpl *>(c);
+ auto r = impl->get_return_value();
+ impl->release();
+
+ auto payload = reinterpret_cast<CompletionPayload*>(arg);
+ payload->c->defer(std::move(payload->c),
+ (r < 0) ? bs::error_code(-r, osd_category()) :
+ bs::error_code());
+ delete payload;
+}
+
+librados::AioCompletionImpl* create_aio_completion(
+ std::unique_ptr<Op::Completion>&& c) {
+ auto payload = new CompletionPayload{std::move(c)};
+
+ auto impl = new librados::AioCompletionImpl();
+ impl->set_complete_callback(payload, completion_callback_adapter);
+
+ return impl;
+}
+
+int save_operation_size(int result, size_t* pval) {
+ if (pval != NULL) {
+ *pval = result;
+ }
+ return result;
+}
+
+int save_operation_ec(int result, boost::system::error_code* ec) {
+ if (ec != NULL) {
+ *ec = {std::abs(result), bs::system_category()};
+ }
+ return result;
+}
+
+} // anonymous namespace
+
+Object::Object() {
+ static_assert(impl_size >= sizeof(object_t));
+ new (&impl) object_t();
+}
+
+Object::Object(std::string&& s) {
+ static_assert(impl_size >= sizeof(object_t));
+ new (&impl) object_t(std::move(s));
+}
+
+Object::~Object() {
+ reinterpret_cast<object_t*>(&impl)->~object_t();
+}
+
+Object::operator std::string_view() const {
+ return std::string_view(reinterpret_cast<const object_t*>(&impl)->name);
+}
+
+struct IOContextImpl {
+ object_locator_t oloc;
+ snapid_t snap_seq = CEPH_NOSNAP;
+ SnapContext snapc;
+};
+
+IOContext::IOContext() {
+ static_assert(impl_size >= sizeof(IOContextImpl));
+ new (&impl) IOContextImpl();
+}
+
+IOContext::IOContext(const IOContext& rhs) {
+ static_assert(impl_size >= sizeof(IOContextImpl));
+ new (&impl) IOContextImpl(*reinterpret_cast<const IOContextImpl*>(&rhs.impl));
+}
+
+IOContext::IOContext(int64_t _pool, std::string&& _ns)
+ : IOContext() {
+ pool(_pool);
+ ns(std::move(_ns));
+}
+
+IOContext::~IOContext() {
+ reinterpret_cast<IOContextImpl*>(&impl)->~IOContextImpl();
+}
+
+std::int64_t IOContext::pool() const {
+ return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.pool;
+}
+
+void IOContext::pool(std::int64_t _pool) {
+ reinterpret_cast<IOContextImpl*>(&impl)->oloc.pool = _pool;
+}
+
+std::string_view IOContext::ns() const {
+ return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.nspace;
+}
+
+void IOContext::ns(std::string&& _ns) {
+ reinterpret_cast<IOContextImpl*>(&impl)->oloc.nspace = std::move(_ns);
+}
+
+std::optional<std::uint64_t> IOContext::read_snap() const {
+ auto& snap_seq = reinterpret_cast<const IOContextImpl*>(&impl)->snap_seq;
+ if (snap_seq == CEPH_NOSNAP)
+ return std::nullopt;
+ else
+ return snap_seq;
+}
+void IOContext::read_snap(std::optional<std::uint64_t> _snapid) {
+ auto& snap_seq = reinterpret_cast<IOContextImpl*>(&impl)->snap_seq;
+ snap_seq = _snapid.value_or(CEPH_NOSNAP);
+}
+
+std::optional<
+ std::pair<std::uint64_t,
+ std::vector<std::uint64_t>>> IOContext::write_snap_context() const {
+ auto& snapc = reinterpret_cast<const IOContextImpl*>(&impl)->snapc;
+ if (snapc.empty()) {
+ return std::nullopt;
+ } else {
+ std::vector<uint64_t> v(snapc.snaps.begin(), snapc.snaps.end());
+ return std::make_optional(std::make_pair(uint64_t(snapc.seq), v));
+ }
+}
+
+void IOContext::write_snap_context(
+ std::optional<std::pair<std::uint64_t, std::vector<std::uint64_t>>> _snapc) {
+ auto& snapc = reinterpret_cast<IOContextImpl*>(&impl)->snapc;
+ if (!_snapc) {
+ snapc.clear();
+ } else {
+ SnapContext n(_snapc->first, { _snapc->second.begin(), _snapc->second.end()});
+ if (!n.is_valid()) {
+ throw bs::system_error(EINVAL,
+ bs::system_category(),
+ "Invalid snap context.");
+ }
+
+ snapc = n;
+ }
+}
+
+void IOContext::full_try(bool _full_try) {
+ // no-op
+}
+
+bool operator ==(const IOContext& lhs, const IOContext& rhs) {
+ auto l = reinterpret_cast<const IOContextImpl*>(&lhs.impl);
+ auto r = reinterpret_cast<const IOContextImpl*>(&rhs.impl);
+ return (l->oloc == r->oloc &&
+ l->snap_seq == r->snap_seq &&
+ l->snapc.seq == r->snapc.seq &&
+ l->snapc.snaps == r->snapc.snaps);
+}
+
+bool operator !=(const IOContext& lhs, const IOContext& rhs) {
+ return !(lhs == rhs);
+}
+
+Op::Op() {
+ static_assert(Op::impl_size >= sizeof(librados::TestObjectOperationImpl*));
+ auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o = new librados::TestObjectOperationImpl();
+ o->get();
+}
+
+Op::~Op() {
+ auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ if (o != nullptr) {
+ o->put();
+ o = nullptr;
+ }
+}
+
+void Op::assert_exists() {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::assert_exists, _1, _2, _4));
+}
+
+void Op::assert_version(uint64_t ver) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::assert_version, _1, _2, ver));
+}
+
+void Op::cmpext(uint64_t off, ceph::buffer::list&& cmp_bl, std::size_t* s) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ librados::ObjectOperationTestImpl op = std::bind(
+ &librados::TestIoCtxImpl::cmpext, _1, _2, off, cmp_bl, _4);
+ if (s != nullptr) {
+ op = std::bind(
+ save_operation_size, std::bind(op, _1, _2, _3, _4, _5, _6), s);
+ }
+ o->ops.push_back(op);
+}
+
+std::size_t Op::size() const {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl* const *>(&impl);
+ return o->ops.size();
+}
+
+void Op::set_fadvise_random() {
+ // no-op
+}
+
+void Op::set_fadvise_sequential() {
+ // no-op
+}
+
+void Op::set_fadvise_willneed() {
+ // no-op
+}
+
+void Op::set_fadvise_dontneed() {
+ // no-op
+}
+
+void Op::set_fadvise_nocache() {
+ // no-op
+}
+
+void Op::balance_reads() {
+ // no-op
+}
+
+void Op::localize_reads() {
+ // no-op
+}
+
+void Op::exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ ceph::buffer::list* out,
+ boost::system::error_code* ec) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+
+ auto cls_handler = librados_test_stub::get_class_handler();
+ librados::ObjectOperationTestImpl op =
+ [cls_handler, cls, method, inbl = const_cast<bufferlist&>(inbl), out]
+ (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
+ uint64_t snap_id, const SnapContext& snapc, uint64_t*) mutable -> int {
+ return io_ctx->exec(
+ oid, cls_handler, std::string(cls).c_str(),
+ std::string(method).c_str(), inbl,
+ (out != nullptr ? out : outbl), snap_id, snapc);
+ };
+ if (ec != nullptr) {
+ op = std::bind(
+ save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
+ }
+ o->ops.push_back(op);
+}
+
+void Op::exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ boost::system::error_code* ec) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+
+ auto cls_handler = librados_test_stub::get_class_handler();
+ librados::ObjectOperationTestImpl op =
+ [cls_handler, cls, method, inbl = const_cast<bufferlist&>(inbl)]
+ (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
+ uint64_t snap_id, const SnapContext& snapc, uint64_t*) mutable -> int {
+ return io_ctx->exec(
+ oid, cls_handler, std::string(cls).c_str(),
+ std::string(method).c_str(), inbl, outbl, snap_id, snapc);
+ };
+ if (ec != NULL) {
+ op = std::bind(
+ save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
+ }
+ o->ops.push_back(op);
+}
+
+void ReadOp::read(size_t off, uint64_t len, ceph::buffer::list* out,
+ boost::system::error_code* ec) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ librados::ObjectOperationTestImpl op;
+ if (out != nullptr) {
+ op = std::bind(
+ &librados::TestIoCtxImpl::read, _1, _2, len, off, out, _4, _6);
+ } else {
+ op = std::bind(
+ &librados::TestIoCtxImpl::read, _1, _2, len, off, _3, _4, _6);
+ }
+
+ if (ec != NULL) {
+ op = std::bind(
+ save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
+ }
+ o->ops.push_back(op);
+}
+
+void ReadOp::sparse_read(uint64_t off, uint64_t len,
+ ceph::buffer::list* out,
+ std::vector<std::pair<std::uint64_t,
+ std::uint64_t>>* extents,
+ boost::system::error_code* ec) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ librados::ObjectOperationTestImpl op =
+ [off, len, out, extents]
+ (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
+ uint64_t snap_id, const SnapContext& snapc, uint64_t*) mutable -> int {
+ std::map<uint64_t,uint64_t> m;
+ int r = io_ctx->sparse_read(
+ oid, off, len, &m, (out != nullptr ? out : outbl), snap_id);
+ if (r >= 0 && extents != nullptr) {
+ extents->clear();
+ extents->insert(extents->end(), m.begin(), m.end());
+ }
+ return r;
+ };
+ if (ec != NULL) {
+ op = std::bind(save_operation_ec,
+ std::bind(op, _1, _2, _3, _4, _5, _6), ec);
+ }
+ o->ops.push_back(op);
+}
+
+void ReadOp::list_snaps(SnapSet* snaps, bs::error_code* ec) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ librados::ObjectOperationTestImpl op =
+ [snaps]
+ (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist*,
+ uint64_t, const SnapContext&, uint64_t*) mutable -> int {
+ librados::snap_set_t snap_set;
+ int r = io_ctx->list_snaps(oid, &snap_set);
+ if (r >= 0 && snaps != nullptr) {
+ *snaps = {};
+ snaps->seq = snap_set.seq;
+ snaps->clones.reserve(snap_set.clones.size());
+ for (auto& clone : snap_set.clones) {
+ neorados::CloneInfo clone_info;
+ clone_info.cloneid = clone.cloneid;
+ clone_info.snaps = clone.snaps;
+ clone_info.overlap = clone.overlap;
+ clone_info.size = clone.size;
+ snaps->clones.push_back(clone_info);
+ }
+ }
+ return r;
+ };
+ if (ec != NULL) {
+ op = std::bind(save_operation_ec,
+ std::bind(op, _1, _2, _3, _4, _5, _6), ec);
+ }
+ o->ops.push_back(op);
+}
+
+void WriteOp::create(bool exclusive) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::create, _1, _2, exclusive, _5));
+}
+
+void WriteOp::write(uint64_t off, ceph::buffer::list&& bl) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::write, _1, _2, bl, bl.length(), off, _5));
+}
+
+void WriteOp::write_full(ceph::buffer::list&& bl) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::write_full, _1, _2, bl, _5));
+}
+
+void WriteOp::remove() {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::remove, _1, _2, _5));
+}
+
+void WriteOp::truncate(uint64_t off) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::truncate, _1, _2, off, _5));
+}
+
+void WriteOp::zero(uint64_t off, uint64_t len) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::zero, _1, _2, off, len, _5));
+}
+
+void WriteOp::writesame(std::uint64_t off, std::uint64_t write_len,
+ ceph::buffer::list&& bl) {
+ auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+ o->ops.push_back(std::bind(
+ &librados::TestIoCtxImpl::writesame, _1, _2, bl, write_len, off, _5));
+}
+
+void WriteOp::set_alloc_hint(uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ alloc_hint::alloc_hint_t flags) {
+ // no-op
+}
+
+RADOS::RADOS() = default;
+
+RADOS::RADOS(RADOS&&) = default;
+
+RADOS::RADOS(std::unique_ptr<detail::Client> impl)
+ : impl(std::move(impl)) {
+}
+
+RADOS::~RADOS() = default;
+
+RADOS RADOS::make_with_librados(librados::Rados& rados) {
+ auto test_rados_client = reinterpret_cast<librados::TestRadosClient*>(
+ rados.client);
+ return RADOS{std::make_unique<detail::Client>(test_rados_client)};
+}
+
+CephContext* neorados::RADOS::cct() {
+ return impl->test_rados_client->cct();
+}
+
+boost::asio::io_context& neorados::RADOS::get_io_context() {
+ return impl->io_context;
+}
+
+boost::asio::io_context::executor_type neorados::RADOS::get_executor() const {
+ return impl->io_context.get_executor();
+}
+
+void RADOS::execute(const Object& o, const IOContext& ioc, ReadOp&& op,
+ ceph::buffer::list* bl, std::unique_ptr<Op::Completion> c,
+ uint64_t* objver, const blkin_trace_info* trace_info) {
+ auto io_ctx = impl->get_io_ctx(ioc);
+ if (io_ctx == nullptr) {
+ c->dispatch(std::move(c), osdc_errc::pool_dne);
+ return;
+ }
+
+ auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
+
+ auto snap_id = CEPH_NOSNAP;
+ auto opt_snap_id = ioc.read_snap();
+ if (opt_snap_id) {
+ snap_id = *opt_snap_id;
+ }
+
+ auto completion = create_aio_completion(std::move(c));
+ auto r = io_ctx->aio_operate_read(std::string{o}, *ops, completion, 0U, bl,
+ snap_id, objver);
+ ceph_assert(r == 0);
+}
+
+void RADOS::execute(const Object& o, const IOContext& ioc, WriteOp&& op,
+ std::unique_ptr<Op::Completion> c, uint64_t* objver,
+ const blkin_trace_info* trace_info) {
+ auto io_ctx = impl->get_io_ctx(ioc);
+ if (io_ctx == nullptr) {
+ c->dispatch(std::move(c), osdc_errc::pool_dne);
+ return;
+ }
+
+ auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
+
+ SnapContext snapc;
+ auto opt_snapc = ioc.write_snap_context();
+ if (opt_snapc) {
+ snapc.seq = opt_snapc->first;
+ snapc.snaps.assign(opt_snapc->second.begin(), opt_snapc->second.end());
+ }
+
+ auto completion = create_aio_completion(std::move(c));
+ auto r = io_ctx->aio_operate(std::string{o}, *ops, completion, &snapc, nullptr, 0U);
+ ceph_assert(r == 0);
+}
+
+void RADOS::mon_command(std::vector<std::string> command,
+ const bufferlist& bl,
+ std::string* outs, bufferlist* outbl,
+ std::unique_ptr<Op::Completion> c) {
+ auto r = impl->test_rados_client->mon_command(command, bl, outbl, outs);
+ c->post(std::move(c),
+ (r < 0 ? bs::error_code(-r, osd_category()) : bs::error_code()));
+}
+
+void RADOS::blocklist_add(std::string_view client_address,
+ std::optional<std::chrono::seconds> expire,
+ std::unique_ptr<SimpleOpComp> c) {
+ auto r = impl->test_rados_client->blocklist_add(
+ std::string(client_address), expire.value_or(0s).count());
+ c->post(std::move(c),
+ (r < 0 ? bs::error_code(-r, mon_category()) : bs::error_code()));
+}
+
+void RADOS::wait_for_latest_osd_map(std::unique_ptr<Op::Completion> c) {
+ auto r = impl->test_rados_client->wait_for_latest_osd_map();
+ c->dispatch(std::move(c),
+ (r < 0 ? bs::error_code(-r, osd_category()) :
+ bs::error_code()));
+}
+
+} // namespace neorados
+
+namespace librados {
+
+MockTestMemIoCtxImpl& get_mock_io_ctx(neorados::RADOS& rados,
+ neorados::IOContext& io_context) {
+ auto& impl = *reinterpret_cast<std::unique_ptr<neorados::detail::Client>*>(
+ &rados);
+ auto io_ctx = impl->get_io_ctx(io_context);
+ ceph_assert(io_ctx != nullptr);
+ return *reinterpret_cast<MockTestMemIoCtxImpl*>(io_ctx);
+}
+
+MockTestMemRadosClient& get_mock_rados_client(neorados::RADOS& rados) {
+ auto& impl = *reinterpret_cast<std::unique_ptr<neorados::detail::Client>*>(
+ &rados);
+ return *reinterpret_cast<MockTestMemRadosClient*>(impl->test_rados_client);
+}
+
+} // namespace librados
diff --git a/src/test/librados_test_stub/TestClassHandler.cc b/src/test/librados_test_stub/TestClassHandler.cc
new file mode 100644
index 000000000..df830918f
--- /dev/null
+++ b/src/test/librados_test_stub/TestClassHandler.cc
@@ -0,0 +1,159 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/TestClassHandler.h"
+#include "test/librados_test_stub/TestIoCtxImpl.h"
+#include <boost/algorithm/string/predicate.hpp>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common/debug.h"
+#include "include/ceph_assert.h"
+#include "include/dlfcn_compat.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rados
+
+namespace librados {
+
+TestClassHandler::TestClassHandler() {
+}
+
+TestClassHandler::~TestClassHandler() {
+ for (ClassHandles::iterator it = m_class_handles.begin();
+ it != m_class_handles.end(); ++it) {
+ dlclose(*it);
+ }
+}
+
+void TestClassHandler::open_class(const std::string& name,
+ const std::string& path) {
+ void *handle = dlopen(path.c_str(), RTLD_NOW);
+ if (handle == NULL) {
+ std::cerr << "Failed to load class: " << name << " (" << path << "): "
+ << dlerror() << std::endl;
+ return;
+ }
+
+ // initialize
+ void (*cls_init)() = reinterpret_cast<void (*)()>(
+ dlsym(handle, "__cls_init"));
+
+ if (!cls_init) {
+ std::cerr << "Error locating initializer: " << dlerror() << std::endl;
+ } else if (cls_init) {
+ m_class_handles.push_back(handle);
+ cls_init();
+ return;
+ }
+
+ std::cerr << "Class: " << name << " (" << path << ") missing initializer"
+ << std::endl;
+ dlclose(handle);
+}
+
+void TestClassHandler::open_all_classes() {
+ ceph_assert(m_class_handles.empty());
+
+ const char* env = getenv("CEPH_LIB");
+ std::string CEPH_LIB(env ? env : "lib");
+ DIR *dir = ::opendir(CEPH_LIB.c_str());
+ if (dir == NULL) {
+ ceph_abort();;
+ }
+
+ std::set<std::string> names;
+ struct dirent *pde = nullptr;
+ while ((pde = ::readdir(dir))) {
+ std::string name(pde->d_name);
+ if (!boost::algorithm::starts_with(name, "libcls_") ||
+ !boost::algorithm::ends_with(name, SHARED_LIB_SUFFIX)) {
+ continue;
+ }
+ names.insert(name);
+ }
+
+ for (auto& name : names) {
+ std::string class_name = name.substr(7, name.size() - 10);
+ open_class(class_name, CEPH_LIB + "/" + name);
+ }
+ closedir(dir);
+}
+
+int TestClassHandler::create(const std::string &name, cls_handle_t *handle) {
+ if (m_classes.find(name) != m_classes.end()) {
+ std::cerr << "Class " << name << " already exists" << std::endl;
+ return -EEXIST;
+ }
+
+ SharedClass cls(new Class());
+ m_classes[name] = cls;
+ *handle = reinterpret_cast<cls_handle_t>(cls.get());
+ return 0;
+}
+
+int TestClassHandler::create_method(cls_handle_t hclass,
+ const char *name,
+ cls_method_cxx_call_t class_call,
+ cls_method_handle_t *handle) {
+ Class *cls = reinterpret_cast<Class*>(hclass);
+ if (cls->methods.find(name) != cls->methods.end()) {
+ std::cerr << "Class method " << hclass << ":" << name << " already exists"
+ << std::endl;
+ return -EEXIST;
+ }
+
+ SharedMethod method(new Method());
+ method->class_call = class_call;
+ cls->methods[name] = method;
+ return 0;
+}
+
+cls_method_cxx_call_t TestClassHandler::get_method(const std::string &cls,
+ const std::string &method) {
+ Classes::iterator c_it = m_classes.find(cls);
+ if (c_it == m_classes.end()) {
+ std::cerr << "Failed to located class " << cls << std::endl;
+ return NULL;
+ }
+
+ SharedClass scls = c_it->second;
+ Methods::iterator m_it = scls->methods.find(method);
+ if (m_it == scls->methods.end()) {
+ std::cerr << "Failed to located class method" << cls << "." << method
+ << std::endl;
+ return NULL;
+ }
+ return m_it->second->class_call;
+}
+
+TestClassHandler::SharedMethodContext TestClassHandler::get_method_context(
+ TestIoCtxImpl *io_ctx_impl, const std::string &oid, uint64_t snap_id,
+ const SnapContext &snapc) {
+ SharedMethodContext ctx(new MethodContext());
+
+ // clone to ioctx to provide a firewall for gmock expectations
+ ctx->io_ctx_impl = io_ctx_impl->clone();
+ ctx->oid = oid;
+ ctx->snap_id = snap_id;
+ ctx->snapc = snapc;
+ return ctx;
+}
+
+int TestClassHandler::create_filter(cls_handle_t hclass,
+ const std::string& name,
+ cls_cxx_filter_factory_t fn)
+{
+ Class *cls = reinterpret_cast<Class*>(hclass);
+ if (cls->filters.find(name) != cls->filters.end()) {
+ return -EEXIST;
+ }
+ cls->filters[name] = fn;
+ return 0;
+}
+
+TestClassHandler::MethodContext::~MethodContext() {
+ io_ctx_impl->put();
+}
+
+} // namespace librados
diff --git a/src/test/librados_test_stub/TestClassHandler.h b/src/test/librados_test_stub/TestClassHandler.h
new file mode 100644
index 000000000..09e009bf8
--- /dev/null
+++ b/src/test/librados_test_stub/TestClassHandler.h
@@ -0,0 +1,79 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_CLASS_HANDLER_H
+#define CEPH_TEST_CLASS_HANDLER_H
+
+#include "objclass/objclass.h"
+#include "common/snap_types.h"
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <map>
+#include <string>
+
+namespace librados
+{
+
+class TestIoCtxImpl;
+
+class TestClassHandler {
+public:
+
+ TestClassHandler();
+ ~TestClassHandler();
+
+ struct MethodContext {
+ ~MethodContext();
+
+ TestIoCtxImpl *io_ctx_impl;
+ std::string oid;
+ uint64_t snap_id;
+ SnapContext snapc;
+ };
+ typedef boost::shared_ptr<MethodContext> SharedMethodContext;
+
+ struct Method {
+ cls_method_cxx_call_t class_call;
+ };
+ typedef boost::shared_ptr<Method> SharedMethod;
+ typedef std::map<std::string, SharedMethod> Methods;
+ typedef std::map<std::string, cls_cxx_filter_factory_t> Filters;
+
+ struct Class {
+ Methods methods;
+ Filters filters;
+ };
+ typedef boost::shared_ptr<Class> SharedClass;
+
+ void open_all_classes();
+
+ int create(const std::string &name, cls_handle_t *handle);
+ int create_method(cls_handle_t hclass, const char *method,
+ cls_method_cxx_call_t class_call,
+ cls_method_handle_t *handle);
+ cls_method_cxx_call_t get_method(const std::string &cls,
+ const std::string &method);
+ SharedMethodContext get_method_context(TestIoCtxImpl *io_ctx_impl,
+ const std::string &oid,
+ uint64_t snap_id,
+ const SnapContext &snapc);
+
+ int create_filter(cls_handle_t hclass, const std::string& filter_name,
+ cls_cxx_filter_factory_t fn);
+
+private:
+
+ typedef std::map<std::string, SharedClass> Classes;
+ typedef std::list<void*> ClassHandles;
+
+ Classes m_classes;
+ ClassHandles m_class_handles;
+ Filters m_filters;
+
+ void open_class(const std::string& name, const std::string& path);
+
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_CLASS_HANDLER_H
diff --git a/src/test/librados_test_stub/TestCluster.h b/src/test/librados_test_stub/TestCluster.h
new file mode 100644
index 000000000..9b7612d31
--- /dev/null
+++ b/src/test/librados_test_stub/TestCluster.h
@@ -0,0 +1,64 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_CLUSTER_H
+#define CEPH_TEST_CLUSTER_H
+
+#include "test/librados_test_stub/TestWatchNotify.h"
+#include "include/common_fwd.h"
+
+namespace librados {
+
+class TestRadosClient;
+class TestWatchNotify;
+
+class TestCluster {
+public:
+ struct ObjectLocator {
+ std::string nspace;
+ std::string name;
+
+ ObjectLocator(const std::string& nspace, const std::string& name)
+ : nspace(nspace), name(name) {
+ }
+
+ bool operator<(const ObjectLocator& rhs) const {
+ if (nspace != rhs.nspace) {
+ return nspace < rhs.nspace;
+ }
+ return name < rhs.name;
+ }
+ };
+
+ struct ObjectHandler {
+ virtual ~ObjectHandler() {}
+
+ virtual void handle_removed(TestRadosClient* test_rados_client) = 0;
+ };
+
+ TestCluster() : m_watch_notify(this) {
+ }
+ virtual ~TestCluster() {
+ }
+
+ virtual TestRadosClient *create_rados_client(CephContext *cct) = 0;
+
+ virtual int register_object_handler(int64_t pool_id,
+ const ObjectLocator& locator,
+ ObjectHandler* object_handler) = 0;
+ virtual void unregister_object_handler(int64_t pool_id,
+ const ObjectLocator& locator,
+ ObjectHandler* object_handler) = 0;
+
+ TestWatchNotify *get_watch_notify() {
+ return &m_watch_notify;
+ }
+
+protected:
+ TestWatchNotify m_watch_notify;
+
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_CLUSTER_H
diff --git a/src/test/librados_test_stub/TestIoCtxImpl.cc b/src/test/librados_test_stub/TestIoCtxImpl.cc
new file mode 100644
index 000000000..8a953ff7c
--- /dev/null
+++ b/src/test/librados_test_stub/TestIoCtxImpl.cc
@@ -0,0 +1,394 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/TestIoCtxImpl.h"
+#include "test/librados_test_stub/TestClassHandler.h"
+#include "test/librados_test_stub/TestRadosClient.h"
+#include "test/librados_test_stub/TestWatchNotify.h"
+#include "librados/AioCompletionImpl.h"
+#include "include/ceph_assert.h"
+#include "common/Finisher.h"
+#include "common/valgrind.h"
+#include "objclass/objclass.h"
+#include <functional>
+#include <errno.h>
+
+using namespace std;
+
+namespace librados {
+
+TestIoCtxImpl::TestIoCtxImpl() : m_client(NULL) {
+ get();
+}
+
+TestIoCtxImpl::TestIoCtxImpl(TestRadosClient *client, int64_t pool_id,
+ const std::string& pool_name)
+ : m_client(client), m_pool_id(pool_id), m_pool_name(pool_name),
+ m_snap_seq(CEPH_NOSNAP)
+{
+ m_client->get();
+ get();
+}
+
+TestIoCtxImpl::TestIoCtxImpl(const TestIoCtxImpl& rhs)
+ : m_client(rhs.m_client),
+ m_pool_id(rhs.m_pool_id),
+ m_pool_name(rhs.m_pool_name),
+ m_namespace_name(rhs.m_namespace_name),
+ m_snap_seq(rhs.m_snap_seq)
+{
+ m_client->get();
+ get();
+}
+
+TestIoCtxImpl::~TestIoCtxImpl() {
+ ceph_assert(m_pending_ops == 0);
+}
+
+void TestObjectOperationImpl::get() {
+ m_refcount++;
+}
+
+void TestObjectOperationImpl::put() {
+ if (--m_refcount == 0) {
+ ANNOTATE_HAPPENS_AFTER(&m_refcount);
+ ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(&m_refcount);
+ delete this;
+ } else {
+ ANNOTATE_HAPPENS_BEFORE(&m_refcount);
+ }
+}
+
+void TestIoCtxImpl::get() {
+ m_refcount++;
+}
+
+void TestIoCtxImpl::put() {
+ if (--m_refcount == 0) {
+ m_client->put();
+ delete this;
+ }
+}
+
+uint64_t TestIoCtxImpl::get_instance_id() const {
+ return m_client->get_instance_id();
+}
+
+int64_t TestIoCtxImpl::get_id() {
+ return m_pool_id;
+}
+
+uint64_t TestIoCtxImpl::get_last_version() {
+ return 0;
+}
+
+std::string TestIoCtxImpl::get_pool_name() {
+ return m_pool_name;
+}
+
+int TestIoCtxImpl::aio_flush() {
+ m_client->flush_aio_operations();
+ return 0;
+}
+
+void TestIoCtxImpl::aio_flush_async(AioCompletionImpl *c) {
+ m_client->flush_aio_operations(c);
+}
+
+void TestIoCtxImpl::aio_notify(const std::string& oid, AioCompletionImpl *c,
+ bufferlist& bl, uint64_t timeout_ms,
+ bufferlist *pbl) {
+ m_pending_ops++;
+ c->get();
+ C_AioNotify *ctx = new C_AioNotify(this, c);
+ m_client->get_watch_notify()->aio_notify(m_client, m_pool_id, get_namespace(),
+ oid, bl, timeout_ms, pbl, ctx);
+}
+
+int TestIoCtxImpl::aio_operate(const std::string& oid, TestObjectOperationImpl &ops,
+ AioCompletionImpl *c, SnapContext *snap_context,
+ const ceph::real_time *pmtime, int flags) {
+ // TODO flags for now
+ ops.get();
+ m_pending_ops++;
+ m_client->add_aio_operation(oid, true, std::bind(
+ &TestIoCtxImpl::execute_aio_operations, this, oid, &ops,
+ reinterpret_cast<bufferlist*>(0), m_snap_seq,
+ snap_context != NULL ? *snap_context : m_snapc, nullptr), c);
+ return 0;
+}
+
+int TestIoCtxImpl::aio_operate_read(const std::string& oid,
+ TestObjectOperationImpl &ops,
+ AioCompletionImpl *c, int flags,
+ bufferlist *pbl, uint64_t snap_id,
+ uint64_t* objver) {
+ // TODO ignoring flags for now
+ ops.get();
+ m_pending_ops++;
+ m_client->add_aio_operation(oid, true, std::bind(
+ &TestIoCtxImpl::execute_aio_operations, this, oid, &ops, pbl, snap_id,
+ m_snapc, objver), c);
+ return 0;
+}
+
+int TestIoCtxImpl::aio_watch(const std::string& o, AioCompletionImpl *c,
+ uint64_t *handle, librados::WatchCtx2 *watch_ctx) {
+ m_pending_ops++;
+ c->get();
+ C_AioNotify *ctx = new C_AioNotify(this, c);
+ if (m_client->is_blocklisted()) {
+ m_client->get_aio_finisher()->queue(ctx, -EBLOCKLISTED);
+ } else {
+ m_client->get_watch_notify()->aio_watch(m_client, m_pool_id,
+ get_namespace(), o,
+ get_instance_id(), handle, nullptr,
+ watch_ctx, ctx);
+ }
+ return 0;
+}
+
+int TestIoCtxImpl::aio_unwatch(uint64_t handle, AioCompletionImpl *c) {
+ m_pending_ops++;
+ c->get();
+ C_AioNotify *ctx = new C_AioNotify(this, c);
+ if (m_client->is_blocklisted()) {
+ m_client->get_aio_finisher()->queue(ctx, -EBLOCKLISTED);
+ } else {
+ m_client->get_watch_notify()->aio_unwatch(m_client, handle, ctx);
+ }
+ return 0;
+}
+
+int TestIoCtxImpl::exec(const std::string& oid, TestClassHandler *handler,
+ const char *cls, const char *method,
+ bufferlist& inbl, bufferlist* outbl,
+ uint64_t snap_id, const SnapContext &snapc) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ cls_method_cxx_call_t call = handler->get_method(cls, method);
+ if (call == NULL) {
+ return -ENOSYS;
+ }
+
+ return (*call)(reinterpret_cast<cls_method_context_t>(
+ handler->get_method_context(this, oid, snap_id, snapc).get()), &inbl,
+ outbl);
+}
+
+int TestIoCtxImpl::list_watchers(const std::string& o,
+ std::list<obj_watch_t> *out_watchers) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ return m_client->get_watch_notify()->list_watchers(m_pool_id, get_namespace(),
+ o, out_watchers);
+}
+
+int TestIoCtxImpl::notify(const std::string& o, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ return m_client->get_watch_notify()->notify(m_client, m_pool_id,
+ get_namespace(), o, bl,
+ timeout_ms, pbl);
+}
+
+void TestIoCtxImpl::notify_ack(const std::string& o, uint64_t notify_id,
+ uint64_t handle, bufferlist& bl) {
+ m_client->get_watch_notify()->notify_ack(m_client, m_pool_id, get_namespace(),
+ o, notify_id, handle,
+ m_client->get_instance_id(), bl);
+}
+
+int TestIoCtxImpl::operate(const std::string& oid,
+ TestObjectOperationImpl &ops) {
+ AioCompletionImpl *comp = new AioCompletionImpl();
+
+ ops.get();
+ m_pending_ops++;
+ m_client->add_aio_operation(oid, false, std::bind(
+ &TestIoCtxImpl::execute_aio_operations, this, oid, &ops,
+ reinterpret_cast<bufferlist*>(0), m_snap_seq, m_snapc, nullptr), comp);
+
+ comp->wait_for_complete();
+ int ret = comp->get_return_value();
+ comp->put();
+ return ret;
+}
+
+int TestIoCtxImpl::operate_read(const std::string& oid,
+ TestObjectOperationImpl &ops,
+ bufferlist *pbl) {
+ AioCompletionImpl *comp = new AioCompletionImpl();
+
+ ops.get();
+ m_pending_ops++;
+ m_client->add_aio_operation(oid, false, std::bind(
+ &TestIoCtxImpl::execute_aio_operations, this, oid, &ops, pbl,
+ m_snap_seq, m_snapc, nullptr), comp);
+
+ comp->wait_for_complete();
+ int ret = comp->get_return_value();
+ comp->put();
+ return ret;
+}
+
+void TestIoCtxImpl::aio_selfmanaged_snap_create(uint64_t *snapid,
+ AioCompletionImpl *c) {
+ m_client->add_aio_operation(
+ "", true,
+ std::bind(&TestIoCtxImpl::selfmanaged_snap_create, this, snapid), c);
+}
+
+void TestIoCtxImpl::aio_selfmanaged_snap_remove(uint64_t snapid,
+ AioCompletionImpl *c) {
+ m_client->add_aio_operation(
+ "", true,
+ std::bind(&TestIoCtxImpl::selfmanaged_snap_remove, this, snapid), c);
+}
+
+int TestIoCtxImpl::selfmanaged_snap_set_write_ctx(snap_t seq,
+ std::vector<snap_t>& snaps) {
+ std::vector<snapid_t> snap_ids(snaps.begin(), snaps.end());
+ m_snapc = SnapContext(seq, snap_ids);
+ return 0;
+}
+
+int TestIoCtxImpl::set_alloc_hint(const std::string& oid,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags,
+ const SnapContext &snapc) {
+ return 0;
+}
+
+void TestIoCtxImpl::set_snap_read(snap_t seq) {
+ if (seq == 0) {
+ seq = CEPH_NOSNAP;
+ }
+ m_snap_seq = seq;
+}
+
+int TestIoCtxImpl::tmap_update(const std::string& oid, bufferlist& cmdbl) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ // TODO: protect against concurrent tmap updates
+ bufferlist tmap_header;
+ std::map<string,bufferlist> tmap;
+ uint64_t size = 0;
+ int r = stat(oid, &size, NULL);
+ if (r == -ENOENT) {
+ r = create(oid, false, m_snapc);
+ }
+ if (r < 0) {
+ return r;
+ }
+
+ if (size > 0) {
+ bufferlist inbl;
+ r = read(oid, size, 0, &inbl, CEPH_NOSNAP, nullptr);
+ if (r < 0) {
+ return r;
+ }
+ auto iter = inbl.cbegin();
+ decode(tmap_header, iter);
+ decode(tmap, iter);
+ }
+
+ __u8 c;
+ std::string key;
+ bufferlist value;
+ auto iter = cmdbl.cbegin();
+ decode(c, iter);
+ decode(key, iter);
+
+ switch (c) {
+ case CEPH_OSD_TMAP_SET:
+ decode(value, iter);
+ tmap[key] = value;
+ break;
+ case CEPH_OSD_TMAP_RM:
+ r = tmap.erase(key);
+ if (r == 0) {
+ return -ENOENT;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bufferlist out;
+ encode(tmap_header, out);
+ encode(tmap, out);
+ r = write_full(oid, out, m_snapc);
+ return r;
+}
+
+int TestIoCtxImpl::unwatch(uint64_t handle) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ return m_client->get_watch_notify()->unwatch(m_client, handle);
+}
+
+int TestIoCtxImpl::watch(const std::string& o, uint64_t *handle,
+ librados::WatchCtx *ctx, librados::WatchCtx2 *ctx2) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ return m_client->get_watch_notify()->watch(m_client, m_pool_id,
+ get_namespace(), o,
+ get_instance_id(), handle, ctx,
+ ctx2);
+}
+
+int TestIoCtxImpl::execute_operation(const std::string& oid,
+ const Operation &operation) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ TestRadosClient::Transaction transaction(m_client, get_namespace(), oid);
+ return operation(this, oid);
+}
+
+int TestIoCtxImpl::execute_aio_operations(const std::string& oid,
+ TestObjectOperationImpl *ops,
+ bufferlist *pbl, uint64_t snap_id,
+ const SnapContext &snapc,
+ uint64_t* objver) {
+ int ret = 0;
+ if (m_client->is_blocklisted()) {
+ ret = -EBLOCKLISTED;
+ } else {
+ TestRadosClient::Transaction transaction(m_client, get_namespace(), oid);
+ for (ObjectOperations::iterator it = ops->ops.begin();
+ it != ops->ops.end(); ++it) {
+ ret = (*it)(this, oid, pbl, snap_id, snapc, objver);
+ if (ret < 0) {
+ break;
+ }
+ }
+ }
+ m_pending_ops--;
+ ops->put();
+ return ret;
+}
+
+void TestIoCtxImpl::handle_aio_notify_complete(AioCompletionImpl *c, int r) {
+ m_pending_ops--;
+
+ m_client->finish_aio_completion(c, r);
+}
+
+} // namespace librados
diff --git a/src/test/librados_test_stub/TestIoCtxImpl.h b/src/test/librados_test_stub/TestIoCtxImpl.h
new file mode 100644
index 000000000..fdda17cfd
--- /dev/null
+++ b/src/test/librados_test_stub/TestIoCtxImpl.h
@@ -0,0 +1,221 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_IO_CTX_IMPL_H
+#define CEPH_TEST_IO_CTX_IMPL_H
+
+#include <list>
+#include <atomic>
+
+#include <boost/function.hpp>
+
+#include "include/rados/librados.hpp"
+#include "include/Context.h"
+#include "common/snap_types.h"
+
+namespace librados {
+
+class TestClassHandler;
+class TestIoCtxImpl;
+class TestRadosClient;
+
+typedef boost::function<int(TestIoCtxImpl*,
+ const std::string&,
+ bufferlist *,
+ uint64_t,
+ const SnapContext &,
+ uint64_t*)> ObjectOperationTestImpl;
+typedef std::list<ObjectOperationTestImpl> ObjectOperations;
+
+struct TestObjectOperationImpl {
+public:
+ void get();
+ void put();
+
+ ObjectOperations ops;
+private:
+ std::atomic<uint64_t> m_refcount = { 0 };
+};
+
+class TestIoCtxImpl {
+public:
+ typedef boost::function<int(TestIoCtxImpl *, const std::string &)> Operation;
+
+
+ TestIoCtxImpl();
+ explicit TestIoCtxImpl(TestRadosClient *client, int64_t m_pool_id,
+ const std::string& pool_name);
+
+ TestRadosClient *get_rados_client() {
+ return m_client;
+ }
+
+ void get();
+ void put();
+
+ inline int64_t get_pool_id() const {
+ return m_pool_id;
+ }
+
+ virtual TestIoCtxImpl *clone() = 0;
+
+ virtual uint64_t get_instance_id() const;
+ virtual int64_t get_id();
+ virtual uint64_t get_last_version();
+ virtual std::string get_pool_name();
+
+ inline void set_namespace(const std::string& namespace_name) {
+ m_namespace_name = namespace_name;
+ }
+ inline std::string get_namespace() const {
+ return m_namespace_name;
+ }
+
+ snap_t get_snap_read() const {
+ return m_snap_seq;
+ }
+
+ inline void set_snap_context(const SnapContext& snapc) {
+ m_snapc = snapc;
+ }
+ const SnapContext &get_snap_context() const {
+ return m_snapc;
+ }
+
+ virtual int aio_flush();
+ virtual void aio_flush_async(AioCompletionImpl *c);
+ virtual void aio_notify(const std::string& oid, AioCompletionImpl *c,
+ bufferlist& bl, uint64_t timeout_ms, bufferlist *pbl);
+ virtual int aio_operate(const std::string& oid, TestObjectOperationImpl &ops,
+ AioCompletionImpl *c, SnapContext *snap_context,
+ const ceph::real_time *pmtime, int flags);
+ virtual int aio_operate_read(const std::string& oid, TestObjectOperationImpl &ops,
+ AioCompletionImpl *c, int flags,
+ bufferlist *pbl, uint64_t snap_id,
+ uint64_t* objver);
+ virtual int aio_remove(const std::string& oid, AioCompletionImpl *c,
+ int flags = 0) = 0;
+ virtual int aio_watch(const std::string& o, AioCompletionImpl *c,
+ uint64_t *handle, librados::WatchCtx2 *ctx);
+ virtual int aio_unwatch(uint64_t handle, AioCompletionImpl *c);
+ virtual int append(const std::string& oid, const bufferlist &bl,
+ const SnapContext &snapc) = 0;
+ virtual int assert_exists(const std::string &oid, uint64_t snap_id) = 0;
+ virtual int assert_version(const std::string &oid, uint64_t ver) = 0;
+
+ virtual int create(const std::string& oid, bool exclusive,
+ const SnapContext &snapc) = 0;
+ virtual int exec(const std::string& oid, TestClassHandler *handler,
+ const char *cls, const char *method,
+ bufferlist& inbl, bufferlist* outbl,
+ uint64_t snap_id, const SnapContext &snapc);
+ virtual int list_snaps(const std::string& o, snap_set_t *out_snaps) = 0;
+ virtual int list_watchers(const std::string& o,
+ std::list<obj_watch_t> *out_watchers);
+ virtual int notify(const std::string& o, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl);
+ virtual void notify_ack(const std::string& o, uint64_t notify_id,
+ uint64_t handle, bufferlist& bl);
+ virtual int omap_get_vals(const std::string& oid,
+ const std::string& start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals) = 0;
+ virtual int omap_get_vals2(const std::string& oid,
+ const std::string& start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ bool *pmore) = 0;
+ virtual int omap_rm_keys(const std::string& oid,
+ const std::set<std::string>& keys) = 0;
+ virtual int omap_set(const std::string& oid,
+ const std::map<std::string, bufferlist> &map) = 0;
+ virtual int operate(const std::string& oid, TestObjectOperationImpl &ops);
+ virtual int operate_read(const std::string& oid, TestObjectOperationImpl &ops,
+ bufferlist *pbl);
+ virtual int read(const std::string& oid, size_t len, uint64_t off,
+ bufferlist *bl, uint64_t snap_id, uint64_t* objver) = 0;
+ virtual int remove(const std::string& oid, const SnapContext &snapc) = 0;
+ virtual int selfmanaged_snap_create(uint64_t *snapid) = 0;
+ virtual void aio_selfmanaged_snap_create(uint64_t *snapid,
+ AioCompletionImpl *c);
+ virtual int selfmanaged_snap_remove(uint64_t snapid) = 0;
+ virtual void aio_selfmanaged_snap_remove(uint64_t snapid,
+ AioCompletionImpl *c);
+ virtual int selfmanaged_snap_rollback(const std::string& oid,
+ uint64_t snapid) = 0;
+ virtual int selfmanaged_snap_set_write_ctx(snap_t seq,
+ std::vector<snap_t>& snaps);
+ virtual int set_alloc_hint(const std::string& oid,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags,
+ const SnapContext &snapc);
+ virtual void set_snap_read(snap_t seq);
+ virtual int sparse_read(const std::string& oid, uint64_t off, uint64_t len,
+ std::map<uint64_t,uint64_t> *m,
+ bufferlist *data_bl, uint64_t snap_id) = 0;
+ virtual int stat(const std::string& oid, uint64_t *psize, time_t *pmtime) = 0;
+ virtual int truncate(const std::string& oid, uint64_t size,
+ const SnapContext &snapc) = 0;
+ virtual int tmap_update(const std::string& oid, bufferlist& cmdbl);
+ virtual int unwatch(uint64_t handle);
+ virtual int watch(const std::string& o, uint64_t *handle,
+ librados::WatchCtx *ctx, librados::WatchCtx2 *ctx2);
+ virtual int write(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off, const SnapContext &snapc) = 0;
+ virtual int write_full(const std::string& oid, bufferlist& bl,
+ const SnapContext &snapc) = 0;
+ virtual int writesame(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off, const SnapContext &snapc) = 0;
+ virtual int cmpext(const std::string& oid, uint64_t off, bufferlist& cmp_bl,
+ uint64_t snap_id) = 0;
+ virtual int xattr_get(const std::string& oid,
+ std::map<std::string, bufferlist>* attrset) = 0;
+ virtual int xattr_set(const std::string& oid, const std::string &name,
+ bufferlist& bl) = 0;
+ virtual int zero(const std::string& oid, uint64_t off, uint64_t len,
+ const SnapContext &snapc) = 0;
+
+ int execute_operation(const std::string& oid,
+ const Operation &operation);
+
+protected:
+ TestIoCtxImpl(const TestIoCtxImpl& rhs);
+ virtual ~TestIoCtxImpl();
+
+ int execute_aio_operations(const std::string& oid,
+ TestObjectOperationImpl *ops,
+ bufferlist *pbl, uint64_t,
+ const SnapContext &snapc,
+ uint64_t* objver);
+
+private:
+ struct C_AioNotify : public Context {
+ TestIoCtxImpl *io_ctx;
+ AioCompletionImpl *aio_comp;
+ C_AioNotify(TestIoCtxImpl *_io_ctx, AioCompletionImpl *_aio_comp)
+ : io_ctx(_io_ctx), aio_comp(_aio_comp) {
+ }
+ void finish(int r) override {
+ io_ctx->handle_aio_notify_complete(aio_comp, r);
+ }
+ };
+
+ TestRadosClient *m_client;
+ int64_t m_pool_id = 0;
+ std::string m_pool_name;
+ std::string m_namespace_name;
+
+ snap_t m_snap_seq = 0;
+ SnapContext m_snapc;
+ std::atomic<uint64_t> m_refcount = { 0 };
+ std::atomic<uint64_t> m_pending_ops = { 0 };
+
+ void handle_aio_notify_complete(AioCompletionImpl *aio_comp, int r);
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_IO_CTX_IMPL_H
diff --git a/src/test/librados_test_stub/TestMemCluster.cc b/src/test/librados_test_stub/TestMemCluster.cc
new file mode 100644
index 000000000..f0995788b
--- /dev/null
+++ b/src/test/librados_test_stub/TestMemCluster.cc
@@ -0,0 +1,203 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/TestMemCluster.h"
+#include "test/librados_test_stub/TestMemRadosClient.h"
+
+namespace librados {
+
+TestMemCluster::File::File()
+ : objver(0), snap_id(), exists(true) {
+}
+
+TestMemCluster::File::File(const File &rhs)
+ : data(rhs.data),
+ mtime(rhs.mtime),
+ objver(rhs.objver),
+ snap_id(rhs.snap_id),
+ exists(rhs.exists) {
+}
+
+TestMemCluster::Pool::Pool() = default;
+
+TestMemCluster::TestMemCluster()
+ : m_next_nonce(static_cast<uint32_t>(reinterpret_cast<uint64_t>(this))) {
+}
+
+TestMemCluster::~TestMemCluster() {
+ for (auto pool_pair : m_pools) {
+ pool_pair.second->put();
+ }
+}
+
+TestRadosClient *TestMemCluster::create_rados_client(CephContext *cct) {
+ return new TestMemRadosClient(cct, this);
+}
+
+int TestMemCluster::register_object_handler(int64_t pool_id,
+ const ObjectLocator& locator,
+ ObjectHandler* object_handler) {
+ std::lock_guard locker{m_lock};
+ auto pool = get_pool(m_lock, pool_id);
+ if (pool == nullptr) {
+ return -ENOENT;
+ }
+
+ std::unique_lock pool_locker{pool->file_lock};
+ auto file_it = pool->files.find(locator);
+ if (file_it == pool->files.end()) {
+ return -ENOENT;
+ }
+
+ auto& object_handlers = pool->file_handlers[locator];
+ auto it = object_handlers.find(object_handler);
+ ceph_assert(it == object_handlers.end());
+
+ object_handlers.insert(object_handler);
+ return 0;
+}
+
+void TestMemCluster::unregister_object_handler(int64_t pool_id,
+ const ObjectLocator& locator,
+ ObjectHandler* object_handler) {
+ std::lock_guard locker{m_lock};
+ auto pool = get_pool(m_lock, pool_id);
+ if (pool == nullptr) {
+ return;
+ }
+
+ std::unique_lock pool_locker{pool->file_lock};
+ auto handlers_it = pool->file_handlers.find(locator);
+ if (handlers_it == pool->file_handlers.end()) {
+ return;
+ }
+
+ auto& object_handlers = handlers_it->second;
+ object_handlers.erase(object_handler);
+}
+
+int TestMemCluster::pool_create(const std::string &pool_name) {
+ std::lock_guard locker{m_lock};
+ if (m_pools.find(pool_name) != m_pools.end()) {
+ return -EEXIST;
+ }
+ Pool *pool = new Pool();
+ pool->pool_id = ++m_pool_id;
+ m_pools[pool_name] = pool;
+ return 0;
+}
+
+int TestMemCluster::pool_delete(const std::string &pool_name) {
+ std::lock_guard locker{m_lock};
+ Pools::iterator iter = m_pools.find(pool_name);
+ if (iter == m_pools.end()) {
+ return -ENOENT;
+ }
+ iter->second->put();
+ m_pools.erase(iter);
+ return 0;
+}
+
+int TestMemCluster::pool_get_base_tier(int64_t pool_id, int64_t* base_tier) {
+ // TODO
+ *base_tier = pool_id;
+ return 0;
+}
+
+int TestMemCluster::pool_list(std::list<std::pair<int64_t, std::string> >& v) {
+ std::lock_guard locker{m_lock};
+ v.clear();
+ for (Pools::iterator iter = m_pools.begin(); iter != m_pools.end(); ++iter) {
+ v.push_back(std::make_pair(iter->second->pool_id, iter->first));
+ }
+ return 0;
+}
+
+int64_t TestMemCluster::pool_lookup(const std::string &pool_name) {
+ std::lock_guard locker{m_lock};
+ Pools::iterator iter = m_pools.find(pool_name);
+ if (iter == m_pools.end()) {
+ return -ENOENT;
+ }
+ return iter->second->pool_id;
+}
+
+int TestMemCluster::pool_reverse_lookup(int64_t id, std::string *name) {
+ std::lock_guard locker{m_lock};
+ for (Pools::iterator iter = m_pools.begin(); iter != m_pools.end(); ++iter) {
+ if (iter->second->pool_id == id) {
+ *name = iter->first;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+TestMemCluster::Pool *TestMemCluster::get_pool(int64_t pool_id) {
+ std::lock_guard locker{m_lock};
+ return get_pool(m_lock, pool_id);
+}
+
+TestMemCluster::Pool *TestMemCluster::get_pool(const ceph::mutex& lock,
+ int64_t pool_id) {
+ for (auto &pool_pair : m_pools) {
+ if (pool_pair.second->pool_id == pool_id) {
+ return pool_pair.second;
+ }
+ }
+ return nullptr;
+}
+
+TestMemCluster::Pool *TestMemCluster::get_pool(const std::string &pool_name) {
+ std::lock_guard locker{m_lock};
+ Pools::iterator iter = m_pools.find(pool_name);
+ if (iter != m_pools.end()) {
+ return iter->second;
+ }
+ return nullptr;
+}
+
+void TestMemCluster::allocate_client(uint32_t *nonce, uint64_t *global_id) {
+ std::lock_guard locker{m_lock};
+ *nonce = m_next_nonce++;
+ *global_id = m_next_global_id++;
+}
+
+void TestMemCluster::deallocate_client(uint32_t nonce) {
+ std::lock_guard locker{m_lock};
+ m_blocklist.erase(nonce);
+}
+
+bool TestMemCluster::is_blocklisted(uint32_t nonce) const {
+ std::lock_guard locker{m_lock};
+ return (m_blocklist.find(nonce) != m_blocklist.end());
+}
+
+void TestMemCluster::blocklist(uint32_t nonce) {
+ {
+ std::lock_guard locker{m_lock};
+ m_blocklist.insert(nonce);
+ }
+
+ // after blocklisting the client, disconnect and drop its watches
+ m_watch_notify.blocklist(nonce);
+}
+
+void TestMemCluster::transaction_start(const ObjectLocator& locator) {
+ std::unique_lock locker{m_lock};
+ m_transaction_cond.wait(locker, [&locator, this] {
+ return m_transactions.count(locator) == 0;
+ });
+ auto result = m_transactions.insert(locator);
+ ceph_assert(result.second);
+}
+
+void TestMemCluster::transaction_finish(const ObjectLocator& locator) {
+ std::lock_guard locker{m_lock};
+ size_t count = m_transactions.erase(locator);
+ ceph_assert(count == 1);
+ m_transaction_cond.notify_all();
+}
+
+} // namespace librados
+
diff --git a/src/test/librados_test_stub/TestMemCluster.h b/src/test/librados_test_stub/TestMemCluster.h
new file mode 100644
index 000000000..2e80bff17
--- /dev/null
+++ b/src/test/librados_test_stub/TestMemCluster.h
@@ -0,0 +1,124 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_MEM_CLUSTER_H
+#define CEPH_TEST_MEM_CLUSTER_H
+
+#include "test/librados_test_stub/TestCluster.h"
+#include "include/buffer.h"
+#include "include/interval_set.h"
+#include "include/int_types.h"
+#include "common/ceph_mutex.h"
+#include "common/RefCountedObj.h"
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+namespace librados {
+
+class TestMemCluster : public TestCluster {
+public:
+ typedef std::map<std::string, bufferlist> OMap;
+ typedef std::map<ObjectLocator, OMap> FileOMaps;
+ typedef std::map<ObjectLocator, bufferlist> FileTMaps;
+ typedef std::map<std::string, bufferlist> XAttrs;
+ typedef std::map<ObjectLocator, XAttrs> FileXAttrs;
+ typedef std::set<ObjectHandler*> ObjectHandlers;
+ typedef std::map<ObjectLocator, ObjectHandlers> FileHandlers;
+
+ struct File {
+ File();
+ File(const File &rhs);
+
+ bufferlist data;
+ time_t mtime;
+ uint64_t objver;
+
+ uint64_t snap_id;
+ std::vector<uint64_t> snaps;
+ interval_set<uint64_t> snap_overlap;
+
+ bool exists;
+ ceph::shared_mutex lock =
+ ceph::make_shared_mutex("TestMemCluster::File::lock");
+ };
+ typedef boost::shared_ptr<File> SharedFile;
+
+ typedef std::list<SharedFile> FileSnapshots;
+ typedef std::map<ObjectLocator, FileSnapshots> Files;
+
+ typedef std::set<uint64_t> SnapSeqs;
+ struct Pool : public RefCountedObject {
+ Pool();
+
+ int64_t pool_id = 0;
+
+ SnapSeqs snap_seqs;
+ uint64_t snap_id = 1;
+
+ ceph::shared_mutex file_lock =
+ ceph::make_shared_mutex("TestMemCluster::Pool::file_lock");
+ Files files;
+ FileOMaps file_omaps;
+ FileTMaps file_tmaps;
+ FileXAttrs file_xattrs;
+ FileHandlers file_handlers;
+ };
+
+ TestMemCluster();
+ ~TestMemCluster() override;
+
+ TestRadosClient *create_rados_client(CephContext *cct) override;
+
+ int register_object_handler(int64_t pool_id, const ObjectLocator& locator,
+ ObjectHandler* object_handler) override;
+ void unregister_object_handler(int64_t pool_id, const ObjectLocator& locator,
+ ObjectHandler* object_handler) override;
+
+ int pool_create(const std::string &pool_name);
+ int pool_delete(const std::string &pool_name);
+ int pool_get_base_tier(int64_t pool_id, int64_t* base_tier);
+ int pool_list(std::list<std::pair<int64_t, std::string> >& v);
+ int64_t pool_lookup(const std::string &name);
+ int pool_reverse_lookup(int64_t id, std::string *name);
+
+ Pool *get_pool(int64_t pool_id);
+ Pool *get_pool(const std::string &pool_name);
+
+ void allocate_client(uint32_t *nonce, uint64_t *global_id);
+ void deallocate_client(uint32_t nonce);
+
+ bool is_blocklisted(uint32_t nonce) const;
+ void blocklist(uint32_t nonce);
+
+ void transaction_start(const ObjectLocator& locator);
+ void transaction_finish(const ObjectLocator& locator);
+
+private:
+
+ typedef std::map<std::string, Pool*> Pools;
+ typedef std::set<uint32_t> Blocklist;
+
+ mutable ceph::mutex m_lock =
+ ceph::make_mutex("TestMemCluster::m_lock");
+
+ Pools m_pools;
+ int64_t m_pool_id = 0;
+
+ uint32_t m_next_nonce;
+ uint64_t m_next_global_id = 1234;
+
+ Blocklist m_blocklist;
+
+ ceph::condition_variable m_transaction_cond;
+ std::set<ObjectLocator> m_transactions;
+
+ Pool *get_pool(const ceph::mutex& lock, int64_t pool_id);
+
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_MEM_CLUSTER_H
diff --git a/src/test/librados_test_stub/TestMemIoCtxImpl.cc b/src/test/librados_test_stub/TestMemIoCtxImpl.cc
new file mode 100644
index 000000000..77ea14366
--- /dev/null
+++ b/src/test/librados_test_stub/TestMemIoCtxImpl.cc
@@ -0,0 +1,924 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/TestMemIoCtxImpl.h"
+#include "test/librados_test_stub/TestMemRadosClient.h"
+#include "common/Clock.h"
+#include "include/err.h"
+#include <functional>
+#include <boost/algorithm/string/predicate.hpp>
+#include <errno.h>
+#include <include/compat.h>
+
+#define dout_subsys ceph_subsys_rados
+#undef dout_prefix
+#define dout_prefix *_dout << "TestMemIoCtxImpl: " << this << " " << __func__ \
+ << ": " << oid << " "
+
+static void to_vector(const interval_set<uint64_t> &set,
+ std::vector<std::pair<uint64_t, uint64_t> > *vec) {
+ vec->clear();
+ for (interval_set<uint64_t>::const_iterator it = set.begin();
+ it != set.end(); ++it) {
+ vec->push_back(*it);
+ }
+}
+
+// see PrimaryLogPG::finish_extent_cmp()
+static int cmpext_compare(const bufferlist &bl, const bufferlist &read_bl) {
+ for (uint64_t idx = 0; idx < bl.length(); ++idx) {
+ char read_byte = (idx < read_bl.length() ? read_bl[idx] : 0);
+ if (bl[idx] != read_byte) {
+ return -MAX_ERRNO - idx;
+ }
+ }
+ return 0;
+}
+
+namespace librados {
+
+TestMemIoCtxImpl::TestMemIoCtxImpl() {
+}
+
+TestMemIoCtxImpl::TestMemIoCtxImpl(const TestMemIoCtxImpl& rhs)
+ : TestIoCtxImpl(rhs), m_client(rhs.m_client), m_pool(rhs.m_pool) {
+ m_pool->get();
+}
+
+TestMemIoCtxImpl::TestMemIoCtxImpl(TestMemRadosClient *client, int64_t pool_id,
+ const std::string& pool_name,
+ TestMemCluster::Pool *pool)
+ : TestIoCtxImpl(client, pool_id, pool_name), m_client(client),
+ m_pool(pool) {
+ m_pool->get();
+}
+
+TestMemIoCtxImpl::~TestMemIoCtxImpl() {
+ m_pool->put();
+}
+
+TestIoCtxImpl *TestMemIoCtxImpl::clone() {
+ return new TestMemIoCtxImpl(*this);
+}
+
+int TestMemIoCtxImpl::aio_remove(const std::string& oid, AioCompletionImpl *c, int flags) {
+ m_client->add_aio_operation(oid, true,
+ std::bind(&TestMemIoCtxImpl::remove, this, oid,
+ get_snap_context()),
+ c);
+ return 0;
+}
+
+int TestMemIoCtxImpl::append(const std::string& oid, const bufferlist &bl,
+ const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ auto cct = m_client->cct();
+ ldout(cct, 20) << "length=" << bl.length() << ", snapc=" << snapc << dendl;
+
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, true, CEPH_NOSNAP, snapc);
+ }
+
+ std::unique_lock l{file->lock};
+ auto off = file->data.length();
+ ensure_minimum_length(off + bl.length(), &file->data);
+ file->data.begin(off).copy_in(bl.length(), bl);
+ return 0;
+}
+
+int TestMemIoCtxImpl::assert_exists(const std::string &oid, uint64_t snap_id) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ std::shared_lock l{m_pool->file_lock};
+ TestMemCluster::SharedFile file = get_file(oid, false, snap_id, {});
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ return 0;
+}
+
+int TestMemIoCtxImpl::assert_version(const std::string &oid, uint64_t ver) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ std::shared_lock l{m_pool->file_lock};
+ TestMemCluster::SharedFile file = get_file(oid, false, CEPH_NOSNAP, {});
+ if (file == NULL || !file->exists) {
+ return -ENOENT;
+ }
+ if (ver < file->objver) {
+ return -ERANGE;
+ }
+ if (ver > file->objver) {
+ return -EOVERFLOW;
+ }
+
+ return 0;
+}
+
+int TestMemIoCtxImpl::create(const std::string& oid, bool exclusive,
+ const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ auto cct = m_client->cct();
+ ldout(cct, 20) << "snapc=" << snapc << dendl;
+
+ std::unique_lock l{m_pool->file_lock};
+ if (exclusive) {
+ TestMemCluster::SharedFile file = get_file(oid, false, CEPH_NOSNAP, {});
+ if (file != NULL && file->exists) {
+ return -EEXIST;
+ }
+ }
+
+ get_file(oid, true, CEPH_NOSNAP, snapc);
+ return 0;
+}
+
+int TestMemIoCtxImpl::list_snaps(const std::string& oid, snap_set_t *out_snaps) {
+ auto cct = m_client->cct();
+ ldout(cct, 20) << dendl;
+
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ out_snaps->seq = 0;
+ out_snaps->clones.clear();
+
+ std::shared_lock l{m_pool->file_lock};
+ TestMemCluster::Files::iterator it = m_pool->files.find(
+ {get_namespace(), oid});
+ if (it == m_pool->files.end()) {
+ return -ENOENT;
+ }
+
+ bool include_head = false;
+ TestMemCluster::FileSnapshots &file_snaps = it->second;
+ for (TestMemCluster::FileSnapshots::iterator s_it = file_snaps.begin();
+ s_it != file_snaps.end(); ++s_it) {
+ TestMemCluster::File &file = *s_it->get();
+
+ if (file_snaps.size() > 1) {
+ out_snaps->seq = file.snap_id;
+ TestMemCluster::FileSnapshots::iterator next_it(s_it);
+ ++next_it;
+ if (next_it == file_snaps.end()) {
+ include_head = true;
+ break;
+ }
+
+ ++out_snaps->seq;
+ if (!file.exists) {
+ continue;
+ }
+
+ // update the overlap with the next version's overlap metadata
+ TestMemCluster::File &next_file = *next_it->get();
+ interval_set<uint64_t> overlap;
+ if (next_file.exists) {
+ overlap = next_file.snap_overlap;
+ }
+
+ clone_info_t clone;
+ clone.cloneid = file.snap_id;
+ clone.snaps = file.snaps;
+ to_vector(overlap, &clone.overlap);
+ clone.size = file.data.length();
+ out_snaps->clones.push_back(clone);
+ }
+ }
+
+ if ((file_snaps.size() == 1 && file_snaps.back()->data.length() > 0) ||
+ include_head)
+ {
+ // Include the SNAP_HEAD
+ TestMemCluster::File &file = *file_snaps.back();
+ if (file.exists) {
+ std::shared_lock l2{file.lock};
+ if (out_snaps->seq == 0 && !include_head) {
+ out_snaps->seq = file.snap_id;
+ }
+ clone_info_t head_clone;
+ head_clone.cloneid = librados::SNAP_HEAD;
+ head_clone.size = file.data.length();
+ out_snaps->clones.push_back(head_clone);
+ }
+ }
+
+ ldout(cct, 20) << "seq=" << out_snaps->seq << ", "
+ << "clones=[";
+ bool first_clone = true;
+ for (auto& clone : out_snaps->clones) {
+ *_dout << "{"
+ << "cloneid=" << clone.cloneid << ", "
+ << "snaps=" << clone.snaps << ", "
+ << "overlap=" << clone.overlap << ", "
+ << "size=" << clone.size << "}";
+ if (!first_clone) {
+ *_dout << ", ";
+ } else {
+ first_clone = false;
+ }
+ }
+ *_dout << "]" << dendl;
+ return 0;
+
+}
+
+int TestMemIoCtxImpl::omap_get_vals2(const std::string& oid,
+ const std::string& start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ bool *pmore) {
+ if (out_vals == NULL) {
+ return -EINVAL;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ TestMemCluster::SharedFile file;
+ {
+ std::shared_lock l{m_pool->file_lock};
+ file = get_file(oid, false, CEPH_NOSNAP, {});
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ }
+
+ out_vals->clear();
+
+ std::shared_lock l{file->lock};
+ TestMemCluster::FileOMaps::iterator o_it = m_pool->file_omaps.find(
+ {get_namespace(), oid});
+ if (o_it == m_pool->file_omaps.end()) {
+ if (pmore) {
+ *pmore = false;
+ }
+ return 0;
+ }
+
+ TestMemCluster::OMap &omap = o_it->second;
+ TestMemCluster::OMap::iterator it = omap.begin();
+ if (!start_after.empty()) {
+ it = omap.upper_bound(start_after);
+ }
+
+ while (it != omap.end() && max_return > 0) {
+ if (filter_prefix.empty() ||
+ boost::algorithm::starts_with(it->first, filter_prefix)) {
+ (*out_vals)[it->first] = it->second;
+ --max_return;
+ }
+ ++it;
+ }
+ if (pmore) {
+ *pmore = (it != omap.end());
+ }
+ return 0;
+}
+
+int TestMemIoCtxImpl::omap_get_vals(const std::string& oid,
+ const std::string& start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals) {
+ return omap_get_vals2(oid, start_after, filter_prefix, max_return, out_vals, nullptr);
+}
+
+int TestMemIoCtxImpl::omap_rm_keys(const std::string& oid,
+ const std::set<std::string>& keys) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, true, CEPH_NOSNAP, get_snap_context());
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ }
+
+ std::unique_lock l{file->lock};
+ for (std::set<std::string>::iterator it = keys.begin();
+ it != keys.end(); ++it) {
+ m_pool->file_omaps[{get_namespace(), oid}].erase(*it);
+ }
+ return 0;
+}
+
+int TestMemIoCtxImpl::omap_set(const std::string& oid,
+ const std::map<std::string, bufferlist> &map) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, true, CEPH_NOSNAP, get_snap_context());
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ }
+
+ std::unique_lock l{file->lock};
+ for (std::map<std::string, bufferlist>::const_iterator it = map.begin();
+ it != map.end(); ++it) {
+ bufferlist bl;
+ bl.append(it->second);
+ m_pool->file_omaps[{get_namespace(), oid}][it->first] = bl;
+ }
+
+ return 0;
+}
+
+int TestMemIoCtxImpl::read(const std::string& oid, size_t len, uint64_t off,
+ bufferlist *bl, uint64_t snap_id,
+ uint64_t* objver) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ TestMemCluster::SharedFile file;
+ {
+ std::shared_lock l{m_pool->file_lock};
+ file = get_file(oid, false, snap_id, {});
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ }
+
+ std::shared_lock l{file->lock};
+ if (len == 0) {
+ len = file->data.length();
+ }
+ len = clip_io(off, len, file->data.length());
+ if (bl != NULL && len > 0) {
+ bufferlist bit;
+ bit.substr_of(file->data, off, len);
+ append_clone(bit, bl);
+ }
+ if (objver != nullptr) {
+ *objver = file->objver;
+ }
+ return len;
+}
+
+int TestMemIoCtxImpl::remove(const std::string& oid, const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ auto cct = m_client->cct();
+ ldout(cct, 20) << "snapc=" << snapc << dendl;
+
+ std::unique_lock l{m_pool->file_lock};
+ TestMemCluster::SharedFile file = get_file(oid, false, CEPH_NOSNAP, snapc);
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ file = get_file(oid, true, CEPH_NOSNAP, snapc);
+
+ {
+ std::unique_lock l2{file->lock};
+ file->exists = false;
+ }
+
+ TestCluster::ObjectLocator locator(get_namespace(), oid);
+ TestMemCluster::Files::iterator it = m_pool->files.find(locator);
+ ceph_assert(it != m_pool->files.end());
+
+ if (*it->second.rbegin() == file) {
+ TestMemCluster::ObjectHandlers object_handlers;
+ std::swap(object_handlers, m_pool->file_handlers[locator]);
+ m_pool->file_handlers.erase(locator);
+
+ for (auto object_handler : object_handlers) {
+ object_handler->handle_removed(m_client);
+ }
+ }
+
+ if (it->second.size() == 1) {
+ m_pool->files.erase(it);
+ m_pool->file_omaps.erase(locator);
+ }
+ return 0;
+}
+
+int TestMemIoCtxImpl::selfmanaged_snap_create(uint64_t *snapid) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ std::unique_lock l{m_pool->file_lock};
+ *snapid = ++m_pool->snap_id;
+ m_pool->snap_seqs.insert(*snapid);
+ return 0;
+}
+
+int TestMemIoCtxImpl::selfmanaged_snap_remove(uint64_t snapid) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ std::unique_lock l{m_pool->file_lock};
+ TestMemCluster::SnapSeqs::iterator it =
+ m_pool->snap_seqs.find(snapid);
+ if (it == m_pool->snap_seqs.end()) {
+ return -ENOENT;
+ }
+
+ // TODO clean up all file snapshots
+ m_pool->snap_seqs.erase(it);
+ return 0;
+}
+
+int TestMemIoCtxImpl::selfmanaged_snap_rollback(const std::string& oid,
+ uint64_t snapid) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ std::unique_lock l{m_pool->file_lock};
+
+ TestMemCluster::SharedFile file;
+ TestMemCluster::Files::iterator f_it = m_pool->files.find(
+ {get_namespace(), oid});
+ if (f_it == m_pool->files.end()) {
+ return 0;
+ }
+
+ TestMemCluster::FileSnapshots &snaps = f_it->second;
+ file = snaps.back();
+
+ size_t versions = 0;
+ for (TestMemCluster::FileSnapshots::reverse_iterator it = snaps.rbegin();
+ it != snaps.rend(); ++it) {
+ TestMemCluster::SharedFile file = *it;
+ if (file->snap_id < get_snap_read()) {
+ if (versions == 0) {
+ // already at the snapshot version
+ return 0;
+ } else if (file->snap_id == CEPH_NOSNAP) {
+ if (versions == 1) {
+ // delete it current HEAD, next one is correct version
+ snaps.erase(it.base());
+ } else {
+ // overwrite contents of current HEAD
+ file = TestMemCluster::SharedFile (new TestMemCluster::File(**it));
+ file->snap_id = CEPH_NOSNAP;
+ *it = file;
+ }
+ } else {
+ // create new head version
+ file = TestMemCluster::SharedFile (new TestMemCluster::File(**it));
+ file->snap_id = m_pool->snap_id;
+ snaps.push_back(file);
+ }
+ return 0;
+ }
+ ++versions;
+ }
+ return 0;
+}
+
+int TestMemIoCtxImpl::set_alloc_hint(const std::string& oid,
+ uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ uint32_t flags,
+ const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ {
+ std::unique_lock l{m_pool->file_lock};
+ get_file(oid, true, CEPH_NOSNAP, snapc);
+ }
+
+ return 0;
+}
+
+int TestMemIoCtxImpl::sparse_read(const std::string& oid, uint64_t off,
+ uint64_t len,
+ std::map<uint64_t,uint64_t> *m,
+ bufferlist *data_bl, uint64_t snap_id) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ // TODO verify correctness
+ TestMemCluster::SharedFile file;
+ {
+ std::shared_lock l{m_pool->file_lock};
+ file = get_file(oid, false, snap_id, {});
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ }
+
+ std::shared_lock l{file->lock};
+ len = clip_io(off, len, file->data.length());
+ // TODO support sparse read
+ if (m != NULL) {
+ m->clear();
+ if (len > 0) {
+ (*m)[off] = len;
+ }
+ }
+ if (data_bl != NULL && len > 0) {
+ bufferlist bit;
+ bit.substr_of(file->data, off, len);
+ append_clone(bit, data_bl);
+ }
+ return len > 0 ? 1 : 0;
+}
+
+int TestMemIoCtxImpl::stat(const std::string& oid, uint64_t *psize,
+ time_t *pmtime) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ TestMemCluster::SharedFile file;
+ {
+ std::shared_lock l{m_pool->file_lock};
+ file = get_file(oid, false, CEPH_NOSNAP, {});
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ }
+
+ std::shared_lock l{file->lock};
+ if (psize != NULL) {
+ *psize = file->data.length();
+ }
+ if (pmtime != NULL) {
+ *pmtime = file->mtime;
+ }
+ return 0;
+}
+
+int TestMemIoCtxImpl::truncate(const std::string& oid, uint64_t size,
+ const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ auto cct = m_client->cct();
+ ldout(cct, 20) << "size=" << size << ", snapc=" << snapc << dendl;
+
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, true, CEPH_NOSNAP, snapc);
+ }
+
+ std::unique_lock l{file->lock};
+ bufferlist bl(size);
+
+ interval_set<uint64_t> is;
+ if (file->data.length() > size) {
+ is.insert(size, file->data.length() - size);
+
+ bl.substr_of(file->data, 0, size);
+ file->data.swap(bl);
+ } else if (file->data.length() != size) {
+ if (size == 0) {
+ bl.clear();
+ } else {
+ is.insert(0, size);
+
+ bl.append_zero(size - file->data.length());
+ file->data.append(bl);
+ }
+ }
+ is.intersection_of(file->snap_overlap);
+ file->snap_overlap.subtract(is);
+ return 0;
+}
+
+int TestMemIoCtxImpl::write(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off, const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ auto cct = m_client->cct();
+ ldout(cct, 20) << "extent=" << off << "~" << len << ", snapc=" << snapc
+ << dendl;
+
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, true, CEPH_NOSNAP, snapc);
+ }
+
+ std::unique_lock l{file->lock};
+ if (len > 0) {
+ interval_set<uint64_t> is;
+ is.insert(off, len);
+ is.intersection_of(file->snap_overlap);
+ file->snap_overlap.subtract(is);
+ }
+
+ ensure_minimum_length(off + len, &file->data);
+ file->data.begin(off).copy_in(len, bl);
+ return 0;
+}
+
+int TestMemIoCtxImpl::write_full(const std::string& oid, bufferlist& bl,
+ const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ auto cct = m_client->cct();
+ ldout(cct, 20) << "length=" << bl.length() << ", snapc=" << snapc << dendl;
+
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, true, CEPH_NOSNAP, snapc);
+ if (file == NULL) {
+ return -ENOENT;
+ }
+ }
+
+ std::unique_lock l{file->lock};
+ if (bl.length() > 0) {
+ interval_set<uint64_t> is;
+ is.insert(0, bl.length());
+ is.intersection_of(file->snap_overlap);
+ file->snap_overlap.subtract(is);
+ }
+
+ file->data.clear();
+ ensure_minimum_length(bl.length(), &file->data);
+ file->data.begin().copy_in(bl.length(), bl);
+ return 0;
+}
+
+int TestMemIoCtxImpl::writesame(const std::string& oid, bufferlist& bl,
+ size_t len, uint64_t off,
+ const SnapContext &snapc) {
+ if (get_snap_read() != CEPH_NOSNAP) {
+ return -EROFS;
+ } else if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ if (len == 0 || (len % bl.length())) {
+ return -EINVAL;
+ }
+
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, true, CEPH_NOSNAP, snapc);
+ }
+
+ std::unique_lock l{file->lock};
+ if (len > 0) {
+ interval_set<uint64_t> is;
+ is.insert(off, len);
+ is.intersection_of(file->snap_overlap);
+ file->snap_overlap.subtract(is);
+ }
+
+ ensure_minimum_length(off + len, &file->data);
+ while (len > 0) {
+ file->data.begin(off).copy_in(bl.length(), bl);
+ off += bl.length();
+ len -= bl.length();
+ }
+ return 0;
+}
+
+int TestMemIoCtxImpl::cmpext(const std::string& oid, uint64_t off,
+ bufferlist& cmp_bl, uint64_t snap_id) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ bufferlist read_bl;
+ uint64_t len = cmp_bl.length();
+
+ TestMemCluster::SharedFile file;
+ {
+ std::shared_lock l{m_pool->file_lock};
+ file = get_file(oid, false, snap_id, {});
+ if (file == NULL) {
+ return cmpext_compare(cmp_bl, read_bl);
+ }
+ }
+
+ std::shared_lock l{file->lock};
+ if (off >= file->data.length()) {
+ len = 0;
+ } else if (off + len > file->data.length()) {
+ len = file->data.length() - off;
+ }
+ read_bl.substr_of(file->data, off, len);
+ return cmpext_compare(cmp_bl, read_bl);
+}
+
+int TestMemIoCtxImpl::xattr_get(const std::string& oid,
+ std::map<std::string, bufferlist>* attrset) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ TestMemCluster::SharedFile file;
+ std::shared_lock l{m_pool->file_lock};
+ TestMemCluster::FileXAttrs::iterator it = m_pool->file_xattrs.find(
+ {get_namespace(), oid});
+ if (it == m_pool->file_xattrs.end()) {
+ return -ENODATA;
+ }
+ *attrset = it->second;
+ return 0;
+}
+
+int TestMemIoCtxImpl::xattr_set(const std::string& oid, const std::string &name,
+ bufferlist& bl) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ std::unique_lock l{m_pool->file_lock};
+ m_pool->file_xattrs[{get_namespace(), oid}][name] = bl;
+ return 0;
+}
+
+int TestMemIoCtxImpl::zero(const std::string& oid, uint64_t off, uint64_t len,
+ const SnapContext &snapc) {
+ if (m_client->is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ auto cct = m_client->cct();
+ ldout(cct, 20) << "extent=" << off << "~" << len << ", snapc=" << snapc
+ << dendl;
+
+ bool truncate_redirect = false;
+ TestMemCluster::SharedFile file;
+ {
+ std::unique_lock l{m_pool->file_lock};
+ file = get_file(oid, false, CEPH_NOSNAP, snapc);
+ if (!file) {
+ return 0;
+ }
+ file = get_file(oid, true, CEPH_NOSNAP, snapc);
+
+ std::shared_lock l2{file->lock};
+ if (len > 0 && off + len >= file->data.length()) {
+ // Zero -> Truncate logic embedded in OSD
+ truncate_redirect = true;
+ }
+ }
+ if (truncate_redirect) {
+ return truncate(oid, off, snapc);
+ }
+
+ bufferlist bl;
+ bl.append_zero(len);
+ return write(oid, bl, len, off, snapc);
+}
+
+void TestMemIoCtxImpl::append_clone(bufferlist& src, bufferlist* dest) {
+ // deep-copy the src to ensure our memory-based mock RADOS data cannot
+ // be modified by callers
+ if (src.length() > 0) {
+ bufferlist::iterator iter = src.begin();
+ buffer::ptr ptr;
+ iter.copy_deep(src.length(), ptr);
+ dest->append(ptr);
+ }
+}
+
+size_t TestMemIoCtxImpl::clip_io(size_t off, size_t len, size_t bl_len) {
+ if (off >= bl_len) {
+ len = 0;
+ } else if (off + len > bl_len) {
+ len = bl_len - off;
+ }
+ return len;
+}
+
+void TestMemIoCtxImpl::ensure_minimum_length(size_t len, bufferlist *bl) {
+ if (len > bl->length()) {
+ bufferptr ptr(buffer::create(len - bl->length()));
+ ptr.zero();
+ bl->append(ptr);
+ }
+}
+
+TestMemCluster::SharedFile TestMemIoCtxImpl::get_file(
+ const std::string &oid, bool write, uint64_t snap_id,
+ const SnapContext &snapc) {
+ ceph_assert(ceph_mutex_is_locked(m_pool->file_lock) ||
+ ceph_mutex_is_wlocked(m_pool->file_lock));
+ ceph_assert(!write || ceph_mutex_is_wlocked(m_pool->file_lock));
+
+ TestMemCluster::SharedFile file;
+ TestMemCluster::Files::iterator it = m_pool->files.find(
+ {get_namespace(), oid});
+ if (it != m_pool->files.end()) {
+ file = it->second.back();
+ } else if (!write) {
+ return TestMemCluster::SharedFile();
+ }
+
+ if (write) {
+ bool new_version = false;
+ if (!file || !file->exists) {
+ file = TestMemCluster::SharedFile(new TestMemCluster::File());
+ new_version = true;
+ } else {
+ if (!snapc.snaps.empty() && file->snap_id < snapc.seq) {
+ for (std::vector<snapid_t>::const_reverse_iterator seq_it =
+ snapc.snaps.rbegin();
+ seq_it != snapc.snaps.rend(); ++seq_it) {
+ if (*seq_it > file->snap_id && *seq_it <= snapc.seq) {
+ file->snaps.push_back(*seq_it);
+ }
+ }
+
+ bufferlist prev_data = file->data;
+ file = TestMemCluster::SharedFile(
+ new TestMemCluster::File(*file));
+ file->data.clear();
+ append_clone(prev_data, &file->data);
+ if (prev_data.length() > 0) {
+ file->snap_overlap.insert(0, prev_data.length());
+ }
+ new_version = true;
+ }
+ }
+
+ if (new_version) {
+ file->snap_id = snapc.seq;
+ file->mtime = ceph_clock_now().sec();
+ m_pool->files[{get_namespace(), oid}].push_back(file);
+ }
+
+ file->objver++;
+ return file;
+ }
+
+ if (snap_id == CEPH_NOSNAP) {
+ if (!file->exists) {
+ ceph_assert(it->second.size() > 1);
+ return TestMemCluster::SharedFile();
+ }
+ return file;
+ }
+
+ TestMemCluster::FileSnapshots &snaps = it->second;
+ for (TestMemCluster::FileSnapshots::reverse_iterator it = snaps.rbegin();
+ it != snaps.rend(); ++it) {
+ TestMemCluster::SharedFile file = *it;
+ if (file->snap_id < snap_id) {
+ if (!file->exists) {
+ return TestMemCluster::SharedFile();
+ }
+ return file;
+ }
+ }
+ return TestMemCluster::SharedFile();
+}
+
+} // namespace librados
diff --git a/src/test/librados_test_stub/TestMemIoCtxImpl.h b/src/test/librados_test_stub/TestMemIoCtxImpl.h
new file mode 100644
index 000000000..4706f46d2
--- /dev/null
+++ b/src/test/librados_test_stub/TestMemIoCtxImpl.h
@@ -0,0 +1,104 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_MEM_IO_CTX_IMPL_H
+#define CEPH_TEST_MEM_IO_CTX_IMPL_H
+
+#include "test/librados_test_stub/TestIoCtxImpl.h"
+#include "test/librados_test_stub/TestMemCluster.h"
+
+namespace librados {
+
+class TestMemRadosClient;
+
+class TestMemIoCtxImpl : public TestIoCtxImpl {
+public:
+ TestMemIoCtxImpl();
+ TestMemIoCtxImpl(TestMemRadosClient *client, int64_t m_pool_id,
+ const std::string& pool_name,
+ TestMemCluster::Pool *pool);
+ ~TestMemIoCtxImpl() override;
+
+ TestIoCtxImpl *clone() override;
+
+ int aio_remove(const std::string& oid, AioCompletionImpl *c, int flags = 0) override;
+
+ int append(const std::string& oid, const bufferlist &bl,
+ const SnapContext &snapc) override;
+
+ int assert_exists(const std::string &oid, uint64_t snap_id) override;
+ int assert_version(const std::string &oid, uint64_t ver) override;
+
+ int create(const std::string& oid, bool exclusive,
+ const SnapContext &snapc) override;
+ int list_snaps(const std::string& o, snap_set_t *out_snaps) override;
+ int omap_get_vals(const std::string& oid,
+ const std::string& start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals) override;
+ int omap_get_vals2(const std::string& oid,
+ const std::string& start_after,
+ const std::string &filter_prefix,
+ uint64_t max_return,
+ std::map<std::string, bufferlist> *out_vals,
+ bool *pmore) override;
+ int omap_rm_keys(const std::string& oid,
+ const std::set<std::string>& keys) override;
+ int omap_set(const std::string& oid, const std::map<std::string,
+ bufferlist> &map) override;
+ int read(const std::string& oid, size_t len, uint64_t off,
+ bufferlist *bl, uint64_t snap_id, uint64_t* objver) override;
+ int remove(const std::string& oid, const SnapContext &snapc) override;
+ int selfmanaged_snap_create(uint64_t *snapid) override;
+ int selfmanaged_snap_remove(uint64_t snapid) override;
+ int selfmanaged_snap_rollback(const std::string& oid,
+ uint64_t snapid) override;
+ int set_alloc_hint(const std::string& oid, uint64_t expected_object_size,
+ uint64_t expected_write_size, uint32_t flags,
+ const SnapContext &snapc) override;
+ int sparse_read(const std::string& oid, uint64_t off, uint64_t len,
+ std::map<uint64_t,uint64_t> *m, bufferlist *data_bl,
+ uint64_t snap_id) override;
+ int stat(const std::string& oid, uint64_t *psize, time_t *pmtime) override;
+ int truncate(const std::string& oid, uint64_t size,
+ const SnapContext &snapc) override;
+ int write(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off, const SnapContext &snapc) override;
+ int write_full(const std::string& oid, bufferlist& bl,
+ const SnapContext &snapc) override;
+ int writesame(const std::string& oid, bufferlist& bl, size_t len,
+ uint64_t off, const SnapContext &snapc) override;
+ int cmpext(const std::string& oid, uint64_t off, bufferlist& cmp_bl,
+ uint64_t snap_id) override;
+ int xattr_get(const std::string& oid,
+ std::map<std::string, bufferlist>* attrset) override;
+ int xattr_set(const std::string& oid, const std::string &name,
+ bufferlist& bl) override;
+ int zero(const std::string& oid, uint64_t off, uint64_t len,
+ const SnapContext &snapc) override;
+
+protected:
+ TestMemCluster::Pool *get_pool() {
+ return m_pool;
+ }
+
+private:
+ TestMemIoCtxImpl(const TestMemIoCtxImpl&);
+
+ TestMemRadosClient *m_client = nullptr;
+ TestMemCluster::Pool *m_pool = nullptr;
+
+ void append_clone(bufferlist& src, bufferlist* dest);
+ size_t clip_io(size_t off, size_t len, size_t bl_len);
+ void ensure_minimum_length(size_t len, bufferlist *bl);
+
+ TestMemCluster::SharedFile get_file(const std::string &oid, bool write,
+ uint64_t snap_id,
+ const SnapContext &snapc);
+
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_MEM_IO_CTX_IMPL_H
diff --git a/src/test/librados_test_stub/TestMemRadosClient.cc b/src/test/librados_test_stub/TestMemRadosClient.cc
new file mode 100644
index 000000000..37d45327c
--- /dev/null
+++ b/src/test/librados_test_stub/TestMemRadosClient.cc
@@ -0,0 +1,118 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/TestMemRadosClient.h"
+#include "test/librados_test_stub/TestMemCluster.h"
+#include "test/librados_test_stub/TestMemIoCtxImpl.h"
+#include <errno.h>
+#include <sstream>
+
+namespace librados {
+
+TestMemRadosClient::TestMemRadosClient(CephContext *cct,
+ TestMemCluster *test_mem_cluster)
+ : TestRadosClient(cct, test_mem_cluster->get_watch_notify()),
+ m_mem_cluster(test_mem_cluster) {
+ m_mem_cluster->allocate_client(&m_nonce, &m_global_id);
+}
+
+TestMemRadosClient::~TestMemRadosClient() {
+ m_mem_cluster->deallocate_client(m_nonce);
+}
+
+TestIoCtxImpl *TestMemRadosClient::create_ioctx(int64_t pool_id,
+ const std::string &pool_name) {
+ return new TestMemIoCtxImpl(this, pool_id, pool_name,
+ m_mem_cluster->get_pool(pool_name));
+}
+
+void TestMemRadosClient::object_list(int64_t pool_id,
+ std::list<librados::TestRadosClient::Object> *list) {
+ list->clear();
+
+ auto pool = m_mem_cluster->get_pool(pool_id);
+ if (pool != nullptr) {
+ std::shared_lock file_locker{pool->file_lock};
+ for (auto &file_pair : pool->files) {
+ Object obj;
+ obj.oid = file_pair.first.name;
+ list->push_back(obj);
+ }
+ }
+}
+
+int TestMemRadosClient::pool_create(const std::string &pool_name) {
+ if (is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+ return m_mem_cluster->pool_create(pool_name);
+}
+
+int TestMemRadosClient::pool_delete(const std::string &pool_name) {
+ if (is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+ return m_mem_cluster->pool_delete(pool_name);
+}
+
+int TestMemRadosClient::pool_get_base_tier(int64_t pool_id, int64_t* base_tier) {
+ // TODO
+ *base_tier = pool_id;
+ return 0;
+}
+
+int TestMemRadosClient::pool_list(std::list<std::pair<int64_t, std::string> >& v) {
+ return m_mem_cluster->pool_list(v);
+}
+
+int64_t TestMemRadosClient::pool_lookup(const std::string &pool_name) {
+ return m_mem_cluster->pool_lookup(pool_name);
+}
+
+int TestMemRadosClient::pool_reverse_lookup(int64_t id, std::string *name) {
+ return m_mem_cluster->pool_reverse_lookup(id, name);
+}
+
+int TestMemRadosClient::watch_flush() {
+ get_watch_notify()->flush(this);
+ return 0;
+}
+
+bool TestMemRadosClient::is_blocklisted() const {
+ return m_mem_cluster->is_blocklisted(m_nonce);
+}
+
+int TestMemRadosClient::blocklist_add(const std::string& client_address,
+ uint32_t expire_seconds) {
+ if (is_blocklisted()) {
+ return -EBLOCKLISTED;
+ }
+
+ // extract the nonce to use as a unique key to the client
+ auto idx = client_address.find("/");
+ if (idx == std::string::npos || idx + 1 >= client_address.size()) {
+ return -EINVAL;
+ }
+
+ std::stringstream nonce_ss(client_address.substr(idx + 1));
+ uint32_t nonce;
+ nonce_ss >> nonce;
+ if (!nonce_ss) {
+ return -EINVAL;
+ }
+
+ m_mem_cluster->blocklist(nonce);
+ return 0;
+}
+
+void TestMemRadosClient::transaction_start(const std::string& nspace,
+ const std::string &oid) {
+ m_mem_cluster->transaction_start({nspace, oid});
+}
+
+void TestMemRadosClient::transaction_finish(const std::string& nspace,
+ const std::string &oid) {
+ m_mem_cluster->transaction_finish({nspace, oid});
+}
+
+} // namespace librados
diff --git a/src/test/librados_test_stub/TestMemRadosClient.h b/src/test/librados_test_stub/TestMemRadosClient.h
new file mode 100644
index 000000000..ecd6fedb0
--- /dev/null
+++ b/src/test/librados_test_stub/TestMemRadosClient.h
@@ -0,0 +1,88 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_MEM_RADOS_CLIENT_H
+#define CEPH_TEST_MEM_RADOS_CLIENT_H
+
+#include "test/librados_test_stub/TestRadosClient.h"
+#include "include/ceph_assert.h"
+#include <list>
+#include <string>
+
+namespace librados {
+
+class AioCompletionImpl;
+class TestMemCluster;
+
+class TestMemRadosClient : public TestRadosClient {
+public:
+ TestMemRadosClient(CephContext *cct, TestMemCluster *test_mem_cluster);
+ ~TestMemRadosClient() override;
+
+ TestIoCtxImpl *create_ioctx(int64_t pool_id,
+ const std::string &pool_name) override;
+
+ uint32_t get_nonce() override {
+ return m_nonce;
+ }
+ uint64_t get_instance_id() override {
+ return m_global_id;
+ }
+
+ int get_min_compatible_osd(int8_t* require_osd_release) override {
+ *require_osd_release = CEPH_RELEASE_OCTOPUS;
+ return 0;
+ }
+
+ int get_min_compatible_client(int8_t* min_compat_client,
+ int8_t* require_min_compat_client) override {
+ *min_compat_client = CEPH_RELEASE_MIMIC;
+ *require_min_compat_client = CEPH_RELEASE_MIMIC;
+ return 0;
+ }
+
+ void object_list(int64_t pool_id,
+ std::list<librados::TestRadosClient::Object> *list) override;
+
+ int service_daemon_register(const std::string& service,
+ const std::string& name,
+ const std::map<std::string,std::string>& metadata) override {
+ return 0;
+ }
+ int service_daemon_update_status(std::map<std::string,std::string>&& status) override {
+ return 0;
+ }
+
+ int pool_create(const std::string &pool_name) override;
+ int pool_delete(const std::string &pool_name) override;
+ int pool_get_base_tier(int64_t pool_id, int64_t* base_tier) override;
+ int pool_list(std::list<std::pair<int64_t, std::string> >& v) override;
+ int64_t pool_lookup(const std::string &name) override;
+ int pool_reverse_lookup(int64_t id, std::string *name) override;
+
+ int watch_flush() override;
+
+ bool is_blocklisted() const override;
+ int blocklist_add(const std::string& client_address,
+ uint32_t expire_seconds) override;
+protected:
+ TestMemCluster *get_mem_cluster() {
+ return m_mem_cluster;
+ }
+
+protected:
+ void transaction_start(const std::string& nspace,
+ const std::string &oid) override;
+ void transaction_finish(const std::string& nspace,
+ const std::string &oid) override;
+
+private:
+ TestMemCluster *m_mem_cluster;
+ uint32_t m_nonce;
+ uint64_t m_global_id;
+
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_MEM_RADOS_CLIENT_H
diff --git a/src/test/librados_test_stub/TestRadosClient.cc b/src/test/librados_test_stub/TestRadosClient.cc
new file mode 100644
index 000000000..1f49aba93
--- /dev/null
+++ b/src/test/librados_test_stub/TestRadosClient.cc
@@ -0,0 +1,311 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/TestRadosClient.h"
+#include "test/librados_test_stub/TestIoCtxImpl.h"
+#include "librados/AioCompletionImpl.h"
+#include "include/ceph_assert.h"
+#include "common/ceph_json.h"
+#include "common/Finisher.h"
+#include "common/async/context_pool.h"
+#include <boost/lexical_cast.hpp>
+#include <boost/thread.hpp>
+#include <errno.h>
+
+#include <atomic>
+#include <functional>
+#include <sstream>
+
+static int get_concurrency() {
+ int concurrency = 0;
+ char *env = getenv("LIBRADOS_CONCURRENCY");
+ if (env != NULL) {
+ concurrency = atoi(env);
+ }
+ if (concurrency == 0) {
+ concurrency = boost::thread::thread::hardware_concurrency();
+ }
+ if (concurrency == 0) {
+ concurrency = 1;
+ }
+ return concurrency;
+}
+
+using namespace std::placeholders;
+
+namespace librados {
+
+namespace {
+
+const char *config_keys[] = {
+ "librados_thread_count",
+ NULL
+};
+
+} // anonymous namespace
+
+static void finish_aio_completion(AioCompletionImpl *c, int r) {
+ c->lock.lock();
+ c->complete = true;
+ c->rval = r;
+ c->lock.unlock();
+
+ rados_callback_t cb_complete = c->callback_complete;
+ void *cb_complete_arg = c->callback_complete_arg;
+ if (cb_complete) {
+ cb_complete(c, cb_complete_arg);
+ }
+
+ rados_callback_t cb_safe = c->callback_safe;
+ void *cb_safe_arg = c->callback_safe_arg;
+ if (cb_safe) {
+ cb_safe(c, cb_safe_arg);
+ }
+
+ c->lock.lock();
+ c->callback_complete = NULL;
+ c->callback_safe = NULL;
+ c->cond.notify_all();
+ c->put_unlock();
+}
+
+class AioFunctionContext : public Context {
+public:
+ AioFunctionContext(const TestRadosClient::AioFunction &callback,
+ Finisher *finisher, AioCompletionImpl *c)
+ : m_callback(callback), m_finisher(finisher), m_comp(c)
+ {
+ if (m_comp != NULL) {
+ m_comp->get();
+ }
+ }
+
+ void finish(int r) override {
+ int ret = m_callback();
+ if (m_comp != NULL) {
+ if (m_finisher != NULL) {
+ m_finisher->queue(new LambdaContext(std::bind(
+ &finish_aio_completion, m_comp, ret)));
+ } else {
+ finish_aio_completion(m_comp, ret);
+ }
+ }
+ }
+private:
+ TestRadosClient::AioFunction m_callback;
+ Finisher *m_finisher;
+ AioCompletionImpl *m_comp;
+};
+
+TestRadosClient::TestRadosClient(CephContext *cct,
+ TestWatchNotify *watch_notify)
+ : m_cct(cct->get()), m_watch_notify(watch_notify),
+ m_aio_finisher(new Finisher(m_cct)),
+ m_io_context_pool(std::make_unique<ceph::async::io_context_pool>())
+{
+ get();
+
+ // simulate multiple OSDs
+ int concurrency = get_concurrency();
+ for (int i = 0; i < concurrency; ++i) {
+ m_finishers.push_back(new Finisher(m_cct));
+ m_finishers.back()->start();
+ }
+
+ // replicate AIO callback processing
+ m_aio_finisher->start();
+
+ // replicate neorados callback processing
+ m_cct->_conf.add_observer(this);
+ m_io_context_pool->start(m_cct->_conf.get_val<uint64_t>(
+ "librados_thread_count"));
+}
+
+TestRadosClient::~TestRadosClient() {
+ flush_aio_operations();
+
+ for (size_t i = 0; i < m_finishers.size(); ++i) {
+ m_finishers[i]->stop();
+ delete m_finishers[i];
+ }
+ m_aio_finisher->stop();
+ delete m_aio_finisher;
+
+ m_cct->_conf.remove_observer(this);
+ m_io_context_pool->stop();
+
+ m_cct->put();
+ m_cct = NULL;
+}
+
+boost::asio::io_context& TestRadosClient::get_io_context() {
+ return m_io_context_pool->get_io_context();
+}
+
+const char** TestRadosClient::get_tracked_conf_keys() const {
+ return config_keys;
+}
+
+void TestRadosClient::handle_conf_change(
+ const ConfigProxy& conf, const std::set<std::string> &changed) {
+ if (changed.count("librados_thread_count")) {
+ m_io_context_pool->stop();
+ m_io_context_pool->start(conf.get_val<std::uint64_t>(
+ "librados_thread_count"));
+ }
+}
+
+void TestRadosClient::get() {
+ m_refcount++;
+}
+
+void TestRadosClient::put() {
+ if (--m_refcount == 0) {
+ shutdown();
+ delete this;
+ }
+}
+
+CephContext *TestRadosClient::cct() {
+ return m_cct;
+}
+
+int TestRadosClient::connect() {
+ return 0;
+}
+
+void TestRadosClient::shutdown() {
+}
+
+int TestRadosClient::wait_for_latest_osdmap() {
+ return 0;
+}
+
+int TestRadosClient::mon_command(const std::vector<std::string>& cmd,
+ const bufferlist &inbl,
+ bufferlist *outbl, std::string *outs) {
+ for (std::vector<std::string>::const_iterator it = cmd.begin();
+ it != cmd.end(); ++it) {
+ JSONParser parser;
+ if (!parser.parse(it->c_str(), it->length())) {
+ return -EINVAL;
+ }
+
+ JSONObjIter j_it = parser.find("prefix");
+ if (j_it.end()) {
+ return -EINVAL;
+ }
+
+ if ((*j_it)->get_data() == "osd tier add") {
+ return 0;
+ } else if ((*j_it)->get_data() == "osd tier cache-mode") {
+ return 0;
+ } else if ((*j_it)->get_data() == "osd tier set-overlay") {
+ return 0;
+ } else if ((*j_it)->get_data() == "osd tier remove-overlay") {
+ return 0;
+ } else if ((*j_it)->get_data() == "osd tier remove") {
+ return 0;
+ } else if ((*j_it)->get_data() == "config-key rm") {
+ return 0;
+ } else if ((*j_it)->get_data() == "config set") {
+ return 0;
+ } else if ((*j_it)->get_data() == "df") {
+ std::stringstream str;
+ str << R"({"pools": [)";
+
+ std::list<std::pair<int64_t, std::string>> pools;
+ pool_list(pools);
+ for (auto& pool : pools) {
+ if (pools.begin()->first != pool.first) {
+ str << ",";
+ }
+ str << R"({"name": ")" << pool.second << R"(", "stats": )"
+ << R"({"percent_used": 1.0, "bytes_used": 0, "max_avail": 0}})";
+ }
+
+ str << "]}";
+ outbl->append(str.str());
+ return 0;
+ } else if ((*j_it)->get_data() == "osd blocklist") {
+ auto op_it = parser.find("blocklistop");
+ if (!op_it.end() && (*op_it)->get_data() == "add") {
+ uint32_t expire = 0;
+ auto expire_it = parser.find("expire");
+ if (!expire_it.end()) {
+ expire = boost::lexical_cast<uint32_t>((*expire_it)->get_data());
+ }
+
+ auto addr_it = parser.find("addr");
+ return blocklist_add((*addr_it)->get_data(), expire);
+ }
+ }
+ }
+ return -ENOSYS;
+}
+
+void TestRadosClient::add_aio_operation(const std::string& oid,
+ bool queue_callback,
+ const AioFunction &aio_function,
+ AioCompletionImpl *c) {
+ AioFunctionContext *ctx = new AioFunctionContext(
+ aio_function, queue_callback ? m_aio_finisher : NULL, c);
+ get_finisher(oid)->queue(ctx);
+}
+
+struct WaitForFlush {
+ int flushed() {
+ if (--count == 0) {
+ aio_finisher->queue(new LambdaContext(std::bind(
+ &finish_aio_completion, c, 0)));
+ delete this;
+ }
+ return 0;
+ }
+
+ std::atomic<int64_t> count = { 0 };
+ Finisher *aio_finisher;
+ AioCompletionImpl *c;
+};
+
+void TestRadosClient::flush_aio_operations() {
+ AioCompletionImpl *comp = new AioCompletionImpl();
+ flush_aio_operations(comp);
+ comp->wait_for_complete();
+ comp->put();
+}
+
+void TestRadosClient::flush_aio_operations(AioCompletionImpl *c) {
+ c->get();
+
+ WaitForFlush *wait_for_flush = new WaitForFlush();
+ wait_for_flush->count = m_finishers.size();
+ wait_for_flush->aio_finisher = m_aio_finisher;
+ wait_for_flush->c = c;
+
+ for (size_t i = 0; i < m_finishers.size(); ++i) {
+ AioFunctionContext *ctx = new AioFunctionContext(
+ std::bind(&WaitForFlush::flushed, wait_for_flush),
+ nullptr, nullptr);
+ m_finishers[i]->queue(ctx);
+ }
+}
+
+int TestRadosClient::aio_watch_flush(AioCompletionImpl *c) {
+ c->get();
+ Context *ctx = new LambdaContext(std::bind(
+ &TestRadosClient::finish_aio_completion, this, c, std::placeholders::_1));
+ get_watch_notify()->aio_flush(this, ctx);
+ return 0;
+}
+
+void TestRadosClient::finish_aio_completion(AioCompletionImpl *c, int r) {
+ librados::finish_aio_completion(c, r);
+}
+
+Finisher *TestRadosClient::get_finisher(const std::string &oid) {
+ std::size_t h = m_hash(oid);
+ return m_finishers[h % m_finishers.size()];
+}
+
+} // namespace librados
diff --git a/src/test/librados_test_stub/TestRadosClient.h b/src/test/librados_test_stub/TestRadosClient.h
new file mode 100644
index 000000000..e7f8d0751
--- /dev/null
+++ b/src/test/librados_test_stub/TestRadosClient.h
@@ -0,0 +1,162 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_RADOS_CLIENT_H
+#define CEPH_TEST_RADOS_CLIENT_H
+
+#include <map>
+#include <memory>
+#include <list>
+#include <string>
+#include <vector>
+#include <atomic>
+
+#include <boost/function.hpp>
+#include <boost/functional/hash.hpp>
+
+#include "include/rados/librados.hpp"
+#include "common/config.h"
+#include "common/config_obs.h"
+#include "include/buffer_fwd.h"
+#include "test/librados_test_stub/TestWatchNotify.h"
+
+class Finisher;
+
+namespace boost { namespace asio { struct io_context; }}
+namespace ceph { namespace async { struct io_context_pool; }}
+
+namespace librados {
+
+class TestIoCtxImpl;
+
+class TestRadosClient : public md_config_obs_t {
+public:
+
+ static void Deallocate(librados::TestRadosClient* client)
+ {
+ client->put();
+ }
+
+ typedef boost::function<int()> AioFunction;
+
+ struct Object {
+ std::string oid;
+ std::string locator;
+ std::string nspace;
+ };
+
+ class Transaction {
+ public:
+ Transaction(TestRadosClient *rados_client, const std::string& nspace,
+ const std::string &oid)
+ : rados_client(rados_client), nspace(nspace), oid(oid) {
+ rados_client->transaction_start(nspace, oid);
+ }
+ ~Transaction() {
+ rados_client->transaction_finish(nspace, oid);
+ }
+ private:
+ TestRadosClient *rados_client;
+ std::string nspace;
+ std::string oid;
+ };
+
+ TestRadosClient(CephContext *cct, TestWatchNotify *watch_notify);
+
+ void get();
+ void put();
+
+ virtual CephContext *cct();
+
+ virtual uint32_t get_nonce() = 0;
+ virtual uint64_t get_instance_id() = 0;
+
+ virtual int get_min_compatible_osd(int8_t* require_osd_release) = 0;
+ virtual int get_min_compatible_client(int8_t* min_compat_client,
+ int8_t* require_min_compat_client) = 0;
+
+ virtual int connect();
+ virtual void shutdown();
+ virtual int wait_for_latest_osdmap();
+
+ virtual TestIoCtxImpl *create_ioctx(int64_t pool_id,
+ const std::string &pool_name) = 0;
+
+ virtual int mon_command(const std::vector<std::string>& cmd,
+ const bufferlist &inbl,
+ bufferlist *outbl, std::string *outs);
+
+ virtual void object_list(int64_t pool_id,
+ std::list<librados::TestRadosClient::Object> *list) = 0;
+
+ virtual int service_daemon_register(const std::string& service,
+ const std::string& name,
+ const std::map<std::string,std::string>& metadata) = 0;
+ virtual int service_daemon_update_status(std::map<std::string,std::string>&& status) = 0;
+
+ virtual int pool_create(const std::string &pool_name) = 0;
+ virtual int pool_delete(const std::string &pool_name) = 0;
+ virtual int pool_get_base_tier(int64_t pool_id, int64_t* base_tier) = 0;
+ virtual int pool_list(std::list<std::pair<int64_t, std::string> >& v) = 0;
+ virtual int64_t pool_lookup(const std::string &name) = 0;
+ virtual int pool_reverse_lookup(int64_t id, std::string *name) = 0;
+
+ virtual int aio_watch_flush(AioCompletionImpl *c);
+ virtual int watch_flush() = 0;
+
+ virtual bool is_blocklisted() const = 0;
+ virtual int blocklist_add(const std::string& client_address,
+ uint32_t expire_seconds) = 0;
+
+ virtual int wait_for_latest_osd_map() {
+ return 0;
+ }
+
+ Finisher *get_aio_finisher() {
+ return m_aio_finisher;
+ }
+ TestWatchNotify *get_watch_notify() {
+ return m_watch_notify;
+ }
+
+ void add_aio_operation(const std::string& oid, bool queue_callback,
+ const AioFunction &aio_function, AioCompletionImpl *c);
+ void flush_aio_operations();
+ void flush_aio_operations(AioCompletionImpl *c);
+
+ void finish_aio_completion(AioCompletionImpl *c, int r);
+
+ boost::asio::io_context& get_io_context();
+
+protected:
+ virtual ~TestRadosClient();
+
+ virtual void transaction_start(const std::string& nspace,
+ const std::string &oid) = 0;
+ virtual void transaction_finish(const std::string& nspace,
+ const std::string &oid) = 0;
+
+ const char** get_tracked_conf_keys() const override;
+ void handle_conf_change(const ConfigProxy& conf,
+ const std::set<std::string> &changed) override;
+
+private:
+ struct IOContextPool;
+
+ CephContext *m_cct;
+ std::atomic<uint64_t> m_refcount = { 0 };
+
+ TestWatchNotify *m_watch_notify;
+
+ Finisher *get_finisher(const std::string& oid);
+
+ Finisher *m_aio_finisher;
+ std::vector<Finisher *> m_finishers;
+ boost::hash<std::string> m_hash;
+
+ std::unique_ptr<ceph::async::io_context_pool> m_io_context_pool;
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_RADOS_CLIENT_H
diff --git a/src/test/librados_test_stub/TestWatchNotify.cc b/src/test/librados_test_stub/TestWatchNotify.cc
new file mode 100644
index 000000000..93875182c
--- /dev/null
+++ b/src/test/librados_test_stub/TestWatchNotify.cc
@@ -0,0 +1,459 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados_test_stub/TestWatchNotify.h"
+#include "include/Context.h"
+#include "common/Cond.h"
+#include "include/stringify.h"
+#include "common/Finisher.h"
+#include "test/librados_test_stub/TestCluster.h"
+#include "test/librados_test_stub/TestRadosClient.h"
+#include <boost/bind/bind.hpp>
+#include <boost/function.hpp>
+#include "include/ceph_assert.h"
+
+#define dout_subsys ceph_subsys_rados
+#undef dout_prefix
+#define dout_prefix *_dout << "TestWatchNotify::" << __func__ << ": "
+
+namespace librados {
+
+std::ostream& operator<<(std::ostream& out,
+ const TestWatchNotify::WatcherID &watcher_id) {
+ out << "(" << watcher_id.first << "," << watcher_id.second << ")";
+ return out;
+}
+
+struct TestWatchNotify::ObjectHandler : public TestCluster::ObjectHandler {
+ TestWatchNotify* test_watch_notify;
+ int64_t pool_id;
+ std::string nspace;
+ std::string oid;
+
+ ObjectHandler(TestWatchNotify* test_watch_notify, int64_t pool_id,
+ const std::string& nspace, const std::string& oid)
+ : test_watch_notify(test_watch_notify), pool_id(pool_id),
+ nspace(nspace), oid(oid) {
+ }
+
+ void handle_removed(TestRadosClient* test_rados_client) override {
+ // copy member variables since this object might be deleted
+ auto _test_watch_notify = test_watch_notify;
+ auto _pool_id = pool_id;
+ auto _nspace = nspace;
+ auto _oid = oid;
+ auto ctx = new LambdaContext([_test_watch_notify, _pool_id, _nspace, _oid](int r) {
+ _test_watch_notify->handle_object_removed(_pool_id, _nspace, _oid);
+ });
+ test_rados_client->get_aio_finisher()->queue(ctx);
+ }
+};
+
+TestWatchNotify::TestWatchNotify(TestCluster* test_cluster)
+ : m_test_cluster(test_cluster) {
+}
+
+void TestWatchNotify::flush(TestRadosClient *rados_client) {
+ CephContext *cct = rados_client->cct();
+
+ ldout(cct, 20) << "enter" << dendl;
+ // block until we know no additional async notify callbacks will occur
+ C_SaferCond ctx;
+ m_async_op_tracker.wait_for_ops(&ctx);
+ ctx.wait();
+}
+
+int TestWatchNotify::list_watchers(int64_t pool_id, const std::string& nspace,
+ const std::string& o,
+ std::list<obj_watch_t> *out_watchers) {
+ std::lock_guard lock{m_lock};
+ SharedWatcher watcher = get_watcher(pool_id, nspace, o);
+ if (!watcher) {
+ return -ENOENT;
+ }
+
+ out_watchers->clear();
+ for (TestWatchNotify::WatchHandles::iterator it =
+ watcher->watch_handles.begin();
+ it != watcher->watch_handles.end(); ++it) {
+ obj_watch_t obj;
+ strncpy(obj.addr, it->second.addr.c_str(), sizeof(obj.addr) - 1);
+ obj.addr[sizeof(obj.addr) - 1] = '\0';
+ obj.watcher_id = static_cast<int64_t>(it->second.gid);
+ obj.cookie = it->second.handle;
+ obj.timeout_seconds = 30;
+ out_watchers->push_back(obj);
+ }
+ return 0;
+}
+
+void TestWatchNotify::aio_flush(TestRadosClient *rados_client,
+ Context *on_finish) {
+ rados_client->get_aio_finisher()->queue(on_finish);
+}
+
+int TestWatchNotify::watch(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& o,
+ uint64_t gid, uint64_t *handle,
+ librados::WatchCtx *ctx, librados::WatchCtx2 *ctx2) {
+ C_SaferCond cond;
+ aio_watch(rados_client, pool_id, nspace, o, gid, handle, ctx, ctx2, &cond);
+ return cond.wait();
+}
+
+void TestWatchNotify::aio_watch(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& o,
+ uint64_t gid, uint64_t *handle,
+ librados::WatchCtx *watch_ctx,
+ librados::WatchCtx2 *watch_ctx2,
+ Context *on_finish) {
+ auto ctx = new LambdaContext([=, this](int) {
+ execute_watch(rados_client, pool_id, nspace, o, gid, handle, watch_ctx,
+ watch_ctx2, on_finish);
+ });
+ rados_client->get_aio_finisher()->queue(ctx);
+}
+
+int TestWatchNotify::unwatch(TestRadosClient *rados_client,
+ uint64_t handle) {
+ C_SaferCond ctx;
+ aio_unwatch(rados_client, handle, &ctx);
+ return ctx.wait();
+}
+
+void TestWatchNotify::aio_unwatch(TestRadosClient *rados_client,
+ uint64_t handle, Context *on_finish) {
+ auto ctx = new LambdaContext([this, rados_client, handle, on_finish](int) {
+ execute_unwatch(rados_client, handle, on_finish);
+ });
+ rados_client->get_aio_finisher()->queue(ctx);
+}
+
+void TestWatchNotify::aio_notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace,
+ const std::string& oid, const bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl,
+ Context *on_notify) {
+ auto ctx = new LambdaContext([=, this](int) {
+ execute_notify(rados_client, pool_id, nspace, oid, bl, pbl, on_notify);
+ });
+ rados_client->get_aio_finisher()->queue(ctx);
+}
+
+int TestWatchNotify::notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& oid,
+ bufferlist& bl, uint64_t timeout_ms,
+ bufferlist *pbl) {
+ C_SaferCond cond;
+ aio_notify(rados_client, pool_id, nspace, oid, bl, timeout_ms, pbl, &cond);
+ return cond.wait();
+}
+
+void TestWatchNotify::notify_ack(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace,
+ const std::string& o, uint64_t notify_id,
+ uint64_t handle, uint64_t gid,
+ bufferlist& bl) {
+ CephContext *cct = rados_client->cct();
+ ldout(cct, 20) << "notify_id=" << notify_id << ", handle=" << handle
+ << ", gid=" << gid << dendl;
+ std::lock_guard lock{m_lock};
+ WatcherID watcher_id = std::make_pair(gid, handle);
+ ack_notify(rados_client, pool_id, nspace, o, notify_id, watcher_id, bl);
+ finish_notify(rados_client, pool_id, nspace, o, notify_id);
+}
+
+void TestWatchNotify::execute_watch(TestRadosClient *rados_client,
+ int64_t pool_id, const std::string& nspace,
+ const std::string& o, uint64_t gid,
+ uint64_t *handle, librados::WatchCtx *ctx,
+ librados::WatchCtx2 *ctx2,
+ Context* on_finish) {
+ CephContext *cct = rados_client->cct();
+
+ m_lock.lock();
+ SharedWatcher watcher = get_watcher(pool_id, nspace, o);
+ if (!watcher) {
+ m_lock.unlock();
+ on_finish->complete(-ENOENT);
+ return;
+ }
+
+ WatchHandle watch_handle;
+ watch_handle.rados_client = rados_client;
+ watch_handle.addr = "127.0.0.1:0/" + stringify(rados_client->get_nonce());
+ watch_handle.nonce = rados_client->get_nonce();
+ watch_handle.gid = gid;
+ watch_handle.handle = ++m_handle;
+ watch_handle.watch_ctx = ctx;
+ watch_handle.watch_ctx2 = ctx2;
+ watcher->watch_handles[watch_handle.handle] = watch_handle;
+
+ *handle = watch_handle.handle;
+
+ ldout(cct, 20) << "oid=" << o << ", gid=" << gid << ": handle=" << *handle
+ << dendl;
+ m_lock.unlock();
+
+ on_finish->complete(0);
+}
+
+void TestWatchNotify::execute_unwatch(TestRadosClient *rados_client,
+ uint64_t handle, Context* on_finish) {
+ CephContext *cct = rados_client->cct();
+
+ ldout(cct, 20) << "handle=" << handle << dendl;
+ {
+ std::lock_guard locker{m_lock};
+ for (FileWatchers::iterator it = m_file_watchers.begin();
+ it != m_file_watchers.end(); ++it) {
+ SharedWatcher watcher = it->second;
+
+ WatchHandles::iterator w_it = watcher->watch_handles.find(handle);
+ if (w_it != watcher->watch_handles.end()) {
+ watcher->watch_handles.erase(w_it);
+ maybe_remove_watcher(watcher);
+ break;
+ }
+ }
+ }
+ on_finish->complete(0);
+}
+
+TestWatchNotify::SharedWatcher TestWatchNotify::get_watcher(
+ int64_t pool_id, const std::string& nspace, const std::string& oid) {
+ ceph_assert(ceph_mutex_is_locked(m_lock));
+
+ auto it = m_file_watchers.find({pool_id, nspace, oid});
+ if (it == m_file_watchers.end()) {
+ SharedWatcher watcher(new Watcher(pool_id, nspace, oid));
+ watcher->object_handler.reset(new ObjectHandler(
+ this, pool_id, nspace, oid));
+ int r = m_test_cluster->register_object_handler(
+ pool_id, {nspace, oid}, watcher->object_handler.get());
+ if (r < 0) {
+ // object doesn't exist
+ return SharedWatcher();
+ }
+ m_file_watchers[{pool_id, nspace, oid}] = watcher;
+ return watcher;
+ }
+
+ return it->second;
+}
+
+void TestWatchNotify::maybe_remove_watcher(SharedWatcher watcher) {
+ ceph_assert(ceph_mutex_is_locked(m_lock));
+
+ // TODO
+ if (watcher->watch_handles.empty() && watcher->notify_handles.empty()) {
+ auto pool_id = watcher->pool_id;
+ auto& nspace = watcher->nspace;
+ auto& oid = watcher->oid;
+ if (watcher->object_handler) {
+ m_test_cluster->unregister_object_handler(pool_id, {nspace, oid},
+ watcher->object_handler.get());
+ watcher->object_handler.reset();
+ }
+
+ m_file_watchers.erase({pool_id, nspace, oid});
+ }
+}
+
+void TestWatchNotify::execute_notify(TestRadosClient *rados_client,
+ int64_t pool_id, const std::string& nspace,
+ const std::string &oid,
+ const bufferlist &bl, bufferlist *pbl,
+ Context *on_notify) {
+ CephContext *cct = rados_client->cct();
+
+ m_lock.lock();
+ uint64_t notify_id = ++m_notify_id;
+
+ SharedWatcher watcher = get_watcher(pool_id, nspace, oid);
+ if (!watcher) {
+ ldout(cct, 1) << "oid=" << oid << ": not found" << dendl;
+ m_lock.unlock();
+ on_notify->complete(-ENOENT);
+ return;
+ }
+
+ ldout(cct, 20) << "oid=" << oid << ": notify_id=" << notify_id << dendl;
+
+ SharedNotifyHandle notify_handle(new NotifyHandle());
+ notify_handle->rados_client = rados_client;
+ notify_handle->pbl = pbl;
+ notify_handle->on_notify = on_notify;
+
+ WatchHandles &watch_handles = watcher->watch_handles;
+ for (auto &watch_handle_pair : watch_handles) {
+ WatchHandle &watch_handle = watch_handle_pair.second;
+ notify_handle->pending_watcher_ids.insert(std::make_pair(
+ watch_handle.gid, watch_handle.handle));
+
+ m_async_op_tracker.start_op();
+ uint64_t notifier_id = rados_client->get_instance_id();
+ watch_handle.rados_client->get_aio_finisher()->queue(new LambdaContext(
+ [this, pool_id, nspace, oid, bl, notify_id, watch_handle, notifier_id](int r) {
+ bufferlist notify_bl;
+ notify_bl.append(bl);
+
+ if (watch_handle.watch_ctx2 != NULL) {
+ watch_handle.watch_ctx2->handle_notify(notify_id,
+ watch_handle.handle,
+ notifier_id, notify_bl);
+ } else if (watch_handle.watch_ctx != NULL) {
+ watch_handle.watch_ctx->notify(0, 0, notify_bl);
+
+ // auto ack old-style watch/notify clients
+ ack_notify(watch_handle.rados_client, pool_id, nspace, oid, notify_id,
+ {watch_handle.gid, watch_handle.handle}, bufferlist());
+ }
+
+ m_async_op_tracker.finish_op();
+ }));
+ }
+ watcher->notify_handles[notify_id] = notify_handle;
+
+ finish_notify(rados_client, pool_id, nspace, oid, notify_id);
+ m_lock.unlock();
+}
+
+void TestWatchNotify::ack_notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace,
+ const std::string &oid, uint64_t notify_id,
+ const WatcherID &watcher_id,
+ const bufferlist &bl) {
+ CephContext *cct = rados_client->cct();
+
+ ceph_assert(ceph_mutex_is_locked(m_lock));
+ SharedWatcher watcher = get_watcher(pool_id, nspace, oid);
+ if (!watcher) {
+ ldout(cct, 1) << "oid=" << oid << ": not found" << dendl;
+ return;
+ }
+
+ NotifyHandles::iterator it = watcher->notify_handles.find(notify_id);
+ if (it == watcher->notify_handles.end()) {
+ ldout(cct, 1) << "oid=" << oid << ", notify_id=" << notify_id
+ << ", WatcherID=" << watcher_id << ": not found" << dendl;
+ return;
+ }
+
+ ldout(cct, 20) << "oid=" << oid << ", notify_id=" << notify_id
+ << ", WatcherID=" << watcher_id << dendl;
+
+ bufferlist response;
+ response.append(bl);
+
+ SharedNotifyHandle notify_handle = it->second;
+ notify_handle->notify_responses[watcher_id] = response;
+ notify_handle->pending_watcher_ids.erase(watcher_id);
+}
+
+void TestWatchNotify::finish_notify(TestRadosClient *rados_client,
+ int64_t pool_id, const std::string& nspace,
+ const std::string &oid,
+ uint64_t notify_id) {
+ CephContext *cct = rados_client->cct();
+
+ ldout(cct, 20) << "oid=" << oid << ", notify_id=" << notify_id << dendl;
+
+ ceph_assert(ceph_mutex_is_locked(m_lock));
+ SharedWatcher watcher = get_watcher(pool_id, nspace, oid);
+ if (!watcher) {
+ ldout(cct, 1) << "oid=" << oid << ": not found" << dendl;
+ return;
+ }
+
+ NotifyHandles::iterator it = watcher->notify_handles.find(notify_id);
+ if (it == watcher->notify_handles.end()) {
+ ldout(cct, 1) << "oid=" << oid << ", notify_id=" << notify_id
+ << ": not found" << dendl;
+ return;
+ }
+
+ SharedNotifyHandle notify_handle = it->second;
+ if (!notify_handle->pending_watcher_ids.empty()) {
+ ldout(cct, 10) << "oid=" << oid << ", notify_id=" << notify_id
+ << ": pending watchers, returning" << dendl;
+ return;
+ }
+
+ ldout(cct, 20) << "oid=" << oid << ", notify_id=" << notify_id
+ << ": completing" << dendl;
+
+ if (notify_handle->pbl != NULL) {
+ encode(notify_handle->notify_responses, *notify_handle->pbl);
+ encode(notify_handle->pending_watcher_ids, *notify_handle->pbl);
+ }
+
+ notify_handle->rados_client->get_aio_finisher()->queue(
+ notify_handle->on_notify, 0);
+ watcher->notify_handles.erase(notify_id);
+ maybe_remove_watcher(watcher);
+}
+
+void TestWatchNotify::blocklist(uint32_t nonce) {
+ std::lock_guard locker{m_lock};
+
+ for (auto file_it = m_file_watchers.begin();
+ file_it != m_file_watchers.end(); ) {
+ auto &watcher = file_it->second;
+ for (auto w_it = watcher->watch_handles.begin();
+ w_it != watcher->watch_handles.end();) {
+ auto& watch_handle = w_it->second;
+ if (watch_handle.nonce == nonce) {
+ auto handle = watch_handle.handle;
+ auto watch_ctx2 = watch_handle.watch_ctx2;
+ if (watch_ctx2 != nullptr) {
+ auto ctx = new LambdaContext([handle, watch_ctx2](int) {
+ watch_ctx2->handle_error(handle, -ENOTCONN);
+ });
+ watch_handle.rados_client->get_aio_finisher()->queue(ctx);
+ }
+ w_it = watcher->watch_handles.erase(w_it);
+ } else {
+ ++w_it;
+ }
+ }
+
+ ++file_it;
+ maybe_remove_watcher(watcher);
+ }
+}
+
+void TestWatchNotify::handle_object_removed(int64_t pool_id,
+ const std::string& nspace,
+ const std::string& oid) {
+ std::lock_guard locker{m_lock};
+ auto it = m_file_watchers.find({pool_id, nspace, oid});
+ if (it == m_file_watchers.end()) {
+ return;
+ }
+
+ auto watcher = it->second;
+
+ // cancel all in-flight notifications
+ for (auto& notify_handle_pair : watcher->notify_handles) {
+ auto notify_handle = notify_handle_pair.second;
+ notify_handle->rados_client->get_aio_finisher()->queue(
+ notify_handle->on_notify, -ENOENT);
+ }
+
+ // alert all watchers of the loss of connection
+ for (auto& watch_handle_pair : watcher->watch_handles) {
+ auto& watch_handle = watch_handle_pair.second;
+ auto handle = watch_handle.handle;
+ auto watch_ctx2 = watch_handle.watch_ctx2;
+ if (watch_ctx2 != nullptr) {
+ auto ctx = new LambdaContext([handle, watch_ctx2](int) {
+ watch_ctx2->handle_error(handle, -ENOTCONN);
+ });
+ watch_handle.rados_client->get_aio_finisher()->queue(ctx);
+ }
+ }
+ m_file_watchers.erase(it);
+}
+
+} // namespace librados
diff --git a/src/test/librados_test_stub/TestWatchNotify.h b/src/test/librados_test_stub/TestWatchNotify.h
new file mode 100644
index 000000000..bb973ea6b
--- /dev/null
+++ b/src/test/librados_test_stub/TestWatchNotify.h
@@ -0,0 +1,148 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_WATCH_NOTIFY_H
+#define CEPH_TEST_WATCH_NOTIFY_H
+
+#include "include/rados/librados.hpp"
+#include "common/AsyncOpTracker.h"
+#include "common/ceph_mutex.h"
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <map>
+
+class Finisher;
+
+namespace librados {
+
+class TestCluster;
+class TestRadosClient;
+
+class TestWatchNotify : boost::noncopyable {
+public:
+ typedef std::pair<uint64_t, uint64_t> WatcherID;
+ typedef std::set<WatcherID> WatcherIDs;
+ typedef std::map<std::pair<uint64_t, uint64_t>, bufferlist> NotifyResponses;
+
+ struct NotifyHandle {
+ TestRadosClient *rados_client = nullptr;
+ WatcherIDs pending_watcher_ids;
+ NotifyResponses notify_responses;
+ bufferlist *pbl = nullptr;
+ Context *on_notify = nullptr;
+ };
+ typedef boost::shared_ptr<NotifyHandle> SharedNotifyHandle;
+ typedef std::map<uint64_t, SharedNotifyHandle> NotifyHandles;
+
+ struct WatchHandle {
+ TestRadosClient *rados_client = nullptr;
+ std::string addr;
+ uint32_t nonce;
+ uint64_t gid;
+ uint64_t handle;
+ librados::WatchCtx* watch_ctx;
+ librados::WatchCtx2* watch_ctx2;
+ };
+
+ typedef std::map<uint64_t, WatchHandle> WatchHandles;
+
+ struct ObjectHandler;
+ typedef boost::shared_ptr<ObjectHandler> SharedObjectHandler;
+
+ struct Watcher {
+ Watcher(int64_t pool_id, const std::string& nspace, const std::string& oid)
+ : pool_id(pool_id), nspace(nspace), oid(oid) {
+ }
+
+ int64_t pool_id;
+ std::string nspace;
+ std::string oid;
+
+ SharedObjectHandler object_handler;
+ WatchHandles watch_handles;
+ NotifyHandles notify_handles;
+ };
+ typedef boost::shared_ptr<Watcher> SharedWatcher;
+
+ TestWatchNotify(TestCluster* test_cluster);
+
+ int list_watchers(int64_t pool_id, const std::string& nspace,
+ const std::string& o, std::list<obj_watch_t> *out_watchers);
+
+ void aio_flush(TestRadosClient *rados_client, Context *on_finish);
+ void aio_watch(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& o, uint64_t gid,
+ uint64_t *handle, librados::WatchCtx *watch_ctx,
+ librados::WatchCtx2 *watch_ctx2, Context *on_finish);
+ void aio_unwatch(TestRadosClient *rados_client, uint64_t handle,
+ Context *on_finish);
+ void aio_notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& oid,
+ const bufferlist& bl, uint64_t timeout_ms, bufferlist *pbl,
+ Context *on_notify);
+
+ void flush(TestRadosClient *rados_client);
+ int notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& o, bufferlist& bl,
+ uint64_t timeout_ms, bufferlist *pbl);
+ void notify_ack(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& o,
+ uint64_t notify_id, uint64_t handle, uint64_t gid,
+ bufferlist& bl);
+
+ int watch(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& o, uint64_t gid,
+ uint64_t *handle, librados::WatchCtx *ctx,
+ librados::WatchCtx2 *ctx2);
+ int unwatch(TestRadosClient *rados_client, uint64_t handle);
+
+ void blocklist(uint32_t nonce);
+
+private:
+ typedef std::tuple<int64_t, std::string, std::string> PoolFile;
+ typedef std::map<PoolFile, SharedWatcher> FileWatchers;
+
+ TestCluster *m_test_cluster;
+
+ uint64_t m_handle = 0;
+ uint64_t m_notify_id = 0;
+
+ ceph::mutex m_lock =
+ ceph::make_mutex("librados::TestWatchNotify::m_lock");
+ AsyncOpTracker m_async_op_tracker;
+
+ FileWatchers m_file_watchers;
+
+ SharedWatcher get_watcher(int64_t pool_id, const std::string& nspace,
+ const std::string& oid);
+ void maybe_remove_watcher(SharedWatcher shared_watcher);
+
+ void execute_watch(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string& o,
+ uint64_t gid, uint64_t *handle,
+ librados::WatchCtx *watch_ctx,
+ librados::WatchCtx2 *watch_ctx2,
+ Context *on_finish);
+ void execute_unwatch(TestRadosClient *rados_client, uint64_t handle,
+ Context *on_finish);
+
+ void execute_notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string &oid,
+ const bufferlist &bl, bufferlist *pbl,
+ Context *on_notify);
+ void ack_notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string &oid,
+ uint64_t notify_id, const WatcherID &watcher_id,
+ const bufferlist &bl);
+ void finish_notify(TestRadosClient *rados_client, int64_t pool_id,
+ const std::string& nspace, const std::string &oid,
+ uint64_t notify_id);
+
+ void handle_object_removed(int64_t pool_id, const std::string& nspace,
+ const std::string& oid);
+};
+
+} // namespace librados
+
+#endif // CEPH_TEST_WATCH_NOTIFY_H
diff --git a/src/test/libradosstriper/CMakeLists.txt b/src/test/libradosstriper/CMakeLists.txt
new file mode 100644
index 000000000..8e53a300a
--- /dev/null
+++ b/src/test/libradosstriper/CMakeLists.txt
@@ -0,0 +1,34 @@
+#
+# Note: only compiled if WITH_LIBRADOSSTRIPER is defined.
+#
+add_library(rados_striper_test STATIC TestCase.cc)
+target_link_libraries(rados_striper_test
+ radostest
+ radostest-cxx
+ GTest::GTest)
+
+add_executable(ceph_test_rados_striper_api_striping
+ striping.cc
+ )
+target_link_libraries(ceph_test_rados_striper_api_striping
+ ${UNITTEST_LIBS} rados_striper_test
+ radosstriper
+ librados)
+install(TARGETS ceph_test_rados_striper_api_striping
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(ceph_test_rados_striper_api_io
+ io.cc)
+target_link_libraries(ceph_test_rados_striper_api_io
+ ${UNITTEST_LIBS} rados_striper_test
+ radosstriper
+ librados)
+install(TARGETS ceph_test_rados_striper_api_io
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(ceph_test_rados_striper_api_aio
+ aio.cc)
+target_link_libraries(ceph_test_rados_striper_api_aio librados radosstriper
+ ${UNITTEST_LIBS} rados_striper_test)
+install(TARGETS ceph_test_rados_striper_api_aio
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/libradosstriper/TestCase.cc b/src/test/libradosstriper/TestCase.cc
new file mode 100644
index 000000000..98e81f49c
--- /dev/null
+++ b/src/test/libradosstriper/TestCase.cc
@@ -0,0 +1,80 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
+#include "test/libradosstriper/TestCase.h"
+
+using namespace libradosstriper;
+
+std::string StriperTest::pool_name;
+rados_t StriperTest::s_cluster = NULL;
+
+void StriperTest::SetUpTestCase()
+{
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool(pool_name, &s_cluster));
+}
+
+void StriperTest::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_pool(pool_name, &s_cluster));
+}
+
+void StriperTest::SetUp()
+{
+ cluster = StriperTest::s_cluster;
+ ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
+ ASSERT_EQ(0, rados_striper_create(ioctx, &striper));
+}
+
+void StriperTest::TearDown()
+{
+ rados_striper_destroy(striper);
+ rados_ioctx_destroy(ioctx);
+}
+
+std::string StriperTestPP::pool_name;
+librados::Rados StriperTestPP::s_cluster;
+
+void StriperTestPP::SetUpTestCase()
+{
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void StriperTestPP::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void StriperTestPP::SetUp()
+{
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ ASSERT_EQ(0, RadosStriper::striper_create(ioctx, &striper));
+}
+
+// this is pure copy and paste from previous class
+// but for the inheritance from TestWithParam
+// with gtest >= 1.6, we couldd avoid this by using
+// inheritance from WithParamInterface
+std::string StriperTestParam::pool_name;
+librados::Rados StriperTestParam::s_cluster;
+
+void StriperTestParam::SetUpTestCase()
+{
+ pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void StriperTestParam::TearDownTestCase()
+{
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void StriperTestParam::SetUp()
+{
+ ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+ ASSERT_EQ(0, RadosStriper::striper_create(ioctx, &striper));
+}
diff --git a/src/test/libradosstriper/TestCase.h b/src/test/libradosstriper/TestCase.h
new file mode 100644
index 000000000..c316b3bfb
--- /dev/null
+++ b/src/test/libradosstriper/TestCase.h
@@ -0,0 +1,82 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_RADOS_TESTCASE_H
+#define CEPH_TEST_RADOS_TESTCASE_H
+
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/radosstriper/libradosstriper.h"
+#include "include/radosstriper/libradosstriper.hpp"
+#include "gtest/gtest.h"
+
+#include <string>
+
+/**
+ * These test cases create a temporary pool that lives as long as the
+ * test case. Each test within a test case gets a new ioctx and striper
+ * set to a unique namespace within the pool.
+ *
+ * Since pool creation and deletion is slow, this allows many tests to
+ * run faster.
+ */
+class StriperTest : public ::testing::Test {
+public:
+ StriperTest() {}
+ ~StriperTest() override {}
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ static rados_t s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ void TearDown() override;
+ rados_t cluster = NULL;
+ rados_ioctx_t ioctx = NULL;
+ rados_striper_t striper = NULL;
+};
+
+class StriperTestPP : public ::testing::Test {
+public:
+ StriperTestPP() : cluster(s_cluster) {}
+ ~StriperTestPP() override {}
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+protected:
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ libradosstriper::RadosStriper striper;
+};
+
+struct TestData {
+ uint32_t stripe_unit;
+ uint32_t stripe_count;
+ uint32_t object_size;
+ size_t size;
+};
+// this is pure copy and paste from previous class
+// but for the inheritance from TestWithParam
+// with gtest >= 1.6, we couldd avoid this by using
+// inheritance from WithParamInterface
+class StriperTestParam : public ::testing::TestWithParam<TestData> {
+public:
+ StriperTestParam() : cluster(s_cluster) {}
+ ~StriperTestParam() override {}
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+protected:
+ static librados::Rados s_cluster;
+ static std::string pool_name;
+
+ void SetUp() override;
+ librados::Rados &cluster;
+ librados::IoCtx ioctx;
+ libradosstriper::RadosStriper striper;
+};
+
+#endif
diff --git a/src/test/libradosstriper/aio.cc b/src/test/libradosstriper/aio.cc
new file mode 100644
index 000000000..71e6abd7a
--- /dev/null
+++ b/src/test/libradosstriper/aio.cc
@@ -0,0 +1,581 @@
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/radosstriper/libradosstriper.h"
+#include "include/radosstriper/libradosstriper.hpp"
+#include "test/librados/test.h"
+#include "test/libradosstriper/TestCase.h"
+
+#include <boost/scoped_ptr.hpp>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <errno.h>
+
+using namespace librados;
+using namespace libradosstriper;
+using std::pair;
+
+class AioTestData
+{
+public:
+ AioTestData() : m_complete(false) {
+ sem_init(&m_sem, 0, 0);
+ }
+
+ ~AioTestData() {
+ sem_destroy(&m_sem);
+ }
+
+ void notify() {
+ sem_post(&m_sem);
+ }
+
+ void wait() {
+ sem_wait(&m_sem);
+ }
+
+ bool m_complete;
+
+private:
+ sem_t m_sem;
+};
+
+void set_completion_complete(rados_completion_t cb, void *arg)
+{
+ AioTestData *test = static_cast<AioTestData*>(arg);
+ test->m_complete = true;
+ test->notify();
+}
+
+TEST_F(StriperTest, SimpleWrite) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "StriperTest", my_completion, buf, sizeof(buf), 0));
+ TestAlarm alarm;
+ test_data.wait();
+ rados_aio_release(my_completion);
+}
+
+TEST_F(StriperTestPP, SimpleWritePP) {
+ AioTestData test_data;
+ AioCompletion *my_completion = librados::Rados::aio_create_completion
+ ((void*)&test_data, set_completion_complete);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_write("SimpleWritePP", my_completion, bl1, sizeof(buf), 0));
+ TestAlarm alarm;
+ test_data.wait();
+ my_completion->release();
+}
+
+TEST_F(StriperTest, WaitForSafe) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "WaitForSafe", my_completion, buf, sizeof(buf), 0));
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion);
+ test_data.wait();
+ rados_aio_release(my_completion);
+}
+
+TEST_F(StriperTestPP, WaitForSafePP) {
+ AioTestData test_data;
+ AioCompletion *my_completion =
+ librados::Rados::aio_create_completion(&test_data,
+ set_completion_complete);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_write("WaitForSafePP", my_completion, bl1, sizeof(buf), 0));
+ TestAlarm alarm;
+ my_completion->wait_for_complete();
+ test_data.wait();
+ my_completion->release();
+}
+
+TEST_F(StriperTest, RoundTrip) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "RoundTrip", my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ test_data.wait();
+ }
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion2));
+ ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTrip", my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion2);
+ }
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ test_data.wait();
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST_F(StriperTest, RoundTrip2) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "RoundTrip2", my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ test_data.wait();
+ }
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion2));
+ ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTrip2", my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion2);
+ }
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ test_data.wait();
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST_F(StriperTestPP, RoundTripPP) {
+ AioTestData test_data;
+ AioCompletion *my_completion =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_write("RoundTripPP", my_completion, bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ test_data.wait();
+ }
+ bufferlist bl2;
+ AioCompletion *my_completion2 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_read("RoundTripPP", my_completion2, &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ my_completion2->wait_for_complete();
+ }
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+ test_data.wait();
+ my_completion->release();
+ my_completion2->release();
+}
+
+TEST_F(StriperTestPP, RoundTripPP2) {
+ AioTestData test_data;
+ AioCompletion *my_completion =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_write("RoundTripPP2", my_completion, bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ test_data.wait();
+ }
+ bufferlist bl2;
+ AioCompletion *my_completion2 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_read("RoundTripPP2", my_completion2, &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ my_completion2->wait_for_complete();
+ }
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+ test_data.wait();
+ my_completion->release();
+ my_completion2->release();
+}
+
+TEST_F(StriperTest, IsComplete) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "IsComplete", my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ test_data.wait();
+ }
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion2));
+ ASSERT_EQ(0, rados_striper_aio_read(striper, "IsComplete", my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_complete.
+ while (true) {
+ int is_complete = rados_aio_is_complete(my_completion2);
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ test_data.wait();
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST_F(StriperTestPP, IsCompletePP) {
+ AioTestData test_data;
+ AioCompletion *my_completion =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_write("IsCompletePP", my_completion, bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ test_data.wait();
+ }
+ bufferlist bl2;
+ AioCompletion *my_completion2 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_read("IsCompletePP", my_completion2, &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_complete.
+ while (true) {
+ int is_complete = my_completion2->is_complete();
+ if (is_complete)
+ break;
+ }
+ }
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+ test_data.wait();
+ my_completion->release();
+ my_completion2->release();
+}
+
+TEST_F(StriperTest, IsSafe) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "IsSafe", my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ // Busy-wait until the AIO completes.
+ // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
+ while (true) {
+ int is_safe = rados_aio_is_safe(my_completion);
+ if (is_safe)
+ break;
+ }
+ }
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion2));
+ ASSERT_EQ(0, rados_striper_aio_read(striper, "IsSafe", my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion2);
+ }
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ test_data.wait();
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST_F(StriperTest, RoundTripAppend) {
+ AioTestData test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_append(striper, "RoundTripAppend", my_completion, buf, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion);
+ }
+ char buf2[128];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion2));
+ ASSERT_EQ(0, rados_striper_aio_append(striper, "RoundTripAppend", my_completion2, buf2, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion2);
+ }
+ char buf3[sizeof(buf) + sizeof(buf2)];
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion3));
+ ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTripAppend", my_completion3, buf3, sizeof(buf3), 0));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion3);
+ }
+ ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)), rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
+ test_data.wait();
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST_F(StriperTestPP, RoundTripAppendPP) {
+ AioTestData test_data;
+ AioCompletion *my_completion =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_append("RoundTripAppendPP", my_completion, bl1, sizeof(buf)));
+ {
+ TestAlarm alarm;
+ my_completion->wait_for_complete();
+ }
+ char buf2[128];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ AioCompletion *my_completion2 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_append("RoundTripAppendPP", my_completion2, bl2, sizeof(buf2)));
+ {
+ TestAlarm alarm;
+ my_completion2->wait_for_complete();
+ }
+ bufferlist bl3;
+ AioCompletion *my_completion3 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_read("RoundTripAppendPP", my_completion3, &bl3, 2 * sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ my_completion3->wait_for_complete();
+ }
+ ASSERT_EQ(sizeof(buf) + sizeof(buf2), (unsigned)my_completion3->get_return_value());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2)));
+ test_data.wait();
+ my_completion->release();
+ my_completion2->release();
+ my_completion3->release();
+}
+
+TEST_F(StriperTest, Flush) {
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "Flush", my_completion, buf, sizeof(buf), 0));
+ rados_striper_aio_flush(striper);
+ char buf2[128];
+ memset(buf2, 0, sizeof(buf2));
+ rados_completion_t my_completion2;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion2));
+ ASSERT_EQ(0, rados_striper_aio_read(striper, "Flush", my_completion2, buf2, sizeof(buf2), 0));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion2);
+ }
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ test_data.wait();
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+}
+
+TEST_F(StriperTestPP, FlushPP) {
+ AioTestData test_data;
+ AioCompletion *my_completion =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ char buf[128];
+ memset(buf, 0xee, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_write("FlushPP", my_completion, bl1, sizeof(buf), 0));
+ striper.aio_flush();
+ bufferlist bl2;
+ AioCompletion *my_completion2 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_read("FlushPP", my_completion2, &bl2, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ my_completion2->wait_for_complete();
+ }
+ ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+ test_data.wait();
+ my_completion->release();
+ my_completion2->release();
+}
+
+TEST_F(StriperTest, RoundTripWriteFull) {
+ AioTestData test_data;
+ rados_completion_t my_completion, my_completion2, my_completion3;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_aio_write(striper, "RoundTripWriteFull", my_completion, buf, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion);
+ }
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion2));
+ ASSERT_EQ(0, rados_striper_aio_write_full(striper, "RoundTripWriteFull", my_completion2, buf2, sizeof(buf2)));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion2);
+ }
+ char buf3[sizeof(buf) + sizeof(buf2)];
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion3));
+ ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTripWriteFull", my_completion3, buf3, sizeof(buf3), 0));
+ {
+ TestAlarm alarm;
+ rados_aio_wait_for_complete(my_completion3);
+ }
+ ASSERT_EQ(sizeof(buf2), (unsigned)rados_aio_get_return_value(my_completion3));
+ ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
+ test_data.wait();
+ rados_aio_release(my_completion);
+ rados_aio_release(my_completion2);
+ rados_aio_release(my_completion3);
+}
+
+TEST_F(StriperTestPP, RoundTripWriteFullPP) {
+ AioTestData test_data;
+ AioCompletion *my_completion =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.aio_write("RoundTripWriteFullPP", my_completion, bl1, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ my_completion->wait_for_complete();
+ }
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ AioCompletion *my_completion2 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_write_full("RoundTripWriteFullPP", my_completion2, bl2));
+ {
+ TestAlarm alarm;
+ my_completion2->wait_for_complete();
+ }
+ bufferlist bl3;
+ AioCompletion *my_completion3 =
+ librados::Rados::aio_create_completion(&test_data, set_completion_complete);
+ ASSERT_EQ(0, striper.aio_read("RoundTripWriteFullPP", my_completion3, &bl3, sizeof(buf), 0));
+ {
+ TestAlarm alarm;
+ my_completion3->wait_for_complete();
+ }
+ ASSERT_EQ(sizeof(buf2), (unsigned)my_completion3->get_return_value());
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+ test_data.wait();
+ my_completion->release();
+ my_completion2->release();
+ my_completion3->release();
+}
+
+TEST_F(StriperTest, RemoveTest) {
+ char buf[128];
+ char buf2[sizeof(buf)];
+ // create oabject
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "RemoveTest", buf, sizeof(buf), 0));
+ // async remove it
+ AioTestData test_data;
+ rados_completion_t my_completion;
+ ASSERT_EQ(0, rados_aio_create_completion2(&test_data,
+ set_completion_complete,
+ &my_completion));
+ ASSERT_EQ(0, rados_striper_aio_remove(striper, "RemoveTest", my_completion));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+ }
+ ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+ rados_aio_release(my_completion);
+ // check we get ENOENT on reading
+ ASSERT_EQ(-ENOENT, rados_striper_read(striper, "RemoveTest", buf2, sizeof(buf2), 0));
+}
+
+TEST_F(StriperTestPP, RemoveTestPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("RemoveTestPP", bl, sizeof(buf), 0));
+ AioCompletion *my_completion = cluster.aio_create_completion(nullptr, nullptr);
+ ASSERT_EQ(0, striper.aio_remove("RemoveTestPP", my_completion));
+ {
+ TestAlarm alarm;
+ ASSERT_EQ(0, my_completion->wait_for_complete());
+ }
+ ASSERT_EQ(0, my_completion->get_return_value());
+ bufferlist bl2;
+ ASSERT_EQ(-ENOENT, striper.read("RemoveTestPP", &bl2, sizeof(buf), 0));
+ my_completion->release();
+}
diff --git a/src/test/libradosstriper/io.cc b/src/test/libradosstriper/io.cc
new file mode 100644
index 000000000..88f69400e
--- /dev/null
+++ b/src/test/libradosstriper/io.cc
@@ -0,0 +1,430 @@
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/radosstriper/libradosstriper.h"
+#include "include/radosstriper/libradosstriper.hpp"
+#include "test/librados/test.h"
+#include "test/libradosstriper/TestCase.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include "gtest/gtest.h"
+
+using namespace librados;
+using namespace libradosstriper;
+using std::string;
+
+TEST_F(StriperTest, SimpleWrite) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "SimpleWrite", buf, sizeof(buf), 0));
+}
+
+TEST_F(StriperTestPP, SimpleWritePP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("SimpleWritePP", bl, sizeof(buf), 0));
+}
+
+TEST_F(StriperTest, SimpleWriteFull) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write_full(striper, "SimpleWrite", buf, sizeof(buf)));
+}
+
+TEST_F(StriperTestPP, SimpleWriteFullPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write_full("SimpleWritePP", bl));
+}
+
+TEST_F(StriperTest, Stat) {
+ uint64_t size = 0;
+ time_t mtime = 0;
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "Stat", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_striper_stat(striper, "Stat", &size, &mtime));
+ ASSERT_EQ(size, sizeof(buf));
+ ASSERT_EQ(-ENOENT, rados_striper_stat(striper, "nonexistent", &size, &mtime));
+}
+
+TEST_F(StriperTest, Stat2) {
+ uint64_t size = 0;
+ struct timespec mtime = {};
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "Stat2", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_striper_stat2(striper, "Stat2", &size, &mtime));
+ ASSERT_EQ(size, sizeof(buf));
+ ASSERT_EQ(-ENOENT, rados_striper_stat2(striper, "nonexistent", &size, &mtime));
+}
+
+TEST_F(StriperTestPP, StatPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("Statpp", bl, sizeof(buf), 0));
+ uint64_t size = 0;
+ time_t mtime = 0;
+ ASSERT_EQ(0, striper.stat("Statpp", &size, &mtime));
+ ASSERT_EQ(size, sizeof(buf));
+ ASSERT_EQ(-ENOENT, striper.stat("nonexistent", &size, &mtime));
+}
+
+TEST_F(StriperTestPP, Stat2PP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("Stat2pp", bl, sizeof(buf), 0));
+ uint64_t size = 0;
+ struct timespec mtime = {};
+ ASSERT_EQ(0, striper.stat2("Stat2pp", &size, &mtime));
+ ASSERT_EQ(size, sizeof(buf));
+ ASSERT_EQ(-ENOENT, striper.stat2("nonexistent", &size, &mtime));
+}
+
+TEST_F(StriperTest, RoundTrip) {
+ char buf[128];
+ char buf2[sizeof(buf)];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "RoundTrip", buf, sizeof(buf), 0));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "RoundTrip", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+}
+
+TEST_F(StriperTestPP, RoundTripPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("RoundTripPP", bl, sizeof(buf), 0));
+ bufferlist cl;
+ ASSERT_EQ((int)sizeof(buf), striper.read("RoundTripPP", &cl, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
+}
+
+TEST_F(StriperTest, OverlappingWriteRoundTrip) {
+ char buf[128];
+ char buf2[64];
+ char buf3[sizeof(buf)];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "OverlappingWriteRoundTrip", buf, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_striper_write(striper, "OverlappingWriteRoundTrip", buf2, sizeof(buf2), 0));
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ((int)sizeof(buf3), rados_striper_read(striper, "OverlappingWriteRoundTrip", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
+ ASSERT_EQ(0, memcmp(buf3 + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
+}
+
+TEST_F(StriperTestPP, OverlappingWriteRoundTripPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("OverlappingWriteRoundTripPP", bl1, sizeof(buf), 0));
+ char buf2[64];
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, striper.write("OverlappingWriteRoundTripPP", bl2, sizeof(buf2), 0));
+ bufferlist bl3;
+ ASSERT_EQ((int)sizeof(buf), striper.read("OverlappingWriteRoundTripPP", &bl3, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+ ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
+}
+
+TEST_F(StriperTest, SparseWriteRoundTrip) {
+ char buf[128];
+ char buf2[2*sizeof(buf)];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "SparseWriteRoundTrip", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_striper_write(striper, "SparseWriteRoundTrip", buf, sizeof(buf), 1000000000));
+ memset(buf2, 0xaa, sizeof(buf2));
+ ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "SparseWriteRoundTrip", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ memset(buf, 0, sizeof(buf));
+ ASSERT_EQ(0, memcmp(buf, buf2+sizeof(buf), sizeof(buf)));
+ memset(buf2, 0xaa, sizeof(buf2));
+ ASSERT_EQ((int)sizeof(buf), rados_striper_read(striper, "SparseWriteRoundTrip", buf2, sizeof(buf), 500000000));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+}
+
+TEST_F(StriperTestPP, SparseWriteRoundTripPP) {
+ char buf[128];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("SparseWriteRoundTripPP", bl1, sizeof(buf), 0));
+ ASSERT_EQ(0, striper.write("SparseWriteRoundTripPP", bl1, sizeof(buf), 1000000000));
+ bufferlist bl2;
+ ASSERT_EQ((int)(2*sizeof(buf)), striper.read("SparseWriteRoundTripPP", &bl2, 2*sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)));
+ memset(buf, 0, sizeof(buf));
+ ASSERT_EQ(0, memcmp(bl2.c_str()+sizeof(buf), buf, sizeof(buf)));
+ ASSERT_EQ((int)sizeof(buf), striper.read("SparseWriteRoundTripPP", &bl2, sizeof(buf), 500000000));
+ ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)));
+}
+
+TEST_F(StriperTest, WriteFullRoundTrip) {
+ char buf[128];
+ char buf2[64];
+ char buf3[128];
+ memset(buf, 0xcc, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "WriteFullRoundTrip", buf, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ ASSERT_EQ(0, rados_striper_write_full(striper, "WriteFullRoundTrip", buf2, sizeof(buf2)));
+ memset(buf3, 0x00, sizeof(buf3));
+ ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "WriteFullRoundTrip", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf2, buf3, sizeof(buf2)));
+}
+
+TEST_F(StriperTestPP, WriteFullRoundTripPP) {
+ char buf[128];
+ char buf2[64];
+ memset(buf, 0xcc, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("WriteFullRoundTripPP", bl1, sizeof(buf), 0));
+ memset(buf2, 0xdd, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, striper.write_full("WriteFullRoundTripPP", bl2));
+ bufferlist bl3;
+ ASSERT_EQ((int)sizeof(buf2), striper.read("WriteFullRoundTripPP", &bl3, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+}
+
+TEST_F(StriperTest, AppendRoundTrip) {
+ char buf[64];
+ char buf2[64];
+ char buf3[sizeof(buf) + sizeof(buf2)];
+ memset(buf, 0xde, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_append(striper, "AppendRoundTrip", buf, sizeof(buf)));
+ memset(buf2, 0xad, sizeof(buf2));
+ ASSERT_EQ(0, rados_striper_append(striper, "AppendRoundTrip", buf2, sizeof(buf2)));
+ memset(buf3, 0, sizeof(buf3));
+ ASSERT_EQ((int)sizeof(buf3), rados_striper_read(striper, "AppendRoundTrip", buf3, sizeof(buf3), 0));
+ ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
+}
+
+TEST_F(StriperTestPP, AppendRoundTripPP) {
+ char buf[64];
+ char buf2[64];
+ memset(buf, 0xde, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.append("AppendRoundTripPP", bl1, sizeof(buf)));
+ memset(buf2, 0xad, sizeof(buf2));
+ bufferlist bl2;
+ bl2.append(buf2, sizeof(buf2));
+ ASSERT_EQ(0, striper.append("AppendRoundTripPP", bl2, sizeof(buf2)));
+ bufferlist bl3;
+ ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)),
+ striper.read("AppendRoundTripPP", &bl3, (sizeof(buf) + sizeof(buf2)), 0));
+ const char *bl3_str = bl3.c_str();
+ ASSERT_EQ(0, memcmp(bl3_str, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(bl3_str + sizeof(buf), buf2, sizeof(buf2)));
+}
+
+TEST_F(StriperTest, TruncTest) {
+ char buf[128];
+ char buf2[sizeof(buf)];
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_append(striper, "TruncTest", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_striper_trunc(striper, "TruncTest", sizeof(buf) / 2));
+ memset(buf2, 0, sizeof(buf2));
+ ASSERT_EQ((int)(sizeof(buf)/2), rados_striper_read(striper, "TruncTest", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)/2));
+}
+
+TEST_F(StriperTestPP, TruncTestPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.append("TruncTestPP", bl, sizeof(buf)));
+ ASSERT_EQ(0, striper.trunc("TruncTestPP", sizeof(buf) / 2));
+ bufferlist bl2;
+ ASSERT_EQ((int)(sizeof(buf)/2), striper.read("TruncTestPP", &bl2, sizeof(buf), 0));
+ ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)/2));
+}
+
+TEST_F(StriperTest, TruncTestGrow) {
+ char buf[128];
+ char buf2[sizeof(buf)*2];
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_append(striper, "TruncTestGrow", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_striper_trunc(striper, "TruncTestGrow", sizeof(buf2)));
+ memset(buf2, 0xbb, sizeof(buf2));
+ ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "TruncTestGrow", buf2, sizeof(buf2), 0));
+ ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+ memset(buf, 0x00, sizeof(buf));
+ ASSERT_EQ(0, memcmp(buf, buf2+sizeof(buf), sizeof(buf)));
+}
+
+TEST_F(StriperTestPP, TruncTestGrowPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.append("TruncTestGrowPP", bl, sizeof(buf)));
+ ASSERT_EQ(0, striper.trunc("TruncTestGrowPP", sizeof(buf) * 2));
+ bufferlist bl2;
+ ASSERT_EQ(sizeof(buf)*2, (unsigned)striper.read("TruncTestGrowPP", &bl2, sizeof(buf)*2, 0));
+ ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)));
+ memset(buf, 0x00, sizeof(buf));
+ ASSERT_EQ(0, memcmp(bl2.c_str()+sizeof(buf), buf, sizeof(buf)));
+}
+
+TEST_F(StriperTest, RemoveTest) {
+ char buf[128];
+ char buf2[sizeof(buf)];
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "RemoveTest", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_striper_remove(striper, "RemoveTest"));
+ ASSERT_EQ(-ENOENT, rados_striper_read(striper, "RemoveTest", buf2, sizeof(buf2), 0));
+}
+
+TEST_F(StriperTestPP, RemoveTestPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("RemoveTestPP", bl, sizeof(buf), 0));
+ ASSERT_EQ(0, striper.remove("RemoveTestPP"));
+ bufferlist bl2;
+ ASSERT_EQ(-ENOENT, striper.read("RemoveTestPP", &bl2, sizeof(buf), 0));
+}
+
+TEST_F(StriperTest, XattrsRoundTrip) {
+ char buf[128];
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "XattrsRoundTrip", buf, sizeof(buf), 0));
+ ASSERT_EQ(-ENODATA, rados_striper_getxattr(striper, "XattrsRoundTrip", "attr1", buf, sizeof(buf)));
+ ASSERT_EQ(0, rados_striper_setxattr(striper, "XattrsRoundTrip", "attr1", attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ((int)sizeof(attr1_buf), rados_striper_getxattr(striper, "XattrsRoundTrip", "attr1", buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
+}
+
+TEST_F(StriperTestPP, XattrsRoundTripPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("XattrsRoundTripPP", bl1, sizeof(buf), 0));
+ char attr1_buf[] = "foo bar baz";
+ bufferlist bl2;
+ ASSERT_EQ(-ENODATA, striper.getxattr("XattrsRoundTripPP", "attr1", bl2));
+ bufferlist bl3;
+ bl3.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, striper.setxattr("XattrsRoundTripPP", "attr1", bl3));
+ bufferlist bl4;
+ ASSERT_EQ((int)sizeof(attr1_buf), striper.getxattr("XattrsRoundTripPP", "attr1", bl4));
+ ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST_F(StriperTest, RmXattr) {
+ char buf[128];
+ char attr1_buf[] = "foo bar baz";
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "RmXattr", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_striper_setxattr(striper, "RmXattr", "attr1", attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ(0, rados_striper_rmxattr(striper, "RmXattr", "attr1"));
+ ASSERT_EQ(-ENODATA, rados_striper_getxattr(striper, "RmXattr", "attr1", buf, sizeof(buf)));
+}
+
+TEST_F(StriperTestPP, RmXattrPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("RmXattrPP", bl1, sizeof(buf), 0));
+ char attr1_buf[] = "foo bar baz";
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, striper.setxattr("RmXattrPP", "attr1", bl2));
+ ASSERT_EQ(0, striper.rmxattr("RmXattrPP", "attr1"));
+ bufferlist bl3;
+ ASSERT_EQ(-ENODATA, striper.getxattr("RmXattrPP", "attr1", bl3));
+}
+
+TEST_F(StriperTest, XattrIter) {
+ char buf[128];
+ char attr1_buf[] = "foo bar baz";
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ memset(buf, 0xaa, sizeof(buf));
+ ASSERT_EQ(0, rados_striper_write(striper, "RmXattr", buf, sizeof(buf), 0));
+ ASSERT_EQ(0, rados_striper_setxattr(striper, "RmXattr", "attr1", attr1_buf, sizeof(attr1_buf)));
+ ASSERT_EQ(0, rados_striper_setxattr(striper, "RmXattr", "attr2", attr2_buf, sizeof(attr2_buf)));
+ rados_xattrs_iter_t iter;
+ ASSERT_EQ(0, rados_striper_getxattrs(striper, "RmXattr", &iter));
+ int num_seen = 0;
+ while (true) {
+ const char *name;
+ const char *val;
+ size_t len;
+ ASSERT_EQ(0, rados_striper_getxattrs_next(iter, &name, &val, &len));
+ if (name == NULL) {
+ break;
+ }
+ ASSERT_LT(num_seen, 2) << "Extra attribute : " << name;
+ if ((strcmp(name, "attr1") == 0) && (val != NULL) && (memcmp(val, attr1_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else if ((strcmp(name, "attr2") == 0) && (val != NULL) && (memcmp(val, attr2_buf, len) == 0)) {
+ num_seen++;
+ continue;
+ }
+ else {
+ ASSERT_EQ(0, 1) << "Unexpected attribute : " << name;;
+ }
+ }
+ rados_striper_getxattrs_end(iter);
+}
+
+TEST_F(StriperTestPP, XattrListPP) {
+ char buf[128];
+ memset(buf, 0xaa, sizeof(buf));
+ bufferlist bl1;
+ bl1.append(buf, sizeof(buf));
+ ASSERT_EQ(0, striper.write("RmXattrPP", bl1, sizeof(buf), 0));
+ char attr1_buf[] = "foo bar baz";
+ bufferlist bl2;
+ bl2.append(attr1_buf, sizeof(attr1_buf));
+ ASSERT_EQ(0, striper.setxattr("RmXattrPP", "attr1", bl2));
+ char attr2_buf[256];
+ for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+ attr2_buf[j] = j % 0xff;
+ }
+ bufferlist bl3;
+ bl3.append(attr2_buf, sizeof(attr2_buf));
+ ASSERT_EQ(0, striper.setxattr("RmXattrPP", "attr2", bl3));
+ std::map<std::string, bufferlist> attrset;
+ ASSERT_EQ(0, striper.getxattrs("RmXattrPP", attrset));
+ for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+ i != attrset.end(); ++i) {
+ if (i->first == string("attr1")) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+ }
+ else if (i->first == string("attr2")) {
+ ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+ }
+ else {
+ ASSERT_EQ(0, 1) << "Unexpected attribute : " << i->first;
+ }
+ }
+}
diff --git a/src/test/libradosstriper/striping.cc b/src/test/libradosstriper/striping.cc
new file mode 100644
index 000000000..2de8b55f8
--- /dev/null
+++ b/src/test/libradosstriper/striping.cc
@@ -0,0 +1,329 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/compat.h"
+#include "include/types.h"
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/radosstriper/libradosstriper.h"
+#include "include/radosstriper/libradosstriper.hpp"
+#include "include/ceph_fs.h"
+#include "test/librados/test.h"
+#include "test/libradosstriper/TestCase.h"
+
+#include <string>
+#include <errno.h>
+using namespace librados;
+using namespace libradosstriper;
+
+class StriperTestRT : public StriperTestParam {
+public:
+ StriperTestRT() : StriperTestParam() {}
+protected:
+ char* getObjName(const std::string& soid, uint64_t nb)
+ {
+ char name[soid.size()+18];
+ sprintf(name, "%s.%016llx", soid.c_str(), (long long unsigned int)nb);
+ return strdup(name);
+ }
+
+ void checkObjectFromRados(const std::string& soid, bufferlist &bl,
+ uint64_t exp_stripe_unit, uint64_t exp_stripe_count,
+ uint64_t exp_object_size, size_t size)
+ {
+ checkObjectFromRados(soid, bl, exp_stripe_unit, exp_stripe_count, exp_object_size, size, size);
+ }
+
+ void checkObjectFromRados(const std::string& soid, bufferlist &bl,
+ uint64_t exp_stripe_unit, uint64_t exp_stripe_count,
+ uint64_t exp_object_size, size_t size,
+ size_t actual_size_if_sparse)
+ {
+ // checking first object's rados xattrs
+ bufferlist xattrbl;
+ char* firstOid = getObjName(soid, 0);
+ ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.layout.stripe_unit", xattrbl));
+ std::string s_xattr(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end
+ uint64_t stripe_unit = strtoll(s_xattr.c_str(), NULL, 10);
+ ASSERT_LT((unsigned)0, stripe_unit);
+ ASSERT_EQ(stripe_unit, exp_stripe_unit);
+ xattrbl.clear();
+ ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.layout.stripe_count", xattrbl));
+ s_xattr = std::string(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end
+ uint64_t stripe_count = strtoll(s_xattr.c_str(), NULL, 10);
+ ASSERT_LT(0U, stripe_count);
+ ASSERT_EQ(stripe_count, exp_stripe_count);
+ xattrbl.clear();
+ ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.layout.object_size", xattrbl));
+ s_xattr = std::string(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end
+ uint64_t object_size = strtoll(s_xattr.c_str(), NULL, 10);
+ ASSERT_EQ(object_size, exp_object_size);
+ xattrbl.clear();
+ ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.size", xattrbl));
+ s_xattr = std::string(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end
+ uint64_t xa_size = strtoll(s_xattr.c_str(), NULL, 10);
+ ASSERT_EQ(xa_size, size);
+ // checking object content from rados point of view
+ // we will go stripe by stripe, read the content of each of them and
+ // check with expectations
+ uint64_t stripe_per_object = object_size / stripe_unit;
+ uint64_t stripe_per_objectset = stripe_per_object * stripe_count;
+ uint64_t nb_stripes_in_object = (size+stripe_unit-1)/stripe_unit;
+ for (uint64_t stripe_nb = 0;
+ stripe_nb < nb_stripes_in_object;
+ stripe_nb++) {
+ // find out where this stripe is stored
+ uint64_t objectset = stripe_nb / stripe_per_objectset;
+ uint64_t stripe_in_object_set = stripe_nb % stripe_per_objectset;
+ uint64_t object_in_set = stripe_in_object_set % stripe_count;
+ uint64_t stripe_in_object = stripe_in_object_set / stripe_count;
+ uint64_t object_nb = objectset * stripe_count + object_in_set;
+ uint64_t start = stripe_in_object * stripe_unit;
+ uint64_t len = stripe_unit;
+ if (stripe_nb == nb_stripes_in_object-1 and size % stripe_unit != 0) {
+ len = size % stripe_unit;
+ }
+ // handle case of sparse object (can only be sparse at the end in our tests)
+ if (actual_size_if_sparse < size and
+ ((actual_size_if_sparse+stripe_unit-1)/stripe_unit)-1 == stripe_nb) {
+ len = actual_size_if_sparse % stripe_unit;
+ if (0 == len) len = stripe_unit;
+ }
+ bufferlist stripe_data;
+ // check object content
+ char* oid = getObjName(soid, object_nb);
+ int rc = ioctx.read(oid, stripe_data, len, start);
+ if (actual_size_if_sparse < size and
+ (actual_size_if_sparse+stripe_unit-1)/stripe_unit <= stripe_nb) {
+ // sparse object case : the stripe does not exist, but the rados object may
+ uint64_t object_start = (object_in_set + objectset*stripe_per_objectset) * stripe_unit;
+ if (actual_size_if_sparse <= object_start) {
+ ASSERT_EQ(rc, -ENOENT);
+ } else {
+ ASSERT_EQ(rc, 0);
+ }
+ } else {
+ ASSERT_EQ((uint64_t)rc, len);
+ bufferlist original_data;
+ original_data.substr_of(bl, stripe_nb*stripe_unit, len);
+ ASSERT_EQ(0, memcmp(original_data.c_str(), stripe_data.c_str(), len));
+ }
+ free(oid);
+ }
+ // checking rados object sizes; we go object by object
+ uint64_t nb_full_object_sets = nb_stripes_in_object / stripe_per_objectset;
+ uint64_t nb_extra_objects = nb_stripes_in_object % stripe_per_objectset;
+ if (nb_extra_objects > stripe_count) nb_extra_objects = stripe_count;
+ uint64_t nb_objects = nb_full_object_sets * stripe_count + nb_extra_objects;
+ for (uint64_t object_nb = 0; object_nb < nb_objects; object_nb++) {
+ uint64_t rados_size;
+ time_t mtime;
+ char* oid = getObjName(soid, object_nb);
+ uint64_t nb_full_object_set = object_nb / stripe_count;
+ uint64_t object_index_in_set = object_nb % stripe_count;
+ uint64_t object_start_stripe = nb_full_object_set * stripe_per_objectset + object_index_in_set;
+ uint64_t object_start_off = object_start_stripe * stripe_unit;
+ if (actual_size_if_sparse < size and actual_size_if_sparse <= object_start_off) {
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, &rados_size, &mtime));
+ } else {
+ ASSERT_EQ(0, ioctx.stat(oid, &rados_size, &mtime));
+ uint64_t offset;
+ uint64_t stripe_size = stripe_count * stripe_unit;
+ uint64_t set_size = stripe_count * object_size;
+ uint64_t len = 0;
+ for (offset = object_start_off;
+ (offset < (object_start_off) + set_size) && (offset < actual_size_if_sparse);
+ offset += stripe_size) {
+ if (offset + stripe_unit > actual_size_if_sparse) {
+ len += actual_size_if_sparse-offset;
+ } else {
+ len += stripe_unit;
+ }
+ }
+ ASSERT_EQ(len, rados_size);
+ }
+ free(oid);
+ }
+ // check we do not have an extra object behind
+ uint64_t rados_size;
+ time_t mtime;
+ char* oid = getObjName(soid, nb_objects);
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, &rados_size, &mtime));
+ free(oid);
+ free(firstOid);
+ }
+};
+
+TEST_P(StriperTestRT, StripedRoundtrip) {
+ // get striping parameters and apply them
+ TestData testData = GetParam();
+ ASSERT_EQ(0, striper.set_object_layout_stripe_unit(testData.stripe_unit));
+ ASSERT_EQ(0, striper.set_object_layout_stripe_count(testData.stripe_count));
+ ASSERT_EQ(0, striper.set_object_layout_object_size(testData.object_size));
+ std::ostringstream oss;
+ oss << "StripedRoundtrip_" << testData.stripe_unit << "_"
+ << testData.stripe_count << "_" << testData.object_size
+ << "_" << testData.size;
+ std::string soid = oss.str();
+ // writing striped data
+ std::unique_ptr<char[]> buf1;
+ bufferlist bl1;
+ {
+ SCOPED_TRACE("Writing initial object");
+ buf1 = std::make_unique<char[]>(testData.size);
+ for (unsigned int i = 0; i < testData.size; i++) buf1[i] = 13*((unsigned char)i);
+ bl1.append(buf1.get(), testData.size);
+ ASSERT_EQ(0, striper.write(soid, bl1, testData.size, 0));
+ // checking object state from Rados point of view
+ ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl1, testData.stripe_unit,
+ testData.stripe_count, testData.object_size,
+ testData.size));
+ }
+ // adding more data to object and checking again
+ std::unique_ptr<char[]> buf2;
+ bufferlist bl2;
+ {
+ SCOPED_TRACE("Testing append");
+ buf2 = std::make_unique<char[]>(testData.size);
+ for (unsigned int i = 0; i < testData.size; i++) buf2[i] = 17*((unsigned char)i);
+ bl2.append(buf2.get(), testData.size);
+ ASSERT_EQ(0, striper.append(soid, bl2, testData.size));
+ bl1.append(buf2.get(), testData.size);
+ ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl1, testData.stripe_unit,
+ testData.stripe_count, testData.object_size,
+ testData.size*2));
+ }
+ // truncating to half original size and checking again
+ {
+ SCOPED_TRACE("Testing trunc to truncate object");
+ ASSERT_EQ(0, striper.trunc(soid, testData.size/2));
+ ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl1, testData.stripe_unit,
+ testData.stripe_count, testData.object_size,
+ testData.size/2));
+ }
+ // truncating back to original size and checking again (especially for 0s)
+ {
+ SCOPED_TRACE("Testing trunc to extend object with 0s");
+ ASSERT_EQ(0, striper.trunc(soid, testData.size));
+ bufferlist bl3;
+ bl3.substr_of(bl1, 0, testData.size/2);
+ bl3.append_zero(testData.size - testData.size/2);
+ ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl3, testData.stripe_unit,
+ testData.stripe_count, testData.object_size,
+ testData.size, testData.size/2));
+ }
+ {
+ SCOPED_TRACE("Testing write_full");
+ // using write_full and checking again
+ ASSERT_EQ(0, striper.write_full(soid, bl2));
+ checkObjectFromRados(soid, bl2, testData.stripe_unit,
+ testData.stripe_count, testData.object_size,
+ testData.size);
+ }
+ {
+ SCOPED_TRACE("Testing standard remove");
+ // call remove
+ ASSERT_EQ(0, striper.remove(soid));
+ // check that the removal was successful
+ uint64_t size;
+ time_t mtime;
+ for (uint64_t object_nb = 0;
+ object_nb < testData.size*2/testData.object_size + testData.stripe_count;
+ object_nb++) {
+ char* oid = getObjName(soid, object_nb);
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, &mtime));
+ free(oid);
+ }
+ }
+ {
+ SCOPED_TRACE("Testing remove when no object size");
+ // recreate object
+ ASSERT_EQ(0, striper.write(soid, bl1, testData.size*2, 0));
+ // remove the object size attribute from the striped object
+ char* firstOid = getObjName(soid, 0);
+ ASSERT_EQ(0, ioctx.rmxattr(firstOid, "striper.size"));
+ free(firstOid);
+ // check that stat fails
+ uint64_t size;
+ time_t mtime;
+ ASSERT_EQ(-ENODATA, striper.stat(soid, &size, &mtime));
+ // call remove
+ ASSERT_EQ(0, striper.remove(soid));
+ // check that the removal was successful
+ for (uint64_t object_nb = 0;
+ object_nb < testData.size*2/testData.object_size + testData.stripe_count;
+ object_nb++) {
+ char* oid = getObjName(soid, object_nb);
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, &mtime));
+ free(oid);
+ }
+ }
+}
+
+const TestData simple_stripe_schemes[] = {
+ // stripe_unit, stripe_count, object_size, size
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 8*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 15*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 25*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 45*CEPH_MIN_STRIPE_UNIT+100},
+ {262144, 5, 262144, 2},
+ {262144, 5, 262144, 262144},
+ {262144, 5, 262144, 262144-1},
+ {262144, 5, 262144, 2*262144},
+ {262144, 5, 262144, 12*262144},
+ {262144, 5, 262144, 2*262144-1},
+ {262144, 5, 262144, 12*262144-1},
+ {262144, 5, 262144, 2*262144+100},
+ {262144, 5, 262144, 12*262144+100},
+ {262144, 5, 3*262144, 2*262144+100},
+ {262144, 5, 3*262144, 8*262144+100},
+ {262144, 5, 3*262144, 12*262144+100},
+ {262144, 5, 3*262144, 15*262144+100},
+ {262144, 5, 3*262144, 25*262144+100},
+ {262144, 5, 3*262144, 45*262144+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 8*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 15*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 25*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 45*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT-1},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 8*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 15*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 25*CEPH_MIN_STRIPE_UNIT+100},
+ {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 45*CEPH_MIN_STRIPE_UNIT+100}
+};
+
+INSTANTIATE_TEST_SUITE_P(SimpleStriping,
+ StriperTestRT,
+ ::testing::ValuesIn(simple_stripe_schemes));
diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt
new file mode 100644
index 000000000..87984bd94
--- /dev/null
+++ b/src/test/librbd/CMakeLists.txt
@@ -0,0 +1,232 @@
+set(librbd_test_support_srcs
+ test_support.cc
+ )
+add_library(rbd_test_support STATIC ${librbd_test_support_srcs})
+target_link_libraries(rbd_test_support PRIVATE
+ GTest::GTest)
+
+set(librbd_test
+ test_fixture.cc
+ test_librbd.cc
+ test_ImageWatcher.cc
+ test_internal.cc
+ test_mirroring.cc
+ test_DeepCopy.cc
+ test_Groups.cc
+ test_Migration.cc
+ test_MirroringWatcher.cc
+ test_ObjectMap.cc
+ test_Operations.cc
+ test_Trash.cc
+ journal/test_Entries.cc
+ journal/test_Replay.cc)
+add_library(rbd_test STATIC ${librbd_test})
+target_link_libraries(rbd_test PRIVATE
+ rbd_test_support
+ radostest
+ radostest-cxx
+ librados
+ Boost::thread
+ GMock::GMock
+ GTest::GTest)
+
+set(librbd_test_mock_srcs
+ mock/MockImageCtx.cc
+ mock/MockJournal.cc)
+add_library(rbd_test_mock STATIC ${librbd_test_mock_srcs})
+target_link_libraries(rbd_test_mock PUBLIC
+ GMock::GMock)
+
+# unittest_librbd
+# doesn't use add_ceph_test because it is called by run-rbd-unit-tests.sh
+set(unittest_librbd_srcs
+ test_BlockGuard.cc
+ test_main.cc
+ test_mock_fixture.cc
+ test_mock_ConfigWatcher.cc
+ test_mock_DeepCopyRequest.cc
+ test_mock_ExclusiveLock.cc
+ test_mock_Journal.cc
+ test_mock_ManagedLock.cc
+ test_mock_ObjectMap.cc
+ test_mock_TrashWatcher.cc
+ test_mock_Watcher.cc
+ cache/test_mock_WriteAroundObjectDispatch.cc
+ cache/test_mock_ParentCacheObjectDispatch.cc
+ crypto/test_mock_BlockCrypto.cc
+ crypto/test_mock_CryptoContextPool.cc
+ crypto/test_mock_CryptoObjectDispatch.cc
+ crypto/test_mock_FormatRequest.cc
+ crypto/test_mock_LoadRequest.cc
+ crypto/test_mock_ShutDownCryptoRequest.cc
+ crypto/openssl/test_DataCryptor.cc
+ deep_copy/test_mock_ImageCopyRequest.cc
+ deep_copy/test_mock_MetadataCopyRequest.cc
+ deep_copy/test_mock_ObjectCopyRequest.cc
+ deep_copy/test_mock_SetHeadRequest.cc
+ deep_copy/test_mock_SnapshotCopyRequest.cc
+ deep_copy/test_mock_SnapshotCreateRequest.cc
+ exclusive_lock/test_mock_PreAcquireRequest.cc
+ exclusive_lock/test_mock_PostAcquireRequest.cc
+ exclusive_lock/test_mock_PreReleaseRequest.cc
+ image/test_mock_AttachChildRequest.cc
+ image/test_mock_AttachParentRequest.cc
+ image/test_mock_CloneRequest.cc
+ image/test_mock_DetachChildRequest.cc
+ image/test_mock_DetachParentRequest.cc
+ image/test_mock_ListWatchersRequest.cc
+ image/test_mock_PreRemoveRequest.cc
+ image/test_mock_RefreshRequest.cc
+ image/test_mock_RemoveRequest.cc
+ image/test_mock_ValidatePoolRequest.cc
+ io/test_mock_CopyupRequest.cc
+ io/test_mock_ImageRequest.cc
+ io/test_mock_ObjectRequest.cc
+ io/test_mock_SimpleSchedulerObjectDispatch.cc
+ journal/test_mock_OpenRequest.cc
+ journal/test_mock_PromoteRequest.cc
+ journal/test_mock_Replay.cc
+ journal/test_mock_ResetRequest.cc
+ managed_lock/test_mock_AcquireRequest.cc
+ managed_lock/test_mock_BreakRequest.cc
+ managed_lock/test_mock_GetLockerRequest.cc
+ managed_lock/test_mock_ReacquireRequest.cc
+ managed_lock/test_mock_ReleaseRequest.cc
+ migration/test_mock_FileStream.cc
+ migration/test_mock_HttpClient.cc
+ migration/test_mock_HttpStream.cc
+ migration/test_mock_RawFormat.cc
+ migration/test_mock_RawSnapshot.cc
+ migration/test_mock_QCOWFormat.cc
+ migration/test_mock_S3Stream.cc
+ migration/test_mock_Utils.cc
+ mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc
+ mirror/snapshot/test_mock_CreatePrimaryRequest.cc
+ mirror/snapshot/test_mock_ImageMeta.cc
+ mirror/snapshot/test_mock_PromoteRequest.cc
+ mirror/snapshot/test_mock_UnlinkPeerRequest.cc
+ mirror/snapshot/test_mock_Utils.cc
+ mirror/test_mock_DisableRequest.cc
+ object_map/test_mock_DiffRequest.cc
+ object_map/test_mock_InvalidateRequest.cc
+ object_map/test_mock_LockRequest.cc
+ object_map/test_mock_RefreshRequest.cc
+ object_map/test_mock_ResizeRequest.cc
+ object_map/test_mock_SnapshotCreateRequest.cc
+ object_map/test_mock_SnapshotRemoveRequest.cc
+ object_map/test_mock_SnapshotRollbackRequest.cc
+ object_map/test_mock_UnlockRequest.cc
+ object_map/test_mock_UpdateRequest.cc
+ operation/test_mock_DisableFeaturesRequest.cc
+ operation/test_mock_EnableFeaturesRequest.cc
+ operation/test_mock_Request.cc
+ operation/test_mock_ResizeRequest.cc
+ operation/test_mock_SnapshotCreateRequest.cc
+ operation/test_mock_SnapshotProtectRequest.cc
+ operation/test_mock_SnapshotRemoveRequest.cc
+ operation/test_mock_SnapshotRollbackRequest.cc
+ operation/test_mock_SnapshotUnprotectRequest.cc
+ operation/test_mock_TrimRequest.cc
+ trash/test_mock_MoveRequest.cc
+ trash/test_mock_RemoveRequest.cc
+ watcher/test_mock_RewatchRequest.cc
+ )
+
+if(WITH_RBD_RWL OR WITH_RBD_SSD_CACHE)
+ list(APPEND unittest_librbd_srcs
+ cache/pwl/test_WriteLogMap.cc)
+ if(WITH_RBD_RWL)
+ list(APPEND unittest_librbd_srcs
+ cache/pwl/test_mock_ReplicatedWriteLog.cc)
+ endif()
+ if(WITH_RBD_SSD_CACHE)
+ list(APPEND unittest_librbd_srcs
+ cache/pwl/test_mock_SSDWriteLog.cc)
+ endif()
+endif()
+
+if(LINUX AND HAVE_LIBCRYPTSETUP)
+ list(APPEND unittest_librbd_srcs
+ crypto/luks/test_mock_FlattenRequest.cc
+ crypto/luks/test_mock_FormatRequest.cc
+ crypto/luks/test_mock_LoadRequest.cc)
+endif()
+
+add_executable(unittest_librbd
+ ${unittest_librbd_srcs}
+ $<TARGET_OBJECTS:common_texttable_obj>)
+target_compile_definitions(unittest_librbd PRIVATE "TEST_LIBRBD_INTERNALS")
+set_target_properties(unittest_librbd PROPERTIES
+ JOB_POOL_COMPILE heavy_compile_job_pool
+ JOB_POOL_LINK heavy_link_job_pool)
+add_dependencies(unittest_librbd
+ cls_journal
+ cls_lock
+ cls_rbd)
+target_link_libraries(unittest_librbd
+ rbd_test
+ rbd_api
+ rbd_internal
+ rbd_test_mock
+ journal
+ journal_test_mock
+ cls_rbd_client
+ cls_lock_client
+ cls_journal_client
+ rbd_types
+ rados_test_stub
+ librados
+ ceph_immutable_object_cache_lib
+ osdc
+ ceph-common
+ global
+ OpenSSL::SSL
+ ${UNITTEST_LIBS})
+
+if(WITH_RBD_RWL OR WITH_RBD_SSD_CACHE)
+ target_link_libraries(unittest_librbd
+ librbd_plugin_pwl_cache)
+endif()
+
+add_executable(ceph_test_librbd
+ test_main.cc
+ $<TARGET_OBJECTS:common_texttable_obj>)
+target_link_libraries(ceph_test_librbd
+ rbd_test
+ rbd_api
+ rbd_internal
+ rbd_types
+ journal
+ cls_journal_client
+ cls_rbd_client
+ libneorados
+ librados
+ ${UNITTEST_LIBS}
+ radostest)
+target_compile_definitions(ceph_test_librbd PRIVATE "TEST_LIBRBD_INTERNALS")
+
+add_executable(ceph_test_librbd_fsx
+ fsx.cc
+ $<TARGET_OBJECTS:common_texttable_obj>
+ )
+target_link_libraries(ceph_test_librbd_fsx
+ librbd
+ librados
+ journal
+ global
+ m
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ )
+if(WITH_KRBD)
+ target_link_libraries(ceph_test_librbd_fsx
+ krbd)
+endif()
+install(TARGETS
+ ceph_test_librbd_fsx
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+install(TARGETS
+ ceph_test_librbd
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/librbd/cache/pwl/test_WriteLogMap.cc b/src/test/librbd/cache/pwl/test_WriteLogMap.cc
new file mode 100644
index 000000000..1cafb00ff
--- /dev/null
+++ b/src/test/librbd/cache/pwl/test_WriteLogMap.cc
@@ -0,0 +1,338 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+
+#include "librbd/cache/pwl/LogMap.cc"
+
+void register_test_write_log_map() {
+}
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using namespace std;
+
+struct TestLogEntry {
+ uint64_t image_offset_bytes;
+ uint64_t write_bytes;
+ uint32_t referring_map_entries = 0;
+ TestLogEntry(const uint64_t image_offset_bytes, const uint64_t write_bytes)
+ : image_offset_bytes(image_offset_bytes), write_bytes(write_bytes) {
+ }
+ uint64_t get_offset_bytes() {
+ return image_offset_bytes;
+ }
+ uint64_t get_write_bytes() {
+ return write_bytes;
+ }
+ BlockExtent block_extent() {
+ return BlockExtent(image_offset_bytes, image_offset_bytes + write_bytes);
+ }
+ uint32_t get_map_ref() {
+ return referring_map_entries;
+ }
+ void inc_map_ref() {
+ referring_map_entries++;
+ }
+ void dec_map_ref() {
+ referring_map_entries--;
+ }
+ friend std::ostream &operator<<(std::ostream &os,
+ const TestLogEntry &entry) {
+ os << "referring_map_entries=" << entry.referring_map_entries << ", "
+ << "image_offset_bytes=" << entry.image_offset_bytes << ", "
+ << "write_bytes=" << entry.write_bytes;
+ return os;
+ };
+};
+
+typedef std::list<std::shared_ptr<TestLogEntry>> TestLogEntries;
+typedef LogMapEntry<TestLogEntry> TestMapEntry;
+typedef LogMapEntries<TestLogEntry> TestLogMapEntries;
+typedef LogMap<TestLogEntry> TestLogMap;
+
+class TestWriteLogMap : public TestFixture {
+public:
+ void SetUp() override {
+ TestFixture::SetUp();
+ m_cct = reinterpret_cast<CephContext*>(m_ioctx.cct());
+ }
+
+ CephContext *m_cct;
+};
+
+TEST_F(TestWriteLogMap, Simple) {
+ TestLogEntries es;
+ TestLogMapEntries lme;
+ TestLogMap map(m_cct);
+
+ /* LogEntry takes offset, length, in bytes */
+ auto e1 = make_shared<TestLogEntry>(4, 8);
+ TestLogEntry *e1_ptr = e1.get();
+ ASSERT_EQ(4, e1_ptr->get_offset_bytes());
+ ASSERT_EQ(8, e1_ptr->get_write_bytes());
+ map.add_log_entry(e1);
+
+ /* BlockExtent takes first, last, in blocks */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ /* Written range includes the single write above */
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+
+ /* Nothing before that */
+ found0 = map.find_map_entries(BlockExtent(0, 3));
+ numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+
+ /* Nothing after that */
+ found0 = map.find_map_entries(BlockExtent(12, 99));
+ numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+
+ /* 4-11 will be e1 */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+ map.remove_log_entry(e1);
+ /* Nothing should be found */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+ }
+}
+
+TEST_F(TestWriteLogMap, OverlapFront) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(4, 8);
+ map.add_log_entry(e0);
+ /* replaces block 4-7 of e0 */
+ auto e1 = make_shared<TestLogEntry>(0, 8);
+ map.add_log_entry(e1);
+
+ /* Written range includes the two writes above */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ ASSERT_EQ(2, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ ASSERT_EQ(0, found0.front().block_extent.block_start);
+ ASSERT_EQ(8, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(8, found0.front().block_extent.block_start);
+ ASSERT_EQ(12, found0.front().block_extent.block_end);
+
+ /* 0-7 will be e1 */
+ for (int i=0; i<8; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+ /* 8-11 will be e0 */
+ for (int i=8; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ }
+}
+
+TEST_F(TestWriteLogMap, OverlapBack) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(0, 8);
+ map.add_log_entry(e0);
+ /* replaces block 4-7 of e0 */
+ auto e1 = make_shared<TestLogEntry>(4, 8);
+ map.add_log_entry(e1);
+
+ /* Written range includes the two writes above */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ ASSERT_EQ(2, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(0, found0.front().block_extent.block_start);
+ ASSERT_EQ(4, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e1, found0.front().log_entry);
+ ASSERT_EQ(4, found0.front().block_extent.block_start);
+ ASSERT_EQ(12, found0.front().block_extent.block_end);
+
+ /* 0-3 will be e0 */
+ for (int i=0; i<4; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ }
+
+ /* 4-11 will be e1 */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+ map.remove_log_entry(e0);
+
+ /* 0-3 will find nothing */
+ for (int i=0; i<4; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+ }
+
+ /* 4-11 will still be e1 */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+}
+
+TEST_F(TestWriteLogMap, OverlapMiddle) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(0, 1);
+ map.add_log_entry(e0);
+
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ TestLogEntries entries = map.find_log_entries(BlockExtent(0, 1));
+ int entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e0, entries.front());
+
+ auto e1 = make_shared<TestLogEntry>(1, 1);
+ map.add_log_entry(e1);
+
+ found0 = map.find_map_entries(BlockExtent(1, 2));
+ numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(1, 2));
+ entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e1, entries.front());
+
+ auto e2 = make_shared<TestLogEntry>(2, 1);
+ map.add_log_entry(e2);
+
+ found0 = map.find_map_entries(BlockExtent(2, 3));
+ numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e2, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(2, 3));
+ entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e2, entries.front());
+
+ /* replaces e1 */
+ auto e3 = make_shared<TestLogEntry>(1, 1);
+ map.add_log_entry(e3);
+
+ found0 = map.find_map_entries(BlockExtent(1, 2));
+ numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e3, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(1, 2));
+ entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e3, entries.front());
+
+ found0 = map.find_map_entries(BlockExtent(0, 100));
+ numfound = found0.size();
+ ASSERT_EQ(3, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ found0.pop_front();
+ ASSERT_EQ(e3, found0.front().log_entry);
+ found0.pop_front();
+ ASSERT_EQ(e2, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(0, 100));
+ entriesfound = entries.size();
+ ASSERT_EQ(3, entriesfound);
+ ASSERT_EQ(e0, entries.front());
+ entries.pop_front();
+ ASSERT_EQ(e3, entries.front());
+ entries.pop_front();
+ ASSERT_EQ(e2, entries.front());
+
+ entries.clear();
+ entries.emplace_back(e0);
+ entries.emplace_back(e1);
+ map.remove_log_entries(entries);
+
+ found0 = map.find_map_entries(BlockExtent(0, 100));
+ numfound = found0.size();
+ ASSERT_EQ(2, numfound);
+ ASSERT_EQ(e3, found0.front().log_entry);
+ found0.pop_front();
+ ASSERT_EQ(e2, found0.front().log_entry);
+}
+
+TEST_F(TestWriteLogMap, OverlapSplit) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(0, 8);
+ map.add_log_entry(e0);
+
+ /* Splits e0 at 1 */
+ auto e1 = make_shared<TestLogEntry>(1, 1);
+ map.add_log_entry(e1);
+
+ /* Splits e0 again at 4 */
+ auto e2 = make_shared<TestLogEntry>(4, 2);
+ map.add_log_entry(e2);
+
+ /* Replaces one block of e2, and one of e0 */
+ auto e3 = make_shared<TestLogEntry>(5, 2);
+ map.add_log_entry(e3);
+
+ /* Expecting: 0:e0, 1:e1, 2..3:e0, 4:e2, 5..6:e3, 7:e0 */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ ASSERT_EQ(6, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(0, found0.front().block_extent.block_start);
+ ASSERT_EQ(1, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e1, found0.front().log_entry);
+ ASSERT_EQ(1, found0.front().block_extent.block_start);
+ ASSERT_EQ(2, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(2, found0.front().block_extent.block_start);
+ ASSERT_EQ(4, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e2, found0.front().log_entry);
+ ASSERT_EQ(4, found0.front().block_extent.block_start);
+ ASSERT_EQ(5, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e3, found0.front().log_entry);
+ ASSERT_EQ(5, found0.front().block_extent.block_start);
+ ASSERT_EQ(7, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(7, found0.front().block_extent.block_start);
+ ASSERT_EQ(8, found0.front().block_extent.block_end);
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
diff --git a/src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc b/src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc
new file mode 100644
index 000000000..a37f58038
--- /dev/null
+++ b/src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc
@@ -0,0 +1,743 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include "common/hostname.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/cache/pwl/ImageCacheState.h"
+#include "librbd/cache/pwl/Types.h"
+#include "librbd/cache/ImageWriteback.h"
+#include "librbd/plugin/Api.h"
+
+namespace librbd {
+namespace {
+
+struct MockContextRWL : public C_SaferCond {
+ MOCK_METHOD1(complete, void(int));
+ MOCK_METHOD1(finish, void(int));
+
+ void do_complete(int r) {
+ C_SaferCond::complete(r);
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/cache/pwl/AbstractWriteLog.cc"
+#include "librbd/cache/pwl/rwl/WriteLog.cc"
+template class librbd::cache::pwl::rwl::WriteLog<librbd::MockImageCtx>;
+
+// template definitions
+#include "librbd/cache/ImageWriteback.cc"
+#include "librbd/cache/pwl/ImageCacheState.cc"
+#include "librbd/cache/pwl/Request.cc"
+#include "librbd/cache/pwl/rwl/Request.cc"
+#include "librbd/plugin/Api.cc"
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+typedef io::Extent Extent;
+typedef io::Extents Extents;
+
+struct TestMockCacheReplicatedWriteLog : public TestMockFixture {
+ typedef librbd::cache::pwl::rwl::WriteLog<librbd::MockImageCtx> MockReplicatedWriteLog;
+ typedef librbd::cache::pwl::ImageCacheState<librbd::MockImageCtx> MockImageCacheStateRWL;
+ typedef librbd::cache::ImageWriteback<librbd::MockImageCtx> MockImageWriteback;
+ typedef librbd::plugin::Api<librbd::MockImageCtx> MockApi;
+
+ MockImageCacheStateRWL *get_cache_state(
+ MockImageCtx& mock_image_ctx, MockApi& mock_api) {
+ MockImageCacheStateRWL *rwl_state = new MockImageCacheStateRWL(&mock_image_ctx, mock_api);
+ return rwl_state;
+ }
+
+ void validate_cache_state(librbd::ImageCtx *image_ctx,
+ MockImageCacheStateRWL &state,
+ bool present, bool empty, bool clean,
+ string host, string path,
+ uint64_t size) {
+ ASSERT_EQ(present, state.present);
+ ASSERT_EQ(empty, state.empty);
+ ASSERT_EQ(clean, state.clean);
+
+ ASSERT_EQ(host, state.host);
+ ASSERT_EQ(path, state.path);
+ ASSERT_EQ(size, state.size);
+ }
+
+ void expect_context_complete(MockContextRWL& mock_context, int r) {
+ EXPECT_CALL(mock_context, complete(r))
+ .WillRepeatedly(Invoke([&mock_context](int r) {
+ mock_context.do_complete(r);
+ }));
+ }
+
+ void expect_metadata_set(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(_, _, _))
+ .WillRepeatedly(Invoke([](std::string key, std::string val, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+
+ void expect_metadata_remove(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(_, _))
+ .WillRepeatedly(Invoke([](std::string key, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+};
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_state_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateRWL image_cache_state(&mock_image_ctx, mock_api);
+
+ validate_cache_state(ictx, image_cache_state, false, true, true, "", "", 0);
+
+ image_cache_state.empty = false;
+ image_cache_state.clean = false;
+ ceph::mutex lock = ceph::make_mutex("MockImageCacheStateRWL lock");
+ MockContextRWL finish_ctx;
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ std::unique_lock locker(lock);
+ image_cache_state.write_image_cache_state(locker, &finish_ctx);
+ ASSERT_FALSE(locker.owns_lock());
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_state_json_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateRWL image_cache_state(&mock_image_ctx, mock_api);
+
+ string strf = "{ \"present\": true, \"empty\": false, \"clean\": false, \
+ \"host\": \"testhost\", \
+ \"path\": \"/tmp\", \
+ \"mode\": \"rwl\", \
+ \"size\": 1024 }";
+ json_spirit::mValue json_root;
+ ASSERT_TRUE(json_spirit::read(strf.c_str(), json_root));
+ ASSERT_TRUE(image_cache_state.init_from_metadata(json_root));
+ validate_cache_state(ictx, image_cache_state, true, false, false,
+ "testhost", "/tmp", 1024);
+
+ MockContextRWL finish_ctx;
+ expect_metadata_remove(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ image_cache_state.clear_image_cache_state(&finish_ctx);
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ MockContextRWL finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ rwl.shut_down(&finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ MockContextRWL finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(&finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_SHUTDOWN, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_internal) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_INTERNAL, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_user) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ usleep(10000);
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_USER, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_hit_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ expect_context_complete(finish_ctx_read, 0);
+ Extents image_extents_read{{0, 4096}};
+ bufferlist read_bl;
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_hit_part_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ Extents image_extents_read{{512, 4096}};
+ bufferlist hit_bl;
+ bl_copy.begin(511).copy(4096-512, hit_bl);
+ expect_context_complete(finish_ctx_read, 512);
+ bufferlist read_bl;
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(512, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ bufferlist read_bl_hit;
+ read_bl.begin(0).copy(4096-512, read_bl_hit);
+ ASSERT_TRUE(hit_bl.contents_equal(read_bl_hit));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_miss_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ Extents image_extents_read{{4096, 4096}};
+ expect_context_complete(finish_ctx_read, 4096);
+ bufferlist read_bl;
+ ASSERT_EQ(0, read_bl.length());
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(4096, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, discard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_discard;
+ expect_context_complete(finish_ctx_discard, 0);
+ rwl.discard(0, 4096, 1, &finish_ctx_discard);
+ ASSERT_EQ(0, finish_ctx_discard.wait());
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(read_bl.is_zero());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, writesame) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ bufferlist bl, test_bl;
+ bl.append(std::string(512, '1'));
+ test_bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.writesame(0, 4096, std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(test_bl.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, invalidate) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_invalidate;
+ expect_context_complete(finish_ctx_invalidate, 0);
+ rwl.invalidate(&finish_ctx_invalidate);
+ ASSERT_EQ(0, finish_ctx_invalidate.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_matched) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist com_bl = bl1;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist bl2_copy = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, 0);
+ rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(0, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl2_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_failed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist bl1_copy = bl1;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist com_bl = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, -EILSEQ);
+ rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(-EILSEQ, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl1_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
diff --git a/src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc b/src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc
new file mode 100644
index 000000000..72a44dcc9
--- /dev/null
+++ b/src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc
@@ -0,0 +1,761 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include "common/hostname.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/cache/pwl/AbstractWriteLog.h"
+#include "librbd/cache/pwl/ImageCacheState.h"
+#include "librbd/cache/pwl/Types.h"
+#include "librbd/cache/ImageWriteback.h"
+#include "librbd/plugin/Api.h"
+
+namespace librbd {
+namespace {
+
+struct MockContextSSD : public C_SaferCond {
+ MOCK_METHOD1(complete, void(int));
+ MOCK_METHOD1(finish, void(int));
+
+ void do_complete(int r) {
+ C_SaferCond::complete(r);
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/cache/pwl/AbstractWriteLog.cc"
+#include "librbd/cache/pwl/ssd/WriteLog.cc"
+template class librbd::cache::pwl::ssd::WriteLog<librbd::MockImageCtx>;
+
+// template definitions
+#include "librbd/cache/ImageWriteback.cc"
+#include "librbd/cache/pwl/ImageCacheState.cc"
+#include "librbd/cache/pwl/Request.cc"
+#include "librbd/plugin/Api.cc"
+#include "librbd/cache/pwl/ssd/Request.cc"
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+typedef io::Extent Extent;
+typedef io::Extents Extents;
+
+struct TestMockCacheSSDWriteLog : public TestMockFixture {
+ typedef librbd::cache::pwl::ssd::WriteLog<librbd::MockImageCtx> MockSSDWriteLog;
+ typedef librbd::cache::pwl::ImageCacheState<librbd::MockImageCtx> MockImageCacheStateSSD;
+ typedef librbd::cache::ImageWriteback<librbd::MockImageCtx> MockImageWriteback;
+ typedef librbd::plugin::Api<librbd::MockImageCtx> MockApi;
+
+ MockImageCacheStateSSD *get_cache_state(
+ MockImageCtx& mock_image_ctx, MockApi& mock_api) {
+ MockImageCacheStateSSD *ssd_state = new MockImageCacheStateSSD(
+ &mock_image_ctx, mock_api);
+ return ssd_state;
+ }
+
+ void validate_cache_state(librbd::ImageCtx *image_ctx,
+ MockImageCacheStateSSD &state,
+ bool present, bool empty, bool clean,
+ string host, string path,
+ uint64_t size) {
+ ASSERT_EQ(present, state.present);
+ ASSERT_EQ(empty, state.empty);
+ ASSERT_EQ(clean, state.clean);
+
+ ASSERT_EQ(host, state.host);
+ ASSERT_EQ(path, state.path);
+ ASSERT_EQ(size, state.size);
+ }
+
+ void expect_context_complete(MockContextSSD& mock_context, int r) {
+ EXPECT_CALL(mock_context, complete(r))
+ .WillRepeatedly(Invoke([&mock_context](int r) {
+ mock_context.do_complete(r);
+ }));
+ }
+
+ void expect_metadata_set(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(_, _, _))
+ .WillRepeatedly(Invoke([](std::string key, std::string val, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+
+ void expect_metadata_remove(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(_, _))
+ .WillRepeatedly(Invoke([](std::string key, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+};
+
+TEST_F(TestMockCacheSSDWriteLog, init_state_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateSSD image_cache_state(&mock_image_ctx, mock_api);
+
+ validate_cache_state(ictx, image_cache_state, false, true, true, "", "", 0);
+
+ image_cache_state.empty = false;
+ image_cache_state.clean = false;
+ ceph::mutex lock = ceph::make_mutex("MockImageCacheStateSSD lock");
+ MockContextSSD finish_ctx;
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ std::unique_lock locker(lock);
+ image_cache_state.write_image_cache_state(locker, &finish_ctx);
+ ASSERT_FALSE(locker.owns_lock());
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, init_state_json_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateSSD image_cache_state(&mock_image_ctx, mock_api);
+
+ string strf = "{ \"present\": true, \"empty\": false, \"clean\": false, \
+ \"host\": \"testhost\", \
+ \"path\": \"/tmp\", \
+ \"mode\": \"ssd\", \
+ \"size\": 1024 }";
+ json_spirit::mValue json_root;
+ ASSERT_TRUE(json_spirit::read(strf.c_str(), json_root));
+ ASSERT_TRUE(image_cache_state.init_from_metadata(json_root));
+ validate_cache_state(ictx, image_cache_state, true, false, false,
+ "testhost", "/tmp", 1024);
+
+ MockContextSSD finish_ctx;
+ expect_metadata_remove(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ image_cache_state.clear_image_cache_state(&finish_ctx);
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, init_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ MockContextSSD finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ ssd.shut_down(&finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ MockContextSSD finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, read_hit_ssd_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl),
+ fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ expect_context_complete(finish_ctx_read, 0);
+ Extents image_extents_read{{0, 4096}};
+ bufferlist read_bl;
+ ssd.read(std::move(image_extents_read), &read_bl,
+ fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl_copy.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, read_hit_part_ssd_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 8192}};
+ bufferlist bl;
+ bl.append(std::string(8192, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl),
+ fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ Extents image_extents_read{{4096, 4096}};
+ bufferlist hit_bl;
+ bl_copy.begin(4095).copy(4096, hit_bl);
+ expect_context_complete(finish_ctx_read, 0);
+ bufferlist read_bl;
+ ssd.read(std::move(image_extents_read), &read_bl,
+ fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ bufferlist read_bl_hit;
+ read_bl.begin(0).copy(4096, read_bl_hit);
+ ASSERT_TRUE(hit_bl.contents_equal(read_bl_hit));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, read_miss_ssd_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl),
+ fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ Extents image_extents_read{{4096, 4096}};
+ expect_context_complete(finish_ctx_read, 4096);
+ bufferlist read_bl;
+ ASSERT_EQ(0, read_bl.length());
+ ssd.read(std::move(image_extents_read), &read_bl,
+ fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(4096, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, compare_and_write_compare_matched) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist com_bl = bl1;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist bl2_copy = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, 0);
+ ssd.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(0, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl2_copy.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, compare_and_write_compare_failed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist bl1_copy = bl1;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist com_bl = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, -EILSEQ);
+ ssd.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(-EILSEQ, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl1_copy.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, writesame) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ bufferlist bl, test_bl;
+ bl.append(std::string(512, '1'));
+ test_bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.writesame(0, 4096, std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(test_bl.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, discard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_discard;
+ expect_context_complete(finish_ctx_discard, 0);
+ ssd.discard(0, 4096, 1, &finish_ctx_discard);
+ ASSERT_EQ(0, finish_ctx_discard.wait());
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(read_bl.is_zero());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, invalidate) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_invalidate;
+ expect_context_complete(finish_ctx_invalidate, 0);
+ ssd.invalidate(&finish_ctx_invalidate);
+ ASSERT_EQ(0, finish_ctx_invalidate.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, flush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(&finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, flush_source_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(io::FLUSH_SOURCE_SHUTDOWN, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+
+TEST_F(TestMockCacheSSDWriteLog, flush_source_internal) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(io::FLUSH_SOURCE_INTERNAL, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, flush_source_user) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ usleep(10000);
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(io::FLUSH_SOURCE_USER, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ Extents image_extents2{{0, 4096}};
+ bufferlist bl2;
+ bl2.append(std::string(4096, '1'));
+ int fadvise_flags2 = 0;
+ ssd.write(std::move(image_extents2), std::move(bl2), fadvise_flags2, &finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+
+ MockContextSSD finish_ctx4;
+ expect_context_complete(finish_ctx4, 0);
+ ssd.shut_down(&finish_ctx4);
+ ASSERT_EQ(0, finish_ctx4.wait());
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
diff --git a/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc b/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc
new file mode 100644
index 000000000..05e56f520
--- /dev/null
+++ b/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc
@@ -0,0 +1,427 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "include/Context.h"
+#include "tools/immutable_object_cache/CacheClient.h"
+#include "test/immutable_object_cache/MockCacheDaemon.h"
+#include "librbd/cache/ParentCacheObjectDispatch.h"
+#include "librbd/plugin/Api.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+using namespace ceph::immutable_obj_cache;
+
+namespace librbd {
+
+namespace {
+
+struct MockParentImageCacheImageCtx : public MockImageCtx {
+ MockParentImageCacheImageCtx(ImageCtx& image_ctx)
+ : MockImageCtx(image_ctx) {
+ }
+ ~MockParentImageCacheImageCtx() {}
+};
+
+} // anonymous namespace
+
+namespace cache {
+
+template<>
+struct TypeTraits<MockParentImageCacheImageCtx> {
+ typedef ceph::immutable_obj_cache::MockCacheClient CacheClient;
+};
+
+} // namespace cache
+
+namespace plugin {
+
+template <>
+struct Api<MockParentImageCacheImageCtx> {
+ MOCK_METHOD6(read_parent, void(MockParentImageCacheImageCtx*, uint64_t,
+ librbd::io::ReadExtents*, librados::snap_t,
+ const ZTracer::Trace &, Context*));
+};
+
+} // namespace plugin
+} // namespace librbd
+
+#include "librbd/cache/ParentCacheObjectDispatch.cc"
+template class librbd::cache::ParentCacheObjectDispatch<librbd::MockParentImageCacheImageCtx>;
+
+namespace librbd {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class TestMockParentCacheObjectDispatch : public TestMockFixture {
+public :
+ typedef cache::ParentCacheObjectDispatch<librbd::MockParentImageCacheImageCtx> MockParentImageCache;
+ typedef plugin::Api<MockParentImageCacheImageCtx> MockPluginApi;
+
+ // ====== mock cache client ====
+ void expect_cache_run(MockParentImageCache& mparent_image_cache, bool ret_val) {
+ auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), run());
+
+ expect.WillOnce((Invoke([]() {
+ })));
+ }
+
+ void expect_cache_session_state(MockParentImageCache& mparent_image_cache, bool ret_val) {
+ auto & expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), is_session_work());
+
+ expect.WillOnce((Invoke([ret_val]() {
+ return ret_val;
+ })));
+ }
+
+ void expect_cache_connect(MockParentImageCache& mparent_image_cache, int ret_val) {
+ auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), connect());
+
+ expect.WillOnce((Invoke([ret_val]() {
+ return ret_val;
+ })));
+ }
+
+ void expect_cache_async_connect(MockParentImageCache& mparent_image_cache, int ret_val,
+ Context* on_finish) {
+ auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), connect(_));
+
+ expect.WillOnce(WithArg<0>(Invoke([on_finish, ret_val](Context* ctx) {
+ ctx->complete(ret_val);
+ on_finish->complete(ret_val);
+ })));
+ }
+
+ void expect_cache_lookup_object(MockParentImageCache& mparent_image_cache,
+ const std::string &cache_path) {
+ EXPECT_CALL(*(mparent_image_cache.get_cache_client()),
+ lookup_object(_, _, _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([cache_path](CacheGenContextURef on_finish) {
+ ObjectCacheReadReplyData ack(RBDSC_READ_REPLY, 0, cache_path);
+ on_finish.release()->complete(&ack);
+ })));
+ }
+
+ void expect_read_parent(MockPluginApi &mock_plugin_api, uint64_t object_no,
+ io::ReadExtents* extents, librados::snap_t snap_id,
+ int r) {
+ EXPECT_CALL(mock_plugin_api,
+ read_parent(_, object_no, extents, snap_id, _, _))
+ .WillOnce(WithArg<5>(CompleteContext(r, static_cast<asio::ContextWQ*>(nullptr))));
+ }
+
+ void expect_cache_close(MockParentImageCache& mparent_image_cache, int ret_val) {
+ auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), close());
+
+ expect.WillOnce((Invoke([]() {
+ })));
+ }
+
+ void expect_cache_stop(MockParentImageCache& mparent_image_cache, int ret_val) {
+ auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), stop());
+
+ expect.WillOnce((Invoke([]() {
+ })));
+ }
+
+ void expect_cache_register(MockParentImageCache& mparent_image_cache, Context* mock_handle_register, int ret_val) {
+ auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), register_client(_));
+
+ expect.WillOnce(WithArg<0>(Invoke([mock_handle_register, ret_val](Context* ctx) {
+ if(ret_val == 0) {
+ mock_handle_register->complete(true);
+ } else {
+ mock_handle_register->complete(false);
+ }
+ ctx->complete(true);
+ return ret_val;
+ })));
+ }
+
+ void expect_io_object_dispatcher_register_state(MockParentImageCache& mparent_image_cache,
+ int ret_val) {
+ auto& expect = EXPECT_CALL((*(mparent_image_cache.get_image_ctx()->io_object_dispatcher)),
+ register_dispatch(_));
+
+ expect.WillOnce(WithArg<0>(Invoke([&mparent_image_cache]
+ (io::ObjectDispatchInterface* object_dispatch) {
+ ASSERT_EQ(object_dispatch, &mparent_image_cache);
+ })));
+ }
+};
+
+TEST_F(TestMockParentCacheObjectDispatch, test_initialization_success) {
+ librbd::ImageCtx* ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ MockParentImageCacheImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.child = &mock_image_ctx;
+
+ MockPluginApi mock_plugin_api;
+ auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx,
+ mock_plugin_api);
+
+ expect_cache_run(*mock_parent_image_cache, 0);
+ C_SaferCond cond;
+ Context* handle_connect = new LambdaContext([&cond](int ret) {
+ ASSERT_EQ(ret, 0);
+ cond.complete(0);
+ });
+ expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect);
+ Context* ctx = new LambdaContext([](bool reg) {
+ ASSERT_EQ(reg, true);
+ });
+ expect_cache_register(*mock_parent_image_cache, ctx, 0);
+ expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0);
+ expect_cache_close(*mock_parent_image_cache, 0);
+ expect_cache_stop(*mock_parent_image_cache, 0);
+
+ mock_parent_image_cache->init();
+ cond.wait();
+
+ ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(),
+ io::OBJECT_DISPATCH_LAYER_PARENT_CACHE);
+ expect_cache_session_state(*mock_parent_image_cache, true);
+ ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true);
+
+ mock_parent_image_cache->get_cache_client()->close();
+ mock_parent_image_cache->get_cache_client()->stop();
+
+ delete mock_parent_image_cache;
+}
+
+TEST_F(TestMockParentCacheObjectDispatch, test_initialization_fail_at_connect) {
+ librbd::ImageCtx* ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ MockParentImageCacheImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.child = &mock_image_ctx;
+
+ MockPluginApi mock_plugin_api;
+ auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx,
+ mock_plugin_api);
+
+ expect_cache_run(*mock_parent_image_cache, 0);
+ C_SaferCond cond;
+ Context* handle_connect = new LambdaContext([&cond](int ret) {
+ ASSERT_EQ(ret, -1);
+ cond.complete(0);
+ });
+ expect_cache_async_connect(*mock_parent_image_cache, -1, handle_connect);
+ expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0);
+ expect_cache_session_state(*mock_parent_image_cache, false);
+ expect_cache_close(*mock_parent_image_cache, 0);
+ expect_cache_stop(*mock_parent_image_cache, 0);
+
+ mock_parent_image_cache->init();
+
+ // initialization fails.
+ ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(),
+ io::OBJECT_DISPATCH_LAYER_PARENT_CACHE);
+ ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), false);
+
+ mock_parent_image_cache->get_cache_client()->close();
+ mock_parent_image_cache->get_cache_client()->stop();
+
+ delete mock_parent_image_cache;
+
+}
+
+TEST_F(TestMockParentCacheObjectDispatch, test_initialization_fail_at_register) {
+ librbd::ImageCtx* ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ MockParentImageCacheImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.child = &mock_image_ctx;
+
+ MockPluginApi mock_plugin_api;
+ auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx,
+ mock_plugin_api);
+
+ expect_cache_run(*mock_parent_image_cache, 0);
+ C_SaferCond cond;
+ Context* handle_connect = new LambdaContext([&cond](int ret) {
+ ASSERT_EQ(ret, 0);
+ cond.complete(0);
+ });
+ expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect);
+ Context* ctx = new LambdaContext([](bool reg) {
+ ASSERT_EQ(reg, false);
+ });
+ expect_cache_register(*mock_parent_image_cache, ctx, -1);
+ expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0);
+ expect_cache_close(*mock_parent_image_cache, 0);
+ expect_cache_stop(*mock_parent_image_cache, 0);
+
+ mock_parent_image_cache->init();
+ cond.wait();
+
+ ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(),
+ io::OBJECT_DISPATCH_LAYER_PARENT_CACHE);
+ expect_cache_session_state(*mock_parent_image_cache, true);
+ ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true);
+
+ mock_parent_image_cache->get_cache_client()->close();
+ mock_parent_image_cache->get_cache_client()->stop();
+
+ delete mock_parent_image_cache;
+}
+
+TEST_F(TestMockParentCacheObjectDispatch, test_disble_interface) {
+ librbd::ImageCtx* ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ MockParentImageCacheImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.child = &mock_image_ctx;
+
+ MockPluginApi mock_plugin_api;
+ auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx,
+ mock_plugin_api);
+
+ std::string temp_oid("12345");
+ ceph::bufferlist temp_bl;
+ IOContext io_context = mock_image_ctx.get_data_io_context();
+ io::DispatchResult* temp_dispatch_result = nullptr;
+ io::Extents temp_buffer_extents;
+ int* temp_op_flags = nullptr;
+ uint64_t* temp_journal_tid = nullptr;
+ Context** temp_on_finish = nullptr;
+ Context* temp_on_dispatched = nullptr;
+ ZTracer::Trace* temp_trace = nullptr;
+ io::LightweightBufferExtents buffer_extents;
+
+ ASSERT_EQ(mock_parent_image_cache->discard(0, 0, 0, io_context, 0,
+ *temp_trace, temp_op_flags, temp_journal_tid, temp_dispatch_result,
+ temp_on_finish, temp_on_dispatched), false);
+ ASSERT_EQ(mock_parent_image_cache->write(0, 0, std::move(temp_bl),
+ io_context, 0, 0, std::nullopt, *temp_trace, temp_op_flags,
+ temp_journal_tid, temp_dispatch_result, temp_on_finish,
+ temp_on_dispatched), false);
+ ASSERT_EQ(mock_parent_image_cache->write_same(0, 0, 0, std::move(buffer_extents),
+ std::move(temp_bl), io_context, 0, *temp_trace, temp_op_flags,
+ temp_journal_tid, temp_dispatch_result, temp_on_finish, temp_on_dispatched), false );
+ ASSERT_EQ(mock_parent_image_cache->compare_and_write(0, 0, std::move(temp_bl), std::move(temp_bl),
+ io_context, 0, *temp_trace, temp_journal_tid, temp_op_flags,
+ temp_journal_tid, temp_dispatch_result, temp_on_finish,
+ temp_on_dispatched), false);
+ ASSERT_EQ(mock_parent_image_cache->flush(io::FLUSH_SOURCE_USER, *temp_trace, temp_journal_tid,
+ temp_dispatch_result, temp_on_finish, temp_on_dispatched), false);
+ ASSERT_EQ(mock_parent_image_cache->invalidate_cache(nullptr), false);
+ ASSERT_EQ(mock_parent_image_cache->reset_existence_cache(nullptr), false);
+
+ delete mock_parent_image_cache;
+
+}
+
+TEST_F(TestMockParentCacheObjectDispatch, test_read) {
+ librbd::ImageCtx* ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ MockParentImageCacheImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.child = &mock_image_ctx;
+
+ MockPluginApi mock_plugin_api;
+ auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx,
+ mock_plugin_api);
+
+ expect_cache_run(*mock_parent_image_cache, 0);
+ C_SaferCond conn_cond;
+ Context* handle_connect = new LambdaContext([&conn_cond](int ret) {
+ ASSERT_EQ(ret, 0);
+ conn_cond.complete(0);
+ });
+ expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect);
+ Context* ctx = new LambdaContext([](bool reg) {
+ ASSERT_EQ(reg, true);
+ });
+ expect_cache_register(*mock_parent_image_cache, ctx, 0);
+ expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0);
+ expect_cache_close(*mock_parent_image_cache, 0);
+ expect_cache_stop(*mock_parent_image_cache, 0);
+
+ mock_parent_image_cache->init();
+ conn_cond.wait();
+
+ ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(),
+ io::OBJECT_DISPATCH_LAYER_PARENT_CACHE);
+ expect_cache_session_state(*mock_parent_image_cache, true);
+ ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true);
+
+ auto& expect = EXPECT_CALL(*(mock_parent_image_cache->get_cache_client()), is_session_work());
+ expect.WillOnce(Return(true));
+
+ expect_cache_lookup_object(*mock_parent_image_cache, "/dev/null");
+
+ C_SaferCond on_dispatched;
+ io::DispatchResult dispatch_result;
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ mock_parent_image_cache->read(
+ 0, &extents, mock_image_ctx.get_data_io_context(), 0, 0, {}, nullptr,
+ nullptr, &dispatch_result, nullptr, &on_dispatched);
+ ASSERT_EQ(0, on_dispatched.wait());
+
+ mock_parent_image_cache->get_cache_client()->close();
+ mock_parent_image_cache->get_cache_client()->stop();
+ delete mock_parent_image_cache;
+}
+
+TEST_F(TestMockParentCacheObjectDispatch, test_read_dne) {
+ librbd::ImageCtx* ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ MockParentImageCacheImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.child = &mock_image_ctx;
+
+ MockPluginApi mock_plugin_api;
+ auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx,
+ mock_plugin_api);
+
+ expect_cache_run(*mock_parent_image_cache, 0);
+ C_SaferCond conn_cond;
+ Context* handle_connect = new LambdaContext([&conn_cond](int ret) {
+ ASSERT_EQ(ret, 0);
+ conn_cond.complete(0);
+ });
+ expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect);
+ Context* ctx = new LambdaContext([](bool reg) {
+ ASSERT_EQ(reg, true);
+ });
+ expect_cache_register(*mock_parent_image_cache, ctx, 0);
+ expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0);
+ expect_cache_close(*mock_parent_image_cache, 0);
+ expect_cache_stop(*mock_parent_image_cache, 0);
+
+ mock_parent_image_cache->init();
+ conn_cond.wait();
+
+ ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(),
+ io::OBJECT_DISPATCH_LAYER_PARENT_CACHE);
+ expect_cache_session_state(*mock_parent_image_cache, true);
+ ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(),
+ true);
+
+ EXPECT_CALL(*(mock_parent_image_cache->get_cache_client()), is_session_work())
+ .WillOnce(Return(true));
+
+ expect_cache_lookup_object(*mock_parent_image_cache, "");
+
+ io::ReadExtents extents = {{0, 4096}};
+ expect_read_parent(mock_plugin_api, 0, &extents, CEPH_NOSNAP, 0);
+
+ C_SaferCond on_dispatched;
+ io::DispatchResult dispatch_result;
+ mock_parent_image_cache->read(
+ 0, &extents, mock_image_ctx.get_data_io_context(), 0, 0, {}, nullptr,
+ nullptr, &dispatch_result, nullptr, &on_dispatched);
+ ASSERT_EQ(0, on_dispatched.wait());
+
+ mock_parent_image_cache->get_cache_client()->close();
+ mock_parent_image_cache->get_cache_client()->stop();
+ delete mock_parent_image_cache;
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc b/src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc
new file mode 100644
index 000000000..abfd185e3
--- /dev/null
+++ b/src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc
@@ -0,0 +1,703 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/cache/WriteAroundObjectDispatch.h"
+#include "librbd/io/ObjectDispatchSpec.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+struct MockContext : public C_SaferCond {
+ MOCK_METHOD1(complete, void(int));
+ MOCK_METHOD1(finish, void(int));
+
+ void do_complete(int r) {
+ C_SaferCond::complete(r);
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "librbd/cache/WriteAroundObjectDispatch.cc"
+
+namespace librbd {
+namespace cache {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+struct TestMockCacheWriteAroundObjectDispatch : public TestMockFixture {
+ typedef WriteAroundObjectDispatch<librbd::MockTestImageCtx> MockWriteAroundObjectDispatch;
+
+ void expect_op_work_queue(MockTestImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.op_work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([](Context* ctx, int r) {
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_context_complete(MockContext& mock_context, int r) {
+ EXPECT_CALL(mock_context, complete(r))
+ .WillOnce(Invoke([&mock_context](int r) {
+ mock_context.do_complete(r);
+ }));
+ }
+};
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, WriteThrough) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 0, false);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx;
+ MockContext dispatch_ctx;
+ Context* finish_ctx_ptr = &finish_ctx;
+ ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr,
+ &dispatch_ctx));
+ ASSERT_EQ(finish_ctx_ptr, &finish_ctx);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, WriteThroughUntilFlushed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4096, true);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx;
+ MockContext dispatch_ctx;
+ Context* finish_ctx_ptr = &finish_ctx;
+ ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr,
+ &dispatch_ctx));
+ ASSERT_EQ(finish_ctx_ptr, &finish_ctx);
+
+ ASSERT_FALSE(object_dispatch.flush(io::FLUSH_SOURCE_USER, {}, nullptr,
+ &dispatch_result, &finish_ctx_ptr,
+ &dispatch_ctx));
+
+ expect_context_complete(dispatch_ctx, 0);
+ expect_context_complete(finish_ctx, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr,
+ &dispatch_ctx));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr, &finish_ctx);
+ ASSERT_EQ(0, dispatch_ctx.wait());
+ ASSERT_EQ(0, finish_ctx.wait());
+ finish_ctx_ptr->complete(0);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, DispatchIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4096, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx;
+ MockContext dispatch_ctx;
+ Context* finish_ctx_ptr = &finish_ctx;
+
+ expect_context_complete(dispatch_ctx, 0);
+ expect_context_complete(finish_ctx, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr,
+ &dispatch_ctx));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr, &finish_ctx);
+
+ ASSERT_EQ(0, dispatch_ctx.wait());
+ ASSERT_EQ(0, finish_ctx.wait());
+ finish_ctx_ptr->complete(0);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 16384, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt,{}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+
+ expect_context_complete(dispatch_ctx2, 0);
+ expect_context_complete(finish_ctx2, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr2, &finish_ctx2);
+
+ MockContext finish_ctx3;
+ MockContext dispatch_ctx3;
+ Context* finish_ctx_ptr3 = &finish_ctx3;
+
+ ASSERT_TRUE(object_dispatch.write(0, 1024, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr3,
+ &dispatch_ctx3));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr3, &finish_ctx3);
+
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+ ASSERT_EQ(0, dispatch_ctx2.wait());
+ ASSERT_EQ(0, finish_ctx1.wait());
+ ASSERT_EQ(0, finish_ctx2.wait());
+ finish_ctx_ptr2->complete(0);
+
+ expect_context_complete(dispatch_ctx3, 0);
+ expect_context_complete(finish_ctx3, 0);
+ finish_ctx_ptr1->complete(0);
+
+ ASSERT_EQ(0, dispatch_ctx3.wait());
+ finish_ctx_ptr3->complete(0);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, QueuedIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4095, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+
+ ASSERT_TRUE(object_dispatch.write(0, 8192, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr2, &finish_ctx2);
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+
+ expect_context_complete(dispatch_ctx2, 0);
+ expect_context_complete(finish_ctx2, 0);
+ finish_ctx_ptr1->complete(0);
+
+ ASSERT_EQ(0, finish_ctx1.wait());
+ ASSERT_EQ(0, dispatch_ctx2.wait());
+ ASSERT_EQ(0, finish_ctx2.wait());
+ finish_ctx_ptr2->complete(0);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedAndQueuedIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 8196, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+
+ expect_context_complete(dispatch_ctx2, 0);
+ expect_context_complete(finish_ctx2, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr2, &finish_ctx2);
+
+ MockContext finish_ctx3;
+ MockContext dispatch_ctx3;
+ Context* finish_ctx_ptr3 = &finish_ctx3;
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr3,
+ &dispatch_ctx3));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr3, &finish_ctx3);
+
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+ ASSERT_EQ(0, dispatch_ctx2.wait());
+ ASSERT_EQ(0, finish_ctx1.wait());
+ ASSERT_EQ(0, finish_ctx2.wait());
+ finish_ctx_ptr2->complete(0);
+
+ expect_context_complete(dispatch_ctx3, 0);
+ expect_context_complete(finish_ctx3, 0);
+ finish_ctx_ptr1->complete(0);
+
+ ASSERT_EQ(0, dispatch_ctx3.wait());
+ ASSERT_EQ(0, finish_ctx3.wait());
+ finish_ctx_ptr3->complete(0);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, Flush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4096, true);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx;
+ MockContext dispatch_ctx;
+ Context* finish_ctx_ptr = &finish_ctx;
+ ASSERT_FALSE(object_dispatch.flush(io::FLUSH_SOURCE_USER, {}, nullptr,
+ &dispatch_result, &finish_ctx_ptr,
+ &dispatch_ctx));
+ ASSERT_EQ(finish_ctx_ptr, &finish_ctx);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, FlushQueuedOnInFlightIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4096, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+ ASSERT_FALSE(object_dispatch.flush(io::FLUSH_SOURCE_USER, {}, nullptr,
+ &dispatch_result, &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr2, &finish_ctx2);
+
+ expect_context_complete(finish_ctx2, 0);
+ finish_ctx_ptr1->complete(0);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ finish_ctx_ptr2->complete(0);
+ ASSERT_EQ(0, finish_ctx2.wait());
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, FlushQueuedOnQueuedIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4096, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+
+ ASSERT_TRUE(object_dispatch.write(0, 8192, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr2, &finish_ctx2);
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+
+ MockContext finish_ctx3;
+ MockContext dispatch_ctx3;
+ Context* finish_ctx_ptr3 = &finish_ctx3;
+ ASSERT_TRUE(object_dispatch.flush(io::FLUSH_SOURCE_USER, {}, nullptr,
+ &dispatch_result, &finish_ctx_ptr3,
+ &dispatch_ctx3));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr3, &finish_ctx3);
+
+ expect_context_complete(dispatch_ctx2, 0);
+ expect_context_complete(finish_ctx2, 0);
+ expect_context_complete(dispatch_ctx3, 0);
+ finish_ctx_ptr1->complete(0);
+
+ ASSERT_EQ(0, finish_ctx1.wait());
+ ASSERT_EQ(0, dispatch_ctx2.wait());
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ expect_context_complete(finish_ctx3, 0);
+ finish_ctx_ptr2->complete(0);
+
+ finish_ctx_ptr3->complete(0);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, FlushError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4096, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+ ASSERT_FALSE(object_dispatch.flush(io::FLUSH_SOURCE_USER, {}, nullptr,
+ &dispatch_result, &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr2, &finish_ctx2);
+
+ expect_context_complete(finish_ctx2, -EPERM);
+ finish_ctx_ptr1->complete(-EPERM);
+ finish_ctx_ptr2->complete(0);
+ ASSERT_EQ(-EPERM, finish_ctx2.wait());
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, UnoptimizedIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 16384, false);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx;
+ MockContext dispatch_ctx;
+ Context* finish_ctx_ptr = &finish_ctx;
+
+ ASSERT_FALSE(object_dispatch.compare_and_write(0, 0, std::move(data),
+ std::move(data), {}, 0, {},
+ nullptr, nullptr, nullptr,
+ &dispatch_result,
+ &finish_ctx_ptr,
+ &dispatch_ctx));
+ ASSERT_EQ(finish_ctx_ptr, &finish_ctx);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, UnoptimizedIOInFlightIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 16384, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+ ASSERT_TRUE(object_dispatch.compare_and_write(0, 0, std::move(data),
+ std::move(data), {}, 0, {},
+ nullptr, nullptr, nullptr,
+ &dispatch_result,
+ &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_EQ(finish_ctx_ptr2, &finish_ctx2);
+
+ expect_context_complete(dispatch_ctx2, 0);
+ finish_ctx_ptr1->complete(0);
+ ASSERT_EQ(0, dispatch_ctx2.wait());
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, UnoptimizedIOBlockedIO) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 4096, false);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx1;
+ MockContext dispatch_ctx1;
+ Context* finish_ctx_ptr1 = &finish_ctx1;
+
+ expect_context_complete(dispatch_ctx1, 0);
+ expect_context_complete(finish_ctx1, 0);
+
+ ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr1,
+ &dispatch_ctx1));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr1, &finish_ctx1);
+ ASSERT_EQ(0, dispatch_ctx1.wait());
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContext finish_ctx2;
+ MockContext dispatch_ctx2;
+ Context* finish_ctx_ptr2 = &finish_ctx2;
+ ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr2,
+ &dispatch_ctx2));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_NE(finish_ctx_ptr2, &finish_ctx2);
+
+ MockContext finish_ctx3;
+ MockContext dispatch_ctx3;
+ Context* finish_ctx_ptr3 = &finish_ctx3;
+ ASSERT_TRUE(object_dispatch.compare_and_write(0, 0, std::move(data),
+ std::move(data), {}, 0, {},
+ nullptr, nullptr, nullptr,
+ &dispatch_result,
+ &finish_ctx_ptr3,
+ &dispatch_ctx3));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_EQ(finish_ctx_ptr3, &finish_ctx3);
+
+ expect_context_complete(dispatch_ctx3, 0);
+ expect_context_complete(dispatch_ctx2, 0);
+ expect_context_complete(finish_ctx2, 0);
+ finish_ctx_ptr1->complete(0);
+ ASSERT_EQ(0, dispatch_ctx3.wait());
+ ASSERT_EQ(0, dispatch_ctx2.wait());
+ ASSERT_EQ(0, finish_ctx2.wait());
+ finish_ctx_ptr2->complete(0);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, WriteFUA) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 16384, false);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(4096, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx;
+ MockContext dispatch_ctx;
+ Context* finish_ctx_ptr = &finish_ctx;
+ ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {},
+ LIBRADOS_OP_FLAG_FADVISE_FUA, 0,
+ std::nullopt, {}, nullptr, nullptr,
+ &dispatch_result, &finish_ctx_ptr,
+ &dispatch_ctx));
+ ASSERT_EQ(finish_ctx_ptr, &finish_ctx);
+}
+
+TEST_F(TestMockCacheWriteAroundObjectDispatch, WriteSameFUA) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockWriteAroundObjectDispatch object_dispatch(&mock_image_ctx, 16384, false);
+
+ InSequence seq;
+
+ bufferlist data;
+ data.append(std::string(512, '1'));
+
+ io::DispatchResult dispatch_result;
+ MockContext finish_ctx;
+ MockContext dispatch_ctx;
+ Context* finish_ctx_ptr = &finish_ctx;
+ ASSERT_FALSE(object_dispatch.write_same(0, 0, 8192, {{0, 8192}},
+ std::move(data), {},
+ LIBRADOS_OP_FLAG_FADVISE_FUA, {},
+ nullptr, nullptr, &dispatch_result,
+ &finish_ctx_ptr, &dispatch_ctx));
+ ASSERT_EQ(finish_ctx_ptr, &finish_ctx);
+}
+
+} // namespace cache
+} // namespace librbd
diff --git a/src/test/librbd/crypto/luks/test_mock_FlattenRequest.cc b/src/test/librbd/crypto/luks/test_mock_FlattenRequest.cc
new file mode 100644
index 000000000..bc615bcf7
--- /dev/null
+++ b/src/test/librbd/crypto/luks/test_mock_FlattenRequest.cc
@@ -0,0 +1,266 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/crypto/MockCryptoInterface.h"
+#include "test/librbd/mock/crypto/MockEncryptionFormat.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/crypto/luks/FlattenRequest.cc"
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+
+struct TestMockCryptoLuksFlattenRequest : public TestMockFixture {
+ typedef FlattenRequest<MockTestImageCtx> MockFlattenRequest;
+
+ const size_t OBJECT_SIZE = 4 * 1024 * 1024;
+ const uint64_t DATA_OFFSET = MockCryptoInterface::DATA_OFFSET;
+ const char* passphrase_cstr = "password";
+ std::string passphrase = passphrase_cstr;
+
+ MockTestImageCtx* mock_image_ctx;
+ MockFlattenRequest* mock_flatten_request;
+ MockEncryptionFormat* mock_encryption_format;
+ MockCryptoInterface mock_crypto;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ Context* image_read_request;
+ io::AioCompletion* aio_comp;
+ ceph::bufferlist header_bl;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockTestImageCtx(*ictx);
+ mock_encryption_format = new MockEncryptionFormat();
+ mock_image_ctx->encryption_format.reset(mock_encryption_format);
+ mock_flatten_request = MockFlattenRequest::create(
+ mock_image_ctx, on_finish);
+ }
+
+ void TearDown() override {
+ delete mock_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void generate_header(const char* type, const char* alg, size_t key_size,
+ const char* cipher_mode, uint32_t sector_size,
+ bool magic_switched) {
+ Header header(mock_image_ctx->cct);
+
+ ASSERT_EQ(0, header.init());
+ ASSERT_EQ(0, header.format(type, alg, nullptr, key_size, cipher_mode,
+ sector_size, OBJECT_SIZE, true));
+ ASSERT_EQ(0, header.add_keyslot(passphrase_cstr, strlen(passphrase_cstr)));
+ ASSERT_LT(0, header.read(&header_bl));
+ if (magic_switched) {
+ ASSERT_EQ(0, Magic::replace_magic(mock_image_ctx->cct, header_bl));
+ }
+ }
+
+ void expect_get_crypto() {
+ EXPECT_CALL(*mock_encryption_format, get_crypto()).WillOnce(
+ Return(&mock_crypto));
+ }
+
+ void expect_image_read(uint64_t offset, uint64_t length) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_))
+ .WillOnce(Invoke([this, offset,
+ length](io::ImageDispatchSpec* spec) {
+ auto* read = boost::get<io::ImageDispatchSpec::Read>(
+ &spec->request);
+ ASSERT_TRUE(read != nullptr);
+
+ ASSERT_EQ(1, spec->image_extents.size());
+ ASSERT_EQ(offset, spec->image_extents[0].first);
+ ASSERT_EQ(length, spec->image_extents[0].second);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ auto aio_comp = spec->aio_comp;
+ aio_comp->set_request_count(1);
+ aio_comp->read_result = std::move(read->read_result);
+ aio_comp->read_result.set_image_extents(spec->image_extents);
+ auto ctx = new io::ReadResult::C_ImageReadRequest(
+ aio_comp, 0, spec->image_extents);
+ if (header_bl.length() < offset + length) {
+ header_bl.append_zero(offset + length - header_bl.length());
+ }
+ ctx->bl.substr_of(header_bl, offset, length);
+ image_read_request = ctx;
+ }));
+ }
+
+ void expect_image_write() {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_))
+ .WillOnce(Invoke([this](io::ImageDispatchSpec* spec) {
+ auto* write = boost::get<io::ImageDispatchSpec::Write>(
+ &spec->request);
+ ASSERT_TRUE(write != nullptr);
+
+ ASSERT_EQ(1, spec->image_extents.size());
+ ASSERT_EQ(0, spec->image_extents[0].first);
+ ASSERT_GT(spec->image_extents[0].second, 0);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ aio_comp = spec->aio_comp;
+
+ // patch header_bl with write
+ bufferlist bl;
+ bl.substr_of(header_bl, write->bl.length(),
+ header_bl.length() - write->bl.length());
+ header_bl = write->bl;
+ header_bl.claim_append(bl);
+ }));
+ }
+
+ void expect_image_flush(int r) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_)).WillOnce(
+ Invoke([r](io::ImageDispatchSpec* spec) {
+ ASSERT_TRUE(boost::get<io::ImageDispatchSpec::Flush>(
+ &spec->request) != nullptr);
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ spec->aio_comp->set_request_count(1);
+ spec->aio_comp->add_request();
+ spec->aio_comp->complete_request(r);
+ }));
+ }
+
+ void complete_aio(int r) {
+ if (r < 0) {
+ aio_comp->fail(r);
+ } else {
+ aio_comp->set_request_count(1);
+ aio_comp->add_request();
+ aio_comp->complete_request(r);
+ }
+ }
+
+ void verify_header(const char* expected_format) {
+ Header header(mock_image_ctx->cct);
+
+ ASSERT_EQ(0, header.init());
+ ASSERT_EQ(0, header.write(header_bl));
+ ASSERT_EQ(0, header.load(expected_format));
+ }
+};
+
+TEST_F(TestMockCryptoLuksFlattenRequest, LUKS1) {
+ generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512, true);
+ expect_get_crypto();
+ expect_image_read(0, DATA_OFFSET);
+ mock_flatten_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_write();
+ image_read_request->complete(DATA_OFFSET);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(0);
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS1));
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLuksFlattenRequest, LUKS2) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096, true);
+ expect_get_crypto();
+ expect_image_read(0, DATA_OFFSET);
+ mock_flatten_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_write();
+ image_read_request->complete(DATA_OFFSET);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(0);
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2));
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLuksFlattenRequest, FailedRead) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096, true);
+ expect_get_crypto();
+ expect_image_read(0, DATA_OFFSET);
+ mock_flatten_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ image_read_request->complete(-EIO);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLuksFlattenRequest, AlreadyFlattened) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096, false);
+ expect_get_crypto();
+ expect_image_read(0, DATA_OFFSET);
+ mock_flatten_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_write();
+ image_read_request->complete(DATA_OFFSET);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(0);
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2));
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLuksFlattenRequest, FailedWrite) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096, true);
+ expect_get_crypto();
+ expect_image_read(0, DATA_OFFSET);
+ mock_flatten_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_write();
+ image_read_request->complete(DATA_OFFSET);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(-EIO);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLuksFlattenRequest, FailedFlush) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096, true);
+ expect_get_crypto();
+ expect_image_read(0, DATA_OFFSET);
+ mock_flatten_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_write();
+ image_read_request->complete(DATA_OFFSET);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(-EIO);
+ complete_aio(0);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/luks/test_mock_FormatRequest.cc b/src/test/librbd/crypto/luks/test_mock_FormatRequest.cc
new file mode 100644
index 000000000..86026b456
--- /dev/null
+++ b/src/test/librbd/crypto/luks/test_mock_FormatRequest.cc
@@ -0,0 +1,230 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace librbd {
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/crypto/luks/FormatRequest.cc"
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+
+struct TestMockCryptoLuksFormatRequest : public TestMockFixture {
+ typedef FormatRequest<librbd::MockImageCtx> MockFormatRequest;
+
+ const size_t OBJECT_SIZE = 4 * 1024 * 1024;
+ const size_t IMAGE_SIZE = 1024 * 1024 * 1024;
+ const char* passphrase_cstr = "password";
+ std::string passphrase = passphrase_cstr;
+
+ MockImageCtx* mock_image_ctx;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ io::AioCompletion* aio_comp;
+ ceph::bufferlist header_bl;
+ std::unique_ptr<CryptoInterface> crypto;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockImageCtx(*ictx);
+ }
+
+ void TearDown() override {
+ delete mock_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_get_stripe_period() {
+ EXPECT_CALL(*mock_image_ctx, get_stripe_period()).WillOnce(Return(
+ OBJECT_SIZE));
+ }
+
+ void expect_get_image_size(uint64_t image_size) {
+ EXPECT_CALL(*mock_image_ctx, get_image_size(CEPH_NOSNAP)).WillOnce(Return(
+ image_size));
+ }
+
+ void expect_image_write() {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_))
+ .WillOnce(Invoke([this](io::ImageDispatchSpec* spec) {
+ auto* write = boost::get<io::ImageDispatchSpec::Write>(
+ &spec->request);
+ ASSERT_TRUE(write != nullptr);
+
+ ASSERT_EQ(1, spec->image_extents.size());
+ ASSERT_EQ(0, spec->image_extents[0].first);
+ ASSERT_GT(spec->image_extents[0].second, 0);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ aio_comp = spec->aio_comp;
+ header_bl = write->bl;
+ }));
+ }
+
+ void complete_aio(int r) {
+ if (r < 0) {
+ aio_comp->fail(r);
+ } else {
+ aio_comp->set_request_count(1);
+ aio_comp->add_request();
+ aio_comp->complete_request(r);
+ }
+ }
+
+ void verify_header(const char* expected_format, size_t expected_key_length,
+ uint64_t expected_sector_size, bool magic_switched) {
+ Header header(mock_image_ctx->cct);
+
+ ASSERT_EQ(0, header.init());
+
+ if (magic_switched) {
+ Header non_switched_header(mock_image_ctx->cct);
+ ASSERT_EQ(0, non_switched_header.init());
+ ASSERT_EQ(0, non_switched_header.write(header_bl));
+ ASSERT_EQ(-EINVAL, non_switched_header.load(expected_format));
+ ASSERT_EQ(0, Magic::replace_magic(mock_image_ctx->cct, header_bl));
+ }
+ ASSERT_EQ(0, header.write(header_bl));
+ ASSERT_EQ(0, header.load(expected_format));
+
+ ASSERT_EQ(expected_sector_size, header.get_sector_size());
+ ASSERT_EQ(0, header.get_data_offset() % OBJECT_SIZE);
+
+ char volume_key[64];
+ size_t volume_key_size = sizeof(volume_key);
+ ASSERT_EQ(0, header.read_volume_key(
+ passphrase_cstr, strlen(passphrase_cstr),
+ reinterpret_cast<char*>(volume_key), &volume_key_size));
+
+ ASSERT_EQ(expected_key_length, crypto->get_key_length());
+ ASSERT_EQ(0, std::memcmp(
+ volume_key, crypto->get_key(), expected_key_length));
+ ASSERT_EQ(expected_sector_size, crypto->get_block_size());
+ ASSERT_EQ(header.get_data_offset(), crypto->get_data_offset());
+ }
+};
+
+TEST_F(TestMockCryptoLuksFormatRequest, LUKS1) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1,
+ RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), &crypto,
+ on_finish, true);
+ expect_get_stripe_period();
+ expect_get_image_size(IMAGE_SIZE);
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS1, 32, 512, false));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, AES128) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
+ RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), &crypto,
+ on_finish, true);
+ expect_get_stripe_period();
+ expect_get_image_size(IMAGE_SIZE);
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2, 32, 4096, false));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, AES256) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
+ RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+ on_finish, true);
+ expect_get_stripe_period();
+ expect_get_image_size(IMAGE_SIZE);
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2, 64, 4096, false));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, LUKS1OnCloned) {
+ mock_image_ctx->parent = mock_image_ctx;
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1,
+ RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+ on_finish, true);
+ expect_get_stripe_period();
+ expect_get_image_size(IMAGE_SIZE);
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS1, 64, 512, true));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, LUKS2OnCloned) {
+ mock_image_ctx->parent = mock_image_ctx;
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
+ RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+ on_finish, true);
+ expect_get_stripe_period();
+ expect_get_image_size(IMAGE_SIZE);
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2, 64, 4096, true));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, ImageTooSmall) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
+ RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+ on_finish, true);
+ expect_get_stripe_period();
+ expect_get_image_size(1024*1024);
+ mock_format_request->send();
+ ASSERT_EQ(-ENOSPC, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, WriteFail) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
+ RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+ on_finish, true);
+ expect_get_stripe_period();
+ expect_get_image_size(IMAGE_SIZE);
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(-123);
+ ASSERT_EQ(-123, finished_cond.wait());
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc b/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc
new file mode 100644
index 000000000..5fb566a07
--- /dev/null
+++ b/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc
@@ -0,0 +1,333 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace librbd {
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/crypto/luks/LoadRequest.cc"
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+
+struct TestMockCryptoLuksLoadRequest : public TestMockFixture {
+ typedef LoadRequest<librbd::MockImageCtx> MockLoadRequest;
+
+ const size_t OBJECT_SIZE = 4 * 1024 * 1024;
+ const char* passphrase_cstr = "password";
+ std::string passphrase = passphrase_cstr;
+
+ MockImageCtx* mock_image_ctx;
+ std::unique_ptr<CryptoInterface> crypto;
+ MockLoadRequest* mock_load_request;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ Context* image_read_request;
+ ceph::bufferlist header_bl;
+ uint64_t data_offset;
+ std::string detected_format_name;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockImageCtx(*ictx);
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, std::move(passphrase),
+ &crypto, &detected_format_name, on_finish);
+ detected_format_name = "";
+ }
+
+ void TearDown() override {
+ delete mock_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ // returns data offset in bytes
+ void generate_header(const char* type, const char* alg, size_t key_size,
+ const char* cipher_mode, uint32_t sector_size,
+ bool magic_switched) {
+ Header header(mock_image_ctx->cct);
+
+ ASSERT_EQ(0, header.init());
+ ASSERT_EQ(0, header.format(type, alg, nullptr, key_size, cipher_mode,
+ sector_size, OBJECT_SIZE, true));
+ ASSERT_EQ(0, header.add_keyslot(passphrase_cstr, strlen(passphrase_cstr)));
+ ASSERT_LT(0, header.read(&header_bl));
+ if (magic_switched) {
+ ASSERT_EQ(0, Magic::replace_magic(mock_image_ctx->cct, header_bl));
+ }
+
+ data_offset = header.get_data_offset();
+ }
+
+ void expect_image_read(uint64_t offset, uint64_t length) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_))
+ .WillOnce(Invoke([this, offset,
+ length](io::ImageDispatchSpec* spec) {
+ auto* read = boost::get<io::ImageDispatchSpec::Read>(
+ &spec->request);
+ ASSERT_TRUE(read != nullptr);
+
+ ASSERT_EQ(1, spec->image_extents.size());
+ ASSERT_EQ(offset, spec->image_extents[0].first);
+ ASSERT_EQ(length, spec->image_extents[0].second);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ auto aio_comp = spec->aio_comp;
+ aio_comp->set_request_count(1);
+ aio_comp->read_result = std::move(read->read_result);
+ aio_comp->read_result.set_image_extents(spec->image_extents);
+ auto ctx = new io::ReadResult::C_ImageReadRequest(
+ aio_comp, 0, spec->image_extents);
+ if (header_bl.length() < offset + length) {
+ header_bl.append_zero(offset + length - header_bl.length());
+ }
+ ctx->bl.substr_of(header_bl, offset, length);
+ image_read_request = ctx;
+ }));
+ }
+
+ void expect_get_image_size(uint64_t size) {
+ EXPECT_CALL(*mock_image_ctx, get_image_size(_)).WillOnce(
+ Return(size));
+ }
+
+ void expect_get_stripe_period(uint64_t period) {
+ EXPECT_CALL(*mock_image_ctx, get_stripe_period()).WillOnce(
+ Return(period));
+ }
+};
+
+TEST_F(TestMockCryptoLuksLoadRequest, AES128) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, AES256) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, LUKS1) {
+ delete mock_load_request;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1, {passphrase_cstr}, &crypto,
+ &detected_format_name, on_finish);
+ generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS1", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, LUKS1ViaLUKS) {
+ delete mock_load_request;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS, {passphrase_cstr}, &crypto,
+ &detected_format_name, on_finish);
+ generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS1", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, UnknownFormat) {
+ header_bl.append_zero(MAXIMUM_HEADER_SIZE);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+ ASSERT_EQ(crypto.get(), nullptr);
+ ASSERT_EQ("<unknown>", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, WrongFormat) {
+ generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+
+ expect_image_read(DEFAULT_INITIAL_READ_SIZE,
+ MAXIMUM_HEADER_SIZE - DEFAULT_INITIAL_READ_SIZE);
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ image_read_request->complete(MAXIMUM_HEADER_SIZE - DEFAULT_INITIAL_READ_SIZE);
+
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+ ASSERT_EQ(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, UnsupportedAlgorithm) {
+ generate_header(CRYPT_LUKS2, "twofish", 32, "xts-plain64", 4096, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+ ASSERT_EQ(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, UnsupportedCipherMode) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "cbc-essiv:sha256", 4096, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+ ASSERT_EQ(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, BadSize) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE - 1);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+ ASSERT_EQ(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, BadStripePattern) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE * 3);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+ ASSERT_EQ(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, HeaderBiggerThanInitialRead) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false);
+ mock_load_request->set_initial_read_size(4096);
+ expect_image_read(0, 4096);
+ mock_load_request->send();
+
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ expect_image_read(4096, MAXIMUM_HEADER_SIZE - 4096);
+ image_read_request->complete(4096); // complete initial read
+
+ image_read_request->complete(MAXIMUM_HEADER_SIZE - 4096);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, LUKS1FormattedClone) {
+ mock_image_ctx->parent = mock_image_ctx;
+ delete mock_load_request;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1, {passphrase_cstr}, &crypto,
+ &detected_format_name, on_finish);
+ generate_header(CRYPT_LUKS1, "aes", 64, "xts-plain64", 512, true);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS1", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, LUKS2FormattedClone) {
+ mock_image_ctx->parent = mock_image_ctx;
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, true);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, KeyslotsBiggerThanInitialRead) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false);
+ mock_load_request->set_initial_read_size(16384);
+ expect_image_read(0, 16384);
+ mock_load_request->send();
+
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ expect_image_read(16384, data_offset - 16384);
+ image_read_request->complete(16384); // complete initial read
+
+ image_read_request->complete(data_offset - 16384);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, WrongPassphrase) {
+ delete mock_load_request;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, "wrong", &crypto,
+ &detected_format_name, on_finish);
+
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false);
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ expect_get_image_size(OBJECT_SIZE << 5);
+ expect_get_stripe_period(OBJECT_SIZE);
+ mock_load_request->send();
+
+ // crypt_volume_key_get will fail, we will retry reading more
+ expect_image_read(DEFAULT_INITIAL_READ_SIZE,
+ data_offset - DEFAULT_INITIAL_READ_SIZE);
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+
+ image_read_request->complete(data_offset - DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-EPERM, finished_cond.wait());
+ ASSERT_EQ(crypto.get(), nullptr);
+ ASSERT_EQ("LUKS2", detected_format_name);
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/openssl/test_DataCryptor.cc b/src/test/librbd/crypto/openssl/test_DataCryptor.cc
new file mode 100644
index 000000000..a3ba4c883
--- /dev/null
+++ b/src/test/librbd/crypto/openssl/test_DataCryptor.cc
@@ -0,0 +1,118 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "librbd/crypto/openssl/DataCryptor.h"
+
+namespace librbd {
+namespace crypto {
+namespace openssl {
+
+const char* TEST_CIPHER_NAME = "aes-256-xts";
+const unsigned char TEST_KEY[64] = {1};
+const unsigned char TEST_IV[16] = {2};
+const unsigned char TEST_IV_2[16] = {3};
+const unsigned char TEST_DATA[4096] = {4};
+
+struct TestCryptoOpensslDataCryptor : public TestFixture {
+ DataCryptor *cryptor;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ cryptor = new DataCryptor(reinterpret_cast<CephContext*>(m_ioctx.cct()));
+ ASSERT_EQ(0,
+ cryptor->init(TEST_CIPHER_NAME, TEST_KEY, sizeof(TEST_KEY)));
+ }
+
+ void TearDown() override {
+ delete cryptor;
+ TestFixture::TearDown();
+ }
+};
+
+TEST_F(TestCryptoOpensslDataCryptor, InvalidCipherName) {
+ EXPECT_EQ(-EINVAL, cryptor->init(nullptr, TEST_KEY, sizeof(TEST_KEY)));
+ EXPECT_EQ(-EINVAL, cryptor->init("", TEST_KEY, sizeof(TEST_KEY)));
+ EXPECT_EQ(-EINVAL, cryptor->init("Invalid", TEST_KEY, sizeof(TEST_KEY)));
+}
+
+TEST_F(TestCryptoOpensslDataCryptor, InvalidKey) {
+ EXPECT_EQ(-EINVAL, cryptor->init(TEST_CIPHER_NAME, nullptr, 0));
+ EXPECT_EQ(-EINVAL, cryptor->init(TEST_CIPHER_NAME, nullptr,
+ sizeof(TEST_KEY)));
+ EXPECT_EQ(-EINVAL, cryptor->init(TEST_CIPHER_NAME, TEST_KEY, 1));
+}
+
+TEST_F(TestCryptoOpensslDataCryptor, GetContextInvalidMode) {
+ EXPECT_EQ(nullptr, cryptor->get_context(static_cast<CipherMode>(-1)));
+}
+
+TEST_F(TestCryptoOpensslDataCryptor, ReturnNullContext) {
+ cryptor->return_context(nullptr, static_cast<CipherMode>(-1));
+}
+
+TEST_F(TestCryptoOpensslDataCryptor, ReturnContextInvalidMode) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_DEC);
+ ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+ cryptor->return_context(ctx, static_cast<CipherMode>(-1));
+}
+
+TEST_F(TestCryptoOpensslDataCryptor, EncryptDecrypt) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+ cryptor->init_context(ctx, TEST_IV, sizeof(TEST_IV));
+
+ unsigned char out[sizeof(TEST_DATA)];
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, TEST_DATA, out, sizeof(TEST_DATA)));
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_ENC);
+ ctx = cryptor->get_context(CipherMode::CIPHER_MODE_DEC);
+ ASSERT_NE(ctx, nullptr);
+ ASSERT_EQ(0, cryptor->init_context(ctx, TEST_IV, sizeof(TEST_IV)));
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, out, out, sizeof(TEST_DATA)));
+ ASSERT_EQ(0, memcmp(out, TEST_DATA, sizeof(TEST_DATA)));
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_DEC);
+}
+
+TEST_F(TestCryptoOpensslDataCryptor, ReuseContext) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+
+ ASSERT_EQ(0, cryptor->init_context(ctx, TEST_IV, sizeof(TEST_IV)));
+ unsigned char out[sizeof(TEST_DATA)];
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, TEST_DATA, out, sizeof(TEST_DATA)));
+
+ ASSERT_EQ(0, cryptor->init_context(ctx, TEST_IV_2, sizeof(TEST_IV_2)));
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, TEST_DATA, out, sizeof(TEST_DATA)));
+
+ auto ctx2 = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx2, nullptr);
+
+ ASSERT_EQ(0, cryptor->init_context(ctx2, TEST_IV_2, sizeof(TEST_IV_2)));
+ unsigned char out2[sizeof(TEST_DATA)];
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx2, TEST_DATA, out2, sizeof(TEST_DATA)));
+
+ ASSERT_EQ(0, memcmp(out, out2, sizeof(TEST_DATA)));
+
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_ENC);
+ cryptor->return_context(ctx2, CipherMode::CIPHER_MODE_ENC);
+}
+
+TEST_F(TestCryptoOpensslDataCryptor, InvalidIVLength) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+
+ ASSERT_EQ(-EINVAL, cryptor->init_context(ctx, TEST_IV, 1));
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_ENC);
+}
+
+} // namespace openssl
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/test_mock_BlockCrypto.cc b/src/test/librbd/crypto/test_mock_BlockCrypto.cc
new file mode 100644
index 000000000..56b0772c0
--- /dev/null
+++ b/src/test/librbd/crypto/test_mock_BlockCrypto.cc
@@ -0,0 +1,156 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "librbd/crypto/BlockCrypto.h"
+#include "test/librbd/mock/crypto/MockDataCryptor.h"
+
+#include "librbd/crypto/BlockCrypto.cc"
+template class librbd::crypto::BlockCrypto<
+ librbd::crypto::MockCryptoContext>;
+
+using ::testing::ExpectationSet;
+using ::testing::internal::ExpectationBase;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::_;
+
+namespace librbd {
+namespace crypto {
+
+MATCHER_P(CompareArrayToString, s, "") {
+ return (memcmp(arg, s.c_str(), s.length()) == 0);
+}
+
+struct TestMockCryptoBlockCrypto : public TestFixture {
+ MockDataCryptor* cryptor;
+ BlockCrypto<MockCryptoContext>* bc;
+ int cryptor_block_size = 16;
+ int cryptor_iv_size = 16;
+ int block_size = 4096;
+ int data_offset = 0;
+ ExpectationSet* expectation_set;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ cryptor = new MockDataCryptor();
+ cryptor->block_size = cryptor_block_size;
+ bc = new BlockCrypto<MockCryptoContext>(
+ reinterpret_cast<CephContext*>(m_ioctx.cct()), cryptor,
+ block_size, data_offset);
+ expectation_set = new ExpectationSet();
+ }
+
+ void TearDown() override {
+ delete expectation_set;
+ delete bc;
+ TestFixture::TearDown();
+ }
+
+ void expect_get_context(CipherMode mode) {
+ _set_last_expectation(
+ EXPECT_CALL(*cryptor, get_context(mode))
+ .After(*expectation_set).WillOnce(Return(
+ new MockCryptoContext())));
+ }
+
+ void expect_return_context(CipherMode mode) {
+ _set_last_expectation(
+ EXPECT_CALL(*cryptor, return_context(_, mode))
+ .After(*expectation_set).WillOnce(WithArg<0>(
+ Invoke([](MockCryptoContext* ctx) {
+ delete ctx;
+ }))));
+ }
+
+ void expect_init_context(const std::string& iv) {
+ _set_last_expectation(
+ EXPECT_CALL(*cryptor, init_context(_, CompareArrayToString(iv),
+ cryptor_iv_size))
+ .After(*expectation_set));
+ }
+
+ void expect_update_context(const std::string& in_str, int out_ret) {
+ _set_last_expectation(
+ EXPECT_CALL(*cryptor, update_context(_,
+ CompareArrayToString(in_str),
+ _, in_str.length()))
+ .After(*expectation_set).WillOnce(Return(out_ret)));
+ }
+
+ void _set_last_expectation(ExpectationBase& expectation) {
+ delete expectation_set;
+ expectation_set = new ExpectationSet(expectation);
+ }
+};
+
+TEST_F(TestMockCryptoBlockCrypto, Encrypt) {
+ uint32_t image_offset = 0x1230 * 512;
+
+ ceph::bufferlist data1;
+ data1.append(std::string(2048, '1'));
+ ceph::bufferlist data2;
+ data2.append(std::string(4096, '2'));
+ ceph::bufferlist data3;
+ data3.append(std::string(2048, '3'));
+
+ ceph::bufferlist data;
+ data.claim_append(data1);
+ data.claim_append(data2);
+ data.claim_append(data3);
+
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ expect_init_context(std::string("\x30\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16));
+ expect_update_context(std::string(2048, '1') + std::string(2048, '2'), 4096);
+ expect_init_context(std::string("\x38\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16));
+ expect_update_context(std::string(2048, '2') + std::string(2048, '3'), 4096);
+ expect_return_context(CipherMode::CIPHER_MODE_ENC);
+
+ ASSERT_EQ(0, bc->encrypt(&data, image_offset));
+
+ ASSERT_EQ(data.length(), 8192);
+}
+
+TEST_F(TestMockCryptoBlockCrypto, UnalignedImageOffset) {
+ ceph::bufferlist data;
+ data.append(std::string(4096, '1'));
+ ASSERT_EQ(-EINVAL, bc->encrypt(&data, 2));
+}
+
+TEST_F(TestMockCryptoBlockCrypto, UnalignedDataLength) {
+ ceph::bufferlist data;
+ data.append(std::string(512, '1'));
+ ASSERT_EQ(-EINVAL, bc->encrypt(&data, 0));
+}
+
+TEST_F(TestMockCryptoBlockCrypto, GetContextError) {
+ ceph::bufferlist data;
+ data.append(std::string(4096, '1'));
+ EXPECT_CALL(*cryptor, get_context(CipherMode::CIPHER_MODE_ENC)).WillOnce(
+ Return(nullptr));
+ ASSERT_EQ(-EIO, bc->encrypt(&data, 0));
+}
+
+TEST_F(TestMockCryptoBlockCrypto, InitContextError) {
+ ceph::bufferlist data;
+ data.append(std::string(4096, '1'));
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ EXPECT_CALL(*cryptor, init_context(_, _, _)).WillOnce(Return(-123));
+ expect_return_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_EQ(-123, bc->encrypt(&data, 0));
+}
+
+TEST_F(TestMockCryptoBlockCrypto, UpdateContextError) {
+ ceph::bufferlist data;
+ data.append(std::string(4096, '1'));
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ EXPECT_CALL(*cryptor, init_context(_, _, _));
+ EXPECT_CALL(*cryptor, update_context(_, _, _, _)).WillOnce(Return(-123));
+ expect_return_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_EQ(-123, bc->encrypt(&data, 0));
+}
+
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/test_mock_CryptoContextPool.cc b/src/test/librbd/crypto/test_mock_CryptoContextPool.cc
new file mode 100644
index 000000000..6eb7877eb
--- /dev/null
+++ b/src/test/librbd/crypto/test_mock_CryptoContextPool.cc
@@ -0,0 +1,54 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "gtest/gtest.h"
+#include "librbd/crypto/CryptoContextPool.h"
+#include "test/librbd/mock/crypto/MockDataCryptor.h"
+
+#include "librbd/crypto/CryptoContextPool.cc"
+template class librbd::crypto::CryptoContextPool<
+ librbd::crypto::MockCryptoContext>;
+
+using ::testing::Return;
+
+namespace librbd {
+namespace crypto {
+
+struct TestMockCryptoCryptoContextPool : public ::testing::Test {
+ MockDataCryptor cryptor;
+
+ void expect_get_context(CipherMode mode) {
+ EXPECT_CALL(cryptor, get_context(mode)).WillOnce(Return(
+ new MockCryptoContext()));
+ }
+
+ void expect_return_context(MockCryptoContext* ctx, CipherMode mode) {
+ delete ctx;
+ EXPECT_CALL(cryptor, return_context(ctx, mode));
+ }
+};
+
+TEST_F(TestMockCryptoCryptoContextPool, Test) {
+ CryptoContextPool<MockCryptoContext> pool(&cryptor, 1);
+
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ auto enc_ctx = pool.get_context(CipherMode::CIPHER_MODE_ENC);
+
+ expect_get_context(CipherMode::CIPHER_MODE_DEC);
+ auto dec_ctx1 = pool.get_context(CipherMode::CIPHER_MODE_DEC);
+ expect_get_context(CipherMode::CIPHER_MODE_DEC);
+ auto dec_ctx2 = pool.get_context(CipherMode::CIPHER_MODE_DEC);
+ pool.return_context(dec_ctx1, CipherMode::CIPHER_MODE_DEC);
+ expect_return_context(dec_ctx2, CipherMode::CIPHER_MODE_DEC);
+ pool.return_context(dec_ctx2, CipherMode::CIPHER_MODE_DEC);
+
+ pool.return_context(enc_ctx, CipherMode::CIPHER_MODE_ENC);
+ ASSERT_EQ(enc_ctx, pool.get_context(CipherMode::CIPHER_MODE_ENC));
+ pool.return_context(enc_ctx, CipherMode::CIPHER_MODE_ENC);
+
+ expect_return_context(enc_ctx, CipherMode::CIPHER_MODE_ENC);
+ expect_return_context(dec_ctx1, CipherMode::CIPHER_MODE_DEC);
+}
+
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc b/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc
new file mode 100644
index 000000000..bfd292616
--- /dev/null
+++ b/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc
@@ -0,0 +1,800 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/mock/crypto/MockCryptoInterface.h"
+#include "librbd/crypto/CryptoObjectDispatch.h"
+#include "librbd/io/ObjectDispatchSpec.h"
+#include "librbd/io/Utils.h"
+
+#include "librbd/io/Utils.cc"
+template bool librbd::io::util::trigger_copyup(
+ MockImageCtx *image_ctx, uint64_t object_no, IOContext io_context,
+ Context* on_finish);
+
+template class librbd::io::ObjectWriteRequest<librbd::MockImageCtx>;
+template class librbd::io::AbstractObjectWriteRequest<librbd::MockImageCtx>;
+#include "librbd/io/ObjectRequest.cc"
+
+namespace librbd {
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace io {
+
+template <>
+struct CopyupRequest<librbd::MockImageCtx> {
+ MOCK_METHOD0(send, void());
+ MOCK_METHOD2(append_request, void(
+ AbstractObjectWriteRequest<librbd::MockImageCtx>*,
+ const Extents&));
+
+ static CopyupRequest* s_instance;
+ static CopyupRequest* create(librbd::MockImageCtx* ictx, uint64_t objectno,
+ Extents&& image_extents, ImageArea area,
+ const ZTracer::Trace& parent_trace) {
+ return s_instance;
+ }
+
+ CopyupRequest() {
+ s_instance = this;
+ }
+};
+
+CopyupRequest<librbd::MockImageCtx>* CopyupRequest<
+ librbd::MockImageCtx>::s_instance = nullptr;
+
+namespace util {
+
+namespace {
+
+struct Mock {
+ static Mock* s_instance;
+
+ Mock() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD6(read_parent,
+ void(MockImageCtx*, uint64_t, io::ReadExtents*,
+ librados::snap_t, const ZTracer::Trace &, Context*));
+};
+
+Mock *Mock::s_instance = nullptr;
+
+} // anonymous namespace
+
+template <> void read_parent(
+ MockImageCtx *image_ctx, uint64_t object_no,
+ io::ReadExtents* extents, librados::snap_t snap_id,
+ const ZTracer::Trace &trace, Context* on_finish) {
+
+ Mock::s_instance->read_parent(image_ctx, object_no, extents, snap_id, trace,
+ on_finish);
+}
+
+} // namespace util
+} // namespace io
+
+} // namespace librbd
+
+#include "librbd/crypto/CryptoObjectDispatch.cc"
+
+namespace librbd {
+namespace crypto {
+
+template <>
+uint64_t get_file_offset(MockImageCtx *image_ctx, uint64_t object_no,
+ uint64_t offset) {
+ return Striper::get_file_offset(image_ctx->cct, &image_ctx->layout,
+ object_no, offset);
+}
+
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Pair;
+using ::testing::Return;
+using ::testing::WithArg;
+
+struct TestMockCryptoCryptoObjectDispatch : public TestMockFixture {
+ typedef CryptoObjectDispatch<librbd::MockImageCtx> MockCryptoObjectDispatch;
+ typedef io::AbstractObjectWriteRequest<librbd::MockImageCtx>
+ MockAbstractObjectWriteRequest;
+ typedef io::CopyupRequest<librbd::MockImageCtx> MockCopyupRequest;
+ typedef io::util::Mock MockUtils;
+
+ MockCryptoInterface crypto;
+ MockImageCtx* mock_image_ctx;
+ MockCryptoObjectDispatch* mock_crypto_object_dispatch;
+
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ C_SaferCond dispatched_cond;
+ Context *on_dispatched = &dispatched_cond;
+ Context *dispatcher_ctx;
+ ceph::bufferlist data;
+ io::DispatchResult dispatch_result;
+ io::Extents extent_map;
+ int object_dispatch_flags = 0;
+ MockUtils mock_utils;
+ MockCopyupRequest copyup_request;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockImageCtx(*ictx);
+ mock_crypto_object_dispatch = new MockCryptoObjectDispatch(
+ mock_image_ctx, &crypto);
+ data.append(std::string(4096, '1'));
+ }
+
+ void TearDown() override {
+ C_SaferCond cond;
+ Context *on_finish = &cond;
+ mock_crypto_object_dispatch->shut_down(on_finish);
+ ASSERT_EQ(0, cond.wait());
+
+ delete mock_crypto_object_dispatch;
+ delete mock_image_ctx;
+
+ TestMockFixture::TearDown();
+ }
+
+ void expect_object_read(io::ReadExtents* extents, uint64_t version = 0) {
+ EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_))
+ .WillOnce(Invoke([this, extents,
+ version](io::ObjectDispatchSpec* spec) {
+ auto* read = boost::get<io::ObjectDispatchSpec::ReadRequest>(
+ &spec->request);
+ ASSERT_TRUE(read != nullptr);
+
+ ASSERT_EQ(extents->size(), read->extents->size());
+ for (uint64_t i = 0; i < extents->size(); ++i) {
+ ASSERT_EQ((*extents)[i].offset, (*read->extents)[i].offset);
+ ASSERT_EQ((*extents)[i].length, (*read->extents)[i].length);
+ (*read->extents)[i].bl = (*extents)[i].bl;
+ (*read->extents)[i].extent_map = (*extents)[i].extent_map;
+ }
+
+ if (read->version != nullptr) {
+ *(read->version) = version;
+ }
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ dispatcher_ctx = &spec->dispatcher_ctx;
+ }));
+ }
+
+ void expect_read_parent(MockUtils &mock_utils, uint64_t object_no,
+ io::ReadExtents* extents, librados::snap_t snap_id,
+ int r) {
+ EXPECT_CALL(mock_utils,
+ read_parent(_, object_no, extents, snap_id, _, _))
+ .WillOnce(WithArg<5>(CompleteContext(
+ r, static_cast<asio::ContextWQ*>(nullptr))));
+ }
+
+ void expect_object_write(uint64_t object_off, const std::string& data,
+ int write_flags,
+ std::optional<uint64_t> assert_version) {
+ EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_))
+ .WillOnce(Invoke([this, object_off, data, write_flags,
+ assert_version](io::ObjectDispatchSpec* spec) {
+ auto* write = boost::get<io::ObjectDispatchSpec::WriteRequest>(
+ &spec->request);
+ ASSERT_TRUE(write != nullptr);
+
+ ASSERT_EQ(object_off, write->object_off);
+ ASSERT_TRUE(data == write->data.to_str());
+ ASSERT_EQ(write_flags, write->write_flags);
+ ASSERT_EQ(assert_version, write->assert_version);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ dispatcher_ctx = &spec->dispatcher_ctx;
+ }));
+ }
+
+ void expect_object_write_same() {
+ EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_))
+ .WillOnce(Invoke([this](io::ObjectDispatchSpec* spec) {
+ auto* write_same = boost::get<
+ io::ObjectDispatchSpec::WriteSameRequest>(
+ &spec->request);
+ ASSERT_TRUE(write_same != nullptr);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ dispatcher_ctx = &spec->dispatcher_ctx;
+ }));
+ }
+
+ void expect_get_object_size() {
+ EXPECT_CALL(*mock_image_ctx, get_object_size()).WillOnce(Return(
+ mock_image_ctx->layout.object_size));
+ }
+
+ void expect_remap_to_logical(uint64_t offset, uint64_t length) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, remap_to_logical(
+ ElementsAre(Pair(offset, length))));
+ }
+
+ void expect_get_parent_overlap(uint64_t overlap) {
+ EXPECT_CALL(*mock_image_ctx, get_parent_overlap(_, _))
+ .WillOnce(WithArg<1>(Invoke([overlap](uint64_t *o) {
+ *o = overlap;
+ return 0;
+ })));
+ }
+
+ void expect_prune_parent_extents(uint64_t object_overlap) {
+ EXPECT_CALL(*mock_image_ctx, prune_parent_extents(_, _, _, _))
+ .WillOnce(Return(object_overlap));
+ }
+
+ void expect_copyup(MockAbstractObjectWriteRequest** write_request, int r) {
+ EXPECT_CALL(copyup_request, append_request(_, _))
+ .WillOnce(WithArg<0>(
+ Invoke([write_request](
+ MockAbstractObjectWriteRequest *req) {
+ *write_request = req;
+ })));
+ EXPECT_CALL(copyup_request, send())
+ .WillOnce(Invoke([write_request, r]() {
+ (*write_request)->handle_copyup(r);
+ }));
+ }
+
+ void expect_encrypt(int count = 1) {
+ EXPECT_CALL(crypto, encrypt(_, _)).Times(count);
+ }
+
+ void expect_decrypt(int count = 1) {
+ EXPECT_CALL(crypto, decrypt(_, _)).Times(count);
+ }
+};
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, Flush) {
+ ASSERT_FALSE(mock_crypto_object_dispatch->flush(
+ io::FLUSH_SOURCE_USER, {}, nullptr, nullptr, &on_finish, nullptr));
+ ASSERT_EQ(on_finish, &finished_cond); // not modified
+ on_finish->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, Discard) {
+ expect_object_write_same();
+ ASSERT_TRUE(mock_crypto_object_dispatch->discard(
+ 11, 0, 4096, mock_image_ctx->get_data_io_context(), 0, {},
+ &object_dispatch_flags, nullptr, &dispatch_result, &on_finish,
+ on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0);
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, AlignedReadFail) {
+ io::ReadExtents extents = {{0, 4096}};
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->read(
+ 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {},
+ nullptr, &object_dispatch_flags, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(-EIO);
+ ASSERT_EQ(-EIO, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, AlignedRead) {
+ io::ReadExtents extents = {{0, 16384}, {32768, 4096}};
+ extents[0].bl.append(std::string(1024, '1') + std::string(1024, '2') +
+ std::string(1024, '3') + std::string(1024, '4'));
+ extents[0].extent_map = {{1024, 1024}, {3072, 2048}, {16384 - 1024, 1024}};
+ extents[1].bl.append(std::string(4096, '0'));
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->read(
+ 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {},
+ nullptr, &object_dispatch_flags, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ expect_decrypt(3);
+ dispatcher_ctx->complete(0);
+ ASSERT_EQ(16384 + 4096, dispatched_cond.wait());
+
+ auto expected_bl_data = (
+ std::string(1024, '\0') + std::string(1024, '1') +
+ std::string(1024, '\0') + std::string(1024, '2') +
+ std::string(1024, '3') + std::string(3072, '\0') +
+ std::string(3072, '\0') + std::string(1024, '4'));
+ ASSERT_TRUE(extents[0].bl.to_str() == expected_bl_data);
+ ASSERT_THAT(extents[0].extent_map,
+ ElementsAre(Pair(0, 8192), Pair(16384 - 4096, 4096)));
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, ReadFromParent) {
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ expect_object_read(&extents);
+ expect_read_parent(mock_utils, 11, &extents, CEPH_NOSNAP, 8192);
+ ASSERT_TRUE(mock_crypto_object_dispatch->read(
+ 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {},
+ nullptr, &object_dispatch_flags, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ // no decrypt
+ dispatcher_ctx->complete(-ENOENT);
+ ASSERT_EQ(8192, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, ReadFromParentDisabled) {
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->read(
+ 11, &extents, mock_image_ctx->get_data_io_context(), 0,
+ io::READ_FLAG_DISABLE_READ_FROM_PARENT, {},
+ nullptr, &object_dispatch_flags, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ // no decrypt
+ dispatcher_ctx->complete(-ENOENT);
+ ASSERT_EQ(-ENOENT, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedRead) {
+ io::ReadExtents extents = {{0, 1}, {8191, 1}, {8193, 1},
+ {16384 + 1, 4096 * 5 - 2}};
+ io::ReadExtents aligned_extents = {{0, 4096}, {4096, 4096}, {8192, 4096},
+ {16384, 4096 * 5}};
+ aligned_extents[0].bl.append(std::string("1") + std::string(4096, '0'));
+ aligned_extents[1].bl.append(std::string(4095, '0') + std::string("2"));
+ aligned_extents[2].bl.append(std::string("03") + std::string(4094, '0'));
+ aligned_extents[3].bl.append(std::string("0") + std::string(4095, '4') +
+ std::string(4096, '5') +
+ std::string(4095, '6') + std::string("0"));
+ aligned_extents[3].extent_map = {{16384, 4096}, {16384 + 2 * 4096, 4096},
+ {16384 + 4 * 4096, 4096}};
+
+ expect_object_read(&aligned_extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->read(
+ 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {},
+ nullptr, &object_dispatch_flags, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ dispatcher_ctx->complete(4096*8);
+ ASSERT_EQ(3 + 4096 * 5 - 2, dispatched_cond.wait());
+ ASSERT_TRUE(extents[0].bl.to_str() == std::string("1"));
+ ASSERT_TRUE(extents[1].bl.to_str() == std::string("2"));
+ ASSERT_TRUE(extents[2].bl.to_str() == std::string("3"));
+
+ auto expected_bl_data = (std::string(4095, '4') + std::string(4096, '5') +
+ std::string(4095, '6'));
+ ASSERT_TRUE(extents[3].bl.to_str() == expected_bl_data);
+ ASSERT_THAT(extents[3].extent_map,
+ ElementsAre(Pair(16384 + 1, 4095), Pair(16384 + 2 * 4096, 4096),
+ Pair(16384 + 4 * 4096, 4095)));
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, AlignedWrite) {
+ expect_encrypt();
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 0, std::move(data), mock_image_ctx->get_data_io_context(), 0, 0,
+ std::nullopt, {}, nullptr, nullptr, &dispatch_result, &on_finish,
+ on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_CONTINUE);
+ ASSERT_EQ(on_finish, &finished_cond); // not modified
+ on_finish->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWrite) {
+ ceph::bufferlist write_data;
+ uint64_t version = 1234;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ expect_object_read(&extents, version);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ auto expected_data =
+ std::string("2") + std::string(8192, '1') + std::string(4095, '3');
+ expect_object_write(0, expected_data, 0, std::make_optional(version));
+ dispatcher_ctx->complete(8192); // complete read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0); // complete write
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteWithNoObject) {
+ ceph::bufferlist write_data;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ expect_get_object_size();
+ expect_get_parent_overlap(0);
+ auto expected_data = (std::string(1, '\0') + std::string(8192, '1') +
+ std::string(4095, '\0'));
+ expect_object_write(0, expected_data, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE,
+ std::nullopt);
+ dispatcher_ctx->complete(-ENOENT); // complete read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0); // complete write
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteFailCreate) {
+ ceph::bufferlist write_data;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ expect_get_object_size();
+ expect_get_parent_overlap(0);
+ auto expected_data = (std::string(1, '\0') + std::string(8192, '1') +
+ std::string(4095, '\0'));
+ expect_object_write(0, expected_data, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE,
+ std::nullopt);
+ dispatcher_ctx->complete(-ENOENT); // complete read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ uint64_t version = 1234;
+ expect_object_read(&extents, version);
+ dispatcher_ctx->complete(-EEXIST); // complete write, request will restart
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ auto expected_data2 =
+ std::string("2") + std::string(8192, '1') + std::string(4095, '3');
+ expect_object_write(0, expected_data2, 0, std::make_optional(version));
+ dispatcher_ctx->complete(8192); // complete read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0); // complete write
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteCopyup) {
+ MockObjectMap mock_object_map;
+ mock_image_ctx->object_map = &mock_object_map;
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx->exclusive_lock = &mock_exclusive_lock;
+
+ ceph::bufferlist write_data;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ expect_get_object_size();
+ expect_get_parent_overlap(100 << 20);
+ expect_remap_to_logical(11 * mock_image_ctx->layout.object_size,
+ mock_image_ctx->layout.object_size);
+ expect_prune_parent_extents(mock_image_ctx->layout.object_size);
+ EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillRepeatedly(
+ Return(true));
+ EXPECT_CALL(*mock_image_ctx->object_map, object_may_exist(11)).WillOnce(
+ Return(false));
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ expect_copyup(&write_request, 0);
+
+ // unaligned write restarted
+ uint64_t version = 1234;
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ expect_object_read(&extents, version);
+ dispatcher_ctx->complete(-ENOENT); // complete first read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ auto expected_data =
+ std::string("2") + std::string(8192, '1') + std::string(4095, '3');
+ expect_object_write(0, expected_data, 0, std::make_optional(version));
+ dispatcher_ctx->complete(8192); // complete second read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0); // complete write
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteEmptyCopyup) {
+ MockObjectMap mock_object_map;
+ mock_image_ctx->object_map = &mock_object_map;
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx->exclusive_lock = &mock_exclusive_lock;
+
+ ceph::bufferlist write_data;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ expect_get_object_size();
+ expect_get_parent_overlap(100 << 20);
+ expect_remap_to_logical(11 * mock_image_ctx->layout.object_size,
+ mock_image_ctx->layout.object_size);
+ expect_prune_parent_extents(mock_image_ctx->layout.object_size);
+ EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillRepeatedly(
+ Return(true));
+ EXPECT_CALL(*mock_image_ctx->object_map, object_may_exist(11)).WillOnce(
+ Return(false));
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ expect_copyup(&write_request, 0);
+
+ // unaligned write restarted
+ expect_object_read(&extents);
+ dispatcher_ctx->complete(-ENOENT); // complete first read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ auto expected_data =
+ std::string(1, '\0') + std::string(8192, '1') +
+ std::string(4095, '\0');
+ expect_object_write(0, expected_data, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE,
+ std::nullopt);
+ dispatcher_ctx->complete(-ENOENT); // complete second read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0); // complete write
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteFailVersionCheck) {
+ ceph::bufferlist write_data;
+ uint64_t version = 1234;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ expect_object_read(&extents, version);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result,
+ &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ auto expected_data =
+ std::string("2") + std::string(8192, '1') + std::string(4095, '3');
+ expect_object_write(0, expected_data, 0, std::make_optional(version));
+ dispatcher_ctx->complete(8192); // complete read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ version = 1235;
+ expect_object_read(&extents, version);
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ dispatcher_ctx->complete(-ERANGE); // complete write, request will restart
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+
+ expect_object_write(0, expected_data, 0, std::make_optional(version));
+ dispatcher_ctx->complete(8192); // complete read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0); // complete write
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteWithAssertVersion) {
+ ceph::bufferlist write_data;
+ uint64_t version = 1234;
+ uint64_t assert_version = 1233;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ expect_object_read(&extents, version);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, 0, std::make_optional(assert_version), {}, nullptr, nullptr,
+ &dispatch_result, &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ dispatcher_ctx->complete(8192); // complete read
+ ASSERT_EQ(-ERANGE, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteWithExclusiveCreate) {
+ ceph::bufferlist write_data;
+ write_data.append(std::string(8192, '1'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ expect_object_read(&extents);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write(
+ 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(),
+ 0, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, std::nullopt, {}, nullptr,
+ nullptr, &dispatch_result, &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ dispatcher_ctx->complete(8192); // complete read
+ ASSERT_EQ(-EEXIST, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, CompareAndWrite) {
+ ceph::bufferlist write_data;
+ uint64_t version = 1234;
+ write_data.append(std::string(8192, '1'));
+ ceph::bufferlist cmp_data;
+ cmp_data.append(std::string(4096, '2'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}, {0, 8192}};
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ extents[2].bl.append(std::string(8192, '2'));
+ expect_object_read(&extents, version);
+
+ ASSERT_TRUE(mock_crypto_object_dispatch->compare_and_write(
+ 11, 1, std::move(cmp_data), std::move(write_data),
+ mock_image_ctx->get_data_io_context(), 0, {}, nullptr, nullptr,
+ nullptr, &dispatch_result, &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ auto expected_data =
+ std::string("2") + std::string(8192, '1') + std::string(4095, '3');
+ expect_object_write(0, expected_data, 0, std::make_optional(version));
+ dispatcher_ctx->complete(4096*4); // complete read
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0); // complete write
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, CompareAndWriteFail) {
+ ceph::bufferlist write_data;
+ uint64_t version = 1234;
+ write_data.append(std::string(8192, '1'));
+ ceph::bufferlist cmp_data;
+ cmp_data.append(std::string(4094, '2') + std::string(2, '4'));
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}, {0, 8192}};
+ extents[0].bl.append(std::string(4096, '2'));
+ extents[1].bl.append(std::string(4096, '3'));
+ extents[2].bl.append(std::string(8192, '2'));
+ expect_object_read(&extents, version);
+
+ uint64_t mismatch_offset;
+ ASSERT_TRUE(mock_crypto_object_dispatch->compare_and_write(
+ 11, 1, std::move(cmp_data), std::move(write_data),
+ mock_image_ctx->get_data_io_context(), 0, {}, &mismatch_offset,
+ nullptr, nullptr, &dispatch_result, &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_EQ(on_finish, &finished_cond);
+
+ dispatcher_ctx->complete(4096*4); // complete read
+ ASSERT_EQ(-EILSEQ, dispatched_cond.wait());
+ ASSERT_EQ(mismatch_offset, 4094);
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, WriteSame) {
+ io::LightweightBufferExtents buffer_extents;
+ ceph::bufferlist write_data;
+ write_data.append(std::string("12"));
+ expect_object_write(0, std::string("12121") , 0, std::nullopt);
+ ASSERT_TRUE(mock_crypto_object_dispatch->write_same(
+ 11, 0, 5, {{0, 5}}, std::move(write_data),
+ mock_image_ctx->get_data_io_context(), 0, {}, nullptr, nullptr,
+ &dispatch_result, &on_finish, on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+
+ ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0));
+ dispatcher_ctx->complete(0);
+ ASSERT_EQ(0, dispatched_cond.wait());
+}
+
+TEST_F(TestMockCryptoCryptoObjectDispatch, PrepareCopyup) {
+ char* data = (char*)"0123456789";
+ io::SnapshotSparseBufferlist snapshot_sparse_bufferlist;
+ auto& snap1 = snapshot_sparse_bufferlist[0];
+ auto& snap2 = snapshot_sparse_bufferlist[1];
+
+ snap1.insert(0, 1, {io::SPARSE_EXTENT_STATE_DATA, 1,
+ ceph::bufferlist::static_from_mem(data + 1, 1)});
+ snap1.insert(8191, 1, {io::SPARSE_EXTENT_STATE_DATA, 1,
+ ceph::bufferlist::static_from_mem(data + 2, 1)});
+ snap1.insert(8193, 3, {io::SPARSE_EXTENT_STATE_DATA, 3,
+ ceph::bufferlist::static_from_mem(data + 3, 3)});
+
+ snap2.insert(0, 2, {io::SPARSE_EXTENT_STATE_ZEROED, 2});
+ snap2.insert(8191, 3, {io::SPARSE_EXTENT_STATE_DATA, 3,
+ ceph::bufferlist::static_from_mem(data + 6, 3)});
+ snap2.insert(16384, 1, {io::SPARSE_EXTENT_STATE_DATA, 1,
+ ceph::bufferlist::static_from_mem(data + 9, 1)});
+
+ expect_get_object_size();
+ expect_encrypt(6);
+ InSequence seq;
+ uint64_t base = 11 * mock_image_ctx->layout.object_size;
+ expect_remap_to_logical(base, 4096);
+ expect_remap_to_logical(base + 4096, 4096);
+ expect_remap_to_logical(base + 8192, 4096);
+ expect_remap_to_logical(base, 4096);
+ expect_remap_to_logical(base + 4096, 8192);
+ expect_remap_to_logical(base + 16384, 4096);
+ ASSERT_EQ(0, mock_crypto_object_dispatch->prepare_copyup(
+ 11, &snapshot_sparse_bufferlist));
+
+ ASSERT_EQ(2, snapshot_sparse_bufferlist.size());
+
+ auto& snap1_result = snapshot_sparse_bufferlist[0];
+ auto& snap2_result = snapshot_sparse_bufferlist[1];
+
+ auto it = snap1_result.begin();
+ ASSERT_NE(it, snap1_result.end());
+ ASSERT_EQ(0, it.get_off());
+ ASSERT_EQ(4096 * 3, it.get_len());
+
+ ASSERT_TRUE(it.get_val().bl.to_str() ==
+ std::string("1") + std::string(4095, '\0') +
+ std::string(4095, '\0') + std::string("2") +
+ std::string(1, '\0') + std::string("345") + std::string(4092, '\0'));
+ ASSERT_EQ(++it, snap1_result.end());
+
+ it = snap2_result.begin();
+ ASSERT_NE(it, snap2_result.end());
+ ASSERT_EQ(0, it.get_off());
+ ASSERT_EQ(4096 * 3, it.get_len());
+ ASSERT_TRUE(it.get_val().bl.to_str() ==
+ std::string(4096, '\0') +
+ std::string(4095, '\0') + std::string("6") +
+ std::string("7845") + std::string(4092, '\0'));
+
+ ASSERT_NE(++it, snap2_result.end());
+ ASSERT_EQ(16384, it.get_off());
+ ASSERT_EQ(4096, it.get_len());
+ ASSERT_TRUE(it.get_val().bl.to_str() ==
+ std::string("9") + std::string(4095, '\0'));
+ ASSERT_EQ(++it, snap2_result.end());
+}
+
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/test_mock_FormatRequest.cc b/src/test/librbd/crypto/test_mock_FormatRequest.cc
new file mode 100644
index 000000000..81b82429d
--- /dev/null
+++ b/src/test/librbd/crypto/test_mock_FormatRequest.cc
@@ -0,0 +1,231 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/crypto/MockCryptoInterface.h"
+#include "test/librbd/mock/crypto/MockEncryptionFormat.h"
+#include "librbd/crypto/Utils.h"
+
+namespace librbd {
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/crypto/FormatRequest.cc"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace crypto {
+namespace {
+
+} // anonymous namespace
+
+namespace util {
+
+template <>
+void set_crypto(MockTestImageCtx *image_ctx,
+ std::unique_ptr<MockEncryptionFormat> encryption_format) {
+ image_ctx->encryption_format = std::move(encryption_format);
+}
+
+} // namespace util
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+
+template <>
+struct ShutDownCryptoRequest<MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static ShutDownCryptoRequest *s_instance;
+ static ShutDownCryptoRequest *create(MockTestImageCtx *image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ ShutDownCryptoRequest() {
+ s_instance = this;
+ }
+};
+
+ShutDownCryptoRequest<MockTestImageCtx> *ShutDownCryptoRequest<
+ MockTestImageCtx>::s_instance = nullptr;
+
+struct TestMockCryptoFormatRequest : public TestMockFixture {
+ typedef FormatRequest<librbd::MockTestImageCtx> MockFormatRequest;
+ typedef ShutDownCryptoRequest<MockTestImageCtx> MockShutDownCryptoRequest;
+
+ MockTestImageCtx* mock_image_ctx;
+ MockTestImageCtx* mock_parent_image_ctx;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ MockShutDownCryptoRequest mock_shutdown_crypto_request;
+ MockEncryptionFormat* old_encryption_format;
+ MockEncryptionFormat* new_encryption_format;
+ Context* format_context;
+ MockFormatRequest* mock_format_request;
+ std::string key = std::string(64, '0');
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockTestImageCtx(*ictx);
+ mock_parent_image_ctx = new MockTestImageCtx(*ictx);
+ old_encryption_format = new MockEncryptionFormat();
+ new_encryption_format = new MockEncryptionFormat();
+ mock_image_ctx->encryption_format.reset(old_encryption_format);
+ mock_format_request = MockFormatRequest::create(
+ mock_image_ctx,
+ std::unique_ptr<MockEncryptionFormat>(new_encryption_format),
+ on_finish);
+ }
+
+ void TearDown() override {
+ delete mock_parent_image_ctx;
+ delete mock_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_test_journal_feature(bool has_journal=false) {
+ EXPECT_CALL(*mock_image_ctx, test_features(
+ RBD_FEATURE_JOURNALING)).WillOnce(Return(has_journal));
+ }
+
+ void expect_shutdown_crypto(int r = 0) {
+ EXPECT_CALL(mock_shutdown_crypto_request, send()).WillOnce(
+ Invoke([this, r]() {
+ if (r == 0) {
+ mock_image_ctx->encryption_format.reset();
+ }
+ mock_shutdown_crypto_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_encryption_format() {
+ EXPECT_CALL(*new_encryption_format, format(
+ mock_image_ctx, _)).WillOnce(
+ WithArg<1>(Invoke([this](Context* ctx) {
+ format_context = ctx;
+ })));
+ }
+
+ void expect_image_flush(int r) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_)).WillOnce(
+ Invoke([r](io::ImageDispatchSpec* spec) {
+ ASSERT_TRUE(boost::get<io::ImageDispatchSpec::Flush>(
+ &spec->request) != nullptr);
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ spec->aio_comp->set_request_count(1);
+ spec->aio_comp->add_request();
+ spec->aio_comp->complete_request(r);
+ }));
+ }
+};
+
+TEST_F(TestMockCryptoFormatRequest, JournalEnabled) {
+ expect_test_journal_feature(true);
+ mock_format_request->send();
+ ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+ ASSERT_EQ(old_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoFormatRequest, FailShutDownCrypto) {
+ expect_test_journal_feature(false);
+ expect_shutdown_crypto(-EIO);
+ mock_format_request->send();
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(old_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoFormatRequest, FormatFail) {
+ mock_image_ctx->encryption_format = nullptr;
+ expect_test_journal_feature(false);
+ expect_encryption_format();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ format_context->complete(-EIO);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format);
+}
+
+TEST_F(TestMockCryptoFormatRequest, Success) {
+ mock_image_ctx->encryption_format = nullptr;
+ expect_test_journal_feature(false);
+ expect_encryption_format();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(0);
+ format_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(new_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoFormatRequest, FailFlush) {
+ expect_test_journal_feature(false);
+ expect_shutdown_crypto();
+ expect_encryption_format();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(-EIO);
+ format_context->complete(0);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoFormatRequest, CryptoAlreadyLoaded) {
+ expect_test_journal_feature(false);
+ expect_shutdown_crypto();
+ expect_encryption_format();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(0);
+ format_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(new_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoFormatRequest, ThinFormat) {
+ mock_image_ctx->encryption_format = nullptr;
+ mock_image_ctx->parent = mock_parent_image_ctx;
+ expect_test_journal_feature(false);
+ expect_encryption_format();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_image_flush(0);
+ format_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoFormatRequest, ThinFormatEncryptionLoaded) {
+ mock_image_ctx->parent = mock_parent_image_ctx;
+ expect_test_journal_feature(false);
+ mock_format_request->send();
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+}
+
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/test_mock_LoadRequest.cc b/src/test/librbd/crypto/test_mock_LoadRequest.cc
new file mode 100644
index 000000000..849710d82
--- /dev/null
+++ b/src/test/librbd/crypto/test_mock_LoadRequest.cc
@@ -0,0 +1,382 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/crypto/CryptoObjectDispatch.h"
+#include "librbd/crypto/Utils.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/crypto/MockCryptoInterface.h"
+#include "test/librbd/mock/crypto/MockEncryptionFormat.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+
+ MockTestImageCtx *parent = nullptr;
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockTestImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace crypto {
+namespace util {
+
+template <>
+void set_crypto(MockTestImageCtx *image_ctx,
+ std::unique_ptr<MockEncryptionFormat> encryption_format) {
+ image_ctx->encryption_format = std::move(encryption_format);
+}
+
+} // namespace util
+} // namespace crypto
+} // namespace librbd
+
+#include "librbd/crypto/LoadRequest.cc"
+
+namespace librbd {
+namespace crypto {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArgs;
+
+struct TestMockCryptoLoadRequest : public TestMockFixture {
+ typedef LoadRequest<librbd::MockTestImageCtx> MockLoadRequest;
+
+ MockTestImageCtx* mock_image_ctx;
+ MockTestImageCtx* mock_parent_image_ctx;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ MockEncryptionFormat* mock_encryption_format;
+ MockEncryptionFormat* cloned_encryption_format;
+ Context* load_context;
+ MockLoadRequest* mock_load_request;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockTestImageCtx(*ictx);
+ mock_parent_image_ctx = new MockTestImageCtx(*ictx);
+ mock_image_ctx->parent = mock_parent_image_ctx;
+ mock_encryption_format = new MockEncryptionFormat();
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ formats.emplace_back(mock_encryption_format);
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, std::move(formats), on_finish);
+ }
+
+ void TearDown() override {
+ delete mock_image_ctx;
+ delete mock_parent_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_test_journal_feature(MockTestImageCtx* ictx,
+ bool has_journal = false) {
+ EXPECT_CALL(*ictx, test_features(
+ RBD_FEATURE_JOURNALING)).WillOnce(Return(has_journal));
+ }
+
+ void expect_encryption_load(MockEncryptionFormat* encryption_format,
+ MockTestImageCtx* ictx,
+ std::string detected_format = "SOMEFORMAT") {
+ EXPECT_CALL(*encryption_format, load(
+ ictx, _, _)).WillOnce(
+ WithArgs<1, 2>(Invoke([this, detected_format](
+ std::string* detected_format_name, Context* ctx) {
+ if (!detected_format.empty()) {
+ *detected_format_name = detected_format;
+ }
+ load_context = ctx;
+ })));
+ }
+
+ void expect_encryption_format_clone(MockEncryptionFormat* encryption_format) {
+ cloned_encryption_format = new MockEncryptionFormat();
+ EXPECT_CALL(*encryption_format, clone()).WillOnce(
+ Invoke([this]() {
+ return std::unique_ptr<MockEncryptionFormat>(
+ cloned_encryption_format);
+ }));
+ }
+
+ void expect_image_flush(int r = 0) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_)).WillOnce(
+ Invoke([r](io::ImageDispatchSpec* spec) {
+ ASSERT_TRUE(boost::get<io::ImageDispatchSpec::Flush>(
+ &spec->request) != nullptr);
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ spec->aio_comp->set_request_count(1);
+ spec->aio_comp->add_request();
+ spec->aio_comp->complete_request(r);
+ }));
+ }
+
+ void expect_invalidate_cache(int r = 0) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher,
+ invalidate_cache(_)).WillOnce(
+ Invoke([r](Context* ctx) {
+ ctx->complete(r);
+ }));
+ }
+};
+
+TEST_F(TestMockCryptoLoadRequest, NoFormats) {
+ delete mock_load_request;
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, std::move(formats), on_finish);
+ mock_load_request->send();
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLoadRequest, CryptoAlreadyLoaded) {
+ mock_image_ctx->encryption_format.reset(new MockEncryptionFormat());
+ mock_load_request->send();
+ ASSERT_EQ(-EEXIST, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLoadRequest, JournalEnabled) {
+ expect_test_journal_feature(mock_image_ctx, true);
+ mock_load_request->send();
+ ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLoadRequest, JournalEnabledOnParent) {
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx, true);
+ mock_load_request->send();
+ ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLoadRequest, LoadFail) {
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ load_context->complete(-EINVAL);
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLoadRequest, Success) {
+ delete mock_load_request;
+ mock_image_ctx->parent = nullptr;
+ mock_encryption_format = new MockEncryptionFormat();
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ formats.emplace_back(mock_encryption_format);
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, std::move(formats), on_finish);
+ expect_test_journal_feature(mock_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_invalidate_cache();
+ load_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLoadRequest, FlushFail) {
+ delete mock_load_request;
+ mock_image_ctx->parent = nullptr;
+ mock_encryption_format = new MockEncryptionFormat();
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ formats.emplace_back(mock_encryption_format);
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, std::move(formats), on_finish);
+ expect_test_journal_feature(mock_image_ctx);
+ expect_image_flush(-EIO);
+ mock_load_request->send();
+ ASSERT_EQ(-EIO, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLoadRequest, InvalidateCacheFail) {
+ delete mock_load_request;
+ mock_image_ctx->parent = nullptr;
+ mock_encryption_format = new MockEncryptionFormat();
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ formats.emplace_back(mock_encryption_format);
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, std::move(formats), on_finish);
+ expect_test_journal_feature(mock_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_invalidate_cache(-EIO);
+ load_context->complete(0);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLoadRequest, LoadClonedEncryptedParent) {
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_encryption_format_clone(mock_encryption_format);
+ expect_encryption_load(cloned_encryption_format, mock_parent_image_ctx);
+ load_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_invalidate_cache();
+ load_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+ ASSERT_EQ(cloned_encryption_format,
+ mock_parent_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLoadRequest, LoadClonedParentFail) {
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_encryption_format_clone(mock_encryption_format);
+ expect_encryption_load(cloned_encryption_format, mock_parent_image_ctx);
+ load_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ load_context->complete(-EIO);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+ ASSERT_EQ(nullptr, mock_parent_image_ctx->encryption_format.get());
+}
+
+
+TEST_F(TestMockCryptoLoadRequest, LoadClonedPlaintextParent) {
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_encryption_format_clone(mock_encryption_format);
+ expect_encryption_load(
+ cloned_encryption_format, mock_parent_image_ctx,
+ LoadRequest<MockImageCtx>::UNKNOWN_FORMAT);
+ load_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_invalidate_cache();
+ load_context->complete(-EINVAL);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+ ASSERT_EQ(nullptr, mock_parent_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLoadRequest, LoadClonedParentDetectionError) {
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_encryption_format_clone(mock_encryption_format);
+ expect_encryption_load(
+ cloned_encryption_format, mock_parent_image_ctx, "");
+ load_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ load_context->complete(-EINVAL);
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+ ASSERT_EQ(nullptr, mock_parent_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLoadRequest, LoadParentFail) {
+ delete mock_load_request;
+ mock_encryption_format = new MockEncryptionFormat();
+ auto mock_parent_encryption_format = new MockEncryptionFormat();
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ formats.emplace_back(mock_encryption_format);
+ formats.emplace_back(mock_parent_encryption_format);
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx,
+ std::move(formats),
+ on_finish);
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_encryption_load(mock_parent_encryption_format, mock_parent_image_ctx);
+ load_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ load_context->complete(-EINVAL);
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+ ASSERT_EQ(nullptr, mock_parent_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLoadRequest, EncryptedParent) {
+ delete mock_load_request;
+ mock_encryption_format = new MockEncryptionFormat();
+ auto mock_parent_encryption_format = new MockEncryptionFormat();
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ formats.emplace_back(mock_encryption_format);
+ formats.emplace_back(mock_parent_encryption_format);
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx,
+ std::move(formats),
+ on_finish);
+ expect_test_journal_feature(mock_image_ctx);
+ expect_test_journal_feature(mock_parent_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_encryption_load(mock_parent_encryption_format, mock_parent_image_ctx);
+ load_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_invalidate_cache();
+ load_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+ ASSERT_EQ(mock_parent_encryption_format,
+ mock_parent_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockCryptoLoadRequest, TooManyFormats) {
+ delete mock_load_request;
+ mock_encryption_format = new MockEncryptionFormat();
+ auto mock_parent_encryption_format = new MockEncryptionFormat();
+ std::vector<std::unique_ptr<MockEncryptionFormat>> formats;
+ formats.emplace_back(mock_encryption_format);
+ formats.emplace_back(mock_parent_encryption_format);
+ mock_image_ctx->parent = nullptr;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx,
+ std::move(formats),
+ on_finish);
+ expect_test_journal_feature(mock_image_ctx);
+ expect_image_flush();
+ expect_encryption_load(mock_encryption_format, mock_image_ctx);
+ mock_load_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ load_context->complete(0);
+ ASSERT_EQ(-EINVAL, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+}
+
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc b/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc
new file mode 100644
index 000000000..7df99b78a
--- /dev/null
+++ b/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc
@@ -0,0 +1,174 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/crypto/Utils.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/crypto/MockEncryptionFormat.h"
+
+#include "librbd/crypto/ShutDownCryptoRequest.cc"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+
+ MockTestImageCtx *parent = nullptr;
+};
+
+} // anonymous namespace
+
+namespace crypto {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArgs;
+
+struct TestMockShutDownCryptoRequest : public TestMockFixture {
+ typedef ShutDownCryptoRequest<MockTestImageCtx> MockShutDownCryptoRequest;
+
+ MockTestImageCtx* mock_image_ctx;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ MockShutDownCryptoRequest* mock_shutdown_crypto_request;
+ MockEncryptionFormat* mock_encryption_format;
+ Context* shutdown_object_dispatch_context;
+ Context* shutdown_image_dispatch_context;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockTestImageCtx(*ictx);
+ mock_encryption_format = new MockEncryptionFormat();
+ mock_image_ctx->encryption_format.reset(mock_encryption_format);
+ mock_shutdown_crypto_request = MockShutDownCryptoRequest::create(
+ mock_image_ctx, on_finish);
+ }
+
+ void TearDown() override {
+ delete mock_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_crypto_object_layer_exists_check(
+ MockTestImageCtx* image_ctx, bool exists) {
+ EXPECT_CALL(*image_ctx->io_object_dispatcher, exists(
+ io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
+ }
+
+ void expect_crypto_image_layer_exists_check(
+ MockTestImageCtx* image_ctx, bool exists) {
+ EXPECT_CALL(*image_ctx->io_image_dispatcher, exists(
+ io::IMAGE_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
+ }
+
+ void expect_shutdown_crypto_object_dispatch(MockTestImageCtx* image_ctx) {
+ EXPECT_CALL(*image_ctx->io_object_dispatcher, shut_down_dispatch(
+ io::OBJECT_DISPATCH_LAYER_CRYPTO, _)).WillOnce(
+ WithArgs<1>(Invoke([this](Context* ctx) {
+ shutdown_object_dispatch_context = ctx;
+ })));
+ }
+
+ void expect_shutdown_crypto_image_dispatch(MockTestImageCtx* image_ctx) {
+ EXPECT_CALL(*image_ctx->io_image_dispatcher, shut_down_dispatch(
+ io::IMAGE_DISPATCH_LAYER_CRYPTO, _)).WillOnce(
+ WithArgs<1>(Invoke([this](Context* ctx) {
+ shutdown_image_dispatch_context = ctx;
+ })));
+ }
+};
+
+TEST_F(TestMockShutDownCryptoRequest, NoCryptoObjectDispatch) {
+ expect_crypto_object_layer_exists_check(mock_image_ctx, false);
+ mock_shutdown_crypto_request->send();
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockShutDownCryptoRequest, FailShutdownObjectDispatch) {
+ expect_crypto_object_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_object_dispatch(mock_image_ctx);
+ mock_shutdown_crypto_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ shutdown_object_dispatch_context->complete(-EIO);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockShutDownCryptoRequest, NoCryptoImageDispatch) {
+ expect_crypto_object_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_object_dispatch(mock_image_ctx);
+ mock_shutdown_crypto_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_crypto_image_layer_exists_check(mock_image_ctx, false);
+ shutdown_object_dispatch_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockShutDownCryptoRequest, FailShutdownImageDispatch) {
+ expect_crypto_object_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_object_dispatch(mock_image_ctx);
+ mock_shutdown_crypto_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_crypto_image_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_image_dispatch(mock_image_ctx);
+ shutdown_object_dispatch_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ shutdown_image_dispatch_context->complete(-EIO);
+ ASSERT_EQ(-EIO, finished_cond.wait());
+ ASSERT_EQ(mock_encryption_format, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockShutDownCryptoRequest, Success) {
+ expect_crypto_object_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_object_dispatch(mock_image_ctx);
+ mock_shutdown_crypto_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_crypto_image_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_image_dispatch(mock_image_ctx);
+ shutdown_object_dispatch_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ shutdown_image_dispatch_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+}
+
+TEST_F(TestMockShutDownCryptoRequest, ShutdownParent) {
+ auto parent_image_ctx = new MockTestImageCtx(*mock_image_ctx->image_ctx);
+ mock_image_ctx->parent = parent_image_ctx;
+ expect_crypto_object_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_object_dispatch(mock_image_ctx);
+ mock_shutdown_crypto_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_crypto_image_layer_exists_check(mock_image_ctx, true);
+ expect_shutdown_crypto_image_dispatch(mock_image_ctx);
+ shutdown_object_dispatch_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_crypto_object_layer_exists_check(parent_image_ctx, true);
+ expect_shutdown_crypto_object_dispatch(parent_image_ctx);
+ shutdown_image_dispatch_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ expect_crypto_image_layer_exists_check(parent_image_ctx, true);
+ expect_shutdown_crypto_image_dispatch(parent_image_ctx);
+ shutdown_object_dispatch_context->complete(0);
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ mock_image_ctx->parent = nullptr;
+ shutdown_image_dispatch_context->complete(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx->encryption_format.get());
+ ASSERT_EQ(nullptr, parent_image_ctx->encryption_format.get());
+ delete parent_image_ctx;
+}
+
+} // namespace crypto
+} // namespace librbd
diff --git a/src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc b/src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc
new file mode 100644
index 000000000..e38ffffdb
--- /dev/null
+++ b/src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc
@@ -0,0 +1,807 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/AsioEngine.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/Operations.h"
+#include "librbd/deep_copy/Handler.h"
+#include "librbd/deep_copy/ImageCopyRequest.h"
+#include "librbd/deep_copy/ObjectCopyRequest.h"
+#include "librbd/object_map/DiffRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/test_support.h"
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ static MockTestImageCtx* s_instance;
+ static MockTestImageCtx* create(const std::string &image_name,
+ const std::string &image_id,
+ librados::snap_t snap_id, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(destroy, void());
+};
+
+MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+
+namespace deep_copy {
+
+template <>
+struct ObjectCopyRequest<librbd::MockTestImageCtx> {
+ static ObjectCopyRequest* s_instance;
+ static ObjectCopyRequest* create(
+ librbd::MockTestImageCtx *src_image_ctx,
+ librbd::MockTestImageCtx *dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t dst_snap_id_start,
+ const SnapMap &snap_map,
+ uint64_t object_number, uint32_t flags, Handler* handler,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ std::lock_guard locker{s_instance->lock};
+ s_instance->snap_map = &snap_map;
+ s_instance->flags = flags;
+ s_instance->object_contexts[object_number] = on_finish;
+ s_instance->cond.notify_all();
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cond;
+
+ const SnapMap *snap_map = nullptr;
+ std::map<uint64_t, Context *> object_contexts;
+ uint32_t flags = 0;
+
+ ObjectCopyRequest() {
+ s_instance = this;
+ }
+};
+
+ObjectCopyRequest<librbd::MockTestImageCtx>* ObjectCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace deep_copy
+
+namespace object_map {
+
+template <>
+struct DiffRequest<MockTestImageCtx> {
+ BitVector<2>* object_diff_state = nullptr;
+ Context* on_finish = nullptr;
+ static DiffRequest* s_instance;
+ static DiffRequest* create(MockTestImageCtx *image_ctx,
+ uint64_t snap_id_start, uint64_t snap_id_end,
+ BitVector<2>* object_diff_state,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->object_diff_state = object_diff_state;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ DiffRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+DiffRequest<MockTestImageCtx>* DiffRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace object_map
+} // namespace librbd
+
+// template definitions
+#include "librbd/deep_copy/ImageCopyRequest.cc"
+template class librbd::deep_copy::ImageCopyRequest<librbd::MockTestImageCtx>;
+
+using namespace std::chrono_literals;
+
+namespace librbd {
+namespace deep_copy {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+
+class TestMockDeepCopyImageCopyRequest : public TestMockFixture {
+public:
+ typedef ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
+ typedef ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest;
+ typedef object_map::DiffRequest<librbd::MockTestImageCtx> MockDiffRequest;
+
+ librbd::ImageCtx *m_src_image_ctx;
+ librbd::ImageCtx *m_dst_image_ctx;
+
+ std::shared_ptr<librbd::AsioEngine> m_asio_engine;
+ asio::ContextWQ *m_work_queue;
+
+ librbd::SnapSeqs m_snap_seqs;
+ SnapMap m_snap_map;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
+
+ librbd::RBD rbd;
+ std::string dst_image_name = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx));
+
+ m_asio_engine = std::make_shared<librbd::AsioEngine>(
+ m_src_image_ctx->md_ctx);
+ m_work_queue = m_asio_engine->get_work_queue();
+ }
+
+ void expect_get_image_size(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t size) {
+ EXPECT_CALL(mock_image_ctx, get_image_size(_))
+ .WillOnce(Return(size)).RetiresOnSaturation();
+ }
+
+ void expect_diff_send(MockDiffRequest& mock_request,
+ const BitVector<2>& diff_state, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &mock_request, diff_state, r]() {
+ if (r >= 0) {
+ *mock_request.object_diff_state = diff_state;
+ }
+ m_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_object_copy_send(MockObjectCopyRequest &mock_object_copy_request,
+ uint32_t flags) {
+ EXPECT_CALL(mock_object_copy_request, send())
+ .WillOnce(Invoke([&mock_object_copy_request, flags]() {
+ ASSERT_EQ(flags, mock_object_copy_request.flags);
+ }));
+ }
+
+ bool complete_object_copy(MockObjectCopyRequest &mock_object_copy_request,
+ uint64_t object_num, Context **object_ctx, int r) {
+ std::unique_lock locker{mock_object_copy_request.lock};
+ while (mock_object_copy_request.object_contexts.count(object_num) == 0) {
+ if (mock_object_copy_request.cond.wait_for(locker, 10s) ==
+ std::cv_status::timeout) {
+ return false;
+ }
+ }
+
+ if (object_ctx != nullptr) {
+ *object_ctx = mock_object_copy_request.object_contexts[object_num];
+ } else {
+ m_work_queue->queue(mock_object_copy_request.object_contexts[object_num],
+ r);
+ }
+ return true;
+ }
+
+ SnapMap wait_for_snap_map(MockObjectCopyRequest &mock_object_copy_request) {
+ std::unique_lock locker{mock_object_copy_request.lock};
+ while (mock_object_copy_request.snap_map == nullptr) {
+ if (mock_object_copy_request.cond.wait_for(locker, 10s) ==
+ std::cv_status::timeout) {
+ return SnapMap();
+ }
+ }
+ return *mock_object_copy_request.snap_map;
+ }
+
+ int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
+ librados::snap_t *snap_id) {
+ NoOpProgressContext prog_ctx;
+ int r = image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace(), snap_name, 0, prog_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ r = image_ctx->state->refresh();
+ if (r < 0) {
+ return r;
+ }
+
+ if (image_ctx->snap_ids.count({cls::rbd::UserSnapshotNamespace(),
+ snap_name}) == 0) {
+ return -ENOENT;
+ }
+
+ if (snap_id != nullptr) {
+ *snap_id = image_ctx->snap_ids[{cls::rbd::UserSnapshotNamespace(),
+ snap_name}];
+ }
+ return 0;
+ }
+
+ int create_snap(const char* snap_name,
+ librados::snap_t *src_snap_id_ = nullptr) {
+ librados::snap_t src_snap_id;
+ int r = create_snap(m_src_image_ctx, snap_name, &src_snap_id);
+ if (r < 0) {
+ return r;
+ }
+
+ if (src_snap_id_ != nullptr) {
+ *src_snap_id_ = src_snap_id;
+ }
+
+ librados::snap_t dst_snap_id;
+ r = create_snap(m_dst_image_ctx, snap_name, &dst_snap_id);
+ if (r < 0) {
+ return r;
+ }
+
+ // collection of all existing snaps in dst image
+ SnapIds dst_snap_ids({dst_snap_id});
+ if (!m_snap_map.empty()) {
+ dst_snap_ids.insert(dst_snap_ids.end(),
+ m_snap_map.rbegin()->second.begin(),
+ m_snap_map.rbegin()->second.end());
+ }
+ m_snap_map[src_snap_id] = dst_snap_ids;
+ m_snap_seqs[src_snap_id] = dst_snap_id;
+ return 0;
+ }
+};
+
+TEST_F(TestMockDeepCopyImageCopyRequest, SimpleImage) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockObjectCopyRequest mock_object_copy_request;
+
+ InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
+ expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ expect_object_copy_send(mock_object_copy_request, 0);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+
+ ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffNonExistent) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ InSequence seq;
+
+ MockDiffRequest mock_diff_request;
+ BitVector<2> diff_state;
+ diff_state.resize(1);
+ expect_diff_send(mock_diff_request, diff_state, 0);
+
+ expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ expect_op_work_queue(mock_src_image_ctx);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffExistsDirty) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ InSequence seq;
+
+ MockDiffRequest mock_diff_request;
+ BitVector<2> diff_state;
+ diff_state.resize(1);
+ diff_state[0] = object_map::DIFF_STATE_DATA_UPDATED;
+ expect_diff_send(mock_diff_request, diff_state, 0);
+
+ expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ MockObjectCopyRequest mock_object_copy_request;
+ expect_object_copy_send(mock_object_copy_request, 0);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+
+ ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffExistsClean) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ InSequence seq;
+
+ MockDiffRequest mock_diff_request;
+ BitVector<2> diff_state;
+ diff_state.resize(1);
+ diff_state[0] = object_map::DIFF_STATE_DATA;
+ expect_diff_send(mock_diff_request, diff_state, 0);
+
+ expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ MockObjectCopyRequest mock_object_copy_request;
+ expect_object_copy_send(mock_object_copy_request,
+ OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+
+ ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffMix) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ uint64_t object_count = 12;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockObjectCopyRequest mock_object_copy_request;
+
+ InSequence seq;
+
+ MockDiffRequest mock_diff_request;
+ BitVector<2> diff_state;
+ diff_state.resize(object_count);
+ diff_state[1] = object_map::DIFF_STATE_DATA_UPDATED;
+ diff_state[2] = object_map::DIFF_STATE_DATA_UPDATED;
+ diff_state[3] = object_map::DIFF_STATE_DATA;
+ diff_state[5] = object_map::DIFF_STATE_DATA_UPDATED;
+ diff_state[8] = object_map::DIFF_STATE_DATA;
+ diff_state[9] = object_map::DIFF_STATE_DATA;
+ diff_state[10] = object_map::DIFF_STATE_DATA_UPDATED;
+ expect_diff_send(mock_diff_request, diff_state, 0);
+
+ expect_get_image_size(mock_src_image_ctx,
+ object_count * (1 << m_src_image_ctx->order));
+ expect_get_image_size(mock_src_image_ctx, 0);
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_object_copy_send(mock_object_copy_request, 0);
+ expect_object_copy_send(mock_object_copy_request, 0);
+ expect_object_copy_send(mock_object_copy_request,
+ OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN);
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_object_copy_send(mock_object_copy_request, 0);
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_object_copy_send(mock_object_copy_request,
+ OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN);
+ expect_object_copy_send(mock_object_copy_request,
+ OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN);
+ expect_object_copy_send(mock_object_copy_request, 0);
+ expect_op_work_queue(mock_src_image_ctx);
+
+ std::vector<bool> seen(object_count);
+ struct Handler : public librbd::deep_copy::NoOpHandler {
+ Handler(std::vector<bool>* seen) : m_seen(seen) {}
+
+ int update_progress(uint64_t object_no, uint64_t end_object_no) override {
+ EXPECT_THAT(object_no, ::testing::AllOf(::testing::Ge(1),
+ ::testing::Le(m_seen->size())));
+ EXPECT_EQ(end_object_no, m_seen->size());
+ EXPECT_FALSE((*m_seen)[object_no - 1]);
+ (*m_seen)[object_no - 1] = true;
+ return 0;
+ }
+
+ std::vector<bool>* m_seen;
+ } handler(&seen);
+
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &handler, &ctx);
+ request->send();
+
+ ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 2, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 3, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 5, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 8, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 9, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 10, nullptr, 0));
+ ASSERT_EQ(0, ctx.wait());
+
+ EXPECT_THAT(seen, ::testing::Each(::testing::IsTrue()));
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, OutOfOrder) {
+ std::string max_ops_str;
+ ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
+ ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "10"));
+ BOOST_SCOPE_EXIT( (max_ops_str) ) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
+ max_ops_str.c_str()));
+ } BOOST_SCOPE_EXIT_END;
+
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ uint64_t object_count = 55;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockObjectCopyRequest mock_object_copy_request;
+
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
+ expect_get_image_size(mock_src_image_ctx,
+ object_count * (1 << m_src_image_ctx->order));
+ expect_get_image_size(mock_src_image_ctx, 0);
+
+ EXPECT_CALL(mock_object_copy_request, send()).Times(object_count);
+
+ class Handler : public librbd::deep_copy::NoOpHandler {
+ public:
+ uint64_t object_count;
+ librbd::deep_copy::ObjectNumber expected_object_number;
+
+ Handler(uint64_t object_count)
+ : object_count(object_count) {
+ }
+
+ int update_progress(uint64_t object_no, uint64_t end_object_no) override {
+ EXPECT_LE(object_no, object_count);
+ EXPECT_EQ(end_object_no, object_count);
+ if (!expected_object_number) {
+ expected_object_number = 0;
+ } else {
+ expected_object_number = *expected_object_number + 1;
+ }
+ EXPECT_EQ(*expected_object_number, object_no - 1);
+
+ return 0;
+ }
+ } handler(object_count);
+
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &handler, &ctx);
+ request->send();
+
+ std::map<uint64_t, Context*> copy_contexts;
+ ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
+ for (uint64_t i = 0; i < object_count; ++i) {
+ if (i % 10 == 0) {
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i,
+ &copy_contexts[i], 0));
+ } else {
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i, nullptr,
+ 0));
+ }
+ }
+
+ for (auto& pair : copy_contexts) {
+ pair.second->complete(0);
+ }
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, SnapshotSubset) {
+ librados::snap_t snap_id_start;
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("snap1"));
+ ASSERT_EQ(0, create_snap("snap2", &snap_id_start));
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockObjectCopyRequest mock_object_copy_request;
+
+ InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
+ expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ expect_object_copy_send(mock_object_copy_request, 0);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ snap_id_start, snap_id_end, 0, false,
+ boost::none, m_snap_seqs, &no_op,
+ &ctx);
+ request->send();
+
+ SnapMap snap_map(m_snap_map);
+ snap_map.erase(snap_map.begin());
+ ASSERT_EQ(snap_map, wait_for_snap_map(mock_object_copy_request));
+
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, RestartPartialSync) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockObjectCopyRequest mock_object_copy_request;
+
+ InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
+ expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order));
+ expect_get_image_size(mock_src_image_ctx, 0);
+ expect_object_copy_send(mock_object_copy_request, 0);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false,
+ librbd::deep_copy::ObjectNumber{0U},
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0));
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, Cancel) {
+ std::string max_ops_str;
+ ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
+ ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "1"));
+ BOOST_SCOPE_EXIT( (max_ops_str) ) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
+ max_ops_str.c_str()));
+ } BOOST_SCOPE_EXIT_END;
+
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockObjectCopyRequest mock_object_copy_request;
+
+ InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
+ expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
+ expect_get_image_size(mock_src_image_ctx, 0);
+ expect_object_copy_send(mock_object_copy_request, 0);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+
+ ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
+ request->cancel();
+
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, Cancel_Inflight_Sync) {
+ std::string max_ops_str;
+ ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
+ ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "3"));
+ BOOST_SCOPE_EXIT( (max_ops_str) ) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
+ max_ops_str.c_str()));
+ } BOOST_SCOPE_EXIT_END;
+
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockObjectCopyRequest mock_object_copy_request;
+
+ InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
+ expect_get_image_size(mock_src_image_ctx, 6 * (1 << m_src_image_ctx->order));
+ expect_get_image_size(mock_src_image_ctx, m_image_size);
+
+ EXPECT_CALL(mock_object_copy_request, send()).Times(6);
+
+ struct Handler : public librbd::deep_copy::NoOpHandler {
+ librbd::deep_copy::ObjectNumber object_number;
+
+ int update_progress(uint64_t object_no, uint64_t end_object_no) override {
+ object_number = object_number ? *object_number + 1 : 0;
+ return 0;
+ }
+ } handler;
+
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &handler, &ctx);
+ request->send();
+
+ ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
+
+ Context *cancel_ctx = nullptr;
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 2, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 3, &cancel_ctx,
+ 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 4, nullptr, 0));
+ ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 5, nullptr, 0));
+
+ request->cancel();
+ cancel_ctx->complete(0);
+
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+ ASSERT_EQ(5u, handler.object_number.get());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, CancelBeforeSend) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ InSequence seq;
+
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
+ expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order));
+ expect_get_image_size(mock_src_image_ctx, 0);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->cancel();
+ request->send();
+
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, MissingSnap) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, 123, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, MissingFromSnap) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 123, snap_id_end, 0, false,
+ boost::none, m_snap_seqs, &no_op,
+ &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapMap) {
+ librados::snap_t snap_id_start;
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("snap1", &snap_id_start));
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ snap_id_start, snap_id_end, 0, false,
+ boost::none, {{0, 0}}, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapSeqs) {
+ librados::snap_t snap_id_start;
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("snap1", &snap_id_start));
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::deep_copy::NoOpHandler no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ snap_id_start, snap_id_end, 0, false,
+ boost::none, {}, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace deep_copy
+} // namespace librbd
diff --git a/src/test/librbd/deep_copy/test_mock_MetadataCopyRequest.cc b/src/test/librbd/deep_copy/test_mock_MetadataCopyRequest.cc
new file mode 100644
index 000000000..ba59e3cdb
--- /dev/null
+++ b/src/test/librbd/deep_copy/test_mock_MetadataCopyRequest.cc
@@ -0,0 +1,220 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "include/stringify.h"
+#include "librbd/AsioEngine.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/deep_copy/MetadataCopyRequest.h"
+#include "librbd/image/GetMetadataRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/test_support.h"
+#include <map>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct GetMetadataRequest<MockTestImageCtx> {
+ std::map<std::string, bufferlist>* pairs = nullptr;
+ Context* on_finish = nullptr;
+
+ static GetMetadataRequest* s_instance;
+ static GetMetadataRequest* create(librados::IoCtx&,
+ const std::string& oid,
+ bool filter_internal,
+ const std::string& filter_key_prefix,
+ const std::string& last_key,
+ uint32_t max_results,
+ std::map<std::string, bufferlist>* pairs,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->pairs = pairs;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetMetadataRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+GetMetadataRequest<MockTestImageCtx>* GetMetadataRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namspace image
+} // namespace librbd
+
+// template definitions
+#include "librbd/deep_copy/MetadataCopyRequest.cc"
+
+namespace librbd {
+namespace deep_copy {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockDeepCopyMetadataCopyRequest : public TestMockFixture {
+public:
+ typedef MetadataCopyRequest<librbd::MockTestImageCtx> MockMetadataCopyRequest;
+ typedef image::GetMetadataRequest<MockTestImageCtx> MockGetMetadataRequest;
+ typedef std::map<std::string, bufferlist> Metadata;
+
+ librbd::ImageCtx *m_src_image_ctx;
+ librbd::ImageCtx *m_dst_image_ctx;
+
+ std::shared_ptr<librbd::AsioEngine> m_asio_engine;
+ asio::ContextWQ *m_work_queue;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
+
+ librbd::RBD rbd;
+ std::string dst_image_name = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx));
+
+ m_asio_engine = std::make_shared<librbd::AsioEngine>(
+ m_src_image_ctx->md_ctx);
+ m_work_queue = m_asio_engine->get_work_queue();
+ }
+
+ void expect_get_metadata(MockGetMetadataRequest& mock_request,
+ const Metadata& metadata, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &mock_request, metadata, r]() {
+ *mock_request.pairs = metadata;
+ m_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_metadata_set(librbd::MockTestImageCtx &mock_image_ctx,
+ const Metadata& metadata, int r) {
+ bufferlist in_bl;
+ encode(metadata, in_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("metadata_set"), ContentsEqual(in_bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+};
+
+TEST_F(TestMockDeepCopyMetadataCopyRequest, Success) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ size_t idx = 1;
+ Metadata key_values_1;
+ for (; idx <= 128; ++idx) {
+ bufferlist bl;
+ bl.append("value" + stringify(idx));
+ key_values_1.emplace("key" + stringify(idx), bl);
+ }
+
+ Metadata key_values_2;
+ for (; idx <= 255; ++idx) {
+ bufferlist bl;
+ bl.append("value" + stringify(idx));
+ key_values_2.emplace("key" + stringify(idx), bl);
+ }
+
+ InSequence seq;
+ MockGetMetadataRequest mock_request;
+ expect_get_metadata(mock_request, key_values_1, 0);
+ expect_metadata_set(mock_dst_image_ctx, key_values_1, 0);
+ expect_get_metadata(mock_request, key_values_2, 0);
+ expect_metadata_set(mock_dst_image_ctx, key_values_2, 0);
+
+ C_SaferCond ctx;
+ auto request = MockMetadataCopyRequest::create(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ &ctx);
+ request->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyMetadataCopyRequest, Empty) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ Metadata key_values;
+
+ InSequence seq;
+ MockGetMetadataRequest mock_request;
+ expect_get_metadata(mock_request, key_values, 0);
+
+ C_SaferCond ctx;
+ auto request = MockMetadataCopyRequest::create(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ &ctx);
+ request->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyMetadataCopyRequest, MetadataListError) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ Metadata key_values;
+
+ InSequence seq;
+ MockGetMetadataRequest mock_request;
+ expect_get_metadata(mock_request, key_values, -EINVAL);
+
+ C_SaferCond ctx;
+ auto request = MockMetadataCopyRequest::create(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ &ctx);
+ request->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyMetadataCopyRequest, MetadataSetError) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ Metadata key_values;
+ bufferlist bl;
+ bl.append("value");
+ key_values.emplace("key", bl);
+
+ InSequence seq;
+ MockGetMetadataRequest mock_request;
+ expect_get_metadata(mock_request, key_values, 0);
+ expect_metadata_set(mock_dst_image_ctx, key_values, -EINVAL);
+
+ C_SaferCond ctx;
+ auto request = MockMetadataCopyRequest::create(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ &ctx);
+ request->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace deep_sync
+} // namespace librbd
diff --git a/src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc b/src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc
new file mode 100644
index 000000000..d813a5a33
--- /dev/null
+++ b/src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc
@@ -0,0 +1,1108 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "include/interval_set.h"
+#include "include/neorados/RADOS.hpp"
+#include "include/rbd/librbd.hpp"
+#include "include/rbd/object_map_types.h"
+#include "librbd/AsioEngine.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Image.h"
+#include "librbd/api/Io.h"
+#include "librbd/deep_copy/ObjectCopyRequest.h"
+#include "librbd/deep_copy/Utils.h"
+#include "librbd/io/ReadResult.h"
+#include "librbd/io/Utils.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/test_support.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+
+ MockTestImageCtx *parent = nullptr;
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx* get_image_ctx(MockTestImageCtx* image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace io {
+namespace util {
+
+template <>
+void area_to_object_extents(MockTestImageCtx* image_ctx, uint64_t offset,
+ uint64_t length, ImageArea area,
+ uint64_t buffer_offset,
+ striper::LightweightObjectExtents* object_extents) {
+ Striper::file_to_extents(image_ctx->cct, &image_ctx->layout, offset, length,
+ 0, buffer_offset, object_extents);
+}
+
+template <>
+std::pair<Extents, ImageArea> object_to_area_extents(
+ MockTestImageCtx* image_ctx, uint64_t object_no,
+ const Extents& object_extents) {
+ Extents extents;
+ for (auto [off, len] : object_extents) {
+ Striper::extent_to_file(image_ctx->cct, &image_ctx->layout, object_no, off,
+ len, extents);
+ }
+ return {std::move(extents), ImageArea::DATA};
+}
+
+} // namespace util
+} // namespace io
+
+} // namespace librbd
+
+// template definitions
+#include "librbd/deep_copy/ObjectCopyRequest.cc"
+template class librbd::deep_copy::ObjectCopyRequest<librbd::MockTestImageCtx>;
+
+static bool operator==(const SnapContext& rhs, const SnapContext& lhs) {
+ return (rhs.seq == lhs.seq && rhs.snaps == lhs.snaps);
+}
+
+namespace librbd {
+namespace deep_copy {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnNew;
+using ::testing::WithArg;
+
+namespace {
+
+void scribble(librbd::ImageCtx *image_ctx, int num_ops, size_t max_size,
+ interval_set<uint64_t> *what)
+{
+ uint64_t object_size = 1 << image_ctx->order;
+ for (int i = 0; i < num_ops; i++) {
+ uint64_t off = rand() % (object_size - max_size + 1);
+ uint64_t len = 1 + rand() % max_size;
+ std::cout << __func__ << ": off=" << off << ", len=" << len << std::endl;
+
+ bufferlist bl;
+ bl.append(std::string(len, '1'));
+
+ int r = api::Io<>::write(*image_ctx, off, len, std::move(bl), 0);
+ ASSERT_EQ(static_cast<int>(len), r);
+
+ interval_set<uint64_t> w;
+ w.insert(off, len);
+ what->union_of(w);
+ }
+ std::cout << " wrote " << *what << std::endl;
+}
+
+} // anonymous namespace
+
+
+MATCHER(IsListSnaps, "") {
+ auto req = boost::get<io::ImageDispatchSpec::ListSnaps>(&arg->request);
+ return (req != nullptr);
+}
+
+MATCHER_P2(IsRead, snap_id, image_interval, "") {
+ auto req = boost::get<io::ImageDispatchSpec::Read>(&arg->request);
+ if (req == nullptr ||
+ arg->io_context->read_snap().value_or(CEPH_NOSNAP) != snap_id) {
+ return false;
+ }
+
+ // ensure the read request encloses the full snapshot delta
+ interval_set<uint64_t> expected_interval(image_interval);
+ interval_set<uint64_t> read_interval;
+ for (auto &image_extent : arg->image_extents) {
+ read_interval.insert(image_extent.first, image_extent.second);
+ }
+
+ interval_set<uint64_t> intersection;
+ intersection.intersection_of(expected_interval, read_interval);
+ expected_interval.subtract(intersection);
+ return expected_interval.empty();
+}
+
+class TestMockDeepCopyObjectCopyRequest : public TestMockFixture {
+public:
+ typedef ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest;
+
+ librbd::ImageCtx *m_src_image_ctx;
+ librbd::ImageCtx *m_dst_image_ctx;
+
+ std::shared_ptr<librbd::AsioEngine> m_asio_engine;
+ asio::ContextWQ *m_work_queue;
+
+ SnapMap m_snap_map;
+ SnapSeqs m_snap_seqs;
+ std::vector<librados::snap_t> m_src_snap_ids;
+ std::vector<librados::snap_t> m_dst_snap_ids;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
+
+ librbd::NoOpProgressContext no_op;
+ m_image_size = 1 << m_src_image_ctx->order;
+ ASSERT_EQ(0, m_src_image_ctx->operations->resize(m_image_size, true, no_op));
+
+ librbd::RBD rbd;
+ std::string dst_image_name = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx));
+
+ m_asio_engine = std::make_shared<librbd::AsioEngine>(
+ m_src_image_ctx->md_ctx);
+ m_work_queue = m_asio_engine->get_work_queue();
+ }
+
+ bool is_fast_diff(librbd::MockImageCtx &mock_image_ctx) {
+ return (mock_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0;
+ }
+
+ void prepare_exclusive_lock(librbd::MockImageCtx &mock_image_ctx,
+ librbd::MockExclusiveLock &mock_exclusive_lock) {
+ if ((mock_image_ctx.features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
+ return;
+ }
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ void expect_get_object_count(librbd::MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, get_object_count(_))
+ .WillRepeatedly(Invoke([&mock_image_ctx](librados::snap_t snap_id) {
+ return mock_image_ctx.image_ctx->get_object_count(snap_id);
+ }));
+ }
+
+ void expect_test_features(librbd::MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_))
+ .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) {
+ return (mock_image_ctx.features & features) != 0;
+ })));
+ }
+
+ void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
+ if ((m_src_image_ctx->features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
+ return;
+ }
+ EXPECT_CALL(mock_exclusive_lock, start_op(_)).WillOnce(Return(new LambdaContext([](int){})));
+ }
+
+ void expect_list_snaps(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, send(IsListSnaps()))
+ .WillOnce(Invoke(
+ [&mock_image_ctx, r](io::ImageDispatchSpec* spec) {
+ if (r < 0) {
+ spec->fail(r);
+ return;
+ }
+
+ spec->image_dispatcher =
+ mock_image_ctx.image_ctx->io_image_dispatcher;
+ mock_image_ctx.image_ctx->io_image_dispatcher->send(spec);
+ }));
+ }
+
+ void expect_get_object_name(librbd::MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, get_object_name(0))
+ .WillOnce(Return(mock_image_ctx.image_ctx->get_object_name(0)));
+ }
+
+ MockObjectCopyRequest *create_request(
+ librbd::MockTestImageCtx &mock_src_image_ctx,
+ librbd::MockTestImageCtx &mock_dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ uint32_t flags, Context *on_finish) {
+ SnapMap snap_map;
+ util::compute_snap_map(mock_dst_image_ctx.cct, src_snap_id_start,
+ src_snap_id_end, m_dst_snap_ids, m_snap_seqs,
+ &snap_map);
+
+ expect_get_object_name(mock_dst_image_ctx);
+ return new MockObjectCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx,
+ src_snap_id_start, dst_snap_id_start,
+ snap_map, 0, flags, nullptr, on_finish);
+ }
+
+ void expect_read(librbd::MockTestImageCtx& mock_image_ctx,
+ uint64_t snap_id, uint64_t offset, uint64_t length, int r) {
+ interval_set<uint64_t> extents;
+ extents.insert(offset, length);
+ expect_read(mock_image_ctx, snap_id, extents, r);
+ }
+
+ void expect_read(librbd::MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
+ const interval_set<uint64_t> &extents, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher,
+ send(IsRead(snap_id, extents)))
+ .WillOnce(Invoke(
+ [&mock_image_ctx, r](io::ImageDispatchSpec* spec) {
+ if (r < 0) {
+ spec->fail(r);
+ return;
+ }
+
+ spec->image_dispatcher =
+ mock_image_ctx.image_ctx->io_image_dispatcher;
+ mock_image_ctx.image_ctx->io_image_dispatcher->send(spec);
+ }));
+ }
+
+ void expect_write(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ uint64_t offset, uint64_t length,
+ const SnapContext &snapc, int r) {
+ auto &expect = EXPECT_CALL(mock_io_ctx, write(_, _, length, offset, snapc));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_write(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ const interval_set<uint64_t> &extents,
+ const SnapContext &snapc, int r) {
+ for (auto extent : extents) {
+ expect_write(mock_io_ctx, extent.first, extent.second, snapc, r);
+ if (r < 0) {
+ break;
+ }
+ }
+ }
+
+ void expect_truncate(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ uint64_t offset, int r) {
+ auto &expect = EXPECT_CALL(mock_io_ctx, truncate(_, offset, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_remove(librados::MockTestMemIoCtxImpl &mock_io_ctx, int r) {
+ auto &expect = EXPECT_CALL(mock_io_ctx, remove(_, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_update_object_map(librbd::MockTestImageCtx &mock_image_ctx,
+ librbd::MockObjectMap &mock_object_map,
+ librados::snap_t snap_id, uint8_t state,
+ int r) {
+ if (mock_image_ctx.image_ctx->object_map != nullptr) {
+ auto &expect = EXPECT_CALL(mock_object_map, aio_update(snap_id, 0, 1, state, _, _, false, _));
+ if (r < 0) {
+ expect.WillOnce(DoAll(WithArg<7>(Invoke([this, r](Context *ctx) {
+ m_work_queue->queue(ctx, r);
+ })),
+ Return(true)));
+ } else {
+ expect.WillOnce(DoAll(WithArg<7>(Invoke([&mock_image_ctx, snap_id, state](Context *ctx) {
+ ceph_assert(ceph_mutex_is_locked(mock_image_ctx.image_ctx->image_lock));
+ mock_image_ctx.image_ctx->object_map->aio_update<Context>(
+ snap_id, 0, 1, state, boost::none, {}, false, ctx);
+ })),
+ Return(true)));
+ }
+ }
+ }
+
+ void expect_prepare_copyup(MockTestImageCtx& mock_image_ctx, int r = 0) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
+ prepare_copyup(_, _)).WillOnce(Return(r));
+ }
+
+ int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
+ librados::snap_t *snap_id) {
+ NoOpProgressContext prog_ctx;
+ int r = image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace(), snap_name, 0, prog_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ r = image_ctx->state->refresh();
+ if (r < 0) {
+ return r;
+ }
+
+ if (image_ctx->snap_ids.count({cls::rbd::UserSnapshotNamespace(),
+ snap_name}) == 0) {
+ return -ENOENT;
+ }
+
+ if (snap_id != nullptr) {
+ *snap_id = image_ctx->snap_ids[{cls::rbd::UserSnapshotNamespace(),
+ snap_name}];
+ }
+ return 0;
+ }
+
+ int create_snap(const char* snap_name) {
+ librados::snap_t src_snap_id;
+ int r = create_snap(m_src_image_ctx, snap_name, &src_snap_id);
+ if (r < 0) {
+ return r;
+ }
+
+ librados::snap_t dst_snap_id;
+ r = create_snap(m_dst_image_ctx, snap_name, &dst_snap_id);
+ if (r < 0) {
+ return r;
+ }
+
+ // collection of all existing snaps in dst image
+ SnapIds dst_snap_ids({dst_snap_id});
+ if (!m_snap_map.empty()) {
+ dst_snap_ids.insert(dst_snap_ids.end(),
+ m_snap_map.rbegin()->second.begin(),
+ m_snap_map.rbegin()->second.end());
+ }
+ m_snap_map[src_snap_id] = dst_snap_ids;
+ m_snap_seqs[src_snap_id] = dst_snap_id;
+ m_src_snap_ids.push_back(src_snap_id);
+ m_dst_snap_ids.push_back(dst_snap_id);
+
+ return 0;
+ }
+
+ std::string get_snap_name(librbd::ImageCtx *image_ctx,
+ librados::snap_t snap_id) {
+ auto it = std::find_if(image_ctx->snap_ids.begin(),
+ image_ctx->snap_ids.end(),
+ [snap_id](const std::pair<std::pair<cls::rbd::SnapshotNamespace,
+ std::string>,
+ librados::snap_t> &pair) {
+ return (pair.second == snap_id);
+ });
+ if (it == image_ctx->snap_ids.end()) {
+ return "";
+ }
+ return it->first.second;
+ }
+
+ int copy_objects() {
+ int r;
+ uint64_t object_size = 1 << m_src_image_ctx->order;
+
+ bufferlist bl;
+ bl.append(std::string(object_size, '1'));
+ r = api::Io<>::read(*m_src_image_ctx, 0, object_size,
+ librbd::io::ReadResult{&bl}, 0);
+ if (r < 0) {
+ return r;
+ }
+
+ r = api::Io<>::write(*m_dst_image_ctx, 0, object_size, std::move(bl), 0);
+ if (r < 0) {
+ return r;
+ }
+
+ return 0;
+ }
+
+ int compare_objects() {
+ SnapMap snap_map(m_snap_map);
+ if (snap_map.empty()) {
+ return -ENOENT;
+ }
+
+ int r;
+ uint64_t object_size = 1 << m_src_image_ctx->order;
+ while (!snap_map.empty()) {
+ librados::snap_t src_snap_id = snap_map.begin()->first;
+ librados::snap_t dst_snap_id = *snap_map.begin()->second.begin();
+ snap_map.erase(snap_map.begin());
+
+ std::string snap_name = get_snap_name(m_src_image_ctx, src_snap_id);
+ if (snap_name.empty()) {
+ return -ENOENT;
+ }
+
+ std::cout << "comparing '" << snap_name << " (" << src_snap_id
+ << " to " << dst_snap_id << ")" << std::endl;
+
+ r = librbd::api::Image<>::snap_set(m_src_image_ctx,
+ cls::rbd::UserSnapshotNamespace(),
+ snap_name.c_str());
+ if (r < 0) {
+ return r;
+ }
+
+ r = librbd::api::Image<>::snap_set(m_dst_image_ctx,
+ cls::rbd::UserSnapshotNamespace(),
+ snap_name.c_str());
+ if (r < 0) {
+ return r;
+ }
+
+ bufferlist src_bl;
+ src_bl.append(std::string(object_size, '1'));
+ r = api::Io<>::read(
+ *m_src_image_ctx, 0, object_size, librbd::io::ReadResult{&src_bl}, 0);
+ if (r < 0) {
+ return r;
+ }
+
+ bufferlist dst_bl;
+ dst_bl.append(std::string(object_size, '1'));
+ r = api::Io<>::read(
+ *m_dst_image_ctx, 0, object_size, librbd::io::ReadResult{&dst_bl}, 0);
+ if (r < 0) {
+ return r;
+ }
+
+ if (!src_bl.contents_equal(dst_bl)) {
+ std::cout << "src block: " << std::endl; src_bl.hexdump(std::cout);
+ std::cout << "dst block: " << std::endl; dst_bl.hexdump(std::cout);
+ return -EBADMSG;
+ }
+ }
+
+ r = librbd::api::Image<>::snap_set(m_src_image_ctx,
+ cls::rbd::UserSnapshotNamespace(),
+ nullptr);
+ if (r < 0) {
+ return r;
+ }
+ r = librbd::api::Image<>::snap_set(m_dst_image_ctx,
+ cls::rbd::UserSnapshotNamespace(),
+ nullptr);
+ if (r < 0) {
+ return r;
+ }
+
+ return 0;
+ }
+};
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, DNE) {
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, -ENOENT);
+
+ request->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, Write) {
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
+ request->get_dst_io_ctx()));
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], OBJECT_EXISTS, 0);
+ expect_prepare_copyup(mock_dst_image_ctx);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
+
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, ReadError) {
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(),
+ -EINVAL);
+
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, WriteError) {
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
+ request->get_dst_io_ctx()));
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
+
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], OBJECT_EXISTS, 0);
+
+ expect_prepare_copyup(mock_dst_image_ctx);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, -EINVAL);
+
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, WriteSnaps) {
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+ ASSERT_EQ(0, create_snap("one"));
+
+ interval_set<uint64_t> two;
+ scribble(m_src_image_ctx, 10, 102400, &two);
+ ASSERT_EQ(0, create_snap("two"));
+
+ if (one.range_end() < two.range_end()) {
+ interval_set<uint64_t> resize_diff;
+ resize_diff.insert(one.range_end(), two.range_end() - one.range_end());
+ two.union_of(resize_diff);
+ }
+
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
+ request->get_dst_io_ctx()));
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[2], two, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], OBJECT_EXISTS, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[1], OBJECT_EXISTS, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[2], is_fast_diff(mock_dst_image_ctx) ?
+ OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
+ expect_prepare_copyup(mock_dst_image_ctx);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, two,
+ {m_dst_snap_ids[0], {m_dst_snap_ids[0]}}, 0);
+
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, Trim) {
+ ASSERT_EQ(0, m_src_image_ctx->operations->metadata_set(
+ "conf_rbd_skip_partial_discard", "false"));
+ m_src_image_ctx->discard_granularity_bytes = 0;
+
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+ ASSERT_EQ(0, create_snap("one"));
+
+ // trim the object
+ uint64_t trim_offset = rand() % one.range_end();
+ ASSERT_LE(0, api::Io<>::discard(
+ *m_src_image_ctx, trim_offset, one.range_end() - trim_offset,
+ m_src_image_ctx->discard_granularity_bytes));
+ ASSERT_EQ(0, create_snap("copy"));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
+ request->get_dst_io_ctx()));
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], OBJECT_EXISTS, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[1], OBJECT_EXISTS, 0);
+ expect_prepare_copyup(mock_dst_image_ctx);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_truncate(mock_dst_io_ctx, trim_offset, 0);
+
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, Remove) {
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+ ASSERT_EQ(0, create_snap("one"));
+ ASSERT_EQ(0, create_snap("two"));
+
+ // remove the object
+ uint64_t object_size = 1 << m_src_image_ctx->order;
+ ASSERT_LE(0, api::Io<>::discard(
+ *m_src_image_ctx, 0, object_size,
+ m_src_image_ctx->discard_granularity_bytes));
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
+ request->get_dst_io_ctx()));
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[1], 0, one.range_end(), 0);
+
+ expect_start_op(mock_exclusive_lock);
+ uint8_t state = OBJECT_EXISTS;
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], state, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[1], is_fast_diff(mock_dst_image_ctx) ?
+ OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
+
+ expect_prepare_copyup(mock_dst_image_ctx);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_remove(mock_dst_io_ctx, 0);
+
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, ObjectMapUpdateError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], OBJECT_EXISTS, -EBLOCKLISTED);
+
+ request->send();
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, PrepareCopyupError) {
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+
+ ASSERT_EQ(0, create_snap("copy"));
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, 0, &ctx);
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
+
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], OBJECT_EXISTS, 0);
+
+ expect_prepare_copyup(mock_dst_image_ctx, -EIO);
+
+ request->send();
+ ASSERT_EQ(-EIO, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, WriteSnapsStart) {
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+ ASSERT_EQ(0, copy_objects());
+ ASSERT_EQ(0, create_snap("one"));
+
+ auto src_snap_id_start = m_src_image_ctx->snaps[0];
+ auto dst_snap_id_start = m_dst_image_ctx->snaps[0];
+
+ interval_set<uint64_t> two;
+ scribble(m_src_image_ctx, 10, 102400, &two);
+ ASSERT_EQ(0, create_snap("two"));
+
+ interval_set<uint64_t> three;
+ scribble(m_src_image_ctx, 10, 102400, &three);
+ ASSERT_EQ(0, create_snap("three"));
+
+ auto max_extent = one.range_end();
+ if (max_extent < two.range_end()) {
+ interval_set<uint64_t> resize_diff;
+ resize_diff.insert(max_extent, two.range_end() - max_extent);
+ two.union_of(resize_diff);
+ }
+
+ max_extent = std::max(max_extent, two.range_end());
+ if (max_extent < three.range_end()) {
+ interval_set<uint64_t> resize_diff;
+ resize_diff.insert(max_extent, three.range_end() - max_extent);
+ three.union_of(resize_diff);
+ }
+
+ interval_set<uint64_t> four;
+ scribble(m_src_image_ctx, 10, 102400, &four);
+
+ // map should begin after src start and src end's dst snap seqs should
+ // point to HEAD revision
+ m_snap_seqs.rbegin()->second = CEPH_NOSNAP;
+ m_dst_snap_ids.pop_back();
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ C_SaferCond ctx;
+ MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx,
+ src_snap_id_start,
+ CEPH_NOSNAP,
+ dst_snap_id_start,
+ 0, &ctx);
+
+ librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
+ request->get_dst_io_ctx()));
+
+ InSequence seq;
+ expect_list_snaps(mock_src_image_ctx, 0);
+
+ expect_read(mock_src_image_ctx, m_src_snap_ids[1], two, 0);
+ expect_read(mock_src_image_ctx, m_src_snap_ids[2], three, 0);
+
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[1], OBJECT_EXISTS, 0);
+
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ CEPH_NOSNAP, OBJECT_EXISTS, 0);
+
+ expect_prepare_copyup(mock_dst_image_ctx);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, two,
+ {m_dst_snap_ids[0], {m_dst_snap_ids[0]}}, 0);
+
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, three,
+ {m_dst_snap_ids[1], {m_dst_snap_ids[1], m_dst_snap_ids[0]}}, 0);
+
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, Incremental) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ // scribble some data
+ interval_set<uint64_t> one;
+ scribble(m_src_image_ctx, 10, 102400, &one);
+ ASSERT_EQ(0, create_snap("snap1"));
+ mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
+
+ InSequence seq;
+
+ C_SaferCond ctx1;
+ auto request1 = create_request(mock_src_image_ctx, mock_dst_image_ctx,
+ 0, m_src_snap_ids[0], 0, 0, &ctx1);
+
+ expect_list_snaps(mock_src_image_ctx, 0);
+
+ expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
+
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[0], OBJECT_EXISTS, 0);
+
+ librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
+ request1->get_dst_io_ctx()));
+ expect_prepare_copyup(mock_dst_image_ctx);
+ expect_start_op(mock_exclusive_lock);
+ expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
+
+ request1->send();
+ ASSERT_EQ(0, ctx1.wait());
+
+ // clean (no-updates) snapshots
+ ASSERT_EQ(0, create_snap("snap2"));
+ ASSERT_EQ(0, create_snap("snap3"));
+ mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
+
+ C_SaferCond ctx2;
+ auto request2 = create_request(mock_src_image_ctx, mock_dst_image_ctx,
+ m_src_snap_ids[0], m_src_snap_ids[2],
+ m_dst_snap_ids[0], 0, &ctx2);
+
+ expect_list_snaps(mock_src_image_ctx, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[1],
+ is_fast_diff(mock_dst_image_ctx) ?
+ OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[2],
+ is_fast_diff(mock_dst_image_ctx) ?
+ OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
+
+ request2->send();
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockDeepCopyObjectCopyRequest, SkipSnapList) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_src_image_ctx);
+ expect_test_features(mock_dst_image_ctx);
+ expect_get_object_count(mock_dst_image_ctx);
+
+ ASSERT_EQ(0, create_snap("snap1"));
+ mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
+
+ InSequence seq;
+
+ // clean (no-updates) snapshots
+ ASSERT_EQ(0, create_snap("snap2"));
+ mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_src_image_ctx, mock_dst_image_ctx,
+ m_src_snap_ids[0], m_src_snap_ids[1],
+ m_dst_snap_ids[0],
+ OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN, &ctx);
+
+ expect_start_op(mock_exclusive_lock);
+ expect_update_object_map(mock_dst_image_ctx, mock_object_map,
+ m_dst_snap_ids[1],
+ is_fast_diff(mock_dst_image_ctx) ?
+ OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
+
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace deep_copy
+} // namespace librbd
diff --git a/src/test/librbd/deep_copy/test_mock_SetHeadRequest.cc b/src/test/librbd/deep_copy/test_mock_SetHeadRequest.cc
new file mode 100644
index 000000000..209339973
--- /dev/null
+++ b/src/test/librbd/deep_copy/test_mock_SetHeadRequest.cc
@@ -0,0 +1,296 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/AsioEngine.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "osdc/Striper.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "librbd/deep_copy/SetHeadRequest.h"
+#include "librbd/image/AttachParentRequest.h"
+#include "librbd/image/DetachParentRequest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct AttachParentRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static AttachParentRequest* s_instance;
+ static AttachParentRequest* create(MockTestImageCtx&,
+ const cls::rbd::ParentImageSpec& pspec,
+ uint64_t parent_overlap, bool reattach,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ AttachParentRequest() {
+ s_instance = this;
+ }
+};
+
+AttachParentRequest<MockTestImageCtx>* AttachParentRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+class DetachParentRequest<MockTestImageCtx> {
+public:
+ static DetachParentRequest *s_instance;
+ static DetachParentRequest *create(MockTestImageCtx &image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ DetachParentRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+DetachParentRequest<MockTestImageCtx> *DetachParentRequest<MockTestImageCtx>::s_instance;
+
+} // namespace image
+} // namespace librbd
+
+// template definitions
+#include "librbd/deep_copy/SetHeadRequest.cc"
+template class librbd::deep_copy::SetHeadRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace deep_copy {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::ReturnNew;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockDeepCopySetHeadRequest : public TestMockFixture {
+public:
+ typedef SetHeadRequest<librbd::MockTestImageCtx> MockSetHeadRequest;
+ typedef image::AttachParentRequest<MockTestImageCtx> MockAttachParentRequest;
+ typedef image::DetachParentRequest<MockTestImageCtx> MockDetachParentRequest;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ std::shared_ptr<librbd::AsioEngine> m_asio_engine;
+ asio::ContextWQ *m_work_queue;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+
+ m_asio_engine = std::make_shared<librbd::AsioEngine>(
+ m_image_ctx->md_ctx);
+ m_work_queue = m_asio_engine->get_work_queue();
+ }
+
+ void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
+ EXPECT_CALL(mock_exclusive_lock, start_op(_)).WillOnce(Return(new LambdaContext([](int){})));
+ }
+
+ void expect_test_features(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t features, bool enabled) {
+ EXPECT_CALL(mock_image_ctx, test_features(features))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_set_size(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("set_size"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_detach_parent(MockImageCtx &mock_image_ctx,
+ MockDetachParentRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
+ }
+
+ void expect_attach_parent(MockImageCtx &mock_image_ctx,
+ MockAttachParentRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
+ }
+
+ MockSetHeadRequest *create_request(
+ librbd::MockTestImageCtx &mock_local_image_ctx, uint64_t size,
+ const cls::rbd::ParentImageSpec &parent_spec, uint64_t parent_overlap,
+ Context *on_finish) {
+ return new MockSetHeadRequest(&mock_local_image_ctx, size, parent_spec,
+ parent_overlap, on_finish);
+ }
+};
+
+TEST_F(TestMockDeepCopySetHeadRequest, Resize) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ expect_set_size(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, 123, {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySetHeadRequest, ResizeError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ expect_set_size(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, 123, {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySetHeadRequest, RemoveParent) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ mock_image_ctx.parent_md.spec.pool_id = 213;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ MockDetachParentRequest mock_detach_parent;
+ expect_detach_parent(mock_image_ctx, mock_detach_parent, 0);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, m_image_ctx->size, {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySetHeadRequest, RemoveParentError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ mock_image_ctx.parent_md.spec.pool_id = 213;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ MockDetachParentRequest mock_detach_parent;
+ expect_detach_parent(mock_image_ctx, mock_detach_parent, -EINVAL);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, m_image_ctx->size, {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySetHeadRequest, RemoveSetParent) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ mock_image_ctx.parent_md.spec.pool_id = 213;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ MockDetachParentRequest mock_detach_parent;
+ expect_detach_parent(mock_image_ctx, mock_detach_parent, 0);
+ expect_start_op(mock_exclusive_lock);
+ MockAttachParentRequest mock_attach_parent;
+ expect_attach_parent(mock_image_ctx, mock_attach_parent, 0);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, m_image_ctx->size,
+ {123, "", "test", 0}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySetHeadRequest, SetParentSpec) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ MockAttachParentRequest mock_attach_parent;
+ expect_attach_parent(mock_image_ctx, mock_attach_parent, 0);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, m_image_ctx->size,
+ {123, "", "test", 0}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySetHeadRequest, SetParentOverlap) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ mock_image_ctx.parent_md.spec = {123, "", "test", 0};
+ mock_image_ctx.parent_md.overlap = m_image_ctx->size;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ expect_set_size(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, 123,
+ mock_image_ctx.parent_md.spec, 123, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(123U, mock_image_ctx.parent_md.overlap);
+}
+
+TEST_F(TestMockDeepCopySetHeadRequest, SetParentError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ MockAttachParentRequest mock_attach_parent;
+ expect_attach_parent(mock_image_ctx, mock_attach_parent, -ESTALE);
+
+ C_SaferCond ctx;
+ auto request = create_request(mock_image_ctx, m_image_ctx->size,
+ {123, "", "test", 0}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-ESTALE, ctx.wait());
+}
+
+} // namespace deep_copy
+} // namespace librbd
diff --git a/src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc b/src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc
new file mode 100644
index 000000000..0190c338a
--- /dev/null
+++ b/src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc
@@ -0,0 +1,922 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/AsioEngine.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/deep_copy/SetHeadRequest.h"
+#include "librbd/deep_copy/SnapshotCopyRequest.h"
+#include "librbd/deep_copy/SnapshotCreateRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/test_support.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace deep_copy {
+
+template <>
+class SetHeadRequest<librbd::MockTestImageCtx> {
+public:
+ static SetHeadRequest* s_instance;
+ Context *on_finish;
+
+ static SetHeadRequest* create(librbd::MockTestImageCtx *image_ctx,
+ uint64_t size,
+ const cls::rbd::ParentImageSpec &parent_spec,
+ uint64_t parent_overlap, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SetHeadRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct SnapshotCreateRequest<librbd::MockTestImageCtx> {
+ static SnapshotCreateRequest* s_instance;
+ static SnapshotCreateRequest* create(librbd::MockTestImageCtx* image_ctx,
+ const std::string &snap_name,
+ const cls::rbd::SnapshotNamespace &snap_namespace,
+ uint64_t size,
+ const cls::rbd::ParentImageSpec &parent_spec,
+ uint64_t parent_overlap,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ SnapshotCreateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SetHeadRequest<librbd::MockTestImageCtx>* SetHeadRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+SnapshotCreateRequest<librbd::MockTestImageCtx>* SnapshotCreateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace deep_copy
+} // namespace librbd
+
+// template definitions
+#include "librbd/deep_copy/SnapshotCopyRequest.cc"
+template class librbd::deep_copy::SnapshotCopyRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace deep_copy {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::ReturnNew;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockDeepCopySnapshotCopyRequest : public TestMockFixture {
+public:
+ typedef SetHeadRequest<librbd::MockTestImageCtx> MockSetHeadRequest;
+ typedef SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest;
+ typedef SnapshotCreateRequest<librbd::MockTestImageCtx> MockSnapshotCreateRequest;
+
+ librbd::ImageCtx *m_src_image_ctx;
+ librbd::ImageCtx *m_dst_image_ctx;
+
+ std::shared_ptr<librbd::AsioEngine> m_asio_engine;
+ asio::ContextWQ *m_work_queue;
+
+ librbd::SnapSeqs m_snap_seqs;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
+
+ librbd::RBD rbd;
+ std::string dst_image_name = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx));
+
+ m_asio_engine = std::make_shared<librbd::AsioEngine>(
+ m_src_image_ctx->md_ctx);
+ m_work_queue = m_asio_engine->get_work_queue();
+ }
+
+ void prepare_exclusive_lock(librbd::MockImageCtx &mock_image_ctx,
+ librbd::MockExclusiveLock &mock_exclusive_lock) {
+ if ((mock_image_ctx.features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
+ return;
+ }
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ void expect_test_features(librbd::MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_, _))
+ .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) {
+ return (mock_image_ctx.features & features) != 0;
+ })));
+ EXPECT_CALL(mock_image_ctx, test_features(_))
+ .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) {
+ return (mock_image_ctx.features & features) != 0;
+ })));
+ }
+
+ void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
+ if ((m_src_image_ctx->features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
+ return;
+ }
+ EXPECT_CALL(mock_exclusive_lock, start_op(_)).WillOnce(Return(new LambdaContext([](int){})));
+ }
+
+ void expect_get_snap_namespace(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id) {
+ EXPECT_CALL(mock_image_ctx, get_snap_namespace(snap_id, _))
+ .WillOnce(Invoke([&mock_image_ctx](uint64_t snap_id,
+ cls::rbd::SnapshotNamespace* snap_ns) {
+ auto it = mock_image_ctx.snap_info.find(snap_id);
+ *snap_ns = it->second.snap_namespace;
+ return 0;
+ }));
+ }
+
+ void expect_snap_create(librbd::MockTestImageCtx &mock_image_ctx,
+ MockSnapshotCreateRequest &mock_snapshot_create_request,
+ const std::string &snap_name, uint64_t snap_id, int r) {
+ EXPECT_CALL(mock_snapshot_create_request, send())
+ .WillOnce(DoAll(Invoke([&mock_image_ctx, snap_id, snap_name]() {
+ inject_snap(mock_image_ctx, snap_id, snap_name);
+ }),
+ Invoke([this, &mock_snapshot_create_request, r]() {
+ m_work_queue->queue(mock_snapshot_create_request.on_finish, r);
+ })));
+ }
+
+ void expect_snap_remove(librbd::MockTestImageCtx &mock_image_ctx,
+ const std::string &snap_name, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_remove(_, StrEq(snap_name), _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context *ctx) {
+ m_work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_snap_protect(librbd::MockTestImageCtx &mock_image_ctx,
+ const std::string &snap_name, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_protect(_, StrEq(snap_name), _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context *ctx) {
+ m_work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_snap_unprotect(librbd::MockTestImageCtx &mock_image_ctx,
+ const std::string &snap_name, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_unprotect(_, StrEq(snap_name), _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context *ctx) {
+ m_work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_snap_is_protected(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, bool is_protected, int r) {
+ EXPECT_CALL(mock_image_ctx, is_snap_protected(snap_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(is_protected),
+ Return(r)));
+ }
+
+ void expect_snap_is_unprotected(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, bool is_unprotected, int r) {
+ EXPECT_CALL(mock_image_ctx, is_snap_unprotected(snap_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(is_unprotected),
+ Return(r)));
+ }
+
+ void expect_set_head(MockSetHeadRequest &mock_set_head_request, int r) {
+ EXPECT_CALL(mock_set_head_request, send())
+ .WillOnce(Invoke([&mock_set_head_request, r]() {
+ mock_set_head_request.on_finish->complete(r);
+ }));
+ }
+
+ static void inject_snap(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, const std::string &snap_name) {
+ mock_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(),
+ snap_name}] = snap_id;
+ }
+
+ MockSnapshotCopyRequest *create_request(
+ librbd::MockTestImageCtx &mock_src_image_ctx,
+ librbd::MockTestImageCtx &mock_dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ Context *on_finish) {
+ return new MockSnapshotCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx,
+ src_snap_id_start, src_snap_id_end,
+ dst_snap_id_start, false, m_work_queue,
+ &m_snap_seqs, on_finish);
+ }
+
+ int create_snap(librbd::ImageCtx *image_ctx,
+ const cls::rbd::SnapshotNamespace& snap_ns,
+ const std::string &snap_name, bool protect) {
+ NoOpProgressContext prog_ctx;
+ int r = image_ctx->operations->snap_create(snap_ns, snap_name.c_str(), 0,
+ prog_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ if (protect) {
+ EXPECT_TRUE(std::holds_alternative<cls::rbd::UserSnapshotNamespace>(snap_ns));
+ r = image_ctx->operations->snap_protect(snap_ns, snap_name.c_str());
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ r = image_ctx->state->refresh();
+ if (r < 0) {
+ return r;
+ }
+ return 0;
+ }
+
+ int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name,
+ bool protect = false) {
+ return create_snap(image_ctx, cls::rbd::UserSnapshotNamespace{}, snap_name,
+ protect);
+ }
+
+ void validate_snap_seqs(const librbd::SnapSeqs &snap_seqs) {
+ ASSERT_EQ(snap_seqs, m_snap_seqs);
+ }
+};
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, Empty) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreate) {
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1"));
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap2"));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t src_snap_id2 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap2"}];
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", 12, 0);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id2);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap2", 14, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id2, false, 0);
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id1, 12}, {src_snap_id2, 14}});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateError) {
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1"));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ uint64_t src_snap_id1 = mock_src_image_ctx.snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1",
+ 12, -EINVAL);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateCancel) {
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1"));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_start_op(mock_exclusive_lock);
+ EXPECT_CALL(mock_snapshot_create_request, send())
+ .WillOnce(DoAll(InvokeWithoutArgs([request]() {
+ request->cancel();
+ }),
+ Invoke([this, &mock_snapshot_create_request]() {
+ m_work_queue->queue(mock_snapshot_create_request.on_finish, 0);
+ })));
+
+ request->send();
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapRemoveAndCreate) {
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1"));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1"));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx,
+ m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}],
+ true, 0);
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_remove(mock_dst_image_ctx, "snap1", 0);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", 12, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0);
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id1, 12}});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapRemoveError) {
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1"));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx,
+ m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}],
+ true, 0);
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_remove(mock_dst_image_ctx, "snap1", -EINVAL);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotect) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ m_snap_seqs[src_snap_id1] = dst_snap_id1;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, false, 0);
+ expect_snap_is_unprotected(mock_src_image_ctx, src_snap_id1, true, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_unprotect(mock_dst_image_ctx, "snap1", 0);
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0);
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id1, dst_snap_id1}});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ m_snap_seqs[src_snap_id1] = dst_snap_id1;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, false, 0);
+ expect_snap_is_unprotected(mock_src_image_ctx, src_snap_id1, true, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_unprotect(mock_dst_image_ctx, "snap1", -EBUSY);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectCancel) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ m_snap_seqs[src_snap_id1] = dst_snap_id1;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, false, 0);
+ expect_snap_is_unprotected(mock_src_image_ctx, src_snap_id1, true, 0);
+ expect_start_op(mock_exclusive_lock);
+ EXPECT_CALL(*mock_dst_image_ctx.operations,
+ execute_snap_unprotect(_, StrEq("snap1"), _))
+ .WillOnce(DoAll(InvokeWithoutArgs([request]() {
+ request->cancel();
+ }),
+ WithArg<2>(Invoke([this](Context *ctx) {
+ m_work_queue->queue(ctx, 0);
+ }))));
+
+ request->send();
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectRemove) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx,
+ m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}],
+ false, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_unprotect(mock_dst_image_ctx, "snap1", 0);
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_remove(mock_dst_image_ctx, "snap1", 0);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1",
+ 12, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0);
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id1, 12}});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateProtect) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1",
+ 12, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0);
+ expect_snap_is_protected(mock_dst_image_ctx, 12, false, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_protect(mock_dst_image_ctx, "snap1", 0);
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id1, 12}});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtect) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ m_snap_seqs[src_snap_id1] = dst_snap_id1;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, true, 0);
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0);
+ expect_snap_is_protected(mock_dst_image_ctx, dst_snap_id1, false, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_protect(mock_dst_image_ctx, "snap1", 0);
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id1, dst_snap_id1}});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtectError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ m_snap_seqs[src_snap_id1] = dst_snap_id1;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, true, 0);
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0);
+ expect_snap_is_protected(mock_dst_image_ctx, dst_snap_id1, false, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_protect(mock_dst_image_ctx, "snap1", -EINVAL);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtectCancel) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+ m_snap_seqs[src_snap_id1] = dst_snap_id1;
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, true, 0);
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0);
+ expect_snap_is_protected(mock_dst_image_ctx, dst_snap_id1, false, 0);
+ expect_start_op(mock_exclusive_lock);
+ EXPECT_CALL(*mock_dst_image_ctx.operations,
+ execute_snap_protect(_, StrEq("snap1"), _))
+ .WillOnce(DoAll(InvokeWithoutArgs([request]() {
+ request->cancel();
+ }),
+ WithArg<2>(Invoke([this](Context *ctx) {
+ m_work_queue->queue(ctx, 0);
+ }))));
+
+ request->send();
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, SetHeadError) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSetHeadRequest mock_set_head_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_set_head(mock_set_head_request, -EINVAL);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, NoSetHead) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true));
+
+ uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace(), "snap1"}];
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1",
+ 12, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx,0,
+ src_snap_id1, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id1, 12}});
+}
+
+TEST_F(TestMockDeepCopySnapshotCopyRequest, StartEndLimit) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", false));
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap2", false));
+ ASSERT_EQ(0, create_snap(m_src_image_ctx,
+ {cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY,
+ {"peer uuid1"}, "", CEPH_NOSNAP}},
+ "snap3", false));
+ auto src_snap_id1 = m_src_image_ctx->snaps[2];
+ auto src_snap_id2 = m_src_image_ctx->snaps[1];
+ auto src_snap_id3 = m_src_image_ctx->snaps[0];
+
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap0", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", false));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap3", false));
+ auto dst_snap_id1 = m_dst_image_ctx->snaps[1];
+ auto dst_snap_id3 = m_dst_image_ctx->snaps[0];
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id3,
+ true, 0);
+
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id3);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_remove(mock_dst_image_ctx, "snap3", 0);
+
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id2);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap2",
+ 12, 0);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id3);
+
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id2, false, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id3, false, 0);
+
+ MockSetHeadRequest mock_set_head_request;
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx,
+ src_snap_id1,
+ src_snap_id3,
+ dst_snap_id1, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id2, 12}, {src_snap_id3, CEPH_NOSNAP}});
+}
+
+} // namespace deep_copy
+} // namespace librbd
diff --git a/src/test/librbd/deep_copy/test_mock_SnapshotCreateRequest.cc b/src/test/librbd/deep_copy/test_mock_SnapshotCreateRequest.cc
new file mode 100644
index 000000000..af24f5a0c
--- /dev/null
+++ b/src/test/librbd/deep_copy/test_mock_SnapshotCreateRequest.cc
@@ -0,0 +1,262 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/AsioEngine.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "osdc/Striper.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "librbd/deep_copy/SetHeadRequest.h"
+#include "librbd/deep_copy/SnapshotCreateRequest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace deep_copy {
+
+template <>
+class SetHeadRequest<librbd::MockTestImageCtx> {
+public:
+ static SetHeadRequest* s_instance;
+ Context *on_finish;
+
+ static SetHeadRequest* create(librbd::MockTestImageCtx *image_ctx,
+ uint64_t size,
+ const cls::rbd::ParentImageSpec &parent_spec,
+ uint64_t parent_overlap, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SetHeadRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SetHeadRequest<librbd::MockTestImageCtx>* SetHeadRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace deep_copy
+} // namespace librbd
+
+// template definitions
+#include "librbd/deep_copy/SnapshotCreateRequest.cc"
+template class librbd::deep_copy::SnapshotCreateRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace deep_copy {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::ReturnNew;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockDeepCopySnapshotCreateRequest : public TestMockFixture {
+public:
+ typedef SetHeadRequest<librbd::MockTestImageCtx> MockSetHeadRequest;
+ typedef SnapshotCreateRequest<librbd::MockTestImageCtx> MockSnapshotCreateRequest;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ std::shared_ptr<librbd::AsioEngine> m_asio_engine;
+ asio::ContextWQ *m_work_queue;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+
+ m_asio_engine = std::make_shared<librbd::AsioEngine>(
+ m_image_ctx->md_ctx);
+ m_work_queue = m_asio_engine->get_work_queue();
+ }
+
+ void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
+ EXPECT_CALL(mock_exclusive_lock, start_op(_)).WillOnce(Return(new LambdaContext([](int){})));
+ }
+
+ void expect_test_features(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t features, bool enabled) {
+ EXPECT_CALL(mock_image_ctx, test_features(features))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_set_head(MockSetHeadRequest &mock_set_head_request, int r) {
+ EXPECT_CALL(mock_set_head_request, send())
+ .WillOnce(Invoke([&mock_set_head_request, r]() {
+ mock_set_head_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_snap_create(librbd::MockTestImageCtx &mock_image_ctx,
+ const std::string &snap_name, uint64_t snap_id, int r) {
+ uint64_t flags = SNAP_CREATE_FLAG_SKIP_OBJECT_MAP |
+ SNAP_CREATE_FLAG_SKIP_NOTIFY_QUIESCE;
+ EXPECT_CALL(*mock_image_ctx.operations,
+ execute_snap_create(_, StrEq(snap_name), _, 0, flags, _))
+ .WillOnce(DoAll(InvokeWithoutArgs([&mock_image_ctx, snap_id, snap_name]() {
+ inject_snap(mock_image_ctx, snap_id, snap_name);
+ }),
+ WithArg<2>(Invoke([this, r](Context *ctx) {
+ m_work_queue->queue(ctx, r);
+ }))));
+ }
+
+ void expect_object_map_resize(librbd::MockTestImageCtx &mock_image_ctx,
+ librados::snap_t snap_id, int r) {
+ std::string oid(librbd::ObjectMap<>::object_map_name(mock_image_ctx.id,
+ snap_id));
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("object_map_resize"), _, _, _,
+ _))
+ .WillOnce(Return(r));
+ }
+
+ static void inject_snap(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, const std::string &snap_name) {
+ mock_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(),
+ snap_name}] = snap_id;
+ }
+
+ MockSnapshotCreateRequest *create_request(librbd::MockTestImageCtx &mock_local_image_ctx,
+ const std::string &snap_name,
+ const cls::rbd::SnapshotNamespace &snap_namespace,
+ uint64_t size,
+ const cls::rbd::ParentImageSpec &spec,
+ uint64_t parent_overlap,
+ Context *on_finish) {
+ return new MockSnapshotCreateRequest(&mock_local_image_ctx, snap_name, snap_namespace, size,
+ spec, parent_overlap, on_finish);
+ }
+};
+
+TEST_F(TestMockDeepCopySnapshotCreateRequest, SnapCreate) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ MockSetHeadRequest mock_set_head_request;
+
+ InSequence seq;
+ expect_set_head(mock_set_head_request, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_image_ctx, "snap1", 10, 0);
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false);
+
+ C_SaferCond ctx;
+ MockSnapshotCreateRequest *request = create_request(mock_image_ctx,
+ "snap1",
+ cls::rbd::UserSnapshotNamespace(),
+ m_image_ctx->size,
+ {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCreateRequest, SetHeadError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ MockSetHeadRequest mock_set_head_request;
+
+ InSequence seq;
+ expect_set_head(mock_set_head_request, -EINVAL);
+
+ C_SaferCond ctx;
+ MockSnapshotCreateRequest *request = create_request(mock_image_ctx,
+ "snap1",
+ cls::rbd::UserSnapshotNamespace(),
+ 123, {}, 0,
+ &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCreateRequest, SnapCreateError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ MockSetHeadRequest mock_set_head_request;
+
+ InSequence seq;
+ expect_set_head(mock_set_head_request, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_image_ctx, "snap1", 10, -EINVAL);
+
+ C_SaferCond ctx;
+ MockSnapshotCreateRequest *request = create_request(mock_image_ctx,
+ "snap1",
+ cls::rbd::UserSnapshotNamespace(),
+ m_image_ctx->size,
+ {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCreateRequest, ResizeObjectMap) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ MockSetHeadRequest mock_set_head_request;
+
+ InSequence seq;
+ expect_set_head(mock_set_head_request, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_image_ctx, "snap1", 10, 0);
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_start_op(mock_exclusive_lock);
+ expect_object_map_resize(mock_image_ctx, 10, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCreateRequest *request = create_request(mock_image_ctx,
+ "snap1",
+ cls::rbd::UserSnapshotNamespace(),
+ m_image_ctx->size,
+ {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopySnapshotCreateRequest, ResizeObjectMapError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ MockSetHeadRequest mock_set_head_request;
+
+ InSequence seq;
+ expect_set_head(mock_set_head_request, 0);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_image_ctx, "snap1", 10, 0);
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_start_op(mock_exclusive_lock);
+ expect_object_map_resize(mock_image_ctx, 10, -EINVAL);
+
+ C_SaferCond ctx;
+ MockSnapshotCreateRequest *request = create_request(mock_image_ctx,
+ "snap1",
+ cls::rbd::UserSnapshotNamespace(),
+ m_image_ctx->size,
+ {}, 0, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace deep_copy
+} // namespace librbd
diff --git a/src/test/librbd/exclusive_lock/test_mock_PostAcquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_PostAcquireRequest.cc
new file mode 100644
index 000000000..943b8cc2d
--- /dev/null
+++ b/src/test/librbd/exclusive_lock/test_mock_PostAcquireRequest.cc
@@ -0,0 +1,582 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/cache/MockImageCache.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "test/librbd/mock/MockJournalPolicy.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/exclusive_lock/PostAcquireRequest.h"
+#include "librbd/image/RefreshRequest.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+inline ImageCtx &get_image_ctx(MockTestImageCtx &image_ctx) {
+ return *(image_ctx.image_ctx);
+}
+
+} // anonymous namespace
+
+namespace image {
+
+template<>
+struct RefreshRequest<librbd::MockTestImageCtx> {
+ static RefreshRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static RefreshRequest *create(librbd::MockTestImageCtx &image_ctx,
+ bool acquire_lock_refresh,
+ bool skip_open_parent, Context *on_finish) {
+ EXPECT_TRUE(acquire_lock_refresh);
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ RefreshRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+RefreshRequest<librbd::MockTestImageCtx> *RefreshRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+} // namespace librbd
+
+// template definitions
+#include "librbd/Journal.cc"
+
+#include "librbd/exclusive_lock/PostAcquireRequest.cc"
+template class librbd::exclusive_lock::PostAcquireRequest<librbd::MockTestImageCtx>;
+
+ACTION_P3(FinishRequest2, request, r, mock) {
+ mock->image_ctx->op_work_queue->queue(request->on_finish, r);
+}
+
+
+namespace librbd {
+namespace exclusive_lock {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+static const std::string TEST_COOKIE("auto 123");
+
+class TestMockExclusiveLockPostAcquireRequest : public TestMockFixture {
+public:
+ typedef PostAcquireRequest<MockTestImageCtx> MockPostAcquireRequest;
+ typedef librbd::image::RefreshRequest<MockTestImageCtx> MockRefreshRequest;
+
+ void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features,
+ bool enabled) {
+ EXPECT_CALL(mock_image_ctx, test_features(features))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features,
+ ceph::shared_mutex &lock, bool enabled) {
+ EXPECT_CALL(mock_image_ctx, test_features(features, _))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_is_refresh_required(MockTestImageCtx &mock_image_ctx, bool required) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(required));
+ }
+
+ void expect_refresh(MockTestImageCtx &mock_image_ctx,
+ MockRefreshRequest &mock_refresh_request, int r) {
+ EXPECT_CALL(mock_refresh_request, send())
+ .WillOnce(FinishRequest2(&mock_refresh_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_create_object_map(MockTestImageCtx &mock_image_ctx,
+ MockObjectMap *mock_object_map) {
+ EXPECT_CALL(mock_image_ctx, create_object_map(_))
+ .WillOnce(Return(mock_object_map));
+ }
+
+ void expect_open_object_map(MockTestImageCtx &mock_image_ctx,
+ MockObjectMap &mock_object_map, int r) {
+ EXPECT_CALL(mock_object_map, open(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_close_object_map(MockTestImageCtx &mock_image_ctx,
+ MockObjectMap &mock_object_map) {
+ EXPECT_CALL(mock_object_map, close(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_create_journal(MockTestImageCtx &mock_image_ctx,
+ MockJournal *mock_journal) {
+ EXPECT_CALL(mock_image_ctx, create_journal())
+ .WillOnce(Return(mock_journal));
+ }
+
+ void expect_open_journal(MockTestImageCtx &mock_image_ctx,
+ MockJournal &mock_journal, int r) {
+ EXPECT_CALL(mock_journal, open(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_close_journal(MockTestImageCtx &mock_image_ctx,
+ MockJournal &mock_journal) {
+ EXPECT_CALL(mock_journal, close(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_get_journal_policy(MockTestImageCtx &mock_image_ctx,
+ MockJournalPolicy &mock_journal_policy) {
+ EXPECT_CALL(mock_image_ctx, get_journal_policy())
+ .WillOnce(Return(&mock_journal_policy));
+ }
+
+ void expect_journal_disabled(MockJournalPolicy &mock_journal_policy,
+ bool disabled) {
+ EXPECT_CALL(mock_journal_policy, journal_disabled())
+ .WillOnce(Return(disabled));
+ }
+
+ void expect_allocate_journal_tag(MockTestImageCtx &mock_image_ctx,
+ MockJournalPolicy &mock_journal_policy,
+ int r) {
+ EXPECT_CALL(mock_journal_policy, allocate_tag_on_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_handle_prepare_lock_complete(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
+ }
+
+ void expect_acquired_exclusive_lock(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.plugin_registry, acquired_exclusive_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_prerelease_exclusive_lock(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.plugin_registry, prerelease_exclusive_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+};
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+
+ MockJournal mock_journal;
+ MockJournalPolicy mock_journal_policy;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, true);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_create_journal(mock_image_ctx, &mock_journal);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_open_journal(mock_image_ctx, mock_journal, 0);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+
+ expect_acquired_exclusive_lock(mock_image_ctx, 0);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, acquire_ctx.wait());
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, SuccessRefresh) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockRefreshRequest mock_refresh_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh(mock_image_ctx, mock_refresh_request, 0);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false);
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, false);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ expect_acquired_exclusive_lock(mock_image_ctx, 0);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, acquire_ctx.wait());
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, SuccessJournalDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, false);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ expect_acquired_exclusive_lock(mock_image_ctx, 0);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, acquire_ctx.wait());
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, SuccessObjectMapDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false);
+
+ MockJournal mock_journal;
+ MockJournalPolicy mock_journal_policy;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, true);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_create_journal(mock_image_ctx, &mock_journal);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_open_journal(mock_image_ctx, mock_journal, 0);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+
+ expect_acquired_exclusive_lock(mock_image_ctx, 0);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, acquire_ctx.wait());
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, RefreshError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockRefreshRequest mock_refresh_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh(mock_image_ctx, mock_refresh_request, -EINVAL);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond *acquire_ctx = new C_SaferCond();
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, RefreshLockDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockRefreshRequest mock_refresh_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh(mock_image_ctx, mock_refresh_request, -ERESTART);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false);
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, false);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ expect_acquired_exclusive_lock(mock_image_ctx, 0);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, acquire_ctx.wait());
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, JournalError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+
+ MockJournal mock_journal;
+ MockJournalPolicy mock_journal_policy;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, true);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_create_journal(mock_image_ctx, &mock_journal);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_open_journal(mock_image_ctx, mock_journal, -EINVAL);
+ expect_close_journal(mock_image_ctx, mock_journal);
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, AllocateJournalTagError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+
+ MockJournal mock_journal;
+ MockJournalPolicy mock_journal_policy;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, true);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_create_journal(mock_image_ctx, &mock_journal);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_open_journal(mock_image_ctx, mock_journal, 0);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, -EPERM);
+ expect_close_journal(mock_image_ctx, mock_journal);
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, InitImageCacheError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+
+ MockJournal mock_journal;
+ MockJournalPolicy mock_journal_policy;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, true);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_create_journal(mock_image_ctx, &mock_journal);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_open_journal(mock_image_ctx, mock_journal, 0);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+
+ expect_acquired_exclusive_lock(mock_image_ctx, -ENOENT);
+ expect_prerelease_exclusive_lock(mock_image_ctx, 0);
+
+ expect_close_journal(mock_image_ctx, mock_journal);
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, OpenObjectMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, -EINVAL);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond *acquire_ctx = new C_SaferCond();
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx.object_map);
+}
+
+TEST_F(TestMockExclusiveLockPostAcquireRequest, OpenObjectMapTooBig) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, -EFBIG);
+
+ MockJournal mock_journal;
+ MockJournalPolicy mock_journal_policy;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, true);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_create_journal(mock_image_ctx, &mock_journal);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_open_journal(mock_image_ctx, mock_journal, 0);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+
+ expect_acquired_exclusive_lock(mock_image_ctx, 0);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, acquire_ctx.wait());
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx.object_map);
+}
+
+} // namespace exclusive_lock
+} // namespace librbd
diff --git a/src/test/librbd/exclusive_lock/test_mock_PreAcquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_PreAcquireRequest.cc
new file mode 100644
index 000000000..5b4bce6dd
--- /dev/null
+++ b/src/test/librbd/exclusive_lock/test_mock_PreAcquireRequest.cc
@@ -0,0 +1,92 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/exclusive_lock/PreAcquireRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+inline ImageCtx &get_image_ctx(MockTestImageCtx &image_ctx) {
+ return *(image_ctx.image_ctx);
+}
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/exclusive_lock/PreAcquireRequest.cc"
+template class librbd::exclusive_lock::PreAcquireRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace exclusive_lock {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+static const std::string TEST_COOKIE("auto 123");
+
+class TestMockExclusiveLockPreAcquireRequest : public TestMockFixture {
+public:
+ typedef PreAcquireRequest<MockTestImageCtx> MockPreAcquireRequest;
+
+ void expect_flush_notifies(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_prepare_lock(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_))
+ .WillOnce(Invoke([](Context *on_ready) {
+ on_ready->complete(0);
+ }));
+ }
+
+ void expect_handle_prepare_lock_complete(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
+ }
+
+};
+
+TEST_F(TestMockExclusiveLockPreAcquireRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_flush_notifies(mock_image_ctx);
+
+ C_SaferCond ctx;
+ MockPreAcquireRequest *req = MockPreAcquireRequest::create(mock_image_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+} // namespace exclusive_lock
+} // namespace librbd
diff --git a/src/test/librbd/exclusive_lock/test_mock_PreReleaseRequest.cc b/src/test/librbd/exclusive_lock/test_mock_PreReleaseRequest.cc
new file mode 100644
index 000000000..466a3ab42
--- /dev/null
+++ b/src/test/librbd/exclusive_lock/test_mock_PreReleaseRequest.cc
@@ -0,0 +1,388 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/cache/MockImageCache.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/AsyncOpTracker.h"
+#include "librbd/exclusive_lock/ImageDispatch.h"
+#include "librbd/exclusive_lock/PreReleaseRequest.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <list>
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace exclusive_lock {
+
+template <>
+struct ImageDispatch<MockTestImageCtx> {
+ MOCK_METHOD3(set_require_lock, void(bool init_shutdown, io::Direction,
+ Context*));
+ MOCK_METHOD1(unset_require_lock, void(io::Direction));
+};
+
+} // namespace exclusive_lock
+
+namespace util {
+
+inline ImageCtx* get_image_ctx(MockTestImageCtx* image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+// template definitions
+#include "librbd/exclusive_lock/PreReleaseRequest.cc"
+
+namespace librbd {
+namespace exclusive_lock {
+
+namespace {
+
+struct MockContext : public Context {
+ MOCK_METHOD1(complete, void(int));
+ MOCK_METHOD1(finish, void(int));
+};
+
+} // anonymous namespace
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+static const std::string TEST_COOKIE("auto 123");
+
+class TestMockExclusiveLockPreReleaseRequest : public TestMockFixture {
+public:
+ typedef ImageDispatch<MockTestImageCtx> MockImageDispatch;
+ typedef PreReleaseRequest<MockTestImageCtx> MockPreReleaseRequest;
+
+ void expect_complete_context(MockContext &mock_context, int r) {
+ EXPECT_CALL(mock_context, complete(r));
+ }
+
+ void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features,
+ bool enabled) {
+ EXPECT_CALL(mock_image_ctx, test_features(features))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_set_require_lock(MockImageDispatch &mock_image_dispatch,
+ bool init_shutdown,
+ librbd::io::Direction direction, int r) {
+ EXPECT_CALL(mock_image_dispatch, set_require_lock(init_shutdown,
+ direction, _))
+ .WillOnce(WithArg<2>(Invoke([r](Context* ctx) { ctx->complete(r); })));
+ }
+
+ void expect_set_require_lock(MockTestImageCtx &mock_image_ctx,
+ MockImageDispatch &mock_image_dispatch,
+ bool init_shutdown, int r) {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_EXCLUSIVE_LOCK, true);
+ if (!mock_image_ctx.clone_copy_on_read) {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ ((mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0));
+ if ((mock_image_ctx.features & RBD_FEATURE_JOURNALING) == 0) {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_DIRTY_CACHE,
+ ((mock_image_ctx.features & RBD_FEATURE_DIRTY_CACHE) != 0));
+ }
+ }
+ if (mock_image_ctx.clone_copy_on_read ||
+ (mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0 ||
+ (mock_image_ctx.features & RBD_FEATURE_DIRTY_CACHE) != 0) {
+ expect_set_require_lock(mock_image_dispatch, init_shutdown,
+ librbd::io::DIRECTION_BOTH, r);
+ } else {
+ expect_set_require_lock(mock_image_dispatch, init_shutdown,
+ librbd::io::DIRECTION_WRITE, r);
+ }
+ }
+
+ void expect_unset_require_lock(MockImageDispatch &mock_image_dispatch) {
+ EXPECT_CALL(mock_image_dispatch, unset_require_lock(
+ io::DIRECTION_BOTH));
+ }
+
+ void expect_cancel_op_requests(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(mock_image_ctx, cancel_async_requests(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_close_journal(MockTestImageCtx &mock_image_ctx,
+ MockJournal &mock_journal, int r) {
+ EXPECT_CALL(mock_journal, close(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_close_object_map(MockTestImageCtx &mock_image_ctx,
+ MockObjectMap &mock_object_map) {
+ EXPECT_CALL(mock_object_map, close(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_prerelease_exclusive_lock(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.plugin_registry, prerelease_exclusive_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_invalidate_cache(MockTestImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, invalidate_cache(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_flush_notifies(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_prepare_lock(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_))
+ .WillOnce(Invoke([](Context *on_ready) {
+ on_ready->complete(0);
+ }));
+ }
+
+ void expect_handle_prepare_lock_complete(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
+ }
+
+ void expect_flush_io(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, send(_))
+ .WillOnce(Invoke([&mock_image_ctx, r](io::ImageDispatchSpec* spec) {
+ ASSERT_TRUE(boost::get<io::ImageDispatchSpec::Flush>(
+ &spec->request) != nullptr);
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ auto aio_comp = spec->aio_comp;
+ auto ctx = new LambdaContext([aio_comp](int r) {
+ if (r < 0) {
+ aio_comp->fail(r);
+ } else {
+ aio_comp->set_request_count(1);
+ aio_comp->add_request();
+ aio_comp->complete_request(r);
+ }
+ });
+ mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
+ }));
+ }
+
+ AsyncOpTracker m_async_op_tracker;
+};
+
+TEST_F(TestMockExclusiveLockPreReleaseRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ expect_cancel_op_requests(mock_image_ctx, 0);
+ MockImageDispatch mock_image_dispatch;
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, false, 0);
+
+ expect_prepare_lock(mock_image_ctx);
+
+ expect_prerelease_exclusive_lock(mock_image_ctx, 0);
+
+ expect_invalidate_cache(mock_image_ctx, 0);
+
+ expect_flush_io(mock_image_ctx, 0);
+
+ expect_flush_notifies(mock_image_ctx);
+
+ MockJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+ expect_close_journal(mock_image_ctx, mock_journal, -EINVAL);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond ctx;
+ MockPreReleaseRequest *req = MockPreReleaseRequest::create(
+ mock_image_ctx, &mock_image_dispatch, false, m_async_op_tracker, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPreReleaseRequest, SuccessJournalDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatch mock_image_dispatch;
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, false, 0);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_cancel_op_requests(mock_image_ctx, 0);
+ expect_prepare_lock(mock_image_ctx);
+
+ expect_prerelease_exclusive_lock(mock_image_ctx, 0);
+
+ expect_invalidate_cache(mock_image_ctx, 0);
+
+ expect_flush_io(mock_image_ctx, 0);
+
+ expect_flush_notifies(mock_image_ctx);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond ctx;
+ MockPreReleaseRequest *req = MockPreReleaseRequest::create(
+ mock_image_ctx, &mock_image_dispatch, false, m_async_op_tracker, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPreReleaseRequest, SuccessObjectMapDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatch mock_image_dispatch;
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true, 0);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_cancel_op_requests(mock_image_ctx, 0);
+
+ expect_prerelease_exclusive_lock(mock_image_ctx, 0);
+
+ expect_invalidate_cache(mock_image_ctx, 0);
+
+ expect_flush_io(mock_image_ctx, 0);
+
+ expect_flush_notifies(mock_image_ctx);
+
+ C_SaferCond release_ctx;
+ C_SaferCond ctx;
+ MockPreReleaseRequest *req = MockPreReleaseRequest::create(
+ mock_image_ctx, &mock_image_dispatch, true, m_async_op_tracker, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPreReleaseRequest, Blocklisted) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_cancel_op_requests(mock_image_ctx, 0);
+ MockImageDispatch mock_image_dispatch;
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, false,
+ -EBLOCKLISTED);
+ expect_prepare_lock(mock_image_ctx);
+
+ expect_prerelease_exclusive_lock(mock_image_ctx, 0);
+
+ expect_invalidate_cache(mock_image_ctx, -EBLOCKLISTED);
+
+ expect_flush_io(mock_image_ctx, -EBLOCKLISTED);
+
+ expect_flush_notifies(mock_image_ctx);
+
+ MockJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+ expect_close_journal(mock_image_ctx, mock_journal, -EBLOCKLISTED);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond ctx;
+ MockPreReleaseRequest *req = MockPreReleaseRequest::create(
+ mock_image_ctx, &mock_image_dispatch, false, m_async_op_tracker, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockExclusiveLockPreReleaseRequest, Disabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ expect_cancel_op_requests(mock_image_ctx, 0);
+ MockImageDispatch mock_image_dispatch;
+
+ expect_test_features(mock_image_ctx, RBD_FEATURE_EXCLUSIVE_LOCK, false);
+
+ expect_prepare_lock(mock_image_ctx);
+
+ expect_prerelease_exclusive_lock(mock_image_ctx, 0);
+
+ expect_invalidate_cache(mock_image_ctx, 0);
+
+ expect_flush_io(mock_image_ctx, 0);
+
+ expect_flush_notifies(mock_image_ctx);
+
+ MockJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+ expect_close_journal(mock_image_ctx, mock_journal, -EINVAL);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond ctx;
+ MockPreReleaseRequest *req = MockPreReleaseRequest::create(
+ mock_image_ctx, &mock_image_dispatch, false, m_async_op_tracker, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace exclusive_lock
+} // namespace librbd
diff --git a/src/test/librbd/fsx.cc b/src/test/librbd/fsx.cc
new file mode 100644
index 000000000..94c454bf3
--- /dev/null
+++ b/src/test/librbd/fsx.cc
@@ -0,0 +1,3472 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:8; indent-tabs-mode:t -*-
+// vim: ts=8 sw=8 smarttab
+/*
+ * Copyright (C) 1991, NeXT Computer, Inc. All Rights Reserverd.
+ *
+ * File: fsx.cc
+ * Author: Avadis Tevanian, Jr.
+ *
+ * File system exerciser.
+ *
+ * Rewritten 8/98 by Conrad Minshall.
+ *
+ * Small changes to work under Linux -- davej.
+ *
+ * Checks for mmap last-page zero fill.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <limits.h>
+#include <strings.h>
+#if defined(__FreeBSD__)
+#include <sys/disk.h>
+#endif
+#include <sys/file.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#endif
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#ifdef HAVE_ERR_H
+#include <err.h>
+#endif
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <math.h>
+#include <fcntl.h>
+#include <random>
+
+#include "include/compat.h"
+#include "include/intarith.h"
+#if defined(WITH_KRBD)
+#include "include/krbd.h"
+#endif
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/rbd/librbd.h"
+#include "include/rbd/librbd.hpp"
+#include "common/Cond.h"
+#include "common/SubProcess.h"
+#include "common/safe_io.h"
+#include "journal/Journaler.h"
+#include "journal/ReplayEntry.h"
+#include "journal/ReplayHandler.h"
+#include "journal/Settings.h"
+
+#include <boost/scope_exit.hpp>
+
+#define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */
+
+/*
+ * A log entry is an operation and a bunch of arguments.
+ */
+
+struct log_entry {
+ int operation;
+ int args[3];
+};
+
+#define LOGSIZE 1000
+
+struct log_entry oplog[LOGSIZE]; /* the log */
+int logptr = 0; /* current position in log */
+int logcount = 0; /* total ops */
+
+/*
+ * The operation matrix is complex due to conditional execution of different
+ * features. Hence when we come to deciding what operation to run, we need to
+ * be careful in how we select the different operations. The active operations
+ * are mapped to numbers as follows:
+ *
+ * lite !lite
+ * READ: 0 0
+ * WRITE: 1 1
+ * MAPREAD: 2 2
+ * MAPWRITE: 3 3
+ * TRUNCATE: - 4
+ * FALLOCATE: - 5
+ * PUNCH HOLE: - 6
+ * WRITESAME: - 7
+ * COMPAREANDWRITE: - 8
+ *
+ * When mapped read/writes are disabled, they are simply converted to normal
+ * reads and writes. When fallocate/fpunch calls are disabled, they are
+ * converted to OP_SKIPPED. Hence OP_SKIPPED needs to have a number higher than
+ * the operation selction matrix, as does the OP_CLOSEOPEN which is an
+ * operation modifier rather than an operation in itself.
+ *
+ * Because of the "lite" version, we also need to have different "maximum
+ * operation" defines to allow the ops to be selected correctly based on the
+ * mode being run.
+ */
+
+/* common operations */
+#define OP_READ 0
+#define OP_WRITE 1
+#define OP_MAPREAD 2
+#define OP_MAPWRITE 3
+#define OP_MAX_LITE 4
+
+/* !lite operations */
+#define OP_TRUNCATE 4
+#define OP_FALLOCATE 5
+#define OP_PUNCH_HOLE 6
+#define OP_WRITESAME 7
+#define OP_COMPARE_AND_WRITE 8
+/* rbd-specific operations */
+#define OP_CLONE 9
+#define OP_FLATTEN 10
+#define OP_MAX_FULL 11
+
+/* operation modifiers */
+#define OP_CLOSEOPEN 100
+#define OP_SKIPPED 101
+
+#undef PAGE_SIZE
+#define PAGE_SIZE get_page_size()
+#undef PAGE_MASK
+#define PAGE_MASK (PAGE_SIZE - 1)
+
+
+char *original_buf; /* a pointer to the original data */
+char *good_buf; /* a pointer to the correct data */
+char *temp_buf; /* a pointer to the current data */
+
+char dirpath[1024];
+
+off_t file_size = 0;
+off_t biggest = 0;
+unsigned long testcalls = 0; /* calls to function "test" */
+
+const char* cluster_name = "ceph"; /* --cluster optional */
+const char* client_id = "admin"; /* --id optional */
+
+unsigned long simulatedopcount = 0; /* -b flag */
+int closeprob = 0; /* -c flag */
+int debug = 0; /* -d flag */
+unsigned long debugstart = 0; /* -D flag */
+int flush_enabled = 0; /* -f flag */
+int deep_copy = 0; /* -g flag */
+int holebdy = 1; /* -h flag */
+bool journal_replay = false; /* -j flah */
+int keep_on_success = 0; /* -k flag */
+int do_fsync = 0; /* -y flag */
+unsigned long maxfilelen = 256 * 1024; /* -l flag */
+int sizechecks = 1; /* -n flag disables them */
+int maxoplen = 64 * 1024; /* -o flag */
+int quiet = 0; /* -q flag */
+unsigned long progressinterval = 0; /* -p flag */
+int readbdy = 1; /* -r flag */
+int style = 0; /* -s flag */
+int prealloc = 0; /* -x flag */
+int truncbdy = 1; /* -t flag */
+int writebdy = 1; /* -w flag */
+long monitorstart = -1; /* -m flag */
+long monitorend = -1; /* -m flag */
+int lite = 0; /* -L flag */
+long numops = -1; /* -N flag */
+int randomoplen = 1; /* -O flag disables it */
+int seed = 1; /* -S flag */
+int mapped_writes = 0; /* -W flag disables */
+int fallocate_calls = 0; /* -F flag disables */
+int punch_hole_calls = 1; /* -H flag disables */
+int clone_calls = 1; /* -C flag disables */
+int randomize_striping = 1; /* -U flag disables */
+int randomize_parent_overlap = 1;
+int mapped_reads = 0; /* -R flag disables it */
+int fsxgoodfd = 0;
+int o_direct = 0; /* -Z flag */
+
+int num_clones = 0;
+
+int page_size;
+int page_mask;
+int mmap_mask;
+
+FILE * fsxlogf = NULL;
+int badoff = -1;
+int closeopen = 0;
+
+void
+vwarnc(int code, const char *fmt, va_list ap) {
+ fprintf(stderr, "fsx: ");
+ if (fmt != NULL) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": ");
+ }
+ fprintf(stderr, "%s\n", strerror(code));
+}
+
+void
+warn(const char * fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vwarnc(errno, fmt, ap);
+ va_end(ap);
+}
+
+#define BUF_SIZE 1024
+
+void
+prt(const char *fmt, ...)
+{
+ va_list args;
+ char buffer[BUF_SIZE];
+
+ va_start(args, fmt);
+ vsnprintf(buffer, BUF_SIZE, fmt, args);
+ va_end(args);
+ fprintf(stdout, "%s", buffer);
+ if (fsxlogf)
+ fprintf(fsxlogf, "%s", buffer);
+}
+
+void
+prterr(const char *prefix)
+{
+ prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
+}
+
+void
+prterrcode(const char *prefix, int code)
+{
+ prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(-code));
+}
+
+void
+simple_err(const char *msg, int err)
+{
+ fprintf(stderr, "%s: %s\n", msg, strerror(-err));
+}
+
+/*
+ * random
+ */
+std::mt19937 random_generator;
+
+uint_fast32_t
+get_random(void)
+{
+ return random_generator();
+}
+
+int get_features(uint64_t* features);
+void replay_imagename(char *buf, size_t len, int clones);
+
+namespace {
+
+static const std::string JOURNAL_CLIENT_ID("fsx");
+
+struct ReplayHandler : public journal::ReplayHandler {
+ journal::Journaler *journaler;
+ journal::Journaler *replay_journaler;
+ Context *on_finish;
+
+ ReplayHandler(journal::Journaler *journaler,
+ journal::Journaler *replay_journaler, Context *on_finish)
+ : journaler(journaler), replay_journaler(replay_journaler),
+ on_finish(on_finish) {
+ }
+
+ void handle_entries_available() override {
+ while (true) {
+ journal::ReplayEntry replay_entry;
+ if (!journaler->try_pop_front(&replay_entry)) {
+ return;
+ }
+
+ replay_journaler->append(0, replay_entry.get_data());
+ }
+ }
+
+ void handle_complete(int r) override {
+ on_finish->complete(r);
+ }
+};
+
+int get_image_id(librados::IoCtx &io_ctx, const char *image_name,
+ std::string *image_id) {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int r = rbd.open(io_ctx, image, image_name);
+ if (r < 0) {
+ simple_err("failed to open image", r);
+ return r;
+ }
+
+ rbd_image_info_t info;
+ r = image.stat(info, sizeof(info));
+ if (r < 0) {
+ simple_err("failed to stat image", r);
+ return r;
+ }
+
+ *image_id = std::string(&info.block_name_prefix[strlen(RBD_DATA_PREFIX)]);
+ return 0;
+}
+
+int register_journal(rados_ioctx_t ioctx, const char *image_name) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(ioctx, io_ctx);
+
+ std::string image_id;
+ int r = get_image_id(io_ctx, image_name, &image_id);
+ if (r < 0) {
+ return r;
+ }
+
+ journal::Journaler journaler(io_ctx, image_id, JOURNAL_CLIENT_ID, {},
+ nullptr);
+ r = journaler.register_client(bufferlist());
+ if (r < 0) {
+ simple_err("failed to register journal client", r);
+ return r;
+ }
+ return 0;
+}
+
+int unregister_journal(rados_ioctx_t ioctx, const char *image_name) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(ioctx, io_ctx);
+
+ std::string image_id;
+ int r = get_image_id(io_ctx, image_name, &image_id);
+ if (r < 0) {
+ return r;
+ }
+
+ journal::Journaler journaler(io_ctx, image_id, JOURNAL_CLIENT_ID, {},
+ nullptr);
+ r = journaler.unregister_client();
+ if (r < 0) {
+ simple_err("failed to unregister journal client", r);
+ return r;
+ }
+ return 0;
+}
+
+int create_replay_image(rados_ioctx_t ioctx, int order,
+ uint64_t stripe_unit, int stripe_count,
+ const char *replay_image_name,
+ const char *last_replay_image_name) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(ioctx, io_ctx);
+
+ uint64_t features;
+ int r = get_features(&features);
+ if (r < 0) {
+ return r;
+ }
+
+ librbd::RBD rbd;
+ if (last_replay_image_name == nullptr) {
+ r = rbd.create2(io_ctx, replay_image_name, 0, features, &order);
+ } else {
+ r = rbd.clone2(io_ctx, last_replay_image_name, "snap",
+ io_ctx, replay_image_name, features, &order,
+ stripe_unit, stripe_count);
+ }
+
+ if (r < 0) {
+ simple_err("failed to create replay image", r);
+ return r;
+ }
+
+ return 0;
+}
+
+int replay_journal(rados_ioctx_t ioctx, const char *image_name,
+ const char *replay_image_name) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(ioctx, io_ctx);
+
+ std::string image_id;
+ int r = get_image_id(io_ctx, image_name, &image_id);
+ if (r < 0) {
+ return r;
+ }
+
+ std::string replay_image_id;
+ r = get_image_id(io_ctx, replay_image_name, &replay_image_id);
+ if (r < 0) {
+ return r;
+ }
+
+ journal::Journaler journaler(io_ctx, image_id, JOURNAL_CLIENT_ID, {},
+ nullptr);
+ C_SaferCond init_ctx;
+ journaler.init(&init_ctx);
+ BOOST_SCOPE_EXIT_ALL( (&journaler) ) {
+ journaler.shut_down();
+ };
+
+ r = init_ctx.wait();
+ if (r < 0) {
+ simple_err("failed to initialize journal", r);
+ return r;
+ }
+
+ journal::Journaler replay_journaler(io_ctx, replay_image_id, "", {},
+ nullptr);
+
+ C_SaferCond replay_init_ctx;
+ replay_journaler.init(&replay_init_ctx);
+ BOOST_SCOPE_EXIT_ALL( (&replay_journaler) ) {
+ replay_journaler.shut_down();
+ };
+
+ r = replay_init_ctx.wait();
+ if (r < 0) {
+ simple_err("failed to initialize replay journal", r);
+ return r;
+ }
+
+ replay_journaler.start_append(0);
+
+ C_SaferCond replay_ctx;
+ ReplayHandler replay_handler(&journaler, &replay_journaler,
+ &replay_ctx);
+
+ // copy journal events from source image to replay image
+ journaler.start_replay(&replay_handler);
+ r = replay_ctx.wait();
+
+ journaler.stop_replay();
+
+ C_SaferCond stop_ctx;
+ replay_journaler.stop_append(&stop_ctx);
+ int stop_r = stop_ctx.wait();
+ if (r == 0 && stop_r < 0) {
+ r = stop_r;
+ }
+
+ if (r < 0) {
+ simple_err("failed to replay journal", r);
+ return r;
+ }
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ r = rbd.open(io_ctx, image, replay_image_name);
+ if (r < 0) {
+ simple_err("failed to open replay image", r);
+ return r;
+ }
+
+ // perform an IO op to initiate the journal replay
+ bufferlist bl;
+ r = static_cast<ssize_t>(image.write(0, 0, bl));
+ if (r < 0) {
+ simple_err("failed to write to replay image", r);
+ return r;
+ }
+ return 0;
+}
+
+int finalize_journal(rados_ioctx_t ioctx, const char *imagename, int clones,
+ int order, uint64_t stripe_unit, int stripe_count) {
+ char replayimagename[1024];
+ replay_imagename(replayimagename, sizeof(replayimagename), clones);
+
+ char lastreplayimagename[1024];
+ if (clones > 0) {
+ replay_imagename(lastreplayimagename,
+ sizeof(lastreplayimagename), clones - 1);
+ }
+
+ int ret = create_replay_image(ioctx, order, stripe_unit,
+ stripe_count, replayimagename,
+ clones > 0 ? lastreplayimagename :
+ nullptr);
+ if (ret < 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ ret = replay_journal(ioctx, imagename, replayimagename);
+ if (ret < 0) {
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+} // anonymous namespace
+
+/*
+ * rbd
+ */
+
+struct rbd_ctx {
+ const char *name; /* image name */
+ rbd_image_t image; /* image handle */
+ const char *krbd_name; /* image /dev/rbd<id> name */ /* reused for nbd test */
+ int krbd_fd; /* image /dev/rbd<id> fd */ /* reused for nbd test */
+};
+
+#define RBD_CTX_INIT (struct rbd_ctx) { NULL, NULL, NULL, -1}
+
+struct rbd_operations {
+ int (*open)(const char *name, struct rbd_ctx *ctx);
+ int (*close)(struct rbd_ctx *ctx);
+ ssize_t (*read)(struct rbd_ctx *ctx, uint64_t off, size_t len, char *buf);
+ ssize_t (*write)(struct rbd_ctx *ctx, uint64_t off, size_t len, const char *buf);
+ int (*flush)(struct rbd_ctx *ctx);
+ int (*discard)(struct rbd_ctx *ctx, uint64_t off, uint64_t len);
+ int (*get_size)(struct rbd_ctx *ctx, uint64_t *size);
+ int (*resize)(struct rbd_ctx *ctx, uint64_t size);
+ int (*clone)(struct rbd_ctx *ctx, const char *src_snapname,
+ const char *dst_imagename, int *order, int stripe_unit,
+ int stripe_count);
+ int (*flatten)(struct rbd_ctx *ctx);
+ ssize_t (*writesame)(struct rbd_ctx *ctx, uint64_t off, size_t len,
+ const char *buf, size_t data_len);
+ ssize_t (*compare_and_write)(struct rbd_ctx *ctx, uint64_t off, size_t len,
+ const char *cmp_buf, const char *buf);
+};
+
+char *pool; /* name of the pool our test image is in */
+char *iname; /* name of our test image */
+rados_t cluster; /* handle for our test cluster */
+rados_ioctx_t ioctx; /* handle for our test pool */
+#if defined(WITH_KRBD)
+struct krbd_ctx *krbd; /* handle for libkrbd */
+#endif
+bool skip_partial_discard; /* rbd_skip_partial_discard config value*/
+
+int get_features(uint64_t* features) {
+ char buf[1024];
+ int r = rados_conf_get(cluster, "rbd_default_features", buf,
+ sizeof(buf));
+ if (r < 0) {
+ simple_err("Could not get rbd_default_features value", r);
+ return r;
+ }
+
+ *features = strtol(buf, NULL, 0);
+
+ if (clone_calls) {
+ *features |= RBD_FEATURE_LAYERING;
+ }
+ if (journal_replay) {
+ *features |= (RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_JOURNALING);
+ }
+ return 0;
+}
+
+/*
+ * librbd/krbd rbd_operations handlers. Given the rest of fsx.c, no
+ * attempt to do error handling is made in these handlers.
+ */
+
+int
+__librbd_open(const char *name, struct rbd_ctx *ctx)
+{
+ rbd_image_t image;
+ int ret;
+
+ ceph_assert(!ctx->name && !ctx->image &&
+ !ctx->krbd_name && ctx->krbd_fd < 0);
+
+ ret = rbd_open(ioctx, name, &image, NULL);
+ if (ret < 0) {
+ prt("rbd_open(%s) failed\n", name);
+ return ret;
+ }
+
+ ctx->name = strdup(name);
+ ctx->image = image;
+ ctx->krbd_name = NULL;
+ ctx->krbd_fd = -1;
+
+ return 0;
+}
+
+int
+librbd_open(const char *name, struct rbd_ctx *ctx)
+{
+ return __librbd_open(name, ctx);
+}
+
+int
+__librbd_close(struct rbd_ctx *ctx)
+{
+ int ret;
+
+ ceph_assert(ctx->name && ctx->image);
+
+ ret = rbd_close(ctx->image);
+ if (ret < 0) {
+ prt("rbd_close(%s) failed\n", ctx->name);
+ return ret;
+ }
+
+ free((void *)ctx->name);
+
+ ctx->name = NULL;
+ ctx->image = NULL;
+
+ return 0;
+}
+
+int
+librbd_close(struct rbd_ctx *ctx)
+{
+ return __librbd_close(ctx);
+}
+
+int
+librbd_verify_object_map(struct rbd_ctx *ctx)
+{
+ int n;
+ uint64_t flags;
+ n = rbd_get_flags(ctx->image, &flags);
+ if (n < 0) {
+ prt("rbd_get_flags() failed\n");
+ return n;
+ }
+
+ if ((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0) {
+ prt("rbd_get_flags() indicates object map is invalid\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+ssize_t
+librbd_read(struct rbd_ctx *ctx, uint64_t off, size_t len, char *buf)
+{
+ ssize_t n;
+
+ n = rbd_read(ctx->image, off, len, buf);
+ if (n < 0)
+ prt("rbd_read(%llu, %zu) failed\n", off, len);
+
+ return n;
+}
+
+ssize_t
+librbd_write(struct rbd_ctx *ctx, uint64_t off, size_t len, const char *buf)
+{
+ ssize_t n;
+ int ret;
+
+ n = rbd_write(ctx->image, off, len, buf);
+ if (n < 0) {
+ prt("rbd_write(%llu, %zu) failed\n", off, len);
+ return n;
+ }
+
+ ret = librbd_verify_object_map(ctx);
+ if (ret < 0) {
+ return ret;
+ }
+ return n;
+}
+
+int
+librbd_flush(struct rbd_ctx *ctx)
+{
+ int ret;
+
+ ret = rbd_flush(ctx->image);
+ if (ret < 0) {
+ prt("rbd_flush failed\n");
+ return ret;
+ }
+
+ return librbd_verify_object_map(ctx);
+}
+
+int
+librbd_discard(struct rbd_ctx *ctx, uint64_t off, uint64_t len)
+{
+ int ret;
+
+ ret = rbd_discard(ctx->image, off, len);
+ if (ret < 0) {
+ prt("rbd_discard(%llu, %llu) failed\n", off, len);
+ return ret;
+ }
+
+ return librbd_verify_object_map(ctx);
+}
+
+ssize_t
+librbd_writesame(struct rbd_ctx *ctx, uint64_t off, size_t len,
+ const char *buf, size_t data_len)
+{
+ ssize_t n;
+ int ret;
+
+ n = rbd_writesame(ctx->image, off, len, buf, data_len, 0);
+ if (n < 0) {
+ prt("rbd_writesame(%llu, %zu) failed\n", off, len);
+ return n;
+ }
+
+ ret = librbd_verify_object_map(ctx);
+ if (ret < 0) {
+ return ret;
+ }
+ return n;
+}
+
+ssize_t
+librbd_compare_and_write(struct rbd_ctx *ctx, uint64_t off, size_t len,
+ const char *cmp_buf, const char *buf)
+{
+ ssize_t n;
+ int ret;
+ uint64_t mismatch_off = 0;
+
+ n = rbd_compare_and_write(ctx->image, off, len, cmp_buf, buf, &mismatch_off, 0);
+ if (n == -EINVAL) {
+ return n;
+ } else if (n < 0) {
+ prt("rbd_compare_and_write mismatch(%llu, %zu, %llu) failed\n",
+ off, len, mismatch_off);
+ return n;
+ }
+
+ ret = librbd_verify_object_map(ctx);
+ if (ret < 0) {
+ return ret;
+ }
+ return n;
+
+}
+
+int
+librbd_get_size(struct rbd_ctx *ctx, uint64_t *size)
+{
+ int ret;
+
+ ret = rbd_get_size(ctx->image, size);
+ if (ret < 0) {
+ prt("rbd_get_size failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+__librbd_resize(struct rbd_ctx *ctx, uint64_t size)
+{
+ int ret;
+
+ ret = rbd_resize(ctx->image, size);
+ if (ret < 0) {
+ prt("rbd_resize(%llu) failed\n", size);
+ return ret;
+ }
+
+ return librbd_verify_object_map(ctx);
+}
+
+int
+librbd_resize(struct rbd_ctx *ctx, uint64_t size)
+{
+ return __librbd_resize(ctx, size);
+}
+
+int
+__librbd_deep_copy(struct rbd_ctx *ctx, const char *src_snapname,
+ const char *dst_imagename, uint64_t features, int *order,
+ int stripe_unit, int stripe_count) {
+ int ret;
+
+ rbd_image_options_t opts;
+ rbd_image_options_create(&opts);
+ BOOST_SCOPE_EXIT_ALL( (&opts) ) {
+ rbd_image_options_destroy(opts);
+ };
+ ret = rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
+ features);
+ ceph_assert(ret == 0);
+ ret = rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
+ *order);
+ ceph_assert(ret == 0);
+ ret = rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
+ stripe_unit);
+ ceph_assert(ret == 0);
+ ret = rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
+ stripe_count);
+ ceph_assert(ret == 0);
+
+ ret = rbd_snap_set(ctx->image, src_snapname);
+ if (ret < 0) {
+ prt("rbd_snap_set(%s@%s) failed\n", ctx->name, src_snapname);
+ return ret;
+ }
+
+ ret = rbd_deep_copy(ctx->image, ioctx, dst_imagename, opts);
+ if (ret < 0) {
+ prt("rbd_deep_copy(%s@%s -> %s) failed\n",
+ ctx->name, src_snapname, dst_imagename);
+ return ret;
+ }
+
+ ret = rbd_snap_set(ctx->image, "");
+ if (ret < 0) {
+ prt("rbd_snap_set(%s@) failed\n", ctx->name);
+ return ret;
+ }
+
+ rbd_image_t image;
+ ret = rbd_open(ioctx, dst_imagename, &image, nullptr);
+ if (ret < 0) {
+ prt("rbd_open(%s) failed\n", dst_imagename);
+ return ret;
+ }
+
+ ret = rbd_snap_unprotect(image, src_snapname);
+ if (ret < 0) {
+ prt("rbd_snap_unprotect(%s@%s) failed\n", dst_imagename,
+ src_snapname);
+ return ret;
+ }
+
+ ret = rbd_snap_remove(image, src_snapname);
+ if (ret < 0) {
+ prt("rbd_snap_remove(%s@%s) failed\n", dst_imagename,
+ src_snapname);
+ return ret;
+ }
+
+ ret = rbd_close(image);
+ if (ret < 0) {
+ prt("rbd_close(%s) failed\n", dst_imagename);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+__librbd_clone(struct rbd_ctx *ctx, const char *src_snapname,
+ const char *dst_imagename, int *order, int stripe_unit,
+ int stripe_count)
+{
+ int ret;
+
+ ret = rbd_snap_create(ctx->image, src_snapname);
+ if (ret < 0) {
+ prt("rbd_snap_create(%s@%s) failed\n", ctx->name,
+ src_snapname);
+ return ret;
+ }
+
+ ret = rbd_snap_protect(ctx->image, src_snapname);
+ if (ret < 0) {
+ prt("rbd_snap_protect(%s@%s) failed\n", ctx->name,
+ src_snapname);
+ return ret;
+ }
+
+ uint64_t features;
+ ret = get_features(&features);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (deep_copy) {
+ ret = __librbd_deep_copy(ctx, src_snapname, dst_imagename, features,
+ order, stripe_unit, stripe_count);
+ if (ret < 0) {
+ prt("deep_copy(%s@%s -> %s) failed\n", ctx->name,
+ src_snapname, dst_imagename);
+ return ret;
+ }
+ } else {
+ ret = rbd_clone2(ioctx, ctx->name, src_snapname, ioctx,
+ dst_imagename, features, order,
+ stripe_unit, stripe_count);
+ if (ret < 0) {
+ prt("rbd_clone2(%s@%s -> %s) failed\n", ctx->name,
+ src_snapname, dst_imagename);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int
+librbd_clone(struct rbd_ctx *ctx, const char *src_snapname,
+ const char *dst_imagename, int *order, int stripe_unit,
+ int stripe_count)
+{
+ return __librbd_clone(ctx, src_snapname, dst_imagename, order,
+ stripe_unit, stripe_count);
+}
+
+int
+__librbd_flatten(struct rbd_ctx *ctx)
+{
+ int ret;
+
+ ret = rbd_flatten(ctx->image);
+ if (ret < 0) {
+ prt("rbd_flatten failed\n");
+ return ret;
+ }
+
+ return librbd_verify_object_map(ctx);
+}
+
+int
+librbd_flatten(struct rbd_ctx *ctx)
+{
+ return __librbd_flatten(ctx);
+}
+
+const struct rbd_operations librbd_operations = {
+ librbd_open,
+ librbd_close,
+ librbd_read,
+ librbd_write,
+ librbd_flush,
+ librbd_discard,
+ librbd_get_size,
+ librbd_resize,
+ librbd_clone,
+ librbd_flatten,
+ librbd_writesame,
+ librbd_compare_and_write,
+};
+
+#if defined(WITH_KRBD)
+int
+krbd_open(const char *name, struct rbd_ctx *ctx)
+{
+ char buf[1024];
+ char *devnode;
+ int fd;
+ int ret;
+
+ ret = __librbd_open(name, ctx);
+ if (ret < 0)
+ return ret;
+
+ ret = rados_conf_get(cluster, "rbd_default_map_options", buf,
+ sizeof(buf));
+ if (ret < 0) {
+ simple_err("Could not get rbd_default_map_options value", ret);
+ return ret;
+ }
+
+ ret = krbd_map(krbd, pool, "", name, "", buf, &devnode);
+ if (ret < 0) {
+ prt("krbd_map(%s) failed\n", name);
+ return ret;
+ }
+
+ fd = open(devnode, O_RDWR | o_direct);
+ if (fd < 0) {
+ ret = -errno;
+ prt("open(%s) failed\n", devnode);
+ return ret;
+ }
+
+ ctx->krbd_name = devnode;
+ ctx->krbd_fd = fd;
+
+ return 0;
+}
+
+int
+krbd_close(struct rbd_ctx *ctx)
+{
+ int ret;
+
+ ceph_assert(ctx->krbd_name && ctx->krbd_fd >= 0);
+
+ if (close(ctx->krbd_fd) < 0) {
+ ret = -errno;
+ prt("close(%s) failed\n", ctx->krbd_name);
+ return ret;
+ }
+
+ ret = krbd_unmap(krbd, ctx->krbd_name, "");
+ if (ret < 0) {
+ prt("krbd_unmap(%s) failed\n", ctx->krbd_name);
+ return ret;
+ }
+
+ free((void *)ctx->krbd_name);
+
+ ctx->krbd_name = NULL;
+ ctx->krbd_fd = -1;
+
+ return __librbd_close(ctx);
+}
+#endif // WITH_KRBD
+
+#if defined(__linux__)
+ssize_t
+krbd_read(struct rbd_ctx *ctx, uint64_t off, size_t len, char *buf)
+{
+ ssize_t n;
+
+ n = pread(ctx->krbd_fd, buf, len, off);
+ if (n < 0) {
+ n = -errno;
+ prt("pread(%llu, %zu) failed\n", off, len);
+ return n;
+ }
+
+ return n;
+}
+
+ssize_t
+krbd_write(struct rbd_ctx *ctx, uint64_t off, size_t len, const char *buf)
+{
+ ssize_t n;
+
+ n = pwrite(ctx->krbd_fd, buf, len, off);
+ if (n < 0) {
+ n = -errno;
+ prt("pwrite(%llu, %zu) failed\n", off, len);
+ return n;
+ }
+
+ return n;
+}
+
+int
+__krbd_flush(struct rbd_ctx *ctx, bool invalidate)
+{
+ int ret;
+
+ if (o_direct)
+ return 0;
+
+ /*
+ * BLKFLSBUF will sync the filesystem on top of the device (we
+ * don't care about that here, since we write directly to it),
+ * write out any dirty buffers and invalidate the buffer cache.
+ * It won't do a hardware cache flush.
+ *
+ * fsync() will write out any dirty buffers and do a hardware
+ * cache flush (which we don't care about either, because for
+ * krbd it's a noop). It won't try to empty the buffer cache
+ * nor poke the filesystem before writing out.
+ *
+ * Given that, for our purposes, fsync is a flush, while
+ * BLKFLSBUF is a flush+invalidate.
+ */
+ if (invalidate)
+ ret = ioctl(ctx->krbd_fd, BLKFLSBUF, NULL);
+ else
+ ret = fsync(ctx->krbd_fd);
+ if (ret < 0) {
+ ret = -errno;
+ prt("%s failed\n", invalidate ? "BLKFLSBUF" : "fsync");
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+krbd_flush(struct rbd_ctx *ctx)
+{
+ return __krbd_flush(ctx, false);
+}
+
+int
+krbd_discard(struct rbd_ctx *ctx, uint64_t off, uint64_t len)
+{
+ uint64_t range[2] = { off, len };
+ int ret;
+
+ /*
+ * BLKZEROOUT goes straight to disk and doesn't do anything
+ * about dirty buffers. This means we need to flush so that
+ *
+ * write 0..3M
+ * discard 1..2M
+ *
+ * results in "data 0000 data" rather than "data data data" on
+ * disk and invalidate so that
+ *
+ * discard 1..2M
+ * read 0..3M
+ *
+ * returns "data 0000 data" rather than "data data data" in
+ * case 1..2M was cached.
+ *
+ * Note: These cache coherency issues are supposed to be fixed
+ * in recent kernels.
+ */
+ ret = __krbd_flush(ctx, true);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * off and len must be 512-byte aligned, otherwise BLKZEROOUT
+ * will fail with -EINVAL. This means that -K (enable krbd
+ * mode) requires -h 512 or similar.
+ */
+ if (ioctl(ctx->krbd_fd, BLKZEROOUT, &range) < 0) {
+ ret = -errno;
+ prt("BLKZEROOUT(%llu, %llu) failed\n", off, len);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+krbd_get_size(struct rbd_ctx *ctx, uint64_t *size)
+{
+ uint64_t bytes;
+
+ if (ioctl(ctx->krbd_fd, BLKGETSIZE64, &bytes) < 0) {
+ int ret = -errno;
+ prt("BLKGETSIZE64 failed\n");
+ return ret;
+ }
+
+ *size = bytes;
+
+ return 0;
+}
+
+int
+krbd_resize(struct rbd_ctx *ctx, uint64_t size)
+{
+ int ret;
+ int count = 0;
+ uint64_t effective_size;
+
+ ceph_assert(size % truncbdy == 0);
+
+ /*
+ * When krbd detects a size change, it calls revalidate_disk(),
+ * which ends up calling invalidate_bdev(), which invalidates
+ * clean pages and does nothing about dirty pages beyond the
+ * new size. The preceding cache flush makes sure those pages
+ * are invalidated, which is what we need on shrink so that
+ *
+ * write 0..1M
+ * resize 0
+ * resize 2M
+ * read 0..2M
+ *
+ * returns "0000 0000" rather than "data 0000".
+ */
+ ret = __krbd_flush(ctx, false);
+ if (ret < 0)
+ return ret;
+
+ ret = __librbd_resize(ctx, size);
+ if (ret < 0)
+ return ret;
+
+ for (;;) {
+ ret = krbd_get_size(ctx, &effective_size);
+ if (ret < 0)
+ return ret;
+
+ if (effective_size == size)
+ break;
+
+ if (count++ >= 15) {
+ prt("BLKGETSIZE64 size error: expected 0x%llx, actual 0x%llx\n",
+ (unsigned long long)size,
+ (unsigned long long)effective_size);
+ return -EINVAL;
+ }
+
+ usleep(count * 250 * 1000);
+ }
+
+ return 0;
+}
+
+int
+krbd_clone(struct rbd_ctx *ctx, const char *src_snapname,
+ const char *dst_imagename, int *order, int stripe_unit,
+ int stripe_count)
+{
+ int ret;
+
+ ret = __krbd_flush(ctx, false);
+ if (ret < 0)
+ return ret;
+
+ return __librbd_clone(ctx, src_snapname, dst_imagename, order,
+ stripe_unit, stripe_count);
+}
+
+int
+krbd_flatten(struct rbd_ctx *ctx)
+{
+ int ret;
+
+ ret = __krbd_flush(ctx, false);
+ if (ret < 0)
+ return ret;
+
+ return __librbd_flatten(ctx);
+}
+#endif // __linux__
+
+#if defined(WITH_KRBD)
+const struct rbd_operations krbd_operations = {
+ krbd_open,
+ krbd_close,
+ krbd_read,
+ krbd_write,
+ krbd_flush,
+ krbd_discard,
+ krbd_get_size,
+ krbd_resize,
+ krbd_clone,
+ krbd_flatten,
+ NULL,
+};
+#endif // WITH_KRBD
+
+#if defined(__linux__)
+int
+nbd_open(const char *name, struct rbd_ctx *ctx)
+{
+ int r;
+ int fd;
+ char dev[4096];
+ char *devnode;
+
+ SubProcess process("rbd-nbd", SubProcess::KEEP, SubProcess::PIPE,
+ SubProcess::KEEP);
+ process.add_cmd_arg("map");
+ process.add_cmd_arg("--io-timeout=600");
+ std::string img;
+ img.append(pool);
+ img.append("/");
+ img.append(name);
+ process.add_cmd_arg(img.c_str());
+
+ r = __librbd_open(name, ctx);
+ if (r < 0)
+ return r;
+
+ r = process.spawn();
+ if (r < 0) {
+ prt("nbd_open failed to run rbd-nbd error: %s\n", process.err().c_str());
+ return r;
+ }
+ r = safe_read(process.get_stdout(), dev, sizeof(dev));
+ if (r < 0) {
+ prt("nbd_open failed to get nbd device path\n");
+ return r;
+ }
+ for (int i = 0; i < r; ++i)
+ if (dev[i] == 10 || dev[i] == 13)
+ dev[i] = 0;
+ dev[r] = 0;
+ r = process.join();
+ if (r) {
+ prt("rbd-nbd failed with error: %s", process.err().c_str());
+ return -EINVAL;
+ }
+
+ devnode = strdup(dev);
+ if (!devnode)
+ return -ENOMEM;
+
+ fd = open(devnode, O_RDWR | o_direct);
+ if (fd < 0) {
+ r = -errno;
+ prt("open(%s) failed\n", devnode);
+ return r;
+ }
+
+ ctx->krbd_name = devnode;
+ ctx->krbd_fd = fd;
+
+ return 0;
+}
+
+int
+nbd_close(struct rbd_ctx *ctx)
+{
+ int r;
+
+ ceph_assert(ctx->krbd_name && ctx->krbd_fd >= 0);
+
+ if (close(ctx->krbd_fd) < 0) {
+ r = -errno;
+ prt("close(%s) failed\n", ctx->krbd_name);
+ return r;
+ }
+
+ SubProcess process("rbd-nbd");
+ process.add_cmd_arg("unmap");
+ process.add_cmd_arg(ctx->krbd_name);
+
+ r = process.spawn();
+ if (r < 0) {
+ prt("nbd_close failed to run rbd-nbd error: %s\n", process.err().c_str());
+ return r;
+ }
+ r = process.join();
+ if (r) {
+ prt("rbd-nbd failed with error: %d", process.err().c_str());
+ return -EINVAL;
+ }
+
+ free((void *)ctx->krbd_name);
+
+ ctx->krbd_name = NULL;
+ ctx->krbd_fd = -1;
+
+ return __librbd_close(ctx);
+}
+
+int
+nbd_clone(struct rbd_ctx *ctx, const char *src_snapname,
+ const char *dst_imagename, int *order, int stripe_unit,
+ int stripe_count)
+{
+ int ret;
+
+ ret = __krbd_flush(ctx, false);
+ if (ret < 0)
+ return ret;
+
+ return __librbd_clone(ctx, src_snapname, dst_imagename, order,
+ stripe_unit, stripe_count);
+}
+
+const struct rbd_operations nbd_operations = {
+ nbd_open,
+ nbd_close,
+ krbd_read,
+ krbd_write,
+ krbd_flush,
+ krbd_discard,
+ krbd_get_size,
+ krbd_resize,
+ nbd_clone,
+ krbd_flatten,
+ NULL,
+};
+#endif // __linux__
+
+#if defined(__FreeBSD__)
+int
+ggate_open(const char *name, struct rbd_ctx *ctx)
+{
+ int r;
+ int fd;
+ char dev[4096];
+ char *devnode;
+
+ SubProcess process("rbd-ggate", SubProcess::KEEP, SubProcess::PIPE,
+ SubProcess::KEEP);
+ process.add_cmd_arg("map");
+ std::string img;
+ img.append(pool);
+ img.append("/");
+ img.append(name);
+ process.add_cmd_arg(img.c_str());
+
+ r = __librbd_open(name, ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ r = process.spawn();
+ if (r < 0) {
+ prt("ggate_open failed to run rbd-ggate: %s\n",
+ process.err().c_str());
+ return r;
+ }
+ r = safe_read(process.get_stdout(), dev, sizeof(dev));
+ if (r < 0) {
+ prt("ggate_open failed to get ggate device path\n");
+ return r;
+ }
+ for (int i = 0; i < r; ++i) {
+ if (dev[i] == '\r' || dev[i] == '\n') {
+ dev[i] = 0;
+ }
+ }
+ dev[r] = 0;
+ r = process.join();
+ if (r) {
+ prt("rbd-ggate failed with error: %s", process.err().c_str());
+ return -EINVAL;
+ }
+
+ devnode = strdup(dev);
+ if (!devnode) {
+ return -ENOMEM;
+ }
+
+ for (int i = 0; i < 100; i++) {
+ fd = open(devnode, O_RDWR | o_direct);
+ if (fd >= 0 || errno != ENOENT) {
+ break;
+ }
+ usleep(100000);
+ }
+ if (fd < 0) {
+ r = -errno;
+ prt("open(%s) failed\n", devnode);
+ return r;
+ }
+
+ ctx->krbd_name = devnode;
+ ctx->krbd_fd = fd;
+
+ return 0;
+}
+
+int
+ggate_close(struct rbd_ctx *ctx)
+{
+ int r;
+
+ ceph_assert(ctx->krbd_name && ctx->krbd_fd >= 0);
+
+ if (close(ctx->krbd_fd) < 0) {
+ r = -errno;
+ prt("close(%s) failed\n", ctx->krbd_name);
+ return r;
+ }
+
+ SubProcess process("rbd-ggate");
+ process.add_cmd_arg("unmap");
+ process.add_cmd_arg(ctx->krbd_name);
+
+ r = process.spawn();
+ if (r < 0) {
+ prt("ggate_close failed to run rbd-nbd: %s\n",
+ process.err().c_str());
+ return r;
+ }
+ r = process.join();
+ if (r) {
+ prt("rbd-ggate failed with error: %d", process.err().c_str());
+ return -EINVAL;
+ }
+
+ free((void *)ctx->krbd_name);
+
+ ctx->krbd_name = NULL;
+ ctx->krbd_fd = -1;
+
+ return __librbd_close(ctx);
+}
+
+ssize_t
+ggate_read(struct rbd_ctx *ctx, uint64_t off, size_t len, char *buf)
+{
+ ssize_t n;
+
+ n = pread(ctx->krbd_fd, buf, len, off);
+ if (n < 0) {
+ n = -errno;
+ prt("pread(%llu, %zu) failed\n", off, len);
+ return n;
+ }
+
+ return n;
+}
+
+ssize_t
+ggate_write(struct rbd_ctx *ctx, uint64_t off, size_t len, const char *buf)
+{
+ ssize_t n;
+
+ n = pwrite(ctx->krbd_fd, buf, len, off);
+ if (n < 0) {
+ n = -errno;
+ prt("pwrite(%llu, %zu) failed\n", off, len);
+ return n;
+ }
+
+ return n;
+}
+
+int
+__ggate_flush(struct rbd_ctx *ctx, bool invalidate)
+{
+ int ret;
+
+ if (o_direct) {
+ return 0;
+ }
+
+ if (invalidate) {
+ ret = ioctl(ctx->krbd_fd, DIOCGFLUSH, NULL);
+ } else {
+ ret = fsync(ctx->krbd_fd);
+ }
+ if (ret < 0) {
+ ret = -errno;
+ prt("%s failed\n", invalidate ? "DIOCGFLUSH" : "fsync");
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+ggate_flush(struct rbd_ctx *ctx)
+{
+ return __ggate_flush(ctx, false);
+}
+
+int
+ggate_discard(struct rbd_ctx *ctx, uint64_t off, uint64_t len)
+{
+ off_t range[2] = {static_cast<off_t>(off), static_cast<off_t>(len)};
+ int ret;
+
+ ret = __ggate_flush(ctx, true);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (ioctl(ctx->krbd_fd, DIOCGDELETE, &range) < 0) {
+ ret = -errno;
+ prt("DIOCGDELETE(%llu, %llu) failed\n", off, len);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+ggate_get_size(struct rbd_ctx *ctx, uint64_t *size)
+{
+ off_t bytes;
+
+ if (ioctl(ctx->krbd_fd, DIOCGMEDIASIZE, &bytes) < 0) {
+ int ret = -errno;
+ prt("DIOCGMEDIASIZE failed\n");
+ return ret;
+ }
+
+ *size = bytes;
+
+ return 0;
+}
+
+int
+ggate_resize(struct rbd_ctx *ctx, uint64_t size)
+{
+ int ret;
+
+ ceph_assert(size % truncbdy == 0);
+
+ ret = __ggate_flush(ctx, false);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return __librbd_resize(ctx, size);
+}
+
+int
+ggate_clone(struct rbd_ctx *ctx, const char *src_snapname,
+ const char *dst_imagename, int *order, int stripe_unit,
+ int stripe_count)
+{
+ int ret;
+
+ ret = __ggate_flush(ctx, false);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return __librbd_clone(ctx, src_snapname, dst_imagename, order,
+ stripe_unit, stripe_count);
+}
+
+int
+ggate_flatten(struct rbd_ctx *ctx)
+{
+ int ret;
+
+ ret = __ggate_flush(ctx, false);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return __librbd_flatten(ctx);
+}
+
+const struct rbd_operations ggate_operations = {
+ ggate_open,
+ ggate_close,
+ ggate_read,
+ ggate_write,
+ ggate_flush,
+ ggate_discard,
+ ggate_get_size,
+ ggate_resize,
+ ggate_clone,
+ ggate_flatten,
+ NULL,
+};
+#endif // __FreeBSD__
+
+struct rbd_ctx ctx = RBD_CTX_INIT;
+const struct rbd_operations *ops = &librbd_operations;
+
+static bool rbd_image_has_parent(struct rbd_ctx *ctx)
+{
+ int ret;
+ rbd_linked_image_spec_t parent_image;
+ rbd_snap_spec_t parent_snap;
+
+ ret = rbd_get_parent(ctx->image, &parent_image, &parent_snap);
+ if (ret < 0 && ret != -ENOENT) {
+ prterrcode("rbd_get_parent_info", ret);
+ exit(1);
+ }
+ rbd_linked_image_spec_cleanup(&parent_image);
+ rbd_snap_spec_cleanup(&parent_snap);
+
+ return !ret;
+}
+
+/*
+ * fsx
+ */
+
+void
+log4(int operation, int arg0, int arg1, int arg2)
+{
+ struct log_entry *le;
+
+ le = &oplog[logptr];
+ le->operation = operation;
+ if (closeopen)
+ le->operation = ~ le->operation;
+ le->args[0] = arg0;
+ le->args[1] = arg1;
+ le->args[2] = arg2;
+ logptr++;
+ logcount++;
+ if (logptr >= LOGSIZE)
+ logptr = 0;
+}
+
+void
+logdump(void)
+{
+ int i, count, down;
+ struct log_entry *lp;
+ const char *falloc_type[3] = {"PAST_EOF", "EXTENDING", "INTERIOR"};
+
+ prt("LOG DUMP (%d total operations):\n", logcount);
+ if (logcount < LOGSIZE) {
+ i = 0;
+ count = logcount;
+ } else {
+ i = logptr;
+ count = LOGSIZE;
+ }
+ for ( ; count > 0; count--) {
+ int opnum;
+
+ opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
+ prt("%d(%3d mod 256): ", opnum, opnum%256);
+ lp = &oplog[i];
+ if ((closeopen = lp->operation < 0))
+ lp->operation = ~ lp->operation;
+
+ switch (lp->operation) {
+ case OP_MAPREAD:
+ prt("MAPREAD 0x%x thru 0x%x\t(0x%x bytes)",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1]);
+ if (badoff >= lp->args[0] && badoff <
+ lp->args[0] + lp->args[1])
+ prt("\t***RRRR***");
+ break;
+ case OP_MAPWRITE:
+ prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1]);
+ if (badoff >= lp->args[0] && badoff <
+ lp->args[0] + lp->args[1])
+ prt("\t******WWWW");
+ break;
+ case OP_READ:
+ prt("READ 0x%x thru 0x%x\t(0x%x bytes)",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1]);
+ if (badoff >= lp->args[0] &&
+ badoff < lp->args[0] + lp->args[1])
+ prt("\t***RRRR***");
+ break;
+ case OP_WRITE:
+ prt("WRITE 0x%x thru 0x%x\t(0x%x bytes)",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1]);
+ if (lp->args[0] > lp->args[2])
+ prt(" HOLE");
+ else if (lp->args[0] + lp->args[1] > lp->args[2])
+ prt(" EXTEND");
+ if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
+ badoff < lp->args[0] + lp->args[1])
+ prt("\t***WWWW");
+ break;
+ case OP_TRUNCATE:
+ down = lp->args[0] < lp->args[1];
+ prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
+ down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
+ if (badoff >= lp->args[!down] &&
+ badoff < lp->args[!!down])
+ prt("\t******WWWW");
+ break;
+ case OP_FALLOCATE:
+ /* 0: offset 1: length 2: where alloced */
+ prt("FALLOC 0x%x thru 0x%x\t(0x%x bytes) %s",
+ lp->args[0], lp->args[0] + lp->args[1],
+ lp->args[1], falloc_type[lp->args[2]]);
+ if (badoff >= lp->args[0] &&
+ badoff < lp->args[0] + lp->args[1])
+ prt("\t******FFFF");
+ break;
+ case OP_PUNCH_HOLE:
+ prt("PUNCH 0x%x thru 0x%x\t(0x%x bytes)",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1]);
+ if (badoff >= lp->args[0] && badoff <
+ lp->args[0] + lp->args[1])
+ prt("\t******PPPP");
+ break;
+ case OP_WRITESAME:
+ prt("WRITESAME 0x%x thru 0x%x\t(0x%x bytes) data_size 0x%x",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1], lp->args[2]);
+ if (badoff >= lp->args[0] &&
+ badoff < lp->args[0] + lp->args[1])
+ prt("\t***WSWSWSWS");
+ break;
+ case OP_COMPARE_AND_WRITE:
+ prt("COMPARE_AND_WRITE 0x%x thru 0x%x\t(0x%x bytes)",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1]);
+ if (lp->args[0] > lp->args[2])
+ prt(" HOLE");
+ else if (lp->args[0] + lp->args[1] > lp->args[2])
+ prt(" EXTEND");
+ if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
+ badoff < lp->args[0] + lp->args[1])
+ prt("\t***WWWW");
+ break;
+ case OP_CLONE:
+ prt("CLONE");
+ break;
+ case OP_FLATTEN:
+ prt("FLATTEN");
+ break;
+ case OP_SKIPPED:
+ prt("SKIPPED (no operation)");
+ break;
+ default:
+ prt("BOGUS LOG ENTRY (operation code = %d)!",
+ lp->operation);
+ }
+ if (closeopen)
+ prt("\n\t\tCLOSE/OPEN");
+ prt("\n");
+ i++;
+ if (i == LOGSIZE)
+ i = 0;
+ }
+}
+
+void
+save_buffer(char *buffer, off_t bufferlength, int fd)
+{
+ off_t ret;
+ ssize_t byteswritten;
+
+ if (fd <= 0 || bufferlength == 0)
+ return;
+
+ if (bufferlength > SSIZE_MAX) {
+ prt("fsx flaw: overflow in save_buffer\n");
+ exit(67);
+ }
+
+ ret = lseek(fd, (off_t)0, SEEK_SET);
+ if (ret == (off_t)-1)
+ prterr("save_buffer: lseek 0");
+
+ byteswritten = write(fd, buffer, (size_t)bufferlength);
+ if (byteswritten != bufferlength) {
+ if (byteswritten == -1)
+ prterr("save_buffer write");
+ else
+ warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n",
+ (unsigned)byteswritten,
+ (unsigned long long)bufferlength);
+ }
+}
+
+
+void
+report_failure(int status)
+{
+ logdump();
+
+ if (fsxgoodfd) {
+ if (good_buf) {
+ save_buffer(good_buf, file_size, fsxgoodfd);
+ prt("Correct content saved for comparison\n");
+ prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n",
+ iname, iname);
+ }
+ close(fsxgoodfd);
+ }
+ sleep(3); // so the log can flush to disk. KLUDGEY!
+ exit(status);
+}
+
+#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
+ *(((unsigned char *)(cp)) + 1)))
+
+int
+fsxcmp(char *good_buf, char *temp_buf, unsigned size)
+{
+ if (!skip_partial_discard) {
+ return memcmp(good_buf, temp_buf, size);
+ }
+
+ for (unsigned i = 0; i < size; i++) {
+ if (good_buf[i] != temp_buf[i] && good_buf[i] != 0) {
+ return good_buf[i] - temp_buf[i];
+ }
+ }
+ return 0;
+}
+
+void
+check_buffers(char *good_buf, char *temp_buf, unsigned offset, unsigned size)
+{
+ if (fsxcmp(good_buf + offset, temp_buf, size) != 0) {
+ unsigned i = 0;
+ unsigned n = 0;
+
+ prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n",
+ offset, size, iname);
+ prt("OFFSET\tGOOD\tBAD\tRANGE\n");
+ while (size > 0) {
+ unsigned char c = good_buf[offset];
+ unsigned char t = temp_buf[i];
+ if (c != t) {
+ if (n < 16) {
+ unsigned bad = short_at(&temp_buf[i]);
+ prt("0x%5x\t0x%04x\t0x%04x", offset,
+ short_at(&good_buf[offset]), bad);
+ unsigned op = temp_buf[(offset & 1) ? i+1 : i];
+ prt("\t0x%5x\n", n);
+ if (op)
+ prt("operation# (mod 256) for "
+ "the bad data may be %u\n",
+ ((unsigned)op & 0xff));
+ else
+ prt("operation# (mod 256) for "
+ "the bad data unknown, check"
+ " HOLE and EXTEND ops\n");
+ }
+ n++;
+ badoff = offset;
+ }
+ offset++;
+ i++;
+ size--;
+ }
+ report_failure(110);
+ }
+}
+
+
+void
+check_size(void)
+{
+ uint64_t size;
+ int ret;
+
+ ret = ops->get_size(&ctx, &size);
+ if (ret < 0)
+ prterrcode("check_size: ops->get_size", ret);
+
+ if ((uint64_t)file_size != size) {
+ prt("Size error: expected 0x%llx stat 0x%llx\n",
+ (unsigned long long)file_size,
+ (unsigned long long)size);
+ report_failure(120);
+ }
+}
+
+#define TRUNC_HACK_SIZE (200ULL << 9) /* 512-byte aligned for krbd */
+
+void
+check_trunc_hack(void)
+{
+ uint64_t size;
+ int ret;
+
+ ret = ops->resize(&ctx, 0ULL);
+ if (ret < 0)
+ prterrcode("check_trunc_hack: ops->resize pre", ret);
+
+ ret = ops->resize(&ctx, TRUNC_HACK_SIZE);
+ if (ret < 0)
+ prterrcode("check_trunc_hack: ops->resize actual", ret);
+
+ ret = ops->get_size(&ctx, &size);
+ if (ret < 0)
+ prterrcode("check_trunc_hack: ops->get_size", ret);
+
+ if (size != TRUNC_HACK_SIZE) {
+ prt("no extend on truncate! not posix!\n");
+ exit(130);
+ }
+
+ ret = ops->resize(&ctx, 0ULL);
+ if (ret < 0)
+ prterrcode("check_trunc_hack: ops->resize post", ret);
+}
+
+int
+create_image()
+{
+ int r;
+ int order = 0;
+ char buf[32];
+ char client_name[256];
+
+ sprintf(client_name, "client.%s", client_id);
+
+ r = rados_create2(&cluster, cluster_name, client_name, 0);
+ if (r < 0) {
+ simple_err("Could not create cluster handle", r);
+ return r;
+ }
+ rados_conf_parse_env(cluster, NULL);
+ r = rados_conf_read_file(cluster, NULL);
+ if (r < 0) {
+ simple_err("Error reading ceph config file", r);
+ goto failed_shutdown;
+ }
+ r = rados_connect(cluster);
+ if (r < 0) {
+ simple_err("Error connecting to cluster", r);
+ goto failed_shutdown;
+ }
+#if defined(WITH_KRBD)
+ r = krbd_create_from_context(rados_cct(cluster), 0, &krbd);
+ if (r < 0) {
+ simple_err("Could not create libkrbd handle", r);
+ goto failed_shutdown;
+ }
+#endif
+
+ r = rados_pool_create(cluster, pool);
+ if (r < 0 && r != -EEXIST) {
+ simple_err("Error creating pool", r);
+ goto failed_krbd;
+ }
+ r = rados_ioctx_create(cluster, pool, &ioctx);
+ if (r < 0) {
+ simple_err("Error creating ioctx", r);
+ goto failed_krbd;
+ }
+ rados_application_enable(ioctx, "rbd", 1);
+
+ if (clone_calls || journal_replay) {
+ uint64_t features;
+ r = get_features(&features);
+ if (r < 0) {
+ goto failed_open;
+ }
+
+ r = rbd_create2(ioctx, iname, file_size, features, &order);
+ } else {
+ r = rbd_create(ioctx, iname, file_size, &order);
+ }
+ if (r < 0) {
+ simple_err("Error creating image", r);
+ goto failed_open;
+ }
+
+ if (journal_replay) {
+ r = register_journal(ioctx, iname);
+ if (r < 0) {
+ goto failed_open;
+ }
+ }
+
+ r = rados_conf_get(cluster, "rbd_skip_partial_discard", buf,
+ sizeof(buf));
+ if (r < 0) {
+ simple_err("Could not get rbd_skip_partial_discard value", r);
+ goto failed_open;
+ }
+ skip_partial_discard = (strcmp(buf, "true") == 0);
+
+ return 0;
+
+ failed_open:
+ rados_ioctx_destroy(ioctx);
+ failed_krbd:
+#if defined(WITH_KRBD)
+ krbd_destroy(krbd);
+#endif
+ failed_shutdown:
+ rados_shutdown(cluster);
+ return r;
+}
+
+void
+doflush(unsigned offset, unsigned size)
+{
+ int ret;
+
+ if (o_direct)
+ return;
+
+ ret = ops->flush(&ctx);
+ if (ret < 0)
+ prterrcode("doflush: ops->flush", ret);
+}
+
+void
+doread(unsigned offset, unsigned size)
+{
+ int ret;
+
+ offset -= offset % readbdy;
+ if (o_direct)
+ size -= size % readbdy;
+ if (size == 0) {
+ if (!quiet && testcalls > simulatedopcount && !o_direct)
+ prt("skipping zero size read\n");
+ log4(OP_SKIPPED, OP_READ, offset, size);
+ return;
+ }
+ if (size + offset > file_size) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping seek/read past end of file\n");
+ log4(OP_SKIPPED, OP_READ, offset, size);
+ return;
+ }
+
+ log4(OP_READ, offset, size, 0);
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ if (!quiet &&
+ ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug &&
+ (monitorstart == -1 ||
+ (static_cast<long>(offset + size) > monitorstart &&
+ (monitorend == -1 ||
+ static_cast<long>(offset) <= monitorend))))))
+ prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
+ offset, offset + size - 1, size);
+
+ ret = ops->read(&ctx, offset, size, temp_buf);
+ if (ret != (int)size) {
+ if (ret < 0)
+ prterrcode("doread: ops->read", ret);
+ else
+ prt("short read: 0x%x bytes instead of 0x%x\n",
+ ret, size);
+ report_failure(141);
+ }
+
+ check_buffers(good_buf, temp_buf, offset, size);
+}
+
+
+void
+check_eofpage(char *s, unsigned offset, char *p, int size)
+{
+ unsigned long last_page, should_be_zero;
+
+ if (offset + size <= (file_size & ~page_mask))
+ return;
+ /*
+ * we landed in the last page of the file
+ * test to make sure the VM system provided 0's
+ * beyond the true end of the file mapping
+ * (as required by mmap def in 1996 posix 1003.1)
+ */
+ last_page = ((unsigned long)p + (offset & page_mask) + size) & ~page_mask;
+
+ for (should_be_zero = last_page + (file_size & page_mask);
+ should_be_zero < last_page + page_size;
+ should_be_zero++)
+ if (*(char *)should_be_zero) {
+ prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n",
+ s, file_size - 1, should_be_zero & page_mask,
+ short_at(should_be_zero));
+ report_failure(205);
+ }
+}
+
+
+void
+gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
+{
+ while (size--) {
+ good_buf[offset] = testcalls % 256;
+ if (offset % 2)
+ good_buf[offset] += original_buf[offset];
+ offset++;
+ }
+}
+
+
+void
+dowrite(unsigned offset, unsigned size)
+{
+ ssize_t ret;
+ off_t newsize;
+
+ offset -= offset % writebdy;
+ if (o_direct)
+ size -= size % writebdy;
+ if (size == 0) {
+ if (!quiet && testcalls > simulatedopcount && !o_direct)
+ prt("skipping zero size write\n");
+ log4(OP_SKIPPED, OP_WRITE, offset, size);
+ return;
+ }
+
+ log4(OP_WRITE, offset, size, file_size);
+
+ gendata(original_buf, good_buf, offset, size);
+ if (file_size < offset + size) {
+ newsize = ceil(((double)offset + size) / truncbdy) * truncbdy;
+ if (file_size < newsize)
+ memset(good_buf + file_size, '\0', newsize - file_size);
+ file_size = newsize;
+ if (lite) {
+ warn("Lite file size bug in fsx!");
+ report_failure(149);
+ }
+ ret = ops->resize(&ctx, newsize);
+ if (ret < 0) {
+ prterrcode("dowrite: ops->resize", ret);
+ report_failure(150);
+ }
+ }
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ if (!quiet &&
+ ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug &&
+ (monitorstart == -1 ||
+ (static_cast<long>(offset + size) > monitorstart &&
+ (monitorend == -1 ||
+ static_cast<long>(offset) <= monitorend))))))
+ prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
+ offset, offset + size - 1, size);
+
+ ret = ops->write(&ctx, offset, size, good_buf + offset);
+ if (ret != (ssize_t)size) {
+ if (ret < 0)
+ prterrcode("dowrite: ops->write", ret);
+ else
+ prt("short write: 0x%x bytes instead of 0x%x\n",
+ ret, size);
+ report_failure(151);
+ }
+
+ if (flush_enabled)
+ doflush(offset, size);
+}
+
+
+void
+dotruncate(unsigned size)
+{
+ int oldsize = file_size;
+ int ret;
+
+ size -= size % truncbdy;
+ if (size > biggest) {
+ biggest = size;
+ if (!quiet && testcalls > simulatedopcount)
+ prt("truncating to largest ever: 0x%x\n", size);
+ }
+
+ log4(OP_TRUNCATE, size, (unsigned)file_size, 0);
+
+ if (size > file_size)
+ memset(good_buf + file_size, '\0', size - file_size);
+ else if (size < file_size)
+ memset(good_buf + size, '\0', file_size - size);
+ file_size = size;
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ if ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug && (monitorstart == -1 || monitorend == -1 ||
+ (long)size <= monitorend)))
+ prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
+
+ ret = ops->resize(&ctx, size);
+ if (ret < 0) {
+ prterrcode("dotruncate: ops->resize", ret);
+ report_failure(160);
+ }
+}
+
+void
+do_punch_hole(unsigned offset, unsigned length)
+{
+ unsigned end_offset;
+ int max_offset = 0;
+ int max_len = 0;
+ int ret;
+
+ offset -= offset % holebdy;
+ length -= length % holebdy;
+ if (length == 0) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping zero length punch hole\n");
+ log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
+ return;
+ }
+
+ if (file_size <= (loff_t)offset) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping hole punch off the end of the file\n");
+ log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
+ return;
+ }
+
+ end_offset = offset + length;
+
+ log4(OP_PUNCH_HOLE, offset, length, 0);
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ if ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug && (monitorstart == -1 || monitorend == -1 ||
+ (long)end_offset <= monitorend))) {
+ prt("%lu punch\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
+ offset, offset+length, length);
+ }
+
+ ret = ops->discard(&ctx, (unsigned long long)offset,
+ (unsigned long long)length);
+ if (ret < 0) {
+ prterrcode("do_punch_hole: ops->discard", ret);
+ report_failure(161);
+ }
+
+ max_offset = offset < file_size ? offset : file_size;
+ max_len = max_offset + length <= file_size ? length :
+ file_size - max_offset;
+ memset(good_buf + max_offset, '\0', max_len);
+}
+
+unsigned get_data_size(unsigned size)
+{
+ unsigned i;
+ unsigned hint;
+ unsigned max = sqrt((double)size) + 1;
+ unsigned good = 1;
+ unsigned curr = good;
+
+ hint = get_random() % max;
+
+ for (i = 1; i < max && curr < hint; i++) {
+ if (size % i == 0) {
+ good = curr;
+ curr = i;
+ }
+ }
+
+ if (curr == hint)
+ good = curr;
+
+ return good;
+}
+
+void
+dowritesame(unsigned offset, unsigned size)
+{
+ ssize_t ret;
+ off_t newsize;
+ unsigned buf_off;
+ unsigned data_size;
+ int n;
+
+ offset -= offset % writebdy;
+ if (o_direct)
+ size -= size % writebdy;
+ if (size == 0) {
+ if (!quiet && testcalls > simulatedopcount && !o_direct)
+ prt("skipping zero size writesame\n");
+ log4(OP_SKIPPED, OP_WRITESAME, offset, size);
+ return;
+ }
+
+ data_size = get_data_size(size);
+
+ log4(OP_WRITESAME, offset, size, data_size);
+
+ gendata(original_buf, good_buf, offset, data_size);
+ if (file_size < offset + size) {
+ newsize = ceil(((double)offset + size) / truncbdy) * truncbdy;
+ if (file_size < newsize)
+ memset(good_buf + file_size, '\0', newsize - file_size);
+ file_size = newsize;
+ if (lite) {
+ warn("Lite file size bug in fsx!");
+ report_failure(162);
+ }
+ ret = ops->resize(&ctx, newsize);
+ if (ret < 0) {
+ prterrcode("dowritesame: ops->resize", ret);
+ report_failure(163);
+ }
+ }
+
+ for (n = size / data_size, buf_off = data_size; n > 1; n--) {
+ memcpy(good_buf + offset + buf_off, good_buf + offset, data_size);
+ buf_off += data_size;
+ }
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ if (!quiet &&
+ ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug &&
+ (monitorstart == -1 ||
+ (static_cast<long>(offset + size) > monitorstart &&
+ (monitorend == -1 ||
+ static_cast<long>(offset) <= monitorend))))))
+ prt("%lu writesame\t0x%x thru\t0x%x\tdata_size\t0x%x(0x%x bytes)\n", testcalls,
+ offset, offset + size - 1, data_size, size);
+
+ ret = ops->writesame(&ctx, offset, size, good_buf + offset, data_size);
+ if (ret != (ssize_t)size) {
+ if (ret < 0)
+ prterrcode("dowritesame: ops->writesame", ret);
+ else
+ prt("short writesame: 0x%x bytes instead of 0x%x\n",
+ ret, size);
+ report_failure(164);
+ }
+
+ if (flush_enabled)
+ doflush(offset, size);
+}
+
+void
+docompareandwrite(unsigned offset, unsigned size)
+{
+ int ret;
+
+ if (skip_partial_discard) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("compare and write disabled\n");
+ log4(OP_SKIPPED, OP_COMPARE_AND_WRITE, offset, size);
+ return;
+ }
+
+ offset -= offset % writebdy;
+ if (o_direct)
+ size -= size % writebdy;
+
+ if (size == 0) {
+ if (!quiet && testcalls > simulatedopcount && !o_direct)
+ prt("skipping zero size read\n");
+ log4(OP_SKIPPED, OP_READ, offset, size);
+ return;
+ }
+
+ if (size + offset > file_size) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping seek/compare past end of file\n");
+ log4(OP_SKIPPED, OP_COMPARE_AND_WRITE, offset, size);
+ return;
+ }
+
+ memcpy(temp_buf + offset, good_buf + offset, size);
+ gendata(original_buf, good_buf, offset, size);
+ log4(OP_COMPARE_AND_WRITE, offset, size, 0);
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ if (!quiet &&
+ ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug &&
+ (monitorstart == -1 ||
+ (static_cast<long>(offset + size) > monitorstart &&
+ (monitorend == -1 ||
+ static_cast<long>(offset) <= monitorend))))))
+ prt("%lu compareandwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
+ offset, offset + size - 1, size);
+
+ ret = ops->compare_and_write(&ctx, offset, size, temp_buf + offset,
+ good_buf + offset);
+ if (ret != (ssize_t)size) {
+ if (ret == -EINVAL) {
+ memcpy(good_buf + offset, temp_buf + offset, size);
+ return;
+ }
+ if (ret < 0)
+ prterrcode("docompareandwrite: ops->compare_and_write", ret);
+ else
+ prt("short write: 0x%x bytes instead of 0x%x\n", ret, size);
+ report_failure(151);
+ return;
+ }
+
+ if (flush_enabled)
+ doflush(offset, size);
+}
+
+void clone_filename(char *buf, size_t len, int clones)
+{
+#if __GNUC__ && __GNUC__ >= 8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-truncation"
+#endif
+ snprintf(buf, len, "%s/fsx-%s-parent%d",
+ dirpath, iname, clones);
+#if __GNUC__ && __GNUC__ >= 8
+#pragma GCC diagnostic pop
+#endif
+}
+
+void clone_imagename(char *buf, size_t len, int clones)
+{
+ if (clones > 0) {
+ snprintf(buf, len, "%s-clone%d", iname, clones);
+ } else {
+ strncpy(buf, iname, len - 1);
+ buf[len - 1] = '\0';
+ }
+}
+
+void replay_imagename(char *buf, size_t len, int clones)
+{
+ clone_imagename(buf, len, clones);
+ strncat(buf, "-replay", len - strlen(buf));
+ buf[len - 1] = '\0';
+}
+
+void check_clone(int clonenum, bool replay_image);
+
+void
+do_clone()
+{
+ char filename[1024];
+ char imagename[1024];
+ char lastimagename[1024];
+ int ret, fd;
+ int order = 0, stripe_unit = 0, stripe_count = 0;
+ uint64_t newsize = file_size;
+
+ log4(OP_CLONE, 0, 0, 0);
+ ++num_clones;
+
+ if (randomize_striping) {
+ order = 18 + get_random() % 8;
+ stripe_unit = 1ull << (order - 1 - (get_random() % 8));
+ stripe_count = 2 + get_random() % 14;
+ }
+
+ prt("%lu clone\t%d order %d su %d sc %d\n", testcalls, num_clones,
+ order, stripe_unit, stripe_count);
+
+ clone_imagename(imagename, sizeof(imagename), num_clones);
+ clone_imagename(lastimagename, sizeof(lastimagename),
+ num_clones - 1);
+ ceph_assert(strcmp(lastimagename, ctx.name) == 0);
+
+ ret = ops->clone(&ctx, "snap", imagename, &order, stripe_unit,
+ stripe_count);
+ if (ret < 0) {
+ prterrcode("do_clone: ops->clone", ret);
+ exit(165);
+ }
+
+ if (randomize_parent_overlap && rbd_image_has_parent(&ctx)) {
+ int rand = get_random() % 16 + 1; // [1..16]
+
+ if (rand < 13) {
+ uint64_t overlap;
+
+ ret = rbd_get_overlap(ctx.image, &overlap);
+ if (ret < 0) {
+ prterrcode("do_clone: rbd_get_overlap", ret);
+ exit(1);
+ }
+
+ if (rand < 10) { // 9/16
+ newsize = overlap * ((double)rand / 10);
+ newsize -= newsize % truncbdy;
+ } else { // 3/16
+ newsize = 0;
+ }
+
+ ceph_assert(newsize != (uint64_t)file_size);
+ prt("truncating image %s from 0x%llx (overlap 0x%llx) to 0x%llx\n",
+ ctx.name, file_size, overlap, newsize);
+
+ ret = ops->resize(&ctx, newsize);
+ if (ret < 0) {
+ prterrcode("do_clone: ops->resize", ret);
+ exit(1);
+ }
+ } else if (rand < 15) { // 2/16
+ prt("flattening image %s\n", ctx.name);
+
+ ret = ops->flatten(&ctx);
+ if (ret < 0) {
+ prterrcode("do_clone: ops->flatten", ret);
+ exit(1);
+ }
+ } else { // 2/16
+ prt("leaving image %s intact\n", ctx.name);
+ }
+ }
+
+ clone_filename(filename, sizeof(filename), num_clones);
+ if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
+ simple_err("do_clone: open", -errno);
+ exit(162);
+ }
+ save_buffer(good_buf, newsize, fd);
+ if ((ret = close(fd)) < 0) {
+ simple_err("do_clone: close", -errno);
+ exit(163);
+ }
+
+ /*
+ * Close parent.
+ */
+ if ((ret = ops->close(&ctx)) < 0) {
+ prterrcode("do_clone: ops->close", ret);
+ exit(174);
+ }
+
+ if (journal_replay) {
+ ret = finalize_journal(ioctx, lastimagename, num_clones - 1,
+ order, stripe_unit, stripe_count);
+ if (ret < 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ ret = register_journal(ioctx, imagename);
+ if (ret < 0) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /*
+ * Open freshly made clone.
+ */
+ if ((ret = ops->open(imagename, &ctx)) < 0) {
+ prterrcode("do_clone: ops->open", ret);
+ exit(166);
+ }
+
+ if (num_clones > 1) {
+ if (journal_replay) {
+ check_clone(num_clones - 2, true);
+ }
+ check_clone(num_clones - 2, false);
+ }
+}
+
+void
+check_clone(int clonenum, bool replay_image)
+{
+ char filename[128];
+ char imagename[128];
+ int ret, fd;
+ struct rbd_ctx cur_ctx = RBD_CTX_INIT;
+ struct stat file_info;
+ char *good_buf, *temp_buf;
+
+ if (replay_image) {
+ replay_imagename(imagename, sizeof(imagename), clonenum);
+ } else {
+ clone_imagename(imagename, sizeof(imagename), clonenum);
+ }
+
+ if ((ret = ops->open(imagename, &cur_ctx)) < 0) {
+ prterrcode("check_clone: ops->open", ret);
+ exit(167);
+ }
+
+ clone_filename(filename, sizeof(filename), clonenum + 1);
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ simple_err("check_clone: open", -errno);
+ exit(168);
+ }
+
+ prt("checking clone #%d, image %s against file %s\n",
+ clonenum, imagename, filename);
+ if ((ret = fstat(fd, &file_info)) < 0) {
+ simple_err("check_clone: fstat", -errno);
+ exit(169);
+ }
+
+ good_buf = NULL;
+ ret = posix_memalign((void **)&good_buf,
+ std::max(writebdy, (int)sizeof(void *)),
+ file_info.st_size);
+ if (ret > 0) {
+ prterrcode("check_clone: posix_memalign(good_buf)", -ret);
+ exit(96);
+ }
+
+ temp_buf = NULL;
+ ret = posix_memalign((void **)&temp_buf,
+ std::max(readbdy, (int)sizeof(void *)),
+ file_info.st_size);
+ if (ret > 0) {
+ prterrcode("check_clone: posix_memalign(temp_buf)", -ret);
+ exit(97);
+ }
+
+ if ((ret = pread(fd, good_buf, file_info.st_size, 0)) < 0) {
+ simple_err("check_clone: pread", -errno);
+ exit(170);
+ }
+ if ((ret = ops->read(&cur_ctx, 0, file_info.st_size, temp_buf)) < 0) {
+ prterrcode("check_clone: ops->read", ret);
+ exit(171);
+ }
+ close(fd);
+ if ((ret = ops->close(&cur_ctx)) < 0) {
+ prterrcode("check_clone: ops->close", ret);
+ exit(174);
+ }
+ check_buffers(good_buf, temp_buf, 0, file_info.st_size);
+
+ if (!replay_image) {
+ unlink(filename);
+ }
+
+ free(good_buf);
+ free(temp_buf);
+}
+
+void
+writefileimage()
+{
+ ssize_t ret;
+
+ ret = ops->write(&ctx, 0, file_size, good_buf);
+ if (ret != file_size) {
+ if (ret < 0)
+ prterrcode("writefileimage: ops->write", ret);
+ else
+ prt("short write: 0x%x bytes instead of 0x%llx\n",
+ ret, (unsigned long long)file_size);
+ report_failure(172);
+ }
+
+ if (!lite) {
+ ret = ops->resize(&ctx, file_size);
+ if (ret < 0) {
+ prterrcode("writefileimage: ops->resize", ret);
+ report_failure(173);
+ }
+ }
+}
+
+void
+do_flatten()
+{
+ int ret;
+
+ if (!rbd_image_has_parent(&ctx)) {
+ log4(OP_SKIPPED, OP_FLATTEN, 0, 0);
+ return;
+ }
+ log4(OP_FLATTEN, 0, 0, 0);
+ prt("%lu flatten\n", testcalls);
+
+ ret = ops->flatten(&ctx);
+ if (ret < 0) {
+ prterrcode("writefileimage: ops->flatten", ret);
+ exit(177);
+ }
+}
+
+void
+docloseopen(void)
+{
+ char *name;
+ int ret;
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ name = strdup(ctx.name);
+
+ if (debug)
+ prt("%lu close/open\n", testcalls);
+
+ ret = ops->close(&ctx);
+ if (ret < 0) {
+ prterrcode("docloseopen: ops->close", ret);
+ report_failure(180);
+ }
+
+ ret = ops->open(name, &ctx);
+ if (ret < 0) {
+ prterrcode("docloseopen: ops->open", ret);
+ report_failure(181);
+ }
+
+ free(name);
+}
+
+#define TRIM_OFF_LEN(off, len, size) \
+do { \
+ if (size) \
+ (off) %= (size); \
+ else \
+ (off) = 0; \
+ if ((unsigned)(off) + (unsigned)(len) > (unsigned)(size)) \
+ (len) = (size) - (off); \
+} while (0)
+
+void
+test(void)
+{
+ unsigned long offset;
+ unsigned long size = maxoplen;
+ unsigned long rv = get_random();
+ unsigned long op;
+
+ if (simulatedopcount > 0 && testcalls == simulatedopcount)
+ writefileimage();
+
+ testcalls++;
+
+ if (closeprob)
+ closeopen = (rv >> 3) < (1u << 28) / (unsigned)closeprob;
+
+ if (debugstart > 0 && testcalls >= debugstart)
+ debug = 1;
+
+ if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
+ prt("%lu...\n", testcalls);
+
+ offset = get_random();
+ if (randomoplen)
+ size = get_random() % (maxoplen + 1);
+
+ /* calculate appropriate op to run */
+ if (lite)
+ op = rv % OP_MAX_LITE;
+ else
+ op = rv % OP_MAX_FULL;
+
+ switch (op) {
+ case OP_MAPREAD:
+ if (!mapped_reads)
+ op = OP_READ;
+ break;
+ case OP_MAPWRITE:
+ if (!mapped_writes)
+ op = OP_WRITE;
+ break;
+ case OP_FALLOCATE:
+ if (!fallocate_calls) {
+ log4(OP_SKIPPED, OP_FALLOCATE, offset, size);
+ goto out;
+ }
+ break;
+ case OP_PUNCH_HOLE:
+ if (!punch_hole_calls) {
+ log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, size);
+ goto out;
+ }
+ break;
+ case OP_CLONE:
+ /* clone, 8% chance */
+ if (!clone_calls || file_size == 0 || get_random() % 100 >= 8) {
+ log4(OP_SKIPPED, OP_CLONE, 0, 0);
+ goto out;
+ }
+ break;
+ case OP_FLATTEN:
+ /* flatten four times as rarely as clone, 2% chance */
+ if (get_random() % 100 >= 2) {
+ log4(OP_SKIPPED, OP_FLATTEN, 0, 0);
+ goto out;
+ }
+ break;
+ case OP_WRITESAME:
+ /* writesame not implemented */
+ if (!ops->writesame) {
+ log4(OP_SKIPPED, OP_WRITESAME, offset, size);
+ goto out;
+ }
+ break;
+ case OP_COMPARE_AND_WRITE:
+ /* compare_and_write not implemented */
+ if (!ops->compare_and_write) {
+ log4(OP_SKIPPED, OP_COMPARE_AND_WRITE, offset, size);
+ goto out;
+ }
+ break;
+ }
+
+ switch (op) {
+ case OP_READ:
+ TRIM_OFF_LEN(offset, size, file_size);
+ doread(offset, size);
+ break;
+
+ case OP_WRITE:
+ TRIM_OFF_LEN(offset, size, maxfilelen);
+ dowrite(offset, size);
+ break;
+
+ case OP_MAPREAD:
+ TRIM_OFF_LEN(offset, size, file_size);
+ exit(183);
+ break;
+
+ case OP_MAPWRITE:
+ TRIM_OFF_LEN(offset, size, maxfilelen);
+ exit(182);
+ break;
+
+ case OP_TRUNCATE:
+ if (!style)
+ size = get_random() % maxfilelen;
+ dotruncate(size);
+ break;
+
+ case OP_PUNCH_HOLE:
+ TRIM_OFF_LEN(offset, size, file_size);
+ do_punch_hole(offset, size);
+ break;
+
+ case OP_WRITESAME:
+ TRIM_OFF_LEN(offset, size, maxfilelen);
+ dowritesame(offset, size);
+ break;
+ case OP_COMPARE_AND_WRITE:
+ TRIM_OFF_LEN(offset, size, file_size);
+ docompareandwrite(offset, size);
+ break;
+
+ case OP_CLONE:
+ do_clone();
+ break;
+
+ case OP_FLATTEN:
+ do_flatten();
+ break;
+
+ default:
+ prterr("test: unknown operation");
+ report_failure(42);
+ break;
+ }
+
+out:
+ if (sizechecks && testcalls > simulatedopcount)
+ check_size();
+ if (closeopen)
+ docloseopen();
+}
+
+
+void
+cleanup(int sig)
+{
+ if (sig)
+ prt("signal %d\n", sig);
+ prt("testcalls = %lu\n", testcalls);
+ exit(sig);
+}
+
+
+void
+usage(void)
+{
+ fprintf(stdout, "usage: %s",
+ "fsx [-dfjknqxyACFHKLORUWZ] [-b opnum] [-c Prob] [-h holebdy] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] pname iname\n\
+ -b opnum: beginning operation number (default 1)\n\
+ -c P: 1 in P chance of file close+open at each op (default infinity)\n\
+ -d: debug output for all operations\n\
+ -f: flush and invalidate cache after I/O\n\
+ -g: deep copy instead of clone\n\
+ -h holebdy: 4096 would make discards page aligned (default 1)\n\
+ -j: journal replay stress test\n\
+ -k: keep data on success (default 0)\n\
+ -l flen: the upper bound on file size (default 262144)\n\
+ -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
+ -n: no verifications of file size\n\
+ -o oplen: the upper bound on operation size (default 65536)\n\
+ -p progressinterval: debug output at specified operation interval\n\
+ -q: quieter operation\n\
+ -r readbdy: 4096 would make reads page aligned (default 1)\n\
+ -s style: 1 gives smaller truncates (default 0)\n\
+ -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
+ -w writebdy: 4096 would make writes page aligned (default 1)\n\
+ -x: preallocate file space before starting, XFS only (default 0)\n\
+ -y: synchronize changes to a file\n"
+
+" -C: do not use clone calls\n\
+ -D startingop: debug output starting at specified operation\n"
+#ifdef FALLOCATE
+" -F: Do not use fallocate (preallocation) calls\n"
+#endif
+#if defined(__FreeBSD__)
+" -G: enable rbd-ggate mode (use -L, -r and -w too)\n"
+#endif
+" -H: do not use punch hole calls\n"
+#if defined(WITH_KRBD)
+" -K: enable krbd mode (use -t and -h too)\n"
+#endif
+#if defined(__linux__)
+" -M: enable rbd-nbd mode (use -t and -h too)\n"
+#endif
+" -L: fsxLite - no file creations & no file size changes\n\
+ -N numops: total # operations to do (default infinity)\n\
+ -O: use oplen (see -o flag) for every op (default random)\n\
+ -P dirpath: save .fsxlog and .fsxgood files in dirpath (default ./)\n\
+ -R: read() system calls only (mapped reads disabled)\n\
+ -S seed: for random # generator (default 1) 0 gets timestamp\n\
+ -U: disable randomized striping\n\
+ -W: mapped write operations DISabled\n\
+ -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
+ poolname: this is REQUIRED (no default)\n\
+ imagename: this is REQUIRED (no default)\n");
+ exit(89);
+}
+
+
+int
+getnum(char *s, char **e)
+{
+ int ret;
+
+ *e = (char *) 0;
+ ret = strtol(s, e, 0);
+ if (*e)
+ switch (**e) {
+ case 'b':
+ case 'B':
+ ret *= 512;
+ *e = *e + 1;
+ break;
+ case 'k':
+ case 'K':
+ ret *= 1024;
+ *e = *e + 1;
+ break;
+ case 'm':
+ case 'M':
+ ret *= 1024*1024;
+ *e = *e + 1;
+ break;
+ case 'w':
+ case 'W':
+ ret *= 4;
+ *e = *e + 1;
+ break;
+ }
+ return (ret);
+}
+
+void
+test_fallocate()
+{
+#ifdef FALLOCATE
+ if (!lite && fallocate_calls) {
+ if (fallocate(fd, 0, 0, 1) && errno == EOPNOTSUPP) {
+ if(!quiet)
+ warn("main: filesystem does not support fallocate, disabling\n");
+ fallocate_calls = 0;
+ } else {
+ ftruncate(fd, 0);
+ }
+ }
+#else /* ! FALLOCATE */
+ fallocate_calls = 0;
+#endif
+
+}
+
+void remove_image(rados_ioctx_t ioctx, char *imagename, bool remove_snap,
+ bool unregister) {
+ rbd_image_t image;
+ char errmsg[128];
+ int ret;
+
+ if ((ret = rbd_open(ioctx, imagename, &image, NULL)) < 0) {
+ sprintf(errmsg, "rbd_open %s", imagename);
+ prterrcode(errmsg, ret);
+ report_failure(101);
+ }
+ if (remove_snap) {
+ if ((ret = rbd_snap_unprotect(image, "snap")) < 0) {
+ sprintf(errmsg, "rbd_snap_unprotect %s@snap",
+ imagename);
+ prterrcode(errmsg, ret);
+ report_failure(102);
+ }
+ if ((ret = rbd_snap_remove(image, "snap")) < 0) {
+ sprintf(errmsg, "rbd_snap_remove %s@snap",
+ imagename);
+ prterrcode(errmsg, ret);
+ report_failure(103);
+ }
+ }
+ if ((ret = rbd_close(image)) < 0) {
+ sprintf(errmsg, "rbd_close %s", imagename);
+ prterrcode(errmsg, ret);
+ report_failure(104);
+ }
+
+ if (unregister &&
+ (ret = unregister_journal(ioctx, imagename)) < 0) {
+ report_failure(105);
+ }
+
+ if ((ret = rbd_remove(ioctx, imagename)) < 0) {
+ sprintf(errmsg, "rbd_remove %s", imagename);
+ prterrcode(errmsg, ret);
+ report_failure(106);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ enum {
+ LONG_OPT_CLUSTER = 1000,
+ LONG_OPT_ID = 1001
+ };
+
+ int i, style, ch, ret;
+ char *endp;
+ char goodfile[1024];
+ char logfile[1024];
+
+ const char* optstring = "b:c:dfgh:jkl:m:no:p:qr:s:t:w:xyCD:FGHKMLN:OP:RS:UWZ";
+ const struct option longopts[] = {
+ {"cluster", 1, NULL, LONG_OPT_CLUSTER},
+ {"id", 1, NULL, LONG_OPT_ID}};
+
+ goodfile[0] = 0;
+ logfile[0] = 0;
+
+ page_size = PAGE_SIZE;
+ page_mask = page_size - 1;
+ mmap_mask = page_mask;
+
+ setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
+
+ while ((ch = getopt_long(argc, argv, optstring, longopts, NULL)) != EOF) {
+ switch (ch) {
+ case LONG_OPT_CLUSTER:
+ cluster_name = optarg;
+ break;
+ case LONG_OPT_ID:
+ client_id = optarg;
+ break;
+ case 'b':
+ simulatedopcount = getnum(optarg, &endp);
+ if (!quiet)
+ fprintf(stdout, "Will begin at operation %lu\n",
+ simulatedopcount);
+ if (simulatedopcount == 0)
+ usage();
+ simulatedopcount -= 1;
+ break;
+ case 'c':
+ closeprob = getnum(optarg, &endp);
+ if (!quiet)
+ fprintf(stdout,
+ "Chance of close/open is 1 in %d\n",
+ closeprob);
+ if (closeprob <= 0)
+ usage();
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ flush_enabled = 1;
+ break;
+ case 'g':
+ deep_copy = 1;
+ break;
+ case 'h':
+ holebdy = getnum(optarg, &endp);
+ if (holebdy <= 0)
+ usage();
+ break;
+ case 'j':
+ journal_replay = true;
+ break;
+ case 'k':
+ keep_on_success = 1;
+ break;
+ case 'l':
+ {
+ int _num = getnum(optarg, &endp);
+ if (_num <= 0)
+ usage();
+ maxfilelen = _num;
+ }
+ break;
+ case 'm':
+ monitorstart = getnum(optarg, &endp);
+ if (monitorstart < 0)
+ usage();
+ if (!endp || *endp++ != ':')
+ usage();
+ monitorend = getnum(endp, &endp);
+ if (monitorend < 0)
+ usage();
+ if (monitorend == 0)
+ monitorend = -1; /* aka infinity */
+ debug = 1;
+ break;
+ case 'n':
+ sizechecks = 0;
+ break;
+ case 'o':
+ maxoplen = getnum(optarg, &endp);
+ if (maxoplen <= 0)
+ usage();
+ break;
+ case 'p':
+ progressinterval = getnum(optarg, &endp);
+ if (progressinterval == 0)
+ usage();
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'r':
+ readbdy = getnum(optarg, &endp);
+ if (readbdy <= 0)
+ usage();
+ break;
+ case 's':
+ style = getnum(optarg, &endp);
+ if (style < 0 || style > 1)
+ usage();
+ break;
+ case 't':
+ truncbdy = getnum(optarg, &endp);
+ if (truncbdy <= 0)
+ usage();
+ break;
+ case 'w':
+ writebdy = getnum(optarg, &endp);
+ if (writebdy <= 0)
+ usage();
+ break;
+ case 'x':
+ prealloc = 1;
+ break;
+ case 'y':
+ do_fsync = 1;
+ break;
+ case 'C':
+ clone_calls = 0;
+ break;
+ case 'D':
+ debugstart = getnum(optarg, &endp);
+ if (debugstart < 1)
+ usage();
+ break;
+ case 'F':
+ fallocate_calls = 0;
+ break;
+#if defined(__FreeBSD__)
+ case 'G':
+ prt("rbd-ggate mode enabled\n");
+ ops = &ggate_operations;
+ break;
+#endif
+ case 'H':
+ punch_hole_calls = 0;
+ break;
+#if defined(WITH_KRBD)
+ case 'K':
+ prt("krbd mode enabled\n");
+ ops = &krbd_operations;
+ break;
+#endif
+#if defined(__linux__)
+ case 'M':
+ prt("rbd-nbd mode enabled\n");
+ ops = &nbd_operations;
+ break;
+#endif
+ case 'L':
+ lite = 1;
+ break;
+ case 'N':
+ numops = getnum(optarg, &endp);
+ if (numops < 0)
+ usage();
+ break;
+ case 'O':
+ randomoplen = 0;
+ break;
+ case 'P':
+ strncpy(dirpath, optarg, sizeof(dirpath)-1);
+ dirpath[sizeof(dirpath)-1] = '\0';
+ strncpy(goodfile, dirpath, sizeof(goodfile)-1);
+ goodfile[sizeof(goodfile)-1] = '\0';
+ if (strlen(goodfile) < sizeof(goodfile)-2) {
+ strcat(goodfile, "/");
+ } else {
+ prt("file name to long\n");
+ exit(1);
+ }
+ strncpy(logfile, dirpath, sizeof(logfile)-1);
+ logfile[sizeof(logfile)-1] = '\0';
+ if (strlen(logfile) < sizeof(logfile)-2) {
+ strcat(logfile, "/");
+ } else {
+ prt("file path to long\n");
+ exit(1);
+ }
+ break;
+ case 'R':
+ mapped_reads = 0;
+ if (!quiet)
+ fprintf(stdout, "mapped reads DISABLED\n");
+ break;
+ case 'S':
+ seed = getnum(optarg, &endp);
+ if (seed == 0)
+ seed = std::random_device()() % 10000;
+ if (!quiet)
+ fprintf(stdout, "Seed set to %d\n", seed);
+ if (seed < 0)
+ usage();
+ break;
+ case 'U':
+ randomize_striping = 0;
+ break;
+ case 'W':
+ mapped_writes = 0;
+ if (!quiet)
+ fprintf(stdout, "mapped writes DISABLED\n");
+ break;
+ case 'Z':
+ #ifdef O_DIRECT
+ o_direct = O_DIRECT;
+ #endif
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 2)
+ usage();
+ pool = argv[0];
+ iname = argv[1];
+
+ #ifndef _WIN32
+ signal(SIGHUP, cleanup);
+ signal(SIGINT, cleanup);
+ signal(SIGPIPE, cleanup);
+ signal(SIGALRM, cleanup);
+ signal(SIGTERM, cleanup);
+ signal(SIGXCPU, cleanup);
+ signal(SIGXFSZ, cleanup);
+ signal(SIGVTALRM, cleanup);
+ signal(SIGUSR1, cleanup);
+ signal(SIGUSR2, cleanup);
+ #endif
+
+ random_generator.seed(seed);
+
+ if (lite) {
+ file_size = maxfilelen;
+ }
+
+ ret = create_image();
+ if (ret < 0) {
+ prterrcode(iname, ret);
+ exit(90);
+ }
+ ret = ops->open(iname, &ctx);
+ if (ret < 0) {
+ simple_err("Error opening image", ret);
+ exit(91);
+ }
+ if (!dirpath[0])
+ strcat(dirpath, ".");
+ strncat(goodfile, iname, 256);
+ strcat (goodfile, ".fsxgood");
+ fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (fsxgoodfd < 0) {
+ prterr(goodfile);
+ exit(92);
+ }
+ strncat(logfile, iname, 256);
+ strcat (logfile, ".fsxlog");
+ fsxlogf = fopen(logfile, "w");
+ if (fsxlogf == NULL) {
+ prterr(logfile);
+ exit(93);
+ }
+
+ original_buf = (char *) malloc(maxfilelen);
+ for (i = 0; i < (int)maxfilelen; i++)
+ original_buf[i] = get_random() % 256;
+
+ ret = posix_memalign((void **)&good_buf,
+ std::max(writebdy, (int)sizeof(void *)), maxfilelen);
+ if (ret > 0) {
+ if (ret == EINVAL)
+ prt("writebdy is not a suitable power of two\n");
+ else
+ prterrcode("main: posix_memalign(good_buf)", -ret);
+ exit(94);
+ }
+ memset(good_buf, '\0', maxfilelen);
+
+ ret = posix_memalign((void **)&temp_buf,
+ std::max(readbdy, (int)sizeof(void *)), maxfilelen);
+ if (ret > 0) {
+ if (ret == EINVAL)
+ prt("readbdy is not a suitable power of two\n");
+ else
+ prterrcode("main: posix_memalign(temp_buf)", -ret);
+ exit(95);
+ }
+ memset(temp_buf, '\0', maxfilelen);
+
+ if (lite) { /* zero entire existing file */
+ ssize_t written;
+
+ written = ops->write(&ctx, 0, (size_t)maxfilelen, good_buf);
+ if (written != (ssize_t)maxfilelen) {
+ if (written < 0) {
+ prterrcode(iname, written);
+ warn("main: error on write");
+ } else
+ warn("main: short write, 0x%x bytes instead "
+ "of 0x%lx\n",
+ (unsigned)written,
+ maxfilelen);
+ exit(98);
+ }
+ } else
+ check_trunc_hack();
+
+ //test_fallocate();
+
+ while (numops == -1 || numops--)
+ test();
+
+ ret = ops->close(&ctx);
+ if (ret < 0) {
+ prterrcode("ops->close", ret);
+ report_failure(99);
+ }
+
+ if (journal_replay) {
+ char imagename[1024];
+ clone_imagename(imagename, sizeof(imagename), num_clones);
+ ret = finalize_journal(ioctx, imagename, num_clones, 0, 0, 0);
+ if (ret < 0) {
+ report_failure(100);
+ }
+ }
+
+ if (num_clones > 0) {
+ if (journal_replay) {
+ check_clone(num_clones - 1, true);
+ }
+ check_clone(num_clones - 1, false);
+ }
+
+ if (!keep_on_success) {
+ while (num_clones >= 0) {
+ static bool remove_snap = false;
+
+ if (journal_replay) {
+ char replayimagename[1024];
+ replay_imagename(replayimagename,
+ sizeof(replayimagename),
+ num_clones);
+ remove_image(ioctx, replayimagename,
+ remove_snap,
+ false);
+ }
+
+ char clonename[128];
+ clone_imagename(clonename, 128, num_clones);
+ remove_image(ioctx, clonename, remove_snap,
+ journal_replay);
+
+ remove_snap = true;
+ num_clones--;
+ }
+ }
+
+ prt("All operations completed A-OK!\n");
+ fclose(fsxlogf);
+
+ rados_ioctx_destroy(ioctx);
+#if defined(WITH_KRBD)
+ krbd_destroy(krbd);
+#endif
+ rados_shutdown(cluster);
+
+ free(original_buf);
+ free(good_buf);
+ free(temp_buf);
+
+ exit(0);
+ return 0;
+}
diff --git a/src/test/librbd/image/test_mock_AttachChildRequest.cc b/src/test/librbd/image/test_mock_AttachChildRequest.cc
new file mode 100644
index 000000000..66d594eb0
--- /dev/null
+++ b/src/test/librbd/image/test_mock_AttachChildRequest.cc
@@ -0,0 +1,275 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/image/AttachChildRequest.h"
+#include "librbd/image/RefreshRequest.h"
+#include "librbd/internal.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct RefreshRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static RefreshRequest* s_instance;
+ static RefreshRequest* create(MockTestImageCtx &image_ctx,
+ bool acquiring_lock, bool skip_open_parent,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ RefreshRequest() {
+ s_instance = this;
+ }
+};
+
+RefreshRequest<MockTestImageCtx>* RefreshRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/AttachChildRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageAttachChildRequest : public TestMockFixture {
+public:
+ typedef AttachChildRequest<MockTestImageCtx> MockAttachChildRequest;
+ typedef RefreshRequest<MockTestImageCtx> MockRefreshRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap", 0, prog_ctx));
+ if (is_feature_enabled(RBD_FEATURE_LAYERING)) {
+ ASSERT_EQ(0, image_ctx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace{}, "snap"));
+
+ uint64_t snap_id = image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace{}, "snap"}];
+ ASSERT_NE(CEPH_NOSNAP, snap_id);
+
+ C_SaferCond ctx;
+ image_ctx->state->snap_set(snap_id, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+ }
+ }
+
+ void expect_add_child(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("add_child"), _, _, _,
+ _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_refresh(MockRefreshRequest& mock_refresh_request, int r) {
+ EXPECT_CALL(mock_refresh_request, send())
+ .WillOnce(Invoke([this, &mock_refresh_request, r]() {
+ image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r);
+ }));
+ }
+
+ void expect_is_snap_protected(MockImageCtx &mock_image_ctx, bool is_protected,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, is_snap_protected(_, _))
+ .WillOnce(WithArg<1>(Invoke([is_protected, r](bool* is_prot) {
+ *is_prot = is_protected;
+ return r;
+ })));
+ }
+
+ void expect_op_features_set(MockImageCtx &mock_image_ctx, int r) {
+ bufferlist bl;
+ encode(static_cast<uint64_t>(RBD_OPERATION_FEATURE_CLONE_CHILD), bl);
+ encode(static_cast<uint64_t>(RBD_OPERATION_FEATURE_CLONE_CHILD), bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(util::header_name(mock_image_ctx.id), _, StrEq("rbd"),
+ StrEq("op_features_set"), ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_child_attach(MockImageCtx &mock_image_ctx, int r) {
+ bufferlist bl;
+ encode(mock_image_ctx.snap_id, bl);
+ encode(cls::rbd::ChildImageSpec{m_ioctx.get_id(), "", mock_image_ctx.id},
+ bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("child_attach"), ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ librbd::ImageCtx *image_ctx;
+};
+
+TEST_F(TestMockImageAttachChildRequest, SuccessV1) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+
+ expect_add_child(mock_image_ctx, 0);
+
+ MockRefreshRequest mock_refresh_request;
+ expect_refresh(mock_refresh_request, 0);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ C_SaferCond ctx;
+ auto req = MockAttachChildRequest::create(&mock_image_ctx, &mock_image_ctx,
+ image_ctx->snap_id, nullptr, 0, 1,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachChildRequest, SuccessV2) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+
+ expect_op_features_set(mock_image_ctx, 0);
+ expect_child_attach(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockAttachChildRequest::create(&mock_image_ctx, &mock_image_ctx,
+ image_ctx->snap_id, nullptr, 0, 2,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachChildRequest, AddChildError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+
+ expect_add_child(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockAttachChildRequest::create(&mock_image_ctx, &mock_image_ctx,
+ image_ctx->snap_id, nullptr, 0, 1,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachChildRequest, RefreshError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+
+ expect_add_child(mock_image_ctx, 0);
+
+ MockRefreshRequest mock_refresh_request;
+ expect_refresh(mock_refresh_request, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockAttachChildRequest::create(&mock_image_ctx, &mock_image_ctx,
+ image_ctx->snap_id, nullptr, 0, 1,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachChildRequest, ValidateProtectedFailed) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+
+ expect_add_child(mock_image_ctx, 0);
+
+ MockRefreshRequest mock_refresh_request;
+ expect_refresh(mock_refresh_request, 0);
+ expect_is_snap_protected(mock_image_ctx, false, 0);
+
+ C_SaferCond ctx;
+ auto req = MockAttachChildRequest::create(&mock_image_ctx, &mock_image_ctx,
+ image_ctx->snap_id, nullptr, 0, 1,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachChildRequest, SetCloneError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+
+ expect_op_features_set(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockAttachChildRequest::create(&mock_image_ctx, &mock_image_ctx,
+ image_ctx->snap_id, nullptr, 0, 2,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachChildRequest, AttachChildError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+
+ expect_op_features_set(mock_image_ctx, 0);
+ expect_child_attach(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockAttachChildRequest::create(&mock_image_ctx, &mock_image_ctx,
+ image_ctx->snap_id, nullptr, 0, 2,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_AttachParentRequest.cc b/src/test/librbd/image/test_mock_AttachParentRequest.cc
new file mode 100644
index 000000000..de5f64431
--- /dev/null
+++ b/src/test/librbd/image/test_mock_AttachParentRequest.cc
@@ -0,0 +1,155 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/image/AttachParentRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/AttachParentRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockImageAttachParentRequest : public TestMockFixture {
+public:
+ typedef AttachParentRequest<MockTestImageCtx> MockAttachParentRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ }
+
+ void expect_parent_attach(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("parent_attach"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_set_parent(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("set_parent"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ librbd::ImageCtx *image_ctx;
+};
+
+TEST_F(TestMockImageAttachParentRequest, ParentAttachSuccess) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_attach(mock_image_ctx, 0);
+
+ cls::rbd::ParentImageSpec parent_image_spec{
+ 1, "ns", "image id", 123};
+
+ C_SaferCond ctx;
+ auto req = MockAttachParentRequest::create(mock_image_ctx, parent_image_spec,
+ 234, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachParentRequest, SetParentSuccess) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_attach(mock_image_ctx, -EOPNOTSUPP);
+ expect_set_parent(mock_image_ctx, 0);
+
+ cls::rbd::ParentImageSpec parent_image_spec{
+ 1, "", "image id", 123};
+
+ C_SaferCond ctx;
+ auto req = MockAttachParentRequest::create(mock_image_ctx, parent_image_spec,
+ 234, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachParentRequest, ParentAttachError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_attach(mock_image_ctx, -EPERM);
+
+ cls::rbd::ParentImageSpec parent_image_spec{
+ 1, "", "image id", 123};
+
+ C_SaferCond ctx;
+ auto req = MockAttachParentRequest::create(mock_image_ctx, parent_image_spec,
+ 234, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachParentRequest, SetParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_attach(mock_image_ctx, -EOPNOTSUPP);
+ expect_set_parent(mock_image_ctx, -EINVAL);
+
+ cls::rbd::ParentImageSpec parent_image_spec{
+ 1, "", "image id", 123};
+
+ C_SaferCond ctx;
+ auto req = MockAttachParentRequest::create(mock_image_ctx, parent_image_spec,
+ 234, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageAttachParentRequest, NamespaceUnsupported) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_attach(mock_image_ctx, -EOPNOTSUPP);
+
+ cls::rbd::ParentImageSpec parent_image_spec{
+ 1, "ns", "image id", 123};
+
+ C_SaferCond ctx;
+ auto req = MockAttachParentRequest::create(mock_image_ctx, parent_image_spec,
+ 234, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EXDEV, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_CloneRequest.cc b/src/test/librbd/image/test_mock_CloneRequest.cc
new file mode 100644
index 000000000..25de66dab
--- /dev/null
+++ b/src/test/librbd/image/test_mock_CloneRequest.cc
@@ -0,0 +1,960 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/deep_copy/MetadataCopyRequest.h"
+#include "librbd/image/TypeTraits.h"
+#include "librbd/image/AttachChildRequest.h"
+#include "librbd/image/AttachParentRequest.h"
+#include "librbd/image/CreateRequest.h"
+#include "librbd/image/RemoveRequest.h"
+#include "librbd/mirror/EnableRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ static MockTestImageCtx* s_instance;
+ static MockTestImageCtx* create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+ static MockTestImageCtx* create(const std::string &image_name,
+ const std::string &image_id,
+ librados::snap_t snap_id, IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ s_instance = this;
+ }
+};
+
+MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+
+namespace deep_copy {
+
+template <>
+struct MetadataCopyRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+
+ static MetadataCopyRequest* s_instance;
+ static MetadataCopyRequest* create(MockTestImageCtx* src_image_ctx,
+ MockTestImageCtx* dst_image_ctx,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MetadataCopyRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+MetadataCopyRequest<MockTestImageCtx>* MetadataCopyRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace deep_copy
+
+namespace image {
+
+template <>
+struct AttachChildRequest<MockTestImageCtx> {
+ uint32_t clone_format;
+ Context* on_finish = nullptr;
+ static AttachChildRequest* s_instance;
+ static AttachChildRequest* create(MockTestImageCtx *,
+ MockTestImageCtx *,
+ const librados::snap_t &,
+ MockTestImageCtx *,
+ const librados::snap_t &,
+ uint32_t clone_format,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->clone_format = clone_format;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ AttachChildRequest() {
+ s_instance = this;
+ }
+};
+
+AttachChildRequest<MockTestImageCtx>* AttachChildRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct AttachParentRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static AttachParentRequest* s_instance;
+ static AttachParentRequest* create(MockTestImageCtx&,
+ const cls::rbd::ParentImageSpec& pspec,
+ uint64_t parent_overlap, bool reattach,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ AttachParentRequest() {
+ s_instance = this;
+ }
+};
+
+AttachParentRequest<MockTestImageCtx>* AttachParentRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct CreateRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static CreateRequest* s_instance;
+ static CreateRequest* create(const ConfigProxy& config, IoCtx &ioctx,
+ const std::string &image_name,
+ const std::string &image_id, uint64_t size,
+ const ImageOptions &image_options,
+ bool skip_mirror_enable,
+ cls::rbd::MirrorImageMode mode,
+ const std::string &non_primary_global_image_id,
+ const std::string &primary_mirror_uuid,
+ asio::ContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ CreateRequest() {
+ s_instance = this;
+ }
+};
+
+CreateRequest<MockTestImageCtx>* CreateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct RemoveRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static RemoveRequest* s_instance;
+ static RemoveRequest* create(librados::IoCtx &ioctx,
+ const std::string &image_name,
+ const std::string &image_id,
+ bool force, bool from_trash_remove,
+ ProgressContext &prog_ctx,
+ asio::ContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+};
+
+RemoveRequest<MockTestImageCtx>* RemoveRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
+namespace mirror {
+
+template <>
+struct EnableRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static EnableRequest* s_instance;
+ static EnableRequest* create(MockTestImageCtx* image_ctx,
+ cls::rbd::MirrorImageMode mode,
+ const std::string &non_primary_global_image_id,
+ bool image_clean, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ EXPECT_TRUE(image_clean);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ EnableRequest() {
+ s_instance = this;
+ }
+};
+
+EnableRequest<MockTestImageCtx>* EnableRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/CloneRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageCloneRequest : public TestMockFixture {
+public:
+ typedef CloneRequest<MockTestImageCtx> MockCloneRequest;
+ typedef AttachChildRequest<MockTestImageCtx> MockAttachChildRequest;
+ typedef AttachParentRequest<MockTestImageCtx> MockAttachParentRequest;
+ typedef CreateRequest<MockTestImageCtx> MockCreateRequest;
+ typedef RemoveRequest<MockTestImageCtx> MockRemoveRequest;
+ typedef deep_copy::MetadataCopyRequest<MockTestImageCtx> MockMetadataCopyRequest;
+ typedef mirror::EnableRequest<MockTestImageCtx> MockMirrorEnableRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "2"));
+
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap", 0, prog_ctx));
+ if (is_feature_enabled(RBD_FEATURE_LAYERING)) {
+ ASSERT_EQ(0, image_ctx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace{}, "snap"));
+
+ uint64_t snap_id = image_ctx->snap_ids[
+ {cls::rbd::UserSnapshotNamespace{}, "snap"}];
+ ASSERT_NE(CEPH_NOSNAP, snap_id);
+
+ C_SaferCond ctx;
+ image_ctx->state->snap_set(snap_id, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+ }
+ }
+
+ void expect_get_min_compat_client(int8_t min_compat_client, int r) {
+ auto mock_rados_client = get_mock_io_ctx(m_ioctx).get_mock_rados_client();
+ EXPECT_CALL(*mock_rados_client, get_min_compatible_client(_, _))
+ .WillOnce(Invoke([min_compat_client, r](int8_t* min, int8_t* required_min) {
+ *min = min_compat_client;
+ *required_min = min_compat_client;
+ return r;
+ }));
+ }
+
+ void expect_get_image_size(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
+ uint64_t size) {
+ EXPECT_CALL(mock_image_ctx, get_image_size(snap_id))
+ .WillOnce(Return(size));
+ }
+
+ void expect_is_snap_protected(MockImageCtx &mock_image_ctx, bool is_protected,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, is_snap_protected(_, _))
+ .WillOnce(WithArg<1>(Invoke([is_protected, r](bool* is_prot) {
+ *is_prot = is_protected;
+ return r;
+ })));
+ }
+
+ void expect_create(MockCreateRequest& mock_create_request, int r) {
+ EXPECT_CALL(mock_create_request, send())
+ .WillOnce(Invoke([this, &mock_create_request, r]() {
+ image_ctx->op_work_queue->queue(mock_create_request.on_finish, r);
+ }));
+ }
+
+ void expect_open(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, open(true, _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
+ image_ctx->op_work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_attach_parent(MockAttachParentRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &mock_request, r]() {
+ image_ctx->op_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_attach_child(MockAttachChildRequest& mock_request,
+ uint32_t clone_format, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &mock_request, clone_format, r]() {
+ EXPECT_EQ(mock_request.clone_format, clone_format);
+ image_ctx->op_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_metadata_copy(MockMetadataCopyRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &mock_request, r]() {
+ image_ctx->op_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_test_features(MockTestImageCtx &mock_image_ctx,
+ uint64_t features, bool enabled) {
+ EXPECT_CALL(mock_image_ctx, test_features(features))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_mirror_mode_get(MockTestImageCtx &mock_image_ctx,
+ cls::rbd::MirrorMode mirror_mode, int r) {
+ bufferlist out_bl;
+ encode(static_cast<uint32_t>(mirror_mode), out_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_mode_get"),
+ _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([out_bl, r](bufferlist* out) {
+ *out = out_bl;
+ return r;
+ })));
+ }
+
+ void expect_mirror_enable(MockMirrorEnableRequest& mock_mirror_enable_request,
+ int r) {
+ EXPECT_CALL(mock_mirror_enable_request, send())
+ .WillOnce(Invoke([this, &mock_mirror_enable_request, r]() {
+ image_ctx->op_work_queue->queue(mock_mirror_enable_request.on_finish, r);
+ }));
+ }
+
+ void expect_close(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, close(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ image_ctx->op_work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_remove(MockRemoveRequest& mock_remove_request, int r) {
+ EXPECT_CALL(mock_remove_request, send())
+ .WillOnce(Invoke([this, &mock_remove_request, r]() {
+ image_ctx->op_work_queue->queue(mock_remove_request.on_finish, r);
+ }));
+ }
+
+ librbd::ImageCtx *image_ctx;
+};
+
+TEST_F(TestMockImageCloneRequest, SuccessV1) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "1"));
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 1, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, 0);
+
+ MockMirrorEnableRequest mock_mirror_enable_request;
+ if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+ expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+ expect_mirror_enable(mock_mirror_enable_request, 0);
+ } else {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+ }
+
+ expect_close(mock_image_ctx, 0);
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, SuccessV2) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "2"));
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, 0);
+
+ MockMirrorEnableRequest mock_mirror_enable_request;
+ if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+ expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+ expect_mirror_enable(mock_mirror_enable_request, 0);
+ } else {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+ }
+
+ expect_close(mock_image_ctx, 0);
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, SuccessAuto) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "auto"));
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, 0);
+
+ MockMirrorEnableRequest mock_mirror_enable_request;
+ if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+ expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+ expect_mirror_enable(mock_mirror_enable_request, 0);
+ } else {
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+ }
+
+ expect_close(mock_image_ctx, 0);
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, OpenParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, CreateError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, OpenError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, -EINVAL);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, AttachParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, AttachChildError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, MetadataCopyError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, GetMirrorModeError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, 0);
+
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+ expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, MirrorEnableError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, 0);
+
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+ expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+ MockMirrorEnableRequest mock_mirror_enable_request;
+ expect_mirror_enable(mock_mirror_enable_request, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, CloseError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, 0);
+
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+
+ expect_close(mock_image_ctx, -EINVAL);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, RemoveError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, -EINVAL);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, -EPERM);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, CloseParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, -EINVAL);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ expect_close(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", "",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, SnapshotMirrorEnableNonPrimary) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ expect_open(mock_image_ctx, 0);
+
+ MockAttachParentRequest mock_attach_parent_request;
+ expect_attach_parent(mock_attach_parent_request, 0);
+
+ MockAttachChildRequest mock_attach_child_request;
+ expect_attach_child(mock_attach_child_request, 2, 0);
+
+ MockMetadataCopyRequest mock_request;
+ expect_metadata_copy(mock_request, 0);
+
+ MockMirrorEnableRequest mock_mirror_enable_request;
+ expect_mirror_enable(mock_mirror_enable_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ ImageOptions clone_opts;
+ auto req = new MockCloneRequest(m_cct->_conf, m_ioctx, "parent id", "", {}, 123,
+ m_ioctx, "clone name", "clone id", clone_opts,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id", "primary mirror uuid",
+ image_ctx->op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_DetachChildRequest.cc b/src/test/librbd/image/test_mock_DetachChildRequest.cc
new file mode 100644
index 000000000..6812125cb
--- /dev/null
+++ b/src/test/librbd/image/test_mock_DetachChildRequest.cc
@@ -0,0 +1,454 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/image/TypeTraits.h"
+#include "librbd/image/DetachChildRequest.h"
+#include "librbd/trash/RemoveRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ static MockTestImageCtx* s_instance;
+ static MockTestImageCtx* create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ s_instance = this;
+ }
+};
+
+MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef librbd::MockContextWQ ContextWQ;
+};
+
+} // namespace image
+
+namespace trash {
+
+template <>
+class RemoveRequest<MockTestImageCtx> {
+private:
+ typedef ::librbd::image::TypeTraits<MockTestImageCtx> TypeTraits;
+ typedef typename TypeTraits::ContextWQ ContextWQ;
+public:
+ static RemoveRequest *s_instance;
+ static RemoveRequest *create(librados::IoCtx &ioctx,
+ MockTestImageCtx *image_ctx,
+ ContextWQ *op_work_queue, bool force,
+ ProgressContext &prog_ctx, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+RemoveRequest<MockTestImageCtx> *RemoveRequest<MockTestImageCtx>::s_instance;
+
+} // namespace trash
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/DetachChildRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageDetachChildRequest : public TestMockFixture {
+public:
+ typedef DetachChildRequest<MockTestImageCtx> MockDetachChildRequest;
+ typedef trash::RemoveRequest<MockTestImageCtx> MockTrashRemoveRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ }
+
+ void expect_test_op_features(MockTestImageCtx& mock_image_ctx, bool enabled) {
+ EXPECT_CALL(mock_image_ctx,
+ test_op_features(RBD_OPERATION_FEATURE_CLONE_CHILD))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_create_ioctx(MockImageCtx &mock_image_ctx,
+ librados::MockTestMemIoCtxImpl **io_ctx_impl) {
+ *io_ctx_impl = &get_mock_io_ctx(mock_image_ctx.md_ctx);
+ auto rados_client = (*io_ctx_impl)->get_mock_rados_client();
+
+ EXPECT_CALL(*rados_client, create_ioctx(_, _))
+ .WillOnce(DoAll(GetReference(*io_ctx_impl), Return(*io_ctx_impl)));
+ }
+
+ void expect_child_detach(MockImageCtx &mock_image_ctx,
+ librados::MockTestMemIoCtxImpl &mock_io_ctx_impl,
+ int r) {
+ auto& parent_spec = mock_image_ctx.parent_md.spec;
+
+ bufferlist bl;
+ encode(parent_spec.snap_id, bl);
+ encode(cls::rbd::ChildImageSpec{mock_image_ctx.md_ctx.get_id(), "",
+ mock_image_ctx.id}, bl);
+
+ EXPECT_CALL(mock_io_ctx_impl,
+ exec(util::header_name(parent_spec.image_id),
+ _, StrEq("rbd"), StrEq("child_detach"), ContentsEqual(bl),
+ _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_remove_child(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("remove_child"), _,
+ _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_snapshot_get(MockImageCtx &mock_image_ctx,
+ librados::MockTestMemIoCtxImpl &mock_io_ctx_impl,
+ const std::string& parent_header_name,
+ const cls::rbd::SnapshotInfo& snap_info, int r) {
+
+ using ceph::encode;
+ EXPECT_CALL(mock_io_ctx_impl,
+ exec(parent_header_name, _, StrEq("rbd"),
+ StrEq("snapshot_get"), _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([snap_info, r](bufferlist* bl) {
+ encode(snap_info, *bl);
+ return r;
+ })));
+ }
+
+ void expect_open(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, open(true, _))
+ .WillOnce(WithArg<1>(Invoke([this, &mock_image_ctx, r](Context* ctx) {
+ EXPECT_EQ(0U, mock_image_ctx.read_only_mask &
+ IMAGE_READ_ONLY_FLAG_NON_PRIMARY);
+ image_ctx->op_work_queue->queue(ctx, r);
+ })));
+ if (r == 0) {
+ EXPECT_CALL(mock_image_ctx, test_features(_))
+ .WillOnce(Return(false));
+ }
+ }
+
+ void expect_close(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, close(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ image_ctx->op_work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_snap_remove(MockImageCtx &mock_image_ctx,
+ const std::string &snap_name, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations,
+ snap_remove({cls::rbd::TrashSnapshotNamespace{}},
+ StrEq(snap_name), _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context *ctx) {
+ image_ctx->op_work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_trash_get(MockImageCtx &mock_image_ctx,
+ librados::MockTestMemIoCtxImpl &mock_io_ctx_impl,
+ const cls::rbd::TrashImageSpec& trash_spec,
+ int r) {
+ using ceph::encode;
+ EXPECT_CALL(mock_io_ctx_impl,
+ exec(RBD_TRASH, _, StrEq("rbd"),
+ StrEq("trash_get"), _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([trash_spec, r](bufferlist* bl) {
+ encode(trash_spec, *bl);
+ return r;
+ })));
+ }
+
+ void expect_trash_remove(MockTrashRemoveRequest& mock_trash_remove_request,
+ int r) {
+ EXPECT_CALL(mock_trash_remove_request, send())
+ .WillOnce(Invoke([&mock_trash_remove_request, r]() {
+ mock_trash_remove_request.on_finish->complete(r);
+ }));
+ }
+
+ librbd::ImageCtx *image_ctx;
+};
+
+TEST_F(TestMockImageDetachChildRequest, SuccessV1) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, false);
+ expect_remove_child(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, SuccessV2) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
+ expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
+ "rbd_header.parent id",
+ {234, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotSuccess) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
+ expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
+ "rbd_header.parent id",
+ {234, {cls::rbd::TrashSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+ expect_open(mock_image_ctx, 0);
+ expect_snap_remove(mock_image_ctx, "snap1", 0);
+ const cls::rbd::TrashImageSpec trash_spec;
+ expect_trash_get(mock_image_ctx, *mock_io_ctx_impl, trash_spec, -ENOENT);
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, ParentAutoRemove) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
+ expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
+ "rbd_header.parent id",
+ {234, {cls::rbd::TrashSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+ expect_open(mock_image_ctx, 0);
+ expect_snap_remove(mock_image_ctx, "snap1", 0);
+ const cls::rbd::TrashImageSpec trash_spec =
+ {cls::rbd::TRASH_IMAGE_SOURCE_USER_PARENT, "parent", {}, {}};
+
+ expect_trash_get(mock_image_ctx, *mock_io_ctx_impl, trash_spec, 0);
+ MockTrashRemoveRequest mock_trash_remove_request;
+ expect_trash_remove(mock_trash_remove_request, 0);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotInUse) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
+ expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
+ "rbd_header.parent id",
+ {234, {cls::rbd::TrashSnapshotNamespace{}},
+ "snap1", 123, {}, 1}, 0);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotSnapshotGetError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
+ expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
+ "rbd_header.parent id",
+ {234, {cls::rbd::TrashSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotOpenParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
+ expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
+ "rbd_header.parent id",
+ {234, {cls::rbd::TrashSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+ expect_open(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotRemoveError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
+ expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
+ "rbd_header.parent id",
+ {234, {cls::rbd::TrashSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+ expect_open(mock_image_ctx, 0);
+ expect_snap_remove(mock_image_ctx, "snap1", -EPERM);
+ expect_close(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, ParentDNE) {
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, ChildDetachError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, true);
+
+ librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+ expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+ expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachChildRequest, RemoveChildError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+ mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
+
+ InSequence seq;
+ expect_test_op_features(mock_image_ctx, false);
+ expect_remove_child(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_DetachParentRequest.cc b/src/test/librbd/image/test_mock_DetachParentRequest.cc
new file mode 100644
index 000000000..4d9f012f8
--- /dev/null
+++ b/src/test/librbd/image/test_mock_DetachParentRequest.cc
@@ -0,0 +1,135 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/image/DetachParentRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/DetachParentRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockImageDetachParentRequest : public TestMockFixture {
+public:
+ typedef DetachParentRequest<MockTestImageCtx> MockDetachParentRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ }
+
+ void expect_parent_detach(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("parent_detach"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_remove_parent(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("remove_parent"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ librbd::ImageCtx *image_ctx;
+};
+
+TEST_F(TestMockImageDetachParentRequest, ParentDetachSuccess) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_detach(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockDetachParentRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachParentRequest, RemoveParentSuccess) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_detach(mock_image_ctx, -EOPNOTSUPP);
+ expect_remove_parent(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockDetachParentRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachParentRequest, ParentDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_detach(mock_image_ctx, -ENOENT);
+
+ C_SaferCond ctx;
+ auto req = MockDetachParentRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachParentRequest, ParentDetachError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_detach(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockDetachParentRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDetachParentRequest, RemoveParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_parent_detach(mock_image_ctx, -EOPNOTSUPP);
+ expect_remove_parent(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockDetachParentRequest::create(mock_image_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_ListWatchersRequest.cc b/src/test/librbd/image/test_mock_ListWatchersRequest.cc
new file mode 100644
index 000000000..d90fc4ab0
--- /dev/null
+++ b/src/test/librbd/image/test_mock_ListWatchersRequest.cc
@@ -0,0 +1,212 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "librbd/image/ListWatchersRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/test_support.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/ListWatchersRequest.cc"
+template class librbd::image::ListWatchersRequest<librbd::MockImageCtx>;
+
+namespace librbd {
+
+namespace image {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+
+class TestMockListWatchersRequest : public TestMockFixture {
+public:
+ typedef ListWatchersRequest<MockImageCtx> MockListWatchersRequest;
+
+ obj_watch_t watcher(const std::string &address, uint64_t watch_handle) {
+ obj_watch_t w;
+ strcpy(w.addr, address.c_str());
+ w.watcher_id = 0;
+ w.cookie = watch_handle;
+ w.timeout_seconds = 0;
+
+ return w;
+ }
+
+ void expect_list_watchers(MockTestImageCtx &mock_image_ctx,
+ const std::string oid,
+ const std::list<obj_watch_t> &watchers, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ list_watchers(oid, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoAll(SetArgPointee<1>(watchers), Return(0)));
+ }
+ }
+
+ void expect_list_image_watchers(MockTestImageCtx &mock_image_ctx,
+ const std::list<obj_watch_t> &watchers,
+ int r) {
+ expect_list_watchers(mock_image_ctx, mock_image_ctx.header_oid,
+ watchers, r);
+ }
+
+ void expect_list_mirror_watchers(MockTestImageCtx &mock_image_ctx,
+ const std::list<obj_watch_t> &watchers,
+ int r) {
+ expect_list_watchers(mock_image_ctx, RBD_MIRRORING, watchers, r);
+ }
+
+ void expect_get_watch_handle(MockImageWatcher &mock_watcher,
+ uint64_t watch_handle) {
+ EXPECT_CALL(mock_watcher, get_watch_handle())
+ .WillOnce(Return(watch_handle));
+ }
+};
+
+TEST_F(TestMockListWatchersRequest, NoImageWatchers) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageWatcher mock_watcher;
+
+ InSequence seq;
+ expect_list_image_watchers(mock_image_ctx, {}, 0);
+
+ std::list<obj_watch_t> watchers;
+ C_SaferCond ctx;
+ auto req = MockListWatchersRequest::create(mock_image_ctx, 0, &watchers,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(watchers.empty());
+}
+
+TEST_F(TestMockListWatchersRequest, Error) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageWatcher mock_watcher;
+
+ InSequence seq;
+ expect_list_image_watchers(mock_image_ctx, {}, -EINVAL);
+
+ std::list<obj_watch_t> watchers;
+ C_SaferCond ctx;
+ auto req = MockListWatchersRequest::create(mock_image_ctx, 0, &watchers,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockListWatchersRequest, Success) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageWatcher mock_watcher;
+
+ InSequence seq;
+ expect_list_image_watchers(mock_image_ctx,
+ {watcher("a", 123), watcher("b", 456)}, 0);
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 123);
+
+ std::list<obj_watch_t> watchers;
+ C_SaferCond ctx;
+ auto req = MockListWatchersRequest::create(mock_image_ctx, 0, &watchers,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(2U, watchers.size());
+
+ auto w = watchers.begin();
+ ASSERT_STREQ("a", w->addr);
+ ASSERT_EQ(123U, w->cookie);
+
+ w++;
+ ASSERT_STREQ("b", w->addr);
+ ASSERT_EQ(456U, w->cookie);
+}
+
+TEST_F(TestMockListWatchersRequest, FilterOutMyInstance) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageWatcher mock_watcher;
+
+ InSequence seq;
+ expect_list_image_watchers(mock_image_ctx,
+ {watcher("a", 123), watcher("b", 456)}, 0);
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 123);
+
+ std::list<obj_watch_t> watchers;
+ C_SaferCond ctx;
+ auto req = MockListWatchersRequest::create(
+ mock_image_ctx, LIST_WATCHERS_FILTER_OUT_MY_INSTANCE, &watchers, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(1U, watchers.size());
+
+ ASSERT_STREQ("b", watchers.begin()->addr);
+ ASSERT_EQ(456U, watchers.begin()->cookie);
+}
+
+TEST_F(TestMockListWatchersRequest, FilterOutMirrorInstance) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageWatcher mock_watcher;
+
+ InSequence seq;
+ expect_list_image_watchers(mock_image_ctx,
+ {watcher("a", 123), watcher("b", 456)}, 0);
+ expect_list_mirror_watchers(mock_image_ctx, {watcher("b", 789)}, 0);
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 123);
+
+ std::list<obj_watch_t> watchers;
+ C_SaferCond ctx;
+ auto req = MockListWatchersRequest::create(
+ mock_image_ctx, LIST_WATCHERS_FILTER_OUT_MIRROR_INSTANCES, &watchers,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(1U, watchers.size());
+
+ ASSERT_STREQ("a", watchers.begin()->addr);
+ ASSERT_EQ(123U, watchers.begin()->cookie);
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_PreRemoveRequest.cc b/src/test/librbd/image/test_mock_PreRemoveRequest.cc
new file mode 100644
index 000000000..faae4f201
--- /dev/null
+++ b/src/test/librbd/image/test_mock_PreRemoveRequest.cc
@@ -0,0 +1,465 @@
+// -*- mode:c++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/image/ListWatchersRequest.h"
+#include "librbd/image/PreRemoveRequest.h"
+#include "librbd/image/RefreshParentRequest.h"
+#include "librbd/operation/SnapshotRemoveRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace operation {
+
+template <>
+class SnapshotRemoveRequest<MockTestImageCtx> {
+public:
+ static SnapshotRemoveRequest *s_instance;
+ static SnapshotRemoveRequest *create(MockTestImageCtx &image_ctx,
+ cls::rbd::SnapshotNamespace sn,
+ std::string name,
+ uint64_t id, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ SnapshotRemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SnapshotRemoveRequest<MockTestImageCtx> *SnapshotRemoveRequest<MockTestImageCtx>::s_instance;
+
+} // namespace operation
+
+namespace image {
+
+template<>
+class ListWatchersRequest<MockTestImageCtx> {
+public:
+ static ListWatchersRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static ListWatchersRequest *create(MockTestImageCtx &image_ctx, int flags,
+ std::list<obj_watch_t> *watchers,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ListWatchersRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+ListWatchersRequest<MockTestImageCtx> *ListWatchersRequest<MockTestImageCtx>::s_instance;
+
+} // namespace image
+} // namespace librbd
+
+// template definitions
+#include "librbd/exclusive_lock/StandardPolicy.cc"
+#include "librbd/image/PreRemoveRequest.cc"
+
+ACTION_P(TestFeatures, image_ctx) {
+ return ((image_ctx->features & arg0) != 0);
+}
+
+ACTION_P(ShutDownExclusiveLock, image_ctx) {
+ // shutting down exclusive lock will close object map and journal
+ image_ctx->exclusive_lock = nullptr;
+ image_ctx->object_map = nullptr;
+ image_ctx->journal = nullptr;
+}
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockImagePreRemoveRequest : public TestMockFixture {
+public:
+ typedef PreRemoveRequest<MockTestImageCtx> MockPreRemoveRequest;
+ typedef ListWatchersRequest<MockTestImageCtx> MockListWatchersRequest;
+ typedef librbd::operation::SnapshotRemoveRequest<MockTestImageCtx> MockSnapshotRemoveRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_test_imctx));
+ m_mock_imctx = new MockTestImageCtx(*m_test_imctx);
+ }
+
+ void TearDown() override {
+ delete m_mock_imctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_test_features(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_))
+ .WillRepeatedly(TestFeatures(&mock_image_ctx));
+ }
+
+ void expect_set_exclusive_lock_policy(MockTestImageCtx& mock_image_ctx) {
+ if (m_mock_imctx->exclusive_lock != nullptr) {
+ EXPECT_CALL(mock_image_ctx, set_exclusive_lock_policy(_))
+ .WillOnce(Invoke([](exclusive_lock::Policy* policy) {
+ ASSERT_FALSE(policy->may_auto_request_lock());
+ delete policy;
+ }));
+ }
+ }
+
+ void expect_set_journal_policy(MockTestImageCtx &mock_image_ctx) {
+ if (m_test_imctx->test_features(RBD_FEATURE_JOURNALING)) {
+ EXPECT_CALL(mock_image_ctx, set_journal_policy(_))
+ .WillOnce(Invoke([](journal::Policy* policy) {
+ ASSERT_TRUE(policy->journal_disabled());
+ delete policy;
+ }));
+ }
+ }
+
+ void expect_acquire_exclusive_lock(MockTestImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ int r) {
+ if (m_mock_imctx->exclusive_lock != nullptr) {
+ EXPECT_CALL(mock_exclusive_lock, acquire_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+ }
+
+ void expect_shut_down_exclusive_lock(MockTestImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ int r) {
+ if (m_mock_imctx->exclusive_lock != nullptr) {
+ EXPECT_CALL(mock_exclusive_lock, shut_down(_))
+ .WillOnce(DoAll(ShutDownExclusiveLock(&mock_image_ctx),
+ CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+ }
+
+ void expect_is_exclusive_lock_owner(MockTestImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ bool is_owner) {
+ if (m_mock_imctx->exclusive_lock != nullptr) {
+ EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillOnce(Return(is_owner));
+ }
+ }
+
+ void expect_list_image_watchers(
+ MockTestImageCtx &mock_image_ctx,
+ MockListWatchersRequest &mock_list_watchers_request, int r) {
+ EXPECT_CALL(mock_list_watchers_request, send())
+ .WillOnce(FinishRequest(&mock_list_watchers_request, r, &mock_image_ctx));
+ }
+
+ void expect_get_group(MockTestImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.old_format) {
+ return;
+ }
+
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("image_group_get"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_remove_snap(MockTestImageCtx &mock_image_ctx,
+ MockSnapshotRemoveRequest& mock_snap_remove_request,
+ int r) {
+ EXPECT_CALL(mock_snap_remove_request, send())
+ .WillOnce(FinishRequest(&mock_snap_remove_request, r, &mock_image_ctx));
+ }
+
+ librbd::ImageCtx *m_test_imctx = nullptr;
+ MockTestImageCtx *m_mock_imctx = nullptr;
+};
+
+TEST_F(TestMockImagePreRemoveRequest, Success) {
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, true);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+
+ expect_get_group(*m_mock_imctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, OperationsDisabled) {
+ REQUIRE_FORMAT_V2();
+
+ m_mock_imctx->operations_disabled = true;
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EROFS, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, ExclusiveLockTryAcquireFailed) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ MockExclusiveLock mock_exclusive_lock;
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock,
+ -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, ExclusiveLockTryAcquireNotLockOwner) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ MockExclusiveLock mock_exclusive_lock;
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, false);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, Force) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ MockExclusiveLock mock_exclusive_lock;
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock,
+ -EINVAL);
+ expect_shut_down_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+
+ expect_get_group(*m_mock_imctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, true, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, ExclusiveLockShutDownFailed) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ MockExclusiveLock mock_exclusive_lock;
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, -EINVAL);
+ expect_shut_down_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, true, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, Migration) {
+ m_mock_imctx->features |= RBD_FEATURE_MIGRATING;
+
+ expect_test_features(*m_mock_imctx);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, Snapshots) {
+ m_mock_imctx->snap_info = {
+ {123, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, {}, {}, {}, {}, {}}}};
+
+ expect_test_features(*m_mock_imctx);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOTEMPTY, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, Watchers) {
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, true);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request,
+ -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, GroupError) {
+ REQUIRE_FORMAT_V2();
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, true);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+
+ expect_get_group(*m_mock_imctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, AutoDeleteSnapshots) {
+ REQUIRE_FORMAT_V2();
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ m_mock_imctx->snap_info = {
+ {123, {"snap1", {cls::rbd::TrashSnapshotNamespace{}}, {}, {}, {}, {}, {}}}};
+
+ InSequence seq;
+ expect_set_exclusive_lock_policy(*m_mock_imctx);
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, true);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+
+ expect_get_group(*m_mock_imctx, 0);
+
+ MockSnapshotRemoveRequest mock_snap_remove_request;
+ expect_remove_snap(*m_mock_imctx, mock_snap_remove_request, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_RefreshRequest.cc b/src/test/librbd/image/test_mock_RefreshRequest.cc
new file mode 100644
index 000000000..e60409615
--- /dev/null
+++ b/src/test/librbd/image/test_mock_RefreshRequest.cc
@@ -0,0 +1,1757 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageWatcher.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "test/librbd/mock/MockJournalPolicy.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Image.h"
+#include "librbd/image/GetMetadataRequest.h"
+#include "librbd/image/RefreshRequest.h"
+#include "librbd/image/RefreshParentRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <queue>
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockRefreshImageCtx : public MockImageCtx {
+ MockRefreshImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct GetMetadataRequest<MockRefreshImageCtx> {
+ std::string oid;
+ std::map<std::string, bufferlist>* pairs = nullptr;
+ Context* on_finish = nullptr;
+
+ static GetMetadataRequest* s_instance;
+ static GetMetadataRequest* create(librados::IoCtx&,
+ const std::string& oid,
+ bool filter_internal,
+ const std::string& filter_key_prefix,
+ const std::string& last_key,
+ uint32_t max_results,
+ std::map<std::string, bufferlist>* pairs,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ EXPECT_EQ("conf_", filter_key_prefix);
+ EXPECT_EQ("conf_", last_key);
+ s_instance->oid = oid;
+ s_instance->pairs = pairs;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetMetadataRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct RefreshParentRequest<MockRefreshImageCtx> {
+ static std::queue<RefreshParentRequest*> s_instances;
+ static RefreshParentRequest* create(MockRefreshImageCtx &mock_image_ctx,
+ const ParentImageInfo &parent_md,
+ const MigrationInfo &migration_info,
+ Context *on_finish) {
+ ceph_assert(!s_instances.empty());
+ auto instance = s_instances.front();
+ instance->on_finish = on_finish;
+ return instance;
+ }
+ static bool is_refresh_required(MockRefreshImageCtx &mock_image_ctx,
+ const ParentImageInfo& parent_md,
+ const MigrationInfo &migration_info) {
+ ceph_assert(!s_instances.empty());
+ return s_instances.front()->is_refresh_required();
+ }
+
+ Context *on_finish = nullptr;
+
+ RefreshParentRequest() {
+ s_instances.push(this);
+ }
+
+ ~RefreshParentRequest() {
+ ceph_assert(this == s_instances.front());
+ s_instances.pop();
+ }
+
+ MOCK_CONST_METHOD0(is_refresh_required, bool());
+ MOCK_METHOD0(send, void());
+ MOCK_METHOD0(apply, void());
+ MOCK_METHOD1(finalize, void(Context *));
+};
+
+GetMetadataRequest<MockRefreshImageCtx>* GetMetadataRequest<MockRefreshImageCtx>::s_instance = nullptr;
+std::queue<RefreshParentRequest<MockRefreshImageCtx>*> RefreshParentRequest<MockRefreshImageCtx>::s_instances;
+
+} // namespace image
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(librbd::MockRefreshImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/RefreshRequest.cc"
+
+ACTION_P(TestFeatures, image_ctx) {
+ return ((image_ctx->features & arg0) != 0);
+}
+
+ACTION_P(ShutDownExclusiveLock, image_ctx) {
+ // shutting down exclusive lock will close object map and journal
+ image_ctx->exclusive_lock = nullptr;
+ image_ctx->object_map = nullptr;
+ image_ctx->journal = nullptr;
+}
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::StrEq;
+
+class TestMockImageRefreshRequest : public TestMockFixture {
+public:
+ typedef GetMetadataRequest<MockRefreshImageCtx> MockGetMetadataRequest;
+ typedef RefreshRequest<MockRefreshImageCtx> MockRefreshRequest;
+ typedef RefreshParentRequest<MockRefreshImageCtx> MockRefreshParentRequest;
+ typedef std::map<std::string, bufferlist> Metadata;
+
+ void set_v1_migration_header(ImageCtx *ictx) {
+ bufferlist hdr;
+ ASSERT_EQ(0, read_header_bl(ictx->md_ctx, ictx->header_oid, hdr, nullptr));
+ ASSERT_TRUE(hdr.length() >= sizeof(rbd_obj_header_ondisk));
+ ASSERT_EQ(0, memcmp(RBD_HEADER_TEXT, hdr.c_str(), sizeof(RBD_HEADER_TEXT)));
+
+ bufferlist::iterator it = hdr.begin();
+ it.copy_in(sizeof(RBD_MIGRATE_HEADER_TEXT), RBD_MIGRATE_HEADER_TEXT);
+ ASSERT_EQ(0, ictx->md_ctx.write(ictx->header_oid, hdr, hdr.length(), 0));
+ }
+
+ void expect_set_require_lock(MockExclusiveLock &mock_exclusive_lock,
+ librbd::io::Direction direction) {
+ EXPECT_CALL(mock_exclusive_lock, set_require_lock(true, direction, _))
+ .WillOnce(WithArg<2>(Invoke([](Context* ctx) { ctx->complete(0); })));
+ }
+
+ void expect_unset_require_lock(MockExclusiveLock &mock_exclusive_lock,
+ librbd::io::Direction direction) {
+ EXPECT_CALL(mock_exclusive_lock, unset_require_lock(direction));
+ }
+
+ void expect_v1_read_header(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ read(mock_image_ctx.header_oid, _, _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_v1_get_snapshots(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("snap_list"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_v1_get_locks(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("lock"),
+ StrEq("get_info"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_get_mutable_metadata(MockRefreshImageCtx &mock_image_ctx,
+ uint64_t features, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_size"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ uint64_t incompatible = (
+ mock_image_ctx.read_only ? features & RBD_FEATURES_INCOMPATIBLE :
+ features & RBD_FEATURES_RW_INCOMPATIBLE);
+
+ expect.WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_features"), _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([features, incompatible](bufferlist* out_bl) {
+ encode(features, *out_bl);
+ encode(incompatible, *out_bl);
+ return 0;
+ })));
+ expect_get_flags(mock_image_ctx, 0);
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_snapcontext"), _, _, _, _))
+ .WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("lock"),
+ StrEq("get_info"), _, _, _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_parent_overlap_get(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto& expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("parent_overlap_get"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_get_parent(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto& expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("parent_get"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ expect_parent_overlap_get(mock_image_ctx, 0);
+ }
+ }
+
+ void expect_get_parent_legacy(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto& expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_parent"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_get_migration_header(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("migration_get"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_get_metadata(MockRefreshImageCtx& mock_image_ctx,
+ MockGetMetadataRequest& mock_request,
+ const std::string& oid,
+ const Metadata& metadata, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_request, oid, metadata, r]() {
+ ASSERT_EQ(oid, mock_request.oid);
+ *mock_request.pairs = metadata;
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_get_flags(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_flags"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_get_op_features(MockRefreshImageCtx &mock_image_ctx,
+ uint64_t op_features, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("op_features_get"), _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([op_features, r](bufferlist* out_bl) {
+ encode(op_features, *out_bl);
+ return r;
+ })));
+ }
+
+ void expect_get_group(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("image_group_get"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_get_snapshots(MockRefreshImageCtx &mock_image_ctx,
+ bool legacy_parent, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("snapshot_get"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ if (legacy_parent) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_parent"), _, _, _, _))
+ .WillOnce(DoDefault());
+ } else {
+ expect_parent_overlap_get(mock_image_ctx, 0);
+ }
+ expect_get_flags(mock_image_ctx, 0);
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_protection_status"), _, _, _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_get_snapshots_legacy(MockRefreshImageCtx &mock_image_ctx,
+ bool include_timestamp, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_snapshot_name"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_size"), _, _, _, _))
+ .WillOnce(DoDefault());
+ if (include_timestamp) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_snapshot_timestamp"), _, _, _, _))
+ .WillOnce(DoDefault());
+ }
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_parent"), _, _, _, _))
+ .WillOnce(DoDefault());
+ expect_get_flags(mock_image_ctx, 0);
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("get_protection_status"), _, _, _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_apply_metadata(MockRefreshImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, is_unregistered())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_image_ctx, apply_metadata(_, false))
+ .WillOnce(Return(r));
+ }
+
+ void expect_add_snap(MockRefreshImageCtx &mock_image_ctx,
+ const std::string &snap_name, uint64_t snap_id) {
+ EXPECT_CALL(mock_image_ctx, add_snap(_, snap_name, snap_id, _, _, _, _, _));
+ }
+
+ void expect_init_exclusive_lock(MockRefreshImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, create_exclusive_lock())
+ .WillOnce(Return(&mock_exclusive_lock));
+ EXPECT_CALL(mock_exclusive_lock, init(mock_image_ctx.features, _))
+ .WillOnce(WithArg<1>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ void expect_shut_down_exclusive_lock(MockRefreshImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ int r) {
+ EXPECT_CALL(mock_exclusive_lock, shut_down(_))
+ .WillOnce(DoAll(ShutDownExclusiveLock(&mock_image_ctx),
+ CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ void expect_init_layout(MockRefreshImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, init_layout(_));
+ }
+
+ void expect_test_features(MockRefreshImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_, _))
+ .WillRepeatedly(TestFeatures(&mock_image_ctx));
+ }
+
+ void expect_refresh_parent_is_required(MockRefreshParentRequest &mock_refresh_parent_request,
+ bool required) {
+ EXPECT_CALL(mock_refresh_parent_request, is_refresh_required())
+ .WillRepeatedly(Return(required));
+ }
+
+ void expect_refresh_parent_send(MockRefreshImageCtx &mock_image_ctx,
+ MockRefreshParentRequest &mock_refresh_parent_request,
+ int r) {
+ EXPECT_CALL(mock_refresh_parent_request, send())
+ .WillOnce(FinishRequest(&mock_refresh_parent_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_refresh_parent_apply(MockRefreshParentRequest &mock_refresh_parent_request) {
+ EXPECT_CALL(mock_refresh_parent_request, apply());
+ }
+
+ void expect_refresh_parent_finalize(MockRefreshImageCtx &mock_image_ctx,
+ MockRefreshParentRequest &mock_refresh_parent_request,
+ int r) {
+ EXPECT_CALL(mock_refresh_parent_request, finalize(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_is_exclusive_lock_owner(MockExclusiveLock &mock_exclusive_lock,
+ bool is_owner) {
+ EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillOnce(Return(is_owner));
+ }
+
+ void expect_get_journal_policy(MockImageCtx &mock_image_ctx,
+ MockJournalPolicy &mock_journal_policy) {
+ EXPECT_CALL(mock_image_ctx, get_journal_policy())
+ .WillOnce(Return(&mock_journal_policy));
+ }
+
+ void expect_journal_disabled(MockJournalPolicy &mock_journal_policy,
+ bool disabled) {
+ EXPECT_CALL(mock_journal_policy, journal_disabled())
+ .WillOnce(Return(disabled));
+ }
+
+ void expect_open_journal(MockRefreshImageCtx &mock_image_ctx,
+ MockJournal &mock_journal, int r) {
+ EXPECT_CALL(mock_image_ctx, create_journal())
+ .WillOnce(Return(&mock_journal));
+ EXPECT_CALL(mock_journal, open(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_close_journal(MockRefreshImageCtx &mock_image_ctx,
+ MockJournal &mock_journal, int r) {
+ EXPECT_CALL(mock_journal, close(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_open_object_map(MockRefreshImageCtx &mock_image_ctx,
+ MockObjectMap *mock_object_map, int r) {
+ EXPECT_CALL(mock_image_ctx, create_object_map(_))
+ .WillOnce(Return(mock_object_map));
+ EXPECT_CALL(*mock_object_map, open(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_close_object_map(MockRefreshImageCtx &mock_image_ctx,
+ MockObjectMap &mock_object_map, int r) {
+ EXPECT_CALL(mock_object_map, close(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_get_snap_id(MockRefreshImageCtx &mock_image_ctx,
+ const std::string &snap_name,
+ uint64_t snap_id) {
+ EXPECT_CALL(mock_image_ctx,
+ get_snap_id(_, snap_name)).WillOnce(Return(snap_id));
+ }
+
+ void expect_block_writes(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, block_writes(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_unblock_writes(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, unblock_writes())
+ .Times(1);
+ }
+
+ void expect_image_flush(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, send(_))
+ .WillOnce(Invoke([r](io::ImageDispatchSpec* spec) {
+ ASSERT_TRUE(boost::get<io::ImageDispatchSpec::Flush>(
+ &spec->request) != nullptr);
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ spec->aio_comp->set_request_count(1);
+ spec->aio_comp->add_request();
+ spec->aio_comp->complete_request(r);
+ }));
+ }
+
+};
+
+TEST_F(TestMockImageRefreshRequest, SuccessV1) {
+ REQUIRE_FORMAT_V1();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_v1_read_header(mock_image_ctx, 0);
+ expect_v1_get_snapshots(mock_image_ctx, 0);
+ expect_v1_get_locks(mock_image_ctx, 0);
+ expect_init_layout(mock_image_ctx);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessSnapshotV1) {
+ REQUIRE_FORMAT_V1();
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_v1_read_header(mock_image_ctx, 0);
+ expect_v1_get_snapshots(mock_image_ctx, 0);
+ expect_v1_get_locks(mock_image_ctx, 0);
+ expect_init_layout(mock_image_ctx);
+ expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessV2) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, -EOPNOTSUPP);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessSnapshotV2) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_get_snapshots(mock_image_ctx, false, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessLegacySnapshotV2) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, -EOPNOTSUPP);
+ expect_get_parent_legacy(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_get_snapshots(mock_image_ctx, true, -EOPNOTSUPP);
+ expect_get_snapshots_legacy(mock_image_ctx, true, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessLegacySnapshotNoTimestampV2) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, -EOPNOTSUPP);
+ expect_get_parent_legacy(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_get_snapshots(mock_image_ctx, true, -EOPNOTSUPP);
+ expect_get_snapshots_legacy(mock_image_ctx, true, -EOPNOTSUPP);
+ expect_get_snapshots_legacy(mock_image_ctx, false, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessSetSnapshotV2) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockObjectMap mock_object_map;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_get_snapshots(mock_image_ctx, false, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ expect_open_object_map(mock_image_ctx, &mock_object_map, 0);
+ }
+ expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+ expect_get_snap_id(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SnapshotV2EnoentRetriesLimit) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ for (int i = 0; i < RefreshRequest<>::MAX_ENOENT_RETRIES + 1; ++i) {
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_get_snapshots(mock_image_ctx, false, -ENOENT);
+ }
+
+ C_SaferCond ctx;
+ auto req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessChild) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ librbd::ImageCtx *ictx2 = nullptr;
+ std::string clone_name = get_temp_image_name();
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*ictx, "snap"));
+ BOOST_SCOPE_EXIT_ALL((&)) {
+ if (ictx2 != nullptr) {
+ close_image(ictx2);
+ }
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
+ ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), "snap"));
+ };
+
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
+ clone_name.c_str(), ictx->features, &order, 0, 0));
+
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx2);
+ MockRefreshParentRequest *mock_refresh_parent_request = new MockRefreshParentRequest();
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ mock_image_ctx.features &= ~RBD_FEATURE_OPERATIONS;
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, mock_image_ctx.features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(*mock_refresh_parent_request, true);
+ expect_refresh_parent_send(mock_image_ctx, *mock_refresh_parent_request, 0);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ expect_refresh_parent_apply(*mock_refresh_parent_request);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_refresh_parent_finalize(mock_image_ctx, *mock_refresh_parent_request, 0);
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessChildDontOpenParent) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ librbd::ImageCtx *ictx2 = nullptr;
+ std::string clone_name = get_temp_image_name();
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*ictx, "snap"));
+ BOOST_SCOPE_EXIT_ALL((&)) {
+ if (ictx2 != nullptr) {
+ close_image(ictx2);
+ }
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
+ ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), "snap"));
+ };
+
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
+ clone_name.c_str(), ictx->features, &order, 0, 0));
+
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx2);
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ mock_image_ctx.features &= ~RBD_FEATURE_OPERATIONS;
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, mock_image_ctx.features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, true, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessChildBeingFlattened) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ librbd::ImageCtx *ictx2 = nullptr;
+ std::string clone_name = get_temp_image_name();
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*ictx, "snap"));
+ BOOST_SCOPE_EXIT_ALL((&)) {
+ if (ictx2 != nullptr) {
+ close_image(ictx2);
+ }
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
+ ASSERT_EQ(0, ictx->operations->snap_unprotect(
+ cls::rbd::UserSnapshotNamespace(), "snap"));
+ };
+
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
+ clone_name.c_str(), ictx->features, &order, 0, 0));
+
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx2);
+ auto mock_refresh_parent_request = new MockRefreshParentRequest();
+ MockRefreshParentRequest mock_refresh_parent_request_ext;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ mock_image_ctx.features &= ~RBD_FEATURE_OPERATIONS;
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, mock_image_ctx.features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(*mock_refresh_parent_request, true);
+ expect_refresh_parent_send(mock_image_ctx, *mock_refresh_parent_request,
+ -ENOENT);
+ expect_get_mutable_metadata(mock_image_ctx, mock_image_ctx.features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request_ext, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ auto req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, ChildEnoentRetriesLimit) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ librbd::ImageCtx *ictx2 = nullptr;
+ std::string clone_name = get_temp_image_name();
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*ictx, "snap"));
+ BOOST_SCOPE_EXIT_ALL((&)) {
+ if (ictx2 != nullptr) {
+ close_image(ictx2);
+ }
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
+ ASSERT_EQ(0, ictx->operations->snap_unprotect(
+ cls::rbd::UserSnapshotNamespace(), "snap"));
+ };
+
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
+ clone_name.c_str(), ictx->features, &order, 0, 0));
+
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx2);
+ constexpr int num_tries = RefreshRequest<>::MAX_ENOENT_RETRIES + 1;
+ MockRefreshParentRequest* mock_refresh_parent_requests[num_tries];
+ for (auto& mock_refresh_parent_request : mock_refresh_parent_requests) {
+ mock_refresh_parent_request = new MockRefreshParentRequest();
+ }
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ mock_image_ctx.features &= ~RBD_FEATURE_OPERATIONS;
+
+ InSequence seq;
+ for (auto mock_refresh_parent_request : mock_refresh_parent_requests) {
+ expect_get_mutable_metadata(mock_image_ctx, mock_image_ctx.features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(*mock_refresh_parent_request, true);
+ expect_refresh_parent_send(mock_image_ctx, *mock_refresh_parent_request,
+ -ENOENT);
+ }
+ expect_refresh_parent_apply(*mock_refresh_parent_requests[num_tries - 1]);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_refresh_parent_finalize(
+ mock_image_ctx, *mock_refresh_parent_requests[num_tries - 1], 0);
+
+ C_SaferCond ctx;
+ auto req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessOpFeatures) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ mock_image_ctx.features |= RBD_FEATURE_OPERATIONS;
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, mock_image_ctx.features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_op_features(mock_image_ctx, 4096, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(4096U, mock_image_ctx.op_features);
+ ASSERT_TRUE(mock_image_ctx.operations_disabled);
+}
+
+TEST_F(TestMockImageRefreshRequest, DisableExclusiveLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ MockJournal mock_journal;
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ mock_image_ctx.journal = &mock_journal;
+ }
+
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ }
+
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
+ false));
+ }
+
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_EXCLUSIVE_LOCK,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ // verify that exclusive lock is properly handled when object map
+ // and journaling were never enabled (or active)
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_shut_down_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, DisableExclusiveLockWhileAcquiringLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ }
+
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
+ false));
+ }
+
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_EXCLUSIVE_LOCK,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ // verify that exclusive lock is properly handled when object map
+ // and journaling were never enabled (or active)
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, true, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-ERESTART, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, JournalDisabledByPolicy) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockJournal mock_journal;
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+ expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+
+ MockJournalPolicy mock_journal_policy;
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, true);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableJournalWithExclusiveLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockJournal mock_journal;
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+ expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
+
+ // journal should be immediately opened if exclusive lock owned
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+
+ MockJournalPolicy mock_journal_policy;
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_open_journal(mock_image_ctx, mock_journal, 0);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableJournalWithoutExclusiveLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+ expect_is_exclusive_lock_owner(mock_exclusive_lock, false);
+
+ // do not open the journal if exclusive lock is not owned
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ expect_set_require_lock(mock_exclusive_lock, librbd::io::DIRECTION_BOTH);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, DisableJournal) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ MockJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ // verify journal is closed if feature disabled
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ expect_block_writes(mock_image_ctx, 0);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ if (!mock_image_ctx.clone_copy_on_read) {
+ expect_unset_require_lock(mock_exclusive_lock, librbd::io::DIRECTION_READ);
+ }
+ expect_close_journal(mock_image_ctx, mock_journal, 0);
+ expect_unblock_writes(mock_image_ctx);
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithExclusiveLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockObjectMap mock_object_map;
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+ expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
+
+ // object map should be immediately opened if exclusive lock owned
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ expect_open_object_map(mock_image_ctx, &mock_object_map, 0);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithoutExclusiveLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+ expect_is_exclusive_lock_owner(mock_exclusive_lock, false);
+
+ // do not open the object map if exclusive lock is not owned
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, DisableObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ MockJournal mock_journal;
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ mock_image_ctx.journal = &mock_journal;
+ }
+
+ if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ // verify object map is closed if feature disabled
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_close_object_map(mock_image_ctx, mock_object_map, 0);
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, OpenObjectMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockObjectMap mock_object_map;
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+ expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
+
+ // object map should be immediately opened if exclusive lock owned
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ expect_open_object_map(mock_image_ctx, &mock_object_map, -EBLOCKLISTED);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx.object_map);
+}
+
+TEST_F(TestMockImageRefreshRequest, OpenObjectMapTooLarge) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ }
+
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ MockObjectMap mock_object_map;
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+ expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
+
+ // object map should be immediately opened if exclusive lock owned
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ expect_open_object_map(mock_image_ctx, &mock_object_map, -EFBIG);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(nullptr, mock_image_ctx.object_map);
+}
+
+TEST_F(TestMockImageRefreshRequest, ApplyMetadataError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, -EINVAL);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, NonPrimaryFeature) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+ MockExclusiveLock mock_exclusive_lock;
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ InSequence seq;
+
+ // ensure the image is put into read-only mode
+ expect_get_mutable_metadata(mock_image_ctx,
+ ictx->features | RBD_FEATURE_NON_PRIMARY, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx1;
+ auto req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx1);
+ req->send();
+
+ ASSERT_EQ(0, ctx1.wait());
+ ASSERT_TRUE(mock_image_ctx.read_only);
+ ASSERT_EQ(IMAGE_READ_ONLY_FLAG_NON_PRIMARY, mock_image_ctx.read_only_flags);
+
+ // try again but permit R/W against non-primary image
+ mock_image_ctx.read_only_mask = ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
+
+ expect_get_mutable_metadata(mock_image_ctx,
+ ictx->features | RBD_FEATURE_NON_PRIMARY, 0);
+ expect_get_parent(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request,
+ mock_image_ctx.header_oid, {}, 0);
+ expect_get_metadata(mock_image_ctx, mock_get_metadata_request, RBD_INFO, {},
+ 0);
+ expect_apply_metadata(mock_image_ctx, 0);
+ expect_get_group(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+ }
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+
+ C_SaferCond ctx2;
+ req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx2);
+ req->send();
+
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_FALSE(mock_image_ctx.read_only);
+ ASSERT_EQ(0U, mock_image_ctx.read_only_flags);
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_RemoveRequest.cc b/src/test/librbd/image/test_mock_RemoveRequest.cc
new file mode 100644
index 000000000..9700202d6
--- /dev/null
+++ b/src/test/librbd/image/test_mock_RemoveRequest.cc
@@ -0,0 +1,480 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/image/TypeTraits.h"
+#include "librbd/image/DetachChildRequest.h"
+#include "librbd/image/PreRemoveRequest.h"
+#include "librbd/image/RemoveRequest.h"
+#include "librbd/journal/RemoveRequest.h"
+#include "librbd/journal/TypeTraits.h"
+#include "librbd/mirror/DisableRequest.h"
+#include "librbd/operation/TrimRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ static MockTestImageCtx* s_instance;
+ static MockTestImageCtx* create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ s_instance = this;
+ }
+};
+
+MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+
+template<>
+struct Journal<MockTestImageCtx> {
+ static void get_work_queue(CephContext*, MockContextWQ**) {
+ }
+};
+
+namespace image {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef librbd::MockContextWQ ContextWQ;
+};
+
+template <>
+class DetachChildRequest<MockTestImageCtx> {
+public:
+ static DetachChildRequest *s_instance;
+ static DetachChildRequest *create(MockTestImageCtx &image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ DetachChildRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+DetachChildRequest<MockTestImageCtx> *DetachChildRequest<MockTestImageCtx>::s_instance;
+
+template <>
+class PreRemoveRequest<MockTestImageCtx> {
+public:
+ static PreRemoveRequest *s_instance;
+ static PreRemoveRequest *create(MockTestImageCtx* image_ctx, bool force,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ PreRemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+PreRemoveRequest<MockTestImageCtx> *PreRemoveRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
+namespace journal {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef librbd::MockContextWQ ContextWQ;
+};
+
+} // namespace journal
+
+namespace operation {
+
+template <>
+class TrimRequest<MockTestImageCtx> {
+public:
+ static TrimRequest *s_instance;
+ static TrimRequest *create(MockTestImageCtx &image_ctx, Context *on_finish,
+ uint64_t original_size, uint64_t new_size,
+ ProgressContext &prog_ctx) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ TrimRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+TrimRequest<MockTestImageCtx> *TrimRequest<MockTestImageCtx>::s_instance;
+
+} // namespace operation
+
+namespace journal {
+
+template <>
+class RemoveRequest<MockTestImageCtx> {
+private:
+ typedef ::librbd::image::TypeTraits<MockTestImageCtx> TypeTraits;
+ typedef typename TypeTraits::ContextWQ ContextWQ;
+public:
+ static RemoveRequest *s_instance;
+ static RemoveRequest *create(IoCtx &ioctx, const std::string &imageid,
+ const std::string &client_id,
+ ContextWQ *op_work_queue, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+RemoveRequest<MockTestImageCtx> *RemoveRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+
+namespace mirror {
+
+template<>
+class DisableRequest<MockTestImageCtx> {
+public:
+ static DisableRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static DisableRequest *create(MockTestImageCtx *image_ctx, bool force,
+ bool remove, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ DisableRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+DisableRequest<MockTestImageCtx> *DisableRequest<MockTestImageCtx>::s_instance;
+
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/RemoveRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+
+class TestMockImageRemoveRequest : public TestMockFixture {
+public:
+ typedef ::librbd::image::TypeTraits<MockTestImageCtx> TypeTraits;
+ typedef typename TypeTraits::ContextWQ ContextWQ;
+ typedef RemoveRequest<MockTestImageCtx> MockRemoveRequest;
+ typedef PreRemoveRequest<MockTestImageCtx> MockPreRemoveRequest;
+ typedef DetachChildRequest<MockTestImageCtx> MockDetachChildRequest;
+ typedef librbd::operation::TrimRequest<MockTestImageCtx> MockTrimRequest;
+ typedef librbd::journal::RemoveRequest<MockTestImageCtx> MockJournalRemoveRequest;
+ typedef librbd::mirror::DisableRequest<MockTestImageCtx> MockMirrorDisableRequest;
+
+ librbd::ImageCtx *m_test_imctx = NULL;
+ MockTestImageCtx *m_mock_imctx = NULL;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_test_imctx));
+ m_mock_imctx = new MockTestImageCtx(*m_test_imctx);
+ librbd::MockTestImageCtx::s_instance = m_mock_imctx;
+ }
+ void TearDown() override {
+ librbd::MockTestImageCtx::s_instance = NULL;
+ delete m_mock_imctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_state_open(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, open(_, _))
+ .WillOnce(Invoke([r](bool open_parent, Context *on_ready) {
+ on_ready->complete(r);
+ }));
+ }
+
+ void expect_state_close(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, close(_))
+ .WillOnce(Invoke([](Context *on_ready) {
+ on_ready->complete(0);
+ }));
+ }
+
+ void expect_wq_queue(ContextWQ &wq, int r) {
+ EXPECT_CALL(wq, queue(_, r))
+ .WillRepeatedly(Invoke([](Context *on_ready, int r) {
+ on_ready->complete(r);
+ }));
+ }
+
+ void expect_pre_remove_image(MockTestImageCtx &mock_image_ctx,
+ MockPreRemoveRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
+ }
+
+ void expect_trim(MockTestImageCtx &mock_image_ctx,
+ MockTrimRequest &mock_trim_request, int r) {
+ EXPECT_CALL(mock_trim_request, send())
+ .WillOnce(FinishRequest(&mock_trim_request, r, &mock_image_ctx));
+ }
+
+ void expect_journal_remove(MockTestImageCtx &mock_image_ctx,
+ MockJournalRemoveRequest &mock_journal_remove_request, int r) {
+ EXPECT_CALL(mock_journal_remove_request, send())
+ .WillOnce(FinishRequest(&mock_journal_remove_request, r, &mock_image_ctx));
+ }
+
+ void expect_mirror_disable(MockTestImageCtx &mock_image_ctx,
+ MockMirrorDisableRequest &mock_mirror_disable_request, int r) {
+ EXPECT_CALL(mock_mirror_disable_request, send())
+ .WillOnce(FinishRequest(&mock_mirror_disable_request, r, &mock_image_ctx));
+ }
+
+ void expect_remove_mirror_image(librados::IoCtx &ioctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(ioctx),
+ exec(StrEq("rbd_mirroring"), _, StrEq("rbd"),
+ StrEq("mirror_image_remove"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_dir_remove_image(librados::IoCtx &ioctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(ioctx),
+ exec(RBD_DIRECTORY, _, StrEq("rbd"), StrEq("dir_remove_image"),
+ _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_detach_child(MockTestImageCtx &mock_image_ctx,
+ MockDetachChildRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
+ }
+};
+
+TEST_F(TestMockImageRemoveRequest, SuccessV1) {
+ REQUIRE_FORMAT_V1();
+ expect_op_work_queue(*m_mock_imctx);
+
+ InSequence seq;
+ expect_state_open(*m_mock_imctx, 0);
+
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
+
+ MockTrimRequest mock_trim_request;
+ expect_trim(*m_mock_imctx, mock_trim_request, 0);
+
+ expect_state_close(*m_mock_imctx);
+
+ ContextWQ op_work_queue;
+ expect_wq_queue(op_work_queue, 0);
+
+ C_SaferCond ctx;
+ librbd::NoOpProgressContext no_op;
+ MockRemoveRequest *req = MockRemoveRequest::create(m_ioctx, m_image_name, "",
+ true, false, no_op, &op_work_queue, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRemoveRequest, OpenFailV1) {
+ REQUIRE_FORMAT_V1();
+
+ InSequence seq;
+ expect_state_open(*m_mock_imctx, -ENOENT);
+
+ ContextWQ op_work_queue;
+ expect_wq_queue(op_work_queue, 0);
+
+ C_SaferCond ctx;
+ librbd::NoOpProgressContext no_op;
+ MockRemoveRequest *req = MockRemoveRequest::create(m_ioctx, m_image_name, "",
+ true, false, no_op, &op_work_queue, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRemoveRequest, SuccessV2CloneV1) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ expect_op_work_queue(*m_mock_imctx);
+
+ m_mock_imctx->parent_md.spec.pool_id = m_ioctx.get_id();
+ m_mock_imctx->parent_md.spec.image_id = "parent id";
+ m_mock_imctx->parent_md.spec.snap_id = 234;
+
+ InSequence seq;
+ expect_state_open(*m_mock_imctx, 0);
+
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
+
+ MockTrimRequest mock_trim_request;
+ expect_trim(*m_mock_imctx, mock_trim_request, 0);
+
+ MockDetachChildRequest mock_detach_child_request;
+ expect_detach_child(*m_mock_imctx, mock_detach_child_request, 0);
+
+ MockMirrorDisableRequest mock_mirror_disable_request;
+ expect_mirror_disable(*m_mock_imctx, mock_mirror_disable_request, 0);
+
+ expect_state_close(*m_mock_imctx);
+
+ MockJournalRemoveRequest mock_journal_remove_request;
+ expect_journal_remove(*m_mock_imctx, mock_journal_remove_request, 0);
+
+ expect_remove_mirror_image(m_ioctx, 0);
+ expect_dir_remove_image(m_ioctx, 0);
+
+ C_SaferCond ctx;
+ librbd::NoOpProgressContext no_op;
+ ContextWQ op_work_queue;
+ MockRemoveRequest *req = MockRemoveRequest::create(
+ m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRemoveRequest, SuccessV2CloneV2) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ expect_op_work_queue(*m_mock_imctx);
+
+ m_mock_imctx->parent_md.spec.pool_id = m_ioctx.get_id();
+ m_mock_imctx->parent_md.spec.image_id = "parent id";
+ m_mock_imctx->parent_md.spec.snap_id = 234;
+
+ InSequence seq;
+ expect_state_open(*m_mock_imctx, 0);
+
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
+
+ MockTrimRequest mock_trim_request;
+ expect_trim(*m_mock_imctx, mock_trim_request, 0);
+
+ MockDetachChildRequest mock_detach_child_request;
+ expect_detach_child(*m_mock_imctx, mock_detach_child_request, 0);
+
+ MockMirrorDisableRequest mock_mirror_disable_request;
+ expect_mirror_disable(*m_mock_imctx, mock_mirror_disable_request, 0);
+
+ expect_state_close(*m_mock_imctx);
+
+ MockJournalRemoveRequest mock_journal_remove_request;
+ expect_journal_remove(*m_mock_imctx, mock_journal_remove_request, 0);
+
+ expect_remove_mirror_image(m_ioctx, 0);
+ expect_dir_remove_image(m_ioctx, 0);
+
+ C_SaferCond ctx;
+ librbd::NoOpProgressContext no_op;
+ ContextWQ op_work_queue;
+ MockRemoveRequest *req = MockRemoveRequest::create(
+ m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRemoveRequest, NotExistsV2) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ expect_op_work_queue(*m_mock_imctx);
+
+ m_mock_imctx->parent_md.spec.pool_id = m_ioctx.get_id();
+ m_mock_imctx->parent_md.spec.image_id = "parent id";
+ m_mock_imctx->parent_md.spec.snap_id = 234;
+
+ InSequence seq;
+ expect_state_open(*m_mock_imctx, 0);
+
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
+
+ MockTrimRequest mock_trim_request;
+ expect_trim(*m_mock_imctx, mock_trim_request, 0);
+
+ MockDetachChildRequest mock_detach_child_request;
+ expect_detach_child(*m_mock_imctx, mock_detach_child_request, 0);
+
+ MockMirrorDisableRequest mock_mirror_disable_request;
+ expect_mirror_disable(*m_mock_imctx, mock_mirror_disable_request, 0);
+
+ expect_state_close(*m_mock_imctx);
+
+ MockJournalRemoveRequest mock_journal_remove_request;
+ expect_journal_remove(*m_mock_imctx, mock_journal_remove_request, 0);
+
+ expect_remove_mirror_image(m_ioctx, 0);
+ expect_dir_remove_image(m_ioctx, -ENOENT);
+
+ C_SaferCond ctx;
+ librbd::NoOpProgressContext no_op;
+ ContextWQ op_work_queue;
+ MockRemoveRequest *req = MockRemoveRequest::create(
+ m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/image/test_mock_ValidatePoolRequest.cc b/src/test/librbd/image/test_mock_ValidatePoolRequest.cc
new file mode 100644
index 000000000..f5204ac20
--- /dev/null
+++ b/src/test/librbd/image/test_mock_ValidatePoolRequest.cc
@@ -0,0 +1,223 @@
+// -*- mode:c++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/ValidatePoolRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageValidatePoolRequest : public TestMockFixture {
+public:
+ typedef ValidatePoolRequest<MockTestImageCtx> MockValidatePoolRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+ m_ioctx.remove(RBD_INFO);
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ }
+
+ void expect_clone(librados::MockTestMemIoCtxImpl &mock_io_ctx) {
+ EXPECT_CALL(mock_io_ctx, clone())
+ .WillOnce(Invoke([&mock_io_ctx]() {
+ mock_io_ctx.get();
+ return &mock_io_ctx;
+ }));
+ }
+
+ void expect_read_rbd_info(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ const std::string& data, int r) {
+ auto& expect = EXPECT_CALL(
+ mock_io_ctx, read(StrEq(RBD_INFO), 0, 0, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(WithArg<3>(Invoke([data](bufferlist* bl) {
+ bl->append(data);
+ return 0;
+ })));
+ }
+ }
+
+ void expect_write_rbd_info(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ const std::string& data, int r) {
+ bufferlist bl;
+ bl.append(data);
+
+ EXPECT_CALL(mock_io_ctx, write(StrEq(RBD_INFO), ContentsEqual(bl),
+ data.length(), 0, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_allocate_snap_id(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ int r) {
+ auto &expect = EXPECT_CALL(mock_io_ctx, selfmanaged_snap_create(_));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_release_snap_id(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ int r) {
+ auto &expect = EXPECT_CALL(mock_io_ctx, selfmanaged_snap_remove(_));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ librbd::ImageCtx *image_ctx;
+};
+
+TEST_F(TestMockImageValidatePoolRequest, Success) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "", -ENOENT);
+ expect_allocate_snap_id(mock_io_ctx, 0);
+ expect_write_rbd_info(mock_io_ctx, "validate", 0);
+ expect_release_snap_id(mock_io_ctx, 0);
+ expect_write_rbd_info(mock_io_ctx, "overwrite validated", 0);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageValidatePoolRequest, AlreadyValidated) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "overwrite validated", 0);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageValidatePoolRequest, SnapshotsValidated) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "validate", 0);
+ expect_write_rbd_info(mock_io_ctx, "overwrite validated", 0);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageValidatePoolRequest, ReadError) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "", -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageValidatePoolRequest, CreateSnapshotError) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "", 0);
+ expect_allocate_snap_id(mock_io_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageValidatePoolRequest, WriteError) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "", -ENOENT);
+ expect_allocate_snap_id(mock_io_ctx, 0);
+ expect_write_rbd_info(mock_io_ctx, "validate", -EPERM);
+ expect_release_snap_id(mock_io_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageValidatePoolRequest, RemoveSnapshotError) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "", -ENOENT);
+ expect_allocate_snap_id(mock_io_ctx, 0);
+ expect_write_rbd_info(mock_io_ctx, "validate", 0);
+ expect_release_snap_id(mock_io_ctx, -EPERM);
+ expect_write_rbd_info(mock_io_ctx, "overwrite validated", 0);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageValidatePoolRequest, OverwriteError) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+
+ InSequence seq;
+ expect_clone(mock_io_ctx);
+ expect_read_rbd_info(mock_io_ctx, "", -ENOENT);
+ expect_allocate_snap_id(mock_io_ctx, 0);
+ expect_write_rbd_info(mock_io_ctx, "validate", 0);
+ expect_release_snap_id(mock_io_ctx, 0);
+ expect_write_rbd_info(mock_io_ctx, "overwrite validated", -EOPNOTSUPP);
+
+ C_SaferCond ctx;
+ auto req = new MockValidatePoolRequest(m_ioctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
diff --git a/src/test/librbd/io/test_mock_CopyupRequest.cc b/src/test/librbd/io/test_mock_CopyupRequest.cc
new file mode 100644
index 000000000..a4fe54af2
--- /dev/null
+++ b/src/test/librbd/io/test_mock_CopyupRequest.cc
@@ -0,0 +1,1340 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockExclusiveLock.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/api/Io.h"
+#include "librbd/deep_copy/ObjectCopyRequest.h"
+#include "librbd/io/CopyupRequest.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/ObjectRequest.h"
+#include "librbd/io/ReadResult.h"
+#include "librbd/io/Utils.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx,
+ MockTestImageCtx* mock_parent_image_ctx = nullptr)
+ : MockImageCtx(image_ctx) {
+ parent = mock_parent_image_ctx;
+ }
+ ~MockTestImageCtx() override {
+ // copyups need to complete prior to attempting to delete this object
+ wait_for_async_ops();
+ }
+
+ std::map<uint64_t, librbd::io::CopyupRequest<librbd::MockTestImageCtx>*> copyup_list;
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace deep_copy {
+
+template <>
+struct ObjectCopyRequest<librbd::MockTestImageCtx> {
+ static ObjectCopyRequest* s_instance;
+ static ObjectCopyRequest* create(librbd::MockImageCtx* parent_image_ctx,
+ librbd::MockTestImageCtx* image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t dst_snap_id_start,
+ const SnapMap &snap_map,
+ uint64_t object_number, uint32_t flags,
+ Handler*, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->object_number = object_number;
+ s_instance->flatten = (
+ (flags & deep_copy::OBJECT_COPY_REQUEST_FLAG_FLATTEN) != 0);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ uint64_t object_number;
+ bool flatten;
+ Context *on_finish;
+
+ ObjectCopyRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+ObjectCopyRequest<librbd::MockTestImageCtx>* ObjectCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace deep_copy
+
+namespace io {
+
+namespace util {
+
+template <>
+void area_to_object_extents(MockTestImageCtx* image_ctx, uint64_t offset,
+ uint64_t length, ImageArea area,
+ uint64_t buffer_offset,
+ striper::LightweightObjectExtents* object_extents) {
+ Striper::file_to_extents(image_ctx->cct, &image_ctx->layout, offset, length,
+ 0, buffer_offset, object_extents);
+}
+
+template <>
+std::pair<Extents, ImageArea> object_to_area_extents(
+ MockTestImageCtx* image_ctx, uint64_t object_no,
+ const Extents& object_extents) {
+ Extents extents;
+ for (auto [off, len] : object_extents) {
+ Striper::extent_to_file(image_ctx->cct, &image_ctx->layout, object_no, off,
+ len, extents);
+ }
+ return {std::move(extents), ImageArea::DATA};
+}
+
+} // namespace util
+
+template <>
+struct ObjectRequest<librbd::MockTestImageCtx> {
+ static void add_write_hint(librbd::MockTestImageCtx&,
+ neorados::WriteOp*) {
+ }
+};
+
+template <>
+struct AbstractObjectWriteRequest<librbd::MockTestImageCtx> {
+ C_SaferCond ctx;
+ void handle_copyup(int r) {
+ ctx.complete(r);
+ }
+
+ MOCK_CONST_METHOD0(get_pre_write_object_map_state, uint8_t());
+ MOCK_CONST_METHOD0(is_empty_write_op, bool());
+
+ MOCK_METHOD1(add_copyup_ops, void(neorados::WriteOp*));
+};
+
+} // namespace io
+} // namespace librbd
+
+static bool operator==(const SnapContext& rhs, const SnapContext& lhs) {
+ return (rhs.seq == lhs.seq && rhs.snaps == lhs.snaps);
+}
+
+#include "librbd/AsyncObjectThrottle.cc"
+#include "librbd/io/CopyupRequest.cc"
+
+MATCHER_P(IsRead, image_extents, "") {
+ auto req = boost::get<librbd::io::ImageDispatchSpec::Read>(&arg->request);
+ return (req != nullptr && image_extents == arg->image_extents);
+}
+
+namespace librbd {
+namespace io {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+using ::testing::WithoutArgs;
+
+struct TestMockIoCopyupRequest : public TestMockFixture {
+ typedef CopyupRequest<librbd::MockTestImageCtx> MockCopyupRequest;
+ typedef ObjectRequest<librbd::MockTestImageCtx> MockObjectRequest;
+ typedef AbstractObjectWriteRequest<librbd::MockTestImageCtx> MockAbstractObjectWriteRequest;
+ typedef deep_copy::ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+ if (!is_feature_enabled(RBD_FEATURE_LAYERING)) {
+ return;
+ }
+
+ m_parent_image_name = m_image_name;
+ m_image_name = get_temp_image_name();
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_parent_image_name.c_str(),
+ nullptr));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ image.close();
+
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_parent_image_name.c_str(), "one", m_ioctx,
+ m_image_name.c_str(), features, &order));
+ }
+
+ void expect_get_parent_overlap(MockTestImageCtx& mock_image_ctx,
+ librados::snap_t snap_id, uint64_t overlap,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, get_parent_overlap(snap_id, _))
+ .WillOnce(WithArg<1>(Invoke([overlap, r](uint64_t *o) {
+ *o = overlap;
+ return r;
+ })));
+ }
+
+ void expect_prune_parent_extents(MockTestImageCtx& mock_image_ctx,
+ uint64_t overlap, uint64_t object_overlap) {
+ EXPECT_CALL(mock_image_ctx, prune_parent_extents(_, _, overlap, _))
+ .WillOnce(WithoutArgs(Invoke([object_overlap]() {
+ return object_overlap;
+ })));
+ }
+
+ void expect_read_parent(librbd::MockTestImageCtx& mock_image_ctx,
+ const Extents& image_extents,
+ const std::string& data, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher,
+ send(IsRead(image_extents)))
+ .WillOnce(Invoke(
+ [&mock_image_ctx, image_extents, data, r](io::ImageDispatchSpec* spec) {
+ auto req = boost::get<librbd::io::ImageDispatchSpec::Read>(
+ &spec->request);
+ ASSERT_TRUE(req != nullptr);
+
+ if (r < 0) {
+ spec->fail(r);
+ return;
+ }
+
+ spec->dispatch_result = DISPATCH_RESULT_COMPLETE;
+
+ auto aio_comp = spec->aio_comp;
+ aio_comp->read_result = std::move(req->read_result);
+ aio_comp->read_result.set_image_extents(image_extents);
+ aio_comp->set_request_count(1);
+ auto ctx = new ReadResult::C_ImageReadRequest(aio_comp, 0,
+ image_extents);
+ ctx->bl.append(data);
+ mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_copyup(MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
+ const std::string& oid, const std::string& data, int r) {
+ bufferlist in_bl;
+ in_bl.append(data);
+
+ SnapContext snapc;
+ if (snap_id == CEPH_NOSNAP) {
+ snapc = mock_image_ctx.snapc;
+ }
+
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ EXPECT_CALL(mock_io_ctx,
+ exec(oid, _, StrEq("rbd"), StrEq("copyup"),
+ ContentsEqual(in_bl), _, _, snapc))
+ .WillOnce(Return(r));
+ }
+
+ void expect_sparse_copyup(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
+ const std::string &oid,
+ const std::map<uint64_t, uint64_t> &extent_map,
+ const std::string &data, int r) {
+ bufferlist data_bl;
+ data_bl.append(data);
+
+ bufferlist in_bl;
+ encode(extent_map, in_bl);
+ encode(data_bl, in_bl);
+
+ SnapContext snapc;
+ if (snap_id == CEPH_NOSNAP) {
+ snapc = mock_image_ctx.snapc;
+ }
+
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ EXPECT_CALL(mock_io_ctx,
+ exec(oid, _, StrEq("rbd"), StrEq("sparse_copyup"),
+ ContentsEqual(in_bl), _, _, snapc))
+ .WillOnce(Return(r));
+ }
+
+ void expect_write(MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
+ const std::string& oid, int r) {
+ SnapContext snapc;
+ if (snap_id == CEPH_NOSNAP) {
+ snapc = mock_image_ctx.snapc;
+ }
+
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ EXPECT_CALL(mock_io_ctx, write(oid, _, 0, 0, snapc))
+ .WillOnce(Return(r));
+ }
+
+ void expect_test_features(MockTestImageCtx& mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_, _))
+ .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) {
+ return (mock_image_ctx.features & features) != 0;
+ })));
+ }
+
+ void expect_is_lock_owner(MockTestImageCtx& mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock,
+ is_lock_owner()).WillRepeatedly(Return(true));
+ }
+ }
+
+ void expect_is_empty_write_op(MockAbstractObjectWriteRequest& mock_write_request,
+ bool is_empty) {
+ EXPECT_CALL(mock_write_request, is_empty_write_op())
+ .WillOnce(Return(is_empty));
+ }
+
+ void expect_add_copyup_ops(MockAbstractObjectWriteRequest& mock_write_request) {
+ EXPECT_CALL(mock_write_request, add_copyup_ops(_))
+ .WillOnce(Invoke([](neorados::WriteOp* op) {
+ op->write(0, bufferlist{});
+ }));
+ }
+
+ void expect_get_pre_write_object_map_state(MockTestImageCtx& mock_image_ctx,
+ MockAbstractObjectWriteRequest& mock_write_request,
+ uint8_t state) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(mock_write_request, get_pre_write_object_map_state())
+ .WillOnce(Return(state));
+ }
+ }
+
+ void expect_object_map_at(MockTestImageCtx& mock_image_ctx,
+ uint64_t object_no, uint8_t state) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, at(object_no))
+ .WillOnce(Return(state));
+ }
+ }
+
+ void expect_object_map_update(MockTestImageCtx& mock_image_ctx,
+ uint64_t snap_id, uint64_t object_no,
+ uint8_t state, bool updated, int ret_val) {
+ if (mock_image_ctx.object_map != nullptr) {
+ if (!mock_image_ctx.image_ctx->test_features(RBD_FEATURE_FAST_DIFF) &&
+ state == OBJECT_EXISTS_CLEAN) {
+ state = OBJECT_EXISTS;
+ }
+
+ EXPECT_CALL(*mock_image_ctx.object_map,
+ aio_update(snap_id, object_no, object_no + 1, state,
+ boost::optional<uint8_t>(), _,
+ (snap_id != CEPH_NOSNAP), _))
+ .WillOnce(WithArg<7>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
+ if (updated) {
+ mock_image_ctx.op_work_queue->queue(ctx, ret_val);
+ }
+ return updated;
+ })));
+ }
+ }
+
+ void expect_object_copy(MockTestImageCtx& mock_image_ctx,
+ MockObjectCopyRequest& mock_object_copy_request,
+ bool flatten, int r) {
+ EXPECT_CALL(mock_object_copy_request, send())
+ .WillOnce(Invoke(
+ [&mock_image_ctx, &mock_object_copy_request, flatten, r]() {
+ ASSERT_EQ(flatten, mock_object_copy_request.flatten);
+ mock_image_ctx.op_work_queue->queue(
+ mock_object_copy_request.on_finish, r);
+ }));
+ }
+
+ void expect_prepare_copyup(MockTestImageCtx& mock_image_ctx, int r = 0) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
+ prepare_copyup(_, _)).WillOnce(Return(r));
+ }
+
+ void expect_prepare_copyup(MockTestImageCtx& mock_image_ctx,
+ const SparseBufferlist& in_sparse_bl,
+ const SparseBufferlist& out_sparse_bl) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
+ prepare_copyup(_, _))
+ .WillOnce(WithArg<1>(Invoke(
+ [in_sparse_bl, out_sparse_bl]
+ (SnapshotSparseBufferlist* snap_sparse_bl) {
+ auto& sparse_bl = (*snap_sparse_bl)[0];
+ EXPECT_EQ(in_sparse_bl, sparse_bl);
+
+ sparse_bl = out_sparse_bl;
+ return 0;
+ })));
+ }
+
+ void flush_async_operations(librbd::ImageCtx* ictx) {
+ api::Io<>::flush(*ictx);
+ }
+
+ std::string m_parent_image_name;
+};
+
+TEST_F(TestMockIoCopyupRequest, Standard) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {{0, 4096}}, data, 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, StandardWithSnaps) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->image_lock.lock();
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "2", 2, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "1", 1, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->snapc = {2, {2, 1}};
+ ictx->image_lock.unlock();
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_test_features(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, 1, 0, OBJECT_EXISTS, true, 0);
+ expect_object_map_update(mock_image_ctx, 2, 0, OBJECT_EXISTS_CLEAN, true, 0);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, 0, ictx->get_object_name(0), {{0, 4096}},
+ data, 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, CopyOnRead) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {{0, 4096}}, data, 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->send();
+ flush_async_operations(ictx);
+}
+
+TEST_F(TestMockIoCopyupRequest, CopyOnReadWithSnaps) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->image_lock.lock();
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "1", 1, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->snapc = {1, {1}};
+ ictx->image_lock.unlock();
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_test_features(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, 1, 0, OBJECT_EXISTS, true, 0);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS_CLEAN,
+ true, 0);
+
+ expect_sparse_copyup(mock_image_ctx, 0, ictx->get_object_name(0), {{0, 4096}},
+ data, 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->send();
+ flush_async_operations(ictx);
+}
+
+TEST_F(TestMockIoCopyupRequest, DeepCopy) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ MockObjectCopyRequest mock_object_copy_request;
+ mock_image_ctx.migration_info = {1, "", "", "image id", "", {}, ictx->size,
+ true};
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_object_copy(mock_image_ctx, mock_object_copy_request, true, 0);
+
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {}, "", 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, DeepCopyOnRead) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ MockObjectCopyRequest mock_object_copy_request;
+ mock_image_ctx.migration_info = {1, "", "", "image id", "", {}, ictx->size,
+ false};
+ expect_object_copy(mock_image_ctx, mock_object_copy_request, true, 0);
+
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->send();
+ flush_async_operations(ictx);
+}
+
+TEST_F(TestMockIoCopyupRequest, DeepCopyWithPostSnaps) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->image_lock.lock();
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "3", 3, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "2", 2, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "1", 1, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->snapc = {3, {3, 2, 1}};
+ ictx->image_lock.unlock();
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_test_features(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ MockObjectCopyRequest mock_object_copy_request;
+ mock_image_ctx.migration_info = {1, "", "", "image id", "",
+ {{CEPH_NOSNAP, {2, 1}}},
+ ictx->size, true};
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_object_copy(mock_image_ctx, mock_object_copy_request, true, 0);
+
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_get_parent_overlap(mock_image_ctx, 1, 0, 0);
+ expect_get_parent_overlap(mock_image_ctx, 2, 1, 0);
+ expect_prune_parent_extents(mock_image_ctx, 1, 1);
+ expect_get_parent_overlap(mock_image_ctx, 3, 1, 0);
+ expect_prune_parent_extents(mock_image_ctx, 1, 1);
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, 2, 0, OBJECT_EXISTS, true, 0);
+ expect_object_map_update(mock_image_ctx, 3, 0, OBJECT_EXISTS_CLEAN, true, 0);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {}, "", 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, DeepCopyWithPreAndPostSnaps) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->image_lock.lock();
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "4", 4, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "3", 3, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "2", 2, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "1", 1, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->snapc = {4, {4, 3, 2, 1}};
+ ictx->image_lock.unlock();
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_test_features(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ MockObjectCopyRequest mock_object_copy_request;
+ mock_image_ctx.migration_info = {1, "", "", "image id", "",
+ {{CEPH_NOSNAP, {2, 1}}, {10, {1}}},
+ ictx->size, true};
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_object_copy(mock_image_ctx, mock_object_copy_request, true, 0);
+
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_get_parent_overlap(mock_image_ctx, 2, 0, 0);
+ expect_get_parent_overlap(mock_image_ctx, 3, 1, 0);
+ expect_prune_parent_extents(mock_image_ctx, 1, 1);
+ expect_get_parent_overlap(mock_image_ctx, 4, 1, 0);
+ expect_prune_parent_extents(mock_image_ctx, 1, 1);
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, 3, 0, OBJECT_EXISTS_CLEAN, true, 0);
+ expect_object_map_update(mock_image_ctx, 4, 0, OBJECT_EXISTS_CLEAN, true, 0);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {}, "", 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, ZeroedCopyup) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_prepare_copyup(mock_image_ctx);
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {}, "", 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, ZeroedCopyOnRead) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '\0');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {}, "", 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->send();
+ flush_async_operations(ictx);
+}
+
+TEST_F(TestMockIoCopyupRequest, NoOpCopyup) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, "", -ENOENT);
+
+ expect_prepare_copyup(mock_image_ctx);
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_is_empty_write_op(mock_write_request, true);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, RestartWrite) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ MockAbstractObjectWriteRequest mock_write_request1;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request1,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ expect_add_copyup_ops(mock_write_request1);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {{0, 4096}}, data, 0);
+
+ MockAbstractObjectWriteRequest mock_write_request2;
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ EXPECT_CALL(mock_io_ctx, write(ictx->get_object_name(0), _, 0, 0, _))
+ .WillOnce(WithoutArgs(Invoke([req, &mock_write_request2]() {
+ req->append_request(&mock_write_request2, {});
+ return 0;
+ })));
+
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request1, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request1.ctx.wait());
+ ASSERT_EQ(-ERESTART, mock_write_request2.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, ReadFromParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, "", -EPERM);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ MockAbstractObjectWriteRequest mock_write_request;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(-EPERM, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, PrepareCopyupError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx, -EIO);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ MockAbstractObjectWriteRequest mock_write_request;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(-EIO, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, DeepCopyError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ MockObjectCopyRequest mock_object_copy_request;
+ mock_image_ctx.migration_info = {1, "", "", "image id", "", {}, ictx->size,
+ true};
+ expect_is_empty_write_op(mock_write_request, false);
+ expect_object_copy(mock_image_ctx, mock_object_copy_request, true, -EPERM);
+
+ expect_is_empty_write_op(mock_write_request, false);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(-EPERM, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, UpdateObjectMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ -EINVAL);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(-EINVAL, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, CopyupError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->image_lock.lock();
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "1", 1, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->snapc = {1, {1}};
+ ictx->image_lock.unlock();
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_test_features(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, 1, 0, OBJECT_EXISTS, true, 0);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, 0, ictx->get_object_name(0), {{0, 4096}},
+ data, -EPERM);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(-EPERM, mock_write_request.ctx.wait());
+ flush_async_operations(ictx);
+}
+
+TEST_F(TestMockIoCopyupRequest, SparseCopyupNotSupported) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+ mock_image_ctx.enable_sparse_copyup = false;
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+ expect_prepare_copyup(mock_image_ctx);
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), data, 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, ProcessCopyup) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+
+ bufferlist in_prepare_bl;
+ in_prepare_bl.append(std::string(3072, '1'));
+ bufferlist out_prepare_bl;
+ out_prepare_bl.substr_of(in_prepare_bl, 0, 1024);
+ expect_prepare_copyup(
+ mock_image_ctx,
+ {{1024U, {3072U, {SPARSE_EXTENT_STATE_DATA, 3072,
+ std::move(in_prepare_bl)}}}},
+ {{2048U, {1024U, {SPARSE_EXTENT_STATE_DATA, 1024,
+ std::move(out_prepare_bl)}}}});
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0),
+ {{2048, 1024}}, data.substr(0, 1024), 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {{0, 1024}});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+TEST_F(TestMockIoCopyupRequest, ProcessCopyupOverwrite) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->image_lock.lock();
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "1", 1, ictx->size,
+ ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
+ 0, {});
+ ictx->snapc = {1, {1}};
+ ictx->image_lock.unlock();
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_test_features(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, {{0, 4096}}, data, 0);
+
+ bufferlist in_prepare_bl;
+ in_prepare_bl.append(data);
+ bufferlist out_prepare_bl;
+ out_prepare_bl.substr_of(in_prepare_bl, 0, 1024);
+ expect_prepare_copyup(
+ mock_image_ctx,
+ {{0, {4096, {SPARSE_EXTENT_STATE_DATA, 4096,
+ std::move(in_prepare_bl)}}}},
+ {{0, {1024, {SPARSE_EXTENT_STATE_DATA, 1024, bufferlist{out_prepare_bl}}}},
+ {2048, {1024, {SPARSE_EXTENT_STATE_DATA, 1024,
+ bufferlist{out_prepare_bl}}}}});
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, 1, 0, OBJECT_EXISTS, true, 0);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_sparse_copyup(mock_image_ctx, 0, ictx->get_object_name(0),
+ {{0, 1024}, {2048, 1024}}, data.substr(0, 2048), 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, ictx->get_object_name(0), 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, 0, {{0, 4096}},
+ ImageArea::DATA, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request, {{0, 1024}});
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
+} // namespace io
+} // namespace librbd
diff --git a/src/test/librbd/io/test_mock_ImageRequest.cc b/src/test/librbd/io/test_mock_ImageRequest.cc
new file mode 100644
index 000000000..9d6423d66
--- /dev/null
+++ b/src/test/librbd/io/test_mock_ImageRequest.cc
@@ -0,0 +1,738 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "test/librbd/mock/cache/MockImageCache.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/io/ObjectDispatchSpec.h"
+#include "librbd/io/Utils.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx;
+
+struct MockTestJournal : public MockJournal {
+ MOCK_METHOD4(append_write_event, uint64_t(uint64_t, size_t,
+ const bufferlist &, bool));
+ MOCK_METHOD5(append_compare_and_write_event, uint64_t(uint64_t, size_t,
+ const bufferlist &,
+ const bufferlist &,
+ bool));
+ MOCK_METHOD5(append_io_event_mock, uint64_t(const journal::EventEntry&,
+ uint64_t, size_t, bool, int));
+ uint64_t append_io_event(journal::EventEntry &&event_entry,
+ uint64_t offset, size_t length,
+ bool flush_entry, int filter_ret_val) {
+ // googlemock doesn't support move semantics
+ return append_io_event_mock(event_entry, offset, length, flush_entry,
+ filter_ret_val);
+ }
+
+ MOCK_METHOD2(commit_io_event, void(uint64_t, int));
+};
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+
+ MockTestJournal* journal;
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockTestImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/io/ImageRequest.cc"
+
+namespace librbd {
+namespace io {
+
+namespace util {
+
+template <>
+void area_to_object_extents(MockTestImageCtx* image_ctx, uint64_t offset,
+ uint64_t length, ImageArea area,
+ uint64_t buffer_offset,
+ striper::LightweightObjectExtents* object_extents) {
+ Striper::file_to_extents(image_ctx->cct, &image_ctx->layout, offset, length,
+ 0, buffer_offset, object_extents);
+}
+
+template <>
+std::pair<Extents, ImageArea> object_to_area_extents(
+ MockTestImageCtx* image_ctx, uint64_t object_no,
+ const Extents& object_extents) {
+ Extents extents;
+ for (auto [off, len] : object_extents) {
+ Striper::extent_to_file(image_ctx->cct, &image_ctx->layout, object_no, off,
+ len, extents);
+ }
+ return {std::move(extents), ImageArea::DATA};
+}
+
+} // namespace util
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::WithoutArgs;
+using ::testing::Exactly;
+
+struct TestMockIoImageRequest : public TestMockFixture {
+ typedef ImageRequest<librbd::MockTestImageCtx> MockImageRequest;
+ typedef ImageReadRequest<librbd::MockTestImageCtx> MockImageReadRequest;
+ typedef ImageWriteRequest<librbd::MockTestImageCtx> MockImageWriteRequest;
+ typedef ImageDiscardRequest<librbd::MockTestImageCtx> MockImageDiscardRequest;
+ typedef ImageFlushRequest<librbd::MockTestImageCtx> MockImageFlushRequest;
+ typedef ImageWriteSameRequest<librbd::MockTestImageCtx> MockImageWriteSameRequest;
+ typedef ImageCompareAndWriteRequest<librbd::MockTestImageCtx> MockImageCompareAndWriteRequest;
+ typedef ImageListSnapsRequest<librbd::MockTestImageCtx> MockImageListSnapsRequest;
+
+ void expect_is_journal_appending(MockTestJournal &mock_journal, bool appending) {
+ EXPECT_CALL(mock_journal, is_journal_appending())
+ .WillOnce(Return(appending));
+ }
+
+ void expect_get_modify_timestamp(MockTestImageCtx &mock_image_ctx,
+ bool needs_update) {
+ if (needs_update) {
+ mock_image_ctx.mtime_update_interval = 5;
+ EXPECT_CALL(mock_image_ctx, get_modify_timestamp())
+ .WillOnce(Return(ceph_clock_now() - utime_t(10,0)));
+ } else {
+ mock_image_ctx.mtime_update_interval = 600;
+ EXPECT_CALL(mock_image_ctx, get_modify_timestamp())
+ .WillOnce(Return(ceph_clock_now()));
+ }
+ }
+
+ void expect_journal_append_io_event(MockTestJournal &mock_journal, uint64_t journal_tid,
+ uint64_t offset, size_t length) {
+ EXPECT_CALL(mock_journal, append_io_event_mock(_, offset, length, _, _))
+ .WillOnce(Return(journal_tid));
+ }
+
+ void expect_object_discard_request(MockTestImageCtx &mock_image_ctx,
+ uint64_t object_no, uint64_t offset,
+ uint32_t length, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_))
+ .WillOnce(Invoke([&mock_image_ctx, object_no, offset, length, r]
+ (ObjectDispatchSpec* spec) {
+ auto* discard_spec = boost::get<ObjectDispatchSpec::DiscardRequest>(&spec->request);
+ ASSERT_TRUE(discard_spec != nullptr);
+ ASSERT_EQ(object_no, discard_spec->object_no);
+ ASSERT_EQ(offset, discard_spec->object_off);
+ ASSERT_EQ(length, discard_spec->object_len);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ mock_image_ctx.image_ctx->op_work_queue->queue(&spec->dispatcher_ctx, r);
+ }));
+ }
+
+ void expect_object_request_send(MockTestImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_))
+ .WillOnce(Invoke([&mock_image_ctx, r](ObjectDispatchSpec* spec) {
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ mock_image_ctx.image_ctx->op_work_queue->queue(&spec->dispatcher_ctx, r);
+ }));
+ }
+
+ void expect_object_list_snaps_request(MockTestImageCtx &mock_image_ctx,
+ uint64_t object_no,
+ const SnapshotDelta& snap_delta,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_))
+ .WillOnce(
+ Invoke([&mock_image_ctx, object_no, snap_delta, r]
+ (ObjectDispatchSpec* spec) {
+ auto request = boost::get<
+ librbd::io::ObjectDispatchSpec::ListSnapsRequest>(
+ &spec->request);
+ ASSERT_TRUE(request != nullptr);
+ ASSERT_EQ(object_no, request->object_no);
+
+ *request->snapshot_delta = snap_delta;
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ mock_image_ctx.image_ctx->op_work_queue->queue(&spec->dispatcher_ctx, r);
+ }));
+ }
+};
+
+TEST_F(TestMockIoImageRequest, AioWriteModifyTimestamp) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ mock_image_ctx.mtime_update_interval = 5;
+
+ utime_t dummy = ceph_clock_now();
+ dummy -= utime_t(10,0);
+
+ EXPECT_CALL(mock_image_ctx, get_modify_timestamp())
+ .Times(Exactly(3))
+ .WillOnce(Return(dummy))
+ .WillOnce(Return(dummy))
+ .WillOnce(Return(dummy + utime_t(10,0)));
+
+ EXPECT_CALL(mock_image_ctx, set_modify_timestamp(_))
+ .Times(Exactly(1));
+
+ InSequence seq;
+ expect_is_journal_appending(mock_journal, false);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ C_SaferCond aio_comp_ctx_1, aio_comp_ctx_2;
+ AioCompletion *aio_comp_1 = AioCompletion::create_and_start(
+ &aio_comp_ctx_1, ictx, AIO_TYPE_WRITE);
+
+ AioCompletion *aio_comp_2 = AioCompletion::create_and_start(
+ &aio_comp_ctx_2, ictx, AIO_TYPE_WRITE);
+
+ bufferlist bl;
+ bl.append("1");
+ MockImageWriteRequest mock_aio_image_write_1(
+ mock_image_ctx, aio_comp_1, {{0, 1}}, ImageArea::DATA, std::move(bl),
+ 0, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_write_1.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx_1.wait());
+
+ expect_is_journal_appending(mock_journal, false);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ bl.append("1");
+ MockImageWriteRequest mock_aio_image_write_2(
+ mock_image_ctx, aio_comp_2, {{0, 1}}, ImageArea::DATA, std::move(bl),
+ 0, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_write_2.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx_2.wait());
+}
+
+TEST_F(TestMockIoImageRequest, AioReadAccessTimestamp) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ mock_image_ctx.atime_update_interval = 5;
+
+ utime_t dummy = ceph_clock_now();
+ dummy -= utime_t(10,0);
+
+ EXPECT_CALL(mock_image_ctx, get_access_timestamp())
+ .Times(Exactly(3))
+ .WillOnce(Return(dummy))
+ .WillOnce(Return(dummy))
+ .WillOnce(Return(dummy + utime_t(10,0)));
+
+ EXPECT_CALL(mock_image_ctx, set_access_timestamp(_))
+ .Times(Exactly(1));
+
+ InSequence seq;
+ expect_object_request_send(mock_image_ctx, 0);
+
+ C_SaferCond aio_comp_ctx_1, aio_comp_ctx_2;
+ AioCompletion *aio_comp_1 = AioCompletion::create_and_start(
+ &aio_comp_ctx_1, ictx, AIO_TYPE_READ);
+
+
+ ReadResult rr;
+ MockImageReadRequest mock_aio_image_read_1(
+ mock_image_ctx, aio_comp_1, {{0, 1}}, ImageArea::DATA, std::move(rr),
+ mock_image_ctx.get_data_io_context(), 0, 0, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_read_1.send();
+ }
+ ASSERT_EQ(1, aio_comp_ctx_1.wait());
+
+ AioCompletion *aio_comp_2 = AioCompletion::create_and_start(
+ &aio_comp_ctx_2, ictx, AIO_TYPE_READ);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ MockImageReadRequest mock_aio_image_read_2(
+ mock_image_ctx, aio_comp_2, {{0, 1}}, ImageArea::DATA, std::move(rr),
+ mock_image_ctx.get_data_io_context(), 0, 0, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_read_2.send();
+ }
+ ASSERT_EQ(1, aio_comp_ctx_2.wait());
+}
+
+TEST_F(TestMockIoImageRequest, PartialDiscard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->discard_granularity_bytes = 0;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.journal = nullptr;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_object_discard_request(mock_image_ctx, 0, 16, 63, 0);
+ expect_object_discard_request(mock_image_ctx, 0, 84, 100, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp, {{16, 63}, {84, 100}}, ImageArea::DATA,
+ ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, TailDiscard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, resize(ictx, ictx->layout.object_size));
+ ictx->discard_granularity_bytes = 2 * ictx->layout.object_size;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.journal = nullptr;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_object_discard_request(
+ mock_image_ctx, 0, ictx->layout.object_size - 1024, 1024, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp,
+ {{ictx->layout.object_size - 1024, 1024}}, ImageArea::DATA,
+ ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, DiscardGranularity) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, resize(ictx, ictx->layout.object_size));
+ ictx->discard_granularity_bytes = 32;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.journal = nullptr;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_object_discard_request(mock_image_ctx, 0, 32, 32, 0);
+ expect_object_discard_request(mock_image_ctx, 0, 96, 64, 0);
+ expect_object_discard_request(
+ mock_image_ctx, 0, ictx->layout.object_size - 32, 32, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp,
+ {{16, 63}, {96, 31}, {84, 100}, {ictx->layout.object_size - 33, 33}},
+ ImageArea::DATA, ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, PartialDiscardJournalAppendEnabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->discard_granularity_bytes = 0;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, true);
+ expect_journal_append_io_event(mock_journal, 0, 16, 63);
+ expect_journal_append_io_event(mock_journal, 1, 84, 100);
+ expect_object_discard_request(mock_image_ctx, 0, 16, 63, 0);
+ expect_object_discard_request(mock_image_ctx, 0, 84, 100, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp, {{16, 63}, {84, 100}}, ImageArea::DATA,
+ ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, TailDiscardJournalAppendEnabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, resize(ictx, ictx->layout.object_size));
+ ictx->discard_granularity_bytes = 2 * ictx->layout.object_size;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, true);
+ expect_journal_append_io_event(
+ mock_journal, 0, ictx->layout.object_size - 1024, 1024);
+ expect_object_discard_request(
+ mock_image_ctx, 0, ictx->layout.object_size - 1024, 1024, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp,
+ {{ictx->layout.object_size - 1024, 1024}}, ImageArea::DATA,
+ ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, PruneRequiredDiscardJournalAppendEnabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->discard_granularity_bytes = 32;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, true);
+ EXPECT_CALL(mock_journal, append_io_event_mock(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_)).Times(0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp, {{96, 31}}, ImageArea::DATA,
+ ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, LengthModifiedDiscardJournalAppendEnabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->discard_granularity_bytes = 32;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, true);
+ expect_journal_append_io_event(mock_journal, 0, 32, 32);
+ expect_object_discard_request(mock_image_ctx, 0, 32, 32, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp, {{16, 63}}, ImageArea::DATA,
+ ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, DiscardGranularityJournalAppendEnabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, resize(ictx, ictx->layout.object_size));
+ ictx->discard_granularity_bytes = 32;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, true);
+ expect_journal_append_io_event(mock_journal, 0, 32, 32);
+ expect_journal_append_io_event(mock_journal, 1, 96, 64);
+ expect_journal_append_io_event(
+ mock_journal, 2, ictx->layout.object_size - 32, 32);
+ expect_object_discard_request(mock_image_ctx, 0, 32, 32, 0);
+ expect_object_discard_request(mock_image_ctx, 0, 96, 64, 0);
+ expect_object_discard_request(
+ mock_image_ctx, 0, ictx->layout.object_size - 32, 32, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp,
+ {{16, 63}, {96, 31}, {84, 100}, {ictx->layout.object_size - 33, 33}},
+ ImageArea::DATA, ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, AioWriteJournalAppendDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, false);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_WRITE);
+
+ bufferlist bl;
+ bl.append("1");
+ MockImageWriteRequest mock_aio_image_write(
+ mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA, std::move(bl), 0, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_write.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, AioDiscardJournalAppendDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->discard_granularity_bytes = 0;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, false);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+ MockImageDiscardRequest mock_aio_image_discard(
+ mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA,
+ ictx->discard_granularity_bytes, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_discard.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, AioFlushJournalAppendDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_journal_appending(mock_journal, false);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_FLUSH);
+ MockImageFlushRequest mock_aio_image_flush(mock_image_ctx, aio_comp,
+ FLUSH_SOURCE_USER, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_flush.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, AioWriteSameJournalAppendDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, false);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_WRITESAME);
+
+ bufferlist bl;
+ bl.append("1");
+ MockImageWriteSameRequest mock_aio_image_writesame(
+ mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA, std::move(bl), 0, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_writesame.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, AioCompareAndWriteJournalAppendDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockTestJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ InSequence seq;
+ expect_get_modify_timestamp(mock_image_ctx, false);
+ expect_is_journal_appending(mock_journal, false);
+ expect_object_request_send(mock_image_ctx, 0);
+
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_COMPARE_AND_WRITE);
+
+ bufferlist cmp_bl;
+ cmp_bl.append("1");
+ bufferlist write_bl;
+ write_bl.append("1");
+ uint64_t mismatch_offset;
+ MockImageCompareAndWriteRequest mock_aio_image_write(
+ mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA,
+ std::move(cmp_bl), std::move(write_bl), &mismatch_offset, 0, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_aio_image_write.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, ListSnaps) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.layout.object_size = 16384;
+ mock_image_ctx.layout.stripe_unit = 4096;
+ mock_image_ctx.layout.stripe_count = 2;
+
+ InSequence seq;
+
+ SnapshotDelta object_snapshot_delta;
+ object_snapshot_delta[{5,6}].insert(
+ 0, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ object_snapshot_delta[{5,5}].insert(
+ 4096, 4096, {SPARSE_EXTENT_STATE_ZEROED, 4096});
+ expect_object_list_snaps_request(mock_image_ctx, 0, object_snapshot_delta, 0);
+ object_snapshot_delta = {};
+ object_snapshot_delta[{5,6}].insert(
+ 1024, 3072, {SPARSE_EXTENT_STATE_DATA, 3072});
+ object_snapshot_delta[{5,5}].insert(
+ 2048, 2048, {SPARSE_EXTENT_STATE_ZEROED, 2048});
+ expect_object_list_snaps_request(mock_image_ctx, 1, object_snapshot_delta, 0);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond aio_comp_ctx;
+ AioCompletion *aio_comp = AioCompletion::create_and_start(
+ &aio_comp_ctx, ictx, AIO_TYPE_GENERIC);
+ MockImageListSnapsRequest mock_image_list_snaps_request(
+ mock_image_ctx, aio_comp, {{0, 16384}, {16384, 16384}}, ImageArea::DATA,
+ {0, CEPH_NOSNAP}, 0, &snapshot_delta, {});
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_image_list_snaps_request.send();
+ }
+ ASSERT_EQ(0, aio_comp_ctx.wait());
+
+ SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{5,6}].insert(
+ 0, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ expected_snapshot_delta[{5,6}].insert(
+ 5120, 3072, {SPARSE_EXTENT_STATE_DATA, 3072});
+ expected_snapshot_delta[{5,5}].insert(
+ 6144, 6144, {SPARSE_EXTENT_STATE_ZEROED, 6144});
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+}
+
+} // namespace io
+} // namespace librbd
diff --git a/src/test/librbd/io/test_mock_ObjectRequest.cc b/src/test/librbd/io/test_mock_ObjectRequest.cc
new file mode 100644
index 000000000..0690b7722
--- /dev/null
+++ b/src/test/librbd/io/test_mock_ObjectRequest.cc
@@ -0,0 +1,1968 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/mock/cache/MockImageCache.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/io/CopyupRequest.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/io/ObjectRequest.h"
+#include "librbd/io/Utils.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace io {
+
+template <>
+struct CopyupRequest<librbd::MockImageCtx> {
+ MOCK_METHOD0(send, void());
+ MOCK_METHOD2(append_request, void(AbstractObjectWriteRequest<librbd::MockTestImageCtx>*,
+ const Extents&));
+};
+
+template <>
+struct CopyupRequest<librbd::MockTestImageCtx> : public CopyupRequest<librbd::MockImageCtx> {
+ static CopyupRequest* s_instance;
+ static CopyupRequest* create(librbd::MockTestImageCtx *ictx,
+ uint64_t objectno, Extents &&image_extents,
+ ImageArea area,
+ const ZTracer::Trace& parent_trace) {
+ return s_instance;
+ }
+
+ CopyupRequest() {
+ s_instance = this;
+ }
+};
+
+CopyupRequest<librbd::MockTestImageCtx>* CopyupRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct ImageListSnapsRequest<librbd::MockTestImageCtx> {
+ static ImageListSnapsRequest* s_instance;
+
+ AioCompletion* aio_comp;
+ Extents image_extents;
+ SnapshotDelta* snapshot_delta;
+
+ ImageListSnapsRequest() {
+ s_instance = this;
+ }
+ ImageListSnapsRequest(
+ librbd::MockImageCtx& image_ctx, AioCompletion* aio_comp,
+ Extents&& image_extents, ImageArea area, SnapIds&& snap_ids,
+ int list_snaps_flags, SnapshotDelta* snapshot_delta,
+ const ZTracer::Trace& parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_comp = aio_comp;
+ s_instance->image_extents = image_extents;
+ s_instance->snapshot_delta = snapshot_delta;
+ }
+
+
+ MOCK_METHOD0(execute_send, void());
+ void send() {
+ ceph_assert(s_instance != nullptr);
+ s_instance->execute_send();
+ }
+};
+
+ImageListSnapsRequest<librbd::MockTestImageCtx>* ImageListSnapsRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace util {
+
+template <>
+void area_to_object_extents(MockTestImageCtx* image_ctx, uint64_t offset,
+ uint64_t length, ImageArea area,
+ uint64_t buffer_offset,
+ striper::LightweightObjectExtents* object_extents) {
+ Striper::file_to_extents(image_ctx->cct, &image_ctx->layout, offset, length,
+ 0, buffer_offset, object_extents);
+}
+
+template <>
+std::pair<Extents, ImageArea> object_to_area_extents(
+ MockTestImageCtx* image_ctx, uint64_t object_no,
+ const Extents& object_extents) {
+ Extents extents;
+ for (auto [off, len] : object_extents) {
+ Striper::extent_to_file(image_ctx->cct, &image_ctx->layout, object_no, off,
+ len, extents);
+ }
+ return {std::move(extents), ImageArea::DATA};
+}
+
+namespace {
+
+struct Mock {
+ static Mock* s_instance;
+
+ Mock() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD6(read_parent,
+ void(librbd::MockTestImageCtx *, uint64_t, ReadExtents*,
+ librados::snap_t, const ZTracer::Trace &, Context*));
+};
+
+Mock *Mock::s_instance = nullptr;
+
+} // anonymous namespace
+
+template<> void read_parent(
+ librbd::MockTestImageCtx *image_ctx, uint64_t object_no,
+ ReadExtents* extents, librados::snap_t snap_id,
+ const ZTracer::Trace &trace, Context* on_finish) {
+ Mock::s_instance->read_parent(image_ctx, object_no, extents, snap_id, trace,
+ on_finish);
+}
+
+} // namespace util
+
+} // namespace io
+} // namespace librbd
+
+#include "librbd/io/ObjectRequest.cc"
+
+namespace librbd {
+namespace io {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+struct TestMockIoObjectRequest : public TestMockFixture {
+ typedef ObjectRequest<librbd::MockTestImageCtx> MockObjectRequest;
+ typedef ObjectReadRequest<librbd::MockTestImageCtx> MockObjectReadRequest;
+ typedef ObjectWriteRequest<librbd::MockTestImageCtx> MockObjectWriteRequest;
+ typedef ObjectDiscardRequest<librbd::MockTestImageCtx> MockObjectDiscardRequest;
+ typedef ObjectWriteSameRequest<librbd::MockTestImageCtx> MockObjectWriteSameRequest;
+ typedef ObjectCompareAndWriteRequest<librbd::MockTestImageCtx> MockObjectCompareAndWriteRequest;
+ typedef ObjectListSnapsRequest<librbd::MockTestImageCtx> MockObjectListSnapsRequest;
+ typedef AbstractObjectWriteRequest<librbd::MockTestImageCtx> MockAbstractObjectWriteRequest;
+ typedef CopyupRequest<librbd::MockTestImageCtx> MockCopyupRequest;
+ typedef ImageListSnapsRequest<librbd::MockTestImageCtx> MockImageListSnapsRequest;
+ typedef util::Mock MockUtils;
+
+ void expect_object_may_exist(MockTestImageCtx &mock_image_ctx,
+ uint64_t object_no, bool exists) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, object_may_exist(object_no))
+ .WillOnce(Return(exists));
+ }
+ }
+
+ void expect_get_object_size(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, get_object_size()).WillRepeatedly(Return(
+ mock_image_ctx.layout.object_size));
+ }
+
+ void expect_get_parent_overlap(MockTestImageCtx &mock_image_ctx,
+ librados::snap_t snap_id, uint64_t overlap,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, get_parent_overlap(snap_id, _))
+ .WillOnce(WithArg<1>(Invoke([overlap, r](uint64_t *o) {
+ *o = overlap;
+ return r;
+ })));
+ }
+
+ void expect_prune_parent_extents(MockTestImageCtx &mock_image_ctx,
+ const Extents& extents,
+ uint64_t overlap, uint64_t object_overlap) {
+ EXPECT_CALL(mock_image_ctx, prune_parent_extents(_, _, overlap, _))
+ .WillOnce(WithArg<0>(Invoke([extents, object_overlap](Extents& e) {
+ e = extents;
+ return object_overlap;
+ })));
+ }
+
+ void expect_get_read_flags(MockTestImageCtx &mock_image_ctx,
+ librados::snap_t snap_id, int flags) {
+ EXPECT_CALL(mock_image_ctx, get_read_flags(snap_id))
+ .WillOnce(Return(flags));
+ }
+
+ void expect_is_lock_owner(MockExclusiveLock& mock_exclusive_lock) {
+ EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillRepeatedly(
+ Return(true));
+ }
+
+ void expect_read(MockTestImageCtx &mock_image_ctx,
+ const std::string& oid, uint64_t off, uint64_t len,
+ const std::string& data, int r) {
+ bufferlist bl;
+ bl.append(data);
+
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto& expect = EXPECT_CALL(mock_io_ctx, read(oid, len, off, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(WithArg<3>(Invoke([bl](bufferlist *out_bl) {
+ out_bl->append(bl);
+ return bl.length();
+ })));
+ }
+ }
+
+ void expect_sparse_read(MockTestImageCtx &mock_image_ctx,
+ const std::string& oid, uint64_t off, uint64_t len,
+ const std::string& data, int r) {
+ bufferlist bl;
+ bl.append(data);
+
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto& expect = EXPECT_CALL(mock_io_ctx,
+ sparse_read(oid, off, len, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(WithArg<4>(Invoke([bl](bufferlist *out_bl) {
+ out_bl->append(bl);
+ return bl.length();
+ })));
+ }
+ }
+
+ void expect_read_parent(MockUtils &mock_utils, uint64_t object_no,
+ ReadExtents* extents, librados::snap_t snap_id,
+ int r) {
+ EXPECT_CALL(mock_utils,
+ read_parent(_, object_no, extents, snap_id, _, _))
+ .WillOnce(WithArg<5>(CompleteContext(r, static_cast<asio::ContextWQ*>(nullptr))));
+ }
+
+ void expect_copyup(MockCopyupRequest& mock_copyup_request, int r) {
+ EXPECT_CALL(mock_copyup_request, send())
+ .WillOnce(Invoke([]() {}));
+ }
+
+ void expect_copyup(MockCopyupRequest& mock_copyup_request,
+ MockAbstractObjectWriteRequest** write_request, int r) {
+ EXPECT_CALL(mock_copyup_request, append_request(_, _))
+ .WillOnce(WithArg<0>(
+ Invoke([write_request](MockAbstractObjectWriteRequest *req) {
+ *write_request = req;
+ })));
+ EXPECT_CALL(mock_copyup_request, send())
+ .WillOnce(Invoke([write_request, r]() {
+ (*write_request)->handle_copyup(r);
+ }));
+ }
+
+ void expect_object_map_update(MockTestImageCtx &mock_image_ctx,
+ uint64_t start_object, uint64_t end_object,
+ uint8_t state,
+ const boost::optional<uint8_t> &current_state,
+ bool updated, int ret_val) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map,
+ aio_update(CEPH_NOSNAP, start_object, end_object, state,
+ current_state, _, false, _))
+ .WillOnce(WithArg<7>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
+ if (updated) {
+ mock_image_ctx.op_work_queue->queue(ctx, ret_val);
+ }
+ return updated;
+ })));
+ }
+ }
+
+ void expect_assert_exists(MockTestImageCtx &mock_image_ctx, int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ EXPECT_CALL(mock_io_ctx, assert_exists(_, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_write(MockTestImageCtx &mock_image_ctx,
+ uint64_t offset, uint64_t length, int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto &expect = EXPECT_CALL(mock_io_ctx,
+ write(_, _, length, offset, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_write_full(MockTestImageCtx &mock_image_ctx, int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto &expect = EXPECT_CALL(mock_io_ctx, write_full(_, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_writesame(MockTestImageCtx &mock_image_ctx,
+ uint64_t offset, uint64_t length, int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto &expect = EXPECT_CALL(mock_io_ctx, writesame(_, _, length, offset, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_remove(MockTestImageCtx &mock_image_ctx, int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto &expect = EXPECT_CALL(mock_io_ctx, remove(_, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_create(MockTestImageCtx &mock_image_ctx, bool exclusive) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ EXPECT_CALL(mock_io_ctx, create(_, exclusive, _))
+ .Times(1);
+ }
+
+ void expect_truncate(MockTestImageCtx &mock_image_ctx, int offset, int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto &expect = EXPECT_CALL(mock_io_ctx, truncate(_, offset, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_zero(MockTestImageCtx &mock_image_ctx, int offset, int length,
+ int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto &expect = EXPECT_CALL(mock_io_ctx, zero(_, offset, length, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_cmpext(MockTestImageCtx &mock_image_ctx, int offset, int r) {
+ auto& mock_io_ctx = librados::get_mock_io_ctx(
+ mock_image_ctx.rados_api, *mock_image_ctx.get_data_io_context());
+ auto &expect = EXPECT_CALL(mock_io_ctx, cmpext(_, offset, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_list_snaps(MockTestImageCtx &mock_image_ctx,
+ const librados::snap_set_t& snap_set, int r) {
+ auto io_context = *mock_image_ctx.get_data_io_context();
+ io_context.read_snap(CEPH_SNAPDIR);
+ auto& mock_io_ctx = librados::get_mock_io_ctx(mock_image_ctx.rados_api,
+ io_context);
+ EXPECT_CALL(mock_io_ctx, list_snaps(_, _))
+ .WillOnce(WithArg<1>(Invoke(
+ [snap_set, r](librados::snap_set_t* out_snap_set) {
+ *out_snap_set = snap_set;
+ return r;
+ })));
+ }
+
+ void expect_image_list_snaps(MockImageListSnapsRequest& req,
+ const Extents& image_extents,
+ const SnapshotDelta& image_snapshot_delta,
+ int r) {
+ EXPECT_CALL(req, execute_send())
+ .WillOnce(Invoke(
+ [&req, image_extents, image_snapshot_delta, r]() {
+ ASSERT_EQ(image_extents, req.image_extents);
+ *req.snapshot_delta = image_snapshot_delta;
+
+ auto aio_comp = req.aio_comp;
+ aio_comp->set_request_count(1);
+ aio_comp->add_request();
+ aio_comp->complete_request(r);
+ }));
+ }
+};
+
+TEST_F(TestMockIoObjectRequest, Read) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->sparse_read_threshold_bytes = 8096;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+ expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096,
+ std::string(4096, '1'), 0);
+ expect_read(mock_image_ctx, ictx->get_object_name(0), 8192, 4096,
+ std::string(4096, '2'), 0);
+
+ C_SaferCond ctx;
+ uint64_t version;
+ ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ auto req = MockObjectReadRequest::create(
+ &mock_image_ctx, 0, &extents,
+ mock_image_ctx.get_data_io_context(), 0, 0, {},
+ &version, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ bufferlist expected_bl1;
+ expected_bl1.append(std::string(4096, '1'));
+ bufferlist expected_bl2;
+ expected_bl2.append(std::string(4096, '2'));
+
+ ASSERT_EQ(extents[0].extent_map.size(), 0);
+ ASSERT_EQ(extents[1].extent_map.size(), 0);
+ ASSERT_TRUE(extents[0].bl.contents_equal(expected_bl1));
+ ASSERT_TRUE(extents[1].bl.contents_equal(expected_bl2));
+}
+
+TEST_F(TestMockIoObjectRequest, SparseReadThreshold) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->sparse_read_threshold_bytes = ictx->get_object_size();
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+ expect_sparse_read(mock_image_ctx, ictx->get_object_name(0), 0,
+ ictx->sparse_read_threshold_bytes,
+ std::string(ictx->sparse_read_threshold_bytes, '1'), 0);
+
+ C_SaferCond ctx;
+
+ ReadExtents extents = {{0, ictx->sparse_read_threshold_bytes}};
+ auto req = MockObjectReadRequest::create(
+ &mock_image_ctx, 0, &extents,
+ mock_image_ctx.get_data_io_context(), 0, 0, {}, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, ReadError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->sparse_read_threshold_bytes = 8096;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+ expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -EPERM);
+
+ C_SaferCond ctx;
+ ReadExtents extents = {{0, 4096}};
+ auto req = MockObjectReadRequest::create(
+ &mock_image_ctx, 0, &extents,
+ mock_image_ctx.get_data_io_context(), 0, 0, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, ParentRead) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ictx->sparse_read_threshold_bytes = 8096;
+ ictx->clone_copy_on_read = false;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.parent = &mock_image_ctx;
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+ expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
+
+ MockUtils mock_utils;
+ ReadExtents extents = {{0, 4096}};
+ expect_read_parent(mock_utils, 0, &extents, CEPH_NOSNAP, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectReadRequest::create(
+ &mock_image_ctx, 0, &extents,
+ mock_image_ctx.get_data_io_context(), 0, 0, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, ParentReadError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ictx->sparse_read_threshold_bytes = 8096;
+ ictx->clone_copy_on_read = false;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.parent = &mock_image_ctx;
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+ expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
+
+ MockUtils mock_utils;
+ ReadExtents extents = {{0, 4096}};
+ expect_read_parent(mock_utils, 0, &extents, CEPH_NOSNAP, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockObjectReadRequest::create(
+ &mock_image_ctx, 0, &extents,
+ mock_image_ctx.get_data_io_context(), 0, 0, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, SkipParentRead) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ictx->sparse_read_threshold_bytes = 8096;
+ ictx->clone_copy_on_read = false;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.parent = &mock_image_ctx;
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+ expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
+
+ ReadExtents extents = {{0, 4096}};
+ C_SaferCond ctx;
+ auto req = MockObjectReadRequest::create(
+ &mock_image_ctx, 0, &extents, mock_image_ctx.get_data_io_context(), 0,
+ READ_FLAG_DISABLE_READ_FROM_PARENT, {}, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CopyOnRead) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ictx->sparse_read_threshold_bytes = 8096;
+ ictx->clone_copy_on_read = true;
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.parent = &mock_image_ctx;
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+ expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
+
+ MockUtils mock_utils;
+ ReadExtents extents = {{0, 4096}};
+ expect_read_parent(mock_utils, 0, &extents, CEPH_NOSNAP, 0);
+
+ MockCopyupRequest mock_copyup_request;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_copyup(mock_copyup_request, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectReadRequest::create(
+ &mock_image_ctx, 0, &extents,
+ mock_image_ctx.get_data_io_context(), 0, 0, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, Write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, WriteWithCreateExclusiveFlag) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ // exclusive create should succeed
+ {
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false,
+ 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl),
+ mock_image_ctx.get_data_io_context(), 0,
+ OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ // exclusive create should fail since object already exists
+ {
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false,
+ 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl),
+ mock_image_ctx.get_data_io_context(), 0,
+ OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(-EEXIST, ctx.wait());
+ }
+}
+
+TEST_F(TestMockIoObjectRequest, WriteWithAssertVersion) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ // write an object
+ {
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false,
+ 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl),
+ mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ // assert version should succeed (version = 1)
+ {
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false,
+ 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl),
+ mock_image_ctx.get_data_io_context(), 0, 0,
+ std::make_optional(1), {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ // assert with wrong (lower) version (version = 2)
+ {
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false,
+ 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl),
+ mock_image_ctx.get_data_io_context(), 0, 0,
+ std::make_optional(1), {}, &ctx);
+ req->send();
+ ASSERT_EQ(-ERANGE, ctx.wait());
+ }
+
+ // assert with wrong (higher) version (version = 2)
+ {
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false,
+ 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl),
+ mock_image_ctx.get_data_io_context(), 0, 0, std::make_optional(3),
+ {}, &ctx);
+ req->send();
+ ASSERT_EQ(-EOVERFLOW, ctx.wait());
+ }
+}
+
+TEST_F(TestMockIoObjectRequest, WriteFull) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist bl;
+ bl.append(std::string(ictx->get_object_size(), '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_write_full(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, WriteObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, true, 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, WriteError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_write(mock_image_ctx, 0, 4096, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, Copyup) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_assert_exists(mock_image_ctx, -ENOENT);
+
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ MockCopyupRequest mock_copyup_request;
+ expect_copyup(mock_copyup_request, &write_request, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CopyupRestart) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_assert_exists(mock_image_ctx, -ENOENT);
+
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ MockCopyupRequest mock_copyup_request;
+ expect_copyup(mock_copyup_request, &write_request, -ERESTART);
+ expect_assert_exists(mock_image_ctx, 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CopyupOptimization) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_object_may_exist(mock_image_ctx, 0, false);
+
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ MockCopyupRequest mock_copyup_request;
+ expect_copyup(mock_copyup_request, &write_request, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CopyupError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_assert_exists(mock_image_ctx, -ENOENT);
+
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ MockCopyupRequest mock_copyup_request;
+ expect_copyup(mock_copyup_request, &write_request, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, DiscardRemove) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_PENDING, {}, false, 0);
+ expect_remove(mock_image_ctx, 0);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_NONEXISTENT,
+ OBJECT_PENDING, false, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectDiscardRequest::create_discard(
+ &mock_image_ctx, 0, 0, mock_image_ctx.get_object_size(),
+ mock_image_ctx.get_data_io_context(), 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, DiscardRemoveTruncate) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), features, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_object_may_exist(mock_image_ctx, 0, false);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_create(mock_image_ctx, false);
+ expect_truncate(mock_image_ctx, 0, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectDiscardRequest::create_discard(
+ &mock_image_ctx, 0, 0, mock_image_ctx.get_object_size(),
+ mock_image_ctx.get_data_io_context(),
+ OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, DiscardTruncateAssertExists) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), features, &order));
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, clone_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ image.close();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_assert_exists(mock_image_ctx, 0);
+ expect_truncate(mock_image_ctx, 0, 0);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx), create(_, _, _))
+ .Times(0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectDiscardRequest::create_discard(
+ &mock_image_ctx, 0, 0, mock_image_ctx.get_object_size(),
+ mock_image_ctx.get_data_io_context(),
+ OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, DiscardTruncate) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_truncate(mock_image_ctx, 1, 0);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx), create(_, _, _))
+ .Times(0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectDiscardRequest::create_discard(
+ &mock_image_ctx, 0, 1, mock_image_ctx.get_object_size() - 1,
+ mock_image_ctx.get_data_io_context(), 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, DiscardZero) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_zero(mock_image_ctx, 1, 1, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectDiscardRequest::create_discard(
+ &mock_image_ctx, 0, 1, 1, mock_image_ctx.get_data_io_context(), 0, {},
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, DiscardDisableObjectMapUpdate) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_remove(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectDiscardRequest::create_discard(
+ &mock_image_ctx, 0, 0, mock_image_ctx.get_object_size(),
+ mock_image_ctx.get_data_io_context(),
+ OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE |
+ OBJECT_DISCARD_FLAG_DISABLE_OBJECT_MAP_UPDATE, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, DiscardNoOp) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, false);
+
+ C_SaferCond ctx;
+ auto req = MockObjectDiscardRequest::create_discard(
+ &mock_image_ctx, 0, 0, mock_image_ctx.get_object_size(),
+ mock_image_ctx.get_data_io_context(),
+ OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE |
+ OBJECT_DISCARD_FLAG_DISABLE_OBJECT_MAP_UPDATE, {},
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, WriteSame) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_writesame(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteSameRequest::create_write_same(
+ &mock_image_ctx, 0, 0, 4096, std::move(bl),
+ mock_image_ctx.get_data_io_context(), 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CompareAndWrite) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append_zero(4096);
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_cmpext(mock_image_ctx, 0, 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ uint64_t mismatch_offset;
+ auto req = MockObjectWriteSameRequest::create_compare_and_write(
+ &mock_image_ctx, 0, 0, std::move(cmp_bl), std::move(bl),
+ mock_image_ctx.get_data_io_context(), &mismatch_offset, 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CompareAndWriteFull) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append_zero(ictx->get_object_size());
+
+ bufferlist bl;
+ bl.append(std::string(ictx->get_object_size(), '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_cmpext(mock_image_ctx, 0, 0);
+ expect_write_full(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ uint64_t mismatch_offset;
+ auto req = MockObjectWriteSameRequest::create_compare_and_write(
+ &mock_image_ctx, 0, 0, std::move(cmp_bl), std::move(bl),
+ mock_image_ctx.get_data_io_context(), &mismatch_offset, 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CompareAndWriteCopyup) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append_zero(4096);
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_assert_exists(mock_image_ctx, -ENOENT);
+
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ MockCopyupRequest mock_copyup_request;
+ expect_copyup(mock_copyup_request, &write_request, 0);
+
+ expect_assert_exists(mock_image_ctx, 0);
+ expect_cmpext(mock_image_ctx, 0, 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ uint64_t mismatch_offset;
+ auto req = MockObjectWriteSameRequest::create_compare_and_write(
+ &mock_image_ctx, 0, 0, std::move(cmp_bl), std::move(bl),
+ mock_image_ctx.get_data_io_context(), &mismatch_offset, 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, CompareAndWriteMismatch) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append_zero(4096);
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_cmpext(mock_image_ctx, 0, -MAX_ERRNO - 1);
+
+ C_SaferCond ctx;
+ uint64_t mismatch_offset;
+ auto req = MockObjectWriteSameRequest::create_compare_and_write(
+ &mock_image_ctx, 0, 0, std::move(cmp_bl), std::move(bl),
+ mock_image_ctx.get_data_io_context(), &mismatch_offset, 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(-EILSEQ, ctx.wait());
+ ASSERT_EQ(1ULL, mismatch_offset);
+}
+
+TEST_F(TestMockIoObjectRequest, ObjectMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, true,
+ -EBLOCKLISTED);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.get_data_io_context(),
+ 0, 0, std::nullopt, {}, &ctx);
+ req->send();
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, ListSnaps) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.snaps = {3, 4, 5, 6, 7};
+
+ librados::snap_set_t snap_set;
+ snap_set.seq = 6;
+ librados::clone_info_t clone_info;
+
+ clone_info.cloneid = 3;
+ clone_info.snaps = {3};
+ clone_info.overlap = std::vector<std::pair<uint64_t,uint64_t>>{
+ {0, 4194304}};
+ clone_info.size = 4194304;
+ snap_set.clones.push_back(clone_info);
+
+ clone_info.cloneid = 4;
+ clone_info.snaps = {4};
+ clone_info.overlap = std::vector<std::pair<uint64_t,uint64_t>>{
+ {278528, 4096}, {442368, 4096}, {1859584, 4096}, {2224128, 4096},
+ {2756608, 4096}, {3227648, 4096}, {3739648, 4096}, {3903488, 4096}};
+ clone_info.size = 4194304;
+ snap_set.clones.push_back(clone_info);
+
+ clone_info.cloneid = 6;
+ clone_info.snaps = {5, 6};
+ clone_info.overlap = std::vector<std::pair<uint64_t,uint64_t>>{
+ {425984, 4096}, {440320, 1024}, {1925120, 4096}, {2125824, 4096},
+ {2215936, 5120}, {3067904, 4096}};
+ clone_info.size = 3072000;
+ snap_set.clones.push_back(clone_info);
+
+ clone_info.cloneid = CEPH_NOSNAP;
+ clone_info.snaps = {};
+ clone_info.overlap = {};
+ clone_info.size = 4194304;
+ snap_set.clones.push_back(clone_info);
+
+ expect_list_snaps(mock_image_ctx, snap_set, 0);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond ctx;
+ auto req = MockObjectListSnapsRequest::create(
+ &mock_image_ctx, 0,
+ {{440320, 1024}, {2122728, 1024}, {2220032, 2048}, {3072000, 4096}},
+ {3, 4, 5, 6, 7, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{5,6}].insert(
+ 440320, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ expected_snapshot_delta[{5,6}].insert(
+ 2122728, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ expected_snapshot_delta[{5,6}].insert(
+ 2220032, 2048, {SPARSE_EXTENT_STATE_DATA, 2048});
+ expected_snapshot_delta[{7,CEPH_NOSNAP}].insert(
+ 2122728, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ expected_snapshot_delta[{7,CEPH_NOSNAP}].insert(
+ 2221056, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ expected_snapshot_delta[{7,CEPH_NOSNAP}].insert(
+ 3072000, 4096, {SPARSE_EXTENT_STATE_DATA, 4096});
+ expected_snapshot_delta[{5,5}].insert(
+ 3072000, 4096, {SPARSE_EXTENT_STATE_ZEROED, 4096});
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+}
+
+TEST_F(TestMockIoObjectRequest, ListSnapsENOENT) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_list_snaps(mock_image_ctx, {}, -ENOENT);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond ctx;
+ auto req = MockObjectListSnapsRequest::create(
+ &mock_image_ctx, 0,
+ {{440320, 1024}},
+ {0, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{0,0}].insert(
+ 440320, 1024, {SPARSE_EXTENT_STATE_DNE, 1024});
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+}
+
+TEST_F(TestMockIoObjectRequest, ListSnapsDNE) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.snaps = {2, 3, 4};
+
+ librados::snap_set_t snap_set;
+ snap_set.seq = 6;
+ librados::clone_info_t clone_info;
+
+ clone_info.cloneid = 4;
+ clone_info.snaps = {3, 4};
+ clone_info.overlap = std::vector<std::pair<uint64_t,uint64_t>>{
+ {0, 4194304}};
+ clone_info.size = 4194304;
+ snap_set.clones.push_back(clone_info);
+
+ expect_list_snaps(mock_image_ctx, snap_set, 0);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond ctx;
+ auto req = MockObjectListSnapsRequest::create(
+ &mock_image_ctx, 0,
+ {{440320, 1024}},
+ {2, 3, 4}, 0, {}, &snapshot_delta, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{2,2}].insert(
+ 440320, 1024, {SPARSE_EXTENT_STATE_DNE, 1024});
+ expected_snapshot_delta[{3,4}].insert(
+ 440320, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+}
+
+TEST_F(TestMockIoObjectRequest, ListSnapsEmpty) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_list_snaps(mock_image_ctx, {}, 0);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond ctx;
+ auto req = MockObjectListSnapsRequest::create(
+ &mock_image_ctx, 0,
+ {{440320, 1024}},
+ {0, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{0,0}].insert(
+ 440320, 1024, {SPARSE_EXTENT_STATE_ZEROED, 1024});
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+}
+
+TEST_F(TestMockIoObjectRequest, ListSnapsError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_list_snaps(mock_image_ctx, {}, -EPERM);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond ctx;
+ auto req = MockObjectListSnapsRequest::create(
+ &mock_image_ctx, 0,
+ {{440320, 1024}, {2122728, 1024}, {2220032, 2048}, {3072000, 4096}},
+ {3, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, ListSnapsParent) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.parent = &mock_image_ctx;
+
+ InSequence seq;
+
+ expect_list_snaps(mock_image_ctx, {}, -ENOENT);
+
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+
+ MockImageListSnapsRequest mock_image_list_snaps_request;
+ SnapshotDelta image_snapshot_delta;
+ image_snapshot_delta[{1,6}].insert(
+ 0, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ expect_image_list_snaps(mock_image_list_snaps_request,
+ {{0, 4096}}, image_snapshot_delta, 0);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond ctx;
+ auto req = MockObjectListSnapsRequest::create(
+ &mock_image_ctx, 0,
+ {{440320, 1024}, {2122728, 1024}, {2220032, 2048}, {3072000, 4096}},
+ {0, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{0,0}].insert(
+ 0, 1024, {SPARSE_EXTENT_STATE_DATA, 1024});
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+}
+
+TEST_F(TestMockIoObjectRequest, ListSnapsWholeObject) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ mock_image_ctx.parent = &mock_image_ctx;
+
+ InSequence seq;
+
+ librados::snap_set_t snap_set;
+ snap_set.seq = 3;
+ librados::clone_info_t clone_info;
+
+ clone_info.cloneid = 3;
+ clone_info.snaps = {3};
+ clone_info.overlap = std::vector<std::pair<uint64_t,uint64_t>>{{0, 1}};
+ clone_info.size = 4194304;
+ snap_set.clones.push_back(clone_info);
+
+ clone_info.cloneid = CEPH_NOSNAP;
+ clone_info.snaps = {};
+ clone_info.overlap = {};
+ clone_info.size = 4194304;
+ snap_set.clones.push_back(clone_info);
+
+ expect_list_snaps(mock_image_ctx, snap_set, 0);
+
+ SnapshotDelta snapshot_delta;
+ C_SaferCond ctx;
+ auto req = MockObjectListSnapsRequest::create(
+ &mock_image_ctx, 0, {{0, mock_image_ctx.layout.object_size - 1}},
+ {0, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{CEPH_NOSNAP,CEPH_NOSNAP}].insert(
+ 0, mock_image_ctx.layout.object_size - 1,
+ {SPARSE_EXTENT_STATE_DATA, mock_image_ctx.layout.object_size - 1});
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+}
+
+} // namespace io
+} // namespace librbd
+
diff --git a/src/test/librbd/io/test_mock_QosImageDispatch.cc b/src/test/librbd/io/test_mock_QosImageDispatch.cc
new file mode 100644
index 000000000..acd3b97c2
--- /dev/null
+++ b/src/test/librbd/io/test_mock_QosImageDispatch.cc
@@ -0,0 +1,89 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/exclusive_lock/MockPolicy.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/ImageRequestWQ.h"
+#include "librbd/io/ImageRequest.h"
+
+namespace librbd {
+namespace io {
+
+TEST_F(TestMockIoImageRequestWQ, QosNoLimit) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatchSpec mock_queued_image_request;
+ expect_was_throttled(mock_queued_image_request, false);
+ expect_set_throttled(mock_queued_image_request);
+
+ InSequence seq;
+ MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
+
+ mock_image_request_wq.apply_qos_limit(IMAGE_DISPATCH_FLAG_QOS_BPS_THROTTLE, 0,
+ 0, 1);
+
+ expect_front(mock_image_request_wq, &mock_queued_image_request);
+ expect_is_refresh_request(mock_image_ctx, false);
+ expect_is_write_op(mock_queued_image_request, true);
+ expect_dequeue(mock_image_request_wq, &mock_queued_image_request);
+ ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == &mock_queued_image_request);
+}
+
+TEST_F(TestMockIoImageRequestWQ, BPSQosNoBurst) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatchSpec mock_queued_image_request;
+ expect_was_throttled(mock_queued_image_request, false);
+ expect_set_throttled(mock_queued_image_request);
+
+ InSequence seq;
+ MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
+
+ mock_image_request_wq.apply_qos_limit(IMAGE_DISPATCH_FLAG_QOS_BPS_THROTTLE, 1,
+ 0, 1);
+
+ expect_front(mock_image_request_wq, &mock_queued_image_request);
+ expect_tokens_requested(mock_queued_image_request, 2, true);
+ expect_dequeue(mock_image_request_wq, &mock_queued_image_request);
+ expect_all_throttled(mock_queued_image_request, true);
+ expect_requeue_back(mock_image_request_wq);
+ expect_signal(mock_image_request_wq);
+ ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr);
+}
+
+TEST_F(TestMockIoImageRequestWQ, BPSQosWithBurst) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatchSpec mock_queued_image_request;
+ expect_was_throttled(mock_queued_image_request, false);
+ expect_set_throttled(mock_queued_image_request);
+
+ InSequence seq;
+ MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
+
+ mock_image_request_wq.apply_qos_limit(IMAGE_DISPATCH_FLAG_QOS_BPS_THROTTLE, 1,
+ 1, 1);
+
+ expect_front(mock_image_request_wq, &mock_queued_image_request);
+ expect_tokens_requested(mock_queued_image_request, 2, true);
+ expect_dequeue(mock_image_request_wq, &mock_queued_image_request);
+ expect_all_throttled(mock_queued_image_request, true);
+ expect_requeue_back(mock_image_request_wq);
+ expect_signal(mock_image_request_wq);
+ ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr);
+}
+
+} // namespace io
+} // namespace librbd
diff --git a/src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc b/src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc
new file mode 100644
index 000000000..a3d823d23
--- /dev/null
+++ b/src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc
@@ -0,0 +1,823 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockSafeTimer.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/io/ObjectDispatchSpec.h"
+#include "librbd/io/SimpleSchedulerObjectDispatch.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace io {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef ::MockSafeTimer SafeTimer;
+};
+
+template <>
+struct FlushTracker<MockTestImageCtx> {
+ FlushTracker(MockTestImageCtx*) {
+ }
+
+ void shut_down() {
+ }
+
+ void flush(Context*) {
+ }
+
+ void start_io(uint64_t) {
+ }
+
+ void finish_io(uint64_t) {
+ }
+
+};
+
+} // namespace io
+} // namespace librbd
+
+#include "librbd/io/SimpleSchedulerObjectDispatch.cc"
+
+namespace librbd {
+namespace io {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+
+struct TestMockIoSimpleSchedulerObjectDispatch : public TestMockFixture {
+ typedef SimpleSchedulerObjectDispatch<librbd::MockTestImageCtx> MockSimpleSchedulerObjectDispatch;
+
+ MockSafeTimer m_mock_timer;
+ ceph::mutex m_mock_timer_lock =
+ ceph::make_mutex("TestMockIoSimpleSchedulerObjectDispatch::Mutex");
+
+ TestMockIoSimpleSchedulerObjectDispatch() {
+ MockTestImageCtx::set_timer_instance(&m_mock_timer, &m_mock_timer_lock);
+ EXPECT_EQ(0, _rados.conf_set("rbd_io_scheduler_simple_max_delay", "1"));
+ }
+
+ void expect_get_object_name(MockTestImageCtx &mock_image_ctx,
+ uint64_t object_no) {
+ EXPECT_CALL(mock_image_ctx, get_object_name(object_no))
+ .WillRepeatedly(Return(
+ mock_image_ctx.image_ctx->get_object_name(object_no)));
+ }
+
+ void expect_dispatch_delayed_requests(MockTestImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_))
+ .WillOnce(Invoke([&mock_image_ctx, r](ObjectDispatchSpec* spec) {
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ &spec->dispatcher_ctx, r);
+ }));
+ }
+
+ void expect_cancel_timer_task(Context *timer_task) {
+ EXPECT_CALL(m_mock_timer, cancel_event(timer_task))
+ .WillOnce(Invoke([](Context *timer_task) {
+ delete timer_task;
+ return true;
+ }));
+ }
+
+ void expect_add_timer_task(Context **timer_task) {
+ EXPECT_CALL(m_mock_timer, add_event_at(_, _))
+ .WillOnce(Invoke([timer_task](ceph::real_clock::time_point, Context *task) {
+ *timer_task = task;
+ return task;
+ }));
+ }
+
+ void expect_schedule_dispatch_delayed_requests(Context *current_task,
+ Context **new_task) {
+ if (current_task != nullptr) {
+ expect_cancel_timer_task(current_task);
+ }
+ if (new_task != nullptr) {
+ expect_add_timer_task(new_task);
+ }
+ }
+
+ void run_timer_task(Context *timer_task) {
+ std::lock_guard timer_locker{m_mock_timer_lock};
+ timer_task->complete(0);
+ }
+};
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Read) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ C_SaferCond cond;
+ Context *on_finish = &cond;
+ io::ReadExtents extents = {{0, 4096}, {8192, 4096}};
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.read(
+ 0, &extents, mock_image_ctx.get_data_io_context(), 0, 0, {}, nullptr,
+ nullptr, nullptr, &on_finish, nullptr));
+ ASSERT_EQ(on_finish, &cond); // not modified
+ on_finish->complete(0);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Discard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ C_SaferCond cond;
+ Context *on_finish = &cond;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.discard(
+ 0, 0, 4096, mock_image_ctx.get_data_io_context(), 0, {}, nullptr, nullptr,
+ nullptr, &on_finish, nullptr));
+ ASSERT_NE(on_finish, &cond);
+ on_finish->complete(0);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ ceph::bufferlist data;
+ data.append("X");
+ int object_dispatch_flags = 0;
+ C_SaferCond cond;
+ Context *on_finish = &cond;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr,
+ &on_finish, nullptr));
+ ASSERT_NE(on_finish, &cond);
+ on_finish->complete(0);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteSame) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ io::LightweightBufferExtents buffer_extents;
+ ceph::bufferlist data;
+ C_SaferCond cond;
+ Context *on_finish = &cond;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write_same(
+ 0, 0, 4096, std::move(buffer_extents), std::move(data),
+ mock_image_ctx.get_data_io_context(), 0, {}, nullptr, nullptr, nullptr,
+ &on_finish, nullptr));
+ ASSERT_NE(on_finish, &cond);
+ on_finish->complete(0);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, CompareAndWrite) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ ceph::bufferlist cmp_data;
+ ceph::bufferlist write_data;
+ C_SaferCond cond;
+ Context *on_finish = &cond;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.compare_and_write(
+ 0, 0, std::move(cmp_data), std::move(write_data),
+ mock_image_ctx.get_data_io_context(), 0, {}, nullptr, nullptr, nullptr,
+ nullptr, &on_finish, nullptr));
+ ASSERT_NE(on_finish, &cond);
+ on_finish->complete(0);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Flush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond;
+ Context *on_finish = &cond;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.flush(
+ FLUSH_SOURCE_USER, {}, nullptr, &dispatch_result, &on_finish, nullptr));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_EQ(on_finish, &cond); // not modified
+ on_finish->complete(0);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteDelayed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ expect_get_object_name(mock_image_ctx, 0);
+
+ InSequence seq;
+
+ ceph::bufferlist data;
+ data.append("X");
+ int object_dispatch_flags = 0;
+ C_SaferCond cond1;
+ Context *on_finish1 = &cond1;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1,
+ nullptr));
+ ASSERT_NE(on_finish1, &cond1);
+
+ Context *timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond2;
+ Context *on_finish2 = &cond2;
+ C_SaferCond on_dispatched;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish2, &on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish2, &cond2);
+ ASSERT_NE(timer_task, nullptr);
+
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+
+ on_finish1->complete(0);
+ ASSERT_EQ(0, cond1.wait());
+ ASSERT_EQ(0, on_dispatched.wait());
+ on_finish2->complete(0);
+ ASSERT_EQ(0, cond2.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteDelayedFlush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ expect_get_object_name(mock_image_ctx, 0);
+
+ InSequence seq;
+
+ ceph::bufferlist data;
+ data.append("X");
+ int object_dispatch_flags = 0;
+ C_SaferCond cond1;
+ Context *on_finish1 = &cond1;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1,
+ nullptr));
+ ASSERT_NE(on_finish1, &cond1);
+
+ Context *timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond2;
+ Context *on_finish2 = &cond2;
+ C_SaferCond on_dispatched;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish2, &on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish2, &cond2);
+ ASSERT_NE(timer_task, nullptr);
+
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+
+ C_SaferCond cond3;
+ Context *on_finish3 = &cond3;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.flush(
+ FLUSH_SOURCE_USER, {}, nullptr, &dispatch_result, &on_finish3, nullptr));
+ ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result);
+ ASSERT_EQ(on_finish3, &cond3);
+
+ on_finish1->complete(0);
+ ASSERT_EQ(0, cond1.wait());
+ ASSERT_EQ(0, on_dispatched.wait());
+ on_finish2->complete(0);
+ ASSERT_EQ(0, cond2.wait());
+ on_finish3->complete(0);
+ ASSERT_EQ(0, cond3.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteMerged) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ expect_get_object_name(mock_image_ctx, 0);
+
+ InSequence seq;
+
+ ceph::bufferlist data;
+ data.append("X");
+ int object_dispatch_flags = 0;
+ C_SaferCond cond1;
+ Context *on_finish1 = &cond1;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1,
+ nullptr));
+ ASSERT_NE(on_finish1, &cond1);
+
+ Context *timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+
+ uint64_t object_off = 20;
+ data.clear();
+ data.append(std::string(10, 'A'));
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond2;
+ Context *on_finish2 = &cond2;
+ C_SaferCond on_dispatched2;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish2, &on_dispatched2));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish2, &cond2);
+ ASSERT_NE(timer_task, nullptr);
+
+ object_off = 0;
+ data.clear();
+ data.append(std::string(10, 'B'));
+ C_SaferCond cond3;
+ Context *on_finish3 = &cond3;
+ C_SaferCond on_dispatched3;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish3, &on_dispatched3));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish3, &cond3);
+
+ object_off = 10;
+ data.clear();
+ data.append(std::string(10, 'C'));
+ C_SaferCond cond4;
+ Context *on_finish4 = &cond4;
+ C_SaferCond on_dispatched4;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {},&object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish4, &on_dispatched4));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish4, &cond4);
+
+ object_off = 30;
+ data.clear();
+ data.append(std::string(10, 'D'));
+ C_SaferCond cond5;
+ Context *on_finish5 = &cond5;
+ C_SaferCond on_dispatched5;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish5, &on_dispatched5));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish5, &cond5);
+
+ object_off = 50;
+ data.clear();
+ data.append(std::string(10, 'E'));
+ C_SaferCond cond6;
+ Context *on_finish6 = &cond6;
+ C_SaferCond on_dispatched6;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish6, &on_dispatched6));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish6, &cond6);
+
+ // expect two requests dispatched:
+ // 0~40 (merged 0~10, 10~10, 20~10, 30~10) and 50~10
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+
+ on_finish1->complete(0);
+ ASSERT_EQ(0, cond1.wait());
+ ASSERT_EQ(0, on_dispatched2.wait());
+ ASSERT_EQ(0, on_dispatched3.wait());
+ ASSERT_EQ(0, on_dispatched4.wait());
+ ASSERT_EQ(0, on_dispatched5.wait());
+ ASSERT_EQ(0, on_dispatched6.wait());
+ on_finish2->complete(0);
+ on_finish3->complete(0);
+ on_finish4->complete(0);
+ on_finish5->complete(0);
+ on_finish6->complete(0);
+ ASSERT_EQ(0, cond2.wait());
+ ASSERT_EQ(0, cond3.wait());
+ ASSERT_EQ(0, cond4.wait());
+ ASSERT_EQ(0, cond5.wait());
+ ASSERT_EQ(0, cond6.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteNonSequential) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ expect_get_object_name(mock_image_ctx, 0);
+
+ InSequence seq;
+
+ ceph::bufferlist data;
+ int object_dispatch_flags = 0;
+ C_SaferCond cond1;
+ Context *on_finish1 = &cond1;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1,
+ nullptr));
+ ASSERT_NE(on_finish1, &cond1);
+
+ Context *timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+
+ uint64_t object_off = 0;
+ data.clear();
+ data.append(std::string(10, 'X'));
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond2;
+ Context *on_finish2 = &cond2;
+ C_SaferCond on_dispatched2;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish2, &on_dispatched2));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish2, &cond2);
+ ASSERT_NE(timer_task, nullptr);
+
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+
+ object_off = 5;
+ data.clear();
+ data.append(std::string(10, 'Y'));
+ C_SaferCond cond3;
+ Context *on_finish3 = &cond3;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish3, nullptr));
+ ASSERT_NE(on_finish3, &cond3);
+
+ on_finish1->complete(0);
+ ASSERT_EQ(0, cond1.wait());
+ ASSERT_EQ(0, on_dispatched2.wait());
+ on_finish2->complete(0);
+ ASSERT_EQ(0, cond2.wait());
+ on_finish3->complete(0);
+ ASSERT_EQ(0, cond3.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Mixed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ expect_get_object_name(mock_image_ctx, 0);
+
+ InSequence seq;
+
+ // write (1) 0~0 (in-flight)
+ // will wrap on_finish with dispatch_seq=1 to dispatch future delayed writes
+ ceph::bufferlist data;
+ int object_dispatch_flags = 0;
+ C_SaferCond cond1;
+ Context *on_finish1 = &cond1;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1,
+ nullptr));
+ ASSERT_NE(on_finish1, &cond1);
+
+ // write (2) 0~10 (delayed)
+ // will wait for write (1) to finish or a non-seq io comes
+ Context *timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+ uint64_t object_off = 0;
+ data.clear();
+ data.append(std::string(10, 'A'));
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond2;
+ Context *on_finish2 = &cond2;
+ C_SaferCond on_dispatched2;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish2, &on_dispatched2));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish2, &cond2);
+ ASSERT_NE(timer_task, nullptr);
+
+ // write (3) 10~10 (delayed)
+ // will be merged with write (2)
+ object_off = 10;
+ data.clear();
+ data.append(std::string(10, 'B'));
+ C_SaferCond cond3;
+ Context *on_finish3 = &cond3;
+ C_SaferCond on_dispatched3;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish3, &on_dispatched3));
+ ASSERT_NE(on_finish3, &cond3);
+
+ // discard (1) (non-seq io)
+ // will dispatch the delayed writes (2) and (3) and wrap on_finish
+ // with dispatch_seq=2 to dispatch future delayed writes
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+ C_SaferCond cond4;
+ Context *on_finish4 = &cond4;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.discard(
+ 0, 4096, 4096, mock_image_ctx.get_data_io_context(), 0, {}, nullptr,
+ nullptr, nullptr, &on_finish4, nullptr));
+ ASSERT_NE(on_finish4, &cond4);
+ ASSERT_EQ(0, on_dispatched2.wait());
+ ASSERT_EQ(0, on_dispatched3.wait());
+
+ // write (4) 20~10 (delayed)
+ // will wait for discard (1) to finish or a non-seq io comes
+ timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+ object_off = 20;
+ data.clear();
+ data.append(std::string(10, 'C'));
+ C_SaferCond cond5;
+ Context *on_finish5 = &cond5;
+ C_SaferCond on_dispatched5;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish5, &on_dispatched5));
+ ASSERT_NE(on_finish5, &cond5);
+ ASSERT_NE(timer_task, nullptr);
+
+ // discard (2) (non-seq io)
+ // will dispatch the delayed write (4) and wrap on_finish with dispatch_seq=3
+ // to dispatch future delayed writes
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+ C_SaferCond cond6;
+ Context *on_finish6 = &cond6;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.discard(
+ 0, 4096, 4096, mock_image_ctx.get_data_io_context(), 0, {}, nullptr,
+ nullptr, nullptr, &on_finish6, nullptr));
+ ASSERT_NE(on_finish6, &cond6);
+ ASSERT_EQ(0, on_dispatched5.wait());
+
+ // write (5) 30~10 (delayed)
+ // will wait for discard (2) to finish or a non-seq io comes
+ timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+ object_off = 30;
+ data.clear();
+ data.append(std::string(10, 'D'));
+ C_SaferCond cond7;
+ Context *on_finish7 = &cond7;
+ C_SaferCond on_dispatched7;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, object_off, std::move(data), mock_image_ctx.get_data_io_context(), 0,
+ 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish7, &on_dispatched7));
+ ASSERT_NE(on_finish7, &cond7);
+ ASSERT_NE(timer_task, nullptr);
+
+ // write (1) finishes
+ // on_finish wrapper will skip dispatch delayed write (5)
+ // due to dispatch_seq(1) < m_dispatch_seq(3)
+ on_finish1->complete(0);
+ ASSERT_EQ(0, cond1.wait());
+
+ // writes (2) and (3) finish ("dispatch delayed" is not called)
+ on_finish2->complete(0);
+ on_finish3->complete(0);
+ ASSERT_EQ(0, cond2.wait());
+ ASSERT_EQ(0, cond3.wait());
+
+ // discard (1) finishes
+ // on_finish wrapper will skip dispatch delayed write (5)
+ // due to dispatch_seq(2) < m_dispatch_seq(3)
+ on_finish4->complete(0);
+ ASSERT_EQ(0, cond4.wait());
+
+ // writes (4) finishes ("dispatch delayed" is not called)
+ on_finish5->complete(0);
+ ASSERT_EQ(0, cond5.wait());
+
+ // discard (2) finishes
+ // on_finish wrapper will dispatch the delayed write (5)
+ // due to dispatch_seq(3) == m_dispatch_seq(3)
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+ on_finish6->complete(0);
+ ASSERT_EQ(0, cond6.wait());
+ ASSERT_EQ(0, on_dispatched7.wait());
+
+ // write (5) finishes ("dispatch delayed" is not called)
+ on_finish7->complete(0);
+ ASSERT_EQ(0, cond7.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, DispatchQueue) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ expect_get_object_name(mock_image_ctx, 0);
+ expect_get_object_name(mock_image_ctx, 1);
+
+ InSequence seq;
+
+ // send 2 writes to object 0
+
+ uint64_t object_no = 0;
+ ceph::bufferlist data;
+ int object_dispatch_flags = 0;
+ C_SaferCond cond1;
+ Context *on_finish1 = &cond1;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ object_no, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1,
+ nullptr));
+ ASSERT_NE(on_finish1, &cond1);
+
+ Context *timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+
+ data.clear();
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond2;
+ Context *on_finish2 = &cond2;
+ C_SaferCond on_dispatched2;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ object_no, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish2, &on_dispatched2));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish2, &cond2);
+ ASSERT_NE(timer_task, nullptr);
+
+ // send 2 writes to object 1
+
+ object_no = 1;
+ data.clear();
+ C_SaferCond cond3;
+ Context *on_finish3 = &cond3;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ object_no, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish3,
+ nullptr));
+ ASSERT_NE(on_finish3, &cond3);
+
+ data.clear();
+ C_SaferCond cond4;
+ Context *on_finish4 = &cond4;
+ C_SaferCond on_dispatched4;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ object_no, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish4, &on_dispatched4));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish4, &cond4);
+
+ // finish write (1) to object 0
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, &timer_task);
+ on_finish1->complete(0);
+ ASSERT_EQ(0, cond1.wait());
+ ASSERT_EQ(0, on_dispatched2.wait());
+
+ // finish write (2) to object 0
+ on_finish2->complete(0);
+ ASSERT_EQ(0, cond2.wait());
+
+ // finish write (1) to object 1
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+ expect_schedule_dispatch_delayed_requests(timer_task, nullptr);
+ on_finish3->complete(0);
+ ASSERT_EQ(0, cond3.wait());
+ ASSERT_EQ(0, on_dispatched4.wait());
+
+ // finish write (2) to object 1
+ on_finish4->complete(0);
+ ASSERT_EQ(0, cond4.wait());
+}
+
+TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Timer) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSimpleSchedulerObjectDispatch
+ mock_simple_scheduler_object_dispatch(&mock_image_ctx);
+
+ expect_get_object_name(mock_image_ctx, 0);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ ceph::bufferlist data;
+ int object_dispatch_flags = 0;
+ C_SaferCond cond1;
+ Context *on_finish1 = &cond1;
+ ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1,
+ nullptr));
+ ASSERT_NE(on_finish1, &cond1);
+
+ Context *timer_task = nullptr;
+ expect_schedule_dispatch_delayed_requests(nullptr, &timer_task);
+
+ data.clear();
+ io::DispatchResult dispatch_result;
+ C_SaferCond cond2;
+ Context *on_finish2 = &cond2;
+ C_SaferCond on_dispatched;
+ ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write(
+ 0, 0, std::move(data), mock_image_ctx.get_data_io_context(), 0, 0,
+ std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result,
+ &on_finish2, &on_dispatched));
+ ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE);
+ ASSERT_NE(on_finish2, &cond2);
+ ASSERT_NE(timer_task, nullptr);
+
+ expect_dispatch_delayed_requests(mock_image_ctx, 0);
+
+ run_timer_task(timer_task);
+ ASSERT_EQ(0, on_dispatched.wait());
+
+ on_finish1->complete(0);
+ ASSERT_EQ(0, cond1.wait());
+ on_finish2->complete(0);
+ ASSERT_EQ(0, cond2.wait());
+}
+
+} // namespace io
+} // namespace librbd
diff --git a/src/test/librbd/journal/test_Entries.cc b/src/test/librbd/journal/test_Entries.cc
new file mode 100644
index 000000000..c392fb9f8
--- /dev/null
+++ b/src/test/librbd/journal/test_Entries.cc
@@ -0,0 +1,228 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/internal.h"
+#include "librbd/Journal.h"
+#include "librbd/api/Io.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/journal/Types.h"
+#include "journal/Journaler.h"
+#include "journal/ReplayEntry.h"
+#include "journal/ReplayHandler.h"
+#include "journal/Settings.h"
+#include <list>
+#include <boost/variant.hpp>
+
+void register_test_journal_entries() {
+}
+
+namespace librbd {
+namespace journal {
+
+class TestJournalEntries : public TestFixture {
+public:
+ typedef std::list<::journal::Journaler *> Journalers;
+
+ struct ReplayHandler : public ::journal::ReplayHandler {
+ ceph::mutex lock = ceph::make_mutex("ReplayHandler::lock");
+ ceph::condition_variable cond;
+ bool entries_available;
+ bool complete;
+
+ ReplayHandler()
+ : entries_available(false), complete(false) {
+ }
+
+ void handle_entries_available() override {
+ std::lock_guard locker{lock};
+ entries_available = true;
+ cond.notify_all();
+ }
+
+ void handle_complete(int r) override {
+ std::lock_guard locker{lock};
+ complete = true;
+ cond.notify_all();
+ }
+ };
+
+ ReplayHandler m_replay_handler;
+ Journalers m_journalers;
+
+ void TearDown() override {
+ for (Journalers::iterator it = m_journalers.begin();
+ it != m_journalers.end(); ++it) {
+ ::journal::Journaler *journaler = *it;
+ journaler->stop_replay();
+ journaler->shut_down();
+ delete journaler;
+ }
+
+ TestFixture::TearDown();
+ }
+
+ ::journal::Journaler *create_journaler(librbd::ImageCtx *ictx) {
+ ::journal::Journaler *journaler = new ::journal::Journaler(
+ ictx->md_ctx, ictx->id, "dummy client", {}, nullptr);
+
+ int r = journaler->register_client(bufferlist());
+ if (r < 0) {
+ ADD_FAILURE() << "failed to register journal client";
+ delete journaler;
+ return NULL;
+ }
+
+ C_SaferCond cond;
+ journaler->init(&cond);
+ r = cond.wait();
+ if (r < 0) {
+ ADD_FAILURE() << "failed to initialize journal client";
+ delete journaler;
+ return NULL;
+ }
+
+ journaler->start_live_replay(&m_replay_handler, 0.1);
+ m_journalers.push_back(journaler);
+ return journaler;
+ }
+
+ bool wait_for_entries_available(librbd::ImageCtx *ictx) {
+ using namespace std::chrono_literals;
+ std::unique_lock locker{m_replay_handler.lock};
+ while (!m_replay_handler.entries_available) {
+ if (m_replay_handler.cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ m_replay_handler.entries_available = false;
+ return true;
+ }
+
+ bool get_event_entry(const ::journal::ReplayEntry &replay_entry,
+ librbd::journal::EventEntry *event_entry) {
+ try {
+ bufferlist data_bl = replay_entry.get_data();
+ auto it = data_bl.cbegin();
+ decode(*event_entry, it);
+ } catch (const buffer::error &err) {
+ return false;
+ }
+ return true;
+ }
+
+};
+
+TEST_F(TestJournalEntries, AioWrite) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ::journal::Journaler *journaler = create_journaler(ictx);
+ ASSERT_TRUE(journaler != NULL);
+
+ std::string buffer(512, '1');
+ bufferlist write_bl;
+ write_bl.append(buffer);
+
+ C_SaferCond cond_ctx;
+ auto c = librbd::io::AioCompletion::create(&cond_ctx);
+ c->get();
+ api::Io<>::aio_write(*ictx, c, 123, buffer.size(), std::move(write_bl), 0,
+ true);
+ ASSERT_EQ(0, c->wait_for_complete());
+ c->put();
+
+ ASSERT_TRUE(wait_for_entries_available(ictx));
+
+ ::journal::ReplayEntry replay_entry;
+ ASSERT_TRUE(journaler->try_pop_front(&replay_entry));
+
+ librbd::journal::EventEntry event_entry;
+ ASSERT_TRUE(get_event_entry(replay_entry, &event_entry));
+
+ ASSERT_EQ(librbd::journal::EVENT_TYPE_AIO_WRITE,
+ event_entry.get_event_type());
+
+ librbd::journal::AioWriteEvent aio_write_event =
+ boost::get<librbd::journal::AioWriteEvent>(event_entry.event);
+ ASSERT_EQ(123U, aio_write_event.offset);
+ ASSERT_EQ(buffer.size(), aio_write_event.length);
+
+ bufferlist buffer_bl;
+ buffer_bl.append(buffer);
+ ASSERT_TRUE(aio_write_event.data.contents_equal(buffer_bl));
+
+ ASSERT_EQ(librbd::journal::AioWriteEvent::get_fixed_size() +
+ aio_write_event.data.length(), replay_entry.get_data().length());
+}
+
+TEST_F(TestJournalEntries, AioDiscard) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ CephContext* cct = reinterpret_cast<CephContext*>(_rados.cct());
+ REQUIRE(!cct->_conf.get_val<bool>("rbd_skip_partial_discard"));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ::journal::Journaler *journaler = create_journaler(ictx);
+ ASSERT_TRUE(journaler != NULL);
+
+ C_SaferCond cond_ctx;
+ auto c = librbd::io::AioCompletion::create(&cond_ctx);
+ c->get();
+ api::Io<>::aio_discard(*ictx, c, 123, 234, ictx->discard_granularity_bytes,
+ true);
+ ASSERT_EQ(0, c->wait_for_complete());
+ c->put();
+
+ ASSERT_TRUE(wait_for_entries_available(ictx));
+
+ ::journal::ReplayEntry replay_entry;
+ ASSERT_TRUE(journaler->try_pop_front(&replay_entry));
+
+ librbd::journal::EventEntry event_entry;
+ ASSERT_TRUE(get_event_entry(replay_entry, &event_entry));
+
+ ASSERT_EQ(librbd::journal::EVENT_TYPE_AIO_DISCARD,
+ event_entry.get_event_type());
+
+ librbd::journal::AioDiscardEvent aio_discard_event =
+ boost::get<librbd::journal::AioDiscardEvent>(event_entry.event);
+ ASSERT_EQ(123U, aio_discard_event.offset);
+ ASSERT_EQ(234U, aio_discard_event.length);
+}
+
+TEST_F(TestJournalEntries, AioFlush) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ::journal::Journaler *journaler = create_journaler(ictx);
+ ASSERT_TRUE(journaler != NULL);
+
+ C_SaferCond cond_ctx;
+ auto c = librbd::io::AioCompletion::create(&cond_ctx);
+ c->get();
+ api::Io<>::aio_flush(*ictx, c, true);
+ ASSERT_EQ(0, c->wait_for_complete());
+ c->put();
+
+ ASSERT_TRUE(wait_for_entries_available(ictx));
+
+ ::journal::ReplayEntry replay_entry;
+ ASSERT_TRUE(journaler->try_pop_front(&replay_entry));
+
+ librbd::journal::EventEntry event_entry;
+ ASSERT_TRUE(get_event_entry(replay_entry, &event_entry));
+
+ ASSERT_EQ(librbd::journal::EVENT_TYPE_AIO_FLUSH,
+ event_entry.get_event_type());
+}
+
+} // namespace journal
+} // namespace librbd
diff --git a/src/test/librbd/journal/test_Replay.cc b/src/test/librbd/journal/test_Replay.cc
new file mode 100644
index 000000000..9b4580e64
--- /dev/null
+++ b/src/test/librbd/journal/test_Replay.cc
@@ -0,0 +1,886 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "cls/journal/cls_journal_types.h"
+#include "cls/journal/cls_journal_client.h"
+#include "journal/Journaler.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/internal.h"
+#include "librbd/Journal.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Io.h"
+#include "librbd/api/Snapshot.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/io/ReadResult.h"
+#include "librbd/journal/Types.h"
+
+void register_test_journal_replay() {
+}
+
+namespace librbd {
+namespace journal {
+
+class TestJournalReplay : public TestFixture {
+public:
+
+ int when_acquired_lock(librbd::ImageCtx *ictx) {
+ C_SaferCond lock_ctx;
+ {
+ std::unique_lock owner_locker{ictx->owner_lock};
+ ictx->exclusive_lock->acquire_lock(&lock_ctx);
+ }
+ int r = lock_ctx.wait();
+ if (r < 0) {
+ return r;
+ }
+
+ C_SaferCond refresh_ctx;
+ ictx->state->refresh(&refresh_ctx);
+ return refresh_ctx.wait();
+ }
+
+ template<typename T>
+ void inject_into_journal(librbd::ImageCtx *ictx, T event) {
+ C_SaferCond ctx;
+ librbd::journal::EventEntry event_entry(event);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ uint64_t tid = ictx->journal->append_io_event(std::move(event_entry),0, 0,
+ true, 0);
+ ictx->journal->wait_event(tid, &ctx);
+ }
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ void get_journal_commit_position(librbd::ImageCtx *ictx, int64_t *tag,
+ int64_t *entry)
+ {
+ const std::string client_id = "";
+ std::string journal_id = ictx->id;
+
+ C_SaferCond close_cond;
+ ictx->journal->close(&close_cond);
+ ASSERT_EQ(0, close_cond.wait());
+
+ ictx->journal->put();
+ ictx->journal = nullptr;
+
+ C_SaferCond cond;
+ uint64_t minimum_set;
+ uint64_t active_set;
+ std::set<cls::journal::Client> registered_clients;
+ std::string oid = ::journal::Journaler::header_oid(journal_id);
+ cls::journal::client::get_mutable_metadata(ictx->md_ctx, oid, &minimum_set,
+ &active_set, &registered_clients, &cond);
+ ASSERT_EQ(0, cond.wait());
+ std::set<cls::journal::Client>::const_iterator c;
+ for (c = registered_clients.begin(); c != registered_clients.end(); ++c) {
+ if (c->id == client_id) {
+ break;
+ }
+ }
+ if (c == registered_clients.end() ||
+ c->commit_position.object_positions.empty()) {
+ *tag = 0;
+ *entry = -1;
+ } else {
+ const cls::journal::ObjectPosition &object_position =
+ *c->commit_position.object_positions.begin();
+ *tag = object_position.tag_tid;
+ *entry = object_position.entry_tid;
+ }
+
+ C_SaferCond open_cond;
+ ictx->journal = new librbd::Journal<>(*ictx);
+ ictx->journal->open(&open_cond);
+ ASSERT_EQ(0, open_cond.wait());
+ }
+};
+
+TEST_F(TestJournalReplay, AioDiscardEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ // write to the image w/o using the journal
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->features &= ~RBD_FEATURE_JOURNALING;
+
+ std::string payload(4096, '1');
+ bufferlist payload_bl;
+ payload_bl.append(payload);
+ auto aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_write(*ictx, aio_comp, 0, payload.size(),
+ std::move(payload_bl), 0, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_flush(*ictx, aio_comp, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+
+ std::string read_payload(4096, '\0');
+ librbd::io::ReadResult read_result{&read_payload[0], read_payload.size()};
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_read(*ictx, aio_comp, 0, read_payload.size(),
+ librbd::io::ReadResult{read_result}, 0, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+ ASSERT_EQ(payload, read_payload);
+ close_image(ictx);
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject a discard operation into the journal
+ inject_into_journal(ictx,
+ librbd::journal::AioDiscardEvent(
+ 0, payload.size(), ictx->discard_granularity_bytes));
+ close_image(ictx);
+
+ // re-open the journal so that it replays the new entry
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_read(*ictx, aio_comp, 0, read_payload.size(),
+ librbd::io::ReadResult{read_result}, 0, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+ if (ictx->discard_granularity_bytes > 0) {
+ ASSERT_EQ(payload, read_payload);
+ } else {
+ ASSERT_EQ(std::string(read_payload.size(), '\0'), read_payload);
+ }
+
+ // check the commit position is properly updated
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(0, current_entry);
+
+ // replay several envents and check the commit position
+ inject_into_journal(ictx,
+ librbd::journal::AioDiscardEvent(
+ 0, payload.size(), ictx->discard_granularity_bytes));
+ inject_into_journal(ictx,
+ librbd::journal::AioDiscardEvent(
+ 0, payload.size(), ictx->discard_granularity_bytes));
+ close_image(ictx);
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 2, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ // verify lock ordering constraints
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_discard(*ictx, aio_comp, 0, read_payload.size(),
+ ictx->discard_granularity_bytes, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+}
+
+TEST_F(TestJournalReplay, AioWriteEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject a write operation into the journal
+ std::string payload(4096, '1');
+ bufferlist payload_bl;
+ payload_bl.append(payload);
+ inject_into_journal(ictx,
+ librbd::journal::AioWriteEvent(0, payload.size(), payload_bl));
+ close_image(ictx);
+
+ // re-open the journal so that it replays the new entry
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ std::string read_payload(4096, '\0');
+ librbd::io::ReadResult read_result{&read_payload[0], read_payload.size()};
+ auto aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_read(*ictx, aio_comp, 0, read_payload.size(),
+ std::move(read_result), 0, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+ ASSERT_EQ(payload, read_payload);
+
+ // check the commit position is properly updated
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(0, current_entry);
+
+ // replay several events and check the commit position
+ inject_into_journal(ictx,
+ librbd::journal::AioWriteEvent(0, payload.size(), payload_bl));
+ inject_into_journal(ictx,
+ librbd::journal::AioWriteEvent(0, payload.size(), payload_bl));
+ close_image(ictx);
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 2, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ // verify lock ordering constraints
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_write(*ictx, aio_comp, 0, payload.size(),
+ bufferlist{payload_bl}, 0, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+}
+
+TEST_F(TestJournalReplay, AioFlushEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject a flush operation into the journal
+ inject_into_journal(ictx, librbd::journal::AioFlushEvent());
+ close_image(ictx);
+
+ // re-open the journal so that it replays the new entry
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // check the commit position is properly updated
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(0, current_entry);
+
+ // replay several events and check the commit position
+ inject_into_journal(ictx, librbd::journal::AioFlushEvent());
+ inject_into_journal(ictx, librbd::journal::AioFlushEvent());
+ close_image(ictx);
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 2, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ // verify lock ordering constraints
+ auto aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_flush(*ictx, aio_comp, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+}
+
+TEST_F(TestJournalReplay, SnapCreate) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx, librbd::journal::SnapCreateEvent(1, cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ ASSERT_NE(CEPH_NOSNAP, ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+ }
+
+ // verify lock ordering constraints
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap2", 0, no_op_progress));
+}
+
+TEST_F(TestJournalReplay, SnapProtect) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, no_op_progress));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx,
+ librbd::journal::SnapProtectEvent(1,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag, current_tag);
+ ASSERT_EQ(initial_entry + 2, current_entry);
+
+ bool is_protected;
+ ASSERT_EQ(0, librbd::api::Snapshot<>::is_protected(ictx, "snap", &is_protected));
+ ASSERT_TRUE(is_protected);
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap2", 0, no_op_progress));
+ ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap2"));
+}
+
+TEST_F(TestJournalReplay, SnapUnprotect) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, no_op_progress));
+ uint64_t snap_id;
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), "snap");
+ ASSERT_NE(CEPH_NOSNAP, snap_id);
+ }
+ ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx,
+ librbd::journal::SnapUnprotectEvent(1,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag, current_tag);
+ ASSERT_EQ(initial_entry + 2, current_entry);
+
+ bool is_protected;
+ ASSERT_EQ(0, librbd::api::Snapshot<>::is_protected(ictx, "snap", &is_protected));
+ ASSERT_FALSE(is_protected);
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap2", 0, no_op_progress));
+ ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap2"));
+ ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(),
+ "snap2"));
+}
+
+TEST_F(TestJournalReplay, SnapRename) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, no_op_progress));
+ uint64_t snap_id;
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), "snap");
+ ASSERT_NE(CEPH_NOSNAP, snap_id);
+ }
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx, librbd::journal::SnapRenameEvent(1, snap_id, "snap",
+ "snap2"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag, current_tag);
+ ASSERT_EQ(initial_entry + 2, current_entry);
+ ASSERT_EQ(0, ictx->state->refresh());
+
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), "snap2");
+ ASSERT_NE(CEPH_NOSNAP, snap_id);
+ }
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->snap_rename("snap2", "snap3"));
+}
+
+TEST_F(TestJournalReplay, SnapRollback) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, no_op_progress));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx,
+ librbd::journal::SnapRollbackEvent(1,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag, current_tag);
+ ASSERT_EQ(initial_entry + 2, current_entry);
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->snap_rollback(cls::rbd::UserSnapshotNamespace(),
+ "snap",
+ no_op_progress));
+}
+
+TEST_F(TestJournalReplay, SnapRemove) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, no_op_progress));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx,
+ librbd::journal::SnapRemoveEvent(1,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag, current_tag);
+ ASSERT_EQ(initial_entry + 2, current_entry);
+
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ uint64_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(),
+ "snap");
+ ASSERT_EQ(CEPH_NOSNAP, snap_id);
+ }
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, no_op_progress));
+ ASSERT_EQ(0, ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+}
+
+TEST_F(TestJournalReplay, Rename) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ std::string new_image_name(get_temp_image_name());
+ inject_into_journal(ictx, librbd::journal::RenameEvent(1, new_image_name));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ // verify lock ordering constraints
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.rename(m_ioctx, new_image_name.c_str(), m_image_name.c_str()));
+}
+
+TEST_F(TestJournalReplay, Resize) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx, librbd::journal::ResizeEvent(1, 16));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ // verify lock ordering constraints
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->resize(0, true, no_op_progress));
+}
+
+TEST_F(TestJournalReplay, Flatten) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ librbd::NoOpProgressContext no_op_progress;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, no_op_progress));
+ ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+
+ std::string clone_name = get_temp_image_name();
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
+ clone_name.c_str(), ictx->features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx2;
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+ ASSERT_EQ(0, when_acquired_lock(ictx2));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx2, &initial_tag, &initial_entry);
+
+ // inject snapshot ops into journal
+ inject_into_journal(ictx2, librbd::journal::FlattenEvent(1));
+ inject_into_journal(ictx2, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx2);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+ ASSERT_EQ(0, when_acquired_lock(ictx2));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx2, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(1, current_entry);
+ ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(),
+ "snap"));
+
+ // verify lock ordering constraints
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(-EINVAL, ictx2->operations->flatten(no_op));
+}
+
+TEST_F(TestJournalReplay, UpdateFeatures) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ uint64_t features = RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF;
+ bool enabled = !ictx->test_features(features);
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject update_features op into journal
+ inject_into_journal(ictx, librbd::journal::UpdateFeaturesEvent(1, features,
+ enabled));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(0, current_entry);
+
+ ASSERT_EQ(enabled, ictx->test_features(features));
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->update_features(features, !enabled));
+}
+
+TEST_F(TestJournalReplay, MetadataSet) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject metadata_set op into journal
+ inject_into_journal(ictx, librbd::journal::MetadataSetEvent(
+ 1, "conf_rbd_mirroring_replay_delay", "9876"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ ASSERT_EQ(9876U, ictx->mirroring_replay_delay);
+
+ std::string value;
+ ASSERT_EQ(0, librbd::metadata_get(ictx, "conf_rbd_mirroring_replay_delay",
+ &value));
+ ASSERT_EQ("9876", value);
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->metadata_set("key2", "value"));
+}
+
+TEST_F(TestJournalReplay, MetadataRemove) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ ASSERT_EQ(0, ictx->operations->metadata_set(
+ "conf_rbd_mirroring_replay_delay", "9876"));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ // inject metadata_remove op into journal
+ inject_into_journal(ictx, librbd::journal::MetadataRemoveEvent(
+ 1, "conf_rbd_mirroring_replay_delay"));
+ inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+ close_image(ictx);
+
+ // replay journal
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag, current_tag);
+ ASSERT_EQ(initial_entry + 2, current_entry);
+ ASSERT_EQ(0U, ictx->mirroring_replay_delay);
+
+ std::string value;
+ ASSERT_EQ(-ENOENT,
+ librbd::metadata_get(ictx, "conf_rbd_mirroring_replay_delay",
+ &value));
+
+ // verify lock ordering constraints
+ ASSERT_EQ(0, ictx->operations->metadata_set("key", "value"));
+ ASSERT_EQ(0, ictx->operations->metadata_remove("key"));
+}
+
+TEST_F(TestJournalReplay, ObjectPosition) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
+
+ // get current commit position
+ int64_t initial_tag;
+ int64_t initial_entry;
+ get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+ std::string payload(4096, '1');
+ bufferlist payload_bl;
+ payload_bl.append(payload);
+ auto aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_write(*ictx, aio_comp, 0, payload.size(),
+ bufferlist{payload_bl}, 0, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_flush(*ictx, aio_comp, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+
+ // check the commit position updated
+ int64_t current_tag;
+ int64_t current_entry;
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(1, current_entry);
+
+ // write again
+
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_write(*ictx, aio_comp, 0, payload.size(),
+ bufferlist{payload_bl}, 0, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+
+ aio_comp = new librbd::io::AioCompletion();
+ api::Io<>::aio_flush(*ictx, aio_comp, true);
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ aio_comp->release();
+
+ // user flush requests are ignored when journaling + cache are enabled
+ C_SaferCond flush_ctx;
+ aio_comp = librbd::io::AioCompletion::create_and_start(
+ &flush_ctx, ictx, librbd::io::AIO_TYPE_FLUSH);
+ auto req = librbd::io::ImageDispatchSpec::create_flush(
+ *ictx, librbd::io::IMAGE_DISPATCH_LAYER_INTERNAL_START, aio_comp,
+ librbd::io::FLUSH_SOURCE_INTERNAL, {});
+ req->send();
+ ASSERT_EQ(0, flush_ctx.wait());
+
+ // check the commit position updated
+ get_journal_commit_position(ictx, &current_tag, &current_entry);
+ ASSERT_EQ(initial_tag + 1, current_tag);
+ ASSERT_EQ(3, current_entry);
+}
+
+} // namespace journal
+} // namespace librbd
diff --git a/src/test/librbd/journal/test_mock_OpenRequest.cc b/src/test/librbd/journal/test_mock_OpenRequest.cc
new file mode 100644
index 000000000..0d86f20e9
--- /dev/null
+++ b/src/test/librbd/journal/test_mock_OpenRequest.cc
@@ -0,0 +1,193 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "common/ceph_mutex.h"
+#include "cls/journal/cls_journal_types.h"
+#include "librbd/journal/OpenRequest.h"
+#include "librbd/journal/Types.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef ::journal::MockJournaler Journaler;
+};
+
+} // namespace journal
+} // namespace librbd
+
+// template definitions
+#include "librbd/journal/OpenRequest.cc"
+template class librbd::journal::OpenRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace journal {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::WithArg;
+
+class TestMockJournalOpenRequest : public TestMockFixture {
+public:
+ typedef OpenRequest<MockTestImageCtx> MockOpenRequest;
+
+ TestMockJournalOpenRequest() = default;
+
+ void expect_init_journaler(::journal::MockJournaler &mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, init(_))
+ .WillOnce(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ void expect_get_journaler_cached_client(::journal::MockJournaler &mock_journaler,
+ int r) {
+ journal::ImageClientMeta image_client_meta;
+ image_client_meta.tag_class = 345;
+
+ journal::ClientData client_data;
+ client_data.client_meta = image_client_meta;
+
+ cls::journal::Client client;
+ encode(client_data, client.data);
+
+ EXPECT_CALL(mock_journaler, get_cached_client("", _))
+ .WillOnce(DoAll(SetArgPointee<1>(client),
+ Return(r)));
+ }
+
+ void expect_get_journaler_tags(MockImageCtx &mock_image_ctx,
+ ::journal::MockJournaler &mock_journaler,
+ int r) {
+ journal::TagData tag_data;
+ tag_data.mirror_uuid = "remote mirror";
+
+ bufferlist tag_data_bl;
+ encode(tag_data, tag_data_bl);
+
+ ::journal::Journaler::Tags tags = {{0, 345, {}}, {1, 345, tag_data_bl}};
+ EXPECT_CALL(mock_journaler, get_tags(345, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(tags),
+ WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))));
+ }
+
+ ceph::mutex m_lock = ceph::make_mutex("m_lock");
+ ImageClientMeta m_client_meta;
+ uint64_t m_tag_tid = 0;
+ TagData m_tag_data;
+};
+
+TEST_F(TestMockJournalOpenRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_init_journaler(mock_journaler, 0);
+ expect_get_journaler_cached_client(mock_journaler, 0);
+ expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0);
+
+ C_SaferCond ctx;
+ auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler,
+ &m_lock, &m_client_meta, &m_tag_tid,
+ &m_tag_data, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(345U, m_client_meta.tag_class);
+ ASSERT_EQ(1U, m_tag_tid);
+ ASSERT_EQ("remote mirror", m_tag_data.mirror_uuid);
+}
+
+TEST_F(TestMockJournalOpenRequest, InitError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_init_journaler(mock_journaler, -ENOENT);
+
+ C_SaferCond ctx;
+ auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler,
+ &m_lock, &m_client_meta, &m_tag_tid,
+ &m_tag_data, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockJournalOpenRequest, GetCachedClientError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_init_journaler(mock_journaler, 0);
+ expect_get_journaler_cached_client(mock_journaler, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler,
+ &m_lock, &m_client_meta, &m_tag_tid,
+ &m_tag_data, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockJournalOpenRequest, GetTagsError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_init_journaler(mock_journaler, 0);
+ expect_get_journaler_cached_client(mock_journaler, 0);
+ expect_get_journaler_tags(mock_image_ctx, mock_journaler, -EBADMSG);
+
+ C_SaferCond ctx;
+ auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler,
+ &m_lock, &m_client_meta, &m_tag_tid,
+ &m_tag_data, &ctx);
+ req->send();
+ ASSERT_EQ(-EBADMSG, ctx.wait());
+}
+
+} // namespace journal
+} // namespace librbd
diff --git a/src/test/librbd/journal/test_mock_PromoteRequest.cc b/src/test/librbd/journal/test_mock_PromoteRequest.cc
new file mode 100644
index 000000000..c4e7ed9dc
--- /dev/null
+++ b/src/test/librbd/journal/test_mock_PromoteRequest.cc
@@ -0,0 +1,356 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "librbd/journal/OpenRequest.h"
+#include "librbd/journal/PromoteRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef ::journal::MockJournalerProxy Journaler;
+ typedef ::journal::MockFutureProxy Future;
+};
+
+template <>
+struct OpenRequest<MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static OpenRequest *s_instance;
+ static OpenRequest *create(MockTestImageCtx *image_ctx,
+ ::journal::MockJournalerProxy *journaler,
+ ceph::mutex *lock, ImageClientMeta *client_meta,
+ uint64_t *tag_tid, journal::TagData *tag_data,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ client_meta->tag_class = 456;
+ tag_data->mirror_uuid = Journal<>::ORPHAN_MIRROR_UUID;
+ *tag_tid = 567;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ OpenRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+OpenRequest<MockTestImageCtx> *OpenRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+} // namespace librbd
+
+// template definitions
+#include "librbd/journal/PromoteRequest.cc"
+template class librbd::journal::PromoteRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace journal {
+
+using ::testing::_;
+using ::testing::A;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockJournalPromoteRequest : public TestMockFixture {
+public:
+ typedef PromoteRequest<MockTestImageCtx> MockPromoteRequest;
+ typedef OpenRequest<MockTestImageCtx> MockOpenRequest;
+
+ void expect_construct_journaler(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, construct());
+ }
+
+ void expect_open_journaler(MockTestImageCtx &mock_image_ctx,
+ MockOpenRequest &mock_open_request, int r) {
+ EXPECT_CALL(mock_open_request, send())
+ .WillOnce(FinishRequest(&mock_open_request, r, &mock_image_ctx));
+ }
+
+ void expect_allocate_tag(::journal::MockJournaler &mock_journaler,
+ const journal::TagPredecessor &predecessor, int r) {
+ TagData tag_data;
+ tag_data.mirror_uuid = Journal<>::LOCAL_MIRROR_UUID;
+ tag_data.predecessor = predecessor;
+
+ bufferlist tag_data_bl;
+ using ceph::encode;
+ encode(tag_data, tag_data_bl);
+
+ EXPECT_CALL(mock_journaler, allocate_tag(456, ContentsEqual(tag_data_bl),
+ _, _))
+ .WillOnce(WithArg<3>(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL))));
+ }
+
+ void expect_append_journaler(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, append(_, _))
+ .WillOnce(Return(::journal::MockFutureProxy()));
+ }
+
+ void expect_future_flush(::journal::MockFuture &mock_future, int r) {
+ EXPECT_CALL(mock_future, flush(_))
+ .WillOnce(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ void expect_future_committed(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, committed(A<const ::journal::MockFutureProxy &>()));
+ }
+
+ void expect_flush_commit_position(::journal::MockJournaler &mock_journaler,
+ int r) {
+ EXPECT_CALL(mock_journaler, flush_commit_position(_))
+ .WillOnce(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ void expect_start_append(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, start_append(_));
+ }
+
+ void expect_stop_append(::journal::MockJournaler &mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, stop_append(_))
+ .WillOnce(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ void expect_shut_down_journaler(::journal::MockJournaler &mock_journaler,
+ int r) {
+ EXPECT_CALL(mock_journaler, shut_down(_))
+ .WillOnce(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+};
+
+TEST_F(TestMockJournalPromoteRequest, SuccessOrderly) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+ MockOpenRequest mock_open_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_open_request, 0);
+ expect_allocate_tag(mock_journaler,
+ {Journal<>::ORPHAN_MIRROR_UUID, true, 567, 1}, 0);
+
+ ::journal::MockFuture mock_future;
+ expect_start_append(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_future_flush(mock_future, 0);
+ expect_future_committed(mock_journaler);
+ expect_flush_commit_position(mock_journaler, 0);
+ expect_stop_append(mock_journaler, 0);
+
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPromoteRequest::create(&mock_image_ctx, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockJournalPromoteRequest, SuccessForced) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+ MockOpenRequest mock_open_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_open_request, 0);
+ expect_allocate_tag(mock_journaler,
+ {Journal<>::LOCAL_MIRROR_UUID, true, 567, 0}, 0);
+
+ ::journal::MockFuture mock_future;
+ expect_start_append(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_future_flush(mock_future, 0);
+ expect_future_committed(mock_journaler);
+ expect_flush_commit_position(mock_journaler, 0);
+ expect_stop_append(mock_journaler, 0);
+
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPromoteRequest::create(&mock_image_ctx, true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockJournalPromoteRequest, OpenError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+ MockOpenRequest mock_open_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_open_request, -ENOENT);
+ expect_shut_down_journaler(mock_journaler, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPromoteRequest::create(&mock_image_ctx, false, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockJournalPromoteRequest, AllocateTagError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+ MockOpenRequest mock_open_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_open_request, 0);
+ expect_allocate_tag(mock_journaler,
+ {Journal<>::LOCAL_MIRROR_UUID, true, 567, 0}, -EBADMSG);
+ expect_shut_down_journaler(mock_journaler, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPromoteRequest::create(&mock_image_ctx, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EBADMSG, ctx.wait());
+}
+
+TEST_F(TestMockJournalPromoteRequest, AppendEventError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+ MockOpenRequest mock_open_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_open_request, 0);
+ expect_allocate_tag(mock_journaler,
+ {Journal<>::ORPHAN_MIRROR_UUID, true, 567, 1}, 0);
+
+ ::journal::MockFuture mock_future;
+ expect_start_append(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_future_flush(mock_future, -EPERM);
+ expect_stop_append(mock_journaler, 0);
+
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPromoteRequest::create(&mock_image_ctx, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockJournalPromoteRequest, CommitEventError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+ MockOpenRequest mock_open_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_open_request, 0);
+ expect_allocate_tag(mock_journaler,
+ {Journal<>::ORPHAN_MIRROR_UUID, true, 567, 1}, 0);
+
+ ::journal::MockFuture mock_future;
+ expect_start_append(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_future_flush(mock_future, 0);
+ expect_future_committed(mock_journaler);
+ expect_flush_commit_position(mock_journaler, -EINVAL);
+ expect_stop_append(mock_journaler, 0);
+
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPromoteRequest::create(&mock_image_ctx, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockJournalPromoteRequest, ShutDownError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ ::journal::MockJournaler mock_journaler;
+ MockOpenRequest mock_open_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_open_request, 0);
+ expect_allocate_tag(mock_journaler,
+ {Journal<>::LOCAL_MIRROR_UUID, true, 567, 0}, 0);
+
+ ::journal::MockFuture mock_future;
+ expect_start_append(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_future_flush(mock_future, 0);
+ expect_future_committed(mock_journaler);
+ expect_flush_commit_position(mock_journaler, 0);
+ expect_stop_append(mock_journaler, 0);
+
+ expect_shut_down_journaler(mock_journaler, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPromoteRequest::create(&mock_image_ctx, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace journal
+} // namespace librbd
diff --git a/src/test/librbd/journal/test_mock_Replay.cc b/src/test/librbd/journal/test_mock_Replay.cc
new file mode 100644
index 000000000..9eb31618e
--- /dev/null
+++ b/src/test/librbd/journal/test_mock_Replay.cc
@@ -0,0 +1,2041 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/journal/Replay.h"
+#include "librbd/journal/Types.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockReplayImageCtx : public MockImageCtx {
+ explicit MockReplayImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace io {
+
+template <>
+struct ImageRequest<MockReplayImageCtx> {
+ static ImageRequest *s_instance;
+
+ MOCK_METHOD4(aio_write, void(AioCompletion *c, const Extents &image_extents,
+ const bufferlist &bl, int op_flags));
+ static void aio_write(MockReplayImageCtx *ictx, AioCompletion *c,
+ Extents&& image_extents, ImageArea area,
+ bufferlist&& bl, int op_flags,
+ const ZTracer::Trace &parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_write(c, image_extents, bl, op_flags);
+ }
+
+ MOCK_METHOD3(aio_discard, void(AioCompletion *c, const Extents& image_extents,
+ uint32_t discard_granularity_bytes));
+ static void aio_discard(MockReplayImageCtx *ictx, AioCompletion *c,
+ Extents&& image_extents, ImageArea area,
+ uint32_t discard_granularity_bytes,
+ const ZTracer::Trace &parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_discard(c, image_extents, discard_granularity_bytes);
+ }
+
+ MOCK_METHOD1(aio_flush, void(AioCompletion *c));
+ static void aio_flush(MockReplayImageCtx *ictx, AioCompletion *c,
+ FlushSource, const ZTracer::Trace &parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_flush(c);
+ }
+
+ MOCK_METHOD4(aio_writesame, void(AioCompletion *c,
+ const Extents& image_extents,
+ const bufferlist &bl,
+ int op_flags));
+ static void aio_writesame(MockReplayImageCtx *ictx, AioCompletion *c,
+ Extents&& image_extents, ImageArea area,
+ bufferlist&& bl, int op_flags,
+ const ZTracer::Trace &parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_writesame(c, image_extents, bl, op_flags);
+ }
+
+ MOCK_METHOD6(aio_compare_and_write, void(AioCompletion *c, const Extents &image_extents,
+ const bufferlist &cmp_bl, const bufferlist &bl,
+ uint64_t *mismatch_offset,
+ int op_flags));
+ static void aio_compare_and_write(MockReplayImageCtx *ictx, AioCompletion *c,
+ Extents&& image_extents, ImageArea area,
+ bufferlist&& cmp_bl, bufferlist&& bl,
+ uint64_t* mismatch_offset, int op_flags,
+ const ZTracer::Trace &parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_compare_and_write(c, image_extents, cmp_bl, bl,
+ mismatch_offset, op_flags);
+ }
+
+ ImageRequest() {
+ s_instance = this;
+ }
+};
+
+ImageRequest<MockReplayImageCtx> *ImageRequest<MockReplayImageCtx>::s_instance = nullptr;
+
+} // namespace io
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(librbd::MockReplayImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+} // namespace librbd
+
+// template definitions
+#include "librbd/journal/Replay.cc"
+template class librbd::journal::Replay<librbd::MockReplayImageCtx>;
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::StrEq;
+using ::testing::WithArgs;
+
+MATCHER_P(BufferlistEqual, str, "") {
+ bufferlist bl(arg);
+ return (strncmp(bl.c_str(), str, strlen(str)) == 0);
+}
+
+MATCHER_P(CStrEq, str, "") {
+ return (strncmp(arg, str, strlen(str)) == 0);
+}
+
+ACTION_P2(NotifyInvoke, lock, cond) {
+ std::lock_guard locker{*lock};
+ cond->notify_all();
+}
+
+ACTION_P2(CompleteAioCompletion, r, image_ctx) {
+ image_ctx->op_work_queue->queue(new LambdaContext([this, arg0](int r) {
+ arg0->get();
+ arg0->init_time(image_ctx, librbd::io::AIO_TYPE_NONE);
+ arg0->set_request_count(1);
+ arg0->complete_request(r);
+ }), r);
+}
+
+namespace librbd {
+namespace journal {
+
+class TestMockJournalReplay : public TestMockFixture {
+public:
+ typedef io::ImageRequest<MockReplayImageCtx> MockIoImageRequest;
+ typedef Replay<MockReplayImageCtx> MockJournalReplay;
+
+ TestMockJournalReplay() = default;
+
+ void expect_accept_ops(MockExclusiveLock &mock_exclusive_lock, bool accept) {
+ EXPECT_CALL(mock_exclusive_lock, accept_ops()).WillRepeatedly(
+ Return(accept));
+ }
+
+ void expect_aio_discard(MockIoImageRequest &mock_io_image_request,
+ io::AioCompletion **aio_comp, uint64_t off,
+ uint64_t len, uint32_t discard_granularity_bytes) {
+ EXPECT_CALL(mock_io_image_request, aio_discard(_, io::Extents{{off, len}},
+ discard_granularity_bytes))
+ .WillOnce(SaveArg<0>(aio_comp));
+ }
+
+ void expect_aio_flush(MockIoImageRequest &mock_io_image_request,
+ io::AioCompletion **aio_comp) {
+ EXPECT_CALL(mock_io_image_request, aio_flush(_))
+ .WillOnce(SaveArg<0>(aio_comp));
+ }
+
+ void expect_aio_flush(MockReplayImageCtx &mock_image_ctx,
+ MockIoImageRequest &mock_io_image_request, int r) {
+ EXPECT_CALL(mock_io_image_request, aio_flush(_))
+ .WillOnce(CompleteAioCompletion(r, mock_image_ctx.image_ctx));
+ }
+
+ void expect_aio_write(MockIoImageRequest &mock_io_image_request,
+ io::AioCompletion **aio_comp, uint64_t off,
+ uint64_t len, const char *data) {
+ EXPECT_CALL(mock_io_image_request,
+ aio_write(_, io::Extents{{off, len}}, BufferlistEqual(data), _))
+ .WillOnce(SaveArg<0>(aio_comp));
+ }
+
+ void expect_aio_writesame(MockIoImageRequest &mock_io_image_request,
+ io::AioCompletion **aio_comp, uint64_t off,
+ uint64_t len, const char *data) {
+ EXPECT_CALL(mock_io_image_request,
+ aio_writesame(_, io::Extents{{off, len}},
+ BufferlistEqual(data), _))
+ .WillOnce(SaveArg<0>(aio_comp));
+ }
+
+ void expect_aio_compare_and_write(MockIoImageRequest &mock_io_image_request,
+ io::AioCompletion **aio_comp, uint64_t off,
+ uint64_t len, const char *cmp_data,
+ const char *data,
+ uint64_t *mismatch_offset) {
+ EXPECT_CALL(mock_io_image_request,
+ aio_compare_and_write(_, io::Extents{{off, len}},
+ BufferlistEqual(cmp_data),
+ BufferlistEqual(data),
+ mismatch_offset, _))
+ .WillOnce(SaveArg<0>(aio_comp));
+ }
+
+ void expect_flatten(MockReplayImageCtx &mock_image_ctx, Context **on_finish) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_flatten(_, _))
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_rename(MockReplayImageCtx &mock_image_ctx, Context **on_finish,
+ const char *image_name) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_rename(StrEq(image_name), _))
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_resize(MockReplayImageCtx &mock_image_ctx, Context **on_finish,
+ uint64_t size, uint64_t op_tid) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_resize(size, _, _, _, op_tid))
+ .WillOnce(DoAll(SaveArg<3>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_snap_create(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, const char *snap_name,
+ uint64_t op_tid) {
+ EXPECT_CALL(*mock_image_ctx.operations,
+ execute_snap_create(_, StrEq(snap_name), _, op_tid,
+ SNAP_CREATE_FLAG_SKIP_NOTIFY_QUIESCE, _))
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_snap_remove(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, const char *snap_name) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_remove(_, StrEq(snap_name), _))
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_snap_rename(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, uint64_t snap_id,
+ const char *snap_name) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_rename(snap_id, StrEq(snap_name), _))
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_snap_protect(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, const char *snap_name) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_protect(_, StrEq(snap_name), _))
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_snap_unprotect(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, const char *snap_name) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_unprotect(_, StrEq(snap_name), _))
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_snap_rollback(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, const char *snap_name) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_rollback(_, StrEq(snap_name), _, _))
+ .WillOnce(DoAll(SaveArg<3>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_update_features(MockReplayImageCtx &mock_image_ctx, Context **on_finish,
+ uint64_t features, bool enabled, uint64_t op_tid) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_update_features(features, enabled, _, op_tid))
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_metadata_set(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, const char *key,
+ const char *value) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(StrEq(key),
+ StrEq(value), _))
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_metadata_remove(MockReplayImageCtx &mock_image_ctx,
+ Context **on_finish, const char *key) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(StrEq(key), _))
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+ }
+
+ void expect_refresh_image(MockReplayImageCtx &mock_image_ctx, bool required,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(required));
+ if (required) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+ }
+
+ void when_process(MockJournalReplay &mock_journal_replay,
+ EventEntry &&event_entry, Context *on_ready,
+ Context *on_safe) {
+ bufferlist bl;
+ encode(event_entry, bl);
+
+ auto it = bl.cbegin();
+ when_process(mock_journal_replay, &it, on_ready, on_safe);
+ }
+
+ void when_process(MockJournalReplay &mock_journal_replay,
+ bufferlist::const_iterator *it, Context *on_ready,
+ Context *on_safe) {
+ EventEntry event_entry;
+ int r = mock_journal_replay.decode(it, &event_entry);
+ ASSERT_EQ(0, r);
+
+ mock_journal_replay.process(event_entry, on_ready, on_safe);
+ }
+
+ void when_complete(MockReplayImageCtx &mock_image_ctx,
+ io::AioCompletion *aio_comp, int r) {
+ aio_comp->get();
+ aio_comp->init_time(mock_image_ctx.image_ctx, librbd::io::AIO_TYPE_NONE);
+ aio_comp->set_request_count(1);
+ aio_comp->complete_request(r);
+ }
+
+ int when_flush(MockJournalReplay &mock_journal_replay) {
+ C_SaferCond ctx;
+ mock_journal_replay.flush(&ctx);
+ return ctx.wait();
+ }
+
+ int when_shut_down(MockJournalReplay &mock_journal_replay, bool cancel_ops) {
+ C_SaferCond ctx;
+ mock_journal_replay.shut_down(cancel_ops, &ctx);
+ return ctx.wait();
+ }
+
+ void when_replay_op_ready(MockJournalReplay &mock_journal_replay,
+ uint64_t op_tid, Context *on_resume) {
+ mock_journal_replay.replay_op_ready(op_tid, on_resume);
+ }
+
+ void wait_for_op_invoked(Context **on_finish, int r) {
+ {
+ std::unique_lock locker{m_invoke_lock};
+ m_invoke_cond.wait(locker, [on_finish] { return *on_finish != nullptr; });
+ }
+ (*on_finish)->complete(r);
+ }
+
+ bufferlist to_bl(const std::string &str) {
+ bufferlist bl;
+ bl.append(str);
+ return bl;
+ }
+
+ ceph::mutex m_invoke_lock = ceph::make_mutex("m_invoke_lock");
+ ceph::condition_variable m_invoke_cond;
+};
+
+TEST_F(TestMockJournalReplay, AioDiscard) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ io::AioCompletion *aio_comp;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+ ictx->discard_granularity_bytes);
+ when_process(mock_journal_replay,
+ EventEntry{AioDiscardEvent(123, 456,
+ ictx->discard_granularity_bytes)},
+ &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+ ASSERT_EQ(0, on_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, AioWrite) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ io::AioCompletion *aio_comp;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ expect_aio_write(mock_io_image_request, &aio_comp, 123, 456, "test");
+ when_process(mock_journal_replay,
+ EventEntry{AioWriteEvent(123, 456, to_bl("test"))},
+ &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+ ASSERT_EQ(0, on_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, AioFlush) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ io::AioCompletion *aio_comp;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ expect_aio_flush(mock_io_image_request, &aio_comp);
+ when_process(mock_journal_replay, EventEntry{AioFlushEvent()},
+ &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_safe.wait());
+
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+ ASSERT_EQ(0, on_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, AioWriteSame) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ io::AioCompletion *aio_comp;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ expect_aio_writesame(mock_io_image_request, &aio_comp, 123, 456, "333");
+ when_process(mock_journal_replay,
+ EventEntry{AioWriteSameEvent(123, 456, to_bl("333"))},
+ &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+ ASSERT_EQ(0, on_safe.wait());
+}
+
+
+TEST_F(TestMockJournalReplay, AioCompareAndWrite) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_write_journal_replay(mock_image_ctx);
+ MockJournalReplay mock_compare_and_write_journal_replay(mock_image_ctx);
+ MockJournalReplay mock_mis_compare_and_write_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ io::AioCompletion *aio_comp;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ expect_aio_write(mock_io_image_request, &aio_comp, 512, 512, "test");
+ when_process(mock_write_journal_replay,
+ EventEntry{AioWriteEvent(512, 512, to_bl("test"))},
+ &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_shut_down(mock_write_journal_replay, false));
+ ASSERT_EQ(0, on_safe.wait());
+
+ expect_aio_compare_and_write(mock_io_image_request, &aio_comp,
+ 512, 512, "test", "test", nullptr);
+ when_process(mock_compare_and_write_journal_replay,
+ EventEntry{AioCompareAndWriteEvent(512, 512, to_bl("test"),
+ to_bl("test"))}, &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_shut_down(mock_compare_and_write_journal_replay, false));
+ ASSERT_EQ(0, on_safe.wait());
+
+ expect_aio_compare_and_write(mock_io_image_request, &aio_comp,
+ 512, 512, "111", "test", nullptr);
+ when_process(mock_mis_compare_and_write_journal_replay,
+ EventEntry{AioCompareAndWriteEvent(512, 512, to_bl("111"),
+ to_bl("test"))}, &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_shut_down(mock_mis_compare_and_write_journal_replay, false));
+ ASSERT_EQ(0, on_safe.wait());
+
+}
+
+TEST_F(TestMockJournalReplay, IOError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ io::AioCompletion *aio_comp;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+ ictx->discard_granularity_bytes);
+ when_process(mock_journal_replay,
+ EventEntry{AioDiscardEvent(123, 456,
+ ictx->discard_granularity_bytes)},
+ &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, -EINVAL);
+ ASSERT_EQ(-EINVAL, on_safe.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+ ASSERT_EQ(0, on_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, SoftFlushIO) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ const size_t io_count = 32;
+ C_SaferCond on_safes[io_count];
+ for (size_t i = 0; i < io_count; ++i) {
+ io::AioCompletion *aio_comp;
+ io::AioCompletion *flush_comp = nullptr;
+ C_SaferCond on_ready;
+ expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+ ictx->discard_granularity_bytes);
+ if (i == io_count - 1) {
+ expect_aio_flush(mock_io_image_request, &flush_comp);
+ }
+ when_process(mock_journal_replay,
+ EventEntry{AioDiscardEvent(123, 456,
+ ictx->discard_granularity_bytes)},
+ &on_ready, &on_safes[i]);
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ if (flush_comp != nullptr) {
+ when_complete(mock_image_ctx, flush_comp, 0);
+ }
+ }
+ for (auto &on_safe : on_safes) {
+ ASSERT_EQ(0, on_safe.wait());
+ }
+
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+}
+
+TEST_F(TestMockJournalReplay, PauseIO) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ const size_t io_count = 64;
+ std::list<io::AioCompletion *> flush_comps;
+ C_SaferCond on_safes[io_count];
+ for (size_t i = 0; i < io_count; ++i) {
+ io::AioCompletion *aio_comp;
+ C_SaferCond on_ready;
+ expect_aio_write(mock_io_image_request, &aio_comp, 123, 456, "test");
+ if ((i + 1) % 32 == 0) {
+ flush_comps.push_back(nullptr);
+ expect_aio_flush(mock_io_image_request, &flush_comps.back());
+ }
+ when_process(mock_journal_replay,
+ EventEntry{AioWriteEvent(123, 456, to_bl("test"))},
+ &on_ready, &on_safes[i]);
+ when_complete(mock_image_ctx, aio_comp, 0);
+ if (i < io_count - 1) {
+ ASSERT_EQ(0, on_ready.wait());
+ } else {
+ for (auto flush_comp : flush_comps) {
+ when_complete(mock_image_ctx, flush_comp, 0);
+ }
+ ASSERT_EQ(0, on_ready.wait());
+ }
+ }
+ for (auto &on_safe : on_safes) {
+ ASSERT_EQ(0, on_safe.wait());
+ }
+
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+}
+
+TEST_F(TestMockJournalReplay, Flush) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ io::AioCompletion *aio_comp = nullptr;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+ ictx->discard_granularity_bytes);
+ when_process(mock_journal_replay,
+ EventEntry{AioDiscardEvent(123, 456,
+ ictx->discard_granularity_bytes)},
+ &on_ready, &on_safe);
+
+ when_complete(mock_image_ctx, aio_comp, 0);
+ ASSERT_EQ(0, on_ready.wait());
+
+ expect_aio_flush(mock_image_ctx, mock_io_image_request, 0);
+ ASSERT_EQ(0, when_flush(mock_journal_replay));
+ ASSERT_EQ(0, on_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, OpFinishError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRemoveEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, -EIO)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(-EIO, on_start_safe.wait());
+ ASSERT_EQ(-EIO, on_finish_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, BlockedOpFinishError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapCreateEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, -EBADMSG)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(-EBADMSG, on_resume.wait());
+ wait_for_op_invoked(&on_finish, -ESTALE);
+
+ ASSERT_EQ(-ESTALE, on_start_safe.wait());
+ ASSERT_EQ(-ESTALE, on_finish_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, MissingOpFinishEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillRepeatedly(Return(false));
+
+ InSequence seq;
+ Context *on_snap_create_finish = nullptr;
+ expect_snap_create(mock_image_ctx, &on_snap_create_finish, "snap", 123);
+
+ Context *on_snap_remove_finish = nullptr;
+ expect_snap_remove(mock_image_ctx, &on_snap_remove_finish, "snap");
+
+ C_SaferCond on_snap_remove_ready;
+ C_SaferCond on_snap_remove_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRemoveEvent(122,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_snap_remove_ready,
+ &on_snap_remove_safe);
+ ictx->op_work_queue->drain();
+ ASSERT_EQ(0, on_snap_remove_ready.wait());
+
+ C_SaferCond on_snap_create_ready;
+ C_SaferCond on_snap_create_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapCreateEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_snap_create_ready,
+ &on_snap_create_safe);
+ ictx->op_work_queue->drain();
+
+ C_SaferCond on_shut_down;
+ mock_journal_replay.shut_down(false, &on_shut_down);
+
+ wait_for_op_invoked(&on_snap_remove_finish, 0);
+ ASSERT_EQ(0, on_snap_remove_safe.wait());
+
+ C_SaferCond on_snap_create_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_snap_create_resume);
+ ASSERT_EQ(0, on_snap_create_resume.wait());
+
+ wait_for_op_invoked(&on_snap_create_finish, 0);
+ ASSERT_EQ(0, on_snap_create_ready.wait());
+ ASSERT_EQ(0, on_snap_create_safe.wait());
+
+ ASSERT_EQ(0, on_shut_down.wait());
+}
+
+TEST_F(TestMockJournalReplay, MissingOpFinishEventCancelOps) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_snap_create_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_create(mock_image_ctx, &on_snap_create_finish, "snap", 123);
+
+ C_SaferCond on_snap_remove_ready;
+ C_SaferCond on_snap_remove_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRemoveEvent(122,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_snap_remove_ready,
+ &on_snap_remove_safe);
+ ictx->op_work_queue->drain();
+ ASSERT_EQ(0, on_snap_remove_ready.wait());
+
+ C_SaferCond on_snap_create_ready;
+ C_SaferCond on_snap_create_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapCreateEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_snap_create_ready,
+ &on_snap_create_safe);
+ ictx->op_work_queue->drain();
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_snap_create_ready.wait());
+
+ C_SaferCond on_shut_down;
+ mock_journal_replay.shut_down(true, &on_shut_down);
+
+ ASSERT_EQ(-ERESTART, on_resume.wait());
+ on_snap_create_finish->complete(-ERESTART);
+ ASSERT_EQ(-ERESTART, on_snap_create_safe.wait());
+
+ ASSERT_EQ(-ERESTART, on_snap_remove_safe.wait());
+ ASSERT_EQ(0, on_shut_down.wait());
+}
+
+TEST_F(TestMockJournalReplay, UnknownOpFinishEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_ready, &on_safe);
+
+ ASSERT_EQ(0, on_safe.wait());
+ ASSERT_EQ(0, on_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, OpEventError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_remove(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRemoveEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -EINVAL);
+ ASSERT_EQ(-EINVAL, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(-EINVAL, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapCreateEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapCreateEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_resume.wait());
+ wait_for_op_invoked(&on_finish, 0);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapCreateEventExists) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapCreateEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+
+ wait_for_op_invoked(&on_finish, -EEXIST);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapRemoveEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_remove(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRemoveEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapRemoveEventDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_remove(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRemoveEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -ENOENT);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapRenameEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_rename(mock_image_ctx, &on_finish, 234, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRenameEvent(123, 234, "snap1", "snap")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapRenameEventExists) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_rename(mock_image_ctx, &on_finish, 234, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRenameEvent(123, 234, "snap1", "snap")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -EEXIST);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapProtectEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_protect(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapProtectEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapProtectEventBusy) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_protect(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapProtectEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -EBUSY);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapUnprotectEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_unprotect(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapUnprotectEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapUnprotectOpFinishBusy) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapUnprotectEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ // aborts the snap unprotect op if image had children
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, -EBUSY)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapUnprotectEventInvalid) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_unprotect(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapUnprotectEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -EINVAL);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, SnapRollbackEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_snap_rollback(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRollbackEvent(123,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap")},
+ &on_start_ready,
+ &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, RenameEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_rename(mock_image_ctx, &on_finish, "image");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{RenameEvent(123, "image")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, RenameEventExists) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_rename(mock_image_ctx, &on_finish, "image");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{RenameEvent(123, "image")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -EEXIST);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, ResizeEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_resize(mock_image_ctx, &on_finish, 234, 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{ResizeEvent(123, 234)},
+ &on_start_ready, &on_start_safe);
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_resume.wait());
+ wait_for_op_invoked(&on_finish, 0);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, FlattenEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_flatten(mock_image_ctx, &on_finish);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{FlattenEvent(123)},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, FlattenEventInvalid) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_flatten(mock_image_ctx, &on_finish);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{FlattenEvent(123)},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -EINVAL);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, UpdateFeaturesEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features = RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF;
+ bool enabled = !ictx->test_features(features);
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_update_features(mock_image_ctx, &on_finish, features, enabled, 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{UpdateFeaturesEvent(123, features, enabled)},
+ &on_start_ready, &on_start_safe);
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_resume.wait());
+ wait_for_op_invoked(&on_finish, 0);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, MetadataSetEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_metadata_set(mock_image_ctx, &on_finish, "key", "value");
+ expect_refresh_image(mock_image_ctx, false, 0);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{MetadataSetEvent(123, "key", "value")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, MetadataRemoveEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_metadata_remove(mock_image_ctx, &on_finish, "key");
+ expect_refresh_image(mock_image_ctx, false, 0);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{MetadataRemoveEvent(123, "key")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, 0);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, MetadataRemoveEventDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_metadata_remove(mock_image_ctx, &on_finish, "key");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{MetadataRemoveEvent(123, "key")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ wait_for_op_invoked(&on_finish, -ENOENT);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, UnknownEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ bufferlist bl;
+ ENCODE_START(1, 1, bl);
+ encode(static_cast<uint32_t>(-1), bl);
+ ENCODE_FINISH(bl);
+
+ auto it = bl.cbegin();
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ when_process(mock_journal_replay, &it, &on_ready, &on_safe);
+
+ ASSERT_EQ(0, on_safe.wait());
+ ASSERT_EQ(0, on_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, RefreshImageBeforeOpStart) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_resize(mock_image_ctx, &on_finish, 234, 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{ResizeEvent(123, 234)},
+ &on_start_ready, &on_start_safe);
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_resume.wait());
+ wait_for_op_invoked(&on_finish, 0);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, FlushEventAfterShutDown) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ when_process(mock_journal_replay, EventEntry{AioFlushEvent()},
+ &on_ready, &on_safe);
+ ASSERT_EQ(0, on_ready.wait());
+ ASSERT_EQ(-ESHUTDOWN, on_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, ModifyEventAfterShutDown) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ when_process(mock_journal_replay,
+ EventEntry{AioWriteEvent(123, 456, to_bl("test"))},
+ &on_ready, &on_safe);
+ ASSERT_EQ(0, on_ready.wait());
+ ASSERT_EQ(-ESHUTDOWN, on_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, OpEventAfterShutDown) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, true);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ when_process(mock_journal_replay, EventEntry{RenameEvent(123, "image")},
+ &on_ready, &on_safe);
+ ASSERT_EQ(0, on_ready.wait());
+ ASSERT_EQ(-ESHUTDOWN, on_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, LockLostBeforeProcess) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, false);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ MockIoImageRequest mock_io_image_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ C_SaferCond on_ready;
+ C_SaferCond on_safe;
+ when_process(mock_journal_replay, EventEntry{AioFlushEvent()},
+ &on_ready, &on_safe);
+ ASSERT_EQ(0, on_ready.wait());
+ ASSERT_EQ(-ECANCELED, on_safe.wait());
+
+ ASSERT_EQ(0, when_shut_down(mock_journal_replay, false));
+}
+
+TEST_F(TestMockJournalReplay, LockLostBeforeExecuteOp) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_accept_ops(mock_exclusive_lock, false);
+
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ EXPECT_CALL(mock_exclusive_lock, accept_ops()).WillOnce(Return(true));
+ EXPECT_CALL(mock_exclusive_lock, accept_ops()).WillOnce(Return(true));
+ expect_refresh_image(mock_image_ctx, false, 0);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{RenameEvent(123, "image")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(-ECANCELED, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(-ECANCELED, on_finish_safe.wait());
+}
+
+} // namespace journal
+} // namespace librbd
diff --git a/src/test/librbd/journal/test_mock_ResetRequest.cc b/src/test/librbd/journal/test_mock_ResetRequest.cc
new file mode 100644
index 000000000..2c722558c
--- /dev/null
+++ b/src/test/librbd/journal/test_mock_ResetRequest.cc
@@ -0,0 +1,278 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "cls/journal/cls_journal_types.h"
+#include "librbd/journal/CreateRequest.h"
+#include "librbd/journal/RemoveRequest.h"
+#include "librbd/journal/ResetRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef ::journal::MockJournalerProxy Journaler;
+};
+
+template <>
+struct CreateRequest<MockTestImageCtx> {
+ static CreateRequest* s_instance;
+ Context* on_finish = nullptr;
+
+ static CreateRequest* create(IoCtx &ioctx, const std::string &imageid,
+ uint8_t order, uint8_t splay_width,
+ const std::string &object_pool,
+ uint64_t tag_class, TagData &tag_data,
+ const std::string &client_id,
+ ContextWQ *op_work_queue, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ CreateRequest() {
+ s_instance = this;
+ }
+};
+
+template <>
+struct RemoveRequest<MockTestImageCtx> {
+ static RemoveRequest* s_instance;
+ Context* on_finish = nullptr;
+
+ static RemoveRequest* create(IoCtx &ioctx, const std::string &image_id,
+ const std::string &client_id,
+ ContextWQ *op_work_queue, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+};
+
+CreateRequest<MockTestImageCtx>* CreateRequest<MockTestImageCtx>::s_instance = nullptr;
+RemoveRequest<MockTestImageCtx>* RemoveRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+} // namespace librbd
+
+#include "librbd/journal/ResetRequest.cc"
+
+namespace librbd {
+namespace journal {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockJournalResetRequest : public TestMockFixture {
+public:
+ typedef ResetRequest<MockTestImageCtx> MockResetRequest;
+ typedef CreateRequest<MockTestImageCtx> MockCreateRequest;
+ typedef RemoveRequest<MockTestImageCtx> MockRemoveRequest;
+
+ void expect_construct_journaler(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, construct());
+ }
+
+ void expect_init_journaler(::journal::MockJournaler &mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, init(_))
+ .WillOnce(CompleteContext(r, static_cast<ContextWQ*>(NULL)));
+ }
+
+ void expect_get_metadata(::journal::MockJournaler& mock_journaler) {
+ EXPECT_CALL(mock_journaler, get_metadata(_, _, _))
+ .WillOnce(Invoke([](uint8_t* order, uint8_t* splay_width,
+ int64_t* pool_id) {
+ *order = 24;
+ *splay_width = 4;
+ *pool_id = -1;
+ }));
+ }
+
+ void expect_shut_down_journaler(::journal::MockJournaler &mock_journaler,
+ int r) {
+ EXPECT_CALL(mock_journaler, shut_down(_))
+ .WillOnce(CompleteContext(r, static_cast<ContextWQ*>(NULL)));
+ }
+
+ void expect_remove(MockRemoveRequest& mock_remove_request, int r) {
+ EXPECT_CALL(mock_remove_request, send())
+ .WillOnce(Invoke([&mock_remove_request, r]() {
+ mock_remove_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_create(MockCreateRequest& mock_create_request, int r) {
+ EXPECT_CALL(mock_create_request, send())
+ .WillOnce(Invoke([&mock_create_request, r]() {
+ mock_create_request.on_finish->complete(r);
+ }));
+ }
+};
+
+TEST_F(TestMockJournalResetRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ InSequence seq;
+ ::journal::MockJournaler mock_journaler;
+ expect_construct_journaler(mock_journaler);
+ expect_init_journaler(mock_journaler, 0);
+ expect_get_metadata(mock_journaler);
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, 0);
+
+ ContextWQ* context_wq;
+ Journal<>::get_work_queue(ictx->cct, &context_wq);
+
+ C_SaferCond ctx;
+ auto req = MockResetRequest::create(m_ioctx, "image id",
+ Journal<>::IMAGE_CLIENT_ID,
+ Journal<>::LOCAL_MIRROR_UUID,
+ context_wq, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockJournalResetRequest, InitError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ InSequence seq;
+ ::journal::MockJournaler mock_journaler;
+ expect_construct_journaler(mock_journaler);
+ expect_init_journaler(mock_journaler, -EINVAL);
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ ContextWQ* context_wq;
+ Journal<>::get_work_queue(ictx->cct, &context_wq);
+
+ C_SaferCond ctx;
+ auto req = MockResetRequest::create(m_ioctx, "image id",
+ Journal<>::IMAGE_CLIENT_ID,
+ Journal<>::LOCAL_MIRROR_UUID,
+ context_wq, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockJournalResetRequest, ShutDownError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ InSequence seq;
+ ::journal::MockJournaler mock_journaler;
+ expect_construct_journaler(mock_journaler);
+ expect_init_journaler(mock_journaler, 0);
+ expect_get_metadata(mock_journaler);
+ expect_shut_down_journaler(mock_journaler, -EINVAL);
+
+ ContextWQ* context_wq;
+ Journal<>::get_work_queue(ictx->cct, &context_wq);
+
+ C_SaferCond ctx;
+ auto req = MockResetRequest::create(m_ioctx, "image id",
+ Journal<>::IMAGE_CLIENT_ID,
+ Journal<>::LOCAL_MIRROR_UUID,
+ context_wq, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockJournalResetRequest, RemoveError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ InSequence seq;
+ ::journal::MockJournaler mock_journaler;
+ expect_construct_journaler(mock_journaler);
+ expect_init_journaler(mock_journaler, 0);
+ expect_get_metadata(mock_journaler);
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, -EINVAL);
+
+ ContextWQ* context_wq;
+ Journal<>::get_work_queue(ictx->cct, &context_wq);
+
+ C_SaferCond ctx;
+ auto req = MockResetRequest::create(m_ioctx, "image id",
+ Journal<>::IMAGE_CLIENT_ID,
+ Journal<>::LOCAL_MIRROR_UUID,
+ context_wq, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockJournalResetRequest, CreateError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ InSequence seq;
+ ::journal::MockJournaler mock_journaler;
+ expect_construct_journaler(mock_journaler);
+ expect_init_journaler(mock_journaler, 0);
+ expect_get_metadata(mock_journaler);
+ expect_shut_down_journaler(mock_journaler, 0);
+
+ MockRemoveRequest mock_remove_request;
+ expect_remove(mock_remove_request, 0);
+
+ MockCreateRequest mock_create_request;
+ expect_create(mock_create_request, -EINVAL);
+
+ ContextWQ* context_wq;
+ Journal<>::get_work_queue(ictx->cct, &context_wq);
+
+ C_SaferCond ctx;
+ auto req = MockResetRequest::create(m_ioctx, "image id",
+ Journal<>::IMAGE_CLIENT_ID,
+ Journal<>::LOCAL_MIRROR_UUID,
+ context_wq, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace journal
+} // namespace librbd
diff --git a/src/test/librbd/managed_lock/test_mock_AcquireRequest.cc b/src/test/librbd/managed_lock/test_mock_AcquireRequest.cc
new file mode 100644
index 000000000..f3e587c92
--- /dev/null
+++ b/src/test/librbd/managed_lock/test_mock_AcquireRequest.cc
@@ -0,0 +1,271 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "cls/lock/cls_lock_ops.h"
+#include "librbd/managed_lock/AcquireRequest.h"
+#include "librbd/managed_lock/BreakRequest.h"
+#include "librbd/managed_lock/GetLockerRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+
+namespace librbd {
+namespace watcher {
+template <>
+struct Traits<MockImageCtx> {
+ typedef librbd::MockImageWatcher Watcher;
+};
+}
+
+namespace managed_lock {
+
+template<>
+struct BreakRequest<librbd::MockImageCtx> {
+ Context *on_finish = nullptr;
+ static BreakRequest *s_instance;
+ static BreakRequest* create(librados::IoCtx& ioctx,
+ AsioEngine& asio_engine,
+ const std::string& oid, const Locker &locker,
+ bool exclusive, bool blocklist_locker,
+ uint32_t blocklist_expire_seconds,
+ bool force_break_lock, Context *on_finish) {
+ CephContext *cct = reinterpret_cast<CephContext *>(ioctx.cct());
+ EXPECT_EQ(cct->_conf.get_val<bool>("rbd_blocklist_on_break_lock"),
+ blocklist_locker);
+ EXPECT_EQ(cct->_conf.get_val<uint64_t>("rbd_blocklist_expire_seconds"),
+ blocklist_expire_seconds);
+ EXPECT_FALSE(force_break_lock);
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ BreakRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct GetLockerRequest<librbd::MockImageCtx> {
+ Locker *locker = nullptr;
+ Context *on_finish = nullptr;
+
+ static GetLockerRequest *s_instance;
+ static GetLockerRequest* create(librados::IoCtx& ioctx,
+ const std::string& oid, bool exclusive,
+ Locker *locker, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->locker = locker;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetLockerRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+BreakRequest<librbd::MockImageCtx> *BreakRequest<librbd::MockImageCtx>::s_instance = nullptr;
+GetLockerRequest<librbd::MockImageCtx> *GetLockerRequest<librbd::MockImageCtx>::s_instance = nullptr;
+
+} // namespace managed_lock
+} // namespace librbd
+
+// template definitions
+#include "librbd/managed_lock/AcquireRequest.cc"
+template class librbd::managed_lock::AcquireRequest<librbd::MockImageCtx>;
+
+namespace {
+
+MATCHER_P(IsLockType, exclusive, "") {
+ cls_lock_lock_op op;
+ bufferlist bl;
+ bl.share(arg);
+ auto iter = bl.cbegin();
+ decode(op, iter);
+ return op.type == (exclusive ? ClsLockType::EXCLUSIVE : ClsLockType::SHARED);
+}
+
+} // anonymous namespace
+
+namespace librbd {
+namespace managed_lock {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+static const std::string TEST_COOKIE("auto 123");
+
+class TestMockManagedLockAcquireRequest : public TestMockFixture {
+public:
+ typedef AcquireRequest<MockImageCtx> MockAcquireRequest;
+ typedef BreakRequest<MockImageCtx> MockBreakRequest;
+ typedef GetLockerRequest<MockImageCtx> MockGetLockerRequest;
+
+ void expect_lock(MockImageCtx &mock_image_ctx, int r,
+ bool exclusive = true) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("lock"),
+ StrEq("lock"), IsLockType(exclusive), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_get_locker(MockImageCtx &mock_image_ctx,
+ MockGetLockerRequest &mock_get_locker_request,
+ const Locker &locker, int r) {
+ EXPECT_CALL(mock_get_locker_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_get_locker_request, locker, r]() {
+ *mock_get_locker_request.locker = locker;
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_get_locker_request.on_finish, r);
+ }));
+ }
+
+ void expect_break_lock(MockImageCtx &mock_image_ctx,
+ MockBreakRequest &mock_break_request, int r) {
+ EXPECT_CALL(mock_break_request, send())
+ .WillOnce(FinishRequest(&mock_break_request, r, &mock_image_ctx));
+ }
+};
+
+TEST_F(TestMockManagedLockAcquireRequest, SuccessExclusive) {
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockGetLockerRequest mock_get_locker_request;
+
+ InSequence seq;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request, {}, 0);
+ expect_lock(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.image_watcher, *ictx->asio_engine, mock_image_ctx.header_oid,
+ TEST_COOKIE, true, true, 0, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockAcquireRequest, SuccessShared) {
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockGetLockerRequest mock_get_locker_request;
+
+ InSequence seq;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request, {}, 0);
+ expect_lock(mock_image_ctx, 0, false);
+
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.image_watcher, *ictx->asio_engine, mock_image_ctx.header_oid,
+ TEST_COOKIE, false, true, 0, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockAcquireRequest, LockBusy) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockGetLockerRequest mock_get_locker_request;
+ MockBreakRequest mock_break_request;
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_break_lock(mock_image_ctx, mock_break_request, 0);
+ expect_lock(mock_image_ctx, -ENOENT);
+
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.image_watcher, *ictx->asio_engine, mock_image_ctx.header_oid,
+ TEST_COOKIE, true, true, 0, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockAcquireRequest, GetLockInfoError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockGetLockerRequest mock_get_locker_request;
+
+ InSequence seq;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request, {}, -EINVAL);
+
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.image_watcher, *ictx->asio_engine, mock_image_ctx.header_oid,
+ TEST_COOKIE, true, true, 0, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockAcquireRequest, GetLockInfoEmpty) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockGetLockerRequest mock_get_locker_request;
+
+ InSequence seq;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request, {}, -ENOENT);
+ expect_lock(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.image_watcher, *ictx->asio_engine, mock_image_ctx.header_oid,
+ TEST_COOKIE, true, true, 0, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockAcquireRequest, BreakLockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockGetLockerRequest mock_get_locker_request;
+ MockBreakRequest mock_break_request;
+
+ InSequence seq;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_break_lock(mock_image_ctx, mock_break_request, -EINVAL);
+
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.image_watcher, *ictx->asio_engine, mock_image_ctx.header_oid,
+ TEST_COOKIE, true, true, 0, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace managed_lock
+} // namespace librbd
diff --git a/src/test/librbd/managed_lock/test_mock_BreakRequest.cc b/src/test/librbd/managed_lock/test_mock_BreakRequest.cc
new file mode 100644
index 000000000..d6239344b
--- /dev/null
+++ b/src/test/librbd/managed_lock/test_mock_BreakRequest.cc
@@ -0,0 +1,484 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "cls/lock/cls_lock_ops.h"
+#include "librbd/managed_lock/BreakRequest.h"
+#include "librbd/managed_lock/GetLockerRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace managed_lock {
+
+template <>
+struct GetLockerRequest<librbd::MockTestImageCtx> {
+ Locker *locker;
+ Context *on_finish = nullptr;
+ static GetLockerRequest *s_instance;
+ static GetLockerRequest* create(librados::IoCtx& ioctx,
+ const std::string& oid, bool exclusive,
+ Locker *locker, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->locker = locker;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+
+ GetLockerRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+GetLockerRequest<librbd::MockTestImageCtx> *GetLockerRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace managed_lock
+} // namespace librbd
+
+// template definitions
+#include "librbd/managed_lock/BreakRequest.cc"
+
+namespace librbd {
+namespace managed_lock {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockManagedLockBreakRequest : public TestMockFixture {
+public:
+ typedef BreakRequest<MockTestImageCtx> MockBreakRequest;
+ typedef GetLockerRequest<MockTestImageCtx> MockGetLockerRequest;
+
+ void expect_list_watchers(MockTestImageCtx &mock_image_ctx, int r,
+ const std::string &address, uint64_t watch_handle) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ list_watchers(mock_image_ctx.header_oid, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ obj_watch_t watcher;
+ strncpy(watcher.addr, (address + ":0/0").c_str(), sizeof(watcher.addr) - 1);
+ watcher.addr[sizeof(watcher.addr) - 1] = '\0';
+ watcher.watcher_id = 0;
+ watcher.cookie = watch_handle;
+
+ std::list<obj_watch_t> watchers;
+ watchers.push_back(watcher);
+
+ expect.WillOnce(DoAll(SetArgPointee<1>(watchers), Return(0)));
+ }
+ }
+
+ void expect_get_locker(MockImageCtx &mock_image_ctx,
+ MockGetLockerRequest &mock_get_locker_request,
+ const Locker &locker, int r) {
+ EXPECT_CALL(mock_get_locker_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_get_locker_request, locker, r]() {
+ *mock_get_locker_request.locker = locker;
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_get_locker_request.on_finish, r);
+ }));
+ }
+
+
+ void expect_blocklist_add(MockTestImageCtx &mock_image_ctx, int r) {
+ auto& mock_rados_client = librados::get_mock_rados_client(
+ mock_image_ctx.rados_api);
+ EXPECT_CALL(mock_rados_client, blocklist_add(_, _)).WillOnce(Return(r));
+ }
+
+ void expect_wait_for_latest_osd_map(MockTestImageCtx &mock_image_ctx, int r) {
+ auto& mock_rados_client = librados::get_mock_rados_client(
+ mock_image_ctx.rados_api);
+ EXPECT_CALL(mock_rados_client, wait_for_latest_osd_map())
+ .WillOnce(Return(r));
+ }
+
+ void expect_break_lock(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("lock"),
+ StrEq("break_lock"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_get_instance_id(MockTestImageCtx &mock_image_ctx, uint64_t id) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), get_instance_id())
+ .WillOnce(Return(id));
+ }
+};
+
+TEST_F(TestMockManagedLockBreakRequest, DeadLockOwner) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+
+ expect_blocklist_add(mock_image_ctx, 0);
+ expect_wait_for_latest_osd_map(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, ForceBreak) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "1.2.3.4", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+
+ expect_blocklist_add(mock_image_ctx, 0);
+ expect_wait_for_latest_osd_map(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, GetWatchersError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, -EINVAL, "dead client", 123);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, GetWatchersAlive) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "1.2.3.4", 123);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EAGAIN, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, GetLockerUpdated) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(2), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, false, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EAGAIN, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, GetLockerBusy) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ -EBUSY);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, false, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EAGAIN, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, GetLockerMissing) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ -ENOENT);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, false, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, GetLockerError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request, {}, -EINVAL);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, false, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, BlocklistDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+
+ expect_break_lock(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, false, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, BlocklistSelf) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 456);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(456), "auto 123", "1.2.3.4:0/0",
+ 123}, 0);
+
+ expect_get_instance_id(mock_image_ctx, 456);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(456), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, BlocklistError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+
+ expect_blocklist_add(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, BreakLockMissing) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+
+ expect_blocklist_add(mock_image_ctx, 0);
+ expect_wait_for_latest_osd_map(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, -ENOENT);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockBreakRequest, BreakLockError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
+
+ MockGetLockerRequest mock_get_locker_request;
+ expect_get_locker(mock_image_ctx, mock_get_locker_request,
+ {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123},
+ 0);
+
+ expect_blocklist_add(mock_image_ctx, 0);
+ expect_wait_for_latest_osd_map(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ Locker locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+ MockBreakRequest *req = MockBreakRequest::create(
+ mock_image_ctx.md_ctx, *ictx->asio_engine, mock_image_ctx.header_oid,
+ locker, true, true, 0, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace managed_lock
+} // namespace librbd
+
diff --git a/src/test/librbd/managed_lock/test_mock_GetLockerRequest.cc b/src/test/librbd/managed_lock/test_mock_GetLockerRequest.cc
new file mode 100644
index 000000000..4ee0cf0f9
--- /dev/null
+++ b/src/test/librbd/managed_lock/test_mock_GetLockerRequest.cc
@@ -0,0 +1,309 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "cls/lock/cls_lock_ops.h"
+#include "librbd/managed_lock/GetLockerRequest.h"
+#include "librbd/managed_lock/Types.h"
+#include "librbd/managed_lock/Utils.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/managed_lock/GetLockerRequest.cc"
+
+namespace librbd {
+namespace managed_lock {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockManagedLockGetLockerRequest : public TestMockFixture {
+public:
+ typedef GetLockerRequest<MockTestImageCtx> MockGetLockerRequest;
+
+ void expect_get_lock_info(MockTestImageCtx &mock_image_ctx, int r,
+ const entity_name_t &locker_entity,
+ const std::string &locker_address,
+ const std::string &locker_cookie,
+ const std::string &lock_tag,
+ ClsLockType lock_type) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("lock"),
+ StrEq("get_info"), _, _, _, _));
+ if (r < 0 && r != -ENOENT) {
+ expect.WillOnce(Return(r));
+ } else {
+ entity_name_t entity(locker_entity);
+ entity_addr_t entity_addr;
+ entity_addr.parse(locker_address.c_str(), NULL);
+
+ cls_lock_get_info_reply reply;
+ if (r != -ENOENT) {
+ reply.lockers.emplace(
+ rados::cls::lock::locker_id_t(entity, locker_cookie),
+ rados::cls::lock::locker_info_t(utime_t(), entity_addr, ""));
+ reply.tag = lock_tag;
+ reply.lock_type = lock_type;
+ }
+
+ bufferlist bl;
+ encode(reply, bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
+
+ std::string str(bl.c_str(), bl.length());
+ expect.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(str)), Return(0)));
+ }
+ }
+};
+
+TEST_F(TestMockManagedLockGetLockerRequest, SuccessExclusive) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
+ "auto 123", util::get_watcher_lock_tag(),
+ ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(entity_name_t::CLIENT(1), locker.entity);
+ ASSERT_EQ("1.2.3.4:0/0", locker.address);
+ ASSERT_EQ("auto 123", locker.cookie);
+ ASSERT_EQ(123U, locker.handle);
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, SuccessShared) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
+ "auto 123", util::get_watcher_lock_tag(),
+ ClsLockType::SHARED);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, false, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(entity_name_t::CLIENT(1), locker.entity);
+ ASSERT_EQ("1.2.3.4:0/0", locker.address);
+ ASSERT_EQ("auto 123", locker.cookie);
+ ASSERT_EQ(123U, locker.handle);
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, -EINVAL, entity_name_t::CLIENT(1), "",
+ "", "", ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoEmpty) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, -ENOENT, entity_name_t::CLIENT(1), "",
+ "", "", ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoExternalTag) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
+ "auto 123", "external tag", ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoIncompatibleShared) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
+ "auto 123", util::get_watcher_lock_tag(),
+ ClsLockType::SHARED);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoIncompatibleExclusive) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
+ "auto 123", util::get_watcher_lock_tag(),
+ ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, false, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoExternalCookie) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
+ "external cookie", util::get_watcher_lock_tag(),
+ ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoEmptyCookie) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
+ "", util::get_watcher_lock_tag(),
+ ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockGetLockerRequest, GetLockInfoBlankAddress) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "",
+ "auto 123", util::get_watcher_lock_tag(),
+ ClsLockType::EXCLUSIVE);
+
+ C_SaferCond ctx;
+ Locker locker;
+ MockGetLockerRequest *req = MockGetLockerRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, true, &locker, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+} // namespace managed_lock
+} // namespace librbd
diff --git a/src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc b/src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc
new file mode 100644
index 000000000..ec63925ac
--- /dev/null
+++ b/src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc
@@ -0,0 +1,123 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "cls/lock/cls_lock_ops.h"
+#include "librbd/managed_lock/ReacquireRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+
+// template definitions
+#include "librbd/managed_lock/ReacquireRequest.cc"
+template class librbd::managed_lock::ReacquireRequest<librbd::MockImageCtx>;
+
+namespace {
+
+MATCHER_P(IsLockType, exclusive, "") {
+ cls_lock_set_cookie_op op;
+ bufferlist bl;
+ bl.share(arg);
+ auto iter = bl.cbegin();
+ decode(op, iter);
+ return op.type == (exclusive ? ClsLockType::EXCLUSIVE : ClsLockType::SHARED);
+}
+
+} // anonymous namespace
+
+namespace librbd {
+namespace managed_lock {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+
+
+class TestMockManagedLockReacquireRequest : public TestMockFixture {
+public:
+ typedef ReacquireRequest<MockImageCtx> MockReacquireRequest;
+
+ void expect_set_cookie(MockImageCtx &mock_image_ctx, int r,
+ bool exclusive = true) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("lock"),
+ StrEq("set_cookie"), IsLockType(exclusive), _, _, _))
+ .WillOnce(Return(r));
+ }
+};
+
+TEST_F(TestMockManagedLockReacquireRequest, SuccessExclusive) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_set_cookie(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockReacquireRequest *req = MockReacquireRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, "old_cookie",
+ "new_cookie", true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockReacquireRequest, SuccessShared) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_set_cookie(mock_image_ctx, 0, false);
+
+ C_SaferCond ctx;
+ MockReacquireRequest *req = MockReacquireRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, "old_cookie",
+ "new_cookie", false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockReacquireRequest, NotSupported) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_set_cookie(mock_image_ctx, -EOPNOTSUPP);
+
+ C_SaferCond ctx;
+ MockReacquireRequest *req = MockReacquireRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, "old_cookie",
+ "new_cookie", true, &ctx);
+ req->send();
+ ASSERT_EQ(-EOPNOTSUPP, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockReacquireRequest, Error) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_set_cookie(mock_image_ctx, -EBUSY);
+
+ C_SaferCond ctx;
+ MockReacquireRequest *req = MockReacquireRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.header_oid, "old_cookie",
+ "new_cookie", true, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+} // namespace managed_lock
+} // namespace librbd
diff --git a/src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc b/src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc
new file mode 100644
index 000000000..63752475e
--- /dev/null
+++ b/src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc
@@ -0,0 +1,91 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/managed_lock/ReleaseRequest.h"
+#include "common/WorkQueue.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <list>
+
+namespace librbd {
+namespace watcher {
+template <>
+struct Traits<MockImageCtx> {
+ typedef librbd::MockImageWatcher Watcher;
+};
+}
+}
+
+// template definitions
+#include "librbd/managed_lock/ReleaseRequest.cc"
+template class librbd::managed_lock::ReleaseRequest<librbd::MockImageCtx>;
+
+namespace librbd {
+namespace managed_lock {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+
+static const std::string TEST_COOKIE("auto 123");
+
+class TestMockManagedLockReleaseRequest : public TestMockFixture {
+public:
+ typedef ReleaseRequest<MockImageCtx> MockReleaseRequest;
+
+ void expect_unlock(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("lock"),
+ StrEq("unlock"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+};
+
+TEST_F(TestMockManagedLockReleaseRequest, Success) {
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ expect_unlock(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockReleaseRequest *req = MockReleaseRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.image_watcher, ictx->op_work_queue,
+ mock_image_ctx.header_oid, TEST_COOKIE, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockManagedLockReleaseRequest, UnlockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ expect_unlock(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ MockReleaseRequest *req = MockReleaseRequest::create(
+ mock_image_ctx.md_ctx, mock_image_ctx.image_watcher, ictx->op_work_queue,
+ mock_image_ctx.header_oid, TEST_COOKIE, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+}
+
+} // namespace managed_lock
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_FileStream.cc b/src/test/librbd/migration/test_mock_FileStream.cc
new file mode 100644
index 000000000..a5bdfebe4
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_FileStream.cc
@@ -0,0 +1,213 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/migration/FileStream.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "json_spirit/json_spirit.h"
+#include <filesystem>
+
+namespace fs = std::filesystem;
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "librbd/migration/FileStream.cc"
+
+namespace librbd {
+namespace migration {
+
+using ::testing::Invoke;
+
+class TestMockMigrationFileStream : public TestMockFixture {
+public:
+ typedef FileStream<MockTestImageCtx> MockFileStream;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+ file_name = fs::temp_directory_path() / "TestMockMigrationFileStream";
+ file_name += stringify(getpid());
+ json_object["file_path"] = file_name;
+ }
+
+ void TearDown() override {
+ fs::remove(file_name);
+ TestMockFixture::TearDown();
+ }
+
+ std::string file_name;
+ json_spirit::mObject json_object;
+};
+
+TEST_F(TestMockMigrationFileStream, OpenClose) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ bufferlist bl;
+ ASSERT_EQ(0, bl.write_file(file_name.c_str()));
+
+ MockFileStream mock_file_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_file_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_file_stream.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationFileStream, GetSize) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(128, '1'));
+ ASSERT_EQ(0, expect_bl.write_file(file_name.c_str()));
+
+ MockFileStream mock_file_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_file_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ uint64_t size;
+ mock_file_stream.get_size(&size, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(128, size);
+
+ C_SaferCond ctx3;
+ mock_file_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationFileStream, Read) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(128, '1'));
+ ASSERT_EQ(0, expect_bl.write_file(file_name.c_str()));
+
+ MockFileStream mock_file_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_file_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ bufferlist bl;
+ mock_file_stream.read({{0, 128}}, &bl, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_file_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationFileStream, SeekRead) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ bufferlist write_bl;
+ write_bl.append(std::string(32, '1'));
+ write_bl.append(std::string(64, '2'));
+ write_bl.append(std::string(16, '3'));
+ ASSERT_EQ(0, write_bl.write_file(file_name.c_str()));
+
+ MockFileStream mock_file_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_file_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ bufferlist bl;
+ mock_file_stream.read({{96, 16}, {32, 64}, {0, 32}}, &bl, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(16, '3'));
+ expect_bl.append(std::string(64, '2'));
+ expect_bl.append(std::string(32, '1'));
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_file_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationFileStream, DNE) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ MockFileStream mock_file_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_file_stream.open(&ctx1);
+ ASSERT_EQ(-ENOENT, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_file_stream.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationFileStream, SeekError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ bufferlist bl;
+ ASSERT_EQ(0, bl.write_file(file_name.c_str()));
+
+ MockFileStream mock_file_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_file_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_file_stream.read({{128, 128}}, &bl, &ctx2);
+ ASSERT_EQ(-ERANGE, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_file_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationFileStream, ShortReadError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(128, '1'));
+ ASSERT_EQ(0, expect_bl.write_file(file_name.c_str()));
+
+ MockFileStream mock_file_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_file_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ bufferlist bl;
+ mock_file_stream.read({{0, 256}}, &bl, &ctx2);
+ ASSERT_EQ(-ERANGE, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_file_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_HttpClient.cc b/src/test/librbd/migration/test_mock_HttpClient.cc
new file mode 100644
index 000000000..f3888755c
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_HttpClient.cc
@@ -0,0 +1,890 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/migration/HttpClient.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include <unistd.h>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/beast/core.hpp>
+#include <boost/beast/http.hpp>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockTestImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/migration/HttpClient.cc"
+
+using EmptyHttpRequest = boost::beast::http::request<
+ boost::beast::http::empty_body>;
+using HttpResponse = boost::beast::http::response<
+ boost::beast::http::string_body>;
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template <typename Body>
+bool operator==(const boost::beast::http::request<Body>& lhs,
+ const boost::beast::http::request<Body>& rhs) {
+ return (lhs.method() == rhs.method() &&
+ lhs.target() == rhs.target());
+}
+
+template <typename Body>
+bool operator==(const boost::beast::http::response<Body>& lhs,
+ const boost::beast::http::response<Body>& rhs) {
+ return (lhs.result() == rhs.result() &&
+ lhs.body() == rhs.body());
+}
+
+} // namespace http
+} // namespace beast
+} // namespace boost
+
+namespace librbd {
+namespace migration {
+
+using ::testing::Invoke;
+
+class TestMockMigrationHttpClient : public TestMockFixture {
+public:
+ typedef HttpClient<MockTestImageCtx> MockHttpClient;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+
+ create_acceptor(false);
+ }
+
+ void TearDown() override {
+ m_acceptor.reset();
+
+ TestMockFixture::TearDown();
+ }
+
+ // if we have a racing where another thread manages to bind and listen the
+ // port picked by this acceptor, try again.
+ static constexpr int MAX_BIND_RETRIES = 60;
+
+ void create_acceptor(bool reuse) {
+ for (int retries = 0;; retries++) {
+ try {
+ m_acceptor.emplace(*m_image_ctx->asio_engine,
+ boost::asio::ip::tcp::endpoint(
+ boost::asio::ip::tcp::v4(), m_server_port), reuse);
+ // yay!
+ break;
+ } catch (const boost::system::system_error& e) {
+ if (retries == MAX_BIND_RETRIES) {
+ throw;
+ }
+ if (e.code() != boost::system::errc::address_in_use) {
+ throw;
+ }
+ }
+ // backoff a little bit
+ sleep(1);
+ }
+ m_server_port = m_acceptor->local_endpoint().port();
+ }
+
+ std::string get_local_url(UrlScheme url_scheme) {
+ std::stringstream sstream;
+ switch (url_scheme) {
+ case URL_SCHEME_HTTP:
+ sstream << "http://127.0.0.1";
+ break;
+ case URL_SCHEME_HTTPS:
+ sstream << "https://localhost";
+ break;
+ default:
+ ceph_assert(false);
+ break;
+ }
+
+ sstream << ":" << m_server_port << "/target";
+ return sstream.str();
+ }
+
+ void client_accept(boost::asio::ip::tcp::socket* socket, bool close,
+ Context* on_connect) {
+ m_acceptor->async_accept(
+ boost::asio::make_strand(m_image_ctx->asio_engine->get_executor()),
+ [socket, close, on_connect]
+ (auto ec, boost::asio::ip::tcp::socket in_socket) {
+ if (close) {
+ in_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
+ } else {
+ ASSERT_FALSE(ec) << "Unexpected error: " << ec;
+ *socket = std::move(in_socket);
+ }
+ on_connect->complete(0);
+ });
+ }
+
+ template <typename Body>
+ void client_read_request(boost::asio::ip::tcp::socket& socket,
+ boost::beast::http::request<Body>& expected_req) {
+ boost::beast::http::request<Body> req;
+ boost::beast::error_code ec;
+ boost::beast::http::read(socket, m_buffer, req, ec);
+ ASSERT_FALSE(ec) << "Unexpected errror: " << ec;
+
+ expected_req.target("/target");
+ ASSERT_EQ(expected_req, req);
+ }
+
+ void client_write_response(boost::asio::ip::tcp::socket& socket,
+ HttpResponse& expected_res) {
+ expected_res.set(boost::beast::http::field::server,
+ BOOST_BEAST_VERSION_STRING);
+ expected_res.set(boost::beast::http::field::content_type, "text/plain");
+ expected_res.content_length(expected_res.body().size());
+ expected_res.prepare_payload();
+
+ boost::beast::error_code ec;
+ boost::beast::http::write(socket, expected_res, ec);
+ ASSERT_FALSE(ec) << "Unexpected errror: " << ec;
+ }
+
+ template <typename Stream>
+ void client_ssl_handshake(Stream& stream, bool ignore_failure,
+ Context* on_handshake) {
+ stream.async_handshake(
+ boost::asio::ssl::stream_base::server,
+ [ignore_failure, on_handshake](auto ec) {
+ ASSERT_FALSE(!ignore_failure && ec) << "Unexpected error: " << ec;
+ on_handshake->complete(-ec.value());
+ });
+ }
+
+ template <typename Stream>
+ void client_ssl_shutdown(Stream& stream, Context* on_shutdown) {
+ stream.async_shutdown(
+ [on_shutdown](auto ec) {
+ ASSERT_FALSE(ec) << "Unexpected error: " << ec;
+ on_shutdown->complete(-ec.value());
+ });
+ }
+
+ void load_server_certificate(boost::asio::ssl::context& ctx) {
+ ctx.set_options(
+ boost::asio::ssl::context::default_workarounds |
+ boost::asio::ssl::context::no_sslv2 |
+ boost::asio::ssl::context::single_dh_use);
+ ctx.use_certificate_chain(
+ boost::asio::buffer(CERTIFICATE.data(), CERTIFICATE.size()));
+ ctx.use_private_key(
+ boost::asio::buffer(KEY.data(), KEY.size()),
+ boost::asio::ssl::context::file_format::pem);
+ ctx.use_tmp_dh(
+ boost::asio::buffer(DH.data(), DH.size()));
+ }
+
+ // dummy self-signed cert for localhost
+ const std::string CERTIFICATE =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDXzCCAkegAwIBAgIUYH6rAaq66LC6yJ3XK1WEMIfmY4cwDQYJKoZIhvcNAQEL\n"
+ "BQAwPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMQ8wDQYDVQQHDAZNY0xlYW4x\n"
+ "EjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMDExMDIyMTM5NTVaFw00ODAzMjAyMTM5\n"
+ "NTVaMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEPMA0GA1UEBwwGTWNMZWFu\n"
+ "MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n"
+ "AoIBAQCeRkyxjP0eNHxzj4/R+Bg/31p7kEjB5d/LYtrzBIYNe+3DN8gdReixEpR5\n"
+ "lgTLDsl8gfk2HRz4cnAiseqYL6GKtw/cFadzLyXTbW4iavmTWiGYw/8RJlKunbhA\n"
+ "hDjM6H99ysLf0NS6t14eK+bEJIW1PiTYRR1U5I4kSIjpCX7+nJVuwMEZ2XBpN3og\n"
+ "nHhv2hZYTdzEkQEyZHz4V/ApfD7rlja5ecd/vJfPJeA8nudnGCh3Uo6f8I9TObAj\n"
+ "8hJdfRiRBvnA4NnkrMrxW9UtVjScnw9Xia11FM/IGJIgMpLQ5dqBw930p6FxMYtn\n"
+ "tRD1AF9sT+YjoCaHv0hXZvBEUEF3AgMBAAGjUzBRMB0GA1UdDgQWBBTQoIiX3+p/\n"
+ "P4Xz2vwERz6pbjPGhzAfBgNVHSMEGDAWgBTQoIiX3+p/P4Xz2vwERz6pbjPGhzAP\n"
+ "BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCVKoYAw+D1qqWRDSh3\n"
+ "2KlKMnT6sySo7XmReGArj8FTKiZUprByj5CfAtaiDSdPOpcg3EazWbdasZbMmSQm\n"
+ "+jpe5WoKnxL9b12lwwUYHrLl6RlrDHVkIVlXLNbJFY5TpfjvZfHpwVAygF3fnbgW\n"
+ "PPuODUNAS5NDwST+t29jBZ/wwU0pyW0CS4K5d3XMGHBc13j2V/FyvmsZ5xfA4U9H\n"
+ "oEnmZ/Qm+FFK/nR40rTAZ37cuv4ysKFtwvatNgTfHGJwaBUkKFdDbcyxt9abCi6x\n"
+ "/K+ScoJtdIeVcfx8Fnc5PNtSpy8bHI3Zy4IEyw4kOqwwI1h37iBafZ2WdQkTxlAx\n"
+ "JIDj\n"
+ "-----END CERTIFICATE-----\n";
+ const std::string KEY =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCeRkyxjP0eNHxz\n"
+ "j4/R+Bg/31p7kEjB5d/LYtrzBIYNe+3DN8gdReixEpR5lgTLDsl8gfk2HRz4cnAi\n"
+ "seqYL6GKtw/cFadzLyXTbW4iavmTWiGYw/8RJlKunbhAhDjM6H99ysLf0NS6t14e\n"
+ "K+bEJIW1PiTYRR1U5I4kSIjpCX7+nJVuwMEZ2XBpN3ognHhv2hZYTdzEkQEyZHz4\n"
+ "V/ApfD7rlja5ecd/vJfPJeA8nudnGCh3Uo6f8I9TObAj8hJdfRiRBvnA4NnkrMrx\n"
+ "W9UtVjScnw9Xia11FM/IGJIgMpLQ5dqBw930p6FxMYtntRD1AF9sT+YjoCaHv0hX\n"
+ "ZvBEUEF3AgMBAAECggEACCaYpoAbPOX5Dr5y6p47KXboIvrgNFQRPVke62rtOF6M\n"
+ "dQQ3YwKJpCzPxp8qKgbd63KKEfZX2peSHMdKzIGPcSRSRcQ7tlvUN9on1M/rgGIg\n"
+ "3swhI5H0qhdnOLNWdX73qdO6S2pmuiLdTvJ11N4IoLfNj/GnPAr1Ivs1ScL6bkQv\n"
+ "UybaNQ/g2lB0tO7vUeVe2W/AqsIb1eQlf2g+SH7xRj2bGQkr4cWTylqfiVoL/Xic\n"
+ "QVTCks3BWaZhYIhTFgvqVhXZpp52O9J+bxsWJItKQrrCBemxwp82xKbiW/KoI9L1\n"
+ "wSnKvxx7Q3RUN5EvXeOpTRR8QIpBoxP3TTeoj+EOMQKBgQDQb/VfLDlLgfYJpgRC\n"
+ "hKCLW90un9op3nA2n9Dmm9TTLYOmUyiv5ub8QDINEw/YK/NE2JsTSUk2msizqTLL\n"
+ "Z82BFbz9kPlDbJ5MgxG5zXeLvOLurAFmZk/z5JJO+65PKjf0QVLncSAJvMCeNFuC\n"
+ "2yZrEzbrItrjQsN6AedWdx6TTwKBgQDCZAsSI3lQgOh2q1GSxjuIzRAc7JnSGBvD\n"
+ "nG8+SkfKAy7BWe638772Dgx8KYO7TLI4zlm8c9Tr/nkZsGWmM5S2DMI69PWOQWNa\n"
+ "R6QzOFFwNg2JETH7ow+x8+9Q9d3WsPzROz3r5uDXgEk0glthaymVoPILFOiYpz3r\n"
+ "heUbd6mFWQKBgQCCJBVJGhyf54IOHij0u0heGrpr/QTDNY5MnNZa1hs4y2cydyOl\n"
+ "SH8aKp7ViPxQlYhriO6ySQS8YkJD4rXDSImIOmFo1Ja9oVjpHsD3iLFGf2YVbTHm\n"
+ "lKUA+8raI8x+wzZyfELeHMTLL534aWpltp0zJ6kXgQi38pyIVh3x36gogwKBgQCt\n"
+ "nba5k49VVFzLKEXqBjzD+QqMGtFjcH7TnZNJmgQ2K9OFgzIPf5atomyKNHXgQibn\n"
+ "T32cMAQaZqR4SjDvWSBX3FtZVtE+Ja57woKn8IPj6ZL7Oa1fpwpskIbM01s31cln\n"
+ "gjbSy9lC/+PiDw9YmeKBLkcfmKQJO021Xlf6yUxRuQKBgBWPODUO8oKjkuJXNI/w\n"
+ "El9hNWkd+/SZDfnt93dlVyXTtTF0M5M95tlOgqvLtWKSyB/BOnoZYWqR8luMl15d\n"
+ "bf75j5mB0lHMWtyQgvZSkFqe9Or7Zy7hfTShDlZ/w+OXK7PGesaE1F14irShXSji\n"
+ "yn5DZYAZ5pU52xreJeYvDngO\n"
+ "-----END PRIVATE KEY-----\n";
+ const std::string DH =
+ "-----BEGIN DH PARAMETERS-----\n"
+ "MIIBCAKCAQEA4+DA1j0gDWS71okwHpnvA65NmmR4mf+B3H39g163zY5S+cnWS2LI\n"
+ "dvqnUDpw13naWtQ+Nu7I4rk1XoPaxOPSTu1MTbtYOxxU9M1ceBu4kQjDeHwasPVM\n"
+ "zyEs1XXX3tsbPUxAuayX+AgW6QQAQUEjKDnv3FzVnQTFjwI49LqjnrSjbgQcoMaH\n"
+ "EdGGUc6t1/We2vtsJZx0/dbaMkzFYO8dAbEYHL4sPKQb2mLpCPJZC3vwzpFkHFCd\n"
+ "QSnLW2qRhy+66Mf8shdr6uvpoMcnKMOAvjKdXl9PBeJM9eJPz2lC4tnTiM3DqNzK\n"
+ "Hn8+Pu3KkSIFL/5uBVu1fZSq+lFIEI23wwIBAg==\n"
+ "-----END DH PARAMETERS-----\n";
+
+ librbd::ImageCtx *m_image_ctx;
+
+ std::optional<boost::asio::ip::tcp::acceptor> m_acceptor;
+ boost::beast::flat_buffer m_buffer;
+ uint64_t m_server_port = 0;
+};
+
+TEST_F(TestMockMigrationHttpClient, OpenCloseHttp) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ http_client.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, OpenCloseHttps) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTPS));
+ http_client.set_ignore_self_signed_cert(true);
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+
+ boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tlsv12};
+ load_server_certificate(ssl_context);
+ boost::beast::ssl_stream<boost::beast::tcp_stream> ssl_stream{
+ std::move(socket), ssl_context};
+
+ C_SaferCond on_ssl_handshake_ctx;
+ client_ssl_handshake(ssl_stream, false, &on_ssl_handshake_ctx);
+ ASSERT_EQ(0, on_ssl_handshake_ctx.wait());
+
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ http_client.close(&ctx2);
+
+ C_SaferCond on_ssl_shutdown_ctx;
+ client_ssl_shutdown(ssl_stream, &on_ssl_shutdown_ctx);
+ ASSERT_EQ(0, on_ssl_shutdown_ctx.wait());
+
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, OpenHttpsHandshakeFail) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTPS));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+
+ boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tlsv12};
+ load_server_certificate(ssl_context);
+ boost::beast::ssl_stream<boost::beast::tcp_stream> ssl_stream{
+ std::move(socket), ssl_context};
+
+ C_SaferCond on_ssl_handshake_ctx;
+ client_ssl_handshake(ssl_stream, true, &on_ssl_handshake_ctx);
+ ASSERT_NE(0, on_ssl_handshake_ctx.wait());
+ ASSERT_NE(0, ctx1.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, OpenInvalidUrl) {
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx, "ftp://nope/");
+
+ C_SaferCond ctx;
+ http_client.open(&ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, OpenResolveFail) {
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx, "http://foo.example");
+
+ C_SaferCond ctx;
+ http_client.open(&ctx);
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, OpenConnectFail) {
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ "http://localhost:2/");
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(-ECONNREFUSED, ctx1.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, IssueHead) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ EmptyHttpRequest req;
+ req.method(boost::beast::http::verb::head);
+
+ C_SaferCond ctx2;
+ HttpResponse res;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx2, &res](int r, HttpResponse&& response) mutable {
+ res = std::move(response);
+ ctx2.complete(r);
+ });
+
+ HttpResponse expected_res;
+ client_read_request(socket, req);
+ client_write_response(socket, expected_res);
+
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(expected_res, res);
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, IssueGet) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ EmptyHttpRequest req;
+ req.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx2;
+ HttpResponse res;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx2, &res](int r, HttpResponse&& response) mutable {
+ res = std::move(response);
+ ctx2.complete(r);
+ });
+
+ HttpResponse expected_res;
+ expected_res.body() = "test";
+ client_read_request(socket, req);
+ client_write_response(socket, expected_res);
+
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(expected_res, res);
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, IssueSendFailed) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx1;
+ client_accept(&socket, false, &on_connect_ctx1);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx1.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ // close connection to client
+ boost::system::error_code ec;
+ socket.close(ec);
+
+ C_SaferCond on_connect_ctx2;
+ client_accept(&socket, false, &on_connect_ctx2);
+
+ // send request via closed connection
+ EmptyHttpRequest req;
+ req.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx2;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx2](int r, HttpResponse&&) mutable {
+ ctx2.complete(r);
+ });
+
+ // connection will be reset and request retried
+ ASSERT_EQ(0, on_connect_ctx2.wait());
+ HttpResponse expected_res;
+ expected_res.body() = "test";
+ client_read_request(socket, req);
+ client_write_response(socket, expected_res);
+ ASSERT_EQ(0, ctx2.wait());
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, IssueReceiveFailed) {
+ boost::asio::ip::tcp::socket socket1(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx1;
+ client_accept(&socket1, false, &on_connect_ctx1);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx1.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ // send request via closed connection
+ EmptyHttpRequest req;
+ req.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx2;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx2](int r, HttpResponse&&) mutable {
+ ctx2.complete(r);
+ });
+
+ // close connection to client after reading request
+ client_read_request(socket1, req);
+
+ C_SaferCond on_connect_ctx2;
+ boost::asio::ip::tcp::socket socket2(*m_image_ctx->asio_engine);
+ client_accept(&socket2, false, &on_connect_ctx2);
+
+ boost::system::error_code ec;
+ socket1.close(ec);
+ ASSERT_EQ(0, on_connect_ctx2.wait());
+
+ // connection will be reset and request retried
+ HttpResponse expected_res;
+ expected_res.body() = "test";
+ client_read_request(socket2, req);
+ client_write_response(socket2, expected_res);
+ ASSERT_EQ(0, ctx2.wait());
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, IssueResetFailed) {
+ m_server_port = 0;
+ create_acceptor(true);
+
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx1;
+ client_accept(&socket, false, &on_connect_ctx1);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx1.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ // send requests then close connection
+ EmptyHttpRequest req;
+ req.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx2;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx2](int r, HttpResponse&&) mutable {
+ ctx2.complete(r);
+ });
+
+ C_SaferCond ctx3;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx3](int r, HttpResponse&&) mutable {
+ ctx3.complete(r);
+ });
+
+ client_read_request(socket, req);
+ client_read_request(socket, req);
+
+ // close connection to client and verify requests are failed
+ m_acceptor.reset();
+ boost::system::error_code ec;
+ socket.close(ec);
+
+ ASSERT_EQ(-ECONNREFUSED, ctx2.wait());
+ ASSERT_EQ(-ECONNREFUSED, ctx3.wait());
+
+ // additional request will retry the failed connection
+ create_acceptor(true);
+
+ C_SaferCond on_connect_ctx2;
+ client_accept(&socket, false, &on_connect_ctx2);
+
+ C_SaferCond ctx4;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx4](int r, HttpResponse&&) mutable {
+ ctx4.complete(r);
+ });
+
+ ASSERT_EQ(0, on_connect_ctx2.wait());
+ client_read_request(socket, req);
+
+ HttpResponse expected_res;
+ expected_res.body() = "test";
+ client_write_response(socket, expected_res);
+ ASSERT_EQ(0, ctx4.wait());
+
+ C_SaferCond ctx5;
+ http_client.close(&ctx5);
+ ASSERT_EQ(0, ctx5.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, IssuePipelined) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ // issue two pipelined (concurrent) get requests
+ EmptyHttpRequest req1;
+ req1.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx2;
+ HttpResponse res1;
+ http_client.issue(EmptyHttpRequest{req1},
+ [&ctx2, &res1](int r, HttpResponse&& response) mutable {
+ res1 = std::move(response);
+ ctx2.complete(r);
+ });
+
+ EmptyHttpRequest req2;
+ req2.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx3;
+ HttpResponse res2;
+ http_client.issue(EmptyHttpRequest{req2},
+ [&ctx3, &res2](int r, HttpResponse&& response) mutable {
+ res2 = std::move(response);
+ ctx3.complete(r);
+ });
+
+ client_read_request(socket, req1);
+ client_read_request(socket, req2);
+
+ // read the responses sequentially
+ HttpResponse expected_res1;
+ expected_res1.body() = "test";
+ client_write_response(socket, expected_res1);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(expected_res1, res1);
+
+ HttpResponse expected_res2;
+ expected_res2.body() = "test";
+ client_write_response(socket, expected_res2);
+ ASSERT_EQ(0, ctx3.wait());
+ ASSERT_EQ(expected_res2, res2);
+
+ C_SaferCond ctx4;
+ http_client.close(&ctx4);
+ ASSERT_EQ(0, ctx4.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, IssuePipelinedRestart) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx1;
+ client_accept(&socket, false, &on_connect_ctx1);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx1.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ // issue two pipelined (concurrent) get requests
+ EmptyHttpRequest req1;
+ req1.keep_alive(false);
+ req1.method(boost::beast::http::verb::get);
+
+ C_SaferCond on_connect_ctx2;
+ client_accept(&socket, false, &on_connect_ctx2);
+
+ C_SaferCond ctx2;
+ HttpResponse res1;
+ http_client.issue(EmptyHttpRequest{req1},
+ [&ctx2, &res1](int r, HttpResponse&& response) mutable {
+ res1 = std::move(response);
+ ctx2.complete(r);
+ });
+
+ EmptyHttpRequest req2;
+ req2.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx3;
+ HttpResponse res2;
+ http_client.issue(EmptyHttpRequest{req2},
+ [&ctx3, &res2](int r, HttpResponse&& response) mutable {
+ res2 = std::move(response);
+ ctx3.complete(r);
+ });
+
+ client_read_request(socket, req1);
+ client_read_request(socket, req2);
+
+ // read the responses sequentially
+ HttpResponse expected_res1;
+ expected_res1.body() = "test";
+ expected_res1.keep_alive(false);
+ client_write_response(socket, expected_res1);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(expected_res1, res1);
+
+ // second request will need to be re-sent due to 'need_eof' condition
+ ASSERT_EQ(0, on_connect_ctx2.wait());
+ client_read_request(socket, req2);
+
+ HttpResponse expected_res2;
+ expected_res2.body() = "test";
+ client_write_response(socket, expected_res2);
+ ASSERT_EQ(0, ctx3.wait());
+ ASSERT_EQ(expected_res2, res2);
+
+ C_SaferCond ctx4;
+ http_client.close(&ctx4);
+ ASSERT_EQ(0, ctx4.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, ShutdownInFlight) {
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ EmptyHttpRequest req;
+ req.method(boost::beast::http::verb::get);
+
+ C_SaferCond ctx2;
+ http_client.issue(EmptyHttpRequest{req},
+ [&ctx2](int r, HttpResponse&&) mutable {
+ ctx2.complete(r);
+ });
+
+ client_read_request(socket, req);
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+ ASSERT_EQ(-ESHUTDOWN, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, GetSize) {
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ uint64_t size = 0;
+ C_SaferCond ctx2;
+ http_client.get_size(&size, &ctx2);
+
+ EmptyHttpRequest expected_req;
+ expected_req.method(boost::beast::http::verb::head);
+ client_read_request(socket, expected_req);
+
+ HttpResponse expected_res;
+ expected_res.body() = std::string(123, '1');
+ client_write_response(socket, expected_res);
+
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(123, size);
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, GetSizeError) {
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ uint64_t size = 0;
+ C_SaferCond ctx2;
+ http_client.get_size(&size, &ctx2);
+
+ EmptyHttpRequest expected_req;
+ expected_req.method(boost::beast::http::verb::head);
+ client_read_request(socket, expected_req);
+
+ HttpResponse expected_res;
+ expected_res.result(boost::beast::http::status::internal_server_error);
+ client_write_response(socket, expected_res);
+
+ ASSERT_EQ(-EIO, ctx2.wait());
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationHttpClient, Read) {
+ MockTestImageCtx mock_test_image_ctx(*m_image_ctx);
+ MockHttpClient http_client(&mock_test_image_ctx,
+ get_local_url(URL_SCHEME_HTTP));
+
+ boost::asio::ip::tcp::socket socket(*m_image_ctx->asio_engine);
+ C_SaferCond on_connect_ctx;
+ client_accept(&socket, false, &on_connect_ctx);
+
+ C_SaferCond ctx1;
+ http_client.open(&ctx1);
+ ASSERT_EQ(0, on_connect_ctx.wait());
+ ASSERT_EQ(0, ctx1.wait());
+
+ bufferlist bl;
+ C_SaferCond ctx2;
+ http_client.read({{0, 128}, {256, 64}}, &bl, &ctx2);
+
+ EmptyHttpRequest expected_req1;
+ expected_req1.method(boost::beast::http::verb::get);
+ expected_req1.set(boost::beast::http::field::range, "bytes=0-127");
+ client_read_request(socket, expected_req1);
+
+ EmptyHttpRequest expected_req2;
+ expected_req2.method(boost::beast::http::verb::get);
+ expected_req2.set(boost::beast::http::field::range, "bytes=256-319");
+ client_read_request(socket, expected_req2);
+
+ HttpResponse expected_res1;
+ expected_res1.result(boost::beast::http::status::partial_content);
+ expected_res1.body() = std::string(128, '1');
+ client_write_response(socket, expected_res1);
+
+ HttpResponse expected_res2;
+ expected_res2.result(boost::beast::http::status::partial_content);
+ expected_res2.body() = std::string(64, '2');
+ client_write_response(socket, expected_res2);
+
+ ASSERT_EQ(192, ctx2.wait());
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(128, '1'));
+ expect_bl.append(std::string(64, '2'));
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ http_client.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_HttpStream.cc b/src/test/librbd/migration/test_mock_HttpStream.cc
new file mode 100644
index 000000000..aff22b757
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_HttpStream.cc
@@ -0,0 +1,194 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/migration/HttpClient.h"
+#include "librbd/migration/HttpStream.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "json_spirit/json_spirit.h"
+#include <boost/beast/http.hpp>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace migration {
+
+template <>
+struct HttpClient<MockTestImageCtx> {
+ static HttpClient* s_instance;
+ static HttpClient* create(MockTestImageCtx*, const std::string&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MOCK_METHOD1(open, void(Context*));
+ MOCK_METHOD1(close, void(Context*));
+ MOCK_METHOD2(get_size, void(uint64_t*, Context*));
+ MOCK_METHOD3(do_read, void(const io::Extents&, bufferlist*, Context*));
+ void read(io::Extents&& extents, bufferlist* bl, Context* ctx) {
+ do_read(extents, bl, ctx);
+ }
+
+ HttpClient() {
+ s_instance = this;
+ }
+};
+
+HttpClient<MockTestImageCtx>* HttpClient<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace migration
+} // namespace librbd
+
+#include "librbd/migration/HttpStream.cc"
+
+namespace librbd {
+namespace migration {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::WithArgs;
+
+class TestMockMigrationHttpStream : public TestMockFixture {
+public:
+ typedef HttpStream<MockTestImageCtx> MockHttpStream;
+ typedef HttpClient<MockTestImageCtx> MockHttpClient;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+ json_object["url"] = "http://some.site/file";
+ }
+
+ void expect_open(MockHttpClient& mock_http_client, int r) {
+ EXPECT_CALL(mock_http_client, open(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_close(MockHttpClient& mock_http_client, int r) {
+ EXPECT_CALL(mock_http_client, close(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_get_size(MockHttpClient& mock_http_client, uint64_t size, int r) {
+ EXPECT_CALL(mock_http_client, get_size(_, _))
+ .WillOnce(Invoke([size, r](uint64_t* out_size, Context* ctx) {
+ *out_size = size;
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_read(MockHttpClient& mock_http_client, io::Extents byte_extents,
+ const bufferlist& bl, int r) {
+ uint64_t len = 0;
+ for (auto [_, byte_len] : byte_extents) {
+ len += byte_len;
+ }
+ EXPECT_CALL(mock_http_client, do_read(byte_extents, _, _))
+ .WillOnce(WithArgs<1, 2>(Invoke(
+ [len, bl, r](bufferlist* out_bl, Context* ctx) {
+ *out_bl = bl;
+ ctx->complete(r < 0 ? r : len);
+ })));
+ }
+
+ json_spirit::mObject json_object;
+};
+
+TEST_F(TestMockMigrationHttpStream, OpenClose) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ auto mock_http_client = new MockHttpClient();
+ expect_open(*mock_http_client, 0);
+
+ expect_close(*mock_http_client, 0);
+
+ MockHttpStream mock_http_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_http_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_http_stream.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationHttpStream, GetSize) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ auto mock_http_client = new MockHttpClient();
+ expect_open(*mock_http_client, 0);
+
+ expect_get_size(*mock_http_client, 128, 0);
+
+ expect_close(*mock_http_client, 0);
+
+ MockHttpStream mock_http_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_http_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ uint64_t size;
+ mock_http_stream.get_size(&size, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(128, size);
+
+ C_SaferCond ctx3;
+ mock_http_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationHttpStream, Read) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ auto mock_http_client = new MockHttpClient();
+ expect_open(*mock_http_client, 0);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(192, '1'));
+ expect_read(*mock_http_client, {{0, 128}, {256, 64}}, expect_bl, 0);
+
+ expect_close(*mock_http_client, 0);
+
+ MockHttpStream mock_http_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_http_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ bufferlist bl;
+ mock_http_stream.read({{0, 128}, {256, 64}}, &bl, &ctx2);
+ ASSERT_EQ(192, ctx2.wait());
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_http_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_QCOWFormat.cc b/src/test/librbd/migration/test_mock_QCOWFormat.cc
new file mode 100644
index 000000000..6e7225d22
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_QCOWFormat.cc
@@ -0,0 +1,1259 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/migration/MockStreamInterface.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/migration/QCOWFormat.h"
+#include "librbd/migration/SourceSpecBuilder.h"
+#include "acconfig.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "json_spirit/json_spirit.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace migration {
+
+template<>
+struct SourceSpecBuilder<librbd::MockTestImageCtx> {
+
+ MOCK_CONST_METHOD2(build_stream, int(const json_spirit::mObject&,
+ std::shared_ptr<StreamInterface>*));
+
+};
+
+} // namespace migration
+
+bool operator==(const SnapInfo& lhs, const SnapInfo& rhs) {
+ return (lhs.name == rhs.name &&
+ lhs.size == rhs.size);
+}
+
+} // namespace librbd
+
+#include "librbd/migration/QCOWFormat.cc"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::ReturnRef;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+namespace librbd {
+namespace migration {
+
+using ::testing::Invoke;
+
+class TestMockMigrationQCOWFormat : public TestMockFixture {
+public:
+ typedef QCOWFormat<MockTestImageCtx> MockQCOWFormat;
+ typedef SourceSpecBuilder<MockTestImageCtx> MockSourceSpecBuilder;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+
+ json_spirit::mObject stream_obj;
+ stream_obj["type"] = "file";
+ json_object["stream"] = stream_obj;
+ }
+
+ void expect_build_stream(MockSourceSpecBuilder& mock_source_spec_builder,
+ MockStreamInterface* mock_stream_interface, int r) {
+ EXPECT_CALL(mock_source_spec_builder, build_stream(_, _))
+ .WillOnce(WithArgs<1>(Invoke([mock_stream_interface, r]
+ (std::shared_ptr<StreamInterface>* ptr) {
+ ptr->reset(mock_stream_interface);
+ return r;
+ })));
+ }
+
+ void expect_stream_open(MockStreamInterface& mock_stream_interface, int r) {
+ EXPECT_CALL(mock_stream_interface, open(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_stream_close(MockStreamInterface& mock_stream_interface, int r) {
+ EXPECT_CALL(mock_stream_interface, close(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_stream_read(MockStreamInterface& mock_stream_interface,
+ const io::Extents& byte_extents,
+ const bufferlist& bl, int r) {
+ EXPECT_CALL(mock_stream_interface, read(byte_extents, _, _))
+ .WillOnce(WithArgs<1, 2>(Invoke([bl, r]
+ (bufferlist* out_bl, Context* ctx) {
+ *out_bl = bl;
+ ctx->complete(r);
+ })));
+ }
+
+ void expect_probe_header(MockStreamInterface& mock_stream_interface,
+ uint32_t magic, uint32_t version, int r) {
+ magic = htobe32(magic);
+ version = htobe32(version);
+
+ bufferlist probe_bl;
+ probe_bl.append(std::string_view(reinterpret_cast<char*>(&magic), 4));
+ probe_bl.append(std::string_view(reinterpret_cast<char*>(&version), 4));
+ expect_stream_read(mock_stream_interface, {{0, 8}}, probe_bl, r);
+ }
+
+ void expect_read_header(MockStreamInterface& mock_stream_interface,
+ uint32_t snapshot_count, int r) {
+ QCowHeader qcow_header;
+ memset(&qcow_header, 0, sizeof(qcow_header));
+ qcow_header.magic = htobe32(QCOW_MAGIC);
+ qcow_header.version = htobe32(2);
+ qcow_header.size = htobe64(1<<30);
+ qcow_header.cluster_bits = htobe32(16);
+ qcow_header.l1_size = htobe32(2);
+ qcow_header.l1_table_offset = htobe64(1<<20);
+ if (snapshot_count > 0) {
+ qcow_header.nb_snapshots = htobe32(snapshot_count);
+ qcow_header.snapshots_offset = htobe64(1<<21);
+ }
+
+ bufferlist header_bl;
+ header_bl.append(std::string_view(reinterpret_cast<char*>(&qcow_header),
+ sizeof(qcow_header)));
+ expect_stream_read(mock_stream_interface, {{0, sizeof(qcow_header)}},
+ header_bl, r);
+ }
+
+ void expect_read_l1_table(MockStreamInterface& mock_stream_interface,
+ std::optional<std::vector<uint64_t>>&& l1_table,
+ int r) {
+ bufferlist l1_table_bl;
+ if (!l1_table) {
+ l1_table.emplace(2);
+ }
+
+ l1_table->resize(2);
+ for (size_t idx = 0; idx < l1_table->size(); ++idx) {
+ (*l1_table)[idx] = htobe64((*l1_table)[idx]);
+ }
+
+ l1_table_bl.append(
+ std::string_view(reinterpret_cast<char*>(l1_table->data()), 16));
+ expect_stream_read(mock_stream_interface, {{1<<20, 16}}, l1_table_bl, r);
+ }
+
+ void expect_read_l2_table(MockStreamInterface& mock_stream_interface,
+ uint64_t l2_table_offset,
+ std::optional<std::vector<uint64_t>>&& l2_table,
+ int r) {
+ size_t l2_table_size = 1<<(16 - 3); // cluster_bit - 3 bits for offset
+ bufferlist l2_table_bl;
+ if (!l2_table) {
+ l2_table.emplace(l2_table_size);
+ }
+
+ l2_table->resize(l2_table_size);
+ for (size_t idx = 0; idx < l2_table->size(); ++idx) {
+ (*l2_table)[idx] = htobe64((*l2_table)[idx]);
+ }
+
+ l2_table_bl.append(
+ std::string_view(reinterpret_cast<char*>(l2_table->data()),
+ l2_table->size() * sizeof(uint64_t)));
+ expect_stream_read(mock_stream_interface,
+ {{l2_table_offset, l2_table->size() * sizeof(uint64_t)}},
+ l2_table_bl, r);
+ }
+
+ void expect_read_cluster(MockStreamInterface& mock_stream_interface,
+ uint64_t cluster_offset, uint32_t offset,
+ const bufferlist& bl, int r) {
+ uint32_t cluster_size = 1<<16;
+
+ bufferlist cluster_bl;
+ if (offset > 0) {
+ cluster_size -= offset;
+ cluster_bl.append_zero(offset);
+ }
+
+ cluster_size -= bl.length();
+ cluster_bl.append(bl);
+
+ if (cluster_size > 0) {
+ cluster_bl.append_zero(cluster_size);
+ }
+
+ expect_stream_read(mock_stream_interface,
+ {{cluster_offset, cluster_bl.length()}}, cluster_bl, r);
+ }
+
+ void expect_open(MockStreamInterface& mock_stream_interface,
+ std::optional<std::vector<uint64_t>>&& l1_table) {
+ expect_stream_open(mock_stream_interface, 0);
+
+ expect_probe_header(mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(mock_stream_interface, 0, 0);
+
+ expect_read_l1_table(mock_stream_interface, std::move(l1_table), 0);
+ }
+
+ void expect_read_snapshot_header(MockStreamInterface& mock_stream_interface,
+ const std::string& id,
+ const std::string& name,
+ uint64_t l1_table_offset,
+ uint64_t* snapshot_offset, int r) {
+ QCowSnapshotHeader snapshot_header;
+ memset(&snapshot_header, 0, sizeof(snapshot_header));
+ snapshot_header.id_str_size = htobe16(id.size());
+ snapshot_header.name_size = htobe16(name.size());
+ snapshot_header.extra_data_size = htobe32(16);
+ snapshot_header.l1_size = htobe32(1);
+ snapshot_header.l1_table_offset = htobe64(l1_table_offset);
+
+ bufferlist snapshot_header_bl;
+ snapshot_header_bl.append(
+ std::string_view(reinterpret_cast<char*>(&snapshot_header),
+ sizeof(snapshot_header)));
+ expect_stream_read(mock_stream_interface,
+ {{*snapshot_offset, sizeof(snapshot_header)}},
+ snapshot_header_bl, r);
+ *snapshot_offset += sizeof(snapshot_header);
+ }
+
+ void expect_read_snapshot_header_extra(
+ MockStreamInterface& mock_stream_interface,
+ const std::string& id, const std::string& name, uint64_t size,
+ uint64_t* snapshot_offset, int r) {
+ QCowSnapshotExtraData snapshot_header_extra;
+ memset(&snapshot_header_extra, 0, sizeof(snapshot_header_extra));
+ snapshot_header_extra.disk_size = htobe64(size);
+
+ bufferlist snapshot_header_extra_bl;
+ snapshot_header_extra_bl.append(
+ std::string_view(reinterpret_cast<char*>(&snapshot_header_extra), 16));
+ snapshot_header_extra_bl.append(id);
+ snapshot_header_extra_bl.append(name);
+ expect_stream_read(mock_stream_interface,
+ {{*snapshot_offset, 16 + id.size() + name.size()}},
+ snapshot_header_extra_bl, r);
+
+ *snapshot_offset += 16 + id.size() + name.size();
+ *snapshot_offset = p2roundup(*snapshot_offset, static_cast<uint64_t>(8));
+ }
+
+ void expect_read_snapshot_l1_table(MockStreamInterface& mock_stream_interface,
+ uint64_t l1_table_offset, int r) {
+ uint64_t l2_table_cluster = htobe64(l1_table_offset);
+
+ bufferlist snapshot_l1_table_bl;
+ snapshot_l1_table_bl.append(
+ std::string_view(reinterpret_cast<char*>(&l2_table_cluster), 8));
+ expect_stream_read(mock_stream_interface, {{l1_table_offset, 8}},
+ snapshot_l1_table_bl, r);
+ }
+
+
+ void expect_read_snapshot(MockStreamInterface& mock_stream_interface,
+ const std::string& id, const std::string& name,
+ uint64_t size, uint64_t l1_table_offset,
+ uint64_t* snapshot_offset) {
+ expect_read_snapshot_header(mock_stream_interface, id, name,
+ l1_table_offset, snapshot_offset, 0);
+ expect_read_snapshot_header_extra(mock_stream_interface, id, name,
+ size, snapshot_offset, 0);
+ expect_read_snapshot_l1_table(mock_stream_interface, l1_table_offset, 0);
+ }
+
+ json_spirit::mObject json_object;
+};
+
+TEST_F(TestMockMigrationQCOWFormat, OpenCloseV1) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ int expected_open_ret_val = 0;
+ QCowHeaderV1 qcow_header;
+ memset(&qcow_header, 0, sizeof(qcow_header));
+ qcow_header.magic = htobe32(QCOW_MAGIC);
+ qcow_header.version = htobe32(1);
+ qcow_header.size = htobe64(1<<30);
+ qcow_header.l1_table_offset = htobe64(1<<20);
+ qcow_header.cluster_bits = 16;
+ qcow_header.l2_bits = 13;
+
+ bufferlist probe_bl;
+ probe_bl.append(std::string_view(reinterpret_cast<char*>(&qcow_header), 8));
+ expect_stream_read(*mock_stream_interface, {{0, 8}}, probe_bl, 0);
+
+#ifdef WITH_RBD_MIGRATION_FORMAT_QCOW_V1
+
+ bufferlist header_bl;
+ header_bl.append(std::string_view(reinterpret_cast<char*>(&qcow_header),
+ sizeof(qcow_header)));
+ expect_stream_read(*mock_stream_interface, {{0, sizeof(qcow_header)}},
+ header_bl, 0);
+
+ bufferlist l1_table_bl;
+ l1_table_bl.append_zero(16);
+ expect_stream_read(*mock_stream_interface, {{1<<20, 16}}, l1_table_bl, 0);
+
+#else // WITH_RBD_MIGRATION_FORMAT_QCOW_V1
+
+ expected_open_ret_val = -ENOTSUP;
+
+#endif // WITH_RBD_MIGRATION_FORMAT_QCOW_V1
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(expected_open_ret_val, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, OpenCloseV2) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {});
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ProbeInvalidMagic) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, 0, 2, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EINVAL, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ProbeInvalidVersion) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 0, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EINVAL, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ProbeError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, -EIO);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EIO, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+#ifdef WITH_RBD_MIGRATION_FORMAT_QCOW_V1
+
+TEST_F(TestMockMigrationQCOWFormat, ReadHeaderV1Error) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 1, 0);
+
+ QCowHeaderV1 qcow_header;
+ memset(&qcow_header, 0, sizeof(qcow_header));
+ qcow_header.magic = htobe32(QCOW_MAGIC);
+ qcow_header.version = htobe32(1);
+ qcow_header.size = htobe64(1<<30);
+ qcow_header.l1_table_offset = htobe64(1<<20);
+ qcow_header.cluster_bits = 16;
+ qcow_header.l2_bits = 13;
+
+ bufferlist header_bl;
+ header_bl.append(std::string_view(reinterpret_cast<char*>(&qcow_header),
+ sizeof(qcow_header)));
+ expect_stream_read(*mock_stream_interface, {{0, sizeof(qcow_header)}},
+ header_bl, -EPERM);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EPERM, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+#endif // WITH_RBD_MIGRATION_FORMAT_QCOW_V1
+
+TEST_F(TestMockMigrationQCOWFormat, ReadHeaderV2Error) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 0, -EIO);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EIO, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadL1TableError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 0, 0);
+
+ expect_read_l1_table(*mock_stream_interface, {}, -EPERM);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EPERM, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, Snapshots) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 2, 0);
+
+ uint64_t snapshot_offset = 1<<21;
+ expect_read_snapshot(*mock_stream_interface, "1", "snap1", 1<<29, 1<<22,
+ &snapshot_offset);
+ expect_read_snapshot(*mock_stream_interface, "2", "snap2", 1<<29, 1<<22,
+ &snapshot_offset);
+
+ expect_read_l1_table(*mock_stream_interface, {}, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ FormatInterface::SnapInfos snap_infos;
+ C_SaferCond ctx2;
+ mock_qcow_format.get_snapshots(&snap_infos, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ FormatInterface::SnapInfos expected_snap_infos{
+ {1, {"snap1", cls::rbd::UserSnapshotNamespace{}, 1<<29, {}, 0, 0, {}}},
+ {2, {"snap2", cls::rbd::UserSnapshotNamespace{}, 1<<29, {}, 0, 0, {}}}};
+ ASSERT_EQ(expected_snap_infos, snap_infos);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, SnapshotHeaderError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 2, 0);
+
+ uint64_t snapshot_offset = 1<<21;
+ expect_read_snapshot_header(*mock_stream_interface, "1", "snap1",
+ 1<<22, &snapshot_offset, -EINVAL);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EINVAL, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, SnapshotExtraError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 2, 0);
+
+ uint64_t snapshot_offset = 1<<21;
+ expect_read_snapshot_header(*mock_stream_interface, "1", "snap1",
+ 1<<22, &snapshot_offset, 0);
+ expect_read_snapshot_header_extra(*mock_stream_interface, "1", "snap1",
+ 1<<29, &snapshot_offset, -EINVAL);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EINVAL, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, SnapshotL1TableError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 2, 0);
+
+ uint64_t snapshot_offset = 1<<21;
+ expect_read_snapshot_header(*mock_stream_interface, "1", "snap1",
+ 1<<22, &snapshot_offset, 0);
+ expect_read_snapshot_header_extra(*mock_stream_interface, "1", "snap1",
+ 1<<29, &snapshot_offset, 0);
+ expect_read_snapshot_l1_table(*mock_stream_interface, 1<<22, -EINVAL);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(-EINVAL, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_qcow_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, GetImageSize) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {});
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ uint64_t size = 0;
+ C_SaferCond ctx2;
+ mock_qcow_format.get_image_size(CEPH_NOSNAP, &size, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(1<<30, size);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, GetImageSizeSnap) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 1, 0);
+
+ uint64_t snapshot_offset = 1<<21;
+ expect_read_snapshot(*mock_stream_interface, "1", "snap1", 1<<29, 1<<22,
+ &snapshot_offset);
+
+ expect_read_l1_table(*mock_stream_interface, {}, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ uint64_t size = 0;
+ C_SaferCond ctx2;
+ mock_qcow_format.get_image_size(1U, &size, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(1<<29, size);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, GetImageSizeSnapDNE) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {});
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ uint64_t size = 0;
+ C_SaferCond ctx2;
+ mock_qcow_format.get_image_size(1U, &size, &ctx2);
+ ASSERT_EQ(-ENOENT, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, Read) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {{1ULL<<40}});
+
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<40,
+ {{0, 1ULL<<32}}, 0);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(123, '1'));
+ expect_read_cluster(*mock_stream_interface, 1ULL<<32, 123, expect_bl, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, CEPH_NOSNAP, {{65659, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(123, ctx2.wait());
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadL1DNE) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {});
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, CEPH_NOSNAP, {{234, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(123, ctx2.wait());
+ bufferlist expect_bl;
+ expect_bl.append_zero(123);
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadL2DNE) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {{1ULL<<40}});
+
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<40,
+ {{0, 1ULL<<32}}, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, CEPH_NOSNAP, {{234, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(123, ctx2.wait());
+ bufferlist expect_bl;
+ expect_bl.append_zero(123);
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadZero) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {{1ULL<<40}});
+
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<40,
+ {{0, QCOW_OFLAG_ZERO}}, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, CEPH_NOSNAP, {{65659, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(123, ctx2.wait());
+ bufferlist expect_bl;
+ expect_bl.append_zero(123);
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadSnap) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 1, 0);
+
+ uint64_t snapshot_offset = 1<<21;
+ expect_read_snapshot(*mock_stream_interface, "1", "snap1", 1<<29, 1<<22,
+ &snapshot_offset);
+
+ expect_read_l1_table(*mock_stream_interface, {}, 0);
+
+ expect_read_l2_table(*mock_stream_interface, 1<<22,
+ {{0, 1ULL<<32}}, 0);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(123, '1'));
+ expect_read_cluster(*mock_stream_interface, 1ULL<<32, 123, expect_bl, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, 1, {{65659, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(123, ctx2.wait());
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadSnapDNE) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {{1ULL<<40}});
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, 1, {{65659, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(-ENOENT, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadClusterCacheHit) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {{1ULL<<40}});
+
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<40,
+ {{0, 1ULL<<32}}, 0);
+
+ bufferlist expect_bl1;
+ expect_bl1.append(std::string(123, '1'));
+
+ bufferlist expect_bl2;
+ expect_bl2.append(std::string(234, '2'));
+
+ bufferlist cluster_bl;
+ cluster_bl.append_zero(123);
+ cluster_bl.append(expect_bl1);
+ cluster_bl.append_zero(234);
+ cluster_bl.append(expect_bl2);
+
+ expect_read_cluster(*mock_stream_interface, 1ULL<<32, 0, cluster_bl, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp1 = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl1;
+ io::ReadResult read_result1{&bl1};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp1, CEPH_NOSNAP, {{65659, 123}},
+ std::move(read_result1), 0, 0, {}));
+ ASSERT_EQ(123, ctx2.wait());
+ ASSERT_EQ(expect_bl1, bl1);
+
+ C_SaferCond ctx3;
+ auto aio_comp2 = io::AioCompletion::create_and_start(
+ &ctx3, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl2;
+ io::ReadResult read_result2{&bl2};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp2, CEPH_NOSNAP, {{66016, 234}},
+ std::move(read_result2), 0, 0, {}));
+ ASSERT_EQ(234, ctx3.wait());
+ ASSERT_EQ(expect_bl2, bl2);
+
+ C_SaferCond ctx4;
+ mock_qcow_format.close(&ctx4);
+ ASSERT_EQ(0, ctx4.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadClusterError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {{1ULL<<40}});
+
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<40,
+ {{0, 1ULL<<32}}, 0);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(123, '1'));
+ expect_read_cluster(*mock_stream_interface, 1ULL<<32, 123, expect_bl, -EIO);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, CEPH_NOSNAP, {{65659, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(-EIO, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ReadL2TableError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_open(*mock_stream_interface, {{1ULL<<40}});
+
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<40,
+ {{0, 1ULL<<32}}, -EIO);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_qcow_format.read(aio_comp, CEPH_NOSNAP, {{65659, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(-EIO, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationQCOWFormat, ListSnaps) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_probe_header(*mock_stream_interface, QCOW_MAGIC, 2, 0);
+
+ expect_read_header(*mock_stream_interface, 2, 0);
+
+ uint64_t snapshot_offset = 1<<21;
+ expect_read_snapshot(*mock_stream_interface, "1", "snap1", 1<<29, 1<<22,
+ &snapshot_offset);
+ expect_read_snapshot(*mock_stream_interface, "2", "snap2", 1<<29, 1<<23,
+ &snapshot_offset);
+
+ expect_read_l1_table(*mock_stream_interface, {{1ULL<<28}}, 0);
+
+ io::SparseExtents sparse_extents_1;
+ sparse_extents_1.insert(0, 196608, {io::SPARSE_EXTENT_STATE_DATA, 196608});
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<22,
+ {{1ULL<<23, 1ULL<<24, 1ULL<<25}}, 0);
+
+ io::SparseExtents sparse_extents_2;
+ sparse_extents_2.insert(0, 65536, {io::SPARSE_EXTENT_STATE_DATA, 65536});
+ sparse_extents_2.insert(65536, 65536,
+ {io::SPARSE_EXTENT_STATE_ZEROED, 65536});
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<23,
+ {{1ULL<<26, QCOW_OFLAG_ZERO, 1ULL<<25}}, 0);
+
+ io::SparseExtents sparse_extents_head;
+ sparse_extents_head.insert(131072, 65536,
+ {io::SPARSE_EXTENT_STATE_DATA, 65536});
+ expect_read_l2_table(*mock_stream_interface, 1ULL<<28,
+ {{1ULL<<26, QCOW_OFLAG_ZERO, 1ULL<<27}}, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockQCOWFormat mock_qcow_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_qcow_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ io::SnapshotDelta snapshot_delta;
+ mock_qcow_format.list_snaps({{0, 196608}}, {1, CEPH_NOSNAP}, 0,
+ &snapshot_delta, {}, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ io::SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{1, 1}] = sparse_extents_1;
+ expected_snapshot_delta[{CEPH_NOSNAP, 2}] = sparse_extents_2;
+ expected_snapshot_delta[{CEPH_NOSNAP, CEPH_NOSNAP}] = sparse_extents_head;
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+
+ C_SaferCond ctx3;
+ mock_qcow_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_RawFormat.cc b/src/test/librbd/migration/test_mock_RawFormat.cc
new file mode 100644
index 000000000..6b69bf20c
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_RawFormat.cc
@@ -0,0 +1,523 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/migration/MockSnapshotInterface.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/migration/RawFormat.h"
+#include "librbd/migration/SourceSpecBuilder.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "json_spirit/json_spirit.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace migration {
+
+template<>
+struct SourceSpecBuilder<librbd::MockTestImageCtx> {
+
+ MOCK_CONST_METHOD3(build_snapshot, int(const json_spirit::mObject&, uint64_t,
+ std::shared_ptr<SnapshotInterface>*));
+
+};
+
+} // namespace migration
+} // namespace librbd
+
+#include "librbd/migration/RawFormat.cc"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::ReturnRef;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+namespace librbd {
+namespace migration {
+
+using ::testing::Invoke;
+
+class TestMockMigrationRawFormat : public TestMockFixture {
+public:
+ typedef RawFormat<MockTestImageCtx> MockRawFormat;
+ typedef SourceSpecBuilder<MockTestImageCtx> MockSourceSpecBuilder;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+
+ json_spirit::mObject stream_obj;
+ stream_obj["type"] = "file";
+ json_object["stream"] = stream_obj;
+ }
+
+ void expect_build_snapshot(MockSourceSpecBuilder& mock_source_spec_builder,
+ uint64_t index,
+ MockSnapshotInterface* mock_snapshot_interface,
+ int r) {
+ EXPECT_CALL(mock_source_spec_builder, build_snapshot(_, index, _))
+ .WillOnce(WithArgs<2>(Invoke([mock_snapshot_interface, r]
+ (std::shared_ptr<SnapshotInterface>* ptr) {
+ ptr->reset(mock_snapshot_interface);
+ return r;
+ })));
+ }
+
+ void expect_snapshot_open(MockSnapshotInterface& mock_snapshot_interface,
+ int r) {
+ EXPECT_CALL(mock_snapshot_interface, open(_, _))
+ .WillOnce(WithArg<1>(Invoke([r](Context* ctx) { ctx->complete(r); })));
+ }
+
+ void expect_snapshot_close(MockSnapshotInterface& mock_snapshot_interface,
+ int r) {
+ EXPECT_CALL(mock_snapshot_interface, close(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_snapshot_get_info(MockSnapshotInterface& mock_snapshot_interface,
+ const SnapInfo& snap_info) {
+ EXPECT_CALL(mock_snapshot_interface, get_snap_info())
+ .WillOnce(ReturnRef(snap_info));
+ }
+
+ void expect_snapshot_read(MockSnapshotInterface& mock_snapshot_interface,
+ const io::Extents& image_extents,
+ const bufferlist& bl, int r) {
+ EXPECT_CALL(mock_snapshot_interface, read(_, image_extents, _))
+ .WillOnce(WithArgs<0, 2>(Invoke([bl, image_extents, r]
+ (io::AioCompletion* aio_comp, io::ReadResult& read_result) {
+ aio_comp->read_result = std::move(read_result);
+ aio_comp->read_result.set_image_extents(image_extents);
+ aio_comp->set_request_count(1);
+ auto ctx = new io::ReadResult::C_ImageReadRequest(aio_comp, 0,
+ image_extents);
+ ctx->bl = std::move(bl);
+ ctx->complete(r);
+ })));
+ }
+
+ void expect_snapshot_list_snap(MockSnapshotInterface& mock_snapshot_interface,
+ const io::Extents& image_extents,
+ const io::SparseExtents& sparse_extents,
+ int r) {
+ EXPECT_CALL(mock_snapshot_interface, list_snap(image_extents, _, _))
+ .WillOnce(WithArgs<1, 2>(Invoke(
+ [sparse_extents, r](io::SparseExtents* out_sparse_extents,
+ Context* ctx) {
+ out_sparse_extents->insert(sparse_extents);
+ ctx->complete(r);
+ })));
+ }
+
+ void expect_close(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, close(_))
+ .WillOnce(Invoke([r](Context* ctx) {
+ ctx->complete(r);
+ }));
+ }
+
+ json_spirit::mObject json_object;
+};
+
+TEST_F(TestMockMigrationRawFormat, OpenClose) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface, 0);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_raw_format.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, OpenError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface, -ENOENT);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+ expect_close(mock_image_ctx, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx;
+ mock_raw_format.open(&ctx);
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, OpenSnapshotError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface_head = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface_head, 0);
+
+ auto mock_snapshot_interface_1 = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, 1,
+ mock_snapshot_interface_1, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface_1, -ENOENT);
+ expect_snapshot_open(*mock_snapshot_interface_head, 0);
+
+ expect_snapshot_close(*mock_snapshot_interface_1, 0);
+ expect_snapshot_close(*mock_snapshot_interface_head, 0);
+ expect_close(mock_image_ctx, 0);
+
+ json_spirit::mArray snapshots;
+ snapshots.push_back(json_spirit::mObject{});
+ json_object["snapshots"] = snapshots;
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx;
+ mock_raw_format.open(&ctx);
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, GetSnapshots) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface, 0);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ FormatInterface::SnapInfos snap_infos;
+ mock_raw_format.get_snapshots(&snap_infos, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_TRUE(snap_infos.empty());
+
+ C_SaferCond ctx3;
+ mock_raw_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, GetImageSize) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface, 0);
+
+ SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}};
+ expect_snapshot_get_info(*mock_snapshot_interface, snap_info);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ uint64_t size;
+ mock_raw_format.get_image_size(CEPH_NOSNAP, &size, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(size, 123);
+
+ C_SaferCond ctx3;
+ mock_raw_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, GetImageSizeSnapshotDNE) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface, 0);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ uint64_t size;
+ mock_raw_format.get_image_size(0, &size, &ctx2);
+ ASSERT_EQ(-ENOENT, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_raw_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, Read) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface, 0);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(123, '1'));
+ expect_snapshot_read(*mock_snapshot_interface, {{123, 123}}, expect_bl, 0);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ ASSERT_TRUE(mock_raw_format.read(aio_comp, CEPH_NOSNAP, {{123, 123}},
+ std::move(read_result), 0, 0, {}));
+ ASSERT_EQ(123, ctx2.wait());
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_raw_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, ListSnaps) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+ expect_snapshot_open(*mock_snapshot_interface, 0);
+
+ SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}};
+ expect_snapshot_get_info(*mock_snapshot_interface, snap_info);
+ io::SparseExtents sparse_extents;
+ sparse_extents.insert(0, 123, {io::SPARSE_EXTENT_STATE_DATA, 123});
+ expect_snapshot_list_snap(*mock_snapshot_interface, {{0, 123}},
+ sparse_extents, 0);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ io::SnapshotDelta snapshot_delta;
+ mock_raw_format.list_snaps({{0, 123}}, {CEPH_NOSNAP}, 0, &snapshot_delta, {},
+ &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ io::SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{CEPH_NOSNAP, CEPH_NOSNAP}] = sparse_extents;
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+
+ C_SaferCond ctx3;
+ mock_raw_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, ListSnapsError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface, 0);
+
+
+ expect_snapshot_open(*mock_snapshot_interface, 0);
+
+ SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}};
+ expect_snapshot_get_info(*mock_snapshot_interface, snap_info);
+ io::SparseExtents sparse_extents;
+ sparse_extents.insert(0, 123, {io::SPARSE_EXTENT_STATE_DATA, 123});
+ expect_snapshot_list_snap(*mock_snapshot_interface, {{0, 123}},
+ sparse_extents, -EINVAL);
+
+ expect_snapshot_close(*mock_snapshot_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ io::SnapshotDelta snapshot_delta;
+ mock_raw_format.list_snaps({{0, 123}}, {CEPH_NOSNAP}, 0, &snapshot_delta, {},
+ &ctx2);
+ ASSERT_EQ(-EINVAL, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_raw_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationRawFormat, ListSnapsMerge) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_snapshot_interface_head = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP,
+ mock_snapshot_interface_head, 0);
+
+ auto mock_snapshot_interface_1 = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, 1,
+ mock_snapshot_interface_1, 0);
+
+ auto mock_snapshot_interface_2 = new MockSnapshotInterface();
+ expect_build_snapshot(mock_source_spec_builder, 2,
+ mock_snapshot_interface_2, 0);
+
+
+ expect_snapshot_open(*mock_snapshot_interface_1, 0);
+ expect_snapshot_open(*mock_snapshot_interface_2, 0);
+ expect_snapshot_open(*mock_snapshot_interface_head, 0);
+
+ SnapInfo snap_info_head{{}, {}, 256, {}, 0, 0, {}};
+ SnapInfo snap_info_1{snap_info_head};
+ snap_info_1.size = 123;
+ expect_snapshot_get_info(*mock_snapshot_interface_1, snap_info_1);
+ io::SparseExtents sparse_extents_1;
+ sparse_extents_1.insert(0, 123, {io::SPARSE_EXTENT_STATE_DATA, 123});
+ expect_snapshot_list_snap(*mock_snapshot_interface_1, {{0, 123}},
+ sparse_extents_1, 0);
+
+ SnapInfo snap_info_2{snap_info_head};
+ snap_info_2.size = 64;
+ expect_snapshot_get_info(*mock_snapshot_interface_2, snap_info_2);
+ io::SparseExtents sparse_extents_2;
+ sparse_extents_2.insert(0, 32, {io::SPARSE_EXTENT_STATE_DATA, 32});
+ expect_snapshot_list_snap(*mock_snapshot_interface_2, {{0, 123}},
+ sparse_extents_2, 0);
+
+ expect_snapshot_get_info(*mock_snapshot_interface_head, snap_info_head);
+ io::SparseExtents sparse_extents_head;
+ sparse_extents_head.insert(0, 16, {io::SPARSE_EXTENT_STATE_DATA, 16});
+ expect_snapshot_list_snap(*mock_snapshot_interface_head, {{0, 123}},
+ sparse_extents_head, 0);
+
+ expect_snapshot_close(*mock_snapshot_interface_1, 0);
+ expect_snapshot_close(*mock_snapshot_interface_2, 0);
+ expect_snapshot_close(*mock_snapshot_interface_head, 0);
+
+ json_spirit::mArray snapshots;
+ snapshots.push_back(json_spirit::mObject{});
+ snapshots.push_back(json_spirit::mObject{});
+ json_object["snapshots"] = snapshots;
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
+
+ C_SaferCond ctx1;
+ mock_raw_format.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ io::SnapshotDelta snapshot_delta;
+ mock_raw_format.list_snaps({{0, 123}}, {1, CEPH_NOSNAP}, 0, &snapshot_delta,
+ {}, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ io::SnapshotDelta expected_snapshot_delta;
+ expected_snapshot_delta[{1, 1}] = sparse_extents_1;
+ sparse_extents_2.erase(0, 16);
+ sparse_extents_2.insert(64, 59, {io::SPARSE_EXTENT_STATE_ZEROED, 59});
+ expected_snapshot_delta[{CEPH_NOSNAP, 2}] = sparse_extents_2;
+ expected_snapshot_delta[{CEPH_NOSNAP, CEPH_NOSNAP}] = sparse_extents_head;
+ ASSERT_EQ(expected_snapshot_delta, snapshot_delta);
+
+ C_SaferCond ctx3;
+ mock_raw_format.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_RawSnapshot.cc b/src/test/librbd/migration/test_mock_RawSnapshot.cc
new file mode 100644
index 000000000..3ce4b5c9d
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_RawSnapshot.cc
@@ -0,0 +1,255 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/migration/MockStreamInterface.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/migration/FileStream.h"
+#include "librbd/migration/RawSnapshot.h"
+#include "librbd/migration/SourceSpecBuilder.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "json_spirit/json_spirit.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace migration {
+
+template<>
+struct SourceSpecBuilder<librbd::MockTestImageCtx> {
+
+ MOCK_CONST_METHOD2(build_stream, int(const json_spirit::mObject&,
+ std::shared_ptr<StreamInterface>*));
+
+};
+
+} // namespace migration
+} // namespace librbd
+
+#include "librbd/migration/RawSnapshot.cc"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::WithArgs;
+
+namespace librbd {
+namespace migration {
+
+using ::testing::Invoke;
+
+class TestMockMigrationRawSnapshot : public TestMockFixture {
+public:
+ typedef RawSnapshot<MockTestImageCtx> MockRawSnapshot;
+ typedef SourceSpecBuilder<MockTestImageCtx> MockSourceSpecBuilder;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+
+ json_spirit::mObject stream_obj;
+ stream_obj["type"] = "file";
+ json_object["stream"] = stream_obj;
+ }
+
+ void expect_build_stream(MockSourceSpecBuilder& mock_source_spec_builder,
+ MockStreamInterface* mock_stream_interface, int r) {
+ EXPECT_CALL(mock_source_spec_builder, build_stream(_, _))
+ .WillOnce(WithArgs<1>(Invoke([mock_stream_interface, r]
+ (std::shared_ptr<StreamInterface>* ptr) {
+ ptr->reset(mock_stream_interface);
+ return r;
+ })));
+ }
+
+ void expect_stream_open(MockStreamInterface& mock_stream_interface, int r) {
+ EXPECT_CALL(mock_stream_interface, open(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_stream_close(MockStreamInterface& mock_stream_interface, int r) {
+ EXPECT_CALL(mock_stream_interface, close(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_stream_get_size(MockStreamInterface& mock_stream_interface,
+ uint64_t size, int r) {
+ EXPECT_CALL(mock_stream_interface, get_size(_, _))
+ .WillOnce(Invoke([size, r](uint64_t* out_size, Context* ctx) {
+ *out_size = size;
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_stream_read(MockStreamInterface& mock_stream_interface,
+ const io::Extents& byte_extents,
+ const bufferlist& bl, int r) {
+ EXPECT_CALL(mock_stream_interface, read(byte_extents, _, _))
+ .WillOnce(WithArgs<1, 2>(Invoke([bl, r]
+ (bufferlist* out_bl, Context* ctx) {
+ *out_bl = bl;
+ ctx->complete(r);
+ })));
+ }
+
+ json_spirit::mObject json_object;
+};
+
+TEST_F(TestMockMigrationRawSnapshot, OpenClose) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+ expect_stream_get_size(*mock_stream_interface, 123, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ json_object["name"] = "snap1";
+ MockRawSnapshot mock_raw_snapshot(&mock_image_ctx, json_object,
+ &mock_source_spec_builder, 1);
+
+ C_SaferCond ctx1;
+ mock_raw_snapshot.open(nullptr, &ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ auto snap_info = mock_raw_snapshot.get_snap_info();
+ ASSERT_EQ("snap1", snap_info.name);
+ ASSERT_EQ(123, snap_info.size);
+
+ C_SaferCond ctx2;
+ mock_raw_snapshot.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationRawSnapshot, OpenError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, -ENOENT);
+
+ MockRawSnapshot mock_raw_snapshot(&mock_image_ctx, json_object,
+ &mock_source_spec_builder, 0);
+
+ C_SaferCond ctx;
+ mock_raw_snapshot.open(nullptr, &ctx);
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockMigrationRawSnapshot, GetSizeError) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+ expect_stream_get_size(*mock_stream_interface, 0, -EINVAL);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockRawSnapshot mock_raw_snapshot(&mock_image_ctx, json_object,
+ &mock_source_spec_builder, 0);
+
+ C_SaferCond ctx;
+ mock_raw_snapshot.open(nullptr, &ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMigrationRawSnapshot, Read) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+ expect_stream_get_size(*mock_stream_interface, 0, 0);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(123, '1'));
+ expect_stream_read(*mock_stream_interface, {{123, 123}}, expect_bl, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockRawSnapshot mock_raw_snapshot(&mock_image_ctx, json_object,
+ &mock_source_spec_builder, 0);
+
+ C_SaferCond ctx1;
+ mock_raw_snapshot.open(nullptr, &ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ auto aio_comp = io::AioCompletion::create_and_start(
+ &ctx2, m_image_ctx, io::AIO_TYPE_READ);
+ bufferlist bl;
+ io::ReadResult read_result{&bl};
+ mock_raw_snapshot.read(aio_comp, {{123, 123}}, std::move(read_result), 0, 0,
+ {});
+ ASSERT_EQ(123, ctx2.wait());
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_raw_snapshot.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationRawSnapshot, ListSnap) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
+ expect_stream_get_size(*mock_stream_interface, 0, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockRawSnapshot mock_raw_snapshot(&mock_image_ctx, json_object,
+ &mock_source_spec_builder, 0);
+
+ C_SaferCond ctx1;
+ mock_raw_snapshot.open(nullptr, &ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ io::SparseExtents sparse_extents;
+ mock_raw_snapshot.list_snap({{0, 123}}, 0, &sparse_extents, {}, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+
+ C_SaferCond ctx3;
+ mock_raw_snapshot.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_S3Stream.cc b/src/test/librbd/migration/test_mock_S3Stream.cc
new file mode 100644
index 000000000..2f2097f79
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_S3Stream.cc
@@ -0,0 +1,238 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/migration/HttpClient.h"
+#include "librbd/migration/S3Stream.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "json_spirit/json_spirit.h"
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/beast/http.hpp>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace migration {
+
+template <>
+struct HttpClient<MockTestImageCtx> {
+ static HttpClient* s_instance;
+ static HttpClient* create(MockTestImageCtx*, const std::string&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ HttpProcessorInterface* http_processor = nullptr;
+ void set_http_processor(HttpProcessorInterface* http_processor) {
+ this->http_processor = http_processor;
+ }
+
+ MOCK_METHOD1(open, void(Context*));
+ MOCK_METHOD1(close, void(Context*));
+ MOCK_METHOD2(get_size, void(uint64_t*, Context*));
+ MOCK_METHOD3(do_read, void(const io::Extents&, bufferlist*, Context*));
+ void read(io::Extents&& extents, bufferlist* bl, Context* ctx) {
+ do_read(extents, bl, ctx);
+ }
+
+ HttpClient() {
+ s_instance = this;
+ }
+};
+
+HttpClient<MockTestImageCtx>* HttpClient<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace migration
+} // namespace librbd
+
+#include "librbd/migration/S3Stream.cc"
+
+namespace librbd {
+namespace migration {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::WithArgs;
+
+class TestMockMigrationS3Stream : public TestMockFixture {
+public:
+ typedef S3Stream<MockTestImageCtx> MockS3Stream;
+ typedef HttpClient<MockTestImageCtx> MockHttpClient;
+
+ using EmptyBody = boost::beast::http::empty_body;
+ using EmptyRequest = boost::beast::http::request<EmptyBody>;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+ json_object["url"] = "http://some.site/bucket/file";
+ json_object["access_key"] = "0555b35654ad1656d804";
+ json_object["secret_key"] = "h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q==";
+ }
+
+ void expect_open(MockHttpClient& mock_http_client, int r) {
+ EXPECT_CALL(mock_http_client, open(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_close(MockHttpClient& mock_http_client, int r) {
+ EXPECT_CALL(mock_http_client, close(_))
+ .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
+ }
+
+ void expect_get_size(MockHttpClient& mock_http_client, uint64_t size, int r) {
+ EXPECT_CALL(mock_http_client, get_size(_, _))
+ .WillOnce(Invoke([size, r](uint64_t* out_size, Context* ctx) {
+ *out_size = size;
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_read(MockHttpClient& mock_http_client, io::Extents byte_extents,
+ const bufferlist& bl, int r) {
+ uint64_t len = 0;
+ for (auto [_, byte_len] : byte_extents) {
+ len += byte_len;
+ }
+ EXPECT_CALL(mock_http_client, do_read(byte_extents, _, _))
+ .WillOnce(WithArgs<1, 2>(Invoke(
+ [len, bl, r](bufferlist* out_bl, Context* ctx) {
+ *out_bl = bl;
+ ctx->complete(r < 0 ? r : len);
+ })));
+ }
+
+ json_spirit::mObject json_object;
+};
+
+TEST_F(TestMockMigrationS3Stream, OpenClose) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ auto mock_http_client = new MockHttpClient();
+ expect_open(*mock_http_client, 0);
+
+ expect_close(*mock_http_client, 0);
+
+ MockS3Stream mock_http_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_http_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_http_stream.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationS3Stream, GetSize) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ auto mock_http_client = new MockHttpClient();
+ expect_open(*mock_http_client, 0);
+
+ expect_get_size(*mock_http_client, 128, 0);
+
+ expect_close(*mock_http_client, 0);
+
+ MockS3Stream mock_http_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_http_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ uint64_t size;
+ mock_http_stream.get_size(&size, &ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+ ASSERT_EQ(128, size);
+
+ C_SaferCond ctx3;
+ mock_http_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationS3Stream, Read) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ auto mock_http_client = new MockHttpClient();
+ expect_open(*mock_http_client, 0);
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(192, '1'));
+ expect_read(*mock_http_client, {{0, 128}, {256, 64}}, expect_bl, 0);
+
+ expect_close(*mock_http_client, 0);
+
+ MockS3Stream mock_http_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_http_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ C_SaferCond ctx2;
+ bufferlist bl;
+ mock_http_stream.read({{0, 128}, {256, 64}}, &bl, &ctx2);
+ ASSERT_EQ(192, ctx2.wait());
+ ASSERT_EQ(expect_bl, bl);
+
+ C_SaferCond ctx3;
+ mock_http_stream.close(&ctx3);
+ ASSERT_EQ(0, ctx3.wait());
+}
+
+TEST_F(TestMockMigrationS3Stream, ProcessRequest) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ auto mock_http_client = new MockHttpClient();
+ expect_open(*mock_http_client, 0);
+
+ expect_close(*mock_http_client, 0);
+
+ MockS3Stream mock_http_stream(&mock_image_ctx, json_object);
+
+ C_SaferCond ctx1;
+ mock_http_stream.open(&ctx1);
+ ASSERT_EQ(0, ctx1.wait());
+
+ EmptyRequest request;
+ request.method(boost::beast::http::verb::get);
+ request.target("/bucket/resource");
+ mock_http_client->http_processor->process_request(request);
+
+ // basic test for date and known portion of authorization
+ ASSERT_EQ(1U, request.count(boost::beast::http::field::date));
+ ASSERT_EQ(1U, request.count(boost::beast::http::field::authorization));
+ ASSERT_TRUE(boost::algorithm::starts_with(
+ request[boost::beast::http::field::authorization],
+ "AWS 0555b35654ad1656d804:"));
+
+ C_SaferCond ctx2;
+ mock_http_stream.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/migration/test_mock_Utils.cc b/src/test/librbd/migration/test_mock_Utils.cc
new file mode 100644
index 000000000..917c191dd
--- /dev/null
+++ b/src/test/librbd/migration/test_mock_Utils.cc
@@ -0,0 +1,47 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/migration/Utils.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+namespace librbd {
+namespace migration {
+namespace util {
+
+class TestMockMigrationUtils : public TestMockFixture {
+public:
+};
+
+TEST_F(TestMockMigrationUtils, ParseUrl) {
+ UrlSpec url_spec;
+ ASSERT_EQ(-EINVAL, parse_url(g_ceph_context, "", &url_spec));
+ ASSERT_EQ(-EINVAL, parse_url(g_ceph_context, "jttp://google.com/path",
+ &url_spec));
+ ASSERT_EQ(-EINVAL, parse_url(g_ceph_context, "http://google.com:absd/path",
+ &url_spec));
+
+ ASSERT_EQ(0, parse_url(g_ceph_context, "ceph.io/path", &url_spec));
+ ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "ceph.io", "80", "/path"), url_spec);
+
+ ASSERT_EQ(0, parse_url(g_ceph_context, "http://google.com/path", &url_spec));
+ ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "google.com", "80", "/path"), url_spec);
+
+ ASSERT_EQ(0, parse_url(g_ceph_context, "https://ceph.io/", &url_spec));
+ ASSERT_EQ(UrlSpec(URL_SCHEME_HTTPS, "ceph.io", "443", "/"), url_spec);
+
+ ASSERT_EQ(0, parse_url(g_ceph_context,
+ "http://google.com:1234/some/other/path", &url_spec));
+ ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "google.com", "1234", "/some/other/path"),
+ url_spec);
+
+ ASSERT_EQ(0, parse_url(g_ceph_context,
+ "http://1.2.3.4/", &url_spec));
+ ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "1.2.3.4", "80", "/"), url_spec);
+}
+
+} // namespace util
+} // namespace migration
+} // namespace librbd
diff --git a/src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc b/src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc
new file mode 100644
index 000000000..d7e70cab7
--- /dev/null
+++ b/src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc
@@ -0,0 +1,388 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
+#include "librbd/mirror/snapshot/Utils.h"
+#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace mirror {
+namespace snapshot {
+namespace util {
+
+namespace {
+
+struct Mock {
+ static Mock* s_instance;
+
+ Mock() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD1(can_create_non_primary_snapshot,
+ bool(librbd::MockTestImageCtx *));
+};
+
+Mock *Mock::s_instance = nullptr;
+
+} // anonymous namespace
+
+template<> bool can_create_non_primary_snapshot(
+ librbd::MockTestImageCtx *image_ctx) {
+ return Mock::s_instance->can_create_non_primary_snapshot(image_ctx);
+}
+
+} // namespace util
+
+template <>
+struct WriteImageStateRequest<MockTestImageCtx> {
+ uint64_t snap_id = CEPH_NOSNAP;
+ ImageState image_state;
+ Context* on_finish = nullptr;
+ static WriteImageStateRequest* s_instance;
+ static WriteImageStateRequest *create(MockTestImageCtx *image_ctx,
+ uint64_t snap_id,
+ const ImageState &image_state,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->snap_id = snap_id;
+ s_instance->image_state = image_state;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ WriteImageStateRequest() {
+ s_instance = this;
+ }
+};
+
+WriteImageStateRequest<MockTestImageCtx>* WriteImageStateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.cc"
+template class librbd::mirror::snapshot::CreateNonPrimaryRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockMirrorSnapshotCreateNonPrimaryRequest : public TestMockFixture {
+public:
+ typedef CreateNonPrimaryRequest<MockTestImageCtx> MockCreateNonPrimaryRequest;
+ typedef WriteImageStateRequest<MockTestImageCtx> MockWriteImageStateRequest;
+ typedef util::Mock MockUtils;
+
+ void expect_clone_md_ctx(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), clone())
+ .WillOnce(Invoke([&mock_image_ctx]() {
+ get_mock_io_ctx(mock_image_ctx.md_ctx).get();
+ return &get_mock_io_ctx(mock_image_ctx.md_ctx);
+ }));
+ }
+
+ void expect_refresh_image(MockTestImageCtx &mock_image_ctx,
+ bool refresh_required, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(refresh_required));
+ if (refresh_required) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+ }
+
+ void expect_get_mirror_image(MockTestImageCtx &mock_image_ctx,
+ const cls::rbd::MirrorImage &mirror_image,
+ int r) {
+ using ceph::encode;
+ bufferlist bl;
+ encode(mirror_image, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_get"),
+ _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)),
+ Return(r)));
+ }
+
+ void expect_can_create_non_primary_snapshot(MockUtils &mock_utils,
+ bool result) {
+ EXPECT_CALL(mock_utils, can_create_non_primary_snapshot(_))
+ .WillOnce(Return(result));
+ }
+
+ void expect_get_mirror_peers(MockTestImageCtx &mock_image_ctx,
+ const std::vector<cls::rbd::MirrorPeer> &peers,
+ int r) {
+ using ceph::encode;
+ bufferlist bl;
+ encode(peers, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_peer_list"),
+ _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)),
+ Return(r)));
+ }
+
+ void expect_create_snapshot(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, snap_create(_, _, _, _, _))
+ .WillOnce(WithArg<4>(CompleteContext(
+ r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ void expect_write_image_state(
+ MockTestImageCtx &mock_image_ctx,
+ MockWriteImageStateRequest &mock_write_image_state_request, int r) {
+ EXPECT_CALL(mock_image_ctx, get_snap_id(_, _))
+ .WillOnce(Return(123));
+ EXPECT_CALL(mock_write_image_state_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_write_image_state_request, r]() {
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_write_image_state_request.on_finish, r);
+ }));
+ }
+};
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, Success) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+ MockUtils mock_utils;
+ expect_can_create_non_primary_snapshot(mock_utils, true);
+ expect_create_snapshot(mock_image_ctx, 0);
+ MockWriteImageStateRequest mock_write_image_state_request;
+ expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, false,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, SuccessDemoted) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+ MockUtils mock_utils;
+ expect_can_create_non_primary_snapshot(mock_utils, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ MockWriteImageStateRequest mock_write_image_state_request;
+ expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, true,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, RefreshError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_refresh_image(mock_image_ctx, true, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, false,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, GetMirrorImageError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, false,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, CanNotError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_refresh_image(mock_image_ctx, false, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+ MockUtils mock_utils;
+ expect_can_create_non_primary_snapshot(mock_utils, false);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, false,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, GetMirrorPeersError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+ MockUtils mock_utils;
+ expect_can_create_non_primary_snapshot(mock_utils, true);
+ expect_get_mirror_peers(mock_image_ctx, {}, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, true,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, CreateSnapshotError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+ MockUtils mock_utils;
+ expect_can_create_non_primary_snapshot(mock_utils, true);
+ expect_create_snapshot(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, false,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, WriteImageStateError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+ MockUtils mock_utils;
+ expect_can_create_non_primary_snapshot(mock_utils, true);
+ expect_create_snapshot(mock_image_ctx, 0);
+ MockWriteImageStateRequest mock_write_image_state_request;
+ expect_write_image_state(mock_image_ctx, mock_write_image_state_request,
+ -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, false,
+ "mirror_uuid", 123, {{1, 2}}, {},
+ nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
diff --git a/src/test/librbd/mirror/snapshot/test_mock_CreatePrimaryRequest.cc b/src/test/librbd/mirror/snapshot/test_mock_CreatePrimaryRequest.cc
new file mode 100644
index 000000000..8bfdcdeb1
--- /dev/null
+++ b/src/test/librbd/mirror/snapshot/test_mock_CreatePrimaryRequest.cc
@@ -0,0 +1,807 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/stringify.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/mirror/snapshot/CreatePrimaryRequest.h"
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
+#include "librbd/mirror/snapshot/Utils.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace mirror {
+namespace snapshot {
+namespace util {
+
+namespace {
+
+struct Mock {
+ static Mock* s_instance;
+
+ Mock() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD4(can_create_primary_snapshot,
+ bool(librbd::MockTestImageCtx *, bool, bool, uint64_t *));
+};
+
+Mock *Mock::s_instance = nullptr;
+
+} // anonymous namespace
+
+template<> bool can_create_primary_snapshot(librbd::MockTestImageCtx *image_ctx,
+ bool demoted, bool force,
+ bool* requires_orphan,
+ uint64_t *rollback_snap_id) {
+ return Mock::s_instance->can_create_primary_snapshot(image_ctx, demoted,
+ force, rollback_snap_id);
+}
+
+} // namespace util
+
+template <>
+struct UnlinkPeerRequest<MockTestImageCtx> {
+ uint64_t snap_id = CEPH_NOSNAP;
+ std::string mirror_peer_uuid;
+ bool allow_remove;
+ Context* on_finish = nullptr;
+ static UnlinkPeerRequest* s_instance;
+ static UnlinkPeerRequest *create(MockTestImageCtx *image_ctx,
+ uint64_t snap_id,
+ const std::string &mirror_peer_uuid,
+ bool allow_remove,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->snap_id = snap_id;
+ s_instance->mirror_peer_uuid = mirror_peer_uuid;
+ s_instance->allow_remove = allow_remove;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ UnlinkPeerRequest() {
+ s_instance = this;
+ }
+};
+
+UnlinkPeerRequest<MockTestImageCtx>* UnlinkPeerRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/mirror/snapshot/CreatePrimaryRequest.cc"
+template class librbd::mirror::snapshot::CreatePrimaryRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockMirrorSnapshotCreatePrimaryRequest : public TestMockFixture {
+public:
+ typedef CreatePrimaryRequest<MockTestImageCtx> MockCreatePrimaryRequest;
+ typedef UnlinkPeerRequest<MockTestImageCtx> MockUnlinkPeerRequest;
+ typedef util::Mock MockUtils;
+
+ uint64_t m_snap_seq = 0;
+
+ void snap_create(MockTestImageCtx &mock_image_ctx,
+ const cls::rbd::SnapshotNamespace &ns,
+ const std::string& snap_name) {
+ ASSERT_TRUE(mock_image_ctx.snap_info.insert(
+ {m_snap_seq++,
+ SnapInfo{snap_name, ns, 0, {}, 0, 0, {}}}).second);
+ }
+
+ void expect_clone_md_ctx(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), clone())
+ .WillOnce(Invoke([&mock_image_ctx]() {
+ get_mock_io_ctx(mock_image_ctx.md_ctx).get();
+ return &get_mock_io_ctx(mock_image_ctx.md_ctx);
+ }));
+ }
+
+ void expect_can_create_primary_snapshot(MockUtils &mock_utils, bool demoted,
+ bool force, bool result) {
+ EXPECT_CALL(mock_utils,
+ can_create_primary_snapshot(_, demoted, force, nullptr))
+ .WillOnce(Return(result));
+ }
+
+ void expect_get_mirror_peers(MockTestImageCtx &mock_image_ctx,
+ const std::vector<cls::rbd::MirrorPeer> &peers,
+ int r) {
+ using ceph::encode;
+ bufferlist bl;
+ encode(peers, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_peer_list"),
+ _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)),
+ Return(r)));
+ }
+
+ void expect_create_snapshot(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, snap_create(_, _, _, _, _))
+ .WillOnce(DoAll(
+ Invoke([this, &mock_image_ctx, r](
+ const cls::rbd::SnapshotNamespace &ns,
+ const std::string& snap_name,
+ uint64_t flags,
+ ProgressContext &prog_ctx,
+ Context *on_finish) {
+ if (r != 0) {
+ return;
+ }
+ auto mirror_ns =
+ std::get<cls::rbd::MirrorSnapshotNamespace>(ns);
+ mirror_ns.complete = true;
+ snap_create(mock_image_ctx, mirror_ns, snap_name);
+ }),
+ WithArg<4>(CompleteContext(
+ r, mock_image_ctx.image_ctx->op_work_queue))
+ ));
+ }
+
+ void expect_refresh_image(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_unlink_peer(MockTestImageCtx &mock_image_ctx,
+ MockUnlinkPeerRequest &mock_unlink_peer_request,
+ uint64_t snap_id, const std::string &peer_uuid,
+ bool is_linked, bool complete, bool allow_remove,
+ int r) {
+ EXPECT_CALL(mock_unlink_peer_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_unlink_peer_request,
+ snap_id, peer_uuid, is_linked, complete, allow_remove, r]() {
+ ASSERT_EQ(mock_unlink_peer_request.mirror_peer_uuid,
+ peer_uuid);
+ ASSERT_EQ(mock_unlink_peer_request.snap_id, snap_id);
+ ASSERT_EQ(mock_unlink_peer_request.allow_remove, allow_remove);
+ if (r == 0) {
+ auto it = mock_image_ctx.snap_info.find(snap_id);
+ ASSERT_NE(it, mock_image_ctx.snap_info.end());
+ auto info =
+ std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &it->second.snap_namespace);
+ ASSERT_NE(nullptr, info);
+ ASSERT_EQ(complete, info->complete);
+ ASSERT_EQ(is_linked, info->mirror_peer_uuids.erase(
+ peer_uuid));
+ if (info->mirror_peer_uuids.empty()) {
+ mock_image_ctx.snap_info.erase(it);
+ }
+ }
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_unlink_peer_request.on_finish, r);
+ }));
+ }
+};
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, Success) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessPrimary) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"uuid"}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessPrimaryDemoted) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {"uuid"}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessNonPrimaryDemoted) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED, {"uuid"},
+ "mirror uuid", 123};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessPrimaryBelowMaxSnapshots) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->config.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ for (int i = 0; i < 2; i++) {
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"uuid"}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+ }
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessPrimaryBelowMaxSnapshotsReset) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->config.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ for (int i = 0; i < 6; i++) {
+ cls::rbd::MirrorSnapshotNamespace ns{
+ (i == 3 ? cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED :
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY),
+ {"uuid"}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+ }
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, CanNotError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, false);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, GetMirrorPeersError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, CreateSnapshotError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkPrimaryNoPeer) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ false, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkPrimaryDemotedNoPeer) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ false, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkNonPrimaryDemotedNoPeer) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED, {},
+ "mirror uuid", 123};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ false, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkOrphanNoPeer) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ false, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkPrimaryIncomplete) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"uuid"}, "", CEPH_NOSNAP};
+ ns.complete = false;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ true, false, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkPrimaryMaxSnapshots) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->config.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ for (int i = 0; i < 3; i++) {
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"uuid"}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+ }
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ true, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkPrimaryMaxSnapshotsReset) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->config.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ for (int i = 0; i < 7; i++) {
+ cls::rbd::MirrorSnapshotNamespace ns{
+ (i == 3 ? cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED :
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY),
+ {"uuid"}, "", CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+ }
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ true, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkMultiplePeers) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ictx->config.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ for (int i = 0; i < 3; i++) {
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"uuid1", "uuid2"}, "",
+ CEPH_NOSNAP};
+ ns.complete = true;
+ snap_create(mock_image_ctx, ns, "mirror_snap");
+ }
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid1", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"},
+ {"uuid2", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.rbegin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid1",
+ true, true, true, 0);
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid2",
+ true, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkMultipleSnapshots) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns1{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {}, "", CEPH_NOSNAP};
+ ns1.complete = true;
+ snap_create(mock_image_ctx, ns1, "mirror_snap");
+ cls::rbd::MirrorSnapshotNamespace ns2{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {}, "", CEPH_NOSNAP};
+ ns2.complete = true;
+ snap_create(mock_image_ctx, ns2, "mirror_snap");
+
+ InSequence seq;
+
+ expect_clone_md_ctx(mock_image_ctx);
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, false, false, true);
+ expect_get_mirror_peers(mock_image_ctx,
+ {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX, "ceph",
+ "mirror", "mirror uuid"}}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ auto it = mock_image_ctx.snap_info.begin();
+ auto snap_id = it->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ false, true, true, 0);
+ snap_id = (++it)->first;
+ expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
+ false, true, true, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
+ 0U, 0U, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
diff --git a/src/test/librbd/mirror/snapshot/test_mock_ImageMeta.cc b/src/test/librbd/mirror/snapshot/test_mock_ImageMeta.cc
new file mode 100644
index 000000000..70a5eaeb0
--- /dev/null
+++ b/src/test/librbd/mirror/snapshot/test_mock_ImageMeta.cc
@@ -0,0 +1,159 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/ImageState.h"
+#include "librbd/mirror/snapshot/ImageMeta.h"
+#include "librbd/mirror/snapshot/Utils.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "librbd/mirror/snapshot/ImageMeta.cc"
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockMirrorSnapshotImageMeta : public TestMockFixture {
+public:
+ typedef ImageMeta<MockTestImageCtx> MockImageMeta;
+
+ void expect_metadata_get(MockTestImageCtx& mock_image_ctx,
+ const std::string& mirror_uuid,
+ const std::string& value, int r) {
+ bufferlist in_bl;
+ ceph::encode(util::get_image_meta_key(mirror_uuid), in_bl);
+
+ bufferlist out_bl;
+ ceph::encode(value, out_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("metadata_get"), ContentsEqual(in_bl), _, _, _))
+ .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(out_bl)),
+ Return(r)));
+ }
+
+ void expect_metadata_set(MockTestImageCtx& mock_image_ctx,
+ const std::string& mirror_uuid,
+ const std::string& value, int r) {
+ bufferlist value_bl;
+ value_bl.append(value);
+
+ bufferlist in_bl;
+ ceph::encode(
+ std::map<std::string, bufferlist>{
+ {util::get_image_meta_key(mirror_uuid), value_bl}},
+ in_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("metadata_set"), ContentsEqual(in_bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+};
+
+TEST_F(TestMockMirrorSnapshotImageMeta, Load) {
+ librbd::ImageCtx* image_ctx;
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_metadata_get(mock_image_ctx, "mirror uuid",
+ "{\"resync_requested\": true}", 0);
+
+ MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
+ C_SaferCond ctx;
+ mock_image_meta.load(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotImageMeta, LoadError) {
+ librbd::ImageCtx* image_ctx;
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_metadata_get(mock_image_ctx, "mirror uuid",
+ "{\"resync_requested\": true}", -EINVAL);
+
+ MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
+ C_SaferCond ctx;
+ mock_image_meta.load(&ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotImageMeta, LoadCorrupt) {
+ librbd::ImageCtx* image_ctx;
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_metadata_get(mock_image_ctx, "mirror uuid",
+ "\"resync_requested\": true}", 0);
+
+ MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
+ C_SaferCond ctx;
+ mock_image_meta.load(&ctx);
+ ASSERT_EQ(-EBADMSG, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotImageMeta, Save) {
+ librbd::ImageCtx* image_ctx;
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_metadata_set(mock_image_ctx, "mirror uuid",
+ "{\"resync_requested\": true}", 0);
+
+ MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
+ mock_image_meta.resync_requested = true;
+
+ C_SaferCond ctx;
+ mock_image_meta.save(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ // should have sent image-update notification
+ ASSERT_TRUE(image_ctx->state->is_refresh_required());
+}
+
+TEST_F(TestMockMirrorSnapshotImageMeta, SaveError) {
+ librbd::ImageCtx* image_ctx;
+ ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+ MockTestImageCtx mock_image_ctx(*image_ctx);
+
+ InSequence seq;
+ expect_metadata_set(mock_image_ctx, "mirror uuid",
+ "{\"resync_requested\": false}", -EINVAL);
+
+ MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
+
+ C_SaferCond ctx;
+ mock_image_meta.save(&ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
diff --git a/src/test/librbd/mirror/snapshot/test_mock_PromoteRequest.cc b/src/test/librbd/mirror/snapshot/test_mock_PromoteRequest.cc
new file mode 100644
index 000000000..af9c34933
--- /dev/null
+++ b/src/test/librbd/mirror/snapshot/test_mock_PromoteRequest.cc
@@ -0,0 +1,389 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/stringify.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/image/ListWatchersRequest.h"
+#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
+#include "librbd/mirror/snapshot/CreatePrimaryRequest.h"
+#include "librbd/mirror/snapshot/PromoteRequest.h"
+#include "librbd/mirror/snapshot/Utils.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct ListWatchersRequest<MockTestImageCtx> {
+ std::list<obj_watch_t> *watchers;
+ Context* on_finish = nullptr;
+ static ListWatchersRequest* s_instance;
+ static ListWatchersRequest *create(MockTestImageCtx &image_ctx, int flags,
+ std::list<obj_watch_t> *watchers,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->watchers = watchers;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ ListWatchersRequest() {
+ s_instance = this;
+ }
+};
+
+ListWatchersRequest<MockTestImageCtx>* ListWatchersRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
+namespace mirror {
+namespace snapshot {
+namespace util {
+
+namespace {
+
+struct Mock {
+ static Mock* s_instance;
+
+ Mock() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD5(can_create_primary_snapshot,
+ bool(librbd::MockTestImageCtx *, bool, bool, bool*, uint64_t *));
+};
+
+Mock *Mock::s_instance = nullptr;
+
+} // anonymous namespace
+
+template<> bool can_create_primary_snapshot(librbd::MockTestImageCtx *image_ctx,
+ bool demoted, bool force,
+ bool* requires_orphan,
+ uint64_t *rollback_snap_id) {
+ return Mock::s_instance->can_create_primary_snapshot(image_ctx, demoted,
+ force, requires_orphan,
+ rollback_snap_id);
+}
+
+} // namespace util
+
+template <>
+struct CreateNonPrimaryRequest<MockTestImageCtx> {
+ std::string primary_mirror_uuid;
+ uint64_t primary_snap_id = CEPH_NOSNAP;
+ Context* on_finish = nullptr;
+ static CreateNonPrimaryRequest* s_instance;
+ static CreateNonPrimaryRequest *create(MockTestImageCtx *image_ctx,
+ bool demoted,
+ const std::string &primary_mirror_uuid,
+ uint64_t primary_snap_id,
+ SnapSeqs snap_seqs,
+ const ImageState &image_state,
+ uint64_t *snap_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->primary_mirror_uuid = primary_mirror_uuid;
+ s_instance->primary_snap_id = primary_snap_id;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ CreateNonPrimaryRequest() {
+ s_instance = this;
+ }
+};
+
+CreateNonPrimaryRequest<MockTestImageCtx>* CreateNonPrimaryRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct CreatePrimaryRequest<MockTestImageCtx> {
+ bool demoted = false;
+ bool force = false;
+ Context* on_finish = nullptr;
+ static CreatePrimaryRequest* s_instance;
+ static CreatePrimaryRequest *create(MockTestImageCtx *image_ctx,
+ const std::string& global_image_id,
+ uint64_t clean_since_snap_id,
+ uint64_t snap_create_flags,
+ uint32_t flags, uint64_t *snap_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->demoted = ((flags & CREATE_PRIMARY_FLAG_DEMOTED) != 0);
+ s_instance->force = ((flags & CREATE_PRIMARY_FLAG_FORCE) != 0);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ CreatePrimaryRequest() {
+ s_instance = this;
+ }
+};
+
+CreatePrimaryRequest<MockTestImageCtx>* CreatePrimaryRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/mirror/snapshot/PromoteRequest.cc"
+template class librbd::mirror::snapshot::PromoteRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class TestMockMirrorSnapshotPromoteRequest : public TestMockFixture {
+public:
+ typedef librbd::image::ListWatchersRequest<MockTestImageCtx> MockListWatchersRequest;
+ typedef PromoteRequest<MockTestImageCtx> MockPromoteRequest;
+ typedef CreateNonPrimaryRequest<MockTestImageCtx> MockCreateNonPrimaryRequest;
+ typedef CreatePrimaryRequest<MockTestImageCtx> MockCreatePrimaryRequest;
+ typedef util::Mock MockUtils;
+
+ void expect_can_create_primary_snapshot(MockUtils &mock_utils, bool force,
+ bool requires_orphan,
+ uint64_t rollback_snap_id,
+ bool result) {
+ EXPECT_CALL(mock_utils,
+ can_create_primary_snapshot(_, false, force, _, _))
+ .WillOnce(DoAll(
+ WithArgs<3,4 >(Invoke(
+ [requires_orphan, rollback_snap_id]
+ (bool* orphan, uint64_t *snap_id) {
+ *orphan = requires_orphan;
+ *snap_id = rollback_snap_id;
+ })),
+ Return(result)));
+ }
+
+ void expect_create_orphan_snapshot(
+ MockTestImageCtx &mock_image_ctx,
+ MockCreateNonPrimaryRequest &mock_create_non_primary_request, int r) {
+ EXPECT_CALL(mock_create_non_primary_request, send())
+ .WillOnce(
+ Invoke([&mock_image_ctx, &mock_create_non_primary_request, r]() {
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_create_non_primary_request.on_finish, r);
+ }));
+ }
+
+ void expect_list_watchers(
+ MockTestImageCtx &mock_image_ctx,
+ MockListWatchersRequest &mock_list_watchers_request,
+ const std::list<obj_watch_t> &watchers, int r) {
+ EXPECT_CALL(mock_list_watchers_request, send())
+ .WillOnce(
+ Invoke([&mock_image_ctx, &mock_list_watchers_request, watchers, r]() {
+ *mock_list_watchers_request.watchers = watchers;
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_list_watchers_request.on_finish, r);
+ }));
+ }
+
+ void expect_acquire_lock(MockTestImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.exclusive_lock == nullptr) {
+ return;
+ }
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillOnce(Return(false));
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, block_requests(_));
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, acquire_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ if (r == 0) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillOnce(Return(true));
+ }
+ }
+
+ void expect_get_snap_info(MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, const SnapInfo* snap_info) {
+ EXPECT_CALL(mock_image_ctx, get_snap_info(snap_id))
+ .WillOnce(Return(snap_info));
+ }
+
+ void expect_rollback(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
+ const SnapInfo* snap_info, int r) {
+ expect_get_snap_info(mock_image_ctx, snap_id, snap_info);
+ EXPECT_CALL(*mock_image_ctx.operations,
+ execute_snap_rollback(snap_info->snap_namespace,
+ snap_info->name, _, _))
+ .WillOnce(WithArg<3>(CompleteContext(
+ r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ void expect_create_promote_snapshot(
+ MockTestImageCtx &mock_image_ctx,
+ MockCreatePrimaryRequest &mock_create_primary_request, int r) {
+ EXPECT_CALL(mock_create_primary_request, send())
+ .WillOnce(
+ Invoke([&mock_image_ctx, &mock_create_primary_request, r]() {
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_create_primary_request.on_finish, r);
+ }));
+ }
+
+ void expect_release_lock(MockTestImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.exclusive_lock == nullptr) {
+ return;
+ }
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, unblock_requests());
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, release_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+};
+
+TEST_F(TestMockMirrorSnapshotPromoteRequest, Success) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, true, false, CEPH_NOSNAP,
+ true);
+ MockCreatePrimaryRequest mock_create_primary_request;
+ expect_create_promote_snapshot(mock_image_ctx, mock_create_primary_request,
+ 0);
+ C_SaferCond ctx;
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotPromoteRequest, SuccessForce) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ InSequence seq;
+
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, true, true, CEPH_NOSNAP, true);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_orphan_snapshot(mock_image_ctx, mock_create_non_primary_request,
+ 0);
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_watchers(mock_image_ctx, mock_list_watchers_request, {}, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ SnapInfo snap_info = {"snap", cls::rbd::MirrorSnapshotNamespace{}, 0,
+ {}, 0, 0, {}};
+ MockCreatePrimaryRequest mock_create_primary_request;
+ expect_create_promote_snapshot(mock_image_ctx, mock_create_primary_request,
+ 0);
+ expect_release_lock(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotPromoteRequest, SuccessRollback) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ InSequence seq;
+
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, true, false, 123, true);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_orphan_snapshot(mock_image_ctx, mock_create_non_primary_request,
+ 0);
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_watchers(mock_image_ctx, mock_list_watchers_request, {}, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ SnapInfo snap_info = {"snap", cls::rbd::MirrorSnapshotNamespace{}, 0,
+ {}, 0, 0, {}};
+ expect_rollback(mock_image_ctx, 123, &snap_info, 0);
+ MockCreatePrimaryRequest mock_create_primary_request;
+ expect_create_promote_snapshot(mock_image_ctx, mock_create_primary_request,
+ 0);
+ expect_release_lock(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotPromoteRequest, ErrorCannotRollback) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, true, false, CEPH_NOSNAP,
+ false);
+
+ C_SaferCond ctx;
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
diff --git a/src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc b/src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc
new file mode 100644
index 000000000..869bdecff
--- /dev/null
+++ b/src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc
@@ -0,0 +1,501 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.cc"
+template class librbd::mirror::snapshot::UnlinkPeerRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockMirrorSnapshotUnlinkPeerRequest : public TestMockFixture {
+public:
+ typedef UnlinkPeerRequest<MockTestImageCtx> MockUnlinkPeerRequest;
+
+ uint64_t m_snap_seq = 0;
+
+ uint64_t snap_create(MockTestImageCtx &mock_image_ctx,
+ const cls::rbd::SnapshotNamespace &ns,
+ const std::string& snap_name) {
+ EXPECT_TRUE(mock_image_ctx.snap_info.insert(
+ {++m_snap_seq,
+ SnapInfo{snap_name, ns, 0, {}, 0, 0, {}}}).second);
+ return m_snap_seq;
+ }
+
+ void expect_get_snap_info(MockTestImageCtx &mock_image_ctx,
+ librados::snap_t snap_id) {
+ EXPECT_CALL(mock_image_ctx, get_snap_info(snap_id))
+ .WillRepeatedly(Invoke([&mock_image_ctx](
+ librados::snap_t snap_id) -> librbd::SnapInfo * {
+ auto it = mock_image_ctx.snap_info.find(snap_id);
+ if (it == mock_image_ctx.snap_info.end()) {
+ return nullptr;
+ }
+ return &it->second;
+ }));
+ }
+
+ void expect_is_refresh_required(MockTestImageCtx &mock_image_ctx,
+ bool refresh_required) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(refresh_required));
+ }
+
+ void expect_refresh_image(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_unlink_peer(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
+ const std::string &peer_uuid, int r) {
+ using ceph::encode;
+ bufferlist bl;
+ encode(snapid_t{snap_id}, bl);
+ encode(peer_uuid, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("mirror_image_snapshot_unlink_peer"),
+ ContentsEqual(bl), _, _, _))
+ .WillOnce(Invoke([&mock_image_ctx, snap_id, peer_uuid, r](auto&&... args) -> int {
+ if (r == 0) {
+ auto it = mock_image_ctx.snap_info.find(snap_id);
+ EXPECT_NE(it, mock_image_ctx.snap_info.end());
+ auto info =
+ std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &it->second.snap_namespace);
+ EXPECT_NE(nullptr, info);
+ EXPECT_NE(0, info->mirror_peer_uuids.erase(
+ peer_uuid));
+ }
+ return r;
+ }));
+ }
+
+ void expect_notify_update(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(mock_image_ctx, notify_update(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_remove_snapshot(MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, snap_remove(_, _, _))
+ .WillOnce(Invoke([&mock_image_ctx, snap_id, r](
+ const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &snap_name, Context *on_finish) {
+ if (r == 0) {
+ auto it = mock_image_ctx.snap_info.find(snap_id);
+ EXPECT_NE(it, mock_image_ctx.snap_info.end());
+ EXPECT_EQ(it->second.snap_namespace, snap_namespace);
+ EXPECT_EQ(it->second.name, snap_name);
+ mock_image_ctx.snap_info.erase(it);
+ }
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ on_finish, r);
+ }));
+ }
+};
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, Success) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer1_uuid", "peer2_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+ expect_unlink_peer(mock_image_ctx, snap_id, "peer1_uuid", 0);
+ expect_notify_update(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshot) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+ snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+ expect_remove_snapshot(mock_image_ctx, snap_id, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshotNotAllowed) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+ snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+ expect_unlink_peer(mock_image_ctx, snap_id, "peer_uuid", 0);
+ expect_notify_update(mock_image_ctx, 0);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+ false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotRemoveEmptyPeers) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+ ns.mirror_peer_uuids = {"peer_uuid"};
+ snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+ expect_remove_snapshot(mock_image_ctx, snap_id, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotRemoveEmptyPeersNotAllowed) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+ ns.mirror_peer_uuids = {"peer_uuid"};
+ snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+ false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotDNE) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_get_snap_info(mock_image_ctx, 123);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, PeerDNE) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "unknown_peer",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, MultiPeerPeerDNE) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer1_uuid", "peer2_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "unknown_peer",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, InvalidSnapshot) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::UserSnapshotNamespace ns;
+ auto snap_id = snap_create(mock_image_ctx, ns, "user_snap");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RefreshError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, UnlinkError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer1_uuid", "peer2_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, false);
+ expect_unlink_peer(mock_image_ctx, snap_id, "peer1_uuid", -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, UnlinkErrorRestart) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+ snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, 0);
+ expect_unlink_peer(mock_image_ctx, snap_id, "peer_uuid", -ERESTART);
+ expect_refresh_image(mock_image_ctx, 0);
+ expect_remove_snapshot(mock_image_ctx, snap_id, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+ false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, NotifyError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer1_uuid", "peer2_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, false);
+ expect_unlink_peer(mock_image_ctx, snap_id, "peer1_uuid", 0);
+ expect_notify_update(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshotError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+ "", CEPH_NOSNAP};
+ auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+ snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+ expect_get_snap_info(mock_image_ctx, snap_id);
+
+ InSequence seq;
+
+ expect_is_refresh_required(mock_image_ctx, false);
+ expect_remove_snapshot(mock_image_ctx, snap_id, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
diff --git a/src/test/librbd/mirror/snapshot/test_mock_Utils.cc b/src/test/librbd/mirror/snapshot/test_mock_Utils.cc
new file mode 100644
index 000000000..410dc3eba
--- /dev/null
+++ b/src/test/librbd/mirror/snapshot/test_mock_Utils.cc
@@ -0,0 +1,177 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/stringify.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
+#include "librbd/mirror/snapshot/Utils.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/mirror/snapshot/Utils.cc"
+template bool librbd::mirror::snapshot::util::can_create_primary_snapshot(
+ librbd::MockTestImageCtx *image_ctx, bool demoted, bool force,
+ bool* requires_orphan, uint64_t *rollback_snap_id);
+template bool librbd::mirror::snapshot::util::can_create_non_primary_snapshot(
+ librbd::MockTestImageCtx *image_ctx);
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockMirrorSnapshotUtils : public TestMockFixture {
+public:
+ uint64_t m_snap_seq = 0;
+
+ uint64_t snap_create(MockTestImageCtx &mock_image_ctx,
+ const cls::rbd::SnapshotNamespace &ns,
+ const std::string& snap_name) {
+ EXPECT_TRUE(mock_image_ctx.snap_info.insert(
+ {++m_snap_seq,
+ SnapInfo{snap_name, ns, 0, {}, 0, 0, {}}}).second);
+ return m_snap_seq;
+ }
+};
+
+TEST_F(TestMockMirrorSnapshotUtils, CanCreatePrimarySnapshot) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ // no previous mirror snapshots found
+ bool requires_orphan;
+ uint64_t rollback_snap_id;
+ ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
+ &requires_orphan,
+ &rollback_snap_id));
+ ASSERT_FALSE(requires_orphan);
+ ASSERT_EQ(rollback_snap_id, CEPH_NOSNAP);
+
+ cls::rbd::MirrorSnapshotNamespace nns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "mirror_uuid", 123};
+ nns.complete = true;
+ auto copied_snap_id = snap_create(mock_image_ctx, nns, "NPS1");
+
+ // without force, previous snapshot is non-primary
+ ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
+ nullptr, nullptr));
+
+ // demoted, previous snapshot is non-primary
+ ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, true, true,
+ nullptr, nullptr));
+
+ // previous non-primary snapshot is copied
+ ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
+ &requires_orphan,
+ &rollback_snap_id));
+ ASSERT_TRUE(requires_orphan);
+ ASSERT_EQ(rollback_snap_id, CEPH_NOSNAP);
+
+ nns.complete = false;
+ snap_create(mock_image_ctx, nns, "NPS2");
+
+ // previous non-primary snapshot is not copied yet
+ ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
+ nullptr, nullptr));
+
+ // can rollback
+ ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
+ nullptr, &rollback_snap_id));
+ ASSERT_EQ(rollback_snap_id, copied_snap_id);
+
+ nns.state = cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED;
+ snap_create(mock_image_ctx, nns, "NPS3");
+
+ // previous non-primary snapshot is orphan
+ ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
+ nullptr, nullptr));
+
+ cls::rbd::MirrorSnapshotNamespace pns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {"uuid"}, "", CEPH_NOSNAP};
+ snap_create(mock_image_ctx, pns, "PS1");
+
+ // previous primary snapshot is demoted, no force
+ ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
+ nullptr, nullptr));
+
+ // previous primary snapshot is demoted, force
+ ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
+ nullptr, nullptr));
+
+ pns.state = cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY;
+ snap_create(mock_image_ctx, pns, "PS2");
+
+ // previous snapshot is not demoted primary
+ ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
+ nullptr, nullptr));
+}
+
+TEST_F(TestMockMirrorSnapshotUtils, CanCreateNonPrimarySnapshot) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ // no previous mirror snapshots found
+ ASSERT_TRUE(util::can_create_non_primary_snapshot(&mock_image_ctx));
+
+ cls::rbd::MirrorSnapshotNamespace nns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "mirror_uuid", 123};
+ snap_create(mock_image_ctx, nns, "NPS1");
+
+ // previous non-primary snapshot is not copied yet
+ ASSERT_FALSE(util::can_create_non_primary_snapshot(&mock_image_ctx));
+
+ nns.complete = true;
+ snap_create(mock_image_ctx, nns, "NPS2");
+
+ // previous non-primary snapshot is copied
+ ASSERT_TRUE(util::can_create_non_primary_snapshot(&mock_image_ctx));
+
+ cls::rbd::MirrorSnapshotNamespace pns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"uuid"}, "", CEPH_NOSNAP};
+ snap_create(mock_image_ctx, pns, "PS1");
+
+ // previous primary snapshot is not in demoted state
+ ASSERT_FALSE(util::can_create_non_primary_snapshot(&mock_image_ctx));
+
+ pns.state = cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED;
+ snap_create(mock_image_ctx, pns, "PS2");
+
+ // previous primary snapshot is in demoted state
+ ASSERT_TRUE(util::can_create_non_primary_snapshot(&mock_image_ctx));
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
diff --git a/src/test/librbd/mirror/test_mock_DisableRequest.cc b/src/test/librbd/mirror/test_mock_DisableRequest.cc
new file mode 100644
index 000000000..823884fe5
--- /dev/null
+++ b/src/test/librbd/mirror/test_mock_DisableRequest.cc
@@ -0,0 +1,694 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/journal/PromoteRequest.h"
+#include "librbd/mirror/DisableRequest.h"
+#include "librbd/mirror/GetInfoRequest.h"
+#include "librbd/mirror/ImageRemoveRequest.h"
+#include "librbd/mirror/ImageStateUpdateRequest.h"
+#include "librbd/mirror/snapshot/PromoteRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct PromoteRequest<librbd::MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static PromoteRequest *s_instance;
+ static PromoteRequest *create(librbd::MockTestImageCtx *, bool force,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ PromoteRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+PromoteRequest<librbd::MockTestImageCtx> *PromoteRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+
+namespace mirror {
+template <>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+ cls::rbd::MirrorImage *mirror_image;
+ PromotionState *promotion_state;
+ Context *on_finish = nullptr;
+ static GetInfoRequest *s_instance;
+ static GetInfoRequest *create(librbd::MockTestImageCtx &,
+ cls::rbd::MirrorImage *mirror_image,
+ PromotionState *promotion_state,
+ std::string* primary_mirror_uuid,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->mirror_image = mirror_image;
+ s_instance->promotion_state = promotion_state;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetInfoRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct ImageRemoveRequest<librbd::MockTestImageCtx> {
+ static ImageRemoveRequest* s_instance;
+ Context* on_finish = nullptr;
+
+ static ImageRemoveRequest* create(
+ librados::IoCtx& io_ctx,
+ const std::string& global_image_id,
+ const std::string& image_id,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ ImageRemoveRequest() {
+ s_instance = this;
+ }
+};
+
+template <>
+struct ImageStateUpdateRequest<librbd::MockTestImageCtx> {
+ static ImageStateUpdateRequest* s_instance;
+ Context* on_finish = nullptr;
+
+ static ImageStateUpdateRequest* create(
+ librados::IoCtx& io_ctx,
+ const std::string& image_id,
+ cls::rbd::MirrorImageState mirror_image_state,
+ const cls::rbd::MirrorImage& mirror_image,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ ImageStateUpdateRequest() {
+ s_instance = this;
+ }
+};
+
+GetInfoRequest<librbd::MockTestImageCtx> *GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+ImageRemoveRequest<librbd::MockTestImageCtx> *ImageRemoveRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+ImageStateUpdateRequest<librbd::MockTestImageCtx> *ImageStateUpdateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace snapshot {
+
+template <>
+struct PromoteRequest<librbd::MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static PromoteRequest *s_instance;
+ static PromoteRequest *create(librbd::MockTestImageCtx*,
+ const std::string& global_image_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ PromoteRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+PromoteRequest<librbd::MockTestImageCtx> *PromoteRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/mirror/DisableRequest.cc"
+template class librbd::mirror::DisableRequest<librbd::MockTestImageCtx>;
+
+ACTION_P(TestFeatures, image_ctx) {
+ return ((image_ctx->features & arg0) != 0);
+}
+
+namespace librbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockMirrorDisableRequest : public TestMockFixture {
+public:
+ typedef DisableRequest<MockTestImageCtx> MockDisableRequest;
+ typedef Journal<MockTestImageCtx> MockJournal;
+ typedef journal::PromoteRequest<MockTestImageCtx> MockJournalPromoteRequest;
+ typedef mirror::GetInfoRequest<MockTestImageCtx> MockGetInfoRequest;
+ typedef mirror::ImageRemoveRequest<MockTestImageCtx> MockImageRemoveRequest;
+ typedef mirror::ImageStateUpdateRequest<MockTestImageCtx> MockImageStateUpdateRequest;
+ typedef mirror::snapshot::PromoteRequest<MockTestImageCtx> MockSnapshotPromoteRequest;
+
+ void expect_get_mirror_info(MockTestImageCtx &mock_image_ctx,
+ MockGetInfoRequest &mock_get_info_request,
+ const cls::rbd::MirrorImage &mirror_image,
+ PromotionState promotion_state, int r) {
+
+ EXPECT_CALL(mock_get_info_request, send())
+ .WillOnce(
+ Invoke([&mock_image_ctx, &mock_get_info_request, mirror_image,
+ promotion_state, r]() {
+ if (r == 0) {
+ *mock_get_info_request.mirror_image = mirror_image;
+ *mock_get_info_request.promotion_state = promotion_state;
+ }
+ mock_image_ctx.op_work_queue->queue(
+ mock_get_info_request.on_finish, r);
+ }));
+ }
+
+ void expect_mirror_image_state_update(
+ MockTestImageCtx &mock_image_ctx,
+ MockImageStateUpdateRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(
+ Invoke([&mock_image_ctx, &mock_request, r]() {
+ mock_image_ctx.op_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_mirror_image_remove(
+ MockTestImageCtx &mock_image_ctx,
+ MockImageRemoveRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(
+ Invoke([&mock_image_ctx, &mock_request, r]() {
+ mock_image_ctx.op_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
+ void expect_journal_client_list(MockTestImageCtx &mock_image_ctx,
+ const std::set<cls::journal::Client> &clients,
+ int r) {
+ bufferlist bl;
+ using ceph::encode;
+ encode(clients, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(::journal::Journaler::header_oid(mock_image_ctx.id),
+ _, StrEq("journal"), StrEq("client_list"), _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)),
+ Return(r)));
+ }
+
+ void expect_journal_client_unregister(MockTestImageCtx &mock_image_ctx,
+ const std::string &client_id,
+ int r) {
+ bufferlist bl;
+ using ceph::encode;
+ encode(client_id, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(::journal::Journaler::header_oid(mock_image_ctx.id),
+ _, StrEq("journal"), StrEq("client_unregister"),
+ ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_journal_promote(MockTestImageCtx &mock_image_ctx,
+ MockJournalPromoteRequest &mock_promote_request,
+ int r) {
+ EXPECT_CALL(mock_promote_request, send())
+ .WillOnce(FinishRequest(&mock_promote_request, r, &mock_image_ctx));
+ }
+
+ void expect_snapshot_promote(MockTestImageCtx &mock_image_ctx,
+ MockSnapshotPromoteRequest &mock_promote_request,
+ int r) {
+ EXPECT_CALL(mock_promote_request, send())
+ .WillOnce(FinishRequest(&mock_promote_request, r, &mock_image_ctx));
+ }
+
+ void expect_is_refresh_required(MockTestImageCtx &mock_image_ctx,
+ bool refresh_required) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(refresh_required));
+ }
+
+ void expect_refresh_image(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_snap_remove(MockTestImageCtx &mock_image_ctx,
+ const std::string &snap_name, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, snap_remove(_, StrEq(snap_name), _))
+ .WillOnce(WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ template <typename T>
+ bufferlist encode(const T &t) {
+ using ceph::encode;
+ bufferlist bl;
+ encode(t, bl);
+ return bl;
+ }
+
+};
+
+TEST_F(TestMockMirrorDisableRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, "snap 1", 0);
+ expect_snap_remove(mock_image_ctx, "snap 2", 0);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_client_list(
+ mock_image_ctx, {
+ {"", encode(journal::ClientData{journal::ImageClientMeta{}})},
+ {"peer 1", encode(journal::ClientData{journal::MirrorPeerClientMeta{}})},
+ {"peer 2", encode(journal::ClientData{journal::MirrorPeerClientMeta{
+ "remote image id", {{cls::rbd::UserSnapshotNamespace(), "snap 1", boost::optional<uint64_t>(0)},
+ {cls::rbd::UserSnapshotNamespace(), "snap 2", boost::optional<uint64_t>(0)}}}
+ })}
+ }, 0);
+ expect_journal_client_unregister(mock_image_ctx, "peer 1", 0);
+ expect_journal_client_unregister(mock_image_ctx, "peer 2", 0);
+ expect_journal_client_list(mock_image_ctx, {}, 0);
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove(
+ mock_image_ctx, mock_image_remove_request, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, SuccessNoRemove) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_client_list(mock_image_ctx, {}, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, SuccessNonPrimary) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockJournalPromoteRequest mock_promote_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_promote(mock_image_ctx, mock_promote_request, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
+ expect_journal_client_list(mock_image_ctx, {}, 0);
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove(
+ mock_image_ctx, mock_image_remove_request, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, NonPrimaryError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, false, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, GetMirrorInfoError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, MirrorImageSetError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, -ENOENT);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, JournalPromoteError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockJournalPromoteRequest mock_promote_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_promote(mock_image_ctx, mock_promote_request, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, JournalClientListError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_client_list(mock_image_ctx, {}, -EBADMSG);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EBADMSG, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, SnapRemoveError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, "snap 1", 0);
+ expect_snap_remove(mock_image_ctx, "snap 2", -EPERM);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_client_list(
+ mock_image_ctx, {
+ {"", encode(journal::ClientData{journal::ImageClientMeta{}})},
+ {"peer 1", encode(journal::ClientData{journal::MirrorPeerClientMeta{}})},
+ {"peer 2", encode(journal::ClientData{journal::MirrorPeerClientMeta{
+ "remote image id", {{cls::rbd::UserSnapshotNamespace(), "snap 1", boost::optional<uint64_t>(0)},
+ {cls::rbd::UserSnapshotNamespace(), "snap 2", boost::optional<uint64_t>(0)}}}
+ })}
+ }, 0);
+ expect_journal_client_unregister(mock_image_ctx, "peer 1", 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, JournalClientUnregisterError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, "snap 1", 0);
+ expect_snap_remove(mock_image_ctx, "snap 2", 0);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_client_list(
+ mock_image_ctx, {
+ {"", encode(journal::ClientData{journal::ImageClientMeta{}})},
+ {"peer 1", encode(journal::ClientData{journal::MirrorPeerClientMeta{}})},
+ {"peer 2", encode(journal::ClientData{journal::MirrorPeerClientMeta{
+ "remote image id", {{cls::rbd::UserSnapshotNamespace(), "snap 1", boost::optional<uint64_t>(0)},
+ {cls::rbd::UserSnapshotNamespace(), "snap 2", boost::optional<uint64_t>(0)}}}
+ })}
+ }, 0);
+ expect_journal_client_unregister(mock_image_ctx, "peer 1", -EINVAL);
+ expect_journal_client_unregister(mock_image_ctx, "peer 2", 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, SnapshotPromoteError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSnapshotPromoteRequest mock_promote_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_snapshot_promote(mock_image_ctx, mock_promote_request, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, RefreshError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSnapshotPromoteRequest mock_promote_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_snapshot_promote(mock_image_ctx, mock_promote_request, 0);
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, MirrorImageRemoveError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_journal_client_list(mock_image_ctx, {}, 0);
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove(
+ mock_image_ctx, mock_image_remove_request, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace mirror
+} // namespace librbd
diff --git a/src/test/librbd/mock/MockContextWQ.h b/src/test/librbd/mock/MockContextWQ.h
new file mode 100644
index 000000000..f900b627d
--- /dev/null
+++ b/src/test/librbd/mock/MockContextWQ.h
@@ -0,0 +1,19 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H
+#define CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H
+
+#include "gmock/gmock.h"
+
+struct Context;
+
+namespace librbd {
+
+struct MockContextWQ {
+ MOCK_METHOD2(queue, void(Context *, int r));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H
diff --git a/src/test/librbd/mock/MockExclusiveLock.h b/src/test/librbd/mock/MockExclusiveLock.h
new file mode 100644
index 000000000..db675abf0
--- /dev/null
+++ b/src/test/librbd/mock/MockExclusiveLock.h
@@ -0,0 +1,50 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_H
+#define CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_H
+
+#include "common/RefCountedObj.h"
+#include "include/int_types.h"
+#include "include/rados/librados.hpp"
+#include "librbd/exclusive_lock/Policy.h"
+#include "librbd/io/Types.h"
+#include "gmock/gmock.h"
+
+class Context;
+
+namespace librbd {
+
+struct MockExclusiveLock {
+ MOCK_CONST_METHOD0(is_lock_owner, bool());
+
+ MOCK_METHOD2(init, void(uint64_t features, Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ MOCK_METHOD1(reacquire_lock, void(Context*));
+ MOCK_METHOD1(try_acquire_lock, void(Context*));
+
+ MOCK_METHOD1(block_requests, void(int));
+ MOCK_METHOD0(unblock_requests, void());
+
+ MOCK_METHOD1(acquire_lock, void(Context *));
+ MOCK_METHOD1(release_lock, void(Context *));
+
+ MOCK_METHOD2(accept_request, bool(exclusive_lock::OperationRequestType,
+ int *));
+ MOCK_METHOD0(accept_ops, bool());
+ MOCK_METHOD0(get_unlocked_op_error, int());
+
+ MOCK_METHOD3(set_require_lock, void(bool init_shutdown, io::Direction,
+ Context*));
+ MOCK_METHOD1(unset_require_lock, void(io::Direction));
+
+ MOCK_METHOD1(start_op, Context*(int*));
+
+ void get() {}
+ void put() {}
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_H
diff --git a/src/test/librbd/mock/MockImageCtx.cc b/src/test/librbd/mock/MockImageCtx.cc
new file mode 100644
index 000000000..3613c6e60
--- /dev/null
+++ b/src/test/librbd/mock/MockImageCtx.cc
@@ -0,0 +1,149 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/neorados/RADOS.hpp"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockSafeTimer.h"
+#include "test/librbd/mock/crypto/MockEncryptionFormat.h"
+#include "librbd/io/AsyncOperation.h"
+
+static MockSafeTimer *s_timer;
+static ceph::mutex *s_timer_lock;
+
+namespace librbd {
+
+MockImageCtx* MockImageCtx::s_instance = nullptr;
+
+MockImageCtx::MockImageCtx(librbd::ImageCtx &image_ctx)
+ : image_ctx(&image_ctx),
+ cct(image_ctx.cct),
+ perfcounter(image_ctx.perfcounter),
+ snap_namespace(image_ctx.snap_namespace),
+ snap_name(image_ctx.snap_name),
+ snap_id(image_ctx.snap_id),
+ snap_exists(image_ctx.snap_exists),
+ snapc(image_ctx.snapc),
+ snaps(image_ctx.snaps),
+ snap_info(image_ctx.snap_info),
+ snap_ids(image_ctx.snap_ids),
+ old_format(image_ctx.old_format),
+ read_only(image_ctx.read_only),
+ read_only_flags(image_ctx.read_only_flags),
+ read_only_mask(image_ctx.read_only_mask),
+ clone_copy_on_read(image_ctx.clone_copy_on_read),
+ lockers(image_ctx.lockers),
+ exclusive_locked(image_ctx.exclusive_locked),
+ lock_tag(image_ctx.lock_tag),
+ asio_engine(image_ctx.asio_engine),
+ rados_api(image_ctx.rados_api),
+ owner_lock(image_ctx.owner_lock),
+ image_lock(image_ctx.image_lock),
+ timestamp_lock(image_ctx.timestamp_lock),
+ async_ops_lock(image_ctx.async_ops_lock),
+ copyup_list_lock(image_ctx.copyup_list_lock),
+ order(image_ctx.order),
+ size(image_ctx.size),
+ features(image_ctx.features),
+ flags(image_ctx.flags),
+ op_features(image_ctx.op_features),
+ operations_disabled(image_ctx.operations_disabled),
+ stripe_unit(image_ctx.stripe_unit),
+ stripe_count(image_ctx.stripe_count),
+ object_prefix(image_ctx.object_prefix),
+ header_oid(image_ctx.header_oid),
+ id(image_ctx.id),
+ name(image_ctx.name),
+ parent_md(image_ctx.parent_md),
+ format_string(image_ctx.format_string),
+ group_spec(image_ctx.group_spec),
+ layout(image_ctx.layout),
+ io_image_dispatcher(new io::MockImageDispatcher()),
+ io_object_dispatcher(new io::MockObjectDispatcher()),
+ op_work_queue(new MockContextWQ()),
+ plugin_registry(new MockPluginRegistry()),
+ readahead_max_bytes(image_ctx.readahead_max_bytes),
+ event_socket(image_ctx.event_socket),
+ parent(NULL), operations(new MockOperations()),
+ state(new MockImageState()),
+ image_watcher(NULL), object_map(NULL),
+ exclusive_lock(NULL), journal(NULL),
+ trace_endpoint(image_ctx.trace_endpoint),
+ sparse_read_threshold_bytes(image_ctx.sparse_read_threshold_bytes),
+ discard_granularity_bytes(image_ctx.discard_granularity_bytes),
+ mirroring_replay_delay(image_ctx.mirroring_replay_delay),
+ non_blocking_aio(image_ctx.non_blocking_aio),
+ blkin_trace_all(image_ctx.blkin_trace_all),
+ enable_alloc_hint(image_ctx.enable_alloc_hint),
+ alloc_hint_flags(image_ctx.alloc_hint_flags),
+ read_flags(image_ctx.read_flags),
+ ignore_migrating(image_ctx.ignore_migrating),
+ enable_sparse_copyup(image_ctx.enable_sparse_copyup),
+ mtime_update_interval(image_ctx.mtime_update_interval),
+ atime_update_interval(image_ctx.atime_update_interval),
+ cache(image_ctx.cache),
+ config(image_ctx.config)
+{
+ md_ctx.dup(image_ctx.md_ctx);
+ data_ctx.dup(image_ctx.data_ctx);
+
+ if (image_ctx.image_watcher != NULL) {
+ image_watcher = new MockImageWatcher();
+ }
+}
+
+MockImageCtx::~MockImageCtx() {
+ wait_for_async_requests();
+ wait_for_async_ops();
+ image_ctx->md_ctx.aio_flush();
+ image_ctx->data_ctx.aio_flush();
+ image_ctx->op_work_queue->drain();
+ delete state;
+ delete operations;
+ delete image_watcher;
+ delete op_work_queue;
+ delete plugin_registry;
+ delete io_image_dispatcher;
+ delete io_object_dispatcher;
+}
+
+void MockImageCtx::set_timer_instance(MockSafeTimer *timer,
+ ceph::mutex *timer_lock) {
+ s_timer = timer;
+ s_timer_lock = timer_lock;
+}
+
+void MockImageCtx::get_timer_instance(CephContext *cct, MockSafeTimer **timer,
+ ceph::mutex **timer_lock) {
+ *timer = s_timer;
+ *timer_lock = s_timer_lock;
+}
+
+void MockImageCtx::wait_for_async_ops() {
+ io::AsyncOperation async_op;
+ async_op.start_op(*image_ctx);
+
+ C_SaferCond ctx;
+ async_op.flush(&ctx);
+ ctx.wait();
+
+ async_op.finish_op();
+}
+
+IOContext MockImageCtx::get_data_io_context() {
+ auto ctx = std::make_shared<neorados::IOContext>(
+ data_ctx.get_id(), data_ctx.get_namespace());
+ if (snap_id != CEPH_NOSNAP) {
+ ctx->read_snap(snap_id);
+ }
+ if (!snapc.snaps.empty()) {
+ ctx->write_snap_context(
+ {{snapc.seq, {snapc.snaps.begin(), snapc.snaps.end()}}});
+ }
+ return ctx;
+}
+
+IOContext MockImageCtx::duplicate_data_io_context() {
+ return std::make_shared<neorados::IOContext>(*get_data_io_context());
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h
new file mode 100644
index 000000000..8da257f44
--- /dev/null
+++ b/src/test/librbd/mock/MockImageCtx.h
@@ -0,0 +1,261 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H
+#define CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H
+
+#include "include/rados/librados.hpp"
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librbd/mock/MockExclusiveLock.h"
+#include "test/librbd/mock/MockImageState.h"
+#include "test/librbd/mock/MockImageWatcher.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librbd/mock/MockPluginRegistry.h"
+#include "test/librbd/mock/MockReadahead.h"
+#include "test/librbd/mock/io/MockImageDispatcher.h"
+#include "test/librbd/mock/io/MockObjectDispatcher.h"
+#include "common/WorkQueue.h"
+#include "common/zipkin_trace.h"
+#include "librbd/ImageCtx.h"
+#include "gmock/gmock.h"
+#include <string>
+
+class MockSafeTimer;
+
+namespace librbd {
+
+namespace operation {
+template <typename> class ResizeRequest;
+}
+
+
+namespace crypto {
+ class MockEncryptionFormat;
+}
+
+struct MockImageCtx {
+ static MockImageCtx *s_instance;
+ static MockImageCtx *create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MockImageCtx(librbd::ImageCtx &image_ctx);
+ virtual ~MockImageCtx();
+
+ void wait_for_async_ops();
+ void wait_for_async_requests() {
+ async_ops_lock.lock();
+ if (async_requests.empty()) {
+ async_ops_lock.unlock();
+ return;
+ }
+
+ C_SaferCond ctx;
+ async_requests_waiters.push_back(&ctx);
+ async_ops_lock.unlock();
+
+ ctx.wait();
+ }
+
+ MOCK_METHOD1(init_layout, void(int64_t));
+
+ MOCK_CONST_METHOD1(get_object_name, std::string(uint64_t));
+ MOCK_CONST_METHOD0(get_object_size, uint64_t());
+ MOCK_CONST_METHOD0(get_current_size, uint64_t());
+ MOCK_CONST_METHOD1(get_image_size, uint64_t(librados::snap_t));
+ MOCK_CONST_METHOD1(get_area_size, uint64_t(io::ImageArea));
+ MOCK_CONST_METHOD1(get_object_count, uint64_t(librados::snap_t));
+ MOCK_CONST_METHOD1(get_read_flags, int(librados::snap_t));
+ MOCK_CONST_METHOD2(get_flags, int(librados::snap_t in_snap_id,
+ uint64_t *flags));
+ MOCK_CONST_METHOD2(get_snap_id,
+ librados::snap_t(cls::rbd::SnapshotNamespace snap_namespace,
+ std::string in_snap_name));
+ MOCK_CONST_METHOD1(get_snap_info, const SnapInfo*(librados::snap_t));
+ MOCK_CONST_METHOD2(get_snap_name, int(librados::snap_t, std::string *));
+ MOCK_CONST_METHOD2(get_snap_namespace, int(librados::snap_t,
+ cls::rbd::SnapshotNamespace *out_snap_namespace));
+ MOCK_CONST_METHOD2(get_parent_spec, int(librados::snap_t in_snap_id,
+ cls::rbd::ParentImageSpec *pspec));
+ MOCK_CONST_METHOD1(get_parent_info, const ParentImageInfo*(librados::snap_t));
+ MOCK_CONST_METHOD2(get_parent_overlap, int(librados::snap_t in_snap_id,
+ uint64_t *raw_overlap));
+ MOCK_CONST_METHOD2(reduce_parent_overlap,
+ std::pair<uint64_t, io::ImageArea>(uint64_t, bool));
+ MOCK_CONST_METHOD4(prune_parent_extents,
+ uint64_t(std::vector<std::pair<uint64_t, uint64_t>>&,
+ io::ImageArea, uint64_t, bool));
+
+ MOCK_CONST_METHOD2(is_snap_protected, int(librados::snap_t in_snap_id,
+ bool *is_protected));
+ MOCK_CONST_METHOD2(is_snap_unprotected, int(librados::snap_t in_snap_id,
+ bool *is_unprotected));
+
+ MOCK_CONST_METHOD0(get_create_timestamp, utime_t());
+ MOCK_CONST_METHOD0(get_access_timestamp, utime_t());
+ MOCK_CONST_METHOD0(get_modify_timestamp, utime_t());
+
+ MOCK_METHOD1(set_access_timestamp, void(const utime_t at));
+ MOCK_METHOD1(set_modify_timestamp, void(const utime_t at));
+
+ MOCK_METHOD8(add_snap, void(cls::rbd::SnapshotNamespace in_snap_namespace,
+ std::string in_snap_name,
+ librados::snap_t id,
+ uint64_t in_size, const ParentImageInfo &parent,
+ uint8_t protection_status, uint64_t flags, utime_t timestamp));
+ MOCK_METHOD3(rm_snap, void(cls::rbd::SnapshotNamespace in_snap_namespace,
+ std::string in_snap_name,
+ librados::snap_t id));
+
+ MOCK_METHOD0(user_flushed, void());
+ MOCK_METHOD1(flush_copyup, void(Context *));
+
+ MOCK_CONST_METHOD1(test_features, bool(uint64_t test_features));
+ MOCK_CONST_METHOD2(test_features, bool(uint64_t test_features,
+ const ceph::shared_mutex &in_image_lock));
+
+ MOCK_CONST_METHOD1(test_op_features, bool(uint64_t op_features));
+
+ MOCK_METHOD1(cancel_async_requests, void(Context*));
+
+ MOCK_METHOD0(create_exclusive_lock, MockExclusiveLock*());
+ MOCK_METHOD1(create_object_map, MockObjectMap*(uint64_t));
+ MOCK_METHOD0(create_journal, MockJournal*());
+
+ MOCK_METHOD0(notify_update, void());
+ MOCK_METHOD1(notify_update, void(Context *));
+
+ MOCK_CONST_METHOD0(get_exclusive_lock_policy, exclusive_lock::Policy*());
+ MOCK_METHOD1(set_exclusive_lock_policy, void(exclusive_lock::Policy*));
+ MOCK_CONST_METHOD0(get_journal_policy, journal::Policy*());
+ MOCK_METHOD1(set_journal_policy, void(journal::Policy*));
+
+ MOCK_METHOD2(apply_metadata, int(const std::map<std::string, bufferlist> &,
+ bool));
+
+ MOCK_CONST_METHOD0(get_stripe_count, uint64_t());
+ MOCK_CONST_METHOD0(get_stripe_period, uint64_t());
+
+ MOCK_METHOD0(rebuild_data_io_context, void());
+ IOContext get_data_io_context();
+ IOContext duplicate_data_io_context();
+
+ static void set_timer_instance(MockSafeTimer *timer, ceph::mutex *timer_lock);
+ static void get_timer_instance(CephContext *cct, MockSafeTimer **timer,
+ ceph::mutex **timer_lock);
+
+ ImageCtx *image_ctx;
+ CephContext *cct;
+ PerfCounters *perfcounter;
+
+ cls::rbd::SnapshotNamespace snap_namespace;
+ std::string snap_name;
+ uint64_t snap_id;
+ bool snap_exists;
+
+ ::SnapContext snapc;
+ std::vector<librados::snap_t> snaps;
+ std::map<librados::snap_t, SnapInfo> snap_info;
+ std::map<ImageCtx::SnapKey, librados::snap_t, ImageCtx::SnapKeyComparator> snap_ids;
+
+ bool old_format;
+ bool read_only;
+ uint32_t read_only_flags;
+ uint32_t read_only_mask;
+
+ bool clone_copy_on_read;
+
+ std::map<rados::cls::lock::locker_id_t,
+ rados::cls::lock::locker_info_t> lockers;
+ bool exclusive_locked;
+ std::string lock_tag;
+
+ std::shared_ptr<AsioEngine> asio_engine;
+ neorados::RADOS& rados_api;
+
+ librados::IoCtx md_ctx;
+ librados::IoCtx data_ctx;
+
+ ceph::shared_mutex &owner_lock;
+ ceph::shared_mutex &image_lock;
+ ceph::shared_mutex &timestamp_lock;
+ ceph::mutex &async_ops_lock;
+ ceph::mutex &copyup_list_lock;
+
+ uint8_t order;
+ uint64_t size;
+ uint64_t features;
+ uint64_t flags;
+ uint64_t op_features;
+ bool operations_disabled;
+ uint64_t stripe_unit;
+ uint64_t stripe_count;
+ std::string object_prefix;
+ std::string header_oid;
+ std::string id;
+ std::string name;
+ ParentImageInfo parent_md;
+ MigrationInfo migration_info;
+ char *format_string;
+ cls::rbd::GroupSpec group_spec;
+
+ file_layout_t layout;
+
+ xlist<operation::ResizeRequest<MockImageCtx>*> resize_reqs;
+ xlist<AsyncRequest<MockImageCtx>*> async_requests;
+ std::list<Context*> async_requests_waiters;
+
+ std::map<uint64_t, io::CopyupRequest<MockImageCtx>*> copyup_list;
+
+ io::MockImageDispatcher *io_image_dispatcher;
+ io::MockObjectDispatcher *io_object_dispatcher;
+ MockContextWQ *op_work_queue;
+
+ MockPluginRegistry* plugin_registry;
+
+ MockReadahead readahead;
+ uint64_t readahead_max_bytes;
+
+ EventSocket &event_socket;
+
+ MockImageCtx *child = nullptr;
+ MockImageCtx *parent;
+ MockOperations *operations;
+ MockImageState *state;
+
+ MockImageWatcher *image_watcher;
+ MockObjectMap *object_map;
+ MockExclusiveLock *exclusive_lock;
+ MockJournal *journal;
+
+ ZTracer::Endpoint trace_endpoint;
+
+ std::unique_ptr<crypto::MockEncryptionFormat> encryption_format;
+
+ uint64_t sparse_read_threshold_bytes;
+ uint32_t discard_granularity_bytes;
+ int mirroring_replay_delay;
+ bool non_blocking_aio;
+ bool blkin_trace_all;
+ bool enable_alloc_hint;
+ uint32_t alloc_hint_flags;
+ uint32_t read_flags;
+ bool ignore_migrating;
+ bool enable_sparse_copyup;
+ uint64_t mtime_update_interval;
+ uint64_t atime_update_interval;
+ bool cache;
+
+ ConfigProxy config;
+ std::set<std::string> config_overrides;
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H
diff --git a/src/test/librbd/mock/MockImageState.h b/src/test/librbd/mock/MockImageState.h
new file mode 100644
index 000000000..f510c4a0f
--- /dev/null
+++ b/src/test/librbd/mock/MockImageState.h
@@ -0,0 +1,39 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_STATE_H
+#define CEPH_TEST_LIBRBD_MOCK_IMAGE_STATE_H
+
+#include <gmock/gmock.h>
+
+#include "cls/rbd/cls_rbd_types.h"
+
+class Context;
+
+namespace librbd {
+
+class UpdateWatchCtx;
+
+struct MockImageState {
+ MOCK_CONST_METHOD0(is_refresh_required, bool());
+ MOCK_METHOD1(refresh, void(Context*));
+
+ MOCK_METHOD2(open, void(bool, Context*));
+
+ MOCK_METHOD0(close, int());
+ MOCK_METHOD1(close, void(Context*));
+
+ MOCK_METHOD2(snap_set, void(uint64_t snap_id, Context*));
+
+ MOCK_METHOD1(prepare_lock, void(Context*));
+ MOCK_METHOD0(handle_prepare_lock_complete, void());
+
+ MOCK_METHOD2(register_update_watcher, int(UpdateWatchCtx *, uint64_t *));
+ MOCK_METHOD2(unregister_update_watcher, void(uint64_t, Context *));
+
+ MOCK_METHOD0(handle_update_notification, void());
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_STATE_H
diff --git a/src/test/librbd/mock/MockImageWatcher.h b/src/test/librbd/mock/MockImageWatcher.h
new file mode 100644
index 000000000..c44238ae1
--- /dev/null
+++ b/src/test/librbd/mock/MockImageWatcher.h
@@ -0,0 +1,34 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H
+#define CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H
+
+#include "gmock/gmock.h"
+
+class Context;
+
+namespace librbd {
+
+class ProgressContext;
+
+struct MockImageWatcher {
+ MOCK_METHOD0(is_registered, bool());
+ MOCK_METHOD0(is_unregistered, bool());
+ MOCK_METHOD0(is_blocklisted, bool());
+ MOCK_METHOD0(unregister_watch, void());
+ MOCK_METHOD1(flush, void(Context *));
+
+ MOCK_CONST_METHOD0(get_watch_handle, uint64_t());
+
+ MOCK_METHOD0(notify_acquired_lock, void());
+ MOCK_METHOD0(notify_released_lock, void());
+ MOCK_METHOD0(notify_request_lock, void());
+
+ MOCK_METHOD3(notify_quiesce, void(uint64_t *, ProgressContext &, Context *));
+ MOCK_METHOD2(notify_unquiesce, void(uint64_t, Context *));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H
diff --git a/src/test/librbd/mock/MockJournal.cc b/src/test/librbd/mock/MockJournal.cc
new file mode 100644
index 000000000..97feb0187
--- /dev/null
+++ b/src/test/librbd/mock/MockJournal.cc
@@ -0,0 +1,10 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/mock/MockJournal.h"
+
+namespace librbd {
+
+MockJournal *MockJournal::s_instance = nullptr;
+
+} // namespace librbd
diff --git a/src/test/librbd/mock/MockJournal.h b/src/test/librbd/mock/MockJournal.h
new file mode 100644
index 000000000..15baf7903
--- /dev/null
+++ b/src/test/librbd/mock/MockJournal.h
@@ -0,0 +1,96 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_JOURNAL_H
+#define CEPH_TEST_LIBRBD_MOCK_JOURNAL_H
+
+#include "common/RefCountedObj.h"
+#include "gmock/gmock.h"
+#include "include/rados/librados_fwd.hpp"
+#include "librbd/Journal.h"
+#include "librbd/journal/Types.h"
+#include <list>
+
+struct Context;
+struct ContextWQ;
+
+namespace librbd {
+
+struct ImageCtx;
+
+struct MockJournal {
+ static MockJournal *s_instance;
+ static MockJournal *get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ template <typename ImageCtxT>
+ static int is_tag_owner(ImageCtxT *image_ctx, bool *is_tag_owner) {
+ return get_instance()->is_tag_owner(is_tag_owner);
+ }
+
+ static void get_tag_owner(librados::IoCtx &,
+ const std::string &global_image_id,
+ std::string *tag_owner, ContextWQ *work_queue,
+ Context *on_finish) {
+ get_instance()->get_tag_owner(global_image_id, tag_owner,
+ work_queue, on_finish);
+ }
+
+ MockJournal() {
+ s_instance = this;
+ }
+
+ void get() {}
+ void put() {}
+
+ MOCK_CONST_METHOD0(is_journal_ready, bool());
+ MOCK_CONST_METHOD0(is_journal_replaying, bool());
+ MOCK_CONST_METHOD0(is_journal_appending, bool());
+
+ MOCK_METHOD1(wait_for_journal_ready, void(Context *));
+
+ MOCK_METHOD4(get_tag_owner, void(const std::string &,
+ std::string *, ContextWQ *,
+ Context *));
+
+ MOCK_CONST_METHOD0(is_tag_owner, bool());
+ MOCK_CONST_METHOD1(is_tag_owner, int(bool *));
+ MOCK_METHOD3(allocate_tag, void(const std::string &mirror_uuid,
+ const journal::TagPredecessor &predecessor,
+ Context *on_finish));
+
+ MOCK_METHOD1(open, void(Context *));
+ MOCK_METHOD1(close, void(Context *));
+
+ MOCK_CONST_METHOD0(get_tag_tid, uint64_t());
+ MOCK_CONST_METHOD0(get_tag_data, journal::TagData());
+
+ MOCK_METHOD0(allocate_op_tid, uint64_t());
+
+ MOCK_METHOD0(user_flushed, void());
+
+ MOCK_METHOD3(append_op_event_mock, void(uint64_t, const journal::EventEntry&,
+ Context *));
+ void append_op_event(uint64_t op_tid, journal::EventEntry &&event_entry,
+ Context *on_safe) {
+ // googlemock doesn't support move semantics
+ append_op_event_mock(op_tid, event_entry, on_safe);
+ }
+
+ MOCK_METHOD2(flush_event, void(uint64_t, Context *));
+ MOCK_METHOD2(wait_event, void(uint64_t, Context *));
+
+ MOCK_METHOD3(commit_op_event, void(uint64_t, int, Context *));
+ MOCK_METHOD2(replay_op_ready, void(uint64_t, Context *));
+
+ MOCK_METHOD1(add_listener, void(journal::Listener *));
+ MOCK_METHOD1(remove_listener, void(journal::Listener *));
+
+ MOCK_METHOD1(is_resync_requested, int(bool *));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_JOURNAL_H
diff --git a/src/test/librbd/mock/MockJournalPolicy.h b/src/test/librbd/mock/MockJournalPolicy.h
new file mode 100644
index 000000000..33bb252ac
--- /dev/null
+++ b/src/test/librbd/mock/MockJournalPolicy.h
@@ -0,0 +1,22 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_JOURNAL_POLICY_H
+#define CEPH_TEST_LIBRBD_MOCK_JOURNAL_POLICY_H
+
+#include "librbd/journal/Policy.h"
+#include "gmock/gmock.h"
+
+namespace librbd {
+
+struct MockJournalPolicy : public journal::Policy {
+
+ MOCK_CONST_METHOD0(append_disabled, bool());
+ MOCK_CONST_METHOD0(journal_disabled, bool());
+ MOCK_METHOD1(allocate_tag_on_lock, void(Context*));
+
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_JOURNAL_POLICY_H
diff --git a/src/test/librbd/mock/MockObjectMap.h b/src/test/librbd/mock/MockObjectMap.h
new file mode 100644
index 000000000..2a1adbcae
--- /dev/null
+++ b/src/test/librbd/mock/MockObjectMap.h
@@ -0,0 +1,70 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H
+#define CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H
+
+#include "librbd/Utils.h"
+#include "gmock/gmock.h"
+
+namespace librbd {
+
+struct MockObjectMap {
+ MOCK_METHOD1(at, uint8_t(uint64_t));
+ uint8_t operator[](uint64_t object_no) {
+ return at(object_no);
+ }
+
+ MOCK_CONST_METHOD0(size, uint64_t());
+
+ MOCK_METHOD1(open, void(Context *on_finish));
+ MOCK_METHOD1(close, void(Context *on_finish));
+
+ MOCK_METHOD3(aio_resize, void(uint64_t new_size, uint8_t default_object_state,
+ Context *on_finish));
+
+ void get() {}
+ void put() {}
+
+ template <typename T, void(T::*MF)(int) = &T::complete>
+ bool aio_update(uint64_t snap_id, uint64_t start_object_no, uint8_t new_state,
+ const boost::optional<uint8_t> &current_state,
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ T *callback_object) {
+ return aio_update<T, MF>(snap_id, start_object_no, start_object_no + 1,
+ new_state, current_state, parent_trace,
+ ignore_enoent, callback_object);
+ }
+
+ template <typename T, void(T::*MF)(int) = &T::complete>
+ bool aio_update(uint64_t snap_id, uint64_t start_object_no,
+ uint64_t end_object_no, uint8_t new_state,
+ const boost::optional<uint8_t> &current_state,
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ T *callback_object) {
+ auto ctx = util::create_context_callback<T, MF>(callback_object);
+ bool updated = aio_update(snap_id, start_object_no, end_object_no,
+ new_state, current_state, parent_trace,
+ ignore_enoent, ctx);
+ if (!updated) {
+ delete ctx;
+ }
+ return updated;
+ }
+ MOCK_METHOD8(aio_update, bool(uint64_t snap_id, uint64_t start_object_no,
+ uint64_t end_object_no, uint8_t new_state,
+ const boost::optional<uint8_t> &current_state,
+ const ZTracer::Trace &parent_trace,
+ bool ignore_enoent, Context *on_finish));
+
+ MOCK_METHOD2(snapshot_add, void(uint64_t snap_id, Context *on_finish));
+ MOCK_METHOD2(snapshot_remove, void(uint64_t snap_id, Context *on_finish));
+ MOCK_METHOD2(rollback, void(uint64_t snap_id, Context *on_finish));
+
+ MOCK_CONST_METHOD1(object_may_exist, bool(uint64_t));
+
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H
diff --git a/src/test/librbd/mock/MockOperations.h b/src/test/librbd/mock/MockOperations.h
new file mode 100644
index 000000000..99dc253d1
--- /dev/null
+++ b/src/test/librbd/mock/MockOperations.h
@@ -0,0 +1,72 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_OPERATIONS_H
+#define CEPH_TEST_LIBRBD_MOCK_OPERATIONS_H
+
+#include "cls/rbd/cls_rbd_types.h"
+#include "include/int_types.h"
+#include "include/rbd/librbd.hpp"
+#include "gmock/gmock.h"
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+struct MockOperations {
+ MOCK_METHOD2(execute_flatten, void(ProgressContext &prog_ctx,
+ Context *on_finish));
+ MOCK_METHOD2(execute_rebuild_object_map, void(ProgressContext &prog_ctx,
+ Context *on_finish));
+ MOCK_METHOD2(execute_rename, void(const std::string &dstname,
+ Context *on_finish));
+ MOCK_METHOD5(execute_resize, void(uint64_t size, bool allow_shrink,
+ ProgressContext &prog_ctx,
+ Context *on_finish,
+ uint64_t journal_op_tid));
+ MOCK_METHOD5(snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
+ const std::string &snap_name,
+ uint64_t flags,
+ ProgressContext &prog_ctx,
+ Context *on_finish));
+ MOCK_METHOD6(execute_snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
+ const std::string &snap_name,
+ Context *on_finish,
+ uint64_t journal_op_tid,
+ uint64_t flags,
+ ProgressContext &prog_ctx));
+ MOCK_METHOD3(snap_remove, void(const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &snap_name,
+ Context *on_finish));
+ MOCK_METHOD3(execute_snap_remove, void(const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &snap_name,
+ Context *on_finish));
+ MOCK_METHOD3(execute_snap_rename, void(uint64_t src_snap_id,
+ const std::string &snap_name,
+ Context *on_finish));
+ MOCK_METHOD4(execute_snap_rollback, void(const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &snap_name,
+ ProgressContext &prog_ctx,
+ Context *on_finish));
+ MOCK_METHOD3(execute_snap_protect, void(const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &snap_name,
+ Context *on_finish));
+ MOCK_METHOD3(execute_snap_unprotect, void(const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &snap_name,
+ Context *on_finish));
+ MOCK_METHOD2(execute_snap_set_limit, void(uint64_t limit,
+ Context *on_finish));
+ MOCK_METHOD4(execute_update_features, void(uint64_t features, bool enabled,
+ Context *on_finish,
+ uint64_t journal_op_tid));
+ MOCK_METHOD3(execute_metadata_set, void(const std::string &key,
+ const std::string &value,
+ Context *on_finish));
+ MOCK_METHOD2(execute_metadata_remove, void(const std::string &key,
+ Context *on_finish));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_OPERATIONS_H
diff --git a/src/test/librbd/mock/MockPluginRegistry.h b/src/test/librbd/mock/MockPluginRegistry.h
new file mode 100644
index 000000000..8854a7426
--- /dev/null
+++ b/src/test/librbd/mock/MockPluginRegistry.h
@@ -0,0 +1,21 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_PLUGIN_REGISTRY_H
+#define CEPH_TEST_LIBRBD_MOCK_PLUGIN_REGISTRY_H
+
+#include <gmock/gmock.h>
+
+class Context;
+
+namespace librbd {
+
+struct MockPluginRegistry{
+ MOCK_METHOD2(init, void(const std::string&, Context*));
+ MOCK_METHOD1(acquired_exclusive_lock, void(Context*));
+ MOCK_METHOD1(prerelease_exclusive_lock, void(Context*));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_PLUGIN_REGISTRY_H
diff --git a/src/test/librbd/mock/MockReadahead.h b/src/test/librbd/mock/MockReadahead.h
new file mode 100644
index 000000000..40fceaa7a
--- /dev/null
+++ b/src/test/librbd/mock/MockReadahead.h
@@ -0,0 +1,21 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_READAHEAD_H
+#define CEPH_TEST_LIBRBD_MOCK_READAHEAD_H
+
+#include "include/int_types.h"
+#include "gmock/gmock.h"
+
+class Context;
+
+namespace librbd {
+
+struct MockReadahead {
+ MOCK_METHOD1(set_max_readahead_size, void(uint64_t));
+ MOCK_METHOD1(wait_for_pending, void(Context *));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_READAHEAD_H
diff --git a/src/test/librbd/mock/MockSafeTimer.h b/src/test/librbd/mock/MockSafeTimer.h
new file mode 100644
index 000000000..9f6be1960
--- /dev/null
+++ b/src/test/librbd/mock/MockSafeTimer.h
@@ -0,0 +1,20 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_MOCK_SAFE_TIMER_H
+#define CEPH_MOCK_SAFE_TIMER_H
+
+#include <gmock/gmock.h>
+
+struct Context;
+
+struct MockSafeTimer {
+ virtual ~MockSafeTimer() {
+ }
+
+ MOCK_METHOD2(add_event_after, Context*(double, Context *));
+ MOCK_METHOD2(add_event_at, Context*(ceph::real_clock::time_point, Context *));
+ MOCK_METHOD1(cancel_event, bool(Context *));
+};
+
+#endif // CEPH_MOCK_SAFE_TIMER_H
diff --git a/src/test/librbd/mock/cache/MockImageCache.h b/src/test/librbd/mock/cache/MockImageCache.h
new file mode 100644
index 000000000..c9a3cc335
--- /dev/null
+++ b/src/test/librbd/mock/cache/MockImageCache.h
@@ -0,0 +1,58 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_CACHE_MOCK_IMAGE_CACHE_H
+#define CEPH_TEST_LIBRBD_CACHE_MOCK_IMAGE_CACHE_H
+
+#include "gmock/gmock.h"
+#include "librbd/io/Types.h"
+#include <vector>
+
+namespace librbd {
+namespace cache {
+
+struct MockImageCache {
+ typedef std::vector<std::pair<uint64_t,uint64_t> > Extents;
+
+ MOCK_METHOD4(aio_read_mock, void(const Extents &, ceph::bufferlist*, int,
+ Context *));
+ void aio_read(Extents&& image_extents, ceph::bufferlist* bl,
+ int fadvise_flags, Context *on_finish) {
+ aio_read_mock(image_extents, bl, fadvise_flags, on_finish);
+ }
+
+
+ MOCK_METHOD4(aio_write_mock, void(const Extents &, const ceph::bufferlist &,
+ int, Context *));
+ void aio_write(Extents&& image_extents, ceph::bufferlist&& bl,
+ int fadvise_flags, Context *on_finish) {
+ aio_write_mock(image_extents, bl, fadvise_flags, on_finish);
+ }
+
+ MOCK_METHOD4(aio_discard, void(uint64_t, uint64_t, uint32_t, Context *));
+ MOCK_METHOD1(aio_flush, void(Context *));
+ MOCK_METHOD2(aio_flush, void(librbd::io::FlushSource, Context *));
+ MOCK_METHOD5(aio_writesame_mock, void(uint64_t, uint64_t, ceph::bufferlist& bl,
+ int, Context *));
+ void aio_writesame(uint64_t off, uint64_t len, ceph::bufferlist&& bl,
+ int fadvise_flags, Context *on_finish) {
+ aio_writesame_mock(off, len, bl, fadvise_flags, on_finish);
+ }
+
+ MOCK_METHOD6(aio_compare_and_write_mock, void(const Extents &,
+ const ceph::bufferlist &,
+ const ceph::bufferlist &,
+ uint64_t *, int, Context *));
+
+ void aio_compare_and_write(Extents&& image_extents, ceph::bufferlist&& cmp_bl,
+ ceph::bufferlist&& bl, uint64_t *mismatch_offset,
+ int fadvise_flags, Context *on_finish) {
+ aio_compare_and_write_mock(image_extents, cmp_bl, bl, mismatch_offset,
+ fadvise_flags, on_finish);
+ }
+};
+
+} // namespace cache
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_CACHE_MOCK_IMAGE_CACHE_H
diff --git a/src/test/librbd/mock/crypto/MockCryptoInterface.h b/src/test/librbd/mock/crypto/MockCryptoInterface.h
new file mode 100644
index 000000000..0e3b003ca
--- /dev/null
+++ b/src/test/librbd/mock/crypto/MockCryptoInterface.h
@@ -0,0 +1,36 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_CRYPTO_INTERFACE_H
+#define CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_CRYPTO_INTERFACE_H
+
+#include "include/buffer.h"
+#include "gmock/gmock.h"
+#include "librbd/crypto/CryptoInterface.h"
+
+namespace librbd {
+namespace crypto {
+
+struct MockCryptoInterface : CryptoInterface {
+
+ static const uint64_t BLOCK_SIZE = 4096;
+ static const uint64_t DATA_OFFSET = 4 * 1024 * 1024;
+
+ MOCK_METHOD2(encrypt, int(ceph::bufferlist*, uint64_t));
+ MOCK_METHOD2(decrypt, int(ceph::bufferlist*, uint64_t));
+ MOCK_CONST_METHOD0(get_key, const unsigned char*());
+ MOCK_CONST_METHOD0(get_key_length, int());
+
+ uint64_t get_block_size() const override {
+ return BLOCK_SIZE;
+ }
+
+ uint64_t get_data_offset() const override {
+ return DATA_OFFSET;
+ }
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_CRYPTO_INTERFACE_H
diff --git a/src/test/librbd/mock/crypto/MockDataCryptor.h b/src/test/librbd/mock/crypto/MockDataCryptor.h
new file mode 100644
index 000000000..51a738862
--- /dev/null
+++ b/src/test/librbd/mock/crypto/MockDataCryptor.h
@@ -0,0 +1,43 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_DATA_CRYPTOR_H
+#define CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_DATA_CRYPTOR_H
+
+#include "gmock/gmock.h"
+#include "librbd/crypto/DataCryptor.h"
+
+namespace librbd {
+namespace crypto {
+
+struct MockCryptoContext {};
+
+class MockDataCryptor : public DataCryptor<MockCryptoContext> {
+
+public:
+ uint32_t block_size = 16;
+ uint32_t iv_size = 16;
+
+ uint32_t get_block_size() const override {
+ return block_size;
+ }
+
+ uint32_t get_iv_size() const override {
+ return iv_size;
+ }
+
+ MOCK_METHOD1(get_context, MockCryptoContext*(CipherMode));
+ MOCK_METHOD2(return_context, void(MockCryptoContext*, CipherMode));
+ MOCK_CONST_METHOD3(init_context, int(MockCryptoContext*,
+ const unsigned char*, uint32_t));
+ MOCK_CONST_METHOD4(update_context, int(MockCryptoContext*,
+ const unsigned char*, unsigned char*,
+ uint32_t));
+ MOCK_CONST_METHOD0(get_key, const unsigned char*());
+ MOCK_CONST_METHOD0(get_key_length, int());
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_DATA_CRYPTOR_H
diff --git a/src/test/librbd/mock/crypto/MockEncryptionFormat.h b/src/test/librbd/mock/crypto/MockEncryptionFormat.h
new file mode 100644
index 000000000..3ad1a54db
--- /dev/null
+++ b/src/test/librbd/mock/crypto/MockEncryptionFormat.h
@@ -0,0 +1,26 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_ENCRYPTION_FORMAT_H
+#define CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_ENCRYPTION_FORMAT_H
+
+#include "gmock/gmock.h"
+#include "librbd/crypto/EncryptionFormat.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/crypto/MockCryptoInterface.h"
+
+namespace librbd {
+namespace crypto {
+
+struct MockEncryptionFormat {
+ MOCK_CONST_METHOD0(clone, std::unique_ptr<MockEncryptionFormat>());
+ MOCK_METHOD2(format, void(MockImageCtx*, Context*));
+ MOCK_METHOD3(load, void(MockImageCtx*, std::string*, Context*));
+ MOCK_METHOD2(flatten, void(MockImageCtx*, Context*));
+ MOCK_METHOD0(get_crypto, MockCryptoInterface*());
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_ENCRYPTION_FORMAT_H
diff --git a/src/test/librbd/mock/exclusive_lock/MockPolicy.h b/src/test/librbd/mock/exclusive_lock/MockPolicy.h
new file mode 100644
index 000000000..f49eeb23f
--- /dev/null
+++ b/src/test/librbd/mock/exclusive_lock/MockPolicy.h
@@ -0,0 +1,23 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_POLICY_H
+#define CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_POLICY_H
+
+#include "librbd/exclusive_lock/Policy.h"
+#include <gmock/gmock.h>
+
+namespace librbd {
+namespace exclusive_lock {
+
+struct MockPolicy : public Policy {
+
+ MOCK_METHOD0(may_auto_request_lock, bool());
+ MOCK_METHOD1(lock_requested, int(bool));
+ MOCK_METHOD1(accept_blocked_request, bool(OperationRequestType));
+};
+
+} // namespace exclusive_lock
+} // librbd
+
+#endif
diff --git a/src/test/librbd/mock/io/MockImageDispatch.h b/src/test/librbd/mock/io/MockImageDispatch.h
new file mode 100644
index 000000000..f9552bebe
--- /dev/null
+++ b/src/test/librbd/mock/io/MockImageDispatch.h
@@ -0,0 +1,98 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IO_IMAGE_DISPATCH_H
+#define CEPH_TEST_LIBRBD_MOCK_IO_IMAGE_DISPATCH_H
+
+#include "gmock/gmock.h"
+#include "include/Context.h"
+#include "librbd/io/ImageDispatchInterface.h"
+#include "librbd/io/Types.h"
+
+class Context;
+
+namespace librbd {
+namespace io {
+
+struct MockImageDispatch : public ImageDispatchInterface {
+public:
+ MOCK_CONST_METHOD0(get_dispatch_layer, ImageDispatchLayer());
+
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ bool read(
+ AioCompletion* aio_comp, Extents &&image_extents,
+ ReadResult &&read_result, IOContext io_context, int op_flags,
+ int read_flags, const ZTracer::Trace &parent_trace, uint64_t tid,
+ std::atomic<uint32_t>* image_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return false;
+ }
+
+ bool write(
+ AioCompletion* aio_comp, Extents &&image_extents, bufferlist &&bl,
+ int op_flags, const ZTracer::Trace &parent_trace,
+ uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return false;
+ }
+
+ bool discard(
+ AioCompletion* aio_comp, Extents &&image_extents,
+ uint32_t discard_granularity_bytes, const ZTracer::Trace &parent_trace,
+ uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return false;
+ }
+
+ bool write_same(
+ AioCompletion* aio_comp, Extents &&image_extents, bufferlist &&bl,
+ int op_flags, const ZTracer::Trace &parent_trace,
+ uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return false;
+ }
+
+ bool compare_and_write(
+ AioCompletion* aio_comp, Extents &&image_extents,
+ bufferlist &&cmp_bl, bufferlist &&bl, uint64_t *mismatch_offset,
+ int op_flags, const ZTracer::Trace &parent_trace,
+ uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return false;
+ }
+
+ bool flush(
+ AioCompletion* aio_comp, FlushSource flush_source,
+ const ZTracer::Trace &parent_trace, uint64_t tid,
+ std::atomic<uint32_t>* image_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return false;
+ }
+
+ bool list_snaps(
+ AioCompletion* aio_comp, Extents&& image_extents, SnapIds&& snap_ids,
+ int list_snaps_flags, SnapshotDelta* snapshot_delta,
+ const ZTracer::Trace &parent_trace, uint64_t tid,
+ std::atomic<uint32_t>* image_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return false;
+ }
+
+ bool invalidate_cache(Context* on_finish) override {
+ return false;
+ }
+
+};
+
+} // namespace io
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IO_IMAGE_DISPATCH_H
diff --git a/src/test/librbd/mock/io/MockImageDispatcher.h b/src/test/librbd/mock/io/MockImageDispatcher.h
new file mode 100644
index 000000000..92cddd501
--- /dev/null
+++ b/src/test/librbd/mock/io/MockImageDispatcher.h
@@ -0,0 +1,50 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IO_IMAGE_DISPATCHER_H
+#define CEPH_TEST_LIBRBD_MOCK_IO_IMAGE_DISPATCHER_H
+
+#include "gmock/gmock.h"
+#include "include/Context.h"
+#include "librbd/io/ImageDispatcher.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/Types.h"
+
+class Context;
+
+namespace librbd {
+namespace io {
+
+struct ImageDispatchInterface;
+
+struct MockImageDispatcher : public ImageDispatcherInterface {
+public:
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ MOCK_METHOD1(register_dispatch, void(ImageDispatchInterface*));
+ MOCK_METHOD1(exists, bool(ImageDispatchLayer));
+ MOCK_METHOD2(shut_down_dispatch, void(ImageDispatchLayer, Context*));
+ MOCK_METHOD1(invalidate_cache, void(Context *));
+
+ MOCK_METHOD1(send, void(ImageDispatchSpec*));
+ MOCK_METHOD3(finish, void(int r, ImageDispatchLayer, uint64_t));
+
+ MOCK_METHOD1(apply_qos_schedule_tick_min, void(uint64_t));
+ MOCK_METHOD4(apply_qos_limit, void(uint64_t, uint64_t, uint64_t, uint64_t));
+ MOCK_METHOD1(apply_qos_exclude_ops, void(uint64_t));
+
+ MOCK_CONST_METHOD0(writes_blocked, bool());
+ MOCK_METHOD0(block_writes, int());
+ MOCK_METHOD1(block_writes, void(Context*));
+
+ MOCK_METHOD0(unblock_writes, void());
+ MOCK_METHOD1(wait_on_writes_unblocked, void(Context*));
+
+ MOCK_METHOD2(remap_to_physical, void(Extents&, ImageArea));
+ MOCK_METHOD1(remap_to_logical, ImageArea(Extents&));
+};
+
+} // namespace io
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IO_IMAGE_DISPATCHER_H
diff --git a/src/test/librbd/mock/io/MockObjectDispatch.h b/src/test/librbd/mock/io/MockObjectDispatch.h
new file mode 100644
index 000000000..f6e75d204
--- /dev/null
+++ b/src/test/librbd/mock/io/MockObjectDispatch.h
@@ -0,0 +1,137 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IO_OBJECT_DISPATCH_H
+#define CEPH_TEST_LIBRBD_MOCK_IO_OBJECT_DISPATCH_H
+
+#include "gmock/gmock.h"
+#include "common/ceph_mutex.h"
+#include "librbd/io/ObjectDispatchInterface.h"
+#include "librbd/io/Types.h"
+
+class Context;
+
+namespace librbd {
+namespace io {
+
+struct MockObjectDispatch : public ObjectDispatchInterface {
+public:
+ ceph::shared_mutex lock = ceph::make_shared_mutex("MockObjectDispatch::lock");
+
+ MockObjectDispatch() {}
+
+ MOCK_CONST_METHOD0(get_dispatch_layer, ObjectDispatchLayer());
+
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ MOCK_METHOD6(execute_read,
+ bool(uint64_t, ReadExtents*, IOContext io_context, uint64_t*,
+ DispatchResult*, Context*));
+ bool read(
+ uint64_t object_no, ReadExtents* extents, IOContext io_context,
+ int op_flags, int read_flags, const ZTracer::Trace& parent_trace,
+ uint64_t* version, int* dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) {
+ return execute_read(object_no, extents, io_context, version,
+ dispatch_result, on_dispatched);
+ }
+
+ MOCK_METHOD9(execute_discard,
+ bool(uint64_t, uint64_t, uint64_t, IOContext, int,
+ int*, uint64_t*, DispatchResult*, Context*));
+ bool discard(
+ uint64_t object_no, uint64_t object_off, uint64_t object_len,
+ IOContext io_context, int discard_flags,
+ const ZTracer::Trace &parent_trace, int* dispatch_flags,
+ uint64_t* journal_tid, DispatchResult* dispatch_result,
+ Context** on_finish, Context* on_dispatched) {
+ return execute_discard(object_no, object_off, object_len, io_context,
+ discard_flags, dispatch_flags, journal_tid,
+ dispatch_result, on_dispatched);
+ }
+
+ MOCK_METHOD10(execute_write,
+ bool(uint64_t, uint64_t, const ceph::bufferlist&,
+ IOContext, int, std::optional<uint64_t>, int*,
+ uint64_t*, DispatchResult*, Context *));
+ bool write(
+ uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data,
+ IOContext io_context, int op_flags, int write_flags,
+ std::optional<uint64_t> assert_version,
+ const ZTracer::Trace &parent_trace, int* dispatch_flags,
+ uint64_t* journal_tid, DispatchResult* dispatch_result,
+ Context** on_finish, Context* on_dispatched) override {
+ return execute_write(object_no, object_off, data, io_context, write_flags,
+ assert_version, dispatch_flags, journal_tid,
+ dispatch_result, on_dispatched);
+ }
+
+ MOCK_METHOD10(execute_write_same,
+ bool(uint64_t, uint64_t, uint64_t,
+ const LightweightBufferExtents&,
+ const ceph::bufferlist&, IOContext, int*,
+ uint64_t*, DispatchResult*, Context *));
+ bool write_same(
+ uint64_t object_no, uint64_t object_off, uint64_t object_len,
+ LightweightBufferExtents&& buffer_extents, ceph::bufferlist&& data,
+ IOContext io_context, int op_flags,
+ const ZTracer::Trace &parent_trace, int* dispatch_flags,
+ uint64_t* journal_tid, DispatchResult* dispatch_result,
+ Context* *on_finish, Context* on_dispatched) override {
+ return execute_write_same(object_no, object_off, object_len, buffer_extents,
+ data, io_context, dispatch_flags, journal_tid,
+ dispatch_result, on_dispatched);
+ }
+
+ MOCK_METHOD9(execute_compare_and_write,
+ bool(uint64_t, uint64_t, const ceph::bufferlist&,
+ const ceph::bufferlist&, uint64_t*, int*, uint64_t*,
+ DispatchResult*, Context *));
+ bool compare_and_write(
+ uint64_t object_no, uint64_t object_off, ceph::bufferlist&& cmp_data,
+ ceph::bufferlist&& write_data, IOContext io_context, int op_flags,
+ const ZTracer::Trace &parent_trace, uint64_t* mismatch_offset,
+ int* dispatch_flags, uint64_t* journal_tid,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return execute_compare_and_write(object_no, object_off, cmp_data,
+ write_data, mismatch_offset,
+ dispatch_flags, journal_tid,
+ dispatch_result, on_dispatched);
+ }
+
+ MOCK_METHOD4(execute_flush, bool(FlushSource, uint64_t*, DispatchResult*,
+ Context*));
+ bool flush(FlushSource flush_source, const ZTracer::Trace &parent_trace,
+ uint64_t* journal_tid, DispatchResult* dispatch_result,
+ Context** on_finish, Context* on_dispatched) {
+ return execute_flush(flush_source, journal_tid, dispatch_result,
+ on_dispatched);
+ }
+
+ MOCK_METHOD7(execute_list_snaps, bool(uint64_t, const Extents&,
+ const SnapIds&, int, SnapshotDelta*,
+ DispatchResult*, Context*));
+ bool list_snaps(
+ uint64_t object_no, io::Extents&& extents, SnapIds&& snap_ids,
+ int list_snaps_flags, const ZTracer::Trace &parent_trace,
+ SnapshotDelta* snapshot_delta, int* object_dispatch_flags,
+ DispatchResult* dispatch_result, Context** on_finish,
+ Context* on_dispatched) override {
+ return execute_list_snaps(object_no, extents, snap_ids, list_snaps_flags,
+ snapshot_delta, dispatch_result, on_dispatched);
+ }
+
+ MOCK_METHOD1(invalidate_cache, bool(Context*));
+ MOCK_METHOD1(reset_existence_cache, bool(Context*));
+
+ MOCK_METHOD5(extent_overwritten, void(uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t));
+ MOCK_METHOD2(prepare_copyup, int(uint64_t, SnapshotSparseBufferlist*));
+};
+
+} // namespace io
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IO_OBJECT_DISPATCH_H
diff --git a/src/test/librbd/mock/io/MockObjectDispatcher.h b/src/test/librbd/mock/io/MockObjectDispatcher.h
new file mode 100644
index 000000000..5e700397b
--- /dev/null
+++ b/src/test/librbd/mock/io/MockObjectDispatcher.h
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IO_OBJECT_DISPATCHER_H
+#define CEPH_TEST_LIBRBD_MOCK_IO_OBJECT_DISPATCHER_H
+
+#include "gmock/gmock.h"
+#include "include/Context.h"
+#include "librbd/io/ObjectDispatcher.h"
+#include "librbd/io/ObjectDispatchSpec.h"
+#include "librbd/io/Types.h"
+
+class Context;
+
+namespace librbd {
+namespace io {
+
+struct ObjectDispatchInterface;
+
+struct MockObjectDispatcher : public ObjectDispatcherInterface {
+public:
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ MOCK_METHOD1(register_dispatch, void(ObjectDispatchInterface*));
+ MOCK_METHOD1(exists, bool(ObjectDispatchLayer));
+ MOCK_METHOD2(shut_down_dispatch, void(ObjectDispatchLayer, Context*));
+
+ MOCK_METHOD2(flush, void(FlushSource, Context*));
+
+ MOCK_METHOD1(invalidate_cache, void(Context*));
+ MOCK_METHOD1(reset_existence_cache, void(Context*));
+
+ MOCK_METHOD5(extent_overwritten, void(uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t));
+
+ MOCK_METHOD2(prepare_copyup, int(uint64_t, SnapshotSparseBufferlist*));
+
+ MOCK_METHOD1(send, void(ObjectDispatchSpec*));
+};
+
+} // namespace io
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IO_OBJECT_DISPATCHER_H
diff --git a/src/test/librbd/mock/io/MockQosImageDispatch.h b/src/test/librbd/mock/io/MockQosImageDispatch.h
new file mode 100644
index 000000000..e49897816
--- /dev/null
+++ b/src/test/librbd/mock/io/MockQosImageDispatch.h
@@ -0,0 +1,24 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IO_QOS_IMAGE_DISPATCH_H
+#define CEPH_TEST_LIBRBD_MOCK_IO_QOS_IMAGE_DISPATCH_H
+
+#include "gmock/gmock.h"
+#include "librbd/io/Types.h"
+#include <atomic>
+
+struct Context;
+
+namespace librbd {
+namespace io {
+
+struct MockQosImageDispatch {
+ MOCK_METHOD4(needs_throttle, bool(bool, const Extents&,
+ std::atomic<uint32_t>*, Context*));
+};
+
+} // namespace io
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IO_QOS_IMAGE_DISPATCH_H
diff --git a/src/test/librbd/mock/migration/MockSnapshotInterface.h b/src/test/librbd/mock/migration/MockSnapshotInterface.h
new file mode 100644
index 000000000..abb6d1a08
--- /dev/null
+++ b/src/test/librbd/mock/migration/MockSnapshotInterface.h
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_SNAPSHOT_INTERFACE_H
+#define CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_SNAPSHOT_INTERFACE_H
+
+#include "include/buffer.h"
+#include "gmock/gmock.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ReadResult.h"
+#include "librbd/io/Types.h"
+#include "librbd/migration/SnapshotInterface.h"
+
+namespace librbd {
+namespace migration {
+
+struct MockSnapshotInterface : public SnapshotInterface {
+ MOCK_METHOD2(open, void(SnapshotInterface*, Context*));
+ MOCK_METHOD1(close, void(Context*));
+
+ MOCK_CONST_METHOD0(get_snap_info, const SnapInfo&());
+
+ MOCK_METHOD3(read, void(io::AioCompletion*, const io::Extents&,
+ io::ReadResult&));
+ void read(io::AioCompletion* aio_comp, io::Extents&& image_extents,
+ io::ReadResult&& read_result, int op_flags, int read_flags,
+ const ZTracer::Trace &parent_trace) override {
+ read(aio_comp, image_extents, read_result);
+ }
+
+ MOCK_METHOD3(list_snap, void(const io::Extents&, io::SparseExtents*,
+ Context*));
+ void list_snap(io::Extents&& image_extents, int list_snaps_flags,
+ io::SparseExtents* sparse_extents,
+ const ZTracer::Trace &parent_trace,
+ Context* on_finish) override {
+ list_snap(image_extents, sparse_extents, on_finish);
+ }
+};
+
+} // namespace migration
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_SNAPSHOT_INTERFACE_H
diff --git a/src/test/librbd/mock/migration/MockStreamInterface.h b/src/test/librbd/mock/migration/MockStreamInterface.h
new file mode 100644
index 000000000..36df86638
--- /dev/null
+++ b/src/test/librbd/mock/migration/MockStreamInterface.h
@@ -0,0 +1,29 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_STREAM_INTERFACE_H
+#define CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_STREAM_INTERFACE_H
+
+#include "include/buffer.h"
+#include "gmock/gmock.h"
+#include "librbd/migration/StreamInterface.h"
+
+namespace librbd {
+namespace migration {
+
+struct MockStreamInterface : public StreamInterface {
+ MOCK_METHOD1(open, void(Context*));
+ MOCK_METHOD1(close, void(Context*));
+
+ MOCK_METHOD2(get_size, void(uint64_t*, Context*));
+
+ MOCK_METHOD3(read, void(const io::Extents&, bufferlist*, Context*));
+ void read(io::Extents&& byte_extents, bufferlist* bl, Context* on_finish) {
+ read(byte_extents, bl, on_finish);
+ }
+};
+
+} // namespace migration
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_STREAM_INTERFACE_H
diff --git a/src/test/librbd/object_map/mock/MockInvalidateRequest.h b/src/test/librbd/object_map/mock/MockInvalidateRequest.h
new file mode 100644
index 000000000..92f30748c
--- /dev/null
+++ b/src/test/librbd/object_map/mock/MockInvalidateRequest.h
@@ -0,0 +1,41 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/object_map/InvalidateRequest.h"
+
+// template definitions
+#include "librbd/object_map/InvalidateRequest.cc"
+
+namespace librbd {
+namespace object_map {
+
+template <typename I>
+struct MockInvalidateRequestBase {
+ static std::list<InvalidateRequest<I>*> s_requests;
+ uint64_t snap_id = 0;
+ bool force = false;
+ Context *on_finish = nullptr;
+
+ static InvalidateRequest<I>* create(I &image_ctx, uint64_t snap_id,
+ bool force, Context *on_finish) {
+ ceph_assert(!s_requests.empty());
+ InvalidateRequest<I>* req = s_requests.front();
+ req->snap_id = snap_id;
+ req->force = force;
+ req->on_finish = on_finish;
+ s_requests.pop_front();
+ return req;
+ }
+
+ MockInvalidateRequestBase() {
+ s_requests.push_back(static_cast<InvalidateRequest<I>*>(this));
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <typename I>
+std::list<InvalidateRequest<I>*> MockInvalidateRequestBase<I>::s_requests;
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_DiffRequest.cc b/src/test/librbd/object_map/test_mock_DiffRequest.cc
new file mode 100644
index 000000000..c25ae4a95
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_DiffRequest.cc
@@ -0,0 +1,493 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/object_map/DiffRequest.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "librbd/object_map/DiffRequest.cc"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+namespace librbd {
+namespace object_map {
+
+class TestMockObjectMapDiffRequest : public TestMockFixture {
+public:
+ typedef DiffRequest<MockTestImageCtx> MockDiffRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+ }
+
+ void expect_get_flags(MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
+ int32_t flags, int r) {
+ EXPECT_CALL(mock_image_ctx, get_flags(snap_id, _))
+ .WillOnce(WithArg<1>(Invoke([flags, r](uint64_t *out_flags) {
+ *out_flags = flags;
+ return r;
+ })));
+ }
+
+ template <typename Lambda>
+ void expect_load_map(MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
+ const BitVector<2>& object_map, int r,
+ Lambda&& lambda) {
+ std::string snap_oid(ObjectMap<>::object_map_name(mock_image_ctx.id,
+ snap_id));
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(snap_oid, _, StrEq("rbd"), StrEq("object_map_load"), _,
+ _, _, _))
+ .WillOnce(WithArg<5>(Invoke([object_map, r, lambda=std::move(lambda)]
+ (bufferlist* out_bl) {
+ lambda();
+
+ auto out_object_map{object_map};
+ out_object_map.set_crc_enabled(false);
+ encode(out_object_map, *out_bl);
+ return r;
+ })));
+ }
+
+ void expect_load_map(MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
+ const BitVector<2>& object_map, int r) {
+ expect_load_map(mock_image_ctx, snap_id, object_map, r, [](){});
+ }
+
+ librbd::ImageCtx* m_image_ctx = nullptr;
+ BitVector<2> m_object_diff_state;
+};
+
+TEST_F(TestMockObjectMapDiffRequest, InvalidStartSnap) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, CEPH_NOSNAP, 0,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, StartEndSnapEqual) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 1, 1,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(0U, m_object_diff_state.size());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, FastDiffDisabled) {
+ // negative test -- object-map implicitly enables fast-diff
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_OBJECT_MAP));
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, FastDiffInvalid) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, {}, {}, {}, {}, {}}}
+ };
+
+ InSequence seq;
+ expect_get_flags(mock_image_ctx, 1U, RBD_FLAG_FAST_DIFF_INVALID, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, FullDelta) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}},
+ {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 1U, 0, 0);
+
+ BitVector<2> object_map_1;
+ object_map_1.resize(object_count);
+ object_map_1[1] = OBJECT_EXISTS_CLEAN;
+ expect_load_map(mock_image_ctx, 1U, object_map_1, 0);
+
+ expect_get_flags(mock_image_ctx, 2U, 0, 0);
+
+ BitVector<2> object_map_2;
+ object_map_2.resize(object_count);
+ object_map_2[1] = OBJECT_EXISTS_CLEAN;
+ object_map_2[2] = OBJECT_EXISTS;
+ object_map_2[3] = OBJECT_EXISTS;
+ expect_load_map(mock_image_ctx, 2U, object_map_2, 0);
+
+ expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+
+ BitVector<2> object_map_head;
+ object_map_head.resize(object_count);
+ object_map_head[1] = OBJECT_EXISTS_CLEAN;
+ object_map_head[2] = OBJECT_EXISTS_CLEAN;
+ expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ BitVector<2> expected_diff_state;
+ expected_diff_state.resize(object_count);
+ expected_diff_state[1] = DIFF_STATE_DATA_UPDATED;
+ expected_diff_state[2] = DIFF_STATE_DATA_UPDATED;
+ expected_diff_state[3] = DIFF_STATE_HOLE_UPDATED;
+ ASSERT_EQ(expected_diff_state, m_object_diff_state);
+}
+
+TEST_F(TestMockObjectMapDiffRequest, IntermediateDelta) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}},
+ {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 1U, 0, 0);
+
+ BitVector<2> object_map_1;
+ object_map_1.resize(object_count);
+ object_map_1[1] = OBJECT_EXISTS;
+ object_map_1[2] = OBJECT_EXISTS_CLEAN;
+ expect_load_map(mock_image_ctx, 1U, object_map_1, 0);
+
+ expect_get_flags(mock_image_ctx, 2U, 0, 0);
+
+ BitVector<2> object_map_2;
+ object_map_2.resize(object_count);
+ object_map_2[1] = OBJECT_EXISTS_CLEAN;
+ object_map_2[2] = OBJECT_EXISTS;
+ object_map_2[3] = OBJECT_EXISTS;
+ expect_load_map(mock_image_ctx, 2U, object_map_2, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 1, 2,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ BitVector<2> expected_diff_state;
+ expected_diff_state.resize(object_count);
+ expected_diff_state[1] = DIFF_STATE_DATA;
+ expected_diff_state[2] = DIFF_STATE_DATA_UPDATED;
+ expected_diff_state[3] = DIFF_STATE_DATA_UPDATED;
+ ASSERT_EQ(expected_diff_state, m_object_diff_state);
+}
+
+TEST_F(TestMockObjectMapDiffRequest, EndDelta) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}},
+ {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 2U, 0, 0);
+
+ BitVector<2> object_map_2;
+ object_map_2.resize(object_count);
+ object_map_2[1] = OBJECT_EXISTS_CLEAN;
+ object_map_2[2] = OBJECT_EXISTS;
+ object_map_2[3] = OBJECT_EXISTS;
+ expect_load_map(mock_image_ctx, 2U, object_map_2, 0);
+
+ expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+
+ BitVector<2> object_map_head;
+ object_map_head.resize(object_count);
+ object_map_head[1] = OBJECT_EXISTS_CLEAN;
+ object_map_head[2] = OBJECT_EXISTS_CLEAN;
+ expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 2, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ BitVector<2> expected_diff_state;
+ expected_diff_state.resize(object_count);
+ expected_diff_state[1] = DIFF_STATE_DATA;
+ expected_diff_state[2] = DIFF_STATE_DATA;
+ expected_diff_state[3] = DIFF_STATE_HOLE_UPDATED;
+ ASSERT_EQ(expected_diff_state, m_object_diff_state);
+}
+
+TEST_F(TestMockObjectMapDiffRequest, StartSnapDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 1, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, EndSnapDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 1U, 0, 0);
+
+ BitVector<2> object_map_1;
+ object_map_1.resize(object_count);
+ expect_load_map(mock_image_ctx, 1U, object_map_1, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 1, 2,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, IntermediateSnapDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}},
+ {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 1U, 0, 0);
+
+ BitVector<2> object_map_1;
+ object_map_1.resize(object_count);
+ object_map_1[1] = OBJECT_EXISTS_CLEAN;
+ expect_load_map(mock_image_ctx, 1U, object_map_1, 0,
+ [&mock_image_ctx]() { mock_image_ctx.snap_info.erase(2); });
+
+ expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+
+ BitVector<2> object_map_head;
+ object_map_head.resize(object_count);
+ object_map_head[1] = OBJECT_EXISTS_CLEAN;
+ expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ BitVector<2> expected_diff_state;
+ expected_diff_state.resize(object_count);
+ expected_diff_state[1] = DIFF_STATE_DATA_UPDATED;
+ ASSERT_EQ(expected_diff_state, m_object_diff_state);
+}
+
+TEST_F(TestMockObjectMapDiffRequest, LoadObjectMapDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+
+ BitVector<2> object_map_head;
+ expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, -ENOENT);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, LoadIntermediateObjectMapDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 1U, 0, 0);
+
+ BitVector<2> object_map_1;
+ expect_load_map(mock_image_ctx, 1U, object_map_1, -ENOENT);
+
+ expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+
+ BitVector<2> object_map_head;
+ object_map_head.resize(object_count);
+ object_map_head[1] = OBJECT_EXISTS_CLEAN;
+ expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ BitVector<2> expected_diff_state;
+ expected_diff_state.resize(object_count);
+ expected_diff_state[1] = DIFF_STATE_DATA_UPDATED;
+ ASSERT_EQ(expected_diff_state, m_object_diff_state);
+}
+
+TEST_F(TestMockObjectMapDiffRequest, LoadObjectMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 1U, 0, 0);
+
+ BitVector<2> object_map_1;
+ expect_load_map(mock_image_ctx, 1U, object_map_1, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapDiffRequest, ObjectMapTooSmall) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ uint32_t object_count = 5;
+ m_image_ctx->size = object_count * (1 << m_image_ctx->order);
+
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+ mock_image_ctx.snap_info = {
+ {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {},
+ {}, {}, {}}}
+ };
+
+ InSequence seq;
+
+ expect_get_flags(mock_image_ctx, 1U, 0, 0);
+
+ BitVector<2> object_map_1;
+ expect_load_map(mock_image_ctx, 1U, object_map_1, 0);
+
+ C_SaferCond ctx;
+ auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP,
+ &m_object_diff_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace object_map
+} // librbd
diff --git a/src/test/librbd/object_map/test_mock_InvalidateRequest.cc b/src/test/librbd/object_map/test_mock_InvalidateRequest.cc
new file mode 100644
index 000000000..5ea40c03d
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_InvalidateRequest.cc
@@ -0,0 +1,158 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/internal.h"
+#include "librbd/api/Image.h"
+#include "librbd/object_map/InvalidateRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockObjectMapInvalidateRequest : public TestMockFixture {
+public:
+};
+
+TEST_F(TestMockObjectMapInvalidateRequest, UpdatesInMemoryFlag) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP,
+ RBD_FLAG_OBJECT_MAP_INVALID, &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new InvalidateRequest<>(*ictx, CEPH_NOSNAP, true, &cond_ctx);
+
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _, _,
+ _, _))
+ .WillOnce(DoDefault());
+
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP,
+ RBD_FLAG_OBJECT_MAP_INVALID, &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestMockObjectMapInvalidateRequest, UpdatesHeadOnDiskFlag) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new InvalidateRequest<>(*ictx, CEPH_NOSNAP, false, &cond_ctx);
+
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _, _,
+ _, _))
+ .WillOnce(DoDefault());
+
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapInvalidateRequest, UpdatesSnapOnDiskFlag) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new InvalidateRequest<>(*ictx, ictx->snap_id, false,
+ &cond_ctx);
+
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _, _,
+ _, _))
+ .WillOnce(DoDefault());
+
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockObjectMapInvalidateRequest, ErrorOnDiskUpdateWithoutLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new InvalidateRequest<>(*ictx, CEPH_NOSNAP, false, &cond_ctx);
+
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _, _,
+ _, _))
+ .Times(0);
+
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(-EROFS, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapInvalidateRequest, ErrorOnDiskUpdateFailure) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new InvalidateRequest<>(*ictx, CEPH_NOSNAP, false, &cond_ctx);
+
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _, _,
+ _, _))
+ .WillOnce(Return(-EINVAL));
+
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_LockRequest.cc b/src/test/librbd/object_map/test_mock_LockRequest.cc
new file mode 100644
index 000000000..a99cab61e
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_LockRequest.cc
@@ -0,0 +1,221 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "cls/lock/cls_lock_ops.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/object_map/LockRequest.h"
+
+// template definitions
+#include "librbd/object_map/LockRequest.cc"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockObjectMapLockRequest : public TestMockFixture {
+public:
+ typedef LockRequest<MockImageCtx> MockLockRequest;
+
+ void expect_lock(MockImageCtx &mock_image_ctx, int r) {
+ std::string oid(ObjectMap<>::object_map_name(mock_image_ctx.id,
+ CEPH_NOSNAP));
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("lock"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_get_lock_info(MockImageCtx &mock_image_ctx, int r) {
+ std::string oid(ObjectMap<>::object_map_name(mock_image_ctx.id,
+ CEPH_NOSNAP));
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("get_info"), _,
+ _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ entity_name_t entity1(entity_name_t::CLIENT(1));
+ entity_name_t entity2(entity_name_t::CLIENT(2));
+
+ cls_lock_get_info_reply reply;
+ reply.lockers = decltype(reply.lockers){
+ {rados::cls::lock::locker_id_t(entity1, "cookie1"),
+ rados::cls::lock::locker_info_t()},
+ {rados::cls::lock::locker_id_t(entity2, "cookie2"),
+ rados::cls::lock::locker_info_t()}};
+
+ bufferlist bl;
+ encode(reply, bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
+
+ std::string str(bl.c_str(), bl.length());
+ expect.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(str)), Return(r)));
+ }
+ }
+
+ void expect_break_lock(MockImageCtx &mock_image_ctx, int r) {
+ std::string oid(ObjectMap<>::object_map_name(mock_image_ctx.id,
+ CEPH_NOSNAP));
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("break_lock"),
+ _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.Times(2).WillRepeatedly(Return(0));
+ }
+ }
+};
+
+TEST_F(TestMockObjectMapLockRequest, Success) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, 0);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapLockRequest, LockBusy) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_get_lock_info(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, 0);
+ expect_lock(mock_image_ctx, 0);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapLockRequest, LockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, -ENOENT);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapLockRequest, GetLockInfoMissing) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_get_lock_info(mock_image_ctx, -ENOENT);
+ expect_lock(mock_image_ctx, 0);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapLockRequest, GetLockInfoError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_get_lock_info(mock_image_ctx, -EINVAL);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapLockRequest, BreakLockMissing) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_get_lock_info(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, -ENOENT);
+ expect_lock(mock_image_ctx, 0);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapLockRequest, BreakLockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_get_lock_info(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, -EINVAL);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapLockRequest, LockErrorAfterBrokeLock) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockLockRequest *req = new MockLockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_lock(mock_image_ctx, -EBUSY);
+ expect_get_lock_info(mock_image_ctx, 0);
+ expect_break_lock(mock_image_ctx, 0);
+ expect_lock(mock_image_ctx, -EBUSY);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_RefreshRequest.cc b/src/test/librbd/object_map/test_mock_RefreshRequest.cc
new file mode 100644
index 000000000..43d9c3b43
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_RefreshRequest.cc
@@ -0,0 +1,465 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/object_map/mock/MockInvalidateRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/ObjectMap.h"
+#include "librbd/object_map/RefreshRequest.h"
+#include "librbd/object_map/LockRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockObjectMapImageCtx : public MockImageCtx {
+ MockObjectMapImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace object_map {
+
+template <>
+class LockRequest<MockObjectMapImageCtx> {
+public:
+ static LockRequest *s_instance;
+ static LockRequest *create(MockObjectMapImageCtx &image_ctx, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ LockRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct InvalidateRequest<MockObjectMapImageCtx> :
+ public MockInvalidateRequestBase<MockObjectMapImageCtx> {
+};
+
+LockRequest<MockObjectMapImageCtx> *LockRequest<MockObjectMapImageCtx>::s_instance = nullptr;
+
+} // namespace object_map
+} // namespace librbd
+
+// template definitions
+#include "librbd/object_map/RefreshRequest.cc"
+#include "librbd/object_map/LockRequest.cc"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockObjectMapRefreshRequest : public TestMockFixture {
+public:
+ static const uint64_t TEST_SNAP_ID = 123;
+
+ typedef RefreshRequest<MockObjectMapImageCtx> MockRefreshRequest;
+ typedef LockRequest<MockObjectMapImageCtx> MockLockRequest;
+ typedef InvalidateRequest<MockObjectMapImageCtx> MockInvalidateRequest;
+
+ void expect_object_map_lock(MockObjectMapImageCtx &mock_image_ctx,
+ MockLockRequest &mock_lock_request) {
+ EXPECT_CALL(mock_lock_request, send())
+ .WillOnce(FinishRequest(&mock_lock_request, 0,
+ &mock_image_ctx));
+ }
+
+ void expect_object_map_load(MockObjectMapImageCtx &mock_image_ctx,
+ ceph::BitVector<2> *object_map, uint64_t snap_id,
+ int r) {
+ std::string oid(ObjectMap<>::object_map_name(mock_image_ctx.id, snap_id));
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(oid, _, StrEq("rbd"),
+ StrEq("object_map_load"), _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ ceph_assert(object_map);
+ object_map->set_crc_enabled(false);
+
+ bufferlist bl;
+ encode(*object_map, bl);
+
+ std::string str(bl.c_str(), bl.length());
+ expect.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(str)), Return(0)));
+ }
+ }
+
+ void expect_get_image_size(MockObjectMapImageCtx &mock_image_ctx, uint64_t snap_id,
+ uint64_t size) {
+ EXPECT_CALL(mock_image_ctx, get_image_size(snap_id))
+ .WillOnce(Return(size));
+ }
+
+ void expect_invalidate_request(MockObjectMapImageCtx &mock_image_ctx,
+ MockInvalidateRequest &invalidate_request,
+ int r) {
+ EXPECT_CALL(invalidate_request, send())
+ .WillOnce(FinishRequest(&invalidate_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_truncate_request(MockObjectMapImageCtx &mock_image_ctx) {
+ std::string oid(ObjectMap<>::object_map_name(mock_image_ctx.id,
+ TEST_SNAP_ID));
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), truncate(oid, 0, _))
+ .WillOnce(Return(0));
+ }
+
+ void expect_object_map_resize(MockObjectMapImageCtx &mock_image_ctx,
+ uint64_t num_objects, int r) {
+ std::string oid(ObjectMap<>::object_map_name(mock_image_ctx.id,
+ TEST_SNAP_ID));
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(oid, _, StrEq("rbd"),
+ StrEq("object_map_resize"), _, _, _, _));
+ expect.WillOnce(Return(r));
+ }
+
+ void init_object_map(MockObjectMapImageCtx &mock_image_ctx,
+ ceph::BitVector<2> *object_map) {
+ uint64_t num_objs = Striper::get_num_objects(
+ mock_image_ctx.layout, mock_image_ctx.image_ctx->size);
+ object_map->resize(num_objs);
+ for (uint64_t i = 0; i < num_objs; ++i) {
+ (*object_map)[i] = rand() % 3;
+ }
+ }
+};
+
+TEST_F(TestMockObjectMapRefreshRequest, SuccessHead) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockLockRequest mock_lock_request;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, CEPH_NOSNAP, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, CEPH_NOSNAP,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_lock(mock_image_ctx, mock_lock_request);
+ expect_object_map_load(mock_image_ctx, &on_disk_object_map, CEPH_NOSNAP, 0);
+ expect_get_image_size(mock_image_ctx, CEPH_NOSNAP,
+ mock_image_ctx.image_ctx->size);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(on_disk_object_map, object_map);
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, SuccessSnapshot) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, &on_disk_object_map, TEST_SNAP_ID, 0);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(on_disk_object_map, object_map);
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, LoadError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, nullptr, TEST_SNAP_ID, -ENOENT);
+
+ MockInvalidateRequest invalidate_request;
+ expect_invalidate_request(mock_image_ctx, invalidate_request, 0);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, LoadInvalidateError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, nullptr, TEST_SNAP_ID, -ENOENT);
+
+ MockInvalidateRequest invalidate_request;
+ expect_invalidate_request(mock_image_ctx, invalidate_request, -EPERM);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, LoadCorrupt) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, nullptr, TEST_SNAP_ID, -EINVAL);
+
+ MockInvalidateRequest invalidate_request;
+ expect_invalidate_request(mock_image_ctx, invalidate_request, 0);
+ expect_truncate_request(mock_image_ctx);
+ expect_object_map_resize(mock_image_ctx, on_disk_object_map.size(), 0);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, TooSmall) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ ceph::BitVector<2> small_object_map;
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, &small_object_map, TEST_SNAP_ID, 0);
+
+ MockInvalidateRequest invalidate_request;
+ expect_invalidate_request(mock_image_ctx, invalidate_request, 0);
+ expect_object_map_resize(mock_image_ctx, on_disk_object_map.size(), 0);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, TooSmallInvalidateError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ ceph::BitVector<2> small_object_map;
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, &small_object_map, TEST_SNAP_ID, 0);
+
+ MockInvalidateRequest invalidate_request;
+ expect_invalidate_request(mock_image_ctx, invalidate_request, -EPERM);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, TooLarge) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ ceph::BitVector<2> large_object_map;
+ large_object_map.resize(on_disk_object_map.size() * 2);
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, &large_object_map, TEST_SNAP_ID, 0);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, ResizeError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ ceph::BitVector<2> small_object_map;
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+ expect_object_map_load(mock_image_ctx, &small_object_map, TEST_SNAP_ID, 0);
+
+ MockInvalidateRequest invalidate_request;
+ expect_invalidate_request(mock_image_ctx, invalidate_request, 0);
+ expect_object_map_resize(mock_image_ctx, on_disk_object_map.size(), -ESTALE);
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ mock_image_ctx.image_ctx->size);
+
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapRefreshRequest, LargeImageError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+ ceph::BitVector<2> on_disk_object_map;
+ init_object_map(mock_image_ctx, &on_disk_object_map);
+
+ C_SaferCond ctx;
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ MockRefreshRequest *req = new MockRefreshRequest(
+ mock_image_ctx, &object_map_lock, &object_map, TEST_SNAP_ID, &ctx);
+
+ InSequence seq;
+ expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+ std::numeric_limits<int64_t>::max());
+
+ MockInvalidateRequest invalidate_request;
+ expect_invalidate_request(mock_image_ctx, invalidate_request, 0);
+
+ req->send();
+ ASSERT_EQ(-EFBIG, ctx.wait());
+}
+
+} // namespace object_map
+} // namespace librbd
+
diff --git a/src/test/librbd/object_map/test_mock_ResizeRequest.cc b/src/test/librbd/object_map/test_mock_ResizeRequest.cc
new file mode 100644
index 000000000..d6a3dd91c
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_ResizeRequest.cc
@@ -0,0 +1,154 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/api/Image.h"
+#include "librbd/object_map/ResizeRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockObjectMapResizeRequest : public TestMockFixture {
+public:
+ void expect_resize(librbd::ImageCtx *ictx, uint64_t snap_id, int r) {
+ std::string oid(ObjectMap<>::object_map_name(ictx->id, snap_id));
+ if (snap_id == CEPH_NOSNAP) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("assert_locked"), _, _, _,
+ _))
+ .WillOnce(DoDefault());
+ }
+
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("object_map_resize"), _, _,
+ _, _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("object_map_resize"), _, _,
+ _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_invalidate(librbd::ImageCtx *ictx) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _,
+ _, _, _))
+ .WillOnce(DoDefault());
+ }
+};
+
+TEST_F(TestMockObjectMapResizeRequest, UpdateInMemory) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new ResizeRequest(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, object_map.size(),
+ OBJECT_EXISTS, &cond_ctx);
+ req->send();
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ for (uint64_t i = 0; i < object_map.size(); ++i) {
+ ASSERT_EQ(i == 0 ? OBJECT_NONEXISTENT : OBJECT_EXISTS,
+ object_map[i]);
+ }
+}
+
+TEST_F(TestMockObjectMapResizeRequest, UpdateHeadOnDisk) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ expect_resize(ictx, CEPH_NOSNAP, 0);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new ResizeRequest(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, object_map.size(),
+ OBJECT_EXISTS, &cond_ctx);
+ req->send();
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapResizeRequest, UpdateSnapOnDisk) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ uint64_t snap_id = ictx->snap_id;
+ expect_resize(ictx, snap_id, 0);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new ResizeRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, object_map.size(),
+ OBJECT_EXISTS, &cond_ctx);
+ req->send();
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapResizeRequest, UpdateOnDiskError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ expect_resize(ictx, CEPH_NOSNAP, -EINVAL);
+ expect_invalidate(ictx);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new ResizeRequest(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, object_map.size(),
+ OBJECT_EXISTS, &cond_ctx);
+ req->send();
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_SnapshotCreateRequest.cc b/src/test/librbd/object_map/test_mock_SnapshotCreateRequest.cc
new file mode 100644
index 000000000..7f77aaf83
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_SnapshotCreateRequest.cc
@@ -0,0 +1,232 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/object_map/SnapshotCreateRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockObjectMapSnapshotCreateRequest : public TestMockFixture {
+public:
+ void inject_snap_info(librbd::ImageCtx *ictx, uint64_t snap_id) {
+ std::unique_lock image_locker{ictx->image_lock};
+ ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "snap name", snap_id,
+ ictx->size, ictx->parent_md,
+ RBD_PROTECTION_STATUS_UNPROTECTED, 0, utime_t());
+ }
+
+ void expect_read_map(librbd::ImageCtx *ictx, int r) {
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ read(ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP),
+ 0, 0, _, _, _)).WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ read(ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP),
+ 0, 0, _, _, _)).WillOnce(DoDefault());
+ }
+ }
+
+ void expect_write_map(librbd::ImageCtx *ictx, uint64_t snap_id, int r) {
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ write_full(
+ ObjectMap<>::object_map_name(ictx->id, snap_id), _, _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ write_full(
+ ObjectMap<>::object_map_name(ictx->id, snap_id), _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_add_snapshot(librbd::ImageCtx *ictx, int r) {
+ std::string oid(ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP));
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("assert_locked"), _, _, _,
+ _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("assert_locked"), _, _, _,
+ _))
+ .WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("object_map_snap_add"), _, _,
+ _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_invalidate(librbd::ImageCtx *ictx) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _,
+ _, _, _))
+ .WillOnce(DoDefault());
+ }
+};
+
+TEST_F(TestMockObjectMapSnapshotCreateRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+
+ uint64_t snap_id = 1;
+ inject_snap_info(ictx, snap_id);
+ expect_read_map(ictx, 0);
+ expect_write_map(ictx, snap_id, 0);
+ if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
+ expect_add_snapshot(ictx, 0);
+ }
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotCreateRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotCreateRequest, ReadMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+
+ uint64_t snap_id = 1;
+ inject_snap_info(ictx, snap_id);
+ expect_read_map(ictx, -ENOENT);
+ expect_invalidate(ictx);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotCreateRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotCreateRequest, WriteMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+
+ uint64_t snap_id = 1;
+ inject_snap_info(ictx, snap_id);
+ expect_read_map(ictx, 0);
+ expect_write_map(ictx, snap_id, -EINVAL);
+ expect_invalidate(ictx);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotCreateRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotCreateRequest, AddSnapshotError) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+
+ uint64_t snap_id = 1;
+ inject_snap_info(ictx, snap_id);
+ expect_read_map(ictx, 0);
+ expect_write_map(ictx, snap_id, 0);
+ expect_add_snapshot(ictx, -EINVAL);
+ expect_invalidate(ictx);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotCreateRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotCreateRequest, FlagCleanObjects) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1024);
+ for (uint64_t i = 0; i < object_map.size(); ++i) {
+ object_map[i] = i % 2 == 0 ? OBJECT_EXISTS : OBJECT_NONEXISTENT;
+ }
+
+ uint64_t snap_id = 1;
+ inject_snap_info(ictx, snap_id);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotCreateRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ for (uint64_t i = 0; i < object_map.size(); ++i) {
+ ASSERT_EQ(i % 2 == 0 ? OBJECT_EXISTS_CLEAN : OBJECT_NONEXISTENT,
+ object_map[i]);
+ }
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_SnapshotRemoveRequest.cc b/src/test/librbd/object_map/test_mock_SnapshotRemoveRequest.cc
new file mode 100644
index 000000000..20318743d
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_SnapshotRemoveRequest.cc
@@ -0,0 +1,345 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/object_map/SnapshotRemoveRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockObjectMapSnapshotRemoveRequest : public TestMockFixture {
+public:
+ void expect_load_map(librbd::ImageCtx *ictx, uint64_t snap_id, int r) {
+ std::string snap_oid(ObjectMap<>::object_map_name(ictx->id, snap_id));
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(snap_oid, _, StrEq("rbd"), StrEq("object_map_load"), _,
+ _, _, _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(snap_oid, _, StrEq("rbd"), StrEq("object_map_load"), _,
+ _, _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_remove_snapshot(librbd::ImageCtx *ictx, int r) {
+ std::string oid(ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP));
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("assert_locked"), _, _, _,
+ _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("assert_locked"), _, _, _,
+ _))
+ .WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("object_map_snap_remove"), _,
+ _, _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_remove_map(librbd::ImageCtx *ictx, uint64_t snap_id, int r) {
+ std::string snap_oid(ObjectMap<>::object_map_name(ictx->id, snap_id));
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx), remove(snap_oid, _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx), remove(snap_oid, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_invalidate(librbd::ImageCtx *ictx) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _,
+ _, _, _))
+ .WillOnce(DoDefault());
+ }
+};
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
+ expect_load_map(ictx, snap_id, 0);
+ expect_remove_snapshot(ictx, 0);
+ }
+ expect_remove_map(ictx, snap_id, 0);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, LoadMapMissing) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ auto snap_it = ictx->snap_info.find(snap_id);
+ ASSERT_NE(ictx->snap_info.end(), snap_it);
+ snap_it->second.flags |= RBD_FLAG_OBJECT_MAP_INVALID;
+
+ expect_load_map(ictx, snap_id, -ENOENT);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ {
+ // shouldn't invalidate the HEAD revision when we fail to load
+ // the already deleted snapshot
+ std::shared_lock image_locker{ictx->image_lock};
+ uint64_t flags;
+ ASSERT_EQ(0, ictx->get_flags(CEPH_NOSNAP, &flags));
+ ASSERT_EQ(0U, flags & RBD_FLAG_OBJECT_MAP_INVALID);
+ }
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, LoadMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_load_map(ictx, snap_id, -EINVAL);
+ expect_invalidate(ictx);
+ expect_remove_map(ictx, snap_id, 0);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, RemoveSnapshotMissing) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_load_map(ictx, snap_id, 0);
+ expect_remove_snapshot(ictx, -ENOENT);
+ expect_remove_map(ictx, snap_id, 0);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, RemoveSnapshotError) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_load_map(ictx, snap_id, 0);
+ expect_remove_snapshot(ictx, -EINVAL);
+ expect_invalidate(ictx);
+ expect_remove_map(ictx, snap_id, 0);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, RemoveMapMissing) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
+ expect_load_map(ictx, snap_id, 0);
+ expect_remove_snapshot(ictx, 0);
+ }
+ expect_remove_map(ictx, snap_id, -ENOENT);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, RemoveMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
+ expect_load_map(ictx, snap_id, 0);
+ expect_remove_snapshot(ictx, 0);
+ }
+ expect_remove_map(ictx, snap_id, -EINVAL);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRemoveRequest, ScrubCleanObjects) {
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ librbd::NoOpProgressContext prog_ctx;
+ uint64_t size = 4294967296; // 4GB = 1024 * 4MB
+ ASSERT_EQ(0, resize(ictx, size));
+
+ // update image objectmap for snap inherit
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1024);
+ for (uint64_t i = 512; i < object_map.size(); ++i) {
+ object_map[i] = i % 2 == 0 ? OBJECT_EXISTS : OBJECT_NONEXISTENT;
+ }
+
+ C_SaferCond cond_ctx1;
+ {
+ librbd::ObjectMap<> *om = new librbd::ObjectMap<>(*ictx, ictx->snap_id);
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ om->set_object_map(object_map);
+ om->aio_save(&cond_ctx1);
+ om->put();
+ }
+ ASSERT_EQ(0, cond_ctx1.wait());
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ // simutate the image objectmap state after creating snap
+ for (uint64_t i = 512; i < object_map.size(); ++i) {
+ object_map[i] = i % 2 == 0 ? OBJECT_EXISTS_CLEAN : OBJECT_NONEXISTENT;
+ }
+
+ C_SaferCond cond_ctx2;
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ AsyncRequest<> *request = new SnapshotRemoveRequest(
+ *ictx, &object_map_lock, &object_map, snap_id, &cond_ctx2);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::unique_lock image_locker{ictx->image_lock};
+ request->send();
+ }
+ ASSERT_EQ(0, cond_ctx2.wait());
+
+ for (uint64_t i = 512; i < object_map.size(); ++i) {
+ ASSERT_EQ(i % 2 == 0 ? OBJECT_EXISTS : OBJECT_NONEXISTENT,
+ object_map[i]);
+ }
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_SnapshotRollbackRequest.cc b/src/test/librbd/object_map/test_mock_SnapshotRollbackRequest.cc
new file mode 100644
index 000000000..7b89a0996
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_SnapshotRollbackRequest.cc
@@ -0,0 +1,148 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/object_map/SnapshotRollbackRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockObjectMapSnapshotRollbackRequest : public TestMockFixture {
+public:
+ void expect_read_map(librbd::ImageCtx *ictx, uint64_t snap_id, int r) {
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ read(ObjectMap<>::object_map_name(ictx->id, snap_id),
+ 0, 0, _, _, _)).WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ read(ObjectMap<>::object_map_name(ictx->id, snap_id),
+ 0, 0, _, _, _)).WillOnce(DoDefault());
+ }
+ }
+
+ void expect_write_map(librbd::ImageCtx *ictx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP), _,
+ StrEq("lock"), StrEq("assert_locked"), _, _, _, _))
+ .WillOnce(DoDefault());
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ write_full(
+ ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP), _, _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ write_full(
+ ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP), _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_invalidate(librbd::ImageCtx *ictx, uint32_t times) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _,
+ _, _, _))
+ .Times(times)
+ .WillRepeatedly(DoDefault());
+ }
+};
+
+TEST_F(TestMockObjectMapSnapshotRollbackRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_read_map(ictx, snap_id, 0);
+ expect_write_map(ictx, 0);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRollbackRequest(
+ *ictx, snap_id, &cond_ctx);
+ request->send();
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRollbackRequest, ReadMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_read_map(ictx, snap_id, -ENOENT);
+ expect_invalidate(ictx, 2);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRollbackRequest(
+ *ictx, snap_id, &cond_ctx);
+ request->send();
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ uint64_t flags;
+ ASSERT_EQ(0, ictx->get_flags(snap_id, &flags));
+ ASSERT_NE(0U, flags & RBD_FLAG_OBJECT_MAP_INVALID);
+ }
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP,
+ RBD_FLAG_OBJECT_MAP_INVALID, &flags_set));
+ ASSERT_TRUE(flags_set);
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapSnapshotRollbackRequest, WriteMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_read_map(ictx, snap_id, 0);
+ expect_write_map(ictx, -EINVAL);
+ expect_invalidate(ictx, 1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *request = new SnapshotRollbackRequest(
+ *ictx, snap_id, &cond_ctx);
+ request->send();
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ uint64_t flags;
+ ASSERT_EQ(0, ictx->get_flags(snap_id, &flags));
+ ASSERT_EQ(0U, flags & RBD_FLAG_OBJECT_MAP_INVALID);
+ }
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP,
+ RBD_FLAG_OBJECT_MAP_INVALID, &flags_set));
+ ASSERT_TRUE(flags_set);
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_UnlockRequest.cc b/src/test/librbd/object_map/test_mock_UnlockRequest.cc
new file mode 100644
index 000000000..f91ee001d
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_UnlockRequest.cc
@@ -0,0 +1,69 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "cls/lock/cls_lock_ops.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/object_map/UnlockRequest.h"
+
+// template definitions
+#include "librbd/object_map/UnlockRequest.cc"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockObjectMapUnlockRequest : public TestMockFixture {
+public:
+ typedef UnlockRequest<MockImageCtx> MockUnlockRequest;
+
+ void expect_unlock(MockImageCtx &mock_image_ctx, int r) {
+ std::string oid(ObjectMap<>::object_map_name(mock_image_ctx.id,
+ CEPH_NOSNAP));
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("unlock"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+};
+
+TEST_F(TestMockObjectMapUnlockRequest, Success) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockUnlockRequest *req = new MockUnlockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_unlock(mock_image_ctx, 0);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockObjectMapUnlockRequest, UnlockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ C_SaferCond ctx;
+ MockUnlockRequest *req = new MockUnlockRequest(mock_image_ctx, &ctx);
+
+ InSequence seq;
+ expect_unlock(mock_image_ctx, -ENOENT);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/object_map/test_mock_UpdateRequest.cc b/src/test/librbd/object_map/test_mock_UpdateRequest.cc
new file mode 100644
index 000000000..c240dec00
--- /dev/null
+++ b/src/test/librbd/object_map/test_mock_UpdateRequest.cc
@@ -0,0 +1,291 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Image.h"
+#include "librbd/object_map/UpdateRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace object_map {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockObjectMapUpdateRequest : public TestMockFixture {
+public:
+ void expect_update(librbd::ImageCtx *ictx, uint64_t snap_id,
+ uint64_t start_object_no, uint64_t end_object_no,
+ uint8_t new_state,
+ const boost::optional<uint8_t>& current_state, int r) {
+ bufferlist bl;
+ encode(start_object_no, bl);
+ encode(end_object_no, bl);
+ encode(new_state, bl);
+ encode(current_state, bl);
+
+ std::string oid(ObjectMap<>::object_map_name(ictx->id, snap_id));
+ if (snap_id == CEPH_NOSNAP) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("lock"), StrEq("assert_locked"), _, _, _,
+ _))
+ .WillOnce(DoDefault());
+ }
+
+ if (r < 0) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("object_map_update"),
+ ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ } else {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("object_map_update"),
+ ContentsEqual(bl), _, _, _))
+ .WillOnce(DoDefault());
+ }
+ }
+
+ void expect_invalidate(librbd::ImageCtx *ictx) {
+ EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx),
+ exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _,
+ _, _, _))
+ .WillOnce(DoDefault());
+ }
+};
+
+TEST_F(TestMockObjectMapUpdateRequest, UpdateInMemory) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ librbd::NoOpProgressContext no_progress;
+ ASSERT_EQ(0, ictx->operations->resize(4 << ictx->order, true, no_progress));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(4);
+ for (uint64_t i = 0; i < object_map.size(); ++i) {
+ object_map[i] = i % 4;
+ }
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(),
+ OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ std::unique_lock object_map_locker{object_map_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ for (uint64_t i = 0; i < object_map.size(); ++i) {
+ if (i % 4 == OBJECT_EXISTS || i % 4 == OBJECT_EXISTS_CLEAN) {
+ ASSERT_EQ(OBJECT_NONEXISTENT, object_map[i]);
+ } else {
+ ASSERT_EQ(i % 4, object_map[i]);
+ }
+ }
+}
+
+TEST_F(TestMockObjectMapUpdateRequest, UpdateHeadOnDisk) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS, 0);
+
+ ceph::shared_mutex object_map_lock =
+ ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(),
+ OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ std::unique_lock object_map_locker{object_map_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapUpdateRequest, UpdateSnapOnDisk) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ uint64_t snap_id = ictx->snap_id;
+ expect_update(ictx, snap_id, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS, 0);
+
+ ceph::shared_mutex object_map_lock =
+ ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map_lock, &object_map, snap_id, 0, object_map.size(),
+ OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ std::unique_lock object_map_locker{object_map_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapUpdateRequest, UpdateOnDiskError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS,
+ -EINVAL);
+ expect_invalidate(ictx);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(),
+ OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ std::unique_lock object_map_locker{object_map_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+TEST_F(TestMockObjectMapUpdateRequest, RebuildSnapOnDisk) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+ ASSERT_EQ(CEPH_NOSNAP, ictx->snap_id);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_update(ictx, snap_id, 0, 1, OBJECT_EXISTS_CLEAN,
+ boost::optional<uint8_t>(), 0);
+ expect_unlock_exclusive_lock(*ictx);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map_lock, &object_map, snap_id, 0, object_map.size(),
+ OBJECT_EXISTS_CLEAN, boost::optional<uint8_t>(), {}, false, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ std::unique_lock object_map_locker{object_map_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ // do not update the in-memory map if rebuilding a snapshot
+ ASSERT_NE(OBJECT_EXISTS_CLEAN, object_map[0]);
+}
+
+TEST_F(TestMockObjectMapUpdateRequest, BatchUpdate) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ librbd::NoOpProgressContext no_progress;
+ ASSERT_EQ(0, ictx->operations->resize(712312 * ictx->get_object_size(), false,
+ no_progress));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ expect_unlock_exclusive_lock(*ictx);
+ InSequence seq;
+ expect_update(ictx, CEPH_NOSNAP, 0, 262144, OBJECT_NONEXISTENT, OBJECT_EXISTS,
+ 0);
+ expect_update(ictx, CEPH_NOSNAP, 262144, 524288, OBJECT_NONEXISTENT,
+ OBJECT_EXISTS, 0);
+ expect_update(ictx, CEPH_NOSNAP, 524288, 712312, OBJECT_NONEXISTENT,
+ OBJECT_EXISTS, 0);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(712312);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(),
+ OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ std::unique_lock object_map_locker{object_map_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockObjectMapUpdateRequest, IgnoreMissingObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS,
+ -ENOENT);
+
+ ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(),
+ OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, true, &cond_ctx);
+ {
+ std::shared_lock image_locker{ictx->image_lock};
+ std::unique_lock object_map_locker{object_map_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
+} // namespace object_map
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc b/src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc
new file mode 100644
index 000000000..171ac41a7
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc
@@ -0,0 +1,538 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournalPolicy.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/internal.h"
+#include "librbd/Journal.h"
+#include "librbd/image/SetFlagsRequest.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/mirror/DisableRequest.h"
+#include "librbd/journal/RemoveRequest.h"
+#include "librbd/journal/StandardPolicy.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "librbd/object_map/RemoveRequest.h"
+#include "librbd/operation/DisableFeaturesRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockOperationImageCtx : public MockImageCtx {
+ MockOperationImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+template<>
+struct Journal<MockOperationImageCtx> {
+ static void get_work_queue(CephContext*, MockContextWQ**) {
+ }
+};
+
+namespace image {
+
+template<>
+class SetFlagsRequest<MockOperationImageCtx> {
+public:
+ static SetFlagsRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static SetFlagsRequest *create(MockOperationImageCtx *image_ctx, uint64_t flags,
+ uint64_t mask, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SetFlagsRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SetFlagsRequest<MockOperationImageCtx> *SetFlagsRequest<MockOperationImageCtx>::s_instance;
+
+} // namespace image
+
+namespace journal {
+
+template<>
+class RemoveRequest<MockOperationImageCtx> {
+public:
+ static RemoveRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static RemoveRequest *create(IoCtx &ioctx, const std::string &imageid,
+ const std::string &client_id,
+ MockContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+RemoveRequest<MockOperationImageCtx> *RemoveRequest<MockOperationImageCtx>::s_instance;
+
+template<>
+class StandardPolicy<MockOperationImageCtx> : public MockJournalPolicy {
+public:
+ StandardPolicy(MockOperationImageCtx* image_ctx) {
+ }
+};
+
+template <>
+struct TypeTraits<MockOperationImageCtx> {
+ typedef librbd::MockContextWQ ContextWQ;
+};
+
+} // namespace journal
+
+namespace mirror {
+
+template<>
+class DisableRequest<MockOperationImageCtx> {
+public:
+ static DisableRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static DisableRequest *create(MockOperationImageCtx *image_ctx, bool force,
+ bool remove, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ DisableRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+DisableRequest<MockOperationImageCtx> *DisableRequest<MockOperationImageCtx>::s_instance;
+
+} // namespace mirror
+
+namespace object_map {
+
+template<>
+class RemoveRequest<MockOperationImageCtx> {
+public:
+ static RemoveRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static RemoveRequest *create(MockOperationImageCtx *image_ctx, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+RemoveRequest<MockOperationImageCtx> *RemoveRequest<MockOperationImageCtx>::s_instance;
+
+} // namespace object_map
+
+template <>
+struct AsyncRequest<MockOperationImageCtx> : public AsyncRequest<MockImageCtx> {
+ MockOperationImageCtx &m_image_ctx;
+
+ AsyncRequest(MockOperationImageCtx &image_ctx, Context *on_finish)
+ : AsyncRequest<MockImageCtx>(image_ctx, on_finish), m_image_ctx(image_ctx) {
+ }
+};
+
+} // namespace librbd
+
+// template definitions
+#include "librbd/AsyncRequest.cc"
+#include "librbd/AsyncObjectThrottle.cc"
+#include "librbd/operation/Request.cc"
+#include "librbd/operation/DisableFeaturesRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::_;
+
+class TestMockOperationDisableFeaturesRequest : public TestMockFixture {
+public:
+ typedef librbd::image::SetFlagsRequest<MockOperationImageCtx> MockSetFlagsRequest;
+ typedef librbd::journal::RemoveRequest<MockOperationImageCtx> MockRemoveJournalRequest;
+ typedef librbd::mirror::DisableRequest<MockOperationImageCtx> MockDisableMirrorRequest;
+ typedef librbd::object_map::RemoveRequest<MockOperationImageCtx> MockRemoveObjectMapRequest;
+ typedef DisableFeaturesRequest<MockOperationImageCtx> MockDisableFeaturesRequest;
+
+ class MirrorModeEnabler {
+ public:
+ MirrorModeEnabler(librados::IoCtx &ioctx, cls::rbd::MirrorMode mirror_mode)
+ : m_ioctx(ioctx), m_mirror_mode(mirror_mode) {
+ EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx, "test-uuid"));
+ EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx, m_mirror_mode));
+ }
+
+ ~MirrorModeEnabler() {
+ EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
+ &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
+ }
+ private:
+ librados::IoCtx &m_ioctx;
+ cls::rbd::MirrorMode m_mirror_mode;
+ };
+
+ void expect_prepare_lock(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_))
+ .WillOnce(Invoke([](Context *on_ready) {
+ on_ready->complete(0);
+ }));
+ expect_op_work_queue(mock_image_ctx);
+ }
+
+ void expect_handle_prepare_lock_complete(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
+ }
+
+ void expect_block_writes(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, block_writes(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_unblock_writes(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, unblock_writes()).Times(1);
+ }
+
+ void expect_verify_lock_ownership(MockOperationImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillRepeatedly(Return(true));
+ }
+ }
+
+ void expect_block_requests(MockOperationImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, block_requests(0)).Times(1);
+ }
+ }
+
+ void expect_unblock_requests(MockOperationImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, unblock_requests()).Times(1);
+ }
+ }
+
+ void expect_set_flags_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockSetFlagsRequest &mock_set_flags_request, int r) {
+ EXPECT_CALL(mock_set_flags_request, send())
+ .WillOnce(FinishRequest(&mock_set_flags_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_disable_mirror_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockDisableMirrorRequest &mock_disable_mirror_request, int r) {
+ EXPECT_CALL(mock_disable_mirror_request, send())
+ .WillOnce(FinishRequest(&mock_disable_mirror_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_close_journal(MockOperationImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.journal, close(_))
+ .WillOnce(Invoke([&mock_image_ctx, r](Context *on_finish) {
+ mock_image_ctx.journal = nullptr;
+ mock_image_ctx.image_ctx->op_work_queue->queue(on_finish, r);
+ }));
+ }
+
+ void expect_remove_journal_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockRemoveJournalRequest &mock_remove_journal_request, int r) {
+ EXPECT_CALL(mock_remove_journal_request, send())
+ .WillOnce(FinishRequest(&mock_remove_journal_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_remove_object_map_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockRemoveObjectMapRequest &mock_remove_object_map_request, int r) {
+ EXPECT_CALL(mock_remove_object_map_request, send())
+ .WillOnce(FinishRequest(&mock_remove_object_map_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_notify_update(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, notify_update(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+};
+
+TEST_F(TestMockOperationDisableFeaturesRequest, All) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ uint64_t features_to_disable = RBD_FEATURES_MUTABLE & features;
+
+ REQUIRE(features_to_disable);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockSetFlagsRequest mock_set_flags_request;
+ MockRemoveJournalRequest mock_remove_journal_request;
+ MockDisableMirrorRequest mock_disable_mirror_request;
+ MockRemoveObjectMapRequest mock_remove_object_map_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ if (mock_image_ctx.journal != nullptr) {
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ }
+ expect_block_requests(mock_image_ctx);
+ if (features_to_disable & RBD_FEATURE_JOURNALING) {
+ expect_disable_mirror_request_send(mock_image_ctx,
+ mock_disable_mirror_request, 0);
+ expect_close_journal(mock_image_ctx, 0);
+ expect_remove_journal_request_send(mock_image_ctx,
+ mock_remove_journal_request, 0);
+ }
+ if (features_to_disable & RBD_FEATURE_OBJECT_MAP) {
+ expect_remove_object_map_request_send(mock_image_ctx,
+ mock_remove_object_map_request, 0);
+ }
+ if (features_to_disable & (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF)) {
+ expect_set_flags_request_send(mock_image_ctx,
+ mock_set_flags_request, 0);
+ }
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, features_to_disable, false);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockSetFlagsRequest mock_set_flags_request;
+ MockRemoveObjectMapRequest mock_remove_object_map_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ if (mock_image_ctx.journal != nullptr) {
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ }
+ expect_block_requests(mock_image_ctx);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_remove_object_map_request_send(mock_image_ctx,
+ mock_remove_object_map_request, 0);
+ expect_set_flags_request_send(mock_image_ctx,
+ mock_set_flags_request, 0);
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0,
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF, false);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockSetFlagsRequest mock_set_flags_request;
+ MockRemoveObjectMapRequest mock_remove_object_map_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ if (mock_image_ctx.journal != nullptr) {
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ }
+ expect_block_requests(mock_image_ctx);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_remove_object_map_request_send(mock_image_ctx,
+ mock_remove_object_map_request, -EINVAL);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0,
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF, false);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationDisableFeaturesRequest, Mirroring) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockRemoveJournalRequest mock_remove_journal_request;
+ MockDisableMirrorRequest mock_disable_mirror_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ expect_block_requests(mock_image_ctx);
+ expect_disable_mirror_request_send(mock_image_ctx,
+ mock_disable_mirror_request, 0);
+ expect_close_journal(mock_image_ctx, 0);
+ expect_remove_journal_request_send(mock_image_ctx,
+ mock_remove_journal_request, 0);
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationDisableFeaturesRequest, MirroringError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockRemoveJournalRequest mock_remove_journal_request;
+ MockDisableMirrorRequest mock_disable_mirror_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ expect_block_requests(mock_image_ctx);
+ expect_disable_mirror_request_send(mock_image_ctx,
+ mock_disable_mirror_request, -EINVAL);
+ expect_close_journal(mock_image_ctx, 0);
+ expect_remove_journal_request_send(mock_image_ctx,
+ mock_remove_journal_request, 0);
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_EnableFeaturesRequest.cc b/src/test/librbd/operation/test_mock_EnableFeaturesRequest.cc
new file mode 100644
index 000000000..b7bf7d178
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_EnableFeaturesRequest.cc
@@ -0,0 +1,644 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/Operations.h"
+#include "librbd/internal.h"
+#include "librbd/Journal.h"
+#include "librbd/image/SetFlagsRequest.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/mirror/EnableRequest.h"
+#include "librbd/journal/CreateRequest.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "librbd/object_map/CreateRequest.h"
+#include "librbd/operation/EnableFeaturesRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockOperationImageCtx : public MockImageCtx {
+ MockOperationImageCtx(librbd::ImageCtx& image_ctx)
+ : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+template<>
+struct Journal<MockOperationImageCtx> {
+ static void get_work_queue(CephContext*, MockContextWQ**) {
+ }
+};
+
+namespace image {
+
+template<>
+class SetFlagsRequest<MockOperationImageCtx> {
+public:
+ static SetFlagsRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static SetFlagsRequest *create(MockOperationImageCtx *image_ctx, uint64_t flags,
+ uint64_t mask, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SetFlagsRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SetFlagsRequest<MockOperationImageCtx> *SetFlagsRequest<MockOperationImageCtx>::s_instance;
+
+} // namespace image
+
+namespace journal {
+
+template<>
+class CreateRequest<MockOperationImageCtx> {
+public:
+ static CreateRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static CreateRequest *create(IoCtx &ioctx, const std::string &imageid,
+ uint8_t order, uint8_t splay_width,
+ const std::string &object_pool,
+ uint64_t tag_class, TagData &tag_data,
+ const std::string &client_id,
+ MockContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ CreateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+CreateRequest<MockOperationImageCtx> *CreateRequest<MockOperationImageCtx>::s_instance = nullptr;
+
+template <>
+struct TypeTraits<MockOperationImageCtx> {
+ typedef librbd::MockContextWQ ContextWQ;
+};
+
+} // namespace journal
+
+namespace mirror {
+
+template<>
+class EnableRequest<MockOperationImageCtx> {
+public:
+ static EnableRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static EnableRequest *create(MockOperationImageCtx *image_ctx,
+ cls::rbd::MirrorImageMode mirror_image_mode,
+ const std::string& non_primary_global_image_id,
+ bool image_clean, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ EnableRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+EnableRequest<MockOperationImageCtx> *EnableRequest<MockOperationImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+
+namespace object_map {
+
+template<>
+class CreateRequest<MockOperationImageCtx> {
+public:
+ static CreateRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static CreateRequest *create(MockOperationImageCtx *image_ctx, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ CreateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+CreateRequest<MockOperationImageCtx> *CreateRequest<MockOperationImageCtx>::s_instance = nullptr;
+
+} // namespace object_map
+
+template <>
+struct AsyncRequest<MockOperationImageCtx> : public AsyncRequest<MockImageCtx> {
+ MockOperationImageCtx &m_image_ctx;
+
+ AsyncRequest(MockOperationImageCtx &image_ctx, Context *on_finish)
+ : AsyncRequest<MockImageCtx>(image_ctx, on_finish), m_image_ctx(image_ctx) {
+ }
+};
+
+} // namespace librbd
+
+// template definitions
+#include "librbd/AsyncRequest.cc"
+#include "librbd/AsyncObjectThrottle.cc"
+#include "librbd/operation/Request.cc"
+#include "librbd/operation/EnableFeaturesRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::_;
+
+class TestMockOperationEnableFeaturesRequest : public TestMockFixture {
+public:
+ typedef librbd::image::SetFlagsRequest<MockOperationImageCtx> MockSetFlagsRequest;
+ typedef librbd::journal::CreateRequest<MockOperationImageCtx> MockCreateJournalRequest;
+ typedef librbd::mirror::EnableRequest<MockOperationImageCtx> MockEnableMirrorRequest;
+ typedef librbd::object_map::CreateRequest<MockOperationImageCtx> MockCreateObjectMapRequest;
+ typedef EnableFeaturesRequest<MockOperationImageCtx> MockEnableFeaturesRequest;
+
+ class MirrorModeEnabler {
+ public:
+ MirrorModeEnabler(librados::IoCtx &ioctx, cls::rbd::MirrorMode mirror_mode)
+ : m_ioctx(ioctx), m_mirror_mode(mirror_mode) {
+ EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx, "test-uuid"));
+ EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx, m_mirror_mode));
+ }
+
+ ~MirrorModeEnabler() {
+ EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
+ &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
+ }
+ private:
+ librados::IoCtx &m_ioctx;
+ cls::rbd::MirrorMode m_mirror_mode;
+ };
+
+ void ensure_features_disabled(librbd::ImageCtx *ictx,
+ uint64_t features_to_disable) {
+ uint64_t features;
+
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ features_to_disable &= features;
+ if (!features_to_disable) {
+ return;
+ }
+ ASSERT_EQ(0, ictx->operations->update_features(features_to_disable, false));
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0U, features & features_to_disable);
+ }
+
+ void expect_prepare_lock(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_))
+ .WillOnce(Invoke([](Context *on_ready) {
+ on_ready->complete(0);
+ }));
+ expect_op_work_queue(mock_image_ctx);
+ }
+
+ void expect_handle_prepare_lock_complete(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
+ }
+
+ void expect_block_writes(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, block_writes(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_unblock_writes(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, unblock_writes()).Times(1);
+ }
+
+ void expect_verify_lock_ownership(MockOperationImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillRepeatedly(Return(true));
+ }
+ }
+
+ void expect_block_requests(MockOperationImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, block_requests(0)).Times(1);
+ }
+ }
+
+ void expect_unblock_requests(MockOperationImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, unblock_requests()).Times(1);
+ }
+ }
+
+ void expect_set_flags_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockSetFlagsRequest &mock_set_flags_request, int r) {
+ EXPECT_CALL(mock_set_flags_request, send())
+ .WillOnce(FinishRequest(&mock_set_flags_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_create_journal_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockCreateJournalRequest &mock_create_journal_request, int r) {
+ EXPECT_CALL(mock_create_journal_request, send())
+ .WillOnce(FinishRequest(&mock_create_journal_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_enable_mirror_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockEnableMirrorRequest &mock_enable_mirror_request, int r) {
+ EXPECT_CALL(mock_enable_mirror_request, send())
+ .WillOnce(FinishRequest(&mock_enable_mirror_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_create_object_map_request_send(
+ MockOperationImageCtx &mock_image_ctx,
+ MockCreateObjectMapRequest &mock_create_object_map_request, int r) {
+ EXPECT_CALL(mock_create_object_map_request, send())
+ .WillOnce(FinishRequest(&mock_create_object_map_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_notify_update(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, notify_update(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+};
+
+TEST_F(TestMockOperationEnableFeaturesRequest, All) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ uint64_t features_to_enable = RBD_FEATURES_MUTABLE & features;
+
+ REQUIRE(features_to_enable);
+
+ ensure_features_disabled(ictx, features_to_enable);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+
+ MockSetFlagsRequest mock_set_flags_request;
+ MockCreateJournalRequest mock_create_journal_request;
+ MockCreateObjectMapRequest mock_create_object_map_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_block_requests(mock_image_ctx);
+ if (features_to_enable & RBD_FEATURE_JOURNALING) {
+ expect_create_journal_request_send(mock_image_ctx,
+ mock_create_journal_request, 0);
+ }
+ if (features_to_enable & (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF)) {
+ expect_set_flags_request_send(mock_image_ctx,
+ mock_set_flags_request, 0);
+ }
+ if (features_to_enable & RBD_FEATURE_OBJECT_MAP) {
+ expect_create_object_map_request_send(mock_image_ctx,
+ mock_create_object_map_request, 0);
+ }
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, features_to_enable);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationEnableFeaturesRequest, ObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ ensure_features_disabled(
+ ictx, RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockSetFlagsRequest mock_set_flags_request;
+ MockCreateObjectMapRequest mock_create_object_map_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ if (mock_image_ctx.journal != nullptr) {
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ }
+ expect_block_requests(mock_image_ctx);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_set_flags_request_send(mock_image_ctx,
+ mock_set_flags_request, 0);
+ expect_create_object_map_request_send(mock_image_ctx,
+ mock_create_object_map_request, 0);
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_OBJECT_MAP);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationEnableFeaturesRequest, ObjectMapError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ ensure_features_disabled(
+ ictx, RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockSetFlagsRequest mock_set_flags_request;
+ MockCreateObjectMapRequest mock_create_object_map_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ if (mock_image_ctx.journal != nullptr) {
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ }
+ expect_block_requests(mock_image_ctx);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_set_flags_request_send(mock_image_ctx,
+ mock_set_flags_request, 0);
+ expect_create_object_map_request_send(
+ mock_image_ctx, mock_create_object_map_request, -EINVAL);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_OBJECT_MAP);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationEnableFeaturesRequest, SetFlagsError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ ensure_features_disabled(
+ ictx, RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockSetFlagsRequest mock_set_flags_request;
+ MockCreateObjectMapRequest mock_create_object_map_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ if (mock_image_ctx.journal != nullptr) {
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ }
+ expect_block_requests(mock_image_ctx);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_set_flags_request_send(mock_image_ctx,
+ mock_set_flags_request, -EINVAL);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_OBJECT_MAP);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationEnableFeaturesRequest, Mirroring) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockCreateJournalRequest mock_create_journal_request;
+ MockEnableMirrorRequest mock_enable_mirror_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_block_requests(mock_image_ctx);
+ expect_create_journal_request_send(mock_image_ctx,
+ mock_create_journal_request, 0);
+ expect_enable_mirror_request_send(mock_image_ctx,
+ mock_enable_mirror_request, 0);
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationEnableFeaturesRequest, JournalingError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockCreateJournalRequest mock_create_journal_request;
+ MockEnableMirrorRequest mock_enable_mirror_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_block_requests(mock_image_ctx);
+ expect_create_journal_request_send(mock_image_ctx,
+ mock_create_journal_request, -EINVAL);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationEnableFeaturesRequest, MirroringError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockCreateJournalRequest mock_create_journal_request;
+ MockEnableMirrorRequest mock_enable_mirror_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_block_requests(mock_image_ctx);
+ expect_create_journal_request_send(mock_image_ctx,
+ mock_create_journal_request, 0);
+ expect_enable_mirror_request_send(mock_image_ctx,
+ mock_enable_mirror_request, -EINVAL);
+ expect_notify_update(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_Request.cc b/src/test/librbd/operation/test_mock_Request.cc
new file mode 100644
index 000000000..5c5e7a375
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_Request.cc
@@ -0,0 +1,175 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "librbd/AsyncRequest.h"
+#include "librbd/operation/Request.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+template <>
+struct AsyncRequest<librbd::MockTestImageCtx> {
+ librbd::MockTestImageCtx &m_image_ctx;
+ Context *m_on_finish;
+
+ AsyncRequest(librbd::MockTestImageCtx &image_ctx, Context *on_finish)
+ : m_image_ctx(image_ctx), m_on_finish(on_finish) {
+ }
+ virtual ~AsyncRequest() {
+ }
+
+ virtual void finish(int r) {
+ m_on_finish->complete(r);
+ }
+ virtual void finish_and_destroy(int r) {
+ finish(r);
+ delete this;
+ }
+};
+
+} // namespace librbd
+
+#include "librbd/operation/Request.cc"
+
+namespace librbd {
+namespace journal {
+
+std::ostream& operator<<(std::ostream& os, const Event&) {
+ return os;
+}
+
+} // namespace journal
+
+namespace operation {
+
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+
+struct MockRequest : public Request<librbd::MockTestImageCtx> {
+ MockRequest(librbd::MockTestImageCtx &image_ctx, Context *on_finish,
+ uint64_t journal_op_tid)
+ : Request<librbd::MockTestImageCtx>(image_ctx, on_finish, journal_op_tid) {
+ }
+
+ void complete(int r) {
+ finish_and_destroy(r);
+ }
+
+ void send_op_impl(int r) {
+ bool appending = append_op_event<
+ MockRequest, &MockRequest::handle_send>(this);
+ if (!appending) {
+ complete(r);
+ }
+ }
+ MOCK_METHOD1(should_complete, bool(int));
+ MOCK_METHOD0(send_op, void());
+ MOCK_METHOD1(handle_send, Context*(int*));
+ MOCK_CONST_METHOD0(can_affect_io, bool());
+ MOCK_CONST_METHOD1(create_event, journal::Event(uint64_t));
+};
+
+struct TestMockOperationRequest : public TestMockFixture {
+ void expect_can_affect_io(MockRequest &mock_request, bool can_affect) {
+ EXPECT_CALL(mock_request, can_affect_io())
+ .WillOnce(Return(can_affect));
+ }
+
+ void expect_is_journal_replaying(MockJournal &mock_journal, bool replaying) {
+ EXPECT_CALL(mock_journal, is_journal_replaying())
+ .WillOnce(Return(replaying));
+ }
+
+ void expect_is_journal_appending(MockJournal &mock_journal, bool appending) {
+ EXPECT_CALL(mock_journal, is_journal_appending())
+ .WillOnce(Return(appending));
+ }
+
+ void expect_send_op(MockRequest &mock_request, int r) {
+ EXPECT_CALL(mock_request, send_op())
+ .WillOnce(Invoke([&mock_request, r]() {
+ mock_request.complete(r);
+ }));
+ }
+
+ void expect_send_op_affects_io(MockImageCtx &mock_image_ctx,
+ MockRequest &mock_request, int r) {
+ EXPECT_CALL(mock_request, send_op())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_request, r]() {
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ new LambdaContext([&mock_request, r](int _) {
+ mock_request.send_op_impl(r);
+ }), 0);
+ }));
+ }
+
+};
+
+TEST_F(TestMockOperationRequest, SendJournalDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ C_SaferCond ctx;
+ MockRequest *mock_request = new MockRequest(mock_image_ctx, &ctx, 0);
+
+ InSequence seq;
+ expect_can_affect_io(*mock_request, false);
+ expect_is_journal_appending(mock_journal, false);
+ expect_send_op(*mock_request, 0);
+
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_request->send();
+ }
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockOperationRequest, SendAffectsIOJournalDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockJournal mock_journal;
+ mock_image_ctx.journal = &mock_journal;
+
+ C_SaferCond ctx;
+ MockRequest *mock_request = new MockRequest(mock_image_ctx, &ctx, 0);
+
+ InSequence seq;
+ expect_can_affect_io(*mock_request, true);
+ expect_send_op_affects_io(mock_image_ctx, *mock_request, 0);
+ expect_can_affect_io(*mock_request, true);
+ expect_is_journal_replaying(mock_journal, false);
+ expect_is_journal_appending(mock_journal, false);
+
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ mock_request->send();
+ }
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_ResizeRequest.cc b/src/test/librbd/operation/test_mock_ResizeRequest.cc
new file mode 100644
index 000000000..552ba5c97
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_ResizeRequest.cc
@@ -0,0 +1,435 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/operation/ResizeRequest.h"
+#include "librbd/operation/TrimRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+
+namespace util {
+
+inline ImageCtx* get_image_ctx(MockImageCtx* image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace operation {
+
+template <>
+class TrimRequest<MockImageCtx> {
+public:
+ static TrimRequest *s_instance;
+ static TrimRequest *create(MockImageCtx &image_ctx, Context *on_finish,
+ uint64_t original_size, uint64_t new_size,
+ ProgressContext &prog_ctx) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ TrimRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+TrimRequest<MockImageCtx> *TrimRequest<MockImageCtx>::s_instance = nullptr;
+
+} // namespace operation
+} // namespace librbd
+
+// template definitions
+#include "librbd/operation/ResizeRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockOperationResizeRequest : public TestMockFixture {
+public:
+ typedef ResizeRequest<MockImageCtx> MockResizeRequest;
+ typedef TrimRequest<MockImageCtx> MockTrimRequest;
+
+ void expect_block_writes(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, block_writes(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_unblock_writes(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, unblock_writes())
+ .Times(1);
+ }
+
+ void expect_is_lock_owner(MockImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillOnce(Return(true));
+ }
+ }
+
+ void expect_grow_object_map(MockImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.object_map != nullptr) {
+ expect_is_lock_owner(mock_image_ctx);
+ EXPECT_CALL(*mock_image_ctx.object_map, aio_resize(_, _, _))
+ .WillOnce(WithArg<2>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+ }
+
+ void expect_shrink_object_map(MockImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.object_map != nullptr) {
+ expect_is_lock_owner(mock_image_ctx);
+ EXPECT_CALL(*mock_image_ctx.object_map, aio_resize(_, _, _))
+ .WillOnce(WithArg<2>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+ }
+
+ void expect_update_header(MockImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.old_format) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ write(mock_image_ctx.header_oid, _, _, _, _))
+ .WillOnce(Return(r));
+ } else {
+ expect_is_lock_owner(mock_image_ctx);
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("set_size"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+ }
+
+ void expect_trim(MockImageCtx &mock_image_ctx,
+ MockTrimRequest &mock_trim_request, int r) {
+ EXPECT_CALL(mock_trim_request, send())
+ .WillOnce(FinishRequest(&mock_trim_request, r, &mock_image_ctx));
+ }
+
+ void expect_flush_cache(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, send(_))
+ .WillOnce(Invoke([&mock_image_ctx, r](io::ImageDispatchSpec* spec) {
+ ASSERT_TRUE(boost::get<io::ImageDispatchSpec::Flush>(
+ &spec->request) != nullptr);
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ auto aio_comp = spec->aio_comp;
+ auto ctx = new LambdaContext([aio_comp](int r) {
+ if (r < 0) {
+ aio_comp->fail(r);
+ } else {
+ aio_comp->set_request_count(1);
+ aio_comp->add_request();
+ aio_comp->complete_request(r);
+ }
+ });
+ mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_invalidate_cache(MockImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, invalidate_cache(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ expect_op_work_queue(mock_image_ctx);
+ }
+
+ void expect_resize_object_map(MockImageCtx &mock_image_ctx,
+ uint64_t new_size) {
+ EXPECT_CALL(*mock_image_ctx.object_map, aio_resize(new_size, _, _))
+ .WillOnce(WithArg<2>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ int when_resize(MockImageCtx &mock_image_ctx, uint64_t new_size,
+ bool allow_shrink, uint64_t journal_op_tid,
+ bool disable_journal) {
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockResizeRequest *req = new MockResizeRequest(
+ mock_image_ctx, &cond_ctx, new_size, allow_shrink, prog_ctx,
+ journal_op_tid, disable_journal);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ return cond_ctx.wait();
+ }
+};
+
+TEST_F(TestMockOperationResizeRequest, NoOpSuccess) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_unblock_writes(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, 0);
+ ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, GrowSuccess) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_grow_object_map(mock_image_ctx);
+ expect_update_header(mock_image_ctx, 0);
+ expect_unblock_writes(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, 0);
+ ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size * 2, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, ShrinkSuccess) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_unblock_writes(mock_image_ctx);
+
+ MockTrimRequest mock_trim_request;
+ expect_flush_cache(mock_image_ctx, 0);
+ expect_invalidate_cache(mock_image_ctx, 0);
+ expect_trim(mock_image_ctx, mock_trim_request, 0);
+ expect_block_writes(mock_image_ctx, 0);
+ expect_update_header(mock_image_ctx, 0);
+ expect_shrink_object_map(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, 0);
+ ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, ShrinkError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, false, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, PreBlockWritesError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, TrimError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_unblock_writes(mock_image_ctx);
+
+ MockTrimRequest mock_trim_request;
+ expect_flush_cache(mock_image_ctx, 0);
+ expect_invalidate_cache(mock_image_ctx, -EBUSY);
+ expect_trim(mock_image_ctx, mock_trim_request, -EINVAL);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, FlushCacheError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ REQUIRE(ictx->cache);
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_unblock_writes(mock_image_ctx);
+
+ MockTrimRequest mock_trim_request;
+ expect_flush_cache(mock_image_ctx, -EINVAL);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, InvalidateCacheError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ REQUIRE(ictx->cache);
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_unblock_writes(mock_image_ctx);
+
+ MockTrimRequest mock_trim_request;
+ expect_flush_cache(mock_image_ctx, 0);
+ expect_invalidate_cache(mock_image_ctx, -EINVAL);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, PostBlockWritesError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_unblock_writes(mock_image_ctx);
+
+ MockTrimRequest mock_trim_request;
+ expect_flush_cache(mock_image_ctx, 0);
+ expect_invalidate_cache(mock_image_ctx, 0);
+ expect_trim(mock_image_ctx, mock_trim_request, 0);
+ expect_block_writes(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0,
+ false));
+}
+
+TEST_F(TestMockOperationResizeRequest, UpdateHeaderError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, 0);
+ expect_grow_object_map(mock_image_ctx);
+ expect_update_header(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size * 2, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, JournalAppendError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_append_op_event(mock_image_ctx, true, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size, true, 0, false));
+}
+
+TEST_F(TestMockOperationResizeRequest, JournalDisabled) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx, 0);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size, true, 0, true));
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc b/src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc
new file mode 100644
index 000000000..218fc6b04
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc
@@ -0,0 +1,495 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
+#include "librbd/operation/SnapshotCreateRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+template<>
+class SetImageStateRequest<MockImageCtx> {
+public:
+ static SetImageStateRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static SetImageStateRequest *create(MockImageCtx *image_ctx, uint64_t snap_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SetImageStateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SetImageStateRequest<MockImageCtx> *SetImageStateRequest<MockImageCtx>::s_instance;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/operation/SnapshotCreateRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockOperationSnapshotCreateRequest : public TestMockFixture {
+public:
+ typedef SnapshotCreateRequest<MockImageCtx> MockSnapshotCreateRequest;
+ typedef mirror::snapshot::SetImageStateRequest<MockImageCtx> MockSetImageStateRequest;
+
+ void expect_notify_quiesce(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_quiesce(_, _, _))
+ .WillOnce(WithArg<2>(CompleteContext(
+ r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ void expect_block_writes(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, block_writes(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_verify_lock_ownership(MockImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillRepeatedly(Return(true));
+ }
+ }
+
+ void expect_allocate_snap_id(MockImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+ selfmanaged_snap_create(_));
+ if (r < 0 && r != -ESTALE) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.Times(r < 0 ? 2 : 1).WillRepeatedly(DoDefault());
+ }
+ }
+
+ void expect_release_snap_id(MockImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+ selfmanaged_snap_remove(_));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_snap_create(MockImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq(mock_image_ctx.old_format ? "snap_add" :
+ "snapshot_add"),
+ _, _, _, _));
+ if (r == -ESTALE) {
+ expect.WillOnce(Return(r)).WillOnce(DoDefault());
+ } else if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_object_map_snap_create(MockImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, snapshot_add(_, _))
+ .WillOnce(WithArg<1>(CompleteContext(
+ 0, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+ }
+
+ void expect_set_image_state(
+ MockImageCtx &mock_image_ctx,
+ MockSetImageStateRequest &mock_set_image_state_request, int r) {
+ EXPECT_CALL(mock_set_image_state_request, send())
+ .WillOnce(FinishRequest(&mock_set_image_state_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_update_snap_context(MockImageCtx &mock_image_ctx) {
+ // state machine checks to ensure a refresh hasn't already added the snap
+ EXPECT_CALL(mock_image_ctx, get_snap_info(_))
+ .WillOnce(Return(static_cast<const librbd::SnapInfo*>(NULL)));
+ EXPECT_CALL(mock_image_ctx, add_snap(_, "snap1", _, _, _, _, _, _));
+ }
+
+ void expect_unblock_writes(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, unblock_writes())
+ .Times(1);
+ }
+
+ void expect_notify_unquiesce(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_unquiesce(_, _))
+ .WillOnce(WithArg<1>(
+ CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+};
+
+TEST_F(TestMockOperationSnapshotCreateRequest, Success) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_notify_quiesce(mock_image_ctx, -EINVAL);
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, 0);
+ expect_snap_create(mock_image_ctx, 0);
+ expect_object_map_snap_create(mock_image_ctx);
+ expect_update_snap_context(mock_image_ctx);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_unblock_writes(mock_image_ctx);
+ expect_notify_unquiesce(mock_image_ctx, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, SNAP_CREATE_FLAG_IGNORE_NOTIFY_QUIESCE_ERROR, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, NotifyQuiesceError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_notify_quiesce(mock_image_ctx, -EINVAL);
+ expect_notify_unquiesce(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, 0, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, AllocateSnapIdError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_notify_quiesce(mock_image_ctx, 0);
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ expect_notify_unquiesce(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, 0, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, CreateSnapStale) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ expect_notify_quiesce(mock_image_ctx, 0);
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, -ESTALE);
+ expect_snap_create(mock_image_ctx, -ESTALE);
+ expect_object_map_snap_create(mock_image_ctx);
+ expect_update_snap_context(mock_image_ctx);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_unblock_writes(mock_image_ctx);
+ expect_notify_unquiesce(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, 0, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, CreateSnapError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ expect_notify_quiesce(mock_image_ctx, 0);
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, 0);
+ expect_snap_create(mock_image_ctx, -EINVAL);
+ expect_release_snap_id(mock_image_ctx, 0);
+ expect_unblock_writes(mock_image_ctx);
+ expect_notify_unquiesce(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, 0, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, ReleaseSnapIdError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ expect_notify_quiesce(mock_image_ctx, 0);
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, 0);
+ expect_snap_create(mock_image_ctx, -EINVAL);
+ expect_release_snap_id(mock_image_ctx, -ESTALE);
+ expect_unblock_writes(mock_image_ctx);
+ expect_notify_unquiesce(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, 0, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, SkipObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_notify_quiesce(mock_image_ctx, 0);
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, 0);
+ expect_snap_create(mock_image_ctx, 0);
+ expect_update_snap_context(mock_image_ctx);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_unblock_writes(mock_image_ctx);
+ expect_notify_unquiesce(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, SNAP_CREATE_FLAG_SKIP_OBJECT_MAP, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, SkipNotifyQuiesce) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, 0);
+ expect_snap_create(mock_image_ctx, 0);
+ expect_object_map_snap_create(mock_image_ctx);
+ expect_update_snap_context(mock_image_ctx);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_unblock_writes(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, SNAP_CREATE_FLAG_SKIP_NOTIFY_QUIESCE, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotCreateRequest, SetImageState) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_notify_quiesce(mock_image_ctx, 0);
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, 0);
+ expect_snap_create(mock_image_ctx, 0);
+ expect_object_map_snap_create(mock_image_ctx);
+ MockSetImageStateRequest mock_set_image_state_request;
+ expect_set_image_state(mock_image_ctx, mock_set_image_state_request, 0);
+ expect_update_snap_context(mock_image_ctx);
+ EXPECT_CALL(mock_image_ctx, rebuild_data_io_context());
+ expect_unblock_writes(mock_image_ctx);
+ expect_notify_unquiesce(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx,
+ cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {}, "", CEPH_NOSNAP},
+ "snap1", 0, 0, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_SnapshotProtectRequest.cc b/src/test/librbd/operation/test_mock_SnapshotProtectRequest.cc
new file mode 100644
index 000000000..aa8c1e78d
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_SnapshotProtectRequest.cc
@@ -0,0 +1,193 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/operation/SnapshotProtectRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+// template definitions
+#include "librbd/operation/SnapshotProtectRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockOperationSnapshotProtectRequest : public TestMockFixture {
+public:
+ typedef SnapshotProtectRequest<MockImageCtx> MockSnapshotProtectRequest;
+
+ void expect_get_snap_id(MockImageCtx &mock_image_ctx, uint64_t snap_id) {
+ EXPECT_CALL(mock_image_ctx, get_snap_id(_, _))
+ .WillOnce(Return(snap_id));
+ }
+
+ void expect_is_snap_protected(MockImageCtx &mock_image_ctx, bool is_protected,
+ int r) {
+ auto &expect = EXPECT_CALL(mock_image_ctx, is_snap_protected(_, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoAll(SetArgPointee<1>(is_protected), Return(0)));
+ }
+ }
+
+ void expect_set_protection_status(MockImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("set_protection_status"), _, _, _,
+ _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+};
+
+TEST_F(TestMockOperationSnapshotProtectRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, ictx->snap_info.rbegin()->first);
+ expect_is_snap_protected(mock_image_ctx, false, 0);
+ expect_set_protection_status(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotProtectRequest *req = new MockSnapshotProtectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotProtectRequest, GetSnapIdMissing) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, CEPH_NOSNAP);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotProtectRequest *req = new MockSnapshotProtectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotProtectRequest, IsSnapProtectedError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, ictx->snap_info.rbegin()->first);
+ expect_is_snap_protected(mock_image_ctx, false, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotProtectRequest *req = new MockSnapshotProtectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotProtectRequest, SnapAlreadyProtected) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, ictx->snap_info.rbegin()->first);
+ expect_is_snap_protected(mock_image_ctx, true, 0);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotProtectRequest *req = new MockSnapshotProtectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EBUSY, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotProtectRequest, SetProtectionStateError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, ictx->snap_info.rbegin()->first);
+ expect_is_snap_protected(mock_image_ctx, false, 0);
+ expect_set_protection_status(mock_image_ctx, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotProtectRequest *req = new MockSnapshotProtectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc b/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc
new file mode 100644
index 000000000..4469cb80d
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc
@@ -0,0 +1,971 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/Operations.h"
+#include "librbd/image/DetachChildRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
+#include "librbd/operation/SnapshotRemoveRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace image {
+
+template <>
+class DetachChildRequest<MockImageCtx> {
+public:
+ static DetachChildRequest *s_instance;
+ static DetachChildRequest *create(MockImageCtx &image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ DetachChildRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+DetachChildRequest<MockImageCtx> *DetachChildRequest<MockImageCtx>::s_instance;
+
+} // namespace image
+
+namespace mirror {
+namespace snapshot {
+
+template<>
+class RemoveImageStateRequest<MockImageCtx> {
+public:
+ static RemoveImageStateRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static RemoveImageStateRequest *create(MockImageCtx *image_ctx,
+ uint64_t snap_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ RemoveImageStateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+RemoveImageStateRequest<MockImageCtx> *RemoveImageStateRequest<MockImageCtx>::s_instance;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/operation/SnapshotRemoveRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockOperationSnapshotRemoveRequest : public TestMockFixture {
+public:
+ typedef SnapshotRemoveRequest<MockImageCtx> MockSnapshotRemoveRequest;
+ typedef image::DetachChildRequest<MockImageCtx> MockDetachChildRequest;
+ typedef mirror::snapshot::RemoveImageStateRequest<MockImageCtx> MockRemoveImageStateRequest;
+
+ int create_snapshot(const char *snap_name) {
+ librbd::ImageCtx *ictx;
+ int r = open_image(m_image_name, &ictx);
+ if (r < 0) {
+ return r;
+ }
+
+ r = snap_create(*ictx, snap_name);
+ if (r < 0) {
+ return r;
+ }
+
+ r = snap_protect(*ictx, snap_name);
+ if (r < 0) {
+ return r;
+ }
+ close_image(ictx);
+ return 0;
+ }
+
+ void expect_snapshot_trash_add(MockImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.old_format) {
+ return;
+ }
+
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("snapshot_trash_add"),
+ _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_snapshot_get(MockImageCtx &mock_image_ctx,
+ const cls::rbd::SnapshotInfo& snap_info, int r) {
+ if (mock_image_ctx.old_format) {
+ return;
+ }
+
+ using ceph::encode;
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("snapshot_get"), _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([snap_info, r](bufferlist* bl) {
+ encode(snap_info, *bl);
+ return r;
+ })));
+ }
+
+ void expect_children_list(MockImageCtx &mock_image_ctx,
+ const cls::rbd::ChildImageSpecs& child_images, int r) {
+ if (mock_image_ctx.old_format) {
+ return;
+ }
+
+ using ceph::encode;
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("children_list"), _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([child_images, r](bufferlist* bl) {
+ encode(child_images, *bl);
+ return r;
+ })));
+ }
+
+ void expect_detach_stale_child(MockImageCtx &mock_image_ctx, int r) {
+ auto& parent_spec = mock_image_ctx.parent_md.spec;
+
+ bufferlist bl;
+ encode(parent_spec.snap_id, bl);
+ encode(cls::rbd::ChildImageSpec{mock_image_ctx.md_ctx.get_id(), "",
+ mock_image_ctx.id}, bl);
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(util::header_name(parent_spec.image_id),
+ _, StrEq("rbd"), StrEq("child_detach"), ContentsEqual(bl),
+ _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_object_map_snap_remove(MockImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, snapshot_remove(_, _))
+ .WillOnce(WithArg<1>(CompleteContext(
+ r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+ }
+
+ void expect_remove_image_state(
+ MockImageCtx &mock_image_ctx,
+ MockRemoveImageStateRequest &mock_remove_image_state_request, int r) {
+ EXPECT_CALL(mock_remove_image_state_request, send())
+ .WillOnce(FinishRequest(&mock_remove_image_state_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_get_parent_spec(MockImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.old_format) {
+ return;
+ }
+
+ auto &expect = EXPECT_CALL(mock_image_ctx, get_parent_spec(_, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ auto &parent_spec = mock_image_ctx.snap_info.rbegin()->second.parent.spec;
+ expect.WillOnce(DoAll(SetArgPointee<1>(parent_spec),
+ Return(0)));
+ }
+ }
+
+ void expect_detach_child(MockImageCtx &mock_image_ctx,
+ MockDetachChildRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
+ }
+
+ void expect_snap_remove(MockImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq(mock_image_ctx.old_format ? "snap_remove" :
+ "snapshot_remove"),
+ _, _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_rm_snap(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, rm_snap(_, _, _)).Times(1);
+ }
+
+ void expect_release_snap_id(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+ selfmanaged_snap_remove(_))
+ .WillOnce(DoDefault());
+ }
+
+};
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, Success) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, 0);
+ expect_rm_snap(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, SuccessCloneParent) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 1}, 0);
+
+ const cls::rbd::ChildImageSpecs child_images;
+ expect_children_list(mock_image_ctx, child_images, 0);
+ expect_get_parent_spec(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, SuccessTrash) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id,
+ {cls::rbd::TrashSnapshotNamespace{
+ cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER, "snap1"}},
+ "snap1", 123, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, 0);
+ expect_rm_snap(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, FlattenedCloneRemovesChild) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_DEEP_FLATTEN))
+
+ ASSERT_EQ(0, create_snapshot("snap1"));
+
+ int order = 22;
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, flatten(*ictx, prog_ctx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+
+ MockDetachChildRequest mock_detach_child_request;
+ expect_detach_child(mock_image_ctx, mock_detach_child_request, -ENOENT);
+
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, 0);
+ expect_rm_snap(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, TrashCloneParent) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, ictx->operations->snap_create(
+ {cls::rbd::TrashSnapshotNamespace{}}, "snap1", 0, prog_ctx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::TrashSnapshotNamespace{}},
+ "snap1", 123, {}, 1}, 0);
+ const cls::rbd::ChildImageSpecs child_images;
+ expect_children_list(mock_image_ctx, child_images, 0);
+ expect_get_parent_spec(mock_image_ctx, 0);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::TrashSnapshotNamespace{}, "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EBUSY, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, MirrorSnapshot) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "mirror uuid", 123};
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {ns}, "mirror", 456, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+ MockRemoveImageStateRequest mock_remove_image_state_request;
+ expect_remove_image_state(mock_image_ctx, mock_remove_image_state_request, 0);
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, 0);
+ expect_rm_snap(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, ns, "mirror", snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, MirrorSnapshotOrphan) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ cls::rbd::MirrorSnapshotNamespace ns{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "", CEPH_NOSNAP};
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {ns}, "mirror", 456, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+ MockRemoveImageStateRequest mock_remove_image_state_request;
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, 0);
+ expect_rm_snap(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, ns, "mirror", snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotTrashAddNotSupported) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, -EOPNOTSUPP);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_get_parent_spec(mock_image_ctx, 0);
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, 0);
+ expect_rm_snap(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotTrashAddError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_trash_add(mock_image_ctx, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotGetError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, -EOPNOTSUPP);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EOPNOTSUPP, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, ObjectMapSnapRemoveError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockObjectMap mock_object_map;
+ mock_image_ctx.object_map = &mock_object_map;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+
+ expect_object_map_snap_remove(mock_image_ctx, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveChildParentError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, -ENOENT);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveChildError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snapshot("snap1"));
+
+ int order = 22;
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ if (ictx->test_features(RBD_FEATURE_DEEP_FLATTEN)) {
+ GTEST_SKIP() << "Skipping due to enabled deep-flatten";
+ }
+
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, flatten(*ictx, prog_ctx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_get_parent_spec(mock_image_ctx, 0);
+
+ MockDetachChildRequest mock_detach_child_request;
+ expect_detach_child(mock_image_ctx, mock_detach_child_request, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveSnapError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, -ENOENT);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, MissingSnap) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ uint64_t snap_id = 456;
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, ListChildrenError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 1}, 0);
+ const cls::rbd::ChildImageSpecs child_images;
+ expect_children_list(mock_image_ctx, child_images, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotRemoveRequest, DetachStaleChildError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snapshot("snap1"));
+
+ int order = 22;
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::UserSnapshotNamespace{}},
+ "snap1", 123, {}, 1}, 0);
+ const cls::rbd::ChildImageSpecs child_images;
+ expect_children_list(mock_image_ctx, child_images, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
+ snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_SnapshotRollbackRequest.cc b/src/test/librbd/operation/test_mock_SnapshotRollbackRequest.cc
new file mode 100644
index 000000000..65eac7a6d
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_SnapshotRollbackRequest.cc
@@ -0,0 +1,367 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "include/stringify.h"
+#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/operation/SnapshotRollbackRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockOperationImageCtx : public MockImageCtx {
+ MockOperationImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace operation {
+
+template <>
+struct ResizeRequest<MockOperationImageCtx> {
+ static ResizeRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static ResizeRequest* create(MockOperationImageCtx &image_ctx, Context *on_finish,
+ uint64_t new_size, bool allow_shrink,
+ ProgressContext &prog_ctx, uint64_t journal_op_tid,
+ bool disable_journal) {
+ ceph_assert(s_instance != nullptr);
+ ceph_assert(journal_op_tid == 0);
+ ceph_assert(disable_journal);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ResizeRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+ResizeRequest<MockOperationImageCtx> *ResizeRequest<MockOperationImageCtx>::s_instance = nullptr;
+
+} // namespace operation
+
+template <>
+struct AsyncRequest<MockOperationImageCtx> : public AsyncRequest<MockImageCtx> {
+ MockOperationImageCtx &m_image_ctx;
+
+ AsyncRequest(MockOperationImageCtx &image_ctx, Context *on_finish)
+ : AsyncRequest<MockImageCtx>(image_ctx, on_finish), m_image_ctx(image_ctx) {
+ }
+};
+
+} // namespace librbd
+
+// template definitions
+#include "librbd/AsyncRequest.cc"
+#include "librbd/AsyncObjectThrottle.cc"
+#include "librbd/operation/Request.cc"
+#include "librbd/operation/SnapshotRollbackRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockOperationSnapshotRollbackRequest : public TestMockFixture {
+public:
+ typedef SnapshotRollbackRequest<MockOperationImageCtx> MockSnapshotRollbackRequest;
+ typedef ResizeRequest<MockOperationImageCtx> MockResizeRequest;
+
+ void expect_block_writes(MockOperationImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, block_writes(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_unblock_writes(MockOperationImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, unblock_writes())
+ .Times(1);
+ }
+
+ void expect_get_image_size(MockOperationImageCtx &mock_image_ctx,
+ uint64_t size) {
+ EXPECT_CALL(mock_image_ctx, get_image_size(CEPH_NOSNAP))
+ .WillOnce(Return(size));
+ }
+
+ void expect_resize(MockOperationImageCtx &mock_image_ctx,
+ MockResizeRequest &mock_resize_request, int r) {
+ expect_get_image_size(mock_image_ctx, 123);
+ EXPECT_CALL(mock_resize_request, send())
+ .WillOnce(FinishRequest(&mock_resize_request, r,
+ &mock_image_ctx));
+ }
+
+ void expect_get_flags(MockOperationImageCtx &mock_image_ctx,
+ uint64_t snap_id, int r) {
+ EXPECT_CALL(mock_image_ctx, get_flags(snap_id, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_object_may_exist(MockOperationImageCtx &mock_image_ctx,
+ uint64_t object_no, bool exists) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, object_may_exist(object_no))
+ .WillOnce(Return(exists));
+ }
+ }
+
+ void expect_get_snap_object_map(MockOperationImageCtx &mock_image_ctx,
+ MockObjectMap *mock_object_map, uint64_t snap_id) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(mock_image_ctx, create_object_map(snap_id))
+ .WillOnce(Return(mock_object_map));
+ EXPECT_CALL(*mock_object_map, open(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+ }
+
+ void expect_rollback_object_map(MockOperationImageCtx &mock_image_ctx,
+ MockObjectMap &mock_object_map) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(mock_object_map, rollback(_, _))
+ .WillOnce(WithArg<1>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+ }
+
+ void expect_get_object_name(MockOperationImageCtx &mock_image_ctx,
+ uint64_t object_num) {
+ EXPECT_CALL(mock_image_ctx, get_object_name(object_num))
+ .WillOnce(Return("object-name-" + stringify(object_num)));
+ }
+
+ void expect_get_current_size(MockOperationImageCtx &mock_image_ctx, uint64_t size) {
+ EXPECT_CALL(mock_image_ctx, get_current_size())
+ .WillOnce(Return(size));
+ }
+
+ void expect_rollback_snap_id(MockOperationImageCtx &mock_image_ctx,
+ const std::string &oid, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+ selfmanaged_snap_rollback(oid, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_rollback(MockOperationImageCtx &mock_image_ctx, int r) {
+ expect_get_current_size(mock_image_ctx, 1);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_object_name(mock_image_ctx, 0);
+ expect_rollback_snap_id(mock_image_ctx, "object-name-0", r);
+ }
+
+ void expect_create_object_map(MockOperationImageCtx &mock_image_ctx,
+ MockObjectMap *mock_object_map) {
+ EXPECT_CALL(mock_image_ctx, create_object_map(_))
+ .WillOnce(Return(mock_object_map));
+ }
+
+ void expect_open_object_map(MockOperationImageCtx &mock_image_ctx,
+ MockObjectMap &mock_object_map) {
+ EXPECT_CALL(mock_object_map, open(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_refresh_object_map(MockOperationImageCtx &mock_image_ctx,
+ MockObjectMap &mock_object_map) {
+ if (mock_image_ctx.object_map != nullptr) {
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map);
+ }
+ }
+
+ void expect_invalidate_cache(MockOperationImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, invalidate_cache(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ int when_snap_rollback(MockOperationImageCtx &mock_image_ctx,
+ const std::string &snap_name,
+ uint64_t snap_id, uint64_t snap_size) {
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ MockSnapshotRollbackRequest *req = new MockSnapshotRollbackRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), snap_name,
+ snap_id, snap_size, prog_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ return cond_ctx.wait();
+ }
+};
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, Success) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ MockObjectMap mock_snap_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ MockResizeRequest mock_resize_request;
+ expect_append_op_event(mock_image_ctx, false, 0);
+ expect_block_writes(mock_image_ctx, 0);
+ expect_resize(mock_image_ctx, mock_resize_request, 0);
+ expect_get_flags(mock_image_ctx, 123, 0);
+ expect_get_snap_object_map(mock_image_ctx, &mock_snap_object_map, 123);
+ expect_rollback_object_map(mock_image_ctx, mock_object_map);
+ expect_rollback(mock_image_ctx, 0);
+ expect_refresh_object_map(mock_image_ctx, mock_object_map);
+ expect_invalidate_cache(mock_image_ctx, 0);
+ expect_commit_op_event(mock_image_ctx, 0);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(0, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, BlockWritesError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_append_op_event(mock_image_ctx, false, 0);
+ expect_block_writes(mock_image_ctx, -EINVAL);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, SkipResize) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ MockObjectMap mock_snap_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_append_op_event(mock_image_ctx, false, 0);
+ expect_block_writes(mock_image_ctx, 0);
+ expect_get_image_size(mock_image_ctx, 345);
+ expect_get_flags(mock_image_ctx, 123, 0);
+ expect_get_snap_object_map(mock_image_ctx, &mock_snap_object_map, 123);
+ expect_rollback_object_map(mock_image_ctx, mock_object_map);
+ expect_rollback(mock_image_ctx, 0);
+ expect_refresh_object_map(mock_image_ctx, mock_object_map);
+ expect_invalidate_cache(mock_image_ctx, 0);
+ expect_commit_op_event(mock_image_ctx, 0);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(0, when_snap_rollback(mock_image_ctx, "snap", 123, 345));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, ResizeError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ MockResizeRequest mock_resize_request;
+ expect_append_op_event(mock_image_ctx, false, 0);
+ expect_block_writes(mock_image_ctx, 0);
+ expect_resize(mock_image_ctx, mock_resize_request, -EINVAL);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, RollbackObjectsError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ MockObjectMap mock_snap_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ MockResizeRequest mock_resize_request;
+ expect_append_op_event(mock_image_ctx, false, 0);
+ expect_block_writes(mock_image_ctx, 0);
+ expect_resize(mock_image_ctx, mock_resize_request, 0);
+ expect_get_flags(mock_image_ctx, 123, 0);
+ expect_get_snap_object_map(mock_image_ctx, &mock_snap_object_map, 123);
+ expect_rollback_object_map(mock_image_ctx, mock_object_map);
+ expect_rollback(mock_image_ctx, -EINVAL);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, InvalidateCacheError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ REQUIRE(ictx->cache);
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ MockObjectMap mock_snap_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ MockResizeRequest mock_resize_request;
+ expect_append_op_event(mock_image_ctx, false, 0);
+ expect_block_writes(mock_image_ctx, 0);
+ expect_resize(mock_image_ctx, mock_resize_request, 0);
+ expect_get_flags(mock_image_ctx, 123, 0);
+ expect_get_snap_object_map(mock_image_ctx, &mock_snap_object_map, 123);
+ expect_rollback_object_map(mock_image_ctx, mock_object_map);
+ expect_rollback(mock_image_ctx, 0);
+ expect_refresh_object_map(mock_image_ctx, mock_object_map);
+ expect_invalidate_cache(mock_image_ctx, -EINVAL);
+ expect_commit_op_event(mock_image_ctx, -EINVAL);
+ expect_unblock_writes(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_SnapshotUnprotectRequest.cc b/src/test/librbd/operation/test_mock_SnapshotUnprotectRequest.cc
new file mode 100644
index 000000000..26b1be206
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_SnapshotUnprotectRequest.cc
@@ -0,0 +1,277 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "include/rados/librados.hpp"
+#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/operation/SnapshotUnprotectRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+// template definitions
+#include "librbd/operation/SnapshotUnprotectRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::SetArgReferee;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockOperationSnapshotUnprotectRequest : public TestMockFixture {
+public:
+ typedef SnapshotUnprotectRequest<MockImageCtx> MockSnapshotUnprotectRequest;
+
+ void expect_get_snap_id(MockImageCtx &mock_image_ctx, uint64_t snap_id) {
+ EXPECT_CALL(mock_image_ctx, get_snap_id(_, _))
+ .WillOnce(Return(snap_id));
+ }
+
+ void expect_is_snap_unprotected(MockImageCtx &mock_image_ctx,
+ bool is_unprotected, int r) {
+ auto &expect = EXPECT_CALL(mock_image_ctx, is_snap_unprotected(_, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoAll(SetArgPointee<1>(is_unprotected), Return(0)));
+ }
+ }
+
+ void expect_set_protection_status(MockImageCtx &mock_image_ctx,
+ uint64_t snap_id, uint8_t status, int r) {
+ bufferlist bl;
+ encode(snap_id, bl);
+ encode(status, bl);
+
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("set_protection_status"),
+ ContentsEqual(bl), _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ size_t expect_create_pool_io_contexts(MockImageCtx &mock_image_ctx) {
+ librados::MockTestMemIoCtxImpl &io_ctx_impl =
+ get_mock_io_ctx(mock_image_ctx.md_ctx);
+ librados::MockTestMemRadosClient *rados_client =
+ io_ctx_impl.get_mock_rados_client();
+
+ std::list<std::pair<int64_t, std::string> > pools;
+ int r = rados_client->pool_list(pools);
+ if (r < 0) {
+ ADD_FAILURE() << "failed to list pools";
+ return 0;
+ }
+
+ EXPECT_CALL(*rados_client, create_ioctx(_, _))
+ .Times(pools.size()).WillRepeatedly(DoAll(
+ GetReference(&io_ctx_impl), Return(&io_ctx_impl)));
+ return pools.size();
+ }
+
+ void expect_get_children(MockImageCtx &mock_image_ctx, size_t pools, int r) {
+ bufferlist bl;
+ std::set<std::string> children;
+ encode(children, bl);
+
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(RBD_CHILDREN, _, StrEq("rbd"),
+ StrEq("get_children"), _, _, _, _));
+ if (r < 0) {
+ expect.WillRepeatedly(Return(r));
+ } else {
+ expect.Times(pools).WillRepeatedly(DoAll(
+ SetArgPointee<5>(bl), Return(0)));
+ }
+ }
+};
+
+TEST_F(TestMockOperationSnapshotUnprotectRequest, Success) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_get_snap_id(mock_image_ctx, snap_id);
+ expect_is_snap_unprotected(mock_image_ctx, false, 0);
+ expect_set_protection_status(mock_image_ctx, snap_id,
+ RBD_PROTECTION_STATUS_UNPROTECTING, 0);
+ size_t pools = expect_create_pool_io_contexts(mock_image_ctx);
+ expect_get_children(mock_image_ctx, pools, -ENOENT);
+ expect_set_protection_status(mock_image_ctx, snap_id,
+ RBD_PROTECTION_STATUS_UNPROTECTED, 0);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotUnprotectRequest *req = new MockSnapshotUnprotectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotUnprotectRequest, GetSnapIdMissing) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, CEPH_NOSNAP);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotUnprotectRequest *req = new MockSnapshotUnprotectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-ENOENT, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotUnprotectRequest, IsSnapUnprotectedError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, ictx->snap_info.rbegin()->first);
+ expect_is_snap_unprotected(mock_image_ctx, false, -EBADMSG);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotUnprotectRequest *req = new MockSnapshotUnprotectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EBADMSG, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotUnprotectRequest, SnapAlreadyUnprotected) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_get_snap_id(mock_image_ctx, ictx->snap_info.rbegin()->first);
+ expect_is_snap_unprotected(mock_image_ctx, true, 0);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotUnprotectRequest *req = new MockSnapshotUnprotectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotUnprotectRequest, SetProtectionStatusError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_get_snap_id(mock_image_ctx, snap_id);
+ expect_is_snap_unprotected(mock_image_ctx, false, 0);
+ expect_set_protection_status(mock_image_ctx, snap_id,
+ RBD_PROTECTION_STATUS_UNPROTECTING, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotUnprotectRequest *req = new MockSnapshotUnprotectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationSnapshotUnprotectRequest, ChildrenExist) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_get_snap_id(mock_image_ctx, snap_id);
+ expect_is_snap_unprotected(mock_image_ctx, false, 0);
+ expect_set_protection_status(mock_image_ctx, snap_id,
+ RBD_PROTECTION_STATUS_UNPROTECTING, 0);
+ size_t pools = expect_create_pool_io_contexts(mock_image_ctx);
+ expect_get_children(mock_image_ctx, pools, 0);
+ expect_set_protection_status(mock_image_ctx, snap_id,
+ RBD_PROTECTION_STATUS_PROTECTED, 0);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotUnprotectRequest *req = new MockSnapshotUnprotectRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1");
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EBUSY, cond_ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_TrimRequest.cc b/src/test/librbd/operation/test_mock_TrimRequest.cc
new file mode 100644
index 000000000..1771e7413
--- /dev/null
+++ b/src/test/librbd/operation/test_mock_TrimRequest.cc
@@ -0,0 +1,493 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "common/bit_vector.hpp"
+#include "librbd/AsyncRequest.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/Utils.h"
+#include "librbd/operation/TrimRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <boost/variant.hpp>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+template<>
+struct AsyncRequest<librbd::MockTestImageCtx> {
+ librbd::MockTestImageCtx& m_image_ctx;
+ Context *on_finish;
+
+ AsyncRequest(librbd::MockTestImageCtx& image_ctx, Context* on_finish)
+ : m_image_ctx(image_ctx), on_finish(on_finish) {
+ }
+ virtual ~AsyncRequest() {
+ }
+
+ Context* create_callback_context() {
+ return util::create_context_callback(this);
+ }
+
+ Context* create_async_callback_context() {
+ return util::create_context_callback<AsyncRequest,
+ &AsyncRequest::async_complete>(this);
+ }
+
+ void complete(int r) {
+ if (should_complete(r)) {
+ async_complete(r);
+ }
+ }
+
+ void async_complete(int r) {
+ on_finish->complete(r);
+ delete this;
+ }
+
+ bool is_canceled() const {
+ return false;
+ }
+
+ virtual void send() = 0;
+ virtual bool should_complete(int r) = 0;
+};
+
+namespace io {
+
+struct DiscardVisitor
+ : public boost::static_visitor<ObjectDispatchSpec::DiscardRequest*> {
+ ObjectDispatchSpec::DiscardRequest*
+ operator()(ObjectDispatchSpec::DiscardRequest& discard) const {
+ return &discard;
+ }
+
+ template <typename T>
+ ObjectDispatchSpec::DiscardRequest*
+ operator()(T& t) const {
+ return nullptr;
+ }
+};
+
+} // namespace io
+} // namespace librbd
+
+// template definitions
+#include "librbd/AsyncObjectThrottle.cc"
+#include "librbd/operation/TrimRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockOperationTrimRequest : public TestMockFixture {
+public:
+ typedef TrimRequest<MockTestImageCtx> MockTrimRequest;
+
+ int create_snapshot(const char *snap_name) {
+ librbd::ImageCtx *ictx;
+ int r = open_image(m_image_name, &ictx);
+ if (r < 0) {
+ return r;
+ }
+
+ r = snap_create(*ictx, snap_name);
+ if (r < 0) {
+ return r;
+ }
+
+ r = snap_protect(*ictx, snap_name);
+ if (r < 0) {
+ return r;
+ }
+ close_image(ictx);
+ return 0;
+ }
+
+ void expect_is_lock_owner(MockTestImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillRepeatedly(Return(true));
+ }
+ }
+
+ void expect_object_map_update(MockTestImageCtx &mock_image_ctx,
+ uint64_t start_object, uint64_t end_object,
+ uint8_t state, uint8_t current_state,
+ bool updated, int ret_val) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map,
+ aio_update(CEPH_NOSNAP, start_object, end_object, state,
+ boost::optional<uint8_t>(current_state), _, false, _))
+ .WillOnce(WithArg<7>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
+ if (updated) {
+ mock_image_ctx.op_work_queue->queue(ctx, ret_val);
+ }
+ return updated;
+ })));
+ }
+ }
+
+ void expect_get_parent_overlap(MockTestImageCtx &mock_image_ctx,
+ uint64_t overlap) {
+ EXPECT_CALL(mock_image_ctx, get_parent_overlap(CEPH_NOSNAP, _))
+ .WillOnce(WithArg<1>(Invoke([overlap](uint64_t *o) {
+ *o = overlap;
+ return 0;
+ })));
+ }
+
+ void expect_reduce_parent_overlap(MockTestImageCtx& mock_image_ctx,
+ uint64_t overlap) {
+ EXPECT_CALL(mock_image_ctx, reduce_parent_overlap(overlap, false))
+ .WillOnce(Return(std::make_pair(overlap, io::ImageArea::DATA)));
+ }
+
+ void expect_get_area_size(MockTestImageCtx& mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, get_area_size(io::ImageArea::CRYPTO_HEADER))
+ .WillOnce(Return(0));
+ }
+
+ void expect_object_may_exist(MockTestImageCtx &mock_image_ctx,
+ uint64_t object_no, bool exists) {
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, object_may_exist(object_no))
+ .WillOnce(Return(exists));
+ }
+ }
+
+ void expect_get_object_name(MockTestImageCtx &mock_image_ctx,
+ uint64_t object_no, const std::string& oid) {
+ EXPECT_CALL(mock_image_ctx, get_object_name(object_no))
+ .WillOnce(Return(oid));
+ }
+
+ void expect_aio_remove(MockTestImageCtx &mock_image_ctx,
+ const std::string& oid, int ret_val) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx), remove(oid, _))
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_object_discard(MockImageCtx &mock_image_ctx,
+ io::MockObjectDispatch& mock_io_object_dispatch,
+ uint64_t offset, uint64_t length,
+ bool update_object_map, int r) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_))
+ .WillOnce(Invoke([&mock_image_ctx, offset, length, update_object_map, r]
+ (io::ObjectDispatchSpec* spec) {
+ auto discard = boost::apply_visitor(io::DiscardVisitor(), spec->request);
+ ASSERT_TRUE(discard != nullptr);
+ ASSERT_EQ(offset, discard->object_off);
+ ASSERT_EQ(length, discard->object_len);
+ int flags = 0;
+ if (!update_object_map) {
+ flags = io::OBJECT_DISCARD_FLAG_DISABLE_OBJECT_MAP_UPDATE;
+ }
+ ASSERT_EQ(flags, discard->discard_flags);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ mock_image_ctx.op_work_queue->queue(&spec->dispatcher_ctx, r);
+ }));
+ }
+};
+
+TEST_F(TestMockOperationTrimRequest, SuccessRemove) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+ EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
+ EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
+
+ // pre
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_PENDING, OBJECT_EXISTS,
+ true, 0);
+
+ // copy-up
+ expect_get_area_size(mock_image_ctx);
+ expect_get_parent_overlap(mock_image_ctx, 0);
+ expect_reduce_parent_overlap(mock_image_ctx, 0);
+
+ // remove
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_object_name(mock_image_ctx, 0, "object0");
+ expect_aio_remove(mock_image_ctx, "object0", 0);
+
+ // post
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_NONEXISTENT,
+ OBJECT_PENDING, true, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext progress_ctx;
+ MockTrimRequest *req = new MockTrimRequest(
+ mock_image_ctx, &cond_ctx, m_image_size, 0, progress_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationTrimRequest, SuccessCopyUp) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING)
+ ASSERT_EQ(0, create_snapshot("snap1"));
+
+ int order = 22;
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+ EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
+ EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
+
+ // pre
+ expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_PENDING, OBJECT_EXISTS,
+ true, 0);
+
+ // copy-up
+ io::MockObjectDispatch mock_io_object_dispatch;
+ expect_get_area_size(mock_image_ctx);
+ expect_get_parent_overlap(mock_image_ctx, ictx->get_object_size());
+ expect_reduce_parent_overlap(mock_image_ctx, ictx->get_object_size());
+ expect_get_object_name(mock_image_ctx, 0, "object0");
+ expect_object_discard(mock_image_ctx, mock_io_object_dispatch, 0,
+ ictx->get_object_size(), false, 0);
+
+ // remove
+ expect_object_may_exist(mock_image_ctx, 1, true);
+ expect_get_object_name(mock_image_ctx, 1, "object1");
+ expect_aio_remove(mock_image_ctx, "object1", 0);
+
+ // post
+ expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_NONEXISTENT,
+ OBJECT_PENDING, true, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext progress_ctx;
+ MockTrimRequest *req = new MockTrimRequest(
+ mock_image_ctx, &cond_ctx, 2 * ictx->get_object_size(), 0, progress_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationTrimRequest, SuccessBoundary) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+ EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
+ EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
+
+ // boundary
+ io::MockObjectDispatch mock_io_object_dispatch;
+ expect_object_discard(mock_image_ctx, mock_io_object_dispatch, 1,
+ ictx->get_object_size() - 1, true, 0);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext progress_ctx;
+ MockTrimRequest *req = new MockTrimRequest(
+ mock_image_ctx, &cond_ctx, ictx->get_object_size(), 1, progress_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationTrimRequest, SuccessNoOp) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+}
+
+TEST_F(TestMockOperationTrimRequest, RemoveError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+ EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
+ EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
+
+ // pre
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_PENDING, OBJECT_EXISTS,
+ false, 0);
+
+ // copy-up
+ expect_get_area_size(mock_image_ctx);
+ expect_get_parent_overlap(mock_image_ctx, 0);
+ expect_reduce_parent_overlap(mock_image_ctx, 0);
+
+ // remove
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_get_object_name(mock_image_ctx, 0, "object0");
+ expect_aio_remove(mock_image_ctx, "object0", -EPERM);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext progress_ctx;
+ MockTrimRequest *req = new MockTrimRequest(
+ mock_image_ctx, &cond_ctx, m_image_size, 0, progress_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EPERM, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationTrimRequest, CopyUpError) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING)
+ ASSERT_EQ(0, create_snapshot("snap1"));
+
+ int order = 22;
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+ EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
+ EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
+
+ // pre
+ expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_PENDING, OBJECT_EXISTS,
+ false, 0);
+
+ // copy-up
+ io::MockObjectDispatch mock_io_object_dispatch;
+ expect_get_area_size(mock_image_ctx);
+ expect_get_parent_overlap(mock_image_ctx, ictx->get_object_size());
+ expect_reduce_parent_overlap(mock_image_ctx, ictx->get_object_size());
+ expect_get_object_name(mock_image_ctx, 0, "object0");
+ expect_object_discard(mock_image_ctx, mock_io_object_dispatch, 0,
+ ictx->get_object_size(), false, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext progress_ctx;
+ MockTrimRequest *req = new MockTrimRequest(
+ mock_image_ctx, &cond_ctx, 2 * ictx->get_object_size(), 0, progress_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+TEST_F(TestMockOperationTrimRequest, BoundaryError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+ EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
+ EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
+
+ // boundary
+ io::MockObjectDispatch mock_io_object_dispatch;
+ expect_object_discard(mock_image_ctx, mock_io_object_dispatch, 1,
+ ictx->get_object_size() - 1, true, -EINVAL);
+
+ C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext progress_ctx;
+ MockTrimRequest *req = new MockTrimRequest(
+ mock_image_ctx, &cond_ctx, ictx->get_object_size(), 1, progress_ctx);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
+} // namespace operation
+} // namespace librbd
diff --git a/src/test/librbd/rbdrw.py b/src/test/librbd/rbdrw.py
new file mode 100644
index 000000000..a4b36d3cb
--- /dev/null
+++ b/src/test/librbd/rbdrw.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+"""
+Loop writing/reading the first 4k of image argv[1] in pool rbd,
+after acquiring exclusive lock named argv[2]. When an exception
+happens, split off the last number in the exception 'args' string
+and use it as the process exit code, if it's convertible to a number.
+
+Designed to run against a blocklist operation and verify the
+ESHUTDOWN expected from the image operation.
+
+Note: this cannot be run with writeback caching on, currently, as
+writeback errors cause reads be marked dirty rather than error, and
+even if they were marked as errored, ObjectCacher would retry them
+rather than note them as errored.
+"""
+
+import rados, rbd, sys
+
+with rados.Rados(conffile='') as r:
+ with r.open_ioctx('rbd') as ioctx:
+ try:
+ with rbd.Image(ioctx, sys.argv[1]) as image:
+ image.lock_exclusive(sys.argv[2])
+ while True:
+ image.write(b'A' * 4096, 0)
+ r = image.read(0, 4096)
+ except rbd.ConnectionShutdown:
+ # it so happens that the errno here is 108, but
+ # anything recognizable would do
+ exit(108)
diff --git a/src/test/librbd/test_BlockGuard.cc b/src/test/librbd/test_BlockGuard.cc
new file mode 100644
index 000000000..e41e58825
--- /dev/null
+++ b/src/test/librbd/test_BlockGuard.cc
@@ -0,0 +1,98 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/BlockGuard.h"
+
+namespace librbd {
+
+class TestIOBlockGuard : public TestFixture {
+public:
+ static uint32_t s_index;
+
+ struct Operation {
+ uint32_t index;
+ Operation() : index(++s_index) {
+ }
+ Operation(Operation &&rhs) : index(rhs.index) {
+ }
+ Operation(const Operation &) = delete;
+
+ Operation& operator=(Operation &&rhs) {
+ index = rhs.index;
+ return *this;
+ }
+
+ bool operator==(const Operation &rhs) const {
+ return index == rhs.index;
+ }
+ };
+
+ typedef std::list<Operation> Operations;
+
+ typedef BlockGuard<Operation> OpBlockGuard;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ m_cct = reinterpret_cast<CephContext*>(m_ioctx.cct());
+ }
+
+ CephContext *m_cct;
+};
+
+TEST_F(TestIOBlockGuard, NonDetainedOps) {
+ OpBlockGuard op_block_guard(m_cct);
+
+ Operation op1;
+ BlockGuardCell *cell1;
+ ASSERT_EQ(0, op_block_guard.detain({1, 3}, &op1, &cell1));
+
+ Operation op2;
+ BlockGuardCell *cell2;
+ ASSERT_EQ(0, op_block_guard.detain({0, 1}, &op2, &cell2));
+
+ Operation op3;
+ BlockGuardCell *cell3;
+ ASSERT_EQ(0, op_block_guard.detain({3, 6}, &op3, &cell3));
+
+ Operations released_ops;
+ op_block_guard.release(cell1, &released_ops);
+ ASSERT_TRUE(released_ops.empty());
+
+ op_block_guard.release(cell2, &released_ops);
+ ASSERT_TRUE(released_ops.empty());
+
+ op_block_guard.release(cell3, &released_ops);
+ ASSERT_TRUE(released_ops.empty());
+}
+
+TEST_F(TestIOBlockGuard, DetainedOps) {
+ OpBlockGuard op_block_guard(m_cct);
+
+ Operation op1;
+ BlockGuardCell *cell1;
+ ASSERT_EQ(0, op_block_guard.detain({1, 3}, &op1, &cell1));
+
+ Operation op2;
+ BlockGuardCell *cell2;
+ ASSERT_EQ(1, op_block_guard.detain({2, 6}, &op2, &cell2));
+ ASSERT_EQ(nullptr, cell2);
+
+ Operation op3;
+ BlockGuardCell *cell3;
+ ASSERT_EQ(2, op_block_guard.detain({0, 2}, &op3, &cell3));
+ ASSERT_EQ(nullptr, cell3);
+
+ Operations expected_ops;
+ expected_ops.push_back(std::move(op2));
+ expected_ops.push_back(std::move(op3));
+ Operations released_ops;
+ op_block_guard.release(cell1, &released_ops);
+ ASSERT_EQ(expected_ops, released_ops);
+}
+
+uint32_t TestIOBlockGuard::s_index = 0;
+
+} // namespace librbd
+
diff --git a/src/test/librbd/test_DeepCopy.cc b/src/test/librbd/test_DeepCopy.cc
new file mode 100644
index 000000000..fcce0c642
--- /dev/null
+++ b/src/test/librbd/test_DeepCopy.cc
@@ -0,0 +1,763 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Io.h"
+#include "librbd/api/Image.h"
+#include "librbd/api/Snapshot.h"
+#include "librbd/internal.h"
+#include "librbd/io/ReadResult.h"
+#include "test/librados/crimson_utils.h"
+
+void register_test_deep_copy() {
+}
+
+namespace librbd {
+
+struct TestDeepCopy : public TestFixture {
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ std::string image_name = get_temp_image_name();
+ int order = 22;
+ uint64_t size = (1 << order) * 20;
+ uint64_t features = 0;
+ bool old_format = !::get_features(&features);
+ EXPECT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, image_name, size,
+ features, old_format, &order));
+ ASSERT_EQ(0, open_image(image_name, &m_src_ictx));
+
+ if (old_format) {
+ // The destination should always be in the new format.
+ uint64_t format = 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FORMAT, format));
+ }
+ }
+
+ void TearDown() override {
+ if (m_src_ictx != nullptr) {
+ deep_copy();
+ if (m_dst_ictx != nullptr) {
+ compare();
+ close_image(m_dst_ictx);
+ }
+ close_image(m_src_ictx);
+ }
+
+ TestFixture::TearDown();
+ }
+
+ void deep_copy() {
+ std::string dst_name = get_temp_image_name();
+ librbd::NoOpProgressContext no_op;
+ EXPECT_EQ(0, api::Io<>::flush(*m_src_ictx));
+ EXPECT_EQ(0, librbd::api::Image<>::deep_copy(m_src_ictx, m_src_ictx->md_ctx,
+ dst_name.c_str(), m_opts,
+ no_op));
+ EXPECT_EQ(0, open_image(dst_name, &m_dst_ictx));
+ }
+
+ void compare() {
+ std::vector<librbd::snap_info_t> src_snaps, dst_snaps;
+
+ EXPECT_EQ(m_src_ictx->size, m_dst_ictx->size);
+ EXPECT_EQ(0, librbd::api::Snapshot<>::list(m_src_ictx, src_snaps));
+ EXPECT_EQ(0, librbd::api::Snapshot<>::list(m_dst_ictx, dst_snaps));
+ EXPECT_EQ(src_snaps.size(), dst_snaps.size());
+ for (size_t i = 0; i <= src_snaps.size(); i++) {
+ const char *src_snap_name = nullptr;
+ const char *dst_snap_name = nullptr;
+ if (i < src_snaps.size()) {
+ EXPECT_EQ(src_snaps[i].name, dst_snaps[i].name);
+ src_snap_name = src_snaps[i].name.c_str();
+ dst_snap_name = dst_snaps[i].name.c_str();
+ }
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(
+ m_src_ictx, cls::rbd::UserSnapshotNamespace(),
+ src_snap_name));
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(
+ m_dst_ictx, cls::rbd::UserSnapshotNamespace(),
+ dst_snap_name));
+ uint64_t src_size, dst_size;
+ {
+ std::shared_lock src_locker{m_src_ictx->image_lock};
+ std::shared_lock dst_locker{m_dst_ictx->image_lock};
+ src_size = m_src_ictx->get_image_size(m_src_ictx->snap_id);
+ dst_size = m_dst_ictx->get_image_size(m_dst_ictx->snap_id);
+ }
+ EXPECT_EQ(src_size, dst_size);
+
+ if (m_dst_ictx->test_features(RBD_FEATURE_LAYERING)) {
+ bool flags_set;
+ std::shared_lock dst_locker{m_dst_ictx->image_lock};
+ EXPECT_EQ(0, m_dst_ictx->test_flags(m_dst_ictx->snap_id,
+ RBD_FLAG_OBJECT_MAP_INVALID,
+ m_dst_ictx->image_lock, &flags_set));
+ EXPECT_FALSE(flags_set);
+ }
+
+ ssize_t read_size = 1 << m_src_ictx->order;
+ uint64_t offset = 0;
+ while (offset < src_size) {
+ read_size = std::min(read_size, static_cast<ssize_t>(src_size - offset));
+
+ bufferptr src_ptr(read_size);
+ bufferlist src_bl;
+ src_bl.push_back(src_ptr);
+ librbd::io::ReadResult src_result{&src_bl};
+ EXPECT_EQ(read_size, api::Io<>::read(
+ *m_src_ictx, offset, read_size,
+ librbd::io::ReadResult{src_result}, 0));
+
+ bufferptr dst_ptr(read_size);
+ bufferlist dst_bl;
+ dst_bl.push_back(dst_ptr);
+ librbd::io::ReadResult dst_result{&dst_bl};
+ EXPECT_EQ(read_size, api::Io<>::read(
+ *m_dst_ictx, offset, read_size,
+ librbd::io::ReadResult{dst_result}, 0));
+
+ if (!src_bl.contents_equal(dst_bl)) {
+ std::cout << "snap: " << (src_snap_name ? src_snap_name : "null")
+ << ", block " << offset << "~" << read_size << " differs"
+ << std::endl;
+ std::cout << "src block: " << std::endl; src_bl.hexdump(std::cout);
+ std::cout << "dst block: " << std::endl; dst_bl.hexdump(std::cout);
+ }
+ EXPECT_TRUE(src_bl.contents_equal(dst_bl));
+ offset += read_size;
+ }
+ }
+ }
+
+ void test_no_snaps() {
+ bufferlist bl;
+ bl.append(std::string(((1 << m_src_ictx->order) * 2) + 1, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 0 * bl.length(), bl.length(),
+ bufferlist{bl}, 0));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 2 * bl.length(), bl.length(),
+ bufferlist{bl}, 0));
+ }
+
+ void test_snaps() {
+ bufferlist bl;
+ bl.append(std::string(((1 << m_src_ictx->order) * 2) + 1, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 0 * bl.length(), bl.length(),
+ bufferlist{bl}, 0));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap1"));
+
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 1 * bl.length(), bl.length(),
+ bufferlist{bl}, 0));
+ bufferlist bl1;
+ bl1.append(std::string(1000, 'X'));
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::write(*m_src_ictx, 0 * bl.length(), bl1.length(),
+ bufferlist{bl1}, 0));
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::discard(*m_src_ictx, bl1.length() + 10,
+ bl1.length(), false));
+
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap2"));
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::write(*m_src_ictx, 1 * bl.length(), bl1.length(),
+ bufferlist{bl1}, 0));
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::discard(*m_src_ictx, 2 * bl1.length() + 10,
+ bl1.length(), false));
+ }
+
+ void test_snap_discard() {
+ bufferlist bl;
+ bl.append(std::string(100, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap"));
+
+ size_t len = (1 << m_src_ictx->order) * 2;
+ ASSERT_EQ(static_cast<ssize_t>(len),
+ api::Io<>::discard(*m_src_ictx, 0, len, false));
+ }
+
+ void test_clone_discard() {
+ bufferlist bl;
+ bl.append(std::string(100, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*m_src_ictx, "snap"));
+
+ std::string clone_name = get_temp_image_name();
+ int order = m_src_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_src_ictx, &features));
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_src_ictx->name.c_str(), "snap",
+ m_ioctx, clone_name.c_str(), features, &order, 0,
+ 0));
+ close_image(m_src_ictx);
+ ASSERT_EQ(0, open_image(clone_name, &m_src_ictx));
+
+ size_t len = (1 << m_src_ictx->order) * 2;
+ ASSERT_EQ(static_cast<ssize_t>(len),
+ api::Io<>::discard(*m_src_ictx, 0, len, false));
+ }
+
+ void test_clone_shrink() {
+ bufferlist bl;
+ bl.append(std::string(100, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*m_src_ictx, "snap"));
+
+ std::string clone_name = get_temp_image_name();
+ int order = m_src_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_src_ictx, &features));
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_src_ictx->name.c_str(), "snap",
+ m_ioctx, clone_name.c_str(), features, &order, 0,
+ 0));
+ close_image(m_src_ictx);
+ ASSERT_EQ(0, open_image(clone_name, &m_src_ictx));
+
+ librbd::NoOpProgressContext no_op;
+ auto new_size = m_src_ictx->size >> 1;
+ ASSERT_EQ(0, m_src_ictx->operations->resize(new_size, true, no_op));
+ }
+
+ void test_clone_expand() {
+ bufferlist bl;
+ bl.append(std::string(100, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*m_src_ictx, "snap"));
+
+ std::string clone_name = get_temp_image_name();
+ int order = m_src_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_src_ictx, &features));
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_src_ictx->name.c_str(), "snap",
+ m_ioctx, clone_name.c_str(), features, &order, 0,
+ 0));
+ close_image(m_src_ictx);
+ ASSERT_EQ(0, open_image(clone_name, &m_src_ictx));
+
+ librbd::NoOpProgressContext no_op;
+ auto new_size = m_src_ictx->size << 1;
+ ASSERT_EQ(0, m_src_ictx->operations->resize(new_size, true, no_op));
+ }
+
+ void test_clone_hide_parent() {
+ uint64_t object_size = 1 << m_src_ictx->order;
+ bufferlist bl;
+ bl.append(std::string(100, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, object_size, bl.length(),
+ bufferlist{bl}, 0));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*m_src_ictx, "snap"));
+
+ std::string clone_name = get_temp_image_name();
+ int order = m_src_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_src_ictx, &features));
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_src_ictx->name.c_str(), "snap",
+ m_ioctx, clone_name.c_str(), features, &order, 0,
+ 0));
+ close_image(m_src_ictx);
+ ASSERT_EQ(0, open_image(clone_name, &m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap1"));
+
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::discard(*m_src_ictx, object_size, bl.length(), false));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap2"));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, m_src_ictx->operations->resize(object_size, true, no_op));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap3"));
+
+ ASSERT_EQ(0, m_src_ictx->operations->resize(2 * object_size, true, no_op));
+ }
+
+ void test_clone() {
+ bufferlist bl;
+ bl.append(std::string(((1 << m_src_ictx->order) * 2) + 1, '1'));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 0 * bl.length(), bl.length(),
+ bufferlist{bl}, 0));
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, 2 * bl.length(), bl.length(),
+ bufferlist{bl}, 0));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*m_src_ictx, "snap"));
+
+ std::string clone_name = get_temp_image_name();
+ int order = m_src_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_src_ictx, &features));
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_src_ictx->name.c_str(), "snap",
+ m_ioctx, clone_name.c_str(), features, &order, 0,
+ 0));
+ close_image(m_src_ictx);
+ ASSERT_EQ(0, open_image(clone_name, &m_src_ictx));
+
+ bufferlist bl1;
+ bl1.append(std::string(1000, 'X'));
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::write(*m_src_ictx, 0 * bl.length(), bl1.length(),
+ bufferlist{bl1}, 0));
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::discard(*m_src_ictx, bl1.length() + 10,
+ bl1.length(), false));
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ ASSERT_EQ(0, snap_create(*m_src_ictx, "snap"));
+ ASSERT_EQ(0, snap_protect(*m_src_ictx, "snap"));
+
+ clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_src_ictx->name.c_str(), "snap",
+ m_ioctx, clone_name.c_str(), features, &order, 0,
+ 0));
+ close_image(m_src_ictx);
+ ASSERT_EQ(0, open_image(clone_name, &m_src_ictx));
+
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::write(*m_src_ictx, 1 * bl.length(), bl1.length(),
+ bufferlist{bl1}, 0));
+ ASSERT_EQ(static_cast<ssize_t>(bl1.length()),
+ api::Io<>::discard(*m_src_ictx, 2 * bl1.length() + 10,
+ bl1.length(), false));
+ }
+
+ void test_stress() {
+ uint64_t initial_size, size;
+ {
+ std::shared_lock src_locker{m_src_ictx->image_lock};
+ size = initial_size = m_src_ictx->get_image_size(CEPH_NOSNAP);
+ }
+
+ int nsnaps = 4;
+ const char *c = getenv("TEST_RBD_DEEPCOPY_STRESS_NSNAPS");
+ if (c != NULL) {
+ std::stringstream ss(c);
+ ASSERT_TRUE(ss >> nsnaps);
+ }
+
+ int nwrites = 4;
+ c = getenv("TEST_RBD_DEEPCOPY_STRESS_NWRITES");
+ if (c != NULL) {
+ std::stringstream ss(c);
+ ASSERT_TRUE(ss >> nwrites);
+ }
+
+ for (int i = 0; i < nsnaps; i++) {
+ for (int j = 0; j < nwrites; j++) {
+ size_t len = rand() % ((1 << m_src_ictx->order) * 2);
+ ASSERT_GT(size, len);
+ bufferlist bl;
+ bl.append(std::string(len, static_cast<char>('A' + i)));
+ uint64_t off = std::min(static_cast<uint64_t>(rand() % size),
+ static_cast<uint64_t>(size - len));
+ std::cout << "write: " << static_cast<char>('A' + i) << " " << off
+ << "~" << len << std::endl;
+ ASSERT_EQ(static_cast<ssize_t>(bl.length()),
+ api::Io<>::write(*m_src_ictx, off, bl.length(),
+ bufferlist{bl}, 0));
+ len = rand() % ((1 << m_src_ictx->order) * 2);
+ ASSERT_GT(size, len);
+ off = std::min(static_cast<uint64_t>(rand() % size),
+ static_cast<uint64_t>(size - len));
+ std::cout << "discard: " << off << "~" << len << std::endl;
+ ASSERT_EQ(static_cast<ssize_t>(len),
+ api::Io<>::discard(*m_src_ictx, off, len, false));
+ }
+
+ ASSERT_EQ(0, api::Io<>::flush(*m_src_ictx));
+
+ std::string snap_name = "snap" + stringify(i);
+ std::cout << "snap: " << snap_name << std::endl;
+ ASSERT_EQ(0, snap_create(*m_src_ictx, snap_name.c_str()));
+
+ if (m_src_ictx->test_features(RBD_FEATURE_LAYERING) && rand() % 4) {
+ ASSERT_EQ(0, snap_protect(*m_src_ictx, snap_name.c_str()));
+
+ std::string clone_name = get_temp_image_name();
+ int order = m_src_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_src_ictx, &features));
+
+ std::cout << "clone " << m_src_ictx->name << " -> " << clone_name
+ << std::endl;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_src_ictx->name.c_str(),
+ snap_name.c_str(), m_ioctx,
+ clone_name.c_str(), features, &order,
+ m_src_ictx->stripe_unit,
+ m_src_ictx->stripe_count));
+ close_image(m_src_ictx);
+ ASSERT_EQ(0, open_image(clone_name, &m_src_ictx));
+ }
+
+ if (rand() % 2) {
+ librbd::NoOpProgressContext no_op;
+ uint64_t new_size = initial_size + rand() % size;
+ std::cout << "resize: " << new_size << std::endl;
+ ASSERT_EQ(0, m_src_ictx->operations->resize(new_size, true, no_op));
+ {
+ std::shared_lock src_locker{m_src_ictx->image_lock};
+ size = m_src_ictx->get_image_size(CEPH_NOSNAP);
+ }
+ ASSERT_EQ(new_size, size);
+ }
+ }
+ }
+
+ librbd::ImageCtx *m_src_ictx = nullptr;
+ librbd::ImageCtx *m_dst_ictx = nullptr;
+ librbd::ImageOptions m_opts;
+};
+
+TEST_F(TestDeepCopy, Empty)
+{
+}
+
+TEST_F(TestDeepCopy, NoSnaps)
+{
+ test_no_snaps();
+}
+
+TEST_F(TestDeepCopy, Snaps)
+{
+ test_snaps();
+}
+
+TEST_F(TestDeepCopy, SnapDiscard)
+{
+ test_snap_discard();
+}
+
+TEST_F(TestDeepCopy, CloneDiscard)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ test_clone_discard();
+}
+
+TEST_F(TestDeepCopy, CloneShrink)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ test_clone_shrink();
+}
+
+TEST_F(TestDeepCopy, CloneExpand)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ test_clone_expand();
+}
+
+TEST_F(TestDeepCopy, CloneHideParent)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ test_clone_hide_parent();
+}
+
+TEST_F(TestDeepCopy, Clone)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, CloneFlatten)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ uint64_t flatten = 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FLATTEN, flatten));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, Stress)
+{
+ test_stress();
+}
+
+TEST_F(TestDeepCopy, NoSnaps_LargerDstObjSize)
+{
+ SKIP_IF_CRIMSON();
+ uint64_t order = m_src_ictx->order + 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+
+ test_no_snaps();
+}
+
+TEST_F(TestDeepCopy, Snaps_LargerDstObjSize)
+{
+ SKIP_IF_CRIMSON();
+ uint64_t order = m_src_ictx->order + 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+
+ test_snaps();
+}
+
+TEST_F(TestDeepCopy, Clone_LargerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ uint64_t order = m_src_ictx->order + 1 + rand() % 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, CloneFlatten_LargerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ uint64_t order = m_src_ictx->order + 1 + rand() % 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ uint64_t flatten = 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FLATTEN, flatten));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, Stress_LargerDstObjSize)
+{
+ SKIP_IF_CRIMSON();
+ uint64_t order = m_src_ictx->order + 1 + rand() % 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+
+ test_stress();
+}
+
+TEST_F(TestDeepCopy, NoSnaps_SmallerDstObjSize)
+{
+ uint64_t order = m_src_ictx->order - 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ uint64_t stripe_unit = m_src_ictx->stripe_unit >> 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+
+ test_no_snaps();
+}
+
+TEST_F(TestDeepCopy, Snaps_SmallerDstObjSize)
+{
+ uint64_t order = m_src_ictx->order - 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ uint64_t stripe_unit = m_src_ictx->stripe_unit >> 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+
+ test_snaps();
+}
+
+TEST_F(TestDeepCopy, Clone_SmallerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ uint64_t order = m_src_ictx->order - 1 - rand() % 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ uint64_t stripe_unit = m_src_ictx->stripe_unit >> 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, CloneFlatten_SmallerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ uint64_t order = m_src_ictx->order - 1 - rand() % 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ uint64_t stripe_unit = m_src_ictx->stripe_unit >> 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ uint64_t flatten = 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FLATTEN, flatten));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, Stress_SmallerDstObjSize)
+{
+ uint64_t order = m_src_ictx->order - 1 - rand() % 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ uint64_t stripe_unit = m_src_ictx->stripe_unit >> 2;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+
+ test_stress();
+}
+
+TEST_F(TestDeepCopy, NoSnaps_StrippingLargerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order + 1;
+ uint64_t stripe_unit = 1 << (order - 2);
+ uint64_t stripe_count = 4;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_no_snaps();
+}
+
+TEST_F(TestDeepCopy, Snaps_StrippingLargerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order + 1;
+ uint64_t stripe_unit = 1 << (order - 2);
+ uint64_t stripe_count = 4;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_snaps();
+}
+
+TEST_F(TestDeepCopy, Clone_StrippingLargerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order + 1;
+ uint64_t stripe_unit = 1 << (order - 2);
+ uint64_t stripe_count = 4;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, CloneFlatten_StrippingLargerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order + 1;
+ uint64_t stripe_unit = 1 << (order - 2);
+ uint64_t stripe_count = 4;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+ uint64_t flatten = 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FLATTEN, flatten));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, Stress_StrippingLargerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order + 1 + rand() % 2;
+ uint64_t stripe_unit = 1 << (order - rand() % 4);
+ uint64_t stripe_count = 2 + rand() % 14;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_stress();
+}
+
+TEST_F(TestDeepCopy, NoSnaps_StrippingSmallerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order - 1;
+ uint64_t stripe_unit = 1 << (order - 2);
+ uint64_t stripe_count = 4;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_no_snaps();
+}
+
+TEST_F(TestDeepCopy, Snaps_StrippingSmallerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order - 1;
+ uint64_t stripe_unit = 1 << (order - 2);
+ uint64_t stripe_count = 4;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_snaps();
+}
+
+TEST_F(TestDeepCopy, Clone_StrippingSmallerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order - 1 - rand() % 2;
+ uint64_t stripe_unit = 1 << (order - rand() % 4);
+ uint64_t stripe_count = 2 + rand() % 14;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, CloneFlatten_StrippingSmallerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order - 1 - rand() % 2;
+ uint64_t stripe_unit = 1 << (order - rand() % 4);
+ uint64_t stripe_count = 2 + rand() % 14;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+ uint64_t flatten = 1;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FLATTEN, flatten));
+
+ test_clone();
+}
+
+TEST_F(TestDeepCopy, Stress_StrippingSmallerDstObjSize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+
+ uint64_t order = m_src_ictx->order - 1 - rand() % 2;
+ uint64_t stripe_unit = 1 << (order - rand() % 4);
+ uint64_t stripe_count = 2 + rand() % 14;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ test_stress();
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_Groups.cc b/src/test/librbd/test_Groups.cc
new file mode 100644
index 000000000..88b19146f
--- /dev/null
+++ b/src/test/librbd/test_Groups.cc
@@ -0,0 +1,445 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd/librbd.h"
+#include "include/rbd/librbd.hpp"
+#include "test/librados/test.h"
+#include "gtest/gtest.h"
+
+#include <boost/scope_exit.hpp>
+#include <chrono>
+#include <vector>
+
+void register_test_groups() {
+}
+
+class TestGroup : public TestFixture {
+
+};
+
+TEST_F(TestGroup, group_create)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, _pool_name.c_str(), &ioctx);
+ BOOST_SCOPE_EXIT(ioctx) {
+ rados_ioctx_destroy(ioctx);
+ } BOOST_SCOPE_EXIT_END;
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd_group_create(ioctx, "mygroup"));
+
+ size_t size = 0;
+ ASSERT_EQ(-ERANGE, rbd_group_list(ioctx, NULL, &size));
+ ASSERT_EQ(strlen("mygroup") + 1, size);
+
+ char groups[80];
+ ASSERT_EQ(static_cast<int>(strlen("mygroup") + 1),
+ rbd_group_list(ioctx, groups, &size));
+ ASSERT_STREQ("mygroup", groups);
+
+ ASSERT_EQ(0, rbd_group_remove(ioctx, "mygroup"));
+
+ ASSERT_EQ(0, rbd_group_list(ioctx, groups, &size));
+}
+
+TEST_F(TestGroup, group_createPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.group_create(ioctx, "mygroup"));
+
+ std::vector<std::string> groups;
+ ASSERT_EQ(0, rbd.group_list(ioctx, &groups));
+ ASSERT_EQ(1U, groups.size());
+ ASSERT_EQ("mygroup", groups[0]);
+
+ groups.clear();
+ ASSERT_EQ(0, rbd.group_rename(ioctx, "mygroup", "newgroup"));
+ ASSERT_EQ(0, rbd.group_list(ioctx, &groups));
+ ASSERT_EQ(1U, groups.size());
+ ASSERT_EQ("newgroup", groups[0]);
+
+ ASSERT_EQ(0, rbd.group_remove(ioctx, "newgroup"));
+
+ groups.clear();
+ ASSERT_EQ(0, rbd.group_list(ioctx, &groups));
+ ASSERT_EQ(0U, groups.size());
+}
+
+TEST_F(TestGroup, add_image)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, _pool_name.c_str(), &ioctx);
+ BOOST_SCOPE_EXIT(ioctx) {
+ rados_ioctx_destroy(ioctx);
+ } BOOST_SCOPE_EXIT_END;
+
+ const char *group_name = "mycg";
+ ASSERT_EQ(0, rbd_group_create(ioctx, group_name));
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, m_image_name.c_str(), &image, NULL));
+ BOOST_SCOPE_EXIT(image) {
+ EXPECT_EQ(0, rbd_close(image));
+ } BOOST_SCOPE_EXIT_END;
+
+ uint64_t features;
+ ASSERT_EQ(0, rbd_get_features(image, &features));
+ ASSERT_TRUE((features & RBD_FEATURE_OPERATIONS) == 0ULL);
+
+ uint64_t op_features;
+ ASSERT_EQ(0, rbd_get_op_features(image, &op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_GROUP) == 0ULL);
+
+ rbd_group_info_t group_info;
+ ASSERT_EQ(0, rbd_get_group(image, &group_info, sizeof(group_info)));
+ ASSERT_EQ(0, strcmp("", group_info.name));
+ ASSERT_EQ(RBD_GROUP_INVALID_POOL, group_info.pool);
+ rbd_group_info_cleanup(&group_info, sizeof(group_info));
+
+ ASSERT_EQ(0, rbd_group_image_add(ioctx, group_name, ioctx,
+ m_image_name.c_str()));
+
+ ASSERT_EQ(-ERANGE, rbd_get_group(image, &group_info, 0));
+ ASSERT_EQ(0, rbd_get_group(image, &group_info, sizeof(group_info)));
+ ASSERT_EQ(0, strcmp(group_name, group_info.name));
+ ASSERT_EQ(rados_ioctx_get_id(ioctx), group_info.pool);
+ rbd_group_info_cleanup(&group_info, sizeof(group_info));
+
+ ASSERT_EQ(0, rbd_get_features(image, &features));
+ ASSERT_TRUE((features & RBD_FEATURE_OPERATIONS) ==
+ RBD_FEATURE_OPERATIONS);
+ ASSERT_EQ(0, rbd_get_op_features(image, &op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_GROUP) ==
+ RBD_OPERATION_FEATURE_GROUP);
+
+ size_t num_images = 0;
+ ASSERT_EQ(-ERANGE, rbd_group_image_list(ioctx, group_name, NULL,
+ sizeof(rbd_group_image_info_t),
+ &num_images));
+ ASSERT_EQ(1U, num_images);
+
+ rbd_group_image_info_t images[1];
+ ASSERT_EQ(1, rbd_group_image_list(ioctx, group_name, images,
+ sizeof(rbd_group_image_info_t),
+ &num_images));
+
+ ASSERT_EQ(m_image_name, images[0].name);
+ ASSERT_EQ(rados_ioctx_get_id(ioctx), images[0].pool);
+
+ ASSERT_EQ(0, rbd_group_image_list_cleanup(images,
+ sizeof(rbd_group_image_info_t),
+ num_images));
+ ASSERT_EQ(0, rbd_group_image_remove(ioctx, group_name, ioctx,
+ m_image_name.c_str()));
+
+ ASSERT_EQ(0, rbd_get_features(image, &features));
+ ASSERT_TRUE((features & RBD_FEATURE_OPERATIONS) == 0ULL);
+ ASSERT_EQ(0, rbd_get_op_features(image, &op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_GROUP) == 0ULL);
+
+ ASSERT_EQ(0, rbd_group_image_list(ioctx, group_name, images,
+ sizeof(rbd_group_image_info_t),
+ &num_images));
+ ASSERT_EQ(0U, num_images);
+
+ ASSERT_EQ(0, rbd_group_remove(ioctx, group_name));
+}
+
+TEST_F(TestGroup, add_imagePP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ const char *group_name = "mycg";
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.group_create(ioctx, group_name));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, m_image_name.c_str(), NULL));
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_TRUE((features & RBD_FEATURE_OPERATIONS) == 0ULL);
+
+ uint64_t op_features;
+ ASSERT_EQ(0, image.get_op_features(&op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_GROUP) == 0ULL);
+
+ librbd::group_info_t group_info;
+ ASSERT_EQ(0, image.get_group(&group_info, sizeof(group_info)));
+ ASSERT_EQ(std::string(""), group_info.name);
+ ASSERT_EQ(RBD_GROUP_INVALID_POOL, group_info.pool);
+
+ ASSERT_EQ(0, rbd.group_image_add(ioctx, group_name, ioctx,
+ m_image_name.c_str()));
+
+ ASSERT_EQ(-ERANGE, image.get_group(&group_info, 0));
+ ASSERT_EQ(0, image.get_group(&group_info, sizeof(group_info)));
+ ASSERT_EQ(std::string(group_name), group_info.name);
+ ASSERT_EQ(ioctx.get_id(), group_info.pool);
+
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_TRUE((features & RBD_FEATURE_OPERATIONS) ==
+ RBD_FEATURE_OPERATIONS);
+ ASSERT_EQ(0, image.get_op_features(&op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_GROUP) ==
+ RBD_OPERATION_FEATURE_GROUP);
+
+ std::vector<librbd::group_image_info_t> images;
+ ASSERT_EQ(0, rbd.group_image_list(ioctx, group_name, &images,
+ sizeof(librbd::group_image_info_t)));
+ ASSERT_EQ(1U, images.size());
+ ASSERT_EQ(m_image_name, images[0].name);
+ ASSERT_EQ(ioctx.get_id(), images[0].pool);
+
+ ASSERT_EQ(0, rbd.group_image_remove(ioctx, group_name, ioctx,
+ m_image_name.c_str()));
+
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_TRUE((features & RBD_FEATURE_OPERATIONS) == 0ULL);
+ ASSERT_EQ(0, image.get_op_features(&op_features));
+ ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_GROUP) == 0ULL);
+
+ images.clear();
+ ASSERT_EQ(0, rbd.group_image_list(ioctx, group_name, &images,
+ sizeof(librbd::group_image_info_t)));
+ ASSERT_EQ(0U, images.size());
+
+ ASSERT_EQ(0, rbd.group_remove(ioctx, group_name));
+}
+
+TEST_F(TestGroup, add_snapshot)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, _pool_name.c_str(), &ioctx);
+ BOOST_SCOPE_EXIT(ioctx) {
+ rados_ioctx_destroy(ioctx);
+ } BOOST_SCOPE_EXIT_END;
+
+ const char *group_name = "snap_group";
+ const char *snap_name = "snap_snapshot";
+
+ const char orig_data[] = "orig data";
+ const char test_data[] = "test data";
+ char read_data[10];
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, m_image_name.c_str(), &image, NULL));
+ BOOST_SCOPE_EXIT(image) {
+ EXPECT_EQ(0, rbd_close(image));
+ } BOOST_SCOPE_EXIT_END;
+
+ ASSERT_EQ(10, rbd_write(image, 0, 10, orig_data));
+ ASSERT_EQ(10, rbd_read(image, 0, 10, read_data));
+ ASSERT_EQ(0, memcmp(orig_data, read_data, 10));
+
+ ASSERT_EQ(0, rbd_group_create(ioctx, group_name));
+
+ ASSERT_EQ(0, rbd_group_image_add(ioctx, group_name, ioctx,
+ m_image_name.c_str()));
+
+ struct Watcher {
+ static void quiesce_cb(void *arg) {
+ Watcher *watcher = static_cast<Watcher *>(arg);
+ watcher->handle_quiesce();
+ }
+ static void unquiesce_cb(void *arg) {
+ Watcher *watcher = static_cast<Watcher *>(arg);
+ watcher->handle_unquiesce();
+ }
+
+ rbd_image_t &image;
+ uint64_t handle = 0;
+ size_t quiesce_count = 0;
+ size_t unquiesce_count = 0;
+ int r = 0;
+
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cv;
+
+ Watcher(rbd_image_t &image) : image(image) {
+ }
+
+ void handle_quiesce() {
+ ASSERT_EQ(quiesce_count, unquiesce_count);
+ quiesce_count++;
+ rbd_quiesce_complete(image, handle, r);
+ }
+ void handle_unquiesce() {
+ std::unique_lock locker(lock);
+ unquiesce_count++;
+ cv.notify_one();
+ }
+ bool wait_for_unquiesce(size_t c) {
+ std::unique_lock locker(lock);
+ return cv.wait_for(locker, std::chrono::seconds(60),
+ [this, c]() { return unquiesce_count >= c; });
+ }
+ } watcher(image);
+
+ ASSERT_EQ(0, rbd_quiesce_watch(image, Watcher::quiesce_cb,
+ Watcher::unquiesce_cb, &watcher,
+ &watcher.handle));
+
+ ASSERT_EQ(0, rbd_group_snap_create(ioctx, group_name, snap_name));
+ ASSERT_TRUE(watcher.wait_for_unquiesce(1U));
+ ASSERT_EQ(1U, watcher.quiesce_count);
+
+ size_t num_snaps = 0;
+ ASSERT_EQ(-ERANGE, rbd_group_snap_list(ioctx, group_name, NULL,
+ sizeof(rbd_group_snap_info_t),
+ &num_snaps));
+ ASSERT_EQ(1U, num_snaps);
+
+ rbd_group_snap_info_t snaps[1];
+ ASSERT_EQ(1, rbd_group_snap_list(ioctx, group_name, snaps,
+ sizeof(rbd_group_snap_info_t),
+ &num_snaps));
+
+ ASSERT_STREQ(snap_name, snaps[0].name);
+
+ ASSERT_EQ(10, rbd_write(image, 11, 10, test_data));
+ ASSERT_EQ(10, rbd_read(image, 11, 10, read_data));
+ ASSERT_EQ(0, memcmp(test_data, read_data, 10));
+
+ ASSERT_EQ(0, rbd_group_snap_rollback(ioctx, group_name, snap_name));
+ ASSERT_EQ(10, rbd_read(image, 0, 10, read_data));
+ ASSERT_EQ(0, memcmp(orig_data, read_data, 10));
+
+ ASSERT_EQ(0, rbd_group_snap_list_cleanup(snaps, sizeof(rbd_group_snap_info_t),
+ num_snaps));
+ ASSERT_EQ(0, rbd_group_snap_remove(ioctx, group_name, snap_name));
+
+ ASSERT_EQ(0, rbd_group_snap_list(ioctx, group_name, snaps,
+ sizeof(rbd_group_snap_info_t),
+ &num_snaps));
+ ASSERT_EQ(0U, num_snaps);
+
+ ASSERT_EQ(-EINVAL, rbd_group_snap_create2(ioctx, group_name, snap_name,
+ RBD_SNAP_CREATE_SKIP_QUIESCE |
+ RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR));
+ watcher.r = -EINVAL;
+ ASSERT_EQ(-EINVAL, rbd_group_snap_create2(ioctx, group_name, snap_name, 0));
+
+ num_snaps = 1;
+ ASSERT_EQ(0, rbd_group_snap_list(ioctx, group_name, snaps,
+ sizeof(rbd_group_snap_info_t),
+ &num_snaps));
+
+ watcher.quiesce_count = 0;
+ watcher.unquiesce_count = 0;
+ ASSERT_EQ(0, rbd_group_snap_create2(ioctx, group_name, snap_name,
+ RBD_SNAP_CREATE_SKIP_QUIESCE));
+ ASSERT_EQ(0U, watcher.quiesce_count);
+ num_snaps = 1;
+ ASSERT_EQ(1, rbd_group_snap_list(ioctx, group_name, snaps,
+ sizeof(rbd_group_snap_info_t),
+ &num_snaps));
+ ASSERT_EQ(0, rbd_group_snap_list_cleanup(snaps, sizeof(rbd_group_snap_info_t),
+ num_snaps));
+ ASSERT_EQ(0, rbd_group_snap_remove(ioctx, group_name, snap_name));
+
+ ASSERT_EQ(0, rbd_group_snap_create2(ioctx, group_name, snap_name,
+ RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR));
+ ASSERT_EQ(1, rbd_group_snap_list(ioctx, group_name, snaps,
+ sizeof(rbd_group_snap_info_t),
+ &num_snaps));
+ ASSERT_EQ(0, rbd_group_snap_list_cleanup(snaps, sizeof(rbd_group_snap_info_t),
+ num_snaps));
+ ASSERT_EQ(0, rbd_group_snap_remove(ioctx, group_name, snap_name));
+
+ ASSERT_EQ(0, rbd_quiesce_unwatch(image, watcher.handle));
+ ASSERT_EQ(0, rbd_group_remove(ioctx, group_name));
+}
+
+TEST_F(TestGroup, add_snapshotPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ const char *group_name = "snap_group";
+ const char *snap_name = "snap_snapshot";
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.group_create(ioctx, group_name));
+
+ ASSERT_EQ(0, rbd.group_image_add(ioctx, group_name, ioctx,
+ m_image_name.c_str()));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, m_image_name.c_str(), NULL));
+ bufferlist expect_bl;
+ bufferlist read_bl;
+ expect_bl.append(std::string(512, '1'));
+ ASSERT_EQ((ssize_t)expect_bl.length(), image.write(0, expect_bl.length(), expect_bl));
+ ASSERT_EQ(512, image.read(0, 512, read_bl));
+ ASSERT_TRUE(expect_bl.contents_equal(read_bl));
+
+ ASSERT_EQ(0, rbd.group_snap_create(ioctx, group_name, snap_name));
+
+ std::vector<librbd::group_snap_info_t> snaps;
+ ASSERT_EQ(0, rbd.group_snap_list(ioctx, group_name, &snaps,
+ sizeof(librbd::group_snap_info_t)));
+ ASSERT_EQ(1U, snaps.size());
+
+ ASSERT_EQ(snap_name, snaps[0].name);
+
+ bufferlist write_bl;
+ write_bl.append(std::string(1024, '2'));
+ ASSERT_EQ(1024, image.write(513, write_bl.length(), write_bl));
+
+ read_bl.clear();
+ ASSERT_EQ(1024, image.read(513, 1024, read_bl));
+ ASSERT_TRUE(write_bl.contents_equal(read_bl));
+
+ ASSERT_EQ(0, rbd.group_snap_rollback(ioctx, group_name, snap_name));
+
+ ASSERT_EQ(512, image.read(0, 512, read_bl));
+ ASSERT_TRUE(expect_bl.contents_equal(read_bl));
+
+ ASSERT_EQ(0, image.close());
+
+ ASSERT_EQ(0, rbd.group_snap_remove(ioctx, group_name, snap_name));
+
+ snaps.clear();
+ ASSERT_EQ(0, rbd.group_snap_list(ioctx, group_name, &snaps,
+ sizeof(librbd::group_snap_info_t)));
+ ASSERT_EQ(0U, snaps.size());
+
+ ASSERT_EQ(0, rbd.group_snap_create(ioctx, group_name, snap_name));
+ ASSERT_EQ(0, rbd.group_snap_list(ioctx, group_name, &snaps,
+ sizeof(librbd::group_snap_info_t)));
+ ASSERT_EQ(1U, snaps.size());
+ ASSERT_EQ(0, rbd.group_snap_remove(ioctx, group_name, snap_name));
+
+ ASSERT_EQ(-EINVAL, rbd.group_snap_create2(ioctx, group_name, snap_name,
+ RBD_SNAP_CREATE_SKIP_QUIESCE |
+ RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR));
+ snaps.clear();
+ ASSERT_EQ(0, rbd.group_snap_list(ioctx, group_name, &snaps,
+ sizeof(librbd::group_snap_info_t)));
+ ASSERT_EQ(0U, snaps.size());
+
+ ASSERT_EQ(0, rbd.group_snap_create2(ioctx, group_name, snap_name,
+ RBD_SNAP_CREATE_SKIP_QUIESCE));
+ snaps.clear();
+ ASSERT_EQ(0, rbd.group_snap_list(ioctx, group_name, &snaps,
+ sizeof(librbd::group_snap_info_t)));
+ ASSERT_EQ(1U, snaps.size());
+
+ ASSERT_EQ(0, rbd.group_snap_remove(ioctx, group_name, snap_name));
+ ASSERT_EQ(0, rbd.group_remove(ioctx, group_name));
+}
diff --git a/src/test/librbd/test_ImageWatcher.cc b/src/test/librbd/test_ImageWatcher.cc
new file mode 100644
index 000000000..780ce7c0e
--- /dev/null
+++ b/src/test/librbd/test_ImageWatcher.cc
@@ -0,0 +1,940 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/int_types.h"
+#include "include/stringify.h"
+#include "include/rados/librados.h"
+#include "include/rbd/librbd.hpp"
+#include "common/Cond.h"
+#include "common/ceph_mutex.h"
+#include "common/errno.h"
+#include "cls/lock/cls_lock_client.h"
+#include "cls/lock/cls_lock_types.h"
+#include "librbd/internal.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/WatchNotifyTypes.h"
+#include "librbd/io/AioCompletion.h"
+#include "test/librados/test.h"
+#include "gtest/gtest.h"
+#include <boost/assign/std/set.hpp>
+#include <boost/assign/std/map.hpp>
+#include <boost/scope_exit.hpp>
+#include <boost/thread/thread.hpp>
+#include <iostream>
+#include <map>
+#include <set>
+#include <sstream>
+#include <vector>
+
+using namespace std::chrono_literals;
+using namespace ceph;
+using namespace boost::assign;
+using namespace librbd::watch_notify;
+
+void register_test_image_watcher() {
+}
+
+class TestImageWatcher : public TestFixture {
+public:
+
+ TestImageWatcher() : m_watch_ctx(NULL)
+ {
+ }
+
+ class WatchCtx : public librados::WatchCtx2 {
+ public:
+ explicit WatchCtx(TestImageWatcher &parent) : m_parent(parent), m_handle(0) {}
+
+ int watch(const librbd::ImageCtx &ictx) {
+ m_header_oid = ictx.header_oid;
+ return m_parent.m_ioctx.watch2(m_header_oid, &m_handle, this);
+ }
+
+ int unwatch() {
+ return m_parent.m_ioctx.unwatch2(m_handle);
+ }
+
+ void handle_notify(uint64_t notify_id,
+ uint64_t cookie,
+ uint64_t notifier_id,
+ bufferlist& bl) override {
+ try {
+ int op;
+ bufferlist payload;
+ auto iter = bl.cbegin();
+ DECODE_START(1, iter);
+ decode(op, iter);
+ iter.copy_all(payload);
+ DECODE_FINISH(iter);
+
+ NotifyOp notify_op = static_cast<NotifyOp>(op);
+ /*
+ std::cout << "NOTIFY: " << notify_op << ", " << notify_id
+ << ", " << cookie << ", " << notifier_id << std::endl;
+ */
+
+ std::lock_guard l{m_parent.m_callback_lock};
+ m_parent.m_notify_payloads[notify_op] = payload;
+
+ bufferlist reply;
+ if (m_parent.m_notify_acks.count(notify_op) > 0) {
+ reply = m_parent.m_notify_acks[notify_op];
+ m_parent.m_notifies += notify_op;
+ m_parent.m_callback_cond.notify_all();
+ }
+
+ m_parent.m_ioctx.notify_ack(m_header_oid, notify_id, cookie, reply);
+ } catch (...) {
+ FAIL();
+ }
+ }
+
+ void handle_error(uint64_t cookie, int err) override {
+ std::cerr << "ERROR: " << cookie << ", " << cpp_strerror(err)
+ << std::endl;
+ }
+
+ uint64_t get_handle() const {
+ return m_handle;
+ }
+
+ private:
+ TestImageWatcher &m_parent;
+ std::string m_header_oid;
+ uint64_t m_handle;
+ };
+
+ void TearDown() override {
+ deregister_image_watch();
+ TestFixture::TearDown();
+ }
+
+ int deregister_image_watch() {
+ if (m_watch_ctx != NULL) {
+ int r = m_watch_ctx->unwatch();
+
+ librados::Rados rados(m_ioctx);
+ rados.watch_flush();
+
+ delete m_watch_ctx;
+ m_watch_ctx = NULL;
+ return r;
+ }
+ return 0;
+ }
+
+ int register_image_watch(librbd::ImageCtx &ictx) {
+ m_watch_ctx = new WatchCtx(*this);
+ return m_watch_ctx->watch(ictx);
+ }
+
+ bool wait_for_notifies(librbd::ImageCtx &ictx) {
+ std::unique_lock l{m_callback_lock};
+ while (m_notifies.size() < m_notify_acks.size()) {
+ if (m_callback_cond.wait_for(l, 10s) == std::cv_status::timeout) {
+ break;
+ }
+ }
+ return (m_notifies.size() == m_notify_acks.size());
+ }
+
+ bufferlist create_response_message(int r) {
+ bufferlist bl;
+ encode(ResponseMessage(r), bl);
+ return bl;
+ }
+
+ bool extract_async_request_id(NotifyOp op, AsyncRequestId *id) {
+ if (m_notify_payloads.count(op) == 0) {
+ return false;
+ }
+
+ bufferlist payload = m_notify_payloads[op];
+ auto iter = payload.cbegin();
+
+ switch (op) {
+ case NOTIFY_OP_FLATTEN:
+ {
+ FlattenPayload payload;
+ payload.decode(2, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_RESIZE:
+ {
+ ResizePayload payload;
+ payload.decode(2, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_SNAP_CREATE:
+ {
+ SnapCreatePayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_SNAP_RENAME:
+ {
+ SnapRenamePayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_SNAP_REMOVE:
+ {
+ SnapRemovePayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_SNAP_PROTECT:
+ {
+ SnapProtectPayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_SNAP_UNPROTECT:
+ {
+ SnapUnprotectPayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_RENAME:
+ {
+ RenamePayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_REBUILD_OBJECT_MAP:
+ {
+ RebuildObjectMapPayload payload;
+ payload.decode(2, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ case NOTIFY_OP_UPDATE_FEATURES:
+ {
+ UpdateFeaturesPayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ int notify_async_progress(librbd::ImageCtx *ictx, const AsyncRequestId &id,
+ uint64_t offset, uint64_t total) {
+ bufferlist bl;
+ encode(NotifyMessage(new AsyncProgressPayload(id, offset, total)), bl);
+ return m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL);
+ }
+
+ int notify_async_complete(librbd::ImageCtx *ictx, const AsyncRequestId &id,
+ int r) {
+ bufferlist bl;
+ encode(NotifyMessage(new AsyncCompletePayload(id, r)), bl);
+ return m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL);
+ }
+
+ typedef std::map<NotifyOp, bufferlist> NotifyOpPayloads;
+ typedef std::set<NotifyOp> NotifyOps;
+
+ WatchCtx *m_watch_ctx;
+
+ NotifyOps m_notifies;
+ NotifyOpPayloads m_notify_payloads;
+ NotifyOpPayloads m_notify_acks;
+
+ AsyncRequestId m_async_request_id;
+
+ ceph::mutex m_callback_lock = ceph::make_mutex("m_callback_lock");
+ ceph::condition_variable m_callback_cond;
+
+};
+
+struct ProgressContext : public librbd::ProgressContext {
+ ceph::mutex mutex = ceph::make_mutex("ProgressContext::mutex");
+ ceph::condition_variable cond;
+ bool received;
+ uint64_t offset;
+ uint64_t total;
+
+ ProgressContext() : received(false),
+ offset(0), total(0) {}
+
+ int update_progress(uint64_t offset_, uint64_t total_) override {
+ std::lock_guard l{mutex};
+ offset = offset_;
+ total = total_;
+ received = true;
+ cond.notify_all();
+ return 0;
+ }
+
+ bool wait(librbd::ImageCtx *ictx, uint64_t offset_, uint64_t total_) {
+ std::unique_lock l{mutex};
+ while (!received) {
+ if (cond.wait_for(l, 10s) == std::cv_status::timeout) {
+ break;
+ }
+ }
+ return (received && offset == offset_ && total == total_);
+ }
+};
+
+struct FlattenTask {
+ librbd::ImageCtx *ictx;
+ ProgressContext *progress_context;
+ int result;
+
+ FlattenTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
+ : ictx(ictx_), progress_context(ctx), result(0) {}
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_flatten(0, *progress_context, &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct ResizeTask {
+ librbd::ImageCtx *ictx;
+ ProgressContext *progress_context;
+ int result;
+
+ ResizeTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
+ : ictx(ictx_), progress_context(ctx), result(0) {}
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_resize(0, 0, true, *progress_context, &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct SnapCreateTask {
+ librbd::ImageCtx *ictx;
+ ProgressContext *progress_context;
+ int result;
+
+ SnapCreateTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
+ : ictx(ictx_), progress_context(ctx), result(0) {}
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_snap_create(0, cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, *progress_context, &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct SnapRenameTask {
+ librbd::ImageCtx *ictx;
+ int result = 0;
+
+ SnapRenameTask(librbd::ImageCtx *ictx)
+ : ictx(ictx) {
+ }
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_snap_rename(0, 1, "snap-rename", &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct SnapRemoveTask {
+ librbd::ImageCtx *ictx;
+ int result = 0;
+
+ SnapRemoveTask(librbd::ImageCtx *ictx)
+ : ictx(ictx) {
+ }
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_snap_remove(
+ 0, cls::rbd::UserSnapshotNamespace(), "snap", &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct SnapProtectTask {
+ librbd::ImageCtx *ictx;
+ int result = 0;
+
+ SnapProtectTask(librbd::ImageCtx *ictx)
+ : ictx(ictx) {
+ }
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_snap_protect(
+ 0, cls::rbd::UserSnapshotNamespace(), "snap", &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct SnapUnprotectTask {
+ librbd::ImageCtx *ictx;
+ int result = 0;
+
+ SnapUnprotectTask(librbd::ImageCtx *ictx)
+ : ictx(ictx) {
+ }
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_snap_unprotect(
+ 0, cls::rbd::UserSnapshotNamespace(), "snap", &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct RenameTask {
+ librbd::ImageCtx *ictx;
+ int result = 0;
+
+ RenameTask(librbd::ImageCtx *ictx)
+ : ictx(ictx) {
+ }
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_rename(0, "new_name", &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct RebuildObjectMapTask {
+ librbd::ImageCtx *ictx;
+ ProgressContext *progress_context;
+ int result;
+
+ RebuildObjectMapTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
+ : ictx(ictx_), progress_context(ctx), result(0) {}
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_rebuild_object_map(0, *progress_context, &ctx);
+ result = ctx.wait();
+ }
+};
+
+struct UpdateFeaturesTask {
+ librbd::ImageCtx *ictx;
+ int result;
+
+ UpdateFeaturesTask(librbd::ImageCtx *ictx)
+ : ictx(ictx), result(0) {}
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ uint64_t features = 24;
+ bool enabled = 0;
+ ictx->image_watcher->notify_update_features(0, features, enabled, &ctx);
+ result = ctx.wait();
+ }
+};
+
+TEST_F(TestImageWatcher, NotifyHeaderUpdate) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+
+ m_notify_acks = {{NOTIFY_OP_HEADER_UPDATE, {}}};
+ ictx->notify_update();
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_HEADER_UPDATE;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+}
+
+TEST_F(TestImageWatcher, NotifyFlatten) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}};
+
+ ProgressContext progress_context;
+ FlattenTask flatten_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(flatten_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_FLATTEN;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_FLATTEN, &async_request_id));
+
+ ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20));
+ ASSERT_TRUE(progress_context.wait(ictx, 10, 20));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, flatten_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyResize) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_RESIZE, create_response_message(0)}};
+
+ ProgressContext progress_context;
+ ResizeTask resize_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(resize_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_RESIZE;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_RESIZE, &async_request_id));
+
+ ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20));
+ ASSERT_TRUE(progress_context.wait(ictx, 10, 20));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, resize_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyRebuildObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_REBUILD_OBJECT_MAP, create_response_message(0)}};
+
+ ProgressContext progress_context;
+ RebuildObjectMapTask rebuild_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(rebuild_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_REBUILD_OBJECT_MAP;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_REBUILD_OBJECT_MAP,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20));
+ ASSERT_TRUE(progress_context.wait(ictx, 10, 20));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, rebuild_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyUpdateFeatures) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_UPDATE_FEATURES, create_response_message(0)}};
+
+ UpdateFeaturesTask update_features_task(ictx);
+ boost::thread thread(boost::ref(update_features_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_UPDATE_FEATURES;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_UPDATE_FEATURES,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, update_features_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifySnapCreate) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(0)}};
+
+ ProgressContext progress_context;
+ SnapCreateTask snap_create_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(snap_create_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_SNAP_CREATE;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_SNAP_CREATE,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 1, 10));
+ ASSERT_TRUE(progress_context.wait(ictx, 1, 10));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, snap_create_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifySnapCreateError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(-EEXIST)}};
+
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond notify_ctx;
+ librbd::NoOpProgressContext prog_ctx;
+ ictx->image_watcher->notify_snap_create(0, cls::rbd::UserSnapshotNamespace(),
+ "snap", 0, prog_ctx, &notify_ctx);
+ ASSERT_EQ(-EEXIST, notify_ctx.wait());
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_SNAP_CREATE;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+}
+
+TEST_F(TestImageWatcher, NotifySnapRename) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_SNAP_RENAME, create_response_message(0)}};
+
+ SnapRenameTask snap_rename_task(ictx);
+ boost::thread thread(boost::ref(snap_rename_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_SNAP_RENAME;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_SNAP_RENAME,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, snap_rename_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifySnapRenameError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_SNAP_RENAME, create_response_message(-EEXIST)}};
+
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond notify_ctx;
+ ictx->image_watcher->notify_snap_rename(0, 1, "snap-rename", &notify_ctx);
+ ASSERT_EQ(-EEXIST, notify_ctx.wait());
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_SNAP_RENAME;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+}
+
+TEST_F(TestImageWatcher, NotifySnapRemove) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_SNAP_REMOVE, create_response_message(0)}};
+
+ SnapRemoveTask snap_remove_task(ictx);
+ boost::thread thread(boost::ref(snap_remove_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_SNAP_REMOVE;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_SNAP_REMOVE,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, snap_remove_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifySnapProtect) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_SNAP_PROTECT, create_response_message(0)}};
+
+ SnapProtectTask snap_protect_task(ictx);
+ boost::thread thread(boost::ref(snap_protect_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_SNAP_PROTECT;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_SNAP_PROTECT,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, snap_protect_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifySnapUnprotect) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_SNAP_UNPROTECT, create_response_message(0)}};
+
+ SnapUnprotectTask snap_unprotect_task(ictx);
+ boost::thread thread(boost::ref(snap_unprotect_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_SNAP_UNPROTECT;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_SNAP_UNPROTECT,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, snap_unprotect_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyRename) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_RENAME, create_response_message(0)}};
+
+ RenameTask rename_task(ictx);
+ boost::thread thread(boost::ref(rename_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_RENAME;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_RENAME,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, rename_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyAsyncTimedOut) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_FLATTEN, {}}};
+
+ ProgressContext progress_context;
+ FlattenTask flatten_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(flatten_task));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(-ETIMEDOUT, flatten_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyAsyncError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(-EIO)}};
+
+ ProgressContext progress_context;
+ FlattenTask flatten_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(flatten_task));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(-EIO, flatten_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyAsyncCompleteError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}};
+
+ ProgressContext progress_context;
+ FlattenTask flatten_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(flatten_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ NotifyOps expected_notify_ops;
+ expected_notify_ops += NOTIFY_OP_FLATTEN;
+ ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_FLATTEN, &async_request_id));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, -ESHUTDOWN));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(-ESHUTDOWN, flatten_task.result);
+}
+
+TEST_F(TestImageWatcher, NotifyAsyncRequestTimedOut) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ictx->config.set_val("rbd_request_timed_out_seconds", "0");
+
+ ASSERT_EQ(0, register_image_watch(*ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE,
+ "auto " + stringify(m_watch_ctx->get_handle())));
+
+ m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}};
+
+ ProgressContext progress_context;
+ FlattenTask flatten_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(flatten_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(-ETIMEDOUT, flatten_task.result);
+}
+
diff --git a/src/test/librbd/test_Migration.cc b/src/test/librbd/test_Migration.cc
new file mode 100644
index 000000000..8c0f4b61b
--- /dev/null
+++ b/src/test/librbd/test_Migration.cc
@@ -0,0 +1,1359 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librados/test.h"
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Group.h"
+#include "librbd/api/Image.h"
+#include "librbd/api/Io.h"
+#include "librbd/api/Migration.h"
+#include "librbd/api/Mirror.h"
+#include "librbd/api/Namespace.h"
+#include "librbd/api/Snapshot.h"
+#include "librbd/image/AttachChildRequest.h"
+#include "librbd/image/AttachParentRequest.h"
+#include "librbd/internal.h"
+#include "librbd/io/ReadResult.h"
+#include "common/Cond.h"
+#include <boost/scope_exit.hpp>
+
+void register_test_migration() {
+}
+
+namespace librbd {
+
+struct TestMigration : public TestFixture {
+ static void SetUpTestCase() {
+ TestFixture::SetUpTestCase();
+
+ _other_pool_name = get_temp_pool_name("test-librbd-");
+ ASSERT_EQ(0, _rados.pool_create(_other_pool_name.c_str()));
+ }
+
+ static void TearDownTestCase() {
+ ASSERT_EQ(0, _rados.pool_delete(_other_pool_name.c_str()));
+
+ TestFixture::TearDownTestCase();
+ }
+
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ ASSERT_EQ(0, _rados.ioctx_create(_other_pool_name.c_str(),
+ _other_pool_ioctx));
+
+ open_image(m_ioctx, m_image_name, &m_ictx);
+ m_image_id = m_ictx->id;
+
+ std::string ref_image_name = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, ref_image_name, m_ictx->size));
+ EXPECT_EQ(0, _rados.ioctx_create2(m_ioctx.get_id(), m_ref_ioctx));
+ open_image(m_ref_ioctx, ref_image_name, &m_ref_ictx);
+
+ resize(20 * (1 << 22));
+ }
+
+ void TearDown() override {
+ if (m_ref_ictx != nullptr) {
+ close_image(m_ref_ictx);
+ }
+ if (m_ictx != nullptr) {
+ close_image(m_ictx);
+ }
+
+ _other_pool_ioctx.close();
+
+ TestFixture::TearDown();
+ }
+
+ void compare(const std::string &description = "") {
+ std::vector<librbd::snap_info_t> src_snaps, dst_snaps;
+
+ EXPECT_EQ(m_ref_ictx->size, m_ictx->size);
+ EXPECT_EQ(0, librbd::api::Snapshot<>::list(m_ref_ictx, src_snaps));
+ EXPECT_EQ(0, librbd::api::Snapshot<>::list(m_ictx, dst_snaps));
+ EXPECT_EQ(src_snaps.size(), dst_snaps.size());
+ for (size_t i = 0; i <= src_snaps.size(); i++) {
+ const char *src_snap_name = nullptr;
+ const char *dst_snap_name = nullptr;
+ if (i < src_snaps.size()) {
+ EXPECT_EQ(src_snaps[i].name, dst_snaps[i].name);
+ src_snap_name = src_snaps[i].name.c_str();
+ dst_snap_name = dst_snaps[i].name.c_str();
+ }
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(
+ m_ref_ictx, cls::rbd::UserSnapshotNamespace(),
+ src_snap_name));
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(
+ m_ictx, cls::rbd::UserSnapshotNamespace(),
+ dst_snap_name));
+ compare_snaps(
+ description + " snap: " + (src_snap_name ? src_snap_name : "null"),
+ m_ref_ictx, m_ictx);
+ }
+ }
+
+ void compare_snaps(const std::string &description, librbd::ImageCtx *src_ictx,
+ librbd::ImageCtx *dst_ictx) {
+ uint64_t src_size, dst_size;
+ {
+ std::shared_lock src_locker{src_ictx->image_lock};
+ std::shared_lock dst_locker{dst_ictx->image_lock};
+ src_size = src_ictx->get_image_size(src_ictx->snap_id);
+ dst_size = dst_ictx->get_image_size(dst_ictx->snap_id);
+ }
+ if (src_size != dst_size) {
+ std::cout << description << ": size differs" << std::endl;
+ EXPECT_EQ(src_size, dst_size);
+ }
+
+ if (dst_ictx->test_features(RBD_FEATURE_LAYERING)) {
+ bool flags_set;
+ std::shared_lock dst_locker{dst_ictx->image_lock};
+ EXPECT_EQ(0, dst_ictx->test_flags(dst_ictx->snap_id,
+ RBD_FLAG_OBJECT_MAP_INVALID,
+ dst_ictx->image_lock, &flags_set));
+ EXPECT_FALSE(flags_set);
+ }
+
+ ssize_t read_size = 1 << src_ictx->order;
+ uint64_t offset = 0;
+ while (offset < src_size) {
+ read_size = std::min(read_size, static_cast<ssize_t>(src_size - offset));
+
+ bufferptr src_ptr(read_size);
+ bufferlist src_bl;
+ src_bl.push_back(src_ptr);
+ librbd::io::ReadResult src_result{&src_bl};
+ EXPECT_EQ(read_size, api::Io<>::read(
+ *src_ictx, offset, read_size,
+ librbd::io::ReadResult{src_result}, 0));
+
+ bufferptr dst_ptr(read_size);
+ bufferlist dst_bl;
+ dst_bl.push_back(dst_ptr);
+ librbd::io::ReadResult dst_result{&dst_bl};
+ EXPECT_EQ(read_size, api::Io<>::read(
+ *dst_ictx, offset, read_size,
+ librbd::io::ReadResult{dst_result}, 0));
+
+ if (!src_bl.contents_equal(dst_bl)) {
+ std::cout << description
+ << ", block " << offset << "~" << read_size << " differs"
+ << std::endl;
+ std::cout << "src block: " << src_ictx->id << ": " << std::endl; src_bl.hexdump(std::cout);
+ std::cout << "dst block: " << dst_ictx->id << ": " << std::endl; dst_bl.hexdump(std::cout);
+ }
+ EXPECT_TRUE(src_bl.contents_equal(dst_bl));
+ offset += read_size;
+ }
+ }
+
+ void open_image(librados::IoCtx& io_ctx, const std::string &name,
+ const std::string &id, bool read_only, int flags,
+ librbd::ImageCtx **ictx) {
+ *ictx = new librbd::ImageCtx(name, id, nullptr, io_ctx, read_only);
+ m_ictxs.insert(*ictx);
+
+ ASSERT_EQ(0, (*ictx)->state->open(flags));
+ (*ictx)->discard_granularity_bytes = 0;
+ }
+
+ void open_image(librados::IoCtx& io_ctx, const std::string &name,
+ librbd::ImageCtx **ictx) {
+ open_image(io_ctx, name, "", false, 0, ictx);
+ }
+
+ void migration_prepare(librados::IoCtx& dst_io_ctx,
+ const std::string &dst_name, int r = 0) {
+ std::cout << __func__ << std::endl;
+
+ close_image(m_ictx);
+ m_ictx = nullptr;
+
+ EXPECT_EQ(r, librbd::api::Migration<>::prepare(m_ioctx, m_image_name,
+ dst_io_ctx, dst_name,
+ m_opts));
+ if (r == 0) {
+ open_image(dst_io_ctx, dst_name, &m_ictx);
+ } else {
+ open_image(m_ioctx, m_image_name, &m_ictx);
+ }
+ compare("after prepare");
+ }
+
+ void migration_execute(librados::IoCtx& io_ctx, const std::string &name,
+ int r = 0) {
+ std::cout << __func__ << std::endl;
+
+ librbd::NoOpProgressContext no_op;
+ EXPECT_EQ(r, librbd::api::Migration<>::execute(io_ctx, name, no_op));
+ }
+
+ void migration_abort(librados::IoCtx& io_ctx, const std::string &name,
+ int r = 0) {
+ std::cout << __func__ << std::endl;
+
+ std::string dst_name = m_ictx->name;
+ close_image(m_ictx);
+ m_ictx = nullptr;
+
+ librbd::NoOpProgressContext no_op;
+ EXPECT_EQ(r, librbd::api::Migration<>::abort(io_ctx, name, no_op));
+
+ if (r == 0) {
+ open_image(m_ioctx, m_image_name, &m_ictx);
+ } else {
+ open_image(m_ioctx, dst_name, &m_ictx);
+ }
+
+ compare("after abort");
+ }
+
+ void migration_commit(librados::IoCtx& io_ctx, const std::string &name) {
+ std::cout << __func__ << std::endl;
+
+ librbd::NoOpProgressContext no_op;
+ EXPECT_EQ(0, librbd::api::Migration<>::commit(io_ctx, name, no_op));
+
+ compare("after commit");
+ }
+
+ void migration_status(librbd::image_migration_state_t state) {
+ librbd::image_migration_status_t status;
+ EXPECT_EQ(0, librbd::api::Migration<>::status(m_ioctx, m_image_name,
+ &status));
+ EXPECT_EQ(status.source_pool_id, m_ioctx.get_id());
+ EXPECT_EQ(status.source_pool_namespace, m_ioctx.get_namespace());
+ EXPECT_EQ(status.source_image_name, m_image_name);
+ EXPECT_EQ(status.source_image_id, m_image_id);
+ EXPECT_EQ(status.dest_pool_id, m_ictx->md_ctx.get_id());
+ EXPECT_EQ(status.dest_pool_namespace, m_ictx->md_ctx.get_namespace());
+ EXPECT_EQ(status.dest_image_name, m_ictx->name);
+ EXPECT_EQ(status.dest_image_id, m_ictx->id);
+ EXPECT_EQ(status.state, state);
+ }
+
+ void migrate(librados::IoCtx& dst_io_ctx, const std::string &dst_name) {
+ migration_prepare(dst_io_ctx, dst_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ migration_execute(dst_io_ctx, dst_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(dst_io_ctx, dst_name);
+ }
+
+ void write(uint64_t off, uint64_t len, char c) {
+ std::cout << "write: " << c << " " << off << "~" << len << std::endl;
+
+ bufferlist ref_bl;
+ ref_bl.append(std::string(len, c));
+ ASSERT_EQ(static_cast<ssize_t>(len),
+ api::Io<>::write(*m_ref_ictx, off, len, std::move(ref_bl), 0));
+ bufferlist bl;
+ bl.append(std::string(len, c));
+ ASSERT_EQ(static_cast<ssize_t>(len),
+ api::Io<>::write(*m_ictx, off, len, std::move(bl), 0));
+ }
+
+ void discard(uint64_t off, uint64_t len) {
+ std::cout << "discard: " << off << "~" << len << std::endl;
+
+ ASSERT_EQ(static_cast<ssize_t>(len),
+ api::Io<>::discard(*m_ref_ictx, off, len, false));
+ ASSERT_EQ(static_cast<ssize_t>(len),
+ api::Io<>::discard(*m_ictx, off, len, false));
+ }
+
+ void flush() {
+ ASSERT_EQ(0, TestFixture::flush_writeback_cache(m_ref_ictx));
+ ASSERT_EQ(0, TestFixture::flush_writeback_cache(m_ictx));
+ }
+
+ void snap_create(const std::string &snap_name) {
+ std::cout << "snap_create: " << snap_name << std::endl;
+
+ flush();
+
+ ASSERT_EQ(0, TestFixture::snap_create(*m_ref_ictx, snap_name));
+ ASSERT_EQ(0, TestFixture::snap_create(*m_ictx, snap_name));
+ }
+
+ void snap_protect(const std::string &snap_name) {
+ std::cout << "snap_protect: " << snap_name << std::endl;
+
+ ASSERT_EQ(0, TestFixture::snap_protect(*m_ref_ictx, snap_name));
+ ASSERT_EQ(0, TestFixture::snap_protect(*m_ictx, snap_name));
+ }
+
+ void clone(const std::string &snap_name) {
+ snap_protect(snap_name);
+
+ int order = m_ref_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_ref_ictx, &features));
+
+ std::string ref_clone_name = get_temp_image_name();
+ std::string clone_name = get_temp_image_name();
+
+ std::cout << "clone " << m_ictx->name << " -> " << clone_name
+ << std::endl;
+
+ ASSERT_EQ(0, librbd::clone(m_ref_ictx->md_ctx, m_ref_ictx->name.c_str(),
+ snap_name.c_str(), m_ref_ioctx,
+ ref_clone_name.c_str(), features, &order,
+ m_ref_ictx->stripe_unit,
+ m_ref_ictx->stripe_count));
+
+ ASSERT_EQ(0, librbd::clone(m_ictx->md_ctx, m_ictx->name.c_str(),
+ snap_name.c_str(), m_ioctx,
+ clone_name.c_str(), features, &order,
+ m_ictx->stripe_unit,
+ m_ictx->stripe_count));
+
+ close_image(m_ref_ictx);
+ open_image(m_ref_ioctx, ref_clone_name, &m_ref_ictx);
+
+ close_image(m_ictx);
+ open_image(m_ioctx, clone_name, &m_ictx);
+ m_image_name = m_ictx->name;
+ m_image_id = m_ictx->id;
+ }
+
+ void resize(uint64_t size) {
+ std::cout << "resize: " << size << std::endl;
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, m_ref_ictx->operations->resize(size, true, no_op));
+ ASSERT_EQ(0, m_ictx->operations->resize(size, true, no_op));
+ }
+
+ void test_no_snaps() {
+ uint64_t len = (1 << m_ictx->order) * 2 + 1;
+ write(0 * len, len, '1');
+ write(2 * len, len, '1');
+ flush();
+ }
+
+ void test_snaps() {
+ uint64_t len = (1 << m_ictx->order) * 2 + 1;
+ write(0 * len, len, '1');
+ snap_create("snap1");
+ write(1 * len, len, '1');
+
+ write(0 * len, 1000, 'X');
+ discard(1000 + 10, 1000);
+
+ snap_create("snap2");
+
+ write(1 * len, 1000, 'X');
+ discard(2 * len + 10, 1000);
+
+ uint64_t size = m_ictx->size;
+
+ resize(size << 1);
+
+ write(size - 1, len, '2');
+
+ snap_create("snap3");
+
+ resize(size);
+
+ discard(size - 1, 1);
+
+ flush();
+ }
+
+ void test_clone() {
+ uint64_t len = (1 << m_ictx->order) * 2 + 1;
+ write(0 * len, len, 'X');
+ write(2 * len, len, 'X');
+
+ snap_create("snap");
+ clone("snap");
+
+ write(0, 1000, 'X');
+ discard(1010, 1000);
+
+ snap_create("snap");
+ clone("snap");
+
+ write(1000, 1000, 'X');
+ discard(2010, 1000);
+
+ flush();
+ }
+
+ template <typename L>
+ void test_migrate_parent(uint32_t clone_format, L&& test) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ std::string prev_clone_format;
+ ASSERT_EQ(0, _rados.conf_get("rbd_default_clone_format",
+ prev_clone_format));
+ ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format",
+ stringify(clone_format).c_str()));
+ BOOST_SCOPE_EXIT_TPL(&prev_clone_format) {
+ _rados.conf_set("rbd_default_clone_format", prev_clone_format.c_str());
+ } BOOST_SCOPE_EXIT_END;
+
+ write(0, 10, 'A');
+ snap_create("snap1");
+ snap_protect("snap1");
+
+ int order = m_ictx->order;
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(m_ictx, &features));
+
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ictx->md_ctx, m_ictx->name.c_str(), "snap1",
+ m_ioctx, clone_name.c_str(), features, &order,
+ m_ictx->stripe_unit, m_ictx->stripe_count));
+
+ librbd::ImageCtx *child_ictx;
+ open_image(m_ioctx, clone_name, &child_ictx);
+
+ test(child_ictx);
+
+ ASSERT_EQ(0, child_ictx->state->refresh());
+
+ bufferlist bl;
+ bufferptr ptr(10);
+ bl.push_back(ptr);
+ librbd::io::ReadResult result{&bl};
+ ASSERT_EQ(10, api::Io<>::read(
+ *child_ictx, 0, 10, librbd::io::ReadResult{result}, 0));
+ bufferlist ref_bl;
+ ref_bl.append(std::string(10, 'A'));
+ ASSERT_TRUE(ref_bl.contents_equal(bl));
+ close_image(child_ictx);
+ }
+
+ void test_stress(const std::string &snap_name_prefix = "snap",
+ char start_char = 'A') {
+ uint64_t initial_size = m_ictx->size;
+
+ int nsnaps = 4;
+ const char *c = getenv("TEST_RBD_MIGRATION_STRESS_NSNAPS");
+ if (c != NULL) {
+ std::stringstream ss(c);
+ ASSERT_TRUE(ss >> nsnaps);
+ }
+
+ int nwrites = 4;
+ c = getenv("TEST_RBD_MIGRATION_STRESS_NWRITES");
+ if (c != NULL) {
+ std::stringstream ss(c);
+ ASSERT_TRUE(ss >> nwrites);
+ }
+
+ for (int i = 0; i < nsnaps; i++) {
+ for (int j = 0; j < nwrites; j++) {
+ size_t len = rand() % ((1 << m_ictx->order) * 2);
+ ASSERT_GT(m_ictx->size, len);
+ uint64_t off = std::min(static_cast<uint64_t>(rand() % m_ictx->size),
+ static_cast<uint64_t>(m_ictx->size - len));
+ write(off, len, start_char + i);
+
+ len = rand() % ((1 << m_ictx->order) * 2);
+ ASSERT_GT(m_ictx->size, len);
+ off = std::min(static_cast<uint64_t>(rand() % m_ictx->size),
+ static_cast<uint64_t>(m_ictx->size - len));
+ discard(off, len);
+ }
+
+ std::string snap_name = snap_name_prefix + stringify(i);
+ snap_create(snap_name);
+
+ if (m_ictx->test_features(RBD_FEATURE_LAYERING) &&
+ !m_ictx->test_features(RBD_FEATURE_MIGRATING) &&
+ rand() % 4) {
+ clone(snap_name);
+ }
+
+ if (rand() % 2) {
+ librbd::NoOpProgressContext no_op;
+ uint64_t new_size = initial_size + rand() % m_ictx->size;
+ resize(new_size);
+ ASSERT_EQ(new_size, m_ictx->size);
+ }
+ }
+ flush();
+ }
+
+ void test_stress2(bool concurrent) {
+ test_stress();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ std::thread user([this]() {
+ test_stress("user", 'a');
+ for (int i = 0; i < 5; i++) {
+ uint64_t off = (i + 1) * m_ictx->size / 10;
+ uint64_t len = m_ictx->size / 40;
+ write(off, len, '1' + i);
+
+ off += len / 4;
+ len /= 2;
+ discard(off, len);
+ }
+ flush();
+ });
+
+ if (concurrent) {
+ librados::IoCtx io_ctx;
+ EXPECT_EQ(0, _rados.ioctx_create2(m_ioctx.get_id(), io_ctx));
+ migration_execute(io_ctx, m_image_name);
+ io_ctx.close();
+ user.join();
+ } else {
+ user.join();
+ compare("before execute");
+ migration_execute(m_ioctx, m_image_name);
+ }
+
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+ }
+
+ static std::string _other_pool_name;
+ static librados::IoCtx _other_pool_ioctx;
+
+ std::string m_image_id;
+ librbd::ImageCtx *m_ictx = nullptr;
+ librados::IoCtx m_ref_ioctx;
+ librbd::ImageCtx *m_ref_ictx = nullptr;
+ librbd::ImageOptions m_opts;
+};
+
+std::string TestMigration::_other_pool_name;
+librados::IoCtx TestMigration::_other_pool_ioctx;
+
+TEST_F(TestMigration, Empty)
+{
+ uint64_t features = m_ictx->features ^ RBD_FEATURE_LAYERING;
+ features &= ~RBD_FEATURE_DIRTY_CACHE;
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FEATURES, features));
+
+ migrate(m_ioctx, m_image_name);
+
+ ASSERT_EQ(features, m_ictx->features);
+}
+
+TEST_F(TestMigration, OtherName)
+{
+ std::string name = get_temp_image_name();
+
+ migrate(m_ioctx, name);
+
+ ASSERT_EQ(name, m_ictx->name);
+}
+
+TEST_F(TestMigration, OtherPool)
+{
+ migrate(_other_pool_ioctx, m_image_name);
+
+ ASSERT_EQ(_other_pool_ioctx.get_id(), m_ictx->md_ctx.get_id());
+}
+
+TEST_F(TestMigration, OtherNamespace)
+{
+ ASSERT_EQ(0, librbd::api::Namespace<>::create(_other_pool_ioctx, "ns1"));
+ _other_pool_ioctx.set_namespace("ns1");
+
+ migrate(_other_pool_ioctx, m_image_name);
+
+ ASSERT_EQ(_other_pool_ioctx.get_id(), m_ictx->md_ctx.get_id());
+ ASSERT_EQ(_other_pool_ioctx.get_namespace(), m_ictx->md_ctx.get_namespace());
+ _other_pool_ioctx.set_namespace("");
+}
+
+TEST_F(TestMigration, DataPool)
+{
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL,
+ _other_pool_ioctx.get_pool_name().c_str()));
+
+ migrate(m_ioctx, m_image_name);
+
+ ASSERT_EQ(_other_pool_ioctx.get_id(), m_ictx->data_ctx.get_id());
+}
+
+TEST_F(TestMigration, AbortAfterPrepare)
+{
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ migration_abort(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, AbortAfterFailedPrepare)
+{
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL, "INVALID_POOL"));
+
+ migration_prepare(m_ioctx, m_image_name, -ENOENT);
+
+ // Migration is automatically aborted if prepare failed
+}
+
+TEST_F(TestMigration, AbortAfterExecute)
+{
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_abort(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, OtherPoolAbortAfterExecute)
+{
+ migration_prepare(_other_pool_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ migration_execute(_other_pool_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_abort(_other_pool_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, OtherNamespaceAbortAfterExecute)
+{
+ ASSERT_EQ(0, librbd::api::Namespace<>::create(_other_pool_ioctx, "ns2"));
+ _other_pool_ioctx.set_namespace("ns2");
+
+ migration_prepare(_other_pool_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ migration_execute(_other_pool_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_abort(_other_pool_ioctx, m_image_name);
+
+ _other_pool_ioctx.set_namespace("");
+ ASSERT_EQ(0, librbd::api::Namespace<>::remove(_other_pool_ioctx, "ns2"));
+}
+
+TEST_F(TestMigration, MirroringSamePool)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(
+ m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false));
+ librbd::mirror_image_info_t info;
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+
+ migrate(m_ioctx, m_image_name);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+}
+
+TEST_F(TestMigration, MirroringAbort)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(
+ m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false));
+ librbd::mirror_image_info_t info;
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, info.state);
+
+ migration_abort(m_ioctx, m_image_name);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+}
+
+TEST_F(TestMigration, MirroringOtherPoolDisabled)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(
+ m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false));
+ librbd::mirror_image_info_t info;
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+
+ migrate(_other_pool_ioctx, m_image_name);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, info.state);
+}
+
+TEST_F(TestMigration, MirroringOtherPoolEnabled)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(_other_pool_ioctx,
+ RBD_MIRROR_MODE_IMAGE));
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(
+ m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false));
+ librbd::mirror_image_info_t info;
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+
+ migrate(_other_pool_ioctx, m_image_name);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+}
+
+TEST_F(TestMigration, MirroringPool)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(_other_pool_ioctx,
+ RBD_MIRROR_MODE_POOL));
+ librbd::mirror_image_info_t info;
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, info.state);
+
+ migrate(_other_pool_ioctx, m_image_name);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+}
+
+TEST_F(TestMigration, Group)
+{
+ REQUIRE_FORMAT_V2();
+
+ ASSERT_EQ(0, librbd::api::Group<>::create(m_ioctx, "123"));
+ ASSERT_EQ(0, librbd::api::Group<>::image_add(m_ioctx, "123", m_ioctx,
+ m_image_name.c_str()));
+ librbd::group_info_t info;
+ ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info));
+
+ std::string name = get_temp_image_name();
+
+ migrate(m_ioctx, name);
+
+ ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info));
+ ASSERT_EQ(info.name, "123");
+
+ ASSERT_EQ(0, librbd::api::Group<>::image_remove(m_ioctx, "123", m_ioctx,
+ name.c_str()));
+ ASSERT_EQ(0, librbd::api::Group<>::remove(m_ioctx, "123"));
+}
+
+TEST_F(TestMigration, GroupAbort)
+{
+ REQUIRE_FORMAT_V2();
+
+ ASSERT_EQ(0, librbd::api::Group<>::create(m_ioctx, "123"));
+ ASSERT_EQ(0, librbd::api::Group<>::image_add(m_ioctx, "123", m_ioctx,
+ m_image_name.c_str()));
+ librbd::group_info_t info;
+ ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info));
+
+ std::string name = get_temp_image_name();
+
+ migration_prepare(m_ioctx, name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info));
+ ASSERT_EQ(info.name, "123");
+
+ migration_abort(m_ioctx, m_image_name);
+
+ ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info));
+ ASSERT_EQ(info.name, "123");
+
+ ASSERT_EQ(0, librbd::api::Group<>::image_remove(m_ioctx, "123", m_ioctx,
+ m_image_name.c_str()));
+ ASSERT_EQ(0, librbd::api::Group<>::remove(m_ioctx, "123"));
+}
+
+TEST_F(TestMigration, NoSnaps)
+{
+ test_no_snaps();
+ migrate(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, NoSnapsOtherPool)
+{
+ test_no_snaps();
+
+ test_no_snaps();
+ migrate(_other_pool_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, NoSnapsDataPool)
+{
+ test_no_snaps();
+
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL,
+ _other_pool_ioctx.get_pool_name().c_str()));
+ migrate(m_ioctx, m_image_name);
+
+ EXPECT_EQ(_other_pool_ioctx.get_id(), m_ictx->data_ctx.get_id());
+}
+
+TEST_F(TestMigration, NoSnapsShrinkAfterPrepare)
+{
+ test_no_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ resize(m_ictx->size >> 1);
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, NoSnapsShrinkToZeroBeforePrepare)
+{
+ test_no_snaps();
+ resize(0);
+
+ migrate(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, NoSnapsShrinkToZeroAfterPrepare)
+{
+ test_no_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ resize(0);
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, NoSnapsExpandAfterPrepare)
+{
+ test_no_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ resize(m_ictx->size << 1);
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, NoSnapsSnapAfterPrepare)
+{
+ test_no_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ snap_create("after_prepare_snap");
+ resize(m_ictx->size >> 1);
+ write(0, 1000, '*');
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, Snaps)
+{
+ test_snaps();
+ migrate(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapsOtherPool)
+{
+ test_snaps();
+
+ test_no_snaps();
+ migrate(_other_pool_ioctx, m_image_name);
+
+ EXPECT_EQ(_other_pool_ioctx.get_id(), m_ictx->md_ctx.get_id());
+}
+
+TEST_F(TestMigration, SnapsDataPool)
+{
+ test_snaps();
+
+ ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL,
+ _other_pool_ioctx.get_pool_name().c_str()));
+ migrate(m_ioctx, m_image_name);
+
+ EXPECT_EQ(_other_pool_ioctx.get_id(), m_ictx->data_ctx.get_id());
+}
+
+TEST_F(TestMigration, SnapsShrinkAfterPrepare)
+{
+ test_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ resize(m_ictx->size >> 1);
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapsShrinkToZeroBeforePrepare)
+{
+ test_snaps();
+ resize(0);
+
+ migrate(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapsShrinkToZeroAfterPrepare)
+{
+ test_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ resize(0);
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapsExpandAfterPrepare)
+{
+ test_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ auto size = m_ictx->size;
+ resize(size << 1);
+ write(size, 1000, '*');
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapsExpandAfterPrepare2)
+{
+ auto size = m_ictx->size;
+
+ write(size >> 1, 10, 'X');
+ snap_create("snap1");
+ resize(size >> 1);
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ resize(size);
+ write(size >> 1, 5, 'Y');
+
+ compare("before execute");
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapsSnapAfterPrepare)
+{
+ test_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ auto ictx = new librbd::ImageCtx(m_ictx->name.c_str(), "", "snap3", m_ioctx,
+ false);
+ ASSERT_EQ(0, ictx->state->open(0));
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(
+ m_ref_ictx, cls::rbd::UserSnapshotNamespace(), "snap3"));
+ compare_snaps("opened after prepare snap3", m_ref_ictx, ictx);
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(
+ m_ref_ictx, cls::rbd::UserSnapshotNamespace(), nullptr));
+ EXPECT_EQ(0, ictx->state->close());
+
+ snap_create("after_prepare_snap");
+ resize(m_ictx->size >> 1);
+ write(0, 1000, '*');
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapsSnapExpandAfterPrepare)
+{
+ test_snaps();
+
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ snap_create("after_prepare_snap");
+ auto size = m_ictx->size;
+ resize(size << 1);
+ write(size, 1000, '*');
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, Clone)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ test_clone();
+ migrate(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, CloneParent) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ snap_create("snap");
+
+ librbd::linked_image_spec_t expected_parent_image;
+ expected_parent_image.image_id = m_ictx->id;
+ expected_parent_image.image_name = m_ictx->name;
+
+ auto it = m_ictx->snap_ids.find({cls::rbd::UserSnapshotNamespace{}, "snap"});
+ ASSERT_TRUE(it != m_ictx->snap_ids.end());
+
+ librbd::snap_spec_t expected_parent_snap;
+ expected_parent_snap.id = it->second;
+
+ clone("snap");
+ migration_prepare(m_ioctx, m_image_name);
+
+ librbd::linked_image_spec_t parent_image;
+ librbd::snap_spec_t parent_snap;
+ ASSERT_EQ(0, librbd::api::Image<>::get_parent(m_ictx, &parent_image,
+ &parent_snap));
+ ASSERT_EQ(expected_parent_image.image_id, parent_image.image_id);
+ ASSERT_EQ(expected_parent_image.image_name, parent_image.image_name);
+ ASSERT_EQ(expected_parent_snap.id, parent_snap.id);
+
+ migration_abort(m_ioctx, m_image_name);
+}
+
+
+TEST_F(TestMigration, CloneUpdateAfterPrepare)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ write(0, 10, 'X');
+ snap_create("snap");
+ clone("snap");
+
+ migration_prepare(m_ioctx, m_image_name);
+
+ write(0, 1, 'Y');
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, TriggerAssertSnapcSeq)
+{
+ auto size = m_ictx->size;
+
+ write((size >> 1) + 0, 10, 'A');
+ snap_create("snap1");
+ write((size >> 1) + 1, 10, 'B');
+
+ migration_prepare(m_ioctx, m_image_name);
+
+ // copyup => deep copy (first time)
+ write((size >> 1) + 2, 10, 'C');
+
+ // preserve data before resizing
+ snap_create("snap2");
+
+ // decrease head overlap
+ resize(size >> 1);
+
+ // migrate object => deep copy (second time) => assert_snapc_seq => -ERANGE
+ migration_execute(m_ioctx, m_image_name);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, SnapTrimBeforePrepare)
+{
+ auto size = m_ictx->size;
+
+ write(size >> 1, 10, 'A');
+ snap_create("snap1");
+ resize(size >> 1);
+
+ migration_prepare(m_ioctx, m_image_name);
+
+ resize(size);
+ snap_create("snap3");
+ write(size >> 1, 10, 'B');
+ snap_create("snap4");
+ resize(size >> 1);
+
+ migration_execute(m_ioctx, m_image_name);
+ migration_commit(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, AbortInUseImage) {
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ librbd::NoOpProgressContext no_op;
+ EXPECT_EQ(-EBUSY, librbd::api::Migration<>::abort(m_ioctx, m_ictx->name,
+ no_op));
+}
+
+TEST_F(TestMigration, AbortWithoutSnapshots) {
+ test_no_snaps();
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ test_no_snaps();
+ migration_abort(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, AbortWithSnapshots) {
+ test_snaps();
+ migration_prepare(m_ioctx, m_image_name);
+ migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ test_no_snaps();
+ flush();
+ ASSERT_EQ(0, TestFixture::snap_create(*m_ictx, "dst-only-snap"));
+
+ test_no_snaps();
+
+ migration_abort(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, CloneV1Parent)
+{
+ const uint32_t CLONE_FORMAT = 1;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *) {
+ migrate(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV2Parent)
+{
+ const uint32_t CLONE_FORMAT = 2;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *) {
+ migrate(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV1ParentAbort)
+{
+ const uint32_t CLONE_FORMAT = 1;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *) {
+ migration_prepare(m_ioctx, m_image_name);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV2ParentAbort)
+{
+ const uint32_t CLONE_FORMAT = 2;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *) {
+ migration_prepare(m_ioctx, m_image_name);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV1ParentAbortFixIncompleteChildReattach)
+{
+ const uint32_t CLONE_FORMAT = 1;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) {
+ auto src_image_id = m_ictx->id;
+ migration_prepare(m_ioctx, m_image_name);
+ // Attach the child to both source and destination
+ // to emulate a crash when re-attaching the child
+ librbd::ImageCtx *src_ictx;
+ open_image(m_ioctx, "", src_image_id, false,
+ librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx);
+ C_SaferCond cond;
+ auto req = librbd::image::AttachChildRequest<>::create(
+ child_ictx, src_ictx, src_ictx->snaps[0], nullptr, 0,
+ CLONE_FORMAT, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ close_image(src_ictx);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV1ParentAbortFixParentReattach)
+{
+ const uint32_t CLONE_FORMAT = 1;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) {
+ auto src_image_id = m_ictx->id;
+ migration_prepare(m_ioctx, m_image_name);
+ // Re-attach the child back to the source to emulate a crash
+ // after the parent reattach but before the child reattach
+ librbd::ImageCtx *src_ictx;
+ open_image(m_ioctx, "", src_image_id, false,
+ librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx);
+ C_SaferCond cond;
+ auto req = librbd::image::AttachChildRequest<>::create(
+ child_ictx, src_ictx, src_ictx->snaps[0], m_ictx,
+ m_ictx->snaps[0], CLONE_FORMAT, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ close_image(src_ictx);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV1ParentAbortRelinkNotNeeded)
+{
+ const uint32_t CLONE_FORMAT = 1;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) {
+ auto src_image_id = m_ictx->id;
+ auto parent_spec = child_ictx->parent_md.spec;
+ parent_spec.image_id = m_ictx->id;
+ parent_spec.snap_id = m_ictx->snaps[0];
+ auto parent_overlap = child_ictx->parent_md.overlap;
+ migration_prepare(m_ioctx, m_image_name);
+ // Relink the child back to emulate a crash
+ // before relinking the child
+ C_SaferCond cond;
+ auto req = librbd::image::AttachParentRequest<>::create(
+ *child_ictx, parent_spec, parent_overlap, true, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ librbd::ImageCtx *src_ictx;
+ open_image(m_ioctx, "", src_image_id, false,
+ librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx);
+ C_SaferCond cond1;
+ auto req1 = librbd::image::AttachChildRequest<>::create(
+ child_ictx, src_ictx, src_ictx->snaps[0], m_ictx,
+ m_ictx->snaps[0], CLONE_FORMAT, &cond1);
+ req1->send();
+ ASSERT_EQ(0, cond1.wait());
+ close_image(src_ictx);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV2ParentAbortFixIncompleteChildReattach)
+{
+ const uint32_t CLONE_FORMAT = 2;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) {
+ auto src_image_id = m_ictx->id;
+ migration_prepare(m_ioctx, m_image_name);
+ // Attach the child to both source and destination
+ // to emulate a crash when re-attaching the child
+ librbd::ImageCtx *src_ictx;
+ open_image(m_ioctx, "", src_image_id, false,
+ librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx);
+ C_SaferCond cond;
+ auto req = librbd::image::AttachChildRequest<>::create(
+ child_ictx, src_ictx, src_ictx->snaps[0], nullptr, 0,
+ CLONE_FORMAT, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ close_image(src_ictx);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV2ParentAbortFixParentReattach)
+{
+ const uint32_t CLONE_FORMAT = 2;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) {
+ auto src_image_id = m_ictx->id;
+ migration_prepare(m_ioctx, m_image_name);
+ // Re-attach the child back to the source to emulate a crash
+ // after the parent reattach but before the child reattach
+ librbd::ImageCtx *src_ictx;
+ open_image(m_ioctx, "", src_image_id, false,
+ librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx);
+ C_SaferCond cond;
+ auto req = librbd::image::AttachChildRequest<>::create(
+ child_ictx, src_ictx, src_ictx->snaps[0], m_ictx,
+ m_ictx->snaps[0], CLONE_FORMAT, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ close_image(src_ictx);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, CloneV2ParentAbortRelinkNotNeeded)
+{
+ const uint32_t CLONE_FORMAT = 2;
+ test_migrate_parent(
+ CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) {
+ auto src_image_id = m_ictx->id;
+ auto parent_spec = child_ictx->parent_md.spec;
+ parent_spec.image_id = m_ictx->id;
+ parent_spec.snap_id = m_ictx->snaps[0];
+ auto parent_overlap = child_ictx->parent_md.overlap;
+ migration_prepare(m_ioctx, m_image_name);
+ // Relink the child back to emulate a crash
+ // before relinking the child
+ C_SaferCond cond;
+ auto req = librbd::image::AttachParentRequest<>::create(
+ *child_ictx, parent_spec, parent_overlap, true, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ librbd::ImageCtx *src_ictx;
+ open_image(m_ioctx, "", src_image_id, false,
+ librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx);
+ C_SaferCond cond1;
+ auto req1 = librbd::image::AttachChildRequest<>::create(
+ child_ictx, src_ictx, src_ictx->snaps[0], m_ictx,
+ m_ictx->snaps[0], CLONE_FORMAT, &cond1);
+ req1->send();
+ ASSERT_EQ(0, cond1.wait());
+ close_image(src_ictx);
+ migration_abort(m_ioctx, m_image_name);
+ });
+}
+
+TEST_F(TestMigration, StressNoMigrate)
+{
+ test_stress();
+
+ compare();
+}
+
+TEST_F(TestMigration, Stress)
+{
+ test_stress();
+
+ migrate(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, Stress2)
+{
+ test_stress2(false);
+}
+
+TEST_F(TestMigration, StressLive)
+{
+ test_stress2(true);
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_MirroringWatcher.cc b/src/test/librbd/test_MirroringWatcher.cc
new file mode 100644
index 000000000..6038b5540
--- /dev/null
+++ b/src/test/librbd/test_MirroringWatcher.cc
@@ -0,0 +1,101 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "librbd/MirroringWatcher.h"
+#include "common/Cond.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include <list>
+
+void register_test_mirroring_watcher() {
+}
+
+namespace librbd {
+
+namespace {
+
+struct MockMirroringWatcher : public MirroringWatcher<> {
+ std::string oid;
+
+ MockMirroringWatcher(ImageCtx &image_ctx)
+ : MirroringWatcher<>(image_ctx.md_ctx, image_ctx.op_work_queue) {
+ }
+
+ MOCK_METHOD1(handle_mode_updated, void(cls::rbd::MirrorMode));
+ MOCK_METHOD3(handle_image_updated, void(cls::rbd::MirrorImageState,
+ const std::string &,
+ const std::string &));
+};
+
+} // anonymous namespace
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::Invoke;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMirroringWatcher : public TestFixture {
+public:
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ bufferlist bl;
+ ASSERT_EQ(0, m_ioctx.write_full(RBD_MIRRORING, bl));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ m_image_watcher = new MockMirroringWatcher(*ictx);
+ C_SaferCond ctx;
+ m_image_watcher->register_watch(&ctx);
+ if (ctx.wait() != 0) {
+ delete m_image_watcher;
+ m_image_watcher = nullptr;
+ FAIL();
+ }
+ }
+
+ void TearDown() override {
+ if (m_image_watcher != nullptr) {
+ C_SaferCond ctx;
+ m_image_watcher->unregister_watch(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ delete m_image_watcher;
+ }
+
+ TestFixture::TearDown();
+ }
+
+ MockMirroringWatcher *m_image_watcher = nullptr;
+};
+
+TEST_F(TestMirroringWatcher, ModeUpdated) {
+ EXPECT_CALL(*m_image_watcher,
+ handle_mode_updated(cls::rbd::MIRROR_MODE_DISABLED))
+ .Times(AtLeast(1));
+
+ C_SaferCond ctx;
+ MockMirroringWatcher::notify_mode_updated(
+ m_ioctx, cls::rbd::MIRROR_MODE_DISABLED, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMirroringWatcher, ImageStatusUpdated) {
+ EXPECT_CALL(*m_image_watcher,
+ handle_image_updated(cls::rbd::MIRROR_IMAGE_STATE_ENABLED,
+ StrEq("image id"),
+ StrEq("global image id")))
+ .Times(AtLeast(1));
+
+ C_SaferCond ctx;
+ MockMirroringWatcher::notify_image_updated(
+ m_ioctx, cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "image id",
+ "global image id", &ctx);
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_ObjectMap.cc b/src/test/librbd/test_ObjectMap.cc
new file mode 100644
index 000000000..32d223a1d
--- /dev/null
+++ b/src/test/librbd/test_ObjectMap.cc
@@ -0,0 +1,240 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "common/Cond.h"
+#include "common/Throttle.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include <list>
+#include <boost/accumulators/accumulators.hpp>
+#include <boost/accumulators/statistics/stats.hpp>
+#include <boost/accumulators/statistics/rolling_sum.hpp>
+
+void register_test_object_map() {
+}
+
+class TestObjectMap : public TestFixture {
+public:
+
+ int when_open_object_map(librbd::ImageCtx *ictx) {
+ C_SaferCond ctx;
+ librbd::ObjectMap<> *object_map = new librbd::ObjectMap<>(*ictx, ictx->snap_id);
+ object_map->open(&ctx);
+ int r = ctx.wait();
+ object_map->put();
+
+ return r;
+ }
+};
+
+TEST_F(TestObjectMap, RefreshInvalidatesWhenCorrupt) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ C_SaferCond lock_ctx;
+ {
+ std::unique_lock owner_locker{ictx->owner_lock};
+ ictx->exclusive_lock->try_acquire_lock(&lock_ctx);
+ }
+ ASSERT_EQ(0, lock_ctx.wait());
+
+ std::string oid = librbd::ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP);
+ bufferlist bl;
+ bl.append("corrupt");
+ ASSERT_EQ(0, ictx->md_ctx.write_full(oid, bl));
+
+ ASSERT_EQ(0, when_open_object_map(ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestObjectMap, RefreshInvalidatesWhenTooSmall) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ C_SaferCond lock_ctx;
+ {
+ std::unique_lock owner_locker{ictx->owner_lock};
+ ictx->exclusive_lock->try_acquire_lock(&lock_ctx);
+ }
+ ASSERT_EQ(0, lock_ctx.wait());
+
+ librados::ObjectWriteOperation op;
+ librbd::cls_client::object_map_resize(&op, 0, OBJECT_NONEXISTENT);
+
+ std::string oid = librbd::ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP);
+ ASSERT_EQ(0, ictx->md_ctx.operate(oid, &op));
+
+ ASSERT_EQ(0, when_open_object_map(ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestObjectMap, InvalidateFlagOnDisk) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ C_SaferCond lock_ctx;
+ {
+ std::unique_lock owner_locker{ictx->owner_lock};
+ ictx->exclusive_lock->try_acquire_lock(&lock_ctx);
+ }
+ ASSERT_EQ(0, lock_ctx.wait());
+
+ std::string oid = librbd::ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP);
+ bufferlist bl;
+ bl.append("corrupt");
+ ASSERT_EQ(0, ictx->md_ctx.write_full(oid, bl));
+
+ ASSERT_EQ(0, when_open_object_map(ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestObjectMap, AcquireLockInvalidatesWhenTooSmall) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ librados::ObjectWriteOperation op;
+ librbd::cls_client::object_map_resize(&op, 0, OBJECT_NONEXISTENT);
+
+ std::string oid = librbd::ObjectMap<>::object_map_name(ictx->id, CEPH_NOSNAP);
+ ASSERT_EQ(0, ictx->md_ctx.operate(oid, &op));
+
+ C_SaferCond lock_ctx;
+ {
+ std::unique_lock owner_locker{ictx->owner_lock};
+ ictx->exclusive_lock->try_acquire_lock(&lock_ctx);
+ }
+ ASSERT_EQ(0, lock_ctx.wait());
+
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+
+ // Test the flag is stored on disk
+ ASSERT_EQ(0, ictx->state->refresh());
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+namespace chrono = std::chrono;
+
+TEST_F(TestObjectMap, DISABLED_StressTest) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ uint64_t object_count = cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT;
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, resize(ictx, ictx->layout.object_size * object_count));
+
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ srand(time(NULL) % (unsigned long) -1);
+
+ coarse_mono_time start = coarse_mono_clock::now();
+ chrono::duration<double> last = chrono::duration<double>::zero();
+
+ const int WINDOW_SIZE = 5;
+ typedef boost::accumulators::accumulator_set<
+ double, boost::accumulators::stats<
+ boost::accumulators::tag::rolling_sum> > RollingSum;
+
+ RollingSum time_acc(
+ boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+ RollingSum ios_acc(
+ boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+
+ uint32_t io_threads = 16;
+ uint64_t cur_ios = 0;
+ SimpleThrottle throttle(io_threads, false);
+ for (uint64_t ios = 0; ios < 100000;) {
+ if (throttle.pending_error()) {
+ break;
+ }
+
+ throttle.start_op();
+ uint64_t object_no = (rand() % object_count);
+ auto ctx = new LambdaContext([&throttle, object_no](int r) {
+ ASSERT_EQ(0, r) << "object_no=" << object_no;
+ throttle.end_op(r);
+ });
+
+ std::shared_lock owner_locker{ictx->owner_lock};
+ std::shared_lock image_locker{ictx->image_lock};
+ ASSERT_TRUE(ictx->object_map != nullptr);
+
+ if (!ictx->object_map->aio_update<
+ Context, &Context::complete>(CEPH_NOSNAP, object_no,
+ OBJECT_EXISTS, {}, {}, true,
+ ctx)) {
+ ctx->complete(0);
+ } else {
+ ++cur_ios;
+ ++ios;
+ }
+
+ coarse_mono_time now = coarse_mono_clock::now();
+ chrono::duration<double> elapsed = now - start;
+ if (last == chrono::duration<double>::zero()) {
+ last = elapsed;
+ } else if ((int)elapsed.count() != (int)last.count()) {
+ time_acc((elapsed - last).count());
+ ios_acc(static_cast<double>(cur_ios));
+ cur_ios = 0;
+
+ double time_sum = boost::accumulators::rolling_sum(time_acc);
+ std::cerr << std::setw(5) << (int)elapsed.count() << "\t"
+ << std::setw(8) << (int)ios << "\t"
+ << std::fixed << std::setw(8) << std::setprecision(2)
+ << boost::accumulators::rolling_sum(ios_acc) / time_sum
+ << std::endl;
+ last = elapsed;
+ }
+ }
+
+ ASSERT_EQ(0, throttle.wait_for_ret());
+}
diff --git a/src/test/librbd/test_Operations.cc b/src/test/librbd/test_Operations.cc
new file mode 100644
index 000000000..43921df5b
--- /dev/null
+++ b/src/test/librbd/test_Operations.cc
@@ -0,0 +1,26 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/Operations.h"
+
+void register_test_operations() {
+}
+
+class TestOperations : public TestFixture {
+public:
+
+};
+
+TEST_F(TestOperations, DisableJournalingCorrupt) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, m_ioctx.remove("journal." + ictx->id));
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+}
+
diff --git a/src/test/librbd/test_Trash.cc b/src/test/librbd/test_Trash.cc
new file mode 100644
index 000000000..b47708279
--- /dev/null
+++ b/src/test/librbd/test_Trash.cc
@@ -0,0 +1,108 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/api/Trash.h"
+#include <set>
+#include <vector>
+
+void register_test_trash() {
+}
+
+namespace librbd {
+
+static bool operator==(const trash_image_info_t& lhs,
+ const trash_image_info_t& rhs) {
+ return (lhs.id == rhs.id &&
+ lhs.name == rhs.name &&
+ lhs.source == rhs.source);
+}
+
+static bool operator==(const image_spec_t& lhs,
+ const image_spec_t& rhs) {
+ return (lhs.id == rhs.id && lhs.name == rhs.name);
+}
+
+class TestTrash : public TestFixture {
+public:
+
+ TestTrash() {}
+};
+
+TEST_F(TestTrash, UserRemovingSource) {
+ REQUIRE_FORMAT_V2();
+
+ auto compare_lambda = [](const trash_image_info_t& lhs,
+ const trash_image_info_t& rhs) {
+ if (lhs.id != rhs.id) {
+ return lhs.id < rhs.id;
+ } else if (lhs.name != rhs.name) {
+ return lhs.name < rhs.name;
+ }
+ return lhs.source < rhs.source;
+ };
+ typedef std::set<trash_image_info_t, decltype(compare_lambda)> TrashEntries;
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ auto image_name1 = m_image_name;
+ std::string image_id1;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, image_name1.c_str()));
+ ASSERT_EQ(0, image.get_id(&image_id1));
+ ASSERT_EQ(0, image.close());
+
+ auto image_name2 = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, image_name2, m_image_size));
+
+ std::string image_id2;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, image_name2.c_str()));
+ ASSERT_EQ(0, image.get_id(&image_id2));
+ ASSERT_EQ(0, image.close());
+
+ ASSERT_EQ(0, api::Trash<>::move(m_ioctx, RBD_TRASH_IMAGE_SOURCE_USER,
+ image_name1, image_id1, 0));
+ ASSERT_EQ(0, api::Trash<>::move(m_ioctx, RBD_TRASH_IMAGE_SOURCE_REMOVING,
+ image_name2, image_id2, 0));
+
+ TrashEntries trash_entries{compare_lambda};
+ TrashEntries expected_trash_entries{compare_lambda};
+
+ std::vector<trash_image_info_t> entries;
+ ASSERT_EQ(0, api::Trash<>::list(m_ioctx, entries, true));
+ trash_entries.insert(entries.begin(), entries.end());
+
+ expected_trash_entries = {
+ {.id = image_id1,
+ .name = image_name1,
+ .source = RBD_TRASH_IMAGE_SOURCE_USER},
+ };
+ ASSERT_EQ(expected_trash_entries, trash_entries);
+
+ std::vector<image_spec_t> expected_images = {
+ {.id = image_id2, .name = image_name2}
+ };
+ std::vector<image_spec_t> images;
+ ASSERT_EQ(0, rbd.list2(m_ioctx, &images));
+ ASSERT_EQ(expected_images, images);
+}
+
+TEST_F(TestTrash, RestoreMirroringSource) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ std::string image_id;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str()));
+ ASSERT_EQ(0, image.get_id(&image_id));
+ ASSERT_EQ(0, image.close());
+
+ ASSERT_EQ(0, api::Trash<>::move(m_ioctx, RBD_TRASH_IMAGE_SOURCE_MIRRORING,
+ m_image_name, 0));
+ ASSERT_EQ(0, rbd.trash_restore(m_ioctx, image_id.c_str(),
+ m_image_name.c_str()));
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_fixture.cc b/src/test/librbd/test_fixture.cc
new file mode 100644
index 000000000..9ddebec48
--- /dev/null
+++ b/src/test/librbd/test_fixture.cc
@@ -0,0 +1,165 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "common/Cond.h"
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/stringify.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageState.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Io.h"
+#include "cls/lock/cls_lock_client.h"
+#include "cls/lock/cls_lock_types.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/internal.h"
+#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
+#include <iostream>
+#include <sstream>
+#include <stdlib.h>
+
+std::string TestFixture::_pool_name;
+librados::Rados TestFixture::_rados;
+rados_t TestFixture::_cluster;
+uint64_t TestFixture::_image_number = 0;
+std::string TestFixture::_data_pool;
+
+TestFixture::TestFixture() : m_image_size(0) {
+}
+
+void TestFixture::SetUpTestCase() {
+ ASSERT_EQ("", connect_cluster(&_cluster));
+ _pool_name = get_temp_pool_name("test-librbd-");
+ ASSERT_EQ("", create_one_pool_pp(_pool_name, _rados));
+
+ bool created = false;
+ ASSERT_EQ(0, create_image_data_pool(_rados, _data_pool, &created));
+ if (!_data_pool.empty()) {
+ printf("using image data pool: %s\n", _data_pool.c_str());
+ if (!created) {
+ _data_pool.clear();
+ }
+ }
+}
+
+void TestFixture::TearDownTestCase() {
+ rados_shutdown(_cluster);
+ if (!_data_pool.empty()) {
+ ASSERT_EQ(0, _rados.pool_delete(_data_pool.c_str()));
+ }
+
+ ASSERT_EQ(0, destroy_one_pool_pp(_pool_name, _rados));
+}
+
+std::string TestFixture::get_temp_image_name() {
+ ++_image_number;
+ return "image" + stringify(_image_number);
+}
+
+void TestFixture::SetUp() {
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), m_ioctx));
+ m_cct = reinterpret_cast<CephContext*>(m_ioctx.cct());
+ librados::Rados rados(m_ioctx);
+ rados.conf_set("rbd_persistent_cache_path", ".");
+
+ m_image_name = get_temp_image_name();
+ m_image_size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, m_image_name, m_image_size));
+}
+
+void TestFixture::TearDown() {
+ unlock_image();
+ for (std::set<librbd::ImageCtx *>::iterator iter = m_ictxs.begin();
+ iter != m_ictxs.end(); ++iter) {
+ (*iter)->state->close();
+ }
+ m_ioctx.close();
+}
+
+int TestFixture::open_image(const std::string &image_name,
+ librbd::ImageCtx **ictx) {
+ *ictx = new librbd::ImageCtx(image_name.c_str(), "", nullptr, m_ioctx, false);
+ m_ictxs.insert(*ictx);
+
+ return (*ictx)->state->open(0);
+}
+
+int TestFixture::snap_create(librbd::ImageCtx &ictx,
+ const std::string &snap_name) {
+ librbd::NoOpProgressContext prog_ctx;
+ return ictx.operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ snap_name.c_str(), 0, prog_ctx);
+}
+
+int TestFixture::snap_protect(librbd::ImageCtx &ictx,
+ const std::string &snap_name) {
+ return ictx.operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ snap_name.c_str());
+}
+
+int TestFixture::flatten(librbd::ImageCtx &ictx,
+ librbd::ProgressContext &prog_ctx) {
+ return ictx.operations->flatten(prog_ctx);
+}
+
+int TestFixture::resize(librbd::ImageCtx *ictx, uint64_t size){
+ librbd::NoOpProgressContext prog_ctx;
+ return ictx->operations->resize(size, true, prog_ctx);
+}
+
+void TestFixture::close_image(librbd::ImageCtx *ictx) {
+ m_ictxs.erase(ictx);
+
+ ictx->state->close();
+}
+
+int TestFixture::lock_image(librbd::ImageCtx &ictx, ClsLockType lock_type,
+ const std::string &cookie) {
+ int r = rados::cls::lock::lock(&ictx.md_ctx, ictx.header_oid, RBD_LOCK_NAME,
+ lock_type, cookie, "internal", "", utime_t(),
+ 0);
+ if (r == 0) {
+ m_lock_object = ictx.header_oid;
+ m_lock_cookie = cookie;
+ }
+ return r;
+}
+
+int TestFixture::unlock_image() {
+ int r = 0;
+ if (!m_lock_cookie.empty()) {
+ r = rados::cls::lock::unlock(&m_ioctx, m_lock_object, RBD_LOCK_NAME,
+ m_lock_cookie);
+ m_lock_cookie = "";
+ }
+ return r;
+}
+
+int TestFixture::acquire_exclusive_lock(librbd::ImageCtx &ictx) {
+ int r = librbd::api::Io<>::write(ictx, 0, 0, {}, 0);
+ if (r != 0) {
+ return r;
+ }
+
+ std::shared_lock owner_locker{ictx.owner_lock};
+ ceph_assert(ictx.exclusive_lock != nullptr);
+ return ictx.exclusive_lock->is_lock_owner() ? 0 : -EINVAL;
+}
+
+int TestFixture::flush_writeback_cache(librbd::ImageCtx *image_ctx) {
+ if (image_ctx->test_features(RBD_FEATURE_DIRTY_CACHE)) {
+ // cache exists. Close to flush data
+ C_SaferCond ctx;
+ auto aio_comp = librbd::io::AioCompletion::create_and_start(
+ &ctx, image_ctx, librbd::io::AIO_TYPE_FLUSH);
+ auto req = librbd::io::ImageDispatchSpec::create_flush(
+ *image_ctx, librbd::io::IMAGE_DISPATCH_LAYER_INTERNAL_START, aio_comp,
+ librbd::io::FLUSH_SOURCE_INTERNAL, {});
+ req->send();
+ return ctx.wait();
+ } else {
+ return librbd::api::Io<>::flush(*image_ctx);
+ }
+}
diff --git a/src/test/librbd/test_fixture.h b/src/test/librbd/test_fixture.h
new file mode 100644
index 000000000..d3bd7b812
--- /dev/null
+++ b/src/test/librbd/test_fixture.h
@@ -0,0 +1,60 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/int_types.h"
+#include "include/rados/librados.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/ImageCtx.h"
+#include "gtest/gtest.h"
+#include <set>
+#include <string>
+
+using namespace ceph;
+
+class TestFixture : public ::testing::Test {
+public:
+
+ TestFixture();
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ static std::string get_temp_image_name();
+
+ void SetUp() override;
+ void TearDown() override;
+
+ int open_image(const std::string &image_name, librbd::ImageCtx **ictx);
+ void close_image(librbd::ImageCtx *ictx);
+
+ int snap_create(librbd::ImageCtx &ictx, const std::string &snap_name);
+ int snap_protect(librbd::ImageCtx &ictx, const std::string &snap_name);
+
+ int flatten(librbd::ImageCtx &ictx, librbd::ProgressContext &prog_ctx);
+ int resize(librbd::ImageCtx *ictx, uint64_t size);
+
+ int lock_image(librbd::ImageCtx &ictx, ClsLockType lock_type,
+ const std::string &cookie);
+ int unlock_image();
+
+ int flush_writeback_cache(librbd::ImageCtx *image_ctx);
+
+ int acquire_exclusive_lock(librbd::ImageCtx &ictx);
+
+ static std::string _pool_name;
+ static librados::Rados _rados;
+ static rados_t _cluster;
+ static uint64_t _image_number;
+ static std::string _data_pool;
+
+ CephContext* m_cct = nullptr;
+ librados::IoCtx m_ioctx;
+ librbd::RBD m_rbd;
+
+ std::string m_image_name;
+ uint64_t m_image_size;
+
+ std::set<librbd::ImageCtx *> m_ictxs;
+
+ std::string m_lock_object;
+ std::string m_lock_cookie;
+};
diff --git a/src/test/librbd/test_internal.cc b/src/test/librbd/test_internal.cc
new file mode 100644
index 000000000..75a1985c0
--- /dev/null
+++ b/src/test/librbd/test_internal.cc
@@ -0,0 +1,1868 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/journal/cls_journal_client.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "test/librados/test_cxx.h"
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd/librbd.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageState.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/Operations.h"
+#include "librbd/api/DiffIterate.h"
+#include "librbd/api/Image.h"
+#include "librbd/api/Io.h"
+#include "librbd/api/Migration.h"
+#include "librbd/api/PoolMetadata.h"
+#include "librbd/api/Snapshot.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageRequest.h"
+#include "osdc/Striper.h"
+#include "common/Cond.h"
+#include <boost/scope_exit.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/assign/list_of.hpp>
+#include <utility>
+#include <vector>
+#include "test/librados/crimson_utils.h"
+
+using namespace std;
+
+void register_test_internal() {
+}
+
+namespace librbd {
+
+class TestInternal : public TestFixture {
+public:
+
+ TestInternal() {}
+
+ typedef std::vector<std::pair<std::string, bool> > Snaps;
+
+ void TearDown() override {
+ unlock_image();
+ for (Snaps::iterator iter = m_snaps.begin(); iter != m_snaps.end(); ++iter) {
+ librbd::ImageCtx *ictx;
+ EXPECT_EQ(0, open_image(m_image_name, &ictx));
+ if (iter->second) {
+ EXPECT_EQ(0,
+ ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(),
+ iter->first.c_str()));
+ }
+ EXPECT_EQ(0,
+ ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
+ iter->first.c_str()));
+ }
+
+ TestFixture::TearDown();
+ }
+
+ int create_snapshot(const char *snap_name, bool snap_protect) {
+ librbd::ImageCtx *ictx;
+ int r = open_image(m_image_name, &ictx);
+ if (r < 0) {
+ return r;
+ }
+
+ r = snap_create(*ictx, snap_name);
+ if (r < 0) {
+ return r;
+ }
+
+ m_snaps.push_back(std::make_pair(snap_name, snap_protect));
+ if (snap_protect) {
+ r = ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(), snap_name);
+ if (r < 0) {
+ return r;
+ }
+ }
+ close_image(ictx);
+ return 0;
+ }
+
+ Snaps m_snaps;
+};
+
+class DummyContext : public Context {
+public:
+ void finish(int r) override {
+ }
+};
+
+void generate_random_iomap(librbd::Image &image, int num_objects, int object_size,
+ int max_count, map<uint64_t, uint64_t> &iomap)
+{
+ uint64_t stripe_unit, stripe_count;
+
+ stripe_unit = image.get_stripe_unit();
+ stripe_count = image.get_stripe_count();
+
+ while (max_count-- > 0) {
+ // generate random image offset based on base random object
+ // number and object offset and then map that back to an
+ // object number based on stripe unit and count.
+ uint64_t ono = rand() % num_objects;
+ uint64_t offset = rand() % (object_size - TEST_IO_SIZE);
+ uint64_t imageoff = (ono * object_size) + offset;
+
+ file_layout_t layout;
+ layout.object_size = object_size;
+ layout.stripe_unit = stripe_unit;
+ layout.stripe_count = stripe_count;
+
+ vector<ObjectExtent> ex;
+ Striper::file_to_extents(g_ceph_context, 1, &layout, imageoff, TEST_IO_SIZE, 0, ex);
+
+ // lets not worry if IO spans multiple extents (>1 object). in such
+ // as case we would perform the write multiple times to the same
+ // offset, but we record all objects that would be generated with
+ // this IO. TODO: fix this if such a need is required by your
+ // test.
+ vector<ObjectExtent>::iterator it;
+ map<uint64_t, uint64_t> curr_iomap;
+ for (it = ex.begin(); it != ex.end(); ++it) {
+ if (iomap.find((*it).objectno) != iomap.end()) {
+ break;
+ }
+
+ curr_iomap.insert(make_pair((*it).objectno, imageoff));
+ }
+
+ if (it == ex.end()) {
+ iomap.insert(curr_iomap.begin(), curr_iomap.end());
+ }
+ }
+}
+
+static bool is_sparsify_supported(librados::IoCtx &ioctx,
+ const std::string &oid) {
+ EXPECT_EQ(0, ioctx.create(oid, true));
+ int r = librbd::cls_client::sparsify(&ioctx, oid, 16, true);
+ EXPECT_TRUE(r == 0 || r == -EOPNOTSUPP);
+ ioctx.remove(oid);
+
+ return (r == 0);
+}
+
+static bool is_sparse_read_supported(librados::IoCtx &ioctx,
+ const std::string &oid) {
+ EXPECT_EQ(0, ioctx.create(oid, true));
+ bufferlist inbl;
+ inbl.append(std::string(1, 'X'));
+ EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 1));
+ EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 3));
+
+ std::map<uint64_t, uint64_t> m;
+ bufferlist outbl;
+ int r = ioctx.sparse_read(oid, m, outbl, 4, 0);
+ ioctx.remove(oid);
+
+ int expected_r = 2;
+ std::map<uint64_t, uint64_t> expected_m = {{1, 1}, {3, 1}};
+ bufferlist expected_outbl;
+ expected_outbl.append(std::string(2, 'X'));
+
+ return (r == expected_r && m == expected_m &&
+ outbl.contents_equal(expected_outbl));
+}
+
+TEST_F(TestInternal, OpenByID) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ std::string id = ictx->id;
+ close_image(ictx);
+
+ ictx = new librbd::ImageCtx("", id, nullptr, m_ioctx, true);
+ ASSERT_EQ(0, ictx->state->open(0));
+ ASSERT_EQ(ictx->name, m_image_name);
+ close_image(ictx);
+}
+
+TEST_F(TestInternal, OpenSnapDNE) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ictx = new librbd::ImageCtx(m_image_name, "", "unknown_snap", m_ioctx, true);
+ ASSERT_EQ(-ENOENT, ictx->state->open(librbd::OPEN_FLAG_SKIP_OPEN_PARENT));
+}
+
+TEST_F(TestInternal, IsExclusiveLockOwner) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_FALSE(is_owner);
+
+ C_SaferCond ctx;
+ {
+ std::unique_lock l{ictx->owner_lock};
+ ictx->exclusive_lock->try_acquire_lock(&ctx);
+ }
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_TRUE(is_owner);
+}
+
+TEST_F(TestInternal, ResizeLocksImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, ictx->operations->resize(m_image_size >> 1, true, no_op));
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_TRUE(is_owner);
+}
+
+TEST_F(TestInternal, ResizeFailsToLockImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE, "manually locked"));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(-EROFS, ictx->operations->resize(m_image_size >> 1, true, no_op));
+}
+
+TEST_F(TestInternal, SnapCreateLocksImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ BOOST_SCOPE_EXIT( (ictx) ) {
+ ASSERT_EQ(0,
+ ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+ } BOOST_SCOPE_EXIT_END;
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_TRUE(is_owner);
+}
+
+TEST_F(TestInternal, SnapCreateFailsToLockImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE, "manually locked"));
+
+ ASSERT_EQ(-EROFS, snap_create(*ictx, "snap1"));
+}
+
+TEST_F(TestInternal, SnapRollbackLocksImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ ASSERT_EQ(0, create_snapshot("snap1", false));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, ictx->operations->snap_rollback(cls::rbd::UserSnapshotNamespace(),
+ "snap1",
+ no_op));
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_TRUE(is_owner);
+}
+
+TEST_F(TestInternal, SnapRollbackFailsToLockImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+
+ ASSERT_EQ(0, create_snapshot("snap1", false));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE, "manually locked"));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(-EROFS,
+ ictx->operations->snap_rollback(cls::rbd::UserSnapshotNamespace(),
+ "snap1",
+ no_op));
+}
+
+TEST_F(TestInternal, SnapSetReleasesLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ ASSERT_EQ(0, create_snapshot("snap1", false));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(
+ ictx, cls::rbd::UserSnapshotNamespace(), "snap1"));
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_FALSE(is_owner);
+}
+
+TEST_F(TestInternal, FlattenLocksImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snapshot("snap1", true));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ std::string clone_name = get_temp_image_name();
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx2;
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, ictx2->operations->flatten(no_op));
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx2, &is_owner));
+ ASSERT_TRUE(is_owner);
+}
+
+TEST_F(TestInternal, FlattenFailsToLockImage) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snapshot("snap1", true));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ std::string clone_name = get_temp_image_name();
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ TestInternal *parent = this;
+ librbd::ImageCtx *ictx2 = NULL;
+ BOOST_SCOPE_EXIT( (&m_ioctx) (clone_name) (parent) (&ictx2) ) {
+ if (ictx2 != NULL) {
+ parent->close_image(ictx2);
+ parent->unlock_image();
+ }
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
+ } BOOST_SCOPE_EXIT_END;
+
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+ ASSERT_EQ(0, lock_image(*ictx2, ClsLockType::EXCLUSIVE, "manually locked"));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(-EROFS, ictx2->operations->flatten(no_op));
+}
+
+TEST_F(TestInternal, WriteFailsToLockImageBlocklisted) {
+ SKIP_IF_CRIMSON();
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::Rados blocklist_rados;
+ ASSERT_EQ("", connect_cluster_pp(blocklist_rados));
+
+ librados::IoCtx blocklist_ioctx;
+ ASSERT_EQ(0, blocklist_rados.ioctx_create(_pool_name.c_str(),
+ blocklist_ioctx));
+
+ auto ictx = new librbd::ImageCtx(m_image_name, "", nullptr, blocklist_ioctx,
+ false);
+ ASSERT_EQ(0, ictx->state->open(0));
+
+ std::list<librbd::image_watcher_t> watchers;
+ ASSERT_EQ(0, librbd::list_watchers(ictx, watchers));
+ ASSERT_EQ(1U, watchers.size());
+
+ bool lock_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, blocklist_rados.blocklist_add(watchers.front().addr, 0));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(256, '1'));
+ ASSERT_EQ(-EBLOCKLISTED, api::Io<>::write(*ictx, 0, bl.length(),
+ std::move(bl), 0));
+ ASSERT_EQ(-EBLOCKLISTED, librbd::is_exclusive_lock_owner(ictx, &lock_owner));
+
+ close_image(ictx);
+}
+
+TEST_F(TestInternal, WriteFailsToLockImageBlocklistedWatch) {
+ SKIP_IF_CRIMSON();
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::Rados blocklist_rados;
+ ASSERT_EQ("", connect_cluster_pp(blocklist_rados));
+
+ librados::IoCtx blocklist_ioctx;
+ ASSERT_EQ(0, blocklist_rados.ioctx_create(_pool_name.c_str(),
+ blocklist_ioctx));
+
+ auto ictx = new librbd::ImageCtx(m_image_name, "", nullptr, blocklist_ioctx,
+ false);
+ ASSERT_EQ(0, ictx->state->open(0));
+
+ std::list<librbd::image_watcher_t> watchers;
+ ASSERT_EQ(0, librbd::list_watchers(ictx, watchers));
+ ASSERT_EQ(1U, watchers.size());
+
+ bool lock_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, blocklist_rados.blocklist_add(watchers.front().addr, 0));
+ // let ImageWatcher discover that the watch can't be re-registered to
+ // eliminate the (intended) race in WriteFailsToLockImageBlocklisted
+ while (!ictx->image_watcher->is_blocklisted()) {
+ sleep(1);
+ }
+
+ ceph::bufferlist bl;
+ bl.append(std::string(256, '1'));
+ ASSERT_EQ(-EBLOCKLISTED, api::Io<>::write(*ictx, 0, bl.length(),
+ std::move(bl), 0));
+ ASSERT_EQ(-EBLOCKLISTED, librbd::is_exclusive_lock_owner(ictx, &lock_owner));
+
+ close_image(ictx);
+}
+
+TEST_F(TestInternal, AioWriteRequestsLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE, "manually locked"));
+
+ std::string buffer(256, '1');
+ Context *ctx = new DummyContext();
+ auto c = librbd::io::AioCompletion::create(ctx);
+ c->get();
+
+ bufferlist bl;
+ bl.append(buffer);
+ api::Io<>::aio_write(*ictx, c, 0, buffer.size(), std::move(bl), 0, true);
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_FALSE(is_owner);
+ ASSERT_FALSE(c->is_complete());
+
+ unlock_image();
+ ASSERT_EQ(0, c->wait_for_complete());
+ c->put();
+}
+
+TEST_F(TestInternal, AioDiscardRequestsLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, lock_image(*ictx, ClsLockType::EXCLUSIVE, "manually locked"));
+
+ Context *ctx = new DummyContext();
+ auto c = librbd::io::AioCompletion::create(ctx);
+ c->get();
+ api::Io<>::aio_discard(*ictx, c, 0, 256, false, true);
+
+ bool is_owner;
+ ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
+ ASSERT_FALSE(is_owner);
+ ASSERT_FALSE(c->is_complete());
+
+ unlock_image();
+ ASSERT_EQ(0, c->wait_for_complete());
+ c->put();
+}
+
+TEST_F(TestInternal, CancelAsyncResize) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ C_SaferCond ctx;
+ {
+ std::unique_lock l{ictx->owner_lock};
+ ictx->exclusive_lock->try_acquire_lock(&ctx);
+ }
+
+ ASSERT_EQ(0, ctx.wait());
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ ASSERT_TRUE(ictx->exclusive_lock->is_lock_owner());
+ }
+
+ uint64_t size;
+ ASSERT_EQ(0, librbd::get_size(ictx, &size));
+
+ uint32_t attempts = 0;
+ while (attempts++ < 20 && size > 0) {
+ C_SaferCond ctx;
+ librbd::NoOpProgressContext prog_ctx;
+
+ size -= std::min<uint64_t>(size, 1 << 18);
+ {
+ std::shared_lock l{ictx->owner_lock};
+ ictx->operations->execute_resize(size, true, prog_ctx, &ctx, 0);
+ }
+
+ // try to interrupt the in-progress resize
+ ictx->cancel_async_requests();
+
+ int r = ctx.wait();
+ if (r == -ERESTART) {
+ std::cout << "detected canceled async request" << std::endl;
+ break;
+ }
+ ASSERT_EQ(0, r);
+ }
+}
+
+TEST_F(TestInternal, MultipleResize) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ if (ictx->exclusive_lock != nullptr) {
+ C_SaferCond ctx;
+ {
+ std::unique_lock l{ictx->owner_lock};
+ ictx->exclusive_lock->try_acquire_lock(&ctx);
+ }
+
+ std::shared_lock owner_locker{ictx->owner_lock};
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(ictx->exclusive_lock->is_lock_owner());
+ }
+
+ uint64_t size;
+ ASSERT_EQ(0, librbd::get_size(ictx, &size));
+ uint64_t original_size = size;
+
+ std::vector<C_SaferCond*> contexts;
+
+ uint32_t attempts = 0;
+ librbd::NoOpProgressContext prog_ctx;
+ while (size > 0) {
+ uint64_t new_size = original_size;
+ if (attempts++ % 2 == 0) {
+ size -= std::min<uint64_t>(size, 1 << 18);
+ new_size = size;
+ }
+
+ std::shared_lock l{ictx->owner_lock};
+ contexts.push_back(new C_SaferCond());
+ ictx->operations->execute_resize(new_size, true, prog_ctx, contexts.back(), 0);
+ }
+
+ for (uint32_t i = 0; i < contexts.size(); ++i) {
+ ASSERT_EQ(0, contexts[i]->wait());
+ delete contexts[i];
+ }
+
+ ASSERT_EQ(0, librbd::get_size(ictx, &size));
+ ASSERT_EQ(0U, size);
+}
+
+TEST_F(TestInternal, Metadata) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ map<string, bool> test_confs = boost::assign::map_list_of(
+ "aaaaaaa", false)(
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", false)(
+ "cccccccccccccc", false);
+ map<string, bool>::iterator it = test_confs.begin();
+ int r;
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ r = ictx->operations->metadata_set(it->first, "value1");
+ ASSERT_EQ(0, r);
+ ++it;
+ r = ictx->operations->metadata_set(it->first, "value2");
+ ASSERT_EQ(0, r);
+ ++it;
+ r = ictx->operations->metadata_set(it->first, "value3");
+ ASSERT_EQ(0, r);
+ r = ictx->operations->metadata_set("abcd", "value4");
+ ASSERT_EQ(0, r);
+ r = ictx->operations->metadata_set("xyz", "value5");
+ ASSERT_EQ(0, r);
+ map<string, bufferlist> pairs;
+ r = librbd::metadata_list(ictx, "", 0, &pairs);
+ ASSERT_EQ(0, r);
+
+ uint8_t original_pairs_num = 0;
+ if (ictx->test_features(RBD_FEATURE_DIRTY_CACHE)) {
+ original_pairs_num = 1;
+ }
+ ASSERT_EQ(original_pairs_num + 5, pairs.size());
+ r = ictx->operations->metadata_remove("abcd");
+ ASSERT_EQ(0, r);
+ r = ictx->operations->metadata_remove("xyz");
+ ASSERT_EQ(0, r);
+ pairs.clear();
+ r = librbd::metadata_list(ictx, "", 0, &pairs);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(original_pairs_num + 3, pairs.size());
+ string val;
+ r = librbd::metadata_get(ictx, it->first, &val);
+ ASSERT_EQ(0, r);
+ ASSERT_STREQ(val.c_str(), "value3");
+}
+
+TEST_F(TestInternal, MetadataConfApply) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_EQ(-ENOENT, ictx->operations->metadata_remove("conf_rbd_cache"));
+
+ bool cache = ictx->cache;
+ std::string rbd_conf_cache = cache ? "true" : "false";
+ std::string new_rbd_conf_cache = !cache ? "true" : "false";
+
+ ASSERT_EQ(0, ictx->operations->metadata_set("conf_rbd_cache",
+ new_rbd_conf_cache));
+ ASSERT_EQ(!cache, ictx->cache);
+
+ ASSERT_EQ(0, ictx->operations->metadata_remove("conf_rbd_cache"));
+ ASSERT_EQ(cache, ictx->cache);
+}
+
+TEST_F(TestInternal, SnapshotCopyup)
+{
+ //https://tracker.ceph.com/issues/58263
+ // Clone overlap is WIP
+ SKIP_IF_CRIMSON();
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ bool sparse_read_supported = is_sparse_read_supported(
+ ictx->data_ctx, ictx->get_object_name(10));
+
+ bufferlist bl;
+ bl.append(std::string(256, '1'));
+ ASSERT_EQ(256, api::Io<>::write(*ictx, 0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(256, api::Io<>::write(*ictx, 1024, bl.length(), bufferlist{bl},
+ 0));
+
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0,
+ ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ std::string clone_name = get_temp_image_name();
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx2;
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
+ ASSERT_EQ(0, snap_create(*ictx2, "snap2"));
+
+ ASSERT_EQ(256, api::Io<>::write(*ictx2, 256, bl.length(), bufferlist{bl},
+ 0));
+
+ ASSERT_EQ(0, flush_writeback_cache(ictx2));
+ librados::IoCtx snap_ctx;
+ snap_ctx.dup(ictx2->data_ctx);
+ snap_ctx.snap_set_read(CEPH_SNAPDIR);
+
+ librados::snap_set_t snap_set;
+ ASSERT_EQ(0, snap_ctx.list_snaps(ictx2->get_object_name(0), &snap_set));
+
+ uint64_t copyup_end = ictx2->enable_sparse_copyup ? 1024 + 256 : 1 << order;
+ std::vector< std::pair<uint64_t,uint64_t> > expected_overlap =
+ boost::assign::list_of(
+ std::make_pair(0, 256))(
+ std::make_pair(512, copyup_end - 512));
+ ASSERT_EQ(2U, snap_set.clones.size());
+ ASSERT_NE(CEPH_NOSNAP, snap_set.clones[0].cloneid);
+ ASSERT_EQ(2U, snap_set.clones[0].snaps.size());
+ ASSERT_EQ(expected_overlap, snap_set.clones[0].overlap);
+ ASSERT_EQ(CEPH_NOSNAP, snap_set.clones[1].cloneid);
+
+ bufferptr read_ptr(256);
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ std::list<std::string> snaps = {"snap1", "snap2", ""};
+ librbd::io::ReadResult read_result{&read_bl};
+ for (std::list<std::string>::iterator it = snaps.begin();
+ it != snaps.end(); ++it) {
+ const char *snap_name = it->empty() ? NULL : it->c_str();
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(
+ ictx2, cls::rbd::UserSnapshotNamespace(), snap_name));
+
+ ASSERT_EQ(256,
+ api::Io<>::read(*ictx2, 0, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ ASSERT_EQ(256,
+ api::Io<>::read(*ictx2, 1024, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ ASSERT_EQ(256,
+ api::Io<>::read(*ictx2, 256, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ if (snap_name == NULL) {
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ } else {
+ ASSERT_TRUE(read_bl.is_zero());
+ }
+
+ // verify sparseness was preserved
+ {
+ librados::IoCtx io_ctx;
+ io_ctx.dup(m_ioctx);
+ librados::Rados rados(io_ctx);
+ EXPECT_EQ(0, rados.conf_set("rbd_cache", "false"));
+ EXPECT_EQ(0, rados.conf_set("rbd_sparse_read_threshold_bytes", "256"));
+ auto ictx3 = new librbd::ImageCtx(clone_name, "", snap_name, io_ctx,
+ true);
+ ASSERT_EQ(0, ictx3->state->open(0));
+ BOOST_SCOPE_EXIT(ictx3) {
+ ictx3->state->close();
+ } BOOST_SCOPE_EXIT_END;
+ std::vector<std::pair<uint64_t, uint64_t>> expected_m;
+ bufferlist expected_bl;
+ if (ictx3->enable_sparse_copyup && sparse_read_supported) {
+ if (snap_name == NULL) {
+ expected_m = {{0, 512}, {1024, 256}};
+ expected_bl.append(std::string(256 * 3, '1'));
+ } else {
+ expected_m = {{0, 256}, {1024, 256}};
+ expected_bl.append(std::string(256 * 2, '1'));
+ }
+ } else {
+ expected_m = {{0, 1024 + 256}};
+ if (snap_name == NULL) {
+ expected_bl.append(std::string(256 * 2, '1'));
+ expected_bl.append(std::string(256 * 2, '\0'));
+ expected_bl.append(std::string(256 * 1, '1'));
+ } else {
+ expected_bl.append(std::string(256 * 1, '1'));
+ expected_bl.append(std::string(256 * 3, '\0'));
+ expected_bl.append(std::string(256 * 1, '1'));
+ }
+ }
+ std::vector<std::pair<uint64_t, uint64_t>> read_m;
+ librbd::io::ReadResult sparse_read_result{&read_m, &read_bl};
+ EXPECT_EQ(1024 + 256,
+ api::Io<>::read(*ictx3, 0, 1024 + 256,
+ librbd::io::ReadResult{sparse_read_result}, 0));
+ EXPECT_EQ(expected_m, read_m);
+ EXPECT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+
+ // verify the object map was properly updated
+ if ((ictx2->features & RBD_FEATURE_OBJECT_MAP) != 0) {
+ uint8_t state = OBJECT_EXISTS;
+ if ((ictx2->features & RBD_FEATURE_FAST_DIFF) != 0 &&
+ it != snaps.begin() && snap_name != NULL) {
+ state = OBJECT_EXISTS_CLEAN;
+ }
+
+ librbd::ObjectMap<> *object_map = new librbd::ObjectMap<>(*ictx2, ictx2->snap_id);
+ C_SaferCond ctx;
+ object_map->open(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ std::shared_lock image_locker{ictx2->image_lock};
+ ASSERT_EQ(state, (*object_map)[0]);
+ object_map->put();
+ }
+ }
+}
+
+TEST_F(TestInternal, SnapshotCopyupZeros)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ // create an empty clone
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0,
+ ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ std::string clone_name = get_temp_image_name();
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx2;
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
+
+ bufferlist bl;
+ bl.append(std::string(256, '1'));
+ ASSERT_EQ(256, api::Io<>::write(*ictx2, 256, bl.length(), bufferlist{bl}, 0));
+
+ ASSERT_EQ(0, flush_writeback_cache(ictx2));
+
+ librados::IoCtx snap_ctx;
+ snap_ctx.dup(ictx2->data_ctx);
+ snap_ctx.snap_set_read(CEPH_SNAPDIR);
+
+ librados::snap_set_t snap_set;
+ ASSERT_EQ(0, snap_ctx.list_snaps(ictx2->get_object_name(0), &snap_set));
+
+ // verify that snapshot wasn't affected
+ ASSERT_EQ(1U, snap_set.clones.size());
+ ASSERT_EQ(CEPH_NOSNAP, snap_set.clones[0].cloneid);
+
+ bufferptr read_ptr(256);
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ std::list<std::string> snaps = {"snap1", ""};
+ librbd::io::ReadResult read_result{&read_bl};
+ for (std::list<std::string>::iterator it = snaps.begin();
+ it != snaps.end(); ++it) {
+ const char *snap_name = it->empty() ? NULL : it->c_str();
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(
+ ictx2, cls::rbd::UserSnapshotNamespace(), snap_name));
+
+ ASSERT_EQ(256,
+ api::Io<>::read(*ictx2, 0, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(read_bl.is_zero());
+
+ ASSERT_EQ(256,
+ api::Io<>::read(*ictx2, 256, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ if (snap_name == NULL) {
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ } else {
+ ASSERT_TRUE(read_bl.is_zero());
+ }
+
+ // verify that only HEAD object map was updated
+ if ((ictx2->features & RBD_FEATURE_OBJECT_MAP) != 0) {
+ uint8_t state = OBJECT_EXISTS;
+ if (snap_name != NULL) {
+ state = OBJECT_NONEXISTENT;
+ }
+
+ librbd::ObjectMap<> *object_map = new librbd::ObjectMap<>(*ictx2, ictx2->snap_id);
+ C_SaferCond ctx;
+ object_map->open(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ std::shared_lock image_locker{ictx2->image_lock};
+ ASSERT_EQ(state, (*object_map)[0]);
+ object_map->put();
+ }
+ }
+}
+
+TEST_F(TestInternal, SnapshotCopyupZerosMigration)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ close_image(ictx);
+
+ // migrate an empty image
+ std::string dst_name = get_temp_image_name();
+ librbd::ImageOptions dst_opts;
+ dst_opts.set(RBD_IMAGE_OPTION_FEATURES, features);
+ ASSERT_EQ(0, librbd::api::Migration<>::prepare(m_ioctx, m_image_name,
+ m_ioctx, dst_name,
+ dst_opts));
+
+ librbd::ImageCtx *ictx2;
+ ASSERT_EQ(0, open_image(dst_name, &ictx2));
+
+ ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
+
+ bufferlist bl;
+ bl.append(std::string(256, '1'));
+ ASSERT_EQ(256, api::Io<>::write(*ictx2, 256, bl.length(), bufferlist{bl}, 0));
+
+ librados::IoCtx snap_ctx;
+ snap_ctx.dup(ictx2->data_ctx);
+ snap_ctx.snap_set_read(CEPH_SNAPDIR);
+
+ librados::snap_set_t snap_set;
+ ASSERT_EQ(0, snap_ctx.list_snaps(ictx2->get_object_name(0), &snap_set));
+
+ // verify that snapshot wasn't affected
+ ASSERT_EQ(1U, snap_set.clones.size());
+ ASSERT_EQ(CEPH_NOSNAP, snap_set.clones[0].cloneid);
+
+ bufferptr read_ptr(256);
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ std::list<std::string> snaps = {"snap1", ""};
+ librbd::io::ReadResult read_result{&read_bl};
+ for (std::list<std::string>::iterator it = snaps.begin();
+ it != snaps.end(); ++it) {
+ const char *snap_name = it->empty() ? NULL : it->c_str();
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(
+ ictx2, cls::rbd::UserSnapshotNamespace(), snap_name));
+
+ ASSERT_EQ(256,
+ api::Io<>::read(*ictx2, 0, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(read_bl.is_zero());
+
+ ASSERT_EQ(256,
+ api::Io<>::read(*ictx2, 256, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ if (snap_name == NULL) {
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ } else {
+ ASSERT_TRUE(read_bl.is_zero());
+ }
+
+ // verify that only HEAD object map was updated
+ if ((ictx2->features & RBD_FEATURE_OBJECT_MAP) != 0) {
+ uint8_t state = OBJECT_EXISTS;
+ if (snap_name != NULL) {
+ state = OBJECT_NONEXISTENT;
+ }
+
+ librbd::ObjectMap<> *object_map = new librbd::ObjectMap<>(*ictx2, ictx2->snap_id);
+ C_SaferCond ctx;
+ object_map->open(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ std::shared_lock image_locker{ictx2->image_lock};
+ ASSERT_EQ(state, (*object_map)[0]);
+ object_map->put();
+ }
+ }
+}
+
+TEST_F(TestInternal, ResizeCopyup)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ m_image_name = get_temp_image_name();
+ m_image_size = 1 << 14;
+
+ uint64_t features = 0;
+ ::get_features(&features);
+ int order = 12;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, m_image_name.c_str(), m_image_size,
+ features, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ for (size_t i = 0; i < m_image_size; i += bl.length()) {
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, i, bl.length(), bufferlist{bl}, 0));
+ }
+
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0,
+ ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx2;
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+ ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
+
+ bufferptr read_ptr(bl.length());
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ // verify full / partial object removal properly copyup
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, ictx2->operations->resize(m_image_size - (1 << order) - 32,
+ true, no_op));
+ ASSERT_EQ(0, ictx2->operations->resize(m_image_size - (2 << order) - 32,
+ true, no_op));
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx2,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ {
+ // hide the parent from the snapshot
+ std::unique_lock image_locker{ictx2->image_lock};
+ ictx2->snap_info.begin()->second.parent = librbd::ParentImageInfo();
+ }
+
+ librbd::io::ReadResult read_result{&read_bl};
+ for (size_t i = 2 << order; i < m_image_size; i += bl.length()) {
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::read(*ictx2, i, bl.length(),
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ }
+}
+
+TEST_F(TestInternal, DiscardCopyup)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ CephContext* cct = reinterpret_cast<CephContext*>(_rados.cct());
+ REQUIRE(!cct->_conf.get_val<bool>("rbd_skip_partial_discard"));
+
+ m_image_name = get_temp_image_name();
+ m_image_size = 1 << 14;
+
+ uint64_t features = 0;
+ ::get_features(&features);
+ int order = 12;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, m_image_name.c_str(), m_image_size,
+ features, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ for (size_t i = 0; i < m_image_size; i += bl.length()) {
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, i, bl.length(), bufferlist{bl}, 0));
+ }
+
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0,
+ ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ std::string clone_name = get_temp_image_name();
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), features, &order, 0, 0));
+
+ librbd::ImageCtx *ictx2;
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+ ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
+
+ bufferptr read_ptr(bl.length());
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ ASSERT_EQ(static_cast<int>(m_image_size - 64),
+ api::Io<>::discard(*ictx2, 32, m_image_size - 64, false));
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx2,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ {
+ // hide the parent from the snapshot
+ std::unique_lock image_locker{ictx2->image_lock};
+ ictx2->snap_info.begin()->second.parent = librbd::ParentImageInfo();
+ }
+
+ librbd::io::ReadResult read_result{&read_bl};
+ for (size_t i = 0; i < m_image_size; i += bl.length()) {
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::read(*ictx2, i, bl.length(),
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ }
+}
+
+TEST_F(TestInternal, ImageOptions) {
+ rbd_image_options_t opts1 = NULL, opts2 = NULL;
+ uint64_t uint64_val1 = 10, uint64_val2 = 0;
+ std::string string_val1;
+
+ librbd::image_options_create(&opts1);
+ ASSERT_NE((rbd_image_options_t)NULL, opts1);
+ ASSERT_TRUE(librbd::image_options_is_empty(opts1));
+
+ ASSERT_EQ(-EINVAL, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_FEATURES,
+ &string_val1));
+ ASSERT_EQ(-ENOENT, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_FEATURES,
+ &uint64_val1));
+
+ ASSERT_EQ(-EINVAL, librbd::image_options_set(opts1, RBD_IMAGE_OPTION_FEATURES,
+ string_val1));
+
+ ASSERT_EQ(0, librbd::image_options_set(opts1, RBD_IMAGE_OPTION_FEATURES,
+ uint64_val1));
+ ASSERT_FALSE(librbd::image_options_is_empty(opts1));
+ ASSERT_EQ(0, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_FEATURES,
+ &uint64_val2));
+ ASSERT_EQ(uint64_val1, uint64_val2);
+
+ librbd::image_options_create_ref(&opts2, opts1);
+ ASSERT_NE((rbd_image_options_t)NULL, opts2);
+ ASSERT_FALSE(librbd::image_options_is_empty(opts2));
+
+ uint64_val2 = 0;
+ ASSERT_NE(uint64_val1, uint64_val2);
+ ASSERT_EQ(0, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_FEATURES,
+ &uint64_val2));
+ ASSERT_EQ(uint64_val1, uint64_val2);
+
+ uint64_val2++;
+ ASSERT_NE(uint64_val1, uint64_val2);
+ ASSERT_EQ(-ENOENT, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_ORDER,
+ &uint64_val1));
+ ASSERT_EQ(-ENOENT, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_ORDER,
+ &uint64_val2));
+ ASSERT_EQ(0, librbd::image_options_set(opts2, RBD_IMAGE_OPTION_ORDER,
+ uint64_val2));
+ ASSERT_EQ(0, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_ORDER,
+ &uint64_val1));
+ ASSERT_EQ(0, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_ORDER,
+ &uint64_val2));
+ ASSERT_EQ(uint64_val1, uint64_val2);
+
+ librbd::image_options_destroy(opts1);
+
+ uint64_val2++;
+ ASSERT_NE(uint64_val1, uint64_val2);
+ ASSERT_EQ(0, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_ORDER,
+ &uint64_val2));
+ ASSERT_EQ(uint64_val1, uint64_val2);
+
+ ASSERT_EQ(0, librbd::image_options_unset(opts2, RBD_IMAGE_OPTION_ORDER));
+ ASSERT_EQ(-ENOENT, librbd::image_options_unset(opts2, RBD_IMAGE_OPTION_ORDER));
+
+ librbd::image_options_clear(opts2);
+ ASSERT_EQ(-ENOENT, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_FEATURES,
+ &uint64_val2));
+ ASSERT_TRUE(librbd::image_options_is_empty(opts2));
+
+ librbd::image_options_destroy(opts2);
+}
+
+TEST_F(TestInternal, WriteFullCopyup) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, ictx->operations->resize(1 << ictx->order, true, no_op));
+
+ bufferlist bl;
+ bl.append(std::string(1 << ictx->order, '1'));
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, 0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(0, flush_writeback_cache(ictx));
+
+ ASSERT_EQ(0, create_snapshot("snap1", true));
+
+ std::string clone_name = get_temp_image_name();
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
+ clone_name.c_str(), ictx->features, &order, 0, 0));
+
+ TestInternal *parent = this;
+ librbd::ImageCtx *ictx2 = NULL;
+ BOOST_SCOPE_EXIT( (&m_ioctx) (clone_name) (parent) (&ictx2) ) {
+ if (ictx2 != NULL) {
+ ictx2->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
+ "snap1");
+ parent->close_image(ictx2);
+ }
+
+ librbd::NoOpProgressContext remove_no_op;
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name,
+ remove_no_op));
+ } BOOST_SCOPE_EXIT_END;
+
+ ASSERT_EQ(0, open_image(clone_name, &ictx2));
+ ASSERT_EQ(0, ictx2->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "snap1", 0, no_op));
+
+ bufferlist write_full_bl;
+ write_full_bl.append(std::string(1 << ictx2->order, '2'));
+ ASSERT_EQ((ssize_t)write_full_bl.length(),
+ api::Io<>::write(*ictx2, 0, write_full_bl.length(),
+ bufferlist{write_full_bl}, 0));
+
+ ASSERT_EQ(0, ictx2->operations->flatten(no_op));
+
+ bufferptr read_ptr(bl.length());
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ librbd::io::ReadResult read_result{&read_bl};
+ ASSERT_EQ((ssize_t)read_bl.length(),
+ api::Io<>::read(*ictx2, 0, read_bl.length(),
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(write_full_bl.contents_equal(read_bl));
+
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx2,
+ cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+ ASSERT_EQ((ssize_t)read_bl.length(),
+ api::Io<>::read(*ictx2, 0, read_bl.length(),
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+}
+
+static int iterate_cb(uint64_t off, size_t len, int exists, void *arg)
+{
+ interval_set<uint64_t> *diff = static_cast<interval_set<uint64_t> *>(arg);
+ diff->insert(off, len);
+ return 0;
+}
+
+TEST_F(TestInternal, DiffIterateCloneOverwrite) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ uint64_t size = 20 << 20;
+ int order = 0;
+
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ ASSERT_EQ(4096, image.write(0, 4096, bl));
+
+ interval_set<uint64_t> one;
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, false, false, iterate_cb,
+ (void *)&one));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "one"));
+ ASSERT_EQ(0,
+ ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "one"));
+
+ // Simulate a client that doesn't support deep flatten (old librbd / krbd)
+ // which will copy up the full object from the parent
+ std::string oid = ictx->object_prefix + ".0000000000000000";
+ librados::IoCtx io_ctx;
+ io_ctx.dup(m_ioctx);
+ io_ctx.selfmanaged_snap_set_write_ctx(ictx->snapc.seq, ictx->snaps);
+ ASSERT_EQ(0, io_ctx.write(oid, bl, 4096, 4096));
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx,
+ cls::rbd::UserSnapshotNamespace(),
+ "one"));
+ ASSERT_EQ(0, librbd::api::DiffIterate<>::diff_iterate(
+ ictx, cls::rbd::UserSnapshotNamespace(), nullptr, 0, size, true, false,
+ iterate_cb, (void *)&diff));
+ ASSERT_EQ(one, diff);
+}
+
+TEST_F(TestInternal, TestCoR)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ std::string config_value;
+ ASSERT_EQ(0, _rados.conf_get("rbd_clone_copy_on_read", config_value));
+ if (config_value == "false") {
+ GTEST_SKIP() << "Skipping due to disabled copy-on-read";
+ }
+
+ m_image_name = get_temp_image_name();
+ m_image_size = 4 << 20;
+
+ int order = 12; // smallest object size is 4K
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+
+ ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, m_image_name, m_image_size,
+ features, false, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+
+ librbd::image_info_t info;
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+
+ const int object_num = info.size / info.obj_size;
+ printf("made parent image \"%s\": %ldK (%d * %" PRIu64 "K)\n", m_image_name.c_str(),
+ (unsigned long)m_image_size, object_num, info.obj_size/1024);
+
+ // write something into parent
+ char test_data[TEST_IO_SIZE + 1];
+ for (int i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+
+ // generate a random map which covers every objects with random
+ // offset
+ map<uint64_t, uint64_t> write_tracker;
+ generate_random_iomap(image, object_num, info.obj_size, 100, write_tracker);
+
+ printf("generated random write map:\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr)
+ printf("\t [%-8lu, %-8lu]\n",
+ (unsigned long)itr->first, (unsigned long)itr->second);
+
+ bufferlist bl;
+ bl.append(test_data, TEST_IO_SIZE);
+
+ printf("write data based on random map\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr) {
+ printf("\twrite object-%-4lu\t\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.write(itr->second, TEST_IO_SIZE, bl));
+ }
+
+ ASSERT_EQ(0, image.flush());
+
+ bufferlist readbl;
+ printf("verify written data by reading\n");
+ {
+ map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ printf("\tread object-%-4lu\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ int64_t data_pool_id = image.get_data_pool_id();
+ rados_ioctx_t d_ioctx;
+ ASSERT_EQ(0, rados_wait_for_latest_osdmap(_cluster));
+ ASSERT_EQ(0, rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx));
+
+ std::string block_name_prefix = image.get_block_name_prefix() + ".";
+
+ const char *entry;
+ rados_list_ctx_t list_ctx;
+ set<string> obj_checker;
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (boost::starts_with(entry, block_name_prefix)) {
+ const char *block_name_suffix = entry + block_name_prefix.length();
+ obj_checker.insert(block_name_suffix);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+
+ std::string snapname = "snap";
+ std::string clonename = get_temp_image_name();
+ ASSERT_EQ(0, image.snap_create(snapname.c_str()));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), snapname.c_str()));
+ ASSERT_EQ(0, image.snap_protect(snapname.c_str()));
+ printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name.c_str());
+
+ ASSERT_EQ(0, clone_image_pp(m_rbd, image, m_ioctx, m_image_name.c_str(), snapname.c_str(),
+ m_ioctx, clonename.c_str(), features));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
+ printf("made and opened clone \"%s\"\n", clonename.c_str());
+
+ printf("read from \"child\"\n");
+ {
+ map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ printf("\tread object-%-4lu\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr) {
+ printf("\tread object-%-4lu\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ printf("read again reversely\n");
+ for (map<uint64_t, uint64_t>::iterator itr = --write_tracker.end();
+ itr != write_tracker.begin(); --itr) {
+ printf("\tread object-%-4lu\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ // close child to flush all copy-on-read
+ ASSERT_EQ(0, image.close());
+
+ printf("check whether child image has the same set of objects as parent\n");
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
+ block_name_prefix = image.get_block_name_prefix() + ".";
+
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (boost::starts_with(entry, block_name_prefix)) {
+ const char *block_name_suffix = entry + block_name_prefix.length();
+ set<string>::iterator it = obj_checker.find(block_name_suffix);
+ ASSERT_TRUE(it != obj_checker.end());
+ obj_checker.erase(it);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+ ASSERT_TRUE(obj_checker.empty());
+ ASSERT_EQ(0, image.close());
+
+ rados_ioctx_destroy(d_ioctx);
+}
+
+TEST_F(TestInternal, FlattenNoEmptyObjects)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ m_image_name = get_temp_image_name();
+ m_image_size = 4 << 20;
+
+ int order = 12; // smallest object size is 4K
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+
+ ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, m_image_name, m_image_size,
+ features, false, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+
+ librbd::image_info_t info;
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+
+ const int object_num = info.size / info.obj_size;
+ printf("made parent image \"%s\": %" PRIu64 "K (%d * %" PRIu64 "K)\n",
+ m_image_name.c_str(), m_image_size, object_num, info.obj_size/1024);
+
+ // write something into parent
+ char test_data[TEST_IO_SIZE + 1];
+ for (int i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+
+ // generate a random map which covers every objects with random
+ // offset
+ map<uint64_t, uint64_t> write_tracker;
+ generate_random_iomap(image, object_num, info.obj_size, 100, write_tracker);
+
+ printf("generated random write map:\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr)
+ printf("\t [%-8lu, %-8lu]\n",
+ (unsigned long)itr->first, (unsigned long)itr->second);
+
+ bufferlist bl;
+ bl.append(test_data, TEST_IO_SIZE);
+
+ printf("write data based on random map\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr) {
+ printf("\twrite object-%-4lu\t\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.write(itr->second, TEST_IO_SIZE, bl));
+ }
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+
+ bufferlist readbl;
+ printf("verify written data by reading\n");
+ {
+ map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ printf("\tread object-%-4lu\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ int64_t data_pool_id = image.get_data_pool_id();
+ rados_ioctx_t d_ioctx;
+ ASSERT_EQ(0, rados_wait_for_latest_osdmap(_cluster));
+ ASSERT_EQ(0, rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx));
+
+ std::string block_name_prefix = image.get_block_name_prefix() + ".";
+
+ const char *entry;
+ rados_list_ctx_t list_ctx;
+ set<string> obj_checker;
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (boost::starts_with(entry, block_name_prefix)) {
+ const char *block_name_suffix = entry + block_name_prefix.length();
+ obj_checker.insert(block_name_suffix);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+
+ std::string snapname = "snap";
+ std::string clonename = get_temp_image_name();
+ ASSERT_EQ(0, image.snap_create(snapname.c_str()));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), snapname.c_str()));
+ ASSERT_EQ(0, image.snap_protect(snapname.c_str()));
+ printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name.c_str());
+
+ ASSERT_EQ(0, clone_image_pp(m_rbd, image, m_ioctx, m_image_name.c_str(), snapname.c_str(),
+ m_ioctx, clonename.c_str(), features));
+ ASSERT_EQ(0, image.close());
+
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
+ printf("made and opened clone \"%s\"\n", clonename.c_str());
+
+ printf("flattening clone: \"%s\"\n", clonename.c_str());
+ ASSERT_EQ(0, image.flatten());
+
+ printf("check whether child image has the same set of objects as parent\n");
+ block_name_prefix = image.get_block_name_prefix() + ".";
+
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (boost::starts_with(entry, block_name_prefix)) {
+ const char *block_name_suffix = entry + block_name_prefix.length();
+ set<string>::iterator it = obj_checker.find(block_name_suffix);
+ ASSERT_TRUE(it != obj_checker.end());
+ obj_checker.erase(it);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+ ASSERT_TRUE(obj_checker.empty());
+ ASSERT_EQ(0, image.close());
+
+ rados_ioctx_destroy(d_ioctx);
+}
+
+TEST_F(TestInternal, PoolMetadataConfApply) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::api::PoolMetadata<>::remove(m_ioctx, "conf_rbd_cache");
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ bool cache = ictx->cache;
+ std::string rbd_conf_cache = cache ? "true" : "false";
+ std::string new_rbd_conf_cache = !cache ? "true" : "false";
+
+ ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx, "conf_rbd_cache",
+ new_rbd_conf_cache));
+ ASSERT_EQ(0, ictx->state->refresh());
+ ASSERT_EQ(!cache, ictx->cache);
+
+ ASSERT_EQ(0, ictx->operations->metadata_set("conf_rbd_cache",
+ rbd_conf_cache));
+ ASSERT_EQ(cache, ictx->cache);
+
+ ASSERT_EQ(0, ictx->operations->metadata_remove("conf_rbd_cache"));
+ ASSERT_EQ(!cache, ictx->cache);
+
+ ASSERT_EQ(0, librbd::api::PoolMetadata<>::remove(m_ioctx, "conf_rbd_cache"));
+ ASSERT_EQ(0, ictx->state->refresh());
+ ASSERT_EQ(cache, ictx->cache);
+ close_image(ictx);
+
+ ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx,
+ "conf_rbd_default_order",
+ "17"));
+ ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx,
+ "conf_rbd_journal_order",
+ "13"));
+ std::string image_name = get_temp_image_name();
+ int order = 0;
+ uint64_t features;
+ ASSERT_TRUE(::get_features(&features));
+ ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, image_name, m_image_size,
+ features, false, &order));
+
+ ASSERT_EQ(0, open_image(image_name, &ictx));
+ ASSERT_EQ(ictx->order, 17);
+ ASSERT_EQ(ictx->config.get_val<uint64_t>("rbd_journal_order"), 13U);
+
+ if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
+ uint8_t order;
+ uint8_t splay_width;
+ int64_t pool_id;
+ C_SaferCond cond;
+ cls::journal::client::get_immutable_metadata(m_ioctx, "journal." + ictx->id,
+ &order, &splay_width, &pool_id,
+ &cond);
+ ASSERT_EQ(0, cond.wait());
+ ASSERT_EQ(order, 13);
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx,
+ "conf_rbd_journal_order",
+ "14"));
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ true));
+ ASSERT_EQ(ictx->config.get_val<uint64_t>("rbd_journal_order"), 14U);
+
+ C_SaferCond cond1;
+ cls::journal::client::get_immutable_metadata(m_ioctx, "journal." + ictx->id,
+ &order, &splay_width, &pool_id,
+ &cond1);
+ ASSERT_EQ(0, cond1.wait());
+ ASSERT_EQ(order, 14);
+ }
+
+ ASSERT_EQ(0, librbd::api::PoolMetadata<>::remove(m_ioctx,
+ "conf_rbd_default_order"));
+ ASSERT_EQ(0, librbd::api::PoolMetadata<>::remove(m_ioctx,
+ "conf_rbd_journal_order"));
+}
+
+TEST_F(TestInternal, Sparsify) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ bool sparsify_supported = is_sparsify_supported(ictx->data_ctx,
+ ictx->get_object_name(10));
+ bool sparse_read_supported = is_sparse_read_supported(
+ ictx->data_ctx, ictx->get_object_name(10));
+
+ std::cout << "sparsify_supported=" << sparsify_supported << std::endl;
+ std::cout << "sparse_read_supported=" << sparse_read_supported << std::endl;
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, ictx->operations->resize((1 << ictx->order) * 20, true, no_op));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '\0'));
+
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, 0, bl.length(), bufferlist{bl}, 0));
+
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, (1 << ictx->order) * 1 + 512,
+ bl.length(), bufferlist{bl}, 0));
+
+ bl.append(std::string(4096, '1'));
+ bl.append(std::string(4096, '\0'));
+ bl.append(std::string(4096, '2'));
+ bl.append(std::string(4096 - 1, '\0'));
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, (1 << ictx->order) * 10, bl.length(),
+ bufferlist{bl}, 0));
+
+ bufferlist bl2;
+ bl2.append(std::string(4096 - 1, '\0'));
+ ASSERT_EQ((ssize_t)bl2.length(),
+ api::Io<>::write(*ictx, (1 << ictx->order) * 10 + 4096 * 10,
+ bl2.length(), bufferlist{bl2}, 0));
+
+ ASSERT_EQ(0, flush_writeback_cache(ictx));
+
+ ASSERT_EQ(0, ictx->operations->sparsify(4096, no_op));
+
+ bufferptr read_ptr(bl.length());
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ librbd::io::ReadResult read_result{&read_bl};
+ ASSERT_EQ((ssize_t)read_bl.length(),
+ api::Io<>::read(*ictx, (1 << ictx->order) * 10, read_bl.length(),
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ std::string oid = ictx->get_object_name(0);
+ uint64_t size;
+ ASSERT_EQ(-ENOENT, ictx->data_ctx.stat(oid, &size, NULL));
+
+ oid = ictx->get_object_name(1);
+ ASSERT_EQ(-ENOENT, ictx->data_ctx.stat(oid, &size, NULL));
+
+ oid = ictx->get_object_name(10);
+ std::map<uint64_t, uint64_t> m;
+ std::map<uint64_t, uint64_t> expected_m;
+ auto read_len = bl.length();
+ bl.clear();
+ if (sparsify_supported && sparse_read_supported) {
+ expected_m = {{4096 * 1, 4096}, {4096 * 3, 4096}};
+ bl.append(std::string(4096, '1'));
+ bl.append(std::string(4096, '2'));
+ } else {
+ expected_m = {{0, 4096 * 4}};
+ bl.append(std::string(4096, '\0'));
+ bl.append(std::string(4096, '1'));
+ bl.append(std::string(4096, '\0'));
+ bl.append(std::string(4096, '2'));
+ }
+ read_bl.clear();
+ EXPECT_EQ(static_cast<int>(expected_m.size()),
+ ictx->data_ctx.sparse_read(oid, m, read_bl, read_len, 0));
+ EXPECT_EQ(m, expected_m);
+ EXPECT_TRUE(bl.contents_equal(read_bl));
+}
+
+
+TEST_F(TestInternal, SparsifyClone) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ bool sparsify_supported = is_sparsify_supported(ictx->data_ctx,
+ ictx->get_object_name(10));
+ std::cout << "sparsify_supported=" << sparsify_supported << std::endl;
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(0, ictx->operations->resize((1 << ictx->order) * 10, true, no_op));
+
+ ASSERT_EQ(0, create_snapshot("snap", true));
+ std::string clone_name = get_temp_image_name();
+ int order = ictx->order;
+ ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
+ clone_name.c_str(), ictx->features, &order, 0, 0));
+ close_image(ictx);
+
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ BOOST_SCOPE_EXIT_ALL(this, &ictx, clone_name) {
+ close_image(ictx);
+ librbd::NoOpProgressContext no_op;
+ EXPECT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
+ };
+
+ ASSERT_EQ(0, ictx->operations->resize((1 << ictx->order) * 20, true, no_op));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '\0'));
+
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, 0, bl.length(), bufferlist{bl}, 0));
+
+ bl.append(std::string(4096, '1'));
+ bl.append(std::string(4096, '\0'));
+ bl.append(std::string(4096, '2'));
+ bl.append(std::string(4096, '\0'));
+ ASSERT_EQ((ssize_t)bl.length(),
+ api::Io<>::write(*ictx, (1 << ictx->order) * 10, bl.length(),
+ bufferlist{bl}, 0));
+ ASSERT_EQ(0, flush_writeback_cache(ictx));
+
+ ASSERT_EQ(0, ictx->operations->sparsify(4096, no_op));
+
+ bufferptr read_ptr(bl.length());
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+
+ librbd::io::ReadResult read_result{&read_bl};
+ ASSERT_EQ((ssize_t)read_bl.length(),
+ api::Io<>::read(*ictx, (1 << ictx->order) * 10, read_bl.length(),
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ std::string oid = ictx->get_object_name(0);
+ uint64_t size;
+ ASSERT_EQ(0, ictx->data_ctx.stat(oid, &size, NULL));
+ ASSERT_EQ(0, ictx->data_ctx.read(oid, read_bl, 4096, 0));
+}
+
+TEST_F(TestInternal, MissingDataPool) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ std::string header_oid = ictx->header_oid;
+ close_image(ictx);
+
+ // emulate non-existent data pool
+ int64_t pool_id = 1234;
+ std::string pool_name;
+ int r;
+ while ((r = _rados.pool_reverse_lookup(pool_id, &pool_name)) == 0) {
+ pool_id++;
+ }
+ ASSERT_EQ(r, -ENOENT);
+ bufferlist bl;
+ using ceph::encode;
+ encode(pool_id, bl);
+ ASSERT_EQ(0, m_ioctx.omap_set(header_oid, {{"data_pool_id", bl}}));
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ ASSERT_FALSE(ictx->data_ctx.is_valid());
+ ASSERT_EQ(pool_id, librbd::api::Image<>::get_data_pool_id(ictx));
+
+ librbd::image_info_t info;
+ ASSERT_EQ(0, librbd::info(ictx, info, sizeof(info)));
+
+ vector<librbd::snap_info_t> snaps;
+ EXPECT_EQ(0, librbd::api::Snapshot<>::list(ictx, snaps));
+ EXPECT_EQ(1U, snaps.size());
+ EXPECT_EQ("snap1", snaps[0].name);
+
+ bufferptr read_ptr(256);
+ bufferlist read_bl;
+ read_bl.push_back(read_ptr);
+ librbd::io::ReadResult read_result{&read_bl};
+ ASSERT_EQ(-ENODEV,
+ api::Io<>::read(*ictx, 0, 256,
+ librbd::io::ReadResult{read_result}, 0));
+ ASSERT_EQ(-ENODEV,
+ api::Io<>::write(*ictx, 0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(-ENODEV, api::Io<>::discard(*ictx, 0, 1, 256));
+ ASSERT_EQ(-ENODEV,
+ api::Io<>::write_same(*ictx, 0, bl.length(), bufferlist{bl}, 0));
+ uint64_t mismatch_off;
+ ASSERT_EQ(-ENODEV,
+ api::Io<>::compare_and_write(*ictx, 0, bl.length(),
+ bufferlist{bl}, bufferlist{bl},
+ &mismatch_off, 0));
+ ASSERT_EQ(-ENODEV, api::Io<>::flush(*ictx));
+
+ ASSERT_EQ(-ENODEV, snap_create(*ictx, "snap2"));
+ ASSERT_EQ(0, ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
+ "snap1"));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(-ENODEV, ictx->operations->resize(0, true, no_op));
+
+ close_image(ictx);
+
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, m_image_name, no_op));
+
+ ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, m_image_name, m_image_size));
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc
new file mode 100644
index 000000000..b09b67793
--- /dev/null
+++ b/src/test/librbd/test_librbd.cc
@@ -0,0 +1,12647 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/int_types.h"
+#include "include/rados/librados.h"
+#include "include/rbd_types.h"
+#include "include/rbd/librbd.h"
+#include "include/rbd/librbd.hpp"
+#include "include/event_type.h"
+#include "include/err.h"
+#include "common/ceph_mutex.h"
+#include "json_spirit/json_spirit.h"
+#include "test/librados/crimson_utils.h"
+
+#include "gtest/gtest.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <poll.h>
+#include <time.h>
+#include <unistd.h>
+#include <algorithm>
+#include <chrono>
+#include <condition_variable>
+#include <iostream>
+#include <sstream>
+#include <list>
+#include <set>
+#include <thread>
+#include <vector>
+#include <limits>
+
+#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
+#include "test/librbd/test_support.h"
+#include "common/event_socket.h"
+#include "include/interval_set.h"
+#include "include/stringify.h"
+
+#include <boost/assign/list_of.hpp>
+#include <boost/scope_exit.hpp>
+
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+using namespace std;
+
+using std::chrono::seconds;
+
+#define ASSERT_PASSED0(x) \
+ do { \
+ bool passed = false; \
+ x(&passed); \
+ ASSERT_TRUE(passed); \
+ } while(0)
+
+#define ASSERT_PASSED(x, args...) \
+ do { \
+ bool passed = false; \
+ x(args, &passed); \
+ ASSERT_TRUE(passed); \
+ } while(0)
+
+void register_test_librbd() {
+}
+
+static int get_features(bool *old_format, uint64_t *features)
+{
+ const char *c = getenv("RBD_FEATURES");
+ if (c && strlen(c) > 0) {
+ stringstream ss;
+ ss << c;
+ ss >> *features;
+ if (ss.fail())
+ return -EINVAL;
+ *old_format = false;
+ cout << "using new format!" << std::endl;
+ } else {
+ *old_format = true;
+ *features = 0;
+ cout << "using old format" << std::endl;
+ }
+
+ return 0;
+}
+
+static int create_image_full(rados_ioctx_t ioctx, const char *name,
+ uint64_t size, int *order, int old_format,
+ uint64_t features)
+{
+ if (old_format) {
+ // ensure old-format tests actually use the old format
+ int r = rados_conf_set(rados_ioctx_get_cluster(ioctx),
+ "rbd_default_format", "1");
+ if (r < 0) {
+ return r;
+ }
+ return rbd_create(ioctx, name, size, order);
+ } else if ((features & RBD_FEATURE_STRIPINGV2) != 0) {
+ uint64_t stripe_unit = IMAGE_STRIPE_UNIT;
+ if (*order) {
+ // use a conservative stripe_unit for non default order
+ stripe_unit = (1ull << (*order-1));
+ }
+
+ printf("creating image with stripe unit: %" PRIu64 ", "
+ "stripe count: %" PRIu64 "\n",
+ stripe_unit, IMAGE_STRIPE_COUNT);
+ return rbd_create3(ioctx, name, size, features, order,
+ stripe_unit, IMAGE_STRIPE_COUNT);
+ } else {
+ return rbd_create2(ioctx, name, size, features, order);
+ }
+}
+
+static int clone_image(rados_ioctx_t p_ioctx,
+ rbd_image_t p_image, const char *p_name,
+ const char *p_snap_name, rados_ioctx_t c_ioctx,
+ const char *c_name, uint64_t features, int *c_order)
+{
+ uint64_t stripe_unit, stripe_count;
+
+ int r;
+ r = rbd_get_stripe_unit(p_image, &stripe_unit);
+ if (r != 0) {
+ return r;
+ }
+
+ r = rbd_get_stripe_count(p_image, &stripe_count);
+ if (r != 0) {
+ return r;
+ }
+
+ return rbd_clone2(p_ioctx, p_name, p_snap_name, c_ioctx,
+ c_name, features, c_order, stripe_unit, stripe_count);
+}
+
+
+static int create_image(rados_ioctx_t ioctx, const char *name,
+ uint64_t size, int *order)
+{
+ bool old_format;
+ uint64_t features;
+
+ int r = get_features(&old_format, &features);
+ if (r < 0)
+ return r;
+ return create_image_full(ioctx, name, size, order, old_format, features);
+}
+
+static int create_image_pp(librbd::RBD &rbd,
+ librados::IoCtx &ioctx,
+ const char *name,
+ uint64_t size, int *order) {
+ bool old_format;
+ uint64_t features;
+ int r = get_features(&old_format, &features);
+ if (r < 0)
+ return r;
+ if (old_format) {
+ librados::Rados rados(ioctx);
+ int r = rados.conf_set("rbd_default_format", "1");
+ if (r < 0) {
+ return r;
+ }
+ return rbd.create(ioctx, name, size, order);
+ } else {
+ return rbd.create2(ioctx, name, size, features, order);
+ }
+}
+
+
+
+void simple_write_cb(rbd_completion_t cb, void *arg)
+{
+ printf("write completion cb called!\n");
+}
+
+void simple_read_cb(rbd_completion_t cb, void *arg)
+{
+ printf("read completion cb called!\n");
+}
+
+void aio_write_test_data_and_poll(rbd_image_t image, int fd, const char *test_data,
+ uint64_t off, size_t len, uint32_t iohint, bool *passed)
+{
+ rbd_completion_t comp;
+ uint64_t data = 0x123;
+ rbd_aio_create_completion((void*)&data, (rbd_callback_t) simple_write_cb, &comp);
+ printf("created completion\n");
+ printf("started write\n");
+ if (iohint)
+ rbd_aio_write2(image, off, len, test_data, comp, iohint);
+ else
+ rbd_aio_write(image, off, len, test_data, comp);
+
+ struct pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+
+ ASSERT_EQ(1, poll(&pfd, 1, -1));
+ ASSERT_TRUE(pfd.revents & POLLIN);
+
+ rbd_completion_t comps[1];
+ ASSERT_EQ(1, rbd_poll_io_events(image, comps, 1));
+ uint64_t count;
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(count)),
+ read(fd, &count, sizeof(count)));
+ int r = rbd_aio_get_return_value(comps[0]);
+ ASSERT_TRUE(rbd_aio_is_complete(comps[0]));
+ ASSERT_TRUE(*(uint64_t*)rbd_aio_get_arg(comps[0]) == data);
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(0, r);
+ printf("finished write\n");
+ rbd_aio_release(comps[0]);
+ *passed = true;
+}
+
+void aio_write_test_data(rbd_image_t image, const char *test_data, uint64_t off, size_t len, uint32_t iohint, bool *passed)
+{
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp);
+ printf("created completion\n");
+ if (iohint)
+ rbd_aio_write2(image, off, len, test_data, comp, iohint);
+ else
+ rbd_aio_write(image, off, len, test_data, comp);
+ printf("started write\n");
+ rbd_aio_wait_for_complete(comp);
+ int r = rbd_aio_get_return_value(comp);
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(0, r);
+ printf("finished write\n");
+ rbd_aio_release(comp);
+ *passed = true;
+}
+
+void write_test_data(rbd_image_t image, const char *test_data, uint64_t off, size_t len, uint32_t iohint, bool *passed)
+{
+ ssize_t written;
+ if (iohint)
+ written = rbd_write2(image, off, len, test_data, iohint);
+ else
+ written = rbd_write(image, off, len, test_data);
+ printf("wrote: %d\n", (int) written);
+ ASSERT_EQ(len, static_cast<size_t>(written));
+ *passed = true;
+}
+
+void aio_discard_test_data(rbd_image_t image, uint64_t off, uint64_t len, bool *passed)
+{
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp);
+ rbd_aio_discard(image, off, len, comp);
+ rbd_aio_wait_for_complete(comp);
+ int r = rbd_aio_get_return_value(comp);
+ ASSERT_EQ(0, r);
+ printf("aio discard: %d~%d = %d\n", (int)off, (int)len, (int)r);
+ rbd_aio_release(comp);
+ *passed = true;
+}
+
+void discard_test_data(rbd_image_t image, uint64_t off, size_t len, bool *passed)
+{
+ ssize_t written;
+ written = rbd_discard(image, off, len);
+ printf("discard: %d~%d = %d\n", (int)off, (int)len, (int)written);
+ ASSERT_EQ(len, static_cast<size_t>(written));
+ *passed = true;
+}
+
+void aio_read_test_data_and_poll(rbd_image_t image, int fd, const char *expected,
+ uint64_t off, size_t len, uint32_t iohint, bool *passed)
+{
+ rbd_completion_t comp;
+ char *result = (char *)malloc(len + 1);
+
+ ASSERT_NE(static_cast<char *>(NULL), result);
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ printf("created completion\n");
+ printf("started read\n");
+ if (iohint)
+ rbd_aio_read2(image, off, len, result, comp, iohint);
+ else
+ rbd_aio_read(image, off, len, result, comp);
+
+ struct pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+
+ ASSERT_EQ(1, poll(&pfd, 1, -1));
+ ASSERT_TRUE(pfd.revents & POLLIN);
+
+ rbd_completion_t comps[1];
+ ASSERT_EQ(1, rbd_poll_io_events(image, comps, 1));
+ uint64_t count;
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(count)),
+ read(fd, &count, sizeof(count)));
+
+ int r = rbd_aio_get_return_value(comps[0]);
+ ASSERT_TRUE(rbd_aio_is_complete(comps[0]));
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(len, static_cast<size_t>(r));
+ rbd_aio_release(comps[0]);
+ if (memcmp(result, expected, len)) {
+ printf("read: %s\nexpected: %s\n", result, expected);
+ ASSERT_EQ(0, memcmp(result, expected, len));
+ }
+ free(result);
+ *passed = true;
+}
+
+void aio_read_test_data(rbd_image_t image, const char *expected, uint64_t off, size_t len, uint32_t iohint, bool *passed)
+{
+ rbd_completion_t comp;
+ char *result = (char *)malloc(len + 1);
+
+ ASSERT_NE(static_cast<char *>(NULL), result);
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ printf("created completion\n");
+ if (iohint)
+ rbd_aio_read2(image, off, len, result, comp, iohint);
+ else
+ rbd_aio_read(image, off, len, result, comp);
+ printf("started read\n");
+ rbd_aio_wait_for_complete(comp);
+ int r = rbd_aio_get_return_value(comp);
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(len, static_cast<size_t>(r));
+ rbd_aio_release(comp);
+ if (memcmp(result, expected, len)) {
+ printf("read: %s\nexpected: %s\n", result, expected);
+ ASSERT_EQ(0, memcmp(result, expected, len));
+ }
+ free(result);
+ *passed = true;
+}
+
+void read_test_data(rbd_image_t image, const char *expected, uint64_t off, size_t len, uint32_t iohint, bool *passed)
+{
+ ssize_t read;
+ char *result = (char *)malloc(len + 1);
+
+ ASSERT_NE(static_cast<char *>(NULL), result);
+ if (iohint)
+ read = rbd_read2(image, off, len, result, iohint);
+ else
+ read = rbd_read(image, off, len, result);
+ printf("read: %d\n", (int) read);
+ ASSERT_EQ(len, static_cast<size_t>(read));
+ result[len] = '\0';
+ if (memcmp(result, expected, len)) {
+ printf("read: %s\nexpected: %s\n", result, expected);
+ ASSERT_EQ(0, memcmp(result, expected, len));
+ }
+ free(result);
+ *passed = true;
+}
+
+void aio_writesame_test_data(rbd_image_t image, const char *test_data, uint64_t off, uint64_t len,
+ uint64_t data_len, uint32_t iohint, bool *passed)
+{
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp);
+ printf("created completion\n");
+ int r;
+ r = rbd_aio_writesame(image, off, len, test_data, data_len, comp, iohint);
+ printf("started writesame\n");
+ if (len % data_len) {
+ ASSERT_EQ(-EINVAL, r);
+ printf("expected fail, finished writesame\n");
+ rbd_aio_release(comp);
+ *passed = true;
+ return;
+ }
+
+ rbd_aio_wait_for_complete(comp);
+ r = rbd_aio_get_return_value(comp);
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(0, r);
+ printf("finished writesame\n");
+ rbd_aio_release(comp);
+
+ //verify data
+ printf("to verify the data\n");
+ ssize_t read;
+ char *result = (char *)malloc(data_len+ 1);
+ ASSERT_NE(static_cast<char *>(NULL), result);
+ uint64_t left = len;
+ while (left > 0) {
+ read = rbd_read(image, off, data_len, result);
+ ASSERT_EQ(data_len, static_cast<size_t>(read));
+ result[data_len] = '\0';
+ if (memcmp(result, test_data, data_len)) {
+ printf("read: %d ~ %d\n", (int) off, (int) read);
+ printf("read: %s\nexpected: %s\n", result, test_data);
+ ASSERT_EQ(0, memcmp(result, test_data, data_len));
+ }
+ off += data_len;
+ left -= data_len;
+ }
+ ASSERT_EQ(0U, left);
+ free(result);
+ printf("verified\n");
+
+ *passed = true;
+}
+
+void writesame_test_data(rbd_image_t image, const char *test_data, uint64_t off, uint64_t len,
+ uint64_t data_len, uint32_t iohint, bool *passed)
+{
+ ssize_t written;
+ written = rbd_writesame(image, off, len, test_data, data_len, iohint);
+ if (len % data_len) {
+ ASSERT_EQ(-EINVAL, written);
+ printf("expected fail, finished writesame\n");
+ *passed = true;
+ return;
+ }
+ ASSERT_EQ(len, static_cast<size_t>(written));
+ printf("wrote: %d\n", (int) written);
+
+ //verify data
+ printf("to verify the data\n");
+ ssize_t read;
+ char *result = (char *)malloc(data_len+ 1);
+ ASSERT_NE(static_cast<char *>(NULL), result);
+ uint64_t left = len;
+ while (left > 0) {
+ read = rbd_read(image, off, data_len, result);
+ ASSERT_EQ(data_len, static_cast<size_t>(read));
+ result[data_len] = '\0';
+ if (memcmp(result, test_data, data_len)) {
+ printf("read: %d ~ %d\n", (int) off, (int) read);
+ printf("read: %s\nexpected: %s\n", result, test_data);
+ ASSERT_EQ(0, memcmp(result, test_data, data_len));
+ }
+ off += data_len;
+ left -= data_len;
+ }
+ ASSERT_EQ(0U, left);
+ free(result);
+ printf("verified\n");
+
+ *passed = true;
+}
+
+void aio_compare_and_write_test_data(rbd_image_t image, const char *cmp_data,
+ const char *test_data, uint64_t off,
+ size_t len, uint32_t iohint, bool *passed)
+{
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp);
+ printf("created completion\n");
+
+ uint64_t mismatch_offset;
+ rbd_aio_compare_and_write(image, off, len, cmp_data, test_data, comp, &mismatch_offset, iohint);
+ printf("started aio compare and write\n");
+ rbd_aio_wait_for_complete(comp);
+ int r = rbd_aio_get_return_value(comp);
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(0, r);
+ printf("finished aio compare and write\n");
+ rbd_aio_release(comp);
+ *passed = true;
+}
+
+void compare_and_write_test_data(rbd_image_t image, const char *cmp_data,
+ const char *test_data, uint64_t off, size_t len,
+ uint64_t *mismatch_off, uint32_t iohint, bool *passed)
+{
+ printf("start compare and write\n");
+ ssize_t written;
+ written = rbd_compare_and_write(image, off, len, cmp_data, test_data, mismatch_off, iohint);
+ printf("compare and wrote: %d\n", (int) written);
+ ASSERT_EQ(len, static_cast<size_t>(written));
+ *passed = true;
+}
+
+class TestLibRBD : public ::testing::Test {
+public:
+
+ TestLibRBD() : m_pool_number() {
+ }
+
+ static void SetUpTestCase() {
+ _pool_names.clear();
+ _unique_pool_names.clear();
+ _image_number = 0;
+ ASSERT_EQ("", connect_cluster(&_cluster));
+ ASSERT_EQ("", connect_cluster_pp(_rados));
+
+ create_optional_data_pool();
+ }
+
+ static void TearDownTestCase() {
+ rados_shutdown(_cluster);
+ _rados.wait_for_latest_osdmap();
+ _pool_names.insert(_pool_names.end(), _unique_pool_names.begin(),
+ _unique_pool_names.end());
+ for (size_t i = 1; i < _pool_names.size(); ++i) {
+ ASSERT_EQ(0, _rados.pool_delete(_pool_names[i].c_str()));
+ }
+ if (!_pool_names.empty()) {
+ ASSERT_EQ(0, destroy_one_pool_pp(_pool_names[0], _rados));
+ }
+ }
+
+ void SetUp() override {
+ ASSERT_NE("", m_pool_name = create_pool());
+ }
+
+ bool is_skip_partial_discard_enabled() {
+ std::string value;
+ EXPECT_EQ(0, _rados.conf_get("rbd_skip_partial_discard", value));
+ return value == "true";
+ }
+
+ bool is_skip_partial_discard_enabled(rbd_image_t image) {
+ if (is_skip_partial_discard_enabled()) {
+ rbd_flush(image);
+ uint64_t features;
+ EXPECT_EQ(0, rbd_get_features(image, &features));
+ return !(features & RBD_FEATURE_DIRTY_CACHE);
+ }
+ return false;
+ }
+
+ bool is_skip_partial_discard_enabled(librbd::Image& image) {
+ if (is_skip_partial_discard_enabled()) {
+ image.flush();
+ uint64_t features;
+ EXPECT_EQ(0, image.features(&features));
+ return !(features & RBD_FEATURE_DIRTY_CACHE);
+ }
+ return false;
+ }
+
+ void validate_object_map(rbd_image_t image, bool *passed) {
+ uint64_t flags;
+ ASSERT_EQ(0, rbd_get_flags(image, &flags));
+ *passed = ((flags & RBD_FLAG_OBJECT_MAP_INVALID) == 0);
+ }
+
+ void validate_object_map(librbd::Image &image, bool *passed) {
+ uint64_t flags;
+ ASSERT_EQ(0, image.get_flags(&flags));
+ *passed = ((flags & RBD_FLAG_OBJECT_MAP_INVALID) == 0);
+ }
+
+ static std::string get_temp_image_name() {
+ ++_image_number;
+ return "image" + stringify(_image_number);
+ }
+
+ static void create_optional_data_pool() {
+ bool created = false;
+ std::string data_pool;
+ ASSERT_EQ(0, create_image_data_pool(_rados, data_pool, &created));
+ if (!data_pool.empty()) {
+ printf("using image data pool: %s\n", data_pool.c_str());
+ if (created) {
+ _unique_pool_names.push_back(data_pool);
+ }
+ }
+ }
+
+ std::string create_pool(bool unique = false) {
+ librados::Rados rados;
+ std::string pool_name;
+ if (unique) {
+ pool_name = get_temp_pool_name("test-librbd-");
+ EXPECT_EQ("", create_one_pool_pp(pool_name, rados));
+ _unique_pool_names.push_back(pool_name);
+ } else if (m_pool_number < _pool_names.size()) {
+ pool_name = _pool_names[m_pool_number];
+ } else {
+ pool_name = get_temp_pool_name("test-librbd-");
+ EXPECT_EQ("", create_one_pool_pp(pool_name, rados));
+ _pool_names.push_back(pool_name);
+ }
+ ++m_pool_number;
+ return pool_name;
+ }
+
+ void test_io(rbd_image_t image) {
+ bool skip_discard = is_skip_partial_discard_enabled(image);
+
+ char test_data[TEST_IO_SIZE + 1];
+ char zero_data[TEST_IO_SIZE + 1];
+ char mismatch_data[TEST_IO_SIZE + 1];
+ int i;
+ uint64_t mismatch_offset;
+
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+ memset(zero_data, 0, sizeof(zero_data));
+ memset(mismatch_data, 9, sizeof(mismatch_data));
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, 0);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(compare_and_write_test_data, image, test_data, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, &mismatch_offset, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_compare_and_write_test_data, image, test_data, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, 0);
+
+ // discard 2nd, 4th sections.
+ ASSERT_PASSED(discard_test_data, image, TEST_IO_SIZE, TEST_IO_SIZE);
+ ASSERT_PASSED(aio_discard_test_data, image, TEST_IO_SIZE*3, TEST_IO_SIZE);
+
+ ASSERT_PASSED(read_test_data, image, test_data, 0, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*2,
+ TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE*3, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*4,
+ TEST_IO_SIZE, 0);
+
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, 0);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE + i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE + i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ } else {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ }
+ }
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE,
+ 0);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE,
+ 0);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data,
+ TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data,
+ TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ } else {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ }
+ }
+
+ rbd_image_info_t info;
+ rbd_completion_t comp;
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ // can't read or write starting past end
+ ASSERT_EQ(-EINVAL, rbd_write(image, info.size, 1, test_data));
+ ASSERT_EQ(-EINVAL, rbd_read(image, info.size, 1, test_data));
+ // reading through end returns amount up to end
+ ASSERT_EQ(10, rbd_read(image, info.size - 10, 100, test_data));
+ // writing through end returns amount up to end
+ ASSERT_EQ(10, rbd_write(image, info.size - 10, 100, test_data));
+
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ ASSERT_EQ(0, rbd_aio_write(image, info.size, 1, test_data, comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ ASSERT_EQ(0, rbd_aio_read(image, info.size, 1, test_data, comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+
+ ASSERT_PASSED(write_test_data, image, zero_data, 0, TEST_IO_SIZE,
+ LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ mismatch_offset = 123;
+ ASSERT_EQ(-EILSEQ, rbd_compare_and_write(image, 0, TEST_IO_SIZE,
+ mismatch_data, mismatch_data, &mismatch_offset, 0));
+ ASSERT_EQ(0U, mismatch_offset);
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ mismatch_offset = 123;
+ ASSERT_EQ(0, rbd_aio_compare_and_write(image, 0, TEST_IO_SIZE, mismatch_data,
+ mismatch_data, comp, &mismatch_offset, 0));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EILSEQ, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(0U, mismatch_offset);
+ rbd_aio_release(comp);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ static std::vector<std::string> _pool_names;
+ static std::vector<std::string> _unique_pool_names;
+ static rados_t _cluster;
+ static librados::Rados _rados;
+ static uint64_t _image_number;
+
+ std::string m_pool_name;
+ uint32_t m_pool_number;
+
+};
+
+std::vector<std::string> TestLibRBD::_pool_names;
+std::vector<std::string> TestLibRBD::_unique_pool_names;
+rados_t TestLibRBD::_cluster;
+librados::Rados TestLibRBD::_rados;
+uint64_t TestLibRBD::_image_number = 0;
+
+TEST_F(TestLibRBD, CreateAndStat)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+
+ rbd_image_info_t info;
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ printf("image has size %llu and order %d\n", (unsigned long long) info.size, info.order);
+ ASSERT_EQ(info.size, size);
+ ASSERT_EQ(info.order, order);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, CreateWithSameDataPool)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+
+ rbd_image_t image;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ rbd_image_options_t image_options;
+ rbd_image_options_create(&image_options);
+ BOOST_SCOPE_EXIT( (&image_options) ) {
+ rbd_image_options_destroy(image_options);
+ } BOOST_SCOPE_EXIT_END;
+
+ ASSERT_EQ(0, rbd_image_options_set_uint64(image_options,
+ RBD_IMAGE_OPTION_FEATURES,
+ features));
+ ASSERT_EQ(0, rbd_image_options_set_string(image_options,
+ RBD_IMAGE_OPTION_DATA_POOL,
+ m_pool_name.c_str()));
+
+ ASSERT_EQ(0, rbd_create4(ioctx, name.c_str(), size, image_options));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, CreateAndStatPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::image_info_t info;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+ ASSERT_EQ(info.size, size);
+ ASSERT_EQ(info.order, order);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, GetId)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), 0, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ char id[4096];
+ if (!is_feature_enabled(0)) {
+ // V1 image
+ ASSERT_EQ(-EINVAL, rbd_get_id(image, id, sizeof(id)));
+ } else {
+ ASSERT_EQ(-ERANGE, rbd_get_id(image, id, 0));
+ ASSERT_EQ(0, rbd_get_id(image, id, sizeof(id)));
+ ASSERT_LT(0U, strlen(id));
+
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_open_by_id(ioctx, id, &image, NULL));
+ size_t name_len = 0;
+ ASSERT_EQ(-ERANGE, rbd_get_name(image, NULL, &name_len));
+ ASSERT_EQ(name_len, name.size() + 1);
+ char image_name[name_len];
+ ASSERT_EQ(0, rbd_get_name(image, image_name, &name_len));
+ ASSERT_STREQ(name.c_str(), image_name);
+ }
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, GetIdPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+
+ std::string id;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), 0, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ if (!is_feature_enabled(0)) {
+ // V1 image
+ ASSERT_EQ(-EINVAL, image.get_id(&id));
+ } else {
+ ASSERT_EQ(0, image.get_id(&id));
+ ASSERT_LT(0U, id.size());
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, rbd.open_by_id(ioctx, image, id.c_str(), NULL));
+ std::string image_name;
+ ASSERT_EQ(0, image.get_name(&image_name));
+ ASSERT_EQ(name, image_name);
+ }
+}
+
+TEST_F(TestLibRBD, GetBlockNamePrefix)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), 0, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ char prefix[4096];
+ ASSERT_EQ(-ERANGE, rbd_get_block_name_prefix(image, prefix, 0));
+ ASSERT_EQ(0, rbd_get_block_name_prefix(image, prefix, sizeof(prefix)));
+ ASSERT_LT(0U, strlen(prefix));
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, GetBlockNamePrefixPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), 0, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ ASSERT_LT(0U, image.get_block_name_prefix().size());
+}
+
+TEST_F(TestLibRBD, TestGetCreateTimestamp)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), 0, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ struct timespec timestamp;
+ ASSERT_EQ(0, rbd_get_create_timestamp(image, &timestamp));
+ ASSERT_LT(0, timestamp.tv_sec);
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, GetCreateTimestampPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), 0, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ struct timespec timestamp;
+ ASSERT_EQ(0, image.get_create_timestamp(&timestamp));
+ ASSERT_LT(0, timestamp.tv_sec);
+}
+
+TEST_F(TestLibRBD, OpenAio)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+
+ rbd_image_info_t info;
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_completion_t open_comp;
+ ASSERT_EQ(0, rbd_aio_create_completion(NULL, NULL, &open_comp));
+ ASSERT_EQ(0, rbd_aio_open(ioctx, name.c_str(), &image, NULL, open_comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(open_comp));
+ ASSERT_EQ(1, rbd_aio_is_complete(open_comp));
+ ASSERT_EQ(0, rbd_aio_get_return_value(open_comp));
+ rbd_aio_release(open_comp);
+
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ printf("image has size %llu and order %d\n", (unsigned long long) info.size, info.order);
+ ASSERT_EQ(info.size, size);
+ ASSERT_EQ(info.order, order);
+
+ rbd_completion_t close_comp;
+ ASSERT_EQ(0, rbd_aio_create_completion(NULL, NULL, &close_comp));
+ ASSERT_EQ(0, rbd_aio_close(image, close_comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(close_comp));
+ ASSERT_EQ(1, rbd_aio_is_complete(close_comp));
+ ASSERT_EQ(0, rbd_aio_get_return_value(close_comp));
+ rbd_aio_release(close_comp);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, OpenAioFail)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+
+ std::string name = get_temp_image_name();
+ rbd_image_t image;
+ rbd_completion_t open_comp;
+ ASSERT_EQ(0, rbd_aio_create_completion(NULL, NULL, &open_comp));
+ ASSERT_EQ(0, rbd_aio_open(ioctx, name.c_str(), &image, NULL, open_comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(open_comp));
+ ASSERT_EQ(1, rbd_aio_is_complete(open_comp));
+ ASSERT_EQ(-ENOENT, rbd_aio_get_return_value(open_comp));
+ rbd_aio_release(open_comp);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, OpenAioPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::image_info_t info;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::RBD::AioCompletion *open_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, rbd.aio_open(ioctx, image, name.c_str(), NULL, open_comp));
+ ASSERT_EQ(0, open_comp->wait_for_complete());
+ ASSERT_EQ(1, open_comp->is_complete());
+ ASSERT_EQ(0, open_comp->get_return_value());
+ open_comp->release();
+
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+ ASSERT_EQ(info.size, size);
+ ASSERT_EQ(info.order, order);
+
+ // reopen
+ open_comp = new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, rbd.aio_open(ioctx, image, name.c_str(), NULL, open_comp));
+ ASSERT_EQ(0, open_comp->wait_for_complete());
+ ASSERT_EQ(1, open_comp->is_complete());
+ ASSERT_EQ(0, open_comp->get_return_value());
+ open_comp->release();
+
+ // close
+ librbd::RBD::AioCompletion *close_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_close(close_comp));
+ ASSERT_EQ(0, close_comp->wait_for_complete());
+ ASSERT_EQ(1, close_comp->is_complete());
+ ASSERT_EQ(0, close_comp->get_return_value());
+ close_comp->release();
+
+ // close closed image
+ close_comp = new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(-EINVAL, image.aio_close(close_comp));
+ close_comp->release();
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, OpenAioFailPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ std::string name = get_temp_image_name();
+
+ librbd::RBD::AioCompletion *open_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, rbd.aio_open(ioctx, image, name.c_str(), NULL, open_comp));
+ ASSERT_EQ(0, open_comp->wait_for_complete());
+ ASSERT_EQ(1, open_comp->is_complete());
+ ASSERT_EQ(-ENOENT, open_comp->get_return_value());
+ open_comp->release();
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, ResizeAndStat)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_info_t info;
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_resize(image, size * 4));
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ ASSERT_EQ(info.size, size * 4);
+
+ ASSERT_EQ(0, rbd_resize(image, size / 2));
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ ASSERT_EQ(info.size, size / 2);
+
+ // downsizing without allowing shrink should fail
+ // and image size should not change
+ ASSERT_EQ(-EINVAL, rbd_resize2(image, size / 4, false, NULL, NULL));
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ ASSERT_EQ(info.size, size / 2);
+
+ ASSERT_EQ(0, rbd_resize2(image, size / 4, true, NULL, NULL));
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ ASSERT_EQ(info.size, size / 4);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, ResizeAndStatPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::image_info_t info;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image.resize(size * 4));
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+ ASSERT_EQ(info.size, size * 4);
+
+ ASSERT_EQ(0, image.resize(size / 2));
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+ ASSERT_EQ(info.size, size / 2);
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, UpdateWatchAndResize)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ struct Watcher {
+ rbd_image_t &m_image;
+ std::mutex m_lock;
+ std::condition_variable m_cond;
+ size_t m_size = 0;
+ static void cb(void *arg) {
+ Watcher *watcher = static_cast<Watcher *>(arg);
+ watcher->handle_notify();
+ }
+ explicit Watcher(rbd_image_t &image) : m_image(image) {}
+ void handle_notify() {
+ rbd_image_info_t info;
+ ASSERT_EQ(0, rbd_stat(m_image, &info, sizeof(info)));
+ std::lock_guard<std::mutex> locker(m_lock);
+ m_size = info.size;
+ m_cond.notify_one();
+ }
+ void wait_for_size(size_t size) {
+ std::unique_lock<std::mutex> locker(m_lock);
+ ASSERT_TRUE(m_cond.wait_for(locker, seconds(5),
+ [size, this] {
+ return this->m_size == size;}));
+ }
+ } watcher(image);
+ uint64_t handle;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_update_watch(image, &handle, Watcher::cb, &watcher));
+
+ ASSERT_EQ(0, rbd_resize(image, size * 4));
+ watcher.wait_for_size(size * 4);
+
+ ASSERT_EQ(0, rbd_resize(image, size / 2));
+ watcher.wait_for_size(size / 2);
+
+ ASSERT_EQ(0, rbd_update_unwatch(image, handle));
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, UpdateWatchAndResizePP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ struct Watcher : public librbd::UpdateWatchCtx {
+ explicit Watcher(librbd::Image &image) : m_image(image) {
+ }
+ void handle_notify() override {
+ librbd::image_info_t info;
+ ASSERT_EQ(0, m_image.stat(info, sizeof(info)));
+ std::lock_guard<std::mutex> locker(m_lock);
+ m_size = info.size;
+ m_cond.notify_one();
+ }
+ void wait_for_size(size_t size) {
+ std::unique_lock<std::mutex> locker(m_lock);
+ ASSERT_TRUE(m_cond.wait_for(locker, seconds(5),
+ [size, this] {
+ return this->m_size == size;}));
+ }
+ librbd::Image &m_image;
+ std::mutex m_lock;
+ std::condition_variable m_cond;
+ size_t m_size = 0;
+ } watcher(image);
+ uint64_t handle;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image.update_watch(&watcher, &handle));
+
+ ASSERT_EQ(0, image.resize(size * 4));
+ watcher.wait_for_size(size * 4);
+
+ ASSERT_EQ(0, image.resize(size / 2));
+ watcher.wait_for_size(size / 2);
+
+ ASSERT_EQ(0, image.update_unwatch(handle));
+ }
+
+ ioctx.close();
+}
+
+int test_ls(rados_ioctx_t io_ctx, size_t num_expected, ...)
+{
+ int num_images, i;
+ char *names, *cur_name;
+ va_list ap;
+ size_t max_size = 1024;
+
+ names = (char *) malloc(sizeof(char) * 1024);
+ int len = rbd_list(io_ctx, names, &max_size);
+
+ std::set<std::string> image_names;
+ for (i = 0, num_images = 0, cur_name = names; cur_name < names + len; i++) {
+ printf("image: %s\n", cur_name);
+ image_names.insert(cur_name);
+ cur_name += strlen(cur_name) + 1;
+ num_images++;
+ }
+ free(names);
+
+ va_start(ap, num_expected);
+ for (i = num_expected; i > 0; i--) {
+ char *expected = va_arg(ap, char *);
+ printf("expected = %s\n", expected);
+ std::set<std::string>::iterator it = image_names.find(expected);
+ if (it != image_names.end()) {
+ printf("found %s\n", expected);
+ image_names.erase(it);
+ printf("erased %s\n", expected);
+ } else {
+ ADD_FAILURE() << "Unable to find image " << expected;
+ va_end(ap);
+ return -ENOENT;
+ }
+ }
+ va_end(ap);
+
+ if (!image_names.empty()) {
+ ADD_FAILURE() << "Unexpected images discovered";
+ return -EINVAL;
+ }
+ return num_images;
+}
+
+TEST_F(TestLibRBD, TestCreateLsDelete)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, create_pool(true).c_str(), &ioctx);
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, test_ls(ioctx, 0));
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(1, test_ls(ioctx, 1, name.c_str()));
+ ASSERT_EQ(0, create_image(ioctx, name2.c_str(), size, &order));
+ ASSERT_EQ(2, test_ls(ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd_remove(ioctx, name.c_str()));
+ ASSERT_EQ(1, test_ls(ioctx, 1, name2.c_str()));
+
+ ASSERT_EQ(-ENOENT, rbd_remove(ioctx, name.c_str()));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+int test_ls_pp(librbd::RBD& rbd, librados::IoCtx& io_ctx, size_t num_expected, ...)
+{
+ int r;
+ size_t i;
+ va_list ap;
+ vector<string> names;
+ r = rbd.list(io_ctx, names);
+ if (r == -ENOENT)
+ r = 0;
+ EXPECT_TRUE(r >= 0);
+ cout << "num images is: " << names.size() << std::endl
+ << "expected: " << num_expected << std::endl;
+ int num = names.size();
+
+ for (i = 0; i < names.size(); i++) {
+ cout << "image: " << names[i] << std::endl;
+ }
+
+ va_start(ap, num_expected);
+ for (i = num_expected; i > 0; i--) {
+ char *expected = va_arg(ap, char *);
+ cout << "expected = " << expected << std::endl;
+ vector<string>::iterator listed_name = find(names.begin(), names.end(), string(expected));
+ if (listed_name == names.end()) {
+ ADD_FAILURE() << "Unable to find image " << expected;
+ va_end(ap);
+ return -ENOENT;
+ }
+ names.erase(listed_name);
+ }
+ va_end(ap);
+
+ if (!names.empty()) {
+ ADD_FAILURE() << "Unexpected images discovered";
+ return -EINVAL;
+ }
+ return num;
+}
+
+TEST_F(TestLibRBD, TestCreateLsDeletePP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name.c_str()));
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name2.c_str(), size, &order));
+ ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd.remove(ioctx, name.c_str()));
+ ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name2.c_str()));
+ }
+
+ ioctx.close();
+}
+
+
+static int print_progress_percent(uint64_t offset, uint64_t src_size,
+ void *data)
+{
+ float percent = ((float)offset * 100) / src_size;
+ printf("%3.2f%% done\n", percent);
+ return 0;
+}
+
+TEST_F(TestLibRBD, TestCopy)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, create_pool(true).c_str(), &ioctx);
+
+ rbd_image_t image;
+ rbd_image_t image2;
+ rbd_image_t image3;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ std::string name3 = get_temp_image_name();
+
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(1, test_ls(ioctx, 1, name.c_str()));
+
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(image, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
+ ASSERT_EQ(0, rbd_copy(image, ioctx, name2.c_str()));
+ ASSERT_EQ(2, test_ls(ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name2.c_str(), &image2, NULL));
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image2, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_copy_with_progress(image, ioctx, name3.c_str(),
+ print_progress_percent, NULL));
+ ASSERT_EQ(3, test_ls(ioctx, 3, name.c_str(), name2.c_str(), name3.c_str()));
+
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ ASSERT_EQ(0, rbd_open(ioctx, name3.c_str(), &image3, NULL));
+ ASSERT_EQ(0, rbd_metadata_list(image3, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image3, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_close(image2));
+ ASSERT_EQ(0, rbd_close(image3));
+ rados_ioctx_destroy(ioctx);
+}
+
+class PrintProgress : public librbd::ProgressContext
+{
+public:
+ int update_progress(uint64_t offset, uint64_t src_size) override
+ {
+ float percent = ((float)offset * 100) / src_size;
+ printf("%3.2f%% done\n", percent);
+ return 0;
+ }
+};
+
+TEST_F(TestLibRBD, TestCopyPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ librbd::Image image2;
+ librbd::Image image3;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ std::string name3 = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ PrintProgress pp;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image.metadata_set(key, val));
+ }
+
+ ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name.c_str()));
+ ASSERT_EQ(0, image.copy(ioctx, name2.c_str()));
+ ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), NULL));
+
+ map<string, bufferlist> pairs;
+ std::string value;
+ ASSERT_EQ(0, image2.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image2.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
+
+ ASSERT_EQ(0, image.copy_with_progress(ioctx, name3.c_str(), pp));
+ ASSERT_EQ(3, test_ls_pp(rbd, ioctx, 3, name.c_str(), name2.c_str(),
+ name3.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name3.c_str(), NULL));
+
+ pairs.clear();
+ ASSERT_EQ(0, image3.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image3.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestDeepCopy)
+{
+ REQUIRE_FORMAT_V2();
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, create_pool(true).c_str(), &ioctx);
+ BOOST_SCOPE_EXIT_ALL( (&ioctx) ) {
+ rados_ioctx_destroy(ioctx);
+ };
+
+ rbd_image_t image;
+ rbd_image_t image2;
+ rbd_image_t image3;
+ rbd_image_t image4;
+ rbd_image_t image5;
+ rbd_image_t image6;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ std::string name3 = get_temp_image_name();
+ std::string name4 = get_temp_image_name();
+ std::string name5 = get_temp_image_name();
+ std::string name6 = get_temp_image_name();
+
+ uint64_t size = 2 << 20;
+
+ rbd_image_options_t opts;
+ rbd_image_options_create(&opts);
+ BOOST_SCOPE_EXIT_ALL( (&opts) ) {
+ rbd_image_options_destroy(opts);
+ };
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image) ) {
+ ASSERT_EQ(0, rbd_close(image));
+ };
+ ASSERT_EQ(1, test_ls(ioctx, 1, name.c_str()));
+
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(image, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
+ ASSERT_EQ(0, rbd_deep_copy(image, ioctx, name2.c_str(), opts));
+ ASSERT_EQ(2, test_ls(ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name2.c_str(), &image2, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image2) ) {
+ ASSERT_EQ(0, rbd_close(image2));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image2, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_deep_copy_with_progress(image, ioctx, name3.c_str(), opts,
+ print_progress_percent, NULL));
+ ASSERT_EQ(3, test_ls(ioctx, 3, name.c_str(), name2.c_str(), name3.c_str()));
+
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ ASSERT_EQ(0, rbd_open(ioctx, name3.c_str(), &image3, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image3) ) {
+ ASSERT_EQ(0, rbd_close(image3));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image3, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image3, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_snap_create(image, "deep_snap"));
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, "deep_snap"));
+ ASSERT_EQ(0, rbd_snap_protect(image, "deep_snap"));
+ ASSERT_EQ(0, rbd_clone3(ioctx, name.c_str(), "deep_snap", ioctx,
+ name4.c_str(), opts));
+
+ ASSERT_EQ(4, test_ls(ioctx, 4, name.c_str(), name2.c_str(), name3.c_str(),
+ name4.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name4.c_str(), &image4, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image4) ) {
+ ASSERT_EQ(0, rbd_close(image4));
+ };
+ ASSERT_EQ(0, rbd_snap_create(image4, "deep_snap"));
+
+ ASSERT_EQ(0, rbd_deep_copy(image4, ioctx, name5.c_str(), opts));
+ ASSERT_EQ(5, test_ls(ioctx, 5, name.c_str(), name2.c_str(), name3.c_str(),
+ name4.c_str(), name5.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name5.c_str(), &image5, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image5) ) {
+ ASSERT_EQ(0, rbd_close(image5));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image5, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image5, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_deep_copy_with_progress(image4, ioctx, name6.c_str(), opts,
+ print_progress_percent, NULL));
+ ASSERT_EQ(6, test_ls(ioctx, 6, name.c_str(), name2.c_str(), name3.c_str(),
+ name4.c_str(), name5.c_str(), name6.c_str()));
+
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ ASSERT_EQ(0, rbd_open(ioctx, name6.c_str(), &image6, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image6) ) {
+ ASSERT_EQ(0, rbd_close(image6));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image6, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image6, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+}
+
+TEST_F(TestLibRBD, TestDeepCopyPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ librbd::Image image2;
+ librbd::Image image3;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ std::string name3 = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ librbd::ImageOptions opts;
+ PrintProgress pp;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image.metadata_set(key, val));
+ }
+
+ ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name.c_str()));
+ ASSERT_EQ(0, image.deep_copy(ioctx, name2.c_str(), opts));
+ ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), NULL));
+
+ map<string, bufferlist> pairs;
+ std::string value;
+ ASSERT_EQ(0, image2.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image2.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
+
+ ASSERT_EQ(0, image.deep_copy_with_progress(ioctx, name3.c_str(), opts, pp));
+ ASSERT_EQ(3, test_ls_pp(rbd, ioctx, 3, name.c_str(), name2.c_str(),
+ name3.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name3.c_str(), NULL));
+
+ pairs.clear();
+ ASSERT_EQ(0, image3.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image3.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
+ }
+
+ ioctx.close();
+}
+
+int test_ls_snaps(rbd_image_t image, int num_expected, ...)
+{
+ int num_snaps, i, j, max_size = 10;
+ va_list ap;
+ rbd_snap_info_t snaps[max_size];
+ num_snaps = rbd_snap_list(image, snaps, &max_size);
+ printf("num snaps is: %d\nexpected: %d\n", num_snaps, num_expected);
+
+ for (i = 0; i < num_snaps; i++) {
+ printf("snap: %s\n", snaps[i].name);
+ }
+
+ va_start(ap, num_expected);
+ for (i = num_expected; i > 0; i--) {
+ char *expected = va_arg(ap, char *);
+ uint64_t expected_size = va_arg(ap, uint64_t);
+ bool found = false;
+ for (j = 0; j < num_snaps; j++) {
+ if (snaps[j].name == NULL)
+ continue;
+ if (strcmp(snaps[j].name, expected) == 0) {
+ printf("found %s with size %llu\n", snaps[j].name, (unsigned long long) snaps[j].size);
+ EXPECT_EQ(expected_size, snaps[j].size);
+ free((void *) snaps[j].name);
+ snaps[j].name = NULL;
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+ va_end(ap);
+
+ for (i = 0; i < num_snaps; i++) {
+ EXPECT_EQ((const char *)0, snaps[i].name);
+ }
+
+ return num_snaps;
+}
+
+TEST_F(TestLibRBD, TestCreateLsDeleteSnap)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ uint64_t size2 = 4 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_snap_create(image, "snap1"));
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "snap1", size));
+ ASSERT_EQ(0, rbd_resize(image, size2));
+ ASSERT_EQ(0, rbd_snap_create(image, "snap2"));
+ ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1", size, "snap2", size2));
+ ASSERT_EQ(0, rbd_snap_remove(image, "snap1"));
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "snap2", size2));
+ ASSERT_EQ(0, rbd_snap_remove(image, "snap2"));
+ ASSERT_EQ(0, test_ls_snaps(image, 0));
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+int test_get_snapshot_timestamp(rbd_image_t image, uint64_t snap_id)
+{
+ struct timespec timestamp;
+ EXPECT_EQ(0, rbd_snap_get_timestamp(image, snap_id, &timestamp));
+ EXPECT_LT(0, timestamp.tv_sec);
+ return 0;
+}
+
+TEST_F(TestLibRBD, TestGetSnapShotTimeStamp)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int num_snaps, max_size = 10;
+ rbd_snap_info_t snaps[max_size];
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_snap_create(image, "snap1"));
+ num_snaps = rbd_snap_list(image, snaps, &max_size);
+ ASSERT_EQ(1, num_snaps);
+ ASSERT_EQ(0, test_get_snapshot_timestamp(image, snaps[0].id));
+ free((void *)snaps[0].name);
+
+ ASSERT_EQ(0, rbd_snap_create(image, "snap2"));
+ num_snaps = rbd_snap_list(image, snaps, &max_size);
+ ASSERT_EQ(2, num_snaps);
+ ASSERT_EQ(0, test_get_snapshot_timestamp(image, snaps[0].id));
+ ASSERT_EQ(0, test_get_snapshot_timestamp(image, snaps[1].id));
+ free((void *)snaps[0].name);
+ free((void *)snaps[1].name);
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+
+int test_ls_snaps(librbd::Image& image, size_t num_expected, ...)
+{
+ int r;
+ size_t i, j;
+ va_list ap;
+ vector<librbd::snap_info_t> snaps;
+ r = image.snap_list(snaps);
+ EXPECT_TRUE(r >= 0);
+ cout << "num snaps is: " << snaps.size() << std::endl
+ << "expected: " << num_expected << std::endl;
+
+ for (i = 0; i < snaps.size(); i++) {
+ cout << "snap: " << snaps[i].name << std::endl;
+ }
+
+ va_start(ap, num_expected);
+ for (i = num_expected; i > 0; i--) {
+ char *expected = va_arg(ap, char *);
+ uint64_t expected_size = va_arg(ap, uint64_t);
+ int found = 0;
+ for (j = 0; j < snaps.size(); j++) {
+ if (snaps[j].name == "")
+ continue;
+ if (strcmp(snaps[j].name.c_str(), expected) == 0) {
+ cout << "found " << snaps[j].name << " with size " << snaps[j].size
+ << std::endl;
+ EXPECT_EQ(expected_size, snaps[j].size);
+ snaps[j].name = "";
+ found = 1;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+ va_end(ap);
+
+ for (i = 0; i < snaps.size(); i++) {
+ EXPECT_EQ("", snaps[i].name);
+ }
+
+ return snaps.size();
+}
+
+TEST_F(TestLibRBD, TestCreateLsDeleteSnapPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ uint64_t size2 = 4 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool exists;
+ ASSERT_EQ(0, image.snap_exists2("snap1", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(0, image.snap_exists2("snap1", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "snap1", size));
+ ASSERT_EQ(0, image.resize(size2));
+ ASSERT_EQ(0, image.snap_exists2("snap2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, image.snap_create("snap2"));
+ ASSERT_EQ(0, image.snap_exists2("snap2", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1", size, "snap2", size2));
+ ASSERT_EQ(0, image.snap_remove("snap1"));
+ ASSERT_EQ(0, image.snap_exists2("snap1", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "snap2", size2));
+ ASSERT_EQ(0, image.snap_remove("snap2"));
+ ASSERT_EQ(0, image.snap_exists2("snap2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, test_ls_snaps(image, 0));
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestGetNameIdSnapPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(0, image.snap_create("snap2"));
+ ASSERT_EQ(0, image.snap_create("snap3"));
+ vector<librbd::snap_info_t> snaps;
+ int r = image.snap_list(snaps);
+ EXPECT_TRUE(r >= 0);
+
+ for (size_t i = 0; i < snaps.size(); ++i) {
+ std::string expected_snap_name;
+ image.snap_get_name(snaps[i].id, &expected_snap_name);
+ ASSERT_EQ(expected_snap_name, snaps[i].name);
+ }
+
+ for (size_t i = 0; i < snaps.size(); ++i) {
+ uint64_t expected_snap_id;
+ image.snap_get_id(snaps[i].name, &expected_snap_id);
+ ASSERT_EQ(expected_snap_id, snaps[i].id);
+ }
+
+ ASSERT_EQ(0, image.snap_remove("snap1"));
+ ASSERT_EQ(0, image.snap_remove("snap2"));
+ ASSERT_EQ(0, image.snap_remove("snap3"));
+ ASSERT_EQ(0, test_ls_snaps(image, 0));
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCreateLsRenameSnapPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ uint64_t size2 = 4 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool exists;
+ ASSERT_EQ(0, image.snap_exists2("snap1", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(0, image.snap_exists2("snap1", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "snap1", size));
+ ASSERT_EQ(0, image.resize(size2));
+ ASSERT_EQ(0, image.snap_exists2("snap2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, image.snap_create("snap2"));
+ ASSERT_EQ(0, image.snap_exists2("snap2", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1", size, "snap2", size2));
+ ASSERT_EQ(0, image.snap_rename("snap1","snap1-rename"));
+ ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1-rename", size, "snap2", size2));
+ ASSERT_EQ(0, image.snap_exists2("snap1", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, image.snap_exists2("snap1-rename", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(0, image.snap_remove("snap1-rename"));
+ ASSERT_EQ(0, image.snap_rename("snap2","snap2-rename"));
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "snap2-rename", size2));
+ ASSERT_EQ(0, image.snap_exists2("snap2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, image.snap_exists2("snap2-rename", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(0, image.snap_remove("snap2-rename"));
+ ASSERT_EQ(0, test_ls_snaps(image, 0));
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, ConcurrentCreatesUnvalidatedPool)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, create_pool(true).c_str(),
+ &ioctx));
+
+ std::vector<std::string> names;
+ for (int i = 0; i < 4; i++) {
+ names.push_back(get_temp_image_name());
+ }
+ uint64_t size = 2 << 20;
+
+ std::vector<std::thread> threads;
+ for (const auto& name : names) {
+ threads.emplace_back([ioctx, &name, size]() {
+ int order = 0;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ });
+ }
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ for (const auto& name : names) {
+ ASSERT_EQ(0, rbd_remove(ioctx, name.c_str()));
+ }
+ rados_ioctx_destroy(ioctx);
+}
+
+static void remove_full_try(rados_ioctx_t ioctx, const std::string& image_name,
+ const std::string& data_pool_name)
+{
+ int order = 0;
+ uint64_t quota = 10 << 20;
+ uint64_t size = 5 * quota;
+ ASSERT_EQ(0, create_image(ioctx, image_name.c_str(), size, &order));
+
+ std::string cmdstr = "{\"prefix\": \"osd pool set-quota\", \"pool\": \"" +
+ data_pool_name + "\", \"field\": \"max_bytes\", \"val\": \"" +
+ std::to_string(quota) + "\"}";
+ char *cmd[1];
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(rados_ioctx_get_cluster(ioctx),
+ (const char **)cmd, 1, "", 0, nullptr, 0,
+ nullptr, 0));
+
+ rados_set_pool_full_try(ioctx);
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, image_name.c_str(), &image, nullptr));
+
+ uint64_t off;
+ size_t len = 1 << 20;
+ ssize_t ret;
+ for (off = 0; off < size; off += len) {
+ ret = rbd_write_zeroes(image, off, len,
+ RBD_WRITE_ZEROES_FLAG_THICK_PROVISION,
+ LIBRADOS_OP_FLAG_FADVISE_FUA);
+ if (ret < 0) {
+ break;
+ }
+ ASSERT_EQ(ret, len);
+ sleep(1);
+ }
+ ASSERT_TRUE(off >= quota && off < size);
+ ASSERT_EQ(ret, -EDQUOT);
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ // make sure we have latest map that marked the pool full
+ ASSERT_EQ(0, rados_wait_for_latest_osdmap(rados_ioctx_get_cluster(ioctx)));
+ ASSERT_EQ(0, rbd_remove(ioctx, image_name.c_str()));
+}
+
+TEST_F(TestLibRBD, RemoveFullTry)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ rados_ioctx_t ioctx;
+ auto pool_name = create_pool(true);
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, pool_name.c_str(), &ioctx));
+ // cancel out rbd_default_data_pool -- we need an image without
+ // a separate data pool
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_default_data_pool",
+ pool_name.c_str()));
+
+ int order = 0;
+ auto image_name = get_temp_image_name();
+ // FIXME: this is a workaround for rbd_trash object being created
+ // on the first remove -- pre-create it to avoid bumping into quota
+ ASSERT_EQ(0, create_image(ioctx, image_name.c_str(), 0, &order));
+ ASSERT_EQ(0, rbd_remove(ioctx, image_name.c_str()));
+ remove_full_try(ioctx, image_name, pool_name);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, RemoveFullTryDataPool)
+{
+ REQUIRE_FORMAT_V2();
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ rados_ioctx_t ioctx;
+ auto pool_name = create_pool(true);
+ auto data_pool_name = create_pool(true);
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, pool_name.c_str(), &ioctx));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_default_data_pool",
+ data_pool_name.c_str()));
+
+ auto image_name = get_temp_image_name();
+ remove_full_try(ioctx, image_name, data_pool_name);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestIO)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_read_from_replica_policy", "balance"));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ test_io(image);
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestEncryptionLUKS1)
+{
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 32 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rados_conf_set(
+ _cluster, "rbd_read_from_replica_policy", "balance"));
+
+ rbd_image_t image;
+ rbd_encryption_luks1_format_options_t luks1_opts = {
+ .alg = RBD_ENCRYPTION_ALGORITHM_AES256,
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ rbd_encryption_luks2_format_options_t luks2_opts = {
+ .alg = RBD_ENCRYPTION_ALGORITHM_AES256,
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ rbd_encryption_luks_format_options_t luks_opts = {
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+#ifndef HAVE_LIBCRYPTSETUP
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+#else
+ ASSERT_EQ(-EINVAL, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+ ASSERT_EQ(0, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_EQ(-EEXIST, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+
+ test_io(image);
+
+ ASSERT_PASSED(write_test_data, image, "test", 0, 4, 0);
+ ASSERT_EQ(0, rbd_close(image));
+
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(-EINVAL, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_EQ(0, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_PASSED(read_test_data, image, "test", 0, 4, 0);
+ ASSERT_EQ(0, rbd_close(image));
+
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(-EINVAL, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_EQ(0, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+ ASSERT_PASSED(read_test_data, image, "test", 0, 4, 0);
+#endif
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestEncryptionLUKS2)
+{
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 32 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rados_conf_set(
+ _cluster, "rbd_read_from_replica_policy", "balance"));
+
+ rbd_image_t image;
+ rbd_encryption_luks1_format_options_t luks1_opts = {
+ .alg = RBD_ENCRYPTION_ALGORITHM_AES256,
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ rbd_encryption_luks2_format_options_t luks2_opts = {
+ .alg = RBD_ENCRYPTION_ALGORITHM_AES256,
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ rbd_encryption_luks_format_options_t luks_opts = {
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+#ifndef HAVE_LIBCRYPTSETUP
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+ ASSERT_EQ(-ENOTSUP, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+#else
+ ASSERT_EQ(-EINVAL, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+ ASSERT_EQ(0, rbd_encryption_format(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_EQ(-EEXIST, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+
+ test_io(image);
+
+ ASSERT_PASSED(write_test_data, image, "test", 0, 4, 0);
+ ASSERT_EQ(0, rbd_close(image));
+
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(-EINVAL, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_EQ(0, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)));
+ ASSERT_PASSED(read_test_data, image, "test", 0, 4, 0);
+ ASSERT_EQ(0, rbd_close(image));
+
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(-EINVAL, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)));
+ ASSERT_EQ(0, rbd_encryption_load(
+ image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)));
+ ASSERT_PASSED(read_test_data, image, "test", 0, 4, 0);
+#endif
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+}
+
+#ifdef HAVE_LIBCRYPTSETUP
+
+TEST_F(TestLibRBD, TestCloneEncryption)
+{
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+ ASSERT_EQ(0, rados_conf_set(
+ _cluster, "rbd_read_from_replica_policy", "balance"));
+
+ // create base image, write 'a's
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 256 << 20;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_PASSED(write_test_data, image, "aaaa", 0, 4, 0);
+ ASSERT_EQ(0, rbd_flush(image));
+
+ // clone, encrypt with LUKS1, write 'b's
+ ASSERT_EQ(0, rbd_snap_create(image, "snap"));
+ ASSERT_EQ(0, rbd_snap_protect(image, "snap"));
+
+ rbd_image_options_t image_opts;
+ rbd_image_options_create(&image_opts);
+ BOOST_SCOPE_EXIT_ALL( (&image_opts) ) {
+ rbd_image_options_destroy(image_opts);
+ };
+ std::string child1_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd_clone3(ioctx, name.c_str(), "snap", ioctx,
+ child1_name.c_str(), image_opts));
+
+ rbd_image_t child1;
+ ASSERT_EQ(0, rbd_open(ioctx, child1_name.c_str(), &child1, NULL));
+
+ rbd_encryption_luks1_format_options_t child1_opts = {
+ .alg = RBD_ENCRYPTION_ALGORITHM_AES256,
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ ASSERT_EQ(-EINVAL, rbd_encryption_load(
+ child1, RBD_ENCRYPTION_FORMAT_LUKS1, &child1_opts,
+ sizeof(child1_opts)));
+ ASSERT_EQ(0, rbd_encryption_format(
+ child1, RBD_ENCRYPTION_FORMAT_LUKS1, &child1_opts,
+ sizeof(child1_opts)));
+ ASSERT_EQ(0, rbd_encryption_load(
+ child1, RBD_ENCRYPTION_FORMAT_LUKS1, &child1_opts,
+ sizeof(child1_opts)));
+ ASSERT_PASSED(write_test_data, child1, "bbbb", 64 << 20, 4, 0);
+ ASSERT_EQ(0, rbd_flush(child1));
+
+ // clone, encrypt with LUKS2 (same passphrase), write 'c's
+ ASSERT_EQ(0, rbd_snap_create(child1, "snap"));
+ ASSERT_EQ(0, rbd_snap_protect(child1, "snap"));
+
+ std::string child2_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd_clone3(ioctx, child1_name.c_str(), "snap", ioctx,
+ child2_name.c_str(), image_opts));
+
+ rbd_image_t child2;
+ ASSERT_EQ(0, rbd_open(ioctx, child2_name.c_str(), &child2, NULL));
+
+ rbd_encryption_luks2_format_options_t child2_opts = {
+ .alg = RBD_ENCRYPTION_ALGORITHM_AES256,
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ ASSERT_EQ(0, rbd_encryption_format(
+ child2, RBD_ENCRYPTION_FORMAT_LUKS2, &child2_opts,
+ sizeof(child2_opts)));
+ rbd_encryption_luks_format_options_t child2_lopts = {
+ .passphrase = "password",
+ .passphrase_size = 8,
+ };
+ ASSERT_EQ(0, rbd_encryption_load(
+ child2, RBD_ENCRYPTION_FORMAT_LUKS, &child2_lopts,
+ sizeof(child2_lopts)));
+ ASSERT_PASSED(write_test_data, child2, "cccc", 128 << 20, 4, 0);
+ ASSERT_EQ(0, rbd_flush(child2));
+
+ // clone, encrypt with LUKS2 (different passphrase)
+ ASSERT_EQ(0, rbd_snap_create(child2, "snap"));
+ ASSERT_EQ(0, rbd_snap_protect(child2, "snap"));
+
+ std::string child3_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd_clone3(ioctx, child2_name.c_str(), "snap", ioctx,
+ child3_name.c_str(), image_opts));
+
+ rbd_image_t child3;
+ ASSERT_EQ(0, rbd_open(ioctx, child3_name.c_str(), &child3, NULL));
+
+ rbd_encryption_luks2_format_options_t child3_opts = {
+ .alg = RBD_ENCRYPTION_ALGORITHM_AES256,
+ .passphrase = "12345678",
+ .passphrase_size = 8,
+ };
+ ASSERT_EQ(0, rbd_encryption_format(
+ child3, RBD_ENCRYPTION_FORMAT_LUKS2, &child3_opts,
+ sizeof(child3_opts)));
+ ASSERT_EQ(-EPERM, rbd_encryption_load(
+ child3, RBD_ENCRYPTION_FORMAT_LUKS2, &child3_opts,
+ sizeof(child3_opts)));
+
+ // verify child3 data
+ rbd_encryption_spec_t specs[] = {
+ { .format = RBD_ENCRYPTION_FORMAT_LUKS2,
+ .opts = &child3_opts,
+ .opts_size = sizeof(child3_opts)},
+ { .format = RBD_ENCRYPTION_FORMAT_LUKS2,
+ .opts = &child2_opts,
+ .opts_size = sizeof(child2_opts)},
+ { .format = RBD_ENCRYPTION_FORMAT_LUKS1,
+ .opts = &child1_opts,
+ .opts_size = sizeof(child1_opts)}
+ };
+
+ ASSERT_EQ(0, rbd_encryption_load2(child3, specs, 3));
+
+ ASSERT_PASSED(read_test_data, child3, "aaaa", 0, 4, 0);
+ ASSERT_PASSED(read_test_data, child3, "bbbb", 64 << 20, 4, 0);
+ ASSERT_PASSED(read_test_data, child3, "cccc", 128 << 20, 4, 0);
+
+ // clone without formatting
+ ASSERT_EQ(0, rbd_snap_create(child3, "snap"));
+ ASSERT_EQ(0, rbd_snap_protect(child3, "snap"));
+
+ std::string child4_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd_clone3(ioctx, child3_name.c_str(), "snap", ioctx,
+ child4_name.c_str(), image_opts));
+
+ rbd_image_t child4;
+ ASSERT_EQ(0, rbd_open(ioctx, child4_name.c_str(), &child4, NULL));
+
+ rbd_encryption_spec_t child4_specs[] = {
+ { .format = RBD_ENCRYPTION_FORMAT_LUKS2,
+ .opts = &child3_opts,
+ .opts_size = sizeof(child3_opts)},
+ { .format = RBD_ENCRYPTION_FORMAT_LUKS2,
+ .opts = &child3_opts,
+ .opts_size = sizeof(child3_opts)},
+ { .format = RBD_ENCRYPTION_FORMAT_LUKS2,
+ .opts = &child2_opts,
+ .opts_size = sizeof(child2_opts)},
+ { .format = RBD_ENCRYPTION_FORMAT_LUKS1,
+ .opts = &child1_opts,
+ .opts_size = sizeof(child1_opts)}
+ };
+
+ ASSERT_EQ(0, rbd_encryption_load2(child4, child4_specs, 4));
+
+ // flatten child4
+ ASSERT_EQ(0, rbd_flatten(child4));
+
+ // reopen child4 and load encryption
+ ASSERT_EQ(0, rbd_close(child4));
+ ASSERT_EQ(0, rbd_open(ioctx, child4_name.c_str(), &child4, NULL));
+ ASSERT_EQ(0, rbd_encryption_load(
+ child4, RBD_ENCRYPTION_FORMAT_LUKS2, &child3_opts,
+ sizeof(child3_opts)));
+
+ // verify flattend image
+ ASSERT_PASSED(read_test_data, child4, "aaaa", 0, 4, 0);
+ ASSERT_PASSED(read_test_data, child4, "bbbb", 64 << 20, 4, 0);
+ ASSERT_PASSED(read_test_data, child4, "cccc", 128 << 20, 4, 0);
+
+ ASSERT_EQ(0, rbd_close(child4));
+ ASSERT_EQ(0, rbd_remove(ioctx, child4_name.c_str()));
+ ASSERT_EQ(0, rbd_snap_unprotect(child3, "snap"));
+ ASSERT_EQ(0, rbd_snap_remove(child3, "snap"));
+ ASSERT_EQ(0, rbd_close(child3));
+ ASSERT_EQ(0, rbd_remove(ioctx, child3_name.c_str()));
+ ASSERT_EQ(0, rbd_snap_unprotect(child2, "snap"));
+ ASSERT_EQ(0, rbd_snap_remove(child2, "snap"));
+ ASSERT_EQ(0, rbd_close(child2));
+ ASSERT_EQ(0, rbd_remove(ioctx, child2_name.c_str()));
+ ASSERT_EQ(0, rbd_snap_unprotect(child1, "snap"));
+ ASSERT_EQ(0, rbd_snap_remove(child1, "snap"));
+ ASSERT_EQ(0, rbd_close(child1));
+ ASSERT_EQ(0, rbd_remove(ioctx, child1_name.c_str()));
+ ASSERT_EQ(0, rbd_snap_unprotect(image, "snap"));
+ ASSERT_EQ(0, rbd_snap_remove(image, "snap"));
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_remove(ioctx, name.c_str()));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, LUKS1UnderLUKS2WithoutResize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+ std::string clone_name = get_temp_image_name();
+ uint64_t data_size = 25 << 20;
+ uint64_t luks1_meta_size = 4 << 20;
+ uint64_t luks2_meta_size = 16 << 20;
+ std::string parent_passphrase = "parent passphrase";
+ std::string clone_passphrase = "clone passphrase";
+
+ {
+ int order = 22;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(),
+ luks1_meta_size + data_size, &order));
+ librbd::Image parent;
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), nullptr));
+
+ librbd::encryption_luks1_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ ASSERT_EQ(0, parent.snap_create("snap"));
+ ASSERT_EQ(0, parent.snap_protect("snap"));
+ uint64_t features;
+ ASSERT_EQ(0, parent.features(&features));
+ ASSERT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap", ioctx,
+ clone_name.c_str(), features, &order));
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), nullptr));
+
+ librbd::encryption_luks2_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+
+ librbd::encryption_luks_format_options_t opts1 = {parent_passphrase};
+ librbd::encryption_luks_format_options_t opts2 = {clone_passphrase};
+ librbd::encryption_spec_t specs[] = {
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts2, sizeof(opts2)},
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts1, sizeof(opts1)}};
+ ASSERT_EQ(0, clone.encryption_load2(specs, std::size(specs)));
+
+ uint64_t size;
+ ASSERT_EQ(0, clone.size(&size));
+ EXPECT_EQ(data_size + luks1_meta_size - luks2_meta_size, size);
+ uint64_t overlap;
+ ASSERT_EQ(0, clone.overlap(&overlap));
+ EXPECT_EQ(data_size + luks1_meta_size - luks2_meta_size, overlap);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(
+ data_size + luks1_meta_size - luks2_meta_size, 'a'));
+
+ ceph::bufferlist read_bl;
+ ASSERT_EQ(expected_bl.length(),
+ clone.read(0, expected_bl.length(), read_bl));
+ EXPECT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+}
+
+TEST_F(TestLibRBD, LUKS2UnderLUKS1WithoutResize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+ std::string clone_name = get_temp_image_name();
+ uint64_t data_size = 25 << 20;
+ uint64_t luks1_meta_size = 4 << 20;
+ uint64_t luks2_meta_size = 16 << 20;
+ std::string parent_passphrase = "parent passphrase";
+ std::string clone_passphrase = "clone passphrase";
+
+ {
+ int order = 22;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(),
+ luks2_meta_size + data_size, &order));
+ librbd::Image parent;
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), nullptr));
+
+ librbd::encryption_luks2_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ ASSERT_EQ(0, parent.snap_create("snap"));
+ ASSERT_EQ(0, parent.snap_protect("snap"));
+ uint64_t features;
+ ASSERT_EQ(0, parent.features(&features));
+ ASSERT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap", ioctx,
+ clone_name.c_str(), features, &order));
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), nullptr));
+
+ librbd::encryption_luks1_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ librbd::encryption_luks_format_options_t opts1 = {parent_passphrase};
+ librbd::encryption_luks_format_options_t opts2 = {clone_passphrase};
+ librbd::encryption_spec_t specs[] = {
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts2, sizeof(opts2)},
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts1, sizeof(opts1)}};
+ ASSERT_EQ(0, clone.encryption_load2(specs, std::size(specs)));
+
+ uint64_t size;
+ ASSERT_EQ(0, clone.size(&size));
+ EXPECT_EQ(data_size + luks2_meta_size - luks1_meta_size, size);
+ uint64_t overlap;
+ ASSERT_EQ(0, clone.overlap(&overlap));
+ EXPECT_EQ(data_size, overlap);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(data_size, 'a'));
+ expected_bl.append_zero(luks2_meta_size - luks1_meta_size);
+
+ ceph::bufferlist read_bl;
+ ASSERT_EQ(expected_bl.length(),
+ clone.read(0, expected_bl.length(), read_bl));
+ EXPECT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+}
+
+TEST_F(TestLibRBD, EncryptionFormatNoData)
+{
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ auto name = get_temp_image_name();
+ uint64_t luks1_meta_size = 4 << 20;
+ std::string passphrase = "some passphrase";
+
+ {
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), luks1_meta_size - 1,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ librbd::encryption_luks1_format_options_t opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ ASSERT_EQ(-ENOSPC, image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1,
+ &opts, sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(luks1_meta_size - 1, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.resize(luks1_meta_size));
+
+ librbd::encryption_luks1_format_options_t opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ ASSERT_EQ(0, image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(0, size);
+ }
+}
+
+TEST_F(TestLibRBD, EncryptionLoadBadSize)
+{
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ auto name = get_temp_image_name();
+ uint64_t luks1_meta_size = 4 << 20;
+ std::string passphrase = "some passphrase";
+
+ {
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), luks1_meta_size,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ librbd::encryption_luks1_format_options_t opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ ASSERT_EQ(0, image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &opts,
+ sizeof(opts)));
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(0, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.resize(luks1_meta_size - 1));
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(-EINVAL, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(luks1_meta_size - 1, size);
+ }
+}
+
+TEST_F(TestLibRBD, EncryptionLoadBadStripePattern)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ librbd::RBD rbd;
+ auto name1 = get_temp_image_name();
+ auto name2 = get_temp_image_name();
+ auto name3 = get_temp_image_name();
+ std::string passphrase = "some passphrase";
+
+ {
+ int order = 22;
+ ASSERT_EQ(0, rbd.create3(ioctx, name1.c_str(), 20 << 20, features, &order,
+ 2 << 20, 2));
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name1.c_str(), nullptr));
+
+ librbd::encryption_luks1_format_options_t opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ ASSERT_EQ(0, image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(12 << 20, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name1.c_str(), nullptr));
+
+ // different but compatible striping pattern
+ librbd::ImageOptions image_opts;
+ ASSERT_EQ(0, image_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, 1 << 20));
+ ASSERT_EQ(0, image_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, 2));
+ ASSERT_EQ(0, image.deep_copy(ioctx, name2.c_str(), image_opts));
+ }
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name2.c_str(), nullptr));
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(12 << 20, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name1.c_str(), nullptr));
+
+ // incompatible striping pattern
+ librbd::ImageOptions image_opts;
+ ASSERT_EQ(0, image_opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, 1 << 20));
+ ASSERT_EQ(0, image_opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, 3));
+ ASSERT_EQ(0, image.deep_copy(ioctx, name3.c_str(), image_opts));
+ }
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name3.c_str(), nullptr));
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(-EINVAL, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(20 << 20, size);
+ }
+}
+
+TEST_F(TestLibRBD, EncryptionLoadFormatMismatch)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name1 = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ std::string name3 = get_temp_image_name();
+ std::string passphrase = "some passphrase";
+
+ librbd::encryption_luks1_format_options_t luks1_opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ librbd::encryption_luks2_format_options_t luks2_opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ librbd::encryption_luks_format_options_t luks_opts = {passphrase};
+
+#define LUKS_ONE {RBD_ENCRYPTION_FORMAT_LUKS1, &luks1_opts, sizeof(luks1_opts)}
+#define LUKS_TWO {RBD_ENCRYPTION_FORMAT_LUKS2, &luks2_opts, sizeof(luks2_opts)}
+#define LUKS_ANY {RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts)}
+
+ const std::vector<librbd::encryption_spec_t> bad_specs[] = {
+ {},
+ {LUKS_ONE},
+ {LUKS_TWO},
+ {LUKS_ONE, LUKS_ONE},
+ {LUKS_ONE, LUKS_TWO},
+ {LUKS_ONE, LUKS_ANY},
+ {LUKS_TWO, LUKS_TWO},
+ {LUKS_ANY, LUKS_TWO},
+ {LUKS_ONE, LUKS_ONE, LUKS_ONE},
+ {LUKS_ONE, LUKS_ONE, LUKS_TWO},
+ {LUKS_ONE, LUKS_ONE, LUKS_ANY},
+ {LUKS_ONE, LUKS_TWO, LUKS_ONE},
+ {LUKS_ONE, LUKS_TWO, LUKS_TWO},
+ {LUKS_ONE, LUKS_TWO, LUKS_ANY},
+ {LUKS_ONE, LUKS_ANY, LUKS_ONE},
+ {LUKS_ONE, LUKS_ANY, LUKS_TWO},
+ {LUKS_ONE, LUKS_ANY, LUKS_ANY},
+ {LUKS_TWO, LUKS_ONE, LUKS_TWO},
+ {LUKS_TWO, LUKS_TWO, LUKS_ONE},
+ {LUKS_TWO, LUKS_TWO, LUKS_TWO},
+ {LUKS_TWO, LUKS_TWO, LUKS_ANY},
+ {LUKS_TWO, LUKS_ANY, LUKS_TWO},
+ {LUKS_ANY, LUKS_ONE, LUKS_TWO},
+ {LUKS_ANY, LUKS_TWO, LUKS_ONE},
+ {LUKS_ANY, LUKS_TWO, LUKS_TWO},
+ {LUKS_ANY, LUKS_TWO, LUKS_ANY},
+ {LUKS_ANY, LUKS_ANY, LUKS_TWO},
+ {LUKS_ANY, LUKS_ANY, LUKS_ANY, LUKS_ANY}};
+
+ const std::vector<librbd::encryption_spec_t> good_specs[] = {
+ {LUKS_ANY},
+ {LUKS_TWO, LUKS_ONE},
+ {LUKS_TWO, LUKS_ANY},
+ {LUKS_ANY, LUKS_ONE},
+ {LUKS_ANY, LUKS_ANY},
+ {LUKS_TWO, LUKS_ONE, LUKS_ONE},
+ {LUKS_TWO, LUKS_ONE, LUKS_ANY},
+ {LUKS_TWO, LUKS_ANY, LUKS_ONE},
+ {LUKS_TWO, LUKS_ANY, LUKS_ANY},
+ {LUKS_ANY, LUKS_ONE, LUKS_ONE},
+ {LUKS_ANY, LUKS_ONE, LUKS_ANY},
+ {LUKS_ANY, LUKS_ANY, LUKS_ONE},
+ {LUKS_ANY, LUKS_ANY, LUKS_ANY}};
+
+ static_assert(std::size(bad_specs) + std::size(good_specs) == 1 + 3 + 9 + 27 + 1);
+
+#undef LUKS_ONE
+#undef LUKS_TWO
+#undef LUKS_ANY
+
+ {
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name1.c_str(), 20 << 20, &order));
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name1.c_str(), nullptr));
+ ASSERT_EQ(0, image1.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1,
+ &luks1_opts, sizeof(luks1_opts)));
+
+ ASSERT_EQ(0, image1.snap_create("snap"));
+ ASSERT_EQ(0, image1.snap_protect("snap"));
+ uint64_t features;
+ ASSERT_EQ(0, image1.features(&features));
+ ASSERT_EQ(0, rbd.clone(ioctx, name1.c_str(), "snap", ioctx, name2.c_str(),
+ features, &order));
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), nullptr));
+ ASSERT_EQ(0, image2.snap_create("snap"));
+ ASSERT_EQ(0, image2.snap_protect("snap"));
+ ASSERT_EQ(0, rbd.clone(ioctx, name2.c_str(), "snap", ioctx, name3.c_str(),
+ features, &order));
+
+ librbd::Image image3;
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name3.c_str(), nullptr));
+ ASSERT_EQ(0, image3.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2,
+ &luks2_opts, sizeof(luks2_opts)));
+ }
+
+ {
+ librbd::Image image3;
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name3.c_str(), nullptr));
+ for (auto& specs : bad_specs) {
+ ASSERT_EQ(-EINVAL, image3.encryption_load2(specs.data(), specs.size()));
+ }
+ }
+
+ for (auto& specs : good_specs) {
+ librbd::Image image3;
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name3.c_str(), nullptr));
+ ASSERT_EQ(0, image3.encryption_load2(specs.data(), specs.size()));
+ }
+}
+
+TEST_F(TestLibRBD, EncryptedResize)
+{
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ auto name = get_temp_image_name();
+ uint64_t luks2_meta_size = 16 << 20;
+ uint64_t data_size = 10 << 20;
+ std::string passphrase = "some passphrase";
+
+ {
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(),
+ luks2_meta_size + data_size, &order));
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(luks2_meta_size + data_size, size);
+
+ librbd::encryption_luks2_format_options_t opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ ASSERT_EQ(0, image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &opts,
+ sizeof(opts)));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(data_size, size);
+ ASSERT_EQ(0, image.resize(data_size * 3));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(data_size * 3, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(luks2_meta_size + data_size * 3, size);
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(data_size * 3, size);
+ ASSERT_EQ(0, image.resize(data_size / 2));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(data_size / 2, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(luks2_meta_size + data_size / 2, size);
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(data_size / 2, size);
+ ASSERT_EQ(0, image.resize(0));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(0, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(luks2_meta_size, size);
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(0, image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(0, size);
+ ASSERT_EQ(0, image.resize(data_size));
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(data_size, size);
+ }
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ ASSERT_EQ(luks2_meta_size + data_size, size);
+ }
+}
+
+TEST_F(TestLibRBD, EncryptedFlattenSmallData)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+ std::string clone_name = get_temp_image_name();
+ uint64_t data_size = 5000;
+ uint64_t luks2_meta_size = 16 << 20;
+ std::string passphrase = "some passphrase";
+
+ {
+ int order = 22;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(),
+ luks2_meta_size + data_size, &order));
+ librbd::Image parent;
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), nullptr));
+
+ librbd::encryption_luks2_format_options_t opts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &opts,
+ sizeof(opts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ ASSERT_EQ(0, parent.snap_create("snap"));
+ ASSERT_EQ(0, parent.snap_protect("snap"));
+ uint64_t features;
+ ASSERT_EQ(0, parent.features(&features));
+ ASSERT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap", ioctx,
+ clone_name.c_str(), features, &order));
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), nullptr));
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(0, clone.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, clone.size(&size));
+ ASSERT_EQ(data_size, size);
+ uint64_t overlap;
+ ASSERT_EQ(0, clone.overlap(&overlap));
+ ASSERT_EQ(data_size, overlap);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(data_size, 'a'));
+
+ ceph::bufferlist read_bl1;
+ ASSERT_EQ(data_size, clone.read(0, data_size, read_bl1));
+ ASSERT_TRUE(expected_bl.contents_equal(read_bl1));
+
+ ASSERT_EQ(0, clone.flatten());
+
+ ceph::bufferlist read_bl2;
+ ASSERT_EQ(data_size, clone.read(0, data_size, read_bl2));
+ ASSERT_TRUE(expected_bl.contents_equal(read_bl2));
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), nullptr));
+
+ librbd::encryption_luks_format_options_t opts = {passphrase};
+ ASSERT_EQ(0, clone.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts)));
+ uint64_t size;
+ ASSERT_EQ(0, clone.size(&size));
+ ASSERT_EQ(data_size, size);
+ uint64_t overlap;
+ ASSERT_EQ(0, clone.overlap(&overlap));
+ ASSERT_EQ(0, overlap);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(data_size, 'a'));
+
+ ceph::bufferlist read_bl;
+ ASSERT_EQ(data_size, clone.read(0, data_size, read_bl));
+ ASSERT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+}
+
+struct LUKSOnePassphrase {
+ int load(librbd::Image& clone) {
+ librbd::encryption_luks_format_options_t opts = {m_passphrase};
+ return clone.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts));
+ }
+
+ int load_flattened(librbd::Image& clone) {
+ return load(clone);
+ }
+
+ std::string m_passphrase = "some passphrase";
+};
+
+struct LUKSTwoPassphrases {
+ int load(librbd::Image& clone) {
+ librbd::encryption_luks_format_options_t opts1 = {m_parent_passphrase};
+ librbd::encryption_luks_format_options_t opts2 = {m_clone_passphrase};
+ librbd::encryption_spec_t specs[] = {
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts2, sizeof(opts2)},
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts1, sizeof(opts1)}};
+ return clone.encryption_load2(specs, std::size(specs));
+ }
+
+ int load_flattened(librbd::Image& clone) {
+ librbd::encryption_luks_format_options_t opts = {m_clone_passphrase};
+ return clone.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, &opts,
+ sizeof(opts));
+ }
+
+ std::string m_parent_passphrase = "parent passphrase";
+ std::string m_clone_passphrase = "clone passphrase";
+};
+
+struct PlaintextUnderLUKS1 : LUKSOnePassphrase {
+protected:
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ // before taking a parent snapshot, (temporarily) add extra space
+ // to the parent to account for upcoming LUKS1 header in the clone,
+ // making the clone able to reach all parent data
+ uint64_t luks1_meta_size = 4 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks1_meta_size));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ librbd::encryption_luks1_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, m_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+ *passed = true;
+ }
+};
+
+struct PlaintextUnderLUKS2 : LUKSOnePassphrase {
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ // before taking a parent snapshot, (temporarily) add extra space
+ // to the parent to account for upcoming LUKS2 header in the clone,
+ // making the clone able to reach all parent data
+ uint64_t luks2_meta_size = 16 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks2_meta_size));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ librbd::encryption_luks2_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, m_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+ *passed = true;
+ }
+};
+
+struct UnformattedLUKS1 : LUKSOnePassphrase {
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ uint64_t luks1_meta_size = 4 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks1_meta_size));
+ librbd::encryption_luks1_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, m_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ *passed = true;
+ }
+};
+
+struct LUKS1UnderLUKS1 : LUKSTwoPassphrases {
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ uint64_t luks1_meta_size = 4 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks1_meta_size));
+ librbd::encryption_luks1_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, m_parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ librbd::encryption_luks1_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, m_clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+ *passed = true;
+ }
+};
+
+struct LUKS1UnderLUKS2 : LUKSTwoPassphrases {
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ uint64_t luks1_meta_size = 4 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks1_meta_size));
+ librbd::encryption_luks1_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, m_parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ // before taking a parent snapshot, (temporarily) add extra space
+ // to the parent to account for upcoming LUKS2 header in the clone,
+ // making the clone able to reach all parent data
+ // space taken by LUKS1 header in the parent would be reused
+ uint64_t luks2_meta_size = 16 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks2_meta_size - luks1_meta_size));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ librbd::encryption_luks2_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, m_clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+ *passed = true;
+ }
+};
+
+struct UnformattedLUKS2 : LUKSOnePassphrase {
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ uint64_t luks2_meta_size = 16 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks2_meta_size));
+ librbd::encryption_luks2_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, m_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ *passed = true;
+ }
+};
+
+struct LUKS2UnderLUKS2 : LUKSTwoPassphrases {
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ uint64_t luks2_meta_size = 16 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks2_meta_size));
+ librbd::encryption_luks2_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, m_parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ librbd::encryption_luks2_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, m_clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+ *passed = true;
+ }
+};
+
+struct LUKS2UnderLUKS1 : LUKSTwoPassphrases {
+ void setup_parent(librbd::Image& parent, uint64_t data_size, bool* passed) {
+ uint64_t luks2_meta_size = 16 << 20;
+ ASSERT_EQ(0, parent.resize(data_size + luks2_meta_size));
+ librbd::encryption_luks2_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, m_parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+ *passed = true;
+ }
+
+ void setup_clone(librbd::Image& clone, uint64_t data_size, bool* passed) {
+ librbd::encryption_luks1_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, m_clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ // after loading encryption on the clone, one can get rid of
+ // unneeded space allowance in the clone arising from LUKS2 header
+ // in the parent being bigger than LUKS1 header in the clone
+ ASSERT_EQ(0, load(clone));
+ ASSERT_EQ(0, clone.resize(data_size));
+ *passed = true;
+ }
+};
+
+template <typename FormatPolicy>
+class EncryptedFlattenTest : public TestLibRBD, FormatPolicy {
+protected:
+ void create_and_setup(bool* passed) {
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), m_ioctx));
+
+ int order = 22;
+ ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, m_parent_name.c_str(),
+ m_data_size, &order));
+ librbd::Image parent;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, parent, m_parent_name.c_str(), nullptr));
+ ASSERT_PASSED(FormatPolicy::setup_parent, parent, m_data_size);
+
+ ASSERT_EQ(0, parent.snap_create("snap"));
+ ASSERT_EQ(0, parent.snap_protect("snap"));
+ uint64_t features;
+ ASSERT_EQ(0, parent.features(&features));
+ ASSERT_EQ(0, m_rbd.clone(m_ioctx, m_parent_name.c_str(), "snap", m_ioctx,
+ m_clone_name.c_str(), features, &order));
+ librbd::Image clone;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, clone, m_clone_name.c_str(), nullptr));
+ ASSERT_PASSED(FormatPolicy::setup_clone, clone, m_data_size);
+
+ *passed = true;
+ }
+
+ void open_and_load(librbd::Image& clone, bool* passed) {
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, clone, m_clone_name.c_str(), nullptr));
+ ASSERT_EQ(0, FormatPolicy::load(clone));
+ *passed = true;
+ }
+
+ void open_and_load_flattened(librbd::Image& clone, bool* passed) {
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, clone, m_clone_name.c_str(), nullptr));
+ ASSERT_EQ(0, FormatPolicy::load_flattened(clone));
+ *passed = true;
+ }
+
+ void verify_size_and_overlap(librbd::Image& image, uint64_t expected_size,
+ uint64_t expected_overlap) {
+ uint64_t size;
+ ASSERT_EQ(0, image.size(&size));
+ EXPECT_EQ(expected_size, size);
+ uint64_t overlap;
+ ASSERT_EQ(0, image.overlap(&overlap));
+ EXPECT_EQ(expected_overlap, overlap);
+ }
+
+ void verify_data(librbd::Image& image, const ceph::bufferlist& expected_bl) {
+ ceph::bufferlist read_bl;
+ ASSERT_EQ(expected_bl.length(),
+ image.read(0, expected_bl.length(), read_bl));
+ EXPECT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+
+ librados::IoCtx m_ioctx;
+ librbd::RBD m_rbd;
+ std::string m_parent_name = get_temp_image_name();
+ std::string m_clone_name = get_temp_image_name();
+ uint64_t m_data_size = 25 << 20;
+};
+
+using EncryptedFlattenTestTypes =
+ ::testing::Types<PlaintextUnderLUKS1, PlaintextUnderLUKS2,
+ UnformattedLUKS1, LUKS1UnderLUKS1, LUKS1UnderLUKS2,
+ UnformattedLUKS2, LUKS2UnderLUKS2, LUKS2UnderLUKS1>;
+TYPED_TEST_SUITE(EncryptedFlattenTest, EncryptedFlattenTestTypes);
+
+TYPED_TEST(EncryptedFlattenTest, Simple)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ ASSERT_PASSED0(this->create_and_setup);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(this->m_data_size, 'a'));
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load, clone);
+ this->verify_size_and_overlap(clone, this->m_data_size, this->m_data_size);
+ this->verify_data(clone, expected_bl);
+ ASSERT_EQ(0, clone.flatten());
+ this->verify_data(clone, expected_bl);
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load_flattened, clone);
+ this->verify_size_and_overlap(clone, this->m_data_size, 0);
+ this->verify_data(clone, expected_bl);
+ }
+}
+
+TYPED_TEST(EncryptedFlattenTest, Grow)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ ASSERT_PASSED0(this->create_and_setup);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(this->m_data_size, 'a'));
+ expected_bl.append_zero(1);
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load, clone);
+ ASSERT_EQ(0, clone.resize(this->m_data_size + 1));
+ this->verify_size_and_overlap(clone, this->m_data_size + 1,
+ this->m_data_size);
+ this->verify_data(clone, expected_bl);
+ ASSERT_EQ(0, clone.flatten());
+ this->verify_data(clone, expected_bl);
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load_flattened, clone);
+ this->verify_size_and_overlap(clone, this->m_data_size + 1, 0);
+ this->verify_data(clone, expected_bl);
+ }
+}
+
+TYPED_TEST(EncryptedFlattenTest, Shrink)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ ASSERT_PASSED0(this->create_and_setup);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(this->m_data_size - 1, 'a'));
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load, clone);
+ ASSERT_EQ(0, clone.resize(this->m_data_size - 1));
+ this->verify_size_and_overlap(clone, this->m_data_size - 1,
+ this->m_data_size - 1);
+ this->verify_data(clone, expected_bl);
+ ASSERT_EQ(0, clone.flatten());
+ this->verify_data(clone, expected_bl);
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load_flattened, clone);
+ this->verify_size_and_overlap(clone, this->m_data_size - 1, 0);
+ this->verify_data(clone, expected_bl);
+ }
+}
+
+TYPED_TEST(EncryptedFlattenTest, ShrinkToOne)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ ASSERT_PASSED0(this->create_and_setup);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(1, 'a'));
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load, clone);
+ ASSERT_EQ(0, clone.resize(1));
+ this->verify_size_and_overlap(clone, 1, 1);
+ this->verify_data(clone, expected_bl);
+ ASSERT_EQ(0, clone.flatten());
+ this->verify_data(clone, expected_bl);
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load_flattened, clone);
+ this->verify_size_and_overlap(clone, 1, 0);
+ this->verify_data(clone, expected_bl);
+ }
+}
+
+TYPED_TEST(EncryptedFlattenTest, ShrinkToOneAfterSnapshot)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ ASSERT_PASSED0(this->create_and_setup);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(1, 'a'));
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load, clone);
+ ASSERT_EQ(0, clone.snap_create("snap"));
+ ASSERT_EQ(0, clone.resize(1));
+ this->verify_size_and_overlap(clone, 1, 1);
+ this->verify_data(clone, expected_bl);
+ ASSERT_EQ(0, clone.flatten());
+ this->verify_data(clone, expected_bl);
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load_flattened, clone);
+ this->verify_size_and_overlap(clone, 1, 0);
+ this->verify_data(clone, expected_bl);
+ }
+}
+
+TYPED_TEST(EncryptedFlattenTest, MinOverlap)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ ASSERT_PASSED0(this->create_and_setup);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(1, 'a'));
+ expected_bl.append_zero(this->m_data_size - 1);
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load, clone);
+ ASSERT_EQ(0, clone.resize(1));
+ ASSERT_EQ(0, clone.resize(this->m_data_size));
+ this->verify_size_and_overlap(clone, this->m_data_size, 1);
+ this->verify_data(clone, expected_bl);
+ ASSERT_EQ(0, clone.flatten());
+ this->verify_data(clone, expected_bl);
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load_flattened, clone);
+ this->verify_size_and_overlap(clone, this->m_data_size, 0);
+ this->verify_data(clone, expected_bl);
+ }
+}
+
+TYPED_TEST(EncryptedFlattenTest, ZeroOverlap)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ ASSERT_PASSED0(this->create_and_setup);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append_zero(this->m_data_size);
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load, clone);
+ ASSERT_EQ(0, clone.resize(0));
+ ASSERT_EQ(0, clone.resize(this->m_data_size));
+ this->verify_size_and_overlap(clone, this->m_data_size, 0);
+ this->verify_data(clone, expected_bl);
+ ASSERT_EQ(0, clone.flatten());
+ this->verify_data(clone, expected_bl);
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_PASSED(this->open_and_load_flattened, clone);
+ this->verify_size_and_overlap(clone, this->m_data_size, 0);
+ this->verify_data(clone, expected_bl);
+ }
+}
+
+#endif
+
+TEST_F(TestLibRBD, TestIOWithIOHint)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ bool skip_discard = is_skip_partial_discard_enabled(image);
+
+ char test_data[TEST_IO_SIZE + 1];
+ char zero_data[TEST_IO_SIZE + 1];
+ char mismatch_data[TEST_IO_SIZE + 1];
+ int i;
+ uint64_t mismatch_offset;
+
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+ memset(zero_data, 0, sizeof(zero_data));
+ memset(mismatch_data, 9, sizeof(mismatch_data));
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(compare_and_write_test_data, image, test_data, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, &mismatch_offset, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_compare_and_write_test_data, image, test_data, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE,
+ LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL|LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+
+ // discard 2nd, 4th sections.
+ ASSERT_PASSED(discard_test_data, image, TEST_IO_SIZE, TEST_IO_SIZE);
+ ASSERT_PASSED(aio_discard_test_data, image, TEST_IO_SIZE*3, TEST_IO_SIZE);
+
+ ASSERT_PASSED(read_test_data, image, test_data, 0, TEST_IO_SIZE,
+ LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE, TEST_IO_SIZE,
+ LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*2, TEST_IO_SIZE,
+ LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE*3, TEST_IO_SIZE,
+ LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*4, TEST_IO_SIZE, 0);
+
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ } else {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ }
+ }
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ } else {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ }
+ }
+
+ rbd_image_info_t info;
+ rbd_completion_t comp;
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ // can't read or write starting past end
+ ASSERT_EQ(-EINVAL, rbd_write(image, info.size, 1, test_data));
+ ASSERT_EQ(-EINVAL, rbd_read(image, info.size, 1, test_data));
+ // reading through end returns amount up to end
+ ASSERT_EQ(10, rbd_read2(image, info.size - 10, 100, test_data,
+ LIBRADOS_OP_FLAG_FADVISE_NOCACHE));
+ // writing through end returns amount up to end
+ ASSERT_EQ(10, rbd_write2(image, info.size - 10, 100, test_data,
+ LIBRADOS_OP_FLAG_FADVISE_DONTNEED));
+
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ ASSERT_EQ(0, rbd_aio_read2(image, info.size, 1, test_data, comp,
+ LIBRADOS_OP_FLAG_FADVISE_DONTNEED));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+
+ ASSERT_PASSED(write_test_data, image, zero_data, 0, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ mismatch_offset = 123;
+ ASSERT_EQ(-EILSEQ, rbd_compare_and_write(image, 0, TEST_IO_SIZE, mismatch_data, mismatch_data,
+ &mismatch_offset, LIBRADOS_OP_FLAG_FADVISE_DONTNEED));
+ ASSERT_EQ(0U, mismatch_offset);
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ mismatch_offset = 123;
+ ASSERT_EQ(0, rbd_aio_compare_and_write(image, 0, TEST_IO_SIZE, mismatch_data, mismatch_data,
+ comp, &mismatch_offset, LIBRADOS_OP_FLAG_FADVISE_DONTNEED));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EILSEQ, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(0U, mismatch_offset);
+ rbd_aio_release(comp);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestDataPoolIO)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ std::string data_pool_name = create_pool(true);
+
+ rbd_image_t image;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ rbd_image_options_t image_options;
+ rbd_image_options_create(&image_options);
+ BOOST_SCOPE_EXIT( (&image_options) ) {
+ rbd_image_options_destroy(image_options);
+ } BOOST_SCOPE_EXIT_END;
+
+ ASSERT_EQ(0, rbd_image_options_set_uint64(image_options,
+ RBD_IMAGE_OPTION_FEATURES,
+ features));
+ ASSERT_EQ(0, rbd_image_options_set_string(image_options,
+ RBD_IMAGE_OPTION_DATA_POOL,
+ data_pool_name.c_str()));
+
+ ASSERT_EQ(0, rbd_create4(ioctx, name.c_str(), size, image_options));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_NE(-1, rbd_get_data_pool_id(image));
+
+ bool skip_discard = is_skip_partial_discard_enabled(image);
+
+ char test_data[TEST_IO_SIZE + 1];
+ char zero_data[TEST_IO_SIZE + 1];
+ int i;
+
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+ memset(zero_data, 0, sizeof(zero_data));
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ // discard 2nd, 4th sections.
+ ASSERT_PASSED(discard_test_data, image, TEST_IO_SIZE, TEST_IO_SIZE);
+ ASSERT_PASSED(aio_discard_test_data, image, TEST_IO_SIZE*3, TEST_IO_SIZE);
+
+ ASSERT_PASSED(read_test_data, image, test_data, 0, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*2, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE*3, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*4, TEST_IO_SIZE, 0);
+
+ rbd_image_info_t info;
+ rbd_completion_t comp;
+ ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info)));
+ // can't read or write starting past end
+ ASSERT_EQ(-EINVAL, rbd_write(image, info.size, 1, test_data));
+ ASSERT_EQ(-EINVAL, rbd_read(image, info.size, 1, test_data));
+ // reading through end returns amount up to end
+ ASSERT_EQ(10, rbd_read(image, info.size - 10, 100, test_data));
+ // writing through end returns amount up to end
+ ASSERT_EQ(10, rbd_write(image, info.size - 10, 100, test_data));
+
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ ASSERT_EQ(0, rbd_aio_write(image, info.size, 1, test_data, comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+
+ rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp);
+ ASSERT_EQ(0, rbd_aio_read(image, info.size, 1, test_data, comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteMismatch)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // We only support to compare and write the same amount of (len) bytes
+ std::string cmp_buffer("This is a test");
+ std::string write_buffer("Write this !!!");
+ std::string mismatch_buffer("This will fail");
+ std::string read_buffer(cmp_buffer.length(), '1');
+
+ ssize_t written = rbd_write(image, off, cmp_buffer.length(),
+ cmp_buffer.data());
+ ASSERT_EQ(cmp_buffer.length(), written);
+
+ // Compare should fail because of mismatch
+ uint64_t mismatch_off = 0;
+ written = rbd_compare_and_write(image, off, write_buffer.length(),
+ mismatch_buffer.data(), write_buffer.data(),
+ &mismatch_off, 0);
+ ASSERT_EQ(-EILSEQ, written);
+ ASSERT_EQ(5U, mismatch_off);
+
+ // check nothing was written
+ ssize_t read = rbd_read(image, off, read_buffer.length(), read_buffer.data());
+ ASSERT_EQ(read_buffer.length(), read);
+ ASSERT_EQ(cmp_buffer, read_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteMismatch)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // We only support to compare and write the same amount of (len) bytes
+ std::string cmp_buffer("This is a test");
+ std::string write_buffer("Write this !!!");
+ std::string mismatch_buffer("This will fail");
+ std::string read_buffer(cmp_buffer.length(), '1');
+
+ ssize_t written = rbd_write(image, off, cmp_buffer.length(),
+ cmp_buffer.data());
+ ASSERT_EQ(cmp_buffer.length(), written);
+
+ // Compare should fail because of mismatch
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_write(image, off, write_buffer.length(),
+ mismatch_buffer.data(),
+ write_buffer.data(), comp,
+ &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EILSEQ, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(5U, mismatch_off);
+ rbd_aio_release(comp);
+
+ // check nothing was written
+ ssize_t read = rbd_read(image, off, read_buffer.length(), read_buffer.data());
+ ASSERT_EQ(read_buffer.length(), read);
+ ASSERT_EQ(cmp_buffer, read_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteSuccess)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // We only support to compare and write the same amount of (len) bytes
+ std::string cmp_buffer("This is a test");
+ std::string write_buffer("Write this !!!");
+ std::string read_buffer(cmp_buffer.length(), '1');
+
+ ssize_t written = rbd_write(image, off, cmp_buffer.length(),
+ cmp_buffer.data());
+ ASSERT_EQ(cmp_buffer.length(), written);
+
+ /*
+ * we compare against the written buffer (cmp_buffer) and write the buffer
+ * We expect: len bytes written
+ */
+ uint64_t mismatch_off = 0;
+ written = rbd_compare_and_write(image, off, write_buffer.length(),
+ cmp_buffer.data(), write_buffer.data(),
+ &mismatch_off, 0);
+ ASSERT_EQ(write_buffer.length(), written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ ssize_t read = rbd_read(image, off, read_buffer.length(), read_buffer.data());
+ ASSERT_EQ(read_buffer.length(), read);
+ ASSERT_EQ(write_buffer, read_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteSuccess)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // We only support to compare and write the same amount of (len) bytes
+ std::string cmp_buffer("This is a test");
+ std::string write_buffer("Write this !!!");
+ std::string read_buffer(cmp_buffer.length(), '1');
+
+ ssize_t written = rbd_write(image, off, cmp_buffer.length(),
+ cmp_buffer.data());
+ ASSERT_EQ(cmp_buffer.length(), written);
+
+ /*
+ * we compare against the written buffer (cmp_buffer) and write the buffer
+ * We expect: len bytes written
+ */
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_write(image, off, write_buffer.length(),
+ cmp_buffer.data(), write_buffer.data(),
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(0U, mismatch_off);
+ rbd_aio_release(comp);
+
+ ssize_t read = rbd_read(image, off, read_buffer.length(), read_buffer.data());
+ ASSERT_EQ(read_buffer.length(), read);
+ ASSERT_EQ(write_buffer, read_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+
+TEST_F(TestLibRBD, TestCompareAndWriteStripeUnitUnaligned)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit;
+ rbd_get_stripe_unit(image, &stripe_unit);
+ std::string large_write_buffer(stripe_unit, '2');
+ std::string large_cmp_buffer(stripe_unit * 2, '4');
+
+ ssize_t written = rbd_write(image, stripe_unit, large_cmp_buffer.length(),
+ large_cmp_buffer.data());
+ ASSERT_EQ(large_cmp_buffer.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit + 1 and stripe unit size
+ * Expect fail because access exceeds stripe (unaligned)
+ */
+ uint64_t mismatch_off = 0;
+ written = rbd_compare_and_write(image, stripe_unit + 1, stripe_unit,
+ large_cmp_buffer.data(),
+ large_write_buffer.data(),
+ &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check nothing has been written
+ std::string large_read_buffer(large_cmp_buffer.length(), '5');
+ ssize_t read = rbd_read(image, stripe_unit, large_read_buffer.length(),
+ large_read_buffer.data());
+ ASSERT_EQ(large_read_buffer.length(), read);
+ auto buffer_mismatch = std::mismatch(large_cmp_buffer.begin(),
+ large_cmp_buffer.end(),
+ large_read_buffer.begin());
+ ASSERT_EQ(large_read_buffer.end(), buffer_mismatch.second);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteStripeUnitUnaligned)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit;
+ rbd_get_stripe_unit(image, &stripe_unit);
+ std::string large_write_buffer(stripe_unit, '2');
+ std::string large_cmp_buffer(stripe_unit * 2, '4');
+
+ ssize_t written = rbd_write(image, stripe_unit, large_cmp_buffer.length(),
+ large_cmp_buffer.data());
+ ASSERT_EQ(large_cmp_buffer.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit + 1 and stripe unit size
+ * Expect fail because access spans stripe unit boundary (unaligned)
+ */
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_write(image, stripe_unit + 1, stripe_unit,
+ large_cmp_buffer.data(),
+ large_write_buffer.data(),
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(0U, mismatch_off);
+ rbd_aio_release(comp);
+
+ // check nothing has been written
+ std::string large_read_buffer(large_cmp_buffer.length(), '5');
+ ssize_t read = rbd_read(image, stripe_unit, large_read_buffer.length(),
+ large_read_buffer.data());
+ ASSERT_EQ(large_read_buffer.length(), read);
+ auto buffer_mismatch = std::mismatch(large_cmp_buffer.begin(),
+ large_cmp_buffer.end(),
+ large_read_buffer.begin());
+ ASSERT_EQ(large_read_buffer.end(), buffer_mismatch.second);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteTooLarge)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit;
+ rbd_get_stripe_unit(image, &stripe_unit);
+ std::string large_write_buffer(stripe_unit * 2, '2');
+ std::string large_cmp_buffer(large_write_buffer.length(), '4');
+
+ ssize_t written = rbd_write(image, stripe_unit, large_cmp_buffer.length(),
+ large_cmp_buffer.data());
+ ASSERT_EQ(large_cmp_buffer.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit and stripe unit size + 1
+ * Expect fail because access is larger than stripe unit size
+ */
+ uint64_t mismatch_off = 0;
+ written = rbd_compare_and_write(image, stripe_unit, stripe_unit + 1,
+ large_cmp_buffer.data(),
+ large_write_buffer.data(),
+ &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check nothing has been written
+ std::string large_read_buffer(large_cmp_buffer.length(), '5');
+ ssize_t read = rbd_read(image, stripe_unit, large_read_buffer.length(),
+ large_read_buffer.data());
+ ASSERT_EQ(large_read_buffer.length(), read);
+ auto buffer_mismatch = std::mismatch(large_cmp_buffer.begin(),
+ large_cmp_buffer.end(),
+ large_read_buffer.begin());
+ ASSERT_EQ(large_read_buffer.end(), buffer_mismatch.second);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteTooLarge)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit;
+ rbd_get_stripe_unit(image, &stripe_unit);
+ std::string large_write_buffer(stripe_unit * 2, '2');
+ std::string large_cmp_buffer(large_write_buffer.length(), '4');
+
+ ssize_t written = rbd_write(image, stripe_unit, large_cmp_buffer.length(),
+ large_cmp_buffer.data());
+ ASSERT_EQ(large_cmp_buffer.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit and stripe unit size + 1
+ * Expect fail because access is larger than stripe unit size
+ */
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_write(image, stripe_unit, stripe_unit + 1,
+ large_cmp_buffer.data(),
+ large_write_buffer.data(),
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(0U, mismatch_off);
+ rbd_aio_release(comp);
+
+ // check nothing has been written
+ std::string large_read_buffer(large_cmp_buffer.length(), '5');
+ ssize_t read = rbd_read(image, stripe_unit, large_read_buffer.length(),
+ large_read_buffer.data());
+ ASSERT_EQ(large_read_buffer.length(), read);
+ auto buffer_mismatch = std::mismatch(large_cmp_buffer.begin(),
+ large_cmp_buffer.end(),
+ large_read_buffer.begin());
+ ASSERT_EQ(large_read_buffer.end(), buffer_mismatch.second);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteStripeUnitSuccess)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit;
+ rbd_get_stripe_unit(image, &stripe_unit);
+ std::string large_write_buffer(stripe_unit, '2');
+ std::string large_cmp_buffer(stripe_unit * 2, '4');
+
+ ssize_t written = rbd_write(image, stripe_unit, large_cmp_buffer.length(),
+ large_cmp_buffer.data());
+ ASSERT_EQ(large_cmp_buffer.length(), written);
+
+ // aligned stripe unit size access => expect success
+ uint64_t mismatch_off = 0;
+ written = rbd_compare_and_write(image, stripe_unit, stripe_unit,
+ large_cmp_buffer.data(),
+ large_write_buffer.data(),
+ &mismatch_off, 0);
+ ASSERT_EQ(stripe_unit, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check stripe_unit bytes of large_write_buffer were written
+ std::string large_read_buffer(large_cmp_buffer.length(), '5');
+ ssize_t read = rbd_read(image, stripe_unit, large_read_buffer.length(),
+ large_read_buffer.data());
+ ASSERT_EQ(large_read_buffer.length(), read);
+ auto buffer_mismatch = std::mismatch(large_read_buffer.begin(),
+ large_read_buffer.begin() + stripe_unit,
+ large_write_buffer.begin());
+ ASSERT_EQ(large_write_buffer.end(), buffer_mismatch.second);
+ // check data beyond stripe_unit size was not overwritten
+ buffer_mismatch = std::mismatch(large_read_buffer.begin() + stripe_unit,
+ large_read_buffer.end(),
+ large_cmp_buffer.begin());
+ ASSERT_EQ(large_cmp_buffer.begin() + stripe_unit, buffer_mismatch.second);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteStripeUnitSuccess)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit;
+ rbd_get_stripe_unit(image, &stripe_unit);
+ std::string large_write_buffer(stripe_unit, '2');
+ std::string large_cmp_buffer(stripe_unit * 2, '4');
+
+ ssize_t written = rbd_write(image, stripe_unit, large_cmp_buffer.length(),
+ large_cmp_buffer.data());
+ ASSERT_EQ(large_cmp_buffer.length(), written);
+
+ // aligned stripe unit size access => expect success
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_write(image, stripe_unit, stripe_unit,
+ large_cmp_buffer.data(),
+ large_write_buffer.data(),
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(0U, mismatch_off);
+ rbd_aio_release(comp);
+
+ // check stripe_unit bytes of large_write_buffer were written
+ std::string large_read_buffer(large_cmp_buffer.length(), '5');
+ ssize_t read = rbd_read(image, stripe_unit, large_read_buffer.length(),
+ large_read_buffer.data());
+ ASSERT_EQ(large_read_buffer.length(), read);
+ auto buffer_mismatch = std::mismatch(large_read_buffer.begin(),
+ large_read_buffer.begin() + stripe_unit,
+ large_write_buffer.begin());
+ ASSERT_EQ(large_write_buffer.end(), buffer_mismatch.second);
+ // check data beyond stripe_unit size was not overwritten
+ buffer_mismatch = std::mismatch(large_read_buffer.begin() + stripe_unit,
+ large_read_buffer.end(),
+ large_cmp_buffer.begin());
+ ASSERT_EQ(large_cmp_buffer.begin() + stripe_unit, buffer_mismatch.second);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteVIovecLenDiffers)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ std::string cmp_buffer("This is a test");
+ size_t cmp_len = cmp_buffer.length();
+
+ std::string write_buffer("Write this !!!");
+ struct iovec write_iovs[] = {
+ {.iov_base = &write_buffer[0], .iov_len = 6},
+ {.iov_base = &write_buffer[6], .iov_len = 5},
+ {.iov_base = &write_buffer[11], .iov_len = 3}
+ };
+
+ ASSERT_EQ(cmp_len, rbd_write(image, off, cmp_len, cmp_buffer.data()));
+
+ // should fail because compare iovec len cannot be different to write iovec len
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_writev(image, off,
+ write_iovs /* cmp_iovs */, 1,
+ write_iovs, std::size(write_iovs),
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, ret);
+ ASSERT_EQ(0U, mismatch_off);
+ rbd_aio_release(comp);
+
+ // check nothing was written
+ std::string read_buffer(cmp_buffer.length(), '1');
+ ssize_t read = rbd_read(image, off, read_buffer.length(), read_buffer.data());
+ ASSERT_EQ(read_buffer.length(), read);
+ ASSERT_EQ(cmp_buffer, read_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteVMismatch)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ std::string cmp_buffer("This is a test");
+ int cmp_len = cmp_buffer.length();
+
+ std::string write_buffer("Write this !!!");
+ struct iovec write_iovs[] = {
+ {.iov_base = &write_buffer[0], .iov_len = 6},
+ {.iov_base = &write_buffer[6], .iov_len = 5},
+ {.iov_base = &write_buffer[11], .iov_len = 3}
+ };
+
+ std::string mismatch_buffer("This will fail");
+ struct iovec mismatch_iovs[] = {
+ {.iov_base = &mismatch_buffer[0], .iov_len = 5},
+ {.iov_base = &mismatch_buffer[5], .iov_len = 5},
+ {.iov_base = &mismatch_buffer[10], .iov_len = 4}
+ };
+
+ ASSERT_EQ(cmp_len, rbd_write(image, off, cmp_len, cmp_buffer.data()));
+
+ // this should execute the compare but fail because of mismatch
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_writev(image, off,
+ mismatch_iovs /* cmp_iovs */,
+ std::size(mismatch_iovs),
+ write_iovs, std::size(write_iovs),
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(-EILSEQ, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(5U, mismatch_off);
+ rbd_aio_release(comp);
+
+ // check nothing was written
+ std::string read_buffer(cmp_buffer.length(), '1');
+ ssize_t read = rbd_read(image, off, read_buffer.length(), read_buffer.data());
+ ASSERT_EQ(read_buffer.length(), read);
+ ASSERT_EQ(cmp_buffer, read_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteVSuccess)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ std::string cmp_buffer("This is a test");
+ struct iovec cmp_iovs[] = {
+ {.iov_base = &cmp_buffer[0], .iov_len = 5},
+ {.iov_base = &cmp_buffer[5], .iov_len = 3},
+ {.iov_base = &cmp_buffer[8], .iov_len = 2},
+ {.iov_base = &cmp_buffer[10], .iov_len = 4}
+ };
+ size_t cmp_len = cmp_buffer.length();
+
+ std::string write_buffer("Write this !!!");
+ struct iovec write_iovs[] = {
+ {.iov_base = &write_buffer[0], .iov_len = 6},
+ {.iov_base = &write_buffer[6], .iov_len = 5},
+ {.iov_base = &write_buffer[11], .iov_len = 3}
+ };
+
+ ASSERT_EQ(cmp_len, rbd_write(image, off, cmp_len, cmp_buffer.data()));
+
+ // compare against the buffer written before => should succeed
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ uint64_t mismatch_off = 0;
+ int ret = rbd_aio_compare_and_writev(image, off,
+ cmp_iovs, std::size(cmp_iovs),
+ write_iovs, std::size(write_iovs),
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rbd_aio_get_return_value(comp));
+ ASSERT_EQ(0U, mismatch_off);
+ rbd_aio_release(comp);
+
+ // check data was successfully written
+ std::string read_buffer(cmp_buffer.length(), '1');
+ ssize_t read = rbd_read(image, off, read_buffer.length(), read_buffer.data());
+ ASSERT_EQ(read_buffer.length(), read);
+ ASSERT_EQ(write_buffer, read_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestScatterGatherIO)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ std::string write_buffer("This is a test");
+ // These iovecs should produce a length overflow
+ struct iovec bad_iovs[] = {
+ {.iov_base = &write_buffer[0], .iov_len = 5},
+ {.iov_base = NULL, .iov_len = std::numeric_limits<size_t>::max()}
+ };
+ struct iovec write_iovs[] = {
+ {.iov_base = &write_buffer[0], .iov_len = 5},
+ {.iov_base = &write_buffer[5], .iov_len = 3},
+ {.iov_base = &write_buffer[8], .iov_len = 2},
+ {.iov_base = &write_buffer[10], .iov_len = 4}
+ };
+
+ rbd_completion_t comp;
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ ASSERT_EQ(-EINVAL, rbd_aio_writev(image, write_iovs, 0, 0, comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_writev(image, bad_iovs, 2, 0, comp));
+ ASSERT_EQ(0, rbd_aio_writev(image, write_iovs,
+ sizeof(write_iovs) / sizeof(struct iovec),
+ 1<<order, comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(0, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+
+ std::string read_buffer(write_buffer.size(), '1');
+ struct iovec read_iovs[] = {
+ {.iov_base = &read_buffer[0], .iov_len = 4},
+ {.iov_base = &read_buffer[8], .iov_len = 4},
+ {.iov_base = &read_buffer[12], .iov_len = 2}
+ };
+
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ ASSERT_EQ(-EINVAL, rbd_aio_readv(image, read_iovs, 0, 0, comp));
+ ASSERT_EQ(-EINVAL, rbd_aio_readv(image, bad_iovs, 2, 0, comp));
+ ASSERT_EQ(0, rbd_aio_readv(image, read_iovs,
+ sizeof(read_iovs) / sizeof(struct iovec),
+ 1<<order, comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(10, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+ ASSERT_EQ("This1111 is a ", read_buffer);
+
+ std::string linear_buffer(write_buffer.size(), '1');
+ struct iovec linear_iovs[] = {
+ {.iov_base = &linear_buffer[4], .iov_len = 4}
+ };
+ rbd_aio_create_completion(NULL, NULL, &comp);
+ ASSERT_EQ(0, rbd_aio_readv(image, linear_iovs,
+ sizeof(linear_iovs) / sizeof(struct iovec),
+ 1<<order, comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comp));
+ ASSERT_EQ(4, rbd_aio_get_return_value(comp));
+ rbd_aio_release(comp);
+ ASSERT_EQ("1111This111111", linear_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestEmptyDiscard)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_PASSED(aio_discard_test_data, image, 0, 1*1024*1024);
+ ASSERT_PASSED(aio_discard_test_data, image, 0, 4*1024*1024);
+ ASSERT_PASSED(aio_discard_test_data, image, 3*1024*1024, 1*1024*1024);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestFUA)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image_write;
+ rbd_image_t image_read;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image_write, NULL));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image_read, NULL));
+
+ // enable writeback cache
+ rbd_flush(image_write);
+
+ char test_data[TEST_IO_SIZE + 1];
+ int i;
+
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image_write, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_FUA);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(read_test_data, image_read, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data, image_write, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_FUA);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data, image_read, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ ASSERT_PASSED(validate_object_map, image_write);
+ ASSERT_PASSED(validate_object_map, image_read);
+ ASSERT_EQ(0, rbd_close(image_write));
+ ASSERT_EQ(0, rbd_close(image_read));
+ ASSERT_EQ(0, rbd_remove(ioctx, name.c_str()));
+ rados_ioctx_destroy(ioctx);
+}
+
+void simple_write_cb_pp(librbd::completion_t cb, void *arg)
+{
+ cout << "write completion cb called!" << std::endl;
+}
+
+void simple_read_cb_pp(librbd::completion_t cb, void *arg)
+{
+ cout << "read completion cb called!" << std::endl;
+}
+
+void aio_write_test_data(librbd::Image& image, const char *test_data,
+ off_t off, uint32_t iohint, bool *passed)
+{
+ ceph::bufferlist bl;
+ bl.append(test_data, strlen(test_data));
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp);
+ printf("created completion\n");
+ if (iohint)
+ image.aio_write2(off, strlen(test_data), bl, comp, iohint);
+ else
+ image.aio_write(off, strlen(test_data), bl, comp);
+ printf("started write\n");
+ comp->wait_for_complete();
+ int r = comp->get_return_value();
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(0, r);
+ printf("finished write\n");
+ comp->release();
+ *passed = true;
+}
+
+void aio_discard_test_data(librbd::Image& image, off_t off, size_t len, bool *passed)
+{
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp);
+ image.aio_discard(off, len, comp);
+ comp->wait_for_complete();
+ int r = comp->get_return_value();
+ ASSERT_EQ(0, r);
+ comp->release();
+ *passed = true;
+}
+
+void write_test_data(librbd::Image& image, const char *test_data, off_t off, uint32_t iohint, bool *passed)
+{
+ size_t written;
+ size_t len = strlen(test_data);
+ ceph::bufferlist bl;
+ bl.append(test_data, len);
+ if (iohint)
+ written = image.write2(off, len, bl, iohint);
+ else
+ written = image.write(off, len, bl);
+ printf("wrote: %u\n", (unsigned int) written);
+ ASSERT_EQ(bl.length(), written);
+ *passed = true;
+}
+
+void discard_test_data(librbd::Image& image, off_t off, size_t len, bool *passed)
+{
+ size_t written;
+ written = image.discard(off, len);
+ printf("discard: %u~%u\n", (unsigned)off, (unsigned)len);
+ ASSERT_EQ(len, written);
+ *passed = true;
+}
+
+void aio_read_test_data(librbd::Image& image, const char *expected, off_t off, size_t expected_len, uint32_t iohint, bool *passed)
+{
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_read_cb_pp);
+ ceph::bufferlist bl;
+ printf("created completion\n");
+ if (iohint)
+ image.aio_read2(off, expected_len, bl, comp, iohint);
+ else
+ image.aio_read(off, expected_len, bl, comp);
+ printf("started read\n");
+ comp->wait_for_complete();
+ int r = comp->get_return_value();
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(TEST_IO_SIZE, r);
+ ASSERT_EQ(0, memcmp(expected, bl.c_str(), TEST_IO_SIZE));
+ printf("finished read\n");
+ comp->release();
+ *passed = true;
+}
+
+void read_test_data(librbd::Image& image, const char *expected, off_t off, size_t expected_len, uint32_t iohint, bool *passed)
+{
+ int read;
+ size_t len = expected_len;
+ ceph::bufferlist bl;
+ if (iohint)
+ read = image.read2(off, len, bl, iohint);
+ else
+ read = image.read(off, len, bl);
+ ASSERT_TRUE(read >= 0);
+ std::string bl_str(bl.c_str(), read);
+
+ printf("read: %u\n", (unsigned int) read);
+ int result = memcmp(bl_str.c_str(), expected, expected_len);
+ if (result != 0) {
+ printf("read: %s\nexpected: %s\n", bl_str.c_str(), expected);
+ ASSERT_EQ(0, result);
+ }
+ *passed = true;
+}
+
+void aio_writesame_test_data(librbd::Image& image, const char *test_data, off_t off,
+ size_t len, size_t data_len, uint32_t iohint, bool *passed)
+{
+ ceph::bufferlist bl;
+ bl.append(test_data, data_len);
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp);
+ printf("created completion\n");
+ int r;
+ r = image.aio_writesame(off, len, bl, comp, iohint);
+ printf("started writesame\n");
+ if (len % data_len) {
+ ASSERT_EQ(-EINVAL, r);
+ printf("expected fail, finished writesame\n");
+ comp->release();
+ *passed = true;
+ return;
+ }
+
+ comp->wait_for_complete();
+ r = comp->get_return_value();
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(0, r);
+ printf("finished writesame\n");
+ comp->release();
+
+ //verify data
+ printf("to verify the data\n");
+ int read;
+ uint64_t left = len;
+ while (left > 0) {
+ ceph::bufferlist bl;
+ read = image.read(off, data_len, bl);
+ ASSERT_EQ(data_len, static_cast<size_t>(read));
+ std::string bl_str(bl.c_str(), read);
+ int result = memcmp(bl_str.c_str(), test_data, data_len);
+ if (result !=0 ) {
+ printf("read: %u ~ %u\n", (unsigned int) off, (unsigned int) read);
+ printf("read: %s\nexpected: %s\n", bl_str.c_str(), test_data);
+ ASSERT_EQ(0, result);
+ }
+ off += data_len;
+ left -= data_len;
+ }
+ ASSERT_EQ(0U, left);
+ printf("verified\n");
+
+ *passed = true;
+}
+
+void writesame_test_data(librbd::Image& image, const char *test_data, off_t off,
+ ssize_t len, size_t data_len, uint32_t iohint,
+ bool *passed)
+{
+ ssize_t written;
+ ceph::bufferlist bl;
+ bl.append(test_data, data_len);
+ written = image.writesame(off, len, bl, iohint);
+ if (len % data_len) {
+ ASSERT_EQ(-EINVAL, written);
+ printf("expected fail, finished writesame\n");
+ *passed = true;
+ return;
+ }
+ ASSERT_EQ(len, written);
+ printf("wrote: %u\n", (unsigned int) written);
+ *passed = true;
+
+ //verify data
+ printf("to verify the data\n");
+ int read;
+ uint64_t left = len;
+ while (left > 0) {
+ ceph::bufferlist bl;
+ read = image.read(off, data_len, bl);
+ ASSERT_EQ(data_len, static_cast<size_t>(read));
+ std::string bl_str(bl.c_str(), read);
+ int result = memcmp(bl_str.c_str(), test_data, data_len);
+ if (result !=0 ) {
+ printf("read: %u ~ %u\n", (unsigned int) off, (unsigned int) read);
+ printf("read: %s\nexpected: %s\n", bl_str.c_str(), test_data);
+ ASSERT_EQ(0, result);
+ }
+ off += data_len;
+ left -= data_len;
+ }
+ ASSERT_EQ(0U, left);
+ printf("verified\n");
+
+ *passed = true;
+}
+
+void aio_compare_and_write_test_data(librbd::Image& image, const char *cmp_data,
+ const char *test_data, off_t off, ssize_t len,
+ uint32_t iohint, bool *passed)
+{
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(cmp_data, strlen(cmp_data));
+ ceph::bufferlist test_bl;
+ test_bl.append(test_data, strlen(test_data));
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp);
+ printf("created completion\n");
+
+ uint64_t mismatch_offset;
+ image.aio_compare_and_write(off, len, cmp_bl, test_bl, comp, &mismatch_offset, iohint);
+ printf("started aio compare and write\n");
+ comp->wait_for_complete();
+ int r = comp->get_return_value();
+ printf("return value is: %d\n", r);
+ ASSERT_EQ(0, r);
+ printf("finished aio compare and write\n");
+ comp->release();
+ *passed = true;
+}
+
+void compare_and_write_test_data(librbd::Image& image, const char *cmp_data, const char *test_data,
+ off_t off, ssize_t len, uint64_t *mismatch_off, uint32_t iohint, bool *passed)
+{
+ size_t written;
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(cmp_data, strlen(cmp_data));
+ ceph::bufferlist test_bl;
+ test_bl.append(test_data, strlen(test_data));
+ printf("start compare and write\n");
+ written = image.compare_and_write(off, len, cmp_bl, test_bl, mismatch_off, iohint);
+ printf("compare and wrote: %d\n", (int) written);
+ ASSERT_EQ(len, static_cast<ssize_t>(written));
+ *passed = true;
+}
+
+TEST_F(TestLibRBD, TestIOPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
+ char test_data[TEST_IO_SIZE + 1];
+ char zero_data[TEST_IO_SIZE + 1];
+ int i;
+ uint64_t mismatch_offset;
+
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+ memset(zero_data, 0, sizeof(zero_data));
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image, test_data, strlen(test_data) * i, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data, image, test_data, strlen(test_data) * i, 0);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(compare_and_write_test_data, image, test_data, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, &mismatch_offset, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_compare_and_write_test_data, image, test_data, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE, 0);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(read_test_data, image, test_data, strlen(test_data) * i, TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data, image, test_data, strlen(test_data) * i, TEST_IO_SIZE, 0);
+
+ // discard 2nd, 4th sections.
+ ASSERT_PASSED(discard_test_data, image, TEST_IO_SIZE, TEST_IO_SIZE);
+ ASSERT_PASSED(aio_discard_test_data, image, TEST_IO_SIZE*3, TEST_IO_SIZE);
+
+ ASSERT_PASSED(read_test_data, image, test_data, 0, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*2, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, skip_discard ? test_data : zero_data,
+ TEST_IO_SIZE*3, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(read_test_data, image, test_data, TEST_IO_SIZE*4, TEST_IO_SIZE, 0);
+
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, 0);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ } else {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ }
+ }
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, 0);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE + i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ } else {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE * i, TEST_IO_SIZE * i * 32, TEST_IO_SIZE, 0);
+ }
+ }
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+static void compare_written(librbd::Image& image, off_t off, size_t len,
+ const std::string& buffer, bool *passed)
+{
+ bufferlist read_bl;
+ ssize_t read = image.read(off, len, read_bl);
+ ASSERT_EQ(len, read);
+ std::string read_buffer(read_bl.c_str(), read);
+ ASSERT_EQ(buffer.substr(0, len), read_buffer);
+ *passed = true;
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteCompareTooSmallPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ std::string small_buffer("Too small");
+ ceph::bufferlist small_bl;
+ small_bl.append(&small_buffer[0], 4);
+ small_bl.append(&small_buffer[4], 4);
+
+ // should fail because compare bufferlist cannot be smaller than len
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, cmp_bl.length(),
+ small_bl, /* cmp_bl */
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteCompareTooSmallPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ std::string small_buffer("Too small");
+ ceph::bufferlist small_bl;
+ small_bl.append(&small_buffer[0], 4);
+ small_bl.append(&small_buffer[4], 4);
+
+ // should fail because compare bufferlist cannot be smaller than len
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, cmp_bl.length(),
+ small_bl, /* cmp_bl */
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteWriteTooSmallPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ std::string small_buffer("Too small");
+ ceph::bufferlist small_bl;
+ small_bl.append(&small_buffer[0], 4);
+ small_bl.append(&small_buffer[4], 4);
+
+ // should fail because write bufferlist cannot be smaller than len
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, cmp_bl.length(),
+ cmp_bl,
+ small_bl, /* write_bl */
+ &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteWriteTooSmallPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ std::string small_buffer("Too small");
+ ceph::bufferlist small_bl;
+ small_bl.append(&small_buffer[0], 4);
+ small_bl.append(&small_buffer[4], 4);
+
+ // should fail because write bufferlist cannot be smaller than len
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, cmp_bl.length(),
+ cmp_bl,
+ small_bl, /* write_bl */
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteMismatchPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // this should execute the compare but fail because of mismatch
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, write_bl.length(),
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(-EILSEQ, written);
+ ASSERT_EQ(5U, mismatch_off);
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteMismatchPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // this should execute the compare but fail because of mismatch
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, write_bl.length(),
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(-EILSEQ, aio_ret);
+ ASSERT_EQ(5U, mismatch_off);
+ comp->release();
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteMismatchBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ /*
+ * we allow cmp_bl and write_bl to be greater than len so this
+ * should execute the compare but fail because of mismatch
+ */
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, cmp_bl.length() - 1,
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(-EILSEQ, written);
+ ASSERT_EQ(5U, mismatch_off);
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteMismatchBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ /*
+ * we allow cmp_bl and write_bl to be greater than len so this
+ * should execute the compare but fail because of mismatch
+ */
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, cmp_bl.length() - 1,
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(-EILSEQ, aio_ret);
+ ASSERT_EQ(5U, mismatch_off);
+ comp->release();
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteSuccessPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // compare against the buffer written before => should succeed
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, cmp_bl.length(),
+ cmp_bl,
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(write_bl.length(), written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check write_bl was written
+ ASSERT_PASSED(compare_written, image, off, write_bl.length(), write_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteSuccessPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // compare against the buffer written before => should succeed
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, write_bl.length(),
+ cmp_bl,
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(0, aio_ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ // check write_bl was written
+ ASSERT_PASSED(compare_written, image, off, write_bl.length(), write_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteSuccessBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ /*
+ * Test len < cmp_bl & write_bl => should succeed but only compare
+ * len bytes resp. only write len bytes
+ */
+ ssize_t written = image.write(off, mismatch_bl.length(), mismatch_bl);
+ ASSERT_EQ(mismatch_bl.length(), written);
+
+ size_t len_m1 = cmp_bl.length() - 1;
+ written = image.write(off, len_m1, cmp_bl);
+ ASSERT_EQ(len_m1, written);
+ // the content of the image at off should now be "This is a tesl"
+
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, len_m1,
+ cmp_bl,
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(len_m1, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check that only write_bl.length() - 1 bytes were written
+ ASSERT_PASSED(compare_written, image, off, len_m1, write_buffer);
+ ASSERT_PASSED(compare_written, image, off + len_m1, 1, std::string("l"));
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteSuccessBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ /*
+ * Test len < cmp_bl & write_bl => should succeed but only compare
+ * len bytes resp. only write len bytes
+ */
+ ssize_t written = image.write(off, mismatch_bl.length(), mismatch_bl);
+ ASSERT_EQ(mismatch_bl.length(), written);
+
+ size_t len_m1 = cmp_bl.length() - 1;
+ written = image.write(off, len_m1, cmp_bl);
+ ASSERT_EQ(len_m1, written);
+ // the content of the image at off should now be "This is a tesl"
+
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, len_m1,
+ cmp_bl,
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(0, aio_ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ // check that only write_bl.length() - 1 bytes were written
+ ASSERT_PASSED(compare_written, image, off, len_m1, write_buffer);
+ ASSERT_PASSED(compare_written, image, off + len_m1, 1, std::string("l"));
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteStripeUnitUnalignedPP)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit = image.get_stripe_unit();
+ std::string large_write_buffer(stripe_unit, '2');
+ ceph::bufferlist large_write_bl;
+ large_write_bl.append(large_write_buffer.data(),
+ large_write_buffer.length());
+
+ std::string large_cmp_buffer(stripe_unit * 2, '3');
+ ceph::bufferlist large_cmp_bl;
+ large_cmp_bl.append(large_cmp_buffer.data(), large_cmp_buffer.length());
+
+ ssize_t written = image.write(stripe_unit, large_cmp_bl.length(),
+ large_cmp_bl);
+ ASSERT_EQ(large_cmp_bl.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit + 1 and stripe unit size
+ * Expect fail because access exceeds stripe
+ */
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(stripe_unit + 1, stripe_unit,
+ large_cmp_bl,
+ large_write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check nothing has been written
+ ASSERT_PASSED(compare_written, image, stripe_unit, large_cmp_bl.length(),
+ large_cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteStripeUnitUnalignedPP)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit = image.get_stripe_unit();
+ std::string large_write_buffer(stripe_unit, '2');
+ ceph::bufferlist large_write_bl;
+ large_write_bl.append(large_write_buffer.data(),
+ large_write_buffer.length());
+
+ std::string large_cmp_buffer(stripe_unit * 2, '3');
+ ceph::bufferlist large_cmp_bl;
+ large_cmp_bl.append(large_cmp_buffer.data(), large_cmp_buffer.length());
+
+ ssize_t written = image.write(stripe_unit, large_cmp_bl.length(),
+ large_cmp_bl);
+ ASSERT_EQ(large_cmp_bl.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit + 1 and stripe unit size
+ * Expect fail because access exceeds stripe
+ */
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(stripe_unit + 1, stripe_unit,
+ large_cmp_bl,
+ large_write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(-EINVAL, aio_ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ // check nothing has been written
+ ASSERT_PASSED(compare_written, image, stripe_unit, large_cmp_bl.length(),
+ large_cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteTooLargePP)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit = image.get_stripe_unit();
+ std::string large_write_buffer(stripe_unit * 2, '2');
+ ceph::bufferlist large_write_bl;
+ large_write_bl.append(large_write_buffer.data(),
+ large_write_buffer.length());
+
+ std::string large_cmp_buffer(stripe_unit * 2, '3');
+ ceph::bufferlist large_cmp_bl;
+ large_cmp_bl.append(large_cmp_buffer.data(), large_cmp_buffer.length());
+
+ ssize_t written = image.write(stripe_unit, large_cmp_bl.length(),
+ large_cmp_bl);
+ ASSERT_EQ(large_cmp_bl.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit and stripe unit size + 1
+ * Expect fail because access is larger than stripe unit size
+ */
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(stripe_unit, stripe_unit + 1,
+ large_cmp_bl,
+ large_write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check nothing has been written
+ ASSERT_PASSED(compare_written, image, stripe_unit, large_cmp_bl.length(),
+ large_cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteTooLargePP)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit = image.get_stripe_unit();
+ std::string large_write_buffer(stripe_unit * 2, '2');
+ ceph::bufferlist large_write_bl;
+ large_write_bl.append(large_write_buffer.data(),
+ large_write_buffer.length());
+
+ std::string large_cmp_buffer(stripe_unit * 2, '3');
+ ceph::bufferlist large_cmp_bl;
+ large_cmp_bl.append(large_cmp_buffer.data(), large_cmp_buffer.length());
+
+ ssize_t written = image.write(stripe_unit, large_cmp_bl.length(),
+ large_cmp_bl);
+ ASSERT_EQ(large_cmp_bl.length(), written);
+
+ /*
+ * compare and write at offset stripe_unit and stripe unit size + 1
+ * Expect fail because access is larger than stripe unit size
+ */
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(stripe_unit, stripe_unit + 1,
+ large_cmp_bl,
+ large_write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(-EINVAL, aio_ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ // check nothing has been written
+ ASSERT_PASSED(compare_written, image, stripe_unit, large_cmp_bl.length(),
+ large_cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteStripeUnitSuccessPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit = image.get_stripe_unit();
+ std::string large_write_buffer(stripe_unit * 2, '2');
+ ceph::bufferlist large_write_bl;
+ large_write_bl.append(large_write_buffer.data(),
+ large_write_buffer.length());
+
+ std::string large_cmp_buffer(stripe_unit * 2, '3');
+ ceph::bufferlist large_cmp_bl;
+ large_cmp_bl.append(large_cmp_buffer.data(), large_cmp_buffer.length());
+
+ ssize_t written = image.write(stripe_unit, large_cmp_bl.length(),
+ large_cmp_bl);
+ ASSERT_EQ(large_cmp_bl.length(), written);
+
+ // aligned stripe unit size access => expect success
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(stripe_unit, stripe_unit,
+ large_cmp_bl,
+ large_write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(stripe_unit, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check large_write_bl was written and nothing beyond
+ ASSERT_PASSED(compare_written, image, stripe_unit, stripe_unit,
+ large_write_buffer);
+ ASSERT_PASSED(compare_written, image, stripe_unit * 2, stripe_unit,
+ large_cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteStripeUnitSuccessPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // large write test => we allow stripe unit size writes (aligned)
+ uint64_t stripe_unit = image.get_stripe_unit();
+ std::string large_write_buffer(stripe_unit * 2, '2');
+ ceph::bufferlist large_write_bl;
+ large_write_bl.append(large_write_buffer.data(),
+ large_write_buffer.length());
+
+ std::string large_cmp_buffer(stripe_unit * 2, '3');
+ ceph::bufferlist large_cmp_bl;
+ large_cmp_bl.append(large_cmp_buffer.data(),
+ large_cmp_buffer.length());
+
+ ssize_t written = image.write(stripe_unit, large_cmp_bl.length(),
+ large_cmp_bl);
+ ASSERT_EQ(large_cmp_bl.length(), written);
+
+ // aligned stripe unit size access => expect success
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(stripe_unit, stripe_unit,
+ large_cmp_bl,
+ large_write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(0, aio_ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ // check large_write_bl was written and nothing beyond
+ ASSERT_PASSED(compare_written, image, stripe_unit, stripe_unit,
+ large_write_buffer);
+ ASSERT_PASSED(compare_written, image, stripe_unit * 2, stripe_unit,
+ large_cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestIOPPWithIOHint)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ char test_data[TEST_IO_SIZE + 1];
+ char zero_data[TEST_IO_SIZE + 1];
+ test_data[TEST_IO_SIZE] = '\0';
+ int i;
+
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ memset(zero_data, 0, sizeof(zero_data));
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image, test_data, strlen(test_data) * i,
+ LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data, image, test_data, strlen(test_data) * i,
+ LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+
+ ASSERT_PASSED(read_test_data, image, test_data, strlen(test_data),
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data, image, test_data, strlen(test_data) * i,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL|LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE + i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE + i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ } else {
+ ASSERT_PASSED(writesame_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_PASSED(writesame_test_data, image, zero_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+ }
+ }
+ for (i = 0; i < 15; ++i) {
+ if (i % 3 == 2) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32 + i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ } else if (i % 3 == 1) {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE + i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE + i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ } else {
+ ASSERT_PASSED(aio_writesame_test_data, image, test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ ASSERT_PASSED(aio_writesame_test_data, image, zero_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE * i * 32, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+ }
+ }
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+
+
+TEST_F(TestLibRBD, TestIOToSnapshot)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t isize = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), isize, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ int i, r;
+ rbd_image_t image_at_snap;
+ char orig_data[TEST_IO_TO_SNAP_SIZE + 1];
+ char test_data[TEST_IO_TO_SNAP_SIZE + 1];
+
+ for (i = 0; i < TEST_IO_TO_SNAP_SIZE; ++i)
+ test_data[i] = (char) (i + 48);
+ test_data[TEST_IO_TO_SNAP_SIZE] = '\0';
+ orig_data[TEST_IO_TO_SNAP_SIZE] = '\0';
+
+ r = rbd_read(image, 0, TEST_IO_TO_SNAP_SIZE, orig_data);
+ ASSERT_EQ(r, TEST_IO_TO_SNAP_SIZE);
+
+ ASSERT_EQ(0, test_ls_snaps(image, 0));
+ ASSERT_EQ(0, rbd_snap_create(image, "orig"));
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "orig", isize));
+ ASSERT_PASSED(read_test_data, image, orig_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+
+ printf("write test data!\n");
+ ASSERT_PASSED(write_test_data, image, test_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+ ASSERT_EQ(0, rbd_snap_create(image, "written"));
+ ASSERT_EQ(2, test_ls_snaps(image, 2, "orig", isize, "written", isize));
+
+ ASSERT_PASSED(read_test_data, image, test_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+
+ rbd_snap_set(image, "orig");
+ ASSERT_PASSED(read_test_data, image, orig_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+
+ rbd_snap_set(image, "written");
+ ASSERT_PASSED(read_test_data, image, test_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+
+ rbd_snap_set(image, "orig");
+
+ r = rbd_write(image, 0, TEST_IO_TO_SNAP_SIZE, test_data);
+ printf("write to snapshot returned %d\n", r);
+ ASSERT_LT(r, 0);
+ cout << strerror(-r) << std::endl;
+
+ ASSERT_PASSED(read_test_data, image, orig_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+ rbd_snap_set(image, "written");
+ ASSERT_PASSED(read_test_data, image, test_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+
+ r = rbd_snap_rollback(image, "orig");
+ ASSERT_EQ(r, -EROFS);
+
+ r = rbd_snap_set(image, NULL);
+ ASSERT_EQ(r, 0);
+ r = rbd_snap_rollback(image, "orig");
+ ASSERT_EQ(r, 0);
+
+ ASSERT_PASSED(write_test_data, image, test_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+
+ rbd_flush(image);
+
+ printf("opening testimg@orig\n");
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image_at_snap, "orig"));
+ ASSERT_PASSED(read_test_data, image_at_snap, orig_data, 0, TEST_IO_TO_SNAP_SIZE, 0);
+ r = rbd_write(image_at_snap, 0, TEST_IO_TO_SNAP_SIZE, test_data);
+ printf("write to snapshot returned %d\n", r);
+ ASSERT_LT(r, 0);
+ cout << strerror(-r) << std::endl;
+ ASSERT_EQ(0, rbd_close(image_at_snap));
+
+ ASSERT_EQ(2, test_ls_snaps(image, 2, "orig", isize, "written", isize));
+ ASSERT_EQ(0, rbd_snap_remove(image, "written"));
+ ASSERT_EQ(1, test_ls_snaps(image, 1, "orig", isize));
+ ASSERT_EQ(0, rbd_snap_remove(image, "orig"));
+ ASSERT_EQ(0, test_ls_snaps(image, 0));
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestSnapshotDeletedIo)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t isize = 2 << 20;
+
+ int r;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), isize, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(0, rbd_snap_create(image, "orig"));
+
+ r = rbd_snap_set(image, "orig");
+ ASSERT_EQ(r, 0);
+
+ ASSERT_EQ(0, rbd_snap_remove(image, "orig"));
+ char test[20];
+ ASSERT_EQ(-ENOENT, rbd_read(image, 20, 20, test));
+
+ r = rbd_snap_set(image, NULL);
+ ASSERT_EQ(r, 0);
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestClone)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "1"));
+ BOOST_SCOPE_EXIT_ALL(&) {
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "auto"));
+ };
+
+ rados_ioctx_t ioctx;
+ rbd_image_info_t pinfo, cinfo;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ bool old_format;
+ uint64_t features;
+ rbd_image_t parent, child;
+ int order = 0;
+
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ std::string parent_name = get_temp_image_name();
+ std::string child_name = get_temp_image_name();
+
+ // make a parent to clone from
+ ASSERT_EQ(0, create_image_full(ioctx, parent_name.c_str(), 4<<20, &order,
+ false, features));
+ ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, NULL));
+ printf("made parent image \"parent\"\n");
+
+ char *data = (char *)"testdata";
+ ASSERT_EQ((ssize_t)strlen(data), rbd_write(parent, 0, strlen(data), data));
+
+ // can't clone a non-snapshot, expect failure
+ EXPECT_NE(0, clone_image(ioctx, parent, parent_name.c_str(), NULL, ioctx,
+ child_name.c_str(), features, &order));
+
+ // verify that there is no parent info on "parent"
+ ASSERT_EQ(-ENOENT, rbd_get_parent_info(parent, NULL, 0, NULL, 0, NULL, 0));
+ printf("parent has no parent info\n");
+
+ // create 70 metadatas to verify we can clone all key/value pairs
+ std::string key;
+ std::string val;
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(parent, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
+ // create a snapshot, reopen as the parent we're interested in
+ ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
+ printf("made snapshot \"parent@parent_snap\"\n");
+ ASSERT_EQ(0, rbd_close(parent));
+ ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, "parent_snap"));
+
+ ASSERT_EQ(-EINVAL, clone_image(ioctx, parent, parent_name.c_str(), "parent_snap",
+ ioctx, child_name.c_str(), features, &order));
+
+ // unprotected image should fail unprotect
+ ASSERT_EQ(-EINVAL, rbd_snap_unprotect(parent, "parent_snap"));
+ printf("can't unprotect an unprotected snap\n");
+
+ ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
+ // protecting again should fail
+ ASSERT_EQ(-EBUSY, rbd_snap_protect(parent, "parent_snap"));
+ printf("can't protect a protected snap\n");
+
+ // This clone and open should work
+ ASSERT_EQ(0, clone_image(ioctx, parent, parent_name.c_str(), "parent_snap",
+ ioctx, child_name.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, child_name.c_str(), &child, NULL));
+ printf("made and opened clone \"child\"\n");
+
+ // check read
+ ASSERT_PASSED(read_test_data, child, data, 0, strlen(data), 0);
+
+ // check write
+ ASSERT_EQ((ssize_t)strlen(data), rbd_write(child, 20, strlen(data), data));
+ ASSERT_PASSED(read_test_data, child, data, 20, strlen(data), 0);
+ ASSERT_PASSED(read_test_data, child, data, 0, strlen(data), 0);
+
+ // check attributes
+ ASSERT_EQ(0, rbd_stat(parent, &pinfo, sizeof(pinfo)));
+ ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo)));
+ EXPECT_EQ(cinfo.size, pinfo.size);
+ uint64_t overlap;
+ rbd_get_overlap(child, &overlap);
+ EXPECT_EQ(overlap, pinfo.size);
+ EXPECT_EQ(cinfo.obj_size, pinfo.obj_size);
+ EXPECT_EQ(cinfo.order, pinfo.order);
+ printf("sizes and overlaps are good between parent and child\n");
+
+ // check key/value pairs in child image
+ ASSERT_EQ(0, rbd_metadata_list(child, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(sum_key_len, keys_len);
+ ASSERT_EQ(sum_value_len, vals_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(child, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+ printf("child image successfully cloned all image-meta pairs\n");
+
+ // sizing down child results in changing overlap and size, not parent size
+ ASSERT_EQ(0, rbd_resize(child, 2UL<<20));
+ ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo)));
+ rbd_get_overlap(child, &overlap);
+ ASSERT_EQ(overlap, 2UL<<20);
+ ASSERT_EQ(cinfo.size, 2UL<<20);
+ ASSERT_EQ(0, rbd_resize(child, 4UL<<20));
+ ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo)));
+ rbd_get_overlap(child, &overlap);
+ ASSERT_EQ(overlap, 2UL<<20);
+ ASSERT_EQ(cinfo.size, 4UL<<20);
+ printf("sized down clone, changed overlap\n");
+
+ // sizing back up doesn't change that
+ ASSERT_EQ(0, rbd_resize(child, 5UL<<20));
+ ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo)));
+ rbd_get_overlap(child, &overlap);
+ ASSERT_EQ(overlap, 2UL<<20);
+ ASSERT_EQ(cinfo.size, 5UL<<20);
+ ASSERT_EQ(0, rbd_stat(parent, &pinfo, sizeof(pinfo)));
+ printf("parent info: size %llu obj_size %llu parent_pool %llu\n",
+ (unsigned long long)pinfo.size, (unsigned long long)pinfo.obj_size,
+ (unsigned long long)pinfo.parent_pool);
+ ASSERT_EQ(pinfo.size, 4UL<<20);
+ printf("sized up clone, changed size but not overlap or parent's size\n");
+
+ ASSERT_PASSED(validate_object_map, child);
+ ASSERT_EQ(0, rbd_close(child));
+
+ ASSERT_PASSED(validate_object_map, parent);
+ ASSERT_EQ(-EBUSY, rbd_snap_remove(parent, "parent_snap"));
+ printf("can't remove parent while child still exists\n");
+ ASSERT_EQ(0, rbd_remove(ioctx, child_name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd_snap_remove(parent, "parent_snap"));
+ printf("can't remove parent while still protected\n");
+ ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
+ printf("removed parent snap after unprotecting\n");
+
+ ASSERT_EQ(0, rbd_close(parent));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestClone2)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "2"));
+ BOOST_SCOPE_EXIT_ALL(&) {
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "auto"));
+ };
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ bool old_format;
+ uint64_t features;
+ rbd_image_t parent, child;
+ int order = 0;
+
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ std::string parent_name = get_temp_image_name();
+ std::string child_name = get_temp_image_name();
+
+ // make a parent to clone from
+ ASSERT_EQ(0, create_image_full(ioctx, parent_name.c_str(), 4<<20, &order,
+ false, features));
+ ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, NULL));
+ printf("made parent image \"parent\"\n");
+
+ char *data = (char *)"testdata";
+ char *childata = (char *)"childata";
+ ASSERT_EQ((ssize_t)strlen(data), rbd_write(parent, 0, strlen(data), data));
+ ASSERT_EQ((ssize_t)strlen(data), rbd_write(parent, 12, strlen(data), data));
+
+ // can't clone a non-snapshot, expect failure
+ EXPECT_NE(0, clone_image(ioctx, parent, parent_name.c_str(), NULL, ioctx,
+ child_name.c_str(), features, &order));
+
+ // verify that there is no parent info on "parent"
+ ASSERT_EQ(-ENOENT, rbd_get_parent_info(parent, NULL, 0, NULL, 0, NULL, 0));
+ printf("parent has no parent info\n");
+
+ // create 70 metadatas to verify we can clone all key/value pairs
+ std::string key;
+ std::string val;
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(parent, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
+ // create a snapshot, reopen as the parent we're interested in
+ ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
+ printf("made snapshot \"parent@parent_snap\"\n");
+ ASSERT_EQ(0, rbd_close(parent));
+ ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, "parent_snap"));
+
+ // This clone and open should work
+ ASSERT_EQ(0, clone_image(ioctx, parent, parent_name.c_str(), "parent_snap",
+ ioctx, child_name.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, child_name.c_str(), &child, NULL));
+ printf("made and opened clone \"child\"\n");
+
+ // check key/value pairs in child image
+ ASSERT_EQ(0, rbd_metadata_list(child, "key", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(sum_key_len, keys_len);
+ ASSERT_EQ(sum_value_len, vals_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(child, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+ printf("child image successfully cloned all image-meta pairs\n");
+
+ // write something in
+ ASSERT_EQ((ssize_t)strlen(childata), rbd_write(child, 20, strlen(childata), childata));
+
+ char test[strlen(data) * 2];
+ ASSERT_EQ((ssize_t)strlen(data), rbd_read(child, 20, strlen(data), test));
+ ASSERT_EQ(0, memcmp(test, childata, strlen(childata)));
+
+ // overlap
+ ASSERT_EQ((ssize_t)sizeof(test), rbd_read(child, 20 - strlen(data), sizeof(test), test));
+ ASSERT_EQ(0, memcmp(test, data, strlen(data)));
+ ASSERT_EQ(0, memcmp(test + strlen(data), childata, strlen(childata)));
+
+ // all parent
+ ASSERT_EQ((ssize_t)sizeof(test), rbd_read(child, 0, sizeof(test), test));
+ ASSERT_EQ(0, memcmp(test, data, strlen(data)));
+
+ ASSERT_PASSED(validate_object_map, child);
+ ASSERT_PASSED(validate_object_map, parent);
+
+ rbd_snap_info_t snaps[2];
+ int max_snaps = 2;
+ ASSERT_EQ(1, rbd_snap_list(parent, snaps, &max_snaps));
+ rbd_snap_list_end(snaps);
+
+ ASSERT_EQ(0, rbd_snap_remove_by_id(parent, snaps[0].id));
+
+ rbd_snap_namespace_type_t snap_namespace_type;
+ ASSERT_EQ(0, rbd_snap_get_namespace_type(parent, snaps[0].id,
+ &snap_namespace_type));
+ ASSERT_EQ(RBD_SNAP_NAMESPACE_TYPE_TRASH, snap_namespace_type);
+
+ char original_name[32];
+ ASSERT_EQ(0, rbd_snap_get_trash_namespace(parent, snaps[0].id,
+ original_name,
+ sizeof(original_name)));
+ ASSERT_EQ(0, strcmp("parent_snap", original_name));
+
+ ASSERT_EQ(0, rbd_close(child));
+ ASSERT_EQ(0, rbd_close(parent));
+ rados_ioctx_destroy(ioctx);
+}
+
+static void test_list_children(rbd_image_t image, ssize_t num_expected, ...)
+{
+ va_list ap;
+ va_start(ap, num_expected);
+ size_t pools_len = 100;
+ size_t children_len = 100;
+ char *pools = NULL;
+ char *children = NULL;
+ ssize_t num_children;
+
+ do {
+ free(pools);
+ free(children);
+ pools = (char *) malloc(pools_len);
+ children = (char *) malloc(children_len);
+ num_children = rbd_list_children(image, pools, &pools_len,
+ children, &children_len);
+ } while (num_children == -ERANGE);
+
+ ASSERT_EQ(num_expected, num_children);
+ for (ssize_t i = num_expected; i > 0; --i) {
+ char *expected_pool = va_arg(ap, char *);
+ char *expected_image = va_arg(ap, char *);
+ char *pool = pools;
+ char *image = children;
+ bool found = 0;
+ printf("\ntrying to find %s/%s\n", expected_pool, expected_image);
+ for (ssize_t j = 0; j < num_children; ++j) {
+ printf("checking %s/%s\n", pool, image);
+ if (strcmp(expected_pool, pool) == 0 &&
+ strcmp(expected_image, image) == 0) {
+ printf("found child %s/%s\n\n", pool, image);
+ found = 1;
+ break;
+ }
+ pool += strlen(pool) + 1;
+ image += strlen(image) + 1;
+ if (j == num_children - 1) {
+ ASSERT_EQ(pool - pools - 1, (ssize_t) pools_len);
+ ASSERT_EQ(image - children - 1, (ssize_t) children_len);
+ }
+ }
+ ASSERT_TRUE(found);
+ }
+ va_end(ap);
+
+ if (pools)
+ free(pools);
+ if (children)
+ free(children);
+}
+
+static void test_list_children2(rbd_image_t image, int num_expected, ...)
+{
+ int num_children, i, j, max_size = 10;
+ va_list ap;
+ rbd_child_info_t children[max_size];
+ num_children = rbd_list_children2(image, children, &max_size);
+ printf("num children is: %d\nexpected: %d\n", num_children, num_expected);
+
+ for (i = 0; i < num_children; i++) {
+ printf("child: %s\n", children[i].image_name);
+ }
+
+ va_start(ap, num_expected);
+ for (i = num_expected; i > 0; i--) {
+ char *expected_id = va_arg(ap, char *);
+ char *expected_pool = va_arg(ap, char *);
+ char *expected_image = va_arg(ap, char *);
+ bool expected_trash = va_arg(ap, int);
+ bool found = false;
+ for (j = 0; j < num_children; j++) {
+ if (children[j].pool_name == NULL ||
+ children[j].image_name == NULL ||
+ children[j].image_id == NULL)
+ continue;
+ if (strcmp(children[j].image_id, expected_id) == 0 &&
+ strcmp(children[j].pool_name, expected_pool) == 0 &&
+ strcmp(children[j].image_name, expected_image) == 0 &&
+ children[j].trash == expected_trash) {
+ printf("found child %s/%s/%s\n\n", children[j].pool_name, children[j].image_name, children[j].image_id);
+ rbd_list_child_cleanup(&children[j]);
+ children[j].pool_name = NULL;
+ children[j].image_name = NULL;
+ children[j].image_id = NULL;
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+ va_end(ap);
+
+ for (i = 0; i < num_children; i++) {
+ EXPECT_EQ((const char *)0, children[i].pool_name);
+ EXPECT_EQ((const char *)0, children[i].image_name);
+ EXPECT_EQ((const char *)0, children[i].image_id);
+ }
+}
+
+TEST_F(TestLibRBD, ListChildren)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::RBD rbd;
+ rados_ioctx_t ioctx1, ioctx2;
+ string pool_name1 = create_pool(true);
+ string pool_name2 = create_pool(true);
+ ASSERT_NE("", pool_name2);
+
+ rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
+ rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2);
+
+ rbd_image_t image1;
+ rbd_image_t image2;
+ rbd_image_t image3;
+ rbd_image_t image4;
+
+ bool old_format;
+ uint64_t features;
+ rbd_image_t parent;
+ int order = 0;
+
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ std::string parent_name = get_temp_image_name();
+ std::string child_name1 = get_temp_image_name();
+ std::string child_name2 = get_temp_image_name();
+ std::string child_name3 = get_temp_image_name();
+ std::string child_name4 = get_temp_image_name();
+
+ char child_id1[4096];
+ char child_id2[4096];
+ char child_id3[4096];
+ char child_id4[4096];
+
+ // make a parent to clone from
+ ASSERT_EQ(0, create_image_full(ioctx1, parent_name.c_str(), 4<<20, &order,
+ false, features));
+ ASSERT_EQ(0, rbd_open(ioctx1, parent_name.c_str(), &parent, NULL));
+ // create a snapshot, reopen as the parent we're interested in
+ ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_snap_set(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
+
+ ASSERT_EQ(0, rbd_close(parent));
+ ASSERT_EQ(0, rbd_open(ioctx1, parent_name.c_str(), &parent, "parent_snap"));
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx2, child_name1.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &image1, NULL));
+ ASSERT_EQ(0, rbd_get_id(image1, child_id1, sizeof(child_id1)));
+ test_list_children(parent, 1, pool_name2.c_str(), child_name1.c_str());
+ test_list_children2(parent, 1,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false);
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx1, child_name2.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &image2, NULL));
+ ASSERT_EQ(0, rbd_get_id(image2, child_id2, sizeof(child_id2)));
+ test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 2,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx2, child_name3.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &image3, NULL));
+ ASSERT_EQ(0, rbd_get_id(image3, child_id3, sizeof(child_id3)));
+ test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name3.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false);
+
+ librados::IoCtx ioctx3;
+ ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+ ASSERT_EQ(0, rbd_close(image3));
+ ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
+ test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true);
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx2, child_name4.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name4.c_str(), &image4, NULL));
+ ASSERT_EQ(0, rbd_get_id(image4, child_id4, sizeof(child_id4)));
+ test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd.trash_restore(ioctx3, child_id3, ""));
+ test_list_children(parent, 4, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name3.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd_close(image1));
+ ASSERT_EQ(0, rbd_remove(ioctx2, child_name1.c_str()));
+ test_list_children(parent, 3,
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name3.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 3,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
+ test_list_children(parent, 2,
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 2,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd_close(image4));
+ ASSERT_EQ(0, rbd_remove(ioctx2, child_name4.c_str()));
+ test_list_children(parent, 1,
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 1,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
+
+
+ ASSERT_EQ(0, rbd_close(image2));
+ ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
+ test_list_children(parent, 0);
+ test_list_children2(parent, 0);
+
+ ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_close(parent));
+ ASSERT_EQ(0, rbd_remove(ioctx1, parent_name.c_str()));
+ rados_ioctx_destroy(ioctx1);
+ rados_ioctx_destroy(ioctx2);
+}
+
+TEST_F(TestLibRBD, ListChildrenTiered)
+{
+ SKIP_IF_CRIMSON();
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::RBD rbd;
+ string pool_name1 = create_pool(true);
+ string pool_name2 = create_pool(true);
+ string pool_name3 = create_pool(true);
+ ASSERT_NE("", pool_name1);
+ ASSERT_NE("", pool_name2);
+ ASSERT_NE("", pool_name3);
+
+ std::string cmdstr = "{\"prefix\": \"osd tier add\", \"pool\": \"" +
+ pool_name1 + "\", \"tierpool\":\"" + pool_name3 + "\", \"force_nonempty\":\"\"}";
+ char *cmd[1];
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ cmdstr = "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" +
+ pool_name3 + "\", \"mode\":\"writeback\"}";
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ cmdstr = "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" +
+ pool_name1 + "\", \"overlaypool\":\"" + pool_name3 + "\"}";
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+
+ EXPECT_EQ(0, rados_wait_for_latest_osdmap(_cluster));
+
+ string parent_name = get_temp_image_name();
+ string child_name1 = get_temp_image_name();
+ string child_name2 = get_temp_image_name();
+ string child_name3 = get_temp_image_name();
+ string child_name4 = get_temp_image_name();
+
+ char child_id1[4096];
+ char child_id2[4096];
+ char child_id3[4096];
+ char child_id4[4096];
+
+ rbd_image_t image1;
+ rbd_image_t image2;
+ rbd_image_t image3;
+ rbd_image_t image4;
+
+ rados_ioctx_t ioctx1, ioctx2;
+ rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
+ rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2);
+
+ bool old_format;
+ uint64_t features;
+ rbd_image_t parent;
+ int order = 0;
+
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ // make a parent to clone from
+ ASSERT_EQ(0, create_image_full(ioctx1, parent_name.c_str(), 4<<20, &order,
+ false, features));
+ ASSERT_EQ(0, rbd_open(ioctx1, parent_name.c_str(), &parent, NULL));
+ // create a snapshot, reopen as the parent we're interested in
+ ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_snap_set(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
+
+ ASSERT_EQ(0, rbd_close(parent));
+ ASSERT_EQ(0, rbd_open(ioctx1, parent_name.c_str(), &parent, "parent_snap"));
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx2, child_name1.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &image1, NULL));
+ ASSERT_EQ(0, rbd_get_id(image1, child_id1, sizeof(child_id1)));
+ test_list_children(parent, 1, pool_name2.c_str(), child_name1.c_str());
+ test_list_children2(parent, 1,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false);
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx1, child_name2.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &image2, NULL));
+ ASSERT_EQ(0, rbd_get_id(image2, child_id2, sizeof(child_id2)));
+ test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 2,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
+
+ // read from the cache to populate it
+ rbd_image_t tier_image;
+ ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &tier_image, NULL));
+ size_t len = 4 * 1024 * 1024;
+ char* buf = (char*)malloc(len);
+ ssize_t size = rbd_read(tier_image, 0, len, buf);
+ ASSERT_GT(size, 0);
+ free(buf);
+ ASSERT_EQ(0, rbd_close(tier_image));
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx2, child_name3.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &image3, NULL));
+ ASSERT_EQ(0, rbd_get_id(image3, child_id3, sizeof(child_id3)));
+ test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name3.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false);
+
+ librados::IoCtx ioctx3;
+ ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+ ASSERT_EQ(0, rbd_close(image3));
+ ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
+ test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true);
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
+ ioctx2, child_name4.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name4.c_str(), &image4, NULL));
+ ASSERT_EQ(0, rbd_get_id(image4, child_id4, sizeof(child_id4)));
+ test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd.trash_restore(ioctx3, child_id3, ""));
+ test_list_children(parent, 4, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name3.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd_close(image1));
+ ASSERT_EQ(0, rbd_remove(ioctx2, child_name1.c_str()));
+ test_list_children(parent, 3,
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name3.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 3,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
+ test_list_children(parent, 2,
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 2,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd_close(image4));
+ ASSERT_EQ(0, rbd_remove(ioctx2, child_name4.c_str()));
+ test_list_children(parent, 1,
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 1,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
+
+ ASSERT_EQ(0, rbd_close(image2));
+ ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
+ test_list_children(parent, 0);
+ test_list_children2(parent, 0);
+
+ ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_close(parent));
+ ASSERT_EQ(0, rbd_remove(ioctx1, parent_name.c_str()));
+ rados_ioctx_destroy(ioctx1);
+ rados_ioctx_destroy(ioctx2);
+ cmdstr = "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" +
+ pool_name1 + "\"}";
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+ cmdstr = "{\"prefix\": \"osd tier remove\", \"pool\": \"" +
+ pool_name1 + "\", \"tierpool\":\"" + pool_name3 + "\"}";
+ cmd[0] = (char *)cmdstr.c_str();
+ ASSERT_EQ(0, rados_mon_command(_cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0));
+}
+
+TEST_F(TestLibRBD, LockingPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ std::string cookie1 = "foo";
+ std::string cookie2 = "bar";
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // no lockers initially
+ std::list<librbd::locker_t> lockers;
+ std::string tag;
+ bool exclusive;
+ ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag));
+ ASSERT_EQ(0u, lockers.size());
+ ASSERT_EQ("", tag);
+
+ // exclusive lock is exclusive
+ ASSERT_EQ(0, image.lock_exclusive(cookie1));
+ ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie1));
+ ASSERT_EQ(-EBUSY, image.lock_exclusive(""));
+ ASSERT_EQ(-EEXIST, image.lock_shared(cookie1, ""));
+ ASSERT_EQ(-EBUSY, image.lock_shared(cookie1, "test"));
+ ASSERT_EQ(-EBUSY, image.lock_shared("", "test"));
+ ASSERT_EQ(-EBUSY, image.lock_shared("", ""));
+
+ // list exclusive
+ ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag));
+ ASSERT_TRUE(exclusive);
+ ASSERT_EQ("", tag);
+ ASSERT_EQ(1u, lockers.size());
+ ASSERT_EQ(cookie1, lockers.front().cookie);
+
+ // unlock
+ ASSERT_EQ(-ENOENT, image.unlock(""));
+ ASSERT_EQ(-ENOENT, image.unlock(cookie2));
+ ASSERT_EQ(0, image.unlock(cookie1));
+ ASSERT_EQ(-ENOENT, image.unlock(cookie1));
+ ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag));
+ ASSERT_EQ(0u, lockers.size());
+
+ ASSERT_EQ(0, image.lock_shared(cookie1, ""));
+ ASSERT_EQ(-EEXIST, image.lock_shared(cookie1, ""));
+ ASSERT_EQ(0, image.lock_shared(cookie2, ""));
+ ASSERT_EQ(-EEXIST, image.lock_shared(cookie2, ""));
+ ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie1));
+ ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie2));
+ ASSERT_EQ(-EBUSY, image.lock_exclusive(""));
+ ASSERT_EQ(-EBUSY, image.lock_exclusive("test"));
+
+ // list shared
+ ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag));
+ ASSERT_EQ(2u, lockers.size());
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, FlushAio)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ size_t num_aios = 256;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ char test_data[TEST_IO_SIZE + 1];
+ size_t i;
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+
+ rbd_completion_t write_comps[num_aios];
+ for (i = 0; i < num_aios; ++i) {
+ ASSERT_EQ(0, rbd_aio_create_completion(NULL, NULL, &write_comps[i]));
+ uint64_t offset = rand() % (size - TEST_IO_SIZE);
+ ASSERT_EQ(0, rbd_aio_write(image, offset, TEST_IO_SIZE, test_data,
+ write_comps[i]));
+ }
+
+ rbd_completion_t flush_comp;
+ ASSERT_EQ(0, rbd_aio_create_completion(NULL, NULL, &flush_comp));
+ ASSERT_EQ(0, rbd_aio_flush(image, flush_comp));
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(flush_comp));
+ ASSERT_EQ(1, rbd_aio_is_complete(flush_comp));
+ rbd_aio_release(flush_comp);
+
+ for (i = 0; i < num_aios; ++i) {
+ ASSERT_EQ(1, rbd_aio_is_complete(write_comps[i]));
+ rbd_aio_release(write_comps[i]);
+ }
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_remove(ioctx, name.c_str()));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, FlushAioPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ const size_t num_aios = 256;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ char test_data[TEST_IO_SIZE + 1];
+ size_t i;
+ for (i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+
+ librbd::RBD::AioCompletion *write_comps[num_aios];
+ ceph::bufferlist bls[num_aios];
+ for (i = 0; i < num_aios; ++i) {
+ bls[i].append(test_data, strlen(test_data));
+ write_comps[i] = new librbd::RBD::AioCompletion(NULL, NULL);
+ uint64_t offset = rand() % (size - TEST_IO_SIZE);
+ ASSERT_EQ(0, image.aio_write(offset, TEST_IO_SIZE, bls[i],
+ write_comps[i]));
+ }
+
+ librbd::RBD::AioCompletion *flush_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_flush(flush_comp));
+ ASSERT_EQ(0, flush_comp->wait_for_complete());
+ ASSERT_EQ(1, flush_comp->is_complete());
+ flush_comp->release();
+
+ for (i = 0; i < num_aios; ++i) {
+ librbd::RBD::AioCompletion *comp = write_comps[i];
+ ASSERT_EQ(1, comp->is_complete());
+ comp->release();
+ }
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+
+int iterate_cb(uint64_t off, size_t len, int exists, void *arg)
+{
+ //cout << "iterate_cb " << off << "~" << len << std::endl;
+ interval_set<uint64_t> *diff = static_cast<interval_set<uint64_t> *>(arg);
+ diff->insert(off, len);
+ return 0;
+}
+
+static int iterate_error_cb(uint64_t off, size_t len, int exists, void *arg)
+{
+ return -EINVAL;
+}
+
+void scribble(librbd::Image& image, int n, int max, bool skip_discard,
+ interval_set<uint64_t> *exists,
+ interval_set<uint64_t> *what)
+{
+ uint64_t size;
+ image.size(&size);
+ interval_set<uint64_t> exists_at_start = *exists;
+
+ for (int i=0; i<n; i++) {
+ uint64_t off = rand() % (size - max + 1);
+ uint64_t len = 1 + rand() % max;
+ if (!skip_discard && rand() % 4 == 0) {
+ ASSERT_EQ((int)len, image.discard(off, len));
+ interval_set<uint64_t> w;
+ w.insert(off, len);
+
+ // the zeroed bit no longer exists...
+ w.intersection_of(*exists);
+ exists->subtract(w);
+
+ // the bits we discarded are no long written...
+ interval_set<uint64_t> w2 = w;
+ w2.intersection_of(*what);
+ what->subtract(w2);
+
+ // except for the extents that existed at the start that we overwrote.
+ interval_set<uint64_t> w3;
+ w3.insert(off, len);
+ w3.intersection_of(exists_at_start);
+ what->union_of(w3);
+
+ } else {
+ bufferlist bl;
+ bl.append(buffer::create(len));
+ bl.zero();
+ ASSERT_EQ((int)len, image.write(off, len, bl));
+ interval_set<uint64_t> w;
+ w.insert(off, len);
+ what->union_of(w);
+ exists->union_of(w);
+ }
+ }
+}
+
+interval_set<uint64_t> round_diff_interval(const interval_set<uint64_t>& diff,
+ uint64_t object_size)
+{
+ if (object_size == 0) {
+ return diff;
+ }
+
+ interval_set<uint64_t> rounded_diff;
+ for (interval_set<uint64_t>::const_iterator it = diff.begin();
+ it != diff.end(); ++it) {
+ uint64_t off = it.get_start();
+ uint64_t len = it.get_len();
+ off -= off % object_size;
+ len += (object_size - (len % object_size));
+ interval_set<uint64_t> interval;
+ interval.insert(off, len);
+ rounded_diff.union_of(interval);
+ }
+ return rounded_diff;
+}
+
+TEST_F(TestLibRBD, SnapDiff)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string image_name = get_temp_image_name();
+ uint64_t size = 100 << 20;
+ ASSERT_EQ(0, create_image(ioctx, image_name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, image_name.c_str(), &image, nullptr));
+
+ char test_data[TEST_IO_SIZE + 1];
+ for (size_t i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+
+ ASSERT_PASSED(write_test_data, image, test_data, 0,
+ TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, rbd_diff_iterate2(image, nullptr, 0, size, true, true,
+ iterate_cb, &diff));
+ EXPECT_EQ(1 << order, diff.size());
+
+ ASSERT_EQ(0, rbd_snap_create(image, "snap1"));
+ ASSERT_EQ(0, rbd_snap_create(image, "snap2"));
+
+ diff.clear();
+ ASSERT_EQ(0, rbd_diff_iterate2(image, nullptr, 0, size, true, true,
+ iterate_cb, &diff));
+ EXPECT_EQ(1 << order, diff.size());
+
+ diff.clear();
+ ASSERT_EQ(0, rbd_diff_iterate2(image, "snap1", 0, size, true, true,
+ iterate_cb, &diff));
+ EXPECT_EQ(0, diff.size());
+
+ diff.clear();
+ ASSERT_EQ(0, rbd_diff_iterate2(image, "snap2", 0, size, true, true,
+ iterate_cb, &diff));
+ EXPECT_EQ(0, diff.size());
+
+ ASSERT_EQ(0, rbd_snap_remove(image, "snap1"));
+ ASSERT_EQ(0, rbd_snap_remove(image, "snap2"));
+
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_remove(ioctx, image_name.c_str()));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+template <typename T>
+class DiffIterateTest : public TestLibRBD {
+public:
+ static const uint8_t whole_object = T::whole_object;
+};
+
+template <bool _whole_object>
+class DiffIterateParams {
+public:
+ static const uint8_t whole_object = _whole_object;
+};
+
+typedef ::testing::Types<DiffIterateParams<false>,
+ DiffIterateParams<true> > DiffIterateTypes;
+TYPED_TEST_SUITE(DiffIterateTest, DiffIterateTypes);
+
+TYPED_TEST(DiffIterateTest, DiffIterate)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = this->get_temp_image_name();
+ uint64_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
+ uint64_t object_size = 0;
+ if (this->whole_object) {
+ object_size = 1 << order;
+ }
+
+ interval_set<uint64_t> exists;
+ interval_set<uint64_t> one, two;
+ scribble(image, 10, 102400, skip_discard, &exists, &one);
+ cout << " wrote " << one << std::endl;
+ ASSERT_EQ(0, image.snap_create("one"));
+ scribble(image, 10, 102400, skip_discard, &exists, &two);
+
+ two = round_diff_interval(two, object_size);
+ cout << " wrote " << two << std::endl;
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, image.diff_iterate2("one", 0, size, true, this->whole_object,
+ iterate_cb, (void *)&diff));
+ cout << " diff was " << diff << std::endl;
+ if (!two.subset_of(diff)) {
+ interval_set<uint64_t> i;
+ i.intersection_of(two, diff);
+ interval_set<uint64_t> l = two;
+ l.subtract(i);
+ cout << " ... two - (two*diff) = " << l << std::endl;
+ }
+ ASSERT_TRUE(two.subset_of(diff));
+ }
+ ioctx.close();
+}
+
+struct diff_extent {
+ diff_extent(uint64_t _offset, uint64_t _length, bool _exists,
+ uint64_t object_size) :
+ offset(_offset), length(_length), exists(_exists)
+ {
+ if (object_size != 0) {
+ offset -= offset % object_size;
+ length = object_size;
+ }
+ }
+ uint64_t offset;
+ uint64_t length;
+ bool exists;
+ bool operator==(const diff_extent& o) const {
+ return offset == o.offset && length == o.length && exists == o.exists;
+ }
+};
+
+ostream& operator<<(ostream & o, const diff_extent& e) {
+ return o << '(' << e.offset << '~' << e.length << ' ' << (e.exists ? "true" : "false") << ')';
+}
+
+int vector_iterate_cb(uint64_t off, size_t len, int exists, void *arg)
+{
+ cout << "iterate_cb " << off << "~" << len << std::endl;
+ vector<diff_extent> *diff = static_cast<vector<diff_extent> *>(arg);
+ diff->push_back(diff_extent(off, len, exists, 0));
+ return 0;
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateDiscard)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = this->get_temp_image_name();
+ uint64_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ uint64_t object_size = 0;
+ if (this->whole_object) {
+ object_size = 1 << order;
+ }
+ vector<diff_extent> extents;
+ ceph::bufferlist bl;
+
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(0u, extents.size());
+
+ char data[256];
+ memset(data, 1, sizeof(data));
+ bl.append(data, 256);
+ ASSERT_EQ(256, image.write(0, 256, bl));
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(1u, extents.size());
+ ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]);
+
+ int obj_ofs = 256;
+ ASSERT_EQ(1 << order, image.discard(0, 1 << order));
+
+ extents.clear();
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(0u, extents.size());
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(256, image.write(0, 256, bl));
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(1u, extents.size());
+ ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]);
+ ASSERT_EQ(0, image.snap_create("snap2"));
+
+ ASSERT_EQ(obj_ofs, image.discard(0, obj_ofs));
+
+ extents.clear();
+ ASSERT_EQ(0, image.snap_set("snap2"));
+ ASSERT_EQ(0, image.diff_iterate2("snap1", 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(1u, extents.size());
+ ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]);
+
+ ASSERT_EQ(0, image.snap_set(NULL));
+ ASSERT_EQ(1 << order, image.discard(0, 1 << order));
+ ASSERT_EQ(0, image.snap_create("snap3"));
+ ASSERT_EQ(0, image.snap_set("snap3"));
+
+ extents.clear();
+ ASSERT_EQ(0, image.diff_iterate2("snap1", 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(1u, extents.size());
+ ASSERT_EQ(diff_extent(0, 256, false, object_size), extents[0]);
+ ASSERT_PASSED(this->validate_object_map, image);
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateStress)
+{
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)this->_rados.cct()));
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = this->get_temp_image_name();
+ uint64_t size = 400 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
+ uint64_t object_size = 0;
+ if (this->whole_object) {
+ object_size = 1 << order;
+ }
+
+ interval_set<uint64_t> curexists;
+ vector<interval_set<uint64_t> > wrote;
+ vector<interval_set<uint64_t> > exists;
+ vector<string> snap;
+ int n = 20;
+ for (int i=0; i<n; i++) {
+ interval_set<uint64_t> w;
+ scribble(image, 10, 8192000, skip_discard, &curexists, &w);
+ cout << " i=" << i << " exists " << curexists << " wrote " << w << std::endl;
+ string s = "snap" + stringify(i);
+ ASSERT_EQ(0, image.snap_create(s.c_str()));
+ wrote.push_back(w);
+ exists.push_back(curexists);
+ snap.push_back(s);
+ }
+
+ for (int h=0; h<n-1; h++) {
+ for (int i=0; i<n-h-1; i++) {
+ for (int j=(h==0 ? i+1 : n-1); j<n; j++) {
+ interval_set<uint64_t> diff, actual, uex;
+ for (int k=i+1; k<=j; k++)
+ diff.union_of(wrote[k]);
+ cout << "from " << i << " to "
+ << (h != 0 ? string("HEAD") : stringify(j)) << " diff "
+ << round_diff_interval(diff, object_size) << std::endl;
+
+ // limit to extents that exists both at the beginning and at the end
+ uex.union_of(exists[i], exists[j]);
+ diff.intersection_of(uex);
+ diff = round_diff_interval(diff, object_size);
+ cout << " limited diff " << diff << std::endl;
+
+ ASSERT_EQ(0, image.snap_set(h==0 ? snap[j].c_str() : NULL));
+ ASSERT_EQ(0, image.diff_iterate2(snap[i].c_str(), 0, size, true,
+ this->whole_object, iterate_cb,
+ (void *)&actual));
+ cout << " actual was " << actual << std::endl;
+ if (!diff.subset_of(actual)) {
+ interval_set<uint64_t> i;
+ i.intersection_of(diff, actual);
+ interval_set<uint64_t> l = diff;
+ l.subtract(i);
+ cout << " ... diff - (actual*diff) = " << l << std::endl;
+ }
+ ASSERT_TRUE(diff.subset_of(actual));
+ }
+ }
+ ASSERT_EQ(0, image.snap_set(NULL));
+ ASSERT_EQ(0, image.snap_remove(snap[n-h-1].c_str()));
+ }
+
+ ASSERT_PASSED(this->validate_object_map, image);
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateRegression6926)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = this->get_temp_image_name();
+ uint64_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ uint64_t object_size = 0;
+ if (this->whole_object) {
+ object_size = 1 << order;
+ }
+ vector<diff_extent> extents;
+ ceph::bufferlist bl;
+
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(0u, extents.size());
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ char data[256];
+ memset(data, 1, sizeof(data));
+ bl.append(data, 256);
+ ASSERT_EQ(256, image.write(0, 256, bl));
+
+ extents.clear();
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(1u, extents.size());
+ ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]);
+
+ ASSERT_EQ(0, image.snap_set("snap1"));
+ extents.clear();
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, (void *) &extents));
+ ASSERT_EQ(static_cast<size_t>(0), extents.size());
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateParent)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 22;
+ std::string name = this->get_temp_image_name();
+ ssize_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ uint64_t object_size = 0;
+ if (this->whole_object) {
+ object_size = 1 << order;
+ }
+
+ ceph::bufferlist bl;
+ bl.append(std::string(size, '1'));
+ ASSERT_EQ(size, image.write(0, size, bl));
+ ASSERT_EQ(0, image.snap_create("snap"));
+ ASSERT_EQ(0, image.snap_protect("snap"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap", ioctx,
+ clone_name.c_str(), features, &order));
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), NULL));
+
+ std::vector<diff_extent> extents;
+ ASSERT_EQ(0, clone.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, &extents));
+ ASSERT_EQ(5u, extents.size());
+ ASSERT_EQ(diff_extent(0, 4194304, true, object_size), extents[0]);
+ ASSERT_EQ(diff_extent(4194304, 4194304, true, object_size), extents[1]);
+ ASSERT_EQ(diff_extent(8388608, 4194304, true, object_size), extents[2]);
+ ASSERT_EQ(diff_extent(12582912, 4194304, true, object_size), extents[3]);
+ ASSERT_EQ(diff_extent(16777216, 4194304, true, object_size), extents[4]);
+ extents.clear();
+
+ ASSERT_EQ(0, clone.resize(size / 2));
+ ASSERT_EQ(0, clone.resize(size));
+ ASSERT_EQ(1, clone.write(size - 1, 1, bl));
+
+ ASSERT_EQ(0, clone.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, &extents));
+ ASSERT_EQ(4u, extents.size());
+ ASSERT_EQ(diff_extent(0, 4194304, true, object_size), extents[0]);
+ ASSERT_EQ(diff_extent(4194304, 4194304, true, object_size), extents[1]);
+ ASSERT_EQ(diff_extent(8388608, 2097152, true, object_size), extents[2]);
+ // hole (parent overlap = 10M) followed by copyup'ed object
+ ASSERT_EQ(diff_extent(16777216, 4194304, true, object_size), extents[3]);
+
+ ASSERT_PASSED(this->validate_object_map, image);
+ ASSERT_PASSED(this->validate_object_map, clone);
+ }
+
+ ioctx.close();
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateIgnoreParent)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ std::string name = this->get_temp_image_name();
+ uint64_t size = 20 << 20;
+ int order = 0;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ uint64_t object_size = 0;
+ if (this->whole_object) {
+ object_size = 1 << order;
+ }
+
+ bufferlist bl;
+ bl.append(buffer::create(size));
+ bl.zero();
+ interval_set<uint64_t> one;
+ one.insert(0, size);
+ ASSERT_EQ((int)size, image.write(0, size, bl));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "one", ioctx, clone_name.c_str(),
+ features, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, clone_name.c_str(), NULL));
+
+ interval_set<uint64_t> exists;
+ interval_set<uint64_t> two;
+ scribble(image, 10, 102400, skip_discard, &exists, &two);
+ two = round_diff_interval(two, object_size);
+ cout << " wrote " << two << " to clone" << std::endl;
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, false, this->whole_object,
+ iterate_cb, (void *)&diff));
+ cout << " diff was " << diff << std::endl;
+ if (!this->whole_object) {
+ ASSERT_FALSE(one.subset_of(diff));
+ }
+ ASSERT_TRUE(two.subset_of(diff));
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateCallbackError)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = this->get_temp_image_name();
+ uint64_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
+ interval_set<uint64_t> exists;
+ interval_set<uint64_t> one;
+ scribble(image, 10, 102400, skip_discard, &exists, &one);
+ cout << " wrote " << one << std::endl;
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(-EINVAL, image.diff_iterate2(NULL, 0, size, true,
+ this->whole_object,
+ iterate_error_cb, NULL));
+ }
+ ioctx.close();
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateParentDiscard)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ std::string name = this->get_temp_image_name();
+ uint64_t size = 20 << 20;
+ int order = 0;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ uint64_t object_size = 0;
+ if (this->whole_object) {
+ object_size = 1 << order;
+ }
+
+ interval_set<uint64_t> exists;
+ interval_set<uint64_t> one;
+ scribble(image, 10, 102400, skip_discard, &exists, &one);
+ ASSERT_EQ(0, image.snap_create("one"));
+
+ ASSERT_EQ(1 << order, image.discard(0, 1 << order));
+ ASSERT_EQ(0, image.snap_create("two"));
+ ASSERT_EQ(0, image.snap_protect("two"));
+ exists.clear();
+ one.clear();
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "two", ioctx,
+ clone_name.c_str(), features, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, clone_name.c_str(), NULL));
+
+ interval_set<uint64_t> two;
+ scribble(image, 10, 102400, skip_discard, &exists, &two);
+ two = round_diff_interval(two, object_size);
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ iterate_cb, (void *)&diff));
+ ASSERT_TRUE(two.subset_of(diff));
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateUnalignedSmall)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = this->get_temp_image_name();
+ ssize_t size = 10 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(size, '1'));
+ ASSERT_EQ(size, image.write(0, size, bl));
+
+ std::vector<diff_extent> extents;
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 5000005, 1234, true,
+ this->whole_object, vector_iterate_cb,
+ &extents));
+ ASSERT_EQ(1u, extents.size());
+ ASSERT_EQ(diff_extent(5000005, 1234, true, 0), extents[0]);
+
+ ASSERT_PASSED(this->validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateUnaligned)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 22;
+ std::string name = this->get_temp_image_name();
+ ssize_t size = 20 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(size, '1'));
+ ASSERT_EQ(size, image.write(0, size, bl));
+
+ std::vector<diff_extent> extents;
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 8376263, 4260970, true,
+ this->whole_object, vector_iterate_cb,
+ &extents));
+ ASSERT_EQ(3u, extents.size());
+ ASSERT_EQ(diff_extent(8376263, 12345, true, 0), extents[0]);
+ ASSERT_EQ(diff_extent(8388608, 4194304, true, 0), extents[1]);
+ ASSERT_EQ(diff_extent(12582912, 54321, true, 0), extents[2]);
+
+ ASSERT_PASSED(this->validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TYPED_TEST(DiffIterateTest, DiffIterateStriping)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_STRIPINGV2);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
+
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 22;
+ std::string name = this->get_temp_image_name();
+ ssize_t size = 24 << 20;
+
+ ASSERT_EQ(0, rbd.create3(ioctx, name.c_str(), size, features, &order,
+ 1 << 20, 3));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(size, '1'));
+ ASSERT_EQ(size, image.write(0, size, bl));
+
+ std::vector<diff_extent> extents;
+ ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object,
+ vector_iterate_cb, &extents));
+ ASSERT_EQ(2u, extents.size());
+ ASSERT_EQ(diff_extent(0, 12 << 20, true, 0), extents[0]);
+ ASSERT_EQ(diff_extent(12 << 20, 12 << 20, true, 0), extents[1]);
+ extents.clear();
+
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(size, image.discard(0, size));
+
+ ASSERT_EQ(0, image.diff_iterate2("one", 0, size, true, this->whole_object,
+ vector_iterate_cb, &extents));
+ ASSERT_EQ(2u, extents.size());
+ ASSERT_EQ(diff_extent(0, 12 << 20, false, 0), extents[0]);
+ ASSERT_EQ(diff_extent(12 << 20, 12 << 20, false, 0), extents[1]);
+ extents.clear();
+
+ ASSERT_EQ(1 << 20, image.write(0, 1 << 20, bl));
+ ASSERT_EQ(2 << 20, image.write(2 << 20, 2 << 20, bl));
+ ASSERT_EQ(2 << 20, image.write(5 << 20, 2 << 20, bl));
+ ASSERT_EQ(2 << 20, image.write(8 << 20, 2 << 20, bl));
+ ASSERT_EQ(13 << 20, image.write(11 << 20, 13 << 20, bl));
+
+ ASSERT_EQ(0, image.diff_iterate2("one", 0, size, true, this->whole_object,
+ vector_iterate_cb, &extents));
+ ASSERT_EQ(10u, extents.size());
+ ASSERT_EQ(diff_extent(0, 1 << 20, true, 0), extents[0]);
+ ASSERT_EQ(diff_extent(1 << 20, 1 << 20, false, 0), extents[1]);
+ ASSERT_EQ(diff_extent(2 << 20, 2 << 20, true, 0), extents[2]);
+ ASSERT_EQ(diff_extent(4 << 20, 1 << 20, false, 0), extents[3]);
+ ASSERT_EQ(diff_extent(5 << 20, 2 << 20, true, 0), extents[4]);
+ ASSERT_EQ(diff_extent(7 << 20, 1 << 20, false, 0), extents[5]);
+ ASSERT_EQ(diff_extent(8 << 20, 2 << 20, true, 0), extents[6]);
+ ASSERT_EQ(diff_extent(10 << 20, 1 << 20, false, 0), extents[7]);
+ ASSERT_EQ(diff_extent(11 << 20, 1 << 20, true, 0), extents[8]);
+ ASSERT_EQ(diff_extent(12 << 20, 12 << 20, true, 0), extents[9]);
+
+ ASSERT_PASSED(this->validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, ZeroLengthWrite)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ char read_data[1];
+ ASSERT_EQ(0, rbd_write(image, 0, 0, NULL));
+ ASSERT_EQ(1, rbd_read(image, 0, 1, read_data));
+ ASSERT_EQ('\0', read_data[0]);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+
+TEST_F(TestLibRBD, ZeroLengthDiscard)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ const char data[] = "blah";
+ char read_data[sizeof(data)];
+ ASSERT_EQ((int)strlen(data), rbd_write(image, 0, strlen(data), data));
+ ASSERT_EQ(0, rbd_discard(image, 0, 0));
+ ASSERT_EQ((int)strlen(data), rbd_read(image, 0, strlen(data), read_data));
+ ASSERT_EQ(0, memcmp(data, read_data, strlen(data)));
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, ZeroLengthRead)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ char read_data[1];
+ ASSERT_EQ(0, rbd_read(image, 0, 0, read_data));
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, LargeCacheRead)
+{
+ std::string config_value;
+ ASSERT_EQ(0, _rados.conf_get("rbd_cache", config_value));
+ if (config_value == "false") {
+ GTEST_SKIP() << "Skipping due to disabled cache";
+ }
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ uint32_t new_cache_size = 1 << 20;
+ std::string orig_cache_size;
+ ASSERT_EQ(0, _rados.conf_get("rbd_cache_size", orig_cache_size));
+ ASSERT_EQ(0, _rados.conf_set("rbd_cache_size",
+ stringify(new_cache_size).c_str()));
+ ASSERT_EQ(0, _rados.conf_get("rbd_cache_size", config_value));
+ ASSERT_EQ(stringify(new_cache_size), config_value);
+ BOOST_SCOPE_EXIT( (orig_cache_size) ) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_cache_size", orig_cache_size.c_str()));
+ } BOOST_SCOPE_EXIT_END;
+
+ rbd_image_t image;
+ int order = 21;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << order;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ std::string buffer(1 << order, '1');
+
+ ASSERT_EQ(static_cast<ssize_t>(buffer.size()),
+ rbd_write(image, 0, buffer.size(), buffer.c_str()));
+
+ ASSERT_EQ(0, rbd_invalidate_cache(image));
+
+ ASSERT_EQ(static_cast<ssize_t>(buffer.size()),
+ rbd_read(image, 0, buffer.size(), &buffer[0]));
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestPendingAio)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ bool old_format;
+ uint64_t features;
+ rbd_image_t image;
+ int order = 0;
+
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 4 << 20;
+ ASSERT_EQ(0, create_image_full(ioctx, name.c_str(), size, &order,
+ false, features));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_invalidate_cache(image));
+
+ char test_data[TEST_IO_SIZE];
+ for (size_t i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+
+ size_t num_aios = 256;
+ rbd_completion_t comps[num_aios];
+ for (size_t i = 0; i < num_aios; ++i) {
+ ASSERT_EQ(0, rbd_aio_create_completion(NULL, NULL, &comps[i]));
+ uint64_t offset = rand() % (size - TEST_IO_SIZE);
+ ASSERT_EQ(0, rbd_aio_write(image, offset, TEST_IO_SIZE, test_data,
+ comps[i]));
+ }
+ for (size_t i = 0; i < num_aios; ++i) {
+ ASSERT_EQ(0, rbd_aio_wait_for_complete(comps[i]));
+ rbd_aio_release(comps[i]);
+ }
+ ASSERT_EQ(0, rbd_invalidate_cache(image));
+
+ for (size_t i = 0; i < num_aios; ++i) {
+ ASSERT_EQ(0, rbd_aio_create_completion(NULL, NULL, &comps[i]));
+ uint64_t offset = rand() % (size - TEST_IO_SIZE);
+ ASSERT_LE(0, rbd_aio_read(image, offset, TEST_IO_SIZE, test_data,
+ comps[i]));
+ }
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+ for (size_t i = 0; i < num_aios; ++i) {
+ ASSERT_EQ(1, rbd_aio_is_complete(comps[i]));
+ rbd_aio_release(comps[i]);
+ }
+
+ rados_ioctx_destroy(ioctx);
+}
+
+void compare_and_write_copyup(librados::IoCtx &ioctx, bool deep_copyup,
+ bool *passed)
+{
+ librbd::RBD rbd;
+ std::string parent_name = TestLibRBD::get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), size, &order));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ ASSERT_EQ((ssize_t)bl.length(), parent_image.write(0, bl.length(), bl));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap1"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ std::string clone_name = TestLibRBD::get_temp_image_name();
+ EXPECT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap1", ioctx,
+ clone_name.c_str(), features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+ if (deep_copyup) {
+ ASSERT_EQ(0, clone_image.snap_create("snap1"));
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append(std::string(512, '1'));
+ bufferlist write_bl;
+ write_bl.append(std::string(512, '2'));
+ uint64_t mismatch_off = 0;
+ ASSERT_EQ((ssize_t)write_bl.length(),
+ clone_image.compare_and_write(512, write_bl.length(), cmp_bl,
+ write_bl, &mismatch_off, 0));
+ ASSERT_EQ(0U, mismatch_off);
+ bufferlist read_bl;
+ ASSERT_EQ(4096, clone_image.read(0, 4096, read_bl));
+
+ bufferlist expected_bl;
+ expected_bl.append(std::string(512, '1'));
+ expected_bl.append(std::string(512, '2'));
+ expected_bl.append(std::string(3072, '1'));
+ ASSERT_TRUE(expected_bl.contents_equal(read_bl));
+ *passed = true;
+}
+
+TEST_F(TestLibRBD, CompareAndWriteCopyup)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_PASSED(compare_and_write_copyup, ioctx, false);
+ ASSERT_PASSED(compare_and_write_copyup, ioctx, true);
+}
+
+void compare_and_write_copyup_mismatch(librados::IoCtx &ioctx,
+ bool deep_copyup, bool *passed)
+{
+ librbd::RBD rbd;
+ std::string parent_name = TestLibRBD::get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), size, &order));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ ASSERT_EQ((ssize_t)bl.length(), parent_image.write(0, bl.length(), bl));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap1"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ std::string clone_name = TestLibRBD::get_temp_image_name();
+ EXPECT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap1", ioctx,
+ clone_name.c_str(), features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+ if (deep_copyup) {
+ ASSERT_EQ(0, clone_image.snap_create("snap1"));
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append(std::string(48, '1'));
+ cmp_bl.append(std::string(464, '3'));
+ bufferlist write_bl;
+ write_bl.append(std::string(512, '2'));
+ uint64_t mismatch_off = 0;
+ ASSERT_EQ(-EILSEQ,
+ clone_image.compare_and_write(512, write_bl.length(), cmp_bl,
+ write_bl, &mismatch_off, 0));
+ ASSERT_EQ(48U, mismatch_off);
+
+ bufferlist read_bl;
+ ASSERT_EQ(4096, clone_image.read(0, 4096, read_bl));
+
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ *passed = true;
+}
+
+TEST_F(TestLibRBD, CompareAndWriteCopyupMismatch)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_PASSED(compare_and_write_copyup_mismatch, ioctx, false);
+ ASSERT_PASSED(compare_and_write_copyup_mismatch, ioctx, true);
+}
+
+TEST_F(TestLibRBD, Flatten)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), size, &order));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ ASSERT_EQ((ssize_t)bl.length(), parent_image.write(0, bl.length(), bl));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap1"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ std::string clone_name = get_temp_image_name();
+ EXPECT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap1", ioctx,
+ clone_name.c_str(), features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+ ASSERT_EQ(0, clone_image.flatten());
+
+ librbd::RBD::AioCompletion *read_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ bufferlist read_bl;
+ clone_image.aio_read(0, bl.length(), read_bl, read_comp);
+ ASSERT_EQ(0, read_comp->wait_for_complete());
+ ASSERT_EQ((ssize_t)bl.length(), read_comp->get_return_value());
+ read_comp->release();
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ ASSERT_PASSED(validate_object_map, clone_image);
+}
+
+TEST_F(TestLibRBD, Sparsify)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+ BOOST_SCOPE_EXIT_ALL(&ioctx) {
+ rados_ioctx_destroy(ioctx);
+ };
+
+ const size_t CHUNK_SIZE = 4096 * 2;
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = CHUNK_SIZE * 1024;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ BOOST_SCOPE_EXIT_ALL(&image) {
+ rbd_close(image);
+ };
+
+ char test_data[4 * CHUNK_SIZE + 1];
+ for (size_t i = 0; i < 4 ; ++i) {
+ for (size_t j = 0; j < CHUNK_SIZE; j++) {
+ if (i % 2) {
+ test_data[i * CHUNK_SIZE + j] = (char)(rand() % (126 - 33) + 33);
+ } else {
+ test_data[i * CHUNK_SIZE + j] = '\0';
+ }
+ }
+ }
+ test_data[4 * CHUNK_SIZE] = '\0';
+
+ ASSERT_PASSED(write_test_data, image, test_data, 0, 4 * CHUNK_SIZE, 0);
+ ASSERT_EQ(0, rbd_flush(image));
+
+ ASSERT_EQ(-EINVAL, rbd_sparsify(image, 16));
+ ASSERT_EQ(-EINVAL, rbd_sparsify(image, 1 << (order + 1)));
+ ASSERT_EQ(-EINVAL, rbd_sparsify(image, 4096 + 1));
+ ASSERT_EQ(0, rbd_sparsify(image, 4096));
+
+ ASSERT_PASSED(read_test_data, image, test_data, 0, 4 * CHUNK_SIZE, 0);
+}
+
+TEST_F(TestLibRBD, SparsifyPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 12 * 1024 * 1024;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '\0'));
+ bl.append(std::string(4096, '1'));
+ bl.append(std::string(4096, '\0'));
+ ASSERT_EQ((ssize_t)bl.length(), image.write(0, bl.length(), bl));
+ ASSERT_EQ(0, image.flush());
+
+ ASSERT_EQ(-EINVAL, image.sparsify(16));
+ ASSERT_EQ(-EINVAL, image.sparsify(1 << (order + 1)));
+ ASSERT_EQ(-EINVAL, image.sparsify(4096 + 1));
+ ASSERT_EQ(0, image.sparsify(4096));
+
+ bufferlist read_bl;
+ ASSERT_EQ((ssize_t)bl.length(), image.read(0, bl.length(), read_bl));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ ASSERT_PASSED(validate_object_map, image);
+}
+
+TEST_F(TestLibRBD, SnapshotLimit)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ uint64_t limit;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_snap_get_limit(image, &limit));
+ ASSERT_EQ(UINT64_MAX, limit);
+ ASSERT_EQ(0, rbd_snap_set_limit(image, 2));
+ ASSERT_EQ(0, rbd_snap_get_limit(image, &limit));
+ ASSERT_EQ(2U, limit);
+
+ ASSERT_EQ(0, rbd_snap_create(image, "snap1"));
+ ASSERT_EQ(-ERANGE, rbd_snap_set_limit(image, 0));
+ ASSERT_EQ(0, rbd_snap_create(image, "snap2"));
+ ASSERT_EQ(-EDQUOT, rbd_snap_create(image, "snap3"));
+ ASSERT_EQ(0, rbd_snap_set_limit(image, UINT64_MAX));
+ ASSERT_EQ(0, rbd_snap_create(image, "snap3"));
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+
+TEST_F(TestLibRBD, SnapshotLimitPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ uint64_t limit;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image.snap_get_limit(&limit));
+ ASSERT_EQ(UINT64_MAX, limit);
+ ASSERT_EQ(0, image.snap_set_limit(2));
+ ASSERT_EQ(0, image.snap_get_limit(&limit));
+ ASSERT_EQ(2U, limit);
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(-ERANGE, image.snap_set_limit(0));
+ ASSERT_EQ(0, image.snap_create("snap2"));
+ ASSERT_EQ(-EDQUOT, image.snap_create("snap3"));
+ ASSERT_EQ(0, image.snap_set_limit(UINT64_MAX));
+ ASSERT_EQ(0, image.snap_create("snap3"));
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, RebuildObjectMapViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_OBJECT_MAP);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ std::string object_map_oid;
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string image_id;
+ ASSERT_EQ(0, get_image_id(image, &image_id));
+ object_map_oid = RBD_OBJECT_MAP_PREFIX + image_id;
+ }
+
+ // corrupt the object map
+ bufferlist bl;
+ bl.append("foo");
+ ASSERT_EQ(0, ioctx.write(object_map_oid, bl, bl.length(), 0));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bool lock_owner;
+ bl.clear();
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ uint64_t flags;
+ ASSERT_EQ(0, image1.get_flags(&flags));
+ ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ PrintProgress prog_ctx;
+ ASSERT_EQ(0, image2.rebuild_object_map(prog_ctx));
+ ASSERT_PASSED(validate_object_map, image1);
+ ASSERT_PASSED(validate_object_map, image2);
+}
+
+TEST_F(TestLibRBD, RenameViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ std::string new_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd.rename(ioctx, name.c_str(), new_name.c_str()));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ name = new_name;
+ new_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd.rename(ioctx, name.c_str(), new_name.c_str()));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, new_name.c_str(), NULL));
+}
+
+TEST_F(TestLibRBD, SnapCreateViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ // switch to writeback cache
+ ASSERT_EQ(0, image1.flush());
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ ASSERT_EQ((ssize_t)bl.length(), image1.write(0, bl.length(), bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.snap_create("snap1"));
+ bool exists;
+ ASSERT_EQ(0, image1.snap_exists2("snap1", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(0, image2.snap_exists2("snap1", &exists));
+ ASSERT_TRUE(exists);
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+}
+
+TEST_F(TestLibRBD, SnapRemoveViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.snap_remove("snap1"));
+ bool exists;
+ ASSERT_EQ(0, image1.snap_exists2("snap1", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, image2.snap_exists2("snap1", &exists));
+ ASSERT_FALSE(exists);
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+}
+
+TEST_F(TestLibRBD, UpdateFeaturesViaLockOwner) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ librbd::RBD rbd;
+ int order = 0;
+ //creates full with rbd default features
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ bool lock_owner;
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.update_features(RBD_FEATURE_OBJECT_MAP, false));
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.update_features(RBD_FEATURE_OBJECT_MAP, true));
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+}
+
+TEST_F(TestLibRBD, EnableJournalingViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.update_features(RBD_FEATURE_JOURNALING, false));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.update_features(RBD_FEATURE_JOURNALING, true));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+}
+
+TEST_F(TestLibRBD, SnapRemove2)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+ bool exists;
+ ASSERT_EQ(0, image1.snap_exists2("snap1", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(0, image1.snap_protect("snap1"));
+ bool is_protected;
+ ASSERT_EQ(0, image1.snap_is_protected("snap1", &is_protected));
+ ASSERT_TRUE(is_protected);
+
+ uint64_t features;
+ ASSERT_EQ(0, image1.features(&features));
+
+ std::string child_name = get_temp_image_name();
+ EXPECT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap1", ioctx,
+ child_name.c_str(), features, &order));
+
+ ASSERT_EQ(0, image1.snap_exists2("snap1", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(0, image1.snap_is_protected("snap1", &is_protected));
+ ASSERT_TRUE(is_protected);
+
+ ASSERT_EQ(-EBUSY, image1.snap_remove("snap1"));
+ PrintProgress pp;
+ ASSERT_EQ(0, image1.snap_remove2("snap1", RBD_SNAP_REMOVE_FORCE, pp));
+ ASSERT_EQ(0, image1.snap_exists2("snap1", &exists));
+ ASSERT_FALSE(exists);
+}
+
+TEST_F(TestLibRBD, SnapRenameViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.snap_rename("snap1", "snap1-rename"));
+ bool exists;
+ ASSERT_EQ(0, image1.snap_exists2("snap1-rename", &exists));
+ ASSERT_TRUE(exists);
+ ASSERT_EQ(0, image2.snap_exists2("snap1-rename", &exists));
+ ASSERT_TRUE(exists);
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+}
+
+TEST_F(TestLibRBD, SnapProtectViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.snap_protect("snap1"));
+ bool is_protected;
+ ASSERT_EQ(0, image2.snap_is_protected("snap1", &is_protected));
+ ASSERT_TRUE(is_protected);
+ ASSERT_EQ(0, image1.snap_is_protected("snap1", &is_protected));
+ ASSERT_TRUE(is_protected);
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+}
+
+TEST_F(TestLibRBD, SnapUnprotectViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+ ASSERT_EQ(0, image1.snap_protect("snap1"));
+ bool is_protected;
+ ASSERT_EQ(0, image1.snap_is_protected("snap1", &is_protected));
+ ASSERT_TRUE(is_protected);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.snap_unprotect("snap1"));
+ ASSERT_EQ(0, image2.snap_is_protected("snap1", &is_protected));
+ ASSERT_FALSE(is_protected);
+ ASSERT_EQ(0, image1.snap_is_protected("snap1", &is_protected));
+ ASSERT_FALSE(is_protected);
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+}
+
+TEST_F(TestLibRBD, FlattenViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), size, &order));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+ ASSERT_EQ(0, parent_image.snap_create("snap1"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ std::string name = get_temp_image_name();
+ EXPECT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap1", ioctx,
+ name.c_str(), features, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.flatten());
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_PASSED(validate_object_map, image1);
+}
+
+TEST_F(TestLibRBD, ResizeViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.resize(0));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_PASSED(validate_object_map, image1);
+}
+
+TEST_F(TestLibRBD, SparsifyViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.sparsify(4096));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_PASSED(validate_object_map, image1);
+}
+
+TEST_F(TestLibRBD, ObjectMapConsistentSnap)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ int num_snaps = 10;
+ for (int i = 0; i < num_snaps; ++i) {
+ std::string snap_name = "snap" + stringify(i);
+ ASSERT_EQ(0, image1.snap_create(snap_name.c_str()));
+ }
+
+
+ thread writer([&image1](){
+ librbd::image_info_t info;
+ int r = image1.stat(info, sizeof(info));
+ ceph_assert(r == 0);
+ bufferlist bl;
+ bl.append("foo");
+ for (unsigned i = 0; i < info.num_objs; ++i) {
+ r = image1.write((1 << info.order) * i, bl.length(), bl);
+ ceph_assert(r == (int) bl.length());
+ }
+ });
+ writer.join();
+
+ for (int i = 0; i < num_snaps; ++i) {
+ std::string snap_name = "snap" + stringify(i);
+ ASSERT_EQ(0, image1.snap_set(snap_name.c_str()));
+ ASSERT_PASSED(validate_object_map, image1);
+ }
+
+ ASSERT_EQ(0, image1.snap_set(NULL));
+ ASSERT_PASSED(validate_object_map, image1);
+}
+
+void memset_rand(char *buf, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ buf[i] = (char) (rand() % (126 - 33) + 33);
+ }
+}
+
+TEST_F(TestLibRBD, Metadata)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ rbd_image_t image1;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image1, NULL));
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+
+ ASSERT_EQ(0, rbd_metadata_list(image, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(0U, keys_len);
+ ASSERT_EQ(0U, vals_len);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+ memset_rand(value, value_len);
+
+ ASSERT_EQ(0, rbd_metadata_set(image1, "key1", "value1"));
+ ASSERT_EQ(0, rbd_metadata_set(image1, "key2", "value2"));
+ ASSERT_EQ(0, rbd_metadata_get(image1, "key1", value, &value_len));
+ ASSERT_STREQ(value, "value1");
+ value_len = 1;
+ ASSERT_EQ(-ERANGE, rbd_metadata_get(image1, "key1", value, &value_len));
+ ASSERT_EQ(value_len, strlen("value1") + 1);
+
+ ASSERT_EQ(-ERANGE, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
+ ASSERT_STREQ(keys, "key1");
+ ASSERT_STREQ(keys + strlen(keys) + 1, "key2");
+ ASSERT_STREQ(vals, "value1");
+ ASSERT_STREQ(vals + strlen(vals) + 1, "value2");
+
+ ASSERT_EQ(0, rbd_metadata_remove(image1, "key1"));
+ ASSERT_EQ(-ENOENT, rbd_metadata_remove(image1, "key3"));
+ value_len = sizeof(value);
+ ASSERT_EQ(-ENOENT, rbd_metadata_get(image1, "key3", value, &value_len));
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value2") + 1);
+ ASSERT_STREQ(keys, "key2");
+ ASSERT_STREQ(vals, "value2");
+
+ // test config setting
+ ASSERT_EQ(0, rbd_metadata_set(image1, "conf_rbd_cache", "false"));
+ ASSERT_EQ(-EINVAL, rbd_metadata_set(image1, "conf_rbd_cache", "INVALID_VAL"));
+ ASSERT_EQ(0, rbd_metadata_remove(image1, "conf_rbd_cache"));
+
+ // test metadata with snapshot adding
+ ASSERT_EQ(0, rbd_snap_create(image1, "snap1"));
+ ASSERT_EQ(0, rbd_snap_protect(image1, "snap1"));
+ ASSERT_EQ(0, rbd_snap_set(image1, "snap1"));
+
+ ASSERT_EQ(-EROFS, rbd_metadata_set(image1, "key1", "value1"));
+ ASSERT_EQ(-EROFS, rbd_metadata_remove(image1, "key2"));
+
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value2") + 1);
+ ASSERT_STREQ(keys, "key2");
+ ASSERT_STREQ(vals, "value2");
+
+ ASSERT_EQ(0, rbd_snap_set(image1, NULL));
+ ASSERT_EQ(0, rbd_metadata_set(image1, "key1", "value1"));
+ ASSERT_EQ(0, rbd_metadata_set(image1, "key3", "value3"));
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len,
+ strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") + 1);
+ ASSERT_EQ(vals_len,
+ strlen("value1") + 1 + strlen("value2") + 1 + strlen("value3") + 1);
+ ASSERT_STREQ(keys, "key1");
+ ASSERT_STREQ(keys + strlen("key1") + 1, "key2");
+ ASSERT_STREQ(keys + strlen("key1") + 1 + strlen("key2") + 1, "key3");
+ ASSERT_STREQ(vals, "value1");
+ ASSERT_STREQ(vals + strlen("value1") + 1, "value2");
+ ASSERT_STREQ(vals + strlen("value1") + 1 + strlen("value2") + 1, "value3");
+
+ // test metadata with cloning
+ uint64_t features;
+ ASSERT_EQ(0, rbd_get_features(image1, &features));
+
+ string cname = get_temp_image_name();
+ EXPECT_EQ(0, rbd_clone(ioctx, name.c_str(), "snap1", ioctx,
+ cname.c_str(), features, &order));
+ rbd_image_t image2;
+ ASSERT_EQ(0, rbd_open(ioctx, cname.c_str(), &image2, NULL));
+ ASSERT_EQ(0, rbd_metadata_set(image2, "key4", "value4"));
+
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") +
+ 1 + strlen("key4") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1 +
+ strlen("value3") + 1 + strlen("value4") + 1);
+ ASSERT_STREQ(keys + strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") +
+ 1, "key4");
+ ASSERT_STREQ(vals + strlen("value1") + 1 + strlen("value2") + 1 +
+ strlen("value3") + 1, "value4");
+
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len,
+ strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") + 1);
+ ASSERT_EQ(vals_len,
+ strlen("value1") + 1 + strlen("value2") + 1 + strlen("value3") + 1);
+ ASSERT_EQ(-ENOENT, rbd_metadata_get(image1, "key4", value, &value_len));
+
+ // test short buffer cases
+ keys_len = strlen("key1") + 1;
+ vals_len = strlen("value1") + 1;
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 1, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1);
+ ASSERT_STREQ(keys, "key1");
+ ASSERT_STREQ(vals, "value1");
+
+ ASSERT_EQ(-ERANGE, rbd_metadata_list(image2, "key", 2, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
+
+ ASSERT_EQ(-ERANGE, rbd_metadata_list(image2, "key", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") +
+ 1 + strlen("key4") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1 +
+ strlen("value3") + 1 + strlen("value4") + 1);
+
+ // test `start` param
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key2", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key3") + 1 + strlen("key4") + 1);
+ ASSERT_EQ(vals_len, strlen("value3") + 1 + strlen("value4") + 1);
+ ASSERT_STREQ(keys, "key3");
+ ASSERT_STREQ(vals, "value3");
+
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_close(image1));
+ ASSERT_EQ(0, rbd_close(image2));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, MetadataPP)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ uint64_t features;
+ string value;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ map<string, bufferlist> pairs;
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
+ ASSERT_TRUE(pairs.empty());
+
+ ASSERT_EQ(0, image1.metadata_set("key1", "value1"));
+ ASSERT_EQ(0, image1.metadata_set("key2", "value2"));
+ ASSERT_EQ(0, image1.metadata_get("key1", &value));
+ ASSERT_EQ(0, strcmp("value1", value.c_str()));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
+ ASSERT_EQ(2U, pairs.size());
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ pairs.clear();
+ ASSERT_EQ(0, image1.metadata_remove("key1"));
+ ASSERT_EQ(-ENOENT, image1.metadata_remove("key3"));
+ ASSERT_TRUE(image1.metadata_get("key3", &value) < 0);
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ // test config setting
+ ASSERT_EQ(0, image1.metadata_set("conf_rbd_cache", "false"));
+ ASSERT_EQ(-EINVAL, image1.metadata_set("conf_rbd_cache", "INVALID_VALUE"));
+ ASSERT_EQ(0, image1.metadata_remove("conf_rbd_cache"));
+
+ // test metadata with snapshot adding
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+ ASSERT_EQ(0, image1.snap_protect("snap1"));
+ ASSERT_EQ(0, image1.snap_set("snap1"));
+
+ pairs.clear();
+ ASSERT_EQ(-EROFS, image1.metadata_set("key1", "value1"));
+ ASSERT_EQ(-EROFS, image1.metadata_remove("key2"));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ ASSERT_EQ(0, image1.snap_set(NULL));
+ ASSERT_EQ(0, image1.metadata_set("key1", "value1"));
+ ASSERT_EQ(0, image1.metadata_set("key3", "value3"));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
+ ASSERT_EQ(3U, pairs.size());
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6));
+
+ // test metadata with cloning
+ string cname = get_temp_image_name();
+ librbd::Image image2;
+ ASSERT_EQ(0, image1.features(&features));
+ EXPECT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap1", ioctx,
+ cname.c_str(), features, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, cname.c_str(), NULL));
+ ASSERT_EQ(0, image2.metadata_set("key4", "value4"));
+ pairs.clear();
+ ASSERT_EQ(0, image2.metadata_list("key", 0, &pairs));
+ ASSERT_EQ(4U, pairs.size());
+ pairs.clear();
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
+ ASSERT_EQ(3U, pairs.size());
+ ASSERT_EQ(-ENOENT, image1.metadata_get("key4", &value));
+}
+
+TEST_F(TestLibRBD, UpdateFeatures)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ uint8_t old_format;
+ ASSERT_EQ(0, image.old_format(&old_format));
+ if (old_format) {
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, true));
+ return;
+ }
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+
+ // must provide a single feature
+ ASSERT_EQ(-EINVAL, image.update_features(0, true));
+
+ uint64_t disable_features;
+ disable_features = features & (RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_OBJECT_MAP |
+ RBD_FEATURE_FAST_DIFF |
+ RBD_FEATURE_JOURNALING);
+ if (disable_features != 0) {
+ ASSERT_EQ(0, image.update_features(disable_features, false));
+ }
+
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_EQ(0U, features & disable_features);
+
+ // cannot enable object map nor journaling w/o exclusive lock
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_JOURNALING, true));
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, true));
+
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_NE(0U, features & RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ // can enable fast diff w/o object map
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_FAST_DIFF, true));
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_NE(0U, features & RBD_FEATURE_OBJECT_MAP);
+
+ uint64_t expected_flags = RBD_FLAG_OBJECT_MAP_INVALID |
+ RBD_FLAG_FAST_DIFF_INVALID;
+ uint64_t flags;
+ ASSERT_EQ(0, image.get_flags(&flags));
+ ASSERT_EQ(expected_flags, flags);
+
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_EQ(0U, features & RBD_FEATURE_OBJECT_MAP);
+
+ // can disable object map w/ fast diff
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_FAST_DIFF, false));
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_EQ(0U, features & RBD_FEATURE_FAST_DIFF);
+
+ ASSERT_EQ(0, image.get_flags(&flags));
+ ASSERT_EQ(0U, flags);
+
+ // cannot disable exclusive lock w/ object map
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, false));
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
+
+ // cannot disable exclusive lock w/ journaling
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_JOURNALING, true));
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, false));
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_JOURNALING, false));
+
+ ASSERT_EQ(0, image.get_flags(&flags));
+ ASSERT_EQ(0U, flags);
+
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, false));
+
+ ASSERT_EQ(0, image.features(&features));
+ if ((features & RBD_FEATURE_DEEP_FLATTEN) != 0) {
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_DEEP_FLATTEN, false));
+ }
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_DEEP_FLATTEN, true));
+}
+
+TEST_F(TestLibRBD, FeaturesBitmaskString)
+{
+ librbd::RBD rbd;
+ uint64_t features = RBD_FEATURES_DEFAULT;
+
+ std::string features_str;
+ std::string expected_str = "deep-flatten,exclusive-lock,fast-diff,layering,object-map";
+ rbd.features_to_string(features, &features_str);
+ ASSERT_EQ(expected_str, features_str);
+
+ features = RBD_FEATURE_LAYERING;
+ features_str = "";
+ expected_str = "layering";
+ rbd.features_to_string(features, &features_str);
+ ASSERT_EQ(expected_str, features_str);
+
+ uint64_t features_bitmask;
+ features_str = "deep-flatten,exclusive-lock,fast-diff,layering,object-map";
+ rbd.features_from_string(features_str, &features_bitmask);
+ ASSERT_EQ(features_bitmask, RBD_FEATURES_DEFAULT);
+
+ features_str = "layering";
+ features_bitmask = 0;
+ rbd.features_from_string(features_str, &features_bitmask);
+ ASSERT_EQ(features_bitmask, RBD_FEATURE_LAYERING);
+}
+
+TEST_F(TestLibRBD, RebuildObjectMap)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 18;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ PrintProgress prog_ctx;
+ std::string object_map_oid;
+ bufferlist bl;
+ bl.append("foo");
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+ if ((features & RBD_FEATURE_OBJECT_MAP) == 0) {
+ ASSERT_EQ(-EINVAL, image.rebuild_object_map(prog_ctx));
+ return;
+ }
+
+ ASSERT_EQ((ssize_t)bl.length(), image.write(0, bl.length(), bl));
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ((ssize_t)bl.length(), image.write(1<<order, bl.length(), bl));
+
+ std::string image_id;
+ ASSERT_EQ(0, get_image_id(image, &image_id));
+ object_map_oid = RBD_OBJECT_MAP_PREFIX + image_id;
+ }
+
+ // corrupt the object map
+ ASSERT_EQ(0, ioctx.write(object_map_oid, bl, bl.length(), 0));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bool lock_owner;
+ bl.clear();
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ uint64_t flags;
+ ASSERT_EQ(0, image1.get_flags(&flags));
+ ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
+
+ ASSERT_EQ(0, image1.rebuild_object_map(prog_ctx));
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ bufferlist read_bl;
+ ASSERT_EQ((ssize_t)bl.length(), image2.read(0, bl.length(), read_bl));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ read_bl.clear();
+ ASSERT_EQ((ssize_t)bl.length(), image2.read(1<<order, bl.length(), read_bl));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ ASSERT_PASSED(validate_object_map, image1);
+ ASSERT_PASSED(validate_object_map, image2);
+}
+
+TEST_F(TestLibRBD, RebuildNewObjectMap)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 18;
+ uint64_t features = RBD_FEATURE_EXCLUSIVE_LOCK;
+ ASSERT_EQ(0, create_image_full(ioctx, name.c_str(), size, &order,
+ false, features));
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(0, rbd_update_features(image, RBD_FEATURE_OBJECT_MAP, true));
+ ASSERT_EQ(0, rbd_rebuild_object_map(image, print_progress_percent, NULL));
+
+ ASSERT_PASSED(validate_object_map, image);
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, CheckObjectMap)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 18;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ PrintProgress prog_ctx;
+ bufferlist bl1;
+ bufferlist bl2;
+ bl1.append("foo");
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ uint64_t features;
+ ASSERT_EQ(0, image.features(&features));
+
+ ASSERT_EQ((ssize_t)bl1.length(), image.write(0, bl1.length(), bl1));
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ((ssize_t)bl1.length(), image.write(1<<order, bl1.length(), bl1));
+ }
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ std::string image_id;
+ ASSERT_EQ(0, get_image_id(image1, &image_id));
+
+ std::string object_map_oid = RBD_OBJECT_MAP_PREFIX + image_id;
+
+ ASSERT_LT(0, ioctx.read(object_map_oid, bl2, 1024, 0));
+
+ bool lock_owner;
+ ASSERT_EQ((ssize_t)bl1.length(), image1.write(3 * (1 << 18), bl1.length(), bl1));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ //reopen image to reread now corrupt object map from disk
+ image1.close();
+
+ bl1.clear();
+ ASSERT_LT(0, ioctx.read(object_map_oid, bl1, 1024, 0));
+ ASSERT_FALSE(bl1.contents_equal(bl2));
+
+ ASSERT_EQ(0, ioctx.write_full(object_map_oid, bl2));
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ uint64_t flags;
+ ASSERT_EQ(0, image1.get_flags(&flags));
+ ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) == 0);
+
+ ASSERT_EQ(0, image1.check_object_map(prog_ctx));
+
+ ASSERT_EQ(0, image1.get_flags(&flags));
+ ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
+}
+
+TEST_F(TestLibRBD, BlockingAIO)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 18;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ std::string non_blocking_aio;
+ ASSERT_EQ(0, _rados.conf_get("rbd_non_blocking_aio", non_blocking_aio));
+ ASSERT_EQ(0, _rados.conf_set("rbd_non_blocking_aio", "0"));
+ BOOST_SCOPE_EXIT( (non_blocking_aio) ) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_non_blocking_aio",
+ non_blocking_aio.c_str()));
+ } BOOST_SCOPE_EXIT_END;
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
+ bufferlist bl;
+ ASSERT_EQ(0, image.write(0, bl.length(), bl));
+
+ bl.append(std::string(256, '1'));
+ librbd::RBD::AioCompletion *write_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_write(0, bl.length(), bl, write_comp));
+
+ librbd::RBD::AioCompletion *flush_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_flush(flush_comp));
+ ASSERT_EQ(0, flush_comp->wait_for_complete());
+ ASSERT_EQ(0, flush_comp->get_return_value());
+ flush_comp->release();
+
+ ASSERT_EQ(1, write_comp->is_complete());
+ ASSERT_EQ(0, write_comp->get_return_value());
+ write_comp->release();
+
+ librbd::RBD::AioCompletion *discard_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_discard(128, 128, discard_comp));
+ ASSERT_EQ(0, discard_comp->wait_for_complete());
+ discard_comp->release();
+
+ librbd::RBD::AioCompletion *read_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ bufferlist read_bl;
+ image.aio_read(0, bl.length(), read_bl, read_comp);
+ ASSERT_EQ(0, read_comp->wait_for_complete());
+ ASSERT_EQ((ssize_t)bl.length(), read_comp->get_return_value());
+ read_comp->release();
+
+ bufferlist expected_bl;
+ expected_bl.append(std::string(128, '1'));
+ expected_bl.append(std::string(128, skip_discard ? '1' : '\0'));
+ ASSERT_TRUE(expected_bl.contents_equal(read_bl));
+}
+
+TEST_F(TestLibRBD, ExclusiveLockTransition)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ std::list<librbd::RBD::AioCompletion *> comps;
+ ceph::bufferlist bl;
+ bl.append(std::string(1 << order, '1'));
+ for (size_t object_no = 0; object_no < (size >> 12); ++object_no) {
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL,
+ NULL);
+ comps.push_back(comp);
+ if (object_no % 2 == 0) {
+ ASSERT_EQ(0, image1.aio_write(object_no << order, bl.length(), bl, comp));
+ } else {
+ ASSERT_EQ(0, image2.aio_write(object_no << order, bl.length(), bl, comp));
+ }
+ }
+
+ while (!comps.empty()) {
+ librbd::RBD::AioCompletion *comp = comps.front();
+ comps.pop_front();
+ ASSERT_EQ(0, comp->wait_for_complete());
+ ASSERT_EQ(1, comp->is_complete());
+ comp->release();
+ }
+
+ librbd::Image image3;
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name.c_str(), NULL));
+ for (size_t object_no = 0; object_no < (size >> 12); ++object_no) {
+ bufferlist read_bl;
+ ASSERT_EQ((ssize_t)bl.length(), image3.read(object_no << order, bl.length(),
+ read_bl));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ }
+
+ ASSERT_PASSED(validate_object_map, image1);
+ ASSERT_PASSED(validate_object_map, image2);
+ ASSERT_PASSED(validate_object_map, image3);
+}
+
+TEST_F(TestLibRBD, ExclusiveLockReadTransition)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ // journaling should force read ops to acquire the lock
+ bufferlist read_bl;
+ ASSERT_EQ(0, image1.read(0, 0, read_bl));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ std::list<librbd::RBD::AioCompletion *> comps;
+ std::list<bufferlist> read_bls;
+ for (size_t object_no = 0; object_no < (size >> 12); ++object_no) {
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL,
+ NULL);
+ comps.push_back(comp);
+ read_bls.emplace_back();
+ if (object_no % 2 == 0) {
+ ASSERT_EQ(0, image1.aio_read(object_no << order, 1 << order, read_bls.back(), comp));
+ } else {
+ ASSERT_EQ(0, image2.aio_read(object_no << order, 1 << order, read_bls.back(), comp));
+ }
+ }
+
+ while (!comps.empty()) {
+ librbd::RBD::AioCompletion *comp = comps.front();
+ comps.pop_front();
+ ASSERT_EQ(0, comp->wait_for_complete());
+ ASSERT_EQ(1, comp->is_complete());
+ comp->release();
+ }
+}
+
+TEST_F(TestLibRBD, CacheMayCopyOnWrite) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "one", ioctx, clone_name.c_str(),
+ RBD_FEATURE_LAYERING, &order));
+
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), NULL));
+ ASSERT_EQ(0, clone.flush());
+
+ bufferlist expect_bl;
+ expect_bl.append(std::string(1024, '\0'));
+
+ // test double read path
+ bufferlist read_bl;
+ uint64_t offset = 0;
+ ASSERT_EQ(1024, clone.read(offset + 2048, 1024, read_bl));
+ ASSERT_TRUE(expect_bl.contents_equal(read_bl));
+
+ bufferlist write_bl;
+ write_bl.append(std::string(1024, '1'));
+ ASSERT_EQ(1024, clone.write(offset, write_bl.length(), write_bl));
+
+ read_bl.clear();
+ ASSERT_EQ(1024, clone.read(offset + 2048, 1024, read_bl));
+ ASSERT_TRUE(expect_bl.contents_equal(read_bl));
+
+ // test read retry path
+ offset = 1 << order;
+ ASSERT_EQ(1024, clone.write(offset, write_bl.length(), write_bl));
+
+ read_bl.clear();
+ ASSERT_EQ(1024, clone.read(offset + 2048, 1024, read_bl));
+ ASSERT_TRUE(expect_bl.contents_equal(read_bl));
+}
+
+TEST_F(TestLibRBD, FlushEmptyOpsOnExternalSnapshot) {
+ std::string cache_enabled;
+ ASSERT_EQ(0, _rados.conf_get("rbd_cache", cache_enabled));
+ ASSERT_EQ(0, _rados.conf_set("rbd_cache", "false"));
+ BOOST_SCOPE_EXIT( (cache_enabled) ) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_cache", cache_enabled.c_str()));
+ } BOOST_SCOPE_EXIT_END;
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 18;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+
+ librbd::RBD::AioCompletion *read_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ bufferlist read_bl;
+ image2.aio_read(0, 1024, read_bl, read_comp);
+ ASSERT_EQ(0, read_comp->wait_for_complete());
+ read_comp->release();
+}
+
+TEST_F(TestLibRBD, TestImageOptions)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ //make create image options
+ uint64_t features = RBD_FEATURE_LAYERING | RBD_FEATURE_STRIPINGV2 ;
+ uint64_t order = 0;
+ uint64_t stripe_unit = IMAGE_STRIPE_UNIT;
+ uint64_t stripe_count = IMAGE_STRIPE_COUNT;
+ rbd_image_options_t opts;
+ rbd_image_options_create(&opts);
+
+ bool is_set;
+ ASSERT_EQ(-EINVAL, rbd_image_options_is_set(opts, 12345, &is_set));
+ ASSERT_EQ(0, rbd_image_options_is_set(opts, RBD_IMAGE_OPTION_FORMAT,
+ &is_set));
+ ASSERT_FALSE(is_set);
+
+ ASSERT_EQ(0, rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FORMAT,
+ 2));
+ ASSERT_EQ(0, rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
+ features));
+ ASSERT_EQ(0, rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
+ order));
+ ASSERT_EQ(0, rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
+ stripe_unit));
+ ASSERT_EQ(0, rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
+ stripe_count));
+
+ ASSERT_EQ(0, rbd_image_options_is_set(opts, RBD_IMAGE_OPTION_FORMAT,
+ &is_set));
+ ASSERT_TRUE(is_set);
+
+ std::string parent_name = get_temp_image_name();
+
+ // make parent
+ ASSERT_EQ(0, rbd_create4(ioctx, parent_name.c_str(), 4<<20, opts));
+
+ // check order is returned in opts
+ ASSERT_EQ(0, rbd_image_options_get_uint64(opts, RBD_IMAGE_OPTION_ORDER,
+ &order));
+ ASSERT_NE((uint64_t)0, order);
+
+ // write some data to parent
+ rbd_image_t parent;
+ ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, NULL));
+ char *data = (char *)"testdata";
+ ASSERT_EQ((ssize_t)strlen(data), rbd_write(parent, 0, strlen(data), data));
+ ASSERT_EQ((ssize_t)strlen(data), rbd_write(parent, 12, strlen(data), data));
+
+ // create a snapshot, reopen as the parent we're interested in
+ ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_close(parent));
+ ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, "parent_snap"));
+
+ // clone
+ std::string child_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
+ ASSERT_EQ(0, rbd_clone3(ioctx, parent_name.c_str(), "parent_snap", ioctx,
+ child_name.c_str(), opts));
+
+ // copy
+ std::string copy1_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd_copy3(parent, ioctx, copy1_name.c_str(), opts));
+ std::string copy2_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd_copy_with_progress3(parent, ioctx, copy2_name.c_str(), opts,
+ print_progress_percent, NULL));
+
+ ASSERT_EQ(0, rbd_close(parent));
+
+ rbd_image_options_destroy(opts);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestImageOptionsPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ //make create image options
+ uint64_t features = RBD_FEATURE_LAYERING | RBD_FEATURE_STRIPINGV2 ;
+ uint64_t order = 0;
+ uint64_t stripe_unit = IMAGE_STRIPE_UNIT;
+ uint64_t stripe_count = IMAGE_STRIPE_COUNT;
+ librbd::ImageOptions opts;
+ ASSERT_EQ(0, opts.set(RBD_IMAGE_OPTION_FORMAT, static_cast<uint64_t>(2)));
+ ASSERT_EQ(0, opts.set(RBD_IMAGE_OPTION_FEATURES, features));
+ ASSERT_EQ(0, opts.set(RBD_IMAGE_OPTION_ORDER, order));
+ ASSERT_EQ(0, opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit));
+ ASSERT_EQ(0, opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+
+ // make parent
+ ASSERT_EQ(0, rbd.create4(ioctx, parent_name.c_str(), 4<<20, opts));
+
+ // check order is returned in opts
+ ASSERT_EQ(0, opts.get(RBD_IMAGE_OPTION_ORDER, &order));
+ ASSERT_NE((uint64_t)0, order);
+
+ // write some data to parent
+ librbd::Image parent;
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), NULL));
+
+ ssize_t len = 1024;
+ bufferlist bl;
+ bl.append(buffer::create(len));
+ bl.zero();
+ ASSERT_EQ(len, parent.write(0, len, bl));
+ ASSERT_EQ(len, parent.write(len, len, bl));
+
+ // create a snapshot, reopen as the parent we're interested in
+ ASSERT_EQ(0, parent.snap_create("parent_snap"));
+ ASSERT_EQ(0, parent.close());
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), "parent_snap"));
+
+ // clone
+ std::string child_name = get_temp_image_name();
+ ASSERT_EQ(0, parent.snap_protect("parent_snap"));
+ ASSERT_EQ(0, rbd.clone3(ioctx, parent_name.c_str(), "parent_snap", ioctx,
+ child_name.c_str(), opts));
+
+ // copy
+ std::string copy1_name = get_temp_image_name();
+ ASSERT_EQ(0, parent.copy3(ioctx, copy1_name.c_str(), opts));
+ std::string copy2_name = get_temp_image_name();
+ PrintProgress pp;
+ ASSERT_EQ(0, parent.copy_with_progress3(ioctx, copy2_name.c_str(), opts, pp));
+
+ ASSERT_EQ(0, parent.close());
+}
+
+TEST_F(TestLibRBD, EventSocketPipe)
+{
+ EventSocket event_sock;
+ int pipe_fd[2]; // read and write fd
+ char buf[32];
+
+ ASSERT_EQ(0, pipe(pipe_fd));
+
+ ASSERT_FALSE(event_sock.is_valid());
+
+ ASSERT_EQ(-EINVAL, event_sock.init(pipe_fd[1], EVENT_SOCKET_TYPE_NONE));
+ ASSERT_FALSE(event_sock.is_valid());
+
+ ASSERT_EQ(-EINVAL, event_sock.init(pipe_fd[1], 44));
+ ASSERT_FALSE(event_sock.is_valid());
+
+#ifndef HAVE_EVENTFD
+ ASSERT_EQ(-EINVAL, event_sock.init(pipe_fd[1], EVENT_SOCKET_TYPE_EVENTFD));
+ ASSERT_FALSE(event_sock.is_valid());
+#endif
+
+ ASSERT_EQ(0, event_sock.init(pipe_fd[1], EVENT_SOCKET_TYPE_PIPE));
+ ASSERT_TRUE(event_sock.is_valid());
+ ASSERT_EQ(0, event_sock.notify());
+ ASSERT_EQ(1, read(pipe_fd[0], buf, 32));
+ ASSERT_EQ('i', buf[0]);
+
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+}
+
+TEST_F(TestLibRBD, EventSocketEventfd)
+{
+#ifdef HAVE_EVENTFD
+ EventSocket event_sock;
+ int event_fd;
+ struct pollfd poll_fd;
+ char buf[32];
+
+ event_fd = eventfd(0, EFD_NONBLOCK);
+ ASSERT_NE(-1, event_fd);
+
+ ASSERT_FALSE(event_sock.is_valid());
+
+ ASSERT_EQ(-EINVAL, event_sock.init(event_fd, EVENT_SOCKET_TYPE_NONE));
+ ASSERT_FALSE(event_sock.is_valid());
+
+ ASSERT_EQ(-EINVAL, event_sock.init(event_fd, 44));
+ ASSERT_FALSE(event_sock.is_valid());
+
+ ASSERT_EQ(0, event_sock.init(event_fd, EVENT_SOCKET_TYPE_EVENTFD));
+ ASSERT_TRUE(event_sock.is_valid());
+ ASSERT_EQ(0, event_sock.notify());
+
+ poll_fd.fd = event_fd;
+ poll_fd.events = POLLIN;
+ ASSERT_EQ(1, poll(&poll_fd, 1, -1));
+ ASSERT_TRUE(poll_fd.revents & POLLIN);
+
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(uint64_t)), read(event_fd, buf, 32));
+ ASSERT_EQ(1U, *reinterpret_cast<uint64_t *>(buf));
+
+ close(event_fd);
+#endif
+}
+
+TEST_F(TestLibRBD, ImagePollIO)
+{
+#ifdef HAVE_EVENTFD
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int fd = eventfd(0, EFD_NONBLOCK);
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_set_image_notification(image, fd, EVENT_SOCKET_TYPE_EVENTFD));
+
+ char test_data[TEST_IO_SIZE + 1];
+ char zero_data[TEST_IO_SIZE + 1];
+ int i;
+
+ for (i = 0; i < TEST_IO_SIZE; ++i)
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ test_data[TEST_IO_SIZE] = '\0';
+ memset(zero_data, 0, sizeof(zero_data));
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data_and_poll, image, fd, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data_and_poll, image, fd, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ ASSERT_EQ(0, rbd_close(image));
+ rados_ioctx_destroy(ioctx);
+#endif
+}
+
+namespace librbd {
+
+static bool operator==(const image_spec_t &lhs, const image_spec_t &rhs) {
+ return (lhs.id == rhs.id && lhs.name == rhs.name);
+}
+
+static bool operator==(const linked_image_spec_t &lhs,
+ const linked_image_spec_t &rhs) {
+ return (lhs.pool_id == rhs.pool_id &&
+ lhs.pool_name == rhs.pool_name &&
+ lhs.pool_namespace == rhs.pool_namespace &&
+ lhs.image_id == rhs.image_id &&
+ lhs.image_name == rhs.image_name &&
+ lhs.trash == rhs.trash);
+}
+
+static bool operator==(const mirror_peer_t &lhs, const mirror_peer_t &rhs) {
+ return (lhs.uuid == rhs.uuid &&
+ lhs.cluster_name == rhs.cluster_name &&
+ lhs.client_name == rhs.client_name);
+}
+
+static std::ostream& operator<<(std::ostream &os, const mirror_peer_t &peer) {
+ os << "uuid=" << peer.uuid << ", "
+ << "cluster=" << peer.cluster_name << ", "
+ << "client=" << peer.client_name;
+ return os;
+}
+
+} // namespace librbd
+
+TEST_F(TestLibRBD, Mirror) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+
+ std::vector<librbd::mirror_peer_t> expected_peers;
+ std::vector<librbd::mirror_peer_t> peers;
+ ASSERT_EQ(0, rbd.mirror_peer_list(ioctx, &peers));
+ ASSERT_EQ(expected_peers, peers);
+
+ std::string uuid1;
+ ASSERT_EQ(-EINVAL, rbd.mirror_peer_add(ioctx, &uuid1, "cluster1", "client"));
+
+ rbd_mirror_mode_t mirror_mode;
+ ASSERT_EQ(0, rbd.mirror_mode_get(ioctx, &mirror_mode));
+ ASSERT_EQ(RBD_MIRROR_MODE_DISABLED, mirror_mode);
+
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE));
+ ASSERT_EQ(0, rbd.mirror_mode_get(ioctx, &mirror_mode));
+
+ // Add some images to the pool
+ int order = 0;
+ std::string parent_name = get_temp_image_name();
+ std::string child_name = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), 2 << 20,
+ &order));
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ if ((features & RBD_FEATURE_LAYERING) != 0) {
+ librbd::Image parent;
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), NULL));
+ ASSERT_EQ(0, parent.snap_create("parent_snap"));
+ ASSERT_EQ(0, parent.close());
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), "parent_snap"));
+ ASSERT_EQ(0, parent.snap_protect("parent_snap"));
+ ASSERT_EQ(0, parent.close());
+ ASSERT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "parent_snap", ioctx,
+ child_name.c_str(), features, &order));
+ }
+
+ ASSERT_EQ(RBD_MIRROR_MODE_IMAGE, mirror_mode);
+
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL));
+ ASSERT_EQ(0, rbd.mirror_mode_get(ioctx, &mirror_mode));
+ ASSERT_EQ(RBD_MIRROR_MODE_POOL, mirror_mode);
+
+ std::string uuid2;
+ std::string uuid3;
+ ASSERT_EQ(0, rbd.mirror_peer_add(ioctx, &uuid1, "cluster1", "client"));
+ ASSERT_EQ(0, rbd.mirror_peer_add(ioctx, &uuid2, "cluster2", "admin"));
+ ASSERT_EQ(-EEXIST, rbd.mirror_peer_add(ioctx, &uuid3, "cluster1", "foo"));
+ ASSERT_EQ(0, rbd.mirror_peer_add(ioctx, &uuid3, "cluster3", "admin"));
+
+ ASSERT_EQ(0, rbd.mirror_peer_list(ioctx, &peers));
+ auto sort_peers = [](const librbd::mirror_peer_t &lhs,
+ const librbd::mirror_peer_t &rhs) {
+ return lhs.uuid < rhs.uuid;
+ };
+ expected_peers = {
+ {uuid1, "cluster1", "client"},
+ {uuid2, "cluster2", "admin"},
+ {uuid3, "cluster3", "admin"}};
+ std::sort(expected_peers.begin(), expected_peers.end(), sort_peers);
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, "uuid4"));
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid2));
+
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_set_client(ioctx, "uuid4", "new client"));
+ ASSERT_EQ(0, rbd.mirror_peer_set_client(ioctx, uuid1, "new client"));
+
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_set_cluster(ioctx, "uuid4",
+ "new cluster"));
+ ASSERT_EQ(0, rbd.mirror_peer_set_cluster(ioctx, uuid3, "new cluster"));
+
+ ASSERT_EQ(0, rbd.mirror_peer_list(ioctx, &peers));
+ expected_peers = {
+ {uuid1, "cluster1", "new client"},
+ {uuid3, "new cluster", "admin"}};
+ std::sort(expected_peers.begin(), expected_peers.end(), sort_peers);
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(-EBUSY, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid1));
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid3));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestLibRBD, MirrorPeerAttributes) {
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ std::string uuid;
+ ASSERT_EQ(0, rbd.mirror_peer_add(ioctx, &uuid, "remote_cluster", "client"));
+
+ std::map<std::string, std::string> attributes;
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_get_attributes(ioctx, uuid, &attributes));
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_set_attributes(ioctx, "missing uuid",
+ attributes));
+
+ std::map<std::string, std::string> expected_attributes{
+ {"mon_host", "1.2.3.4"},
+ {"key", "ABC"}};
+ ASSERT_EQ(0, rbd.mirror_peer_set_attributes(ioctx, uuid,
+ expected_attributes));
+
+ ASSERT_EQ(0, rbd.mirror_peer_get_attributes(ioctx, uuid,
+ &attributes));
+ ASSERT_EQ(expected_attributes, attributes);
+
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestLibRBD, CreateWithMirrorEnabled) {
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ librbd::ImageOptions image_options;
+ ASSERT_EQ(0, image_options.set(
+ RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE,
+ static_cast<uint64_t>(RBD_MIRROR_IMAGE_MODE_SNAPSHOT)));
+
+ std::string parent_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd.create4(ioctx, parent_name.c_str(), 2<<20, image_options));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+
+ librbd::mirror_image_mode_t mirror_image_mode;
+ ASSERT_EQ(0, parent_image.mirror_image_get_mode(&mirror_image_mode));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mirror_image_mode);
+
+ ASSERT_EQ(0, parent_image.snap_create("parent_snap"));
+ ASSERT_EQ(0, parent_image.snap_protect("parent_snap"));
+
+ std::string child_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone3(ioctx, parent_name.c_str(), "parent_snap", ioctx,
+ child_name.c_str(), image_options));
+
+ librbd::Image child_image;
+ ASSERT_EQ(0, rbd.open(ioctx, child_image, child_name.c_str(), NULL));
+
+ ASSERT_EQ(0, child_image.mirror_image_get_mode(&mirror_image_mode));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mirror_image_mode);
+
+ ASSERT_EQ(0, child_image.mirror_image_disable(true));
+ ASSERT_EQ(0, parent_image.mirror_image_disable(true));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestLibRBD, FlushCacheWithCopyupOnExternalSnapshot) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 0;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ bufferlist bl;
+ bl.append(std::string(size, '1'));
+ ASSERT_EQ((int)size, image.write(0, size, bl));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "one", ioctx, clone_name.c_str(),
+ RBD_FEATURE_LAYERING, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, clone_name.c_str(), NULL));
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, clone_name.c_str(), NULL));
+
+ // prepare CoW writeback that will be flushed on next op
+ bl.clear();
+ bl.append(std::string(1, '1'));
+ ASSERT_EQ(0, image.flush());
+ ASSERT_EQ(1, image.write(0, 1, bl));
+ ASSERT_EQ(0, image2.snap_create("snap1"));
+
+ librbd::RBD::AioCompletion *read_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ bufferlist read_bl;
+ image.aio_read(0, 1024, read_bl, read_comp);
+ ASSERT_EQ(0, read_comp->wait_for_complete());
+ read_comp->release();
+}
+
+TEST_F(TestLibRBD, ExclusiveLock)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ static char buf[10];
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_image_t image1;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image1, NULL));
+
+ int lock_owner;
+ ASSERT_EQ(0, rbd_lock_acquire(image1, RBD_LOCK_MODE_EXCLUSIVE));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image1, &lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ rbd_lock_mode_t lock_mode;
+ char *lock_owners[1];
+ size_t max_lock_owners = 0;
+ ASSERT_EQ(-ERANGE, rbd_lock_get_owners(image1, &lock_mode, lock_owners,
+ &max_lock_owners));
+ ASSERT_EQ(1U, max_lock_owners);
+
+ ASSERT_EQ(0, rbd_lock_get_owners(image1, &lock_mode, lock_owners,
+ &max_lock_owners));
+ ASSERT_EQ(RBD_LOCK_MODE_EXCLUSIVE, lock_mode);
+ ASSERT_STRNE("", lock_owners[0]);
+ ASSERT_EQ(1U, max_lock_owners);
+
+ rbd_image_t image2;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image2, NULL));
+
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image2, &lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(-EOPNOTSUPP, rbd_lock_break(image1, RBD_LOCK_MODE_SHARED, ""));
+ ASSERT_EQ(-EBUSY, rbd_lock_break(image1, RBD_LOCK_MODE_EXCLUSIVE,
+ "not the owner"));
+
+ ASSERT_EQ(0, rbd_lock_release(image1));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image1, &lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(-ENOENT, rbd_lock_break(image1, RBD_LOCK_MODE_EXCLUSIVE,
+ lock_owners[0]));
+ rbd_lock_get_owners_cleanup(lock_owners, max_lock_owners);
+
+ ASSERT_EQ(-EROFS, rbd_write(image1, 0, sizeof(buf), buf));
+ ASSERT_EQ((ssize_t)sizeof(buf), rbd_write(image2, 0, sizeof(buf), buf));
+
+ ASSERT_EQ(0, rbd_lock_acquire(image2, RBD_LOCK_MODE_EXCLUSIVE));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image2, &lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ ASSERT_EQ(0, rbd_lock_release(image2));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image2, &lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, rbd_lock_acquire(image1, RBD_LOCK_MODE_EXCLUSIVE));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image1, &lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ ASSERT_EQ((ssize_t)sizeof(buf), rbd_write(image1, 0, sizeof(buf), buf));
+ ASSERT_EQ(-EROFS, rbd_write(image2, 0, sizeof(buf), buf));
+
+ ASSERT_EQ(0, rbd_lock_release(image1));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image1, &lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ int owner_id = -1;
+ std::mutex lock;
+ const auto pingpong = [&](int m_id, rbd_image_t &m_image) {
+ for (int i = 0; i < 10; i++) {
+ {
+ std::lock_guard<std::mutex> locker(lock);
+ if (owner_id == m_id) {
+ std::cout << m_id << ": releasing exclusive lock" << std::endl;
+ EXPECT_EQ(0, rbd_lock_release(m_image));
+ int lock_owner;
+ EXPECT_EQ(0, rbd_is_exclusive_lock_owner(m_image, &lock_owner));
+ EXPECT_FALSE(lock_owner);
+ owner_id = -1;
+ std::cout << m_id << ": exclusive lock released" << std::endl;
+ continue;
+ }
+ }
+
+ std::cout << m_id << ": acquiring exclusive lock" << std::endl;
+ int r;
+ do {
+ r = rbd_lock_acquire(m_image, RBD_LOCK_MODE_EXCLUSIVE);
+ if (r == -EROFS) {
+ usleep(1000);
+ }
+ } while (r == -EROFS);
+ EXPECT_EQ(0, r);
+
+ int lock_owner;
+ EXPECT_EQ(0, rbd_is_exclusive_lock_owner(m_image, &lock_owner));
+ EXPECT_TRUE(lock_owner);
+ std::cout << m_id << ": exclusive lock acquired" << std::endl;
+ {
+ std::lock_guard<std::mutex> locker(lock);
+ owner_id = m_id;
+ }
+ usleep(rand() % 50000);
+ }
+
+ std::lock_guard<std::mutex> locker(lock);
+ if (owner_id == m_id) {
+ EXPECT_EQ(0, rbd_lock_release(m_image));
+ int lock_owner;
+ EXPECT_EQ(0, rbd_is_exclusive_lock_owner(m_image, &lock_owner));
+ EXPECT_FALSE(lock_owner);
+ owner_id = -1;
+ }
+ };
+ thread ping(bind(pingpong, 1, ref(image1)));
+ thread pong(bind(pingpong, 2, ref(image2)));
+
+ ping.join();
+ pong.join();
+
+ ASSERT_EQ(0, rbd_lock_acquire(image2, RBD_LOCK_MODE_EXCLUSIVE));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image2, &lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ ASSERT_EQ(0, rbd_close(image2));
+
+ ASSERT_EQ(0, rbd_lock_acquire(image1, RBD_LOCK_MODE_EXCLUSIVE));
+ ASSERT_EQ(0, rbd_is_exclusive_lock_owner(image1, &lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ ASSERT_EQ(0, rbd_close(image1));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, BreakLock)
+{
+ SKIP_IF_CRIMSON();
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+ REQUIRE(!is_rbd_pwl_enabled((CephContext *)_rados.cct()));
+
+ static char buf[10];
+
+ rados_t blocklist_cluster;
+ ASSERT_EQ("", connect_cluster(&blocklist_cluster));
+
+ rados_ioctx_t ioctx, blocklist_ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+ ASSERT_EQ(0, rados_ioctx_create(blocklist_cluster, m_pool_name.c_str(),
+ &blocklist_ioctx));
+
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_image_t image, blocklist_image;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(0, rbd_open(blocklist_ioctx, name.c_str(), &blocklist_image, NULL));
+
+ ASSERT_EQ(0, rbd_metadata_set(image, "conf_rbd_blocklist_on_break_lock", "true"));
+ ASSERT_EQ(0, rbd_lock_acquire(blocklist_image, RBD_LOCK_MODE_EXCLUSIVE));
+
+ rbd_lock_mode_t lock_mode;
+ char *lock_owners[1];
+ size_t max_lock_owners = 1;
+ ASSERT_EQ(0, rbd_lock_get_owners(image, &lock_mode, lock_owners,
+ &max_lock_owners));
+ ASSERT_EQ(RBD_LOCK_MODE_EXCLUSIVE, lock_mode);
+ ASSERT_STRNE("", lock_owners[0]);
+ ASSERT_EQ(1U, max_lock_owners);
+
+ ASSERT_EQ(0, rbd_lock_break(image, RBD_LOCK_MODE_EXCLUSIVE, lock_owners[0]));
+ ASSERT_EQ(0, rbd_lock_acquire(image, RBD_LOCK_MODE_EXCLUSIVE));
+ EXPECT_EQ(0, rados_wait_for_latest_osdmap(blocklist_cluster));
+
+ ASSERT_EQ((ssize_t)sizeof(buf), rbd_write(image, 0, sizeof(buf), buf));
+ ASSERT_EQ(-EBLOCKLISTED, rbd_write(blocklist_image, 0, sizeof(buf), buf));
+
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_close(blocklist_image));
+
+ rbd_lock_get_owners_cleanup(lock_owners, max_lock_owners);
+
+ rados_ioctx_destroy(ioctx);
+ rados_ioctx_destroy(blocklist_ioctx);
+ rados_shutdown(blocklist_cluster);
+}
+
+TEST_F(TestLibRBD, DiscardAfterWrite)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 18;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ if (this->is_skip_partial_discard_enabled(image)) {
+ return;
+ }
+
+ // enable writeback cache
+ ASSERT_EQ(0, image.flush());
+
+ bufferlist bl;
+ bl.append(std::string(256, '1'));
+
+ librbd::RBD::AioCompletion *write_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_write(0, bl.length(), bl, write_comp));
+ ASSERT_EQ(0, write_comp->wait_for_complete());
+ write_comp->release();
+
+ librbd::RBD::AioCompletion *discard_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_discard(0, 256, discard_comp));
+ ASSERT_EQ(0, discard_comp->wait_for_complete());
+ discard_comp->release();
+
+ librbd::RBD::AioCompletion *read_comp =
+ new librbd::RBD::AioCompletion(NULL, NULL);
+ bufferlist read_bl;
+ image.aio_read(0, bl.length(), read_bl, read_comp);
+ ASSERT_EQ(0, read_comp->wait_for_complete());
+ ASSERT_EQ(bl.length(), read_comp->get_return_value());
+ ASSERT_TRUE(read_bl.is_zero());
+ read_comp->release();
+}
+
+TEST_F(TestLibRBD, DefaultFeatures) {
+ std::string orig_default_features;
+ ASSERT_EQ(0, _rados.conf_get("rbd_default_features", orig_default_features));
+ BOOST_SCOPE_EXIT_ALL(orig_default_features) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_default_features",
+ orig_default_features.c_str()));
+ };
+
+ std::list<std::pair<std::string, std::string> > feature_names_to_bitmask = {
+ {"", orig_default_features},
+ {"layering", "1"},
+ {"layering, exclusive-lock", "5"},
+ {"exclusive-lock,journaling", "68"},
+ {"125", "125"}
+ };
+
+ for (auto &pair : feature_names_to_bitmask) {
+ ASSERT_EQ(0, _rados.conf_set("rbd_default_features", pair.first.c_str()));
+ std::string features;
+ ASSERT_EQ(0, _rados.conf_get("rbd_default_features", features));
+ ASSERT_EQ(pair.second, features);
+ }
+}
+
+TEST_F(TestLibRBD, TestTrashMoveAndPurge) {
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name.c_str(), 0));
+
+ std::vector<std::string> images;
+ ASSERT_EQ(0, rbd.list(ioctx, images));
+ for (const auto& image : images) {
+ ASSERT_TRUE(image != name);
+ }
+
+ librbd::trash_image_info_t info;
+ ASSERT_EQ(-ENOENT, rbd.trash_get(ioctx, "dummy image id", &info));
+ ASSERT_EQ(0, rbd.trash_get(ioctx, image_id.c_str(), &info));
+ ASSERT_EQ(image_id, info.id);
+
+ std::vector<librbd::trash_image_info_t> entries;
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_FALSE(entries.empty());
+ ASSERT_EQ(entries.begin()->id, image_id);
+
+ entries.clear();
+ PrintProgress pp;
+ ASSERT_EQ(0, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ false, pp));
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_TRUE(entries.empty());
+}
+
+TEST_F(TestLibRBD, TestTrashMoveAndPurgeNonExpiredDelay) {
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name.c_str(), 100));
+
+ PrintProgress pp;
+ ASSERT_EQ(-EPERM, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ false, pp));
+
+ PrintProgress pp2;
+ ASSERT_EQ(0, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ true, pp2));
+}
+
+TEST_F(TestLibRBD, TestTrashPurge) {
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name1 = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name1.c_str(), size, &order));
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name2.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name1.c_str(), nullptr));
+
+ std::string image_id1;
+ ASSERT_EQ(0, image1.get_id(&image_id1));
+ image1.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name1.c_str(), 0));
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), nullptr));
+ ceph::bufferlist bl;
+ bl.append(std::string(1024, '0'));
+ ASSERT_EQ(1024, image2.write(0, 1024, bl));
+
+ std::string image_id2;
+ ASSERT_EQ(0, image2.get_id(&image_id2));
+ image2.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name2.c_str(), 100));
+ ASSERT_EQ(0, rbd.trash_purge(ioctx, 0, -1));
+
+ std::vector<librbd::trash_image_info_t> entries;
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_EQ(1U, entries.size());
+ ASSERT_EQ(image_id2, entries[0].id);
+ ASSERT_EQ(name2, entries[0].name);
+ entries.clear();
+
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ float threshold = 0.0;
+ if (!is_librados_test_stub(_rados)) {
+ // real cluster usage reports have a long latency to update
+ threshold = -1.0;
+ }
+
+ ASSERT_EQ(0, rbd.trash_purge(ioctx, now.tv_sec+1000, threshold));
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_EQ(0U, entries.size());
+}
+
+TEST_F(TestLibRBD, TestTrashMoveAndRestore) {
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name.c_str(), 10));
+
+ std::vector<std::string> images;
+ ASSERT_EQ(0, rbd.list(ioctx, images));
+ for (const auto& image : images) {
+ ASSERT_TRUE(image != name);
+ }
+
+ std::vector<librbd::trash_image_info_t> entries;
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_FALSE(entries.empty());
+ ASSERT_EQ(entries.begin()->id, image_id);
+
+ images.clear();
+ ASSERT_EQ(0, rbd.trash_restore(ioctx, image_id.c_str(), ""));
+ ASSERT_EQ(0, rbd.list(ioctx, images));
+ ASSERT_FALSE(images.empty());
+ bool found = false;
+ for (const auto& image : images) {
+ if (image == name) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+}
+
+TEST_F(TestLibRBD, TestListWatchers) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ std::list<librbd::image_watcher_t> watchers;
+
+ // No watchers
+ ASSERT_EQ(0, rbd.open_read_only(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.list_watchers(watchers));
+ ASSERT_EQ(0U, watchers.size());
+ ASSERT_EQ(0, image.close());
+
+ // One watcher
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.list_watchers(watchers));
+ ASSERT_EQ(1U, watchers.size());
+ auto watcher1 = watchers.front();
+ ASSERT_EQ(0, image.close());
+
+ // (Still) one watcher
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.list_watchers(watchers));
+ ASSERT_EQ(1U, watchers.size());
+ auto watcher2 = watchers.front();
+ ASSERT_EQ(0, image.close());
+
+ EXPECT_EQ(watcher1.addr, watcher2.addr);
+ EXPECT_EQ(watcher1.id, watcher2.id);
+}
+
+TEST_F(TestLibRBD, TestSetSnapById) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.snap_create("snap"));
+
+ vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_EQ(1U, snaps.size());
+
+ ASSERT_EQ(0, image.snap_set_by_id(snaps[0].id));
+ ASSERT_EQ(0, image.snap_set_by_id(CEPH_NOSNAP));
+}
+
+TEST_F(TestLibRBD, Namespaces) {
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+ rados_remove(ioctx, RBD_NAMESPACE);
+
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name1"));
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name2"));
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name3"));
+ ASSERT_EQ(0, rbd_namespace_remove(ioctx, "name2"));
+
+ char names[1024];
+ size_t max_size = sizeof(names);
+ int len = rbd_namespace_list(ioctx, names, &max_size);
+
+ std::vector<std::string> cpp_names;
+ for (char* cur_name = names; cur_name < names + len; ) {
+ cpp_names.push_back(cur_name);
+ cur_name += strlen(cur_name) + 1;
+ }
+ ASSERT_EQ(2U, cpp_names.size());
+ ASSERT_EQ("name1", cpp_names[0]);
+ ASSERT_EQ("name3", cpp_names[1]);
+ bool exists;
+ ASSERT_EQ(0, rbd_namespace_exists(ioctx, "name2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, rbd_namespace_exists(ioctx, "name3", &exists));
+ ASSERT_TRUE(exists);
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, NamespacesPP) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_NAMESPACE);
+
+ librbd::RBD rbd;
+ ASSERT_EQ(-EINVAL, rbd.namespace_create(ioctx, ""));
+ ASSERT_EQ(-EINVAL, rbd.namespace_remove(ioctx, ""));
+
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name1"));
+ ASSERT_EQ(-EEXIST, rbd.namespace_create(ioctx, "name1"));
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name2"));
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name3"));
+ ASSERT_EQ(0, rbd.namespace_remove(ioctx, "name2"));
+ ASSERT_EQ(-ENOENT, rbd.namespace_remove(ioctx, "name2"));
+
+ std::vector<std::string> names;
+ ASSERT_EQ(0, rbd.namespace_list(ioctx, &names));
+ ASSERT_EQ(2U, names.size());
+ ASSERT_EQ("name1", names[0]);
+ ASSERT_EQ("name3", names[1]);
+ bool exists;
+ ASSERT_EQ(0, rbd.namespace_exists(ioctx, "name2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, rbd.namespace_exists(ioctx, "name3", &exists));
+ ASSERT_TRUE(exists);
+
+ librados::IoCtx ns_io_ctx;
+ ns_io_ctx.dup(ioctx);
+
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t features = 0;
+ if (!get_features(&features)) {
+ // old format doesn't support namespaces
+ ns_io_ctx.set_namespace("name1");
+ ASSERT_EQ(-EINVAL, create_image_pp(rbd, ns_io_ctx, name.c_str(), 0,
+ &order));
+ return;
+ }
+
+ ns_io_ctx.set_namespace("missing");
+ ASSERT_EQ(-ENOENT, create_image_pp(rbd, ns_io_ctx, name.c_str(), 0, &order));
+
+ ns_io_ctx.set_namespace("name1");
+ ASSERT_EQ(0, create_image_pp(rbd, ns_io_ctx, name.c_str(), 0, &order));
+ ASSERT_EQ(-EBUSY, rbd.namespace_remove(ns_io_ctx, "name1"));
+
+ std::string image_id;
+ {
+ librbd::Image image;
+ ASSERT_EQ(-ENOENT, rbd.open(ioctx, image, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ns_io_ctx, image, name.c_str(), NULL));
+ ASSERT_EQ(0, get_image_id(image, &image_id));
+ }
+
+ ASSERT_EQ(-ENOENT, rbd.trash_move(ioctx, name.c_str(), 0));
+ ASSERT_EQ(0, rbd.trash_move(ns_io_ctx, name.c_str(), 0));
+ ASSERT_EQ(-EBUSY, rbd.namespace_remove(ns_io_ctx, "name1"));
+
+ PrintProgress pp;
+ ASSERT_EQ(-ENOENT, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ false, pp));
+ ASSERT_EQ(0, rbd.trash_remove_with_progress(ns_io_ctx, image_id.c_str(),
+ false, pp));
+ ASSERT_EQ(0, rbd.namespace_remove(ns_io_ctx, "name1"));
+
+ names.clear();
+ ASSERT_EQ(0, rbd.namespace_list(ioctx, &names));
+ ASSERT_EQ(1U, names.size());
+ ASSERT_EQ("name3", names[0]);
+}
+
+TEST_F(TestLibRBD, Migration) {
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+ BOOST_SCOPE_EXIT(&ioctx) {
+ rados_ioctx_destroy(ioctx);
+ } BOOST_SCOPE_EXIT_END;
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_image_options_t image_options;
+ rbd_image_options_create(&image_options);
+ BOOST_SCOPE_EXIT(&image_options) {
+ rbd_image_options_destroy(image_options);
+ } BOOST_SCOPE_EXIT_END;
+
+ ASSERT_EQ(0, rbd_migration_prepare(ioctx, name.c_str(), ioctx, name.c_str(),
+ image_options));
+
+ rbd_image_migration_status_t status;
+ ASSERT_EQ(0, rbd_migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.source_pool_id, rados_ioctx_get_id(ioctx));
+ ASSERT_EQ(status.source_image_name, name);
+ if (old_format) {
+ ASSERT_EQ(status.source_image_id, string());
+ } else {
+ ASSERT_NE(status.source_image_id, string());
+ ASSERT_EQ(-EROFS, rbd_trash_remove(ioctx, status.source_image_id, false));
+ ASSERT_EQ(-EINVAL, rbd_trash_restore(ioctx, status.source_image_id, name.c_str()));
+ }
+ ASSERT_EQ(status.dest_pool_id, rados_ioctx_get_id(ioctx));
+ ASSERT_EQ(status.dest_image_name, name);
+ ASSERT_NE(status.dest_image_id, string());
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ rbd_migration_status_cleanup(&status);
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ char source_spec[2048];
+ size_t source_spec_length = sizeof(source_spec);
+ ASSERT_EQ(0, rbd_get_migration_source_spec(image, source_spec,
+ &source_spec_length));
+ json_spirit::mValue json_source_spec;
+ json_spirit::read(source_spec, json_source_spec);
+ EXPECT_EQ(0, rbd_close(image));
+
+ ASSERT_EQ(-EBUSY, rbd_remove(ioctx, name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd_trash_move(ioctx, name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd_migration_execute(ioctx, name.c_str()));
+
+ ASSERT_EQ(0, rbd_migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ rbd_migration_status_cleanup(&status);
+
+ ASSERT_EQ(0, rbd_migration_commit(ioctx, name.c_str()));
+
+ std::string new_name = get_temp_image_name();
+
+ ASSERT_EQ(0, rbd_migration_prepare(ioctx, name.c_str(), ioctx,
+ new_name.c_str(), image_options));
+
+ ASSERT_EQ(-EBUSY, rbd_remove(ioctx, new_name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd_trash_move(ioctx, new_name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd_migration_abort(ioctx, name.c_str()));
+
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ EXPECT_EQ(0, rbd_close(image));
+}
+
+TEST_F(TestLibRBD, MigrationPP) {
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::ImageOptions image_options;
+
+ ASSERT_EQ(0, rbd.migration_prepare(ioctx, name.c_str(), ioctx, name.c_str(),
+ image_options));
+
+ librbd::image_migration_status_t status;
+ ASSERT_EQ(0, rbd.migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.source_pool_id, ioctx.get_id());
+ ASSERT_EQ(status.source_image_name, name);
+ if (old_format) {
+ ASSERT_EQ(status.source_image_id, "");
+ } else {
+ ASSERT_NE(status.source_image_id, "");
+ ASSERT_EQ(-EROFS, rbd.trash_remove(ioctx, status.source_image_id.c_str(), false));
+ ASSERT_EQ(-EINVAL, rbd.trash_restore(ioctx, status.source_image_id.c_str(), name.c_str()));
+ }
+ ASSERT_EQ(status.dest_pool_id, ioctx.get_id());
+ ASSERT_EQ(status.dest_image_name, name);
+ ASSERT_NE(status.dest_image_id, "");
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ std::string source_spec;
+ ASSERT_EQ(0, image.get_migration_source_spec(&source_spec));
+ json_spirit::mValue json_source_spec;
+ json_spirit::read(source_spec, json_source_spec);
+ json_spirit::mObject json_source_spec_obj = json_source_spec.get_obj();
+ ASSERT_EQ("native", json_source_spec_obj["type"].get_str());
+ ASSERT_EQ(ioctx.get_id(), json_source_spec_obj["pool_id"].get_int64());
+ ASSERT_EQ("", json_source_spec_obj["pool_namespace"].get_str());
+ ASSERT_EQ(1, json_source_spec_obj.count("image_name"));
+ if (!old_format) {
+ ASSERT_EQ(1, json_source_spec_obj.count("image_id"));
+ }
+ ASSERT_EQ(0, image.close());
+
+ ASSERT_EQ(-EBUSY, rbd.remove(ioctx, name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd.trash_move(ioctx, name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd.migration_execute(ioctx, name.c_str()));
+
+ ASSERT_EQ(0, rbd.migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+
+ ASSERT_EQ(0, rbd.migration_commit(ioctx, name.c_str()));
+
+ std::string new_name = get_temp_image_name();
+
+ ASSERT_EQ(0, rbd.migration_prepare(ioctx, name.c_str(), ioctx,
+ new_name.c_str(), image_options));
+
+ ASSERT_EQ(-EBUSY, rbd.remove(ioctx, new_name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd.trash_move(ioctx, new_name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd.migration_abort(ioctx, name.c_str()));
+
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+}
+
+TEST_F(TestLibRBD, TestGetAccessTimestamp)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ struct timespec timestamp;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_get_access_timestamp(image, &timestamp));
+ ASSERT_LT(0, timestamp.tv_sec);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestGetModifyTimestamp)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ struct timespec timestamp;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(0, rbd_get_modify_timestamp(image, &timestamp));
+ ASSERT_LT(0, timestamp.tv_sec);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, ZeroOverlapFlatten) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image parent_image;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1;
+ int order = 0;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, name.c_str(), NULL));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap", ioctx, clone_name.c_str(),
+ features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+ ASSERT_EQ(0, clone_image.resize(0));
+ ASSERT_EQ(0, clone_image.flatten());
+}
+
+TEST_F(TestLibRBD, PoolMetadata)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(0U, keys_len);
+ ASSERT_EQ(0U, vals_len);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+ memset_rand(value, value_len);
+
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key2", "value2"));
+ ASSERT_EQ(0, rbd_pool_metadata_get(ioctx, "key1", value, &value_len));
+ ASSERT_STREQ(value, "value1");
+ value_len = 1;
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_get(ioctx, "key1", value, &value_len));
+ ASSERT_EQ(value_len, strlen("value1") + 1);
+
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
+ ASSERT_STREQ(keys, "key1");
+ ASSERT_STREQ(keys + strlen(keys) + 1, "key2");
+ ASSERT_STREQ(vals, "value1");
+ ASSERT_STREQ(vals + strlen(vals) + 1, "value2");
+
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(-ENOENT, rbd_pool_metadata_remove(ioctx, "key3"));
+ value_len = sizeof(value);
+ ASSERT_EQ(-ENOENT, rbd_pool_metadata_get(ioctx, "key3", value, &value_len));
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value2") + 1);
+ ASSERT_STREQ(keys, "key2");
+ ASSERT_STREQ(vals, "value2");
+
+ // test config setting
+ ASSERT_EQ(-EINVAL, rbd_pool_metadata_set(ioctx, "conf_UNKNOWN", "false"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+ ASSERT_EQ(-EINVAL, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "INVALID"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ // test short buffer cases
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key3", "value3"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key4", "value4"));
+
+ keys_len = strlen("key1") + 1;
+ vals_len = strlen("value1") + 1;
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 1, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1);
+ ASSERT_STREQ(keys, "key1");
+ ASSERT_STREQ(vals, "value1");
+
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 2, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
+
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") +
+ 1 + strlen("key4") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1 +
+ strlen("value3") + 1 + strlen("value4") + 1);
+
+ // test `start` param
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "key2", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key3") + 1 + strlen("key4") + 1);
+ ASSERT_EQ(vals_len, strlen("value3") + 1 + strlen("value4") + 1);
+ ASSERT_STREQ(keys, "key3");
+ ASSERT_STREQ(vals, "value3");
+
+ //cleanup
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key2"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key3"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key4"));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, PoolMetadataPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librbd::RBD rbd;
+ string value;
+ map<string, bufferlist> pairs;
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs));
+ ASSERT_TRUE(pairs.empty());
+
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key2", "value2"));
+ ASSERT_EQ(0, rbd.pool_metadata_get(ioctx, "key1", &value));
+ ASSERT_EQ(value, "value1");
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs));
+ ASSERT_EQ(2U, pairs.size());
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(-ENOENT, rbd.pool_metadata_remove(ioctx, "key3"));
+ ASSERT_EQ(-ENOENT, rbd.pool_metadata_get(ioctx, "key3", &value));
+ pairs.clear();
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs));
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ // test `start` param
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key3", "value3"));
+
+ pairs.clear();
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "key2", 0, &pairs));
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6));
+
+ // test config setting
+ ASSERT_EQ(-EINVAL, rbd.pool_metadata_set(ioctx, "conf_UNKNOWN", "false"));
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+ ASSERT_EQ(-EINVAL, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "INVALID"));
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ // cleanup
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key2"));
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key3"));
+}
+
+TEST_F(TestLibRBD, Config)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+
+ rbd_config_option_t options[1024];
+ int max_options = 0;
+ ASSERT_EQ(-ERANGE, rbd_config_pool_list(ioctx, options, &max_options));
+ ASSERT_EQ(0, rbd_config_pool_list(ioctx, options, &max_options));
+ ASSERT_GT(max_options, 0);
+ ASSERT_LT(max_options, 1024);
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_STREQ("false", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_pool_list_cleanup(options, max_options);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_STREQ("false", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_image_list_cleanup(options, max_options);
+
+ ASSERT_EQ(0, rbd_metadata_set(image, "conf_rbd_cache", "true"));
+
+ ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_IMAGE);
+ ASSERT_STREQ("true", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_image_list_cleanup(options, max_options);
+
+ ASSERT_EQ(0, rbd_metadata_remove(image, "conf_rbd_cache"));
+
+ ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_STREQ("false", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_image_list_cleanup(options, max_options);
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ ASSERT_EQ(-ERANGE, rbd_config_pool_list(ioctx, options, &max_options));
+ ASSERT_EQ(0, rbd_config_pool_list(ioctx, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ rbd_config_pool_list_cleanup(options, max_options);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, ConfigPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librbd::RBD rbd;
+ string value;
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+
+ std::vector<librbd::config_option_t> options;
+ ASSERT_EQ(0, rbd.config_list(ioctx, &options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_EQ("false", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ options.clear();
+ ASSERT_EQ(0, image.config_list(&options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_EQ("false", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ ASSERT_EQ(0, image.metadata_set("conf_rbd_cache", "true"));
+
+ options.clear();
+ ASSERT_EQ(0, image.config_list(&options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_IMAGE);
+ ASSERT_EQ("true", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ ASSERT_EQ(0, image.metadata_remove("conf_rbd_cache"));
+
+ options.clear();
+ ASSERT_EQ(0, image.config_list(&options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_EQ("false", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ options.clear();
+ ASSERT_EQ(0, rbd.config_list(ioctx, &options));
+ for (auto &option : options) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+}
+
+TEST_F(TestLibRBD, PoolStatsPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string image_name;
+ uint64_t size = 2 << 20;
+ uint64_t expected_size = 0;
+ for (size_t idx = 0; idx < 4; ++idx) {
+ image_name = get_temp_image_name();
+
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, image_name.c_str(), size, &order));
+ expected_size += size;
+ }
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(0, image.resize(0));
+ ASSERT_EQ(0, image.close());
+ uint64_t expect_head_size = (expected_size - size);
+
+ uint64_t image_count;
+ uint64_t provisioned_bytes;
+ uint64_t max_provisioned_bytes;
+ uint64_t snap_count;
+ uint64_t trash_image_count;
+ uint64_t trash_provisioned_bytes;
+ uint64_t trash_max_provisioned_bytes;
+ uint64_t trash_snap_count;
+
+ librbd::PoolStats pool_stats1;
+ pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGES, &image_count);
+ pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
+ &provisioned_bytes);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats1));
+
+ ASSERT_EQ(4U, image_count);
+ ASSERT_EQ(expect_head_size, provisioned_bytes);
+
+ pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
+ &max_provisioned_bytes);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats1));
+ ASSERT_EQ(4U, image_count);
+ ASSERT_EQ(expect_head_size, provisioned_bytes);
+ ASSERT_EQ(expected_size, max_provisioned_bytes);
+
+ librbd::PoolStats pool_stats2;
+ pool_stats2.add(RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS, &snap_count);
+ pool_stats2.add(RBD_POOL_STAT_OPTION_TRASH_IMAGES, &trash_image_count);
+ pool_stats2.add(RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &trash_snap_count);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats2));
+ ASSERT_EQ(1U, snap_count);
+ ASSERT_EQ(0U, trash_image_count);
+ ASSERT_EQ(0U, trash_snap_count);
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, image_name.c_str(), 0));
+
+ librbd::PoolStats pool_stats3;
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_IMAGES, &trash_image_count);
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
+ &trash_provisioned_bytes);
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
+ &trash_max_provisioned_bytes);
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &trash_snap_count);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats3));
+ ASSERT_EQ(1U, trash_image_count);
+ ASSERT_EQ(0U, trash_provisioned_bytes);
+ ASSERT_EQ(size, trash_max_provisioned_bytes);
+ ASSERT_EQ(1U, trash_snap_count);
+}
+
+TEST_F(TestLibRBD, ImageSpec) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image parent_image;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1;
+ int order = 0;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, name.c_str(), NULL));
+
+ std::string parent_id;
+ ASSERT_EQ(0, parent_image.get_id(&parent_id));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap", ioctx, clone_name.c_str(),
+ features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+
+ std::string clone_id;
+ ASSERT_EQ(0, clone_image.get_id(&clone_id));
+
+ std::vector<librbd::image_spec_t> images;
+ ASSERT_EQ(0, rbd.list2(ioctx, &images));
+
+ std::vector<librbd::image_spec_t> expected_images{
+ {.id = parent_id, .name = name},
+ {.id = clone_id, .name = clone_name}
+ };
+ std::sort(expected_images.begin(), expected_images.end(),
+ [](const librbd::image_spec_t& lhs, const librbd::image_spec_t &rhs) {
+ return lhs.name < rhs.name;
+ });
+ ASSERT_EQ(expected_images, images);
+
+ librbd::linked_image_spec_t parent_image_spec;
+ librbd::snap_spec_t parent_snap_spec;
+ ASSERT_EQ(0, clone_image.get_parent(&parent_image_spec, &parent_snap_spec));
+
+ librbd::linked_image_spec_t expected_parent_image_spec{
+ .pool_id = ioctx.get_id(),
+ .pool_name = ioctx.get_pool_name(),
+ .pool_namespace = ioctx.get_namespace(),
+ .image_id = parent_id,
+ .image_name = name,
+ .trash = false
+ };
+ ASSERT_EQ(expected_parent_image_spec, parent_image_spec);
+ ASSERT_EQ(RBD_SNAP_NAMESPACE_TYPE_USER, parent_snap_spec.namespace_type);
+ ASSERT_EQ("snap", parent_snap_spec.name);
+
+ std::vector<librbd::linked_image_spec_t> children;
+ ASSERT_EQ(0, parent_image.list_children3(&children));
+
+ std::vector<librbd::linked_image_spec_t> expected_children{
+ {
+ .pool_id = ioctx.get_id(),
+ .pool_name = ioctx.get_pool_name(),
+ .pool_namespace = ioctx.get_namespace(),
+ .image_id = clone_id,
+ .image_name = clone_name,
+ .trash = false
+ }
+ };
+ ASSERT_EQ(expected_children, children);
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_descendants(&children));
+ ASSERT_EQ(expected_children, children);
+
+ ASSERT_EQ(0, clone_image.snap_create("snap"));
+ ASSERT_EQ(0, clone_image.snap_protect("snap"));
+
+ auto grand_clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, clone_name.c_str(), "snap", ioctx,
+ grand_clone_name.c_str(), features, &order));
+ librbd::Image grand_clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, grand_clone_image, grand_clone_name.c_str(),
+ nullptr));
+ std::string grand_clone_id;
+ ASSERT_EQ(0, grand_clone_image.get_id(&grand_clone_id));
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_children3(&children));
+ ASSERT_EQ(expected_children, children);
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_descendants(&children));
+ expected_children.push_back(
+ {
+ .pool_id = ioctx.get_id(),
+ .pool_name = ioctx.get_pool_name(),
+ .pool_namespace = ioctx.get_namespace(),
+ .image_id = grand_clone_id,
+ .image_name = grand_clone_name,
+ .trash = false
+ }
+ );
+ ASSERT_EQ(expected_children, children);
+}
+
+void super_simple_write_cb_pp(librbd::completion_t cb, void *arg)
+{
+}
+
+TEST_F(TestLibRBD, DISABLED_TestSeqWriteAIOPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 21;
+ std::string name = get_temp_image_name();
+ uint64_t size = 5 * (1 << order);
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ char test_data[(TEST_IO_SIZE + 1) * 10];
+
+ for (int i = 0; i < 10; i++) {
+ for (uint64_t j = 0; j < TEST_IO_SIZE; j++) {
+ test_data[(TEST_IO_SIZE + 1) * i + j] = (char)(rand() % (126 - 33) + 33);
+ }
+ test_data[(TEST_IO_SIZE + 1) * i + TEST_IO_SIZE] = '\0';
+ }
+
+ struct timespec start_time;
+ clock_gettime(CLOCK_REALTIME, &start_time);
+
+ std::list<librbd::RBD::AioCompletion *> comps;
+ for (uint64_t i = 0; i < size / TEST_IO_SIZE; ++i) {
+ char *p = test_data + (TEST_IO_SIZE + 1) * (i % 10);
+ ceph::bufferlist bl;
+ bl.append(p, strlen(p));
+ auto comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) super_simple_write_cb_pp);
+ image.aio_write(strlen(p) * i, strlen(p), bl, comp);
+ comps.push_back(comp);
+ if (i % 1000 == 0) {
+ cout << i << " reqs sent" << std::endl;
+ image.flush();
+ for (auto comp : comps) {
+ comp->wait_for_complete();
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+ }
+ comps.clear();
+ }
+ }
+ int i = 0;
+ for (auto comp : comps) {
+ comp->wait_for_complete();
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+ if (i % 1000 == 0) {
+ std::cout << i << " reqs completed" << std::endl;
+ }
+ i++;
+ }
+ comps.clear();
+
+ struct timespec end_time;
+ clock_gettime(CLOCK_REALTIME, &end_time);
+ int duration = end_time.tv_sec * 1000 + end_time.tv_nsec / 1000000 -
+ start_time.tv_sec * 1000 - start_time.tv_nsec / 1000000;
+ std::cout << "duration: " << duration << " msec" << std::endl;
+
+ for (uint64_t i = 0; i < size / TEST_IO_SIZE; ++i) {
+ char *p = test_data + (TEST_IO_SIZE + 1) * (i % 10);
+ ASSERT_PASSED(read_test_data, image, p, strlen(p) * i, TEST_IO_SIZE, 0);
+ }
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, SnapRemoveWithChildMissing)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "2"));
+ BOOST_SCOPE_EXIT_ALL(&) {
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "auto"));
+ };
+
+ librbd::RBD rbd;
+ rados_ioctx_t ioctx1, ioctx2;
+ string pool_name1 = create_pool(true);
+ rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx2));
+
+ bool old_format;
+ uint64_t features;
+ rbd_image_t parent, child1, child2, child3;
+ int order = 0;
+ char child_id1[4096];
+ char child_id2[4096];
+ char child_id3[4096];
+
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+ std::string parent_name = get_temp_image_name();
+ std::string child_name1 = get_temp_image_name();
+ std::string child_name2 = get_temp_image_name();
+ std::string child_name3 = get_temp_image_name();
+ ASSERT_EQ(0, create_image_full(ioctx1, parent_name.c_str(), 4<<20, &order,
+ false, features));
+ ASSERT_EQ(0, rbd_open(ioctx1, parent_name.c_str(), &parent, NULL));
+ ASSERT_EQ(0, rbd_snap_create(parent, "snap1"));
+ ASSERT_EQ(0, rbd_snap_create(parent, "snap2"));
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "snap1",
+ ioctx2, child_name1.c_str(), features, &order));
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "snap2",
+ ioctx1, child_name2.c_str(), features, &order));
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "snap2",
+ ioctx2, child_name3.c_str(), features, &order));
+
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &child1, NULL));
+ ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &child2, NULL));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &child3, NULL));
+ ASSERT_EQ(0, rbd_get_id(child1, child_id1, sizeof(child_id1)));
+ ASSERT_EQ(0, rbd_get_id(child2, child_id2, sizeof(child_id2)));
+ ASSERT_EQ(0, rbd_get_id(child3, child_id3, sizeof(child_id3)));
+ test_list_children2(parent, 3,
+ child_id1, m_pool_name.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, m_pool_name.c_str(), child_name3.c_str(), false);
+
+ size_t max_size = 10;
+ rbd_linked_image_spec_t children[max_size];
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(3, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+
+ ASSERT_EQ(0, rbd_close(child1));
+ ASSERT_EQ(0, rbd_close(child2));
+ ASSERT_EQ(0, rbd_close(child3));
+ rados_ioctx_destroy(ioctx2);
+ ASSERT_EQ(0, rados_pool_delete(_cluster, m_pool_name.c_str()));
+ _pool_names.erase(std::remove(_pool_names.begin(),
+ _pool_names.end(), m_pool_name),
+ _pool_names.end());
+ EXPECT_EQ(0, rados_wait_for_latest_osdmap(_cluster));
+
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(3, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+ ASSERT_EQ(0, rbd_snap_remove(parent, "snap1"));
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(2, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+
+ ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(1, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+
+ ASSERT_EQ(0, rbd_snap_remove(parent, "snap2"));
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(0, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+ test_list_children2(parent, 0);
+ ASSERT_EQ(0, test_ls_snaps(parent, 0));
+
+ ASSERT_EQ(0, rbd_close(parent));
+ rados_ioctx_destroy(ioctx1);
+}
+
+TEST_F(TestLibRBD, QuiesceWatch)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_image_t image1, image2;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image1, NULL));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image2, NULL));
+
+ struct Watcher {
+ static void quiesce_cb(void *arg) {
+ Watcher *watcher = static_cast<Watcher *>(arg);
+ watcher->handle_quiesce();
+ }
+ static void unquiesce_cb(void *arg) {
+ Watcher *watcher = static_cast<Watcher *>(arg);
+ watcher->handle_unquiesce();
+ }
+
+ rbd_image_t &image;
+ uint64_t handle = 0;
+ size_t quiesce_count = 0;
+ size_t unquiesce_count = 0;
+
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cv;
+
+ Watcher(rbd_image_t &image) : image(image) {
+ }
+
+ void handle_quiesce() {
+ ASSERT_EQ(quiesce_count, unquiesce_count);
+ quiesce_count++;
+ rbd_quiesce_complete(image, handle, 0);
+ }
+ void handle_unquiesce() {
+ std::unique_lock locker(lock);
+ unquiesce_count++;
+ ASSERT_EQ(quiesce_count, unquiesce_count);
+ cv.notify_one();
+ }
+ bool wait_for_unquiesce(size_t c) {
+ std::unique_lock locker(lock);
+ return cv.wait_for(locker, seconds(60),
+ [this, c]() { return unquiesce_count >= c; });
+ }
+ } watcher1(image1), watcher2(image2);
+
+ ASSERT_EQ(0, rbd_quiesce_watch(image1, Watcher::quiesce_cb,
+ Watcher::unquiesce_cb, &watcher1,
+ &watcher1.handle));
+ ASSERT_EQ(0, rbd_quiesce_watch(image2, Watcher::quiesce_cb,
+ Watcher::unquiesce_cb, &watcher2,
+ &watcher2.handle));
+
+ ASSERT_EQ(0, rbd_snap_create(image1, "snap1"));
+ ASSERT_EQ(1U, watcher1.quiesce_count);
+ ASSERT_TRUE(watcher1.wait_for_unquiesce(1U));
+ ASSERT_EQ(1U, watcher2.quiesce_count);
+ ASSERT_TRUE(watcher2.wait_for_unquiesce(1U));
+
+ ASSERT_EQ(0, rbd_snap_create(image2, "snap2"));
+ ASSERT_EQ(2U, watcher1.quiesce_count);
+ ASSERT_TRUE(watcher1.wait_for_unquiesce(2U));
+ ASSERT_EQ(2U, watcher2.quiesce_count);
+ ASSERT_TRUE(watcher2.wait_for_unquiesce(2U));
+
+ ASSERT_EQ(0, rbd_quiesce_unwatch(image1, watcher1.handle));
+
+ ASSERT_EQ(0, rbd_snap_create(image1, "snap3"));
+ ASSERT_EQ(2U, watcher1.quiesce_count);
+ ASSERT_EQ(2U, watcher1.unquiesce_count);
+ ASSERT_EQ(3U, watcher2.quiesce_count);
+ ASSERT_TRUE(watcher2.wait_for_unquiesce(3U));
+
+ ASSERT_EQ(0, rbd_quiesce_unwatch(image2, watcher2.handle));
+
+ ASSERT_EQ(0, rbd_snap_remove(image1, "snap1"));
+ ASSERT_EQ(0, rbd_snap_remove(image1, "snap2"));
+ ASSERT_EQ(0, rbd_snap_remove(image1, "snap3"));
+ ASSERT_EQ(0, rbd_close(image1));
+ ASSERT_EQ(0, rbd_close(image2));
+ ASSERT_EQ(0, rbd_remove(ioctx, name.c_str()));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, QuiesceWatchPP)
+{
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ {
+ librbd::Image image1, image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ struct Watcher : public librbd::QuiesceWatchCtx {
+ librbd::Image &image;
+ uint64_t handle = 0;
+ size_t quiesce_count = 0;
+ size_t unquiesce_count = 0;
+
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cv;
+
+ Watcher(librbd::Image &image) : image(image) {
+ }
+
+ void handle_quiesce() override {
+ ASSERT_EQ(quiesce_count, unquiesce_count);
+ quiesce_count++;
+ image.quiesce_complete(handle, 0);
+ }
+ void handle_unquiesce() override {
+ std::unique_lock locker(lock);
+ unquiesce_count++;
+ ASSERT_EQ(quiesce_count, unquiesce_count);
+ cv.notify_one();
+ }
+ bool wait_for_unquiesce(size_t c) {
+ std::unique_lock locker(lock);
+ return cv.wait_for(locker, seconds(60),
+ [this, c]() { return unquiesce_count >= c; });
+ }
+ } watcher1(image1), watcher2(image2);
+
+ ASSERT_EQ(0, image1.quiesce_watch(&watcher1, &watcher1.handle));
+ ASSERT_EQ(0, image2.quiesce_watch(&watcher2, &watcher2.handle));
+
+ ASSERT_EQ(0, image1.snap_create("snap1"));
+ ASSERT_EQ(1U, watcher1.quiesce_count);
+ ASSERT_TRUE(watcher1.wait_for_unquiesce(1U));
+ ASSERT_EQ(1U, watcher2.quiesce_count);
+ ASSERT_TRUE(watcher2.wait_for_unquiesce(1U));
+
+ ASSERT_EQ(0, image2.snap_create("snap2"));
+ ASSERT_EQ(2U, watcher1.quiesce_count);
+ ASSERT_TRUE(watcher1.wait_for_unquiesce(2U));
+ ASSERT_EQ(2U, watcher2.quiesce_count);
+ ASSERT_TRUE(watcher2.wait_for_unquiesce(2U));
+
+ ASSERT_EQ(0, image1.quiesce_unwatch(watcher1.handle));
+
+ ASSERT_EQ(0, image1.snap_create("snap3"));
+ ASSERT_EQ(2U, watcher1.quiesce_count);
+ ASSERT_EQ(2U, watcher1.unquiesce_count);
+ ASSERT_EQ(3U, watcher2.quiesce_count);
+ ASSERT_TRUE(watcher2.wait_for_unquiesce(3U));
+
+ ASSERT_EQ(0, image2.quiesce_unwatch(watcher2.handle));
+
+ ASSERT_EQ(0, image1.snap_remove("snap1"));
+ ASSERT_EQ(0, image1.snap_remove("snap2"));
+ ASSERT_EQ(0, image1.snap_remove("snap3"));
+ }
+
+ ASSERT_EQ(0, rbd.remove(ioctx, name.c_str()));
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, QuiesceWatchError)
+{
+ SKIP_IF_CRIMSON();
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ {
+ librbd::Image image1, image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ struct Watcher : public librbd::QuiesceWatchCtx {
+ librbd::Image &image;
+ int r;
+ uint64_t handle;
+ size_t quiesce_count = 0;
+ size_t unquiesce_count = 0;
+
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cv;
+
+ Watcher(librbd::Image &image, int r) : image(image), r(r) {
+ }
+
+ void reset_counters() {
+ quiesce_count = 0;
+ unquiesce_count = 0;
+ }
+
+ void handle_quiesce() override {
+ quiesce_count++;
+ image.quiesce_complete(handle, r);
+ }
+
+ void handle_unquiesce() override {
+ std::unique_lock locker(lock);
+ unquiesce_count++;
+ cv.notify_one();
+ }
+
+ bool wait_for_unquiesce() {
+ std::unique_lock locker(lock);
+ return cv.wait_for(locker, seconds(60),
+ [this]() {
+ return quiesce_count == unquiesce_count;
+ });
+ }
+ } watcher10(image1, -EINVAL), watcher11(image1, 0), watcher20(image2, 0);
+
+ ASSERT_EQ(0, image1.quiesce_watch(&watcher10, &watcher10.handle));
+ ASSERT_EQ(0, image1.quiesce_watch(&watcher11, &watcher11.handle));
+ ASSERT_EQ(0, image2.quiesce_watch(&watcher20, &watcher20.handle));
+
+ ASSERT_EQ(-EINVAL, image1.snap_create("snap1"));
+ ASSERT_GT(watcher10.quiesce_count, 0U);
+ ASSERT_EQ(watcher10.unquiesce_count, 0U);
+ ASSERT_GT(watcher11.quiesce_count, 0U);
+ ASSERT_TRUE(watcher11.wait_for_unquiesce());
+ ASSERT_GT(watcher20.quiesce_count, 0U);
+ ASSERT_TRUE(watcher20.wait_for_unquiesce());
+
+ PrintProgress prog_ctx;
+ watcher10.reset_counters();
+ watcher11.reset_counters();
+ watcher20.reset_counters();
+ ASSERT_EQ(0, image2.snap_create2("snap2",
+ RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR,
+ prog_ctx));
+ ASSERT_GT(watcher10.quiesce_count, 0U);
+ ASSERT_EQ(watcher10.unquiesce_count, 0U);
+ ASSERT_GT(watcher11.quiesce_count, 0U);
+ ASSERT_TRUE(watcher11.wait_for_unquiesce());
+ ASSERT_GT(watcher20.quiesce_count, 0U);
+ ASSERT_TRUE(watcher20.wait_for_unquiesce());
+
+ ASSERT_EQ(0, image1.quiesce_unwatch(watcher10.handle));
+
+ watcher11.reset_counters();
+ watcher20.reset_counters();
+ ASSERT_EQ(0, image1.snap_create("snap3"));
+ ASSERT_GT(watcher11.quiesce_count, 0U);
+ ASSERT_TRUE(watcher11.wait_for_unquiesce());
+ ASSERT_GT(watcher20.quiesce_count, 0U);
+ ASSERT_TRUE(watcher20.wait_for_unquiesce());
+
+ ASSERT_EQ(0, image1.quiesce_unwatch(watcher11.handle));
+
+ watcher20.reset_counters();
+ ASSERT_EQ(0, image2.snap_create2("snap4", RBD_SNAP_CREATE_SKIP_QUIESCE,
+ prog_ctx));
+ ASSERT_EQ(watcher20.quiesce_count, 0U);
+ ASSERT_EQ(watcher20.unquiesce_count, 0U);
+
+ ASSERT_EQ(0, image2.quiesce_unwatch(watcher20.handle));
+
+ ASSERT_EQ(0, image1.snap_remove("snap2"));
+ ASSERT_EQ(0, image1.snap_remove("snap3"));
+ ASSERT_EQ(0, image1.snap_remove("snap4"));
+ }
+
+ ASSERT_EQ(0, rbd.remove(ioctx, name.c_str()));
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, QuiesceWatchTimeout)
+{
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ ASSERT_EQ(0, _rados.conf_set("rbd_quiesce_notification_attempts", "2"));
+
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ {
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ struct Watcher : public librbd::QuiesceWatchCtx {
+ librbd::Image &image;
+ std::mutex m_lock;
+ std::condition_variable m_cond;
+ size_t quiesce_count = 0;
+ size_t unquiesce_count = 0;
+
+ Watcher(librbd::Image &image) : image(image) {
+ }
+
+ void handle_quiesce() override {
+ std::lock_guard<std::mutex> locker(m_lock);
+ quiesce_count++;
+ m_cond.notify_one();
+ }
+
+ void handle_unquiesce() override {
+ std::lock_guard<std::mutex> locker(m_lock);
+ unquiesce_count++;
+ m_cond.notify_one();
+ }
+
+ void wait_for_quiesce() {
+ std::unique_lock<std::mutex> locker(m_lock);
+ ASSERT_TRUE(m_cond.wait_for(locker, seconds(60),
+ [this] {
+ return quiesce_count >= 1;
+ }));
+ }
+
+ void wait_for_unquiesce() {
+ std::unique_lock<std::mutex> locker(m_lock);
+ ASSERT_TRUE(m_cond.wait_for(locker, seconds(60),
+ [this] {
+ return quiesce_count == unquiesce_count;
+ }));
+ quiesce_count = unquiesce_count = 0;
+ }
+ } watcher(image);
+ uint64_t handle;
+
+ ASSERT_EQ(0, image.quiesce_watch(&watcher, &handle));
+
+ std::cerr << "test quiesce is not long enough to time out" << std::endl;
+
+ thread quiesce1([&image, &watcher, handle]() {
+ watcher.wait_for_quiesce();
+ sleep(8);
+ image.quiesce_complete(handle, 0);
+ });
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ quiesce1.join();
+ ASSERT_GE(watcher.quiesce_count, 1U);
+ watcher.wait_for_unquiesce();
+
+ std::cerr << "test quiesce is timed out" << std::endl;
+
+ bool timed_out = false;
+ thread quiesce2([&image, &watcher, handle, &timed_out]() {
+ watcher.wait_for_quiesce();
+ for (int i = 0; !timed_out && i < 60; i++) {
+ std::cerr << "waiting for timed out ... " << i << std::endl;
+ sleep(1);
+ }
+ image.quiesce_complete(handle, 0);
+ });
+
+ ASSERT_EQ(-ETIMEDOUT, image.snap_create("snap2"));
+ timed_out = true;
+ quiesce2.join();
+ ASSERT_GE(watcher.quiesce_count, 1U);
+ watcher.wait_for_unquiesce();
+
+ thread quiesce3([&image, handle, &watcher]() {
+ watcher.wait_for_quiesce();
+ image.quiesce_complete(handle, 0);
+ });
+
+ std::cerr << "test retry succeeds" << std::endl;
+
+ ASSERT_EQ(0, image.snap_create("snap2"));
+ quiesce3.join();
+ ASSERT_GE(watcher.quiesce_count, 1U);
+ watcher.wait_for_unquiesce();
+
+ ASSERT_EQ(0, image.snap_remove("snap1"));
+ ASSERT_EQ(0, image.snap_remove("snap2"));
+ }
+
+ ASSERT_EQ(0, rbd.remove(ioctx, name.c_str()));
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, WriteZeroes) {
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // 1s from [0, 256) / length 256
+ char data[256];
+ memset(data, 1, sizeof(data));
+ bufferlist bl;
+ bl.append(data, 256);
+ ASSERT_EQ(256, image.write(0, 256, bl));
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ auto expected_diff = interval_set<uint64_t>{{{0, 256}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // writes zero passed the current end extents.
+ // Now 1s from [0, 192) / length 192
+ ASSERT_EQ(size - 192,
+ image.write_zeroes(192, size - 192, 0U, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 192}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // zero an existing extent and truncate some off the end
+ // Now 1s from [64, 192) / length 192
+ ASSERT_EQ(64, image.write_zeroes(0, 64, 0U, 0));
+
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 192}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ bufferlist expected_bl;
+ expected_bl.append_zero(64);
+ bufferlist sub_bl;
+ sub_bl.substr_of(bl, 0, 128);
+ expected_bl.claim_append(sub_bl);
+ expected_bl.append_zero(size - 192);
+
+ bufferlist read_bl;
+ EXPECT_EQ(size, image.read(0, size, read_bl));
+ EXPECT_EQ(expected_bl, read_bl);
+
+ ASSERT_EQ(0, image.close());
+}
+
+TEST_F(TestLibRBD, WriteZeroesThickProvision) {
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ auto expected_diff = interval_set<uint64_t>{{}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // writes unaligned zeroes as a prepend
+ ASSERT_EQ(128, image.write_zeroes(
+ 0, 128, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 128}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ ASSERT_EQ(512, image.write_zeroes(
+ 384, 512, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 896}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // prepend with write-same
+ ASSERT_EQ(640, image.write_zeroes(
+ 896, 640, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 1536}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // write-same with append
+ ASSERT_EQ(640, image.write_zeroes(
+ 1536, 640, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 2176}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // prepend + write-same + append
+ ASSERT_EQ(768, image.write_zeroes(
+ 2176, 768, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 2944}}};
+
+ // write-same
+ ASSERT_EQ(1024, image.write_zeroes(
+ 3072, 1024, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 4096}}};
+
+ bufferlist expected_bl;
+ expected_bl.append_zero(size);
+
+ bufferlist read_bl;
+ EXPECT_EQ(size, image.read(0, size, read_bl));
+ EXPECT_EQ(expected_bl, read_bl);
+
+ ASSERT_EQ(0, image.close());
+}
+
+TEST_F(TestLibRBD, ConcurentOperations)
+{
+ SKIP_IF_CRIMSON();
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ // Test creating/removing many snapshots simultaneously
+
+ std::vector<librbd::Image> images(10);
+ std::vector<librbd::RBD::AioCompletion *> comps;
+
+ for (auto &image : images) {
+ auto comp = new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, rbd.aio_open(ioctx, image, name.c_str(), NULL, comp));
+ comps.push_back(comp);
+ }
+
+ for (auto &comp : comps) {
+ ASSERT_EQ(0, comp->wait_for_complete());
+ ASSERT_EQ(1, comp->is_complete());
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+ }
+ comps.clear();
+
+ std::vector<std::thread> threads;
+ int i = 0;
+ for (auto &image : images) {
+ std::string snap_name = "snap" + stringify(i++);
+ threads.emplace_back([&image, snap_name]() {
+ int r = image.snap_create(snap_name.c_str());
+ ceph_assert(r == 0);
+ });
+ }
+
+ for (auto &t : threads) {
+ t.join();
+ }
+ threads.clear();
+
+ i = 0;
+ for (auto &image : images) {
+ std::string snap_name = "snap" + stringify(i++);
+ threads.emplace_back([&image, snap_name](){
+ int r = image.snap_remove(snap_name.c_str());
+ ceph_assert(r == 0);
+ });
+ }
+
+ for (auto &t : threads) {
+ t.join();
+ }
+ threads.clear();
+
+ for (auto &image : images) {
+ auto comp = new librbd::RBD::AioCompletion(NULL, NULL);
+ ASSERT_EQ(0, image.aio_close(comp));
+ comps.push_back(comp);
+ }
+
+ for (auto &comp : comps) {
+ ASSERT_EQ(0, comp->wait_for_complete());
+ ASSERT_EQ(1, comp->is_complete());
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+ }
+ comps.clear();
+
+ // Test shutdown
+ {
+ librbd::Image image1, image2, image3;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image1.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE));
+
+ struct Watcher : public librbd::QuiesceWatchCtx {
+ size_t count = 0;
+
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cv;
+
+ void handle_quiesce() override {
+ std::unique_lock locker(lock);
+ count++;
+ cv.notify_one();
+ }
+
+ void handle_unquiesce() override {
+ }
+
+ bool wait_for_quiesce(size_t c) {
+ std::unique_lock locker(lock);
+ return cv.wait_for(locker, seconds(60),
+ [this, c]() { return count >= c; });
+ }
+ } watcher;
+ uint64_t handle;
+ ASSERT_EQ(0, image2.quiesce_watch(&watcher, &handle));
+
+ auto close1_comp = new librbd::RBD::AioCompletion(NULL, NULL);
+
+ std::thread create_snap1([&image1, close1_comp]() {
+ int r = image1.snap_create("snap1");
+ ceph_assert(r == 0);
+ r = image1.aio_close(close1_comp);
+ ceph_assert(r == 0);
+ });
+
+ ASSERT_TRUE(watcher.wait_for_quiesce(1));
+
+ std::thread create_snap2([&image2]() {
+ int r = image2.snap_create("snap2");
+ ceph_assert(r == 0);
+ });
+
+ std::thread create_snap3([&image3]() {
+ int r = image3.snap_create("snap3");
+ ceph_assert(r == 0);
+ });
+
+ image2.quiesce_complete(handle, 0);
+ create_snap1.join();
+
+ ASSERT_TRUE(watcher.wait_for_quiesce(2));
+ image2.quiesce_complete(handle, 0);
+
+ ASSERT_TRUE(watcher.wait_for_quiesce(3));
+ image2.quiesce_complete(handle, 0);
+
+ ASSERT_EQ(0, close1_comp->wait_for_complete());
+ ASSERT_EQ(1, close1_comp->is_complete());
+ ASSERT_EQ(0, close1_comp->get_return_value());
+ close1_comp->release();
+
+ create_snap2.join();
+ create_snap3.join();
+
+ ASSERT_EQ(0, image2.quiesce_unwatch(handle));
+ ASSERT_EQ(0, image2.snap_remove("snap1"));
+ ASSERT_EQ(0, image2.snap_remove("snap2"));
+ ASSERT_EQ(0, image2.snap_remove("snap3"));
+ }
+
+ ASSERT_EQ(0, rbd.remove(ioctx, name.c_str()));
+ ioctx.close();
+}
+
+
+// poorman's ceph_assert()
+namespace ceph {
+ void __ceph_assert_fail(const char *assertion, const char *file, int line,
+ const char *func) {
+ ceph_abort();
+ }
+}
+
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"
diff --git a/src/test/librbd/test_main.cc b/src/test/librbd/test_main.cc
new file mode 100644
index 000000000..2ff9f69de
--- /dev/null
+++ b/src/test/librbd/test_main.cc
@@ -0,0 +1,71 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "global/global_context.h"
+#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+#include <iostream>
+#include <string>
+
+extern void register_test_librbd();
+#ifdef TEST_LIBRBD_INTERNALS
+extern void register_test_deep_copy();
+extern void register_test_groups();
+extern void register_test_image_watcher();
+extern void register_test_internal();
+extern void register_test_journal_entries();
+extern void register_test_journal_replay();
+extern void register_test_migration();
+extern void register_test_mirroring();
+extern void register_test_mirroring_watcher();
+extern void register_test_object_map();
+extern void register_test_operations();
+extern void register_test_trash();
+#endif // TEST_LIBRBD_INTERNALS
+
+int main(int argc, char **argv)
+{
+ setenv("RBD_FORCE_ALLOW_V1","1",1);
+
+ register_test_librbd();
+#ifdef TEST_LIBRBD_INTERNALS
+ register_test_deep_copy();
+ register_test_groups();
+ register_test_image_watcher();
+ register_test_internal();
+ register_test_journal_entries();
+ register_test_journal_replay();
+ register_test_migration();
+ register_test_mirroring();
+ register_test_mirroring_watcher();
+ register_test_object_map();
+ register_test_operations();
+ register_test_trash();
+#endif // TEST_LIBRBD_INTERNALS
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ librados::Rados rados;
+ std::string result = connect_cluster_pp(rados);
+ if (result != "" ) {
+ std::cerr << result << std::endl;
+ return 1;
+ }
+
+#ifdef TEST_LIBRBD_INTERNALS
+ g_ceph_context = reinterpret_cast<CephContext*>(rados.cct());
+#endif // TEST_LIBRBD_INTERNALS
+
+ int r = rados.conf_set("lockdep", "true");
+ if (r < 0) {
+ std::cerr << "warning: failed to enable lockdep" << std::endl;
+ }
+
+ int seed = getpid();
+ std::cout << "seed " << seed << std::endl;
+ srand(seed);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librbd/test_mirroring.cc b/src/test/librbd/test_mirroring.cc
new file mode 100644
index 000000000..19ba5277d
--- /dev/null
+++ b/src/test/librbd/test_mirroring.cc
@@ -0,0 +1,1543 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageState.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Image.h"
+#include "librbd/api/Namespace.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/journal/Types.h"
+#include "librbd/mirror/snapshot/GetImageStateRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
+#include "journal/Journaler.h"
+#include "journal/Settings.h"
+#include "common/Cond.h"
+#include <boost/scope_exit.hpp>
+#include <boost/assign/list_of.hpp>
+#include <utility>
+#include <vector>
+
+using namespace std;
+
+void register_test_mirroring() {
+}
+
+namespace librbd {
+
+static bool operator==(const mirror_peer_site_t& lhs,
+ const mirror_peer_site_t& rhs) {
+ return (lhs.uuid == rhs.uuid &&
+ lhs.direction == rhs.direction &&
+ lhs.site_name == rhs.site_name &&
+ lhs.client_name == rhs.client_name &&
+ lhs.last_seen == rhs.last_seen);
+}
+
+static std::ostream& operator<<(std::ostream& os,
+ const mirror_peer_site_t& rhs) {
+ os << "uuid=" << rhs.uuid << ", "
+ << "direction=" << rhs.direction << ", "
+ << "site_name=" << rhs.site_name << ", "
+ << "client_name=" << rhs.client_name << ", "
+ << "last_seen=" << rhs.last_seen;
+ return os;
+}
+
+};
+
+class TestMirroring : public TestFixture {
+public:
+
+ TestMirroring() {}
+
+
+ void TearDown() override {
+ unlock_image();
+
+ TestFixture::TearDown();
+ }
+
+ void SetUp() override {
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), m_ioctx));
+ }
+
+ std::string image_name = "mirrorimg1";
+
+ int get_local_mirror_image_site_status(
+ const librbd::mirror_image_global_status_t& status,
+ librbd::mirror_image_site_status_t* local_status) {
+ auto it = std::find_if(status.site_statuses.begin(),
+ status.site_statuses.end(),
+ [](auto& site_status) {
+ return (site_status.mirror_uuid ==
+ RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID);
+ });
+ if (it == status.site_statuses.end()) {
+ return -ENOENT;
+ }
+
+ *local_status = *it;
+ return 0;
+ }
+
+ void check_mirror_image_enable(
+ rbd_mirror_mode_t mirror_mode, uint64_t features, int expected_r,
+ rbd_mirror_image_state_t mirror_state,
+ rbd_mirror_image_mode_t mirror_image_mode = RBD_MIRROR_IMAGE_MODE_JOURNAL) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features, &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ ASSERT_EQ(expected_r, image.mirror_image_enable2(mirror_image_mode));
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ ASSERT_EQ(mirror_state, mirror_image.state);
+
+ if (mirror_image.state == RBD_MIRROR_IMAGE_ENABLED) {
+ librbd::mirror_image_mode_t mode;
+ ASSERT_EQ(0, image.mirror_image_get_mode(&mode));
+ ASSERT_EQ(mirror_image_mode, mode);
+ }
+
+ librbd::mirror_image_global_status_t status;
+ ASSERT_EQ(0, image.mirror_image_get_global_status(&status, sizeof(status)));
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+
+ std::string instance_id;
+ ASSERT_EQ(mirror_state == RBD_MIRROR_IMAGE_ENABLED ? -ENOENT : -EINVAL,
+ image.mirror_image_get_instance_id(&instance_id));
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ }
+
+ void check_mirror_image_disable(rbd_mirror_mode_t mirror_mode,
+ uint64_t features,
+ int expected_r,
+ rbd_mirror_image_state_t mirror_state) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features, &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ ASSERT_EQ(expected_r, image.mirror_image_disable(false));
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ ASSERT_EQ(mirror_state, mirror_image.state);
+
+ librbd::mirror_image_global_status_t status;
+ ASSERT_EQ(0, image.mirror_image_get_global_status(&status, sizeof(status)));
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+
+ std::string instance_id;
+ ASSERT_EQ(mirror_state == RBD_MIRROR_IMAGE_ENABLED ? -ENOENT : -EINVAL,
+ image.mirror_image_get_instance_id(&instance_id));
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ }
+
+ void check_mirroring_status(size_t *images_count) {
+ std::map<std::string, librbd::mirror_image_global_status_t> images;
+ ASSERT_EQ(0, m_rbd.mirror_image_global_status_list(m_ioctx, "", 4096,
+ &images));
+
+ std::map<librbd::mirror_image_status_state_t, int> states;
+ ASSERT_EQ(0, m_rbd.mirror_image_status_summary(m_ioctx, &states));
+ size_t states_count = 0;
+ for (auto &s : states) {
+ states_count += s.second;
+ }
+ ASSERT_EQ(images.size(), states_count);
+
+ *images_count = images.size();
+
+ std::map<std::string, std::string> instance_ids;
+ ASSERT_EQ(0, m_rbd.mirror_image_instance_id_list(m_ioctx, "", 4096,
+ &instance_ids));
+ ASSERT_TRUE(instance_ids.empty());
+ }
+
+ void check_mirroring_on_create(uint64_t features,
+ rbd_mirror_mode_t mirror_mode,
+ rbd_mirror_image_state_t mirror_state) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ size_t mirror_images_count = 0;
+ check_mirroring_status(&mirror_images_count);
+
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features, &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ ASSERT_EQ(mirror_state, mirror_image.state);
+
+ librbd::mirror_image_global_status_t status;
+ ASSERT_EQ(0, image.mirror_image_get_global_status(&status, sizeof(status)));
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+
+ size_t mirror_images_new_count = 0;
+ check_mirroring_status(&mirror_images_new_count);
+ if (mirror_mode == RBD_MIRROR_MODE_POOL &&
+ mirror_state == RBD_MIRROR_IMAGE_ENABLED) {
+ ASSERT_EQ(mirror_images_new_count, mirror_images_count + 1);
+ } else {
+ ASSERT_EQ(mirror_images_new_count, mirror_images_count);
+ }
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+
+ check_mirroring_status(&mirror_images_new_count);
+ ASSERT_EQ(mirror_images_new_count, mirror_images_count);
+ }
+
+ void check_mirroring_on_update_features(
+ uint64_t init_features, bool enable, bool enable_mirroring,
+ uint64_t features, int expected_r, rbd_mirror_mode_t mirror_mode,
+ rbd_mirror_image_state_t mirror_state,
+ rbd_mirror_image_mode_t mirror_image_mode = RBD_MIRROR_IMAGE_MODE_JOURNAL) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, init_features, &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ if (enable_mirroring) {
+ ASSERT_EQ(0, image.mirror_image_enable2(mirror_image_mode));
+ }
+
+ ASSERT_EQ(expected_r, image.update_features(features, enable));
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ ASSERT_EQ(mirror_state, mirror_image.state);
+
+ if (mirror_image.state == RBD_MIRROR_IMAGE_ENABLED) {
+ librbd::mirror_image_mode_t mode;
+ ASSERT_EQ(0, image.mirror_image_get_mode(&mode));
+ ASSERT_EQ(mirror_image_mode, mode);
+ }
+
+ librbd::mirror_image_global_status_t status;
+ ASSERT_EQ(0, image.mirror_image_get_global_status(&status, sizeof(status)));
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ }
+
+ void setup_images_with_mirror_mode(rbd_mirror_mode_t mirror_mode,
+ std::vector<uint64_t>& features_vec) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ int id = 1;
+ int order = 20;
+ for (const auto& features : features_vec) {
+ std::stringstream img_name("img_");
+ img_name << id++;
+ std::string img_name_str = img_name.str();
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, img_name_str.c_str(), 2048, features, &order));
+ }
+ }
+
+ void check_mirroring_on_mirror_mode_set(rbd_mirror_mode_t mirror_mode,
+ std::vector<rbd_mirror_image_state_t>& states_vec) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ std::vector< std::tuple<std::string, rbd_mirror_image_state_t> > images;
+ int id = 1;
+ for (const auto& mirror_state : states_vec) {
+ std::stringstream img_name("img_");
+ img_name << id++;
+ std::string img_name_str = img_name.str();
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, img_name_str.c_str()));
+ images.push_back(std::make_tuple(img_name_str, mirror_state));
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ ASSERT_EQ(mirror_state, mirror_image.state);
+
+ librbd::mirror_image_global_status_t status;
+ ASSERT_EQ(0, image.mirror_image_get_global_status(&status,
+ sizeof(status)));
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, img_name_str.c_str()));
+ }
+ }
+
+ void check_remove_image(rbd_mirror_mode_t mirror_mode, uint64_t features,
+ bool enable_mirroring, bool demote = false) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ if (enable_mirroring) {
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_JOURNAL));
+ }
+
+ if (demote) {
+ ASSERT_EQ(0, image.mirror_image_demote());
+ ASSERT_EQ(0, image.mirror_image_disable(true));
+ }
+
+ image.close();
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ }
+
+ void check_trash_move_restore(rbd_mirror_mode_t mirror_mode,
+ bool enable_mirroring) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ int order = 20;
+ uint64_t features = RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ if (enable_mirroring) {
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_JOURNAL));
+ }
+
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
+
+ ASSERT_EQ(0, m_rbd.trash_move(m_ioctx, image_name.c_str(), 100));
+
+ ASSERT_EQ(0, m_rbd.open_by_id(m_ioctx, image, image_id.c_str(), NULL));
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ ASSERT_EQ(mirror_image.state, RBD_MIRROR_IMAGE_DISABLED);
+
+ ASSERT_EQ(0, m_rbd.trash_restore(m_ioctx, image_id.c_str(), ""));
+
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ if (mirror_mode == RBD_MIRROR_MODE_POOL) {
+ ASSERT_EQ(mirror_image.state, RBD_MIRROR_IMAGE_ENABLED);
+ } else {
+ ASSERT_EQ(mirror_image.state, RBD_MIRROR_IMAGE_DISABLED);
+ }
+
+ image.close();
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ }
+
+ void setup_mirror_peer(librados::IoCtx &io_ctx, librbd::Image &image) {
+ ASSERT_EQ(0, image.snap_create("sync-point-snap"));
+
+ std::string image_id;
+ ASSERT_EQ(0, get_image_id(image, &image_id));
+
+ librbd::journal::MirrorPeerClientMeta peer_client_meta(
+ "remote-image-id", {{{}, "sync-point-snap", boost::none}}, {});
+ librbd::journal::ClientData client_data(peer_client_meta);
+
+ journal::Journaler journaler(io_ctx, image_id, "peer-client", {}, nullptr);
+ C_SaferCond init_ctx;
+ journaler.init(&init_ctx);
+ ASSERT_EQ(-ENOENT, init_ctx.wait());
+
+ bufferlist client_data_bl;
+ encode(client_data, client_data_bl);
+ ASSERT_EQ(0, journaler.register_client(client_data_bl));
+
+ C_SaferCond shut_down_ctx;
+ journaler.shut_down(&shut_down_ctx);
+ ASSERT_EQ(0, shut_down_ctx.wait());
+ }
+
+};
+
+TEST_F(TestMirroring, EnableImageMirror_In_MirrorModeImage) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirror_image_enable(RBD_MIRROR_MODE_IMAGE, features, 0,
+ RBD_MIRROR_IMAGE_ENABLED, RBD_MIRROR_IMAGE_MODE_JOURNAL);
+}
+
+TEST_F(TestMirroring, EnableImageMirror_In_MirrorModePool) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirror_image_enable(RBD_MIRROR_MODE_POOL, features, -EINVAL,
+ RBD_MIRROR_IMAGE_ENABLED, RBD_MIRROR_IMAGE_MODE_JOURNAL);
+}
+
+TEST_F(TestMirroring, EnableImageMirror_In_MirrorModeDisabled) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirror_image_enable(RBD_MIRROR_MODE_DISABLED, features, -EINVAL,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, DisableImageMirror_In_MirrorModeImage) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirror_image_disable(RBD_MIRROR_MODE_IMAGE, features, 0,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, DisableImageMirror_In_MirrorModeImage_NoObjectMap) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirror_image_disable(RBD_MIRROR_MODE_IMAGE, features, 0,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, DisableImageMirror_In_MirrorModePool) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirror_image_disable(RBD_MIRROR_MODE_POOL, features, -EINVAL,
+ RBD_MIRROR_IMAGE_ENABLED);
+}
+
+TEST_F(TestMirroring, DisableImageMirror_In_MirrorModeDisabled) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirror_image_disable(RBD_MIRROR_MODE_DISABLED, features, -EINVAL,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, DisableImageMirrorWithPeer) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ uint64_t features = RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_JOURNAL));
+
+ setup_mirror_peer(m_ioctx, image);
+
+ ASSERT_EQ(0, image.mirror_image_disable(false));
+
+ std::vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_TRUE(snaps.empty());
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image,
+ sizeof(mirror_image)));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, mirror_image.state);
+
+ librbd::mirror_image_global_status_t status;
+ ASSERT_EQ(0, image.mirror_image_get_global_status(&status, sizeof(status)));
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestMirroring, DisableJournalingWithPeer) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+
+ uint64_t features = RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ setup_mirror_peer(m_ioctx, image);
+
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_JOURNALING, false));
+
+ std::vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_TRUE(snaps.empty());
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image,
+ sizeof(mirror_image)));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, mirror_image.state);
+
+ librbd::mirror_image_global_status_t status;
+ ASSERT_EQ(0, image.mirror_image_get_global_status(&status, sizeof(status)));
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestMirroring, EnableImageMirror_In_MirrorModeDisabled_WithoutJournaling) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ check_mirror_image_enable(RBD_MIRROR_MODE_DISABLED, features, -EINVAL,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, EnableImageMirror_In_MirrorModePool_WithoutJournaling) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ check_mirror_image_enable(RBD_MIRROR_MODE_POOL, features, -EINVAL,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, EnableImageMirror_In_MirrorModeImage_WithoutJournaling) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ check_mirror_image_enable(RBD_MIRROR_MODE_IMAGE, features, 0,
+ RBD_MIRROR_IMAGE_ENABLED, RBD_MIRROR_IMAGE_MODE_SNAPSHOT);
+}
+
+TEST_F(TestMirroring, EnableImageMirror_In_MirrorModeImage_WithoutExclusiveLock) {
+ uint64_t features = 0;
+ check_mirror_image_enable(RBD_MIRROR_MODE_IMAGE, features, 0,
+ RBD_MIRROR_IMAGE_ENABLED, RBD_MIRROR_IMAGE_MODE_SNAPSHOT);
+}
+
+TEST_F(TestMirroring, CreateImage_In_MirrorModeDisabled) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirroring_on_create(features, RBD_MIRROR_MODE_DISABLED,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, CreateImage_In_MirrorModeImage) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirroring_on_create(features, RBD_MIRROR_MODE_IMAGE,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, CreateImage_In_MirrorModePool) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ features |= RBD_FEATURE_JOURNALING;
+ check_mirroring_on_create(features, RBD_MIRROR_MODE_POOL,
+ RBD_MIRROR_IMAGE_ENABLED);
+}
+
+TEST_F(TestMirroring, CreateImage_In_MirrorModePool_WithoutJournaling) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ check_mirroring_on_create(features, RBD_MIRROR_MODE_POOL,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, CreateImage_In_MirrorModeImage_WithoutJournaling) {
+ uint64_t features = 0;
+ features |= RBD_FEATURE_OBJECT_MAP;
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ check_mirroring_on_create(features, RBD_MIRROR_MODE_IMAGE,
+ RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, EnableJournaling_In_MirrorModeDisabled) {
+ uint64_t init_features = 0;
+ init_features |= RBD_FEATURE_OBJECT_MAP;
+ init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ uint64_t features = RBD_FEATURE_JOURNALING;
+ check_mirroring_on_update_features(init_features, true, false, features, 0,
+ RBD_MIRROR_MODE_DISABLED, RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, EnableJournaling_In_MirrorModeImage) {
+ uint64_t init_features = 0;
+ init_features |= RBD_FEATURE_OBJECT_MAP;
+ init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ uint64_t features = RBD_FEATURE_JOURNALING;
+ check_mirroring_on_update_features(init_features, true, false, features, 0,
+ RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, EnableJournaling_In_MirrorModeImage_SnapshotMirroringEnabled) {
+ uint64_t init_features = 0;
+ init_features |= RBD_FEATURE_OBJECT_MAP;
+ init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ uint64_t features = RBD_FEATURE_JOURNALING;
+ check_mirroring_on_update_features(init_features, true, true, features,
+ 0, RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED,
+ RBD_MIRROR_IMAGE_MODE_SNAPSHOT);
+}
+
+TEST_F(TestMirroring, EnableJournaling_In_MirrorModePool) {
+ uint64_t init_features = 0;
+ init_features |= RBD_FEATURE_OBJECT_MAP;
+ init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ uint64_t features = RBD_FEATURE_JOURNALING;
+ check_mirroring_on_update_features(init_features, true, false, features, 0,
+ RBD_MIRROR_MODE_POOL, RBD_MIRROR_IMAGE_ENABLED,
+ RBD_MIRROR_IMAGE_MODE_JOURNAL);
+}
+
+TEST_F(TestMirroring, DisableJournaling_In_MirrorModePool) {
+ uint64_t init_features = 0;
+ init_features |= RBD_FEATURE_OBJECT_MAP;
+ init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ init_features |= RBD_FEATURE_JOURNALING;
+ uint64_t features = RBD_FEATURE_JOURNALING;
+ check_mirroring_on_update_features(init_features, false, false, features, 0,
+ RBD_MIRROR_MODE_POOL, RBD_MIRROR_IMAGE_DISABLED);
+}
+
+TEST_F(TestMirroring, DisableJournaling_In_MirrorModeImage) {
+ uint64_t init_features = 0;
+ init_features |= RBD_FEATURE_OBJECT_MAP;
+ init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ init_features |= RBD_FEATURE_JOURNALING;
+ uint64_t features = RBD_FEATURE_JOURNALING;
+ check_mirroring_on_update_features(init_features, false, true, features,
+ -EINVAL, RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED,
+ RBD_MIRROR_IMAGE_MODE_JOURNAL);
+}
+
+TEST_F(TestMirroring, MirrorModeSet_DisabledMode_To_PoolMode) {
+ std::vector<uint64_t> features_vec;
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK);
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);
+
+ setup_images_with_mirror_mode(RBD_MIRROR_MODE_DISABLED, features_vec);
+
+ std::vector<rbd_mirror_image_state_t> states_vec;
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ states_vec.push_back(RBD_MIRROR_IMAGE_ENABLED);
+ check_mirroring_on_mirror_mode_set(RBD_MIRROR_MODE_POOL, states_vec);
+}
+
+TEST_F(TestMirroring, MirrorModeSet_PoolMode_To_DisabledMode) {
+ std::vector<uint64_t> features_vec;
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK);
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);
+
+ setup_images_with_mirror_mode(RBD_MIRROR_MODE_POOL, features_vec);
+
+ std::vector<rbd_mirror_image_state_t> states_vec;
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ check_mirroring_on_mirror_mode_set(RBD_MIRROR_MODE_DISABLED, states_vec);
+}
+
+TEST_F(TestMirroring, MirrorModeSet_DisabledMode_To_ImageMode) {
+ std::vector<uint64_t> features_vec;
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK);
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);
+
+ setup_images_with_mirror_mode(RBD_MIRROR_MODE_DISABLED, features_vec);
+
+ std::vector<rbd_mirror_image_state_t> states_vec;
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ check_mirroring_on_mirror_mode_set(RBD_MIRROR_MODE_IMAGE, states_vec);
+}
+
+
+TEST_F(TestMirroring, MirrorModeSet_PoolMode_To_ImageMode) {
+ std::vector<uint64_t> features_vec;
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK);
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);
+
+ setup_images_with_mirror_mode(RBD_MIRROR_MODE_POOL, features_vec);
+
+ std::vector<rbd_mirror_image_state_t> states_vec;
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ states_vec.push_back(RBD_MIRROR_IMAGE_ENABLED);
+ check_mirroring_on_mirror_mode_set(RBD_MIRROR_MODE_IMAGE, states_vec);
+}
+
+TEST_F(TestMirroring, MirrorModeSet_ImageMode_To_PoolMode) {
+ std::vector<uint64_t> features_vec;
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK);
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);
+
+ setup_images_with_mirror_mode(RBD_MIRROR_MODE_IMAGE, features_vec);
+
+ std::vector<rbd_mirror_image_state_t> states_vec;
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ states_vec.push_back(RBD_MIRROR_IMAGE_ENABLED);
+ check_mirroring_on_mirror_mode_set(RBD_MIRROR_MODE_POOL, states_vec);
+}
+
+TEST_F(TestMirroring, MirrorModeSet_ImageMode_To_DisabledMode) {
+ std::vector<uint64_t> features_vec;
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK);
+ features_vec.push_back(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);
+
+ setup_images_with_mirror_mode(RBD_MIRROR_MODE_POOL, features_vec);
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+ ASSERT_EQ(-EINVAL, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+
+ std::vector<rbd_mirror_image_state_t> states_vec;
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ states_vec.push_back(RBD_MIRROR_IMAGE_DISABLED);
+ check_mirroring_on_mirror_mode_set(RBD_MIRROR_MODE_DISABLED, states_vec);
+}
+
+TEST_F(TestMirroring, RemoveImage_With_MirrorImageEnabled) {
+ check_remove_image(RBD_MIRROR_MODE_IMAGE,
+ RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING,
+ true);
+}
+
+TEST_F(TestMirroring, RemoveImage_With_MirrorImageDisabled) {
+ check_remove_image(RBD_MIRROR_MODE_IMAGE,
+ RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING,
+ false);
+}
+
+TEST_F(TestMirroring, RemoveImage_With_ImageWithoutJournal) {
+ check_remove_image(RBD_MIRROR_MODE_IMAGE,
+ RBD_FEATURE_EXCLUSIVE_LOCK,
+ false);
+}
+
+TEST_F(TestMirroring, RemoveImage_With_MirrorImageDemoted) {
+ check_remove_image(RBD_MIRROR_MODE_IMAGE,
+ RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING,
+ true, true);
+}
+
+TEST_F(TestMirroring, TrashMoveRestore_PoolMode) {
+ check_trash_move_restore(RBD_MIRROR_MODE_POOL, false);
+}
+
+TEST_F(TestMirroring, TrashMoveRestore_ImageMode_MirroringDisabled) {
+ check_trash_move_restore(RBD_MIRROR_MODE_IMAGE, false);
+}
+
+TEST_F(TestMirroring, TrashMoveRestore_ImageMode_MirroringEnabled) {
+ check_trash_move_restore(RBD_MIRROR_MODE_IMAGE, true);
+}
+
+TEST_F(TestMirroring, MirrorStatusList) {
+ std::vector<uint64_t>
+ features_vec(5, RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);
+ setup_images_with_mirror_mode(RBD_MIRROR_MODE_POOL, features_vec);
+
+ std::string last_read = "";
+ std::map<std::string, librbd::mirror_image_global_status_t> images;
+ ASSERT_EQ(0, m_rbd.mirror_image_global_status_list(m_ioctx, last_read, 2,
+ &images));
+ ASSERT_EQ(2U, images.size());
+
+ last_read = images.rbegin()->first;
+ images.clear();
+ ASSERT_EQ(0, m_rbd.mirror_image_global_status_list(m_ioctx, last_read, 2,
+ &images));
+ ASSERT_EQ(2U, images.size());
+
+ last_read = images.rbegin()->first;
+ images.clear();
+ ASSERT_EQ(0, m_rbd.mirror_image_global_status_list(m_ioctx, last_read, 4096,
+ &images));
+ ASSERT_EQ(1U, images.size());
+
+ last_read = images.rbegin()->first;
+ images.clear();
+ ASSERT_EQ(0, m_rbd.mirror_image_global_status_list(m_ioctx, last_read, 4096,
+ &images));
+ ASSERT_EQ(0U, images.size());
+}
+
+TEST_F(TestMirroring, RemoveBootstrapped)
+{
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+
+ uint64_t features = RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(-EBUSY, librbd::api::Image<>::remove(m_ioctx, image_name, no_op));
+
+ // simulate the image is open by rbd-mirror bootstrap
+ uint64_t handle;
+ struct MirrorWatcher : public librados::WatchCtx2 {
+ explicit MirrorWatcher(librados::IoCtx &ioctx) : m_ioctx(ioctx) {
+ }
+ void handle_notify(uint64_t notify_id, uint64_t cookie,
+ uint64_t notifier_id, bufferlist& bl) override {
+ // received IMAGE_UPDATED notification from remove
+ m_notified = true;
+ m_ioctx.notify_ack(RBD_MIRRORING, notify_id, cookie, bl);
+ }
+ void handle_error(uint64_t cookie, int err) override {
+ }
+ librados::IoCtx &m_ioctx;
+ bool m_notified = false;
+ } watcher(m_ioctx);
+ ASSERT_EQ(0, m_ioctx.create(RBD_MIRRORING, false));
+ ASSERT_EQ(0, m_ioctx.watch2(RBD_MIRRORING, &handle, &watcher));
+ // now remove should succeed
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, image_name, no_op));
+ ASSERT_EQ(0, m_ioctx.unwatch2(handle));
+ ASSERT_TRUE(watcher.m_notified);
+ ASSERT_EQ(0, image.close());
+}
+
+TEST_F(TestMirroring, AioPromoteDemote) {
+ std::list<std::string> image_names;
+ for (size_t idx = 0; idx < 10; ++idx) {
+ image_names.push_back(get_temp_image_name());
+ }
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ // create mirror images
+ int order = 20;
+ std::list<librbd::Image> images;
+ for (auto &image_name : image_names) {
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 2048,
+ RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_JOURNALING,
+ &order));
+
+ images.emplace_back();
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, images.back(), image_name.c_str()));
+ ASSERT_EQ(0, images.back().mirror_image_enable2(
+ RBD_MIRROR_IMAGE_MODE_JOURNAL));
+ }
+
+ // demote all images
+ std::list<librbd::RBD::AioCompletion *> aio_comps;
+ for (auto &image : images) {
+ aio_comps.push_back(new librbd::RBD::AioCompletion(nullptr, nullptr));
+ ASSERT_EQ(0, image.aio_mirror_image_demote(aio_comps.back()));
+ }
+ for (auto aio_comp : aio_comps) {
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ ASSERT_EQ(1, aio_comp->is_complete());
+ ASSERT_EQ(0, aio_comp->get_return_value());
+ aio_comp->release();
+ }
+ aio_comps.clear();
+
+ // verify demotions
+ for (auto &image : images) {
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image,
+ sizeof(mirror_image)));
+ ASSERT_FALSE(mirror_image.primary);
+ }
+
+ // promote all images
+ for (auto &image : images) {
+ aio_comps.push_back(new librbd::RBD::AioCompletion(nullptr, nullptr));
+ ASSERT_EQ(0, image.aio_mirror_image_promote(false, aio_comps.back()));
+ }
+ for (auto aio_comp : aio_comps) {
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ ASSERT_EQ(1, aio_comp->is_complete());
+ ASSERT_EQ(0, aio_comp->get_return_value());
+ aio_comp->release();
+ }
+
+ // verify promotions
+ for (auto &image : images) {
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image,
+ sizeof(mirror_image)));
+ ASSERT_TRUE(mirror_image.primary);
+ }
+}
+
+TEST_F(TestMirroring, AioGetInfo) {
+ std::list<std::string> image_names;
+ for (size_t idx = 0; idx < 10; ++idx) {
+ image_names.push_back(get_temp_image_name());
+ }
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+
+ // create mirror images
+ int order = 20;
+ std::list<librbd::Image> images;
+ for (auto &image_name : image_names) {
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 2048,
+ RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_JOURNALING,
+ &order));
+
+ images.emplace_back();
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, images.back(), image_name.c_str()));
+ }
+
+ std::list<librbd::RBD::AioCompletion *> aio_comps;
+ std::list<librbd::mirror_image_info_t> infos;
+ for (auto &image : images) {
+ aio_comps.push_back(new librbd::RBD::AioCompletion(nullptr, nullptr));
+ infos.emplace_back();
+ ASSERT_EQ(0, image.aio_mirror_image_get_info(&infos.back(),
+ sizeof(infos.back()),
+ aio_comps.back()));
+ }
+ for (auto aio_comp : aio_comps) {
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ ASSERT_EQ(1, aio_comp->is_complete());
+ ASSERT_EQ(0, aio_comp->get_return_value());
+ aio_comp->release();
+ }
+ aio_comps.clear();
+
+ for (auto &info : infos) {
+ ASSERT_NE("", info.global_id);
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state);
+ ASSERT_TRUE(info.primary);
+ }
+}
+
+TEST_F(TestMirroring, AioGetStatus) {
+ std::list<std::string> image_names;
+ for (size_t idx = 0; idx < 10; ++idx) {
+ image_names.push_back(get_temp_image_name());
+ }
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+
+ // create mirror images
+ int order = 20;
+ std::list<librbd::Image> images;
+ for (auto &image_name : image_names) {
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 2048,
+ RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_JOURNALING,
+ &order));
+
+ images.emplace_back();
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, images.back(), image_name.c_str()));
+ }
+
+ std::list<librbd::RBD::AioCompletion *> aio_comps;
+ std::list<librbd::mirror_image_global_status_t> statuses;
+ for (auto &image : images) {
+ aio_comps.push_back(new librbd::RBD::AioCompletion(nullptr, nullptr));
+ statuses.emplace_back();
+ ASSERT_EQ(0, image.aio_mirror_image_get_global_status(
+ &statuses.back(), sizeof(statuses.back()),
+ aio_comps.back()));
+ }
+ for (auto aio_comp : aio_comps) {
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ ASSERT_EQ(1, aio_comp->is_complete());
+ ASSERT_EQ(0, aio_comp->get_return_value());
+ aio_comp->release();
+ }
+ aio_comps.clear();
+
+ for (auto &status : statuses) {
+ ASSERT_NE("", status.name);
+ ASSERT_NE("", status.info.global_id);
+ ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, status.info.state);
+ ASSERT_TRUE(status.info.primary);
+
+ librbd::mirror_image_site_status_t local_status;
+ ASSERT_EQ(0, get_local_mirror_image_site_status(status, &local_status));
+ ASSERT_EQ(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, local_status.state);
+ ASSERT_EQ("status not found", local_status.description);
+ ASSERT_FALSE(local_status.up);
+ ASSERT_EQ(0, local_status.last_update);
+ }
+}
+
+TEST_F(TestMirroring, SiteName) {
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ const std::string expected_site_name("us-east-1a");
+ ASSERT_EQ(0, m_rbd.mirror_site_name_set(_rados, expected_site_name));
+
+ std::string site_name;
+ ASSERT_EQ(0, m_rbd.mirror_site_name_get(_rados, &site_name));
+ ASSERT_EQ(expected_site_name, site_name);
+
+ ASSERT_EQ(0, m_rbd.mirror_site_name_set(_rados, ""));
+
+ std::string fsid;
+ ASSERT_EQ(0, _rados.cluster_fsid(&fsid));
+ ASSERT_EQ(0, m_rbd.mirror_site_name_get(_rados, &site_name));
+ ASSERT_EQ(fsid, site_name);
+}
+
+TEST_F(TestMirroring, Bootstrap) {
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ std::string token_b64;
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ ASSERT_EQ(-EINVAL, m_rbd.mirror_peer_bootstrap_create(m_ioctx, &token_b64));
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+ ASSERT_EQ(0, m_rbd.mirror_peer_bootstrap_create(m_ioctx, &token_b64));
+
+ bufferlist token_b64_bl;
+ token_b64_bl.append(token_b64);
+
+ bufferlist token_bl;
+ token_bl.decode_base64(token_b64_bl);
+
+ // cannot import token into same cluster
+ ASSERT_EQ(-EINVAL,
+ m_rbd.mirror_peer_bootstrap_import(
+ m_ioctx, RBD_MIRROR_PEER_DIRECTION_RX, token_b64));
+}
+
+TEST_F(TestMirroring, PeerDirection) {
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
+
+ std::string uuid;
+ ASSERT_EQ(-EINVAL, m_rbd.mirror_peer_site_add(
+ m_ioctx, &uuid, RBD_MIRROR_PEER_DIRECTION_TX, "siteA",
+ "client.admin"));
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "siteA", "client.admin"));
+
+ std::vector<librbd::mirror_peer_site_t> peers;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_list(m_ioctx, &peers));
+ std::vector<librbd::mirror_peer_site_t> expected_peers = {
+ {uuid, RBD_MIRROR_PEER_DIRECTION_RX_TX, "siteA", "", "client.admin", 0}};
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_set_direction(
+ m_ioctx, uuid, RBD_MIRROR_PEER_DIRECTION_RX));
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_list(m_ioctx, &peers));
+ expected_peers = {
+ {uuid, RBD_MIRROR_PEER_DIRECTION_RX, "siteA", "", "client.admin", 0}};
+ ASSERT_EQ(expected_peers, peers);
+
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, uuid));
+}
+
+TEST_F(TestMirroring, Snapshot)
+{
+ REQUIRE_FORMAT_V2();
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ ASSERT_EQ(0, image.metadata_set(
+ "conf_rbd_mirroring_max_mirroring_snapshots", "5"));
+
+ uint64_t snap_id;
+
+ ASSERT_EQ(-EINVAL, image.mirror_image_create_snapshot(&snap_id));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+ ASSERT_EQ(-EINVAL, image.mirror_image_create_snapshot(&snap_id));
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_SNAPSHOT));
+ librbd::mirror_image_mode_t mode;
+ ASSERT_EQ(0, image.mirror_image_get_mode(&mode));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mode);
+ ASSERT_EQ(-EINVAL, image.mirror_image_create_snapshot(&snap_id));
+ std::string peer_uuid;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &peer_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "cluster", "client"));
+ // The mirroring was enabled when no peer was configured. Therefore, the
+ // initial snapshot has no peers linked and will be removed after the
+ // creation of a new mirror snapshot.
+ ASSERT_EQ(0, image.mirror_image_create_snapshot(&snap_id));
+ vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_EQ(1U, snaps.size());
+ ASSERT_EQ(snaps[0].id, snap_id);
+
+ for (int i = 0; i < 5; i++) {
+ ASSERT_EQ(0, image.mirror_image_create_snapshot(&snap_id));
+ }
+ snaps.clear();
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_EQ(5U, snaps.size());
+ ASSERT_EQ(snaps[4].id, snap_id);
+
+ // automatic peer unlink on max_mirroring_snapshots reached
+ ASSERT_EQ(0, image.mirror_image_create_snapshot(&snap_id));
+ vector<librbd::snap_info_t> snaps1;
+ ASSERT_EQ(0, image.snap_list(snaps1));
+ ASSERT_EQ(5U, snaps1.size());
+ ASSERT_EQ(snaps1[0].id, snaps[0].id);
+ ASSERT_EQ(snaps1[1].id, snaps[1].id);
+ ASSERT_EQ(snaps1[2].id, snaps[2].id);
+ ASSERT_EQ(snaps1[3].id, snaps[3].id);
+ ASSERT_EQ(snaps1[4].id, snap_id);
+
+ librbd::snap_namespace_type_t snap_ns_type;
+ ASSERT_EQ(0, image.snap_get_namespace_type(snap_id, &snap_ns_type));
+ ASSERT_EQ(RBD_SNAP_NAMESPACE_TYPE_MIRROR, snap_ns_type);
+ librbd::snap_mirror_namespace_t mirror_snap;
+ ASSERT_EQ(0, image.snap_get_mirror_namespace(snap_id, &mirror_snap,
+ sizeof(mirror_snap)));
+ ASSERT_EQ(1U, mirror_snap.mirror_peer_uuids.size());
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer_uuid));
+
+ for (auto &snap : snaps1) {
+ ASSERT_EQ(0, image.snap_remove_by_id(snap.id));
+ }
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer_uuid));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestMirroring, SnapshotRemoveOnDisable)
+{
+ REQUIRE_FORMAT_V2();
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+ std::string peer_uuid;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &peer_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "cluster", "client"));
+
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_SNAPSHOT));
+ uint64_t snap_id;
+ ASSERT_EQ(0, image.mirror_image_create_snapshot(&snap_id));
+
+ vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_EQ(2U, snaps.size());
+ ASSERT_EQ(snaps[1].id, snap_id);
+
+ ASSERT_EQ(0, image.mirror_image_disable(false));
+
+ snaps.clear();
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_TRUE(snaps.empty());
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer_uuid));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestMirroring, SnapshotUnlinkPeer)
+{
+ REQUIRE_FORMAT_V2();
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+ std::string peer1_uuid;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &peer1_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "cluster1", "client"));
+ std::string peer2_uuid;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &peer2_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "cluster2", "client"));
+ std::string peer3_uuid;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &peer3_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "cluster3", "client"));
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+ features &= ~RBD_FEATURE_JOURNALING;
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_SNAPSHOT));
+ uint64_t snap_id;
+ ASSERT_EQ(0, image.mirror_image_create_snapshot(&snap_id));
+ uint64_t snap_id2;
+ ASSERT_EQ(0, image.mirror_image_create_snapshot(&snap_id2));
+ librbd::snap_mirror_namespace_t mirror_snap;
+ ASSERT_EQ(0, image.snap_get_mirror_namespace(snap_id, &mirror_snap,
+ sizeof(mirror_snap)));
+ ASSERT_EQ(3U, mirror_snap.mirror_peer_uuids.size());
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer1_uuid));
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer2_uuid));
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer3_uuid));
+
+ auto ictx = new librbd::ImageCtx(image_name, "", nullptr, m_ioctx, false);
+ ASSERT_EQ(0, ictx->state->open(0));
+ BOOST_SCOPE_EXIT(&ictx) {
+ if (ictx != nullptr) {
+ ictx->state->close();
+ }
+ } BOOST_SCOPE_EXIT_END;
+
+ C_SaferCond cond1;
+ auto req = librbd::mirror::snapshot::UnlinkPeerRequest<>::create(
+ ictx, snap_id, peer1_uuid, true, &cond1);
+ req->send();
+ ASSERT_EQ(0, cond1.wait());
+
+ ASSERT_EQ(0, image.snap_get_mirror_namespace(snap_id, &mirror_snap,
+ sizeof(mirror_snap)));
+ ASSERT_EQ(2U, mirror_snap.mirror_peer_uuids.size());
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer2_uuid));
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer3_uuid));
+
+ ASSERT_EQ(0, librbd::api::Namespace<>::create(m_ioctx, "ns1"));
+ librados::IoCtx ns_ioctx;
+ ns_ioctx.dup(m_ioctx);
+ ns_ioctx.set_namespace("ns1");
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(ns_ioctx, RBD_MIRROR_MODE_IMAGE));
+ ASSERT_EQ(0, m_rbd.create2(ns_ioctx, image_name.c_str(), 4096, features,
+ &order));
+
+ librbd::Image ns_image;
+ ASSERT_EQ(0, m_rbd.open(ns_ioctx, ns_image, image_name.c_str()));
+ ASSERT_EQ(0, ns_image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_SNAPSHOT));
+ uint64_t ns_snap_id;
+ ASSERT_EQ(0, ns_image.mirror_image_create_snapshot(&ns_snap_id));
+ ASSERT_EQ(0, ns_image.snap_get_mirror_namespace(ns_snap_id, &mirror_snap,
+ sizeof(mirror_snap)));
+ ASSERT_EQ(3U, mirror_snap.mirror_peer_uuids.size());
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer1_uuid));
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer2_uuid));
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer3_uuid));
+
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer3_uuid));
+
+ ASSERT_EQ(0, image.snap_get_mirror_namespace(snap_id, &mirror_snap,
+ sizeof(mirror_snap)));
+ ASSERT_EQ(1U, mirror_snap.mirror_peer_uuids.size());
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer2_uuid));
+
+ ASSERT_EQ(0, ns_image.snap_get_mirror_namespace(ns_snap_id, &mirror_snap,
+ sizeof(mirror_snap)));
+ ASSERT_EQ(2U, mirror_snap.mirror_peer_uuids.size());
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer1_uuid));
+ ASSERT_EQ(1, mirror_snap.mirror_peer_uuids.count(peer2_uuid));
+
+ C_SaferCond cond2;
+ req = librbd::mirror::snapshot::UnlinkPeerRequest<>::create(
+ ictx, snap_id, peer2_uuid, true, &cond2);
+ req->send();
+ ASSERT_EQ(0, cond2.wait());
+
+ ASSERT_EQ(-ENOENT, image.snap_get_mirror_namespace(snap_id, &mirror_snap,
+ sizeof(mirror_snap)));
+ ictx->state->close();
+ ictx = nullptr;
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+
+ ASSERT_EQ(0, ns_image.close());
+ ASSERT_EQ(0, m_rbd.remove(ns_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, librbd::api::Namespace<>::remove(m_ioctx, "ns1"));
+
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer1_uuid));
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer2_uuid));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestMirroring, SnapshotImageState)
+{
+ REQUIRE_FORMAT_V2();
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+ ASSERT_EQ(0, image.snap_create("snap"));
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_SNAPSHOT));
+ std::vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_EQ(2U, snaps.size());
+ auto snap_id = snaps[1].id;
+
+ auto ictx = new librbd::ImageCtx(image_name, "", nullptr, m_ioctx, false);
+ ASSERT_EQ(0, ictx->state->open(0));
+ BOOST_SCOPE_EXIT(&ictx) {
+ if (ictx != nullptr) {
+ ictx->state->close();
+ }
+ } BOOST_SCOPE_EXIT_END;
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::SetImageStateRequest<>::create(
+ ictx, snap_id, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ librbd::mirror::snapshot::ImageState image_state;
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::GetImageStateRequest<>::create(
+ ictx, snap_id, &image_state, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ ASSERT_EQ(image_name, image_state.name);
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_EQ(features & ~RBD_FEATURES_IMPLICIT_ENABLE, image_state.features);
+ ASSERT_EQ(1U, image_state.snapshots.size());
+ ASSERT_EQ("snap", image_state.snapshots.begin()->second.name);
+ uint8_t original_pairs_num = image_state.metadata.size();
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::RemoveImageStateRequest<>::create(
+ ictx, snap_id, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ // test storing "large" image state in multiple objects
+
+ ASSERT_EQ(0, ictx->config.set_val("rbd_default_order", "8"));
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(0, image.metadata_set(stringify(i), std::string(1024, 'A' + i)));
+ }
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::SetImageStateRequest<>::create(
+ ictx, snap_id, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::GetImageStateRequest<>::create(
+ ictx, snap_id, &image_state, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ ASSERT_EQ(image_name, image_state.name);
+ ASSERT_EQ(features & ~RBD_FEATURES_IMPLICIT_ENABLE, image_state.features);
+ ASSERT_EQ(original_pairs_num + 10, image_state.metadata.size());
+ for (int i = 0; i < 10; i++) {
+ auto &bl = image_state.metadata[stringify(i)];
+ ASSERT_EQ(0, strncmp(std::string(1024, 'A' + i).c_str(), bl.c_str(),
+ bl.length()));
+ }
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::RemoveImageStateRequest<>::create(
+ ictx, snap_id, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ ASSERT_EQ(0, ictx->state->close());
+ ictx = nullptr;
+
+ ASSERT_EQ(0, image.snap_remove("snap"));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+}
+
+TEST_F(TestMirroring, SnapshotPromoteDemote)
+{
+ REQUIRE_FORMAT_V2();
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+ std::string peer_uuid;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &peer_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "cluster", "client"));
+
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+ ASSERT_EQ(0, image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_SNAPSHOT));
+ librbd::mirror_image_mode_t mode;
+ ASSERT_EQ(0, image.mirror_image_get_mode(&mode));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mode);
+
+ ASSERT_EQ(-EINVAL, image.mirror_image_promote(false));
+ ASSERT_EQ(0, image.mirror_image_demote());
+ ASSERT_EQ(0, image.mirror_image_promote(false));
+ ASSERT_EQ(0, image.mirror_image_demote());
+ ASSERT_EQ(0, image.mirror_image_promote(false));
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer_uuid));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestMirroring, AioSnapshotCreate)
+{
+ REQUIRE_FORMAT_V2();
+
+ std::list<std::string> image_names;
+ for (size_t idx = 0; idx < 10; ++idx) {
+ image_names.push_back(get_temp_image_name());
+ }
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE));
+ std::string peer_uuid;
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_add(m_ioctx, &peer_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ "cluster", "client"));
+ // create mirror images
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+ int order = 20;
+ std::list<librbd::Image> images;
+ for (auto &image_name : image_names) {
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 2048, features,
+ &order));
+ images.emplace_back();
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, images.back(), image_name.c_str()));
+ ASSERT_EQ(0, images.back().mirror_image_enable2(
+ RBD_MIRROR_IMAGE_MODE_SNAPSHOT));
+ }
+
+ // create snapshots
+ std::list<uint64_t> snap_ids;
+ std::list<librbd::RBD::AioCompletion *> aio_comps;
+ for (auto &image : images) {
+ snap_ids.emplace_back();
+ aio_comps.push_back(new librbd::RBD::AioCompletion(nullptr, nullptr));
+ ASSERT_EQ(0, image.aio_mirror_image_create_snapshot(0, &snap_ids.back(),
+ aio_comps.back()));
+ }
+ for (auto aio_comp : aio_comps) {
+ ASSERT_EQ(0, aio_comp->wait_for_complete());
+ ASSERT_EQ(1, aio_comp->is_complete());
+ ASSERT_EQ(0, aio_comp->get_return_value());
+ aio_comp->release();
+ }
+ aio_comps.clear();
+
+ // verify
+ for (auto &image : images) {
+ vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_EQ(2U, snaps.size());
+ ASSERT_EQ(snaps[1].id, snap_ids.front());
+
+ std::string image_name;
+ ASSERT_EQ(0, image.get_name(&image_name));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ snap_ids.pop_front();
+ }
+
+ ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer_uuid));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+}
diff --git a/src/test/librbd/test_mock_ConfigWatcher.cc b/src/test/librbd/test_mock_ConfigWatcher.cc
new file mode 100644
index 000000000..264e3d2c1
--- /dev/null
+++ b/src/test/librbd/test_mock_ConfigWatcher.cc
@@ -0,0 +1,100 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/ConfigWatcher.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include <list>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "librbd/ConfigWatcher.cc"
+
+namespace librbd {
+
+using ::testing::Invoke;
+
+class TestMockConfigWatcher : public TestMockFixture {
+public:
+ typedef ConfigWatcher<MockTestImageCtx> MockConfigWatcher;
+
+ librbd::ImageCtx *m_image_ctx;
+
+ ceph::mutex m_lock = ceph::make_mutex("m_lock");
+ ceph::condition_variable m_cv;
+ bool m_refreshed = false;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+ }
+
+ void expect_update_notification(MockTestImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, handle_update_notification())
+ .WillOnce(Invoke([this]() {
+ std::unique_lock locker{m_lock};
+ m_refreshed = true;
+ m_cv.notify_all();
+ }));
+ }
+
+ void wait_for_update_notification() {
+ std::unique_lock locker{m_lock};
+ m_cv.wait(locker, [this] {
+ if (m_refreshed) {
+ m_refreshed = false;
+ return true;
+ }
+ return false;
+ });
+ }
+};
+
+TEST_F(TestMockConfigWatcher, GlobalConfig) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ MockConfigWatcher mock_config_watcher(mock_image_ctx);
+ mock_config_watcher.init();
+
+ expect_update_notification(mock_image_ctx);
+ mock_image_ctx.cct->_conf.set_val("rbd_cache", "false");
+ mock_image_ctx.cct->_conf.set_val("rbd_cache", "true");
+ mock_image_ctx.cct->_conf.apply_changes(nullptr);
+ wait_for_update_notification();
+
+ mock_config_watcher.shut_down();
+}
+
+TEST_F(TestMockConfigWatcher, IgnoreOverriddenGlobalConfig) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ MockConfigWatcher mock_config_watcher(mock_image_ctx);
+ mock_config_watcher.init();
+
+ EXPECT_CALL(*mock_image_ctx.state, handle_update_notification())
+ .Times(0);
+ mock_image_ctx.config_overrides.insert("rbd_cache");
+ mock_image_ctx.cct->_conf.set_val("rbd_cache", "false");
+ mock_image_ctx.cct->_conf.set_val("rbd_cache", "true");
+ mock_image_ctx.cct->_conf.apply_changes(nullptr);
+
+ mock_config_watcher.shut_down();
+
+ ASSERT_FALSE(m_refreshed);
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_mock_DeepCopyRequest.cc b/src/test/librbd/test_mock_DeepCopyRequest.cc
new file mode 100644
index 000000000..de6ceb64d
--- /dev/null
+++ b/src/test/librbd/test_mock_DeepCopyRequest.cc
@@ -0,0 +1,466 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/AsioEngine.h"
+#include "librbd/DeepCopyRequest.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/internal.h"
+#include "librbd/api/Image.h"
+#include "librbd/deep_copy/Handler.h"
+#include "librbd/deep_copy/ImageCopyRequest.h"
+#include "librbd/deep_copy/MetadataCopyRequest.h"
+#include "librbd/deep_copy/SnapshotCopyRequest.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/test_support.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace deep_copy {
+
+template <>
+class ImageCopyRequest<librbd::MockTestImageCtx> {
+public:
+ static ImageCopyRequest* s_instance;
+ Context *on_finish;
+
+ static ImageCopyRequest* create(
+ librbd::MockTestImageCtx *src_image_ctx,
+ librbd::MockTestImageCtx *dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ bool flatten, const ObjectNumber &object_number,
+ const SnapSeqs &snap_seqs, Handler *handler,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ImageCopyRequest() {
+ s_instance = this;
+ }
+
+ void put() {
+ }
+
+ void get() {
+ }
+
+ MOCK_METHOD0(cancel, void());
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+class MetadataCopyRequest<librbd::MockTestImageCtx> {
+public:
+ static MetadataCopyRequest* s_instance;
+ Context *on_finish;
+
+ static MetadataCopyRequest* create(librbd::MockTestImageCtx *src_image_ctx,
+ librbd::MockTestImageCtx *dst_image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MetadataCopyRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+class SnapshotCopyRequest<librbd::MockTestImageCtx> {
+public:
+ static SnapshotCopyRequest* s_instance;
+ Context *on_finish;
+
+ static SnapshotCopyRequest* create(librbd::MockTestImageCtx *src_image_ctx,
+ librbd::MockTestImageCtx *dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ bool flatten,
+ librbd::asio::ContextWQ *work_queue,
+ SnapSeqs *snap_seqs, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SnapshotCopyRequest() {
+ s_instance = this;
+ }
+
+ void put() {
+ }
+
+ void get() {
+ }
+
+ MOCK_METHOD0(cancel, void());
+ MOCK_METHOD0(send, void());
+};
+
+ImageCopyRequest<librbd::MockTestImageCtx>* ImageCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+MetadataCopyRequest<librbd::MockTestImageCtx>* MetadataCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+SnapshotCopyRequest<librbd::MockTestImageCtx>* SnapshotCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace deep_copy
+} // namespace librbd
+
+// template definitions
+#include "librbd/DeepCopyRequest.cc"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnNew;
+using ::testing::SetArgPointee;
+using ::testing::WithArg;
+
+class TestMockDeepCopyRequest : public TestMockFixture {
+public:
+ typedef librbd::DeepCopyRequest<librbd::MockTestImageCtx> MockDeepCopyRequest;
+ typedef librbd::deep_copy::ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
+ typedef librbd::deep_copy::MetadataCopyRequest<librbd::MockTestImageCtx> MockMetadataCopyRequest;
+ typedef librbd::deep_copy::SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest;
+
+ librbd::ImageCtx *m_src_image_ctx;
+ librbd::ImageCtx *m_dst_image_ctx;
+
+ std::shared_ptr<librbd::AsioEngine> m_asio_engine;
+ librbd::asio::ContextWQ *m_work_queue;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_dst_image_ctx));
+
+ m_asio_engine = std::make_shared<librbd::AsioEngine>(
+ m_src_image_ctx->md_ctx);
+ m_work_queue = m_asio_engine->get_work_queue();
+ }
+
+ void TearDown() override {
+ TestMockFixture::TearDown();
+ }
+
+ void expect_test_features(librbd::MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_, _))
+ .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) {
+ return (mock_image_ctx.features & features) != 0;
+ })));
+ }
+
+ void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
+ EXPECT_CALL(mock_exclusive_lock, start_op(_)).WillOnce(Return(new LambdaContext([](int){})));
+ }
+
+ void expect_rollback_object_map(librbd::MockObjectMap &mock_object_map, int r) {
+ EXPECT_CALL(mock_object_map, rollback(_, _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context *ctx) {
+ m_work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_create_object_map(librbd::MockTestImageCtx &mock_image_ctx,
+ librbd::MockObjectMap *mock_object_map) {
+ EXPECT_CALL(mock_image_ctx, create_object_map(CEPH_NOSNAP))
+ .WillOnce(Return(mock_object_map));
+ }
+
+ void expect_open_object_map(librbd::MockTestImageCtx &mock_image_ctx,
+ librbd::MockObjectMap &mock_object_map, int r) {
+ EXPECT_CALL(mock_object_map, open(_))
+ .WillOnce(Invoke([this, r](Context *ctx) {
+ m_work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_copy_snapshots(
+ MockSnapshotCopyRequest &mock_snapshot_copy_request, int r) {
+ EXPECT_CALL(mock_snapshot_copy_request, send())
+ .WillOnce(Invoke([this, &mock_snapshot_copy_request, r]() {
+ m_work_queue->queue(mock_snapshot_copy_request.on_finish, r);
+ }));
+ }
+
+ void expect_copy_image(MockImageCopyRequest &mock_image_copy_request, int r) {
+ EXPECT_CALL(mock_image_copy_request, send())
+ .WillOnce(Invoke([this, &mock_image_copy_request, r]() {
+ m_work_queue->queue(mock_image_copy_request.on_finish, r);
+ }));
+ }
+
+ void expect_copy_object_map(librbd::MockExclusiveLock &mock_exclusive_lock,
+ librbd::MockObjectMap *mock_object_map, int r) {
+ expect_start_op(mock_exclusive_lock);
+ expect_rollback_object_map(*mock_object_map, r);
+ }
+
+ void expect_refresh_object_map(librbd::MockTestImageCtx &mock_image_ctx,
+ librbd::MockObjectMap *mock_object_map,
+ int r) {
+ expect_start_op(*mock_image_ctx.exclusive_lock);
+ expect_create_object_map(mock_image_ctx, mock_object_map);
+ expect_open_object_map(mock_image_ctx, *mock_object_map, r);
+ }
+
+ void expect_copy_metadata(MockMetadataCopyRequest &mock_metadata_copy_request,
+ int r) {
+ EXPECT_CALL(mock_metadata_copy_request, send())
+ .WillOnce(Invoke([this, &mock_metadata_copy_request, r]() {
+ m_work_queue->queue(mock_metadata_copy_request.on_finish, r);
+ }));
+ }
+};
+
+TEST_F(TestMockDeepCopyRequest, SimpleCopy) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockImageCopyRequest mock_image_copy_request;
+ MockMetadataCopyRequest mock_metadata_copy_request;
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_dst_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ librbd::MockObjectMap mock_object_map;
+
+ mock_dst_image_ctx.object_map = nullptr;
+ if (is_feature_enabled(RBD_FEATURE_OBJECT_MAP)) {
+ mock_dst_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_copy_snapshots(mock_snapshot_copy_request, 0);
+ expect_copy_image(mock_image_copy_request, 0);
+ if (mock_dst_image_ctx.object_map != nullptr) {
+ expect_refresh_object_map(mock_dst_image_ctx, &mock_object_map, 0);
+ }
+ expect_copy_metadata(mock_metadata_copy_request, 0);
+
+ C_SaferCond ctx;
+ librbd::SnapSeqs snap_seqs;
+ librbd::deep_copy::NoOpHandler no_op;
+ auto request = librbd::DeepCopyRequest<librbd::MockTestImageCtx>::create(
+ &mock_src_image_ctx, &mock_dst_image_ctx, 0, CEPH_NOSNAP, 0, false,
+ boost::none, m_work_queue, &snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyRequest, ErrorOnCopySnapshots) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+
+ InSequence seq;
+ expect_copy_snapshots(mock_snapshot_copy_request, -EINVAL);
+
+ C_SaferCond ctx;
+ librbd::SnapSeqs snap_seqs;
+ librbd::deep_copy::NoOpHandler no_op;
+ auto request = librbd::DeepCopyRequest<librbd::MockTestImageCtx>::create(
+ &mock_src_image_ctx, &mock_dst_image_ctx, 0, CEPH_NOSNAP, 0, false,
+ boost::none, m_work_queue, &snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyRequest, ErrorOnRefreshObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockImageCopyRequest mock_image_copy_request;
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_dst_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
+ mock_dst_image_ctx.object_map = mock_object_map;
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_copy_snapshots(mock_snapshot_copy_request, 0);
+ expect_copy_image(mock_image_copy_request, 0);
+ expect_start_op(*mock_dst_image_ctx.exclusive_lock);
+ expect_create_object_map(mock_dst_image_ctx, mock_object_map);
+ expect_open_object_map(mock_dst_image_ctx, *mock_object_map, -EINVAL);
+
+ C_SaferCond ctx;
+ librbd::SnapSeqs snap_seqs;
+ librbd::deep_copy::NoOpHandler no_op;
+ auto request = librbd::DeepCopyRequest<librbd::MockTestImageCtx>::create(
+ &mock_src_image_ctx, &mock_dst_image_ctx, 0, CEPH_NOSNAP, 0, false,
+ boost::none, m_work_queue, &snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyRequest, ErrorOnCopyImage) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockImageCopyRequest mock_image_copy_request;
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+
+ InSequence seq;
+ expect_copy_snapshots(mock_snapshot_copy_request, 0);
+ expect_copy_image(mock_image_copy_request, -EINVAL);
+
+ C_SaferCond ctx;
+ librbd::SnapSeqs snap_seqs;
+ librbd::deep_copy::NoOpHandler no_op;
+ auto request = librbd::DeepCopyRequest<librbd::MockTestImageCtx>::create(
+ &mock_src_image_ctx, &mock_dst_image_ctx, 0, CEPH_NOSNAP, 0, false,
+ boost::none, m_work_queue, &snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyRequest, ErrorOnCopyMetadata) {
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockImageCopyRequest mock_image_copy_request;
+ MockMetadataCopyRequest mock_metadata_copy_request;
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_dst_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ librbd::MockObjectMap mock_object_map;
+
+ mock_dst_image_ctx.object_map = nullptr;
+ if (is_feature_enabled(RBD_FEATURE_OBJECT_MAP)) {
+ mock_dst_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_copy_snapshots(mock_snapshot_copy_request, 0);
+ expect_copy_image(mock_image_copy_request, 0);
+ if (mock_dst_image_ctx.object_map != nullptr) {
+ expect_refresh_object_map(mock_dst_image_ctx, &mock_object_map, 0);
+ }
+ expect_copy_metadata(mock_metadata_copy_request, -EINVAL);
+
+ C_SaferCond ctx;
+ librbd::SnapSeqs snap_seqs;
+ librbd::deep_copy::NoOpHandler no_op;
+ auto request = librbd::DeepCopyRequest<librbd::MockTestImageCtx>::create(
+ &mock_src_image_ctx, &mock_dst_image_ctx, 0, CEPH_NOSNAP, 0, false,
+ boost::none, m_work_queue, &snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyRequest, Snap) {
+ EXPECT_EQ(0, snap_create(*m_src_image_ctx, "copy"));
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(m_src_image_ctx,
+ cls::rbd::UserSnapshotNamespace(),
+ "copy"));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockImageCopyRequest mock_image_copy_request;
+ MockMetadataCopyRequest mock_metadata_copy_request;
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_dst_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ librbd::MockObjectMap mock_object_map;
+ if (is_feature_enabled(RBD_FEATURE_OBJECT_MAP)) {
+ mock_dst_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_copy_snapshots(mock_snapshot_copy_request, 0);
+ expect_copy_image(mock_image_copy_request, 0);
+ if (mock_dst_image_ctx.object_map != nullptr) {
+ expect_copy_object_map(mock_exclusive_lock, &mock_object_map, 0);
+ expect_refresh_object_map(mock_dst_image_ctx, &mock_object_map, 0);
+ }
+ expect_copy_metadata(mock_metadata_copy_request, 0);
+
+ C_SaferCond ctx;
+ librbd::SnapSeqs snap_seqs = {{m_src_image_ctx->snap_id, 123}};
+ librbd::deep_copy::NoOpHandler no_op;
+ auto request = librbd::DeepCopyRequest<librbd::MockTestImageCtx>::create(
+ &mock_src_image_ctx, &mock_dst_image_ctx, 0, m_src_image_ctx->snap_id,
+ 0, false, boost::none, m_work_queue, &snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockDeepCopyRequest, ErrorOnRollbackObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ EXPECT_EQ(0, snap_create(*m_src_image_ctx, "copy"));
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(m_src_image_ctx,
+ cls::rbd::UserSnapshotNamespace(),
+ "copy"));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockImageCopyRequest mock_image_copy_request;
+ MockMetadataCopyRequest mock_metadata_copy_request;
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_dst_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ librbd::MockObjectMap mock_object_map;
+ mock_dst_image_ctx.object_map = &mock_object_map;
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_copy_snapshots(mock_snapshot_copy_request, 0);
+ expect_copy_image(mock_image_copy_request, 0);
+ expect_copy_object_map(mock_exclusive_lock, &mock_object_map, -EINVAL);
+
+ C_SaferCond ctx;
+ librbd::SnapSeqs snap_seqs = {{m_src_image_ctx->snap_id, 123}};
+ librbd::deep_copy::NoOpHandler no_op;
+ auto request = librbd::DeepCopyRequest<librbd::MockTestImageCtx>::create(
+ &mock_src_image_ctx, &mock_dst_image_ctx, 0, m_src_image_ctx->snap_id,
+ 0, false, boost::none, m_work_queue, &snap_seqs, &no_op, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
diff --git a/src/test/librbd/test_mock_ExclusiveLock.cc b/src/test/librbd/test_mock_ExclusiveLock.cc
new file mode 100644
index 000000000..6feb54ec6
--- /dev/null
+++ b/src/test/librbd/test_mock_ExclusiveLock.cc
@@ -0,0 +1,831 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/exclusive_lock/MockPolicy.h"
+#include "test/librbd/mock/io/MockImageDispatch.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ManagedLock.h"
+#include "librbd/exclusive_lock/ImageDispatch.h"
+#include "librbd/exclusive_lock/PreAcquireRequest.h"
+#include "librbd/exclusive_lock/PostAcquireRequest.h"
+#include "librbd/exclusive_lock/PreReleaseRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <list>
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockExclusiveLockImageCtx : public MockImageCtx {
+ asio::ContextWQ *op_work_queue;
+
+ MockExclusiveLockImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ op_work_queue = image_ctx.op_work_queue;
+ }
+};
+
+} // anonymous namespace
+
+namespace watcher {
+template <>
+struct Traits<MockExclusiveLockImageCtx> {
+ typedef librbd::MockImageWatcher Watcher;
+};
+}
+
+template <>
+struct ManagedLock<MockExclusiveLockImageCtx> {
+ ManagedLock(librados::IoCtx& ioctx, AsioEngine& asio_engine,
+ const std::string& oid, librbd::MockImageWatcher *watcher,
+ managed_lock::Mode mode, bool blocklist_on_break_lock,
+ uint32_t blocklist_expire_seconds)
+ {}
+
+ virtual ~ManagedLock() = default;
+
+ mutable ceph::mutex m_lock = ceph::make_mutex("ManagedLock::m_lock");
+
+ virtual void shutdown_handler(int r, Context *) = 0;
+ virtual void pre_acquire_lock_handler(Context *) = 0;
+ virtual void post_acquire_lock_handler(int, Context *) = 0;
+ virtual void pre_release_lock_handler(bool, Context *) = 0;
+ virtual void post_release_lock_handler(bool, int, Context *) = 0;
+ virtual void post_reacquire_lock_handler(int, Context *) = 0;
+
+ MOCK_CONST_METHOD0(is_lock_owner, bool());
+
+ MOCK_METHOD1(shut_down, void(Context*));
+ MOCK_METHOD1(acquire_lock, void(Context*));
+
+ void set_state_uninitialized() {
+ }
+
+ MOCK_METHOD0(set_state_initializing, void());
+ MOCK_METHOD0(set_state_unlocked, void());
+ MOCK_METHOD0(set_state_waiting_for_lock, void());
+ MOCK_METHOD0(set_state_post_acquiring, void());
+
+ MOCK_CONST_METHOD0(is_state_shutdown, bool());
+ MOCK_CONST_METHOD0(is_state_acquiring, bool());
+ MOCK_CONST_METHOD0(is_state_post_acquiring, bool());
+ MOCK_CONST_METHOD0(is_state_releasing, bool());
+ MOCK_CONST_METHOD0(is_state_pre_releasing, bool());
+ MOCK_CONST_METHOD0(is_state_locked, bool());
+ MOCK_CONST_METHOD0(is_state_waiting_for_lock, bool());
+
+ MOCK_CONST_METHOD0(is_action_acquire_lock, bool());
+ MOCK_METHOD0(execute_next_action, void());
+
+};
+
+namespace exclusive_lock {
+
+using librbd::ImageWatcher;
+
+template<typename T>
+struct BaseRequest {
+ static std::list<T *> s_requests;
+ Context *on_lock_unlock = nullptr;
+ Context *on_finish = nullptr;
+
+ static T* create(MockExclusiveLockImageCtx &image_ctx,
+ Context *on_lock_unlock, Context *on_finish) {
+ ceph_assert(!s_requests.empty());
+ T* req = s_requests.front();
+ req->on_lock_unlock = on_lock_unlock;
+ req->on_finish = on_finish;
+ s_requests.pop_front();
+ return req;
+ }
+
+ BaseRequest() {
+ s_requests.push_back(reinterpret_cast<T*>(this));
+ }
+};
+
+template<typename T>
+std::list<T *> BaseRequest<T>::s_requests;
+
+template<>
+struct ImageDispatch<MockExclusiveLockImageCtx>
+ : public librbd::io::MockImageDispatch {
+ static ImageDispatch* s_instance;
+ static ImageDispatch* create(MockExclusiveLockImageCtx*) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ void destroy() {
+ }
+
+ ImageDispatch() {
+ s_instance = this;
+ }
+
+ io::ImageDispatchLayer get_dispatch_layer() const override {
+ return io::IMAGE_DISPATCH_LAYER_EXCLUSIVE_LOCK;
+ }
+
+ MOCK_METHOD3(set_require_lock, void(bool, io::Direction, Context*));
+ MOCK_METHOD1(unset_require_lock, void(io::Direction));
+
+};
+
+ImageDispatch<MockExclusiveLockImageCtx>* ImageDispatch<MockExclusiveLockImageCtx>::s_instance = nullptr;
+
+template <>
+struct PreAcquireRequest<MockExclusiveLockImageCtx> : public BaseRequest<PreAcquireRequest<MockExclusiveLockImageCtx> > {
+ static PreAcquireRequest<MockExclusiveLockImageCtx> *create(
+ MockExclusiveLockImageCtx &image_ctx, Context *on_finish) {
+ return BaseRequest::create(image_ctx, nullptr, on_finish);
+ }
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct PostAcquireRequest<MockExclusiveLockImageCtx> : public BaseRequest<PostAcquireRequest<MockExclusiveLockImageCtx> > {
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct PreReleaseRequest<MockExclusiveLockImageCtx> : public BaseRequest<PreReleaseRequest<MockExclusiveLockImageCtx> > {
+ static PreReleaseRequest<MockExclusiveLockImageCtx> *create(
+ MockExclusiveLockImageCtx &image_ctx,
+ ImageDispatch<MockExclusiveLockImageCtx>* ImageDispatch,
+ bool shutting_down, AsyncOpTracker &async_op_tracker,
+ Context *on_finish) {
+ return BaseRequest::create(image_ctx, nullptr, on_finish);
+ }
+ MOCK_METHOD0(send, void());
+};
+
+} // namespace exclusive_lock
+} // namespace librbd
+
+// template definitions
+#include "librbd/ExclusiveLock.cc"
+
+ACTION_P(FinishLockUnlock, request) {
+ if (request->on_lock_unlock != nullptr) {
+ request->on_lock_unlock->complete(0);
+ }
+}
+
+ACTION_P2(CompleteRequest, request, ret) {
+ request->on_finish->complete(ret);
+}
+
+namespace librbd {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockExclusiveLock : public TestMockFixture {
+public:
+ typedef ManagedLock<MockExclusiveLockImageCtx> MockManagedLock;
+ typedef ExclusiveLock<MockExclusiveLockImageCtx> MockExclusiveLock;
+ typedef exclusive_lock::ImageDispatch<MockExclusiveLockImageCtx> MockImageDispatch;
+ typedef exclusive_lock::PreAcquireRequest<MockExclusiveLockImageCtx> MockPreAcquireRequest;
+ typedef exclusive_lock::PostAcquireRequest<MockExclusiveLockImageCtx> MockPostAcquireRequest;
+ typedef exclusive_lock::PreReleaseRequest<MockExclusiveLockImageCtx> MockPreReleaseRequest;
+
+
+ void expect_set_state_initializing(MockManagedLock *managed_lock) {
+ EXPECT_CALL(*managed_lock, set_state_initializing());
+ }
+
+ void expect_set_state_unlocked(MockManagedLock *managed_lock) {
+ EXPECT_CALL(*managed_lock, set_state_unlocked());
+ }
+
+ void expect_set_state_waiting_for_lock(MockManagedLock *managed_lock) {
+ EXPECT_CALL(*managed_lock, set_state_waiting_for_lock());
+ }
+
+ void expect_set_state_post_acquiring(MockManagedLock *managed_lock) {
+ EXPECT_CALL(*managed_lock, set_state_post_acquiring());
+ }
+
+ void expect_is_state_acquiring(MockManagedLock *managed_lock, bool ret_val) {
+ EXPECT_CALL(*managed_lock, is_state_acquiring())
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_is_state_waiting_for_lock(MockManagedLock *managed_lock,
+ bool ret_val) {
+ EXPECT_CALL(*managed_lock, is_state_waiting_for_lock())
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_is_state_pre_releasing(MockManagedLock *managed_lock,
+ bool ret_val) {
+ EXPECT_CALL(*managed_lock, is_state_pre_releasing())
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_is_state_releasing(MockManagedLock *managed_lock, bool ret_val) {
+ EXPECT_CALL(*managed_lock, is_state_releasing())
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_is_state_locked(MockManagedLock *managed_lock, bool ret_val) {
+ EXPECT_CALL(*managed_lock, is_state_locked())
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_is_state_shutdown(MockManagedLock *managed_lock, bool ret_val) {
+ EXPECT_CALL(*managed_lock, is_state_shutdown())
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_is_action_acquire_lock(MockManagedLock *managed_lock,
+ bool ret_val) {
+ EXPECT_CALL(*managed_lock, is_action_acquire_lock())
+ .WillOnce(Return(ret_val));
+ }
+
+ void expect_set_require_lock(MockImageDispatch &mock_image_dispatch,
+ bool init_shutdown, io::Direction direction) {
+ EXPECT_CALL(mock_image_dispatch, set_require_lock(init_shutdown,
+ direction, _))
+ .WillOnce(WithArg<2>(Invoke([](Context* ctx) { ctx->complete(0); })));
+ }
+
+ void expect_set_require_lock(MockExclusiveLockImageCtx &mock_image_ctx,
+ MockImageDispatch &mock_image_dispatch,
+ bool init_shutdown) {
+ if (mock_image_ctx.clone_copy_on_read ||
+ (mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0 ||
+ is_rbd_pwl_enabled(mock_image_ctx.cct)) {
+ expect_set_require_lock(mock_image_dispatch, init_shutdown,
+ io::DIRECTION_BOTH);
+ } else {
+ expect_set_require_lock(mock_image_dispatch, init_shutdown,
+ io::DIRECTION_WRITE);
+ }
+ }
+
+ void expect_unset_require_lock(MockImageDispatch &mock_image_dispatch) {
+ EXPECT_CALL(mock_image_dispatch, unset_require_lock(io::DIRECTION_BOTH));
+ }
+
+ void expect_register_dispatch(MockExclusiveLockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, register_dispatch(_));
+ }
+
+ void expect_shut_down_dispatch(MockExclusiveLockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, shut_down_dispatch(_, _))
+ .WillOnce(WithArg<1>(Invoke([](Context* ctx) {
+ ctx->complete(0);
+ })));
+ }
+
+ void expect_prepare_lock_complete(MockExclusiveLockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
+ }
+
+ void expect_pre_acquire_request(MockPreAcquireRequest &pre_acquire_request,
+ int r) {
+ EXPECT_CALL(pre_acquire_request, send())
+ .WillOnce(CompleteRequest(&pre_acquire_request, r));
+ }
+
+ void expect_post_acquire_request(MockExclusiveLock *mock_exclusive_lock,
+ MockPostAcquireRequest &post_acquire_request,
+ int r) {
+ EXPECT_CALL(post_acquire_request, send())
+ .WillOnce(DoAll(FinishLockUnlock(&post_acquire_request),
+ CompleteRequest(&post_acquire_request, r)));
+ expect_set_state_post_acquiring(mock_exclusive_lock);
+ }
+
+ void expect_pre_release_request(MockPreReleaseRequest &pre_release_request,
+ int r) {
+ EXPECT_CALL(pre_release_request, send())
+ .WillOnce(CompleteRequest(&pre_release_request, r));
+ }
+
+ void expect_notify_request_lock(MockExclusiveLockImageCtx &mock_image_ctx,
+ MockExclusiveLock *mock_exclusive_lock) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_request_lock());
+ }
+
+ void expect_notify_acquired_lock(MockExclusiveLockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_acquired_lock())
+ .Times(1);
+ }
+
+ void expect_notify_released_lock(MockExclusiveLockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_released_lock())
+ .Times(1);
+ }
+
+ void expect_flush_notifies(MockExclusiveLockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
+ void expect_shut_down(MockManagedLock *managed_lock) {
+ EXPECT_CALL(*managed_lock, shut_down(_))
+ .WillOnce(CompleteContext(0, static_cast<asio::ContextWQ*>(nullptr)));
+ }
+
+ void expect_accept_blocked_request(
+ MockExclusiveLockImageCtx &mock_image_ctx,
+ exclusive_lock::MockPolicy &policy,
+ exclusive_lock::OperationRequestType request_type, bool value) {
+ EXPECT_CALL(mock_image_ctx, get_exclusive_lock_policy())
+ .WillOnce(Return(&policy));
+ EXPECT_CALL(policy, accept_blocked_request(request_type))
+ .WillOnce(Return(value));
+ }
+
+ int when_init(MockExclusiveLockImageCtx &mock_image_ctx,
+ MockExclusiveLock *exclusive_lock) {
+ C_SaferCond ctx;
+ {
+ std::unique_lock owner_locker{mock_image_ctx.owner_lock};
+ exclusive_lock->init(mock_image_ctx.features, &ctx);
+ }
+ return ctx.wait();
+ }
+
+ int when_pre_acquire_lock_handler(MockManagedLock *managed_lock) {
+ C_SaferCond ctx;
+ managed_lock->pre_acquire_lock_handler(&ctx);
+ return ctx.wait();
+ }
+
+ int when_post_acquire_lock_handler(MockManagedLock *managed_lock, int r) {
+ C_SaferCond ctx;
+ managed_lock->post_acquire_lock_handler(r, &ctx);
+ return ctx.wait();
+ }
+
+ int when_pre_release_lock_handler(MockManagedLock *managed_lock,
+ bool shutting_down) {
+ C_SaferCond ctx;
+ managed_lock->pre_release_lock_handler(shutting_down, &ctx);
+ return ctx.wait();
+ }
+
+ int when_post_release_lock_handler(MockManagedLock *managed_lock,
+ bool shutting_down, int r) {
+ C_SaferCond ctx;
+ managed_lock->post_release_lock_handler(shutting_down, r, &ctx);
+ return ctx.wait();
+ }
+
+ int when_post_reacquire_lock_handler(MockManagedLock *managed_lock, int r) {
+ C_SaferCond ctx;
+ managed_lock->post_reacquire_lock_handler(r, &ctx);
+ return ctx.wait();
+ }
+
+ int when_shut_down(MockExclusiveLockImageCtx &mock_image_ctx,
+ MockExclusiveLock *exclusive_lock) {
+ C_SaferCond ctx;
+ {
+ std::unique_lock owner_locker{mock_image_ctx.owner_lock};
+ exclusive_lock->shut_down(&ctx);
+ }
+ return ctx.wait();
+ }
+
+ bool is_lock_owner(MockExclusiveLockImageCtx &mock_image_ctx,
+ MockExclusiveLock *exclusive_lock) {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ return exclusive_lock->is_lock_owner();
+ }
+};
+
+TEST_F(TestMockExclusiveLock, StateTransitions) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // (try) acquire lock
+ MockPreAcquireRequest try_lock_pre_acquire;
+ expect_pre_acquire_request(try_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ MockPostAcquireRequest try_lock_post_acquire;
+ expect_post_acquire_request(exclusive_lock, try_lock_post_acquire, 0);
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_notify_acquired_lock(mock_image_ctx);
+ expect_unset_require_lock(mock_image_dispatch);
+ ASSERT_EQ(0, when_post_acquire_lock_handler(exclusive_lock, 0));
+
+ // release lock
+ MockPreReleaseRequest pre_request_release;
+ expect_pre_release_request(pre_request_release, 0);
+ ASSERT_EQ(0, when_pre_release_lock_handler(exclusive_lock, false));
+
+ expect_is_state_pre_releasing(exclusive_lock, false);
+ expect_is_state_releasing(exclusive_lock, true);
+ expect_notify_released_lock(mock_image_ctx);
+ ASSERT_EQ(0, when_post_release_lock_handler(exclusive_lock, false, 0));
+
+ // (try) acquire lock
+ MockPreAcquireRequest request_lock_pre_acquire;
+ expect_pre_acquire_request(request_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ MockPostAcquireRequest request_lock_post_acquire;
+ expect_post_acquire_request(exclusive_lock, request_lock_post_acquire, 0);
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_notify_acquired_lock(mock_image_ctx);
+ expect_unset_require_lock(mock_image_dispatch);
+ ASSERT_EQ(0, when_post_acquire_lock_handler(exclusive_lock, 0));
+
+ // shut down (and release)
+ expect_shut_down(exclusive_lock);
+ expect_is_state_waiting_for_lock(exclusive_lock, false);
+ ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+
+ MockPreReleaseRequest shutdown_pre_release;
+ expect_pre_release_request(shutdown_pre_release, 0);
+ ASSERT_EQ(0, when_pre_release_lock_handler(exclusive_lock, true));
+
+ expect_shut_down_dispatch(mock_image_ctx);
+ expect_notify_released_lock(mock_image_ctx);
+ ASSERT_EQ(0, when_post_release_lock_handler(exclusive_lock, true, 0));
+}
+
+TEST_F(TestMockExclusiveLock, TryLockAlreadyLocked) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // try acquire lock
+ MockPreAcquireRequest try_lock_pre_acquire;
+ expect_pre_acquire_request(try_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_prepare_lock_complete(mock_image_ctx);
+ expect_is_action_acquire_lock(exclusive_lock, false);
+ ASSERT_EQ(0, when_post_acquire_lock_handler(exclusive_lock, -EAGAIN));
+}
+
+TEST_F(TestMockExclusiveLock, TryLockError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // try acquire lock
+ MockPreAcquireRequest try_lock_pre_acquire;
+ expect_pre_acquire_request(try_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_prepare_lock_complete(mock_image_ctx);
+ expect_is_action_acquire_lock(exclusive_lock, false);
+ ASSERT_EQ(-EBUSY, when_post_acquire_lock_handler(exclusive_lock, -EBUSY));
+}
+
+TEST_F(TestMockExclusiveLock, AcquireLockAlreadyLocked) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // acquire lock
+ MockPreAcquireRequest try_lock_pre_acquire;
+ expect_pre_acquire_request(try_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_prepare_lock_complete(mock_image_ctx);
+ expect_is_action_acquire_lock(exclusive_lock, true);
+ expect_set_state_waiting_for_lock(exclusive_lock);
+ expect_notify_request_lock(mock_image_ctx, exclusive_lock);
+ ASSERT_EQ(-ECANCELED, when_post_acquire_lock_handler(exclusive_lock,
+ -EAGAIN));
+}
+
+TEST_F(TestMockExclusiveLock, AcquireLockBusy) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // acquire lock
+ MockPreAcquireRequest try_lock_pre_acquire;
+ expect_pre_acquire_request(try_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_prepare_lock_complete(mock_image_ctx);
+ expect_is_action_acquire_lock(exclusive_lock, true);
+ expect_set_state_waiting_for_lock(exclusive_lock);
+ expect_notify_request_lock(mock_image_ctx, exclusive_lock);
+ ASSERT_EQ(-ECANCELED, when_post_acquire_lock_handler(exclusive_lock,
+ -EBUSY));
+}
+
+TEST_F(TestMockExclusiveLock, AcquireLockError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // acquire lock
+ MockPreAcquireRequest try_lock_pre_acquire;
+ expect_pre_acquire_request(try_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_prepare_lock_complete(mock_image_ctx);
+ expect_is_action_acquire_lock(exclusive_lock, true);
+ ASSERT_EQ(-EBLOCKLISTED, when_post_acquire_lock_handler(exclusive_lock,
+ -EBLOCKLISTED));
+}
+
+TEST_F(TestMockExclusiveLock, PostAcquireLockError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // (try) acquire lock
+ MockPreAcquireRequest request_lock_pre_acquire;
+ expect_pre_acquire_request(request_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ MockPostAcquireRequest request_lock_post_acquire;
+ expect_post_acquire_request(exclusive_lock, request_lock_post_acquire,
+ -EPERM);
+ expect_is_state_acquiring(exclusive_lock, true);
+ ASSERT_EQ(-EPERM, when_post_acquire_lock_handler(exclusive_lock, 0));
+}
+
+TEST_F(TestMockExclusiveLock, PreReleaseLockError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // release lock
+ MockPreReleaseRequest pre_request_release;
+ expect_pre_release_request(pre_request_release, -EINVAL);
+ ASSERT_EQ(-EINVAL, when_pre_release_lock_handler(exclusive_lock, false));
+
+ expect_is_state_pre_releasing(exclusive_lock, true);
+ ASSERT_EQ(-EINVAL, when_post_release_lock_handler(exclusive_lock, false,
+ -EINVAL));
+}
+
+TEST_F(TestMockExclusiveLock, ReacquireLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ // (try) acquire lock
+ MockPreAcquireRequest try_lock_pre_acquire;
+ expect_pre_acquire_request(try_lock_pre_acquire, 0);
+ ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+ MockPostAcquireRequest try_lock_post_acquire;
+ expect_post_acquire_request(exclusive_lock, try_lock_post_acquire, 0);
+ expect_is_state_acquiring(exclusive_lock, true);
+ expect_notify_acquired_lock(mock_image_ctx);
+ expect_unset_require_lock(mock_image_dispatch);
+ ASSERT_EQ(0, when_post_acquire_lock_handler(exclusive_lock, 0));
+
+ // reacquire lock
+ expect_notify_acquired_lock(mock_image_ctx);
+ ASSERT_EQ(0, when_post_reacquire_lock_handler(exclusive_lock, 0));
+
+ // shut down (and release)
+ expect_shut_down(exclusive_lock);
+ expect_is_state_waiting_for_lock(exclusive_lock, false);
+ ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+
+ MockPreReleaseRequest shutdown_pre_release;
+ expect_pre_release_request(shutdown_pre_release, 0);
+ ASSERT_EQ(0, when_pre_release_lock_handler(exclusive_lock, true));
+
+ expect_shut_down_dispatch(mock_image_ctx);
+ expect_notify_released_lock(mock_image_ctx);
+ ASSERT_EQ(0, when_post_release_lock_handler(exclusive_lock, true, 0));
+}
+
+TEST_F(TestMockExclusiveLock, BlockRequests) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock *exclusive_lock = new MockExclusiveLock(mock_image_ctx);
+ exclusive_lock::MockPolicy mock_exclusive_lock_policy;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&exclusive_lock) {
+ exclusive_lock->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ expect_set_state_initializing(exclusive_lock);
+ MockImageDispatch mock_image_dispatch;
+ expect_register_dispatch(mock_image_ctx);
+ expect_set_require_lock(mock_image_ctx, mock_image_dispatch, true);
+ expect_set_state_unlocked(exclusive_lock);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ int ret_val;
+ expect_is_state_shutdown(exclusive_lock, false);
+ expect_is_state_locked(exclusive_lock, true);
+ ASSERT_TRUE(exclusive_lock->accept_request(
+ exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, &ret_val));
+ ASSERT_EQ(0, ret_val);
+
+ exclusive_lock->block_requests(-EROFS);
+ expect_is_state_shutdown(exclusive_lock, false);
+ expect_is_state_locked(exclusive_lock, true);
+ expect_accept_blocked_request(mock_image_ctx, mock_exclusive_lock_policy,
+ exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL,
+ false);
+ ASSERT_FALSE(exclusive_lock->accept_request(
+ exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, &ret_val));
+ ASSERT_EQ(-EROFS, ret_val);
+
+ expect_is_state_shutdown(exclusive_lock, false);
+ expect_is_state_locked(exclusive_lock, true);
+ expect_accept_blocked_request(
+ mock_image_ctx, mock_exclusive_lock_policy,
+ exclusive_lock::OPERATION_REQUEST_TYPE_TRASH_SNAP_REMOVE, true);
+ ASSERT_TRUE(exclusive_lock->accept_request(
+ exclusive_lock::OPERATION_REQUEST_TYPE_TRASH_SNAP_REMOVE,
+ &ret_val));
+ ASSERT_EQ(0, ret_val);
+
+ exclusive_lock->unblock_requests();
+ expect_is_state_shutdown(exclusive_lock, false);
+ expect_is_state_locked(exclusive_lock, true);
+ ASSERT_TRUE(exclusive_lock->accept_request(
+ exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, &ret_val));
+ ASSERT_EQ(0, ret_val);
+}
+
+} // namespace librbd
+
diff --git a/src/test/librbd/test_mock_Journal.cc b/src/test/librbd/test_mock_Journal.cc
new file mode 100644
index 000000000..2fe74d2fe
--- /dev/null
+++ b/src/test/librbd/test_mock_Journal.cc
@@ -0,0 +1,1632 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournalPolicy.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
+#include "common/Cond.h"
+#include "common/ceph_mutex.h"
+#include "common/WorkQueue.h"
+#include "cls/journal/cls_journal_types.h"
+#include "journal/Journaler.h"
+#include "librbd/Journal.h"
+#include "librbd/Utils.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ObjectDispatchSpec.h"
+#include "librbd/journal/Replay.h"
+#include "librbd/journal/RemoveRequest.h"
+#include "librbd/journal/CreateRequest.h"
+#include "librbd/journal/ObjectDispatch.h"
+#include "librbd/journal/OpenRequest.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "librbd/journal/PromoteRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <functional>
+#include <list>
+#include <boost/scope_exit.hpp>
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rbd
+
+namespace librbd {
+
+namespace {
+
+struct MockJournalImageCtx : public MockImageCtx {
+ MockJournalImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<MockJournalImageCtx> {
+ typedef ::journal::MockJournalerProxy Journaler;
+ typedef ::journal::MockFutureProxy Future;
+ typedef ::journal::MockReplayEntryProxy ReplayEntry;
+};
+
+struct MockReplay {
+ static MockReplay *s_instance;
+ static MockReplay &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockReplay() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD2(shut_down, void(bool cancel_ops, Context *));
+ MOCK_METHOD2(decode, int(bufferlist::const_iterator*, EventEntry *));
+ MOCK_METHOD3(process, void(const EventEntry&, Context *, Context *));
+ MOCK_METHOD2(replay_op_ready, void(uint64_t, Context *));
+};
+
+template <>
+class Replay<MockJournalImageCtx> {
+public:
+ static Replay *create(MockJournalImageCtx &image_ctx) {
+ return new Replay();
+ }
+
+ void shut_down(bool cancel_ops, Context *on_finish) {
+ MockReplay::get_instance().shut_down(cancel_ops, on_finish);
+ }
+
+ int decode(bufferlist::const_iterator *it, EventEntry *event_entry) {
+ return MockReplay::get_instance().decode(it, event_entry);
+ }
+
+ void process(const EventEntry& event_entry, Context *on_ready,
+ Context *on_commit) {
+ MockReplay::get_instance().process(event_entry, on_ready, on_commit);
+ }
+
+ void replay_op_ready(uint64_t op_tid, Context *on_resume) {
+ MockReplay::get_instance().replay_op_ready(op_tid, on_resume);
+ }
+};
+
+MockReplay *MockReplay::s_instance = nullptr;
+
+struct MockRemove {
+ static MockRemove *s_instance;
+ static MockRemove &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockRemove() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+class RemoveRequest<MockJournalImageCtx> {
+public:
+ static RemoveRequest *create(IoCtx &ioctx, const std::string &imageid,
+ const std::string &client_id,
+ ContextWQ *op_work_queue, Context *on_finish) {
+ return new RemoveRequest();
+ }
+
+ void send() {
+ MockRemove::get_instance().send();
+ }
+};
+
+MockRemove *MockRemove::s_instance = nullptr;
+
+struct MockCreate {
+ static MockCreate *s_instance;
+ static MockCreate &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockCreate() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+class CreateRequest<MockJournalImageCtx> {
+public:
+ static CreateRequest *create(IoCtx &ioctx, const std::string &imageid,
+ uint8_t order, uint8_t splay_width,
+ const std::string &object_pool,
+ uint64_t tag_class, TagData &tag_data,
+ const std::string &client_id,
+ ContextWQ *op_work_queue, Context *on_finish) {
+ return new CreateRequest();
+ }
+
+ void send() {
+ MockCreate::get_instance().send();
+ }
+};
+
+MockCreate *MockCreate::s_instance = nullptr;
+
+template<>
+class OpenRequest<MockJournalImageCtx> {
+public:
+ TagData *tag_data;
+ Context *on_finish;
+ static OpenRequest *s_instance;
+ static OpenRequest *create(MockJournalImageCtx *image_ctx,
+ ::journal::MockJournalerProxy *journaler,
+ ceph::mutex *lock, journal::ImageClientMeta *client_meta,
+ uint64_t *tag_tid, journal::TagData *tag_data,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->tag_data = tag_data;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ OpenRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+OpenRequest<MockJournalImageCtx> *OpenRequest<MockJournalImageCtx>::s_instance = nullptr;
+
+
+template <>
+class PromoteRequest<MockJournalImageCtx> {
+public:
+ static PromoteRequest s_instance;
+ static PromoteRequest *create(MockJournalImageCtx *image_ctx, bool force,
+ Context *on_finish) {
+ return &s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+PromoteRequest<MockJournalImageCtx> PromoteRequest<MockJournalImageCtx>::s_instance;
+
+template <>
+struct ObjectDispatch<MockJournalImageCtx> : public io::MockObjectDispatch {
+ static ObjectDispatch* s_instance;
+
+ static ObjectDispatch* create(MockJournalImageCtx* image_ctx,
+ Journal<MockJournalImageCtx>* journal) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ ObjectDispatch() {
+ s_instance = this;
+ }
+};
+
+ObjectDispatch<MockJournalImageCtx>* ObjectDispatch<MockJournalImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+} // namespace librbd
+
+// template definitions
+#include "librbd/Journal.cc"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::MatcherCast;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SetArgPointee;
+using ::testing::WithArg;
+using namespace std::placeholders;
+
+ACTION_P2(StartReplay, wq, ctx) {
+ wq->queue(ctx, 0);
+}
+
+namespace librbd {
+
+class TestMockJournal : public TestMockFixture {
+public:
+ typedef journal::MockReplay MockJournalReplay;
+ typedef Journal<MockJournalImageCtx> MockJournal;
+ typedef journal::OpenRequest<MockJournalImageCtx> MockJournalOpenRequest;
+ typedef journal::ObjectDispatch<MockJournalImageCtx> MockObjectDispatch;
+ typedef std::function<void(::journal::ReplayHandler*)> ReplayAction;
+ typedef std::list<Context *> Contexts;
+
+ TestMockJournal() = default;
+ ~TestMockJournal() override {
+ ceph_assert(m_commit_contexts.empty());
+ }
+
+ ceph::mutex m_lock = ceph::make_mutex("lock");
+ ceph::condition_variable m_cond;
+ Contexts m_commit_contexts;
+
+ struct C_ReplayAction : public Context {
+ ::journal::ReplayHandler **replay_handler;
+ ReplayAction replay_action;
+
+ C_ReplayAction(::journal::ReplayHandler **replay_handler,
+ const ReplayAction &replay_action)
+ : replay_handler(replay_handler), replay_action(replay_action) {
+ }
+ void finish(int r) override {
+ if (replay_action) {
+ replay_action(*replay_handler);
+ }
+ }
+ };
+
+ void expect_construct_journaler(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, construct());
+ }
+
+ void expect_open_journaler(MockImageCtx &mock_image_ctx,
+ ::journal::MockJournaler &mock_journaler,
+ MockJournalOpenRequest &mock_open_request,
+ bool primary, int r) {
+ EXPECT_CALL(mock_journaler, add_listener(_))
+ .WillOnce(SaveArg<0>(&m_listener));
+ EXPECT_CALL(mock_open_request, send())
+ .WillOnce(DoAll(Invoke([&mock_open_request, primary]() {
+ if (!primary) {
+ mock_open_request.tag_data->mirror_uuid = "remote mirror uuid";
+ }
+ }),
+ FinishRequest(&mock_open_request, r,
+ &mock_image_ctx)));
+ }
+
+ void expect_shut_down_journaler(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, remove_listener(_));
+ EXPECT_CALL(mock_journaler, shut_down(_))
+ .WillOnce(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ void expect_register_dispatch(MockImageCtx& mock_image_ctx,
+ MockObjectDispatch& mock_object_dispatch) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
+ register_dispatch(&mock_object_dispatch));
+ }
+
+ void expect_shut_down_dispatch(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
+ shut_down_dispatch(io::OBJECT_DISPATCH_LAYER_JOURNAL, _))
+ .WillOnce(WithArg<1>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+
+ void expect_get_max_append_size(::journal::MockJournaler &mock_journaler,
+ uint32_t max_size) {
+ EXPECT_CALL(mock_journaler, get_max_append_size())
+ .WillOnce(Return(max_size));
+ }
+
+ void expect_get_journaler_cached_client(::journal::MockJournaler &mock_journaler,
+ const journal::ImageClientMeta &client_meta,
+ int r) {
+ journal::ClientData client_data;
+ client_data.client_meta = client_meta;
+
+ cls::journal::Client client;
+ encode(client_data, client.data);
+
+ EXPECT_CALL(mock_journaler, get_cached_client("", _))
+ .WillOnce(DoAll(SetArgPointee<1>(client),
+ Return(r)));
+ }
+
+ void expect_get_journaler_tags(MockImageCtx &mock_image_ctx,
+ ::journal::MockJournaler &mock_journaler,
+ uint64_t start_after_tag_tid,
+ ::journal::Journaler::Tags &&tags, int r) {
+ EXPECT_CALL(mock_journaler, get_tags(start_after_tag_tid, 0, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(tags),
+ WithArg<3>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))));
+ }
+
+ void expect_start_replay(MockJournalImageCtx &mock_image_ctx,
+ ::journal::MockJournaler &mock_journaler,
+ const ReplayAction &action) {
+ EXPECT_CALL(mock_journaler, start_replay(_))
+ .WillOnce(DoAll(SaveArg<0>(&m_replay_handler),
+ StartReplay(mock_image_ctx.image_ctx->op_work_queue,
+ new C_ReplayAction(&m_replay_handler,
+ action))));
+ }
+
+ void expect_stop_replay(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, stop_replay(_))
+ .WillOnce(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ void expect_shut_down_replay(MockJournalImageCtx &mock_image_ctx,
+ MockJournalReplay &mock_journal_replay, int r,
+ bool cancel_ops = false) {
+ EXPECT_CALL(mock_journal_replay, shut_down(cancel_ops, _))
+ .WillOnce(WithArg<1>(Invoke([this, &mock_image_ctx, r](Context *on_flush) {
+ this->commit_replay(mock_image_ctx, on_flush, r);})));
+ }
+
+ void expect_get_data(::journal::MockReplayEntry &mock_replay_entry) {
+ EXPECT_CALL(mock_replay_entry, get_data())
+ .WillOnce(Return(bufferlist()));
+ }
+
+ void expect_try_pop_front(MockJournalImageCtx &mock_image_ctx,
+ ::journal::MockJournaler &mock_journaler,
+ bool entries_available,
+ ::journal::MockReplayEntry &mock_replay_entry,
+ const ReplayAction &action = {}) {
+ EXPECT_CALL(mock_journaler, try_pop_front(_))
+ .WillOnce(DoAll(SetArgPointee<0>(::journal::MockReplayEntryProxy()),
+ StartReplay(mock_image_ctx.image_ctx->op_work_queue,
+ new C_ReplayAction(&m_replay_handler,
+ action)),
+ Return(entries_available)));
+ if (entries_available) {
+ expect_get_data(mock_replay_entry);
+ }
+ }
+
+ void expect_replay_process(MockJournalReplay &mock_journal_replay) {
+ EXPECT_CALL(mock_journal_replay, decode(_, _))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock_journal_replay, process(_, _, _))
+ .WillOnce(DoAll(WithArg<1>(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL))),
+ WithArg<2>(Invoke(this, &TestMockJournal::save_commit_context))));
+ }
+
+ void expect_start_append(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, start_append(_));
+ }
+
+ void expect_set_append_batch_options(MockJournalImageCtx &mock_image_ctx,
+ ::journal::MockJournaler &mock_journaler,
+ bool user_flushed) {
+ if (mock_image_ctx.image_ctx->config.get_val<bool>("rbd_journal_object_writethrough_until_flush") ==
+ user_flushed) {
+ EXPECT_CALL(mock_journaler, set_append_batch_options(_, _, _));
+ }
+ }
+
+ void expect_stop_append(::journal::MockJournaler &mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, stop_append(_))
+ .WillOnce(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ void expect_committed(::journal::MockJournaler &mock_journaler,
+ size_t events) {
+ EXPECT_CALL(mock_journaler, committed(MatcherCast<const ::journal::MockReplayEntryProxy&>(_)))
+ .Times(events);
+ }
+
+ void expect_append_journaler(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, append(_, _))
+ .WillOnce(Return(::journal::MockFutureProxy()));
+ }
+
+ void expect_wait_future(::journal::MockFuture &mock_future,
+ Context **on_safe) {
+ EXPECT_CALL(mock_future, wait(_))
+ .WillOnce(SaveArg<0>(on_safe));
+ }
+
+ void expect_future_committed(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, committed(MatcherCast<const ::journal::MockFutureProxy&>(_)));
+ }
+
+ void expect_future_is_valid(::journal::MockFuture &mock_future) {
+ EXPECT_CALL(mock_future, is_valid()).WillOnce(Return(false));
+ }
+
+ void expect_flush_commit_position(::journal::MockJournaler &mock_journaler) {
+ EXPECT_CALL(mock_journaler, flush_commit_position(_))
+ .WillOnce(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL)));
+ }
+
+ int when_open(MockJournal *mock_journal) {
+ C_SaferCond ctx;
+ mock_journal->open(&ctx);
+ return ctx.wait();
+ }
+
+ int when_close(MockJournal *mock_journal) {
+ C_SaferCond ctx;
+ mock_journal->close(&ctx);
+ return ctx.wait();
+ }
+
+ uint64_t when_append_write_event(MockJournalImageCtx &mock_image_ctx,
+ MockJournal *mock_journal, uint64_t length) {
+ bufferlist bl;
+ bl.append_zero(length);
+
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ return mock_journal->append_write_event(0, length, bl, false);
+ }
+
+ uint64_t when_append_compare_and_write_event(
+ MockJournalImageCtx &mock_image_ctx, MockJournal *mock_journal,
+ uint64_t length) {
+ bufferlist cmp_bl;
+ cmp_bl.append_zero(length);
+ bufferlist write_bl;
+ write_bl.append_zero(length);
+
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ return mock_journal->append_compare_and_write_event(0, length, cmp_bl,
+ write_bl, false);
+ }
+
+ uint64_t when_append_io_event(MockJournalImageCtx &mock_image_ctx,
+ MockJournal *mock_journal,
+ int filter_ret_val) {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ return mock_journal->append_io_event(
+ journal::EventEntry{journal::AioFlushEvent{}}, 0, 0, false,
+ filter_ret_val);
+ }
+
+ void save_commit_context(Context *ctx) {
+ std::lock_guard locker{m_lock};
+ m_commit_contexts.push_back(ctx);
+ m_cond.notify_all();
+ }
+
+ void wake_up() {
+ std::lock_guard locker{m_lock};
+ m_cond.notify_all();
+ }
+
+ void commit_replay(MockJournalImageCtx &mock_image_ctx, Context *on_flush,
+ int r) {
+ Contexts commit_contexts;
+ std::swap(commit_contexts, m_commit_contexts);
+
+ derr << "SHUT DOWN REPLAY START" << dendl;
+ for (auto ctx : commit_contexts) {
+ mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
+ }
+
+ on_flush = new LambdaContext([on_flush](int r) {
+ derr << "FLUSH START" << dendl;
+ on_flush->complete(r);
+ derr << "FLUSH FINISH" << dendl;
+ });
+ mock_image_ctx.image_ctx->op_work_queue->queue(on_flush, 0);
+ derr << "SHUT DOWN REPLAY FINISH" << dendl;
+ }
+
+ void open_journal(MockJournalImageCtx &mock_image_ctx,
+ MockJournal *mock_journal,
+ MockObjectDispatch& mock_object_dispatch,
+ ::journal::MockJournaler &mock_journaler,
+ MockJournalOpenRequest &mock_open_request,
+ bool primary = true) {
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ primary, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_complete, _1, 0));
+
+ MockJournalReplay mock_journal_replay;
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_committed(mock_journaler, 0);
+ expect_flush_commit_position(mock_journaler);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+ ASSERT_EQ(0, when_open(mock_journal));
+ }
+
+ void close_journal(MockJournalImageCtx& mock_image_ctx,
+ MockJournal *mock_journal,
+ ::journal::MockJournaler &mock_journaler) {
+ expect_stop_append(mock_journaler, 0);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(0, when_close(mock_journal));
+ }
+
+ static void invoke_replay_ready(::journal::ReplayHandler *handler) {
+ handler->handle_entries_available();
+ }
+
+ static void invoke_replay_complete(::journal::ReplayHandler *handler, int r) {
+ handler->handle_complete(r);
+ }
+
+ ::journal::ReplayHandler *m_replay_handler = nullptr;
+ ::journal::JournalMetadataListener *m_listener = nullptr;
+};
+
+TEST_F(TestMockJournal, StateTransitions) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_ready, _1));
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ MockJournalReplay mock_journal_replay;
+ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
+ expect_replay_process(mock_journal_replay);
+ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
+ expect_replay_process(mock_journal_replay);
+ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
+ std::bind(&invoke_replay_ready, _1));
+ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
+ expect_replay_process(mock_journal_replay);
+ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
+ std::bind(&invoke_replay_complete, _1, 0));
+
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_committed(mock_journaler, 3);
+ expect_flush_commit_position(mock_journaler);
+
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+
+ ASSERT_EQ(0, when_open(mock_journal));
+
+ expect_stop_append(mock_journaler, 0);
+ expect_shut_down_journaler(mock_journaler);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(0, when_close(mock_journal));
+}
+
+TEST_F(TestMockJournal, InitError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, -EINVAL);
+ expect_shut_down_journaler(mock_journaler);
+ ASSERT_EQ(-EINVAL, when_open(mock_journal));
+}
+
+TEST_F(TestMockJournal, ReplayCompleteError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_complete, _1, -EINVAL));
+
+ MockJournalReplay mock_journal_replay;
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
+ expect_flush_commit_position(mock_journaler);
+ expect_shut_down_journaler(mock_journaler);
+
+ // replay failure should result in replay-restart
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_complete, _1, 0));
+
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_flush_commit_position(mock_journaler);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+ ASSERT_EQ(0, when_open(mock_journal));
+
+ expect_stop_append(mock_journaler, 0);
+ expect_shut_down_journaler(mock_journaler);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(0, when_close(mock_journal));
+}
+
+TEST_F(TestMockJournal, FlushReplayError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_ready, _1));
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ MockJournalReplay mock_journal_replay;
+ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
+ expect_replay_process(mock_journal_replay);
+ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
+ std::bind(&invoke_replay_complete, _1, 0));
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, -EINVAL);
+ expect_flush_commit_position(mock_journaler);
+ expect_shut_down_journaler(mock_journaler);
+
+ // replay flush failure should result in replay-restart
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_complete, _1, 0));
+
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_flush_commit_position(mock_journaler);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+ ASSERT_EQ(0, when_open(mock_journal));
+
+ expect_stop_append(mock_journaler, 0);
+ expect_shut_down_journaler(mock_journaler);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(0, when_close(mock_journal));
+}
+
+TEST_F(TestMockJournal, CorruptEntry) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_ready, _1));
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ MockJournalReplay mock_journal_replay;
+ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
+ EXPECT_CALL(mock_journal_replay, decode(_, _)).WillOnce(Return(-EBADMSG));
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
+ expect_flush_commit_position(mock_journaler);
+ expect_shut_down_journaler(mock_journaler);
+
+ // replay failure should result in replay-restart
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_complete, _1, 0));
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_flush_commit_position(mock_journaler);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+ ASSERT_EQ(0, when_open(mock_journal));
+
+ expect_stop_append(mock_journaler, -EINVAL);
+ expect_shut_down_journaler(mock_journaler);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_close(mock_journal));
+}
+
+TEST_F(TestMockJournal, StopError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_complete, _1, 0));
+
+ MockJournalReplay mock_journal_replay;
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_flush_commit_position(mock_journaler);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+ ASSERT_EQ(0, when_open(mock_journal));
+
+ expect_stop_append(mock_journaler, -EINVAL);
+ expect_shut_down_journaler(mock_journaler);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(-EINVAL, when_close(mock_journal));
+}
+
+TEST_F(TestMockJournal, ReplayOnDiskPreFlushError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_ready, _1));
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ MockJournalReplay mock_journal_replay;
+ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
+
+ EXPECT_CALL(mock_journal_replay, decode(_, _))
+ .WillOnce(Return(0));
+ Context *on_ready;
+ EXPECT_CALL(mock_journal_replay, process(_, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&on_ready),
+ WithArg<2>(Invoke(this, &TestMockJournal::save_commit_context))));
+
+ expect_try_pop_front(mock_image_ctx, mock_journaler, false,
+ mock_replay_entry);
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
+ expect_flush_commit_position(mock_journaler);
+ expect_shut_down_journaler(mock_journaler);
+
+ // replay write-to-disk failure should result in replay-restart
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler, {
+ std::bind(&invoke_replay_complete, _1, 0)
+ });
+
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_flush_commit_position(mock_journaler);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+
+ C_SaferCond ctx;
+ mock_journal->open(&ctx);
+
+ // wait for the process callback
+ {
+ std::unique_lock locker{m_lock};
+ m_cond.wait(locker, [this] { return !m_commit_contexts.empty(); });
+ }
+ on_ready->complete(0);
+
+ // inject RADOS error in the middle of replay
+ Context *on_safe = m_commit_contexts.front();
+ m_commit_contexts.clear();
+ on_safe->complete(-EINVAL);
+
+ // flag the replay as complete
+ m_replay_handler->handle_complete(0);
+
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_stop_append(mock_journaler, 0);
+ expect_shut_down_journaler(mock_journaler);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(0, when_close(mock_journal));
+}
+
+TEST_F(TestMockJournal, ReplayOnDiskPostFlushError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ InSequence seq;
+
+ MockObjectDispatch mock_object_dispatch;
+ expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_ready, _1));
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ MockJournalReplay mock_journal_replay;
+ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
+ expect_replay_process(mock_journal_replay);
+ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
+ std::bind(&invoke_replay_complete, _1, 0));
+ expect_stop_replay(mock_journaler);
+
+ Context *on_flush = nullptr;
+ EXPECT_CALL(mock_journal_replay, shut_down(false, _))
+ .WillOnce(DoAll(SaveArg<1>(&on_flush),
+ InvokeWithoutArgs(this, &TestMockJournal::wake_up)));
+ expect_flush_commit_position(mock_journaler);
+
+ // replay write-to-disk failure should result in replay-restart
+ expect_shut_down_journaler(mock_journaler);
+ expect_construct_journaler(mock_journaler);
+ expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
+ true, 0);
+ expect_get_max_append_size(mock_journaler, 1 << 16);
+ expect_start_replay(
+ mock_image_ctx, mock_journaler,
+ std::bind(&invoke_replay_complete, _1, 0));
+
+ expect_stop_replay(mock_journaler);
+ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
+ expect_flush_commit_position(mock_journaler);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+
+ C_SaferCond ctx;
+ mock_journal->open(&ctx);
+
+ // proceed with the flush
+ {
+ // wait for on_flush callback
+ std::unique_lock locker{m_lock};
+ m_cond.wait(locker, [&] {return on_flush != nullptr;});
+ }
+
+ {
+ // wait for the on_safe process callback
+ std::unique_lock locker{m_lock};
+ m_cond.wait(locker, [this] {return !m_commit_contexts.empty();});
+ }
+ m_commit_contexts.front()->complete(-EINVAL);
+ m_commit_contexts.clear();
+ on_flush->complete(0);
+
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_stop_append(mock_journaler, 0);
+ expect_shut_down_journaler(mock_journaler);
+ expect_shut_down_dispatch(mock_image_ctx);
+ ASSERT_EQ(0, when_close(mock_journal));
+}
+
+TEST_F(TestMockJournal, EventAndIOCommitOrder) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ ::journal::MockFuture mock_future;
+ Context *on_journal_safe1;
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe1);
+ ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
+ mock_journal->get_work_queue()->drain();
+
+ Context *on_journal_safe2;
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe2);
+ ASSERT_EQ(2U, when_append_io_event(mock_image_ctx, mock_journal, 0));
+ mock_journal->get_work_queue()->drain();
+
+ // commit journal event followed by IO event (standard)
+ on_journal_safe1->complete(0);
+ ictx->op_work_queue->drain();
+ expect_future_committed(mock_journaler);
+ mock_journal->commit_io_event(1U, 0);
+
+ // commit IO event followed by journal event (cache overwrite)
+ mock_journal->commit_io_event(2U, 0);
+ expect_future_committed(mock_journaler);
+
+ C_SaferCond event_ctx;
+ mock_journal->wait_event(2U, &event_ctx);
+ on_journal_safe2->complete(0);
+ ictx->op_work_queue->drain();
+ ASSERT_EQ(0, event_ctx.wait());
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, AppendWriteEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ InSequence seq;
+
+ ::journal::MockFuture mock_future;
+ Context *on_journal_safe = nullptr;
+ expect_append_journaler(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe);
+ ASSERT_EQ(1U, when_append_write_event(mock_image_ctx, mock_journal, 1 << 17));
+ mock_journal->get_work_queue()->drain();
+
+ on_journal_safe->complete(0);
+ C_SaferCond event_ctx;
+ mock_journal->wait_event(1U, &event_ctx);
+ ASSERT_EQ(0, event_ctx.wait());
+
+ expect_future_committed(mock_journaler);
+ expect_future_committed(mock_journaler);
+ expect_future_committed(mock_journaler);
+ mock_journal->commit_io_event(1U, 0);
+ ictx->op_work_queue->drain();
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, AppendCompareAndWriteEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ InSequence seq;
+
+ ::journal::MockFuture mock_future;
+ Context *on_journal_safe = nullptr;
+ expect_append_journaler(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe);
+ ASSERT_EQ(1U, when_append_compare_and_write_event(mock_image_ctx,
+ mock_journal,
+ 1 << 16));
+ mock_journal->get_work_queue()->drain();
+
+ on_journal_safe->complete(0);
+ C_SaferCond event_ctx;
+ mock_journal->wait_event(1U, &event_ctx);
+ ASSERT_EQ(0, event_ctx.wait());
+
+ expect_future_committed(mock_journaler);
+ expect_future_committed(mock_journaler);
+ expect_future_committed(mock_journaler);
+ mock_journal->commit_io_event(1U, 0);
+ ictx->op_work_queue->drain();
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, EventCommitError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ ::journal::MockFuture mock_future;
+ Context *on_journal_safe;
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe);
+ ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
+ mock_journal->get_work_queue()->drain();
+
+ // commit the event in the journal w/o waiting writeback
+ expect_future_committed(mock_journaler);
+ C_SaferCond object_request_ctx;
+ mock_journal->wait_event(1U, &object_request_ctx);
+ on_journal_safe->complete(-EINVAL);
+ ASSERT_EQ(-EINVAL, object_request_ctx.wait());
+
+ // cache should receive the error after attempting writeback
+ expect_future_is_valid(mock_future);
+ C_SaferCond flush_ctx;
+ mock_journal->flush_event(1U, &flush_ctx);
+ ASSERT_EQ(-EINVAL, flush_ctx.wait());
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, EventCommitErrorWithPendingWriteback) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ ::journal::MockFuture mock_future;
+ Context *on_journal_safe;
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe);
+ ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
+ mock_journal->get_work_queue()->drain();
+
+ expect_future_is_valid(mock_future);
+ C_SaferCond flush_ctx;
+ mock_journal->flush_event(1U, &flush_ctx);
+
+ // commit the event in the journal w/ waiting cache writeback
+ expect_future_committed(mock_journaler);
+ C_SaferCond object_request_ctx;
+ mock_journal->wait_event(1U, &object_request_ctx);
+ on_journal_safe->complete(-EINVAL);
+ ASSERT_EQ(-EINVAL, object_request_ctx.wait());
+
+ // cache should receive the error if waiting
+ ASSERT_EQ(-EINVAL, flush_ctx.wait());
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, IOCommitError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ ::journal::MockFuture mock_future;
+ Context *on_journal_safe;
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe);
+ ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
+ mock_journal->get_work_queue()->drain();
+
+ // failed IO remains uncommitted in journal
+ on_journal_safe->complete(0);
+ ictx->op_work_queue->drain();
+ mock_journal->commit_io_event(1U, -EINVAL);
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, IOCommitErrorFiltered) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ ::journal::MockFuture mock_future;
+ Context *on_journal_safe;
+ expect_append_journaler(mock_journaler);
+ expect_wait_future(mock_future, &on_journal_safe);
+ ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, -EILSEQ));
+ mock_journal->get_work_queue()->drain();
+
+ // filter failed IO committed in journal
+ on_journal_safe->complete(0);
+ ictx->op_work_queue->drain();
+ expect_future_committed(mock_journaler);
+ mock_journal->commit_io_event(1U, -EILSEQ);
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, FlushCommitPosition) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ expect_flush_commit_position(mock_journaler);
+ C_SaferCond ctx;
+ mock_journal->flush_commit_position(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, ExternalReplay) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ InSequence seq;
+ expect_stop_append(mock_journaler, 0);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+ expect_shut_down_journaler(mock_journaler);
+
+ C_SaferCond start_ctx;
+
+ journal::Replay<MockJournalImageCtx> *journal_replay = nullptr;
+ mock_journal->start_external_replay(&journal_replay, &start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+
+ mock_journal->stop_external_replay();
+}
+
+TEST_F(TestMockJournal, ExternalReplayFailure) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ InSequence seq;
+ expect_stop_append(mock_journaler, -EINVAL);
+ expect_start_append(mock_journaler);
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
+ expect_shut_down_journaler(mock_journaler);
+
+ C_SaferCond start_ctx;
+
+ journal::Replay<MockJournalImageCtx> *journal_replay = nullptr;
+ mock_journal->start_external_replay(&journal_replay, &start_ctx);
+ ASSERT_EQ(-EINVAL, start_ctx.wait());
+}
+
+TEST_F(TestMockJournal, AppendDisabled) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ MockJournalPolicy mock_journal_policy;
+
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ InSequence seq;
+ std::shared_lock image_locker{mock_image_ctx.image_lock};
+ EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce(
+ Return(ictx->get_journal_policy()));
+ ASSERT_TRUE(mock_journal->is_journal_appending());
+
+ EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce(
+ Return(&mock_journal_policy));
+ EXPECT_CALL(mock_journal_policy, append_disabled()).WillOnce(Return(true));
+ ASSERT_FALSE(mock_journal->is_journal_appending());
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+TEST_F(TestMockJournal, CloseListenerEvent) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+
+ BOOST_SCOPE_EXIT(&mock_journal) {
+ mock_journal->put();
+ } BOOST_SCOPE_EXIT_END
+
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+
+ struct Listener : public journal::Listener {
+ C_SaferCond ctx;
+ void handle_close() override {
+ ctx.complete(0);
+ }
+ void handle_resync() override {
+ ADD_FAILURE() << "unexpected resync request";
+ }
+ void handle_promoted() override {
+ ADD_FAILURE() << "unexpected promotion event";
+ }
+ } listener;
+ mock_journal->add_listener(&listener);
+
+ expect_shut_down_journaler(mock_journaler);
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+
+ ASSERT_EQ(0, listener.ctx.wait());
+ mock_journal->remove_listener(&listener);
+}
+
+TEST_F(TestMockJournal, ResyncRequested) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request,
+ false);
+
+ struct Listener : public journal::Listener {
+ C_SaferCond ctx;
+ void handle_close() override {
+ ADD_FAILURE() << "unexpected close action";
+ }
+ void handle_resync() override {
+ ctx.complete(0);
+ }
+ void handle_promoted() override {
+ ADD_FAILURE() << "unexpected promotion event";
+ }
+ } listener;
+ mock_journal->add_listener(&listener);
+
+ BOOST_SCOPE_EXIT_ALL(&) {
+ mock_journal->remove_listener(&listener);
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ InSequence seq;
+
+ journal::TagData tag_data;
+ tag_data.mirror_uuid = Journal<>::LOCAL_MIRROR_UUID;
+
+ bufferlist tag_data_bl;
+ encode(tag_data, tag_data_bl);
+ expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0,
+ {{0, 0, tag_data_bl}}, 0);
+
+ journal::ImageClientMeta image_client_meta;
+ image_client_meta.tag_class = 0;
+ image_client_meta.resync_requested = true;
+ expect_get_journaler_cached_client(mock_journaler, image_client_meta, 0);
+ expect_shut_down_journaler(mock_journaler);
+
+ m_listener->handle_update(nullptr);
+ ASSERT_EQ(0, listener.ctx.wait());
+}
+
+TEST_F(TestMockJournal, ForcePromoted) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request, false);
+
+ struct Listener : public journal::Listener {
+ C_SaferCond ctx;
+ void handle_close() override {
+ ADD_FAILURE() << "unexpected close action";
+ }
+ void handle_resync() override {
+ ADD_FAILURE() << "unexpected resync event";
+ }
+ void handle_promoted() override {
+ ctx.complete(0);
+ }
+ } listener;
+ mock_journal->add_listener(&listener);
+
+ BOOST_SCOPE_EXIT_ALL(&) {
+ mock_journal->remove_listener(&listener);
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ InSequence seq;
+
+ journal::TagData tag_data;
+ tag_data.mirror_uuid = Journal<>::LOCAL_MIRROR_UUID;
+
+ bufferlist tag_data_bl;
+ encode(tag_data, tag_data_bl);
+ expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0,
+ {{100, 0, tag_data_bl}}, 0);
+
+ journal::ImageClientMeta image_client_meta;
+ image_client_meta.tag_class = 0;
+ expect_get_journaler_cached_client(mock_journaler, image_client_meta, 0);
+ expect_shut_down_journaler(mock_journaler);
+
+ m_listener->handle_update(nullptr);
+ ASSERT_EQ(0, listener.ctx.wait());
+}
+
+TEST_F(TestMockJournal, UserFlushed) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockJournalImageCtx mock_image_ctx(*ictx);
+ MockJournal *mock_journal = new MockJournal(mock_image_ctx);
+ MockObjectDispatch mock_object_dispatch;
+ ::journal::MockJournaler mock_journaler;
+ MockJournalOpenRequest mock_open_request;
+ open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
+ mock_journaler, mock_open_request);
+ BOOST_SCOPE_EXIT_ALL(&) {
+ close_journal(mock_image_ctx, mock_journal, mock_journaler);
+ mock_journal->put();
+ };
+
+ expect_set_append_batch_options(mock_image_ctx, mock_journaler, true);
+ mock_journal->user_flushed();
+
+ expect_shut_down_journaler(mock_journaler);
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_mock_ManagedLock.cc b/src/test/librbd/test_mock_ManagedLock.cc
new file mode 100644
index 000000000..e3d060771
--- /dev/null
+++ b/src/test/librbd/test_mock_ManagedLock.cc
@@ -0,0 +1,723 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/ManagedLock.h"
+#include "librbd/managed_lock/AcquireRequest.h"
+#include "librbd/managed_lock/BreakRequest.h"
+#include "librbd/managed_lock/GetLockerRequest.h"
+#include "librbd/managed_lock/ReacquireRequest.h"
+#include "librbd/managed_lock/ReleaseRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <list>
+
+using namespace std;
+
+namespace librbd {
+
+struct MockManagedLockImageCtx : public MockImageCtx {
+ explicit MockManagedLockImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {}
+};
+
+namespace watcher {
+template <>
+struct Traits<MockManagedLockImageCtx> {
+ typedef librbd::MockImageWatcher Watcher;
+};
+}
+
+struct MockMockManagedLock : public ManagedLock<MockManagedLockImageCtx> {
+ MockMockManagedLock(librados::IoCtx& ioctx, AsioEngine& asio_engine,
+ const std::string& oid, librbd::MockImageWatcher *watcher,
+ managed_lock::Mode mode, bool blocklist_on_break_lock,
+ uint32_t blocklist_expire_seconds)
+ : ManagedLock<MockManagedLockImageCtx>(ioctx, asio_engine, oid, watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0) {
+ };
+ virtual ~MockMockManagedLock() = default;
+
+ MOCK_METHOD2(post_reacquire_lock_handler, void(int, Context*));
+
+ MOCK_METHOD2(pre_release_lock_handler, void(bool, Context*));
+ MOCK_METHOD3(post_release_lock_handler, void(bool, int, Context*));
+};
+
+namespace managed_lock {
+
+template<typename T>
+struct BaseRequest {
+ static std::list<T *> s_requests;
+ Context *on_finish = nullptr;
+
+ static T* create(librados::IoCtx& ioctx, MockImageWatcher *watcher,
+ const std::string& oid, const std::string& cookie,
+ Context *on_finish) {
+ ceph_assert(!s_requests.empty());
+ T* req = s_requests.front();
+ req->on_finish = on_finish;
+ s_requests.pop_front();
+ return req;
+ }
+
+ BaseRequest() {
+ s_requests.push_back(reinterpret_cast<T*>(this));
+ }
+};
+
+template<typename T>
+std::list<T *> BaseRequest<T>::s_requests;
+
+template <>
+struct AcquireRequest<MockManagedLockImageCtx>
+ : public BaseRequest<AcquireRequest<MockManagedLockImageCtx> > {
+ static AcquireRequest* create(librados::IoCtx& ioctx,
+ MockImageWatcher *watcher,
+ AsioEngine& asio_engine,
+ const std::string& oid,
+ const std::string& cookie,
+ bool exclusive, bool blocklist_on_break_lock,
+ uint32_t blocklist_expire_seconds,
+ Context *on_finish) {
+ return BaseRequest::create(ioctx, watcher, oid, cookie, on_finish);
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct ReacquireRequest<MockManagedLockImageCtx> : public BaseRequest<ReacquireRequest<MockManagedLockImageCtx> > {
+ static ReacquireRequest* create(librados::IoCtx &ioctx, const std::string& oid,
+ const string& old_cookie, const std::string& new_cookie,
+ bool exclusive, Context *on_finish) {
+ return BaseRequest::create(ioctx, nullptr, oid, new_cookie,
+ on_finish);
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct ReleaseRequest<MockManagedLockImageCtx> : public BaseRequest<ReleaseRequest<MockManagedLockImageCtx> > {
+ static ReleaseRequest* create(librados::IoCtx& ioctx, MockImageWatcher *watcher,
+ asio::ContextWQ *work_queue,
+ const std::string& oid,
+ const std::string& cookie, Context *on_finish) {
+ return BaseRequest::create(ioctx, watcher, oid, cookie, on_finish);
+ }
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct GetLockerRequest<MockManagedLockImageCtx> {
+ static GetLockerRequest* create(librados::IoCtx& ioctx,
+ const std::string& oid, bool exclusive,
+ Locker *locker, Context *on_finish) {
+ ceph_abort_msg("unexpected call");
+ }
+
+ void send() {
+ ceph_abort_msg("unexpected call");
+ }
+};
+
+template <>
+struct BreakRequest<MockManagedLockImageCtx> {
+ static BreakRequest* create(librados::IoCtx& ioctx,
+ AsioEngine& asio_engine,
+ const std::string& oid, const Locker &locker,
+ bool exclusive, bool blocklist_locker,
+ uint32_t blocklist_expire_seconds,
+ bool force_break_lock, Context *on_finish) {
+ ceph_abort_msg("unexpected call");
+ }
+
+ void send() {
+ ceph_abort_msg("unexpected call");
+ }
+};
+
+} // namespace managed_lock
+} // namespace librbd
+
+// template definitions
+#include "librbd/ManagedLock.cc"
+template class librbd::ManagedLock<librbd::MockManagedLockImageCtx>;
+
+
+ACTION_P3(QueueRequest, request, r, wq) {
+ if (request->on_finish != nullptr) {
+ if (wq != nullptr) {
+ wq->queue(request->on_finish, r);
+ } else {
+ request->on_finish->complete(r);
+ }
+ }
+}
+
+ACTION_P2(QueueContext, r, wq) {
+ wq->queue(arg0, r);
+}
+
+ACTION_P(Notify, ctx) {
+ ctx->complete(0);
+}
+
+namespace librbd {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockManagedLock : public TestMockFixture {
+public:
+ typedef ManagedLock<MockManagedLockImageCtx> MockManagedLock;
+ typedef managed_lock::AcquireRequest<MockManagedLockImageCtx> MockAcquireRequest;
+ typedef managed_lock::ReacquireRequest<MockManagedLockImageCtx> MockReacquireRequest;
+ typedef managed_lock::ReleaseRequest<MockManagedLockImageCtx> MockReleaseRequest;
+
+ void expect_get_watch_handle(MockImageWatcher &mock_watcher,
+ uint64_t watch_handle = 1234567890) {
+ EXPECT_CALL(mock_watcher, get_watch_handle())
+ .WillOnce(Return(watch_handle));
+ }
+
+ void expect_acquire_lock(MockImageWatcher &watcher,
+ asio::ContextWQ *work_queue,
+ MockAcquireRequest &acquire_request, int r) {
+ expect_get_watch_handle(watcher);
+ EXPECT_CALL(acquire_request, send())
+ .WillOnce(QueueRequest(&acquire_request, r, work_queue));
+ }
+
+ void expect_is_blocklisted(MockImageWatcher &watcher,
+ bool blocklisted) {
+ EXPECT_CALL(watcher, is_blocklisted()).WillOnce(Return(blocklisted));
+ }
+
+ void expect_release_lock(asio::ContextWQ *work_queue,
+ MockReleaseRequest &release_request, int r) {
+ EXPECT_CALL(release_request, send())
+ .WillOnce(QueueRequest(&release_request, r, work_queue));
+ }
+
+ void expect_reacquire_lock(MockImageWatcher& watcher,
+ asio::ContextWQ *work_queue,
+ MockReacquireRequest &mock_reacquire_request,
+ int r) {
+ EXPECT_CALL(mock_reacquire_request, send())
+ .WillOnce(QueueRequest(&mock_reacquire_request, r, work_queue));
+ }
+
+ void expect_flush_notifies(MockImageWatcher *mock_watcher) {
+ EXPECT_CALL(*mock_watcher, flush(_))
+ .WillOnce(CompleteContext(0, (asio::ContextWQ *)nullptr));
+ }
+
+ void expect_post_reacquired_lock_handler(MockImageWatcher& watcher,
+ MockMockManagedLock &managed_lock, uint64_t &client_id) {
+ expect_get_watch_handle(watcher);
+ EXPECT_CALL(managed_lock, post_reacquire_lock_handler(_, _))
+ .WillOnce(Invoke([&client_id](int r, Context *on_finish){
+ if (r >= 0) {
+ client_id = 98765;
+ }
+ on_finish->complete(r);}));
+ }
+
+ void expect_pre_release_lock_handler(MockMockManagedLock &managed_lock,
+ bool shutting_down, int r) {
+ EXPECT_CALL(managed_lock, pre_release_lock_handler(shutting_down, _))
+ .WillOnce(WithArg<1>(Invoke([r](Context *on_finish){
+ on_finish->complete(r);
+ })));
+ }
+
+ void expect_post_release_lock_handler(MockMockManagedLock &managed_lock,
+ bool shutting_down, int expect_r,
+ int r) {
+ EXPECT_CALL(managed_lock, post_release_lock_handler(shutting_down, expect_r, _))
+ .WillOnce(WithArg<2>(Invoke([r](Context *on_finish){
+ on_finish->complete(r);
+ })));
+ }
+
+ int when_acquire_lock(MockManagedLock &managed_lock) {
+ C_SaferCond ctx;
+ {
+ managed_lock.acquire_lock(&ctx);
+ }
+ return ctx.wait();
+ }
+ int when_release_lock(MockManagedLock &managed_lock) {
+ C_SaferCond ctx;
+ {
+ managed_lock.release_lock(&ctx);
+ }
+ return ctx.wait();
+ }
+ int when_shut_down(MockManagedLock &managed_lock) {
+ C_SaferCond ctx;
+ {
+ managed_lock.shut_down(&ctx);
+ }
+ return ctx.wait();
+ }
+
+ bool is_lock_owner(MockManagedLock &managed_lock) {
+ return managed_lock.is_lock_owner();
+ }
+};
+
+TEST_F(TestMockManagedLock, StateTransitions) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest request_lock_acquire1;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire1, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ MockReleaseRequest request_release;
+ expect_release_lock(ictx->op_work_queue, request_release, 0);
+ ASSERT_EQ(0, when_release_lock(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+
+ MockAcquireRequest request_lock_acquire2;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire2, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ MockReleaseRequest shutdown_release;
+ expect_release_lock(ictx->op_work_queue, shutdown_release, 0);
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, AcquireLockLockedState) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest try_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+
+ MockReleaseRequest shutdown_release;
+ expect_release_lock(ictx->op_work_queue, shutdown_release, 0);
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, AcquireLockAlreadyLocked) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest try_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, -EAGAIN);
+ ASSERT_EQ(-EAGAIN, when_acquire_lock(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, AcquireLockBusy) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest try_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, -EBUSY);
+ ASSERT_EQ(-EBUSY, when_acquire_lock(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, AcquireLockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest try_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, -EINVAL);
+
+ ASSERT_EQ(-EINVAL, when_acquire_lock(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, AcquireLockBlocklist) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ // will abort after seeing blocklist error (avoid infinite request loop)
+ MockAcquireRequest request_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire, -EBLOCKLISTED);
+ ASSERT_EQ(-EBLOCKLISTED, when_acquire_lock(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, AcquireLockBlocklistedWatch) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+
+ InSequence seq;
+
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 0);
+ expect_is_blocklisted(*mock_image_ctx.image_watcher, true);
+
+ ASSERT_EQ(-EBLOCKLISTED, when_acquire_lock(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ReleaseLockUnlockedState) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ ASSERT_EQ(0, when_release_lock(managed_lock));
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ReleaseLockBlocklist) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockMockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest try_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+
+ expect_pre_release_lock_handler(managed_lock, false, -EBLOCKLISTED);
+ expect_post_release_lock_handler(managed_lock, false, -EBLOCKLISTED, -EBLOCKLISTED);
+ ASSERT_EQ(-EBLOCKLISTED, when_release_lock(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ReleaseLockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest try_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+
+ MockReleaseRequest release;
+ expect_release_lock(ictx->op_work_queue, release, -EINVAL);
+
+ ASSERT_EQ(-EINVAL, when_release_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ MockReleaseRequest shutdown_release;
+ expect_release_lock(ictx->op_work_queue, shutdown_release, 0);
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ConcurrentRequests) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ expect_get_watch_handle(*mock_image_ctx.image_watcher);
+
+ C_SaferCond wait_for_send_ctx1;
+ MockAcquireRequest acquire_error;
+ EXPECT_CALL(acquire_error, send())
+ .WillOnce(Notify(&wait_for_send_ctx1));
+
+ MockAcquireRequest request_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_acquire, 0);
+
+ MockReleaseRequest release;
+ C_SaferCond wait_for_send_ctx2;
+ EXPECT_CALL(release, send())
+ .WillOnce(Notify(&wait_for_send_ctx2));
+
+ C_SaferCond acquire_request_ctx1;
+ managed_lock.acquire_lock(&acquire_request_ctx1);
+
+ C_SaferCond acquire_lock_ctx1;
+ C_SaferCond acquire_lock_ctx2;
+ managed_lock.acquire_lock(&acquire_lock_ctx1);
+ managed_lock.acquire_lock(&acquire_lock_ctx2);
+
+ // fail the try_lock
+ ASSERT_EQ(0, wait_for_send_ctx1.wait());
+ acquire_error.on_finish->complete(-EINVAL);
+ ASSERT_EQ(-EINVAL, acquire_request_ctx1.wait());
+
+ C_SaferCond acquire_lock_ctx3;
+ managed_lock.acquire_lock(&acquire_lock_ctx3);
+
+ C_SaferCond release_lock_ctx1;
+ managed_lock.release_lock(&release_lock_ctx1);
+
+ // all three pending request locks should complete
+ ASSERT_EQ(-EINVAL, acquire_lock_ctx1.wait());
+ ASSERT_EQ(-EINVAL, acquire_lock_ctx2.wait());
+ ASSERT_EQ(0, acquire_lock_ctx3.wait());
+
+ // proceed with the release
+ ASSERT_EQ(0, wait_for_send_ctx2.wait());
+ release.on_finish->complete(0);
+ ASSERT_EQ(0, release_lock_ctx1.wait());
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ReacquireLock) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest request_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ MockReacquireRequest mock_reacquire_request;
+ C_SaferCond reacquire_ctx;
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 98765);
+ expect_reacquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, mock_reacquire_request, 0);
+ managed_lock.reacquire_lock(&reacquire_ctx);
+ ASSERT_EQ(0, reacquire_ctx.wait());
+
+ MockReleaseRequest shutdown_release;
+ expect_release_lock(ictx->op_work_queue, shutdown_release, 0);
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, AttemptReacquireBlocklistedLock) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest request_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue,
+ request_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 0);
+
+ MockReleaseRequest request_release;
+ expect_release_lock(ictx->op_work_queue, request_release, 0);
+
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 0);
+ expect_is_blocklisted(*mock_image_ctx.image_watcher, false);
+
+ managed_lock.reacquire_lock(nullptr);
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ReacquireBlocklistedLock) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest request_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue,
+ request_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 0);
+
+ MockReleaseRequest request_release;
+ expect_release_lock(ictx->op_work_queue, request_release, 0);
+
+ MockAcquireRequest request_lock_reacquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue,
+ request_lock_reacquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ C_SaferCond reacquire_ctx;
+ managed_lock.reacquire_lock(&reacquire_ctx);
+ ASSERT_EQ(0, reacquire_ctx.wait());
+
+ MockReleaseRequest shutdown_release;
+ expect_release_lock(ictx->op_work_queue, shutdown_release, 0);
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ReacquireLockError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest request_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ MockReacquireRequest mock_reacquire_request;
+ C_SaferCond reacquire_ctx;
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 98765);
+ expect_reacquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, mock_reacquire_request, -EOPNOTSUPP);
+
+ MockReleaseRequest reacquire_lock_release;
+ expect_release_lock(ictx->op_work_queue, reacquire_lock_release, 0);
+
+ MockAcquireRequest reacquire_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, reacquire_lock_acquire, 0);
+
+ managed_lock.reacquire_lock(&reacquire_ctx);
+ ASSERT_EQ(0, reacquire_ctx.wait());
+
+ MockReleaseRequest shutdown_release;
+ expect_release_lock(ictx->op_work_queue, shutdown_release, 0);
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ReacquireWithSameCookie) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockMockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+ InSequence seq;
+
+ MockAcquireRequest request_lock_acquire;
+ expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire, 0);
+ ASSERT_EQ(0, when_acquire_lock(managed_lock));
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ // watcher with same cookie after rewatch
+ uint64_t client_id = 0;
+ C_SaferCond reacquire_ctx;
+ expect_post_reacquired_lock_handler(*mock_image_ctx.image_watcher, managed_lock, client_id);
+ managed_lock.reacquire_lock(&reacquire_ctx);
+ ASSERT_LT(0U, client_id);
+ ASSERT_TRUE(is_lock_owner(managed_lock));
+
+ MockReleaseRequest shutdown_release;
+ expect_pre_release_lock_handler(managed_lock, true, 0);
+ expect_release_lock(ictx->op_work_queue, shutdown_release, 0);
+ expect_post_release_lock_handler(managed_lock, true, 0, 0);
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+}
+
+TEST_F(TestMockManagedLock, ShutDownWhileWaiting) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockManagedLockImageCtx mock_image_ctx(*ictx);
+ MockMockManagedLock managed_lock(ictx->md_ctx, *ictx->asio_engine,
+ ictx->header_oid, mock_image_ctx.image_watcher,
+ librbd::managed_lock::EXCLUSIVE, true, 0);
+
+ InSequence seq;
+
+ expect_get_watch_handle(*mock_image_ctx.image_watcher, 0);
+ expect_is_blocklisted(*mock_image_ctx.image_watcher, false);
+
+ C_SaferCond acquire_ctx;
+ managed_lock.acquire_lock(&acquire_ctx);
+
+ ASSERT_EQ(0, when_shut_down(managed_lock));
+ ASSERT_EQ(-ERESTART, acquire_ctx.wait());
+ ASSERT_FALSE(is_lock_owner(managed_lock));
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_mock_ObjectMap.cc b/src/test/librbd/test_mock_ObjectMap.cc
new file mode 100644
index 000000000..39e29172c
--- /dev/null
+++ b/src/test/librbd/test_mock_ObjectMap.cc
@@ -0,0 +1,285 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/object_map/RefreshRequest.h"
+#include "librbd/object_map/UnlockRequest.h"
+#include "librbd/object_map/UpdateRequest.h"
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace object_map {
+
+template <>
+struct RefreshRequest<MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ ceph::BitVector<2u> *object_map = nullptr;
+ static RefreshRequest *s_instance;
+ static RefreshRequest *create(MockTestImageCtx &image_ctx, ceph::shared_mutex*,
+ ceph::BitVector<2u> *object_map,
+ uint64_t snap_id, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ s_instance->object_map = object_map;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ RefreshRequest() {
+ s_instance = this;
+ }
+};
+
+template <>
+struct UnlockRequest<MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static UnlockRequest *s_instance;
+ static UnlockRequest *create(MockTestImageCtx &image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+ UnlockRequest() {
+ s_instance = this;
+ }
+};
+
+template <>
+struct UpdateRequest<MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static UpdateRequest *s_instance;
+ static UpdateRequest *create(MockTestImageCtx &image_ctx, ceph::shared_mutex*,
+ ceph::BitVector<2u> *object_map,
+ uint64_t snap_id,
+ uint64_t start_object_no, uint64_t end_object_no,
+ uint8_t new_state,
+ const boost::optional<uint8_t> &current_state,
+ const ZTracer::Trace &parent_trace,
+ bool ignore_enoent, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ s_instance->construct(snap_id, start_object_no, end_object_no, new_state,
+ current_state, ignore_enoent);
+ return s_instance;
+ }
+
+ MOCK_METHOD6(construct, void(uint64_t snap_id, uint64_t start_object_no,
+ uint64_t end_object_no, uint8_t new_state,
+ const boost::optional<uint8_t> &current_state,
+ bool ignore_enoent));
+ MOCK_METHOD0(send, void());
+ UpdateRequest() {
+ s_instance = this;
+ }
+};
+
+RefreshRequest<MockTestImageCtx> *RefreshRequest<MockTestImageCtx>::s_instance = nullptr;
+UnlockRequest<MockTestImageCtx> *UnlockRequest<MockTestImageCtx>::s_instance = nullptr;
+UpdateRequest<MockTestImageCtx> *UpdateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace object_map
+} // namespace librbd
+
+#include "librbd/ObjectMap.cc"
+
+namespace librbd {
+
+using testing::_;
+using testing::InSequence;
+using testing::Invoke;
+
+class TestMockObjectMap : public TestMockFixture {
+public:
+ typedef ObjectMap<MockTestImageCtx> MockObjectMap;
+ typedef object_map::RefreshRequest<MockTestImageCtx> MockRefreshRequest;
+ typedef object_map::UnlockRequest<MockTestImageCtx> MockUnlockRequest;
+ typedef object_map::UpdateRequest<MockTestImageCtx> MockUpdateRequest;
+
+ void expect_refresh(MockTestImageCtx &mock_image_ctx,
+ MockRefreshRequest &mock_refresh_request,
+ const ceph::BitVector<2u> &object_map, int r) {
+ EXPECT_CALL(mock_refresh_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_refresh_request, &object_map, r]() {
+ *mock_refresh_request.object_map = object_map;
+ mock_image_ctx.image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r);
+ }));
+ }
+
+ void expect_unlock(MockTestImageCtx &mock_image_ctx,
+ MockUnlockRequest &mock_unlock_request, int r) {
+ EXPECT_CALL(mock_unlock_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_unlock_request, r]() {
+ mock_image_ctx.image_ctx->op_work_queue->queue(mock_unlock_request.on_finish, r);
+ }));
+ }
+
+ void expect_update(MockTestImageCtx &mock_image_ctx,
+ MockUpdateRequest &mock_update_request,
+ uint64_t snap_id, uint64_t start_object_no,
+ uint64_t end_object_no, uint8_t new_state,
+ const boost::optional<uint8_t> &current_state,
+ bool ignore_enoent, Context **on_finish) {
+ EXPECT_CALL(mock_update_request, construct(snap_id, start_object_no,
+ end_object_no, new_state,
+ current_state, ignore_enoent))
+ .Times(1);
+ EXPECT_CALL(mock_update_request, send())
+ .WillOnce(Invoke([&mock_update_request, on_finish]() {
+ *on_finish = mock_update_request.on_finish;
+ }));
+ }
+
+};
+
+TEST_F(TestMockObjectMap, NonDetainedUpdate) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ ceph::BitVector<2u> object_map;
+ object_map.resize(4);
+ MockRefreshRequest mock_refresh_request;
+ expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
+
+ MockUpdateRequest mock_update_request;
+ Context *finish_update_1;
+ expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+ 0, 1, 1, {}, false, &finish_update_1);
+ Context *finish_update_2;
+ expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+ 1, 2, 1, {}, false, &finish_update_2);
+
+ MockUnlockRequest mock_unlock_request;
+ expect_unlock(mock_image_ctx, mock_unlock_request, 0);
+
+ MockObjectMap *mock_object_map = new MockObjectMap(mock_image_ctx, CEPH_NOSNAP);
+ BOOST_SCOPE_EXIT(&mock_object_map) {
+ mock_object_map->put();
+ } BOOST_SCOPE_EXIT_END
+
+ C_SaferCond open_ctx;
+ mock_object_map->open(&open_ctx);
+ ASSERT_EQ(0, open_ctx.wait());
+
+ C_SaferCond update_ctx1;
+ C_SaferCond update_ctx2;
+ {
+ std::shared_lock image_locker{mock_image_ctx.image_lock};
+ mock_object_map->aio_update(CEPH_NOSNAP, 0, 1, {}, {}, false, &update_ctx1);
+ mock_object_map->aio_update(CEPH_NOSNAP, 1, 1, {}, {}, false, &update_ctx2);
+ }
+
+ finish_update_2->complete(0);
+ ASSERT_EQ(0, update_ctx2.wait());
+
+ finish_update_1->complete(0);
+ ASSERT_EQ(0, update_ctx1.wait());
+
+ C_SaferCond close_ctx;
+ mock_object_map->close(&close_ctx);
+ ASSERT_EQ(0, close_ctx.wait());
+}
+
+TEST_F(TestMockObjectMap, DetainedUpdate) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ ceph::BitVector<2u> object_map;
+ object_map.resize(4);
+ MockRefreshRequest mock_refresh_request;
+ expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
+
+ MockUpdateRequest mock_update_request;
+ Context *finish_update_1;
+ expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+ 1, 4, 1, {}, false, &finish_update_1);
+ Context *finish_update_2 = nullptr;
+ expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+ 1, 3, 1, {}, false, &finish_update_2);
+ Context *finish_update_3 = nullptr;
+ expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+ 2, 3, 1, {}, false, &finish_update_3);
+ Context *finish_update_4 = nullptr;
+ expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+ 0, 2, 1, {}, false, &finish_update_4);
+
+ MockUnlockRequest mock_unlock_request;
+ expect_unlock(mock_image_ctx, mock_unlock_request, 0);
+
+ MockObjectMap *mock_object_map = new MockObjectMap(mock_image_ctx, CEPH_NOSNAP);
+ BOOST_SCOPE_EXIT(&mock_object_map) {
+ mock_object_map->put();
+ } BOOST_SCOPE_EXIT_END
+
+ C_SaferCond open_ctx;
+ mock_object_map->open(&open_ctx);
+ ASSERT_EQ(0, open_ctx.wait());
+
+ C_SaferCond update_ctx1;
+ C_SaferCond update_ctx2;
+ C_SaferCond update_ctx3;
+ C_SaferCond update_ctx4;
+ {
+ std::shared_lock image_locker{mock_image_ctx.image_lock};
+ mock_object_map->aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, false,
+ &update_ctx1);
+ mock_object_map->aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, false,
+ &update_ctx2);
+ mock_object_map->aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, false,
+ &update_ctx3);
+ mock_object_map->aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, false,
+ &update_ctx4);
+ }
+
+ // updates 2, 3, and 4 are blocked on update 1
+ ASSERT_EQ(nullptr, finish_update_2);
+ finish_update_1->complete(0);
+ ASSERT_EQ(0, update_ctx1.wait());
+
+ // updates 3 and 4 are blocked on update 2
+ ASSERT_NE(nullptr, finish_update_2);
+ ASSERT_EQ(nullptr, finish_update_3);
+ ASSERT_EQ(nullptr, finish_update_4);
+ finish_update_2->complete(0);
+ ASSERT_EQ(0, update_ctx2.wait());
+
+ ASSERT_NE(nullptr, finish_update_3);
+ ASSERT_NE(nullptr, finish_update_4);
+ finish_update_3->complete(0);
+ finish_update_4->complete(0);
+ ASSERT_EQ(0, update_ctx3.wait());
+ ASSERT_EQ(0, update_ctx4.wait());
+
+ C_SaferCond close_ctx;
+ mock_object_map->close(&close_ctx);
+ ASSERT_EQ(0, close_ctx.wait());
+}
+
+} // namespace librbd
+
diff --git a/src/test/librbd/test_mock_TrashWatcher.cc b/src/test/librbd/test_mock_TrashWatcher.cc
new file mode 100644
index 000000000..d7f3c679e
--- /dev/null
+++ b/src/test/librbd/test_mock_TrashWatcher.cc
@@ -0,0 +1,95 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "librbd/TrashWatcher.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include <list>
+
+namespace librbd {
+namespace {
+
+struct MockTrashWatcher : public TrashWatcher<> {
+ MockTrashWatcher(ImageCtx &image_ctx)
+ : TrashWatcher<>(image_ctx.md_ctx, image_ctx.op_work_queue) {
+ }
+
+ MOCK_METHOD2(handle_image_added, void(const std::string&,
+ const cls::rbd::TrashImageSpec&));
+ MOCK_METHOD1(handle_image_removed, void(const std::string&));
+};
+
+} // anonymous namespace
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::StrEq;
+
+class TestTrashWatcher : public TestMockFixture {
+public:
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ bufferlist bl;
+ ASSERT_EQ(0, m_ioctx.write_full(RBD_TRASH, bl));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ m_trash_watcher = new MockTrashWatcher(*ictx);
+
+ C_SaferCond ctx;
+ m_trash_watcher->register_watch(&ctx);
+ if (ctx.wait() != 0) {
+ delete m_trash_watcher;
+ m_trash_watcher = nullptr;
+ FAIL();
+ }
+ }
+
+ void TearDown() override {
+ if (m_trash_watcher != nullptr) {
+ C_SaferCond ctx;
+ m_trash_watcher->unregister_watch(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ delete m_trash_watcher;
+ }
+
+ TestFixture::TearDown();
+ }
+
+ MockTrashWatcher *m_trash_watcher = nullptr;
+};
+
+TEST_F(TestTrashWatcher, ImageAdded) {
+ REQUIRE_FORMAT_V2();
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name",
+ ceph_clock_now(), ceph_clock_now()};
+
+ EXPECT_CALL(*m_trash_watcher, handle_image_added(StrEq("image id"),
+ trash_image_spec))
+ .Times(AtLeast(1));
+
+ C_SaferCond ctx;
+ MockTrashWatcher::notify_image_added(m_ioctx, "image id", trash_image_spec,
+ &ctx);
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestTrashWatcher, ImageRemoved) {
+ REQUIRE_FORMAT_V2();
+
+ EXPECT_CALL(*m_trash_watcher, handle_image_removed(StrEq("image id")))
+ .Times(AtLeast(1));
+
+ C_SaferCond ctx;
+ MockTrashWatcher::notify_image_removed(m_ioctx, "image id", &ctx);
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_mock_Watcher.cc b/src/test/librbd/test_mock_Watcher.cc
new file mode 100644
index 000000000..54b395b1f
--- /dev/null
+++ b/src/test/librbd/test_mock_Watcher.cc
@@ -0,0 +1,405 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "common/Cond.h"
+#include "common/ceph_mutex.h"
+#include "librados/AioCompletionImpl.h"
+#include "librbd/Watcher.h"
+#include "librbd/watcher/RewatchRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <list>
+
+namespace librbd {
+
+namespace {
+
+struct MockWatcher : public Watcher {
+ std::string oid;
+
+ MockWatcher(librados::IoCtx& ioctx, asio::ContextWQ *work_queue,
+ const std::string& oid)
+ : Watcher(ioctx, work_queue, oid) {
+ }
+
+ virtual void handle_notify(uint64_t notify_id, uint64_t handle,
+ uint64_t notifier_id, bufferlist &bl) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+namespace librbd {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class TestMockWatcher : public TestMockFixture {
+public:
+ TestMockWatcher() = default;
+
+ virtual void SetUp() {
+ TestMockFixture::SetUp();
+
+ m_oid = get_temp_image_name();
+
+ bufferlist bl;
+ ASSERT_EQ(0, m_ioctx.write_full(m_oid, bl));
+ }
+
+ void expect_aio_watch(MockImageCtx &mock_image_ctx, int r,
+ const std::function<void()> &action = std::function<void()>()) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+ librados::MockTestMemRadosClient *mock_rados_client(
+ mock_io_ctx.get_mock_rados_client());
+
+ EXPECT_CALL(mock_io_ctx, aio_watch(m_oid, _, _, _))
+ .WillOnce(DoAll(WithArgs<1, 2, 3>(Invoke([this, &mock_image_ctx, mock_rados_client, r, action](
+ librados::AioCompletionImpl *c, uint64_t *cookie,
+ librados::WatchCtx2 *watch_ctx) {
+ if (r == 0) {
+ *cookie = 234U;
+ m_watch_ctx = watch_ctx;
+ }
+
+ c->get();
+ mock_image_ctx.image_ctx->op_work_queue->queue(new LambdaContext([mock_rados_client, action, c](int r) {
+ if (action) {
+ action();
+ }
+
+ mock_rados_client->finish_aio_completion(c, r);
+ }), r);
+ notify_watch();
+ })), Return(0)));
+ }
+
+ void expect_aio_unwatch(MockImageCtx &mock_image_ctx, int r,
+ const std::function<void()> &action = std::function<void()>()) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx));
+ librados::MockTestMemRadosClient *mock_rados_client(
+ mock_io_ctx.get_mock_rados_client());
+
+ EXPECT_CALL(mock_io_ctx, aio_unwatch(_, _))
+ .WillOnce(DoAll(Invoke([this, &mock_image_ctx, mock_rados_client, r, action](
+ uint64_t handle, librados::AioCompletionImpl *c) {
+ c->get();
+ mock_image_ctx.image_ctx->op_work_queue->queue(new LambdaContext([mock_rados_client, action, c](int r) {
+ if (action) {
+ action();
+ }
+
+ mock_rados_client->finish_aio_completion(c, r);
+ }), r);
+ notify_watch();
+ }), Return(0)));
+ }
+
+ std::string m_oid;
+ librados::WatchCtx2 *m_watch_ctx = nullptr;
+
+ void notify_watch() {
+ std::lock_guard locker{m_lock};
+ ++m_watch_count;
+ m_cond.notify_all();
+ }
+
+ bool wait_for_watch(MockImageCtx &mock_image_ctx, size_t count) {
+ using namespace std::chrono_literals;
+ std::unique_lock locker{m_lock};
+ while (m_watch_count < count) {
+ if (m_cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ m_watch_count -= count;
+ return true;
+ }
+
+ ceph::mutex m_lock = ceph::make_mutex("TestMockWatcher::m_lock");
+ ceph::condition_variable m_cond;
+ size_t m_watch_count = 0;
+};
+
+TEST_F(TestMockWatcher, Success) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ C_SaferCond unregister_ctx;
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, RegisterError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, -EINVAL);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(-EINVAL, register_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, UnregisterError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, -EINVAL);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ C_SaferCond unregister_ctx;
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ ASSERT_EQ(-EINVAL, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, Reregister) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ ceph_assert(m_watch_ctx != nullptr);
+ m_watch_ctx->handle_error(0, -ESHUTDOWN);
+
+ // wait for recovery unwatch/watch
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 3));
+
+ C_SaferCond unregister_ctx;
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, ReregisterUnwatchError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, -EINVAL);
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ ceph_assert(m_watch_ctx != nullptr);
+ m_watch_ctx->handle_error(0, -ESHUTDOWN);
+
+ // wait for recovery unwatch/watch
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 3));
+
+ C_SaferCond unregister_ctx;
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, ReregisterWatchError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+ expect_aio_watch(mock_image_ctx, -EPERM);
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ ceph_assert(m_watch_ctx != nullptr);
+ m_watch_ctx->handle_error(0, -ESHUTDOWN);
+
+ // wait for recovery unwatch/watch
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 4));
+
+ C_SaferCond unregister_ctx;
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, ReregisterWatchBlocklist) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+ expect_aio_watch(mock_image_ctx, -EBLOCKLISTED);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 1));
+ ASSERT_EQ(0, register_ctx.wait());
+
+ ceph_assert(m_watch_ctx != nullptr);
+ m_watch_ctx->handle_error(0, -EBLOCKLISTED);
+
+ // wait for recovery unwatch/watch
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 2));
+ ASSERT_TRUE(mock_image_watcher.is_blocklisted());
+
+ C_SaferCond unregister_ctx;
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, ReregisterUnwatchPendingUnregister) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+
+ // inject an unregister
+ C_SaferCond unregister_ctx;
+ expect_aio_unwatch(mock_image_ctx, -EBLOCKLISTED,
+ [&mock_image_watcher, &unregister_ctx]() {
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ });
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ ceph_assert(m_watch_ctx != nullptr);
+ m_watch_ctx->handle_error(0, -EBLOCKLISTED);
+
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, ReregisterWatchPendingUnregister) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+
+ // inject an unregister
+ C_SaferCond unregister_ctx;
+ expect_aio_watch(mock_image_ctx, -ESHUTDOWN,
+ [&mock_image_watcher, &unregister_ctx]() {
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ });
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ ceph_assert(m_watch_ctx != nullptr);
+ m_watch_ctx->handle_error(0, -ESHUTDOWN);
+
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+TEST_F(TestMockWatcher, ReregisterPendingUnregister) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+ expect_aio_unwatch(mock_image_ctx, 0);
+
+ // inject an unregister
+ C_SaferCond unregister_ctx;
+ expect_aio_watch(mock_image_ctx, 0,
+ [&mock_image_watcher, &unregister_ctx]() {
+ mock_image_watcher.unregister_watch(&unregister_ctx);
+ });
+
+ expect_aio_unwatch(mock_image_ctx, 0);
+
+ C_SaferCond register_ctx;
+ mock_image_watcher.register_watch(&register_ctx);
+ ASSERT_EQ(0, register_ctx.wait());
+
+ ceph_assert(m_watch_ctx != nullptr);
+ m_watch_ctx->handle_error(0, -ESHUTDOWN);
+
+ ASSERT_EQ(0, unregister_ctx.wait());
+}
+
+} // namespace librbd
diff --git a/src/test/librbd/test_mock_fixture.cc b/src/test/librbd/test_mock_fixture.cc
new file mode 100644
index 000000000..d2a6837c6
--- /dev/null
+++ b/src/test/librbd/test_mock_fixture.cc
@@ -0,0 +1,134 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "test/librados_test_stub/MockTestMemCluster.h"
+
+// template definitions
+#include "librbd/AsyncRequest.cc"
+#include "librbd/AsyncObjectThrottle.cc"
+#include "librbd/operation/Request.cc"
+
+template class librbd::AsyncRequest<librbd::MockImageCtx>;
+template class librbd::AsyncObjectThrottle<librbd::MockImageCtx>;
+template class librbd::operation::Request<librbd::MockImageCtx>;
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+TestMockFixture::TestClusterRef TestMockFixture::s_test_cluster;
+
+void TestMockFixture::SetUpTestCase() {
+ s_test_cluster = librados_test_stub::get_cluster();
+
+ // use a mock version of the in-memory cluster
+ librados_test_stub::set_cluster(boost::shared_ptr<librados::TestCluster>(
+ new ::testing::NiceMock<librados::MockTestMemCluster>()));
+ TestFixture::SetUpTestCase();
+}
+
+void TestMockFixture::TearDownTestCase() {
+ TestFixture::TearDownTestCase();
+ librados_test_stub::set_cluster(s_test_cluster);
+}
+
+void TestMockFixture::TearDown() {
+ // Mock rados client lives across tests -- reset it to initial state
+ librados::MockTestMemRadosClient *mock_rados_client =
+ get_mock_io_ctx(m_ioctx).get_mock_rados_client();
+ ASSERT_TRUE(mock_rados_client != nullptr);
+
+ ::testing::Mock::VerifyAndClear(mock_rados_client);
+ mock_rados_client->default_to_dispatch();
+ dynamic_cast<librados::MockTestMemCluster*>(
+ librados_test_stub::get_cluster().get())->default_to_dispatch();
+
+ TestFixture::TearDown();
+}
+
+void TestMockFixture::expect_unlock_exclusive_lock(librbd::ImageCtx &ictx) {
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(_, _, StrEq("lock"), StrEq("unlock"), _, _, _, _))
+ .WillRepeatedly(DoDefault());
+ if (ictx.test_features(RBD_FEATURE_DIRTY_CACHE)) {
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(ictx.header_oid, _, StrEq("rbd"), StrEq("set_features"), _, _, _, _))
+ .WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(ictx.header_oid, _, StrEq("rbd"), StrEq("metadata_set"), _, _, _, _))
+ .WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(ictx.header_oid, _, StrEq("rbd"), StrEq("metadata_remove"), _, _, _, _))
+ .WillOnce(DoDefault());
+ }
+}
+
+void TestMockFixture::expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.op_work_queue, queue(_, _))
+ .WillRepeatedly(DispatchContext(
+ mock_image_ctx.image_ctx->op_work_queue));
+}
+
+void TestMockFixture::initialize_features(librbd::ImageCtx *ictx,
+ librbd::MockImageCtx &mock_image_ctx,
+ librbd::MockExclusiveLock &mock_exclusive_lock,
+ librbd::MockJournal &mock_journal,
+ librbd::MockObjectMap &mock_object_map) {
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ mock_image_ctx.journal = &mock_journal;
+ }
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+}
+
+void TestMockFixture::expect_is_journal_appending(librbd::MockJournal &mock_journal,
+ bool appending) {
+ EXPECT_CALL(mock_journal, is_journal_appending()).WillOnce(Return(appending));
+}
+
+void TestMockFixture::expect_is_journal_replaying(librbd::MockJournal &mock_journal) {
+ EXPECT_CALL(mock_journal, is_journal_replaying()).WillOnce(Return(false));
+}
+
+void TestMockFixture::expect_is_journal_ready(librbd::MockJournal &mock_journal) {
+ EXPECT_CALL(mock_journal, is_journal_ready()).WillOnce(Return(true));
+}
+
+void TestMockFixture::expect_allocate_op_tid(librbd::MockImageCtx &mock_image_ctx) {
+ if (mock_image_ctx.journal != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.journal, allocate_op_tid())
+ .WillOnce(Return(1U));
+ }
+}
+
+void TestMockFixture::expect_append_op_event(librbd::MockImageCtx &mock_image_ctx,
+ bool can_affect_io, int r) {
+ if (mock_image_ctx.journal != nullptr) {
+ if (can_affect_io) {
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ }
+ expect_is_journal_appending(*mock_image_ctx.journal, true);
+ expect_allocate_op_tid(mock_image_ctx);
+ EXPECT_CALL(*mock_image_ctx.journal, append_op_event_mock(_, _, _))
+ .WillOnce(WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+}
+
+void TestMockFixture::expect_commit_op_event(librbd::MockImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.journal != nullptr) {
+ expect_is_journal_appending(*mock_image_ctx.journal, true);
+ expect_is_journal_ready(*mock_image_ctx.journal);
+ EXPECT_CALL(*mock_image_ctx.journal, commit_op_event(1U, r, _))
+ .WillOnce(WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+}
+
diff --git a/src/test/librbd/test_mock_fixture.h b/src/test/librbd/test_mock_fixture.h
new file mode 100644
index 000000000..4e96d955f
--- /dev/null
+++ b/src/test/librbd/test_mock_fixture.h
@@ -0,0 +1,89 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H
+#define CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "librbd/asio/ContextWQ.h"
+#include <boost/shared_ptr.hpp>
+#include <gmock/gmock.h>
+
+namespace librados {
+class TestCluster;
+class MockTestMemCluster;
+class MockTestMemIoCtxImpl;
+class MockTestMemRadosClient;
+}
+namespace librbd {
+class MockImageCtx;
+}
+
+ACTION_P(CopyInBufferlist, str) {
+ arg0->append(str);
+}
+
+ACTION_P2(CompleteContext, r, wq) {
+ librbd::asio::ContextWQ *context_wq = reinterpret_cast<
+ librbd::asio::ContextWQ *>(wq);
+ if (context_wq != NULL) {
+ context_wq->queue(arg0, r);
+ } else {
+ arg0->complete(r);
+ }
+}
+
+ACTION_P(DispatchContext, wq) {
+ wq->queue(arg0, arg1);
+}
+
+ACTION_P3(FinishRequest, request, r, mock) {
+ librbd::MockImageCtx *mock_image_ctx =
+ reinterpret_cast<librbd::MockImageCtx *>(mock);
+ mock_image_ctx->image_ctx->op_work_queue->queue(request->on_finish, r);
+}
+
+ACTION_P(GetReference, ref_object) {
+ ref_object->get();
+}
+
+MATCHER_P(ContentsEqual, bl, "") {
+ // TODO fix const-correctness of bufferlist
+ return const_cast<bufferlist &>(arg).contents_equal(
+ const_cast<bufferlist &>(bl));
+}
+
+class TestMockFixture : public TestFixture {
+public:
+ typedef boost::shared_ptr<librados::TestCluster> TestClusterRef;
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ void TearDown() override;
+
+ void expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx);
+ void expect_unlock_exclusive_lock(librbd::ImageCtx &ictx);
+
+ void initialize_features(librbd::ImageCtx *ictx,
+ librbd::MockImageCtx &mock_image_ctx,
+ librbd::MockExclusiveLock &mock_exclusive_lock,
+ librbd::MockJournal &mock_journal,
+ librbd::MockObjectMap &mock_object_map);
+
+ void expect_is_journal_appending(librbd::MockJournal &mock_journal,
+ bool appending);
+ void expect_is_journal_replaying(librbd::MockJournal &mock_journal);
+ void expect_is_journal_ready(librbd::MockJournal &mock_journal);
+ void expect_allocate_op_tid(librbd::MockImageCtx &mock_image_ctx);
+ void expect_append_op_event(librbd::MockImageCtx &mock_image_ctx,
+ bool can_affect_io, int r);
+ void expect_commit_op_event(librbd::MockImageCtx &mock_image_ctx, int r);
+
+private:
+ static TestClusterRef s_test_cluster;
+};
+
+#endif // CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H
diff --git a/src/test/librbd/test_notify.py b/src/test/librbd/test_notify.py
new file mode 100755
index 000000000..4e929d74f
--- /dev/null
+++ b/src/test/librbd/test_notify.py
@@ -0,0 +1,183 @@
+#!/usr/bin/python3
+import os
+import sys
+import time
+
+from rados import Rados
+from rbd import (RBD,
+ Image,
+ ImageNotFound,
+ RBD_FEATURE_EXCLUSIVE_LOCK,
+ RBD_FEATURE_LAYERING,
+ RBD_FEATURE_OBJECT_MAP,
+ RBD_FEATURE_FAST_DIFF,
+ RBD_FLAG_OBJECT_MAP_INVALID)
+
+POOL_NAME='rbd'
+PARENT_IMG_NAME='test_notify_parent'
+CLONE_IMG_NAME='test_notify_clone'
+CLONE_IMG_RENAME='test_notify_clone2'
+IMG_SIZE = 16 << 20
+IMG_ORDER = 20
+
+def delete_image(ioctx, img_name):
+ image = Image(ioctx, img_name)
+ for snap in image.list_snaps():
+ snap_name = snap['name']
+ print("removing snapshot: %s@%s" % (img_name, snap_name))
+ if image.is_protected_snap(snap_name):
+ image.unprotect_snap(snap_name)
+ image.remove_snap(snap_name)
+ image.close()
+ print("removing image: %s" % img_name)
+ RBD().remove(ioctx, img_name)
+
+def safe_delete_image(ioctx, img_name):
+ try:
+ delete_image(ioctx, img_name)
+ except ImageNotFound:
+ pass
+
+def get_features():
+ features = os.getenv("RBD_FEATURES")
+ if features is not None:
+ features = int(features)
+ else:
+ features = int(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_LAYERING |
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF)
+ assert((features & RBD_FEATURE_EXCLUSIVE_LOCK) != 0)
+ assert((features & RBD_FEATURE_LAYERING) != 0)
+ assert((features & RBD_FEATURE_OBJECT_MAP) != 0)
+ assert((features & RBD_FEATURE_FAST_DIFF) != 0)
+ return features
+
+def master(ioctx):
+ print("starting master")
+ safe_delete_image(ioctx, CLONE_IMG_RENAME)
+ safe_delete_image(ioctx, CLONE_IMG_NAME)
+ safe_delete_image(ioctx, PARENT_IMG_NAME)
+
+ features = get_features()
+ RBD().create(ioctx, PARENT_IMG_NAME, IMG_SIZE, IMG_ORDER, old_format=False,
+ features=features)
+ with Image(ioctx, PARENT_IMG_NAME) as image:
+ image.create_snap('snap1')
+ image.protect_snap('snap1')
+
+ features = features & ~(RBD_FEATURE_FAST_DIFF)
+ RBD().clone(ioctx, PARENT_IMG_NAME, 'snap1', ioctx, CLONE_IMG_NAME,
+ features=features)
+ with Image(ioctx, CLONE_IMG_NAME) as image:
+ print("acquiring exclusive lock")
+ offset = 0
+ data = os.urandom(512)
+ while offset < IMG_SIZE:
+ image.write(data, offset)
+ offset += (1 << IMG_ORDER)
+ image.write(b'1', IMG_SIZE - 1)
+ assert(image.is_exclusive_lock_owner())
+
+ print("waiting for slave to complete")
+ while image.is_exclusive_lock_owner():
+ time.sleep(5)
+
+ safe_delete_image(ioctx, CLONE_IMG_RENAME)
+ safe_delete_image(ioctx, CLONE_IMG_NAME)
+ delete_image(ioctx, PARENT_IMG_NAME)
+ print("finished")
+
+def slave(ioctx):
+ print("starting slave")
+
+ while True:
+ try:
+ with Image(ioctx, CLONE_IMG_NAME) as image:
+ if (image.list_lockers() != [] and
+ image.read(IMG_SIZE - 1, 1) == b'1'):
+ break
+ except Exception:
+ pass
+
+ print("detected master")
+
+ print("rename")
+ RBD().rename(ioctx, CLONE_IMG_NAME, CLONE_IMG_RENAME);
+
+ with Image(ioctx, CLONE_IMG_RENAME) as image:
+ print("flatten")
+ image.flatten()
+ assert(not image.is_exclusive_lock_owner())
+
+ print("resize")
+ image.resize(IMG_SIZE // 2)
+ assert(not image.is_exclusive_lock_owner())
+ assert(image.stat()['size'] == IMG_SIZE // 2)
+
+ print("create_snap")
+ image.create_snap('snap1')
+ assert(not image.is_exclusive_lock_owner())
+ assert(any(snap['name'] == 'snap1'
+ for snap in image.list_snaps()))
+
+ print("protect_snap")
+ image.protect_snap('snap1')
+ assert(not image.is_exclusive_lock_owner())
+ assert(image.is_protected_snap('snap1'))
+
+ print("unprotect_snap")
+ image.unprotect_snap('snap1')
+ assert(not image.is_exclusive_lock_owner())
+ assert(not image.is_protected_snap('snap1'))
+
+ print("rename_snap")
+ image.rename_snap('snap1', 'snap1-new')
+ assert(not image.is_exclusive_lock_owner())
+ assert(any(snap['name'] == 'snap1-new'
+ for snap in image.list_snaps()))
+
+ print("remove_snap")
+ image.remove_snap('snap1-new')
+ assert(not image.is_exclusive_lock_owner())
+ assert(list(image.list_snaps()) == [])
+
+ if 'RBD_DISABLE_UPDATE_FEATURES' not in os.environ:
+ print("update_features")
+ assert((image.features() & RBD_FEATURE_OBJECT_MAP) != 0)
+ image.update_features(RBD_FEATURE_OBJECT_MAP, False)
+ assert(not image.is_exclusive_lock_owner())
+ assert((image.features() & RBD_FEATURE_OBJECT_MAP) == 0)
+ image.update_features(RBD_FEATURE_OBJECT_MAP, True)
+ assert(not image.is_exclusive_lock_owner())
+ assert((image.features() & RBD_FEATURE_OBJECT_MAP) != 0)
+ assert((image.flags() & RBD_FLAG_OBJECT_MAP_INVALID) != 0)
+ else:
+ print("skipping update_features")
+
+ print("rebuild object map")
+ image.rebuild_object_map()
+ assert(not image.is_exclusive_lock_owner())
+ assert((image.flags() & RBD_FLAG_OBJECT_MAP_INVALID) == 0)
+
+ print("write")
+ data = os.urandom(512)
+ image.write(data, 0)
+ assert(image.is_exclusive_lock_owner())
+
+ print("finished")
+
+def main():
+ if len(sys.argv) != 2 or sys.argv[1] not in ['master', 'slave']:
+ print("usage: %s: [master/slave]" % sys.argv[0])
+ sys.exit(2)
+
+ rados = Rados(conffile='')
+ rados.connect()
+ ioctx = rados.open_ioctx(POOL_NAME)
+ if sys.argv[1] == 'master':
+ master(ioctx)
+ else:
+ slave(ioctx)
+ rados.shutdown()
+
+if __name__ == "__main__":
+ main()
diff --git a/src/test/librbd/test_support.cc b/src/test/librbd/test_support.cc
new file mode 100644
index 000000000..bc9d2543d
--- /dev/null
+++ b/src/test/librbd/test_support.cc
@@ -0,0 +1,137 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "test/librbd/test_support.h"
+#include "include/rbd_types.h"
+#include "gtest/gtest.h"
+#include "common/ceph_context.h"
+#include <sstream>
+
+bool get_features(uint64_t *features) {
+ const char *c = getenv("RBD_FEATURES");
+ if (c == NULL) {
+ return false;
+ }
+
+ std::stringstream ss(c);
+ if (!(ss >> *features)) {
+ return false;
+ }
+ return true;
+}
+
+bool is_feature_enabled(uint64_t feature) {
+ uint64_t features;
+ return (get_features(&features) && (features & feature) == feature);
+}
+
+int create_image_full_pp(librbd::RBD &rbd, librados::IoCtx &ioctx,
+ const std::string &name, uint64_t size,
+ uint64_t features, bool old_format, int *order)
+{
+ if (old_format) {
+ librados::Rados rados(ioctx);
+ int r = rados.conf_set("rbd_default_format", "1");
+ if (r < 0) {
+ return r;
+ }
+ return rbd.create(ioctx, name.c_str(), size, order);
+ } else if ((features & RBD_FEATURE_STRIPINGV2) != 0) {
+ uint64_t stripe_unit = IMAGE_STRIPE_UNIT;
+ if (*order) {
+ // use a conservative stripe_unit for non default order
+ stripe_unit = (1ull << (*order-1));
+ }
+
+ printf("creating image with stripe unit: %" PRIu64 ", stripe count: %" PRIu64 "\n",
+ stripe_unit, IMAGE_STRIPE_COUNT);
+ return rbd.create3(ioctx, name.c_str(), size, features, order, stripe_unit,
+ IMAGE_STRIPE_COUNT);
+ } else {
+ return rbd.create2(ioctx, name.c_str(), size, features, order);
+ }
+}
+
+int create_image_pp(librbd::RBD &rbd, librados::IoCtx &ioctx,
+ const std::string &name, uint64_t size) {
+ int order = 0;
+ uint64_t features = 0;
+ if (!get_features(&features)) {
+ // ensure old-format tests actually use the old format
+ librados::Rados rados(ioctx);
+ int r = rados.conf_set("rbd_default_format", "1");
+ if (r < 0) {
+ return r;
+ }
+ return rbd.create(ioctx, name.c_str(), size, &order);
+ } else {
+ return rbd.create2(ioctx, name.c_str(), size, features, &order);
+ }
+}
+
+int clone_image_pp(librbd::RBD &rbd, librbd::Image &p_image, librados::IoCtx &p_ioctx,
+ const char *p_name, const char *p_snap_name, librados::IoCtx &c_ioctx,
+ const char *c_name, uint64_t features)
+{
+ uint64_t stripe_unit = p_image.get_stripe_unit();
+ uint64_t stripe_count = p_image.get_stripe_count();
+
+ librbd::image_info_t p_info;
+ int r = p_image.stat(p_info, sizeof(p_info));
+ if (r < 0) {
+ return r;
+ }
+
+ int c_order = p_info.order;
+ return rbd.clone2(p_ioctx, p_name, p_snap_name, c_ioctx, c_name,
+ features, &c_order, stripe_unit, stripe_count);
+}
+
+int get_image_id(librbd::Image &image, std::string *image_id)
+{
+ int r = image.get_id(image_id);
+ if (r < 0) {
+ return r;
+ }
+ return 0;
+}
+
+int create_image_data_pool(librados::Rados &rados, std::string &data_pool, bool *created) {
+ std::string pool;
+ int r = rados.conf_get("rbd_default_data_pool", pool);
+ if (r != 0) {
+ return r;
+ } else if (pool.empty()) {
+ return 0;
+ }
+
+ r = rados.pool_create(pool.c_str());
+ if ((r == 0) || (r == -EEXIST)) {
+ data_pool = pool;
+ *created = (r == 0);
+ return 0;
+ }
+
+ librados::IoCtx ioctx;
+ r = rados.ioctx_create(pool.c_str(), ioctx);
+ if (r < 0) {
+ return r;
+ }
+
+ librbd::RBD rbd;
+ return rbd.pool_init(ioctx, true);
+}
+
+bool is_librados_test_stub(librados::Rados &rados) {
+ std::string fsid;
+ EXPECT_EQ(0, rados.cluster_fsid(&fsid));
+ return fsid == "00000000-1111-2222-3333-444444444444";
+}
+
+bool is_rbd_pwl_enabled(ceph::common::CephContext *cct) {
+#if defined(WITH_RBD_RWL) || defined(WITH_RBD_SSD_CACHE)
+ auto value = cct->_conf.get_val<std::string>("rbd_persistent_cache_mode");
+ return value == "disabled" ? false : true;
+#else
+ return false;
+#endif
+}
diff --git a/src/test/librbd/test_support.h b/src/test/librbd/test_support.h
new file mode 100644
index 000000000..602ec0955
--- /dev/null
+++ b/src/test/librbd/test_support.h
@@ -0,0 +1,39 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/int_types.h"
+#include "include/rados/librados.h"
+#include "include/rbd/librbd.hpp"
+#include <string>
+
+static const uint64_t IMAGE_STRIPE_UNIT = 65536;
+static const uint64_t IMAGE_STRIPE_COUNT = 16;
+
+#define TEST_IO_SIZE 512
+#define TEST_IO_TO_SNAP_SIZE 80
+
+bool get_features(uint64_t *features);
+bool is_feature_enabled(uint64_t feature);
+int create_image_pp(librbd::RBD &rbd, librados::IoCtx &ioctx,
+ const std::string &name, uint64_t size);
+int create_image_full_pp(librbd::RBD &rbd, librados::IoCtx &ioctx,
+ const std::string &name, uint64_t size,
+ uint64_t features, bool old_format, int *order);
+int clone_image_pp(librbd::RBD &rbd, librbd::Image &p_image, librados::IoCtx &p_ioctx,
+ const char *p_name, const char *p_snap_name, librados::IoCtx &c_ioctx,
+ const char *c_name, uint64_t features);
+int get_image_id(librbd::Image &image, std::string *image_id);
+int create_image_data_pool(librados::Rados &rados, std::string &data_pool, bool *created);
+
+bool is_librados_test_stub(librados::Rados &rados);
+
+bool is_rbd_pwl_enabled(ceph::common::CephContext *ctx);
+
+#define REQUIRE(x) { \
+ if (!(x)) { \
+ GTEST_SKIP() << "Skipping due to unmet REQUIRE"; \
+ } \
+}
+
+#define REQUIRE_FEATURE(feature) REQUIRE(is_feature_enabled(feature))
+#define REQUIRE_FORMAT_V1() REQUIRE(!is_feature_enabled(0))
+#define REQUIRE_FORMAT_V2() REQUIRE_FEATURE(0)
diff --git a/src/test/librbd/trash/test_mock_MoveRequest.cc b/src/test/librbd/trash/test_mock_MoveRequest.cc
new file mode 100644
index 000000000..1a35c62ae
--- /dev/null
+++ b/src/test/librbd/trash/test_mock_MoveRequest.cc
@@ -0,0 +1,230 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockExclusiveLock.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/Utils.h"
+#include "librbd/trash/MoveRequest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ static MockTestImageCtx *s_instance;
+ static MockTestImageCtx *create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->construct(image_name, image_id);
+ return s_instance;
+ }
+
+ MOCK_METHOD2(construct, void(const std::string&, const std::string&));
+
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ s_instance = this;
+ }
+};
+
+MockTestImageCtx *MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "librbd/trash/MoveRequest.cc"
+
+namespace librbd {
+namespace trash {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+struct TestMockTrashMoveRequest : public TestMockFixture {
+ typedef MoveRequest<librbd::MockTestImageCtx> MockMoveRequest;
+
+ void expect_trash_add(MockTestImageCtx &mock_image_ctx,
+ const std::string& image_id,
+ cls::rbd::TrashImageSource trash_image_source,
+ const std::string& name,
+ const utime_t& end_time,
+ int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(StrEq("rbd_trash"), _, StrEq("rbd"), StrEq("trash_add"),
+ _, _, _, _))
+ .WillOnce(WithArg<4>(Invoke([=](bufferlist& in_bl) {
+ std::string id;
+ cls::rbd::TrashImageSpec trash_image_spec;
+
+ auto bl_it = in_bl.cbegin();
+ decode(id, bl_it);
+ decode(trash_image_spec, bl_it);
+
+ EXPECT_EQ(id, image_id);
+ EXPECT_EQ(trash_image_spec.source,
+ trash_image_source);
+ EXPECT_EQ(trash_image_spec.name, name);
+ EXPECT_EQ(trash_image_spec.deferment_end_time,
+ end_time);
+ return r;
+ })));
+ }
+
+ void expect_aio_remove(MockTestImageCtx &mock_image_ctx,
+ const std::string& oid, int r) {
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), remove(oid, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_dir_remove(MockTestImageCtx& mock_image_ctx,
+ const std::string& name, const std::string& id,
+ int r) {
+ bufferlist in_bl;
+ encode(name, in_bl);
+ encode(id, in_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(StrEq("rbd_directory"), _, StrEq("rbd"), StrEq("dir_remove_image"),
+ ContentsEqual(in_bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+};
+
+TEST_F(TestMockTrashMoveRequest, Success) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ utime_t delete_time{ceph_clock_now()};
+ expect_trash_add(mock_image_ctx, "image id",
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ 0);
+ expect_aio_remove(mock_image_ctx, util::id_obj_name("image name"), 0);
+ expect_dir_remove(mock_image_ctx, "image name", "image id", 0);
+
+ C_SaferCond ctx;
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ delete_time};
+ auto req = MockMoveRequest::create(mock_image_ctx.md_ctx, "image id",
+ trash_image_spec, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockTrashMoveRequest, TrashAddError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ utime_t delete_time{ceph_clock_now()};
+ expect_trash_add(mock_image_ctx, "image id",
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ -EPERM);
+
+ C_SaferCond ctx;
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ delete_time};
+ auto req = MockMoveRequest::create(mock_image_ctx.md_ctx, "image id",
+ trash_image_spec, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockTrashMoveRequest, RemoveIdError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ utime_t delete_time{ceph_clock_now()};
+ expect_trash_add(mock_image_ctx, "image id",
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ 0);
+ expect_aio_remove(mock_image_ctx, util::id_obj_name("image name"), -EPERM);
+
+ C_SaferCond ctx;
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ delete_time};
+ auto req = MockMoveRequest::create(mock_image_ctx.md_ctx, "image id",
+ trash_image_spec, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockTrashMoveRequest, DirectoryRemoveError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ utime_t delete_time{ceph_clock_now()};
+ expect_trash_add(mock_image_ctx, "image id",
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ 0);
+ expect_aio_remove(mock_image_ctx, util::id_obj_name("image name"), 0);
+ expect_dir_remove(mock_image_ctx, "image name", "image id", -EPERM);
+
+ C_SaferCond ctx;
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", delete_time,
+ delete_time};
+ auto req = MockMoveRequest::create(mock_image_ctx.md_ctx, "image id",
+ trash_image_spec, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+} // namespace trash
+} // namespace librbd
diff --git a/src/test/librbd/trash/test_mock_RemoveRequest.cc b/src/test/librbd/trash/test_mock_RemoveRequest.cc
new file mode 100644
index 000000000..51d6123de
--- /dev/null
+++ b/src/test/librbd/trash/test_mock_RemoveRequest.cc
@@ -0,0 +1,230 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockExclusiveLock.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/Utils.h"
+#include "librbd/image/TypeTraits.h"
+#include "librbd/image/RemoveRequest.h"
+#include "librbd/internal.h"
+#include "librbd/trash/RemoveRequest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+// template <>
+// struct TypeTraits<MockTestImageCtx> {
+// typedef librbd::MockContextWQ ContextWQ;
+// };
+
+template <>
+class RemoveRequest<MockTestImageCtx> {
+private:
+ typedef ::librbd::image::TypeTraits<MockTestImageCtx> TypeTraits;
+ typedef typename TypeTraits::ContextWQ ContextWQ;
+public:
+ static RemoveRequest *s_instance;
+ static RemoveRequest *create(librados::IoCtx &ioctx,
+ const std::string &image_name,
+ const std::string &image_id,
+ bool force, bool from_trash_remove,
+ ProgressContext &prog_ctx,
+ ContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ static RemoveRequest *create(librados::IoCtx &ioctx, MockTestImageCtx *image_ctx,
+ bool force, bool from_trash_remove,
+ ProgressContext &prog_ctx,
+ ContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+RemoveRequest<MockTestImageCtx> *RemoveRequest<MockTestImageCtx>::s_instance;
+
+} // namespace image
+} // namespace librbd
+
+#include "librbd/trash/RemoveRequest.cc"
+
+namespace librbd {
+namespace trash {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+
+struct TestMockTrashRemoveRequest : public TestMockFixture {
+ typedef RemoveRequest<librbd::MockTestImageCtx> MockRemoveRequest;
+ typedef image::RemoveRequest<librbd::MockTestImageCtx> MockImageRemoveRequest;
+
+ NoOpProgressContext m_prog_ctx;
+
+ void expect_set_state(MockTestImageCtx& mock_image_ctx,
+ cls::rbd::TrashImageState trash_set_state,
+ cls::rbd::TrashImageState trash_expect_state,
+ int r) {
+ bufferlist in_bl;
+ encode(mock_image_ctx.id, in_bl);
+ encode(trash_set_state, in_bl);
+ encode(trash_expect_state, in_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(StrEq("rbd_trash"), _, StrEq("rbd"), StrEq("trash_state_set"),
+ ContentsEqual(in_bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_set_deleting_state(MockTestImageCtx& mock_image_ctx, int r) {
+ expect_set_state(mock_image_ctx, cls::rbd::TRASH_IMAGE_STATE_REMOVING,
+ cls::rbd::TRASH_IMAGE_STATE_NORMAL, r);
+ }
+
+ void expect_restore_normal_state(MockTestImageCtx& mock_image_ctx, int r) {
+ expect_set_state(mock_image_ctx, cls::rbd::TRASH_IMAGE_STATE_NORMAL,
+ cls::rbd::TRASH_IMAGE_STATE_REMOVING, r);
+ }
+
+ void expect_close_image(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, close(_))
+ .WillOnce(Invoke([r](Context *on_finish) {
+ on_finish->complete(r);
+ }));
+ }
+
+ void expect_remove_image(MockImageRemoveRequest& mock_image_remove_request,
+ int r) {
+ EXPECT_CALL(mock_image_remove_request, send())
+ .WillOnce(Invoke([&mock_image_remove_request, r]() {
+ mock_image_remove_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_remove_trash_entry(MockTestImageCtx& mock_image_ctx,
+ int r) {
+ bufferlist in_bl;
+ encode(mock_image_ctx.id, in_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(StrEq("rbd_trash"), _, StrEq("rbd"), StrEq("trash_remove"),
+ ContentsEqual(in_bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+};
+
+TEST_F(TestMockTrashRemoveRequest, Success) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageRemoveRequest mock_image_remove_request;
+
+ InSequence seq;
+ expect_set_deleting_state(mock_image_ctx, 0);
+ expect_remove_image(mock_image_remove_request, 0);
+ expect_remove_trash_entry(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockRemoveRequest::create(mock_image_ctx.md_ctx, &mock_image_ctx,
+ nullptr, false, m_prog_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+TEST_F(TestMockTrashRemoveRequest, SetDeletingStateError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageRemoveRequest mock_image_remove_request;
+
+ InSequence seq;
+ expect_set_deleting_state(mock_image_ctx, -EINVAL);
+ expect_close_image(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockRemoveRequest::create(mock_image_ctx.md_ctx, &mock_image_ctx,
+ nullptr, false, m_prog_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockTrashRemoveRequest, RemoveImageError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageRemoveRequest mock_image_remove_request;
+
+ InSequence seq;
+ expect_set_deleting_state(mock_image_ctx, 0);
+ expect_remove_image(mock_image_remove_request, -EINVAL);
+ expect_restore_normal_state(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockRemoveRequest::create(mock_image_ctx.md_ctx, &mock_image_ctx,
+ nullptr, false, m_prog_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockTrashRemoveRequest, RemoveTrashEntryError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockImageRemoveRequest mock_image_remove_request;
+
+ InSequence seq;
+ expect_set_deleting_state(mock_image_ctx, 0);
+ expect_remove_image(mock_image_remove_request, 0);
+ expect_remove_trash_entry(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockRemoveRequest::create(mock_image_ctx.md_ctx, &mock_image_ctx,
+ nullptr, false, m_prog_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace trash
+} // namespace librbd
diff --git a/src/test/librbd/watcher/test_mock_RewatchRequest.cc b/src/test/librbd/watcher/test_mock_RewatchRequest.cc
new file mode 100644
index 000000000..9e7c5e023
--- /dev/null
+++ b/src/test/librbd/watcher/test_mock_RewatchRequest.cc
@@ -0,0 +1,227 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "include/rados/librados.hpp"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "librados/AioCompletionImpl.h"
+#include "librbd/watcher/RewatchRequest.h"
+
+namespace librbd {
+namespace watcher {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+struct TestMockWatcherRewatchRequest : public TestMockFixture {
+ typedef RewatchRequest MockRewatchRequest;
+
+ TestMockWatcherRewatchRequest() = default;
+
+ void expect_aio_watch(MockImageCtx &mock_image_ctx, int r) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(
+ mock_image_ctx.md_ctx));
+
+ EXPECT_CALL(mock_io_ctx, aio_watch(mock_image_ctx.header_oid, _, _, _))
+ .WillOnce(DoAll(WithArgs<1, 2>(Invoke([&mock_image_ctx, &mock_io_ctx, r](librados::AioCompletionImpl *c, uint64_t *cookie) {
+ *cookie = 234;
+ c->get();
+ mock_image_ctx.image_ctx->op_work_queue->queue(new LambdaContext([&mock_io_ctx, c](int r) {
+ mock_io_ctx.get_mock_rados_client()->finish_aio_completion(c, r);
+ }), r);
+ })),
+ Return(0)));
+ }
+
+ void expect_aio_unwatch(MockImageCtx &mock_image_ctx, int r) {
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(
+ mock_image_ctx.md_ctx));
+
+ EXPECT_CALL(mock_io_ctx, aio_unwatch(m_watch_handle, _))
+ .WillOnce(DoAll(Invoke([&mock_image_ctx, &mock_io_ctx, r](uint64_t handle,
+ librados::AioCompletionImpl *c) {
+ c->get();
+ mock_image_ctx.image_ctx->op_work_queue->queue(new LambdaContext([&mock_io_ctx, c](int r) {
+ mock_io_ctx.get_mock_rados_client()->finish_aio_completion(c, r);
+ }), r);
+ }),
+ Return(0)));
+ }
+
+ struct WatchCtx : public librados::WatchCtx2 {
+ void handle_notify(uint64_t, uint64_t, uint64_t,
+ ceph::bufferlist&) override {
+ ceph_abort();
+ }
+ void handle_error(uint64_t, int) override {
+ ceph_abort();
+ }
+ };
+
+ ceph::shared_mutex m_watch_lock = ceph::make_shared_mutex("watch_lock");
+ WatchCtx m_watch_ctx;
+ uint64_t m_watch_handle = 123;
+};
+
+TEST_F(TestMockWatcherRewatchRequest, Success) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_aio_unwatch(mock_image_ctx, 0);
+ expect_aio_watch(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.header_oid,
+ m_watch_lock,
+ &m_watch_ctx,
+ &m_watch_handle,
+ &ctx);
+ {
+ std::unique_lock watch_locker{m_watch_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(234U, m_watch_handle);
+}
+
+TEST_F(TestMockWatcherRewatchRequest, UnwatchError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_aio_unwatch(mock_image_ctx, -EINVAL);
+ expect_aio_watch(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.header_oid,
+ m_watch_lock,
+ &m_watch_ctx,
+ &m_watch_handle,
+ &ctx);
+ {
+ std::unique_lock watch_locker{m_watch_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(234U, m_watch_handle);
+}
+
+TEST_F(TestMockWatcherRewatchRequest, WatchBlocklist) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_aio_unwatch(mock_image_ctx, 0);
+ expect_aio_watch(mock_image_ctx, -EBLOCKLISTED);
+
+ C_SaferCond ctx;
+ MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.header_oid,
+ m_watch_lock,
+ &m_watch_ctx,
+ &m_watch_handle,
+ &ctx);
+ {
+ std::unique_lock watch_locker{m_watch_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+ ASSERT_EQ(0U, m_watch_handle);
+}
+
+TEST_F(TestMockWatcherRewatchRequest, WatchDNE) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_aio_unwatch(mock_image_ctx, 0);
+ expect_aio_watch(mock_image_ctx, -ENOENT);
+
+ C_SaferCond ctx;
+ MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.header_oid,
+ m_watch_lock,
+ &m_watch_ctx,
+ &m_watch_handle,
+ &ctx);
+ {
+ std::unique_lock watch_locker{m_watch_lock};
+ req->send();
+ }
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_EQ(0U, m_watch_handle);
+}
+
+TEST_F(TestMockWatcherRewatchRequest, WatchError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_aio_unwatch(mock_image_ctx, 0);
+ expect_aio_watch(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.header_oid,
+ m_watch_lock,
+ &m_watch_ctx,
+ &m_watch_handle,
+ &ctx);
+ {
+ std::unique_lock watch_locker{m_watch_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, ctx.wait());
+ ASSERT_EQ(0U, m_watch_handle);
+}
+
+TEST_F(TestMockWatcherRewatchRequest, InvalidWatchHandler) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+ expect_aio_watch(mock_image_ctx, 0);
+
+ m_watch_handle = 0;
+
+ C_SaferCond ctx;
+ MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx.md_ctx,
+ mock_image_ctx.header_oid,
+ m_watch_lock,
+ &m_watch_ctx,
+ &m_watch_handle,
+ &ctx);
+ {
+ std::unique_lock watch_locker{m_watch_lock};
+ req->send();
+ }
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(234U, m_watch_handle);
+}
+
+} // namespace watcher
+} // namespace librbd
diff --git a/src/test/librgw_file.cc b/src/test/librgw_file.cc
new file mode 100644
index 000000000..8f50ab7ac
--- /dev/null
+++ b/src/test/librgw_file.cc
@@ -0,0 +1,291 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "global/global_init.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace {
+ librgw_t h_rgw = nullptr;
+ string uid("testuser");
+ string access_key("");
+ string secret_key("");
+ struct rgw_fs *fs = nullptr;
+ typedef std::tuple<string, uint64_t, struct rgw_file_handle*> fid_type; //in c++2014 can alias...
+ typedef std::tuple<fid_type, std::vector<fid_type>> bucket_type;
+
+ std::vector<fid_type> fids1;
+ std::vector<bucket_type> bucket_matrix;
+
+ bool do_getattr = false;
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&h_rgw, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(h_rgw, nullptr);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount2(h_rgw, uid.c_str(), access_key.c_str(), secret_key.c_str(),
+ "/", &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+}
+
+TEST(LibRGW, GETATTR_ROOT) {
+ if (do_getattr) {
+ if (! fs)
+ return;
+
+ struct stat st;
+ int ret = rgw_getattr(fs, fs->root_fh, &st, RGW_GETATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+extern "C" {
+ static int r1_cb(const char* name, void *arg, uint64_t offset,
+ struct stat* st, uint32_t st_mask,
+ uint32_t flags) {
+ // don't need arg--it would point to fids1
+ fids1.push_back(fid_type(name, offset, nullptr /* handle */));
+ return true; /* XXX ? */
+ }
+}
+
+TEST(LibRGW, LIST_BUCKETS) {
+ /* list buckets via readdir in fs root */
+ using std::get;
+
+ if (! fs)
+ return;
+
+ bool eof = false;
+ uint64_t offset = 0;
+ int ret = rgw_readdir(fs, fs->root_fh, &offset, r1_cb, &fids1, &eof,
+ RGW_READDIR_FLAG_NONE);
+ for (auto& fid : fids1) {
+ std::cout << "fname: " << get<0>(fid) << " fid: " << get<1>(fid)
+ << std::endl;
+ /* push row for bucket into bucket_matrix */
+ bucket_matrix.push_back(bucket_type(fid, std::vector<fid_type>()));
+ }
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, LOOKUP_BUCKETS) {
+ using std::get;
+
+ if (! fs)
+ return;
+
+ for (auto& fid_row : bucket_matrix) {
+ auto& fid = get<0>(fid_row);
+ // auto& obj_vector = get<1>(fid_row);
+ struct rgw_file_handle *rgw_fh = nullptr;
+ ASSERT_EQ(0, rgw_lookup(fs, fs->root_fh, get<0>(fid).c_str(), &rgw_fh,
+ nullptr /* stat */, 0 /* mask */, 0 /* flags */));
+ get<2>(fid) = rgw_fh;
+ ASSERT_NE(get<2>(fid), nullptr);
+ }
+}
+
+TEST(LibRGW, GETATTR_BUCKETS) {
+ if (do_getattr) {
+ using std::get;
+
+ if (! fs)
+ return;
+
+ struct stat st;
+
+ for (auto& fid_row : bucket_matrix) {
+ auto& fid = get<0>(fid_row);
+ struct rgw_file_handle *rgw_fh = get<2>(fid);
+ ASSERT_EQ(0, rgw_getattr(fs, rgw_fh, &st, RGW_GETATTR_FLAG_NONE));
+ }
+ }
+}
+
+extern "C" {
+ static int r2_cb(const char* name, void *arg, uint64_t offset,
+ struct stat* st, uint32_t st_mask,
+ uint32_t flags) {
+ std::vector<fid_type>& obj_vector = *(static_cast<std::vector<fid_type>*>(arg));
+ obj_vector.push_back(fid_type(name, offset, nullptr));
+ return true; /* XXX ? */
+ }
+}
+
+TEST(LibRGW, LIST_OBJECTS) {
+ /* list objects via readdir, bucketwise */
+ using std::get;
+
+ if (! fs)
+ return;
+
+ for (auto& fid_row : bucket_matrix) {
+ auto& fid = get<0>(fid_row); // bucket
+ std::vector<fid_type>& obj_vector = get<1>(fid_row); // objects in bucket
+ struct rgw_file_handle *bucket_fh = get<2>(fid);
+
+ ldout(g_ceph_context, 0) << __func__ << " readdir on bucket " << get<0>(fid)
+ << dendl;
+
+ bool eof = false;
+ uint64_t offset = 0;
+ int ret = rgw_readdir(fs, bucket_fh, &offset, r2_cb, &obj_vector, &eof,
+ RGW_READDIR_FLAG_NONE);
+ for (auto& obj : obj_vector) {
+ std::cout << "fname: " << get<0>(obj) << " fid: " << get<1>(obj)
+ << std::endl;
+ }
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+namespace rgw {
+ extern bool global_stop;
+}
+
+TEST(LibRGW, GETATTR_OBJECTS) {
+ if (do_getattr) {
+ using std::get;
+ struct stat st;
+ int ret;
+
+ rgw::global_stop = true;
+
+ for (auto& fid_row : bucket_matrix) {
+ auto& fid = get<0>(fid_row); // bucket
+ std::vector<fid_type>& obj_vector = get<1>(fid_row); // objects in bucket
+ struct rgw_file_handle *bucket_fh = get<2>(fid);
+
+ for (auto& obj : obj_vector) {
+ struct rgw_file_handle *obj_fh = nullptr;
+ std::string object_name = get<0>(obj);
+ ret = rgw_lookup(fs, bucket_fh, get<0>(obj).c_str(), &obj_fh,
+ nullptr /* stat */, 0 /* mask */, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ get<2>(obj) = obj_fh; // stash obj_fh for cleanup
+ ASSERT_NE(get<2>(obj), nullptr);
+ ret = rgw_getattr(fs, obj_fh, &st,
+ RGW_GETATTR_FLAG_NONE); // now, getattr
+ ASSERT_EQ(ret, 0);
+ }
+ }
+ }
+}
+
+TEST(LibRGW, CLEANUP) {
+ int ret = 0;
+ using std::get;
+
+ /* release file handles */
+ for (auto& fid_row : bucket_matrix) {
+ auto& bucket = get<0>(fid_row); // bucket
+ std::vector<fid_type>& obj_vector = get<1>(fid_row); // objects in bucket
+ for (auto& obj : obj_vector) {
+ struct rgw_file_handle *obj_fh = get<2>(obj);
+ if (obj_fh) {
+ ret = rgw_fh_rele(fs, obj_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ }
+ }
+ // now the bucket
+ struct rgw_file_handle *bucket_fh = get<2>(bucket);
+ if (bucket_fh) {
+ ret = rgw_fh_rele(fs, bucket_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ }
+ }
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(h_rgw);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ char* v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ uid = val;
+ } else if (ceph_argparse_flag(args, arg_iter, "--getattr",
+ (char*) nullptr)) {
+ do_getattr = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* don't accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librgw_file_aw.cc b/src/test/librgw_file_aw.cc
new file mode 100644
index 000000000..11c81ad7f
--- /dev/null
+++ b/src/test/librgw_file_aw.cc
@@ -0,0 +1,436 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <random>
+#include "xxhash.h"
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace {
+ librgw_t rgw = nullptr;
+ string userid("testuser");
+ string access_key("");
+ string secret_key("");
+ struct rgw_fs *fs = nullptr;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+ bool do_create = false;
+ bool do_delete = false;
+ bool do_large = false;
+ bool do_verify = false;
+ bool do_hexdump = false;
+
+ string bucket_name = "sorrydave";
+ string object_name = "jocaml";
+
+ struct rgw_file_handle *bucket_fh = nullptr;
+ struct rgw_file_handle *object_fh = nullptr;
+
+ typedef std::tuple<string,uint64_t, struct rgw_file_handle*> fid_type;
+ std::vector<fid_type> fids;
+
+ std::uniform_int_distribution<uint8_t> uint_dist;
+ std::mt19937 rng;
+
+ constexpr int iovcnt = 4;
+ constexpr int page_size = /* 65536 */ 4 * 1024*1024;
+ constexpr int seed = 8675309;
+
+ struct ZPage
+ {
+ char data[page_size];
+ uint64_t cksum;
+ }; /* ZPage */
+
+ struct ZPageSet
+ {
+ std::vector<ZPage*> pages;
+ struct iovec* iovs;
+
+ explicit ZPageSet(int n) {
+ pages.reserve(n);
+ iovs = (struct iovec*) calloc(n, sizeof(struct iovec));
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p = new ZPage();
+ for (int data_ix = 0; data_ix < page_size; ++data_ix) {
+ p->data[data_ix] = uint_dist(rng);
+ } // data_ix
+ p->cksum = XXH64(p->data, page_size, seed);
+ pages.emplace_back(p);
+ // and iovs
+ struct iovec* iov = &iovs[page_ix];
+ iov->iov_base = p->data;
+ iov->iov_len = page_size;
+ } // page_ix
+ }
+
+ int size() { return pages.size(); }
+
+ struct iovec* get_iovs() { return iovs; }
+
+ bool operator==(const ZPageSet& rhs) {
+ int n = size();
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p1 = pages[page_ix];
+ ZPage* p2 = rhs.pages[page_ix];
+ if (p1->cksum != p2->cksum)
+ return false;
+ }
+ return true;
+ }
+
+ bool operator==(const rgw_uio* uio) {
+ uint64_t cksum;
+ int vix = 0, off = 0;
+ rgw_vio* vio = &uio->uio_vio[vix];
+ int vio_len = vio->vio_len;
+ char *data;
+
+ for (int ix = 0; ix < iovcnt; ++ix) {
+ ZPage* p1 = pages[ix];
+ data = static_cast<char*>(vio->vio_base) + off;
+ cksum = XXH64(data, page_size, seed);
+
+ if (p1->cksum != cksum) {
+ int r = memcmp(data, p1->data, page_size);
+ std::cout << "problem at ix " << ix << " r " << r<< std::endl;
+ return false;
+ }
+
+ off += page_size;
+ if (off >= vio_len) {
+ vio = &uio->uio_vio[++vix];
+ vio_len = vio->vio_len;
+ off = 0;
+ }
+ }
+ return true;
+ }
+
+ void cksum() {
+ int n = size();
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p = pages[page_ix];
+ p->cksum = XXH64(p->data, page_size, seed);
+ }
+ }
+
+ void reset_iovs() { // VOP_READ and VOP_WRITE update
+ int n = size();
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p = pages[page_ix];
+ struct iovec* iov = &iovs[page_ix];
+ iov->iov_base = p->data;
+ iov->iov_len = page_size;
+ }
+ }
+
+ ~ZPageSet() {
+ for (unsigned int ix = 0; ix < pages.size(); ++ix)
+ delete pages[ix];
+ free(iovs);
+ }
+ }; /* ZPageSet */
+
+ ZPageSet zp_set1{iovcnt}; // random data
+ ZPageSet zp_set2{iovcnt}; // random data in 64K pages
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&rgw, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rgw, nullptr);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount2(rgw, userid.c_str(), access_key.c_str(),
+ secret_key.c_str(), "/", &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+}
+
+TEST(LibRGW, CREATE_BUCKET) {
+ if (do_create) {
+ struct stat st;
+ struct rgw_file_handle *fh;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
+ &fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, LOOKUP_BUCKET) {
+ int ret = rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, LOOKUP_OBJECT) {
+ int ret = rgw_lookup(fs, bucket_fh, object_name.c_str(), &object_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
+ ASSERT_EQ(ret, 0);
+}
+
+#if 0
+TEST(LibRGW, OPEN1) {
+ int ret = rgw_open(fs, object_fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, PUT_OBJECT) {
+ size_t nbytes;
+ struct iovec *iovs = zp_set1.get_iovs();
+ off_t offset = 0;
+ for (int ix : {0, 1}) {
+ struct iovec *iov = &iovs[ix];
+ // quick check
+ sprintf(static_cast<char*>(iov->iov_base), "::hi mom (%d)", ix);
+ iov->iov_len = 14;
+ int ret = rgw_write(fs, object_fh, offset, iov->iov_len, &nbytes,
+ iov->iov_base, RGW_WRITE_FLAG_NONE);
+ offset += iov->iov_len;
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(nbytes, iov->iov_len);
+ }
+}
+
+TEST(LibRGW, CLOSE1) {
+ int ret = rgw_close(fs, object_fh, RGW_CLOSE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, OPEN2) {
+ int ret = rgw_open(fs, object_fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, GET_OBJECT) {
+ size_t nread;
+ off_t offset = 0;
+ struct iovec *iovs = zp_set1.get_iovs();
+ for (int ix : {2 , 3}) {
+ struct iovec *iov = &iovs[ix];
+ int ret = rgw_read(fs, object_fh, offset, iovs[ix-2].iov_len, &nread,
+ iov->iov_base, RGW_READ_FLAG_NONE);
+ iov->iov_len = nread;
+ offset += iov->iov_len;
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(nread, iovs[ix-2].iov_len);
+ std::cout << "read: " << static_cast<char*>(iov->iov_base) << std::endl;
+ }
+}
+
+TEST(LibRGW, CLOSE2) {
+ int ret = rgw_close(fs, object_fh, RGW_CLOSE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+#endif
+
+TEST (LibRGW, LARGE1) {
+ if (do_large) {
+ int ret;
+
+ ret = rgw_open(fs, object_fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+
+ size_t nbytes;
+ struct iovec *iovs = zp_set1.get_iovs();
+ off_t offset = 0;
+
+ for (int ix = 0; ix < iovcnt; ++ix) {
+ struct iovec *iov = &iovs[ix];
+ // write iov->iov_len
+ ret = rgw_write(fs, object_fh, offset, iov->iov_len, &nbytes,
+ iov->iov_base, RGW_WRITE_FLAG_NONE);
+ offset += iov->iov_len;
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(nbytes, iov->iov_len);
+ }
+
+ ret = rgw_close(fs, object_fh, RGW_CLOSE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ } /* do_large */
+}
+
+TEST (LibRGW, LARGE2) {
+ if (do_large) {
+ int ret;
+ if (do_verify) {
+ ret = rgw_open(fs, object_fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+
+ size_t nread;
+ off_t offset2 = 0;
+ struct iovec *iovs2 = zp_set2.get_iovs();
+ for (int ix = 0; ix < iovcnt; ++ix) {
+ struct iovec *iov2 = &iovs2[ix];
+ ret = rgw_read(fs, object_fh, offset2, iov2->iov_len, &nread,
+ iov2->iov_base, RGW_READ_FLAG_NONE);
+ iov2->iov_len = nread;
+ offset2 += iov2->iov_len;
+ ASSERT_EQ(ret, 0);
+ }
+ zp_set1.cksum();
+ zp_set2.cksum();
+ ASSERT_TRUE(zp_set1 == zp_set2);
+
+ ret = rgw_close(fs, object_fh, RGW_CLOSE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+ } /* do_large */
+}
+
+TEST(LibRGW, STAT_OBJECT) {
+ struct stat st;
+ int ret = rgw_getattr(fs, object_fh, &st, RGW_GETATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ dout(15) << "rgw_getattr on " << object_name << " size = "
+ << st.st_size << dendl;
+}
+
+TEST(LibRGW, DELETE_OBJECT) {
+ if (do_delete) {
+ int ret = rgw_unlink(fs, bucket_fh, object_name.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, DELETE_BUCKET) {
+ if (do_delete) {
+ int ret = rgw_unlink(fs, fs->root_fh, bucket_name.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, CLEANUP) {
+ int ret;
+ if (object_fh) {
+ ret = rgw_fh_rele(fs, object_fh, RGW_FH_RELE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+ ret = rgw_fh_rele(fs, bucket_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(rgw);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ char* v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ string val;
+
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--userid",
+ (char*) nullptr)) {
+ userid = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
+ (char*) nullptr)) {
+ bucket_name = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ owner_uid = std::stoi(val);
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--gid",
+ (char*) nullptr)) {
+ owner_gid = std::stoi(val);
+ } else if (ceph_argparse_flag(args, arg_iter, "--verify",
+ (char*) nullptr)) {
+ do_verify = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--create",
+ (char*) nullptr)) {
+ do_create = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--delete",
+ (char*) nullptr)) {
+ do_delete = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--large",
+ (char*) nullptr)) {
+ do_large = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--hexdump",
+ (char*) nullptr)) {
+ do_hexdump = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* don't accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librgw_file_cd.cc b/src/test/librgw_file_cd.cc
new file mode 100644
index 000000000..e0f18022e
--- /dev/null
+++ b/src/test/librgw_file_cd.cc
@@ -0,0 +1,200 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace {
+ librgw_t rgw = nullptr;
+ string userid("testuser");
+ string access_key("");
+ string secret_key("");
+ struct rgw_fs *fs = nullptr;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+ bool do_create = false;
+ bool do_delete = false;
+ bool do_multi = false;
+ int multi_cnt = 10;
+
+ string bucket_name = "sorrydave";
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&rgw, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rgw, nullptr);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount2(rgw, userid.c_str(), access_key.c_str(),
+ secret_key.c_str(), "/", &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+}
+
+TEST(LibRGW, CREATE_BUCKET) {
+ if (do_create) {
+ struct stat st;
+ struct rgw_file_handle *fh;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
+ &fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, DELETE_BUCKET) {
+ if (do_delete) {
+ int ret = rgw_unlink(fs, fs->root_fh, bucket_name.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, CREATE_BUCKET_MULTI) {
+ if (do_multi) {
+ int ret;
+ struct stat st;
+ struct rgw_file_handle *fh;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ for (int ix = 0; ix < multi_cnt; ++ix) {
+ string bn = bucket_name;
+ bn += to_string(ix);
+ ret = rgw_mkdir(fs, fs->root_fh, bn.c_str(), &st, create_mask, &fh,
+ RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ std::cout << "created: " << bn << std::endl;
+ }
+ }
+}
+
+TEST(LibRGW, DELETE_BUCKET_MULTI) {
+ if (do_multi) {
+ for (int ix = 0; ix < multi_cnt; ++ix) {
+ string bn = bucket_name;
+ bn += to_string(ix);
+ int ret = rgw_unlink(fs, fs->root_fh, bn.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+ }
+}
+
+TEST(LibRGW, CLEANUP) {
+ // do nothing
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(rgw);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ char* v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--userid",
+ (char*) nullptr)) {
+ userid = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
+ (char*) nullptr)) {
+ bucket_name = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ owner_uid = std::stoi(val);
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--gid",
+ (char*) nullptr)) {
+ owner_gid = std::stoi(val);
+ } else if (ceph_argparse_flag(args, arg_iter, "--create",
+ (char*) nullptr)) {
+ do_create = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--delete",
+ (char*) nullptr)) {
+ do_delete = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--multi",
+ (char*) nullptr)) {
+ do_multi = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* don't accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librgw_file_gp.cc b/src/test/librgw_file_gp.cc
new file mode 100644
index 000000000..4c4cfaa61
--- /dev/null
+++ b/src/test/librgw_file_gp.cc
@@ -0,0 +1,519 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <random>
+#include "xxhash.h"
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace {
+ librgw_t rgw = nullptr;
+ string uid("testuser");
+ string access_key("");
+ string secret_key("");
+ struct rgw_fs *fs = nullptr;
+
+ bool do_pre_list = false;
+ bool do_put = false;
+ bool do_bulk = false;
+ bool do_writev = false;
+ bool do_readv = false;
+ bool do_verify = false;
+ bool do_get = false;
+ bool do_create = false;
+ bool do_delete = false;
+ bool do_stat = false; // stat objects (not buckets)
+ bool do_hexdump = false;
+
+ bool object_open = false;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+ string bucket_name = "blastoff";
+ string object_name = "jocaml";
+
+ struct rgw_file_handle *bucket_fh = nullptr;
+ struct rgw_file_handle *object_fh = nullptr;
+
+ typedef std::tuple<string,uint64_t, struct rgw_file_handle*> fid_type;
+ std::vector<fid_type> fids;
+
+ std::uniform_int_distribution<uint8_t> uint_dist;
+ std::mt19937 rng;
+
+ constexpr int iovcnt = 16;
+ constexpr int page_size = 65536;
+ constexpr int seed = 8675309;
+
+ struct ZPage
+ {
+ char data[page_size];
+ uint64_t cksum;
+ }; /* ZPage */
+
+ struct ZPageSet
+ {
+ std::vector<ZPage*> pages;
+ struct iovec* iovs;
+
+ explicit ZPageSet(int n) {
+ pages.reserve(n);
+ iovs = (struct iovec*) calloc(n, sizeof(struct iovec));
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p = new ZPage();
+ for (int data_ix = 0; data_ix < page_size; ++data_ix) {
+ p->data[data_ix] = uint_dist(rng);
+ } // data_ix
+ p->cksum = XXH64(p->data, page_size, seed);
+ pages.emplace_back(p);
+ // and iovs
+ struct iovec* iov = &iovs[page_ix];
+ iov->iov_base = p->data;
+ iov->iov_len = page_size;
+ } // page_ix
+ }
+
+ int size() { return pages.size(); }
+
+ struct iovec* get_iovs() { return iovs; }
+
+ bool operator==(const ZPageSet& rhs) {
+ int n = size();
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p1 = pages[page_ix];
+ ZPage* p2 = rhs.pages[page_ix];
+ if (p1->cksum != p2->cksum)
+ return false;
+ }
+ return true;
+ }
+
+ bool operator==(const rgw_uio* uio) {
+ uint64_t cksum;
+ int vix = 0, off = 0;
+ rgw_vio* vio = &uio->uio_vio[vix];
+ int vio_len = vio->vio_len;
+ char *data;
+
+ for (int ix = 0; ix < iovcnt; ++ix) {
+ ZPage* p1 = pages[ix];
+ data = static_cast<char*>(vio->vio_base) + off;
+ cksum = XXH64(data, page_size, seed);
+
+ if (p1->cksum != cksum) {
+ int r = memcmp(data, p1->data, page_size);
+ std::cout << "problem at ix " << ix << " r " << r<< std::endl;
+ return false;
+ }
+
+ off += page_size;
+ if (off >= vio_len) {
+ vio = &uio->uio_vio[++vix];
+ vio_len = vio->vio_len;
+ off = 0;
+ }
+ }
+ return true;
+ }
+
+ void cksum() {
+ int n = size();
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p = pages[page_ix];
+ p->cksum = XXH64(p->data, page_size, seed);
+ }
+ }
+
+ void reset_iovs() { // VOP_READ and VOP_WRITE update
+ int n = size();
+ for (int page_ix = 0; page_ix < n; ++page_ix) {
+ ZPage* p = pages[page_ix];
+ struct iovec* iov = &iovs[page_ix];
+ iov->iov_base = p->data;
+ iov->iov_len = page_size;
+ }
+ }
+
+ ~ZPageSet() {
+ for (unsigned int ix = 0; ix < pages.size(); ++ix)
+ delete pages[ix];
+ free(iovs);
+ }
+ }; /* ZPageSet */
+
+ rgw_uio uio[1];
+ ZPageSet zp_set1{iovcnt}; // 1M random data in 16 64K pages
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&rgw, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rgw, nullptr);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount2(rgw, uid.c_str(), access_key.c_str(), secret_key.c_str(),
+ "/", &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+}
+
+TEST(LibRGW, CREATE_BUCKET) {
+ if (do_create) {
+ struct stat st;
+ struct rgw_file_handle *fh;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
+ &fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, LOOKUP_BUCKET) {
+ int ret = rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+extern "C" {
+ static int r2_cb(const char* name, void *arg, uint64_t offset,
+ struct stat *st, uint32_t st_mask,
+ uint32_t flags) {
+ // don't need arg--it would point to fids
+ fids.push_back(fid_type(name, offset, nullptr));
+ return true; /* XXX ? */
+ }
+}
+
+TEST(LibRGW, LIST_OBJECTS) {
+ if (do_pre_list) {
+ /* list objects via readdir, bucketwise */
+ using std::get;
+
+ ldout(g_ceph_context, 0) << __func__ << " readdir on bucket "
+ << bucket_name << dendl;
+ bool eof = false;
+ uint64_t offset = 0;
+ int ret = rgw_readdir(fs, bucket_fh, &offset, r2_cb, &fids,
+ &eof, RGW_READDIR_FLAG_NONE);
+ for (auto& fid : fids) {
+ std::cout << "fname: " << get<0>(fid) << " fid: " << get<1>(fid)
+ << std::endl;
+ }
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, LOOKUP_OBJECT) {
+ if (do_get || do_stat || do_put || do_bulk || do_readv || do_writev) {
+ int ret = rgw_lookup(fs, bucket_fh, object_name.c_str(), &object_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, OBJ_OPEN) {
+ if (do_get || do_put || do_readv || do_writev) {
+ int ret = rgw_open(fs, object_fh, 0 /* posix flags */, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ object_open = true;
+ }
+}
+
+TEST(LibRGW, PUT_OBJECT) {
+ if (do_put) {
+ size_t nbytes;
+ string data = "hi mom"; // fix this
+ int ret = rgw_write(fs, object_fh, 0, data.length(), &nbytes,
+ (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(nbytes, data.length());
+ /* commit write transaction */
+ ret = rgw_close(fs, object_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, GET_OBJECT) {
+ if (do_get) {
+ char sbuf[512];
+ memset(sbuf, 0, 512);
+ size_t nread;
+ int ret = rgw_read(fs, object_fh, 0 /* off */, 512 /* len */, &nread, sbuf,
+ RGW_READ_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ buffer::list bl;
+ bl.push_back(buffer::create_static(nread, sbuf));
+ if (do_hexdump) {
+ dout(15) << "";
+ bl.hexdump(*_dout);
+ *_dout << dendl;
+ }
+ }
+}
+
+TEST(LibRGW, STAT_OBJECT) {
+ if (do_stat) {
+ struct stat st;
+ int ret = rgw_getattr(fs, object_fh, &st, RGW_GETATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ dout(15) << "rgw_getattr on " << object_name << " size = "
+ << st.st_size << dendl;
+ }
+}
+
+TEST(LibRGW, WRITE_READ_VERIFY)
+{
+ if (do_bulk && do_put) {
+ ZPageSet zp_set1{iovcnt}; // 1M random data in 16 64K pages
+ struct iovec *iovs = zp_set1.get_iovs();
+
+ /* read after write POSIX-style */
+ size_t nbytes, off = 0;
+ for (int ix = 0; ix < 16; ++ix, off += page_size) {
+ struct iovec *iov = &iovs[ix];
+ int ret = rgw_write(fs, object_fh, off, page_size, &nbytes,
+ iov->iov_base, RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(nbytes, size_t(page_size));
+ }
+ zp_set1.reset_iovs();
+ }
+}
+
+/* "functions that call alloca are not inlined"
+ * --alexandre oliva
+ * http://gcc.gnu.org/ml/gcc-help/2004-04/msg00158.html
+ */
+#define alloca_uio() \
+ do {\
+ int uiosz = sizeof(rgw_uio) + iovcnt*sizeof(rgw_vio); \
+ uio = static_cast<rgw_uio*>(alloca(uiosz)); \
+ memset(uio, 0, uiosz); \
+ uio->uio_vio = reinterpret_cast<rgw_vio*>(uio+sizeof(rgw_uio)); \
+ } while (0); \
+
+TEST(LibRGW, WRITEV)
+{
+ if (do_writev) {
+ rgw_uio* uio;
+ struct iovec *iovs = zp_set1.get_iovs();
+ alloca_uio();
+ ASSERT_NE(uio, nullptr);
+
+ for (int ix = 0; ix < iovcnt; ++ix) {
+ struct iovec *iov = &iovs[ix];
+ rgw_vio *vio = &(uio->uio_vio[ix]);
+ vio->vio_base = iov->iov_base;
+ vio->vio_len = iov->iov_len;
+ vio->vio_u1 = iov; // private data
+ }
+ uio->uio_cnt = iovcnt;
+ uio->uio_offset = iovcnt * page_size;
+
+ int ret = rgw_writev(fs, object_fh, uio, RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, READV)
+{
+ if (do_readv) {
+ memset(uio, 0, sizeof(rgw_uio));
+ uio->uio_offset = 0; // ok, it was already 0
+ uio->uio_resid = UINT64_MAX;
+ int ret = rgw_readv(fs, object_fh, uio, RGW_READ_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ buffer::list bl;
+ for (unsigned int ix = 0; ix < uio->uio_cnt; ++ix) {
+ rgw_vio *vio = &(uio->uio_vio[ix]);
+ bl.push_back(
+ buffer::create_static(vio->vio_len,
+ static_cast<char*>(vio->vio_base)));
+ }
+
+ /* length check */
+ ASSERT_EQ(uint32_t{bl.length()}, uint32_t{iovcnt*page_size});
+
+ if (do_hexdump) {
+ dout(15) << "";
+ bl.hexdump(*_dout);
+ *_dout << dendl;
+ }
+ }
+}
+
+TEST(LibRGW, READV_AFTER_WRITEV)
+{
+ /* checksum data */
+ if (do_readv && do_writev && do_verify) {
+ ASSERT_TRUE(zp_set1 == uio);
+ }
+}
+
+TEST(LibRGW, DELETE_OBJECT) {
+ if (do_delete) {
+ int ret = rgw_unlink(fs, bucket_fh, object_name.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, DELETE_BUCKET) {
+ if (do_delete) {
+ int ret = rgw_unlink(fs, fs->root_fh, bucket_name.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, CLEANUP) {
+ if (do_readv) {
+ // release resources
+ ASSERT_NE(uio->uio_rele, nullptr);
+ if (uio->uio_rele) {
+ uio->uio_rele(uio, RGW_UIO_NONE);
+ }
+ }
+ int ret;
+ if (object_open) {
+ ret = rgw_close(fs, object_fh, RGW_CLOSE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+ if (object_fh) {
+ ret = rgw_fh_rele(fs, object_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ }
+ ret = rgw_fh_rele(fs, bucket_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(rgw);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ char* v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ uid = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
+ (char*) nullptr)) {
+ bucket_name = val;
+ } else if (ceph_argparse_flag(args, arg_iter, "--get",
+ (char*) nullptr)) {
+ do_get = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--stat",
+ (char*) nullptr)) {
+ do_stat = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--put",
+ (char*) nullptr)) {
+ do_put = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--bulk",
+ (char*) nullptr)) {
+ do_bulk = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--writev",
+ (char*) nullptr)) {
+ do_writev = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--readv",
+ (char*) nullptr)) {
+ do_readv = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--verify",
+ (char*) nullptr)) {
+ do_verify = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--delete",
+ (char*) nullptr)) {
+ do_delete = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--prelist",
+ (char*) nullptr)) {
+ do_pre_list = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--create",
+ (char*) nullptr)) {
+ do_create = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--hexdump",
+ (char*) nullptr)) {
+ do_hexdump = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* don't accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librgw_file_marker.cc b/src/test/librgw_file_marker.cc
new file mode 100644
index 000000000..44815bdeb
--- /dev/null
+++ b/src/test/librgw_file_marker.cc
@@ -0,0 +1,495 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+#include <fstream>
+#include <stack>
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+#include "rgw_file.h"
+#include "rgw_lib_frontend.h" // direct requests
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "global/global_init.h"
+#include "include/ceph_assert.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace {
+
+ using namespace rgw;
+ using std::get;
+ using std::string;
+
+ librgw_t rgw_h = nullptr;
+ string userid("testuser");
+ string access_key("");
+ string secret_key("");
+ struct rgw_fs *fs = nullptr;
+ CephContext* cct = nullptr;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+ string bucket_name("dmarker");
+
+ class obj_rec
+ {
+ public:
+ string name;
+ struct rgw_file_handle* fh;
+ struct rgw_file_handle* parent_fh;
+ RGWFileHandle* rgw_fh; // alias into fh
+
+ struct state {
+ bool readdir;
+ state() : readdir(false) {}
+ } state;
+
+ obj_rec(string _name, struct rgw_file_handle* _fh,
+ struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
+ : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
+ rgw_fh(_rgw_fh) {}
+
+ void clear() {
+ fh = nullptr;
+ rgw_fh = nullptr;
+ }
+
+ void sync() {
+ if (fh)
+ rgw_fh = get_rgwfh(fh);
+ }
+
+ friend ostream& operator<<(ostream& os, const obj_rec& rec);
+ };
+
+ /* Unused
+ ostream& operator<<(ostream& os, const obj_rec& rec)
+ {
+ RGWFileHandle* rgw_fh = rec.rgw_fh;
+ if (rgw_fh) {
+ const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
+ os << rec.rgw_fh->full_object_name()
+ << " (" << rec.rgw_fh->object_name() << "): "
+ << type;
+ }
+ return os;
+ }
+ */
+
+ std::stack<obj_rec> obj_stack;
+ std::deque<obj_rec> cleanup_queue;
+
+ typedef std::vector<obj_rec> obj_vec;
+ typedef std::tuple<obj_rec, obj_vec> dirs1_rec;
+ typedef std::vector<dirs1_rec> dirs1_vec;
+
+ dirs1_vec dirs_vec;
+
+ struct obj_rec_st
+ {
+ const obj_rec& obj;
+ const struct stat& st;
+
+ obj_rec_st(const obj_rec& _obj, const struct stat& _st)
+ : obj(_obj), st(_st) {}
+ };
+
+ /* Unused
+ ostream& operator<<(ostream& os, const obj_rec_st& rec)
+ {
+ RGWFileHandle* rgw_fh = rec.obj.rgw_fh;
+ if (rgw_fh) {
+ const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
+ os << rgw_fh->full_object_name()
+ << " (" << rgw_fh->object_name() << "): "
+ << type;
+ const struct stat& st = rec.st;
+ switch(uint8_t(rgw_fh->is_dir())) {
+ case 1:
+ os << " mode: " << st.st_mode;
+ os << " nlinks: " << st.st_nlink;
+ break;
+ case 0:
+ default:
+ os << " mode: " << st.st_mode;
+ os << " size: " << st.st_size;
+ // xxx
+ break;
+ }
+ }
+ return os;
+ }
+ */
+
+ bool do_marker1 = false;
+ bool do_marker2 = true;
+ bool do_create = false;
+ bool do_delete = false;
+ bool verbose = false;
+
+ string marker_dir("nfs_marker");
+ struct rgw_file_handle *bucket_fh = nullptr;
+ struct rgw_file_handle *marker_fh;
+ uint32_t marker_nobjs = 2*1024;
+ std::deque<obj_rec> marker_objs;
+
+ using dirent_t = std::tuple<std::string, uint64_t>;
+ struct dirent_vec
+ {
+ std::vector<dirent_t> obj_names;
+ uint32_t count;
+ dirent_vec() : count(0) {}
+ };
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, TVAR) {
+ typedef boost::variant<uint64_t*, const char*> readdir_offset;
+
+ uint64_t i1{64001};
+ std::string s1{"blunderbuss"};
+
+ readdir_offset v1{&i1};
+ readdir_offset v2{s1.c_str()};
+ readdir_offset v3{static_cast<const char*>(nullptr)};
+
+ uint64_t* pi1 = get<uint64_t*>(v1);
+ ASSERT_NE(pi1, nullptr);
+ std::cout << "read i1: " << *pi1 << std::endl;
+
+ const char* ps1 = get<const char*>(v2);
+ ASSERT_NE(ps1, nullptr);
+ std::cout << "read s1: " << ps1 << std::endl;
+
+ const char* ps3 = get<const char*>(v3);
+ ASSERT_EQ(ps3, nullptr);
+ std::cout << "read s3: " << ps3 << std::endl;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rgw_h, nullptr);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount2(rgw_h, userid.c_str(), access_key.c_str(),
+ secret_key.c_str(), "/", &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+
+ cct = static_cast<RGWLibFS*>(fs->fs_private)->get_context();
+}
+
+TEST(LibRGW, MARKER1_SETUP_BUCKET) {
+ /* "large" directory enumeration test. this one deals only with
+ * file objects */
+ struct stat st;
+ int ret;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ (void) rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! bucket_fh) {
+ if (do_create) {
+ struct stat st;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
+ &bucket_fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+ }
+
+ ASSERT_NE(bucket_fh, nullptr);
+
+ (void) rgw_lookup(fs, bucket_fh, marker_dir.c_str(), &marker_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! marker_fh) {
+ if (do_create) {
+ ret = rgw_mkdir(fs, bucket_fh, marker_dir.c_str(), &st, create_mask,
+ &marker_fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+ }
+
+ ASSERT_NE(marker_fh, nullptr);
+}
+
+TEST(LibRGW, MARKER1_SETUP_OBJECTS)
+{
+ /* "large" directory enumeration test. this one deals only with
+ * file objects */
+
+ if (do_create) {
+ int ret;
+
+ for (uint32_t ix = 0; ix < marker_nobjs; ++ix) {
+ std::string object_name("f_");
+ object_name += to_string(ix);
+ obj_rec obj{object_name, nullptr, marker_fh, nullptr};
+ // lookup object--all operations are by handle
+ ret = rgw_lookup(fs, marker_fh, obj.name.c_str(), &obj.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
+ ASSERT_EQ(ret, 0);
+ obj.rgw_fh = get_rgwfh(obj.fh);
+ // open object--open transaction
+ ret = rgw_open(fs, obj.fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_TRUE(obj.rgw_fh->is_open());
+ // unstable write data
+ size_t nbytes;
+ string data("data for ");
+ data += object_name;
+ int ret = rgw_write(fs, obj.fh, 0, data.length(), &nbytes,
+ (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(nbytes, data.length());
+ // commit transaction (write on close)
+ ret = rgw_close(fs, obj.fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ if (verbose) {
+ /* XXX std:cout fragged...did it get /0 in the stream
+ * somewhere? */
+ printf("created: %s:%s\n", bucket_name.c_str(), obj.name.c_str());
+ }
+ // save for cleanup
+ marker_objs.push_back(obj);
+ }
+ }
+}
+
+extern "C" {
+ static int r2_cb(const char* name, void *arg, uint64_t offset,
+ struct stat* st, uint32_t st_mask,
+ uint32_t flags) {
+ dirent_vec& dvec =
+ *(static_cast<dirent_vec*>(arg));
+
+ printf("%s bucket=%s dir=%s iv count=%d called back name=%s flags=%d\n",
+ __func__,
+ bucket_name.c_str(),
+ marker_dir.c_str(),
+ dvec.count,
+ name,
+ flags);
+
+ string name_str{name};
+ if (! ((name_str == ".") ||
+ (name_str == ".."))) {
+ dvec.obj_names.push_back(dirent_t{std::move(name_str), offset});
+ }
+ return true; /* XXX */
+ }
+}
+
+TEST(LibRGW, MARKER1_READDIR)
+{
+ if (do_marker1) {
+ using std::get;
+
+ dirent_vec dvec;
+ uint64_t offset = 0;
+ bool eof = false;
+
+ /* because RGWReaddirRequest::default_max is 1000 (XXX make
+ * configurable?) and marker_nobjs is 5*1024, the number
+ * of required rgw_readdir operations N should be
+ * marker_nobjs/1000 < N < marker_nobjs/1000+1, i.e., 6 when
+ * marker_nobjs==5*1024 */
+ uint32_t max_iterations = marker_nobjs/1000+1;
+
+ do {
+ ASSERT_TRUE(dvec.count <= max_iterations);
+ int ret = rgw_readdir(fs, marker_fh, &offset, r2_cb, &dvec, &eof,
+ RGW_READDIR_FLAG_DOTDOT);
+ ASSERT_EQ(ret, 0);
+ ASSERT_GE(dvec.obj_names.size(), 0);
+ ASSERT_EQ(offset, get<1>(dvec.obj_names.back())); // cookie check
+ ++dvec.count;
+ } while(!eof);
+ std::cout << "Read " << dvec.obj_names.size() << " objects in "
+ << marker_dir.c_str() << std::endl;
+ }
+}
+
+TEST(LibRGW, MARKER2_READDIR)
+{
+ if (do_marker2) {
+ using std::get;
+
+ dirent_vec dvec;
+ std::string marker{""};
+ bool eof = false;
+
+ /* because RGWReaddirRequest::default_max is 1000 (XXX make
+ * configurable?) and marker_nobjs is 5*1024, the number
+ * of required rgw_readdir operations N should be
+ * marker_nobjs/1000 < N < marker_nobjs/1000+1, i.e., 6 when
+ * marker_nobjs==5*1024 */
+ uint32_t max_iterations = marker_nobjs/1000+1;
+
+ do {
+ ASSERT_TRUE(dvec.count <= max_iterations);
+ int ret = rgw_readdir2(fs, marker_fh,
+ (marker.length() > 0) ? marker.c_str() : nullptr,
+ r2_cb, &dvec, &eof,
+ RGW_READDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_GE(dvec.obj_names.size(), 0);
+ marker = get<0>(dvec.obj_names.back());
+ ++dvec.count;
+ } while((!eof) && dvec.count < 4);
+ std::cout << "Read " << dvec.obj_names.size() << " objects in "
+ << marker_dir.c_str() << std::endl;
+ }
+}
+
+TEST(LibRGW, MARKER1_OBJ_CLEANUP)
+{
+ int rc;
+ for (auto& obj : marker_objs) {
+ if (obj.fh) {
+ if (do_delete) {
+ if (verbose) {
+ std::cout << "unlinking: " << bucket_name << ":" << obj.name
+ << std::endl;
+ }
+ rc = rgw_unlink(fs, marker_fh, obj.name.c_str(), RGW_UNLINK_FLAG_NONE);
+ }
+ rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+ marker_objs.clear();
+}
+
+TEST(LibRGW, CLEANUP) {
+ int rc;
+
+ if (do_marker1) {
+ cleanup_queue.push_back(
+ obj_rec{bucket_name, bucket_fh, fs->root_fh, get_rgwfh(fs->root_fh)});
+ }
+
+ for (auto& elt : cleanup_queue) {
+ if (elt.fh) {
+ rc = rgw_fh_rele(fs, elt.fh, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+ cleanup_queue.clear();
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(rgw_h);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ char* v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--userid",
+ (char*) nullptr)) {
+ userid = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
+ (char*) nullptr)) {
+ bucket_name = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ owner_uid = std::stoi(val);
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--gid",
+ (char*) nullptr)) {
+ owner_gid = std::stoi(val);
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--nobjs",
+ (char*) nullptr)) {
+ marker_nobjs = std::stoi(val);
+ } else if (ceph_argparse_flag(args, arg_iter, "--marker1",
+ (char*) nullptr)) {
+ do_marker1 = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--create",
+ (char*) nullptr)) {
+ do_create = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--delete",
+ (char*) nullptr)) {
+ do_delete = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
+ (char*) nullptr)) {
+ verbose = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* don't accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librgw_file_nfsns.cc b/src/test/librgw_file_nfsns.cc
new file mode 100644
index 000000000..f38b03c46
--- /dev/null
+++ b/src/test/librgw_file_nfsns.cc
@@ -0,0 +1,1214 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+#include <fstream>
+#include <stack>
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+#include "rgw_file.h"
+#include "rgw_lib.h"
+#include "rgw_lib_frontend.h" // direct requests
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "include/ceph_assert.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+using namespace rgw; // g_rgwlib
+
+namespace {
+
+ using namespace rgw;
+ using std::get;
+ using std::string;
+
+ librgw_t rgw_h = nullptr;
+ string userid("testuser");
+ string access_key("");
+ string secret_key("");
+ struct rgw_fs *fs = nullptr;
+ CephContext* cct = nullptr;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+
+ uint32_t magic_uid = 1701;
+ uint32_t magic_gid = 9876;
+
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+ string bucket_name("nfsroot");
+ string dirs1_bucket_name("bdirs1");
+ string readf_name("toyland");
+ string readf_out_name("rgwlib_readf.out");
+ std::string writef_name{"bigbird"};
+
+ int n_dirs1_dirs = 3;
+ int n_dirs1_objs = 2;
+
+ class obj_rec
+ {
+ public:
+ string name;
+ struct rgw_file_handle* fh;
+ struct rgw_file_handle* parent_fh;
+ RGWFileHandle* rgw_fh; // alias into fh
+
+ struct state {
+ bool readdir;
+ state() : readdir(false) {}
+ } state;
+
+ obj_rec(string _name, struct rgw_file_handle* _fh,
+ struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
+ : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
+ rgw_fh(_rgw_fh) {}
+
+ void clear() {
+ fh = nullptr;
+ rgw_fh = nullptr;
+ }
+
+ void sync() {
+ if (fh)
+ rgw_fh = get_rgwfh(fh);
+ }
+
+ friend ostream& operator<<(ostream& os, const obj_rec& rec);
+ };
+
+ ostream& operator<<(ostream& os, const obj_rec& rec)
+ {
+ RGWFileHandle* rgw_fh = rec.rgw_fh;
+ if (rgw_fh) {
+ const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
+ os << rec.rgw_fh->full_object_name()
+ << " (" << rec.rgw_fh->object_name() << "): "
+ << type;
+ }
+ return os;
+ }
+
+ std::stack<obj_rec> obj_stack;
+ std::deque<obj_rec> cleanup_queue;
+
+ typedef std::vector<obj_rec> obj_vec;
+ typedef std::tuple<obj_rec, obj_vec> dirs1_rec;
+ typedef std::vector<dirs1_rec> dirs1_vec;
+
+ dirs1_vec dirs_vec;
+
+ struct obj_rec_st
+ {
+ const obj_rec& obj;
+ const struct stat& st;
+
+ obj_rec_st(const obj_rec& _obj, const struct stat& _st)
+ : obj(_obj), st(_st) {}
+ };
+
+ ostream& operator<<(ostream& os, const obj_rec_st& rec)
+ {
+ RGWFileHandle* rgw_fh = rec.obj.rgw_fh;
+ if (rgw_fh) {
+ const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
+ os << rgw_fh->full_object_name()
+ << " (" << rgw_fh->object_name() << "): "
+ << type;
+ const struct stat& st = rec.st;
+ switch(uint8_t(rgw_fh->is_dir())) {
+ case 1:
+ os << " mode: " << st.st_mode;
+ os << " nlinks: " << st.st_nlink;
+ break;
+ case 0:
+ default:
+ os << " mode: " << st.st_mode;
+ os << " size: " << st.st_size;
+ // xxx
+ break;
+ }
+ }
+ return os;
+ }
+
+ bool do_hier1 = false;
+ bool do_dirs1 = false;
+ bool do_readf = false;
+ bool do_writef = false;
+ bool do_marker1 = false;
+ bool do_create = false;
+ bool do_delete = false;
+ bool do_rename = false;
+ bool do_setattr = false;
+ bool verbose = false;
+
+ string marker_dir("nfs_marker");
+ struct rgw_file_handle *bucket_fh = nullptr;
+ struct rgw_file_handle *marker_fh;
+ static constexpr int marker_nobjs = 2*1024;
+ std::deque<obj_rec> marker_objs;
+
+ using dirent_t = std::tuple<std::string, uint64_t>;
+ struct dirent_vec
+ {
+ std::vector<dirent_t> obj_names;
+ uint32_t count;
+ dirent_vec() : count(0) {}
+ };
+
+ obj_rec dirs1_b{dirs1_bucket_name, nullptr, nullptr, nullptr};
+
+ dirs1_vec renames_vec;
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rgw_h, nullptr);
+}
+
+TEST(LibRGW, MOUNT_NOROOT) {
+ /* do a mount at root="" and verify that it's root is "/" */
+ struct rgw_fs *fs = nullptr;
+ int ret = rgw_mount2(rgw_h, userid.c_str(), access_key.c_str(),
+ secret_key.c_str(), "", &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+
+ auto& root_fh = static_cast<RGWLibFS*>(fs->fs_private)->get_fh();
+ ASSERT_EQ(root_fh.get_name(), "/");
+
+ ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount2(rgw_h, userid.c_str(), access_key.c_str(),
+ secret_key.c_str(), "/", &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+
+ cct = static_cast<RGWLibFS*>(fs->fs_private)->get_context();
+}
+
+TEST(LibRGW, SETUP_HIER1)
+{
+ if (do_hier1) {
+ (void) rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! bucket_fh) {
+ if (do_create) {
+ struct stat st;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int rc = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st,
+ create_mask, &bucket_fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+
+ ASSERT_NE(bucket_fh, nullptr);
+
+ if (do_create) {
+ /* create objects directly */
+ std::vector<std::string> obj_names =
+ {"foo/bar/baz/quux",
+ "foo/f1",
+ "foo/f2",
+ "foo/bar/f1",
+ "foo/bar/d1/",
+ "foo/bar/baz/hungry",
+ "foo/bar/baz/hungry/",
+ "foo/bar/baz/momma",
+ "foo/bar/baz/bear/",
+ "foo/bar/baz/sasquatch",
+ "foo/bar/baz/sasquatch/",
+ "foo/bar/baz/frobozz"};
+
+ buffer::list bl; // empty object
+ RGWLibFS *fs_private = static_cast<RGWLibFS*>(fs->fs_private);
+
+ for (const auto& obj_name : obj_names) {
+ if (verbose) {
+ std::cout << "creating: " << bucket_name << ":" << obj_name
+ << std::endl;
+ }
+ RGWPutObjRequest req(cct,
+ g_rgwlib->get_driver()->get_user(fs_private->get_user()->user_id),
+ bucket_name, obj_name, bl);
+ int rc = g_rgwlib->get_fe()->execute_req(&req);
+ int rc2 = req.get_ret();
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(rc2, 0);
+ }
+ }
+ }
+}
+
+TEST(LibRGW, SETUP_DIRS1) {
+ if (do_dirs1) {
+ int rc;
+ struct stat st;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ dirs1_b.parent_fh = fs->root_fh;
+
+ (void) rgw_lookup(fs, dirs1_b.parent_fh, dirs1_bucket_name.c_str(),
+ &dirs1_b.fh, nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+
+ if (! dirs1_b.fh) {
+ if (do_create) {
+ rc = rgw_mkdir(fs, dirs1_b.parent_fh, dirs1_b.name.c_str(), &st,
+ create_mask, &dirs1_b.fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ } else {
+ /* no top-level dir and can't create it--skip remaining tests */
+ return;
+ }
+ }
+ dirs1_b.sync();
+
+ /* make top-level dirs */
+ int d_ix;
+ obj_vec ovec;
+ for (d_ix = 0; d_ix < n_dirs1_dirs; ++d_ix) {
+ std::string dname{"dir_"};
+ dname += to_string(d_ix);
+ obj_rec dir{dname, nullptr, dirs1_b.fh, nullptr};
+ ovec.clear();
+
+ (void) rgw_lookup(fs, dir.parent_fh, dir.name.c_str(), &dir.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! dir.fh) {
+ if (do_create) {
+ rc = rgw_mkdir(fs, dir.parent_fh, dir.name.c_str(), &st, create_mask,
+ &dir.fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+
+ ASSERT_NE(dir.fh, nullptr);
+ dir.sync();
+ ASSERT_NE(dir.rgw_fh, nullptr);
+ ASSERT_TRUE(dir.rgw_fh->is_dir());
+
+ int f_ix;
+ for (f_ix = 0; f_ix < n_dirs1_objs; ++f_ix) {
+ /* child dir */
+ std::string sdname{"sdir_"};
+ sdname += to_string(f_ix);
+ obj_rec sdir{sdname, nullptr, dir.fh, nullptr};
+
+ (void) rgw_lookup(fs, sdir.parent_fh, sdir.name.c_str(), &sdir.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+
+ if (! sdir.fh) {
+ if (do_create) {
+ rc = rgw_mkdir(fs, sdir.parent_fh, sdir.name.c_str(), &st,
+ create_mask, &sdir.fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+
+ ASSERT_NE(sdir.fh, nullptr); // suppress !lookup && !create case
+
+ sdir.sync();
+ ASSERT_TRUE(sdir.rgw_fh->is_dir());
+ ovec.push_back(sdir);
+
+ /* child file */
+ std::string sfname{"sfile_"};
+
+ sfname += to_string(f_ix);
+ obj_rec sf{sfname, nullptr, dir.fh, nullptr};
+
+ (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+
+ if (! sf.fh) {
+ if (do_create) {
+ /* make a new file object (the hard way) */
+ rc = rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
+ ASSERT_EQ(rc, 0);
+ sf.sync();
+ ASSERT_TRUE(sf.rgw_fh->is_file());
+
+ /* because we made it the hard way, fixup attributes */
+ struct stat st;
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 644;
+ sf.rgw_fh->create_stat(&st, create_mask);
+
+ /* open handle */
+ rc = rgw_open(fs, sf.fh, 0 /* posix flags */, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ ASSERT_TRUE(sf.rgw_fh->is_open());
+ /* stage seq write */
+ size_t nbytes;
+ string data = "data for " + sf.name;
+ rc = rgw_write(fs, sf.fh, 0, data.length(), &nbytes,
+ (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(nbytes, data.length());
+ /* commit write transaction */
+ rc = rgw_close(fs, sf.fh, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ }
+ } else {
+ sf.sync();
+ ASSERT_TRUE(sf.rgw_fh->is_file());
+ }
+
+ if (sf.fh)
+ ovec.push_back(sf);
+ }
+ dirs_vec.push_back(dirs1_rec{dir, ovec});
+ }
+ } /* dirs1 top-level !exist */
+}
+
+TEST(LibRGW, SETATTR) {
+ if (do_dirs1) {
+ if (do_setattr) {
+
+ int rc;
+ struct stat st;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ std::string dname{"dir_0"};
+ obj_rec dir{dname, nullptr, dirs1_b.fh, nullptr};
+
+ /* dir_0 MUST exist and MUST be resident */
+ (void) rgw_lookup(fs, dir.parent_fh, dir.name.c_str(), &dir.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+
+ ASSERT_NE(dir.fh, nullptr);
+ dir.sync();
+ ASSERT_NE(dir.rgw_fh, nullptr);
+ ASSERT_TRUE(dir.rgw_fh->is_dir());
+
+ /* child file */
+ std::string sfname{"setattr_file_0"};
+ obj_rec sf{sfname, nullptr, dir.fh, nullptr};
+
+ (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+
+ if (! sf.fh) {
+ /* make a new file object (the hard way) */
+ rc = rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
+ ASSERT_EQ(rc, 0);
+ sf.sync();
+ ASSERT_TRUE(sf.rgw_fh->is_file());
+
+ /* because we made it the hard way, fixup attributes */
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 644;
+ sf.rgw_fh->create_stat(&st, create_mask);
+
+ /* open handle */
+ rc = rgw_open(fs, sf.fh, 0 /* posix flags */, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ ASSERT_TRUE(sf.rgw_fh->is_open());
+ /* stage seq write */
+ size_t nbytes;
+ string data = "data for " + sf.name;
+ rc = rgw_write(fs, sf.fh, 0, data.length(), &nbytes,
+ (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(nbytes, data.length());
+ /* commit write transaction */
+ rc = rgw_close(fs, sf.fh, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ } else {
+ sf.sync();
+ ASSERT_TRUE(sf.rgw_fh->is_file());
+ }
+
+ /* sf MUST now be materialized--now change it's attributes */
+ st.st_uid = magic_uid;
+ st.st_gid = magic_gid;
+
+ rc = rgw_setattr(fs, sf.fh, &st, create_mask, RGW_SETATTR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+
+ /* force evict--subsequent lookups must reload */
+ static_cast<RGWLibFS*>(fs->fs_private)->release_evict(sf.rgw_fh);
+
+ sf.clear();
+
+ /* revalidate -- expect magic uid and gid */
+ (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ sf.sync();
+ ASSERT_NE(sf.fh, nullptr);
+
+ memset(&st, 0, sizeof(struct stat)); /* nothing up my sleeve... */
+
+ rc = rgw_getattr(fs, sf.fh, &st, RGW_GETATTR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+
+ ASSERT_EQ(st.st_uid, magic_uid);
+ ASSERT_EQ(st.st_gid, magic_gid);
+
+ /* release 1 ref on sf */
+ rgw_fh_rele(fs, sf.fh, RGW_FH_RELE_FLAG_NONE);
+
+ /* release 1 ref on dir */
+ rgw_fh_rele(fs, dir.fh, RGW_FH_RELE_FLAG_NONE);
+ } /* dirs1 */
+ }
+}
+
+TEST(LibRGW, RGW_CREATE_DIRS1) {
+ /* verify rgw_create (create [empty] file objects the easy way) */
+ if (do_dirs1) {
+ if (do_create) {
+ int rc;
+ struct stat st;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 644;
+
+ for (auto& dirs_rec : dirs_vec) {
+ /* create 1 more file in each sdir */
+ obj_rec& dir = get<0>(dirs_rec);
+ std::string sfname{"sfile_" + to_string(n_dirs1_objs)};
+ obj_rec sf{sfname, nullptr, dir.fh, nullptr};
+ (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! sf.fh) {
+ rc = rgw_create(fs, sf.parent_fh, sf.name.c_str(), &st, create_mask,
+ &sf.fh, 0 /* posix flags */, RGW_CREATE_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+ sf.sync();
+ }
+ n_dirs1_objs++;
+ }
+ }
+}
+
+TEST(LibRGW, RGW_SETUP_RENAME1) {
+ /* verify rgw_create (create [empty] file objects the easy way) */
+ if (do_rename) {
+ int rc;
+ struct stat st;
+ obj_vec ovec;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ for (int b_ix : {0, 1}) {
+ std::string bname{"brename" + to_string(b_ix)};
+ obj_rec brec{bname, nullptr, nullptr, nullptr};
+ (void) rgw_lookup(fs, fs->root_fh, brec.name.c_str(), &brec.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! brec.fh) {
+ if (do_create) {
+ struct stat st;
+ int rc = rgw_mkdir(fs, fs->root_fh, brec.name.c_str(), &st,
+ create_mask, &brec.fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+ ASSERT_NE(brec.fh, nullptr);
+ brec.sync();
+
+ st.st_mode = 644; /* file mask */
+
+ for (int f_ix : {0, 1}) {
+ std::string rfname{"rfile_"};
+ rfname += to_string(f_ix);
+ obj_rec rf{rfname, nullptr, brec.fh, nullptr};
+ (void) rgw_lookup(fs, rf.parent_fh, rf.name.c_str(), &rf.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! rf.fh) {
+ rc = rgw_create(fs, rf.parent_fh, rf.name.c_str(), &st, create_mask,
+ &rf.fh, 0 /* posix flags */, RGW_CREATE_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+ rf.sync();
+ ovec.push_back(rf);
+ }
+ renames_vec.push_back(dirs1_rec{brec, ovec});
+ ovec.clear();
+ } /* b_ix */
+ }
+}
+
+TEST(LibRGW, RGW_INTRABUCKET_RENAME1) {
+ /* rgw_rename a file within a bucket */
+ if (do_rename) {
+ int rc;
+ obj_rec& bdir0 = get<0>(renames_vec[0]);
+ obj_rec& src_obj = get<1>(renames_vec[0])[0];
+ std::string rfname{"rfile_r0"};
+ if (verbose) {
+ std::cout << "rename file " << src_obj.name << " to "
+ << rfname << " (bucket " << bdir0.name << ")"
+ << std::endl;
+ }
+ rc = rgw_rename(fs, bdir0.fh, src_obj.name.c_str(), bdir0.fh,
+ rfname.c_str(), RGW_RENAME_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+}
+
+TEST(LibRGW, RGW_CROSSBUCKET_RENAME1) {
+ /* rgw_rename a file within a bucket */
+ if (do_rename) {
+ int rc;
+ obj_rec& bdir0 = get<0>(renames_vec[0]);
+ obj_rec& bdir1 = get<0>(renames_vec[1]);
+ obj_rec& src_obj = get<1>(renames_vec[0])[1];
+ std::string rfname{"rfile_rhilldog"};
+ if (verbose) {
+ std::cout << "rename file " << src_obj.name
+ << " (bucket " << bdir0.name << ") to "
+ << rfname << " (bucket " << bdir1.name << ")"
+ << std::endl;
+ }
+ rc = rgw_rename(fs, bdir0.fh, src_obj.name.c_str(), bdir1.fh,
+ rfname.c_str(), RGW_RENAME_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ }
+}
+
+#if 0 /* XXX inconsistent failure here */
+TEST(LibRGW, BAD_DELETES_DIRS1) {
+ if (do_dirs1) {
+ int rc;
+
+ if (dirs_vec.size() == 0) {
+ /* skip */
+ return;
+ }
+
+ if (do_delete) {
+ /* try to unlink a non-empty directory (bucket) */
+ rc = rgw_unlink(fs, dirs1_b.parent_fh, dirs1_b.name.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_NE(rc, 0);
+ }
+ /* try to unlink a non-empty directory (non-bucket) */
+ obj_rec& sdir_0 = get<1>(dirs_vec[0])[0];
+ ASSERT_EQ(sdir_0.name, "sdir_0");
+ ASSERT_TRUE(sdir_0.rgw_fh->is_dir());
+ /* XXX we can't enforce this currently */
+#if 0
+ ASSERT_EQ(sdir_0.name, "sdir_0");
+ ASSERT_TRUE(sdir_0.rgw_fh->is_dir());
+ rc = rgw_unlink(fs, sdir_0.parent_fh, sdir_0.name.c_str(),
+ RGW_UNLINK_FLAG_NONE);
+ ASSERT_NE(rc, 0);
+#endif
+ }
+}
+#endif
+
+TEST(LibRGW, GETATTR_DIRS1)
+{
+ if (do_dirs1) {
+ int rc;
+ struct stat st;
+ for (auto& dirs_rec : dirs_vec) {
+ obj_rec& dir = get<0>(dirs_rec);
+ if (verbose) {
+ std::cout << "scanning objects in "
+ << dir.rgw_fh->full_object_name()
+ << std::endl;
+ }
+ for (auto& sobj : get<1>(dirs_rec)) {
+ rc = rgw_getattr(fs, sobj.fh, &st, RGW_GETATTR_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ /* validate, pretty-print */
+ if (sobj.rgw_fh->object_name().find("sfile") != std::string::npos) {
+ ASSERT_TRUE(sobj.rgw_fh->is_file());
+ ASSERT_TRUE(S_ISREG(st.st_mode));
+ }
+ if (sobj.rgw_fh->object_name().find("sdir") != std::string::npos) {
+ ASSERT_TRUE(sobj.rgw_fh->is_dir());
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+ }
+ /* validate Unix owners */
+ ASSERT_EQ(st.st_uid, owner_uid);
+ ASSERT_EQ(st.st_gid, owner_gid);
+ if (verbose) {
+ obj_rec_st rec_st{sobj, st};
+ std::cout << "\t"
+ << rec_st
+ << std::endl;
+ }
+ }
+ }
+ }
+}
+
+TEST(LibRGW, READ_DIRS1)
+{
+ if (do_dirs1) {
+ int rc;
+ char buf[256];
+ size_t nread;
+ for (auto& dirs_rec : dirs_vec) {
+ obj_rec& dir = get<0>(dirs_rec);
+ if (verbose) {
+ std::cout << "read back objects in "
+ << dir.rgw_fh->full_object_name()
+ << std::endl;
+ }
+ for (auto& sobj : get<1>(dirs_rec)) {
+ /* only the first 2 file objects have data */
+ if ((sobj.rgw_fh->object_name().find("sfile_0")
+ != std::string::npos) ||
+ (sobj.rgw_fh->object_name().find("sfile_1")
+ != std::string::npos)) {
+ ASSERT_TRUE(sobj.rgw_fh->is_file());
+ ASSERT_EQ(sobj.rgw_fh->get_size(), 16UL);
+ // do it
+ memset(buf, 0, 256);
+ if (verbose) {
+ std::cout << "reading 0,256 " << sobj.rgw_fh->relative_object_name()
+ << std::endl;
+ }
+ rc = rgw_read(fs, sobj.fh, 0, 256, &nread, buf, RGW_READ_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ if (verbose) {
+ std::cout << "\tread back from " << sobj.name
+ << " : \"" << buf << "\""
+ << std::endl;
+ }
+ }
+ }
+ }
+ }
+}
+
+TEST(LibRGW, READF_SETUP1)
+{
+ struct stat st;
+ if (do_dirs1) {
+ if (do_create) {
+ if ((! stat(readf_out_name.c_str(), &st)) &&
+ (S_ISREG(st.st_mode)) &&
+ (st.st_size == 6291456))
+ return;
+ ofstream of;
+ of.open(readf_out_name, ios::out|ios::app|ios::binary);
+ for (int ix1 = 0; ix1 < 6; ++ix1) {
+ for (int ix2 = 0; ix2 < 1024*1024; ++ix2) {
+ of << ix1;
+ }
+ }
+ }
+ }
+}
+
+TEST(LibRGW, READF_DIRS1) {
+ if (do_dirs1) {
+ if (do_readf) {
+ obj_rec fobj{readf_name, nullptr, dirs1_b.fh, nullptr};
+
+ int rc = rgw_lookup(fs, dirs1_b.fh, fobj.name.c_str(), &fobj.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ ASSERT_NE(fobj.fh, nullptr);
+ fobj.sync();
+
+ ofstream of;
+ of.open(readf_out_name, ios::out|ios::app|ios::binary);
+ int bufsz = 1024 * 1024 * sizeof(char);
+ auto buffer = std::make_unique<char[]>(bufsz);
+
+ uint64_t offset = 0;
+ uint64_t length = bufsz;
+ for (int ix = 0; ix < 6; ++ix) {
+ size_t nread = 0;
+ memset(buffer.get(), 0, length); // XXX
+ rc = rgw_read(fs, fobj.fh, offset, length, &nread, buffer.get(),
+ RGW_READ_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(nread, length);
+ of.write(buffer.get(), length);
+ offset += nread;
+ }
+ of.close();
+ rgw_fh_rele(fs, fobj.fh, 0 /* flags */);
+ }
+ }
+}
+
+TEST(LibRGW, WRITEF_DIRS1) {
+ if (do_dirs1) {
+ if (do_writef) {
+ int rc;
+ ifstream ifs;
+ ifs.open(readf_out_name, ios::out|ios::app|ios::binary);
+ ASSERT_TRUE(ifs.is_open());
+
+ obj_rec fobj{writef_name, nullptr, dirs1_b.fh, nullptr};
+
+ (void) rgw_lookup(fs, fobj.parent_fh, fobj.name.c_str(), &fobj.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ if (! fobj.fh) {
+ if (do_create) {
+ /* make a new file object (the hard way) */
+ rc = rgw_lookup(fs, fobj.parent_fh, fobj.name.c_str(), &fobj.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+ ASSERT_NE(fobj.fh, nullptr);
+ fobj.sync();
+
+ /* begin write transaction */
+ rc = rgw_open(fs, fobj.fh, 0 /* posix flags */, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ ASSERT_TRUE(fobj.rgw_fh->is_open());
+
+ int bufsz = 1024 * 1024 * sizeof(char);
+ char *buffer = (char*) malloc(bufsz);
+
+ uint64_t offset = 0;
+ uint64_t length = bufsz;
+ for (int ix = 0; ix < 6; ++ix) {
+ ASSERT_TRUE(ifs.good());
+ ifs.read(buffer, bufsz);
+ size_t nwritten = 0;
+ string str;
+ str.assign(buffer, 4);
+ if (verbose) {
+ std::cout << "read and writing " << length << " bytes"
+ << " from " << readf_out_name
+ << " at offset " << offset
+ << " (" << str << "... [first 4 chars])"
+ << std::endl;
+ }
+ char* leakbuf = (char*) malloc(bufsz);
+ memcpy(leakbuf, buffer, length);
+ rc = rgw_write(fs, fobj.fh, offset, length, &nwritten, leakbuf,
+ RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(nwritten, length);
+ offset += length;
+ }
+
+ /* commit write transaction */
+ rc = rgw_close(fs, fobj.fh, RGW_CLOSE_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+
+ ifs.close();
+ free(buffer);
+ rgw_fh_rele(fs, fobj.fh, 0 /* flags */);
+ }
+ }
+}
+
+TEST(LibRGW, RELEASE_DIRS1) {
+ if (do_dirs1) {
+ /* force release of handles for children of dirs1--force subsequent
+ * checks to reload them from the cluster.
+ *
+ * while doing this, verify handle cleanup and correct LRU state
+ * (not reachable)
+ */
+ int rc;
+ for (auto& dirs_rec : dirs_vec) {
+ for (auto& obj : get<1>(dirs_rec)) {
+ if (verbose) {
+ std::cout << "release " << obj.name
+ << " type: " << obj.rgw_fh->stype()
+ << " refs: " << obj.rgw_fh->get_refcnt()
+ << std::endl;
+ }
+ ASSERT_EQ(obj.rgw_fh->get_refcnt(), 2UL);
+ rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(obj.rgw_fh->get_refcnt(), 1UL);
+ /* try-discard handle */
+ /* clear obj_rec vec */
+ }
+ }
+ }
+}
+
+extern "C" {
+ static int r1_cb(const char* name, void *arg, uint64_t offset,
+ struct stat* st, uint32_t st_mask,
+ uint32_t flags) {
+ struct rgw_file_handle* parent_fh
+ = static_cast<struct rgw_file_handle*>(arg);
+ RGWFileHandle* rgw_fh = get_rgwfh(parent_fh);
+ lsubdout(cct, rgw, 10) << __func__
+ << " bucket=" << rgw_fh->bucket_name()
+ << " dir=" << rgw_fh->full_object_name()
+ << " called back name=" << name
+ << " flags=" << flags
+ << dendl;
+ string name_str{name};
+ if (! ((name_str == ".") ||
+ (name_str == ".."))) {
+ obj_stack.push(
+ obj_rec{std::move(name_str), nullptr, parent_fh, nullptr});
+ }
+ return true; /* XXX */
+ }
+}
+
+TEST(LibRGW, HIER1) {
+ if (do_hier1) {
+ int rc;
+ obj_stack.push(
+ obj_rec{bucket_name, nullptr, nullptr, nullptr});
+ while (! obj_stack.empty()) {
+ auto& elt = obj_stack.top();
+ if (! elt.fh) {
+ struct rgw_file_handle* parent_fh = elt.parent_fh
+ ? elt.parent_fh : fs->root_fh;
+ RGWFileHandle* pfh = get_rgwfh(parent_fh);
+ rgw::ignore(pfh);
+ lsubdout(cct, rgw, 10)
+ << "rgw_lookup:"
+ << " parent object_name()=" << pfh->object_name()
+ << " parent full_object_name()=" << pfh->full_object_name()
+ << " elt.name=" << elt.name
+ << dendl;
+ rc = rgw_lookup(fs, parent_fh, elt.name.c_str(), &elt.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ ASSERT_EQ(rc, 0);
+ // XXXX
+ RGWFileHandle* efh = get_rgwfh(elt.fh);
+ rgw::ignore(efh);
+ lsubdout(cct, rgw, 10)
+ << "rgw_lookup result:"
+ << " elt object_name()=" << efh->object_name()
+ << " elt full_object_name()=" << efh->full_object_name()
+ << " elt.name=" << elt.name
+ << dendl;
+
+ ASSERT_NE(elt.fh, nullptr);
+ elt.rgw_fh = get_rgwfh(elt.fh);
+ elt.parent_fh = elt.rgw_fh->get_parent()->get_fh();
+ ASSERT_EQ(elt.parent_fh, parent_fh);
+ continue;
+ } else {
+ // we have a handle in some state in top position
+ switch(elt.fh->fh_type) {
+ case RGW_FS_TYPE_DIRECTORY:
+ if (! elt.state.readdir) {
+ // descending
+ uint64_t offset = 0;
+ bool eof; // XXX
+ lsubdout(cct, rgw, 10)
+ << "readdir in"
+ << " bucket: " << elt.rgw_fh->bucket_name()
+ << " object_name: " << elt.rgw_fh->object_name()
+ << " full_name: " << elt.rgw_fh->full_object_name()
+ << dendl;
+ rc = rgw_readdir(fs, elt.fh, &offset, r1_cb, elt.fh, &eof,
+ RGW_READDIR_FLAG_DOTDOT);
+ elt.state.readdir = true;
+ ASSERT_EQ(rc, 0);
+ // ASSERT_TRUE(eof); // XXXX working incorrectly w/single readdir
+ } else {
+ // ascending
+ std::cout << elt << std::endl;
+ cleanup_queue.push_back(elt);
+ obj_stack.pop();
+ }
+ break;
+ case RGW_FS_TYPE_FILE:
+ // ascending
+ std::cout << elt << std::endl;
+ cleanup_queue.push_back(elt);
+ obj_stack.pop();
+ break;
+ default:
+ ceph_abort();
+ };
+ }
+ }
+ }
+}
+
+TEST(LibRGW, MARKER1_SETUP_BUCKET) {
+ /* "large" directory enumeration test. this one deals only with
+ * file objects */
+ if (do_marker1) {
+ struct stat st;
+ int ret;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ if (do_create) {
+ ret = rgw_mkdir(fs, bucket_fh, marker_dir.c_str(), &st, create_mask,
+ &marker_fh, RGW_MKDIR_FLAG_NONE);
+ } else {
+ ret = rgw_lookup(fs, bucket_fh, marker_dir.c_str(), &marker_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ }
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, MARKER1_SETUP_OBJECTS)
+{
+ /* "large" directory enumeration test. this one deals only with
+ * file objects */
+ if (do_marker1 && do_create) {
+ int ret;
+
+ for (int ix = 0; ix < marker_nobjs; ++ix) {
+ std::string object_name("f_");
+ object_name += to_string(ix);
+ obj_rec obj{object_name, nullptr, marker_fh, nullptr};
+ // lookup object--all operations are by handle
+ ret = rgw_lookup(fs, marker_fh, obj.name.c_str(), &obj.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
+ ASSERT_EQ(ret, 0);
+ obj.rgw_fh = get_rgwfh(obj.fh);
+ // open object--open transaction
+ ret = rgw_open(fs, obj.fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_TRUE(obj.rgw_fh->is_open());
+ // unstable write data
+ size_t nbytes;
+ string data("data for ");
+ data += object_name;
+ int ret = rgw_write(fs, obj.fh, 0, data.length(), &nbytes,
+ (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(nbytes, data.length());
+ // commit transaction (write on close)
+ ret = rgw_close(fs, obj.fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ // save for cleanup
+ marker_objs.push_back(obj);
+ }
+ }
+}
+
+extern "C" {
+ static int r2_cb(const char* name, void *arg, uint64_t offset,
+ struct stat* st, uint32_t st_mask,
+ uint32_t flags) {
+ dirent_vec& dvec =
+ *(static_cast<dirent_vec*>(arg));
+ lsubdout(cct, rgw, 10) << __func__
+ << " bucket=" << bucket_name
+ << " dir=" << marker_dir
+ << " iv count=" << dvec.count
+ << " called back name=" << name
+ << " flags=" << flags
+ << dendl;
+ string name_str{name};
+ if (! ((name_str == ".") ||
+ (name_str == ".."))) {
+ dvec.obj_names.push_back(dirent_t{std::move(name_str), offset});
+ }
+ return true; /* XXX */
+ }
+}
+
+TEST(LibRGW, MARKER1_READDIR)
+{
+ if (do_marker1) {
+ using std::get;
+
+ dirent_vec dvec;
+ uint64_t offset = 0;
+ bool eof = false;
+
+ /* because RGWReaddirRequest::default_max is 1000 (XXX make
+ * configurable?) and marker_nobjs is 5*1024, the number
+ * of required rgw_readdir operations N should be
+ * marker_nobjs/1000 < N < marker_nobjs/1000+1, i.e., 6 when
+ * marker_nobjs==5*1024 */
+ uint32_t max_iterations = marker_nobjs/1000+1;
+
+ do {
+ ASSERT_TRUE(dvec.count <= max_iterations);
+ int ret = rgw_readdir(fs, marker_fh, &offset, r2_cb, &dvec, &eof,
+ RGW_READDIR_FLAG_DOTDOT);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(offset, get<1>(dvec.obj_names.back())); // cookie check
+ ++dvec.count;
+ } while(!eof);
+ std::cout << "Read " << dvec.obj_names.size() << " objects in "
+ << marker_dir.c_str() << std::endl;
+ }
+}
+
+TEST(LibRGW, MARKER1_OBJ_CLEANUP)
+{
+ int rc;
+ for (auto& obj : marker_objs) {
+ if (obj.fh) {
+ if (do_delete) {
+ if (verbose) {
+ std::cout << "unlinking: " << bucket_name << ":" << obj.name
+ << std::endl;
+ }
+ rc = rgw_unlink(fs, marker_fh, obj.name.c_str(), RGW_UNLINK_FLAG_NONE);
+ }
+ rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+ marker_objs.clear();
+}
+
+TEST(LibRGW, CLEANUP) {
+ int rc;
+
+ if (do_marker1) {
+ cleanup_queue.push_back(
+ obj_rec{bucket_name, bucket_fh, fs->root_fh, get_rgwfh(fs->root_fh)});
+ }
+
+ for (auto& elt : cleanup_queue) {
+ if (elt.fh) {
+ rc = rgw_fh_rele(fs, elt.fh, 0 /* flags */);
+ ASSERT_EQ(rc, 0);
+ }
+ }
+ cleanup_queue.clear();
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(rgw_h);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ char* v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--userid",
+ (char*) nullptr)) {
+ userid = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
+ (char*) nullptr)) {
+ bucket_name = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ owner_uid = std::stoi(val);
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--gid",
+ (char*) nullptr)) {
+ owner_gid = std::stoi(val);
+ } else if (ceph_argparse_flag(args, arg_iter, "--hier1",
+ (char*) nullptr)) {
+ do_hier1 = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--dirs1",
+ (char*) nullptr)) {
+ do_dirs1 = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--marker1",
+ (char*) nullptr)) {
+ do_marker1 = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--setattr",
+ (char*) nullptr)) {
+ do_setattr = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--create",
+ (char*) nullptr)) {
+ do_create = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--delete",
+ (char*) nullptr)) {
+ do_delete = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--rename",
+ (char*) nullptr)) {
+ do_rename = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--readf",
+ (char*) nullptr)) {
+ do_readf = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--writef",
+ (char*) nullptr)) {
+ do_writef = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
+ (char*) nullptr)) {
+ verbose = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* don't accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/librgw_file_xattr.cc b/src/test/librgw_file_xattr.cc
new file mode 100644
index 000000000..e112c40d1
--- /dev/null
+++ b/src/test/librgw_file_xattr.cc
@@ -0,0 +1,433 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <random>
+#include <boost/algorithm/string.hpp>
+#include "xxhash.h"
+
+#include "include/rados/librgw.h"
+#include "include/rados/rgw_file.h"
+#include "rgw_file.h"
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/errno.h"
+#include "common/debug.h"
+#include "global/global_init.h"
+#include "include/ceph_assert.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace {
+
+ using namespace rgw;
+
+ string uid("testuser");
+ string access_key("");
+ string secret_key("");
+
+ librgw_t rgw_h = nullptr;
+ struct rgw_fs *fs = nullptr;
+
+ uint32_t owner_uid = 867;
+ uint32_t owner_gid = 5309;
+ uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
+
+ string bucket_name{"v4recov"};
+ string object_path{"node0/clientids"};
+
+ string key1{"black"};
+ string val1{"metallic"};
+
+ struct rgw_file_handle *bucket_fh = nullptr;
+ struct rgw_file_handle *object_fh = nullptr;
+
+ typedef std::tuple<string,uint64_t, struct rgw_file_handle*> fid_type;
+ std::vector<fid_type> fids;
+
+ class obj_rec
+ {
+ public:
+ string name;
+ struct rgw_file_handle* fh;
+ struct rgw_file_handle* parent_fh;
+ RGWFileHandle* rgw_fh; // alias into fh
+
+ struct state {
+ bool readdir;
+ state() : readdir(false) {}
+ } state;
+
+ obj_rec(string _name, struct rgw_file_handle* _fh,
+ struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
+ : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
+ rgw_fh(_rgw_fh) {}
+
+ void clear() {
+ fh = nullptr;
+ rgw_fh = nullptr;
+ }
+
+ void sync() {
+ if (fh)
+ rgw_fh = get_rgwfh(fh);
+ }
+
+ friend ostream& operator<<(ostream& os, const obj_rec& rec);
+ };
+
+ [[maybe_unused]] ostream& operator<<(ostream& os, const obj_rec& rec)
+ {
+ RGWFileHandle* rgw_fh = rec.rgw_fh;
+ if (rgw_fh) {
+ const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
+ os << rec.rgw_fh->full_object_name()
+ << " (" << rec.rgw_fh->object_name() << "): "
+ << type;
+ }
+ return os;
+ }
+
+ typedef std::vector<obj_rec> obj_vec;
+ obj_vec ovec;
+
+ bool do_stat = false;
+ bool do_create = false;
+ bool do_delete = false;
+ bool do_hexdump = false;
+ bool verbose = false;
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+}
+
+TEST(LibRGW, INIT) {
+ int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rgw_h, nullptr);
+}
+
+TEST(LibRGW, MOUNT) {
+ int ret = rgw_mount(rgw_h, uid.c_str(), access_key.c_str(),
+ secret_key.c_str(), &fs, RGW_MOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(fs, nullptr);
+}
+
+TEST(LibRGW, LOOKUP_BUCKET) {
+ int ret = rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, CREATE_BUCKET) {
+ if ((! bucket_fh) && do_create) {
+ struct stat st;
+
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
+ &bucket_fh, RGW_MKDIR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ }
+}
+
+TEST(LibRGW, CREATE_PATH) {
+
+ if (!bucket_fh)
+ return;
+
+ vector<string> segs;
+ boost::split(segs, object_path, boost::is_any_of("/"));
+
+ struct stat st;
+ st.st_uid = owner_uid;
+ st.st_gid = owner_gid;
+ st.st_mode = 755;
+
+ int ix, ret, sz = segs.size();
+ for (ix = 0; ix < sz; ++ix) {
+ auto& seg = segs[ix];
+ struct rgw_file_handle* parent_fh = (ix > 0) ? ovec[ix-1].fh : bucket_fh;
+ obj_rec dir{seg, nullptr, parent_fh, nullptr};
+ if (do_create) {
+ ret = rgw_mkdir(fs, dir.parent_fh, dir.name.c_str(), &st, create_mask,
+ &dir.fh, RGW_MKDIR_FLAG_NONE);
+ } else {
+ ret = rgw_lookup(fs, dir.parent_fh, dir.name.c_str(), &dir.fh,
+ nullptr, 0, RGW_LOOKUP_FLAG_NONE);
+ }
+ ASSERT_EQ(ret, 0);
+ dir.sync();
+ ovec.push_back(dir);
+ if (verbose) {
+ std::cout << "create: " << dir.name << std::endl;
+ }
+ }
+}
+
+TEST(LibRGW, CHECK_PATH_REFS) {
+
+ if (!bucket_fh)
+ return;
+
+ int ix, sz = ovec.size();
+ for (ix = 0; ix < sz; ++ix) {
+ auto& dir = ovec[ix];
+ if (verbose) {
+ std::cout << "name: " << dir.name
+ << " refcnt: " << dir.rgw_fh->get_refcnt()
+ << std::endl;
+ }
+ if (ix == 0) {
+ // sentinel, +1 parent, +1 path
+ ASSERT_EQ(dir.rgw_fh->get_refcnt(), 3U);
+ }
+ if (ix == 1) {
+ // sentinel, +1 path
+ ASSERT_EQ(dir.rgw_fh->get_refcnt(), 2U);
+ }
+ }
+}
+
+TEST(LibRGW, SETXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ auto& dir = ovec[ovec.size()-1];
+ rgw_xattrstr xattr_k = { const_cast<char*>(key1.c_str()),
+ uint32_t(key1.length()) };
+ rgw_xattrstr xattr_v = { const_cast<char*>(val1.c_str()),
+ uint32_t(val1.length()) };
+
+ rgw_xattr xattr = { xattr_k, xattr_v };
+ rgw_xattrlist xattrlist = { &xattr, 1 };
+
+ int ret = rgw_setxattrs(fs, dir.fh, &xattrlist, RGW_SETXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+extern "C" {
+ static int getattr_cb(rgw_xattrlist *attrs, void *arg, uint32_t flags)
+ {
+ auto& attrmap =
+ *(static_cast<std::map<std::string, std::string>*>(arg));
+ for (uint32_t ix = 0; ix < attrs->xattr_cnt; ++ix) {
+ auto& xattr = attrs->xattrs[ix];
+ string k{xattr.key.val, xattr.key.len};
+ string v{xattr.val.val, xattr.val.len};
+ if (verbose) {
+ std::cout << __func__
+ << " attr k: " << k << " v: " << v
+ << std::endl;
+ }
+ attrmap.insert(std::map<std::string, std::string>::value_type(k, v));
+ }
+ return 0;
+ }
+}
+
+TEST(LibRGW, GETXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr xattr_k1 =
+ {const_cast<char*>(key1.c_str()), uint32_t(key1.length())};
+ rgw_xattrstr xattr_v1 = {nullptr, 0};
+
+ std::string key2 = "user.rgw.etag";
+ rgw_xattrstr xattr_k2 =
+ {const_cast<char*>(key2.c_str()), uint32_t(key2.length())};
+ rgw_xattrstr xattr_v2 = {nullptr, 0};
+
+ rgw_xattr xattrs[2] = {{xattr_k1, xattr_v1},
+ {xattr_k2, xattr_v2}};
+
+ /* XXX gcc won't accept static_cast here, don't see why */
+ rgw_xattrlist xattrlist = {reinterpret_cast<rgw_xattr*>(&xattrs), 2};
+
+ std::map<std::string, std::string> out_attrmap;
+
+ int ret = rgw_getxattrs(fs, dir.fh, &xattrlist, getattr_cb, &out_attrmap,
+ RGW_GETXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ /* check exposed attrs */
+ ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
+ /* check our user attr */
+ ASSERT_TRUE(out_attrmap.find(key1) != out_attrmap.end());
+}
+
+TEST(LibRGW, LSXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr filter_prefix = { nullptr, 0}; // XXX ignored, for now
+
+ std::map<std::string, std::string> out_attrmap;
+
+ int ret = rgw_lsxattrs(fs, dir.fh, &filter_prefix, getattr_cb,
+ &out_attrmap, RGW_LSXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+
+ /* check exposed attrs */
+ ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
+}
+
+TEST(LibRGW, RMXATTR1) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr xattr_k = { const_cast<char*>(key1.c_str()),
+ uint32_t(key1.length()) };
+ rgw_xattrstr xattr_v = { nullptr, 0 };
+
+ rgw_xattr xattr = { xattr_k, xattr_v };
+ rgw_xattrlist xattrlist = { &xattr, 1 };
+
+ int ret = rgw_rmxattrs(fs, dir.fh, &xattrlist, RGW_RMXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, LSXATTR2) {
+
+ if (!bucket_fh)
+ return;
+
+ using std::get;
+ auto& dir = ovec[ovec.size()-1];
+
+ rgw_xattrstr filter_prefix = { nullptr, 0 }; // XXX ignored, for now
+
+ std::map<std::string, std::string> out_attrmap;
+
+ int ret = rgw_lsxattrs(fs, dir.fh, &filter_prefix, getattr_cb,
+ &out_attrmap, RGW_LSXATTR_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+ /* check exposed attrs */
+ ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
+ /* verify deletion */
+ ASSERT_TRUE(out_attrmap.find(key1) == out_attrmap.end());
+}
+
+TEST(LibRGW, CLEANUP) {
+ int ret = 0;
+ if (object_fh) {
+ ret = rgw_fh_rele(fs, object_fh, 0 /* flags */);
+ ASSERT_EQ(ret, 0);
+ }
+ if (bucket_fh) {
+ ret = rgw_fh_rele(fs, bucket_fh, 0 /* flags */);
+ }
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, UMOUNT) {
+ if (! fs)
+ return;
+
+ int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(LibRGW, SHUTDOWN) {
+ librgw_shutdown(rgw_h);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ char* v = getenv("AWS_ACCESS_KEY_ID");
+ if (v) {
+ access_key = v;
+ }
+
+ v = getenv("AWS_SECRET_ACCESS_KEY");
+ if (v) {
+ secret_key = v;
+ }
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
+ (char*) nullptr)) {
+ secret_key = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
+ (char*) nullptr)) {
+ uid = val;
+ } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
+ (char*) nullptr)) {
+ bucket_name = val;
+ } else if (ceph_argparse_flag(args, arg_iter, "--stat",
+ (char*) nullptr)) {
+ do_stat = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--create",
+ (char*) nullptr)) {
+ do_create = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--delete",
+ (char*) nullptr)) {
+ do_delete = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--hexdump",
+ (char*) nullptr)) {
+ do_hexdump = true;
+ } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
+ (char*) nullptr)) {
+ verbose = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* dont accidentally run as anonymous */
+ if ((access_key == "") ||
+ (secret_key == "")) {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/mds/CMakeLists.txt b/src/test/mds/CMakeLists.txt
new file mode 100644
index 000000000..857b205e1
--- /dev/null
+++ b/src/test/mds/CMakeLists.txt
@@ -0,0 +1,16 @@
+# unittest_mds_authcap
+add_executable(unittest_mds_authcap
+ TestMDSAuthCaps.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_mds_authcap)
+target_link_libraries(unittest_mds_authcap mds global ${BLKID_LIBRARIES})
+
+# unittest_mds_sessionfilter
+add_executable(unittest_mds_sessionfilter
+ TestSessionFilter.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_mds_sessionfilter)
+target_link_libraries(unittest_mds_sessionfilter mds osdc ceph-common global ${BLKID_LIBRARIES})
+
diff --git a/src/test/mds/TestMDSAuthCaps.cc b/src/test/mds/TestMDSAuthCaps.cc
new file mode 100644
index 000000000..a05f16027
--- /dev/null
+++ b/src/test/mds/TestMDSAuthCaps.cc
@@ -0,0 +1,312 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2012 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+
+#include "include/stringify.h"
+#include "mds/MDSAuthCaps.h"
+
+#include "gtest/gtest.h"
+
+using namespace std;
+
+entity_addr_t addr;
+
+const char *parse_good[] = {
+ "allow rw uid=1 gids=1",
+ "allow * path=\"/foo\"",
+ "allow * path=/foo",
+ "allow * path=/foo-bar_baz",
+ "allow * path=\"/foo bar/baz\"",
+ "allow * uid=1",
+ "allow * path=\"/foo\" uid=1",
+ "allow *",
+ "allow r",
+ "allow rw",
+ "allow rw uid=1 gids=1,2,3",
+ "allow rw path=/foo uid=1 gids=1,2,3",
+ "allow r, allow rw path=/foo",
+ "allow r, allow * uid=1",
+ "allow r ,allow * uid=1",
+ "allow r ;allow * uid=1",
+ "allow r ; allow * uid=1",
+ "allow r ; allow * uid=1",
+ "allow r uid=1 gids=1,2,3, allow * uid=2",
+ "allow r network 1.2.3.4/8",
+ "allow rw path=/foo uid=1 gids=1,2,3 network 2.3.4.5/16",
+ "allow r root_squash",
+ "allow rw path=/foo root_squash",
+ "allow rw fsname=a root_squash",
+ "allow rw fsname=a path=/foo root_squash",
+ "allow rw fsname=a root_squash, allow rwp fsname=a path=/volumes",
+ 0
+};
+
+TEST(MDSAuthCaps, ParseGood) {
+ for (int i=0; parse_good[i]; i++) {
+ string str = parse_good[i];
+ MDSAuthCaps cap;
+ std::cout << "Testing good input: '" << str << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(str, &cout));
+ }
+}
+
+const char *parse_bad[] = {
+ "allow r poolfoo",
+ "allow r w",
+ "ALLOW r",
+ "allow w",
+ "allow rwx,",
+ "allow rwx x",
+ "allow r pool foo r",
+ "allow wwx pool taco",
+ "allow wwx pool taco^funny&chars",
+ "allow rwx pool 'weird name''",
+ "allow rwx object_prefix \"beforepool\" pool weird",
+ "allow rwx auid 123 pool asdf",
+ "allow xrwx pool foo,, allow r pool bar",
+ ";allow rwx pool foo rwx ; allow r pool bar",
+ "allow rwx pool foo ;allow r pool bar gibberish",
+ "allow rwx auid 123 pool asdf namespace=foo",
+ "allow rwx auid 123 namespace",
+ "allow rwx namespace",
+ "allow namespace",
+ "allow namespace=foo",
+ "allow rwx auid 123 namespace asdf",
+ "allow wwx pool ''",
+ "allow rw gids=1",
+ "allow rw gids=1,2,3",
+ "allow rw uid=123 gids=asdf",
+ "allow rw uid=123 gids=1,2,asdf",
+ 0
+};
+
+TEST(MDSAuthCaps, ParseBad) {
+ for (int i=0; parse_bad[i]; i++) {
+ string str = parse_bad[i];
+ MDSAuthCaps cap;
+ std::cout << "Testing bad input: '" << str << "'" << std::endl;
+ ASSERT_FALSE(cap.parse(str, &cout));
+ }
+}
+
+TEST(MDSAuthCaps, AllowAll) {
+ MDSAuthCaps cap;
+ ASSERT_FALSE(cap.allow_all());
+
+ ASSERT_TRUE(cap.parse("allow r", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap = MDSAuthCaps();
+
+ ASSERT_TRUE(cap.parse("allow rw", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap = MDSAuthCaps();
+
+ ASSERT_TRUE(cap.parse("allow", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap = MDSAuthCaps();
+
+ ASSERT_TRUE(cap.parse("allow *", NULL));
+ ASSERT_TRUE(cap.allow_all());
+ ASSERT_TRUE(cap.is_capable("foo/bar", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+}
+
+TEST(MDSAuthCaps, AllowUid) {
+ MDSAuthCaps cap;
+ ASSERT_TRUE(cap.parse("allow * uid=10", NULL));
+ ASSERT_FALSE(cap.allow_all());
+
+ // uid/gid must be valid
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 10, 0, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 12, 12, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 10, 13, NULL, MAY_READ, 0, 0, addr));
+}
+
+TEST(MDSAuthCaps, AllowUidGid) {
+ MDSAuthCaps cap;
+ ASSERT_TRUE(cap.parse("allow * uid=10 gids=10,11,12; allow * uid=12 gids=12,10", NULL));
+ ASSERT_FALSE(cap.allow_all());
+
+ // uid/gid must be valid
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 10, 0, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 9, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 12, 12, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 10, 13, NULL, MAY_READ, 0, 0, addr));
+
+ // user
+ ASSERT_TRUE(cap.is_capable("foo", 10, 10, 0500, 10, 11, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 10, 10, 0500, 10, 11, NULL, MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 10, 10, 0500, 10, 11, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 10, 10, 0700, 10, 11, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 10, 10, 0700, 10, 11, NULL, MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 10, 10, 0700, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 10, 0, 0700, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 12, 0, 0700, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 12, 0, 0700, 12, 12, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0700, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+
+ // group
+ vector<uint64_t> glist10;
+ glist10.push_back(10);
+ vector<uint64_t> dglist10;
+ dglist10.push_back(8);
+ dglist10.push_back(10);
+ vector<uint64_t> glist11;
+ glist11.push_back(11);
+ vector<uint64_t> glist12;
+ glist12.push_back(12);
+ ASSERT_TRUE(cap.is_capable("foo", 0, 10, 0750, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 10, 0750, 10, 10, NULL, MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 10, 0770, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 10, 0770, 10, 11, &glist10, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 11, 0770, 10, 10, &glist11, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 11, 0770, 10, 11, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 12, 0770, 12, 12, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 10, 0770, 12, 12, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 10, 0770, 12, 12, &glist10, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 10, 0770, 12, 12, &dglist10, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 11, 0770, 12, 12, &glist11, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 12, 0770, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 12, 0770, 10, 10, &glist12, MAY_READ | MAY_WRITE, 0, 0, addr));
+
+ // user > group
+ ASSERT_TRUE(cap.is_capable("foo", 10, 10, 0570, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 10, 10, 0570, 10, 10, NULL, MAY_WRITE, 0, 0, addr));
+
+ // other
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0775, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0770, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0775, 10, 10, NULL, MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0775, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0773, 10, 10, NULL, MAY_READ, 0, 0, addr));
+
+ // group > other
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0557, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 10, 0557, 10, 10, NULL, MAY_WRITE, 0, 0, addr));
+
+ // user > other
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0557, 10, 10, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 10, 0, 0557, 10, 10, NULL, MAY_WRITE, 0, 0, addr));
+}
+
+TEST(MDSAuthCaps, AllowPath) {
+ MDSAuthCaps cap;
+ ASSERT_TRUE(cap.parse("allow * path=/sandbox", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ ASSERT_TRUE(cap.is_capable("sandbox/foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(cap.is_capable("sandbox", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("sandboxed", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+}
+
+TEST(MDSAuthCaps, AllowPathChars) {
+ MDSAuthCaps unquo_cap;
+ ASSERT_TRUE(unquo_cap.parse("allow * path=/sandbox-._foo", NULL));
+ ASSERT_FALSE(unquo_cap.allow_all());
+ ASSERT_TRUE(unquo_cap.is_capable("sandbox-._foo/foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(unquo_cap.is_capable("sandbox", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(unquo_cap.is_capable("sandbox-._food", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(unquo_cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+}
+
+
+TEST(MDSAuthCaps, AllowPathCharsQuoted) {
+ MDSAuthCaps quo_cap;
+ ASSERT_TRUE(quo_cap.parse("allow * path=\"/sandbox-._foo\"", NULL));
+ ASSERT_FALSE(quo_cap.allow_all());
+ ASSERT_TRUE(quo_cap.is_capable("sandbox-._foo/foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(quo_cap.is_capable("sandbox", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(quo_cap.is_capable("sandbox-._food", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(quo_cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+}
+
+TEST(MDSAuthCaps, RootSquash) {
+ MDSAuthCaps rs_cap;
+ ASSERT_TRUE(rs_cap.parse("allow rw root_squash, allow rw path=/sandbox", NULL));
+ ASSERT_TRUE(rs_cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ, 0, 0, addr));
+ ASSERT_TRUE(rs_cap.is_capable("foo", 0, 0, 0777, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_FALSE(rs_cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(rs_cap.is_capable("sandbox", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(rs_cap.is_capable("sandbox/foo", 0, 0, 0777, 0, 0, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+ ASSERT_TRUE(rs_cap.is_capable("sandbox/foo", 0, 0, 0777, 10, 10, NULL, MAY_READ | MAY_WRITE, 0, 0, addr));
+}
+
+TEST(MDSAuthCaps, OutputParsed) {
+ struct CapsTest {
+ const char *input;
+ const char *output;
+ };
+ CapsTest test_values[] = {
+ {"allow",
+ "MDSAuthCaps[allow rwps]"},
+ {"allow *",
+ "MDSAuthCaps[allow *]"},
+ {"allow r",
+ "MDSAuthCaps[allow r]"},
+ {"allow rw",
+ "MDSAuthCaps[allow rw]"},
+ {"allow * uid=1",
+ "MDSAuthCaps[allow * uid=1]"},
+ {"allow * uid=1 gids=1",
+ "MDSAuthCaps[allow * uid=1 gids=1]"},
+ {"allow * uid=1 gids=1,2,3",
+ "MDSAuthCaps[allow * uid=1 gids=1,2,3]"},
+ {"allow * path=/foo",
+ "MDSAuthCaps[allow * path=\"/foo\"]"},
+ {"allow * path=\"/foo\"",
+ "MDSAuthCaps[allow * path=\"/foo\"]"},
+ {"allow rw root_squash",
+ "MDSAuthCaps[allow rw root_squash]"},
+ {"allow rw fsname=a root_squash",
+ "MDSAuthCaps[allow rw fsname=a root_squash]"},
+ {"allow * path=\"/foo\" root_squash",
+ "MDSAuthCaps[allow * path=\"/foo\" root_squash]"},
+ {"allow * path=\"/foo\" uid=1",
+ "MDSAuthCaps[allow * path=\"/foo\" uid=1]"},
+ {"allow * path=\"/foo\" uid=1 gids=1,2,3",
+ "MDSAuthCaps[allow * path=\"/foo\" uid=1 gids=1,2,3]"},
+ {"allow r uid=1 gids=1,2,3, allow * uid=2",
+ "MDSAuthCaps[allow r uid=1 gids=1,2,3, allow * uid=2]"},
+ {"allow r uid=1 gids=1,2,3, allow * uid=2 network 10.0.0.0/8",
+ "MDSAuthCaps[allow r uid=1 gids=1,2,3, allow * uid=2 network 10.0.0.0/8]"},
+ {"allow rw fsname=b, allow rw fsname=a root_squash",
+ "MDSAuthCaps[allow rw fsname=b, allow rw fsname=a root_squash]"},
+ };
+ size_t num_tests = sizeof(test_values) / sizeof(*test_values);
+ for (size_t i = 0; i < num_tests; ++i) {
+ MDSAuthCaps cap;
+ std::cout << "Testing input '" << test_values[i].input << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(test_values[i].input, &cout));
+ ASSERT_EQ(test_values[i].output, stringify(cap));
+ }
+}
+
+TEST(MDSAuthCaps, network) {
+ entity_addr_t a, b, c;
+ a.parse("10.1.2.3");
+ b.parse("192.168.2.3");
+ c.parse("192.167.2.3");
+
+ MDSAuthCaps cap;
+ ASSERT_TRUE(cap.parse("allow * network 192.168.0.0/16, allow * network 10.0.0.0/8", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ, 0, 0, a));
+ ASSERT_TRUE(cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ, 0, 0, b));
+ ASSERT_FALSE(cap.is_capable("foo", 0, 0, 0777, 0, 0, NULL, MAY_READ, 0, 0, c));
+}
diff --git a/src/test/mds/TestSessionFilter.cc b/src/test/mds/TestSessionFilter.cc
new file mode 100644
index 000000000..9fa33f451
--- /dev/null
+++ b/src/test/mds/TestSessionFilter.cc
@@ -0,0 +1,142 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2012 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+
+#include "include/stringify.h"
+#include "mds/SessionMap.h"
+
+#include "gtest/gtest.h"
+
+typedef std::vector<std::string> args_eg;
+typedef std::vector<args_eg> args_eg_set;
+
+TEST(MDSSessionFilter, ParseGood)
+{
+ args_eg_set examples = {
+ {"id=34"},
+ {"auth_name=foxtrot"},
+ {"state=reconnecting"},
+ {"reconnecting=true"},
+ {"client_metadata.root=/foo/bar"},
+ {},
+ {"id=123"},
+ {"id=34", "client_metadata.root=/foo/bar", "auth_name=foxtrot",
+ "state=reconnecting", "reconnecting=true"}
+ };
+
+ for (auto ex : examples) {
+ SessionFilter f;
+ std::stringstream ss;
+
+ std::cout << "Testing '" << ex << "'" << std::endl;
+ int r = f.parse(ex, &ss);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(ss.str().empty());
+ }
+}
+
+TEST(MDSSessionFilter, ParseBad)
+{
+ args_eg_set examples = {
+ {"rhubarb"},
+ {"id="},
+ {"id=custard"},
+ {"=custard"},
+ {"reconnecting=MAYBE"},
+ {"reconnecting=2"}
+ };
+
+ for (auto ex : examples) {
+ SessionFilter f;
+ std::stringstream ss;
+
+ std::cout << "Testing '" << ex << "'" << std::endl;
+ int r = f.parse(ex, &ss);
+ ASSERT_EQ(r, -EINVAL);
+ ASSERT_FALSE(ss.str().empty());
+ }
+}
+
+TEST(MDSSessionFilter, IdEquality)
+{
+ SessionFilter filter;
+ std::stringstream ss;
+ filter.parse({"id=123"}, &ss);
+ auto a = ceph::make_ref<Session>(nullptr);;
+ auto b = ceph::make_ref<Session>(nullptr);;
+ a->info.inst.name.parse("client.123");
+ b->info.inst.name.parse("client.456");
+
+ ASSERT_TRUE(filter.match(*a, [](client_t c) -> bool {return false;}));
+ ASSERT_FALSE(filter.match(*b, [](client_t c) -> bool {return false;}));
+}
+
+TEST(MDSSessionFilter, StateEquality)
+{
+ SessionFilter filter;
+ std::stringstream ss;
+ filter.parse({"state=closing"}, &ss);
+ auto a = ceph::make_ref<Session>(nullptr);
+ a->set_state(Session::STATE_CLOSING);
+ auto b = ceph::make_ref<Session>(nullptr);
+ b->set_state(Session::STATE_OPENING);
+
+ ASSERT_TRUE(filter.match(*a, [](client_t c) -> bool {return false;}));
+ ASSERT_FALSE(filter.match(*b, [](client_t c) -> bool {return false;}));
+}
+
+TEST(MDSSessionFilter, AuthEquality)
+{
+ SessionFilter filter;
+ std::stringstream ss;
+ filter.parse({"auth_name=rhubarb"}, &ss);
+ auto a = ceph::make_ref<Session>(nullptr);
+ a->info.auth_name.set_id("rhubarb");
+ auto b = ceph::make_ref<Session>(nullptr);
+ b->info.auth_name.set_id("custard");
+
+ ASSERT_TRUE(filter.match(*a, [](client_t c) -> bool {return false;}));
+ ASSERT_FALSE(filter.match(*b, [](client_t c) -> bool {return false;}));
+}
+
+TEST(MDSSessionFilter, MetadataEquality)
+{
+ SessionFilter filter;
+ std::stringstream ss;
+ int r = filter.parse({"client_metadata.root=/rhubarb"}, &ss);
+ ASSERT_EQ(r, 0);
+ client_metadata_t meta;
+ auto a = ceph::make_ref<Session>(nullptr);
+ meta.kv_map = {{"root", "/rhubarb"}};
+ a->set_client_metadata(meta);
+ auto b = ceph::make_ref<Session>(nullptr);
+ meta.kv_map = {{"root", "/custard"}};
+ b->set_client_metadata(meta);
+
+ ASSERT_TRUE(filter.match(*a, [](client_t c) -> bool {return false;}));
+ ASSERT_FALSE(filter.match(*b, [](client_t c) -> bool {return false;}));
+}
+
+TEST(MDSSessionFilter, ReconnectingEquality)
+{
+ SessionFilter filter;
+ std::stringstream ss;
+ int r = filter.parse({"reconnecting=true"}, &ss);
+ ASSERT_EQ(r, 0);
+ auto a = ceph::make_ref<Session>(nullptr);
+
+ ASSERT_TRUE(filter.match(*a, [](client_t c) -> bool {return true;}));
+ ASSERT_FALSE(filter.match(*a, [](client_t c) -> bool {return false;}));
+}
diff --git a/src/test/memuse/test_pool_memuse.sh b/src/test/memuse/test_pool_memuse.sh
new file mode 100755
index 000000000..5d6d6bca1
--- /dev/null
+++ b/src/test/memuse/test_pool_memuse.sh
@@ -0,0 +1,19 @@
+#! /bin/sh -x
+
+#
+# Create a bunch of pools in parallel
+# This test isn't very smart -- run it from your src dir.
+#
+
+set -e
+
+CEPH_NUM_MON=1 CEPH_NUM_MDS=1 CEPH_NUM_OSD=$2 ./vstart.sh -n -d --valgrind_osd 'massif'
+
+for i in `seq 0 $1`; do
+ for j in `seq 0 9`; do
+ poolnum=$((i*10+j))
+ poolname="pool$poolnum"
+ ./ceph osd pool create $poolname 8 &
+ done
+ wait
+done
diff --git a/src/test/memuse/test_pool_memuse_tcmalloc.sh b/src/test/memuse/test_pool_memuse_tcmalloc.sh
new file mode 100755
index 000000000..ab8baaaf7
--- /dev/null
+++ b/src/test/memuse/test_pool_memuse_tcmalloc.sh
@@ -0,0 +1,25 @@
+#! /bin/sh -x
+
+#
+# Create a bunch of pools in parallel
+# This test isn't very smart -- run it from your src dir.
+#
+
+set -e
+
+CEPH_NUM_MON=1 CEPH_NUM_MDS=1 CEPH_NUM_OSD=$2 ./vstart.sh -n -d
+
+num_osd=$2
+maxosd=$((num_osd-1))
+for osd_num in `seq 0 $maxosd`; do
+ ./ceph osd tell $osd_num start_profiler
+done
+
+for i in `seq 0 $1`; do
+ for j in `seq 0 9`; do
+ poolnum=$((i*10+j))
+ poolname="pool$poolnum"
+ ./ceph osd pool create $poolname 8 &
+ done
+ wait
+done
diff --git a/src/test/memuse/test_written_pool_memuse.sh b/src/test/memuse/test_written_pool_memuse.sh
new file mode 100755
index 000000000..db5ece731
--- /dev/null
+++ b/src/test/memuse/test_written_pool_memuse.sh
@@ -0,0 +1,12 @@
+#! /bin/sh -x
+
+set -e
+
+for i in `seq 0 $1`; do
+ for j in `seq 0 9`; do
+ poolnum=$((i*10+j))
+ poolname="pool$poolnum"
+ ./rados -p $poolname bench 1 write -t 1 &
+ done
+ wait
+done \ No newline at end of file
diff --git a/src/test/memuse/test_written_pool_memuse_tcmalloc.sh b/src/test/memuse/test_written_pool_memuse_tcmalloc.sh
new file mode 100755
index 000000000..3453aabdd
--- /dev/null
+++ b/src/test/memuse/test_written_pool_memuse_tcmalloc.sh
@@ -0,0 +1,54 @@
+#!/bin/sh -x
+
+set -e
+
+num_osd=$2
+maxosd=$((num_osd-1))
+
+eval "rm out/*.heap" || echo "no heap dumps to rm"
+
+mkdir -p out/pg_stable
+for osd_num in `seq 0 $maxosd`; do
+ ./ceph osd tell $osd_num heapdump
+ sleep 1
+ eval "mv out/*.heap out/pg_stable"
+done
+
+
+for i in `seq 0 $1`; do
+ for j in `seq 0 9`; do
+ poolnum=$((i*10+j))
+ poolname="pool$poolnum"
+ ./rados -p $poolname bench 1 write -t 1 &
+ done
+ wait
+done
+
+eval "rm out/*.heap" || echo "no heap dumps to rm"
+mkdir out/one_write
+
+for osd_num in `seq 0 $maxosd`; do
+ ./ceph osd tell $osd_num heapdump
+ sleep 1
+ eval "mv out/*.heap out/one_write"
+done
+
+
+for i in `seq 0 $1`; do
+ for j in `seq 0 9`; do
+ poolnum=$((i*10+j))
+ poolname="pool$poolnum"
+ ./rados -p $poolname bench 1 write -t 4 &
+ done
+ wait
+done
+
+eval "rm out/*.heap"
+mkdir out/five_writes
+
+for osd_num in `seq 0 $maxosd`; do
+ ./ceph osd tell $osd_num heapdump
+ sleep 1
+ eval "mv out/*.heap out/five_writes"
+done
+
diff --git a/src/test/mgr/CMakeLists.txt b/src/test/mgr/CMakeLists.txt
new file mode 100644
index 000000000..169243824
--- /dev/null
+++ b/src/test/mgr/CMakeLists.txt
@@ -0,0 +1,21 @@
+# unittest_mgr_mgrcap
+add_executable(unittest_mgr_mgrcap
+ test_mgrcap.cc
+ $<TARGET_OBJECTS:mgr_cap_obj>)
+add_ceph_unittest(unittest_mgr_mgrcap)
+target_link_libraries(unittest_mgr_mgrcap global)
+
+# unittest_mgr_ttlcache
+add_executable(unittest_mgr_ttlcache test_ttlcache.cc)
+add_ceph_unittest(unittest_mgr_ttlcache)
+target_link_libraries(unittest_mgr_ttlcache
+ Python3::Python ${CMAKE_DL_LIBS} ${GSSAPI_LIBRARIES})
+
+#scripts
+if(WITH_MGR_DASHBOARD_FRONTEND)
+ if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64|arm|ARM")
+ add_ceph_test(mgr-dashboard-frontend-unittests ${CMAKE_SOURCE_DIR}/src/pybind/mgr/dashboard/run-frontend-unittests.sh)
+ endif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64|arm|ARM")
+
+ add_ceph_test(mgr-dashboard-smoke.sh ${CMAKE_CURRENT_SOURCE_DIR}/mgr-dashboard-smoke.sh)
+endif(WITH_MGR_DASHBOARD_FRONTEND)
diff --git a/src/test/mgr/mgr-dashboard-smoke.sh b/src/test/mgr/mgr-dashboard-smoke.sh
new file mode 100755
index 000000000..f50bd99c9
--- /dev/null
+++ b/src/test/mgr/mgr-dashboard-smoke.sh
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2014,2015,2017 Red Hat <contact@redhat.com>
+# Copyright (C) 2018 SUSE LLC
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+source $(dirname $0)/../detect-build-env-vars.sh
+source $CEPH_ROOT/qa/standalone/ceph-helpers.sh
+
+mon_port=$(get_unused_port)
+dashboard_port=$((mon_port+1))
+
+function run() {
+ local dir=$1
+ shift
+
+ export CEPH_MON=127.0.0.1:$mon_port
+ export CEPH_ARGS
+ CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
+ CEPH_ARGS+="--mon-initial-members=a --mon-host=$MON "
+ CEPH_ARGS+="--mgr-initial-modules=dashboard "
+ CEPH_ARGS+="--mon-host=$CEPH_MON"
+
+ setup $dir || return 1
+ TEST_dashboard $dir || return 1
+ teardown $dir || return 1
+}
+
+function TEST_dashboard() {
+ local dir=$1
+ shift
+
+ run_mon $dir a || return 1
+ timeout 30 ceph mon stat || return 1
+ ceph config-key set mgr/dashboard/x/server_port $dashboard_port
+ MGR_ARGS+="--mgr_module_path=${CEPH_ROOT}/src/pybind/mgr "
+ run_mgr $dir x ${MGR_ARGS} || return 1
+
+ tries=0
+ while [[ $tries < 30 ]] ; do
+ if [ $(ceph status -f json | jq .mgrmap.available) = "true" ]
+ then
+ break
+ fi
+ tries=$((tries+1))
+ sleep 1
+ done
+
+ DASHBOARD_ADMIN_SECRET_FILE="/tmp/dashboard-admin-secret.txt"
+ printf 'admin' > "${DASHBOARD_ADMIN_SECRET_FILE}"
+ ceph_adm dashboard ac-user-create admin -i "${DASHBOARD_ADMIN_SECRET_FILE}" --force-password
+
+ tries=0
+ while [[ $tries < 30 ]] ; do
+ if curl -c $dir/cookiefile -X POST -d '{"username":"admin","password":"admin"}' http://127.0.0.1:$dashboard_port/api/auth
+ then
+ if curl -b $dir/cookiefile -s http://127.0.0.1:$dashboard_port/api/summary | \
+ jq '.health.overall_status' | grep HEALTH_
+ then
+ break
+ fi
+ fi
+ tries=$((tries+1))
+ sleep 0.5
+ done
+}
+
+main mgr-dashboard-smoke "$@"
+
+# Local Variables:
+# compile-command: "cd ../.. ; make -j4 TESTS=test/mgr/mgr-dashboard-smoke.sh check"
+# End:
diff --git a/src/test/mgr/test_mgrcap.cc b/src/test/mgr/test_mgrcap.cc
new file mode 100644
index 000000000..279e0c28e
--- /dev/null
+++ b/src/test/mgr/test_mgrcap.cc
@@ -0,0 +1,300 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2012 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+
+#include "include/stringify.h"
+#include "mgr/MgrCap.h"
+
+#include "gtest/gtest.h"
+
+using namespace std;
+
+const char *parse_good[] = {
+
+ // MgrCapMatch
+ "allow *",
+ "allow r",
+ "allow rwx",
+ "allow r",
+ " allow rwx",
+ "allow rwx ",
+ " allow rwx ",
+ " allow\t rwx ",
+ "\tallow\nrwx\t",
+ "allow service=foo x",
+ "allow service=\"froo\" x",
+ "allow profile read-only",
+ "allow profile read-write",
+ "allow profile \"rbd-read-only\", allow *",
+ "allow command \"a b c\"",
+ "allow command abc",
+ "allow command abc with arg=foo",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz",
+ "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz",
+ "allow command abc with arg regex \"^[0-9a-z.]*$\"",
+ "allow command abc with arg regex \"\(invaluid regex\"",
+ "allow service foo x",
+ "allow service foo x; allow service bar x",
+ "allow service foo w ;allow service bar x",
+ "allow service foo w , allow service bar x",
+ "allow service foo r , allow service bar x",
+ "allow service foo_foo r, allow service bar r",
+ "allow service foo-foo r, allow service bar r",
+ "allow service \" foo \" w, allow service bar r",
+ "allow module foo x",
+ "allow module=foo x",
+ "allow module foo_foo r",
+ "allow module \" foo \" w",
+ "allow module foo with arg1=value1 x",
+ "allow command abc with arg=foo arg2=bar, allow service foo r",
+ "allow command abc.def with arg=foo arg2=bar, allow service foo r",
+ "allow command \"foo bar\" with arg=\"baz\"",
+ "allow command \"foo bar\" with arg=\"baz.xx\"",
+ "allow command \"foo bar\" with arg = \"baz.xx\"",
+ "profile crash",
+ "profile osd",
+ "profile mds",
+ "profile rbd pool=ABC namespace=NS",
+ "profile \"rbd-read-only\", profile crash",
+ "allow * network 1.2.3.4/24",
+ "allow * network ::1/128",
+ "allow * network [aa:bb::1]/128",
+ "allow service=foo x network 1.2.3.4/16",
+ "allow command abc network 1.2.3.4/8",
+ "profile crash network 1.2.3.4/32",
+ "allow profile crash network 1.2.3.4/32",
+ 0
+};
+
+TEST(MgrCap, ParseGood) {
+ for (int i=0; parse_good[i]; ++i) {
+ string str = parse_good[i];
+ MgrCap cap;
+ std::cout << "Testing good input: '" << str << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(str, &cout));
+ std::cout << " -> " << cap
+ << std::endl;
+ }
+}
+
+// these should stringify to the input value
+const char *parse_identity[] = {
+ "allow *",
+ "allow r",
+ "allow rwx",
+ "allow service foo x",
+ "profile crash",
+ "profile rbd-read-only, allow *",
+ "profile rbd namespace=NS pool=ABC",
+ "allow command abc",
+ "allow command \"a b c\"",
+ "allow command abc with arg=foo",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz",
+ "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz",
+ "allow service foo x",
+ "allow service foo x, allow service bar x",
+ "allow service foo w, allow service bar x",
+ "allow service foo r, allow service bar x",
+ "allow service foo_foo r, allow service bar r",
+ "allow service foo-foo r, allow service bar r",
+ "allow service \" foo \" w, allow service bar r",
+ "allow module foo x",
+ "allow module \" foo_foo \" r",
+ "allow module foo with arg1=value1 x",
+ "allow command abc with arg=foo arg2=bar, allow service foo r",
+ 0
+};
+
+TEST(MgrCap, ParseIdentity)
+{
+ for (int i=0; parse_identity[i]; ++i) {
+ string str = parse_identity[i];
+ MgrCap cap;
+ std::cout << "Testing good input: '" << str << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(str, &cout));
+ string out = stringify(cap);
+ ASSERT_EQ(out, str);
+ }
+}
+
+const char *parse_bad[] = {
+ "allow r foo",
+ "allow*",
+ "foo allow *",
+ "profile foo rwx",
+ "profile",
+ "profile foo bar rwx",
+ "allow profile foo rwx",
+ "allow profile",
+ "allow profile foo bar rwx",
+ "allow service bar",
+ "allow command baz x",
+ "allow r w",
+ "ALLOW r",
+ "allow rwx,",
+ "allow rwx x",
+ "allow r pool foo r",
+ "allow wwx pool taco",
+ "allow wwx pool taco^funny&chars",
+ "allow rwx pool 'weird name''",
+ "allow rwx object_prefix \"beforepool\" pool weird",
+ "allow rwx auid 123 pool asdf",
+ "allow command foo a prefix b",
+ "allow command foo with a prefixb",
+ "allow command foo with a = prefix b",
+ "allow command foo with a prefix b c",
+ 0
+};
+
+TEST(MgrCap, ParseBad) {
+ for (int i=0; parse_bad[i]; ++i) {
+ string str = parse_bad[i];
+ MgrCap cap;
+ std::cout << "Testing bad input: '" << str << "'" << std::endl;
+ ASSERT_FALSE(cap.parse(str, &cout));
+ }
+}
+
+TEST(MgrCap, AllowAll) {
+ MgrCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+
+ ASSERT_TRUE(cap.parse("allow r", nullptr));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow w", nullptr));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow x", nullptr));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rwx", nullptr));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rw", nullptr));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rx", nullptr));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow wx", nullptr));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow *", nullptr));
+ ASSERT_TRUE(cap.is_allow_all());
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, {}));
+
+ MgrCap cap2;
+ ASSERT_FALSE(cap2.is_allow_all());
+ cap2.set_allow_all();
+ ASSERT_TRUE(cap2.is_allow_all());
+}
+
+TEST(MgrCap, Network) {
+ MgrCap cap;
+ bool r = cap.parse("allow * network 192.168.0.0/16, allow * network 10.0.0.0/8", nullptr);
+ ASSERT_TRUE(r);
+
+ entity_addr_t a, b, c;
+ a.parse("10.1.2.3");
+ b.parse("192.168.2.3");
+ c.parse("192.167.2.3");
+
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, a));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, b));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, c));
+}
+
+TEST(MgrCap, CommandRegEx) {
+ MgrCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("allow command abc with arg regex \"^[0-9a-z.]*$\"",
+ nullptr));
+
+ EntityName name;
+ name.from_str("osd.123");
+ ASSERT_TRUE(cap.is_capable(nullptr, name, "", "", "abc",
+ {{"arg", "12345abcde"}}, true, true, true, {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "", "", "abc", {{"arg", "~!@#$"}},
+ true, true, true, {}));
+
+ ASSERT_TRUE(cap.parse("allow command abc with arg regex \"[*\"", nullptr));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "", "", "abc", {{"arg", ""}}, true,
+ true, true, {}));
+}
+
+TEST(MgrCap, Module) {
+ MgrCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("allow module abc r, allow module bcd w", nullptr));
+
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, true, false,
+ {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false, false,
+ {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "bcd", "", {}, true, true, false,
+ {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "bcd", "", {}, false, true, false,
+ {}));
+}
+
+TEST(MgrCap, Profile) {
+ MgrCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+
+ ASSERT_FALSE(cap.parse("profile unknown"));
+ ASSERT_FALSE(cap.parse("profile rbd invalid-key=value"));
+
+ ASSERT_TRUE(cap.parse("profile rbd", nullptr));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false,
+ false, {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
+ true, false, {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
+ false, false, {}));
+
+ ASSERT_TRUE(cap.parse("profile rbd pool=abc namespace prefix def", nullptr));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {},
+ true, true, false, {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "rbd_support", "",
+ {{"pool", "abc"}},
+ true, true, false, {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "",
+ {{"pool", "abc"}, {"namespace", "defghi"}},
+ true, true, false, {}));
+
+ ASSERT_TRUE(cap.parse("profile rbd-read-only", nullptr));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false,
+ false, {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
+ true, false, {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
+ false, false, {}));
+}
diff --git a/src/test/mgr/test_ttlcache.cc b/src/test/mgr/test_ttlcache.cc
new file mode 100644
index 000000000..a1ee0a862
--- /dev/null
+++ b/src/test/mgr/test_ttlcache.cc
@@ -0,0 +1,70 @@
+#include <iostream>
+
+#include "mgr/TTLCache.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(TTLCache, Get) {
+ TTLCache<string, int> c{100};
+ c.insert("foo", 1);
+ int foo = c.get("foo");
+ ASSERT_EQ(foo, 1);
+}
+
+TEST(TTLCache, Erase) {
+ TTLCache<string, int> c{100};
+ c.insert("foo", 1);
+ int foo = c.get("foo");
+ ASSERT_EQ(foo, 1);
+ c.erase("foo");
+ try{
+ foo = c.get("foo");
+ FAIL();
+ } catch (std::out_of_range& e) {
+ SUCCEED();
+ }
+}
+
+TEST(TTLCache, Clear) {
+ TTLCache<string, int> c{100};
+ c.insert("foo", 1);
+ c.insert("foo2", 2);
+ c.clear();
+ ASSERT_FALSE(c.size());
+}
+
+TEST(TTLCache, NoTTL) {
+ TTLCache<string, int> c{100};
+ c.insert("foo", 1);
+ int foo = c.get("foo");
+ ASSERT_EQ(foo, 1);
+ c.set_ttl(0);
+ c.insert("foo2", 2);
+ try{
+ foo = c.get("foo2");
+ FAIL();
+ } catch (std::out_of_range& e) {
+ SUCCEED();
+ }
+}
+
+TEST(TTLCache, SizeLimit) {
+ TTLCache<string, int> c{100, 2};
+ c.insert("foo", 1);
+ c.insert("foo2", 2);
+ c.insert("foo3", 3);
+ ASSERT_EQ(c.size(), 2);
+}
+
+TEST(TTLCache, HitRatio) {
+ TTLCache<string, int> c{100};
+ c.insert("foo", 1);
+ c.insert("foo2", 2);
+ c.insert("foo3", 3);
+ c.get("foo2");
+ c.get("foo3");
+ std::pair<uint64_t, uint64_t> hit_miss_ratio = c.get_hit_miss_ratio();
+ ASSERT_EQ(std::get<1>(hit_miss_ratio), 3);
+ ASSERT_EQ(std::get<0>(hit_miss_ratio), 2);
+}
diff --git a/src/test/mime.cc b/src/test/mime.cc
new file mode 100644
index 000000000..29c73e5a0
--- /dev/null
+++ b/src/test/mime.cc
@@ -0,0 +1,150 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "common/mime.h"
+#include "gtest/gtest.h"
+
+#include <stdint.h>
+#include <string>
+
+using std::string;
+
+TEST(MimeTests, SimpleEncode) {
+ char output[256];
+ memset(output, 0, sizeof(output));
+ int len;
+
+ len = mime_encode_as_qp("abc", NULL, 0);
+ ASSERT_EQ(len, 4);
+ len = mime_encode_as_qp("abc", output, 4);
+ ASSERT_EQ(len, 4);
+ ASSERT_EQ(string("abc"), string(output));
+
+ len = mime_encode_as_qp("a=b", NULL, 0);
+ ASSERT_EQ(len, 6);
+ len = mime_encode_as_qp("a=b", output, 6);
+ ASSERT_EQ(len, 6);
+ ASSERT_EQ(string("a=3Db"), string(output));
+
+ len = mime_encode_as_qp("Libert\xc3\xa9", NULL, 0);
+ ASSERT_EQ(len, 13);
+ len = mime_encode_as_qp("Libert\xc3\xa9", output, 13);
+ ASSERT_EQ(len, 13);
+ ASSERT_EQ(string("Libert=C3=A9"), string(output));
+}
+
+TEST(MimeTests, EncodeOutOfSpace) {
+ char output[256];
+ memset(output, 0, sizeof(output));
+ int len;
+
+ len = mime_encode_as_qp("abcdefg", NULL, 0);
+ ASSERT_EQ(len, 8);
+ len = mime_encode_as_qp("abcdefg", output, 4);
+ ASSERT_EQ(len, 8);
+ ASSERT_EQ(string("abc"), string(output));
+ len = mime_encode_as_qp("abcdefg", output, 1);
+ ASSERT_EQ(len, 8);
+ ASSERT_EQ(string(""), string(output));
+
+ len = mime_encode_as_qp("a=b", output, 2);
+ ASSERT_EQ(len, 6);
+ ASSERT_EQ(string("a"), string(output));
+ len = mime_encode_as_qp("a=b", output, 3);
+ ASSERT_EQ(len, 6);
+ ASSERT_EQ(string("a"), string(output));
+}
+
+TEST(MimeTests, SimpleDecode) {
+ char output[256];
+ memset(output, 0, sizeof(output));
+ int len;
+
+ len = mime_decode_from_qp("abc", NULL, 0);
+ ASSERT_EQ(len, 4);
+ len = mime_decode_from_qp("abc", output, 4);
+ ASSERT_EQ(len, 4);
+ ASSERT_EQ(string("abc"), string(output));
+
+ len = mime_decode_from_qp("a=3Db", NULL, 0);
+ ASSERT_EQ(len, 4);
+ len = mime_decode_from_qp("a=3Db", output, 4);
+ ASSERT_EQ(len, 4);
+ ASSERT_EQ(string("a=b"), string(output));
+
+ len = mime_decode_from_qp("Libert=C3=A9", NULL, 0);
+ ASSERT_EQ(len, 9);
+ len = mime_decode_from_qp("Libert=C3=A9", output, 9);
+ ASSERT_EQ(len, 9);
+ ASSERT_EQ(string("Libert\xc3\xa9"), string(output));
+}
+
+TEST(MimeTests, LowercaseDecode) {
+ char output[256];
+ memset(output, 0, sizeof(output));
+ int len;
+
+ len = mime_decode_from_qp("Libert=c3=a9", NULL, 0);
+ ASSERT_EQ(len, 9);
+ len = mime_decode_from_qp("Libert=c3=a9", output, 9);
+ ASSERT_EQ(len, 9);
+ ASSERT_EQ(string("Libert\xc3\xa9"), string(output));
+}
+
+TEST(MimeTests, DecodeOutOfSpace) {
+ char output[256];
+ memset(output, 0, sizeof(output));
+ int len;
+
+ len = mime_decode_from_qp("abcdefg", NULL, 0);
+ ASSERT_EQ(len, 8);
+ len = mime_decode_from_qp("abcdefg", output, 4);
+ ASSERT_EQ(len, 8);
+ ASSERT_EQ(string("abc"), string(output));
+ len = mime_decode_from_qp("abcdefg", output, 1);
+ ASSERT_EQ(len, 8);
+ ASSERT_EQ(string(""), string(output));
+
+ len = mime_decode_from_qp("a=3Db", output, 2);
+ ASSERT_EQ(len, 4);
+ ASSERT_EQ(string("a"), string(output));
+ len = mime_decode_from_qp("a=3Db", output, 3);
+ ASSERT_EQ(len, 4);
+ ASSERT_EQ(string("a="), string(output));
+}
+
+TEST(MimeTests, DecodeErrors) {
+ char output[128];
+ memset(output, 0, sizeof(output));
+ int len;
+
+ // incomplete escape sequence
+ len = mime_decode_from_qp("boo=", output, sizeof(output));
+ ASSERT_LT(len, 0);
+
+ // invalid escape sequences
+ len = mime_decode_from_qp("boo=gg", output, sizeof(output));
+ ASSERT_LT(len, 0);
+ len = mime_decode_from_qp("boo=g", output, sizeof(output));
+ ASSERT_LT(len, 0);
+ len = mime_decode_from_qp("boo==", output, sizeof(output));
+ ASSERT_LT(len, 0);
+ len = mime_decode_from_qp("boo=44bar=z", output, sizeof(output));
+ ASSERT_LT(len, 0);
+
+ // high bit should not be set in quoted-printable mime output
+ unsigned char bad_input2[] = { 0x81, 0x6a, 0x0 };
+ len = mime_decode_from_qp(reinterpret_cast<const char*>(bad_input2),
+ output, sizeof(output));
+ ASSERT_LT(len, 0);
+}
diff --git a/src/test/mon/CMakeLists.txt b/src/test/mon/CMakeLists.txt
new file mode 100644
index 000000000..943ca99a3
--- /dev/null
+++ b/src/test/mon/CMakeLists.txt
@@ -0,0 +1,83 @@
+# ceph_test_mon_workloadgen
+add_executable(ceph_test_mon_workloadgen
+ test_mon_workloadgen.cc
+ )
+target_link_libraries(ceph_test_mon_workloadgen
+ os
+ osdc
+ global
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+install(TARGETS ceph_test_mon_workloadgen
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_mon_msg
+add_executable(ceph_test_mon_msg
+ test-mon-msg.cc
+ )
+target_link_libraries(ceph_test_mon_msg os osdc global ${UNITTEST_LIBS})
+
+# unittest_mon_moncap
+add_executable(unittest_mon_moncap
+ moncap.cc
+ )
+add_ceph_unittest(unittest_mon_moncap)
+target_link_libraries(unittest_mon_moncap mon global)
+
+# unittest_mon_map
+add_executable(unittest_mon_monmap
+ MonMap.cc
+ )
+add_ceph_unittest(unittest_mon_monmap)
+target_link_libraries(unittest_mon_monmap mon global)
+
+# unittest_mon_pgmap
+add_executable(unittest_mon_pgmap
+ PGMap.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_mon_pgmap)
+target_link_libraries(unittest_mon_pgmap mon global)
+
+# unittest_mon_montypes
+add_executable(unittest_mon_montypes
+ test_mon_types.cc
+ )
+add_ceph_unittest(unittest_mon_montypes)
+target_link_libraries(unittest_mon_montypes mon global)
+
+# ceph_test_mon_memory_target
+add_executable(ceph_test_mon_memory_target
+ test_mon_memory_target.cc)
+target_link_libraries(ceph_test_mon_memory_target Boost::system Threads::Threads)
+set_target_properties(ceph_test_mon_memory_target PROPERTIES
+ SKIP_RPATH TRUE
+ INSTALL_RPATH "")
+install(TARGETS ceph_test_mon_memory_target
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_mon_log_rss_usage
+add_executable(ceph_test_log_rss_usage
+ test_log_rss_usage.cc)
+set_target_properties(ceph_test_log_rss_usage PROPERTIES
+ SKIP_RPATH TRUE
+ INSTALL_RPATH "")
+install(TARGETS ceph_test_log_rss_usage
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# ceph_test_mon_rss_usage
+add_executable(ceph_test_mon_rss_usage
+ test_mon_rss_usage.cc)
+set_target_properties(ceph_test_mon_rss_usage PROPERTIES
+ SKIP_RPATH TRUE
+ INSTALL_RPATH "")
+install(TARGETS ceph_test_mon_rss_usage
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+#unittest_mon_election
+add_executable(unittest_mon_election
+ test_election.cc
+ )
+add_ceph_unittest(unittest_mon_election)
+target_link_libraries(unittest_mon_election mon global)
diff --git a/src/test/mon/MonMap.cc b/src/test/mon/MonMap.cc
new file mode 100644
index 000000000..81b054a90
--- /dev/null
+++ b/src/test/mon/MonMap.cc
@@ -0,0 +1,241 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "mon/MonMap.h"
+#include "common/ceph_context.h"
+#include "common/dns_resolve.h"
+#include "test/common/dns_messages.h"
+
+#include "common/debug.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+
+#include <sstream>
+
+#define TEST_DEBUG 20
+
+#define dout_subsys ceph_subsys_mon
+
+
+using ::testing::Return;
+using ::testing::_;
+using ::testing::SetArrayArgument;
+using ::testing::DoAll;
+using ::testing::StrEq;
+
+
+class MonMapTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ g_ceph_context->_conf->subsys.set_log_level(dout_subsys, TEST_DEBUG);
+ }
+
+ virtual void TearDown() {
+ DNSResolver::get_instance(nullptr);
+ }
+};
+
+TEST_F(MonMapTest, DISABLED_build_initial_config_from_dns) {
+
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+ DNSResolver::get_instance(resolvH);
+
+ int len = sizeof(ns_search_msg_ok_payload);
+ int lena = sizeof(ns_query_msg_mon_a_payload);
+ int lenb = sizeof(ns_query_msg_mon_b_payload);
+ int lenc = sizeof(ns_query_msg_mon_c_payload);
+
+ using ::testing::InSequence;
+ {
+ InSequence s;
+
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nsearch(_, StrEq("_cephmon._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_, StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#else
+ EXPECT_CALL(*resolvH, res_search(StrEq("_cephmon._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#endif
+ }
+
+
+
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_MON);
+ cct->_conf.set_val("mon_dns_srv_name", "cephmon");
+ MonMap monmap;
+ int r = monmap.build_initial(cct.get(), false, std::cerr);
+
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(monmap.mon_info.size(), (unsigned int)3);
+ auto it = monmap.mon_info.find("mon.a");
+ ASSERT_NE(it, monmap.mon_info.end());
+ std::ostringstream os;
+ os << it->second.public_addrs;
+ ASSERT_EQ(os.str(), "192.168.1.11:6789/0");
+ os.str("");
+ it = monmap.mon_info.find("mon.b");
+ ASSERT_NE(it, monmap.mon_info.end());
+ os << it->second.public_addrs;
+ ASSERT_EQ(os.str(), "192.168.1.12:6789/0");
+ os.str("");
+ it = monmap.mon_info.find("mon.c");
+ ASSERT_NE(it, monmap.mon_info.end());
+ os << it->second.public_addrs;
+ ASSERT_EQ(os.str(), "192.168.1.13:6789/0");
+}
+
+TEST_F(MonMapTest, DISABLED_build_initial_config_from_dns_fail) {
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+ DNSResolver::get_instance(resolvH);
+
+
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nsearch(_, StrEq("_ceph-mon._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(Return(0));
+#else
+ EXPECT_CALL(*resolvH, res_search(StrEq("_ceph-mon._tcp"), C_IN, T_SRV, _, _))
+ .WillOnce(Return(0));
+#endif
+
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_MON);
+ // using default value of mon_dns_srv_name option
+ MonMap monmap;
+ int r = monmap.build_initial(cct.get(), false, std::cerr);
+
+ ASSERT_EQ(r, -ENOENT);
+ ASSERT_EQ(monmap.mon_info.size(), (unsigned int)0);
+
+}
+
+TEST_F(MonMapTest, DISABLED_build_initial_config_from_dns_with_domain) {
+
+ MockResolvHWrapper *resolvH = new MockResolvHWrapper();
+ DNSResolver::get_instance(resolvH);
+
+ int len = sizeof(ns_search_msg_ok_payload);
+ int lena = sizeof(ns_query_msg_mon_a_payload);
+ int lenb = sizeof(ns_query_msg_mon_b_payload);
+ int lenc = sizeof(ns_query_msg_mon_c_payload);
+
+ using ::testing::InSequence;
+ {
+ InSequence s;
+
+#ifdef HAVE_RES_NQUERY
+ EXPECT_CALL(*resolvH, res_nsearch(_, StrEq("_cephmon._tcp.ceph.com"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_, StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_nquery(_,StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<4>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#else
+ EXPECT_CALL(*resolvH, res_search(StrEq("_cephmon._tcp.ceph.com"), C_IN, T_SRV, _, _))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_search_msg_ok_payload,
+ ns_search_msg_ok_payload+len), Return(len)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.a.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_a_payload,
+ ns_query_msg_mon_a_payload+lena), Return(lena)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.c.ceph.com"), C_IN, T_A,_,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_c_payload,
+ ns_query_msg_mon_c_payload+lenc), Return(lenc)));
+
+ EXPECT_CALL(*resolvH, res_query(StrEq("mon.b.ceph.com"), C_IN, T_A, _,_))
+ .WillOnce(DoAll(SetArrayArgument<3>(ns_query_msg_mon_b_payload,
+ ns_query_msg_mon_b_payload+lenb), Return(lenb)));
+#endif
+ }
+
+
+
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_MON);
+ cct->_conf.set_val("mon_dns_srv_name", "cephmon_ceph.com");
+ MonMap monmap;
+ int r = monmap.build_initial(cct.get(), false, std::cerr);
+
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(monmap.mon_info.size(), (unsigned int)3);
+ auto it = monmap.mon_info.find("mon.a");
+ ASSERT_NE(it, monmap.mon_info.end());
+ std::ostringstream os;
+ os << it->second.public_addrs;
+ ASSERT_EQ(os.str(), "192.168.1.11:6789/0");
+ os.str("");
+ it = monmap.mon_info.find("mon.b");
+ ASSERT_NE(it, monmap.mon_info.end());
+ os << it->second.public_addrs;
+ ASSERT_EQ(os.str(), "192.168.1.12:6789/0");
+ os.str("");
+ it = monmap.mon_info.find("mon.c");
+ ASSERT_NE(it, monmap.mon_info.end());
+ os << it->second.public_addrs;
+ ASSERT_EQ(os.str(), "192.168.1.13:6789/0");
+}
+
+TEST(MonMapBuildInitial, build_initial_mon_host_from_dns) {
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_MON);
+ cct->_conf.set_val("mon_host", "ceph.io");
+ MonMap monmap;
+ int r = monmap.build_initial(cct.get(), false, std::cerr);
+ ASSERT_EQ(r, 0);
+ ASSERT_GE(monmap.mon_info.size(), 1u);
+ for (const auto& [name, info] : monmap.mon_info) {
+ std::cerr << info << std::endl;
+ }
+}
+
+TEST(MonMapBuildInitial, build_initial_mon_host_from_dns_fail) {
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_MON);
+ cct->_conf.set_val("mon_host", "ceph.noname");
+ MonMap monmap;
+ int r = monmap.build_initial(cct.get(), false, std::cerr);
+ ASSERT_EQ(r, -EINVAL);
+}
diff --git a/src/test/mon/PGMap.cc b/src/test/mon/PGMap.cc
new file mode 100644
index 000000000..6c052fe5f
--- /dev/null
+++ b/src/test/mon/PGMap.cc
@@ -0,0 +1,169 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Inktank <info@inktank.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include "mon/PGMap.h"
+#include "gtest/gtest.h"
+
+#include "include/stringify.h"
+
+using namespace std;
+
+namespace {
+ class CheckTextTable : public TextTable {
+ public:
+ explicit CheckTextTable(bool verbose) {
+ for (int i = 0; i < 5; i++) {
+ define_column("", TextTable::LEFT, TextTable::LEFT);
+ }
+ if (verbose) {
+ for (int i = 0; i < 9; i++) {
+ define_column("", TextTable::LEFT, TextTable::LEFT);
+ }
+ }
+ }
+ const string& get(unsigned r, unsigned c) const {
+ ceph_assert(r < row.size());
+ ceph_assert(c < row[r].size());
+ return row[r][c];
+ }
+ };
+
+ // copied from PGMap.cc
+ string percentify(float a) {
+ stringstream ss;
+ if (a < 0.01)
+ ss << "0";
+ else
+ ss << std::fixed << std::setprecision(2) << a;
+ return ss.str();
+ }
+}
+
+// dump_object_stat_sum() is called by "ceph df" command
+// with table, without formatter, verbose = true, not empty, avail > 0
+TEST(pgmap, dump_object_stat_sum_0)
+{
+ bool verbose = true;
+ CheckTextTable tbl(verbose);
+ pool_stat_t pool_stat;
+ object_stat_sum_t& sum = pool_stat.stats.sum;
+ sum.num_bytes = 42 * 1024 * 1024;
+ sum.num_objects = 42;
+ sum.num_objects_degraded = 13; // there are 13 missings + not_yet_backfilled
+ sum.num_objects_dirty = 2;
+ sum.num_rd = 100;
+ sum.num_rd_kb = 123;
+ sum.num_wr = 101;
+ sum.num_wr_kb = 321;
+ pool_stat.num_store_stats = 3;
+ store_statfs_t &statfs = pool_stat.store_stats;
+ statfs.data_stored = 40 * 1024 * 1024;
+ statfs.allocated = 41 * 1024 * 1024 * 2;
+ statfs.data_compressed_allocated = 4334;
+ statfs.data_compressed_original = 1213;
+
+ sum.calc_copies(3); // assuming we have 3 copies for each obj
+ // nominal amount of space available for new objects in this pool
+ uint64_t avail = 2016 * 1024 * 1024;
+ pg_pool_t pool;
+ pool.quota_max_objects = 2000;
+ pool.quota_max_bytes = 2000 * 1024 * 1024;
+ pool.size = 2;
+ pool.type = pg_pool_t::TYPE_REPLICATED;
+ pool.tier_of = 0;
+ PGMap::dump_object_stat_sum(tbl, nullptr, pool_stat, avail,
+ pool.get_size(), verbose, true, true, &pool);
+ float copies_rate =
+ (static_cast<float>(sum.num_object_copies - sum.num_objects_degraded) /
+ sum.num_object_copies) * pool.get_size();
+ float used_percent = (float)statfs.allocated /
+ (statfs.allocated + avail) * 100;
+ uint64_t stored = statfs.data_stored / copies_rate;
+
+ unsigned col = 0;
+ ASSERT_EQ(stringify(byte_u_t(stored)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(stored)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(si_u_t(sum.num_objects)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(statfs.allocated)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(statfs.allocated)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(percentify(used_percent), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(avail/copies_rate)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(si_u_t(pool.quota_max_objects)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(pool.quota_max_bytes)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(si_u_t(sum.num_objects_dirty)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(statfs.data_compressed_allocated)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(statfs.data_compressed_original)), tbl.get(0, col++));
+}
+
+// with table, without formatter, verbose = true, empty, avail > 0
+TEST(pgmap, dump_object_stat_sum_1)
+{
+ bool verbose = true;
+ CheckTextTable tbl(verbose);
+ pool_stat_t pool_stat;
+ object_stat_sum_t& sum = pool_stat.stats.sum; // zero by default
+ ASSERT_TRUE(sum.is_zero());
+ // nominal amount of space available for new objects in this pool
+ uint64_t avail = 2016 * 1024 * 1024;
+ pg_pool_t pool;
+ pool.quota_max_objects = 2000;
+ pool.quota_max_bytes = 2000 * 1024 * 1024;
+ pool.size = 2;
+ pool.type = pg_pool_t::TYPE_REPLICATED;
+ pool.tier_of = 0;
+ PGMap::dump_object_stat_sum(tbl, nullptr, pool_stat, avail,
+ pool.get_size(), verbose, true, true, &pool);
+ unsigned col = 0;
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(si_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(percentify(0), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(avail/pool.size)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(si_u_t(pool.quota_max_objects)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(pool.quota_max_bytes)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(si_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+}
+
+// with table, without formatter, verbose = false, empty, avail = 0
+TEST(pgmap, dump_object_stat_sum_2)
+{
+ bool verbose = false;
+ CheckTextTable tbl(verbose);
+ pool_stat_t pool_stat;
+ object_stat_sum_t& sum = pool_stat.stats.sum; // zero by default
+ ASSERT_TRUE(sum.is_zero());
+ // nominal amount of space available for new objects in this pool
+ uint64_t avail = 0;
+ pg_pool_t pool;
+ pool.quota_max_objects = 2000;
+ pool.quota_max_bytes = 2000 * 1024 * 1024;
+ pool.size = 2;
+ pool.type = pg_pool_t::TYPE_REPLICATED;
+
+ PGMap::dump_object_stat_sum(tbl, nullptr, pool_stat, avail,
+ pool.get_size(), verbose, true, true, &pool);
+ unsigned col = 0;
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(si_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
+ ASSERT_EQ(percentify(0), tbl.get(0, col++));
+ ASSERT_EQ(stringify(byte_u_t(avail/pool.size)), tbl.get(0, col++));
+}
diff --git a/src/test/mon/bench_auth.py b/src/test/mon/bench_auth.py
new file mode 100755
index 000000000..5242f6892
--- /dev/null
+++ b/src/test/mon/bench_auth.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python3
+
+import argparse
+import copy
+import json
+import rados
+import time
+import multiprocessing
+
+caps_base = ["mon", "profile rbd", "osd", "profile rbd pool=rbd namespace=test"]
+
+def create_users(conn, num_namespaces, num_users):
+ cmd = {'prefix': 'auth get-or-create'}
+
+ for i in range(num_namespaces):
+ caps_base[-1] += ", profile rbd pool=rbd namespace=namespace{}".format(i)
+
+ cmd['caps'] = caps_base
+ for i in range(num_users):
+ cmd['entity'] = "client.{}".format(i)
+ conn.mon_command(json.dumps(cmd), b'')
+
+class Worker(multiprocessing.Process):
+ def __init__(self, conn, num, queue, duration):
+ super().__init__()
+ self.conn = conn
+ self.num = num
+ self.queue = queue
+ self.duration = duration
+
+ def run(self):
+ client = "client.{}".format(self.num)
+ cmd = {'prefix': 'auth caps', 'entity': client}
+ start_time = time.time()
+ num_complete = 0
+ with rados.Rados(conffile='') as conn:
+ while True:
+ now = time.time()
+ diff = now - start_time
+ if diff > self.duration:
+ self.queue.put((num_complete, diff))
+ return
+ caps = copy.deepcopy(caps_base)
+ caps[-1] += ", profile rbd pool=rbd namespace=namespace{}".format(self.num * 10000 + num_complete)
+ cmd['caps'] = caps
+ cmd_start = time.time()
+ ret, buf, out = conn.mon_command(json.dumps(cmd), b'')
+ cmd_end = time.time()
+ if ret != 0:
+ self.queue.put((Exception("{0}: {1}".format(ret, out)), 0))
+ return
+ num_complete += 1
+ print("Process {} finished op {} - latency: {}".format(self.num, num_complete, cmd_end - cmd_start))
+
+def main():
+ parser = argparse.ArgumentParser(description="""
+Benchmark updates to ceph users' capabilities. Run one update at a time in each thread.
+""")
+ parser.add_argument(
+ '-n', '--num-namespaces',
+ type=int,
+ default=300,
+ help='number of namespaces per user',
+ )
+ parser.add_argument(
+ '-t', '--threads',
+ type=int,
+ default=10,
+ help='number of threads (and thus parallel operations) to use',
+ )
+ parser.add_argument(
+ '-d', '--duration',
+ type=int,
+ default=30,
+ help='how long to run, in seconds',
+ )
+ args = parser.parse_args()
+ num_namespaces = args.num_namespaces
+ num_threads = args.threads
+ duration = args.duration
+ workers = []
+ results = []
+ q = multiprocessing.Queue()
+ with rados.Rados(conffile=rados.Rados.DEFAULT_CONF_FILES) as conn:
+ create_users(conn, num_namespaces, num_threads)
+ for i in range(num_threads):
+ workers.append(Worker(conn, i, q, duration))
+ workers[-1].start()
+ for i in range(num_threads):
+ num_complete, seconds = q.get()
+ if isinstance(num_complete, Exception):
+ raise num_complete
+ results.append((num_complete, seconds))
+ total = 0
+ total_rate = 0
+ for num, sec in results:
+ print("Completed {} in {} ({} / s)".format(num, sec, num / sec))
+ total += num
+ total_rate += num / sec
+
+ print("Total: ", total)
+ print("Avg rate: ", total_rate / len(results))
+
+if __name__ == '__main__':
+ main()
diff --git a/src/test/mon/moncap.cc b/src/test/mon/moncap.cc
new file mode 100644
index 000000000..dd86224bd
--- /dev/null
+++ b/src/test/mon/moncap.cc
@@ -0,0 +1,378 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2012 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+
+#include "include/stringify.h"
+#include "mon/MonCap.h"
+
+#include "gtest/gtest.h"
+
+using namespace std;
+
+const char *parse_good[] = {
+
+ // MonCapMatch
+ "allow *",
+ "allow r",
+ "allow rwx",
+ "allow r",
+ " allow rwx",
+ "allow rwx ",
+ " allow rwx ",
+ " allow\t rwx ",
+ "\tallow\nrwx\t",
+ "allow service=foo x",
+ "allow service=\"froo\" x",
+ "allow profile osd",
+ "allow profile osd-bootstrap",
+ "allow profile \"mds-bootstrap\", allow *",
+ "allow command \"a b c\"",
+ "allow command abc",
+ "allow command abc with arg=foo",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz",
+ "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz",
+ "allow command abc with arg regex \"^[0-9a-z.]*$\"",
+ "allow command abc with arg regex \"\(invaluid regex\"",
+ "allow service foo x",
+ "allow service foo x; allow service bar x",
+ "allow service foo w ;allow service bar x",
+ "allow service foo w , allow service bar x",
+ "allow service foo r , allow service bar x",
+ "allow service foo_foo r, allow service bar r",
+ "allow service foo-foo r, allow service bar r",
+ "allow service \" foo \" w, allow service bar r",
+ "allow command abc with arg=foo arg2=bar, allow service foo r",
+ "allow command abc.def with arg=foo arg2=bar, allow service foo r",
+ "allow command \"foo bar\" with arg=\"baz\"",
+ "allow command \"foo bar\" with arg=\"baz.xx\"",
+ "profile osd",
+ "profile \"mds-bootstrap\", profile foo",
+ "allow * network 1.2.3.4/24",
+ "allow * network ::1/128",
+ "allow * network [aa:bb::1]/128",
+ "allow service=foo x network 1.2.3.4/16",
+ "allow command abc network 1.2.3.4/8",
+ "profile osd network 1.2.3.4/32",
+ "allow profile mon network 1.2.3.4/32",
+ 0
+};
+
+TEST(MonCap, ParseGood) {
+ for (int i=0; parse_good[i]; ++i) {
+ string str = parse_good[i];
+ MonCap cap;
+ std::cout << "Testing good input: '" << str << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(str, &cout));
+ std::cout << " -> " << cap << std::endl;
+ }
+}
+
+// these should stringify to the input value
+const char *parse_identity[] = {
+ "allow *",
+ "allow r",
+ "allow rwx",
+ "allow service foo x",
+ "allow profile osd",
+ "allow profile osd-bootstrap",
+ "allow profile mds-bootstrap, allow *",
+ "allow profile \"foo bar\", allow *",
+ "allow command abc",
+ "allow command \"a b c\"",
+ "allow command abc with arg=foo",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2=bar",
+ "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz",
+ "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz",
+ "allow service foo x",
+ "allow service foo x, allow service bar x",
+ "allow service foo w, allow service bar x",
+ "allow service foo r, allow service bar x",
+ "allow service foo_foo r, allow service bar r",
+ "allow service foo-foo r, allow service bar r",
+ "allow service \" foo \" w, allow service bar r",
+ "allow command abc with arg=foo arg2=bar, allow service foo r",
+ 0
+};
+
+TEST(MonCap, ParseIdentity)
+{
+ for (int i=0; parse_identity[i]; ++i) {
+ string str = parse_identity[i];
+ MonCap cap;
+ std::cout << "Testing good input: '" << str << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(str, &cout));
+ string out = stringify(cap);
+ ASSERT_EQ(out, str);
+ }
+}
+
+const char *parse_bad[] = {
+ "allow r foo",
+ "allow*",
+ "foo allow *",
+ "allow profile foo rwx",
+ "allow profile",
+ "allow profile foo bar rwx",
+ "allow service bar",
+ "allow command baz x",
+ "allow r w",
+ "ALLOW r",
+ "allow rwx,",
+ "allow rwx x",
+ "allow r pool foo r",
+ "allow wwx pool taco",
+ "allow wwx pool taco^funny&chars",
+ "allow rwx pool 'weird name''",
+ "allow rwx object_prefix \"beforepool\" pool weird",
+ "allow rwx auid 123 pool asdf",
+ "allow command foo a prefix b",
+ "allow command foo with a prefixb",
+ "allow command foo with a = prefix b",
+ "allow command foo with a prefix b c",
+ 0
+};
+
+TEST(MonCap, ParseBad) {
+ for (int i=0; parse_bad[i]; ++i) {
+ string str = parse_bad[i];
+ MonCap cap;
+ std::cout << "Testing bad input: '" << str << "'" << std::endl;
+ ASSERT_FALSE(cap.parse(str, &cout));
+ }
+}
+
+TEST(MonCap, AllowAll) {
+ MonCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+
+ ASSERT_TRUE(cap.parse("allow r", NULL));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow w", NULL));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow x", NULL));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rwx", NULL));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rw", NULL));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rx", NULL));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow wx", NULL));
+ ASSERT_FALSE(cap.is_allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow *", NULL));
+ ASSERT_TRUE(cap.is_allow_all());
+ ASSERT_TRUE(cap.is_capable(NULL, {}, "foo", "asdf", {}, true, true, true,
+ {}));
+
+ MonCap cap2;
+ ASSERT_FALSE(cap2.is_allow_all());
+ cap2.set_allow_all();
+ ASSERT_TRUE(cap2.is_allow_all());
+}
+
+TEST(MonCap, Network) {
+ MonCap cap;
+ bool r = cap.parse("allow * network 192.168.0.0/16, allow * network 10.0.0.0/8", NULL);
+ ASSERT_TRUE(r);
+
+ entity_addr_t a, b, c;
+ a.parse("10.1.2.3");
+ b.parse("192.168.2.3");
+ c.parse("192.167.2.3");
+
+ ASSERT_TRUE(cap.is_capable(NULL, {}, "foo", "asdf", {}, true, true, true,
+ a));
+ ASSERT_TRUE(cap.is_capable(NULL, {}, "foo", "asdf", {}, true, true, true,
+ b));
+ ASSERT_FALSE(cap.is_capable(NULL, {}, "foo", "asdf", {}, true, true, true,
+ c));
+}
+
+TEST(MonCap, ProfileOSD) {
+ MonCap cap;
+ bool r = cap.parse("allow profile osd", NULL);
+ ASSERT_TRUE(r);
+
+ EntityName name;
+ name.from_str("osd.123");
+ map<string,string> ca;
+
+ ASSERT_TRUE(cap.is_capable(NULL, name, "osd", "", ca, true, false, false,
+ {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "osd", "", ca, true, true, false, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "osd", "", ca, true, true, true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "osd", "", ca, true, true, true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "mon", "", ca, true, false, false,
+ {}));
+
+ ASSERT_FALSE(cap.is_capable(NULL, name, "mds", "", ca, true, true, true, {}));
+ ASSERT_FALSE(cap.is_capable(NULL, name, "mon", "", ca, true, true, true, {}));
+
+ ca.clear();
+ ASSERT_FALSE(cap.is_capable(NULL, name, "", "config-key get", ca, true, true,
+ true, {}));
+ ca["key"] = "daemon-private/osd.123";
+ ASSERT_FALSE(cap.is_capable(NULL, name, "", "config-key get", ca, true, true,
+ true, {}));
+ ca["key"] = "daemon-private/osd.12/asdf";
+ ASSERT_FALSE(cap.is_capable(NULL, name, "", "config-key get", ca, true, true,
+ true, {}));
+ ca["key"] = "daemon-private/osd.123/";
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key get", ca, true, true,
+ true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key get", ca, true, true,
+ true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key get", ca, true, true,
+ true, {}));
+ ca["key"] = "daemon-private/osd.123/foo";
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key get", ca, true, true,
+ true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key put", ca, true, true,
+ true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key set", ca, true, true,
+ true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key exists", ca, true,
+ true, true, {}));
+ ASSERT_TRUE(cap.is_capable(NULL, name, "", "config-key delete", ca, true,
+ true, true, {}));
+}
+
+TEST(MonCap, CommandRegEx) {
+ MonCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("allow command abc with arg regex \"^[0-9a-z.]*$\"",
+ NULL));
+
+ EntityName name;
+ name.from_str("osd.123");
+ ASSERT_TRUE(cap.is_capable(nullptr, name, "", "abc", {{"arg", "12345abcde"}},
+ true, true, true, {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "", "abc", {{"arg", "~!@#$"}},
+ true, true, true, {}));
+
+ ASSERT_TRUE(cap.parse("allow command abc with arg regex \"[*\"", NULL));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "", "abc", {{"arg", ""}}, true,
+ true, true, {}));
+}
+
+TEST(MonCap, ProfileBootstrapRBD) {
+ MonCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("profile bootstrap-rbd", NULL));
+
+ EntityName name;
+ name.from_str("mon.a");
+ ASSERT_TRUE(cap.is_capable(nullptr, name, "",
+ "auth get-or-create", {
+ {"entity", "client.rbd"},
+ {"caps_mon", "profile rbd"},
+ {"caps_osd", "profile rbd pool=foo, profile rbd-read-only"},
+ }, true, true, true,
+ {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "",
+ "auth get-or-create", {
+ {"entity", "client.rbd"},
+ {"caps_mon", "allow *"},
+ {"caps_osd", "profile rbd"},
+ }, true, true, true,
+ {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "",
+ "auth get-or-create", {
+ {"entity", "client.rbd"},
+ {"caps_mon", "profile rbd"},
+ {"caps_osd", "profile rbd pool=foo, allow *, profile rbd-read-only"},
+ }, true, true, true,
+ {}));
+}
+
+TEST(MonCap, ProfileBootstrapRBDMirror) {
+ MonCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("profile bootstrap-rbd-mirror", NULL));
+
+ EntityName name;
+ name.from_str("mon.a");
+ ASSERT_TRUE(cap.is_capable(nullptr, name, "",
+ "auth get-or-create", {
+ {"entity", "client.rbd"},
+ {"caps_mon", "profile rbd-mirror"},
+ {"caps_osd", "profile rbd pool=foo, profile rbd-read-only"},
+ }, true, true, true,
+ {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "",
+ "auth get-or-create", {
+ {"entity", "client.rbd"},
+ {"caps_mon", "profile rbd"},
+ {"caps_osd", "profile rbd pool=foo, profile rbd-read-only"},
+ }, true, true, true,
+ {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "",
+ "auth get-or-create", {
+ {"entity", "client.rbd"},
+ {"caps_mon", "allow *"},
+ {"caps_osd", "profile rbd"},
+ }, true, true, true,
+ {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "",
+ "auth get-or-create", {
+ {"entity", "client.rbd"},
+ {"caps_mon", "profile rbd-mirror"},
+ {"caps_osd", "profile rbd pool=foo, allow *, profile rbd-read-only"},
+ }, true, true, true,
+ {}));
+}
+
+TEST(MonCap, ProfileRBD) {
+ MonCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("profile rbd", NULL));
+
+ EntityName name;
+ name.from_str("mon.a");
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "config-key",
+ "config-key get", {
+ {"key", "rbd/mirror/peer/1/1234"},
+ }, true, false, false, {}));
+}
+
+TEST(MonCap, ProfileRBDMirror) {
+ MonCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("profile rbd-mirror", NULL));
+
+ EntityName name;
+ name.from_str("mon.a");
+ ASSERT_TRUE(cap.is_capable(nullptr, name, "config-key",
+ "config-key get", {
+ {"key", "rbd/mirror/peer/1/1234"},
+ }, true, false, false, {}));
+}
diff --git a/src/test/mon/test-mon-msg.cc b/src/test/mon/test-mon-msg.cc
new file mode 100644
index 000000000..1ec328d1f
--- /dev/null
+++ b/src/test/mon/test-mon-msg.cc
@@ -0,0 +1,339 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2014 Red Hat
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <sstream>
+#include <time.h>
+#include <stdlib.h>
+#include <map>
+
+#include "global/global_init.h"
+#include "global/global_context.h"
+#include "common/async/context_pool.h"
+#include "common/ceph_argparse.h"
+#include "common/version.h"
+#include "common/dout.h"
+#include "common/debug.h"
+#include "common/ceph_mutex.h"
+#include "common/Timer.h"
+#include "common/errno.h"
+#include "mon/MonClient.h"
+#include "msg/Dispatcher.h"
+#include "include/err.h"
+#include <boost/scoped_ptr.hpp>
+
+#include "gtest/gtest.h"
+
+#include "common/config.h"
+#include "include/ceph_assert.h"
+
+#include "messages/MMonProbe.h"
+#include "messages/MRoute.h"
+#include "messages/MGenericMessage.h"
+#include "messages/MMonJoin.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_
+#undef dout_prefix
+#define dout_prefix *_dout << "test-mon-msg "
+
+using namespace std;
+
+class MonClientHelper : public Dispatcher
+{
+protected:
+ CephContext *cct;
+ ceph::async::io_context_pool poolctx;
+ Messenger *msg;
+ MonClient monc;
+
+ ceph::mutex lock = ceph::make_mutex("mon-msg-test::lock");
+
+ set<int> wanted;
+
+public:
+
+ explicit MonClientHelper(CephContext *cct_)
+ : Dispatcher(cct_),
+ cct(cct_),
+ poolctx(1),
+ msg(NULL),
+ monc(cct_, poolctx)
+ { }
+
+
+ int post_init() {
+ dout(1) << __func__ << dendl;
+ if (!msg)
+ return -EINVAL;
+ msg->add_dispatcher_tail(this);
+ return 0;
+ }
+
+ int init_messenger() {
+ dout(1) << __func__ << dendl;
+
+ std::string public_msgr_type = cct->_conf->ms_public_type.empty() ? cct->_conf.get_val<std::string>("ms_type") : cct->_conf->ms_public_type;
+ msg = Messenger::create(cct, public_msgr_type, entity_name_t::CLIENT(-1),
+ "test-mon-msg", 0);
+ ceph_assert(msg != NULL);
+ msg->set_default_policy(Messenger::Policy::lossy_client(0));
+ dout(0) << __func__ << " starting messenger at "
+ << msg->get_myaddrs() << dendl;
+ msg->start();
+ return 0;
+ }
+
+ int init_monc() {
+ dout(1) << __func__ << dendl;
+ ceph_assert(msg != NULL);
+ int err = monc.build_initial_monmap();
+ if (err < 0) {
+ derr << __func__ << " error building monmap: "
+ << cpp_strerror(err) << dendl;
+ return err;
+ }
+
+ monc.set_messenger(msg);
+ msg->add_dispatcher_head(&monc);
+
+ monc.set_want_keys(CEPH_ENTITY_TYPE_MON);
+ err = monc.init();
+ if (err < 0) {
+ derr << __func__ << " monc init failed: "
+ << cpp_strerror(err) << dendl;
+ goto fail;
+ }
+
+ err = monc.authenticate();
+ if (err < 0) {
+ derr << __func__ << " monc auth failed: "
+ << cpp_strerror(err) << dendl;
+ goto fail_monc;
+ }
+ monc.wait_auth_rotating(30.0);
+ monc.renew_subs();
+ dout(0) << __func__ << " finished" << dendl;
+ return 0;
+
+fail_monc:
+ derr << __func__ << " failing monc" << dendl;
+ monc.shutdown();
+fail:
+ return err;
+ }
+
+ void shutdown_messenger() {
+ dout(0) << __func__ << dendl;
+ msg->shutdown();
+ msg->wait();
+ }
+
+ void shutdown_monc() {
+ dout(0) << __func__ << dendl;
+ monc.shutdown();
+ }
+
+ void shutdown() {
+ dout(0) << __func__ << dendl;
+ shutdown_monc();
+ shutdown_messenger();
+ }
+
+ MonMap *get_monmap() {
+ return &monc.monmap;
+ }
+
+ int init() {
+ int err = init_messenger();
+ if (err < 0)
+ goto fail;
+ err = init_monc();
+ if (err < 0)
+ goto fail_msgr;
+ err = post_init();
+ if (err < 0)
+ goto fail_monc;
+ return 0;
+fail_monc:
+ shutdown_monc();
+fail_msgr:
+ shutdown_messenger();
+fail:
+ return err;
+ }
+
+ virtual void handle_wanted(Message *m) { }
+
+ bool handle_message(Message *m) {
+ dout(1) << __func__ << " " << *m << dendl;
+ if (!is_wanted(m)) {
+ dout(10) << __func__ << " not wanted" << dendl;
+ return false;
+ }
+ handle_wanted(m);
+ m->put();
+
+ return true;
+ }
+
+ bool ms_dispatch(Message *m) override {
+ return handle_message(m);
+ }
+ void ms_handle_connect(Connection *con) override { }
+ void ms_handle_remote_reset(Connection *con) override { }
+ bool ms_handle_reset(Connection *con) override { return false; }
+ bool ms_handle_refused(Connection *con) override { return false; }
+
+ bool is_wanted(Message *m) {
+ dout(20) << __func__ << " " << *m << " type " << m->get_type() << dendl;
+ return (wanted.find(m->get_type()) != wanted.end());
+ }
+
+ void add_wanted(int t) {
+ dout(20) << __func__ << " type " << t << dendl;
+ wanted.insert(t);
+ }
+
+ void rm_wanted(int t) {
+ dout(20) << __func__ << " type " << t << dendl;
+ wanted.erase(t);
+ }
+
+ void send_message(Message *m) {
+ dout(15) << __func__ << " " << *m << dendl;
+ monc.send_mon_message(m);
+ }
+
+ void wait() { msg->wait(); }
+};
+
+class MonMsgTest : public MonClientHelper,
+ public ::testing::Test
+{
+protected:
+ int reply_type = 0;
+ Message *reply_msg = nullptr;
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cond;
+
+ MonMsgTest() :
+ MonClientHelper(g_ceph_context) { }
+
+public:
+ void SetUp() override {
+ reply_type = -1;
+ if (reply_msg) {
+ reply_msg->put();
+ reply_msg = nullptr;
+ }
+ ASSERT_EQ(init(), 0);
+ }
+
+ void TearDown() override {
+ shutdown();
+ if (reply_msg) {
+ reply_msg->put();
+ reply_msg = nullptr;
+ }
+ }
+
+ void handle_wanted(Message *m) override {
+ std::lock_guard l{lock};
+ // caller will put() after they call us, so hold on to a ref
+ m->get();
+ reply_msg = m;
+ cond.notify_all();
+ }
+
+ Message *send_wait_reply(Message *m, int t, double timeout=30.0) {
+ std::unique_lock l{lock};
+ reply_type = t;
+ add_wanted(t);
+ send_message(m);
+
+ std::cv_status status = std::cv_status::no_timeout;
+ if (timeout > 0) {
+ utime_t s = ceph_clock_now();
+ status = cond.wait_for(l, ceph::make_timespan(timeout));
+ utime_t e = ceph_clock_now();
+ dout(20) << __func__ << " took " << (e-s) << " seconds" << dendl;
+ } else {
+ cond.wait(l);
+ }
+ rm_wanted(t);
+ l.unlock();
+ if (status == std::cv_status::timeout) {
+ dout(20) << __func__ << " error: " << cpp_strerror(ETIMEDOUT) << dendl;
+ return (Message*)((long)-ETIMEDOUT);
+ }
+
+ if (!reply_msg)
+ dout(20) << __func__ << " reply_msg is nullptr" << dendl;
+ else
+ dout(20) << __func__ << " reply_msg " << *reply_msg << dendl;
+ return reply_msg;
+ }
+};
+
+TEST_F(MonMsgTest, MMonProbeTest)
+{
+ Message *m = new MMonProbe(get_monmap()->fsid,
+ MMonProbe::OP_PROBE, "b", false,
+ ceph_release());
+ Message *r = send_wait_reply(m, MSG_MON_PROBE);
+ ASSERT_NE(IS_ERR(r), 0);
+ ASSERT_EQ(PTR_ERR(r), -ETIMEDOUT);
+}
+
+TEST_F(MonMsgTest, MRouteTest)
+{
+ Message *payload = new MGenericMessage(CEPH_MSG_SHUTDOWN);
+ MRoute *m = new MRoute;
+ m->msg = payload;
+ Message *r = send_wait_reply(m, CEPH_MSG_SHUTDOWN);
+ // we want an error
+ ASSERT_NE(IS_ERR(r), 0);
+ ASSERT_EQ(PTR_ERR(r), -ETIMEDOUT);
+}
+
+/* MMonScrub and MMonSync have other safeguards in place that prevent
+ * us from actually receiving a reply even if the message is handled
+ * by the monitor due to lack of cap checking.
+ */
+TEST_F(MonMsgTest, MMonJoin)
+{
+ Message *m = new MMonJoin(get_monmap()->fsid, string("client"),
+ msg->get_myaddrs());
+ send_wait_reply(m, MSG_MON_PAXOS, 10.0);
+
+ int r = monc.get_monmap();
+ ASSERT_EQ(r, 0);
+ ASSERT_FALSE(monc.monmap.contains("client"));
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(nullptr, args,
+ CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
+
diff --git a/src/test/mon/test_election.cc b/src/test/mon/test_election.cc
new file mode 100644
index 000000000..9dba99136
--- /dev/null
+++ b/src/test/mon/test_election.cc
@@ -0,0 +1,1003 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "gtest/gtest.h"
+#include "mon/ElectionLogic.h"
+#include "mon/ConnectionTracker.h"
+#include "common/dout.h"
+
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/common_init.h"
+#include "common/ceph_argparse.h"
+
+using namespace std;
+
+#define dout_subsys ceph_subsys_test
+#undef dout_prefix
+#define dout_prefix _prefix(_dout, prefix_name(), timestep_count())
+static ostream& _prefix(std::ostream *_dout, const char *prefix, int timesteps) {
+ return *_dout << prefix << timesteps << " ";
+}
+
+const char* prefix_name() { return "test_election: "; }
+int timestep_count() { return -1; }
+
+int main(int argc, char **argv) {
+ vector<const char*> args(argv, argv+argc);
+ bool user_set_debug = false;
+ for (auto& arg : args) {
+ if (strncmp("--debug_mon", arg, 11) == 0) user_set_debug = true;
+ }
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ if (!user_set_debug) g_ceph_context->_conf.set_val("debug mon", "0/20");
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+
+class Owner;
+struct Election {
+ map<int, Owner*> electors;
+ map<int, set<int> > blocked_messages;
+ int count;
+ ElectionLogic::election_strategy election_strategy;
+ int ping_interval;
+ set<int> disallowed_leaders;
+
+ vector< function<void()> > messages;
+ int pending_election_messages;
+ int timesteps_run = 0;
+ int last_quorum_change = 0;
+ int last_quorum_formed = -1;
+ set<int> last_quorum_reported;
+ int last_leader = -1;
+
+ Election(int c, ElectionLogic::election_strategy es, int pingi=1, double tracker_halflife=5);
+ ~Election();
+ // ElectionOwner interfaces
+ int get_paxos_size() { return count; }
+ const set<int>& get_disallowed_leaders() const { return disallowed_leaders; }
+ void propose_to(int from, int to, epoch_t e, bufferlist& cbl);
+ void defer_to(int from, int to, epoch_t e);
+ void claim_victory(int from, int to, epoch_t e, const set<int>& members);
+ void accept_victory(int from, int to, epoch_t e);
+ void report_quorum(const set<int>& quorum);
+ void queue_stable_message(int from, int to, function<void()> m);
+ void queue_timeout_message(int from, int to, function<void()> m);
+ void queue_stable_or_timeout(int from, int to,
+ function<void()> m, function<void()> t);
+ void queue_election_message(int from, int to, function<void()> m);
+
+ // test runner interfaces
+ int run_timesteps(int max);
+ void start_one(int who);
+ void start_all();
+ bool election_stable() const;
+ bool quorum_stable(int timesteps_stable) const;
+ bool all_agree_on_leader() const;
+ bool check_epoch_agreement() const;
+ void block_messages(int from, int to);
+ void block_bidirectional_messages(int a, int b);
+ void unblock_messages(int from, int to);
+ void unblock_bidirectional_messages(int a, int b);
+ void add_disallowed_leader(int disallowed) { disallowed_leaders.insert(disallowed); }
+ void remove_elector(int rank);
+ const char* prefix_name() const { return "Election: "; }
+ int timestep_count() const { return timesteps_run; }
+};
+struct Owner : public ElectionOwner, RankProvider {
+ Election *parent;
+ int rank;
+ epoch_t persisted_epoch;
+ bool ever_joined;
+ ConnectionTracker peer_tracker;
+ ElectionLogic logic;
+ set<int> quorum;
+ int victory_accepters;
+ int timer_steps; // timesteps until we trigger timeout
+ bool timer_election; // the timeout is for normal election, or victory
+ bool rank_deleted = false;
+ string prefix_str;
+ Owner(int r, ElectionLogic::election_strategy es, double tracker_halflife,
+ Election *p) : parent(p), rank(r), persisted_epoch(0),
+ ever_joined(false),
+ peer_tracker(this, rank, tracker_halflife, 5, g_ceph_context),
+ logic(this, es, &peer_tracker, 0.0005, g_ceph_context),
+ victory_accepters(0),
+ timer_steps(-1), timer_election(true) {
+ std::stringstream str;
+ str << "Owner" << rank << " ";
+ prefix_str = str.str();
+ }
+
+ // in-memory store: just save to variable
+ void persist_epoch(epoch_t e) { persisted_epoch = e; }
+ // in-memory store: just return variable
+ epoch_t read_persisted_epoch() const { return persisted_epoch; }
+ // in-memory store: don't need to validate
+ void validate_store() { return; }
+ // don't need to do anything with our state right now
+ void notify_bump_epoch() {}
+ void notify_rank_removed(int removed_rank) {
+ ldout(g_ceph_context, 1) << "removed_rank: " << removed_rank << dendl;
+ ldout(g_ceph_context, 1) << "rank before: " << rank << dendl;
+ if (removed_rank < rank) {
+ --rank;
+ }
+ peer_tracker.notify_rank_removed(removed_rank, rank);
+ ldout(g_ceph_context, 1) << "rank after: " << rank << dendl;
+ }
+ void notify_deleted() { rank_deleted = true; rank = -1; cancel_timer(); }
+ // pass back to ElectionLogic; we don't need this redirect ourselves
+ void trigger_new_election() { ceph_assert (!rank_deleted); logic.start(); }
+ int get_my_rank() const { return rank; }
+ // we don't need to persist scores as we don't reset and lose memory state
+ void persist_connectivity_scores() {}
+ void propose_to_peers(epoch_t e, bufferlist& bl) {
+ ceph_assert (!rank_deleted);
+ for (int i = 0; i < parent->get_paxos_size(); ++i) {
+ if (i == rank) continue;
+ parent->propose_to(rank, i, e, bl);
+ }
+ }
+ void reset_election() {
+ ceph_assert (!rank_deleted);
+ _start();
+ logic.start();
+ }
+ bool ever_participated() const { return ever_joined; }
+ unsigned paxos_size() const { return parent->get_paxos_size(); }
+ const set<int>& get_disallowed_leaders() const {
+ return parent->get_disallowed_leaders();
+ }
+ void cancel_timer() {
+ timer_steps = -1;
+ }
+ void reset_timer(int steps) {
+ cancel_timer();
+ timer_steps = 3 + steps; // FIXME? magic number, current step + roundtrip
+ timer_election = true;
+ }
+ void start_victory_timer() {
+ cancel_timer();
+ timer_election = false;
+ timer_steps = 3; // FIXME? current step + roundtrip
+ }
+ void _start() {
+ reset_timer(0);
+ quorum.clear();
+ }
+ void _defer_to(int who) {
+ ceph_assert (!rank_deleted);
+ parent->defer_to(rank, who, logic.get_epoch());
+ reset_timer(0); // wtf does changing this 0->1 cause breakage?
+ }
+ void message_victory(const std::set<int>& members) {
+ ceph_assert (!rank_deleted);
+ for (auto i : members) {
+ if (i == rank) continue;
+ parent->claim_victory(rank, i, logic.get_epoch(), members);
+ }
+ start_victory_timer();
+ quorum = members;
+ victory_accepters = 1;
+ }
+ bool is_current_member(int r) const { return quorum.count(r) != 0; }
+ void receive_propose(int from, epoch_t e, ConnectionTracker *oct) {
+ if (rank_deleted) return;
+ logic.receive_propose(from, e, oct);
+ delete oct;
+ }
+ void receive_ack(int from, epoch_t e) {
+ if (rank_deleted) return;
+ if (e < logic.get_epoch())
+ return;
+ logic.receive_ack(from, e);
+ }
+ void receive_victory_claim(int from, epoch_t e, const set<int>& members) {
+ if (rank_deleted) return;
+ if (e < logic.get_epoch())
+ return;
+ if (logic.receive_victory_claim(from, e)) {
+ quorum = members;
+ cancel_timer();
+ parent->accept_victory(rank, from, e);
+ }
+ }
+ void receive_victory_ack(int from, epoch_t e) {
+ if (rank_deleted) return;
+ if (e < logic.get_epoch())
+ return;
+ ++victory_accepters;
+ if (victory_accepters == static_cast<int>(quorum.size())) {
+ cancel_timer();
+ parent->report_quorum(quorum);
+ }
+ }
+ void receive_scores(bufferlist bl) {
+ ConnectionTracker oct(bl, g_ceph_context);
+ peer_tracker.receive_peer_report(oct);
+ ldout(g_ceph_context, 10) << "received scores " << oct << dendl;
+ }
+ void receive_ping(int from_rank, bufferlist bl) {
+ ldout(g_ceph_context, 6) << "receive ping from " << from_rank << dendl;
+ peer_tracker.report_live_connection(from_rank, parent->ping_interval);
+ receive_scores(bl);
+ }
+ void receive_ping_timeout(int from_rank) {
+ ldout(g_ceph_context, 6) << "timeout ping from " << from_rank << dendl;
+ peer_tracker.report_dead_connection(from_rank, parent->ping_interval);
+ }
+ void election_timeout() {
+ ldout(g_ceph_context, 2) << "election epoch " << logic.get_epoch()
+ << " timed out for " << rank
+ << ", electing me:" << logic.electing_me
+ << ", acked_me:" << logic.acked_me << dendl;
+ ceph_assert (!rank_deleted);
+ logic.end_election_period();
+ }
+ void victory_timeout() {
+ ldout(g_ceph_context, 2) << "victory epoch " << logic.get_epoch()
+ << " timed out for " << rank
+ << ", electing me:" << logic.electing_me
+ << ", acked_me:" << logic.acked_me << dendl;
+ ceph_assert (!rank_deleted);
+ reset_election();
+ }
+ void encode_scores(bufferlist& bl) {
+ encode(peer_tracker, bl);
+ }
+ void send_pings() {
+ ceph_assert (!rank_deleted);
+ if (!parent->ping_interval ||
+ parent->timesteps_run % parent->ping_interval != 0) {
+ return;
+ }
+
+ bufferlist bl;
+ encode_scores(bl);
+ for (int i = 0; i < parent->get_paxos_size(); ++i) {
+ if (i == rank)
+ continue;
+ Owner *o = parent->electors[i];
+ parent->queue_stable_or_timeout(rank, i,
+ [o, r=rank, bl] { o->receive_ping(r, bl); },
+ [o, r=rank] { o->receive_ping_timeout(r); }
+ );
+ }
+ }
+ void notify_timestep() {
+ ceph_assert (!rank_deleted);
+ assert(timer_steps != 0);
+ if (timer_steps > 0) {
+ --timer_steps;
+ }
+ if (timer_steps == 0) {
+ if (timer_election) {
+ election_timeout();
+ } else {
+ victory_timeout();
+ }
+ }
+ send_pings();
+ }
+ const char *prefix_name() const {
+ return prefix_str.c_str();
+ }
+ int timestep_count() const { return parent->timesteps_run; }
+};
+
+Election::Election(int c, ElectionLogic::election_strategy es, int pingi,
+ double tracker_halflife) : count(c), election_strategy(es), ping_interval(pingi),
+ pending_election_messages(0), timesteps_run(0), last_quorum_change(0), last_quorum_formed(-1)
+{
+ for (int i = 0; i < count; ++i) {
+ electors[i] = new Owner(i, election_strategy, tracker_halflife, this);
+ }
+}
+
+Election::~Election()
+{
+ {
+ for (auto i : electors) {
+ delete i.second;
+ }
+ }
+}
+
+void Election::queue_stable_message(int from, int to, function<void()> m)
+{
+ if (!blocked_messages[from].count(to)) {
+ messages.push_back(m);
+ }
+}
+
+void Election::queue_election_message(int from, int to, function<void()> m)
+{
+ if (last_quorum_reported.count(from)) {
+ last_quorum_change = timesteps_run;
+ last_quorum_reported.clear();
+ last_leader = -1;
+ }
+ if (!blocked_messages[from].count(to)) {
+ bufferlist bl;
+ electors[from]->encode_scores(bl);
+ Owner *o = electors[to];
+ messages.push_back([this,m,o,bl] {
+ --this->pending_election_messages;
+ o->receive_scores(bl);
+ m();
+ });
+ ++pending_election_messages;
+ }
+}
+
+void Election::queue_timeout_message(int from, int to, function<void()> m)
+{
+ ceph_assert(blocked_messages[from].count(to));
+ messages.push_back(m);
+}
+
+void Election::queue_stable_or_timeout(int from, int to,
+ function<void()> m, function<void()> t)
+{
+ if (blocked_messages[from].count(to)) {
+ queue_timeout_message(from, to, t);
+ } else {
+ queue_stable_message(from, to, m);
+ }
+}
+
+void Election::defer_to(int from, int to, epoch_t e)
+{
+ Owner *o = electors[to];
+ queue_election_message(from, to, [o, from, e] {
+ o->receive_ack(from, e);
+ });
+}
+
+void Election::propose_to(int from, int to, epoch_t e, bufferlist& cbl)
+{
+ Owner *o = electors[to];
+ ConnectionTracker *oct = NULL;
+ if (cbl.length()) {
+ oct = new ConnectionTracker(cbl, g_ceph_context); // we leak these on blocked cons, meh
+ }
+ queue_election_message(from, to, [o, from, e, oct] {
+ o->receive_propose(from, e, oct);
+ });
+}
+
+void Election::claim_victory(int from, int to, epoch_t e, const set<int>& members)
+{
+ Owner *o = electors[to];
+ queue_election_message(from, to, [o, from, e, members] {
+ o->receive_victory_claim(from, e, members);
+ });
+}
+
+void Election::accept_victory(int from, int to, epoch_t e)
+{
+ Owner *o = electors[to];
+ queue_election_message(from, to, [o, from, e] {
+ o->receive_victory_ack(from, e);
+ });
+}
+
+void Election::report_quorum(const set<int>& quorum)
+{
+ for (int i : quorum) {
+ electors[i]->ever_joined = true;
+ }
+ last_quorum_formed = last_quorum_change = timesteps_run;
+ last_quorum_reported = quorum;
+ last_leader = electors[*(quorum.begin())]->logic.get_election_winner();
+}
+
+int Election::run_timesteps(int max)
+{
+ vector< function<void()> > current_m;
+ int steps = 0;
+ for (; (!max || steps < max) && // we have timesteps left AND ONE OF
+ (pending_election_messages || // there are messages pending.
+ !election_stable()); // somebody's not happy and will act in future
+ ++steps) {
+ current_m.clear();
+ current_m.swap(messages);
+ ++timesteps_run;
+ for (auto& m : current_m) {
+ m();
+ }
+ for (auto o : electors) {
+ o.second->notify_timestep();
+ }
+ }
+
+ return steps;
+}
+
+void Election::start_one(int who)
+{
+ assert(who < static_cast<int>(electors.size()));
+ electors[who]->logic.start();
+}
+
+void Election::start_all() {
+ for (auto e : electors) {
+ e.second->logic.start();
+ }
+}
+
+bool Election::election_stable() const
+{
+ // see if anybody has a timer running
+ for (auto i : electors) {
+ if (i.second->timer_steps != -1) {
+ ldout(g_ceph_context, 30) << "rank " << i.first << " has timer value " << i.second->timer_steps << dendl;
+ return false;
+ }
+ }
+ return (pending_election_messages == 0);
+}
+
+bool Election::quorum_stable(int timesteps_stable) const
+{
+ ldout(g_ceph_context, 1) << "quorum_stable? last formed:" << last_quorum_formed
+ << ", last changed " << last_quorum_change
+ << ", last reported members " << last_quorum_reported << dendl;
+ if (last_quorum_reported.empty()) {
+ return false;
+ }
+ if (last_quorum_formed < last_quorum_change) {
+ return false;
+ }
+ for (auto i : last_quorum_reported) {
+ if (electors.find(i)->second->timer_steps != -1) {
+ return false;
+ }
+ }
+ if (timesteps_run - timesteps_stable > last_quorum_change)
+ return true;
+ return election_stable();
+}
+
+bool Election::all_agree_on_leader() const
+{
+ int leader = electors.find(0)->second->logic.get_election_winner();
+ ldout(g_ceph_context, 10) << "all_agree_on_leader on " << leader << dendl;
+ for (auto& i: electors) {
+ if (leader != i.second->logic.get_election_winner()) {
+ ldout(g_ceph_context, 10) << "rank " << i.first << " has different leader "
+ << i.second->logic.get_election_winner() << dendl;
+ return false;
+ }
+ }
+ if (disallowed_leaders.count(leader)) {
+ ldout(g_ceph_context, 10) << "that leader is disallowed! member of "
+ << disallowed_leaders << dendl;
+ return false;
+ }
+ return true;
+}
+
+bool Election::check_epoch_agreement() const
+{
+ epoch_t epoch = electors.find(0)->second->logic.get_epoch();
+ for (auto& i : electors) {
+ if (epoch != i.second->logic.get_epoch()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void Election::block_messages(int from, int to)
+{
+ blocked_messages[from].insert(to);
+}
+void Election::block_bidirectional_messages(int a, int b)
+{
+ block_messages(a, b);
+ block_messages(b, a);
+}
+void Election::unblock_messages(int from, int to)
+{
+ blocked_messages[from].erase(to);
+}
+void Election::unblock_bidirectional_messages(int a, int b)
+{
+ unblock_messages(a, b);
+ unblock_messages(b, a);
+}
+
+void Election::remove_elector(int rank)
+{
+ for (auto ei = electors.begin(); ei != electors.end(); ) {
+ if (ei->first == rank) {
+ ei->second->notify_deleted();
+ electors.erase(ei++);
+ continue;
+ }
+ ei->second->notify_rank_removed(rank);
+ if (ei->first > rank) {
+ electors[ei->first - 1] = ei->second;
+ electors.erase(ei++);
+ continue;
+ }
+ ++ei;
+ }
+ for (auto bi = blocked_messages.begin(); bi != blocked_messages.end(); ) {
+ if (bi->first == rank) {
+ blocked_messages.erase(bi++);
+ continue;
+ }
+ bi->second.erase(rank);
+ for (auto i = bi->second.upper_bound(rank);
+ i != bi->second.end();) {
+ bi->second.insert(*i - 1);
+ bi->second.erase(*(i++));
+ }
+ ++bi;
+ }
+ --count;
+}
+
+void single_startup_election_completes(ElectionLogic::election_strategy strategy)
+{
+ for (int starter = 0; starter < 5; ++starter) {
+ Election election(5, strategy);
+ election.start_one(starter);
+ // This test is not actually legit since you should start
+ // all the ElectionLogics, but it seems to work
+ int steps = election.run_timesteps(0);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ }
+}
+
+void everybody_starts_completes(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy);
+ election.start_all();
+ int steps = election.run_timesteps(0);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void blocked_connection_continues_election(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy);
+ election.block_bidirectional_messages(0, 1);
+ election.start_all();
+ int steps = election.run_timesteps(100);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ // This is a failure mode!
+ ASSERT_FALSE(election.election_stable());
+ ASSERT_FALSE(election.quorum_stable(6)); // double the timer_steps we use
+ election.unblock_bidirectional_messages(0, 1);
+ steps = election.run_timesteps(100);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void blocked_connection_converges_election(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy);
+ election.block_bidirectional_messages(0, 1);
+ election.start_all();
+ int steps = election.run_timesteps(100);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ election.unblock_bidirectional_messages(0, 1);
+ steps = election.run_timesteps(100);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void disallowed_doesnt_win(ElectionLogic::election_strategy strategy)
+{
+ int MON_COUNT = 5;
+ for (int i = 0; i < MON_COUNT - 1; ++i) {
+ Election election(MON_COUNT, strategy);
+ for (int j = 0; j <= i; ++j) {
+ election.add_disallowed_leader(j);
+ }
+ election.start_all();
+ int steps = election.run_timesteps(0);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ int leader = election.electors[0]->logic.get_election_winner();
+ for (int j = 0; j <= i; ++j) {
+ ASSERT_NE(j, leader);
+ }
+ }
+ for (int i = MON_COUNT - 1; i > 0; --i) {
+ Election election(MON_COUNT, strategy);
+ for (int j = i; j <= MON_COUNT - 1; ++j) {
+ election.add_disallowed_leader(j);
+ }
+ election.start_all();
+ int steps = election.run_timesteps(0);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ int leader = election.electors[0]->logic.get_election_winner();
+ for (int j = i; j < MON_COUNT; ++j) {
+ ASSERT_NE(j, leader);
+ }
+ }
+}
+
+void converges_after_flapping(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy);
+ auto block_cons = [&] {
+ auto& e = election;
+ // leave 4 connected to both sides so it will trigger but not trivially win
+ e.block_bidirectional_messages(0, 2);
+ e.block_bidirectional_messages(0, 3);
+ e.block_bidirectional_messages(1, 2);
+ e.block_bidirectional_messages(1, 3);
+ };
+ auto unblock_cons = [&] {
+ auto& e = election;
+ e.unblock_bidirectional_messages(0, 2);
+ e.unblock_bidirectional_messages(0, 3);
+ e.unblock_bidirectional_messages(1, 2);
+ e.unblock_bidirectional_messages(1, 3);
+ };
+ block_cons();
+ election.start_all();
+ for (int i = 0; i < 5; ++i) {
+ election.run_timesteps(5);
+ unblock_cons();
+ election.run_timesteps(5);
+ block_cons();
+ }
+ unblock_cons();
+ election.run_timesteps(100);
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void converges_while_flapping(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy);
+ auto block_cons = [&] {
+ auto& e = election;
+ // leave 4 connected to both sides so it will trigger but not trivially win
+ e.block_bidirectional_messages(0, 2);
+ e.block_bidirectional_messages(0, 3);
+ e.block_bidirectional_messages(1, 2);
+ e.block_bidirectional_messages(1, 3);
+ };
+ auto unblock_cons = [&] {
+ auto& e = election;
+ e.unblock_bidirectional_messages(0, 2);
+ e.unblock_bidirectional_messages(0, 3);
+ e.unblock_bidirectional_messages(1, 2);
+ e.unblock_bidirectional_messages(1, 3);
+ };
+ block_cons();
+ election.start_all();
+ for (int i = 0; i < 5; ++i) {
+ election.run_timesteps(10);
+ ASSERT_TRUE(election.quorum_stable(6));
+ unblock_cons();
+ election.run_timesteps(5);
+ block_cons();
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ }
+ unblock_cons();
+ election.run_timesteps(100);
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6));
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void netsplit_with_disallowed_tiebreaker_converges(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy);
+ election.add_disallowed_leader(4);
+ auto netsplit = [&] {
+ auto& e = election;
+ e.block_bidirectional_messages(0, 2);
+ e.block_bidirectional_messages(0, 3);
+ e.block_bidirectional_messages(1, 2);
+ e.block_bidirectional_messages(1, 3);
+ };
+ auto unsplit = [&] {
+ auto& e = election;
+ e.unblock_bidirectional_messages(0, 2);
+ e.unblock_bidirectional_messages(0, 3);
+ e.unblock_bidirectional_messages(1, 2);
+ e.unblock_bidirectional_messages(1, 3);
+ };
+ // hmm, we don't have timeouts to call elections automatically yet
+ auto call_elections = [&] {
+ for (auto i : election.electors) {
+ i.second->trigger_new_election();
+ }
+ };
+ // turn everybody on, run happy for a while
+ election.start_all();
+ election.run_timesteps(0);
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6));
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ int starting_leader = election.last_leader;
+ // do some netsplits, but leave disallowed tiebreaker alive
+ for (int i = 0; i < 5; ++i) {
+ netsplit();
+ call_elections();
+ election.run_timesteps(15); // tests fail when I run 10 because 0 and 1 time out on same timestamp for some reason, why?
+ // this ASSERT_EQ only holds while we bias for ranks
+ ASSERT_EQ(starting_leader, election.last_leader);
+ ASSERT_TRUE(election.quorum_stable(6));
+ ASSERT_FALSE(election.election_stable());
+ unsplit();
+ call_elections();
+ election.run_timesteps(10);
+ ASSERT_EQ(starting_leader, election.last_leader);
+ ASSERT_TRUE(election.quorum_stable(6));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ }
+
+ // now disconnect the tiebreaker and make sure nobody can win
+ int presplit_quorum_time = election.last_quorum_formed;
+ netsplit();
+ election.block_bidirectional_messages(4, 0);
+ election.block_bidirectional_messages(4, 1);
+ election.block_bidirectional_messages(4, 2);
+ election.block_bidirectional_messages(4, 3);
+ call_elections();
+ election.run_timesteps(100);
+ ASSERT_EQ(election.last_quorum_formed, presplit_quorum_time);
+
+ // now let in the previously-losing side
+ election.unblock_bidirectional_messages(4, 2);
+ election.unblock_bidirectional_messages(4, 3);
+ call_elections();
+ election.run_timesteps(100);
+ ASSERT_TRUE(election.quorum_stable(50));
+ ASSERT_FALSE(election.election_stable());
+
+ // now reconnect everybody
+ unsplit();
+ election.unblock_bidirectional_messages(4, 0);
+ election.unblock_bidirectional_messages(4, 1);
+ call_elections();
+ election.run_timesteps(100);
+ ASSERT_TRUE(election.quorum_stable(50));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void handles_singly_connected_peon(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy);
+ election.block_bidirectional_messages(0, 1);
+ election.block_bidirectional_messages(0, 2);
+ election.block_bidirectional_messages(0, 3);
+ election.block_bidirectional_messages(0, 4);
+
+ election.start_all();
+ election.run_timesteps(20);
+ ASSERT_TRUE(election.quorum_stable(5));
+ ASSERT_FALSE(election.election_stable());
+
+ election.unblock_bidirectional_messages(0, 1);
+ election.run_timesteps(100);
+ ASSERT_TRUE(election.quorum_stable(50));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+
+ election.block_bidirectional_messages(0, 1);
+ election.unblock_bidirectional_messages(0, 4);
+ for (auto i : election.electors) {
+ i.second->trigger_new_election();
+ }
+ election.run_timesteps(15);
+ ASSERT_TRUE(election.quorum_stable(50));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+ConnectionReport *get_connection_reports(ConnectionTracker& ct) {
+ return &ct.my_reports;
+}
+map<int,ConnectionReport> *get_peer_reports(ConnectionTracker& ct) {
+ return &ct.peer_reports;
+}
+void handles_outdated_scoring(ElectionLogic::election_strategy strategy)
+{
+ Election election(3, strategy, 5); // ping every 5 timesteps so they start elections before settling scores!
+
+ // start everybody up and run for a bit
+ election.start_all();
+ election.run_timesteps(20);
+ ASSERT_TRUE(election.quorum_stable(5));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+
+ // now mess up the scores to disagree
+ ConnectionTracker& ct0 = election.electors[0]->peer_tracker;
+ ConnectionReport& cr0 = *get_connection_reports(ct0);
+ cr0.history[1] = 0.5;
+ cr0.history[2] = 0.5;
+ ct0.increase_version();
+ ConnectionTracker& ct1 = election.electors[1]->peer_tracker;
+ ConnectionReport& cr1 = *get_connection_reports(ct1);
+ cr1.history[0] = 0.5;
+ cr1.history[2] = 0.5;
+ ct1.increase_version();
+ ConnectionTracker& ct2 = election.electors[2]->peer_tracker;
+ ConnectionReport& cr2 = *get_connection_reports(ct2);
+ cr2.history[0] = 0.5;
+ map<int,ConnectionReport>&cp2 = *get_peer_reports(ct2);
+ cp2[0].history[2] = 0;
+ cp2[1].history[2] = 0;
+ ct2.increase_version();
+ election.ping_interval = 0; // disable pinging to update the scores
+ ldout(g_ceph_context, 5) << "mangled the scores to be different" << dendl;
+
+ election.start_all();
+ election.run_timesteps(50);
+ ASSERT_TRUE(election.quorum_stable(30));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void handles_disagreeing_connectivity(ElectionLogic::election_strategy strategy)
+{
+ Election election(5, strategy, 5); // ping every 5 timesteps so they start elections before settling scores!
+
+ // start everybody up and run for a bit
+ election.start_all();
+ election.run_timesteps(20);
+ ASSERT_TRUE(election.quorum_stable(5));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+
+ // block all the connections
+ for (int i = 0; i < 5; ++i) {
+ for (int j = i+1; j < 5; ++j) {
+ election.block_bidirectional_messages(i, j);
+ }
+ }
+
+ // now start them electing, which will obviously fail
+ election.start_all();
+ election.run_timesteps(50); // let them all demote scores of their peers
+ ASSERT_FALSE(election.quorum_stable(10));
+ ASSERT_FALSE(election.election_stable());
+
+ // now reconnect them, at which point they should start running an election before exchanging scores
+ for (int i = 0; i < 5; ++i) {
+ for (int j = i+1; j < 5; ++j) {
+ election.unblock_bidirectional_messages(i, j);
+ }
+ }
+ election.run_timesteps(100);
+
+ // these will pass if the nodes managed to converge on scores, but I expect failure
+ ASSERT_TRUE(election.quorum_stable(5));
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+}
+
+void handles_removing_ranks(ElectionLogic::election_strategy strategy)
+{
+ ceph_assert(strategy == ElectionLogic::CONNECTIVITY);
+ for (int deletee = 0; deletee < 5; ++deletee) {
+ Election election(5, strategy);
+ election.start_all();
+ int steps = election.run_timesteps(0);
+ ldout(g_ceph_context, 10) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ election.remove_elector(deletee);
+ ldout(g_ceph_context, 1) << "removed rank " << deletee << " from set" << dendl;
+ election.start_all();
+ steps = election.run_timesteps(0);
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ }
+ {
+ Election election(7, strategy);
+ for (int i = 0; i < (7 - 3); ++i) {
+ election.start_all();
+ election.remove_elector(0);
+ int steps = election.run_timesteps(0);
+ ldout(g_ceph_context, 1) << "ran in " << steps << " timesteps" << dendl;
+ ASSERT_TRUE(election.election_stable());
+ ASSERT_TRUE(election.quorum_stable(6)); // double the timer_steps we use
+ ASSERT_TRUE(election.all_agree_on_leader());
+ ASSERT_TRUE(election.check_epoch_agreement());
+ }
+ }
+}
+
+// TODO: write a test with more complicated connectivity graphs and make sure
+// they are stable with multiple disconnected ranks pinging peons
+
+// TODO: Write a test that disallowing and disconnecting 0 is otherwise stable?
+
+// TODO: figure out how to test for bumping election epochs with changing scores,
+// a la what happened in run
+// http://pulpito.ceph.com/gregf-2019-11-26_10:50:50-rados:monthrash-wip-elector-distro-basic-mira/
+
+#define test_classic(utest) TEST(classic, utest) { utest(ElectionLogic::CLASSIC); }
+
+#define test_disallowed(utest) TEST(disallowed, utest) { utest(ElectionLogic::DISALLOW); }
+
+#define test_connectivity(utest) TEST(connectivity, utest) { utest(ElectionLogic::CONNECTIVITY); }
+
+
+// TODO: test for expected failures; gtest probably supports that?
+test_classic(single_startup_election_completes)
+test_classic(everybody_starts_completes)
+test_classic(blocked_connection_continues_election)
+test_classic(converges_after_flapping)
+
+test_disallowed(single_startup_election_completes)
+test_disallowed(everybody_starts_completes)
+test_disallowed(blocked_connection_continues_election)
+test_disallowed(disallowed_doesnt_win)
+test_disallowed(converges_after_flapping)
+
+/* skip single_startup_election_completes because we crash
+ on init conditions. That's fine since as noted above it's not
+ quite following the rules anyway. */
+test_connectivity(everybody_starts_completes)
+test_connectivity(blocked_connection_converges_election)
+test_connectivity(disallowed_doesnt_win)
+test_connectivity(converges_after_flapping)
+test_connectivity(converges_while_flapping)
+test_connectivity(netsplit_with_disallowed_tiebreaker_converges)
+test_connectivity(handles_singly_connected_peon)
+test_connectivity(handles_disagreeing_connectivity)
+test_connectivity(handles_outdated_scoring)
+test_connectivity(handles_removing_ranks)
diff --git a/src/test/mon/test_log_rss_usage.cc b/src/test/mon/test_log_rss_usage.cc
new file mode 100644
index 000000000..b8ca3012a
--- /dev/null
+++ b/src/test/mon/test_log_rss_usage.cc
@@ -0,0 +1,102 @@
+#include <sys/types.h>
+#include <cstdint>
+#include <dirent.h>
+#include <errno.h>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+using namespace std;
+
+int getPidByName(string procName)
+{
+ int pid = -1;
+
+ // Open the /proc directory
+ DIR *dp = opendir("/proc");
+ if (dp != NULL)
+ {
+ // Enumerate all entries in '/proc' until process is found
+ struct dirent *dirp;
+ while (pid < 0 && (dirp = readdir(dp)))
+ {
+ // Skip non-numeric entries
+ int id = atoi(dirp->d_name);
+ if (id > 0)
+ {
+ // Read contents of virtual /proc/{pid}/cmdline file
+ string cmdPath = string("/proc/") + dirp->d_name + "/cmdline";
+ ifstream cmdFile(cmdPath.c_str());
+ string cmdLine;
+ getline(cmdFile, cmdLine);
+ if (!cmdLine.empty())
+ {
+ // Keep first cmdline item which contains the program path
+ size_t pos = cmdLine.find('\0');
+ if (pos != string::npos) {
+ cmdLine = cmdLine.substr(0, pos);
+ }
+ // Get program name only, removing the path
+ pos = cmdLine.rfind('/');
+ if (pos != string::npos) {
+ cmdLine = cmdLine.substr(pos + 1);
+ }
+ // Compare against requested process name
+ if (procName == cmdLine) {
+ pid = id;
+ }
+ }
+ }
+ }
+ }
+
+ closedir(dp);
+
+ return pid;
+}
+
+uint64_t getRssUsage(string pid)
+{
+ int totalSize = 0;
+ int resSize = 0;
+
+ string statmPath = string("/proc/") + pid + "/statm";
+ ifstream buffer(statmPath);
+ buffer >> totalSize >> resSize;
+ buffer.close();
+
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ uint64_t rss = resSize * page_size;
+
+ return rss;
+}
+
+int main(int argc, char* argv[])
+{
+ if (argc != 2) {
+ cout << "Syntax: "
+ << "ceph_test_log_rss_usage <process name>"
+ << endl;
+ exit(EINVAL);
+ }
+ uint64_t rss = 0;
+ int pid = getPidByName(argv[1]);
+ string rssUsage;
+
+ // Use the pid to get RSS memory usage
+ // and print it to stdout
+ if (pid != -1) {
+ rss = getRssUsage(to_string(pid));
+ } else {
+ cout << "Process " << argv[1] << " NOT FOUND!\n" << endl;
+ exit(ESRCH);
+ }
+
+ rssUsage = to_string(rss) + ":" + to_string(pid) + ":";
+ cout << rssUsage.c_str() << endl;
+ return 0;
+}
diff --git a/src/test/mon/test_mon_memory_target.cc b/src/test/mon/test_mon_memory_target.cc
new file mode 100644
index 000000000..e8f975b47
--- /dev/null
+++ b/src/test/mon/test_mon_memory_target.cc
@@ -0,0 +1,79 @@
+#include <algorithm>
+#include <cmath>
+#include <iostream>
+#include <string>
+#include <numeric>
+#include <regex>
+#include <system_error>
+
+#include <boost/process.hpp>
+#include <boost/tokenizer.hpp>
+
+namespace bp = boost::process;
+using namespace std;
+
+int main(int argc, char** argv)
+{
+ cout << "Mon Memory Target Test" << endl;
+
+ if (argc != 2) {
+ cout << "Syntax: "
+ << "ceph_test_mon_memory_target <mon-memory-target-bytes>"
+ << endl;
+ exit(EINVAL);
+ }
+
+ string target_directory("/var/log/ceph/");
+ unsigned long maxallowed = stoul(argv[1], nullptr, 10);
+ regex reg(R"(cache_size:(\d*)\s)");
+
+ string grep_command("grep _set_new_cache_sizes " + target_directory
+ + "ceph-mon.a.log");
+ bp::ipstream is;
+ error_code ec;
+ bp::child grep(grep_command, bp::std_out > is, ec);
+ if (ec) {
+ cout << "Error grepping logs! Exiting" << endl;
+ cout << "Error: " << ec.value() << " " << ec.message() << endl;
+ exit(ec.value());
+ }
+
+ string line;
+ vector<unsigned long> results;
+ while (grep.running() && getline(is, line) && !line.empty()) {
+ smatch match;
+ if (regex_search(line, match, reg)) {
+ results.push_back(stoul(match[1].str()));
+ }
+ }
+
+ if (results.empty()) {
+ cout << "Error: No grep results found!" << endl;
+ exit(ENOENT);
+ }
+
+ auto maxe = *(max_element(results.begin(), results.end()));
+ cout << "Results for mon_memory_target:" << endl;
+ cout << "Max: " << maxe << endl;
+ cout << "Min: " << *(min_element(results.begin(), results.end())) << endl;
+ auto sum = accumulate(results.begin(), results.end(),
+ static_cast<unsigned long long>(0));
+ auto mean = sum / results.size();
+ cout << "Mean average: " << mean << endl;
+ vector<unsigned long> diff(results.size());
+ transform(results.begin(), results.end(), diff.begin(),
+ [mean](unsigned long x) { return x - mean; });
+ auto sump = inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
+ auto stdev = sqrt(sump / results.size());
+ cout << "Standard deviation: " << stdev << endl;
+
+ if (maxe > maxallowed) {
+ cout << "Error: Mon memory consumption exceeds maximum allowed!" << endl;
+ exit(ENOMEM);
+ }
+
+ grep.wait();
+
+ cout << "Completed successfully" << endl;
+ return 0;
+}
diff --git a/src/test/mon/test_mon_rss_usage.cc b/src/test/mon/test_mon_rss_usage.cc
new file mode 100644
index 000000000..76b5856f6
--- /dev/null
+++ b/src/test/mon/test_mon_rss_usage.cc
@@ -0,0 +1,72 @@
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <numeric>
+#include <regex>
+#include <cmath>
+#include <system_error>
+
+using namespace std;
+
+int main(int argc, char **argv)
+{
+ cout << "Mon RSS Usage Test" << endl;
+
+ if (argc != 2) {
+ cout << "Syntax: "
+ << "ceph_test_mon_rss_usage <mon-memory-target-bytes>"
+ << endl;
+ exit(EINVAL);
+ }
+
+ unsigned long maxallowed = stoul(argv[1], nullptr, 10);
+ // Set max allowed RSS usage to be 125% of mon-memory-target
+ maxallowed *= 1.25;
+
+ string target_directory("/var/log/ceph/");
+ string filePath = target_directory + "ceph-mon-rss-usage.log";
+ ifstream buffer(filePath.c_str());
+ string line;
+ vector<unsigned long> results;
+ while(getline(buffer, line) && !line.empty()) {
+ string rssUsage;
+ size_t pos = line.find(':');
+ if (pos != string::npos) {
+ rssUsage = line.substr(0, pos);
+ }
+ if (!rssUsage.empty()) {
+ results.push_back(stoul(rssUsage));
+ }
+ }
+
+ buffer.close();
+ if (results.empty()) {
+ cout << "Error: No grep results found!" << endl;
+ exit(ENOENT);
+ }
+
+ auto maxe = *(max_element(results.begin(), results.end()));
+ cout << "Stats for mon RSS Memory Usage:" << endl;
+ cout << "Parsed " << results.size() << " entries." << endl;
+ cout << "Max: " << maxe << endl;
+ cout << "Min: " << *(min_element(results.begin(), results.end())) << endl;
+ auto sum = accumulate(results.begin(), results.end(),
+ static_cast<unsigned long long>(0));
+ auto mean = sum / results.size();
+ cout << "Mean average: " << mean << endl;
+ vector<unsigned long> diff(results.size());
+ transform(results.begin(), results.end(), diff.begin(),
+ [mean](unsigned long x) { return x - mean; });
+ auto sump = inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
+ auto stdev = sqrt(sump / results.size());
+ cout << fixed << "Standard deviation: " << stdev << endl;
+
+ if (maxe > maxallowed) {
+ cout << "Error: Mon RSS memory usage exceeds maximum allowed!" << endl;
+ exit(ENOMEM);
+ }
+
+ cout << "Completed successfully" << endl;
+ return 0;
+}
diff --git a/src/test/mon/test_mon_types.cc b/src/test/mon/test_mon_types.cc
new file mode 100644
index 000000000..e9997f14f
--- /dev/null
+++ b/src/test/mon/test_mon_types.cc
@@ -0,0 +1,140 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2012 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include "mon/mon_types.h"
+
+#include "gtest/gtest.h"
+
+TEST(mon_features, supported_v_persistent) {
+
+ mon_feature_t supported = ceph::features::mon::get_supported();
+ mon_feature_t persistent = ceph::features::mon::get_persistent();
+
+ ASSERT_EQ(supported.intersection(persistent), persistent);
+ ASSERT_TRUE(supported.contains_all(persistent));
+
+ mon_feature_t diff = supported.diff(persistent);
+ ASSERT_TRUE((persistent | diff) == supported);
+ ASSERT_TRUE((supported & persistent) == persistent);
+}
+
+TEST(mon_features, binary_ops) {
+
+ mon_feature_t FEATURE_NONE(0ULL);
+ mon_feature_t FEATURE_A((1ULL << 1));
+ mon_feature_t FEATURE_B((1ULL << 2));
+ mon_feature_t FEATURE_C((1ULL << 3));
+ mon_feature_t FEATURE_D((1ULL << 4));
+
+ mon_feature_t FEATURE_ALL(
+ FEATURE_A | FEATURE_B |
+ FEATURE_C | FEATURE_D
+ );
+
+ mon_feature_t foo(FEATURE_A|FEATURE_B);
+ mon_feature_t bar(FEATURE_C|FEATURE_D);
+
+ ASSERT_EQ(FEATURE_A|FEATURE_B, foo);
+ ASSERT_EQ(FEATURE_C|FEATURE_D, bar);
+
+ ASSERT_NE(FEATURE_C, foo);
+ ASSERT_NE(FEATURE_B, bar);
+ ASSERT_NE(FEATURE_NONE, foo);
+ ASSERT_NE(FEATURE_NONE, bar);
+
+ ASSERT_FALSE(foo.empty());
+ ASSERT_FALSE(bar.empty());
+ ASSERT_TRUE(FEATURE_NONE.empty());
+
+ ASSERT_EQ(FEATURE_ALL, (foo ^ bar));
+ ASSERT_EQ(FEATURE_NONE, (foo & bar));
+
+ mon_feature_t baz = foo;
+ ASSERT_EQ(baz, foo);
+
+ baz |= bar;
+ ASSERT_EQ(FEATURE_ALL, baz);
+ baz ^= foo;
+ ASSERT_EQ(baz, bar);
+
+ baz |= FEATURE_A;
+ ASSERT_EQ(FEATURE_C, baz & FEATURE_C);
+ ASSERT_EQ((FEATURE_A|FEATURE_D), baz & (FEATURE_A|FEATURE_D));
+ ASSERT_EQ(FEATURE_B|FEATURE_C|FEATURE_D, (baz ^ foo));
+}
+
+TEST(mon_features, set_funcs) {
+
+ mon_feature_t FEATURE_A((1ULL << 1));
+ mon_feature_t FEATURE_B((1ULL << 2));
+ mon_feature_t FEATURE_C((1ULL << 3));
+ mon_feature_t FEATURE_D((1ULL << 4));
+
+ mon_feature_t FEATURE_ALL(
+ FEATURE_A | FEATURE_B |
+ FEATURE_C | FEATURE_D
+ );
+
+ mon_feature_t foo(FEATURE_A|FEATURE_B);
+ mon_feature_t bar(FEATURE_C|FEATURE_D);
+
+ ASSERT_TRUE(FEATURE_ALL.contains_all(foo));
+ ASSERT_TRUE(FEATURE_ALL.contains_all(bar));
+ ASSERT_TRUE(FEATURE_ALL.contains_all(foo|bar));
+
+ ASSERT_EQ(foo.diff(bar), foo);
+ ASSERT_EQ(bar.diff(foo), bar);
+ ASSERT_EQ(FEATURE_ALL.diff(foo), bar);
+ ASSERT_EQ(FEATURE_ALL.diff(bar), foo);
+
+ ASSERT_TRUE(foo.contains_any(FEATURE_A|bar));
+ ASSERT_TRUE(bar.contains_any(FEATURE_ALL));
+ ASSERT_TRUE(FEATURE_ALL.contains_any(foo));
+
+ mon_feature_t FEATURE_X((1ULL << 10));
+
+ ASSERT_FALSE(FEATURE_ALL.contains_any(FEATURE_X));
+ ASSERT_FALSE(FEATURE_ALL.contains_all(FEATURE_X));
+ ASSERT_EQ(FEATURE_ALL.diff(FEATURE_X), FEATURE_ALL);
+
+ ASSERT_EQ(foo.intersection(FEATURE_ALL), foo);
+ ASSERT_EQ(bar.intersection(FEATURE_ALL), bar);
+}
+
+TEST(mon_features, set_unset) {
+
+ mon_feature_t FEATURE_A((1ULL << 1));
+ mon_feature_t FEATURE_B((1ULL << 2));
+ mon_feature_t FEATURE_C((1ULL << 3));
+
+ mon_feature_t foo;
+ ASSERT_EQ(ceph::features::mon::FEATURE_NONE, foo);
+
+ foo.set_feature(FEATURE_A);
+ ASSERT_EQ(FEATURE_A, foo);
+ ASSERT_TRUE(foo.contains_all(FEATURE_A));
+
+ foo.set_feature(FEATURE_B|FEATURE_C);
+ ASSERT_EQ((FEATURE_A|FEATURE_B|FEATURE_C), foo);
+ ASSERT_TRUE(foo.contains_all((FEATURE_A|FEATURE_B|FEATURE_C)));
+
+ foo.unset_feature(FEATURE_A);
+ ASSERT_EQ((FEATURE_B|FEATURE_C), foo);
+ ASSERT_FALSE(foo.contains_any(FEATURE_A));
+ ASSERT_TRUE(foo.contains_all((FEATURE_B|FEATURE_C)));
+
+ foo.unset_feature(FEATURE_B|FEATURE_C);
+ ASSERT_EQ(ceph::features::mon::FEATURE_NONE, foo);
+ ASSERT_FALSE(foo.contains_any(FEATURE_A|FEATURE_B|FEATURE_C));
+}
diff --git a/src/test/mon/test_mon_workloadgen.cc b/src/test/mon/test_mon_workloadgen.cc
new file mode 100644
index 000000000..a8c01cda8
--- /dev/null
+++ b/src/test/mon/test_mon_workloadgen.cc
@@ -0,0 +1,1074 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "acconfig.h"
+
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+
+#include <iostream>
+#include <string>
+#include <map>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+
+
+#include "osd/osd_types.h"
+#include "osdc/Objecter.h"
+#include "mon/MonClient.h"
+#include "msg/Dispatcher.h"
+#include "msg/Messenger.h"
+#include "common/async/context_pool.h"
+#include "common/Timer.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "global/signal_handler.h"
+#include "common/config.h"
+#include "common/debug.h"
+#include "common/errno.h"
+#include "common/ceph_mutex.h"
+#include "common/strtol.h"
+#include "common/LogEntry.h"
+#include "auth/KeyRing.h"
+#include "auth/AuthAuthorizeHandler.h"
+#include "include/uuid.h"
+#include "include/ceph_assert.h"
+
+#include "messages/MOSDBoot.h"
+#include "messages/MOSDAlive.h"
+#include "messages/MOSDPGRemove.h"
+#include "messages/MOSDMap.h"
+#include "messages/MPGStats.h"
+#include "messages/MLog.h"
+#include "messages/MOSDPGTemp.h"
+
+using namespace std;
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_
+#undef dout_prefix
+#define dout_prefix _prefix(_dout, get_name())
+static ostream& _prefix(std::ostream *_dout, const string &n) {
+ return *_dout << " stub(" << n << ") ";
+}
+
+
+typedef boost::mt11213b rngen_t;
+typedef boost::scoped_ptr<Messenger> MessengerRef;
+typedef boost::scoped_ptr<Objecter> ObjecterRef;
+
+class TestStub : public Dispatcher
+{
+ protected:
+ MessengerRef messenger;
+ ceph::async::io_context_pool poolctx;
+ MonClient monc;
+
+ ceph::mutex lock;
+ ceph::condition_variable cond;
+ SafeTimer timer;
+
+ bool do_shutdown;
+ double tick_seconds;
+
+ struct C_Tick : public Context {
+ TestStub *s;
+ explicit C_Tick(TestStub *stub) : s(stub) {}
+ void finish(int r) override {
+ generic_dout(20) << "C_Tick::" << __func__ << dendl;
+ if (r == -ECANCELED) {
+ generic_dout(20) << "C_Tick::" << __func__
+ << " shutdown" << dendl;
+ return;
+ }
+ s->tick();
+ }
+ };
+
+ bool ms_dispatch(Message *m) override = 0;
+ void ms_handle_connect(Connection *con) override = 0;
+ void ms_handle_remote_reset(Connection *con) override = 0;
+ virtual int _shutdown() = 0;
+ // courtesy method to be implemented by the stubs at their
+ // own discretion
+ virtual void _tick() { }
+ // different stubs may have different needs; if a stub needs
+ // to tick, then it must call this function.
+ void start_ticking(double t=1.0) {
+ tick_seconds = t;
+ if (t <= 0) {
+ stop_ticking();
+ return;
+ }
+ dout(20) << __func__ << " adding tick timer" << dendl;
+ timer.add_event_after(tick_seconds, new C_Tick(this));
+ }
+ // If we have a function to start ticking that the stubs can
+ // use at their own discretion, then we should also have a
+ // function to disable said ticking to be used the same way.
+ // Just in case.
+ // For simplicity's sake, we don't cancel the tick right off
+ // the bat; instead, we wait for the next tick to kick in and
+ // disable itself.
+ void stop_ticking() {
+ dout(20) << __func__ << " disable tick" << dendl;
+ tick_seconds = 0;
+ }
+
+ public:
+ void tick() {
+ std::cout << __func__ << std::endl;
+ if (do_shutdown || (tick_seconds <= 0)) {
+ std::cout << __func__ << " "
+ << (do_shutdown ? "shutdown" : "stop ticking")
+ << std::endl;
+ return;
+ }
+ _tick();
+ timer.add_event_after(tick_seconds, new C_Tick(this));
+ }
+
+ virtual const string get_name() = 0;
+ virtual int init() = 0;
+
+ virtual int shutdown() {
+ std::lock_guard l{lock};
+ do_shutdown = true;
+ int r = _shutdown();
+ if (r < 0) {
+ dout(10) << __func__ << " error shutting down: "
+ << cpp_strerror(-r) << dendl;
+ return r;
+ }
+ monc.shutdown();
+ timer.shutdown();
+ messenger->shutdown();
+ poolctx.finish();
+ return 0;
+ }
+
+ virtual void print(ostream &out) {
+ out << "stub(" << get_name() << ")";
+ }
+
+ void wait() {
+ if (messenger != NULL)
+ messenger->wait();
+ }
+
+ TestStub(CephContext *cct, string who)
+ : Dispatcher(cct),
+ monc(cct, poolctx),
+ lock(ceph::make_mutex(who.append("::lock"))),
+ timer(cct, lock),
+ do_shutdown(false),
+ tick_seconds(0.0) { }
+};
+
+class ClientStub : public TestStub
+{
+ ObjecterRef objecter;
+ rngen_t gen;
+
+ protected:
+ bool ms_dispatch(Message *m) override {
+ std::lock_guard l{lock};
+ dout(1) << "client::" << __func__ << " " << *m << dendl;
+ switch (m->get_type()) {
+ case CEPH_MSG_OSD_MAP:
+ objecter->handle_osd_map((MOSDMap*)m);
+ cond.notify_all();
+ break;
+ }
+ return true;
+ }
+
+ void ms_handle_connect(Connection *con) override {
+ dout(1) << "client::" << __func__ << " " << con << dendl;
+ std::lock_guard l{lock};
+ objecter->ms_handle_connect(con);
+ }
+
+ void ms_handle_remote_reset(Connection *con) override {
+ dout(1) << "client::" << __func__ << " " << con << dendl;
+ std::lock_guard l{lock};
+ objecter->ms_handle_remote_reset(con);
+ }
+
+ bool ms_handle_reset(Connection *con) override {
+ dout(1) << "client::" << __func__ << dendl;
+ std::lock_guard l{lock};
+ objecter->ms_handle_reset(con);
+ return false;
+ }
+
+ bool ms_handle_refused(Connection *con) override {
+ return false;
+ }
+
+ const string get_name() override {
+ return "client";
+ }
+
+ int _shutdown() override {
+ if (objecter) {
+ objecter->shutdown();
+ }
+ return 0;
+ }
+
+ public:
+ explicit ClientStub(CephContext *cct)
+ : TestStub(cct, "client"),
+ gen((int) time(NULL))
+ { }
+
+ int init() override {
+ int err;
+ poolctx.start(1);
+ err = monc.build_initial_monmap();
+ if (err < 0) {
+ derr << "ClientStub::" << __func__ << " ERROR: build initial monmap: "
+ << cpp_strerror(err) << dendl;
+ return err;
+ }
+
+ messenger.reset(Messenger::create_client_messenger(cct, "stubclient"));
+ ceph_assert(messenger.get() != NULL);
+
+ messenger->set_default_policy(
+ Messenger::Policy::lossy_client(CEPH_FEATURE_OSDREPLYMUX));
+ dout(10) << "ClientStub::" << __func__ << " starting messenger at "
+ << messenger->get_myaddrs() << dendl;
+
+ objecter.reset(new Objecter(cct, messenger.get(), &monc, poolctx));
+ ceph_assert(objecter.get() != NULL);
+ objecter->set_balanced_budget();
+
+ monc.set_messenger(messenger.get());
+ objecter->init();
+ messenger->add_dispatcher_head(this);
+ messenger->start();
+ monc.set_want_keys(CEPH_ENTITY_TYPE_MON|CEPH_ENTITY_TYPE_OSD);
+
+ err = monc.init();
+ if (err < 0) {
+ derr << "ClientStub::" << __func__ << " monc init error: "
+ << cpp_strerror(-err) << dendl;
+ return err;
+ }
+
+ err = monc.authenticate();
+ if (err < 0) {
+ derr << "ClientStub::" << __func__ << " monc authenticate error: "
+ << cpp_strerror(-err) << dendl;
+ monc.shutdown();
+ return err;
+ }
+ monc.wait_auth_rotating(30.0);
+
+ objecter->set_client_incarnation(0);
+ objecter->start();
+
+ lock.lock();
+ timer.init();
+ monc.renew_subs();
+
+ lock.unlock();
+
+ objecter->wait_for_osd_map();
+
+ dout(10) << "ClientStub::" << __func__ << " done" << dendl;
+ return 0;
+ }
+};
+
+class OSDStub : public TestStub
+{
+ int whoami;
+ OSDSuperblock sb;
+ OSDMap osdmap;
+ osd_stat_t osd_stat;
+
+ map<pg_t,pg_stat_t> pgs;
+ set<pg_t> pgs_changes;
+
+ rngen_t gen;
+ boost::uniform_int<> mon_osd_rng;
+
+ utime_t last_boot_attempt;
+ static const double STUB_BOOT_INTERVAL;
+
+
+ public:
+
+ enum {
+ STUB_MON_OSD_ALIVE = 1,
+ STUB_MON_OSD_PGTEMP = 2,
+ STUB_MON_OSD_FAILURE = 3,
+ STUB_MON_OSD_PGSTATS = 4,
+ STUB_MON_LOG = 5,
+
+ STUB_MON_OSD_FIRST = STUB_MON_OSD_ALIVE,
+ STUB_MON_OSD_LAST = STUB_MON_LOG,
+ };
+
+ struct C_CreatePGs : public Context {
+ OSDStub *s;
+ explicit C_CreatePGs(OSDStub *stub) : s(stub) {}
+ void finish(int r) override {
+ if (r == -ECANCELED) {
+ generic_dout(20) << "C_CreatePGs::" << __func__
+ << " shutdown" << dendl;
+ return;
+ }
+ generic_dout(20) << "C_CreatePGs::" << __func__ << dendl;
+ s->auto_create_pgs();
+ }
+ };
+
+
+ OSDStub(int _whoami, CephContext *cct)
+ : TestStub(cct, "osd"),
+ whoami(_whoami),
+ gen(whoami),
+ mon_osd_rng(STUB_MON_OSD_FIRST, STUB_MON_OSD_LAST)
+ {
+ dout(20) << __func__ << " auth supported: "
+ << cct->_conf->auth_supported << dendl;
+ stringstream ss;
+ ss << "client-osd" << whoami;
+ std::string public_msgr_type = cct->_conf->ms_public_type.empty() ? cct->_conf.get_val<std::string>("ms_type") : cct->_conf->ms_public_type;
+ messenger.reset(Messenger::create(cct, public_msgr_type, entity_name_t::OSD(whoami),
+ ss.str().c_str(), getpid()));
+
+ Throttle throttler(g_ceph_context, "osd_client_bytes",
+ g_conf()->osd_client_message_size_cap);
+
+ messenger->set_default_policy(
+ Messenger::Policy::stateless_server(0));
+ messenger->set_policy_throttlers(entity_name_t::TYPE_CLIENT,
+ &throttler, NULL);
+ messenger->set_policy(entity_name_t::TYPE_MON,
+ Messenger::Policy::lossy_client(
+ CEPH_FEATURE_UID |
+ CEPH_FEATURE_PGID64 |
+ CEPH_FEATURE_OSDENC));
+ messenger->set_policy(entity_name_t::TYPE_OSD,
+ Messenger::Policy::stateless_server(0));
+
+ dout(10) << __func__ << " public addr " << g_conf()->public_addr << dendl;
+ int err = messenger->bind(g_conf()->public_addr);
+ if (err < 0)
+ exit(1);
+
+ if (monc.build_initial_monmap() < 0)
+ exit(1);
+
+ messenger->start();
+ monc.set_messenger(messenger.get());
+ }
+
+ int init() override {
+ dout(10) << __func__ << dendl;
+ std::lock_guard l{lock};
+
+ dout(1) << __func__ << " fsid " << monc.monmap.fsid
+ << " osd_fsid " << g_conf()->osd_uuid << dendl;
+ dout(1) << __func__ << " name " << g_conf()->name << dendl;
+
+ timer.init();
+ messenger->add_dispatcher_head(this);
+ monc.set_want_keys(CEPH_ENTITY_TYPE_MON | CEPH_ENTITY_TYPE_OSD);
+
+ int err = monc.init();
+ if (err < 0) {
+ derr << __func__ << " monc init error: "
+ << cpp_strerror(-err) << dendl;
+ return err;
+ }
+
+ err = monc.authenticate();
+ if (err < 0) {
+ derr << __func__ << " monc authenticate error: "
+ << cpp_strerror(-err) << dendl;
+ monc.shutdown();
+ return err;
+ }
+ ceph_assert(!monc.get_fsid().is_zero());
+
+ monc.wait_auth_rotating(30.0);
+
+
+ dout(10) << __func__ << " creating osd superblock" << dendl;
+ sb.cluster_fsid = monc.monmap.fsid;
+ sb.osd_fsid.generate_random();
+ sb.whoami = whoami;
+ sb.compat_features = CompatSet();
+ dout(20) << __func__ << " " << sb << dendl;
+ dout(20) << __func__ << " osdmap " << osdmap << dendl;
+
+ update_osd_stat();
+
+ start_ticking();
+ // give a chance to the mons to inform us of what PGs we should create
+ timer.add_event_after(30.0, new C_CreatePGs(this));
+
+ return 0;
+ }
+
+ int _shutdown() override {
+
+ return 0;
+ }
+
+ void boot() {
+ dout(1) << __func__ << " boot?" << dendl;
+
+ utime_t now = ceph_clock_now();
+ if ((last_boot_attempt > 0.0)
+ && ((now - last_boot_attempt)) <= STUB_BOOT_INTERVAL) {
+ dout(1) << __func__ << " backoff and try again later." << dendl;
+ return;
+ }
+
+ dout(1) << __func__ << " boot!" << dendl;
+ MOSDBoot *mboot = new MOSDBoot;
+ mboot->sb = sb;
+ last_boot_attempt = now;
+ monc.send_mon_message(mboot);
+ }
+
+ void add_pg(pg_t pgid, epoch_t epoch, pg_t parent) {
+
+ utime_t now = ceph_clock_now();
+
+ pg_stat_t s;
+ s.created = epoch;
+ s.last_epoch_clean = epoch;
+ s.parent = parent;
+ s.state |= PG_STATE_CLEAN | PG_STATE_ACTIVE;
+ s.last_fresh = now;
+ s.last_change = now;
+ s.last_clean = now;
+ s.last_active = now;
+ s.last_unstale = now;
+
+ pgs[pgid] = s;
+ pgs_changes.insert(pgid);
+ }
+
+ void auto_create_pgs() {
+ bool has_pgs = !pgs.empty();
+ dout(10) << __func__
+ << ": " << (has_pgs ? "has pgs; ignore" : "create pgs") << dendl;
+ if (has_pgs)
+ return;
+
+ if (!osdmap.get_epoch()) {
+ dout(1) << __func__
+ << " still don't have osdmap; reschedule pg creation" << dendl;
+ timer.add_event_after(10.0, new C_CreatePGs(this));
+ return;
+ }
+
+ auto& osdmap_pools = osdmap.get_pools();
+ for (auto pit = osdmap_pools.begin(); pit != osdmap_pools.end(); ++pit) {
+ const int64_t pool_id = pit->first;
+ const pg_pool_t &pool = pit->second;
+ int ruleno = pool.get_crush_rule();
+
+ if (!osdmap.crush->rule_exists(ruleno)) {
+ dout(20) << __func__
+ << " no crush rule for pool id " << pool_id
+ << " rule no " << ruleno << dendl;
+ continue;
+ }
+
+ epoch_t pool_epoch = pool.get_last_change();
+ dout(20) << __func__
+ << " pool num pgs " << pool.get_pg_num()
+ << " epoch " << pool_epoch << dendl;
+
+ for (ps_t ps = 0; ps < pool.get_pg_num(); ++ps) {
+ pg_t pgid(ps, pool_id);
+ pg_t parent;
+ dout(20) << __func__
+ << " pgid " << pgid << " parent " << parent << dendl;
+ add_pg(pgid, pool_epoch, parent);
+ }
+ }
+ }
+
+ void update_osd_stat() {
+ struct statfs stbuf;
+ int ret = statfs(".", &stbuf);
+ if (ret < 0) {
+ ret = -errno;
+ dout(0) << __func__
+ << " cannot statfs ." << cpp_strerror(ret) << dendl;
+ return;
+ }
+
+ osd_stat.statfs.total = stbuf.f_blocks * stbuf.f_bsize;
+ osd_stat.statfs.available = stbuf.f_bavail * stbuf.f_bsize;
+ osd_stat.statfs.internally_reserved = 0;
+ }
+
+ void send_pg_stats() {
+ dout(10) << __func__
+ << " pgs " << pgs.size() << " osdmap " << osdmap << dendl;
+ MPGStats *mstats = new MPGStats(monc.get_fsid(), osdmap.get_epoch());
+
+ mstats->set_tid(1);
+ mstats->osd_stat = osd_stat;
+
+ set<pg_t>::iterator it;
+ for (it = pgs_changes.begin(); it != pgs_changes.end(); ++it) {
+ pg_t pgid = (*it);
+ if (pgs.count(pgid) == 0) {
+ derr << __func__
+ << " pgid " << pgid << " not on our map" << dendl;
+ ceph_abort_msg("pgid not on our map");
+ }
+ pg_stat_t &s = pgs[pgid];
+ mstats->pg_stat[pgid] = s;
+
+ JSONFormatter f(true);
+ s.dump(&f);
+ dout(20) << __func__
+ << " pg " << pgid << " stats:\n";
+ f.flush(*_dout);
+ *_dout << dendl;
+
+ }
+ dout(10) << __func__ << " send " << *mstats << dendl;
+ monc.send_mon_message(mstats);
+ }
+
+ void modify_pg(pg_t pgid) {
+ dout(10) << __func__ << " pg " << pgid << dendl;
+ ceph_assert(pgs.count(pgid) > 0);
+
+ pg_stat_t &s = pgs[pgid];
+ utime_t now = ceph_clock_now();
+
+ if (now - s.last_change < 10.0) {
+ dout(10) << __func__
+ << " pg " << pgid << " changed in the last 10s" << dendl;
+ return;
+ }
+
+ s.state ^= PG_STATE_CLEAN;
+ if (s.state & PG_STATE_CLEAN)
+ s.last_clean = now;
+ s.last_change = now;
+ s.reported_seq++;
+
+ pgs_changes.insert(pgid);
+ }
+
+ void modify_pgs() {
+ dout(10) << __func__ << dendl;
+
+ if (pgs.empty()) {
+ dout(1) << __func__
+ << " no pgs available! don't attempt to modify." << dendl;
+ return;
+ }
+
+ boost::uniform_int<> pg_rng(0, pgs.size()-1);
+ set<int> pgs_pos;
+
+ int num_pgs = pg_rng(gen);
+ while ((int)pgs_pos.size() < num_pgs)
+ pgs_pos.insert(pg_rng(gen));
+
+ map<pg_t,pg_stat_t>::iterator it = pgs.begin();
+ set<int>::iterator pos_it = pgs_pos.begin();
+
+ int pgs_at = 0;
+ while (pos_it != pgs_pos.end()) {
+ int at = *pos_it;
+ dout(20) << __func__ << " pg at pos " << at << dendl;
+ while ((pgs_at != at) && (it != pgs.end())) {
+ ++it;
+ ++pgs_at;
+ }
+ ceph_assert(it != pgs.end());
+ dout(20) << __func__
+ << " pg at pos " << at << ": " << it->first << dendl;
+ modify_pg(it->first);
+ ++pos_it;
+ }
+ }
+
+ void op_alive() {
+ dout(10) << __func__ << dendl;
+ if (!osdmap.exists(whoami)) {
+ dout(0) << __func__ << " I'm not in the osdmap!!\n";
+ JSONFormatter f(true);
+ osdmap.dump(&f);
+ f.flush(*_dout);
+ *_dout << dendl;
+ }
+ if (osdmap.get_epoch() == 0) {
+ dout(1) << __func__ << " wait for osdmap" << dendl;
+ return;
+ }
+ epoch_t up_thru = osdmap.get_up_thru(whoami);
+ dout(10) << __func__ << "up_thru: " << osdmap.get_up_thru(whoami) << dendl;
+
+ monc.send_mon_message(new MOSDAlive(osdmap.get_epoch(), up_thru));
+ }
+
+ void op_pgtemp() {
+ if (osdmap.get_epoch() == 0) {
+ dout(1) << __func__ << " wait for osdmap" << dendl;
+ return;
+ }
+ dout(10) << __func__ << dendl;
+ MOSDPGTemp *m = new MOSDPGTemp(osdmap.get_epoch());
+ monc.send_mon_message(m);
+ }
+
+ void op_failure() {
+ dout(10) << __func__ << dendl;
+ }
+
+ void op_pgstats() {
+ dout(10) << __func__ << dendl;
+
+ modify_pgs();
+ if (!pgs_changes.empty())
+ send_pg_stats();
+ monc.sub_want("osd_pg_creates", 0, CEPH_SUBSCRIBE_ONETIME);
+ monc.renew_subs();
+
+ dout(20) << __func__ << " pg pools:\n";
+
+ JSONFormatter f(true);
+ f.open_array_section("pools");
+ auto& osdmap_pools = osdmap.get_pools();
+ for (auto pit = osdmap_pools.begin(); pit != osdmap_pools.end(); ++pit) {
+ const int64_t pool_id = pit->first;
+ const pg_pool_t &pool = pit->second;
+ f.open_object_section("pool");
+ f.dump_int("pool_id", pool_id);
+ f.open_object_section("pool_dump");
+ pool.dump(&f);
+ f.close_section();
+ f.close_section();
+ }
+ f.close_section();
+ f.flush(*_dout);
+ *_dout << dendl;
+ }
+
+ void op_log() {
+ dout(10) << __func__ << dendl;
+
+ MLog *m = new MLog(monc.get_fsid());
+
+ boost::uniform_int<> log_rng(1, 10);
+ size_t num_entries = log_rng(gen);
+ dout(10) << __func__
+ << " send " << num_entries << " log messages" << dendl;
+
+ utime_t now = ceph_clock_now();
+ int seq = 0;
+ for (; num_entries > 0; --num_entries) {
+ LogEntry e;
+ e.rank = messenger->get_myname();
+ e.addrs = messenger->get_myaddrs();
+ e.stamp = now;
+ e.seq = seq++;
+ e.prio = CLOG_DEBUG;
+ e.msg = "OSDStub::op_log";
+ m->entries.push_back(e);
+ }
+
+ monc.send_mon_message(m);
+ }
+
+ void _tick() override {
+ if (!osdmap.exists(whoami)) {
+ std::cout << __func__ << " not in the cluster; boot!" << std::endl;
+ boot();
+ return;
+ }
+
+ update_osd_stat();
+
+ boost::uniform_int<> op_rng(STUB_MON_OSD_FIRST, STUB_MON_OSD_LAST);
+ int op = op_rng(gen);
+ switch (op) {
+ case STUB_MON_OSD_ALIVE:
+ op_alive();
+ break;
+ case STUB_MON_OSD_PGTEMP:
+ op_pgtemp();
+ break;
+ case STUB_MON_OSD_FAILURE:
+ op_failure();
+ break;
+ case STUB_MON_OSD_PGSTATS:
+ op_pgstats();
+ break;
+ case STUB_MON_LOG:
+ op_log();
+ break;
+ }
+ }
+
+ void handle_osd_map(MOSDMap *m) {
+ dout(1) << __func__ << dendl;
+ if (m->fsid != monc.get_fsid()) {
+ dout(0) << __func__
+ << " message fsid " << m->fsid << " != " << monc.get_fsid()
+ << dendl;
+ dout(0) << __func__ << " " << m
+ << " from " << m->get_source_inst()
+ << dendl;
+ dout(0) << monc.get_monmap() << dendl;
+ }
+ ceph_assert(m->fsid == monc.get_fsid());
+
+ epoch_t first = m->get_first();
+ epoch_t last = m->get_last();
+ dout(5) << __func__
+ << " epochs [" << first << "," << last << "]"
+ << " current " << osdmap.get_epoch() << dendl;
+
+ if (last <= osdmap.get_epoch()) {
+ dout(5) << __func__ << " no new maps here; dropping" << dendl;
+ m->put();
+ return;
+ }
+
+ if (first > osdmap.get_epoch() + 1) {
+ dout(5) << __func__
+ << osdmap.get_epoch() + 1 << ".." << (first-1) << dendl;
+ if ((m->cluster_osdmap_trim_lower_bound <
+ first && osdmap.get_epoch() == 0) ||
+ m->cluster_osdmap_trim_lower_bound <=
+ osdmap.get_epoch()) {
+ monc.sub_want("osdmap", osdmap.get_epoch()+1,
+ CEPH_SUBSCRIBE_ONETIME);
+ monc.renew_subs();
+ m->put();
+ return;
+ }
+ }
+
+ epoch_t start_full = std::max(osdmap.get_epoch() + 1, first);
+
+ if (m->maps.size() > 0) {
+ map<epoch_t,bufferlist>::reverse_iterator rit;
+ rit = m->maps.rbegin();
+ if (start_full <= rit->first) {
+ start_full = rit->first;
+ dout(5) << __func__
+ << " full epoch " << start_full << dendl;
+ bufferlist &bl = rit->second;
+ auto p = bl.cbegin();
+ osdmap.decode(p);
+ }
+ }
+
+ for (epoch_t e = start_full; e <= last; e++) {
+ map<epoch_t,bufferlist>::iterator it;
+ it = m->incremental_maps.find(e);
+ if (it == m->incremental_maps.end())
+ continue;
+
+ dout(20) << __func__
+ << " incremental epoch " << e
+ << " on full epoch " << start_full << dendl;
+ OSDMap::Incremental inc;
+ bufferlist &bl = it->second;
+ auto p = bl.cbegin();
+ inc.decode(p);
+
+ int err = osdmap.apply_incremental(inc);
+ if (err < 0) {
+ derr << "osd." << whoami << "::" << __func__
+ << "** ERROR: applying incremental: "
+ << cpp_strerror(err) << dendl;
+ ceph_abort_msg("error applying incremental");
+ }
+ }
+ dout(30) << __func__ << "\nosdmap:\n";
+ JSONFormatter f(true);
+ osdmap.dump(&f);
+ f.flush(*_dout);
+ *_dout << dendl;
+
+ if (osdmap.is_up(whoami) &&
+ osdmap.get_addrs(whoami) == messenger->get_myaddrs()) {
+ dout(1) << __func__
+ << " got into the osdmap and we're up!" << dendl;
+ }
+
+ if (m->newest_map && m->newest_map > last) {
+ dout(1) << __func__
+ << " they have more maps; requesting them!" << dendl;
+ monc.sub_want("osdmap", osdmap.get_epoch()+1, CEPH_SUBSCRIBE_ONETIME);
+ monc.renew_subs();
+ }
+
+ dout(10) << __func__ << " done" << dendl;
+ m->put();
+ }
+
+ bool ms_dispatch(Message *m) override {
+ dout(1) << __func__ << " " << *m << dendl;
+
+ switch (m->get_type()) {
+ case CEPH_MSG_OSD_MAP:
+ handle_osd_map((MOSDMap*)m);
+ break;
+ default:
+ m->put();
+ break;
+ }
+ return true;
+ }
+
+ void ms_handle_connect(Connection *con) override {
+ dout(1) << __func__ << " " << con << dendl;
+ if (con->get_peer_type() == CEPH_ENTITY_TYPE_MON) {
+ dout(10) << __func__ << " on mon" << dendl;
+ }
+ }
+
+ void ms_handle_remote_reset(Connection *con) override {}
+
+ bool ms_handle_reset(Connection *con) override {
+ dout(1) << __func__ << dendl;
+ return con->get_priv().get();
+ }
+
+ bool ms_handle_refused(Connection *con) override {
+ return false;
+ }
+
+ const string get_name() override {
+ stringstream ss;
+ ss << "osd." << whoami;
+ return ss.str();
+ }
+};
+
+double const OSDStub::STUB_BOOT_INTERVAL = 10.0;
+
+#undef dout_prefix
+#define dout_prefix *_dout << "main "
+
+const char *our_name = NULL;
+vector<TestStub*> stubs;
+ceph::mutex shutdown_lock = ceph::make_mutex("main::shutdown_lock");
+ceph::condition_variable shutdown_cond;
+Context *shutdown_cb = NULL;
+SafeTimer *shutdown_timer = NULL;
+
+struct C_Shutdown : public Context
+{
+ void finish(int r) override {
+ generic_dout(10) << "main::shutdown time has ran out" << dendl;
+ shutdown_cond.notify_all();
+ }
+};
+
+void handle_test_signal(int signum)
+{
+ if ((signum != SIGINT) && (signum != SIGTERM))
+ return;
+
+ std::cerr << "*** Got signal " << sig_str(signum) << " ***" << std::endl;
+ std::lock_guard l{shutdown_lock};
+ if (shutdown_timer) {
+ shutdown_timer->cancel_all_events();
+ shutdown_cond.notify_all();
+ }
+}
+
+void usage() {
+ ceph_assert(our_name != NULL);
+
+ std::cout << "usage: " << our_name
+ << " <--stub-id ID> [--stub-id ID...]"
+ << std::endl;
+ std::cout << "\n\
+Global Options:\n\
+ -c FILE Read configuration from FILE\n\
+ --keyring FILE Read keyring from FILE\n\
+ --help This message\n\
+\n\
+Test-specific Options:\n\
+ --stub-id ID1..ID2 Interval of OSD ids for multiple stubs to mimic.\n\
+ --stub-id ID OSD id a stub will mimic to be\n\
+ (same as --stub-id ID..ID)\n\
+" << std::endl;
+}
+
+int get_id_interval(int &first, int &last, string &str)
+{
+ size_t found = str.find("..");
+ string first_str, last_str;
+ if (found == string::npos) {
+ first_str = last_str = str;
+ } else {
+ first_str = str.substr(0, found);
+ last_str = str.substr(found+2);
+ }
+
+ string err;
+ first = strict_strtol(first_str.c_str(), 10, &err);
+ if ((first == 0) && (!err.empty())) {
+ std::cerr << err << std::endl;
+ return -1;
+ }
+
+ last = strict_strtol(last_str.c_str(), 10, &err);
+ if ((last == 0) && (!err.empty())) {
+ std::cerr << err << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ our_name = argv[0];
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(nullptr, args,
+ CEPH_ENTITY_TYPE_OSD, CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ set<int> stub_ids;
+ double duration = 300.0;
+
+ for (std::vector<const char*>::iterator i = args.begin(); i != args.end();) {
+ string val;
+
+ if (ceph_argparse_double_dash(args, i)) {
+ break;
+ } else if (ceph_argparse_witharg(args, i, &val,
+ "--stub-id", (char*) NULL)) {
+ int first = -1, last = -1;
+ if (get_id_interval(first, last, val) < 0) {
+ std::cerr << "** error parsing stub id '" << val << "'" << std::endl;
+ exit(1);
+ }
+
+ for (; first <= last; ++first)
+ stub_ids.insert(first);
+ } else if (ceph_argparse_witharg(args, i, &val,
+ "--duration", (char*) NULL)) {
+ string err;
+ duration = (double) strict_strtol(val.c_str(), 10, &err);
+ if ((duration == 0) && (!err.empty())) {
+ std::cerr << "** error parsing '--duration " << val << "': '"
+ << err << std::endl;
+ exit(1);
+ }
+ } else if (ceph_argparse_flag(args, i, "--help", (char*) NULL)) {
+ usage();
+ exit(0);
+ } else {
+ std::cerr << "unknown argument '" << *i << "'" << std::endl;
+ return 1;
+ }
+ }
+
+ if (stub_ids.empty()) {
+ std::cerr << "** error: must specify at least one '--stub-id <ID>'"
+ << std::endl;
+ usage();
+ return 1;
+ }
+
+ for (set<int>::iterator i = stub_ids.begin(); i != stub_ids.end(); ++i) {
+ int whoami = *i;
+
+ std::cout << __func__ << " starting stub." << whoami << std::endl;
+ OSDStub *stub = new OSDStub(whoami, g_ceph_context);
+ int err = stub->init();
+ if (err < 0) {
+ std::cerr << "** osd stub error: " << cpp_strerror(-err) << std::endl;
+ return 1;
+ }
+ stubs.push_back(stub);
+ }
+
+ std::cout << __func__ << " starting client stub" << std::endl;
+ ClientStub *cstub = new ClientStub(g_ceph_context);
+ int err = cstub->init();
+ if (err < 0) {
+ std::cerr << "** client stub error: " << cpp_strerror(-err) << std::endl;
+ return 1;
+ }
+ stubs.push_back(cstub);
+
+ init_async_signal_handler();
+ register_async_signal_handler_oneshot(SIGINT, handle_test_signal);
+ register_async_signal_handler_oneshot(SIGTERM, handle_test_signal);
+
+ {
+ unique_lock locker{shutdown_lock};
+ shutdown_timer = new SafeTimer(g_ceph_context, shutdown_lock);
+ shutdown_timer->init();
+ if (duration != 0) {
+ std::cout << __func__
+ << " run test for " << duration << " seconds" << std::endl;
+ shutdown_timer->add_event_after((double) duration, new C_Shutdown);
+ }
+ shutdown_cond.wait(locker);
+ shutdown_timer->shutdown();
+ delete shutdown_timer;
+ shutdown_timer = NULL;
+ }
+ unregister_async_signal_handler(SIGINT, handle_test_signal);
+ unregister_async_signal_handler(SIGTERM, handle_test_signal);
+
+ std::cout << __func__ << " waiting for stubs to finish" << std::endl;
+ vector<TestStub*>::iterator it;
+ int i;
+ for (i = 0, it = stubs.begin(); it != stubs.end(); ++it, ++i) {
+ if (*it != NULL) {
+ (*it)->shutdown();
+ (*it)->wait();
+ std::cout << __func__ << " finished " << (*it)->get_name() << std::endl;
+ delete (*it);
+ (*it) = NULL;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/test/msgr/CMakeLists.txt b/src/test/msgr/CMakeLists.txt
new file mode 100644
index 000000000..beaa7133d
--- /dev/null
+++ b/src/test/msgr/CMakeLists.txt
@@ -0,0 +1,58 @@
+# ceph_test_async_driver
+add_executable(ceph_test_async_driver
+ test_async_driver.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+target_link_libraries(ceph_test_async_driver os global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS})
+
+# ceph_test_msgr
+add_executable(ceph_test_msgr
+ test_msgr.cc
+ )
+target_link_libraries(ceph_test_msgr os global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS})
+
+# ceph_test_async_networkstack
+add_executable(ceph_test_async_networkstack
+ test_async_networkstack.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+target_link_libraries(ceph_test_async_networkstack global ${CRYPTO_LIBS} ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS})
+
+#ceph_perf_msgr_server
+add_executable(ceph_perf_msgr_server perf_msgr_server.cc)
+target_link_libraries(ceph_perf_msgr_server os global ${UNITTEST_LIBS})
+
+#ceph_perf_msgr_client
+add_executable(ceph_perf_msgr_client perf_msgr_client.cc)
+target_link_libraries(ceph_perf_msgr_client os global ${UNITTEST_LIBS})
+
+# unitttest_frames_v2
+add_executable(unittest_frames_v2 test_frames_v2.cc)
+add_ceph_unittest(unittest_frames_v2)
+target_link_libraries(unittest_frames_v2 os global ${UNITTEST_LIBS})
+
+add_executable(unittest_comp_registry
+ test_comp_registry.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_comp_registry)
+target_link_libraries(unittest_comp_registry global)
+
+# test_userspace_event
+if(HAVE_DPDK)
+ add_executable(ceph_test_userspace_event
+ test_userspace_event.cc
+ $<TARGET_OBJECTS:unit-main>)
+ target_link_libraries(ceph_test_userspace_event
+ global
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS})
+endif(HAVE_DPDK)
+
+install(TARGETS
+ ceph_test_async_driver
+ ceph_test_msgr
+ ceph_test_async_networkstack
+ ceph_perf_msgr_server
+ ceph_perf_msgr_client
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/msgr/perf_msgr_client.cc b/src/test/msgr/perf_msgr_client.cc
new file mode 100644
index 000000000..ffbfc1614
--- /dev/null
+++ b/src/test/msgr/perf_msgr_client.cc
@@ -0,0 +1,219 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Haomai Wang
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string>
+#include <unistd.h>
+#include <iostream>
+
+using namespace std;
+
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "common/Cycles.h"
+#include "global/global_init.h"
+#include "msg/Messenger.h"
+#include "messages/MOSDOp.h"
+#include "auth/DummyAuth.h"
+
+#include <atomic>
+
+class MessengerClient {
+ class ClientThread;
+ class ClientDispatcher : public Dispatcher {
+ uint64_t think_time;
+ ClientThread *thread;
+
+ public:
+ ClientDispatcher(uint64_t delay, ClientThread *t): Dispatcher(g_ceph_context), think_time(delay), thread(t) {}
+ bool ms_can_fast_dispatch_any() const override { return true; }
+ bool ms_can_fast_dispatch(const Message *m) const override {
+ switch (m->get_type()) {
+ case CEPH_MSG_OSD_OPREPLY:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void ms_handle_fast_connect(Connection *con) override {}
+ void ms_handle_fast_accept(Connection *con) override {}
+ bool ms_dispatch(Message *m) override { return true; }
+ void ms_fast_dispatch(Message *m) override;
+ bool ms_handle_reset(Connection *con) override { return true; }
+ void ms_handle_remote_reset(Connection *con) override {}
+ bool ms_handle_refused(Connection *con) override { return false; }
+ int ms_handle_fast_authentication(Connection *con) override {
+ return 1;
+ }
+ };
+
+ class ClientThread : public Thread {
+ Messenger *msgr;
+ int concurrent;
+ ConnectionRef conn;
+ std::atomic<unsigned> client_inc = { 0 };
+ object_t oid;
+ object_locator_t oloc;
+ pg_t pgid;
+ int msg_len;
+ bufferlist data;
+ int ops;
+ ClientDispatcher dispatcher;
+
+ public:
+ ceph::mutex lock = ceph::make_mutex("MessengerBenchmark::ClientThread::lock");
+ ceph::condition_variable cond;
+ uint64_t inflight;
+
+ ClientThread(Messenger *m, int c, ConnectionRef con, int len, int ops, int think_time_us):
+ msgr(m), concurrent(c), conn(con), oid("object-name"), oloc(1, 1), msg_len(len), ops(ops),
+ dispatcher(think_time_us, this), inflight(0) {
+ m->add_dispatcher_head(&dispatcher);
+ bufferptr ptr(msg_len);
+ memset(ptr.c_str(), 0, msg_len);
+ data.append(ptr);
+ }
+ void *entry() override {
+ std::unique_lock locker{lock};
+ for (int i = 0; i < ops; ++i) {
+ if (inflight > uint64_t(concurrent)) {
+ cond.wait(locker);
+ }
+ hobject_t hobj(oid, oloc.key, CEPH_NOSNAP, pgid.ps(), pgid.pool(),
+ oloc.nspace);
+ spg_t spgid(pgid);
+ MOSDOp *m = new MOSDOp(client_inc, 0, hobj, spgid, 0, 0, 0);
+ bufferlist msg_data(data);
+ m->write(0, msg_len, msg_data);
+ inflight++;
+ conn->send_message(m);
+ //cerr << __func__ << " send m=" << m << std::endl;
+ }
+ locker.unlock();
+ msgr->shutdown();
+ return 0;
+ }
+ };
+
+ string type;
+ string serveraddr;
+ int think_time_us;
+ vector<Messenger*> msgrs;
+ vector<ClientThread*> clients;
+ DummyAuthClientServer dummy_auth;
+
+ public:
+ MessengerClient(const string &t, const string &addr, int delay):
+ type(t), serveraddr(addr), think_time_us(delay),
+ dummy_auth(g_ceph_context) {
+ }
+ ~MessengerClient() {
+ for (uint64_t i = 0; i < clients.size(); ++i)
+ delete clients[i];
+ for (uint64_t i = 0; i < msgrs.size(); ++i) {
+ msgrs[i]->shutdown();
+ msgrs[i]->wait();
+ }
+ }
+ void ready(int c, int jobs, int ops, int msg_len) {
+ entity_addr_t addr;
+ addr.parse(serveraddr.c_str());
+ addr.set_nonce(0);
+ dummy_auth.auth_registry.refresh_config();
+ for (int i = 0; i < jobs; ++i) {
+ Messenger *msgr = Messenger::create(g_ceph_context, type, entity_name_t::CLIENT(0), "client", getpid()+i);
+ msgr->set_default_policy(Messenger::Policy::lossless_client(0));
+ msgr->set_auth_client(&dummy_auth);
+ msgr->start();
+ entity_addrvec_t addrs(addr);
+ ConnectionRef conn = msgr->connect_to_osd(addrs);
+ ClientThread *t = new ClientThread(msgr, c, conn, msg_len, ops, think_time_us);
+ msgrs.push_back(msgr);
+ clients.push_back(t);
+ }
+ usleep(1000*1000);
+ }
+ void start() {
+ for (uint64_t i = 0; i < clients.size(); ++i)
+ clients[i]->create("client");
+ for (uint64_t i = 0; i < msgrs.size(); ++i)
+ msgrs[i]->wait();
+ }
+};
+
+void MessengerClient::ClientDispatcher::ms_fast_dispatch(Message *m) {
+ usleep(think_time);
+ m->put();
+ std::lock_guard l{thread->lock};
+ thread->inflight--;
+ thread->cond.notify_all();
+}
+
+
+void usage(const string &name) {
+ cout << "Usage: " << name << " [server ip:port] [numjobs] [concurrency] [ios] [thinktime us] [msg length]" << std::endl;
+ cout << " [server ip:port]: connect to the ip:port pair" << std::endl;
+ cout << " [numjobs]: how much client threads spawned and do benchmark" << std::endl;
+ cout << " [concurrency]: the max inflight messages(like iodepth in fio)" << std::endl;
+ cout << " [ios]: how much messages sent for each client" << std::endl;
+ cout << " [thinktime]: sleep time when do fast dispatching(match client logic)" << std::endl;
+ cout << " [msg length]: message data bytes" << std::endl;
+}
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ if (args.size() < 6) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ int numjobs = atoi(args[1]);
+ int concurrent = atoi(args[2]);
+ int ios = atoi(args[3]);
+ int think_time = atoi(args[4]);
+ int len = atoi(args[5]);
+
+ std::string public_msgr_type = g_ceph_context->_conf->ms_public_type.empty() ? g_ceph_context->_conf.get_val<std::string>("ms_type") : g_ceph_context->_conf->ms_public_type;
+
+ cout << " using ms-public-type " << public_msgr_type << std::endl;
+ cout << " server ip:port " << args[0] << std::endl;
+ cout << " numjobs " << numjobs << std::endl;
+ cout << " concurrency " << concurrent << std::endl;
+ cout << " ios " << ios << std::endl;
+ cout << " thinktime(us) " << think_time << std::endl;
+ cout << " message data bytes " << len << std::endl;
+
+ MessengerClient client(public_msgr_type, args[0], think_time);
+
+ client.ready(concurrent, numjobs, ios, len);
+ Cycles::init();
+ uint64_t start = Cycles::rdtsc();
+ client.start();
+ uint64_t stop = Cycles::rdtsc();
+ cout << " Total op " << (ios * numjobs) << " run time " << Cycles::to_microseconds(stop - start) << "us." << std::endl;
+
+ return 0;
+}
diff --git a/src/test/msgr/perf_msgr_server.cc b/src/test/msgr/perf_msgr_server.cc
new file mode 100644
index 000000000..0c492ab17
--- /dev/null
+++ b/src/test/msgr/perf_msgr_server.cc
@@ -0,0 +1,176 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Haomai Wang
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string>
+#include <unistd.h>
+#include <iostream>
+
+using namespace std;
+
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "common/WorkQueue.h"
+#include "global/global_init.h"
+#include "msg/Messenger.h"
+#include "messages/MOSDOp.h"
+#include "messages/MOSDOpReply.h"
+#include "auth/DummyAuth.h"
+
+class ServerDispatcher : public Dispatcher {
+ uint64_t think_time;
+ ThreadPool op_tp;
+ class OpWQ : public ThreadPool::WorkQueue<Message> {
+ list<Message*> messages;
+
+ public:
+ OpWQ(ceph::timespan timeout, ceph::timespan suicide_timeout, ThreadPool *tp)
+ : ThreadPool::WorkQueue<Message>("ServerDispatcher::OpWQ", timeout, suicide_timeout, tp) {}
+
+ bool _enqueue(Message *m) override {
+ messages.push_back(m);
+ return true;
+ }
+ void _dequeue(Message *m) override {
+ ceph_abort();
+ }
+ bool _empty() override {
+ return messages.empty();
+ }
+ Message *_dequeue() override {
+ if (messages.empty())
+ return NULL;
+ Message *m = messages.front();
+ messages.pop_front();
+ return m;
+ }
+ void _process(Message *m, ThreadPool::TPHandle &handle) override {
+ MOSDOp *osd_op = static_cast<MOSDOp*>(m);
+ MOSDOpReply *reply = new MOSDOpReply(osd_op, 0, 0, 0, false);
+ m->get_connection()->send_message(reply);
+ m->put();
+ }
+ void _process_finish(Message *m) override { }
+ void _clear() override {
+ ceph_assert(messages.empty());
+ }
+ } op_wq;
+
+ public:
+ ServerDispatcher(int threads, uint64_t delay): Dispatcher(g_ceph_context), think_time(delay),
+ op_tp(g_ceph_context, "ServerDispatcher::op_tp", "tp_serv_disp", threads, "serverdispatcher_op_threads"),
+ op_wq(ceph::make_timespan(30), ceph::make_timespan(30), &op_tp) {
+ op_tp.start();
+ }
+ ~ServerDispatcher() override {
+ op_tp.stop();
+ }
+ bool ms_can_fast_dispatch_any() const override { return true; }
+ bool ms_can_fast_dispatch(const Message *m) const override {
+ switch (m->get_type()) {
+ case CEPH_MSG_OSD_OP:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void ms_handle_fast_connect(Connection *con) override {}
+ void ms_handle_fast_accept(Connection *con) override {}
+ bool ms_dispatch(Message *m) override { return true; }
+ bool ms_handle_reset(Connection *con) override { return true; }
+ void ms_handle_remote_reset(Connection *con) override {}
+ bool ms_handle_refused(Connection *con) override { return false; }
+ void ms_fast_dispatch(Message *m) override {
+ usleep(think_time);
+ //cerr << __func__ << " reply message=" << m << std::endl;
+ op_wq.queue(m);
+ }
+ int ms_handle_fast_authentication(Connection *con) override {
+ return 1;
+ }
+};
+
+class MessengerServer {
+ Messenger *msgr;
+ string type;
+ string bindaddr;
+ ServerDispatcher dispatcher;
+ DummyAuthClientServer dummy_auth;
+
+ public:
+ MessengerServer(const string &t, const string &addr, int threads, int delay):
+ msgr(NULL), type(t), bindaddr(addr), dispatcher(threads, delay),
+ dummy_auth(g_ceph_context) {
+ msgr = Messenger::create(g_ceph_context, type, entity_name_t::OSD(0), "server", 0);
+ msgr->set_default_policy(Messenger::Policy::stateless_server(0));
+ dummy_auth.auth_registry.refresh_config();
+ msgr->set_auth_server(&dummy_auth);
+ }
+ ~MessengerServer() {
+ msgr->shutdown();
+ msgr->wait();
+ }
+ void start() {
+ entity_addr_t addr;
+ addr.parse(bindaddr.c_str());
+ msgr->bind(addr);
+ msgr->add_dispatcher_head(&dispatcher);
+ msgr->start();
+ msgr->wait();
+ }
+};
+
+void usage(const string &name) {
+ cerr << "Usage: " << name << " [bind ip:port] [server worker threads] [thinktime us]" << std::endl;
+ cerr << " [bind ip:port]: The ip:port pair to bind, client need to specify this pair to connect" << std::endl;
+ cerr << " [server worker threads]: threads will process incoming messages and reply(matching pg threads)" << std::endl;
+ cerr << " [thinktime]: sleep time when do dispatching(match fast dispatch logic in OSD.cc)" << std::endl;
+}
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ if (args.size() < 3) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ int worker_threads = atoi(args[1]);
+ int think_time = atoi(args[2]);
+ std::string public_msgr_type = g_ceph_context->_conf->ms_public_type.empty() ? g_ceph_context->_conf.get_val<std::string>("ms_type") : g_ceph_context->_conf->ms_public_type;
+
+ cerr << " This tool won't handle connection error alike things, " << std::endl;
+ cerr << "please ensure the proper network environment to test." << std::endl;
+ cerr << " Or ctrl+c when meeting error and restart tests" << std::endl;
+ cerr << " using ms-public-type " << public_msgr_type << std::endl;
+ cerr << " bind ip:port " << args[0] << std::endl;
+ cerr << " worker threads " << worker_threads << std::endl;
+ cerr << " thinktime(us) " << think_time << std::endl;
+
+ MessengerServer server(public_msgr_type, args[0], worker_threads, think_time);
+ server.start();
+
+ return 0;
+}
diff --git a/src/test/msgr/test_async_driver.cc b/src/test/msgr/test_async_driver.cc
new file mode 100644
index 000000000..6cf4211b6
--- /dev/null
+++ b/src/test/msgr/test_async_driver.cc
@@ -0,0 +1,354 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 UnitedStack <haomai@unitedstack.com>
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include "include/Context.h"
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "msg/async/Event.h"
+
+#include <atomic>
+
+// We use epoll, kqueue, evport, select in descending order by performance.
+#if defined(__linux__)
+#define HAVE_EPOLL 1
+#endif
+
+#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
+#define HAVE_KQUEUE 1
+#endif
+
+#ifdef __sun
+#include <sys/feature_tests.h>
+#ifdef _DTRACE_VERSION
+#define HAVE_EVPORT 1
+#endif
+#endif
+
+#ifdef HAVE_EPOLL
+#include "msg/async/EventEpoll.h"
+#endif
+#ifdef HAVE_KQUEUE
+#include "msg/async/EventKqueue.h"
+#endif
+#include "msg/async/EventSelect.h"
+
+#include <gtest/gtest.h>
+
+using namespace std;
+
+class EventDriverTest : public ::testing::TestWithParam<const char*> {
+ public:
+ EventDriver *driver;
+
+ EventDriverTest(): driver(0) {}
+ void SetUp() override {
+ cerr << __func__ << " start set up " << GetParam() << std::endl;
+#ifdef HAVE_EPOLL
+ if (strcmp(GetParam(), "epoll"))
+ driver = new EpollDriver(g_ceph_context);
+#endif
+#ifdef HAVE_KQUEUE
+ if (strcmp(GetParam(), "kqueue"))
+ driver = new KqueueDriver(g_ceph_context);
+#endif
+ if (strcmp(GetParam(), "select"))
+ driver = new SelectDriver(g_ceph_context);
+ driver->init(NULL, 100);
+ }
+ void TearDown() override {
+ delete driver;
+ }
+};
+
+int set_nonblock(int sd)
+{
+ int flags;
+
+ /* Set the socket nonblocking.
+ * Note that fcntl(2) for F_GETFL and F_SETFL can't be
+ * interrupted by a signal. */
+ if ((flags = fcntl(sd, F_GETFL)) < 0 ) {
+ return -1;
+ }
+ if (fcntl(sd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+
+TEST_P(EventDriverTest, PipeTest) {
+ int fds[2];
+ vector<FiredFileEvent> fired_events;
+ int r;
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1;
+
+ r = pipe(fds);
+ ASSERT_EQ(r, 0);
+ r = driver->add_event(fds[0], EVENT_NONE, EVENT_READABLE);
+ ASSERT_EQ(r, 0);
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(r, 0);
+
+ char c = 'A';
+ r = write(fds[1], &c, sizeof(c));
+ ASSERT_EQ(r, 1);
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(fired_events[0].fd, fds[0]);
+
+
+ fired_events.clear();
+ r = write(fds[1], &c, sizeof(c));
+ ASSERT_EQ(r, 1);
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(fired_events[0].fd, fds[0]);
+
+ fired_events.clear();
+ driver->del_event(fds[0], EVENT_READABLE, EVENT_READABLE);
+ r = write(fds[1], &c, sizeof(c));
+ ASSERT_EQ(r, 1);
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(r, 0);
+}
+
+void* echoclient(void *arg)
+{
+ intptr_t port = (intptr_t)arg;
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ char addr[] = "127.0.0.1";
+ int r = inet_pton(AF_INET, addr, &sa.sin_addr);
+ ceph_assert(r == 1);
+
+ int connect_sd = ::socket(AF_INET, SOCK_STREAM, 0);
+ if (connect_sd >= 0) {
+ r = connect(connect_sd, (struct sockaddr*)&sa, sizeof(sa));
+ ceph_assert(r == 0);
+ int t = 0;
+
+ do {
+ char c[] = "banner";
+ r = write(connect_sd, c, sizeof(c));
+ char d[100];
+ r = read(connect_sd, d, sizeof(d));
+ if (r == 0)
+ break;
+ if (t++ == 30)
+ break;
+ } while (1);
+ ::close(connect_sd);
+ }
+ return 0;
+}
+
+TEST_P(EventDriverTest, NetworkSocketTest) {
+ int listen_sd = ::socket(AF_INET, SOCK_STREAM, 0);
+ ASSERT_TRUE(listen_sd > 0);
+ int on = 1;
+ int r = ::setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ ASSERT_EQ(r, 0);
+ r = set_nonblock(listen_sd);
+ ASSERT_EQ(r, 0);
+ struct sockaddr_in sa;
+ long port = 0;
+ for (port = 38788; port < 40000; port++) {
+ memset(&sa,0,sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ r = ::bind(listen_sd, (struct sockaddr *)&sa, sizeof(sa));
+ if (r == 0) {
+ break;
+ }
+ }
+ ASSERT_EQ(r, 0);
+ r = listen(listen_sd, 511);
+ ASSERT_EQ(r, 0);
+
+ vector<FiredFileEvent> fired_events;
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1;
+ r = driver->add_event(listen_sd, EVENT_NONE, EVENT_READABLE);
+ ASSERT_EQ(r, 0);
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(r, 0);
+
+ fired_events.clear();
+ pthread_t thread1;
+ r = pthread_create(&thread1, NULL, echoclient, (void*)(intptr_t)port);
+ ASSERT_EQ(r, 0);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(r, 1);
+ ASSERT_EQ(fired_events[0].fd, listen_sd);
+
+ fired_events.clear();
+ int client_sd = ::accept(listen_sd, NULL, NULL);
+ ASSERT_TRUE(client_sd > 0);
+ r = driver->add_event(client_sd, EVENT_NONE, EVENT_READABLE);
+ ASSERT_EQ(r, 0);
+
+ do {
+ fired_events.clear();
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ(EVENT_READABLE, fired_events[0].mask);
+
+ fired_events.clear();
+ char data[100];
+ r = ::read(client_sd, data, sizeof(data));
+ if (r == 0)
+ break;
+ ASSERT_GT(r, 0);
+ r = driver->add_event(client_sd, EVENT_READABLE, EVENT_WRITABLE);
+ ASSERT_EQ(0, r);
+ r = driver->event_wait(fired_events, &tv);
+ ASSERT_EQ(1, r);
+ ASSERT_EQ(fired_events[0].mask, EVENT_WRITABLE);
+ r = write(client_sd, data, strlen(data));
+ ASSERT_EQ((int)strlen(data), r);
+ driver->del_event(client_sd, EVENT_READABLE|EVENT_WRITABLE,
+ EVENT_WRITABLE);
+ } while (1);
+
+ ::close(client_sd);
+ ::close(listen_sd);
+}
+
+class FakeEvent : public EventCallback {
+
+ public:
+ void do_request(uint64_t fd_or_id) override {}
+};
+
+TEST(EventCenterTest, FileEventExpansion) {
+ vector<int> sds;
+ EventCenter center(g_ceph_context);
+ center.init(100, 0, "posix");
+ center.set_owner();
+ EventCallbackRef e(new FakeEvent());
+ for (int i = 0; i < 300; i++) {
+ int sd = ::socket(AF_INET, SOCK_STREAM, 0);
+ center.create_file_event(sd, EVENT_READABLE, e);
+ sds.push_back(sd);
+ }
+
+ for (vector<int>::iterator it = sds.begin(); it != sds.end(); ++it)
+ center.delete_file_event(*it, EVENT_READABLE);
+}
+
+
+class Worker : public Thread {
+ CephContext *cct;
+ bool done;
+
+ public:
+ EventCenter center;
+ explicit Worker(CephContext *c, int idx): cct(c), done(false), center(c) {
+ center.init(100, idx, "posix");
+ }
+ void stop() {
+ done = true;
+ center.wakeup();
+ }
+ void* entry() override {
+ center.set_owner();
+ while (!done)
+ center.process_events(1000000);
+ return 0;
+ }
+};
+
+class CountEvent: public EventCallback {
+ std::atomic<unsigned> *count;
+ ceph::mutex *lock;
+ ceph::condition_variable *cond;
+
+ public:
+ CountEvent(std::atomic<unsigned> *atomic,
+ ceph::mutex *l, ceph::condition_variable *c)
+ : count(atomic), lock(l), cond(c) {}
+ void do_request(uint64_t id) override {
+ std::scoped_lock l{*lock};
+ (*count)--;
+ cond->notify_all();
+ }
+};
+
+TEST(EventCenterTest, DispatchTest) {
+ Worker worker1(g_ceph_context, 1), worker2(g_ceph_context, 2);
+ std::atomic<unsigned> count = { 0 };
+ ceph::mutex lock = ceph::make_mutex("DispatchTest::lock");
+ ceph::condition_variable cond;
+ worker1.create("worker_1");
+ worker2.create("worker_2");
+ for (int i = 0; i < 10000; ++i) {
+ count++;
+ worker1.center.dispatch_event_external(EventCallbackRef(new CountEvent(&count, &lock, &cond)));
+ count++;
+ worker2.center.dispatch_event_external(EventCallbackRef(new CountEvent(&count, &lock, &cond)));
+ std::unique_lock l{lock};
+ cond.wait(l, [&] { return count == 0; });
+ }
+ worker1.stop();
+ worker2.stop();
+ worker1.join();
+ worker2.join();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AsyncMessenger,
+ EventDriverTest,
+ ::testing::Values(
+#ifdef HAVE_EPOLL
+ "epoll",
+#endif
+#ifdef HAVE_KQUEUE
+ "kqueue",
+#endif
+ "select"
+ )
+);
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make ceph_test_async_driver &&
+ * ./ceph_test_async_driver
+ *
+ * End:
+ */
diff --git a/src/test/msgr/test_async_networkstack.cc b/src/test/msgr/test_async_networkstack.cc
new file mode 100644
index 000000000..14923fd7d
--- /dev/null
+++ b/src/test/msgr/test_async_networkstack.cc
@@ -0,0 +1,1072 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 XSky <haomai@xsky.com>
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <algorithm>
+#include <atomic>
+#include <iostream>
+#include <list>
+#include <random>
+#include <string>
+#include <set>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "acconfig.h"
+#include "common/config_obs.h"
+#include "include/Context.h"
+#include "msg/async/Event.h"
+#include "msg/async/Stack.h"
+
+using namespace std;
+
+class NoopConfigObserver : public md_config_obs_t {
+ std::list<std::string> options;
+ const char **ptrs = 0;
+
+public:
+ NoopConfigObserver(std::list<std::string> l) : options(l) {
+ ptrs = new const char*[options.size() + 1];
+ unsigned j = 0;
+ for (auto& i : options) {
+ ptrs[j++] = i.c_str();
+ }
+ ptrs[j] = 0;
+ }
+ ~NoopConfigObserver() {
+ delete[] ptrs;
+ }
+
+ const char** get_tracked_conf_keys() const override {
+ return ptrs;
+ }
+ void handle_conf_change(const ConfigProxy& conf,
+ const std::set <std::string> &changed) override {
+ }
+};
+
+class NetworkWorkerTest : public ::testing::TestWithParam<const char*> {
+ public:
+ std::shared_ptr<NetworkStack> stack;
+ string addr, port_addr;
+
+ NoopConfigObserver fake_obs = {{"ms_type",
+ "ms_dpdk_coremask",
+ "ms_dpdk_host_ipv4_addr",
+ "ms_dpdk_gateway_ipv4_addr",
+ "ms_dpdk_netmask_ipv4_addr"}};
+
+ NetworkWorkerTest() {}
+ void SetUp() override {
+ cerr << __func__ << " start set up " << GetParam() << std::endl;
+ if (strncmp(GetParam(), "dpdk", 4)) {
+ g_ceph_context->_conf.set_val("ms_type", "async+posix");
+ addr = "127.0.0.1:15000";
+ port_addr = "127.0.0.1:15001";
+ } else {
+ g_ceph_context->_conf.set_val_or_die("ms_dpdk_debug_allow_loopback", "true");
+ g_ceph_context->_conf.set_val_or_die("ms_async_op_threads", "2");
+ string ipv4_addr = g_ceph_context->_conf.get_val<std::string>("ms_dpdk_host_ipv4_addr");
+ addr = ipv4_addr + std::string(":15000");
+ port_addr = ipv4_addr + std::string(":15001");
+ }
+ stack = NetworkStack::create(g_ceph_context, GetParam());
+ stack->start();
+ }
+ void TearDown() override {
+ stack->stop();
+ }
+ string get_addr() const {
+ return addr;
+ }
+ string get_ip_different_port() const {
+ return port_addr;
+ }
+ string get_different_ip() const {
+ return "10.0.123.100:4323";
+ }
+ EventCenter *get_center(unsigned i) {
+ return &stack->get_worker(i)->center;
+ }
+ Worker *get_worker(unsigned i) {
+ return stack->get_worker(i);
+ }
+ template<typename func>
+ class C_dispatch : public EventCallback {
+ Worker *worker;
+ func f;
+ std::atomic_bool done;
+ public:
+ C_dispatch(Worker *w, func &&_f): worker(w), f(std::move(_f)), done(false) {}
+ void do_request(uint64_t id) override {
+ f(worker);
+ done = true;
+ }
+ void wait() {
+ int us = 1000 * 1000 * 1000;
+ while (!done) {
+ ASSERT_TRUE(us > 0);
+ usleep(100);
+ us -= 100;
+ }
+ }
+ };
+ template<typename func>
+ void exec_events(func &&f) {
+ std::vector<C_dispatch<func>*> dis;
+ for (unsigned i = 0; i < stack->get_num_worker(); ++i) {
+ Worker *w = stack->get_worker(i);
+ C_dispatch<func> *e = new C_dispatch<func>(w, std::move(f));
+ stack->get_worker(i)->center.dispatch_event_external(e);
+ dis.push_back(e);
+ }
+
+ for (auto &&e : dis) {
+ e->wait();
+ delete e;
+ }
+ }
+};
+
+class C_poll : public EventCallback {
+ EventCenter *center;
+ std::atomic<bool> woken;
+ static const int sleepus = 500;
+
+ public:
+ explicit C_poll(EventCenter *c): center(c), woken(false) {}
+ void do_request(uint64_t r) override {
+ woken = true;
+ }
+ bool poll(int milliseconds) {
+ auto start = ceph::coarse_real_clock::now();
+ while (!woken) {
+ center->process_events(sleepus);
+ usleep(sleepus);
+ auto r = std::chrono::duration_cast<std::chrono::milliseconds>(
+ ceph::coarse_real_clock::now() - start);
+ if (r >= std::chrono::milliseconds(milliseconds))
+ break;
+ }
+ return woken;
+ }
+ void reset() {
+ woken = false;
+ }
+};
+
+TEST_P(NetworkWorkerTest, SimpleTest) {
+ entity_addr_t bind_addr;
+ ASSERT_TRUE(bind_addr.parse(get_addr().c_str()));
+ std::atomic_bool accepted(false);
+ std::atomic_bool *accepted_p = &accepted;
+
+ exec_events([this, accepted_p, bind_addr](Worker *worker) mutable {
+ entity_addr_t cli_addr;
+ SocketOptions options;
+ ServerSocket bind_socket;
+ EventCenter *center = &worker->center;
+ ssize_t r = 0;
+ if (stack->support_local_listen_table() || worker->id == 0)
+ r = worker->listen(bind_addr, 0, options, &bind_socket);
+ ASSERT_EQ(0, r);
+
+ ConnectedSocket cli_socket, srv_socket;
+ if (worker->id == 0) {
+ r = worker->connect(bind_addr, options, &cli_socket);
+ ASSERT_EQ(0, r);
+ }
+
+ bool is_my_accept = false;
+ if (bind_socket) {
+ C_poll cb(center);
+ center->create_file_event(bind_socket.fd(), EVENT_READABLE, &cb);
+ if (cb.poll(500)) {
+ *accepted_p = true;
+ is_my_accept = true;
+ }
+ ASSERT_TRUE(*accepted_p);
+ center->delete_file_event(bind_socket.fd(), EVENT_READABLE);
+ }
+
+ if (is_my_accept) {
+ r = bind_socket.accept(&srv_socket, options, &cli_addr, worker);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(srv_socket.fd() > 0);
+ }
+
+ if (worker->id == 0) {
+ C_poll cb(center);
+ center->create_file_event(cli_socket.fd(), EVENT_READABLE, &cb);
+ r = cli_socket.is_connected();
+ if (r == 0) {
+ ASSERT_EQ(true, cb.poll(500));
+ r = cli_socket.is_connected();
+ }
+ ASSERT_EQ(1, r);
+ center->delete_file_event(cli_socket.fd(), EVENT_READABLE);
+ }
+
+ const char *message = "this is a new message";
+ int len = strlen(message);
+ bufferlist bl;
+ bl.append(message, len);
+ if (worker->id == 0) {
+ r = cli_socket.send(bl, false);
+ ASSERT_EQ(len, r);
+ }
+
+ char buf[1024];
+ C_poll cb(center);
+ if (is_my_accept) {
+ center->create_file_event(srv_socket.fd(), EVENT_READABLE, &cb);
+ {
+ r = srv_socket.read(buf, sizeof(buf));
+ while (r == -EAGAIN) {
+ ASSERT_TRUE(cb.poll(500));
+ r = srv_socket.read(buf, sizeof(buf));
+ cb.reset();
+ }
+ ASSERT_EQ(len, r);
+ ASSERT_EQ(0, memcmp(buf, message, len));
+ }
+ bind_socket.abort_accept();
+ }
+ if (worker->id == 0) {
+ cli_socket.shutdown();
+ // ack delay is 200 ms
+ }
+
+ bl.clear();
+ bl.append(message, len);
+ if (worker->id == 0) {
+ r = cli_socket.send(bl, false);
+ ASSERT_EQ(-EPIPE, r);
+ }
+ if (is_my_accept) {
+ cb.reset();
+ ASSERT_TRUE(cb.poll(500));
+ r = srv_socket.read(buf, sizeof(buf));
+ if (r == -EAGAIN) {
+ cb.reset();
+ ASSERT_TRUE(cb.poll(1000*500));
+ r = srv_socket.read(buf, sizeof(buf));
+ }
+ ASSERT_EQ(0, r);
+ center->delete_file_event(srv_socket.fd(), EVENT_READABLE);
+ srv_socket.close();
+ }
+ });
+}
+
+TEST_P(NetworkWorkerTest, ConnectFailedTest) {
+ entity_addr_t bind_addr;
+ ASSERT_TRUE(bind_addr.parse(get_addr().c_str()));
+
+ exec_events([this, bind_addr](Worker *worker) mutable {
+ EventCenter *center = &worker->center;
+ entity_addr_t cli_addr;
+ SocketOptions options;
+ ServerSocket bind_socket;
+ int r = 0;
+ if (stack->support_local_listen_table() || worker->id == 0)
+ r = worker->listen(bind_addr, 0, options, &bind_socket);
+ ASSERT_EQ(0, r);
+
+ ConnectedSocket cli_socket1, cli_socket2;
+ if (worker->id == 0) {
+ ASSERT_TRUE(cli_addr.parse(get_ip_different_port().c_str()));
+ r = worker->connect(cli_addr, options, &cli_socket1);
+ ASSERT_EQ(0, r);
+ C_poll cb(center);
+ center->create_file_event(cli_socket1.fd(), EVENT_READABLE, &cb);
+ r = cli_socket1.is_connected();
+ if (r == 0) {
+ ASSERT_TRUE(cb.poll(500));
+ r = cli_socket1.is_connected();
+ }
+ ASSERT_TRUE(r == -ECONNREFUSED || r == -ECONNRESET);
+ }
+
+ if (worker->id == 1) {
+ ASSERT_TRUE(cli_addr.parse(get_different_ip().c_str()));
+ r = worker->connect(cli_addr, options, &cli_socket2);
+ ASSERT_EQ(0, r);
+ C_poll cb(center);
+ center->create_file_event(cli_socket2.fd(), EVENT_READABLE, &cb);
+ r = cli_socket2.is_connected();
+ if (r == 0) {
+ cb.poll(500);
+ r = cli_socket2.is_connected();
+ }
+ ASSERT_TRUE(r != 1);
+ center->delete_file_event(cli_socket2.fd(), EVENT_READABLE);
+ }
+ });
+}
+
+TEST_P(NetworkWorkerTest, ListenTest) {
+ Worker *worker = get_worker(0);
+ entity_addr_t bind_addr;
+ ASSERT_TRUE(bind_addr.parse(get_addr().c_str()));
+ SocketOptions options;
+ ServerSocket bind_socket1, bind_socket2;
+ int r = worker->listen(bind_addr, 0, options, &bind_socket1);
+ ASSERT_EQ(0, r);
+
+ r = worker->listen(bind_addr, 0, options, &bind_socket2);
+ ASSERT_EQ(-EADDRINUSE, r);
+}
+
+TEST_P(NetworkWorkerTest, AcceptAndCloseTest) {
+ entity_addr_t bind_addr;
+ ASSERT_TRUE(bind_addr.parse(get_addr().c_str()));
+ std::atomic_bool accepted(false);
+ std::atomic_bool *accepted_p = &accepted;
+ std::atomic_int unbind_count(stack->get_num_worker());
+ std::atomic_int *count_p = &unbind_count;
+ exec_events([this, bind_addr, accepted_p, count_p](Worker *worker) mutable {
+ SocketOptions options;
+ EventCenter *center = &worker->center;
+ entity_addr_t cli_addr;
+ int r = 0;
+ {
+ ServerSocket bind_socket;
+ if (stack->support_local_listen_table() || worker->id == 0)
+ r = worker->listen(bind_addr, 0, options, &bind_socket);
+ ASSERT_EQ(0, r);
+
+ ConnectedSocket srv_socket, cli_socket;
+ if (bind_socket) {
+ r = bind_socket.accept(&srv_socket, options, &cli_addr, worker);
+ ASSERT_EQ(-EAGAIN, r);
+ }
+
+ C_poll cb(center);
+ if (worker->id == 0) {
+ center->create_file_event(bind_socket.fd(), EVENT_READABLE, &cb);
+ r = worker->connect(bind_addr, options, &cli_socket);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(cb.poll(500));
+ }
+
+ if (bind_socket) {
+ cb.reset();
+ cb.poll(500);
+ ConnectedSocket srv_socket2;
+ do {
+ r = bind_socket.accept(&srv_socket2, options, &cli_addr, worker);
+ usleep(100);
+ } while (r == -EAGAIN && !*accepted_p);
+ if (r == 0)
+ *accepted_p = true;
+ ASSERT_TRUE(*accepted_p);
+ // srv_socket2 closed
+ center->delete_file_event(bind_socket.fd(), EVENT_READABLE);
+ }
+
+ if (worker->id == 0) {
+ char buf[100];
+ cb.reset();
+ center->create_file_event(cli_socket.fd(), EVENT_READABLE, &cb);
+ int i = 3;
+ while (!i--) {
+ ASSERT_TRUE(cb.poll(500));
+ r = cli_socket.read(buf, sizeof(buf));
+ if (r == 0)
+ break;
+ }
+ ASSERT_EQ(0, r);
+ center->delete_file_event(cli_socket.fd(), EVENT_READABLE);
+ }
+
+ if (bind_socket)
+ center->create_file_event(bind_socket.fd(), EVENT_READABLE, &cb);
+ if (worker->id == 0) {
+ *accepted_p = false;
+ r = worker->connect(bind_addr, options, &cli_socket);
+ ASSERT_EQ(0, r);
+ cb.reset();
+ ASSERT_TRUE(cb.poll(500));
+ cli_socket.close();
+ }
+
+ if (bind_socket) {
+ do {
+ r = bind_socket.accept(&srv_socket, options, &cli_addr, worker);
+ usleep(100);
+ } while (r == -EAGAIN && !*accepted_p);
+ if (r == 0)
+ *accepted_p = true;
+ ASSERT_TRUE(*accepted_p);
+ center->delete_file_event(bind_socket.fd(), EVENT_READABLE);
+ }
+ // unbind
+ }
+
+ --*count_p;
+ while (*count_p > 0)
+ usleep(100);
+
+ ConnectedSocket cli_socket;
+ r = worker->connect(bind_addr, options, &cli_socket);
+ ASSERT_EQ(0, r);
+ {
+ C_poll cb(center);
+ center->create_file_event(cli_socket.fd(), EVENT_READABLE, &cb);
+ r = cli_socket.is_connected();
+ if (r == 0) {
+ ASSERT_TRUE(cb.poll(500));
+ r = cli_socket.is_connected();
+ }
+ ASSERT_TRUE(r == -ECONNREFUSED || r == -ECONNRESET);
+ }
+ });
+}
+
+TEST_P(NetworkWorkerTest, ComplexTest) {
+ entity_addr_t bind_addr;
+ std::atomic_bool listen_done(false);
+ std::atomic_bool *listen_p = &listen_done;
+ std::atomic_bool accepted(false);
+ std::atomic_bool *accepted_p = &accepted;
+ std::atomic_bool done(false);
+ std::atomic_bool *done_p = &done;
+ ASSERT_TRUE(bind_addr.parse(get_addr().c_str()));
+ exec_events([this, bind_addr, listen_p, accepted_p, done_p](Worker *worker) mutable {
+ entity_addr_t cli_addr;
+ EventCenter *center = &worker->center;
+ SocketOptions options;
+ ServerSocket bind_socket;
+ int r = 0;
+ if (stack->support_local_listen_table() || worker->id == 0) {
+ r = worker->listen(bind_addr, 0, options, &bind_socket);
+ ASSERT_EQ(0, r);
+ *listen_p = true;
+ }
+ ConnectedSocket cli_socket, srv_socket;
+ if (worker->id == 1) {
+ while (!*listen_p || stack->support_local_listen_table()) {
+ usleep(50);
+ r = worker->connect(bind_addr, options, &cli_socket);
+ ASSERT_EQ(0, r);
+ if (stack->support_local_listen_table())
+ break;
+ }
+ }
+
+ if (bind_socket) {
+ C_poll cb(center);
+ center->create_file_event(bind_socket.fd(), EVENT_READABLE, &cb);
+ int count = 3;
+ while (count--) {
+ if (cb.poll(500)) {
+ r = bind_socket.accept(&srv_socket, options, &cli_addr, worker);
+ ASSERT_EQ(0, r);
+ *accepted_p = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(*accepted_p);
+ center->delete_file_event(bind_socket.fd(), EVENT_READABLE);
+ }
+
+ if (worker->id == 1) {
+ C_poll cb(center);
+ center->create_file_event(cli_socket.fd(), EVENT_WRITABLE, &cb);
+ r = cli_socket.is_connected();
+ if (r == 0) {
+ ASSERT_TRUE(cb.poll(500));
+ r = cli_socket.is_connected();
+ }
+ ASSERT_EQ(1, r);
+ center->delete_file_event(cli_socket.fd(), EVENT_WRITABLE);
+ }
+
+ const size_t message_size = 10240;
+ size_t count = 100;
+ string message(message_size, '!');
+ for (size_t i = 0; i < message_size; i += 100)
+ message[i] = ',';
+ size_t len = message_size * count;
+ C_poll cb(center);
+ if (worker->id == 1)
+ center->create_file_event(cli_socket.fd(), EVENT_WRITABLE, &cb);
+ if (srv_socket)
+ center->create_file_event(srv_socket.fd(), EVENT_READABLE, &cb);
+ size_t left = len;
+ len *= 2;
+ string read_string;
+ int again_count = 0;
+ int c = 2;
+ bufferlist bl;
+ for (size_t i = 0; i < count; ++i)
+ bl.push_back(bufferptr((char*)message.data(), message_size));
+ while (!*done_p) {
+ again_count = 0;
+ if (worker->id == 1) {
+ if (c > 0) {
+ ssize_t r = 0;
+ usleep(100);
+ if (left > 0) {
+ r = cli_socket.send(bl, false);
+ ASSERT_TRUE(r >= 0 || r == -EAGAIN);
+ if (r > 0)
+ left -= r;
+ if (r == -EAGAIN)
+ ++again_count;
+ }
+ if (left == 0) {
+ --c;
+ left = message_size * count;
+ ASSERT_EQ(0U, bl.length());
+ for (size_t i = 0; i < count; ++i)
+ bl.push_back(bufferptr((char*)message.data(), message_size));
+ }
+ }
+ }
+
+ if (srv_socket) {
+ char buf[1000];
+ if (len > 0) {
+ r = srv_socket.read(buf, sizeof(buf));
+ ASSERT_TRUE(r > 0 || r == -EAGAIN);
+ if (r > 0) {
+ read_string.append(buf, r);
+ len -= r;
+ } else if (r == -EAGAIN) {
+ ++again_count;
+ }
+ }
+ if (len == 0) {
+ for (size_t i = 0; i < read_string.size(); i += message_size)
+ ASSERT_EQ(0, memcmp(read_string.c_str()+i, message.c_str(), message_size));
+ *done_p = true;
+ }
+ }
+ if (again_count) {
+ cb.reset();
+ cb.poll(500);
+ }
+ }
+ if (worker->id == 1)
+ center->delete_file_event(cli_socket.fd(), EVENT_WRITABLE);
+ if (srv_socket)
+ center->delete_file_event(srv_socket.fd(), EVENT_READABLE);
+
+ if (bind_socket)
+ bind_socket.abort_accept();
+ if (srv_socket)
+ srv_socket.close();
+ if (worker->id == 1)
+ cli_socket.close();
+ });
+}
+
+class StressFactory {
+ struct Client;
+ struct Server;
+ struct ThreadData {
+ Worker *worker;
+ std::set<Client*> clients;
+ std::set<Server*> servers;
+ ~ThreadData() {
+ for (auto && i : clients)
+ delete i;
+ for (auto && i : servers)
+ delete i;
+ }
+ };
+
+ struct RandomString {
+ size_t slen;
+ vector<std::string> strs;
+ std::random_device rd;
+ std::default_random_engine rng;
+
+ explicit RandomString(size_t s): slen(s), rng(rd()) {}
+ void prepare(size_t n) {
+ static const char alphabet[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+
+ std::uniform_int_distribution<> dist(
+ 0, sizeof(alphabet) / sizeof(*alphabet) - 2);
+
+ strs.reserve(n);
+ std::generate_n(
+ std::back_inserter(strs), strs.capacity(), [&] {
+ std::string str;
+ str.reserve(slen);
+ std::generate_n(std::back_inserter(str), slen, [&]() {
+ return alphabet[dist(rng)];
+ });
+ return str;
+ }
+ );
+ }
+ std::string &get_random_string() {
+ std::uniform_int_distribution<> dist(
+ 0, strs.size() - 1);
+ return strs[dist(rng)];
+ }
+ };
+ struct Message {
+ size_t idx;
+ size_t len;
+ std::string content;
+
+ explicit Message(RandomString &rs, size_t i, size_t l): idx(i) {
+ size_t slen = rs.slen;
+ len = std::max(slen, l);
+
+ std::vector<std::string> strs;
+ strs.reserve(len / slen);
+ std::generate_n(
+ std::back_inserter(strs), strs.capacity(), [&] {
+ return rs.get_random_string();
+ }
+ );
+ len = slen * strs.size();
+ content.reserve(len);
+ for (auto &&s : strs)
+ content.append(s);
+ }
+ bool verify(const char *b, size_t len = 0) const {
+ return content.compare(0, len, b, 0, len) == 0;
+ }
+ };
+
+ template <typename T>
+ class C_delete : public EventCallback {
+ T *ctxt;
+ public:
+ explicit C_delete(T *c): ctxt(c) {}
+ void do_request(uint64_t id) override {
+ delete ctxt;
+ delete this;
+ }
+ };
+
+ class Client {
+ StressFactory *factory;
+ EventCenter *center;
+ ConnectedSocket socket;
+ std::deque<StressFactory::Message*> acking;
+ std::deque<StressFactory::Message*> writings;
+ std::string buffer;
+ size_t index = 0;
+ size_t left;
+ bool write_enabled = false;
+ size_t read_offset = 0, write_offset = 0;
+ bool first = true;
+ bool dead = false;
+ StressFactory::Message homeless_message;
+
+ class Client_read_handle : public EventCallback {
+ Client *c;
+ public:
+ explicit Client_read_handle(Client *_c): c(_c) {}
+ void do_request(uint64_t id) override {
+ c->do_read_request();
+ }
+ } read_ctxt;
+
+ class Client_write_handle : public EventCallback {
+ Client *c;
+ public:
+ explicit Client_write_handle(Client *_c): c(_c) {}
+ void do_request(uint64_t id) override {
+ c->do_write_request();
+ }
+ } write_ctxt;
+
+ public:
+ Client(StressFactory *f, EventCenter *cen, ConnectedSocket s, size_t c)
+ : factory(f), center(cen), socket(std::move(s)), left(c), homeless_message(factory->rs, -1, 1024),
+ read_ctxt(this), write_ctxt(this) {
+ center->create_file_event(
+ socket.fd(), EVENT_READABLE, &read_ctxt);
+ center->dispatch_event_external(&read_ctxt);
+ }
+ void close() {
+ ASSERT_FALSE(write_enabled);
+ dead = true;
+ socket.shutdown();
+ center->delete_file_event(socket.fd(), EVENT_READABLE);
+ center->dispatch_event_external(new C_delete<Client>(this));
+ }
+
+ void do_read_request() {
+ if (dead)
+ return ;
+ ASSERT_TRUE(socket.is_connected() >= 0);
+ if (!socket.is_connected())
+ return ;
+ ASSERT_TRUE(!acking.empty() || first);
+ if (first) {
+ first = false;
+ center->dispatch_event_external(&write_ctxt);
+ if (acking.empty())
+ return ;
+ }
+ StressFactory::Message *m = acking.front();
+ int r = 0;
+ if (buffer.empty())
+ buffer.resize(m->len);
+ bool must_no = false;
+ while (true) {
+ r = socket.read((char*)buffer.data() + read_offset,
+ m->len - read_offset);
+ ASSERT_TRUE(r == -EAGAIN || r > 0);
+ if (r == -EAGAIN)
+ break;
+ read_offset += r;
+
+ std::cerr << " client " << this << " receive " << m->idx << " len " << r << " content: " << std::endl;
+ ASSERT_FALSE(must_no);
+ if ((m->len - read_offset) == 0) {
+ ASSERT_TRUE(m->verify(buffer.data(), 0));
+ delete m;
+ acking.pop_front();
+ read_offset = 0;
+ buffer.clear();
+ if (acking.empty()) {
+ m = &homeless_message;
+ must_no = true;
+ } else {
+ m = acking.front();
+ buffer.resize(m->len);
+ }
+ }
+ }
+ if (acking.empty()) {
+ center->dispatch_event_external(&write_ctxt);
+ return ;
+ }
+ }
+
+ void do_write_request() {
+ if (dead)
+ return ;
+ ASSERT_TRUE(socket.is_connected() > 0);
+
+ while (left > 0 && factory->queue_depth > writings.size() + acking.size()) {
+ StressFactory::Message *m = new StressFactory::Message(
+ factory->rs, ++index,
+ factory->rd() % factory->max_message_length);
+ std::cerr << " client " << this << " generate message " << m->idx << " length " << m->len << std::endl;
+ ASSERT_EQ(m->len, m->content.size());
+ writings.push_back(m);
+ --left;
+ --factory->message_left;
+ }
+
+ while (!writings.empty()) {
+ StressFactory::Message *m = writings.front();
+ bufferlist bl;
+ bl.append(m->content.data() + write_offset, m->content.size() - write_offset);
+ ssize_t r = socket.send(bl, false);
+ if (r == 0)
+ break;
+ std::cerr << " client " << this << " send " << m->idx << " len " << r << " content: " << std::endl;
+ ASSERT_TRUE(r >= 0);
+ write_offset += r;
+ if (write_offset == m->content.size()) {
+ write_offset = 0;
+ writings.pop_front();
+ acking.push_back(m);
+ }
+ }
+ if (writings.empty() && write_enabled) {
+ center->delete_file_event(socket.fd(), EVENT_WRITABLE);
+ write_enabled = false;
+ } else if (!writings.empty() && !write_enabled) {
+ ASSERT_EQ(0, center->create_file_event(
+ socket.fd(), EVENT_WRITABLE, &write_ctxt));
+ write_enabled = true;
+ }
+ }
+
+ bool finish() const {
+ return left == 0 && acking.empty() && writings.empty();
+ }
+ };
+ friend class Client;
+
+ class Server {
+ StressFactory *factory;
+ EventCenter *center;
+ ConnectedSocket socket;
+ std::deque<std::string> buffers;
+ bool write_enabled = false;
+ bool dead = false;
+
+ class Server_read_handle : public EventCallback {
+ Server *s;
+ public:
+ explicit Server_read_handle(Server *_s): s(_s) {}
+ void do_request(uint64_t id) override {
+ s->do_read_request();
+ }
+ } read_ctxt;
+
+ class Server_write_handle : public EventCallback {
+ Server *s;
+ public:
+ explicit Server_write_handle(Server *_s): s(_s) {}
+ void do_request(uint64_t id) override {
+ s->do_write_request();
+ }
+ } write_ctxt;
+
+ public:
+ Server(StressFactory *f, EventCenter *c, ConnectedSocket s):
+ factory(f), center(c), socket(std::move(s)), read_ctxt(this), write_ctxt(this) {
+ center->create_file_event(socket.fd(), EVENT_READABLE, &read_ctxt);
+ center->dispatch_event_external(&read_ctxt);
+ }
+ void close() {
+ ASSERT_FALSE(write_enabled);
+ socket.shutdown();
+ center->delete_file_event(socket.fd(), EVENT_READABLE);
+ center->dispatch_event_external(new C_delete<Server>(this));
+ }
+ void do_read_request() {
+ if (dead)
+ return ;
+ int r = 0;
+ while (true) {
+ char buf[4096];
+ bufferptr data;
+ r = socket.read(buf, sizeof(buf));
+ ASSERT_TRUE(r == -EAGAIN || (r >= 0 && (size_t)r <= sizeof(buf)));
+ if (r == 0) {
+ ASSERT_TRUE(buffers.empty());
+ dead = true;
+ return ;
+ } else if (r == -EAGAIN)
+ break;
+ buffers.emplace_back(buf, 0, r);
+ std::cerr << " server " << this << " receive " << r << " content: " << std::endl;
+ }
+ if (!buffers.empty() && !write_enabled)
+ center->dispatch_event_external(&write_ctxt);
+ }
+
+ void do_write_request() {
+ if (dead)
+ return ;
+
+ while (!buffers.empty()) {
+ bufferlist bl;
+ auto it = buffers.begin();
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ bl.push_back(bufferptr((char*)it->data(), it->size()));
+ ++it;
+ }
+
+ ssize_t r = socket.send(bl, false);
+ std::cerr << " server " << this << " send " << r << std::endl;
+ if (r == 0)
+ break;
+ ASSERT_TRUE(r >= 0);
+ while (r > 0) {
+ ASSERT_TRUE(!buffers.empty());
+ string &buffer = buffers.front();
+ if (r >= (int)buffer.size()) {
+ r -= (int)buffer.size();
+ buffers.pop_front();
+ } else {
+ std::cerr << " server " << this << " sent " << r << std::endl;
+ buffer = buffer.substr(r, buffer.size());
+ break;
+ }
+ }
+ }
+ if (buffers.empty()) {
+ if (write_enabled) {
+ center->delete_file_event(socket.fd(), EVENT_WRITABLE);
+ write_enabled = false;
+ }
+ } else if (!write_enabled) {
+ ASSERT_EQ(0, center->create_file_event(
+ socket.fd(), EVENT_WRITABLE, &write_ctxt));
+ write_enabled = true;
+ }
+ }
+
+ bool finish() {
+ return dead;
+ }
+ };
+ friend class Server;
+
+ class C_accept : public EventCallback {
+ StressFactory *factory;
+ ServerSocket bind_socket;
+ ThreadData *t_data;
+ Worker *worker;
+
+ public:
+ C_accept(StressFactory *f, ServerSocket s, ThreadData *data, Worker *w)
+ : factory(f), bind_socket(std::move(s)), t_data(data), worker(w) {}
+ void do_request(uint64_t id) override {
+ while (true) {
+ entity_addr_t cli_addr;
+ ConnectedSocket srv_socket;
+ SocketOptions options;
+ int r = bind_socket.accept(&srv_socket, options, &cli_addr, worker);
+ if (r == -EAGAIN) {
+ break;
+ }
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(srv_socket.fd() > 0);
+ Server *cb = new Server(factory, &t_data->worker->center, std::move(srv_socket));
+ t_data->servers.insert(cb);
+ }
+ }
+ };
+ friend class C_accept;
+
+ public:
+ static const size_t min_client_send_messages = 100;
+ static const size_t max_client_send_messages = 1000;
+ std::shared_ptr<NetworkStack> stack;
+ RandomString rs;
+ std::random_device rd;
+ const size_t client_num, queue_depth, max_message_length;
+ atomic_int message_count, message_left;
+ entity_addr_t bind_addr;
+ std::atomic_bool already_bind = {false};
+ SocketOptions options;
+
+ explicit StressFactory(const std::shared_ptr<NetworkStack> &s, const string &addr,
+ size_t cli, size_t qd, size_t mc, size_t l)
+ : stack(s), rs(128), client_num(cli), queue_depth(qd),
+ max_message_length(l), message_count(mc), message_left(mc) {
+ bind_addr.parse(addr.c_str());
+ rs.prepare(100);
+ }
+ ~StressFactory() {
+ }
+
+ void add_client(ThreadData *t_data) {
+ static ceph::mutex lock = ceph::make_mutex("add_client_lock");
+ std::lock_guard l{lock};
+ ConnectedSocket sock;
+ int r = t_data->worker->connect(bind_addr, options, &sock);
+ std::default_random_engine rng(rd());
+ std::uniform_int_distribution<> dist(
+ min_client_send_messages, max_client_send_messages);
+ ASSERT_EQ(0, r);
+ int c = dist(rng);
+ if (c > message_count.load())
+ c = message_count.load();
+ Client *cb = new Client(this, &t_data->worker->center, std::move(sock), c);
+ t_data->clients.insert(cb);
+ message_count -= c;
+ }
+
+ void drop_client(ThreadData *t_data, Client *c) {
+ c->close();
+ ASSERT_EQ(1U, t_data->clients.erase(c));
+ }
+
+ void drop_server(ThreadData *t_data, Server *s) {
+ s->close();
+ ASSERT_EQ(1U, t_data->servers.erase(s));
+ }
+
+ void start(Worker *worker) {
+ int r = 0;
+ ThreadData t_data;
+ t_data.worker = worker;
+ ServerSocket bind_socket;
+ if (stack->support_local_listen_table() || worker->id == 0) {
+ r = worker->listen(bind_addr, 0, options, &bind_socket);
+ ASSERT_EQ(0, r);
+ already_bind = true;
+ }
+ while (!already_bind)
+ usleep(50);
+ C_accept *accept_handler = nullptr;
+ int bind_fd = 0;
+ if (bind_socket) {
+ bind_fd = bind_socket.fd();
+ accept_handler = new C_accept(this, std::move(bind_socket), &t_data, worker);
+ ASSERT_EQ(0, worker->center.create_file_event(
+ bind_fd, EVENT_READABLE, accept_handler));
+ }
+
+ int echo_throttle = message_count;
+ while (message_count > 0 || !t_data.clients.empty() || !t_data.servers.empty()) {
+ if (message_count > 0 && t_data.clients.size() < client_num && t_data.servers.size() < client_num)
+ add_client(&t_data);
+ for (auto &&c : t_data.clients) {
+ if (c->finish()) {
+ drop_client(&t_data, c);
+ break;
+ }
+ }
+ for (auto &&s : t_data.servers) {
+ if (s->finish()) {
+ drop_server(&t_data, s);
+ break;
+ }
+ }
+
+ worker->center.process_events(1);
+ if (echo_throttle > message_left) {
+ std::cerr << " clients " << t_data.clients.size() << " servers " << t_data.servers.size()
+ << " message count " << message_left << std::endl;
+ echo_throttle -= 100;
+ }
+ }
+ if (bind_fd)
+ worker->center.delete_file_event(bind_fd, EVENT_READABLE);
+ delete accept_handler;
+ }
+};
+
+TEST_P(NetworkWorkerTest, StressTest) {
+ StressFactory factory(stack, get_addr(), 16, 16, 10000, 1024);
+ StressFactory *f = &factory;
+ exec_events([f](Worker *worker) mutable {
+ f->start(worker);
+ });
+ ASSERT_EQ(0, factory.message_left);
+}
+
+
+INSTANTIATE_TEST_SUITE_P(
+ NetworkStack,
+ NetworkWorkerTest,
+ ::testing::Values(
+#ifdef HAVE_DPDK
+ "dpdk",
+#endif
+ "posix"
+ )
+);
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make ceph_test_async_networkstack &&
+ * ./ceph_test_async_networkstack
+ *
+ * End:
+ */
diff --git a/src/test/msgr/test_comp_registry.cc b/src/test/msgr/test_comp_registry.cc
new file mode 100644
index 000000000..d5513e2e4
--- /dev/null
+++ b/src/test/msgr/test_comp_registry.cc
@@ -0,0 +1,98 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "include/stringify.h"
+#include "compressor/Compressor.h"
+#include "msg/compressor_registry.h"
+#include "gtest/gtest.h"
+#include "common/ceph_context.h"
+#include "global/global_context.h"
+
+#include <sstream>
+
+TEST(CompressorRegistry, con_modes)
+{
+ auto cct = g_ceph_context;
+ CompressorRegistry reg(cct);
+ std::vector<uint32_t> methods;
+ uint32_t method;
+ uint32_t mode;
+
+ const std::vector<uint32_t> snappy_method = { Compressor::COMP_ALG_SNAPPY };
+ const std::vector<uint32_t> zlib_method = { Compressor::COMP_ALG_ZLIB };
+ const std::vector<uint32_t> both_methods = { Compressor::COMP_ALG_ZLIB, Compressor::COMP_ALG_SNAPPY};
+ const std::vector<uint32_t> no_method = { Compressor::COMP_ALG_NONE };
+
+ cct->_conf.set_val(
+ "enable_experimental_unrecoverable_data_corrupting_features", "*");
+
+ // baseline: compression for communication with osd is enabled
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ cct->_conf.set_val("ms_osd_compress_mode", "force");
+ cct->_conf.set_val("ms_osd_compression_algorithm", "snappy");
+ cct->_conf.set_val("ms_compress_secure", "false");
+ cct->_conf.apply_changes(NULL);
+
+ ASSERT_EQ(reg.get_is_compress_secure(), false);
+
+ methods = reg.get_methods(CEPH_ENTITY_TYPE_MON);
+ ASSERT_EQ(methods.size(), 0);
+ method = reg.pick_method(CEPH_ENTITY_TYPE_MON, both_methods);
+ ASSERT_EQ(method, Compressor::COMP_ALG_NONE);
+ mode = reg.get_mode(CEPH_ENTITY_TYPE_MON, false);
+ ASSERT_EQ(mode, Compressor::COMP_NONE);
+
+ methods = reg.get_methods(CEPH_ENTITY_TYPE_OSD);
+ ASSERT_EQ(methods, snappy_method);
+ const std::vector<uint32_t> rev_both_methods (both_methods.rbegin(), both_methods.rend());
+ method = reg.pick_method(CEPH_ENTITY_TYPE_OSD, rev_both_methods);
+ ASSERT_EQ(method, Compressor::COMP_ALG_SNAPPY);
+ mode = reg.get_mode(CEPH_ENTITY_TYPE_OSD, false);
+ ASSERT_EQ(mode, Compressor::COMP_FORCE);
+ mode = reg.get_mode(CEPH_ENTITY_TYPE_OSD, true);
+ ASSERT_EQ(mode, Compressor::COMP_NONE);
+
+ method = reg.pick_method(CEPH_ENTITY_TYPE_OSD, zlib_method);
+ ASSERT_EQ(method, Compressor::COMP_ALG_NONE);
+
+ // disable compression mode
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ cct->_conf.set_val("ms_osd_compress_mode", "none");
+ cct->_conf.apply_changes(NULL);
+
+ mode = reg.get_mode(CEPH_ENTITY_TYPE_OSD, false);
+ ASSERT_EQ(mode, Compressor::COMP_NONE);
+
+ // no compression methods
+ cct->_conf.set_val("ms_osd_compress_mode", "force");
+ cct->_conf.set_val("ms_osd_compression_algorithm", "none");
+ cct->_conf.apply_changes(NULL);
+
+ method = reg.pick_method(CEPH_ENTITY_TYPE_OSD, both_methods);
+ ASSERT_EQ(method, Compressor::COMP_ALG_NONE);
+
+ // min compression size
+ cct->_conf.set_val("ms_osd_compress_min_size", "1024");
+ cct->_conf.apply_changes(NULL);
+
+ uint32_t s = reg.get_min_compression_size(CEPH_ENTITY_TYPE_OSD);
+ ASSERT_EQ(s, 1024);
+
+ // allow secure with compression
+ cct->_conf.set_val("ms_osd_compress_mode", "force");
+ cct->_conf.set_val("ms_osd_compression_algorithm", "snappy");
+ cct->_conf.set_val("ms_compress_secure", "true");
+ cct->_conf.apply_changes(NULL);
+
+ ASSERT_EQ(reg.get_is_compress_secure(), true);
+
+ mode = reg.get_mode(CEPH_ENTITY_TYPE_OSD, true);
+ ASSERT_EQ(mode, Compressor::COMP_FORCE);
+
+ mode = reg.get_mode(CEPH_ENTITY_TYPE_OSD, false);
+ ASSERT_EQ(mode, Compressor::COMP_FORCE);
+
+ // back to normalish, for the benefit of the next test(s)
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+}
diff --git a/src/test/msgr/test_frames_v2.cc b/src/test/msgr/test_frames_v2.cc
new file mode 100644
index 000000000..c5be32a9c
--- /dev/null
+++ b/src/test/msgr/test_frames_v2.cc
@@ -0,0 +1,483 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "msg/async/frames_v2.h"
+
+#include <numeric>
+#include <ostream>
+#include <string>
+#include <tuple>
+
+#include "msg/async/compression_meta.h"
+#include "auth/Auth.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+#include "include/Context.h"
+
+#include <gtest/gtest.h>
+
+#define COMP_THRESHOLD 1 << 10
+#define EXPECT_COMPRESSED(is_compressed, val1, val2) \
+ if (is_compressed && val1 > COMP_THRESHOLD) { \
+ EXPECT_GE(val1, val2); \
+ } else { \
+ EXPECT_EQ(val1, val2); \
+ }
+
+using namespace std;
+
+namespace ceph::msgr::v2 {
+
+// MessageFrame with the first segment not fixed to ceph_msg_header2
+struct TestFrame : Frame<TestFrame,
+ /* four segments */
+ segment_t::DEFAULT_ALIGNMENT,
+ segment_t::DEFAULT_ALIGNMENT,
+ segment_t::DEFAULT_ALIGNMENT,
+ segment_t::PAGE_SIZE_ALIGNMENT> {
+ static constexpr Tag tag = static_cast<Tag>(123);
+
+ static TestFrame Encode(const bufferlist& header,
+ const bufferlist& front,
+ const bufferlist& middle,
+ const bufferlist& data) {
+ TestFrame f;
+ f.segments[SegmentIndex::Msg::HEADER] = header;
+ f.segments[SegmentIndex::Msg::FRONT] = front;
+ f.segments[SegmentIndex::Msg::MIDDLE] = middle;
+ f.segments[SegmentIndex::Msg::DATA] = data;
+
+ // discard cached crcs for perf tests
+ f.segments[SegmentIndex::Msg::HEADER].invalidate_crc();
+ f.segments[SegmentIndex::Msg::FRONT].invalidate_crc();
+ f.segments[SegmentIndex::Msg::MIDDLE].invalidate_crc();
+ f.segments[SegmentIndex::Msg::DATA].invalidate_crc();
+ return f;
+ }
+
+ static TestFrame Decode(segment_bls_t& segment_bls) {
+ TestFrame f;
+ // Transfer segments' bufferlists. If segment_bls contains
+ // less than SegmentsNumV segments, the missing ones will be
+ // seen as empty.
+ for (size_t i = 0; i < segment_bls.size(); i++) {
+ f.segments[i] = std::move(segment_bls[i]);
+ }
+ return f;
+ }
+
+ bufferlist& header() {
+ return segments[SegmentIndex::Msg::HEADER];
+ }
+ bufferlist& front() {
+ return segments[SegmentIndex::Msg::FRONT];
+ }
+ bufferlist& middle() {
+ return segments[SegmentIndex::Msg::MIDDLE];
+ }
+ bufferlist& data() {
+ return segments[SegmentIndex::Msg::DATA];
+ }
+
+protected:
+ using Frame::Frame;
+};
+
+struct mode_t {
+ bool is_rev1;
+ bool is_secure;
+ bool is_compress;
+};
+
+static std::ostream& operator<<(std::ostream& os, const mode_t& m) {
+ os << "msgr2." << (m.is_rev1 ? "1" : "0")
+ << (m.is_secure ? "-secure" : "-crc")
+ << (m.is_compress ? "-compress": "-nocompress");
+ return os;
+}
+
+static const mode_t modes[] = {
+ {false, false, false},
+ {false, true, false},
+ {true, false, false},
+ {true, true, false},
+ {false, false, true},
+ {false, true, true},
+ {true, false, true},
+ {true, true, true}
+};
+
+struct round_trip_instance_t {
+ uint32_t header_len;
+ uint32_t front_len;
+ uint32_t middle_len;
+ uint32_t data_len;
+
+ // expected number of segments (same for each mode)
+ size_t num_segments;
+ // expected layout (different for each mode)
+ uint32_t onwire_lens[4][MAX_NUM_SEGMENTS + 2];
+};
+
+static std::ostream& operator<<(std::ostream& os,
+ const round_trip_instance_t& rti) {
+ os << rti.header_len << "+" << rti.front_len << "+"
+ << rti.middle_len << "+" << rti.data_len;
+ return os;
+}
+
+static bufferlist make_bufferlist(size_t len, char c) {
+ bufferlist bl;
+ if (len > 0) {
+ bl.reserve(len);
+ bl.append(std::string(len, c));
+ }
+ return bl;
+}
+
+bool disassemble_frame(FrameAssembler& frame_asm, bufferlist& frame_bl,
+ Tag& tag, segment_bls_t& segment_bls) {
+ bufferlist preamble_bl;
+ frame_bl.splice(0, frame_asm.get_preamble_onwire_len(), &preamble_bl);
+ tag = frame_asm.disassemble_preamble(preamble_bl);
+
+ do {
+ size_t seg_idx = segment_bls.size();
+ segment_bls.emplace_back();
+
+ uint32_t onwire_len = frame_asm.get_segment_onwire_len(seg_idx);
+ if (onwire_len > 0) {
+ frame_bl.splice(0, onwire_len, &segment_bls.back());
+ }
+ } while (segment_bls.size() < frame_asm.get_num_segments());
+
+ bufferlist epilogue_bl;
+ uint32_t epilogue_onwire_len = frame_asm.get_epilogue_onwire_len();
+ if (epilogue_onwire_len > 0) {
+ frame_bl.splice(0, epilogue_onwire_len, &epilogue_bl);
+ }
+
+ return frame_asm.disassemble_segments(preamble_bl, segment_bls.data(), epilogue_bl);
+}
+
+class RoundTripTestBase : public ::testing::TestWithParam<
+ std::tuple<round_trip_instance_t, mode_t>> {
+protected:
+ RoundTripTestBase()
+ : m_tx_frame_asm(&m_tx_crypto, std::get<1>(GetParam()).is_rev1, true,
+ &m_tx_comp),
+ m_rx_frame_asm(&m_rx_crypto, std::get<1>(GetParam()).is_rev1, true,
+ &m_rx_comp),
+ m_header(make_bufferlist(std::get<0>(GetParam()).header_len, 'H')),
+ m_front(make_bufferlist(std::get<0>(GetParam()).front_len, 'F')),
+ m_middle(make_bufferlist(std::get<0>(GetParam()).middle_len, 'M')),
+ m_data(make_bufferlist(std::get<0>(GetParam()).data_len, 'D')) {
+ const auto& m = std::get<1>(GetParam());
+ if (m.is_secure) {
+ AuthConnectionMeta auth_meta;
+ auth_meta.con_mode = CEPH_CON_MODE_SECURE;
+ // see AuthConnectionMeta::get_connection_secret_length()
+ auth_meta.connection_secret.resize(64);
+ g_ceph_context->random()->get_bytes(auth_meta.connection_secret.data(),
+ auth_meta.connection_secret.size());
+ m_tx_crypto = ceph::crypto::onwire::rxtx_t::create_handler_pair(
+ g_ceph_context, auth_meta, /*new_nonce_format=*/m.is_rev1,
+ /*crossed=*/false);
+ m_rx_crypto = ceph::crypto::onwire::rxtx_t::create_handler_pair(
+ g_ceph_context, auth_meta, /*new_nonce_format=*/m.is_rev1,
+ /*crossed=*/true);
+ }
+
+ if (m.is_compress) {
+ CompConnectionMeta comp_meta;
+ comp_meta.con_mode = Compressor::COMP_FORCE;
+ comp_meta.con_method = Compressor::COMP_ALG_SNAPPY;
+ m_tx_comp = ceph::compression::onwire::rxtx_t::create_handler_pair(
+ g_ceph_context, comp_meta, /*min_compress_size=*/COMP_THRESHOLD
+ );
+ m_rx_comp = ceph::compression::onwire::rxtx_t::create_handler_pair(
+ g_ceph_context, comp_meta, /*min_compress_size=*/COMP_THRESHOLD
+ );
+ }
+ }
+
+ void check_frame_assembler(const FrameAssembler& frame_asm) {
+ const auto& [rti, m] = GetParam();
+ const auto& onwire_lens = rti.onwire_lens[m.is_rev1 << 1 | m.is_secure];
+
+ EXPECT_COMPRESSED(m.is_compress, rti.header_len + rti.front_len + rti.middle_len + rti.data_len,
+ frame_asm.get_frame_logical_len());
+ ASSERT_EQ(rti.num_segments, frame_asm.get_num_segments());
+ EXPECT_COMPRESSED(m.is_compress, onwire_lens[0], frame_asm.get_preamble_onwire_len());
+ for (size_t i = 0; i < rti.num_segments; i++) {
+ EXPECT_COMPRESSED(m.is_compress, onwire_lens[i + 1], frame_asm.get_segment_onwire_len(i));
+ }
+ EXPECT_COMPRESSED(m.is_compress, onwire_lens[rti.num_segments + 1],
+ frame_asm.get_epilogue_onwire_len());
+ EXPECT_COMPRESSED(m.is_compress,
+ std::accumulate(std::begin(onwire_lens), std::end(onwire_lens),
+ uint64_t(0)),
+ frame_asm.get_frame_onwire_len());
+ }
+
+ void test_round_trip() {
+ auto tx_frame = TestFrame::Encode(m_header, m_front, m_middle, m_data);
+ auto onwire_bl = tx_frame.get_buffer(m_tx_frame_asm);
+ check_frame_assembler(m_tx_frame_asm);
+ EXPECT_EQ(m_tx_frame_asm.get_frame_onwire_len(), onwire_bl.length());
+
+ Tag rx_tag;
+ segment_bls_t rx_segment_bls;
+ EXPECT_TRUE(disassemble_frame(m_rx_frame_asm, onwire_bl, rx_tag,
+ rx_segment_bls));
+ check_frame_assembler(m_rx_frame_asm);
+ EXPECT_EQ(0, onwire_bl.length());
+ EXPECT_EQ(TestFrame::tag, rx_tag);
+ EXPECT_EQ(m_rx_frame_asm.get_num_segments(), rx_segment_bls.size());
+
+ auto rx_frame = TestFrame::Decode(rx_segment_bls);
+ EXPECT_TRUE(m_header.contents_equal(rx_frame.header()));
+ EXPECT_TRUE(m_front.contents_equal(rx_frame.front()));
+ EXPECT_TRUE(m_middle.contents_equal(rx_frame.middle()));
+ EXPECT_TRUE(m_data.contents_equal(rx_frame.data()));
+ }
+
+ ceph::crypto::onwire::rxtx_t m_tx_crypto;
+ ceph::crypto::onwire::rxtx_t m_rx_crypto;
+ ceph::compression::onwire::rxtx_t m_tx_comp;
+ ceph::compression::onwire::rxtx_t m_rx_comp;
+ FrameAssembler m_tx_frame_asm;
+ FrameAssembler m_rx_frame_asm;
+
+ const bufferlist m_header;
+ const bufferlist m_front;
+ const bufferlist m_middle;
+ const bufferlist m_data;
+};
+
+class RoundTripTest : public RoundTripTestBase {};
+
+TEST_P(RoundTripTest, Basic) {
+ test_round_trip();
+}
+
+TEST_P(RoundTripTest, Reuse) {
+ for (int i = 0; i < 3; i++) {
+ test_round_trip();
+ }
+}
+
+static const round_trip_instance_t round_trip_instances[] = {
+ // first segment is empty
+ { 0, 0, 0, 0, 1, {{32, 0, 17, 0, 0, 0},
+ {32, 0, 32, 0, 0, 0},
+ {32, 0, 0, 0, 0, 0},
+ {96, 0, 0, 0, 0, 0}}},
+ { 0, 0, 0, 303, 4, {{32, 0, 0, 0, 303, 17},
+ {32, 0, 0, 0, 304, 32},
+ {32, 0, 0, 0, 303, 13},
+ {96, 0, 0, 0, 304, 32}}},
+ { 0, 0, 202, 0, 3, {{32, 0, 0, 202, 17, 0},
+ {32, 0, 0, 208, 32, 0},
+ {32, 0, 0, 202, 13, 0},
+ {96, 0, 0, 208, 32, 0}}},
+ { 0, 0, 202, 303, 4, {{32, 0, 0, 202, 303, 17},
+ {32, 0, 0, 208, 304, 32},
+ {32, 0, 0, 202, 303, 13},
+ {96, 0, 0, 208, 304, 32}}},
+ { 0, 101, 0, 0, 2, {{32, 0, 101, 17, 0, 0},
+ {32, 0, 112, 32, 0, 0},
+ {32, 0, 101, 13, 0, 0},
+ {96, 0, 112, 32, 0, 0}}},
+ { 0, 101, 0, 303, 4, {{32, 0, 101, 0, 303, 17},
+ {32, 0, 112, 0, 304, 32},
+ {32, 0, 101, 0, 303, 13},
+ {96, 0, 112, 0, 304, 32}}},
+ { 0, 101, 202, 0, 3, {{32, 0, 101, 202, 17, 0},
+ {32, 0, 112, 208, 32, 0},
+ {32, 0, 101, 202, 13, 0},
+ {96, 0, 112, 208, 32, 0}}},
+ { 0, 101, 202, 303, 4, {{32, 0, 101, 202, 303, 17},
+ {32, 0, 112, 208, 304, 32},
+ {32, 0, 101, 202, 303, 13},
+ {96, 0, 112, 208, 304, 32}}},
+
+ // first segment is fully inlined, inline buffer is not full
+ { 1, 0, 0, 0, 1, {{32, 1, 17, 0, 0, 0},
+ {32, 16, 32, 0, 0, 0},
+ {32, 5, 0, 0, 0, 0},
+ {96, 0, 0, 0, 0, 0}}},
+ { 1, 0, 0, 303, 4, {{32, 1, 0, 0, 303, 17},
+ {32, 16, 0, 0, 304, 32},
+ {32, 5, 0, 0, 303, 13},
+ {96, 0, 0, 0, 304, 32}}},
+ { 1, 0, 202, 0, 3, {{32, 1, 0, 202, 17, 0},
+ {32, 16, 0, 208, 32, 0},
+ {32, 5, 0, 202, 13, 0},
+ {96, 0, 0, 208, 32, 0}}},
+ { 1, 0, 202, 303, 4, {{32, 1, 0, 202, 303, 17},
+ {32, 16, 0, 208, 304, 32},
+ {32, 5, 0, 202, 303, 13},
+ {96, 0, 0, 208, 304, 32}}},
+ { 1, 101, 0, 0, 2, {{32, 1, 101, 17, 0, 0},
+ {32, 16, 112, 32, 0, 0},
+ {32, 5, 101, 13, 0, 0},
+ {96, 0, 112, 32, 0, 0}}},
+ { 1, 101, 0, 303, 4, {{32, 1, 101, 0, 303, 17},
+ {32, 16, 112, 0, 304, 32},
+ {32, 5, 101, 0, 303, 13},
+ {96, 0, 112, 0, 304, 32}}},
+ { 1, 101, 202, 0, 3, {{32, 1, 101, 202, 17, 0},
+ {32, 16, 112, 208, 32, 0},
+ {32, 5, 101, 202, 13, 0},
+ {96, 0, 112, 208, 32, 0}}},
+ { 1, 101, 202, 303, 4, {{32, 1, 101, 202, 303, 17},
+ {32, 16, 112, 208, 304, 32},
+ {32, 5, 101, 202, 303, 13},
+ {96, 0, 112, 208, 304, 32}}},
+
+ // first segment is fully inlined, inline buffer is full
+ {48, 0, 0, 0, 1, {{32, 48, 17, 0, 0, 0},
+ {32, 48, 32, 0, 0, 0},
+ {32, 52, 0, 0, 0, 0},
+ {96, 0, 0, 0, 0, 0}}},
+ {48, 0, 0, 303, 4, {{32, 48, 0, 0, 303, 17},
+ {32, 48, 0, 0, 304, 32},
+ {32, 52, 0, 0, 303, 13},
+ {96, 0, 0, 0, 304, 32}}},
+ {48, 0, 202, 0, 3, {{32, 48, 0, 202, 17, 0},
+ {32, 48, 0, 208, 32, 0},
+ {32, 52, 0, 202, 13, 0},
+ {96, 0, 0, 208, 32, 0}}},
+ {48, 0, 202, 303, 4, {{32, 48, 0, 202, 303, 17},
+ {32, 48, 0, 208, 304, 32},
+ {32, 52, 0, 202, 303, 13},
+ {96, 0, 0, 208, 304, 32}}},
+ {48, 101, 0, 0, 2, {{32, 48, 101, 17, 0, 0},
+ {32, 48, 112, 32, 0, 0},
+ {32, 52, 101, 13, 0, 0},
+ {96, 0, 112, 32, 0, 0}}},
+ {48, 101, 0, 303, 4, {{32, 48, 101, 0, 303, 17},
+ {32, 48, 112, 0, 304, 32},
+ {32, 52, 101, 0, 303, 13},
+ {96, 0, 112, 0, 304, 32}}},
+ {48, 101, 202, 0, 3, {{32, 48, 101, 202, 17, 0},
+ {32, 48, 112, 208, 32, 0},
+ {32, 52, 101, 202, 13, 0},
+ {96, 0, 112, 208, 32, 0}}},
+ {48, 101, 202, 303, 4, {{32, 48, 101, 202, 303, 17},
+ {32, 48, 112, 208, 304, 32},
+ {32, 52, 101, 202, 303, 13},
+ {96, 0, 112, 208, 304, 32}}},
+
+ // first segment is partially inlined
+ {49, 0, 0, 0, 1, {{32, 49, 17, 0, 0, 0},
+ {32, 64, 32, 0, 0, 0},
+ {32, 53, 0, 0, 0, 0},
+ {96, 32, 0, 0, 0, 0}}},
+ {49, 0, 0, 303, 4, {{32, 49, 0, 0, 303, 17},
+ {32, 64, 0, 0, 304, 32},
+ {32, 53, 0, 0, 303, 13},
+ {96, 32, 0, 0, 304, 32}}},
+ {49, 0, 202, 0, 3, {{32, 49, 0, 202, 17, 0},
+ {32, 64, 0, 208, 32, 0},
+ {32, 53, 0, 202, 13, 0},
+ {96, 32, 0, 208, 32, 0}}},
+ {49, 0, 202, 303, 4, {{32, 49, 0, 202, 303, 17},
+ {32, 64, 0, 208, 304, 32},
+ {32, 53, 0, 202, 303, 13},
+ {96, 32, 0, 208, 304, 32}}},
+ {49, 101, 0, 0, 2, {{32, 49, 101, 17, 0, 0},
+ {32, 64, 112, 32, 0, 0},
+ {32, 53, 101, 13, 0, 0},
+ {96, 32, 112, 32, 0, 0}}},
+ {49, 101, 0, 303, 4, {{32, 49, 101, 0, 303, 17},
+ {32, 64, 112, 0, 304, 32},
+ {32, 53, 101, 0, 303, 13},
+ {96, 32, 112, 0, 304, 32}}},
+ {49, 101, 202, 0, 3, {{32, 49, 101, 202, 17, 0},
+ {32, 64, 112, 208, 32, 0},
+ {32, 53, 101, 202, 13, 0},
+ {96, 32, 112, 208, 32, 0}}},
+ {49, 101, 202, 303, 4, {{32, 49, 101, 202, 303, 17},
+ {32, 64, 112, 208, 304, 32},
+ {32, 53, 101, 202, 303, 13},
+ {96, 32, 112, 208, 304, 32}}},
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ RoundTripTests, RoundTripTest, ::testing::Combine(
+ ::testing::ValuesIn(round_trip_instances),
+ ::testing::ValuesIn(modes)));
+
+class RoundTripPerfTest : public RoundTripTestBase {};
+
+TEST_P(RoundTripPerfTest, DISABLED_Basic) {
+ for (int i = 0; i < 100000; i++) {
+ auto tx_frame = TestFrame::Encode(m_header, m_front, m_middle, m_data);
+ auto onwire_bl = tx_frame.get_buffer(m_tx_frame_asm);
+
+ Tag rx_tag;
+ segment_bls_t rx_segment_bls;
+ ASSERT_TRUE(disassemble_frame(m_rx_frame_asm, onwire_bl, rx_tag,
+ rx_segment_bls));
+ }
+}
+
+static const round_trip_instance_t round_trip_perf_instances[] = {
+ {41, 250, 0, 0, 2, {{32, 41, 250, 17, 0, 0},
+ {32, 48, 256, 32, 0, 0},
+ {32, 45, 250, 13, 0, 0},
+ {96, 0, 256, 32, 0, 0}}},
+ {41, 250, 0, 512, 4, {{32, 41, 250, 0, 512, 17},
+ {32, 48, 256, 0, 512, 32},
+ {32, 45, 250, 0, 512, 13},
+ {96, 0, 256, 0, 512, 32}}},
+ {41, 250, 0, 4096, 4, {{32, 41, 250, 0, 4096, 17},
+ {32, 48, 256, 0, 4096, 32},
+ {32, 45, 250, 0, 4096, 13},
+ {96, 0, 256, 0, 4096, 32}}},
+ {41, 250, 0, 32768, 4, {{32, 41, 250, 0, 32768, 17},
+ {32, 48, 256, 0, 32768, 32},
+ {32, 45, 250, 0, 32768, 13},
+ {96, 0, 256, 0, 32768, 32}}},
+ {41, 250, 0, 131072, 4, {{32, 41, 250, 0, 131072, 17},
+ {32, 48, 256, 0, 131072, 32},
+ {32, 45, 250, 0, 131072, 13},
+ {96, 0, 256, 0, 131072, 32}}},
+ {41, 250, 0, 4194304, 4, {{32, 41, 250, 0, 4194304, 17},
+ {32, 48, 256, 0, 4194304, 32},
+ {32, 45, 250, 0, 4194304, 13},
+ {96, 0, 256, 0, 4194304, 32}}},
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ RoundTripPerfTests, RoundTripPerfTest, ::testing::Combine(
+ ::testing::ValuesIn(round_trip_perf_instances),
+ ::testing::ValuesIn(modes)));
+
+} // namespace ceph::msgr::v2
+
+int main(int argc, char* argv[]) {
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/msgr/test_msgr.cc b/src/test/msgr/test_msgr.cc
new file mode 100644
index 000000000..f702cc288
--- /dev/null
+++ b/src/test/msgr/test_msgr.cc
@@ -0,0 +1,2424 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 UnitedStack <haomai@unitedstack.com>
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <atomic>
+#include <iostream>
+#include <list>
+#include <memory>
+#include <set>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <boost/random/binomial_distribution.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <gtest/gtest.h>
+
+#define MSG_POLICY_UNIT_TESTING
+
+#include "common/ceph_argparse.h"
+#include "common/ceph_mutex.h"
+#include "global/global_init.h"
+#include "messages/MCommand.h"
+#include "messages/MPing.h"
+#include "msg/Connection.h"
+#include "msg/Dispatcher.h"
+#include "msg/Message.h"
+#include "msg/Messenger.h"
+#include "msg/msg_types.h"
+
+typedef boost::mt11213b gen_type;
+
+#include "common/dout.h"
+#include "include/ceph_assert.h"
+
+#include "auth/DummyAuth.h"
+
+#define dout_subsys ceph_subsys_ms
+#undef dout_prefix
+#define dout_prefix *_dout << " ceph_test_msgr "
+
+
+#define CHECK_AND_WAIT_TRUE(expr) do { \
+ int n = 1000; \
+ while (--n) { \
+ if (expr) \
+ break; \
+ usleep(1000); \
+ } \
+} while(0);
+
+using namespace std;
+
+class MessengerTest : public ::testing::TestWithParam<const char*> {
+ public:
+ DummyAuthClientServer dummy_auth;
+ Messenger *server_msgr;
+ Messenger *client_msgr;
+
+ MessengerTest() : dummy_auth(g_ceph_context),
+ server_msgr(NULL), client_msgr(NULL) {
+ dummy_auth.auth_registry.refresh_config();
+ }
+ void SetUp() override {
+ lderr(g_ceph_context) << __func__ << " start set up " << GetParam() << dendl;
+ server_msgr = Messenger::create(g_ceph_context, string(GetParam()), entity_name_t::OSD(0), "server", getpid());
+ client_msgr = Messenger::create(g_ceph_context, string(GetParam()), entity_name_t::CLIENT(-1), "client", getpid());
+ server_msgr->set_default_policy(Messenger::Policy::stateless_server(0));
+ client_msgr->set_default_policy(Messenger::Policy::lossy_client(0));
+ server_msgr->set_auth_client(&dummy_auth);
+ server_msgr->set_auth_server(&dummy_auth);
+ client_msgr->set_auth_client(&dummy_auth);
+ client_msgr->set_auth_server(&dummy_auth);
+ server_msgr->set_require_authorizer(false);
+ }
+ void TearDown() override {
+ ASSERT_EQ(server_msgr->get_dispatch_queue_len(), 0);
+ ASSERT_EQ(client_msgr->get_dispatch_queue_len(), 0);
+ delete server_msgr;
+ delete client_msgr;
+ }
+
+};
+
+
+class FakeDispatcher : public Dispatcher {
+ public:
+ struct Session : public RefCountedObject {
+ atomic<uint64_t> count;
+ ConnectionRef con;
+
+ explicit Session(ConnectionRef c): RefCountedObject(g_ceph_context), count(0), con(c) {
+ }
+ uint64_t get_count() { return count; }
+ };
+
+ ceph::mutex lock = ceph::make_mutex("FakeDispatcher::lock");
+ ceph::condition_variable cond;
+ bool is_server;
+ bool got_new;
+ bool got_remote_reset;
+ bool got_connect;
+ bool loopback;
+ entity_addrvec_t last_accept;
+ ConnectionRef *last_accept_con_ptr = nullptr;
+
+ explicit FakeDispatcher(bool s): Dispatcher(g_ceph_context),
+ is_server(s), got_new(false), got_remote_reset(false),
+ got_connect(false), loopback(false) {
+ }
+ bool ms_can_fast_dispatch_any() const override { return true; }
+ bool ms_can_fast_dispatch(const Message *m) const override {
+ switch (m->get_type()) {
+ case CEPH_MSG_PING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void ms_handle_fast_connect(Connection *con) override {
+ std::scoped_lock l{lock};
+ lderr(g_ceph_context) << __func__ << " " << con << dendl;
+ auto s = con->get_priv();
+ if (!s) {
+ auto session = new Session(con);
+ con->set_priv(RefCountedPtr{session, false});
+ lderr(g_ceph_context) << __func__ << " con: " << con
+ << " count: " << session->count << dendl;
+ }
+ got_connect = true;
+ cond.notify_all();
+ }
+ void ms_handle_fast_accept(Connection *con) override {
+ last_accept = con->get_peer_addrs();
+ if (last_accept_con_ptr) {
+ *last_accept_con_ptr = con;
+ }
+ if (!con->get_priv()) {
+ con->set_priv(RefCountedPtr{new Session(con), false});
+ }
+ }
+ bool ms_dispatch(Message *m) override {
+ auto priv = m->get_connection()->get_priv();
+ auto s = static_cast<Session*>(priv.get());
+ if (!s) {
+ s = new Session(m->get_connection());
+ priv.reset(s, false);
+ m->get_connection()->set_priv(priv);
+ }
+ s->count++;
+ lderr(g_ceph_context) << __func__ << " conn: " << m->get_connection() << " session " << s << " count: " << s->count << dendl;
+ if (is_server) {
+ reply_message(m);
+ }
+ std::lock_guard l{lock};
+ got_new = true;
+ cond.notify_all();
+ m->put();
+ return true;
+ }
+ bool ms_handle_reset(Connection *con) override {
+ std::lock_guard l{lock};
+ lderr(g_ceph_context) << __func__ << " " << con << dendl;
+ auto priv = con->get_priv();
+ if (auto s = static_cast<Session*>(priv.get()); s) {
+ s->con.reset(); // break con <-> session ref cycle
+ con->set_priv(nullptr); // break ref <-> session cycle, if any
+ }
+ return true;
+ }
+ void ms_handle_remote_reset(Connection *con) override {
+ std::lock_guard l{lock};
+ lderr(g_ceph_context) << __func__ << " " << con << dendl;
+ auto priv = con->get_priv();
+ if (auto s = static_cast<Session*>(priv.get()); s) {
+ s->con.reset(); // break con <-> session ref cycle
+ con->set_priv(nullptr); // break ref <-> session cycle, if any
+ }
+ got_remote_reset = true;
+ cond.notify_all();
+ }
+ bool ms_handle_refused(Connection *con) override {
+ return false;
+ }
+ void ms_fast_dispatch(Message *m) override {
+ auto priv = m->get_connection()->get_priv();
+ auto s = static_cast<Session*>(priv.get());
+ if (!s) {
+ s = new Session(m->get_connection());
+ priv.reset(s, false);
+ m->get_connection()->set_priv(priv);
+ }
+ s->count++;
+ lderr(g_ceph_context) << __func__ << " conn: " << m->get_connection() << " session " << s << " count: " << s->count << dendl;
+ if (is_server) {
+ if (loopback)
+ ceph_assert(m->get_source().is_osd());
+ else
+ reply_message(m);
+ } else if (loopback) {
+ ceph_assert(m->get_source().is_client());
+ }
+ m->put();
+ std::lock_guard l{lock};
+ got_new = true;
+ cond.notify_all();
+ }
+
+ int ms_handle_fast_authentication(Connection *con) override {
+ return 1;
+ }
+
+ void reply_message(Message *m) {
+ MPing *rm = new MPing();
+ m->get_connection()->send_message(rm);
+ }
+};
+
+typedef FakeDispatcher::Session Session;
+
+struct TestInterceptor : public Interceptor {
+
+ bool step_waiting = false;
+ bool waiting = true;
+ std::map<Connection *, uint32_t> current_step;
+ std::map<Connection *, std::list<uint32_t>> step_history;
+ std::map<uint32_t, std::optional<ACTION>> decisions;
+ std::set<uint32_t> breakpoints;
+
+ uint32_t count_step(Connection *conn, uint32_t step) {
+ uint32_t count = 0;
+ for (auto s : step_history[conn]) {
+ if (s == step) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ void breakpoint(uint32_t step) {
+ breakpoints.insert(step);
+ }
+
+ void remove_bp(uint32_t step) {
+ breakpoints.erase(step);
+ }
+
+ Connection *wait(uint32_t step, Connection *conn=nullptr) {
+ std::unique_lock<std::mutex> l(lock);
+ while(true) {
+ if (conn) {
+ auto it = current_step.find(conn);
+ if (it != current_step.end()) {
+ if (it->second == step) {
+ break;
+ }
+ }
+ } else {
+ for (auto it : current_step) {
+ if (it.second == step) {
+ conn = it.first;
+ break;
+ }
+ }
+ if (conn) {
+ break;
+ }
+ }
+ step_waiting = true;
+ cond_var.wait(l);
+ }
+ step_waiting = false;
+ return conn;
+ }
+
+ ACTION wait_for_decision(uint32_t step, std::unique_lock<std::mutex> &l) {
+ if (decisions[step]) {
+ return *(decisions[step]);
+ }
+ waiting = true;
+ cond_var.wait(l, [this] { return !waiting; });
+ return *(decisions[step]);
+ }
+
+ void proceed(uint32_t step, ACTION decision) {
+ std::unique_lock<std::mutex> l(lock);
+ decisions[step] = decision;
+ if (waiting) {
+ waiting = false;
+ cond_var.notify_one();
+ }
+ }
+
+ ACTION intercept(Connection *conn, uint32_t step) override {
+ lderr(g_ceph_context) << __func__ << " conn(" << conn
+ << ") intercept called on step=" << step << dendl;
+
+ {
+ std::unique_lock<std::mutex> l(lock);
+ step_history[conn].push_back(step);
+ current_step[conn] = step;
+ if (step_waiting) {
+ cond_var.notify_one();
+ }
+ }
+
+ std::unique_lock<std::mutex> l(lock);
+ ACTION decision = ACTION::CONTINUE;
+ if (breakpoints.find(step) != breakpoints.end()) {
+ lderr(g_ceph_context) << __func__ << " conn(" << conn
+ << ") pausing on step=" << step << dendl;
+ decision = wait_for_decision(step, l);
+ } else {
+ if (decisions[step]) {
+ decision = *(decisions[step]);
+ }
+ }
+ lderr(g_ceph_context) << __func__ << " conn(" << conn
+ << ") resuming step=" << step << " with decision="
+ << decision << dendl;
+ decisions[step].reset();
+ return decision;
+ }
+
+};
+
+/**
+ * Scenario: A connects to B, and B connects to A at the same time.
+ */
+TEST_P(MessengerTest, ConnectionRaceTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(false);
+
+ TestInterceptor *cli_interceptor = new TestInterceptor();
+ TestInterceptor *srv_interceptor = new TestInterceptor();
+
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, Messenger::Policy::lossless_peer_reuse(0));
+ server_msgr->interceptor = srv_interceptor;
+
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, Messenger::Policy::lossless_peer_reuse(0));
+ client_msgr->interceptor = cli_interceptor;
+
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1:3300");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ bind_addr.parse("v2:127.0.0.1:3301");
+ client_msgr->bind(bind_addr);
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // pause before sending client_ident message
+ cli_interceptor->breakpoint(Interceptor::STEP::SEND_CLIENT_IDENTITY);
+ // pause before sending client_ident message
+ srv_interceptor->breakpoint(Interceptor::STEP::SEND_CLIENT_IDENTITY);
+
+ ConnectionRef c2s = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ MPing *m1 = new MPing();
+ ASSERT_EQ(c2s->send_message(m1), 0);
+
+ ConnectionRef s2c = server_msgr->connect_to(client_msgr->get_mytype(),
+ client_msgr->get_myaddrs());
+ MPing *m2 = new MPing();
+ ASSERT_EQ(s2c->send_message(m2), 0);
+
+ cli_interceptor->wait(Interceptor::STEP::SEND_CLIENT_IDENTITY, c2s.get());
+ srv_interceptor->wait(Interceptor::STEP::SEND_CLIENT_IDENTITY, s2c.get());
+
+ // at this point both connections (A->B, B->A) are paused just before sending
+ // the client_ident message.
+
+ cli_interceptor->remove_bp(Interceptor::STEP::SEND_CLIENT_IDENTITY);
+ srv_interceptor->remove_bp(Interceptor::STEP::SEND_CLIENT_IDENTITY);
+
+ cli_interceptor->proceed(Interceptor::STEP::SEND_CLIENT_IDENTITY, Interceptor::ACTION::CONTINUE);
+ srv_interceptor->proceed(Interceptor::STEP::SEND_CLIENT_IDENTITY, Interceptor::ACTION::CONTINUE);
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ {
+ std::unique_lock l{srv_dispatcher.lock};
+ srv_dispatcher.cond.wait(l, [&] { return srv_dispatcher.got_new; });
+ srv_dispatcher.got_new = false;
+ }
+
+ ASSERT_TRUE(s2c->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(s2c->get_priv().get())->get_count());
+ ASSERT_TRUE(s2c->peer_is_client());
+
+ ASSERT_TRUE(c2s->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(c2s->get_priv().get())->get_count());
+ ASSERT_TRUE(c2s->peer_is_osd());
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ delete cli_interceptor;
+ delete srv_interceptor;
+}
+
+/**
+ * Scenario: A connects to B, and B connects to A at the same time.
+ * The first (A -> B) connection gets to message flow handshake, the
+ * second (B -> A) connection is stuck waiting for a banner from A.
+ * After A sends client_ident to B, the first connection wins and B
+ * calls reuse_connection() to replace the second connection's socket
+ * while the second connection is still in BANNER_CONNECTING.
+ */
+TEST_P(MessengerTest, ConnectionRaceReuseBannerTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(false);
+
+ auto cli_interceptor = std::make_unique<TestInterceptor>();
+ auto srv_interceptor = std::make_unique<TestInterceptor>();
+
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT,
+ Messenger::Policy::lossless_peer_reuse(0));
+ server_msgr->interceptor = srv_interceptor.get();
+
+ client_msgr->set_policy(entity_name_t::TYPE_OSD,
+ Messenger::Policy::lossless_peer_reuse(0));
+ client_msgr->interceptor = cli_interceptor.get();
+
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1:3300");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ bind_addr.parse("v2:127.0.0.1:3301");
+ client_msgr->bind(bind_addr);
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // pause before sending client_ident message
+ srv_interceptor->breakpoint(Interceptor::STEP::SEND_CLIENT_IDENTITY);
+
+ ConnectionRef s2c = server_msgr->connect_to(client_msgr->get_mytype(),
+ client_msgr->get_myaddrs());
+ MPing *m1 = new MPing();
+ ASSERT_EQ(s2c->send_message(m1), 0);
+
+ srv_interceptor->wait(Interceptor::STEP::SEND_CLIENT_IDENTITY);
+ srv_interceptor->remove_bp(Interceptor::STEP::SEND_CLIENT_IDENTITY);
+
+ // pause before sending banner
+ cli_interceptor->breakpoint(Interceptor::STEP::BANNER_EXCHANGE_BANNER_CONNECTING);
+
+ ConnectionRef c2s = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ MPing *m2 = new MPing();
+ ASSERT_EQ(c2s->send_message(m2), 0);
+
+ cli_interceptor->wait(Interceptor::STEP::BANNER_EXCHANGE_BANNER_CONNECTING);
+ cli_interceptor->remove_bp(Interceptor::STEP::BANNER_EXCHANGE_BANNER_CONNECTING);
+
+ // second connection is in BANNER_CONNECTING, ensure it stays so
+ // and send client_ident
+ srv_interceptor->breakpoint(Interceptor::STEP::BANNER_EXCHANGE);
+ srv_interceptor->proceed(Interceptor::STEP::SEND_CLIENT_IDENTITY, Interceptor::ACTION::CONTINUE);
+
+ // handle client_ident -- triggers reuse_connection() with exproto
+ // in BANNER_CONNECTING
+ cli_interceptor->breakpoint(Interceptor::STEP::READY);
+ cli_interceptor->proceed(Interceptor::STEP::BANNER_EXCHANGE_BANNER_CONNECTING, Interceptor::ACTION::CONTINUE);
+
+ cli_interceptor->wait(Interceptor::STEP::READY);
+ cli_interceptor->remove_bp(Interceptor::STEP::READY);
+
+ // first connection is in READY
+ Connection *s2c_accepter = srv_interceptor->wait(Interceptor::STEP::BANNER_EXCHANGE);
+ srv_interceptor->remove_bp(Interceptor::STEP::BANNER_EXCHANGE);
+
+ srv_interceptor->proceed(Interceptor::STEP::BANNER_EXCHANGE, Interceptor::ACTION::CONTINUE);
+ cli_interceptor->proceed(Interceptor::STEP::READY, Interceptor::ACTION::CONTINUE);
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ {
+ std::unique_lock l{srv_dispatcher.lock};
+ srv_dispatcher.cond.wait(l, [&] { return srv_dispatcher.got_new; });
+ srv_dispatcher.got_new = false;
+ }
+
+ EXPECT_TRUE(s2c->is_connected());
+ EXPECT_EQ(1u, static_cast<Session*>(s2c->get_priv().get())->get_count());
+ EXPECT_TRUE(s2c->peer_is_client());
+
+ EXPECT_TRUE(c2s->is_connected());
+ EXPECT_EQ(1u, static_cast<Session*>(c2s->get_priv().get())->get_count());
+ EXPECT_TRUE(c2s->peer_is_osd());
+
+ // closed in reuse_connection() -- EPIPE when writing banner/hello
+ EXPECT_FALSE(s2c_accepter->is_connected());
+
+ // established exactly once, never faulted and reconnected
+ EXPECT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::START_CLIENT_BANNER_EXCHANGE), 1u);
+ EXPECT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::SEND_RECONNECT), 0u);
+ EXPECT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::READY), 1u);
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+}
+
+/**
+ * Scenario:
+ * - A connects to B
+ * - A sends client_ident to B
+ * - B fails before sending server_ident to A
+ * - A reconnects
+ */
+TEST_P(MessengerTest, MissingServerIdenTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(false);
+
+ TestInterceptor *cli_interceptor = new TestInterceptor();
+ TestInterceptor *srv_interceptor = new TestInterceptor();
+
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, Messenger::Policy::stateful_server(0));
+ server_msgr->interceptor = srv_interceptor;
+
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, Messenger::Policy::lossy_client(0));
+ client_msgr->interceptor = cli_interceptor;
+
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1:3300");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ bind_addr.parse("v2:127.0.0.1:3301");
+ client_msgr->bind(bind_addr);
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // pause before sending server_ident message
+ srv_interceptor->breakpoint(Interceptor::STEP::SEND_SERVER_IDENTITY);
+
+ ConnectionRef c2s = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ MPing *m1 = new MPing();
+ ASSERT_EQ(c2s->send_message(m1), 0);
+
+ Connection *c2s_accepter = srv_interceptor->wait(Interceptor::STEP::SEND_SERVER_IDENTITY);
+ srv_interceptor->remove_bp(Interceptor::STEP::SEND_SERVER_IDENTITY);
+
+ // We inject a message from this side of the connection to force it to be
+ // in standby when we inject the failure below
+ MPing *m2 = new MPing();
+ ASSERT_EQ(c2s_accepter->send_message(m2), 0);
+
+ srv_interceptor->proceed(Interceptor::STEP::SEND_SERVER_IDENTITY, Interceptor::ACTION::FAIL);
+
+ {
+ std::unique_lock l{srv_dispatcher.lock};
+ srv_dispatcher.cond.wait(l, [&] { return srv_dispatcher.got_new; });
+ srv_dispatcher.got_new = false;
+ }
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ ASSERT_TRUE(c2s->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(c2s->get_priv().get())->get_count());
+ ASSERT_TRUE(c2s->peer_is_osd());
+
+ ASSERT_TRUE(c2s_accepter->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(c2s_accepter->get_priv().get())->get_count());
+ ASSERT_TRUE(c2s_accepter->peer_is_client());
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ delete cli_interceptor;
+ delete srv_interceptor;
+}
+
+/**
+ * Scenario:
+ * - A connects to B
+ * - A sends client_ident to B
+ * - B fails before sending server_ident to A
+ * - A goes to standby
+ * - B reconnects to A
+ */
+TEST_P(MessengerTest, MissingServerIdenTest2) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(false);
+
+ TestInterceptor *cli_interceptor = new TestInterceptor();
+ TestInterceptor *srv_interceptor = new TestInterceptor();
+
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, Messenger::Policy::lossless_peer(0));
+ server_msgr->interceptor = srv_interceptor;
+
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, Messenger::Policy::lossless_peer(0));
+ client_msgr->interceptor = cli_interceptor;
+
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1:3300");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ bind_addr.parse("v2:127.0.0.1:3301");
+ client_msgr->bind(bind_addr);
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // pause before sending server_ident message
+ srv_interceptor->breakpoint(Interceptor::STEP::SEND_SERVER_IDENTITY);
+
+ ConnectionRef c2s = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+
+ Connection *c2s_accepter = srv_interceptor->wait(Interceptor::STEP::SEND_SERVER_IDENTITY);
+ srv_interceptor->remove_bp(Interceptor::STEP::SEND_SERVER_IDENTITY);
+
+ // We inject a message from this side of the connection to force it to be
+ // in standby when we inject the failure below
+ MPing *m2 = new MPing();
+ ASSERT_EQ(c2s_accepter->send_message(m2), 0);
+
+ srv_interceptor->proceed(Interceptor::STEP::SEND_SERVER_IDENTITY, Interceptor::ACTION::FAIL);
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ ASSERT_TRUE(c2s->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(c2s->get_priv().get())->get_count());
+ ASSERT_TRUE(c2s->peer_is_osd());
+
+ ASSERT_TRUE(c2s_accepter->is_connected());
+ ASSERT_EQ(0u, static_cast<Session*>(c2s_accepter->get_priv().get())->get_count());
+ ASSERT_TRUE(c2s_accepter->peer_is_client());
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ delete cli_interceptor;
+ delete srv_interceptor;
+}
+
+/**
+ * Scenario:
+ * - A connects to B
+ * - A and B exchange messages
+ * - A fails
+ * - B goes into standby
+ * - A reconnects
+ */
+TEST_P(MessengerTest, ReconnectTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+
+ TestInterceptor *cli_interceptor = new TestInterceptor();
+ TestInterceptor *srv_interceptor = new TestInterceptor();
+
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, Messenger::Policy::stateful_server(0));
+ server_msgr->interceptor = srv_interceptor;
+
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, Messenger::Policy::lossless_peer(0));
+ client_msgr->interceptor = cli_interceptor;
+
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1:3300");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ bind_addr.parse("v2:127.0.0.1:3301");
+ client_msgr->bind(bind_addr);
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ ConnectionRef c2s = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+
+ MPing *m1 = new MPing();
+ ASSERT_EQ(c2s->send_message(m1), 0);
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ ASSERT_TRUE(c2s->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(c2s->get_priv().get())->get_count());
+ ASSERT_TRUE(c2s->peer_is_osd());
+
+ cli_interceptor->breakpoint(Interceptor::STEP::HANDLE_MESSAGE);
+
+ MPing *m2 = new MPing();
+ ASSERT_EQ(c2s->send_message(m2), 0);
+
+ cli_interceptor->wait(Interceptor::STEP::HANDLE_MESSAGE, c2s.get());
+ cli_interceptor->remove_bp(Interceptor::STEP::HANDLE_MESSAGE);
+
+ // at this point client and server are connected together
+
+ srv_interceptor->breakpoint(Interceptor::STEP::READY);
+
+ // failing client
+ cli_interceptor->proceed(Interceptor::STEP::HANDLE_MESSAGE, Interceptor::ACTION::FAIL);
+
+ MPing *m3 = new MPing();
+ ASSERT_EQ(c2s->send_message(m3), 0);
+
+ Connection *c2s_accepter = srv_interceptor->wait(Interceptor::STEP::READY);
+ // the srv end of theconnection is now paused at ready
+ // this means that the reconnect was successful
+ srv_interceptor->remove_bp(Interceptor::STEP::READY);
+
+ ASSERT_TRUE(c2s_accepter->peer_is_client());
+ // c2s_accepter sent 0 reconnect messages
+ ASSERT_EQ(srv_interceptor->count_step(c2s_accepter, Interceptor::STEP::SEND_RECONNECT), 0u);
+ // c2s_accepter sent 1 reconnect_ok messages
+ ASSERT_EQ(srv_interceptor->count_step(c2s_accepter, Interceptor::STEP::SEND_RECONNECT_OK), 1u);
+ // c2s sent 1 reconnect messages
+ ASSERT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::SEND_RECONNECT), 1u);
+ // c2s sent 0 reconnect_ok messages
+ ASSERT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::SEND_RECONNECT_OK), 0u);
+
+ srv_interceptor->proceed(15, Interceptor::ACTION::CONTINUE);
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ delete cli_interceptor;
+ delete srv_interceptor;
+}
+
+/**
+ * Scenario:
+ * - A connects to B
+ * - A and B exchange messages
+ * - A fails
+ * - A reconnects // B reconnects
+ */
+TEST_P(MessengerTest, ReconnectRaceTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+
+ TestInterceptor *cli_interceptor = new TestInterceptor();
+ TestInterceptor *srv_interceptor = new TestInterceptor();
+
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, Messenger::Policy::lossless_peer(0));
+ server_msgr->interceptor = srv_interceptor;
+
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, Messenger::Policy::lossless_peer(0));
+ client_msgr->interceptor = cli_interceptor;
+
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1:3300");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ bind_addr.parse("v2:127.0.0.1:3301");
+ client_msgr->bind(bind_addr);
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ ConnectionRef c2s = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+
+ MPing *m1 = new MPing();
+ ASSERT_EQ(c2s->send_message(m1), 0);
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ ASSERT_TRUE(c2s->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(c2s->get_priv().get())->get_count());
+ ASSERT_TRUE(c2s->peer_is_osd());
+
+ cli_interceptor->breakpoint(Interceptor::STEP::HANDLE_MESSAGE);
+
+ MPing *m2 = new MPing();
+ ASSERT_EQ(c2s->send_message(m2), 0);
+
+ cli_interceptor->wait(Interceptor::STEP::HANDLE_MESSAGE, c2s.get());
+ cli_interceptor->remove_bp(Interceptor::STEP::HANDLE_MESSAGE);
+
+ // at this point client and server are connected together
+
+ // force both client and server to race on reconnect
+ cli_interceptor->breakpoint(Interceptor::STEP::SEND_RECONNECT);
+ srv_interceptor->breakpoint(Interceptor::STEP::SEND_RECONNECT);
+
+ // failing client
+ // this will cause both client and server to reconnect at the same time
+ cli_interceptor->proceed(Interceptor::STEP::HANDLE_MESSAGE, Interceptor::ACTION::FAIL);
+
+ MPing *m3 = new MPing();
+ ASSERT_EQ(c2s->send_message(m3), 0);
+
+ cli_interceptor->wait(Interceptor::STEP::SEND_RECONNECT, c2s.get());
+ srv_interceptor->wait(Interceptor::STEP::SEND_RECONNECT);
+
+ cli_interceptor->remove_bp(Interceptor::STEP::SEND_RECONNECT);
+ srv_interceptor->remove_bp(Interceptor::STEP::SEND_RECONNECT);
+
+ // pause on "ready"
+ srv_interceptor->breakpoint(Interceptor::STEP::READY);
+
+ cli_interceptor->proceed(Interceptor::STEP::SEND_RECONNECT, Interceptor::ACTION::CONTINUE);
+ srv_interceptor->proceed(Interceptor::STEP::SEND_RECONNECT, Interceptor::ACTION::CONTINUE);
+
+ Connection *c2s_accepter = srv_interceptor->wait(Interceptor::STEP::READY);
+
+ // the server has reconnected and is "ready"
+ srv_interceptor->remove_bp(Interceptor::STEP::READY);
+
+ ASSERT_TRUE(c2s_accepter->peer_is_client());
+ ASSERT_TRUE(c2s->peer_is_osd());
+
+ // the server should win the reconnect race
+
+ // c2s_accepter sent 1 or 2 reconnect messages
+ ASSERT_LT(srv_interceptor->count_step(c2s_accepter, Interceptor::STEP::SEND_RECONNECT), 3u);
+ ASSERT_GT(srv_interceptor->count_step(c2s_accepter, Interceptor::STEP::SEND_RECONNECT), 0u);
+ // c2s_accepter sent 0 reconnect_ok messages
+ ASSERT_EQ(srv_interceptor->count_step(c2s_accepter, Interceptor::STEP::SEND_RECONNECT_OK), 0u);
+ // c2s sent 1 reconnect messages
+ ASSERT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::SEND_RECONNECT), 1u);
+ // c2s sent 1 reconnect_ok messages
+ ASSERT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::SEND_RECONNECT_OK), 1u);
+
+ if (srv_interceptor->count_step(c2s_accepter, Interceptor::STEP::SEND_RECONNECT) == 2) {
+ // if the server send the reconnect message two times then
+ // the client must have sent a session retry message to the server
+ ASSERT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::SESSION_RETRY), 1u);
+ } else {
+ ASSERT_EQ(cli_interceptor->count_step(c2s.get(), Interceptor::STEP::SESSION_RETRY), 0u);
+ }
+
+ srv_interceptor->proceed(Interceptor::STEP::READY, Interceptor::ACTION::CONTINUE);
+
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ delete cli_interceptor;
+ delete srv_interceptor;
+}
+
+TEST_P(MessengerTest, SimpleTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // 1. simple round trip
+ MPing *m = new MPing();
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_TRUE(conn->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(conn->get_priv().get())->get_count());
+ ASSERT_TRUE(conn->peer_is_osd());
+
+ // 2. test rebind port
+ set<int> avoid_ports;
+ for (int i = 0; i < 10 ; i++) {
+ for (auto a : server_msgr->get_myaddrs().v) {
+ avoid_ports.insert(a.get_port() + i);
+ }
+ }
+ server_msgr->rebind(avoid_ports);
+ for (auto a : server_msgr->get_myaddrs().v) {
+ ASSERT_TRUE(avoid_ports.count(a.get_port()) == 0);
+ }
+
+ conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+
+ // 3. test markdown connection
+ conn->mark_down();
+ ASSERT_FALSE(conn->is_connected());
+
+ // 4. test failed connection
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ m = new MPing();
+ conn->send_message(m);
+ CHECK_AND_WAIT_TRUE(!conn->is_connected());
+ ASSERT_FALSE(conn->is_connected());
+
+ // 5. loopback connection
+ srv_dispatcher.loopback = true;
+ conn = client_msgr->get_loopback_connection();
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ srv_dispatcher.loopback = false;
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+}
+
+TEST_P(MessengerTest, SimpleMsgr2Test) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t legacy_addr;
+ legacy_addr.parse("v1:127.0.0.1");
+ entity_addr_t msgr2_addr;
+ msgr2_addr.parse("v2:127.0.0.1");
+ entity_addrvec_t bind_addrs;
+ bind_addrs.v.push_back(legacy_addr);
+ bind_addrs.v.push_back(msgr2_addr);
+ server_msgr->bindv(bind_addrs);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // 1. simple round trip
+ MPing *m = new MPing();
+ ConnectionRef conn = client_msgr->connect_to(
+ server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_TRUE(conn->is_connected());
+ ASSERT_EQ(1u, static_cast<Session*>(conn->get_priv().get())->get_count());
+ ASSERT_TRUE(conn->peer_is_osd());
+
+ // 2. test rebind port
+ set<int> avoid_ports;
+ for (int i = 0; i < 10 ; i++) {
+ for (auto a : server_msgr->get_myaddrs().v) {
+ avoid_ports.insert(a.get_port() + i);
+ }
+ }
+ server_msgr->rebind(avoid_ports);
+ for (auto a : server_msgr->get_myaddrs().v) {
+ ASSERT_TRUE(avoid_ports.count(a.get_port()) == 0);
+ }
+
+ conn = client_msgr->connect_to(
+ server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+
+ // 3. test markdown connection
+ conn->mark_down();
+ ASSERT_FALSE(conn->is_connected());
+
+ // 4. test failed connection
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ m = new MPing();
+ conn->send_message(m);
+ CHECK_AND_WAIT_TRUE(!conn->is_connected());
+ ASSERT_FALSE(conn->is_connected());
+
+ // 5. loopback connection
+ srv_dispatcher.loopback = true;
+ conn = client_msgr->get_loopback_connection();
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ srv_dispatcher.loopback = false;
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ client_msgr->shutdown();
+ client_msgr->wait();
+ server_msgr->shutdown();
+ server_msgr->wait();
+}
+
+TEST_P(MessengerTest, FeatureTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ uint64_t all_feature_supported, feature_required, feature_supported = 0;
+ for (int i = 0; i < 10; i++)
+ feature_supported |= 1ULL << i;
+ feature_supported |= CEPH_FEATUREMASK_MSG_ADDR2;
+ feature_supported |= CEPH_FEATUREMASK_SERVER_NAUTILUS;
+ feature_required = feature_supported | 1ULL << 13;
+ all_feature_supported = feature_required | 1ULL << 14;
+
+ Messenger::Policy p = server_msgr->get_policy(entity_name_t::TYPE_CLIENT);
+ p.features_required = feature_required;
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, p);
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ // 1. Suppose if only support less than required
+ p = client_msgr->get_policy(entity_name_t::TYPE_OSD);
+ p.features_supported = feature_supported;
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, p);
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ MPing *m = new MPing();
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ conn->send_message(m);
+ CHECK_AND_WAIT_TRUE(!conn->is_connected());
+ // should failed build a connection
+ ASSERT_FALSE(conn->is_connected());
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+
+ // 2. supported met required
+ p = client_msgr->get_policy(entity_name_t::TYPE_OSD);
+ p.features_supported = all_feature_supported;
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, p);
+ client_msgr->start();
+
+ conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+}
+
+TEST_P(MessengerTest, TimeoutTest) {
+ g_ceph_context->_conf.set_val("ms_connection_idle_timeout", "1");
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // 1. build the connection
+ MPing *m = new MPing();
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_TRUE(conn->is_connected());
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ ASSERT_TRUE(conn->peer_is_osd());
+
+ // 2. wait for idle
+ usleep(2500*1000);
+ ASSERT_FALSE(conn->is_connected());
+
+ server_msgr->shutdown();
+ server_msgr->wait();
+
+ client_msgr->shutdown();
+ client_msgr->wait();
+ g_ceph_context->_conf.set_val("ms_connection_idle_timeout", "900");
+}
+
+TEST_P(MessengerTest, StatefulTest) {
+ Message *m;
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ Messenger::Policy p = Messenger::Policy::stateful_server(0);
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, p);
+ p = Messenger::Policy::lossless_client(0);
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, p);
+
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // 1. test for server standby
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ conn->mark_down();
+ ASSERT_FALSE(conn->is_connected());
+ ConnectionRef server_conn = server_msgr->connect_to(
+ client_msgr->get_mytype(), srv_dispatcher.last_accept);
+ // don't lose state
+ ASSERT_EQ(1U, static_cast<Session*>(server_conn->get_priv().get())->get_count());
+
+ srv_dispatcher.got_new = false;
+ conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ server_conn = server_msgr->connect_to(client_msgr->get_mytype(),
+ srv_dispatcher.last_accept);
+ {
+ std::unique_lock l{srv_dispatcher.lock};
+ srv_dispatcher.cond.wait(l, [&] { return srv_dispatcher.got_remote_reset; });
+ }
+
+ // 2. test for client reconnect
+ ASSERT_FALSE(cli_dispatcher.got_remote_reset);
+ cli_dispatcher.got_connect = false;
+ cli_dispatcher.got_new = false;
+ cli_dispatcher.got_remote_reset = false;
+ server_conn->mark_down();
+ ASSERT_FALSE(server_conn->is_connected());
+ // ensure client detect server socket closed
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_remote_reset; });
+ cli_dispatcher.got_remote_reset = false;
+ }
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_connect; });
+ cli_dispatcher.got_connect = false;
+ }
+ CHECK_AND_WAIT_TRUE(conn->is_connected());
+ ASSERT_TRUE(conn->is_connected());
+
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ ASSERT_TRUE(conn->is_connected());
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ // resetcheck happen
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ server_conn = server_msgr->connect_to(client_msgr->get_mytype(),
+ srv_dispatcher.last_accept);
+ ASSERT_EQ(1U, static_cast<Session*>(server_conn->get_priv().get())->get_count());
+ cli_dispatcher.got_remote_reset = false;
+
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+}
+
+TEST_P(MessengerTest, StatelessTest) {
+ Message *m;
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ Messenger::Policy p = Messenger::Policy::stateless_server(0);
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, p);
+ p = Messenger::Policy::lossy_client(0);
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, p);
+
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // 1. test for server lose state
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ conn->mark_down();
+ ASSERT_FALSE(conn->is_connected());
+
+ srv_dispatcher.got_new = false;
+ ConnectionRef server_conn;
+ srv_dispatcher.last_accept_con_ptr = &server_conn;
+ conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ ASSERT_TRUE(server_conn);
+
+ // server lose state
+ {
+ std::unique_lock l{srv_dispatcher.lock};
+ srv_dispatcher.cond.wait(l, [&] { return srv_dispatcher.got_new; });
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(server_conn->get_priv().get())->get_count());
+
+ // 2. test for client lossy
+ server_conn->mark_down();
+ ASSERT_FALSE(server_conn->is_connected());
+ conn->send_keepalive();
+ CHECK_AND_WAIT_TRUE(!conn->is_connected());
+ ASSERT_FALSE(conn->is_connected());
+ conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+}
+
+TEST_P(MessengerTest, AnonTest) {
+ Message *m;
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ Messenger::Policy p = Messenger::Policy::stateless_server(0);
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, p);
+ p = Messenger::Policy::lossy_client(0);
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, p);
+
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ ConnectionRef server_con_a, server_con_b;
+
+ // a
+ srv_dispatcher.last_accept_con_ptr = &server_con_a;
+ ConnectionRef con_a = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs(),
+ true);
+ {
+ m = new MPing();
+ ASSERT_EQ(con_a->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(con_a->get_priv().get())->get_count());
+
+ // b
+ srv_dispatcher.last_accept_con_ptr = &server_con_b;
+ ConnectionRef con_b = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs(),
+ true);
+ {
+ m = new MPing();
+ ASSERT_EQ(con_b->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(con_b->get_priv().get())->get_count());
+
+ // these should be distinct
+ ASSERT_NE(con_a, con_b);
+ ASSERT_NE(server_con_a, server_con_b);
+
+ // and both connected
+ {
+ m = new MPing();
+ ASSERT_EQ(con_a->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ {
+ m = new MPing();
+ ASSERT_EQ(con_b->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+
+ // clean up
+ con_a->mark_down();
+ ASSERT_FALSE(con_a->is_connected());
+ con_b->mark_down();
+ ASSERT_FALSE(con_b->is_connected());
+
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+}
+
+TEST_P(MessengerTest, ClientStandbyTest) {
+ Message *m;
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ Messenger::Policy p = Messenger::Policy::stateful_server(0);
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, p);
+ p = Messenger::Policy::lossless_peer(0);
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, p);
+
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // 1. test for client standby, resetcheck
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ ConnectionRef server_conn = server_msgr->connect_to(
+ client_msgr->get_mytype(),
+ srv_dispatcher.last_accept);
+ ASSERT_FALSE(cli_dispatcher.got_remote_reset);
+ cli_dispatcher.got_connect = false;
+ server_conn->mark_down();
+ ASSERT_FALSE(server_conn->is_connected());
+ // client should be standby
+ usleep(300*1000);
+ // client should be standby, so we use original connection
+ {
+ // Try send message to verify got remote reset callback
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ {
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_remote_reset; });
+ cli_dispatcher.got_remote_reset = false;
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_connect; });
+ cli_dispatcher.got_connect = false;
+ }
+ CHECK_AND_WAIT_TRUE(conn->is_connected());
+ ASSERT_TRUE(conn->is_connected());
+ m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ server_conn = server_msgr->connect_to(client_msgr->get_mytype(),
+ srv_dispatcher.last_accept);
+ ASSERT_EQ(1U, static_cast<Session*>(server_conn->get_priv().get())->get_count());
+
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+}
+
+TEST_P(MessengerTest, AuthTest) {
+ g_ceph_context->_conf.set_val("auth_cluster_required", "cephx");
+ g_ceph_context->_conf.set_val("auth_service_required", "cephx");
+ g_ceph_context->_conf.set_val("auth_client_required", "cephx");
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+ // 1. simple auth round trip
+ MPing *m = new MPing();
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_TRUE(conn->is_connected());
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+
+ // 2. mix auth
+ g_ceph_context->_conf.set_val("auth_cluster_required", "none");
+ g_ceph_context->_conf.set_val("auth_service_required", "none");
+ g_ceph_context->_conf.set_val("auth_client_required", "none");
+ conn->mark_down();
+ ASSERT_FALSE(conn->is_connected());
+ conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ MPing *m = new MPing();
+ ASSERT_EQ(conn->send_message(m), 0);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ cli_dispatcher.got_new = false;
+ }
+ ASSERT_TRUE(conn->is_connected());
+ ASSERT_EQ(1U, static_cast<Session*>(conn->get_priv().get())->get_count());
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+}
+
+TEST_P(MessengerTest, MessageTest) {
+ FakeDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1");
+ Messenger::Policy p = Messenger::Policy::stateful_server(0);
+ server_msgr->set_policy(entity_name_t::TYPE_CLIENT, p);
+ p = Messenger::Policy::lossless_peer(0);
+ client_msgr->set_policy(entity_name_t::TYPE_OSD, p);
+
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->start();
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->start();
+
+
+ // 1. A very large "front"(as well as "payload")
+ // Because a external message need to invade Messenger::decode_message,
+ // here we only use existing message class(MCommand)
+ ConnectionRef conn = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ {
+ uuid_d uuid;
+ uuid.generate_random();
+ vector<string> cmds;
+ string s("abcdefghijklmnopqrstuvwxyz");
+ for (int i = 0; i < 1024*30; i++)
+ cmds.push_back(s);
+ MCommand *m = new MCommand(uuid);
+ m->cmd = cmds;
+ conn->send_message(m);
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait_for(l, 500s, [&] { return cli_dispatcher.got_new; });
+ ASSERT_TRUE(cli_dispatcher.got_new);
+ cli_dispatcher.got_new = false;
+ }
+
+ // 2. A very large "data"
+ {
+ bufferlist bl;
+ string s("abcdefghijklmnopqrstuvwxyz");
+ for (int i = 0; i < 1024*30; i++)
+ bl.append(s);
+ MPing *m = new MPing();
+ m->set_data(bl);
+ conn->send_message(m);
+ utime_t t;
+ t += 1000*1000*500;
+ std::unique_lock l{cli_dispatcher.lock};
+ cli_dispatcher.cond.wait(l, [&] { return cli_dispatcher.got_new; });
+ ASSERT_TRUE(cli_dispatcher.got_new);
+ cli_dispatcher.got_new = false;
+ }
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+}
+
+
+class SyntheticWorkload;
+
+struct Payload {
+ enum Who : uint8_t {
+ PING = 0,
+ PONG = 1,
+ };
+ uint8_t who = 0;
+ uint64_t seq = 0;
+ bufferlist data;
+
+ Payload(Who who, uint64_t seq, const bufferlist& data)
+ : who(who), seq(seq), data(data)
+ {}
+ Payload() = default;
+ DENC(Payload, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.who, p);
+ denc(v.seq, p);
+ denc(v.data, p);
+ DENC_FINISH(p);
+ }
+};
+WRITE_CLASS_DENC(Payload)
+
+ostream& operator<<(ostream& out, const Payload &pl)
+{
+ return out << "reply=" << pl.who << " i = " << pl.seq;
+}
+
+class SyntheticDispatcher : public Dispatcher {
+ public:
+ ceph::mutex lock = ceph::make_mutex("SyntheticDispatcher::lock");
+ ceph::condition_variable cond;
+ bool is_server;
+ bool got_new;
+ bool got_remote_reset;
+ bool got_connect;
+ map<ConnectionRef, list<uint64_t> > conn_sent;
+ map<uint64_t, bufferlist> sent;
+ atomic<uint64_t> index;
+ SyntheticWorkload *workload;
+
+ SyntheticDispatcher(bool s, SyntheticWorkload *wl):
+ Dispatcher(g_ceph_context), is_server(s), got_new(false),
+ got_remote_reset(false), got_connect(false), index(0), workload(wl) {
+ }
+ bool ms_can_fast_dispatch_any() const override { return true; }
+ bool ms_can_fast_dispatch(const Message *m) const override {
+ switch (m->get_type()) {
+ case CEPH_MSG_PING:
+ case MSG_COMMAND:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void ms_handle_fast_connect(Connection *con) override {
+ std::lock_guard l{lock};
+ list<uint64_t> c = conn_sent[con];
+ for (list<uint64_t>::iterator it = c.begin();
+ it != c.end(); ++it)
+ sent.erase(*it);
+ conn_sent.erase(con);
+ got_connect = true;
+ cond.notify_all();
+ }
+ void ms_handle_fast_accept(Connection *con) override {
+ std::lock_guard l{lock};
+ list<uint64_t> c = conn_sent[con];
+ for (list<uint64_t>::iterator it = c.begin();
+ it != c.end(); ++it)
+ sent.erase(*it);
+ conn_sent.erase(con);
+ cond.notify_all();
+ }
+ bool ms_dispatch(Message *m) override {
+ ceph_abort();
+ }
+ bool ms_handle_reset(Connection *con) override;
+ void ms_handle_remote_reset(Connection *con) override {
+ std::lock_guard l{lock};
+ list<uint64_t> c = conn_sent[con];
+ for (list<uint64_t>::iterator it = c.begin();
+ it != c.end(); ++it)
+ sent.erase(*it);
+ conn_sent.erase(con);
+ got_remote_reset = true;
+ }
+ bool ms_handle_refused(Connection *con) override {
+ return false;
+ }
+ void ms_fast_dispatch(Message *m) override {
+ // MSG_COMMAND is used to disorganize regular message flow
+ if (m->get_type() == MSG_COMMAND) {
+ m->put();
+ return ;
+ }
+
+ Payload pl;
+ auto p = m->get_data().cbegin();
+ decode(pl, p);
+ if (pl.who == Payload::PING) {
+ lderr(g_ceph_context) << __func__ << " conn=" << m->get_connection() << pl << dendl;
+ reply_message(m, pl);
+ m->put();
+ std::lock_guard l{lock};
+ got_new = true;
+ cond.notify_all();
+ } else {
+ std::lock_guard l{lock};
+ if (sent.count(pl.seq)) {
+ lderr(g_ceph_context) << __func__ << " conn=" << m->get_connection() << pl << dendl;
+ ASSERT_EQ(conn_sent[m->get_connection()].front(), pl.seq);
+ ASSERT_TRUE(pl.data.contents_equal(sent[pl.seq]));
+ conn_sent[m->get_connection()].pop_front();
+ sent.erase(pl.seq);
+ }
+ m->put();
+ got_new = true;
+ cond.notify_all();
+ }
+ }
+
+ int ms_handle_fast_authentication(Connection *con) override {
+ return 1;
+ }
+
+ void reply_message(const Message *m, Payload& pl) {
+ pl.who = Payload::PONG;
+ bufferlist bl;
+ encode(pl, bl);
+ MPing *rm = new MPing();
+ rm->set_data(bl);
+ m->get_connection()->send_message(rm);
+ lderr(g_ceph_context) << __func__ << " conn=" << m->get_connection() << " reply m=" << m << " i=" << pl.seq << dendl;
+ }
+
+ void send_message_wrap(ConnectionRef con, const bufferlist& data) {
+ Message *m = new MPing();
+ Payload pl{Payload::PING, index++, data};
+ bufferlist bl;
+ encode(pl, bl);
+ m->set_data(bl);
+ if (!con->get_messenger()->get_default_policy().lossy) {
+ std::lock_guard l{lock};
+ sent[pl.seq] = pl.data;
+ conn_sent[con].push_back(pl.seq);
+ }
+ lderr(g_ceph_context) << __func__ << " conn=" << con.get() << " send m=" << m << " i=" << pl.seq << dendl;
+ ASSERT_EQ(0, con->send_message(m));
+ }
+
+ uint64_t get_pending() {
+ std::lock_guard l{lock};
+ return sent.size();
+ }
+
+ void clear_pending(ConnectionRef con) {
+ std::lock_guard l{lock};
+
+ for (list<uint64_t>::iterator it = conn_sent[con].begin();
+ it != conn_sent[con].end(); ++it)
+ sent.erase(*it);
+ conn_sent.erase(con);
+ }
+
+ void print() {
+ for (auto && p : conn_sent) {
+ if (!p.second.empty()) {
+ lderr(g_ceph_context) << __func__ << " " << p.first << " wait " << p.second.size() << dendl;
+ }
+ }
+ }
+};
+
+
+class SyntheticWorkload {
+ ceph::mutex lock = ceph::make_mutex("SyntheticWorkload::lock");
+ ceph::condition_variable cond;
+ set<Messenger*> available_servers;
+ set<Messenger*> available_clients;
+ Messenger::Policy client_policy;
+ map<ConnectionRef, pair<Messenger*, Messenger*> > available_connections;
+ SyntheticDispatcher dispatcher;
+ gen_type rng;
+ vector<bufferlist> rand_data;
+ DummyAuthClientServer dummy_auth;
+
+ public:
+ const unsigned max_in_flight = 0;
+ const unsigned max_connections = 0;
+ static const unsigned max_message_len = 1024 * 1024 * 4;
+
+ SyntheticWorkload(int servers, int clients, string type, int random_num,
+ Messenger::Policy srv_policy, Messenger::Policy cli_policy,
+ int _max_in_flight = 64, int _max_connections = 128)
+ : client_policy(cli_policy),
+ dispatcher(false, this),
+ rng(time(NULL)),
+ dummy_auth(g_ceph_context),
+ max_in_flight(_max_in_flight),
+ max_connections(_max_connections) {
+
+ dummy_auth.auth_registry.refresh_config();
+ Messenger *msgr;
+ int base_port = 16800;
+ entity_addr_t bind_addr;
+ char addr[64];
+ for (int i = 0; i < servers; ++i) {
+ msgr = Messenger::create(g_ceph_context, type, entity_name_t::OSD(0),
+ "server", getpid()+i);
+ snprintf(addr, sizeof(addr), "v2:127.0.0.1:%d",
+ base_port+i);
+ bind_addr.parse(addr);
+ msgr->bind(bind_addr);
+ msgr->add_dispatcher_head(&dispatcher);
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+
+ ceph_assert(msgr);
+ msgr->set_default_policy(srv_policy);
+ available_servers.insert(msgr);
+ msgr->start();
+ }
+
+ for (int i = 0; i < clients; ++i) {
+ msgr = Messenger::create(g_ceph_context, type, entity_name_t::CLIENT(-1),
+ "client", getpid()+i+servers);
+ if (cli_policy.standby) {
+ snprintf(addr, sizeof(addr), "v2:127.0.0.1:%d",
+ base_port+i+servers);
+ bind_addr.parse(addr);
+ msgr->bind(bind_addr);
+ }
+ msgr->add_dispatcher_head(&dispatcher);
+ msgr->set_auth_client(&dummy_auth);
+ msgr->set_auth_server(&dummy_auth);
+
+ ceph_assert(msgr);
+ msgr->set_default_policy(cli_policy);
+ available_clients.insert(msgr);
+ msgr->start();
+ }
+
+ for (int i = 0; i < random_num; i++) {
+ bufferlist bl;
+ boost::uniform_int<> u(32, max_message_len);
+ uint64_t value_len = u(rng);
+ bufferptr bp(value_len);
+ bp.zero();
+ for (uint64_t j = 0; j < value_len-sizeof(i); ) {
+ memcpy(bp.c_str()+j, &i, sizeof(i));
+ j += 4096;
+ }
+
+ bl.append(bp);
+ rand_data.push_back(bl);
+ }
+ }
+
+ ConnectionRef _get_random_connection() {
+ while (dispatcher.get_pending() > max_in_flight) {
+ lock.unlock();
+ usleep(500);
+ lock.lock();
+ }
+ ceph_assert(ceph_mutex_is_locked(lock));
+ boost::uniform_int<> choose(0, available_connections.size() - 1);
+ int index = choose(rng);
+ map<ConnectionRef, pair<Messenger*, Messenger*> >::iterator i = available_connections.begin();
+ for (; index > 0; --index, ++i) ;
+ return i->first;
+ }
+
+ bool can_create_connection() {
+ return available_connections.size() < max_connections;
+ }
+
+ void generate_connection() {
+ std::lock_guard l{lock};
+ if (!can_create_connection())
+ return ;
+
+ Messenger *server, *client;
+ {
+ boost::uniform_int<> choose(0, available_servers.size() - 1);
+ int index = choose(rng);
+ set<Messenger*>::iterator i = available_servers.begin();
+ for (; index > 0; --index, ++i) ;
+ server = *i;
+ }
+ {
+ boost::uniform_int<> choose(0, available_clients.size() - 1);
+ int index = choose(rng);
+ set<Messenger*>::iterator i = available_clients.begin();
+ for (; index > 0; --index, ++i) ;
+ client = *i;
+ }
+
+ pair<Messenger*, Messenger*> p;
+ {
+ boost::uniform_int<> choose(0, available_servers.size() - 1);
+ if (server->get_default_policy().server) {
+ p = make_pair(client, server);
+ ConnectionRef conn = client->connect_to(server->get_mytype(),
+ server->get_myaddrs());
+ available_connections[conn] = p;
+ } else {
+ ConnectionRef conn = client->connect_to(server->get_mytype(),
+ server->get_myaddrs());
+ p = make_pair(client, server);
+ available_connections[conn] = p;
+ }
+ }
+ }
+
+ void send_message() {
+ std::lock_guard l{lock};
+ ConnectionRef conn = _get_random_connection();
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val >= 95) {
+ uuid_d uuid;
+ uuid.generate_random();
+ MCommand *m = new MCommand(uuid);
+ vector<string> cmds;
+ cmds.push_back("command");
+ m->cmd = cmds;
+ m->set_priority(200);
+ conn->send_message(m);
+ } else {
+ boost::uniform_int<> u(0, rand_data.size()-1);
+ dispatcher.send_message_wrap(conn, rand_data[u(rng)]);
+ }
+ }
+
+ void send_large_message(bool inject_network_congestion=false) {
+ std::lock_guard l{lock};
+ ConnectionRef conn = _get_random_connection();
+ uuid_d uuid;
+ uuid.generate_random();
+ MCommand *m = new MCommand(uuid);
+ vector<string> cmds;
+ cmds.push_back("command");
+ // set the random data to make the large message
+ bufferlist bl;
+ string s("abcdefghijklmnopqrstuvwxyz");
+ for (int i = 0; i < 1024*256; i++)
+ bl.append(s);
+ // bl is around 6M
+ m->set_data(bl);
+ m->cmd = cmds;
+ m->set_priority(200);
+ // setup after connection is ready
+ if (inject_network_congestion && conn->is_connected()) {
+ g_ceph_context->_conf.set_val("ms_inject_network_congestion", "100");
+ } else {
+ g_ceph_context->_conf.set_val("ms_inject_network_congestion", "0");
+ }
+ conn->send_message(m);
+ }
+
+ void drop_connection() {
+ std::lock_guard l{lock};
+ if (available_connections.size() < 10)
+ return;
+ ConnectionRef conn = _get_random_connection();
+ dispatcher.clear_pending(conn);
+ conn->mark_down();
+ if (!client_policy.server &&
+ !client_policy.lossy &&
+ client_policy.standby) {
+ // it's a lossless policy, so we need to mark down each side
+ pair<Messenger*, Messenger*> &p = available_connections[conn];
+ if (!p.first->get_default_policy().server && !p.second->get_default_policy().server) {
+ ASSERT_EQ(conn->get_messenger(), p.first);
+ ConnectionRef peer = p.second->connect_to(p.first->get_mytype(),
+ p.first->get_myaddrs());
+ peer->mark_down();
+ dispatcher.clear_pending(peer);
+ available_connections.erase(peer);
+ }
+ }
+ ASSERT_EQ(available_connections.erase(conn), 1U);
+ }
+
+ void print_internal_state(bool detail=false) {
+ std::lock_guard l{lock};
+ lderr(g_ceph_context) << "available_connections: " << available_connections.size()
+ << " inflight messages: " << dispatcher.get_pending() << dendl;
+ if (detail && !available_connections.empty()) {
+ dispatcher.print();
+ }
+ }
+
+ void wait_for_done() {
+ int64_t tick_us = 1000 * 100; // 100ms
+ int64_t timeout_us = 5 * 60 * 1000 * 1000; // 5 mins
+ int i = 0;
+ while (dispatcher.get_pending()) {
+ usleep(tick_us);
+ timeout_us -= tick_us;
+ if (i++ % 50 == 0)
+ print_internal_state(true);
+ if (timeout_us < 0)
+ ceph_abort_msg(" loop time exceed 5 mins, it looks we stuck into some problems!");
+ }
+ for (set<Messenger*>::iterator it = available_servers.begin();
+ it != available_servers.end(); ++it) {
+ (*it)->shutdown();
+ (*it)->wait();
+ ASSERT_EQ((*it)->get_dispatch_queue_len(), 0);
+ delete (*it);
+ }
+ available_servers.clear();
+
+ for (set<Messenger*>::iterator it = available_clients.begin();
+ it != available_clients.end(); ++it) {
+ (*it)->shutdown();
+ (*it)->wait();
+ ASSERT_EQ((*it)->get_dispatch_queue_len(), 0);
+ delete (*it);
+ }
+ available_clients.clear();
+ }
+
+ void handle_reset(Connection *con) {
+ std::lock_guard l{lock};
+ available_connections.erase(con);
+ dispatcher.clear_pending(con);
+ }
+};
+
+bool SyntheticDispatcher::ms_handle_reset(Connection *con) {
+ workload->handle_reset(con);
+ return true;
+}
+
+TEST_P(MessengerTest, SyntheticStressTest) {
+ SyntheticWorkload test_msg(8, 32, GetParam(), 100,
+ Messenger::Policy::stateful_server(0),
+ Messenger::Policy::lossless_client(0));
+ for (int i = 0; i < 100; ++i) {
+ if (!(i % 10)) lderr(g_ceph_context) << "seeding connection " << i << dendl;
+ test_msg.generate_connection();
+ }
+ gen_type rng(time(NULL));
+ for (int i = 0; i < 5000; ++i) {
+ if (!(i % 10)) {
+ lderr(g_ceph_context) << "Op " << i << ": " << dendl;
+ test_msg.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val > 90) {
+ test_msg.generate_connection();
+ } else if (val > 80) {
+ test_msg.drop_connection();
+ } else if (val > 10) {
+ test_msg.send_message();
+ } else {
+ usleep(rand() % 1000 + 500);
+ }
+ }
+ test_msg.wait_for_done();
+}
+
+TEST_P(MessengerTest, SyntheticStressTest1) {
+ SyntheticWorkload test_msg(16, 32, GetParam(), 100,
+ Messenger::Policy::lossless_peer_reuse(0),
+ Messenger::Policy::lossless_peer_reuse(0));
+ for (int i = 0; i < 10; ++i) {
+ if (!(i % 10)) lderr(g_ceph_context) << "seeding connection " << i << dendl;
+ test_msg.generate_connection();
+ }
+ gen_type rng(time(NULL));
+ for (int i = 0; i < 10000; ++i) {
+ if (!(i % 10)) {
+ lderr(g_ceph_context) << "Op " << i << ": " << dendl;
+ test_msg.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val > 80) {
+ test_msg.generate_connection();
+ } else if (val > 60) {
+ test_msg.drop_connection();
+ } else if (val > 10) {
+ test_msg.send_message();
+ } else {
+ usleep(rand() % 1000 + 500);
+ }
+ }
+ test_msg.wait_for_done();
+}
+
+
+TEST_P(MessengerTest, SyntheticInjectTest) {
+ uint64_t dispatch_throttle_bytes = g_ceph_context->_conf->ms_dispatch_throttle_bytes;
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "30");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0.1");
+ g_ceph_context->_conf.set_val("ms_dispatch_throttle_bytes", "16777216");
+ SyntheticWorkload test_msg(8, 32, GetParam(), 100,
+ Messenger::Policy::stateful_server(0),
+ Messenger::Policy::lossless_client(0));
+ for (int i = 0; i < 100; ++i) {
+ if (!(i % 10)) lderr(g_ceph_context) << "seeding connection " << i << dendl;
+ test_msg.generate_connection();
+ }
+ gen_type rng(time(NULL));
+ for (int i = 0; i < 1000; ++i) {
+ if (!(i % 10)) {
+ lderr(g_ceph_context) << "Op " << i << ": " << dendl;
+ test_msg.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val > 90) {
+ test_msg.generate_connection();
+ } else if (val > 80) {
+ test_msg.drop_connection();
+ } else if (val > 10) {
+ test_msg.send_message();
+ } else {
+ usleep(rand() % 500 + 100);
+ }
+ }
+ test_msg.wait_for_done();
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "0");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0");
+ g_ceph_context->_conf.set_val(
+ "ms_dispatch_throttle_bytes", std::to_string(dispatch_throttle_bytes));
+}
+
+TEST_P(MessengerTest, SyntheticInjectTest2) {
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "30");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0.1");
+ SyntheticWorkload test_msg(8, 16, GetParam(), 100,
+ Messenger::Policy::lossless_peer_reuse(0),
+ Messenger::Policy::lossless_peer_reuse(0));
+ for (int i = 0; i < 100; ++i) {
+ if (!(i % 10)) lderr(g_ceph_context) << "seeding connection " << i << dendl;
+ test_msg.generate_connection();
+ }
+ gen_type rng(time(NULL));
+ for (int i = 0; i < 1000; ++i) {
+ if (!(i % 10)) {
+ lderr(g_ceph_context) << "Op " << i << ": " << dendl;
+ test_msg.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val > 90) {
+ test_msg.generate_connection();
+ } else if (val > 80) {
+ test_msg.drop_connection();
+ } else if (val > 10) {
+ test_msg.send_message();
+ } else {
+ usleep(rand() % 500 + 100);
+ }
+ }
+ test_msg.wait_for_done();
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "0");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0");
+}
+
+TEST_P(MessengerTest, SyntheticInjectTest3) {
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "600");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0.1");
+ SyntheticWorkload test_msg(8, 16, GetParam(), 100,
+ Messenger::Policy::stateless_server(0),
+ Messenger::Policy::lossy_client(0));
+ for (int i = 0; i < 100; ++i) {
+ if (!(i % 10)) lderr(g_ceph_context) << "seeding connection " << i << dendl;
+ test_msg.generate_connection();
+ }
+ gen_type rng(time(NULL));
+ for (int i = 0; i < 1000; ++i) {
+ if (!(i % 10)) {
+ lderr(g_ceph_context) << "Op " << i << ": " << dendl;
+ test_msg.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val > 90) {
+ test_msg.generate_connection();
+ } else if (val > 80) {
+ test_msg.drop_connection();
+ } else if (val > 10) {
+ test_msg.send_message();
+ } else {
+ usleep(rand() % 500 + 100);
+ }
+ }
+ test_msg.wait_for_done();
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "0");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0");
+}
+
+
+TEST_P(MessengerTest, SyntheticInjectTest4) {
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "30");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0.1");
+ g_ceph_context->_conf.set_val("ms_inject_delay_probability", "1");
+ g_ceph_context->_conf.set_val("ms_inject_delay_type", "client osd");
+ g_ceph_context->_conf.set_val("ms_inject_delay_max", "5");
+ SyntheticWorkload test_msg(16, 32, GetParam(), 100,
+ Messenger::Policy::lossless_peer(0),
+ Messenger::Policy::lossless_peer(0));
+ for (int i = 0; i < 100; ++i) {
+ if (!(i % 10)) lderr(g_ceph_context) << "seeding connection " << i << dendl;
+ test_msg.generate_connection();
+ }
+ gen_type rng(time(NULL));
+ for (int i = 0; i < 1000; ++i) {
+ if (!(i % 10)) {
+ lderr(g_ceph_context) << "Op " << i << ": " << dendl;
+ test_msg.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val > 95) {
+ test_msg.generate_connection();
+ } else if (val > 80) {
+ // test_msg.drop_connection();
+ } else if (val > 10) {
+ test_msg.send_message();
+ } else {
+ usleep(rand() % 500 + 100);
+ }
+ }
+ test_msg.wait_for_done();
+ g_ceph_context->_conf.set_val("ms_inject_socket_failures", "0");
+ g_ceph_context->_conf.set_val("ms_inject_internal_delays", "0");
+ g_ceph_context->_conf.set_val("ms_inject_delay_probability", "0");
+ g_ceph_context->_conf.set_val("ms_inject_delay_type", "");
+ g_ceph_context->_conf.set_val("ms_inject_delay_max", "0");
+}
+
+// This is test for network block, means ::send return EAGAIN
+TEST_P(MessengerTest, SyntheticInjectTest5) {
+ SyntheticWorkload test_msg(1, 8, GetParam(), 100,
+ Messenger::Policy::stateful_server(0),
+ Messenger::Policy::lossless_client(0),
+ 64, 2);
+ bool simulate_network_congestion = true;
+ for (int i = 0; i < 2; ++i)
+ test_msg.generate_connection();
+ for (int i = 0; i < 5000; ++i) {
+ if (!(i % 10)) {
+ ldout(g_ceph_context, 0) << "Op " << i << ": " << dendl;
+ test_msg.print_internal_state();
+ }
+ if (i < 1600) {
+ // means that we would stuck 1600 * 6M (9.6G) around with 2 connections
+ test_msg.send_large_message(simulate_network_congestion);
+ } else {
+ simulate_network_congestion = false;
+ test_msg.send_large_message(simulate_network_congestion);
+ }
+ }
+ test_msg.wait_for_done();
+}
+
+
+class MarkdownDispatcher : public Dispatcher {
+ ceph::mutex lock = ceph::make_mutex("MarkdownDispatcher::lock");
+ set<ConnectionRef> conns;
+ bool last_mark;
+ public:
+ std::atomic<uint64_t> count = { 0 };
+ explicit MarkdownDispatcher(bool s): Dispatcher(g_ceph_context),
+ last_mark(false) {
+ }
+ bool ms_can_fast_dispatch_any() const override { return false; }
+ bool ms_can_fast_dispatch(const Message *m) const override {
+ switch (m->get_type()) {
+ case CEPH_MSG_PING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void ms_handle_fast_connect(Connection *con) override {
+ lderr(g_ceph_context) << __func__ << " " << con << dendl;
+ std::lock_guard l{lock};
+ conns.insert(con);
+ }
+ void ms_handle_fast_accept(Connection *con) override {
+ std::lock_guard l{lock};
+ conns.insert(con);
+ }
+ bool ms_dispatch(Message *m) override {
+ lderr(g_ceph_context) << __func__ << " conn: " << m->get_connection() << dendl;
+ std::lock_guard l{lock};
+ count++;
+ conns.insert(m->get_connection());
+ if (conns.size() < 2 && !last_mark) {
+ m->put();
+ return true;
+ }
+
+ last_mark = true;
+ usleep(rand() % 500);
+ for (set<ConnectionRef>::iterator it = conns.begin(); it != conns.end(); ++it) {
+ if ((*it) != m->get_connection().get()) {
+ (*it)->mark_down();
+ conns.erase(it);
+ break;
+ }
+ }
+ if (conns.empty())
+ last_mark = false;
+ m->put();
+ return true;
+ }
+ bool ms_handle_reset(Connection *con) override {
+ lderr(g_ceph_context) << __func__ << " " << con << dendl;
+ std::lock_guard l{lock};
+ conns.erase(con);
+ usleep(rand() % 500);
+ return true;
+ }
+ void ms_handle_remote_reset(Connection *con) override {
+ std::lock_guard l{lock};
+ conns.erase(con);
+ lderr(g_ceph_context) << __func__ << " " << con << dendl;
+ }
+ bool ms_handle_refused(Connection *con) override {
+ return false;
+ }
+ void ms_fast_dispatch(Message *m) override {
+ ceph_abort();
+ }
+ int ms_handle_fast_authentication(Connection *con) override {
+ return 1;
+ }
+};
+
+
+// Markdown with external lock
+TEST_P(MessengerTest, MarkdownTest) {
+ Messenger *server_msgr2 = Messenger::create(g_ceph_context, string(GetParam()), entity_name_t::OSD(0), "server", getpid());
+ MarkdownDispatcher cli_dispatcher(false), srv_dispatcher(true);
+ DummyAuthClientServer dummy_auth(g_ceph_context);
+ dummy_auth.auth_registry.refresh_config();
+ entity_addr_t bind_addr;
+ bind_addr.parse("v2:127.0.0.1:16800");
+ server_msgr->bind(bind_addr);
+ server_msgr->add_dispatcher_head(&srv_dispatcher);
+ server_msgr->set_auth_client(&dummy_auth);
+ server_msgr->set_auth_server(&dummy_auth);
+ server_msgr->start();
+ bind_addr.parse("v2:127.0.0.1:16801");
+ server_msgr2->bind(bind_addr);
+ server_msgr2->add_dispatcher_head(&srv_dispatcher);
+ server_msgr2->set_auth_client(&dummy_auth);
+ server_msgr2->set_auth_server(&dummy_auth);
+ server_msgr2->start();
+
+ client_msgr->add_dispatcher_head(&cli_dispatcher);
+ client_msgr->set_auth_client(&dummy_auth);
+ client_msgr->set_auth_server(&dummy_auth);
+ client_msgr->start();
+
+ int i = 1000;
+ uint64_t last = 0;
+ bool equal = false;
+ uint64_t equal_count = 0;
+ while (i--) {
+ ConnectionRef conn1 = client_msgr->connect_to(server_msgr->get_mytype(),
+ server_msgr->get_myaddrs());
+ ConnectionRef conn2 = client_msgr->connect_to(server_msgr2->get_mytype(),
+ server_msgr2->get_myaddrs());
+ MPing *m = new MPing();
+ ASSERT_EQ(conn1->send_message(m), 0);
+ m = new MPing();
+ ASSERT_EQ(conn2->send_message(m), 0);
+ CHECK_AND_WAIT_TRUE(srv_dispatcher.count > last + 1);
+ if (srv_dispatcher.count == last) {
+ lderr(g_ceph_context) << __func__ << " last is " << last << dendl;
+ equal = true;
+ equal_count++;
+ } else {
+ equal = false;
+ equal_count = 0;
+ }
+ last = srv_dispatcher.count;
+ if (equal_count)
+ usleep(1000*500);
+ ASSERT_FALSE(equal && equal_count > 3);
+ }
+ server_msgr->shutdown();
+ client_msgr->shutdown();
+ server_msgr2->shutdown();
+ server_msgr->wait();
+ client_msgr->wait();
+ server_msgr2->wait();
+ delete server_msgr2;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Messenger,
+ MessengerTest,
+ ::testing::Values(
+ "async+posix"
+ )
+);
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ g_ceph_context->_conf.set_val("auth_cluster_required", "none");
+ g_ceph_context->_conf.set_val("auth_service_required", "none");
+ g_ceph_context->_conf.set_val("auth_client_required", "none");
+ g_ceph_context->_conf.set_val("keyring", "/dev/null");
+ g_ceph_context->_conf.set_val("enable_experimental_unrecoverable_data_corrupting_features", "ms-type-async");
+ g_ceph_context->_conf.set_val("ms_die_on_bad_msg", "true");
+ g_ceph_context->_conf.set_val("ms_die_on_old_message", "true");
+ g_ceph_context->_conf.set_val("ms_max_backoff", "1");
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 ceph_test_msgr && valgrind --tool=memcheck ./ceph_test_msgr"
+ * End:
+ */
diff --git a/src/test/msgr/test_userspace_event.cc b/src/test/msgr/test_userspace_event.cc
new file mode 100644
index 000000000..067d81545
--- /dev/null
+++ b/src/test/msgr/test_userspace_event.cc
@@ -0,0 +1,174 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 XSky <haomai@xsky.com>
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <map>
+#include <random>
+#include <gtest/gtest.h>
+
+#include "msg/async/dpdk/UserspaceEvent.h"
+#include "global/global_context.h"
+
+class UserspaceManagerTest : public ::testing::Test {
+ public:
+ UserspaceEventManager *manager;
+
+ UserspaceManagerTest() {}
+ virtual void SetUp() {
+ manager = new UserspaceEventManager(g_ceph_context);
+ }
+ virtual void TearDown() {
+ delete manager;
+ }
+};
+
+TEST_F(UserspaceManagerTest, BasicTest) {
+ int events[10];
+ int masks[10];
+ int fd = manager->get_eventfd();
+ ASSERT_EQ(fd, 1);
+ ASSERT_EQ(0, manager->listen(fd, 1));
+ ASSERT_EQ(0, manager->notify(fd, 1));
+ ASSERT_EQ(1, manager->poll(events, masks, 10, nullptr));
+ ASSERT_EQ(fd, events[0]);
+ ASSERT_EQ(1, masks[0]);
+ ASSERT_EQ(0, manager->notify(fd, 2));
+ ASSERT_EQ(0, manager->poll(events, masks, 10, nullptr));
+ ASSERT_EQ(0, manager->unlisten(fd, 1));
+ ASSERT_EQ(0, manager->notify(fd, 1));
+ ASSERT_EQ(0, manager->poll(events, masks, 10, nullptr));
+ manager->close(fd);
+ fd = manager->get_eventfd();
+ ASSERT_EQ(fd, 1);
+ ASSERT_EQ(0, manager->poll(events, masks, 10, nullptr));
+}
+
+TEST_F(UserspaceManagerTest, FailTest) {
+ int events[10];
+ int masks[10];
+ int fd = manager->get_eventfd();
+ ASSERT_EQ(fd, 1);
+ ASSERT_EQ(-ENOENT, manager->listen(fd+1, 1));
+ ASSERT_EQ(-ENOENT, manager->notify(fd+1, 1));
+ ASSERT_EQ(0, manager->poll(events, masks, 10, nullptr));
+ ASSERT_EQ(-ENOENT, manager->unlisten(fd+1, 1));
+ manager->close(fd);
+}
+
+TEST_F(UserspaceManagerTest, StressTest) {
+ std::vector<std::pair<int, int> > mappings;
+ int events[10];
+ int masks[10];
+ std::random_device rd;
+ std::default_random_engine rng(rd());
+ std::uniform_int_distribution<> dist(0, 100);
+
+ mappings.resize(1001);
+ mappings[0] = std::make_pair(-1, -1);
+ for (int i = 0; i < 1000; ++i) {
+ int fd = manager->get_eventfd();
+ ASSERT_TRUE(fd > 0);
+ mappings[fd] = std::make_pair(0, 0);
+ }
+ int r = 0;
+ int fd = manager->get_eventfd();
+ auto get_activate_count = [](std::vector<std::pair<int, int> > &m) {
+ std::vector<int> fds;
+ int mask = 0;
+ size_t idx = 0;
+ for (auto &&p : m) {
+ mask = p.first & p.second;
+ if (p.first != -1 && mask) {
+ p.second &= (~mask);
+ fds.push_back(idx);
+ std::cerr << " activate " << idx << " mask " << mask << std::endl;
+ }
+ ++idx;
+ }
+ return fds;
+ };
+ for (int i = 0; i < 10000; ++i) {
+ int value = dist(rng);
+ fd = dist(rng) % mappings.size();
+ auto &p = mappings[fd];
+ int mask = dist(rng) % 2 + 1;
+ if (value > 55) {
+ r = manager->notify(fd, mask);
+ if (p.first == -1) {
+ ASSERT_EQ(p.second, -1);
+ ASSERT_EQ(r, -ENOENT);
+ } else {
+ p.second |= mask;
+ ASSERT_EQ(r, 0);
+ }
+ std::cerr << " notify fd " << fd << " mask " << mask << " r " << r << std::endl;
+ } else if (value > 45) {
+ r = manager->listen(fd, mask);
+ std::cerr << " listen fd " << fd << " mask " << mask << " r " << r << std::endl;
+ if (p.first == -1) {
+ ASSERT_EQ(p.second, -1);
+ ASSERT_EQ(r, -ENOENT);
+ } else {
+ p.first |= mask;
+ ASSERT_EQ(r, 0);
+ }
+ } else if (value > 35) {
+ r = manager->unlisten(fd, mask);
+ std::cerr << " unlisten fd " << fd << " mask " << mask << " r " << r << std::endl;
+ if (p.first == -1) {
+ ASSERT_EQ(p.second, -1);
+ ASSERT_EQ(r, -ENOENT);
+ } else {
+ p.first &= ~mask;
+ ASSERT_EQ(r, 0);
+ }
+ } else if (value > 20) {
+ std::set<int> actual, expected;
+ do {
+ r = manager->poll(events, masks, 3, nullptr);
+ std::cerr << " poll " << r;
+ for (int k = 0; k < r; ++k) {
+ std::cerr << events[k] << " ";
+ actual.insert(events[k]);
+ }
+ } while (r == 3);
+ std::cerr << std::endl;
+ auto fds = get_activate_count(mappings);
+ for (auto &&d : fds)
+ expected.insert(d);
+ ASSERT_EQ(expected, actual);
+ } else if (value > 10) {
+ r = manager->get_eventfd();
+ std::cerr << " open fd " << r << std::endl;
+ ASSERT_TRUE(r > 0);
+ if ((size_t)r >= mappings.size())
+ mappings.resize(r+1);
+ mappings[r] = std::make_pair(0, 0);
+ } else {
+ manager->close(fd);
+ std::cerr << " close fd " << fd << std::endl;
+ mappings[fd] = std::make_pair(-1, -1);
+ }
+ ASSERT_TRUE(manager->check());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make ceph_test_userspace_event &&
+ * ./ceph_test_userspace_event.cc
+ *
+ * End:
+ */
diff --git a/src/test/multi_stress_watch.cc b/src/test/multi_stress_watch.cc
new file mode 100644
index 000000000..1c3eb4dd2
--- /dev/null
+++ b/src/test/multi_stress_watch.cc
@@ -0,0 +1,169 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "test/librados/test_cxx.h"
+
+#include <errno.h>
+
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <map>
+#include <iostream>
+#include <string>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace librados;
+using std::map;
+using std::string;
+
+class WatchNotifyTestCtx : public WatchCtx
+{
+public:
+ WatchNotifyTestCtx(std::mutex &lock)
+ : lock{lock} {}
+ void notify(uint8_t opcode, uint64_t ver, bufferlist &bl) override {
+ std::unique_lock locker {lock};
+ notified = true;
+ cond.notify_one();
+ }
+ bool wait() {
+ std::unique_lock locker {lock};
+ return cond.wait_for(locker, std::chrono::seconds(1200),
+ [this] { return notified; });
+ }
+
+private:
+ bool notified = false;
+ std::mutex& lock;
+ std::condition_variable cond;
+};
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+void
+test_loop(Rados &cluster, std::string pool_name, std::string obj_name)
+{
+ int ret;
+ IoCtx ioctx;
+ ret = cluster.ioctx_create(pool_name.c_str(), ioctx);
+ if (ret < 0) {
+ std::cerr << "ioctx_create " << pool_name << " failed with " << ret << std::endl;
+ exit(1);
+ }
+ ioctx.application_enable("rados", true);
+
+ ret = ioctx.create(obj_name, false);
+ if (ret < 0) {
+ std::cerr << "create failed with " << ret << std::endl;
+ exit(1);
+ }
+
+ std::mutex lock;
+ constexpr int NR_ITERATIONS = 10000;
+ for (int i = 0; i < NR_ITERATIONS; ++i) {
+ std::cout << "Iteration " << i << std::endl;
+ uint64_t handle;
+ WatchNotifyTestCtx ctx{lock};
+ ret = ioctx.watch(obj_name, 0, &handle, &ctx);
+ ceph_assert(!ret);
+ bufferlist bl2;
+ ret = ioctx.notify(obj_name, 0, bl2);
+ ceph_assert(!ret);
+ ceph_assert_always(ctx.wait());
+ ioctx.unwatch(obj_name, handle);
+ }
+ ioctx.close();
+ ret = cluster.pool_delete(pool_name.c_str());
+ if (ret < 0) {
+ std::cerr << "pool_delete failed with " << ret << std::endl;
+ exit(1);
+ }
+}
+
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"
+
+void
+test_replicated(Rados &cluster, std::string pool_name, const std::string &obj_name)
+{
+ // May already exist
+ cluster.pool_create(pool_name.c_str());
+
+ test_loop(cluster, pool_name, obj_name);
+}
+
+void
+test_erasure(Rados &cluster, const std::string &pool_name, const std::string &obj_name)
+{
+ string outs;
+ bufferlist inbl;
+ int ret;
+ ret = cluster.mon_command(
+ "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile\", \"profile\": [ \"k=2\", \"m=1\", \"crush-failure-domain=osd\"]}",
+ inbl, NULL, &outs);
+ if (ret < 0) {
+ std::cerr << "mon_command erasure-code-profile set failed with " << ret << std::endl;
+ exit(1);
+ }
+ //std::cout << outs << std::endl;
+
+ outs.clear();
+ ret = cluster.mon_command(
+ "{\"prefix\": \"osd pool create\", \"pool\": \"" + pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":12, \"pgp_num\":12, \"erasure_code_profile\":\"testprofile\"}",
+ inbl, NULL, &outs);
+ if (ret < 0) {
+ std::cerr << outs << std::endl;
+ std::cerr << "mon_command create pool failed with " << ret << std::endl;
+ exit(1);
+ }
+ //std::cout << outs << std::endl;
+
+ cluster.wait_for_latest_osdmap();
+ test_loop(cluster, pool_name, obj_name);
+ return;
+}
+
+int main(int args, char **argv)
+{
+ if (args != 3 && args != 4) {
+ std::cerr << "Error: " << argv[0] << " [ec|rep] pool_name obj_name" << std::endl;
+ return 1;
+ }
+
+ std::string pool_name, obj_name, type;
+ // For backward compatibility with unmodified teuthology version
+ if (args == 3) {
+ type = "rep";
+ pool_name = argv[1];
+ obj_name = argv[2];
+ } else {
+ type = argv[1];
+ pool_name = argv[2];
+ obj_name = argv[3];
+ }
+ std::cout << "Test type " << type << std::endl;
+ std::cout << "pool_name, obj_name are " << pool_name << ", " << obj_name << std::endl;
+
+ if (type != "ec" && type != "rep") {
+ std::cerr << "Error: " << argv[0] << " Invalid arg must be 'ec' or 'rep' saw " << type << std::endl;
+ return 1;
+ }
+
+ Rados cluster;
+ std::string err = connect_cluster_pp(cluster);
+ if (err.length()) {
+ std::cerr << "Error " << err << std::endl;
+ return 1;
+ }
+
+ if (type == "rep")
+ test_replicated(cluster, pool_name, obj_name);
+ else if (type == "ec")
+ test_erasure(cluster, pool_name, obj_name);
+
+ return 0;
+}
diff --git a/src/test/neorados/CMakeLists.txt b/src/test/neorados/CMakeLists.txt
new file mode 100644
index 000000000..31e79a661
--- /dev/null
+++ b/src/test/neorados/CMakeLists.txt
@@ -0,0 +1,28 @@
+
+add_executable(ceph_test_neorados test_neorados.cc)
+target_link_libraries(ceph_test_neorados global libneorados
+ ${unittest_libs}
+ radostest
+ radostest-cxx
+ librados
+ GTest::GTest)
+
+add_executable(ceph_test_neorados_start_stop start_stop.cc)
+target_link_libraries(ceph_test_neorados_start_stop global libneorados
+ ${unittest_libs})
+
+add_executable(ceph_test_neorados_completions completions.cc)
+target_link_libraries(ceph_test_neorados_completions Boost::system pthread
+ ${unittest_libs})
+
+add_executable(ceph_test_neorados_op_speed op_speed.cc)
+target_link_libraries(ceph_test_neorados_op_speed
+ libneorados fmt::fmt ${unittest_libs})
+
+add_library(neoradostest-support STATIC common_tests.cc)
+target_link_libraries(neoradostest-support
+ libneorados fmt::fmt)
+
+add_executable(ceph_test_neorados_list_pool list_pool.cc)
+target_link_libraries(ceph_test_neorados_list_pool
+ libneorados neoradostest-support global fmt::fmt ${unittest_libs})
diff --git a/src/test/neorados/common_tests.cc b/src/test/neorados/common_tests.cc
new file mode 100644
index 000000000..4e4b6c0af
--- /dev/null
+++ b/src/test/neorados/common_tests.cc
@@ -0,0 +1,34 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ * Author: Adam C. Emerson <aemerson@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include <cstring>
+#include <string>
+#include <string_view>
+
+#include <boost/asio/ip/host_name.hpp>
+
+#include <fmt/format.h>
+
+#include "common_tests.h"
+#include "include/neorados/RADOS.hpp"
+
+namespace ba = boost::asio;
+namespace R = neorados;
+
+std::string get_temp_pool_name(std::string_view prefix)
+{
+ static auto hostname = ba::ip::host_name();
+ static auto num = 1ull;
+ return fmt::format("{}{}-{}-{}", prefix, hostname, getpid(), num++);
+}
diff --git a/src/test/neorados/common_tests.h b/src/test/neorados/common_tests.h
new file mode 100644
index 000000000..ca3d7bf7f
--- /dev/null
+++ b/src/test/neorados/common_tests.h
@@ -0,0 +1,41 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ * Author: Adam C. Emerson <aemerson@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include <string>
+#include <string_view>
+
+#include "include/neorados/RADOS.hpp"
+
+std::string get_temp_pool_name(std::string_view prefix = {});
+
+template<typename CompletionToken>
+auto create_pool(neorados::RADOS& r, std::string_view pname,
+ CompletionToken&& token)
+{
+ boost::asio::async_completion<CompletionToken,
+ void(boost::system::error_code,
+ std::int64_t)> init(token);
+ r.create_pool(pname, std::nullopt,
+ [&r, pname = std::string(pname),
+ h = std::move(init.completion_handler)]
+ (boost::system::error_code ec) mutable {
+ r.lookup_pool(
+ pname,
+ [h = std::move(h)]
+ (boost::system::error_code ec, std::int64_t pool) mutable {
+ std::move(h)(ec, pool);
+ });
+ });
+ return init.result.get();
+}
diff --git a/src/test/neorados/completions.cc b/src/test/neorados/completions.cc
new file mode 100644
index 000000000..d9c0e0870
--- /dev/null
+++ b/src/test/neorados/completions.cc
@@ -0,0 +1,20 @@
+#include <cassert>
+#include <boost/asio.hpp>
+#include <boost/system/system_error.hpp>
+
+constexpr int max_completions = 10'000'000;
+int completed = 0;
+
+boost::asio::io_context c;
+
+void nested_cb() {
+ if (++completed < max_completions)
+ c.post(&nested_cb);
+}
+
+int main(void) {
+ c.post(&nested_cb);
+ c.run();
+ assert(completed == max_completions);
+ return 0;
+}
diff --git a/src/test/neorados/list_pool.cc b/src/test/neorados/list_pool.cc
new file mode 100644
index 000000000..ae36c36e6
--- /dev/null
+++ b/src/test/neorados/list_pool.cc
@@ -0,0 +1,166 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat <contact@redhat.com>
+ * Author: Adam C. Emerson <aemerson@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <initializer_list>
+#include <optional>
+#include <thread>
+#include <tuple>
+#include <string_view>
+#include <vector>
+
+#include <sys/param.h>
+
+#include <unistd.h>
+
+#include <boost/system/system_error.hpp>
+
+#include <fmt/format.h>
+
+#include "include/neorados/RADOS.hpp"
+
+#include "include/scope_guard.h"
+
+#include "common/async/context_pool.h"
+#include "common/ceph_time.h"
+#include "common/ceph_argparse.h"
+#include "common/async/blocked_completion.h"
+
+#include "global/global_init.h"
+
+#include "test/neorados/common_tests.h"
+
+
+namespace ba = boost::asio;
+namespace bs = boost::system;
+namespace ca = ceph::async;
+namespace R = neorados;
+
+std::string_view hostname() {
+ static char hostname[MAXHOSTNAMELEN] = { 0 };
+ static size_t len = 0;
+ if (!len) {
+ auto r = gethostname(hostname, sizeof(hostname));
+ if (r != 0) {
+ throw bs::system_error(
+ errno, bs::system_category());
+ }
+ len = std::strlen(hostname);
+ }
+ return {hostname, len};
+}
+
+std::string temp_pool_name(const std::string_view prefix)
+{
+ using namespace std::chrono;
+ static std::uint64_t num = 1;
+ return fmt::format(
+ "{}-{}-{}-{}-{}",
+ prefix,
+ hostname(),
+ getpid(),
+ duration_cast<milliseconds>(ceph::coarse_real_clock::now()
+ .time_since_epoch()).count(),
+ num++);
+}
+
+bs::error_code noisy_list(R::RADOS& r, int64_t p)
+{
+ auto b = R::Cursor::begin();
+ auto e = R::Cursor::end();
+
+ std::cout << "begin = " << b.to_str() << std::endl;
+ std::cout << "end = " << e.to_str() << std::endl;
+ try {
+ auto [v, next] = r.enumerate_objects(p, b, e, 1000, {}, ca::use_blocked,
+ R::all_nspaces);
+
+ std::cout << "Got " << v.size() << " entries." << std::endl;
+ std::cout << "next cursor = " << next.to_str() << std::endl;
+ std::cout << "next == end: " << (next == e) << std::endl;
+ std::cout << "Returned Objects: ";
+ std::cout << "[";
+ auto o = v.cbegin();
+ while (o != v.cend()) {
+ std::cout << *o;
+ if (++o != v.cend())
+ std::cout << " ";
+ }
+ std::cout << "]" << std::endl;
+ } catch (const bs::system_error& e) {
+ std::cerr << "RADOS::enumerate_objects: " << e.what() << std::endl;
+ return e.code();
+ }
+ return {};
+}
+
+bs::error_code create_several(R::RADOS& r, const R::IOContext& i,
+ std::initializer_list<std::string> l)
+{
+ for (const auto& o : l) try {
+ R::WriteOp op;
+ std::cout << "Creating " << o << std::endl;
+ ceph::bufferlist bl;
+ bl.append("My bologna has no name.");
+ op.write_full(std::move(bl));
+ r.execute(o, i, std::move(op), ca::use_blocked);
+ } catch (const bs::system_error& e) {
+ std::cerr << "RADOS::execute: " << e.what() << std::endl;
+ return e.code();
+ }
+ return {};
+}
+
+int main(int argc, char** argv)
+{
+ using namespace std::literals;
+
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(cct.get());
+
+ try {
+ ca::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p, ca::use_blocked);
+
+ auto pool_name = get_temp_pool_name("ceph_test_RADOS_list_pool"sv);
+ r.create_pool(pool_name, std::nullopt, ca::use_blocked);
+ auto pd = make_scope_guard(
+ [&pool_name, &r]() {
+ r.delete_pool(pool_name, ca::use_blocked);
+ });
+ auto pool = r.lookup_pool(pool_name, ca::use_blocked);
+ R::IOContext i(pool);
+
+ if (noisy_list(r, pool)) {
+ return 1;
+ }
+ if (create_several(r, i, {"meow", "woof", "squeak"})) {
+ return 1;
+ }
+ if (noisy_list(r, pool)) {
+ return 1;
+ }
+
+ } catch (const bs::system_error& e) {
+ std::cerr << "Error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/test/neorados/op_speed.cc b/src/test/neorados/op_speed.cc
new file mode 100644
index 000000000..587c345d4
--- /dev/null
+++ b/src/test/neorados/op_speed.cc
@@ -0,0 +1,34 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat <contact@redhat.com>
+ * Author: Adam C. Emerson <aemerson@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/neorados/RADOS.hpp"
+
+constexpr int to_create = 10'000'000;
+
+int main() {
+ for (int i = 0; i < to_create; ++i) {
+ neorados::ReadOp op;
+ bufferlist bl;
+ std::uint64_t sz;
+ ceph::real_time tm;
+ boost::container::flat_map<std::string, ceph::buffer::list> xattrs;
+ boost::container::flat_map<std::string, ceph::buffer::list> omap;
+ bool trunc;
+ op.read(0, 0, &bl);
+ op.stat(&sz, &tm);
+ op.get_xattrs(&xattrs);
+ op.get_omap_vals(std::nullopt, std::nullopt, 1000, &omap, &trunc);
+ }
+}
diff --git a/src/test/neorados/start_stop.cc b/src/test/neorados/start_stop.cc
new file mode 100644
index 000000000..4ea0ae564
--- /dev/null
+++ b/src/test/neorados/start_stop.cc
@@ -0,0 +1,174 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat <contact@redhat.com>
+ * Author: Adam C. Emerson <aemerson@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <thread>
+#include <vector>
+
+#include "include/neorados/RADOS.hpp"
+
+#include "common/async/context_pool.h"
+#include "common/ceph_argparse.h"
+
+#include "global/global_init.h"
+
+namespace R = neorados;
+
+
+int main(int argc, char** argv)
+{
+ using namespace std::literals;
+
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(cct.get());
+
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(30s);
+ }
+ std::this_thread::sleep_for(30s);
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(30s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(1s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(1s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(1s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(1s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(1s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(1s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(1s);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(500ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(500ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(50ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(50ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(50ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5ms);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5us);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5us);
+ }
+ {
+ ceph::async::io_context_pool p(1);
+ auto r = R::RADOS::make_with_cct(cct.get(), p,
+ boost::asio::use_future).get();
+ std::this_thread::sleep_for(5us);
+ }
+ return 0;
+}
diff --git a/src/test/neorados/test_neorados.cc b/src/test/neorados/test_neorados.cc
new file mode 100644
index 000000000..953e772e1
--- /dev/null
+++ b/src/test/neorados/test_neorados.cc
@@ -0,0 +1,47 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "include/neorados/RADOS.hpp"
+#include "common/async/blocked_completion.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+#include <iostream>
+
+namespace neorados {
+
+class TestNeoRADOS : public ::testing::Test {
+public:
+ TestNeoRADOS() {
+ }
+};
+
+TEST_F(TestNeoRADOS, MakeWithLibRADOS) {
+ librados::Rados paleo_rados;
+ auto result = connect_cluster_pp(paleo_rados);
+ ASSERT_EQ("", result);
+
+ auto rados = RADOS::make_with_librados(paleo_rados);
+
+ ReadOp op;
+ bufferlist bl;
+ op.read(0, 0, &bl);
+
+ // provide pool that doesn't exists -- just testing round-trip
+ ASSERT_THROW(
+ rados.execute({"dummy-obj"}, std::numeric_limits<int64_t>::max(),
+ std::move(op), nullptr, ceph::async::use_blocked),
+ boost::system::system_error);
+}
+
+} // namespace neorados
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ int seed = getpid();
+ std::cout << "seed " << seed << std::endl;
+ srand(seed);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/objectstore/Allocator_aging_fragmentation.cc b/src/test/objectstore/Allocator_aging_fragmentation.cc
new file mode 100755
index 000000000..220f8841b
--- /dev/null
+++ b/src/test/objectstore/Allocator_aging_fragmentation.cc
@@ -0,0 +1,463 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Bitmap allocator fragmentation benchmarks.
+ * Author: Adam Kupczyk, akupczyk@redhat.com
+ */
+#include <bit>
+#include <iostream>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+#include <boost/random/triangle_distribution.hpp>
+
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "global/global_init.h"
+#include "include/stringify.h"
+#include "include/Context.h"
+#include "os/bluestore/Allocator.h"
+
+#include <boost/random/uniform_int.hpp>
+
+typedef boost::mt11213b gen_type;
+
+#include "common/debug.h"
+#define dout_context cct
+#define dout_subsys ceph_subsys_
+
+struct Scenario {
+ uint64_t capacity;
+ uint64_t alloc_unit;
+ double high_mark;
+ double low_mark;
+ double leakness;
+ uint32_t repeats;
+};
+
+std::vector<Scenario> scenarios{
+ Scenario{512, 65536, 0.8, 0.6, 0.1, 3},
+ Scenario{512, 65536, 0.9, 0.7, 0.0, 3},
+ Scenario{512, 65536, 0.9, 0.7, 0.1, 3},
+ Scenario{512, 65536, 0.8, 0.6, 0.5, 3},
+ Scenario{512, 65536, 0.9, 0.7, 0.5, 3},
+ Scenario{1024, 65536, 0.8, 0.6, 0.1, 3},
+ Scenario{1024, 65536, 0.9, 0.7, 0.0, 3},
+ Scenario{1024, 65536, 0.9, 0.7, 0.1, 3},
+ Scenario{1024*2, 65536, 0.8, 0.6, 0.3, 3},
+ Scenario{1024*2, 65536, 0.9, 0.7, 0.0, 3},
+ Scenario{1024*2, 65536, 0.9, 0.7, 0.3, 3},
+ Scenario{512, 65536/16, 0.8, 0.6, 0.1, 3},
+ Scenario{512, 65536/16, 0.9, 0.7, 0.0, 3},
+ Scenario{512, 65536/16, 0.9, 0.7, 0.1, 3},
+ Scenario{512, 65536/16, 0.8, 0.6, 0.5, 3},
+ Scenario{512, 65536/16, 0.9, 0.7, 0.5, 3},
+ Scenario{1024, 65536/16, 0.8, 0.6, 0.1, 3},
+ Scenario{1024, 65536/16, 0.9, 0.7, 0.0, 3},
+ Scenario{1024, 65536/16, 0.9, 0.7, 0.1, 3},
+ Scenario{1024*2, 65536/16, 0.8, 0.6, 0.3, 3},
+ Scenario{1024*2, 65536/16, 0.9, 0.7, 0.0, 3},
+ Scenario{1024*2, 65536/16, 0.9, 0.7, 0.3, 3}
+};
+
+void PrintTo(const Scenario& s, ::std::ostream* os)
+{
+ *os << "(capacity=" << s.capacity;
+ *os << "G, alloc_unit=" << s.alloc_unit;
+ *os << ", high_mark=" << s.high_mark;
+ *os << ", low_mark=" << s.low_mark;
+ *os << ", leakness=" << s.leakness;
+ *os << ", repeats=" << s.repeats << ")";
+}
+bool verbose = getenv("VERBOSE") != nullptr;
+
+class AllocTracker;
+class AllocTest : public ::testing::TestWithParam<std::string> {
+protected:
+ boost::scoped_ptr<AllocTracker> at;
+ gen_type rng;
+ static boost::intrusive_ptr<CephContext> cct;
+
+public:
+ boost::scoped_ptr<Allocator> alloc;
+ AllocTest(): alloc(nullptr) {}
+ void init_alloc(const std::string& alloc_name, int64_t size, uint64_t min_alloc_size);
+ void init_close();
+ void doAgingTest(std::function<uint32_t()> size_generator,
+ const std::string& alloc_name, uint64_t capacity, uint32_t alloc_unit,
+ uint64_t high_mark, uint64_t low_mark, uint32_t iterations, double leak_factor = 0);
+
+ uint64_t capacity;
+ uint32_t alloc_unit;
+
+ uint64_t level = 0;
+ uint64_t allocs = 0;
+ uint64_t fragmented = 0;
+ uint64_t fragments = 0;
+ uint64_t total_fragments = 0;
+
+ void do_fill(uint64_t high_mark, std::function<uint32_t()> size_generator, double leak_factor = 0);
+ void do_free(uint64_t low_mark);
+ uint32_t free_random();
+
+ void TearDown() final;
+ static void SetUpTestSuite();
+ static void TearDownTestSuite();
+};
+
+struct test_result {
+ uint64_t tests_cnt = 0;
+ double fragmented_percent = 0;
+ double fragments_count = 0;
+ double time = 0;
+ double frag_score = 0;
+};
+
+std::map<std::string, test_result> results_per_allocator;
+
+const uint64_t _1m = 1024 * 1024;
+const uint64_t _1G = 1024 * 1024 * 1024;
+
+const uint64_t _2m = 2 * 1024 * 1024;
+
+class AllocTracker
+{
+ std::vector<bluestore_pextent_t> allocations;
+ uint64_t size = 0;
+
+public:
+ bool push(uint64_t offs, uint32_t len)
+ {
+ assert(len != 0);
+ if (size + 1 > allocations.size())
+ allocations.resize(size + 100);
+ allocations[size++] = bluestore_pextent_t(offs, len);
+ return true;
+ }
+
+ bool pop_random(gen_type& rng, uint64_t* offs, uint32_t* len,
+ uint32_t max_len = 0)
+ {
+ if (size == 0)
+ return false;
+ uint64_t pos = rng() % size;
+ *len = allocations[pos].length;
+ *offs = allocations[pos].offset;
+
+ if (max_len && *len > max_len) {
+ allocations[pos].length = *len - max_len;
+ allocations[pos].offset = *offs + max_len;
+ *len = max_len;
+ } else {
+ allocations[pos] = allocations[size-1];
+ --size;
+ }
+ return true;
+ }
+};
+
+boost::intrusive_ptr<CephContext> AllocTest::cct;
+
+void AllocTest::init_alloc(const std::string& allocator_name, int64_t size, uint64_t min_alloc_size) {
+ this->capacity = size;
+ this->alloc_unit = min_alloc_size;
+ rng.seed(0);
+ alloc.reset(Allocator::create(cct.get(), allocator_name, size,
+ min_alloc_size));
+ at.reset(new AllocTracker());
+}
+
+void AllocTest::init_close() {
+ alloc.reset(0);
+ at.reset(nullptr);
+}
+
+uint32_t AllocTest::free_random() {
+ uint64_t o = 0;
+ uint32_t l = 0;
+ interval_set<uint64_t> release_set;
+ if (!at->pop_random(rng, &o, &l)) {
+ //empty?
+ return 0;
+ }
+ release_set.insert(o, l);
+ alloc->release(release_set);
+ level -= l;
+ return l;
+}
+
+
+void AllocTest::do_fill(uint64_t high_mark, std::function<uint32_t()> size_generator, double leak_factor) {
+ assert (leak_factor >= 0);
+ assert (leak_factor < 1);
+ uint32_t leak_level = leak_factor * std::numeric_limits<uint32_t>::max();
+ PExtentVector tmp;
+ while (level < high_mark)
+ {
+ uint32_t want = size_generator();
+ tmp.clear();
+ auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp);
+ if (r < want) {
+ break;
+ }
+ level += r;
+ for(auto a : tmp) {
+ bool full = !at->push(a.offset, a.length);
+ EXPECT_EQ(full, false);
+ }
+ allocs++;
+ if (tmp.size() > 1) {
+ fragmented ++;
+ total_fragments += r;
+ fragments += tmp.size();
+ }
+ if (leak_level > 0) {
+ for (size_t i=0; i<tmp.size(); i++) {
+ if (uint32_t(rng()) < leak_level) {
+ free_random();
+ }
+ }
+ }
+ }
+}
+
+void AllocTest::do_free(uint64_t low_mark) {
+ while (level > low_mark)
+ {
+ if (free_random() == 0)
+ break;
+ }
+}
+
+void AllocTest::doAgingTest(
+ std::function<uint32_t()> size_generator,
+ const std::string& allocator_name,
+ uint64_t capacity, uint32_t alloc_unit,
+ uint64_t high_mark, uint64_t low_mark, uint32_t iterations, double leak_factor)
+{
+ assert(std::has_single_bit(alloc_unit));
+ cct->_conf->bdev_block_size = alloc_unit;
+ PExtentVector allocated, tmp;
+ init_alloc(allocator_name, capacity, alloc_unit);
+ alloc->init_add_free(0, capacity);
+
+ utime_t start = ceph_clock_now();
+ level = 0;
+ allocs = 0;
+ fragmented = 0;
+ fragments = 0;
+ total_fragments = 0;
+ if (verbose) std::cout << "INITIAL FILL" << std::endl;
+ do_fill(high_mark, size_generator, leak_factor); //initial fill with data
+ if (verbose) std::cout << " fragmented allocs=" << 100.0 * fragmented / allocs << "%" <<
+ " #frags=" << ( fragmented != 0 ? double(fragments) / fragmented : 0 )<<
+ " time=" << (ceph_clock_now() - start) * 1000 << "ms" << std::endl;
+
+ for (uint32_t i=0; i < iterations; i++)
+ {
+ allocs = 0;
+ fragmented = 0;
+ fragments = 0;
+ total_fragments = 0;
+
+ uint64_t level_previous = level;
+ start = ceph_clock_now();
+ if (verbose) std::cout << "ADDING CAPACITY " << i + 1 << std::endl;
+ do_free(low_mark); //simulates adding new capacity to cluster
+ if (verbose) std::cout << " level change: " <<
+ double(level_previous) / capacity * 100 << "% -> " <<
+ double(level) / capacity * 100 << "% time=" <<
+ (ceph_clock_now() - start) * 1000 << "ms" << std::endl;
+
+ start = ceph_clock_now();
+ if (verbose) std::cout << "APPENDING " << i + 1 << std::endl;
+ do_fill(high_mark, size_generator, leak_factor); //only creating elements
+ if (verbose) std::cout << " fragmented allocs=" << 100.0 * fragmented / allocs << "%" <<
+ " #frags=" << ( fragmented != 0 ? double(fragments) / fragmented : 0 ) <<
+ " time=" << (ceph_clock_now() - start) * 1000 << "ms" << std::endl;
+ }
+ double frag_score = alloc->get_fragmentation_score();
+ do_free(0);
+ double free_frag_score = alloc->get_fragmentation_score();
+ ASSERT_EQ(alloc->get_free(), capacity);
+
+ std::cout << " fragmented allocs=" << 100.0 * fragmented / allocs << "%" <<
+ " #frags=" << ( fragmented != 0 ? double(fragments) / fragmented : 0 ) <<
+ " time=" << (ceph_clock_now() - start) * 1000 << "ms" <<
+ " frag.score=" << frag_score << " after free frag.score=" << free_frag_score << std::endl;
+
+ uint64_t sum = 0;
+ uint64_t cnt = 0;
+ auto list_free = [&](size_t off, size_t len) {
+ cnt++;
+ sum+=len;
+ };
+ alloc->dump(list_free);
+ ASSERT_EQ(sum, capacity);
+ if (verbose)
+ std::cout << "free chunks sum=" << sum << " free chunks count=" << cnt << std::endl;
+
+ //adding to totals
+ test_result &r = results_per_allocator[allocator_name];
+ r.tests_cnt ++;
+ r.fragmented_percent += 100.0 * fragmented / allocs;
+ r.fragments_count += ( fragmented != 0 ? double(fragments) / fragmented : 2 );
+ r.time += ceph_clock_now() - start;
+ r.frag_score += frag_score;
+}
+
+void AllocTest::SetUpTestSuite()
+{
+ vector<const char*> args;
+ cct = global_init(NULL, args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(cct.get());
+}
+
+void AllocTest::TearDown()
+{
+ at.reset();
+ alloc.reset();
+}
+
+void AllocTest::TearDownTestSuite()
+{
+ cct.reset();
+
+ std::cout << "Summary: " << std::endl;
+ for (auto& r: results_per_allocator) {
+ std::cout << r.first <<
+ " fragmented allocs=" << r.second.fragmented_percent / r.second.tests_cnt << "%" <<
+ " #frags=" << r.second.fragments_count / r.second.tests_cnt <<
+ " free_score=" << r.second.frag_score / r.second.tests_cnt <<
+ " time=" << r.second.time * 1000 << "ms" << std::endl;
+ }
+}
+
+
+TEST_P(AllocTest, test_alloc_triangle_0_8M_16M)
+{
+ std::string allocator_name = GetParam();
+ boost::triangle_distribution<double> D(1, (8 * 1024 * 1024) , (16 * 1024 * 1024) );
+ for (auto& s:scenarios) {
+ std::cout << "Allocator: " << allocator_name << ", ";
+ PrintTo(s, &std::cout);
+ std::cout << std::endl;
+
+ auto size_generator = [&]() -> uint32_t {
+ return (uint32_t(D(rng)) + s.alloc_unit) & ~(s.alloc_unit - 1);
+ };
+
+ doAgingTest(size_generator, allocator_name, s.capacity * _1G, s.alloc_unit,
+ s.high_mark * s.capacity * _1G,
+ s.low_mark * s.capacity * _1G,
+ s.repeats, s.leakness);
+ }
+}
+
+TEST_P(AllocTest, test_alloc_8M_and_64K)
+{
+ std::string allocator_name = GetParam();
+ constexpr uint32_t max_chunk_size = 8*1024*1024;
+ constexpr uint32_t min_chunk_size = 64*1024;
+ for (auto& s:scenarios) {
+ std::cout << "Allocator: " << allocator_name << ", ";
+ PrintTo(s, &std::cout);
+ std::cout << std::endl;
+ boost::uniform_int<> D(0, 1);
+
+ auto size_generator = [&]() -> uint32_t {
+ if (D(rng) == 0)
+ return max_chunk_size;
+ else
+ return min_chunk_size;
+ };
+
+ doAgingTest(size_generator, allocator_name, s.capacity * _1G, s.alloc_unit,
+ s.high_mark * s.capacity * _1G,
+ s.low_mark * s.capacity * _1G,
+ s.repeats, s.leakness);
+ }
+}
+
+TEST_P(AllocTest, test_alloc_fragmentation_max_chunk_8M)
+{
+ std::string allocator_name = GetParam();
+ constexpr uint32_t max_object_size = 150*1000*1000;
+ constexpr uint32_t max_chunk_size = 8*1024*1024;
+ for (auto& s:scenarios) {
+ std::cout << "Allocator: " << allocator_name << ", ";
+ PrintTo(s, &std::cout);
+ std::cout << std::endl;
+ boost::uniform_int<> D(1, max_object_size / s.alloc_unit);
+
+ uint32_t object_size = 0;
+
+ auto size_generator = [&]() -> uint32_t {
+ uint32_t c;
+ if (object_size == 0)
+ object_size = (uint32_t(D(rng))* s.alloc_unit);
+ if (object_size > max_chunk_size)
+ c = max_chunk_size;
+ else
+ c = object_size;
+ object_size -= c;
+ return c;
+ };
+
+ doAgingTest(size_generator, allocator_name, s.capacity * _1G, s.alloc_unit,
+ s.high_mark * s.capacity * _1G,
+ s.low_mark * s.capacity * _1G,
+ s.repeats, s.leakness);
+ }
+}
+
+TEST_P(AllocTest, test_bonus_empty_fragmented)
+{
+ uint64_t capacity = uint64_t(512) * 1024 * 1024 * 1024; //512 G
+ uint64_t alloc_unit = 64 * 1024;
+ std::string allocator_name = GetParam();
+ std::cout << "Allocator: " << allocator_name << std::endl;
+ init_alloc(allocator_name, capacity, alloc_unit);
+ alloc->init_add_free(0, capacity);
+ PExtentVector tmp;
+ for (size_t i = 0; i < capacity / (1024 * 1024); i++) {
+ tmp.clear();
+ uint32_t want = 1024 * 1024;
+ int r = alloc->allocate(want, alloc_unit, 0, 0, &tmp);
+ ASSERT_EQ(r, want);
+ if (tmp.size() > 1) {
+ interval_set<uint64_t> release_set;
+ for (auto& t: tmp) {
+ release_set.insert(t.offset, t.length);
+ }
+ alloc->release(release_set);
+ } else {
+ interval_set<uint64_t> release_set;
+ uint64_t offset = tmp[0].offset;
+ uint64_t length = tmp[0].length;
+
+ release_set.insert(offset + alloc_unit, length - 3 * alloc_unit);
+ alloc->release(release_set);
+ release_set.clear();
+
+ release_set.insert(offset , alloc_unit);
+ alloc->release(release_set);
+ release_set.clear();
+
+ release_set.insert(offset + length - 2 * alloc_unit, 2 * alloc_unit);
+ alloc->release(release_set);
+ release_set.clear();
+ }
+ }
+ double frag_score = alloc->get_fragmentation_score();
+ ASSERT_EQ(alloc->get_free(), capacity);
+ std::cout << " empty storage frag.score=" << frag_score << std::endl;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Allocator,
+ AllocTest,
+ ::testing::Values("stupid", "bitmap", "avl", "btree"));
diff --git a/src/test/objectstore/Allocator_bench.cc b/src/test/objectstore/Allocator_bench.cc
new file mode 100644
index 000000000..0d04a854e
--- /dev/null
+++ b/src/test/objectstore/Allocator_bench.cc
@@ -0,0 +1,368 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * In memory space allocator benchmarks.
+ * Author: Igor Fedotov, ifedotov@suse.com
+ */
+#include <iostream>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "include/stringify.h"
+#include "include/Context.h"
+#include "os/bluestore/Allocator.h"
+
+#include <boost/random/uniform_int.hpp>
+typedef boost::mt11213b gen_type;
+
+#include "common/debug.h"
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_
+
+using namespace std;
+
+class AllocTest : public ::testing::TestWithParam<const char*> {
+
+public:
+ boost::scoped_ptr<Allocator> alloc;
+ AllocTest(): alloc(0) { }
+ void init_alloc(int64_t size, uint64_t min_alloc_size) {
+ std::cout << "Creating alloc type " << string(GetParam()) << " \n";
+ alloc.reset(Allocator::create(g_ceph_context, GetParam(), size,
+ min_alloc_size));
+ }
+
+ void init_close() {
+ alloc.reset(0);
+ }
+ void doOverwriteTest(uint64_t capacity, uint64_t prefill,
+ uint64_t overwrite);
+};
+
+const uint64_t _1m = 1024 * 1024;
+
+void dump_mempools()
+{
+ ostringstream ostr;
+ Formatter* f = Formatter::create("json-pretty", "json-pretty", "json-pretty");
+ ostr << "Mempools: ";
+ f->open_object_section("mempools");
+ mempool::dump(f);
+ f->close_section();
+ f->flush(ostr);
+ delete f;
+ ldout(g_ceph_context, 0) << ostr.str() << dendl;
+}
+
+class AllocTracker
+{
+ std::vector<uint64_t> allocations;
+ uint64_t head = 0;
+ uint64_t tail = 0;
+ uint64_t size = 0;
+ boost::uniform_int<> u1;
+
+public:
+ AllocTracker(uint64_t capacity, uint64_t alloc_unit)
+ : u1(0, capacity)
+ {
+ ceph_assert(alloc_unit >= 0x100);
+ ceph_assert(capacity <= (uint64_t(1) << 48)); // we use 5 octets (bytes 1 - 5) to store
+ // offset to save the required space.
+ // This supports capacity up to 281 TB
+
+ allocations.resize(capacity / alloc_unit);
+ }
+ inline uint64_t get_head() const
+ {
+ return head;
+ }
+
+ inline uint64_t get_tail() const
+ {
+ return tail;
+ }
+
+ bool push(uint64_t offs, uint32_t len)
+ {
+ ceph_assert((len & 0xff) == 0);
+ ceph_assert((offs & 0xff) == 0);
+ ceph_assert((offs & 0xffff000000000000) == 0);
+
+ if (head + 1 == tail)
+ return false;
+ uint64_t val = (offs << 16) | (len >> 8);
+ allocations[head++] = val;
+ head %= allocations.size();
+ ++size;
+ return true;
+ }
+ bool pop(uint64_t* offs, uint32_t* len)
+ {
+ if (size == 0)
+ return false;
+ uint64_t val = allocations[tail++];
+ *len = uint64_t((val & 0xffffff) << 8);
+ *offs = (val >> 16) & ~uint64_t(0xff);
+ tail %= allocations.size();
+ --size;
+ return true;
+ }
+ bool pop_random(gen_type& rng, uint64_t* offs, uint32_t* len,
+ uint32_t max_len = 0)
+ {
+ if (size == 0)
+ return false;
+
+ uint64_t pos = (u1(rng) % size) + tail;
+ pos %= allocations.size();
+ uint64_t val = allocations[pos];
+ *len = uint64_t((val & 0xffffff) << 8);
+ *offs = (val >> 16) & ~uint64_t(0xff);
+ if (max_len && *len > max_len) {
+ val = ((*offs + max_len) << 16) | ((*len - max_len) >> 8);
+ allocations[pos] = val;
+ *len = max_len;
+ } else {
+ allocations[pos] = allocations[tail++];
+ tail %= allocations.size();
+ --size;
+ }
+ return true;
+ }
+};
+
+TEST_P(AllocTest, test_alloc_bench_seq)
+{
+ uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024;
+ uint64_t alloc_unit = 4096;
+ uint64_t want_size = alloc_unit;
+ PExtentVector allocated, tmp;
+
+ init_alloc(capacity, alloc_unit);
+ alloc->init_add_free(0, capacity);
+
+ utime_t start = ceph_clock_now();
+ for (uint64_t i = 0; i < capacity; i += want_size)
+ {
+ tmp.clear();
+ EXPECT_EQ(static_cast<int64_t>(want_size),
+ alloc->allocate(want_size, alloc_unit, 0, 0, &tmp));
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "alloc " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+
+ std::cout << "releasing..." << std::endl;
+ for (size_t i = 0; i < capacity; i += want_size)
+ {
+ interval_set<uint64_t> release_set;
+ release_set.insert(i, want_size);
+ alloc->release(release_set);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "release " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl;
+ dump_mempools();
+}
+
+TEST_P(AllocTest, test_alloc_bench)
+{
+ uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024;
+ uint64_t alloc_unit = 4096;
+ PExtentVector allocated, tmp;
+ AllocTracker at(capacity, alloc_unit);
+
+ init_alloc(capacity, alloc_unit);
+ alloc->init_add_free(0, capacity);
+
+ gen_type rng(time(NULL));
+ boost::uniform_int<> u1(0, 9); // 4K-2M
+ boost::uniform_int<> u2(0, 7); // 4K-512K
+
+ utime_t start = ceph_clock_now();
+ for (uint64_t i = 0; i < capacity * 2; )
+ {
+ uint32_t want = alloc_unit << u1(rng);
+
+ tmp.clear();
+ auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp);
+ if (r < want) {
+ break;
+ }
+ i += r;
+
+ for(auto a : tmp) {
+ bool full = !at.push(a.offset, a.length);
+ EXPECT_EQ(full, false);
+ }
+ uint64_t want_release = alloc_unit << u2(rng);
+ uint64_t released = 0;
+ do {
+ uint64_t o = 0;
+ uint32_t l = 0;
+ interval_set<uint64_t> release_set;
+ if (!at.pop_random(rng, &o, &l, want_release - released)) {
+ break;
+ }
+ release_set.insert(o, l);
+ alloc->release(release_set);
+ released += l;
+ } while (released < want_release);
+
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "alloc " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl;
+ std::cout<<"Avail "<< alloc->get_free() / _1m << " MB" << std::endl;
+ dump_mempools();
+}
+
+void AllocTest::doOverwriteTest(uint64_t capacity, uint64_t prefill,
+ uint64_t overwrite)
+{
+ uint64_t alloc_unit = 4096;
+ PExtentVector allocated, tmp;
+ AllocTracker at(capacity, alloc_unit);
+
+ init_alloc(capacity, alloc_unit);
+ alloc->init_add_free(0, capacity);
+
+ gen_type rng(time(NULL));
+ boost::uniform_int<> u1(0, 9); // 4K-2M
+ boost::uniform_int<> u2(0, 9); // 4K-512K
+
+ utime_t start = ceph_clock_now();
+ // allocate 90% of the capacity
+ auto cap = prefill;
+ for (uint64_t i = 0; i < cap; )
+ {
+ uint32_t want = alloc_unit << u1(rng);
+ tmp.clear();
+ auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp);
+ if (r < want) {
+ break;
+ }
+ i += r;
+
+ for(auto a : tmp) {
+ bool full = !at.push(a.offset, a.length);
+ EXPECT_EQ(full, false);
+ }
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "alloc " << i / 1024 / 1024 << " mb of "
+ << cap / 1024 / 1024 << std::endl;
+ }
+ }
+
+ cap = overwrite;
+ for (uint64_t i = 0; i < cap; )
+ {
+ uint64_t want_release = alloc_unit << u2(rng);
+ uint64_t released = 0;
+ do {
+ uint64_t o = 0;
+ uint32_t l = 0;
+ interval_set<uint64_t> release_set;
+ if (!at.pop_random(rng, &o, &l, want_release - released)) {
+ break;
+ }
+ release_set.insert(o, l);
+ alloc->release(release_set);
+ released += l;
+ } while (released < want_release);
+
+ uint32_t want = alloc_unit << u1(rng);
+ tmp.clear();
+ auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp);
+ if (r != want) {
+ std::cout<<"Can't allocate more space, stopping."<< std::endl;
+ break;
+ }
+ i += r;
+
+ for(auto a : tmp) {
+ bool full = !at.push(a.offset, a.length);
+ EXPECT_EQ(full, false);
+ }
+
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "reuse " << i / 1024 / 1024 << " mb of "
+ << cap / 1024 / 1024 << std::endl;
+ }
+ }
+ std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl;
+ std::cout<<"Avail "<< alloc->get_free() / _1m << " MB" << std::endl;
+
+ dump_mempools();
+}
+
+TEST_P(AllocTest, test_alloc_bench_90_300)
+{
+ uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024;
+ auto prefill = capacity - capacity / 10;
+ auto overwrite = capacity * 3;
+ doOverwriteTest(capacity, prefill, overwrite);
+}
+
+TEST_P(AllocTest, test_alloc_bench_50_300)
+{
+ uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024;
+ auto prefill = capacity / 2;
+ auto overwrite = capacity * 3;
+ doOverwriteTest(capacity, prefill, overwrite);
+}
+
+TEST_P(AllocTest, test_alloc_bench_10_300)
+{
+ uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024;
+ auto prefill = capacity / 10;
+ auto overwrite = capacity * 3;
+ doOverwriteTest(capacity, prefill, overwrite);
+}
+
+TEST_P(AllocTest, mempoolAccounting)
+{
+ uint64_t bytes = mempool::bluestore_alloc::allocated_bytes();
+ uint64_t items = mempool::bluestore_alloc::allocated_items();
+
+ uint64_t alloc_size = 4 * 1024;
+ uint64_t capacity = 512ll * 1024 * 1024 * 1024;
+ Allocator* alloc = Allocator::create(g_ceph_context, GetParam(),
+ capacity, alloc_size);
+ ASSERT_NE(alloc, nullptr);
+ alloc->init_add_free(0, capacity);
+
+ std::map<uint32_t, PExtentVector> all_allocs;
+ for (size_t i = 0; i < 10000; i++) {
+ PExtentVector tmp;
+ alloc->allocate(alloc_size, alloc_size, 0, 0, &tmp);
+ all_allocs[rand()] = tmp;
+ tmp.clear();
+ alloc->allocate(alloc_size, alloc_size, 0, 0, &tmp);
+ all_allocs[rand()] = tmp;
+ tmp.clear();
+
+ auto it = all_allocs.upper_bound(rand());
+ if (it != all_allocs.end()) {
+ alloc->release(it->second);
+ all_allocs.erase(it);
+ }
+ }
+
+ delete(alloc);
+ ASSERT_EQ(mempool::bluestore_alloc::allocated_bytes(), bytes);
+ ASSERT_EQ(mempool::bluestore_alloc::allocated_items(), items);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Allocator,
+ AllocTest,
+ ::testing::Values("stupid", "bitmap", "avl", "btree", "hybrid"));
diff --git a/src/test/objectstore/Allocator_test.cc b/src/test/objectstore/Allocator_test.cc
new file mode 100644
index 000000000..b00650015
--- /dev/null
+++ b/src/test/objectstore/Allocator_test.cc
@@ -0,0 +1,566 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * In memory space allocator test cases.
+ * Author: Ramesh Chander, Ramesh.Chander@sandisk.com
+ */
+#include <iostream>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "include/stringify.h"
+#include "include/Context.h"
+#include "os/bluestore/Allocator.h"
+
+using namespace std;
+
+typedef boost::mt11213b gen_type;
+
+class AllocTest : public ::testing::TestWithParam<const char*> {
+
+public:
+ boost::scoped_ptr<Allocator> alloc;
+ AllocTest(): alloc(0) { }
+ void init_alloc(int64_t size, uint64_t min_alloc_size) {
+ std::cout << "Creating alloc type " << string(GetParam()) << " \n";
+ alloc.reset(Allocator::create(g_ceph_context, GetParam(), size,
+ min_alloc_size,
+ 256*1048576, 100*256*1048576ull));
+ }
+
+ void init_close() {
+ alloc.reset(0);
+ }
+};
+
+TEST_P(AllocTest, test_alloc_init)
+{
+ int64_t blocks = 64;
+ init_alloc(blocks, 1);
+ ASSERT_EQ(0U, alloc->get_free());
+ alloc->shutdown();
+ blocks = 1024 * 2 + 16;
+ init_alloc(blocks, 1);
+ ASSERT_EQ(0U, alloc->get_free());
+ alloc->shutdown();
+ blocks = 1024 * 2;
+ init_alloc(blocks, 1);
+ ASSERT_EQ(alloc->get_free(), (uint64_t) 0);
+}
+
+TEST_P(AllocTest, test_init_add_free)
+{
+ int64_t block_size = 1024;
+ int64_t capacity = 4 * 1024 * block_size;
+
+ {
+ init_alloc(capacity, block_size);
+
+ auto free = alloc->get_free();
+ alloc->init_add_free(block_size, 0);
+ ASSERT_EQ(free, alloc->get_free());
+
+ alloc->init_rm_free(block_size, 0);
+ ASSERT_EQ(free, alloc->get_free());
+ }
+}
+
+TEST_P(AllocTest, test_alloc_min_alloc)
+{
+ int64_t block_size = 1024;
+ int64_t capacity = 4 * 1024 * block_size;
+
+ {
+ init_alloc(capacity, block_size);
+
+ alloc->init_add_free(block_size, block_size);
+ PExtentVector extents;
+ EXPECT_EQ(block_size, alloc->allocate(block_size, block_size,
+ 0, (int64_t) 0, &extents));
+ }
+
+ /*
+ * Allocate extent and make sure all comes in single extent.
+ */
+ {
+ init_alloc(capacity, block_size);
+ alloc->init_add_free(0, block_size * 4);
+ PExtentVector extents;
+ EXPECT_EQ(4*block_size,
+ alloc->allocate(4 * (uint64_t)block_size, (uint64_t) block_size,
+ 0, (int64_t) 0, &extents));
+ EXPECT_EQ(1u, extents.size());
+ EXPECT_EQ(extents[0].length, 4 * block_size);
+ }
+
+ /*
+ * Allocate extent and make sure we get two different extents.
+ */
+ {
+ init_alloc(capacity, block_size);
+ alloc->init_add_free(0, block_size * 2);
+ alloc->init_add_free(3 * block_size, block_size * 2);
+ PExtentVector extents;
+
+ EXPECT_EQ(4*block_size,
+ alloc->allocate(4 * (uint64_t)block_size, (uint64_t) block_size,
+ 0, (int64_t) 0, &extents));
+ EXPECT_EQ(2u, extents.size());
+ EXPECT_EQ(extents[0].length, 2 * block_size);
+ EXPECT_EQ(extents[1].length, 2 * block_size);
+ }
+ alloc->shutdown();
+}
+
+TEST_P(AllocTest, test_alloc_min_max_alloc)
+{
+ int64_t block_size = 1024;
+
+ int64_t capacity = 4 * 1024 * block_size;
+ init_alloc(capacity, block_size);
+
+ /*
+ * Make sure we get all extents different when
+ * min_alloc_size == max_alloc_size
+ */
+ {
+ init_alloc(capacity, block_size);
+ alloc->init_add_free(0, block_size * 4);
+ PExtentVector extents;
+ EXPECT_EQ(4*block_size,
+ alloc->allocate(4 * (uint64_t)block_size, (uint64_t) block_size,
+ block_size, (int64_t) 0, &extents));
+ for (auto e : extents) {
+ EXPECT_EQ(e.length, block_size);
+ }
+ EXPECT_EQ(4u, extents.size());
+ }
+
+
+ /*
+ * Make sure we get extents of length max_alloc size
+ * when max alloc size > min_alloc size
+ */
+ {
+ init_alloc(capacity, block_size);
+ alloc->init_add_free(0, block_size * 4);
+ PExtentVector extents;
+ EXPECT_EQ(4*block_size,
+ alloc->allocate(4 * (uint64_t)block_size, (uint64_t) block_size,
+ 2 * block_size, (int64_t) 0, &extents));
+ EXPECT_EQ(2u, extents.size());
+ for (auto& e : extents) {
+ EXPECT_EQ(e.length, block_size * 2);
+ }
+ }
+
+ /*
+ * Make sure allocations are of min_alloc_size when min_alloc_size > block_size.
+ */
+ {
+ init_alloc(capacity, block_size);
+ alloc->init_add_free(0, block_size * 1024);
+ PExtentVector extents;
+ EXPECT_EQ(1024 * block_size,
+ alloc->allocate(1024 * (uint64_t)block_size,
+ (uint64_t) block_size * 4,
+ block_size * 4, (int64_t) 0, &extents));
+ for (auto& e : extents) {
+ EXPECT_EQ(e.length, block_size * 4);
+ }
+ EXPECT_EQ(1024u/4, extents.size());
+ }
+
+ /*
+ * Allocate and free.
+ */
+ {
+ init_alloc(capacity, block_size);
+ alloc->init_add_free(0, block_size * 16);
+ PExtentVector extents;
+ EXPECT_EQ(16 * block_size,
+ alloc->allocate(16 * (uint64_t)block_size, (uint64_t) block_size,
+ 2 * block_size, (int64_t) 0, &extents));
+
+ EXPECT_EQ(extents.size(), 8u);
+ for (auto& e : extents) {
+ EXPECT_EQ(e.length, 2 * block_size);
+ }
+ }
+}
+
+TEST_P(AllocTest, test_alloc_failure)
+{
+ int64_t block_size = 1024;
+ int64_t capacity = 4 * 1024 * block_size;
+
+ {
+ init_alloc(capacity, block_size);
+ alloc->init_add_free(0, block_size * 256);
+ alloc->init_add_free(block_size * 512, block_size * 256);
+
+ PExtentVector extents;
+ EXPECT_EQ(512 * block_size,
+ alloc->allocate(512 * (uint64_t)block_size,
+ (uint64_t) block_size * 256,
+ block_size * 256, (int64_t) 0, &extents));
+ alloc->init_add_free(0, block_size * 256);
+ alloc->init_add_free(block_size * 512, block_size * 256);
+ extents.clear();
+ EXPECT_EQ(-ENOSPC,
+ alloc->allocate(512 * (uint64_t)block_size,
+ (uint64_t) block_size * 512,
+ block_size * 512, (int64_t) 0, &extents));
+ }
+}
+
+TEST_P(AllocTest, test_alloc_big)
+{
+ int64_t block_size = 4096;
+ int64_t blocks = 104857600;
+ int64_t mas = 4096;
+ init_alloc(blocks*block_size, block_size);
+ alloc->init_add_free(2*block_size, (blocks-2)*block_size);
+ for (int64_t big = mas; big < 1048576*128; big*=2) {
+ cout << big << std::endl;
+ PExtentVector extents;
+ EXPECT_EQ(big,
+ alloc->allocate(big, mas, 0, &extents));
+ }
+}
+
+TEST_P(AllocTest, test_alloc_non_aligned_len)
+{
+ int64_t block_size = 1 << 12;
+ int64_t blocks = (1 << 20) * 100;
+ int64_t want_size = 1 << 22;
+ int64_t alloc_unit = 1 << 20;
+
+ init_alloc(blocks*block_size, block_size);
+ alloc->init_add_free(0, 2097152);
+ alloc->init_add_free(2097152, 1064960);
+ alloc->init_add_free(3670016, 2097152);
+
+ PExtentVector extents;
+ EXPECT_EQ(want_size, alloc->allocate(want_size, alloc_unit, 0, &extents));
+}
+
+TEST_P(AllocTest, test_alloc_39334)
+{
+ uint64_t block = 0x4000;
+ uint64_t size = 0x5d00000000;
+
+ init_alloc(size, block);
+ alloc->init_add_free(0x4000, 0x5cffffc000);
+ EXPECT_EQ(size - block, alloc->get_free());
+}
+
+TEST_P(AllocTest, test_alloc_fragmentation)
+{
+ uint64_t capacity = 4 * 1024 * 1024;
+ uint64_t alloc_unit = 4096;
+ uint64_t want_size = alloc_unit;
+ PExtentVector allocated, tmp;
+
+ init_alloc(capacity, alloc_unit);
+ alloc->init_add_free(0, capacity);
+ bool bitmap_alloc = GetParam() == std::string("bitmap");
+
+ EXPECT_EQ(0.0, alloc->get_fragmentation());
+
+ for (size_t i = 0; i < capacity / alloc_unit; ++i)
+ {
+ tmp.clear();
+ EXPECT_EQ(static_cast<int64_t>(want_size),
+ alloc->allocate(want_size, alloc_unit, 0, 0, &tmp));
+ allocated.insert(allocated.end(), tmp.begin(), tmp.end());
+
+ // bitmap fragmentation calculation doesn't provide such constant
+ // estimate
+ if (!bitmap_alloc) {
+ EXPECT_EQ(0.0, alloc->get_fragmentation());
+ }
+ }
+ tmp.clear();
+ EXPECT_EQ(-ENOSPC, alloc->allocate(want_size, alloc_unit, 0, 0, &tmp));
+
+ if (GetParam() == string("avl")) {
+ // AVL allocator uses a different allocating strategy
+ GTEST_SKIP() << "skipping for AVL allocator";
+ } else if (GetParam() == string("hybrid")) {
+ // AVL allocator uses a different allocating strategy
+ GTEST_SKIP() << "skipping for Hybrid allocator";
+ }
+
+ for (size_t i = 0; i < allocated.size(); i += 2)
+ {
+ interval_set<uint64_t> release_set;
+ release_set.insert(allocated[i].offset, allocated[i].length);
+ alloc->release(release_set);
+ }
+ EXPECT_EQ(1.0, alloc->get_fragmentation());
+ EXPECT_EQ(66u, uint64_t(alloc->get_fragmentation_score() * 100));
+
+ for (size_t i = 1; i < allocated.size() / 2; i += 2)
+ {
+ interval_set<uint64_t> release_set;
+ release_set.insert(allocated[i].offset, allocated[i].length);
+ alloc->release(release_set);
+ }
+ if (bitmap_alloc) {
+ // fragmentation = one l1 slot is free + one l1 slot is partial
+ EXPECT_EQ(50U, uint64_t(alloc->get_fragmentation() * 100));
+ } else {
+ // fragmentation approx = 257 intervals / 768 max intervals
+ EXPECT_EQ(33u, uint64_t(alloc->get_fragmentation() * 100));
+ }
+ EXPECT_EQ(27u, uint64_t(alloc->get_fragmentation_score() * 100));
+
+ for (size_t i = allocated.size() / 2 + 1; i < allocated.size(); i += 2)
+ {
+ interval_set<uint64_t> release_set;
+ release_set.insert(allocated[i].offset, allocated[i].length);
+ alloc->release(release_set);
+ }
+ // doing some rounding trick as stupid allocator doesn't merge all the
+ // extents that causes some minor fragmentation (minor bug or by-design behavior?).
+ // Hence leaving just two
+ // digits after decimal point due to this.
+ EXPECT_EQ(0u, uint64_t(alloc->get_fragmentation() * 100));
+ if (bitmap_alloc) {
+ EXPECT_EQ(0u, uint64_t(alloc->get_fragmentation_score() * 100));
+ } else {
+ EXPECT_EQ(11u, uint64_t(alloc->get_fragmentation_score() * 100));
+ }
+}
+
+TEST_P(AllocTest, test_dump_fragmentation_score)
+{
+ uint64_t capacity = 1024 * 1024 * 1024;
+ uint64_t one_alloc_max = 2 * 1024 * 1024;
+ uint64_t alloc_unit = 4096;
+ uint64_t want_size = alloc_unit;
+ uint64_t rounds = 10;
+ uint64_t actions_per_round = 1000;
+ PExtentVector allocated, tmp;
+ gen_type rng;
+
+ init_alloc(capacity, alloc_unit);
+ alloc->init_add_free(0, capacity);
+
+ EXPECT_EQ(0.0, alloc->get_fragmentation());
+ EXPECT_EQ(0.0, alloc->get_fragmentation_score());
+
+ uint64_t allocated_cnt = 0;
+ for (size_t round = 0; round < rounds ; round++) {
+ for (size_t j = 0; j < actions_per_round ; j++) {
+ //free or allocate ?
+ if ( rng() % capacity >= allocated_cnt ) {
+ //allocate
+ want_size = ( rng() % one_alloc_max ) / alloc_unit * alloc_unit + alloc_unit;
+ tmp.clear();
+ int64_t r = alloc->allocate(want_size, alloc_unit, 0, 0, &tmp);
+ if (r > 0) {
+ for (auto& t: tmp) {
+ if (t.length > 0)
+ allocated.push_back(t);
+ }
+ allocated_cnt += r;
+ }
+ } else {
+ //free
+ ceph_assert(allocated.size() > 0);
+ size_t item = rng() % allocated.size();
+ ceph_assert(allocated[item].length > 0);
+ allocated_cnt -= allocated[item].length;
+ interval_set<uint64_t> release_set;
+ release_set.insert(allocated[item].offset, allocated[item].length);
+ alloc->release(release_set);
+ std::swap(allocated[item], allocated[allocated.size() - 1]);
+ allocated.resize(allocated.size() - 1);
+ }
+ }
+
+ size_t free_sum = 0;
+ auto iterated_allocation = [&](size_t off, size_t len) {
+ ceph_assert(len > 0);
+ free_sum += len;
+ };
+ alloc->foreach(iterated_allocation);
+ EXPECT_GT(1, alloc->get_fragmentation_score());
+ EXPECT_EQ(capacity, free_sum + allocated_cnt);
+ }
+
+ for (size_t i = 0; i < allocated.size(); i ++)
+ {
+ interval_set<uint64_t> release_set;
+ release_set.insert(allocated[i].offset, allocated[i].length);
+ alloc->release(release_set);
+ }
+}
+
+TEST_P(AllocTest, test_alloc_bug_24598)
+{
+ if (string(GetParam()) != "bitmap")
+ return;
+
+ uint64_t capacity = 0x2625a0000ull;
+ uint64_t alloc_unit = 0x4000;
+ uint64_t want_size = 0x200000;
+ PExtentVector allocated, tmp;
+
+ init_alloc(capacity, alloc_unit);
+
+ alloc->init_add_free(0x4800000, 0x100000);
+ alloc->init_add_free(0x4a00000, 0x100000);
+
+ alloc->init_rm_free(0x4800000, 0x100000);
+ alloc->init_rm_free(0x4a00000, 0x100000);
+
+ alloc->init_add_free(0x3f00000, 0x500000);
+ alloc->init_add_free(0x4500000, 0x100000);
+ alloc->init_add_free(0x4700000, 0x100000);
+ alloc->init_add_free(0x4900000, 0x100000);
+ alloc->init_add_free(0x4b00000, 0x200000);
+
+ EXPECT_EQ(static_cast<int64_t>(want_size),
+ alloc->allocate(want_size, 0x100000, 0, 0, &tmp));
+ EXPECT_EQ(1u, tmp.size());
+ EXPECT_EQ(0x4b00000u, tmp[0].offset);
+ EXPECT_EQ(0x200000u, tmp[0].length);
+}
+
+//Verifies issue from
+//http://tracker.ceph.com/issues/40703
+//
+TEST_P(AllocTest, test_alloc_big2)
+{
+ int64_t block_size = 4096;
+ int64_t blocks = 1048576 * 2;
+ int64_t mas = 1024*1024;
+ init_alloc(blocks*block_size, block_size);
+ alloc->init_add_free(0, blocks * block_size);
+
+ PExtentVector extents;
+ uint64_t need = block_size * blocks / 4; // 2GB
+ EXPECT_EQ(need,
+ alloc->allocate(need, mas, 0, &extents));
+ need = block_size * blocks / 4; // 2GB
+ extents.clear();
+ EXPECT_EQ(need,
+ alloc->allocate(need, mas, 0, &extents));
+ EXPECT_TRUE(extents[0].length > 0);
+}
+
+//Verifies stuck 4GB chunk allocation
+//in StupidAllocator
+//
+TEST_P(AllocTest, test_alloc_big3)
+{
+ int64_t block_size = 4096;
+ int64_t blocks = 1048576 * 2;
+ int64_t mas = 1024*1024;
+ init_alloc(blocks*block_size, block_size);
+ alloc->init_add_free(0, blocks * block_size);
+
+ PExtentVector extents;
+ uint64_t need = block_size * blocks / 2; // 4GB
+ EXPECT_EQ(need,
+ alloc->allocate(need, mas, 0, &extents));
+ EXPECT_TRUE(extents[0].length > 0);
+}
+
+TEST_P(AllocTest, test_alloc_contiguous)
+{
+ int64_t block_size = 0x1000;
+ int64_t capacity = block_size * 1024 * 1024;
+
+ {
+ init_alloc(capacity, block_size);
+
+ alloc->init_add_free(0, capacity);
+ PExtentVector extents;
+ uint64_t need = 4 * block_size;
+ EXPECT_EQ(need,
+ alloc->allocate(need, need,
+ 0, (int64_t)0, &extents));
+ EXPECT_EQ(1u, extents.size());
+ EXPECT_EQ(extents[0].offset, 0);
+ EXPECT_EQ(extents[0].length, 4 * block_size);
+
+ extents.clear();
+ EXPECT_EQ(need,
+ alloc->allocate(need, need,
+ 0, (int64_t)0, &extents));
+ EXPECT_EQ(1u, extents.size());
+ EXPECT_EQ(extents[0].offset, 4 * block_size);
+ EXPECT_EQ(extents[0].length, 4 * block_size);
+ }
+
+ alloc->shutdown();
+}
+
+TEST_P(AllocTest, test_alloc_47883)
+{
+ uint64_t block = 0x1000;
+ uint64_t size = 1599858540544ul;
+
+ init_alloc(size, block);
+
+ alloc->init_add_free(0x1b970000, 0x26000);
+ alloc->init_add_free(0x1747e9d5000, 0x493000);
+ alloc->init_add_free(0x1747ee6a000, 0x196000);
+
+ PExtentVector extents;
+ auto need = 0x3f980000;
+ auto got = alloc->allocate(need, 0x10000, 0, (int64_t)0, &extents);
+ EXPECT_GE(got, 0x630000);
+}
+
+TEST_P(AllocTest, test_alloc_50656_best_fit)
+{
+ uint64_t block = 0x1000;
+ uint64_t size = 0x3b9e400000;
+
+ init_alloc(size, block);
+
+ // too few free extents - causes best fit mode for avls
+ for (size_t i = 0; i < 0x10; i++) {
+ alloc->init_add_free(i * 2 * 0x100000, 0x100000);
+ }
+
+ alloc->init_add_free(0x1e1bd13000, 0x404000);
+
+ PExtentVector extents;
+ auto need = 0x400000;
+ auto got = alloc->allocate(need, 0x10000, 0, (int64_t)0, &extents);
+ EXPECT_GT(got, 0);
+ EXPECT_EQ(got, 0x400000);
+}
+
+TEST_P(AllocTest, test_alloc_50656_first_fit)
+{
+ uint64_t block = 0x1000;
+ uint64_t size = 0x3b9e400000;
+
+ init_alloc(size, block);
+
+ for (size_t i = 0; i < 0x10000; i += 2) {
+ alloc->init_add_free(i * 0x100000, 0x100000);
+ }
+
+ alloc->init_add_free(0x1e1bd13000, 0x404000);
+
+ PExtentVector extents;
+ auto need = 0x400000;
+ auto got = alloc->allocate(need, 0x10000, 0, (int64_t)0, &extents);
+ EXPECT_GT(got, 0);
+ EXPECT_EQ(got, 0x400000);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Allocator,
+ AllocTest,
+ ::testing::Values("stupid", "bitmap", "avl", "hybrid"));
diff --git a/src/test/objectstore/CMakeLists.txt b/src/test/objectstore/CMakeLists.txt
new file mode 100644
index 000000000..a012264e8
--- /dev/null
+++ b/src/test/objectstore/CMakeLists.txt
@@ -0,0 +1,140 @@
+add_executable(ceph_perf_objectstore
+ ObjectStoreTransactionBenchmark.cc)
+target_link_libraries(ceph_perf_objectstore os osdc global ${UNITTEST_LIBS})
+install(TARGETS ceph_perf_objectstore
+ DESTINATION bin)
+
+add_library(store_test_fixture OBJECT store_test_fixture.cc)
+target_include_directories(store_test_fixture PRIVATE
+ $<TARGET_PROPERTY:GTest::GTest,INTERFACE_INCLUDE_DIRECTORIES>)
+
+add_executable(ceph_test_objectstore
+ store_test.cc
+ $<TARGET_OBJECTS:store_test_fixture>)
+target_link_libraries(ceph_test_objectstore
+ os
+ ceph-common
+ ${UNITTEST_LIBS}
+ global
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ )
+install(TARGETS ceph_test_objectstore
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(ceph_test_keyvaluedb
+ test_kv.cc)
+target_link_libraries(ceph_test_keyvaluedb
+ os
+ ceph-common
+ ${UNITTEST_LIBS}
+ global
+ ${EXTRALIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ )
+install(TARGETS ceph_test_keyvaluedb
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# unittest_rocksdb_option
+add_executable(unittest_rocksdb_option
+ TestRocksdbOptionParse.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_rocksdb_option)
+target_link_libraries(unittest_rocksdb_option global os ${BLKID_LIBRARIES})
+
+if(WITH_EVENTTRACE)
+ add_dependencies(os eventtrace_tp)
+endif()
+
+if(WITH_BLUESTORE)
+
+ add_executable(unittest_alloc
+ Allocator_test.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+ add_ceph_unittest(unittest_alloc)
+ target_link_libraries(unittest_alloc os global)
+
+ add_executable(unittest_alloc_bench
+ Allocator_bench.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+ target_link_libraries(unittest_alloc_bench ${UNITTEST_LIBS} os global)
+
+ add_executable(unittest_fastbmap_allocator
+ fastbmap_allocator_test.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+ add_ceph_unittest(unittest_fastbmap_allocator)
+ target_link_libraries(unittest_fastbmap_allocator os global)
+
+ set_target_properties(unittest_fastbmap_allocator PROPERTIES COMPILE_FLAGS
+ "${UNITTEST_CXX_FLAGS}")
+
+ add_executable(unittest_hybrid_allocator
+ hybrid_allocator_test.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+ add_ceph_unittest(unittest_hybrid_allocator)
+ target_link_libraries(unittest_hybrid_allocator os global)
+
+ set_target_properties(unittest_hybrid_allocator PROPERTIES COMPILE_FLAGS
+ "${UNITTEST_CXX_FLAGS}")
+
+ add_executable(unittest_alloc_aging EXCLUDE_FROM_ALL
+ Allocator_aging_fragmentation.cc)
+ target_link_libraries(unittest_alloc_aging os global GTest::Main)
+
+ # unittest_bluefs
+ add_executable(unittest_bluefs
+ test_bluefs.cc
+ )
+ add_ceph_unittest(unittest_bluefs)
+ target_link_libraries(unittest_bluefs os global)
+
+ # unittest_bluestore_types
+ add_executable(unittest_bluestore_types
+ test_bluestore_types.cc
+ )
+ add_ceph_unittest(unittest_bluestore_types)
+ target_link_libraries(unittest_bluestore_types os global)
+
+ # unittest_bdev
+ add_executable(unittest_bdev
+ test_bdev.cc
+ )
+ add_ceph_unittest(unittest_bdev)
+ target_link_libraries(unittest_bdev os global)
+
+ # unittest_deferred
+ add_executable(unittest_deferred
+ test_deferred.cc
+ )
+ add_ceph_unittest(unittest_deferred)
+ target_link_libraries(unittest_deferred os global)
+
+endif(WITH_BLUESTORE)
+
+# unittest_transaction
+add_executable(unittest_transaction
+ test_transaction.cc)
+add_ceph_unittest(unittest_transaction)
+target_link_libraries(unittest_transaction os ceph-common)
+
+# unittest_memstore_clone
+add_executable(unittest_memstore_clone
+ test_memstore_clone.cc
+ $<TARGET_OBJECTS:store_test_fixture>)
+add_ceph_unittest(unittest_memstore_clone)
+target_link_libraries(unittest_memstore_clone os global)
+
+if(WITH_BLUESTORE)
+ add_executable(ceph_test_alloc_replay
+ allocator_replay_test.cc)
+ target_link_libraries(ceph_test_alloc_replay os global ${UNITTEST_LIBS})
+ install(TARGETS ceph_test_alloc_replay
+ DESTINATION bin)
+endif()
diff --git a/src/test/objectstore/ObjectStoreTransactionBenchmark.cc b/src/test/objectstore/ObjectStoreTransactionBenchmark.cc
new file mode 100644
index 000000000..e2ce3b2ef
--- /dev/null
+++ b/src/test/objectstore/ObjectStoreTransactionBenchmark.cc
@@ -0,0 +1,266 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 UnitedStack <haomai@unitedstack.com>
+ *
+ * Author: Haomai Wang <haomaiwang@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string>
+#include <iostream>
+
+using namespace std;
+
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "common/Cycles.h"
+#include "global/global_init.h"
+#include "os/ObjectStore.h"
+
+class Transaction {
+ private:
+ ObjectStore::Transaction t;
+
+ public:
+ struct Tick {
+ uint64_t ticks;
+ uint64_t count;
+ Tick(): ticks(0), count(0) {}
+ void add(uint64_t a) {
+ ticks += a;
+ count++;
+ }
+ };
+ static Tick write_ticks, setattr_ticks, omap_setkeys_ticks, omap_rmkey_ticks;
+ static Tick encode_ticks, decode_ticks, iterate_ticks;
+
+ void write(coll_t cid, const ghobject_t& oid, uint64_t off, uint64_t len,
+ const bufferlist& data) {
+ uint64_t start_time = Cycles::rdtsc();
+ t.write(cid, oid, off, len, data);
+ write_ticks.add(Cycles::rdtsc() - start_time);
+ }
+ void setattr(coll_t cid, const ghobject_t& oid, const string &name,
+ bufferlist& val) {
+ uint64_t start_time = Cycles::rdtsc();
+ t.setattr(cid, oid, name, val);
+ setattr_ticks.add(Cycles::rdtsc() - start_time);
+ }
+ void omap_setkeys(coll_t cid, const ghobject_t &oid,
+ const map<string, bufferlist> &attrset) {
+
+ uint64_t start_time = Cycles::rdtsc();
+ t.omap_setkeys(cid, oid, attrset);
+ omap_setkeys_ticks.add(Cycles::rdtsc() - start_time);
+ }
+ void omap_rmkey(coll_t cid, const ghobject_t &oid,
+ const string &key) {
+ uint64_t start_time = Cycles::rdtsc();
+ t.omap_rmkey(cid, oid, key);
+ omap_rmkey_ticks.add(Cycles::rdtsc() - start_time);
+ }
+
+ void apply_encode_decode() {
+ bufferlist bl;
+ ObjectStore::Transaction d;
+ uint64_t start_time = Cycles::rdtsc();
+ t.encode(bl);
+ encode_ticks.add(Cycles::rdtsc() - start_time);
+
+ auto bliter = bl.cbegin();
+ start_time = Cycles::rdtsc();
+ d.decode(bliter);
+ decode_ticks.add(Cycles::rdtsc() - start_time);
+ }
+
+ void apply_iterate() {
+ uint64_t start_time = Cycles::rdtsc();
+ ObjectStore::Transaction::iterator i = t.begin();
+ while (i.have_op()) {
+ ObjectStore::Transaction::Op *op = i.decode_op();
+
+ switch (op->op) {
+ case ObjectStore::Transaction::OP_WRITE:
+ {
+ ghobject_t oid = i.get_oid(op->oid);
+ bufferlist bl;
+ i.decode_bl(bl);
+ }
+ break;
+ case ObjectStore::Transaction::OP_SETATTR:
+ {
+ ghobject_t oid = i.get_oid(op->oid);
+ string name = i.decode_string();
+ bufferlist bl;
+ i.decode_bl(bl);
+ map<string, bufferptr> to_set;
+ to_set[name] = bufferptr(bl.c_str(), bl.length());
+ }
+ break;
+ case ObjectStore::Transaction::OP_OMAP_SETKEYS:
+ {
+ ghobject_t oid = i.get_oid(op->oid);
+ map<string, bufferptr> aset;
+ i.decode_attrset(aset);
+ }
+ break;
+ case ObjectStore::Transaction::OP_OMAP_RMKEYS:
+ {
+ ghobject_t oid = i.get_oid(op->oid);
+ set<string> keys;
+ i.decode_keyset(keys);
+ }
+ break;
+ }
+ }
+ iterate_ticks.add(Cycles::rdtsc() - start_time);
+ }
+
+ static void dump_stat() {
+ cerr << " write op: " << Cycles::to_microseconds(write_ticks.ticks) << "us count: " << write_ticks.count << std::endl;
+ cerr << " setattr op: " << Cycles::to_microseconds(setattr_ticks.ticks) << "us count: " << setattr_ticks.count << std::endl;
+ cerr << " omap_setkeys op: " << Cycles::to_microseconds(Transaction::omap_setkeys_ticks.ticks) << "us count: " << Transaction::omap_setkeys_ticks.count << std::endl;
+ cerr << " omap_rmkey op: " << Cycles::to_microseconds(Transaction::omap_rmkey_ticks.ticks) << "us count: " << Transaction::omap_rmkey_ticks.count << std::endl;
+ cerr << " encode op: " << Cycles::to_microseconds(Transaction::encode_ticks.ticks) << "us count: " << Transaction::encode_ticks.count << std::endl;
+ cerr << " decode op: " << Cycles::to_microseconds(Transaction::decode_ticks.ticks) << "us count: " << Transaction::decode_ticks.count << std::endl;
+ cerr << " iterate op: " << Cycles::to_microseconds(Transaction::iterate_ticks.ticks) << "us count: " << Transaction::iterate_ticks.count << std::endl;
+ }
+};
+
+class PerfCase {
+ static const uint64_t Kib = 1024;
+ static const uint64_t Mib = 1024 * 1024;
+ static const string info_epoch_attr;
+ static const string info_info_attr;
+ static const string attr;
+ static const string snapset_attr;
+ static const string pglog_attr;
+ static const coll_t meta_cid;
+ static const coll_t cid;
+ static const ghobject_t pglog_oid;
+ static const ghobject_t info_oid;
+ map<string, bufferlist> data;
+
+ ghobject_t create_object() {
+ bufferlist bl = generate_random(100, 1);
+ return ghobject_t(hobject_t(string("obj_")+string(bl.c_str()), string(), rand() & 2 ? CEPH_NOSNAP : rand(), rand() & 0xFF, 0, ""));
+ }
+
+
+ bufferlist generate_random(uint64_t len, int frag) {
+ static const char alphanum[] = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ uint64_t per_frag = len / frag;
+ bufferlist bl;
+ for (int i = 0; i < frag; i++ ) {
+ bufferptr bp(per_frag);
+ for (unsigned int j = 0; j < len; j++) {
+ bp[j] = alphanum[rand() % (sizeof(alphanum) - 1)];
+ }
+ bl.append(bp);
+ }
+ return bl;
+ }
+ public:
+ PerfCase() {
+ uint64_t four_kb = Kib * 4;
+ uint64_t one_mb = Mib * 1;
+ uint64_t four_mb = Mib * 4;
+ data["4k"] = generate_random(four_kb, 1);
+ data["1m"] = generate_random(one_mb, 1);
+ data["4m"] = generate_random(four_mb, 1);
+ data[attr] = generate_random(256, 1);
+ data[snapset_attr] = generate_random(32, 1);
+ data[pglog_attr] = generate_random(128, 1);
+ data[info_epoch_attr] = generate_random(4, 1);
+ data[info_info_attr] = generate_random(560, 1);
+ }
+
+ uint64_t rados_write_4k(int times) {
+ uint64_t ticks = 0;
+ uint64_t len = Kib *4;
+ for (int i = 0; i < times; i++) {
+ uint64_t start_time = 0;
+ {
+ Transaction t;
+ ghobject_t oid = create_object();
+ start_time = Cycles::rdtsc();
+ t.write(cid, oid, 0, len, data["4k"]);
+ t.setattr(cid, oid, attr, data[attr]);
+ t.setattr(cid, oid, snapset_attr, data[snapset_attr]);
+ t.apply_encode_decode();
+ t.apply_iterate();
+ ticks += Cycles::rdtsc() - start_time;
+ }
+ {
+ Transaction t;
+ map<string, bufferlist> pglog_attrset;
+ map<string, bufferlist> info_attrset;
+ pglog_attrset[pglog_attr] = data[pglog_attr];
+ info_attrset[info_epoch_attr] = data[info_epoch_attr];
+ info_attrset[info_info_attr] = data[info_info_attr];
+ start_time = Cycles::rdtsc();
+ t.omap_setkeys(meta_cid, pglog_oid, pglog_attrset);
+ t.omap_setkeys(meta_cid, info_oid, info_attrset);
+ t.omap_rmkey(meta_cid, pglog_oid, pglog_attr);
+ t.apply_encode_decode();
+ t.apply_iterate();
+ ticks += Cycles::rdtsc() - start_time;
+ }
+ }
+ return ticks;
+ }
+};
+const string PerfCase::info_epoch_attr("11.40_epoch");
+const string PerfCase::info_info_attr("11.40_info");
+const string PerfCase::attr("_");
+const string PerfCase::snapset_attr("snapset");
+const string PerfCase::pglog_attr("pglog_attr");
+const coll_t PerfCase::meta_cid;
+const coll_t PerfCase::cid;
+const ghobject_t PerfCase::pglog_oid(hobject_t(sobject_t(object_t("cid_pglog"), 0)));
+const ghobject_t PerfCase::info_oid(hobject_t(sobject_t(object_t("infos"), 0)));
+Transaction::Tick Transaction::write_ticks, Transaction::setattr_ticks, Transaction::omap_setkeys_ticks, Transaction::omap_rmkey_ticks;
+Transaction::Tick Transaction::encode_ticks, Transaction::decode_ticks, Transaction::iterate_ticks;
+
+void usage(const string &name) {
+ cerr << "Usage: " << name << " [times] "
+ << std::endl;
+}
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+ Cycles::init();
+
+ cerr << "args: " << args << std::endl;
+ if (args.size() < 1) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ uint64_t times = atoi(args[0]);
+ PerfCase c;
+ uint64_t ticks = c.rados_write_4k(times);
+ Transaction::dump_stat();
+ cerr << " Total rados op " << times << " run time " << Cycles::to_microseconds(ticks) << "us." << std::endl;
+
+ return 0;
+}
diff --git a/src/test/objectstore/TestObjectStoreState.cc b/src/test/objectstore/TestObjectStoreState.cc
new file mode 100644
index 000000000..f4ccef4f0
--- /dev/null
+++ b/src/test/objectstore/TestObjectStoreState.cc
@@ -0,0 +1,299 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2012 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <time.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "os/ObjectStore.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "common/debug.h"
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include "TestObjectStoreState.h"
+#include "include/ceph_assert.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_filestore
+#undef dout_prefix
+#define dout_prefix *_dout << "ceph_test_objectstore_state "
+
+using namespace std;
+
+void TestObjectStoreState::init(int colls, int objs)
+{
+ dout(5) << "init " << colls << " colls " << objs << " objs" << dendl;
+
+ ObjectStore::Transaction t;
+ auto meta_ch = m_store->create_new_collection(coll_t::meta());
+ t.create_collection(coll_t::meta(), 0);
+ m_store->queue_transaction(meta_ch, std::move(t));
+
+ wait_for_ready();
+
+ int baseid = 0;
+ for (int i = 0; i < colls; i++) {
+ spg_t pgid(pg_t(i, 1), shard_id_t::NO_SHARD);
+ coll_t cid(pgid);
+ auto ch = m_store->create_new_collection(cid);
+ coll_entry_t *entry = coll_create(pgid, ch);
+ dout(5) << "init create collection " << entry->m_cid
+ << " meta " << entry->m_meta_obj << dendl;
+
+ ObjectStore::Transaction *t = new ObjectStore::Transaction;
+ t->create_collection(entry->m_cid, 32);
+ bufferlist hint;
+ uint32_t pg_num = colls;
+ uint64_t num_objs = uint64_t(objs / colls);
+ encode(pg_num, hint);
+ encode(num_objs, hint);
+ t->collection_hint(entry->m_cid, ObjectStore::Transaction::COLL_HINT_EXPECTED_NUM_OBJECTS, hint);
+ dout(5) << "give collection hint, number of objects per collection: " << num_objs << dendl;
+ t->touch(cid, entry->m_meta_obj);
+
+ for (int i = 0; i < objs; i++) {
+ hobject_t *obj = entry->touch_obj(i + baseid);
+ t->touch(entry->m_cid, ghobject_t(*obj));
+ ceph_assert(i + baseid == m_num_objects);
+ m_num_objects++;
+ }
+ baseid += objs;
+
+ t->register_on_commit(new C_OnFinished(this));
+ m_store->queue_transaction(entry->m_ch, std::move(*t), nullptr);
+
+ delete t;
+ inc_in_flight();
+
+ m_collections.insert(make_pair(cid, entry));
+ rebuild_id_vec();
+ m_next_coll_nr++;
+ }
+ dout(5) << "init has " << m_in_flight.load() << "in-flight transactions" << dendl;
+ wait_for_done();
+ dout(5) << "init finished" << dendl;
+}
+
+TestObjectStoreState::coll_entry_t *TestObjectStoreState::coll_create(
+ spg_t pgid, ObjectStore::CollectionHandle ch)
+{
+ char meta_buf[100];
+ memset(meta_buf, 0, 100);
+ snprintf(meta_buf, 100, "pglog_0_head");
+ return (new coll_entry_t(pgid, ch, meta_buf));
+}
+
+TestObjectStoreState::coll_entry_t*
+TestObjectStoreState::get_coll(coll_t cid, bool erase)
+{
+ dout(5) << "get_coll id " << cid << dendl;
+
+ coll_entry_t *entry = NULL;
+ auto it = m_collections.find(cid);
+ if (it != m_collections.end()) {
+ entry = it->second;
+ if (erase) {
+ m_collections.erase(it);
+ rebuild_id_vec();
+ }
+ }
+
+ dout(5) << "get_coll id " << cid;
+ if (!entry)
+ *_dout << " non-existent";
+ else
+ *_dout << " name " << entry->m_cid;
+ *_dout << dendl;
+ return entry;
+}
+
+TestObjectStoreState::coll_entry_t*
+TestObjectStoreState::get_coll_at(int pos, bool erase)
+{
+ dout(5) << "get_coll_at pos " << pos << dendl;
+
+ if (m_collections.empty())
+ return NULL;
+
+ ceph_assert((size_t) pos < m_collections_ids.size());
+
+ coll_t cid = m_collections_ids[pos];
+ coll_entry_t *entry = m_collections[cid];
+
+ if (entry == NULL) {
+ dout(5) << "get_coll_at pos " << pos << " non-existent" << dendl;
+ return NULL;
+ }
+
+ if (erase) {
+ m_collections.erase(cid);
+ rebuild_id_vec();
+ }
+
+ dout(5) << "get_coll_at pos " << pos << ": "
+ << entry->m_cid << "(removed: " << erase << ")" << dendl;
+
+ return entry;
+}
+
+TestObjectStoreState::coll_entry_t::~coll_entry_t()
+{
+ if (m_objects.size() > 0) {
+ map<int, hobject_t*>::iterator it = m_objects.begin();
+ for (; it != m_objects.end(); ++it) {
+ hobject_t *obj = it->second;
+ if (obj) {
+ delete obj;
+ }
+ }
+ m_objects.clear();
+ }
+}
+
+bool TestObjectStoreState::coll_entry_t::check_for_obj(int id)
+{
+ if (m_objects.count(id))
+ return true;
+ return false;
+}
+
+hobject_t *TestObjectStoreState::coll_entry_t::touch_obj(int id)
+{
+ map<int, hobject_t*>::iterator it = m_objects.find(id);
+ if (it != m_objects.end()) {
+ dout(5) << "touch_obj coll id " << m_cid
+ << " name " << it->second->oid.name << dendl;
+ return it->second;
+ }
+
+ char buf[100];
+ memset(buf, 0, 100);
+ snprintf(buf, 100, "obj%d", id);
+
+ hobject_t *obj = new hobject_t(sobject_t(object_t(buf), CEPH_NOSNAP));
+ obj->set_hash(m_pgid.ps());
+ obj->pool = m_pgid.pool();
+ m_objects.insert(make_pair(id, obj));
+
+ dout(5) << "touch_obj coll id " << m_cid << " name " << buf << dendl;
+ return obj;
+}
+
+hobject_t *TestObjectStoreState::coll_entry_t::get_obj(int id)
+{
+ return get_obj(id, false);
+}
+
+/**
+ * remove_obj - Removes object without freeing it.
+ * @param id Object's id in the map.
+ * @return The object or NULL in case of error.
+ */
+hobject_t *TestObjectStoreState::coll_entry_t::remove_obj(int id)
+{
+ return get_obj(id, true);
+}
+
+hobject_t *TestObjectStoreState::coll_entry_t::get_obj(int id, bool remove)
+{
+ map<int, hobject_t*>::iterator it = m_objects.find(id);
+ if (it == m_objects.end()) {
+ dout(5) << "get_obj coll " << m_cid
+ << " obj #" << id << " non-existent" << dendl;
+ return NULL;
+ }
+
+ hobject_t *obj = it->second;
+ if (remove)
+ m_objects.erase(it);
+
+ dout(5) << "get_obj coll " << m_cid << " id " << id
+ << ": " << obj->oid.name << "(removed: " << remove << ")" << dendl;
+
+ return obj;
+}
+
+hobject_t *TestObjectStoreState::coll_entry_t::get_obj_at(int pos, int *key)
+{
+ return get_obj_at(pos, false, key);
+}
+
+/**
+ * remove_obj_at - Removes object without freeing it.
+ * @param pos The map's position in which the object lies.
+ * @return The object or NULL in case of error.
+ */
+hobject_t *TestObjectStoreState::coll_entry_t::remove_obj_at(int pos, int *key)
+{
+ return get_obj_at(pos, true, key);
+}
+
+hobject_t *TestObjectStoreState::coll_entry_t::get_obj_at(int pos,
+ bool remove, int *key)
+{
+ if (m_objects.empty()) {
+ dout(5) << "get_obj_at coll " << m_cid << " pos " << pos
+ << " in an empty collection" << dendl;
+ return NULL;
+ }
+
+ hobject_t *ret = NULL;
+ map<int, hobject_t*>::iterator it = m_objects.begin();
+ for (int i = 0; it != m_objects.end(); ++it, i++) {
+ if (i == pos) {
+ ret = it->second;
+ break;
+ }
+ }
+
+ if (ret == NULL) {
+ dout(5) << "get_obj_at coll " << m_cid << " pos " << pos
+ << " non-existent" << dendl;
+ return NULL;
+ }
+
+ if (key != NULL)
+ *key = it->first;
+
+ if (remove)
+ m_objects.erase(it);
+
+ dout(5) << "get_obj_at coll id " << m_cid << " pos " << pos
+ << ": " << ret->oid.name << "(removed: " << remove << ")" << dendl;
+
+ return ret;
+}
+
+hobject_t*
+TestObjectStoreState::coll_entry_t::replace_obj(int id, hobject_t *obj) {
+ hobject_t *old_obj = remove_obj(id);
+ m_objects.insert(make_pair(id, obj));
+ return old_obj;
+}
+
+int TestObjectStoreState::coll_entry_t::get_random_obj_id(rngen_t& gen)
+{
+ ceph_assert(!m_objects.empty());
+
+ boost::uniform_int<> orig_obj_rng(0, m_objects.size()-1);
+ int pos = orig_obj_rng(gen);
+ map<int, hobject_t*>::iterator it = m_objects.begin();
+ for (int i = 0; it != m_objects.end(); ++it, i++) {
+ if (i == pos) {
+ return it->first;
+ }
+ }
+ ceph_abort_msg("INTERNAL ERROR");
+}
diff --git a/src/test/objectstore/TestObjectStoreState.h b/src/test/objectstore/TestObjectStoreState.h
new file mode 100644
index 000000000..d1e31bd8a
--- /dev/null
+++ b/src/test/objectstore/TestObjectStoreState.h
@@ -0,0 +1,158 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2012 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*/
+#ifndef TEST_OBJECTSTORE_STATE_H_
+#define TEST_OBJECTSTORE_STATE_H_
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <map>
+#include <vector>
+
+#include "os/ObjectStore.h"
+#include "common/Cond.h"
+
+typedef boost::mt11213b rngen_t;
+
+class TestObjectStoreState {
+public:
+ struct coll_entry_t {
+ spg_t m_pgid;
+ coll_t m_cid;
+ ghobject_t m_meta_obj;
+ ObjectStore::CollectionHandle m_ch;
+ std::map<int, hobject_t*> m_objects;
+ int m_next_object_id;
+
+ coll_entry_t(spg_t pgid, ObjectStore::CollectionHandle& ch,
+ char *meta_obj_buf)
+ : m_pgid(pgid),
+ m_cid(m_pgid),
+ m_meta_obj(hobject_t(sobject_t(object_t(meta_obj_buf), CEPH_NOSNAP))),
+ m_ch(ch),
+ m_next_object_id(0) {
+ m_meta_obj.hobj.pool = m_pgid.pool();
+ m_meta_obj.hobj.set_hash(m_pgid.ps());
+ }
+ ~coll_entry_t();
+
+ hobject_t *touch_obj(int id);
+ bool check_for_obj(int id);
+ hobject_t *get_obj(int id);
+ hobject_t *remove_obj(int id);
+ hobject_t *get_obj_at(int pos, int *key = NULL);
+ hobject_t *remove_obj_at(int pos, int *key = NULL);
+ hobject_t *replace_obj(int id, hobject_t *obj);
+ int get_random_obj_id(rngen_t& gen);
+
+ private:
+ hobject_t *get_obj(int id, bool remove);
+ hobject_t *get_obj_at(int pos, bool remove, int *key = NULL);
+ };
+
+ protected:
+ boost::shared_ptr<ObjectStore> m_store;
+ std::map<coll_t, coll_entry_t*> m_collections;
+ std::vector<coll_t> m_collections_ids;
+ int m_next_coll_nr;
+ int m_num_objs_per_coll;
+ int m_num_objects;
+
+ int m_max_in_flight;
+ std::atomic<int> m_in_flight = { 0 };
+ ceph::mutex m_finished_lock = ceph::make_mutex("Finished Lock");
+ ceph::condition_variable m_finished_cond;
+
+ void rebuild_id_vec() {
+ m_collections_ids.clear();
+ m_collections_ids.reserve(m_collections.size());
+ for (auto& i : m_collections) {
+ m_collections_ids.push_back(i.first);
+ }
+ }
+
+ void wait_for_ready() {
+ std::unique_lock locker{m_finished_lock};
+ m_finished_cond.wait(locker, [this] {
+ return m_max_in_flight <= 0 || m_in_flight < m_max_in_flight;
+ });
+ }
+
+ void wait_for_done() {
+ std::unique_lock locker{m_finished_lock};
+ m_finished_cond.wait(locker, [this] { return m_in_flight == 0; });
+ }
+
+ void set_max_in_flight(int max) {
+ m_max_in_flight = max;
+ }
+ void set_num_objs_per_coll(int val) {
+ m_num_objs_per_coll = val;
+ }
+
+ coll_entry_t *get_coll(coll_t cid, bool erase = false);
+ coll_entry_t *get_coll_at(int pos, bool erase = false);
+ int get_next_pool_id() { return m_next_pool++; }
+
+ private:
+ static const int m_default_num_colls = 30;
+ // The pool ID used for collection creation, ID 0 is preserve for other tests
+ int m_next_pool;
+
+ public:
+ explicit TestObjectStoreState(ObjectStore *store) :
+ m_next_coll_nr(0), m_num_objs_per_coll(10), m_num_objects(0),
+ m_max_in_flight(0), m_next_pool(2) {
+ m_store.reset(store);
+ }
+ ~TestObjectStoreState() {
+ auto it = m_collections.begin();
+ while (it != m_collections.end()) {
+ if (it->second)
+ delete it->second;
+ m_collections.erase(it++);
+ }
+ }
+
+ void init(int colls, int objs);
+ void init() {
+ init(m_default_num_colls, 0);
+ }
+
+ int inc_in_flight() {
+ return ++m_in_flight;
+ }
+
+ int dec_in_flight() {
+ return --m_in_flight;
+ }
+
+ coll_entry_t *coll_create(spg_t pgid, ObjectStore::CollectionHandle ch);
+
+ class C_OnFinished: public Context {
+ protected:
+ TestObjectStoreState *m_state;
+
+ public:
+ explicit C_OnFinished(TestObjectStoreState *state) : m_state(state) { }
+
+ void finish(int r) override {
+ std::lock_guard locker{m_state->m_finished_lock};
+ m_state->dec_in_flight();
+ m_state->m_finished_cond.notify_all();
+
+ }
+ };
+};
+
+#endif /* TEST_OBJECTSTORE_STATE_H_ */
diff --git a/src/test/objectstore/TestRocksdbOptionParse.cc b/src/test/objectstore/TestRocksdbOptionParse.cc
new file mode 100644
index 000000000..c34ea6bc2
--- /dev/null
+++ b/src/test/objectstore/TestRocksdbOptionParse.cc
@@ -0,0 +1,78 @@
+#include <gtest/gtest.h>
+#include "include/Context.h"
+#include "rocksdb/db.h"
+#include "rocksdb/env.h"
+#include "rocksdb/thread_status.h"
+#include "kv/RocksDBStore.h"
+#include <iostream>
+
+using namespace std;
+
+const string dir("rocksdb.test_temp_dir");
+
+TEST(RocksDBOption, simple) {
+ rocksdb::Options options;
+ rocksdb::Status status;
+ map<string,string> kvoptions;
+ RocksDBStore *db = new RocksDBStore(g_ceph_context, dir, kvoptions, NULL);
+ string options_string = ""
+ "write_buffer_size=536870912;"
+ "create_if_missing=true;"
+ "max_write_buffer_number=4;"
+ "max_background_compactions=4;"
+ "stats_dump_period_sec = 5;"
+ "min_write_buffer_number_to_merge = 2;"
+ "level0_file_num_compaction_trigger = 4;"
+ "max_bytes_for_level_base = 104857600;"
+ "target_file_size_base = 10485760;"
+ "num_levels = 3;"
+ "compression = kNoCompression;"
+ "compaction_options_universal = {min_merge_width=4;size_ratio=2;max_size_amplification_percent=500}";
+ int r = db->ParseOptionsFromString(options_string, options);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(536870912u, options.write_buffer_size);
+ ASSERT_EQ(4, options.max_write_buffer_number);
+ ASSERT_EQ(4, options.max_background_compactions);
+ ASSERT_EQ(5u, options.stats_dump_period_sec);
+ ASSERT_EQ(2, options.min_write_buffer_number_to_merge);
+ ASSERT_EQ(4, options.level0_file_num_compaction_trigger);
+ ASSERT_EQ(104857600u, options.max_bytes_for_level_base);
+ ASSERT_EQ(10485760u, options.target_file_size_base);
+ ASSERT_EQ(3, options.num_levels);
+ ASSERT_EQ(rocksdb::kNoCompression, options.compression);
+ ASSERT_EQ(2, options.compaction_options_universal.size_ratio);
+ ASSERT_EQ(4, options.compaction_options_universal.min_merge_width);
+ ASSERT_EQ(500, options.compaction_options_universal.max_size_amplification_percent);
+}
+TEST(RocksDBOption, interpret) {
+ rocksdb::Options options;
+ rocksdb::Status status;
+ map<string,string> kvoptions;
+ RocksDBStore *db = new RocksDBStore(g_ceph_context, dir, kvoptions, NULL);
+ string options_string = "compact_on_mount = true; compaction_threads=10;flusher_threads=5;";
+
+ int r = db->ParseOptionsFromString(options_string, options);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(db->compact_on_mount);
+ //check thread pool setting
+ options.env->SleepForMicroseconds(100000);
+ std::vector<rocksdb::ThreadStatus> thread_list;
+ status = options.env->GetThreadList(&thread_list);
+ ASSERT_TRUE(status.ok());
+
+ int num_high_pri_threads = 0;
+ int num_low_pri_threads = 0;
+ for (vector<rocksdb::ThreadStatus>::iterator it = thread_list.begin();
+ it!= thread_list.end();
+ ++it) {
+ if (it->thread_type == rocksdb::ThreadStatus::HIGH_PRIORITY)
+ num_high_pri_threads++;
+ if (it->thread_type == rocksdb::ThreadStatus::LOW_PRIORITY)
+ num_low_pri_threads++;
+ }
+ ASSERT_EQ(15u, thread_list.size());
+ //low pri threads is compaction_threads
+ ASSERT_EQ(10, num_low_pri_threads);
+ //high pri threads is flusher_threads
+ ASSERT_EQ(5, num_high_pri_threads);
+}
diff --git a/src/test/objectstore/allocator_replay_test.cc b/src/test/objectstore/allocator_replay_test.cc
new file mode 100644
index 000000000..18959a56c
--- /dev/null
+++ b/src/test/objectstore/allocator_replay_test.cc
@@ -0,0 +1,694 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Allocator replay tool.
+ * Author: Igor Fedotov, ifedotov@suse.com
+ */
+#include <iostream>
+#include <vector>
+
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "common/Cycles.h"
+#include "common/errno.h"
+#include "common/ceph_json.h"
+#include "common/admin_socket.h"
+#include "include/denc.h"
+#include "global/global_init.h"
+#include "os/bluestore/Allocator.h"
+
+using namespace std;
+
+void usage(const string &name) {
+ cerr << "Usage: " << name << " <log_to_replay> <raw_duplicates|duplicates|free_dump|try_alloc count want alloc_unit|replay_alloc alloc_list_file|export_binary out_file>" << std::endl;
+}
+
+void usage_replay_alloc(const string &name) {
+ cerr << "Detailed replay_alloc usage: " << name << " <allocator_dump_JSON> replay_alloc <alloc_list_file> [number of replays]" << std::endl;
+ cerr << "The number of replays defaults to 1." << std::endl;
+ cerr << "The \"alloc_list_file\" parameter should be a file with allocation requests, one per line." << std::endl;
+ cerr << "Allocation request format (space separated, optional parameters are 0 if not given): want unit [max] [hint]" << std::endl;
+}
+
+struct binary_alloc_map_t {
+ std::vector<std::pair<uint64_t, uint64_t>> free_extents;
+
+ DENC(binary_alloc_map_t, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.free_extents, p);
+ DENC_FINISH(p);
+ }
+};
+WRITE_CLASS_DENC(binary_alloc_map_t)
+
+int replay_and_check_for_duplicate(char* fname)
+{
+ unique_ptr<Allocator> alloc;
+
+ FILE* f = fopen(fname, "r");
+ if (!f) {
+ std::cerr << "error: unable to open " << fname << std::endl;
+ return -1;
+ }
+
+ PExtentVector tmp;
+ bool init_done = false;
+ char s[4096];
+ char* sp, *token;
+ interval_set<uint64_t> owned_by_app;
+ while (true) {
+ if (fgets(s, sizeof(s), f) == nullptr) {
+ break;
+ }
+ sp = strstr(s, "init_add_free");
+ if (!sp) {
+ sp = strstr(s, "release");
+ }
+ if (sp) {
+ //2019-05-30 03:23:46.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_add_free 0x100000~680000000
+ // or
+ //2019-05-30 03:23:46.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_add_free done
+ // or
+ // 2019 - 10 - 08T16:19 : 32.257 + 0300 7f5679f3fe80 10 fbmap_alloc 0x564fab96f100 release 0x450000~10000
+ // or
+ // 2019 - 10 - 08T16 : 19 : 32.257 + 0300 7f5679f3fe80 10 fbmap_alloc 0x564fab96f100 release done
+ if (strstr(sp, "done") != nullptr) {
+ continue;
+ }
+ std::cout << s << std::endl;
+ if (!init_done) {
+ std::cerr << "error: no allocator init before: " << s << std::endl;
+ return -1;
+ }
+ uint64_t offs, len;
+ strtok(sp, " ~");
+ token = strtok(nullptr, " ~");
+ ceph_assert(token);
+ offs = strtoul(token, nullptr, 16);
+ token = strtok(nullptr, " ~");
+ ceph_assert(token);
+ len = strtoul(token, nullptr, 16);
+ if (len == 0) {
+ std::cerr << "error: " << sp <<": " << s << std::endl;
+ return -1;
+ }
+ if (!owned_by_app.contains(offs, len)) {
+ std::cerr << "error: unexpected return to allocator, not owned by app: "
+ << s << std::endl;
+ return -1;
+ }
+ owned_by_app.erase(offs, len);
+ if (strstr(sp, "init_add_free") != nullptr) {
+ alloc->init_add_free(offs, len);
+ } else {
+ PExtentVector release_set;
+ release_set.emplace_back(offs, len);
+ alloc->release(release_set);
+ }
+ continue;
+ }
+ sp = strstr(s, "init_rm_free");
+ if (sp) {
+ //2019-05-30 03:23:46.912 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_rm_free 0x100000~680000000
+ // or
+ // 2019-05-30 03:23:46.916 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_rm_free done
+
+ if (strstr(sp, "done") != nullptr) {
+ continue;
+ }
+ std::cout << s << std::endl;
+ if (!init_done) {
+ std::cerr << "error: no allocator init before: " << s << std::endl;
+ return -1;
+ }
+ uint64_t offs, len;
+ strtok(sp, " ~");
+ token = strtok(nullptr, " ~");
+ ceph_assert(token);
+ offs = strtoul(token, nullptr, 16);
+ token = strtok(nullptr, " ~");
+ ceph_assert(token);
+ len = strtoul(token, nullptr, 16);
+ if (len == 0) {
+ std::cerr << "error: " << sp <<": " << s << std::endl;
+ return -1;
+ }
+ alloc->init_rm_free(offs, len);
+
+ if (owned_by_app.intersects(offs, len)) {
+ std::cerr
+ << "error: unexpected takeover from allocator, already owned by app: "
+ << s << std::endl;
+ return -1;
+ } else {
+ owned_by_app.insert(offs, len);
+ }
+
+ continue;
+ }
+ sp = strstr(s, "allocate");
+ if (sp) {
+ //2019-05-30 03:23:48.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 allocate 0x80000000/100000,0,0
+ // and need to bypass
+ // 2019-05-30 03:23:48.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 allocate 0x69d400000~200000/100000,0,0
+
+ // Very simple and stupid check to bypass actual allocations
+ if (strstr(sp, "~") != nullptr) {
+ continue;
+ }
+
+ std::cout << s << std::endl;
+ if (!init_done) {
+ std::cerr << "error: no allocator init before: " << s << std::endl;
+ return -1;
+ }
+ uint64_t want, alloc_unit;
+ strtok(sp, " /");
+ token = strtok(nullptr, " /");
+ ceph_assert(token);
+ want = strtoul(token, nullptr, 16);
+ token = strtok(nullptr, " ~");
+ ceph_assert(token);
+ alloc_unit = strtoul(token, nullptr, 16);
+ if (want == 0 || alloc_unit == 0) {
+ std::cerr << "error: allocate: " << s << std::endl;
+ return -1;
+ }
+ tmp.clear();
+ auto allocated = alloc->allocate(want, alloc_unit, 0, 0, &tmp);
+ std::cout << "allocated TOTAL: " << allocated << std::endl;
+ for (auto& ee : tmp) {
+ std::cerr << "dump extent: " << std::hex
+ << ee.offset << "~" << ee.length
+ << std::dec << std::endl;
+ }
+ std::cerr << "dump completed." << std::endl;
+ for (auto& e : tmp) {
+ if (owned_by_app.intersects(e.offset, e.length)) {
+ std::cerr << "error: unexpected allocated extent: " << std::hex
+ << e.offset << "~" << e.length
+ << " dumping all allocations:" << std::dec << std::endl;
+ for (auto& ee : tmp) {
+ std::cerr <<"dump extent: " << std::hex
+ << ee.offset << "~" << ee.length
+ << std::dec << std::endl;
+ }
+ std::cerr <<"dump completed." << std::endl;
+ return -1;
+ } else {
+ owned_by_app.insert(e.offset, e.length);
+ }
+ }
+ continue;
+ }
+
+ string alloc_type = "bitmap";
+ sp = strstr(s, "BitmapAllocator");
+ if (!sp) {
+ alloc_type = "avl";
+ sp = strstr(s, "AvlAllocator");
+ }
+ if (!sp) {
+ alloc_type = "hybrid";
+ sp = strstr(s, "HybridAllocator");
+ }
+ if (!sp) {
+ alloc_type = "stupid";
+ sp = strstr(s, "StupidAllocator");
+ }
+ if (sp) {
+ // 2019-05-30 03:23:43.460 7f889a5edf00 10 fbmap_alloc 0x5642ed36e900 BitmapAllocator 0x15940000000/100000
+ std::cout << s << std::endl;
+ if (init_done) {
+ std::cerr << "error: duplicate init: " << s << std::endl;
+ return -1;
+ }
+ uint64_t total, alloc_unit;
+ strtok(sp, " /");
+ token = strtok(nullptr, " /");
+ ceph_assert(token);
+ total = strtoul(token, nullptr, 16);
+ token = strtok(nullptr, " /");
+ ceph_assert(token);
+ alloc_unit = strtoul(token, nullptr, 16);
+ if (total == 0 || alloc_unit == 0) {
+ std::cerr << "error: invalid init: " << s << std::endl;
+ return -1;
+ }
+ alloc.reset(Allocator::create(g_ceph_context, alloc_type, total,
+ alloc_unit));
+ owned_by_app.insert(0, total);
+
+ init_done = true;
+ continue;
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+int replay_free_dump_and_apply_raw(
+ char* fname,
+ std::function<void (
+ std::string_view,
+ int64_t,
+ int64_t,
+ std::string_view)> create,
+ std::function<void (uint64_t, uint64_t)> add_ext)
+{
+ string alloc_type;
+ string alloc_name;
+ uint64_t capacity = 0;
+ uint64_t alloc_unit = 0;
+
+ JSONParser p;
+ std::cout << "parsing..." << std::endl;
+ bool b = p.parse(fname);
+ if (!b) {
+ std::cerr << "Failed to parse json: " << fname << std::endl;
+ return -1;
+ }
+
+ JSONObj::data_val v;
+ ceph_assert(p.is_object());
+
+ auto *o = p.find_obj("alloc_type");
+ ceph_assert(o);
+ alloc_type = o->get_data_val().str;
+
+ o = p.find_obj("alloc_name");
+ ceph_assert(o);
+ alloc_name = o->get_data_val().str;
+
+ o = p.find_obj("capacity");
+ ceph_assert(o);
+ decode_json_obj(capacity, o);
+ o = p.find_obj("alloc_unit");
+ ceph_assert(o);
+ decode_json_obj(alloc_unit, o);
+
+ int fd = -1;
+ o = p.find_obj("extents_file");
+ if (o) {
+ string filename = o->get_data_val().str;
+ fd = open(filename.c_str(), O_RDONLY);
+ if (fd < 0) {
+ std::cerr << "error: unable to open extents file: " << filename
+ << ", " << cpp_strerror(-errno)
+ << std::endl;
+ return -1;
+ }
+ } else {
+ o = p.find_obj("extents");
+ ceph_assert(o);
+ ceph_assert(o->is_array());
+ }
+ std::cout << "parsing completed!" << std::endl;
+
+ create(alloc_type, capacity, alloc_unit, alloc_name);
+ int r = 0;
+ if (fd < 0) {
+ auto it = o->find_first();
+ while (!it.end()) {
+ auto *item_obj = *it;
+ uint64_t offset = 0;
+ uint64_t length = 0;
+ string offset_str, length_str;
+
+ bool b = JSONDecoder::decode_json("offset", offset_str, item_obj);
+ ceph_assert(b);
+ b = JSONDecoder::decode_json("length", length_str, item_obj);
+ ceph_assert(b);
+
+ char* p;
+ offset = strtol(offset_str.c_str(), &p, 16);
+ length = strtol(length_str.c_str(), &p, 16);
+
+ // intentionally skip/trim entries that are above the capacity,
+ // just to be able to "shrink" allocator by editing that field
+ if (offset < capacity) {
+ if (offset + length > capacity) {
+ length = offset + length - capacity;
+ }
+ add_ext(offset, length);
+ }
+ ++it;
+ }
+ } else {
+ bufferlist bl;
+ char buf[4096];
+ do {
+ r = read(fd, buf, sizeof(buf));
+ if (r > 0) {
+ bl.append(buf, r);
+ }
+ } while(r > 0);
+ if (r < 0) {
+ std::cerr << "error: error reading from extents file: "
+ << cpp_strerror(-errno)
+ << std::endl;
+ } else {
+ auto p = bl.cbegin();
+ binary_alloc_map_t amap;
+ try {
+ decode(amap, p);
+ for (auto p : amap.free_extents) {
+ add_ext(p.first, p.second);
+ }
+ } catch (ceph::buffer::error& e) {
+ std::cerr << __func__ << " unable to decode extents "
+ << ": " << e.what()
+ << std::endl;
+ r = -1;
+ }
+ }
+ close(fd);
+ }
+ return r;
+}
+
+/*
+* This replays allocator dump (in JSON) reported by
+ "ceph daemon <osd> bluestore allocator dump <name>"
+ command and applies custom method to it
+*/
+int replay_free_dump_and_apply(char* fname,
+ std::function<int (Allocator*, const string& aname)> fn)
+{
+ unique_ptr<Allocator> alloc;
+ auto create_fn = [&](std::string_view alloc_type,
+ int64_t capacity,
+ int64_t alloc_unit,
+ std::string_view alloc_name) {
+ alloc.reset(
+ Allocator::create(
+ g_ceph_context, alloc_type, capacity, alloc_unit, 0, 0, alloc_name));
+ };
+ auto add_fn = [&](uint64_t offset,
+ uint64_t len) {
+ alloc->init_add_free(offset, len);
+ };
+ int r = replay_free_dump_and_apply_raw(
+ fname,
+ create_fn,
+ add_fn);
+ if (r == 0) {
+ r = fn(alloc.get(), alloc->get_name());
+ }
+
+ return r;
+}
+
+void dump_alloc(Allocator* alloc, const string& aname)
+{
+ AdminSocket* admin_socket = g_ceph_context->get_admin_socket();
+ ceph_assert(admin_socket);
+
+ ceph::bufferlist in, out;
+ ostringstream err;
+
+ string cmd = "{\"prefix\": \"bluestore allocator dump " + aname + "\"}";
+ auto r = admin_socket->execute_command(
+ { cmd },
+ in, err, &out);
+ if (r != 0) {
+ cerr << "failure querying: " << cpp_strerror(r) << std::endl;
+ }
+ else {
+ std::cout << std::string(out.c_str(), out.length()) << std::endl;
+ }
+}
+
+int export_as_binary(char* fname, char* target_fname)
+{
+ int fd = creat(target_fname, 0);
+ if (fd < 0) {
+ std::cerr << "error: unable to open target file: " << target_fname
+ << ", " << cpp_strerror(-errno)
+ << std::endl;
+ return -1;
+ }
+
+ binary_alloc_map_t amap;
+ auto dummy_create_fn =
+ [&](std::string_view alloc_type,
+ int64_t capacity,
+ int64_t alloc_unit,
+ std::string_view alloc_name) {
+ };
+ auto add_fn = [&](uint64_t offset,
+ uint64_t len) {
+ amap.free_extents.emplace_back(offset, len);
+ };
+ int r = replay_free_dump_and_apply_raw(
+ fname,
+ dummy_create_fn,
+ add_fn);
+ if (r == 0) {
+ bufferlist out;
+ ceph::encode(amap, out);
+ auto w = write(fd, out.c_str(), out.length());
+ if (w < 1) {
+ std::cerr << "error: unable to open target file: " << target_fname
+ << ", " << cpp_strerror(-errno)
+ << std::endl;
+ }
+ }
+ close(fd);
+ return r;
+}
+
+int check_duplicates(char* fname)
+{
+ interval_set<uint64_t> free_extents;
+ interval_set<uint64_t> invalid_extentsA;
+ interval_set<uint64_t> invalid_extentsB;
+ auto dummy_create_fn =
+ [&](std::string_view alloc_type,
+ int64_t capacity,
+ int64_t alloc_unit,
+ std::string_view alloc_name) {
+ };
+ size_t errors = 0;
+ size_t pos = 0;
+ size_t first_err_pos = 0;
+ auto add_fn = [&](uint64_t offset,
+ uint64_t len) {
+ ++pos;
+ if (free_extents.intersects(offset, len)) {
+ invalid_extentsB.insert(offset, len);
+ ++errors;
+ if (first_err_pos == 0) {
+ first_err_pos = pos;
+ }
+ } else {
+ free_extents.insert(offset, len);
+ }
+ };
+ int r = replay_free_dump_and_apply_raw(
+ fname,
+ dummy_create_fn,
+ add_fn);
+ if (r < 0) {
+ return r;
+ }
+ pos = 0;
+ auto add_fn2 = [&](uint64_t offset,
+ uint64_t len) {
+ ++pos;
+ if (pos < first_err_pos) {
+ if (invalid_extentsB.intersects(offset, len)) {
+ invalid_extentsA.insert(offset, len);
+ }
+ }
+ };
+ r = replay_free_dump_and_apply_raw(
+ fname,
+ dummy_create_fn,
+ add_fn2);
+ ceph_assert(r >= 0);
+ auto itA = invalid_extentsA.begin();
+ auto itB = invalid_extentsB.begin();
+ while (itA != invalid_extentsA.end()) {
+ std::cerr << "error: overlapping extents: " << std::hex
+ << itA.get_start() << "~" << itA.get_end() - itA.get_start()
+ << " vs.";
+ while (itB != invalid_extentsB.end() &&
+ itB.get_start() >= itA.get_start() &&
+ itB.get_end() <= itA.get_end()) {
+ std::cerr << " " << itB.get_start() << "~" << itB.get_end() - itB.get_start();
+ ++itB;
+ }
+ std::cerr << std::dec << std::endl;
+ ++itA;
+ }
+ return r >= 0 ? errors != 0 : r;
+}
+
+int main(int argc, char **argv)
+{
+ vector<const char*> args;
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ if (argc < 3) {
+ usage(argv[0]);
+ return 1;
+ }
+ if (strcmp(argv[2], "raw_duplicates") == 0) {
+ return replay_and_check_for_duplicate(argv[1]);
+ } else if (strcmp(argv[2], "free_dump") == 0) {
+ return replay_free_dump_and_apply(argv[1],
+ [&](Allocator* a, const string& aname) {
+ ceph_assert(a);
+ std::cout << "Fragmentation:" << a->get_fragmentation()
+ << std::endl;
+ std::cout << "Fragmentation score:" << a->get_fragmentation_score()
+ << std::endl;
+ std::cout << "Free:" << std::hex << a->get_free() << std::dec
+ << std::endl;
+ {
+ // stub to implement various testing stuff on properly initialized allocator
+ // e.g. one can dump allocator back via dump_alloc(a, aname);
+ }
+ return 0;
+ });
+ } else if (strcmp(argv[2], "try_alloc") == 0) {
+ if (argc < 6) {
+ std::cerr << "Error: insufficient arguments for \"try_alloc\" operation."
+ << std::endl;
+ usage(argv[0]);
+ return 1;
+ }
+ auto count = strtoul(argv[3], nullptr, 10);
+ auto want = strtoul(argv[4], nullptr, 10);
+ auto alloc_unit = strtoul(argv[5], nullptr, 10);
+
+ return replay_free_dump_and_apply(argv[1],
+ [&](Allocator* a, const string& aname) {
+ ceph_assert(a);
+ std::cout << "Fragmentation:" << a->get_fragmentation()
+ << std::endl;
+ std::cout << "Fragmentation score:" << a->get_fragmentation_score()
+ << std::endl;
+ std::cout << "Free:" << std::hex << a->get_free() << std::dec
+ << std::endl;
+ {
+ PExtentVector extents;
+ for(size_t i = 0; i < count; i++) {
+ extents.clear();
+ auto r = a->allocate(want, alloc_unit, 0, &extents);
+ if (r < 0) {
+ std::cerr << "Error: allocation failure at step:" << i + 1
+ << ", ret = " << r << std::endl;
+ return -1;
+ }
+ }
+ }
+ std::cout << "Successfully allocated: " << count << " * " << want
+ << ", unit:" << alloc_unit << std::endl;
+ return 0;
+ });
+ } else if (strcmp(argv[2], "replay_alloc") == 0) {
+ if (argc < 4) {
+ std::cerr << "Error: insufficient arguments for \"replay_alloc\" option."
+ << std::endl;
+ usage_replay_alloc(argv[0]);
+ return 1;
+ }
+ return replay_free_dump_and_apply(argv[1],
+ [&](Allocator *a, const string &aname) {
+ ceph_assert(a);
+ std::cout << "Fragmentation:" << a->get_fragmentation()
+ << std::endl;
+ std::cout << "Fragmentation score:" << a->get_fragmentation_score()
+ << std::endl;
+ std::cout << "Free:" << std::hex << a->get_free() << std::dec
+ << std::endl;
+ {
+ /* replay a set of allocation requests */
+ char s[4096];
+
+ FILE *f_alloc_list = fopen(argv[3], "r");
+ if (!f_alloc_list) {
+ std::cerr << "error: unable to open " << argv[3] << std::endl;
+ return -1;
+ }
+
+ /* Replay user specified number of times to simulate extended activity
+ * Defaults to 1 replay.
+ */
+ auto replay_count = 1;
+ if (argc == 5) {
+ replay_count = atoi(argv[4]);
+ }
+
+ for (auto i = 0; i < replay_count; ++i) {
+ while (fgets(s, sizeof(s), f_alloc_list) != nullptr) {
+ /* parse allocation request */
+ uint64_t want = 0, unit = 0, max = 0, hint = 0;
+
+ if (std::sscanf(s, "%ji %ji %ji %ji", &want, &unit, &max, &hint) < 2)
+ {
+ cerr << "Error: malformed allocation request:" << std::endl;
+ cerr << s << std::endl;
+ /* do not attempt to allocate a malformed request */
+ continue;
+ }
+
+ /* timestamp for allocation start */
+ auto t0 = ceph::mono_clock::now();
+
+ /* allocate */
+ PExtentVector extents;
+ auto r = a->allocate(want, unit, max, hint, &extents);
+ if (r < 0) {
+ /* blind replays of allocations may run out of space, provide info for easy confirmation */
+ std::cerr << "Error: allocation failure code: " << r
+ << " requested want/unit/max/hint (hex): " << std::hex
+ << want << "/" << unit << "/" << max << "/" << hint
+ << std::dec << std::endl;
+ std::cerr << "Fragmentation:" << a->get_fragmentation()
+ << std::endl;
+ std::cerr << "Fragmentation score:" << a->get_fragmentation_score()
+ << std::endl;
+ std::cerr << "Free:" << std::hex << a->get_free() << std::dec
+ << std::endl;
+ /* return 0 if the allocator ran out of space */
+ if (r == -ENOSPC) {
+ return 0;
+ }
+ return -1;
+ }
+
+ /* Outputs the allocation's duration in nanoseconds and the allocation request parameters */
+ std::cout << "Duration (ns): " << (ceph::mono_clock::now() - t0).count()
+ << " want/unit/max/hint (hex): " << std::hex
+ << want << "/" << unit << "/" << max << "/" << hint
+ << std::dec << std::endl;
+
+ /* Do not release. */
+ //alloc->release(extents);
+ extents.clear();
+ }
+ fseek(f_alloc_list, 0, SEEK_SET);
+ }
+ fclose(f_alloc_list);
+ std::cout << "Fragmentation:" << a->get_fragmentation()
+ << std::endl;
+ std::cout << "Fragmentation score:" << a->get_fragmentation_score()
+ << std::endl;
+ std::cout << "Free:" << std::hex << a->get_free() << std::dec
+ << std::endl;
+ }
+ return 0;
+ });
+ } else if (strcmp(argv[2], "export_binary") == 0) {
+ return export_as_binary(argv[1], argv[3]);
+ } else if (strcmp(argv[2], "duplicates") == 0) {
+ return check_duplicates(argv[1]);
+ }
+}
diff --git a/src/test/objectstore/fastbmap_allocator_test.cc b/src/test/objectstore/fastbmap_allocator_test.cc
new file mode 100644
index 000000000..710b3798f
--- /dev/null
+++ b/src/test/objectstore/fastbmap_allocator_test.cc
@@ -0,0 +1,1145 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "os/bluestore/fastbmap_allocator_impl.h"
+
+class TestAllocatorLevel01 : public AllocatorLevel01Loose
+{
+public:
+ void init(uint64_t capacity, uint64_t alloc_unit)
+ {
+ _init(capacity, alloc_unit);
+ }
+ interval_t allocate_l1_cont(uint64_t length, uint64_t min_length,
+ uint64_t pos_start, uint64_t pos_end)
+ {
+ return _allocate_l1_contiguous(length, min_length, 0, pos_start, pos_end);
+ }
+ void free_l1(const interval_t& r)
+ {
+ _free_l1(r.offset, r.length);
+ }
+};
+
+class TestAllocatorLevel02 : public AllocatorLevel02<AllocatorLevel01Loose>
+{
+public:
+ void init(uint64_t capacity, uint64_t alloc_unit)
+ {
+ _init(capacity, alloc_unit);
+ }
+ void allocate_l2(uint64_t length, uint64_t min_length,
+ uint64_t* allocated0,
+ interval_vector_t* res)
+ {
+ uint64_t allocated = 0;
+ uint64_t hint = 0; // trigger internal l2 hint support
+ _allocate_l2(length, min_length, 0, hint, &allocated, res);
+ *allocated0 += allocated;
+ }
+ void free_l2(const interval_vector_t& r)
+ {
+ _free_l2(r);
+ }
+ void mark_free(uint64_t o, uint64_t len)
+ {
+ _mark_free(o, len);
+ }
+ void mark_allocated(uint64_t o, uint64_t len)
+ {
+ _mark_allocated(o, len);
+ }
+};
+
+const uint64_t _1m = 1024 * 1024;
+const uint64_t _2m = 2 * 1024 * 1024;
+
+TEST(TestAllocatorLevel01, test_l1)
+{
+ TestAllocatorLevel01 al1;
+ uint64_t num_l1_entries = 3 * 256;
+ uint64_t capacity = num_l1_entries * 512 * 4096;
+ al1.init(capacity, 0x1000);
+ ASSERT_EQ(capacity, al1.debug_get_free());
+
+ auto i1 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i1.offset, 0u);
+ ASSERT_EQ(i1.length, 0x1000u);
+ ASSERT_EQ(capacity - 0x1000, al1.debug_get_free());
+
+ auto i2 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 0x1000u);
+ ASSERT_EQ(i2.length, 0x1000u);
+ al1.free_l1(i2);
+ al1.free_l1(i1);
+ i1 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i1.offset, 0u);
+ ASSERT_EQ(i1.length, 0x1000u);
+ i2 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 0x1000u);
+ ASSERT_EQ(i2.length, 0x1000u);
+ al1.free_l1(i1);
+ al1.free_l1(i2);
+
+ i1 = al1.allocate_l1_cont(0x2000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i1.offset, 0u);
+ ASSERT_EQ(i1.length, 0x2000u);
+
+ i2 = al1.allocate_l1_cont(0x3000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 0x2000u);
+ ASSERT_EQ(i2.length, 0x3000u);
+
+ al1.free_l1(i1);
+ al1.free_l1(i2);
+
+ i1 = al1.allocate_l1_cont(0x2000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i1.offset, 0u);
+ ASSERT_EQ(i1.length, 0x2000u);
+
+ i2 = al1.allocate_l1_cont(2 * 1024 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 2u * 1024u * 1024u);
+ ASSERT_EQ(i2.length, 2u * 1024u * 1024u);
+
+ al1.free_l1(i1);
+ i1 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i1.offset, 0u);
+ ASSERT_EQ(i1.length, 1024u * 1024u);
+
+ auto i3 = al1.allocate_l1_cont(1024 * 1024 + 0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i3.offset, 2u * 2u * 1024u * 1024u);
+ ASSERT_EQ(i3.length, 1024u * 1024u + 0x1000u);
+
+ // here we have the following layout:
+ // Alloc: 0~1M, 2M~2M, 4M~1M+4K
+ // Free: 1M~1M, 4M+4K ~ 2M-4K, 6M ~...
+ //
+ auto i4 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(1 * 1024 * 1024u, i4.offset);
+ ASSERT_EQ(1024 * 1024u, i4.length);
+ al1.free_l1(i4);
+
+ i4 = al1.allocate_l1_cont(1024 * 1024 - 0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i4.offset, 5u * 1024u * 1024u + 0x1000u);
+ ASSERT_EQ(i4.length, 1024u * 1024u - 0x1000u);
+ al1.free_l1(i4);
+
+ i4 = al1.allocate_l1_cont(1024 * 1024 + 0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i4.offset, 6u * 1024u * 1024u);
+ //ASSERT_EQ(i4.offset, 5 * 1024 * 1024 + 0x1000);
+ ASSERT_EQ(i4.length, 1024u * 1024u + 0x1000u);
+
+ al1.free_l1(i1);
+ al1.free_l1(i2);
+ al1.free_l1(i3);
+ al1.free_l1(i4);
+
+ i1 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i1.offset, 0u);
+ ASSERT_EQ(i1.length, 1024u * 1024u);
+
+ i2 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 1u * 1024u * 1024u);
+ ASSERT_EQ(i2.length, 1024u * 1024u);
+
+ i3 = al1.allocate_l1_cont(512 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i3.offset, 2u * 1024u * 1024u);
+ ASSERT_EQ(i3.length, 512u * 1024u);
+
+ i4 = al1.allocate_l1_cont(1536 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i4.offset, (2u * 1024u + 512u) * 1024u);
+ ASSERT_EQ(i4.length, 1536u * 1024u);
+ // making a hole 1.5 Mb length
+ al1.free_l1(i2);
+ al1.free_l1(i3);
+ // and trying to fill it
+ i2 = al1.allocate_l1_cont(1536 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 1024u * 1024u);
+ ASSERT_EQ(i2.length, 1536u * 1024u);
+
+ al1.free_l1(i2);
+ // and trying to fill it partially
+ i2 = al1.allocate_l1_cont(1528 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 1024u * 1024u);
+ ASSERT_EQ(i2.length, 1528u * 1024u);
+
+ i3 = al1.allocate_l1_cont(8 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i3.offset, 2552u * 1024u);
+ ASSERT_EQ(i3.length, 8u * 1024u);
+
+ al1.free_l1(i2);
+ // here we have the following layout:
+ // Alloc: 0~1M, 2552K~8K, num_l1_entries0K~1.5M
+ // Free: 1M~1528K, 4M ~...
+ //
+ i2 = al1.allocate_l1_cont(1536 * 1024, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, 4u * 1024u * 1024u);
+ ASSERT_EQ(i2.length, 1536u * 1024u);
+
+ al1.free_l1(i1);
+ al1.free_l1(i2);
+ al1.free_l1(i3);
+ al1.free_l1(i4);
+ ASSERT_EQ(capacity, al1.debug_get_free());
+
+ for (uint64_t i = 0; i < capacity; i += _2m) {
+ i1 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries);
+ ASSERT_EQ(i1.offset, i);
+ ASSERT_EQ(i1.length, _2m);
+ }
+ ASSERT_EQ(0u, al1.debug_get_free());
+ i2 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries);
+ ASSERT_EQ(i2.length, 0u);
+ ASSERT_EQ(0u, al1.debug_get_free());
+
+ al1.free_l1(i1);
+ i2 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries);
+ ASSERT_EQ(i2, i1);
+ al1.free_l1(i2);
+ i2 = al1.allocate_l1_cont(_1m, _1m, 0, num_l1_entries);
+ ASSERT_EQ(i2.offset, i1.offset);
+ ASSERT_EQ(i2.length, _1m);
+
+ i3 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries);
+ ASSERT_EQ(i3.length, 0u);
+
+ i3 = al1.allocate_l1_cont(_2m, _1m, 0, num_l1_entries);
+ ASSERT_EQ(i3.length, _1m);
+
+ i4 = al1.allocate_l1_cont(_2m, _1m, 0, num_l1_entries);
+ ASSERT_EQ(i4.length, 0u);
+
+ al1.free_l1(i2);
+ i2 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries);
+ ASSERT_EQ(i2.length, 0u);
+
+ i2 = al1.allocate_l1_cont(_2m, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.length, _1m);
+
+ al1.free_l1(i2);
+ al1.free_l1(i3);
+ ASSERT_EQ(_2m, al1.debug_get_free());
+
+ i1 = al1.allocate_l1_cont(_2m - 3 * 0x1000, 0x1000, 0, num_l1_entries);
+ i2 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries);
+ i3 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries);
+ i4 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(0u, al1.debug_get_free());
+
+ al1.free_l1(i2);
+ al1.free_l1(i4);
+
+ i2 = al1.allocate_l1_cont(0x4000, 0x2000, 0, num_l1_entries);
+ ASSERT_EQ(i2.length, 0u);
+ i2 = al1.allocate_l1_cont(0x4000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i2.length, 0x1000u);
+
+ al1.free_l1(i3);
+ i3 = al1.allocate_l1_cont(0x6000, 0x3000, 0, num_l1_entries);
+ ASSERT_EQ(i3.length, 0u);
+ i3 = al1.allocate_l1_cont(0x6000, 0x1000, 0, num_l1_entries);
+ ASSERT_EQ(i3.length, 0x2000u);
+ ASSERT_EQ(0u, al1.debug_get_free());
+
+ std::cout << "Done L1" << std::endl;
+}
+
+TEST(TestAllocatorLevel01, test_l2)
+{
+ TestAllocatorLevel02 al2;
+ uint64_t num_l2_entries = 64;// *512;
+ uint64_t capacity = num_l2_entries * 256 * 512 * 4096;
+ al2.init(capacity, 0x1000);
+ std::cout << "Init L2" << std::endl;
+
+ uint64_t allocated1 = 0;
+ interval_vector_t a1;
+ al2.allocate_l2(0x2000, 0x2000, &allocated1, &a1);
+ ASSERT_EQ(allocated1, 0x2000u);
+ ASSERT_EQ(a1[0].offset, 0u);
+ ASSERT_EQ(a1[0].length, 0x2000u);
+
+ // limit query range in debug_get_free for the sake of performance
+ ASSERT_EQ(0x2000u, al2.debug_get_allocated(0, 1));
+ ASSERT_EQ(0u, al2.debug_get_allocated(1, 2));
+
+ uint64_t allocated2 = 0;
+ interval_vector_t a2;
+ al2.allocate_l2(0x2000, 0x2000, &allocated2, &a2);
+ ASSERT_EQ(allocated2, 0x2000u);
+ ASSERT_EQ(a2[0].offset, 0x2000u);
+ ASSERT_EQ(a2[0].length, 0x2000u);
+ // limit query range in debug_get_free for the sake of performance
+ ASSERT_EQ(0x4000u, al2.debug_get_allocated(0, 1));
+ ASSERT_EQ(0u, al2.debug_get_allocated(1, 2));
+
+ al2.free_l2(a1);
+
+ allocated2 = 0;
+ a2.clear();
+ al2.allocate_l2(0x1000, 0x1000, &allocated2, &a2);
+ ASSERT_EQ(allocated2, 0x1000u);
+ ASSERT_EQ(a2[0].offset, 0x0000u);
+ ASSERT_EQ(a2[0].length, 0x1000u);
+ // limit query range in debug_get_free for the sake of performance
+ ASSERT_EQ(0x3000u, al2.debug_get_allocated(0, 1));
+ ASSERT_EQ(0u, al2.debug_get_allocated(1, 2));
+
+ uint64_t allocated3 = 0;
+ interval_vector_t a3;
+ al2.allocate_l2(0x2000, 0x1000, &allocated3, &a3);
+ ASSERT_EQ(allocated3, 0x2000u);
+ ASSERT_EQ(a3.size(), 2u);
+ ASSERT_EQ(a3[0].offset, 0x1000u);
+ ASSERT_EQ(a3[0].length, 0x1000u);
+ ASSERT_EQ(a3[1].offset, 0x4000u);
+ ASSERT_EQ(a3[1].length, 0x1000u);
+ // limit query range in debug_get_free for the sake of performance
+ ASSERT_EQ(0x5000u, al2.debug_get_allocated(0, 1));
+ ASSERT_EQ(0u, al2.debug_get_allocated(1, 2));
+ {
+ interval_vector_t r;
+ r.emplace_back(0x0, 0x5000);
+ al2.free_l2(r);
+ }
+
+ a3.clear();
+ allocated3 = 0;
+ al2.allocate_l2(_1m, _1m, &allocated3, &a3);
+ ASSERT_EQ(a3.size(), 1u);
+ ASSERT_EQ(a3[0].offset, 0u);
+ ASSERT_EQ(a3[0].length, _1m);
+
+ al2.free_l2(a3);
+
+ a3.clear();
+ allocated3 = 0;
+ al2.allocate_l2(4 * _1m, _1m, &allocated3, &a3);
+ ASSERT_EQ(a3.size(), 1u);
+ ASSERT_EQ(a3[0].offset, 0u);
+ ASSERT_EQ(a3[0].length, 4 * _1m);
+
+ al2.free_l2(a3);
+
+#ifndef _DEBUG
+ for (uint64_t i = 0; i < capacity; i += 0x1000) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, 0x1000u);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "alloc1 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+#else
+ for (uint64_t i = 0; i < capacity; i += _2m) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_2m, _2m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, _2m);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "alloc1 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+#endif
+
+ ASSERT_EQ(0u, al2.debug_get_free());
+ for (uint64_t i = 0; i < capacity; i += _1m) {
+ interval_vector_t r;
+ r.emplace_back(i, _1m);
+ al2.free_l2(r);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "free1 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ ASSERT_EQ(capacity, al2.debug_get_free());
+
+ for (uint64_t i = 0; i < capacity; i += _1m) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, _1m);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, _1m);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "alloc2 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ ASSERT_EQ(0u, al2.debug_get_free());
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 0u);
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 0u);
+
+ for (uint64_t i = 0; i < capacity; i += 0x2000) {
+ interval_vector_t r;
+ r.emplace_back(i, 0x1000);
+ al2.free_l2(r);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "free2 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ ASSERT_EQ(capacity / 2, al2.debug_get_free());
+
+ // unable to allocate due to fragmentation
+ al2.allocate_l2(_1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 0u);
+
+ for (uint64_t i = 0; i < capacity; i += 2 * _1m) {
+ a4.clear();
+ allocated4 = 0;
+ al2.allocate_l2(_1m, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), _1m / 0x1000);
+ ASSERT_EQ(allocated4, _1m);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, 0x1000u);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "alloc3 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ ASSERT_EQ(0u, al2.debug_get_free());
+
+ std::cout << "Done L2" << std::endl;
+}
+
+TEST(TestAllocatorLevel01, test_l2_huge)
+{
+ TestAllocatorLevel02 al2;
+ uint64_t num_l2_entries = 4 * 512;
+ uint64_t capacity = num_l2_entries * 256 * 512 * 4096; // 1 TB
+ al2.init(capacity, 0x1000);
+ std::cout << "Init L2 Huge" << std::endl;
+
+ for (uint64_t i = 0; i < capacity; i += _1m) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, 0x1000u);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, 0x1000u);
+
+ allocated4 = 0;
+ a4.clear();
+ al2.allocate_l2(_1m - 0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, _1m - 0x1000);
+ ASSERT_EQ(a4[0].offset, i + 0x1000);
+ ASSERT_EQ(a4[0].length, _1m - 0x1000);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "allocH " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ for (uint64_t i = 0; i < capacity; i += _1m) {
+ interval_vector_t a4;
+ a4.emplace_back(i, 0x1000);
+ al2.free_l2(a4);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "freeH1 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ {
+ std::cout << "Try" << std::endl;
+ time_t t = time(NULL);
+ for (int i = 0; i < 10; ++i) {
+ uint64_t allocated = 0;
+ interval_vector_t a;
+ al2.allocate_l2(0x2000, 0x2000, &allocated, &a);
+ ASSERT_EQ(a.size(), 0u);
+ }
+ std::cout << "End try in " << time(NULL) - t << " seconds" << std::endl;
+ }
+ {
+ std::cout << "Try" << std::endl;
+ time_t t = time(NULL);
+ for (int i = 0; i < 10; ++i) {
+ uint64_t allocated = 0;
+ interval_vector_t a;
+ al2.allocate_l2(_2m, _2m, &allocated, &a);
+ ASSERT_EQ(a.size(), 0u);
+ }
+ std::cout << "End try in " << time(NULL) - t << " seconds" << std::endl;
+ }
+
+ ASSERT_EQ((capacity / _1m) * 0x1000, al2.debug_get_free());
+
+ std::cout << "Done L2 Huge" << std::endl;
+}
+
+TEST(TestAllocatorLevel01, test_l2_unaligned)
+{
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t num_l2_entries = 3;
+ uint64_t capacity = num_l2_entries * 256 * 512 * 4096; // 3x512 MB
+ al2.init(capacity, 0x1000);
+ std::cout << "Init L2 Unaligned" << std::endl;
+
+ for (uint64_t i = 0; i < capacity; i += _1m / 2) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m / 2, _1m / 2, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, _1m / 2);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, _1m / 2);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "allocU " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ ASSERT_EQ(0u, al2.debug_get_free());
+ {
+ // no space to allocate
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 0u);
+ }
+ }
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t capacity = 500 * 512 * 4096; // 500x2 MB
+ al2.init(capacity, 0x1000);
+ std::cout << ("Init L2 Unaligned2\n");
+ for (uint64_t i = 0; i < capacity; i += _1m / 2) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m / 2, _1m / 2, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, _1m / 2);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, _1m / 2);
+ if (0 == (i % (1 * 1024 * _1m))) {
+ std::cout << "allocU2 " << i / 1024 / 1024 << " mb of "
+ << capacity / 1024 / 1024 << std::endl;
+ }
+ }
+ ASSERT_EQ(0u, al2.debug_get_free());
+ {
+ // no space to allocate
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 0u);
+ }
+ }
+
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t capacity = 100 * 512 * 4096 + 127 * 4096;
+ al2.init(capacity, 0x1000);
+ std::cout << "Init L2 Unaligned2" << std::endl;
+ for (uint64_t i = 0; i < capacity; i += 0x1000) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, 0x1000u);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, 0x1000u);
+ }
+ ASSERT_EQ(0u, al2.debug_get_free());
+ {
+ // no space to allocate
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 0u);
+ }
+ }
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t capacity = 3 * 4096;
+ al2.init(capacity, 0x1000);
+ std::cout << "Init L2 Unaligned2" << std::endl;
+ for (uint64_t i = 0; i < capacity; i += 0x1000) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, 0x1000u);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, 0x1000u);
+ }
+ ASSERT_EQ(0u, al2.debug_get_free());
+ {
+ // no space to allocate
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 0u);
+ }
+ }
+
+ std::cout << "Done L2 Unaligned" << std::endl;
+}
+
+TEST(TestAllocatorLevel01, test_l2_contiguous_alignment)
+{
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t num_l2_entries = 3;
+ uint64_t capacity = num_l2_entries * 256 * 512 * 4096; // 3x512 MB
+ uint64_t num_chunks = capacity / 4096;
+ al2.init(capacity, 4096);
+ std::cout << "Init L2 cont aligned" << std::endl;
+
+ std::map<size_t, size_t> bins_overall;
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 1u);
+// std::cout<<bins_overall.begin()->first << std::endl;
+ ASSERT_EQ(bins_overall[cbits(num_chunks) - 1], 1u);
+
+ for (uint64_t i = 0; i < capacity / 2; i += _1m) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, _1m);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, _1m);
+ }
+ ASSERT_EQ(capacity / 2, al2.debug_get_free());
+
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+
+ {
+ // Original free space disposition (start chunk, count):
+ // <NC/2, NC/2>
+ size_t to_release = 2 * _1m + 0x1000;
+ // release 2M + 4K at the beginning
+ interval_vector_t r;
+ r.emplace_back(0, to_release);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits(to_release / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <0, 513>, <NC / 2, NC / 2>
+ // allocate 4K within the deallocated range
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, 0x1000u);
+ ASSERT_EQ(a4[0].offset, 0u);
+ ASSERT_EQ(a4[0].length, 0x1000u);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits(2 * _1m / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <1, 512>, <NC / 2, NC / 2>
+ // allocate 1M - should go to offset 4096
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, _1m);
+ ASSERT_EQ(a4[0].offset, 4096);
+ ASSERT_EQ(a4[0].length, _1m);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits(_1m / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <257, 256>, <NC / 2, NC / 2>
+ // and allocate yet another 8K within the deallocated range
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x2000, 0x1000, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, 0x2000u);
+ ASSERT_EQ(a4[0].offset, _1m + 0x1000u);
+ ASSERT_EQ(a4[0].length, 0x2000u);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <259, 254>, <NC / 2, NC / 2>
+ // release 4K~1M
+ interval_vector_t r;
+ r.emplace_back(0x1000, _1m);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 3u);
+ //ASSERT_EQ(bins_overall[cbits((2 * _1m - 0x3000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(_1m / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <1, 257>, <259, 254>, <NC / 2, NC / 2>
+ // allocate 3M - should go to the first 1M chunk and @capacity/2
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(3 * _1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 2u);
+ ASSERT_EQ(allocated4, 3 * _1m);
+ ASSERT_EQ(a4[0].offset, 0x1000);
+ ASSERT_EQ(a4[0].length, _1m);
+ ASSERT_EQ(a4[1].offset, capacity / 2);
+ ASSERT_EQ(a4[1].length, 2 * _1m);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((num_chunks - 512) / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <259, 254>, <NC / 2 - 512, NC / 2 - 512>
+ // release allocated 1M in the first meg chunk except
+ // the first 4K chunk
+ interval_vector_t r;
+ r.emplace_back(0x1000, _1m);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 3u);
+ ASSERT_EQ(bins_overall[cbits(_1m / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((num_chunks - 512) / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <1, 256>, <259, 254>, <NC / 2 - 512, NC / 2 - 512>
+ // release 2M @(capacity / 2)
+ interval_vector_t r;
+ r.emplace_back(capacity / 2, 2 * _1m);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 3u);
+ ASSERT_EQ(bins_overall[cbits(_1m / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((num_chunks) / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <1, 256>, <259, 254>, <NC / 2, NC / 2>
+ // allocate 4x512K - should go to the second halves of
+ // the first and second 1M chunks and @(capacity / 2)
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(2 * _1m, _1m / 2, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 3u);
+ ASSERT_EQ(allocated4, 2 * _1m);
+ ASSERT_EQ(a4[1].offset, 0x1000);
+ ASSERT_EQ(a4[1].length, _1m);
+ ASSERT_EQ(a4[0].offset, _1m + 0x3000);
+ ASSERT_EQ(a4[0].length, _1m / 2);
+ ASSERT_EQ(a4[2].offset, capacity / 2);
+ ASSERT_EQ(a4[2].length, _1m / 2);
+
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000 - 0x80000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((num_chunks - 256) / 2) - 1], 1u);
+
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <387, 126>, <NC / 2 + 128, NC / 2 - 128>
+ // cleanup first 1536K except the last 4K chunk
+ interval_vector_t r;
+ r.emplace_back(0, _1m + _1m / 2 - 0x1000);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+
+ ASSERT_EQ(bins_overall.size(), 3u);
+ ASSERT_EQ(bins_overall[cbits((_1m + _1m / 2 - 0x1000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000 - 0x80000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((num_chunks - 256) / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <0, 383> <387, 126>, <NC / 2 + 128, NC / 2 - 128>
+ // release 512K @(capacity / 2)
+ interval_vector_t r;
+ r.emplace_back(capacity / 2, _1m / 2);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+
+ ASSERT_EQ(bins_overall.size(), 3u);
+ ASSERT_EQ(bins_overall[cbits((_1m + _1m / 2 - 0x1000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000 - 0x80000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <0, 383> <387, 126>, <NC / 2, NC / 2>
+ // allocate 132M (=33792*4096) = using 4M granularity should go to (capacity / 2)
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(132 * _1m, 4 * _1m , &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(a4[0].offset, capacity / 2);
+ ASSERT_EQ(a4[0].length, 132 * _1m);
+
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 3u);
+ ASSERT_EQ(bins_overall[cbits((_1m + _1m / 2 - 0x1000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits((_1m - 0x2000 - 0x80000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2 - 33792) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <0, 383> <387, 126>, <NC / 2 + 33792, NC / 2 - 33792>
+ // cleanup remaining 4*4K chunks in the first 2M
+ interval_vector_t r;
+ r.emplace_back(383 * 4096, 4 * 0x1000);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits((2 * _1m + 0x1000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2 - 33792) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <0, 513>, <NC / 2 + 33792, NC / 2 - 33792>
+ // release 132M @(capacity / 2)
+ interval_vector_t r;
+ r.emplace_back(capacity / 2, 132 * _1m);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits((2 * _1m + 0x1000) / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <0, 513>, <NC / 2, NC / 2>
+ // allocate 132M using 2M granularity should go to the first chunk and to
+ // (capacity / 2)
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(132 * _1m, 2 * _1m , &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 2u);
+ ASSERT_EQ(a4[0].offset, 0u);
+ ASSERT_EQ(a4[0].length, 2 * _1m);
+ ASSERT_EQ(a4[1].offset, capacity / 2);
+ ASSERT_EQ(a4[1].length, 130 * _1m);
+
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits(0)], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2 - 33792) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <512, 1>, <NC / 2 + 33792, NC / 2 - 33792>
+ // release 130M @(capacity / 2)
+ interval_vector_t r;
+ r.emplace_back(capacity / 2, 132 * _1m);
+ al2.free_l2(r);
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+
+ ASSERT_EQ(bins_overall.size(), 2u);
+ ASSERT_EQ(bins_overall[cbits(0)], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <512,1>, <NC / 2, NC / 2>
+ // release 4K~16K
+ // release 28K~32K
+ // release 68K~24K
+ interval_vector_t r;
+ r.emplace_back(0x1000, 0x4000);
+ r.emplace_back(0x7000, 0x8000);
+ r.emplace_back(0x11000, 0x6000);
+ al2.free_l2(r);
+
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+
+ ASSERT_EQ(bins_overall.size(), 4u);
+ ASSERT_EQ(bins_overall[cbits(0)], 1u);
+ ASSERT_EQ(bins_overall[cbits(0x4000 / 0x1000) - 1], 2u); // accounts both 0x4000 & 0x6000
+ ASSERT_EQ(bins_overall[cbits(0x8000 / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <1, 4>, <7, 8>, <17, 6> <512,1>, <NC / 2, NC / 2>
+ // allocate 80K using 16K granularity
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x14000, 0x4000, &allocated4, &a4);
+
+ ASSERT_EQ(a4.size(), 4);
+ ASSERT_EQ(a4[1].offset, 0x1000u);
+ ASSERT_EQ(a4[1].length, 0x4000u);
+ ASSERT_EQ(a4[0].offset, 0x7000u);
+ ASSERT_EQ(a4[0].length, 0x8000u);
+ ASSERT_EQ(a4[2].offset, 0x11000u);
+ ASSERT_EQ(a4[2].length, 0x4000u);
+ ASSERT_EQ(a4[3].offset, capacity / 2);
+ ASSERT_EQ(a4[3].length, 0x4000u);
+
+ bins_overall.clear();
+ al2.collect_stats(bins_overall);
+
+ ASSERT_EQ(bins_overall.size(), 3u);
+ ASSERT_EQ(bins_overall[cbits(0)], 1u);
+ ASSERT_EQ(bins_overall[cbits(0x2000 / 0x1000) - 1], 1u);
+ ASSERT_EQ(bins_overall[cbits(num_chunks / 2 - 1) - 1], 1u);
+ }
+ {
+ // Original free space disposition (start chunk, count):
+ // <21, 2> <512,1>, <NC / 2 + 1, NC / 2 - 1>
+ }
+ }
+ std::cout << "Done L2 cont aligned" << std::endl;
+}
+
+TEST(TestAllocatorLevel01, test_4G_alloc_bug)
+{
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t capacity = 0x8000 * _1m; // = 32GB
+ al2.init(capacity, 0x10000);
+ std::cout << "Init L2 cont aligned" << std::endl;
+
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u); // the bug caused no allocations here
+ ASSERT_EQ(allocated4, _1m);
+ ASSERT_EQ(a4[0].offset, 0u);
+ ASSERT_EQ(a4[0].length, _1m);
+ }
+}
+
+TEST(TestAllocatorLevel01, test_4G_alloc_bug2)
+{
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t capacity = 0x8000 * _1m; // = 32GB
+ al2.init(capacity, 0x10000);
+
+ for (uint64_t i = 0; i < capacity; i += _1m) {
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(_1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 1u);
+ ASSERT_EQ(allocated4, _1m);
+ ASSERT_EQ(a4[0].offset, i);
+ ASSERT_EQ(a4[0].length, _1m);
+ }
+ ASSERT_EQ(0u , al2.debug_get_free());
+
+ interval_vector_t r;
+ r.emplace_back(0x5fec30000, 0x13d0000);
+ r.emplace_back(0x628000000, 0x80000000);
+ r.emplace_back(0x6a8000000, 0x80000000);
+ r.emplace_back(0x728100000, 0x70000);
+ al2.free_l2(r);
+
+ std::map<size_t, size_t> bins_overall;
+ al2.collect_stats(bins_overall);
+
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(0x3e000000, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 2u);
+ ASSERT_EQ(allocated4, 0x3e000000u);
+ ASSERT_EQ(a4[0].offset, 0x5fec30000u);
+ ASSERT_EQ(a4[0].length, 0x1300000u);
+ ASSERT_EQ(a4[1].offset, 0x628000000u);
+ ASSERT_EQ(a4[1].length, 0x3cd00000u);
+ }
+}
+
+TEST(TestAllocatorLevel01, test_4G_alloc_bug3)
+{
+ {
+ TestAllocatorLevel02 al2;
+ uint64_t capacity = 0x8000 * _1m; // = 32GB
+ al2.init(capacity, 0x10000);
+ std::cout << "Init L2 cont aligned" << std::endl;
+
+ uint64_t allocated4 = 0;
+ interval_vector_t a4;
+ al2.allocate_l2(4096ull * _1m, _1m, &allocated4, &a4);
+ ASSERT_EQ(a4.size(), 2u); // allocator has to split into 2 allocations
+ ASSERT_EQ(allocated4, 4096ull * _1m);
+ ASSERT_EQ(a4[0].offset, 0u);
+ ASSERT_EQ(a4[0].length, 2048ull * _1m);
+ ASSERT_EQ(a4[1].offset, 2048ull * _1m);
+ ASSERT_EQ(a4[1].length, 2048ull * _1m);
+ }
+}
+
+TEST(TestAllocatorLevel01, test_claim_free_l2)
+{
+ TestAllocatorLevel02 al2;
+ uint64_t num_l2_entries = 64;// *512;
+ uint64_t capacity = num_l2_entries * 256 * 512 * 4096;
+ al2.init(capacity, 0x1000);
+ std::cout << "Init L2" << std::endl;
+
+ uint64_t max_available = 0x20000;
+ al2.mark_allocated(max_available, capacity - max_available);
+
+ uint64_t allocated1 = 0;
+ interval_vector_t a1;
+ al2.allocate_l2(0x2000, 0x2000, &allocated1, &a1);
+ ASSERT_EQ(allocated1, 0x2000u);
+ ASSERT_EQ(a1[0].offset, 0u);
+ ASSERT_EQ(a1[0].length, 0x2000u);
+
+ uint64_t allocated2 = 0;
+ interval_vector_t a2;
+ al2.allocate_l2(0x2000, 0x2000, &allocated2, &a2);
+ ASSERT_EQ(allocated2, 0x2000u);
+ ASSERT_EQ(a2[0].offset, 0x2000u);
+ ASSERT_EQ(a2[0].length, 0x2000u);
+
+ uint64_t allocated3 = 0;
+ interval_vector_t a3;
+ al2.allocate_l2(0x3000, 0x3000, &allocated3, &a3);
+ ASSERT_EQ(allocated3, 0x3000u);
+ ASSERT_EQ(a3[0].offset, 0x4000u);
+ ASSERT_EQ(a3[0].length, 0x3000u);
+
+ al2.free_l2(a1);
+ al2.free_l2(a3);
+ ASSERT_EQ(max_available - 0x2000, al2.debug_get_free());
+
+ auto claimed = al2.claim_free_to_right(0x4000);
+ ASSERT_EQ(max_available - 0x4000u, claimed);
+ ASSERT_EQ(0x2000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_right(0x4000);
+ ASSERT_EQ(0, claimed);
+ ASSERT_EQ(0x2000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_left(0x2000);
+ ASSERT_EQ(0x2000u, claimed);
+ ASSERT_EQ(0, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_left(0x2000);
+ ASSERT_EQ(0, claimed);
+ ASSERT_EQ(0, al2.debug_get_free());
+
+
+ al2.mark_free(0x3000, 0x4000);
+ ASSERT_EQ(0x4000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_right(0x7000);
+ ASSERT_EQ(0, claimed);
+ ASSERT_EQ(0x4000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_right(0x6000);
+ ASSERT_EQ(0x1000, claimed);
+ ASSERT_EQ(0x3000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_right(0x6000);
+ ASSERT_EQ(0, claimed);
+ ASSERT_EQ(0x3000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_left(0x3000);
+ ASSERT_EQ(0u, claimed);
+ ASSERT_EQ(0x3000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_left(0x4000);
+ ASSERT_EQ(0x1000, claimed);
+ ASSERT_EQ(0x2000, al2.debug_get_free());
+
+ // claiming on the right boundary
+ claimed = al2.claim_free_to_right(capacity);
+ ASSERT_EQ(0x0, claimed);
+ ASSERT_EQ(0x2000, al2.debug_get_free());
+
+ // extend allocator space up to 64M
+ auto max_available2 = 64 * 1024 * 1024;
+ al2.mark_free(max_available, max_available2 - max_available);
+ ASSERT_EQ(max_available2 - max_available + 0x2000, al2.debug_get_free());
+
+ // pin some allocations
+ al2.mark_allocated(0x400000 + 0x2000, 1000);
+ al2.mark_allocated(0x400000 + 0x5000, 1000);
+ al2.mark_allocated(0x400000 + 0x20000, 1000);
+ ASSERT_EQ(max_available2 - max_available - 0x1000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_left(0x403000);
+ ASSERT_EQ(0x0, claimed);
+
+ claimed = al2.claim_free_to_left(0x404000);
+ ASSERT_EQ(0x1000, claimed);
+ ASSERT_EQ(max_available2 - max_available - 0x2000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_left(max_available);
+ ASSERT_EQ(0, claimed);
+
+ claimed = al2.claim_free_to_left(0x400000);
+ ASSERT_EQ(0x3e0000, claimed);
+ ASSERT_EQ(max_available2 - max_available - 0x3e2000, al2.get_available());
+ ASSERT_EQ(max_available2 - max_available - 0x3e2000, al2.debug_get_free());
+
+ claimed = al2.claim_free_to_right(0x407000);
+ ASSERT_EQ(0x19000, claimed);
+ ASSERT_EQ(max_available2 - max_available - 0x3e2000 - 0x19000,
+ al2.get_available());
+ ASSERT_EQ(max_available2 - max_available - 0x3e2000 - 0x19000,
+ al2.debug_get_free());
+
+ claimed = al2.claim_free_to_right(0x407000);
+ ASSERT_EQ(0, claimed);
+
+ claimed = al2.claim_free_to_right(0x430000);
+ ASSERT_EQ(max_available2 - 0x430000, claimed);
+ ASSERT_EQ(0x15000,
+ al2.get_available());
+ ASSERT_EQ(0x15000,
+ al2.debug_get_free());
+}
diff --git a/src/test/objectstore/hybrid_allocator_test.cc b/src/test/objectstore/hybrid_allocator_test.cc
new file mode 100755
index 000000000..e43d28b28
--- /dev/null
+++ b/src/test/objectstore/hybrid_allocator_test.cc
@@ -0,0 +1,231 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "os/bluestore/HybridAllocator.h"
+
+class TestHybridAllocator : public HybridAllocator {
+public:
+ TestHybridAllocator(CephContext* cct,
+ int64_t device_size,
+ int64_t _block_size,
+ uint64_t max_entries,
+ const std::string& name) :
+ HybridAllocator(cct, device_size, _block_size,
+ max_entries,
+ name) {
+ }
+
+ uint64_t get_bmap_free() {
+ return get_bmap() ? get_bmap()->get_free() : 0;
+ }
+ uint64_t get_avl_free() {
+ return AvlAllocator::get_free();
+ }
+};
+
+const uint64_t _1m = 1024 * 1024;
+const uint64_t _4m = 4 * 1024 * 1024;
+
+TEST(HybridAllocator, basic)
+{
+ {
+ uint64_t block_size = 0x1000;
+ uint64_t capacity = 0x10000 * _1m; // = 64GB
+ TestHybridAllocator ha(g_ceph_context, capacity, block_size,
+ 4 * sizeof(range_seg_t), "test_hybrid_allocator");
+
+ ASSERT_EQ(0, ha.get_free());
+ ASSERT_EQ(0, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+
+ ha.init_add_free(0, _4m);
+ ASSERT_EQ(_4m, ha.get_free());
+ ASSERT_EQ(_4m, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+
+ ha.init_add_free(2 * _4m, _4m);
+ ASSERT_EQ(_4m * 2, ha.get_free());
+ ASSERT_EQ(_4m * 2, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+
+ ha.init_add_free(100 * _4m, _4m);
+ ha.init_add_free(102 * _4m, _4m);
+
+ ASSERT_EQ(_4m * 4, ha.get_free());
+ ASSERT_EQ(_4m * 4, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+
+ // next allocs will go to bitmap
+ ha.init_add_free(4 * _4m, _4m);
+ ASSERT_EQ(_4m * 5, ha.get_free());
+ ASSERT_EQ(_4m * 4, ha.get_avl_free());
+ ASSERT_EQ(_4m * 1, ha.get_bmap_free());
+
+ ha.init_add_free(6 * _4m, _4m);
+ ASSERT_EQ(_4m * 6, ha.get_free());
+ ASSERT_EQ(_4m * 4, ha.get_avl_free());
+ ASSERT_EQ(_4m * 2, ha.get_bmap_free());
+
+ // so we have 6x4M chunks, 4 chunks at AVL and 2 at bitmap
+
+ ha.init_rm_free(_1m, _1m); // take 1M from AVL
+ ASSERT_EQ(_1m * 23, ha.get_free());
+ ASSERT_EQ(_1m * 14, ha.get_avl_free());
+ ASSERT_EQ(_1m * 9, ha.get_bmap_free());
+
+ ha.init_rm_free(6 * _4m + _1m, _1m); // take 1M from bmap
+ ASSERT_EQ(_1m * 22, ha.get_free());
+ ASSERT_EQ(_1m * 14, ha.get_avl_free());
+ ASSERT_EQ(_1m * 8, ha.get_bmap_free());
+
+ // so we have at avl: 2M~2M, 8M~4M, 400M~4M , 408M~4M
+ // and at bmap: 0~1M, 16M~1M, 18M~2M, 24~4M
+
+ PExtentVector extents;
+ // allocate 4K, to be served from bitmap
+ EXPECT_EQ(block_size, ha.allocate(block_size, block_size,
+ 0, (int64_t)0, &extents));
+ ASSERT_EQ(1, extents.size());
+ ASSERT_EQ(0, extents[0].offset);
+
+ ASSERT_EQ(_1m * 14, ha.get_avl_free());
+ ASSERT_EQ(_1m * 8 - block_size, ha.get_bmap_free());
+
+ interval_set<uint64_t> release_set;
+ // release 4K, to be returned to bitmap
+ release_set.insert(extents[0].offset, extents[0].length);
+ ha.release(release_set);
+
+ ASSERT_EQ(_1m * 14, ha.get_avl_free());
+ ASSERT_EQ(_1m * 8, ha.get_bmap_free());
+ extents.clear();
+ release_set.clear();
+
+ // again we have at avl: 2M~2M, 8M~4M, 400M~4M , 408M~4M
+ // and at bmap: 0~1M, 16M~1M, 18M~2M, 24~4M
+
+ // add 12M~3M which will go to avl
+ ha.init_add_free(3 * _4m, 3 * _1m);
+ ASSERT_EQ(_1m * 17, ha.get_avl_free());
+ ASSERT_EQ(_1m * 8, ha.get_bmap_free());
+
+
+ // add 15M~4K which will be appended to existing slot
+ ha.init_add_free(15 * _1m, 0x1000);
+ ASSERT_EQ(_1m * 17 + 0x1000, ha.get_avl_free());
+ ASSERT_EQ(_1m * 8, ha.get_bmap_free());
+
+
+ // again we have at avl: 2M~2M, 8M~(7M+4K), 400M~4M , 408M~4M
+ // and at bmap: 0~1M, 16M~1M, 18M~2M, 24~4M
+
+ //some removals from bmap
+ ha.init_rm_free(28 * _1m - 0x1000, 0x1000);
+ ASSERT_EQ(_1m * 17 + 0x1000, ha.get_avl_free());
+ ASSERT_EQ(_1m * 8 - 0x1000, ha.get_bmap_free());
+
+ ha.init_rm_free(24 * _1m + 0x1000, 0x1000);
+ ASSERT_EQ(_1m * 17 + 0x1000, ha.get_avl_free());
+ ASSERT_EQ(_1m * 8 - 0x2000, ha.get_bmap_free());
+
+ ha.init_rm_free(24 * _1m + 0x1000, _4m - 0x2000);
+ ASSERT_EQ(_1m * 17 + 0x1000, ha.get_avl_free());
+ ASSERT_EQ(_1m * 4, ha.get_bmap_free());
+
+ //4K removal from avl
+ ha.init_rm_free(15 * _1m, 0x1000);
+ ASSERT_EQ(_1m * 17, ha.get_avl_free());
+ ASSERT_EQ(_1m * 4, ha.get_bmap_free());
+
+ //remove highest 4Ms from avl
+ ha.init_rm_free(_1m * 400, _4m);
+ ha.init_rm_free(_1m * 408, _4m);
+ ASSERT_EQ(_1m * 9, ha.get_avl_free());
+ ASSERT_EQ(_1m * 4, ha.get_bmap_free());
+
+ // we have at avl: 2M~2M, 8M~7M
+ // and at bmap: 0~1M, 16M~1M, 18M~2M
+
+ // this will be merged with neighbors from bmap and go to avl
+ ha.init_add_free(17 * _1m, _1m);
+ ASSERT_EQ(_1m * 1, ha.get_bmap_free());
+ ASSERT_EQ(_1m * 13, ha.get_avl_free());
+
+ // we have at avl: 2M~2M, 8M~7M, 16M~4M
+ // and at bmap: 0~1M
+
+ // and now do some cutoffs from 0~1M span
+
+ //cut off 4K from bmap
+ ha.init_rm_free(0 * _1m, 0x1000);
+ ASSERT_EQ(_1m * 13, ha.get_avl_free());
+ ASSERT_EQ(_1m * 1 - 0x1000, ha.get_bmap_free());
+
+ //cut off 1M-4K from bmap
+ ha.init_rm_free(0 * _1m + 0x1000, _1m - 0x1000);
+ ASSERT_EQ(_1m * 13, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+
+ //cut off 512K avl
+ ha.init_rm_free(17 * _1m + 0x1000, _1m / 2);
+ ASSERT_EQ(_1m * 13 - _1m / 2, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+
+ //cut off the rest from avl
+ ha.init_rm_free(17 * _1m + 0x1000 + _1m / 2, _1m / 2);
+ ASSERT_EQ(_1m * 12, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+ }
+
+ {
+ uint64_t block_size = 0x1000;
+ uint64_t capacity = 0x10000 * _1m; // = 64GB
+ TestHybridAllocator ha(g_ceph_context, capacity, block_size,
+ 4 * sizeof(range_seg_t), "test_hybrid_allocator");
+
+ ha.init_add_free(_1m, _1m);
+ ha.init_add_free(_1m * 3, _1m);
+ ha.init_add_free(_1m * 5, _1m);
+ ha.init_add_free(0x4000, 0x1000);
+
+ ASSERT_EQ(_1m * 3 + 0x1000, ha.get_free());
+ ASSERT_EQ(_1m * 3 + 0x1000, ha.get_avl_free());
+ ASSERT_EQ(0, ha.get_bmap_free());
+
+ // This will substitute chunk 0x4000~1000.
+ // Since new chunk insertion into into AvlAllocator:range_tree
+ // happens immediately before 0x4000~1000 chunk care should be taken
+ // to order operations properly and do not use already disposed iterator.
+ ha.init_add_free(0, 0x2000);
+
+ ASSERT_EQ(_1m * 3 + 0x3000, ha.get_free());
+ ASSERT_EQ(_1m * 3 + 0x2000, ha.get_avl_free());
+ ASSERT_EQ(0x1000, ha.get_bmap_free());
+ }
+}
+
+TEST(HybridAllocator, fragmentation)
+{
+ {
+ uint64_t block_size = 0x1000;
+ uint64_t capacity = 0x1000 * 0x1000; // = 16M
+ TestHybridAllocator ha(g_ceph_context, capacity, block_size,
+ 4 * sizeof(range_seg_t), "test_hybrid_allocator");
+
+ ha.init_add_free(0, 0x2000);
+ ha.init_add_free(0x4000, 0x2000);
+ ha.init_add_free(0x8000, 0x2000);
+ ha.init_add_free(0xc000, 0x1000);
+
+ ASSERT_EQ(0.5, ha.get_fragmentation());
+
+ // this will got to bmap with fragmentation = 1
+ ha.init_add_free(0x10000, 0x1000);
+
+ // which results in the following total fragmentation
+ ASSERT_EQ(0.5 * 7 / 8 + 1.0 / 8, ha.get_fragmentation());
+ }
+}
diff --git a/src/test/objectstore/run_seed_to.sh b/src/test/objectstore/run_seed_to.sh
new file mode 100755
index 000000000..5a624a5d4
--- /dev/null
+++ b/src/test/objectstore/run_seed_to.sh
@@ -0,0 +1,293 @@
+#!/usr/bin/env bash
+# vim: ts=8 sw=2 smarttab
+#
+# run_seed_to.sh - Run ceph_test_filestore_idempotent_sequence up until an
+# injection point, generating a sequence of operations based on a
+# provided seed.
+#
+# We also perform three additional tests, focused on assessing if
+# replaying a larger chunck of the journal affects the expected store
+# behavior. These tests will be performed by increasing the store's
+# journal sync interval to a very large value, allowing the store to
+# finish execution before the first sync (unless the store runs for
+# over 10 hours, case on which the interval variables must be changed
+# to an appropriate value). Unless the '--no-journal-test' option is
+# specified, we will run the 3 following scenarios:
+#
+# 1) journal sync'ing for both stores is good as disabled
+# (we call it '00', for store naming purposes)
+# 2) journal sync'ing for store A is as good as disabled
+# (we call it '01', for store naming purposes)
+# 3) journal sync'ing for store B is as good as disabled
+# (we call it '10', for store naming purposes)
+#
+# All log files are also appropriately named accordingly (i.e., a.00.fail,
+# a.10.recover, or b.01.clean).
+#
+# By default, the test will not exit on error, although it will show the
+# fail message. This behavior is so defined so we run the whole battery of
+# tests, and obtain as many mismatches as possible in one go. We may force
+# the test to exit on error by specifying the '--exit-on-error' option.
+#
+#
+set -e
+
+test_opts=""
+
+usage() {
+ echo "usage: $1 [options..] <seed> <kill-at>"
+ echo
+ echo "options:"
+ echo " -c, --colls <VAL> # of collections"
+ echo " -o, --objs <VAL> # of objects"
+ echo " -b, --btrfs <VAL> seq number for btrfs stores"
+ echo " --no-journal-test don't perform journal replay tests"
+ echo " -e, --exit-on-error exit with 1 on error"
+ echo " -v, --valgrind run commands through valgrind"
+ echo
+ echo "env vars:"
+ echo " OPTS_STORE additional opts for both stores"
+ echo " OPTS_STORE_A additional opts for store A"
+ echo " OPTS_STORE_B additional opts for store B"
+ echo
+}
+
+echo $0 $*
+
+die_on_missing_arg() {
+ if [[ "$2" == "" ]]; then
+ echo "$1: missing required parameter"
+ exit 1
+ fi
+}
+
+
+required_args=2
+obtained_args=0
+
+seed=""
+killat=""
+on_btrfs=0
+on_btrfs_seq=0
+journal_test=1
+min_sync_interval="36000" # ten hours, yes.
+max_sync_interval="36001"
+exit_on_error=0
+v=""
+
+do_rm() {
+ if [[ $on_btrfs -eq 0 ]]; then
+ rm -fr $*
+ fi
+}
+
+set_arg() {
+ if [[ $1 -eq 1 ]]; then
+ seed=$2
+ elif [[ $1 -eq 2 ]]; then
+ killat=$2
+ else
+ echo "error: unknown purpose for '$2'"
+ usage $0
+ exit 1
+ fi
+}
+
+while [[ $# -gt 0 ]];
+do
+ case "$1" in
+ -c | --colls)
+ die_on_missing_arg "$1" "$2"
+ test_opts="$test_opts --test-num-colls $2"
+ shift 2
+ ;;
+ -o | --objs)
+ die_on_missing_arg "$1" "$2"
+ test_opts="$test_opts --test-num-objs $2"
+ shift 2
+ ;;
+ -h | --help)
+ usage $0 ;
+ exit 0
+ ;;
+ -b | --btrfs)
+ die_on_missing_arg "$1" "$2"
+ on_btrfs=1
+ on_btrfs_seq=$2
+ shift 2
+ ;;
+ --no-journal-test)
+ journal_test=0
+ shift
+ ;;
+ -e | --exit-on-error)
+ exit_on_error=1
+ shift
+ ;;
+ -v | --valgrind)
+ v="valgrind --leak-check=full"
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "$1: unknown option" >&2
+ usage $0
+ exit 1
+ ;;
+ *)
+ obtained_args=$(($obtained_args+1))
+ set_arg $obtained_args $1
+ shift
+ ;;
+ esac
+done
+
+if [[ $obtained_args -ne $required_args ]]; then
+ echo "error: missing argument"
+ usage $0 ;
+ exit 1
+fi
+
+if [[ "$OPTS_STORE" != "" ]]; then
+ test_opts="$test_opts $OPTS_STORE"
+fi
+
+test_opts_a="$test_opts"
+test_opts_b="$test_opts"
+
+if [[ "$OPTS_STORE_A" != "" ]]; then
+ test_opts_a="$test_opts_a $OPTS_STORE_A"
+fi
+if [[ "$OPTS_STORE_B" != "" ]]; then
+ test_opts_b="$test_opts_b $OPTS_STORE_B"
+fi
+
+echo seed $seed
+echo kill at $killat
+
+# run forever, until $killat...
+to=1000000000
+
+#
+# store names
+#
+# We need these for two reasons:
+# 1) if we are running the tests on a btrfs volume, then we need to use
+# a seq number for each run. Being on btrfs means we will fail when
+# removing the store's directories and it's far more simple to just
+# specify differente store names such as 'a.$seq' or 'b.$seq'.
+#
+# 2) unless the '--no-journal-test' option is specified, we will run
+# three additional tests for each store, and we will reuse the same
+# command for each one of the runs, but varying the store's name and
+# arguments.
+#
+store_a="a"
+store_b="b"
+
+if [[ $on_btrfs -eq 1 ]]; then
+ store_a="$store_a.$on_btrfs_seq"
+ store_b="$store_b.$on_btrfs_seq"
+fi
+
+total_runs=1
+
+if [[ $journal_test -eq 1 ]]; then
+ total_runs=$(($total_runs + 3))
+fi
+
+num_runs=0
+
+opt_min_sync="--filestore-min-sync-interval $min_sync_interval"
+opt_max_sync="--filestore-max-sync-interval $max_sync_interval"
+
+ret=0
+
+while [[ $num_runs -lt $total_runs ]];
+do
+ tmp_name_a=$store_a
+ tmp_name_b=$store_b
+ tmp_opts_a=$test_opts_a
+ tmp_opts_b=$test_opts_b
+
+ #
+ # We have already tested whether there are diffs when both journals
+ # are properly working. Now let's try on three other scenarios:
+ # 1) journal sync'ing for both stores is good as disabled
+ # (we call it '00')
+ # 2) journal sync'ing for store A is as good as disabled
+ # (we call it '01')
+ # 3) journal sync'ing for store B is as good as disabled
+ # (we call it '10')
+ #
+ if [[ $num_runs -gt 0 && $journal_test -eq 1 ]]; then
+ echo "run #$num_runs"
+ case $num_runs in
+ 1)
+ tmp_name_a="$tmp_name_a.00"
+ tmp_name_b="$tmp_name_b.00"
+ tmp_opts_a="$tmp_opts_a $opt_min_sync $opt_max_sync"
+ tmp_opts_b="$tmp_opts_b $opt_min_sync $opt_max_sync"
+ ;;
+ 2)
+ tmp_name_a="$tmp_name_a.01"
+ tmp_name_b="$tmp_name_b.01"
+ tmp_opts_a="$tmp_opts_a $opt_min_sync $opt_max_sync"
+ ;;
+ 3)
+ tmp_name_a="$tmp_name_a.10"
+ tmp_name_b="$tmp_name_b.10"
+ tmp_opts_b="$tmp_opts_b $opt_min_sync $opt_max_sync"
+ ;;
+ esac
+ fi
+
+ do_rm $tmp_name_a $tmp_name_a.fail $tmp_name_a.recover
+ $v ceph_test_filestore_idempotent_sequence run-sequence-to $to \
+ $tmp_name_a $tmp_name_a/journal \
+ --test-seed $seed --osd-journal-size 100 \
+ --filestore-kill-at $killat $tmp_opts_a \
+ --log-file $tmp_name_a.fail --debug-filestore 20 --no-log-to-stderr || true
+
+ stop_at=`ceph_test_filestore_idempotent_sequence get-last-op \
+ $tmp_name_a $tmp_name_a/journal \
+ --log-file $tmp_name_a.recover \
+ --debug-filestore 20 --debug-journal 20 --no-log-to-stderr`
+
+ if [[ "`expr $stop_at - $stop_at 2>/dev/null`" != "0" ]]; then
+ echo "error: get-last-op returned '$stop_at'"
+ exit 1
+ fi
+
+ echo stopped at $stop_at
+
+ do_rm $tmp_name_b $tmp_name_b.clean
+ $v ceph_test_filestore_idempotent_sequence run-sequence-to \
+ $stop_at $tmp_name_b $tmp_name_b/journal \
+ --test-seed $seed --osd-journal-size 100 \
+ --log-file $tmp_name_b.clean --debug-filestore 20 --no-log-to-stderr \
+ $tmp_opts_b
+
+ if $v ceph_test_filestore_idempotent_sequence diff \
+ $tmp_name_a $tmp_name_a/journal $tmp_name_b $tmp_name_b/journal --no-log-to-stderr --log-file $tmp_name_a.diff.log --debug-filestore 20 ; then
+ echo OK
+ else
+ echo "FAIL"
+ echo " see:"
+ echo " $tmp_name_a.fail -- leading up to failure"
+ echo " $tmp_name_a.recover -- journal replay"
+ echo " $tmp_name_b.clean -- the clean reference"
+
+ ret=1
+ if [[ $exit_on_error -eq 1 ]]; then
+ exit 1
+ fi
+ fi
+
+ num_runs=$(($num_runs+1))
+done
+
+exit $ret
diff --git a/src/test/objectstore/run_seed_to_range.sh b/src/test/objectstore/run_seed_to_range.sh
new file mode 100755
index 000000000..7af2e59ce
--- /dev/null
+++ b/src/test/objectstore/run_seed_to_range.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+set -x
+set -e
+
+seed=$1
+from=$2
+to=$3
+dir=$4
+
+mydir=`dirname $0`
+
+for f in `seq $from $to`
+do
+ if ! $mydir/run_seed_to.sh -o 10 -e $seed $f; then
+ if [ -d "$dir" ]; then
+ echo copying evidence to $dir
+ cp -a . $dir
+ else
+ echo no dir provided for evidence disposal
+ fi
+ exit 1
+ fi
+done
diff --git a/src/test/objectstore/run_smr_bluestore_test.sh b/src/test/objectstore/run_smr_bluestore_test.sh
new file mode 100644
index 000000000..d689cf2c5
--- /dev/null
+++ b/src/test/objectstore/run_smr_bluestore_test.sh
@@ -0,0 +1,48 @@
+#!/bin/bash -ex
+
+# 1) run_smr_bluestore_test.sh
+# Setup smr device, run all tests
+
+# 2) run_smr_bluestore_test.sh --smr
+# Setup smr device but skip tests failing on smr
+
+
+before_creation=$(mktemp)
+lsscsi > $before_creation
+
+echo "cd /backstores/user:zbc
+create name=zbc0 size=20G cfgstring=model-HM/zsize-256/conv-10@zbc0.raw
+/loopback create
+cd /loopback
+create naa.50014055e5f25aa0
+cd naa.50014055e5f25aa0/luns
+create /backstores/user:zbc/zbc0 0
+" | sudo targetcli
+
+sleep 1 #if too fast device does not show up
+after_creation=$(mktemp)
+lsscsi > $after_creation
+if [[ $(diff $before_creation $after_creation | wc -l ) != 2 ]]
+then
+ echo New zbc device not created
+ false
+fi
+
+function cleanup() {
+ echo "cd /loopback
+delete naa.50014055e5f25aa0
+cd /backstores/user:zbc
+delete zbc0" | sudo targetcli
+ sudo rm -f zbc0.raw
+ rm -f $before_creation $after_creation
+}
+trap cleanup EXIT
+
+DEV=$(diff $before_creation $after_creation |grep zbc |sed "s@.* /@/@")
+sudo chmod 666 $DEV
+# Need sudo
+# https://patchwork.kernel.org/project/linux-block/patch/20210811110505.29649-3-Niklas.Cassel@wdc.com/
+sudo ceph_test_objectstore \
+ --bluestore-block-path $DEV \
+ --gtest_filter=*/2 \
+ $*
diff --git a/src/test/objectstore/run_test_deferred.sh b/src/test/objectstore/run_test_deferred.sh
new file mode 100755
index 000000000..1be4d9104
--- /dev/null
+++ b/src/test/objectstore/run_test_deferred.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+
+if [[ ! (-x ./bin/unittest_deferred) || ! (-x ./bin/ceph-kvstore-tool) || ! (-x ./bin/ceph-bluestore-tool)]]
+then
+ echo Test must be run from ceph build directory
+ echo with unittest_deferred, ceph-kvstore-tool and ceph-bluestore-tool compiled
+ exit 1
+fi
+
+# Create BlueStore, only main block device, 4K AU, forced deferred 4K, 64K AU for BlueFS
+
+# Create file zapchajdziura, that is 0xe000 in size.
+# This adds to 0x0000 - 0x1000 of BlueStore superblock and 0x1000 - 0x2000 of BlueFS superblock,
+# making 0x00000 - 0x10000 filled, nicely aligning for 64K BlueFS requirements
+
+# Prefill 10 objects Object-0 .. Object-9, each 64K. Sync to disk.
+# Do transactions like:
+# - fill Object-x+1 16 times at offsets 0x0000, 0x1000, ... 0xf000 with 8bytes, trigerring deferred writes
+# - fill Object-x with 64K data
+# Repeat for Object-0 to Object-8.
+
+# Right after getting notification on_complete for all 9 transactions, immediately exit(1).
+./bin/unittest_deferred --log-to-stderr=false
+
+# Now we should have a considerable amount of pending deferred writes.
+# They do refer disk regions that do not belong to any object.
+
+# Perform compaction on RocksDB
+# This initializes BlueFS, but does not replay deferred writes.
+# It jiggles RocksDB files around. CURRENT and MANIFEST are recreated, with some .sst files too.
+# The hope here is that newly created RocksDB files will occupy space that is free,
+# but targetted by pending deferred writes.
+./bin/ceph-kvstore-tool bluestore-kv bluestore.test_temp_dir/ compact --log-to-stderr=false
+
+# It this step we (hopefully) get RocksDB files overwritten
+# We initialize BlueFS and RocksDB, there should be no problem here.
+# Then we apply deferred writes. Now some of RocksDB files might get corrupted.
+# It is very likely that this will not cause any problems, since CURRENT and MANIFEST are only read at bootup.
+./bin/ceph-bluestore-tool --path bluestore.test_temp_dir/ --command fsck --deep 1 --debug-bluestore=30/30 --debug-bdev=30/30 --log-file=log-bs-corrupts.txt --log-to-file --log-to-stderr=false
+
+# If we were lucky, this command now fails
+./bin/ceph-bluestore-tool --path bluestore.test_temp_dir/ --command fsck --deep 1 --debug-bluestore=30/30 --debug-bdev=30/30 --log-file=log-bs-crash.txt --log-to-file --log-to-stderr=false
+if [[ $? != 0 ]]
+then
+ echo "Deferred writes corruption successfully created !"
+else
+ echo "No deferred write problems detected."
+fi
+
+#cleanup
+rm -rf bluestore.test_temp_dir/
diff --git a/src/test/objectstore/store_test.cc b/src/test/objectstore/store_test.cc
new file mode 100644
index 000000000..9edfebd6b
--- /dev/null
+++ b/src/test/objectstore/store_test.cc
@@ -0,0 +1,10932 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <glob.h>
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <memory>
+#include <time.h>
+#include <sys/mount.h>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/binomial_distribution.hpp>
+#include <fmt/format.h>
+#include <gtest/gtest.h>
+
+#include "os/ObjectStore.h"
+#if defined(WITH_BLUESTORE)
+#include "os/bluestore/BlueStore.h"
+#include "os/bluestore/BlueFS.h"
+#endif
+#include "include/Context.h"
+#include "common/buffer_instrumentation.h"
+#include "common/ceph_argparse.h"
+#include "common/admin_socket.h"
+#include "global/global_init.h"
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "common/options.h" // for the size literals
+#include "common/pretty_binary.h"
+#include "include/stringify.h"
+#include "include/coredumpctl.h"
+#include "include/unordered_map.h"
+#include "os/kv.h"
+#include "store_test_fixture.h"
+
+
+using namespace std;
+using namespace std::placeholders;
+
+typedef boost::mt11213b gen_type;
+
+const uint64_t DEF_STORE_TEST_BLOCKDEV_SIZE = 10240000000;
+#define dout_context g_ceph_context
+
+bool smr = false;
+
+static bool bl_eq(bufferlist& expected, bufferlist& actual)
+{
+ if (expected.contents_equal(actual))
+ return true;
+
+ unsigned first = 0;
+ if(expected.length() != actual.length()) {
+ cout << "--- buffer lengths mismatch " << std::hex
+ << "expected 0x" << expected.length() << " != actual 0x"
+ << actual.length() << std::dec << std::endl;
+ derr << "--- buffer lengths mismatch " << std::hex
+ << "expected 0x" << expected.length() << " != actual 0x"
+ << actual.length() << std::dec << dendl;
+ }
+ auto len = std::min(expected.length(), actual.length());
+ while ( first<len && expected[first] == actual[first])
+ ++first;
+ unsigned last = len;
+ while (last > 0 && expected[last-1] == actual[last-1])
+ --last;
+ if(len > 0) {
+ cout << "--- buffer mismatch between offset 0x" << std::hex << first
+ << " and 0x" << last << ", total 0x" << len << std::dec
+ << std::endl;
+ derr << "--- buffer mismatch between offset 0x" << std::hex << first
+ << " and 0x" << last << ", total 0x" << len << std::dec
+ << dendl;
+ cout << "--- expected:\n";
+ expected.hexdump(cout);
+ cout << "--- actual:\n";
+ actual.hexdump(cout);
+ }
+ return false;
+}
+
+
+
+template <typename T>
+int queue_transaction(
+ T &store,
+ ObjectStore::CollectionHandle ch,
+ ObjectStore::Transaction &&t) {
+ if (rand() % 2) {
+ ObjectStore::Transaction t2;
+ t2.append(t);
+ return store->queue_transaction(ch, std::move(t2));
+ } else {
+ return store->queue_transaction(ch, std::move(t));
+ }
+}
+
+template <typename T>
+int collection_list(T &store, ObjectStore::CollectionHandle &c,
+ const ghobject_t& start, const ghobject_t& end, int max,
+ vector<ghobject_t> *ls, ghobject_t *pnext,
+ bool disable_legacy = false) {
+ if (disable_legacy || rand() % 2) {
+ return store->collection_list(c, start, end, max, ls, pnext);
+ } else {
+ return store->collection_list_legacy(c, start, end, max, ls, pnext);
+ }
+}
+
+bool sorted(const vector<ghobject_t> &in) {
+ ghobject_t start;
+ for (vector<ghobject_t>::const_iterator i = in.begin();
+ i != in.end();
+ ++i) {
+ if (start > *i) {
+ cout << start << " should follow " << *i << std::endl;
+ return false;
+ }
+ start = *i;
+ }
+ return true;
+}
+
+class StoreTest : public StoreTestFixture,
+ public ::testing::WithParamInterface<const char*> {
+public:
+ StoreTest()
+ : StoreTestFixture(GetParam())
+ {}
+ void doCompressionTest();
+ void doSyntheticTest(
+ int num_ops,
+ uint64_t max_obj, uint64_t max_wr, uint64_t align);
+};
+
+class StoreTestDeferredSetup : public StoreTest {
+ void SetUp() override {
+ //do nothing
+ }
+
+protected:
+ void DeferredSetup() {
+ StoreTest::SetUp();
+ }
+
+public:
+};
+
+
+class StoreTestSpecificAUSize : public StoreTestDeferredSetup {
+
+public:
+ typedef
+ std::function<void(
+ uint64_t num_ops,
+ uint64_t max_obj,
+ uint64_t max_wr,
+ uint64_t align)> MatrixTest;
+
+ void StartDeferred(size_t min_alloc_size) {
+ SetVal(g_conf(), "bluestore_min_alloc_size", stringify(min_alloc_size).c_str());
+ DeferredSetup();
+ }
+
+private:
+ // bluestore matrix testing
+ uint64_t max_write = 40 * 1024;
+ uint64_t max_size = 400 * 1024;
+ uint64_t alignment = 0;
+ uint64_t num_ops = 10000;
+
+protected:
+ string matrix_get(const char *k) {
+ if (string(k) == "max_write") {
+ return stringify(max_write);
+ } else if (string(k) == "max_size") {
+ return stringify(max_size);
+ } else if (string(k) == "alignment") {
+ return stringify(alignment);
+ } else if (string(k) == "num_ops") {
+ return stringify(num_ops);
+ } else {
+ char *buf;
+ g_conf().get_val(k, &buf, -1);
+ string v = buf;
+ free(buf);
+ return v;
+ }
+ }
+
+ void matrix_set(const char *k, const char *v) {
+ if (string(k) == "max_write") {
+ max_write = atoll(v);
+ } else if (string(k) == "max_size") {
+ max_size = atoll(v);
+ } else if (string(k) == "alignment") {
+ alignment = atoll(v);
+ } else if (string(k) == "num_ops") {
+ num_ops = atoll(v);
+ } else {
+ SetVal(g_conf(), k, v);
+ }
+ }
+
+ void do_matrix_choose(const char *matrix[][10],
+ int i, int pos, int num,
+ MatrixTest fn) {
+ if (matrix[i][0]) {
+ int count;
+ for (count = 0; matrix[i][count+1]; ++count) ;
+ for (int j = 1; matrix[i][j]; ++j) {
+ matrix_set(matrix[i][0], matrix[i][j]);
+ do_matrix_choose(matrix,
+ i + 1,
+ pos * count + j - 1,
+ num * count,
+ fn);
+ }
+ } else {
+ cout << "---------------------- " << (pos + 1) << " / " << num
+ << " ----------------------" << std::endl;
+ for (unsigned k=0; matrix[k][0]; ++k) {
+ cout << " " << matrix[k][0] << " = " << matrix_get(matrix[k][0])
+ << std::endl;
+ }
+ g_ceph_context->_conf.apply_changes(nullptr);
+ fn(num_ops, max_size, max_write, alignment);
+ }
+ }
+
+ void do_matrix(const char *matrix[][10],
+ MatrixTest fn) {
+
+ if (strcmp(matrix[0][0], "bluestore_min_alloc_size") == 0) {
+ int count;
+ for (count = 0; matrix[0][count+1]; ++count) ;
+ for (size_t j = 1; matrix[0][j]; ++j) {
+ if (j > 1) {
+ TearDown();
+ }
+ StartDeferred(strtoll(matrix[0][j], NULL, 10));
+ do_matrix_choose(matrix, 1, j - 1, count, fn);
+ }
+ } else {
+ StartDeferred(0);
+ do_matrix_choose(matrix, 0, 0, 1, fn);
+ }
+ }
+
+};
+
+class StoreTestOmapUpgrade : public StoreTestDeferredSetup {
+protected:
+ void StartDeferred() {
+ DeferredSetup();
+ }
+
+public:
+ struct generator {
+ double r = 3.6;
+ double x = 0.5;
+ double operator()(){
+ double v = x;
+ x = r * x * (1 - x);
+ return v;
+ }
+ };
+
+ std::string generate_monotonic_name(uint32_t SUM, uint32_t i, double r, double x)
+ {
+ generator gen{r, x};
+ //std::cout << "r=" << r << " x=" << x << std::endl;
+ std::string s;
+ while (SUM > 1) {
+ uint32_t lo = 0;
+ uint32_t hi = 1 + gen() * 10;
+ uint32_t start = ('z' - 'a' + 1 - hi) * gen();
+ while (hi - lo > 0) {
+ uint32_t mid = (lo + hi + 1 + (SUM&1)) / 2; // round up or down, depending on SUM
+ // std::cout << "SUM=" << SUM << " x=" << gen.x << std::endl;
+ uint32_t mid_val = gen() * (SUM - 1) + 1;
+ // LEFT = lo .. mid - 1
+ // RIGHT = mid .. hi
+ // std::cout << "lo=" << lo << " hi=" << hi << " mid=" << mid
+ // << " SUM=" << SUM << " i=" << i << " x=" << gen.x << " mid_val=" << mid_val << std::endl;
+ if (i < mid_val) {
+ hi = mid - 1;
+ SUM = mid_val;
+ } else {
+ lo = mid;
+ SUM = SUM - mid_val;
+ i = i - mid_val;
+ }
+ }
+ //std::cout << "lo=" << lo << " hi=" << hi
+ // << " SUM=" << SUM << " i=" << i << std::endl;
+
+ s.push_back('a' + lo + start); // to keep alphabetic order
+ uint32_t cnt = gen() * 8;
+ for (uint32_t j = 0; j < cnt; j++) {
+ s.push_back('a' + ('z' - 'a' + 1) * gen());
+ }
+ s.push_back('.');
+ }
+ return s;
+ }
+
+ std::string gen_string(size_t size, generator& gen) {
+ std::string s;
+ for (size_t i = 0; i < size; i++) {
+ s.push_back('a' + ('z' - 'a' + 1 ) * gen());
+ }
+ return s;
+ }
+
+ void make_omap_data(size_t object_count,
+ int64_t poolid,
+ coll_t cid) {
+ int r;
+ ObjectStore::CollectionHandle ch = store->open_collection(cid);
+ for (size_t o = 0; o < object_count; o++)
+ {
+ ObjectStore::Transaction t;
+ std::string oid = generate_monotonic_name(object_count, o, 3.71, 0.5);
+ ghobject_t hoid(hobject_t(oid, "", CEPH_NOSNAP, 0, poolid, ""));
+ t.touch(cid, hoid);
+ generator gen{3.85 + 0.1 * o / object_count, 1 - double(o) / object_count};
+
+ map<string, bufferlist> start_set;
+ size_t omap_count = 1 + gen() * 20;
+ bool do_omap_header = gen() > 0.5;
+ if (do_omap_header) {
+ bufferlist header;
+ header.append(gen_string(50, gen));
+ t.omap_setheader(cid, hoid, header);
+ }
+ for (size_t i = 0; i < omap_count; i++) {
+ std::string name = generate_monotonic_name(omap_count, i, 3.66 + 0.22 * o / object_count, 0.5);
+ bufferlist val;
+ val.append(gen_string(100, gen));
+ start_set.emplace(name, val);
+ }
+ t.omap_setkeys(cid, hoid, start_set);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ }
+
+ void check_omap_data(size_t object_count,
+ int64_t poolid,
+ coll_t cid) {
+ int r;
+ ObjectStore::CollectionHandle ch = store->open_collection(cid);
+
+ for (size_t o = 0; o < object_count; o++)
+ {
+ ObjectStore::Transaction t;
+ std::string oid = generate_monotonic_name(object_count, o, 3.71, 0.5);
+ ghobject_t hoid(hobject_t(oid, "", CEPH_NOSNAP, 0, poolid, ""));
+ generator gen{3.85 + 0.1 * o / object_count, 1 - double(o) / object_count};
+
+ bufferlist omap_header;
+ map<string, bufferlist> omap_set;
+ r = store->omap_get(ch, hoid, &omap_header, &omap_set);
+ ASSERT_EQ(r, 0);
+ size_t omap_count = 1 + gen() * 20;
+ bool do_omap_header = gen() > 0.5;
+ if (do_omap_header) {
+ std::string header_str = gen_string(50, gen);
+ ASSERT_EQ(header_str, omap_header.to_str());
+ }
+ auto it = omap_set.begin();
+ for (size_t i = 0; i < omap_count; i++) {
+ ASSERT_TRUE(it != omap_set.end());
+ std::string name = generate_monotonic_name(omap_count, i, 3.66 + 0.22 * o / object_count, 0.5);
+ std::string val_gen = gen_string(100, gen);
+ ASSERT_EQ(it->first, name);
+ ASSERT_EQ(it->second.to_str(), val_gen);
+ ++it;
+ }
+ }
+ }
+};
+
+TEST_P(StoreTest, collect_metadata) {
+ map<string,string> pm;
+ store->collect_metadata(&pm);
+ if (GetParam() == string("filestore")) {
+ ASSERT_NE(pm.count("filestore_backend"), 0u);
+ ASSERT_NE(pm.count("filestore_f_type"), 0u);
+ ASSERT_NE(pm.count("backend_filestore_partition_path"), 0u);
+ ASSERT_NE(pm.count("backend_filestore_dev_node"), 0u);
+ }
+}
+
+TEST_P(StoreTest, Trivial) {
+}
+
+TEST_P(StoreTest, TrivialRemount) {
+ int r = store->umount();
+ ASSERT_EQ(0, r);
+ r = store->mount();
+ ASSERT_EQ(0, r);
+}
+
+TEST_P(StoreTest, TrivialRemountFsck) {
+ if(string(GetParam()) != "bluestore")
+ return;
+ int r = store->umount();
+ ASSERT_EQ(0, r);
+ r = store->fsck(false);
+ ASSERT_EQ(0, r);
+ r = store->mount();
+ ASSERT_EQ(0, r);
+}
+
+TEST_P(StoreTest, SimpleRemount) {
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ ghobject_t hoid2(hobject_t(sobject_t("Object 2", CEPH_NOSNAP)));
+ bufferlist bl;
+ bl.append("1234512345");
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ cerr << "create collection + write" << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ch.reset();
+ r = store->umount();
+ ASSERT_EQ(0, r);
+ r = store->mount();
+ ASSERT_EQ(0, r);
+ ch = store->open_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid2, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ch.reset();
+ r = store->umount();
+ ASSERT_EQ(0, r);
+ r = store->mount();
+ ASSERT_EQ(0, r);
+ ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, IORemount) {
+ coll_t cid;
+ bufferlist bl;
+ bl.append("1234512345");
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ cerr << "create collection + objects" << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ for (int n=1; n<=100; ++n) {
+ ghobject_t hoid(hobject_t(sobject_t("Object " + stringify(n), CEPH_NOSNAP)));
+ t.write(cid, hoid, 0, bl.length(), bl);
+ }
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ // overwrites
+ {
+ cout << "overwrites" << std::endl;
+ for (int n=1; n<=100; ++n) {
+ ObjectStore::Transaction t;
+ ghobject_t hoid(hobject_t(sobject_t("Object " + stringify(n), CEPH_NOSNAP)));
+ t.write(cid, hoid, 1, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ }
+ ch.reset();
+ r = store->umount();
+ ASSERT_EQ(0, r);
+ r = store->mount();
+ ASSERT_EQ(0, r);
+ {
+ ObjectStore::Transaction t;
+ for (int n=1; n<=100; ++n) {
+ ghobject_t hoid(hobject_t(sobject_t("Object " + stringify(n), CEPH_NOSNAP)));
+ t.remove(cid, hoid);
+ }
+ t.remove_collection(cid);
+ auto ch = store->open_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, UnprintableCharsName) {
+ coll_t cid;
+ string name = "funnychars_";
+ for (unsigned i = 0; i < 256; ++i) {
+ name.push_back(i);
+ }
+ ghobject_t oid(hobject_t(sobject_t(name, CEPH_NOSNAP)));
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ cerr << "create collection + object" << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, oid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ch.reset();
+ r = store->umount();
+ ASSERT_EQ(0, r);
+ r = store->mount();
+ ASSERT_EQ(0, r);
+ {
+ cout << "removing" << std::endl;
+ ObjectStore::Transaction t;
+ t.remove(cid, oid);
+ t.remove_collection(cid);
+ auto ch = store->open_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, FiemapEmpty) {
+ coll_t cid;
+ int r = 0;
+ ghobject_t oid(hobject_t(sobject_t("fiemap_object", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, oid);
+ t.truncate(cid, oid, 100000);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist bl;
+ store->fiemap(ch, oid, 0, 100000, bl);
+ map<uint64_t,uint64_t> m, e;
+ auto p = bl.cbegin();
+ decode(m, p);
+ cout << " got " << m << std::endl;
+ e[0] = 100000;
+ EXPECT_TRUE(m == e || m.empty());
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, oid);
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, FiemapHoles) {
+ const uint64_t MAX_EXTENTS = 4000;
+ const uint64_t SKIP_STEP = 65536;
+ coll_t cid;
+ int r = 0;
+ ghobject_t oid(hobject_t(sobject_t("fiemap_object", CEPH_NOSNAP)));
+ bufferlist bl;
+ bl.append("foo");
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, oid);
+ for (uint64_t i = 0; i < MAX_EXTENTS; i++)
+ t.write(cid, oid, SKIP_STEP * i, 3, bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ //fiemap test from 0 to SKIP_STEP * (MAX_EXTENTS - 1) + 3
+ bufferlist bl;
+ store->fiemap(ch, oid, 0, SKIP_STEP * (MAX_EXTENTS - 1) + 3, bl);
+ map<uint64_t,uint64_t> m, e;
+ auto p = bl.cbegin();
+ decode(m, p);
+ cout << " got " << m << std::endl;
+ ASSERT_TRUE(!m.empty());
+ ASSERT_GE(m[0], 3u);
+ auto last = m.crbegin();
+ if (m.size() == 1) {
+ ASSERT_EQ(0u, last->first);
+ } else if (m.size() == MAX_EXTENTS) {
+ for (uint64_t i = 0; i < MAX_EXTENTS; i++) {
+ ASSERT_TRUE(m.count(SKIP_STEP * i));
+ }
+ }
+ ASSERT_GT(last->first + last->second, SKIP_STEP * (MAX_EXTENTS - 1));
+ }
+ {
+ // fiemap test from SKIP_STEP to SKIP_STEP * (MAX_EXTENTS - 2) + 3
+ bufferlist bl;
+ store->fiemap(ch, oid, SKIP_STEP, SKIP_STEP * (MAX_EXTENTS - 2) + 3, bl);
+ map<uint64_t,uint64_t> m, e;
+ auto p = bl.cbegin();
+ decode(m, p);
+ cout << " got " << m << std::endl;
+ ASSERT_TRUE(!m.empty());
+ // kstore always returns [0, object_size] regardless of offset and length
+ // FIXME: if fiemap logic in kstore is refined
+ if (string(GetParam()) != "kstore") {
+ ASSERT_GE(m[SKIP_STEP], 3u);
+ auto last = m.crbegin();
+ if (m.size() == 1) {
+ ASSERT_EQ(SKIP_STEP, last->first);
+ } else if (m.size() == MAX_EXTENTS - 2) {
+ for (uint64_t i = 1; i < MAX_EXTENTS - 1; i++) {
+ ASSERT_TRUE(m.count(SKIP_STEP*i));
+ }
+ }
+ ASSERT_GT(last->first + last->second, SKIP_STEP * (MAX_EXTENTS - 1));
+ }
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, oid);
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SimpleMetaColTest) {
+ coll_t cid;
+ int r = 0;
+ {
+ auto ch = store->create_new_collection(cid);
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "create collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ auto ch = store->open_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ auto ch = store->create_new_collection(cid);
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "add collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ auto ch = store->open_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SimplePGColTest) {
+ coll_t cid(spg_t(pg_t(1,2), shard_id_t::NO_SHARD));
+ int r = 0;
+ {
+ ObjectStore::Transaction t;
+ auto ch = store->create_new_collection(cid);
+ t.create_collection(cid, 4);
+ cerr << "create collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ auto ch = store->open_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 4);
+ cerr << "add collection" << std::endl;
+ auto ch = store->create_new_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ auto ch = store->open_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SimpleColPreHashTest) {
+ // Firstly we will need to revert the value making sure
+ // collection hint actually works
+ int merge_threshold = g_ceph_context->_conf->filestore_merge_threshold;
+ std::ostringstream oss;
+ if (merge_threshold > 0) {
+ oss << "-" << merge_threshold;
+ SetVal(g_conf(), "filestore_merge_threshold", oss.str().c_str());
+ }
+
+ uint32_t pg_num = 128;
+
+ boost::uniform_int<> pg_id_range(0, pg_num);
+ gen_type rng(time(NULL));
+ int pg_id = pg_id_range(rng);
+
+ int objs_per_folder = abs(merge_threshold) * 16 * g_ceph_context->_conf->filestore_split_multiple;
+ boost::uniform_int<> folders_range(5, 256);
+ uint64_t expected_num_objs = (uint64_t)objs_per_folder * (uint64_t)folders_range(rng);
+
+ coll_t cid(spg_t(pg_t(pg_id, 15), shard_id_t::NO_SHARD));
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ // Create a collection along with a hint
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 5);
+ cerr << "create collection" << std::endl;
+ bufferlist hint;
+ encode(pg_num, hint);
+ encode(expected_num_objs, hint);
+ t.collection_hint(cid, ObjectStore::Transaction::COLL_HINT_EXPECTED_NUM_OBJECTS, hint);
+ cerr << "collection hint" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // Remove the collection
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ cerr << "remove collection" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SmallBlockWrites) {
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist a;
+ bufferptr ap(0x1000);
+ memset(ap.c_str(), 'a', 0x1000);
+ a.append(ap);
+ bufferlist b;
+ bufferptr bp(0x1000);
+ memset(bp.c_str(), 'b', 0x1000);
+ b.append(bp);
+ bufferlist c;
+ bufferptr cp(0x1000);
+ memset(cp.c_str(), 'c', 0x1000);
+ c.append(cp);
+ bufferptr zp(0x1000);
+ zp.zero();
+ bufferlist z;
+ z.append(zp);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, 0x1000, a);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in, exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(0x1000, r);
+ exp.append(a);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0x1000, 0x1000, b);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in, exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(0x2000, r);
+ exp.append(a);
+ exp.append(b);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0x3000, 0x1000, c);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in, exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(0x4000, r);
+ exp.append(a);
+ exp.append(b);
+ exp.append(z);
+ exp.append(c);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0x2000, 0x1000, a);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in, exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(0x4000, r);
+ exp.append(a);
+ exp.append(b);
+ exp.append(a);
+ exp.append(c);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, 0x1000, c);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist in, exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(0x4000, r);
+ exp.append(c);
+ exp.append(b);
+ exp.append(a);
+ exp.append(c);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, BufferCacheReadTest) {
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ {
+ auto ch = store->open_collection(cid);
+ ASSERT_FALSE(ch);
+ }
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append("abcde");
+ t.write(cid, hoid, 0, 5, bl);
+ t.write(cid, hoid, 10, 5, bl);
+ cerr << "TwinWrite" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0, 15, newdata);
+ ASSERT_EQ(r, 15);
+ {
+ bufferlist expected;
+ expected.append(bl);
+ expected.append_zero(5);
+ expected.append(bl);
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ }
+ //overwrite over the same extents
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append("edcba");
+ t.write(cid, hoid, 0, 5, bl);
+ t.write(cid, hoid, 10, 5, bl);
+ cerr << "TwinWrite" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0, 15, newdata);
+ ASSERT_EQ(r, 15);
+ {
+ bufferlist expected;
+ expected.append(bl);
+ expected.append_zero(5);
+ expected.append(bl);
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ }
+ //additional write to an unused region of some blob
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl2, newdata;
+ bl2.append("1234567890");
+
+ t.write(cid, hoid, 20, bl2.length(), bl2);
+ cerr << "Append" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0, 30, newdata);
+ ASSERT_EQ(r, 30);
+ {
+ bufferlist expected;
+ expected.append("edcba");
+ expected.append_zero(5);
+ expected.append("edcba");
+ expected.append_zero(5);
+ expected.append(bl2);
+
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ }
+ //additional write to an unused region of some blob and partial owerite over existing extents
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, bl2, bl3, newdata;
+ bl.append("DCB");
+ bl2.append("1234567890");
+ bl3.append("BA");
+
+ t.write(cid, hoid, 30, bl2.length(), bl2);
+ t.write(cid, hoid, 1, bl.length(), bl);
+ t.write(cid, hoid, 13, bl3.length(), bl3);
+ cerr << "TripleWrite" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0, 40, newdata);
+ ASSERT_EQ(r, 40);
+ {
+ bufferlist expected;
+ expected.append("eDCBa");
+ expected.append_zero(5);
+ expected.append("edcBA");
+ expected.append_zero(5);
+ expected.append(bl2);
+ expected.append(bl2);
+
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ }
+}
+
+void StoreTest::doCompressionTest()
+{
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ {
+ auto ch = store->open_collection(cid);
+ ASSERT_FALSE(ch);
+ }
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ std::string data;
+ data.resize(0x10000 * 4);
+ for(size_t i = 0;i < data.size(); i++)
+ data[i] = i / 256;
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append(data);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "CompressibleData (4xAU) Write" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0, data.size() , newdata);
+
+ ASSERT_EQ(r, (int)data.size());
+ {
+ bufferlist expected;
+ expected.append(data);
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ r = store->read(ch, hoid, 0, 711 , newdata);
+ ASSERT_EQ(r, 711);
+ {
+ bufferlist expected;
+ expected.append(data.substr(0,711));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ r = store->read(ch, hoid, 0xf00f, data.size(), newdata);
+ ASSERT_EQ(r, int(data.size() - 0xf00f) );
+ {
+ bufferlist expected;
+ expected.append(data.substr(0xf00f));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)data.size());
+ ASSERT_LE(statfs.data_compressed, (unsigned)data.size());
+ ASSERT_EQ(statfs.data_compressed_original, (unsigned)data.size());
+ ASSERT_LE(statfs.data_compressed_allocated, (unsigned)data.size());
+ }
+ }
+ std::string data2;
+ data2.resize(0x10000 * 4 - 0x9000);
+ for(size_t i = 0;i < data2.size(); i++)
+ data2[i] = (i+1) / 256;
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append(data2);
+ t.write(cid, hoid, 0x8000, bl.length(), bl);
+ cerr << "CompressibleData partial overwrite" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0, 0x10000, newdata);
+ ASSERT_EQ(r, (int)0x10000);
+ {
+ bufferlist expected;
+ expected.append(data.substr(0, 0x8000));
+ expected.append(data2.substr(0, 0x8000));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ r = store->read(ch, hoid, 0x9000, 711 , newdata);
+ ASSERT_EQ(r, 711);
+ {
+ bufferlist expected;
+ expected.append(data2.substr(0x1000,711));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ r = store->read(ch, hoid, 0x0, 0x40000, newdata);
+ ASSERT_EQ(r, int(0x40000) );
+ {
+ bufferlist expected;
+ expected.append(data.substr(0, 0x8000));
+ expected.append(data2.substr(0, 0x37000));
+ expected.append(data.substr(0x3f000, 0x1000));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ }
+ data2.resize(0x3f000);
+ for(size_t i = 0;i < data2.size(); i++)
+ data2[i] = (i+2) / 256;
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append(data2);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "CompressibleData partial overwrite, two extents overlapped, single one to be removed" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0, 0x3e000 - 1, newdata);
+ ASSERT_EQ(r, (int)0x3e000 - 1);
+ {
+ bufferlist expected;
+ expected.append(data2.substr(0, 0x3e000 - 1));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ r = store->read(ch, hoid, 0x3e000-1, 0x2001, newdata);
+ ASSERT_EQ(r, 0x2001);
+ {
+ bufferlist expected;
+ expected.append(data2.substr(0x3e000-1, 0x1001));
+ expected.append(data.substr(0x3f000, 0x1000));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ r = store->read(ch, hoid, 0x0, 0x40000, newdata);
+ ASSERT_EQ(r, int(0x40000) );
+ {
+ bufferlist expected;
+ expected.append(data2.substr(0, 0x3f000));
+ expected.append(data.substr(0x3f000, 0x1000));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ }
+ data.resize(0x1001);
+ for(size_t i = 0;i < data.size(); i++)
+ data[i] = (i+3) / 256;
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append(data);
+ t.write(cid, hoid, 0x3f000-1, bl.length(), bl);
+ cerr << "Small chunk partial overwrite, two extents overlapped, single one to be removed" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 0x3e000, 0x2000, newdata);
+ ASSERT_EQ(r, (int)0x2000);
+ {
+ bufferlist expected;
+ expected.append(data2.substr(0x3e000, 0x1000 - 1));
+ expected.append(data.substr(0, 0x1001));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ cerr << "Cleaning object" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ auto settingsBookmark = BookmarkSettings();
+ SetVal(g_conf(), "bluestore_compression_min_blob_size", "262144");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ {
+ data.resize(0x10000*6);
+
+ for(size_t i = 0;i < data.size(); i++)
+ data[i] = i / 256;
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append(data);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "CompressibleData large blob" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, CompressionTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "TODO: need to adjust statfs check for smr" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_compression_algorithm", "snappy");
+ SetVal(g_conf(), "bluestore_compression_mode", "force");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ doCompressionTest();
+
+ SetVal(g_conf(), "bluestore_compression_algorithm", "zlib");
+ SetVal(g_conf(), "bluestore_compression_mode", "aggressive");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ doCompressionTest();
+}
+
+TEST_P(StoreTest, SimpleObjectTest) {
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ {
+ auto ch = store->open_collection(cid);
+ ASSERT_FALSE(ch);
+ }
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.touch(cid, hoid);
+ cerr << "Remove then create" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ bl.append("abcde");
+ orig = bl;
+ t.remove(cid, hoid);
+ t.write(cid, hoid, 0, 5, bl);
+ cerr << "Remove then create" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 5, in);
+ ASSERT_EQ(5, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, exp;
+ bl.append("abcde");
+ exp = bl;
+ exp.append(bl);
+ t.write(cid, hoid, 5, 5, bl);
+ cerr << "Append" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 10, in);
+ ASSERT_EQ(10, r);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, exp;
+ bl.append("abcdeabcde");
+ exp = bl;
+ t.write(cid, hoid, 0, 10, bl);
+ cerr << "Full overwrite" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 10, in);
+ ASSERT_EQ(10, r);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append("abcde");
+ t.write(cid, hoid, 3, 5, bl);
+ cerr << "Partial overwrite" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in, exp;
+ exp.append("abcabcdede");
+ r = store->read(ch, hoid, 0, 10, in);
+ ASSERT_EQ(10, r);
+ in.hexdump(cout);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append("fghij");
+ t.truncate(cid, hoid, 0);
+ t.write(cid, hoid, 5, 5, bl);
+ cerr << "Truncate + hole" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append("abcde");
+ t.write(cid, hoid, 0, 5, bl);
+ cerr << "Reverse fill-in" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ bufferlist in, exp;
+ exp.append("abcdefghij");
+ r = store->read(ch, hoid, 0, 10, in);
+ ASSERT_EQ(10, r);
+ in.hexdump(cout);
+ ASSERT_TRUE(bl_eq(exp, in));
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append("abcde01234012340123401234abcde01234012340123401234abcde01234012340123401234abcde01234012340123401234");
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "larger overwrite" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, bl.length(), in);
+ ASSERT_EQ((int)bl.length(), r);
+ in.hexdump(cout);
+ ASSERT_TRUE(bl_eq(bl, in));
+ }
+ {
+ bufferlist bl;
+ bl.append("abcde01234012340123401234abcde01234012340123401234abcde01234012340123401234abcde01234012340123401234");
+
+ //test: offset=len=0 mean read all data
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 0, in);
+ ASSERT_EQ((int)bl.length(), r);
+ in.hexdump(cout);
+ ASSERT_TRUE(bl_eq(bl, in));
+ }
+ {
+ //verifying unaligned csums
+ std::string s1("1"), s2(0x1000, '2'), s3("00");
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(s1);
+ bl.append(s2);
+ t.truncate(cid, hoid, 0);
+ t.write(cid, hoid, 0x1000-1, bl.length(), bl);
+ cerr << "Write unaligned csum, stage 1" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ bufferlist in, exp1, exp2, exp3;
+ exp1.append(s1);
+ exp2.append(s2);
+ exp3.append(s3);
+ r = store->read(ch, hoid, 0x1000-1, 1, in);
+ ASSERT_EQ(1, r);
+ ASSERT_TRUE(bl_eq(exp1, in));
+ in.clear();
+ r = store->read(ch, hoid, 0x1000, 0x1000, in);
+ ASSERT_EQ(0x1000, r);
+ ASSERT_TRUE(bl_eq(exp2, in));
+
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(s3);
+ t.write(cid, hoid, 1, bl.length(), bl);
+ cerr << "Write unaligned csum, stage 2" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ in.clear();
+ r = store->read(ch, hoid, 1, 2, in);
+ ASSERT_EQ(2, r);
+ ASSERT_TRUE(bl_eq(exp3, in));
+ in.clear();
+ r = store->read(ch, hoid, 0x1000-1, 1, in);
+ ASSERT_EQ(1, r);
+ ASSERT_TRUE(bl_eq(exp1, in));
+ in.clear();
+ r = store->read(ch, hoid, 0x1000, 0x1000, in);
+ ASSERT_EQ(0x1000, r);
+ ASSERT_TRUE(bl_eq(exp2, in));
+
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+#if defined(WITH_BLUESTORE)
+
+TEST_P(StoreTestSpecificAUSize, ReproBug41901Test) {
+ if(string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP (smr)" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_max_blob_size", "524288");
+ SetVal(g_conf(), "bluestore_debug_enforce_settings", "hdd");
+ g_conf().apply_changes(nullptr);
+ StartDeferred(65536);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ const PerfCounters* logger = store->get_perf_counters();
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(4096, 'a');
+ bl.append(s);
+ t.write(cid, hoid, 0x11000, bl.length(), bl);
+ cerr << "write1" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(4096 * 3, 'a');
+ bl.append(s);
+ t.write(cid, hoid, 0x15000, bl.length(), bl);
+ cerr << "write2" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_unused), 1u);
+
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(4096 * 2, 'a');
+ bl.append(s);
+ t.write(cid, hoid, 0xe000, bl.length(), bl);
+ cerr << "write3" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 3u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_unused), 2u);
+
+
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(4096, 'a');
+ bl.append(s);
+ t.write(cid, hoid, 0xf000, bl.length(), bl);
+ t.write(cid, hoid, 0x10000, bl.length(), bl);
+ cerr << "write3" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 5u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_unused), 2u);
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+
+TEST_P(StoreTestSpecificAUSize, BluestoreStatFSTest) {
+ if(string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "TODO: fix this for smr" << std::endl;
+ return;
+ }
+ SetVal(g_conf(), "bluestore_block_db_path", "");
+ StartDeferred(65536);
+ SetVal(g_conf(), "bluestore_compression_mode", "force");
+ SetVal(g_conf(), "bluestore_max_blob_size", "524288");
+ // just a big number to disble gc
+ SetVal(g_conf(), "bluestore_gc_enable_total_threshold", "100000");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "true");
+ g_conf().apply_changes(nullptr);
+ int r;
+
+ int poolid = 4373;
+ coll_t cid = coll_t(spg_t(pg_t(0, poolid), shard_id_t::NO_SHARD));
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP),
+ string(),
+ 0,
+ poolid,
+ string()));
+ ghobject_t hoid2 = hoid;
+ hoid2.hobj.snap = 1;
+ {
+ auto ch = store->open_collection(cid);
+ ASSERT_FALSE(ch);
+ }
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ( 0u, statfs.allocated);
+ ASSERT_EQ( 0u, statfs.data_stored);
+ ASSERT_EQ(g_conf()->bluestore_block_size, statfs.total);
+ ASSERT_TRUE(statfs.available > 0u && statfs.available < g_conf()->bluestore_block_size);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ( 0u, statfs_pool.allocated);
+ ASSERT_EQ( 0u, statfs_pool.data_stored);
+
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append("abcde");
+ t.write(cid, hoid, 0, 5, bl);
+ cerr << "Append 5 bytes" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(5, statfs.data_stored);
+ ASSERT_EQ(0x10000, statfs.allocated);
+ ASSERT_EQ(0, statfs.data_compressed);
+ ASSERT_EQ(0, statfs.data_compressed_original);
+ ASSERT_EQ(0, statfs.data_compressed_allocated);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(5, statfs_pool.data_stored);
+ ASSERT_EQ(0x10000, statfs_pool.allocated);
+ ASSERT_EQ(0, statfs_pool.data_compressed);
+ ASSERT_EQ(0, statfs_pool.data_compressed_original);
+ ASSERT_EQ(0, statfs_pool.data_compressed_allocated);
+
+ // accessing unknown pool
+ r = store->pool_statfs(poolid + 1, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0, statfs_pool.data_stored);
+ ASSERT_EQ(0, statfs_pool.allocated);
+ ASSERT_EQ(0, statfs_pool.data_compressed);
+ ASSERT_EQ(0, statfs_pool.data_compressed_original);
+ ASSERT_EQ(0, statfs_pool.data_compressed_allocated);
+
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ ObjectStore::Transaction t;
+ std::string s(0x30000, 'a');
+ bufferlist bl;
+ bl.append(s);
+ t.write(cid, hoid, 0x10000, bl.length(), bl);
+ cerr << "Append 0x30000 compressible bytes" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30005, statfs.data_stored);
+ ASSERT_EQ(0x30000, statfs.allocated);
+ ASSERT_LE(statfs.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000, statfs.data_compressed_original);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x10000);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30005, statfs_pool.data_stored);
+ ASSERT_EQ(0x30000, statfs_pool.allocated);
+ ASSERT_LE(statfs_pool.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000, statfs_pool.data_compressed_original);
+ ASSERT_EQ(statfs_pool.data_compressed_allocated, 0x10000);
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, hoid, 1, 3);
+ t.zero(cid, hoid, 0x20000, 9);
+ cerr << "Punch hole at 1~3, 0x20000~9" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30005 - 3 - 9, statfs.data_stored);
+ ASSERT_EQ(0x30000, statfs.allocated);
+ ASSERT_LE(statfs.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000 - 9, statfs.data_compressed_original);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x10000);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30005 - 3 - 9, statfs_pool.data_stored);
+ ASSERT_EQ(0x30000, statfs_pool.allocated);
+ ASSERT_LE(statfs_pool.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000 - 9, statfs_pool.data_compressed_original);
+ ASSERT_EQ(statfs_pool.data_compressed_allocated, 0x10000);
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ ObjectStore::Transaction t;
+ std::string s(0x1000, 'b');
+ bufferlist bl;
+ bl.append(s);
+ t.write(cid, hoid, 1, bl.length(), bl);
+ t.write(cid, hoid, 0x10001, bl.length(), bl);
+ cerr << "Overwrite first and second(compressible) extents" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30001 - 9 + 0x1000, statfs.data_stored);
+ ASSERT_EQ(0x40000, statfs.allocated);
+ ASSERT_LE(statfs.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000 - 9 - 0x1000, statfs.data_compressed_original);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x10000);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30001 - 9 + 0x1000, statfs_pool.data_stored);
+ ASSERT_EQ(0x40000, statfs_pool.allocated);
+ ASSERT_LE(statfs_pool.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000 - 9 - 0x1000, statfs_pool.data_compressed_original);
+ ASSERT_EQ(statfs_pool.data_compressed_allocated, 0x10000);
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ ObjectStore::Transaction t;
+ std::string s(0x10000, 'c');
+ bufferlist bl;
+ bl.append(s);
+ t.write(cid, hoid, 0x10000, bl.length(), bl);
+ t.write(cid, hoid, 0x20000, bl.length(), bl);
+ t.write(cid, hoid, 0x30000, bl.length(), bl);
+ cerr << "Overwrite compressed extent with 3 uncompressible ones" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30000 + 0x1001, statfs.data_stored);
+ ASSERT_EQ(0x40000, statfs.allocated);
+ ASSERT_LE(statfs.data_compressed, 0);
+ ASSERT_EQ(0, statfs.data_compressed_original);
+ ASSERT_EQ(0, statfs.data_compressed_allocated);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30000 + 0x1001, statfs_pool.data_stored);
+ ASSERT_EQ(0x40000, statfs_pool.allocated);
+ ASSERT_LE(statfs_pool.data_compressed, 0);
+ ASSERT_EQ(0, statfs_pool.data_compressed_original);
+ ASSERT_EQ(0, statfs_pool.data_compressed_allocated);
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, hoid, 0, 0x40000);
+ cerr << "Zero object" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0u, statfs.allocated);
+ ASSERT_EQ(0u, statfs.data_stored);
+ ASSERT_EQ(0u, statfs.data_compressed_original);
+ ASSERT_EQ(0u, statfs.data_compressed);
+ ASSERT_EQ(0u, statfs.data_compressed_allocated);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0u, statfs_pool.allocated);
+ ASSERT_EQ(0u, statfs_pool.data_stored);
+ ASSERT_EQ(0u, statfs_pool.data_compressed_original);
+ ASSERT_EQ(0u, statfs_pool.data_compressed);
+ ASSERT_EQ(0u, statfs_pool.data_compressed_allocated);
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ ObjectStore::Transaction t;
+ std::string s(0x10000, 'c');
+ bufferlist bl;
+ bl.append(s);
+ bl.append(s);
+ bl.append(s);
+ bl.append(s.substr(0, 0x10000-2));
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "Yet another compressible write" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ struct store_statfs_t statfs;
+ r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x40000 - 2, statfs.data_stored);
+ ASSERT_EQ(0x30000, statfs.allocated);
+ ASSERT_LE(statfs.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000, statfs.data_compressed_original);
+ ASSERT_EQ(0x10000, statfs.data_compressed_allocated);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x40000 - 2, statfs_pool.data_stored);
+ ASSERT_EQ(0x30000, statfs_pool.allocated);
+ ASSERT_LE(statfs_pool.data_compressed, 0x10000);
+ ASSERT_EQ(0x20000, statfs_pool.data_compressed_original);
+ ASSERT_EQ(0x10000, statfs_pool.data_compressed_allocated);
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+ {
+ struct store_statfs_t statfs;
+ r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+
+ ObjectStore::Transaction t;
+ t.clone(cid, hoid, hoid2);
+ cerr << "Clone compressed objecte" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ struct store_statfs_t statfs2;
+ r = store->statfs(&statfs2);
+ ASSERT_EQ(r, 0);
+ ASSERT_GT(statfs2.data_stored, statfs.data_stored);
+ ASSERT_EQ(statfs2.allocated, statfs.allocated);
+ ASSERT_GT(statfs2.data_compressed, statfs.data_compressed);
+ ASSERT_GT(statfs2.data_compressed_original, statfs.data_compressed_original);
+ ASSERT_EQ(statfs2.data_compressed_allocated, statfs.data_compressed_allocated);
+
+ struct store_statfs_t statfs2_pool;
+ r = store->pool_statfs(poolid, &statfs2_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_GT(statfs2_pool.data_stored, statfs_pool.data_stored);
+ ASSERT_EQ(statfs2_pool.allocated, statfs_pool.allocated);
+ ASSERT_GT(statfs2_pool.data_compressed, statfs_pool.data_compressed);
+ ASSERT_GT(statfs2_pool.data_compressed_original,
+ statfs_pool.data_compressed_original);
+ ASSERT_EQ(statfs2_pool.data_compressed_allocated,
+ statfs_pool.data_compressed_allocated);
+ }
+
+ {
+ // verify no
+ auto poolid2 = poolid + 1;
+ coll_t cid2 = coll_t(spg_t(pg_t(20, poolid2), shard_id_t::NO_SHARD));
+ ghobject_t hoid(hobject_t(sobject_t("Object 2", CEPH_NOSNAP),
+ string(),
+ 0,
+ poolid2,
+ string()));
+ auto ch = store->create_new_collection(cid2);
+
+ {
+
+ struct store_statfs_t statfs1_pool;
+ bool per_pool_omap;
+ int r = store->pool_statfs(poolid, &statfs1_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+
+ cerr << "Creating second collection " << cid2 << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid2, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ t = ObjectStore::Transaction();
+ bufferlist bl;
+ bl.append("abcde");
+ t.write(cid2, hoid, 0, 5, bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs2_pool;
+ r = store->pool_statfs(poolid2, &statfs2_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(5, statfs2_pool.data_stored);
+ ASSERT_EQ(0x10000, statfs2_pool.allocated);
+ ASSERT_EQ(0, statfs2_pool.data_compressed);
+ ASSERT_EQ(0, statfs2_pool.data_compressed_original);
+ ASSERT_EQ(0, statfs2_pool.data_compressed_allocated);
+
+ struct store_statfs_t statfs1_pool_again;
+ r = store->pool_statfs(poolid, &statfs1_pool_again, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ // adjust 'available' since it has changed
+ statfs1_pool_again.available = statfs1_pool.available;
+ ASSERT_EQ(statfs1_pool_again, statfs1_pool);
+
+ t = ObjectStore::Transaction();
+ t.remove(cid2, hoid);
+ t.remove_collection(cid2);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ }
+
+ {
+ // verify ops on temporary object
+
+ auto poolid3 = poolid + 2;
+ coll_t cid3 = coll_t(spg_t(pg_t(20, poolid3), shard_id_t::NO_SHARD));
+ ghobject_t hoid3(hobject_t(sobject_t("Object 3", CEPH_NOSNAP),
+ string(),
+ 0,
+ poolid3,
+ string()));
+ ghobject_t hoid3_temp;
+ hoid3_temp.hobj = hoid3.hobj.make_temp_hobject("Object 3 temp");
+ auto ch3 = store->create_new_collection(cid3);
+ {
+ struct store_statfs_t statfs1_pool;
+ bool per_pool_omap;
+ int r = store->pool_statfs(poolid, &statfs1_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+
+ cerr << "Creating third collection " << cid3 << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid3, 0);
+ r = queue_transaction(store, ch3, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ t = ObjectStore::Transaction();
+ bufferlist bl;
+ bl.append("abcde");
+ t.write(cid3, hoid3_temp, 0, 5, bl);
+ r = queue_transaction(store, ch3, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs3_pool;
+ r = store->pool_statfs(poolid3, &statfs3_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(5, statfs3_pool.data_stored);
+ ASSERT_EQ(0x10000, statfs3_pool.allocated);
+ ASSERT_EQ(0, statfs3_pool.data_compressed);
+ ASSERT_EQ(0, statfs3_pool.data_compressed_original);
+ ASSERT_EQ(0, statfs3_pool.data_compressed_allocated);
+
+ struct store_statfs_t statfs1_pool_again;
+ r = store->pool_statfs(poolid, &statfs1_pool_again, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ // adjust 'available' since it has changed
+ statfs1_pool_again.available = statfs1_pool.available;
+ ASSERT_EQ(statfs1_pool_again, statfs1_pool);
+
+ //force fsck
+ ch.reset();
+ ch3.reset();
+ EXPECT_EQ(store->umount(), 0);
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ ch3 = store->open_collection(cid3);
+
+ t = ObjectStore::Transaction();
+ t.collection_move_rename(
+ cid3, hoid3_temp,
+ cid3, hoid3);
+ r = queue_transaction(store, ch3, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs3_pool_again;
+ r = store->pool_statfs(poolid3, &statfs3_pool_again, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs3_pool_again, statfs3_pool);
+
+ //force fsck
+ ch.reset();
+ ch3.reset();
+ EXPECT_EQ(store->umount(), 0);
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ ch3 = store->open_collection(cid3);
+
+ t = ObjectStore::Transaction();
+ t.remove(cid3, hoid3);
+ t.remove_collection(cid3);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch3, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ( 0u, statfs.allocated);
+ ASSERT_EQ( 0u, statfs.data_stored);
+ ASSERT_EQ( 0u, statfs.data_compressed_original);
+ ASSERT_EQ( 0u, statfs.data_compressed);
+ ASSERT_EQ( 0u, statfs.data_compressed_allocated);
+
+ struct store_statfs_t statfs_pool;
+ bool per_pool_omap;
+ r = store->pool_statfs(poolid, &statfs_pool, &per_pool_omap);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ( 0u, statfs_pool.allocated);
+ ASSERT_EQ( 0u, statfs_pool.data_stored);
+ ASSERT_EQ( 0u, statfs_pool.data_compressed_original);
+ ASSERT_EQ( 0u, statfs_pool.data_compressed);
+ ASSERT_EQ( 0u, statfs_pool.data_compressed_allocated);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BluestoreFragmentedBlobTest) {
+ if(string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "TODO: fix this for smr" << std::endl;
+ return;
+ }
+ SetVal(g_conf(), "bluestore_block_db_path", "");
+ StartDeferred(0x10000);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(g_conf()->bluestore_block_size, statfs.total);
+ ASSERT_EQ(0u, statfs.allocated);
+ ASSERT_EQ(0u, statfs.data_stored);
+ ASSERT_TRUE(statfs.available > 0u && statfs.available < g_conf()->bluestore_block_size);
+ }
+ std::string data;
+ data.resize(0x10000 * 3);
+ {
+ ObjectStore::Transaction t;
+ for(size_t i = 0;i < data.size(); i++)
+ data[i] = i / 256 + 1;
+ bufferlist bl, newdata;
+ bl.append(data);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ t.zero(cid, hoid, 0x10000, 0x10000);
+ cerr << "Append 3*0x10000 bytes and punch a hole 0x10000~10000" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x20000, statfs.data_stored);
+ ASSERT_EQ(0x20000, statfs.allocated);
+
+ r = store->read(ch, hoid, 0, data.size(), newdata);
+ ASSERT_EQ(r, (int)data.size());
+ {
+ bufferlist expected;
+ expected.append(data.substr(0, 0x10000));
+ expected.append(string(0x10000, 0));
+ expected.append(data.substr(0x20000, 0x10000));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+
+ r = store->read(ch, hoid, 1, data.size()-2, newdata);
+ ASSERT_EQ(r, (int)data.size()-2);
+ {
+ bufferlist expected;
+ expected.append(data.substr(1, 0x10000-1));
+ expected.append(string(0x10000, 0));
+ expected.append(data.substr(0x20000, 0x10000 - 1));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ }
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+
+ {
+ ObjectStore::Transaction t;
+ std::string data2(3, 'b');
+ bufferlist bl, newdata;
+ bl.append(data2);
+ t.write(cid, hoid, 0x20000, bl.length(), bl);
+ cerr << "Write 3 bytes after the hole" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x20000, statfs.allocated);
+ ASSERT_EQ(0x20000, statfs.data_stored);
+
+ r = store->read(ch, hoid, 0x20000-1, 21, newdata);
+ ASSERT_EQ(r, (int)21);
+ {
+ bufferlist expected;
+ expected.append(string(0x1, 0));
+ expected.append(string(data2));
+ expected.append(data.substr(0x20003, 21-4));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ }
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+
+ {
+ ObjectStore::Transaction t;
+ std::string data2(3, 'a');
+ bufferlist bl, newdata;
+ bl.append(data2);
+ t.write(cid, hoid, 0x10000+1, bl.length(), bl);
+ cerr << "Write 3 bytes to the hole" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x30000, statfs.allocated);
+ ASSERT_EQ(0x20003, statfs.data_stored);
+
+ r = store->read(ch, hoid, 0x10000-1, 0x10000+22, newdata);
+ ASSERT_EQ(r, (int)0x10000+22);
+ {
+ bufferlist expected;
+ expected.append(data.substr(0x10000-1, 1));
+ expected.append(string(0x1, 0));
+ expected.append(data2);
+ expected.append(string(0x10000-4, 0));
+ expected.append(string(0x3, 'b'));
+ expected.append(data.substr(0x20004, 21-3));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, newdata;
+ bl.append(string(0x30000, 'c'));
+ t.write(cid, hoid, 0, 0x30000, bl);
+ t.zero(cid, hoid, 0, 0x10000);
+ t.zero(cid, hoid, 0x20000, 0x10000);
+ cerr << "Rewrite an object and create two holes at the beginning and the end" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(0x10000, statfs.allocated);
+ ASSERT_EQ(0x10000, statfs.data_stored);
+
+ r = store->read(ch, hoid, 0, 0x30000, newdata);
+ ASSERT_EQ(r, (int)0x30000);
+ {
+ bufferlist expected;
+ expected.append(string(0x10000, 0));
+ expected.append(string(0x10000, 'c'));
+ expected.append(string(0x10000, 0));
+ ASSERT_TRUE(bl_eq(expected, newdata));
+ }
+ newdata.clear();
+ }
+
+ //force fsck
+ ch.reset();
+ EXPECT_EQ(store->umount(), 0);
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ struct store_statfs_t statfs;
+ r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ( 0u, statfs.allocated);
+ ASSERT_EQ( 0u, statfs.data_stored);
+ ASSERT_EQ( 0u, statfs.data_compressed_original);
+ ASSERT_EQ( 0u, statfs.data_compressed);
+ ASSERT_EQ( 0u, statfs.data_compressed_allocated);
+ }
+}
+#endif
+
+TEST_P(StoreTest, ManySmallWrite) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ ghobject_t b(hobject_t(sobject_t("Object 2", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ bufferptr bp(4096);
+ bp.zero();
+ bl.append(bp);
+ for (int i=0; i<100; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, a, i*4096, 4096, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ for (int i=0; i<100; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, b, (rand() % 1024)*4096, 4096, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove(cid, b);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, MultiSmallWriteSameBlock) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ bl.append("short");
+ C_SaferCond c, d;
+ // touch same block in both same transaction, tls, and pipelined txns
+ {
+ ObjectStore::Transaction t, u;
+ t.write(cid, a, 0, 5, bl, 0);
+ t.write(cid, a, 5, 5, bl, 0);
+ t.write(cid, a, 4094, 5, bl, 0);
+ t.write(cid, a, 9000, 5, bl, 0);
+ u.write(cid, a, 10, 5, bl, 0);
+ u.write(cid, a, 7000, 5, bl, 0);
+ t.register_on_commit(&c);
+ vector<ObjectStore::Transaction> v = {t, u};
+ store->queue_transactions(ch, v);
+ }
+ {
+ ObjectStore::Transaction t, u;
+ t.write(cid, a, 40, 5, bl, 0);
+ t.write(cid, a, 45, 5, bl, 0);
+ t.write(cid, a, 4094, 5, bl, 0);
+ t.write(cid, a, 6000, 5, bl, 0);
+ u.write(cid, a, 610, 5, bl, 0);
+ u.write(cid, a, 11000, 5, bl, 0);
+ t.register_on_commit(&d);
+ vector<ObjectStore::Transaction> v = {t, u};
+ store->queue_transactions(ch, v);
+ }
+ c.wait();
+ d.wait();
+ {
+ bufferlist bl2;
+ r = store->read(ch, a, 0, 16000, bl2);
+ ASSERT_GE(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SmallSkipFront) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, a);
+ t.truncate(cid, a, 3000);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist bl;
+ bufferptr bp(4096);
+ memset(bp.c_str(), 1, 4096);
+ bl.append(bp);
+ ObjectStore::Transaction t;
+ t.write(cid, a, 4096, 4096, bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist bl;
+ ASSERT_EQ(8192, store->read(ch, a, 0, 8192, bl));
+ for (unsigned i=0; i<4096; ++i)
+ ASSERT_EQ(0, bl[i]);
+ for (unsigned i=4096; i<8192; ++i)
+ ASSERT_EQ(1, bl[i]);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, AppendDeferredVsTailCache) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("fooo", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ unsigned min_alloc = g_conf()->bluestore_min_alloc_size;
+ unsigned size = min_alloc / 3;
+ bufferptr bpa(size);
+ memset(bpa.c_str(), 1, bpa.length());
+ bufferlist bla;
+ bla.append(bpa);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, 0, bla.length(), bla, 0);
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // force cached tail to clear ...
+ {
+ ch.reset();
+ int r = store->umount();
+ ASSERT_EQ(0, r);
+ r = store->mount();
+ ASSERT_EQ(0, r);
+ ch = store->open_collection(cid);
+ }
+
+ bufferptr bpb(size);
+ memset(bpb.c_str(), 2, bpb.length());
+ bufferlist blb;
+ blb.append(bpb);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, bla.length(), blb.length(), blb, 0);
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferptr bpc(size);
+ memset(bpc.c_str(), 3, bpc.length());
+ bufferlist blc;
+ blc.append(bpc);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, bla.length() + blb.length(), blc.length(), blc, 0);
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist final;
+ final.append(bla);
+ final.append(blb);
+ final.append(blc);
+ bufferlist actual;
+ {
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, a, 0, final.length(), actual));
+ ASSERT_TRUE(bl_eq(final, actual));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, AppendZeroTrailingSharedBlock) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("fooo", CEPH_NOSNAP)));
+ ghobject_t b = a;
+ b.hobj.snap = 1;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ unsigned min_alloc = g_conf()->bluestore_min_alloc_size;
+ unsigned size = min_alloc / 3;
+ bufferptr bpa(size);
+ memset(bpa.c_str(), 1, bpa.length());
+ bufferlist bla;
+ bla.append(bpa);
+ // make sure there is some trailing gunk in the last block
+ {
+ bufferlist bt;
+ bt.append(bla);
+ bt.append("BADBADBADBAD");
+ ObjectStore::Transaction t;
+ t.write(cid, a, 0, bt.length(), bt, 0);
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.truncate(cid, a, size);
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // clone
+ {
+ ObjectStore::Transaction t;
+ t.clone(cid, a, b);
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // append with implicit zeroing
+ bufferptr bpb(size);
+ memset(bpb.c_str(), 2, bpb.length());
+ bufferlist blb;
+ blb.append(bpb);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, min_alloc * 3, blb.length(), blb, 0);
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist final;
+ final.append(bla);
+ bufferlist zeros;
+ zeros.append_zero(min_alloc * 3 - size);
+ final.append(zeros);
+ final.append(blb);
+ bufferlist actual;
+ {
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, a, 0, final.length(), actual));
+ final.hexdump(cout);
+ actual.hexdump(cout);
+ ASSERT_TRUE(bl_eq(final, actual));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove(cid, b);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = store->queue_transaction(ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SmallSequentialUnaligned) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ int len = 1000;
+ bufferptr bp(len);
+ bp.zero();
+ bl.append(bp);
+ for (int i=0; i<1000; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, a, i*len, len, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, ManyBigWrite) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ ghobject_t b(hobject_t(sobject_t("Object 2", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ bufferptr bp(4 * 1048576);
+ bp.zero();
+ bl.append(bp);
+ for (int i=0; i<10; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, a, i*4*1048586, 4*1048576, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ // aligned
+ for (int i=0; i<10; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, b, (rand() % 256)*4*1048576, 4*1048576, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ // unaligned
+ for (int i=0; i<10; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, b, (rand() % (256*4096))*1024, 4*1048576, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ // do some zeros
+ for (int i=0; i<10; ++i) {
+ ObjectStore::Transaction t;
+ t.zero(cid, b, (rand() % (256*4096))*1024, 16*1048576);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove(cid, b);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, BigWriteBigZero) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ bufferptr bp(1048576);
+ memset(bp.c_str(), 'b', bp.length());
+ bl.append(bp);
+ bufferlist s;
+ bufferptr sp(4096);
+ memset(sp.c_str(), 's', sp.length());
+ s.append(sp);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, a, bl.length() / 4, bl.length() / 2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, bl.length() / 2, s.length(), s);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, MiscFragmentTests) {
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ bufferptr bp(524288);
+ bp.zero();
+ bl.append(bp);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, 0, 524288, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, 1048576, 524288, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist inbl;
+ int r = store->read(ch, a, 524288 + 131072, 1024, inbl);
+ ASSERT_EQ(r, 1024);
+ ASSERT_EQ(inbl.length(), 1024u);
+ ASSERT_TRUE(inbl.is_zero());
+ }
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, a, 1048576 - 4096, 524288, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+}
+
+TEST_P(StoreTest, ZeroVsObjectSize) {
+ int r;
+ coll_t cid;
+ struct stat stat;
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist a;
+ a.append("stuff");
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, 5, a);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(0, store->stat(ch, hoid, &stat));
+ ASSERT_EQ(5, stat.st_size);
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, hoid, 1, 2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(0, store->stat(ch, hoid, &stat));
+ ASSERT_EQ(5, stat.st_size);
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, hoid, 3, 200);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(0, store->stat(ch, hoid, &stat));
+ ASSERT_EQ(203, stat.st_size);
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, hoid, 100000, 200);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(0, store->stat(ch, hoid, &stat));
+ ASSERT_EQ(100200, stat.st_size);
+}
+
+TEST_P(StoreTest, ZeroLengthWrite) {
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist empty;
+ t.write(cid, hoid, 1048576, 0, empty);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ struct stat stat;
+ r = store->stat(ch, hoid, &stat);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(0, stat.st_size);
+
+ bufferlist newdata;
+ r = store->read(ch, hoid, 0, 1048576, newdata);
+ ASSERT_EQ(0, r);
+}
+
+TEST_P(StoreTest, ZeroLengthZero) {
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(0, r);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, hoid, 1048576, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(0, r);
+ }
+ struct stat stat;
+ r = store->stat(ch, hoid, &stat);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(0, stat.st_size);
+
+ bufferlist newdata;
+ r = store->read(ch, hoid, 0, 1048576, newdata);
+ ASSERT_EQ(0, r);
+}
+
+TEST_P(StoreTest, SimpleAttrTest) {
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("attr object 1", CEPH_NOSNAP)));
+ bufferlist val, val2;
+ val.append("value");
+ val.append("value2");
+ {
+ auto ch = store->open_collection(cid);
+ ASSERT_FALSE(ch);
+ }
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool empty;
+ int r = store->collection_empty(ch, &empty);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(empty);
+ }
+ {
+ bufferptr bp;
+ r = store->getattr(ch, hoid, "nofoo", bp);
+ ASSERT_EQ(-ENOENT, r);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.setattr(cid, hoid, "foo", val);
+ t.setattr(cid, hoid, "bar", val2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool empty;
+ int r = store->collection_empty(ch, &empty);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(!empty);
+ }
+ {
+ bufferptr bp;
+ r = store->getattr(ch, hoid, "nofoo", bp);
+ ASSERT_EQ(-ENODATA, r);
+
+ r = store->getattr(ch, hoid, "foo", bp);
+ ASSERT_EQ(0, r);
+ bufferlist bl;
+ bl.append(bp);
+ ASSERT_TRUE(bl_eq(val, bl));
+
+ map<string,bufferptr,less<>> bm;
+ r = store->getattrs(ch, hoid, bm);
+ ASSERT_EQ(0, r);
+
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SimpleListTest) {
+ int r;
+ coll_t cid(spg_t(pg_t(0, 1), shard_id_t(1)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ set<ghobject_t> all;
+ {
+ ObjectStore::Transaction t;
+ for (int i=0; i<200; ++i) {
+ string name("object_");
+ name += stringify(i);
+ ghobject_t hoid(hobject_t(sobject_t(name, CEPH_NOSNAP)),
+ ghobject_t::NO_GEN, shard_id_t(1));
+ hoid.hobj.pool = 1;
+ all.insert(hoid);
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ }
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ set<ghobject_t> saw;
+ vector<ghobject_t> objects;
+ ghobject_t next, current;
+ while (!next.is_max()) {
+ int r = collection_list(store, ch, current, ghobject_t::get_max(), 50,
+ &objects, &next);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(sorted(objects));
+ cout << " got " << objects.size() << " next " << next << std::endl;
+ for (vector<ghobject_t>::iterator p = objects.begin(); p != objects.end();
+ ++p) {
+ if (saw.count(*p)) {
+ cout << "got DUP " << *p << std::endl;
+ } else {
+ //cout << "got new " << *p << std::endl;
+ }
+ saw.insert(*p);
+ }
+ objects.clear();
+ current = next;
+ }
+ ASSERT_EQ(saw.size(), all.size());
+ ASSERT_EQ(saw, all);
+ }
+ {
+ ObjectStore::Transaction t;
+ for (set<ghobject_t>::iterator p = all.begin(); p != all.end(); ++p)
+ t.remove(cid, *p);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, ListEndTest) {
+ int r;
+ coll_t cid(spg_t(pg_t(0, 1), shard_id_t(1)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ set<ghobject_t> all;
+ {
+ ObjectStore::Transaction t;
+ for (int i=0; i<200; ++i) {
+ string name("object_");
+ name += stringify(i);
+ ghobject_t hoid(hobject_t(sobject_t(name, CEPH_NOSNAP)),
+ ghobject_t::NO_GEN, shard_id_t(1));
+ hoid.hobj.pool = 1;
+ all.insert(hoid);
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ }
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ghobject_t end(hobject_t(sobject_t("object_100", CEPH_NOSNAP)),
+ ghobject_t::NO_GEN, shard_id_t(1));
+ end.hobj.pool = 1;
+ vector<ghobject_t> objects;
+ ghobject_t next;
+ int r = collection_list(store, ch, ghobject_t(), end, 500, &objects, &next);
+ ASSERT_EQ(r, 0);
+ for (auto &p : objects) {
+ ASSERT_NE(p, end);
+ }
+ }
+ {
+ ObjectStore::Transaction t;
+ for (set<ghobject_t>::iterator p = all.begin(); p != all.end(); ++p)
+ t.remove(cid, *p);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, List_0xfffffff_Hash_Test_in_meta) {
+ int r = 0;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ ghobject_t hoid(hobject_t(sobject_t("obj", CEPH_NOSNAP),
+ "", UINT32_C(0xffffffff), -1, "nspace"));
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, nullptr, true);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(objects.size(), 1);
+ }
+}
+
+TEST_P(StoreTest, List_0xfffffff_Hash_Test_in_PG) {
+ int r = 0;
+ const int64_t poolid = 1;
+ coll_t cid(spg_t(pg_t(0, poolid), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ ghobject_t hoid(hobject_t(sobject_t("obj", CEPH_NOSNAP),
+ "", UINT32_C(0xffffffff), poolid, "nspace"));
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, nullptr, true);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(objects.size(), 1);
+ }
+}
+
+TEST_P(StoreTest, Sort) {
+ {
+ hobject_t a(sobject_t("a", CEPH_NOSNAP));
+ hobject_t b = a;
+ ASSERT_EQ(a, b);
+ b.oid.name = "b";
+ ASSERT_NE(a, b);
+ ASSERT_TRUE(a < b);
+ a.pool = 1;
+ b.pool = 2;
+ ASSERT_TRUE(a < b);
+ a.pool = 3;
+ ASSERT_TRUE(a > b);
+ }
+ {
+ ghobject_t a(hobject_t(sobject_t("a", CEPH_NOSNAP)));
+ ghobject_t b(hobject_t(sobject_t("b", CEPH_NOSNAP)));
+ a.hobj.pool = 1;
+ b.hobj.pool = 1;
+ ASSERT_TRUE(a < b);
+ a.hobj.pool = -3;
+ ASSERT_TRUE(a < b);
+ a.hobj.pool = 1;
+ b.hobj.pool = -3;
+ ASSERT_TRUE(a > b);
+ }
+}
+
+TEST_P(StoreTest, MultipoolListTest) {
+ int r;
+ int poolid = 4373;
+ coll_t cid = coll_t(spg_t(pg_t(0, poolid), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ set<ghobject_t> all, saw;
+ {
+ ObjectStore::Transaction t;
+ for (int i=0; i<200; ++i) {
+ string name("object_");
+ name += stringify(i);
+ ghobject_t hoid(hobject_t(sobject_t(name, CEPH_NOSNAP)));
+ if (rand() & 1)
+ hoid.hobj.pool = -2 - poolid;
+ else
+ hoid.hobj.pool = poolid;
+ all.insert(hoid);
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ }
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ vector<ghobject_t> objects;
+ ghobject_t next, current;
+ while (!next.is_max()) {
+ int r = collection_list(store, ch, current, ghobject_t::get_max(), 50,
+ &objects, &next);
+ ASSERT_EQ(r, 0);
+ cout << " got " << objects.size() << " next " << next << std::endl;
+ for (vector<ghobject_t>::iterator p = objects.begin(); p != objects.end();
+ ++p) {
+ saw.insert(*p);
+ }
+ objects.clear();
+ current = next;
+ }
+ ASSERT_EQ(saw, all);
+ }
+ {
+ ObjectStore::Transaction t;
+ for (set<ghobject_t>::iterator p = all.begin(); p != all.end(); ++p)
+ t.remove(cid, *p);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SimpleCloneTest) {
+ int r;
+ coll_t cid;
+
+ SetDeathTestStyle("threadsafe");
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP),
+ "key", 123, -1, ""));
+ bufferlist small, large, xlarge, newdata, attr;
+ small.append("small");
+ large.append("large");
+ xlarge.append("xlarge");
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.setattr(cid, hoid, "attr1", small);
+ t.setattr(cid, hoid, "attr2", large);
+ t.setattr(cid, hoid, "attr3", xlarge);
+ t.write(cid, hoid, 0, small.length(), small);
+ t.write(cid, hoid, 10, small.length(), small);
+ cerr << "Creating object and set attr " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ ghobject_t hoid2(hobject_t(sobject_t("Object 2", CEPH_NOSNAP),
+ "key", 123, -1, ""));
+ ghobject_t hoid3(hobject_t(sobject_t("Object 3", CEPH_NOSNAP)));
+ {
+ ObjectStore::Transaction t;
+ t.clone(cid, hoid, hoid2);
+ t.setattr(cid, hoid2, "attr2", small);
+ t.rmattr(cid, hoid2, "attr1");
+ t.write(cid, hoid, 10, large.length(), large);
+ t.setattr(cid, hoid, "attr1", large);
+ t.setattr(cid, hoid, "attr2", small);
+ cerr << "Clone object and rm attr" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ r = store->read(ch, hoid, 10, 5, newdata);
+ ASSERT_EQ(r, 5);
+ ASSERT_TRUE(bl_eq(large, newdata));
+
+ newdata.clear();
+ r = store->read(ch, hoid, 0, 5, newdata);
+ ASSERT_EQ(r, 5);
+ ASSERT_TRUE(bl_eq(small, newdata));
+
+ newdata.clear();
+ r = store->read(ch, hoid2, 10, 5, newdata);
+ ASSERT_EQ(r, 5);
+ ASSERT_TRUE(bl_eq(small, newdata));
+
+ r = store->getattr(ch, hoid2, "attr2", attr);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(bl_eq(small, attr));
+
+ attr.clear();
+ r = store->getattr(ch, hoid2, "attr3", attr);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(bl_eq(xlarge, attr));
+
+ attr.clear();
+ r = store->getattr(ch, hoid, "attr1", attr);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(bl_eq(large, attr));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ }
+ {
+ bufferlist final;
+ bufferptr p(16384);
+ memset(p.c_str(), 1, p.length());
+ bufferlist pl;
+ pl.append(p);
+ final.append(p);
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, pl.length(), pl);
+ t.clone(cid, hoid, hoid2);
+ bufferptr a(4096);
+ memset(a.c_str(), 2, a.length());
+ bufferlist al;
+ al.append(a);
+ final.append(a);
+ t.write(cid, hoid, pl.length(), a.length(), al);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ bufferlist rl;
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, hoid, 0, final.length(), rl));
+ ASSERT_TRUE(bl_eq(rl, final));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ }
+ {
+ bufferlist final;
+ bufferptr p(16384);
+ memset(p.c_str(), 111, p.length());
+ bufferlist pl;
+ pl.append(p);
+ final.append(p);
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, pl.length(), pl);
+ t.clone(cid, hoid, hoid2);
+ bufferptr z(4096);
+ z.zero();
+ final.append(z);
+ bufferptr a(4096);
+ memset(a.c_str(), 112, a.length());
+ bufferlist al;
+ al.append(a);
+ final.append(a);
+ t.write(cid, hoid, pl.length() + z.length(), a.length(), al);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ bufferlist rl;
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, hoid, 0, final.length(), rl));
+ ASSERT_TRUE(bl_eq(rl, final));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ }
+ {
+ bufferlist final;
+ bufferptr p(16000);
+ memset(p.c_str(), 5, p.length());
+ bufferlist pl;
+ pl.append(p);
+ final.append(p);
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, pl.length(), pl);
+ t.clone(cid, hoid, hoid2);
+ bufferptr z(1000);
+ z.zero();
+ final.append(z);
+ bufferptr a(8000);
+ memset(a.c_str(), 6, a.length());
+ bufferlist al;
+ al.append(a);
+ final.append(a);
+ t.write(cid, hoid, 17000, a.length(), al);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ bufferlist rl;
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, hoid, 0, final.length(), rl));
+ /*cout << "expected:\n";
+ final.hexdump(cout);
+ cout << "got:\n";
+ rl.hexdump(cout);*/
+ ASSERT_TRUE(bl_eq(rl, final));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ }
+ {
+ bufferptr p(1048576);
+ memset(p.c_str(), 3, p.length());
+ bufferlist pl;
+ pl.append(p);
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, pl.length(), pl);
+ t.clone(cid, hoid, hoid2);
+ bufferptr a(65536);
+ memset(a.c_str(), 4, a.length());
+ bufferlist al;
+ al.append(a);
+ t.write(cid, hoid, a.length(), a.length(), al);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ bufferlist rl;
+ bufferlist final;
+ final.substr_of(pl, 0, al.length());
+ final.append(al);
+ bufferlist end;
+ end.substr_of(pl, al.length()*2, pl.length() - al.length()*2);
+ final.append(end);
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, hoid, 0, final.length(), rl));
+ /*cout << "expected:\n";
+ final.hexdump(cout);
+ cout << "got:\n";
+ rl.hexdump(cout);*/
+ ASSERT_TRUE(bl_eq(rl, final));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ }
+ {
+ bufferptr p(65536);
+ memset(p.c_str(), 7, p.length());
+ bufferlist pl;
+ pl.append(p);
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, pl.length(), pl);
+ t.clone(cid, hoid, hoid2);
+ bufferptr a(4096);
+ memset(a.c_str(), 8, a.length());
+ bufferlist al;
+ al.append(a);
+ t.write(cid, hoid, 32768, a.length(), al);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ bufferlist rl;
+ bufferlist final;
+ final.substr_of(pl, 0, 32768);
+ final.append(al);
+ bufferlist end;
+ end.substr_of(pl, final.length(), pl.length() - final.length());
+ final.append(end);
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, hoid, 0, final.length(), rl));
+ /*cout << "expected:\n";
+ final.hexdump(cout);
+ cout << "got:\n";
+ rl.hexdump(cout);*/
+ ASSERT_TRUE(bl_eq(rl, final));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ }
+ {
+ bufferptr p(65536);
+ memset(p.c_str(), 9, p.length());
+ bufferlist pl;
+ pl.append(p);
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, pl.length(), pl);
+ t.clone(cid, hoid, hoid2);
+ bufferptr a(4096);
+ memset(a.c_str(), 10, a.length());
+ bufferlist al;
+ al.append(a);
+ t.write(cid, hoid, 33768, a.length(), al);
+ ASSERT_EQ(0, queue_transaction(store, ch, std::move(t)));
+ bufferlist rl;
+ bufferlist final;
+ final.substr_of(pl, 0, 33768);
+ final.append(al);
+ bufferlist end;
+ end.substr_of(pl, final.length(), pl.length() - final.length());
+ final.append(end);
+ ASSERT_EQ((int)final.length(),
+ store->read(ch, hoid, 0, final.length(), rl));
+ /*cout << "expected:\n";
+ final.hexdump(cout);
+ cout << "got:\n";
+ rl.hexdump(cout);*/
+ ASSERT_TRUE(bl_eq(rl, final));
+ }
+
+ {
+ //verify if non-empty collection is properly handled after store reload
+ ch.reset();
+ r = store->umount();
+ ASSERT_EQ(r, 0);
+ r = store->mount();
+ ASSERT_EQ(r, 0);
+ ch = store->open_collection(cid);
+
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ cerr << "Invalid rm coll" << std::endl;
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(queue_transaction(store, ch, std::move(t)), "");
+ }
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid3); //new record in db
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ //verify if non-empty collection is properly handled when there are some pending removes and live records in db
+ cerr << "Invalid rm coll again" << std::endl;
+ ch.reset();
+ r = store->umount();
+ ASSERT_EQ(r, 0);
+ r = store->mount();
+ ASSERT_EQ(r, 0);
+ ch = store->open_collection(cid);
+
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ t.remove_collection(cid);
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(queue_transaction(store, ch, std::move(t)), "");
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ t.remove(cid, hoid3);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, OmapSimple) {
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid(hobject_t(sobject_t("omap_obj", CEPH_NOSNAP),
+ "key", 123, -1, ""));
+ bufferlist small;
+ small.append("small");
+ map<string,bufferlist> km;
+ km["foo"] = small;
+ km["bar"].append("asdfjkasdkjdfsjkafskjsfdj");
+ bufferlist header;
+ header.append("this is a header");
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.omap_setkeys(cid, hoid, km);
+ t.omap_setheader(cid, hoid, header);
+ cerr << "Creating object and set omap " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ // get header, keys
+ {
+ bufferlist h;
+ map<string,bufferlist> r;
+ store->omap_get(ch, hoid, &h, &r);
+ ASSERT_TRUE(bl_eq(header, h));
+ ASSERT_EQ(r.size(), km.size());
+ cout << "r: " << r << std::endl;
+ }
+ // test iterator with seek_to_first
+ {
+ map<string,bufferlist> r;
+ ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(ch, hoid);
+ for (iter->seek_to_first(); iter->valid(); iter->next()) {
+ r[iter->key()] = iter->value();
+ }
+ cout << "r: " << r << std::endl;
+ ASSERT_EQ(r.size(), km.size());
+ }
+ // test iterator with initial lower_bound
+ {
+ map<string,bufferlist> r;
+ ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(ch, hoid);
+ for (iter->lower_bound(string()); iter->valid(); iter->next()) {
+ r[iter->key()] = iter->value();
+ }
+ cout << "r: " << r << std::endl;
+ ASSERT_EQ(r.size(), km.size());
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, OmapCloneTest) {
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP),
+ "key", 123, -1, ""));
+ bufferlist small;
+ small.append("small");
+ map<string,bufferlist> km;
+ km["foo"] = small;
+ km["bar"].append("asdfjkasdkjdfsjkafskjsfdj");
+ bufferlist header;
+ header.append("this is a header");
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.omap_setkeys(cid, hoid, km);
+ t.omap_setheader(cid, hoid, header);
+ cerr << "Creating object and set omap " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid2(hobject_t(sobject_t("Object 2", CEPH_NOSNAP),
+ "key", 123, -1, ""));
+ {
+ ObjectStore::Transaction t;
+ t.clone(cid, hoid, hoid2);
+ cerr << "Clone object" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ map<string,bufferlist> r;
+ bufferlist h;
+ store->omap_get(ch, hoid2, &h, &r);
+ ASSERT_TRUE(bl_eq(header, h));
+ ASSERT_EQ(r.size(), km.size());
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, SimpleCloneRangeTest) {
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ hoid.hobj.pool = -1;
+ bufferlist small, newdata;
+ small.append("small");
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 10, 5, small);
+ cerr << "Creating object and write bl " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid2(hobject_t(sobject_t("Object 2", CEPH_NOSNAP)));
+ hoid2.hobj.pool = -1;
+ {
+ ObjectStore::Transaction t;
+ t.clone_range(cid, hoid, hoid2, 10, 5, 10);
+ cerr << "Clone range object" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ r = store->read(ch, hoid2, 10, 5, newdata);
+ ASSERT_EQ(r, 5);
+ ASSERT_TRUE(bl_eq(small, newdata));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.truncate(cid, hoid, 1024*1024);
+ t.clone_range(cid, hoid, hoid2, 0, 1024*1024, 0);
+ cerr << "Clone range object" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ struct stat stat, stat2;
+ r = store->stat(ch, hoid, &stat);
+ r = store->stat(ch, hoid2, &stat2);
+ ASSERT_EQ(stat.st_size, stat2.st_size);
+ ASSERT_EQ(1024*1024, stat2.st_size);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+#if defined(WITH_BLUESTORE)
+TEST_P(StoreTest, BlueStoreUnshareBlobTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: non-deterministic behavior with smr" << std::endl;
+ return;
+ }
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ hoid.hobj.pool = -1;
+ ghobject_t hoid2(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ hoid2.hobj.pool = -1;
+ hoid2.generation = 2;
+ {
+ // check if blob is unshared properly
+ bufferlist data, newdata;
+ data.append(string(8192, 'a'));
+
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, data.length(), data);
+ cerr << "Creating object and write 8K " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ ObjectStore::Transaction t2;
+ t2.clone_range(cid, hoid, hoid2, 0, 4096, 0);
+ cerr << "Clone range object" << std::endl;
+ r = queue_transaction(store, ch, std::move(t2));
+ ASSERT_EQ(r, 0);
+
+ data.clear();
+ data.append(string(4096, 'b'));
+
+ ObjectStore::Transaction t3;
+ t3.write(cid, hoid, 0, data.length(), data);
+ cerr << "Writing 4k to source object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t3));
+ ASSERT_EQ(r, 0);
+
+ {
+ // this trims hoid one out of onode cache
+ EXPECT_EQ(store->umount(), 0);
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+
+ ObjectStore::Transaction t4;
+ t4.remove(cid, hoid2);
+ cerr << "Deleting dest object" << hoid2 << std::endl;
+ r = queue_transaction(store, ch, std::move(t4));
+ ASSERT_EQ(r, 0);
+
+ {
+ // this ensures remove operation submitted to kv store
+ EXPECT_EQ(store->umount(), 0);
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+
+ bufferlist resdata;
+ r = store->read(ch, hoid, 0, 0x2000, resdata);
+ ASSERT_EQ(r, 0x2000);
+
+ {
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+ auto* kv = bstore->get_kv();
+
+ // to be inline with BlueStore.cc
+ const string PREFIX_SHARED_BLOB = "X";
+
+ size_t cnt = 0;
+ auto it = kv->get_iterator(PREFIX_SHARED_BLOB);
+ ceph_assert(it);
+ for (it->lower_bound(string()); it->valid(); it->next()) {
+ ++cnt;
+ }
+ ASSERT_EQ(cnt, 0);
+ }
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, BlueStoreUnshareBlobBugTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ hoid.hobj.pool = -1;
+ ghobject_t hoid2(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ hoid2.hobj.pool = -1;
+ hoid2.generation = 2;
+ {
+ // check if blob is unshared properly
+ bufferlist data, newdata;
+ data.append(string(8192, 'a'));
+
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, data.length(), data);
+ cerr << "Creating object and write 8K " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ ObjectStore::Transaction t2;
+ t2.clone_range(cid, hoid, hoid2, 0, 4096, 0);
+ cerr << "Clone range object" << std::endl;
+ r = queue_transaction(store, ch, std::move(t2));
+ ASSERT_EQ(r, 0);
+
+ data.clear();
+ data.append(string(4096, 'b'));
+
+ ObjectStore::Transaction t3;
+ t3.write(cid, hoid, 0, data.length(), data);
+ cerr << "Writing 4k to source object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t3));
+ ASSERT_EQ(r, 0);
+
+ {
+ // this trims hoid one out of onode cache
+ EXPECT_EQ(store->umount(), 0);
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+
+ ObjectStore::Transaction t4;
+ t4.write(cid, hoid2, 0, data.length(), data);
+ cerr << "Writing 4k to second object " << hoid2 << std::endl;
+ r = queue_transaction(store, ch, std::move(t4));
+ ASSERT_EQ(r, 0);
+
+ bufferlist resdata;
+ r = store->read(ch, hoid, 0, 0x2000, resdata);
+ ASSERT_EQ(r, 0x2000);
+
+ {
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+ auto* kv = bstore->get_kv();
+
+ // to be inline with BlueStore.cc
+ const string PREFIX_SHARED_BLOB = "X";
+
+ size_t cnt = 0;
+ auto it = kv->get_iterator(PREFIX_SHARED_BLOB);
+ ceph_assert(it);
+ for (it->lower_bound(string()); it->valid(); it->next()) {
+ ++cnt;
+ }
+ // This shows a bug in unsharing a blob,
+ // after writing to 0x0~1000 to hoid2 share blob at hoid should be
+ //unshared but it doesn't in the current implementation
+ ASSERT_EQ(cnt, 1);
+ }
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+#endif
+
+TEST_P(StoreTest, SimpleObjectLongnameTest) {
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ghobject_t hoid(hobject_t(sobject_t("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaObjectaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1", CEPH_NOSNAP)));
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+ghobject_t generate_long_name(unsigned i)
+{
+ stringstream name;
+ name << "object id " << i << " ";
+ for (unsigned j = 0; j < 500; ++j) name << 'a';
+ ghobject_t hoid(hobject_t(sobject_t(name.str(), CEPH_NOSNAP)));
+ hoid.hobj.set_hash(i % 2);
+ return hoid;
+}
+
+TEST_P(StoreTest, LongnameSplitTest) {
+ int r;
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(0, r);
+ }
+ for (unsigned i = 0; i < 320; ++i) {
+ ObjectStore::Transaction t;
+ ghobject_t hoid = generate_long_name(i);
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(0, r);
+ }
+
+ ghobject_t test_obj = generate_long_name(319);
+ ghobject_t test_obj_2 = test_obj;
+ test_obj_2.generation = 0;
+ {
+ ObjectStore::Transaction t;
+ // should cause a split
+ t.collection_move_rename(
+ cid, test_obj,
+ cid, test_obj_2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(0, r);
+ }
+
+ for (unsigned i = 0; i < 319; ++i) {
+ ObjectStore::Transaction t;
+ ghobject_t hoid = generate_long_name(i);
+ t.remove(cid, hoid);
+ cerr << "Removing object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(0, r);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, test_obj_2);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(0, r);
+ }
+
+}
+
+TEST_P(StoreTest, ManyObjectTest) {
+ int NUM_OBJS = 2000;
+ int r = 0;
+ coll_t cid;
+ string base = "";
+ for (int i = 0; i < 100; ++i) base.append("aaaaa");
+ set<ghobject_t> created;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ for (int i = 0; i < NUM_OBJS; ++i) {
+ if (!(i % 5)) {
+ cerr << "Object " << i << std::endl;
+ }
+ ObjectStore::Transaction t;
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%d", i);
+ ghobject_t hoid(hobject_t(sobject_t(string(buf) + base, CEPH_NOSNAP)));
+ t.touch(cid, hoid);
+ created.insert(hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ for (set<ghobject_t>::iterator i = created.begin();
+ i != created.end();
+ ++i) {
+ struct stat buf;
+ ASSERT_TRUE(!store->stat(ch, *i, &buf));
+ }
+
+ set<ghobject_t> listed, listed2;
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, 0);
+ ASSERT_EQ(r, 0);
+
+ cerr << "objects.size() is " << objects.size() << std::endl;
+ for (vector<ghobject_t> ::iterator i = objects.begin();
+ i != objects.end();
+ ++i) {
+ listed.insert(*i);
+ ASSERT_TRUE(created.count(*i));
+ }
+ ASSERT_TRUE(listed.size() == created.size());
+
+ ghobject_t start, next;
+ objects.clear();
+ r = collection_list(
+ store,
+ ch,
+ ghobject_t::get_max(),
+ ghobject_t::get_max(),
+ 50,
+ &objects,
+ &next
+ );
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(objects.empty());
+
+ objects.clear();
+ listed.clear();
+ ghobject_t start2, next2;
+ while (1) {
+ r = collection_list(store, ch, start, ghobject_t::get_max(), 50, &objects,
+ &next);
+ ASSERT_TRUE(sorted(objects));
+ ASSERT_EQ(r, 0);
+ listed.insert(objects.begin(), objects.end());
+ if (objects.size() < 50) {
+ ASSERT_TRUE(next.is_max());
+ break;
+ }
+ objects.clear();
+
+ start = next;
+ }
+ cerr << "listed.size() is " << listed.size() << std::endl;
+ ASSERT_TRUE(listed.size() == created.size());
+ if (listed2.size()) {
+ ASSERT_EQ(listed.size(), listed2.size());
+ }
+ for (set<ghobject_t>::iterator i = listed.begin();
+ i != listed.end();
+ ++i) {
+ ASSERT_TRUE(created.count(*i));
+ }
+
+ for (set<ghobject_t>::iterator i = created.begin();
+ i != created.end();
+ ++i) {
+ ObjectStore::Transaction t;
+ t.remove(cid, *i);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ cerr << "cleaning up" << std::endl;
+ {
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+
+class ObjectGenerator {
+public:
+ virtual ghobject_t create_object(gen_type *gen) = 0;
+ virtual ~ObjectGenerator() {}
+};
+
+class MixedGenerator : public ObjectGenerator {
+public:
+ unsigned seq;
+ int64_t poolid;
+ explicit MixedGenerator(int64_t p) : seq(0), poolid(p) {}
+ ghobject_t create_object(gen_type *gen) override {
+ char buf[100];
+ snprintf(buf, sizeof(buf), "OBJ_%u", seq);
+ string name(buf);
+ if (seq % 2) {
+ for (unsigned i = 0; i < 300; ++i) {
+ name.push_back('a');
+ }
+ }
+ ++seq;
+ return ghobject_t(
+ hobject_t(
+ name, string(), rand() & 2 ? CEPH_NOSNAP : rand(),
+ (((seq / 1024) % 2) * 0xF00 ) +
+ (seq & 0xFF),
+ poolid, ""));
+ }
+};
+
+class SyntheticWorkloadState {
+ struct Object {
+ bufferlist data;
+ map<string, bufferlist> attrs;
+ };
+public:
+ static const unsigned max_in_flight = 16;
+ static const unsigned max_objects = 3000;
+ static const unsigned max_attr_size = 5;
+ static const unsigned max_attr_name_len = 100;
+ static const unsigned max_attr_value_len = 1024 * 64;
+ coll_t cid;
+ unsigned write_alignment;
+ unsigned max_object_len, max_write_len;
+ unsigned in_flight;
+ map<ghobject_t, Object> contents;
+ set<ghobject_t> available_objects;
+ set<ghobject_t>::iterator next_available_object;
+ set<ghobject_t> in_flight_objects;
+ ObjectGenerator *object_gen;
+ gen_type *rng;
+ ObjectStore *store;
+ ObjectStore::CollectionHandle ch;
+
+ ceph::mutex lock = ceph::make_mutex("State lock");
+ ceph::condition_variable cond;
+
+ struct EnterExit {
+ const char *msg;
+ explicit EnterExit(const char *m) : msg(m) {
+ //cout << pthread_self() << " enter " << msg << std::endl;
+ }
+ ~EnterExit() {
+ //cout << pthread_self() << " exit " << msg << std::endl;
+ }
+ };
+
+ class C_SyntheticOnReadable : public Context {
+ public:
+ SyntheticWorkloadState *state;
+ ghobject_t hoid;
+ C_SyntheticOnReadable(SyntheticWorkloadState *state, ghobject_t hoid)
+ : state(state), hoid(hoid) {}
+
+ void finish(int r) override {
+ std::lock_guard locker{state->lock};
+ EnterExit ee("onreadable finish");
+ ASSERT_TRUE(state->in_flight_objects.count(hoid));
+ ASSERT_EQ(r, 0);
+ state->in_flight_objects.erase(hoid);
+ if (state->contents.count(hoid))
+ state->available_objects.insert(hoid);
+ --(state->in_flight);
+ state->cond.notify_all();
+
+ bufferlist r2;
+ r = state->store->read(state->ch, hoid, 0, state->contents[hoid].data.length(), r2);
+ ceph_assert(bl_eq(state->contents[hoid].data, r2));
+ state->cond.notify_all();
+ }
+ };
+
+ class C_SyntheticOnStash : public Context {
+ public:
+ SyntheticWorkloadState *state;
+ ghobject_t oid, noid;
+
+ C_SyntheticOnStash(SyntheticWorkloadState *state,
+ ghobject_t oid, ghobject_t noid)
+ : state(state), oid(oid), noid(noid) {}
+
+ void finish(int r) override {
+ std::lock_guard locker{state->lock};
+ EnterExit ee("stash finish");
+ ASSERT_TRUE(state->in_flight_objects.count(oid));
+ ASSERT_EQ(r, 0);
+ state->in_flight_objects.erase(oid);
+ if (state->contents.count(noid))
+ state->available_objects.insert(noid);
+ --(state->in_flight);
+ bufferlist r2;
+ r = state->store->read(
+ state->ch, noid, 0,
+ state->contents[noid].data.length(), r2);
+ ceph_assert(bl_eq(state->contents[noid].data, r2));
+ state->cond.notify_all();
+ }
+ };
+
+ class C_SyntheticOnClone : public Context {
+ public:
+ SyntheticWorkloadState *state;
+ ghobject_t oid, noid;
+
+ C_SyntheticOnClone(SyntheticWorkloadState *state,
+ ghobject_t oid, ghobject_t noid)
+ : state(state), oid(oid), noid(noid) {}
+
+ void finish(int r) override {
+ std::lock_guard locker{state->lock};
+ EnterExit ee("clone finish");
+ ASSERT_TRUE(state->in_flight_objects.count(oid));
+ ASSERT_EQ(r, 0);
+ state->in_flight_objects.erase(oid);
+ if (state->contents.count(oid))
+ state->available_objects.insert(oid);
+ if (state->contents.count(noid))
+ state->available_objects.insert(noid);
+ --(state->in_flight);
+ bufferlist r2;
+ r = state->store->read(state->ch, noid, 0, state->contents[noid].data.length(), r2);
+ ceph_assert(bl_eq(state->contents[noid].data, r2));
+ state->cond.notify_all();
+ }
+ };
+
+ static void filled_byte_array(bufferlist& bl, size_t size)
+ {
+ static const char alphanum[] = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ if (!size) {
+ return;
+ }
+ bufferptr bp(size);
+ for (unsigned int i = 0; i < size - 1; i++) {
+ // severely limit entropy so we can compress...
+ bp[i] = alphanum[rand() % 10]; //(sizeof(alphanum) - 1)];
+ }
+ bp[size - 1] = '\0';
+
+ bl.append(bp);
+ }
+
+ SyntheticWorkloadState(ObjectStore *store,
+ ObjectGenerator *gen,
+ gen_type *rng,
+ coll_t cid,
+ unsigned max_size,
+ unsigned max_write,
+ unsigned alignment)
+ : cid(cid), write_alignment(alignment), max_object_len(max_size),
+ max_write_len(max_write), in_flight(0),
+ next_available_object(available_objects.end()),
+ object_gen(gen), rng(rng), store(store) {}
+
+ int init() {
+ ObjectStore::Transaction t;
+ ch = store->create_new_collection(cid);
+ t.create_collection(cid, 0);
+ return queue_transaction(store, ch, std::move(t));
+ }
+ void shutdown() {
+ ghobject_t next;
+ while (1) {
+ vector<ghobject_t> objects;
+ int r = collection_list(store, ch, next, ghobject_t::get_max(), 10,
+ &objects, &next);
+ ceph_assert(r >= 0);
+ if (objects.size() == 0)
+ break;
+ ObjectStore::Transaction t;
+ std::map<std::string, ceph::buffer::list> attrset;
+ for (vector<ghobject_t>::iterator p = objects.begin();
+ p != objects.end(); ++p) {
+ t.remove(cid, *p);
+ }
+ queue_transaction(store, ch, std::move(t));
+ }
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ queue_transaction(store, ch, std::move(t));
+ }
+ void statfs(store_statfs_t& stat) {
+ store->statfs(&stat);
+ }
+
+ ghobject_t get_uniform_random_object(std::unique_lock<ceph::mutex>& locker) {
+ cond.wait(locker, [this] {
+ return in_flight < max_in_flight && !available_objects.empty();
+ });
+ boost::uniform_int<> choose(0, available_objects.size() - 1);
+ int index = choose(*rng);
+ set<ghobject_t>::iterator i = available_objects.begin();
+ for ( ; index > 0; --index, ++i) ;
+ ghobject_t ret = *i;
+ return ret;
+ }
+
+ ghobject_t get_next_object(std::unique_lock<ceph::mutex>& locker) {
+ cond.wait(locker, [this] {
+ return in_flight < max_in_flight && !available_objects.empty();
+ });
+
+ if (next_available_object == available_objects.end()) {
+ next_available_object = available_objects.begin();
+ }
+
+ ghobject_t ret = *next_available_object;
+ ++next_available_object;
+ return ret;
+ }
+
+ void wait_for_ready(std::unique_lock<ceph::mutex>& locker) {
+ cond.wait(locker, [this] { return in_flight < max_in_flight; });
+ }
+
+ void wait_for_done() {
+ std::unique_lock locker{lock};
+ cond.wait(locker, [this] { return in_flight == 0; });
+ }
+
+ bool can_create() {
+ return (available_objects.size() + in_flight_objects.size()) < max_objects;
+ }
+
+ bool can_unlink() {
+ return (available_objects.size() + in_flight_objects.size()) > 0;
+ }
+
+ unsigned get_random_alloc_hints() {
+ unsigned f = 0;
+ {
+ boost::uniform_int<> u(0, 3);
+ switch (u(*rng)) {
+ case 1:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_SEQUENTIAL_WRITE;
+ break;
+ case 2:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_RANDOM_WRITE;
+ break;
+ }
+ }
+ {
+ boost::uniform_int<> u(0, 3);
+ switch (u(*rng)) {
+ case 1:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_SEQUENTIAL_READ;
+ break;
+ case 2:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_RANDOM_READ;
+ break;
+ }
+ }
+ {
+ // append_only, immutable
+ boost::uniform_int<> u(0, 4);
+ f |= u(*rng) << 4;
+ }
+ {
+ boost::uniform_int<> u(0, 3);
+ switch (u(*rng)) {
+ case 1:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_SHORTLIVED;
+ break;
+ case 2:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_LONGLIVED;
+ break;
+ }
+ }
+ {
+ boost::uniform_int<> u(0, 3);
+ switch (u(*rng)) {
+ case 1:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_COMPRESSIBLE;
+ break;
+ case 2:
+ f |= CEPH_OSD_ALLOC_HINT_FLAG_INCOMPRESSIBLE;
+ break;
+ }
+ }
+ return f;
+ }
+
+ int touch() {
+ std::unique_lock locker{lock};
+ EnterExit ee("touch");
+ if (!can_create())
+ return -ENOSPC;
+ wait_for_ready(locker);
+ ghobject_t new_obj = object_gen->create_object(rng);
+ available_objects.erase(new_obj);
+ ObjectStore::Transaction t;
+ t.touch(cid, new_obj);
+ boost::uniform_int<> u(17, 22);
+ boost::uniform_int<> v(12, 17);
+ t.set_alloc_hint(cid, new_obj,
+ 1ull << u(*rng),
+ 1ull << v(*rng),
+ get_random_alloc_hints());
+ ++in_flight;
+ in_flight_objects.insert(new_obj);
+ if (!contents.count(new_obj))
+ contents[new_obj] = Object();
+ t.register_on_applied(new C_SyntheticOnReadable(this, new_obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ int stash() {
+ std::unique_lock locker{lock};
+ EnterExit ee("stash");
+ if (!can_unlink())
+ return -ENOENT;
+ if (!can_create())
+ return -ENOSPC;
+ wait_for_ready(locker);
+
+ ghobject_t old_obj;
+ int max = 20;
+ do {
+ old_obj = get_uniform_random_object(locker);
+ } while (--max && !contents[old_obj].data.length());
+ available_objects.erase(old_obj);
+ ghobject_t new_obj = old_obj;
+ new_obj.generation++;
+ available_objects.erase(new_obj);
+
+ ObjectStore::Transaction t;
+ t.collection_move_rename(cid, old_obj, cid, new_obj);
+ ++in_flight;
+ in_flight_objects.insert(old_obj);
+
+ contents[new_obj].attrs = contents[old_obj].attrs;
+ contents[new_obj].data = contents[old_obj].data;
+ contents.erase(old_obj);
+ t.register_on_applied(new C_SyntheticOnStash(this, old_obj, new_obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ int clone() {
+ std::unique_lock locker{lock};
+ EnterExit ee("clone");
+ if (!can_unlink())
+ return -ENOENT;
+ if (!can_create())
+ return -ENOSPC;
+ wait_for_ready(locker);
+
+ ghobject_t old_obj;
+ int max = 20;
+ do {
+ old_obj = get_uniform_random_object(locker);
+ } while (--max && !contents[old_obj].data.length());
+ available_objects.erase(old_obj);
+ ghobject_t new_obj = object_gen->create_object(rng);
+ // make the hash match
+ new_obj.hobj.set_hash(old_obj.hobj.get_hash());
+ available_objects.erase(new_obj);
+
+ ObjectStore::Transaction t;
+ t.clone(cid, old_obj, new_obj);
+ ++in_flight;
+ in_flight_objects.insert(old_obj);
+
+ contents[new_obj].attrs = contents[old_obj].attrs;
+ contents[new_obj].data = contents[old_obj].data;
+
+ t.register_on_applied(new C_SyntheticOnClone(this, old_obj, new_obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ int clone_range() {
+ std::unique_lock locker{lock};
+ EnterExit ee("clone_range");
+ if (!can_unlink())
+ return -ENOENT;
+ if (!can_create())
+ return -ENOSPC;
+ wait_for_ready(locker);
+
+ ghobject_t old_obj;
+ int max = 20;
+ do {
+ old_obj = get_uniform_random_object(locker);
+ } while (--max && !contents[old_obj].data.length());
+ bufferlist &srcdata = contents[old_obj].data;
+ if (srcdata.length() == 0) {
+ return 0;
+ }
+ available_objects.erase(old_obj);
+ ghobject_t new_obj = get_uniform_random_object(locker);
+ available_objects.erase(new_obj);
+
+ boost::uniform_int<> u1(0, max_object_len - max_write_len);
+ boost::uniform_int<> u2(0, max_write_len);
+ uint64_t srcoff = u1(*rng);
+ // make src and dst offsets match, since that's what the osd does
+ uint64_t dstoff = srcoff; //u1(*rng);
+ uint64_t len = u2(*rng);
+ if (write_alignment) {
+ srcoff = round_up_to(srcoff, write_alignment);
+ dstoff = round_up_to(dstoff, write_alignment);
+ len = round_up_to(len, write_alignment);
+ }
+
+ if (srcoff > srcdata.length() - 1) {
+ srcoff = srcdata.length() - 1;
+ }
+ if (srcoff + len > srcdata.length()) {
+ len = srcdata.length() - srcoff;
+ }
+ if (0)
+ cout << __func__ << " from " << srcoff << "~" << len
+ << " (size " << srcdata.length() << ") to "
+ << dstoff << "~" << len << std::endl;
+
+ ObjectStore::Transaction t;
+ t.clone_range(cid, old_obj, new_obj, srcoff, len, dstoff);
+ ++in_flight;
+ in_flight_objects.insert(old_obj);
+
+ bufferlist bl;
+ if (srcoff < srcdata.length()) {
+ if (srcoff + len > srcdata.length()) {
+ bl.substr_of(srcdata, srcoff, srcdata.length() - srcoff);
+ } else {
+ bl.substr_of(srcdata, srcoff, len);
+ }
+ }
+
+ bufferlist& dstdata = contents[new_obj].data;
+ if (dstdata.length() <= dstoff) {
+ if (bl.length() > 0) {
+ dstdata.append_zero(dstoff - dstdata.length());
+ dstdata.append(bl);
+ }
+ } else {
+ bufferlist value;
+ ceph_assert(dstdata.length() > dstoff);
+ dstdata.cbegin().copy(dstoff, value);
+ value.append(bl);
+ if (value.length() < dstdata.length())
+ dstdata.cbegin(value.length()).copy(
+ dstdata.length() - value.length(), value);
+ value.swap(dstdata);
+ }
+
+ t.register_on_applied(new C_SyntheticOnClone(this, old_obj, new_obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+
+ int write() {
+ std::unique_lock locker{lock};
+ EnterExit ee("write");
+ if (!can_unlink())
+ return -ENOENT;
+ wait_for_ready(locker);
+
+ ghobject_t new_obj = get_uniform_random_object(locker);
+ available_objects.erase(new_obj);
+ ObjectStore::Transaction t;
+
+ boost::uniform_int<> u1(0, max_object_len - max_write_len);
+ boost::uniform_int<> u2(0, max_write_len);
+ uint64_t offset = u1(*rng);
+ uint64_t len = u2(*rng);
+ bufferlist bl;
+ if (write_alignment) {
+ offset = round_up_to(offset, write_alignment);
+ len = round_up_to(len, write_alignment);
+ }
+
+ filled_byte_array(bl, len);
+
+ bufferlist& data = contents[new_obj].data;
+ if (data.length() <= offset) {
+ if (len > 0) {
+ data.append_zero(offset-data.length());
+ data.append(bl);
+ }
+ } else {
+ bufferlist value;
+ ceph_assert(data.length() > offset);
+ data.cbegin().copy(offset, value);
+ value.append(bl);
+ if (value.length() < data.length())
+ data.cbegin(value.length()).copy(
+ data.length()-value.length(), value);
+ value.swap(data);
+ }
+
+ t.write(cid, new_obj, offset, len, bl);
+ ++in_flight;
+ in_flight_objects.insert(new_obj);
+ t.register_on_applied(new C_SyntheticOnReadable(this, new_obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ int truncate() {
+ std::unique_lock locker{lock};
+ EnterExit ee("truncate");
+ if (!can_unlink())
+ return -ENOENT;
+ wait_for_ready(locker);
+
+ ghobject_t obj = get_uniform_random_object(locker);
+ available_objects.erase(obj);
+ ObjectStore::Transaction t;
+
+ boost::uniform_int<> choose(0, max_object_len);
+ size_t len = choose(*rng);
+ if (write_alignment) {
+ len = round_up_to(len, write_alignment);
+ }
+
+ t.truncate(cid, obj, len);
+ ++in_flight;
+ in_flight_objects.insert(obj);
+ bufferlist& data = contents[obj].data;
+ if (data.length() <= len) {
+ data.append_zero(len - data.length());
+ } else {
+ bufferlist bl;
+ data.cbegin().copy(len, bl);
+ bl.swap(data);
+ }
+
+ t.register_on_applied(new C_SyntheticOnReadable(this, obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ int zero() {
+ std::unique_lock locker{lock};
+ EnterExit ee("zero");
+ if (!can_unlink())
+ return -ENOENT;
+ wait_for_ready(locker);
+
+ ghobject_t new_obj = get_uniform_random_object(locker);
+ available_objects.erase(new_obj);
+ ObjectStore::Transaction t;
+
+ boost::uniform_int<> u1(0, max_object_len - max_write_len);
+ boost::uniform_int<> u2(0, max_write_len);
+ uint64_t offset = u1(*rng);
+ uint64_t len = u2(*rng);
+ if (write_alignment) {
+ offset = round_up_to(offset, write_alignment);
+ len = round_up_to(len, write_alignment);
+ }
+
+ if (len > 0) {
+ auto& data = contents[new_obj].data;
+ if (data.length() < offset + len) {
+ data.append_zero(offset+len-data.length());
+ }
+ bufferlist n;
+ n.substr_of(data, 0, offset);
+ n.append_zero(len);
+ if (data.length() > offset + len)
+ data.cbegin(offset + len).copy(data.length() - offset - len, n);
+ data.swap(n);
+ }
+
+ t.zero(cid, new_obj, offset, len);
+ ++in_flight;
+ in_flight_objects.insert(new_obj);
+ t.register_on_applied(new C_SyntheticOnReadable(this, new_obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ void read() {
+ EnterExit ee("read");
+ boost::uniform_int<> u1(0, max_object_len/2);
+ boost::uniform_int<> u2(0, max_object_len);
+ uint64_t offset = u1(*rng);
+ uint64_t len = u2(*rng);
+ if (offset > len)
+ swap(offset, len);
+
+ ghobject_t obj;
+ bufferlist expected;
+ int r;
+ {
+ std::unique_lock locker{lock};
+ EnterExit ee("read locked");
+ if (!can_unlink())
+ return ;
+ wait_for_ready(locker);
+
+ obj = get_uniform_random_object(locker);
+ expected = contents[obj].data;
+ }
+ bufferlist bl, result;
+ if (0) cout << " obj " << obj
+ << " size " << expected.length()
+ << " offset " << offset
+ << " len " << len << std::endl;
+ r = store->read(ch, obj, offset, len, result);
+ if (offset >= expected.length()) {
+ ASSERT_EQ(r, 0);
+ } else {
+ size_t max_len = expected.length() - offset;
+ if (len > max_len)
+ len = max_len;
+ ceph_assert(len == result.length());
+ ASSERT_EQ(len, result.length());
+ expected.cbegin(offset).copy(len, bl);
+ ASSERT_EQ(r, (int)len);
+ ASSERT_TRUE(bl_eq(bl, result));
+ }
+ }
+
+ int setattrs() {
+ std::unique_lock locker{lock};
+ EnterExit ee("setattrs");
+ if (!can_unlink())
+ return -ENOENT;
+ wait_for_ready(locker);
+
+ ghobject_t obj = get_uniform_random_object(locker);
+ available_objects.erase(obj);
+ ObjectStore::Transaction t;
+
+ boost::uniform_int<> u0(1, max_attr_size);
+ boost::uniform_int<> u1(4, max_attr_name_len);
+ boost::uniform_int<> u2(4, max_attr_value_len);
+ boost::uniform_int<> u3(0, 100);
+ uint64_t size = u0(*rng);
+ uint64_t name_len;
+ map<string, bufferlist, less<>> attrs;
+ set<string> keys;
+ for (map<string, bufferlist>::iterator it = contents[obj].attrs.begin();
+ it != contents[obj].attrs.end(); ++it)
+ keys.insert(it->first);
+
+ while (size--) {
+ bufferlist name, value;
+ uint64_t get_exist = u3(*rng);
+ uint64_t value_len = u2(*rng);
+ filled_byte_array(value, value_len);
+ if (get_exist < 50 && keys.size()) {
+ set<string>::iterator k = keys.begin();
+ attrs[*k] = value;
+ contents[obj].attrs[*k] = value;
+ keys.erase(k);
+ } else {
+ name_len = u1(*rng);
+ filled_byte_array(name, name_len);
+ attrs[name.c_str()] = value;
+ contents[obj].attrs[name.c_str()] = value;
+ }
+ }
+ t.setattrs(cid, obj, attrs);
+ ++in_flight;
+ in_flight_objects.insert(obj);
+ t.register_on_applied(new C_SyntheticOnReadable(this, obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ int set_fixed_attrs(size_t entries, size_t key_size, size_t val_size) {
+ std::unique_lock locker{ lock };
+ EnterExit ee("setattrs");
+ if (!can_unlink())
+ return -ENOENT;
+ wait_for_ready(locker);
+
+ ghobject_t obj = get_next_object(locker);
+ available_objects.erase(obj);
+ ObjectStore::Transaction t;
+
+ map<string, bufferlist, less<>> attrs;
+ set<string> keys;
+
+ while (entries--) {
+ bufferlist name, value;
+ filled_byte_array(value, val_size);
+ filled_byte_array(name, key_size);
+ attrs[name.c_str()] = value;
+ contents[obj].attrs[name.c_str()] = value;
+ }
+ t.setattrs(cid, obj, attrs);
+ ++in_flight;
+ in_flight_objects.insert(obj);
+ t.register_on_applied(new C_SyntheticOnReadable(this, obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ void getattrs() {
+ EnterExit ee("getattrs");
+ ghobject_t obj;
+ map<string, bufferlist> expected;
+ {
+ std::unique_lock locker{lock};
+ EnterExit ee("getattrs locked");
+ if (!can_unlink())
+ return ;
+ wait_for_ready(locker);
+
+ int retry = 10;
+ do {
+ obj = get_uniform_random_object(locker);
+ if (!--retry)
+ return ;
+ } while (contents[obj].attrs.empty());
+ expected = contents[obj].attrs;
+ }
+ map<string, bufferlist, less<>> attrs;
+ int r = store->getattrs(ch, obj, attrs);
+ ASSERT_TRUE(r == 0);
+ ASSERT_TRUE(attrs.size() == expected.size());
+ for (map<string, bufferlist>::iterator it = expected.begin();
+ it != expected.end(); ++it) {
+ ASSERT_TRUE(bl_eq(attrs[it->first], it->second));
+ }
+ }
+
+ void getattr() {
+ EnterExit ee("getattr");
+ ghobject_t obj;
+ int r;
+ int retry;
+ map<string, bufferlist> expected;
+ {
+ std::unique_lock locker{lock};
+ EnterExit ee("getattr locked");
+ if (!can_unlink())
+ return ;
+ wait_for_ready(locker);
+
+ retry = 10;
+ do {
+ obj = get_uniform_random_object(locker);
+ if (!--retry)
+ return ;
+ } while (contents[obj].attrs.empty());
+ expected = contents[obj].attrs;
+ }
+ boost::uniform_int<> u(0, expected.size()-1);
+ retry = u(*rng);
+ map<string, bufferlist>::iterator it = expected.begin();
+ while (retry) {
+ retry--;
+ ++it;
+ }
+
+ bufferlist bl;
+ r = store->getattr(ch, obj, it->first, bl);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(bl_eq(it->second, bl));
+ }
+
+ int rmattr() {
+ std::unique_lock locker{lock};
+ EnterExit ee("rmattr");
+ if (!can_unlink())
+ return -ENOENT;
+ wait_for_ready(locker);
+
+ ghobject_t obj;
+ int retry = 10;
+ do {
+ obj = get_uniform_random_object(locker);
+ if (!--retry)
+ return 0;
+ } while (contents[obj].attrs.empty());
+
+ boost::uniform_int<> u(0, contents[obj].attrs.size()-1);
+ retry = u(*rng);
+ map<string, bufferlist>::iterator it = contents[obj].attrs.begin();
+ while (retry) {
+ retry--;
+ ++it;
+ }
+
+ available_objects.erase(obj);
+ ObjectStore::Transaction t;
+ t.rmattr(cid, obj, it->first);
+
+ contents[obj].attrs.erase(it->first);
+ ++in_flight;
+ in_flight_objects.insert(obj);
+ t.register_on_applied(new C_SyntheticOnReadable(this, obj));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ void fsck(bool deep) {
+ std::unique_lock locker{lock};
+ EnterExit ee("fsck");
+ cond.wait(locker, [this] { return in_flight == 0; });
+ ch.reset();
+ store->umount();
+ int r = store->fsck(deep);
+ ceph_assert(r == 0 || r == -EOPNOTSUPP);
+ store->mount();
+ ch = store->open_collection(cid);
+ }
+
+ void scan() {
+ std::unique_lock locker{lock};
+ EnterExit ee("scan");
+ cond.wait(locker, [this] { return in_flight == 0; });
+ vector<ghobject_t> objects;
+ set<ghobject_t> objects_set, objects_set2;
+ ghobject_t next, current;
+ while (1) {
+ //cerr << "scanning..." << std::endl;
+ int r = collection_list(store, ch, current, ghobject_t::get_max(), 100,
+ &objects, &next);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(sorted(objects));
+ objects_set.insert(objects.begin(), objects.end());
+ objects.clear();
+ if (next.is_max()) break;
+ current = next;
+ }
+ if (objects_set.size() != available_objects.size()) {
+ for (set<ghobject_t>::iterator p = objects_set.begin();
+ p != objects_set.end();
+ ++p)
+ if (available_objects.count(*p) == 0) {
+ cerr << "+ " << *p << std::endl;
+ ceph_abort();
+ }
+ for (set<ghobject_t>::iterator p = available_objects.begin();
+ p != available_objects.end();
+ ++p)
+ if (objects_set.count(*p) == 0)
+ cerr << "- " << *p << std::endl;
+ //cerr << " objects_set: " << objects_set << std::endl;
+ //cerr << " available_set: " << available_objects << std::endl;
+ ceph_abort_msg("badness");
+ }
+
+ ASSERT_EQ(objects_set.size(), available_objects.size());
+ for (set<ghobject_t>::iterator i = objects_set.begin();
+ i != objects_set.end();
+ ++i) {
+ ASSERT_GT(available_objects.count(*i), (unsigned)0);
+ }
+
+ int r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(),
+ INT_MAX, &objects, 0);
+ ASSERT_EQ(r, 0);
+ objects_set2.insert(objects.begin(), objects.end());
+ ASSERT_EQ(objects_set2.size(), available_objects.size());
+ for (set<ghobject_t>::iterator i = objects_set2.begin();
+ i != objects_set2.end();
+ ++i) {
+ ASSERT_GT(available_objects.count(*i), (unsigned)0);
+ if (available_objects.count(*i) == 0) {
+ cerr << "+ " << *i << std::endl;
+ }
+ }
+ }
+
+ void stat() {
+ EnterExit ee("stat");
+ ghobject_t hoid;
+ uint64_t expected;
+ {
+ std::unique_lock locker{lock};
+ EnterExit ee("stat lock1");
+ if (!can_unlink())
+ return ;
+ hoid = get_uniform_random_object(locker);
+ in_flight_objects.insert(hoid);
+ available_objects.erase(hoid);
+ ++in_flight;
+ expected = contents[hoid].data.length();
+ }
+ struct stat buf;
+ int r = store->stat(ch, hoid, &buf);
+ ASSERT_EQ(0, r);
+ ceph_assert((uint64_t)buf.st_size == expected);
+ ASSERT_TRUE((uint64_t)buf.st_size == expected);
+ {
+ std::lock_guard locker{lock};
+ EnterExit ee("stat lock2");
+ --in_flight;
+ cond.notify_all();
+ in_flight_objects.erase(hoid);
+ available_objects.insert(hoid);
+ }
+ }
+
+ int unlink() {
+ std::unique_lock locker{lock};
+ EnterExit ee("unlink");
+ if (!can_unlink())
+ return -ENOENT;
+ ghobject_t to_remove = get_uniform_random_object(locker);
+ ObjectStore::Transaction t;
+ t.remove(cid, to_remove);
+ ++in_flight;
+ available_objects.erase(to_remove);
+ in_flight_objects.insert(to_remove);
+ contents.erase(to_remove);
+ t.register_on_applied(new C_SyntheticOnReadable(this, to_remove));
+ int status = store->queue_transaction(ch, std::move(t));
+ return status;
+ }
+
+ void print_internal_state() {
+ std::lock_guard locker{lock};
+ cerr << "available_objects: " << available_objects.size()
+ << " in_flight_objects: " << in_flight_objects.size()
+ << " total objects: " << in_flight_objects.size() + available_objects.size()
+ << " in_flight " << in_flight << std::endl;
+ }
+};
+
+
+void StoreTest::doSyntheticTest(
+ int num_ops,
+ uint64_t max_obj, uint64_t max_wr, uint64_t align)
+{
+ MixedGenerator gen(555);
+ gen_type rng(time(NULL));
+ coll_t cid(spg_t(pg_t(0,555), shard_id_t::NO_SHARD));
+
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "false");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ SyntheticWorkloadState test_obj(store.get(), &gen, &rng, cid,
+ max_obj, max_wr, align);
+ test_obj.init();
+ for (int i = 0; i < num_ops/10; ++i) {
+ if (!(i % 500)) cerr << "seeding object " << i << std::endl;
+ test_obj.touch();
+ }
+ for (int i = 0; i < num_ops; ++i) {
+ if (!(i % 1000)) {
+ cerr << "Op " << i << std::endl;
+ test_obj.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 999);
+ int val = true_false(rng);
+ if (val > 998) {
+ test_obj.fsck(true);
+ } else if (val > 997) {
+ test_obj.fsck(false);
+ } else if (val > 970) {
+ test_obj.scan();
+ } else if (val > 950) {
+ test_obj.stat();
+ } else if (val > 850) {
+ test_obj.zero();
+ } else if (val > 800) {
+ test_obj.unlink();
+ } else if (val > 550) {
+ test_obj.write();
+ } else if (val > 500) {
+ test_obj.clone();
+ } else if (val > 450) {
+ test_obj.clone_range();
+ } else if (val > 300) {
+ test_obj.stash();
+ } else if (val > 100) {
+ test_obj.read();
+ } else {
+ test_obj.truncate();
+ }
+ }
+ test_obj.wait_for_done();
+ test_obj.shutdown();
+}
+
+TEST_P(StoreTest, Synthetic) {
+ doSyntheticTest(10000, 400*1024, 40*1024, 0);
+}
+
+#if defined(WITH_BLUESTORE)
+TEST_P(StoreTestSpecificAUSize, SyntheticMatrixSharding) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ const char *m[][10] = {
+ { "bluestore_min_alloc_size", "4096", 0 }, // must be the first!
+ { "num_ops", "50000", 0 },
+ { "max_write", "65536", 0 },
+ { "max_size", "262144", 0 },
+ { "alignment", "4096", 0 },
+ { "bluestore_max_blob_size", "65536", 0 },
+ { "bluestore_extent_map_shard_min_size", "60", 0 },
+ { "bluestore_extent_map_shard_max_size", "300", 0 },
+ { "bluestore_extent_map_shard_target_size", "150", 0 },
+ { "bluestore_default_buffered_read", "true", 0 },
+ { "bluestore_default_buffered_write", "true", 0 },
+ { 0 },
+ };
+ do_matrix(m, std::bind(&StoreTest::doSyntheticTest, this, _1, _2, _3, _4));
+}
+
+TEST_P(StoreTestSpecificAUSize, ZipperPatternSharded) {
+ if(string(GetParam()) != "bluestore")
+ return;
+ StartDeferred(4096);
+
+ int r;
+ coll_t cid;
+ ghobject_t a(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ int len = 4096;
+ bufferptr bp(len);
+ bp.zero();
+ bl.append(bp);
+ for (int i=0; i<1000; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, a, i*2*len, len, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ for (int i=0; i<1000; ++i) {
+ ObjectStore::Transaction t;
+ t.write(cid, a, i*2*len + 1, len, bl, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, a);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, SyntheticMatrixCsumAlgorithm) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ const char *m[][10] = {
+ { "bluestore_min_alloc_size", "65536", 0 }, // must be the first!
+ { "max_write", "65536", 0 },
+ { "max_size", "1048576", 0 },
+ { "alignment", "16", 0 },
+ { "bluestore_csum_type", "crc32c", "crc32c_16", "crc32c_8", "xxhash32",
+ "xxhash64", "none", 0 },
+ { "bluestore_default_buffered_write", "false", 0 },
+ { 0 },
+ };
+ do_matrix(m, std::bind(&StoreTest::doSyntheticTest, this, _1, _2, _3, _4));
+}
+
+TEST_P(StoreTestSpecificAUSize, SyntheticMatrixCsumVsCompression) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ const char *m[][10] = {
+ { "bluestore_min_alloc_size", "4096", "16384", 0 }, //to be the first!
+ { "max_write", "131072", 0 },
+ { "max_size", "262144", 0 },
+ { "alignment", "512", 0 },
+ { "bluestore_compression_mode", "force", 0},
+ { "bluestore_compression_algorithm", "snappy", "zlib", 0 },
+ { "bluestore_csum_type", "crc32c", 0 },
+ { "bluestore_default_buffered_read", "true", "false", 0 },
+ { "bluestore_default_buffered_write", "true", "false", 0 },
+ { "bluestore_sync_submit_transaction", "false", 0 },
+ { 0 },
+ };
+ do_matrix(m, std::bind(&StoreTest::doSyntheticTest, this, _1, _2, _3, _4));
+}
+
+TEST_P(StoreTestSpecificAUSize, SyntheticMatrixCompression) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ const char *m[][10] = {
+ { "bluestore_min_alloc_size", "4096", "65536", 0 }, // to be the first!
+ { "max_write", "1048576", 0 },
+ { "max_size", "4194304", 0 },
+ { "alignment", "65536", 0 },
+ { "bluestore_compression_mode", "force", "aggressive", "passive", "none", 0},
+ { "bluestore_default_buffered_write", "false", 0 },
+ { "bluestore_sync_submit_transaction", "true", 0 },
+ { 0 },
+ };
+ do_matrix(m, std::bind(&StoreTest::doSyntheticTest, this, _1, _2, _3, _4));
+}
+
+TEST_P(StoreTestSpecificAUSize, SyntheticMatrixCompressionAlgorithm) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ const char *m[][10] = {
+ { "bluestore_min_alloc_size", "4096", "65536", 0 }, // to be the first!
+ { "max_write", "1048576", 0 },
+ { "max_size", "4194304", 0 },
+ { "alignment", "65536", 0 },
+ { "bluestore_compression_algorithm", "zlib", "snappy", 0 },
+ { "bluestore_compression_mode", "force", 0 },
+ { "bluestore_default_buffered_write", "false", 0 },
+ { 0 },
+ };
+ do_matrix(m, std::bind(&StoreTest::doSyntheticTest, this, _1, _2, _3, _4));
+}
+
+TEST_P(StoreTestSpecificAUSize, SyntheticMatrixNoCsum) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ const char *m[][10] = {
+ { "bluestore_min_alloc_size", "4096", "65536", 0 }, // to be the first!
+ { "max_write", "65536", 0 },
+ { "max_size", "1048576", 0 },
+ { "alignment", "512", 0 },
+ { "bluestore_max_blob_size", "262144", 0 },
+ { "bluestore_compression_mode", "force", "none", 0},
+ { "bluestore_csum_type", "none", 0},
+ { "bluestore_default_buffered_read", "true", "false", 0 },
+ { "bluestore_default_buffered_write", "true", 0 },
+ { "bluestore_sync_submit_transaction", "true", "false", 0 },
+ { 0 },
+ };
+ do_matrix(m, std::bind(&StoreTest::doSyntheticTest, this, _1, _2, _3, _4));
+}
+
+TEST_P(StoreTestSpecificAUSize, SyntheticMatrixPreferDeferred) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ const char *m[][10] = {
+ { "bluestore_min_alloc_size", "4096", "65536", 0 }, // to be the first!
+ { "max_write", "65536", 0 },
+ { "max_size", "1048576", 0 },
+ { "alignment", "512", 0 },
+ { "bluestore_max_blob_size", "262144", 0 },
+ { "bluestore_compression_mode", "force", "none", 0},
+ { "bluestore_prefer_deferred_size", "32768", "0", 0},
+ { 0 },
+ };
+ do_matrix(m, std::bind(&StoreTest::doSyntheticTest, this, _1, _2, _3, _4));
+}
+#endif // WITH_BLUESTORE
+
+TEST_P(StoreTest, AttrSynthetic) {
+ MixedGenerator gen(447);
+ gen_type rng(time(NULL));
+ coll_t cid(spg_t(pg_t(0,447),shard_id_t::NO_SHARD));
+
+ SyntheticWorkloadState test_obj(store.get(), &gen, &rng, cid, 40*1024, 4*1024, 0);
+ test_obj.init();
+ for (int i = 0; i < 500; ++i) {
+ if (!(i % 10)) cerr << "seeding object " << i << std::endl;
+ test_obj.touch();
+ }
+ for (int i = 0; i < 1000; ++i) {
+ if (!(i % 100)) {
+ cerr << "Op " << i << std::endl;
+ test_obj.print_internal_state();
+ }
+ boost::uniform_int<> true_false(0, 99);
+ int val = true_false(rng);
+ if (val > 97) {
+ test_obj.scan();
+ } else if (val > 93) {
+ test_obj.stat();
+ } else if (val > 75) {
+ test_obj.rmattr();
+ } else if (val > 47) {
+ test_obj.setattrs();
+ } else if (val > 45) {
+ test_obj.clone();
+ } else if (val > 37) {
+ test_obj.stash();
+ } else if (val > 30) {
+ test_obj.getattrs();
+ } else {
+ test_obj.getattr();
+ }
+ }
+ test_obj.wait_for_done();
+ test_obj.shutdown();
+}
+
+TEST_P(StoreTest, HashCollisionTest) {
+ int64_t poolid = 11;
+ coll_t cid(spg_t(pg_t(0,poolid),shard_id_t::NO_SHARD));
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ string base = "";
+ for (int i = 0; i < 100; ++i) base.append("aaaaa");
+ set<ghobject_t> created;
+ for (int n = 0; n < 10; ++n) {
+ char nbuf[100];
+ sprintf(nbuf, "n%d", n);
+ for (int i = 0; i < 1000; ++i) {
+ char buf[100];
+ sprintf(buf, "%d", i);
+ if (!(i % 100)) {
+ cerr << "Object n" << n << " "<< i << std::endl;
+ }
+ ghobject_t hoid(hobject_t(string(buf) + base, string(), CEPH_NOSNAP, 0, poolid, string(nbuf)));
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ created.insert(hoid);
+ }
+ }
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, 0);
+ ASSERT_EQ(r, 0);
+ set<ghobject_t> listed(objects.begin(), objects.end());
+ cerr << "listed.size() is " << listed.size() << " and created.size() is " << created.size() << std::endl;
+ ASSERT_TRUE(listed.size() == created.size());
+ objects.clear();
+ listed.clear();
+ ghobject_t current, next;
+ while (1) {
+ r = collection_list(store, ch, current, ghobject_t::get_max(), 60, &objects,
+ &next);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(sorted(objects));
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end();
+ ++i) {
+ if (listed.count(*i))
+ cerr << *i << " repeated" << std::endl;
+ listed.insert(*i);
+ }
+ if (objects.size() < 50) {
+ ASSERT_TRUE(next.is_max());
+ break;
+ }
+ objects.clear();
+ current = next;
+ }
+ cerr << "listed.size() is " << listed.size() << std::endl;
+ ASSERT_TRUE(listed.size() == created.size());
+ for (set<ghobject_t>::iterator i = listed.begin();
+ i != listed.end();
+ ++i) {
+ ASSERT_TRUE(created.count(*i));
+ }
+
+ for (set<ghobject_t>::iterator i = created.begin();
+ i != created.end();
+ ++i) {
+ ObjectStore::Transaction t;
+ t.remove(cid, *i);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+}
+
+TEST_P(StoreTest, HashCollisionSorting) {
+ bool disable_legacy = (string(GetParam()) == "bluestore");
+
+ char buf121664318_1[] = {18, -119, -121, -111, 0};
+ char buf121664318_2[] = {19, 127, -121, 32, 0};
+ char buf121664318_3[] = {19, -118, 15, 19, 0};
+ char buf121664318_4[] = {28, 27, -116, -113, 0};
+ char buf121664318_5[] = {28, 27, -115, -124, 0};
+
+ char buf121666222_1[] = {18, -119, -120, -111, 0};
+ char buf121666222_2[] = {19, 127, -120, 32, 0};
+ char buf121666222_3[] = {19, -118, 15, 30, 0};
+ char buf121666222_4[] = {29, 17, -126, -113, 0};
+ char buf121666222_5[] = {29, 17, -125, -124, 0};
+
+ std::map<uint32_t, std::vector<std::string>> object_names = {
+ {121664318, {{buf121664318_1},
+ {buf121664318_2},
+ {buf121664318_3},
+ {buf121664318_4},
+ {buf121664318_5}}},
+ {121666222, {{buf121666222_1},
+ {buf121666222_2},
+ {buf121666222_3},
+ {buf121666222_4},
+ {buf121666222_5}}}};
+
+ int64_t poolid = 111;
+ coll_t cid = coll_t(spg_t(pg_t(0, poolid), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ int r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ std::set<ghobject_t> created;
+ for (auto &[hash, names] : object_names) {
+ for (auto &name : names) {
+ ghobject_t hoid(hobject_t(sobject_t(name, CEPH_NOSNAP),
+ string(),
+ hash,
+ poolid,
+ string()));
+ ASSERT_EQ(hash, hoid.hobj.get_hash());
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ int r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ created.insert(hoid);
+ }
+ }
+
+ vector<ghobject_t> objects;
+ int r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(),
+ INT_MAX, &objects, 0, disable_legacy);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(created.size(), objects.size());
+ auto it = objects.begin();
+ for (auto &hoid : created) {
+ ASSERT_EQ(hoid, *it);
+ it++;
+ }
+
+ for (auto i = created.begin(); i != created.end(); i++) {
+ auto j = i;
+ for (j++; j != created.end(); j++) {
+ std::set<ghobject_t> created_sub(i, j);
+ objects.clear();
+ ghobject_t next;
+ r = collection_list(store, ch, *i, ghobject_t::get_max(),
+ created_sub.size(), &objects, &next, disable_legacy);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(created_sub.size(), objects.size());
+ it = objects.begin();
+ for (auto &hoid : created_sub) {
+ ASSERT_EQ(hoid, *it);
+ it++;
+ }
+ if (j == created.end()) {
+ ASSERT_TRUE(next.is_max());
+ } else {
+ ASSERT_EQ(*j, next);
+ }
+ }
+ }
+
+ for (auto i = created.begin(); i != created.end(); i++) {
+ auto j = i;
+ for (j++; j != created.end(); j++) {
+ std::set<ghobject_t> created_sub(i, j);
+ objects.clear();
+ ghobject_t next;
+ r = collection_list(store, ch, *i, *j, INT_MAX, &objects, &next,
+ disable_legacy);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(created_sub.size(), objects.size());
+ it = objects.begin();
+ for (auto &hoid : created_sub) {
+ ASSERT_EQ(hoid, *it);
+ it++;
+ }
+ if (j == created.end()) {
+ ASSERT_TRUE(next.is_max());
+ } else {
+ ASSERT_EQ(*j, next);
+ }
+ }
+ }
+}
+
+TEST_P(StoreTest, ScrubTest) {
+ int64_t poolid = 111;
+ coll_t cid(spg_t(pg_t(0, poolid),shard_id_t(1)));
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ string base = "aaaaa";
+ set<ghobject_t> created;
+ for (int i = 0; i < 1000; ++i) {
+ char buf[100];
+ sprintf(buf, "%d", i);
+ if (!(i % 5)) {
+ cerr << "Object " << i << std::endl;
+ }
+ ghobject_t hoid(hobject_t(string(buf) + base, string(), CEPH_NOSNAP, i,
+ poolid, ""),
+ ghobject_t::NO_GEN, shard_id_t(1));
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ created.insert(hoid);
+ }
+
+ // Add same hobject_t but different generation
+ {
+ ghobject_t hoid1(hobject_t("same-object", string(), CEPH_NOSNAP, 0, poolid, ""),
+ ghobject_t::NO_GEN, shard_id_t(1));
+ ghobject_t hoid2(hobject_t("same-object", string(), CEPH_NOSNAP, 0, poolid, ""), (gen_t)1, shard_id_t(1));
+ ghobject_t hoid3(hobject_t("same-object", string(), CEPH_NOSNAP, 0, poolid, ""), (gen_t)2, shard_id_t(1));
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid1);
+ t.touch(cid, hoid2);
+ t.touch(cid, hoid3);
+ r = queue_transaction(store, ch, std::move(t));
+ created.insert(hoid1);
+ created.insert(hoid2);
+ created.insert(hoid3);
+ ASSERT_EQ(r, 0);
+ }
+
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, 0);
+ ASSERT_EQ(r, 0);
+ set<ghobject_t> listed(objects.begin(), objects.end());
+ cerr << "listed.size() is " << listed.size() << " and created.size() is " << created.size() << std::endl;
+ ASSERT_TRUE(listed.size() == created.size());
+ objects.clear();
+ listed.clear();
+ ghobject_t current, next;
+ while (1) {
+ r = collection_list(store, ch, current, ghobject_t::get_max(), 60, &objects,
+ &next);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(sorted(objects));
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end(); ++i) {
+ if (listed.count(*i))
+ cerr << *i << " repeated" << std::endl;
+ listed.insert(*i);
+ }
+ if (objects.size() < 50) {
+ ASSERT_TRUE(next.is_max());
+ break;
+ }
+ objects.clear();
+ current = next.get_boundary();
+ }
+ cerr << "listed.size() is " << listed.size() << std::endl;
+ ASSERT_TRUE(listed.size() == created.size());
+ for (set<ghobject_t>::iterator i = listed.begin();
+ i != listed.end();
+ ++i) {
+ ASSERT_TRUE(created.count(*i));
+ }
+
+ for (set<ghobject_t>::iterator i = created.begin();
+ i != created.end();
+ ++i) {
+ ObjectStore::Transaction t;
+ t.remove(cid, *i);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+}
+
+
+TEST_P(StoreTest, OMapTest) {
+ coll_t cid;
+ ghobject_t hoid(hobject_t("tesomap", "", CEPH_NOSNAP, 0, 0, ""));
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ map<string, bufferlist> attrs;
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.omap_clear(cid, hoid);
+ map<string, bufferlist> start_set;
+ t.omap_setkeys(cid, hoid, start_set);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ for (int i = 0; i < 100; i++) {
+ if (!(i%5)) {
+ std::cout << "On iteration " << i << std::endl;
+ }
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ map<string, bufferlist> cur_attrs;
+ r = store->omap_get(ch, hoid, &bl, &cur_attrs);
+ ASSERT_EQ(r, 0);
+ for (map<string, bufferlist>::iterator j = attrs.begin();
+ j != attrs.end();
+ ++j) {
+ bool correct = cur_attrs.count(j->first) && string(cur_attrs[j->first].c_str()) == string(j->second.c_str());
+ if (!correct) {
+ std::cout << j->first << " is present in cur_attrs " << cur_attrs.count(j->first) << " times " << std::endl;
+ if (cur_attrs.count(j->first) > 0) {
+ std::cout << j->second.c_str() << " : " << cur_attrs[j->first].c_str() << std::endl;
+ }
+ }
+ ASSERT_EQ(correct, true);
+ }
+ ASSERT_EQ(attrs.size(), cur_attrs.size());
+
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%d", i);
+ bl.clear();
+ bufferptr bp(buf, strlen(buf) + 1);
+ bl.append(bp);
+ map<string, bufferlist> to_add;
+ to_add.insert(pair<string, bufferlist>("key-" + string(buf), bl));
+ attrs.insert(pair<string, bufferlist>("key-" + string(buf), bl));
+ t.omap_setkeys(cid, hoid, to_add);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ int i = 0;
+ while (attrs.size()) {
+ if (!(i%5)) {
+ std::cout << "removal: On iteration " << i << std::endl;
+ }
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ map<string, bufferlist> cur_attrs;
+ r = store->omap_get(ch, hoid, &bl, &cur_attrs);
+ ASSERT_EQ(r, 0);
+ for (map<string, bufferlist>::iterator j = attrs.begin();
+ j != attrs.end();
+ ++j) {
+ bool correct = cur_attrs.count(j->first) && string(cur_attrs[j->first].c_str()) == string(j->second.c_str());
+ if (!correct) {
+ std::cout << j->first << " is present in cur_attrs " << cur_attrs.count(j->first) << " times " << std::endl;
+ if (cur_attrs.count(j->first) > 0) {
+ std::cout << j->second.c_str() << " : " << cur_attrs[j->first].c_str() << std::endl;
+ }
+ }
+ ASSERT_EQ(correct, true);
+ }
+
+ string to_remove = attrs.begin()->first;
+ t.omap_rmkey(cid, hoid, to_remove);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ attrs.erase(to_remove);
+
+ ++i;
+ }
+
+ {
+ bufferlist bl1;
+ bl1.append("omap_header");
+ ObjectStore::Transaction t;
+ t.omap_setheader(cid, hoid, bl1);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ t = ObjectStore::Transaction();
+
+ bufferlist bl2;
+ bl2.append("value");
+ map<string, bufferlist> to_add;
+ to_add.insert(pair<string, bufferlist>("key", bl2));
+ t.omap_setkeys(cid, hoid, to_add);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bufferlist bl3;
+ map<string, bufferlist> cur_attrs;
+ r = store->omap_get(ch, hoid, &bl3, &cur_attrs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(cur_attrs.size(), size_t(1));
+ ASSERT_TRUE(bl_eq(bl1, bl3));
+
+ set<string> keys;
+ r = store->omap_get_keys(ch, hoid, &keys);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(keys.size(), size_t(1));
+ }
+
+ // test omap_clear, omap_rmkey_range
+ {
+ {
+ map<string,bufferlist> to_set;
+ for (int n=0; n<10; ++n) {
+ to_set[stringify(n)].append("foo");
+ }
+ bufferlist h;
+ h.append("header");
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.touch(cid, hoid);
+ t.omap_setheader(cid, hoid, h);
+ t.omap_setkeys(cid, hoid, to_set);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.omap_rmkeyrange(cid, hoid, "3", "7");
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist hdr;
+ map<string,bufferlist> m;
+ store->omap_get(ch, hoid, &hdr, &m);
+ ASSERT_EQ(6u, hdr.length());
+ ASSERT_TRUE(m.count("2"));
+ ASSERT_TRUE(!m.count("3"));
+ ASSERT_TRUE(!m.count("6"));
+ ASSERT_TRUE(m.count("7"));
+ ASSERT_TRUE(m.count("8"));
+ //cout << m << std::endl;
+ ASSERT_EQ(6u, m.size());
+ }
+ {
+ ObjectStore::Transaction t;
+ t.omap_clear(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist hdr;
+ map<string,bufferlist> m;
+ store->omap_get(ch, hoid, &hdr, &m);
+ ASSERT_EQ(0u, hdr.length());
+ ASSERT_EQ(0u, m.size());
+ }
+ }
+
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+}
+
+TEST_P(StoreTest, OMapIterator) {
+ coll_t cid;
+ ghobject_t hoid(hobject_t("tesomap", "", CEPH_NOSNAP, 0, 0, ""));
+ int count = 0;
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ map<string, bufferlist> attrs;
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.omap_clear(cid, hoid);
+ map<string, bufferlist> start_set;
+ t.omap_setkeys(cid, hoid, start_set);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ObjectMap::ObjectMapIterator iter;
+ bool correct;
+ //basic iteration
+ for (int i = 0; i < 100; i++) {
+ if (!(i%5)) {
+ std::cout << "On iteration " << i << std::endl;
+ }
+ bufferlist bl;
+
+ // FileStore may deadlock two active iterators over the same data
+ iter = ObjectMap::ObjectMapIterator();
+
+ iter = store->get_omap_iterator(ch, hoid);
+ for (iter->seek_to_first(), count=0; iter->valid(); iter->next(), count++) {
+ string key = iter->key();
+ bufferlist value = iter->value();
+ correct = attrs.count(key) && (string(value.c_str()) == string(attrs[key].c_str()));
+ if (!correct) {
+ if (attrs.count(key) > 0) {
+ std::cout << "key " << key << "in omap , " << value.c_str() << " : " << attrs[key].c_str() << std::endl;
+ }
+ else
+ std::cout << "key " << key << "should not exists in omap" << std::endl;
+ }
+ ASSERT_EQ(correct, true);
+ }
+ ASSERT_EQ((int)attrs.size(), count);
+
+ // FileStore may deadlock an active iterator vs queue_transaction
+ iter = ObjectMap::ObjectMapIterator();
+
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%d", i);
+ bl.clear();
+ bufferptr bp(buf, strlen(buf) + 1);
+ bl.append(bp);
+ map<string, bufferlist> to_add;
+ to_add.insert(pair<string, bufferlist>("key-" + string(buf), bl));
+ attrs.insert(pair<string, bufferlist>("key-" + string(buf), bl));
+ ObjectStore::Transaction t;
+ t.omap_setkeys(cid, hoid, to_add);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ iter = store->get_omap_iterator(ch, hoid);
+ //lower bound
+ string bound_key = "key-5";
+ iter->lower_bound(bound_key);
+ correct = bound_key <= iter->key();
+ if (!correct) {
+ std::cout << "lower bound, bound key is " << bound_key << " < iter key is " << iter->key() << std::endl;
+ }
+ ASSERT_EQ(correct, true);
+ //upper bound
+ iter->upper_bound(bound_key);
+ correct = iter->key() > bound_key;
+ if (!correct) {
+ std::cout << "upper bound, bound key is " << bound_key << " >= iter key is " << iter->key() << std::endl;
+ }
+ ASSERT_EQ(correct, true);
+
+ // FileStore may deadlock an active iterator vs queue_transaction
+ iter = ObjectMap::ObjectMapIterator();
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, XattrTest) {
+ coll_t cid;
+ ghobject_t hoid(hobject_t("tesomap", "", CEPH_NOSNAP, 0, 0, ""));
+ bufferlist big;
+ for (unsigned i = 0; i < 10000; ++i) {
+ big.append('\0');
+ }
+ bufferlist small;
+ for (unsigned i = 0; i < 10; ++i) {
+ small.append('\0');
+ }
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ map<string, bufferlist> attrs;
+ {
+ ObjectStore::Transaction t;
+ t.setattr(cid, hoid, "attr1", small);
+ attrs["attr1"] = small;
+ t.setattr(cid, hoid, "attr2", big);
+ attrs["attr2"] = big;
+ t.setattr(cid, hoid, "attr3", small);
+ attrs["attr3"] = small;
+ t.setattr(cid, hoid, "attr1", small);
+ attrs["attr1"] = small;
+ t.setattr(cid, hoid, "attr4", big);
+ attrs["attr4"] = big;
+ t.setattr(cid, hoid, "attr3", big);
+ attrs["attr3"] = big;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ map<string, bufferptr, less<>> aset;
+ store->getattrs(ch, hoid, aset);
+ ASSERT_EQ(aset.size(), attrs.size());
+ for (map<string, bufferptr>::iterator i = aset.begin();
+ i != aset.end();
+ ++i) {
+ bufferlist bl;
+ bl.push_back(i->second);
+ ASSERT_TRUE(attrs[i->first] == bl);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.rmattr(cid, hoid, "attr2");
+ attrs.erase("attr2");
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ aset.clear();
+ store->getattrs(ch, hoid, aset);
+ ASSERT_EQ(aset.size(), attrs.size());
+ for (map<string, bufferptr>::iterator i = aset.begin();
+ i != aset.end();
+ ++i) {
+ bufferlist bl;
+ bl.push_back(i->second);
+ ASSERT_TRUE(attrs[i->first] == bl);
+ }
+
+ bufferptr bp;
+ r = store->getattr(ch, hoid, "attr2", bp);
+ ASSERT_EQ(r, -ENODATA);
+
+ r = store->getattr(ch, hoid, "attr3", bp);
+ ASSERT_EQ(r, 0);
+ bufferlist bl2;
+ bl2.push_back(bp);
+ ASSERT_TRUE(bl2 == attrs["attr3"]);
+
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+}
+
+void colsplittest(
+ ObjectStore *store,
+ unsigned num_objects,
+ unsigned common_suffix_size,
+ bool clones
+ ) {
+ coll_t cid(spg_t(pg_t(0,52),shard_id_t::NO_SHARD));
+ coll_t tid(spg_t(pg_t(1<<common_suffix_size,52),shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+ auto tch = store->create_new_collection(tid);
+ int r = 0;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, common_suffix_size);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist small;
+ small.append("small");
+ {
+ ObjectStore::Transaction t;
+ for (uint32_t i = 0; i < (2 - (int)clones)*num_objects; ++i) {
+ stringstream objname;
+ objname << "obj" << i;
+ ghobject_t a(hobject_t(
+ objname.str(),
+ "",
+ CEPH_NOSNAP,
+ i<<common_suffix_size,
+ 52, ""));
+ t.write(cid, a, 0, small.length(), small,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ if (clones) {
+ objname << "-clone";
+ ghobject_t b(hobject_t(
+ objname.str(),
+ "",
+ CEPH_NOSNAP,
+ i<<common_suffix_size,
+ 52, ""));
+ t.clone(cid, a, b);
+ }
+ if (i % 100) {
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ t = ObjectStore::Transaction();
+ }
+ }
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(tid, common_suffix_size + 1);
+ t.split_collection(cid, common_suffix_size+1, 1<<common_suffix_size, tid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ch->flush();
+
+ // check
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, 0);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(objects.size(), num_objects);
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end();
+ ++i) {
+ ASSERT_EQ(!!(i->hobj.get_hash() & (1<<common_suffix_size)), 0u);
+ }
+
+ objects.clear();
+ r = collection_list(store, tch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, 0);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(objects.size(), num_objects);
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end();
+ ++i) {
+ ASSERT_EQ(!(i->hobj.get_hash() & (1<<common_suffix_size)), 0u);
+ }
+
+ // merge them again!
+ {
+ ObjectStore::Transaction t;
+ t.merge_collection(tid, cid, common_suffix_size);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // check and clean up
+ ObjectStore::Transaction t;
+ {
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, 0);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(objects.size(), num_objects * 2); // both halves
+ unsigned size = 0;
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end();
+ ++i) {
+ t.remove(cid, *i);
+ if (++size > 100) {
+ size = 0;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ t = ObjectStore::Transaction();
+ }
+ }
+ }
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ ch->flush();
+ ASSERT_TRUE(!store->collection_exists(tid));
+}
+
+TEST_P(StoreTest, ColSplitTest0) {
+ colsplittest(store.get(), 10, 5, false);
+}
+TEST_P(StoreTest, ColSplitTest1) {
+ colsplittest(store.get(), 10000, 11, false);
+}
+TEST_P(StoreTest, ColSplitTest1Clones) {
+ colsplittest(store.get(), 10000, 11, true);
+}
+TEST_P(StoreTest, ColSplitTest2) {
+ colsplittest(store.get(), 100, 7, false);
+}
+TEST_P(StoreTest, ColSplitTest2Clones) {
+ colsplittest(store.get(), 100, 7, true);
+}
+
+#if 0
+TEST_P(StoreTest, ColSplitTest3) {
+ colsplittest(store.get(), 100000, 25);
+}
+#endif
+
+void test_merge_skewed(ObjectStore *store,
+ unsigned base, unsigned bits,
+ unsigned anum, unsigned bnum)
+{
+ cout << __func__ << " 0x" << std::hex << base << std::dec
+ << " bits " << bits
+ << " anum " << anum << " bnum " << bnum << std::endl;
+ /*
+ make merge source pgs have radically different # of objects in them,
+ which should trigger different splitting in filestore, and verify that
+ post-merge all objects are accessible.
+ */
+ int r;
+ coll_t a(spg_t(pg_t(base, 0), shard_id_t::NO_SHARD));
+ coll_t b(spg_t(pg_t(base | (1<<bits), 0), shard_id_t::NO_SHARD));
+
+ auto cha = store->create_new_collection(a);
+ auto chb = store->create_new_collection(b);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(a, bits + 1);
+ r = queue_transaction(store, cha, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(b, bits + 1);
+ r = queue_transaction(store, chb, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ bufferlist small;
+ small.append("small");
+ string suffix = "ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooaaaaaaaaaa";
+ set<ghobject_t> aobjects, bobjects;
+ {
+ // fill a
+ ObjectStore::Transaction t;
+ for (unsigned i = 0; i < 1000; ++i) {
+ string objname = "a" + stringify(i) + suffix;
+ ghobject_t o(hobject_t(
+ objname,
+ "",
+ CEPH_NOSNAP,
+ i<<(bits+1) | base,
+ 52, ""));
+ aobjects.insert(o);
+ t.write(a, o, 0, small.length(), small, 0);
+ if (i % 100) {
+ r = queue_transaction(store, cha, std::move(t));
+ ASSERT_EQ(r, 0);
+ t = ObjectStore::Transaction();
+ }
+ }
+ r = queue_transaction(store, cha, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // fill b
+ ObjectStore::Transaction t;
+ for (unsigned i = 0; i < 10; ++i) {
+ string objname = "b" + stringify(i) + suffix;
+ ghobject_t o(hobject_t(
+ objname,
+ "",
+ CEPH_NOSNAP,
+ (i<<(base+1)) | base | (1<<bits),
+ 52, ""));
+ bobjects.insert(o);
+ t.write(b, o, 0, small.length(), small, 0);
+ if (i % 100) {
+ r = queue_transaction(store, chb, std::move(t));
+ ASSERT_EQ(r, 0);
+ t = ObjectStore::Transaction();
+ }
+ }
+ r = queue_transaction(store, chb, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // merge b->a
+ {
+ ObjectStore::Transaction t;
+ t.merge_collection(b, a, bits);
+ r = queue_transaction(store, cha, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // verify
+ {
+ vector<ghobject_t> got;
+ collection_list(store, cha, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &got, 0);
+ set<ghobject_t> gotset;
+ for (auto& o : got) {
+ ASSERT_TRUE(aobjects.count(o) || bobjects.count(o));
+ gotset.insert(o);
+ }
+ // check both listing and stat-ability (different code paths!)
+ struct stat st;
+ for (auto& o : aobjects) {
+ ASSERT_TRUE(gotset.count(o));
+ int r = store->stat(cha, o, &st, false);
+ ASSERT_EQ(r, 0);
+ }
+ for (auto& o : bobjects) {
+ ASSERT_TRUE(gotset.count(o));
+ int r = store->stat(cha, o, &st, false);
+ ASSERT_EQ(r, 0);
+ }
+ }
+
+ // clean up
+ {
+ ObjectStore::Transaction t;
+ for (auto &o : aobjects) {
+ t.remove(a, o);
+ }
+ r = queue_transaction(store, cha, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ for (auto &o : bobjects) {
+ t.remove(a, o);
+ }
+ t.remove_collection(a);
+ r = queue_transaction(store, cha, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, MergeSkewed) {
+ if (string(GetParam()) != "filestore")
+ return;
+
+ // this is sufficient to exercise merges with different hashing levels
+ test_merge_skewed(store.get(), 0xf, 4, 10, 10000);
+ test_merge_skewed(store.get(), 0xf, 4, 10000, 10);
+
+ /*
+ // this covers a zillion variations that all boil down to the same thing
+ for (unsigned base = 3; base < 0x1000; base *= 5) {
+ unsigned bits;
+ unsigned t = base;
+ for (bits = 0; t; t >>= 1) {
+ ++bits;
+ }
+ for (unsigned b = bits; b < bits + 10; b += 3) {
+ for (auto anum : { 10, 1000, 10000 }) {
+ for (auto bnum : { 10, 1000, 10000 }) {
+ if (anum == bnum) {
+ continue;
+ }
+ test_merge_skewed(store.get(), base, b, anum, bnum);
+ }
+ }
+ }
+ }
+ */
+}
+
+
+/**
+ * This test tests adding two different groups
+ * of objects, each with 1 common prefix and 1
+ * different prefix. We then remove half
+ * in order to verify that the merging correctly
+ * stops at the common prefix subdir. See bug
+ * #5273 */
+TEST_P(StoreTest, TwoHash) {
+ coll_t cid;
+ int r;
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ std::cout << "Making objects" << std::endl;
+ for (int i = 0; i < 360; ++i) {
+ ObjectStore::Transaction t;
+ ghobject_t o;
+ o.hobj.pool = -1;
+ if (i < 8) {
+ o.hobj.set_hash((i << 16) | 0xA1);
+ t.touch(cid, o);
+ }
+ o.hobj.set_hash((i << 16) | 0xB1);
+ t.touch(cid, o);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ std::cout << "Removing half" << std::endl;
+ for (int i = 1; i < 8; ++i) {
+ ObjectStore::Transaction t;
+ ghobject_t o;
+ o.hobj.pool = -1;
+ o.hobj.set_hash((i << 16) | 0xA1);
+ t.remove(cid, o);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ std::cout << "Checking" << std::endl;
+ for (int i = 1; i < 8; ++i) {
+ ObjectStore::Transaction t;
+ ghobject_t o;
+ o.hobj.set_hash((i << 16) | 0xA1);
+ o.hobj.pool = -1;
+ bool exists = store->exists(ch, o);
+ ASSERT_EQ(exists, false);
+ }
+ {
+ ghobject_t o;
+ o.hobj.set_hash(0xA1);
+ o.hobj.pool = -1;
+ bool exists = store->exists(ch, o);
+ ASSERT_EQ(exists, true);
+ }
+ std::cout << "Cleanup" << std::endl;
+ for (int i = 0; i < 360; ++i) {
+ ObjectStore::Transaction t;
+ ghobject_t o;
+ o.hobj.set_hash((i << 16) | 0xA1);
+ o.hobj.pool = -1;
+ t.remove(cid, o);
+ o.hobj.set_hash((i << 16) | 0xB1);
+ t.remove(cid, o);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+}
+
+TEST_P(StoreTest, Rename) {
+ coll_t cid(spg_t(pg_t(0, 2122),shard_id_t::NO_SHARD));
+ ghobject_t srcoid(hobject_t("src_oid", "", CEPH_NOSNAP, 0, 0, ""));
+ ghobject_t dstoid(hobject_t("dest_oid", "", CEPH_NOSNAP, 0, 0, ""));
+ bufferlist a, b;
+ a.append("foo");
+ b.append("bar");
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.write(cid, srcoid, 0, a.length(), a);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_TRUE(store->exists(ch, srcoid));
+ {
+ ObjectStore::Transaction t;
+ t.collection_move_rename(cid, srcoid, cid, dstoid);
+ t.write(cid, srcoid, 0, b.length(), b);
+ t.setattr(cid, srcoid, "attr", b);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_TRUE(store->exists(ch, srcoid));
+ ASSERT_TRUE(store->exists(ch, dstoid));
+ {
+ bufferlist bl;
+ store->read(ch, srcoid, 0, 3, bl);
+ ASSERT_TRUE(bl_eq(b, bl));
+ store->read(ch, dstoid, 0, 3, bl);
+ ASSERT_TRUE(bl_eq(a, bl));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, dstoid);
+ t.collection_move_rename(cid, srcoid, cid, dstoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_TRUE(store->exists(ch, dstoid));
+ ASSERT_FALSE(store->exists(ch, srcoid));
+ {
+ bufferlist bl;
+ store->read(ch, dstoid, 0, 3, bl);
+ ASSERT_TRUE(bl_eq(b, bl));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, dstoid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, MoveRename) {
+ coll_t cid(spg_t(pg_t(0, 212),shard_id_t::NO_SHARD));
+ ghobject_t temp_oid(hobject_t("tmp_oid", "", CEPH_NOSNAP, 0, 0, ""));
+ ghobject_t oid(hobject_t("dest_oid", "", CEPH_NOSNAP, 0, 0, ""));
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, oid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_TRUE(store->exists(ch, oid));
+ bufferlist data, attr;
+ map<string, bufferlist> omap;
+ data.append("data payload");
+ attr.append("attr value");
+ omap["omap_key"].append("omap value");
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, temp_oid);
+ t.write(cid, temp_oid, 0, data.length(), data);
+ t.setattr(cid, temp_oid, "attr", attr);
+ t.omap_setkeys(cid, temp_oid, omap);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_TRUE(store->exists(ch, temp_oid));
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, oid);
+ t.collection_move_rename(cid, temp_oid, cid, oid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_TRUE(store->exists(ch, oid));
+ ASSERT_FALSE(store->exists(ch, temp_oid));
+ {
+ bufferlist newdata;
+ r = store->read(ch, oid, 0, 1000, newdata);
+ ASSERT_GE(r, 0);
+ ASSERT_TRUE(bl_eq(data, newdata));
+ bufferlist newattr;
+ r = store->getattr(ch, oid, "attr", newattr);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(bl_eq(attr, newattr));
+ set<string> keys;
+ keys.insert("omap_key");
+ map<string, bufferlist> newomap;
+ r = store->omap_get_values(ch, oid, keys, &newomap);
+ ASSERT_GE(r, 0);
+ ASSERT_EQ(1u, newomap.size());
+ ASSERT_TRUE(newomap.count("omap_key"));
+ ASSERT_TRUE(bl_eq(omap["omap_key"], newomap["omap_key"]));
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, oid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, BigRGWObjectName) {
+ coll_t cid(spg_t(pg_t(0,12),shard_id_t::NO_SHARD));
+ ghobject_t oid(
+ hobject_t(
+ "default.4106.50_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "",
+ CEPH_NOSNAP,
+ 0x81920472,
+ 12,
+ ""),
+ 15,
+ shard_id_t::NO_SHARD);
+ ghobject_t oid2(oid);
+ oid2.generation = 17;
+ ghobject_t oidhead(oid);
+ oidhead.generation = ghobject_t::NO_GEN;
+
+ auto ch = store->create_new_collection(cid);
+
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, oidhead);
+ t.collection_move_rename(cid, oidhead, cid, oid);
+ t.touch(cid, oidhead);
+ t.collection_move_rename(cid, oidhead, cid, oid2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, oid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ vector<ghobject_t> objects;
+ r = collection_list(store, ch, ghobject_t(), ghobject_t::get_max(), INT_MAX,
+ &objects, 0);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(objects.size(), 1u);
+ ASSERT_EQ(objects[0], oid2);
+ }
+
+ ASSERT_FALSE(store->exists(ch, oid));
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, oid2);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ }
+}
+
+TEST_P(StoreTest, SetAllocHint) {
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_hint", "", CEPH_NOSNAP, 0, 0, ""));
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.set_alloc_hint(cid, hoid, 4*1024*1024, 1024*4, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.set_alloc_hint(cid, hoid, 4*1024*1024, 1024*4, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTest, TryMoveRename) {
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_hint", "", CEPH_NOSNAP, 0, -1, ""));
+ ghobject_t hoid2(hobject_t("test_hint2", "", CEPH_NOSNAP, 0, -1, ""));
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.try_rename(cid, hoid, hoid2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.try_rename(cid, hoid, hoid2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ struct stat st;
+ ASSERT_EQ(store->stat(ch, hoid, &st), -ENOENT);
+ ASSERT_EQ(store->stat(ch, hoid2, &st), 0);
+}
+
+#if defined(WITH_BLUESTORE)
+TEST_P(StoreTest, BluestoreOnOffCSumTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ SetVal(g_conf(), "bluestore_csum_type", "crc32c");
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ {
+ auto ch = store->open_collection(cid);
+ ASSERT_FALSE(ch);
+ }
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ //write with csum enabled followed by read with csum disabled
+ size_t block_size = 64*1024;
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ bl.append(std::string(block_size, 'a'));
+ orig = bl;
+ t.remove(cid, hoid);
+ t.set_alloc_hint(cid, hoid, 4*1024*1024, 1024*8, 0);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "Remove then create" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ SetVal(g_conf(), "bluestore_csum_type", "none");
+ g_conf().apply_changes(nullptr);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+
+ }
+ {
+ //write with csum disabled followed by read with csum enabled
+
+ size_t block_size = 64*1024;
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ bl.append(std::string(block_size, 'a'));
+ orig = bl;
+ t.remove(cid, hoid);
+ t.set_alloc_hint(cid, hoid, 4*1024*1024, 1024*8, 0);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "Remove then create" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ SetVal(g_conf(), "bluestore_csum_type", "crc32c");
+ g_conf().apply_changes(nullptr);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+ }
+ {
+ //'mixed' non-overlapping writes to the same blob
+
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ size_t block_size = 8000;
+ bl.append(std::string(block_size, 'a'));
+ orig = bl;
+ t.remove(cid, hoid);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "Remove then create" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ SetVal(g_conf(), "bluestore_csum_type", "none");
+ g_conf().apply_changes(nullptr);
+
+ ObjectStore::Transaction t2;
+ t2.write(cid, hoid, block_size*2, bl.length(), bl);
+ cerr << "Append 'unprotected'" << std::endl;
+ r = queue_transaction(store, ch, std::move(t2));
+ ASSERT_EQ(r, 0);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+ in.clear();
+ r = store->read(ch, hoid, block_size*2, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+
+ SetVal(g_conf(), "bluestore_csum_type", "crc32c");
+ g_conf().apply_changes(nullptr);
+ in.clear();
+ r = store->read(ch, hoid, 0, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+ in.clear();
+ r = store->read(ch, hoid, block_size*2, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+ }
+ {
+ //partially blob overwrite under a different csum enablement mode
+
+ ObjectStore::Transaction t;
+ bufferlist bl, orig, orig2;
+ size_t block_size0 = 0x10000;
+ size_t block_size = 9000;
+ size_t block_size2 = 5000;
+ bl.append(std::string(block_size0, 'a'));
+ t.remove(cid, hoid);
+ t.set_alloc_hint(cid, hoid, 4*1024*1024, 1024*8, 0);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "Remove then create" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ SetVal(g_conf(), "bluestore_csum_type", "none");
+ g_conf().apply_changes(nullptr);
+
+ ObjectStore::Transaction t2;
+ bl.clear();
+ bl.append(std::string(block_size, 'b'));
+ t2.write(cid, hoid, 0, bl.length(), bl);
+ t2.write(cid, hoid, block_size0, bl.length(), bl);
+ cerr << "Overwrite with unprotected data" << std::endl;
+ r = queue_transaction(store, ch, std::move(t2));
+ ASSERT_EQ(r, 0);
+
+ orig = bl;
+ orig2 = bl;
+ orig.append( std::string(block_size0 - block_size, 'a'));
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, block_size0, in);
+ ASSERT_EQ((int)block_size0, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+
+ r = store->read(ch, hoid, block_size0, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig2, in));
+
+ SetVal(g_conf(), "bluestore_csum_type", "crc32c");
+ g_conf().apply_changes(nullptr);
+
+ ObjectStore::Transaction t3;
+ bl.clear();
+ bl.append(std::string(block_size2, 'c'));
+ t3.write(cid, hoid, block_size0, bl.length(), bl);
+ cerr << "Overwrite with protected data" << std::endl;
+ r = queue_transaction(store, ch, std::move(t3));
+ ASSERT_EQ(r, 0);
+
+ in.clear();
+ orig = bl;
+ orig.append( std::string(block_size - block_size2, 'b'));
+ r = store->read(ch, hoid, block_size0, block_size, in);
+ ASSERT_EQ((int)block_size, r);
+ ASSERT_TRUE(bl_eq(orig, in));
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+#endif
+
+INSTANTIATE_TEST_SUITE_P(
+ ObjectStore,
+ StoreTest,
+ ::testing::Values(
+ "memstore",
+#if defined(WITH_BLUESTORE)
+ "bluestore",
+#endif
+ "kstore"));
+
+// Note: instantiate all stores to preserve store numbering order only
+INSTANTIATE_TEST_SUITE_P(
+ ObjectStore,
+ StoreTestSpecificAUSize,
+ ::testing::Values(
+ "memstore",
+#if defined(WITH_BLUESTORE)
+ "bluestore",
+#endif
+ "kstore"));
+
+// Note: instantiate all stores to preserve store numbering order only
+INSTANTIATE_TEST_SUITE_P(
+ ObjectStore,
+ StoreTestOmapUpgrade,
+ ::testing::Values(
+ "memstore",
+#if defined(WITH_BLUESTORE)
+ "bluestore",
+#endif
+ "kstore"));
+
+#if defined(WITH_BLUESTORE)
+INSTANTIATE_TEST_SUITE_P(
+ ObjectStore,
+ StoreTestDeferredSetup,
+ ::testing::Values(
+ "bluestore"));
+#endif
+
+
+struct deferred_test_t {
+ uint32_t bdev_block_size;
+ uint32_t min_alloc_size;
+ uint32_t max_blob_size;
+ uint32_t prefer_deferred_size;
+};
+
+void PrintTo(const deferred_test_t& t, ::std::ostream* os)
+{
+ *os << t.bdev_block_size << "/" << t.min_alloc_size << "/"
+ << t.max_blob_size << "/" << t.prefer_deferred_size;
+}
+
+class DeferredWriteTest : public StoreTestFixture,
+ public ::testing::WithParamInterface<deferred_test_t> {
+public:
+ DeferredWriteTest()
+ : StoreTestFixture("bluestore")
+ {}
+ void SetUp() override {
+ //do nothing
+ }
+protected:
+ void DeferredSetup() {
+ StoreTestFixture::SetUp();
+ }
+public:
+ std::vector<uint32_t> offsets = {0, 3000, 4096, 20000, 32768, 65000, 65536, 80000, 128 * 1024};
+ std::vector<uint32_t> lengths = {1, 1000, 4096, 12000, 32768, 30000, 80000, 128 * 1024};
+};
+
+TEST_P(DeferredWriteTest, NewData) {
+ const bool print = false;
+ deferred_test_t t = GetParam();
+ SetVal(g_conf(), "bdev_block_size", stringify(t.bdev_block_size).c_str());
+ SetVal(g_conf(), "bluestore_min_alloc_size", stringify(t.min_alloc_size).c_str());
+ SetVal(g_conf(), "bluestore_max_blob_size", stringify(t.max_blob_size).c_str());
+ SetVal(g_conf(), "bluestore_prefer_deferred_size", stringify(t.prefer_deferred_size).c_str());
+ g_conf().apply_changes(nullptr);
+ DeferredSetup();
+
+ int r;
+ coll_t cid;
+ const PerfCounters* logger = store->get_perf_counters();
+ ObjectStore::CollectionHandle ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ for (auto offset:offsets) {
+ for (auto length:lengths) {
+ std::string hname = fmt::format("test-{}-{}", offset, length);
+ ghobject_t hoid(hobject_t(hname, "", CEPH_NOSNAP, 0, -1, ""));
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ if (print)
+ std::cout << hname << std::endl;
+
+ auto w_new = logger->get(l_bluestore_write_new);
+ auto w_big_deferred = logger->get(l_bluestore_write_big_deferred);
+ auto i_deferred_w = logger->get(l_bluestore_issued_deferred_writes);
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(length, 'x'));
+ t.write(cid, hoid, offset, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ uint32_t first_db = offset / t.bdev_block_size;
+ uint32_t last_db = (offset + length - 1) / t.bdev_block_size;
+
+ uint32_t write_size = (last_db - first_db + 1) * t.bdev_block_size;
+ if (write_size < t.prefer_deferred_size) {
+ // expect no direct writes
+ ASSERT_EQ(w_new , logger->get(l_bluestore_write_new));
+ } else {
+ // expect no deferred
+ ASSERT_EQ(w_big_deferred , logger->get(l_bluestore_write_big_deferred));
+ ASSERT_EQ(i_deferred_w , logger->get(l_bluestore_issued_deferred_writes));
+ }
+ }
+ }
+ }
+}
+
+#if defined(WITH_BLUESTORE)
+INSTANTIATE_TEST_SUITE_P(
+ BlueStore,
+ DeferredWriteTest,
+ ::testing::Values(
+ // bdev alloc blob deferred
+ deferred_test_t{4 * 1024, 4 * 1024, 16 * 1024, 32 * 1024},
+ deferred_test_t{4 * 1024, 16 * 1024, 64 * 1024, 64 * 1024},
+ deferred_test_t{4 * 1024, 64 * 1024, 64 * 1024, 4 * 1024},
+ deferred_test_t{4 * 1024, 4 * 1024, 64 * 1024, 0 * 1024},
+ deferred_test_t{4 * 1024, 16 * 1024, 32 * 1024, 32 * 1024},
+ deferred_test_t{4 * 1024, 16 * 1024, 64 * 1024, 128 * 1024}
+ ));
+#endif
+
+void doMany4KWritesTest(ObjectStore* store,
+ unsigned max_objects,
+ unsigned max_ops,
+ unsigned max_object_size,
+ unsigned max_write_size,
+ unsigned write_alignment)
+{
+ MixedGenerator gen(555);
+ gen_type rng(time(NULL));
+ coll_t cid(spg_t(pg_t(0,555), shard_id_t::NO_SHARD));
+ store_statfs_t res_stat;
+
+ SyntheticWorkloadState test_obj(store,
+ &gen,
+ &rng,
+ cid,
+ max_object_size,
+ max_write_size,
+ write_alignment);
+ test_obj.init();
+ for (unsigned i = 0; i < max_objects; ++i) {
+ if (!(i % 500)) cerr << "seeding object " << i << std::endl;
+ test_obj.touch();
+ }
+ for (unsigned i = 0; i < max_ops; ++i) {
+ if (!(i % 200)) {
+ cerr << "Op " << i << std::endl;
+ test_obj.print_internal_state();
+ }
+ test_obj.write();
+ }
+ test_obj.wait_for_done();
+ test_obj.statfs(res_stat);
+ if (!(res_stat.data_stored <= max_object_size) ||
+ !(res_stat.allocated <= max_object_size)) {
+ // this will provide more insight on the mismatch and
+ // helps to avoid any races during stats collection
+ test_obj.fsck(false);
+ // retrieving stats once again and assert if still broken
+ test_obj.statfs(res_stat);
+ ASSERT_LE(res_stat.data_stored, max_object_size);
+ ASSERT_LE(res_stat.allocated, max_object_size);
+ }
+ test_obj.shutdown();
+}
+
+TEST_P(StoreTestSpecificAUSize, Many4KWritesTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred; assertions around res_stat.allocated don't apply"
+ << std::endl;
+ return;
+ }
+
+ StartDeferred(0x10000);
+
+ const unsigned max_object = 4*1024*1024;
+ doMany4KWritesTest(store.get(), 1, 1000, max_object, 4*1024, 0);
+}
+
+TEST_P(StoreTestSpecificAUSize, Many4KWritesNoCSumTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred; assertions around res_stat.allocated don't apply"
+ << std::endl;
+ return;
+ }
+ StartDeferred(0x10000);
+ SetVal(g_conf(), "bluestore_csum_type", "none");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ const unsigned max_object = 4*1024*1024;
+
+ doMany4KWritesTest(store.get(), 1, 1000, max_object, 4*1024, 0 );
+}
+
+TEST_P(StoreTestSpecificAUSize, TooManyBlobsTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred; assertions around res_stat.allocated don't apply"
+ << std::endl;
+ return;
+ }
+ StartDeferred(0x10000);
+ const unsigned max_object = 4*1024*1024;
+ doMany4KWritesTest(store.get(), 1, 1000, max_object, 4*1024, 0);
+}
+
+#if defined(WITH_BLUESTORE)
+void get_mempool_stats(uint64_t* total_bytes, uint64_t* total_items)
+{
+ uint64_t meta_allocated = mempool::bluestore_cache_meta::allocated_bytes();
+ uint64_t onode_allocated = mempool::bluestore_cache_onode::allocated_bytes();
+ uint64_t other_allocated = mempool::bluestore_cache_other::allocated_bytes();
+
+ uint64_t meta_items = mempool::bluestore_cache_meta::allocated_items();
+ uint64_t onode_items = mempool::bluestore_cache_onode::allocated_items();
+ uint64_t other_items = mempool::bluestore_cache_other::allocated_items();
+ cout << "meta(" << meta_allocated << "/" << meta_items
+ << ") onode(" << onode_allocated << "/" << onode_items
+ << ") other(" << other_allocated << "/" << other_items
+ << ")" << std::endl;
+ *total_bytes = meta_allocated + onode_allocated + other_allocated;
+ *total_items = onode_items;
+}
+
+TEST_P(StoreTestSpecificAUSize, OnodeSizeTracking) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+ SetVal(g_conf(), "bluestore_compression_mode", "none");
+ SetVal(g_conf(), "bluestore_csum_type", "none");
+ SetVal(g_conf(), "bluestore_cache_size_hdd", "400000000");
+ SetVal(g_conf(), "bluestore_cache_size_ssd", "400000000");
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_hint", "", CEPH_NOSNAP, 0, -1, ""));
+ size_t obj_size = 4 * 1024 * 1024;
+ uint64_t total_bytes_prev;
+ uint64_t total_bytes, total_bytes2;
+ uint64_t total_onodes;
+ get_mempool_stats(&total_bytes, &total_onodes);
+ total_bytes_prev = total_bytes;
+ // 5u for onode_cache_shards vector
+ ASSERT_EQ(total_onodes, 5u);
+ ASSERT_EQ(total_bytes, 40u);
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig, orig2;
+
+ bl.append(std::string(obj_size, 'a'));
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ get_mempool_stats(&total_bytes, &total_onodes);
+ ASSERT_GT(total_bytes - total_bytes_prev, 0u);
+ ASSERT_EQ(total_onodes, 6u);
+
+ {
+ ObjectStore::Transaction t;
+ t.truncate(cid, hoid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ for(size_t i = 0; i < 1; ++i) {
+ bufferlist bl;
+ bl.append(std::string(block_size * (i+1), 'a'));
+ for( size_t j = 0; j < obj_size; j+= bl.length()) {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, j, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ get_mempool_stats(&total_bytes2, &total_onodes);
+ ASSERT_NE(total_bytes2, 0u);
+ ASSERT_EQ(total_onodes, 6u);
+ }
+ {
+ cout <<" mempool dump:\n";
+ JSONFormatter f(true);
+ f.open_object_section("transaction");
+ mempool::dump(&f);
+ f.close_section();
+ f.flush(cout);
+ cout << std::endl;
+ }
+ {
+ bufferlist bl;
+ for (size_t i = 0; i < obj_size; i += 0x1000) {
+ store->read(ch, hoid, i, 0x1000, bl);
+ }
+ }
+ get_mempool_stats(&total_bytes, &total_onodes);
+ ASSERT_NE(total_bytes, 0u);
+ ASSERT_EQ(total_onodes, 6u);
+
+ {
+ cout <<" mempool dump:\n";
+ JSONFormatter f(true);
+ f.open_object_section("transaction");
+ mempool::dump(&f);
+ f.close_section();
+ f.flush(cout);
+ cout << std::endl;
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BlobReuseOnOverwrite) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+ SetVal(g_conf(), "bluestore_max_blob_size", "65536");
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_hint", "", CEPH_NOSNAP, 0, -1, ""));
+
+ const PerfCounters* logger = store->get_perf_counters();
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'a'));
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // overwrite at the beginning
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'b'));
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // append
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'c'));
+ t.write(cid, hoid, block_size * 2, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // append with a gap
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'd'));
+ t.write(cid, hoid, block_size * 5, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 2u);
+ }
+ {
+ // overwrite at end
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'e'));
+
+ // Currently we are unable to reuse blob when overwriting in a single step
+ t.write(cid, hoid, block_size * 6, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 2u);
+ }
+ {
+ // fill the gap
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'f'));
+
+ t.write(cid, hoid, block_size * 4, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // we need to wait some time for mempool
+ // thread to update stats to be able to check blob/extent numbers from
+ // perf counters.
+ sleep(1);
+
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+
+ bl.clear();
+ expected.clear();
+ r = store->read(ch, hoid, block_size, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+
+ bl.clear();
+ expected.clear();
+ r = store->read(ch, hoid, block_size * 2, block_size * 2, bl);
+ ASSERT_EQ(r, (int)block_size * 2);
+ expected.append(string(block_size * 2, 'c'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+
+ bl.clear();
+ expected.clear();
+ r = store->read(ch, hoid, block_size * 4, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'f'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+
+ bl.clear();
+ expected.clear();
+ r = store->read(ch, hoid, block_size * 5, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'd'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+
+ bl.clear();
+ expected.clear();
+ r = store->read(ch, hoid, block_size * 5, block_size * 3, bl);
+ ASSERT_EQ(r, (int)block_size * 3);
+ expected.append(string(block_size, 'd'));
+ expected.append(string(block_size * 2, 'e'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 1u);
+
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, ZeroBlockDetectionSmallAppend) {
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+ if (string(GetParam()) != "bluestore" || !cct->_conf->bluestore_zero_block_detection) {
+ GTEST_SKIP() << "not bluestore or bluestore_zero_block_detection=false, skipping";
+ }
+
+ size_t block_size = 65536;
+ StartDeferred(block_size);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+
+ const PerfCounters* logger = store->get_perf_counters();
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // [1] append zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append_zero(4096);
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_bytes), 4096u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped_bytes), 4096u);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(4096, r);
+ ASSERT_TRUE(in.is_zero());
+ }
+
+ {
+ // [2] append non-zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(4096, 'c'));
+
+ t.write(cid, hoid, 4096, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_bytes), 4096u*2);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped_bytes), 4096u);
+
+ bufferlist in, _exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(4096 * 2, r);
+ _exp.append_zero(4096);
+ _exp.append(bl);
+ ASSERT_TRUE(bl_eq(_exp, in));
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, ZeroBlockDetectionSmallOverwrite) {
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+ if (string(GetParam()) != "bluestore" || !cct->_conf->bluestore_zero_block_detection) {
+ GTEST_SKIP() << "not bluestore or bluestore_zero_block_detection=false, skipping";
+ }
+ if (smr) {
+ GTEST_SKIP() << "smr, skipping";
+ }
+
+ size_t block_size = 65536;
+ StartDeferred(block_size);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+
+ const PerfCounters* logger = store->get_perf_counters();
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ // {setting up the scenario} append non-zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(4096, 'c'));
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_bytes), 4096u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped_bytes), 0u);
+
+ bufferlist in, _exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(4096, r);
+ _exp.append(bl);
+ ASSERT_TRUE(bl_eq(_exp, in));
+ }
+
+ {
+ // [1] overwrite non-zeros with zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append_zero(4096);
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_bytes), 4096u*2);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped_bytes), 0u);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(4096, r);
+ ASSERT_TRUE(in.is_zero());
+ }
+
+ {
+ // [2] overwrite zeros with non-zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(4096, 'c'));
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_small), 3u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_bytes), 4096u*3);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_write_small_skipped_bytes), 0u);
+
+ bufferlist in, _exp;
+ r = store->read(ch, hoid, 0, 0x4000, in);
+ ASSERT_EQ(4096, r);
+ _exp.append(bl);
+ ASSERT_TRUE(bl_eq(_exp, in));
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, ZeroBlockDetectionBigAppend) {
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+ if (string(GetParam()) != "bluestore" || !cct->_conf->bluestore_zero_block_detection) {
+ GTEST_SKIP() << "not bluestore or bluestore_zero_block_detection=false, skipping";
+ }
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+
+ const PerfCounters* logger = store->get_perf_counters();
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ // [1] append zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append_zero(block_size * 2);
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), 4096u*2);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_bytes), 4096u*2);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, block_size * 8, in);
+ ASSERT_EQ(block_size * 2, r);
+ ASSERT_TRUE(in.is_zero());
+ }
+
+ {
+ // [2] append non-zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'c'));
+
+ t.write(cid, hoid, block_size * 2, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), 4096u*4);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_bytes), 4096u*2);
+
+ bufferlist in, _exp;
+ r = store->read(ch, hoid, 0, block_size * 8, in);
+ ASSERT_EQ(block_size * 4, r);
+ _exp.append_zero(block_size * 2);
+ _exp.append(bl);
+ ASSERT_TRUE(bl_eq(_exp, in));
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, ZeroBlockDetectionBigOverwrite) {
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+ if (string(GetParam()) != "bluestore" || !cct->_conf->bluestore_zero_block_detection) {
+ GTEST_SKIP() << "not bluestore or bluestore_zero_block_detection=false, skipping";
+ }
+ if (smr) {
+ GTEST_SKIP() << "smr, skipping";
+ }
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+
+ const PerfCounters* logger = store->get_perf_counters();
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ // {setting up the scenario} append non-zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'c'));
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), 4096u*2);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_blobs), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_bytes), 0u);
+
+ bufferlist in, _exp;
+ r = store->read(ch, hoid, 0, block_size * 8, in);
+ ASSERT_EQ(block_size * 2, r);
+ _exp.append(bl);
+ ASSERT_TRUE(bl_eq(_exp, in));
+ }
+
+ {
+ // [1] overwrite non-zeros with zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append_zero(block_size * 2);
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), 4096u*4);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_bytes), 4096u*2);
+
+ bufferlist in;
+ r = store->read(ch, hoid, 0, block_size * 8, in);
+ ASSERT_EQ(block_size * 2, r);
+ ASSERT_TRUE(in.is_zero());
+ }
+
+ {
+ // [2] overwrite zeros with non-zeros
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'c'));
+
+ t.write(cid, hoid, 0, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 3u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), 4096u*6);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_skipped_bytes), 4096u*2);
+
+ bufferlist in, _exp;
+ r = store->read(ch, hoid, 0, block_size * 8, in);
+ ASSERT_EQ(block_size * 2, r);
+ _exp.append(bl);
+ ASSERT_TRUE(bl_eq(_exp, in));
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, DeferredOnBigOverwrite) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred" << std::endl;
+ return;
+ }
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+ SetVal(g_conf(), "bluestore_max_blob_size", "131072");
+ SetVal(g_conf(), "bluestore_prefer_deferred_size", "65536");
+
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+ ghobject_t hoid2(hobject_t("test2", "", CEPH_NOSNAP, 0, -1, ""));
+
+ PerfCounters* logger = const_cast<PerfCounters*>(store->get_perf_counters());
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, bl2;
+
+ bl.append(std::string(block_size * 2, 'c'));
+ bl2.append(std::string(block_size * 3, 'd'));
+
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ t.set_alloc_hint(cid, hoid2, block_size * 4, block_size * 4,
+ CEPH_OSD_ALLOC_HINT_FLAG_SEQUENTIAL_READ);
+ t.write(cid, hoid2, 0, bl2.length(), bl2, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 0u);
+
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 5);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 5);
+ }
+
+ // overwrite at the beginning, 4K alignment
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'b'));
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 3u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 1u);
+
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'c'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+
+ // overwrite at the end, 4K alignment
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'g'));
+ t.write(cid, hoid, block_size, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 4u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 2u);
+
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'g'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+
+ // overwrite at 4K, 12K alignment
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'e'));
+ t.write(cid, hoid2, block_size , bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 5u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 3u);
+
+ // makes sure deferred has been submitted
+ // and do all the checks again
+ sleep(g_conf().get_val<double>("bluestore_max_defer_interval") + 2);
+
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 5u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 3u);
+
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'g'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid2, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'd'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid2, block_size, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'e'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid2, block_size * 2, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'd'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 5);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 5);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 2u);
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(block_size * 2, 'f'));
+
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 6u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 3u);
+
+ {
+ ObjectStore::Transaction t;
+ t.zero(cid, hoid, 0, 100);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, 100, bl);
+ ASSERT_EQ(r, (int)100);
+ expected.append(string(100, 0));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 100, block_size * 2 - 100, bl);
+ ASSERT_EQ(r, (int)block_size * 2 - 100);
+ expected.append(string(block_size * 2 - 100, 'f'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ sleep(2);
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 2 - 100);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 2);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 1u);
+
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(block_size, 'g'));
+
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 7u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 4u);
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'g'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size, block_size, bl);
+ ASSERT_EQ(r, (int)block_size);
+ expected.append(string(block_size, 'f'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 2);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 2);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 1u);
+
+ // check whether full overwrite bypass deferred
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(block_size * 2, 'h'));
+
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 8u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 4u);
+
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size * 2, bl);
+ ASSERT_EQ(r, (int)block_size * 2);
+ expected.append(string(block_size * 2, 'h'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 2);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 2);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(block_size * 32, 'a'));
+
+ // this will create two 128K aligned blobs
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ t.write(cid, hoid, bl.length(), bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 10u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 4u);
+
+ // check whether overwrite (less than prefer_deferred_size) partially overlapping two adjacent blobs goes
+ // deferred
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(block_size * 3, 'b'));
+
+ t.write(cid, hoid, 0x20000 - block_size, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 11u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 6u);
+
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, 0x20000 - block_size, bl);
+ ASSERT_EQ(r, 0x20000 - block_size);
+ expected.append(string(r, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ expected.clear();
+
+ r = store->read(ch, hoid, 0x20000 - block_size, block_size * 3, bl);
+ ASSERT_EQ(r, 3 * block_size);
+ expected.append(string(r, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ expected.clear();
+
+ r = store->read(ch, hoid, 0x20000 + 2 * block_size, block_size * 30, bl);
+ ASSERT_EQ(r, 30 * block_size);
+ expected.append(string(r, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ expected.clear();
+ }
+
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 64);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 64);
+ }
+
+ // check whether overwrite (larger than prefer_deferred_size) partially
+ // overlapping two adjacent blobs goes deferred
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(block_size * 30, 'c'));
+
+ t.write(cid, hoid, 0x10000 + block_size, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ sleep(2);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 12u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 8u);
+
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, 0x11000, bl);
+ ASSERT_EQ(r, 0x11000);
+ expected.append(string(r, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ expected.clear();
+
+ r = store->read(ch, hoid, 0x11000, block_size * 30, bl);
+ ASSERT_EQ(r, block_size * 30);
+ expected.append(string(r, 'c'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ expected.clear();
+
+ r = store->read(ch, hoid, block_size * 47, 0x10000 + block_size, bl);
+ ASSERT_EQ(r, 0x10000 + block_size);
+ expected.append(string(r, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ expected.clear();
+ }
+
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 64);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 64);
+ }
+
+ logger->reset();
+ // check whether overwrite (prefer_deferred_size < 120K < 2 * prefer_defer_size) partially
+ // overlapping two adjacent blobs goes partly deferred
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(block_size * 30, 'e'));
+
+ t.write(cid, hoid, 0x20000 - block_size, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ sleep(2);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_writes), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_write_bytes), block_size);
+
+ {
+ struct store_statfs_t statfs;
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_stored, (unsigned)block_size * 64);
+ ASSERT_LE(statfs.allocated, (unsigned)block_size * 64);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid2);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, DeferredOnBigOverwrite2) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred" << std::endl;
+ return;
+ }
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+ SetVal(g_conf(), "bluestore_max_blob_size", "65536");
+ SetVal(g_conf(), "bluestore_prefer_deferred_size", "65536");
+
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+
+ PerfCounters* logger = const_cast<PerfCounters*>(store->get_perf_counters());
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(128 * 1024, 'c'));
+
+ t.write(cid, hoid, 0x1000, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), bl.length());
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 3u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_writes), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_write_bytes), 0);
+ }
+
+ logger->reset();
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(128 * 1024, 'c'));
+
+ t.write(cid, hoid, 0x2000, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), bl.length());
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 3u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_writes), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_write_bytes), 57344);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, DeferredOnBigOverwrite3) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred" << std::endl;
+ return;
+ }
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+ SetVal(g_conf(), "bluestore_max_blob_size", "65536");
+ SetVal(g_conf(), "bluestore_prefer_deferred_size", "65536");
+
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+
+ PerfCounters* logger = const_cast<PerfCounters*>(store->get_perf_counters());
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ logger->reset();
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(4096 * 1024, 'c'));
+
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), bl.length());
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 64u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_writes), 0u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_write_bytes), 0u);
+ }
+ logger->reset();
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(4096 * 1024, 'c'));
+
+ t.write(cid, hoid, 0x1000, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ ASSERT_EQ(logger->get(l_bluestore_write_big), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_bytes), bl.length());
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 65u);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_writes), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_write_bytes), 61440);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, DeferredDifferentChunks) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred" << std::endl;
+ return;
+ }
+
+ size_t alloc_size = 4096;
+ size_t large_object_size = 1 * 1024 * 1024;
+ size_t prefer_deferred_size = 65536;
+ StartDeferred(alloc_size);
+ SetVal(g_conf(), "bluestore_max_blob_size", "131072");
+ SetVal(g_conf(), "bluestore_prefer_deferred_size",
+ stringify(prefer_deferred_size).c_str());
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ const PerfCounters* logger = store->get_perf_counters();
+ size_t exp_bluestore_write_big = 0;
+ size_t exp_bluestore_write_big_deferred = 0;
+
+ ObjectStore::CollectionHandle ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ for (size_t expected_write_size = 1024; expected_write_size <= prefer_deferred_size; expected_write_size *= 2) {
+ //create object with hint
+ ghobject_t hoid(hobject_t("test-"+to_string(expected_write_size), "", CEPH_NOSNAP, 0, -1, ""));
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.set_alloc_hint(cid, hoid, large_object_size, expected_write_size,
+ CEPH_OSD_ALLOC_HINT_FLAG_SEQUENTIAL_READ |
+ CEPH_OSD_ALLOC_HINT_FLAG_APPEND_ONLY);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ //fill object
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(large_object_size, 'h'));
+ t.write(cid, hoid, 0, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ++exp_bluestore_write_big;
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), exp_bluestore_write_big);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), exp_bluestore_write_big_deferred);
+
+ // check whether write will properly use deferred
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(alloc_size + 2, 'z'));
+ t.write(cid, hoid, large_object_size - 2 * alloc_size - 1, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ++exp_bluestore_write_big;
+ if (expected_write_size < prefer_deferred_size)
+ ++exp_bluestore_write_big_deferred;
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_write_big), exp_bluestore_write_big);
+ ASSERT_EQ(logger->get(l_bluestore_write_big_deferred), exp_bluestore_write_big_deferred);
+ }
+ ch.reset(nullptr);
+ CloseAndReopen();
+ ch = store->open_collection(cid);
+ // check values
+ for (size_t expected_write_size = 1024; expected_write_size <= 65536; expected_write_size *= 2) {
+ ghobject_t hoid(hobject_t("test-"+to_string(expected_write_size), "", CEPH_NOSNAP, 0, -1, ""));
+ {
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, large_object_size, bl);
+ ASSERT_EQ(r, large_object_size);
+ expected.append(string(large_object_size - 2 * alloc_size - 1, 'h'));
+ expected.append(string(alloc_size + 2, 'z'));
+ expected.append(string(alloc_size - 1, 'h'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ }
+ }
+ {
+ ObjectStore::Transaction t;
+ for (size_t expected_write_size = 1024; expected_write_size <= 65536; expected_write_size *= 2) {
+ ghobject_t hoid(hobject_t("test-"+to_string(expected_write_size), "", CEPH_NOSNAP, 0, -1, ""));
+ t.remove(cid, hoid);
+ }
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BlobReuseOnOverwriteReverse) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no overwrite" << std::endl;
+ return;
+ }
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+ SetVal(g_conf(), "bluestore_max_blob_size", "65536");
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_hint", "", CEPH_NOSNAP, 0, -1, ""));
+
+ auto ch = store->create_new_collection(cid);
+
+ const PerfCounters* logger = store->get_perf_counters();
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size * 2, 'a'));
+ t.write(cid, hoid, block_size * 10, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // prepend existing
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'b'));
+ t.write(cid, hoid, block_size * 9, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size * 9, block_size * 2, bl);
+ ASSERT_EQ(r, (int)block_size * 2);
+ expected.append(string(block_size, 'b'));
+ expected.append(string(block_size, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 1u);
+ }
+
+
+ {
+ // prepend existing with a gap
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'c'));
+ t.write(cid, hoid, block_size * 7, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size * 7, block_size * 3, bl);
+ ASSERT_EQ(r, (int)block_size * 3);
+ expected.append(string(block_size, 'c'));
+ expected.append(string(block_size, 0));
+ expected.append(string(block_size, 'b'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 2u);
+ }
+
+ {
+ // append after existing with a gap
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'd'));
+ t.write(cid, hoid, block_size * 13, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size * 11, block_size * 3, bl);
+ ASSERT_EQ(r, (int)block_size * 3);
+ expected.append(string(block_size, 'a'));
+ expected.append(string(block_size, 0));
+ expected.append(string(block_size, 'd'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 3u);
+ }
+
+ {
+ // append twice to the next max_blob slot
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'e'));
+ t.write(cid, hoid, block_size * 17, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ t.write(cid, hoid, block_size * 19, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size * 17, block_size * 3, bl);
+ ASSERT_EQ(r, (int)block_size * 3);
+ expected.append(string(block_size, 'e'));
+ expected.append(string(block_size, 0));
+ expected.append(string(block_size, 'e'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 5u);
+ }
+ {
+ // fill gaps at the second slot
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'f'));
+ t.write(cid, hoid, block_size * 16, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ t.write(cid, hoid, block_size * 18, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, block_size * 16, block_size * 4, bl);
+ ASSERT_EQ(r, (int)block_size * 4);
+ expected.append(string(block_size, 'f'));
+ expected.append(string(block_size, 'e'));
+ expected.append(string(block_size, 'f'));
+ expected.append(string(block_size, 'e'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 2u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 4u);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BlobReuseOnSmallOverwrite) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no overwrite" << std::endl;
+ return;
+ }
+
+ size_t block_size = 4096;
+ StartDeferred(block_size);
+ SetVal(g_conf(), "bluestore_max_blob_size", "65536");
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_hint", "", CEPH_NOSNAP, 0, -1, ""));
+
+ const PerfCounters* logger = store->get_perf_counters();
+ auto ch = store->create_new_collection(cid);
+
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(block_size, 'a'));
+ t.write(cid, hoid, 0, bl.length(), bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ t.write(cid, hoid, block_size * 2, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // write small into the gap
+ ObjectStore::Transaction t;
+ bufferlist bl;
+
+ bl.append(std::string(3, 'b'));
+ t.write(cid, hoid, block_size + 1, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ // We need to issue a read to trigger cache stat update that refresh
+ // perf counters. additionally we need to wait some time for mempool
+ // thread to update stats.
+ sleep(1);
+ bufferlist bl, expected;
+ r = store->read(ch, hoid, 0, block_size * 3, bl);
+ ASSERT_EQ(r, (int)block_size * 3);
+ expected.append(string(block_size, 'a'));
+ expected.append(string(1, 0));
+ expected.append(string(3, 'b'));
+ expected.append(string(block_size - 4, 0));
+ expected.append(string(block_size, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+
+ ASSERT_EQ(logger->get(l_bluestore_blobs), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_extents), 3u);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+// The test case to reproduce an issue when write happens
+// to a zero space between the extents sharing the same spanning blob
+// with unloaded shard map.
+// Second extent might be filled with zeros this way due to wrong result
+// returned by has_any_extents() call in do_write_small. The latter is caused
+// by incompletly loaded extent map.
+TEST_P(StoreTestSpecificAUSize, SmallWriteOnShardedExtents) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ size_t block_size = 0x10000;
+ StartDeferred(block_size);
+
+ SetVal(g_conf(), "bluestore_csum_type", "xxhash64");
+ SetVal(g_conf(), "bluestore_max_blob_size", "524288"); // for sure
+
+ g_conf().apply_changes(nullptr);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid1(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ //doing some tricks to have sharded extents/spanning objects
+ ObjectStore::Transaction t;
+ bufferlist bl, bl2;
+
+ bl.append(std::string(0x80000, 'a'));
+ t.write(cid, hoid1, 0, bl.length(), bl, 0);
+ t.zero(cid, hoid1, 0x719e0, 0x75b0 );
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ bl2.append(std::string(0x70000, 'b'));
+ t.write(cid, hoid1, 0, bl2.length(), bl2, 0);
+ t.zero(cid, hoid1, 0, 0x50000);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ }
+ ch.reset();
+ store->umount();
+ store->mount();
+ ch = store->open_collection(cid);
+
+ {
+ // do a write to zero space in between some extents sharing the same blob
+ ObjectStore::Transaction t;
+ bufferlist bl, bl2;
+
+ bl.append(std::string(0x6520, 'c'));
+ t.write(cid, hoid1, 0x71c00, bl.length(), bl, 0);
+
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, expected;
+
+ r = store->read(ch, hoid1, 0x70000, 0x9c00, bl);
+ ASSERT_EQ(r, (int)0x9c00);
+ expected.append(string(0x19e0, 'a'));
+ expected.append(string(0x220, 0));
+ expected.append(string(0x6520, 'c'));
+ expected.append(string(0xe70, 0));
+ expected.append(string(0xc70, 'a'));
+ ASSERT_TRUE(bl_eq(expected, bl));
+ bl.clear();
+
+ }
+
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid1);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, ReproBug56488Test) {
+
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: no deferred" << std::endl;
+ return;
+ }
+
+ size_t alloc_size = 65536;
+ size_t write_size = 4096;
+ SetVal(g_conf(), "bluestore_debug_enforce_settings", "hdd");
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+ SetVal(g_conf(), "bluestore_block_db_size", stringify(1 << 30).c_str());
+
+ g_conf().apply_changes(nullptr);
+ StartDeferred(alloc_size);
+
+ int r;
+ coll_t cid;
+ const PerfCounters* logger = store->get_perf_counters();
+
+ ObjectStore::CollectionHandle ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ auto issued_dw = logger->get(l_bluestore_issued_deferred_writes);
+ auto issued_dw_bytes = logger->get(l_bluestore_issued_deferred_write_bytes);
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(write_size, 'x'));
+ t.write(cid, hoid, 0, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_writes), issued_dw + 1);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_write_bytes),
+ issued_dw_bytes + write_size);
+ }
+ {
+ ghobject_t hoid(hobject_t("test-a", "", CEPH_NOSNAP, 0, -1, ""));
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ auto issued_dw = logger->get(l_bluestore_issued_deferred_writes);
+ auto issued_dw_bytes = logger->get(l_bluestore_issued_deferred_write_bytes);
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append(std::string(write_size * 2, 'x'));
+ t.write(cid, hoid, alloc_size - write_size, bl.length(), bl,
+ CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_writes), issued_dw + 2);
+ ASSERT_EQ(logger->get(l_bluestore_issued_deferred_write_bytes),
+ issued_dw_bytes + write_size * 2);
+ }
+ {
+ ObjectStore::Transaction t;
+ ghobject_t hoid(hobject_t("test", "", CEPH_NOSNAP, 0, -1, ""));
+ t.remove(cid, hoid);
+ ghobject_t hoid_a(hobject_t("test-a", "", CEPH_NOSNAP, 0, -1, ""));
+ t.remove(cid, hoid_a);
+ t.remove_collection(cid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+#endif //#if defined(WITH_BLUESTORE)
+
+TEST_P(StoreTest, KVDBHistogramTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ int NUM_OBJS = 200;
+ int r = 0;
+ coll_t cid;
+ string base("testobj.");
+ bufferlist a;
+ bufferptr ap(0x1000);
+ memset(ap.c_str(), 'a', 0x1000);
+ a.append(ap);
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ for (int i = 0; i < NUM_OBJS; ++i) {
+ ObjectStore::Transaction t;
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%d", i);
+ ghobject_t hoid(hobject_t(sobject_t(base + string(buf), CEPH_NOSNAP)));
+ t.write(cid, hoid, 0, 0x1000, a);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ std::unique_ptr<Formatter> f(Formatter::create("store_test", "json-pretty", "json-pretty"));
+ store->generate_db_histogram(f.get());
+ f->flush(cout);
+ cout << std::endl;
+}
+
+TEST_P(StoreTest, KVDBStatsTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "rocksdb_perf", "true");
+ SetVal(g_conf(), "rocksdb_collect_compaction_stats", "true");
+ SetVal(g_conf(), "rocksdb_collect_extended_stats","true");
+ SetVal(g_conf(), "rocksdb_collect_memory_stats","true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ int r = store->umount();
+ ASSERT_EQ(r, 0);
+ r = store->mount(); //to force rocksdb stats
+ ASSERT_EQ(r, 0);
+
+ int NUM_OBJS = 200;
+ coll_t cid;
+ string base("testobj.");
+ bufferlist a;
+ bufferptr ap(0x1000);
+ memset(ap.c_str(), 'a', 0x1000);
+ a.append(ap);
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ for (int i = 0; i < NUM_OBJS; ++i) {
+ ObjectStore::Transaction t;
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%d", i);
+ ghobject_t hoid(hobject_t(sobject_t(base + string(buf), CEPH_NOSNAP)));
+ t.write(cid, hoid, 0, 0x1000, a);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ std::unique_ptr<Formatter> f(Formatter::create("store_test", "json-pretty", "json-pretty"));
+ store->get_db_statistics(f.get());
+ f->flush(cout);
+ cout << std::endl;
+}
+
+#if defined(WITH_BLUESTORE)
+TEST_P(StoreTestSpecificAUSize, garbageCollection) {
+ int r;
+ coll_t cid;
+ int buf_len = 256 * 1024;
+ int overlap_offset = 64 * 1024;
+ int write_offset = buf_len;
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: assertions about allocations need to be adjusted" << std::endl;
+ return;
+ }
+
+#define WRITE_AT(offset, _length) {\
+ ObjectStore::Transaction t;\
+ if ((uint64_t)_length != bl.length()) { \
+ buffer::ptr p(bl.c_str(), _length);\
+ bufferlist bl_tmp;\
+ bl_tmp.push_back(p);\
+ t.write(cid, hoid, offset, bl_tmp.length(), bl_tmp);\
+ } else {\
+ t.write(cid, hoid, offset, bl.length(), bl);\
+ }\
+ r = queue_transaction(store, ch, std::move(t));\
+ ASSERT_EQ(r, 0);\
+ }
+
+ StartDeferred(65536);
+
+ SetVal(g_conf(), "bluestore_compression_max_blob_size", "524288");
+ SetVal(g_conf(), "bluestore_compression_min_blob_size", "262144");
+ SetVal(g_conf(), "bluestore_max_blob_size", "524288");
+ SetVal(g_conf(), "bluestore_compression_mode", "force");
+ g_conf().apply_changes(nullptr);
+
+ auto ch = store->create_new_collection(cid);
+
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ {
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 5, in);
+ ASSERT_EQ(-ENOENT, r);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ std::string data;
+ data.resize(buf_len);
+
+ {
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ bufferlist bl;
+
+ for(size_t i = 0; i < data.size(); i++)
+ data[i] = i % 256;
+
+ bl.append(data);
+
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(0, buf_len);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x10000);
+ }
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(write_offset - 2 * overlap_offset, buf_len);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x20000);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0u);
+ }
+
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(write_offset - overlap_offset, buf_len);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x20000);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0x10000u);
+ }
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(write_offset - 3 * overlap_offset, buf_len);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x20000);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0x20000u);
+ }
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(write_offset + 1, overlap_offset-1);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x20000);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0x20000u);
+ }
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(write_offset + 1, overlap_offset);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x10000);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0x3ffffu);
+ }
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(0, buf_len-1);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x10000);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0x40001u);
+ }
+ SetVal(g_conf(), "bluestore_gc_enable_total_threshold", "1"); //forbid GC when saving = 0
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(1, overlap_offset-2);
+ WRITE_AT(overlap_offset * 2 + 1, overlap_offset-2);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x10000);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0x40001u);
+ }
+ {
+ struct store_statfs_t statfs;
+ WRITE_AT(overlap_offset + 1, overlap_offset-2);
+ int r = store->statfs(&statfs);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(statfs.data_compressed_allocated, 0x0);
+ const PerfCounters* counters = store->get_perf_counters();
+ ASSERT_EQ(counters->get(l_bluestore_gc_merged), 0x40007u);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ cerr << "Cleaning" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, fsckOnUnalignedDevice) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_block_size",
+ stringify(0x280005000).c_str()); //10 Gb + 4K
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "false");
+ StartDeferred(0x4000);
+ store->umount();
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ store->mount();
+
+}
+
+TEST_P(StoreTestSpecificAUSize, fsckOnUnalignedDevice2) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_block_size",
+ stringify(0x280005000).c_str()); //10 Gb + 20K
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "false");
+ StartDeferred(0x1000);
+ store->umount();
+ ASSERT_EQ(store->fsck(false), 0); // do fsck explicitly
+ store->mount();
+}
+
+namespace {
+ ghobject_t make_object(const char* name, int64_t pool) {
+ sobject_t soid{name, CEPH_NOSNAP};
+ uint32_t hash = std::hash<sobject_t>{}(soid);
+ return ghobject_t{hobject_t{soid, "", hash, pool, ""}};
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BluestoreRepairTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "TODO: repair mismatched write pointer (+ dead bytes mismatch)" << std::endl;
+ return;
+ }
+ const size_t offs_base = 65536 / 2;
+
+
+ // Now we need standalone db to pass "false free fix" section below
+ // Due to new BlueFS allocation model (single allocator for main device)
+ // it might cause "false free" blob overwrite by BlueFS/DB stuff
+ // and hence fail the test case and corrupt data.
+ //
+
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+ SetVal(g_conf(), "bluestore_block_db_size", "4294967296");
+
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "false");
+ SetVal(g_conf(), "bluestore_max_blob_size",
+ stringify(2 * offs_base).c_str());
+ SetVal(g_conf(), "bluestore_extent_map_shard_max_size", "12000");
+
+ StartDeferred(0x10000);
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+
+ // fill the store with some data
+ const uint64_t pool = 555;
+ coll_t cid(spg_t(pg_t(0, pool), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+
+ ghobject_t hoid = make_object("Object 1", pool);
+ ghobject_t hoid_dup = make_object("Object 1(dup)", pool);
+ ghobject_t hoid2 = make_object("Object 2", pool);
+ ghobject_t hoid_cloned = hoid2;
+ hoid_cloned.hobj.snap = 1;
+ ghobject_t hoid3 = make_object("Object 3", pool);
+ ghobject_t hoid3_cloned = hoid3;
+ hoid3_cloned.hobj.snap = 1;
+ bufferlist bl;
+ bl.append("1234512345");
+ int r;
+ const size_t repeats = 16;
+ {
+ auto ch = store->create_new_collection(cid);
+ cerr << "create collection + write" << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ for( auto i = 0ul; i < repeats; ++i ) {
+ t.write(cid, hoid, i * offs_base, bl.length(), bl);
+ t.write(cid, hoid_dup, i * offs_base, bl.length(), bl);
+ }
+ for( auto i = 0ul; i < repeats; ++i ) {
+ t.write(cid, hoid2, i * offs_base, bl.length(), bl);
+ }
+ t.clone(cid, hoid2, hoid_cloned);
+
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ bstore->umount();
+ bool err_was_injected = false;
+ //////////// leaked pextent fix ////////////
+ cerr << "fix leaked pextents" << std::endl;
+ ASSERT_EQ(bstore->fsck(false), 0);
+ ASSERT_EQ(bstore->repair(false), 0);
+ bstore->mount();
+ if (!bstore->has_null_manager()) {
+ bstore->inject_leaked(0x30000);
+ err_was_injected = true;
+ }
+
+ bstore->umount();
+ if (err_was_injected) {
+ ASSERT_EQ(bstore->fsck(false), 1);
+ }
+ ASSERT_EQ(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+
+ //////////// false free fix ////////////
+ cerr << "fix false free pextents" << std::endl;
+ bstore->mount();
+ if (!bstore->has_null_manager()) {
+ bstore->inject_false_free(cid, hoid);
+ err_was_injected = true;
+ }
+ bstore->umount();
+ if (err_was_injected) {
+ ASSERT_EQ(bstore->fsck(false), 2);
+ ASSERT_EQ(bstore->repair(false), 0);
+ }
+ ASSERT_EQ(bstore->fsck(false), 0);
+
+
+ ///////// undecodable shared blob key / stray shared blob records ///////
+ bstore->mount();
+ cerr << "undecodable shared blob key" << std::endl;
+ bstore->inject_broken_shared_blob_key("undec1",
+ bufferlist());
+ bstore->inject_broken_shared_blob_key("undecodable key 2",
+ bufferlist());
+ bstore->inject_broken_shared_blob_key("undecodable key 3",
+ bufferlist());
+ bstore->umount();
+ ASSERT_EQ(bstore->fsck(false), 3);
+ ASSERT_EQ(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+
+ cerr << "misreferencing" << std::endl;
+ bstore->mount();
+ bstore->inject_misreference(cid, hoid, cid, hoid_dup, 0);
+ bstore->inject_misreference(cid, hoid, cid, hoid_dup, (offs_base * repeats) / 2);
+ bstore->inject_misreference(cid, hoid, cid, hoid_dup, offs_base * (repeats -1) );
+ int expected_errors = bstore->has_null_manager() ? 3 : 6;
+ bstore->umount();
+ ASSERT_EQ(bstore->fsck(false), expected_errors);
+ ASSERT_EQ(bstore->repair(false), 0);
+
+ ASSERT_EQ(bstore->fsck(true), 0);
+
+ // reproducing issues #21040 & 20983
+ SetVal(g_conf(), "bluestore_debug_inject_bug21040", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ bstore->mount();
+
+ cerr << "repro bug #21040" << std::endl;
+ {
+ auto ch = store->open_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ bl.append("0123456789012345");
+ t.write(cid, hoid3, offs_base, bl.length(), bl);
+ bl.clear();
+ bl.append('!');
+ t.write(cid, hoid3, 0, bl.length(), bl);
+
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.clone(cid, hoid3, hoid3_cloned);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ bstore->umount();
+ // depending on statfs tracking we might meet or miss relevant error
+ // hence error count >= 3
+ ASSERT_GE(bstore->fsck(false), 3);
+ ASSERT_LE(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+ }
+
+ cerr << "Zombie spanning blob" << std::endl;
+ {
+ bstore->mount();
+ ghobject_t hoid4 = make_object("Object 4", pool);
+ auto ch = store->open_collection(cid);
+ {
+ bufferlist bl;
+ string s(0x1000, 'a');
+ bl.append(s);
+ ObjectStore::Transaction t;
+ for(size_t i = 0; i < 0x10; i++) {
+ t.write(cid, hoid4, i * bl.length(), bl.length(), bl);
+ }
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ sleep(5);
+ {
+ bstore->inject_zombie_spanning_blob(cid, hoid4, 12345);
+ bstore->inject_zombie_spanning_blob(cid, hoid4, 23456);
+ bstore->inject_zombie_spanning_blob(cid, hoid4, 23457);
+ }
+
+ bstore->umount();
+ ASSERT_EQ(bstore->fsck(false), 1);
+ ASSERT_LE(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+ }
+
+ //////////// verify invalid statfs ///////////
+ cerr << "fix invalid statfs" << std::endl;
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_stats", "true");
+ SetVal(g_conf(),
+ "bluestore_debug_inject_allocation_from_file_failure", "1");
+ store_statfs_t statfs0;
+ store_statfs_t statfs;
+ bstore->mount();
+ ASSERT_EQ(bstore->statfs(&statfs0), 0);
+ statfs = statfs0;
+ statfs.allocated += 0x10000;
+ statfs.data_stored += 0x10000;
+ ASSERT_FALSE(statfs0 == statfs);
+ // this enforces global stats usage
+ bstore->inject_statfs("bluestore_statfs", statfs);
+ bstore->umount();
+
+ ASSERT_GE(bstore->fsck(false), 1); // global stats mismatch might omitted when
+ // NCB restore is applied. Hence using >= for
+ // error count
+ ASSERT_EQ(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+ ASSERT_EQ(bstore->mount(), 0);
+ ASSERT_EQ(bstore->statfs(&statfs), 0);
+ // adjust free/internal meta space to success in comparison
+ statfs0.available = statfs.available;
+ statfs0.internal_metadata = statfs.internal_metadata;
+ ASSERT_EQ(statfs0, statfs);
+
+ SetVal(g_conf(),
+ "bluestore_debug_inject_allocation_from_file_failure", "0");
+ cerr << "fix invalid statfs2" << std::endl;
+ ASSERT_EQ(bstore->statfs(&statfs0), 0);
+ statfs = statfs0;
+ statfs.allocated += 0x20000;
+ statfs.data_stored += 0x20000;
+ ASSERT_FALSE(statfs0 == statfs);
+ // this enforces global stats usage
+ bstore->inject_statfs("bluestore_statfs", statfs);
+ bstore->umount();
+
+ ASSERT_EQ(bstore->fsck(false), 2);
+ ASSERT_EQ(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+ ASSERT_EQ(bstore->mount(), 0);
+ ASSERT_EQ(bstore->statfs(&statfs), 0);
+ // adjust free/internal meta space to success in comparison
+ statfs0.available = statfs.available;
+ statfs0.internal_metadata = statfs.internal_metadata;
+ ASSERT_EQ(statfs0, statfs);
+
+ cerr << "Completing" << std::endl;
+}
+
+TEST_P(StoreTestSpecificAUSize, BluestoreBrokenZombieRepairTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: smr repair is different" << std::endl;
+ return;
+ }
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "false");
+
+ StartDeferred(0x10000);
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+
+ int r;
+
+ cerr << "initializing" << std::endl;
+ {
+ const size_t col_count = 16;
+ const size_t obj_count = 1024;
+ ObjectStore::CollectionHandle ch[col_count];
+ ghobject_t hoid[col_count][obj_count];
+
+ unique_ptr<coll_t> cid[col_count];
+
+ for (size_t i = 0; i < col_count; i++) {
+ cid[i].reset(new coll_t(spg_t(pg_t(0, i), shard_id_t::NO_SHARD)));
+ ch[i] = store->create_new_collection(*cid[i]);
+ for (size_t j = 0; j < obj_count; j++) {
+ hoid[i][j] = make_object(stringify(j).c_str(), i);
+ }
+ }
+
+ for (size_t i = 0; i < col_count; i++) {
+ ObjectStore::Transaction t;
+ t.create_collection(*cid[i], 0);
+ r = queue_transaction(store, ch[i], std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ cerr << "onode preparing" << std::endl;
+ bufferlist bl;
+ string s(0x1000, 'a');
+ bl.append(s);
+
+ for (size_t i = 0; i < col_count; i++) {
+ for (size_t j = 0; j < obj_count; j++) {
+ ObjectStore::Transaction t;
+ t.write(*cid[i], hoid[i][j], bl.length(), bl.length(), bl);
+ r = queue_transaction(store, ch[i], std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ }
+ cerr << "Zombie spanning blob injection" << std::endl;
+
+ sleep(5);
+
+ for (size_t i = 0; i < col_count; i++) {
+ for (size_t j = 0; j < obj_count; j++) {
+ bstore->inject_zombie_spanning_blob(*cid[i], hoid[i][j], 12345);
+ }
+ }
+
+ cerr << "fscking/fixing" << std::endl;
+ bstore->umount();
+ ASSERT_EQ(bstore->fsck(false), col_count * obj_count);
+ ASSERT_LE(bstore->quick_fix(), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+ }
+
+ cerr << "Completing" << std::endl;
+ bstore->mount();
+}
+
+TEST_P(StoreTestSpecificAUSize, BluestoreRepairSharedBlobTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "TODO: repair mismatched write pointer (+ dead bytes mismatch)" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "false");
+
+ const size_t block_size = 0x1000;
+ StartDeferred(block_size);
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+
+ // fill the store with some data
+ const uint64_t pool = 555;
+ coll_t cid(spg_t(pg_t(0, pool), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+
+ ghobject_t hoid = make_object("Object 1", pool);
+ ghobject_t hoid_cloned = hoid;
+ hoid_cloned.hobj.snap = 1;
+ ghobject_t hoid2 = make_object("Object 2", pool);
+
+ string s(block_size, 1);
+ bufferlist bl;
+ bl.append(s);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // check the scenario when shared blob contains
+ // references to extents from two objects which don't overlapp
+ // o1 -> 0x2000~1K
+ // o2 -> 0x4000~1k
+ cerr << "introduce 2 non-overlapped extents in a shared blob"
+ << std::endl;
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, bl.length(), bl);
+ t.write(cid, hoid2, 0, bl.length(), bl); // to make a gap in allocations
+ t.write(cid, hoid, block_size * 2 , bl.length(), bl);
+ t.clone(cid, hoid, hoid_cloned);
+ t.zero(cid, hoid, 0, bl.length());
+ t.zero(cid, hoid_cloned, block_size * 2, bl.length());
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bstore->umount();
+ bstore->mount();
+ {
+ string key;
+ _key_encode_u64(1, &key);
+ bluestore_shared_blob_t sb(1);
+ sb.ref_map.get(0x822000, block_size);
+ sb.ref_map.get(0x824000, block_size);
+ sb.ref_map.get(0x824000, block_size);
+ bufferlist bl;
+ encode(sb, bl);
+ bstore->inject_broken_shared_blob_key(key, bl);
+ }
+ bstore->umount();
+ ASSERT_EQ(bstore->fsck(false), 2);
+ ASSERT_EQ(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+
+ cerr << "Completing" << std::endl;
+ bstore->mount();
+}
+
+TEST_P(StoreTestSpecificAUSize, BluestoreBrokenNoSharedBlobRepairTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: smr repair is different" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "false");
+
+ StartDeferred(0x10000);
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+
+ int r;
+
+ // initializing
+ cerr << "initializing" << std::endl;
+ {
+ const uint64_t pool = 555;
+ coll_t cid(spg_t(pg_t(0, pool), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+
+ ghobject_t hoid = make_object("Object", pool);
+ ghobject_t hoid_cloned = hoid;
+ hoid_cloned.hobj.snap = 1;
+
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append("0123456789012345");
+ t.write(cid, hoid, 0, bl.length(), bl);
+
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.clone(cid, hoid, hoid_cloned);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ }
+ // injecting an error and checking
+ cerr << "injecting" << std::endl;
+ sleep(3); // need some time for the previous write to land
+ bstore->inject_no_shared_blob_key();
+ bstore->inject_stray_shared_blob_key(12345678);
+
+ {
+ cerr << "fscking/fixing" << std::endl;
+ // we need to check for null-manager before umount()
+ bool has_null_manager = bstore->has_null_manager();
+ bstore->umount();
+ // depending on the allocation map's source we can
+ // either observe or don't observe an additional
+ // extent leak detection. Hence adjusting the expected
+ // value
+ size_t expected_error_count =
+ has_null_manager ?
+ 4: // 4 sb ref mismatch errors [+ 1 optional statfs, hence ASSERT_GE]
+ 7; // 4 sb ref mismatch errors + 1 statfs + 1 block leak + 1 non-free
+ ASSERT_GE(bstore->fsck(false), expected_error_count);
+ // repair might report less errors than fsck above showed
+ // as some errors, e.g. statfs mismatch, are implicitly fixed
+ // before the detection during the previous repair steps...
+ ASSERT_LE(bstore->repair(false), expected_error_count);
+ ASSERT_EQ(bstore->fsck(false), 0);
+ }
+
+ cerr << "Completing" << std::endl;
+ bstore->mount();
+}
+
+TEST_P(StoreTest, BluestoreRepairGlobalStats) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ const size_t offs_base = 65536 / 2;
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+
+ // start with global stats
+ bstore->inject_global_statfs({});
+ bstore->umount();
+ SetVal(g_conf(), "bluestore_fsck_quick_fix_on_mount", "false");
+ bstore->mount();
+
+ // fill the store with some data
+ const uint64_t pool = 555;
+ coll_t cid(spg_t(pg_t(0, pool), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+
+ ghobject_t hoid = make_object("Object 1", pool);
+ ghobject_t hoid_dup = make_object("Object 1(dup)", pool);
+ ghobject_t hoid2 = make_object("Object 2", pool);
+ ghobject_t hoid_cloned = hoid2;
+ hoid_cloned.hobj.snap = 1;
+ ghobject_t hoid3 = make_object("Object 3", pool);
+ ghobject_t hoid3_cloned = hoid3;
+ hoid3_cloned.hobj.snap = 1;
+ bufferlist bl;
+ bl.append("1234512345");
+ int r;
+ const size_t repeats = 16;
+ {
+ auto ch = store->create_new_collection(cid);
+ cerr << "create collection + write" << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ for( auto i = 0ul; i < repeats; ++i ) {
+ t.write(cid, hoid, i * offs_base, bl.length(), bl);
+ t.write(cid, hoid_dup, i * offs_base, bl.length(), bl);
+ }
+ for( auto i = 0ul; i < repeats; ++i ) {
+ t.write(cid, hoid2, i * offs_base, bl.length(), bl);
+ }
+ t.clone(cid, hoid2, hoid_cloned);
+
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ bstore->umount();
+
+ // enable per-pool stats collection hence causing fsck to fail
+ cerr << "per-pool statfs" << std::endl;
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_stats", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ ASSERT_EQ(bstore->fsck(false), 1);
+ ASSERT_EQ(bstore->repair(false), 0);
+ ASSERT_EQ(bstore->fsck(false), 0);
+
+ bstore->mount();
+}
+
+TEST_P(StoreTest, BluestoreRepairGlobalStatsFixOnMount) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ const size_t offs_base = 65536 / 2;
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+
+ // start with global stats
+ bstore->inject_global_statfs({});
+ bstore->umount();
+ SetVal(g_conf(), "bluestore_fsck_quick_fix_on_mount", "false");
+ bstore->mount();
+
+ // fill the store with some data
+ const uint64_t pool = 555;
+ coll_t cid(spg_t(pg_t(0, pool), shard_id_t::NO_SHARD));
+ auto ch = store->create_new_collection(cid);
+
+ ghobject_t hoid = make_object("Object 1", pool);
+ ghobject_t hoid_dup = make_object("Object 1(dup)", pool);
+ ghobject_t hoid2 = make_object("Object 2", pool);
+ ghobject_t hoid_cloned = hoid2;
+ hoid_cloned.hobj.snap = 1;
+ ghobject_t hoid3 = make_object("Object 3", pool);
+ ghobject_t hoid3_cloned = hoid3;
+ hoid3_cloned.hobj.snap = 1;
+ bufferlist bl;
+ bl.append("1234512345");
+ int r;
+ const size_t repeats = 16;
+ {
+ auto ch = store->create_new_collection(cid);
+ cerr << "create collection + write" << std::endl;
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ for( auto i = 0ul; i < repeats; ++i ) {
+ t.write(cid, hoid, i * offs_base, bl.length(), bl);
+ t.write(cid, hoid_dup, i * offs_base, bl.length(), bl);
+ }
+ for( auto i = 0ul; i < repeats; ++i ) {
+ t.write(cid, hoid2, i * offs_base, bl.length(), bl);
+ }
+ t.clone(cid, hoid2, hoid_cloned);
+
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ bstore->umount();
+
+ // enable per-pool stats collection hence causing fsck to fail
+ cerr << "per-pool statfs" << std::endl;
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_stats", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ ASSERT_EQ(bstore->fsck(false), 1);
+
+ SetVal(g_conf(), "bluestore_fsck_quick_fix_on_mount", "true");
+ bstore->mount();
+ bstore->umount();
+ ASSERT_EQ(bstore->fsck(false), 0);
+
+ bstore->mount();
+}
+
+TEST_P(StoreTest, BluestoreStatistics) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "rocksdb_perf", "true");
+ SetVal(g_conf(), "rocksdb_collect_compaction_stats", "true");
+ SetVal(g_conf(), "rocksdb_collect_extended_stats","true");
+ SetVal(g_conf(), "rocksdb_collect_memory_stats","true");
+
+ // disable cache
+ SetVal(g_conf(), "bluestore_cache_size_ssd", "0");
+ SetVal(g_conf(), "bluestore_cache_size_hdd", "0");
+ SetVal(g_conf(), "bluestore_cache_size", "0");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ int r = store->umount();
+ ASSERT_EQ(r, 0);
+ r = store->mount();
+ ASSERT_EQ(r, 0);
+
+ BlueStore* bstore = NULL;
+ EXPECT_NO_THROW(bstore = dynamic_cast<BlueStore*> (store.get()));
+
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_db_statistics", "", CEPH_NOSNAP, 0, 0, ""));
+ auto ch = bstore->create_new_collection(cid);
+ bufferlist bl;
+ bl.append("0123456789abcdefghi");
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, hoid);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "Write object" << std::endl;
+ r = queue_transaction(bstore, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bufferlist readback;
+ r = store->read(ch, hoid, 0, bl.length(), readback);
+ ASSERT_EQ(static_cast<int>(bl.length()), r);
+ ASSERT_TRUE(bl_eq(bl, readback));
+ }
+ std::unique_ptr<Formatter> f(Formatter::create("store_test", "json-pretty", "json-pretty"));
+ EXPECT_NO_THROW(store->get_db_statistics(f.get()));
+ f->flush(cout);
+ cout << std::endl;
+}
+
+TEST_P(StoreTest, BluestoreStrayOmapDetection)
+{
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+ const uint64_t pool = 555;
+ coll_t cid(spg_t(pg_t(0, pool), shard_id_t::NO_SHARD));
+ ghobject_t oid = make_object("Object 1", pool);
+ ghobject_t oid2 = make_object("Object 2", pool);
+ // fill the store with some data
+ auto ch = store->create_new_collection(cid);
+ bufferlist h;
+ h.append("header");
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, oid);
+ t.omap_setheader(cid, oid, h);
+ t.touch(cid, oid2);
+ t.omap_setheader(cid, oid2, h);
+ int r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // inject stray omap
+ bstore->inject_stray_omap(123456, "somename");
+
+ bstore->umount();
+ // check we detect injected stray omap..
+
+ ASSERT_EQ(bstore->fsck(false), 1);
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ bstore->mount();
+}
+
+TEST_P(StoreTest, BluestorePerPoolOmapFixOnMount)
+{
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+ const uint64_t pool = 555;
+ coll_t cid(spg_t(pg_t(0, pool), shard_id_t::NO_SHARD));
+ ghobject_t oid = make_object("Object 1", pool);
+ ghobject_t oid2 = make_object("Object 2", pool);
+ // fill the store with some data
+ auto ch = store->create_new_collection(cid);
+ map<string, bufferlist> omap;
+ bufferlist h;
+ h.append("header");
+ {
+ omap["omap_key"].append("omap value");
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, oid);
+ t.omap_setheader(cid, oid, h);
+ t.touch(cid, oid2);
+ t.omap_setheader(cid, oid2, h);
+ int r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // inject legacy omaps
+ bstore->inject_legacy_omap();
+ bstore->inject_legacy_omap(cid, oid);
+ bstore->inject_legacy_omap(cid, oid2);
+
+ bstore->umount();
+
+ // check we injected an issue
+ SetVal(g_conf(), "bluestore_fsck_quick_fix_on_mount", "false");
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_omap", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ ASSERT_EQ(bstore->fsck(false), 3);
+
+ // set autofix and mount
+ SetVal(g_conf(), "bluestore_fsck_quick_fix_on_mount", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ bstore->mount();
+ bstore->umount();
+
+ // check we fixed it..
+ ASSERT_EQ(bstore->fsck(false), 0);
+ bstore->mount();
+
+ //
+ // Now repro https://tracker.ceph.com/issues/43824
+ //
+ // inject legacy omaps again
+ bstore->inject_legacy_omap();
+ bstore->inject_legacy_omap(cid, oid);
+ bstore->inject_legacy_omap(cid, oid2);
+ bstore->umount();
+
+ // check we injected an issue
+ SetVal(g_conf(), "bluestore_fsck_quick_fix_on_mount", "true");
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_omap", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ bstore->mount();
+ ch = store->open_collection(cid);
+
+ {
+ // write to onode which will partiall revert per-pool
+ // omap repair done on mount due to #43824.
+ // And object removal will leave stray per-pool omap recs
+ //
+ ObjectStore::Transaction t;
+ bufferlist bl;
+ bl.append("data");
+ //this triggers onode rec update and hence legacy omap
+ t.write(cid, oid, 0, bl.length(), bl);
+ t.remove(cid, oid2); // this will trigger stray per-pool omap
+ int r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bstore->umount();
+ // check omap's been fixed.
+ ASSERT_EQ(bstore->fsck(false), 0); // this will fail without fix for #43824
+
+ bstore->mount();
+}
+
+class hugepaged_raw;
+
+static bool is_hugepaged(const bufferptr& bp)
+{
+ const auto& ibp =
+ static_cast<const ceph::buffer_instrumentation::instrumented_bptr&>(bp);
+ return ibp.is_raw_marked<BlockDevice::hugepaged_raw_marker_t>();
+}
+
+// disabled by default b/c of the dependency on huge page ssome test
+// environments might not offer without extra configuration.
+TEST_P(StoreTestDeferredSetup, DISABLED_BluestoreHugeReads)
+{
+ if (string(GetParam()) != "bluestore") {
+ return;
+ }
+
+ constexpr static size_t HUGE_BUFFER_SIZE{2_M};
+ cout << "Configuring huge page pools" << std::endl;
+ {
+ SetVal(g_conf(), "bdev_read_preallocated_huge_buffers",
+ fmt::format("{}=2", HUGE_BUFFER_SIZE).c_str());
+ SetVal(g_conf(), "bluestore_max_blob_size",
+ std::to_string(HUGE_BUFFER_SIZE).c_str());
+ // let's verify the per-IOContext no-cache override
+ SetVal(g_conf(), "bluestore_default_buffered_read", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ }
+ DeferredSetup();
+
+ coll_t cid;
+ ghobject_t hoid(hobject_t("test_huge_buffers", "", CEPH_NOSNAP, 0, 0, ""));
+ auto ch = store->create_new_collection(cid);
+
+ bufferlist bl;
+ {
+ bufferptr bp{HUGE_BUFFER_SIZE};
+ // non-zeros! Otherwise the deduplication will take place.
+ ::memset(bp.c_str(), 0x42, HUGE_BUFFER_SIZE);
+ bl.push_back(std::move(bp));
+ ASSERT_EQ(bl.get_num_buffers(), 1);
+ ASSERT_EQ(bl.length(), HUGE_BUFFER_SIZE);
+ }
+
+ cout << "Write object" << std::endl;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ t.touch(cid, hoid);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ const auto r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ // force cache clear
+ {
+ EXPECT_EQ(store->umount(), 0);
+ EXPECT_EQ(store->mount(), 0);
+ ch = store->open_collection(cid);
+ }
+
+ // we want to extend the life-time of all huge paged-backed
+ // bufferlists to validate the behaviour on pool exhaustion.
+ bufferlist bl_1_huge, bl_2_huge, bl_3_plain;
+
+ cout << "Read object 1st time" << std::endl;
+ {
+ const auto r = store->read(ch, hoid, 0, HUGE_BUFFER_SIZE, bl_1_huge);
+ ASSERT_EQ(static_cast<int>(HUGE_BUFFER_SIZE), r);
+ ASSERT_TRUE(bl_eq(bl, bl_1_huge));
+ ASSERT_EQ(bl_1_huge.get_num_buffers(), 1);
+ ASSERT_TRUE(is_hugepaged(bl_1_huge.front()));
+ }
+
+ cout << "Read object 2nd time" << std::endl;
+ {
+ const auto r = store->read(ch, hoid, 0, HUGE_BUFFER_SIZE, bl_2_huge);
+ ASSERT_EQ(static_cast<int>(HUGE_BUFFER_SIZE), r);
+ ASSERT_TRUE(bl_eq(bl, bl_2_huge));
+ ASSERT_EQ(bl_2_huge.get_num_buffers(), 1);
+ ASSERT_TRUE(is_hugepaged(bl_2_huge.front()));
+ }
+
+ cout << "Read object 3rd time" << std::endl;
+ {
+ const auto r = store->read(ch, hoid, 0, HUGE_BUFFER_SIZE, bl_3_plain);
+ ASSERT_EQ(static_cast<int>(HUGE_BUFFER_SIZE), r);
+ ASSERT_TRUE(bl_eq(bl, bl_3_plain));
+ ASSERT_EQ(bl_3_plain.get_num_buffers(), 1);
+ ASSERT_FALSE(is_hugepaged(bl_3_plain.front()));
+ }
+}
+
+TEST_P(StoreTest, SpuriousReadErrorTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ int r;
+ auto logger = store->get_perf_counters();
+ coll_t cid;
+ auto ch = store->create_new_collection(cid);
+ ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)));
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist test_data;
+ bufferptr ap(0x2000);
+ memset(ap.c_str(), 'a', 0x2000);
+ test_data.append(ap);
+ {
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, 0, 0x2000, test_data);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ // force cache clear
+ EXPECT_EQ(store->umount(), 0);
+ EXPECT_EQ(store->mount(), 0);
+ }
+ ch = store->open_collection(cid);
+
+ cerr << "Injecting CRC error with no retry, expecting EIO" << std::endl;
+ SetVal(g_conf(), "bluestore_retry_disk_reads", "0");
+ SetVal(g_conf(), "bluestore_debug_inject_csum_err_probability", "1");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ {
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 0x2000, in, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_EQ(-EIO, r);
+ ASSERT_EQ(logger->get(l_bluestore_read_eio), 1u);
+ ASSERT_EQ(logger->get(l_bluestore_reads_with_retries), 0u);
+ }
+
+ cerr << "Injecting CRC error with retries, expecting success after several retries" << std::endl;
+ SetVal(g_conf(), "bluestore_retry_disk_reads", "255");
+ SetVal(g_conf(), "bluestore_debug_inject_csum_err_probability", "0.8");
+ /**
+ * Probabilistic test: 25 reads, each has a 80% chance of failing with 255 retries
+ * Probability of at least one retried read: 1 - (0.2 ** 25) = 100% - 3e-18
+ * Probability of a random test failure: 1 - ((1 - (0.8 ** 255)) ** 25) ~= 5e-24
+ */
+ g_ceph_context->_conf.apply_changes(nullptr);
+ {
+ for (int i = 0; i < 25; ++i) {
+ bufferlist in;
+ r = store->read(ch, hoid, 0, 0x2000, in, CEPH_OSD_OP_FLAG_FADVISE_NOCACHE);
+ ASSERT_EQ(0x2000, r);
+ ASSERT_TRUE(bl_eq(test_data, in));
+ }
+ ASSERT_GE(logger->get(l_bluestore_reads_with_retries), 1u);
+ }
+}
+
+TEST_P(StoreTest, mergeRegionTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "true");
+ SetVal(g_conf(), "bluestore_fsck_on_umount", "true");
+ SetVal(g_conf(), "bdev_debug_inflight_ios", "true");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ uint32_t chunk_size = g_ceph_context->_conf->bdev_block_size;
+ int r = -1;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl5;
+ bl5.append("abcde");
+ uint64_t offset = 0;
+ { // 1. same region
+ ObjectStore::Transaction t;
+ t.write(cid, hoid, offset, 5, bl5);
+ t.write(cid, hoid, 0xa + offset, 5, bl5);
+ t.write(cid, hoid, 0x14 + offset, 5, bl5);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ { // 2. adjacent regions
+ ObjectStore::Transaction t;
+ offset = chunk_size;
+ t.write(cid, hoid, offset, 5, bl5);
+ t.write(cid, hoid, offset + chunk_size + 3, 5, bl5);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ { // 3. front merge
+ ObjectStore::Transaction t;
+ offset = chunk_size * 2;
+ t.write(cid, hoid, offset, 5, bl5);
+ t.write(cid, hoid, offset + chunk_size - 2, 5, bl5);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ { // 4. back merge
+ ObjectStore::Transaction t;
+ bufferlist blc2;
+ blc2.append_zero(chunk_size + 2);
+
+ offset = chunk_size * 3;
+ t.write(cid, hoid, offset, chunk_size + 2, blc2);
+ t.write(cid, hoid, offset + chunk_size + 3, 5, bl5);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ { // 5. overlapping
+ ObjectStore::Transaction t;
+ uint64_t final_len = 0;
+ offset = chunk_size * 10;
+ bufferlist bl2c2;
+ bl2c2.append_zero(chunk_size * 2);
+ t.write(cid, hoid, offset + chunk_size * 3 - 3, chunk_size * 2, bl2c2);
+ bl2c2.append_zero(2);
+ t.write(cid, hoid, offset + chunk_size - 2, chunk_size * 2 + 2, bl2c2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ final_len = (offset + chunk_size * 3 - 3) + (chunk_size * 2);
+ bufferlist bl;
+ r = store->read(ch, hoid, 0, final_len, bl);
+ ASSERT_EQ(final_len, static_cast<uint64_t>(r));
+ }
+}
+
+TEST_P(StoreTest, FixSMRWritePointer) {
+ if(string(GetParam()) != "bluestore")
+ return;
+ if (!smr)
+ return;
+ int r = store->umount();
+ ASSERT_EQ(0, r);
+
+ // copied from StoreTestFixture
+ std::string path = GetParam() + ".test_temp_dir"s;
+
+ std::string p = path + "/block";
+ BlockDevice* bdev = BlockDevice::create(g_ceph_context, p, nullptr, nullptr, nullptr, nullptr);
+ r = bdev->open(p);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(true, bdev->is_smr());
+
+ std::vector<uint64_t> wp = bdev->get_zones();
+ uint64_t first_seq_zone = bdev->get_conventional_region_size() / bdev->get_zone_size();
+
+ IOContext ioc(g_ceph_context, NULL, true);
+ bufferlist bl;
+ bl.append(std::string(1024 * 1024, 'x'));
+ r = bdev->aio_write(wp[first_seq_zone], bl, &ioc, false);
+ ASSERT_EQ(0, r);
+ bdev->aio_submit(&ioc);
+ ioc.aio_wait();
+ bdev->close();
+ delete bdev;
+
+ r = store->mount();
+ ASSERT_EQ(0, r);
+}
+
+
+TEST_P(StoreTestSpecificAUSize, BluestoreEnforceHWSettingsHdd) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_debug_enforce_settings", "hdd");
+ StartDeferred(0x1000);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(g_ceph_context->_conf->bluestore_max_blob_size_hdd, '0');
+ bl.append(s);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "write" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ const PerfCounters* logger = store->get_perf_counters();
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 1u);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BluestoreEnforceHWSettingsSsd) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_debug_enforce_settings", "ssd");
+ StartDeferred(0x1000);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(g_ceph_context->_conf->bluestore_max_blob_size_ssd * 8, '0');
+ bl.append(s);
+ t.write(cid, hoid, 0, bl.length(), bl);
+ cerr << "write" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ const PerfCounters* logger = store->get_perf_counters();
+ ASSERT_EQ(logger->get(l_bluestore_write_big_blobs), 8u);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, ReproNoBlobMultiTest) {
+
+ if(string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP (FIXME): bluestore gc does not seem to do the trick here" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+ SetVal(g_conf(), "bluestore_block_db_size", "4294967296");
+ SetVal(g_conf(), "bluestore_block_size", "12884901888");
+ SetVal(g_conf(), "bluestore_max_blob_size", "524288");
+
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(65536);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP)));
+ ghobject_t hoid2 = hoid;
+ hoid2.hobj.snap = 1;
+
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ bool exists = store->exists(ch, hoid);
+ ASSERT_TRUE(!exists);
+
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ cerr << "Creating object " << hoid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+
+ exists = store->exists(ch, hoid);
+ ASSERT_EQ(true, exists);
+ }
+ {
+ uint64_t offs = 0;
+ bufferlist bl;
+ const int size = 0x100;
+ bufferptr ap(size);
+ memset(ap.c_str(), 'a', size);
+ bl.append(ap);
+ int i = 0;
+ uint64_t blob_size = 524288;
+ uint64_t total = 0;
+ for (i = 0; i <= 512; i++) {
+ offs = 0 + i * size;
+ ObjectStore::Transaction t;
+ ghobject_t hoid2 = hoid;
+ hoid2.hobj.snap = i + 1;
+ while (offs < 128 * 1024 * 1024) {
+
+ t.write(cid, hoid, offs, ap.length(), bl);
+ offs += blob_size;
+ total += ap.length();
+ }
+ t.clone(cid, hoid, hoid2);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ cerr << "Total written = " << total << std::endl;
+ }
+ {
+ cerr << "Finalizing" << std::endl;
+ const PerfCounters* logger = store->get_perf_counters();
+ ASSERT_GE(logger->get(l_bluestore_gc_merged), 1024*1024*1024);
+ }
+}
+
+void doManySetAttr(ObjectStore* store,
+ std::function<void(ObjectStore*)> do_check_fn)
+{
+ MixedGenerator gen(447);
+ gen_type rng(time(NULL));
+ coll_t cid(spg_t(pg_t(0, 447), shard_id_t::NO_SHARD));
+
+ SyntheticWorkloadState test_obj(store, &gen, &rng, cid, 0, 0, 0);
+ test_obj.init();
+ size_t object_count = 256;
+ for (size_t i = 0; i < object_count; ++i) {
+ if (!(i % 10)) cerr << "seeding object " << i << std::endl;
+ test_obj.touch();
+ }
+ for (size_t i = 0; i < object_count; ++i) {
+ if (!(i % 100)) {
+ cerr << "Op " << i << std::endl;
+ test_obj.print_internal_state();
+ }
+ test_obj.set_fixed_attrs(1024, 64, 4096); // 1024 attributes, 64 bytes name and 4K value
+ }
+ test_obj.wait_for_done();
+
+ std::cout << "done" << std::endl;
+ do_check_fn(store);
+ AdminSocket* admin_socket = g_ceph_context->get_admin_socket();
+ ceph_assert(admin_socket);
+
+ ceph::bufferlist in, out;
+ ostringstream err;
+
+ auto r = admin_socket->execute_command(
+ { "{\"prefix\": \"bluefs stats\"}" },
+ in, err, &out);
+ if (r != 0) {
+ cerr << "failure querying: " << cpp_strerror(r) << std::endl;
+ } else {
+ std::cout << std::string(out.c_str(), out.length()) << std::endl;
+ }
+ test_obj.shutdown();
+}
+
+TEST_P(StoreTestSpecificAUSize, SpilloverTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: (FIXME?) adjust me for smr at some point?" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+ SetVal(g_conf(), "bluestore_block_db_size", "3221225472");
+ SetVal(g_conf(), "bluestore_volume_selection_policy", "rocksdb_original");
+ // original RocksDB settings used before https://github.com/ceph/ceph/pull/47221/
+ // which enable BlueFS spillover.
+ SetVal(g_conf(), "bluestore_rocksdb_options",
+ "compression=kNoCompression,max_write_buffer_number=4,"
+ "min_write_buffer_number_to_merge=1,recycle_log_file_num=4,"
+ "write_buffer_size=268435456,writable_file_max_buffer_size=0,"
+ "compaction_readahead_size=2097152,max_background_compactions=2,"
+ "max_total_wal_size=1073741824");
+
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(65536);
+ doManySetAttr(store.get(),
+ [&](ObjectStore* _store) {
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (_store);
+ ceph_assert(bstore);
+ bstore->compact();
+ const PerfCounters* logger = bstore->get_bluefs_perf_counters();
+ //experimentally it was discovered that this case results in 400+MB spillover
+ //using lower 300MB threshold just to be safe enough
+ std::cout << "DB used:" << logger->get(l_bluefs_db_used_bytes) << std::endl;
+ std::cout << "SLOW used:" << logger->get(l_bluefs_slow_used_bytes) << std::endl;
+ ASSERT_GE(logger->get(l_bluefs_slow_used_bytes), 16 * 1024 * 1024);
+
+ struct store_statfs_t statfs;
+ osd_alert_list_t alerts;
+ int r = store->statfs(&statfs, &alerts);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(alerts.count("BLUEFS_SPILLOVER"), 1);
+ std::cout << "spillover_alert:" << alerts.find("BLUEFS_SPILLOVER")->second
+ << std::endl;
+ }
+ );
+}
+
+TEST_P(StoreTestSpecificAUSize, SpilloverFixedTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: (FIXME?) adjust me for smr at some point?" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+ SetVal(g_conf(), "bluestore_block_db_size", "3221225472");
+ SetVal(g_conf(), "bluestore_volume_selection_policy", "use_some_extra");
+ SetVal(g_conf(), "bluestore_volume_selection_reserved", "1"); // just use non-zero to enable
+
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(65536);
+ doManySetAttr(store.get(),
+ [&](ObjectStore* _store) {
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (_store);
+ ceph_assert(bstore);
+ bstore->compact();
+ const PerfCounters* logger = bstore->get_bluefs_perf_counters();
+ ASSERT_EQ(0, logger->get(l_bluefs_slow_used_bytes));
+ }
+ );
+}
+
+TEST_P(StoreTestSpecificAUSize, SpilloverFixed2Test) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: (FIXME?) adjust me for smr at some point?" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+ SetVal(g_conf(), "bluestore_block_db_size", "3221225472");
+ SetVal(g_conf(), "bluestore_volume_selection_policy", "use_some_extra");
+ //default 2.0 factor results in too high threshold, using less value
+ // that results in less but still present spillover.
+ SetVal(g_conf(), "bluestore_volume_selection_reserved_factor", "0.5");
+
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(65536);
+ doManySetAttr(store.get(),
+ [&](ObjectStore* _store) {
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (_store);
+ ceph_assert(bstore);
+ bstore->compact();
+ const PerfCounters* logger = bstore->get_bluefs_perf_counters();
+ ASSERT_LE(logger->get(l_bluefs_slow_used_bytes), 300 * 1024 * 1024); // see SpilloverTest for 300MB choice rationale
+ }
+ );
+}
+
+TEST_P(StoreTestSpecificAUSize, SpilloverFixed3Test) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ cout << "SKIP: (FIXME?) adjust me for smr at some point?" << std::endl;
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+ SetVal(g_conf(), "bluestore_block_db_size", "3221225472");
+ SetVal(g_conf(), "bluestore_volume_selection_policy", "fit_to_fast");
+
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(65536);
+ doManySetAttr(store.get(),
+ [&](ObjectStore* _store) {
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (_store);
+ ceph_assert(bstore);
+ bstore->compact();
+ const PerfCounters* logger = bstore->get_bluefs_perf_counters();
+ ASSERT_EQ(logger->get(l_bluefs_slow_used_bytes), 0); // reffering to SpilloverFixedTest
+ }
+ );
+}
+
+TEST_P(StoreTestSpecificAUSize, Ticket45195Repro) {
+ if (string(GetParam()) != "bluestore")
+ return;
+ if (smr) {
+ return;
+ }
+
+ SetVal(g_conf(), "bluestore_default_buffered_write", "true");
+ SetVal(g_conf(), "bluestore_max_blob_size", "65536");
+ SetVal(g_conf(), "bluestore_debug_enforce_settings", "hdd");
+ SetVal(g_conf(), "bluestore_fsck_on_mount", "false");
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(0x1000);
+
+ int r;
+ coll_t cid;
+ ghobject_t hoid(hobject_t(sobject_t("Object", CEPH_NOSNAP)));
+ auto ch = store->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ cerr << "Creating collection " << cid << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ size_t large_object_size = 1 * 1024 * 1024;
+ size_t expected_write_size = 0x8000;
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ t.set_alloc_hint(cid, hoid, large_object_size, expected_write_size,
+ CEPH_OSD_ALLOC_HINT_FLAG_SEQUENTIAL_READ |
+ CEPH_OSD_ALLOC_HINT_FLAG_APPEND_ONLY);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(0xc000, '0');
+ bl.append(s);
+ t.write(cid, hoid, 0xb000, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(0x10000, '1');
+ bl.append(s);
+ t.write(cid, hoid, 0x16000, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(0x4000, '1');
+ bl.append(s);
+ t.write(cid, hoid, 0x1b000, bl.length(), bl);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ bufferlist bl;
+ r = store->read(ch, hoid, 0xb000, 0xb000, bl);
+ ASSERT_EQ(r, 0xb000);
+
+ store->umount();
+ store->mount();
+
+ ch = store->open_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ bufferlist bl, orig;
+ string s(0xf000, '3');
+ bl.append(s);
+ t.write(cid, hoid, 0xf000, bl.length(), bl);
+ cerr << "write4" << std::endl;
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ r = store->read(ch, hoid, 0xb000, 0x10000, bl);
+ ASSERT_EQ(r, 0x10000);
+}
+
+TEST_P(StoreTestOmapUpgrade, WithOmapHeader) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_debug_legacy_omap", "true");
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred();
+ int64_t poolid = 11;
+ coll_t cid(spg_t(pg_t(1, poolid), shard_id_t::NO_SHARD));
+ ghobject_t hoid(hobject_t("tesomap", "", CEPH_NOSNAP, 0, poolid, ""));
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ map<string, bufferlist> attrs;
+ bufferlist expected_header;
+ expected_header.append("this is a header");
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ bufferlist header;
+ header.append(expected_header);
+ t.omap_setheader(cid, hoid, header);
+ map<string, bufferlist> start_set;
+ bufferlist bl;
+ bl.append(string("value"));
+ start_set.emplace(string("key1"), bl);
+ t.omap_setkeys(cid, hoid, start_set);
+ r = queue_transaction(store, ch, std::move(t));
+ }
+ {
+ map<string,bufferlist> res;
+ bufferlist h;
+ r = store->omap_get(ch, hoid, &h, &res);
+ ASSERT_EQ(r, 0);
+ ASSERT_TRUE(bl_eq(h, expected_header));
+ ASSERT_EQ(res.size(), 1);
+ ASSERT_EQ(res.begin()->first, "key1");
+ }
+ store->umount();
+ ASSERT_EQ(store->fsck(false), 0);
+ SetVal(g_conf(), "bluestore_debug_legacy_omap", "false");
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_omap", "true");
+ g_conf().apply_changes(nullptr);
+ ASSERT_EQ(store->fsck(false), 2);
+ ASSERT_EQ(store->quick_fix(), 0);
+ store->mount();
+ ch = store->open_collection(cid);
+ {
+ map<string,bufferlist> res;
+ bufferlist h;
+ r = store->omap_get(ch, hoid, &h, &res);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(res.size(), 1);
+ ASSERT_EQ(res.begin()->first, "key1");
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BluefsWriteInSingleDiskEnvTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(0x1000);
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+ ceph_assert(bstore);
+ bstore->inject_bluefs_file("db.slow", "store_test_injection_slow", 1 << 20ul);
+ bstore->inject_bluefs_file("db.wal", "store_test_injection_wal", 1 << 20ul);
+ bstore->inject_bluefs_file("db", "store_test_injection_wal", 1 << 20ul);
+
+ AdminSocket* admin_socket = g_ceph_context->get_admin_socket();
+ ceph_assert(admin_socket);
+
+ ceph::bufferlist in, out;
+ ostringstream err;
+ auto r = admin_socket->execute_command(
+ { "{\"prefix\": \"bluefs stats\"}" },
+ in, err, &out);
+ if (r != 0) {
+ cerr << "failure querying: " << cpp_strerror(r) << std::endl;
+ } else {
+ std::cout << std::string(out.c_str(), out.length()) << std::endl;
+ }
+}
+
+TEST_P(StoreTestSpecificAUSize, BluefsWriteInNoWalDiskEnvTest) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_block_db_path", "db");
+ SetVal(g_conf(), "bluestore_block_db_size", stringify(1ull << 31).c_str());
+ SetVal(g_conf(), "bluestore_block_db_create", "true");
+
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred(0x1000);
+
+ BlueStore* bstore = dynamic_cast<BlueStore*> (store.get());
+ ceph_assert(bstore);
+ bstore->inject_bluefs_file("db.slow", "store_test_injection_slow", 1 << 20ul);
+ bstore->inject_bluefs_file("db.wal", "store_test_injection_wal", 1 << 20ul);
+ bstore->inject_bluefs_file("db", "store_test_injection_wal", 1 << 20ul);
+
+ AdminSocket* admin_socket = g_ceph_context->get_admin_socket();
+ ceph_assert(admin_socket);
+
+ ceph::bufferlist in, out;
+ ostringstream err;
+ auto r = admin_socket->execute_command(
+ { "{\"prefix\": \"bluefs stats\"}" },
+ in, err, &out);
+ if (r != 0) {
+ cerr << "failure querying: " << cpp_strerror(r) << std::endl;
+ }
+ else {
+ std::cout << std::string(out.c_str(), out.length()) << std::endl;
+ }
+}
+
+TEST_P(StoreTestOmapUpgrade, NoOmapHeader) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_debug_legacy_omap", "true");
+ g_conf().apply_changes(nullptr);
+
+ StartDeferred();
+ int64_t poolid = 11;
+ coll_t cid(spg_t(pg_t(1, poolid), shard_id_t::NO_SHARD));
+ ghobject_t hoid(hobject_t("tesomap", "", CEPH_NOSNAP, 0, poolid, ""));
+ auto ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+
+ map<string, bufferlist> attrs;
+ {
+ ObjectStore::Transaction t;
+ t.touch(cid, hoid);
+ map<string, bufferlist> start_set;
+ bufferlist bl;
+ bl.append(string("value"));
+ start_set.emplace(string("key1"), bl);
+ t.omap_setkeys(cid, hoid, start_set);
+ r = queue_transaction(store, ch, std::move(t));
+ }
+ {
+ map<string,bufferlist> res;
+ bufferlist h;
+ r = store->omap_get(ch, hoid, &h, &res);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(h.length(), 0);
+ ASSERT_EQ(res.size(), 1);
+ ASSERT_EQ(res.begin()->first, "key1");
+ }
+ store->umount();
+ ASSERT_EQ(store->fsck(false), 0);
+ SetVal(g_conf(), "bluestore_debug_legacy_omap", "false");
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_omap", "true");
+ g_conf().apply_changes(nullptr);
+ ASSERT_EQ(store->fsck(false), 2);
+ ASSERT_EQ(store->quick_fix(), 0);
+ store->mount();
+ ch = store->open_collection(cid);
+ {
+ map<string,bufferlist> res;
+ bufferlist h;
+ r = store->omap_get(ch, hoid, &h, &res);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(res.size(), 1);
+ ASSERT_EQ(res.begin()->first, "key1");
+ }
+ {
+ ObjectStore::Transaction t;
+ t.remove(cid, hoid);
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+TEST_P(StoreTestOmapUpgrade, LargeLegacyToPG) {
+ if (string(GetParam()) != "bluestore")
+ return;
+
+ SetVal(g_conf(), "bluestore_debug_legacy_omap", "true");
+ g_conf().apply_changes(nullptr);
+
+ int64_t poolid;
+ coll_t cid;
+ ghobject_t hoid;
+ ObjectStore::CollectionHandle ch;
+ StartDeferred();
+ poolid = 11;
+ cid = coll_t(spg_t(pg_t(1, poolid), shard_id_t::NO_SHARD));
+ ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+ //ASSERT_EQ(false, g_conf().get_val<bool>("bluestore_debug_inject_upgrade_bug53062"));
+ map<string, bufferlist> attrs;
+ bufferlist expected_header;
+ expected_header.append("this is a header");
+
+ size_t object_count = 1000;
+ make_omap_data(object_count, poolid, cid);
+ //checking just written data
+ check_omap_data(object_count, poolid, cid);
+
+ store->umount();
+ ASSERT_EQ(store->fsck(false), 0);
+ SetVal(g_conf(), "bluestore_debug_legacy_omap", "false");
+ SetVal(g_conf(), "bluestore_fsck_error_on_no_per_pool_omap", "true");
+ g_conf().apply_changes(nullptr);
+ ASSERT_EQ(store->fsck(false), 1001);
+ ASSERT_EQ(store->quick_fix(), 0);
+ store->mount();
+ ch = store->open_collection(cid);
+
+ //checking quick_fix() data
+ check_omap_data(object_count, poolid, cid);
+
+ {
+ ObjectStore::Transaction t;
+ for (size_t o = 0; o < object_count; o++)
+ {
+ std::string oid = generate_monotonic_name(object_count, o, 3.71, 0.5);
+ ghobject_t hoid(hobject_t(oid, "", CEPH_NOSNAP, 0, poolid, ""));
+ t.remove(cid, hoid);
+ }
+ t.remove_collection(cid);
+ r = queue_transaction(store, ch, std::move(t));
+ ASSERT_EQ(r, 0);
+ }
+}
+
+#endif // WITH_BLUESTORE
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ for (auto& i : args) {
+ if (i == "--smr"s) {
+#if defined(HAVE_LIBZBD)
+ derr << "Adjusting tests for smr mode." << dendl;
+ smr = true;
+#else
+ derr << "smr mode selected, but support not compiled in" << dendl;
+ return 1;
+#endif
+ }
+ }
+
+ // make sure we can adjust any config settings
+ g_ceph_context->_conf._clear_safe_to_start_threads();
+
+ g_ceph_context->_conf.set_val_or_die("osd_journal_size", "400");
+ g_ceph_context->_conf.set_val_or_die("filestore_index_retry_probability", "0.5");
+ g_ceph_context->_conf.set_val_or_die("filestore_op_thread_timeout", "1000");
+ g_ceph_context->_conf.set_val_or_die("filestore_op_thread_suicide_timeout", "10000");
+ //g_ceph_context->_conf.set_val_or_die("filestore_fiemap", "true");
+ g_ceph_context->_conf.set_val_or_die("bluestore_fsck_on_mkfs", "false");
+ g_ceph_context->_conf.set_val_or_die("bluestore_fsck_on_mount", "false");
+ g_ceph_context->_conf.set_val_or_die("bluestore_fsck_on_umount", "false");
+ g_ceph_context->_conf.set_val_or_die("bluestore_debug_small_allocations", "4");
+ g_ceph_context->_conf.set_val_or_die("bluestore_debug_freelist", "true");
+ g_ceph_context->_conf.set_val_or_die("bluestore_clone_cow", "true");
+ g_ceph_context->_conf.set_val_or_die("bluestore_max_alloc_size", "196608");
+ // set small cache sizes so we see trimming during Synthetic tests
+ g_ceph_context->_conf.set_val_or_die("bluestore_cache_size_hdd", "4000000");
+ g_ceph_context->_conf.set_val_or_die("bluestore_cache_size_ssd", "4000000");
+ g_ceph_context->_conf.set_val_or_die(
+ "bluestore_debug_inject_allocation_from_file_failure", "0.66");
+
+ // very short *_max prealloc so that we fall back to async submits
+ g_ceph_context->_conf.set_val_or_die("bluestore_blobid_prealloc", "10");
+ g_ceph_context->_conf.set_val_or_die("bluestore_nid_prealloc", "10");
+ g_ceph_context->_conf.set_val_or_die("bluestore_debug_randomize_serial_transaction",
+ "10");
+
+ g_ceph_context->_conf.set_val_or_die("bdev_debug_aio", "true");
+
+ // specify device size
+ g_ceph_context->_conf.set_val_or_die("bluestore_block_size",
+ stringify(DEF_STORE_TEST_BLOCKDEV_SIZE));
+
+ g_ceph_context->_conf.set_val_or_die(
+ "enable_experimental_unrecoverable_data_corrupting_features", "*");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make ceph_test_objectstore &&
+ * ./ceph_test_objectstore \
+ * --gtest_filter=*.collect_metadata* --log-to-stderr=true --debug-filestore=20
+ * "
+ * End:
+ */
diff --git a/src/test/objectstore/store_test_fixture.cc b/src/test/objectstore/store_test_fixture.cc
new file mode 100644
index 000000000..a3bdc7a36
--- /dev/null
+++ b/src/test/objectstore/store_test_fixture.cc
@@ -0,0 +1,135 @@
+#include <stdlib.h>
+#include <string>
+#include <iostream>
+#include <assert.h>
+#include <gtest/gtest.h>
+
+#include "common/errno.h"
+#include "common/config.h"
+#include "os/ObjectStore.h"
+
+#if defined(WITH_BLUESTORE)
+#include "os/bluestore/BlueStore.h"
+#endif
+#include "store_test_fixture.h"
+
+using namespace std;
+
+static void rm_r(const string& path)
+{
+ string cmd = string("rm -r ") + path;
+ cout << "==> " << cmd << std::endl;
+ int r = ::system(cmd.c_str());
+ if (r) {
+ if (r == -1) {
+ r = errno;
+ cerr << "system() failed to fork() " << cpp_strerror(r)
+ << ", continuing anyway" << std::endl;
+ } else {
+ cerr << "failed with exit code " << r
+ << ", continuing anyway" << std::endl;
+ }
+ }
+}
+
+void StoreTestFixture::SetUp()
+{
+
+ int r = ::mkdir(data_dir.c_str(), 0777);
+ if (r < 0) {
+ r = -errno;
+ cerr << __func__ << ": unable to create " << data_dir << ": " << cpp_strerror(r) << std::endl;
+ }
+ ASSERT_EQ(0, r);
+
+ store = ObjectStore::create(g_ceph_context,
+ type,
+ data_dir,
+ "store_test_temp_journal");
+ if (!store) {
+ cerr << __func__ << ": objectstore type " << type << " doesn't exist yet!" << std::endl;
+ }
+ ASSERT_TRUE(store);
+#if defined(WITH_BLUESTORE)
+ if (type == "bluestore") {
+ BlueStore *s = static_cast<BlueStore*>(store.get());
+ // better test coverage!
+ s->set_cache_shards(5);
+ }
+#endif
+ ASSERT_EQ(0, store->mkfs());
+ ASSERT_EQ(0, store->mount());
+
+ // we keep this stuff 'unsafe' out of test case scope to be able to update ANY
+ // config settings. Hence setting it to 'safe' here to proceed with the test
+ // case
+ g_conf().set_safe_to_start_threads();
+}
+
+void StoreTestFixture::TearDown()
+{
+ if (store) {
+ int r = store->umount();
+ EXPECT_EQ(0, r);
+ rm_r(data_dir);
+ }
+ // we keep this stuff 'unsafe' out of test case scope to be able to update ANY
+ // config settings. Hence setting it to 'unsafe' here as test case is closing.
+ g_conf()._clear_safe_to_start_threads();
+ PopSettings(0);
+ if (!orig_death_test_style.empty()) {
+ ::testing::FLAGS_gtest_death_test_style = orig_death_test_style;
+ orig_death_test_style.clear();
+ }
+}
+
+void StoreTestFixture::SetVal(ConfigProxy& _conf, const char* key, const char* val)
+{
+ ceph_assert(!conf || conf == &_conf);
+ conf = &_conf;
+ std::string skey(key);
+ std::string prev_val;
+ conf->get_val(skey, &prev_val);
+ conf->set_val_or_die(key, val);
+ saved_settings.emplace(skey, prev_val);
+}
+
+void StoreTestFixture::PopSettings(size_t pos)
+{
+ if (conf) {
+ ceph_assert(pos == 0 || pos <= saved_settings.size()); // for sanity
+ while(pos < saved_settings.size())
+ {
+ auto& e = saved_settings.top();
+ conf->set_val_or_die(e.first, e.second);
+ saved_settings.pop();
+ }
+ conf->apply_changes(NULL);
+ }
+}
+
+void StoreTestFixture::CloseAndReopen() {
+ ceph_assert(store != nullptr);
+ g_conf()._clear_safe_to_start_threads();
+ int r = store->umount();
+ EXPECT_EQ(0, r);
+ ch.reset(nullptr);
+ store.reset(nullptr);
+ store = ObjectStore::create(g_ceph_context,
+ type,
+ data_dir,
+ "store_test_temp_journal");
+ if (!store) {
+ cerr << __func__ << ": objectstore type " << type << " failed to reopen!" << std::endl;
+ }
+ ASSERT_TRUE(store);
+#if defined(WITH_BLUESTORE)
+ if (type == "bluestore") {
+ BlueStore *s = static_cast<BlueStore*>(store.get());
+ // better test coverage!
+ s->set_cache_shards(5);
+ }
+#endif
+ ASSERT_EQ(0, store->mount());
+ g_conf().set_safe_to_start_threads();
+}
diff --git a/src/test/objectstore/store_test_fixture.h b/src/test/objectstore/store_test_fixture.h
new file mode 100644
index 000000000..3f25fd493
--- /dev/null
+++ b/src/test/objectstore/store_test_fixture.h
@@ -0,0 +1,52 @@
+#include <string>
+#include <stack>
+#include <memory>
+#include <gtest/gtest.h>
+#include "common/config_fwd.h"
+
+class ObjectStore;
+
+class StoreTestFixture : virtual public ::testing::Test {
+ const std::string type;
+ const std::string data_dir;
+
+ std::stack<std::pair<std::string, std::string>> saved_settings;
+ ConfigProxy* conf = nullptr;
+
+ std::string orig_death_test_style;
+
+public:
+ std::unique_ptr<ObjectStore> store;
+ ObjectStore::CollectionHandle ch;
+
+ explicit StoreTestFixture(const std::string& type)
+ : type(type), data_dir(type + ".test_temp_dir")
+ {}
+
+ void SetUp() override;
+ void TearDown() override;
+ void SetDeathTestStyle(const char* new_style) {
+ if (orig_death_test_style.empty()) {
+ orig_death_test_style = ::testing::FLAGS_gtest_death_test_style;
+ }
+ ::testing::FLAGS_gtest_death_test_style = new_style;
+ }
+
+ void SetVal(ConfigProxy& conf, const char* key, const char* val);
+ struct SettingsBookmark {
+ StoreTestFixture& s;
+ size_t pos;
+
+ SettingsBookmark(StoreTestFixture& _s, size_t p) : s(_s), pos(p)
+ {}
+
+ ~SettingsBookmark() {
+ s.PopSettings(pos);
+ }
+ };
+ SettingsBookmark BookmarkSettings() {
+ return SettingsBookmark(*this, saved_settings.size());
+ }
+ void PopSettings(size_t);
+ void CloseAndReopen();
+};
diff --git a/src/test/objectstore/test_bdev.cc b/src/test/objectstore/test_bdev.cc
new file mode 100755
index 000000000..628b586bc
--- /dev/null
+++ b/src/test/objectstore/test_bdev.cc
@@ -0,0 +1,111 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <gtest/gtest.h>
+#include "global/global_init.h"
+#include "global/global_context.h"
+#include "common/ceph_context.h"
+#include "common/ceph_argparse.h"
+#include "include/stringify.h"
+#include "common/errno.h"
+
+#include "blk/BlockDevice.h"
+
+using namespace std;
+
+class TempBdev {
+public:
+ TempBdev(uint64_t size)
+ : path{get_temp_bdev(size)}
+ {}
+ ~TempBdev() {
+ rm_temp_bdev(path);
+ }
+ const std::string path;
+private:
+ static string get_temp_bdev(uint64_t size)
+ {
+ static int n = 0;
+ string fn = "ceph_test_bluefs.tmp.block." + stringify(getpid())
+ + "." + stringify(++n);
+ int fd = ::open(fn.c_str(), O_CREAT|O_RDWR|O_TRUNC, 0644);
+ ceph_assert(fd >= 0);
+ int r = ::ftruncate(fd, size);
+ ceph_assert(r >= 0);
+ ::close(fd);
+ return fn;
+ }
+ static void rm_temp_bdev(string f)
+ {
+ ::unlink(f.c_str());
+ }
+};
+
+TEST(KernelDevice, Ticket45337) {
+ // Large (>=2 GB) writes are incomplete when bluefs_buffered_io = true
+
+ uint64_t size = 1048576ull * 8192;
+ TempBdev bdev{ size };
+
+ const bool buffered = true;
+
+ std::unique_ptr<BlockDevice> b(
+ BlockDevice::create(g_ceph_context, bdev.path, NULL, NULL,
+ [](void* handle, void* aio) {}, NULL));
+ bufferlist bl;
+ // writing a bit less than 4GB
+ for (auto i = 0; i < 4000; i++) {
+ string s(1048576, 'a' + (i % 28));
+ bl.append(s);
+ }
+ uint64_t magic_offs = bl.length();
+ string s(4086, 'z');
+ s += "0123456789";
+ bl.append(s);
+
+ {
+ int r = b->open(bdev.path);
+ if (r < 0) {
+ std::cerr << "open " << bdev.path << " failed" << std::endl;
+ return;
+ }
+ }
+ std::unique_ptr<IOContext> ioc(new IOContext(g_ceph_context, NULL));
+
+ auto r = b->aio_write(0, bl, ioc.get(), buffered);
+ ASSERT_EQ(r, 0);
+
+ if (ioc->has_pending_aios()) {
+ b->aio_submit(ioc.get());
+ ioc->aio_wait();
+ }
+
+ char outbuf[0x1000];
+ r = b->read_random(magic_offs, sizeof(outbuf), outbuf, buffered);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(memcmp(s.c_str(), outbuf, sizeof(outbuf)), 0);
+
+ b->close();
+}
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ map<string,string> defaults = {
+ { "debug_bdev", "1/20" }
+ };
+
+ auto cct = global_init(&defaults, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.set_val(
+ "enable_experimental_unrecoverable_data_corrupting_features",
+ "*");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/objectstore/test_bluefs.cc b/src/test/objectstore/test_bluefs.cc
new file mode 100644
index 000000000..4f77d8597
--- /dev/null
+++ b/src/test/objectstore/test_bluefs.cc
@@ -0,0 +1,1422 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <random>
+#include <thread>
+#include <stack>
+#include <gtest/gtest.h>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "include/stringify.h"
+#include "include/scope_guard.h"
+#include "common/errno.h"
+
+#include "os/bluestore/Allocator.h"
+#include "os/bluestore/BlueFS.h"
+
+using namespace std;
+
+std::unique_ptr<char[]> gen_buffer(uint64_t size)
+{
+ std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size);
+ std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned char> e;
+ std::generate(buffer.get(), buffer.get()+size, std::ref(e));
+ return buffer;
+}
+
+class TempBdev {
+public:
+ TempBdev(uint64_t size)
+ : path{get_temp_bdev(size)}
+ {}
+ ~TempBdev() {
+ rm_temp_bdev(path);
+ }
+ const std::string path;
+private:
+ static string get_temp_bdev(uint64_t size)
+ {
+ static int n = 0;
+ string fn = "ceph_test_bluefs.tmp.block." + stringify(getpid())
+ + "." + stringify(++n);
+ int fd = ::open(fn.c_str(), O_CREAT|O_RDWR|O_TRUNC, 0644);
+ ceph_assert(fd >= 0);
+ int r = ::ftruncate(fd, size);
+ ceph_assert(r >= 0);
+ ::close(fd);
+ return fn;
+ }
+ static void rm_temp_bdev(string f)
+ {
+ ::unlink(f.c_str());
+ }
+};
+
+class ConfSaver {
+ std::stack<std::pair<std::string, std::string>> saved_settings;
+ ConfigProxy& conf;
+public:
+ ConfSaver(ConfigProxy& conf) : conf(conf) {
+ conf._clear_safe_to_start_threads();
+ };
+ ~ConfSaver() {
+ conf._clear_safe_to_start_threads();
+ while(saved_settings.size() > 0) {
+ auto& e = saved_settings.top();
+ conf.set_val_or_die(e.first, e.second);
+ saved_settings.pop();
+ }
+ conf.set_safe_to_start_threads();
+ conf.apply_changes(nullptr);
+ }
+ void SetVal(const char* key, const char* val) {
+ std::string skey(key);
+ std::string prev_val;
+ conf.get_val(skey, &prev_val);
+ conf.set_val_or_die(skey, val);
+ saved_settings.emplace(skey, prev_val);
+ }
+ void ApplyChanges() {
+ conf.set_safe_to_start_threads();
+ conf.apply_changes(nullptr);
+ }
+};
+
+TEST(BlueFS, mkfs) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ uuid_d fsid;
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+}
+
+TEST(BlueFS, mkfs_mount) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(fs.get_total(BlueFS::BDEV_DB), size - 1048576);
+ ASSERT_LT(fs.get_free(BlueFS::BDEV_DB), size - 1048576);
+ fs.umount();
+}
+
+TEST(BlueFS, write_read) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.mkdir("dir"));
+ ASSERT_EQ(0, fs.open_for_write("dir", "file", &h, false));
+ h->append("foo", 3);
+ h->append("bar", 3);
+ h->append("baz", 3);
+ fs.fsync(h);
+ fs.close_writer(h);
+ }
+ {
+ BlueFS::FileReader *h;
+ ASSERT_EQ(0, fs.open_for_read("dir", "file", &h));
+ bufferlist bl;
+ ASSERT_EQ(9, fs.read(h, 0, 1024, &bl, NULL));
+ ASSERT_EQ(0, strncmp("foobarbaz", bl.c_str(), 9));
+ delete h;
+ }
+ fs.umount();
+}
+
+TEST(BlueFS, small_appends) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.mkdir("dir"));
+ ASSERT_EQ(0, fs.open_for_write("dir", "file", &h, false));
+ for (unsigned i = 0; i < 10000; ++i) {
+ h->append("abcdeabcdeabcdeabcdeabcdeabc", 23);
+ }
+ fs.fsync(h);
+ fs.close_writer(h);
+ }
+ {
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write("dir", "file_sync", &h, false));
+ for (unsigned i = 0; i < 1000; ++i) {
+ h->append("abcdeabcdeabcdeabcdeabcdeabc", 23);
+ ASSERT_EQ(0, fs.fsync(h));
+ }
+ fs.close_writer(h);
+ }
+ fs.umount();
+}
+
+TEST(BlueFS, very_large_write) {
+ // we'll write a ~5G file, so allocate more than that for the whole fs
+ uint64_t size = 1048576 * 1024 * 6ull;
+ TempBdev bdev{size};
+ BlueFS fs(g_ceph_context);
+
+ bool old = g_ceph_context->_conf.get_val<bool>("bluefs_buffered_io");
+ g_ceph_context->_conf.set_val("bluefs_buffered_io", "false");
+ uint64_t total_written = 0;
+
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ char buf[1048571]; // this is biggish, but intentionally not evenly aligned
+ for (unsigned i = 0; i < sizeof(buf); ++i) {
+ buf[i] = i;
+ }
+ {
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.mkdir("dir"));
+ ASSERT_EQ(0, fs.open_for_write("dir", "bigfile", &h, false));
+ for (unsigned i = 0; i < 3*1024*1048576ull / sizeof(buf); ++i) {
+ h->append(buf, sizeof(buf));
+ total_written += sizeof(buf);
+ }
+ fs.fsync(h);
+ for (unsigned i = 0; i < 2*1024*1048576ull / sizeof(buf); ++i) {
+ h->append(buf, sizeof(buf));
+ total_written += sizeof(buf);
+ }
+ fs.fsync(h);
+ fs.close_writer(h);
+ }
+ {
+ BlueFS::FileReader *h;
+ ASSERT_EQ(0, fs.open_for_read("dir", "bigfile", &h));
+ bufferlist bl;
+ ASSERT_EQ(h->file->fnode.size, total_written);
+ for (unsigned i = 0; i < 3*1024*1048576ull / sizeof(buf); ++i) {
+ bl.clear();
+ fs.read(h, i * sizeof(buf), sizeof(buf), &bl, NULL);
+ int r = memcmp(buf, bl.c_str(), sizeof(buf));
+ if (r) {
+ cerr << "read got mismatch at offset " << i*sizeof(buf) << " r " << r
+ << std::endl;
+ }
+ ASSERT_EQ(0, r);
+ }
+ for (unsigned i = 0; i < 2*1024*1048576ull / sizeof(buf); ++i) {
+ bl.clear();
+ fs.read(h, i * sizeof(buf), sizeof(buf), &bl, NULL);
+ int r = memcmp(buf, bl.c_str(), sizeof(buf));
+ if (r) {
+ cerr << "read got mismatch at offset " << i*sizeof(buf) << " r " << r
+ << std::endl;
+ }
+ ASSERT_EQ(0, r);
+ }
+ delete h;
+ ASSERT_EQ(0, fs.open_for_read("dir", "bigfile", &h));
+ ASSERT_EQ(h->file->fnode.size, total_written);
+ auto huge_buf = std::make_unique<char[]>(h->file->fnode.size);
+ auto l = h->file->fnode.size;
+ int64_t r = fs.read(h, 0, l, NULL, huge_buf.get());
+ ASSERT_EQ(r, l);
+ delete h;
+ }
+ fs.umount();
+
+ g_ceph_context->_conf.set_val("bluefs_buffered_io", stringify((int)old));
+}
+
+TEST(BlueFS, very_large_write2) {
+ // we'll write a ~5G file, so allocate more than that for the whole fs
+ uint64_t size_full = 1048576 * 1024 * 6ull;
+ uint64_t size = 1048576 * 1024 * 5ull;
+ TempBdev bdev{ size_full };
+ BlueFS fs(g_ceph_context);
+
+ bool old = g_ceph_context->_conf.get_val<bool>("bluefs_buffered_io");
+ g_ceph_context->_conf.set_val("bluefs_buffered_io", "false");
+ uint64_t total_written = 0;
+
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+
+ char fill_arr[1 << 20]; // 1M
+ for (size_t i = 0; i < sizeof(fill_arr); ++i) {
+ fill_arr[i] = (char)i;
+ }
+ std::unique_ptr<char[]> buf;
+ buf.reset(new char[size]);
+ for (size_t i = 0; i < size; i += sizeof(fill_arr)) {
+ memcpy(buf.get() + i, fill_arr, sizeof(fill_arr));
+ }
+ {
+ BlueFS::FileWriter* h;
+ ASSERT_EQ(0, fs.mkdir("dir"));
+ ASSERT_EQ(0, fs.open_for_write("dir", "bigfile", &h, false));
+ fs.append_try_flush(h, buf.get(), size);
+ total_written = size;
+ fs.fsync(h);
+ fs.close_writer(h);
+ }
+ memset(buf.get(), 0, size);
+ {
+ BlueFS::FileReader* h;
+ ASSERT_EQ(0, fs.open_for_read("dir", "bigfile", &h));
+ ASSERT_EQ(h->file->fnode.size, total_written);
+ auto l = h->file->fnode.size;
+ int64_t r = fs.read(h, 0, l, NULL, buf.get());
+ ASSERT_EQ(r, l);
+ for (size_t i = 0; i < size; i += sizeof(fill_arr)) {
+ ceph_assert(memcmp(buf.get() + i, fill_arr, sizeof(fill_arr)) == 0);
+ }
+ delete h;
+ }
+ fs.umount();
+
+ g_ceph_context->_conf.set_val("bluefs_buffered_io", stringify((int)old));
+}
+
+#define ALLOC_SIZE 4096
+
+void write_data(BlueFS &fs, uint64_t rationed_bytes)
+{
+ int j=0, r=0;
+ uint64_t written_bytes = 0;
+ rationed_bytes -= ALLOC_SIZE;
+ stringstream ss;
+ string dir = "dir.";
+ ss << std::this_thread::get_id();
+ dir.append(ss.str());
+ dir.append(".");
+ dir.append(to_string(j));
+ ASSERT_EQ(0, fs.mkdir(dir));
+ while (1) {
+ string file = "file.";
+ file.append(to_string(j));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(ALLOC_SIZE);
+ bufferptr bp = buffer::claim_char(ALLOC_SIZE, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ r = fs.fsync(h);
+ if (r < 0) {
+ break;
+ }
+ written_bytes += g_conf()->bluefs_alloc_size;
+ j++;
+ if ((rationed_bytes - written_bytes) <= g_conf()->bluefs_alloc_size) {
+ break;
+ }
+ }
+}
+
+void create_single_file(BlueFS &fs)
+{
+ BlueFS::FileWriter *h;
+ stringstream ss;
+ string dir = "dir.test";
+ ASSERT_EQ(0, fs.mkdir(dir));
+ string file = "testfile";
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(ALLOC_SIZE);
+ bufferptr bp = buffer::claim_char(ALLOC_SIZE, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ fs.fsync(h);
+ fs.close_writer(h);
+}
+
+void write_single_file(BlueFS &fs, uint64_t rationed_bytes)
+{
+ stringstream ss;
+ const string dir = "dir.test";
+ const string file = "testfile";
+ uint64_t written_bytes = 0;
+ rationed_bytes -= ALLOC_SIZE;
+ while (1) {
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(ALLOC_SIZE);
+ bufferptr bp = buffer::claim_char(ALLOC_SIZE, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ int r = fs.fsync(h);
+ if (r < 0) {
+ break;
+ }
+ written_bytes += g_conf()->bluefs_alloc_size;
+ if ((rationed_bytes - written_bytes) <= g_conf()->bluefs_alloc_size) {
+ break;
+ }
+ }
+}
+
+bool writes_done = false;
+
+void sync_fs(BlueFS &fs)
+{
+ while (1) {
+ if (writes_done == true)
+ break;
+ fs.sync_metadata(false);
+ sleep(1);
+ }
+}
+
+
+void do_join(std::thread& t)
+{
+ t.join();
+}
+
+void join_all(std::vector<std::thread>& v)
+{
+ std::for_each(v.begin(),v.end(),do_join);
+}
+
+#define NUM_WRITERS 3
+#define NUM_SYNC_THREADS 1
+
+#define NUM_SINGLE_FILE_WRITERS 1
+#define NUM_MULTIPLE_FILE_WRITERS 2
+
+TEST(BlueFS, test_flush_1) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ g_ceph_context->_conf.set_val(
+ "bluefs_alloc_size",
+ "65536");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ std::vector<std::thread> write_thread_multiple;
+ uint64_t effective_size = size - (32 * 1048576); // leaving the last 32 MB for log compaction
+ uint64_t per_thread_bytes = (effective_size/(NUM_MULTIPLE_FILE_WRITERS + NUM_SINGLE_FILE_WRITERS));
+ for (int i=0; i<NUM_MULTIPLE_FILE_WRITERS ; i++) {
+ write_thread_multiple.push_back(std::thread(write_data, std::ref(fs), per_thread_bytes));
+ }
+
+ create_single_file(fs);
+ std::vector<std::thread> write_thread_single;
+ for (int i=0; i<NUM_SINGLE_FILE_WRITERS; i++) {
+ write_thread_single.push_back(std::thread(write_single_file, std::ref(fs), per_thread_bytes));
+ }
+
+ join_all(write_thread_single);
+ join_all(write_thread_multiple);
+ }
+ fs.umount();
+}
+
+TEST(BlueFS, test_flush_2) {
+ uint64_t size = 1048576 * 256;
+ TempBdev bdev{size};
+ g_ceph_context->_conf.set_val(
+ "bluefs_alloc_size",
+ "65536");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ uint64_t effective_size = size - (128 * 1048576); // leaving the last 32 MB for log compaction
+ uint64_t per_thread_bytes = (effective_size/(NUM_WRITERS));
+ std::vector<std::thread> write_thread_multiple;
+ for (int i=0; i<NUM_WRITERS; i++) {
+ write_thread_multiple.push_back(std::thread(write_data, std::ref(fs), per_thread_bytes));
+ }
+
+ join_all(write_thread_multiple);
+ }
+ fs.umount();
+}
+
+TEST(BlueFS, test_flush_3) {
+ uint64_t size = 1048576 * 256;
+ TempBdev bdev{size};
+ g_ceph_context->_conf.set_val(
+ "bluefs_alloc_size",
+ "65536");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ std::vector<std::thread> write_threads;
+ uint64_t effective_size = size - (64 * 1048576); // leaving the last 11 MB for log compaction
+ uint64_t per_thread_bytes = (effective_size/(NUM_WRITERS));
+ for (int i=0; i<NUM_WRITERS; i++) {
+ write_threads.push_back(std::thread(write_data, std::ref(fs), per_thread_bytes));
+ }
+
+ std::vector<std::thread> sync_threads;
+ for (int i=0; i<NUM_SYNC_THREADS; i++) {
+ sync_threads.push_back(std::thread(sync_fs, std::ref(fs)));
+ }
+
+ join_all(write_threads);
+ writes_done = true;
+ join_all(sync_threads);
+ }
+ fs.umount();
+}
+
+TEST(BlueFS, test_simple_compaction_sync) {
+ g_ceph_context->_conf.set_val(
+ "bluefs_compact_log_sync",
+ "true");
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ for (int i=0; i<10; i++) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ ASSERT_EQ(0, fs.mkdir(dir));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(4096);
+ bufferptr bp = buffer::claim_char(4096, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ fs.fsync(h);
+ }
+ }
+ }
+ {
+ for (int i=0; i<10; i+=2) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ fs.unlink(dir, file);
+ fs.sync_metadata(false);
+ }
+ ASSERT_EQ(0, fs.rmdir(dir));
+ fs.sync_metadata(false);
+ }
+ }
+ fs.compact_log();
+ fs.umount();
+}
+
+TEST(BlueFS, test_simple_compaction_async) {
+ g_ceph_context->_conf.set_val(
+ "bluefs_compact_log_sync",
+ "false");
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ for (int i=0; i<10; i++) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ ASSERT_EQ(0, fs.mkdir(dir));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(4096);
+ bufferptr bp = buffer::claim_char(4096, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ fs.fsync(h);
+ }
+ }
+ }
+ {
+ for (int i=0; i<10; i+=2) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ fs.unlink(dir, file);
+ fs.sync_metadata(false);
+ }
+ ASSERT_EQ(0, fs.rmdir(dir));
+ fs.sync_metadata(false);
+ }
+ }
+ fs.compact_log();
+ fs.umount();
+}
+
+TEST(BlueFS, test_compaction_sync) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ g_ceph_context->_conf.set_val(
+ "bluefs_alloc_size",
+ "65536");
+ g_ceph_context->_conf.set_val(
+ "bluefs_compact_log_sync",
+ "true");
+ const char* canary_dir = "dir.after_compact_test";
+ const char* canary_file = "file.after_compact_test";
+ const char* canary_data = "some random data";
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ std::vector<std::thread> write_threads;
+ uint64_t effective_size = size - (32 * 1048576); // leaving the last 32 MB for log compaction
+ uint64_t per_thread_bytes = (effective_size/(NUM_WRITERS));
+ for (int i=0; i<NUM_WRITERS; i++) {
+ write_threads.push_back(std::thread(write_data, std::ref(fs), per_thread_bytes));
+ }
+
+ std::vector<std::thread> sync_threads;
+ for (int i=0; i<NUM_SYNC_THREADS; i++) {
+ sync_threads.push_back(std::thread(sync_fs, std::ref(fs)));
+ }
+
+ join_all(write_threads);
+ writes_done = true;
+ join_all(sync_threads);
+ fs.compact_log();
+
+ {
+ ASSERT_EQ(0, fs.mkdir(canary_dir));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(canary_dir, canary_file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ h->append(canary_data, strlen(canary_data));
+ int r = fs.fsync(h);
+ ASSERT_EQ(r, 0);
+ }
+ }
+ fs.umount();
+
+ fs.mount();
+ {
+ BlueFS::FileReader *h;
+ ASSERT_EQ(0, fs.open_for_read(canary_dir, canary_file, &h));
+ ASSERT_NE(nullptr, h);
+ bufferlist bl;
+ ASSERT_EQ(strlen(canary_data), fs.read(h, 0, 1024, &bl, NULL));
+ std::cout << bl.c_str() << std::endl;
+ ASSERT_EQ(0, strncmp(canary_data, bl.c_str(), strlen(canary_data)));
+ delete h;
+ }
+ fs.umount();
+}
+
+TEST(BlueFS, test_compaction_async) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ g_ceph_context->_conf.set_val(
+ "bluefs_alloc_size",
+ "65536");
+ g_ceph_context->_conf.set_val(
+ "bluefs_compact_log_sync",
+ "false");
+ const char* canary_dir = "dir.after_compact_test";
+ const char* canary_file = "file.after_compact_test";
+ const char* canary_data = "some random data";
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ std::vector<std::thread> write_threads;
+ uint64_t effective_size = size - (32 * 1048576); // leaving the last 32 MB for log compaction
+ uint64_t per_thread_bytes = (effective_size/(NUM_WRITERS));
+ for (int i=0; i<NUM_WRITERS; i++) {
+ write_threads.push_back(std::thread(write_data, std::ref(fs), per_thread_bytes));
+ }
+
+ std::vector<std::thread> sync_threads;
+ for (int i=0; i<NUM_SYNC_THREADS; i++) {
+ sync_threads.push_back(std::thread(sync_fs, std::ref(fs)));
+ }
+
+ join_all(write_threads);
+ writes_done = true;
+ join_all(sync_threads);
+ fs.compact_log();
+
+ {
+ ASSERT_EQ(0, fs.mkdir(canary_dir));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(canary_dir, canary_file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ h->append(canary_data, strlen(canary_data));
+ int r = fs.fsync(h);
+ ASSERT_EQ(r, 0);
+ }
+ }
+ fs.umount();
+
+ fs.mount();
+ {
+ BlueFS::FileReader *h;
+ ASSERT_EQ(0, fs.open_for_read(canary_dir, canary_file, &h));
+ ASSERT_NE(nullptr, h);
+ bufferlist bl;
+ ASSERT_EQ(strlen(canary_data), fs.read(h, 0, 1024, &bl, NULL));
+ std::cout << bl.c_str() << std::endl;
+ ASSERT_EQ(0, strncmp(canary_data, bl.c_str(), strlen(canary_data)));
+ delete h;
+ }
+ fs.umount();
+}
+
+TEST(BlueFS, test_replay) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ g_ceph_context->_conf.set_val(
+ "bluefs_alloc_size",
+ "65536");
+ g_ceph_context->_conf.set_val(
+ "bluefs_compact_log_sync",
+ "false");
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ std::vector<std::thread> write_threads;
+ uint64_t effective_size = size - (32 * 1048576); // leaving the last 32 MB for log compaction
+ uint64_t per_thread_bytes = (effective_size/(NUM_WRITERS));
+ for (int i=0; i<NUM_WRITERS; i++) {
+ write_threads.push_back(std::thread(write_data, std::ref(fs), per_thread_bytes));
+ }
+
+ std::vector<std::thread> sync_threads;
+ for (int i=0; i<NUM_SYNC_THREADS; i++) {
+ sync_threads.push_back(std::thread(sync_fs, std::ref(fs)));
+ }
+
+ join_all(write_threads);
+ writes_done = true;
+ join_all(sync_threads);
+ fs.compact_log();
+ }
+ fs.umount();
+ // remount and check log can replay safe?
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ fs.umount();
+}
+
+TEST(BlueFS, test_replay_growth) {
+ uint64_t size = 1048576LL * (2 * 1024 + 128);
+ TempBdev bdev{size};
+
+ ConfSaver conf(g_ceph_context->_conf);
+ conf.SetVal("bluefs_alloc_size", "4096");
+ conf.SetVal("bluefs_shared_alloc_size", "4096");
+ conf.SetVal("bluefs_compact_log_sync", "false");
+ conf.SetVal("bluefs_min_log_runway", "32768");
+ conf.SetVal("bluefs_max_log_runway", "65536");
+ conf.SetVal("bluefs_allocator", "stupid");
+ conf.SetVal("bluefs_sync_write", "true");
+ conf.ApplyChanges();
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mkdir("dir"));
+
+ char data[2000];
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write("dir", "file", &h, false));
+ for (size_t i = 0; i < 10000; i++) {
+ h->append(data, 2000);
+ fs.fsync(h);
+ }
+ fs.close_writer(h);
+ fs.umount(true); //do not compact on exit!
+
+ // remount and check log can replay safe?
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ fs.umount();
+}
+
+TEST(BlueFS, test_tracker_50965) {
+ uint64_t size_wal = 1048576 * 64;
+ TempBdev bdev_wal{size_wal};
+ uint64_t size_db = 1048576 * 128;
+ TempBdev bdev_db{size_db};
+ uint64_t size_slow = 1048576 * 256;
+ TempBdev bdev_slow{size_slow};
+
+ ConfSaver conf(g_ceph_context->_conf);
+ conf.SetVal("bluefs_min_flush_size", "65536");
+ conf.ApplyChanges();
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_WAL, bdev_wal.path, false, 0));
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev_db.path, false, 0));
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_SLOW, bdev_slow.path, false, 0));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, true, true }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, true, true }));
+
+ string dir_slow = "dir.slow";
+ ASSERT_EQ(0, fs.mkdir(dir_slow));
+ string dir_db = "dir_db";
+ ASSERT_EQ(0, fs.mkdir(dir_db));
+
+ string file_slow = "file";
+ BlueFS::FileWriter *h_slow;
+ ASSERT_EQ(0, fs.open_for_write(dir_slow, file_slow, &h_slow, false));
+ ASSERT_NE(nullptr, h_slow);
+
+ string file_db = "file";
+ BlueFS::FileWriter *h_db;
+ ASSERT_EQ(0, fs.open_for_write(dir_db, file_db, &h_db, false));
+ ASSERT_NE(nullptr, h_db);
+
+ bufferlist bl1;
+ std::unique_ptr<char[]> buf1 = gen_buffer(70000);
+ bufferptr bp1 = buffer::claim_char(70000, buf1.get());
+ bl1.push_back(bp1);
+ h_slow->append(bl1.c_str(), bl1.length());
+ fs.flush(h_slow);
+
+ uint64_t h_slow_dirty_seq_1 = fs.debug_get_dirty_seq(h_slow);
+
+ bufferlist bl2;
+ std::unique_ptr<char[]> buf2 = gen_buffer(1000);
+ bufferptr bp2 = buffer::claim_char(1000, buf2.get());
+ bl2.push_back(bp2);
+ h_db->append(bl2.c_str(), bl2.length());
+ fs.fsync(h_db);
+
+ uint64_t h_slow_dirty_seq_2 = fs.debug_get_dirty_seq(h_slow);
+ bool h_slow_dev_dirty = fs.debug_get_is_dev_dirty(h_slow, BlueFS::BDEV_SLOW);
+
+ //problem if allocations are stable in log but slow device is not flushed yet
+ ASSERT_FALSE(h_slow_dirty_seq_1 != 0 &&
+ h_slow_dirty_seq_2 == 0 &&
+ h_slow_dev_dirty == true);
+
+ fs.close_writer(h_slow);
+ fs.close_writer(h_db);
+
+ fs.umount();
+}
+
+TEST(BlueFS, test_truncate_stable_53129) {
+
+ ConfSaver conf(g_ceph_context->_conf);
+ conf.SetVal("bluefs_min_flush_size", "65536");
+ conf.ApplyChanges();
+
+ uint64_t size_wal = 1048576 * 64;
+ TempBdev bdev_wal{size_wal};
+ uint64_t size_db = 1048576 * 128;
+ TempBdev bdev_db{size_db};
+ uint64_t size_slow = 1048576 * 256;
+ TempBdev bdev_slow{size_slow};
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_WAL, bdev_wal.path, false, 0));
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev_db.path, false, 0));
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_SLOW, bdev_slow.path, false, 0));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, true, true }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, true, true }));
+
+ string dir_slow = "dir.slow";
+ ASSERT_EQ(0, fs.mkdir(dir_slow));
+ string dir_db = "dir_db";
+ ASSERT_EQ(0, fs.mkdir(dir_db));
+
+ string file_slow = "file";
+ BlueFS::FileWriter *h_slow;
+ ASSERT_EQ(0, fs.open_for_write(dir_slow, file_slow, &h_slow, false));
+ ASSERT_NE(nullptr, h_slow);
+
+ string file_db = "file";
+ BlueFS::FileWriter *h_db;
+ ASSERT_EQ(0, fs.open_for_write(dir_db, file_db, &h_db, false));
+ ASSERT_NE(nullptr, h_db);
+
+ bufferlist bl1;
+ std::unique_ptr<char[]> buf1 = gen_buffer(70000);
+ bufferptr bp1 = buffer::claim_char(70000, buf1.get());
+ bl1.push_back(bp1);
+ // add 70000 bytes
+ h_slow->append(bl1.c_str(), bl1.length());
+ fs.flush(h_slow);
+ // and truncate to 60000 bytes
+ fs.truncate(h_slow, 60000);
+
+ // write something to file on DB device
+ bufferlist bl2;
+ std::unique_ptr<char[]> buf2 = gen_buffer(1000);
+ bufferptr bp2 = buffer::claim_char(1000, buf2.get());
+ bl2.push_back(bp2);
+ h_db->append(bl2.c_str(), bl2.length());
+ // and force bluefs log to flush
+ fs.fsync(h_db);
+
+ // This is the actual test point.
+ // We completed truncate, and we expect
+ // - size to be 60000
+ // - data to be stable on slow device
+ // OR
+ // - size = 0 or file does not exist
+ // - dev_dirty is irrelevant
+ bool h_slow_dev_dirty = fs.debug_get_is_dev_dirty(h_slow, BlueFS::BDEV_SLOW);
+ // Imagine power goes down here.
+
+ fs.close_writer(h_slow);
+ fs.close_writer(h_db);
+
+ fs.umount();
+
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, true, true }));
+
+ uint64_t size;
+ utime_t mtime;
+ ASSERT_EQ(0, fs.stat("dir.slow", "file", &size, &mtime));
+ // check file size 60000
+ ASSERT_EQ(size, 60000);
+ // check that dev_dirty was false (data stable on media)
+ ASSERT_EQ(h_slow_dev_dirty, false);
+
+ fs.umount();
+}
+
+TEST(BlueFS, test_update_ino1_delta_after_replay) {
+ uint64_t size = 1048576LL * (2 * 1024 + 128);
+ TempBdev bdev{size};
+
+ ConfSaver conf(g_ceph_context->_conf);
+ conf.SetVal("bluefs_alloc_size", "4096");
+ conf.SetVal("bluefs_shared_alloc_size", "4096");
+ conf.SetVal("bluefs_compact_log_sync", "false");
+ conf.SetVal("bluefs_min_log_runway", "32768");
+ conf.SetVal("bluefs_max_log_runway", "65536");
+ conf.SetVal("bluefs_allocator", "stupid");
+ conf.ApplyChanges();
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mkdir("dir"));
+
+ char data[2000];
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write("dir", "file", &h, false));
+ for (size_t i = 0; i < 100; i++) {
+ h->append(data, 2000);
+ fs.fsync(h);
+ }
+ fs.close_writer(h);
+ fs.umount(true); //do not compact on exit!
+
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.open_for_write("dir", "file2", &h, false));
+ for (size_t i = 0; i < 100; i++) {
+ h->append(data, 2000);
+ fs.fsync(h);
+ }
+ fs.close_writer(h);
+ fs.umount();
+
+ // remount and check log can replay safe?
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ fs.umount();
+}
+
+TEST(BlueFS, broken_unlink_fsync_seq) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ /*
+ * This reproduces a weird file op sequence (unlink+fsync) that Octopus
+ * RocksDB might issue to BlueFS when recycle_log_file_num setting is 0
+ * See https://tracker.ceph.com/issues/55636 for more details
+ *
+ */
+ char buf[1048571]; // this is biggish, but intentionally not evenly aligned
+ for (unsigned i = 0; i < sizeof(buf); ++i) {
+ buf[i] = i;
+ }
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.mkdir("dir"));
+ ASSERT_EQ(0, fs.open_for_write("dir", "file", &h, false));
+
+ h->append(buf, sizeof(buf));
+ fs.flush(h);
+ h->append(buf, sizeof(buf));
+ fs.unlink("dir", "file");
+ fs.fsync(h);
+ fs.close_writer(h);
+ }
+ fs.umount();
+
+ // remount and check log can replay safe?
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ fs.umount();
+}
+
+TEST(BlueFS, truncate_fsync) {
+ uint64_t bdev_size = 128 * 1048576;
+ uint64_t block_size = 4096;
+ uint64_t reserved = 1048576;
+ TempBdev bdev{bdev_size};
+ uuid_d fsid;
+ const char* DIR_NAME="dir";
+ const char* FILE_NAME="file1";
+
+ size_t sizes[] = {3, 1024, 4096, 1024 * 4096};
+ for (size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
+ const size_t content_size= sizes[i];
+ const size_t read_size = p2roundup(content_size, size_t(block_size));
+ const std::string content(content_size, 'x');
+ {
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, reserved));
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.mkdir("dir"));
+ ASSERT_EQ(0, fs.open_for_write(DIR_NAME, FILE_NAME, &h, false));
+ h->append(content.c_str(), content.length());
+ fs.fsync(h);
+ fs.close_writer(h);
+ }
+ {
+ BlueFS::FileReader *h;
+ ASSERT_EQ(0, fs.open_for_read(DIR_NAME, FILE_NAME, &h));
+ bufferlist bl;
+ ASSERT_EQ(content.length(), fs.read(h, 0, read_size, &bl, NULL));
+ ASSERT_EQ(0, strncmp(content.c_str(), bl.c_str(), content.length()));
+ delete h;
+ }
+ {
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(DIR_NAME, FILE_NAME, &h, true));
+ fs.truncate(h, 0);
+ fs.fsync(h);
+ fs.close_writer(h);
+ }
+ }
+ {
+ //this was broken due to https://tracker.ceph.com/issues/55307
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, reserved));
+ ASSERT_EQ(0, fs.mount());
+ BlueFS::FileReader *h;
+ ASSERT_EQ(0, fs.open_for_read(DIR_NAME, FILE_NAME, &h));
+ bufferlist bl;
+ ASSERT_EQ(0, fs.read(h, 0, read_size, &bl, NULL));
+ delete h;
+ fs.umount();
+ }
+ }
+}
+
+TEST(BlueFS, test_shared_alloc) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev_slow{size};
+ uint64_t size_db = 1048576 * 8;
+ TempBdev bdev_db{size_db};
+
+ ConfSaver conf(g_ceph_context->_conf);
+ conf.SetVal("bluefs_shared_alloc_size", "1048576");
+
+ bluefs_shared_alloc_context_t shared_alloc;
+ uint64_t shared_alloc_unit = 4096;
+ shared_alloc.set(
+ Allocator::create(g_ceph_context, g_ceph_context->_conf->bluefs_allocator,
+ size, shared_alloc_unit, 0, 0, "test shared allocator"),
+ shared_alloc_unit);
+ shared_alloc.a->init_add_free(0, size);
+
+ BlueFS fs(g_ceph_context);
+ // DB device is fully utilized
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev_db.path, false, size_db - 0x1000));
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_SLOW, bdev_slow.path, false, 0,
+ &shared_alloc));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ for (int i=0; i<10; i++) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ ASSERT_EQ(0, fs.mkdir(dir));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(4096);
+ bufferptr bp = buffer::claim_char(4096, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ fs.fsync(h);
+ }
+ }
+ }
+ {
+ for (int i=0; i<10; i+=2) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ fs.unlink(dir, file);
+ fs.sync_metadata(false);
+ }
+ ASSERT_EQ(0, fs.rmdir(dir));
+ fs.sync_metadata(false);
+ }
+ }
+ fs.compact_log();
+ auto *logger = fs.get_perf_counters();
+ ASSERT_NE(logger->get(l_bluefs_alloc_shared_dev_fallbacks), 0);
+ auto num_files = logger->get(l_bluefs_num_files);
+ fs.umount();
+ fs.mount();
+ ASSERT_EQ(num_files, logger->get(l_bluefs_num_files));
+ fs.umount();
+}
+
+TEST(BlueFS, test_shared_alloc_sparse) {
+ uint64_t size = 1048576 * 128 * 2;
+ uint64_t main_unit = 4096;
+ uint64_t bluefs_alloc_unit = 1048576;
+ TempBdev bdev_slow{size};
+
+ ConfSaver conf(g_ceph_context->_conf);
+ conf.SetVal("bluefs_shared_alloc_size",
+ stringify(bluefs_alloc_unit).c_str());
+
+ bluefs_shared_alloc_context_t shared_alloc;
+ shared_alloc.set(
+ Allocator::create(g_ceph_context, g_ceph_context->_conf->bluefs_allocator,
+ size, main_unit, 0, 0, "test shared allocator"),
+ main_unit);
+ // prepare sparse free space but let's have a continuous chunk at
+ // the beginning to fit initial log's fnode into superblock,
+ // we don't have any tricks to deal with sparse allocations
+ // (and hence long fnode) at mkfs
+ shared_alloc.a->init_add_free(bluefs_alloc_unit, 4 * bluefs_alloc_unit);
+ for(uint64_t i = 5 * bluefs_alloc_unit; i < size; i += 2 * main_unit) {
+ shared_alloc.a->init_add_free(i, main_unit);
+ }
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev_slow.path, false, 0,
+ &shared_alloc));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ for (int i=0; i<10; i++) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ ASSERT_EQ(0, fs.mkdir(dir));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(4096);
+ bufferptr bp = buffer::claim_char(4096, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ fs.fsync(h);
+ }
+ }
+ }
+ {
+ for (int i=0; i<10; i+=2) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ fs.unlink(dir, file);
+ fs.sync_metadata(false);
+ }
+ ASSERT_EQ(0, fs.rmdir(dir));
+ fs.sync_metadata(false);
+ }
+ }
+ fs.compact_log();
+ auto *logger = fs.get_perf_counters();
+ ASSERT_NE(logger->get(l_bluefs_alloc_shared_size_fallbacks), 0);
+ auto num_files = logger->get(l_bluefs_num_files);
+ fs.umount();
+
+ fs.mount();
+ ASSERT_EQ(num_files, logger->get(l_bluefs_num_files));
+ fs.umount();
+}
+
+TEST(BlueFS, test_4k_shared_alloc) {
+ uint64_t size = 1048576 * 128 * 2;
+ uint64_t main_unit = 4096;
+ uint64_t bluefs_alloc_unit = main_unit;
+ TempBdev bdev_slow{size};
+
+ ConfSaver conf(g_ceph_context->_conf);
+ conf.SetVal("bluefs_shared_alloc_size",
+ stringify(bluefs_alloc_unit).c_str());
+
+ bluefs_shared_alloc_context_t shared_alloc;
+ shared_alloc.set(
+ Allocator::create(g_ceph_context, g_ceph_context->_conf->bluefs_allocator,
+ size, main_unit, 0, 0, "test shared allocator"),
+ main_unit);
+ shared_alloc.a->init_add_free(bluefs_alloc_unit, size - bluefs_alloc_unit);
+
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev_slow.path, false, 0,
+ &shared_alloc));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ for (int i=0; i<10; i++) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ ASSERT_EQ(0, fs.mkdir(dir));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ auto sg = make_scope_guard([&fs, h] { fs.close_writer(h); });
+ bufferlist bl;
+ std::unique_ptr<char[]> buf = gen_buffer(4096);
+ bufferptr bp = buffer::claim_char(4096, buf.get());
+ bl.push_back(bp);
+ h->append(bl.c_str(), bl.length());
+ fs.fsync(h);
+ }
+ }
+ }
+ {
+ for (int i=0; i<10; i+=2) {
+ string dir = "dir.";
+ dir.append(to_string(i));
+ for (int j=0; j<10; j++) {
+ string file = "file.";
+ file.append(to_string(j));
+ fs.unlink(dir, file);
+ fs.sync_metadata(false);
+ }
+ ASSERT_EQ(0, fs.rmdir(dir));
+ fs.sync_metadata(false);
+ }
+ }
+ fs.compact_log();
+ auto *logger = fs.get_perf_counters();
+ ASSERT_EQ(logger->get(l_bluefs_alloc_shared_dev_fallbacks), 0);
+ ASSERT_EQ(logger->get(l_bluefs_alloc_shared_size_fallbacks), 0);
+ auto num_files = logger->get(l_bluefs_num_files);
+ fs.umount();
+
+ fs.mount();
+ ASSERT_EQ(num_files, logger->get(l_bluefs_num_files));
+ fs.umount();
+}
+
+void create_files(BlueFS &fs,
+ atomic_bool& stop_creating,
+ atomic_bool& started_creating)
+{
+ uint32_t i = 0;
+ stringstream ss;
+ string dir = "dir.";
+ ss << std::this_thread::get_id();
+ dir.append(ss.str());
+ dir.append(".");
+ dir.append(to_string(i));
+ ASSERT_EQ(0, fs.mkdir(dir));
+ while (!stop_creating.load()) {
+ string file = "file.";
+ file.append(to_string(i));
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.open_for_write(dir, file, &h, false));
+ ASSERT_NE(nullptr, h);
+ fs.close_writer(h);
+ i++;
+ started_creating = true;
+ }
+}
+
+
+TEST(BlueFS, test_concurrent_dir_link_and_compact_log_56210) {
+ uint64_t size = 1048576 * 128;
+ TempBdev bdev{size};
+ ConfSaver conf(g_ceph_context->_conf);
+
+ conf.SetVal("bluefs_alloc_size", "65536");
+ conf.SetVal("bluefs_compact_log_sync", "false");
+ // make sure fsync always trigger log compact
+ conf.SetVal("bluefs_log_compact_min_ratio", "0");
+ conf.SetVal("bluefs_log_compact_min_size", "0");
+ conf.ApplyChanges();
+
+ for (int i=0; i<10; ++i) {
+ BlueFS fs(g_ceph_context);
+ ASSERT_EQ(0, fs.add_block_device(BlueFS::BDEV_DB, bdev.path, false, 1048576));
+ uuid_d fsid;
+ ASSERT_EQ(0, fs.mkfs(fsid, { BlueFS::BDEV_DB, false, false }));
+ ASSERT_EQ(0, fs.mount());
+ ASSERT_EQ(0, fs.maybe_verify_layout({ BlueFS::BDEV_DB, false, false }));
+ {
+ atomic_bool stop_creating{false};
+ atomic_bool started_creating{false};
+ std::thread create_thread;
+ create_thread = std::thread(create_files,
+ std::ref(fs),
+ std::ref(stop_creating),
+ std::ref(started_creating));
+ while (!started_creating.load()) {
+ }
+ BlueFS::FileWriter *h;
+ ASSERT_EQ(0, fs.mkdir("foo"));
+ ASSERT_EQ(0, fs.open_for_write("foo", "bar", &h, false));
+ fs.fsync(h);
+ fs.close_writer(h);
+
+ stop_creating = true;
+ do_join(create_thread);
+
+ fs.umount(true); //do not compact on exit!
+ ASSERT_EQ(0, fs.mount());
+ fs.umount();
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ map<string,string> defaults = {
+ { "debug_bluefs", "1/20" },
+ { "debug_bdev", "1/20" }
+ };
+
+ auto cct = global_init(&defaults, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.set_val(
+ "enable_experimental_unrecoverable_data_corrupting_features",
+ "*");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/objectstore/test_bluestore_types.cc b/src/test/objectstore/test_bluestore_types.cc
new file mode 100644
index 000000000..18ccaff91
--- /dev/null
+++ b/src/test/objectstore/test_bluestore_types.cc
@@ -0,0 +1,2346 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "os/bluestore/bluestore_types.h"
+#include "gtest/gtest.h"
+#include "include/stringify.h"
+#include "common/ceph_time.h"
+#include "os/bluestore/BlueStore.h"
+#include "os/bluestore/simple_bitmap.h"
+#include "os/bluestore/AvlAllocator.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+#include "perfglue/heap_profiler.h"
+
+#include <sstream>
+
+#define _STR(x) #x
+#define STRINGIFY(x) _STR(x)
+
+using namespace std;
+
+TEST(bluestore, sizeof) {
+#define P(t) cout << STRINGIFY(t) << "\t" << sizeof(t) << std::endl
+ P(BlueStore::Onode);
+ P(BlueStore::Extent);
+ P(BlueStore::Blob);
+ P(BlueStore::SharedBlob);
+ P(BlueStore::ExtentMap);
+ P(BlueStore::extent_map_t);
+ P(BlueStore::blob_map_t);
+ P(BlueStore::BufferSpace);
+ P(BlueStore::Buffer);
+ P(bluestore_onode_t);
+ P(bluestore_blob_t);
+ P(PExtentVector);
+ P(ghobject_t);
+ P(bluestore_shared_blob_t);
+ P(bluestore_extent_ref_map_t);
+ P(bluestore_extent_ref_map_t::record_t);
+ P(bluestore_blob_use_tracker_t);
+ P(std::atomic_int);
+ P(BlueStore::SharedBlobRef);
+ P(boost::intrusive::set_base_hook<>);
+ P(boost::intrusive::unordered_set_base_hook<>);
+ P(bufferlist);
+ P(bufferptr);
+ P(range_seg_t);
+ P(sb_info_t);
+ P(SimpleBitmap);
+ cout << "map<uint64_t,uint64_t>\t" << sizeof(map<uint64_t,uint64_t>) << std::endl;
+ cout << "map<char,char>\t" << sizeof(map<char,char>) << std::endl;
+}
+
+void dump_mempools()
+{
+ ostringstream ostr;
+ auto f = Formatter::create_unique("json-pretty", "json-pretty", "json-pretty");
+ ostr << "Mempools: ";
+ f->open_object_section("mempools");
+ mempool::dump(f.get());
+ f->close_section();
+ f->flush(ostr);
+ cout << ostr.str() << std::endl;
+}
+/*void get_mempool_stats(uint64_t* total_bytes, uint64_t* total_items)
+{
+ uint64_t meta_allocated = mempool::bluestore_cache_meta::allocated_bytes();
+ uint64_t onode_allocated = mempool::bluestore_cache_onode::allocated_bytes();
+ uint64_t other_allocated = mempool::bluestore_cache_other::allocated_bytes();
+
+ uint64_t meta_items = mempool::bluestore_cache_meta::allocated_items();
+ uint64_t onode_items = mempool::bluestore_cache_onode::allocated_items();
+ uint64_t other_items = mempool::bluestore_cache_other::allocated_items();
+ cout << "meta(" << meta_allocated << "/" << meta_items
+ << ") onode(" << onode_allocated << "/" << onode_items
+ << ") other(" << other_allocated << "/" << other_items
+ << ")" << std::endl;
+ *total_bytes = meta_allocated + onode_allocated + other_allocated;
+ *total_items = onode_items;
+}*/
+
+TEST(sb_info_space_efficient_map_t, basic) {
+ sb_info_space_efficient_map_t sb_info;
+ const size_t num_shared = 1000;
+ for (size_t i = 0; i < num_shared; i += 2) {
+ auto& sbi = sb_info.add_maybe_stray(i);
+ sbi.pool_id = i;
+ }
+ ASSERT_TRUE(sb_info.find(0) != sb_info.end());
+ ASSERT_TRUE(sb_info.find(1) == sb_info.end());
+ ASSERT_TRUE(sb_info.find(2) != sb_info.end());
+ ASSERT_TRUE(sb_info.find(4)->pool_id == 4);
+ ASSERT_TRUE(sb_info.find(num_shared) == sb_info.end());
+
+ // ordered insertion
+ sb_info.add_or_adopt(num_shared).pool_id = num_shared;
+ ASSERT_TRUE(sb_info.find(num_shared) != sb_info.end());
+ ASSERT_TRUE(sb_info.find(num_shared)->pool_id == num_shared);
+
+ // out of order insertion
+ sb_info.add_or_adopt(1).pool_id = 1;
+ ASSERT_TRUE(sb_info.find(1) != sb_info.end());
+ ASSERT_TRUE(sb_info.find(1)->pool_id == 1);
+
+ // ordered insertion
+ sb_info.add_maybe_stray(num_shared + 1).pool_id = num_shared + 1;
+ ASSERT_TRUE(sb_info.find(num_shared + 1) != sb_info.end());
+ ASSERT_TRUE(sb_info.find(num_shared + 1)->pool_id == num_shared + 1);
+
+ // out of order insertion
+ sb_info.add_maybe_stray(105).pool_id = 105;
+ ASSERT_TRUE(sb_info.find(105) != sb_info.end());
+ ASSERT_TRUE(sb_info.find(105)->pool_id == 105);
+}
+
+TEST(sb_info_space_efficient_map_t, size) {
+ const size_t num_shared = 10000000;
+ sb_info_space_efficient_map_t sb_info;
+
+ BlueStore store(g_ceph_context, "", 4096);
+ BlueStore::OnodeCacheShard* oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard* bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+
+ for (size_t i = 0; i < num_shared; i++) {
+ auto& sbi = sb_info.add_or_adopt(i);
+ // primarily to silent the 'unused' warning
+ ceph_assert(sbi.pool_id == sb_info_t::INVALID_POOL_ID);
+ }
+ dump_mempools();
+}
+
+TEST(bluestore_extent_ref_map_t, add)
+{
+ bluestore_extent_ref_map_t m;
+ m.get(10, 10);
+ ASSERT_EQ(1u, m.ref_map.size());
+ cout << m << std::endl;
+ m.get(20, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(1u, m.ref_map.size());
+ ASSERT_EQ(20u, m.ref_map[10].length);
+ ASSERT_EQ(1u, m.ref_map[10].refs);
+ m.get(40, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(2u, m.ref_map.size());
+ m.get(30, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(1u, m.ref_map.size());
+ m.get(50, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(1u, m.ref_map.size());
+ m.get(5, 5);
+ cout << m << std::endl;
+ ASSERT_EQ(1u, m.ref_map.size());
+}
+
+TEST(bluestore_extent_ref_map_t, get)
+{
+ bluestore_extent_ref_map_t m;
+ m.get(00, 30);
+ cout << m << std::endl;
+ m.get(10, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(3u, m.ref_map.size());
+ ASSERT_EQ(10u, m.ref_map[0].length);
+ ASSERT_EQ(1u, m.ref_map[0].refs);
+ ASSERT_EQ(10u, m.ref_map[10].length);
+ ASSERT_EQ(2u, m.ref_map[10].refs);
+ ASSERT_EQ(10u, m.ref_map[20].length);
+ ASSERT_EQ(1u, m.ref_map[20].refs);
+ m.get(20, 5);
+ cout << m << std::endl;
+ ASSERT_EQ(3u, m.ref_map.size());
+ ASSERT_EQ(15u, m.ref_map[10].length);
+ ASSERT_EQ(2u, m.ref_map[10].refs);
+ ASSERT_EQ(5u, m.ref_map[25].length);
+ ASSERT_EQ(1u, m.ref_map[25].refs);
+ m.get(5, 20);
+ cout << m << std::endl;
+ ASSERT_EQ(4u, m.ref_map.size());
+ ASSERT_EQ(5u, m.ref_map[0].length);
+ ASSERT_EQ(1u, m.ref_map[0].refs);
+ ASSERT_EQ(5u, m.ref_map[5].length);
+ ASSERT_EQ(2u, m.ref_map[5].refs);
+ ASSERT_EQ(15u, m.ref_map[10].length);
+ ASSERT_EQ(3u, m.ref_map[10].refs);
+ ASSERT_EQ(5u, m.ref_map[25].length);
+ ASSERT_EQ(1u, m.ref_map[25].refs);
+ m.get(25, 3);
+ cout << m << std::endl;
+ ASSERT_EQ(5u, m.ref_map.size());
+ ASSERT_EQ(5u, m.ref_map[0].length);
+ ASSERT_EQ(1u, m.ref_map[0].refs);
+ ASSERT_EQ(5u, m.ref_map[5].length);
+ ASSERT_EQ(2u, m.ref_map[5].refs);
+ ASSERT_EQ(15u, m.ref_map[10].length);
+ ASSERT_EQ(3u, m.ref_map[10].refs);
+ ASSERT_EQ(3u, m.ref_map[25].length);
+ ASSERT_EQ(2u, m.ref_map[25].refs);
+ ASSERT_EQ(2u, m.ref_map[28].length);
+ ASSERT_EQ(1u, m.ref_map[28].refs);
+}
+
+TEST(bluestore_extent_ref_map_t, put)
+{
+ bluestore_extent_ref_map_t m;
+ PExtentVector r;
+ bool maybe_unshared = false;
+ m.get(10, 30);
+ maybe_unshared = true;
+ m.put(10, 30, &r, &maybe_unshared);
+ cout << m << " " << r << " " << (int)maybe_unshared << std::endl;
+ ASSERT_EQ(0u, m.ref_map.size());
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(10u, r[0].offset);
+ ASSERT_EQ(30u, r[0].length);
+ ASSERT_TRUE(maybe_unshared);
+ r.clear();
+ m.get(10, 30);
+ m.get(20, 10);
+ maybe_unshared = true;
+ m.put(10, 30, &r, &maybe_unshared);
+ cout << m << " " << r << " " << (int)maybe_unshared << std::endl;
+ ASSERT_EQ(1u, m.ref_map.size());
+ ASSERT_EQ(10u, m.ref_map[20].length);
+ ASSERT_EQ(1u, m.ref_map[20].refs);
+ ASSERT_EQ(2u, r.size());
+ ASSERT_EQ(10u, r[0].offset);
+ ASSERT_EQ(10u, r[0].length);
+ ASSERT_EQ(30u, r[1].offset);
+ ASSERT_EQ(10u, r[1].length);
+ ASSERT_TRUE(maybe_unshared);
+ r.clear();
+ m.get(30, 10);
+ m.get(30, 10);
+ maybe_unshared = true;
+ m.put(20, 15, &r, &maybe_unshared);
+ cout << m << " " << r << " " << (int)maybe_unshared << std::endl;
+ ASSERT_EQ(2u, m.ref_map.size());
+ ASSERT_EQ(5u, m.ref_map[30].length);
+ ASSERT_EQ(1u, m.ref_map[30].refs);
+ ASSERT_EQ(5u, m.ref_map[35].length);
+ ASSERT_EQ(2u, m.ref_map[35].refs);
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(20u, r[0].offset);
+ ASSERT_EQ(10u, r[0].length);
+ ASSERT_FALSE(maybe_unshared);
+ r.clear();
+ maybe_unshared = true;
+ m.put(33, 5, &r, &maybe_unshared);
+ cout << m << " " << r << " " << (int)maybe_unshared << std::endl;
+ ASSERT_EQ(3u, m.ref_map.size());
+ ASSERT_EQ(3u, m.ref_map[30].length);
+ ASSERT_EQ(1u, m.ref_map[30].refs);
+ ASSERT_EQ(3u, m.ref_map[35].length);
+ ASSERT_EQ(1u, m.ref_map[35].refs);
+ ASSERT_EQ(2u, m.ref_map[38].length);
+ ASSERT_EQ(2u, m.ref_map[38].refs);
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(33u, r[0].offset);
+ ASSERT_EQ(2u, r[0].length);
+ ASSERT_FALSE(maybe_unshared);
+ r.clear();
+ maybe_unshared = true;
+ m.put(38, 2, &r, &maybe_unshared);
+ cout << m << " " << r << " " << (int)maybe_unshared << std::endl;
+ ASSERT_TRUE(maybe_unshared);
+}
+
+TEST(bluestore_extent_ref_map_t, contains)
+{
+ bluestore_extent_ref_map_t m;
+ m.get(10, 30);
+ ASSERT_TRUE(m.contains(10, 30));
+ ASSERT_TRUE(m.contains(10, 10));
+ ASSERT_TRUE(m.contains(30, 10));
+ ASSERT_FALSE(m.contains(0, 10));
+ ASSERT_FALSE(m.contains(0, 20));
+ ASSERT_FALSE(m.contains(0, 100));
+ ASSERT_FALSE(m.contains(40, 10));
+ ASSERT_FALSE(m.contains(30, 11));
+ m.get(40, 10);
+ m.get(40, 10);
+ ASSERT_TRUE(m.contains(30, 11));
+ ASSERT_TRUE(m.contains(30, 20));
+ ASSERT_TRUE(m.contains(10, 40));
+ ASSERT_FALSE(m.contains(0, 50));
+ ASSERT_FALSE(m.contains(40, 20));
+ m.get(60, 100);
+ ASSERT_TRUE(m.contains(60, 10));
+ ASSERT_TRUE(m.contains(40, 10));
+ ASSERT_FALSE(m.contains(40, 11));
+ ASSERT_FALSE(m.contains(40, 20));
+ ASSERT_FALSE(m.contains(40, 30));
+ ASSERT_FALSE(m.contains(40, 3000));
+ ASSERT_FALSE(m.contains(4000, 30));
+}
+
+TEST(bluestore_extent_ref_map_t, intersects)
+{
+ bluestore_extent_ref_map_t m;
+ m.get(10, 30);
+ ASSERT_TRUE(m.intersects(10, 30));
+ ASSERT_TRUE(m.intersects(0, 11));
+ ASSERT_TRUE(m.intersects(10, 40));
+ ASSERT_TRUE(m.intersects(15, 40));
+ ASSERT_FALSE(m.intersects(0, 10));
+ ASSERT_FALSE(m.intersects(0, 5));
+ ASSERT_FALSE(m.intersects(40, 20));
+ ASSERT_FALSE(m.intersects(41, 20));
+ m.get(40, 10);
+ m.get(40, 10);
+ ASSERT_TRUE(m.intersects(0, 100));
+ ASSERT_TRUE(m.intersects(10, 35));
+ ASSERT_TRUE(m.intersects(45, 10));
+ ASSERT_FALSE(m.intersects(50, 5));
+ m.get(60, 100);
+ ASSERT_TRUE(m.intersects(45, 10));
+ ASSERT_TRUE(m.intersects(55, 10));
+ ASSERT_TRUE(m.intersects(50, 11));
+ ASSERT_FALSE(m.intersects(50, 10));
+ ASSERT_FALSE(m.intersects(51, 9));
+ ASSERT_FALSE(m.intersects(55, 1));
+}
+
+TEST(bluestore_blob_t, calc_csum)
+{
+ bufferlist bl;
+ bl.append("asdfghjkqwertyuizxcvbnm,");
+ bufferlist bl2;
+ bl2.append("xxxxXXXXyyyyYYYYzzzzZZZZ");
+ bufferlist f;
+ f.substr_of(bl, 0, 8);
+ bufferlist m;
+ m.substr_of(bl, 8, 8);
+ bufferlist e;
+ e.substr_of(bl, 16, 8);
+ bufferlist n;
+ n.append("12345678");
+
+ for (unsigned csum_type = Checksummer::CSUM_NONE + 1;
+ csum_type < Checksummer::CSUM_MAX;
+ ++csum_type) {
+ cout << "csum_type " << Checksummer::get_csum_type_string(csum_type)
+ << std::endl;
+
+ bluestore_blob_t b;
+ int bad_off;
+ uint64_t bad_csum;
+ ASSERT_EQ(0, b.verify_csum(0, bl, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+
+ b.init_csum(csum_type, 3, 24);
+ cout << " value size " << b.get_csum_value_size() << std::endl;
+ b.calc_csum(0, bl);
+ ASSERT_EQ(0, b.verify_csum(0, bl, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+ ASSERT_EQ(-1, b.verify_csum(0, bl2, &bad_off, &bad_csum));
+ ASSERT_EQ(0, bad_off);
+
+ ASSERT_EQ(0, b.verify_csum(0, f, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+ ASSERT_EQ(-1, b.verify_csum(8, f, &bad_off, &bad_csum));
+ ASSERT_EQ(8, bad_off);
+ ASSERT_EQ(-1, b.verify_csum(16, f, &bad_off, &bad_csum));
+ ASSERT_EQ(16, bad_off);
+
+ ASSERT_EQ(-1, b.verify_csum(0, m, &bad_off, &bad_csum));
+ ASSERT_EQ(0, bad_off);
+ ASSERT_EQ(0, b.verify_csum(8, m, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+ ASSERT_EQ(-1, b.verify_csum(16, m, &bad_off, &bad_csum));
+ ASSERT_EQ(16, bad_off);
+
+ ASSERT_EQ(-1, b.verify_csum(0, e, &bad_off, &bad_csum));
+ ASSERT_EQ(0, bad_off);
+ ASSERT_EQ(-1, b.verify_csum(8, e, &bad_off, &bad_csum));
+ ASSERT_EQ(8, bad_off);
+ ASSERT_EQ(0, b.verify_csum(16, e, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+
+ b.calc_csum(8, n);
+ ASSERT_EQ(0, b.verify_csum(0, f, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+ ASSERT_EQ(0, b.verify_csum(8, n, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+ ASSERT_EQ(0, b.verify_csum(16, e, &bad_off, &bad_csum));
+ ASSERT_EQ(-1, bad_off);
+ ASSERT_EQ(-1, b.verify_csum(0, bl, &bad_off, &bad_csum));
+ ASSERT_EQ(8, bad_off);
+ }
+}
+
+TEST(bluestore_blob_t, csum_bench)
+{
+ bufferlist bl;
+ bufferptr bp(10485760);
+ for (char *a = bp.c_str(); a < bp.c_str() + bp.length(); ++a)
+ *a = (unsigned long)a & 0xff;
+ bl.append(bp);
+ int count = 256;
+ for (unsigned csum_type = 1;
+ csum_type < Checksummer::CSUM_MAX;
+ ++csum_type) {
+ bluestore_blob_t b;
+ b.init_csum(csum_type, 12, bl.length());
+ ceph::mono_clock::time_point start = ceph::mono_clock::now();
+ for (int i = 0; i<count; ++i) {
+ b.calc_csum(0, bl);
+ }
+ ceph::mono_clock::time_point end = ceph::mono_clock::now();
+ auto dur = std::chrono::duration_cast<ceph::timespan>(end - start);
+ double mbsec = (double)count * (double)bl.length() / 1000000.0 / (double)dur.count() * 1000000000.0;
+ cout << "csum_type " << Checksummer::get_csum_type_string(csum_type)
+ << ", " << dur << " seconds, "
+ << mbsec << " MB/sec" << std::endl;
+ }
+}
+
+TEST(Blob, put_ref)
+{
+ {
+ BlueStore store(g_ceph_context, "", 4096);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Blob b;
+ b.shared_blob = new BlueStore::SharedBlob(coll.get());
+ b.dirty_blob().allocated_test(bluestore_pextent_t(0x40715000, 0x2000));
+ b.dirty_blob().allocated_test(
+ bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x8000));
+ b.dirty_blob().allocated_test(bluestore_pextent_t(0x4071f000, 0x5000));
+ b.get_ref(coll.get(), 0, 0x1200);
+ b.get_ref(coll.get(), 0xae00, 0x4200);
+ ASSERT_EQ(0x5400u, b.get_referenced_bytes());
+ cout << b << std::endl;
+ PExtentVector r;
+
+ ASSERT_FALSE(b.put_ref(coll.get(), 0, 0x1200, &r));
+ ASSERT_EQ(0x4200u, b.get_referenced_bytes());
+ cout << " r " << r << std::endl;
+ cout << b << std::endl;
+
+ r.clear();
+ ASSERT_TRUE(b.put_ref(coll.get(), 0xae00, 0x4200, &r));
+ ASSERT_EQ(0u, b.get_referenced_bytes());
+ cout << " r " << r << std::endl;
+ cout << b << std::endl;
+ }
+
+ unsigned mas = 4096;
+ BlueStore store(g_ceph_context, "", 8192);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(0, mas * 2));
+ B.get_ref(coll.get(), 0, mas*2);
+ ASSERT_EQ(mas * 2, B.get_referenced_bytes());
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_TRUE(B.put_ref(coll.get(), 0, mas*2, &r));
+ ASSERT_EQ(0u, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_FALSE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(0, mas));
+ ASSERT_FALSE(b.is_allocated(mas, 0));
+ ASSERT_FALSE(b.get_extents()[0].is_valid());
+ ASSERT_EQ(mas*2, b.get_extents()[0].length);
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(123, mas * 2));
+ B.get_ref(coll.get(), 0, mas*2);
+ ASSERT_EQ(mas * 2, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), 0, mas, &r));
+ ASSERT_EQ(mas, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_TRUE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(0u, B.get_referenced_bytes());
+ ASSERT_EQ(0u, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(123u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_FALSE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.get_extents()[0].is_valid());
+ ASSERT_EQ(mas*2, b.get_extents()[0].length);
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(1, mas));
+ b.allocated_test(bluestore_pextent_t(2, mas));
+ b.allocated_test(bluestore_pextent_t(3, mas));
+ b.allocated_test(bluestore_pextent_t(4, mas));
+ B.get_ref(coll.get(), 0, mas*4);
+ ASSERT_EQ(mas * 4, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(mas * 3, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*4));
+ ASSERT_TRUE(b.is_allocated(mas, mas));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r));
+ ASSERT_EQ(mas * 2, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(mas*2, mas));
+ ASSERT_TRUE(b.is_allocated(0, mas*4));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas, &r));
+ ASSERT_EQ(mas, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(2u, r.size());
+ ASSERT_EQ(3u, r[0].offset);
+ ASSERT_EQ(mas, r[0].length);
+ ASSERT_EQ(4u, r[1].offset);
+ ASSERT_EQ(mas, r[1].length);
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(mas*2, mas*2));
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_TRUE(b.get_extents()[1].is_valid());
+ ASSERT_FALSE(b.get_extents()[2].is_valid());
+ ASSERT_EQ(3u, b.get_extents().size());
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(1, mas));
+ b.allocated_test(bluestore_pextent_t(2, mas));
+ b.allocated_test(bluestore_pextent_t(3, mas));
+ b.allocated_test(bluestore_pextent_t(4, mas));
+ b.allocated_test(bluestore_pextent_t(5, mas));
+ b.allocated_test(bluestore_pextent_t(6, mas));
+ B.get_ref(coll.get(), 0, mas*6);
+ ASSERT_EQ(mas * 6, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(mas * 5, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*6));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r));
+ ASSERT_EQ(mas * 4, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*6));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas, &r));
+ ASSERT_EQ(mas * 3, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(2u, r.size());
+ ASSERT_EQ(3u, r[0].offset);
+ ASSERT_EQ(mas, r[0].length);
+ ASSERT_EQ(4u, r[1].offset);
+ ASSERT_EQ(mas, r[1].length);
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(mas*2, mas*2));
+ ASSERT_TRUE(b.is_allocated(mas*4, mas*2));
+ ASSERT_EQ(5u, b.get_extents().size());
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_TRUE(b.get_extents()[1].is_valid());
+ ASSERT_FALSE(b.get_extents()[2].is_valid());
+ ASSERT_TRUE(b.get_extents()[3].is_valid());
+ ASSERT_TRUE(b.get_extents()[4].is_valid());
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(1, mas * 6));
+ B.get_ref(coll.get(), 0, mas*6);
+ ASSERT_EQ(mas * 6, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(mas * 5, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*6));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r));
+ ASSERT_EQ(mas * 4, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*6));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas, &r));
+ ASSERT_EQ(mas * 3, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x2001u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(mas*2, mas*2));
+ ASSERT_TRUE(b.is_allocated(mas*4, mas*2));
+ ASSERT_EQ(3u, b.get_extents().size());
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_FALSE(b.get_extents()[1].is_valid());
+ ASSERT_TRUE(b.get_extents()[2].is_valid());
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(1, mas * 4));
+ b.allocated_test(bluestore_pextent_t(2, mas * 4));
+ b.allocated_test(bluestore_pextent_t(3, mas * 4));
+ B.get_ref(coll.get(), 0, mas*12);
+ ASSERT_EQ(mas * 12, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(mas * 11, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*12));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*9, mas, &r));
+ ASSERT_EQ(mas * 10, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*12));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas*7, &r));
+ ASSERT_EQ(mas * 3, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(3u, r.size());
+ ASSERT_EQ(0x2001u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(0x2u, r[1].offset);
+ ASSERT_EQ(mas*4, r[1].length);
+ ASSERT_EQ(0x3u, r[2].offset);
+ ASSERT_EQ(mas*2, r[2].length);
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(mas*2, mas*8));
+ ASSERT_TRUE(b.is_allocated(mas*10, mas*2));
+ ASSERT_EQ(3u, b.get_extents().size());
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_FALSE(b.get_extents()[1].is_valid());
+ ASSERT_TRUE(b.get_extents()[2].is_valid());
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(1, mas * 4));
+ b.allocated_test(bluestore_pextent_t(2, mas * 4));
+ b.allocated_test(bluestore_pextent_t(3, mas * 4));
+ B.get_ref(coll.get(), 0, mas*12);
+ ASSERT_EQ(mas * 12, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(mas * 11, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*12));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*9, mas, &r));
+ ASSERT_EQ(mas * 10, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*12));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas*7, &r));
+ ASSERT_EQ(mas * 3, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(3u, r.size());
+ ASSERT_EQ(0x2001u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(0x2u, r[1].offset);
+ ASSERT_EQ(mas*4, r[1].length);
+ ASSERT_EQ(0x3u, r[2].offset);
+ ASSERT_EQ(mas*2, r[2].length);
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(mas*2, mas*8));
+ ASSERT_TRUE(b.is_allocated(mas*10, mas*2));
+ ASSERT_EQ(3u, b.get_extents().size());
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_FALSE(b.get_extents()[1].is_valid());
+ ASSERT_TRUE(b.get_extents()[2].is_valid());
+ ASSERT_FALSE(B.put_ref(coll.get(), 0, mas, &r));
+ ASSERT_EQ(mas * 2, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x1u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(2u, b.get_extents().size());
+ ASSERT_FALSE(b.get_extents()[0].is_valid());
+ ASSERT_TRUE(b.get_extents()[1].is_valid());
+ ASSERT_TRUE(B.put_ref(coll.get(), mas*10, mas*2, &r));
+ ASSERT_EQ(mas * 0, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x2003u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(1u, b.get_extents().size());
+ ASSERT_FALSE(b.get_extents()[0].is_valid());
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(1, mas * 4));
+ b.allocated_test(bluestore_pextent_t(2, mas * 4));
+ b.allocated_test(bluestore_pextent_t(3, mas * 4));
+ B.get_ref(coll.get(), 0, mas*12);
+ ASSERT_EQ(mas * 12, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(mas * 11, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*12));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*9, mas, &r));
+ ASSERT_EQ(mas * 10, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*12));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas*7, &r));
+ ASSERT_EQ(mas * 3, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(3u, r.size());
+ ASSERT_EQ(0x2001u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(0x2u, r[1].offset);
+ ASSERT_EQ(mas*4, r[1].length);
+ ASSERT_EQ(0x3u, r[2].offset);
+ ASSERT_EQ(mas*2, r[2].length);
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(mas*2, mas*8));
+ ASSERT_TRUE(b.is_allocated(mas*10, mas*2));
+ ASSERT_EQ(3u, b.get_extents().size());
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_FALSE(b.get_extents()[1].is_valid());
+ ASSERT_TRUE(b.get_extents()[2].is_valid());
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*10, mas*2, &r));
+ ASSERT_EQ(mas * 1, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x2003u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(2u, b.get_extents().size());
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_FALSE(b.get_extents()[1].is_valid());
+ ASSERT_TRUE(B.put_ref(coll.get(), 0, mas, &r));
+ ASSERT_EQ(mas * 0, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x1u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(1u, b.get_extents().size());
+ ASSERT_FALSE(b.get_extents()[0].is_valid());
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(1, mas * 8));
+ B.get_ref(coll.get(), 0, mas*8);
+ ASSERT_EQ(mas * 8, B.get_referenced_bytes());
+ ASSERT_FALSE(B.put_ref(coll.get(), 0, mas, &r));
+ ASSERT_EQ(mas * 7, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*8));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*7, mas, &r));
+ ASSERT_EQ(mas * 6, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*8));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r));
+ ASSERT_EQ(mas * 5, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, 8));
+ ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas*4, &r));
+ ASSERT_EQ(mas * 1, B.get_referenced_bytes());
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x2001u, r[0].offset);
+ ASSERT_EQ(mas*6, r[0].length);
+ ASSERT_TRUE(b.is_allocated(0, mas*2));
+ ASSERT_FALSE(b.is_allocated(mas*2, mas*6));
+ ASSERT_EQ(2u, b.get_extents().size());
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_FALSE(b.get_extents()[1].is_valid());
+ ASSERT_TRUE(B.put_ref(coll.get(), mas, mas, &r));
+ ASSERT_EQ(mas * 0, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x1u, r[0].offset);
+ ASSERT_EQ(mas*2, r[0].length);
+ ASSERT_EQ(1u, b.get_extents().size());
+ ASSERT_FALSE(b.get_extents()[0].is_valid());
+ }
+ // verify csum chunk size if factored in properly
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ PExtentVector r;
+ b.allocated_test(bluestore_pextent_t(0, mas*4));
+ b.init_csum(Checksummer::CSUM_CRC32C, 14, mas * 4);
+ B.get_ref(coll.get(), 0, mas*4);
+ ASSERT_EQ(mas * 4, B.get_referenced_bytes());
+ ASSERT_TRUE(b.is_allocated(0, mas*4));
+ ASSERT_FALSE(B.put_ref(coll.get(), 0, mas*3, &r));
+ ASSERT_EQ(mas * 1, B.get_referenced_bytes());
+ cout << "r " << r << " " << b << std::endl;
+ ASSERT_EQ(0u, r.size());
+ ASSERT_TRUE(b.is_allocated(0, mas*4));
+ ASSERT_TRUE(b.get_extents()[0].is_valid());
+ ASSERT_EQ(mas*4, b.get_extents()[0].length);
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ b.allocated_test(bluestore_pextent_t(0x40101000, 0x4000));
+ b.allocated_test(bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET,
+ 0x13000));
+
+ b.allocated_test(bluestore_pextent_t(0x40118000, 0x7000));
+ B.get_ref(coll.get(), 0x0, 0x3800);
+ B.get_ref(coll.get(), 0x17c00, 0x6400);
+ ASSERT_EQ(0x3800u + 0x6400u, B.get_referenced_bytes());
+ b.set_flag(bluestore_blob_t::FLAG_SHARED);
+ b.init_csum(Checksummer::CSUM_CRC32C, 12, 0x1e000);
+
+ cout << "before: " << B << std::endl;
+ PExtentVector r;
+ ASSERT_FALSE(B.put_ref(coll.get(), 0x1800, 0x2000, &r));
+ ASSERT_EQ(0x3800u + 0x6400u - 0x2000u, B.get_referenced_bytes());
+ cout << "after: " << B << std::endl;
+ cout << "r " << r << std::endl;
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ b.allocated_test(bluestore_pextent_t(1, 0x5000));
+ b.allocated_test(bluestore_pextent_t(2, 0x5000));
+ B.get_ref(coll.get(), 0x0, 0xa000);
+ ASSERT_EQ(0xa000u, B.get_referenced_bytes());
+ cout << "before: " << B << std::endl;
+ PExtentVector r;
+ ASSERT_FALSE(B.put_ref(coll.get(), 0x8000, 0x2000, &r));
+ cout << "after: " << B << std::endl;
+ cout << "r " << r << std::endl;
+ ASSERT_EQ(0x8000u, B.get_referenced_bytes());
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(0x3002u, r[0].offset);
+ ASSERT_EQ(0x2000u, r[0].length);
+ }
+ {
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ b.allocated_test(bluestore_pextent_t(1, 0x7000));
+ b.allocated_test(bluestore_pextent_t(2, 0x7000));
+ B.get_ref(coll.get(), 0x0, 0xe000);
+ ASSERT_EQ(0xe000u, B.get_referenced_bytes());
+ cout << "before: " << B << std::endl;
+ PExtentVector r;
+ ASSERT_FALSE(B.put_ref(coll.get(), 0, 0xb000, &r));
+ ASSERT_EQ(0x3000u, B.get_referenced_bytes());
+ cout << "after: " << B << std::endl;
+ cout << "r " << r << std::endl;
+ ASSERT_EQ(0x3000u, B.get_referenced_bytes());
+ ASSERT_EQ(2u, r.size());
+ ASSERT_EQ(1u, r[0].offset);
+ ASSERT_EQ(0x7000u, r[0].length);
+ ASSERT_EQ(2u, r[1].offset);
+ ASSERT_EQ(0x3000u, r[1].length); // we have 0x1000 bytes less due to
+ // alignment caused by min_alloc_size = 0x2000
+ }
+ {
+ BlueStore store(g_ceph_context, "", 0x4000);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Blob B;
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ bluestore_blob_t& b = B.dirty_blob();
+ b.allocated_test(bluestore_pextent_t(1, 0x5000));
+ b.allocated_test(bluestore_pextent_t(2, 0x7000));
+ B.get_ref(coll.get(), 0x0, 0xc000);
+ ASSERT_EQ(0xc000u, B.get_referenced_bytes());
+ cout << "before: " << B << std::endl;
+ PExtentVector r;
+ ASSERT_FALSE(B.put_ref(coll.get(), 0x2000, 0xa000, &r));
+ cout << "after: " << B << std::endl;
+ cout << "r " << r << std::endl;
+ ASSERT_EQ(0x2000u, B.get_referenced_bytes());
+ ASSERT_EQ(2u, r.size());
+ ASSERT_EQ(0x4001u, r[0].offset);
+ ASSERT_EQ(0x1000u, r[0].length);
+ ASSERT_EQ(2u, r[1].offset);
+ ASSERT_EQ(0x7000u, r[1].length);
+ ASSERT_EQ(1u, b.get_extents()[0].offset);
+ ASSERT_EQ(0x4000u, b.get_extents()[0].length);
+ }
+}
+
+TEST(bluestore_blob_t, can_split)
+{
+ bluestore_blob_t a;
+ ASSERT_TRUE(a.can_split());
+ a.flags = bluestore_blob_t::FLAG_SHARED;
+ ASSERT_FALSE(a.can_split());
+ a.flags = bluestore_blob_t::FLAG_COMPRESSED;
+ ASSERT_FALSE(a.can_split());
+ a.flags = bluestore_blob_t::FLAG_HAS_UNUSED;
+ ASSERT_FALSE(a.can_split());
+}
+
+TEST(bluestore_blob_t, can_split_at)
+{
+ bluestore_blob_t a;
+ a.allocated_test(bluestore_pextent_t(0x10000, 0x2000));
+ a.allocated_test(bluestore_pextent_t(0x20000, 0x2000));
+ ASSERT_TRUE(a.can_split_at(0x1000));
+ ASSERT_TRUE(a.can_split_at(0x1800));
+ a.init_csum(Checksummer::CSUM_CRC32C, 12, 0x4000);
+ ASSERT_TRUE(a.can_split_at(0x1000));
+ ASSERT_TRUE(a.can_split_at(0x2000));
+ ASSERT_TRUE(a.can_split_at(0x3000));
+ ASSERT_FALSE(a.can_split_at(0x2800));
+}
+
+TEST(bluestore_blob_t, prune_tail)
+{
+ bluestore_blob_t a;
+ a.allocated_test(bluestore_pextent_t(0x10000, 0x2000));
+ a.allocated_test(bluestore_pextent_t(0x20000, 0x2000));
+ ASSERT_FALSE(a.can_prune_tail());
+ a.allocated_test(
+ bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x2000));
+ ASSERT_TRUE(a.can_prune_tail());
+ a.prune_tail();
+ ASSERT_FALSE(a.can_prune_tail());
+ ASSERT_EQ(2u, a.get_extents().size());
+ ASSERT_EQ(0x4000u, a.get_logical_length());
+
+ a.allocated_test(
+ bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x2000));
+ a.init_csum(Checksummer::CSUM_CRC32C_8, 12, 0x6000);
+ ASSERT_EQ(6u, a.csum_data.length());
+ ASSERT_TRUE(a.can_prune_tail());
+ a.prune_tail();
+ ASSERT_FALSE(a.can_prune_tail());
+ ASSERT_EQ(2u, a.get_extents().size());
+ ASSERT_EQ(0x4000u, a.get_logical_length());
+ ASSERT_EQ(4u, a.csum_data.length());
+
+ bluestore_blob_t b;
+ b.allocated_test(
+ bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x2000));
+ ASSERT_FALSE(a.can_prune_tail());
+}
+
+TEST(Blob, split)
+{
+ BlueStore store(g_ceph_context, "", 4096);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ {
+ BlueStore::Blob L, R;
+ L.shared_blob = new BlueStore::SharedBlob(coll.get());
+ R.shared_blob = new BlueStore::SharedBlob(coll.get());
+ L.dirty_blob().allocated_test(bluestore_pextent_t(0x2000, 0x2000));
+ L.dirty_blob().init_csum(Checksummer::CSUM_CRC32C, 12, 0x2000);
+ L.get_ref(coll.get(), 0, 0x2000);
+ L.split(coll.get(), 0x1000, &R);
+ ASSERT_EQ(0x1000u, L.get_blob().get_logical_length());
+ ASSERT_EQ(4u, L.get_blob().csum_data.length());
+ ASSERT_EQ(1u, L.get_blob().get_extents().size());
+ ASSERT_EQ(0x2000u, L.get_blob().get_extents().front().offset);
+ ASSERT_EQ(0x1000u, L.get_blob().get_extents().front().length);
+ ASSERT_EQ(0x1000u, L.get_referenced_bytes());
+ ASSERT_EQ(0x1000u, R.get_blob().get_logical_length());
+ ASSERT_EQ(4u, R.get_blob().csum_data.length());
+ ASSERT_EQ(1u, R.get_blob().get_extents().size());
+ ASSERT_EQ(0x3000u, R.get_blob().get_extents().front().offset);
+ ASSERT_EQ(0x1000u, R.get_blob().get_extents().front().length);
+ ASSERT_EQ(0x1000u, R.get_referenced_bytes());
+ }
+ {
+ BlueStore::Blob L, R;
+ L.shared_blob = new BlueStore::SharedBlob(coll.get());
+ R.shared_blob = new BlueStore::SharedBlob(coll.get());
+ L.dirty_blob().allocated_test(bluestore_pextent_t(0x2000, 0x1000));
+ L.dirty_blob().allocated_test(bluestore_pextent_t(0x12000, 0x1000));
+ L.dirty_blob().init_csum(Checksummer::CSUM_CRC32C, 12, 0x2000);
+ L.get_ref(coll.get(), 0, 0x1000);
+ L.get_ref(coll.get(), 0x1000, 0x1000);
+ L.split(coll.get(), 0x1000, &R);
+ ASSERT_EQ(0x1000u, L.get_blob().get_logical_length());
+ ASSERT_EQ(4u, L.get_blob().csum_data.length());
+ ASSERT_EQ(1u, L.get_blob().get_extents().size());
+ ASSERT_EQ(0x2000u, L.get_blob().get_extents().front().offset);
+ ASSERT_EQ(0x1000u, L.get_blob().get_extents().front().length);
+ ASSERT_EQ(0x1000u, L.get_referenced_bytes());
+ ASSERT_EQ(0x1000u, R.get_blob().get_logical_length());
+ ASSERT_EQ(4u, R.get_blob().csum_data.length());
+ ASSERT_EQ(1u, R.get_blob().get_extents().size());
+ ASSERT_EQ(0x12000u, R.get_blob().get_extents().front().offset);
+ ASSERT_EQ(0x1000u, R.get_blob().get_extents().front().length);
+ ASSERT_EQ(0x1000u, R.get_referenced_bytes());
+ }
+}
+
+TEST(Blob, legacy_decode)
+{
+ BlueStore store(g_ceph_context, "", 4096);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ bufferlist bl, bl2;
+ {
+ BlueStore::Blob B;
+
+ B.shared_blob = new BlueStore::SharedBlob(coll.get());
+ B.dirty_blob().allocated_test(bluestore_pextent_t(0x1, 0x2000));
+ B.dirty_blob().init_csum(Checksummer::CSUM_CRC32C, 12, 0x2000);
+ B.get_ref(coll.get(), 0, 0xff0);
+ B.get_ref(coll.get(), 0x1fff, 1);
+
+ bluestore_extent_ref_map_t fake_ref_map;
+ fake_ref_map.get(0, 0xff0);
+ fake_ref_map.get(0x1fff, 1);
+
+ size_t bound = 0, bound2 = 0;
+
+ B.bound_encode(
+ bound,
+ 1, /*struct_v*/
+ 0, /*sbid*/
+ false);
+ fake_ref_map.bound_encode(bound);
+
+ B.bound_encode(
+ bound2,
+ 2, /*struct_v*/
+ 0, /*sbid*/
+ true);
+
+ {
+ auto app = bl.get_contiguous_appender(bound);
+ auto app2 = bl2.get_contiguous_appender(bound2);
+ B.encode(
+ app,
+ 1, /*struct_v*/
+ 0, /*sbid*/
+ false);
+ fake_ref_map.encode(app);
+
+ B.encode(
+ app2,
+ 2, /*struct_v*/
+ 0, /*sbid*/
+ true);
+ }
+
+ auto p = bl.front().begin_deep();
+ auto p2 = bl2.front().begin_deep();
+ BlueStore::Blob Bres, Bres2;
+ Bres.shared_blob = new BlueStore::SharedBlob(coll.get());
+ Bres2.shared_blob = new BlueStore::SharedBlob(coll.get());
+
+ uint64_t sbid, sbid2;
+ Bres.decode(
+ p,
+ 1, /*struct_v*/
+ &sbid,
+ true,
+ coll.get());
+ Bres2.decode(
+ p2,
+ 2, /*struct_v*/
+ &sbid2,
+ true,
+ coll.get());
+
+ ASSERT_EQ(0xff0u + 1u, Bres.get_blob_use_tracker().get_referenced_bytes());
+ ASSERT_EQ(0xff0u + 1u, Bres2.get_blob_use_tracker().get_referenced_bytes());
+ ASSERT_TRUE(Bres.get_blob_use_tracker().equal(Bres2.get_blob_use_tracker()));
+ }
+}
+
+TEST(ExtentMap, seek_lextent)
+{
+ BlueStore store(g_ceph_context, "", 4096);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Onode onode(coll.get(), ghobject_t(), "");
+ BlueStore::ExtentMap em(&onode,
+ g_ceph_context->_conf->bluestore_extent_map_inline_shard_prealloc_size);
+ BlueStore::BlobRef br(new BlueStore::Blob);
+ br->shared_blob = new BlueStore::SharedBlob(coll.get());
+
+ ASSERT_EQ(em.extent_map.end(), em.seek_lextent(0));
+ ASSERT_EQ(em.extent_map.end(), em.seek_lextent(100));
+
+ em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, br));
+ auto a = em.find(100);
+ ASSERT_EQ(a, em.seek_lextent(0));
+ ASSERT_EQ(a, em.seek_lextent(99));
+ ASSERT_EQ(a, em.seek_lextent(100));
+ ASSERT_EQ(a, em.seek_lextent(101));
+ ASSERT_EQ(a, em.seek_lextent(199));
+ ASSERT_EQ(em.extent_map.end(), em.seek_lextent(200));
+
+ em.extent_map.insert(*new BlueStore::Extent(200, 0, 100, br));
+ auto b = em.find(200);
+ ASSERT_EQ(a, em.seek_lextent(0));
+ ASSERT_EQ(a, em.seek_lextent(99));
+ ASSERT_EQ(a, em.seek_lextent(100));
+ ASSERT_EQ(a, em.seek_lextent(101));
+ ASSERT_EQ(a, em.seek_lextent(199));
+ ASSERT_EQ(b, em.seek_lextent(200));
+ ASSERT_EQ(b, em.seek_lextent(299));
+ ASSERT_EQ(em.extent_map.end(), em.seek_lextent(300));
+
+ em.extent_map.insert(*new BlueStore::Extent(400, 0, 100, br));
+ auto d = em.find(400);
+ ASSERT_EQ(a, em.seek_lextent(0));
+ ASSERT_EQ(a, em.seek_lextent(99));
+ ASSERT_EQ(a, em.seek_lextent(100));
+ ASSERT_EQ(a, em.seek_lextent(101));
+ ASSERT_EQ(a, em.seek_lextent(199));
+ ASSERT_EQ(b, em.seek_lextent(200));
+ ASSERT_EQ(b, em.seek_lextent(299));
+ ASSERT_EQ(d, em.seek_lextent(300));
+ ASSERT_EQ(d, em.seek_lextent(399));
+ ASSERT_EQ(d, em.seek_lextent(400));
+ ASSERT_EQ(d, em.seek_lextent(499));
+ ASSERT_EQ(em.extent_map.end(), em.seek_lextent(500));
+}
+
+TEST(ExtentMap, has_any_lextents)
+{
+ BlueStore store(g_ceph_context, "", 4096);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Onode onode(coll.get(), ghobject_t(), "");
+ BlueStore::ExtentMap em(&onode,
+ g_ceph_context->_conf->bluestore_extent_map_inline_shard_prealloc_size);
+ BlueStore::BlobRef b(new BlueStore::Blob);
+ b->shared_blob = new BlueStore::SharedBlob(coll.get());
+
+ ASSERT_FALSE(em.has_any_lextents(0, 0));
+ ASSERT_FALSE(em.has_any_lextents(0, 1000));
+ ASSERT_FALSE(em.has_any_lextents(1000, 1000));
+
+ em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, b));
+ ASSERT_FALSE(em.has_any_lextents(0, 50));
+ ASSERT_FALSE(em.has_any_lextents(0, 100));
+ ASSERT_FALSE(em.has_any_lextents(50, 50));
+ ASSERT_TRUE(em.has_any_lextents(50, 51));
+ ASSERT_TRUE(em.has_any_lextents(50, 100051));
+ ASSERT_TRUE(em.has_any_lextents(100, 100));
+ ASSERT_TRUE(em.has_any_lextents(100, 1));
+ ASSERT_TRUE(em.has_any_lextents(199, 1));
+ ASSERT_TRUE(em.has_any_lextents(199, 2));
+ ASSERT_FALSE(em.has_any_lextents(200, 2));
+
+ em.extent_map.insert(*new BlueStore::Extent(200, 0, 100, b));
+ ASSERT_TRUE(em.has_any_lextents(199, 1));
+ ASSERT_TRUE(em.has_any_lextents(199, 2));
+ ASSERT_TRUE(em.has_any_lextents(200, 2));
+ ASSERT_TRUE(em.has_any_lextents(200, 200));
+ ASSERT_TRUE(em.has_any_lextents(299, 1));
+ ASSERT_FALSE(em.has_any_lextents(300, 1));
+
+ em.extent_map.insert(*new BlueStore::Extent(400, 0, 100, b));
+ ASSERT_TRUE(em.has_any_lextents(0, 10000));
+ ASSERT_TRUE(em.has_any_lextents(199, 1));
+ ASSERT_FALSE(em.has_any_lextents(300, 1));
+ ASSERT_FALSE(em.has_any_lextents(300, 100));
+ ASSERT_FALSE(em.has_any_lextents(399, 1));
+ ASSERT_TRUE(em.has_any_lextents(400, 1));
+ ASSERT_TRUE(em.has_any_lextents(400, 100));
+ ASSERT_TRUE(em.has_any_lextents(400, 1000));
+ ASSERT_TRUE(em.has_any_lextents(499, 1000));
+ ASSERT_FALSE(em.has_any_lextents(500, 1000));
+}
+
+void erase_and_delete(BlueStore::ExtentMap& em, size_t v)
+{
+ auto d = em.find(v);
+ ASSERT_NE(d, em.extent_map.end());
+ em.extent_map.erase(d);
+ delete &*d;
+}
+
+TEST(ExtentMap, compress_extent_map)
+{
+ BlueStore store(g_ceph_context, "", 4096);
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Onode onode(coll.get(), ghobject_t(), "");
+ BlueStore::ExtentMap em(&onode,
+ g_ceph_context->_conf->bluestore_extent_map_inline_shard_prealloc_size);
+ BlueStore::BlobRef b1(new BlueStore::Blob);
+ BlueStore::BlobRef b2(new BlueStore::Blob);
+ BlueStore::BlobRef b3(new BlueStore::Blob);
+ b1->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b2->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b3->shared_blob = new BlueStore::SharedBlob(coll.get());
+
+ em.extent_map.insert(*new BlueStore::Extent(0, 0, 100, b1));
+ em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, b2));
+ ASSERT_EQ(0, em.compress_extent_map(0, 10000));
+ ASSERT_EQ(2u, em.extent_map.size());
+
+ em.extent_map.insert(*new BlueStore::Extent(200, 100, 100, b2));
+ em.extent_map.insert(*new BlueStore::Extent(300, 200, 100, b2));
+ ASSERT_EQ(0, em.compress_extent_map(0, 0));
+ ASSERT_EQ(0, em.compress_extent_map(100000, 1000));
+ ASSERT_EQ(2, em.compress_extent_map(0, 100000));
+ ASSERT_EQ(2u, em.extent_map.size());
+ erase_and_delete(em, 100);
+ em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, b2));
+ em.extent_map.insert(*new BlueStore::Extent(200, 100, 100, b3));
+ em.extent_map.insert(*new BlueStore::Extent(300, 200, 100, b2));
+ ASSERT_EQ(0, em.compress_extent_map(0, 1));
+ ASSERT_EQ(0, em.compress_extent_map(0, 100000));
+ ASSERT_EQ(4u, em.extent_map.size());
+
+ em.extent_map.insert(*new BlueStore::Extent(400, 300, 100, b2));
+ em.extent_map.insert(*new BlueStore::Extent(500, 500, 100, b2));
+ em.extent_map.insert(*new BlueStore::Extent(600, 600, 100, b2));
+ em.extent_map.insert(*new BlueStore::Extent(700, 0, 100, b1));
+ em.extent_map.insert(*new BlueStore::Extent(800, 0, 100, b3));
+ ASSERT_EQ(0, em.compress_extent_map(0, 99));
+ ASSERT_EQ(0, em.compress_extent_map(800, 1000));
+ ASSERT_EQ(2, em.compress_extent_map(100, 500));
+ ASSERT_EQ(7u, em.extent_map.size());
+ erase_and_delete(em, 300);
+ erase_and_delete(em, 500);
+ erase_and_delete(em, 700);
+ em.extent_map.insert(*new BlueStore::Extent(400, 300, 100, b2));
+ em.extent_map.insert(*new BlueStore::Extent(500, 400, 100, b2));
+ em.extent_map.insert(*new BlueStore::Extent(700, 500, 100, b2));
+ ASSERT_EQ(1, em.compress_extent_map(0, 1000));
+ ASSERT_EQ(6u, em.extent_map.size());
+}
+
+
+void clear_and_dispose(BlueStore::old_extent_map_t& old_em)
+{
+ auto oep = old_em.begin();
+ while (oep != old_em.end()) {
+ auto &lo = *oep;
+ oep = old_em.erase(oep);
+ delete &lo;
+ }
+}
+
+TEST(GarbageCollector, BasicTest)
+{
+ BlueStore::OnodeCacheShard *oc = BlueStore::OnodeCacheShard::create(
+ g_ceph_context, "lru", NULL);
+ BlueStore::BufferCacheShard *bc = BlueStore::BufferCacheShard::create(
+ g_ceph_context, "lru", NULL);
+
+ BlueStore store(g_ceph_context, "", 4096);
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Onode onode(coll.get(), ghobject_t(), "");
+ BlueStore::ExtentMap em(&onode,
+ g_ceph_context->_conf->bluestore_extent_map_inline_shard_prealloc_size);
+
+ BlueStore::old_extent_map_t old_extents;
+
+
+ /*
+ min_alloc_size = 4096
+ original disposition
+ extent1 <loffs = 100, boffs = 100, len = 10>
+ -> blob1<compressed, len_on_disk=4096, logical_len=8192>
+ extent2 <loffs = 200, boffs = 200, len = 10>
+ -> blob2<raw, len_on_disk=4096, llen=4096>
+ extent3 <loffs = 300, boffs = 300, len = 10>
+ -> blob1<compressed, len_on_disk=4096, llen=8192>
+ extent4 <loffs = 4096, boffs = 0, len = 10>
+ -> blob3<raw, len_on_disk=4096, llen=4096>
+ on write(300~100) resulted in
+ extent1 <loffs = 100, boffs = 100, len = 10>
+ -> blob1<compressed, len_on_disk=4096, logical_len=8192>
+ extent2 <loffs = 200, boffs = 200, len = 10>
+ -> blob2<raw, len_on_disk=4096, llen=4096>
+ extent3 <loffs = 300, boffs = 300, len = 100>
+ -> blob4<raw, len_on_disk=4096, llen=4096>
+ extent4 <loffs = 4096, boffs = 0, len = 10>
+ -> blob3<raw, len_on_disk=4096, llen=4096>
+ */
+ {
+ BlueStore::GarbageCollector gc(g_ceph_context);
+ int64_t saving;
+ BlueStore::BlobRef b1(new BlueStore::Blob);
+ BlueStore::BlobRef b2(new BlueStore::Blob);
+ BlueStore::BlobRef b3(new BlueStore::Blob);
+ BlueStore::BlobRef b4(new BlueStore::Blob);
+ b1->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b2->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b3->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b4->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b1->dirty_blob().set_compressed(0x2000, 0x1000);
+ b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x1000));
+ b2->dirty_blob().allocated_test(bluestore_pextent_t(1, 0x1000));
+ b3->dirty_blob().allocated_test(bluestore_pextent_t(2, 0x1000));
+ b4->dirty_blob().allocated_test(bluestore_pextent_t(3, 0x1000));
+ em.extent_map.insert(*new BlueStore::Extent(100, 100, 10, b1));
+ b1->get_ref(coll.get(), 100, 10);
+ em.extent_map.insert(*new BlueStore::Extent(200, 200, 10, b2));
+ b2->get_ref(coll.get(), 200, 10);
+ em.extent_map.insert(*new BlueStore::Extent(300, 300, 100, b4));
+ b4->get_ref(coll.get(), 300, 100);
+ em.extent_map.insert(*new BlueStore::Extent(4096, 0, 10, b3));
+ b3->get_ref(coll.get(), 0, 10);
+
+ old_extents.push_back(*new BlueStore::OldExtent(300, 300, 10, b1));
+
+ saving = gc.estimate(300, 100, em, old_extents, 4096);
+ ASSERT_EQ(saving, 1);
+ auto& to_collect = gc.get_extents_to_collect();
+ ASSERT_EQ(to_collect.num_intervals(), 1u);
+ {
+ auto it = to_collect.begin();
+ using p = decltype(*it);
+ auto v = p{100ul, 10ul};
+ ASSERT_EQ(*it, v);
+ }
+ em.clear();
+ clear_and_dispose(old_extents);
+ }
+ /*
+ original disposition
+ min_alloc_size = 0x10000
+ extent1 <loffs = 0, boffs = 0, len = 0x40000>
+ -> blob1<compressed, len_on_disk=0x20000, logical_len=0x40000>
+ Write 0x8000~37000 resulted in the following extent map prior to GC
+ for the last write_small(0x30000~0xf000):
+
+ extent1 <loffs = 0, boffs = 0, len = 0x8000>
+ -> blob1<compressed, len_on_disk=0x20000, logical_len=0x40000>
+ extent2 <loffs = 0x8000, boffs = 0x8000, len = 0x8000>
+ -> blob2<raw, len_on_disk=0x10000, llen=0x10000>
+ extent3 <loffs = 0x10000, boffs = 0, len = 0x20000>
+ -> blob3<raw, len_on_disk=0x20000, llen=0x20000>
+ extent4 <loffs = 0x30000, boffs = 0, len = 0xf000>
+ -> blob4<raw, len_on_disk=0x10000, llen=0x10000>
+ extent5 <loffs = 0x3f000, boffs = 0x3f000, len = 0x1000>
+ -> blob1<compressed, len_on_disk=0x20000, llen=0x40000>
+ */
+ {
+ BlueStore store(g_ceph_context, "", 0x10000);
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Onode onode(coll.get(), ghobject_t(), "");
+ BlueStore::ExtentMap em(&onode,
+ g_ceph_context->_conf->bluestore_extent_map_inline_shard_prealloc_size);
+
+ BlueStore::old_extent_map_t old_extents;
+ BlueStore::GarbageCollector gc(g_ceph_context);
+ int64_t saving;
+ BlueStore::BlobRef b1(new BlueStore::Blob);
+ BlueStore::BlobRef b2(new BlueStore::Blob);
+ BlueStore::BlobRef b3(new BlueStore::Blob);
+ BlueStore::BlobRef b4(new BlueStore::Blob);
+ b1->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b2->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b3->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b4->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b1->dirty_blob().set_compressed(0x40000, 0x20000);
+ b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x20000));
+ b2->dirty_blob().allocated_test(bluestore_pextent_t(1, 0x10000));
+ b3->dirty_blob().allocated_test(bluestore_pextent_t(2, 0x20000));
+ b4->dirty_blob().allocated_test(bluestore_pextent_t(3, 0x10000));
+
+ em.extent_map.insert(*new BlueStore::Extent(0, 0, 0x8000, b1));
+ b1->get_ref(coll.get(), 0, 0x8000);
+ em.extent_map.insert(
+ *new BlueStore::Extent(0x8000, 0x8000, 0x8000, b2)); // new extent
+ b2->get_ref(coll.get(), 0x8000, 0x8000);
+ em.extent_map.insert(
+ *new BlueStore::Extent(0x10000, 0, 0x20000, b3)); // new extent
+ b3->get_ref(coll.get(), 0, 0x20000);
+ em.extent_map.insert(
+ *new BlueStore::Extent(0x30000, 0, 0xf000, b4)); // new extent
+ b4->get_ref(coll.get(), 0, 0xf000);
+ em.extent_map.insert(*new BlueStore::Extent(0x3f000, 0x3f000, 0x1000, b1));
+ b1->get_ref(coll.get(), 0x3f000, 0x1000);
+
+ old_extents.push_back(*new BlueStore::OldExtent(0x8000, 0x8000, 0x8000, b1));
+ old_extents.push_back(
+ *new BlueStore::OldExtent(0x10000, 0x10000, 0x20000, b1));
+ old_extents.push_back(*new BlueStore::OldExtent(0x30000, 0x30000, 0xf000, b1));
+
+ saving = gc.estimate(0x30000, 0xf000, em, old_extents, 0x10000);
+ ASSERT_EQ(saving, 2);
+ auto& to_collect = gc.get_extents_to_collect();
+ ASSERT_EQ(to_collect.num_intervals(), 2u);
+ {
+ auto it1 = to_collect.begin();
+ auto it2 = ++to_collect.begin();
+ using p = decltype(*it1);
+ {
+ auto v1 = p{0x0ul ,0x8000ul};
+ auto v2 = p{0x0ul, 0x8000ul};
+ ASSERT_TRUE(*it1 == v1 || *it2 == v2);
+ }
+ {
+ auto v1 = p{0x3f000ul, 0x1000ul};
+ auto v2 = p{0x3f000ul, 0x1000ul};
+ ASSERT_TRUE(*it1 == v1 || *it2 == v2);
+ }
+ }
+
+ em.clear();
+ clear_and_dispose(old_extents);
+ }
+ /*
+ original disposition
+ min_alloc_size = 0x1000
+ extent1 <loffs = 0, boffs = 0, len = 0x4000>
+ -> blob1<compressed, len_on_disk=0x2000, logical_len=0x4000>
+ write 0x3000~4000 resulted in the following extent map
+ (future feature - suppose we can compress incoming write prior to
+ GC invocation)
+
+ extent1 <loffs = 0, boffs = 0, len = 0x4000>
+ -> blob1<compressed, len_on_disk=0x2000, logical_len=0x4000>
+ extent2 <loffs = 0x3000, boffs = 0, len = 0x4000>
+ -> blob2<compressed, len_on_disk=0x2000, llen=0x4000>
+ */
+ {
+ BlueStore::GarbageCollector gc(g_ceph_context);
+ int64_t saving;
+ BlueStore::BlobRef b1(new BlueStore::Blob);
+ BlueStore::BlobRef b2(new BlueStore::Blob);
+ b1->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b2->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b1->dirty_blob().set_compressed(0x4000, 0x2000);
+ b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x2000));
+ b2->dirty_blob().set_compressed(0x4000, 0x2000);
+ b2->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x2000));
+
+ em.extent_map.insert(*new BlueStore::Extent(0, 0, 0x3000, b1));
+ b1->get_ref(coll.get(), 0, 0x3000);
+ em.extent_map.insert(
+ *new BlueStore::Extent(0x3000, 0, 0x4000, b2)); // new extent
+ b2->get_ref(coll.get(), 0, 0x4000);
+
+ old_extents.push_back(*new BlueStore::OldExtent(0x3000, 0x3000, 0x1000, b1));
+
+ saving = gc.estimate(0x3000, 0x4000, em, old_extents, 0x1000);
+ ASSERT_EQ(saving, 0);
+ auto& to_collect = gc.get_extents_to_collect();
+ ASSERT_EQ(to_collect.num_intervals(), 0u);
+ em.clear();
+ clear_and_dispose(old_extents);
+ }
+ /*
+ original disposition
+ min_alloc_size = 0x10000
+ extent0 <loffs = 0, boffs = 0, len = 0x20000>
+ -> blob0<compressed, len_on_disk=0x10000, logical_len=0x20000>
+ extent1 <loffs = 0x20000, boffs = 0, len = 0x20000>
+ -> blob1<compressed, len_on_disk=0x10000, logical_len=0x20000>
+ write 0x8000~37000 resulted in the following extent map prior
+ to GC for the last write_small(0x30000~0xf000)
+
+ extent0 <loffs = 0, boffs = 0, len = 0x8000>
+ -> blob0<compressed, len_on_disk=0x10000, logical_len=0x20000>
+ extent2 <loffs = 0x8000, boffs = 0x8000, len = 0x8000>
+ -> blob2<raw, len_on_disk=0x10000, llen=0x10000>
+ extent3 <loffs = 0x10000, boffs = 0, len = 0x20000>
+ -> blob3<raw, len_on_disk=0x20000, llen=0x20000>
+ extent4 <loffs = 0x30000, boffs = 0, len = 0xf000>
+ -> blob4<raw, len_on_disk=0x1000, llen=0x1000>
+ extent5 <loffs = 0x3f000, boffs = 0x1f000, len = 0x1000>
+ -> blob1<compressed, len_on_disk=0x10000, llen=0x20000>
+ */
+ {
+ BlueStore store(g_ceph_context, "", 0x10000);
+ auto coll = ceph::make_ref<BlueStore::Collection>(&store, oc, bc, coll_t());
+ BlueStore::Onode onode(coll.get(), ghobject_t(), "");
+ BlueStore::ExtentMap em(&onode,
+ g_ceph_context->_conf->bluestore_extent_map_inline_shard_prealloc_size);
+
+ BlueStore::old_extent_map_t old_extents;
+ BlueStore::GarbageCollector gc(g_ceph_context);
+ int64_t saving;
+ BlueStore::BlobRef b0(new BlueStore::Blob);
+ BlueStore::BlobRef b1(new BlueStore::Blob);
+ BlueStore::BlobRef b2(new BlueStore::Blob);
+ BlueStore::BlobRef b3(new BlueStore::Blob);
+ BlueStore::BlobRef b4(new BlueStore::Blob);
+ b0->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b1->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b2->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b3->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b4->shared_blob = new BlueStore::SharedBlob(coll.get());
+ b0->dirty_blob().set_compressed(0x2000, 0x1000);
+ b0->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x10000));
+ b1->dirty_blob().set_compressed(0x20000, 0x10000);
+ b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x10000));
+ b2->dirty_blob().allocated_test(bluestore_pextent_t(1, 0x10000));
+ b3->dirty_blob().allocated_test(bluestore_pextent_t(2, 0x20000));
+ b4->dirty_blob().allocated_test(bluestore_pextent_t(3, 0x1000));
+
+ em.extent_map.insert(*new BlueStore::Extent(0, 0, 0x8000, b0));
+ b0->get_ref(coll.get(), 0, 0x8000);
+ em.extent_map.insert(
+ *new BlueStore::Extent(0x8000, 0x8000, 0x8000, b2)); // new extent
+ b2->get_ref(coll.get(), 0x8000, 0x8000);
+ em.extent_map.insert(
+ *new BlueStore::Extent(0x10000, 0, 0x20000, b3)); // new extent
+ b3->get_ref(coll.get(), 0, 0x20000);
+ em.extent_map.insert(
+ *new BlueStore::Extent(0x30000, 0, 0xf000, b4)); // new extent
+ b4->get_ref(coll.get(), 0, 0xf000);
+ em.extent_map.insert(*new BlueStore::Extent(0x3f000, 0x1f000, 0x1000, b1));
+ b1->get_ref(coll.get(), 0x1f000, 0x1000);
+
+ old_extents.push_back(*new BlueStore::OldExtent(0x8000, 0x8000, 0x8000, b0));
+ old_extents.push_back(
+ *new BlueStore::OldExtent(0x10000, 0x10000, 0x10000, b0));
+ old_extents.push_back(
+ *new BlueStore::OldExtent(0x20000, 0x00000, 0x1f000, b1));
+
+ saving = gc.estimate(0x30000, 0xf000, em, old_extents, 0x10000);
+ ASSERT_EQ(saving, 2);
+ auto& to_collect = gc.get_extents_to_collect();
+ ASSERT_EQ(to_collect.num_intervals(), 2u);
+ {
+ auto it1 = to_collect.begin();
+ auto it2 = ++to_collect.begin();
+ using p = decltype(*it1);
+ {
+ auto v1 = p{0x0ul, 0x8000ul};
+ auto v2 = p{0x0ul, 0x8000ul};
+ ASSERT_TRUE(*it1 == v1 || *it2 == v2);
+ }
+ {
+ auto v1 = p{0x3f000ul, 0x1000ul};
+ auto v2 = p{0x3f000ul, 0x1000ul};
+ ASSERT_TRUE(*it1 == v1 || *it2 == v2);
+ }
+ }
+
+ em.clear();
+ clear_and_dispose(old_extents);
+ }
+}
+
+TEST(BlueStoreRepairer, StoreSpaceTracker)
+{
+ BlueStoreRepairer::StoreSpaceTracker bmap0;
+ bmap0.init((uint64_t)4096 * 1024 * 1024 * 1024, 0x1000);
+ ASSERT_EQ(bmap0.granularity, 2 * 1024 * 1024U);
+ ASSERT_EQ(bmap0.collections_bfs.size(), 2048u * 1024u);
+ ASSERT_EQ(bmap0.objects_bfs.size(), 2048u * 1024u);
+
+ BlueStoreRepairer::StoreSpaceTracker bmap;
+ bmap.init(0x2000 * 0x1000 - 1, 0x1000, 512 * 1024);
+ ASSERT_EQ(bmap.granularity, 0x1000u);
+ ASSERT_EQ(bmap.collections_bfs.size(), 0x2000u);
+ ASSERT_EQ(bmap.objects_bfs.size(), 0x2000u);
+
+ coll_t cid;
+ ghobject_t hoid;
+
+ ASSERT_FALSE(bmap.is_used(cid, 0));
+ ASSERT_FALSE(bmap.is_used(hoid, 0));
+ bmap.set_used(0, 1, cid, hoid);
+ ASSERT_TRUE(bmap.is_used(cid, 0));
+ ASSERT_TRUE(bmap.is_used(hoid, 0));
+
+ ASSERT_FALSE(bmap.is_used(cid, 0x1023));
+ ASSERT_FALSE(bmap.is_used(hoid, 0x1023));
+ ASSERT_FALSE(bmap.is_used(cid, 0x2023));
+ ASSERT_FALSE(bmap.is_used(hoid, 0x2023));
+ ASSERT_FALSE(bmap.is_used(cid, 0x3023));
+ ASSERT_FALSE(bmap.is_used(hoid, 0x3023));
+ bmap.set_used(0x1023, 0x3000, cid, hoid);
+ ASSERT_TRUE(bmap.is_used(cid, 0x1023));
+ ASSERT_TRUE(bmap.is_used(hoid, 0x1023));
+ ASSERT_TRUE(bmap.is_used(cid, 0x2023));
+ ASSERT_TRUE(bmap.is_used(hoid, 0x2023));
+ ASSERT_TRUE(bmap.is_used(cid, 0x3023));
+ ASSERT_TRUE(bmap.is_used(hoid, 0x3023));
+
+ ASSERT_FALSE(bmap.is_used(cid, 0x9001));
+ ASSERT_FALSE(bmap.is_used(hoid, 0x9001));
+ ASSERT_FALSE(bmap.is_used(cid, 0xa001));
+ ASSERT_FALSE(bmap.is_used(hoid, 0xa001));
+ ASSERT_FALSE(bmap.is_used(cid, 0xb000));
+ ASSERT_FALSE(bmap.is_used(hoid, 0xb000));
+ ASSERT_FALSE(bmap.is_used(cid, 0xc000));
+ ASSERT_FALSE(bmap.is_used(hoid, 0xc000));
+ bmap.set_used(0x9001, 0x2fff, cid, hoid);
+ ASSERT_TRUE(bmap.is_used(cid, 0x9001));
+ ASSERT_TRUE(bmap.is_used(hoid, 0x9001));
+ ASSERT_TRUE(bmap.is_used(cid, 0xa001));
+ ASSERT_TRUE(bmap.is_used(hoid, 0xa001));
+ ASSERT_TRUE(bmap.is_used(cid, 0xb001));
+ ASSERT_TRUE(bmap.is_used(hoid, 0xb001));
+ ASSERT_FALSE(bmap.is_used(cid, 0xc000));
+ ASSERT_FALSE(bmap.is_used(hoid, 0xc000));
+
+ bmap.set_used(0xa001, 0x2, cid, hoid);
+ ASSERT_TRUE(bmap.is_used(cid, 0x9001));
+ ASSERT_TRUE(bmap.is_used(hoid, 0x9001));
+ ASSERT_TRUE(bmap.is_used(cid, 0xa001));
+ ASSERT_TRUE(bmap.is_used(hoid, 0xa001));
+ ASSERT_TRUE(bmap.is_used(cid, 0xb001));
+ ASSERT_TRUE(bmap.is_used(hoid, 0xb001));
+ ASSERT_FALSE(bmap.is_used(cid, 0xc000));
+ ASSERT_FALSE(bmap.is_used(hoid, 0xc000));
+
+ ASSERT_FALSE(bmap.is_used(cid, 0xc0000));
+ ASSERT_FALSE(bmap.is_used(hoid, 0xc0000));
+ ASSERT_FALSE(bmap.is_used(cid, 0xc1000));
+ ASSERT_FALSE(bmap.is_used(hoid, 0xc1000));
+
+ bmap.set_used(0xc0000, 0x2000, cid, hoid);
+ ASSERT_TRUE(bmap.is_used(cid, 0xc0000));
+ ASSERT_TRUE(bmap.is_used(hoid, 0xc0000));
+ ASSERT_TRUE(bmap.is_used(cid, 0xc1000));
+ ASSERT_TRUE(bmap.is_used(hoid, 0xc1000));
+
+ interval_set<uint64_t> extents;
+ extents.insert(0,0x500);
+ extents.insert(0x800,0x100);
+ extents.insert(0x1000,0x1000);
+ extents.insert(0xa001,1);
+ extents.insert(0xa0000,0xff8);
+
+ ASSERT_EQ(3u, bmap.filter_out(extents));
+ ASSERT_TRUE(bmap.is_used(cid));
+ ASSERT_TRUE(bmap.is_used(hoid));
+
+ BlueStoreRepairer::StoreSpaceTracker bmap2;
+ bmap2.init((uint64_t)0x3223b1d1000, 0x10000);
+ ASSERT_EQ(0x1a0000u, bmap2.granularity);
+ ASSERT_EQ(0x1edae4u, bmap2.collections_bfs.size());
+ ASSERT_EQ(0x1edae4u, bmap2.objects_bfs.size());
+ bmap2.set_used(0x3223b190000, 0x10000, cid, hoid);
+ ASSERT_TRUE(bmap2.is_used(cid, 0x3223b190000));
+ ASSERT_TRUE(bmap2.is_used(hoid, 0x3223b190000));
+ ASSERT_TRUE(bmap2.is_used(cid, 0x3223b19f000));
+ ASSERT_TRUE(bmap2.is_used(hoid, 0x3223b19ffff));
+}
+
+TEST(bluestore_blob_t, unused)
+{
+ {
+ bluestore_blob_t b;
+ uint64_t min_alloc_size = 64 << 10; // 64 kB
+
+ // _do_write_small 0x0~1000
+ uint64_t offset = 0x0;
+ uint64_t length = 0x1000; // 4kB
+ uint64_t suggested_boff = 0;
+ PExtentVector extents;
+ extents.emplace_back(0x1a560000, min_alloc_size);
+ b.allocated(p2align(suggested_boff, min_alloc_size), 0 /*no matter*/, extents);
+ b.mark_used(offset, length);
+ ASSERT_FALSE(b.is_unused(offset, length));
+
+ // _do_write_small 0x2000~1000
+ offset = 0x2000;
+ length = 0x1000;
+ b.add_unused(0, 0x10000);
+ ASSERT_TRUE(b.is_unused(offset, length));
+ b.mark_used(offset, length);
+ ASSERT_FALSE(b.is_unused(offset, length));
+
+ // _do_write_small 0xc000~2000
+ offset = 0xc000;
+ length = 0x2000;
+ ASSERT_TRUE(b.is_unused(offset, length));
+ b.mark_used(offset, length);
+ ASSERT_FALSE(b.is_unused(offset, length));
+ }
+
+ {
+ bluestore_blob_t b;
+ uint64_t min_alloc_size = 64 << 10; // 64 kB
+
+ // _do_write_small 0x11000~1000
+ uint64_t offset = 0x11000;
+ uint64_t length = 0x1000; // 4kB
+ uint64_t suggested_boff = 0x11000;
+ PExtentVector extents;
+ extents.emplace_back(0x1a560000, min_alloc_size);
+ b.allocated(p2align(suggested_boff, min_alloc_size), 0 /*no matter*/, extents);
+ b.add_unused(0, offset);
+ b.add_unused(offset + length, min_alloc_size * 2 - offset - length);
+ b.mark_used(offset, length);
+ ASSERT_FALSE(b.is_unused(offset, length));
+
+ // _do_write_small 0x15000~3000
+ offset = 0x15000;
+ length = 0x3000;
+ ASSERT_TRUE(b.is_unused(offset, length));
+ b.mark_used(offset, length);
+ ASSERT_FALSE(b.is_unused(offset, length));
+ }
+
+ {
+ // reuse blob
+ bluestore_blob_t b;
+ uint64_t min_alloc_size = 64 << 10; // 64 kB
+
+ // _do_write_small 0x2a000~1000
+ // and 0x1d000~1000
+ uint64_t unused_granularity = 0x3000;
+ // offsets and lenght below are selected to
+ // be aligned with unused_granularity
+ uint64_t offset0 = 0x2a000;
+ uint64_t offset = 0x1d000;
+ uint64_t length = 0x1000; // 4kB
+ PExtentVector extents;
+ extents.emplace_back(0x410000, min_alloc_size);
+ b.allocated(p2align(offset0, min_alloc_size), min_alloc_size, extents);
+ b.add_unused(0, min_alloc_size * 3);
+ b.mark_used(offset0, length);
+ ASSERT_FALSE(b.is_unused(offset0, length));
+ ASSERT_TRUE(b.is_unused(offset, length));
+
+ extents.clear();
+ extents.emplace_back(0x430000, min_alloc_size);
+ b.allocated(p2align(offset, min_alloc_size), min_alloc_size, extents);
+ b.mark_used(offset, length);
+ ASSERT_FALSE(b.is_unused(offset0, length));
+ ASSERT_FALSE(b.is_unused(offset, length));
+ ASSERT_FALSE(b.is_unused(offset, unused_granularity));
+
+ ASSERT_TRUE(b.is_unused(0, offset / unused_granularity * unused_granularity));
+ ASSERT_TRUE(b.is_unused(offset + length, offset0 - offset - length));
+ auto end0_aligned = round_up_to(offset0 + length, unused_granularity);
+ ASSERT_TRUE(b.is_unused(end0_aligned, min_alloc_size * 3 - end0_aligned));
+ }
+}
+// This UT is primarily intended to show how repair procedure
+// causes erroneous write to INVALID_OFFSET which is reported in
+// https://tracker.ceph.com/issues/51682
+// Basic map_any functionality is tested as well though.
+//
+TEST(bluestore_blob_t, wrong_map_bl_in_51682)
+{
+ {
+ bluestore_blob_t b;
+ uint64_t min_alloc_size = 4 << 10; // 64 kB
+
+ b.allocated_test(bluestore_pextent_t(0x17ba000, 4 * min_alloc_size));
+ b.allocated_test(bluestore_pextent_t(0x17bf000, 4 * min_alloc_size));
+ b.allocated_test(
+ bluestore_pextent_t(
+ bluestore_pextent_t::INVALID_OFFSET,
+ 1 * min_alloc_size));
+ b.allocated_test(bluestore_pextent_t(0x153c44d000, 7 * min_alloc_size));
+
+ b.mark_used(0, 0x8000);
+ b.mark_used(0x9000, 0x7000);
+
+ string s(0x7000, 'a');
+ bufferlist bl;
+ bl.append(s);
+ const size_t num_expected_entries = 5;
+ uint64_t expected[num_expected_entries][2] = {
+ {0x17ba000, 0x4000},
+ {0x17bf000, 0x3000},
+ {0x17c0000, 0x3000},
+ {0xffffffffffffffff, 0x1000},
+ {0x153c44d000, 0x3000}};
+ size_t expected_pos = 0;
+ b.map_bl(0, bl,
+ [&](uint64_t o, bufferlist& bl) {
+ ASSERT_EQ(o, expected[expected_pos][0]);
+ ASSERT_EQ(bl.length(), expected[expected_pos][1]);
+ ++expected_pos;
+ });
+ // 0x5000 is an improper offset presumably provided when doing a repair
+ b.map_bl(0x5000, bl,
+ [&](uint64_t o, bufferlist& bl) {
+ ASSERT_EQ(o, expected[expected_pos][0]);
+ ASSERT_EQ(bl.length(), expected[expected_pos][1]);
+ ++expected_pos;
+ });
+ ASSERT_EQ(expected_pos, num_expected_entries);
+ }
+}
+
+//---------------------------------------------------------------------------------
+static int verify_extent(const extent_t & ext, const extent_t *ext_arr, uint64_t ext_arr_size, uint64_t idx)
+{
+ const extent_t & ext_ref = ext_arr[idx];
+ if (ext.offset == ext_ref.offset && ext.length == ext_ref.length) {
+ return 0;
+ } else {
+ std::cerr << "mismatch was found at index " << idx << std::endl;
+ if (ext.length == 0) {
+ std::cerr << "Null extent was returned at idx = " << idx << std::endl;
+ }
+ unsigned start = std::max(((int32_t)(idx)-3), 0);
+ unsigned end = std::min(idx+3, ext_arr_size);
+ for (unsigned j = start; j < end; j++) {
+ const extent_t & ext_ref = ext_arr[j];
+ std::cerr << j << ") ref_ext = [" << ext_ref.offset << ", " << ext_ref.length << "]" << std::endl;
+ }
+ std::cerr << idx << ") ext = [" << ext.offset << ", " << ext.length << "]" << std::endl;
+ return -1;
+ }
+}
+
+//---------------------------------------------------------------------------------
+static int test_extents(uint64_t index, extent_t *ext_arr, uint64_t ext_arr_size, SimpleBitmap& sbmap, bool set)
+{
+ const uint64_t MAX_JUMP_BIG = 1523;
+ const uint64_t MAX_JUMP_SMALL = 19;
+ const uint64_t MAX_LEN_BIG = 523;
+ const uint64_t MAX_LEN_SMALL = 23;
+
+ uint64_t n = sbmap.get_size();
+ uint64_t offset = 0;
+ unsigned length, jump, i;
+ for (i = 0; i < ext_arr_size; i++) {
+ if (i & 3) {
+ jump = std::rand() % MAX_JUMP_BIG;
+ } else {
+ jump = std::rand() % MAX_JUMP_SMALL;
+ }
+ offset += jump;
+ if (i & 1) {
+ length = std::rand() % MAX_LEN_BIG;
+ } else {
+ length = std::rand() % MAX_LEN_SMALL;
+ }
+ // make sure no zero length will be used
+ length++;
+ if (offset + length >= n) {
+ break;
+ }
+
+ bool success;
+ if (set) {
+ success = sbmap.set(offset, length);
+ } else {
+ success = sbmap.clr(offset, length);
+ }
+ if (!success) {
+ std::cerr << "Failed sbmap." << (set ? "set(" : "clr(") << offset << ", " << length << ")"<< std::endl;
+ return -1;
+ }
+
+ // if this is not the first entry and no jump -> merge extents
+ if ( (i==0) || (jump > 0) ) {
+ ext_arr[i] = {offset, length};
+ } else {
+ // merge 2 extents
+ i --;
+ ext_arr[i].length += length;
+ }
+ offset += length;
+ }
+ unsigned arr_size = std::min((uint64_t)i, ext_arr_size);
+ std::cout << std::hex << std::right;
+ std::cout << "[" << index << "] " << (set ? "Set::" : "Clr::") << " extents count = 0x" << arr_size;
+ std::cout << std::dec << std::endl;
+
+ offset = 0;
+ extent_t ext;
+ for(unsigned i = 0; i < arr_size; i++) {
+ if (set) {
+ ext = sbmap.get_next_set_extent(offset);
+ } else {
+ ext = sbmap.get_next_clr_extent(offset);
+ }
+
+ if (verify_extent(ext, ext_arr, ext_arr_size, i) != 0) {
+ return -1;
+ }
+ offset = ext.offset + ext.length;
+ }
+
+ if (set) {
+ ext = sbmap.get_next_set_extent(offset);
+ } else {
+ ext = sbmap.get_next_clr_extent(offset);
+ }
+ if (ext.length == 0) {
+ return 0;
+ } else {
+ std::cerr << "sbmap.get_next_" << (set ? "set" : "clr") << "_extent(" << offset << ") return length = " << ext.length << std::endl;
+ return -1;
+ }
+}
+
+//---------------------------------------------------------------------------------
+TEST(SimpleBitmap, basic)
+{
+ const uint64_t MAX_EXTENTS_COUNT = 7131177;
+ std::unique_ptr<extent_t[]> ext_arr = std::make_unique<extent_t[]>(MAX_EXTENTS_COUNT);
+ ASSERT_TRUE(ext_arr != nullptr);
+ const uint64_t BIT_COUNT = 4ULL << 30; // 4Gb = 512MB
+ SimpleBitmap sbmap(g_ceph_context, BIT_COUNT);
+
+ // use current time as seed for random generator
+ std::srand(std::time(nullptr));
+ for (unsigned i = 0; i < 3; i++ ) {
+ memset(ext_arr.get(), 0, sizeof(extent_t)*MAX_EXTENTS_COUNT);
+ sbmap.clear_all();
+ ASSERT_TRUE(test_extents(i, ext_arr.get(), MAX_EXTENTS_COUNT, sbmap, true) == 0);
+
+ memset(ext_arr.get(), 0, sizeof(extent_t)*MAX_EXTENTS_COUNT);
+ sbmap.set_all();
+ ASSERT_TRUE(test_extents(i, ext_arr.get(), MAX_EXTENTS_COUNT, sbmap, false) == 0);
+ }
+}
+
+//---------------------------------------------------------------------------------
+static int test_intersections(unsigned test_idx, SimpleBitmap &sbmap, uint8_t map[], uint64_t map_size)
+{
+ const uint64_t MAX_LEN_BIG = 523;
+ const uint64_t MAX_LEN_SMALL = 23;
+
+ bool success;
+ uint64_t set_op_count = 0, clr_op_count = 0;
+ unsigned length, i;
+ for (i = 0; i < map_size / (MAX_LEN_BIG*2); i++) {
+ uint64_t offset = (std::rand() % (map_size - 1));
+ if (i & 1) {
+ length = std::rand() % MAX_LEN_BIG;
+ } else {
+ length = std::rand() % MAX_LEN_SMALL;
+ }
+ // make sure no zero length will be used
+ length++;
+ if (offset + length >= map_size) {
+ continue;
+ }
+ // 2:1 set/clr
+ bool set = (std::rand() % 3);
+ if (set) {
+ success = sbmap.set(offset, length);
+ memset(map+offset, 0xFF, length);
+ set_op_count++;
+ } else {
+ success = sbmap.clr(offset, length);
+ memset(map+offset, 0x0, length);
+ clr_op_count++;
+ }
+ if (!success) {
+ std::cerr << "Failed sbmap." << (set ? "set(" : "clr(") << offset << ", " << length << ")"<< std::endl;
+ return -1;
+ }
+ }
+
+ uint64_t set_bit_count = 0;
+ uint64_t clr_bit_count = 0;
+ for(uint64_t idx = 0; idx < map_size; idx++) {
+ if (map[idx]) {
+ set_bit_count++;
+ success = sbmap.bit_is_set(idx);
+ } else {
+ clr_bit_count++;
+ success = sbmap.bit_is_clr(idx);
+ }
+ if (!success) {
+ std::cerr << "expected: sbmap.bit_is_" << (map[idx] ? "set(" : "clr(") << idx << ")"<< std::endl;
+ return -1;
+ }
+
+ }
+ std::cout << std::hex << std::right << __func__ ;
+ std::cout << " [" << test_idx << "] set_bit_count = 0x" << std::setfill('0') << std::setw(8) << set_bit_count
+ << ", clr_bit_count = 0x" << std::setfill('0') << std::setw(8) << clr_bit_count
+ << ", sum = 0x" << set_bit_count + clr_bit_count << std::endl;
+ std::cout << std::dec;
+ uint64_t offset = 0;
+ for(uint64_t i = 0; i < (set_op_count + clr_op_count); i++) {
+ extent_t ext = sbmap.get_next_set_extent(offset);
+ //std::cout << "set_ext:: " << i << ") [" << ext.offset << ", " << ext.length << "]" << std::endl;
+ for (uint64_t idx = ext.offset; idx < ext.offset + ext.length; idx++) {
+ if (map[idx] != 0xFF) {
+ std::cerr << "map[" << idx << "] is clear, but extent [" << ext.offset << ", " << ext.length << "] is set" << std::endl;
+ return -1;
+ }
+ }
+ offset = ext.offset + ext.length;
+ }
+
+ offset = 0;
+ for(uint64_t i = 0; i < (set_op_count + clr_op_count); i++) {
+ extent_t ext = sbmap.get_next_clr_extent(offset);
+ //std::cout << "clr_ext:: " << i << ") [" << ext.offset << ", " << ext.length << "]" << std::endl;
+ for (uint64_t idx = ext.offset; idx < ext.offset + ext.length; idx++) {
+ if (map[idx] ) {
+ std::cerr << "map[" << idx << "] is set, but extent [" << ext.offset << ", " << ext.length << "] is free" << std::endl;
+ return -1;
+ }
+ }
+ offset = ext.offset + ext.length;
+ }
+
+ return 0;
+}
+
+//---------------------------------------------------------------------------------
+TEST(SimpleBitmap, intersection)
+{
+ const uint64_t MAP_SIZE = 1ULL << 30; // 1G
+ SimpleBitmap sbmap(g_ceph_context, MAP_SIZE);
+
+ // use current time as seed for random generator
+ std::srand(std::time(nullptr));
+
+ std::unique_ptr<uint8_t[]> map = std::make_unique<uint8_t[]> (MAP_SIZE);
+ ASSERT_TRUE(map != nullptr);
+
+ for (unsigned i = 0; i < 1; i++ ) {
+ sbmap.clear_all();
+ memset(map.get(), 0, MAP_SIZE);
+ ASSERT_TRUE(test_intersections(i, sbmap, map.get(), MAP_SIZE) == 0);
+
+ sbmap.set_all();
+ memset(map.get(), 0xFF, MAP_SIZE);
+ ASSERT_TRUE(test_intersections(i, sbmap, map.get(), MAP_SIZE) == 0);
+ }
+}
+
+
+//---------------------------------------------------------------------------------
+static int test_extents_boundaries(uint64_t index, extent_t *ext_arr, uint64_t ext_arr_size, SimpleBitmap& sbmap, bool set)
+{
+ uint64_t n = sbmap.get_size();
+ uint64_t offset = 0, k = 0;
+ for(unsigned i = 0; i < 64; i++) {
+ offset += i;
+ if (offset >= n) {
+ break;
+ }
+
+ for(unsigned length = 1; length <= 128; length++) {
+ if (offset + length >= n) {
+ break;
+ }
+
+ if (k >= ext_arr_size) {
+ break;
+ }
+ bool success;
+ if (set) {
+ success = sbmap.set(offset, length);
+ } else {
+ success = sbmap.clr(offset, length);
+ }
+ if (!success) {
+ std::cerr << "Failed sbmap." << (set ? "set(" : "clr(") << offset << ", " << length << ")"<< std::endl;
+ return -1;
+ }
+ ext_arr[k++] = {offset, length};
+ if (length < 64) {
+ offset += 64;
+ } else {
+ offset += 128;
+ }
+ }
+ if (k >= ext_arr_size) {
+ break;
+ }
+ }
+
+ unsigned arr_size = std::min((uint64_t)k, ext_arr_size);
+ std::cout << std::hex << std::right << __func__ ;
+ std::cout << " [" << index << "] " << (set ? "Set::" : "Clr::") << " extents count = 0x" << arr_size;
+ std::cout << std::dec << std::endl;
+
+ offset = 0;
+ extent_t ext;
+ for(unsigned i = 0; i < arr_size; i++) {
+ if (set) {
+ ext = sbmap.get_next_set_extent(offset);
+ } else {
+ ext = sbmap.get_next_clr_extent(offset);
+ }
+
+ if (verify_extent(ext, ext_arr, ext_arr_size, i) != 0) {
+ return -1;
+ }
+ offset = ext.offset + ext.length;
+ }
+
+ if (set) {
+ ext = sbmap.get_next_set_extent(offset);
+ } else {
+ ext = sbmap.get_next_clr_extent(offset);
+ }
+ if (ext.length == 0) {
+ return 0;
+ } else {
+ std::cerr << "sbmap.get_next_" << (set ? "set" : "clr") << "_extent(" << offset << ") return length = " << ext.length << std::endl;
+ return -1;
+ }
+
+}
+
+//---------------------------------------------------------------------------------
+TEST(SimpleBitmap, boundaries)
+{
+ const uint64_t MAX_EXTENTS_COUNT = 64 << 10;
+ std::unique_ptr<extent_t[]> ext_arr = std::make_unique<extent_t[]>(MAX_EXTENTS_COUNT);
+ ASSERT_TRUE(ext_arr != nullptr);
+
+ // use current time as seed for random generator
+ std::srand(std::time(nullptr));
+
+ uint64_t bit_count = 32 << 20; // 32Mb = 4MB
+ unsigned count = 0;
+ for (unsigned i = 0; i < 64; i++) {
+ SimpleBitmap sbmap(g_ceph_context, bit_count+i);
+ memset(ext_arr.get(), 0, sizeof(extent_t)*MAX_EXTENTS_COUNT);
+ sbmap.clear_all();
+ ASSERT_TRUE(test_extents_boundaries(count, ext_arr.get(), MAX_EXTENTS_COUNT, sbmap, true) == 0);
+
+ memset(ext_arr.get(), 0, sizeof(extent_t)*MAX_EXTENTS_COUNT);
+ sbmap.set_all();
+ ASSERT_TRUE(test_extents_boundaries(count++, ext_arr.get(), MAX_EXTENTS_COUNT, sbmap, false) == 0);
+ }
+}
+
+//---------------------------------------------------------------------------------
+TEST(SimpleBitmap, boundaries2)
+{
+ const uint64_t bit_count_base = 64 << 10; // 64Kb = 8MB
+ const extent_t null_extent = {0, 0};
+
+ for (unsigned i = 0; i < 64; i++) {
+ uint64_t bit_count = bit_count_base + i;
+ extent_t full_extent = {0, bit_count};
+ SimpleBitmap sbmap(g_ceph_context, bit_count);
+
+ sbmap.set(0, bit_count);
+ ASSERT_TRUE(sbmap.get_next_set_extent(0) == full_extent);
+ ASSERT_TRUE(sbmap.get_next_clr_extent(0) == null_extent);
+
+ for (uint64_t bit = 0; bit < bit_count; bit++) {
+ sbmap.clr(bit, 1);
+ }
+ ASSERT_TRUE(sbmap.get_next_set_extent(0) == null_extent);
+ ASSERT_TRUE(sbmap.get_next_clr_extent(0) == full_extent);
+
+ for (uint64_t bit = 0; bit < bit_count; bit++) {
+ sbmap.set(bit, 1);
+ }
+ ASSERT_TRUE(sbmap.get_next_set_extent(0) == full_extent);
+ ASSERT_TRUE(sbmap.get_next_clr_extent(0) == null_extent);
+
+ sbmap.clr(0, bit_count);
+ ASSERT_TRUE(sbmap.get_next_set_extent(0) == null_extent);
+ ASSERT_TRUE(sbmap.get_next_clr_extent(0) == full_extent);
+ }
+}
+
+TEST(shared_blob_2hash_tracker_t, basic_test)
+{
+ shared_blob_2hash_tracker_t t1(1024 * 1024, 4096);
+
+ ASSERT_TRUE(t1.count_non_zero() == 0);
+
+ t1.inc(0, 0, 1);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(0, 0, -1);
+ ASSERT_TRUE(t1.count_non_zero() == 0);
+
+ t1.inc(3, 0x1000, 2);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(3, 0x1000, -1);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(3, 0x1000, -1);
+ ASSERT_TRUE(t1.count_non_zero() == 0);
+
+ t1.inc(2, 0x2000, 5);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(18, 0x2000, -5);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(18, 0x2000, 1);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(2, 0x2000, -1);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(18, 0x2000, 4);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(2, 0x2000, -4);
+ ASSERT_TRUE(t1.count_non_zero() == 0);
+
+ t1.inc(3, 0x3000, 2);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(4, 0x3000, -1);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(4, 0x3000, -1);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(3, 0x3000, -2);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(4, 0x3000, 1);
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+ t1.inc(4, 0x3000, 1);
+ ASSERT_TRUE(t1.count_non_zero() == 0);
+
+ t1.inc(5, 0x1000, 1);
+ t1.inc(5, 0x2000, 3);
+ t1.inc(5, 0x3000, 2);
+ t1.inc(5, 0x8000, 1);
+
+ ASSERT_TRUE(t1.count_non_zero() != 0);
+
+ ASSERT_TRUE(!t1.test_all_zero(5,0x1000));
+ ASSERT_TRUE(!t1.test_all_zero(5, 0x2000));
+ ASSERT_TRUE(!t1.test_all_zero(5, 0x3000));
+ ASSERT_TRUE(t1.test_all_zero(5, 0x4000));
+ ASSERT_TRUE(!t1.test_all_zero(5, 0x8000));
+
+ ASSERT_TRUE(t1.test_all_zero_range(5, 0, 0x1000));
+ ASSERT_TRUE(t1.test_all_zero_range(5, 0x500, 0x500));
+ ASSERT_TRUE(!t1.test_all_zero_range(5, 0x500, 0x1500));
+ ASSERT_TRUE(!t1.test_all_zero_range(5, 0x1500, 0x3200));
+ ASSERT_TRUE(t1.test_all_zero_range(5, 0x4500, 0x1500));
+ ASSERT_TRUE(t1.test_all_zero_range(5, 0x4500, 0x3b00));
+ ASSERT_TRUE(!t1.test_all_zero_range(5, 0, 0x9000));
+}
+
+TEST(bluestore_blob_use_tracker_t, mempool_stats_test)
+{
+ using mempool::bluestore_cache_other::allocated_items;
+ using mempool::bluestore_cache_other::allocated_bytes;
+ uint64_t other_items0 = allocated_items();
+ uint64_t other_bytes0 = allocated_bytes();
+ {
+ bluestore_blob_use_tracker_t* t1 = new bluestore_blob_use_tracker_t;
+
+ t1->init(1024 * 1024, 4096);
+ ASSERT_EQ(256, allocated_items() - other_items0); // = 1M / 4K
+ ASSERT_EQ(1024, allocated_bytes() - other_bytes0); // = 1M / 4K * 4
+
+ delete t1;
+ ASSERT_EQ(allocated_items(), other_items0);
+ ASSERT_EQ(allocated_bytes(), other_bytes0);
+ }
+ {
+ bluestore_blob_use_tracker_t* t1 = new bluestore_blob_use_tracker_t;
+
+ t1->init(1024 * 1024, 4096);
+ t1->add_tail(2048 * 1024, 4096);
+ // proper stats update after tail add
+ ASSERT_EQ(512, allocated_items() - other_items0); // = 2M / 4K
+ ASSERT_EQ(2048, allocated_bytes() - other_bytes0); // = 2M / 4K * 4
+
+ delete t1;
+ ASSERT_EQ(allocated_items(), other_items0);
+ ASSERT_EQ(allocated_bytes(), other_bytes0);
+ }
+ {
+ bluestore_blob_use_tracker_t* t1 = new bluestore_blob_use_tracker_t;
+
+ t1->init(1024 * 1024, 4096);
+ t1->prune_tail(512 * 1024);
+ // no changes in stats after pruning
+ ASSERT_EQ(256, allocated_items() - other_items0); // = 1M / 4K
+ ASSERT_EQ(1024, allocated_bytes() - other_bytes0); // = 1M / 4K * 4
+
+ delete t1;
+ ASSERT_EQ(allocated_items(), other_items0);
+ ASSERT_EQ(allocated_bytes(), other_bytes0);
+ }
+ {
+ bluestore_blob_use_tracker_t* t1 = new bluestore_blob_use_tracker_t;
+ bluestore_blob_use_tracker_t* t2 = new bluestore_blob_use_tracker_t;
+
+ t1->init(1024 * 1024, 4096);
+
+ // t1 keeps the same amount of entries + t2 has got half of them
+ t1->split(512 * 1024, t2);
+ ASSERT_EQ(256 + 128, allocated_items() - other_items0); //= 1M / 4K*1.5
+ ASSERT_EQ(1024 + 512, allocated_bytes() - other_bytes0); //= 1M / 4K*4*1.5
+
+ // t1 & t2 release everything, then t2 get one less entry than t2 had had
+ // before
+ t1->split(4096, t2);
+ ASSERT_EQ(127, allocated_items() - other_items0); // = 512K / 4K - 1
+ ASSERT_EQ(127 * 4, allocated_bytes() - other_bytes0); // = 512L / 4K * 4 - 4
+ delete t1;
+ delete t2;
+ ASSERT_EQ(allocated_items(), other_items0);
+ ASSERT_EQ(allocated_bytes(), other_bytes0);
+ }
+}
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/objectstore/test_deferred.cc b/src/test/objectstore/test_deferred.cc
new file mode 100644
index 000000000..1b5608101
--- /dev/null
+++ b/src/test/objectstore/test_deferred.cc
@@ -0,0 +1,146 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <memory>
+#include <time.h>
+
+#include "os/ObjectStore.h"
+#include "os/bluestore/BlueStore.h"
+#include "include/Context.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "common/options.h" // for the size literals
+#include <semaphore.h>
+
+
+
+class C_do_action : public Context {
+public:
+ std::function<void()> action;
+ C_do_action(std::function<void()> action)
+ : action(action) {}
+
+ void finish(int r) override {
+ action();
+ }
+};
+
+void create_deferred_and_terminate() {
+ std::unique_ptr<ObjectStore> store;
+
+ g_ceph_context->_conf._clear_safe_to_start_threads();
+ g_ceph_context->_conf.set_val_or_die("bluestore_prefer_deferred_size", "4096");
+ g_ceph_context->_conf.set_val_or_die("bluestore_allocator", "bitmap");
+ g_ceph_context->_conf.set_val_or_die("bluestore_block_size", "10240000000");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ int64_t poolid;
+ coll_t cid;
+ ghobject_t hoid;
+ ObjectStore::CollectionHandle ch;
+ ceph_assert(::mkdir("bluestore.test_temp_dir", 0777) == 0);
+ store = ObjectStore::create(g_ceph_context,
+ "bluestore",
+ "bluestore.test_temp_dir",
+ "store_test_temp_journal");
+ ceph_assert(store->mkfs() == 0);
+ ceph_assert(store->mount() == 0);
+
+ poolid = 11;
+ cid = coll_t(spg_t(pg_t(1, poolid), shard_id_t::NO_SHARD));
+ ch = store->create_new_collection(cid);
+ int r;
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ r = store->queue_transaction(ch, std::move(t));
+ ceph_assert(r == 0);
+ }
+
+ {
+ ObjectStore::Transaction t;
+ std::string oid = "zapchajdziura";
+ ghobject_t hoid(hobject_t(oid, "", CEPH_NOSNAP, 1, poolid, ""));
+ bufferlist bl;
+ bl.append(std::string(0xe000, '-'));
+ t.write(cid, hoid, 0, 0xe000, bl);
+ r = store->queue_transaction(ch, std::move(t));
+ ceph_assert(r == 0);
+ }
+
+ size_t object_count = 10;
+
+ // initial fill
+ bufferlist bl_64K;
+ bl_64K.append(std::string(64 * 1024, '-'));
+
+ std::atomic<size_t> prefill_counter{0};
+ sem_t prefill_mutex;
+ sem_init(&prefill_mutex, 0, 0);
+
+ for (size_t o = 0; o < object_count; o++) {
+ ObjectStore::Transaction t;
+ std::string oid = "object-" + std::to_string(o);
+ ghobject_t hoid(hobject_t(oid, "", CEPH_NOSNAP, 1, poolid, ""));
+
+ t.write(cid, hoid, 0, bl_64K.length(), bl_64K);
+ t.register_on_commit(new C_do_action([&] {
+ if (++prefill_counter == object_count) {
+ sem_post(&prefill_mutex);
+ }
+ }));
+
+ r = store->queue_transaction(ch, std::move(t));
+ ceph_assert(r == 0);
+ }
+ sem_wait(&prefill_mutex);
+
+ // small deferred writes over object
+ // and complete overwrite of previous one
+ bufferlist bl_8_bytes;
+ bl_8_bytes.append("abcdefgh");
+ std::atomic<size_t> deferred_counter{0};
+ for (size_t o = 0; o < object_count - 1; o++) {
+ ObjectStore::Transaction t;
+
+ // sprinkle deferred writes
+ std::string oid_d = "object-" + std::to_string(o + 1);
+ ghobject_t hoid_d(hobject_t(oid_d, "", CEPH_NOSNAP, 1, poolid, ""));
+
+ for(int i = 0; i < 16; i++) {
+ t.write(cid, hoid_d, 4096 * i, bl_8_bytes.length(), bl_8_bytes);
+ }
+
+ // overwrite previous object
+ std::string oid_m = "object-" + std::to_string(o);
+ ghobject_t hoid_m(hobject_t(oid_m, "", CEPH_NOSNAP, 1, poolid, ""));
+ t.write(cid, hoid_m, 0, bl_64K.length(), bl_64K);
+
+ t.register_on_commit(new C_do_action([&] {
+ if (++deferred_counter == object_count - 1) {
+ exit(0);
+ }
+ }));
+ r = store->queue_transaction(ch, std::move(t));
+ ceph_assert(r == 0);
+ }
+ sleep(10);
+ ceph_assert(0 && "should not reach here");
+}
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ create_deferred_and_terminate();
+ return 0;
+}
diff --git a/src/test/objectstore/test_kv.cc b/src/test/objectstore/test_kv.cc
new file mode 100644
index 000000000..33ffd6ab3
--- /dev/null
+++ b/src/test/objectstore/test_kv.cc
@@ -0,0 +1,1304 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <time.h>
+#include <sys/mount.h>
+#include "kv/KeyValueDB.h"
+#include "kv/RocksDBStore.h"
+#include "include/Context.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "include/stringify.h"
+#include <gtest/gtest.h>
+
+using namespace std;
+
+class KVTest : public ::testing::TestWithParam<const char*> {
+public:
+ boost::scoped_ptr<KeyValueDB> db;
+
+ KVTest() : db(0) {}
+
+ string _bl_to_str(bufferlist val) {
+ string str(val.c_str(), val.length());
+ return str;
+ }
+
+ void rm_r(string path) {
+ string cmd = string("rm -r ") + path;
+ cout << "==> " << cmd << std::endl;
+ int r = ::system(cmd.c_str());
+ if (r) {
+ cerr << "failed with exit code " << r
+ << ", continuing anyway" << std::endl;
+ }
+ }
+
+ void init() {
+ cout << "Creating " << string(GetParam()) << "\n";
+ db.reset(KeyValueDB::create(g_ceph_context, string(GetParam()),
+ "kv_test_temp_dir"));
+ }
+ void fini() {
+ db.reset(NULL);
+ }
+
+ void SetUp() override {
+ int r = ::mkdir("kv_test_temp_dir", 0777);
+ if (r < 0 && errno != EEXIST) {
+ r = -errno;
+ cerr << __func__ << ": unable to create kv_test_temp_dir: "
+ << cpp_strerror(r) << std::endl;
+ return;
+ }
+ init();
+ }
+ void TearDown() override {
+ fini();
+ rm_r("kv_test_temp_dir");
+ }
+};
+
+TEST_P(KVTest, OpenClose) {
+ ASSERT_EQ(0, db->create_and_open(cout));
+ db->close();
+ db->open(cout);
+ fini();
+}
+
+TEST_P(KVTest, OpenCloseReopenClose) {
+ ASSERT_EQ(0, db->create_and_open(cout));
+ fini();
+ init();
+ ASSERT_EQ(0, db->open(cout));
+ fini();
+}
+
+/*
+ * Basic write and read test case in same database session.
+ */
+TEST_P(KVTest, OpenWriteRead) {
+ ASSERT_EQ(0, db->create_and_open(cout));
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist value;
+ value.append("value");
+ t->set("prefix", "key", value);
+ value.clear();
+ value.append("value2");
+ t->set("prefix", "key2", value);
+ value.clear();
+ value.append("value3");
+ t->set("prefix", "key3", value);
+ db->submit_transaction_sync(t);
+
+ bufferlist v1, v2;
+ ASSERT_EQ(0, db->get("prefix", "key", &v1));
+ ASSERT_EQ(v1.length(), 5u);
+ (v1.c_str())[v1.length()] = 0x0;
+ ASSERT_EQ(std::string(v1.c_str()), std::string("value"));
+ ASSERT_EQ(0, db->get("prefix", "key2", &v2));
+ ASSERT_EQ(v2.length(), 6u);
+ (v2.c_str())[v2.length()] = 0x0;
+ ASSERT_EQ(std::string(v2.c_str()), std::string("value2"));
+ }
+ fini();
+}
+
+TEST_P(KVTest, PutReopen) {
+ ASSERT_EQ(0, db->create_and_open(cout));
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist value;
+ value.append("value");
+ t->set("prefix", "key", value);
+ t->set("prefix", "key2", value);
+ t->set("prefix", "key3", value);
+ db->submit_transaction_sync(t);
+ }
+ fini();
+
+ init();
+ ASSERT_EQ(0, db->open(cout));
+ {
+ bufferlist v1, v2;
+ ASSERT_EQ(0, db->get("prefix", "key", &v1));
+ ASSERT_EQ(v1.length(), 5u);
+ ASSERT_EQ(0, db->get("prefix", "key2", &v2));
+ ASSERT_EQ(v2.length(), 5u);
+ }
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->rmkey("prefix", "key");
+ t->rmkey("prefix", "key3");
+ db->submit_transaction_sync(t);
+ }
+ fini();
+
+ init();
+ ASSERT_EQ(0, db->open(cout));
+ {
+ bufferlist v1, v2, v3;
+ ASSERT_EQ(-ENOENT, db->get("prefix", "key", &v1));
+ ASSERT_EQ(0, db->get("prefix", "key2", &v2));
+ ASSERT_EQ(v2.length(), 5u);
+ ASSERT_EQ(-ENOENT, db->get("prefix", "key3", &v3));
+ }
+ fini();
+}
+
+TEST_P(KVTest, BenchCommit) {
+ int n = 1024;
+ ASSERT_EQ(0, db->create_and_open(cout));
+ utime_t start = ceph_clock_now();
+ {
+ cout << "priming" << std::endl;
+ // prime
+ bufferlist big;
+ bufferptr bp(1048576);
+ bp.zero();
+ big.append(bp);
+ for (int i=0; i<30; ++i) {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->set("prefix", "big" + stringify(i), big);
+ db->submit_transaction_sync(t);
+ }
+ }
+ cout << "now doing small writes" << std::endl;
+ bufferlist data;
+ bufferptr bp(1024);
+ bp.zero();
+ data.append(bp);
+ for (int i=0; i<n; ++i) {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->set("prefix", "key" + stringify(i), data);
+ db->submit_transaction_sync(t);
+ }
+ utime_t end = ceph_clock_now();
+ utime_t dur = end - start;
+ cout << n << " commits in " << dur << ", avg latency " << (dur / (double)n)
+ << std::endl;
+ fini();
+}
+
+struct AppendMOP : public KeyValueDB::MergeOperator {
+ void merge_nonexistent(
+ const char *rdata, size_t rlen, std::string *new_value) override {
+ *new_value = "?" + std::string(rdata, rlen);
+ }
+ void merge(
+ const char *ldata, size_t llen,
+ const char *rdata, size_t rlen,
+ std::string *new_value) override {
+ *new_value = std::string(ldata, llen) + std::string(rdata, rlen);
+ }
+ // We use each operator name and each prefix to construct the
+ // overall RocksDB operator name for consistency check at open time.
+ const char *name() const override {
+ return "Append";
+ }
+};
+
+string tostr(bufferlist& b) {
+ return string(b.c_str(),b.length());
+}
+
+TEST_P(KVTest, Merge) {
+ shared_ptr<KeyValueDB::MergeOperator> p(new AppendMOP);
+ int r = db->set_merge_operator("A",p);
+ if (r < 0)
+ return; // No merge operators for this database type
+ ASSERT_EQ(0, db->create_and_open(cout));
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist v1, v2, v3;
+ v1.append(string("1"));
+ v2.append(string("2"));
+ v3.append(string("3"));
+ t->set("P", "K1", v1);
+ t->set("A", "A1", v2);
+ t->rmkey("A", "A2");
+ t->merge("A", "A2", v3);
+ db->submit_transaction_sync(t);
+ }
+ {
+ bufferlist v1, v2, v3;
+ ASSERT_EQ(0, db->get("P", "K1", &v1));
+ ASSERT_EQ(tostr(v1), "1");
+ ASSERT_EQ(0, db->get("A", "A1", &v2));
+ ASSERT_EQ(tostr(v2), "2");
+ ASSERT_EQ(0, db->get("A", "A2", &v3));
+ ASSERT_EQ(tostr(v3), "?3");
+ }
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist v1;
+ v1.append(string("1"));
+ t->merge("A", "A2", v1);
+ db->submit_transaction_sync(t);
+ }
+ {
+ bufferlist v;
+ ASSERT_EQ(0, db->get("A", "A2", &v));
+ ASSERT_EQ(tostr(v), "?31");
+ }
+ fini();
+}
+
+TEST_P(KVTest, RMRange) {
+ ASSERT_EQ(0, db->create_and_open(cout));
+ bufferlist value;
+ value.append("value");
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->set("prefix", "key1", value);
+ t->set("prefix", "key2", value);
+ t->set("prefix", "key3", value);
+ t->set("prefix", "key4", value);
+ t->set("prefix", "key45", value);
+ t->set("prefix", "key5", value);
+ t->set("prefix", "key6", value);
+ db->submit_transaction_sync(t);
+ }
+
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->set("prefix", "key7", value);
+ t->set("prefix", "key8", value);
+ t->rm_range_keys("prefix", "key2", "key7");
+ db->submit_transaction_sync(t);
+ bufferlist v1, v2;
+ ASSERT_EQ(0, db->get("prefix", "key1", &v1));
+ v1.clear();
+ ASSERT_EQ(-ENOENT, db->get("prefix", "key45", &v1));
+ ASSERT_EQ(0, db->get("prefix", "key8", &v1));
+ v1.clear();
+ ASSERT_EQ(-ENOENT, db->get("prefix", "key2", &v1));
+ ASSERT_EQ(0, db->get("prefix", "key7", &v2));
+ }
+
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->rm_range_keys("prefix", "key", "key");
+ db->submit_transaction_sync(t);
+ bufferlist v1, v2;
+ ASSERT_EQ(0, db->get("prefix", "key1", &v1));
+ ASSERT_EQ(0, db->get("prefix", "key8", &v2));
+ }
+
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->rm_range_keys("prefix", "key-", "key~");
+ db->submit_transaction_sync(t);
+ bufferlist v1, v2;
+ ASSERT_EQ(-ENOENT, db->get("prefix", "key1", &v1));
+ ASSERT_EQ(-ENOENT, db->get("prefix", "key8", &v2));
+ }
+
+ fini();
+}
+
+TEST_P(KVTest, ShardingRMRange) {
+ if(string(GetParam()) != "rocksdb")
+ return;
+ std::string cfs("O(7)=");
+ ASSERT_EQ(0, db->create_and_open(cout, cfs));
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ for (size_t i = 0; i < 1000; i++) {
+ bufferlist value;
+ char* a;
+ ASSERT_EQ(asprintf(&a, "key%3.3ld", i), 6);
+ value.append(a);
+ t->set("O", a, value);
+ free(a);
+ }
+ db->submit_transaction_sync(t);
+ }
+
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->rm_range_keys("O", "key277", "key467");
+ db->submit_transaction_sync(t);
+ }
+
+ for (size_t i = 0; i < 1000; i++) {
+ char* key;
+ ASSERT_EQ(asprintf(&key, "key%3.3ld", i), 6);
+ bufferlist value;
+ int r = db->get("O", key, &value);
+ ASSERT_EQ(r, (i >= 277 && i < 467 ? -ENOENT : 0));
+ free(key);
+ }
+
+ fini();
+}
+
+
+TEST_P(KVTest, RocksDBColumnFamilyTest) {
+ if(string(GetParam()) != "rocksdb")
+ return;
+
+ std::string cfs("cf1 cf2");
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ cout << "creating two column families and opening them" << std::endl;
+ ASSERT_EQ(0, db->create_and_open(cout, cfs));
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist value;
+ value.append("value");
+ cout << "write a transaction includes three keys in different CFs" << std::endl;
+ t->set("prefix", "key", value);
+ t->set("cf1", "key", value);
+ t->set("cf2", "key2", value);
+ ASSERT_EQ(0, db->submit_transaction_sync(t));
+ }
+ fini();
+
+ init();
+ ASSERT_EQ(0, db->open(cout, cfs));
+ {
+ bufferlist v1, v2, v3;
+ cout << "reopen db and read those keys" << std::endl;
+ ASSERT_EQ(0, db->get("prefix", "key", &v1));
+ ASSERT_EQ(0, _bl_to_str(v1) != "value");
+ ASSERT_EQ(0, db->get("cf1", "key", &v2));
+ ASSERT_EQ(0, _bl_to_str(v2) != "value");
+ ASSERT_EQ(0, db->get("cf2", "key2", &v3));
+ ASSERT_EQ(0, _bl_to_str(v2) != "value");
+ }
+ {
+ cout << "delete two keys in CFs" << std::endl;
+ KeyValueDB::Transaction t = db->get_transaction();
+ t->rmkey("prefix", "key");
+ t->rmkey("cf2", "key2");
+ ASSERT_EQ(0, db->submit_transaction_sync(t));
+ }
+ fini();
+
+ init();
+ ASSERT_EQ(0, db->open(cout, cfs));
+ {
+ cout << "reopen db and read keys again." << std::endl;
+ bufferlist v1, v2, v3;
+ ASSERT_EQ(-ENOENT, db->get("prefix", "key", &v1));
+ ASSERT_EQ(0, db->get("cf1", "key", &v2));
+ ASSERT_EQ(0, _bl_to_str(v2) != "value");
+ ASSERT_EQ(-ENOENT, db->get("cf2", "key2", &v3));
+ }
+ fini();
+}
+
+TEST_P(KVTest, RocksDBIteratorTest) {
+ if(string(GetParam()) != "rocksdb")
+ return;
+
+ std::string cfs("cf1");
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ cout << "creating one column family and opening it" << std::endl;
+ ASSERT_EQ(0, db->create_and_open(cout, cfs));
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist bl1;
+ bl1.append("hello");
+ bufferlist bl2;
+ bl2.append("world");
+ cout << "write some kv pairs into default and new CFs" << std::endl;
+ t->set("prefix", "key1", bl1);
+ t->set("prefix", "key2", bl2);
+ t->set("cf1", "key1", bl1);
+ t->set("cf1", "key2", bl2);
+ ASSERT_EQ(0, db->submit_transaction_sync(t));
+ }
+ {
+ cout << "iterating the default CF" << std::endl;
+ KeyValueDB::Iterator iter = db->get_iterator("prefix");
+ iter->seek_to_first();
+ ASSERT_EQ(1, iter->valid());
+ ASSERT_EQ("key1", iter->key());
+ ASSERT_EQ("hello", _bl_to_str(iter->value()));
+ ASSERT_EQ(0, iter->next());
+ ASSERT_EQ(1, iter->valid());
+ ASSERT_EQ("key2", iter->key());
+ ASSERT_EQ("world", _bl_to_str(iter->value()));
+ }
+ {
+ cout << "iterating the new CF" << std::endl;
+ KeyValueDB::Iterator iter = db->get_iterator("cf1");
+ iter->seek_to_first();
+ ASSERT_EQ(1, iter->valid());
+ ASSERT_EQ("key1", iter->key());
+ ASSERT_EQ("hello", _bl_to_str(iter->value()));
+ ASSERT_EQ(0, iter->next());
+ ASSERT_EQ(1, iter->valid());
+ ASSERT_EQ("key2", iter->key());
+ ASSERT_EQ("world", _bl_to_str(iter->value()));
+ }
+ fini();
+}
+
+TEST_P(KVTest, RocksDBShardingIteratorTest) {
+ if(string(GetParam()) != "rocksdb")
+ return;
+
+ std::string cfs("A(6)");
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ cout << "creating one column family and opening it" << std::endl;
+ ASSERT_EQ(0, db->create_and_open(cout, cfs));
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ for (int v = 100; v <= 999; v++) {
+ std::string str = to_string(v);
+ bufferlist val;
+ val.append(str);
+ t->set("A", str, val);
+ }
+ ASSERT_EQ(0, db->submit_transaction_sync(t));
+ }
+ {
+ KeyValueDB::Iterator it = db->get_iterator("A");
+ int pos = 0;
+ ASSERT_EQ(it->lower_bound(to_string(pos)), 0);
+ for (pos = 100; pos <= 999; pos++) {
+ ASSERT_EQ(it->valid(), true);
+ ASSERT_EQ(it->key(), to_string(pos));
+ ASSERT_EQ(it->value().to_str(), to_string(pos));
+ it->next();
+ }
+ ASSERT_EQ(it->valid(), false);
+ pos = 999;
+ ASSERT_EQ(it->lower_bound(to_string(pos)), 0);
+ for (pos = 999; pos >= 100; pos--) {
+ ASSERT_EQ(it->valid(), true);
+ ASSERT_EQ(it->key(), to_string(pos));
+ ASSERT_EQ(it->value().to_str(), to_string(pos));
+ it->prev();
+ }
+ ASSERT_EQ(it->valid(), false);
+ }
+ fini();
+}
+
+TEST_P(KVTest, RocksDBCFMerge) {
+ if(string(GetParam()) != "rocksdb")
+ return;
+
+ shared_ptr<KeyValueDB::MergeOperator> p(new AppendMOP);
+ int r = db->set_merge_operator("cf1",p);
+ if (r < 0)
+ return; // No merge operators for this database type
+ std::string cfs("cf1");
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ cout << "creating one column family and opening it" << std::endl;
+ ASSERT_EQ(0, db->create_and_open(cout, cfs));
+
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist v1, v2, v3;
+ v1.append(string("1"));
+ v2.append(string("2"));
+ v3.append(string("3"));
+ t->set("P", "K1", v1);
+ t->set("cf1", "A1", v2);
+ t->rmkey("cf1", "A2");
+ t->merge("cf1", "A2", v3);
+ db->submit_transaction_sync(t);
+ }
+ {
+ bufferlist v1, v2, v3;
+ ASSERT_EQ(0, db->get("P", "K1", &v1));
+ ASSERT_EQ(tostr(v1), "1");
+ ASSERT_EQ(0, db->get("cf1", "A1", &v2));
+ ASSERT_EQ(tostr(v2), "2");
+ ASSERT_EQ(0, db->get("cf1", "A2", &v3));
+ ASSERT_EQ(tostr(v3), "?3");
+ }
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist v1;
+ v1.append(string("1"));
+ t->merge("cf1", "A2", v1);
+ db->submit_transaction_sync(t);
+ }
+ {
+ bufferlist v;
+ ASSERT_EQ(0, db->get("cf1", "A2", &v));
+ ASSERT_EQ(tostr(v), "?31");
+ }
+ fini();
+}
+
+TEST_P(KVTest, RocksDB_estimate_size) {
+ if(string(GetParam()) != "rocksdb")
+ GTEST_SKIP();
+
+ std::string cfs("cf1");
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ cout << "creating one column family and opening it" << std::endl;
+ ASSERT_EQ(0, db->create_and_open(cout));
+
+ for(int test = 0; test < 20; test++)
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist v1;
+ v1.append(string(1000, '1'));
+ for (int i = 0; i < 100; i++)
+ t->set("A", to_string(rand()%100000), v1);
+ db->submit_transaction_sync(t);
+ db->compact();
+
+ int64_t size_a = db->estimate_prefix_size("A","");
+ ASSERT_GT(size_a, (test + 1) * 1000 * 100 * 0.5);
+ ASSERT_LT(size_a, (test + 1) * 1000 * 100 * 1.5);
+ int64_t size_a1 = db->estimate_prefix_size("A","1");
+ ASSERT_GT(size_a1, (test + 1) * 1000 * 100 * 0.1 * 0.5);
+ ASSERT_LT(size_a1, (test + 1) * 1000 * 100 * 0.1 * 1.5);
+ int64_t size_b = db->estimate_prefix_size("B","");
+ ASSERT_EQ(size_b, 0);
+ }
+
+ fini();
+}
+
+TEST_P(KVTest, RocksDB_estimate_size_column_family) {
+ if(string(GetParam()) != "rocksdb")
+ GTEST_SKIP();
+
+ std::string cfs("cf1");
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ cout << "creating one column family and opening it" << std::endl;
+ ASSERT_EQ(0, db->create_and_open(cout, cfs));
+
+ for(int test = 0; test < 20; test++)
+ {
+ KeyValueDB::Transaction t = db->get_transaction();
+ bufferlist v1;
+ v1.append(string(1000, '1'));
+ for (int i = 0; i < 100; i++)
+ t->set("cf1", to_string(rand()%100000), v1);
+ db->submit_transaction_sync(t);
+ db->compact();
+
+ int64_t size_a = db->estimate_prefix_size("cf1","");
+ ASSERT_GT(size_a, (test + 1) * 1000 * 100 * 0.5);
+ ASSERT_LT(size_a, (test + 1) * 1000 * 100 * 1.5);
+ int64_t size_a1 = db->estimate_prefix_size("cf1","1");
+ ASSERT_GT(size_a1, (test + 1) * 1000 * 100 * 0.1 * 0.5);
+ ASSERT_LT(size_a1, (test + 1) * 1000 * 100 * 0.1 * 1.5);
+ int64_t size_b = db->estimate_prefix_size("B","");
+ ASSERT_EQ(size_b, 0);
+ }
+
+ fini();
+}
+
+TEST_P(KVTest, RocksDB_parse_sharding_def) {
+ if(string(GetParam()) != "rocksdb")
+ GTEST_SKIP();
+
+ bool result;
+ std::vector<RocksDBStore::ColumnFamily> sharding_def;
+ char const* error_position = nullptr;
+ std::string error_msg;
+
+ std::string_view text_def = "A(10,0-30) B(6)=option1,option2=aaaa C";
+ result = RocksDBStore::parse_sharding_def(text_def,
+ sharding_def,
+ &error_position,
+ &error_msg);
+
+ ASSERT_EQ(result, true);
+ ASSERT_EQ(error_position, nullptr);
+ ASSERT_EQ(error_msg, "");
+ std::cout << text_def << std::endl;
+ if (error_position) std::cout << std::string(error_position - text_def.begin(), ' ') << "^" << error_msg << std::endl;
+
+ ASSERT_EQ(sharding_def.size(), 3);
+ ASSERT_EQ(sharding_def[0].name, "A");
+ ASSERT_EQ(sharding_def[0].shard_cnt, 10);
+ ASSERT_EQ(sharding_def[0].hash_l, 0);
+ ASSERT_EQ(sharding_def[0].hash_h, 30);
+
+ ASSERT_EQ(sharding_def[1].name, "B");
+ ASSERT_EQ(sharding_def[1].shard_cnt, 6);
+ ASSERT_EQ(sharding_def[1].options, "option1,option2=aaaa");
+ ASSERT_EQ(sharding_def[2].name, "C");
+ ASSERT_EQ(sharding_def[2].shard_cnt, 1);
+
+
+ text_def = "A(10 B(6)=option C";
+ result = RocksDBStore::parse_sharding_def(text_def,
+ sharding_def,
+ &error_position,
+ &error_msg);
+ std::cout << text_def << std::endl;
+ if (error_position)
+ std::cout << std::string(error_position - text_def.begin(), ' ') << "^" << error_msg << std::endl;
+ ASSERT_EQ(result, false);
+ ASSERT_NE(error_position, nullptr);
+ ASSERT_NE(error_msg, "");
+
+ text_def = "A(10,1) B(6)=option C";
+ result = RocksDBStore::parse_sharding_def(text_def,
+ sharding_def,
+ &error_position,
+ &error_msg);
+ std::cout << text_def << std::endl;
+ std::cout << std::string(error_position - text_def.begin(), ' ') << "^" << error_msg << std::endl;
+ ASSERT_EQ(result, false);
+ ASSERT_NE(error_position, nullptr);
+ ASSERT_NE(error_msg, "");
+}
+
+
+
+class RocksDBShardingTest : public ::testing::TestWithParam<const char*> {
+public:
+ boost::scoped_ptr<KeyValueDB> db;
+
+ RocksDBShardingTest() : db(0) {}
+
+ string _bl_to_str(bufferlist val) {
+ string str(val.c_str(), val.length());
+ return str;
+ }
+
+ void rm_r(string path) {
+ string cmd = string("rm -r ") + path;
+ if (verbose)
+ cout << "==> " << cmd << std::endl;
+ int r = ::system(cmd.c_str());
+ if (r) {
+ cerr << "failed with exit code " << r
+ << ", continuing anyway" << std::endl;
+ }
+ }
+
+ void SetUp() override {
+ verbose = getenv("VERBOSE") && strcmp(getenv("VERBOSE"), "1") == 0;
+
+ int r = ::mkdir("kv_test_temp_dir", 0777);
+ if (r < 0 && errno != EEXIST) {
+ r = -errno;
+ cerr << __func__ << ": unable to create kv_test_temp_dir: "
+ << cpp_strerror(r) << std::endl;
+ return;
+ }
+ db.reset(KeyValueDB::create(g_ceph_context, "rocksdb",
+ "kv_test_temp_dir"));
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ if (verbose)
+ cout << "Creating database with sharding: " << GetParam() << std::endl;
+ ASSERT_EQ(0, db->create_and_open(cout, GetParam()));
+ }
+ void TearDown() override {
+ db.reset(nullptr);
+ rm_r("kv_test_temp_dir");
+ }
+
+ /*
+ A - main 0/1/20
+ B - shard 1/3 x 0/1/20
+ C - main 0/1/20
+ D - shard 1/3 x 0/1/20
+ E - main 0/1/20
+ */
+ bool verbose;
+ std::vector<std::string> sharding_defs = {
+ "Betelgeuse D",
+ "Betelgeuse(3) D",
+ "Betelgeuse D(3)",
+ "Betelgeuse(3) D(3)"};
+ std::vector<std::string> prefixes = {"Ad", "Betelgeuse", "C", "D", "Evade"};
+ std::vector<std::string> randoms = {"0", "1", "2", "3", "4", "5",
+ "found", "brain", "fully", "pen", "worth", "race",
+ "stand", "nodded", "whenever", "surrounded", "industrial", "skin",
+ "this", "direction", "family", "beginning", "whenever", "held",
+ "metal", "year", "like", "valuable", "softly", "whistle",
+ "perfectly", "broken", "idea", "also", "coffee", "branch",
+ "tongue", "immediately", "bent", "partly", "burn", "include",
+ "certain", "burst", "final", "smoke", "positive", "perfectly"
+ };
+ int R = randoms.size();
+
+ typedef int test_id[6];
+ void zero(test_id& x) {
+ k = 0;
+ v = 0;
+ for (auto& i:x)
+ i = 0;
+ }
+ bool end(const test_id& x) {
+ return x[5] != 0;
+ }
+ void next(test_id& x) {
+ x[0]++;
+ for (int i = 0; i < 5; i++) {
+ if (x[i] == 3) {
+ x[i] = 0;
+ ++x[i + 1];
+ }
+ }
+ }
+
+ std::map<std::string, std::string> data;
+ int k = 0;
+ int v = 0;
+
+ void generate_data(const test_id& x) {
+ data.clear();
+ for (int i = 0; i < 5; i++) {
+ if (verbose)
+ std::cout << x[i] << "-";
+ switch (x[i]) {
+ case 0:
+ break;
+ case 1:
+ data[RocksDBStore::combine_strings(prefixes[i], randoms[k++ % R])] = randoms[v++ % R];
+ break;
+ case 2:
+ std::string base = randoms[k++ % R];
+ for (int j = 0; j < 10; j++) {
+ data[RocksDBStore::combine_strings(prefixes[i], base + "." + randoms[k++ % R])] = randoms[v++ % R];
+ }
+ break;
+ }
+ }
+ }
+
+ void data_to_db() {
+ KeyValueDB::Transaction t = db->get_transaction();
+ for (auto &d : data) {
+ bufferlist v1;
+ v1.append(d.second);
+ string prefix;
+ string key;
+ RocksDBStore::split_key(d.first, &prefix, &key);
+ t->set(prefix, key, v1);
+ if (verbose)
+ std::cout << "SET " << prefix << " " << key << std::endl;
+ }
+ ASSERT_EQ(db->submit_transaction_sync(t), 0);
+ }
+
+ void clear_db() {
+ KeyValueDB::Transaction t = db->get_transaction();
+ for (auto &d : data) {
+ string prefix;
+ string key;
+ RocksDBStore::split_key(d.first, &prefix, &key);
+ t->rmkey(prefix, key);
+ }
+ ASSERT_EQ(db->submit_transaction_sync(t), 0);
+ //paranoid, check if db empty
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ ASSERT_EQ(it->seek_to_first(), 0);
+ ASSERT_EQ(it->valid(), false);
+ }
+};
+
+TEST_P(RocksDBShardingTest, wholespace_next) {
+ test_id X;
+ zero(X);
+ do {
+ generate_data(X);
+ data_to_db();
+
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ //move forward
+ auto dit = data.begin();
+ int r = it->seek_to_first();
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(it->valid(), (dit != data.end()));
+
+ while (dit != data.end()) {
+ ASSERT_EQ(it->valid(), true);
+ string prefix;
+ string key;
+ RocksDBStore::split_key(dit->first, &prefix, &key);
+ auto raw_key = it->raw_key();
+ ASSERT_EQ(raw_key.first, prefix);
+ ASSERT_EQ(raw_key.second, key);
+ ASSERT_EQ(it->value().to_str(), dit->second);
+ if (verbose)
+ std::cout << "next " << prefix << " " << key << std::endl;
+ ASSERT_EQ(it->next(), 0);
+ ++dit;
+ }
+ ASSERT_EQ(it->valid(), false);
+
+ clear_db();
+ next(X);
+ } while (!end(X));
+}
+
+TEST_P(RocksDBShardingTest, wholespace_prev) {
+ test_id X;
+ zero(X);
+ do {
+ generate_data(X);
+ data_to_db();
+
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ auto dit = data.rbegin();
+ int r = it->seek_to_last();
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(it->valid(), (dit != data.rend()));
+
+ while (dit != data.rend()) {
+ ASSERT_EQ(it->valid(), true);
+ string prefix;
+ string key;
+ RocksDBStore::split_key(dit->first, &prefix, &key);
+ auto raw_key = it->raw_key();
+ ASSERT_EQ(raw_key.first, prefix);
+ ASSERT_EQ(raw_key.second, key);
+ ASSERT_EQ(it->value().to_str(), dit->second);
+ if (verbose)
+ std::cout << "prev " << prefix << " " << key << std::endl;
+ ASSERT_EQ(it->prev(), 0);
+ ++dit;
+ }
+ ASSERT_EQ(it->valid(), false);
+
+ clear_db();
+ next(X);
+ } while (!end(X));
+}
+
+TEST_P(RocksDBShardingTest, wholespace_lower_bound) {
+ test_id X;
+ zero(X);
+ do {
+ generate_data(X);
+ data_to_db();
+
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ auto dit = data.begin();
+ int r = it->seek_to_first();
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(it->valid(), (dit != data.end()));
+
+ while (dit != data.end()) {
+ ASSERT_EQ(it->valid(), true);
+ string prefix;
+ string key;
+ RocksDBStore::split_key(dit->first, &prefix, &key);
+ KeyValueDB::WholeSpaceIterator it1 = db->get_wholespace_iterator();
+ ASSERT_EQ(it1->lower_bound(prefix, key), 0);
+ ASSERT_EQ(it1->valid(), true);
+ auto raw_key = it1->raw_key();
+ ASSERT_EQ(raw_key.first, prefix);
+ ASSERT_EQ(raw_key.second, key);
+ if (verbose)
+ std::cout << "lower_bound " << prefix << " " << key << std::endl;
+ ASSERT_EQ(it->next(), 0);
+ ++dit;
+ }
+ ASSERT_EQ(it->valid(), false);
+
+ clear_db();
+ next(X);
+ } while (!end(X));
+}
+
+TEST_P(RocksDBShardingTest, wholespace_upper_bound) {
+ test_id X;
+ zero(X);
+ do {
+ generate_data(X);
+ data_to_db();
+
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ auto dit = data.begin();
+ int r = it->seek_to_first();
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(it->valid(), (dit != data.end()));
+
+ while (dit != data.end()) {
+ ASSERT_EQ(it->valid(), true);
+ string prefix;
+ string key;
+ string key_minus_1;
+ RocksDBStore::split_key(dit->first, &prefix, &key);
+ //decrement key minimally
+ key_minus_1 = key.substr(0, key.length() - 1) + std::string(1, key[key.length() - 1] - 1);
+ KeyValueDB::WholeSpaceIterator it1 = db->get_wholespace_iterator();
+ ASSERT_EQ(it1->upper_bound(prefix, key_minus_1), 0);
+ ASSERT_EQ(it1->valid(), true);
+ auto raw_key = it1->raw_key();
+ ASSERT_EQ(raw_key.first, prefix);
+ ASSERT_EQ(raw_key.second, key);
+ if (verbose)
+ std::cout << "upper_bound " << prefix << " " << key_minus_1 << std::endl;
+ ASSERT_EQ(it->next(), 0);
+ ++dit;
+ }
+ ASSERT_EQ(it->valid(), false);
+
+ clear_db();
+ next(X);
+ } while (!end(X));
+}
+
+TEST_P(RocksDBShardingTest, wholespace_lookup_limits) {
+ test_id X;
+ zero(X);
+ do {
+ generate_data(X);
+ data_to_db();
+
+ //lookup before first
+ if (data.size() > 0) {
+ auto dit = data.begin();
+ string prefix;
+ string key;
+ RocksDBStore::split_key(dit->first, &prefix, &key);
+ KeyValueDB::WholeSpaceIterator it1 = db->get_wholespace_iterator();
+ ASSERT_EQ(it1->lower_bound(" ", " "), 0);
+ ASSERT_EQ(it1->valid(), true);
+ auto raw_key = it1->raw_key();
+ ASSERT_EQ(raw_key.first, prefix);
+ ASSERT_EQ(raw_key.second, key);
+ }
+ //lookup after last
+ KeyValueDB::WholeSpaceIterator it1 = db->get_wholespace_iterator();
+ ASSERT_EQ(it1->lower_bound("~", "~"), 0);
+ ASSERT_EQ(it1->valid(), false);
+
+ clear_db();
+ next(X);
+ } while (!end(X));
+}
+
+
+
+class RocksDBResharding : public ::testing::Test {
+public:
+ boost::scoped_ptr<RocksDBStore> db;
+
+ RocksDBResharding() : db(0) {}
+
+ string _bl_to_str(bufferlist val) {
+ string str(val.c_str(), val.length());
+ return str;
+ }
+
+ void rm_r(string path) {
+ string cmd = string("rm -r ") + path;
+ if (verbose)
+ cout << "==> " << cmd << std::endl;
+ int r = ::system(cmd.c_str());
+ if (r) {
+ cerr << "failed with exit code " << r
+ << ", continuing anyway" << std::endl;
+ }
+ }
+
+ void SetUp() override {
+ verbose = getenv("VERBOSE") && strcmp(getenv("VERBOSE"), "1") == 0;
+
+ int r = ::mkdir("kv_test_temp_dir", 0777);
+ if (r < 0 && errno != EEXIST) {
+ r = -errno;
+ cerr << __func__ << ": unable to create kv_test_temp_dir: "
+ << cpp_strerror(r) << std::endl;
+ return;
+ }
+
+ KeyValueDB* db_kv = KeyValueDB::create(g_ceph_context, "rocksdb",
+ "kv_test_temp_dir");
+ RocksDBStore* db_rocks = dynamic_cast<RocksDBStore*>(db_kv);
+ ceph_assert(db_rocks);
+ db.reset(db_rocks);
+ ASSERT_EQ(0, db->init(g_conf()->bluestore_rocksdb_options));
+ }
+ void TearDown() override {
+ db.reset(nullptr);
+ rm_r("kv_test_temp_dir");
+ }
+
+ bool verbose;
+ std::vector<std::string> prefixes = {"Ad", "Betelgeuse", "C", "D", "Evade"};
+ std::vector<std::string> randoms = {"0", "1", "2", "3", "4", "5",
+ "found", "brain", "fully", "pen", "worth", "race",
+ "stand", "nodded", "whenever", "surrounded", "industrial", "skin",
+ "this", "direction", "family", "beginning", "whenever", "held",
+ "metal", "year", "like", "valuable", "softly", "whistle",
+ "perfectly", "broken", "idea", "also", "coffee", "branch",
+ "tongue", "immediately", "bent", "partly", "burn", "include",
+ "certain", "burst", "final", "smoke", "positive", "perfectly"
+ };
+ int R = randoms.size();
+ int k = 0;
+ std::map<std::string, std::string> data;
+
+ void generate_data() {
+ data.clear();
+ for (size_t p = 0; p < prefixes.size(); p++) {
+ size_t elem_count = 1 << (( p * 3 ) + 3);
+ for (size_t i = 0; i < elem_count; i++) {
+ std::string key;
+ for (int x = 0; x < 5; x++) {
+ key = key + randoms[rand() % R];
+ }
+ std::string value;
+ for (int x = 0; x < 3; x++) {
+ value = value + randoms[rand() % R];
+ }
+ data[RocksDBStore::combine_strings(prefixes[p], key)] = value;
+ }
+ }
+ }
+
+ void data_to_db() {
+ KeyValueDB::Transaction t = db->get_transaction();
+ size_t i = 0;
+ for (auto& d: data) {
+ bufferlist v1;
+ v1.append(d.second);
+ string prefix;
+ string key;
+ RocksDBStore::split_key(d.first, &prefix, &key);
+ t->set(prefix, key, v1);
+ if (verbose)
+ std::cout << "SET " << prefix << " " << key << std::endl;
+ i++;
+ if ((i % 1000) == 0) {
+ ASSERT_EQ(db->submit_transaction_sync(t), 0);
+ t.reset();
+ if (verbose)
+ std::cout << "writing key to DB" << std::endl;
+ t = db->get_transaction();
+ }
+ }
+ if (verbose)
+ std::cout << "writing keys to DB" << std::endl;
+ ASSERT_EQ(db->submit_transaction_sync(t), 0);
+ }
+
+ void clear_db() {
+ KeyValueDB::Transaction t = db->get_transaction();
+ for (auto &d : data) {
+ string prefix;
+ string key;
+ RocksDBStore::split_key(d.first, &prefix, &key);
+ t->rmkey(prefix, key);
+ }
+ ASSERT_EQ(db->submit_transaction_sync(t), 0);
+ //paranoid, check if db empty
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ ASSERT_EQ(it->seek_to_first(), 0);
+ ASSERT_EQ(it->valid(), false);
+ }
+
+ void check_db() {
+ KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
+ //move forward
+ auto dit = data.begin();
+ int r = it->seek_to_first();
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(it->valid(), (dit != data.end()));
+
+ while (dit != data.end()) {
+ ASSERT_EQ(it->valid(), true);
+ string prefix;
+ string key;
+ RocksDBStore::split_key(dit->first, &prefix, &key);
+ auto raw_key = it->raw_key();
+ ASSERT_EQ(raw_key.first, prefix);
+ ASSERT_EQ(raw_key.second, key);
+ ASSERT_EQ(it->value().to_str(), dit->second);
+ if (verbose)
+ std::cout << "next " << prefix << " " << key << std::endl;
+ ASSERT_EQ(it->next(), 0);
+ ++dit;
+ }
+ ASSERT_EQ(it->valid(), false);
+ }
+};
+
+TEST_F(RocksDBResharding, basic) {
+ ASSERT_EQ(0, db->create_and_open(cout, ""));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ ASSERT_EQ(db->reshard("Evade(4)"), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+}
+
+TEST_F(RocksDBResharding, all_to_shards) {
+ ASSERT_EQ(0, db->create_and_open(cout, ""));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ ASSERT_EQ(db->reshard("Ad(1) Betelgeuse(1) C(1) D(1) Evade(1)"), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+}
+
+TEST_F(RocksDBResharding, all_to_shards_and_back_again) {
+ ASSERT_EQ(0, db->create_and_open(cout, ""));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ ASSERT_EQ(db->reshard("Ad(1) Betelgeuse(1) C(1) D(1) Evade(1)"), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+ ASSERT_EQ(db->reshard(""), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+}
+
+TEST_F(RocksDBResharding, resume_interrupted_at_batch) {
+ ASSERT_EQ(0, db->create_and_open(cout, ""));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ RocksDBStore::resharding_ctrl ctrl;
+ ctrl.unittest_fail_after_first_batch = true;
+ ASSERT_EQ(db->reshard("Evade(4)", &ctrl), -1000);
+ ASSERT_NE(db->open(cout), 0);
+ ASSERT_EQ(db->reshard("Evade(4)"), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+}
+
+TEST_F(RocksDBResharding, resume_interrupted_at_column) {
+ ASSERT_EQ(0, db->create_and_open(cout, ""));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ RocksDBStore::resharding_ctrl ctrl;
+ ctrl.unittest_fail_after_processing_column = true;
+ ASSERT_EQ(db->reshard("Evade(4)", &ctrl), -1001);
+ ASSERT_NE(db->open(cout), 0);
+ ASSERT_EQ(db->reshard("Evade(4)"), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+}
+
+TEST_F(RocksDBResharding, resume_interrupted_before_commit) {
+ ASSERT_EQ(0, db->create_and_open(cout, ""));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ RocksDBStore::resharding_ctrl ctrl;
+ ctrl.unittest_fail_after_successful_processing = true;
+ ASSERT_EQ(db->reshard("Evade(4)", &ctrl), -1002);
+ ASSERT_NE(db->open(cout), 0);
+ ASSERT_EQ(db->reshard("Evade(4)"), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+}
+
+TEST_F(RocksDBResharding, prevent_incomplete_hash_change) {
+ ASSERT_EQ(0, db->create_and_open(cout, "Evade(4,0-3)"));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ RocksDBStore::resharding_ctrl ctrl;
+ ctrl.unittest_fail_after_successful_processing = true;
+ ASSERT_EQ(db->reshard("Evade(4,0-8)", &ctrl), -1002);
+ ASSERT_NE(db->open(cout), 0);
+ ASSERT_EQ(db->reshard("Evade(4,0-8)"), 0);
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+}
+
+TEST_F(RocksDBResharding, change_reshard) {
+ ASSERT_EQ(0, db->create_and_open(cout, "Ad(4)"));
+ generate_data();
+ data_to_db();
+ check_db();
+ db->close();
+ {
+ RocksDBStore::resharding_ctrl ctrl;
+ ctrl.unittest_fail_after_first_batch = true;
+ ASSERT_EQ(db->reshard("C(5) D(3)", &ctrl), -1000);
+ }
+ {
+ RocksDBStore::resharding_ctrl ctrl;
+ ASSERT_NE(db->open(cout), 0);
+ ctrl.unittest_fail_after_first_batch = false;
+ ctrl.unittest_fail_after_processing_column = true;
+ ASSERT_EQ(db->reshard("C(5) Evade(2)", &ctrl), -1001);
+ }
+ {
+ RocksDBStore::resharding_ctrl ctrl;
+ ASSERT_NE(db->open(cout), 0);
+ ctrl.unittest_fail_after_processing_column = false;
+ ctrl.unittest_fail_after_successful_processing = true;
+ ASSERT_EQ(db->reshard("Evade(2) D(3)", &ctrl), -1002);
+ }
+ {
+ ASSERT_NE(db->open(cout), 0);
+ ASSERT_EQ(db->reshard("Ad(1) Evade(5)"), 0);
+ }
+ {
+ ASSERT_EQ(db->open(cout), 0);
+ check_db();
+ db->close();
+ }
+}
+
+
+INSTANTIATE_TEST_SUITE_P(
+ KeyValueDB,
+ KVTest,
+ ::testing::Values("rocksdb"));
+
+INSTANTIATE_TEST_SUITE_P(
+ KeyValueDB,
+ RocksDBShardingTest,
+ ::testing::Values("Betelgeuse D",
+ "Betelgeuse(3) D",
+ "Betelgeuse D(3)",
+ "Betelgeuse(3) D(3)"));
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_ceph_context->_conf.set_val(
+ "enable_experimental_unrecoverable_data_corrupting_features",
+ "rocksdb");
+ g_ceph_context->_conf.apply_changes(nullptr);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/objectstore/test_memstore_clone.cc b/src/test/objectstore/test_memstore_clone.cc
new file mode 100644
index 000000000..507f74d22
--- /dev/null
+++ b/src/test/objectstore/test_memstore_clone.cc
@@ -0,0 +1,202 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <boost/intrusive_ptr.hpp>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "os/ObjectStore.h"
+#include <gtest/gtest.h>
+#include "include/ceph_assert.h"
+#include "common/errno.h"
+#include "store_test_fixture.h"
+
+#define dout_context g_ceph_context
+
+using namespace std;
+
+namespace {
+
+const coll_t cid;
+
+ghobject_t make_ghobject(const char *oid)
+{
+ return ghobject_t{hobject_t{oid, "", CEPH_NOSNAP, 0, 0, ""}};
+}
+
+} // anonymous namespace
+
+class MemStoreClone : public StoreTestFixture {
+public:
+ MemStoreClone()
+ : StoreTestFixture("memstore")
+ {}
+ void SetUp() override {
+ StoreTestFixture::SetUp();
+ if (HasFailure()) {
+ return;
+ }
+ ObjectStore::Transaction t;
+ ch = store->create_new_collection(cid);
+ t.create_collection(cid, 4);
+ unsigned r = store->queue_transaction(ch, std::move(t));
+ if (r != 0) {
+ derr << "failed to create collection with " << cpp_strerror(r) << dendl;
+ }
+ ASSERT_EQ(0U, r);
+ }
+ void TearDown() override {
+ ch.reset();
+ StoreTestFixture::TearDown();
+ }
+};
+
+// src 11[11 11 11 11]11
+// dst 22 22 22 22 22 22
+// res 22 11 11 11 11 22
+TEST_F(MemStoreClone, CloneRangeAllocated)
+{
+ ASSERT_TRUE(store);
+
+ const auto src = make_ghobject("src1");
+ const auto dst = make_ghobject("dst1");
+
+ bufferlist srcbl, dstbl, result, expected;
+ srcbl.append("111111111111");
+ dstbl.append("222222222222");
+ expected.append("221111111122");
+
+ ObjectStore::Transaction t;
+ t.write(cid, src, 0, 12, srcbl);
+ t.write(cid, dst, 0, 12, dstbl);
+ t.clone_range(cid, src, dst, 2, 8, 2);
+ ASSERT_EQ(0, store->queue_transaction(ch, std::move(t)));
+ ASSERT_EQ(12, store->read(ch, dst, 0, 12, result));
+ ASSERT_EQ(expected, result);
+}
+
+// src __[__ __ __ __]__ 11 11
+// dst 22 22 22 22 22 22
+// res 22 00 00 00 00 22
+TEST_F(MemStoreClone, CloneRangeHole)
+{
+ ASSERT_TRUE(store);
+
+ const auto src = make_ghobject("src2");
+ const auto dst = make_ghobject("dst2");
+
+ bufferlist srcbl, dstbl, result, expected;
+ srcbl.append("1111");
+ dstbl.append("222222222222");
+ expected.append("22\000\000\000\000\000\000\000\00022", 12);
+
+ ObjectStore::Transaction t;
+ t.write(cid, src, 12, 4, srcbl);
+ t.write(cid, dst, 0, 12, dstbl);
+ t.clone_range(cid, src, dst, 2, 8, 2);
+ ASSERT_EQ(0, store->queue_transaction(ch, std::move(t)));
+ ASSERT_EQ(12, store->read(ch, dst, 0, 12, result));
+ ASSERT_EQ(expected, result);
+}
+
+// src __[__ __ __ 11]11
+// dst 22 22 22 22 22 22
+// res 22 00 00 00 11 22
+TEST_F(MemStoreClone, CloneRangeHoleStart)
+{
+ ASSERT_TRUE(store);
+
+ const auto src = make_ghobject("src3");
+ const auto dst = make_ghobject("dst3");
+
+ bufferlist srcbl, dstbl, result, expected;
+ srcbl.append("1111");
+ dstbl.append("222222222222");
+ expected.append("22\000\000\000\000\000\0001122", 12);
+
+ ObjectStore::Transaction t;
+ t.write(cid, src, 8, 4, srcbl);
+ t.write(cid, dst, 0, 12, dstbl);
+ t.clone_range(cid, src, dst, 2, 8, 2);
+ ASSERT_EQ(0, store->queue_transaction(ch, std::move(t)));
+ ASSERT_EQ(12, store->read(ch, dst, 0, 12, result));
+ ASSERT_EQ(expected, result);
+}
+
+// src 11[11 __ __ 11]11
+// dst 22 22 22 22 22 22
+// res 22 11 00 00 11 22
+TEST_F(MemStoreClone, CloneRangeHoleMiddle)
+{
+ ASSERT_TRUE(store);
+
+ const auto src = make_ghobject("src4");
+ const auto dst = make_ghobject("dst4");
+
+ bufferlist srcbl, dstbl, result, expected;
+ srcbl.append("1111");
+ dstbl.append("222222222222");
+ expected.append("2211\000\000\000\0001122", 12);
+
+ ObjectStore::Transaction t;
+ t.write(cid, src, 0, 4, srcbl);
+ t.write(cid, src, 8, 4, srcbl);
+ t.write(cid, dst, 0, 12, dstbl);
+ t.clone_range(cid, src, dst, 2, 8, 2);
+ ASSERT_EQ(0, store->queue_transaction(ch, std::move(t)));
+ ASSERT_EQ(12, store->read(ch, dst, 0, 12, result));
+ ASSERT_EQ(expected, result);
+}
+
+// src 11[11 __ __ __]__ 11 11
+// dst 22 22 22 22 22 22
+// res 22 11 00 00 00 22
+TEST_F(MemStoreClone, CloneRangeHoleEnd)
+{
+ ASSERT_TRUE(store);
+
+ const auto src = make_ghobject("src5");
+ const auto dst = make_ghobject("dst5");
+
+ bufferlist srcbl, dstbl, result, expected;
+ srcbl.append("1111");
+ dstbl.append("222222222222");
+ expected.append("2211\000\000\000\000\000\00022", 12);
+
+ ObjectStore::Transaction t;
+ t.write(cid, src, 0, 4, srcbl);
+ t.write(cid, src, 12, 4, srcbl);
+ t.write(cid, dst, 0, 12, dstbl);
+ t.clone_range(cid, src, dst, 2, 8, 2);
+ ASSERT_EQ(0, store->queue_transaction(ch, std::move(t)));
+ ASSERT_EQ(12, store->read(ch, dst, 0, 12, result));
+ ASSERT_EQ(expected, result);
+}
+
+int main(int argc, char** argv)
+{
+ // default to memstore
+ map<string,string> defaults = {
+ { "osd_objectstore", "memstore" },
+ { "osd_data", "msc.test_temp_dir" },
+ { "memstore_page_size", "4" }
+ };
+
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(&defaults, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/objectstore/test_transaction.cc b/src/test/objectstore/test_transaction.cc
new file mode 100644
index 000000000..381b9df7d
--- /dev/null
+++ b/src/test/objectstore/test_transaction.cc
@@ -0,0 +1,215 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Casey Bodley <cbodley@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "os/ObjectStore.h"
+#include <gtest/gtest.h>
+#include "common/Clock.h"
+#include "include/utime.h"
+#include <boost/tuple/tuple.hpp>
+
+using namespace std;
+
+TEST(Transaction, MoveConstruct)
+{
+ auto a = ObjectStore::Transaction{};
+ a.nop();
+ ASSERT_FALSE(a.empty());
+
+ // move-construct in b
+ auto b = std::move(a);
+ ASSERT_TRUE(a.empty());
+ ASSERT_FALSE(b.empty());
+}
+
+TEST(Transaction, MoveAssign)
+{
+ auto a = ObjectStore::Transaction{};
+ a.nop();
+ ASSERT_FALSE(a.empty());
+
+ auto b = ObjectStore::Transaction{};
+ b = std::move(a); // move-assign to b
+ ASSERT_TRUE(a.empty());
+ ASSERT_FALSE(b.empty());
+}
+
+TEST(Transaction, CopyConstruct)
+{
+ auto a = ObjectStore::Transaction{};
+ a.nop();
+ ASSERT_FALSE(a.empty());
+
+ auto b = a; // copy-construct in b
+ ASSERT_FALSE(a.empty());
+ ASSERT_FALSE(b.empty());
+}
+
+TEST(Transaction, CopyAssign)
+{
+ auto a = ObjectStore::Transaction{};
+ a.nop();
+ ASSERT_FALSE(a.empty());
+
+ auto b = ObjectStore::Transaction{};
+ b = a; // copy-assign to b
+ ASSERT_FALSE(a.empty());
+ ASSERT_FALSE(b.empty());
+}
+
+TEST(Transaction, Swap)
+{
+ auto a = ObjectStore::Transaction{};
+ a.nop();
+ ASSERT_FALSE(a.empty());
+
+ auto b = ObjectStore::Transaction{};
+ std::swap(a, b); // swap a and b
+ ASSERT_TRUE(a.empty());
+ ASSERT_FALSE(b.empty());
+}
+
+ObjectStore::Transaction generate_transaction()
+{
+ auto a = ObjectStore::Transaction{};
+ a.nop();
+
+ coll_t cid;
+ object_t obj("test_name");
+ snapid_t snap(0);
+ hobject_t hoid(obj, "key", snap, 0, 0, "nspace");
+ ghobject_t oid(hoid);
+
+ coll_t acid;
+ object_t aobj("another_test_name");
+ snapid_t asnap(0);
+ hobject_t ahoid(obj, "another_key", snap, 0, 0, "another_nspace");
+ ghobject_t aoid(hoid);
+ std::set<string> keys;
+ keys.insert("any_1");
+ keys.insert("any_2");
+ keys.insert("any_3");
+
+ bufferlist bl;
+ bl.append_zero(4096);
+
+ a.write(cid, oid, 1, 4096, bl, 0);
+
+ a.omap_setkeys(acid, aoid, bl);
+
+ a.omap_rmkeys(cid, aoid, keys);
+
+ a.touch(acid, oid);
+
+ return a;
+}
+
+TEST(Transaction, MoveRangesDelSrcObj)
+{
+ auto t = ObjectStore::Transaction{};
+ t.nop();
+
+ coll_t c(spg_t(pg_t(1,2), shard_id_t::NO_SHARD));
+
+ ghobject_t o1(hobject_t("obj", "", 123, 456, -1, ""));
+ ghobject_t o2(hobject_t("obj2", "", 123, 456, -1, ""));
+ vector<std::pair<uint64_t, uint64_t>> move_info = {
+ make_pair(1, 5),
+ make_pair(10, 5)
+ };
+
+ t.touch(c, o1);
+ bufferlist bl;
+ bl.append("some data");
+ t.write(c, o1, 1, bl.length(), bl);
+ t.write(c, o1, 10, bl.length(), bl);
+
+ t.clone(c, o1, o2);
+ bl.append("some other data");
+ t.write(c, o2, 1, bl.length(), bl);
+}
+
+TEST(Transaction, GetNumBytes)
+{
+ auto a = ObjectStore::Transaction{};
+ a.nop();
+ ASSERT_TRUE(a.get_encoded_bytes() == a.get_encoded_bytes_test());
+
+ coll_t cid;
+ object_t obj("test_name");
+ snapid_t snap(0);
+ hobject_t hoid(obj, "key", snap, 0, 0, "nspace");
+ ghobject_t oid(hoid);
+
+ coll_t acid;
+ object_t aobj("another_test_name");
+ snapid_t asnap(0);
+ hobject_t ahoid(obj, "another_key", snap, 0, 0, "another_nspace");
+ ghobject_t aoid(hoid);
+ std::set<string> keys;
+ keys.insert("any_1");
+ keys.insert("any_2");
+ keys.insert("any_3");
+
+ bufferlist bl;
+ bl.append_zero(4096);
+
+ a.write(cid, oid, 1, 4096, bl, 0);
+ ASSERT_TRUE(a.get_encoded_bytes() == a.get_encoded_bytes_test());
+
+ a.omap_setkeys(acid, aoid, bl);
+ ASSERT_TRUE(a.get_encoded_bytes() == a.get_encoded_bytes_test());
+
+ a.omap_rmkeys(cid, aoid, keys);
+ ASSERT_TRUE(a.get_encoded_bytes() == a.get_encoded_bytes_test());
+
+ a.touch(acid, oid);
+ ASSERT_TRUE(a.get_encoded_bytes() == a.get_encoded_bytes_test());
+}
+
+void bench_num_bytes(bool legacy)
+{
+ const int max = 2500000;
+ auto a = generate_transaction();
+
+ if (legacy) {
+ cout << "get_encoded_bytes_test: ";
+ } else {
+ cout << "get_encoded_bytes: ";
+ }
+
+ utime_t start = ceph_clock_now();
+ if (legacy) {
+ for (int i = 0; i < max; ++i) {
+ a.get_encoded_bytes_test();
+ }
+ } else {
+ for (int i = 0; i < max; ++i) {
+ a.get_encoded_bytes();
+ }
+ }
+
+ utime_t end = ceph_clock_now();
+ cout << max << " encodes in " << (end - start) << std::endl;
+
+}
+
+TEST(Transaction, GetNumBytesBenchLegacy)
+{
+ bench_num_bytes(true);
+}
+
+TEST(Transaction, GetNumBytesBenchCurrent)
+{
+ bench_num_bytes(false);
+}
diff --git a/src/test/objectstore_bench.cc b/src/test/objectstore_bench.cc
new file mode 100644
index 000000000..65a2987d0
--- /dev/null
+++ b/src/test/objectstore_bench.cc
@@ -0,0 +1,329 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <chrono>
+#include <cassert>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "os/ObjectStore.h"
+
+#include "global/global_init.h"
+
+#include "common/strtol.h"
+#include "common/ceph_argparse.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_filestore
+
+using namespace std;
+
+static void usage()
+{
+ cout << "usage: ceph_objectstore_bench [flags]\n"
+ " --size\n"
+ " total size in bytes\n"
+ " --block-size\n"
+ " block size in bytes for each write\n"
+ " --repeats\n"
+ " number of times to repeat the write cycle\n"
+ " --threads\n"
+ " number of threads to carry out this workload\n"
+ " --multi-object\n"
+ " have each thread write to a separate object\n" << std::endl;
+ generic_server_usage();
+}
+
+// helper class for bytes with units
+struct byte_units {
+ size_t v;
+ // cppcheck-suppress noExplicitConstructor
+ byte_units(size_t v) : v(v) {}
+
+ bool parse(const std::string &val, std::string *err);
+
+ operator size_t() const { return v; }
+};
+
+bool byte_units::parse(const std::string &val, std::string *err)
+{
+ v = strict_iecstrtoll(val, err);
+ return err->empty();
+}
+
+std::ostream& operator<<(std::ostream &out, const byte_units &amount)
+{
+ static const char* units[] = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
+ static const int max_units = sizeof(units)/sizeof(*units);
+
+ int unit = 0;
+ auto v = amount.v;
+ while (v >= 1024 && unit < max_units) {
+ // preserve significant bytes
+ if (v < 1048576 && (v % 1024 != 0))
+ break;
+ v >>= 10;
+ unit++;
+ }
+ return out << v << ' ' << units[unit];
+}
+
+struct Config {
+ byte_units size;
+ byte_units block_size;
+ int repeats;
+ int threads;
+ bool multi_object;
+ Config()
+ : size(1048576), block_size(4096),
+ repeats(1), threads(1),
+ multi_object(false) {}
+};
+
+class C_NotifyCond : public Context {
+ std::mutex *mutex;
+ std::condition_variable *cond;
+ bool *done;
+public:
+ C_NotifyCond(std::mutex *mutex, std::condition_variable *cond, bool *done)
+ : mutex(mutex), cond(cond), done(done) {}
+ void finish(int r) override {
+ std::lock_guard<std::mutex> lock(*mutex);
+ *done = true;
+ cond->notify_one();
+ }
+};
+
+void osbench_worker(ObjectStore *os, const Config &cfg,
+ const coll_t cid, const ghobject_t oid,
+ uint64_t starting_offset)
+{
+ bufferlist data;
+ data.append(buffer::create(cfg.block_size));
+
+ dout(0) << "Writing " << cfg.size
+ << " in blocks of " << cfg.block_size << dendl;
+
+ ceph_assert(starting_offset < cfg.size);
+ ceph_assert(starting_offset % cfg.block_size == 0);
+
+ ObjectStore::CollectionHandle ch = os->open_collection(cid);
+ ceph_assert(ch);
+
+ for (int i = 0; i < cfg.repeats; ++i) {
+ uint64_t offset = starting_offset;
+ size_t len = cfg.size;
+
+ vector<ObjectStore::Transaction> tls;
+
+ std::cout << "Write cycle " << i << std::endl;
+ while (len) {
+ size_t count = len < cfg.block_size ? len : (size_t)cfg.block_size;
+
+ auto t = new ObjectStore::Transaction;
+ t->write(cid, oid, offset, count, data);
+ tls.push_back(std::move(*t));
+ delete t;
+
+ offset += count;
+ if (offset > cfg.size)
+ offset -= cfg.size;
+ len -= count;
+ }
+
+ // set up the finisher
+ std::mutex mutex;
+ std::condition_variable cond;
+ bool done = false;
+
+ tls.back().register_on_commit(new C_NotifyCond(&mutex, &cond, &done));
+ os->queue_transactions(ch, tls);
+
+ std::unique_lock<std::mutex> lock(mutex);
+ cond.wait(lock, [&done](){ return done; });
+ lock.unlock();
+ }
+}
+
+int main(int argc, const char *argv[])
+{
+ // command-line arguments
+ auto args = argv_to_vec(argc, argv);
+
+ if (args.empty()) {
+ cerr << argv[0] << ": -h or --help for usage" << std::endl;
+ exit(1);
+ }
+ if (ceph_argparse_need_usage(args)) {
+ usage();
+ exit(0);
+ }
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_OSD,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+
+ Config cfg;
+ std::string val;
+ vector<const char*>::iterator i = args.begin();
+ while (i != args.end()) {
+ if (ceph_argparse_double_dash(args, i))
+ break;
+
+ if (ceph_argparse_witharg(args, i, &val, "--size", (char*)nullptr)) {
+ std::string err;
+ if (!cfg.size.parse(val, &err)) {
+ derr << "error parsing size: " << err << dendl;
+ exit(1);
+ }
+ } else if (ceph_argparse_witharg(args, i, &val, "--block-size", (char*)nullptr)) {
+ std::string err;
+ if (!cfg.block_size.parse(val, &err)) {
+ derr << "error parsing block-size: " << err << dendl;
+ exit(1);
+ }
+ } else if (ceph_argparse_witharg(args, i, &val, "--repeats", (char*)nullptr)) {
+ cfg.repeats = atoi(val.c_str());
+ } else if (ceph_argparse_witharg(args, i, &val, "--threads", (char*)nullptr)) {
+ cfg.threads = atoi(val.c_str());
+ } else if (ceph_argparse_flag(args, i, "--multi-object", (char*)nullptr)) {
+ cfg.multi_object = true;
+ } else {
+ derr << "Error: can't understand argument: " << *i << "\n" << dendl;
+ exit(1);
+ }
+ }
+
+ common_init_finish(g_ceph_context);
+
+ // create object store
+ dout(0) << "objectstore " << g_conf()->osd_objectstore << dendl;
+ dout(0) << "data " << g_conf()->osd_data << dendl;
+ dout(0) << "journal " << g_conf()->osd_journal << dendl;
+ dout(0) << "size " << cfg.size << dendl;
+ dout(0) << "block-size " << cfg.block_size << dendl;
+ dout(0) << "repeats " << cfg.repeats << dendl;
+ dout(0) << "threads " << cfg.threads << dendl;
+
+ auto os =
+ ObjectStore::create(g_ceph_context,
+ g_conf()->osd_objectstore,
+ g_conf()->osd_data,
+ g_conf()->osd_journal);
+
+ //Checking data folder: create if needed or error if it's not empty
+ DIR *dir = ::opendir(g_conf()->osd_data.c_str());
+ if (!dir) {
+ std::string cmd("mkdir -p ");
+ cmd+=g_conf()->osd_data;
+ int r = ::system( cmd.c_str() );
+ if( r<0 ){
+ derr << "Failed to create data directory, ret = " << r << dendl;
+ return 1;
+ }
+ }
+ else {
+ bool non_empty = readdir(dir) != NULL && readdir(dir) != NULL && readdir(dir) != NULL;
+ if( non_empty ){
+ derr << "Data directory '"<<g_conf()->osd_data<<"' isn't empty, please clean it first."<< dendl;
+ return 1;
+ }
+ }
+ ::closedir(dir);
+
+ //Create folders for journal if needed
+ string journal_base = g_conf()->osd_journal.substr(0, g_conf()->osd_journal.rfind('/'));
+ struct stat sb;
+ if (stat(journal_base.c_str(), &sb) != 0 ){
+ std::string cmd("mkdir -p ");
+ cmd+=journal_base;
+ int r = ::system( cmd.c_str() );
+ if( r<0 ){
+ derr << "Failed to create journal directory, ret = " << r << dendl;
+ return 1;
+ }
+ }
+
+ if (!os) {
+ derr << "bad objectstore type " << g_conf()->osd_objectstore << dendl;
+ return 1;
+ }
+ if (os->mkfs() < 0) {
+ derr << "mkfs failed" << dendl;
+ return 1;
+ }
+ if (os->mount() < 0) {
+ derr << "mount failed" << dendl;
+ return 1;
+ }
+
+ dout(10) << "created objectstore " << os.get() << dendl;
+
+ // create a collection
+ spg_t pg;
+ const coll_t cid(pg);
+ ObjectStore::CollectionHandle ch = os->create_new_collection(cid);
+ {
+ ObjectStore::Transaction t;
+ t.create_collection(cid, 0);
+ os->queue_transaction(ch, std::move(t));
+ }
+
+ // create the objects
+ std::vector<ghobject_t> oids;
+ if (cfg.multi_object) {
+ oids.reserve(cfg.threads);
+ for (int i = 0; i < cfg.threads; i++) {
+ std::stringstream oss;
+ oss << "osbench-thread-" << i;
+ oids.emplace_back(hobject_t(sobject_t(oss.str(), CEPH_NOSNAP)));
+
+ ObjectStore::Transaction t;
+ t.touch(cid, oids[i]);
+ int r = os->queue_transaction(ch, std::move(t));
+ ceph_assert(r == 0);
+ }
+ } else {
+ oids.emplace_back(hobject_t(sobject_t("osbench", CEPH_NOSNAP)));
+
+ ObjectStore::Transaction t;
+ t.touch(cid, oids.back());
+ int r = os->queue_transaction(ch, std::move(t));
+ ceph_assert(r == 0);
+ }
+
+ // run the worker threads
+ std::vector<std::thread> workers;
+ workers.reserve(cfg.threads);
+
+ using namespace std::chrono;
+ auto t1 = high_resolution_clock::now();
+ for (int i = 0; i < cfg.threads; i++) {
+ const auto &oid = cfg.multi_object ? oids[i] : oids[0];
+ workers.emplace_back(osbench_worker, os.get(), std::ref(cfg),
+ cid, oid, i * cfg.size / cfg.threads);
+ }
+ for (auto &worker : workers)
+ worker.join();
+ auto t2 = high_resolution_clock::now();
+ workers.clear();
+
+ auto duration = duration_cast<microseconds>(t2 - t1);
+ byte_units total = cfg.size * cfg.repeats * cfg.threads;
+ byte_units rate = (1000000LL * total) / duration.count();
+ size_t iops = (1000000LL * total / cfg.block_size) / duration.count();
+ dout(0) << "Wrote " << total << " in "
+ << duration.count() << "us, at a rate of " << rate << "/s and "
+ << iops << " iops" << dendl;
+
+ // remove the objects
+ ObjectStore::Transaction t;
+ for (const auto &oid : oids)
+ t.remove(cid, oid);
+ os->queue_transaction(ch, std::move(t));
+
+ os->umount();
+ return 0;
+}
diff --git a/src/test/old/test_disk_bw.cc b/src/test/old/test_disk_bw.cc
new file mode 100644
index 000000000..d509a510d
--- /dev/null
+++ b/src/test/old/test_disk_bw.cc
@@ -0,0 +1,62 @@
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/uio.h>
+
+#include "common/Clock.h"
+#include "common/safe_io.h"
+
+#include <iostream>
+using namespace std;
+
+int main(int argc, char **argv)
+{
+ void *buf;
+ int fd, count, loop = 0;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: %s device bsize count\n", argv[0]);
+ exit (0);
+ }
+
+ int bsize = atoi(argv[2]);
+ count = atoi(argv[3]);
+
+ posix_memalign(&buf, sysconf(_SC_PAGESIZE), bsize);
+
+ //if ((fd = open(argv[1], O_SYNC|O_RDWR)) < 0) {
+ if ((fd = open(argv[1], O_DIRECT|O_RDWR)) < 0) {
+
+ fprintf(stderr, "Can't open device %s\n", argv[1]);
+ exit (4);
+ }
+
+
+ utime_t start = ceph_clock_now();
+ while (loop++ < count) {
+ int ret = safe_write(fd, buf, bsize);
+ if (ret)
+ ceph_abort();
+ //if ((loop % 100) == 0)
+ //fprintf(stderr, ".");
+ }
+ ::fsync(fd);
+ ::close(fd);
+ utime_t end = ceph_clock_now();
+ end -= start;
+
+
+ char hostname[80];
+ gethostname(hostname, 80);
+
+ double mb = bsize*count/1024/1024;
+
+ cout << hostname << "\t" << mb << " MB\t" << end << " seconds\t" << (mb / (double)end) << " MB/sec" << std::endl;
+}
diff --git a/src/test/old/test_setlayout.c b/src/test/old/test_setlayout.c
new file mode 100644
index 000000000..d0ad8954e
--- /dev/null
+++ b/src/test/old/test_setlayout.c
@@ -0,0 +1,24 @@
+#define __USE_GNU 1
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <linux/types.h>
+#include "include/ceph_fs.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "kernel/ioctl.h"
+
+
+main() {
+ struct ceph_file_layout l;
+ int fd = open("foo.txt", O_RDONLY);
+ int r = ioctl(fd, CEPH_IOC_GET_LAYOUT, &l, sizeof(l));
+ printf("get = %d\n", r);
+
+ l.fl_stripe_unit = 65536;
+ l.fl_object_size = 65536;
+
+ r = ioctl(fd, CEPH_IOC_SET_LAYOUT, &l, sizeof(l));
+ printf("set = %d\n", r);
+}
diff --git a/src/test/old/testfilepath.cc b/src/test/old/testfilepath.cc
new file mode 100644
index 000000000..78ecc197f
--- /dev/null
+++ b/src/test/old/testfilepath.cc
@@ -0,0 +1,22 @@
+
+#include "include/filepath.h"
+#include <iostream>
+using namespace std;
+
+int print(const string &s) {
+ filepath fp = s;
+ cout << "s = " << s << " filepath = " << fp << endl;
+ cout << " depth " << fp.depth() << endl;
+ for (int i=0; i<fp.depth(); i++) {
+ cout << "\t" << i << " " << fp[i] << endl;
+ }
+}
+
+int main() {
+ filepath p;
+ print("/home/sage");
+ print("a/b/c");
+ print("/a/b/c");
+ print("/a/b/c/");
+ print("/a/b/../d");
+}
diff --git a/src/test/omap_bench.cc b/src/test/omap_bench.cc
new file mode 100644
index 000000000..714774f12
--- /dev/null
+++ b/src/test/omap_bench.cc
@@ -0,0 +1,431 @@
+/*
+ * Generate latency statistics for a configurable number of write
+ * operations of configurable size.
+ *
+ * Created on: May 21, 2012
+ * Author: Eleanor Cawthon
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include "include/rados/librados.hpp"
+#include "include/Context.h"
+#include "common/ceph_context.h"
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "include/utime.h"
+#include "common/ceph_argparse.h"
+#include "test/omap_bench.h"
+
+#include <string>
+#include <iostream>
+#include <cassert>
+#include <climits>
+#include <cmath>
+
+using namespace std;
+using ceph::bufferlist;
+
+int OmapBench::setup(int argc, const char** argv) {
+ //parse key_value_store_bench args
+ auto args = argv_to_vec(argc, argv);
+ for (unsigned i = 0; i < args.size(); i++) {
+ if(i < args.size() - 1) {
+ if (strcmp(args[i], "-t") == 0) {
+ threads = atoi(args[i+1]);
+ } else if (strcmp(args[i], "-o") == 0) {
+ objects = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--entries") == 0) {
+ entries_per_omap = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--keysize") == 0) {
+ key_size = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--valsize") == 0) {
+ value_size = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--inc") == 0) {
+ increment = atoi(args[i+1]);
+ } else if (strcmp(args[i], "--omaptype") == 0) {
+ if(strcmp("rand",args[i+1]) == 0) {
+ omap_generator = OmapBench::generate_non_uniform_omap;
+ }
+ else if (strcmp("uniform", args[i+1]) == 0) {
+ omap_generator = OmapBench::generate_uniform_omap;
+ }
+ } else if (strcmp(args[i], "--name") == 0) {
+ rados_id = args[i+1];
+ }
+ } else if (strcmp(args[i], "--help") == 0) {
+ cout << "\nUsage: ostorebench [options]\n"
+ << "Generate latency statistics for a configurable number of "
+ << "key value pair operations of\n"
+ << "configurable size.\n\n"
+ << "OPTIONS\n"
+ << " -t number of threads to use (default "<<threads;
+ cout << ")\n"
+ << " -o number of objects to write (default "<<objects;
+ cout << ")\n"
+ << " --entries number of entries per (default "
+ << entries_per_omap;
+ cout <<")\n"
+ << " --keysize number of characters per key "
+ << "(default "<<key_size;
+ cout << ")\n"
+ << " --valsize number of characters per value "
+ << "(default "<<value_size;
+ cout << ")\n"
+ << " --inc specify the increment to use in the displayed "
+ << "histogram (default "<<increment;
+ cout << ")\n"
+ << " --omaptype specify how omaps should be generated - "
+ << "rand for random sizes between\n"
+ << " 0 and max size, uniform for all sizes"
+ << " to be specified size.\n"
+ << " (default uniform)\n";
+ cout << " --name the rados id to use (default "<< rados_id
+ << ")\n";
+ exit(1);
+ }
+ }
+ int r = rados.init(rados_id.c_str());
+ if (r < 0) {
+ cout << "error during init" << std::endl;
+ return r;
+ }
+ r = rados.conf_parse_argv(argc, argv);
+ if (r < 0) {
+ cout << "error during parsing args" << std::endl;
+ return r;
+ }
+ r = rados.conf_parse_env(NULL);
+ if (r < 0) {
+ cout << "error during parsing env" << std::endl;
+ return r;
+ }
+ r = rados.conf_read_file(NULL);
+ if (r < 0) {
+ cout << "error during read file" << std::endl;
+ return r;
+ }
+ r = rados.connect();
+ if (r < 0) {
+ cout << "error during connect" << std::endl;
+ return r;
+ }
+ r = rados.ioctx_create(pool_name.c_str(), io_ctx);
+ if (r < 0) {
+ cout << "error creating io ctx" << std::endl;
+ rados.shutdown();
+ return r;
+ }
+ return 0;
+}
+
+//Writer functions
+Writer::Writer(OmapBench *omap_bench) : ob(omap_bench) {
+ stringstream name;
+ ob->data_lock.lock();
+ name << omap_bench->prefix << ++(ob->data.started_ops);
+ ob->data_lock.unlock();
+ oid = name.str();
+}
+void Writer::start_time() {
+ begin_time = ceph_clock_now();
+}
+void Writer::stop_time() {
+ end_time = ceph_clock_now();
+}
+double Writer::get_time() {
+ return (end_time - begin_time) * 1000;
+}
+string Writer::get_oid() {
+ return oid;
+}
+std::map<std::string, bufferlist> & Writer::get_omap() {
+ return omap;
+}
+
+//AioWriter functions
+AioWriter::AioWriter(OmapBench *ob) : Writer(ob) {
+ aioc = NULL;
+}
+AioWriter::~AioWriter() {
+ if(aioc) aioc->release();
+}
+librados::AioCompletion * AioWriter::get_aioc() {
+ return aioc;
+}
+void AioWriter::set_aioc(librados::callback_t complete) {
+ aioc = ob->rados.aio_create_completion(this, complete);
+}
+
+
+//Helper methods
+void OmapBench::aio_is_complete(rados_completion_t c, void *arg) {
+ AioWriter *aiow = reinterpret_cast<AioWriter *>(arg);
+ aiow->stop_time();
+ ceph::mutex * data_lock = &aiow->ob->data_lock;
+ ceph::mutex * thread_is_free_lock = &aiow->ob->thread_is_free_lock;
+ ceph::condition_variable* thread_is_free = &aiow->ob->thread_is_free;
+ int &busythreads_count = aiow->ob->busythreads_count;
+ o_bench_data &data = aiow->ob->data;
+ int INCREMENT = aiow->ob->increment;
+ int err = aiow->get_aioc()->get_return_value();
+ if (err < 0) {
+ cout << "error writing AioCompletion";
+ return;
+ }
+ double time = aiow->get_time();
+ delete aiow;
+ data_lock->lock();
+ data.avg_latency = (data.avg_latency * data.completed_ops + time)
+ / (data.completed_ops + 1);
+ data.completed_ops++;
+ if (time < data.min_latency) {
+ data.min_latency = time;
+ }
+ if (time > data.max_latency) {
+ data.max_latency = time;
+ }
+ data.total_latency += time;
+ ++(data.freq_map[time / INCREMENT]);
+ if(data.freq_map[time/INCREMENT] > data.mode.second) {
+ data.mode.first = time/INCREMENT;
+ data.mode.second = data.freq_map[time/INCREMENT];
+ }
+ data_lock->unlock();
+
+ thread_is_free_lock->lock();
+ busythreads_count--;
+ thread_is_free->notify_all();
+ thread_is_free_lock->unlock();
+}
+
+string OmapBench::random_string(int len) {
+ string ret;
+ string alphanum = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+
+ for (int i = 0; i < len; ++i) {
+ ret.push_back(alphanum[rand() % (alphanum.size() - 1)]);
+ }
+
+ return ret;
+}
+
+int OmapBench::run() {
+ return (((OmapBench *)this)->*OmapBench::test)(omap_generator);
+}
+
+int OmapBench::print_written_omap() {
+ for (int i = 1; i <= objects; i++) {
+ int err = 0;
+ librados::ObjectReadOperation key_read;
+ set<string> out_keys;
+ map<string, bufferlist> out_vals;
+ std::stringstream objstrm;
+ objstrm << prefix;
+ objstrm << i;
+ cout << "\nPrinting omap for "<<objstrm.str() << std::endl;
+ // FIXME: we ignore pmore here. this shouldn't happen for benchmark
+ // keys, though, unless the OSD limit is *really* low.
+ key_read.omap_get_keys2("", LONG_MAX, &out_keys, nullptr, &err);
+ io_ctx.operate(objstrm.str(), &key_read, NULL);
+ if (err < 0) {
+ cout << "error " << err;
+ cout << " getting omap key set " << std::endl;
+ return err;
+ }
+
+ librados::ObjectReadOperation val_read;
+ val_read.omap_get_vals_by_keys(out_keys, &out_vals, &err);
+ if (err < 0) {
+ cout << "error " << err;
+ cout << " getting omap value set " << std::endl;
+ return err;
+ }
+ io_ctx.operate(objstrm.str(), &val_read, NULL);
+
+ for (set<string>::iterator iter = out_keys.begin();
+ iter != out_keys.end(); ++iter) {
+ cout << *iter << "\t" << (out_vals)[*iter] << std::endl;
+ }
+ }
+ return 0;
+}
+
+void OmapBench::print_results() {
+ cout << "========================================================";
+ cout << "\nNumber of kvmaps written:\t" << objects;
+ cout << "\nNumber of ops at once:\t" << threads;
+ cout << "\nEntries per kvmap:\t\t" << entries_per_omap;
+ cout << "\nCharacters per key:\t" << key_size;
+ cout << "\nCharacters per val:\t" << value_size;
+ cout << std::endl;
+ cout << std::endl;
+ cout << "Average latency:\t" << data.avg_latency;
+ cout << "ms\nMinimum latency:\t" << data.min_latency;
+ cout << "ms\nMaximum latency:\t" << data.max_latency;
+ cout << "ms\nMode latency:\t\t"<<"between "<<data.mode.first * increment;
+ cout << " and " <<data.mode.first * increment + increment;
+ cout << "ms\nTotal latency:\t\t" << data.total_latency;
+ cout << "ms"<<std::endl;
+ cout << std::endl;
+ cout << "Histogram:" << std::endl;
+ for(int i = floor(data.min_latency / increment); i <
+ ceil(data.max_latency / increment); i++) {
+ cout << ">= "<< i * increment;
+ cout << "ms";
+ int spaces;
+ if (i == 0) spaces = 4;
+ else spaces = 3 - floor(log10(i));
+ for (int j = 0; j < spaces; j++) {
+ cout << " ";
+ }
+ cout << "[";
+ for(int j = 0; j < ((data.freq_map)[i])*45/(data.mode.second); j++) {
+ cout << "*";
+ }
+ cout << std::endl;
+ }
+ cout << "\n========================================================"
+ << std::endl;
+}
+
+int OmapBench::write_omap_asynchronously(AioWriter *aiow,
+ const std::map<std::string,bufferlist> &omap) {
+ librados::ObjectWriteOperation owo;
+ owo.create(false);
+ owo.omap_clear();
+ owo.omap_set(omap);
+ aiow->start_time();
+ int err = io_ctx.aio_operate(aiow->get_oid(), aiow->get_aioc(), &owo);
+ if (err < 0) {
+ cout << "writing omap failed with code "<<err;
+ cout << std::endl;
+ return err;
+ }
+ return 0;
+}
+
+//Omap Generators
+int OmapBench::generate_uniform_omap(const int omap_entries, const int key_size,
+ const int value_size, std::map<std::string,bufferlist> * out_omap) {
+ bufferlist bl;
+
+ //setup omap
+ for (int i = 0; i < omap_entries; i++) {
+ bufferlist omap_val;
+ omap_val.append(random_string(value_size));
+ string key = random_string(key_size);
+ (*out_omap)[key]= omap_val;
+ }
+ return 0;
+}
+
+int OmapBench::generate_non_uniform_omap(const int omap_entries,
+ const int key_size, const int value_size,
+ std::map<std::string,bufferlist> * out_omap) {
+ bufferlist bl;
+
+ int num_entries = rand() % omap_entries + 1;
+ int key_len = rand() % key_size +1;
+ int val_len = rand() % value_size +1;
+
+ //setup omap
+ for (int i = 0; i < num_entries; i++) {
+ bufferlist omap_val;
+ omap_val.append(random_string(val_len));
+ string key = random_string(key_len);
+ (*out_omap)[key] = omap_val;
+ }
+ return 0;
+}
+
+int OmapBench::generate_small_non_random_omap(const int omap_entries,
+ const int key_size, const int value_size,
+ std::map<std::string,bufferlist> * out_omap) {
+ bufferlist bl;
+ stringstream key;
+
+ //setup omap
+ for (int i = 0; i < omap_entries; i++) {
+ bufferlist omap_val;
+ omap_val.append("Value ");
+ omap_val.append(i);
+ key << "Key " << i;
+ (*out_omap)[key.str()]= omap_val;
+ }
+ return 0;
+}
+
+//tests
+int OmapBench::test_write_objects_in_parallel(omap_generator_t omap_gen) {
+ AioWriter *this_aio_writer;
+
+ std::unique_lock l{thread_is_free_lock};
+ for (int i = 0; i < objects; i++) {
+ ceph_assert(busythreads_count <= threads);
+ //wait for a writer to be free
+ if (busythreads_count == threads) {
+ thread_is_free.wait(l);
+ ceph_assert(busythreads_count < threads);
+ }
+
+ //set up the write
+ this_aio_writer = new AioWriter(this);
+ this_aio_writer->set_aioc(comp);
+
+ //perform the write
+ busythreads_count++;
+ int err = omap_gen(entries_per_omap, key_size, value_size,
+ & this_aio_writer->get_omap());
+ if (err < 0) {
+ return err;
+ }
+ err = OmapBench::write_omap_asynchronously(this_aio_writer,
+ (this_aio_writer->get_omap()));
+
+
+ if (err < 0) {
+ return err;
+ }
+ }
+ thread_is_free.wait(l, [this] { return busythreads_count <= 0;});
+ return 0;
+}
+
+/**
+ * runs the specified test with the specified parameters and generates
+ * a histogram of latencies
+ */
+int main(int argc, const char** argv) {
+ OmapBench ob;
+ int err = ob.setup(argc, argv);
+ if (err<0) {
+ cout << "error during setup: "<<err;
+ cout << std::endl;
+ exit(1);
+ }
+ err = ob.run();
+ if (err < 0) {
+ cout << "writing objects failed with code " << err;
+ cout << std::endl;
+ return err;
+ }
+
+ ob.print_results();
+
+ //uncomment to show omaps
+ /*err = ob.return print_written_omap();
+ if (err < 0) {
+ cout << "printing omaps failed with code " << err;
+ cout << std::endl;
+ return err;
+ }
+ */
+ return 0;
+
+}
diff --git a/src/test/omap_bench.h b/src/test/omap_bench.h
new file mode 100644
index 000000000..1874bd8d6
--- /dev/null
+++ b/src/test/omap_bench.h
@@ -0,0 +1,206 @@
+/*
+ * Generate latency statistics for a configurable number of object map write
+ * operations of configurable size.
+ *
+ * Created on: May 21, 2012
+ * Author: Eleanor Cawthon
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#ifndef OMAP_BENCH_HPP_
+#define OMAP_BENCH_HPP_
+
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "include/rados/librados.hpp"
+#include <string>
+#include <map>
+#include <cfloat>
+
+using ceph::bufferlist;
+
+struct o_bench_data {
+ double avg_latency;
+ double min_latency;
+ double max_latency;
+ double total_latency;
+ int started_ops;
+ int completed_ops;
+ std::map<int,int> freq_map;
+ std::pair<int,int> mode;
+ o_bench_data()
+ : avg_latency(0.0), min_latency(DBL_MAX), max_latency(0.0),
+ total_latency(0.0),
+ started_ops(0), completed_ops(0)
+ {}
+};
+
+class OmapBench;
+
+typedef int (*omap_generator_t)(const int omap_entries, const int key_size,
+ const int value_size,
+ std::map<std::string,bufferlist> *out_omap);
+typedef int (OmapBench::*test_t)(omap_generator_t omap_gen);
+
+
+class Writer{
+protected:
+ std::string oid;
+ utime_t begin_time;
+ utime_t end_time;
+ std::map<std::string,bufferlist> omap;
+ OmapBench *ob;
+ friend class OmapBench;
+public:
+ Writer(OmapBench *omap_bench);
+ virtual ~Writer(){};
+ virtual void start_time();
+ virtual void stop_time();
+ virtual double get_time();
+ virtual std::string get_oid();
+ virtual std::map<std::string,bufferlist> & get_omap();
+};
+
+class AioWriter : public Writer{
+protected:
+ librados::AioCompletion * aioc;
+ friend class OmapBench;
+
+public:
+ AioWriter(OmapBench *omap_bench);
+ ~AioWriter() override;
+ virtual librados::AioCompletion * get_aioc();
+ virtual void set_aioc(librados::callback_t complete);
+};
+
+class OmapBench{
+protected:
+ librados::IoCtx io_ctx;
+ librados::Rados rados;
+ struct o_bench_data data;
+ test_t test;
+ omap_generator_t omap_generator;
+
+ //aio things
+ ceph::condition_variable thread_is_free;
+ ceph::mutex thread_is_free_lock =
+ ceph::make_mutex("OmapBench::thread_is_free_lock");
+ ceph::mutex data_lock =
+ ceph::make_mutex("OmapBench::data_lock");
+ int busythreads_count;
+ librados::callback_t comp;
+
+ std::string pool_name;
+ std::string rados_id;
+ std::string prefix;
+ int threads;
+ int objects;
+ int entries_per_omap;
+ int key_size;
+ int value_size;
+ double increment;
+
+ friend class Writer;
+ friend class AioWriter;
+
+public:
+ OmapBench()
+ : test(&OmapBench::test_write_objects_in_parallel),
+ omap_generator(generate_uniform_omap),
+ busythreads_count(0),
+ comp(aio_is_complete),
+ pool_name("rbd"),
+ rados_id("admin"),
+ prefix(rados_id+".obj."),
+ threads(3), objects(100), entries_per_omap(10), key_size(10),
+ value_size(100), increment(10)
+ {}
+ /**
+ * Parses command line args, initializes rados and ioctx
+ */
+ int setup(int argc, const char** argv);
+
+ /**
+ * Callback for when an AioCompletion (called from an AioWriter)
+ * is complete. deletes the AioWriter that called it,
+ * Updates data, updates busythreads, and signals thread_is_free.
+ *
+ * @param c provided by aio_write - not used
+ * @param arg the AioWriter that contains this AioCompletion
+ */
+ static void aio_is_complete(rados_completion_t c, void *arg);
+
+ /**
+ * Generates a random string len characters long
+ */
+ static std::string random_string(int len);
+
+ /*
+ * runs the test specified by test using the omap generator specified by
+ * omap_generator
+ *
+ * @return error code
+ */
+ int run();
+
+ /*
+ * Prints all keys and values for all omap entries for all objects
+ */
+ int print_written_omap();
+
+ /*
+ * Displays relevant constants and the histogram generated through a test
+ */
+ void print_results();
+
+ /**
+ * Writes an object with the specified AioWriter.
+ *
+ * @param aiow the AioWriter to write with
+ * @param omap the omap to write
+ * @post: an asynchronous omap_set is launched
+ */
+ int write_omap_asynchronously(AioWriter *aiow,
+ const std::map<std::string,bufferlist> &map);
+
+
+ /**
+ * Generates an omap with omap_entries entries, each with keys key_size
+ * characters long and with string values value_size characters long.
+ *
+ * @param out_map pointer to the map to be created
+ * @return error code
+ */
+ static int generate_uniform_omap(const int omap_entries, const int key_size,
+ const int value_size, std::map<std::string,bufferlist> * out_omap);
+
+ /**
+ * The same as generate_uniform_omap except that string lengths are picked
+ * randomly between 1 and the int arguments
+ */
+ static int generate_non_uniform_omap(const int omap_entries,
+ const int key_size,
+ const int value_size, std::map<std::string,bufferlist> * out_omap);
+
+ static int generate_small_non_random_omap(const int omap_entries,
+ const int key_size, const int value_size,
+ std::map<std::string,bufferlist> * out_omap);
+
+ /*
+ * Uses aio_write to write omaps generated by omap_gen to OBJECTS objects
+ * using THREADS AioWriters at a time.
+ *
+ * @param omap_gen the method used to generate the omaps.
+ */
+ int test_write_objects_in_parallel(omap_generator_t omap_gen);
+
+};
+
+
+
+#endif /* OMAP_BENCH_HPP_ */
+
diff --git a/src/test/on_exit.cc b/src/test/on_exit.cc
new file mode 100644
index 000000000..aec1f78b7
--- /dev/null
+++ b/src/test/on_exit.cc
@@ -0,0 +1,115 @@
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "include/on_exit.h"
+#include "include/ceph_assert.h"
+
+#ifndef MAP_ANONYMOUS
+# ifdef MAP_ANON
+# define MAP_ANONYMOUS MAP_ANON
+# else
+// cppcheck-suppress preprocessorErrorDirective
+# error "Don't know how to create anonymous mmap"
+# endif
+#endif
+
+static int func_scope_val;
+
+static void add(void *incp)
+{
+ func_scope_val += *((int*)incp);
+}
+
+static void func_scope(void)
+{
+ OnExitManager mgr;
+
+ int *inc_1 = (int*)malloc(sizeof(*inc_1));
+ *inc_1 = 5;
+ mgr.add_callback(add, inc_1);
+
+ int *inc_2 = (int*)malloc(sizeof(*inc_2));
+ *inc_2 = 3;
+ mgr.add_callback(add, inc_2);
+}
+
+// shared between processes
+static int *shared_val;
+
+#define MAIN_SCOPE_VAL 0x1111111
+static OnExitManager main_scope_mgr;
+static void main_scope_cb(void *val)
+{
+ *shared_val = *((int*)val);
+}
+
+#define EXIT_FUNC_VAL 0x22222222
+static OnExitManager exit_func_mgr;
+static void exit_func_cb(void *val)
+{
+ *shared_val = *((int*)val);
+}
+
+static void call_exit()
+{
+ exit(3);
+}
+
+int main(int argc, char **argv)
+{
+ // test basic function scope behavior
+ ceph_assert(func_scope_val == 0);
+ func_scope();
+ ceph_assert(func_scope_val == 8);
+
+ // shared mem for exit tests
+ shared_val = (int*)mmap(NULL, sizeof(int),
+ PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+ ceph_assert(shared_val != MAP_FAILED);
+
+ // test normal exit returning from main
+ *shared_val = 0;
+ int pid = fork();
+ ceph_assert(pid >= 0);
+ if (pid) {
+ int status;
+ int ret = waitpid(pid, &status, 0);
+ ceph_assert(ret == pid); // should be our child
+ ceph_assert(status == 0);
+ ceph_assert(*shared_val == MAIN_SCOPE_VAL);
+ } else {
+ // child adds a callback to the static scope callback manager and then
+ // exits by returning from main. The parent checks the value after the
+ // child exits via the memory map.
+ ceph_assert(*shared_val == 0);
+ int *new_val = (int*)malloc(sizeof(*new_val));
+ *new_val = MAIN_SCOPE_VAL;
+ main_scope_mgr.add_callback(main_scope_cb, new_val);
+ return 0;
+ }
+
+ // test exit via exit()
+ *shared_val = 0;
+ pid = fork();
+ ceph_assert(pid >= 0);
+ if (pid) {
+ int status;
+ int ret = waitpid(pid, &status, 0);
+ ceph_assert(ret == pid); // should be our child
+ ceph_assert(WEXITSTATUS(status) == 3);
+ ceph_assert(*shared_val == EXIT_FUNC_VAL);
+ } else {
+ // child adds a callback to the static scope callback manager and then
+ // exits via exit().
+ ceph_assert(*shared_val == 0);
+ int *new_val = (int*)malloc(sizeof(*new_val));
+ *new_val = EXIT_FUNC_VAL;
+ exit_func_mgr.add_callback(exit_func_cb, new_val);
+ call_exit();
+ ceph_abort();
+ }
+
+ return 0;
+}
diff --git a/src/test/opensuse-13.2/Dockerfile.in b/src/test/opensuse-13.2/Dockerfile.in
new file mode 100644
index 000000000..26402d77a
--- /dev/null
+++ b/src/test/opensuse-13.2/Dockerfile.in
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2015 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+# Environment variables are substituted via envsubst(1)
+#
+# user_id=$(id -u)
+# os_version= the desired REPOSITORY TAG
+#
+FROM opensuse:%%os_version%%
+
+COPY install-deps.sh /root/
+COPY ceph.spec.in /root/
+# build dependencies
+RUN cd /root ; ./install-deps.sh
+# development tools
+# nc (ncat) is required to run make check on firefly only (giant+ do not use nc)
+RUN zypper --non-interactive install ccache valgrind gdb git python-virtualenv gdisk kpartx ncat sudo xmlstarlet parted
+RUN if test %%USER%% != root ; then useradd -M --uid %%user_id%% %%USER%% && echo '%%USER%% ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ; fi
diff --git a/src/test/opensuse-13.2/ceph.spec.in b/src/test/opensuse-13.2/ceph.spec.in
new file mode 120000
index 000000000..9abcafd12
--- /dev/null
+++ b/src/test/opensuse-13.2/ceph.spec.in
@@ -0,0 +1 @@
+../../../ceph.spec.in \ No newline at end of file
diff --git a/src/test/opensuse-13.2/install-deps.sh b/src/test/opensuse-13.2/install-deps.sh
new file mode 120000
index 000000000..fc9c78b27
--- /dev/null
+++ b/src/test/opensuse-13.2/install-deps.sh
@@ -0,0 +1 @@
+../../../install-deps.sh \ No newline at end of file
diff --git a/src/test/osd/CMakeLists.txt b/src/test/osd/CMakeLists.txt
new file mode 100644
index 000000000..c9216c42d
--- /dev/null
+++ b/src/test/osd/CMakeLists.txt
@@ -0,0 +1,141 @@
+# test_rados
+add_executable(ceph_test_rados
+ TestRados.cc
+ TestOpStat.cc
+ Object.cc
+ RadosModel.cc
+ )
+target_link_libraries(ceph_test_rados
+ librados
+ global
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ cls_cas_internal
+ cls_cas_client
+ )
+install(TARGETS
+ ceph_test_rados
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# test_stale_read
+add_executable(ceph_test_osd_stale_read
+ ceph_test_osd_stale_read.cc
+ )
+target_link_libraries(ceph_test_osd_stale_read
+ librados
+ global
+ ${CMAKE_DL_LIBS}
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ )
+install(TARGETS
+ ceph_test_osd_stale_read
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# scripts
+add_ceph_test(safe-to-destroy.sh ${CMAKE_CURRENT_SOURCE_DIR}/safe-to-destroy.sh)
+
+# unittest_osdmap
+add_executable(unittest_osdmap
+ TestOSDMap.cc
+ )
+add_ceph_unittest(unittest_osdmap)
+target_link_libraries(unittest_osdmap global ${BLKID_LIBRARIES})
+
+# unittest_osd_types
+add_executable(unittest_osd_types
+ types.cc
+ )
+add_ceph_unittest(unittest_osd_types)
+target_link_libraries(unittest_osd_types global)
+
+# unittest_ecbackend
+add_executable(unittest_ecbackend
+ TestECBackend.cc
+ )
+add_ceph_unittest(unittest_ecbackend)
+target_link_libraries(unittest_ecbackend osd global)
+
+# unittest_osdscrub
+add_executable(unittest_osdscrub
+ TestOSDScrub.cc
+ $<TARGET_OBJECTS:unit-main>
+ )
+add_ceph_unittest(unittest_osdscrub)
+target_link_libraries(unittest_osdscrub osd os global ${CMAKE_DL_LIBS} mon ${BLKID_LIBRARIES})
+
+# unittest_scrubber_be
+add_executable(unittest_scrubber_be
+ test_scrubber_be.cc
+ scrubber_generators.cc
+ scrubber_test_datasets.cc
+ )
+add_ceph_unittest(unittest_scrubber_be)
+target_link_libraries(unittest_scrubber_be osd os global ${CMAKE_DL_LIBS} mon ${BLKID_LIBRARIES})
+
+# unittest_scrub_sched
+add_executable(unittest_scrub_sched
+ test_scrub_sched.cc
+ )
+add_ceph_unittest(unittest_scrub_sched)
+target_link_libraries(unittest_scrub_sched osd os global ${CMAKE_DL_LIBS} mon ${BLKID_LIBRARIES})
+
+# unittest_pglog
+add_executable(unittest_pglog
+ TestPGLog.cc
+ $<TARGET_OBJECTS:unit-main>
+ $<TARGET_OBJECTS:store_test_fixture>
+ )
+add_ceph_unittest(unittest_pglog)
+target_link_libraries(unittest_pglog osd os global ${CMAKE_DL_LIBS} ${BLKID_LIBRARIES})
+
+# unittest_hitset
+add_executable(unittest_hitset
+ hitset.cc
+ )
+add_ceph_unittest(unittest_hitset)
+target_link_libraries(unittest_hitset osd global ${BLKID_LIBRARIES})
+
+# unittest_osd_osdcap
+add_executable(unittest_osd_osdcap
+ osdcap.cc
+)
+if(HAS_VTA)
+ set_source_files_properties(osdcap.cc PROPERTIES
+ COMPILE_FLAGS -fno-var-tracking-assignments)
+endif()
+add_ceph_unittest(unittest_osd_osdcap)
+target_link_libraries(unittest_osd_osdcap osd global ${BLKID_LIBRARIES})
+
+# unittest ExtentCache
+add_executable(unittest_extent_cache
+ test_extent_cache.cc
+)
+add_ceph_unittest(unittest_extent_cache)
+target_link_libraries(unittest_extent_cache osd global ${BLKID_LIBRARIES})
+
+# unittest PGTransaction
+add_executable(unittest_pg_transaction
+ test_pg_transaction.cc
+)
+add_ceph_unittest(unittest_pg_transaction)
+target_link_libraries(unittest_pg_transaction osd global ${BLKID_LIBRARIES})
+
+# unittest ECTransaction
+add_executable(unittest_ec_transaction
+ test_ec_transaction.cc
+)
+add_ceph_unittest(unittest_ec_transaction)
+target_link_libraries(unittest_ec_transaction osd global ${BLKID_LIBRARIES})
+
+# unittest_mclock_scheduler
+add_executable(unittest_mclock_scheduler
+ TestMClockScheduler.cc
+)
+add_ceph_unittest(unittest_mclock_scheduler)
+target_link_libraries(unittest_mclock_scheduler
+ global osd dmclock os
+)
diff --git a/src/test/osd/Object.cc b/src/test/osd/Object.cc
new file mode 100644
index 000000000..9d914abd7
--- /dev/null
+++ b/src/test/osd/Object.cc
@@ -0,0 +1,200 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include <list>
+#include <map>
+#include <set>
+#include <iostream>
+
+#include "Object.h"
+
+void ContDesc::encode(bufferlist &bl) const
+{
+ ENCODE_START(1, 1, bl);
+ encode(objnum, bl);
+ encode(cursnap, bl);
+ encode(seqnum, bl);
+ encode(prefix, bl);
+ encode(oid, bl);
+ ENCODE_FINISH(bl);
+}
+
+void ContDesc::decode(bufferlist::const_iterator &bl)
+{
+ DECODE_START(1, bl);
+ decode(objnum, bl);
+ decode(cursnap, bl);
+ decode(seqnum, bl);
+ decode(prefix, bl);
+ decode(oid, bl);
+ DECODE_FINISH(bl);
+}
+
+std::ostream &operator<<(std::ostream &out, const ContDesc &rhs)
+{
+ return out << "(ObjNum " << rhs.objnum
+ << " snap " << rhs.cursnap
+ << " seq_num " << rhs.seqnum
+ << ")";
+}
+
+void AppendGenerator::get_ranges_map(
+ const ContDesc &cont, std::map<uint64_t, uint64_t> &out) {
+ RandWrap rand(cont.seqnum);
+ uint64_t pos = off;
+ uint64_t limit = off + get_append_size(cont);
+ while (pos < limit) {
+ uint64_t segment_length = round_up(
+ rand() % (max_append_size - min_append_size),
+ alignment) + min_append_size;
+ ceph_assert(segment_length >= min_append_size);
+ if (segment_length + pos > limit) {
+ segment_length = limit - pos;
+ }
+ if (alignment)
+ ceph_assert(segment_length % alignment == 0);
+ out.insert(std::pair<uint64_t, uint64_t>(pos, segment_length));
+ pos += segment_length;
+ }
+}
+
+void VarLenGenerator::get_ranges_map(
+ const ContDesc &cont, std::map<uint64_t, uint64_t> &out) {
+ RandWrap rand(cont.seqnum);
+ uint64_t pos = 0;
+ uint64_t limit = get_length(cont);
+ bool include = false;
+ while (pos < limit) {
+ uint64_t segment_length = (rand() % (max_stride_size - min_stride_size)) + min_stride_size;
+ ceph_assert(segment_length < max_stride_size);
+ ceph_assert(segment_length >= min_stride_size);
+ if (segment_length + pos > limit) {
+ segment_length = limit - pos;
+ }
+ if (include) {
+ out.insert(std::pair<uint64_t, uint64_t>(pos, segment_length));
+ include = false;
+ } else {
+ include = true;
+ }
+ pos += segment_length;
+ }
+}
+
+void ObjectDesc::iterator::adjust_stack() {
+ while (!stack.empty() && pos >= stack.top().second.next) {
+ ceph_assert(pos == stack.top().second.next);
+ size = stack.top().second.size;
+ current = stack.top().first;
+ stack.pop();
+ }
+
+ if (stack.empty()) {
+ cur_valid_till = std::numeric_limits<uint64_t>::max();
+ } else {
+ cur_valid_till = stack.top().second.next;
+ }
+
+ while (current != layers.end() && !current->covers(pos)) {
+ uint64_t next = current->next(pos);
+ if (next < cur_valid_till) {
+ stack.emplace(current, StackState{next, size});
+ cur_valid_till = next;
+ }
+
+ ++current;
+ }
+
+ if (current == layers.end()) {
+ size = 0;
+ } else {
+ current->iter.seek(pos);
+ size = std::min(size, current->get_size());
+ cur_valid_till = std::min(
+ current->valid_till(pos),
+ cur_valid_till);
+ }
+}
+
+const ContDesc &ObjectDesc::most_recent() {
+ return layers.begin()->second;
+}
+
+void ObjectDesc::update(ContentsGenerator *gen, const ContDesc &next) {
+ layers.push_front(std::pair<std::shared_ptr<ContentsGenerator>, ContDesc>(std::shared_ptr<ContentsGenerator>(gen), next));
+ return;
+}
+
+bool ObjectDesc::check(bufferlist &to_check) {
+ iterator objiter = begin();
+ uint64_t error_at = 0;
+ if (!objiter.check_bl_advance(to_check, &error_at)) {
+ std::cout << "incorrect buffer at pos " << error_at << std::endl;
+ return false;
+ }
+
+ uint64_t size = layers.begin()->first->get_length(layers.begin()->second);
+ if (to_check.length() < size) {
+ std::cout << "only read " << to_check.length()
+ << " out of size " << size << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool ObjectDesc::check_sparse(const std::map<uint64_t, uint64_t>& extents,
+ bufferlist &to_check)
+{
+ uint64_t off = 0;
+ uint64_t pos = 0;
+ auto objiter = begin();
+ for (auto &&extiter : extents) {
+ // verify hole
+ {
+ bufferlist bl;
+ bl.append_zero(extiter.first - pos);
+ uint64_t error_at = 0;
+ if (!objiter.check_bl_advance(bl, &error_at)) {
+ std::cout << "sparse read omitted non-zero data at "
+ << error_at << std::endl;
+ return false;
+ }
+ }
+
+ ceph_assert(off <= to_check.length());
+ pos = extiter.first;
+ objiter.seek(pos);
+
+ {
+ bufferlist bl;
+ bl.substr_of(
+ to_check,
+ off,
+ std::min(to_check.length() - off, extiter.second));
+ uint64_t error_at = 0;
+ if (!objiter.check_bl_advance(bl, &error_at)) {
+ std::cout << "incorrect buffer at pos " << error_at << std::endl;
+ return false;
+ }
+ off += extiter.second;
+ pos += extiter.second;
+ }
+
+ if (pos < extiter.first + extiter.second) {
+ std::cout << "reached end of iterator first" << std::endl;
+ return false;
+ }
+ }
+
+ // final hole
+ bufferlist bl;
+ uint64_t size = layers.begin()->first->get_length(layers.begin()->second);
+ bl.append_zero(size - pos);
+ uint64_t error_at;
+ if (!objiter.check_bl_advance(bl, &error_at)) {
+ std::cout << "sparse read omitted non-zero data at "
+ << error_at << std::endl;
+ return false;
+ }
+ return true;
+}
diff --git a/src/test/osd/Object.h b/src/test/osd/Object.h
new file mode 100644
index 000000000..76ce2d2a2
--- /dev/null
+++ b/src/test/osd/Object.h
@@ -0,0 +1,540 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include "include/encoding.h"
+#include <list>
+#include <map>
+#include <set>
+#include <stack>
+#include <random>
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+/// describes an object
+class ContDesc {
+public:
+ int objnum;
+ int cursnap;
+ unsigned seqnum;
+ std::string prefix;
+ std::string oid;
+
+ ContDesc() :
+ objnum(0), cursnap(0),
+ seqnum(0), prefix("") {}
+
+ ContDesc(int objnum,
+ int cursnap,
+ unsigned seqnum,
+ const std::string &prefix) :
+ objnum(objnum), cursnap(cursnap),
+ seqnum(seqnum), prefix(prefix) {}
+
+ bool operator==(const ContDesc &rhs) {
+ return (rhs.objnum == objnum &&
+ rhs.cursnap == cursnap &&
+ rhs.seqnum == seqnum &&
+ rhs.prefix == prefix &&
+ rhs.oid == oid);
+ }
+
+ bool operator<(const ContDesc &rhs) const {
+ return seqnum < rhs.seqnum;
+ }
+
+ bool operator!=(const ContDesc &rhs) {
+ return !((*this) == rhs);
+ }
+ void encode(bufferlist &bl) const;
+ void decode(bufferlist::const_iterator &bp);
+};
+WRITE_CLASS_ENCODER(ContDesc)
+
+std::ostream &operator<<(std::ostream &out, const ContDesc &rhs);
+
+class ChunkDesc {
+public:
+ uint32_t offset;
+ uint32_t length;
+ std::string oid;
+};
+
+class ContentsGenerator {
+public:
+
+ class iterator_impl {
+ public:
+ virtual char operator*() = 0;
+ virtual iterator_impl &operator++() = 0;
+ virtual void seek(uint64_t pos) = 0;
+ virtual bool end() = 0;
+ virtual ContDesc get_cont() const = 0;
+ virtual uint64_t get_pos() const = 0;
+ virtual bufferlist gen_bl_advance(uint64_t s) {
+ bufferptr ret = buffer::create(s);
+ for (uint64_t i = 0; i < s; ++i, ++(*this)) {
+ ret[i] = **this;
+ }
+ bufferlist _ret;
+ _ret.push_back(ret);
+ return _ret;
+ }
+ /// walk through given @c bl
+ ///
+ /// @param[out] off the offset of the first byte which does not match
+ /// @returns true if @c bl matches with the content, false otherwise
+ virtual bool check_bl_advance(bufferlist &bl, uint64_t *off = nullptr) {
+ uint64_t _off = 0;
+ for (bufferlist::iterator i = bl.begin();
+ !i.end();
+ ++i, ++_off, ++(*this)) {
+ if (*i != **this) {
+ if (off)
+ *off = _off;
+ return false;
+ }
+ }
+ return true;
+ }
+ virtual ~iterator_impl() {};
+ };
+
+ class iterator {
+ public:
+ ContentsGenerator *parent;
+ iterator_impl *impl;
+ char operator *() { return **impl; }
+ iterator &operator++() { ++(*impl); return *this; };
+ void seek(uint64_t pos) { impl->seek(pos); }
+ bool end() { return impl->end(); }
+ ~iterator() { parent->put_iterator_impl(impl); }
+ iterator(const iterator &rhs) : parent(rhs.parent) {
+ impl = parent->dup_iterator_impl(rhs.impl);
+ }
+ iterator &operator=(const iterator &rhs) {
+ iterator new_iter(rhs);
+ swap(new_iter);
+ return *this;
+ }
+ void swap(iterator &other) {
+ ContentsGenerator *otherparent = other.parent;
+ other.parent = parent;
+ parent = otherparent;
+
+ iterator_impl *otherimpl = other.impl;
+ other.impl = impl;
+ impl = otherimpl;
+ }
+ bufferlist gen_bl_advance(uint64_t s) {
+ return impl->gen_bl_advance(s);
+ }
+ bool check_bl_advance(bufferlist &bl, uint64_t *off = nullptr) {
+ return impl->check_bl_advance(bl, off);
+ }
+ iterator(ContentsGenerator *parent, iterator_impl *impl) :
+ parent(parent), impl(impl) {}
+ };
+
+ virtual uint64_t get_length(const ContDesc &in) = 0;
+
+ virtual void get_ranges_map(
+ const ContDesc &cont, std::map<uint64_t, uint64_t> &out) = 0;
+ void get_ranges(const ContDesc &cont, interval_set<uint64_t> &out) {
+ std::map<uint64_t, uint64_t> ranges;
+ get_ranges_map(cont, ranges);
+ for (std::map<uint64_t, uint64_t>::iterator i = ranges.begin();
+ i != ranges.end();
+ ++i) {
+ out.insert(i->first, i->second);
+ }
+ }
+
+
+ virtual iterator_impl *get_iterator_impl(const ContDesc &in) = 0;
+
+ virtual iterator_impl *dup_iterator_impl(const iterator_impl *in) = 0;
+
+ virtual void put_iterator_impl(iterator_impl *in) = 0;
+
+ virtual ~ContentsGenerator() {};
+
+ iterator get_iterator(const ContDesc &in) {
+ return iterator(this, get_iterator_impl(in));
+ }
+};
+
+class RandGenerator : public ContentsGenerator {
+public:
+ typedef std::minstd_rand0 RandWrap;
+
+ class iterator_impl : public ContentsGenerator::iterator_impl {
+ public:
+ uint64_t pos;
+ ContDesc cont;
+ RandWrap rand;
+ RandGenerator *cont_gen;
+ char current;
+ iterator_impl(const ContDesc &cont, RandGenerator *cont_gen) :
+ pos(0), cont(cont), rand(cont.seqnum), cont_gen(cont_gen) {
+ current = rand();
+ }
+
+ ContDesc get_cont() const override { return cont; }
+ uint64_t get_pos() const override { return pos; }
+
+ iterator_impl &operator++() override {
+ pos++;
+ current = rand();
+ return *this;
+ }
+
+ char operator*() override {
+ return current;
+ }
+
+ void seek(uint64_t _pos) override {
+ if (_pos < pos) {
+ iterator_impl begin = iterator_impl(cont, cont_gen);
+ begin.seek(_pos);
+ *this = begin;
+ }
+ while (pos < _pos) {
+ ++(*this);
+ }
+ }
+
+ bool end() override {
+ return pos >= cont_gen->get_length(cont);
+ }
+ };
+
+ ContentsGenerator::iterator_impl *get_iterator_impl(const ContDesc &in) override {
+ RandGenerator::iterator_impl *i = new iterator_impl(in, this);
+ return i;
+ }
+
+ void put_iterator_impl(ContentsGenerator::iterator_impl *in) override {
+ delete in;
+ }
+
+ ContentsGenerator::iterator_impl *dup_iterator_impl(
+ const ContentsGenerator::iterator_impl *in) override {
+ ContentsGenerator::iterator_impl *retval = get_iterator_impl(in->get_cont());
+ retval->seek(in->get_pos());
+ return retval;
+ }
+};
+
+class VarLenGenerator : public RandGenerator {
+ uint64_t max_length;
+ uint64_t min_stride_size;
+ uint64_t max_stride_size;
+public:
+ VarLenGenerator(
+ uint64_t length, uint64_t min_stride_size, uint64_t max_stride_size) :
+ max_length(length),
+ min_stride_size(min_stride_size),
+ max_stride_size(max_stride_size) {}
+ void get_ranges_map(
+ const ContDesc &cont, std::map<uint64_t, uint64_t> &out) override;
+ uint64_t get_length(const ContDesc &in) override {
+ RandWrap rand(in.seqnum);
+ if (max_length == 0)
+ return 0;
+ return (rand() % (max_length/2)) + ((max_length - 1)/2) + 1;
+ }
+};
+
+class AttrGenerator : public RandGenerator {
+ uint64_t max_len;
+ uint64_t big_max_len;
+public:
+ AttrGenerator(uint64_t max_len, uint64_t big_max_len)
+ : max_len(max_len), big_max_len(big_max_len) {}
+ void get_ranges_map(
+ const ContDesc &cont, std::map<uint64_t, uint64_t> &out) override {
+ out.insert(std::pair<uint64_t, uint64_t>(0, get_length(cont)));
+ }
+ uint64_t get_length(const ContDesc &in) override {
+ RandWrap rand(in.seqnum);
+ // make some attrs big
+ if (in.seqnum & 3)
+ return (rand() % max_len);
+ else
+ return (rand() % big_max_len);
+ }
+ bufferlist gen_bl(const ContDesc &in) {
+ bufferlist bl;
+ for (iterator i = get_iterator(in); !i.end(); ++i) {
+ bl.append(*i);
+ }
+ ceph_assert(bl.length() < big_max_len);
+ return bl;
+ }
+};
+
+class AppendGenerator : public RandGenerator {
+ uint64_t off;
+ uint64_t alignment;
+ uint64_t min_append_size;
+ uint64_t max_append_size;
+ uint64_t max_append_total;
+
+ uint64_t round_up(uint64_t in, uint64_t by) {
+ if (by)
+ in += (by - (in % by));
+ return in;
+ }
+
+public:
+ AppendGenerator(
+ uint64_t off,
+ uint64_t alignment,
+ uint64_t min_append_size,
+ uint64_t _max_append_size,
+ uint64_t max_append_multiple) :
+ off(off), alignment(alignment),
+ min_append_size(round_up(min_append_size, alignment)),
+ max_append_size(round_up(_max_append_size, alignment)) {
+ if (_max_append_size == min_append_size)
+ max_append_size += alignment;
+ max_append_total = max_append_multiple * max_append_size;
+ }
+ uint64_t get_append_size(const ContDesc &in) {
+ RandWrap rand(in.seqnum);
+ return round_up(rand() % max_append_total, alignment);
+ }
+ uint64_t get_length(const ContDesc &in) override {
+ return off + get_append_size(in);
+ }
+ void get_ranges_map(
+ const ContDesc &cont, std::map<uint64_t, uint64_t> &out) override;
+};
+
+class ObjectDesc {
+public:
+ ObjectDesc()
+ : exists(false), dirty(false),
+ version(0), flushed(false) {}
+ ObjectDesc(const ContDesc &init, ContentsGenerator *cont_gen)
+ : exists(false), dirty(false),
+ version(0), flushed(false) {
+ layers.push_front(std::pair<std::shared_ptr<ContentsGenerator>, ContDesc>(std::shared_ptr<ContentsGenerator>(cont_gen), init));
+ }
+
+ class iterator {
+ public:
+ uint64_t pos;
+ uint64_t size;
+ uint64_t cur_valid_till;
+
+ class ContState {
+ interval_set<uint64_t> ranges;
+ const uint64_t size;
+
+ public:
+ ContDesc cont;
+ std::shared_ptr<ContentsGenerator> gen;
+ ContentsGenerator::iterator iter;
+
+ ContState(
+ const ContDesc &_cont,
+ std::shared_ptr<ContentsGenerator> _gen,
+ ContentsGenerator::iterator _iter)
+ : size(_gen->get_length(_cont)), cont(_cont), gen(_gen), iter(_iter) {
+ gen->get_ranges(cont, ranges);
+ }
+
+ const interval_set<uint64_t> &get_ranges() {
+ return ranges;
+ }
+
+ uint64_t get_size() {
+ return gen->get_length(cont);
+ }
+
+ bool covers(uint64_t pos) {
+ return ranges.contains(pos) || (!ranges.starts_after(pos) && pos >= size);
+ }
+
+ uint64_t next(uint64_t pos) {
+ ceph_assert(!covers(pos));
+ return ranges.starts_after(pos) ? ranges.start_after(pos) : size;
+ }
+
+ uint64_t valid_till(uint64_t pos) {
+ ceph_assert(covers(pos));
+ return ranges.contains(pos) ?
+ ranges.end_after(pos) :
+ std::numeric_limits<uint64_t>::max();
+ }
+ };
+ // from latest to earliest
+ using layers_t = std::vector<ContState>;
+ layers_t layers;
+
+ struct StackState {
+ const uint64_t next;
+ const uint64_t size;
+ };
+ std::stack<std::pair<layers_t::iterator, StackState> > stack;
+ layers_t::iterator current;
+
+ explicit iterator(ObjectDesc &obj) :
+ pos(0),
+ size(obj.layers.begin()->first->get_length(obj.layers.begin()->second)),
+ cur_valid_till(0) {
+ for (auto &&i : obj.layers) {
+ layers.push_back({i.second, i.first, i.first->get_iterator(i.second)});
+ }
+ current = layers.begin();
+
+ adjust_stack();
+ }
+
+ void adjust_stack();
+ iterator &operator++() {
+ ceph_assert(cur_valid_till >= pos);
+ ++pos;
+ if (pos >= cur_valid_till) {
+ adjust_stack();
+ }
+ return *this;
+ }
+
+ char operator*() {
+ if (current == layers.end()) {
+ return '\0';
+ } else {
+ return pos >= size ? '\0' : *(current->iter);
+ }
+ }
+
+ bool end() {
+ return pos >= size;
+ }
+
+ // advance @c pos to given position
+ void seek(uint64_t _pos) {
+ if (_pos < pos) {
+ ceph_abort();
+ }
+ while (pos < _pos) {
+ ceph_assert(cur_valid_till >= pos);
+ uint64_t next = std::min(_pos - pos, cur_valid_till - pos);
+ pos += next;
+
+ if (pos >= cur_valid_till) {
+ ceph_assert(pos == cur_valid_till);
+ adjust_stack();
+ }
+ }
+ ceph_assert(pos == _pos);
+ }
+
+ // grab the bytes in the range of [pos, pos+s), and advance @c pos
+ //
+ // @returns the bytes in the specified range
+ bufferlist gen_bl_advance(uint64_t s) {
+ bufferlist ret;
+ while (s > 0) {
+ ceph_assert(cur_valid_till >= pos);
+ uint64_t next = std::min(s, cur_valid_till - pos);
+ if (current != layers.end() && pos < size) {
+ ret.append(current->iter.gen_bl_advance(next));
+ } else {
+ ret.append_zero(next);
+ }
+
+ pos += next;
+ ceph_assert(next <= s);
+ s -= next;
+
+ if (pos >= cur_valid_till) {
+ ceph_assert(cur_valid_till == pos);
+ adjust_stack();
+ }
+ }
+ return ret;
+ }
+
+ // compare the range of [pos, pos+bl.length()) with given @c bl, and
+ // advance @pos if all bytes in the range match
+ //
+ // @param error_at the offset of the first byte which does not match
+ // @returns true if all bytes match, false otherwise
+ bool check_bl_advance(bufferlist &bl, uint64_t *error_at = nullptr) {
+ uint64_t off = 0;
+ while (off < bl.length()) {
+ ceph_assert(cur_valid_till >= pos);
+ uint64_t next = std::min(bl.length() - off, cur_valid_till - pos);
+
+ bufferlist to_check;
+ to_check.substr_of(bl, off, next);
+ if (current != layers.end() && pos < size) {
+ if (!current->iter.check_bl_advance(to_check, error_at)) {
+ if (error_at)
+ *error_at += off;
+ return false;
+ }
+ } else {
+ uint64_t at = pos;
+ for (auto i = to_check.begin(); !i.end(); ++i, ++at) {
+ if (*i) {
+ if (error_at)
+ *error_at = at;
+ return false;
+ }
+ }
+ }
+
+ pos += next;
+ off += next;
+ ceph_assert(off <= bl.length());
+
+ if (pos >= cur_valid_till) {
+ ceph_assert(cur_valid_till == pos);
+ adjust_stack();
+ }
+ }
+ ceph_assert(off == bl.length());
+ return true;
+ }
+ };
+
+ iterator begin() {
+ return iterator(*this);
+ }
+
+ bool deleted() {
+ return !exists;
+ }
+
+ bool has_contents() {
+ return layers.size();
+ }
+
+ // takes ownership of gen
+ void update(ContentsGenerator *gen, const ContDesc &next);
+ bool check(bufferlist &to_check);
+ bool check_sparse(const std::map<uint64_t, uint64_t>& extends,
+ bufferlist &to_check);
+ const ContDesc &most_recent();
+ ContentsGenerator *most_recent_gen() {
+ return layers.begin()->first.get();
+ }
+ std::map<std::string, ContDesc> attrs; // Both omap and xattrs
+ bufferlist header;
+ bool exists;
+ bool dirty;
+
+ uint64_t version;
+ std::string redirect_target;
+ std::map<uint64_t, ChunkDesc> chunk_info;
+ bool flushed;
+private:
+ std::list<std::pair<std::shared_ptr<ContentsGenerator>, ContDesc> > layers;
+};
+
+#endif
diff --git a/src/test/osd/RadosModel.cc b/src/test/osd/RadosModel.cc
new file mode 100644
index 000000000..501bf3b13
--- /dev/null
+++ b/src/test/osd/RadosModel.cc
@@ -0,0 +1,36 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include <list>
+#include <map>
+#include <set>
+#include "include/rados/librados.h"
+#include "RadosModel.h"
+#include "TestOpStat.h"
+
+
+void TestOp::begin()
+{
+ _begin();
+}
+
+void TestOp::finish(TestOp::CallbackInfo *info)
+{
+ _finish(info);
+}
+
+void read_callback(librados::completion_t comp, void *arg) {
+ TestOp* op = static_cast<TestOp*>(arg);
+ op->finish(NULL);
+}
+
+void write_callback(librados::completion_t comp, void *arg) {
+ std::pair<TestOp*, TestOp::CallbackInfo*> *args =
+ static_cast<std::pair<TestOp*, TestOp::CallbackInfo*> *>(arg);
+ TestOp* op = args->first;
+ TestOp::CallbackInfo *info = args->second;
+ op->finish(info);
+ delete args;
+ delete info;
+}
diff --git a/src/test/osd/RadosModel.h b/src/test/osd/RadosModel.h
new file mode 100644
index 000000000..1e5d0e908
--- /dev/null
+++ b/src/test/osd/RadosModel.h
@@ -0,0 +1,3520 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/int_types.h"
+
+#include "common/ceph_mutex.h"
+#include "include/rados/librados.hpp"
+
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <map>
+#include <set>
+#include <list>
+#include <string>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include "Object.h"
+#include "TestOpStat.h"
+#include "test/librados/test.h"
+#include "common/sharedptr_registry.hpp"
+#include "common/errno.h"
+#include "osd/HitSet.h"
+#include "common/ceph_crypto.h"
+
+#include "cls/cas/cls_cas_client.h"
+#include "cls/cas/cls_cas_internal.h"
+
+#ifndef RADOSMODEL_H
+#define RADOSMODEL_H
+
+class RadosTestContext;
+class TestOpStat;
+
+template <typename T>
+typename T::iterator rand_choose(T &cont) {
+ if (std::empty(cont)) {
+ return std::end(cont);
+ }
+ return std::next(std::begin(cont), rand() % cont.size());
+}
+
+enum TestOpType {
+ TEST_OP_READ,
+ TEST_OP_WRITE,
+ TEST_OP_WRITE_EXCL,
+ TEST_OP_WRITESAME,
+ TEST_OP_DELETE,
+ TEST_OP_SNAP_CREATE,
+ TEST_OP_SNAP_REMOVE,
+ TEST_OP_ROLLBACK,
+ TEST_OP_SETATTR,
+ TEST_OP_RMATTR,
+ TEST_OP_WATCH,
+ TEST_OP_COPY_FROM,
+ TEST_OP_HIT_SET_LIST,
+ TEST_OP_UNDIRTY,
+ TEST_OP_IS_DIRTY,
+ TEST_OP_CACHE_FLUSH,
+ TEST_OP_CACHE_TRY_FLUSH,
+ TEST_OP_CACHE_EVICT,
+ TEST_OP_APPEND,
+ TEST_OP_APPEND_EXCL,
+ TEST_OP_SET_REDIRECT,
+ TEST_OP_UNSET_REDIRECT,
+ TEST_OP_CHUNK_READ,
+ TEST_OP_TIER_PROMOTE,
+ TEST_OP_TIER_FLUSH,
+ TEST_OP_SET_CHUNK,
+ TEST_OP_TIER_EVICT
+};
+
+class TestWatchContext : public librados::WatchCtx2 {
+ TestWatchContext(const TestWatchContext&);
+public:
+ ceph::condition_variable cond;
+ uint64_t handle = 0;
+ bool waiting = false;
+ ceph::mutex lock = ceph::make_mutex("watch lock");
+ TestWatchContext() = default;
+ void handle_notify(uint64_t notify_id, uint64_t cookie,
+ uint64_t notifier_id,
+ bufferlist &bl) override {
+ std::lock_guard l{lock};
+ waiting = false;
+ cond.notify_all();
+ }
+ void handle_error(uint64_t cookie, int err) override {
+ std::lock_guard l{lock};
+ std::cout << "watch handle_error " << err << std::endl;
+ }
+ void start() {
+ std::lock_guard l{lock};
+ waiting = true;
+ }
+ void wait() {
+ std::unique_lock l{lock};
+ cond.wait(l, [this] { return !waiting; });
+ }
+ uint64_t &get_handle() {
+ return handle;
+ }
+};
+
+class TestOp {
+public:
+ const int num;
+ RadosTestContext *context;
+ TestOpStat *stat;
+ bool done = false;
+ TestOp(int n, RadosTestContext *context,
+ TestOpStat *stat = 0)
+ : num(n),
+ context(context),
+ stat(stat)
+ {}
+
+ virtual ~TestOp() {};
+
+ /**
+ * This struct holds data to be passed by a callback
+ * to a TestOp::finish method.
+ */
+ struct CallbackInfo {
+ uint64_t id;
+ explicit CallbackInfo(uint64_t id) : id(id) {}
+ virtual ~CallbackInfo() {};
+ };
+
+ virtual void _begin() = 0;
+
+ /**
+ * Called when the operation completes.
+ * This should be overridden by asynchronous operations.
+ *
+ * @param info information stored by a callback, or NULL -
+ * useful for multi-operation TestOps
+ */
+ virtual void _finish(CallbackInfo *info)
+ {
+ return;
+ }
+ virtual std::string getType() = 0;
+ virtual bool finished()
+ {
+ return true;
+ }
+
+ void begin();
+ void finish(CallbackInfo *info);
+ virtual bool must_quiesce_other_ops() { return false; }
+};
+
+class TestOpGenerator {
+public:
+ virtual ~TestOpGenerator() {};
+ virtual TestOp *next(RadosTestContext &context) = 0;
+};
+
+class RadosTestContext {
+public:
+ ceph::mutex state_lock = ceph::make_mutex("Context Lock");
+ ceph::condition_variable wait_cond;
+ // snap => {oid => desc}
+ std::map<int, std::map<std::string,ObjectDesc> > pool_obj_cont;
+ std::set<std::string> oid_in_use;
+ std::set<std::string> oid_not_in_use;
+ std::set<std::string> oid_flushing;
+ std::set<std::string> oid_not_flushing;
+ std::set<std::string> oid_redirect_not_in_use;
+ std::set<std::string> oid_redirect_in_use;
+ std::set<std::string> oid_set_chunk_tgt_pool;
+ SharedPtrRegistry<int, int> snaps_in_use;
+ int current_snap;
+ std::string pool_name;
+ librados::IoCtx io_ctx;
+ librados::Rados rados;
+ int next_oid;
+ std::string prefix;
+ int errors;
+ int max_in_flight;
+ int seq_num;
+ std::map<int,uint64_t> snaps;
+ uint64_t seq;
+ const char *rados_id;
+ bool initialized;
+ std::map<std::string, TestWatchContext*> watches;
+ const uint64_t max_size;
+ const uint64_t min_stride_size;
+ const uint64_t max_stride_size;
+ AttrGenerator attr_gen;
+ const bool no_omap;
+ const bool no_sparse;
+ bool pool_snaps;
+ bool write_fadvise_dontneed;
+ std::string low_tier_pool_name;
+ librados::IoCtx low_tier_io_ctx;
+ int snapname_num;
+ std::map<std::string, std::string> redirect_objs;
+ bool enable_dedup;
+ std::string chunk_algo;
+ std::string chunk_size;
+
+ RadosTestContext(const std::string &pool_name,
+ int max_in_flight,
+ uint64_t max_size,
+ uint64_t min_stride_size,
+ uint64_t max_stride_size,
+ bool no_omap,
+ bool no_sparse,
+ bool pool_snaps,
+ bool write_fadvise_dontneed,
+ const std::string &low_tier_pool_name,
+ bool enable_dedup,
+ std::string chunk_algo,
+ std::string chunk_size,
+ const char *id = 0) :
+ pool_obj_cont(),
+ current_snap(0),
+ pool_name(pool_name),
+ next_oid(0),
+ errors(0),
+ max_in_flight(max_in_flight),
+ seq_num(0), seq(0),
+ rados_id(id), initialized(false),
+ max_size(max_size),
+ min_stride_size(min_stride_size), max_stride_size(max_stride_size),
+ attr_gen(2000, 20000),
+ no_omap(no_omap),
+ no_sparse(no_sparse),
+ pool_snaps(pool_snaps),
+ write_fadvise_dontneed(write_fadvise_dontneed),
+ low_tier_pool_name(low_tier_pool_name),
+ snapname_num(0),
+ enable_dedup(enable_dedup),
+ chunk_algo(chunk_algo),
+ chunk_size(chunk_size)
+ {
+ }
+
+ int init()
+ {
+ int r = rados.init(rados_id);
+ if (r < 0)
+ return r;
+ r = rados.conf_read_file(NULL);
+ if (r < 0)
+ return r;
+ r = rados.conf_parse_env(NULL);
+ if (r < 0)
+ return r;
+ r = rados.connect();
+ if (r < 0)
+ return r;
+ r = rados.ioctx_create(pool_name.c_str(), io_ctx);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+ if (!low_tier_pool_name.empty()) {
+ r = rados.ioctx_create(low_tier_pool_name.c_str(), low_tier_io_ctx);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+ }
+ bufferlist inbl;
+ r = rados.mon_command(
+ "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
+ "\", \"var\": \"write_fadvise_dontneed\", \"val\": \"" + (write_fadvise_dontneed ? "true" : "false") + "\"}",
+ inbl, NULL, NULL);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+ if (enable_dedup) {
+ r = rados.mon_command(
+ "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
+ "\", \"var\": \"fingerprint_algorithm\", \"val\": \"" + "sha256" + "\"}",
+ inbl, NULL, NULL);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+ r = rados.mon_command(
+ "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
+ "\", \"var\": \"dedup_tier\", \"val\": \"" + low_tier_pool_name + "\"}",
+ inbl, NULL, NULL);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+ r = rados.mon_command(
+ "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
+ "\", \"var\": \"dedup_chunk_algorithm\", \"val\": \"" + chunk_algo + "\"}",
+ inbl, NULL, NULL);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+ r = rados.mon_command(
+ "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
+ "\", \"var\": \"dedup_cdc_chunk_size\", \"val\": \"" + chunk_size + "\"}",
+ inbl, NULL, NULL);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+ }
+
+ char hostname_cstr[100];
+ gethostname(hostname_cstr, 100);
+ std::stringstream hostpid;
+ hostpid << hostname_cstr << getpid() << "-";
+ prefix = hostpid.str();
+ ceph_assert(!initialized);
+ initialized = true;
+ return 0;
+ }
+
+ void shutdown()
+ {
+ if (initialized) {
+ rados.shutdown();
+ }
+ }
+
+ void loop(TestOpGenerator *gen)
+ {
+ ceph_assert(initialized);
+ std::list<TestOp*> inflight;
+ std::unique_lock state_locker{state_lock};
+
+ TestOp *next = gen->next(*this);
+ TestOp *waiting = NULL;
+
+ while (next || !inflight.empty()) {
+ if (next && next->must_quiesce_other_ops() && !inflight.empty()) {
+ waiting = next;
+ next = NULL; // Force to wait for inflight to drain
+ }
+ if (next) {
+ inflight.push_back(next);
+ }
+ state_lock.unlock();
+ if (next) {
+ (*inflight.rbegin())->begin();
+ }
+ state_lock.lock();
+ while (1) {
+ for (auto i = inflight.begin();
+ i != inflight.end();) {
+ if ((*i)->finished()) {
+ std::cout << (*i)->num << ": done (" << (inflight.size()-1) << " left)" << std::endl;
+ delete *i;
+ inflight.erase(i++);
+ } else {
+ ++i;
+ }
+ }
+
+ if (inflight.size() >= (unsigned) max_in_flight || (!next && !inflight.empty())) {
+ std::cout << " waiting on " << inflight.size() << std::endl;
+ wait_cond.wait(state_locker);
+ } else {
+ break;
+ }
+ }
+ if (waiting) {
+ next = waiting;
+ waiting = NULL;
+ } else {
+ next = gen->next(*this);
+ }
+ }
+ }
+
+ void kick()
+ {
+ wait_cond.notify_all();
+ }
+
+ TestWatchContext *get_watch_context(const std::string &oid) {
+ return watches.count(oid) ? watches[oid] : 0;
+ }
+
+ TestWatchContext *watch(const std::string &oid) {
+ ceph_assert(!watches.count(oid));
+ return (watches[oid] = new TestWatchContext);
+ }
+
+ void unwatch(const std::string &oid) {
+ ceph_assert(watches.count(oid));
+ delete watches[oid];
+ watches.erase(oid);
+ }
+
+ ObjectDesc get_most_recent(const std::string &oid) {
+ ObjectDesc new_obj;
+ for (auto i = pool_obj_cont.rbegin();
+ i != pool_obj_cont.rend();
+ ++i) {
+ std::map<std::string,ObjectDesc>::iterator j = i->second.find(oid);
+ if (j != i->second.end()) {
+ new_obj = j->second;
+ break;
+ }
+ }
+ return new_obj;
+ }
+
+ void rm_object_attrs(const std::string &oid, const std::set<std::string> &attrs)
+ {
+ ObjectDesc new_obj = get_most_recent(oid);
+ for (std::set<std::string>::const_iterator i = attrs.begin();
+ i != attrs.end();
+ ++i) {
+ new_obj.attrs.erase(*i);
+ }
+ new_obj.dirty = true;
+ new_obj.flushed = false;
+ pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
+ }
+
+ void remove_object_header(const std::string &oid)
+ {
+ ObjectDesc new_obj = get_most_recent(oid);
+ new_obj.header = bufferlist();
+ new_obj.dirty = true;
+ new_obj.flushed = false;
+ pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
+ }
+
+
+ void update_object_header(const std::string &oid, const bufferlist &bl)
+ {
+ ObjectDesc new_obj = get_most_recent(oid);
+ new_obj.header = bl;
+ new_obj.exists = true;
+ new_obj.dirty = true;
+ new_obj.flushed = false;
+ pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
+ }
+
+ void update_object_attrs(const std::string &oid, const std::map<std::string, ContDesc> &attrs)
+ {
+ ObjectDesc new_obj = get_most_recent(oid);
+ for (auto i = attrs.cbegin();
+ i != attrs.cend();
+ ++i) {
+ new_obj.attrs[i->first] = i->second;
+ }
+ new_obj.exists = true;
+ new_obj.dirty = true;
+ new_obj.flushed = false;
+ pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
+ }
+
+ void update_object(ContentsGenerator *cont_gen,
+ const std::string &oid, const ContDesc &contents)
+ {
+ ObjectDesc new_obj = get_most_recent(oid);
+ new_obj.exists = true;
+ new_obj.dirty = true;
+ new_obj.flushed = false;
+ new_obj.update(cont_gen,
+ contents);
+ pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
+ }
+
+ void update_object_full(const std::string &oid, const ObjectDesc &contents)
+ {
+ pool_obj_cont[current_snap].insert_or_assign(oid, contents);
+ pool_obj_cont[current_snap][oid].dirty = true;
+ }
+
+ void update_object_undirty(const std::string &oid)
+ {
+ ObjectDesc new_obj = get_most_recent(oid);
+ new_obj.dirty = false;
+ pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
+ }
+
+ void update_object_version(const std::string &oid, uint64_t version,
+ int snap = -1)
+ {
+ for (auto i = pool_obj_cont.rbegin();
+ i != pool_obj_cont.rend();
+ ++i) {
+ if (snap != -1 && snap < i->first)
+ continue;
+ std::map<std::string,ObjectDesc>::iterator j = i->second.find(oid);
+ if (j != i->second.end()) {
+ if (version)
+ j->second.version = version;
+ std::cout << __func__ << " oid " << oid
+ << " v " << version << " " << j->second.most_recent()
+ << " " << (j->second.dirty ? "dirty" : "clean")
+ << " " << (j->second.exists ? "exists" : "dne")
+ << std::endl;
+ break;
+ }
+ }
+ }
+
+ void remove_object(const std::string &oid)
+ {
+ ceph_assert(!get_watch_context(oid));
+ ObjectDesc new_obj;
+ pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
+ }
+
+ bool find_object(const std::string &oid, ObjectDesc *contents, int snap = -1) const
+ {
+ for (auto i = pool_obj_cont.crbegin();
+ i != pool_obj_cont.crend();
+ ++i) {
+ if (snap != -1 && snap < i->first) continue;
+ if (i->second.count(oid) != 0) {
+ *contents = i->second.find(oid)->second;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void update_object_redirect_target(const std::string &oid, const std::string &target)
+ {
+ redirect_objs[oid] = target;
+ }
+
+ void update_object_chunk_target(const std::string &oid, uint64_t offset, const ChunkDesc &info)
+ {
+ for (auto i = pool_obj_cont.crbegin();
+ i != pool_obj_cont.crend();
+ ++i) {
+ if (i->second.count(oid) != 0) {
+ ObjectDesc obj_desc = i->second.find(oid)->second;
+ obj_desc.chunk_info[offset] = info;
+ update_object_full(oid, obj_desc);
+ return ;
+ }
+ }
+ return;
+ }
+
+ bool object_existed_at(const std::string &oid, int snap = -1) const
+ {
+ ObjectDesc contents;
+ bool found = find_object(oid, &contents, snap);
+ return found && contents.exists;
+ }
+
+ void remove_snap(int snap)
+ {
+ std::map<int, std::map<std::string,ObjectDesc> >::iterator next_iter = pool_obj_cont.find(snap);
+ ceph_assert(next_iter != pool_obj_cont.end());
+ std::map<int, std::map<std::string,ObjectDesc> >::iterator current_iter = next_iter++;
+ ceph_assert(current_iter != pool_obj_cont.end());
+ std::map<std::string,ObjectDesc> &current = current_iter->second;
+ std::map<std::string,ObjectDesc> &next = next_iter->second;
+ for (auto i = current.begin(); i != current.end(); ++i) {
+ if (next.count(i->first) == 0) {
+ next.insert(std::pair<std::string,ObjectDesc>(i->first, i->second));
+ }
+ }
+ pool_obj_cont.erase(current_iter);
+ snaps.erase(snap);
+ }
+
+ void add_snap(uint64_t snap)
+ {
+ snaps[current_snap] = snap;
+ current_snap++;
+ pool_obj_cont[current_snap];
+ seq = snap;
+ }
+
+ void roll_back(const std::string &oid, int snap)
+ {
+ ceph_assert(!get_watch_context(oid));
+ ObjectDesc contents;
+ find_object(oid, &contents, snap);
+ contents.dirty = true;
+ contents.flushed = false;
+ pool_obj_cont.rbegin()->second.insert_or_assign(oid, contents);
+ }
+
+ void update_object_tier_flushed(const std::string &oid, int snap)
+ {
+ for (auto i = pool_obj_cont.rbegin();
+ i != pool_obj_cont.rend();
+ ++i) {
+ if (snap != -1 && snap < i->first)
+ continue;
+ std::map<std::string,ObjectDesc>::iterator j = i->second.find(oid);
+ if (j != i->second.end()) {
+ j->second.flushed = true;
+ break;
+ }
+ }
+ }
+
+ bool check_oldest_snap_flushed(const std::string &oid, int snap)
+ {
+ for (auto i = pool_obj_cont.rbegin();
+ i != pool_obj_cont.rend();
+ ++i) {
+ if (snap != -1 && snap < i->first)
+ continue;
+ std::map<std::string,ObjectDesc>::iterator j = i->second.find(oid);
+ if (j != i->second.end() && !j->second.flushed) {
+ std::cout << __func__ << " oid " << oid
+ << " v " << j->second.version << " " << j->second.most_recent()
+ << " " << (j->second.flushed ? "flushed" : "unflushed")
+ << " " << i->first << std::endl;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool check_chunks_refcount(librados::IoCtx &chunk_pool_ctx, librados::IoCtx &manifest_pool_ctx)
+ {
+ librados::ObjectCursor shard_start;
+ librados::ObjectCursor shard_end;
+ librados::ObjectCursor begin;
+ librados::ObjectCursor end;
+ begin = chunk_pool_ctx.object_list_begin();
+ end = chunk_pool_ctx.object_list_end();
+
+ chunk_pool_ctx.object_list_slice(
+ begin,
+ end,
+ 1,
+ 1,
+ &shard_start,
+ &shard_end);
+
+ librados::ObjectCursor c(shard_start);
+ while(c < shard_end)
+ {
+ std::vector<librados::ObjectItem> result;
+ int r = chunk_pool_ctx.object_list(c, shard_end, 12, {}, &result, &c);
+ if (r < 0) {
+ std::cerr << "error object_list : " << cpp_strerror(r) << std::endl;
+ return false;
+ }
+
+ for (const auto & i : result) {
+ auto oid = i.oid;
+ chunk_refs_t refs;
+ {
+ bufferlist t;
+ r = chunk_pool_ctx.getxattr(oid, CHUNK_REFCOUNT_ATTR, t);
+ if (r < 0) {
+ continue;
+ }
+ auto p = t.cbegin();
+ decode(refs, p);
+ }
+ ceph_assert(refs.get_type() == chunk_refs_t::TYPE_BY_OBJECT);
+
+ chunk_refs_by_object_t *byo =
+ static_cast<chunk_refs_by_object_t*>(refs.r.get());
+
+ for (auto& pp : byo->by_object) {
+ int src_refcount = 0;
+ int dst_refcount = byo->by_object.count(pp);
+ for (int tries = 0; tries < 10; tries++) {
+ r = cls_cas_references_chunk(manifest_pool_ctx, pp.oid.name, oid);
+ if (r == -ENOENT || r == -ENOLINK) {
+ src_refcount = 0;
+ } else if (r == -EBUSY) {
+ sleep(10);
+ continue;
+ } else {
+ src_refcount = r;
+ }
+ break;
+ }
+ if (src_refcount > dst_refcount) {
+ std::cerr << " src_object " << pp
+ << ": src_refcount " << src_refcount
+ << ", dst_object " << oid
+ << ": dst_refcount " << dst_refcount
+ << std::endl;
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+};
+
+void read_callback(librados::completion_t comp, void *arg);
+void write_callback(librados::completion_t comp, void *arg);
+
+/// remove random xattrs from given object, and optionally remove omap
+/// entries if @c no_omap is not specified in context
+class RemoveAttrsOp : public TestOp {
+public:
+ std::string oid;
+ librados::ObjectWriteOperation op;
+ librados::AioCompletion *comp;
+ RemoveAttrsOp(int n, RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat)
+ : TestOp(n, context, stat), oid(oid), comp(NULL)
+ {}
+
+ void _begin() override
+ {
+ ContDesc cont;
+ std::set<std::string> to_remove;
+ {
+ std::lock_guard l{context->state_lock};
+ ObjectDesc obj;
+ if (!context->find_object(oid, &obj)) {
+ context->kick();
+ done = true;
+ return;
+ }
+ cont = ContDesc(context->seq_num, context->current_snap,
+ context->seq_num, "");
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ if (rand() % 30) {
+ ContentsGenerator::iterator iter = context->attr_gen.get_iterator(cont);
+ for (auto i = obj.attrs.begin();
+ i != obj.attrs.end();
+ ++i, ++iter) {
+ if (!(*iter % 3)) {
+ to_remove.insert(i->first);
+ op.rmxattr(i->first.c_str());
+ }
+ }
+ if (to_remove.empty()) {
+ context->kick();
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ done = true;
+ return;
+ }
+ if (!context->no_omap) {
+ op.omap_rm_keys(to_remove);
+ }
+ } else {
+ if (!context->no_omap) {
+ op.omap_clear();
+ }
+ for (auto i = obj.attrs.begin();
+ i != obj.attrs.end();
+ ++i) {
+ op.rmxattr(i->first.c_str());
+ to_remove.insert(i->first);
+ }
+ context->remove_object_header(oid);
+ }
+ context->rm_object_attrs(oid, to_remove);
+ }
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp = context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+ done = true;
+ context->update_object_version(oid, comp->get_version64());
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "RemoveAttrsOp";
+ }
+};
+
+/// add random xattrs to given object, and optionally add omap
+/// entries if @c no_omap is not specified in context
+class SetAttrsOp : public TestOp {
+public:
+ std::string oid;
+ librados::ObjectWriteOperation op;
+ librados::AioCompletion *comp;
+ SetAttrsOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat)
+ : TestOp(n, context, stat),
+ oid(oid), comp(NULL)
+ {}
+
+ void _begin() override
+ {
+ ContDesc cont;
+ {
+ std::lock_guard l{context->state_lock};
+ cont = ContDesc(context->seq_num, context->current_snap,
+ context->seq_num, "");
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ }
+
+ std::map<std::string, bufferlist> omap_contents;
+ std::map<std::string, ContDesc> omap;
+ bufferlist header;
+ ContentsGenerator::iterator keygen = context->attr_gen.get_iterator(cont);
+ op.create(false);
+ while (!*keygen) ++keygen;
+ while (*keygen) {
+ if (*keygen != '_')
+ header.append(*keygen);
+ ++keygen;
+ }
+ for (int i = 0; i < 20; ++i) {
+ std::string key;
+ while (!*keygen) ++keygen;
+ while (*keygen && key.size() < 40) {
+ key.push_back((*keygen % 20) + 'a');
+ ++keygen;
+ }
+ ContDesc val(cont);
+ val.seqnum += (unsigned)(*keygen);
+ val.prefix = ("oid: " + oid);
+ omap[key] = val;
+ bufferlist val_buffer = context->attr_gen.gen_bl(val);
+ omap_contents[key] = val_buffer;
+ op.setxattr(key.c_str(), val_buffer);
+ }
+ if (!context->no_omap) {
+ op.omap_set_header(header);
+ op.omap_set(omap_contents);
+ }
+
+ {
+ std::lock_guard l{context->state_lock};
+ context->update_object_header(oid, header);
+ context->update_object_attrs(oid, omap);
+ }
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+ int r;
+ if ((r = comp->get_return_value())) {
+ std::cerr << "err " << r << std::endl;
+ ceph_abort();
+ }
+ done = true;
+ context->update_object_version(oid, comp->get_version64());
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "SetAttrsOp";
+ }
+};
+
+class WriteOp : public TestOp {
+public:
+ const std::string oid;
+ ContDesc cont;
+ std::set<librados::AioCompletion *> waiting;
+ librados::AioCompletion *rcompletion = nullptr;
+ // numbers of async ops submitted
+ uint64_t waiting_on = 0;
+ uint64_t last_acked_tid = 0;
+
+ librados::ObjectReadOperation read_op;
+ librados::ObjectWriteOperation write_op;
+ bufferlist rbuffer;
+
+ const bool do_append;
+ const bool do_excl;
+
+ WriteOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ bool do_append,
+ bool do_excl,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(oid),
+ do_append(do_append),
+ do_excl(do_excl)
+ {}
+
+ void _begin() override
+ {
+ assert(!done);
+ std::stringstream acc;
+ std::lock_guard state_locker{context->state_lock};
+ acc << context->prefix << "OID: " << oid << " snap " << context->current_snap << std::endl;
+ std::string prefix = acc.str();
+
+ cont = ContDesc(context->seq_num, context->current_snap, context->seq_num, prefix);
+
+ ContentsGenerator *cont_gen;
+ if (do_append) {
+ ObjectDesc old_value;
+ bool found = context->find_object(oid, &old_value);
+ uint64_t prev_length = found && old_value.has_contents() ?
+ old_value.most_recent_gen()->get_length(old_value.most_recent()) :
+ 0;
+ bool requires_alignment;
+ int r = context->io_ctx.pool_requires_alignment2(&requires_alignment);
+ ceph_assert(r == 0);
+ uint64_t alignment = 0;
+ if (requires_alignment) {
+ r = context->io_ctx.pool_required_alignment2(&alignment);
+ ceph_assert(r == 0);
+ ceph_assert(alignment != 0);
+ }
+ cont_gen = new AppendGenerator(
+ prev_length,
+ alignment,
+ context->min_stride_size,
+ context->max_stride_size,
+ 3);
+ } else {
+ cont_gen = new VarLenGenerator(
+ context->max_size, context->min_stride_size, context->max_stride_size);
+ }
+ context->update_object(cont_gen, oid, cont);
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ std::map<uint64_t, uint64_t> ranges;
+
+ cont_gen->get_ranges_map(cont, ranges);
+ std::cout << num << ": seq_num " << context->seq_num << " ranges " << ranges << std::endl;
+ context->seq_num++;
+
+ waiting_on = ranges.size();
+ ContentsGenerator::iterator gen_pos = cont_gen->get_iterator(cont);
+ // assure that tid is greater than last_acked_tid
+ uint64_t tid = last_acked_tid + 1;
+ for (auto [offset, len] : ranges) {
+ gen_pos.seek(offset);
+ bufferlist to_write = gen_pos.gen_bl_advance(len);
+ ceph_assert(to_write.length() == len);
+ ceph_assert(to_write.length() > 0);
+ std::cout << num << ": writing " << context->prefix+oid
+ << " from " << offset
+ << " to " << len + offset << " tid " << tid << std::endl;
+ auto cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(tid++));
+ librados::AioCompletion *completion =
+ context->rados.aio_create_completion((void*) cb_arg, &write_callback);
+ waiting.insert(completion);
+ librados::ObjectWriteOperation op;
+ if (do_append) {
+ op.append(to_write);
+ } else {
+ op.write(offset, to_write);
+ }
+ if (do_excl && cb_arg->second->id == last_acked_tid + 1)
+ op.assert_exists();
+ context->io_ctx.aio_operate(
+ context->prefix+oid, completion,
+ &op);
+ }
+
+ bufferlist contbl;
+ encode(cont, contbl);
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(
+ this,
+ new TestOp::CallbackInfo(tid++));
+ librados::AioCompletion *completion = context->rados.aio_create_completion(
+ (void*) cb_arg, &write_callback);
+ waiting.insert(completion);
+ waiting_on++;
+ write_op.setxattr("_header", contbl);
+ if (!do_append) {
+ write_op.truncate(cont_gen->get_length(cont));
+ }
+ context->io_ctx.aio_operate(
+ context->prefix+oid, completion, &write_op);
+
+ cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(
+ this,
+ new TestOp::CallbackInfo(tid++));
+ rcompletion = context->rados.aio_create_completion(
+ (void*) cb_arg, &write_callback);
+ waiting_on++;
+ read_op.read(0, 1, &rbuffer, 0);
+ context->io_ctx.aio_operate(
+ context->prefix+oid, rcompletion,
+ &read_op,
+ librados::OPERATION_ORDER_READS_WRITES, // order wrt previous write/update
+ 0);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ ceph_assert(info);
+ std::lock_guard state_locker{context->state_lock};
+ uint64_t tid = info->id;
+
+ std::cout << num << ": finishing write tid " << tid << " to " << context->prefix + oid << std::endl;
+
+ if (tid <= last_acked_tid) {
+ std::cerr << "Error: finished tid " << tid
+ << " when last_acked_tid was " << last_acked_tid << std::endl;
+ ceph_abort();
+ }
+ last_acked_tid = tid;
+
+ ceph_assert(!done);
+ waiting_on--;
+ if (waiting_on == 0) {
+ uint64_t version = 0;
+ for (auto i = waiting.begin(); i != waiting.end();) {
+ ceph_assert((*i)->is_complete());
+ if (int err = (*i)->get_return_value()) {
+ std::cerr << "Error: oid " << oid << " write returned error code "
+ << err << std::endl;
+ ceph_abort();
+ }
+ if ((*i)->get_version64() > version) {
+ std::cout << num << ": oid " << oid << " updating version " << version
+ << " to " << (*i)->get_version64() << std::endl;
+ version = (*i)->get_version64();
+ } else {
+ std::cout << num << ": oid " << oid << " version " << version
+ << " is already newer than " << (*i)->get_version64() << std::endl;
+ }
+ (*i)->release();
+ waiting.erase(i++);
+ }
+
+ context->update_object_version(oid, version);
+ ceph_assert(rcompletion->is_complete());
+ int r = rcompletion->get_return_value();
+ assertf(r >= 0, "r = %d", r);
+ if (rcompletion->get_version64() != version) {
+ std::cerr << "Error: racing read on " << oid << " returned version "
+ << rcompletion->get_version64() << " rather than version "
+ << version << std::endl;
+ ceph_abort_msg("racing read got wrong version");
+ }
+ rcompletion->release();
+
+ {
+ ObjectDesc old_value;
+ ceph_assert(context->find_object(oid, &old_value, -1));
+ if (old_value.deleted())
+ std::cout << num << ": left oid " << oid << " deleted" << std::endl;
+ else
+ std::cout << num << ": left oid " << oid << " "
+ << old_value.most_recent() << std::endl;
+ }
+
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ done = true;
+ }
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "WriteOp";
+ }
+};
+
+class WriteSameOp : public TestOp {
+public:
+ std::string oid;
+ ContDesc cont;
+ std::set<librados::AioCompletion *> waiting;
+ librados::AioCompletion *rcompletion;
+ uint64_t waiting_on;
+ uint64_t last_acked_tid;
+
+ librados::ObjectReadOperation read_op;
+ librados::ObjectWriteOperation write_op;
+ bufferlist rbuffer;
+
+ WriteSameOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(oid), rcompletion(NULL), waiting_on(0),
+ last_acked_tid(0)
+ {}
+
+ void _begin() override
+ {
+ std::lock_guard state_locker{context->state_lock};
+ done = 0;
+ std::stringstream acc;
+ acc << context->prefix << "OID: " << oid << " snap " << context->current_snap << std::endl;
+ std::string prefix = acc.str();
+
+ cont = ContDesc(context->seq_num, context->current_snap, context->seq_num, prefix);
+
+ ContentsGenerator *cont_gen;
+ cont_gen = new VarLenGenerator(
+ context->max_size, context->min_stride_size, context->max_stride_size);
+ context->update_object(cont_gen, oid, cont);
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ std::map<uint64_t, uint64_t> ranges;
+
+ cont_gen->get_ranges_map(cont, ranges);
+ std::cout << num << ": seq_num " << context->seq_num << " ranges " << ranges << std::endl;
+ context->seq_num++;
+
+ waiting_on = ranges.size();
+ ContentsGenerator::iterator gen_pos = cont_gen->get_iterator(cont);
+ // assure that tid is greater than last_acked_tid
+ uint64_t tid = last_acked_tid + 1;
+ for (auto [offset, len] : ranges) {
+ gen_pos.seek(offset);
+ bufferlist to_write = gen_pos.gen_bl_advance(len);
+ ceph_assert(to_write.length() == len);
+ ceph_assert(to_write.length() > 0);
+ std::cout << num << ": writing " << context->prefix+oid
+ << " from " << offset
+ << " to " << offset + len << " tid " << tid << std::endl;
+ auto cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(tid++));
+ librados::AioCompletion *completion =
+ context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ waiting.insert(completion);
+ librados::ObjectWriteOperation op;
+ /* no writesame multiplication factor for now */
+ op.writesame(offset, to_write.length(), to_write);
+
+ context->io_ctx.aio_operate(
+ context->prefix+oid, completion,
+ &op);
+ }
+
+ bufferlist contbl;
+ encode(cont, contbl);
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(
+ this,
+ new TestOp::CallbackInfo(tid++));
+ librados::AioCompletion *completion = context->rados.aio_create_completion(
+ (void*) cb_arg, &write_callback);
+ waiting.insert(completion);
+ waiting_on++;
+ write_op.setxattr("_header", contbl);
+ write_op.truncate(cont_gen->get_length(cont));
+ context->io_ctx.aio_operate(
+ context->prefix+oid, completion, &write_op);
+
+ cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(
+ this,
+ new TestOp::CallbackInfo(tid++));
+ rcompletion = context->rados.aio_create_completion(
+ (void*) cb_arg, &write_callback);
+ waiting_on++;
+ read_op.read(0, 1, &rbuffer, 0);
+ context->io_ctx.aio_operate(
+ context->prefix+oid, rcompletion,
+ &read_op,
+ librados::OPERATION_ORDER_READS_WRITES, // order wrt previous write/update
+ 0);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ ceph_assert(info);
+ std::lock_guard state_locker{context->state_lock};
+ uint64_t tid = info->id;
+
+ std::cout << num << ": finishing writesame tid " << tid << " to " << context->prefix + oid << std::endl;
+
+ if (tid <= last_acked_tid) {
+ std::cerr << "Error: finished tid " << tid
+ << " when last_acked_tid was " << last_acked_tid << std::endl;
+ ceph_abort();
+ }
+ last_acked_tid = tid;
+
+ ceph_assert(!done);
+ waiting_on--;
+ if (waiting_on == 0) {
+ uint64_t version = 0;
+ for (auto i = waiting.begin(); i != waiting.end();) {
+ ceph_assert((*i)->is_complete());
+ if (int err = (*i)->get_return_value()) {
+ std::cerr << "Error: oid " << oid << " writesame returned error code "
+ << err << std::endl;
+ ceph_abort();
+ }
+ if ((*i)->get_version64() > version) {
+ std::cout << "oid " << oid << "updating version " << version
+ << "to " << (*i)->get_version64() << std::endl;
+ version = (*i)->get_version64();
+ } else {
+ std::cout << "oid " << oid << "version " << version
+ << "is already newer than " << (*i)->get_version64() << std::endl;
+ }
+ (*i)->release();
+ waiting.erase(i++);
+ }
+
+ context->update_object_version(oid, version);
+ ceph_assert(rcompletion->is_complete());
+ int r = rcompletion->get_return_value();
+ assertf(r >= 0, "r = %d", r);
+ if (rcompletion->get_version64() != version) {
+ std::cerr << "Error: racing read on " << oid << " returned version "
+ << rcompletion->get_version64() << " rather than version "
+ << version << std::endl;
+ ceph_abort_msg("racing read got wrong version");
+ }
+ rcompletion->release();
+
+ {
+ ObjectDesc old_value;
+ ceph_assert(context->find_object(oid, &old_value, -1));
+ if (old_value.deleted())
+ std::cout << num << ": left oid " << oid << " deleted" << std::endl;
+ else
+ std::cout << num << ": left oid " << oid << " "
+ << old_value.most_recent() << std::endl;
+ }
+
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ done = true;
+ }
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "WriteSameOp";
+ }
+};
+
+class DeleteOp : public TestOp {
+public:
+ std::string oid;
+
+ DeleteOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat), oid(oid)
+ {}
+
+ void _begin() override
+ {
+ std::unique_lock state_locker{context->state_lock};
+ if (context->get_watch_context(oid)) {
+ context->kick();
+ return;
+ }
+
+ ObjectDesc contents;
+ context->find_object(oid, &contents);
+ bool present = !contents.deleted();
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->seq_num++;
+
+ context->remove_object(oid);
+
+ interval_set<uint64_t> ranges;
+ state_locker.unlock();
+
+ int r = 0;
+ if (rand() % 2) {
+ librados::ObjectWriteOperation op;
+ op.assert_exists();
+ op.remove();
+ r = context->io_ctx.operate(context->prefix+oid, &op);
+ } else {
+ r = context->io_ctx.remove(context->prefix+oid);
+ }
+ if (r && !(r == -ENOENT && !present)) {
+ std::cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
+ ceph_abort();
+ }
+
+ state_locker.lock();
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ }
+
+ std::string getType() override
+ {
+ return "DeleteOp";
+ }
+};
+
+class ReadOp : public TestOp {
+public:
+ std::vector<librados::AioCompletion *> completions;
+ librados::ObjectReadOperation op;
+ std::string oid;
+ ObjectDesc old_value;
+ int snap;
+ bool balance_reads;
+ bool localize_reads;
+
+ std::shared_ptr<int> in_use;
+
+ std::vector<bufferlist> results;
+ std::vector<int> retvals;
+ std::vector<std::map<uint64_t, uint64_t>> extent_results;
+ std::vector<bool> is_sparse_read;
+ uint64_t waiting_on;
+
+ std::vector<bufferlist> checksums;
+ std::vector<int> checksum_retvals;
+
+ std::map<std::string, bufferlist> attrs;
+ int attrretval;
+
+ std::set<std::string> omap_requested_keys;
+ std::map<std::string, bufferlist> omap_returned_values;
+ std::set<std::string> omap_keys;
+ std::map<std::string, bufferlist> omap;
+ bufferlist header;
+
+ std::map<std::string, bufferlist> xattrs;
+ ReadOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ bool balance_reads,
+ bool localize_reads,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ completions(3),
+ oid(oid),
+ snap(0),
+ balance_reads(balance_reads),
+ localize_reads(localize_reads),
+ results(3),
+ retvals(3),
+ extent_results(3),
+ is_sparse_read(3, false),
+ waiting_on(0),
+ checksums(3),
+ checksum_retvals(3),
+ attrretval(0)
+ {}
+
+ void _do_read(librados::ObjectReadOperation& read_op, int index) {
+ uint64_t len = 0;
+ if (old_value.has_contents())
+ len = old_value.most_recent_gen()->get_length(old_value.most_recent());
+ if (context->no_sparse || rand() % 2) {
+ is_sparse_read[index] = false;
+ read_op.read(0,
+ len,
+ &results[index],
+ &retvals[index]);
+ bufferlist init_value_bl;
+ encode(static_cast<uint32_t>(-1), init_value_bl);
+ read_op.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl, 0, len,
+ 0, &checksums[index], &checksum_retvals[index]);
+ } else {
+ is_sparse_read[index] = true;
+ read_op.sparse_read(0,
+ len,
+ &extent_results[index],
+ &results[index],
+ &retvals[index]);
+ }
+ }
+
+ void _begin() override
+ {
+ std::unique_lock state_locker{context->state_lock};
+ if (!(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+ std::cout << num << ": read oid " << oid << " snap " << snap << std::endl;
+ done = 0;
+ for (uint32_t i = 0; i < 3; i++) {
+ completions[i] = context->rados.aio_create_completion((void *) this, &read_callback);
+ }
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ ceph_assert(context->find_object(oid, &old_value, snap));
+ if (old_value.deleted())
+ std::cout << num << ": expect deleted" << std::endl;
+ else
+ std::cout << num << ": expect " << old_value.most_recent() << std::endl;
+
+ TestWatchContext *ctx = context->get_watch_context(oid);
+ state_locker.unlock();
+ if (ctx) {
+ ceph_assert(old_value.exists);
+ TestAlarm alarm;
+ std::cerr << num << ": about to start" << std::endl;
+ ctx->start();
+ std::cerr << num << ": started" << std::endl;
+ bufferlist bl;
+ context->io_ctx.set_notify_timeout(600);
+ int r = context->io_ctx.notify2(context->prefix+oid, bl, 0, NULL);
+ if (r < 0) {
+ std::cerr << "r is " << r << std::endl;
+ ceph_abort();
+ }
+ std::cerr << num << ": notified, waiting" << std::endl;
+ ctx->wait();
+ }
+ state_locker.lock();
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(context->snaps[snap]);
+ }
+ _do_read(op, 0);
+ for (auto i = old_value.attrs.begin(); i != old_value.attrs.end(); ++i) {
+ if (rand() % 2) {
+ std::string key = i->first;
+ if (rand() % 2)
+ key.push_back((rand() % 26) + 'a');
+ omap_requested_keys.insert(key);
+ }
+ }
+ if (!context->no_omap) {
+ op.omap_get_vals_by_keys(omap_requested_keys, &omap_returned_values, 0);
+ // NOTE: we're ignore pmore here, which assumes the OSD limit is high
+ // enough for us.
+ op.omap_get_keys2("", -1, &omap_keys, nullptr, nullptr);
+ op.omap_get_vals2("", -1, &omap, nullptr, nullptr);
+ op.omap_get_header(&header, 0);
+ }
+ op.getxattrs(&xattrs, 0);
+
+ unsigned flags = 0;
+ if (balance_reads)
+ flags |= librados::OPERATION_BALANCE_READS;
+ if (localize_reads)
+ flags |= librados::OPERATION_LOCALIZE_READS;
+
+ ceph_assert(!context->io_ctx.aio_operate(context->prefix+oid, completions[0], &op,
+ flags, NULL));
+ waiting_on++;
+
+ // send 2 pipelined reads on the same object/snap. This can help testing
+ // OSD's read behavior in some scenarios
+ for (uint32_t i = 1; i < 3; ++i) {
+ librados::ObjectReadOperation pipeline_op;
+ _do_read(pipeline_op, i);
+ ceph_assert(!context->io_ctx.aio_operate(context->prefix+oid, completions[i], &pipeline_op, 0));
+ waiting_on++;
+ }
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(0);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::unique_lock state_locker{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(waiting_on > 0);
+ if (--waiting_on) {
+ return;
+ }
+
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ int retval = completions[0]->get_return_value();
+ for (auto it = completions.begin();
+ it != completions.end(); ++it) {
+ ceph_assert((*it)->is_complete());
+ uint64_t version = (*it)->get_version64();
+ int err = (*it)->get_return_value();
+ if (err != retval) {
+ std::cerr << num << ": Error: oid " << oid << " read returned different error codes: "
+ << retval << " and " << err << std::endl;
+ ceph_abort();
+ }
+ if (err) {
+ if (!(err == -ENOENT && old_value.deleted())) {
+ std::cerr << num << ": Error: oid " << oid << " read returned error code "
+ << err << std::endl;
+ ceph_abort();
+ }
+ } else if (version != old_value.version) {
+ std::cerr << num << ": oid " << oid << " version is " << version
+ << " and expected " << old_value.version << std::endl;
+ ceph_assert(version == old_value.version);
+ }
+ }
+ if (!retval) {
+ std::map<std::string, bufferlist>::iterator iter = xattrs.find("_header");
+ bufferlist headerbl;
+ if (iter == xattrs.end()) {
+ if (old_value.has_contents()) {
+ std::cerr << num << ": Error: did not find header attr, has_contents: "
+ << old_value.has_contents()
+ << std::endl;
+ ceph_assert(!old_value.has_contents());
+ }
+ } else {
+ headerbl = iter->second;
+ xattrs.erase(iter);
+ }
+ if (old_value.deleted()) {
+ std::cout << num << ": expect deleted" << std::endl;
+ ceph_abort_msg("expected deleted");
+ } else {
+ std::cout << num << ": expect " << old_value.most_recent() << std::endl;
+ }
+ if (old_value.has_contents()) {
+ ContDesc to_check;
+ auto p = headerbl.cbegin();
+ decode(to_check, p);
+ if (to_check != old_value.most_recent()) {
+ std::cerr << num << ": oid " << oid << " found incorrect object contents " << to_check
+ << ", expected " << old_value.most_recent() << std::endl;
+ context->errors++;
+ }
+ for (unsigned i = 0; i < results.size(); i++) {
+ if (is_sparse_read[i]) {
+ if (!old_value.check_sparse(extent_results[i], results[i])) {
+ std::cerr << num << ": oid " << oid << " contents " << to_check << " corrupt" << std::endl;
+ context->errors++;
+ }
+ } else {
+ if (!old_value.check(results[i])) {
+ std::cerr << num << ": oid " << oid << " contents " << to_check << " corrupt" << std::endl;
+ context->errors++;
+ }
+
+ uint32_t checksum = 0;
+ if (checksum_retvals[i] == 0) {
+ try {
+ auto bl_it = checksums[i].cbegin();
+ uint32_t csum_count;
+ decode(csum_count, bl_it);
+ decode(checksum, bl_it);
+ } catch (const buffer::error &err) {
+ checksum_retvals[i] = -EBADMSG;
+ }
+ }
+ if (checksum_retvals[i] != 0 || checksum != results[i].crc32c(-1)) {
+ std::cerr << num << ": oid " << oid << " checksum " << checksums[i]
+ << " incorrect, expecting " << results[i].crc32c(-1)
+ << std::endl;
+ context->errors++;
+ }
+ }
+ }
+ if (context->errors) ceph_abort();
+ }
+
+ // Attributes
+ if (!context->no_omap) {
+ if (!(old_value.header == header)) {
+ std::cerr << num << ": oid " << oid << " header does not match, old size: "
+ << old_value.header.length() << " new size " << header.length()
+ << std::endl;
+ ceph_assert(old_value.header == header);
+ }
+ if (omap.size() != old_value.attrs.size()) {
+ std::cerr << num << ": oid " << oid << " omap.size() is " << omap.size()
+ << " and old is " << old_value.attrs.size() << std::endl;
+ ceph_assert(omap.size() == old_value.attrs.size());
+ }
+ if (omap_keys.size() != old_value.attrs.size()) {
+ std::cerr << num << ": oid " << oid << " omap.size() is " << omap_keys.size()
+ << " and old is " << old_value.attrs.size() << std::endl;
+ ceph_assert(omap_keys.size() == old_value.attrs.size());
+ }
+ }
+ if (xattrs.size() != old_value.attrs.size()) {
+ std::cerr << num << ": oid " << oid << " xattrs.size() is " << xattrs.size()
+ << " and old is " << old_value.attrs.size() << std::endl;
+ ceph_assert(xattrs.size() == old_value.attrs.size());
+ }
+ for (auto iter = old_value.attrs.begin();
+ iter != old_value.attrs.end();
+ ++iter) {
+ bufferlist bl = context->attr_gen.gen_bl(
+ iter->second);
+ if (!context->no_omap) {
+ std::map<std::string, bufferlist>::iterator omap_iter = omap.find(iter->first);
+ ceph_assert(omap_iter != omap.end());
+ ceph_assert(bl.length() == omap_iter->second.length());
+ bufferlist::iterator k = bl.begin();
+ for(bufferlist::iterator l = omap_iter->second.begin();
+ !k.end() && !l.end();
+ ++k, ++l) {
+ ceph_assert(*l == *k);
+ }
+ }
+ auto xattr_iter = xattrs.find(iter->first);
+ ceph_assert(xattr_iter != xattrs.end());
+ ceph_assert(bl.length() == xattr_iter->second.length());
+ bufferlist::iterator k = bl.begin();
+ for (bufferlist::iterator j = xattr_iter->second.begin();
+ !k.end() && !j.end();
+ ++j, ++k) {
+ ceph_assert(*j == *k);
+ }
+ }
+ if (!context->no_omap) {
+ for (std::set<std::string>::iterator i = omap_requested_keys.begin();
+ i != omap_requested_keys.end();
+ ++i) {
+ if (!omap_returned_values.count(*i))
+ ceph_assert(!old_value.attrs.count(*i));
+ if (!old_value.attrs.count(*i))
+ ceph_assert(!omap_returned_values.count(*i));
+ }
+ for (auto i = omap_returned_values.begin();
+ i != omap_returned_values.end();
+ ++i) {
+ ceph_assert(omap_requested_keys.count(i->first));
+ ceph_assert(omap.count(i->first));
+ ceph_assert(old_value.attrs.count(i->first));
+ ceph_assert(i->second == omap[i->first]);
+ }
+ }
+ }
+ for (auto it = completions.begin(); it != completions.end(); ++it) {
+ (*it)->release();
+ }
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "ReadOp";
+ }
+};
+
+class SnapCreateOp : public TestOp {
+public:
+ SnapCreateOp(int n,
+ RadosTestContext *context,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat)
+ {}
+
+ void _begin() override
+ {
+ uint64_t snap;
+ std::string snapname;
+
+ if (context->pool_snaps) {
+ std::stringstream ss;
+
+ ss << context->prefix << "snap" << ++context->snapname_num;
+ snapname = ss.str();
+
+ int ret = context->io_ctx.snap_create(snapname.c_str());
+ if (ret) {
+ std::cerr << "snap_create returned " << ret << std::endl;
+ ceph_abort();
+ }
+ ceph_assert(!context->io_ctx.snap_lookup(snapname.c_str(), &snap));
+
+ } else {
+ ceph_assert(!context->io_ctx.selfmanaged_snap_create(&snap));
+ }
+
+ std::unique_lock state_locker{context->state_lock};
+ context->add_snap(snap);
+
+ if (!context->pool_snaps) {
+ std::vector<uint64_t> snapset(context->snaps.size());
+
+ int j = 0;
+ for (auto i = context->snaps.rbegin();
+ i != context->snaps.rend();
+ ++i, ++j) {
+ snapset[j] = i->second;
+ }
+
+ state_locker.unlock();
+
+ int r = context->io_ctx.selfmanaged_snap_set_write_ctx(context->seq, snapset);
+ if (r) {
+ std::cerr << "r is " << r << " snapset is " << snapset << " seq is " << context->seq << std::endl;
+ ceph_abort();
+ }
+ }
+ }
+
+ std::string getType() override
+ {
+ return "SnapCreateOp";
+ }
+ bool must_quiesce_other_ops() override { return context->pool_snaps; }
+};
+
+class SnapRemoveOp : public TestOp {
+public:
+ int to_remove;
+ SnapRemoveOp(int n, RadosTestContext *context,
+ int snap,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ to_remove(snap)
+ {}
+
+ void _begin() override
+ {
+ std::unique_lock state_locker{context->state_lock};
+ uint64_t snap = context->snaps[to_remove];
+ context->remove_snap(to_remove);
+
+ if (context->pool_snaps) {
+ std::string snapname;
+
+ ceph_assert(!context->io_ctx.snap_get_name(snap, &snapname));
+ ceph_assert(!context->io_ctx.snap_remove(snapname.c_str()));
+ } else {
+ ceph_assert(!context->io_ctx.selfmanaged_snap_remove(snap));
+
+ std::vector<uint64_t> snapset(context->snaps.size());
+ int j = 0;
+ for (auto i = context->snaps.rbegin();
+ i != context->snaps.rend();
+ ++i, ++j) {
+ snapset[j] = i->second;
+ }
+
+ int r = context->io_ctx.selfmanaged_snap_set_write_ctx(context->seq, snapset);
+ if (r) {
+ std::cerr << "r is " << r << " snapset is " << snapset << " seq is " << context->seq << std::endl;
+ ceph_abort();
+ }
+ }
+ }
+
+ std::string getType() override
+ {
+ return "SnapRemoveOp";
+ }
+};
+
+class WatchOp : public TestOp {
+ std::string oid;
+public:
+ WatchOp(int n,
+ RadosTestContext *context,
+ const std::string &_oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(_oid)
+ {}
+
+ void _begin() override
+ {
+ std::unique_lock state_locker{context->state_lock};
+ ObjectDesc contents;
+ context->find_object(oid, &contents);
+ if (contents.deleted()) {
+ context->kick();
+ return;
+ }
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ TestWatchContext *ctx = context->get_watch_context(oid);
+ state_locker.unlock();
+ int r;
+ if (!ctx) {
+ {
+ std::lock_guard l{context->state_lock};
+ ctx = context->watch(oid);
+ }
+
+ r = context->io_ctx.watch2(context->prefix+oid,
+ &ctx->get_handle(),
+ ctx);
+ } else {
+ r = context->io_ctx.unwatch2(ctx->get_handle());
+ {
+ std::lock_guard l{context->state_lock};
+ context->unwatch(oid);
+ }
+ }
+
+ if (r) {
+ std::cerr << "r is " << r << std::endl;
+ ceph_abort();
+ }
+
+ {
+ std::lock_guard l{context->state_lock};
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ }
+ }
+
+ std::string getType() override
+ {
+ return "WatchOp";
+ }
+};
+
+class RollbackOp : public TestOp {
+public:
+ std::string oid;
+ int roll_back_to;
+ librados::ObjectWriteOperation zero_write_op1;
+ librados::ObjectWriteOperation zero_write_op2;
+ librados::ObjectWriteOperation op;
+ std::vector<librados::AioCompletion *> comps;
+ std::shared_ptr<int> in_use;
+ int last_finished;
+ int outstanding;
+
+ RollbackOp(int n,
+ RadosTestContext *context,
+ const std::string &_oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(_oid), roll_back_to(-1),
+ comps(3, NULL),
+ last_finished(-1), outstanding(3)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+ if (context->get_watch_context(oid)) {
+ context->kick();
+ context->state_lock.unlock();
+ return;
+ }
+
+ if (context->snaps.empty()) {
+ context->kick();
+ context->state_lock.unlock();
+ done = true;
+ return;
+ }
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ roll_back_to = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(
+ roll_back_to,
+ roll_back_to);
+
+
+ std::cout << "rollback oid " << oid << " to " << roll_back_to << std::endl;
+
+ bool existed_before = context->object_existed_at(oid);
+ bool existed_after = context->object_existed_at(oid, roll_back_to);
+
+ context->roll_back(oid, roll_back_to);
+ uint64_t snap = context->snaps[roll_back_to];
+
+ outstanding -= (!existed_before) + (!existed_after);
+
+ context->state_lock.unlock();
+
+ bufferlist bl, bl2;
+ zero_write_op1.append(bl);
+ zero_write_op2.append(bl2);
+
+ if (context->pool_snaps) {
+ op.snap_rollback(snap);
+ } else {
+ op.selfmanaged_snap_rollback(snap);
+ }
+
+ if (existed_before) {
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comps[0] =
+ context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ context->io_ctx.aio_operate(
+ context->prefix+oid, comps[0], &zero_write_op1);
+ }
+ {
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(1));
+ comps[1] =
+ context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ context->io_ctx.aio_operate(
+ context->prefix+oid, comps[1], &op);
+ }
+ if (existed_after) {
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(2));
+ comps[2] =
+ context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ context->io_ctx.aio_operate(
+ context->prefix+oid, comps[2], &zero_write_op2);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+ uint64_t tid = info->id;
+ std::cout << num << ": finishing rollback tid " << tid
+ << " to " << context->prefix + oid << std::endl;
+ ceph_assert((int)(info->id) > last_finished);
+ last_finished = info->id;
+
+ int r;
+ if ((r = comps[last_finished]->get_return_value()) != 0) {
+ std::cerr << "err " << r << std::endl;
+ ceph_abort();
+ }
+ if (--outstanding == 0) {
+ done = true;
+ context->update_object_version(oid, comps[tid]->get_version64());
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ in_use = std::shared_ptr<int>();
+ context->kick();
+ }
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "RollBackOp";
+ }
+};
+
+class CopyFromOp : public TestOp {
+public:
+ std::string oid, oid_src;
+ ObjectDesc src_value;
+ librados::ObjectWriteOperation op;
+ librados::ObjectReadOperation rd_op;
+ librados::AioCompletion *comp;
+ librados::AioCompletion *comp_racing_read = nullptr;
+ std::shared_ptr<int> in_use;
+ int snap;
+ int done;
+ uint64_t version;
+ int r;
+ CopyFromOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ const std::string &oid_src,
+ TestOpStat *stat)
+ : TestOp(n, context, stat),
+ oid(oid), oid_src(oid_src),
+ comp(NULL), snap(-1), done(0),
+ version(0), r(0)
+ {}
+
+ void _begin() override
+ {
+ ContDesc cont;
+ {
+ std::lock_guard l{context->state_lock};
+ cont = ContDesc(context->seq_num, context->current_snap,
+ context->seq_num, "");
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->oid_in_use.insert(oid_src);
+ context->oid_not_in_use.erase(oid_src);
+
+ // choose source snap
+ if (0 && !(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+ context->find_object(oid_src, &src_value, snap);
+ if (!src_value.deleted())
+ context->update_object_full(oid, src_value);
+ }
+
+ std::string src = context->prefix+oid_src;
+ op.copy_from(src.c_str(), context->io_ctx, src_value.version, 0);
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp = context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
+
+ // queue up a racing read, too.
+ std::pair<TestOp*, TestOp::CallbackInfo*> *read_cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(1));
+ comp_racing_read = context->rados.aio_create_completion((void*) read_cb_arg, &write_callback);
+ rd_op.stat(NULL, NULL, NULL);
+ context->io_ctx.aio_operate(context->prefix+oid, comp_racing_read, &rd_op,
+ librados::OPERATION_ORDER_READS_WRITES, // order wrt previous write/update
+ NULL);
+
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+
+ // note that the read can (and atm will) come back before the
+ // write reply, but will reflect the update and the versions will
+ // match.
+
+ if (info->id == 0) {
+ // copy_from
+ ceph_assert(comp->is_complete());
+ std::cout << num << ": finishing copy_from to " << context->prefix + oid << std::endl;
+ if ((r = comp->get_return_value())) {
+ if (r == -ENOENT && src_value.deleted()) {
+ std::cout << num << ": got expected ENOENT (src dne)" << std::endl;
+ } else {
+ std::cerr << "Error: oid " << oid << " copy_from " << oid_src << " returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ } else {
+ ceph_assert(!version || comp->get_version64() == version);
+ version = comp->get_version64();
+ context->update_object_version(oid, comp->get_version64());
+ }
+ } else if (info->id == 1) {
+ // racing read
+ ceph_assert(comp_racing_read->is_complete());
+ std::cout << num << ": finishing copy_from racing read to " << context->prefix + oid << std::endl;
+ if ((r = comp_racing_read->get_return_value())) {
+ if (!(r == -ENOENT && src_value.deleted())) {
+ std::cerr << "Error: oid " << oid << " copy_from " << oid_src << " returned error code "
+ << r << std::endl;
+ }
+ } else {
+ ceph_assert(comp_racing_read->get_return_value() == 0);
+ ceph_assert(!version || comp_racing_read->get_version64() == version);
+ version = comp_racing_read->get_version64();
+ }
+ }
+ if (++done == 2) {
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->oid_in_use.erase(oid_src);
+ context->oid_not_in_use.insert(oid_src);
+ context->kick();
+ }
+ }
+
+ bool finished() override
+ {
+ return done == 2;
+ }
+
+ std::string getType() override
+ {
+ return "CopyFromOp";
+ }
+};
+
+class ChunkReadOp : public TestOp {
+public:
+ std::vector<librados::AioCompletion *> completions;
+ librados::ObjectReadOperation op;
+ std::string oid;
+ ObjectDesc old_value;
+ ObjectDesc tgt_value;
+ int snap;
+ bool balance_reads;
+ bool localize_reads;
+
+ std::shared_ptr<int> in_use;
+
+ std::vector<bufferlist> results;
+ std::vector<int> retvals;
+ std::vector<bool> is_sparse_read;
+ uint64_t waiting_on;
+
+ std::vector<bufferlist> checksums;
+ std::vector<int> checksum_retvals;
+ uint32_t offset = 0;
+ uint32_t length = 0;
+ std::string tgt_oid;
+ std::string tgt_pool_name;
+ uint32_t tgt_offset = 0;
+
+ ChunkReadOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ const std::string &tgt_pool_name,
+ bool balance_reads,
+ bool localize_reads,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ completions(2),
+ oid(oid),
+ snap(0),
+ balance_reads(balance_reads),
+ localize_reads(localize_reads),
+ results(2),
+ retvals(2),
+ waiting_on(0),
+ checksums(2),
+ checksum_retvals(2),
+ tgt_pool_name(tgt_pool_name)
+ {}
+
+ void _do_read(librados::ObjectReadOperation& read_op, uint32_t offset, uint32_t length, int index) {
+ read_op.read(offset,
+ length,
+ &results[index],
+ &retvals[index]);
+ if (index != 0) {
+ bufferlist init_value_bl;
+ encode(static_cast<uint32_t>(-1), init_value_bl);
+ read_op.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl, offset, length,
+ 0, &checksums[index], &checksum_retvals[index]);
+ }
+
+ }
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+ std::cout << num << ": chunk read oid " << oid << " snap " << snap << std::endl;
+ done = 0;
+ for (uint32_t i = 0; i < 2; i++) {
+ completions[i] = context->rados.aio_create_completion((void *) this, &read_callback);
+ }
+
+ context->find_object(oid, &old_value);
+
+ if (old_value.chunk_info.size() == 0) {
+ std::cout << ": no chunks" << std::endl;
+ context->kick();
+ context->state_lock.unlock();
+ done = true;
+ return;
+ }
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ if (old_value.deleted()) {
+ std::cout << num << ": expect deleted" << std::endl;
+ } else {
+ std::cout << num << ": expect " << old_value.most_recent() << std::endl;
+ }
+
+ int rand_index = rand() % old_value.chunk_info.size();
+ auto iter = old_value.chunk_info.begin();
+ for (int i = 0; i < rand_index; i++) {
+ iter++;
+ }
+ offset = iter->first;
+ offset += (rand() % iter->second.length)/2;
+ uint32_t t_length = rand() % iter->second.length;
+ while (t_length + offset > iter->first + iter->second.length) {
+ t_length = rand() % iter->second.length;
+ }
+ length = t_length;
+ tgt_offset = iter->second.offset + offset - iter->first;
+ tgt_oid = iter->second.oid;
+
+ std::cout << num << ": ori offset " << iter->first << " req offset " << offset
+ << " ori length " << iter->second.length << " req length " << length
+ << " ori tgt_offset " << iter->second.offset << " req tgt_offset " << tgt_offset
+ << " tgt_oid " << tgt_oid << std::endl;
+
+ TestWatchContext *ctx = context->get_watch_context(oid);
+ context->state_lock.unlock();
+ if (ctx) {
+ ceph_assert(old_value.exists);
+ TestAlarm alarm;
+ std::cerr << num << ": about to start" << std::endl;
+ ctx->start();
+ std::cerr << num << ": started" << std::endl;
+ bufferlist bl;
+ context->io_ctx.set_notify_timeout(600);
+ int r = context->io_ctx.notify2(context->prefix+oid, bl, 0, NULL);
+ if (r < 0) {
+ std::cerr << "r is " << r << std::endl;
+ ceph_abort();
+ }
+ std::cerr << num << ": notified, waiting" << std::endl;
+ ctx->wait();
+ }
+ std::lock_guard state_locker{context->state_lock};
+
+ _do_read(op, offset, length, 0);
+
+ unsigned flags = 0;
+ if (balance_reads)
+ flags |= librados::OPERATION_BALANCE_READS;
+ if (localize_reads)
+ flags |= librados::OPERATION_LOCALIZE_READS;
+
+ ceph_assert(!context->io_ctx.aio_operate(context->prefix+oid, completions[0], &op,
+ flags, NULL));
+ waiting_on++;
+
+ _do_read(op, tgt_offset, length, 1);
+ ceph_assert(!context->io_ctx.aio_operate(context->prefix+tgt_oid, completions[1], &op,
+ flags, NULL));
+
+ waiting_on++;
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(waiting_on > 0);
+ if (--waiting_on) {
+ return;
+ }
+
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ int retval = completions[0]->get_return_value();
+ std::cout << ": finish!! ret: " << retval << std::endl;
+ context->find_object(tgt_oid, &tgt_value);
+
+ for (int i = 0; i < 2; i++) {
+ ceph_assert(completions[i]->is_complete());
+ int err = completions[i]->get_return_value();
+ if (err != retval) {
+ std::cerr << num << ": Error: oid " << oid << " read returned different error codes: "
+ << retval << " and " << err << std::endl;
+ ceph_abort();
+ }
+ if (err) {
+ if (!(err == -ENOENT && old_value.deleted())) {
+ std::cerr << num << ": Error: oid " << oid << " read returned error code "
+ << err << std::endl;
+ ceph_abort();
+ }
+ }
+ }
+
+ if (!retval) {
+ if (old_value.deleted()) {
+ std::cout << num << ": expect deleted" << std::endl;
+ ceph_abort_msg("expected deleted");
+ } else {
+ std::cout << num << ": expect " << old_value.most_recent() << std::endl;
+ }
+ if (tgt_value.has_contents()) {
+ uint32_t checksum[2] = {0};
+ if (checksum_retvals[1] == 0) {
+ try {
+ auto bl_it = checksums[1].cbegin();
+ uint32_t csum_count;
+ decode(csum_count, bl_it);
+ decode(checksum[1], bl_it);
+ } catch (const buffer::error &err) {
+ checksum_retvals[1] = -EBADMSG;
+ }
+ }
+
+ if (checksum_retvals[1] != 0) {
+ std::cerr << num << ": oid " << oid << " checksum retvals " << checksums[0]
+ << " error " << std::endl;
+ context->errors++;
+ }
+
+ checksum[0] = results[0].crc32c(-1);
+
+ if (checksum[0] != checksum[1]) {
+ std::cerr << num << ": oid " << oid << " checksum src " << checksum[0]
+ << " chunksum tgt " << checksum[1] << " incorrect, expecting "
+ << results[0].crc32c(-1)
+ << std::endl;
+ context->errors++;
+ }
+ if (context->errors) ceph_abort();
+ }
+ }
+ for (auto it = completions.begin(); it != completions.end(); ++it) {
+ (*it)->release();
+ }
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "ChunkReadOp";
+ }
+};
+
+class CopyOp : public TestOp {
+public:
+ std::string oid, oid_src, tgt_pool_name;
+ librados::ObjectWriteOperation op;
+ librados::ObjectReadOperation rd_op;
+ librados::AioCompletion *comp;
+ ObjectDesc src_value, tgt_value;
+ int done;
+ int r;
+ CopyOp(int n,
+ RadosTestContext *context,
+ const std::string &oid_src,
+ const std::string &oid,
+ const std::string &tgt_pool_name,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(oid), oid_src(oid_src), tgt_pool_name(tgt_pool_name),
+ comp(NULL), done(0), r(0)
+ {}
+
+ void _begin() override
+ {
+ std::lock_guard l{context->state_lock};
+ context->oid_in_use.insert(oid_src);
+ context->oid_not_in_use.erase(oid_src);
+
+ std::string src = context->prefix+oid_src;
+ context->find_object(oid_src, &src_value);
+ op.copy_from(src.c_str(), context->io_ctx, src_value.version, 0);
+
+ std::cout << "copy op oid " << oid_src << " to " << oid << " tgt_pool_name " << tgt_pool_name << std::endl;
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
+ if (tgt_pool_name == context->low_tier_pool_name) {
+ context->low_tier_io_ctx.aio_operate(context->prefix+oid, comp, &op);
+ } else {
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+
+ if (info->id == 0) {
+ ceph_assert(comp->is_complete());
+ std::cout << num << ": finishing copy op to oid " << oid << std::endl;
+ if ((r = comp->get_return_value())) {
+ std::cerr << "Error: oid " << oid << " write returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ }
+
+ if (++done == 1) {
+ context->oid_in_use.erase(oid_src);
+ context->oid_not_in_use.insert(oid_src);
+ context->kick();
+ }
+ }
+
+ bool finished() override
+ {
+ return done == 1;
+ }
+
+ std::string getType() override
+ {
+ return "CopyOp";
+ }
+};
+
+class SetChunkOp : public TestOp {
+public:
+ std::string oid, oid_tgt;
+ ObjectDesc src_value, tgt_value;
+ librados::ObjectReadOperation op;
+ librados::AioCompletion *comp;
+ int done;
+ int r;
+ uint64_t offset;
+ uint32_t length;
+ uint32_t tgt_offset;
+ int snap;
+ std::shared_ptr<int> in_use;
+ SetChunkOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ const std::string &oid_tgt,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(oid), oid_tgt(oid_tgt),
+ comp(NULL), done(0),
+ r(0), offset(0), length(0),
+ tgt_offset(0),
+ snap(0)
+ {}
+
+ std::pair<uint64_t, uint64_t> get_rand_off_len(uint32_t max_len) {
+ std::pair<uint64_t, uint64_t> r (0, 0);
+ r.first = rand() % max_len;
+ r.second = rand() % max_len;
+ r.first = r.first - (r.first % 512);
+ r.second = r.second - (r.second % 512);
+
+ while (r.first + r.second > max_len || r.second == 0) {
+ r.first = rand() % max_len;
+ r.second = rand() % max_len;
+ r.first = r.first - (r.first % 512);
+ r.second = r.second - (r.second % 512);
+ }
+ return r;
+ }
+
+ void _begin() override
+ {
+ std::lock_guard l{context->state_lock};
+ if (!(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ context->find_object(oid, &src_value, snap);
+ context->find_object(oid_tgt, &tgt_value);
+
+ uint32_t max_len = 0;
+ if (src_value.deleted()) {
+ /* just random length to check ENOENT */
+ max_len = context->max_size;
+ } else {
+ max_len = src_value.most_recent_gen()->get_length(src_value.most_recent());
+ }
+ std::pair<uint64_t, uint64_t> off_len; // first: offset, second: length
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(context->snaps[snap]);
+ off_len = get_rand_off_len(max_len);
+ } else if (src_value.version != 0 && !src_value.deleted()) {
+ op.assert_version(src_value.version);
+ off_len = get_rand_off_len(max_len);
+ } else if (src_value.deleted()) {
+ off_len.first = 0;
+ off_len.second = max_len;
+ }
+ offset = off_len.first;
+ length = off_len.second;
+ tgt_offset = offset;
+
+ std::string target_oid;
+ if (!src_value.deleted() && oid_tgt.empty()) {
+ bufferlist bl;
+ int r = context->io_ctx.read(context->prefix+oid, bl, length, offset);
+ ceph_assert(r > 0);
+ std::string fp_oid = ceph::crypto::digest<ceph::crypto::SHA256>(bl).to_str();
+ r = context->low_tier_io_ctx.write(fp_oid, bl, bl.length(), 0);
+ ceph_assert(r == 0);
+ target_oid = fp_oid;
+ tgt_offset = 0;
+ } else {
+ target_oid = context->prefix+oid_tgt;
+ }
+
+ std::cout << num << ": " << "set_chunk oid " << oid << " offset: " << offset
+ << " length: " << length << " target oid " << target_oid
+ << " offset: " << tgt_offset << " snap " << snap << std::endl;
+
+ op.set_chunk(offset, length, context->low_tier_io_ctx,
+ target_oid, tgt_offset, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp = context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES, NULL);
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(0);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+
+ if (info->id == 0) {
+ ceph_assert(comp->is_complete());
+ std::cout << num << ": finishing set_chunk to oid " << oid << std::endl;
+ if ((r = comp->get_return_value())) {
+ if (r == -ENOENT && src_value.deleted()) {
+ std::cout << num << ": got expected ENOENT (src dne)" << std::endl;
+ } else if (r == -ENOENT && context->oid_set_chunk_tgt_pool.find(oid_tgt) !=
+ context->oid_set_chunk_tgt_pool.end()) {
+ std::cout << num << ": get expected ENOENT tgt oid " << oid_tgt << std::endl;
+ } else if (r == -ERANGE && src_value.deleted()) {
+ std::cout << num << ": got expected ERANGE (src dne)" << std::endl;
+ } else if (r == -EOPNOTSUPP) {
+ std::cout << "Range is overlapped: oid " << oid << " set_chunk " << oid_tgt << " returned error code "
+ << r << " offset: " << offset << " length: " << length << std::endl;
+ context->update_object_version(oid, comp->get_version64());
+ } else {
+ std::cerr << "Error: oid " << oid << " set_chunk " << oid_tgt << " returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ } else {
+ if (snap == -1) {
+ ChunkDesc info {tgt_offset, length, oid_tgt};
+ context->update_object_chunk_target(oid, offset, info);
+ context->update_object_version(oid, comp->get_version64());
+ }
+ }
+ }
+
+ if (++done == 1) {
+ context->oid_set_chunk_tgt_pool.insert(oid_tgt);
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ }
+ }
+
+ bool finished() override
+ {
+ return done == 1;
+ }
+
+ std::string getType() override
+ {
+ return "SetChunkOp";
+ }
+};
+
+class SetRedirectOp : public TestOp {
+public:
+ std::string oid, oid_tgt, tgt_pool_name;
+ ObjectDesc src_value, tgt_value;
+ librados::ObjectWriteOperation op;
+ librados::ObjectReadOperation rd_op;
+ librados::AioCompletion *comp;
+ std::shared_ptr<int> in_use;
+ int done;
+ int r;
+ SetRedirectOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ const std::string &oid_tgt,
+ const std::string &tgt_pool_name,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(oid), oid_tgt(oid_tgt), tgt_pool_name(tgt_pool_name),
+ comp(NULL), done(0),
+ r(0)
+ {}
+
+ void _begin() override
+ {
+ std::lock_guard l{context->state_lock};
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->oid_redirect_in_use.insert(oid_tgt);
+ context->oid_redirect_not_in_use.erase(oid_tgt);
+
+ if (tgt_pool_name.empty()) ceph_abort();
+
+ context->find_object(oid, &src_value);
+ if(!context->redirect_objs[oid].empty()) {
+ /* copy_from oid --> oid_tgt */
+ comp = context->rados.aio_create_completion();
+ std::string src = context->prefix+oid;
+ op.copy_from(src.c_str(), context->io_ctx, src_value.version, 0);
+ context->low_tier_io_ctx.aio_operate(context->prefix+oid_tgt, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES);
+ comp->wait_for_complete();
+ if ((r = comp->get_return_value())) {
+ std::cerr << "Error: oid " << oid << " copy_from " << oid_tgt << " returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ comp->release();
+
+ /* unset redirect target */
+ comp = context->rados.aio_create_completion();
+ bool present = !src_value.deleted();
+ op.unset_manifest();
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES |
+ librados::OPERATION_IGNORE_REDIRECT);
+ comp->wait_for_complete();
+ if ((r = comp->get_return_value())) {
+ if (!(r == -ENOENT && !present) && r != -EOPNOTSUPP) {
+ std::cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
+ ceph_abort();
+ }
+ }
+ comp->release();
+
+ context->oid_redirect_not_in_use.insert(context->redirect_objs[oid]);
+ context->oid_redirect_in_use.erase(context->redirect_objs[oid]);
+ }
+
+ comp = context->rados.aio_create_completion();
+ rd_op.stat(NULL, NULL, NULL);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &rd_op,
+ librados::OPERATION_ORDER_READS_WRITES |
+ librados::OPERATION_IGNORE_REDIRECT,
+ NULL);
+ comp->wait_for_complete();
+ if ((r = comp->get_return_value()) && !src_value.deleted()) {
+ std::cerr << "Error: oid " << oid << " stat returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ context->update_object_version(oid, comp->get_version64());
+ comp->release();
+
+ comp = context->rados.aio_create_completion();
+ rd_op.stat(NULL, NULL, NULL);
+ context->low_tier_io_ctx.aio_operate(context->prefix+oid_tgt, comp, &rd_op,
+ librados::OPERATION_ORDER_READS_WRITES |
+ librados::OPERATION_IGNORE_REDIRECT,
+ NULL);
+ comp->wait_for_complete();
+ if ((r = comp->get_return_value())) {
+ std::cerr << "Error: oid " << oid_tgt << " stat returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ uint64_t tgt_version = comp->get_version64();
+ comp->release();
+
+
+ context->find_object(oid, &src_value);
+
+ if (src_value.version != 0 && !src_value.deleted())
+ op.assert_version(src_value.version);
+ op.set_redirect(context->prefix+oid_tgt, context->low_tier_io_ctx, tgt_version);
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+
+ if (info->id == 0) {
+ ceph_assert(comp->is_complete());
+ std::cout << num << ": finishing set_redirect to oid " << oid << std::endl;
+ if ((r = comp->get_return_value())) {
+ if (r == -ENOENT && src_value.deleted()) {
+ std::cout << num << ": got expected ENOENT (src dne)" << std::endl;
+ } else {
+ std::cerr << "Error: oid " << oid << " set_redirect " << oid_tgt << " returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ } else {
+ context->update_object_redirect_target(oid, oid_tgt);
+ context->update_object_version(oid, comp->get_version64());
+ }
+ }
+
+ if (++done == 1) {
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ }
+ }
+
+ bool finished() override
+ {
+ return done == 1;
+ }
+
+ std::string getType() override
+ {
+ return "SetRedirectOp";
+ }
+};
+
+class UnsetRedirectOp : public TestOp {
+public:
+ std::string oid;
+ librados::ObjectWriteOperation op;
+ librados::AioCompletion *comp = nullptr;
+
+ UnsetRedirectOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat), oid(oid)
+ {}
+
+ void _begin() override
+ {
+ std::unique_lock state_locker{context->state_lock};
+ if (context->get_watch_context(oid)) {
+ context->kick();
+ return;
+ }
+
+ ObjectDesc contents;
+ context->find_object(oid, &contents);
+ bool present = !contents.deleted();
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->seq_num++;
+
+ context->remove_object(oid);
+
+ state_locker.unlock();
+
+ comp = context->rados.aio_create_completion();
+ op.remove();
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES |
+ librados::OPERATION_IGNORE_REDIRECT);
+ comp->wait_for_complete();
+ int r = comp->get_return_value();
+ if (r && !(r == -ENOENT && !present)) {
+ std::cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
+ ceph_abort();
+ }
+ state_locker.lock();
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ if(!context->redirect_objs[oid].empty()) {
+ context->oid_redirect_not_in_use.insert(context->redirect_objs[oid]);
+ context->oid_redirect_in_use.erase(context->redirect_objs[oid]);
+ context->update_object_redirect_target(oid, {});
+ }
+ context->kick();
+ }
+
+ std::string getType() override
+ {
+ return "UnsetRedirectOp";
+ }
+};
+
+class TierPromoteOp : public TestOp {
+public:
+ librados::AioCompletion *completion;
+ librados::ObjectWriteOperation op;
+ std::string oid;
+ std::shared_ptr<int> in_use;
+ ObjectDesc src_value;
+
+ TierPromoteOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat)
+ : TestOp(n, context, stat),
+ completion(NULL),
+ oid(oid)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ context->find_object(oid, &src_value);
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ completion = context->rados.aio_create_completion((void *) cb_arg,
+ &write_callback);
+ context->state_lock.unlock();
+
+ op.tier_promote();
+ int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
+ &op);
+ ceph_assert(!r);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard l{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(completion->is_complete());
+
+ ObjectDesc oid_value;
+ context->find_object(oid, &oid_value);
+ int r = completion->get_return_value();
+ std::cout << num << ": got " << cpp_strerror(r) << std::endl;
+ if (r == 0) {
+ // sucess
+ } else if (r == -ENOENT && src_value.deleted()) {
+ std::cout << num << ": got expected ENOENT (src dne)" << std::endl;
+ } else {
+ ceph_abort_msg("shouldn't happen");
+ }
+ context->update_object_version(oid, completion->get_version64());
+ context->find_object(oid, &oid_value);
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "TierPromoteOp";
+ }
+};
+
+class TierFlushOp : public TestOp {
+public:
+ librados::AioCompletion *completion;
+ librados::ObjectReadOperation op;
+ std::string oid;
+ std::shared_ptr<int> in_use;
+ int snap;
+ ObjectDesc src_value;
+
+
+ TierFlushOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat)
+ : TestOp(n, context, stat),
+ completion(NULL),
+ oid(oid),
+ snap(-1)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ if (0 && !(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+
+ std::cout << num << ": tier_flush oid " << oid << " snap " << snap << std::endl;
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(context->snaps[snap]);
+ }
+
+ context->find_object(oid, &src_value, snap);
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ completion = context->rados.aio_create_completion((void *) cb_arg,
+ &write_callback);
+ context->state_lock.unlock();
+
+ op.tier_flush();
+ unsigned flags = librados::OPERATION_IGNORE_CACHE;
+ int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
+ &op, flags, NULL);
+ ceph_assert(!r);
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(0);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ context->state_lock.lock();
+ ceph_assert(!done);
+ ceph_assert(completion->is_complete());
+
+ int r = completion->get_return_value();
+ std::cout << num << ": got " << cpp_strerror(r) << std::endl;
+ if (r == 0) {
+ // sucess
+ context->update_object_tier_flushed(oid, snap);
+ context->update_object_version(oid, completion->get_version64(), snap);
+ } else if (r == -EBUSY) {
+ // could fail if snap is not oldest
+ ceph_assert(!context->check_oldest_snap_flushed(oid, snap));
+ } else if (r == -ENOENT) {
+ // could fail if object is removed
+ if (src_value.deleted()) {
+ std::cout << num << ": got expected ENOENT (src dne)" << std::endl;
+ } else {
+ std::cerr << num << ": got unexpected ENOENT" << std::endl;
+ ceph_abort();
+ }
+ } else {
+ if (r != -ENOENT && src_value.deleted()) {
+ std::cerr << num << ": src dne, but r is not ENOENT" << std::endl;
+ }
+ ceph_abort_msg("shouldn't happen");
+ }
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ done = true;
+ context->state_lock.unlock();
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "TierFlushOp";
+ }
+};
+
+class TierEvictOp : public TestOp {
+public:
+ librados::AioCompletion *completion;
+ librados::ObjectReadOperation op;
+ std::string oid;
+ std::shared_ptr<int> in_use;
+ int snap;
+ ObjectDesc src_value;
+
+ TierEvictOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat)
+ : TestOp(n, context, stat),
+ completion(NULL),
+ oid(oid),
+ snap(-1)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+
+ if (0 && !(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+
+ std::cout << num << ": tier_evict oid " << oid << " snap " << snap << std::endl;
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(context->snaps[snap]);
+ }
+
+ context->find_object(oid, &src_value, snap);
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ completion = context->rados.aio_create_completion((void *) cb_arg,
+ &write_callback);
+ context->state_lock.unlock();
+
+ op.cache_evict();
+ int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
+ &op, librados::OPERATION_IGNORE_CACHE,
+ NULL);
+ ceph_assert(!r);
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(0);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard state_locker{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(completion->is_complete());
+
+ int r = completion->get_return_value();
+ std::cout << num << ": got " << cpp_strerror(r) << std::endl;
+ if (r == 0) {
+ // ok
+ } else if (r == -EINVAL) {
+ // modifying manifest object makes existing chunk_map clear
+ // as a result, the modified object is no longer manifest object
+ // this casues to return -EINVAL
+ } else if (r == -ENOENT) {
+ // could fail if object is removed
+ if (src_value.deleted()) {
+ std::cout << num << ": got expected ENOENT (src dne)" << std::endl;
+ } else {
+ std::cerr << num << ": got unexpected ENOENT" << std::endl;
+ ceph_abort();
+ }
+ } else {
+ if (r != -ENOENT && src_value.deleted()) {
+ std::cerr << num << ": src dne, but r is not ENOENT" << std::endl;
+ }
+ ceph_abort_msg("shouldn't happen");
+ }
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "TierEvictOp";
+ }
+};
+
+class HitSetListOp : public TestOp {
+ librados::AioCompletion *comp1, *comp2;
+ uint32_t hash;
+ std::list< std::pair<time_t, time_t> > ls;
+ bufferlist bl;
+
+public:
+ HitSetListOp(int n,
+ RadosTestContext *context,
+ uint32_t hash,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ comp1(NULL), comp2(NULL),
+ hash(hash)
+ {}
+
+ void _begin() override
+ {
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp1 = context->rados.aio_create_completion((void*) cb_arg,
+ &write_callback);
+ int r = context->io_ctx.hit_set_list(hash, comp1, &ls);
+ ceph_assert(r == 0);
+ }
+
+ void _finish(CallbackInfo *info) override {
+ std::lock_guard l{context->state_lock};
+ if (!comp2) {
+ if (ls.empty()) {
+ std::cerr << num << ": no hitsets" << std::endl;
+ done = true;
+ } else {
+ std::cerr << num << ": hitsets are " << ls << std::endl;
+ int r = rand() % ls.size();
+ auto p = ls.begin();
+ while (r--)
+ ++p;
+ auto cb_arg = new std::pair<TestOp*, TestOp::CallbackInfo*>(
+ this, new TestOp::CallbackInfo(0));
+ comp2 = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
+ r = context->io_ctx.hit_set_get(hash, comp2, p->second, &bl);
+ ceph_assert(r == 0);
+ }
+ } else {
+ int r = comp2->get_return_value();
+ if (r == 0) {
+ HitSet hitset;
+ auto p = bl.cbegin();
+ decode(hitset, p);
+ std::cout << num << ": got hitset of type " << hitset.get_type_name()
+ << " size " << bl.length()
+ << std::endl;
+ } else {
+ // FIXME: we could verify that we did in fact race with a trim...
+ ceph_assert(r == -ENOENT);
+ }
+ done = true;
+ }
+
+ context->kick();
+ }
+
+ bool finished() override {
+ return done;
+ }
+
+ std::string getType() override {
+ return "HitSetListOp";
+ }
+};
+
+class UndirtyOp : public TestOp {
+public:
+ librados::AioCompletion *completion;
+ librados::ObjectWriteOperation op;
+ std::string oid;
+
+ UndirtyOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ completion(NULL),
+ oid(oid)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ completion = context->rados.aio_create_completion((void *) cb_arg,
+ &write_callback);
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->update_object_undirty(oid);
+ context->state_lock.unlock();
+
+ op.undirty();
+ int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
+ &op, 0);
+ ceph_assert(!r);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard state_locker{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(completion->is_complete());
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->update_object_version(oid, completion->get_version64());
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "UndirtyOp";
+ }
+};
+
+class IsDirtyOp : public TestOp {
+public:
+ librados::AioCompletion *completion;
+ librados::ObjectReadOperation op;
+ std::string oid;
+ bool dirty;
+ ObjectDesc old_value;
+ int snap = 0;
+ std::shared_ptr<int> in_use;
+
+ IsDirtyOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ completion(NULL),
+ oid(oid),
+ dirty(false)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+
+ if (!(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+ std::cout << num << ": is_dirty oid " << oid << " snap " << snap
+ << std::endl;
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ completion = context->rados.aio_create_completion((void *) cb_arg,
+ &write_callback);
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->state_lock.unlock();
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(context->snaps[snap]);
+ }
+
+ op.is_dirty(&dirty, NULL);
+ int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
+ &op, 0);
+ ceph_assert(!r);
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(0);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard state_locker{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(completion->is_complete());
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+
+ ceph_assert(context->find_object(oid, &old_value, snap));
+
+ int r = completion->get_return_value();
+ if (r == 0) {
+ std::cout << num << ": " << (dirty ? "dirty" : "clean") << std::endl;
+ ceph_assert(!old_value.deleted());
+ ceph_assert(dirty == old_value.dirty);
+ } else {
+ std::cout << num << ": got " << r << std::endl;
+ ceph_assert(r == -ENOENT);
+ ceph_assert(old_value.deleted());
+ }
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "IsDirtyOp";
+ }
+};
+
+
+
+class CacheFlushOp : public TestOp {
+public:
+ librados::AioCompletion *completion;
+ librados::ObjectReadOperation op;
+ std::string oid;
+ bool blocking;
+ int snap;
+ bool can_fail;
+ std::shared_ptr<int> in_use;
+
+ CacheFlushOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat,
+ bool b)
+ : TestOp(n, context, stat),
+ completion(NULL),
+ oid(oid),
+ blocking(b),
+ snap(0),
+ can_fail(false)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+
+ if (!(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+ // not being particularly specific here about knowing which
+ // flushes are on the oldest clean snap and which ones are not.
+ can_fail = !blocking || !context->snaps.empty();
+ // FIXME: we could fail if we've ever removed a snap due to
+ // the async snap trimming.
+ can_fail = true;
+ std::cout << num << ": " << (blocking ? "cache_flush" : "cache_try_flush")
+ << " oid " << oid << " snap " << snap << std::endl;
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(context->snaps[snap]);
+ }
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ completion = context->rados.aio_create_completion((void *) cb_arg,
+ &write_callback);
+ context->oid_flushing.insert(oid);
+ context->oid_not_flushing.erase(oid);
+ context->state_lock.unlock();
+
+ unsigned flags = librados::OPERATION_IGNORE_CACHE;
+ if (blocking) {
+ op.cache_flush();
+ } else {
+ op.cache_try_flush();
+ flags = librados::OPERATION_SKIPRWLOCKS;
+ }
+ int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
+ &op, flags, NULL);
+ ceph_assert(!r);
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(0);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard state_locker{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(completion->is_complete());
+ context->oid_flushing.erase(oid);
+ context->oid_not_flushing.insert(oid);
+ int r = completion->get_return_value();
+ std::cout << num << ": got " << cpp_strerror(r) << std::endl;
+ if (r == 0) {
+ context->update_object_version(oid, 0, snap);
+ } else if (r == -EBUSY) {
+ ceph_assert(can_fail);
+ } else if (r == -EINVAL) {
+ // caching not enabled?
+ } else if (r == -ENOENT) {
+ // may have raced with a remove?
+ } else {
+ ceph_abort_msg("shouldn't happen");
+ }
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "CacheFlushOp";
+ }
+};
+
+class CacheEvictOp : public TestOp {
+public:
+ librados::AioCompletion *completion;
+ librados::ObjectReadOperation op;
+ std::string oid;
+ std::shared_ptr<int> in_use;
+
+ CacheEvictOp(int n,
+ RadosTestContext *context,
+ const std::string &oid,
+ TestOpStat *stat)
+ : TestOp(n, context, stat),
+ completion(NULL),
+ oid(oid)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.lock();
+
+ int snap;
+ if (!(rand() % 4) && !context->snaps.empty()) {
+ snap = rand_choose(context->snaps)->first;
+ in_use = context->snaps_in_use.lookup_or_create(snap, snap);
+ } else {
+ snap = -1;
+ }
+ std::cout << num << ": cache_evict oid " << oid << " snap " << snap << std::endl;
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(context->snaps[snap]);
+ }
+
+ std::pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new std::pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ completion = context->rados.aio_create_completion((void *) cb_arg,
+ &write_callback);
+ context->state_lock.unlock();
+
+ op.cache_evict();
+ int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
+ &op, librados::OPERATION_IGNORE_CACHE,
+ NULL);
+ ceph_assert(!r);
+
+ if (snap >= 0) {
+ context->io_ctx.snap_set_read(0);
+ }
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ std::lock_guard state_locker{context->state_lock};
+ ceph_assert(!done);
+ ceph_assert(completion->is_complete());
+
+ int r = completion->get_return_value();
+ std::cout << num << ": got " << cpp_strerror(r) << std::endl;
+ if (r == 0) {
+ // yay!
+ } else if (r == -EBUSY) {
+ // raced with something that dirtied the object
+ } else if (r == -EINVAL) {
+ // caching not enabled?
+ } else if (r == -ENOENT) {
+ // may have raced with a remove?
+ } else {
+ ceph_abort_msg("shouldn't happen");
+ }
+ context->kick();
+ done = true;
+ }
+
+ bool finished() override
+ {
+ return done;
+ }
+
+ std::string getType() override
+ {
+ return "CacheEvictOp";
+ }
+};
+
+
+#endif
diff --git a/src/test/osd/TestECBackend.cc b/src/test/osd/TestECBackend.cc
new file mode 100644
index 000000000..1c13fb4c9
--- /dev/null
+++ b/src/test/osd/TestECBackend.cc
@@ -0,0 +1,62 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank Storage, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <signal.h>
+#include "osd/ECBackend.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+TEST(ECUtil, stripe_info_t)
+{
+ const uint64_t swidth = 4096;
+ const uint64_t ssize = 4;
+
+ ECUtil::stripe_info_t s(ssize, swidth);
+ ASSERT_EQ(s.get_stripe_width(), swidth);
+
+ ASSERT_EQ(s.logical_to_next_chunk_offset(0), 0u);
+ ASSERT_EQ(s.logical_to_next_chunk_offset(1), s.get_chunk_size());
+ ASSERT_EQ(s.logical_to_next_chunk_offset(swidth - 1),
+ s.get_chunk_size());
+
+ ASSERT_EQ(s.logical_to_prev_chunk_offset(0), 0u);
+ ASSERT_EQ(s.logical_to_prev_chunk_offset(swidth), s.get_chunk_size());
+ ASSERT_EQ(s.logical_to_prev_chunk_offset((swidth * 2) - 1),
+ s.get_chunk_size());
+
+ ASSERT_EQ(s.logical_to_next_stripe_offset(0), 0u);
+ ASSERT_EQ(s.logical_to_next_stripe_offset(swidth - 1),
+ s.get_stripe_width());
+
+ ASSERT_EQ(s.logical_to_prev_stripe_offset(swidth), s.get_stripe_width());
+ ASSERT_EQ(s.logical_to_prev_stripe_offset(swidth), s.get_stripe_width());
+ ASSERT_EQ(s.logical_to_prev_stripe_offset((swidth * 2) - 1),
+ s.get_stripe_width());
+
+ ASSERT_EQ(s.aligned_logical_offset_to_chunk_offset(2*swidth),
+ 2*s.get_chunk_size());
+ ASSERT_EQ(s.aligned_chunk_offset_to_logical_offset(2*s.get_chunk_size()),
+ 2*s.get_stripe_width());
+
+ ASSERT_EQ(s.aligned_offset_len_to_chunk(make_pair(swidth, 10*swidth)),
+ make_pair(s.get_chunk_size(), 10*s.get_chunk_size()));
+
+ ASSERT_EQ(s.offset_len_to_stripe_bounds(make_pair(swidth-10, (uint64_t)20)),
+ make_pair((uint64_t)0, 2*swidth));
+}
+
diff --git a/src/test/osd/TestMClockScheduler.cc b/src/test/osd/TestMClockScheduler.cc
new file mode 100644
index 000000000..8291da268
--- /dev/null
+++ b/src/test/osd/TestMClockScheduler.cc
@@ -0,0 +1,256 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+
+#include <chrono>
+
+#include "gtest/gtest.h"
+
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/common_init.h"
+
+#include "osd/scheduler/mClockScheduler.h"
+#include "osd/scheduler/OpSchedulerItem.h"
+
+using namespace ceph::osd::scheduler;
+
+int main(int argc, char **argv) {
+ std::vector<const char*> args(argv, argv+argc);
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_OSD,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+
+class mClockSchedulerTest : public testing::Test {
+public:
+ int whoami;
+ uint32_t num_shards;
+ int shard_id;
+ bool is_rotational;
+ MonClient *monc;
+ mClockScheduler q;
+
+ uint64_t client1;
+ uint64_t client2;
+ uint64_t client3;
+
+ mClockSchedulerTest() :
+ whoami(0),
+ num_shards(1),
+ shard_id(0),
+ is_rotational(false),
+ monc(nullptr),
+ q(g_ceph_context, whoami, num_shards, shard_id, is_rotational, monc),
+ client1(1001),
+ client2(9999),
+ client3(100000001)
+ {}
+
+ struct MockDmclockItem : public PGOpQueueable {
+ op_scheduler_class scheduler_class;
+
+ MockDmclockItem(op_scheduler_class _scheduler_class) :
+ PGOpQueueable(spg_t()),
+ scheduler_class(_scheduler_class) {}
+
+ MockDmclockItem()
+ : MockDmclockItem(op_scheduler_class::background_best_effort) {}
+
+ ostream &print(ostream &rhs) const final { return rhs; }
+
+ std::optional<OpRequestRef> maybe_get_op() const final {
+ return std::nullopt;
+ }
+
+ op_scheduler_class get_scheduler_class() const final {
+ return scheduler_class;
+ }
+
+ void run(OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final {}
+ };
+};
+
+template <typename... Args>
+OpSchedulerItem create_item(
+ epoch_t e, uint64_t owner, Args&&... args)
+{
+ return OpSchedulerItem(
+ std::make_unique<mClockSchedulerTest::MockDmclockItem>(
+ std::forward<Args>(args)...),
+ 12, 12,
+ utime_t(), owner, e);
+}
+
+template <typename... Args>
+OpSchedulerItem create_high_prio_item(
+ unsigned priority, epoch_t e, uint64_t owner, Args&&... args)
+{
+ // Create high priority item for testing high prio queue
+ return OpSchedulerItem(
+ std::make_unique<mClockSchedulerTest::MockDmclockItem>(
+ std::forward<Args>(args)...),
+ 12, priority,
+ utime_t(), owner, e);
+}
+
+OpSchedulerItem get_item(WorkItem item)
+{
+ return std::move(std::get<OpSchedulerItem>(item));
+}
+
+TEST_F(mClockSchedulerTest, TestEmpty) {
+ ASSERT_TRUE(q.empty());
+
+ for (unsigned i = 100; i < 105; i+=2) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ ASSERT_FALSE(q.empty());
+
+ std::list<OpSchedulerItem> reqs;
+
+ reqs.push_back(get_item(q.dequeue()));
+ reqs.push_back(get_item(q.dequeue()));
+
+ ASSERT_EQ(2u, reqs.size());
+ ASSERT_FALSE(q.empty());
+
+ for (auto &&i : reqs) {
+ q.enqueue_front(std::move(i));
+ }
+ reqs.clear();
+
+ ASSERT_FALSE(q.empty());
+
+ for (int i = 0; i < 3; ++i) {
+ ASSERT_FALSE(q.empty());
+ q.dequeue();
+ }
+
+ ASSERT_TRUE(q.empty());
+}
+
+TEST_F(mClockSchedulerTest, TestSingleClientOrderedEnqueueDequeue) {
+ ASSERT_TRUE(q.empty());
+
+ for (unsigned i = 100; i < 105; ++i) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ auto r = get_item(q.dequeue());
+ ASSERT_EQ(100u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(101u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(102u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(103u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(104u, r.get_map_epoch());
+}
+
+TEST_F(mClockSchedulerTest, TestMultiClientOrderedEnqueueDequeue) {
+ const unsigned NUM = 1000;
+ for (unsigned i = 0; i < NUM; ++i) {
+ for (auto &&c: {client1, client2, client3}) {
+ q.enqueue(create_item(i, c));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+ }
+
+ std::map<uint64_t, epoch_t> next;
+ for (auto &&c: {client1, client2, client3}) {
+ next[c] = 0;
+ }
+ for (unsigned i = 0; i < NUM * 3; ++i) {
+ ASSERT_FALSE(q.empty());
+ auto r = get_item(q.dequeue());
+ auto owner = r.get_owner();
+ auto niter = next.find(owner);
+ ASSERT_FALSE(niter == next.end());
+ ASSERT_EQ(niter->second, r.get_map_epoch());
+ niter->second++;
+ }
+ ASSERT_TRUE(q.empty());
+}
+
+TEST_F(mClockSchedulerTest, TestHighPriorityQueueEnqueueDequeue) {
+ ASSERT_TRUE(q.empty());
+ for (unsigned i = 200; i < 205; ++i) {
+ q.enqueue(create_high_prio_item(i, i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ ASSERT_FALSE(q.empty());
+ // Higher priority ops should be dequeued first
+ auto r = get_item(q.dequeue());
+ ASSERT_EQ(204u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(203u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(202u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(201u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(200u, r.get_map_epoch());
+
+ ASSERT_TRUE(q.empty());
+}
+
+TEST_F(mClockSchedulerTest, TestAllQueuesEnqueueDequeue) {
+ ASSERT_TRUE(q.empty());
+
+ // Insert ops into the mClock queue
+ for (unsigned i = 100; i < 102; ++i) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ // Insert Immediate ops
+ for (unsigned i = 103; i < 105; ++i) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::immediate));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ // Insert ops into the high queue
+ for (unsigned i = 200; i < 202; ++i) {
+ q.enqueue(create_high_prio_item(i, i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ ASSERT_FALSE(q.empty());
+ auto r = get_item(q.dequeue());
+ // Ops classified as Immediate should be dequeued first
+ ASSERT_EQ(103u, r.get_map_epoch());
+ r = get_item(q.dequeue());
+ ASSERT_EQ(104u, r.get_map_epoch());
+
+ // High priority queue should be dequeued second
+ // higher priority operation first
+ r = get_item(q.dequeue());
+ ASSERT_EQ(201u, r.get_map_epoch());
+ r = get_item(q.dequeue());
+ ASSERT_EQ(200u, r.get_map_epoch());
+
+ // mClock queue will be dequeued last
+ r = get_item(q.dequeue());
+ ASSERT_EQ(100u, r.get_map_epoch());
+ r = get_item(q.dequeue());
+ ASSERT_EQ(101u, r.get_map_epoch());
+
+ ASSERT_TRUE(q.empty());
+}
diff --git a/src/test/osd/TestOSDMap.cc b/src/test/osd/TestOSDMap.cc
new file mode 100644
index 000000000..c3c8a1531
--- /dev/null
+++ b/src/test/osd/TestOSDMap.cc
@@ -0,0 +1,2719 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "gtest/gtest.h"
+#include "osd/OSDMap.h"
+#include "osd/OSDMapMapping.h"
+#include "mon/OSDMonitor.h"
+#include "mon/PGMap.h"
+
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/common_init.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_json.h"
+
+#include <iostream>
+#include <cmath>
+
+using namespace std;
+
+int main(int argc, char **argv) {
+ map<string,string> defaults = {
+ // make sure we have 3 copies, or some tests won't work
+ { "osd_pool_default_size", "3" },
+ // our map is flat, so just try and split across OSDs, not hosts or whatever
+ { "osd_crush_chooseleaf_type", "0" },
+ };
+ std::vector<const char*> args(argv, argv+argc);
+ auto cct = global_init(&defaults, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+class OSDMapTest : public testing::Test,
+ public ::testing::WithParamInterface<std::pair<int, int>> {
+ int num_osds = 6;
+public:
+ OSDMap osdmap;
+ OSDMapMapping mapping;
+ const uint64_t my_ec_pool = 1;
+ const uint64_t my_rep_pool = 2;
+
+ // Blacklist testing lists
+ // I pulled the first two ranges and their start/end points from
+ // https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation
+ static const string range_addrs[];
+ static const string ip_addrs[];
+ static const string unblocked_ip_addrs[];
+ const string EC_RULE_NAME = "erasure";
+
+ OSDMapTest() {}
+
+ void set_up_map(int new_num_osds = 6, bool no_default_pools = false) {
+ num_osds = new_num_osds;
+ uuid_d fsid;
+ osdmap.build_simple(g_ceph_context, 0, fsid, num_osds);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.fsid = osdmap.get_fsid();
+ entity_addrvec_t sample_addrs;
+ sample_addrs.v.push_back(entity_addr_t());
+ uuid_d sample_uuid;
+ for (int i = 0; i < num_osds; ++i) {
+ sample_uuid.generate_random();
+ sample_addrs.v[0].nonce = i;
+ pending_inc.new_state[i] = CEPH_OSD_EXISTS | CEPH_OSD_NEW;
+ pending_inc.new_up_client[i] = sample_addrs;
+ pending_inc.new_up_cluster[i] = sample_addrs;
+ pending_inc.new_hb_back_up[i] = sample_addrs;
+ pending_inc.new_hb_front_up[i] = sample_addrs;
+ pending_inc.new_weight[i] = CEPH_OSD_IN;
+ pending_inc.new_uuid[i] = sample_uuid;
+ }
+ osdmap.apply_incremental(pending_inc);
+ if (no_default_pools) // do not create any default pool(s)
+ return;
+
+ OSDMap::Incremental new_pool_inc(osdmap.get_epoch() + 1);
+ new_pool_inc.new_pool_max = osdmap.get_pool_max();
+ new_pool_inc.fsid = osdmap.get_fsid();
+ // make an ec pool
+ set_ec_pool("ec", new_pool_inc);
+ // and a replicated pool
+ set_rep_pool("reppool",new_pool_inc);
+ osdmap.apply_incremental(new_pool_inc);
+ }
+ int get_ec_crush_rule() {
+ int r = osdmap.crush->get_rule_id(EC_RULE_NAME);
+ if (r < 0) {
+ r = osdmap.crush->add_simple_rule(
+ EC_RULE_NAME, "default", "osd", "",
+ "indep", pg_pool_t::TYPE_ERASURE,
+ &cerr);
+ }
+ return r;
+ }
+ uint64_t set_ec_pool(const string &name, OSDMap::Incremental &new_pool_inc,
+ bool assert_pool_id = true) {
+ pg_pool_t empty;
+ uint64_t pool_id = ++new_pool_inc.new_pool_max;
+ if (assert_pool_id)
+ ceph_assert(pool_id == my_ec_pool);
+ pg_pool_t *p = new_pool_inc.get_new_pool(pool_id, &empty);
+ p->size = 3;
+ p->set_pg_num(64);
+ p->set_pgp_num(64);
+ p->type = pg_pool_t::TYPE_ERASURE;
+ p->crush_rule = get_ec_crush_rule();
+ new_pool_inc.new_pool_names[pool_id] = name;//"ec";
+ return pool_id;
+ }
+ uint64_t set_rep_pool(const string name, OSDMap::Incremental &new_pool_inc,
+ bool assert_pool_id = true) {
+ pg_pool_t empty;
+ uint64_t pool_id = ++new_pool_inc.new_pool_max;
+ if (assert_pool_id)
+ ceph_assert(pool_id == my_rep_pool);
+ pg_pool_t *p = new_pool_inc.get_new_pool(pool_id, &empty);
+ p->size = 3;
+ p->set_pg_num(64);
+ p->set_pgp_num(64);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = 0;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ new_pool_inc.new_pool_names[pool_id] = name;//"reppool";
+ return pool_id;
+ }
+
+ unsigned int get_num_osds() { return num_osds; }
+ void get_crush(const OSDMap& tmap, CrushWrapper& newcrush) {
+ bufferlist bl;
+ tmap.crush->encode(bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ auto p = bl.cbegin();
+ newcrush.decode(p);
+ }
+ int crush_move(OSDMap& tmap, const string &name, const vector<string> &argvec) {
+ map<string,string> loc;
+ CrushWrapper::parse_loc_map(argvec, &loc);
+ CrushWrapper newcrush;
+ get_crush(tmap, newcrush);
+ if (!newcrush.name_exists(name)) {
+ return -ENOENT;
+ }
+ int id = newcrush.get_item_id(name);
+ int err;
+ if (!newcrush.check_item_loc(g_ceph_context, id, loc, (int *)NULL)) {
+ if (id >= 0) {
+ err = newcrush.create_or_move_item(g_ceph_context, id, 0, name, loc);
+ } else {
+ err = newcrush.move_bucket(g_ceph_context, id, loc);
+ }
+ if (err >= 0) {
+ OSDMap::Incremental pending_inc(tmap.get_epoch() + 1);
+ pending_inc.crush.clear();
+ newcrush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ tmap.apply_incremental(pending_inc);
+ err = 0;
+ }
+ } else {
+ // already there
+ err = 0;
+ }
+ return err;
+ }
+ int crush_rule_create_replicated(const string &name,
+ const string &root,
+ const string &type) {
+ if (osdmap.crush->rule_exists(name)) {
+ return osdmap.crush->get_rule_id(name);
+ }
+ CrushWrapper newcrush;
+ get_crush(osdmap, newcrush);
+ string device_class;
+ stringstream ss;
+ int ruleno = newcrush.add_simple_rule(
+ name, root, type, device_class,
+ "firstn", pg_pool_t::TYPE_REPLICATED, &ss);
+ if (ruleno >= 0) {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.crush.clear();
+ newcrush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ osdmap.apply_incremental(pending_inc);
+ }
+ return ruleno;
+ }
+ void test_mappings(int pool,
+ int num,
+ vector<int> *any,
+ vector<int> *first,
+ vector<int> *primary) {
+ mapping.update(osdmap);
+ for (int i=0; i<num; ++i) {
+ vector<int> up, acting;
+ int up_primary, acting_primary;
+ pg_t pgid(i, pool);
+ osdmap.pg_to_up_acting_osds(pgid,
+ &up, &up_primary, &acting, &acting_primary);
+ for (unsigned j=0; j<acting.size(); ++j)
+ (*any)[acting[j]]++;
+ if (!acting.empty())
+ (*first)[acting[0]]++;
+ if (acting_primary >= 0)
+ (*primary)[acting_primary]++;
+
+ // compare to precalc mapping
+ vector<int> up2, acting2;
+ int up_primary2, acting_primary2;
+ pgid = osdmap.raw_pg_to_pg(pgid);
+ mapping.get(pgid, &up2, &up_primary2, &acting2, &acting_primary2);
+ ASSERT_EQ(up, up2);
+ ASSERT_EQ(up_primary, up_primary2);
+ ASSERT_EQ(acting, acting2);
+ ASSERT_EQ(acting_primary, acting_primary2);
+ }
+ cout << "any: " << *any << std::endl;;
+ cout << "first: " << *first << std::endl;;
+ cout << "primary: " << *primary << std::endl;;
+ }
+ void clean_pg_upmaps(CephContext *cct,
+ const OSDMap& om,
+ OSDMap::Incremental& pending_inc) {
+ int cpu_num = 8;
+ int pgs_per_chunk = 256;
+ ThreadPool tp(cct, "BUG_40104::clean_upmap_tp", "clean_upmap_tp", cpu_num);
+ tp.start();
+ ParallelPGMapper mapper(cct, &tp);
+ vector<pg_t> pgs_to_check;
+ om.get_upmap_pgs(&pgs_to_check);
+ OSDMonitor::CleanUpmapJob job(cct, om, pending_inc);
+ mapper.queue(&job, pgs_per_chunk, pgs_to_check);
+ job.wait();
+ tp.stop();
+ }
+ void set_primary_affinity_all(float pa) {
+ for (uint i = 0 ; i < get_num_osds() ; i++) {
+ osdmap.set_primary_affinity(i, int(pa * CEPH_OSD_MAX_PRIMARY_AFFINITY));
+ }
+ }
+ bool score_in_range(float score, uint nosds = 0) {
+ if (nosds == 0) {
+ nosds = get_num_osds();
+ }
+ return score >= 1.0 && score <= float(nosds);
+ }
+};
+
+TEST_F(OSDMapTest, Create) {
+ set_up_map();
+ ASSERT_EQ(get_num_osds(), (unsigned)osdmap.get_max_osd());
+ ASSERT_EQ(get_num_osds(), osdmap.get_num_in_osds());
+}
+
+TEST_F(OSDMapTest, Features) {
+ // with EC pool
+ set_up_map();
+ uint64_t features = osdmap.get_features(CEPH_ENTITY_TYPE_OSD, NULL);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES2);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES3);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_V2);
+ ASSERT_TRUE(features & CEPH_FEATURE_OSDHASHPSPOOL);
+ ASSERT_TRUE(features & CEPH_FEATURE_OSD_PRIMARY_AFFINITY);
+
+ // clients have a slightly different view
+ features = osdmap.get_features(CEPH_ENTITY_TYPE_CLIENT, NULL);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES2);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES3);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_V2);
+ ASSERT_TRUE(features & CEPH_FEATURE_OSDHASHPSPOOL);
+ ASSERT_TRUE(features & CEPH_FEATURE_OSD_PRIMARY_AFFINITY);
+
+ // remove teh EC pool, but leave the rule. add primary affinity.
+ {
+ OSDMap::Incremental new_pool_inc(osdmap.get_epoch() + 1);
+ new_pool_inc.old_pools.insert(osdmap.lookup_pg_pool_name("ec"));
+ new_pool_inc.new_primary_affinity[0] = 0x8000;
+ osdmap.apply_incremental(new_pool_inc);
+ }
+
+ features = osdmap.get_features(CEPH_ENTITY_TYPE_MON, NULL);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES2);
+ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES3); // shared bit with primary affinity
+ ASSERT_FALSE(features & CEPH_FEATURE_CRUSH_V2);
+ ASSERT_TRUE(features & CEPH_FEATURE_OSDHASHPSPOOL);
+ ASSERT_TRUE(features & CEPH_FEATURE_OSD_PRIMARY_AFFINITY);
+
+ // FIXME: test tiering feature bits
+}
+
+TEST_F(OSDMapTest, MapPG) {
+ set_up_map();
+
+ std::cerr << " osdmap.pool_max==" << osdmap.get_pool_max() << std::endl;
+ pg_t rawpg(0, my_rep_pool);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up_osds, acting_osds;
+ int up_primary, acting_primary;
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+
+ vector<int> old_up_osds, old_acting_osds;
+ osdmap.pg_to_up_acting_osds(pgid, old_up_osds, old_acting_osds);
+ ASSERT_EQ(old_up_osds, up_osds);
+ ASSERT_EQ(old_acting_osds, acting_osds);
+
+ ASSERT_EQ(osdmap.get_pg_pool(my_rep_pool)->get_size(), up_osds.size());
+}
+
+TEST_F(OSDMapTest, MapFunctionsMatch) {
+ // TODO: make sure pg_to_up_acting_osds and pg_to_acting_osds match
+ set_up_map();
+ pg_t rawpg(0, my_rep_pool);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up_osds, acting_osds;
+ int up_primary, acting_primary;
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+
+ vector<int> up_osds_two, acting_osds_two;
+
+ osdmap.pg_to_up_acting_osds(pgid, up_osds_two, acting_osds_two);
+
+ ASSERT_EQ(up_osds, up_osds_two);
+ ASSERT_EQ(acting_osds, acting_osds_two);
+
+ int acting_primary_two;
+ osdmap.pg_to_acting_osds(pgid, &acting_osds_two, &acting_primary_two);
+ EXPECT_EQ(acting_osds, acting_osds_two);
+ EXPECT_EQ(acting_primary, acting_primary_two);
+ osdmap.pg_to_acting_osds(pgid, acting_osds_two);
+ EXPECT_EQ(acting_osds, acting_osds_two);
+}
+
+/** This test must be removed or modified appropriately when we allow
+ * other ways to specify a primary. */
+TEST_F(OSDMapTest, PrimaryIsFirst) {
+ set_up_map();
+
+ pg_t rawpg(0, my_rep_pool);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up_osds, acting_osds;
+ int up_primary, acting_primary;
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+ EXPECT_EQ(up_osds[0], up_primary);
+ EXPECT_EQ(acting_osds[0], acting_primary);
+}
+
+TEST_F(OSDMapTest, PGTempRespected) {
+ set_up_map();
+
+ pg_t rawpg(0, my_rep_pool);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up_osds, acting_osds;
+ int up_primary, acting_primary;
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+
+ // copy and swap first and last element in acting_osds
+ vector<int> new_acting_osds(acting_osds);
+ int first = new_acting_osds[0];
+ new_acting_osds[0] = *new_acting_osds.rbegin();
+ *new_acting_osds.rbegin() = first;
+
+ // apply pg_temp to osdmap
+ OSDMap::Incremental pgtemp_map(osdmap.get_epoch() + 1);
+ pgtemp_map.new_pg_temp[pgid] = mempool::osdmap::vector<int>(
+ new_acting_osds.begin(), new_acting_osds.end());
+ osdmap.apply_incremental(pgtemp_map);
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+ EXPECT_EQ(new_acting_osds, acting_osds);
+}
+
+TEST_F(OSDMapTest, PrimaryTempRespected) {
+ set_up_map();
+
+ pg_t rawpg(0, my_rep_pool);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up_osds;
+ vector<int> acting_osds;
+ int up_primary, acting_primary;
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+
+ // make second OSD primary via incremental
+ OSDMap::Incremental pgtemp_map(osdmap.get_epoch() + 1);
+ pgtemp_map.new_primary_temp[pgid] = acting_osds[1];
+ osdmap.apply_incremental(pgtemp_map);
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+ EXPECT_EQ(acting_primary, acting_osds[1]);
+}
+
+TEST_F(OSDMapTest, CleanTemps) {
+ set_up_map();
+
+ OSDMap::Incremental pgtemp_map(osdmap.get_epoch() + 1);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 2);
+ pg_t pga = osdmap.raw_pg_to_pg(pg_t(0, my_rep_pool));
+ {
+ vector<int> up_osds, acting_osds;
+ int up_primary, acting_primary;
+ osdmap.pg_to_up_acting_osds(pga, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+ pgtemp_map.new_pg_temp[pga] = mempool::osdmap::vector<int>(
+ up_osds.begin(), up_osds.end());
+ pgtemp_map.new_primary_temp[pga] = up_primary;
+ }
+ pg_t pgb = osdmap.raw_pg_to_pg(pg_t(1, my_rep_pool));
+ {
+ vector<int> up_osds, acting_osds;
+ int up_primary, acting_primary;
+ osdmap.pg_to_up_acting_osds(pgb, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+ pending_inc.new_pg_temp[pgb] = mempool::osdmap::vector<int>(
+ up_osds.begin(), up_osds.end());
+ pending_inc.new_primary_temp[pgb] = up_primary;
+ }
+
+ osdmap.apply_incremental(pgtemp_map);
+
+ OSDMap tmpmap;
+ tmpmap.deepish_copy_from(osdmap);
+ tmpmap.apply_incremental(pending_inc);
+ OSDMap::clean_temps(g_ceph_context, osdmap, tmpmap, &pending_inc);
+
+ EXPECT_TRUE(pending_inc.new_pg_temp.count(pga) &&
+ pending_inc.new_pg_temp[pga].size() == 0);
+ EXPECT_EQ(-1, pending_inc.new_primary_temp[pga]);
+
+ EXPECT_TRUE(!pending_inc.new_pg_temp.count(pgb) &&
+ !pending_inc.new_primary_temp.count(pgb));
+}
+
+TEST_F(OSDMapTest, KeepsNecessaryTemps) {
+ set_up_map();
+
+ pg_t rawpg(0, my_rep_pool);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up_osds, acting_osds;
+ int up_primary, acting_primary;
+
+ osdmap.pg_to_up_acting_osds(pgid, &up_osds, &up_primary,
+ &acting_osds, &acting_primary);
+
+ // find unused OSD and stick it in there
+ OSDMap::Incremental pgtemp_map(osdmap.get_epoch() + 1);
+ // find an unused osd and put it in place of the first one
+ int i = 0;
+ for(; i != (int)get_num_osds(); ++i) {
+ bool in_use = false;
+ for (vector<int>::iterator osd_it = up_osds.begin();
+ osd_it != up_osds.end();
+ ++osd_it) {
+ if (i == *osd_it) {
+ in_use = true;
+ break;
+ }
+ }
+ if (!in_use) {
+ up_osds[1] = i;
+ break;
+ }
+ }
+ if (i == (int)get_num_osds())
+ FAIL() << "did not find unused OSD for temp mapping";
+
+ pgtemp_map.new_pg_temp[pgid] = mempool::osdmap::vector<int>(
+ up_osds.begin(), up_osds.end());
+ pgtemp_map.new_primary_temp[pgid] = up_osds[1];
+ osdmap.apply_incremental(pgtemp_map);
+
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+
+ OSDMap tmpmap;
+ tmpmap.deepish_copy_from(osdmap);
+ tmpmap.apply_incremental(pending_inc);
+ OSDMap::clean_temps(g_ceph_context, osdmap, tmpmap, &pending_inc);
+ EXPECT_FALSE(pending_inc.new_pg_temp.count(pgid));
+ EXPECT_FALSE(pending_inc.new_primary_temp.count(pgid));
+}
+
+TEST_F(OSDMapTest, PrimaryAffinity) {
+ set_up_map();
+
+ int n = get_num_osds();
+ for (map<int64_t,pg_pool_t>::const_iterator p = osdmap.get_pools().begin();
+ p != osdmap.get_pools().end();
+ ++p) {
+ int pool = p->first;
+ int expect_primary = 10000 / n;
+ cout << "pool " << pool << " size " << (int)p->second.size
+ << " expect_primary " << expect_primary << std::endl;
+ {
+ vector<int> any(n, 0);
+ vector<int> first(n, 0);
+ vector<int> primary(n, 0);
+ test_mappings(pool, 10000, &any, &first, &primary);
+ for (int i=0; i<n; ++i) {
+ ASSERT_LT(0, any[i]);
+ ASSERT_LT(0, first[i]);
+ ASSERT_LT(0, primary[i]);
+ }
+ }
+
+ osdmap.set_primary_affinity(0, 0);
+ osdmap.set_primary_affinity(1, 0);
+ {
+ vector<int> any(n, 0);
+ vector<int> first(n, 0);
+ vector<int> primary(n, 0);
+ test_mappings(pool, 10000, &any, &first, &primary);
+ for (int i=0; i<n; ++i) {
+ ASSERT_LT(0, any[i]);
+ if (i >= 2) {
+ ASSERT_LT(0, first[i]);
+ ASSERT_LT(0, primary[i]);
+ } else {
+ if (p->second.is_replicated()) {
+ ASSERT_EQ(0, first[i]);
+ }
+ ASSERT_EQ(0, primary[i]);
+ }
+ }
+ }
+
+ osdmap.set_primary_affinity(0, 0x8000);
+ osdmap.set_primary_affinity(1, 0);
+ {
+ vector<int> any(n, 0);
+ vector<int> first(n, 0);
+ vector<int> primary(n, 0);
+ test_mappings(pool, 10000, &any, &first, &primary);
+ int expect = (10000 / (n-2)) / 2; // half weight
+ cout << "expect " << expect << std::endl;
+ for (int i=0; i<n; ++i) {
+ ASSERT_LT(0, any[i]);
+ if (i >= 2) {
+ ASSERT_LT(0, first[i]);
+ ASSERT_LT(0, primary[i]);
+ } else if (i == 1) {
+ if (p->second.is_replicated()) {
+ ASSERT_EQ(0, first[i]);
+ }
+ ASSERT_EQ(0, primary[i]);
+ } else {
+ ASSERT_LT(expect *2/3, primary[0]);
+ ASSERT_GT(expect *4/3, primary[0]);
+ }
+ }
+ }
+
+ osdmap.set_primary_affinity(0, 0x10000);
+ osdmap.set_primary_affinity(1, 0x10000);
+ }
+}
+
+TEST_F(OSDMapTest, get_osd_crush_node_flags) {
+ set_up_map();
+
+ for (unsigned i=0; i<get_num_osds(); ++i) {
+ ASSERT_EQ(0u, osdmap.get_osd_crush_node_flags(i));
+ }
+
+ OSDMap::Incremental inc(osdmap.get_epoch() + 1);
+ inc.new_crush_node_flags[-1] = 123u;
+ osdmap.apply_incremental(inc);
+ for (unsigned i=0; i<get_num_osds(); ++i) {
+ ASSERT_EQ(123u, osdmap.get_osd_crush_node_flags(i));
+ }
+ ASSERT_EQ(0u, osdmap.get_osd_crush_node_flags(1000));
+
+ OSDMap::Incremental inc3(osdmap.get_epoch() + 1);
+ inc3.new_crush_node_flags[-1] = 456u;
+ osdmap.apply_incremental(inc3);
+ for (unsigned i=0; i<get_num_osds(); ++i) {
+ ASSERT_EQ(456u, osdmap.get_osd_crush_node_flags(i));
+ }
+ ASSERT_EQ(0u, osdmap.get_osd_crush_node_flags(1000));
+
+ OSDMap::Incremental inc2(osdmap.get_epoch() + 1);
+ inc2.new_crush_node_flags[-1] = 0;
+ osdmap.apply_incremental(inc2);
+ for (unsigned i=0; i<get_num_osds(); ++i) {
+ ASSERT_EQ(0u, osdmap.get_crush_node_flags(i));
+ }
+}
+
+TEST_F(OSDMapTest, parse_osd_id_list) {
+ set_up_map();
+ set<int> out;
+ set<int> all;
+ osdmap.get_all_osds(all);
+
+ ASSERT_EQ(0, osdmap.parse_osd_id_list({"osd.0"}, &out, &cout));
+ ASSERT_EQ(1u, out.size());
+ ASSERT_EQ(0, *out.begin());
+
+ ASSERT_EQ(0, osdmap.parse_osd_id_list({"1"}, &out, &cout));
+ ASSERT_EQ(1u, out.size());
+ ASSERT_EQ(1, *out.begin());
+
+ ASSERT_EQ(0, osdmap.parse_osd_id_list({"osd.0","osd.1"}, &out, &cout));
+ ASSERT_EQ(2u, out.size());
+ ASSERT_EQ(0, *out.begin());
+ ASSERT_EQ(1, *out.rbegin());
+
+ ASSERT_EQ(0, osdmap.parse_osd_id_list({"osd.0","1"}, &out, &cout));
+ ASSERT_EQ(2u, out.size());
+ ASSERT_EQ(0, *out.begin());
+ ASSERT_EQ(1, *out.rbegin());
+
+ ASSERT_EQ(0, osdmap.parse_osd_id_list({"*"}, &out, &cout));
+ ASSERT_EQ(all.size(), out.size());
+ ASSERT_EQ(all, out);
+
+ ASSERT_EQ(0, osdmap.parse_osd_id_list({"all"}, &out, &cout));
+ ASSERT_EQ(all, out);
+
+ ASSERT_EQ(0, osdmap.parse_osd_id_list({"any"}, &out, &cout));
+ ASSERT_EQ(all, out);
+
+ ASSERT_EQ(-EINVAL, osdmap.parse_osd_id_list({"foo"}, &out, &cout));
+ ASSERT_EQ(-EINVAL, osdmap.parse_osd_id_list({"-12"}, &out, &cout));
+}
+
+TEST_F(OSDMapTest, CleanPGUpmaps) {
+ set_up_map();
+
+ // build a crush rule of type host
+ const int expected_host_num = 3;
+ int osd_per_host = get_num_osds() / expected_host_num;
+ ASSERT_GE(2, osd_per_host);
+ int index = 0;
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (i && i % osd_per_host == 0) {
+ ++index;
+ }
+ stringstream osd_name;
+ stringstream host_name;
+ vector<string> move_to;
+ osd_name << "osd." << i;
+ host_name << "host-" << index;
+ move_to.push_back("root=default");
+ string host_loc = "host=" + host_name.str();
+ move_to.push_back(host_loc);
+ int r = crush_move(osdmap, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ }
+ const string upmap_rule = "upmap";
+ int upmap_rule_no = crush_rule_create_replicated(
+ upmap_rule, "default", "host");
+ ASSERT_LT(0, upmap_rule_no);
+
+ // create a replicated pool which references the above rule
+ OSDMap::Incremental new_pool_inc(osdmap.get_epoch() + 1);
+ new_pool_inc.new_pool_max = osdmap.get_pool_max();
+ new_pool_inc.fsid = osdmap.get_fsid();
+ pg_pool_t empty;
+ uint64_t upmap_pool_id = ++new_pool_inc.new_pool_max;
+ pg_pool_t *p = new_pool_inc.get_new_pool(upmap_pool_id, &empty);
+ p->size = 2;
+ p->set_pg_num(64);
+ p->set_pgp_num(64);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = upmap_rule_no;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ new_pool_inc.new_pool_names[upmap_pool_id] = "upmap_pool";
+ osdmap.apply_incremental(new_pool_inc);
+
+ pg_t rawpg(0, upmap_pool_id);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up;
+ int up_primary;
+ osdmap.pg_to_raw_up(pgid, &up, &up_primary);
+ ASSERT_LT(1U, up.size());
+ {
+ // validate we won't have two OSDs from a same host
+ int parent_0 = osdmap.crush->get_parent_of_type(up[0],
+ osdmap.crush->get_type_id("host"));
+ int parent_1 = osdmap.crush->get_parent_of_type(up[1],
+ osdmap.crush->get_type_id("host"));
+ ASSERT_TRUE(parent_0 != parent_1);
+ }
+
+ {
+ // cancel stale upmaps
+ osdmap.pg_to_raw_up(pgid, &up, &up_primary);
+ int from = -1;
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(up.begin(), up.end(), i) == up.end()) {
+ from = i;
+ break;
+ }
+ }
+ ASSERT_TRUE(from >= 0);
+ int to = -1;
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(up.begin(), up.end(), i) == up.end() && i != from) {
+ to = i;
+ break;
+ }
+ }
+ ASSERT_TRUE(to >= 0);
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(from, to));
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ OSDMap nextmap;
+ nextmap.deepish_copy_from(osdmap);
+ nextmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(nextmap.have_pg_upmaps(pgid));
+ OSDMap::Incremental new_pending_inc(nextmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, nextmap, new_pending_inc);
+ nextmap.apply_incremental(new_pending_inc);
+ ASSERT_TRUE(!nextmap.have_pg_upmaps(pgid));
+ }
+
+ {
+ // https://tracker.ceph.com/issues/37493
+ pg_t ec_pg(0, my_ec_pool);
+ pg_t ec_pgid = osdmap.raw_pg_to_pg(ec_pg);
+ OSDMap tmpmap; // use a tmpmap here, so we do not dirty origin map..
+ int from = -1;
+ int to = -1;
+ {
+ // insert a valid pg_upmap_item
+ vector<int> ec_up;
+ int ec_up_primary;
+ osdmap.pg_to_raw_up(ec_pgid, &ec_up, &ec_up_primary);
+ ASSERT_TRUE(!ec_up.empty());
+ from = *(ec_up.begin());
+ ASSERT_TRUE(from >= 0);
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(ec_up.begin(), ec_up.end(), i) == ec_up.end()) {
+ to = i;
+ break;
+ }
+ }
+ ASSERT_TRUE(to >= 0);
+ ASSERT_TRUE(from != to);
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(from, to));
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[ec_pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ tmpmap.deepish_copy_from(osdmap);
+ tmpmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmpmap.have_pg_upmaps(ec_pgid));
+ }
+ {
+ // mark one of the target OSDs of the above pg_upmap_item as down
+ OSDMap::Incremental pending_inc(tmpmap.get_epoch() + 1);
+ pending_inc.new_state[to] = CEPH_OSD_UP;
+ tmpmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(!tmpmap.is_up(to));
+ ASSERT_TRUE(tmpmap.have_pg_upmaps(ec_pgid));
+ }
+ {
+ // confirm *clean_pg_upmaps* won't do anything bad
+ OSDMap::Incremental pending_inc(tmpmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, tmpmap, pending_inc);
+ tmpmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmpmap.have_pg_upmaps(ec_pgid));
+ }
+ }
+
+ {
+ // http://tracker.ceph.com/issues/37501
+ pg_t ec_pg(0, my_ec_pool);
+ pg_t ec_pgid = osdmap.raw_pg_to_pg(ec_pg);
+ OSDMap tmpmap; // use a tmpmap here, so we do not dirty origin map..
+ int from = -1;
+ int to = -1;
+ {
+ // insert a valid pg_upmap_item
+ vector<int> ec_up;
+ int ec_up_primary;
+ osdmap.pg_to_raw_up(ec_pgid, &ec_up, &ec_up_primary);
+ ASSERT_TRUE(!ec_up.empty());
+ from = *(ec_up.begin());
+ ASSERT_TRUE(from >= 0);
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(ec_up.begin(), ec_up.end(), i) == ec_up.end()) {
+ to = i;
+ break;
+ }
+ }
+ ASSERT_TRUE(to >= 0);
+ ASSERT_TRUE(from != to);
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(from, to));
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[ec_pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ tmpmap.deepish_copy_from(osdmap);
+ tmpmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmpmap.have_pg_upmaps(ec_pgid));
+ }
+ {
+ // mark one of the target OSDs of the above pg_upmap_item as out
+ OSDMap::Incremental pending_inc(tmpmap.get_epoch() + 1);
+ pending_inc.new_weight[to] = CEPH_OSD_OUT;
+ tmpmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmpmap.is_out(to));
+ ASSERT_TRUE(tmpmap.have_pg_upmaps(ec_pgid));
+ }
+ {
+ // *clean_pg_upmaps* should be able to remove the above *bad* mapping
+ OSDMap::Incremental pending_inc(tmpmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, tmpmap, pending_inc);
+ tmpmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(!tmpmap.have_pg_upmaps(ec_pgid));
+ }
+ }
+
+ {
+ // http://tracker.ceph.com/issues/37968
+
+ // build a temporary crush topology of 2 hosts, 3 osds per host
+ OSDMap tmp; // use a tmpmap here, so we do not dirty origin map..
+ tmp.deepish_copy_from(osdmap);
+ const int expected_host_num = 2;
+ int osd_per_host = get_num_osds() / expected_host_num;
+ ASSERT_GE(osd_per_host, 3);
+ int index = 0;
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (i && i % osd_per_host == 0) {
+ ++index;
+ }
+ stringstream osd_name;
+ stringstream host_name;
+ vector<string> move_to;
+ osd_name << "osd." << i;
+ host_name << "host-" << index;
+ move_to.push_back("root=default");
+ string host_loc = "host=" + host_name.str();
+ move_to.push_back(host_loc);
+ auto r = crush_move(tmp, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ }
+
+ // build crush rule
+ CrushWrapper crush;
+ get_crush(tmp, crush);
+ string rule_name = "rule_37968";
+ int rule_type = pg_pool_t::TYPE_ERASURE;
+ ASSERT_TRUE(!crush.rule_exists(rule_name));
+ int rno;
+ for (rno = 0; rno < crush.get_max_rules(); rno++) {
+ if (!crush.rule_exists(rno))
+ break;
+ }
+ string root_name = "default";
+ int root = crush.get_item_id(root_name);
+ int steps = 6;
+ crush_rule *rule = crush_make_rule(steps, rule_type);
+ int step = 0;
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 5, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSE_TRIES, 100, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, root, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSE_INDEP, 2, 1 /* host*/);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSE_INDEP, 2, 0 /* osd */);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ ASSERT_TRUE(step == steps);
+ auto r = crush_add_rule(crush.get_crush_map(), rule, rno);
+ ASSERT_TRUE(r >= 0);
+ crush.set_rule_name(rno, rule_name);
+ {
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ tmp.apply_incremental(pending_inc);
+ }
+
+ // create a erasuce-coded pool referencing the above rule
+ int64_t pool_37968;
+ {
+ OSDMap::Incremental new_pool_inc(tmp.get_epoch() + 1);
+ new_pool_inc.new_pool_max = tmp.get_pool_max();
+ new_pool_inc.fsid = tmp.get_fsid();
+ pg_pool_t empty;
+ pool_37968 = ++new_pool_inc.new_pool_max;
+ pg_pool_t *p = new_pool_inc.get_new_pool(pool_37968, &empty);
+ p->size = 4;
+ p->set_pg_num(8);
+ p->set_pgp_num(8);
+ p->type = pg_pool_t::TYPE_ERASURE;
+ p->crush_rule = rno;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ new_pool_inc.new_pool_names[pool_37968] = "pool_37968";
+ tmp.apply_incremental(new_pool_inc);
+ }
+
+ pg_t ec_pg(0, pool_37968);
+ pg_t ec_pgid = tmp.raw_pg_to_pg(ec_pg);
+ int from = -1;
+ int to = -1;
+ {
+ // insert a valid pg_upmap_item
+ vector<int> ec_up;
+ int ec_up_primary;
+ tmp.pg_to_raw_up(ec_pgid, &ec_up, &ec_up_primary);
+ ASSERT_TRUE(ec_up.size() == 4);
+ from = *(ec_up.begin());
+ ASSERT_TRUE(from >= 0);
+ auto parent = tmp.crush->get_parent_of_type(from, 1 /* host */, rno);
+ ASSERT_TRUE(parent < 0);
+ // pick an osd of the same parent with *from*
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(ec_up.begin(), ec_up.end(), i) == ec_up.end()) {
+ auto p = tmp.crush->get_parent_of_type(i, 1 /* host */, rno);
+ if (p == parent) {
+ to = i;
+ break;
+ }
+ }
+ }
+ ASSERT_TRUE(to >= 0);
+ ASSERT_TRUE(from != to);
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(from, to));
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[ec_pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ tmp.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmp.have_pg_upmaps(ec_pgid));
+ }
+ {
+ // *clean_pg_upmaps* should not remove the above upmap_item
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, tmp, pending_inc);
+ tmp.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmp.have_pg_upmaps(ec_pgid));
+ }
+ }
+
+ {
+ // TEST pg_upmap
+ {
+ // STEP-1: enumerate all children of up[0]'s parent,
+ // replace up[1] with one of them (other than up[0])
+ int parent = osdmap.crush->get_parent_of_type(up[0],
+ osdmap.crush->get_type_id("host"));
+ set<int> candidates;
+ osdmap.crush->get_leaves(osdmap.crush->get_item_name(parent), &candidates);
+ ASSERT_LT(1U, candidates.size());
+ int replaced_by = -1;
+ for (auto c: candidates) {
+ if (c != up[0]) {
+ replaced_by = c;
+ break;
+ }
+ }
+ {
+ // Check we can handle a negative pg_upmap value
+ vector<int32_t> new_pg_upmap;
+ new_pg_upmap.push_back(up[0]);
+ new_pg_upmap.push_back(-823648512);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap[pgid] = mempool::osdmap::vector<int32_t>(
+ new_pg_upmap.begin(), new_pg_upmap.end());
+ osdmap.apply_incremental(pending_inc);
+ vector<int> new_up;
+ int new_up_primary;
+ // crucial call - _apply_upmap should ignore the negative value
+ osdmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ }
+ ASSERT_NE(-1, replaced_by);
+ // generate a new pg_upmap item and apply
+ vector<int32_t> new_pg_upmap;
+ new_pg_upmap.push_back(up[0]);
+ new_pg_upmap.push_back(replaced_by); // up[1] -> replaced_by
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap[pgid] = mempool::osdmap::vector<int32_t>(
+ new_pg_upmap.begin(), new_pg_upmap.end());
+ osdmap.apply_incremental(pending_inc);
+ {
+ // validate pg_upmap is there
+ vector<int> new_up;
+ int new_up_primary;
+ osdmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ ASSERT_EQ(new_up.size(), up.size());
+ ASSERT_EQ(new_up[0], new_pg_upmap[0]);
+ ASSERT_EQ(new_up[1], new_pg_upmap[1]);
+ // and we shall have two OSDs from a same host now..
+ int parent_0 = osdmap.crush->get_parent_of_type(new_up[0],
+ osdmap.crush->get_type_id("host"));
+ int parent_1 = osdmap.crush->get_parent_of_type(new_up[1],
+ osdmap.crush->get_type_id("host"));
+ ASSERT_EQ(parent_0, parent_1);
+ }
+ }
+ {
+ // STEP-2: apply cure
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, osdmap, pending_inc);
+ osdmap.apply_incremental(pending_inc);
+ {
+ // validate pg_upmap is gone (reverted)
+ vector<int> new_up;
+ int new_up_primary;
+ osdmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ ASSERT_EQ(new_up, up);
+ ASSERT_EQ(new_up_primary, up_primary);
+ }
+ }
+ }
+
+ {
+ // TEST pg_upmap_items
+ // enumerate all used hosts first
+ set<int> parents;
+ for (auto u: up) {
+ int parent = osdmap.crush->get_parent_of_type(u,
+ osdmap.crush->get_type_id("host"));
+ ASSERT_GT(0, parent);
+ parents.insert(parent);
+ }
+ int candidate_parent = 0;
+ set<int> candidate_children;
+ vector<int> up_after_out;
+ {
+ // STEP-1: try mark out up[1] and all other OSDs from the same host
+ int parent = osdmap.crush->get_parent_of_type(up[1],
+ osdmap.crush->get_type_id("host"));
+ set<int> children;
+ osdmap.crush->get_leaves(osdmap.crush->get_item_name(parent),
+ &children);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ for (auto c: children) {
+ pending_inc.new_weight[c] = CEPH_OSD_OUT;
+ }
+ OSDMap tmpmap;
+ tmpmap.deepish_copy_from(osdmap);
+ tmpmap.apply_incremental(pending_inc);
+ vector<int> new_up;
+ int new_up_primary;
+ tmpmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ // verify that we'll have OSDs from a different host..
+ int will_choose = -1;
+ for (auto o: new_up) {
+ int parent = tmpmap.crush->get_parent_of_type(o,
+ osdmap.crush->get_type_id("host"));
+ if (!parents.count(parent)) {
+ will_choose = o;
+ candidate_parent = parent; // record
+ break;
+ }
+ }
+ ASSERT_LT(-1, will_choose); // it is an OSD!
+ ASSERT_NE(candidate_parent, 0);
+ osdmap.crush->get_leaves(osdmap.crush->get_item_name(candidate_parent),
+ &candidate_children);
+ ASSERT_TRUE(candidate_children.count(will_choose));
+ candidate_children.erase(will_choose);
+ ASSERT_FALSE(candidate_children.empty());
+ up_after_out = new_up; // needed for verification..
+ }
+ {
+ // Make sure we can handle a negative pg_upmap_item
+ int victim = up[0];
+ int replaced_by = -823648512;
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(victim, replaced_by));
+ // apply
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ osdmap.apply_incremental(pending_inc);
+ vector<int> new_up;
+ int new_up_primary;
+ // crucial call - _apply_upmap should ignore the negative value
+ osdmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ }
+ {
+ // STEP-2: generating a new pg_upmap_items entry by
+ // replacing up[0] with one coming from candidate_children
+ int victim = up[0];
+ int replaced_by = *candidate_children.begin();
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(victim, replaced_by));
+ // apply
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ osdmap.apply_incremental(pending_inc);
+ {
+ // validate pg_upmap_items is there
+ vector<int> new_up;
+ int new_up_primary;
+ osdmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ ASSERT_EQ(new_up.size(), up.size());
+ ASSERT_TRUE(std::find(new_up.begin(), new_up.end(), replaced_by) !=
+ new_up.end());
+ // and up[1] too
+ ASSERT_TRUE(std::find(new_up.begin(), new_up.end(), up[1]) !=
+ new_up.end());
+ }
+ }
+ {
+ // STEP-3: mark out up[1] and all other OSDs from the same host
+ int parent = osdmap.crush->get_parent_of_type(up[1],
+ osdmap.crush->get_type_id("host"));
+ set<int> children;
+ osdmap.crush->get_leaves(osdmap.crush->get_item_name(parent),
+ &children);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ for (auto c: children) {
+ pending_inc.new_weight[c] = CEPH_OSD_OUT;
+ }
+ osdmap.apply_incremental(pending_inc);
+ {
+ // validate we have two OSDs from the same host now..
+ vector<int> new_up;
+ int new_up_primary;
+ osdmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ ASSERT_EQ(up.size(), new_up.size());
+ int parent_0 = osdmap.crush->get_parent_of_type(new_up[0],
+ osdmap.crush->get_type_id("host"));
+ int parent_1 = osdmap.crush->get_parent_of_type(new_up[1],
+ osdmap.crush->get_type_id("host"));
+ ASSERT_EQ(parent_0, parent_1);
+ }
+ }
+ {
+ // STEP-4: apply cure
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, osdmap, pending_inc);
+ osdmap.apply_incremental(pending_inc);
+ {
+ // validate pg_upmap_items is gone (reverted)
+ vector<int> new_up;
+ int new_up_primary;
+ osdmap.pg_to_raw_up(pgid, &new_up, &new_up_primary);
+ ASSERT_EQ(new_up, up_after_out);
+ }
+ }
+ }
+}
+
+TEST_F(OSDMapTest, BUG_38897) {
+ // http://tracker.ceph.com/issues/38897
+ // build a fresh map with 12 OSDs, without any default pools
+ set_up_map(12, true);
+ const string pool_1("pool1");
+ const string pool_2("pool2");
+ int64_t pool_1_id = -1;
+
+ {
+ // build customized crush rule for "pool1"
+ string host_name = "host_for_pool_1";
+ // build a customized host to capture osd.1~5
+ for (int i = 1; i < 5; i++) {
+ stringstream osd_name;
+ vector<string> move_to;
+ osd_name << "osd." << i;
+ move_to.push_back("root=default");
+ string host_loc = "host=" + host_name;
+ move_to.push_back(host_loc);
+ auto r = crush_move(osdmap, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ }
+ CrushWrapper crush;
+ get_crush(osdmap, crush);
+ auto host_id = crush.get_item_id(host_name);
+ ASSERT_TRUE(host_id < 0);
+ string rule_name = "rule_for_pool1";
+ int rule_type = pg_pool_t::TYPE_REPLICATED;
+ ASSERT_TRUE(!crush.rule_exists(rule_name));
+ int rno;
+ for (rno = 0; rno < crush.get_max_rules(); rno++) {
+ if (!crush.rule_exists(rno))
+ break;
+ }
+ int steps = 7;
+ crush_rule *rule = crush_make_rule(steps, rule_type);
+ int step = 0;
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 5, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSE_TRIES, 100, 0);
+ // always choose osd.0
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, 0, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ // then pick any other random osds
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, host_id, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSELEAF_FIRSTN, 2, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ ASSERT_TRUE(step == steps);
+ auto r = crush_add_rule(crush.get_crush_map(), rule, rno);
+ ASSERT_TRUE(r >= 0);
+ crush.set_rule_name(rno, rule_name);
+ {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ osdmap.apply_incremental(pending_inc);
+ }
+
+ // create "pool1"
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pool_max = osdmap.get_pool_max();
+ auto pool_id = ++pending_inc.new_pool_max;
+ pool_1_id = pool_id;
+ pg_pool_t empty;
+ auto p = pending_inc.get_new_pool(pool_id, &empty);
+ p->size = 3;
+ p->min_size = 1;
+ p->set_pg_num(3);
+ p->set_pgp_num(3);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = rno;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ pending_inc.new_pool_names[pool_id] = pool_1;
+ osdmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(osdmap.have_pg_pool(pool_id));
+ ASSERT_TRUE(osdmap.get_pool_name(pool_id) == pool_1);
+ {
+ for (unsigned i = 0; i < 3; i++) {
+ // 1.x -> [1]
+ pg_t rawpg(i, pool_id);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up;
+ int up_primary;
+ osdmap.pg_to_raw_up(pgid, &up, &up_primary);
+ ASSERT_TRUE(up.size() == 3);
+ ASSERT_TRUE(up[0] == 0);
+
+ // insert a new pg_upmap
+ vector<int32_t> new_up;
+ // and remap 1.x to osd.1 only
+ // this way osd.0 is deemed to be *underfull*
+ // and osd.1 is deemed to be *overfull*
+ new_up.push_back(1);
+ {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap[pgid] = mempool::osdmap::vector<int32_t>(
+ new_up.begin(), new_up.end());
+ osdmap.apply_incremental(pending_inc);
+ }
+ osdmap.pg_to_raw_up(pgid, &up, &up_primary);
+ ASSERT_TRUE(up.size() == 1);
+ ASSERT_TRUE(up[0] == 1);
+ }
+ }
+ }
+
+ {
+ // build customized crush rule for "pool2"
+ string host_name = "host_for_pool_2";
+ // build a customized host to capture osd.6~11
+ for (int i = 6; i < (int)get_num_osds(); i++) {
+ stringstream osd_name;
+ vector<string> move_to;
+ osd_name << "osd." << i;
+ move_to.push_back("root=default");
+ string host_loc = "host=" + host_name;
+ move_to.push_back(host_loc);
+ auto r = crush_move(osdmap, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ }
+ CrushWrapper crush;
+ get_crush(osdmap, crush);
+ auto host_id = crush.get_item_id(host_name);
+ ASSERT_TRUE(host_id < 0);
+ string rule_name = "rule_for_pool2";
+ int rule_type = pg_pool_t::TYPE_REPLICATED;
+ ASSERT_TRUE(!crush.rule_exists(rule_name));
+ int rno;
+ for (rno = 0; rno < crush.get_max_rules(); rno++) {
+ if (!crush.rule_exists(rno))
+ break;
+ }
+ int steps = 7;
+ crush_rule *rule = crush_make_rule(steps, rule_type);
+ int step = 0;
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 5, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSE_TRIES, 100, 0);
+ // always choose osd.0
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, 0, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ // then pick any other random osds
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, host_id, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSELEAF_FIRSTN, 2, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ ASSERT_TRUE(step == steps);
+ auto r = crush_add_rule(crush.get_crush_map(), rule, rno);
+ ASSERT_TRUE(r >= 0);
+ crush.set_rule_name(rno, rule_name);
+ {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ osdmap.apply_incremental(pending_inc);
+ }
+
+ // create "pool2"
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pool_max = osdmap.get_pool_max();
+ auto pool_id = ++pending_inc.new_pool_max;
+ pg_pool_t empty;
+ auto p = pending_inc.get_new_pool(pool_id, &empty);
+ p->size = 3;
+ // include a single PG
+ p->set_pg_num(1);
+ p->set_pgp_num(1);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = rno;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ pending_inc.new_pool_names[pool_id] = pool_2;
+ osdmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(osdmap.have_pg_pool(pool_id));
+ ASSERT_TRUE(osdmap.get_pool_name(pool_id) == pool_2);
+ pg_t rawpg(0, pool_id);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ EXPECT_TRUE(!osdmap.have_pg_upmaps(pgid));
+ vector<int> up;
+ int up_primary;
+ osdmap.pg_to_raw_up(pgid, &up, &up_primary);
+ ASSERT_TRUE(up.size() == 3);
+ ASSERT_TRUE(up[0] == 0);
+
+ {
+ // build a pg_upmap_item that will
+ // remap pg out from *underfull* osd.0
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(0, 10)); // osd.0 -> osd.10
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ osdmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(osdmap.have_pg_upmaps(pgid));
+ vector<int> up;
+ int up_primary;
+ osdmap.pg_to_raw_up(pgid, &up, &up_primary);
+ ASSERT_TRUE(up.size() == 3);
+ ASSERT_TRUE(up[0] == 10);
+ }
+ }
+
+ // ready to go
+ {
+ set<int64_t> only_pools;
+ ASSERT_TRUE(pool_1_id >= 0);
+ only_pools.insert(pool_1_id);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ // require perfect distribution! (max deviation 0)
+ osdmap.calc_pg_upmaps(g_ceph_context,
+ 0, // so we can force optimizing
+ 100,
+ only_pools,
+ &pending_inc);
+ osdmap.apply_incremental(pending_inc);
+ }
+}
+
+TEST_F(OSDMapTest, BUG_40104) {
+ // http://tracker.ceph.com/issues/40104
+ int big_osd_num = 5000;
+ int big_pg_num = 10000;
+ set_up_map(big_osd_num, true);
+ int pool_id;
+ {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pool_max = osdmap.get_pool_max();
+ pool_id = ++pending_inc.new_pool_max;
+ pg_pool_t empty;
+ auto p = pending_inc.get_new_pool(pool_id, &empty);
+ p->size = 3;
+ p->min_size = 1;
+ p->set_pg_num(big_pg_num);
+ p->set_pgp_num(big_pg_num);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = 0;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ pending_inc.new_pool_names[pool_id] = "big_pool";
+ osdmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(osdmap.have_pg_pool(pool_id));
+ ASSERT_TRUE(osdmap.get_pool_name(pool_id) == "big_pool");
+ }
+ {
+ // generate pg_upmap_items for each pg
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ for (int i = 0; i < big_pg_num; i++) {
+ pg_t rawpg(i, pool_id);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ vector<int> up;
+ int up_primary;
+ osdmap.pg_to_raw_up(pgid, &up, &up_primary);
+ ASSERT_TRUE(up.size() == 3);
+ int victim = up[0];
+ int replaced_by = random() % big_osd_num;
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ // note that it might or might not be valid, we don't care
+ new_pg_upmap_items.push_back(make_pair(victim, replaced_by));
+ pending_inc.new_pg_upmap_items[pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ }
+ osdmap.apply_incremental(pending_inc);
+ }
+ {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ auto start = mono_clock::now();
+ clean_pg_upmaps(g_ceph_context, osdmap, pending_inc);
+ auto latency = mono_clock::now() - start;
+ std::cout << "clean_pg_upmaps (~" << big_pg_num
+ << " pg_upmap_items) latency:" << timespan_str(latency)
+ << std::endl;
+ }
+}
+
+TEST_F(OSDMapTest, BUG_42052) {
+ // https://tracker.ceph.com/issues/42052
+ set_up_map(6, true);
+ const string pool_name("pool");
+ // build customized crush rule for "pool"
+ CrushWrapper crush;
+ get_crush(osdmap, crush);
+ string rule_name = "rule";
+ int rule_type = pg_pool_t::TYPE_REPLICATED;
+ ASSERT_TRUE(!crush.rule_exists(rule_name));
+ int rno;
+ for (rno = 0; rno < crush.get_max_rules(); rno++) {
+ if (!crush.rule_exists(rno))
+ break;
+ }
+ int steps = 8;
+ crush_rule *rule = crush_make_rule(steps, rule_type);
+ int step = 0;
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 5, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSE_TRIES, 100, 0);
+ // always choose osd.0, osd.1, osd.2
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, 0, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, 0, 1);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, 0, 2);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ ASSERT_TRUE(step == steps);
+ auto r = crush_add_rule(crush.get_crush_map(), rule, rno);
+ ASSERT_TRUE(r >= 0);
+ crush.set_rule_name(rno, rule_name);
+ {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ osdmap.apply_incremental(pending_inc);
+ }
+
+ // create "pool"
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pool_max = osdmap.get_pool_max();
+ auto pool_id = ++pending_inc.new_pool_max;
+ pg_pool_t empty;
+ auto p = pending_inc.get_new_pool(pool_id, &empty);
+ p->size = 3;
+ p->min_size = 1;
+ p->set_pg_num(1);
+ p->set_pgp_num(1);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = rno;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ pending_inc.new_pool_names[pool_id] = pool_name;
+ osdmap.apply_incremental(pending_inc);
+ ASSERT_TRUE(osdmap.have_pg_pool(pool_id));
+ ASSERT_TRUE(osdmap.get_pool_name(pool_id) == pool_name);
+ pg_t rawpg(0, pool_id);
+ pg_t pgid = osdmap.raw_pg_to_pg(rawpg);
+ {
+ // pg_upmap 1.0 [2,3,5]
+ vector<int32_t> new_up{2,3,5};
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap[pgid] = mempool::osdmap::vector<int32_t>(
+ new_up.begin(), new_up.end());
+ osdmap.apply_incremental(pending_inc);
+ }
+ {
+ // pg_upmap_items 1.0 [0,3,4,5]
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(0, 3));
+ new_pg_upmap_items.push_back(make_pair(4, 5));
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ osdmap.apply_incremental(pending_inc);
+ }
+ {
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, osdmap, pending_inc);
+ osdmap.apply_incremental(pending_inc);
+ ASSERT_FALSE(osdmap.have_pg_upmaps(pgid));
+ }
+}
+
+TEST_F(OSDMapTest, BUG_42485) {
+ set_up_map(60);
+ {
+ // build a temporary crush topology of 2datacenters, 3racks per dc,
+ // 1host per rack, 10osds per host
+ OSDMap tmp; // use a tmpmap here, so we do not dirty origin map..
+ tmp.deepish_copy_from(osdmap);
+ const int expected_host_num = 6;
+ int osd_per_host = (int)get_num_osds() / expected_host_num;
+ ASSERT_GE(osd_per_host, 10);
+ int host_per_dc = 3;
+ int index = 0;
+ int dc_index = 0;
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (i && i % osd_per_host == 0) {
+ ++index;
+ }
+ if (i && i % (host_per_dc * osd_per_host) == 0) {
+ ++dc_index;
+ }
+ stringstream osd_name;
+ stringstream host_name;
+ stringstream rack_name;
+ stringstream dc_name;
+ vector<string> move_to;
+ osd_name << "osd." << i;
+ host_name << "host-" << index;
+ rack_name << "rack-" << index;
+ dc_name << "dc-" << dc_index;
+ move_to.push_back("root=default");
+ string dc_loc = "datacenter=" + dc_name.str();
+ move_to.push_back(dc_loc);
+ string rack_loc = "rack=" + rack_name.str();
+ move_to.push_back(rack_loc);
+ string host_loc = "host=" + host_name.str();
+ move_to.push_back(host_loc);
+ auto r = crush_move(tmp, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ }
+
+ // build crush rule
+ CrushWrapper crush;
+ get_crush(tmp, crush);
+ string rule_name = "rule_xeus_993_1";
+ int rule_type = pg_pool_t::TYPE_REPLICATED;
+ ASSERT_TRUE(!crush.rule_exists(rule_name));
+ int rno;
+ for (rno = 0; rno < crush.get_max_rules(); rno++) {
+ if (!crush.rule_exists(rno))
+ break;
+ }
+ string root_name = "default";
+ string dc_1 = "dc-0";
+ int dc1 = crush.get_item_id(dc_1);
+ string dc_2 = "dc-1";
+ int dc2 = crush.get_item_id(dc_2);
+ int steps = 8;
+ crush_rule *rule = crush_make_rule(steps, rule_type);
+ int step = 0;
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 5, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSE_TRIES, 100, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, dc1, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSELEAF_FIRSTN, 2, 3 /* rack */);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, dc2, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSELEAF_FIRSTN, 2, 3 /* rack */);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ ASSERT_TRUE(step == steps);
+ auto r = crush_add_rule(crush.get_crush_map(), rule, rno);
+ ASSERT_TRUE(r >= 0);
+ crush.set_rule_name(rno, rule_name);
+ {
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ tmp.apply_incremental(pending_inc);
+ }
+ // create a repliacted pool referencing the above rule
+ int64_t pool_xeus_993;
+ {
+ OSDMap::Incremental new_pool_inc(tmp.get_epoch() + 1);
+ new_pool_inc.new_pool_max = tmp.get_pool_max();
+ new_pool_inc.fsid = tmp.get_fsid();
+ pg_pool_t empty;
+ pool_xeus_993 = ++new_pool_inc.new_pool_max;
+ pg_pool_t *p = new_pool_inc.get_new_pool(pool_xeus_993, &empty);
+ p->size = 4;
+ p->set_pg_num(4096);
+ p->set_pgp_num(4096);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = rno;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ new_pool_inc.new_pool_names[pool_xeus_993] = "pool_xeus_993";
+ tmp.apply_incremental(new_pool_inc);
+ }
+
+ pg_t rep_pg(0, pool_xeus_993);
+ pg_t rep_pgid = tmp.raw_pg_to_pg(rep_pg);
+ {
+ int from = -1;
+ int to = -1;
+ vector<int> rep_up;
+ int rep_up_primary;
+ tmp.pg_to_raw_up(rep_pgid, &rep_up, &rep_up_primary);
+ std::cout << "pgid " << rep_up << " up " << rep_up << std::endl;
+ ASSERT_TRUE(rep_up.size() == 4);
+ from = *(rep_up.begin());
+ ASSERT_TRUE(from >= 0);
+ auto dc_parent = tmp.crush->get_parent_of_type(from, 8 /* dc */, rno);
+ if (dc_parent == dc1)
+ dc_parent = dc2;
+ else
+ dc_parent = dc1;
+ auto rack_parent = tmp.crush->get_parent_of_type(from, 3 /* rack */, rno);
+ ASSERT_TRUE(dc_parent < 0);
+ ASSERT_TRUE(rack_parent < 0);
+ set<int> rack_parents;
+ for (auto &i: rep_up) {
+ if (i == from) continue;
+ auto rack_parent = tmp.crush->get_parent_of_type(i, 3 /* rack */, rno);
+ rack_parents.insert(rack_parent);
+ }
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(rep_up.begin(), rep_up.end(), i) == rep_up.end()) {
+ auto dc_p = tmp.crush->get_parent_of_type(i, 8 /* dc */, rno);
+ auto rack_p = tmp.crush->get_parent_of_type(i, 3 /* rack */, rno);
+ if (dc_p == dc_parent &&
+ rack_parents.find(rack_p) == rack_parents.end()) {
+ to = i;
+ break;
+ }
+ }
+ }
+ ASSERT_TRUE(to >= 0);
+ ASSERT_TRUE(from != to);
+ std::cout << "from " << from << " to " << to << std::endl;
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(from, to));
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[rep_pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ tmp.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmp.have_pg_upmaps(rep_pgid));
+ }
+ pg_t rep_pg2(2, pool_xeus_993);
+ pg_t rep_pgid2 = tmp.raw_pg_to_pg(rep_pg2);
+ {
+ pg_t rep_pgid = rep_pgid2;
+ vector<int> from_osds{-1, -1};
+ vector<int> rep_up;
+ int rep_up_primary;
+ tmp.pg_to_raw_up(rep_pgid, &rep_up, &rep_up_primary);
+ ASSERT_TRUE(rep_up.size() == 4);
+ from_osds[0] = *(rep_up.begin());
+ from_osds[1] = *(rep_up.rbegin());
+ std::cout << "pgid " << rep_pgid2 << " up " << rep_up << std::endl;
+ ASSERT_TRUE(*(from_osds.begin()) >= 0);
+ ASSERT_TRUE(*(from_osds.rbegin()) >= 0);
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ for (auto &from: from_osds) {
+ int to = -1;
+ auto dc_parent = tmp.crush->get_parent_of_type(from, 8 /* dc */, rno);
+ if (dc_parent == dc1)
+ dc_parent = dc2;
+ else
+ dc_parent = dc1;
+ auto rack_parent = tmp.crush->get_parent_of_type(from, 3 /* rack */, rno);
+ ASSERT_TRUE(dc_parent < 0);
+ ASSERT_TRUE(rack_parent < 0);
+ set<int> rack_parents;
+ for (auto &i: rep_up) {
+ if (i == from) continue;
+ auto rack_parent = tmp.crush->get_parent_of_type(i, 3 /* rack */, rno);
+ rack_parents.insert(rack_parent);
+ }
+ for (auto &i: new_pg_upmap_items) {
+ auto rack_from = tmp.crush->get_parent_of_type(i.first, 3, rno);
+ auto rack_to = tmp.crush->get_parent_of_type(i.second, 3, rno);
+ rack_parents.insert(rack_from);
+ rack_parents.insert(rack_to);
+ }
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(rep_up.begin(), rep_up.end(), i) == rep_up.end()) {
+ auto dc_p = tmp.crush->get_parent_of_type(i, 8 /* dc */, rno);
+ auto rack_p = tmp.crush->get_parent_of_type(i, 3 /* rack */, rno);
+ if (dc_p == dc_parent &&
+ rack_parents.find(rack_p) == rack_parents.end()) {
+ to = i;
+ break;
+ }
+ }
+ }
+ ASSERT_TRUE(to >= 0);
+ ASSERT_TRUE(from != to);
+ std::cout << "from " << from << " to " << to << std::endl;
+ new_pg_upmap_items.push_back(make_pair(from, to));
+ }
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[rep_pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ tmp.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmp.have_pg_upmaps(rep_pgid));
+ }
+ {
+ // *maybe_remove_pg_upmaps* should remove the above upmap_item
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, tmp, pending_inc);
+ tmp.apply_incremental(pending_inc);
+ ASSERT_FALSE(tmp.have_pg_upmaps(rep_pgid));
+ ASSERT_FALSE(tmp.have_pg_upmaps(rep_pgid2));
+ }
+ }
+}
+
+TEST(PGTempMap, basic)
+{
+ PGTempMap m;
+ pg_t a(1,1);
+ for (auto i=3; i<1000; ++i) {
+ pg_t x(i, 1);
+ m.set(x, {static_cast<int>(i)});
+ }
+ pg_t b(2,1);
+ m.set(a, {1, 2});
+ ASSERT_NE(m.find(a), m.end());
+ ASSERT_EQ(m.find(a), m.begin());
+ ASSERT_EQ(m.find(b), m.end());
+ ASSERT_EQ(998u, m.size());
+}
+
+TEST_F(OSDMapTest, BUG_43124) {
+ set_up_map(200);
+ {
+ // https://tracker.ceph.com/issues/43124
+
+ // build a temporary crush topology of 5racks,
+ // 4 hosts per rack, 10osds per host
+ OSDMap tmp; // use a tmpmap here, so we do not dirty origin map..
+ tmp.deepish_copy_from(osdmap);
+ const int expected_host_num = 20;
+ int osd_per_host = (int)get_num_osds() / expected_host_num;
+ ASSERT_GE(osd_per_host, 10);
+ int host_per_rack = 4;
+ int index = 0;
+ int rack_index = 0;
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (i && i % osd_per_host == 0) {
+ ++index;
+ }
+ if (i && i % (host_per_rack * osd_per_host) == 0) {
+ ++rack_index;
+ }
+ stringstream osd_name;
+ stringstream host_name;
+ stringstream rack_name;
+ vector<string> move_to;
+ osd_name << "osd." << i;
+ host_name << "host-" << index;
+ rack_name << "rack-" << rack_index;
+ move_to.push_back("root=default");
+ string rack_loc = "rack=" + rack_name.str();
+ move_to.push_back(rack_loc);
+ string host_loc = "host=" + host_name.str();
+ move_to.push_back(host_loc);
+ auto r = crush_move(tmp, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ }
+
+ // build crush rule
+ CrushWrapper crush;
+ get_crush(tmp, crush);
+ string rule_name = "rule_angel_1944";
+ int rule_type = pg_pool_t::TYPE_ERASURE;
+ ASSERT_TRUE(!crush.rule_exists(rule_name));
+ int rno;
+ for (rno = 0; rno < crush.get_max_rules(); rno++) {
+ if (!crush.rule_exists(rno))
+ break;
+ }
+ int steps = 6;
+ string root_name = "default";
+ int root = crush.get_item_id(root_name);
+ crush_rule *rule = crush_make_rule(steps, rule_type);
+ int step = 0;
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 5, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSE_TRIES, 100, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, root, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSE_FIRSTN, 4, 3 /* rack */);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSELEAF_INDEP, 3, 1 /* host */);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ ASSERT_TRUE(step == steps);
+ auto r = crush_add_rule(crush.get_crush_map(), rule, rno);
+ ASSERT_TRUE(r >= 0);
+ crush.set_rule_name(rno, rule_name);
+ {
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ tmp.apply_incremental(pending_inc);
+ }
+ {
+ stringstream oss;
+ crush.dump_tree(&oss, NULL);
+ std::cout << oss.str() << std::endl;
+ Formatter *f = Formatter::create("json-pretty");
+ f->open_object_section("crush_rules");
+ crush.dump_rules(f);
+ f->close_section();
+ f->flush(cout);
+ delete f;
+ }
+ // create a erasuce-coded pool referencing the above rule
+ int64_t pool_angel_1944;
+ {
+ OSDMap::Incremental new_pool_inc(tmp.get_epoch() + 1);
+ new_pool_inc.new_pool_max = tmp.get_pool_max();
+ new_pool_inc.fsid = tmp.get_fsid();
+ pg_pool_t empty;
+ pool_angel_1944 = ++new_pool_inc.new_pool_max;
+ pg_pool_t *p = new_pool_inc.get_new_pool(pool_angel_1944, &empty);
+ p->size = 12;
+ p->set_pg_num(4096);
+ p->set_pgp_num(4096);
+ p->type = pg_pool_t::TYPE_ERASURE;
+ p->crush_rule = rno;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ new_pool_inc.new_pool_names[pool_angel_1944] = "pool_angel_1944";
+ tmp.apply_incremental(new_pool_inc);
+ }
+
+ pg_t rep_pg(0, pool_angel_1944);
+ pg_t rep_pgid = tmp.raw_pg_to_pg(rep_pg);
+ {
+ // insert a pg_upmap_item
+ int from = -1;
+ int to = -1;
+ vector<int> rep_up;
+ int rep_up_primary;
+ tmp.pg_to_raw_up(rep_pgid, &rep_up, &rep_up_primary);
+ std::cout << "pgid " << rep_pgid << " up " << rep_up << std::endl;
+ ASSERT_TRUE(rep_up.size() == 12);
+ from = *(rep_up.begin());
+ ASSERT_TRUE(from >= 0);
+ auto from_rack = tmp.crush->get_parent_of_type(from, 3 /* rack */, rno);
+ set<int> failure_domains;
+ for (auto &osd : rep_up) {
+ failure_domains.insert(tmp.crush->get_parent_of_type(osd, 1 /* host */, rno));
+ }
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ if (std::find(rep_up.begin(), rep_up.end(), i) == rep_up.end()) {
+ auto to_rack = tmp.crush->get_parent_of_type(i, 3 /* rack */, rno);
+ auto to_host = tmp.crush->get_parent_of_type(i, 1 /* host */, rno);
+ if (to_rack != from_rack && failure_domains.count(to_host) == 0) {
+ to = i;
+ break;
+ }
+ }
+ }
+ ASSERT_TRUE(to >= 0);
+ ASSERT_TRUE(from != to);
+ std::cout << "from " << from << " to " << to << std::endl;
+ vector<pair<int32_t,int32_t>> new_pg_upmap_items;
+ new_pg_upmap_items.push_back(make_pair(from, to));
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.new_pg_upmap_items[rep_pgid] =
+ mempool::osdmap::vector<pair<int32_t,int32_t>>(
+ new_pg_upmap_items.begin(), new_pg_upmap_items.end());
+ tmp.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmp.have_pg_upmaps(rep_pgid));
+ }
+ {
+ // *maybe_remove_pg_upmaps* should not remove the above upmap_item
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, tmp, pending_inc);
+ tmp.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmp.have_pg_upmaps(rep_pgid));
+ }
+ }
+}
+
+TEST_F(OSDMapTest, BUG_48884)
+{
+
+ set_up_map(12);
+
+ unsigned int host_index = 1;
+ for (unsigned int x=0; x < get_num_osds();) {
+ // Create three hosts with four osds each
+ for (unsigned int y=0; y < 4; y++) {
+ stringstream osd_name;
+ stringstream host_name;
+ vector<string> move_to;
+ osd_name << "osd." << x;
+ host_name << "host-" << host_index;
+ move_to.push_back("root=default");
+ move_to.push_back("rack=localrack");
+ string host_loc = "host=" + host_name.str();
+ move_to.push_back(host_loc);
+ int r = crush_move(osdmap, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ x++;
+ }
+ host_index++;
+ }
+
+ CrushWrapper crush;
+ get_crush(osdmap, crush);
+ auto host_id = crush.get_item_id("localhost");
+ crush.remove_item(g_ceph_context, host_id, false);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ osdmap.apply_incremental(pending_inc);
+
+ PGMap pgmap;
+ osd_stat_t stats, stats_null;
+ stats.statfs.total = 500000;
+ stats.statfs.available = 50000;
+ stats.statfs.omap_allocated = 50000;
+ stats.statfs.internal_metadata = 50000;
+ stats_null.statfs.total = 0;
+ stats_null.statfs.available = 0;
+ stats_null.statfs.omap_allocated = 0;
+ stats_null.statfs.internal_metadata = 0;
+ for (unsigned int x=0; x < get_num_osds(); x++) {
+ if (x > 3 && x < 8) {
+ pgmap.osd_stat.insert({x,stats_null});
+ } else {
+ pgmap.osd_stat.insert({x,stats});
+ }
+ }
+
+ stringstream ss;
+ boost::scoped_ptr<Formatter> f(Formatter::create("json-pretty"));
+ print_osd_utilization(osdmap, pgmap, ss, f.get(), true, "root");
+ JSONParser parser;
+ parser.parse(ss.str().c_str(), static_cast<int>(ss.str().size()));
+ auto iter = parser.find_first();
+ for (const auto& bucket : (*iter)->get_array_elements()) {
+ JSONParser parser2;
+ parser2.parse(bucket.c_str(), static_cast<int>(bucket.size()));
+ auto* obj = parser2.find_obj("name");
+ if (obj->get_data().compare("localrack") == 0) {
+ obj = parser2.find_obj("kb");
+ ASSERT_EQ(obj->get_data(), "3904");
+ obj = parser2.find_obj("kb_used");
+ ASSERT_EQ(obj->get_data(), "3512");
+ obj = parser2.find_obj("kb_used_omap");
+ ASSERT_EQ(obj->get_data(), "384");
+ obj = parser2.find_obj("kb_used_meta");
+ ASSERT_EQ(obj->get_data(), "384");
+ obj = parser2.find_obj("kb_avail");
+ ASSERT_EQ(obj->get_data(), "384");
+ }
+ }
+}
+
+TEST_P(OSDMapTest, BUG_51842) {
+ set_up_map(3, true);
+ OSDMap tmp; // use a tmpmap here, so we do not dirty origin map..
+ tmp.deepish_copy_from(osdmap);
+ for (int i = 0; i < (int)get_num_osds(); i++) {
+ stringstream osd_name;
+ stringstream host_name;
+ vector<string> move_to;
+ osd_name << "osd." << i;
+ host_name << "host=host-" << i;
+ move_to.push_back("root=infra-1706");
+ move_to.push_back(host_name.str());
+ auto r = crush_move(tmp, osd_name.str(), move_to);
+ ASSERT_EQ(0, r);
+ }
+
+ // build crush rule
+ CrushWrapper crush;
+ get_crush(tmp, crush);
+ string rule_name = "infra-1706";
+ int rule_type = pg_pool_t::TYPE_REPLICATED;
+ ASSERT_TRUE(!crush.rule_exists(rule_name));
+ int rno;
+ for (rno = 0; rno < crush.get_max_rules(); rno++) {
+ if (!crush.rule_exists(rno))
+ break;
+ }
+ string root_bucket = "infra-1706";
+ int root = crush.get_item_id(root_bucket);
+ int steps = 5;
+ crush_rule *rule = crush_make_rule(steps, rule_type);
+ int step = 0;
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 5, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_SET_CHOOSE_TRIES, 100, 0);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_TAKE, root, 0);
+ // note: it's ok to set like 'step chooseleaf_firstn 0 host'
+ std::pair<int, int> param = GetParam();
+ int rep_num = std::get<0>(param);
+ int domain = std::get<1>(param);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_CHOOSELEAF_FIRSTN, rep_num, domain);
+ crush_rule_set_step(rule, step++, CRUSH_RULE_EMIT, 0, 0);
+ ASSERT_TRUE(step == steps);
+ auto r = crush_add_rule(crush.get_crush_map(), rule, rno);
+ ASSERT_TRUE(r >= 0);
+ crush.set_rule_name(rno, rule_name);
+ {
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.crush.clear();
+ crush.encode(pending_inc.crush, CEPH_FEATURES_SUPPORTED_DEFAULT);
+ tmp.apply_incremental(pending_inc);
+ }
+ {
+ stringstream oss;
+ crush.dump_tree(&oss, NULL);
+ std::cout << oss.str() << std::endl;
+ Formatter *f = Formatter::create("json-pretty");
+ f->open_object_section("crush_rules");
+ crush.dump_rules(f);
+ f->close_section();
+ f->flush(cout);
+ delete f;
+ }
+ // create a replicated pool referencing the above rule
+ int64_t pool_infra_1706;
+ {
+ OSDMap::Incremental new_pool_inc(tmp.get_epoch() + 1);
+ new_pool_inc.new_pool_max = tmp.get_pool_max();
+ new_pool_inc.fsid = tmp.get_fsid();
+ pg_pool_t empty;
+ pool_infra_1706 = ++new_pool_inc.new_pool_max;
+ pg_pool_t *p = new_pool_inc.get_new_pool(pool_infra_1706, &empty);
+ p->size = 3;
+ p->min_size = 1;
+ p->set_pg_num(256);
+ p->set_pgp_num(256);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = rno;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ new_pool_inc.new_pool_names[pool_infra_1706] = "pool_infra_1706";
+ tmp.apply_incremental(new_pool_inc);
+ }
+
+ // add upmaps
+ pg_t rep_pg(3, pool_infra_1706);
+ pg_t rep_pgid = tmp.raw_pg_to_pg(rep_pg);
+ pg_t rep_pg2(4, pool_infra_1706);
+ pg_t rep_pgid2 = tmp.raw_pg_to_pg(rep_pg2);
+ pg_t rep_pg3(6, pool_infra_1706);
+ pg_t rep_pgid3 = tmp.raw_pg_to_pg(rep_pg3);
+ {
+ OSDMap::Incremental pending_inc(tmp.get_epoch() + 1);
+ pending_inc.new_pg_upmap[rep_pgid] = mempool::osdmap::vector<int32_t>({1,0,2});
+ pending_inc.new_pg_upmap[rep_pgid2] = mempool::osdmap::vector<int32_t>({1,2,0});
+ pending_inc.new_pg_upmap[rep_pgid3] = mempool::osdmap::vector<int32_t>({1,2,0});
+ tmp.apply_incremental(pending_inc);
+ ASSERT_TRUE(tmp.have_pg_upmaps(rep_pgid));
+ ASSERT_TRUE(tmp.have_pg_upmaps(rep_pgid2));
+ ASSERT_TRUE(tmp.have_pg_upmaps(rep_pgid3));
+ }
+
+ {
+ // now, set pool size to 1
+ OSDMap tmpmap;
+ tmpmap.deepish_copy_from(tmp);
+ OSDMap::Incremental new_pool_inc(tmpmap.get_epoch() + 1);
+ pg_pool_t p = *tmpmap.get_pg_pool(pool_infra_1706);
+ p.size = 1;
+ p.last_change = new_pool_inc.epoch;
+ new_pool_inc.new_pools[pool_infra_1706] = p;
+ tmpmap.apply_incremental(new_pool_inc);
+
+ OSDMap::Incremental new_pending_inc(tmpmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, tmpmap, new_pending_inc);
+ tmpmap.apply_incremental(new_pending_inc);
+ // check pg upmaps
+ ASSERT_TRUE(!tmpmap.have_pg_upmaps(rep_pgid));
+ ASSERT_TRUE(!tmpmap.have_pg_upmaps(rep_pgid2));
+ ASSERT_TRUE(!tmpmap.have_pg_upmaps(rep_pgid3));
+ }
+ {
+ // now, set pool size to 4
+ OSDMap tmpmap;
+ tmpmap.deepish_copy_from(tmp);
+ OSDMap::Incremental new_pool_inc(tmpmap.get_epoch() + 1);
+ pg_pool_t p = *tmpmap.get_pg_pool(pool_infra_1706);
+ p.size = 4;
+ p.last_change = new_pool_inc.epoch;
+ new_pool_inc.new_pools[pool_infra_1706] = p;
+ tmpmap.apply_incremental(new_pool_inc);
+
+ OSDMap::Incremental new_pending_inc(tmpmap.get_epoch() + 1);
+ clean_pg_upmaps(g_ceph_context, tmpmap, new_pending_inc);
+ tmpmap.apply_incremental(new_pending_inc);
+ // check pg upmaps
+ ASSERT_TRUE(!tmpmap.have_pg_upmaps(rep_pgid));
+ ASSERT_TRUE(!tmpmap.have_pg_upmaps(rep_pgid2));
+ ASSERT_TRUE(!tmpmap.have_pg_upmaps(rep_pgid3));
+ }
+}
+
+const string OSDMapTest::range_addrs[] = {"198.51.100.0/22", "10.2.5.102/32", "2001:db8::/48",
+ "3001:db8::/72", "4001:db8::/30", "5001:db8::/64", "6001:db8::/128", "7001:db8::/127"};
+const string OSDMapTest::ip_addrs[] = {"198.51.100.14", "198.51.100.0", "198.51.103.255",
+ "10.2.5.102",
+ "2001:db8:0:0:0:0:0:0", "2001:db8:0:0:0:0001:ffff:ffff",
+ "2001:db8:0:ffff:ffff:ffff:ffff:ffff",
+ "3001:db8:0:0:0:0:0:0", "3001:db8:0:0:0:0001:ffff:ffff",
+ "3001:db8:0:0:00ff:ffff:ffff:ffff",
+ "4001:db8::", "4001:db8:0:0:0:0001:ffff:ffff",
+ "4001:dbb:ffff:ffff:ffff:ffff:ffff:ffff",
+ "5001:db8:0:0:0:0:0:0", "5001:db8:0:0:0:0:ffff:ffff",
+ "5001:db8:0:0:ffff:ffff:ffff:ffff",
+ "6001:db8:0:0:0:0:0:0",
+ "7001:db8:0:0:0:0:0:0", "7001:db8:0:0:0:0:0:0001"
+};
+const string OSDMapTest::unblocked_ip_addrs[] = { "0.0.0.0", "1.1.1.1", "192.168.1.1",
+ "198.51.99.255", "198.51.104.0",
+ "10.2.5.101", "10.2.5.103",
+ "2001:db7:ffff:ffff:ffff:ffff:ffff:ffff", "2001:db8:0001::",
+ "3001:db7:ffff:ffff:ffff:ffff:ffff:ffff", "3001:db8:0:0:0100::",
+ "4001:db7:ffff:ffff:ffff:ffff:ffff:ffff", "4001:dbc::",
+ "5001:db7:ffff:ffff:ffff:ffff:ffff:ffff", "5001:db8:0:0001:0:0:0:0",
+ "6001:db8:0:0:0:0:0:0001",
+ "7001:db7:ffff:ffff:ffff:ffff:ffff:ffff", "7001:db8:0:0:0:0:0:0002"
+};
+
+TEST_F(OSDMapTest, blocklisting_ips) {
+ set_up_map(6); //whatever
+
+ OSDMap::Incremental new_blocklist_inc(osdmap.get_epoch() + 1);
+ for (const auto& a : ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ new_blocklist_inc.new_blocklist[addr] = ceph_clock_now();
+ }
+ osdmap.apply_incremental(new_blocklist_inc);
+
+ for (const auto& a: ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ ASSERT_TRUE(osdmap.is_blocklisted(addr, g_ceph_context));
+ }
+ for (const auto& a: unblocked_ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ ASSERT_FALSE(osdmap.is_blocklisted(addr, g_ceph_context));
+ }
+
+ OSDMap::Incremental rm_blocklist_inc(osdmap.get_epoch() + 1);
+ for (const auto& a : ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ rm_blocklist_inc.old_blocklist.push_back(addr);
+ }
+ osdmap.apply_incremental(rm_blocklist_inc);
+ for (const auto& a: ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ ASSERT_FALSE(osdmap.is_blocklisted(addr, g_ceph_context));
+ }
+ for (const auto& a: unblocked_ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (blocklisted) {
+ cout << "erroneously blocklisted " << addr << std::endl;
+ }
+ EXPECT_FALSE(blocklisted);
+ }
+}
+
+TEST_F(OSDMapTest, blocklisting_ranges) {
+ set_up_map(6); //whatever
+ OSDMap::Incremental range_blocklist_inc(osdmap.get_epoch() + 1);
+ for (const auto& a : range_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.type = entity_addr_t::TYPE_CIDR;
+ range_blocklist_inc.new_range_blocklist[addr] = ceph_clock_now();
+ }
+ osdmap.apply_incremental(range_blocklist_inc);
+
+ for (const auto& a: ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (!blocklisted) {
+ cout << "erroneously not blocklisted " << addr << std::endl;
+ }
+ ASSERT_TRUE(blocklisted);
+ }
+ for (const auto& a: unblocked_ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (blocklisted) {
+ cout << "erroneously blocklisted " << addr << std::endl;
+ }
+ EXPECT_FALSE(blocklisted);
+ }
+
+ OSDMap::Incremental rm_range_blocklist(osdmap.get_epoch() + 1);
+ for (const auto& a : range_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.type = entity_addr_t::TYPE_CIDR;
+ rm_range_blocklist.old_range_blocklist.push_back(addr);
+ }
+ osdmap.apply_incremental(rm_range_blocklist);
+
+ for (const auto& a: ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ ASSERT_FALSE(osdmap.is_blocklisted(addr, g_ceph_context));
+ }
+ for (const auto& a: unblocked_ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (blocklisted) {
+ cout << "erroneously blocklisted " << addr << std::endl;
+ }
+ EXPECT_FALSE(blocklisted);
+ }
+}
+
+TEST_F(OSDMapTest, blocklisting_everything) {
+ set_up_map(6); //whatever
+ OSDMap::Incremental range_blocklist_inc(osdmap.get_epoch() + 1);
+ entity_addr_t baddr;
+ baddr.parse("2001:db8::/0");
+ baddr.type = entity_addr_t::TYPE_CIDR;
+ range_blocklist_inc.new_range_blocklist[baddr] = ceph_clock_now();
+ osdmap.apply_incremental(range_blocklist_inc);
+
+ for (const auto& a: ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ if (addr.is_ipv4()) continue;
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (!blocklisted) {
+ cout << "erroneously not blocklisted " << addr << std::endl;
+ }
+ ASSERT_TRUE(blocklisted);
+ }
+ for (const auto& a: unblocked_ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ if (addr.is_ipv4()) continue;
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (!blocklisted) {
+ cout << "erroneously not blocklisted " << addr << std::endl;
+ }
+ ASSERT_TRUE(blocklisted);
+ }
+
+ OSDMap::Incremental swap_blocklist_inc(osdmap.get_epoch()+1);
+ swap_blocklist_inc.old_range_blocklist.push_back(baddr);
+
+ entity_addr_t caddr;
+ caddr.parse("1.1.1.1/0");
+ caddr.type = entity_addr_t::TYPE_CIDR;
+ swap_blocklist_inc.new_range_blocklist[caddr] = ceph_clock_now();
+ osdmap.apply_incremental(swap_blocklist_inc);
+
+ for (const auto& a: ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ if (!addr.is_ipv4()) continue;
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (!blocklisted) {
+ cout << "erroneously not blocklisted " << addr << std::endl;
+ }
+ ASSERT_TRUE(blocklisted);
+ }
+ for (const auto& a: unblocked_ip_addrs) {
+ entity_addr_t addr;
+ addr.parse(a);
+ addr.set_type(entity_addr_t::TYPE_LEGACY);
+ if (!addr.is_ipv4()) continue;
+ bool blocklisted = osdmap.is_blocklisted(addr, g_ceph_context);
+ if (!blocklisted) {
+ cout << "erroneously not blocklisted " << addr << std::endl;
+ }
+ ASSERT_TRUE(blocklisted);
+ }
+}
+
+TEST_F(OSDMapTest, ReadBalanceScore1) {
+ std::srand ( unsigned ( std::time(0) ) );
+ uint osd_rand = rand() % 13;
+ set_up_map(6 + osd_rand); //whatever
+ auto pools = osdmap.get_pools();
+ for (auto &[pid, pg_pool] : pools) {
+ const pg_pool_t *pi = osdmap.get_pg_pool(pid);
+ if (pi->is_replicated()) {
+ //cout << "pool " << pid << " " << pg_pool << std::endl;
+ auto replica_count = pi->get_size();
+ OSDMap::read_balance_info_t rbi;
+ auto rc = osdmap.calc_read_balance_score(g_ceph_context, pid, &rbi);
+
+ // "Normal" score is between 1 and num_osds
+ ASSERT_TRUE(rc == 0);
+ ASSERT_TRUE(score_in_range(rbi.adjusted_score));
+ ASSERT_TRUE(score_in_range(rbi.acting_adj_score));
+ ASSERT_TRUE(rbi.err_msg.empty());
+
+ // When all OSDs have primary_affinity 0, score should be 0
+ auto num_osds = get_num_osds();
+ set_primary_affinity_all(0.);
+
+ rc = osdmap.calc_read_balance_score(g_ceph_context, pid, &rbi);
+ ASSERT_TRUE(rc < 0);
+ ASSERT_TRUE(rbi.adjusted_score == 0.);
+ ASSERT_TRUE(rbi.acting_adj_score == 0.);
+ ASSERT_FALSE(rbi.err_msg.empty());
+
+ std::vector<uint> osds;
+ for (uint i = 0 ; i < num_osds ; i++) {
+ osds.push_back(i);
+ }
+
+ // Change primary_affinity of some OSDs to 1 others are 0
+ float fratio = 1. / (float)replica_count;
+ for (int iter = 0 ; iter < 100 ; iter++) { // run the test 100 times
+ // Create random shuffle of OSDs
+ std::random_device seed;
+ std::default_random_engine generator(seed());
+ std::shuffle(osds.begin(), osds.end(), generator);
+ for (uint i = 0 ; i < num_osds ; i++) {
+ if ((float(i + 1) / float(num_osds)) < fratio) {
+ ASSERT_TRUE(osds[i] < num_osds);
+ osdmap.set_primary_affinity(osds[i], CEPH_OSD_MAX_PRIMARY_AFFINITY);
+ rc = osdmap.calc_read_balance_score(g_ceph_context, pid, &rbi);
+
+ ASSERT_TRUE(rc < 0);
+ ASSERT_TRUE(rbi.adjusted_score == 0.);
+ ASSERT_TRUE(rbi.acting_adj_score == 0.);
+ ASSERT_FALSE(rbi.err_msg.empty());
+ }
+ else {
+ if (rc < 0) {
+ ASSERT_TRUE(rbi.adjusted_score == 0.);
+ ASSERT_TRUE(rbi.acting_adj_score == 0.);
+ ASSERT_FALSE(rbi.err_msg.empty());
+ }
+ else {
+ ASSERT_TRUE(score_in_range(rbi.acting_adj_score, i + 1));
+ ASSERT_TRUE(rbi.err_msg.empty());
+ }
+ }
+ }
+ set_primary_affinity_all(0.);
+ }
+ }
+ }
+
+ }
+
+TEST_F(OSDMapTest, ReadBalanceScore2) {
+ std::srand ( unsigned ( std::time(0) ) );
+ uint osd_num = 6 + rand() % 13;
+ set_up_map(osd_num, true);
+ for (int i = 0 ; i < 100 ; i++) { //running 100 random tests
+ uint num_pa_osds = 0;
+ float pa_sum = 0.;
+ OSDMap::read_balance_info_t rbi;
+
+ // set pa for all osds
+ for (uint j = 0 ; j < osd_num ; j++) {
+ uint pa = 1 + rand() % 100;
+ if (pa > 80)
+ pa = 100;
+ if (pa < 20)
+ pa = 0;
+ float fpa = (float)pa / 100.;
+ if (pa > 0) {
+ num_pa_osds++;
+ pa_sum += fpa;
+ }
+ osdmap.set_primary_affinity(j, int(fpa * CEPH_OSD_MAX_PRIMARY_AFFINITY));
+ }
+ float pa_ratio = pa_sum / (float) osd_num;
+
+ // create a pool with the current osdmap configuration
+ OSDMap::Incremental new_pool_inc(osdmap.get_epoch() + 1);
+ new_pool_inc.new_pool_max = osdmap.get_pool_max();
+ new_pool_inc.fsid = osdmap.get_fsid();
+ string pool_name = "rep_pool" + stringify(i);
+ uint64_t new_pid = set_rep_pool(pool_name, new_pool_inc, false);
+ ASSERT_TRUE(new_pid > 0);
+ osdmap.apply_incremental(new_pool_inc);
+
+ // now run the test on the pool.
+ const pg_pool_t *pi = osdmap.get_pg_pool(new_pid);
+ ASSERT_NE(pi, nullptr);
+ ASSERT_TRUE(pi->is_replicated());
+ float fratio = 1. / (float)pi->get_size();
+ auto rc = osdmap.calc_read_balance_score(g_ceph_context, new_pid, &rbi);
+ if (pa_ratio < fratio) {
+ ASSERT_TRUE(rc < 0);
+ ASSERT_FALSE(rbi.err_msg.empty());
+ ASSERT_TRUE(rbi.acting_adj_score == 0.);
+ ASSERT_TRUE(rbi.adjusted_score == 0.);
+ }
+ else {
+ if (rc < 0) {
+ ASSERT_TRUE(rbi.adjusted_score == 0.);
+ ASSERT_TRUE(rbi.acting_adj_score == 0.);
+ ASSERT_FALSE(rbi.err_msg.empty());
+ }
+ else {
+ if (rbi.err_msg.empty()) {
+ ASSERT_TRUE(score_in_range(rbi.acting_adj_score, num_pa_osds));
+ }
+ }
+ }
+
+ }
+ //TODO add ReadBalanceScore3 - with weighted osds.
+
+ }
+
+TEST_F(OSDMapTest, read_balance_small_map) {
+ // Set up a map with 4 OSDs and default pools
+ set_up_map(4);
+
+ const vector<string> test_cases = {"basic", "prim_affinity"};
+ for (const auto & test : test_cases) {
+ if (test == "prim_affinity") {
+ // Make osd.0 off-limits for primaries by giving it prim affinity 0
+ OSDMap::Incremental pending_inc0(osdmap.get_epoch() + 1);
+ pending_inc0.new_primary_affinity[0] = 0;
+ osdmap.apply_incremental(pending_inc0);
+
+ // Ensure osd.0 has no primaries assigned to it
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd, acting_prims_by_osd;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd, &acting_prims_by_osd);
+ ASSERT_TRUE(prim_pgs_by_osd[0].size() == 0);
+ ASSERT_TRUE(acting_prims_by_osd[0].size() == 0);
+ }
+
+ // Make sure capacity is balanced first
+ set<int64_t> only_pools;
+ only_pools.insert(my_rep_pool);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch()+1);
+ osdmap.calc_pg_upmaps(g_ceph_context,
+ 0,
+ 100,
+ only_pools,
+ &pending_inc);
+ osdmap.apply_incremental(pending_inc);
+
+ // Get read balance score before balancing
+ OSDMap::read_balance_info_t rb_info;
+ auto rc = osdmap.calc_read_balance_score(g_ceph_context, my_rep_pool, &rb_info);
+ ASSERT_TRUE(rc >= 0);
+ float read_balance_score_before = rb_info.adjusted_score;
+
+ // Calculate desired prim distributions to verify later
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_2, acting_prims_by_osd_2;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_2, &acting_prims_by_osd_2);
+ vector<uint64_t> osds_to_check;
+ for (const auto & [osd, pgs] : prim_pgs_by_osd_2) {
+ osds_to_check.push_back(osd);
+ }
+ map<uint64_t,float> desired_prim_dist;
+ rc = osdmap.calc_desired_primary_distribution(g_ceph_context, my_rep_pool,
+ osds_to_check, desired_prim_dist);
+ ASSERT_TRUE(rc >= 0);
+
+ // Balance reads
+ OSDMap::Incremental pending_inc_2(osdmap.get_epoch()+1);
+ int num_changes = osdmap.balance_primaries(g_ceph_context, my_rep_pool, &pending_inc_2, osdmap);
+ osdmap.apply_incremental(pending_inc_2);
+
+ if (test == "prim_affinity") {
+ // Ensure osd.0 still has no primaries assigned to it
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_3, acting_prims_by_osd_3;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_3, &acting_prims_by_osd_3);
+ ASSERT_TRUE(prim_pgs_by_osd_3[0].size() == 0);
+ ASSERT_TRUE(acting_prims_by_osd_3[0].size() == 0);
+ }
+
+ // Get read balance score after balancing
+ rc = osdmap.calc_read_balance_score(g_ceph_context, my_rep_pool, &rb_info);
+ ASSERT_TRUE(rc >= 0);
+ float read_balance_score_after = rb_info.adjusted_score;
+
+ // Ensure the score hasn't gotten worse
+ ASSERT_TRUE(read_balance_score_after <= read_balance_score_before);
+
+ // Check for improvements
+ if (num_changes > 0) {
+ ASSERT_TRUE(read_balance_score_after < read_balance_score_before);
+
+ // Check num primaries for each OSD is within range
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_4, acting_prims_by_osd_4;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_4, &acting_prims_by_osd_4);
+ for (const auto & [osd, primaries] : prim_pgs_by_osd_4) {
+ ASSERT_TRUE(primaries.size() >= floor(desired_prim_dist[osd] - 1));
+ ASSERT_TRUE(primaries.size() <= ceil(desired_prim_dist[osd] + 1));
+ }
+ }
+ }
+}
+
+TEST_F(OSDMapTest, read_balance_large_map) {
+ // Set up a map with 60 OSDs and default pools
+ set_up_map(60);
+
+ const vector<string> test_cases = {"basic", "prim_affinity"};
+ for (const auto & test : test_cases) {
+ if (test == "prim_affinity") {
+ // Make osd.0 off-limits for primaries by giving it prim affinity 0
+ OSDMap::Incremental pending_inc0(osdmap.get_epoch() + 1);
+ pending_inc0.new_primary_affinity[0] = 0;
+ osdmap.apply_incremental(pending_inc0);
+
+ // Ensure osd.0 has no primaries assigned to it
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd, acting_prims_by_osd;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd, &acting_prims_by_osd);
+ ASSERT_TRUE(prim_pgs_by_osd[0].size() == 0);
+ ASSERT_TRUE(acting_prims_by_osd[0].size() == 0);
+ }
+
+ // Make sure capacity is balanced first
+ set<int64_t> only_pools;
+ only_pools.insert(my_rep_pool);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch()+1);
+ osdmap.calc_pg_upmaps(g_ceph_context,
+ 0,
+ 100,
+ only_pools,
+ &pending_inc);
+ osdmap.apply_incremental(pending_inc);
+
+ // Get read balance score before balancing
+ OSDMap::read_balance_info_t rb_info;
+ auto rc = osdmap.calc_read_balance_score(g_ceph_context, my_rep_pool, &rb_info);
+ ASSERT_TRUE(rc >= 0);
+ float read_balance_score_before = rb_info.adjusted_score;
+
+ // Calculate desired prim distributions to verify later
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_2, acting_prims_by_osd_2;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_2, &acting_prims_by_osd_2);
+ vector<uint64_t> osds_to_check;
+ for (auto [osd, pgs] : prim_pgs_by_osd_2) {
+ osds_to_check.push_back(osd);
+ }
+ map<uint64_t,float> desired_prim_dist;
+ rc = osdmap.calc_desired_primary_distribution(g_ceph_context, my_rep_pool,
+ osds_to_check, desired_prim_dist);
+ ASSERT_TRUE(rc >= 0);
+
+ // Balance reads
+ OSDMap::Incremental pending_inc_2(osdmap.get_epoch()+1);
+ int num_changes = osdmap.balance_primaries(g_ceph_context, my_rep_pool, &pending_inc_2, osdmap);
+ osdmap.apply_incremental(pending_inc_2);
+
+ if (test == "prim_affinity") {
+ // Ensure osd.0 still has no primaries assigned to it
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_3, acting_prims_by_osd_3;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_3, &acting_prims_by_osd_3);
+ ASSERT_TRUE(prim_pgs_by_osd_3[0].size() == 0);
+ ASSERT_TRUE(acting_prims_by_osd_3[0].size() == 0);
+ }
+
+ // Get read balance score after balancing
+ rc = osdmap.calc_read_balance_score(g_ceph_context, my_rep_pool, &rb_info);
+ ASSERT_TRUE(rc >= 0);
+ float read_balance_score_after = rb_info.adjusted_score;
+
+ // Ensure the score hasn't gotten worse
+ ASSERT_TRUE(read_balance_score_after <= read_balance_score_before);
+
+ // Check for improvements
+ if (num_changes > 0) {
+ ASSERT_TRUE(read_balance_score_after < read_balance_score_before);
+
+ // Check num primaries for each OSD is within range
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_4, acting_prims_by_osd_4;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_4, &acting_prims_by_osd_4);
+ for (const auto & [osd, primaries] : prim_pgs_by_osd_4) {
+ ASSERT_TRUE(primaries.size() >= floor(desired_prim_dist[osd] - 1));
+ ASSERT_TRUE(primaries.size() <= ceil(desired_prim_dist[osd] + 1));
+ }
+ }
+ }
+}
+
+TEST_F(OSDMapTest, read_balance_random_map) {
+ // Set up map with random number of OSDs
+ std::srand ( unsigned ( std::time(0) ) );
+ uint num_osds = 3 + (rand() % 10);
+ ASSERT_TRUE(num_osds >= 3);
+ set_up_map(num_osds);
+
+ const vector<string> test_cases = {"basic", "prim_affinity"};
+ for (const auto & test : test_cases) {
+ uint rand_osd = rand() % num_osds;
+ if (test == "prim_affinity") {
+ // Make a random OSD off-limits for primaries by giving it prim affinity 0
+ ASSERT_TRUE(rand_osd < num_osds);
+ OSDMap::Incremental pending_inc0(osdmap.get_epoch() + 1);
+ pending_inc0.new_primary_affinity[rand_osd] = 0;
+ osdmap.apply_incremental(pending_inc0);
+
+ // Ensure the random OSD has no primaries assigned to it
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd, acting_prims_by_osd;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd, &acting_prims_by_osd);
+ ASSERT_TRUE(prim_pgs_by_osd[rand_osd].size() == 0);
+ ASSERT_TRUE(acting_prims_by_osd[rand_osd].size() == 0);
+ }
+
+ // Make sure capacity is balanced first
+ set<int64_t> only_pools;
+ only_pools.insert(my_rep_pool);
+ OSDMap::Incremental pending_inc(osdmap.get_epoch()+1);
+ osdmap.calc_pg_upmaps(g_ceph_context,
+ 0,
+ 100,
+ only_pools,
+ &pending_inc);
+ osdmap.apply_incremental(pending_inc);
+
+ // Get read balance score before balancing
+ OSDMap::read_balance_info_t rb_info;
+ auto rc = osdmap.calc_read_balance_score(g_ceph_context, my_rep_pool, &rb_info);
+ ASSERT_TRUE(rc >= 0);
+ float read_balance_score_before = rb_info.adjusted_score;
+
+ // Calculate desired prim distributions to verify later
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_2, acting_prims_by_osd_2;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_2, &acting_prims_by_osd_2);
+ vector<uint64_t> osds_to_check;
+ for (const auto & [osd, pgs] : prim_pgs_by_osd_2) {
+ osds_to_check.push_back(osd);
+ }
+ map<uint64_t,float> desired_prim_dist;
+ rc = osdmap.calc_desired_primary_distribution(g_ceph_context, my_rep_pool,
+ osds_to_check, desired_prim_dist);
+ ASSERT_TRUE(rc >= 0);
+
+ // Balance reads
+ OSDMap::Incremental pending_inc_2(osdmap.get_epoch()+1);
+ int num_changes = osdmap.balance_primaries(g_ceph_context, my_rep_pool, &pending_inc_2, osdmap);
+ osdmap.apply_incremental(pending_inc_2);
+
+ if (test == "prim_affinity") {
+ // Ensure the random OSD still has no primaries assigned to it
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_3, acting_prims_by_osd_3;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_3, &acting_prims_by_osd_3);
+ ASSERT_TRUE(prim_pgs_by_osd_3[rand_osd].size() == 0);
+ ASSERT_TRUE(acting_prims_by_osd_3[rand_osd].size() == 0);
+ }
+
+ // Get read balance score after balancing
+ rc = osdmap.calc_read_balance_score(g_ceph_context, my_rep_pool, &rb_info);
+ ASSERT_TRUE(rc >= 0);
+ float read_balance_score_after = rb_info.adjusted_score;
+
+ // Ensure the score hasn't gotten worse
+ ASSERT_TRUE(read_balance_score_after <= read_balance_score_before);
+
+ // Check for improvements
+ if (num_changes > 0) {
+ ASSERT_TRUE(read_balance_score_after < read_balance_score_before);
+
+ // Check num primaries for each OSD is within range
+ map<uint64_t,set<pg_t>> prim_pgs_by_osd_4, acting_prims_by_osd_4;
+ osdmap.get_pgs_by_osd(g_ceph_context, my_rep_pool, &prim_pgs_by_osd_4, &acting_prims_by_osd_4);
+ for (auto [osd, primaries] : prim_pgs_by_osd_4) {
+ ASSERT_TRUE(primaries.size() >= floor(desired_prim_dist[osd] - 1));
+ ASSERT_TRUE(primaries.size() <= ceil(desired_prim_dist[osd] + 1));
+ }
+ for (auto [osd, primaries] : prim_pgs_by_osd_4) {
+ ASSERT_TRUE(primaries.size() >= floor(desired_prim_dist[osd] - 1));
+ ASSERT_TRUE(primaries.size() <= ceil(desired_prim_dist[osd] + 1));
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ OSDMap,
+ OSDMapTest,
+ ::testing::Values(
+ std::make_pair<int, int>(0, 1), // chooseleaf firstn 0 host
+ std::make_pair<int, int>(3, 1), // chooseleaf firstn 3 host
+ std::make_pair<int, int>(0, 0), // chooseleaf firstn 0 osd
+ std::make_pair<int, int>(3, 0) // chooseleaf firstn 3 osd
+ )
+);
diff --git a/src/test/osd/TestOSDScrub.cc b/src/test/osd/TestOSDScrub.cc
new file mode 100644
index 000000000..4c6d4ccee
--- /dev/null
+++ b/src/test/osd/TestOSDScrub.cc
@@ -0,0 +1,203 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <gtest/gtest.h>
+#include "common/async/context_pool.h"
+#include "osd/OSD.h"
+#include "os/ObjectStore.h"
+#include "mon/MonClient.h"
+#include "common/ceph_argparse.h"
+#include "msg/Messenger.h"
+
+class TestOSDScrub: public OSD {
+
+public:
+ TestOSDScrub(CephContext *cct_,
+ std::unique_ptr<ObjectStore> store_,
+ int id,
+ Messenger *internal,
+ Messenger *external,
+ Messenger *hb_front_client,
+ Messenger *hb_back_client,
+ Messenger *hb_front_server,
+ Messenger *hb_back_server,
+ Messenger *osdc_messenger,
+ MonClient *mc, const std::string &dev, const std::string &jdev,
+ ceph::async::io_context_pool& ictx) :
+ OSD(cct_, std::move(store_), id, internal, external,
+ hb_front_client, hb_back_client,
+ hb_front_server, hb_back_server,
+ osdc_messenger, mc, dev, jdev, ictx)
+ {
+ }
+
+ bool scrub_time_permit(utime_t now) {
+ return service.get_scrub_services().scrub_time_permit(now);
+ }
+};
+
+TEST(TestOSDScrub, scrub_time_permit) {
+ ceph::async::io_context_pool icp(1);
+ std::unique_ptr<ObjectStore> store = ObjectStore::create(g_ceph_context,
+ g_conf()->osd_objectstore,
+ g_conf()->osd_data,
+ g_conf()->osd_journal);
+ std::string cluster_msgr_type = g_conf()->ms_cluster_type.empty() ? g_conf().get_val<std::string>("ms_type") : g_conf()->ms_cluster_type;
+ Messenger *ms = Messenger::create(g_ceph_context, cluster_msgr_type,
+ entity_name_t::OSD(0), "make_checker",
+ getpid());
+ ms->set_cluster_protocol(CEPH_OSD_PROTOCOL);
+ ms->set_default_policy(Messenger::Policy::stateless_server(0));
+ ms->bind(g_conf()->public_addr);
+ MonClient mc(g_ceph_context, icp);
+ mc.build_initial_monmap();
+ TestOSDScrub* osd = new TestOSDScrub(g_ceph_context, std::move(store), 0, ms, ms, ms, ms, ms, ms, ms, &mc, "", "", icp);
+
+ // These are now invalid
+ int err = g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "24");
+ ASSERT_TRUE(err < 0);
+ //GTEST_LOG_(INFO) << " osd_scrub_begin_hour = " << g_ceph_context->_conf.get_val<int64_t>("osd_scrub_begin_hour");
+
+ err = g_ceph_context->_conf.set_val("osd_scrub_end_hour", "24");
+ ASSERT_TRUE(err < 0);
+ //GTEST_LOG_(INFO) << " osd_scrub_end_hour = " << g_ceph_context->_conf.get_val<int64_t>("osd_scrub_end_hour");
+
+ err = g_ceph_context->_conf.set_val("osd_scrub_begin_week_day", "7");
+ ASSERT_TRUE(err < 0);
+ //GTEST_LOG_(INFO) << " osd_scrub_begin_week_day = " << g_ceph_context->_conf.get_val<int64_t>("osd_scrub_begin_week_day");
+
+ err = g_ceph_context->_conf.set_val("osd_scrub_end_week_day", "7");
+ ASSERT_TRUE(err < 0);
+ //GTEST_LOG_(INFO) << " osd_scrub_end_week_day = " << g_ceph_context->_conf.get_val<int64_t>("osd_scrub_end_week_day");
+
+ // Test all day
+ g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "0");
+ g_ceph_context->_conf.set_val("osd_scrub_end_hour", "0");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ tm tm;
+ tm.tm_isdst = -1;
+ strptime("2015-01-16 12:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ utime_t now = utime_t(mktime(&tm), 0);
+ bool ret = osd->scrub_time_permit(now);
+ ASSERT_TRUE(ret);
+
+ g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "20");
+ g_ceph_context->_conf.set_val("osd_scrub_end_hour", "07");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 01:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_TRUE(ret);
+
+ g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "20");
+ g_ceph_context->_conf.set_val("osd_scrub_end_hour", "07");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 20:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_TRUE(ret);
+
+ g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "20");
+ g_ceph_context->_conf.set_val("osd_scrub_end_hour", "07");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 08:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_FALSE(ret);
+
+ g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "01");
+ g_ceph_context->_conf.set_val("osd_scrub_end_hour", "07");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 20:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_FALSE(ret);
+
+ g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "01");
+ g_ceph_context->_conf.set_val("osd_scrub_end_hour", "07");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 00:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_FALSE(ret);
+
+ g_ceph_context->_conf.set_val("osd_scrub_begin_hour", "01");
+ g_ceph_context->_conf.set_val("osd_scrub_end_hour", "07");
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 04:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_TRUE(ret);
+
+ // Sun = 0, Mon = 1, Tue = 2, Wed = 3, Thu = 4m, Fri = 5, Sat = 6
+ // Jan 16, 2015 is a Friday (5)
+ // every day
+ g_ceph_context->_conf.set_val("osd_scrub_begin_week day", "0"); // inclusive
+ g_ceph_context->_conf.set_val("osd_scrub_end_week_day", "0"); // not inclusive
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 04:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_TRUE(ret);
+
+ // test Sun - Thu
+ g_ceph_context->_conf.set_val("osd_scrub_begin_week day", "0"); // inclusive
+ g_ceph_context->_conf.set_val("osd_scrub_end_week_day", "5"); // not inclusive
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 04:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_FALSE(ret);
+
+ // test Fri - Sat
+ g_ceph_context->_conf.set_val("osd_scrub_begin_week day", "5"); // inclusive
+ g_ceph_context->_conf.set_val("osd_scrub_end_week_day", "0"); // not inclusive
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-16 04:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_TRUE(ret);
+
+ // Jan 14, 2015 is a Wednesday (3)
+ // test Tue - Fri
+ g_ceph_context->_conf.set_val("osd_scrub_begin_week day", "2"); // inclusive
+ g_ceph_context->_conf.set_val("osd_scrub_end_week_day", "6"); // not inclusive
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-14 04:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_TRUE(ret);
+
+ // Test Sat - Sun
+ g_ceph_context->_conf.set_val("osd_scrub_begin_week day", "6"); // inclusive
+ g_ceph_context->_conf.set_val("osd_scrub_end_week_day", "1"); // not inclusive
+ g_ceph_context->_conf.apply_changes(nullptr);
+ strptime("2015-01-14 04:05:13", "%Y-%m-%d %H:%M:%S", &tm);
+ now = utime_t(mktime(&tm), 0);
+ ret = osd->scrub_time_permit(now);
+ ASSERT_FALSE(ret);
+}
+
+// Local Variables:
+// compile-command: "cd ../.. ; make unittest_osdscrub ; ./unittest_osdscrub --log-to-stderr=true --debug-osd=20 # --gtest_filter=*.* "
+// End:
diff --git a/src/test/osd/TestOpStat.cc b/src/test/osd/TestOpStat.cc
new file mode 100644
index 000000000..eb13e1d55
--- /dev/null
+++ b/src/test/osd/TestOpStat.cc
@@ -0,0 +1,58 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include <list>
+#include <map>
+#include <set>
+#include "RadosModel.h"
+#include "TestOpStat.h"
+
+void TestOpStat::begin(TestOp *in) {
+ std::lock_guard l{stat_lock};
+ stats[in->getType()].begin(in);
+}
+
+void TestOpStat::end(TestOp *in) {
+ std::lock_guard l{stat_lock};
+ stats[in->getType()].end(in);
+}
+
+void TestOpStat::TypeStatus::export_latencies(std::map<double,uint64_t> &in) const
+{
+ auto i = in.begin();
+ auto j = latencies.begin();
+ int count = 0;
+ while (j != latencies.end() && i != in.end()) {
+ count++;
+ if ((((double)count)/((double)latencies.size())) * 100 >= i->first) {
+ i->second = *j;
+ ++i;
+ }
+ ++j;
+ }
+}
+
+std::ostream & operator<<(std::ostream &out, const TestOpStat &rhs)
+{
+ std::lock_guard l{rhs.stat_lock};
+ for (auto i = rhs.stats.begin();
+ i != rhs.stats.end();
+ ++i) {
+ std::map<double,uint64_t> latency;
+ latency[10] = 0;
+ latency[50] = 0;
+ latency[90] = 0;
+ latency[99] = 0;
+ i->second.export_latencies(latency);
+
+ out << i->first << " latency: " << std::endl;
+ for (auto j = latency.begin();
+ j != latency.end();
+ ++j) {
+ if (j->second == 0) break;
+ out << "\t" << j->first << "th percentile: "
+ << j->second / 1000 << "ms" << std::endl;
+ }
+ }
+ return out;
+}
diff --git a/src/test/osd/TestOpStat.h b/src/test/osd/TestOpStat.h
new file mode 100644
index 000000000..2c680558f
--- /dev/null
+++ b/src/test/osd/TestOpStat.h
@@ -0,0 +1,53 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "include/rados/librados.hpp"
+
+#ifndef TESTOPSTAT_H
+#define TESTOPSTAT_H
+
+class TestOp;
+
+class TestOpStat {
+public:
+ mutable ceph::mutex stat_lock = ceph::make_mutex("TestOpStat lock");
+
+ TestOpStat() = default;
+
+ static uint64_t gettime()
+ {
+ timeval t;
+ gettimeofday(&t,0);
+ return (1000000*t.tv_sec) + t.tv_usec;
+ }
+
+ class TypeStatus {
+ public:
+ std::map<TestOp*,uint64_t> inflight;
+ std::multiset<uint64_t> latencies;
+ void begin(TestOp *in)
+ {
+ ceph_assert(!inflight.count(in));
+ inflight[in] = gettime();
+ }
+
+ void end(TestOp *in)
+ {
+ ceph_assert(inflight.count(in));
+ uint64_t curtime = gettime();
+ latencies.insert(curtime - inflight[in]);
+ inflight.erase(in);
+ }
+
+ void export_latencies(std::map<double,uint64_t> &in) const;
+ };
+ std::map<std::string,TypeStatus> stats;
+
+ void begin(TestOp *in);
+ void end(TestOp *in);
+ friend std::ostream & operator<<(std::ostream &, const TestOpStat &);
+};
+
+std::ostream & operator<<(std::ostream &out, const TestOpStat &rhs);
+
+#endif
diff --git a/src/test/osd/TestPGLog.cc b/src/test/osd/TestPGLog.cc
new file mode 100644
index 000000000..1fff469d1
--- /dev/null
+++ b/src/test/osd/TestPGLog.cc
@@ -0,0 +1,3249 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include "gtest/gtest.h"
+#include "osd/PGLog.h"
+#include "osd/OSDMap.h"
+#include "include/coredumpctl.h"
+#include "../objectstore/store_test_fixture.h"
+
+using namespace std;
+
+struct PGLogTestBase {
+ static hobject_t mk_obj(unsigned id) {
+ hobject_t hoid;
+ stringstream ss;
+ ss << "obj_" << id;
+ hoid.oid = ss.str();
+ hoid.set_hash(id);
+ hoid.pool = 1;
+ return hoid;
+ }
+ static eversion_t mk_evt(unsigned ep, unsigned v) {
+ return eversion_t(ep, v);
+ }
+ static pg_log_entry_t mk_ple_mod(
+ const hobject_t &hoid, eversion_t v, eversion_t pv, osd_reqid_t reqid) {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+ e.op = pg_log_entry_t::MODIFY;
+ e.soid = hoid;
+ e.version = v;
+ e.prior_version = pv;
+ e.reqid = reqid;
+ return e;
+ }
+ static pg_log_entry_t mk_ple_dt(
+ const hobject_t &hoid, eversion_t v, eversion_t pv, osd_reqid_t reqid) {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+ e.op = pg_log_entry_t::DELETE;
+ e.soid = hoid;
+ e.version = v;
+ e.prior_version = pv;
+ e.reqid = reqid;
+ return e;
+ }
+ static pg_log_entry_t mk_ple_ldt(
+ const hobject_t &hoid, eversion_t v, eversion_t pv) {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+ e.op = pg_log_entry_t::LOST_DELETE;
+ e.soid = hoid;
+ e.version = v;
+ e.prior_version = pv;
+ return e;
+ }
+ static pg_log_entry_t mk_ple_mod_rb(
+ const hobject_t &hoid, eversion_t v, eversion_t pv, osd_reqid_t reqid) {
+ pg_log_entry_t e;
+ e.op = pg_log_entry_t::MODIFY;
+ e.soid = hoid;
+ e.version = v;
+ e.prior_version = pv;
+ e.reqid = reqid;
+ return e;
+ }
+ static pg_log_entry_t mk_ple_dt_rb(
+ const hobject_t &hoid, eversion_t v, eversion_t pv, osd_reqid_t reqid) {
+ pg_log_entry_t e;
+ e.op = pg_log_entry_t::DELETE;
+ e.soid = hoid;
+ e.version = v;
+ e.prior_version = pv;
+ e.reqid = reqid;
+ return e;
+ }
+ static pg_log_entry_t mk_ple_err(
+ const hobject_t &hoid, eversion_t v, osd_reqid_t reqid) {
+ pg_log_entry_t e;
+ e.op = pg_log_entry_t::ERROR;
+ e.soid = hoid;
+ e.version = v;
+ e.prior_version = eversion_t(0, 0);
+ e.reqid = reqid;
+ return e;
+ }
+ static pg_log_entry_t mk_ple_mod(
+ const hobject_t &hoid, eversion_t v, eversion_t pv) {
+ return mk_ple_mod(hoid, v, pv, osd_reqid_t());
+ }
+ static pg_log_entry_t mk_ple_dt(
+ const hobject_t &hoid, eversion_t v, eversion_t pv) {
+ return mk_ple_dt(hoid, v, pv, osd_reqid_t());
+ }
+ static pg_log_entry_t mk_ple_mod_rb(
+ const hobject_t &hoid, eversion_t v, eversion_t pv) {
+ return mk_ple_mod_rb(hoid, v, pv, osd_reqid_t());
+ }
+ static pg_log_entry_t mk_ple_dt_rb(
+ const hobject_t &hoid, eversion_t v, eversion_t pv) {
+ return mk_ple_dt_rb(hoid, v, pv, osd_reqid_t());
+ }
+ static pg_log_entry_t mk_ple_err(
+ const hobject_t &hoid, eversion_t v) {
+ return mk_ple_err(hoid, v, osd_reqid_t());
+ }
+}; // PGLogTestBase
+
+
+class PGLogTest : virtual public ::testing::Test, protected PGLog, public PGLogTestBase {
+public:
+ PGLogTest() : PGLog(g_ceph_context) {}
+ void SetUp() override {
+ missing.may_include_deletes = true;
+ }
+
+#include "common/ceph_context.h"
+#include "common/config.h"
+
+ void TearDown() override {
+ clear();
+ }
+
+
+ struct TestCase {
+ list<pg_log_entry_t> base;
+ list<pg_log_entry_t> auth;
+ list<pg_log_entry_t> div;
+
+ pg_missing_t init;
+ pg_missing_t final;
+
+ set<hobject_t> toremove;
+ list<pg_log_entry_t> torollback;
+ bool deletes_during_peering;
+
+ private:
+ IndexedLog fullauth;
+ IndexedLog fulldiv;
+ pg_info_t authinfo;
+ pg_info_t divinfo;
+ public:
+ TestCase() : deletes_during_peering(false) {}
+ void setup() {
+ init.may_include_deletes = !deletes_during_peering;
+ final.may_include_deletes = !deletes_during_peering;
+ fullauth.log.insert(fullauth.log.end(), base.begin(), base.end());
+ fullauth.log.insert(fullauth.log.end(), auth.begin(), auth.end());
+ fulldiv.log.insert(fulldiv.log.end(), base.begin(), base.end());
+ fulldiv.log.insert(fulldiv.log.end(), div.begin(), div.end());
+
+ fullauth.head = authinfo.last_update = fullauth.log.rbegin()->version;
+ authinfo.last_complete = fullauth.log.rbegin()->version;
+ authinfo.log_tail = fullauth.log.begin()->version;
+ authinfo.log_tail.version--;
+ fullauth.tail = authinfo.log_tail;
+ authinfo.last_backfill = hobject_t::get_max();
+
+ fulldiv.head = divinfo.last_update = fulldiv.log.rbegin()->version;
+ divinfo.last_complete = eversion_t();
+ divinfo.log_tail = fulldiv.log.begin()->version;
+ divinfo.log_tail.version--;
+ fulldiv.tail = divinfo.log_tail;
+ divinfo.last_backfill = hobject_t::get_max();
+
+ if (init.get_items().empty()) {
+ divinfo.last_complete = divinfo.last_update;
+ } else {
+ eversion_t fmissing = init.get_items().at(init.get_rmissing().begin()->second).need;
+ for (list<pg_log_entry_t>::const_iterator i = fulldiv.log.begin();
+ i != fulldiv.log.end();
+ ++i) {
+ if (i->version < fmissing)
+ divinfo.last_complete = i->version;
+ else
+ break;
+ }
+ }
+
+ fullauth.index();
+ fulldiv.index();
+ }
+ void set_div_bounds(eversion_t head, eversion_t tail) {
+ fulldiv.tail = divinfo.log_tail = tail;
+ fulldiv.head = divinfo.last_update = head;
+ }
+ void set_auth_bounds(eversion_t head, eversion_t tail) {
+ fullauth.tail = authinfo.log_tail = tail;
+ fullauth.head = authinfo.last_update = head;
+ }
+ const IndexedLog &get_fullauth() const { return fullauth; }
+ const IndexedLog &get_fulldiv() const { return fulldiv; }
+ const pg_info_t &get_authinfo() const { return authinfo; }
+ const pg_info_t &get_divinfo() const { return divinfo; }
+ }; // struct TestCase
+
+ struct LogHandler : public PGLog::LogEntryHandler {
+ set<hobject_t> removed;
+ list<pg_log_entry_t> rolledback;
+
+ void rollback(
+ const pg_log_entry_t &entry) override {
+ rolledback.push_back(entry);
+ }
+ void rollforward(
+ const pg_log_entry_t &entry) override {}
+ void remove(
+ const hobject_t &hoid) override {
+ removed.insert(hoid);
+ }
+ void try_stash(const hobject_t &, version_t) override {
+ // lost/unfound cases are not tested yet
+ }
+ void trim(
+ const pg_log_entry_t &entry) override {}
+ };
+
+ template <typename missing_t>
+ void verify_missing(
+ const TestCase &tcase,
+ const missing_t &missing) {
+ ASSERT_EQ(tcase.final.get_items().size(), missing.get_items().size());
+ for (auto i = missing.get_items().begin();
+ i != missing.get_items().end();
+ ++i) {
+ EXPECT_TRUE(tcase.final.get_items().count(i->first));
+ EXPECT_EQ(tcase.final.get_items().find(i->first)->second.need, i->second.need);
+ EXPECT_EQ(tcase.final.get_items().find(i->first)->second.have, i->second.have);
+ }
+ bool correct = missing.debug_verify_from_init(tcase.init, &(std::cout));
+ ASSERT_TRUE(correct);
+ }
+
+ void verify_sideeffects(
+ const TestCase &tcase,
+ const LogHandler &handler) {
+ ASSERT_EQ(tcase.toremove.size(), handler.removed.size());
+ ASSERT_EQ(tcase.torollback.size(), handler.rolledback.size());
+
+ {
+ list<pg_log_entry_t>::const_iterator titer = tcase.torollback.begin();
+ list<pg_log_entry_t>::const_iterator hiter = handler.rolledback.begin();
+ for (; titer != tcase.torollback.end(); ++titer, ++hiter) {
+ EXPECT_EQ(titer->version, hiter->version);
+ }
+ }
+
+ {
+ set<hobject_t>::const_iterator titer = tcase.toremove.begin();
+ set<hobject_t>::const_iterator hiter = handler.removed.begin();
+ for (; titer != tcase.toremove.end(); ++titer, ++hiter) {
+ EXPECT_EQ(*titer, *hiter);
+ }
+ }
+ }
+
+ void test_merge_log(const TestCase &tcase) {
+ clear();
+ log = tcase.get_fulldiv();
+ pg_info_t info = tcase.get_divinfo();
+
+ missing = tcase.init;
+ missing.flush();
+
+ IndexedLog olog;
+ olog = tcase.get_fullauth();
+ pg_info_t oinfo = tcase.get_authinfo();
+
+ LogHandler h;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+ merge_log(
+ oinfo, std::move(olog), pg_shard_t(1, shard_id_t(0)), info,
+ &h, dirty_info, dirty_big_info);
+
+ ASSERT_EQ(info.last_update, oinfo.last_update);
+ verify_missing(tcase, missing);
+ verify_sideeffects(tcase, h);
+ }
+
+ void test_proc_replica_log(const TestCase &tcase) {
+ clear();
+ log = tcase.get_fullauth();
+ pg_info_t info = tcase.get_authinfo();
+
+ pg_missing_t omissing = tcase.init;
+
+ IndexedLog olog;
+ olog = tcase.get_fulldiv();
+ pg_info_t oinfo = tcase.get_divinfo();
+
+ proc_replica_log(
+ oinfo, olog, omissing, pg_shard_t(1, shard_id_t(0)));
+
+ ceph_assert(oinfo.last_update >= log.tail);
+
+ if (!tcase.base.empty()) {
+ ASSERT_EQ(tcase.base.rbegin()->version, oinfo.last_update);
+ }
+
+ for (list<pg_log_entry_t>::const_iterator i = tcase.auth.begin();
+ i != tcase.auth.end();
+ ++i) {
+ if (i->version > oinfo.last_update) {
+ if (i->is_delete() && tcase.deletes_during_peering) {
+ omissing.rm(i->soid, i->version);
+ } else {
+ omissing.add_next_event(*i);
+ }
+ }
+ }
+ verify_missing(tcase, omissing);
+ } // test_proc_replica_log
+
+ void run_test_case(const TestCase &tcase) {
+ test_merge_log(tcase);
+ test_proc_replica_log(tcase);
+ }
+}; // class PGLogTest
+
+struct TestHandler : public PGLog::LogEntryHandler {
+ list<hobject_t> &removed;
+ explicit TestHandler(list<hobject_t> &removed) : removed(removed) {}
+
+ void rollback(
+ const pg_log_entry_t &entry) override {}
+ void rollforward(
+ const pg_log_entry_t &entry) override {}
+ void remove(
+ const hobject_t &hoid) override {
+ removed.push_back(hoid);
+ }
+ void cant_rollback(const pg_log_entry_t &entry) {}
+ void try_stash(const hobject_t &, version_t) override {
+ // lost/unfound cases are not tested yet
+ }
+ void trim(
+ const pg_log_entry_t &entry) override {}
+};
+
+TEST_F(PGLogTest, rewind_divergent_log) {
+ /* +----------------+
+ | log |
+ +--------+-------+
+ | |object |
+ |version | hash |
+ | | |
+ tail > (1,1) | x5 |
+ | | |
+ | | |
+ | (1,4) | x9 < newhead
+ | MODIFY | |
+ | | |
+ head > (1,5) | x9 |
+ | DELETE | |
+ | | |
+ +--------+-------+
+
+ */
+ {
+ clear();
+
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ hobject_t divergent_object;
+ eversion_t divergent_version;
+ eversion_t newhead;
+
+ hobject_t divergent;
+ divergent.set_hash(0x9);
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = newhead = eversion_t(1, 4);
+ e.soid = divergent;
+ e.op = pg_log_entry_t::MODIFY;
+ log.log.push_back(e);
+ e.version = divergent_version = eversion_t(1, 5);
+ e.prior_version = eversion_t(1, 4);
+ e.soid = divergent;
+ divergent_object = e.soid;
+ e.op = pg_log_entry_t::DELETE;
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ info.last_update = log.head;
+ info.last_complete = log.head;
+ }
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(3U, log.log.size());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_EQ(log.head, info.last_update);
+ EXPECT_EQ(log.head, info.last_complete);
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ rewind_divergent_log(newhead, info, &h,
+ dirty_info, dirty_big_info);
+
+ EXPECT_TRUE(log.objects.count(divergent));
+ EXPECT_TRUE(missing.is_missing(divergent_object));
+ EXPECT_EQ(1U, log.objects.count(divergent_object));
+ EXPECT_EQ(2U, log.log.size());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_EQ(newhead, info.last_update);
+ EXPECT_EQ(newhead, info.last_complete);
+ EXPECT_TRUE(is_dirty());
+ EXPECT_TRUE(dirty_info);
+ EXPECT_TRUE(dirty_big_info);
+ }
+
+ /* +----------------+
+ | log |
+ +--------+-------+
+ | |object |
+ |version | hash |
+ | | |
+ tail > (1,1) | NULL |
+ | | |
+ | (1,4) | NULL < newhead
+ | | |
+ head > (1,5) | x9 |
+ | | |
+ +--------+-------+
+
+ */
+ {
+ clear();
+
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ hobject_t divergent_object;
+ eversion_t divergent_version;
+ eversion_t prior_version;
+ eversion_t newhead;
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ info.log_tail = log.tail = eversion_t(1, 1);
+ newhead = eversion_t(1, 3);
+ e.version = divergent_version = eversion_t(1, 5);
+ e.soid.set_hash(0x9);
+ divergent_object = e.soid;
+ e.op = pg_log_entry_t::DELETE;
+ e.prior_version = prior_version = eversion_t(0, 2);
+ log.log.push_back(e);
+ log.head = e.version;
+ }
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.log.size());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ rewind_divergent_log(newhead, info, &h,
+ dirty_info, dirty_big_info);
+
+ EXPECT_TRUE(missing.is_missing(divergent_object));
+ EXPECT_EQ(0U, log.objects.count(divergent_object));
+ EXPECT_TRUE(log.empty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(is_dirty());
+ EXPECT_TRUE(dirty_info);
+ EXPECT_TRUE(dirty_big_info);
+ }
+
+ // Test for 13965
+ {
+ clear();
+
+ list<hobject_t> remove_snap;
+ pg_info_t info;
+ info.log_tail = log.tail = eversion_t(1, 5);
+ info.last_update = eversion_t(1, 6);
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+ e.version = eversion_t(1, 5);
+ e.soid.set_hash(0x9);
+ add(e);
+ }
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+ e.version = eversion_t(1, 6);
+ e.soid.set_hash(0x10);
+ add(e);
+ }
+ TestHandler h(remove_snap);
+ roll_forward_to(eversion_t(1, 6), &h);
+ rewind_divergent_log(eversion_t(1, 5), info, &h,
+ dirty_info, dirty_big_info);
+ pg_log_t log;
+ reset_backfill_claim_log(log, &h);
+ }
+}
+
+TEST_F(PGLogTest, merge_old_entry) {
+ // entries > last_backfill are silently ignored
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ info.last_backfill = hobject_t();
+ info.last_backfill.set_hash(100);
+ oe.soid.set_hash(2);
+ ASSERT_GT(oe.soid, info.last_backfill);
+
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_TRUE(log.empty());
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_TRUE(log.empty());
+ }
+
+ // the new entry (from the logs) has a version that is higher than
+ // the old entry (from the log entry given in argument) : do
+ // nothing and return false
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ pg_log_entry_t ne;
+ ne.mark_unrollbackable();
+ ne.version = eversion_t(2,1);
+ log.add(ne);
+
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.log.size());
+ EXPECT_EQ(ne.version, log.log.front().version);
+
+ // the newer entry ( from the logs ) can be DELETE
+ {
+ log.log.front().op = pg_log_entry_t::DELETE;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ oe.version = eversion_t(1,1);
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+ }
+
+ // if the newer entry is not DELETE, the object must be in missing
+ {
+ pg_log_entry_t &ne = log.log.front();
+ ne.op = pg_log_entry_t::MODIFY;
+ missing.add_next_event(ne);
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ oe.version = eversion_t(1,1);
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ missing.rm(ne.soid, ne.version);
+ }
+
+ missing.flush();
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.log.size());
+ EXPECT_EQ(ne.version, log.log.front().version);
+
+ }
+
+ // the new entry (from the logs) has a version that is lower than
+ // the old entry (from the log entry given in argument) and
+ // old and new are delete : do nothing and return false
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ pg_log_entry_t ne;
+ ne.mark_unrollbackable();
+ ne.version = eversion_t(1,1);
+ ne.op = pg_log_entry_t::DELETE;
+ log.add(ne);
+
+ oe.version = eversion_t(2,1);
+ oe.op = pg_log_entry_t::DELETE;
+
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.log.size());
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.log.size());
+ }
+
+ // the new entry (from the logs) has a version that is lower than
+ // the old entry (from the log entry given in argument) and
+ // old is update and new is DELETE :
+ // if the object is in missing, it is removed
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ pg_log_entry_t ne;
+ ne.mark_unrollbackable();
+ ne.version = eversion_t(1,1);
+ ne.op = pg_log_entry_t::DELETE;
+ log.add(ne);
+
+ oe.version = eversion_t(2,1);
+ oe.op = pg_log_entry_t::MODIFY;
+ missing.add_next_event(oe);
+
+ missing.flush();
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_TRUE(missing.is_missing(oe.soid));
+ EXPECT_EQ(1U, log.log.size());
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ missing.flush();
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.size() > 0);
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.log.size());
+ }
+
+ // there is no new entry (from the logs) and
+ // the old entry (from the log entry given in argument) is not a CLONE and
+ // the old entry prior_version is greater than the tail of the log :
+ // do nothing and return false
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ info.log_tail = eversion_t(1,1);
+ oe.op = pg_log_entry_t::MODIFY;
+ oe.prior_version = eversion_t(2,1);
+ missing_add(oe.soid, oe.prior_version, eversion_t());
+
+ missing.flush();
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_TRUE(log.empty());
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ missing.flush();
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_TRUE(log.empty());
+ }
+
+ // there is no new entry (from the logs) and
+ // the old entry (from the log entry given in argument) is not a CLONE and
+ // the old entry (from the log entry given in argument) is not a DELETE and
+ // the old entry prior_version is lower than the tail of the log :
+ // add the old object to the remove_snap list and
+ // add the old object to divergent priors and
+ // add or update the prior_version of the object to missing and
+ // return false
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ info.log_tail = eversion_t(2,1);
+ oe.soid.set_hash(1);
+ oe.op = pg_log_entry_t::MODIFY;
+ oe.prior_version = eversion_t(1,1);
+
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_TRUE(log.empty());
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ EXPECT_TRUE(is_dirty());
+ EXPECT_EQ(oe.soid, remove_snap.front());
+ EXPECT_TRUE(t.empty());
+ EXPECT_TRUE(missing.is_missing(oe.soid));
+ EXPECT_TRUE(log.empty());
+ }
+
+ // there is no new entry (from the logs) and
+ // the old entry (from the log entry given in argument) is not a CLONE and
+ // the old entry (from the log entry given in argument) is a DELETE and
+ // the old entry prior_version is lower than the tail of the log :
+ // add the old object to divergent priors and
+ // add or update the prior_version of the object to missing and
+ // return false
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ info.log_tail = eversion_t(2,1);
+ oe.soid.set_hash(1);
+ oe.op = pg_log_entry_t::DELETE;
+ oe.prior_version = eversion_t(1,1);
+
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_TRUE(log.empty());
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ EXPECT_TRUE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_TRUE(missing.is_missing(oe.soid));
+ EXPECT_TRUE(log.empty());
+ }
+
+
+ // there is no new entry (from the logs) and
+ // the old entry (from the log entry given in argument) is not a CLONE and
+ // the old entry (from the log entry given in argument) is not a DELETE and
+ // the old entry prior_version is eversion_t() :
+ // add the old object to the remove_snap list and
+ // remove the prior_version of the object from missing, if any and
+ // return false
+ {
+ clear();
+
+ ObjectStore::Transaction t;
+ pg_log_entry_t oe;
+ oe.mark_unrollbackable();
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+
+ info.log_tail = eversion_t(10,1);
+ oe.soid.set_hash(1);
+ oe.op = pg_log_entry_t::MODIFY;
+ oe.prior_version = eversion_t();
+
+ missing.add(oe.soid, eversion_t(1,1), eversion_t(), false);
+
+ missing.flush();
+ EXPECT_FALSE(is_dirty());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(t.empty());
+ EXPECT_TRUE(missing.is_missing(oe.soid));
+ EXPECT_TRUE(log.empty());
+
+ TestHandler h(remove_snap);
+ merge_old_entry(t, oe, info, &h);
+
+ missing.flush();
+ EXPECT_FALSE(is_dirty());
+ EXPECT_EQ(oe.soid, remove_snap.front());
+ EXPECT_TRUE(t.empty());
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_TRUE(log.empty());
+ }
+
+}
+
+TEST_F(PGLogTest, merge_log) {
+ // head and tail match, last_backfill is set:
+ // noop
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_shard_t fromosd;
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ hobject_t last_backfill(object_t("oname"), string("key"), 1, 234, 1, "");
+ info.last_backfill = last_backfill;
+ eversion_t stat_version(10, 1);
+ info.stats.version = stat_version;
+ log.tail = olog.tail = eversion_t(1, 1);
+ log.head = olog.head = eversion_t(2, 1);
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(0U, log.log.size());
+ EXPECT_EQ(stat_version, info.stats.version);
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_EQ(last_backfill, info.last_backfill);
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ merge_log(oinfo, std::move(olog), fromosd, info, &h,
+ dirty_info, dirty_big_info);
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(0U, log.log.size());
+ EXPECT_EQ(stat_version, info.stats.version);
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+ }
+
+ // head and tail match, last_backfill is not set: info.stats is
+ // copied from oinfo.stats but info.stats.reported_* is guaranteed to
+ // never be replaced by a lower version
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_shard_t fromosd;
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ eversion_t stat_version(10, 1);
+ oinfo.stats.version = stat_version;
+ info.stats.reported_seq = 1;
+ info.stats.reported_epoch = 10;
+ oinfo.stats.reported_seq = 1;
+ oinfo.stats.reported_epoch = 1;
+ log.tail = olog.tail = eversion_t(1, 1);
+ log.head = olog.head = eversion_t(2, 1);
+ missing.may_include_deletes = false;
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(0U, log.log.size());
+ EXPECT_EQ(eversion_t(), info.stats.version);
+ EXPECT_EQ(1ull, info.stats.reported_seq);
+ EXPECT_EQ(10u, info.stats.reported_epoch);
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(info.last_backfill.is_max());
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ merge_log(oinfo, std::move(olog), fromosd, info, &h,
+ dirty_info, dirty_big_info);
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(0U, log.log.size());
+ EXPECT_EQ(stat_version, info.stats.version);
+ EXPECT_EQ(1ull, info.stats.reported_seq);
+ EXPECT_EQ(10u, info.stats.reported_epoch);
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+ }
+
+ /* Before
+ +--------------------------+
+ | log olog |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ | | x5 | (1,1) < tail
+ | | | |
+ | | | |
+ tail > (1,4) | x7 | |
+ | | | |
+ | | | |
+ head > (1,5) | x9 | (1,5) < head
+ | | | |
+ | | | |
+ +--------+-------+---------+
+
+ After
+ +-----------------
+ | log |
+ +--------+-------+
+ | |object |
+ |version | hash |
+ | | |
+ tail > (1,1) | x5 |
+ | | |
+ | | |
+ | (1,4) | x7 |
+ | | |
+ | | |
+ head > (1,5) | x9 |
+ | | |
+ | | |
+ +--------+-------+
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_shard_t fromosd;
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+ missing.may_include_deletes = false;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 4);
+ e.soid.set_hash(0x5);
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = eversion_t(1, 5);
+ e.soid.set_hash(0x9);
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ info.last_update = log.head;
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = eversion_t(1, 5);
+ e.soid.set_hash(0x9);
+ olog.log.push_back(e);
+ olog.head = e.version;
+ }
+
+ hobject_t last_backfill(object_t("oname"), string("key"), 1, 234, 1, "");
+ info.last_backfill = last_backfill;
+ eversion_t stat_version(10, 1);
+ info.stats.version = stat_version;
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(2U, log.log.size());
+ EXPECT_EQ(stat_version, info.stats.version);
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_EQ(last_backfill, info.last_backfill);
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ merge_log(oinfo, std::move(olog), fromosd, info, &h,
+ dirty_info, dirty_big_info);
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(3U, log.log.size());
+ EXPECT_EQ(stat_version, info.stats.version);
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_TRUE(is_dirty());
+ EXPECT_TRUE(dirty_info);
+ EXPECT_TRUE(dirty_big_info);
+ }
+
+ /* +--------------------------+
+ | log olog |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ tail > (1,1) | x5 | (1,1) < tail
+ | | | |
+ | | | |
+ | (1,2) | x3 | (1,2) < lower_bound
+ | | | |
+ | | | |
+ head > (1,3) | x9 | |
+ | DELETE | | |
+ | | | |
+ | | x9 | (2,3) |
+ | | | MODIFY |
+ | | | |
+ | | x7 | (2,4) < head
+ | | | DELETE |
+ +--------+-------+---------+
+
+ The log entry (1,3) deletes the object x9 but the olog entry (2,3) modifies
+ it and is authoritative : the log entry (1,3) is divergent.
+
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_shard_t fromosd;
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ hobject_t divergent_object;
+ missing.may_include_deletes = true;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = eversion_t(1, 2);
+ e.soid.set_hash(0x3);
+ log.log.push_back(e);
+ e.version = eversion_t(1,3);
+ e.soid.set_hash(0x9);
+ divergent_object = e.soid;
+ e.op = pg_log_entry_t::DELETE;
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ info.last_update = log.head;
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = eversion_t(1, 2);
+ e.soid.set_hash(0x3);
+ olog.log.push_back(e);
+ e.version = eversion_t(2, 3);
+ e.soid.set_hash(0x9);
+ e.op = pg_log_entry_t::MODIFY;
+ olog.log.push_back(e);
+ e.version = eversion_t(2, 4);
+ e.soid.set_hash(0x7);
+ e.op = pg_log_entry_t::DELETE;
+ olog.log.push_back(e);
+ olog.head = e.version;
+ }
+
+ snapid_t purged_snap(1);
+ {
+ oinfo.last_update = olog.head;
+ oinfo.purged_snaps.insert(purged_snap);
+ }
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.objects.count(divergent_object));
+ EXPECT_EQ(3U, log.log.size());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_EQ(log.head, info.last_update);
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ merge_log(oinfo, std::move(olog), fromosd, info, &h,
+ dirty_info, dirty_big_info);
+
+ /* When the divergent entry is a DELETE and the authoritative
+ entry is a MODIFY, the object will be added to missing : it is
+ a verifiable side effect proving the entry was identified
+ to be divergent.
+ */
+ EXPECT_TRUE(missing.is_missing(divergent_object));
+ EXPECT_EQ(1U, log.objects.count(divergent_object));
+ EXPECT_EQ(4U, log.log.size());
+ /* DELETE entries from olog that are appended to the hed of the
+ log, and the divergent version of the object is removed (added
+ to remove_snap)
+ */
+ EXPECT_EQ(0x9U, remove_snap.front().get_hash());
+ EXPECT_EQ(log.head, info.last_update);
+ EXPECT_TRUE(info.purged_snaps.contains(purged_snap));
+ EXPECT_TRUE(is_dirty());
+ EXPECT_TRUE(dirty_info);
+ EXPECT_TRUE(dirty_big_info);
+ }
+
+ /* +--------------------------+
+ | log olog |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ tail > (1,1) | x5 | (1,1) < tail
+ | | | |
+ | | | |
+ | (1,2) | x3 | (1,2) < lower_bound
+ | | | |
+ | | | |
+ head > (1,3) | x9 | |
+ | DELETE | | |
+ | | | |
+ | | x9 | (2,3) |
+ | | | MODIFY |
+ | | | |
+ | | x7 | (2,4) < head
+ | | | DELETE |
+ +--------+-------+---------+
+
+ The log entry (1,3) deletes the object x9 but the olog entry (2,3) modifies
+ it and is authoritative : the log entry (1,3) is divergent.
+
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_shard_t fromosd;
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ hobject_t divergent_object;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = eversion_t(1, 2);
+ e.soid.set_hash(0x3);
+ log.log.push_back(e);
+ e.version = eversion_t(1,3);
+ e.soid.set_hash(0x9);
+ divergent_object = e.soid;
+ e.op = pg_log_entry_t::DELETE;
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ info.last_update = log.head;
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = eversion_t(1, 2);
+ e.soid.set_hash(0x3);
+ olog.log.push_back(e);
+ e.version = eversion_t(2, 3);
+ e.soid.set_hash(0x9);
+ e.op = pg_log_entry_t::MODIFY;
+ olog.log.push_back(e);
+ e.version = eversion_t(2, 4);
+ e.soid.set_hash(0x7);
+ e.op = pg_log_entry_t::DELETE;
+ olog.log.push_back(e);
+ olog.head = e.version;
+ }
+
+ snapid_t purged_snap(1);
+ {
+ oinfo.last_update = olog.head;
+ oinfo.purged_snaps.insert(purged_snap);
+ }
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(1U, log.objects.count(divergent_object));
+ EXPECT_EQ(3U, log.log.size());
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_EQ(log.head, info.last_update);
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ missing.may_include_deletes = false;
+ merge_log(oinfo, std::move(olog), fromosd, info, &h,
+ dirty_info, dirty_big_info);
+
+ /* When the divergent entry is a DELETE and the authoritative
+ entry is a MODIFY, the object will be added to missing : it is
+ a verifiable side effect proving the entry was identified
+ to be divergent.
+ */
+ EXPECT_TRUE(missing.is_missing(divergent_object));
+ EXPECT_EQ(1U, log.objects.count(divergent_object));
+ EXPECT_EQ(4U, log.log.size());
+ /* DELETE entries from olog that are appended to the hed of the
+ log, and the divergent version of the object is removed (added
+ to remove_snap). When peering handles deletes, it is the earlier
+ version that is in the removed list.
+ */
+ EXPECT_EQ(0x7U, remove_snap.front().get_hash());
+ EXPECT_EQ(log.head, info.last_update);
+ EXPECT_TRUE(info.purged_snaps.contains(purged_snap));
+ EXPECT_TRUE(is_dirty());
+ EXPECT_TRUE(dirty_info);
+ EXPECT_TRUE(dirty_big_info);
+ }
+
+ /* +--------------------------+
+ | log olog |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ tail > (1,1) | x5 | (1,1) < tail
+ | | | |
+ | | | |
+ | (1,4) | x7 | (1,4) < head
+ | | | |
+ | | | |
+ head > (1,5) | x9 | |
+ | | | |
+ | | | |
+ +--------+-------+---------+
+
+ The head of the log entry (1,5) is divergent because it is greater than the
+ head of olog.
+
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_shard_t fromosd;
+ pg_info_t info;
+ list<hobject_t> remove_snap;
+ bool dirty_info = false;
+ bool dirty_big_info = false;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = eversion_t(1, 4);
+ e.soid.set_hash(0x7);
+ log.log.push_back(e);
+ e.version = eversion_t(1, 5);
+ e.soid.set_hash(0x9);
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ info.last_update = log.head;
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x5);
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = eversion_t(1, 4);
+ e.soid.set_hash(0x7);
+ olog.log.push_back(e);
+ olog.head = e.version;
+ }
+
+ hobject_t last_backfill(object_t("oname"), string("key"), 1, 234, 1, "");
+ info.last_backfill = last_backfill;
+ eversion_t stat_version(10, 1);
+ info.stats.version = stat_version;
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(3U, log.log.size());
+ EXPECT_EQ(stat_version, info.stats.version);
+ EXPECT_TRUE(remove_snap.empty());
+ EXPECT_EQ(last_backfill, info.last_backfill);
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_FALSE(is_dirty());
+ EXPECT_FALSE(dirty_info);
+ EXPECT_FALSE(dirty_big_info);
+
+ TestHandler h(remove_snap);
+ missing.may_include_deletes = false;
+ merge_log(oinfo, std::move(olog), fromosd, info, &h,
+ dirty_info, dirty_big_info);
+
+ EXPECT_FALSE(missing.have_missing());
+ EXPECT_EQ(2U, log.log.size());
+ EXPECT_EQ(stat_version, info.stats.version);
+ EXPECT_EQ(0x9U, remove_snap.front().get_hash());
+ EXPECT_TRUE(info.purged_snaps.empty());
+ EXPECT_TRUE(is_dirty());
+ EXPECT_TRUE(dirty_info);
+ EXPECT_TRUE(dirty_big_info);
+ }
+
+}
+
+TEST_F(PGLogTest, proc_replica_log) {
+ // empty log : no side effect
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_missing_t omissing;
+ pg_shard_t from;
+
+ eversion_t last_update(1, 1);
+ log.head = olog.head = oinfo.last_update = last_update;
+ eversion_t last_complete(1, 1);
+ oinfo.last_complete = last_complete;
+
+ EXPECT_FALSE(omissing.have_missing());
+ EXPECT_EQ(last_update, oinfo.last_update);
+ EXPECT_EQ(last_complete, oinfo.last_complete);
+
+ missing.may_include_deletes = false;
+ proc_replica_log(oinfo, olog, omissing, from);
+
+ EXPECT_FALSE(omissing.have_missing());
+ EXPECT_EQ(last_update, oinfo.last_update);
+ EXPECT_EQ(last_update, oinfo.last_complete);
+ }
+
+ /* +--------------------------+
+ | log olog |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ | | x3 | (1,1) < tail
+ | | | |
+ | | | |
+ tail > (1,2) | x5 | |
+ | | | |
+ | | | |
+ head > (1,3) | x9 | |
+ | DELETE | | |
+ | | | |
+ | | x9 | (2,3) < head
+ | | | DELETE |
+ | | | |
+ +--------+-------+---------+
+
+ The log entry (1,3) deletes the object x9 and the olog entry
+ (2,3) also deletes it : do nothing. The olog tail is ignored
+ because it is before the log tail.
+
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_missing_t omissing;
+ pg_shard_t from;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 2);
+ e.soid.set_hash(0x5);
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = eversion_t(1, 3);
+ e.soid.set_hash(0x9);
+ e.op = pg_log_entry_t::DELETE;
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x3);
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = eversion_t(2, 3);
+ e.soid.set_hash(0x9);
+ e.op = pg_log_entry_t::DELETE;
+ olog.log.push_back(e);
+ olog.head = e.version;
+
+ oinfo.last_update = olog.head;
+ oinfo.last_complete = olog.head;
+ }
+
+ EXPECT_FALSE(omissing.have_missing());
+ EXPECT_EQ(olog.head, oinfo.last_update);
+ EXPECT_EQ(olog.head, oinfo.last_complete);
+
+ missing.may_include_deletes = false;
+ proc_replica_log(oinfo, olog, omissing, from);
+
+ EXPECT_FALSE(omissing.have_missing());
+ }
+
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_missing_t omissing;
+ pg_shard_t from;
+
+ hobject_t divergent_object;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ {
+ e.soid = divergent_object;
+ e.soid.set_hash(0x1);
+ e.version = eversion_t(1, 1);
+ log.tail = e.version;
+ log.log.push_back(e);
+
+ e.soid = divergent_object;
+ e.prior_version = eversion_t(1, 1);
+ e.version = eversion_t(1, 2);
+ log.tail = e.version;
+ log.log.push_back(e);
+
+ e.soid.set_hash(0x3);
+ e.version = eversion_t(1, 4);
+ log.log.push_back(e);
+
+ e.soid.set_hash(0x7);
+ e.version = eversion_t(1, 5);
+ log.log.push_back(e);
+
+ e.soid.set_hash(0x8);
+ e.version = eversion_t(1, 6);
+ log.log.push_back(e);
+
+ e.soid.set_hash(0x9);
+ e.op = pg_log_entry_t::DELETE;
+ e.version = eversion_t(2, 7);
+ log.log.push_back(e);
+
+ e.soid.set_hash(0xa);
+ e.version = eversion_t(2, 8);
+ log.head = e.version;
+ log.log.push_back(e);
+ }
+ log.index();
+
+ {
+ e.soid = divergent_object;
+ e.soid.set_hash(0x1);
+ e.version = eversion_t(1, 1);
+ olog.tail = e.version;
+ olog.log.push_back(e);
+
+ e.soid = divergent_object;
+ e.prior_version = eversion_t(1, 1);
+ e.version = eversion_t(1, 2);
+ olog.log.push_back(e);
+
+ e.prior_version = eversion_t(0, 0);
+ e.soid.set_hash(0x3);
+ e.version = eversion_t(1, 4);
+ olog.log.push_back(e);
+
+ e.soid.set_hash(0x7);
+ e.version = eversion_t(1, 5);
+ olog.log.push_back(e);
+
+ e.soid.set_hash(0x8);
+ e.version = eversion_t(1, 6);
+ olog.log.push_back(e);
+
+ e.soid.set_hash(0x9); // should not be added to missing, create
+ e.op = pg_log_entry_t::MODIFY;
+ e.version = eversion_t(1, 7);
+ olog.log.push_back(e);
+
+ e.soid = divergent_object; // should be added to missing at 1,2
+ e.op = pg_log_entry_t::MODIFY;
+ e.version = eversion_t(1, 8);
+ e.prior_version = eversion_t(1, 2);
+ olog.log.push_back(e);
+ olog.head = e.version;
+ }
+ oinfo.last_update = olog.head;
+ oinfo.last_complete = olog.head;
+ }
+
+ EXPECT_FALSE(omissing.have_missing());
+ EXPECT_EQ(olog.head, oinfo.last_update);
+ EXPECT_EQ(olog.head, oinfo.last_complete);
+
+ missing.may_include_deletes = false;
+ proc_replica_log(oinfo, olog, omissing, from);
+
+ EXPECT_TRUE(omissing.have_missing());
+ EXPECT_TRUE(omissing.is_missing(divergent_object));
+ EXPECT_EQ(eversion_t(1, 2), omissing.get_items().at(divergent_object).need);
+ EXPECT_EQ(eversion_t(1, 6), oinfo.last_update);
+ EXPECT_EQ(eversion_t(1, 1), oinfo.last_complete);
+ }
+
+ /* +--------------------------+
+ | olog log |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ tail > (1,1) | x9 | (1,1) < tail
+ | | | |
+ | | | |
+ | (1,2) | x3 | (1,2) |
+ | | | |
+ | | | |
+ head > (1,3) | x9 | |
+ | DELETE | | |
+ | | | |
+ | | x9 | (2,3) < head
+ | | | DELETE |
+ | | | |
+ +--------+-------+---------+
+
+ The log entry (1,3) deletes the object x9 and the olog entry
+ (2,3) also deletes it : do nothing.
+
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_missing_t omissing;
+ pg_shard_t from;
+
+ eversion_t last_update(1, 2);
+ hobject_t divergent_object;
+ divergent_object.set_hash(0x9);
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = last_update;
+ e.soid.set_hash(0x3);
+ log.log.push_back(e);
+ e.version = eversion_t(2, 3);
+ e.prior_version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ e.op = pg_log_entry_t::DELETE;
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ e.version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = last_update;
+ e.soid.set_hash(0x3);
+ olog.log.push_back(e);
+ e.version = eversion_t(1, 3);
+ e.prior_version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ e.op = pg_log_entry_t::DELETE;
+ olog.log.push_back(e);
+ olog.head = e.version;
+
+ oinfo.last_update = olog.head;
+ oinfo.last_complete = olog.head;
+ }
+
+ EXPECT_FALSE(omissing.have_missing());
+ EXPECT_EQ(olog.head, oinfo.last_update);
+ EXPECT_EQ(olog.head, oinfo.last_complete);
+
+ missing.may_include_deletes = false;
+ proc_replica_log(oinfo, olog, omissing, from);
+
+ EXPECT_TRUE(omissing.have_missing());
+ EXPECT_TRUE(omissing.is_missing(divergent_object));
+ EXPECT_EQ(omissing.get_items().at(divergent_object).have, eversion_t(0, 0));
+ EXPECT_EQ(omissing.get_items().at(divergent_object).need, eversion_t(1, 1));
+ EXPECT_EQ(last_update, oinfo.last_update);
+ }
+
+ /* +--------------------------+
+ | olog log |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ tail > (1,1) | x9 | (1,1) < tail
+ | | | |
+ | | | |
+ | (1,2) | x3 | (1,2) |
+ | | | |
+ | | | |
+ head > (1,3) | x9 | |
+ | MODIFY | | |
+ | | | |
+ | | x9 | (2,3) < head
+ | | | DELETE |
+ | | | |
+ +--------+-------+---------+
+
+ The log entry (2,3) deletes the object x9 but the olog entry
+ (1,3) modifies it : remove it from omissing.
+
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_missing_t omissing;
+ pg_shard_t from;
+
+ eversion_t last_update(1, 2);
+ hobject_t divergent_object;
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = last_update;
+ e.soid.set_hash(0x3);
+ log.log.push_back(e);
+ e.version = eversion_t(2, 3);
+ e.prior_version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ e.op = pg_log_entry_t::DELETE;
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ e.version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = last_update;
+ e.soid.set_hash(0x3);
+ olog.log.push_back(e);
+ e.version = eversion_t(1, 3);
+ e.prior_version = eversion_t(1, 1);
+ e.soid = divergent_object;
+ divergent_object = e.soid;
+ omissing.add(divergent_object, e.version, eversion_t(), false);
+ e.op = pg_log_entry_t::MODIFY;
+ olog.log.push_back(e);
+ olog.head = e.version;
+
+ oinfo.last_update = olog.head;
+ oinfo.last_complete = olog.head;
+ }
+
+ EXPECT_TRUE(omissing.have_missing());
+ EXPECT_TRUE(omissing.is_missing(divergent_object));
+ EXPECT_EQ(eversion_t(1, 3), omissing.get_items().at(divergent_object).need);
+ EXPECT_EQ(olog.head, oinfo.last_update);
+ EXPECT_EQ(olog.head, oinfo.last_complete);
+
+ missing.may_include_deletes = false;
+ proc_replica_log(oinfo, olog, omissing, from);
+
+ EXPECT_TRUE(omissing.have_missing());
+ EXPECT_TRUE(omissing.is_missing(divergent_object));
+ EXPECT_EQ(omissing.get_items().at(divergent_object).have, eversion_t(0, 0));
+ EXPECT_EQ(omissing.get_items().at(divergent_object).need, eversion_t(1, 1));
+ EXPECT_EQ(last_update, oinfo.last_update);
+ }
+
+ /* +--------------------------+
+ | log olog |
+ +--------+-------+---------+
+ | |object | |
+ |version | hash | version |
+ | | | |
+ tail > (1,1) | x9 | (1,1) < tail
+ | | | |
+ | | | |
+ | (1,2) | x3 | (1,2) |
+ | | | |
+ | | | |
+ | | x9 | (1,3) < head
+ | | | MODIFY |
+ | | | |
+ head > (2,3) | x9 | |
+ | DELETE | | |
+ | | | |
+ +--------+-------+---------+
+
+ The log entry (2,3) deletes the object x9 but the olog entry
+ (1,3) modifies it : proc_replica_log should adjust missing to
+ 1,1 for that object until add_next_event in PG::activate processes
+ the delete.
+ */
+ {
+ clear();
+
+ pg_log_t olog;
+ pg_info_t oinfo;
+ pg_missing_t omissing;
+ pg_shard_t from;
+
+ eversion_t last_update(1, 2);
+ hobject_t divergent_object;
+ eversion_t new_version(2, 3);
+ eversion_t divergent_version(1, 3);
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x9);
+ log.tail = e.version;
+ log.log.push_back(e);
+ e.version = last_update;
+ e.soid.set_hash(0x3);
+ log.log.push_back(e);
+ e.version = new_version;
+ e.prior_version = eversion_t(1, 1);
+ e.soid.set_hash(0x9);
+ e.op = pg_log_entry_t::DELETE;
+ log.log.push_back(e);
+ log.head = e.version;
+ log.index();
+
+ e.op = pg_log_entry_t::MODIFY;
+ e.version = eversion_t(1, 1);
+ e.soid.set_hash(0x9);
+ olog.tail = e.version;
+ olog.log.push_back(e);
+ e.version = last_update;
+ e.soid.set_hash(0x3);
+ olog.log.push_back(e);
+ e.version = divergent_version;
+ e.prior_version = eversion_t(1, 1);
+ e.soid.set_hash(0x9);
+ divergent_object = e.soid;
+ omissing.add(divergent_object, e.version, eversion_t(), false);
+ e.op = pg_log_entry_t::MODIFY;
+ olog.log.push_back(e);
+ olog.head = e.version;
+
+ oinfo.last_update = olog.head;
+ oinfo.last_complete = olog.head;
+ }
+
+ EXPECT_TRUE(omissing.have_missing());
+ EXPECT_TRUE(omissing.is_missing(divergent_object));
+ EXPECT_EQ(divergent_version, omissing.get_items().at(divergent_object).need);
+ EXPECT_EQ(olog.head, oinfo.last_update);
+ EXPECT_EQ(olog.head, oinfo.last_complete);
+
+ missing.may_include_deletes = false;
+ proc_replica_log(oinfo, olog, omissing, from);
+
+ EXPECT_TRUE(omissing.have_missing());
+ EXPECT_TRUE(omissing.get_items().begin()->second.need == eversion_t(1, 1));
+ EXPECT_EQ(last_update, oinfo.last_update);
+ EXPECT_EQ(eversion_t(0, 0), oinfo.last_complete);
+ }
+
+}
+
+TEST_F(PGLogTest, merge_log_1) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.div.push_back(mk_ple_mod(mk_obj(1), mk_evt(10, 101), mk_evt(10, 100)));
+
+ t.final.add(mk_obj(1), mk_evt(10, 100), mk_evt(0, 0), false);
+
+ t.toremove.insert(mk_obj(1));
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_2) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.div.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 101), mk_evt(10, 100)));
+ t.div.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 102), mk_evt(10, 101)));
+
+ t.torollback.insert(
+ t.torollback.begin(), t.div.rbegin(), t.div.rend());
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_3) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.div.push_back(mk_ple_mod(mk_obj(1), mk_evt(10, 101), mk_evt(10, 100)));
+ t.div.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 102), mk_evt(10, 101)));
+
+ t.final.add(mk_obj(1), mk_evt(10, 100), mk_evt(0, 0), false);
+
+ t.toremove.insert(mk_obj(1));
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_4) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.div.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 101), mk_evt(10, 100)));
+ t.div.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 102), mk_evt(10, 101)));
+
+ t.init.add(mk_obj(1), mk_evt(10, 102), mk_evt(0, 0), false);
+ t.final.add(mk_obj(1), mk_evt(10, 100), mk_evt(0, 0), false);
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_5) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.div.push_back(mk_ple_mod(mk_obj(1), mk_evt(10, 101), mk_evt(10, 100)));
+ t.div.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 102), mk_evt(10, 101)));
+
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(11, 101), mk_evt(10, 100)));
+
+ t.final.add(mk_obj(1), mk_evt(11, 101), mk_evt(0, 0), false);
+
+ t.toremove.insert(mk_obj(1));
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_6) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(11, 101), mk_evt(10, 100)));
+
+ t.final.add(mk_obj(1), mk_evt(11, 101), mk_evt(10, 100), false);
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_7) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(11, 101), mk_evt(10, 100)));
+
+ t.init.add(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80), false);
+ t.final.add(mk_obj(1), mk_evt(11, 101), mk_evt(8, 80), false);
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_8) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.auth.push_back(mk_ple_dt(mk_obj(1), mk_evt(11, 101), mk_evt(10, 100)));
+
+ t.init.add(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80), false);
+ t.final.add(mk_obj(1), mk_evt(11, 101), mk_evt(8, 80), true);
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_9) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.auth.push_back(mk_ple_dt(mk_obj(1), mk_evt(11, 101), mk_evt(10, 100)));
+
+ t.init.add(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80), false);
+ t.toremove.insert(mk_obj(1));
+ t.deletes_during_peering = true;
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_10) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.auth.push_back(mk_ple_ldt(mk_obj(1), mk_evt(11, 101), mk_evt(10, 100)));
+
+ t.init.add(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80), false);
+ t.final.add(mk_obj(1), mk_evt(11, 101), mk_evt(8, 80), true);
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_prior_version_have) {
+ TestCase t;
+ t.base.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 80)));
+
+ t.div.push_back(mk_ple_mod(mk_obj(1), mk_evt(10, 101), mk_evt(10, 100)));
+
+ t.init.add(mk_obj(1), mk_evt(10, 101), mk_evt(10, 100), false);
+
+ t.setup();
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, merge_log_split_missing_entries_at_head) {
+ TestCase t;
+ t.auth.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ t.auth.push_back(mk_ple_mod_rb(mk_obj(1), mk_evt(15, 150), mk_evt(10, 100)));
+
+ t.div.push_back(mk_ple_mod(mk_obj(1), mk_evt(8, 70), mk_evt(8, 65)));
+
+ t.setup();
+ t.set_div_bounds(mk_evt(9, 79), mk_evt(8, 69));
+ t.set_auth_bounds(mk_evt(15, 160), mk_evt(9, 77));
+ t.final.add(mk_obj(1), mk_evt(15, 150), mk_evt(8, 70), false);
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, olog_tail_gt_log_tail_split) {
+ TestCase t;
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(15, 150), mk_evt(10, 100)));
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(15, 155), mk_evt(15, 150)));
+
+ t.setup();
+ t.set_div_bounds(mk_evt(15, 153), mk_evt(15, 151));
+ t.set_auth_bounds(mk_evt(15, 156), mk_evt(10, 99));
+ t.final.add(mk_obj(1), mk_evt(15, 155), mk_evt(15, 150), false);
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, olog_tail_gt_log_tail_split2) {
+ TestCase t;
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(15, 150), mk_evt(10, 100)));
+ t.auth.push_back(mk_ple_mod(mk_obj(1), mk_evt(16, 155), mk_evt(15, 150)));
+ t.div.push_back(mk_ple_mod(mk_obj(1), mk_evt(15, 153), mk_evt(15, 150)));
+
+ t.setup();
+ t.set_div_bounds(mk_evt(15, 153), mk_evt(15, 151));
+ t.set_auth_bounds(mk_evt(16, 156), mk_evt(10, 99));
+ t.final.add(mk_obj(1), mk_evt(16, 155), mk_evt(0, 0), false);
+ t.toremove.insert(mk_obj(1));
+ run_test_case(t);
+}
+
+TEST_F(PGLogTest, filter_log_1) {
+ {
+ clear();
+
+ int osd_id = 1;
+ epoch_t epoch = 40;
+ int64_t pool_id = 1;
+ int bits = 2;
+ int max_osd = 4;
+ int pg_num = max_osd << bits;
+ int num_objects = 1000;
+ int num_internal = 10;
+
+ // Set up splitting map
+ std::unique_ptr<OSDMap> osdmap(new OSDMap);
+ uuid_d test_uuid;
+ test_uuid.generate_random();
+ osdmap->build_simple_with_pool(g_ceph_context, epoch, test_uuid, max_osd, bits, bits);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+
+ const string hit_set_namespace("internal");
+
+ {
+ pg_log_entry_t e;
+ e.mark_unrollbackable();
+ e.op = pg_log_entry_t::MODIFY;
+ e.soid.pool = pool_id;
+
+ uuid_d uuid_name;
+ int i;
+ for (i = 1; i <= num_objects; ++i) {
+ e.version = eversion_t(epoch, i);
+ // Use this to generate random file names
+ uuid_name.generate_random();
+ ostringstream name;
+ name << uuid_name;
+ e.soid.oid.name = name.str();
+ // First has no namespace
+ if (i != 1) {
+ // num_internal have the internal namspace
+ if (i <= num_internal + 1) {
+ e.soid.nspace = hit_set_namespace;
+ } else { // rest have different namespaces
+ ostringstream ns;
+ ns << "ns" << i;
+ e.soid.nspace = ns.str();
+ }
+ }
+ log.log.push_back(e);
+ if (i == 1)
+ log.tail = e.version;
+ }
+ log.head = e.version;
+ log.index();
+ }
+
+ spg_t pgid(pg_t(2, pool_id), shard_id_t::NO_SHARD);
+
+ // See if we created the right number of entries
+ int total = log.log.size();
+ ASSERT_EQ(total, num_objects);
+
+ // Some should be removed
+ {
+ pg_log_t filtered, reject;
+ pg_log_t::filter_log(
+ pgid, *osdmap, hit_set_namespace, log, filtered, reject);
+ log = IndexedLog(filtered);
+ }
+ EXPECT_LE(log.log.size(), (size_t)total);
+
+ // If we filter a second time, there should be the same total
+ total = log.log.size();
+ {
+ pg_log_t filtered, reject;
+ pg_log_t::filter_log(
+ pgid, *osdmap, hit_set_namespace, log, filtered, reject);
+ log = IndexedLog(filtered);
+ }
+ EXPECT_EQ(log.log.size(), (size_t)total);
+
+ // Increase pg_num as if there would be a split
+ int new_pg_num = pg_num * 16;
+ OSDMap::Incremental inc(epoch + 1);
+ inc.fsid = test_uuid;
+ const pg_pool_t *pool = osdmap->get_pg_pool(pool_id);
+ pg_pool_t newpool;
+ newpool = *pool;
+ newpool.set_pg_num(new_pg_num);
+ newpool.set_pgp_num(new_pg_num);
+ inc.new_pools[pool_id] = newpool;
+ int ret = osdmap->apply_incremental(inc);
+ ASSERT_EQ(ret, 0);
+
+ // We should have fewer entries after a filter
+ {
+ pg_log_t filtered, reject;
+ pg_log_t::filter_log(
+ pgid, *osdmap, hit_set_namespace, log, filtered, reject);
+ log = IndexedLog(filtered);
+ }
+ EXPECT_LE(log.log.size(), (size_t)total);
+
+ // Make sure all internal entries are retained
+ int count = 0;
+ for (list<pg_log_entry_t>::iterator i = log.log.begin();
+ i != log.log.end(); ++i) {
+ if (i->soid.nspace == hit_set_namespace) count++;
+ }
+ EXPECT_EQ(count, num_internal);
+ }
+}
+
+TEST_F(PGLogTest, get_request) {
+ clear();
+
+ // make sure writes, deletes, and errors are found
+ vector<pg_log_entry_t> entries;
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ entries.push_back(
+ pg_log_entry_t(pg_log_entry_t::ERROR, oid, eversion_t(6,2), eversion_t(3,4),
+ 1, osd_reqid_t(entity_name_t::CLIENT(777), 8, 1),
+ utime_t(0,1), -ENOENT));
+ entries.push_back(
+ pg_log_entry_t(pg_log_entry_t::MODIFY, oid, eversion_t(6,3), eversion_t(3,4),
+ 2, osd_reqid_t(entity_name_t::CLIENT(777), 8, 2),
+ utime_t(1,2), 0));
+ entries.push_back(
+ pg_log_entry_t(pg_log_entry_t::DELETE, oid, eversion_t(7,4), eversion_t(7,4),
+ 3, osd_reqid_t(entity_name_t::CLIENT(777), 8, 3),
+ utime_t(10,2), 0));
+ entries.push_back(
+ pg_log_entry_t(pg_log_entry_t::ERROR, oid, eversion_t(7,5), eversion_t(7,4),
+ 3, osd_reqid_t(entity_name_t::CLIENT(777), 8, 4),
+ utime_t(20,1), -ENOENT));
+
+ for (auto &entry : entries) {
+ log.add(entry);
+ }
+
+ for (auto &entry : entries) {
+ eversion_t replay_version;
+ version_t user_version;
+ int return_code = 0;
+ vector<pg_log_op_return_item_t> op_returns;
+ bool got = log.get_request(
+ entry.reqid, &replay_version, &user_version, &return_code, &op_returns);
+ EXPECT_TRUE(got);
+ EXPECT_EQ(entry.return_code, return_code);
+ EXPECT_EQ(entry.version, replay_version);
+ EXPECT_EQ(entry.user_version, user_version);
+ }
+}
+
+TEST_F(PGLogTest, ErrorNotIndexedByObject) {
+ clear();
+
+ // make sure writes, deletes, and errors are found
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ log.add(
+ pg_log_entry_t(pg_log_entry_t::ERROR, oid, eversion_t(6,2), eversion_t(3,4),
+ 1, osd_reqid_t(entity_name_t::CLIENT(777), 8, 1),
+ utime_t(0,1), -ENOENT));
+
+ EXPECT_FALSE(log.logged_object(oid));
+
+ pg_log_entry_t modify(pg_log_entry_t::MODIFY, oid, eversion_t(6,3),
+ eversion_t(3,4), 2,
+ osd_reqid_t(entity_name_t::CLIENT(777), 8, 2),
+ utime_t(1,2), 0);
+ log.add(modify);
+
+ EXPECT_TRUE(log.logged_object(oid));
+ pg_log_entry_t *entry = log.objects[oid];
+ EXPECT_EQ(modify.op, entry->op);
+ EXPECT_EQ(modify.version, entry->version);
+ EXPECT_EQ(modify.prior_version, entry->prior_version);
+ EXPECT_EQ(modify.user_version, entry->user_version);
+ EXPECT_EQ(modify.reqid, entry->reqid);
+
+ pg_log_entry_t del(pg_log_entry_t::DELETE, oid, eversion_t(7,4),
+ eversion_t(7,4), 3,
+ osd_reqid_t(entity_name_t::CLIENT(777), 8, 3),
+ utime_t(10,2), 0);
+ log.add(del);
+
+ EXPECT_TRUE(log.logged_object(oid));
+ entry = log.objects[oid];
+ EXPECT_EQ(del.op, entry->op);
+ EXPECT_EQ(del.version, entry->version);
+ EXPECT_EQ(del.prior_version, entry->prior_version);
+ EXPECT_EQ(del.user_version, entry->user_version);
+ EXPECT_EQ(del.reqid, entry->reqid);
+
+ log.add(
+ pg_log_entry_t(pg_log_entry_t::ERROR, oid, eversion_t(7,5), eversion_t(7,4),
+ 3, osd_reqid_t(entity_name_t::CLIENT(777), 8, 4),
+ utime_t(20,1), -ENOENT));
+
+ EXPECT_TRUE(log.logged_object(oid));
+ entry = log.objects[oid];
+ EXPECT_EQ(del.op, entry->op);
+ EXPECT_EQ(del.version, entry->version);
+ EXPECT_EQ(del.prior_version, entry->prior_version);
+ EXPECT_EQ(del.user_version, entry->user_version);
+ EXPECT_EQ(del.reqid, entry->reqid);
+}
+
+TEST_F(PGLogTest, split_into_preserves_may_include_deletes) {
+ clear();
+
+ {
+ may_include_deletes_in_missing_dirty = false;
+ missing.may_include_deletes = true;
+ PGLog child_log(cct);
+ pg_t child_pg;
+ split_into(child_pg, 6, &child_log);
+ ASSERT_TRUE(child_log.get_missing().may_include_deletes);
+ ASSERT_TRUE(child_log.get_may_include_deletes_in_missing_dirty());
+ }
+
+ {
+ may_include_deletes_in_missing_dirty = false;
+ missing.may_include_deletes = false;
+ PGLog child_log(cct);
+ pg_t child_pg;
+ split_into(child_pg, 6, &child_log);
+ ASSERT_FALSE(child_log.get_missing().may_include_deletes);
+ ASSERT_FALSE(child_log.get_may_include_deletes_in_missing_dirty());
+ }
+}
+
+class PGLogTestRebuildMissing : public PGLogTest, public StoreTestFixture {
+public:
+ PGLogTestRebuildMissing() : PGLogTest(), StoreTestFixture("memstore") {}
+ void SetUp() override {
+ StoreTestFixture::SetUp();
+ ObjectStore::Transaction t;
+ test_coll = coll_t(spg_t(pg_t(1, 1)));
+ ch = store->create_new_collection(test_coll);
+ t.create_collection(test_coll, 0);
+ store->queue_transaction(ch, std::move(t));
+ existing_oid = mk_obj(0);
+ nonexistent_oid = mk_obj(1);
+ ghobject_t existing_ghobj(existing_oid);
+ object_info_t existing_info;
+ existing_info.version = eversion_t(6, 2);
+ bufferlist enc_oi;
+ encode(existing_info, enc_oi, 0);
+ ObjectStore::Transaction t2;
+ t2.touch(test_coll, ghobject_t(existing_oid));
+ t2.setattr(test_coll, ghobject_t(existing_oid), OI_ATTR, enc_oi);
+ ASSERT_EQ(0, store->queue_transaction(ch, std::move(t2)));
+ info.last_backfill = hobject_t::get_max();
+ info.last_complete = eversion_t();
+ }
+
+ void TearDown() override {
+ clear();
+ missing.may_include_deletes = false;
+ StoreTestFixture::TearDown();
+ }
+
+ pg_info_t info;
+ coll_t test_coll;
+ hobject_t existing_oid, nonexistent_oid;
+
+ void run_rebuild_missing_test(const map<hobject_t, pg_missing_item> &expected_missing_items) {
+ rebuild_missing_set_with_deletes(store.get(), ch, info);
+ ASSERT_EQ(expected_missing_items, missing.get_items());
+ }
+};
+
+TEST_F(PGLogTestRebuildMissing, EmptyLog) {
+ missing.add(existing_oid, mk_evt(6, 2), mk_evt(6, 3), false);
+ missing.add(nonexistent_oid, mk_evt(7, 4), mk_evt(0, 0), false);
+ map<hobject_t, pg_missing_item> orig_missing = missing.get_items();
+ run_rebuild_missing_test(orig_missing);
+}
+
+TEST_F(PGLogTestRebuildMissing, SameVersionMod) {
+ missing.add(existing_oid, mk_evt(6, 2), mk_evt(6, 1), false);
+ log.add(mk_ple_mod(existing_oid, mk_evt(6, 2), mk_evt(6, 1)));
+ map<hobject_t, pg_missing_item> empty_missing;
+ run_rebuild_missing_test(empty_missing);
+}
+
+TEST_F(PGLogTestRebuildMissing, DelExisting) {
+ missing.add(existing_oid, mk_evt(6, 3), mk_evt(6, 2), false);
+ log.add(mk_ple_dt(existing_oid, mk_evt(7, 5), mk_evt(7, 4)));
+ map<hobject_t, pg_missing_item> expected;
+ expected[existing_oid] = pg_missing_item(mk_evt(7, 5), mk_evt(6, 2), true);
+ run_rebuild_missing_test(expected);
+}
+
+TEST_F(PGLogTestRebuildMissing, DelNonexistent) {
+ log.add(mk_ple_dt(nonexistent_oid, mk_evt(7, 5), mk_evt(7, 4)));
+ map<hobject_t, pg_missing_item> expected;
+ expected[nonexistent_oid] = pg_missing_item(mk_evt(7, 5), mk_evt(0, 0), true);
+ run_rebuild_missing_test(expected);
+}
+
+TEST_F(PGLogTestRebuildMissing, MissingNotInLog) {
+ missing.add(mk_obj(10), mk_evt(8, 12), mk_evt(8, 10), false);
+ log.add(mk_ple_dt(nonexistent_oid, mk_evt(7, 5), mk_evt(7, 4)));
+ map<hobject_t, pg_missing_item> expected;
+ expected[nonexistent_oid] = pg_missing_item(mk_evt(7, 5), mk_evt(0, 0), true);
+ expected[mk_obj(10)] = pg_missing_item(mk_evt(8, 12), mk_evt(8, 10), false);
+ run_rebuild_missing_test(expected);
+}
+
+
+class PGLogMergeDupsTest : protected PGLog, public StoreTestFixture {
+
+public:
+
+ PGLogMergeDupsTest() : PGLog(g_ceph_context), StoreTestFixture("memstore") { }
+
+ void SetUp() override {
+ StoreTestFixture::SetUp();
+ ObjectStore::Transaction t;
+ test_coll = coll_t(spg_t(pg_t(1, 1)));
+ auto ch = store->create_new_collection(test_coll);
+ t.create_collection(test_coll, 0);
+ store->queue_transaction(ch, std::move(t));
+ }
+
+ void TearDown() override {
+ test_disk_roundtrip();
+ clear();
+ StoreTestFixture::TearDown();
+ }
+
+ static pg_log_dup_t create_dup_entry(uint a, uint b) {
+ // make each dup_entry unique by using different client id's
+ static uint client_id = 777;
+ return pg_log_dup_t(eversion_t(a, b),
+ a,
+ osd_reqid_t(entity_name_t::CLIENT(client_id++), 8, 1),
+ 0);
+ }
+
+ static std::vector<pg_log_dup_t> example_dups_1() {
+ std::vector<pg_log_dup_t> result = {
+ create_dup_entry(10, 11),
+ create_dup_entry(10, 12),
+ create_dup_entry(11, 1),
+ create_dup_entry(12, 3),
+ create_dup_entry(13, 99)
+ };
+ return result;
+ }
+
+ static std::vector<pg_log_dup_t> example_dups_2() {
+ std::vector<pg_log_dup_t> result = {
+ create_dup_entry(12, 3),
+ create_dup_entry(13, 99),
+ create_dup_entry(15, 11),
+ create_dup_entry(16, 14),
+ create_dup_entry(16, 32)
+ };
+ return result;
+ }
+
+ void add_dups(uint a, uint b) {
+ log.dups.push_back(create_dup_entry(a, b));
+ write_from_dups = std::min(write_from_dups, log.dups.back().version);
+ }
+
+ void add_dups(const std::vector<pg_log_dup_t>& l) {
+ for (auto& i : l) {
+ log.dups.push_back(i);
+ write_from_dups = std::min(write_from_dups, log.dups.back().version);
+ }
+ }
+
+ static void add_dups(IndexedLog& log, const std::vector<pg_log_dup_t>& dups) {
+ for (auto& i : dups) {
+ log.dups.push_back(i);
+ }
+ }
+
+ void check_order() {
+ eversion_t prev(0, 0);
+
+ for (auto& i : log.dups) {
+ EXPECT_LT(prev, i.version) << "verify versions monotonically increase";
+ prev = i.version;
+ }
+ }
+
+ void check_index() {
+ EXPECT_EQ(log.dups.size(), log.dup_index.size());
+ for (auto& i : log.dups) {
+ EXPECT_EQ(1u, log.dup_index.count(i.reqid));
+ }
+ }
+
+ void test_disk_roundtrip() {
+ ObjectStore::Transaction t;
+ hobject_t hoid;
+ hoid.pool = 1;
+ hoid.oid = "log";
+ ghobject_t log_oid(hoid);
+ map<string, bufferlist> km;
+ write_log_and_missing(t, &km, test_coll, log_oid, false);
+ if (!km.empty()) {
+ t.omap_setkeys(test_coll, log_oid, km);
+ }
+ auto ch = store->open_collection(test_coll);
+ ASSERT_EQ(0, store->queue_transaction(ch, std::move(t)));
+
+ auto orig_dups = log.dups;
+ clear();
+ ostringstream err;
+ read_log_and_missing(store.get(), ch, log_oid,
+ pg_info_t(), err, false);
+ ASSERT_EQ(orig_dups.size(), log.dups.size());
+ ASSERT_EQ(orig_dups, log.dups);
+ auto dups_it = log.dups.begin();
+ for (auto orig_dup : orig_dups) {
+ ASSERT_EQ(orig_dup, *dups_it);
+ ++dups_it;
+ }
+ }
+
+ coll_t test_coll;
+};
+
+TEST_F(PGLogMergeDupsTest, OtherEmpty) {
+ log.tail = eversion_t(14, 5);
+
+ IndexedLog olog;
+
+ add_dups(example_dups_1());
+ index();
+
+ bool changed = merge_log_dups(olog);
+
+ EXPECT_FALSE(changed);
+ EXPECT_EQ(5u, log.dups.size());
+
+ if (5 == log.dups.size()) {
+ EXPECT_EQ(10u, log.dups.front().version.epoch);
+ EXPECT_EQ(11u, log.dups.front().version.version);
+ EXPECT_EQ(13u, log.dups.back().version.epoch);
+ EXPECT_EQ(99u, log.dups.back().version.version);
+ }
+
+ check_order();
+ check_index();
+}
+
+TEST_F(PGLogMergeDupsTest, AmEmpty) {
+ log.tail = eversion_t(14, 5);
+ index();
+
+ IndexedLog olog;
+
+ add_dups(olog, example_dups_1());
+
+ bool changed = merge_log_dups(olog);
+
+ EXPECT_TRUE(changed);
+ EXPECT_EQ(5u, log.dups.size());
+
+ if (5 == log.dups.size()) {
+ EXPECT_EQ(10u, log.dups.front().version.epoch);
+ EXPECT_EQ(11u, log.dups.front().version.version);
+
+ EXPECT_EQ(13u, log.dups.back().version.epoch);
+ EXPECT_EQ(99u, log.dups.back().version.version);
+ }
+
+ check_order();
+ check_index();
+}
+
+TEST_F(PGLogMergeDupsTest, AmEmptyOverlap) {
+ log.tail = eversion_t(12, 3);
+ index();
+
+ IndexedLog olog;
+
+ add_dups(olog, example_dups_1());
+
+ bool changed = merge_log_dups(olog);
+
+ EXPECT_TRUE(changed);
+ EXPECT_EQ(4u, log.dups.size());
+
+ if (4 == log.dups.size()) {
+ EXPECT_EQ(10u, log.dups.front().version.epoch);
+ EXPECT_EQ(11u, log.dups.front().version.version);
+
+ EXPECT_EQ(12u, log.dups.back().version.epoch);
+ EXPECT_EQ(3u, log.dups.back().version.version);
+ }
+
+ check_order();
+ check_index();
+}
+
+TEST_F(PGLogMergeDupsTest, Same) {
+ log.tail = eversion_t(14, 1);
+
+ IndexedLog olog;
+
+ add_dups(example_dups_1());
+ index();
+ add_dups(olog, example_dups_1());
+
+ bool changed = merge_log_dups(olog);
+
+ EXPECT_FALSE(changed);
+ EXPECT_EQ(5u, log.dups.size());
+
+ if (5 == log.dups.size()) {
+ EXPECT_EQ(10u, log.dups.front().version.epoch);
+ EXPECT_EQ(11u, log.dups.front().version.version);
+
+ EXPECT_EQ(13u, log.dups.back().version.epoch);
+ EXPECT_EQ(99u, log.dups.back().version.version);
+ }
+
+ check_order();
+ check_index();
+}
+
+
+TEST_F(PGLogMergeDupsTest, Later) {
+ log.tail = eversion_t(16, 14);
+
+ IndexedLog olog;
+
+ add_dups(example_dups_1());
+ index();
+ add_dups(olog, example_dups_2());
+
+ bool changed = merge_log_dups(olog);
+
+ EXPECT_TRUE(changed);
+ EXPECT_EQ(7u, log.dups.size());
+
+ if (7 == log.dups.size()) {
+ EXPECT_EQ(10u, log.dups.front().version.epoch);
+ EXPECT_EQ(11u, log.dups.front().version.version);
+
+ EXPECT_EQ(16u, log.dups.back().version.epoch);
+ EXPECT_EQ(14u, log.dups.back().version.version);
+ }
+
+ check_order();
+ check_index();
+}
+
+
+TEST_F(PGLogMergeDupsTest, Earlier) {
+ log.tail = eversion_t(17, 2);
+
+ IndexedLog olog;
+
+ add_dups(example_dups_2());
+ index();
+ add_dups(olog, example_dups_1());
+
+ bool changed = merge_log_dups(olog);
+
+ EXPECT_TRUE(changed);
+ EXPECT_EQ(8u, log.dups.size());
+
+ if (6 == log.dups.size()) {
+ EXPECT_EQ(10u, log.dups.front().version.epoch);
+ EXPECT_EQ(11u, log.dups.front().version.version);
+
+ EXPECT_EQ(16u, log.dups.back().version.epoch);
+ EXPECT_EQ(32u, log.dups.back().version.version);
+ }
+
+ check_order();
+ check_index();
+}
+
+
+TEST_F(PGLogMergeDupsTest, Superset) {
+ log.tail = eversion_t(17, 2);
+
+ IndexedLog olog;
+
+ add_dups(example_dups_1());
+ index();
+
+ olog.dups.push_back(create_dup_entry(9, 5));
+ olog.dups.push_back(create_dup_entry(15, 11));
+
+ bool changed = merge_log_dups(olog);
+
+ EXPECT_TRUE(changed);
+ EXPECT_EQ(7u, log.dups.size());
+
+ if (7 == log.dups.size()) {
+ EXPECT_EQ(9u, log.dups.front().version.epoch);
+ EXPECT_EQ(5u, log.dups.front().version.version);
+
+ EXPECT_EQ(15u, log.dups.back().version.epoch);
+ EXPECT_EQ(11u, log.dups.back().version.version);
+ }
+
+ check_order();
+ check_index();
+}
+
+
+struct PGLogTrimTest :
+ public ::testing::Test,
+ public PGLogTestBase,
+ public PGLog::IndexedLog
+{
+ CephContext *cct = g_ceph_context;
+
+ using ::testing::Test::SetUp;
+ void SetUp(unsigned dup_track) {
+ constexpr size_t size = 10;
+
+ char dup_track_s[size];
+
+ snprintf(dup_track_s, size, "%u", dup_track);
+
+ cct->_conf.set_val_or_die("osd_pg_log_dups_tracked", dup_track_s);
+ }
+}; // struct PGLogTrimTest
+
+
+TEST_F(PGLogTrimTest, TestMakingCephContext)
+{
+ SetUp(5);
+
+ EXPECT_EQ(5u, cct->_conf->osd_pg_log_dups_tracked);
+}
+
+
+TEST_F(PGLogTrimTest, TestPartialTrim)
+{
+ SetUp(20);
+ PGLog::IndexedLog log;
+ log.head = mk_evt(24, 0);
+ log.skip_can_rollback_to_to_head();
+ log.head = mk_evt(9, 0);
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 150), mk_evt(10, 100)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 155), mk_evt(15, 150)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(19, 160), mk_evt(25, 152)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 165), mk_evt(26, 160)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 167), mk_evt(31, 166)));
+
+ std::set<eversion_t> trimmed;
+ std::set<std::string> trimmed_dups;
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(19, 157), &trimmed, &trimmed_dups, &write_from_dups);
+
+ EXPECT_EQ(eversion_t(15, 150), write_from_dups);
+ EXPECT_EQ(3u, log.log.size());
+ EXPECT_EQ(3u, trimmed.size());
+ EXPECT_EQ(2u, log.dups.size());
+ EXPECT_EQ(0u, trimmed_dups.size());
+
+ SetUp(15);
+
+ std::set<eversion_t> trimmed2;
+ std::set<std::string> trimmed_dups2;
+ eversion_t write_from_dups2 = eversion_t::max();
+
+ log.trim(cct, mk_evt(20, 164), &trimmed2, &trimmed_dups2, &write_from_dups2);
+
+ EXPECT_EQ(eversion_t(19, 160), write_from_dups2);
+ EXPECT_EQ(2u, log.log.size());
+ EXPECT_EQ(1u, trimmed2.size());
+ EXPECT_EQ(3u, log.dups.size());
+ EXPECT_EQ(0u, trimmed_dups2.size());
+}
+
+
+TEST_F(PGLogTrimTest, TestTrimNoTrimmed) {
+ SetUp(20);
+ PGLog::IndexedLog log;
+ log.head = mk_evt(20, 0);
+ log.skip_can_rollback_to_to_head();
+ log.head = mk_evt(9, 0);
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 150), mk_evt(10, 100)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 155), mk_evt(15, 150)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 160), mk_evt(25, 152)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 165), mk_evt(26, 160)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 167), mk_evt(31, 166)));
+
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(19, 157), nullptr, nullptr, &write_from_dups);
+
+ EXPECT_EQ(eversion_t(15, 150), write_from_dups);
+ EXPECT_EQ(3u, log.log.size());
+ EXPECT_EQ(2u, log.dups.size());
+}
+
+
+TEST_F(PGLogTrimTest, TestTrimNoDups)
+{
+ SetUp(10);
+ PGLog::IndexedLog log;
+ log.head = mk_evt(20, 0);
+ log.skip_can_rollback_to_to_head();
+ log.head = mk_evt(9, 0);
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 150), mk_evt(10, 100)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 155), mk_evt(15, 150)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 160), mk_evt(25, 152)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 165), mk_evt(26, 160)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 167), mk_evt(31, 166)));
+
+ std::set<eversion_t> trimmed;
+ std::set<std::string> trimmed_dups;
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(19, 157), &trimmed, &trimmed_dups, &write_from_dups);
+
+ EXPECT_EQ(eversion_t::max(), write_from_dups);
+ EXPECT_EQ(3u, log.log.size());
+ EXPECT_EQ(3u, trimmed.size());
+ EXPECT_EQ(0u, log.dups.size());
+ EXPECT_EQ(0u, trimmed_dups.size());
+}
+
+TEST_F(PGLogTrimTest, TestNoTrim)
+{
+ SetUp(20);
+ PGLog::IndexedLog log;
+ log.head = mk_evt(24, 0);
+ log.skip_can_rollback_to_to_head();
+ log.head = mk_evt(9, 0);
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 150), mk_evt(10, 100)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 155), mk_evt(15, 150)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(19, 160), mk_evt(25, 152)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 165), mk_evt(26, 160)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 167), mk_evt(31, 166)));
+
+ std::set<eversion_t> trimmed;
+ std::set<std::string> trimmed_dups;
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(9, 99), &trimmed, &trimmed_dups, &write_from_dups);
+
+ EXPECT_EQ(eversion_t::max(), write_from_dups);
+ EXPECT_EQ(6u, log.log.size());
+ EXPECT_EQ(0u, trimmed.size());
+ EXPECT_EQ(0u, log.dups.size());
+ EXPECT_EQ(0u, trimmed_dups.size());
+}
+
+TEST_F(PGLogTrimTest, TestTrimAll)
+{
+ SetUp(20);
+ PGLog::IndexedLog log;
+ EXPECT_EQ(0u, log.dup_index.size()); // Sanity check
+ log.head = mk_evt(24, 0);
+ log.skip_can_rollback_to_to_head();
+ log.head = mk_evt(9, 0);
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 150), mk_evt(10, 100)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 155), mk_evt(15, 150)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(19, 160), mk_evt(25, 152)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 165), mk_evt(26, 160)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 167), mk_evt(31, 166)));
+
+ std::set<eversion_t> trimmed;
+ std::set<std::string> trimmed_dups;
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(22, 180), &trimmed, &trimmed_dups, &write_from_dups);
+
+ EXPECT_EQ(eversion_t(15, 150), write_from_dups);
+ EXPECT_EQ(0u, log.log.size());
+ EXPECT_EQ(6u, trimmed.size());
+ EXPECT_EQ(5u, log.dups.size());
+ EXPECT_EQ(0u, trimmed_dups.size());
+ EXPECT_EQ(0u, log.dup_index.size()); // dup_index entry should be trimmed
+}
+
+
+TEST_F(PGLogTrimTest, TestGetRequest) {
+ SetUp(20);
+ PGLog::IndexedLog log;
+ log.head = mk_evt(20, 0);
+ log.skip_can_rollback_to_to_head();
+ log.head = mk_evt(9, 0);
+
+ entity_name_t client = entity_name_t::CLIENT(777);
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(8, 70),
+ osd_reqid_t(client, 8, 1)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 150), mk_evt(10, 100),
+ osd_reqid_t(client, 8, 2)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 155), mk_evt(15, 150),
+ osd_reqid_t(client, 8, 3)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 160), mk_evt(25, 152),
+ osd_reqid_t(client, 8, 4)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 165), mk_evt(26, 160),
+ osd_reqid_t(client, 8, 5)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 167), mk_evt(31, 166),
+ osd_reqid_t(client, 8, 6)));
+
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(19, 157), nullptr, nullptr, &write_from_dups);
+
+ EXPECT_EQ(eversion_t(15, 150), write_from_dups);
+ EXPECT_EQ(3u, log.log.size());
+ EXPECT_EQ(2u, log.dups.size());
+
+ eversion_t version;
+ version_t user_version;
+ int return_code;
+ vector<pg_log_op_return_item_t> op_returns;
+
+ osd_reqid_t log_reqid = osd_reqid_t(client, 8, 5);
+ osd_reqid_t dup_reqid = osd_reqid_t(client, 8, 3);
+ osd_reqid_t bad_reqid = osd_reqid_t(client, 8, 1);
+
+ bool result;
+
+ result = log.get_request(log_reqid, &version, &user_version, &return_code,
+ &op_returns);
+ EXPECT_EQ(true, result);
+ EXPECT_EQ(mk_evt(21, 165), version);
+
+ result = log.get_request(dup_reqid, &version, &user_version, &return_code,
+ &op_returns);
+ EXPECT_EQ(true, result);
+ EXPECT_EQ(mk_evt(15, 155), version);
+
+ result = log.get_request(bad_reqid, &version, &user_version, &return_code,
+ &op_returns);
+ EXPECT_FALSE(result);
+}
+
+TEST_F(PGLogTest, _merge_object_divergent_entries) {
+ {
+ // Test for issue 20843
+ clear();
+ hobject_t hoid(object_t(/*name*/"notify.7"),
+ /*key*/string(""),
+ /*snap*/7,
+ /*hash*/77,
+ /*pool*/5,
+ /*nspace*/string(""));
+ mempool::osd_pglog::list<pg_log_entry_t> orig_entries;
+ orig_entries.push_back(mk_ple_mod(hoid, eversion_t(8336, 957), eversion_t(8336, 952)));
+ orig_entries.push_back(mk_ple_err(hoid, eversion_t(8336, 958)));
+ orig_entries.push_back(mk_ple_err(hoid, eversion_t(8336, 959)));
+ orig_entries.push_back(mk_ple_mod(hoid, eversion_t(8336, 960), eversion_t(8336, 957)));
+ log.add(mk_ple_mod(hoid, eversion_t(8973, 1075), eversion_t(8971, 1070)));
+ missing.add(hoid,
+ /*need*/eversion_t(8971, 1070),
+ /*have*/eversion_t(8336, 952),
+ false);
+ pg_info_t oinfo;
+ LogHandler rollbacker;
+ _merge_object_divergent_entries(log, hoid,
+ orig_entries, oinfo,
+ log.get_can_rollback_to(),
+ missing, &rollbacker,
+ this);
+ // No core dump
+ }
+ {
+ // skip leading error entries
+ clear();
+ hobject_t hoid(object_t(/*name*/"notify.7"),
+ /*key*/string(""),
+ /*snap*/7,
+ /*hash*/77,
+ /*pool*/5,
+ /*nspace*/string(""));
+ mempool::osd_pglog::list<pg_log_entry_t> orig_entries;
+ orig_entries.push_back(mk_ple_err(hoid, eversion_t(8336, 956)));
+ orig_entries.push_back(mk_ple_mod(hoid, eversion_t(8336, 957), eversion_t(8336, 952)));
+ log.add(mk_ple_mod(hoid, eversion_t(8973, 1075), eversion_t(8971, 1070)));
+ missing.add(hoid,
+ /*need*/eversion_t(8971, 1070),
+ /*have*/eversion_t(8336, 952),
+ false);
+ pg_info_t oinfo;
+ LogHandler rollbacker;
+ _merge_object_divergent_entries(log, hoid,
+ orig_entries, oinfo,
+ log.get_can_rollback_to(),
+ missing, &rollbacker,
+ this);
+ // No core dump
+ }
+}
+
+TEST(eversion_t, get_key_name) {
+ eversion_t a(1234, 5678);
+ std::string a_key_name = a.get_key_name();
+ EXPECT_EQ("0000001234.00000000000000005678", a_key_name);
+}
+
+TEST(pg_log_dup_t, get_key_name) {
+ pg_log_dup_t a(eversion_t(1234, 5678),
+ 13,
+ osd_reqid_t(entity_name_t::CLIENT(777), 8, 999),
+ 15);
+ std::string a_key_name = a.get_key_name();
+ EXPECT_EQ("dup_0000001234.00000000000000005678", a_key_name);
+}
+
+
+// This tests trim() to make copies of
+// 2 log entries (107, 106) and 3 additional for a total
+// of 5 dups. Nothing from the original dups is copied.
+TEST_F(PGLogTrimTest, TestTrimDups) {
+ SetUp(5);
+ PGLog::IndexedLog log;
+ log.head = mk_evt(21, 107);
+ log.skip_can_rollback_to_to_head();
+ log.tail = mk_evt(9, 99);
+ log.head = mk_evt(9, 99);
+
+ entity_name_t client = entity_name_t::CLIENT(777);
+
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(9, 99), mk_evt(8, 98), osd_reqid_t(client, 8, 1))));
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(9, 99),
+ osd_reqid_t(client, 8, 1)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 101), mk_evt(10, 100),
+ osd_reqid_t(client, 8, 2)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 102), mk_evt(15, 101),
+ osd_reqid_t(client, 8, 3)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 103), mk_evt(15, 102),
+ osd_reqid_t(client, 8, 4)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 104), mk_evt(20, 103),
+ osd_reqid_t(client, 8, 5)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 105), mk_evt(21, 104),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 106), mk_evt(21, 105),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 107), mk_evt(21, 106),
+ osd_reqid_t(client, 8, 6)));
+
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(21, 105), nullptr, nullptr, &write_from_dups);
+
+ EXPECT_EQ(eversion_t(20, 103), write_from_dups) << log;
+ EXPECT_EQ(2u, log.log.size()) << log;
+ EXPECT_EQ(4u, log.dups.size()) << log;
+}
+
+// This tests trim() to make copies of
+// 4 log entries (107, 106, 105, 104) and 5 additional for a total
+// of 9 dups. Only 1 of 2 existing dups are copied.
+TEST_F(PGLogTrimTest, TestTrimDups2) {
+ SetUp(9);
+ PGLog::IndexedLog log;
+ log.head = mk_evt(21, 107);
+ log.skip_can_rollback_to_to_head();
+ log.tail = mk_evt(9, 99);
+ log.head = mk_evt(9, 99);
+
+ entity_name_t client = entity_name_t::CLIENT(777);
+
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(9, 98), mk_evt(8, 97), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(9, 99), mk_evt(8, 98), osd_reqid_t(client, 8, 1))));
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(9, 99),
+ osd_reqid_t(client, 8, 1)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 101), mk_evt(10, 100),
+ osd_reqid_t(client, 8, 2)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 102), mk_evt(15, 101),
+ osd_reqid_t(client, 8, 3)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 103), mk_evt(15, 102),
+ osd_reqid_t(client, 8, 4)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 104), mk_evt(20, 103),
+ osd_reqid_t(client, 8, 5)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 105), mk_evt(21, 104),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 106), mk_evt(21, 105),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 107), mk_evt(21, 106),
+ osd_reqid_t(client, 8, 6)));
+
+ eversion_t write_from_dups = eversion_t::max();
+
+ log.trim(cct, mk_evt(20, 103), nullptr, nullptr, &write_from_dups);
+
+ EXPECT_EQ(eversion_t(10, 100), write_from_dups) << log;
+ EXPECT_EQ(4u, log.log.size()) << log;
+ EXPECT_EQ(6u, log.dups.size()) << log;
+}
+
+// This tests copy_up_to() to make copies of
+// 2 log entries (107, 106) and 3 additional for a total
+// of 5 dups. Nothing from the original dups is copied.
+TEST_F(PGLogTrimTest, TestCopyUpTo) {
+ SetUp(5);
+ PGLog::IndexedLog log, copy;
+ log.tail = mk_evt(9, 99);
+ log.head = mk_evt(9, 99);
+
+ entity_name_t client = entity_name_t::CLIENT(777);
+
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(9, 99), mk_evt(8, 98), osd_reqid_t(client, 8, 1))));
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(9, 99),
+ osd_reqid_t(client, 8, 1)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 101), mk_evt(10, 100),
+ osd_reqid_t(client, 8, 2)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 102), mk_evt(15, 101),
+ osd_reqid_t(client, 8, 3)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 103), mk_evt(15, 102),
+ osd_reqid_t(client, 8, 4)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 104), mk_evt(20, 103),
+ osd_reqid_t(client, 8, 5)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 105), mk_evt(21, 104),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 106), mk_evt(21, 105),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 107), mk_evt(21, 106),
+ osd_reqid_t(client, 8, 6)));
+
+ copy.copy_up_to(cct, log, 2);
+
+ EXPECT_EQ(2u, copy.log.size()) << copy;
+ EXPECT_EQ(copy.head, mk_evt(21, 107)) << copy;
+ EXPECT_EQ(copy.tail, mk_evt(21, 105)) << copy;
+ // Tracking 5 means 3 additional as dups
+ EXPECT_EQ(3u, copy.dups.size()) << copy;
+}
+
+// This tests copy_up_to() to make copies of
+// 4 log entries (107, 106, 105, 104) and 5 additional for a total
+// of 5 dups. Only 1 of 2 existing dups are copied.
+TEST_F(PGLogTrimTest, TestCopyUpTo2) {
+ SetUp(9);
+ PGLog::IndexedLog log, copy;
+ log.tail = mk_evt(9, 99);
+ log.head = mk_evt(9, 99);
+
+ entity_name_t client = entity_name_t::CLIENT(777);
+
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(8, 98), mk_evt(8, 97), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(9, 99), mk_evt(8, 98), osd_reqid_t(client, 8, 1))));
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(9, 99),
+ osd_reqid_t(client, 8, 1)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 101), mk_evt(10, 100),
+ osd_reqid_t(client, 8, 2)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 102), mk_evt(15, 101),
+ osd_reqid_t(client, 8, 3)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 103), mk_evt(15, 102),
+ osd_reqid_t(client, 8, 4)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 104), mk_evt(20, 103),
+ osd_reqid_t(client, 8, 5)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 105), mk_evt(21, 104),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 106), mk_evt(21, 105),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 107), mk_evt(21, 106),
+ osd_reqid_t(client, 8, 6)));
+
+ copy.copy_up_to(cct, log, 4);
+
+ EXPECT_EQ(4u, copy.log.size()) << copy;
+ EXPECT_EQ(copy.head, mk_evt(21, 107)) << copy;
+ EXPECT_EQ(copy.tail, mk_evt(20, 103)) << copy;
+ // Tracking 5 means 3 additional as dups
+ EXPECT_EQ(5u, copy.dups.size()) << copy;
+}
+
+// This tests copy_after() by specifying a version that copies
+// 2 log entries (107, 106) and 3 additional for a total
+// of 5 dups. Nothing of the original dups is copied.
+TEST_F(PGLogTrimTest, TestCopyAfter) {
+ SetUp(5);
+ PGLog::IndexedLog log, copy;
+ log.tail = mk_evt(9, 99);
+ log.head = mk_evt(9, 99);
+
+ entity_name_t client = entity_name_t::CLIENT(777);
+
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(9, 99), mk_evt(8, 98), osd_reqid_t(client, 8, 1))));
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(9, 99),
+ osd_reqid_t(client, 8, 1)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 101), mk_evt(10, 100),
+ osd_reqid_t(client, 8, 2)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 102), mk_evt(15, 101),
+ osd_reqid_t(client, 8, 3)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 103), mk_evt(15, 102),
+ osd_reqid_t(client, 8, 4)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 104), mk_evt(20, 103),
+ osd_reqid_t(client, 8, 5)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 105), mk_evt(21, 104),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 106), mk_evt(21, 105),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 107), mk_evt(21, 106),
+ osd_reqid_t(client, 8, 6)));
+
+ copy.copy_after(cct, log, mk_evt(21, 105));
+
+ EXPECT_EQ(2u, copy.log.size()) << copy;
+ EXPECT_EQ(copy.head, mk_evt(21, 107)) << copy;
+ EXPECT_EQ(copy.tail, mk_evt(21, 105)) << copy;
+ // Tracking 5 means 3 additional as dups
+ EXPECT_EQ(3u, copy.dups.size()) << copy;
+}
+
+// This copies everything dups and log because of the large max dups
+// and value passed to copy_after().
+TEST_F(PGLogTrimTest, TestCopyAfter2) {
+ SetUp(3000);
+ PGLog::IndexedLog log, copy;
+ log.tail = mk_evt(9, 99);
+ log.head = mk_evt(9, 99);
+
+ entity_name_t client = entity_name_t::CLIENT(777);
+
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(8, 93), mk_evt(8, 92), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(8, 94), mk_evt(8, 93), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(8, 95), mk_evt(8, 94), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(8, 96), mk_evt(8, 95), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(8, 97), mk_evt(8, 96), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(8, 98), mk_evt(8, 97), osd_reqid_t(client, 8, 1))));
+ log.dups.push_back(pg_log_dup_t(mk_ple_mod(mk_obj(1),
+ mk_evt(9, 99), mk_evt(8, 98), osd_reqid_t(client, 8, 1))));
+
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(10, 100), mk_evt(9, 99),
+ osd_reqid_t(client, 8, 1)));
+ log.add(mk_ple_dt(mk_obj(2), mk_evt(15, 101), mk_evt(10, 100),
+ osd_reqid_t(client, 8, 2)));
+ log.add(mk_ple_mod_rb(mk_obj(3), mk_evt(15, 102), mk_evt(15, 101),
+ osd_reqid_t(client, 8, 3)));
+ log.add(mk_ple_mod(mk_obj(1), mk_evt(20, 103), mk_evt(15, 102),
+ osd_reqid_t(client, 8, 4)));
+ log.add(mk_ple_mod(mk_obj(4), mk_evt(21, 104), mk_evt(20, 103),
+ osd_reqid_t(client, 8, 5)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 105), mk_evt(21, 104),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 106), mk_evt(21, 105),
+ osd_reqid_t(client, 8, 6)));
+ log.add(mk_ple_dt_rb(mk_obj(5), mk_evt(21, 107), mk_evt(21, 106),
+ osd_reqid_t(client, 8, 6)));
+
+ copy.copy_after(cct, log, mk_evt(9, 99));
+
+ EXPECT_EQ(8u, copy.log.size()) << copy;
+ EXPECT_EQ(copy.head, mk_evt(21, 107)) << copy;
+ EXPECT_EQ(copy.tail, mk_evt(9, 99)) << copy;
+ // Tracking 3000 is larger than all entries, so all dups copied
+ EXPECT_EQ(7u, copy.dups.size()) << copy;
+}
+
+// Local Variables:
+// compile-command: "cd ../.. ; make unittest_pglog ; ./unittest_pglog --log-to-stderr=true --debug-osd=20 # --gtest_filter=*.* "
+// End:
diff --git a/src/test/osd/TestRados.cc b/src/test/osd/TestRados.cc
new file mode 100644
index 000000000..1a1389b2a
--- /dev/null
+++ b/src/test/osd/TestRados.cc
@@ -0,0 +1,729 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "common/version.h"
+
+#include <iostream>
+#include <sstream>
+#include <map>
+#include <numeric>
+#include <string>
+#include <vector>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "test/osd/RadosModel.h"
+
+using namespace std;
+
+class WeightedTestGenerator : public TestOpGenerator
+{
+public:
+
+ WeightedTestGenerator(int ops,
+ int objects,
+ map<TestOpType, unsigned int> op_weights,
+ TestOpStat *stats,
+ int max_seconds,
+ bool ec_pool,
+ bool balance_reads,
+ bool localize_reads,
+ bool set_redirect,
+ bool set_chunk,
+ bool enable_dedup) :
+ m_nextop(NULL), m_op(0), m_ops(ops), m_seconds(max_seconds),
+ m_objects(objects), m_stats(stats),
+ m_total_weight(0),
+ m_ec_pool(ec_pool),
+ m_balance_reads(balance_reads),
+ m_localize_reads(localize_reads),
+ m_set_redirect(set_redirect),
+ m_set_chunk(set_chunk),
+ m_enable_dedup(enable_dedup)
+ {
+ m_start = time(0);
+ for (map<TestOpType, unsigned int>::const_iterator it = op_weights.begin();
+ it != op_weights.end();
+ ++it) {
+ m_total_weight += it->second;
+ m_weight_sums.insert(pair<TestOpType, unsigned int>(it->first,
+ m_total_weight));
+ }
+ if (m_set_redirect || m_set_chunk) {
+ if (m_set_redirect) {
+ m_ops = ops+m_objects+m_objects;
+ } else {
+ /* create 10 chunks per an object*/
+ m_ops = ops+m_objects+m_objects*10;
+ }
+ }
+ }
+
+ TestOp *next(RadosTestContext &context) override
+ {
+ TestOp *retval = NULL;
+
+ ++m_op;
+ if (m_op <= m_objects && !m_set_redirect && !m_set_chunk ) {
+ stringstream oid;
+ oid << m_op;
+ /*if (m_op % 2) {
+ // make it a long name
+ oid << " " << string(300, 'o');
+ }*/
+ cout << m_op << ": write initial oid " << oid.str() << std::endl;
+ context.oid_not_flushing.insert(oid.str());
+ if (m_ec_pool) {
+ return new WriteOp(m_op, &context, oid.str(), true, true);
+ } else {
+ return new WriteOp(m_op, &context, oid.str(), false, true);
+ }
+ } else if (m_op >= m_ops) {
+ return NULL;
+ }
+
+ if (m_set_redirect || m_set_chunk) {
+ if (init_extensible_tier(context, retval)) {
+ return retval;
+ }
+ }
+
+ if (m_nextop) {
+ retval = m_nextop;
+ m_nextop = NULL;
+ return retval;
+ }
+
+ while (retval == NULL) {
+ unsigned int rand_val = rand() % m_total_weight;
+
+ time_t now = time(0);
+ if (m_seconds && now - m_start > m_seconds)
+ break;
+
+ for (map<TestOpType, unsigned int>::const_iterator it = m_weight_sums.begin();
+ it != m_weight_sums.end();
+ ++it) {
+ if (rand_val < it->second) {
+ retval = gen_op(context, it->first);
+ break;
+ }
+ }
+ }
+ return retval;
+ }
+
+ bool init_extensible_tier(RadosTestContext &context, TestOp *& op) {
+ /*
+ * set-redirect or set-chunk test (manifest test)
+ * 0. make default objects (using create op)
+ * 1. set-redirect or set-chunk
+ * 2. initialize target objects (using write op)
+ * 3. wait for set-* completion
+ */
+ int copy_manifest_end = 0;
+ if (m_set_chunk) {
+ copy_manifest_end = m_objects*2;
+ } else {
+ copy_manifest_end = m_objects*3;
+ }
+ int make_manifest_end = copy_manifest_end;
+ if (m_set_chunk) {
+ /* make 10 chunks per an object*/
+ make_manifest_end = make_manifest_end + m_objects * 10;
+ } else {
+ /* redirect */
+ make_manifest_end = make_manifest_end + m_objects;
+ }
+
+ if (m_op <= m_objects) {
+ stringstream oid;
+ oid << m_op;
+ /*if (m_op % 2) {
+ oid << " " << string(300, 'o');
+ }*/
+ cout << m_op << ": write initial oid " << oid.str() << std::endl;
+ context.oid_not_flushing.insert(oid.str());
+ if (m_ec_pool) {
+ op = new WriteOp(m_op, &context, oid.str(), true, true);
+ } else {
+ op = new WriteOp(m_op, &context, oid.str(), false, true);
+ }
+ return true;
+ } else if (m_op <= copy_manifest_end) {
+ stringstream oid, oid2;
+ //int _oid = m_op-m_objects;
+ int _oid = m_op % m_objects + 1;
+ oid << _oid;
+ /*if ((_oid) % 2) {
+ oid << " " << string(300, 'o');
+ }*/
+
+ if (context.oid_in_use.count(oid.str())) {
+ /* previous write is not finished */
+ op = NULL;
+ m_op--;
+ cout << m_op << " wait for completion of write op! " << std::endl;
+ return true;
+ }
+
+ int _oid2 = m_op - m_objects + 1;
+ if (_oid2 > copy_manifest_end - m_objects) {
+ _oid2 -= (copy_manifest_end - m_objects);
+ }
+ oid2 << _oid2 << " " << context.low_tier_pool_name;
+ if ((_oid2) % 2) {
+ oid2 << " " << string(300, 'm');
+ }
+ cout << m_op << ": " << "copy oid " << oid.str() << " target oid "
+ << oid2.str() << std::endl;
+ op = new CopyOp(m_op, &context, oid.str(), oid2.str(), context.low_tier_pool_name);
+ return true;
+ } else if (m_op <= make_manifest_end) {
+ if (m_set_redirect) {
+ stringstream oid, oid2;
+ int _oid = m_op-copy_manifest_end;
+ oid << _oid;
+ /*if ((_oid) % 2) {
+ oid << " " << string(300, 'o');
+ }*/
+ oid2 << _oid << " " << context.low_tier_pool_name;
+ if ((_oid) % 2) {
+ oid2 << " " << string(300, 'm');
+ }
+ if (context.oid_in_use.count(oid.str())) {
+ /* previous copy is not finished */
+ op = NULL;
+ m_op--;
+ cout << m_op << " retry set_redirect !" << std::endl;
+ return true;
+ }
+ cout << m_op << ": " << "set_redirect oid " << oid.str() << " target oid "
+ << oid2.str() << std::endl;
+ op = new SetRedirectOp(m_op, &context, oid.str(), oid2.str(), context.pool_name);
+ return true;
+ } else if (m_set_chunk) {
+ stringstream oid;
+ int _oid = m_op % m_objects +1;
+ oid << _oid;
+ /*if ((_oid) % 2) {
+ oid << " " << string(300, 'o');
+ }*/
+ if (context.oid_in_use.count(oid.str())) {
+ /* previous set-chunk is not finished */
+ op = NULL;
+ m_op--;
+ cout << m_op << " retry set_chunk !" << std::endl;
+ return true;
+ }
+ stringstream oid2;
+ oid2 << _oid << " " << context.low_tier_pool_name;
+ if ((_oid) % 2) {
+ oid2 << " " << string(300, 'm');
+ }
+
+ cout << m_op << ": " << "set_chunk oid " << oid.str()
+ << " target oid " << oid2.str() << std::endl;
+ op = new SetChunkOp(m_op, &context, oid.str(), oid2.str(), m_stats);
+ return true;
+ }
+ } else if (m_op == make_manifest_end + 1) {
+ int set_size = context.oid_not_in_use.size();
+ int set_manifest_size = context.oid_redirect_not_in_use.size();
+ cout << m_op << " oid_not_in_use " << set_size << " oid_redirect_not_in_use " << set_manifest_size << std::endl;
+ /* wait for redirect or set_chunk initialization */
+ if (set_size != m_objects || set_manifest_size != 0) {
+ op = NULL;
+ m_op--;
+ cout << m_op << " wait for manifest initialization " << std::endl;
+ return true;
+ }
+ for (int t_op = m_objects+1; t_op <= m_objects*2; t_op++) {
+ stringstream oid;
+ oid << t_op << " " << context.low_tier_pool_name;
+ if (t_op % 2) {
+ oid << " " << string(300, 'm');
+ }
+ cout << " redirect_not_in_use: " << oid.str() << std::endl;
+ context.oid_redirect_not_in_use.insert(oid.str());
+ }
+ }
+
+ return false;
+ }
+
+private:
+
+ TestOp *gen_op(RadosTestContext &context, TestOpType type)
+ {
+ string oid, oid2;
+ ceph_assert(context.oid_not_in_use.size());
+
+ switch (type) {
+ case TEST_OP_READ:
+ oid = *(rand_choose(context.oid_not_in_use));
+ return new ReadOp(m_op, &context, oid, m_balance_reads, m_localize_reads,
+ m_stats);
+
+ case TEST_OP_WRITE:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "write oid " << oid << " current snap is "
+ << context.current_snap << std::endl;
+ return new WriteOp(m_op, &context, oid, false, false, m_stats);
+
+ case TEST_OP_WRITE_EXCL:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "write (excl) oid "
+ << oid << " current snap is "
+ << context.current_snap << std::endl;
+ return new WriteOp(m_op, &context, oid, false, true, m_stats);
+
+ case TEST_OP_WRITESAME:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "writesame oid "
+ << oid << " current snap is "
+ << context.current_snap << std::endl;
+ return new WriteSameOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_DELETE:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "delete oid " << oid << " current snap is "
+ << context.current_snap << std::endl;
+ return new DeleteOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_SNAP_CREATE:
+ cout << m_op << ": " << "snap_create" << std::endl;
+ return new SnapCreateOp(m_op, &context, m_stats);
+
+ case TEST_OP_SNAP_REMOVE:
+ if (context.snaps.size() <= context.snaps_in_use.size()) {
+ return NULL;
+ }
+ while (true) {
+ int snap = rand_choose(context.snaps)->first;
+ if (context.snaps_in_use.lookup(snap))
+ continue; // in use; try again!
+ cout << m_op << ": " << "snap_remove snap " << snap << std::endl;
+ return new SnapRemoveOp(m_op, &context, snap, m_stats);
+ }
+
+ case TEST_OP_ROLLBACK:
+ {
+ string oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "rollback oid " << oid << " current snap is "
+ << context.current_snap << std::endl;
+ return new RollbackOp(m_op, &context, oid);
+ }
+
+ case TEST_OP_SETATTR:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "setattr oid " << oid
+ << " current snap is " << context.current_snap << std::endl;
+ return new SetAttrsOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_RMATTR:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "rmattr oid " << oid
+ << " current snap is " << context.current_snap << std::endl;
+ return new RemoveAttrsOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_WATCH:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "watch oid " << oid
+ << " current snap is " << context.current_snap << std::endl;
+ return new WatchOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_COPY_FROM:
+ oid = *(rand_choose(context.oid_not_in_use));
+ do {
+ oid2 = *(rand_choose(context.oid_not_in_use));
+ } while (oid == oid2);
+ cout << m_op << ": " << "copy_from oid " << oid << " from oid " << oid2
+ << " current snap is " << context.current_snap << std::endl;
+ return new CopyFromOp(m_op, &context, oid, oid2, m_stats);
+
+ case TEST_OP_HIT_SET_LIST:
+ {
+ uint32_t hash = rjhash32(rand());
+ cout << m_op << ": " << "hit_set_list " << hash << std::endl;
+ return new HitSetListOp(m_op, &context, hash, m_stats);
+ }
+
+ case TEST_OP_UNDIRTY:
+ {
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "undirty oid " << oid << std::endl;
+ return new UndirtyOp(m_op, &context, oid, m_stats);
+ }
+
+ case TEST_OP_IS_DIRTY:
+ {
+ oid = *(rand_choose(context.oid_not_flushing));
+ return new IsDirtyOp(m_op, &context, oid, m_stats);
+ }
+
+ case TEST_OP_CACHE_FLUSH:
+ {
+ oid = *(rand_choose(context.oid_not_in_use));
+ return new CacheFlushOp(m_op, &context, oid, m_stats, true);
+ }
+
+ case TEST_OP_CACHE_TRY_FLUSH:
+ {
+ oid = *(rand_choose(context.oid_not_in_use));
+ return new CacheFlushOp(m_op, &context, oid, m_stats, false);
+ }
+
+ case TEST_OP_CACHE_EVICT:
+ {
+ oid = *(rand_choose(context.oid_not_in_use));
+ return new CacheEvictOp(m_op, &context, oid, m_stats);
+ }
+
+ case TEST_OP_APPEND:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << "append oid " << oid << " current snap is "
+ << context.current_snap << std::endl;
+ return new WriteOp(m_op, &context, oid, true, false, m_stats);
+
+ case TEST_OP_APPEND_EXCL:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << "append oid (excl) " << oid << " current snap is "
+ << context.current_snap << std::endl;
+ return new WriteOp(m_op, &context, oid, true, true, m_stats);
+
+ case TEST_OP_CHUNK_READ:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "chunk read oid " << oid << " target oid " << oid2 << std::endl;
+ return new ChunkReadOp(m_op, &context, oid, context.pool_name, false, m_stats);
+
+ case TEST_OP_TIER_PROMOTE:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "tier_promote oid " << oid << std::endl;
+ return new TierPromoteOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_TIER_FLUSH:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "tier_flush oid " << oid << std::endl;
+ return new TierFlushOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_SET_REDIRECT:
+ oid = *(rand_choose(context.oid_not_in_use));
+ oid2 = *(rand_choose(context.oid_redirect_not_in_use));
+ cout << m_op << ": " << "set_redirect oid " << oid << " target oid " << oid2 << std::endl;
+ return new SetRedirectOp(m_op, &context, oid, oid2, context.pool_name, m_stats);
+
+ case TEST_OP_UNSET_REDIRECT:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "unset_redirect oid " << oid << std::endl;
+ return new UnsetRedirectOp(m_op, &context, oid, m_stats);
+
+ case TEST_OP_SET_CHUNK:
+ {
+ ceph_assert(m_enable_dedup);
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "set_chunk oid " << oid
+ << " target oid " << std::endl;
+ return new SetChunkOp(m_op, &context, oid, "", m_stats);
+ }
+
+ case TEST_OP_TIER_EVICT:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "tier_evict oid " << oid << std::endl;
+ return new TierEvictOp(m_op, &context, oid, m_stats);
+
+ default:
+ cerr << m_op << ": Invalid op type " << type << std::endl;
+ ceph_abort();
+ return nullptr;
+ }
+ }
+
+ TestOp *m_nextop;
+ int m_op;
+ int m_ops;
+ int m_seconds;
+ int m_objects;
+ time_t m_start;
+ TestOpStat *m_stats;
+ map<TestOpType, unsigned int> m_weight_sums;
+ unsigned int m_total_weight;
+ bool m_ec_pool;
+ bool m_balance_reads;
+ bool m_localize_reads;
+ bool m_set_redirect;
+ bool m_set_chunk;
+ bool m_enable_dedup;
+};
+
+int main(int argc, char **argv)
+{
+ int ops = 1000;
+ int objects = 50;
+ int max_in_flight = 16;
+ int64_t size = 4000000; // 4 MB
+ int64_t min_stride_size = -1, max_stride_size = -1;
+ int max_seconds = 0;
+ bool pool_snaps = false;
+ bool write_fadvise_dontneed = false;
+
+ struct {
+ TestOpType op;
+ const char *name;
+ bool ec_pool_valid;
+ } op_types[] = {
+ { TEST_OP_READ, "read", true },
+ { TEST_OP_WRITE, "write", false },
+ { TEST_OP_WRITE_EXCL, "write_excl", false },
+ { TEST_OP_WRITESAME, "writesame", false },
+ { TEST_OP_DELETE, "delete", true },
+ { TEST_OP_SNAP_CREATE, "snap_create", true },
+ { TEST_OP_SNAP_REMOVE, "snap_remove", true },
+ { TEST_OP_ROLLBACK, "rollback", true },
+ { TEST_OP_SETATTR, "setattr", true },
+ { TEST_OP_RMATTR, "rmattr", true },
+ { TEST_OP_WATCH, "watch", true },
+ { TEST_OP_COPY_FROM, "copy_from", true },
+ { TEST_OP_HIT_SET_LIST, "hit_set_list", true },
+ { TEST_OP_IS_DIRTY, "is_dirty", true },
+ { TEST_OP_UNDIRTY, "undirty", true },
+ { TEST_OP_CACHE_FLUSH, "cache_flush", true },
+ { TEST_OP_CACHE_TRY_FLUSH, "cache_try_flush", true },
+ { TEST_OP_CACHE_EVICT, "cache_evict", true },
+ { TEST_OP_APPEND, "append", true },
+ { TEST_OP_APPEND_EXCL, "append_excl", true },
+ { TEST_OP_SET_REDIRECT, "set_redirect", true },
+ { TEST_OP_UNSET_REDIRECT, "unset_redirect", true },
+ { TEST_OP_CHUNK_READ, "chunk_read", true },
+ { TEST_OP_TIER_PROMOTE, "tier_promote", true },
+ { TEST_OP_TIER_FLUSH, "tier_flush", true },
+ { TEST_OP_SET_CHUNK, "set_chunk", true },
+ { TEST_OP_TIER_EVICT, "tier_evict", true },
+ { TEST_OP_READ /* grr */, NULL },
+ };
+
+ struct {
+ const char *name;
+ } chunk_algo_types[] = {
+ { "fastcdc" },
+ { "fixcdc" },
+ };
+
+ map<TestOpType, unsigned int> op_weights;
+ string pool_name = "rbd";
+ string low_tier_pool_name = "";
+ bool ec_pool = false;
+ bool no_omap = false;
+ bool no_sparse = false;
+ bool balance_reads = false;
+ bool localize_reads = false;
+ bool set_redirect = false;
+ bool set_chunk = false;
+ bool enable_dedup = false;
+ string chunk_algo = "";
+ string chunk_size = "";
+
+
+ for (int i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--max-ops") == 0)
+ ops = atoi(argv[++i]);
+ else if (strcmp(argv[i], "--pool") == 0)
+ pool_name = argv[++i];
+ else if (strcmp(argv[i], "--max-seconds") == 0)
+ max_seconds = atoi(argv[++i]);
+ else if (strcmp(argv[i], "--objects") == 0)
+ objects = atoi(argv[++i]);
+ else if (strcmp(argv[i], "--max-in-flight") == 0)
+ max_in_flight = atoi(argv[++i]);
+ else if (strcmp(argv[i], "--size") == 0)
+ size = atoi(argv[++i]);
+ else if (strcmp(argv[i], "--min-stride-size") == 0)
+ min_stride_size = atoi(argv[++i]);
+ else if (strcmp(argv[i], "--max-stride-size") == 0)
+ max_stride_size = atoi(argv[++i]);
+ else if (strcmp(argv[i], "--no-omap") == 0)
+ no_omap = true;
+ else if (strcmp(argv[i], "--no-sparse") == 0)
+ no_sparse = true;
+ else if (strcmp(argv[i], "--balance-reads") == 0)
+ balance_reads = true;
+ else if (strcmp(argv[i], "--localize-reads") == 0)
+ localize_reads = true;
+ else if (strcmp(argv[i], "--pool-snaps") == 0)
+ pool_snaps = true;
+ else if (strcmp(argv[i], "--write-fadvise-dontneed") == 0)
+ write_fadvise_dontneed = true;
+ else if (strcmp(argv[i], "--ec-pool") == 0) {
+ if (!op_weights.empty()) {
+ cerr << "--ec-pool must be specified prior to any ops" << std::endl;
+ exit(1);
+ }
+ ec_pool = true;
+ no_omap = true;
+ no_sparse = true;
+ } else if (strcmp(argv[i], "--op") == 0) {
+ i++;
+ if (i == argc) {
+ cerr << "Missing op after --op" << std::endl;
+ return 1;
+ }
+ int j;
+ for (j = 0; op_types[j].name; ++j) {
+ if (strcmp(op_types[j].name, argv[i]) == 0) {
+ break;
+ }
+ }
+ if (!op_types[j].name) {
+ cerr << "unknown op " << argv[i] << std::endl;
+ exit(1);
+ }
+ i++;
+ if (i == argc) {
+ cerr << "Weight unspecified." << std::endl;
+ return 1;
+ }
+ int weight = atoi(argv[i]);
+ if (weight < 0) {
+ cerr << "Weights must be nonnegative." << std::endl;
+ return 1;
+ } else if (weight > 0) {
+ if (ec_pool && !op_types[j].ec_pool_valid) {
+ cerr << "Error: cannot use op type " << op_types[j].name
+ << " with --ec-pool" << std::endl;
+ exit(1);
+ }
+ cout << "adding op weight " << op_types[j].name << " -> " << weight << std::endl;
+ op_weights.insert(pair<TestOpType, unsigned int>(op_types[j].op, weight));
+ }
+ } else if (strcmp(argv[i], "--set_redirect") == 0) {
+ set_redirect = true;
+ } else if (strcmp(argv[i], "--set_chunk") == 0) {
+ set_chunk = true;
+ } else if (strcmp(argv[i], "--low_tier_pool") == 0) {
+ /*
+ * disallow redirect or chunk object into the same pool
+ * to prevent the race. see https://github.com/ceph/ceph/pull/20096
+ */
+ low_tier_pool_name = argv[++i];
+ } else if (strcmp(argv[i], "--enable_dedup") == 0) {
+ enable_dedup = true;
+ } else if (strcmp(argv[i], "--dedup_chunk_algo") == 0) {
+ i++;
+ if (i == argc) {
+ cerr << "Missing chunking algorithm after --dedup_chunk_algo" << std::endl;
+ return 1;
+ }
+ int j;
+ for (j = 0; chunk_algo_types[j].name; ++j) {
+ if (strcmp(chunk_algo_types[j].name, argv[i]) == 0) {
+ break;
+ }
+ }
+ if (!chunk_algo_types[j].name) {
+ cerr << "unknown op " << argv[i] << std::endl;
+ exit(1);
+ }
+ chunk_algo = chunk_algo_types[j].name;
+ } else if (strcmp(argv[i], "--dedup_chunk_size") == 0) {
+ chunk_size = argv[++i];
+ } else {
+ cerr << "unknown arg " << argv[i] << std::endl;
+ exit(1);
+ }
+ }
+
+ if (set_redirect || set_chunk) {
+ if (low_tier_pool_name == "") {
+ cerr << "low_tier_pool is needed" << std::endl;
+ exit(1);
+ }
+ }
+
+ if (enable_dedup) {
+ if (chunk_algo == "" || chunk_size == "") {
+ cerr << "Missing chunking algorithm: " << chunk_algo
+ << " or chunking size: " << chunk_size << std::endl;
+ exit(1);
+ }
+ }
+
+ if (op_weights.empty()) {
+ cerr << "No operations specified" << std::endl;
+ exit(1);
+ }
+
+ if (min_stride_size < 0)
+ min_stride_size = size / 10;
+ if (max_stride_size < 0)
+ max_stride_size = size / 5;
+
+ cout << pretty_version_to_str() << std::endl;
+ cout << "Configuration:" << std::endl
+ << "\tNumber of operations: " << ops << std::endl
+ << "\tNumber of objects: " << objects << std::endl
+ << "\tMax in flight operations: " << max_in_flight << std::endl
+ << "\tObject size (in bytes): " << size << std::endl
+ << "\tWrite stride min: " << min_stride_size << std::endl
+ << "\tWrite stride max: " << max_stride_size << std::endl;
+
+ if (min_stride_size >= max_stride_size) {
+ cerr << "Error: max_stride_size must be more than min_stride_size"
+ << std::endl;
+ return 1;
+ }
+
+ if (min_stride_size > size || max_stride_size > size) {
+ cerr << "Error: min_stride_size and max_stride_size must be "
+ << "smaller than object size" << std::endl;
+ return 1;
+ }
+
+ if (max_in_flight * 2 > objects) {
+ cerr << "Error: max_in_flight must be <= than the number of objects / 2"
+ << std::endl;
+ return 1;
+ }
+
+ char *id = getenv("CEPH_CLIENT_ID");
+ RadosTestContext context(
+ pool_name,
+ max_in_flight,
+ size,
+ min_stride_size,
+ max_stride_size,
+ no_omap,
+ no_sparse,
+ pool_snaps,
+ write_fadvise_dontneed,
+ low_tier_pool_name,
+ enable_dedup,
+ chunk_algo,
+ chunk_size,
+ id);
+
+ TestOpStat stats;
+ WeightedTestGenerator gen = WeightedTestGenerator(
+ ops, objects,
+ op_weights, &stats, max_seconds,
+ ec_pool, balance_reads, localize_reads,
+ set_redirect, set_chunk, enable_dedup);
+ int r = context.init();
+ if (r < 0) {
+ cerr << "Error initializing rados test context: "
+ << cpp_strerror(r) << std::endl;
+ exit(1);
+ }
+ context.loop(&gen);
+ if (enable_dedup) {
+ if (!context.check_chunks_refcount(context.low_tier_io_ctx, context.io_ctx)) {
+ cerr << " Invalid refcount " << std::endl;
+ exit(1);
+ }
+ }
+
+ context.shutdown();
+ cerr << context.errors << " errors." << std::endl;
+ cerr << stats << std::endl;
+ return 0;
+}
diff --git a/src/test/osd/ceph_test_osd_stale_read.cc b/src/test/osd/ceph_test_osd_stale_read.cc
new file mode 100644
index 000000000..7ee1255ed
--- /dev/null
+++ b/src/test/osd/ceph_test_osd_stale_read.cc
@@ -0,0 +1,177 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "mds/mdstypes.h"
+#include "include/buffer.h"
+#include "include/rbd_types.h"
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "include/types.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "common/common_init.h"
+#include "common/Cond.h"
+#include "json_spirit/json_spirit.h"
+
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+
+using namespace std;
+using namespace librados;
+
+int get_primary_osd(Rados& rados, const string& pool_name,
+ const string& oid, int *pprimary)
+{
+ bufferlist inbl;
+ string cmd = string("{\"prefix\": \"osd map\",\"pool\":\"")
+ + pool_name
+ + string("\",\"object\": \"")
+ + oid
+ + string("\",\"format\": \"json\"}");
+ bufferlist outbl;
+ if (int r = rados.mon_command(cmd, inbl, &outbl, nullptr);
+ r < 0) {
+ return r;
+ }
+ string outstr(outbl.c_str(), outbl.length());
+ json_spirit::Value v;
+ if (!json_spirit::read(outstr, v)) {
+ cerr <<" unable to parse json " << outstr << std::endl;
+ return -1;
+ }
+
+ json_spirit::Object& o = v.get_obj();
+ for (json_spirit::Object::size_type i=0; i<o.size(); i++) {
+ json_spirit::Pair& p = o[i];
+ if (p.name_ == "acting_primary") {
+ cout << "primary = " << p.value_.get_int() << std::endl;
+ *pprimary = p.value_.get_int();
+ return 0;
+ }
+ }
+ cerr << "didn't find primary in " << outstr << std::endl;
+ return -1;
+}
+
+int fence_osd(Rados& rados, int osd)
+{
+ bufferlist inbl, outbl;
+ string cmd("{\"prefix\": \"injectargs\",\"injected_args\":["
+ "\"--ms-blackhole-osd\", "
+ "\"--ms-blackhole-mon\"]}");
+ return rados.osd_command(osd, cmd, inbl, &outbl, NULL);
+}
+
+int mark_down_osd(Rados& rados, int osd)
+{
+ bufferlist inbl, outbl;
+ string cmd("{\"prefix\": \"osd down\",\"ids\":[\"" +
+ stringify(osd) + "\"]}");
+ return rados.mon_command(cmd, inbl, &outbl, NULL);
+}
+
+TEST(OSD, StaleRead) {
+ // create two rados instances, one pool
+ Rados rados1, rados2;
+ IoCtx ioctx1, ioctx2;
+ int r;
+
+ r = rados1.init_with_context(g_ceph_context);
+ ASSERT_EQ(0, r);
+ r = rados1.connect();
+ ASSERT_EQ(0, r);
+
+ srand(time(0));
+ string pool_name = "read-hole-test-" + stringify(rand());
+ r = rados1.pool_create(pool_name.c_str());
+ ASSERT_EQ(0, r);
+
+ r = rados1.ioctx_create(pool_name.c_str(), ioctx1);
+ ASSERT_EQ(0, r);
+
+ r = rados2.init_with_context(g_ceph_context);
+ ASSERT_EQ(0, r);
+ r = rados2.connect();
+ ASSERT_EQ(0, r);
+ r = rados2.ioctx_create(pool_name.c_str(), ioctx2);
+ ASSERT_EQ(0, r);
+
+ string oid = "foo";
+ bufferlist one;
+ one.append("one");
+ {
+ cout << "client1: writing 'one'" << std::endl;
+ r = ioctx1.write_full(oid, one);
+ ASSERT_EQ(0, r);
+ }
+
+ // make sure 2 can read it
+ {
+ cout << "client2: reading 'one'" << std::endl;
+ bufferlist bl;
+ r = ioctx2.read(oid, bl, 3, 0);
+ ASSERT_EQ(3, r);
+ ASSERT_EQ('o', bl[0]);
+ ASSERT_EQ('n', bl[1]);
+ ASSERT_EQ('e', bl[2]);
+ }
+
+ // find the primary
+ int primary;
+ r = get_primary_osd(rados1, pool_name, oid, &primary);
+ ASSERT_EQ(0, r);
+
+ // fence it
+ cout << "client1: fencing primary" << std::endl;
+ fence_osd(rados1, primary);
+ mark_down_osd(rados1, primary);
+ rados1.wait_for_latest_osdmap();
+
+ // should still be able to read the old value on 2
+ {
+ cout << "client2: reading 'one' again from old primary" << std::endl;
+ bufferlist bl;
+ r = ioctx2.read(oid, bl, 3, 0);
+ ASSERT_EQ(3, r);
+ ASSERT_EQ('o', bl[0]);
+ ASSERT_EQ('n', bl[1]);
+ ASSERT_EQ('e', bl[2]);
+ }
+
+ // update object on 1
+ bufferlist two;
+ two.append("two");
+ {
+ cout << "client1: writing 'two' to new acting set" << std::endl;
+ r = ioctx1.write_full(oid, two);
+ ASSERT_EQ(0, r);
+ }
+
+ // make sure we can't still read the old value on 2
+ {
+ cout << "client2: reading again from old primary" << std::endl;
+ bufferlist bl;
+ r = ioctx2.read(oid, bl, 3, 0);
+ ASSERT_EQ(3, r);
+ ASSERT_EQ('t', bl[0]);
+ ASSERT_EQ('w', bl[1]);
+ ASSERT_EQ('o', bl[2]);
+ }
+
+ rados1.shutdown();
+ rados2.shutdown();
+}
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/osd/hitset.cc b/src/test/osd/hitset.cc
new file mode 100644
index 000000000..6234bdaba
--- /dev/null
+++ b/src/test/osd/hitset.cc
@@ -0,0 +1,197 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ * Copyright 2013 Inktank
+ */
+
+#include "gtest/gtest.h"
+#include "osd/HitSet.h"
+#include <iostream>
+
+class HitSetTestStrap {
+public:
+ HitSet *hitset;
+
+ explicit HitSetTestStrap(HitSet *h) : hitset(h) {}
+
+ void fill(unsigned count) {
+ char buf[50];
+ for (unsigned i = 0; i < count; ++i) {
+ sprintf(buf, "hitsettest_%u", i);
+ hobject_t obj(object_t(buf), "", 0, i, 0, "");
+ hitset->insert(obj);
+ }
+ EXPECT_EQ(count, hitset->insert_count());
+ }
+ void verify_fill(unsigned count) {
+ char buf[50];
+ for (unsigned i = 0; i < count; ++i) {
+ sprintf(buf, "hitsettest_%u", i);
+ hobject_t obj(object_t(buf), "", 0, i, 0, "");
+ EXPECT_TRUE(hitset->contains(obj));
+ }
+ }
+
+};
+
+class BloomHitSetTest : public testing::Test, public HitSetTestStrap {
+public:
+
+ BloomHitSetTest() : HitSetTestStrap(new HitSet(new BloomHitSet)) {}
+
+ void rebuild(double fp, uint64_t target, uint64_t seed) {
+ BloomHitSet::Params *bparams = new BloomHitSet::Params(fp, target, seed);
+ HitSet::Params param(bparams);
+ HitSet new_set(param);
+ *hitset = new_set;
+ }
+
+ BloomHitSet *get_hitset() { return static_cast<BloomHitSet*>(hitset->impl.get()); }
+};
+
+TEST_F(BloomHitSetTest, Params) {
+ BloomHitSet::Params params(0.01, 100, 5);
+ EXPECT_EQ(.01, params.get_fpp());
+ EXPECT_EQ((unsigned)100, params.target_size);
+ EXPECT_EQ((unsigned)5, params.seed);
+ params.set_fpp(0.1);
+ EXPECT_EQ(0.1, params.get_fpp());
+
+ bufferlist bl;
+ params.encode(bl);
+ BloomHitSet::Params p2;
+ auto iter = bl.cbegin();
+ p2.decode(iter);
+ EXPECT_EQ(0.1, p2.get_fpp());
+ EXPECT_EQ((unsigned)100, p2.target_size);
+ EXPECT_EQ((unsigned)5, p2.seed);
+}
+
+TEST_F(BloomHitSetTest, Construct) {
+ ASSERT_EQ(hitset->impl->get_type(), HitSet::TYPE_BLOOM);
+ // success!
+}
+
+TEST_F(BloomHitSetTest, Rebuild) {
+ rebuild(0.1, 100, 1);
+ ASSERT_EQ(hitset->impl->get_type(), HitSet::TYPE_BLOOM);
+}
+
+TEST_F(BloomHitSetTest, InsertsMatch) {
+ rebuild(0.1, 100, 1);
+ fill(50);
+ /*
+ * the approx unique count is atrocious on bloom filters. Empirical
+ * evidence suggests the current test will produce a value of 62
+ * regardless of hitset size
+ */
+ EXPECT_TRUE(hitset->approx_unique_insert_count() >= 50 &&
+ hitset->approx_unique_insert_count() <= 62);
+ verify_fill(50);
+ EXPECT_FALSE(hitset->is_full());
+}
+
+TEST_F(BloomHitSetTest, FillsUp) {
+ rebuild(0.1, 20, 1);
+ fill(20);
+ verify_fill(20);
+ EXPECT_TRUE(hitset->is_full());
+}
+
+TEST_F(BloomHitSetTest, RejectsNoMatch) {
+ rebuild(0.001, 100, 1);
+ fill(100);
+ verify_fill(100);
+ EXPECT_TRUE(hitset->is_full());
+
+ char buf[50];
+ int matches = 0;
+ for (int i = 100; i < 200; ++i) {
+ sprintf(buf, "hitsettest_%d", i);
+ hobject_t obj(object_t(buf), "", 0, i, 0, "");
+ if (hitset->contains(obj))
+ ++matches;
+ }
+ // we set a 1 in 1000 false positive; allow one in our 100
+ EXPECT_LT(matches, 2);
+}
+
+class ExplicitHashHitSetTest : public testing::Test, public HitSetTestStrap {
+public:
+
+ ExplicitHashHitSetTest() : HitSetTestStrap(new HitSet(new ExplicitHashHitSet)) {}
+
+ ExplicitHashHitSet *get_hitset() { return static_cast<ExplicitHashHitSet*>(hitset->impl.get()); }
+};
+
+TEST_F(ExplicitHashHitSetTest, Construct) {
+ ASSERT_EQ(hitset->impl->get_type(), HitSet::TYPE_EXPLICIT_HASH);
+ // success!
+}
+
+TEST_F(ExplicitHashHitSetTest, InsertsMatch) {
+ fill(50);
+ verify_fill(50);
+ EXPECT_EQ((unsigned)50, hitset->approx_unique_insert_count());
+ EXPECT_FALSE(hitset->is_full());
+}
+
+TEST_F(ExplicitHashHitSetTest, RejectsNoMatch) {
+ fill(100);
+ verify_fill(100);
+ EXPECT_FALSE(hitset->is_full());
+
+ char buf[50];
+ int matches = 0;
+ for (int i = 100; i < 200; ++i) {
+ sprintf(buf, "hitsettest_%d", i);
+ hobject_t obj(object_t(buf), "", 0, i, 0, "");
+ if (hitset->contains(obj)) {
+ ++matches;
+ }
+ }
+ EXPECT_EQ(matches, 0);
+}
+
+class ExplicitObjectHitSetTest : public testing::Test, public HitSetTestStrap {
+public:
+
+ ExplicitObjectHitSetTest() : HitSetTestStrap(new HitSet(new ExplicitObjectHitSet)) {}
+
+ ExplicitObjectHitSet *get_hitset() { return static_cast<ExplicitObjectHitSet*>(hitset->impl.get()); }
+};
+
+TEST_F(ExplicitObjectHitSetTest, Construct) {
+ ASSERT_EQ(hitset->impl->get_type(), HitSet::TYPE_EXPLICIT_OBJECT);
+ // success!
+}
+
+TEST_F(ExplicitObjectHitSetTest, InsertsMatch) {
+ fill(50);
+ verify_fill(50);
+ EXPECT_EQ((unsigned)50, hitset->approx_unique_insert_count());
+ EXPECT_FALSE(hitset->is_full());
+}
+
+TEST_F(ExplicitObjectHitSetTest, RejectsNoMatch) {
+ fill(100);
+ verify_fill(100);
+ EXPECT_FALSE(hitset->is_full());
+
+ char buf[50];
+ int matches = 0;
+ for (int i = 100; i < 200; ++i) {
+ sprintf(buf, "hitsettest_%d", i);
+ hobject_t obj(object_t(buf), "", 0, i, 0, "");
+ if (hitset->contains(obj)) {
+ ++matches;
+ }
+ }
+ EXPECT_EQ(matches, 0);
+}
diff --git a/src/test/osd/osdcap.cc b/src/test/osd/osdcap.cc
new file mode 100644
index 000000000..4d961a426
--- /dev/null
+++ b/src/test/osd/osdcap.cc
@@ -0,0 +1,1398 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2012 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+
+#include "include/stringify.h"
+#include "osd/OSDCap.h"
+
+#include "gtest/gtest.h"
+
+using namespace std;
+
+const char *parse_good[] = {
+ "allow *",
+ "allow r",
+ "allow rwx",
+ "allow r pool foo ",
+ "allow r pool=foo",
+ "allow wx pool taco",
+ "allow pool foo r",
+ "allow pool taco wx",
+ "allow wx pool taco object_prefix obj",
+ "allow wx pool taco object_prefix obj_with_underscores_and_no_quotes",
+ "allow pool taco object_prefix obj wx",
+ "allow pool taco object_prefix obj_with_underscores_and_no_quotes wx",
+ "allow rwx pool 'weird name'",
+ "allow rwx pool \"weird name with ''s\"",
+ "allow rwx pool foo, allow r pool bar",
+ "allow rwx pool foo ; allow r pool bar",
+ "allow rwx pool foo ;allow r pool bar",
+ "allow rwx pool foo; allow r pool bar",
+ "allow pool foo rwx, allow pool bar r",
+ "allow pool foo.froo.foo rwx, allow pool bar r",
+ "allow pool foo rwx ; allow pool bar r",
+ "allow pool foo rwx ;allow pool bar r",
+ "allow pool foo rwx; allow pool bar r",
+ "allow pool data rw, allow pool rbd rwx, allow pool images class rbd foo",
+ "allow class-read",
+ "allow class-write",
+ "allow class-read class-write",
+ "allow r class-read pool foo",
+ "allow rw class-read class-write pool foo",
+ "allow r class-read pool foo",
+ "allow pool bar rwx; allow pool baz r class-read",
+ "allow class foo",
+ "allow class clsname \"clsthingidon'tunderstand\"",
+ " allow rwx pool foo; allow r pool bar ",
+ " allow rwx pool foo; allow r pool bar ",
+ " allow pool foo rwx; allow pool bar r ",
+ " allow pool foo rwx; allow pool bar r ",
+ " allow wx pool taco",
+ "\tallow\nwx\tpool \n taco\t",
+ "allow class-read object_prefix rbd_children, allow pool libvirt-pool-test rwx",
+ "allow class-read object_prefix rbd-children, allow pool libvirt_pool_test rwx",
+ "allow pool foo namespace nfoo rwx, allow pool bar namespace=nbar r",
+ "allow pool foo namespace=nfoo rwx ; allow pool bar namespace=nbar r",
+ "allow pool foo namespace nfoo rwx ;allow pool bar namespace nbar r",
+ "allow pool foo namespace=nfoo rwx; allow pool bar namespace nbar object_prefix rbd r",
+ "allow rwx namespace=nfoo tag cephfs data=cephfs_a",
+ "allow rwx namespace foo tag cephfs data =cephfs_a",
+ "allow pool foo namespace=nfoo* rwx",
+ "allow pool foo namespace=\"\" rwx; allow pool bar namespace='' object_prefix rbd r",
+ "allow pool foo namespace \"\" rwx; allow pool bar namespace '' object_prefix rbd r",
+ "profile abc, profile abc pool=bar, profile abc pool=bar namespace=foo",
+ "allow rwx tag application key=value",
+ "allow rwx tag application key = value",
+ "allow rwx tag application key =value",
+ "allow rwx tag application key= value",
+ "allow rwx tag application key = value",
+ "allow all tag application all=all",
+ "allow rwx network 127.0.0.1/8",
+ "allow rwx network ::1/128",
+ "allow rwx network [ff::1]/128",
+ "profile foo network 127.0.0.1/8",
+ "allow rwx namespace foo tag cephfs data =cephfs_a network 127.0.0.1/8",
+ "allow pool foo rwx network 1.2.3.4/24",
+ 0
+};
+
+TEST(OSDCap, ParseGood) {
+ for (int i=0; parse_good[i]; i++) {
+ string str = parse_good[i];
+ OSDCap cap;
+ std::cout << "Testing good input: '" << str << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(str, &cout));
+ }
+}
+
+const char *parse_bad[] = {
+ "allow r poolfoo",
+ "allow r w",
+ "ALLOW r",
+ "allow rwx,",
+ "allow rwx x",
+ "allow r pool foo r",
+ "allow wwx pool taco",
+ "allow wwx pool taco^funny&chars",
+ "allow rwx pool 'weird name''",
+ "allow rwx object_prefix \"beforepool\" pool weird",
+ "allow rwx auid 123 pool asdf",
+ "allow xrwx pool foo,, allow r pool bar",
+ ";allow rwx pool foo rwx ; allow r pool bar",
+ "allow rwx pool foo ;allow r pool bar gibberish",
+ "allow rwx auid 123 pool asdf namespace=foo",
+ "allow rwx auid 123 namespace",
+ "allow rwx namespace",
+ "allow namespace",
+ "allow namespace=foo",
+ "allow namespace=f*oo",
+ "allow rwx auid 123 namespace asdf",
+ "allow wwx pool ''",
+ "allow rwx tag application key value",
+ "allow rwx auid 123",
+ "allow auid 123 rwx",
+ "allow r pool foo object_prefix blah ; allow w auid 5",
+ 0
+};
+
+TEST(OSDCap, ParseBad) {
+ for (int i=0; parse_bad[i]; i++) {
+ string str = parse_bad[i];
+ OSDCap cap;
+ std::cout << "Testing bad input: '" << str << "'" << std::endl;
+ ASSERT_FALSE(cap.parse(str, &cout));
+ }
+}
+
+TEST(OSDCap, AllowAll) {
+ OSDCap cap;
+ entity_addr_t addr;
+ ASSERT_FALSE(cap.allow_all());
+
+ ASSERT_TRUE(cap.parse("allow r", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow w", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow x", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rwx", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rw", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow rx", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow wx", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ cap.grants.clear();
+
+ ASSERT_TRUE(cap.parse("allow *", NULL));
+ ASSERT_TRUE(cap.allow_all());
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "anamespace", {}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "anamespace", {}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "anamespace", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "anamespace", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, true}}, addr));
+ // 'allow *' overrides allow list
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "anamespace", {}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "anamespace", {}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "anamespace", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "anamespace", {{"application", {{"key", "value"}}}}, "asdf", true, true, {{"cls", "", true, true, false}}, addr));
+}
+
+TEST(OSDCap, AllowPool) {
+ OSDCap cap;
+ entity_addr_t addr;
+ bool r = cap.parse("allow rwx pool foo", NULL);
+ ASSERT_TRUE(r);
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ // true->false for classes not on allow list
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, false}}, addr));
+
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, AllowPools) {
+ entity_addr_t addr;
+ OSDCap cap;
+ bool r = cap.parse("allow rwx pool foo, allow r pool bar", NULL);
+ ASSERT_TRUE(r);
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ // true-false for classes not on allow list
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "ns", {}, "", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "ns", {{"application", {{"key", "value"}}}}, "", true, false, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {{"application", {{"key", "value"}}}}, "", true, true, {{"cls", "", true, true, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "ns", {}, "", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {{"application", {{"key", "value"}}}}, "", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "ns", {{"application", {{"key", "value"}}}}, "", true, false, {}, addr));
+}
+
+TEST(OSDCap, AllowPools2) {
+ entity_addr_t addr;
+ OSDCap cap;
+ bool r = cap.parse("allow r, allow rwx pool foo", NULL);
+ ASSERT_TRUE(r);
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+ // true-false for classes not on allow list
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "", true, true, {{"cls", "", true, true, true}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "", true, false, {}, addr));
+}
+
+TEST(OSDCap, ObjectPrefix) {
+ entity_addr_t addr;
+ OSDCap cap;
+ bool r = cap.parse("allow rwx object_prefix foo", NULL);
+ ASSERT_TRUE(r);
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "food", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo_bar", true, true, {{"cls", "", true, true, true}}, addr));
+ // true-false for classes not on allow list
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "food", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo_bar", true, true, {{"cls", "", true, true, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "_foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, " foo ", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "fo", true, true, {{"cls", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, ObjectPoolAndPrefix) {
+ entity_addr_t addr;
+ OSDCap cap;
+ bool r = cap.parse("allow rwx pool bar object_prefix foo", NULL);
+ ASSERT_TRUE(r);
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "food", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo_bar", true, true, {{"cls", "", true, true, true}}, addr));
+ // true-false for classes not on allow list
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "food", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo_bar", true, true, {{"cls", "", true, true, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "food", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "fo", true, true, {{"cls", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, Namespace) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rw namespace=nfoo"));
+
+ ASSERT_TRUE(cap.is_capable("bar", "nfoo", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "nfoobar", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, NamespaceGlob) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rw namespace=nfoo*"));
+
+ ASSERT_TRUE(cap.is_capable("bar", "nfoo", {}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "nfoobar", {}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "nfo", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, BasicR) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow r", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, BasicW) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow w", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, BasicX) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow x", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ // true->false when class not on allow list
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, BasicRW) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rw", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, BasicRX) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rx", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, true, true}}, addr));
+ // true->false for class not on allow list
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, true, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, BasicWX) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow wx", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ // true->false for class not on allow list
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, BasicRWX) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ // true->false for class not on allow list
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+}
+
+TEST(OSDCap, BasicRWClassRClassW) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rw class-read class-write", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+}
+
+TEST(OSDCap, ClassR) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow class-read", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, ClassW) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow class-write", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, ClassRW) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow class-read class-write", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, BasicRClassR) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow r class-read", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, false, {}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "any", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "any", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "any", {}, "foo", true, false, {}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "any", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "any", {}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "any", {{"application", {{"key", "value"}}}}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "any", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "any", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "any", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "any", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "any", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, PoolClassR) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow pool bar r class-read, allow pool foo rwx", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, false, {}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "ns", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "ns", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "ns", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "ns", {{"application", {{"key", "value"}}}}, "foo", true, false, {}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {{"application", {{"key", "value"}}}}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", false, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+}
+
+TEST(OSDCap, PoolClassRNS) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow pool bar namespace='' r class-read, allow pool foo namespace=ns rwx", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, false, {}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, false, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "ns", {{"application", {{"key", "value"}}}}, "foo", true, false, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "other", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "other", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "other", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "other", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "other", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", false, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("baz", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+}
+
+TEST(OSDCap, NSClassR) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow namespace '' rw class-read class-write, allow namespace test r", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", false, false, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, false, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, false, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {{"cls", "", true, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "test", {}, "foo", true, false, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "test", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "test", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "test", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "test", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "test", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "test", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "test", {}, "foo", true, false, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "test", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "test", {}, "foo", true, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "test", {}, "foo", true, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "test", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "test", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "test", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "bad", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "bad", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "bad", {}, "foo", false, false, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "bad", {}, "foo", false, false, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "bad", {{"application", {{"key", "value"}}}}, "foo", false, false, {{"cls", "", false, true, true}}, addr));
+}
+
+TEST(OSDCap, PoolTagBasic) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx tag application key=value", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key2", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"app2", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key2", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, false, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", false, false, true}}, addr));
+ // true->false when class not allow listed
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", false, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", false, true, {{"cls", "", false, true, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", false, true, {{"cls", "", false, false, true}}, addr));
+}
+
+TEST(OSDCap, PoolTagWildK)
+{
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx tag application *=value", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key2", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"app2", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key2", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, PoolTagWildV)
+{
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx tag application key=*", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key2", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"app2", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key2", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, PoolTagWildKV)
+{
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx tag application *=*", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {}}}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"key2", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"app2", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {{"application", {{"foo", "bar"}, {"key2", "value"}}}, {"app2", {{"foo", "bar"}}}}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, NSPool)
+{
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx namespace ns tag application key=value", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns2", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key", "value2"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key2", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, NSPoolGlob)
+{
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx namespace ns* tag application key=value", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", "ns", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+
+ ASSERT_TRUE(cap.is_capable("foo", "ns2", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {{"application", {{"key", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key", "value2"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "ns", {{"application", {{"key2", "value"}}}}, "foo", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "foo", true, true, {}, addr));
+}
+
+TEST(OSDCap, OutputParsed)
+{
+ entity_addr_t addr;
+ struct CapsTest {
+ const char *input;
+ const char *output;
+ };
+ CapsTest test_values[] = {
+ {"allow *",
+ "osdcap[grant(*)]"},
+ {"allow r",
+ "osdcap[grant(r)]"},
+ {"allow rx",
+ "osdcap[grant(rx)]"},
+ {"allow rwx",
+ "osdcap[grant(rwx)]"},
+ {"allow rw class-read class-write",
+ "osdcap[grant(rwx)]"},
+ {"allow rw class-read",
+ "osdcap[grant(rw class-read)]"},
+ {"allow rw class-write",
+ "osdcap[grant(rw class-write)]"},
+ {"allow rwx pool images",
+ "osdcap[grant(pool images rwx)]"},
+ {"allow r pool images",
+ "osdcap[grant(pool images r)]"},
+ {"allow pool images rwx",
+ "osdcap[grant(pool images rwx)]"},
+ {"allow pool images r",
+ "osdcap[grant(pool images r)]"},
+ {"allow pool images w",
+ "osdcap[grant(pool images w)]"},
+ {"allow pool images x",
+ "osdcap[grant(pool images x)]"},
+ {"allow r pool images namespace ''",
+ "osdcap[grant(pool images namespace \"\" r)]"},
+ {"allow r pool images namespace foo",
+ "osdcap[grant(pool images namespace foo r)]"},
+ {"allow r pool images namespace \"\"",
+ "osdcap[grant(pool images namespace \"\" r)]"},
+ {"allow r namespace foo",
+ "osdcap[grant(namespace foo r)]"},
+ {"allow pool images r; allow pool rbd rwx",
+ "osdcap[grant(pool images r),grant(pool rbd rwx)]"},
+ {"allow pool images r, allow pool rbd rwx",
+ "osdcap[grant(pool images r),grant(pool rbd rwx)]"},
+ {"allow class-read object_prefix rbd_children, allow pool libvirt-pool-test rwx",
+ "osdcap[grant(object_prefix rbd_children class-read),grant(pool libvirt-pool-test rwx)]"},
+ {"allow rwx tag application key=value",
+ "osdcap[grant(app application key key val value rwx)]"},
+ {"allow rwx namespace ns* tag application key=value",
+ "osdcap[grant(namespace ns* app application key key val value rwx)]"},
+ {"allow all",
+ "osdcap[grant(*)]"},
+ {"allow rwx tag application all=all",
+ "osdcap[grant(app application key * val * rwx)]"},
+ {"allow rwx network 1.2.3.4/24",
+ "osdcap[grant(rwx network 1.2.3.4/24)]"},
+ };
+
+ size_t num_tests = sizeof(test_values) / sizeof(*test_values);
+ for (size_t i = 0; i < num_tests; ++i) {
+ OSDCap cap;
+ std::cout << "Testing input '" << test_values[i].input << "'" << std::endl;
+ ASSERT_TRUE(cap.parse(test_values[i].input));
+ ASSERT_EQ(test_values[i].output, stringify(cap));
+ }
+}
+
+TEST(OSDCap, AllowClass) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow class foo", NULL));
+
+ // can call any method on class foo regardless of allow list status
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}}, addr));
+
+ // does not permit invoking class bar
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, false}}, addr));
+}
+
+TEST(OSDCap, AllowClassMethod) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow class foo xyz", NULL));
+
+ // can call the xyz method on class foo regardless of allow list status
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "xyz", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "xyz", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "xyz", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "xyz", true, false, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "xyz", false, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "xyz", true, true, false}}, addr));
+
+ // does not permit invoking class bar
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "xyz", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "xyz", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "xyz", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "xyz", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "xyz", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "xyz", true, true, false}}, addr));
+}
+
+TEST(OSDCap, AllowClass2) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow class foo, allow class bar", NULL));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, false}}, addr));
+}
+
+TEST(OSDCap, AllowClassRWX) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx, allow class foo", NULL));
+
+ // can call any method on class foo regardless of allow list status
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}}, addr));
+
+ // does not permit invoking class bar
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, false}}, addr));
+
+ // allows class bar if it is allow listed
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"bar", "", true, true, true}}, addr));
+}
+
+TEST(OSDCap, AllowClassMulti) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow class foo", NULL));
+
+ // can call any method on foo, but not bar, so the entire op is rejected
+ // bar with allow list is rejected because it still needs rwx/class-read,write
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, false, false}}, addr));
+
+ // these are OK because 'bar' is on the allow list BUT the calls don't read or write
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, false, true}}, addr));
+
+ // can call any method on foo or bar regardless of allow list status
+ OSDCap cap2;
+ ASSERT_TRUE(cap2.parse("allow class foo, allow class bar", NULL));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, false, true}}, addr));
+ ASSERT_TRUE(cap2.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, false, false}}, addr));
+}
+
+TEST(OSDCap, AllowClassMultiRWX) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow rwx, allow class foo", NULL));
+
+ // can call anything on foo, but only allow listed methods on bar
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, false, true}}, addr));
+
+ // fails because bar not allow listed
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, false, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, true, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, false, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, false, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", true, false, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, false, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, false, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, true, false}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, false, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, true}, {"bar", "", false, false, false}}, addr));
+
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, false, true}}, addr));
+
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", true, false, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, true, false}}, addr));
+ ASSERT_FALSE(cap.is_capable("bar", "", {}, "foo", false, false, {{"foo", "", false, false, false}, {"bar", "", false, false, false}}, addr));
+}
+
+TEST(OSDCap, AllowProfile) {
+ entity_addr_t addr;
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("profile read-only, profile read-write pool abc", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "asdf", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "asdf", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("abc", "", {}, "asdf", false, true, {}, addr));
+
+ // RBD
+ cap.grants.clear();
+ ASSERT_TRUE(cap.parse("profile rbd pool abc", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "asdf", true, true, {}, addr));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "rbd_children", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "rbd_children", false, false,
+ {{"rbd", "", true, false, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("abc", "", {}, "asdf", true, true,
+ {{"rbd", "", true, true, true}}, addr));
+
+ cap.grants.clear();
+ ASSERT_TRUE(cap.parse("profile rbd-read-only pool abc", NULL));
+ ASSERT_FALSE(cap.allow_all());
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "rbd_children", true, false, {}, addr));
+ ASSERT_TRUE(cap.is_capable("abc", "", {}, "asdf", true, false,
+ {{"rbd", "", true, false, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("abc", "", {}, "asdf", true, true, {}, addr));
+ ASSERT_TRUE(cap.is_capable("abc", "", {}, "rbd_header.ABC", false, false,
+ {{"rbd", "child_attach", true, true, true}}, addr));
+ ASSERT_TRUE(cap.is_capable("abc", "", {}, "rbd_header.ABC", false, false,
+ {{"rbd", "child_detach", true, true, true}}, addr));
+ ASSERT_FALSE(cap.is_capable("abc", "", {}, "rbd_header.ABC", false, false,
+ {{"rbd", "other function", true, true, true}}, addr));
+
+ cap.grants.clear();
+ ASSERT_TRUE(cap.parse("profile rbd pool pool1 namespace ns1", nullptr));
+ ASSERT_TRUE(cap.is_capable("pool1", "", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_TRUE(cap.is_capable("pool1", "ns1", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool1", "ns2", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool2", "", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool1", "", {}, "asdf", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool1", "", {}, "rbd_info", false, false,
+ {{"rbd", "other_method", true, false, true}},
+ addr));
+
+ cap.grants.clear();
+ ASSERT_TRUE(cap.parse("profile rbd-read-only pool pool1 namespace ns1",
+ nullptr));
+ ASSERT_TRUE(cap.is_capable("pool1", "", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_TRUE(cap.is_capable("pool1", "ns1", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool1", "ns2", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool2", "", {}, "rbd_info", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool1", "", {}, "asdf", false, false,
+ {{"rbd", "metadata_list", true, false, true}},
+ addr));
+ ASSERT_FALSE(cap.is_capable("pool1", "", {}, "rbd_info", false, false,
+ {{"rbd", "other_method", true, false, true}},
+ addr));
+}
+
+TEST(OSDCap, network) {
+ entity_addr_t a, b, c;
+ a.parse("10.1.2.3");
+ b.parse("192.168.2.3");
+ c.parse("192.167.2.3");
+
+ OSDCap cap;
+ ASSERT_TRUE(cap.parse("allow * network 192.168.0.0/16, allow * network 10.0.0.0/8", NULL));
+
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "asdf", true, true, {{"cls", "", true, true, true}}, a));
+ ASSERT_TRUE(cap.is_capable("foo", "", {}, "asdf", true, true, {{"cls", "", true, true, true}}, b));
+ ASSERT_FALSE(cap.is_capable("foo", "", {}, "asdf", true, true, {{"cls", "", true, true, true}}, c));
+}
diff --git a/src/test/osd/safe-to-destroy.sh b/src/test/osd/safe-to-destroy.sh
new file mode 100755
index 000000000..08afc8e8d
--- /dev/null
+++ b/src/test/osd/safe-to-destroy.sh
@@ -0,0 +1,99 @@
+#!/usr/bin/env bash
+
+source $CEPH_ROOT/qa/standalone/ceph-helpers.sh
+
+set -e
+
+function run() {
+ local dir=$1
+ shift
+
+ export CEPH_MON="127.0.0.1:$(get_unused_port)"
+ export CEPH_ARGS
+ CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
+ CEPH_ARGS+="--mon-host=$CEPH_MON "
+ set -e
+
+ local funcs=${@:-$(set | sed -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')}
+ for func in $funcs ; do
+ setup $dir || return 1
+ $func $dir || return 1
+ teardown $dir || return 1
+ done
+}
+
+function TEST_safe_to_destroy() {
+ local dir=$1
+
+ run_mon $dir a
+ run_mgr $dir x
+ run_osd $dir 0
+ run_osd $dir 1
+ run_osd $dir 2
+ run_osd $dir 3
+
+ flush_pg_stats
+
+ ceph osd safe-to-destroy 0
+ ceph osd safe-to-destroy 1
+ ceph osd safe-to-destroy 2
+ ceph osd safe-to-destroy 3
+
+ ceph osd pool create foo 128
+ sleep 2
+ flush_pg_stats
+ wait_for_clean
+
+ expect_failure $dir 'pgs currently' ceph osd safe-to-destroy 0
+ expect_failure $dir 'pgs currently' ceph osd safe-to-destroy 1
+ expect_failure $dir 'pgs currently' ceph osd safe-to-destroy 2
+ expect_failure $dir 'pgs currently' ceph osd safe-to-destroy 3
+
+ ceph osd out 0
+ sleep 2
+ flush_pg_stats
+ wait_for_clean
+
+ ceph osd safe-to-destroy 0
+
+ # even osds without osd_stat are ok if all pgs are active+clean
+ id=`ceph osd create`
+ ceph osd safe-to-destroy $id
+}
+
+function TEST_ok_to_stop() {
+ local dir=$1
+
+ run_mon $dir a
+ run_mgr $dir x
+ run_osd $dir 0
+ run_osd $dir 1
+ run_osd $dir 2
+ run_osd $dir 3
+
+ ceph osd pool create foo 128
+ ceph osd pool set foo size 3
+ ceph osd pool set foo min_size 2
+ sleep 1
+ flush_pg_stats
+ wait_for_clean
+
+ ceph osd ok-to-stop 0
+ ceph osd ok-to-stop 1
+ ceph osd ok-to-stop 2
+ ceph osd ok-to-stop 3
+ expect_failure $dir bad_become_inactive ceph osd ok-to-stop 0 1
+
+ ceph osd pool set foo min_size 1
+ sleep 1
+ flush_pg_stats
+ wait_for_clean
+ ceph osd ok-to-stop 0 1
+ ceph osd ok-to-stop 1 2
+ ceph osd ok-to-stop 2 3
+ ceph osd ok-to-stop 3 4
+ expect_failure $dir bad_become_inactive ceph osd ok-to-stop 0 1 2
+ expect_failure $dir bad_become_inactive ceph osd ok-to-stop 0 1 2 3
+}
+
+main safe-to-destroy "$@"
diff --git a/src/test/osd/scrubber_generators.cc b/src/test/osd/scrubber_generators.cc
new file mode 100644
index 000000000..0f2f371e7
--- /dev/null
+++ b/src/test/osd/scrubber_generators.cc
@@ -0,0 +1,168 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/osd/scrubber_generators.h"
+
+#include <fmt/ranges.h>
+
+using namespace ScrubGenerator;
+
+// ref: PGLogTestRebuildMissing()
+bufferptr create_object_info(const ScrubGenerator::RealObj& objver)
+{
+ object_info_t oi{};
+ oi.soid = objver.ghobj.hobj;
+ oi.version = eversion_t(objver.ghobj.generation, 0);
+ oi.size = objver.data.size;
+
+ bufferlist bl;
+ oi.encode(bl,
+ 0 /*get_osdmap()->get_features(CEPH_ENTITY_TYPE_OSD, nullptr)*/);
+ bufferptr bp(bl.c_str(), bl.length());
+ return bp;
+}
+
+std::pair<bufferptr, std::vector<snapid_t>> create_object_snapset(
+ const ScrubGenerator::RealObj& robj,
+ const SnapsetMockData* snapset_mock_data)
+{
+ if (!snapset_mock_data) {
+ return {bufferptr(), {}};
+ }
+ /// \todo fill in missing version/osd details from the robj
+ auto sns = snapset_mock_data->make_snapset();
+ bufferlist bl;
+ encode(sns, bl);
+ bufferptr bp = bufferptr(bl.c_str(), bl.length());
+
+ // extract the set of object snaps
+ return {bp, sns.snaps};
+}
+
+RealObjsConfList ScrubGenerator::make_real_objs_conf(
+ int64_t pool_id,
+ const RealObjsConf& blueprint,
+ std::vector<int32_t> active_osds)
+{
+ RealObjsConfList all_osds;
+
+ for (auto osd : active_osds) {
+ RealObjsConfRef this_osd_fakes = std::make_unique<RealObjsConf>(blueprint);
+ // now - fix & corrupt every "object" in the blueprint
+ for (RealObj& robj : this_osd_fakes->objs) {
+
+ robj.ghobj.hobj.pool = pool_id;
+ }
+
+ all_osds[osd] = std::move(this_osd_fakes);
+ }
+ return all_osds; // reconsider (maybe add a move ctor?)
+}
+
+///\todo dispose of the created buffer pointers
+
+ScrubGenerator::SmapEntry ScrubGenerator::make_smobject(
+ const ScrubGenerator::RealObj& blueprint,
+ int osd_num)
+{
+ ScrubGenerator::SmapEntry ret;
+
+ ret.ghobj = blueprint.ghobj;
+ ret.smobj.attrs[OI_ATTR] = create_object_info(blueprint);
+ if (blueprint.snapset_mock_data) {
+ auto [bp, snaps] =
+ create_object_snapset(blueprint, blueprint.snapset_mock_data);
+ ret.smobj.attrs[SS_ATTR] = bp;
+ std::cout << fmt::format("{}: ({}) osd:{} snaps:{}",
+ __func__,
+ ret.ghobj.hobj,
+ osd_num,
+ snaps)
+ << std::endl;
+ }
+
+ for (const auto& [at_k, at_v] : blueprint.data.attrs) {
+ ret.smobj.attrs[at_k] = ceph::buffer::copy(at_v.c_str(), at_v.size());
+ {
+ // verifying (to be removed after dev phase)
+ auto bk = ret.smobj.attrs[at_k].begin_deep().get_ptr(
+ ret.smobj.attrs[at_k].length());
+ std::string bkstr{bk.raw_c_str(), bk.raw_length()};
+ std::cout << fmt::format("{}: verification: {}", __func__, bkstr)
+ << std::endl;
+ }
+ }
+ ret.smobj.size = blueprint.data.size;
+ ret.smobj.digest = blueprint.data.hash;
+ /// \todo handle the 'present' etc'
+
+ ret.smobj.object_omap_keys = blueprint.data.omap.size();
+ ret.smobj.object_omap_bytes = blueprint.data.omap_bytes;
+ return ret;
+}
+
+all_clones_snaps_t ScrubGenerator::all_clones(
+ const ScrubGenerator::RealObj& head_obj)
+{
+ std::cout << fmt::format("{}: head_obj.ghobj.hobj:{}",
+ __func__,
+ head_obj.ghobj.hobj)
+ << std::endl;
+
+ std::map<hobject_t, std::vector<snapid_t>> ret;
+
+ for (const auto& clone : head_obj.snapset_mock_data->clones) {
+ auto clone_set_it = head_obj.snapset_mock_data->clone_snaps.find(clone);
+ if (clone_set_it == head_obj.snapset_mock_data->clone_snaps.end()) {
+ std::cout << "note: no clone_snaps for " << clone << std::endl;
+ continue;
+ }
+ auto clone_set = clone_set_it->second;
+ hobject_t clone_hobj{head_obj.ghobj.hobj};
+ clone_hobj.snap = clone;
+
+ ret[clone_hobj] = clone_set_it->second;
+ std::cout << fmt::format("{}: clone:{} clone_set:{}",
+ __func__,
+ clone_hobj,
+ clone_set)
+ << std::endl;
+ }
+
+ return ret;
+}
+
+void ScrubGenerator::add_object(ScrubMap& map,
+ const ScrubGenerator::RealObj& real_obj,
+ int osd_num)
+{
+ // do we have data corruption recipe for this OSD?
+ /// \todo c++20: use contains()
+ CorruptFunc relevant_fix = crpt_do_nothing;
+
+ auto p = real_obj.corrupt_funcs->find(osd_num);
+ if (p != real_obj.corrupt_funcs->end()) {
+ // yes, we have a corruption recepie for this OSD
+ // \todo c++20: use at()
+ relevant_fix = p->second;
+ }
+
+ // create a possibly-corrupted copy of the "real object"
+ auto modified_obj = (relevant_fix)(real_obj, osd_num);
+
+ std::cout << fmt::format("{}: modified: osd:{} ho:{} key:{}",
+ __func__,
+ osd_num,
+ modified_obj.ghobj.hobj,
+ modified_obj.ghobj.hobj.get_key())
+ << std::endl;
+
+ auto entry = make_smobject(modified_obj, osd_num);
+ std::cout << fmt::format("{}: osd:{} smap entry: {} {}",
+ __func__,
+ osd_num,
+ entry.smobj.size,
+ entry.smobj.attrs.size())
+ << std::endl;
+ map.objects[entry.ghobj.hobj] = entry.smobj;
+}
diff --git a/src/test/osd/scrubber_generators.h b/src/test/osd/scrubber_generators.h
new file mode 100644
index 000000000..d0cbb22c4
--- /dev/null
+++ b/src/test/osd/scrubber_generators.h
@@ -0,0 +1,266 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+
+/// \file generating scrub-related maps & objects for unit tests
+
+#include <functional>
+#include <map>
+#include <sstream>
+#include <string>
+#include <variant>
+#include <vector>
+
+#include "include/buffer.h"
+#include "include/buffer_raw.h"
+#include "include/object_fmt.h"
+#include "osd/osd_types_fmt.h"
+#include "osd/scrubber/pg_scrubber.h"
+
+namespace ScrubGenerator {
+
+/// \todo enhance the MockLog to capture the log messages
+class MockLog : public LoggerSinkSet {
+ public:
+ void debug(std::stringstream& s) final
+ {
+ std::cout << "\n<<debug>> " << s.str() << std::endl;
+ }
+ void info(std::stringstream& s) final
+ {
+ std::cout << "\n<<info>> " << s.str() << std::endl;
+ }
+ void sec(std::stringstream& s) final
+ {
+ std::cout << "\n<<sec>> " << s.str() << std::endl;
+ }
+ void warn(std::stringstream& s) final
+ {
+ std::cout << "\n<<warn>> " << s.str() << std::endl;
+ }
+ void error(std::stringstream& s) final
+ {
+ err_count++;
+ std::cout << "\n<<error>> " << s.str() << std::endl;
+ }
+ OstreamTemp info() final { return OstreamTemp(CLOG_INFO, this); }
+ OstreamTemp warn() final { return OstreamTemp(CLOG_WARN, this); }
+ OstreamTemp error() final { return OstreamTemp(CLOG_ERROR, this); }
+ OstreamTemp sec() final { return OstreamTemp(CLOG_ERROR, this); }
+ OstreamTemp debug() final { return OstreamTemp(CLOG_DEBUG, this); }
+
+ void do_log(clog_type prio, std::stringstream& ss) final
+ {
+ switch (prio) {
+ case CLOG_DEBUG:
+ debug(ss);
+ break;
+ case CLOG_INFO:
+ info(ss);
+ break;
+ case CLOG_SEC:
+ sec(ss);
+ break;
+ case CLOG_WARN:
+ warn(ss);
+ break;
+ case CLOG_ERROR:
+ default:
+ error(ss);
+ break;
+ }
+ }
+
+ void do_log(clog_type prio, const std::string& ss) final
+ {
+ switch (prio) {
+ case CLOG_DEBUG:
+ debug() << ss;
+ break;
+ case CLOG_INFO:
+ info() << ss;
+ break;
+ case CLOG_SEC:
+ sec() << ss;
+ break;
+ case CLOG_WARN:
+ warn() << ss;
+ break;
+ case CLOG_ERROR:
+ default:
+ error() << ss;
+ break;
+ }
+ }
+
+ virtual ~MockLog() {}
+
+ int err_count{0};
+ int expected_err_count{0};
+ void set_expected_err_count(int c) { expected_err_count = c; }
+};
+
+// ///////////////////////////////////////////////////////////////////////// //
+// ///////////////////////////////////////////////////////////////////////// //
+
+struct pool_conf_t {
+ int pg_num{3};
+ int pgp_num{3};
+ int size{3};
+ int min_size{3};
+ std::string name{"rep_pool"};
+};
+
+using attr_t = std::map<std::string, std::string>;
+
+using all_clones_snaps_t = std::map<hobject_t, std::vector<snapid_t>>;
+
+struct RealObj;
+
+// a function to manipulate (i.e. corrupt) an object in a specific OSD
+using CorruptFunc =
+ std::function<RealObj(const RealObj& s, [[maybe_unused]] int osd_num)>;
+using CorruptFuncList = std::map<int, CorruptFunc>; // per OSD
+
+struct SnapsetMockData {
+
+ using CookedCloneSnaps =
+ std::tuple<std::map<snapid_t, uint64_t>,
+ std::map<snapid_t, std::vector<snapid_t>>,
+ std::map<snapid_t, interval_set<uint64_t>>>;
+
+ // an auxiliary function to cook the data for the SnapsetMockData
+ using clone_snaps_cooker = CookedCloneSnaps (*)();
+
+ snapid_t seq;
+ std::vector<snapid_t> snaps; // descending
+ std::vector<snapid_t> clones; // ascending
+
+ std::map<snapid_t, interval_set<uint64_t>> clone_overlap; // overlap w/ next
+ // newest
+ std::map<snapid_t, uint64_t> clone_size;
+ std::map<snapid_t, std::vector<snapid_t>> clone_snaps; // descending
+
+
+ SnapsetMockData(snapid_t seq,
+ std::vector<snapid_t> snaps,
+ std::vector<snapid_t> clones,
+ std::map<snapid_t, interval_set<uint64_t>> clone_overlap,
+ std::map<snapid_t, uint64_t> clone_size,
+ std::map<snapid_t, std::vector<snapid_t>> clone_snaps)
+ : seq(seq)
+ , snaps(snaps)
+ , clones(clones)
+ , clone_overlap(clone_overlap)
+ , clone_size(clone_size)
+ , clone_snaps(clone_snaps)
+ {}
+
+ SnapsetMockData(snapid_t seq,
+ std::vector<snapid_t> snaps,
+ std::vector<snapid_t> clones,
+ clone_snaps_cooker func)
+ : seq{seq}
+ , snaps{snaps}
+ , clones(clones)
+ {
+ auto [clone_size_, clone_snaps_, clone_overlap_] = func();
+ clone_size = clone_size_;
+ clone_snaps = clone_snaps_;
+ clone_overlap = clone_overlap_;
+ }
+
+ SnapSet make_snapset() const
+ {
+ SnapSet ss;
+ ss.seq = seq;
+ ss.snaps = snaps;
+ ss.clones = clones;
+ ss.clone_overlap = clone_overlap;
+ ss.clone_size = clone_size;
+ ss.clone_snaps = clone_snaps;
+ return ss;
+ }
+};
+
+// an object in our "DB" - with its versioned snaps, "data" (size and hash),
+// and "omap" (size and hash)
+
+struct RealData {
+ // not needed at this level of "data falsification": std::byte data;
+ uint64_t size;
+ uint32_t hash;
+ uint32_t omap_digest;
+ uint32_t omap_bytes;
+ attr_t omap;
+ attr_t attrs;
+};
+
+struct RealObj {
+ // the ghobject - oid, version, snap, hash, pool
+ ghobject_t ghobj;
+ RealData data;
+ const CorruptFuncList* corrupt_funcs;
+ const SnapsetMockData* snapset_mock_data;
+};
+
+static inline RealObj crpt_do_nothing(const RealObj& s, int osdn)
+{
+ return s;
+}
+
+struct SmapEntry {
+ ghobject_t ghobj;
+ ScrubMap::object smobj;
+};
+
+
+ScrubGenerator::SmapEntry make_smobject(
+ const ScrubGenerator::RealObj& blueprint, // the whole set of versions
+ int osd_num);
+
+
+/**
+ * returns the object's snap-set
+ */
+void add_object(ScrubMap& map, const RealObj& obj_versions, int osd_num);
+
+struct RealObjsConf {
+ std::vector<RealObj> objs;
+};
+
+using RealObjsConfRef = std::unique_ptr<RealObjsConf>;
+
+// RealObjsConf will be "developed" into the following of per-osd sets,
+// now with the correct pool ID, and with the corrupting functions
+// activated on the data
+using RealObjsConfList = std::map<int, RealObjsConfRef>;
+
+RealObjsConfList make_real_objs_conf(int64_t pool_id,
+ const RealObjsConf& blueprint,
+ std::vector<int32_t> active_osds);
+
+/**
+ * create the snap-ids set for all clones appearing in the head
+ * object's snapset (those will be injected into the scrubber's mock,
+ * to be used as the 'snap_mapper')
+ */
+all_clones_snaps_t all_clones(const RealObj& head_obj);
+} // namespace ScrubGenerator
+
+template <>
+struct fmt::formatter<ScrubGenerator::RealObj> {
+ constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
+
+ template <typename FormatContext>
+ auto format(const ScrubGenerator::RealObj& rlo, FormatContext& ctx)
+ {
+ using namespace ScrubGenerator;
+ return fmt::format_to(ctx.out(),
+ "RealObj(gh:{}, dt:{}, snaps:{})",
+ rlo.ghobj,
+ rlo.data.size,
+ (rlo.snapset_mock_data ? rlo.snapset_mock_data->snaps
+ : std::vector<snapid_t>{}));
+ }
+};
diff --git a/src/test/osd/scrubber_test_datasets.cc b/src/test/osd/scrubber_test_datasets.cc
new file mode 100644
index 000000000..478fd25fe
--- /dev/null
+++ b/src/test/osd/scrubber_test_datasets.cc
@@ -0,0 +1,120 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+/// \file data-sets used by the scrubber unit tests
+
+#include "./scrubber_test_datasets.h"
+
+
+using namespace ScrubGenerator;
+using namespace std::string_literals;
+
+namespace ScrubDatasets {
+
+static RealObj corrupt_object_size(const RealObj& s, [[maybe_unused]] int osdn)
+{
+ RealObj ret = s;
+ ret.data.size = s.data.size + 1;
+ return ret;
+}
+
+static RealObj corrupt_nothing(const RealObj& s, int osdn)
+{
+ return s;
+}
+
+
+static CorruptFuncList crpt_funcs_set0 = {{0, &corrupt_nothing}};
+
+CorruptFuncList crpt_funcs_set1 = {{0, &corrupt_object_size},
+ {1, &corrupt_nothing}};
+
+
+// object with head & two snaps
+
+static hobject_t hobj_ms1{object_t{"hobj_ms1"},
+ "keykey", // key
+ CEPH_NOSNAP, // snap_id
+ 0, // hash
+ 0, // pool
+ ""s}; // nspace
+
+SnapsetMockData::CookedCloneSnaps ms1_fn()
+{
+ std::map<snapid_t, uint64_t> clnsz;
+ clnsz[0x20] = 222;
+ clnsz[0x30] = 333;
+
+ std::map<snapid_t, std::vector<snapid_t>> clnsn;
+ clnsn[0x20] = {0x20};
+ clnsn[0x30] = {0x30};
+
+ std::map<snapid_t, interval_set<uint64_t>> overlaps;
+ overlaps[0x20] = {};
+ overlaps[0x30] = {};
+ return {clnsz, clnsn, overlaps};
+}
+
+static SnapsetMockData hobj_ms1_snapset{/* seq */ 0x40,
+ /* snaps */ {0x30, 0x20},
+ /* clones */ {0x20, 0x30},
+ ms1_fn};
+
+hobject_t hobj_ms1_snp30{object_t{"hobj_ms1"},
+ "keykey", // key
+ 0x30, // snap_id
+ 0, // hash
+ 0, // pool
+ ""s}; // nspace
+
+static hobject_t hobj_ms1_snp20{object_t{"hobj_ms1"},
+ "keykey", // key
+ 0x20, // snap_id
+ 0, // hash
+ 0, // pool
+ ""s}; // nspace
+
+
+ScrubGenerator::RealObjsConf minimal_snaps_configuration{
+ /* RealObjsConf::objs */ {
+
+ /* Clone 30 */ {
+ ghobject_t{hobj_ms1_snp30, 0, shard_id_t{0}},
+ RealData{
+ 333,
+ 0x17,
+ 17,
+ 21,
+ attr_t{/*{"_om1k", "om1v"}, {"om1k", "om1v"},*/ {"om3k", "om3v"}},
+ attr_t{{"_at1k", "_at1v"}, {"_at2k", "at2v"}, {"at3k", "at3v"}}},
+ &crpt_funcs_set0,
+ nullptr},
+
+ /* Clone 20 */
+ {ghobject_t{hobj_ms1_snp20, 0, shard_id_t{0}},
+ RealData{222,
+ 0x17,
+ 17,
+ 21,
+ attr_t{/*{"_om1k", "om1v"}, {"om1k", "om1v"},*/ {"om3k", "om3v"}},
+ attr_t{{"_at1k", "_at1v"}, {"_at2k", "at2v"}, {"at3k", "at3v"}}},
+ &crpt_funcs_set0,
+ nullptr},
+
+ /* Head */
+ {ghobject_t{hobj_ms1, 0, shard_id_t{0}},
+ RealData{100,
+ 0x17,
+ 17,
+ 21,
+ attr_t{{"_om1k", "om1v"}, {"om1k", "om1v"}, {"om3k", "om3v"}},
+ attr_t{{"_at1k", "_at1v"}, {"_at2k", "at2v"}, {"at3k", "at3v"}}
+
+
+ },
+ &crpt_funcs_set0,
+ &hobj_ms1_snapset}}
+
+};
+
+} // namespace ScrubDatasets
diff --git a/src/test/osd/scrubber_test_datasets.h b/src/test/osd/scrubber_test_datasets.h
new file mode 100644
index 000000000..181528568
--- /dev/null
+++ b/src/test/osd/scrubber_test_datasets.h
@@ -0,0 +1,21 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+
+/// \file data-sets used by the scrubber unit tests
+
+#include "./scrubber_generators.h"
+
+namespace ScrubDatasets {
+/*
+ * Two objects with some clones. No inconsitencies.
+ */
+extern ScrubGenerator::RealObjsConf minimal_snaps_configuration;
+
+// and a part of this configuration, one that we will corrupt in a test:
+extern hobject_t hobj_ms1_snp30;
+
+// a manipulation set used in TestTScrubberBe_data_2:
+extern ScrubGenerator::CorruptFuncList crpt_funcs_set1;
+
+} // namespace ScrubDatasets
diff --git a/src/test/osd/test_ec_transaction.cc b/src/test/osd/test_ec_transaction.cc
new file mode 100644
index 000000000..98669667a
--- /dev/null
+++ b/src/test/osd/test_ec_transaction.cc
@@ -0,0 +1,124 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <gtest/gtest.h>
+#include "osd/PGTransaction.h"
+#include "osd/ECTransaction.h"
+
+#include "test/unit.cc"
+
+struct mydpp : public DoutPrefixProvider {
+ std::ostream& gen_prefix(std::ostream& out) const override { return out << "foo"; }
+ CephContext *get_cct() const override { return g_ceph_context; }
+ unsigned get_subsys() const override { return ceph_subsys_osd; }
+} dpp;
+
+#define dout_context g_ceph_context
+
+TEST(ectransaction, two_writes_separated)
+{
+ hobject_t h;
+ PGTransactionUPtr t(new PGTransaction);
+ bufferlist a, b;
+ t->create(h);
+ a.append_zero(565760);
+ t->write(h, 0, a.length(), a, 0);
+ b.append_zero(2437120);
+ t->write(h, 669856, b.length(), b, 0);
+
+ ECUtil::stripe_info_t sinfo(2, 8192);
+ auto plan = ECTransaction::get_write_plan(
+ sinfo,
+ std::move(t),
+ [&](const hobject_t &i) {
+ ECUtil::HashInfoRef ref(new ECUtil::HashInfo(1));
+ return ref;
+ },
+ &dpp);
+ generic_derr << "to_read " << plan.to_read << dendl;
+ generic_derr << "will_write " << plan.will_write << dendl;
+
+ ASSERT_EQ(0u, plan.to_read.size());
+ ASSERT_EQ(1u, plan.will_write.size());
+}
+
+TEST(ectransaction, two_writes_nearby)
+{
+ hobject_t h;
+ PGTransactionUPtr t(new PGTransaction);
+ bufferlist a, b;
+ t->create(h);
+
+ // two nearby writes, both partly touching the same 8192-byte stripe
+ ECUtil::stripe_info_t sinfo(2, 8192);
+ a.append_zero(565760);
+ t->write(h, 0, a.length(), a, 0);
+ b.append_zero(2437120);
+ t->write(h, 569856, b.length(), b, 0);
+
+ auto plan = ECTransaction::get_write_plan(
+ sinfo,
+ std::move(t),
+ [&](const hobject_t &i) {
+ ECUtil::HashInfoRef ref(new ECUtil::HashInfo(1));
+ return ref;
+ },
+ &dpp);
+ generic_derr << "to_read " << plan.to_read << dendl;
+ generic_derr << "will_write " << plan.will_write << dendl;
+
+ ASSERT_EQ(0u, plan.to_read.size());
+ ASSERT_EQ(1u, plan.will_write.size());
+}
+
+TEST(ectransaction, many_writes)
+{
+ hobject_t h;
+ PGTransactionUPtr t(new PGTransaction);
+ bufferlist a, b;
+ a.append_zero(512);
+ b.append_zero(4096);
+ t->create(h);
+
+ ECUtil::stripe_info_t sinfo(2, 8192);
+ // write 2801664~512
+ // write 2802176~512
+ // write 2802688~512
+ // write 2803200~512
+ t->write(h, 2801664, a.length(), a, 0);
+ t->write(h, 2802176, a.length(), a, 0);
+ t->write(h, 2802688, a.length(), a, 0);
+ t->write(h, 2803200, a.length(), a, 0);
+
+ // write 2805760~4096
+ // write 2809856~4096
+ // write 2813952~4096
+ t->write(h, 2805760, b.length(), b, 0);
+ t->write(h, 2809856, b.length(), b, 0);
+ t->write(h, 2813952, b.length(), b, 0);
+
+ auto plan = ECTransaction::get_write_plan(
+ sinfo,
+ std::move(t),
+ [&](const hobject_t &i) {
+ ECUtil::HashInfoRef ref(new ECUtil::HashInfo(1));
+ return ref;
+ },
+ &dpp);
+ generic_derr << "to_read " << plan.to_read << dendl;
+ generic_derr << "will_write " << plan.will_write << dendl;
+
+ ASSERT_EQ(0u, plan.to_read.size());
+ ASSERT_EQ(1u, plan.will_write.size());
+}
diff --git a/src/test/osd/test_extent_cache.cc b/src/test/osd/test_extent_cache.cc
new file mode 100644
index 000000000..9c789ca32
--- /dev/null
+++ b/src/test/osd/test_extent_cache.cc
@@ -0,0 +1,282 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#include <gtest/gtest.h>
+#include "osd/ExtentCache.h"
+#include <iostream>
+
+using namespace std;
+
+extent_map imap_from_vector(vector<pair<uint64_t, uint64_t> > &&in)
+{
+ extent_map out;
+ for (auto &&tup: in) {
+ bufferlist bl;
+ bl.append_zero(tup.second);
+ out.insert(tup.first, bl.length(), bl);
+ }
+ return out;
+}
+
+extent_map imap_from_iset(const extent_set &set)
+{
+ extent_map out;
+ for (auto &&iter: set) {
+ bufferlist bl;
+ bl.append_zero(iter.second);
+ out.insert(iter.first, iter.second, bl);
+ }
+ return out;
+}
+
+extent_set iset_from_vector(vector<pair<uint64_t, uint64_t> > &&in)
+{
+ extent_set out;
+ for (auto &&tup: in) {
+ out.insert(tup.first, tup.second);
+ }
+ return out;
+}
+
+TEST(extentcache, simple_write)
+{
+ hobject_t oid;
+
+ ExtentCache c;
+ ExtentCache::write_pin pin;
+ c.open_write_pin(pin);
+
+ auto to_read = iset_from_vector(
+ {{0, 2}, {8, 2}, {20, 2}});
+ auto to_write = iset_from_vector(
+ {{0, 10}, {20, 4}});
+ auto must_read = c.reserve_extents_for_rmw(
+ oid, pin, to_write, to_read);
+ ASSERT_EQ(
+ must_read,
+ to_read);
+
+ c.print(std::cerr);
+
+ auto got = imap_from_iset(must_read);
+ auto pending_read = to_read;
+ pending_read.subtract(must_read);
+
+ auto pending = c.get_remaining_extents_for_rmw(
+ oid,
+ pin,
+ pending_read);
+ ASSERT_TRUE(pending.empty());
+
+ auto write_map = imap_from_iset(to_write);
+ c.present_rmw_update(
+ oid,
+ pin,
+ write_map);
+
+ c.release_write_pin(pin);
+}
+
+TEST(extentcache, write_write_overlap)
+{
+ hobject_t oid;
+
+ ExtentCache c;
+ ExtentCache::write_pin pin;
+ c.open_write_pin(pin);
+
+ // start write 1
+ auto to_read = iset_from_vector(
+ {{0, 2}, {8, 2}, {20, 2}});
+ auto to_write = iset_from_vector(
+ {{0, 10}, {20, 4}});
+ auto must_read = c.reserve_extents_for_rmw(
+ oid, pin, to_write, to_read);
+ ASSERT_EQ(
+ must_read,
+ to_read);
+
+ c.print(std::cerr);
+
+ // start write 2
+ ExtentCache::write_pin pin2;
+ c.open_write_pin(pin2);
+ auto to_read2 = iset_from_vector(
+ {{2, 4}, {10, 4}, {18, 4}});
+ auto to_write2 = iset_from_vector(
+ {{2, 12}, {18, 12}});
+ auto must_read2 = c.reserve_extents_for_rmw(
+ oid, pin2, to_write2, to_read2);
+ ASSERT_EQ(
+ must_read2,
+ iset_from_vector({{10, 4}, {18, 2}}));
+
+ c.print(std::cerr);
+
+ // complete read for write 1 and start commit
+ auto got = imap_from_iset(must_read);
+ auto pending_read = to_read;
+ pending_read.subtract(must_read);
+ auto pending = c.get_remaining_extents_for_rmw(
+ oid,
+ pin,
+ pending_read);
+ ASSERT_TRUE(pending.empty());
+
+ auto write_map = imap_from_iset(to_write);
+ c.present_rmw_update(
+ oid,
+ pin,
+ write_map);
+
+ c.print(std::cerr);
+
+ // complete read for write 2 and start commit
+ auto pending_read2 = to_read2;
+ pending_read2.subtract(must_read2);
+ auto pending2 = c.get_remaining_extents_for_rmw(
+ oid,
+ pin2,
+ pending_read2);
+ ASSERT_EQ(
+ pending2,
+ imap_from_iset(pending_read2));
+
+ auto write_map2 = imap_from_iset(to_write2);
+ c.present_rmw_update(
+ oid,
+ pin2,
+ write_map2);
+
+ c.print(std::cerr);
+
+ c.release_write_pin(pin);
+
+ c.print(std::cerr);
+
+ c.release_write_pin(pin2);
+}
+
+TEST(extentcache, write_write_overlap2)
+{
+ hobject_t oid;
+
+ ExtentCache c;
+ ExtentCache::write_pin pin;
+ c.open_write_pin(pin);
+
+ // start write 1
+ auto to_read = extent_set();
+ auto to_write = iset_from_vector(
+ {{659456, 4096}});
+ auto must_read = c.reserve_extents_for_rmw(
+ oid, pin, to_write, to_read);
+ ASSERT_EQ(
+ must_read,
+ to_read);
+
+ c.print(std::cerr);
+
+ // start write 2
+ ExtentCache::write_pin pin2;
+ c.open_write_pin(pin2);
+ auto to_read2 = extent_set();
+ auto to_write2 = iset_from_vector(
+ {{663552, 4096}});
+ auto must_read2 = c.reserve_extents_for_rmw(
+ oid, pin2, to_write2, to_read2);
+ ASSERT_EQ(
+ must_read2,
+ to_read2);
+
+
+ // start write 3
+ ExtentCache::write_pin pin3;
+ c.open_write_pin(pin3);
+ auto to_read3 = iset_from_vector({{659456, 8192}});
+ auto to_write3 = iset_from_vector({{659456, 8192}});
+ auto must_read3 = c.reserve_extents_for_rmw(
+ oid, pin3, to_write3, to_read3);
+ ASSERT_EQ(
+ must_read3,
+ extent_set());
+
+ c.print(std::cerr);
+
+ // complete read for write 1 and start commit
+ auto got = imap_from_iset(must_read);
+ auto pending_read = to_read;
+ pending_read.subtract(must_read);
+ auto pending = c.get_remaining_extents_for_rmw(
+ oid,
+ pin,
+ pending_read);
+ ASSERT_TRUE(pending.empty());
+
+ auto write_map = imap_from_iset(to_write);
+ c.present_rmw_update(
+ oid,
+ pin,
+ write_map);
+
+ c.print(std::cerr);
+
+ // complete read for write 2 and start commit
+ auto pending_read2 = to_read2;
+ pending_read2.subtract(must_read2);
+ auto pending2 = c.get_remaining_extents_for_rmw(
+ oid,
+ pin2,
+ pending_read2);
+ ASSERT_EQ(
+ pending2,
+ imap_from_iset(pending_read2));
+
+ auto write_map2 = imap_from_iset(to_write2);
+ c.present_rmw_update(
+ oid,
+ pin2,
+ write_map2);
+
+ // complete read for write 2 and start commit
+ auto pending_read3 = to_read3;
+ pending_read3.subtract(must_read3);
+ auto pending3 = c.get_remaining_extents_for_rmw(
+ oid,
+ pin3,
+ pending_read3);
+ ASSERT_EQ(
+ pending3,
+ imap_from_iset(pending_read3));
+
+ auto write_map3 = imap_from_iset(to_write3);
+ c.present_rmw_update(
+ oid,
+ pin3,
+ write_map3);
+
+
+ c.print(std::cerr);
+
+ c.release_write_pin(pin);
+
+ c.print(std::cerr);
+
+ c.release_write_pin(pin2);
+
+ c.print(std::cerr);
+
+ c.release_write_pin(pin3);
+}
diff --git a/src/test/osd/test_pg_transaction.cc b/src/test/osd/test_pg_transaction.cc
new file mode 100644
index 000000000..6aa26920d
--- /dev/null
+++ b/src/test/osd/test_pg_transaction.cc
@@ -0,0 +1,131 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <gtest/gtest.h>
+#include "osd/PGTransaction.h"
+
+using namespace std;
+
+TEST(pgtransaction, simple)
+{
+ hobject_t h;
+ PGTransaction t;
+ ASSERT_TRUE(t.empty());
+ t.nop(h);
+ ASSERT_FALSE(t.empty());
+ unsigned num = 0;
+ t.safe_create_traverse(
+ [&](const pair<const hobject_t, PGTransaction::ObjectOperation> &p) {
+ ASSERT_EQ(p.first, h);
+ using T = PGTransaction::ObjectOperation::Init;
+ ASSERT_TRUE(boost::get<T::None>(&p.second.init_type));
+ ++num;
+ });
+ ASSERT_EQ(num, 1u);
+}
+
+TEST(pgtransaction, clone_safe_create_traverse)
+{
+ hobject_t h, h2;
+ h2.snap = 1;
+ PGTransaction t;
+ ASSERT_TRUE(t.empty());
+ t.nop(h2);
+ ASSERT_FALSE(t.empty());
+ t.clone(h, h2);
+ unsigned num = 0;
+ t.safe_create_traverse(
+ [&](const pair<const hobject_t, PGTransaction::ObjectOperation> &p) {
+ using T = PGTransaction::ObjectOperation::Init;
+ if (num == 0) {
+ ASSERT_EQ(p.first, h);
+ ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+ ASSERT_EQ(
+ boost::get<T::Clone>(&p.second.init_type)->source,
+ h2);
+ } else if (num == 1) {
+ ASSERT_EQ(p.first, h2);
+ ASSERT_TRUE(boost::get<T::None>(&p.second.init_type));
+ } else {
+ ASSERT_LT(num, 2u);
+ }
+ ++num;
+ });
+}
+
+TEST(pgtransaction, clone_safe_create_traverse2)
+{
+ hobject_t h, h2, h3;
+ h.snap = 10;
+ h2.snap = 5;
+ h3.snap = 3;
+ PGTransaction t;
+ ASSERT_TRUE(t.empty());
+ t.nop(h3);
+ ASSERT_FALSE(t.empty());
+ t.clone(h, h2);
+ t.remove(h2);
+ t.clone(h2, h3);
+ unsigned num = 0;
+ t.safe_create_traverse(
+ [&](const pair<const hobject_t, PGTransaction::ObjectOperation> &p) {
+ using T = PGTransaction::ObjectOperation::Init;
+ if (num == 0) {
+ ASSERT_EQ(p.first, h);
+ ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+ ASSERT_EQ(
+ boost::get<T::Clone>(&p.second.init_type)->source,
+ h2);
+ } else if (num == 1) {
+ ASSERT_EQ(p.first, h2);
+ ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+ ASSERT_EQ(
+ boost::get<T::Clone>(&p.second.init_type)->source,
+ h3);
+ } else if (num == 2) {
+ ASSERT_EQ(p.first, h3);
+ ASSERT_TRUE(boost::get<T::None>(&p.second.init_type));
+ } else {
+ ASSERT_LT(num, 3u);
+ }
+ ++num;
+ });
+}
+
+TEST(pgtransaction, clone_safe_create_traverse3)
+{
+ hobject_t h, h2, h3;
+ h.snap = 10;
+ h2.snap = 5;
+ h3.snap = 3;
+ PGTransaction t;
+ t.remove(h);
+ t.remove(h2);
+ t.clone(h2, h3);
+ unsigned num = 0;
+ t.safe_create_traverse(
+ [&](const pair<const hobject_t, PGTransaction::ObjectOperation> &p) {
+ using T = PGTransaction::ObjectOperation::Init;
+ if (p.first == h) {
+ ASSERT_TRUE(p.second.is_delete());
+ } else if (p.first == h2) {
+ ASSERT_TRUE(boost::get<T::Clone>(&p.second.init_type));
+ ASSERT_EQ(
+ boost::get<T::Clone>(&p.second.init_type)->source,
+ h3);
+ }
+ ASSERT_LT(num, 2u);
+ ++num;
+ });
+}
diff --git a/src/test/osd/test_scrub_sched.cc b/src/test/osd/test_scrub_sched.cc
new file mode 100644
index 000000000..efd7ba324
--- /dev/null
+++ b/src/test/osd/test_scrub_sched.cc
@@ -0,0 +1,402 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+/// \file testing the scrub scheduling algorithm
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <map>
+
+#include "common/async/context_pool.h"
+#include "common/ceph_argparse.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "include/utime_fmt.h"
+#include "mon/MonClient.h"
+#include "msg/Messenger.h"
+#include "os/ObjectStore.h"
+#include "osd/PG.h"
+#include "osd/osd_types.h"
+#include "osd/osd_types_fmt.h"
+#include "osd/scrubber/osd_scrub_sched.h"
+#include "osd/scrubber_common.h"
+
+int main(int argc, char** argv)
+{
+ std::map<std::string, std::string> defaults = {
+ // make sure we have 3 copies, or some tests won't work
+ {"osd_pool_default_size", "3"},
+ // our map is flat, so just try and split across OSDs, not hosts or whatever
+ {"osd_crush_chooseleaf_type", "0"},
+ };
+ std::vector<const char*> args(argv, argv + argc);
+ auto cct = global_init(&defaults,
+ args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+using schedule_result_t = Scrub::schedule_result_t;
+using ScrubJobRef = ScrubQueue::ScrubJobRef;
+using qu_state_t = ScrubQueue::qu_state_t;
+
+/// enabling access into ScrubQueue internals
+class ScrubSchedTestWrapper : public ScrubQueue {
+ public:
+ ScrubSchedTestWrapper(Scrub::ScrubSchedListener& osds)
+ : ScrubQueue(g_ceph_context, osds)
+ {}
+
+ void rm_unregistered_jobs()
+ {
+ ScrubQueue::rm_unregistered_jobs(to_scrub);
+ ScrubQueue::rm_unregistered_jobs(penalized);
+ }
+
+ ScrubQContainer collect_ripe_jobs()
+ {
+ return ScrubQueue::collect_ripe_jobs(to_scrub, time_now());
+ }
+
+ /**
+ * unit-test support for faking the current time. When
+ * not activated specifically - the default is to use ceph_clock_now()
+ */
+ void set_time_for_testing(long faked_now)
+ {
+ m_time_for_testing = utime_t{timeval{faked_now}};
+ }
+ void clear_time_for_testing() { m_time_for_testing.reset(); }
+ mutable std::optional<utime_t> m_time_for_testing;
+
+ utime_t time_now() const final
+ {
+ if (m_time_for_testing) {
+ m_time_for_testing->tv.tv_nsec += 1'000'000;
+ }
+ return m_time_for_testing.value_or(ceph_clock_now());
+ }
+
+ ~ScrubSchedTestWrapper() override = default;
+};
+
+
+/**
+ * providing the small number of OSD services used when scheduling
+ * a scrub
+ */
+class FakeOsd : public Scrub::ScrubSchedListener {
+ public:
+ FakeOsd(int osd_num) : m_osd_num(osd_num) {}
+
+ int get_nodeid() const final { return m_osd_num; }
+
+ schedule_result_t initiate_a_scrub(spg_t pgid,
+ bool allow_requested_repair_only) final
+ {
+ std::ignore = allow_requested_repair_only;
+ auto res = m_next_response.find(pgid);
+ if (res == m_next_response.end()) {
+ return schedule_result_t::no_such_pg;
+ }
+ return m_next_response[pgid];
+ }
+
+ void set_initiation_response(spg_t pgid, schedule_result_t result)
+ {
+ m_next_response[pgid] = result;
+ }
+
+ private:
+ int m_osd_num;
+ std::map<spg_t, schedule_result_t> m_next_response;
+};
+
+
+/// the static blueprint for creating a scrub job in the scrub queue
+struct sjob_config_t {
+ spg_t spg;
+ bool are_stats_valid;
+
+ utime_t history_scrub_stamp;
+ std::optional<double> pool_conf_min;
+ std::optional<double> pool_conf_max;
+ bool is_must;
+ bool is_need_auto;
+ ScrubQueue::scrub_schedule_t initial_schedule;
+};
+
+
+/**
+ * the runtime configuration for a scrub job. Created basde on the blueprint
+ * above (sjob_config_t)
+ */
+struct sjob_dynamic_data_t {
+ sjob_config_t initial_config;
+ pg_info_t mocked_pg_info;
+ pool_opts_t mocked_pool_opts;
+ requested_scrub_t request_flags;
+ ScrubQueue::ScrubJobRef job;
+};
+
+class TestScrubSched : public ::testing::Test {
+ public:
+ TestScrubSched() = default;
+
+ protected:
+ int m_osd_num{1};
+ FakeOsd m_osds{m_osd_num};
+ std::unique_ptr<ScrubSchedTestWrapper> m_sched{
+ new ScrubSchedTestWrapper(m_osds)};
+
+ /// the pg-info is queried for stats validity and for the last-scrub-stamp
+ pg_info_t pg_info{};
+
+ /// the pool configuration holds some per-pool scrub timing settings
+ pool_opts_t pool_opts{};
+
+ /**
+ * the scrub-jobs created for the tests, along with their corresponding
+ * "pg info" and pool configuration. In real life - the scrub jobs
+ * are owned by the respective PGs.
+ */
+ std::vector<sjob_dynamic_data_t> m_scrub_jobs;
+
+ protected:
+ sjob_dynamic_data_t create_scrub_job(const sjob_config_t& sjob_data)
+ {
+ sjob_dynamic_data_t dyn_data;
+ dyn_data.initial_config = sjob_data;
+
+ // populate the 'pool options' object with the scrub timing settings
+ if (sjob_data.pool_conf_min) {
+ dyn_data.mocked_pool_opts.set<double>(pool_opts_t::SCRUB_MIN_INTERVAL,
+ sjob_data.pool_conf_min.value());
+ }
+ if (sjob_data.pool_conf_max) {
+ dyn_data.mocked_pool_opts.set(pool_opts_t::SCRUB_MAX_INTERVAL,
+ sjob_data.pool_conf_max.value());
+ }
+
+ // create the 'pg info' object with the stats
+ dyn_data.mocked_pg_info = pg_info_t{sjob_data.spg};
+
+ dyn_data.mocked_pg_info.history.last_scrub_stamp =
+ sjob_data.history_scrub_stamp;
+ dyn_data.mocked_pg_info.stats.stats_invalid = !sjob_data.are_stats_valid;
+
+ // fake hust the required 'requested-scrub' flags
+ std::cout << "request_flags: sjob_data.is_must " << sjob_data.is_must
+ << std::endl;
+ dyn_data.request_flags.must_scrub = sjob_data.is_must;
+ dyn_data.request_flags.need_auto = sjob_data.is_need_auto;
+
+ // create the scrub job
+ dyn_data.job = ceph::make_ref<ScrubQueue::ScrubJob>(g_ceph_context,
+ sjob_data.spg,
+ m_osd_num);
+ m_scrub_jobs.push_back(dyn_data);
+ return dyn_data;
+ }
+
+ void register_job_set(const std::vector<sjob_config_t>& job_configs)
+ {
+ std::for_each(job_configs.begin(),
+ job_configs.end(),
+ [this](const sjob_config_t& sj) {
+ auto dynjob = create_scrub_job(sj);
+ m_sched->register_with_osd(
+ dynjob.job,
+ m_sched->determine_scrub_time(dynjob.request_flags,
+ dynjob.mocked_pg_info,
+ dynjob.mocked_pool_opts));
+ });
+ }
+
+ /// count the scrub-jobs that are currently in a specific state
+ int count_scrub_jobs_in_state(qu_state_t state)
+ {
+ return std::count_if(m_scrub_jobs.begin(),
+ m_scrub_jobs.end(),
+ [state](const sjob_dynamic_data_t& sj) {
+ return sj.job->state == state;
+ });
+ }
+
+ void list_testers_jobs(std::string hdr)
+ {
+ std::cout << fmt::format("{}: {} jobs created for the test:",
+ hdr,
+ m_scrub_jobs.size())
+ << std::endl;
+ for (const auto& job : m_scrub_jobs) {
+ std::cout << fmt::format("\t{}: job {}", hdr, *job.job) << std::endl;
+ }
+ }
+
+ void print_all_states(std::string hdr)
+ {
+ std::cout << fmt::format(
+ "{}: Created:{}. Per state: not-reg:{} reg:{} unreg:{}",
+ hdr,
+ m_scrub_jobs.size(),
+ count_scrub_jobs_in_state(qu_state_t::not_registered),
+ count_scrub_jobs_in_state(qu_state_t::registered),
+ count_scrub_jobs_in_state(qu_state_t::unregistering))
+ << std::endl;
+ }
+
+ void debug_print_jobs(std::string hdr,
+ const ScrubQueue::ScrubQContainer& jobs)
+ {
+ std::cout << fmt::format("{}: time now {}", hdr, m_sched->time_now())
+ << std::endl;
+ for (const auto& job : jobs) {
+ std::cout << fmt::format(
+ "\t{}: job {} ({}): scheduled {}",
+ hdr,
+ job->pgid,
+ job->scheduling_state(m_sched->time_now(), false),
+ job->get_sched_time())
+ << std::endl;
+ }
+ }
+};
+
+// ///////////////////////////////////////////////////////////////////////////
+// test data. Scrub-job creation requires a PG-id, and a set of 'scrub request'
+// flags
+
+namespace {
+
+// the times used during the tests are offset to 1.1.2000, so that
+// utime_t formatting will treat them as absolute (not as a relative time)
+static const auto epoch_2000 = 946'684'800;
+
+std::vector<sjob_config_t> sjob_configs = {
+ {
+ spg_t{pg_t{1, 1}},
+ true, // PG has valid stats
+ utime_t{std::time_t(epoch_2000 + 1'000'000), 0}, // last-scrub-stamp
+ 100.0, // min scrub delay in pool config
+ std::nullopt, // max scrub delay in pool config
+ false, // must-scrub
+ false, // need-auto
+ ScrubQueue::scrub_schedule_t{} // initial schedule
+ },
+
+ {spg_t{pg_t{4, 1}},
+ true,
+ utime_t{epoch_2000 + 1'000'000, 0},
+ 100.0,
+ std::nullopt,
+ true,
+ false,
+ ScrubQueue::scrub_schedule_t{}},
+
+ {spg_t{pg_t{7, 1}},
+ true,
+ utime_t{},
+ 1.0,
+ std::nullopt,
+ false,
+ false,
+ ScrubQueue::scrub_schedule_t{}},
+
+ {spg_t{pg_t{5, 1}},
+ true,
+ utime_t{epoch_2000 + 1'900'000, 0},
+ 1.0,
+ std::nullopt,
+ false,
+ false,
+ ScrubQueue::scrub_schedule_t{}}};
+
+} // anonymous namespace
+
+// //////////////////////////// tests ////////////////////////////////////////
+
+/// basic test: scheduling simple jobs, validating their calculated schedule
+TEST_F(TestScrubSched, populate_queue)
+{
+ ASSERT_EQ(0, m_sched->list_registered_jobs().size());
+
+ auto dynjob_0 = create_scrub_job(sjob_configs[0]);
+ auto suggested = m_sched->determine_scrub_time(dynjob_0.request_flags,
+ dynjob_0.mocked_pg_info,
+ dynjob_0.mocked_pool_opts);
+ m_sched->register_with_osd(dynjob_0.job, suggested);
+ std::cout << fmt::format("scheduled at: {}", dynjob_0.job->get_sched_time())
+ << std::endl;
+
+ auto dynjob_1 = create_scrub_job(sjob_configs[1]);
+ suggested = m_sched->determine_scrub_time(dynjob_1.request_flags,
+ dynjob_1.mocked_pg_info,
+ dynjob_1.mocked_pool_opts);
+ m_sched->register_with_osd(dynjob_1.job, suggested);
+ std::cout << fmt::format("scheduled at: {}", dynjob_1.job->get_sched_time())
+ << std::endl;
+
+ EXPECT_EQ(dynjob_1.job->get_sched_time(), utime_t(1, 1));
+ EXPECT_EQ(2, m_sched->list_registered_jobs().size());
+}
+
+/// validate the states of the scrub-jobs (as set in the jobs themselves)
+TEST_F(TestScrubSched, states)
+{
+ m_sched->set_time_for_testing(epoch_2000);
+ register_job_set(sjob_configs);
+ list_testers_jobs("testing states");
+ EXPECT_EQ(sjob_configs.size(), m_sched->list_registered_jobs().size());
+
+ // check the initial state of the jobs
+ print_all_states("<initial state>");
+ m_sched->rm_unregistered_jobs();
+ EXPECT_EQ(0, count_scrub_jobs_in_state(qu_state_t::not_registered));
+
+ // now - remove a couple of them
+ m_sched->remove_from_osd_queue(m_scrub_jobs[2].job);
+ m_sched->remove_from_osd_queue(m_scrub_jobs[1].job);
+ m_sched->remove_from_osd_queue(m_scrub_jobs[2].job); // should have no effect
+
+ print_all_states("<w/ 2 jobs removed>");
+ EXPECT_EQ(2, count_scrub_jobs_in_state(qu_state_t::registered));
+ EXPECT_EQ(2, count_scrub_jobs_in_state(qu_state_t::unregistering));
+
+ m_sched->rm_unregistered_jobs();
+ EXPECT_EQ(2, count_scrub_jobs_in_state(qu_state_t::not_registered));
+ std::cout << fmt::format("inp size: {}. In list-registered: {}",
+ sjob_configs.size(),
+ m_sched->list_registered_jobs().size())
+ << std::endl;
+ EXPECT_EQ(sjob_configs.size() - 2, m_sched->list_registered_jobs().size());
+}
+
+/// jobs that are ripe should be in the ready list, sorted by their scheduled
+/// time
+TEST_F(TestScrubSched, ready_list)
+{
+ m_sched->set_time_for_testing(epoch_2000 + 900'000);
+ register_job_set(sjob_configs);
+ list_testers_jobs("testing states");
+ EXPECT_EQ(sjob_configs.size(), m_sched->list_registered_jobs().size());
+
+ m_sched->set_time_for_testing(epoch_2000 + 1'000'000);
+ auto all_reg_jobs = m_sched->list_registered_jobs();
+ debug_print_jobs("registered", all_reg_jobs);
+
+ auto ripe_jobs = m_sched->collect_ripe_jobs();
+ EXPECT_EQ(2, ripe_jobs.size());
+ debug_print_jobs("ready_list", ripe_jobs);
+
+ m_sched->set_time_for_testing(epoch_2000 + 3'000'000);
+ // all jobs should be in the ready list
+ ripe_jobs = m_sched->collect_ripe_jobs();
+ EXPECT_EQ(4, ripe_jobs.size());
+ debug_print_jobs("ready_list", ripe_jobs);
+}
diff --git a/src/test/osd/test_scrubber_be.cc b/src/test/osd/test_scrubber_be.cc
new file mode 100644
index 000000000..65fd7e730
--- /dev/null
+++ b/src/test/osd/test_scrubber_be.cc
@@ -0,0 +1,669 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "./scrubber_generators.h"
+#include "./scrubber_test_datasets.h"
+
+#include <gtest/gtest.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include <fmt/ranges.h>
+
+#include "common/async/context_pool.h"
+#include "common/ceph_argparse.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "mon/MonClient.h"
+#include "msg/Messenger.h"
+#include "os/ObjectStore.h"
+#include "osd/PG.h"
+#include "osd/PGBackend.h"
+#include "osd/PrimaryLogPG.h"
+#include "osd/osd_types.h"
+#include "osd/osd_types_fmt.h"
+#include "osd/scrubber/pg_scrubber.h"
+#include "osd/scrubber/scrub_backend.h"
+
+/// \file testing isolated parts of the Scrubber backend
+
+using namespace std::string_literals;
+
+int main(int argc, char** argv)
+{
+ std::map<std::string, std::string> defaults = {
+ // make sure we have 3 copies, or some tests won't work
+ {"osd_pool_default_size", "3"},
+ // our map is flat, so just try and split across OSDs, not hosts or whatever
+ {"osd_crush_chooseleaf_type", "0"},
+ };
+ std::vector<const char*> args(argv, argv + argc);
+ auto cct = global_init(&defaults,
+ args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+
+class TestScrubBackend : public ScrubBackend {
+ public:
+ TestScrubBackend(ScrubBeListener& scrubber,
+ PgScrubBeListener& pg,
+ pg_shard_t i_am,
+ bool repair,
+ scrub_level_t shallow_or_deep,
+ const std::set<pg_shard_t>& acting)
+ : ScrubBackend(scrubber, pg, i_am, repair, shallow_or_deep, acting)
+ {}
+
+ bool get_m_repair() const { return m_repair; }
+ bool get_is_replicated() const { return m_is_replicated; }
+ auto get_omap_stats() const { return m_omap_stats; }
+
+ const std::vector<pg_shard_t>& all_but_me() const { return m_acting_but_me; }
+
+ /// populate the scrub-maps set for the 'chunk' being scrubbed
+ void insert_faked_smap(pg_shard_t shard, const ScrubMap& smap);
+};
+
+// mocking the PG
+class TestPg : public PgScrubBeListener {
+ public:
+ ~TestPg() = default;
+
+ TestPg(std::shared_ptr<PGPool> pool, pg_info_t& pginfo, pg_shard_t my_osd)
+ : m_pool{pool}
+ , m_info{pginfo}
+ , m_pshard{my_osd}
+ {}
+
+ const PGPool& get_pgpool() const final { return *(m_pool.get()); }
+ pg_shard_t get_primary() const final { return m_pshard; }
+ void force_object_missing(ScrubberPasskey,
+ const std::set<pg_shard_t>& peer,
+ const hobject_t& oid,
+ eversion_t version) final
+ {}
+
+ const pg_info_t& get_pg_info(ScrubberPasskey) const final { return m_info; }
+
+ uint64_t logical_to_ondisk_size(uint64_t logical_size) const final
+ {
+ return logical_size;
+ }
+
+ bool is_waiting_for_unreadable_object() const final { return false; }
+
+ std::shared_ptr<PGPool> m_pool;
+ pg_info_t& m_info;
+ pg_shard_t m_pshard;
+};
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// ///////////////////////////////////////////////////////////////////////////
+
+// and the scrubber
+class TestScrubber : public ScrubBeListener, public Scrub::SnapMapReaderI {
+ using result_t = Scrub::SnapMapReaderI::result_t;
+ public:
+ ~TestScrubber() = default;
+
+ TestScrubber(spg_t spg, OSDMapRef osdmap, LoggerSinkSet& logger)
+ : m_spg{spg}
+ , m_logger{logger}
+ , m_osdmap{osdmap}
+ {}
+
+ std::ostream& gen_prefix(std::ostream& out) const final { return out; }
+
+ CephContext* get_pg_cct() const final { return g_ceph_context; }
+
+ LoggerSinkSet& get_logger() const final { return m_logger; }
+
+ bool is_primary() const final { return m_primary; }
+
+ spg_t get_pgid() const final { return m_info.pgid; }
+
+ const OSDMapRef& get_osdmap() const final { return m_osdmap; }
+
+ void add_to_stats(const object_stat_sum_t& stat) final { m_stats.add(stat); }
+
+ // submit_digest_fixes() mock can be set to expect a specific set of
+ // fixes to perform.
+ /// \todo implement the mock.
+ void submit_digest_fixes(const digests_fixes_t& fixes) final
+ {
+ std::cout << fmt::format("{} submit_digest_fixes({})",
+ __func__,
+ fmt::join(fixes, ","))
+ << std::endl;
+ }
+
+ int get_snaps(const hobject_t& hoid,
+ std::set<snapid_t>* snaps_set) const;
+
+ tl::expected<std::set<snapid_t>, result_t> get_snaps(
+ const hobject_t& oid) const final;
+
+ tl::expected<std::set<snapid_t>, result_t> get_snaps_check_consistency(
+ const hobject_t& oid) const final
+ {
+ /// \todo for now
+ return get_snaps(oid);
+ }
+
+ void set_snaps(const hobject_t& hoid, const std::vector<snapid_t>& snaps)
+ {
+ std::cout
+ << fmt::format("{}: ({}) -> #{} {}", __func__, hoid, snaps.size(), snaps)
+ << std::endl;
+ std::set<snapid_t> snaps_set(snaps.begin(), snaps.end());
+ m_snaps[hoid] = snaps_set;
+ }
+
+ void set_snaps(const ScrubGenerator::all_clones_snaps_t& clones_snaps)
+ {
+ for (const auto& [clone, snaps] : clones_snaps) {
+ std::cout << fmt::format("{}: ({}) -> #{} {}",
+ __func__,
+ clone,
+ snaps.size(),
+ snaps)
+ << std::endl;
+ std::set<snapid_t> snaps_set(snaps.begin(), snaps.end());
+ m_snaps[clone] = snaps_set;
+ }
+ }
+
+ bool m_primary{true};
+ spg_t m_spg;
+ LoggerSinkSet& m_logger;
+ OSDMapRef m_osdmap;
+ pg_info_t m_info;
+ object_stat_sum_t m_stats;
+
+ // the "snap-mapper" database (returned by get_snaps())
+ std::map<hobject_t, std::set<snapid_t>> m_snaps;
+};
+
+int TestScrubber::get_snaps(const hobject_t& hoid,
+ std::set<snapid_t>* snaps_set) const
+{
+ auto it = m_snaps.find(hoid);
+ if (it == m_snaps.end()) {
+ std::cout << fmt::format("{}: ({}) no snaps", __func__, hoid) << std::endl;
+ return -ENOENT;
+ }
+
+ *snaps_set = it->second;
+ std::cout << fmt::format("{}: ({}) -> #{} {}",
+ __func__,
+ hoid,
+ snaps_set->size(),
+ *snaps_set)
+ << std::endl;
+ return 0;
+}
+
+tl::expected<std::set<snapid_t>, Scrub::SnapMapReaderI::result_t>
+TestScrubber::get_snaps(const hobject_t& oid) const
+{
+ std::set<snapid_t> snapset;
+ auto r = get_snaps(oid, &snapset);
+ if (r >= 0) {
+ return snapset;
+ }
+ return tl::make_unexpected(Scrub::SnapMapReaderI::result_t{
+ Scrub::SnapMapReaderI::result_t::code_t::not_found,
+ r});
+}
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// ///////////////////////////////////////////////////////////////////////////
+
+
+/// parameters for TestTScrubberBe construction
+struct TestTScrubberBeParams {
+ ScrubGenerator::pool_conf_t pool_conf;
+ ScrubGenerator::RealObjsConf objs_conf;
+ int num_osds;
+};
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// ///////////////////////////////////////////////////////////////////////////
+
+
+// the actual owner of the OSD "objects" that are used by
+// the mockers
+class TestTScrubberBe : public ::testing::Test {
+ public:
+ // the test data source
+ virtual TestTScrubberBeParams inject_params() = 0;
+
+ // initial test data
+ ScrubGenerator::MockLog logger;
+ ScrubGenerator::pool_conf_t pool_conf;
+ ScrubGenerator::RealObjsConf real_objs;
+ int num_osds{0};
+
+ // ctor & initialization
+
+ TestTScrubberBe() = default;
+ ~TestTScrubberBe() = default;
+ void SetUp() override;
+ void TearDown() override;
+
+ /**
+ * Create the set of scrub-maps supposedly sent by the replica (or
+ * generated by the Primary). Then - create the snap-sets for all
+ * the objects in the set.
+ */
+ void fake_a_scrub_set(ScrubGenerator::RealObjsConfList& all_sets);
+
+ std::unique_ptr<TestScrubBackend> sbe;
+
+ spg_t spg;
+ pg_shard_t i_am; // set to 'my osd and no shard'
+ std::set<pg_shard_t> acting_shards;
+ std::vector<int> acting_osds;
+ int acting_primary;
+
+ std::unique_ptr<TestScrubber> test_scrubber;
+
+ int64_t pool_id;
+ pg_pool_t pool_info;
+
+ OSDMapRef osdmap;
+
+ std::shared_ptr<PGPool> pool;
+ pg_info_t info;
+
+ std::unique_ptr<TestPg> test_pg;
+
+ // generated sets of "objects" for the active OSDs
+ ScrubGenerator::RealObjsConfList real_objs_list;
+
+ protected:
+ /**
+ * Create the OSDmap and populate it with one pool, based on
+ * the pool configuration.
+ * For now - only replicated pools are supported.
+ */
+ OSDMapRef setup_map(int num_osds, const ScrubGenerator::pool_conf_t& pconf);
+
+ /**
+ * Create a PG in the one pool we have. Fake the PG info.
+ * Use the primary of the PG to determine "who we are".
+ *
+ * \returns the PG info
+ */
+ pg_info_t setup_pg_in_map();
+};
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// ///////////////////////////////////////////////////////////////////////////
+
+void TestTScrubberBe::SetUp()
+{
+ std::cout << "TestTScrubberBe::SetUp()" << std::endl;
+ logger.err_count = 0;
+
+ // fetch test configuration
+ auto params = inject_params();
+ pool_conf = params.pool_conf;
+ real_objs = params.objs_conf;
+ num_osds = params.num_osds;
+
+ // create the OSDMap
+
+ osdmap = setup_map(num_osds, pool_conf);
+
+ std::cout << "osdmap: " << *osdmap << std::endl;
+
+ // extract the pool from the osdmap
+
+ pool_id = osdmap->lookup_pg_pool_name(pool_conf.name);
+ const pg_pool_t* ext_pool_info = osdmap->get_pg_pool(pool_id);
+ pool =
+ std::make_shared<PGPool>(osdmap, pool_id, *ext_pool_info, pool_conf.name);
+
+ std::cout << "pool: " << pool->info << std::endl;
+
+ // a PG in that pool?
+ info = setup_pg_in_map();
+ std::cout << fmt::format("PG info: {}", info) << std::endl;
+
+ real_objs_list =
+ ScrubGenerator::make_real_objs_conf(pool_id, real_objs, acting_osds);
+
+ // now we can create the main mockers
+
+ // the "PgScrubber"
+ test_scrubber = std::make_unique<TestScrubber>(spg, osdmap, logger);
+
+ // the "PG" (and its backend)
+ test_pg = std::make_unique<TestPg>(pool, info, i_am);
+ std::cout << fmt::format("{}: acting: {}", __func__, acting_shards)
+ << std::endl;
+ sbe = std::make_unique<TestScrubBackend>(*test_scrubber,
+ *test_pg,
+ i_am,
+ /* repair? */ false,
+ scrub_level_t::deep,
+ acting_shards);
+
+ // create a osd-num only copy of the relevant OSDs
+ acting_osds.reserve(acting_shards.size());
+ for (const auto& shard : acting_shards) {
+ acting_osds.push_back(shard.osd);
+ }
+
+ sbe->new_chunk();
+ fake_a_scrub_set(real_objs_list);
+}
+
+
+// Note: based on TestOSDMap.cc.
+OSDMapRef TestTScrubberBe::setup_map(int num_osds,
+ const ScrubGenerator::pool_conf_t& pconf)
+{
+ auto osdmap = std::make_shared<OSDMap>();
+ uuid_d fsid;
+ osdmap->build_simple(g_ceph_context, 0, fsid, num_osds);
+ OSDMap::Incremental pending_inc(osdmap->get_epoch() + 1);
+ pending_inc.fsid = osdmap->get_fsid();
+ entity_addrvec_t sample_addrs;
+ sample_addrs.v.push_back(entity_addr_t());
+ uuid_d sample_uuid;
+ for (int i = 0; i < num_osds; ++i) {
+ sample_uuid.generate_random();
+ sample_addrs.v[0].nonce = i;
+ pending_inc.new_state[i] = CEPH_OSD_EXISTS | CEPH_OSD_NEW;
+ pending_inc.new_up_client[i] = sample_addrs;
+ pending_inc.new_up_cluster[i] = sample_addrs;
+ pending_inc.new_hb_back_up[i] = sample_addrs;
+ pending_inc.new_hb_front_up[i] = sample_addrs;
+ pending_inc.new_weight[i] = CEPH_OSD_IN;
+ pending_inc.new_uuid[i] = sample_uuid;
+ }
+ osdmap->apply_incremental(pending_inc);
+
+ // create a replicated pool
+ OSDMap::Incremental new_pool_inc(osdmap->get_epoch() + 1);
+ new_pool_inc.new_pool_max = osdmap->get_pool_max();
+ new_pool_inc.fsid = osdmap->get_fsid();
+ uint64_t pool_id = ++new_pool_inc.new_pool_max;
+ pg_pool_t empty;
+ auto p = new_pool_inc.get_new_pool(pool_id, &empty);
+ p->size = pconf.size;
+ p->set_pg_num(pconf.pg_num);
+ p->set_pgp_num(pconf.pgp_num);
+ p->type = pg_pool_t::TYPE_REPLICATED;
+ p->crush_rule = 0;
+ p->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
+ new_pool_inc.new_pool_names[pool_id] = pconf.name;
+ osdmap->apply_incremental(new_pool_inc);
+ return osdmap;
+}
+
+pg_info_t TestTScrubberBe::setup_pg_in_map()
+{
+ pg_t rawpg(0, pool_id);
+ pg_t pgid = osdmap->raw_pg_to_pg(rawpg);
+ std::vector<int> up_osds;
+ int up_primary;
+
+ osdmap->pg_to_up_acting_osds(pgid,
+ &up_osds,
+ &up_primary,
+ &acting_osds,
+ &acting_primary);
+
+ std::cout << fmt::format(
+ "{}: pg: {} up_osds: {} up_primary: {} acting_osds: {} "
+ "acting_primary: "
+ "{}",
+ __func__,
+ pgid,
+ up_osds,
+ up_primary,
+ acting_osds,
+ acting_primary)
+ << std::endl;
+
+ spg = spg_t{pgid};
+ i_am = pg_shard_t{up_primary};
+ std::cout << fmt::format("{}: spg: {} and I am {}", __func__, spg, i_am)
+ << std::endl;
+
+ // the 'acting shards' set - the one actually used by the scrubber
+ std::for_each(acting_osds.begin(), acting_osds.end(), [&](int osd) {
+ acting_shards.insert(pg_shard_t{osd});
+ });
+ std::cout << fmt::format("{}: acting_shards: {}", __func__, acting_shards)
+ << std::endl;
+
+ pg_info_t info;
+ info.pgid = spg;
+ /// \todo: handle the epochs:
+ // info.last_update = osdmap->get_epoch();
+ // info.last_complete = osdmap->get_epoch();
+ // info.last_osdmap_epoch = osdmap->get_epoch();
+ // info.history.last_epoch_marked_removed = osdmap->get_epoch();
+ info.last_user_version = 1;
+ info.purged_snaps = {};
+ info.last_user_version = 1;
+ info.history.last_epoch_clean = osdmap->get_epoch();
+ info.history.last_epoch_split = osdmap->get_epoch();
+ info.history.last_epoch_marked_full = osdmap->get_epoch();
+ info.last_backfill = hobject_t::get_max();
+ return info;
+}
+
+void TestTScrubberBe::TearDown()
+{
+ EXPECT_EQ(logger.err_count, logger.expected_err_count);
+}
+
+void TestTScrubberBe::fake_a_scrub_set(
+ ScrubGenerator::RealObjsConfList& all_sets)
+{
+ for (int osd_num = 0; osd_num < pool_conf.size; ++osd_num) {
+ ScrubMap smap;
+ smap.valid_through = eversion_t{1, 1};
+ smap.incr_since = eversion_t{1, 1};
+ smap.has_omap_keys = true; // to force omap checks
+
+ // fill the map with the objects relevant to this OSD
+ for (auto& obj : all_sets[osd_num]->objs) {
+ std::cout << fmt::format("{}: object: {}", __func__, obj.ghobj.hobj)
+ << std::endl;
+ ScrubGenerator::add_object(smap, obj, osd_num);
+ }
+
+ std::cout << fmt::format("{}: {} inserting smap {:D}",
+ __func__,
+ osd_num,
+ smap)
+ << std::endl;
+ sbe->insert_faked_smap(pg_shard_t{osd_num}, smap);
+ }
+
+ // create the snap_mapper state
+
+ for (const auto& robj : all_sets[i_am.osd]->objs) {
+
+ std::cout << fmt::format("{}: object: {}", __func__, robj.ghobj.hobj)
+ << std::endl;
+
+ if (robj.ghobj.hobj.snap == CEPH_NOSNAP) {
+ // head object
+ auto objects_snapset = ScrubGenerator::all_clones(robj);
+ test_scrubber->set_snaps(objects_snapset);
+ }
+ }
+}
+
+void TestScrubBackend::insert_faked_smap(pg_shard_t shard, const ScrubMap& smap)
+{
+ ASSERT_TRUE(this_chunk.has_value());
+ std::cout << fmt::format("{}: inserting faked smap for osd {}",
+ __func__,
+ shard.osd)
+ << std::endl;
+ this_chunk->received_maps[shard] = smap;
+}
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// ///////////////////////////////////////////////////////////////////////////
+
+
+using namespace ScrubGenerator;
+
+class TestTScrubberBe_data_1 : public TestTScrubberBe {
+ public:
+ TestTScrubberBe_data_1() : TestTScrubberBe() {}
+
+ // test configuration
+ pool_conf_t pl{3, 3, 3, 3, "rep_pool"};
+
+ TestTScrubberBeParams inject_params() override
+ {
+ std::cout << fmt::format("{}: injecting params (minimal snaps conf.)",
+ __func__)
+ << std::endl;
+ return TestTScrubberBeParams{
+ /* pool_conf */ pl,
+ /* real_objs_conf */ ScrubDatasets::minimal_snaps_configuration,
+ /*num_osds */ 3};
+ }
+};
+
+// some basic sanity checks
+// (mainly testing the constructor)
+
+TEST_F(TestTScrubberBe_data_1, creation_1)
+{
+ /// \todo copy some osdmap tests from TestOSDMap.cc
+ ASSERT_TRUE(sbe);
+ ASSERT_TRUE(sbe->get_is_replicated());
+ ASSERT_FALSE(sbe->get_m_repair());
+ sbe->update_repair_status(true);
+ ASSERT_TRUE(sbe->get_m_repair());
+
+ // make sure *I* do not appear in 'all_but_me' set of OSDs
+ auto others = sbe->all_but_me();
+ auto in_others = std::find(others.begin(), others.end(), i_am);
+ EXPECT_EQ(others.end(), in_others);
+}
+
+
+TEST_F(TestTScrubberBe_data_1, smaps_creation_1)
+{
+ ASSERT_TRUE(sbe);
+ ASSERT_EQ(sbe->get_omap_stats().omap_bytes, 0);
+
+ // for test data 'minimal_snaps_configuration':
+ // scrub_compare_maps() should not emmit any error, nor
+ // return any snap-mapper fix
+ auto [incons, fix_list] = sbe->scrub_compare_maps(true, *test_scrubber);
+
+ EXPECT_EQ(fix_list.size(), 0); // snap-mapper fix should be empty
+
+ EXPECT_EQ(incons.size(), 0); // no inconsistency
+
+ // make sure the test did execute *something*
+ EXPECT_TRUE(sbe->get_omap_stats().omap_bytes != 0);
+}
+
+
+// whitebox testing (OK if failing after a change to the backend internals)
+
+
+// blackbox testing - testing the published functionality
+// (should not depend on internals of the backend)
+
+
+/// corrupt the snap_mapper data
+TEST_F(TestTScrubberBe_data_1, snapmapper_1)
+{
+ using snap_mapper_op_t = Scrub::snap_mapper_op_t;
+ ASSERT_TRUE(sbe);
+
+ // a bogus version of hobj_ms1_snp30 (a clone) snap_ids
+ hobject_t hobj_ms1_snp30_inpool = hobject_t{ScrubDatasets::hobj_ms1_snp30};
+ hobj_ms1_snp30_inpool.pool = pool_id;
+ all_clones_snaps_t bogus_30;
+ bogus_30[hobj_ms1_snp30_inpool] = {0x333, 0x666};
+
+ test_scrubber->set_snaps(bogus_30);
+ auto [incons, fix_list] = sbe->scrub_compare_maps(true, *test_scrubber);
+
+ EXPECT_EQ(fix_list.size(), 1);
+
+ // debug - print the fix-list:
+ for (const auto& fix : fix_list) {
+ std::cout << fmt::format("snapmapper_1: fix {}: {} {}->{}",
+ fix.hoid,
+ (fix.op == snap_mapper_op_t::add ? "add" : "upd"),
+ fix.wrong_snaps,
+ fix.snaps)
+ << std::endl;
+ }
+ EXPECT_EQ(fix_list[0].hoid, hobj_ms1_snp30_inpool);
+ EXPECT_EQ(fix_list[0].snaps, std::set<snapid_t>{0x30});
+
+ EXPECT_EQ(incons.size(), 0); // no inconsistency
+}
+
+// a dataset similar to 'minimal_snaps_configuration',
+// but with the hobj_ms1_snp30 clone being modified by a corruption
+// function
+class TestTScrubberBe_data_2 : public TestTScrubberBe {
+ public:
+ TestTScrubberBe_data_2() : TestTScrubberBe() {}
+
+ // basic test configuration - 3 OSDs, all involved in the pool
+ pool_conf_t pl{3, 3, 3, 3, "rep_pool"};
+
+ TestTScrubberBeParams inject_params() override
+ {
+ std::cout << fmt::format(
+ "{}: injecting params (minimal-snaps + size change)",
+ __func__)
+ << std::endl;
+ TestTScrubberBeParams params{
+ /* pool_conf */ pl,
+ /* real_objs_conf */ ScrubDatasets::minimal_snaps_configuration,
+ /*num_osds */ 3};
+
+ // inject a corruption function that will modify osd.0's version of
+ // the object
+ params.objs_conf.objs[0].corrupt_funcs = &ScrubDatasets::crpt_funcs_set1;
+ return params;
+ }
+};
+
+TEST_F(TestTScrubberBe_data_2, smaps_clone_size)
+{
+ ASSERT_TRUE(sbe);
+ EXPECT_EQ(sbe->get_omap_stats().omap_bytes, 0);
+ logger.set_expected_err_count(1);
+ auto [incons, fix_list] = sbe->scrub_compare_maps(true, *test_scrubber);
+
+ EXPECT_EQ(fix_list.size(), 0); // snap-mapper fix should be empty
+
+ EXPECT_EQ(incons.size(), 1); // one inconsistency
+}
+
+// Local Variables:
+// compile-command: "cd ../.. ; make unittest_osdscrub ; ./unittest_osdscrub
+// --log-to-stderr=true --debug-osd=20 # --gtest_filter=*.* " End:
diff --git a/src/test/osd/types.cc b/src/test/osd/types.cc
new file mode 100644
index 000000000..d7b7862f5
--- /dev/null
+++ b/src/test/osd/types.cc
@@ -0,0 +1,2204 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/types.h"
+#include "osd/osd_types.h"
+#include "osd/OSDMap.h"
+#include "gtest/gtest.h"
+#include "include/coredumpctl.h"
+#include "common/Thread.h"
+#include "include/stringify.h"
+#include "osd/ReplicatedBackend.h"
+#include <sstream>
+
+using namespace std;
+
+TEST(hobject, prefixes0)
+{
+ uint32_t mask = 0xE947FA20;
+ uint32_t bits = 12;
+ int64_t pool = 0;
+
+ set<string> prefixes_correct;
+ prefixes_correct.insert(string("0000000000000000.02A"));
+
+ set<string> prefixes_out(hobject_t::get_prefixes(bits, mask, pool));
+ ASSERT_EQ(prefixes_out, prefixes_correct);
+}
+
+TEST(hobject, prefixes1)
+{
+ uint32_t mask = 0x0000000F;
+ uint32_t bits = 6;
+ int64_t pool = 20;
+
+ set<string> prefixes_correct;
+ prefixes_correct.insert(string("0000000000000014.F0"));
+ prefixes_correct.insert(string("0000000000000014.F4"));
+ prefixes_correct.insert(string("0000000000000014.F8"));
+ prefixes_correct.insert(string("0000000000000014.FC"));
+
+ set<string> prefixes_out(hobject_t::get_prefixes(bits, mask, pool));
+ ASSERT_EQ(prefixes_out, prefixes_correct);
+}
+
+TEST(hobject, prefixes2)
+{
+ uint32_t mask = 0xDEADBEAF;
+ uint32_t bits = 25;
+ int64_t pool = 0;
+
+ set<string> prefixes_correct;
+ prefixes_correct.insert(string("0000000000000000.FAEBDA0"));
+ prefixes_correct.insert(string("0000000000000000.FAEBDA2"));
+ prefixes_correct.insert(string("0000000000000000.FAEBDA4"));
+ prefixes_correct.insert(string("0000000000000000.FAEBDA6"));
+ prefixes_correct.insert(string("0000000000000000.FAEBDA8"));
+ prefixes_correct.insert(string("0000000000000000.FAEBDAA"));
+ prefixes_correct.insert(string("0000000000000000.FAEBDAC"));
+ prefixes_correct.insert(string("0000000000000000.FAEBDAE"));
+
+ set<string> prefixes_out(hobject_t::get_prefixes(bits, mask, pool));
+ ASSERT_EQ(prefixes_out, prefixes_correct);
+}
+
+TEST(hobject, prefixes3)
+{
+ uint32_t mask = 0xE947FA20;
+ uint32_t bits = 32;
+ int64_t pool = 0x23;
+
+ set<string> prefixes_correct;
+ prefixes_correct.insert(string("0000000000000023.02AF749E"));
+
+ set<string> prefixes_out(hobject_t::get_prefixes(bits, mask, pool));
+ ASSERT_EQ(prefixes_out, prefixes_correct);
+}
+
+TEST(hobject, prefixes4)
+{
+ uint32_t mask = 0xE947FA20;
+ uint32_t bits = 0;
+ int64_t pool = 0x23;
+
+ set<string> prefixes_correct;
+ prefixes_correct.insert(string("0000000000000023."));
+
+ set<string> prefixes_out(hobject_t::get_prefixes(bits, mask, pool));
+ ASSERT_EQ(prefixes_out, prefixes_correct);
+}
+
+TEST(hobject, prefixes5)
+{
+ uint32_t mask = 0xDEADBEAF;
+ uint32_t bits = 1;
+ int64_t pool = 0x34AC5D00;
+
+ set<string> prefixes_correct;
+ prefixes_correct.insert(string("0000000034AC5D00.1"));
+ prefixes_correct.insert(string("0000000034AC5D00.3"));
+ prefixes_correct.insert(string("0000000034AC5D00.5"));
+ prefixes_correct.insert(string("0000000034AC5D00.7"));
+ prefixes_correct.insert(string("0000000034AC5D00.9"));
+ prefixes_correct.insert(string("0000000034AC5D00.B"));
+ prefixes_correct.insert(string("0000000034AC5D00.D"));
+ prefixes_correct.insert(string("0000000034AC5D00.F"));
+
+ set<string> prefixes_out(hobject_t::get_prefixes(bits, mask, pool));
+ ASSERT_EQ(prefixes_out, prefixes_correct);
+}
+
+TEST(pg_interval_t, check_new_interval)
+{
+// iterate through all 4 combinations
+for (unsigned i = 0; i < 4; ++i) {
+ //
+ // Create a situation where osdmaps are the same so that
+ // each test case can diverge from it using minimal code.
+ //
+ int osd_id = 1;
+ epoch_t epoch = 40;
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ std::shared_ptr<OSDMap> lastmap(new OSDMap());
+ lastmap->set_max_osd(10);
+ lastmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ lastmap->set_epoch(epoch);
+ epoch_t same_interval_since = epoch;
+ epoch_t last_epoch_clean = same_interval_since;
+ int64_t pool_id = 200;
+ int pg_num = 4;
+ __u8 min_size = 2;
+ boost::scoped_ptr<IsPGRecoverablePredicate> recoverable(new ReplicatedBackend::RPCRecPred());
+ {
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num);
+ inc.new_pools[pool_id].set_pg_num_pending(pg_num);
+ inc.new_up_thru[osd_id] = epoch + 1;
+ osdmap->apply_incremental(inc);
+ lastmap->apply_incremental(inc);
+ }
+ vector<int> new_acting;
+ new_acting.push_back(osd_id);
+ new_acting.push_back(osd_id + 1);
+ vector<int> old_acting = new_acting;
+ int old_primary = osd_id;
+ int new_primary = osd_id;
+ vector<int> new_up;
+ new_up.push_back(osd_id);
+ int old_up_primary = osd_id;
+ int new_up_primary = osd_id;
+ vector<int> old_up = new_up;
+ pg_t pgid;
+ pgid.set_pool(pool_id);
+
+ //
+ // Do nothing if there are no modifications in
+ // acting, up or pool size and that the pool is not
+ // being split
+ //
+ {
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_FALSE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals));
+ ASSERT_TRUE(past_intervals.empty());
+ }
+
+ //
+ // The acting set has changed
+ //
+ {
+ vector<int> new_acting;
+ int _new_primary = osd_id + 1;
+ new_acting.push_back(_new_primary);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals));
+ old_primary = new_primary;
+ }
+
+ //
+ // The up set has changed
+ //
+ {
+ vector<int> new_up;
+ int _new_primary = osd_id + 1;
+ new_up.push_back(_new_primary);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // The up primary has changed
+ //
+ {
+ vector<int> new_up;
+ int _new_up_primary = osd_id + 1;
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ _new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG is splitting
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ int new_pg_num = pg_num ^ 2;
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(new_pg_num);
+ osdmap->apply_incremental(inc);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG is pre-merge source
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num);
+ inc.new_pools[pool_id].set_pg_num_pending(pg_num - 1);
+ osdmap->apply_incremental(inc);
+ cout << "pg_num " << pg_num << std::endl;
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pg_t(pg_num - 1, pool_id),
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG was pre-merge source
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num);
+ inc.new_pools[pool_id].set_pg_num_pending(pg_num - 1);
+ osdmap->apply_incremental(inc);
+
+ cout << "pg_num " << pg_num << std::endl;
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ lastmap, // reverse order!
+ osdmap,
+ pg_t(pg_num - 1, pool_id),
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG is merge source
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num - 1);
+ osdmap->apply_incremental(inc);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pg_t(pg_num - 1, pool_id),
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG is pre-merge target
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num_pending(pg_num - 1);
+ osdmap->apply_incremental(inc);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pg_t(pg_num / 2 - 1, pool_id),
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG was pre-merge target
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num_pending(pg_num - 1);
+ osdmap->apply_incremental(inc);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ lastmap, // reverse order!
+ osdmap,
+ pg_t(pg_num / 2 - 1, pool_id),
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG is merge target
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num - 1);
+ osdmap->apply_incremental(inc);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pg_t(pg_num / 2 - 1, pool_id),
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // PG size has changed
+ //
+ {
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ __u8 new_min_size = min_size + 1;
+ inc.new_pools[pool_id].min_size = new_min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num);
+ osdmap->apply_incremental(inc);
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals));
+ }
+
+ //
+ // The old acting set was empty : the previous interval could not
+ // have been rw
+ //
+ {
+ vector<int> old_acting;
+
+ PastIntervals past_intervals;
+
+ ostringstream out;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals,
+ &out));
+ ASSERT_NE(string::npos, out.str().find("acting set is too small"));
+ }
+
+ //
+ // The old acting set did not have enough osd : it could
+ // not have been rw
+ //
+ {
+ vector<int> old_acting;
+ old_acting.push_back(osd_id);
+
+ //
+ // see http://tracker.ceph.com/issues/5780
+ // the size of the old acting set should be compared
+ // with the min_size of the old osdmap
+ //
+ // The new osdmap is created so that it triggers the
+ // bug.
+ //
+ std::shared_ptr<OSDMap> osdmap(new OSDMap());
+ osdmap->set_max_osd(10);
+ osdmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ osdmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ __u8 new_min_size = old_acting.size();
+ inc.new_pools[pool_id].min_size = new_min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num);
+ osdmap->apply_incremental(inc);
+
+ ostringstream out;
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals,
+ &out));
+ ASSERT_NE(string::npos, out.str().find("acting set is too small"));
+ }
+
+ //
+ // The acting set changes. The old acting set primary was up during the
+ // previous interval and may have been rw.
+ //
+ {
+ vector<int> new_acting;
+ new_acting.push_back(osd_id + 4);
+ new_acting.push_back(osd_id + 5);
+
+ ostringstream out;
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals,
+ &out));
+ ASSERT_NE(string::npos, out.str().find("includes interval"));
+ }
+ //
+ // The acting set changes. The old acting set primary was not up
+ // during the old interval but last_epoch_clean is in the
+ // old interval and it may have been rw.
+ //
+ {
+ vector<int> new_acting;
+ new_acting.push_back(osd_id + 4);
+ new_acting.push_back(osd_id + 5);
+
+ std::shared_ptr<OSDMap> lastmap(new OSDMap());
+ lastmap->set_max_osd(10);
+ lastmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ lastmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num);
+ inc.new_up_thru[osd_id] = epoch - 10;
+ lastmap->apply_incremental(inc);
+
+ ostringstream out;
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals,
+ &out));
+ ASSERT_NE(string::npos, out.str().find("presumed to have been rw"));
+ }
+
+ //
+ // The acting set changes. The old acting set primary was not up
+ // during the old interval and last_epoch_clean is before the
+ // old interval : the previous interval could not possibly have
+ // been rw.
+ //
+ {
+ vector<int> new_acting;
+ new_acting.push_back(osd_id + 4);
+ new_acting.push_back(osd_id + 5);
+
+ epoch_t last_epoch_clean = epoch - 10;
+
+ std::shared_ptr<OSDMap> lastmap(new OSDMap());
+ lastmap->set_max_osd(10);
+ lastmap->set_state(osd_id, CEPH_OSD_EXISTS);
+ lastmap->set_epoch(epoch);
+ OSDMap::Incremental inc(epoch + 1);
+ inc.new_pools[pool_id].min_size = min_size;
+ inc.new_pools[pool_id].set_pg_num(pg_num);
+ inc.new_up_thru[osd_id] = last_epoch_clean;
+ lastmap->apply_incremental(inc);
+
+ ostringstream out;
+
+ PastIntervals past_intervals;
+
+ ASSERT_TRUE(past_intervals.empty());
+ ASSERT_TRUE(PastIntervals::check_new_interval(old_primary,
+ new_primary,
+ old_acting,
+ new_acting,
+ old_up_primary,
+ new_up_primary,
+ old_up,
+ new_up,
+ same_interval_since,
+ last_epoch_clean,
+ osdmap,
+ lastmap,
+ pgid,
+ *recoverable,
+ &past_intervals,
+ &out));
+ ASSERT_NE(string::npos, out.str().find("does not include interval"));
+ }
+} // end for, didn't want to reindent
+}
+
+TEST(pg_t, get_ancestor)
+{
+ ASSERT_EQ(pg_t(0, 0), pg_t(16, 0).get_ancestor(16));
+ ASSERT_EQ(pg_t(1, 0), pg_t(17, 0).get_ancestor(16));
+ ASSERT_EQ(pg_t(0, 0), pg_t(16, 0).get_ancestor(8));
+ ASSERT_EQ(pg_t(16, 0), pg_t(16, 0).get_ancestor(80));
+ ASSERT_EQ(pg_t(16, 0), pg_t(16, 0).get_ancestor(83));
+ ASSERT_EQ(pg_t(1, 0), pg_t(1321, 0).get_ancestor(123).get_ancestor(8));
+ ASSERT_EQ(pg_t(3, 0), pg_t(1323, 0).get_ancestor(123).get_ancestor(8));
+ ASSERT_EQ(pg_t(3, 0), pg_t(1323, 0).get_ancestor(8));
+}
+
+TEST(pg_t, split)
+{
+ pg_t pgid(0, 0);
+ set<pg_t> s;
+ bool b;
+
+ s.clear();
+ b = pgid.is_split(1, 1, &s);
+ ASSERT_TRUE(!b);
+
+ s.clear();
+ b = pgid.is_split(2, 4, NULL);
+ ASSERT_TRUE(b);
+ b = pgid.is_split(2, 4, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(s.count(pg_t(2, 0)));
+
+ s.clear();
+ b = pgid.is_split(2, 8, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(3u, s.size());
+ ASSERT_TRUE(s.count(pg_t(2, 0)));
+ ASSERT_TRUE(s.count(pg_t(4, 0)));
+ ASSERT_TRUE(s.count(pg_t(6, 0)));
+
+ s.clear();
+ b = pgid.is_split(3, 8, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(s.count(pg_t(4, 0)));
+
+ s.clear();
+ b = pgid.is_split(6, 8, NULL);
+ ASSERT_TRUE(!b);
+ b = pgid.is_split(6, 8, &s);
+ ASSERT_TRUE(!b);
+ ASSERT_EQ(0u, s.size());
+
+ pgid = pg_t(1, 0);
+
+ s.clear();
+ b = pgid.is_split(2, 4, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(s.count(pg_t(3, 0)));
+
+ s.clear();
+ b = pgid.is_split(2, 6, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(2u, s.size());
+ ASSERT_TRUE(s.count(pg_t(3, 0)));
+ ASSERT_TRUE(s.count(pg_t(5, 0)));
+
+ s.clear();
+ b = pgid.is_split(2, 8, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(3u, s.size());
+ ASSERT_TRUE(s.count(pg_t(3, 0)));
+ ASSERT_TRUE(s.count(pg_t(5, 0)));
+ ASSERT_TRUE(s.count(pg_t(7, 0)));
+
+ s.clear();
+ b = pgid.is_split(4, 8, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(s.count(pg_t(5, 0)));
+
+ s.clear();
+ b = pgid.is_split(3, 8, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(3u, s.size());
+ ASSERT_TRUE(s.count(pg_t(3, 0)));
+ ASSERT_TRUE(s.count(pg_t(5, 0)));
+ ASSERT_TRUE(s.count(pg_t(7, 0)));
+
+ s.clear();
+ b = pgid.is_split(6, 8, &s);
+ ASSERT_TRUE(!b);
+ ASSERT_EQ(0u, s.size());
+
+ pgid = pg_t(3, 0);
+
+ s.clear();
+ b = pgid.is_split(7, 8, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(s.count(pg_t(7, 0)));
+
+ s.clear();
+ b = pgid.is_split(7, 12, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(2u, s.size());
+ ASSERT_TRUE(s.count(pg_t(7, 0)));
+ ASSERT_TRUE(s.count(pg_t(11, 0)));
+
+ s.clear();
+ b = pgid.is_split(7, 11, &s);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(s.count(pg_t(7, 0)));
+
+}
+
+TEST(pg_t, merge)
+{
+ pg_t pgid, parent;
+ bool b;
+
+ pgid = pg_t(7, 0);
+ b = pgid.is_merge_source(8, 7, &parent);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(parent, pg_t(3, 0));
+ ASSERT_TRUE(parent.is_merge_target(8, 7));
+
+ b = pgid.is_merge_source(8, 5, &parent);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(parent, pg_t(3, 0));
+ ASSERT_TRUE(parent.is_merge_target(8, 5));
+
+ b = pgid.is_merge_source(8, 4, &parent);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(parent, pg_t(3, 0));
+ ASSERT_TRUE(parent.is_merge_target(8, 4));
+
+ b = pgid.is_merge_source(8, 3, &parent);
+ ASSERT_TRUE(b);
+ ASSERT_EQ(parent, pg_t(1, 0));
+ ASSERT_TRUE(parent.is_merge_target(8, 4));
+
+ b = pgid.is_merge_source(9, 8, &parent);
+ ASSERT_FALSE(b);
+ ASSERT_FALSE(parent.is_merge_target(9, 8));
+}
+
+TEST(ObjectCleanRegions, mark_data_region_dirty)
+{
+ ObjectCleanRegions clean_regions;
+ uint64_t offset_1, len_1, offset_2, len_2;
+ offset_1 = 4096;
+ len_1 = 8192;
+ offset_2 = 40960;
+ len_2 = 4096;
+
+ interval_set<uint64_t> expect_dirty_region;
+ EXPECT_EQ(expect_dirty_region, clean_regions.get_dirty_regions());
+ expect_dirty_region.insert(offset_1, len_1);
+ expect_dirty_region.insert(offset_2, len_2);
+
+ clean_regions.mark_data_region_dirty(offset_1, len_1);
+ clean_regions.mark_data_region_dirty(offset_2, len_2);
+ EXPECT_EQ(expect_dirty_region, clean_regions.get_dirty_regions());
+}
+
+TEST(ObjectCleanRegions, mark_omap_dirty)
+{
+ ObjectCleanRegions clean_regions;
+
+ EXPECT_FALSE(clean_regions.omap_is_dirty());
+ clean_regions.mark_omap_dirty();
+ EXPECT_TRUE(clean_regions.omap_is_dirty());
+}
+
+TEST(ObjectCleanRegions, merge)
+{
+ ObjectCleanRegions cr1, cr2;
+ interval_set<uint64_t> cr1_expect;
+ interval_set<uint64_t> cr2_expect;
+ ASSERT_EQ(cr1_expect, cr1.get_dirty_regions());
+ ASSERT_EQ(cr2_expect, cr2.get_dirty_regions());
+
+ cr1.mark_data_region_dirty(4096, 4096);
+ cr1_expect.insert(4096, 4096);
+ ASSERT_EQ(cr1_expect, cr1.get_dirty_regions());
+ cr1.mark_data_region_dirty(12288, 8192);
+ cr1_expect.insert(12288, 8192);
+ ASSERT_TRUE(cr1_expect.subset_of(cr1.get_dirty_regions()));
+ cr1.mark_data_region_dirty(32768, 10240);
+ cr1_expect.insert(32768, 10240);
+ cr1_expect.erase(4096, 4096);
+ ASSERT_TRUE(cr1_expect.subset_of(cr1.get_dirty_regions()));
+
+ cr2.mark_data_region_dirty(20480, 12288);
+ cr2_expect.insert(20480, 12288);
+ ASSERT_EQ(cr2_expect, cr2.get_dirty_regions());
+ cr2.mark_data_region_dirty(102400, 4096);
+ cr2_expect.insert(102400, 4096);
+ cr2.mark_data_region_dirty(204800, 8192);
+ cr2_expect.insert(204800, 8192);
+ cr2.mark_data_region_dirty(409600, 4096);
+ cr2_expect.insert(409600, 4096);
+ ASSERT_TRUE(cr2_expect.subset_of(cr2.get_dirty_regions()));
+
+ ASSERT_FALSE(cr2.omap_is_dirty());
+ cr2.mark_omap_dirty();
+ ASSERT_FALSE(cr1.omap_is_dirty());
+ ASSERT_TRUE(cr2.omap_is_dirty());
+
+ cr1.merge(cr2);
+ cr1_expect.insert(204800, 8192);
+ ASSERT_TRUE(cr1_expect.subset_of(cr1.get_dirty_regions()));
+ ASSERT_TRUE(cr1.omap_is_dirty());
+}
+
+TEST(pg_missing_t, constructor)
+{
+ pg_missing_t missing;
+ EXPECT_EQ((unsigned int)0, missing.num_missing());
+ EXPECT_FALSE(missing.have_missing());
+}
+
+TEST(pg_missing_t, have_missing)
+{
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ EXPECT_FALSE(missing.have_missing());
+ missing.add(oid, eversion_t(), eversion_t(), false);
+ EXPECT_TRUE(missing.have_missing());
+}
+
+TEST(pg_missing_t, claim)
+{
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ EXPECT_FALSE(missing.have_missing());
+ missing.add(oid, eversion_t(), eversion_t(), false);
+ EXPECT_TRUE(missing.have_missing());
+
+ pg_missing_t other;
+ EXPECT_FALSE(other.have_missing());
+
+ other.claim(std::move(missing));
+ EXPECT_TRUE(other.have_missing());
+}
+
+TEST(pg_missing_t, is_missing)
+{
+ // pg_missing_t::is_missing(const hobject_t& oid) const
+ {
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add(oid, eversion_t(), eversion_t(), false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ }
+
+ // bool pg_missing_t::is_missing(const hobject_t& oid, eversion_t v) const
+ {
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ eversion_t need(10,5);
+ EXPECT_FALSE(missing.is_missing(oid, eversion_t()));
+ missing.add(oid, need, eversion_t(), false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_FALSE(missing.is_missing(oid, eversion_t()));
+ EXPECT_TRUE(missing.is_missing(oid, need));
+ }
+}
+
+TEST(pg_missing_t, add_next_event)
+{
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ hobject_t oid_other(object_t("other"), "key", 9123, 9456, 0, "");
+ eversion_t version(10,5);
+ eversion_t prior_version(3,4);
+ pg_log_entry_t sample_e(pg_log_entry_t::DELETE, oid, version, prior_version,
+ 0, osd_reqid_t(entity_name_t::CLIENT(777), 8, 999),
+ utime_t(8,9), 0);
+
+ // new object (MODIFY)
+ {
+ pg_missing_t missing;
+ pg_log_entry_t e = sample_e;
+
+ e.op = pg_log_entry_t::MODIFY;
+ e.prior_version = eversion_t();
+ EXPECT_TRUE(e.is_update());
+ EXPECT_TRUE(e.object_is_indexed());
+ EXPECT_TRUE(e.reqid_is_indexed());
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have);
+ EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+
+ // adding the same object replaces the previous one
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+ }
+
+ // new object (CLONE)
+ {
+ pg_missing_t missing;
+ pg_log_entry_t e = sample_e;
+
+ e.op = pg_log_entry_t::CLONE;
+ e.prior_version = eversion_t();
+ EXPECT_TRUE(e.is_clone());
+ EXPECT_TRUE(e.object_is_indexed());
+ EXPECT_FALSE(e.reqid_is_indexed());
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have);
+ EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+
+ // adding the same object replaces the previous one
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+ }
+
+ // existing object (MODIFY)
+ {
+ pg_missing_t missing;
+ pg_log_entry_t e = sample_e;
+
+ e.op = pg_log_entry_t::MODIFY;
+ e.prior_version = eversion_t();
+ EXPECT_TRUE(e.is_update());
+ EXPECT_TRUE(e.object_is_indexed());
+ EXPECT_TRUE(e.reqid_is_indexed());
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have);
+ EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+
+ // adding the same object with a different version
+ e.prior_version = prior_version;
+ missing.add_next_event(e);
+ EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+ }
+
+ // object with prior version (MODIFY)
+ {
+ pg_missing_t missing;
+ pg_log_entry_t e = sample_e;
+
+ e.op = pg_log_entry_t::MODIFY;
+ EXPECT_TRUE(e.is_update());
+ EXPECT_TRUE(e.object_is_indexed());
+ EXPECT_TRUE(e.reqid_is_indexed());
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(prior_version, missing.get_items().at(oid).have);
+ EXPECT_EQ(version, missing.get_items().at(oid).need);
+ EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+ }
+
+ // adding a DELETE matching an existing event
+ {
+ pg_missing_t missing;
+ pg_log_entry_t e = sample_e;
+
+ e.op = pg_log_entry_t::MODIFY;
+ EXPECT_TRUE(e.is_update());
+ EXPECT_TRUE(e.object_is_indexed());
+ EXPECT_TRUE(e.reqid_is_indexed());
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+
+ e.op = pg_log_entry_t::DELETE;
+ EXPECT_TRUE(e.is_delete());
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_TRUE(missing.get_items().at(oid).is_delete());
+ EXPECT_EQ(prior_version, missing.get_items().at(oid).have);
+ EXPECT_EQ(version, missing.get_items().at(oid).need);
+ EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+ }
+
+ // adding a LOST_DELETE after an existing event
+ {
+ pg_missing_t missing;
+ pg_log_entry_t e = sample_e;
+
+ e.op = pg_log_entry_t::MODIFY;
+ EXPECT_TRUE(e.is_update());
+ EXPECT_TRUE(e.object_is_indexed());
+ EXPECT_TRUE(e.reqid_is_indexed());
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_FALSE(missing.get_items().at(oid).is_delete());
+
+ e.op = pg_log_entry_t::LOST_DELETE;
+ e.version.version++;
+ EXPECT_TRUE(e.is_delete());
+ missing.add_next_event(e);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_TRUE(missing.get_items().at(oid).is_delete());
+ EXPECT_EQ(prior_version, missing.get_items().at(oid).have);
+ EXPECT_EQ(e.version, missing.get_items().at(oid).need);
+ EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version));
+ EXPECT_EQ(1U, missing.num_missing());
+ EXPECT_EQ(1U, missing.get_rmissing().size());
+ }
+}
+
+TEST(pg_missing_t, revise_need)
+{
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ // create a new entry
+ EXPECT_FALSE(missing.is_missing(oid));
+ eversion_t need(10,10);
+ missing.revise_need(oid, need, false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have);
+ EXPECT_EQ(need, missing.get_items().at(oid).need);
+ // update an existing entry and preserve have
+ eversion_t have(1,1);
+ missing.revise_have(oid, have);
+ eversion_t new_need(10,12);
+ EXPECT_EQ(have, missing.get_items().at(oid).have);
+ missing.revise_need(oid, new_need, false);
+ EXPECT_EQ(have, missing.get_items().at(oid).have);
+ EXPECT_EQ(new_need, missing.get_items().at(oid).need);
+}
+
+TEST(pg_missing_t, revise_have)
+{
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ // a non existing entry means noop
+ EXPECT_FALSE(missing.is_missing(oid));
+ eversion_t have(1,1);
+ missing.revise_have(oid, have);
+ EXPECT_FALSE(missing.is_missing(oid));
+ // update an existing entry
+ eversion_t need(10,12);
+ missing.add(oid, need, have, false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ eversion_t new_have(2,2);
+ EXPECT_EQ(have, missing.get_items().at(oid).have);
+ missing.revise_have(oid, new_have);
+ EXPECT_EQ(new_have, missing.get_items().at(oid).have);
+ EXPECT_EQ(need, missing.get_items().at(oid).need);
+}
+
+TEST(pg_missing_t, add)
+{
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ EXPECT_FALSE(missing.is_missing(oid));
+ eversion_t have(1,1);
+ eversion_t need(10,10);
+ missing.add(oid, need, have, false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ EXPECT_EQ(have, missing.get_items().at(oid).have);
+ EXPECT_EQ(need, missing.get_items().at(oid).need);
+}
+
+TEST(pg_missing_t, rm)
+{
+ // void pg_missing_t::rm(const hobject_t& oid, eversion_t v)
+ {
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ EXPECT_FALSE(missing.is_missing(oid));
+ epoch_t epoch = 10;
+ eversion_t need(epoch,10);
+ missing.add(oid, need, eversion_t(), false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ // rm of an older version is a noop
+ missing.rm(oid, eversion_t(epoch / 2,20));
+ EXPECT_TRUE(missing.is_missing(oid));
+ // rm of a later version removes the object
+ missing.rm(oid, eversion_t(epoch * 2,20));
+ EXPECT_FALSE(missing.is_missing(oid));
+ }
+ // void pg_missing_t::rm(const std::map<hobject_t, pg_missing_item>::iterator &m)
+ {
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add(oid, eversion_t(), eversion_t(), false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ auto m = missing.get_items().find(oid);
+ missing.rm(m);
+ EXPECT_FALSE(missing.is_missing(oid));
+ }
+}
+
+TEST(pg_missing_t, got)
+{
+ // void pg_missing_t::got(const hobject_t& oid, eversion_t v)
+ {
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ // assert if the oid does not exist
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(missing.got(oid, eversion_t()), "");
+ }
+ EXPECT_FALSE(missing.is_missing(oid));
+ epoch_t epoch = 10;
+ eversion_t need(epoch,10);
+ missing.add(oid, need, eversion_t(), false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ // assert if that the version to be removed is lower than the version of the object
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(missing.got(oid, eversion_t(epoch / 2,20)), "");
+ }
+ // remove of a later version removes the object
+ missing.got(oid, eversion_t(epoch * 2,20));
+ EXPECT_FALSE(missing.is_missing(oid));
+ }
+ // void pg_missing_t::got(const std::map<hobject_t, pg_missing_item>::iterator &m)
+ {
+ hobject_t oid(object_t("objname"), "key", 123, 456, 0, "");
+ pg_missing_t missing;
+ EXPECT_FALSE(missing.is_missing(oid));
+ missing.add(oid, eversion_t(), eversion_t(), false);
+ EXPECT_TRUE(missing.is_missing(oid));
+ auto m = missing.get_items().find(oid);
+ missing.got(m);
+ EXPECT_FALSE(missing.is_missing(oid));
+ }
+}
+
+TEST(pg_missing_t, split_into)
+{
+ uint32_t hash1 = 1;
+ hobject_t oid1(object_t("objname"), "key1", 123, hash1, 0, "");
+ uint32_t hash2 = 2;
+ hobject_t oid2(object_t("objname"), "key2", 123, hash2, 0, "");
+ pg_missing_t missing;
+ missing.add(oid1, eversion_t(), eversion_t(), false);
+ missing.add(oid2, eversion_t(), eversion_t(), false);
+ pg_t child_pgid;
+ child_pgid.m_seed = 1;
+ pg_missing_t child;
+ unsigned split_bits = 1;
+ missing.split_into(child_pgid, split_bits, &child);
+ EXPECT_TRUE(child.is_missing(oid1));
+ EXPECT_FALSE(child.is_missing(oid2));
+ EXPECT_FALSE(missing.is_missing(oid1));
+ EXPECT_TRUE(missing.is_missing(oid2));
+}
+
+TEST(pg_pool_t_test, get_pg_num_divisor) {
+ pg_pool_t p;
+ p.set_pg_num(16);
+ p.set_pgp_num(16);
+
+ for (int i = 0; i < 16; ++i)
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(i, 1)));
+
+ p.set_pg_num(12);
+ p.set_pgp_num(12);
+
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(0, 1)));
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(1, 1)));
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(2, 1)));
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(3, 1)));
+ ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(4, 1)));
+ ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(5, 1)));
+ ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(6, 1)));
+ ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(7, 1)));
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(8, 1)));
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(9, 1)));
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(10, 1)));
+ ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(11, 1)));
+}
+
+TEST(pg_pool_t_test, get_random_pg_position) {
+ srand(getpid());
+ for (int i = 0; i < 100; ++i) {
+ pg_pool_t p;
+ p.set_pg_num(1 + (rand() % 1000));
+ p.set_pgp_num(p.get_pg_num());
+ pg_t pgid(rand() % p.get_pg_num(), 1);
+ uint32_t h = p.get_random_pg_position(pgid, rand());
+ uint32_t ps = p.raw_hash_to_pg(h);
+ cout << p.get_pg_num() << " " << pgid << ": "
+ << h << " -> " << pg_t(ps, 1) << std::endl;
+ ASSERT_EQ(pgid.ps(), ps);
+ }
+}
+
+TEST(shard_id_t, iostream) {
+ set<shard_id_t> shards;
+ shards.insert(shard_id_t(0));
+ shards.insert(shard_id_t(1));
+ shards.insert(shard_id_t(2));
+ ostringstream out;
+ out << shards;
+ ASSERT_EQ(out.str(), "0,1,2");
+
+ shard_id_t noshard = shard_id_t::NO_SHARD;
+ shard_id_t zero(0);
+ ASSERT_GT(zero, noshard);
+}
+
+TEST(spg_t, parse) {
+ spg_t a(pg_t(1,2), shard_id_t::NO_SHARD);
+ spg_t aa, bb;
+ spg_t b(pg_t(3,2), shard_id_t(2));
+ std::string s = stringify(a);
+ ASSERT_TRUE(aa.parse(s.c_str()));
+ ASSERT_EQ(a, aa);
+
+ s = stringify(b);
+ ASSERT_TRUE(bb.parse(s.c_str()));
+ ASSERT_EQ(b, bb);
+}
+
+TEST(coll_t, parse) {
+ const char *ok[] = {
+ "meta",
+ "1.2_head",
+ "1.2_TEMP",
+ "1.2s3_head",
+ "1.3s2_TEMP",
+ "1.2s0_head",
+ 0
+ };
+ const char *bad[] = {
+ "foo",
+ "1.2_food",
+ "1.2_head ",
+ //" 1.2_head", // hrm, this parses, which is not ideal.. pg_t's fault?
+ "1.2_temp",
+ "1.2_HEAD",
+ "1.xS3_HEAD",
+ "1.2s_HEAD",
+ "1.2sfoo_HEAD",
+ 0
+ };
+ coll_t a;
+ for (int i = 0; ok[i]; ++i) {
+ cout << "check ok " << ok[i] << std::endl;
+ ASSERT_TRUE(a.parse(ok[i]));
+ ASSERT_EQ(string(ok[i]), a.to_str());
+ }
+ for (int i = 0; bad[i]; ++i) {
+ cout << "check bad " << bad[i] << std::endl;
+ ASSERT_FALSE(a.parse(bad[i]));
+ }
+}
+
+TEST(coll_t, temp) {
+ spg_t pgid;
+ coll_t foo(pgid);
+ ASSERT_EQ(foo.to_str(), string("0.0_head"));
+
+ coll_t temp = foo.get_temp();
+ ASSERT_EQ(temp.to_str(), string("0.0_TEMP"));
+
+ spg_t pgid2;
+ ASSERT_TRUE(temp.is_temp());
+ ASSERT_TRUE(temp.is_temp(&pgid2));
+ ASSERT_EQ(pgid, pgid2);
+}
+
+TEST(coll_t, assigment) {
+ spg_t pgid;
+ coll_t right(pgid);
+ ASSERT_EQ(right.to_str(), string("0.0_head"));
+
+ coll_t left, middle;
+
+ ASSERT_EQ(left.to_str(), string("meta"));
+ ASSERT_EQ(middle.to_str(), string("meta"));
+
+ left = middle = right;
+
+ ASSERT_EQ(left.to_str(), string("0.0_head"));
+ ASSERT_EQ(middle.to_str(), string("0.0_head"));
+
+ ASSERT_NE(middle.c_str(), right.c_str());
+ ASSERT_NE(left.c_str(), middle.c_str());
+}
+
+TEST(hobject_t, parse) {
+ const char *v[] = {
+ "MIN",
+ "MAX",
+ "-1:60c2fa6d:::inc_osdmap.1:0",
+ "-1:60c2fa6d:::inc_osdmap.1:333",
+ "0:00000000::::head",
+ "1:00000000:nspace:key:obj:head",
+ "-40:00000000:nspace::obj:head",
+ "20:00000000::key:obj:head",
+ "20:00000000:::o%fdj:head",
+ "20:00000000:::o%02fdj:head",
+ "20:00000000:::_zero_%00_:head",
+ NULL
+ };
+
+ for (unsigned i=0; v[i]; ++i) {
+ hobject_t o;
+ bool b = o.parse(v[i]);
+ if (!b) {
+ cout << "failed to parse " << v[i] << std::endl;
+ ASSERT_TRUE(false);
+ }
+ string s = stringify(o);
+ if (s != v[i]) {
+ cout << v[i] << " -> " << o << " -> " << s << std::endl;
+ ASSERT_EQ(s, string(v[i]));
+ }
+ }
+}
+
+TEST(ghobject_t, cmp) {
+ ghobject_t min;
+ ghobject_t sep;
+ sep.set_shard(shard_id_t(1));
+ sep.hobj.pool = -1;
+ cout << min << " < " << sep << std::endl;
+ ASSERT_TRUE(min < sep);
+
+ sep.set_shard(shard_id_t::NO_SHARD);
+ cout << "sep shard " << sep.shard_id << std::endl;
+ ghobject_t o(hobject_t(object_t(), string(), CEPH_NOSNAP, 0x42,
+ 1, string()));
+ cout << "o " << o << std::endl;
+ ASSERT_TRUE(o > sep);
+}
+
+TEST(ghobject_t, parse) {
+ const char *v[] = {
+ "GHMIN",
+ "GHMAX",
+ "13#0:00000000::::head#",
+ "13#0:00000000::::head#deadbeef",
+ "#-1:60c2fa6d:::inc_osdmap.1:333#deadbeef",
+ "#-1:60c2fa6d:::inc%02osdmap.1:333#deadbeef",
+ "#-1:60c2fa6d:::inc_osdmap.1:333#",
+ "1#MIN#deadbeefff",
+ "1#MAX#",
+ "#MAX#123",
+ "#-40:00000000:nspace::obj:head#",
+ NULL
+ };
+
+ for (unsigned i=0; v[i]; ++i) {
+ ghobject_t o;
+ bool b = o.parse(v[i]);
+ if (!b) {
+ cout << "failed to parse " << v[i] << std::endl;
+ ASSERT_TRUE(false);
+ }
+ string s = stringify(o);
+ if (s != v[i]) {
+ cout << v[i] << " -> " << o << " -> " << s << std::endl;
+ ASSERT_EQ(s, string(v[i]));
+ }
+ }
+}
+
+TEST(pool_opts_t, invalid_opt) {
+ EXPECT_FALSE(pool_opts_t::is_opt_name("INVALID_OPT"));
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(pool_opts_t::get_opt_desc("INVALID_OPT"), "");
+}
+
+TEST(pool_opts_t, scrub_min_interval) {
+ EXPECT_TRUE(pool_opts_t::is_opt_name("scrub_min_interval"));
+ EXPECT_EQ(pool_opts_t::get_opt_desc("scrub_min_interval"),
+ pool_opts_t::opt_desc_t(pool_opts_t::SCRUB_MIN_INTERVAL,
+ pool_opts_t::DOUBLE));
+
+ pool_opts_t opts;
+ EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MIN_INTERVAL));
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(opts.get(pool_opts_t::SCRUB_MIN_INTERVAL), "");
+ }
+ double val;
+ EXPECT_FALSE(opts.get(pool_opts_t::SCRUB_MIN_INTERVAL, &val));
+ opts.set(pool_opts_t::SCRUB_MIN_INTERVAL, static_cast<double>(2015));
+ EXPECT_TRUE(opts.get(pool_opts_t::SCRUB_MIN_INTERVAL, &val));
+ EXPECT_EQ(val, 2015);
+ opts.unset(pool_opts_t::SCRUB_MIN_INTERVAL);
+ EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MIN_INTERVAL));
+}
+
+TEST(pool_opts_t, scrub_max_interval) {
+ EXPECT_TRUE(pool_opts_t::is_opt_name("scrub_max_interval"));
+ EXPECT_EQ(pool_opts_t::get_opt_desc("scrub_max_interval"),
+ pool_opts_t::opt_desc_t(pool_opts_t::SCRUB_MAX_INTERVAL,
+ pool_opts_t::DOUBLE));
+
+ pool_opts_t opts;
+ EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MAX_INTERVAL));
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(opts.get(pool_opts_t::SCRUB_MAX_INTERVAL), "");
+ }
+ double val;
+ EXPECT_FALSE(opts.get(pool_opts_t::SCRUB_MAX_INTERVAL, &val));
+ opts.set(pool_opts_t::SCRUB_MAX_INTERVAL, static_cast<double>(2015));
+ EXPECT_TRUE(opts.get(pool_opts_t::SCRUB_MAX_INTERVAL, &val));
+ EXPECT_EQ(val, 2015);
+ opts.unset(pool_opts_t::SCRUB_MAX_INTERVAL);
+ EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MAX_INTERVAL));
+}
+
+TEST(pool_opts_t, deep_scrub_interval) {
+ EXPECT_TRUE(pool_opts_t::is_opt_name("deep_scrub_interval"));
+ EXPECT_EQ(pool_opts_t::get_opt_desc("deep_scrub_interval"),
+ pool_opts_t::opt_desc_t(pool_opts_t::DEEP_SCRUB_INTERVAL,
+ pool_opts_t::DOUBLE));
+
+ pool_opts_t opts;
+ EXPECT_FALSE(opts.is_set(pool_opts_t::DEEP_SCRUB_INTERVAL));
+ {
+ PrCtl unset_dumpable;
+ EXPECT_DEATH(opts.get(pool_opts_t::DEEP_SCRUB_INTERVAL), "");
+ }
+ double val;
+ EXPECT_FALSE(opts.get(pool_opts_t::DEEP_SCRUB_INTERVAL, &val));
+ opts.set(pool_opts_t::DEEP_SCRUB_INTERVAL, static_cast<double>(2015));
+ EXPECT_TRUE(opts.get(pool_opts_t::DEEP_SCRUB_INTERVAL, &val));
+ EXPECT_EQ(val, 2015);
+ opts.unset(pool_opts_t::DEEP_SCRUB_INTERVAL);
+ EXPECT_FALSE(opts.is_set(pool_opts_t::DEEP_SCRUB_INTERVAL));
+}
+
+struct RequiredPredicate : IsPGRecoverablePredicate {
+ unsigned required_size;
+ explicit RequiredPredicate(unsigned required_size) : required_size(required_size) {}
+ bool operator()(const set<pg_shard_t> &have) const override {
+ return have.size() >= required_size;
+ }
+};
+
+using namespace std;
+struct MapPredicate {
+ map<int, pair<PastIntervals::osd_state_t, epoch_t>> states;
+ explicit MapPredicate(
+ const vector<pair<int, pair<PastIntervals::osd_state_t, epoch_t>>> &_states)
+ : states(_states.begin(), _states.end()) {}
+ PastIntervals::osd_state_t operator()(epoch_t start, int osd, epoch_t *lost_at) {
+ auto val = states.at(osd);
+ if (lost_at)
+ *lost_at = val.second;
+ return val.first;
+ }
+};
+
+using sit = shard_id_t;
+using PI = PastIntervals;
+using pst = pg_shard_t;
+using ival = PastIntervals::pg_interval_t;
+using ivallst = std::list<ival>;
+const int N = 0x7fffffff /* CRUSH_ITEM_NONE, can't import crush.h here */;
+
+struct PITest : ::testing::Test {
+ PITest() {}
+ void run(
+ bool ec_pool,
+ ivallst intervals,
+ epoch_t last_epoch_started,
+ unsigned min_to_peer,
+ vector<pair<int, pair<PastIntervals::osd_state_t, epoch_t>>> osd_states,
+ vector<int> up,
+ vector<int> acting,
+ set<pg_shard_t> probe,
+ set<int> down,
+ map<int, epoch_t> blocked_by,
+ bool pg_down) {
+ RequiredPredicate rec_pred(min_to_peer);
+ MapPredicate map_pred(osd_states);
+
+ PI::PriorSet correct(
+ ec_pool,
+ probe,
+ down,
+ blocked_by,
+ pg_down,
+ new RequiredPredicate(rec_pred));
+
+ PastIntervals compact;
+ for (auto &&i: intervals) {
+ compact.add_interval(ec_pool, i);
+ }
+ PI::PriorSet compact_ps = compact.get_prior_set(
+ ec_pool,
+ last_epoch_started,
+ new RequiredPredicate(rec_pred),
+ map_pred,
+ up,
+ acting,
+ nullptr);
+ ASSERT_EQ(correct, compact_ps);
+ }
+};
+
+TEST_F(PITest, past_intervals_rep) {
+ run(
+ /* ec_pool */ false,
+ /* intervals */
+ { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0}
+ , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1}
+ , ival{{ 2}, { 2}, 31, 35, false, 2, 2}
+ , ival{{0, 2}, {0, 2}, 36, 50, true, 0, 0}
+ },
+ /* les */ 5,
+ /* min_peer */ 1,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::UP , 0))
+ , make_pair(2, make_pair(PI::DOWN , 0))
+ },
+ /* acting */ {0, 1 },
+ /* up */ {0, 1 },
+ /* probe */ {pst(0), pst(1)},
+ /* down */ {2},
+ /* blocked_by */ {},
+ /* pg_down */ false);
+}
+
+TEST_F(PITest, past_intervals_ec) {
+ run(
+ /* ec_pool */ true,
+ /* intervals */
+ { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0}
+ , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1}
+ },
+ /* les */ 5,
+ /* min_peer */ 2,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::DOWN , 0))
+ , make_pair(1, make_pair(PI::UP , 0))
+ , make_pair(2, make_pair(PI::UP , 0))
+ },
+ /* acting */ {N, 1, 2},
+ /* up */ {N, 1, 2},
+ /* probe */ {pst(1, sit(1)), pst(2, sit(2))},
+ /* down */ {0},
+ /* blocked_by */ {},
+ /* pg_down */ false);
+}
+
+TEST_F(PITest, past_intervals_rep_down) {
+ run(
+ /* ec_pool */ false,
+ /* intervals */
+ { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0}
+ , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1}
+ , ival{{ 2}, { 2}, 31, 35, true, 2, 2}
+ , ival{{0, 2}, {0, 2}, 36, 50, true, 0, 0}
+ },
+ /* les */ 5,
+ /* min_peer */ 1,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::UP , 0))
+ , make_pair(2, make_pair(PI::DOWN , 0))
+ },
+ /* acting */ {0, 1 },
+ /* up */ {0, 1 },
+ /* probe */ {pst(0), pst(1)},
+ /* down */ {2},
+ /* blocked_by */ {{2, 0}},
+ /* pg_down */ true);
+}
+
+TEST_F(PITest, past_intervals_ec_down) {
+ run(
+ /* ec_pool */ true,
+ /* intervals */
+ { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0}
+ , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1}
+ , ival{{N, N, 2}, {N, N, 2}, 31, 35, false, 2, 2}
+ },
+ /* les */ 5,
+ /* min_peer */ 2,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::DOWN , 0))
+ , make_pair(2, make_pair(PI::UP , 0))
+ },
+ /* acting */ {0, N, 2},
+ /* up */ {0, N, 2},
+ /* probe */ {pst(0, sit(0)), pst(2, sit(2))},
+ /* down */ {1},
+ /* blocked_by */ {{1, 0}},
+ /* pg_down */ true);
+}
+
+TEST_F(PITest, past_intervals_rep_no_subsets) {
+ run(
+ /* ec_pool */ false,
+ /* intervals */
+ { ival{{0, 2}, {0, 2}, 10, 20, true, 0, 0}
+ , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1}
+ , ival{{0, 1 }, {0, 1 }, 31, 35, true, 0, 0}
+ },
+ /* les */ 5,
+ /* min_peer */ 1,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::UP , 0))
+ , make_pair(2, make_pair(PI::DOWN , 0))
+ },
+ /* acting */ {0, 1 },
+ /* up */ {0, 1 },
+ /* probe */ {pst(0), pst(1)},
+ /* down */ {2},
+ /* blocked_by */ {},
+ /* pg_down */ false);
+}
+
+TEST_F(PITest, past_intervals_ec_no_subsets) {
+ run(
+ /* ec_pool */ true,
+ /* intervals */
+ { ival{{0, N, 2}, {0, N, 2}, 10, 20, true, 0, 0}
+ , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1}
+ , ival{{0, 1, N}, {0, 1, N}, 31, 35, true, 0, 0}
+ },
+ /* les */ 5,
+ /* min_peer */ 2,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::DOWN , 0))
+ , make_pair(2, make_pair(PI::UP , 0))
+ },
+ /* acting */ {0, N, 2},
+ /* up */ {0, N, 2},
+ /* probe */ {pst(0, sit(0)), pst(2, sit(2))},
+ /* down */ {1},
+ /* blocked_by */ {{1, 0}},
+ /* pg_down */ true);
+}
+
+TEST_F(PITest, past_intervals_ec_no_subsets2) {
+ run(
+ /* ec_pool */ true,
+ /* intervals */
+ { ival{{N, 1, 2}, {N, 1, 2}, 10, 20, true, 0, 0}
+ , ival{{0, N, 2}, {0, N, 2}, 21, 30, true, 1, 1}
+ , ival{{0, 3, N}, {0, 3, N}, 31, 35, true, 0, 0}
+ },
+ /* les */ 31,
+ /* min_peer */ 2,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::DOWN , 0))
+ , make_pair(2, make_pair(PI::UP , 0))
+ , make_pair(3, make_pair(PI::UP , 0))
+ },
+ /* acting */ {0, N, 2},
+ /* up */ {0, N, 2},
+ /* probe */ {pst(0, sit(0)), pst(2, sit(2)), pst(3, sit(1))},
+ /* down */ {1},
+ /* blocked_by */ {},
+ /* pg_down */ false);
+}
+
+TEST_F(PITest, past_intervals_rep_lost) {
+ run(
+ /* ec_pool */ false,
+ /* intervals */
+ { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0}
+ , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1}
+ , ival{{ 2}, { 2}, 31, 35, true, 2, 2}
+ , ival{{0, 2}, {0, 2}, 36, 50, true, 0, 0}
+ },
+ /* les */ 5,
+ /* min_peer */ 1,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::UP , 0))
+ , make_pair(2, make_pair(PI::LOST , 55))
+ },
+ /* acting */ {0, 1 },
+ /* up */ {0, 1 },
+ /* probe */ {pst(0), pst(1)},
+ /* down */ {2},
+ /* blocked_by */ {},
+ /* pg_down */ false);
+}
+
+TEST_F(PITest, past_intervals_ec_lost) {
+ run(
+ /* ec_pool */ true,
+ /* intervals */
+ { ival{{0, N, 2}, {0, N, 2}, 10, 20, true, 0, 0}
+ , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1}
+ , ival{{0, 1, N}, {0, 1, N}, 31, 35, true, 0, 0}
+ },
+ /* les */ 5,
+ /* min_peer */ 2,
+ /* osd states at end */
+ { make_pair(0, make_pair(PI::UP , 0))
+ , make_pair(1, make_pair(PI::LOST , 36))
+ , make_pair(2, make_pair(PI::UP , 0))
+ },
+ /* acting */ {0, N, 2},
+ /* up */ {0, N, 2},
+ /* probe */ {pst(0, sit(0)), pst(2, sit(2))},
+ /* down */ {1},
+ /* blocked_by */ {},
+ /* pg_down */ false);
+}
+
+void ci_ref_test(
+ object_manifest_t l,
+ object_manifest_t to_remove,
+ object_manifest_t g,
+ object_ref_delta_t expected_delta)
+{
+ {
+ object_ref_delta_t delta;
+ to_remove.calc_refs_to_drop_on_removal(
+ &l,
+ &g,
+ delta);
+ ASSERT_EQ(
+ expected_delta,
+ delta);
+ }
+
+ // calc_refs_to_drop specifically handles nullptr identically to empty
+ // chunk_map
+ if (l.chunk_map.empty() || g.chunk_map.empty()) {
+ object_ref_delta_t delta;
+ to_remove.calc_refs_to_drop_on_removal(
+ l.chunk_map.empty() ? nullptr : &l,
+ g.chunk_map.empty() ? nullptr : &g,
+ delta);
+ ASSERT_EQ(
+ expected_delta,
+ delta);
+ }
+}
+
+void ci_ref_test_on_modify(
+ object_manifest_t l,
+ object_manifest_t to_remove,
+ ObjectCleanRegions clean_regions,
+ object_ref_delta_t expected_delta)
+{
+ {
+ object_ref_delta_t delta;
+ to_remove.calc_refs_to_drop_on_modify(
+ &l,
+ clean_regions,
+ delta);
+ ASSERT_EQ(
+ expected_delta,
+ delta);
+ }
+}
+
+void ci_ref_test_inc_on_set(
+ object_manifest_t l,
+ object_manifest_t added_set,
+ object_manifest_t g,
+ object_ref_delta_t expected_delta)
+{
+ {
+ object_ref_delta_t delta;
+ added_set.calc_refs_to_inc_on_set(
+ &l,
+ &g,
+ delta);
+ ASSERT_EQ(
+ expected_delta,
+ delta);
+ }
+}
+
+hobject_t mk_hobject(string name)
+{
+ return hobject_t(
+ std::move(name),
+ string(),
+ CEPH_NOSNAP,
+ 0x42,
+ 1,
+ string());
+}
+
+object_manifest_t mk_manifest(
+ std::map<uint64_t, std::tuple<uint64_t, uint64_t, string>> m)
+{
+ object_manifest_t ret;
+ ret.type = object_manifest_t::TYPE_CHUNKED;
+ for (auto &[offset, tgt] : m) {
+ auto &[tgt_off, length, name] = tgt;
+ auto &ci = ret.chunk_map[offset];
+ ci.offset = tgt_off;
+ ci.length = length;
+ ci.oid = mk_hobject(name);
+ }
+ return ret;
+}
+
+object_ref_delta_t mk_delta(std::map<string, int> _m) {
+ std::map<hobject_t, int> m;
+ for (auto &[name, delta] : _m) {
+ m.insert(
+ std::make_pair(
+ mk_hobject(name),
+ delta));
+ }
+ return object_ref_delta_t(std::move(m));
+}
+
+TEST(chunk_info_test, calc_refs_to_drop) {
+ ci_ref_test(
+ mk_manifest({}),
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({}),
+ mk_delta({{"foo", -1}}));
+
+}
+
+
+TEST(chunk_info_test, calc_refs_to_drop_match) {
+ ci_ref_test(
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_delta({}));
+
+}
+
+TEST(chunk_info_test, calc_refs_to_drop_head_match) {
+ ci_ref_test(
+ mk_manifest({}),
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_delta({}));
+
+}
+
+TEST(chunk_info_test, calc_refs_to_drop_tail_match) {
+ ci_ref_test(
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({}),
+ mk_delta({}));
+
+}
+
+TEST(chunk_info_test, calc_refs_to_drop_second_reference) {
+ ci_ref_test(
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "foo"}}, {4<<10, {0, 1<<10, "foo"}}}),
+ mk_manifest({}),
+ mk_delta({{"foo", -1}}));
+
+}
+
+TEST(chunk_info_test, calc_refs_offsets_dont_match) {
+ ci_ref_test(
+ mk_manifest({{0, {0, 1024, "foo"}}}),
+ mk_manifest({{512, {0, 1024, "foo"}}, {(4<<10) + 512, {0, 1<<10, "foo"}}}),
+ mk_manifest({}),
+ mk_delta({{"foo", -2}}));
+
+}
+
+TEST(chunk_info_test, calc_refs_g_l_match) {
+ ci_ref_test(
+ mk_manifest({{4096, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "foo"}}, {4096, {0, 1024, "bar"}}}),
+ mk_manifest({{4096, {0, 1024, "foo"}}}),
+ mk_delta({{"foo", -2}, {"bar", -1}}));
+
+}
+
+TEST(chunk_info_test, calc_refs_g_l_match_no_this) {
+ ci_ref_test(
+ mk_manifest({{4096, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "bar"}}}),
+ mk_manifest({{4096, {0, 1024, "foo"}}}),
+ mk_delta({{"foo", -1}, {"bar", -1}}));
+
+}
+
+TEST(chunk_info_test, calc_refs_modify_mismatch) {
+ ObjectCleanRegions clean_regions(0, 8192, false);
+ clean_regions.mark_data_region_dirty(0, 1024);
+ clean_regions.mark_data_region_dirty(512, 1024);
+ ci_ref_test_on_modify(
+ mk_manifest({{512, {2048, 1024, "foo"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{0, {0, 1024, "bar"}}, {512, {2048, 1024, "ttt"}}}),
+ clean_regions,
+ mk_delta({{"bar", -1}, {"ttt", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_modify_match) {
+ ObjectCleanRegions clean_regions(0, 8192, false);
+ clean_regions.mark_data_region_dirty(0, 1024);
+ clean_regions.mark_data_region_dirty(512, 1024);
+ clean_regions.mark_data_region_dirty(4096, 1024);
+ ci_ref_test_on_modify(
+ mk_manifest({{512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ mk_manifest({{0, {0, 1024, "bar"}}, {512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ clean_regions,
+ mk_delta({{"bar", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_modify_match_dirty_overlap) {
+ ObjectCleanRegions clean_regions(0, 8192, false);
+ clean_regions.mark_data_region_dirty(0, 256);
+ clean_regions.mark_data_region_dirty(256, 4096);
+ ci_ref_test_on_modify(
+ mk_manifest({}),
+ mk_manifest({{0, {0, 256, "bar"}}, {512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ clean_regions,
+ mk_delta({{"bar", -1}, {"foo", -1}, {"ttt", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_modify_match_dirty_overlap2) {
+ ObjectCleanRegions clean_regions(0, 8192, false);
+ clean_regions.mark_data_region_dirty(0, 256);
+ clean_regions.mark_data_region_dirty(256, 1024);
+ clean_regions.mark_data_region_dirty(3584, 1024);
+ ci_ref_test_on_modify(
+ mk_manifest({{512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ mk_manifest({{0, {0, 256, "bar"}}, {512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ clean_regions,
+ mk_delta({{"bar", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_modify_match_dirty_overlap3) {
+ ObjectCleanRegions clean_regions(0, 8192, false);
+ clean_regions.mark_data_region_dirty(0, 256);
+ clean_regions.mark_data_region_dirty(256, 4096);
+ ci_ref_test_on_modify(
+ mk_manifest({{512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ mk_manifest({{0, {0, 256, "bar"}}, {512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ clean_regions,
+ mk_delta({{"bar", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_modify_match_clone_overlap) {
+ ObjectCleanRegions clean_regions(0, 8192, false);
+ clean_regions.mark_data_region_dirty(0, 256);
+ clean_regions.mark_data_region_dirty(256, 1024);
+ clean_regions.mark_data_region_dirty(3584, 1024);
+ ci_ref_test_on_modify(
+ mk_manifest({{512, {2048, 1024, "foo"}}, {4096, {0, 1024, "ttt"}}}),
+ mk_manifest({{0, {0, 256, "bar"}}, {256, {2048, 1024, "foo"}}, {3584, {0, 1024, "ttt"}}}),
+ clean_regions,
+ mk_delta({{"bar", -1}, {"foo", -1}, {"ttt", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_modify_no_snap) {
+ ObjectCleanRegions clean_regions(0, 8192, false);
+ clean_regions.mark_data_region_dirty(0, 1024);
+ clean_regions.mark_data_region_dirty(512, 1024);
+ ci_ref_test_on_modify(
+ mk_manifest({}),
+ mk_manifest({{0, {0, 1024, "bar"}}, {512, {2048, 1024, "ttt"}}}),
+ clean_regions,
+ mk_delta({{"bar", -1}, {"ttt", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_inc) {
+ ci_ref_test_inc_on_set(
+ mk_manifest({{256, {0, 256, "aaa"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{1024, {0, 1024, "bar"}}}),
+ mk_manifest({{4096, {0, 1024, "foo"}}}),
+ mk_delta({{"bar", 1}}));
+}
+
+TEST(chunk_info_test, calc_refs_inc2) {
+ ci_ref_test_inc_on_set(
+ mk_manifest({{512, {0, 1024, "aaa"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{1024, {0, 1024, "bar"}}, {4096, {0, 1024, "bbb"}}}),
+ mk_manifest({{512, {0, 1024, "foo"}}}),
+ mk_delta({{"bar", 1}, {"bbb", 1}}));
+}
+
+TEST(chunk_info_test, calc_refs_inc_no_l) {
+ ci_ref_test_inc_on_set(
+ mk_manifest({}),
+ mk_manifest({{1024, {0, 1024, "bar"}}, {4096, {0, 1024, "bbb"}}}),
+ mk_manifest({{512, {0, 1024, "foo"}}}),
+ mk_delta({{"bar", 1}, {"bbb", 1}}));
+}
+
+TEST(chunk_info_test, calc_refs_inc_no_g) {
+ ci_ref_test_inc_on_set(
+ mk_manifest({{512, {0, 1024, "aaa"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{1024, {0, 1024, "bar"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({}),
+ mk_delta({{"bar", 1}}));
+}
+
+TEST(chunk_info_test, calc_refs_inc_match_g_l) {
+ ci_ref_test_inc_on_set(
+ mk_manifest({{256, {0, 256, "aaa"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{256, {0, 256, "aaa"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{256, {0, 256, "aaa"}}, {4096, {0, 1024, "foo"}}}),
+ mk_delta({{"aaa", -1}, {"foo", -1}}));
+}
+
+TEST(chunk_info_test, calc_refs_inc_match) {
+ ci_ref_test_inc_on_set(
+ mk_manifest({{256, {0, 256, "bbb"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{256, {0, 256, "aaa"}}, {4096, {0, 1024, "foo"}}}),
+ mk_manifest({{256, {0, 256, "aaa"}}, {4096, {0, 1024, "ccc"}}}),
+ mk_delta({}));
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make unittest_osd_types ;
+ * ./unittest_osd_types # --gtest_filter=pg_missing_t.constructor
+ * "
+ * End:
+ */
diff --git a/src/test/osdc/CMakeLists.txt b/src/test/osdc/CMakeLists.txt
new file mode 100644
index 000000000..297c2672c
--- /dev/null
+++ b/src/test/osdc/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_executable(ceph_test_objectcacher_stress
+ object_cacher_stress.cc
+ FakeWriteback.cc
+ MemWriteback.cc
+ )
+target_link_libraries(ceph_test_objectcacher_stress
+ osdc
+ global
+ ${EXTRALIBS}
+ ${CMAKE_DL_LIBS}
+ )
+install(TARGETS ceph_test_objectcacher_stress
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/osdc/FakeWriteback.cc b/src/test/osdc/FakeWriteback.cc
new file mode 100644
index 000000000..2f58965cc
--- /dev/null
+++ b/src/test/osdc/FakeWriteback.cc
@@ -0,0 +1,93 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+#include <time.h>
+
+#include <thread>
+#include "common/debug.h"
+#include "common/Cond.h"
+#include "common/Finisher.h"
+#include "common/ceph_mutex.h"
+#include "include/ceph_assert.h"
+#include "common/ceph_time.h"
+
+#include "FakeWriteback.h"
+
+#define dout_subsys ceph_subsys_objectcacher
+#undef dout_prefix
+#define dout_prefix *_dout << "FakeWriteback(" << this << ") "
+
+class C_Delay : public Context {
+ CephContext *m_cct;
+ Context *m_con;
+ ceph::timespan m_delay;
+ ceph::mutex *m_lock;
+ bufferlist *m_bl;
+ uint64_t m_off;
+
+public:
+ C_Delay(CephContext *cct, Context *c, ceph::mutex *lock, uint64_t off,
+ bufferlist *pbl, uint64_t delay_ns=0)
+ : m_cct(cct), m_con(c), m_delay(delay_ns * std::chrono::nanoseconds(1)),
+ m_lock(lock), m_bl(pbl), m_off(off) {}
+ void finish(int r) override {
+ std::this_thread::sleep_for(m_delay);
+ if (m_bl) {
+ buffer::ptr bp(r);
+ bp.zero();
+ m_bl->append(bp);
+ ldout(m_cct, 20) << "finished read " << m_off << "~" << r << dendl;
+ }
+ std::lock_guard locker{*m_lock};
+ m_con->complete(r);
+ }
+};
+
+FakeWriteback::FakeWriteback(CephContext *cct, ceph::mutex *lock, uint64_t delay_ns)
+ : m_cct(cct), m_lock(lock), m_delay_ns(delay_ns)
+{
+ m_finisher = new Finisher(cct);
+ m_finisher->start();
+}
+
+FakeWriteback::~FakeWriteback()
+{
+ m_finisher->stop();
+ delete m_finisher;
+}
+
+void FakeWriteback::read(const object_t& oid, uint64_t object_no,
+ const object_locator_t& oloc,
+ uint64_t off, uint64_t len, snapid_t snapid,
+ bufferlist *pbl, uint64_t trunc_size,
+ __u32 trunc_seq, int op_flags,
+ const ZTracer::Trace &parent_trace,
+ Context *onfinish)
+{
+ C_Delay *wrapper = new C_Delay(m_cct, onfinish, m_lock, off, pbl,
+ m_delay_ns);
+ m_finisher->queue(wrapper, len);
+}
+
+ceph_tid_t FakeWriteback::write(const object_t& oid,
+ const object_locator_t& oloc,
+ uint64_t off, uint64_t len,
+ const SnapContext& snapc,
+ const bufferlist &bl, ceph::real_time mtime,
+ uint64_t trunc_size, __u32 trunc_seq,
+ ceph_tid_t journal_tid,
+ const ZTracer::Trace &parent_trace,
+ Context *oncommit)
+{
+ C_Delay *wrapper = new C_Delay(m_cct, oncommit, m_lock, off, NULL,
+ m_delay_ns);
+ m_finisher->queue(wrapper, 0);
+ return ++m_tid;
+}
+
+bool FakeWriteback::may_copy_on_write(const object_t&, uint64_t, uint64_t,
+ snapid_t)
+{
+ return false;
+}
diff --git a/src/test/osdc/FakeWriteback.h b/src/test/osdc/FakeWriteback.h
new file mode 100644
index 000000000..11f78e813
--- /dev/null
+++ b/src/test/osdc/FakeWriteback.h
@@ -0,0 +1,47 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_TEST_OSDC_FAKEWRITEBACK_H
+#define CEPH_TEST_OSDC_FAKEWRITEBACK_H
+
+#include "include/Context.h"
+#include "include/types.h"
+#include "osd/osd_types.h"
+#include "osdc/WritebackHandler.h"
+
+#include <atomic>
+
+class Finisher;
+
+class FakeWriteback : public WritebackHandler {
+public:
+ FakeWriteback(CephContext *cct, ceph::mutex *lock, uint64_t delay_ns);
+ ~FakeWriteback() override;
+
+ void read(const object_t& oid, uint64_t object_no,
+ const object_locator_t& oloc, uint64_t off, uint64_t len,
+ snapid_t snapid, bufferlist *pbl, uint64_t trunc_size,
+ __u32 trunc_seq, int op_flags,
+ const ZTracer::Trace &parent_trace,
+ Context *onfinish) override;
+
+ ceph_tid_t write(const object_t& oid, const object_locator_t& oloc,
+ uint64_t off, uint64_t len,
+ const SnapContext& snapc, const bufferlist &bl,
+ ceph::real_time mtime, uint64_t trunc_size,
+ __u32 trunc_seq, ceph_tid_t journal_tid,
+ const ZTracer::Trace &parent_trace,
+ Context *oncommit) override;
+
+ using WritebackHandler::write;
+
+ bool may_copy_on_write(const object_t&, uint64_t, uint64_t,
+ snapid_t) override;
+private:
+ CephContext *m_cct;
+ ceph::mutex *m_lock;
+ uint64_t m_delay_ns;
+ std::atomic<unsigned> m_tid = { 0 };
+ Finisher *m_finisher;
+};
+
+#endif
diff --git a/src/test/osdc/MemWriteback.cc b/src/test/osdc/MemWriteback.cc
new file mode 100644
index 000000000..4cb11291a
--- /dev/null
+++ b/src/test/osdc/MemWriteback.cc
@@ -0,0 +1,166 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+#include <time.h>
+
+#include <thread>
+#include "common/debug.h"
+#include "common/Cond.h"
+#include "common/Finisher.h"
+#include "common/ceph_mutex.h"
+#include "include/ceph_assert.h"
+#include "common/ceph_time.h"
+
+#include "MemWriteback.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_objectcacher
+#undef dout_prefix
+#define dout_prefix *_dout << "MemWriteback(" << this << ") "
+
+class C_DelayRead : public Context {
+ MemWriteback *wb;
+ CephContext *m_cct;
+ Context *m_con;
+ ceph::timespan m_delay;
+ ceph::mutex *m_lock;
+ object_t m_oid;
+ uint64_t m_off;
+ uint64_t m_len;
+ bufferlist *m_bl;
+
+public:
+ C_DelayRead(MemWriteback *mwb, CephContext *cct, Context *c, ceph::mutex *lock,
+ const object_t& oid, uint64_t off, uint64_t len, bufferlist *pbl,
+ uint64_t delay_ns=0)
+ : wb(mwb), m_cct(cct), m_con(c),
+ m_delay(delay_ns * std::chrono::nanoseconds(1)),
+ m_lock(lock), m_oid(oid), m_off(off), m_len(len), m_bl(pbl) {}
+ void finish(int r) override {
+ std::this_thread::sleep_for(m_delay);
+ std::lock_guard locker{*m_lock};
+ r = wb->read_object_data(m_oid, m_off, m_len, m_bl);
+ if (m_con)
+ m_con->complete(r);
+ }
+};
+
+class C_DelayWrite : public Context {
+ MemWriteback *wb;
+ CephContext *m_cct;
+ Context *m_con;
+ ceph::timespan m_delay;
+ ceph::mutex *m_lock;
+ object_t m_oid;
+ uint64_t m_off;
+ uint64_t m_len;
+ const bufferlist& m_bl;
+
+public:
+ C_DelayWrite(MemWriteback *mwb, CephContext *cct, Context *c, ceph::mutex *lock,
+ const object_t& oid, uint64_t off, uint64_t len,
+ const bufferlist& bl, uint64_t delay_ns=0)
+ : wb(mwb), m_cct(cct), m_con(c),
+ m_delay(delay_ns * std::chrono::nanoseconds(1)),
+ m_lock(lock), m_oid(oid), m_off(off), m_len(len), m_bl(bl) {}
+ void finish(int r) override {
+ std::this_thread::sleep_for(m_delay);
+ std::lock_guard locker{*m_lock};
+ wb->write_object_data(m_oid, m_off, m_len, m_bl);
+ if (m_con)
+ m_con->complete(r);
+ }
+};
+
+MemWriteback::MemWriteback(CephContext *cct, ceph::mutex *lock, uint64_t delay_ns)
+ : m_cct(cct), m_lock(lock), m_delay_ns(delay_ns)
+{
+ m_finisher = new Finisher(cct);
+ m_finisher->start();
+}
+
+MemWriteback::~MemWriteback()
+{
+ m_finisher->stop();
+ delete m_finisher;
+}
+
+void MemWriteback::read(const object_t& oid, uint64_t object_no,
+ const object_locator_t& oloc,
+ uint64_t off, uint64_t len, snapid_t snapid,
+ bufferlist *pbl, uint64_t trunc_size,
+ __u32 trunc_seq, int op_flags,
+ const ZTracer::Trace &parent_trace,
+ Context *onfinish)
+{
+ ceph_assert(snapid == CEPH_NOSNAP);
+ C_DelayRead *wrapper = new C_DelayRead(this, m_cct, onfinish, m_lock, oid,
+ off, len, pbl, m_delay_ns);
+ m_finisher->queue(wrapper, len);
+}
+
+ceph_tid_t MemWriteback::write(const object_t& oid,
+ const object_locator_t& oloc,
+ uint64_t off, uint64_t len,
+ const SnapContext& snapc,
+ const bufferlist &bl, ceph::real_time mtime,
+ uint64_t trunc_size, __u32 trunc_seq,
+ ceph_tid_t journal_tid,
+ const ZTracer::Trace &parent_trace,
+ Context *oncommit)
+{
+ ceph_assert(snapc.seq == 0);
+ C_DelayWrite *wrapper = new C_DelayWrite(this, m_cct, oncommit, m_lock, oid,
+ off, len, bl, m_delay_ns);
+ m_finisher->queue(wrapper, 0);
+ return ++m_tid;
+}
+
+void MemWriteback::write_object_data(const object_t& oid, uint64_t off, uint64_t len,
+ const bufferlist& data_bl)
+{
+ dout(1) << "writing " << oid << " " << off << "~" << len << dendl;
+ ceph_assert(len == data_bl.length());
+ bufferlist& obj_bl = object_data[oid];
+ bufferlist new_obj_bl;
+ // ensure size, or set it if new object
+ if (off + len > obj_bl.length()) {
+ obj_bl.append_zero(off + len - obj_bl.length());
+ }
+
+ // beginning
+ new_obj_bl.substr_of(obj_bl, 0, off);
+ // overwritten bit
+ new_obj_bl.append(data_bl);
+ // tail bit
+ bufferlist tmp;
+ tmp.substr_of(obj_bl, off+len, obj_bl.length()-(off+len));
+ new_obj_bl.append(tmp);
+ obj_bl.swap(new_obj_bl);
+ dout(1) << oid << " final size " << obj_bl.length() << dendl;
+}
+
+int MemWriteback::read_object_data(const object_t& oid, uint64_t off, uint64_t len,
+ bufferlist *data_bl)
+{
+ dout(1) << "reading " << oid << " " << off << "~" << len << dendl;
+ auto obj_i = object_data.find(oid);
+ if (obj_i == object_data.end()) {
+ dout(1) << oid << "DNE!" << dendl;
+ return -ENOENT;
+ }
+
+ const bufferlist& obj_bl = obj_i->second;
+ dout(1) << "reading " << oid << " from total size " << obj_bl.length() << dendl;
+
+ uint64_t read_len = std::min(len, obj_bl.length()-off);
+ data_bl->substr_of(obj_bl, off, read_len);
+ return 0;
+}
+
+bool MemWriteback::may_copy_on_write(const object_t&, uint64_t, uint64_t,
+ snapid_t)
+{
+ return false;
+}
diff --git a/src/test/osdc/MemWriteback.h b/src/test/osdc/MemWriteback.h
new file mode 100644
index 000000000..12c1ac3c0
--- /dev/null
+++ b/src/test/osdc/MemWriteback.h
@@ -0,0 +1,52 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_TEST_OSDC_MEMWRITEBACK_H
+#define CEPH_TEST_OSDC_MEMWRITEBACK_H
+
+#include "include/Context.h"
+#include "include/types.h"
+#include "osd/osd_types.h"
+#include "osdc/WritebackHandler.h"
+
+#include <atomic>
+
+class Finisher;
+
+class MemWriteback : public WritebackHandler {
+public:
+ MemWriteback(CephContext *cct, ceph::mutex *lock, uint64_t delay_ns);
+ ~MemWriteback() override;
+
+ void read(const object_t& oid, uint64_t object_no,
+ const object_locator_t& oloc, uint64_t off, uint64_t len,
+ snapid_t snapid, bufferlist *pbl, uint64_t trunc_size,
+ __u32 trunc_seq, int op_flags,
+ const ZTracer::Trace &parent_trace,
+ Context *onfinish) override;
+
+ ceph_tid_t write(const object_t& oid, const object_locator_t& oloc,
+ uint64_t off, uint64_t len,
+ const SnapContext& snapc, const bufferlist &bl,
+ ceph::real_time mtime, uint64_t trunc_size,
+ __u32 trunc_seq, ceph_tid_t journal_tid,
+ const ZTracer::Trace &parent_trace,
+ Context *oncommit) override;
+
+ using WritebackHandler::write;
+
+ bool may_copy_on_write(const object_t&, uint64_t, uint64_t,
+ snapid_t) override;
+ void write_object_data(const object_t& oid, uint64_t off, uint64_t len,
+ const bufferlist& data_bl);
+ int read_object_data(const object_t& oid, uint64_t off, uint64_t len,
+ bufferlist *data_bl);
+private:
+ std::map<object_t, bufferlist> object_data;
+ CephContext *m_cct;
+ ceph::mutex *m_lock;
+ uint64_t m_delay_ns;
+ std::atomic<unsigned> m_tid = { 0 };
+ Finisher *m_finisher;
+};
+
+#endif
diff --git a/src/test/osdc/object_cacher_stress.cc b/src/test/osdc/object_cacher_stress.cc
new file mode 100644
index 000000000..096f9b49e
--- /dev/null
+++ b/src/test/osdc/object_cacher_stress.cc
@@ -0,0 +1,425 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <cstdlib>
+#include <ctime>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <boost/scoped_ptr.hpp>
+
+#include "common/ceph_argparse.h"
+#include "common/ceph_mutex.h"
+#include "common/common_init.h"
+#include "common/config.h"
+#include "common/snap_types.h"
+#include "global/global_init.h"
+#include "include/buffer.h"
+#include "include/Context.h"
+#include "include/stringify.h"
+#include "osdc/ObjectCacher.h"
+
+#include "FakeWriteback.h"
+#include "MemWriteback.h"
+
+#include <atomic>
+
+using namespace std;
+
+// XXX: Only tests default namespace
+struct op_data {
+ op_data(const std::string &oid, uint64_t offset, uint64_t len, bool read)
+ : extent(oid, 0, offset, len, 0), is_read(read)
+ {
+ extent.oloc.pool = 0;
+ extent.buffer_extents.push_back(make_pair(0, len));
+ }
+
+ ObjectExtent extent;
+ bool is_read;
+ ceph::bufferlist result;
+ std::atomic<unsigned> done = { 0 };
+};
+
+class C_Count : public Context {
+ op_data *m_op;
+ std::atomic<unsigned> *m_outstanding = nullptr;
+public:
+ C_Count(op_data *op, std::atomic<unsigned> *outstanding)
+ : m_op(op), m_outstanding(outstanding) {}
+ void finish(int r) override {
+ m_op->done++;
+ ceph_assert(*m_outstanding > 0);
+ (*m_outstanding)--;
+ }
+};
+
+int stress_test(uint64_t num_ops, uint64_t num_objs,
+ uint64_t max_obj_size, uint64_t delay_ns,
+ uint64_t max_op_len, float percent_reads)
+{
+ ceph::mutex lock = ceph::make_mutex("object_cacher_stress::object_cacher");
+ FakeWriteback writeback(g_ceph_context, &lock, delay_ns);
+
+ ObjectCacher obc(g_ceph_context, "test", writeback, lock, NULL, NULL,
+ g_conf()->client_oc_size,
+ g_conf()->client_oc_max_objects,
+ g_conf()->client_oc_max_dirty,
+ g_conf()->client_oc_target_dirty,
+ g_conf()->client_oc_max_dirty_age,
+ true);
+ obc.start();
+
+ std::atomic<unsigned> outstanding_reads = { 0 };
+ vector<std::shared_ptr<op_data> > ops;
+ ObjectCacher::ObjectSet object_set(NULL, 0, 0);
+ SnapContext snapc;
+ ceph::buffer::ptr bp(max_op_len);
+ ceph::bufferlist bl;
+ uint64_t journal_tid = 0;
+ bp.zero();
+ bl.append(bp);
+
+ // schedule ops
+ std::cout << "Test configuration:\n\n"
+ << setw(10) << "ops: " << num_ops << "\n"
+ << setw(10) << "objects: " << num_objs << "\n"
+ << setw(10) << "obj size: " << max_obj_size << "\n"
+ << setw(10) << "delay: " << delay_ns << "\n"
+ << setw(10) << "max op len: " << max_op_len << "\n"
+ << setw(10) << "percent reads: " << percent_reads << "\n\n";
+
+ for (uint64_t i = 0; i < num_ops; ++i) {
+ uint64_t offset = random() % max_obj_size;
+ uint64_t max_len = std::min(max_obj_size - offset, max_op_len);
+ // no zero-length operations
+ uint64_t length = random() % (std::max<uint64_t>(max_len - 1, 1)) + 1;
+ std::string oid = "test" + stringify(random() % num_objs);
+ bool is_read = random() < percent_reads * float(RAND_MAX);
+ std::shared_ptr<op_data> op(new op_data(oid, offset, length, is_read));
+ ops.push_back(op);
+ std::cout << "op " << i << " " << (is_read ? "read" : "write")
+ << " " << op->extent << "\n";
+ if (op->is_read) {
+ ObjectCacher::OSDRead *rd = obc.prepare_read(CEPH_NOSNAP, &op->result, 0);
+ rd->extents.push_back(op->extent);
+ outstanding_reads++;
+ Context *completion = new C_Count(op.get(), &outstanding_reads);
+ lock.lock();
+ int r = obc.readx(rd, &object_set, completion);
+ lock.unlock();
+ ceph_assert(r >= 0);
+ if ((uint64_t)r == length)
+ completion->complete(r);
+ else
+ ceph_assert(r == 0);
+ } else {
+ ObjectCacher::OSDWrite *wr = obc.prepare_write(snapc, bl,
+ ceph::real_time::min(), 0,
+ ++journal_tid);
+ wr->extents.push_back(op->extent);
+ lock.lock();
+ obc.writex(wr, &object_set, NULL);
+ lock.unlock();
+ }
+ }
+
+ // check that all reads completed
+ for (uint64_t i = 0; i < num_ops; ++i) {
+ if (!ops[i]->is_read)
+ continue;
+ std::cout << "waiting for read " << i << ops[i]->extent << std::endl;
+ uint64_t done = 0;
+ while (done == 0) {
+ done = ops[i]->done;
+ if (!done) {
+ usleep(500);
+ }
+ }
+ if (done > 1) {
+ std::cout << "completion called more than once!\n" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ lock.lock();
+ obc.release_set(&object_set);
+ lock.unlock();
+
+ int r = 0;
+ ceph::mutex mylock = ceph::make_mutex("librbd::ImageCtx::flush_cache");
+ ceph::condition_variable cond;
+ bool done;
+ Context *onfinish = new C_SafeCond(mylock, cond, &done, &r);
+ lock.lock();
+ bool already_flushed = obc.flush_set(&object_set, onfinish);
+ std::cout << "already flushed = " << already_flushed << std::endl;
+ lock.unlock();
+ {
+ std::unique_lock locker{mylock};
+ cond.wait(locker, [&done] { return done; });
+ }
+ lock.lock();
+ bool unclean = obc.release_set(&object_set);
+ lock.unlock();
+
+ if (unclean) {
+ std::cout << "unclean buffers left over!" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ obc.stop();
+
+ std::cout << "Test completed successfully." << std::endl;
+
+ return EXIT_SUCCESS;
+}
+
+int correctness_test(uint64_t delay_ns)
+{
+ std::cerr << "starting correctness test" << std::endl;
+ ceph::mutex lock = ceph::make_mutex("object_cacher_stress::object_cacher");
+ MemWriteback writeback(g_ceph_context, &lock, delay_ns);
+
+ ObjectCacher obc(g_ceph_context, "test", writeback, lock, NULL, NULL,
+ 1<<21, // max cache size, 2MB
+ 1, // max objects, just one
+ 1<<18, // max dirty, 256KB
+ 1<<17, // target dirty, 128KB
+ g_conf()->client_oc_max_dirty_age,
+ true);
+ obc.start();
+ std::cerr << "just start()ed ObjectCacher" << std::endl;
+
+ SnapContext snapc;
+ ceph_tid_t journal_tid = 0;
+ std::string oid("correctness_test_obj");
+ ObjectCacher::ObjectSet object_set(NULL, 0, 0);
+ ceph::bufferlist zeroes_bl;
+ zeroes_bl.append_zero(1<<20);
+
+ // set up a 4MB all-zero object
+ std::cerr << "writing 4x1MB object" << std::endl;
+ std::map<int, C_SaferCond> create_finishers;
+ for (int i = 0; i < 4; ++i) {
+ ObjectCacher::OSDWrite *wr = obc.prepare_write(snapc, zeroes_bl,
+ ceph::real_time::min(), 0,
+ ++journal_tid);
+ ObjectExtent extent(oid, 0, zeroes_bl.length()*i, zeroes_bl.length(), 0);
+ extent.oloc.pool = 0;
+ extent.buffer_extents.push_back(make_pair(0, 1<<20));
+ wr->extents.push_back(extent);
+ lock.lock();
+ obc.writex(wr, &object_set, &create_finishers[i]);
+ lock.unlock();
+ }
+
+ // write some 1-valued bits at 256-KB intervals for checking consistency
+ std::cerr << "Writing some 0xff values" << std::endl;
+ ceph::buffer::ptr ones(1<<16);
+ memset(ones.c_str(), 0xff, ones.length());
+ ceph::bufferlist ones_bl;
+ ones_bl.append(ones);
+ for (int i = 1<<18; i < 1<<22; i+=1<<18) {
+ ObjectCacher::OSDWrite *wr = obc.prepare_write(snapc, ones_bl,
+ ceph::real_time::min(), 0,
+ ++journal_tid);
+ ObjectExtent extent(oid, 0, i, ones_bl.length(), 0);
+ extent.oloc.pool = 0;
+ extent.buffer_extents.push_back(make_pair(0, 1<<16));
+ wr->extents.push_back(extent);
+ lock.lock();
+ obc.writex(wr, &object_set, &create_finishers[i]);
+ lock.unlock();
+ }
+
+ for (auto i = create_finishers.begin(); i != create_finishers.end(); ++i) {
+ i->second.wait();
+ }
+ std::cout << "Finished setting up object" << std::endl;
+ lock.lock();
+ C_SaferCond flushcond;
+ bool done = obc.flush_all(&flushcond);
+ if (!done) {
+ std::cout << "Waiting for flush" << std::endl;
+ lock.unlock();
+ flushcond.wait();
+ lock.lock();
+ }
+ lock.unlock();
+
+ /* now read the back half of the object in, check consistency,
+ */
+ std::cout << "Reading back half of object (1<<21~1<<21)" << std::endl;
+ bufferlist readbl;
+ C_SaferCond backreadcond;
+ ObjectCacher::OSDRead *back_half_rd = obc.prepare_read(CEPH_NOSNAP, &readbl, 0);
+ ObjectExtent back_half_extent(oid, 0, 1<<21, 1<<21, 0);
+ back_half_extent.oloc.pool = 0;
+ back_half_extent.buffer_extents.push_back(make_pair(0, 1<<21));
+ back_half_rd->extents.push_back(back_half_extent);
+ lock.lock();
+ int r = obc.readx(back_half_rd, &object_set, &backreadcond);
+ lock.unlock();
+ ceph_assert(r >= 0);
+ if (r == 0) {
+ std::cout << "Waiting to read data into cache" << std::endl;
+ r = backreadcond.wait();
+ }
+
+ ceph_assert(r == 1<<21);
+
+ /* Read the whole object in,
+ * verify we have to wait for it to complete,
+ * overwrite a small piece, (http://tracker.ceph.com/issues/16002),
+ * and check consistency */
+
+ readbl.clear();
+ std::cout<< "Reading whole object (0~1<<22)" << std::endl;
+ C_SaferCond frontreadcond;
+ ObjectCacher::OSDRead *whole_rd = obc.prepare_read(CEPH_NOSNAP, &readbl, 0);
+ ObjectExtent whole_extent(oid, 0, 0, 1<<22, 0);
+ whole_extent.oloc.pool = 0;
+ whole_extent.buffer_extents.push_back(make_pair(0, 1<<22));
+ whole_rd->extents.push_back(whole_extent);
+ lock.lock();
+ r = obc.readx(whole_rd, &object_set, &frontreadcond);
+ // we cleared out the cache by reading back half, it shouldn't pass immediately!
+ ceph_assert(r == 0);
+ std::cout << "Data (correctly) not available without fetching" << std::endl;
+
+ ObjectCacher::OSDWrite *verify_wr = obc.prepare_write(snapc, ones_bl,
+ ceph::real_time::min(), 0,
+ ++journal_tid);
+ ObjectExtent verify_extent(oid, 0, (1<<18)+(1<<16), ones_bl.length(), 0);
+ verify_extent.oloc.pool = 0;
+ verify_extent.buffer_extents.push_back(make_pair(0, 1<<16));
+ verify_wr->extents.push_back(verify_extent);
+ C_SaferCond verify_finisher;
+ obc.writex(verify_wr, &object_set, &verify_finisher);
+ lock.unlock();
+ std::cout << "wrote dirtying data" << std::endl;
+
+ std::cout << "Waiting to read data into cache" << std::endl;
+ frontreadcond.wait();
+ verify_finisher.wait();
+
+ std::cout << "Validating data" << std::endl;
+
+ for (int i = 1<<18; i < 1<<22; i+=1<<18) {
+ bufferlist ones_maybe;
+ ones_maybe.substr_of(readbl, i, ones_bl.length());
+ ceph_assert(0 == memcmp(ones_maybe.c_str(), ones_bl.c_str(), ones_bl.length()));
+ }
+ bufferlist ones_maybe;
+ ones_maybe.substr_of(readbl, (1<<18)+(1<<16), ones_bl.length());
+ ceph_assert(0 == memcmp(ones_maybe.c_str(), ones_bl.c_str(), ones_bl.length()));
+
+ std::cout << "validated that data is 0xff where it should be" << std::endl;
+
+ lock.lock();
+ C_SaferCond flushcond2;
+ done = obc.flush_all(&flushcond2);
+ if (!done) {
+ std::cout << "Waiting for final write flush" << std::endl;
+ lock.unlock();
+ flushcond2.wait();
+ lock.lock();
+ }
+
+ bool unclean = obc.release_set(&object_set);
+ if (unclean) {
+ std::cout << "unclean buffers left over!" << std::endl;
+ vector<ObjectExtent> discard_extents;
+ int i = 0;
+ for (auto oi = object_set.objects.begin(); !oi.end(); ++oi) {
+ discard_extents.emplace_back(oid, i++, 0, 1<<22, 0);
+ }
+ obc.discard_set(&object_set, discard_extents);
+ lock.unlock();
+ obc.stop();
+ goto fail;
+ }
+ lock.unlock();
+
+ obc.stop();
+
+ std::cout << "Testing ObjectCacher correctness complete" << std::endl;
+ return EXIT_SUCCESS;
+
+ fail:
+ return EXIT_FAILURE;
+}
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+
+ long long delay_ns = 0;
+ long long num_ops = 1000;
+ long long obj_bytes = 4 << 20;
+ long long max_len = 128 << 10;
+ long long num_objs = 10;
+ float percent_reads = 0.90;
+ int seed = time(0) % 100000;
+ bool stress = false;
+ bool correctness = false;
+ std::ostringstream err;
+ std::vector<const char*>::iterator i;
+ for (i = args.begin(); i != args.end();) {
+ if (ceph_argparse_witharg(args, i, &delay_ns, err, "--delay-ns", (char*)NULL)) {
+ if (!err.str().empty()) {
+ cerr << argv[0] << ": " << err.str() << std::endl;
+ return EXIT_FAILURE;
+ }
+ } else if (ceph_argparse_witharg(args, i, &num_ops, err, "--ops", (char*)NULL)) {
+ if (!err.str().empty()) {
+ cerr << argv[0] << ": " << err.str() << std::endl;
+ return EXIT_FAILURE;
+ }
+ } else if (ceph_argparse_witharg(args, i, &num_objs, err, "--objects", (char*)NULL)) {
+ if (!err.str().empty()) {
+ cerr << argv[0] << ": " << err.str() << std::endl;
+ return EXIT_FAILURE;
+ }
+ } else if (ceph_argparse_witharg(args, i, &obj_bytes, err, "--obj-size", (char*)NULL)) {
+ if (!err.str().empty()) {
+ cerr << argv[0] << ": " << err.str() << std::endl;
+ return EXIT_FAILURE;
+ }
+ } else if (ceph_argparse_witharg(args, i, &max_len, err, "--max-op-size", (char*)NULL)) {
+ if (!err.str().empty()) {
+ cerr << argv[0] << ": " << err.str() << std::endl;
+ return EXIT_FAILURE;
+ }
+ } else if (ceph_argparse_witharg(args, i, &percent_reads, err, "--percent-read", (char*)NULL)) {
+ if (!err.str().empty()) {
+ cerr << argv[0] << ": " << err.str() << std::endl;
+ return EXIT_FAILURE;
+ }
+ } else if (ceph_argparse_witharg(args, i, &seed, err, "--seed", (char*)NULL)) {
+ if (!err.str().empty()) {
+ cerr << argv[0] << ": " << err.str() << std::endl;
+ return EXIT_FAILURE;
+ }
+ } else if (ceph_argparse_flag(args, i, "--stress-test", NULL)) {
+ stress = true;
+ } else if (ceph_argparse_flag(args, i, "--correctness-test", NULL)) {
+ correctness = true;
+ } else {
+ cerr << "unknown option " << *i << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (stress) {
+ srandom(seed);
+ return stress_test(num_ops, num_objs, obj_bytes, delay_ns, max_len, percent_reads);
+ }
+ if (correctness) {
+ return correctness_test(delay_ns);
+ }
+}
diff --git a/src/test/perf_counters.cc b/src/test/perf_counters.cc
new file mode 100644
index 000000000..7ab9561bc
--- /dev/null
+++ b/src/test/perf_counters.cc
@@ -0,0 +1,692 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "include/int_types.h"
+#include "include/types.h" // FIXME: ordering shouldn't be important, but right
+ // now, this include has to come before the others.
+
+
+#include "common/perf_counters_key.h"
+#include "common/perf_counters_collection.h"
+#include "common/admin_socket_client.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "common/errno.h"
+#include "common/safe_io.h"
+
+#include "common/code_environment.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "include/msgr.h" // for CEPH_ENTITY_TYPE_CLIENT
+#include "gtest/gtest.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <map>
+#include <poll.h>
+#include <sstream>
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+#include <thread>
+
+#include "common/common_init.h"
+
+using namespace std;
+
+int main(int argc, char **argv) {
+ map<string,string> defaults = {
+ { "admin_socket", get_rand_socket_path() }
+ };
+ std::vector<const char*> args;
+ auto cct = global_init(&defaults, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE|
+ CINIT_FLAG_NO_CCT_PERF_COUNTERS);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+TEST(PerfCounters, SimpleTest) {
+ AdminSocketClient client(get_rand_socket_path());
+ std::string message;
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\" }", &message));
+ ASSERT_EQ("{}\n", message);
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"counter dump\" }", &message));
+ ASSERT_EQ("{}\n", message);
+}
+
+enum {
+ TEST_PERFCOUNTERS1_ELEMENT_FIRST = 200,
+ TEST_PERFCOUNTERS1_ELEMENT_1,
+ TEST_PERFCOUNTERS1_ELEMENT_2,
+ TEST_PERFCOUNTERS1_ELEMENT_3,
+ TEST_PERFCOUNTERS1_ELEMENT_LAST,
+};
+
+std::string sd(const char *c)
+{
+ std::string ret(c);
+ std::string::size_type sz = ret.size();
+ for (std::string::size_type i = 0; i < sz; ++i) {
+ if (ret[i] == '\'') {
+ ret[i] = '\"';
+ }
+ }
+ return ret;
+}
+
+static PerfCounters* setup_test_perfcounters1(CephContext *cct)
+{
+ PerfCountersBuilder bld(cct, "test_perfcounter_1",
+ TEST_PERFCOUNTERS1_ELEMENT_FIRST, TEST_PERFCOUNTERS1_ELEMENT_LAST);
+ bld.add_u64(TEST_PERFCOUNTERS1_ELEMENT_1, "element1");
+ bld.add_time(TEST_PERFCOUNTERS1_ELEMENT_2, "element2");
+ bld.add_time_avg(TEST_PERFCOUNTERS1_ELEMENT_3, "element3");
+ return bld.create_perf_counters();
+}
+
+TEST(PerfCounters, SinglePerfCounters) {
+ PerfCountersCollection *coll = g_ceph_context->get_perfcounters_collection();
+ PerfCounters* fake_pf = setup_test_perfcounters1(g_ceph_context);
+ coll->add(fake_pf);
+ AdminSocketClient client(get_rand_socket_path());
+ std::string msg;
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":0,"
+ "\"element2\":0.000000000,\"element3\":{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}}}"), msg);
+ fake_pf->inc(TEST_PERFCOUNTERS1_ELEMENT_1);
+ fake_pf->tset(TEST_PERFCOUNTERS1_ELEMENT_2, utime_t(0, 500000000));
+ fake_pf->tinc(TEST_PERFCOUNTERS1_ELEMENT_3, utime_t(100, 0));
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":1,"
+ "\"element2\":0.500000000,\"element3\":{\"avgcount\":1,\"sum\":100.000000000,\"avgtime\":100.000000000}}}"), msg);
+ fake_pf->tinc(TEST_PERFCOUNTERS1_ELEMENT_3, utime_t());
+ fake_pf->tinc(TEST_PERFCOUNTERS1_ELEMENT_3, utime_t(20,0));
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":1,\"element2\":0.500000000,"
+ "\"element3\":{\"avgcount\":3,\"sum\":120.000000000,\"avgtime\":40.000000000}}}"), msg);
+
+ fake_pf->reset();
+ msg.clear();
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":1,"
+ "\"element2\":0.000000000,\"element3\":{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}}}"), msg);
+
+}
+
+enum {
+ TEST_PERFCOUNTERS2_ELEMENT_FIRST = 400,
+ TEST_PERFCOUNTERS2_ELEMENT_FOO,
+ TEST_PERFCOUNTERS2_ELEMENT_BAR,
+ TEST_PERFCOUNTERS2_ELEMENT_LAST,
+};
+
+static PerfCounters* setup_test_perfcounter2(CephContext *cct)
+{
+ PerfCountersBuilder bld(cct, "test_perfcounter_2",
+ TEST_PERFCOUNTERS2_ELEMENT_FIRST, TEST_PERFCOUNTERS2_ELEMENT_LAST);
+ bld.add_u64(TEST_PERFCOUNTERS2_ELEMENT_FOO, "foo");
+ bld.add_time(TEST_PERFCOUNTERS2_ELEMENT_BAR, "bar");
+ return bld.create_perf_counters();
+}
+
+TEST(PerfCounters, MultiplePerfCounters) {
+ PerfCountersCollection *coll = g_ceph_context->get_perfcounters_collection();
+ coll->clear();
+ PerfCounters* fake_pf1 = setup_test_perfcounters1(g_ceph_context);
+ PerfCounters* fake_pf2 = setup_test_perfcounter2(g_ceph_context);
+ coll->add(fake_pf1);
+ coll->add(fake_pf2);
+ AdminSocketClient client(get_rand_socket_path());
+ std::string msg;
+
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":0,\"element2\":0.000000000,\"element3\":"
+ "{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}},\"test_perfcounter_2\":{\"foo\":0,\"bar\":0.000000000}}"), msg);
+
+ fake_pf1->inc(TEST_PERFCOUNTERS1_ELEMENT_1);
+ fake_pf1->inc(TEST_PERFCOUNTERS1_ELEMENT_1, 5);
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":6,\"element2\":0.000000000,\"element3\":"
+ "{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}},\"test_perfcounter_2\":{\"foo\":0,\"bar\":0.000000000}}"), msg);
+
+ coll->reset(string("test_perfcounter_1"));
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":6,\"element2\":0.000000000,\"element3\":"
+ "{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}},\"test_perfcounter_2\":{\"foo\":0,\"bar\":0.000000000}}"), msg);
+
+ fake_pf1->inc(TEST_PERFCOUNTERS1_ELEMENT_1);
+ fake_pf1->inc(TEST_PERFCOUNTERS1_ELEMENT_1, 6);
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":13,\"element2\":0.000000000,\"element3\":"
+ "{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}},\"test_perfcounter_2\":{\"foo\":0,\"bar\":0.000000000}}"), msg);
+
+ coll->reset(string("all"));
+ msg.clear();
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":13,\"element2\":0.000000000,\"element3\":"
+ "{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}},\"test_perfcounter_2\":{\"foo\":0,\"bar\":0.000000000}}"), msg);
+
+ coll->remove(fake_pf2);
+ delete fake_pf2;
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":13,\"element2\":0.000000000,"
+ "\"element3\":{\"avgcount\":0,\"sum\":0.000000000,\"avgtime\":0.000000000}}}"), msg);
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf schema\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"test_perfcounter_1\":{\"element1\":{\"type\":2,\"metric_type\":\"gauge\",\"value_type\":\"integer\",\"description\":\"\",\"nick\":\"\",\"priority\":0,\"units\":\"none\"},\"element2\":{\"type\":1,\"metric_type\":\"gauge\",\"value_type\":\"real\",\"description\":\"\",\"nick\":\"\",\"priority\":0,\"units\":\"none\"},\"element3\":{\"type\":5,\"metric_type\":\"gauge\",\"value_type\":\"real-integer-pair\",\"description\":\"\",\"nick\":\"\",\"priority\":0,\"units\":\"none\"}}}"), msg);
+ coll->clear();
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf dump\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ("{}", msg);
+}
+
+TEST(PerfCounters, ResetPerfCounters) {
+ AdminSocketClient client(get_rand_socket_path());
+ std::string msg;
+ PerfCountersCollection *coll = g_ceph_context->get_perfcounters_collection();
+ coll->clear();
+ PerfCounters* fake_pf1 = setup_test_perfcounters1(g_ceph_context);
+ coll->add(fake_pf1);
+
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf reset\", \"var\": \"all\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"success\":\"perf reset all\"}"), msg);
+
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf reset\", \"var\": \"test_perfcounter_1\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"success\":\"perf reset test_perfcounter_1\"}"), msg);
+
+ coll->clear();
+ ASSERT_EQ("", client.do_request("{ \"prefix\": \"perf reset\", \"var\": \"test_perfcounter_1\", \"format\": \"json\" }", &msg));
+ ASSERT_EQ(sd("{\"error\":\"Not find: test_perfcounter_1\"}"), msg);
+}
+
+enum {
+ TEST_PERFCOUNTERS3_ELEMENT_FIRST = 400,
+ TEST_PERFCOUNTERS3_ELEMENT_READ,
+ TEST_PERFCOUNTERS3_ELEMENT_LAST,
+};
+
+static std::shared_ptr<PerfCounters> setup_test_perfcounter3(CephContext* cct) {
+ PerfCountersBuilder bld(cct, "test_percounter_3",
+ TEST_PERFCOUNTERS3_ELEMENT_FIRST, TEST_PERFCOUNTERS3_ELEMENT_LAST);
+ bld.add_time_avg(TEST_PERFCOUNTERS3_ELEMENT_READ, "read_avg");
+ std::shared_ptr<PerfCounters> p(bld.create_perf_counters());
+ return p;
+}
+
+static void counters_inc_test(std::shared_ptr<PerfCounters> fake_pf) {
+ int i = 100000;
+ utime_t t;
+
+ // set to 1 nsec
+ t.set_from_double(0.000000001);
+ while (i--) {
+ // increase by one, make sure data.u64 equal to data.avgcount
+ fake_pf->tinc(TEST_PERFCOUNTERS3_ELEMENT_READ, t);
+ }
+}
+
+static void counters_readavg_test(std::shared_ptr<PerfCounters> fake_pf) {
+ int i = 100000;
+
+ while (i--) {
+ std::pair<uint64_t, uint64_t> dat = fake_pf->get_tavg_ns(TEST_PERFCOUNTERS3_ELEMENT_READ);
+ // sum and count should be identical as we increment TEST_PERCOUNTERS_ELEMENT_READ by 1 nsec eveytime
+ ASSERT_EQ(dat.first, dat.second);
+ }
+}
+
+TEST(PerfCounters, read_avg) {
+ std::shared_ptr<PerfCounters> fake_pf = setup_test_perfcounter3(g_ceph_context);
+
+ std::thread t1(counters_inc_test, fake_pf);
+ std::thread t2(counters_readavg_test, fake_pf);
+ t2.join();
+ t1.join();
+}
+
+static PerfCounters* setup_test_perfcounter4(std::string name, CephContext *cct)
+{
+ PerfCountersBuilder bld(cct, name,
+ TEST_PERFCOUNTERS2_ELEMENT_FIRST, TEST_PERFCOUNTERS2_ELEMENT_LAST);
+ bld.add_u64(TEST_PERFCOUNTERS2_ELEMENT_FOO, "foo");
+ bld.add_time(TEST_PERFCOUNTERS2_ELEMENT_BAR, "bar");
+
+ PerfCounters* counters = bld.create_perf_counters();
+ cct->get_perfcounters_collection()->add(counters);
+ return counters;
+}
+
+TEST(PerfCounters, TestLabeledCountersOnly) {
+ constexpr std::string_view empty_dump_format_raw = R"({}
+)";
+ std::string counter_key1 = ceph::perf_counters::key_create("name1", {{"label1", "val1"}});
+ std::string counter_key2 = ceph::perf_counters::key_create("name2", {{"label2", "val2"}});
+ std::string counter_key3 = ceph::perf_counters::key_create("name1", {{"label1", "val3"}});
+
+ PerfCounters* counters1 = setup_test_perfcounter4(counter_key1, g_ceph_context);
+ PerfCounters* counters2 = setup_test_perfcounter4(counter_key2, g_ceph_context);
+ PerfCounters* counters3 = setup_test_perfcounter4(counter_key3, g_ceph_context);
+
+ counters1->inc(TEST_PERFCOUNTERS2_ELEMENT_FOO, 3);
+ counters1->dec(TEST_PERFCOUNTERS2_ELEMENT_FOO, 1);
+ counters2->set(TEST_PERFCOUNTERS2_ELEMENT_FOO, 4);
+ counters3->inc(TEST_PERFCOUNTERS2_ELEMENT_FOO, 3);
+
+ AdminSocketClient client(get_rand_socket_path());
+ std::string message;
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "counter dump", "format": "raw" })", &message));
+ ASSERT_EQ(R"({
+ "name1": [
+ {
+ "labels": {
+ "label1": "val1"
+ },
+ "counters": {
+ "foo": 2,
+ "bar": 0.000000000
+ }
+ },
+ {
+ "labels": {
+ "label1": "val3"
+ },
+ "counters": {
+ "foo": 3,
+ "bar": 0.000000000
+ }
+ }
+ ],
+ "name2": [
+ {
+ "labels": {
+ "label2": "val2"
+ },
+ "counters": {
+ "foo": 4,
+ "bar": 0.000000000
+ }
+ }
+ ]
+}
+)", message);
+
+ // make sure labeled counters are not in normal perf dump
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "perf dump", "format": "raw" })", &message));
+ ASSERT_EQ(empty_dump_format_raw, message);
+
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "counter schema", "format": "raw" })", &message));
+ ASSERT_EQ(R"({
+ "name1": [
+ {
+ "labels": {
+ "label1": "val1"
+ },
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ },
+ {
+ "labels": {
+ "label1": "val3"
+ },
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ }
+ ],
+ "name2": [
+ {
+ "labels": {
+ "label2": "val2"
+ },
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ }
+ ]
+}
+)", message);
+
+ // make sure labeled counters are not in normal perf schema
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "perf schema", "format": "raw" })", &message));
+ ASSERT_EQ(empty_dump_format_raw, message);
+
+ g_ceph_context->get_perfcounters_collection()->clear();
+}
+
+TEST(PerfCounters, TestLabelStrings) {
+ AdminSocketClient client(get_rand_socket_path());
+ std::string message;
+
+ // test empty val in a label pair will get the label pair added but empty key will not
+ std::string counter_key1 = ceph::perf_counters::key_create("good_ctrs", {{"label3", "val4"}, {"label1", ""}});
+ PerfCounters* counters1 = setup_test_perfcounter4(counter_key1, g_ceph_context);
+
+ std::string counter_key2 = ceph::perf_counters::key_create("bad_ctrs", {{"", "val4"}, {"label1", "val1"}});
+ PerfCounters* counters2 = setup_test_perfcounter4(counter_key2, g_ceph_context);
+
+ counters1->set(TEST_PERFCOUNTERS2_ELEMENT_FOO, 2);
+ counters2->set(TEST_PERFCOUNTERS2_ELEMENT_FOO, 4);
+
+ // test empty keys in each of the label pairs will get only the labels section added
+ std::string counter_key3 = ceph::perf_counters::key_create("bad_ctrs2", {{"", "val2"}, {"", "val33"}});
+ PerfCounters* counters3 = setup_test_perfcounter4(counter_key3, g_ceph_context);
+ counters3->set(TEST_PERFCOUNTERS2_ELEMENT_FOO, 6);
+
+ // a key with a somehow odd number of entries after the the key name will omit final unfinished label pair
+ std::string counter_key4 = "too_many_delimiters";
+ counter_key4 += '\0';
+ counter_key4 += "label1";
+ counter_key4 += '\0';
+ counter_key4 += "val1";
+ counter_key4 += '\0';
+ counter_key4 += "label2";
+ counter_key4 += '\0';
+ PerfCounters* counters4 = setup_test_perfcounter4(counter_key4, g_ceph_context);
+ counters4->set(TEST_PERFCOUNTERS2_ELEMENT_FOO, 8);
+
+ // test unlabeled perf counters are in the counter dump with labels and counters sections
+ std::string counter_key5 = "only_key";
+ PerfCounters* no_label_counters = setup_test_perfcounter4(counter_key5, g_ceph_context);
+ no_label_counters->set(TEST_PERFCOUNTERS2_ELEMENT_FOO, 4);
+
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "counter dump", "format": "raw" })", &message));
+ ASSERT_EQ(R"({
+ "bad_ctrs": [
+ {
+ "labels": {
+ "label1": "val1"
+ },
+ "counters": {
+ "foo": 4,
+ "bar": 0.000000000
+ }
+ }
+ ],
+ "bad_ctrs2": [
+ {
+ "labels": {},
+ "counters": {
+ "foo": 6,
+ "bar": 0.000000000
+ }
+ }
+ ],
+ "good_ctrs": [
+ {
+ "labels": {
+ "label1": "",
+ "label3": "val4"
+ },
+ "counters": {
+ "foo": 2,
+ "bar": 0.000000000
+ }
+ }
+ ],
+ "only_key": [
+ {
+ "labels": {},
+ "counters": {
+ "foo": 4,
+ "bar": 0.000000000
+ }
+ }
+ ],
+ "too_many_delimiters": [
+ {
+ "labels": {
+ "label1": "val1"
+ },
+ "counters": {
+ "foo": 8,
+ "bar": 0.000000000
+ }
+ }
+ ]
+}
+)", message);
+
+ // test unlabeled perf counters are in the schema dump with labels and counters sections
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "counter schema", "format": "raw" })", &message));
+ ASSERT_EQ(R"({
+ "bad_ctrs": [
+ {
+ "labels": {
+ "label1": "val1"
+ },
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ }
+ ],
+ "bad_ctrs2": [
+ {
+ "labels": {},
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ }
+ ],
+ "good_ctrs": [
+ {
+ "labels": {
+ "label1": "",
+ "label3": "val4"
+ },
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ }
+ ],
+ "only_key": [
+ {
+ "labels": {},
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ }
+ ],
+ "too_many_delimiters": [
+ {
+ "labels": {
+ "label1": "val1"
+ },
+ "counters": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+ }
+ ]
+}
+)", message);
+
+ // test unlabeled perf counters are in the perf dump without the labels and counters section
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "perf dump", "format": "raw" })", &message));
+ ASSERT_EQ(R"({
+ "only_key": {
+ "foo": 4,
+ "bar": 0.000000000
+ }
+}
+)", message);
+
+ // test unlabeled perf counters are in the perf schema without the labels and counters section
+ ASSERT_EQ("", client.do_request(R"({ "prefix": "perf schema", "format": "raw" })", &message));
+ ASSERT_EQ(R"({
+ "only_key": {
+ "foo": {
+ "type": 2,
+ "metric_type": "gauge",
+ "value_type": "integer",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ },
+ "bar": {
+ "type": 1,
+ "metric_type": "gauge",
+ "value_type": "real",
+ "description": "",
+ "nick": "",
+ "priority": 0,
+ "units": "none"
+ }
+ }
+}
+)", message);
+
+ g_ceph_context->get_perfcounters_collection()->clear();
+}
diff --git a/src/test/perf_helper.cc b/src/test/perf_helper.cc
new file mode 100644
index 000000000..00527ce9f
--- /dev/null
+++ b/src/test/perf_helper.cc
@@ -0,0 +1,52 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/* Copyright (c) 2011 Facebook
+ *
+ * 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(S) DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS 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/buffer.h"
+#include "common/error_code.h"
+
+using namespace ceph;
+
+namespace PerfHelper {
+
+/// Flush the CPU data cache by reading and writing 100MB of new data.
+void flush_cache()
+{
+ int hundredMegs = 100 * 1024 * 1024;
+ volatile char* block = new char[hundredMegs];
+ for (int i = 0; i < hundredMegs; i++)
+ block[i] = 1;
+ delete[] block;
+}
+
+/// Used in functionCall().
+uint64_t plus_one(uint64_t x)
+{
+ return x + 1;
+}
+
+/// Used in throwIntNL.
+void throw_int()
+{
+ throw 0;
+}
+
+/// Used in throwExceptionNL.
+void throw_end_of_buffer()
+{
+ throw buffer::end_of_buffer();
+}
+}
diff --git a/src/test/perf_helper.h b/src/test/perf_helper.h
new file mode 100644
index 000000000..2133b54d8
--- /dev/null
+++ b/src/test/perf_helper.h
@@ -0,0 +1,30 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/* Copyright (c) 2011 Facebook
+ *
+ * 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(S) DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS 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.
+ */
+
+#ifndef CEPH_TEST_PERFHELPER_H
+#define CEPH_TEST_PERFHELPER_H
+
+namespace PerfHelper {
+
+void flush_cache();
+uint64_t plus_one(uint64_t x);
+void throw_end_of_buffer();
+void throw_int();
+
+} // PerfHelper
+
+#endif // CEPH_TEST_PERFHELPER_H
diff --git a/src/test/perf_local.cc b/src/test/perf_local.cc
new file mode 100644
index 000000000..321f2d30c
--- /dev/null
+++ b/src/test/perf_local.cc
@@ -0,0 +1,1067 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/* Copyright (c) 2015 Haomai Wang <haomaiwang@gmail.com>
+ * Copyright (c) 2011-2014 Stanford University
+ * Copyright (c) 2011 Facebook
+ *
+ * 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(S) DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS 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.
+ */
+
+// This program contains a collection of low-level performance measurements
+// for Ceph, which can be run either individually or altogether. These
+// tests measure performance in a single stand-alone process, not in a cluster
+// with multiple servers. Invoke the program like this:
+//
+// Perf test1 test2 ...
+//
+// test1 and test2 are the names of individual performance measurements to
+// run. If no test names are provided then all of the performance tests
+// are run.
+//
+// To add a new test:
+// * Write a function that implements the test. Use existing test functions
+// as a guideline, and be sure to generate output in the same form as
+// other tests.
+// * Create a new entry for the test in the #tests table.
+#include <vector>
+#include <sched.h>
+
+#include "acconfig.h"
+#ifdef HAVE_SSE
+#include <xmmintrin.h>
+#endif
+
+#include "include/buffer.h"
+#include "include/encoding.h"
+#include "include/ceph_hash.h"
+#include "include/spinlock.h"
+#include "common/ceph_argparse.h"
+#include "common/Cycles.h"
+#include "common/Cond.h"
+#include "common/ceph_mutex.h"
+#include "common/Thread.h"
+#include "common/Timer.h"
+#include "msg/async/Event.h"
+#include "global/global_init.h"
+
+#include "test/perf_helper.h"
+
+#include <atomic>
+
+using namespace std;
+using namespace ceph;
+
+/**
+ * Ask the operating system to pin the current thread to a given CPU.
+ *
+ * \param cpu
+ * Indicates the desired CPU and hyperthread; low order 2 bits
+ * specify CPU, next bit specifies hyperthread.
+ */
+void bind_thread_to_cpu(int cpu)
+{
+#ifdef HAVE_SCHED
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ CPU_SET(cpu, &set);
+ sched_setaffinity(0, sizeof(set), &set);
+#endif
+}
+
+/*
+ * This function just discards its argument. It's used to make it
+ * appear that data is used, so that the compiler won't optimize
+ * away the code we're trying to measure.
+ *
+ * \param value
+ * Pointer to arbitrary value; it's discarded.
+ */
+void discard(void* value) {
+ int x = *reinterpret_cast<int*>(value);
+ if (x == 0x43924776) {
+ printf("Value was 0x%x\n", x);
+ }
+}
+
+//----------------------------------------------------------------------
+// Test functions start here
+//----------------------------------------------------------------------
+
+// Measure the cost of atomic compare-and-swap
+double atomic_int_cmp()
+{
+ int count = 1000000;
+ std::atomic<unsigned> value = { 11 };
+ unsigned int test = 11;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ value.compare_exchange_strong(test, test+2);
+ test += 2;
+ }
+ uint64_t stop = Cycles::rdtsc();
+ // printf("Final value: %d\n", value.load());
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of incrementing an atomic
+double atomic_int_inc()
+{
+ int count = 1000000;
+ std::atomic<int64_t> value = { 11 };
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ value++;
+ }
+ uint64_t stop = Cycles::rdtsc();
+ // printf("Final value: %d\n", value.load());
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of reading an atomic
+double atomic_int_read()
+{
+ int count = 1000000;
+ std::atomic<int64_t> value = { 11 };
+ [[maybe_unused]] int total = 0;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ total += value;
+ }
+ uint64_t stop = Cycles::rdtsc();
+ // printf("Total: %d\n", total);
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of storing a new value in an atomic
+double atomic_int_set()
+{
+ int count = 1000000;
+ std::atomic<int64_t> value = { 11 };
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ value = 88;
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of acquiring and releasing a mutex in the
+// fast case where the mutex is free.
+double mutex_nonblock()
+{
+ int count = 1000000;
+ ceph::mutex m = ceph::make_mutex("mutex_nonblock::m");
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ m.lock();
+ m.unlock();
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of allocating and deallocating a buffer, plus
+// appending (logically) one ptr.
+double buffer_basic()
+{
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ bufferptr ptr("abcdefg", 7);
+ for (int i = 0; i < count; i++) {
+ bufferlist b;
+ b.append(ptr, 0, 5);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+struct DummyBlock {
+ int a = 1, b = 2, c = 3, d = 4;
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(a, bl);
+ encode(b, bl);
+ encode(c, bl);
+ encode(d, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::const_iterator &bl) {
+ DECODE_START(1, bl);
+ decode(a, bl);
+ decode(b, bl);
+ decode(c, bl);
+ decode(d, bl);
+ DECODE_FINISH(bl);
+ }
+};
+WRITE_CLASS_ENCODER(DummyBlock)
+
+// Measure the cost of encoding and decoding a buffer, plus
+// allocating space for one chunk.
+double buffer_encode_decode()
+{
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ bufferlist b;
+ DummyBlock dummy_block;
+ encode(dummy_block, b);
+ auto iter = b.cbegin();
+ decode(dummy_block, iter);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of allocating and deallocating a buffer, plus
+// copying in a small block.
+double buffer_basic_copy()
+{
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ bufferlist b;
+ b.append("abcdefg", 6);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of making a copy of parts of two ptrs.
+double buffer_copy()
+{
+ int count = 1000000;
+ bufferlist b;
+ b.append("abcde", 5);
+ b.append("01234", 5);
+ char copy[10];
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ b.cbegin(2).copy(6, copy);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of allocating new space by extending the
+// bufferlist
+double buffer_encode()
+{
+ int count = 100000;
+ uint64_t total = 0;
+ for (int i = 0; i < count; i++) {
+ bufferlist b;
+ DummyBlock dummy_block;
+ encode(dummy_block, b);
+ uint64_t start = Cycles::rdtsc();
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ encode(dummy_block, b);
+ total += Cycles::rdtsc() - start;
+ }
+ return Cycles::to_seconds(total)/(count*10);
+}
+
+// Measure the cost of creating an iterator and iterating over 10
+// chunks in a buffer.
+double buffer_iterator()
+{
+ bufferlist b;
+ const char s[] = "abcdefghijklmnopqrstuvwxyz";
+ bufferptr ptr(s, sizeof(s));
+ for (int i = 0; i < 5; i++) {
+ b.append(ptr, i, 5);
+ }
+ int count = 100000;
+ int sum = 0;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ auto it = b.cbegin();
+ while (!it.end()) {
+ sum += (static_cast<const char*>(it.get_current_ptr().c_str()))[it.get_remaining()-1];
+ ++it;
+ }
+ }
+ uint64_t stop = Cycles::rdtsc();
+ discard(&sum);
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Implements the CondPingPong test.
+class CondPingPong {
+ ceph::mutex mutex = ceph::make_mutex("CondPingPong::mutex");
+ ceph::condition_variable cond;
+ int prod = 0;
+ int cons = 0;
+ const int count = 10000;
+
+ class Consumer : public Thread {
+ CondPingPong *p;
+ public:
+ explicit Consumer(CondPingPong *p): p(p) {}
+ void* entry() override {
+ p->consume();
+ return 0;
+ }
+ } consumer;
+
+ public:
+ CondPingPong(): consumer(this) {}
+
+ double run() {
+ consumer.create("consumer");
+ uint64_t start = Cycles::rdtsc();
+ produce();
+ uint64_t stop = Cycles::rdtsc();
+ consumer.join();
+ return Cycles::to_seconds(stop - start)/count;
+ }
+
+ void produce() {
+ std::unique_lock l{mutex};
+ while (cons < count) {
+ cond.wait(l, [this] { return cons >= prod; });
+ ++prod;
+ cond.notify_all();
+ }
+ }
+
+ void consume() {
+ std::unique_lock l{mutex};
+ while (cons < count) {
+ cond.wait(l, [this] { return cons != prod; });
+ ++cons;
+ cond.notify_all();
+ }
+ }
+};
+
+// Measure the cost of coordinating between threads using a condition variable.
+double cond_ping_pong()
+{
+ return CondPingPong().run();
+}
+
+// Measure the cost of a 32-bit divide. Divides don't take a constant
+// number of cycles. Values were chosen here semi-randomly to depict a
+// fairly expensive scenario. Someone with fancy ALU knowledge could
+// probably pick worse values.
+double div32()
+{
+#if defined(__i386__) || defined(__x86_64__)
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ // NB: Expect an x86 processor exception is there's overflow.
+ uint32_t numeratorHi = 0xa5a5a5a5U;
+ uint32_t numeratorLo = 0x55aa55aaU;
+ uint32_t divisor = 0xaa55aa55U;
+ uint32_t quotient;
+ uint32_t remainder;
+ for (int i = 0; i < count; i++) {
+ __asm__ __volatile__("div %4" :
+ "=a"(quotient), "=d"(remainder) :
+ "a"(numeratorLo), "d"(numeratorHi), "r"(divisor) :
+ "cc");
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#elif defined(__aarch64__)
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ uint64_t numerator = 0xa5a5a5a555aa55aaUL;
+ uint32_t divisor = 0xaa55aa55U;
+ uint32_t result;
+ for (int i = 0; i < count; i++) {
+ asm volatile("udiv %0, %1, %2" : "=r"(result) :
+ "r"(numerator), "r"(divisor));
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#else
+ return -1;
+#endif
+}
+
+// Measure the cost of a 64-bit divide. Divides don't take a constant
+// number of cycles. Values were chosen here semi-randomly to depict a
+// fairly expensive scenario. Someone with fancy ALU knowledge could
+// probably pick worse values.
+double div64()
+{
+#if defined(__x86_64__) || defined(__amd64__)
+ int count = 1000000;
+ // NB: Expect an x86 processor exception is there's overflow.
+ uint64_t start = Cycles::rdtsc();
+ uint64_t numeratorHi = 0x5a5a5a5a5a5UL;
+ uint64_t numeratorLo = 0x55aa55aa55aa55aaUL;
+ uint64_t divisor = 0xaa55aa55aa55aa55UL;
+ uint64_t quotient;
+ uint64_t remainder;
+ for (int i = 0; i < count; i++) {
+ __asm__ __volatile__("divq %4" :
+ "=a"(quotient), "=d"(remainder) :
+ "a"(numeratorLo), "d"(numeratorHi), "r"(divisor) :
+ "cc");
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#else
+ return -1;
+#endif
+}
+
+// Measure the cost of calling a non-inlined function.
+double function_call()
+{
+ int count = 1000000;
+ uint64_t x = 0;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ x = PerfHelper::plus_one(x);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the minimum cost of EventCenter::process_events, when there are no
+// Pollers and no Timers.
+double eventcenter_poll()
+{
+ int count = 1000000;
+ EventCenter center(g_ceph_context);
+ center.init(1000, 0, "posix");
+ center.set_owner();
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ center.process_events(0);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+class CenterWorker : public Thread {
+ CephContext *cct;
+ bool done;
+
+ public:
+ EventCenter center;
+ explicit CenterWorker(CephContext *c): cct(c), done(false), center(c) {
+ center.init(100, 0, "posix");
+ }
+ void stop() {
+ done = true;
+ center.wakeup();
+ }
+ void* entry() override {
+ center.set_owner();
+ bind_thread_to_cpu(2);
+ while (!done)
+ center.process_events(1000);
+ return 0;
+ }
+};
+
+class CountEvent: public EventCallback {
+ std::atomic<int64_t> *count;
+
+ public:
+ explicit CountEvent(std::atomic<int64_t> *atomic): count(atomic) {}
+ void do_request(uint64_t id) override {
+ (*count)--;
+ }
+};
+
+double eventcenter_dispatch()
+{
+ int count = 100000;
+
+ CenterWorker worker(g_ceph_context);
+ std::atomic<int64_t> flag = { 1 };
+ worker.create("evt_center_disp");
+ EventCallbackRef count_event(new CountEvent(&flag));
+
+ worker.center.dispatch_event_external(count_event);
+ // Start a new thread and wait for it to ready.
+ while (flag)
+ usleep(100);
+
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ flag = 1;
+ worker.center.dispatch_event_external(count_event);
+ while (flag)
+ ;
+ }
+ uint64_t stop = Cycles::rdtsc();
+ worker.stop();
+ worker.join();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of copying a given number of bytes with memcpy.
+double memcpy_shared(size_t size)
+{
+ int count = 1000000;
+ char src[size], dst[size];
+
+ memset(src, 0, sizeof(src));
+
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ memcpy(dst, src, size);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+double memcpy100()
+{
+ return memcpy_shared(100);
+}
+
+double memcpy1000()
+{
+ return memcpy_shared(1000);
+}
+
+double memcpy10000()
+{
+ return memcpy_shared(10000);
+}
+
+// Benchmark rjenkins hashing performance on cached data.
+template <int key_length>
+double ceph_str_hash_rjenkins()
+{
+ int count = 100000;
+ char buf[key_length];
+
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++)
+ ceph_str_hash(CEPH_STR_HASH_RJENKINS, buf, sizeof(buf));
+ uint64_t stop = Cycles::rdtsc();
+
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of reading the fine-grain cycle counter.
+double rdtsc_test()
+{
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ [[maybe_unused]] uint64_t total = 0;
+ for (int i = 0; i < count; i++) {
+ total += Cycles::rdtsc();
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of the Cycles::to_seconds method.
+double perf_cycles_to_seconds()
+{
+ int count = 1000000;
+ [[maybe_unused]] double total = 0;
+ uint64_t cycles = 994261;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ total += Cycles::to_seconds(cycles);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ // printf("Result: %.4f\n", total/count);
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of the Cylcles::toNanoseconds method.
+double perf_cycles_to_nanoseconds()
+{
+ int count = 1000000;
+ [[maybe_unused]] uint64_t total = 0;
+ uint64_t cycles = 994261;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ total += Cycles::to_nanoseconds(cycles);
+ }
+ uint64_t stop = Cycles::rdtsc();
+ // printf("Result: %lu\n", total/count);
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+
+#ifdef HAVE_SSE
+/**
+ * Prefetch the cache lines containing [object, object + numBytes) into the
+ * processor's caches.
+ * The best docs for this are in the Intel instruction set reference under
+ * PREFETCH.
+ * \param object
+ * The start of the region of memory to prefetch.
+ * \param num_bytes
+ * The size of the region of memory to prefetch.
+ */
+static inline void prefetch(const void *object, uint64_t num_bytes)
+{
+ uint64_t offset = reinterpret_cast<uint64_t>(object) & 0x3fUL;
+ const char* p = reinterpret_cast<const char*>(object) - offset;
+ for (uint64_t i = 0; i < offset + num_bytes; i += 64)
+ _mm_prefetch(p + i, _MM_HINT_T0);
+}
+#elif defined(__aarch64__)
+static inline void prefetch(const void *object, uint64_t num_bytes)
+{
+ uint64_t offset = reinterpret_cast<uint64_t>(object) & 0x3fUL;
+ const char* ptr = reinterpret_cast<const char*>(object) - offset;
+ for (uint64_t i = 0; i < offset + num_bytes; i += 64, ptr += 64)
+ asm volatile("prfm pldl1keep, %a0\n" : : "p" (ptr));
+}
+#endif
+
+// Measure the cost of the prefetch instruction.
+double perf_prefetch()
+{
+#if defined(HAVE_SSE) || defined(__aarch64__)
+ uint64_t total_ticks = 0;
+ int count = 10;
+ char buf[16 * 64];
+
+ for (int i = 0; i < count; i++) {
+ PerfHelper::flush_cache();
+ uint64_t start = Cycles::rdtsc();
+ prefetch(&buf[576], 64);
+ prefetch(&buf[0], 64);
+ prefetch(&buf[512], 64);
+ prefetch(&buf[960], 64);
+ prefetch(&buf[640], 64);
+ prefetch(&buf[896], 64);
+ prefetch(&buf[256], 64);
+ prefetch(&buf[704], 64);
+ prefetch(&buf[320], 64);
+ prefetch(&buf[384], 64);
+ prefetch(&buf[128], 64);
+ prefetch(&buf[448], 64);
+ prefetch(&buf[768], 64);
+ prefetch(&buf[832], 64);
+ prefetch(&buf[64], 64);
+ prefetch(&buf[192], 64);
+ uint64_t stop = Cycles::rdtsc();
+ total_ticks += stop - start;
+ }
+ return Cycles::to_seconds(total_ticks) / count / 16;
+#else
+ return -1;
+#endif
+}
+
+#if defined(__x86_64__)
+/**
+ * This function is used to seralize machine instructions so that no
+ * instructions that appear after it in the current thread can run before any
+ * instructions that appear before it.
+ *
+ * It is useful for putting around rdpmc instructions (to pinpoint cache
+ * misses) as well as before rdtsc instructions, to prevent time pollution from
+ * instructions supposed to be executing before the timer starts.
+ */
+static inline void serialize() {
+ uint32_t eax, ebx, ecx, edx;
+ __asm volatile("cpuid"
+ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
+ : "a" (1U));
+}
+#endif
+
+// Measure the cost of cpuid
+double perf_serialize() {
+#if defined(__x86_64__)
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ serialize();
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#else
+ return -1;
+#endif
+}
+
+// Measure the cost of an lfence instruction.
+double lfence()
+{
+#ifdef HAVE_SSE2
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ __asm__ __volatile__("lfence" ::: "memory");
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#elif defined(__aarch64__)
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ asm volatile("dmb ishld" ::: "memory");
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#else
+ return -1;
+#endif
+}
+
+// Measure the cost of an sfence instruction.
+double sfence()
+{
+#ifdef HAVE_SSE
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ __asm__ __volatile__("sfence" ::: "memory");
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#elif defined(__aarch64__)
+ int count = 1000000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ asm volatile("dmb ishst" ::: "memory");
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+#else
+ return -1;
+#endif
+}
+
+// Measure the cost of acquiring and releasing a SpinLock (assuming the
+// lock is initially free).
+double test_spinlock()
+{
+ int count = 1000000;
+ ceph::spinlock lock;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ lock.lock();
+ lock.unlock();
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Helper for spawn_thread. This is the main function that the thread executes
+// (intentionally empty).
+class ThreadHelper : public Thread {
+ void *entry() override { return 0; }
+};
+
+// Measure the cost of start and joining with a thread.
+double spawn_thread()
+{
+ int count = 10000;
+ ThreadHelper thread;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ thread.create("thread_helper");
+ thread.join();
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+class FakeContext : public Context {
+ public:
+ void finish(int r) override {}
+};
+
+// Measure the cost of starting and stopping a Dispatch::Timer.
+double perf_timer()
+{
+ int count = 1000000;
+ ceph::mutex lock = ceph::make_mutex("perf_timer::lock");
+ SafeTimer timer(g_ceph_context, lock);
+ FakeContext **c = new FakeContext*[count];
+ for (int i = 0; i < count; i++) {
+ c[i] = new FakeContext();
+ }
+ uint64_t start = Cycles::rdtsc();
+ std::lock_guard l{lock};
+ for (int i = 0; i < count; i++) {
+ if (timer.add_event_after(12345, c[i])) {
+ timer.cancel_event(c[i]);
+ }
+ }
+ uint64_t stop = Cycles::rdtsc();
+ delete[] c;
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of throwing and catching an int. This uses an integer as
+// the value thrown, which is presumably as fast as possible.
+double throw_int()
+{
+ int count = 10000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ try {
+ throw 0;
+ } catch (int) { // NOLINT
+ // pass
+ }
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of throwing and catching an int from a function call.
+double throw_int_call()
+{
+ int count = 10000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ try {
+ PerfHelper::throw_int();
+ } catch (int) { // NOLINT
+ // pass
+ }
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of throwing and catching an Exception. This uses an actual
+// exception as the value thrown, which may be slower than throwInt.
+double throw_exception()
+{
+ int count = 10000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ try {
+ throw buffer::end_of_buffer();
+ } catch (const buffer::end_of_buffer&) {
+ // pass
+ }
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of throwing and catching an Exception from a function call.
+double throw_exception_call()
+{
+ int count = 10000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ try {
+ PerfHelper::throw_end_of_buffer();
+ } catch (const buffer::end_of_buffer&) {
+ // pass
+ }
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// Measure the cost of pushing a new element on a std::vector, copying
+// from the end to an internal element, and popping the end element.
+double vector_push_pop()
+{
+ int count = 100000;
+ std::vector<int> vector;
+ vector.push_back(1);
+ vector.push_back(2);
+ vector.push_back(3);
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ vector.push_back(i);
+ vector.push_back(i+1);
+ vector.push_back(i+2);
+ vector[2] = vector.back();
+ vector.pop_back();
+ vector[0] = vector.back();
+ vector.pop_back();
+ vector[1] = vector.back();
+ vector.pop_back();
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/(count*3);
+}
+
+// Measure the cost of ceph_clock_now
+double perf_ceph_clock_now()
+{
+ int count = 100000;
+ uint64_t start = Cycles::rdtsc();
+ for (int i = 0; i < count; i++) {
+ ceph_clock_now();
+ }
+ uint64_t stop = Cycles::rdtsc();
+ return Cycles::to_seconds(stop - start)/count;
+}
+
+// The following struct and table define each performance test in terms of
+// a string name and a function that implements the test.
+struct TestInfo {
+ const char* name; // Name of the performance test; this is
+ // what gets typed on the command line to
+ // run the test.
+ double (*func)(); // Function that implements the test;
+ // returns the time (in seconds) for each
+ // iteration of that test.
+ const char *description; // Short description of this test (not more
+ // than about 40 characters, so the entire
+ // test output fits on a single line).
+};
+TestInfo tests[] = {
+ {"atomic_int_cmp", atomic_int_cmp,
+ "atomic_t::compare_and_swap"},
+ {"atomic_int_inc", atomic_int_inc,
+ "atomic_t::inc"},
+ {"atomic_int_read", atomic_int_read,
+ "atomic_t::read"},
+ {"atomic_int_set", atomic_int_set,
+ "atomic_t::set"},
+ {"mutex_nonblock", mutex_nonblock,
+ "Mutex lock/unlock (no blocking)"},
+ {"buffer_basic", buffer_basic,
+ "buffer create, add one ptr, delete"},
+ {"buffer_encode_decode", buffer_encode_decode,
+ "buffer create, encode/decode object, delete"},
+ {"buffer_basic_copy", buffer_basic_copy,
+ "buffer create, copy small block, delete"},
+ {"buffer_copy", buffer_copy,
+ "copy out 2 small ptrs from buffer"},
+ {"buffer_encode10", buffer_encode,
+ "buffer encoding 10 structures onto existing ptr"},
+ {"buffer_iterator", buffer_iterator,
+ "iterate over buffer with 5 ptrs"},
+ {"cond_ping_pong", cond_ping_pong,
+ "condition variable round-trip"},
+ {"div32", div32,
+ "32-bit integer division instruction"},
+ {"div64", div64,
+ "64-bit integer division instruction"},
+ {"function_call", function_call,
+ "Call a function that has not been inlined"},
+ {"eventcenter_poll", eventcenter_poll,
+ "EventCenter::process_events (no timers or events)"},
+ {"eventcenter_dispatch", eventcenter_dispatch,
+ "EventCenter::dispatch_event_external latency"},
+ {"memcpy100", memcpy100,
+ "Copy 100 bytes with memcpy"},
+ {"memcpy1000", memcpy1000,
+ "Copy 1000 bytes with memcpy"},
+ {"memcpy10000", memcpy10000,
+ "Copy 10000 bytes with memcpy"},
+ {"ceph_str_hash_rjenkins", ceph_str_hash_rjenkins<16>,
+ "rjenkins hash on 16 byte of data"},
+ {"ceph_str_hash_rjenkins", ceph_str_hash_rjenkins<256>,
+ "rjenkins hash on 256 bytes of data"},
+ {"rdtsc", rdtsc_test,
+ "Read the fine-grain cycle counter"},
+ {"cycles_to_seconds", perf_cycles_to_seconds,
+ "Convert a rdtsc result to (double) seconds"},
+ {"cycles_to_seconds", perf_cycles_to_nanoseconds,
+ "Convert a rdtsc result to (uint64_t) nanoseconds"},
+ {"prefetch", perf_prefetch,
+ "Prefetch instruction"},
+ {"serialize", perf_serialize,
+ "serialize instruction"},
+ {"lfence", lfence,
+ "Lfence instruction"},
+ {"sfence", sfence,
+ "Sfence instruction"},
+ {"spin_lock", test_spinlock,
+ "Acquire/release SpinLock"},
+ {"spawn_thread", spawn_thread,
+ "Start and stop a thread"},
+ {"perf_timer", perf_timer,
+ "Insert and cancel a SafeTimer"},
+ {"throw_int", throw_int,
+ "Throw an int"},
+ {"throw_int_call", throw_int_call,
+ "Throw an int in a function call"},
+ {"throw_exception", throw_exception,
+ "Throw an Exception"},
+ {"throw_exception_call", throw_exception_call,
+ "Throw an Exception in a function call"},
+ {"vector_push_pop", vector_push_pop,
+ "Push and pop a std::vector"},
+ {"ceph_clock_now", perf_ceph_clock_now,
+ "ceph_clock_now function"},
+};
+
+/**
+ * Runs a particular test and prints a one-line result message.
+ *
+ * \param info
+ * Describes the test to run.
+ */
+void run_test(TestInfo& info)
+{
+ double secs = info.func();
+ int width = printf("%-24s ", info.name);
+ if (secs == -1) {
+ width += printf(" architecture nonsupport ");
+ } else if (secs < 1.0e-06) {
+ width += printf("%8.2fns", 1e09*secs);
+ } else if (secs < 1.0e-03) {
+ width += printf("%8.2fus", 1e06*secs);
+ } else if (secs < 1.0) {
+ width += printf("%8.2fms", 1e03*secs);
+ } else {
+ width += printf("%8.2fs", secs);
+ }
+ printf("%*s %s\n", 32-width, "", info.description);
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ Cycles::init();
+
+ bind_thread_to_cpu(3);
+ if (argc == 1) {
+ // No test names specified; run all tests.
+ for (size_t i = 0; i < sizeof(tests)/sizeof(TestInfo); ++i) {
+ run_test(tests[i]);
+ }
+ } else {
+ // Run only the tests that were specified on the command line.
+ for (int i = 1; i < argc; i++) {
+ bool found_test = false;
+ for (size_t j = 0; j < sizeof(tests)/sizeof(TestInfo); ++j) {
+ if (strcmp(argv[i], tests[j].name) == 0) {
+ found_test = true;
+ run_test(tests[j]);
+ break;
+ }
+ }
+ if (!found_test) {
+ int width = printf("%-24s ??", argv[i]);
+ printf("%*s No such test\n", 32-width, "");
+ }
+ }
+ }
+}
diff --git a/src/test/pybind/CMakeLists.txt b/src/test/pybind/CMakeLists.txt
new file mode 100644
index 000000000..b2f0552a4
--- /dev/null
+++ b/src/test/pybind/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_ceph_test(test_ceph_daemon.py
+ ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_ceph_daemon.py)
+add_ceph_test(test_ceph_argparse.py
+ ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_ceph_argparse.py)
diff --git a/src/test/pybind/assertions.py b/src/test/pybind/assertions.py
new file mode 100644
index 000000000..719700f3a
--- /dev/null
+++ b/src/test/pybind/assertions.py
@@ -0,0 +1,26 @@
+def assert_equal(a, b):
+ assert a == b
+
+def assert_not_equal(a, b):
+ assert a != b
+
+def assert_greater(a, b):
+ assert a > b
+
+def assert_greater_equal(a, b):
+ assert a >= b
+
+def assert_raises(excClass, callableObj, *args, **kwargs):
+ """
+ Like unittest.TestCase.assertRaises, but returns the exception.
+ """
+ try:
+ callableObj(*args, **kwargs)
+ except excClass as e:
+ return e
+ else:
+ if hasattr(excClass, '__name__'):
+ excName = excClass.__name__
+ else:
+ excName = str(excClass)
+ raise AssertionError("%s not raised" % excName)
diff --git a/src/test/pybind/pytest.ini b/src/test/pybind/pytest.ini
new file mode 100644
index 000000000..dccf2a346
--- /dev/null
+++ b/src/test/pybind/pytest.ini
@@ -0,0 +1,9 @@
+[pytest]
+markers =
+ bench
+ ec
+ rollback
+ skip_if_crimson
+ stats
+ tier
+ watch
diff --git a/src/test/pybind/test_ceph_argparse.py b/src/test/pybind/test_ceph_argparse.py
new file mode 100755
index 000000000..6fe56bf98
--- /dev/null
+++ b/src/test/pybind/test_ceph_argparse.py
@@ -0,0 +1,1334 @@
+#!/usr/bin/env python3
+# -*- mode:python; tab-width:4; indent-tabs-mode:nil; coding:utf-8 -*-
+# vim: ts=4 sw=4 smarttab expandtab fileencoding=utf-8
+#
+# Ceph - scalable distributed file system
+#
+# Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
+# Copyright (C) 2014 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+
+from ceph_argparse import validate_command, parse_json_funcsigs, validate, \
+ parse_funcsig, ArgumentError, ArgumentTooFew, ArgumentMissing, \
+ ArgumentNumber, ArgumentValid
+
+import os
+import random
+import re
+import string
+import sys
+import unittest
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+
+def get_command_descriptions(what):
+ CEPH_BIN = os.environ.get('CEPH_BIN', ".")
+ return os.popen(CEPH_BIN + "/get_command_descriptions " + "--" + what).read()
+
+
+class ParseJsonFuncsigs(unittest.TestCase):
+ def test_parse_json_funcsigs(self):
+ commands = get_command_descriptions("all")
+ cmd_json = parse_json_funcsigs(commands, 'cli')
+
+ # syntax error https://github.com/ceph/ceph/pull/585
+ commands = get_command_descriptions("pull585")
+ self.assertRaises(TypeError, parse_json_funcsigs, commands, 'cli')
+
+
+sigdict = parse_json_funcsigs(get_command_descriptions("all"), 'cli')
+
+
+class TestArgparse(unittest.TestCase):
+
+ def _assert_valid_command(self, args):
+ result = validate_command(sigdict, args)
+ self.assertNotIn(result, [{}, None])
+
+ def check_1_natural_arg(self, prefix, command):
+ self._assert_valid_command([prefix, command, '1'])
+ self.assertEqual({}, validate_command(sigdict, [prefix, command]))
+ self.assertEqual({}, validate_command(sigdict, [prefix, command, '-1']))
+ self.assertEqual({}, validate_command(sigdict, [prefix, command, '1',
+ '1']))
+
+ def check_0_or_1_natural_arg(self, prefix, command):
+ self._assert_valid_command([prefix, command, '1'])
+ self._assert_valid_command([prefix, command])
+ self.assertEqual({}, validate_command(sigdict, [prefix, command, '-1']))
+ self.assertEqual({}, validate_command(sigdict, [prefix, command, '1',
+ '1']))
+
+ def check_1_string_arg(self, prefix, command):
+ self.assertEqual({}, validate_command(sigdict, [prefix, command]))
+ self._assert_valid_command([prefix, command, 'string'])
+ self.assertEqual({}, validate_command(sigdict, [prefix,
+ command,
+ 'string',
+ 'toomany']))
+
+ def check_0_or_1_string_arg(self, prefix, command):
+ self._assert_valid_command([prefix, command, 'string'])
+ self._assert_valid_command([prefix, command])
+ self.assertEqual({}, validate_command(sigdict, [prefix,
+ command,
+ 'string',
+ 'toomany']))
+
+ def check_1_or_more_string_args(self, prefix, command):
+ self.assertEqual({}, validate_command(sigdict, [prefix,
+ command]))
+ self._assert_valid_command([prefix, command, 'string'])
+ self._assert_valid_command([prefix, command, 'string', 'more string'])
+
+ def check_no_arg(self, prefix, command):
+ self._assert_valid_command([prefix, command])
+ self.assertEqual({}, validate_command(sigdict, [prefix,
+ command,
+ 'toomany']))
+
+ def _capture_output(self, args, stdout=None, stderr=None):
+ if stdout:
+ stdout = StringIO()
+ sys.stdout = stdout
+ if stderr:
+ stderr = StringIO()
+ sys.stderr = stderr
+ ret = validate_command(sigdict, args)
+ if stdout:
+ stdout = stdout.getvalue().strip()
+ if stderr:
+ stderr = stderr.getvalue().strip()
+ return ret, stdout, stderr
+
+
+class TestBasic(unittest.TestCase):
+
+ def test_non_ascii_in_non_options(self):
+ # ArgumentPrefix("no match for {0}".format(s)) is not able to convert
+ # unicode str parameter into str. and validate_command() should not
+ # choke on it.
+ self.assertEqual({}, validate_command(sigdict, [u'章鱼和鱿鱼']))
+ self.assertEqual({}, validate_command(sigdict, [u'–w']))
+ # actually we always pass unicode strings to validate_command() in "ceph"
+ # CLI, but we also use bytestrings in our tests, so make sure it does not
+ # break.
+ self.assertEqual({}, validate_command(sigdict, ['章鱼和鱿鱼']))
+ self.assertEqual({}, validate_command(sigdict, ['–w']))
+
+
+class TestPG(TestArgparse):
+
+ def test_stat(self):
+ self._assert_valid_command(['pg', 'stat'])
+
+ def test_getmap(self):
+ self._assert_valid_command(['pg', 'getmap'])
+
+ def test_dump(self):
+ valid_commands = {
+ 'pg dump': {'prefix': 'pg dump'},
+ 'pg dump all summary sum delta pools osds pgs pgs_brief':
+ {'prefix': 'pg dump',
+ 'dumpcontents':
+ 'all summary sum delta pools osds pgs pgs_brief'.split()
+ },
+ 'pg dump --dumpcontents summary,sum':
+ {'prefix': 'pg dump',
+ 'dumpcontents': 'summary,sum'.split(',')
+ }
+ }
+ for command, expected_result in valid_commands.items():
+ actual_result = validate_command(sigdict, command.split())
+ expected_result['target'] = ('mon-mgr', '')
+ self.assertEqual(expected_result, actual_result)
+ invalid_commands = ['pg dump invalid']
+ for command in invalid_commands:
+ actual_result = validate_command(sigdict, command.split())
+ self.assertEqual({}, actual_result)
+
+ def test_dump_json(self):
+ self._assert_valid_command(['pg', 'dump_json'])
+ self._assert_valid_command(['pg', 'dump_json',
+ 'all',
+ 'summary',
+ 'sum',
+ 'pools',
+ 'osds',
+ 'pgs'])
+ self.assertEqual({}, validate_command(sigdict, ['pg', 'dump_json',
+ 'invalid']))
+
+ def test_dump_pools_json(self):
+ self._assert_valid_command(['pg', 'dump_pools_json'])
+
+ def test_dump_pools_stuck(self):
+ self._assert_valid_command(['pg', 'dump_stuck'])
+ self._assert_valid_command(['pg', 'dump_stuck',
+ 'inactive',
+ 'unclean',
+ 'stale'])
+ self.assertEqual({}, validate_command(sigdict, ['pg', 'dump_stuck',
+ 'invalid']))
+ self._assert_valid_command(['pg', 'dump_stuck',
+ 'inactive',
+ '1234'])
+
+ def one_pgid(self, command):
+ self._assert_valid_command(['pg', command, '1.1'])
+ self.assertEqual({}, validate_command(sigdict, ['pg', command]))
+ self.assertEqual({}, validate_command(sigdict, ['pg', command, '1']))
+
+ def test_map(self):
+ self.one_pgid('map')
+
+ def test_scrub(self):
+ self.one_pgid('scrub')
+
+ def test_deep_scrub(self):
+ self.one_pgid('deep-scrub')
+
+ def test_repair(self):
+ self.one_pgid('repair')
+
+ def test_debug(self):
+ self._assert_valid_command(['pg',
+ 'debug',
+ 'unfound_objects_exist'])
+ self._assert_valid_command(['pg',
+ 'debug',
+ 'degraded_pgs_exist'])
+ self.assertEqual({}, validate_command(sigdict, ['pg', 'debug']))
+ self.assertEqual({}, validate_command(sigdict, ['pg', 'debug',
+ 'invalid']))
+
+ def test_pg_missing_args_output(self):
+ ret, _, stderr = self._capture_output(['pg'], stderr=True)
+ self.assertEqual({}, ret)
+ self.assertRegexpMatches(stderr, re.compile('no valid command found.* closest matches'))
+
+ def test_pg_wrong_arg_output(self):
+ ret, _, stderr = self._capture_output(['pg', 'map', 'bad-pgid'],
+ stderr=True)
+ self.assertEqual({}, ret)
+ self.assertIn("Invalid command", stderr)
+
+
+class TestAuth(TestArgparse):
+
+ def test_export(self):
+ self._assert_valid_command(['auth', 'export'])
+ self._assert_valid_command(['auth', 'export', 'string'])
+ self.assertEqual({}, validate_command(sigdict, ['auth',
+ 'export',
+ 'string',
+ 'toomany']))
+
+ def test_get(self):
+ self.check_1_string_arg('auth', 'get')
+
+ def test_get_key(self):
+ self.check_1_string_arg('auth', 'get-key')
+
+ def test_print_key(self):
+ self.check_1_string_arg('auth', 'print-key')
+ self.check_1_string_arg('auth', 'print_key')
+
+ def test_list(self):
+ self.check_no_arg('auth', 'list')
+
+ def test_import(self):
+ self.check_no_arg('auth', 'import')
+
+ def test_add(self):
+ self.check_1_or_more_string_args('auth', 'add')
+
+ def test_get_or_create_key(self):
+ self.check_1_or_more_string_args('auth', 'get-or-create-key')
+ prefix = 'auth get-or-create-key'
+ entity = 'client.test'
+ caps = ['mon',
+ 'allow r',
+ 'osd',
+ 'allow rw pool=nfs-ganesha namespace=test, allow rw tag cephfs data=user_test_fs',
+ 'mds',
+ 'allow rw path=/']
+ cmd = prefix.split() + [entity] + caps
+ self.assertEqual(
+ {
+ 'prefix': prefix,
+ 'entity': entity,
+ 'caps': caps
+ }, validate_command(sigdict, cmd))
+
+ def test_get_or_create(self):
+ self.check_1_or_more_string_args('auth', 'get-or-create')
+
+ def test_caps(self):
+ self.assertEqual({}, validate_command(sigdict, ['auth',
+ 'caps']))
+ self.assertEqual({}, validate_command(sigdict, ['auth',
+ 'caps',
+ 'string']))
+ self._assert_valid_command(['auth',
+ 'caps',
+ 'string',
+ 'more string'])
+
+ def test_del(self):
+ self.check_1_string_arg('auth', 'del')
+
+
+class TestMonitor(TestArgparse):
+
+ def test_compact(self):
+ self._assert_valid_command(['compact'])
+
+ def test_fsid(self):
+ self._assert_valid_command(['fsid'])
+
+ def test_log(self):
+ self.assertEqual({}, validate_command(sigdict, ['log']))
+ self._assert_valid_command(['log', 'a logtext'])
+ self._assert_valid_command(['log', 'a logtext', 'and another'])
+
+ def test_injectargs(self):
+ self.assertEqual({}, validate_command(sigdict, ['injectargs']))
+ self._assert_valid_command(['injectargs', 'one'])
+ self._assert_valid_command(['injectargs', 'one', 'two'])
+
+ def test_status(self):
+ self._assert_valid_command(['status'])
+
+ def test_health(self):
+ self._assert_valid_command(['health'])
+ self._assert_valid_command(['health', 'detail'])
+ self.assertEqual({}, validate_command(sigdict, ['health', 'invalid']))
+ self.assertEqual({}, validate_command(sigdict, ['health', 'detail',
+ 'toomany']))
+
+ def test_df(self):
+ self._assert_valid_command(['df'])
+ self._assert_valid_command(['df', 'detail'])
+ self.assertEqual({}, validate_command(sigdict, ['df', 'invalid']))
+ self.assertEqual({}, validate_command(sigdict, ['df', 'detail',
+ 'toomany']))
+
+ def test_report(self):
+ self._assert_valid_command(['report'])
+ self._assert_valid_command(['report', 'tag1'])
+ self._assert_valid_command(['report', 'tag1', 'tag2'])
+
+ def test_quorum_status(self):
+ self._assert_valid_command(['quorum_status'])
+
+ def test_tell(self):
+ self.assertEqual({}, validate_command(sigdict, ['tell']))
+ self.assertEqual({}, validate_command(sigdict, ['tell', 'invalid']))
+ for name in ('osd', 'mon', 'client', 'mds'):
+ self.assertEqual({}, validate_command(sigdict, ['tell', name]))
+ self.assertEqual({}, validate_command(sigdict, ['tell',
+ name + ".42"]))
+ self._assert_valid_command(['tell', name + ".42", 'something'])
+ self._assert_valid_command(['tell', name + ".42",
+ 'something',
+ 'something else'])
+
+
+class TestMDS(TestArgparse):
+
+ def test_stat(self):
+ self.check_no_arg('mds', 'stat')
+
+ def test_compat_show(self):
+ self._assert_valid_command(['mds', 'compat', 'show'])
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'compat']))
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'compat',
+ 'show', 'toomany']))
+
+ def test_set_state(self):
+ self._assert_valid_command(['mds', 'set_state', '1', '2'])
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'set_state']))
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'set_state', '-1']))
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'set_state',
+ '1', '-1']))
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'set_state',
+ '1', '21']))
+
+ def test_fail(self):
+ self.check_1_string_arg('mds', 'fail')
+
+ def test_rm(self):
+ # Valid: single GID argument present
+ self._assert_valid_command(['mds', 'rm', '1'])
+
+ # Missing GID arg: invalid
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'rm']))
+ # Extra arg: invalid
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'rm', '1', 'mds.42']))
+
+ def test_rmfailed(self):
+ self._assert_valid_command(['mds', 'rmfailed', '0'])
+ self._assert_valid_command(['mds', 'rmfailed', '0', '--yes-i-really-mean-it'])
+ self.assertEqual({}, validate_command(sigdict, ['mds', 'rmfailed', '0',
+ '--yes-i-really-mean-it',
+ 'toomany']))
+
+ def test_compat_rm_compat(self):
+ self._assert_valid_command(['mds', 'compat', 'rm_compat', '1'])
+ self.assertEqual({}, validate_command(sigdict, ['mds',
+ 'compat',
+ 'rm_compat']))
+ self.assertEqual({}, validate_command(sigdict, ['mds',
+ 'compat',
+ 'rm_compat', '-1']))
+ self.assertEqual({}, validate_command(sigdict, ['mds',
+ 'compat',
+ 'rm_compat',
+ '1',
+ '1']))
+
+ def test_incompat_rm_incompat(self):
+ self._assert_valid_command(['mds', 'compat', 'rm_incompat', '1'])
+ self.assertEqual({}, validate_command(sigdict, ['mds',
+ 'compat',
+ 'rm_incompat']))
+ self.assertEqual({}, validate_command(sigdict, ['mds',
+ 'compat',
+ 'rm_incompat', '-1']))
+ self.assertEqual({}, validate_command(sigdict, ['mds',
+ 'compat',
+ 'rm_incompat',
+ '1',
+ '1']))
+
+
+class TestFS(TestArgparse):
+
+ def test_dump(self):
+ self.check_0_or_1_natural_arg('fs', 'dump')
+
+ def test_fs_new(self):
+ self._assert_valid_command(['fs', 'new', 'default', 'metadata', 'data'])
+
+ def test_fs_set_max_mds(self):
+ self._assert_valid_command(['fs', 'set', 'default', 'max_mds', '1'])
+ self._assert_valid_command(['fs', 'set', 'default', 'max_mds', '2'])
+
+ def test_fs_set_cluster_down(self):
+ self._assert_valid_command(['fs', 'set', 'default', 'down', 'true'])
+
+ def test_fs_set_cluster_up(self):
+ self._assert_valid_command(['fs', 'set', 'default', 'down', 'false'])
+
+ def test_fs_set_cluster_joinable(self):
+ self._assert_valid_command(['fs', 'set', 'default', 'joinable', 'true'])
+
+ def test_fs_set_cluster_not_joinable(self):
+ self._assert_valid_command(['fs', 'set', 'default', 'joinable', 'false'])
+
+ def test_fs_set(self):
+ self._assert_valid_command(['fs', 'set', 'default', 'max_file_size', '2'])
+ self._assert_valid_command(['fs', 'set', 'default', 'allow_new_snaps', 'no'])
+ self.assertEqual({}, validate_command(sigdict, ['fs',
+ 'set',
+ 'invalid']))
+
+ def test_fs_add_data_pool(self):
+ self._assert_valid_command(['fs', 'add_data_pool', 'default', '1'])
+ self._assert_valid_command(['fs', 'add_data_pool', 'default', 'foo'])
+
+ def test_fs_remove_data_pool(self):
+ self._assert_valid_command(['fs', 'rm_data_pool', 'default', '1'])
+ self._assert_valid_command(['fs', 'rm_data_pool', 'default', 'foo'])
+
+ def test_fs_rm(self):
+ self._assert_valid_command(['fs', 'rm', 'default'])
+ self._assert_valid_command(['fs', 'rm', 'default', '--yes-i-really-mean-it'])
+ self.assertEqual({}, validate_command(sigdict, ['fs', 'rm', 'default', '--yes-i-really-mean-it', 'toomany']))
+
+ def test_fs_ls(self):
+ self._assert_valid_command(['fs', 'ls'])
+ self.assertEqual({}, validate_command(sigdict, ['fs', 'ls', 'toomany']))
+
+ def test_fs_set_default(self):
+ self._assert_valid_command(['fs', 'set-default', 'cephfs'])
+ self.assertEqual({}, validate_command(sigdict, ['fs', 'set-default']))
+ self.assertEqual({}, validate_command(sigdict, ['fs', 'set-default', 'cephfs', 'toomany']))
+
+
+class TestMon(TestArgparse):
+
+ def test_dump(self):
+ self.check_0_or_1_natural_arg('mon', 'dump')
+
+ def test_stat(self):
+ self.check_no_arg('mon', 'stat')
+
+ def test_getmap(self):
+ self.check_0_or_1_natural_arg('mon', 'getmap')
+
+ def test_add(self):
+ self._assert_valid_command(['mon', 'add', 'name', '1.2.3.4:1234'])
+ self.assertEqual({}, validate_command(sigdict, ['mon', 'add']))
+ self.assertEqual({}, validate_command(sigdict, ['mon', 'add', 'name']))
+ self.assertEqual({}, validate_command(sigdict, ['mon', 'add',
+ 'name',
+ '400.500.600.700']))
+
+ def test_remove(self):
+ self._assert_valid_command(['mon', 'remove', 'name'])
+ self.assertEqual({}, validate_command(sigdict, ['mon', 'remove']))
+ self.assertEqual({}, validate_command(sigdict, ['mon', 'remove',
+ 'name', 'toomany']))
+
+
+class TestOSD(TestArgparse):
+
+ def test_stat(self):
+ self.check_no_arg('osd', 'stat')
+
+ def test_dump(self):
+ self.check_0_or_1_natural_arg('osd', 'dump')
+
+ def test_osd_tree(self):
+ self.check_0_or_1_natural_arg('osd', 'tree')
+ cmd = 'osd tree down,out'
+ self.assertEqual(
+ {
+ 'prefix': 'osd tree',
+ 'states': ['down', 'out']
+ }, validate_command(sigdict, cmd.split()))
+
+ def test_osd_ls(self):
+ self.check_0_or_1_natural_arg('osd', 'ls')
+
+ def test_osd_getmap(self):
+ self.check_0_or_1_natural_arg('osd', 'getmap')
+
+ def test_osd_getcrushmap(self):
+ self.check_0_or_1_natural_arg('osd', 'getcrushmap')
+
+ def test_perf(self):
+ self.check_no_arg('osd', 'perf')
+
+ def test_getmaxosd(self):
+ self.check_no_arg('osd', 'getmaxosd')
+
+ def test_find(self):
+ self.check_1_natural_arg('osd', 'find')
+
+ def test_map(self):
+ self._assert_valid_command(['osd', 'map', 'poolname', 'objectname'])
+ self._assert_valid_command(['osd', 'map', 'poolname', 'objectname', 'nspace'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'map']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'map', 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'map',
+ 'poolname', 'objectname', 'nspace',
+ 'toomany']))
+
+ def test_metadata(self):
+ self.check_0_or_1_natural_arg('osd', 'metadata')
+
+ def test_scrub(self):
+ self.check_1_string_arg('osd', 'scrub')
+
+ def test_deep_scrub(self):
+ self.check_1_string_arg('osd', 'deep-scrub')
+
+ def test_repair(self):
+ self.check_1_string_arg('osd', 'repair')
+
+ def test_lspools(self):
+ self._assert_valid_command(['osd', 'lspools'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'lspools',
+ 'toomany']))
+
+ def test_blocklist_ls(self):
+ self._assert_valid_command(['osd', 'blocklist', 'ls'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'blocklist']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'blocklist',
+ 'ls', 'toomany']))
+
+ def test_crush_rule(self):
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule']))
+ for subcommand in ('list', 'ls'):
+ self._assert_valid_command(['osd', 'crush', 'rule', subcommand])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rule', subcommand,
+ 'toomany']))
+
+ def test_crush_rule_dump(self):
+ self._assert_valid_command(['osd', 'crush', 'rule', 'dump'])
+ self._assert_valid_command(['osd', 'crush', 'rule', 'dump', 'RULE'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rule', 'dump',
+ 'RULE',
+ 'toomany']))
+
+ def test_crush_dump(self):
+ self._assert_valid_command(['osd', 'crush', 'dump'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'dump',
+ 'toomany']))
+
+ def test_setcrushmap(self):
+ self.check_no_arg('osd', 'setcrushmap')
+
+ def test_crush_add_bucket(self):
+ self._assert_valid_command(['osd', 'crush', 'add-bucket',
+ 'name', 'type'])
+ self._assert_valid_command(['osd', 'crush', 'add-bucket',
+ 'name', 'type', 'root=foo-root', 'host=foo-host'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'add-bucket']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'add-bucket', '^^^',
+ 'type']))
+
+ def test_crush_rename_bucket(self):
+ self._assert_valid_command(['osd', 'crush', 'rename-bucket',
+ 'srcname', 'dstname'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rename-bucket']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rename-bucket',
+ 'srcname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rename-bucket',
+ 'srcname',
+ 'dstname',
+ 'toomany']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rename-bucket', '^^^',
+ 'dstname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rename-bucket',
+ 'srcname',
+ '^^^^']))
+
+ def _check_crush_setter(self, setter):
+ self._assert_valid_command(['osd', 'crush', setter,
+ '*', '2.3', 'AZaz09-_.='])
+ self._assert_valid_command(['osd', 'crush', setter,
+ 'osd.0', '2.3', 'AZaz09-_.='])
+ self._assert_valid_command(['osd', 'crush', setter,
+ '0', '2.3', 'AZaz09-_.='])
+ self._assert_valid_command(['osd', 'crush', setter,
+ '0', '2.3', 'AZaz09-_.=', 'AZaz09-_.='])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ setter,
+ 'osd.0']))
+ ret = validate_command(sigdict, ['osd', 'crush',
+ setter,
+ 'osd.0',
+ '-1.0'])
+ assert ret in [None, {}]
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ setter,
+ 'osd.0',
+ '1.0',
+ '^^^']))
+
+ def test_crush_set(self):
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush']))
+ self._check_crush_setter('set')
+
+ def test_crush_add(self):
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush']))
+ self._check_crush_setter('add')
+
+ def test_crush_create_or_move(self):
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush']))
+ self._check_crush_setter('create-or-move')
+
+ def test_crush_move(self):
+ self._assert_valid_command(['osd', 'crush', 'move',
+ 'AZaz09-_.', 'AZaz09-_.='])
+ self._assert_valid_command(['osd', 'crush', 'move',
+ '0', 'AZaz09-_.=', 'AZaz09-_.='])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'move']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'move', 'AZaz09-_.']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'move', '^^^',
+ 'AZaz09-_.=']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'move', 'AZaz09-_.',
+ '^^^']))
+
+ def test_crush_link(self):
+ self._assert_valid_command(['osd', 'crush', 'link',
+ 'name', 'AZaz09-_.='])
+ self._assert_valid_command(['osd', 'crush', 'link',
+ 'name', 'AZaz09-_.=', 'AZaz09-_.='])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'link']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'link',
+ 'name']))
+
+ def test_crush_rm(self):
+ for alias in ('rm', 'remove', 'unlink'):
+ self._assert_valid_command(['osd', 'crush', alias, 'AZaz09-_.'])
+ self._assert_valid_command(['osd', 'crush', alias,
+ 'AZaz09-_.', 'AZaz09-_.'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ alias]))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ alias,
+ 'AZaz09-_.',
+ 'AZaz09-_.',
+ 'toomany']))
+
+ def test_crush_reweight(self):
+ self._assert_valid_command(['osd', 'crush', 'reweight',
+ 'AZaz09-_.', '2.3'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'reweight']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'reweight',
+ 'AZaz09-_.']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'reweight',
+ 'AZaz09-_.',
+ '-1.0']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'reweight',
+ '^^^',
+ '2.3']))
+
+ def test_crush_tunables(self):
+ for tunable in ('legacy', 'argonaut', 'bobtail', 'firefly',
+ 'optimal', 'default'):
+ self._assert_valid_command(['osd', 'crush', 'tunables',
+ tunable])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'tunables']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'tunables',
+ 'default', 'toomany']))
+
+ def test_crush_rule_create_simple(self):
+ self._assert_valid_command(['osd', 'crush', 'rule', 'create-simple',
+ 'AZaz09-_.', 'AZaz09-_.', 'AZaz09-_.'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-simple']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-simple',
+ 'AZaz09-_.']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-simple',
+ 'AZaz09-_.',
+ 'AZaz09-_.']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-simple',
+ '^^^',
+ 'AZaz09-_.',
+ 'AZaz09-_.']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-simple',
+ 'AZaz09-_.',
+ '|||',
+ 'AZaz09-_.']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-simple',
+ 'AZaz09-_.',
+ 'AZaz09-_.',
+ '+++']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-simple',
+ 'AZaz09-_.',
+ 'AZaz09-_.',
+ 'AZaz09-_.',
+ 'toomany']))
+
+ def test_crush_rule_create_erasure(self):
+ self._assert_valid_command(['osd', 'crush', 'rule', 'create-erasure',
+ 'AZaz09-_.'])
+ self._assert_valid_command(['osd', 'crush', 'rule', 'create-erasure',
+ 'AZaz09-_.', 'whatever'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-erasure']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-erasure',
+ '^^^']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush', 'rule',
+ 'create-erasure',
+ 'name', '^^^']))
+
+ def test_crush_rule_rm(self):
+ self._assert_valid_command(['osd', 'crush', 'rule', 'rm', 'AZaz09-_.'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rule', 'rm']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rule', 'rm',
+ '^^^^']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'crush',
+ 'rule', 'rm',
+ 'AZaz09-_.',
+ 'toomany']))
+
+ def test_setmaxosd(self):
+ self.check_1_natural_arg('osd', 'setmaxosd')
+
+ def test_pause(self):
+ self.check_no_arg('osd', 'pause')
+
+ def test_unpause(self):
+ self.check_no_arg('osd', 'unpause')
+
+ def test_erasure_code_profile_set(self):
+ self._assert_valid_command(['osd', 'erasure-code-profile', 'set',
+ 'name'])
+ self._assert_valid_command(['osd', 'erasure-code-profile', 'set',
+ 'name', 'A=B'])
+ self._assert_valid_command(['osd', 'erasure-code-profile', 'set',
+ 'name', 'A=B', 'C=D'])
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'erasure-code-profile',
+ 'set']))
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'erasure-code-profile',
+ 'set',
+ '^^^^']))
+
+ def test_erasure_code_profile_get(self):
+ self._assert_valid_command(['osd', 'erasure-code-profile', 'get',
+ 'name'])
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'erasure-code-profile',
+ 'get']))
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'erasure-code-profile',
+ 'get',
+ '^^^^']))
+
+ def test_erasure_code_profile_rm(self):
+ self._assert_valid_command(['osd', 'erasure-code-profile', 'rm',
+ 'name'])
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'erasure-code-profile',
+ 'rm']))
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'erasure-code-profile',
+ 'rm',
+ '^^^^']))
+
+ def test_erasure_code_profile_ls(self):
+ self._assert_valid_command(['osd', 'erasure-code-profile', 'ls'])
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'erasure-code-profile',
+ 'ls',
+ 'toomany']))
+
+ def test_set_unset(self):
+ for action in ('set', 'unset'):
+ for flag in ('pause', 'noup', 'nodown', 'noout', 'noin',
+ 'nobackfill', 'norecover', 'noscrub', 'nodeep-scrub'):
+ self._assert_valid_command(['osd', action, flag])
+ self.assertEqual({}, validate_command(sigdict, ['osd', action]))
+ self.assertEqual({}, validate_command(sigdict, ['osd', action,
+ 'invalid']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', action,
+ 'pause',
+ 'toomany']))
+
+ def test_down(self):
+ self.check_1_or_more_string_args('osd', 'down')
+
+ def test_out(self):
+ self.check_1_or_more_string_args('osd', 'out')
+
+ def test_in(self):
+ self.check_1_or_more_string_args('osd', 'in')
+
+ def test_rm(self):
+ self.check_1_or_more_string_args('osd', 'rm')
+
+ def test_reweight(self):
+ self._assert_valid_command(['osd', 'reweight', '1', '0.1'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'reweight']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'reweight',
+ '1']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'reweight',
+ '1', '2.0']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'reweight',
+ '-1', '0.1']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'reweight',
+ '1', '0.1',
+ 'toomany']))
+
+ def test_lost(self):
+ self._assert_valid_command(['osd', 'lost', '1',
+ '--yes-i-really-mean-it'])
+ self._assert_valid_command(['osd', 'lost', '1'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'lost']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'lost',
+ '1',
+ 'what?']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'lost',
+ '-1',
+ '--yes-i-really-mean-it']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'lost',
+ '1',
+ '--yes-i-really-mean-it',
+ 'toomany']))
+
+ def test_create(self):
+ uuid = '12345678123456781234567812345678'
+ self._assert_valid_command(['osd', 'create'])
+ self._assert_valid_command(['osd', 'create', uuid])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'create',
+ 'invalid']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'create',
+ uuid,
+ 'toomany']))
+
+ def test_blocklist(self):
+ for action in ('add', 'rm'):
+ self._assert_valid_command(['osd', 'blocklist', action,
+ '1.2.3.4/567'])
+ self._assert_valid_command(['osd', 'blocklist', action,
+ '1.2.3.4'])
+ self._assert_valid_command(['osd', 'blocklist', action,
+ '1.2.3.4/567', '600.40'])
+ self._assert_valid_command(['osd', 'blocklist', action,
+ '1.2.3.4', '600.40'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'blocklist',
+ action,
+ 'invalid',
+ '600.40']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'blocklist',
+ action,
+ '1.2.3.4/567',
+ '-1.0']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'blocklist',
+ action,
+ '1.2.3.4/567',
+ '600.40',
+ 'toomany']))
+
+ def test_pool_mksnap(self):
+ self._assert_valid_command(['osd', 'pool', 'mksnap',
+ 'poolname', 'snapname'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'mksnap']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'mksnap',
+ 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'mksnap',
+ 'poolname', 'snapname',
+ 'toomany']))
+
+ def test_pool_rmsnap(self):
+ self._assert_valid_command(['osd', 'pool', 'rmsnap',
+ 'poolname', 'snapname'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'rmsnap']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'rmsnap',
+ 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'rmsnap',
+ 'poolname', 'snapname',
+ 'toomany']))
+
+ def test_pool_kwargs(self):
+ """
+ Use the pool creation command to exercise keyword-style arguments
+ since it has lots of parameters
+ """
+ # Simply use a keyword arg instead of a positional arg, in its
+ # normal order (pgp_num after pg_num)
+ self.assertEqual(
+ {
+ "prefix": "osd pool create",
+ "pool": "foo",
+ "pg_num": 8,
+ "pgp_num": 16
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'create', "foo", "8", "--pgp_num", "16"]))
+
+ # Again, but using the "--foo=bar" style
+ self.assertEqual(
+ {
+ "prefix": "osd pool create",
+ "pool": "foo",
+ "pg_num": 8,
+ "pgp_num": 16
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'create', "foo", "8", "--pgp_num=16"]))
+
+ # Specify keyword args in a different order than their definitions
+ # (pgp_num after pool_type)
+ self.assertEqual(
+ {
+ "prefix": "osd pool create",
+ "pool": "foo",
+ "pg_num": 8,
+ "pgp_num": 16,
+ "pool_type": "replicated"
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'create', "foo", "8",
+ "--pool_type", "replicated",
+ "--pgp_num", "16"]))
+
+ # Use a keyword argument that doesn't exist, should fail validation
+ self.assertEqual({}, validate_command(sigdict,
+ ['osd', 'pool', 'create', "foo", "8", "--foo=bar"]))
+
+ def test_foo(self):
+ # Long form of a boolean argument (--foo=true)
+ self.assertEqual(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": True
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=true"]))
+
+ def test_pool_bool_args(self):
+ """
+ Use pool deletion to exercise boolean arguments since it has
+ the --yes-i-really-really-mean-it flags
+ """
+
+ # Short form of a boolean argument (--foo)
+ self.assertEqual(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": True
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it"]))
+
+ # Long form of a boolean argument (--foo=true)
+ self.assertEqual(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": True
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=true"]))
+
+ # Negative form of a boolean argument (--foo=false)
+ self.assertEqual(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": False
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=false"]))
+
+ # Invalid value boolean argument (--foo=somethingelse)
+ self.assertEqual({}, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=rhubarb"]))
+
+ def test_pool_create(self):
+ self._assert_valid_command(['osd', 'pool', 'create',
+ 'poolname', '128'])
+ self._assert_valid_command(['osd', 'pool', 'create',
+ 'poolname', '128', '128'])
+ self._assert_valid_command(['osd', 'pool', 'create',
+ 'poolname', '128', '128',
+ 'replicated'])
+ self._assert_valid_command(['osd', 'pool', 'create',
+ 'poolname', '128', '128',
+ 'erasure', 'A-Za-z0-9-_.', 'rule^^'])
+ self._assert_valid_command(['osd', 'pool', 'create', 'poolname'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'create']))
+ # invalid pg_num and pgp_num, like "-1", could spill over to
+ # erasure_code_profile and rule as they are valid profile and rule
+ # names, so validate_commands() cannot identify such cases.
+ # but if they are matched by profile and rule, the "rule" argument
+ # won't get a chance to be matched anymore.
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'create',
+ 'poolname',
+ '-1', '-1',
+ 'rule']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'create',
+ 'poolname',
+ '128', '128',
+ 'erasure', '^^^',
+ 'rule']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'create',
+ 'poolname',
+ '128', '128',
+ 'erasure', 'profile',
+ 'rule',
+ 'toomany']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'create',
+ 'poolname',
+ '128', '128',
+ 'INVALID', 'profile',
+ 'rule']))
+
+ def test_pool_delete(self):
+ self._assert_valid_command(['osd', 'pool', 'delete',
+ 'poolname', 'poolname',
+ '--yes-i-really-really-mean-it'])
+ self._assert_valid_command(['osd', 'pool', 'delete',
+ 'poolname', 'poolname'])
+ self._assert_valid_command(['osd', 'pool', 'delete',
+ 'poolname'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'delete']))
+ self.assertEqual({}, validate_command(sigdict,
+ ['osd', 'pool', 'delete',
+ 'poolname', 'poolname',
+ '--yes-i-really-really-mean-it',
+ 'toomany']))
+
+ def test_pool_rename(self):
+ self._assert_valid_command(['osd', 'pool', 'rename',
+ 'poolname', 'othername'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'rename']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'rename',
+ 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool', 'rename',
+ 'poolname', 'othername',
+ 'toomany']))
+
+ def test_pool_get(self):
+ for var in ('size', 'min_size',
+ 'pg_num', 'pgp_num', 'crush_rule', 'fast_read',
+ 'scrub_min_interval', 'scrub_max_interval',
+ 'deep_scrub_interval', 'recovery_priority',
+ 'recovery_op_priority'):
+ self._assert_valid_command(['osd', 'pool', 'get', 'poolname', var])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'get']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'get', 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'get', 'poolname',
+ 'size', 'toomany']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'get', 'poolname',
+ 'invalid']))
+
+ def test_pool_set(self):
+ for var in ('size', 'min_size',
+ 'pg_num', 'pgp_num', 'crush_rule',
+ 'hashpspool', 'fast_read',
+ 'scrub_min_interval', 'scrub_max_interval',
+ 'deep_scrub_interval', 'recovery_priority',
+ 'recovery_op_priority'):
+ self._assert_valid_command(['osd', 'pool',
+ 'set', 'poolname', var, 'value'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set', 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set', 'poolname',
+ 'size', 'value',
+ 'toomany']))
+
+ def test_pool_set_quota(self):
+ for field in ('max_objects', 'max_bytes'):
+ self._assert_valid_command(['osd', 'pool', 'set-quota',
+ 'poolname', field, '10K'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set-quota']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set-quota',
+ 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set-quota',
+ 'poolname',
+ 'max_objects']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set-quota',
+ 'poolname',
+ 'invalid',
+ '10K']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'pool',
+ 'set-quota',
+ 'poolname',
+ 'max_objects',
+ '10K',
+ 'toomany']))
+
+ def test_reweight_by_utilization(self):
+ self._assert_valid_command(['osd', 'reweight-by-utilization'])
+ self._assert_valid_command(['osd', 'reweight-by-utilization', '100'])
+ self._assert_valid_command(['osd', 'reweight-by-utilization', '100', '.1'])
+ self.assertEqual({}, validate_command(sigdict, ['osd',
+ 'reweight-by-utilization',
+ '100',
+ 'toomany']))
+
+ def test_tier_op(self):
+ for op in ('add', 'remove', 'set-overlay'):
+ self._assert_valid_command(['osd', 'tier', op,
+ 'poolname', 'othername'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'tier', op]))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'tier', op,
+ 'poolname']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'tier', op,
+ 'poolname',
+ 'othername',
+ 'toomany']))
+
+ def test_tier_cache_mode(self):
+ for mode in ('none', 'writeback', 'readonly', 'readproxy'):
+ self._assert_valid_command(['osd', 'tier', 'cache-mode',
+ 'poolname', mode])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'tier',
+ 'cache-mode']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'tier',
+ 'cache-mode',
+ 'invalid']))
+
+ def test_tier_remove_overlay(self):
+ self._assert_valid_command(['osd', 'tier', 'remove-overlay',
+ 'poolname'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'tier',
+ 'remove-overlay']))
+ self.assertEqual({}, validate_command(sigdict, ['osd', 'tier',
+ 'remove-overlay',
+ 'poolname',
+ 'toomany']))
+
+ def _set_ratio(self, command):
+ self._assert_valid_command(['osd', command, '0.0'])
+ self.assertEqual({}, validate_command(sigdict, ['osd', command]))
+ self.assertEqual({}, validate_command(sigdict, ['osd', command, '2.0']))
+
+ def test_set_full_ratio(self):
+ self._set_ratio('set-full-ratio')
+
+ def test_set_backfillfull_ratio(self):
+ self._set_ratio('set-backfillfull-ratio')
+
+ def test_set_nearfull_ratio(self):
+ self._set_ratio('set-nearfull-ratio')
+
+
+class TestConfigKey(TestArgparse):
+
+ def test_get(self):
+ self.check_1_string_arg('config-key', 'get')
+
+ def test_put(self):
+ self._assert_valid_command(['config-key', 'put',
+ 'key'])
+ self._assert_valid_command(['config-key', 'put',
+ 'key', 'value'])
+ self.assertEqual({}, validate_command(sigdict, ['config-key', 'put']))
+ self.assertEqual({}, validate_command(sigdict, ['config-key', 'put',
+ 'key', 'value',
+ 'toomany']))
+
+ def test_del(self):
+ self.check_1_string_arg('config-key', 'del')
+
+ def test_exists(self):
+ self.check_1_string_arg('config-key', 'exists')
+
+ def test_dump(self):
+ self.check_0_or_1_string_arg('config-key', 'dump')
+
+ def test_list(self):
+ self.check_no_arg('config-key', 'list')
+
+
+class TestValidate(unittest.TestCase):
+
+ ARGS = 0
+ KWARGS = 1
+ KWARGS_EQ = 2
+ MIXED = 3
+
+ def setUp(self):
+ self.prefix = ['some', 'random', 'cmd']
+ self.args_dict = [
+ {'name': 'variable_one', 'type': 'CephString'},
+ {'name': 'variable_two', 'type': 'CephString'},
+ {'name': 'variable_three', 'type': 'CephString'},
+ {'name': 'variable_four', 'type': 'CephInt'},
+ {'name': 'variable_five', 'type': 'CephString'}]
+ self.args = []
+ for d in self.args_dict:
+ if d['type'] == 'CephInt':
+ val = "{}".format(random.randint(0, 100))
+ elif d['type'] == 'CephString':
+ letters = string.ascii_letters
+ str_len = random.randint(5, 10)
+ val = ''.join(random.choice(letters) for _ in range(str_len))
+ else:
+ raise skipTest()
+
+ self.args.append((d['name'], val))
+
+ self.sig = parse_funcsig(self.prefix + self.args_dict)
+
+ def _arg_kwarg_test(self, prefix, args, sig, arg_type=0):
+ """
+ Runs validate in different arg/kargs ways.
+
+ :param prefix: List of prefix commands (that can't be kwarged)
+ :param args: a list of kwarg, arg pairs: [(k1, v1), (k2, v2), ...]
+ :param sig: The sig to match
+ :param arg_type: how to build the args to send. As positional args (ARGS),
+ as long kwargs (KWARGS [--k v]), other style long kwargs
+ (KWARGS_EQ (--k=v]), and mixed (MIXED) where there will be
+ a random mix of the above.
+ :return: None, the method will assert.
+ """
+ final_args = list(prefix)
+ for k, v in args:
+ a_type = arg_type
+ if a_type == self.MIXED:
+ a_type = random.choice((self.ARGS,
+ self.KWARGS,
+ self.KWARGS_EQ))
+ if a_type == self.ARGS:
+ final_args.append(v)
+ elif a_type == self.KWARGS:
+ final_args.extend(["--{}".format(k), v])
+ else:
+ final_args.append("--{}={}".format(k, v))
+
+ try:
+ validate(final_args, sig)
+ except (ArgumentError, ArgumentMissing,
+ ArgumentNumber, ArgumentTooFew, ArgumentValid) as ex:
+ self.fail("Validation failed: {}".format(str(ex)))
+
+ def test_args_and_kwargs_validate(self):
+ for arg_type in (self.ARGS, self.KWARGS, self.KWARGS_EQ, self.MIXED):
+ self._arg_kwarg_test(self.prefix, self.args, self.sig, arg_type)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+# Local Variables:
+# compile-command: "cd ../../..; cmake --build build --target get_command_descriptions -j4 &&
+# CEPH_BIN=build/bin \
+# PYTHONPATH=src/pybind python3 \
+# src/test/pybind/test_ceph_argparse.py"
+# End:
diff --git a/src/test/pybind/test_ceph_daemon.py b/src/test/pybind/test_ceph_daemon.py
new file mode 100755
index 000000000..df8d4c0b0
--- /dev/null
+++ b/src/test/pybind/test_ceph_daemon.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# -*- mode:python; tab-width:4; indent-tabs-mode:t -*-
+# vim: ts=4 sw=4 smarttab expandtab
+#
+"""
+Copyright (C) 2015 Red Hat
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public
+License version 2, as published by the Free Software
+Foundation. See file COPYING.
+"""
+
+import unittest
+
+from ceph_daemon import DaemonWatcher
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+
+class TestDaemonWatcher(unittest.TestCase):
+ def test_format(self):
+ dw = DaemonWatcher(None)
+
+ self.assertEqual(dw.format_dimless(1, 4), " 1 ")
+ self.assertEqual(dw.format_dimless(1000, 4), "1.0k")
+ self.assertEqual(dw.format_dimless(3.14159, 4), " 3 ")
+ self.assertEqual(dw.format_dimless(1400000, 4), "1.4M")
+
+ def test_col_width(self):
+ dw = DaemonWatcher(None)
+
+ self.assertEqual(dw.col_width("foo"), 4)
+ self.assertEqual(dw.col_width("foobar"), 6)
+
+ def test_supports_color(self):
+ dw = DaemonWatcher(None)
+ # Can't count on having a tty available during tests, so only test the false case
+ self.assertFalse(dw.supports_color(StringIO()))
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+# Local Variables:
+# compile-command: "cd ../../..;
+# PYTHONPATH=src/pybind python3 src/test/pybind/test_ceph_daemon.py"
+# End:
diff --git a/src/test/pybind/test_cephfs.py b/src/test/pybind/test_cephfs.py
new file mode 100644
index 000000000..d16807de9
--- /dev/null
+++ b/src/test/pybind/test_cephfs.py
@@ -0,0 +1,909 @@
+# vim: expandtab smarttab shiftwidth=4 softtabstop=4
+from assertions import assert_raises, assert_equal, assert_not_equal, assert_greater
+import collections
+collections.Callable = collections.abc.Callable
+import cephfs as libcephfs
+import fcntl
+import os
+import pytest
+import random
+import time
+import stat
+import uuid
+from datetime import datetime
+
+cephfs = None
+
+def setup_module():
+ global cephfs
+ cephfs = libcephfs.LibCephFS(conffile='')
+ cephfs.mount()
+
+def teardown_module():
+ global cephfs
+ cephfs.shutdown()
+
+def purge_dir(path, is_snap = False):
+ print(b"Purge " + path)
+ d = cephfs.opendir(path)
+ if (not path.endswith(b"/")):
+ path = path + b"/"
+ dent = cephfs.readdir(d)
+ while dent:
+ if (dent.d_name not in [b".", b".."]):
+ print(path + dent.d_name)
+ if dent.is_dir():
+ if (not is_snap):
+ try:
+ snappath = path + dent.d_name + b"/.snap"
+ cephfs.stat(snappath)
+ purge_dir(snappath, True)
+ except:
+ pass
+ purge_dir(path + dent.d_name, False)
+ cephfs.rmdir(path + dent.d_name)
+ else:
+ print("rmsnap on {} snap {}".format(path, dent.d_name))
+ cephfs.rmsnap(path, dent.d_name);
+ else:
+ cephfs.unlink(path + dent.d_name)
+ dent = cephfs.readdir(d)
+ cephfs.closedir(d)
+
+@pytest.fixture
+def testdir():
+ purge_dir(b"/")
+
+ cephfs.chdir(b"/")
+ _, ret_buf = cephfs.listxattr("/")
+ print(f'ret_buf={ret_buf}')
+ xattrs = ret_buf.decode('utf-8').split('\x00')
+ for xattr in xattrs[:-1]:
+ cephfs.removexattr("/", xattr)
+
+def test_conf_get(testdir):
+ fsid = cephfs.conf_get("fsid")
+ assert(len(fsid) > 0)
+
+def test_version():
+ cephfs.version()
+
+def test_fstat(testdir):
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+ stat = cephfs.fstat(fd)
+ assert(len(stat) == 13)
+ cephfs.close(fd)
+
+def test_statfs(testdir):
+ stat = cephfs.statfs(b'/')
+ assert(len(stat) == 11)
+
+def test_statx(testdir):
+ stat = cephfs.statx(b'/', libcephfs.CEPH_STATX_MODE, 0)
+ assert('mode' in stat.keys())
+ stat = cephfs.statx(b'/', libcephfs.CEPH_STATX_BTIME, 0)
+ assert('btime' in stat.keys())
+
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ cephfs.close(fd)
+ cephfs.symlink(b'file-1', b'file-2')
+ stat = cephfs.statx(b'file-2', libcephfs.CEPH_STATX_MODE | libcephfs.CEPH_STATX_BTIME, libcephfs.AT_SYMLINK_NOFOLLOW)
+ assert('mode' in stat.keys())
+ assert('btime' in stat.keys())
+ cephfs.unlink(b'file-2')
+ cephfs.unlink(b'file-1')
+
+def test_syncfs(testdir):
+ stat = cephfs.sync_fs()
+
+def test_fsync(testdir):
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+ cephfs.write(fd, b"asdf", 0)
+ stat = cephfs.fsync(fd, 0)
+ cephfs.write(fd, b"qwer", 0)
+ stat = cephfs.fsync(fd, 1)
+ cephfs.close(fd)
+ #sync on non-existing fd (assume fd 12345 is not exists)
+ assert_raises(libcephfs.Error, cephfs.fsync, 12345, 0)
+
+def test_directory(testdir):
+ cephfs.mkdir(b"/temp-directory", 0o755)
+ cephfs.mkdirs(b"/temp-directory/foo/bar", 0o755)
+ cephfs.chdir(b"/temp-directory")
+ assert_equal(cephfs.getcwd(), b"/temp-directory")
+ cephfs.rmdir(b"/temp-directory/foo/bar")
+ cephfs.rmdir(b"/temp-directory/foo")
+ cephfs.rmdir(b"/temp-directory")
+ assert_raises(libcephfs.ObjectNotFound, cephfs.chdir, b"/temp-directory")
+
+def test_walk_dir(testdir):
+ cephfs.chdir(b"/")
+ dirs = [b"dir-1", b"dir-2", b"dir-3"]
+ for i in dirs:
+ cephfs.mkdir(i, 0o755)
+ handler = cephfs.opendir(b"/")
+ d = cephfs.readdir(handler)
+ dirs += [b".", b".."]
+ while d:
+ assert(d.d_name in dirs)
+ dirs.remove(d.d_name)
+ d = cephfs.readdir(handler)
+ assert(len(dirs) == 0)
+ dirs = [b"/dir-1", b"/dir-2", b"/dir-3"]
+ for i in dirs:
+ cephfs.rmdir(i)
+ cephfs.closedir(handler)
+
+def test_xattr(testdir):
+ assert_raises(libcephfs.OperationNotSupported, cephfs.setxattr, "/", "key", b"value", 0)
+ cephfs.setxattr("/", "user.key", b"value", 0)
+ assert_equal(b"value", cephfs.getxattr("/", "user.key"))
+
+ cephfs.setxattr("/", "user.big", b"x" * 300, 0)
+
+ # Default size is 255, get ERANGE
+ assert_raises(libcephfs.OutOfRange, cephfs.getxattr, "/", "user.big")
+
+ # Pass explicit size, and we'll get the value
+ assert_equal(300, len(cephfs.getxattr("/", "user.big", 300)))
+
+ cephfs.removexattr("/", "user.key")
+ # user.key is already removed
+ assert_raises(libcephfs.NoData, cephfs.getxattr, "/", "user.key")
+
+ # user.big is only listed
+ ret_val, ret_buff = cephfs.listxattr("/")
+ assert_equal(9, ret_val)
+ assert_equal("user.big\x00", ret_buff.decode('utf-8'))
+
+def test_ceph_mirror_xattr(testdir):
+ def gen_mirror_xattr():
+ cluster_id = str(uuid.uuid4())
+ fs_id = random.randint(1, 10)
+ mirror_xattr = f'cluster_id={cluster_id} fs_id={fs_id}'
+ return mirror_xattr.encode('utf-8')
+
+ mirror_xattr_enc_1 = gen_mirror_xattr()
+
+ # mirror xattr is only allowed on root
+ cephfs.mkdir('/d0', 0o755)
+ assert_raises(libcephfs.InvalidValue, cephfs.setxattr,
+ '/d0', 'ceph.mirror.info', mirror_xattr_enc_1, os.XATTR_CREATE)
+ cephfs.rmdir('/d0')
+
+ cephfs.setxattr('/', 'ceph.mirror.info', mirror_xattr_enc_1, os.XATTR_CREATE)
+ assert_equal(mirror_xattr_enc_1, cephfs.getxattr('/', 'ceph.mirror.info'))
+
+ # setting again with XATTR_CREATE should fail
+ assert_raises(libcephfs.ObjectExists, cephfs.setxattr,
+ '/', 'ceph.mirror.info', mirror_xattr_enc_1, os.XATTR_CREATE)
+
+ # ceph.mirror.info should not show up in listing
+ ret_val, _ = cephfs.listxattr("/")
+ assert_equal(0, ret_val)
+
+ mirror_xattr_enc_2 = gen_mirror_xattr()
+
+ cephfs.setxattr('/', 'ceph.mirror.info', mirror_xattr_enc_2, os.XATTR_REPLACE)
+ assert_equal(mirror_xattr_enc_2, cephfs.getxattr('/', 'ceph.mirror.info'))
+
+ cephfs.removexattr('/', 'ceph.mirror.info')
+ # ceph.mirror.info is already removed
+ assert_raises(libcephfs.NoData, cephfs.getxattr, '/', 'ceph.mirror.info')
+ # removing again should throw error
+ assert_raises(libcephfs.NoData, cephfs.removexattr, "/", "ceph.mirror.info")
+
+ # check mirror info xattr format
+ assert_raises(libcephfs.InvalidValue, cephfs.setxattr, '/', 'ceph.mirror.info', b"unknown", 0)
+
+def test_fxattr(testdir):
+ fd = cephfs.open(b'/file-fxattr', 'w', 0o755)
+ assert_raises(libcephfs.OperationNotSupported, cephfs.fsetxattr, fd, "key", b"value", 0)
+ assert_raises(TypeError, cephfs.fsetxattr, "fd", "user.key", b"value", 0)
+ assert_raises(TypeError, cephfs.fsetxattr, fd, "user.key", "value", 0)
+ assert_raises(TypeError, cephfs.fsetxattr, fd, "user.key", b"value", "0")
+ cephfs.fsetxattr(fd, "user.key", b"value", 0)
+ assert_equal(b"value", cephfs.fgetxattr(fd, "user.key"))
+
+ cephfs.fsetxattr(fd, "user.big", b"x" * 300, 0)
+
+ # Default size is 255, get ERANGE
+ assert_raises(libcephfs.OutOfRange, cephfs.fgetxattr, fd, "user.big")
+
+ # Pass explicit size, and we'll get the value
+ assert_equal(300, len(cephfs.fgetxattr(fd, "user.big", 300)))
+
+ cephfs.fremovexattr(fd, "user.key")
+ # user.key is already removed
+ assert_raises(libcephfs.NoData, cephfs.fgetxattr, fd, "user.key")
+
+ # user.big is only listed
+ ret_val, ret_buff = cephfs.flistxattr(fd)
+ assert_equal(9, ret_val)
+ assert_equal("user.big\x00", ret_buff.decode('utf-8'))
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-fxattr')
+
+def test_rename(testdir):
+ cephfs.mkdir(b"/a", 0o755)
+ cephfs.mkdir(b"/a/b", 0o755)
+ cephfs.rename(b"/a", b"/b")
+ cephfs.stat(b"/b/b")
+ cephfs.rmdir(b"/b/b")
+ cephfs.rmdir(b"/b")
+
+def test_open(testdir):
+ assert_raises(libcephfs.ObjectNotFound, cephfs.open, b'file-1', 'r')
+ assert_raises(libcephfs.ObjectNotFound, cephfs.open, b'file-1', 'r+')
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+ cephfs.write(fd, b"asdf", 0)
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-1', 'r', 0o755)
+ assert_equal(cephfs.read(fd, 0, 4), b"asdf")
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-1', 'r+', 0o755)
+ cephfs.write(fd, b"zxcv", 4)
+ assert_equal(cephfs.read(fd, 4, 8), b"zxcv")
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-1', 'w+', 0o755)
+ assert_equal(cephfs.read(fd, 0, 4), b"")
+ cephfs.write(fd, b"zxcv", 4)
+ assert_equal(cephfs.read(fd, 4, 8), b"zxcv")
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-1', os.O_RDWR, 0o755)
+ cephfs.write(fd, b"asdf", 0)
+ assert_equal(cephfs.read(fd, 0, 4), b"asdf")
+ cephfs.close(fd)
+ assert_raises(libcephfs.OperationNotSupported, cephfs.open, b'file-1', 'a')
+ cephfs.unlink(b'file-1')
+
+def test_link(testdir):
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ cephfs.close(fd)
+ cephfs.link(b'file-1', b'file-2')
+ fd = cephfs.open(b'file-2', 'r', 0o755)
+ assert_equal(cephfs.read(fd, 0, 4), b"1111")
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-2', 'r+', 0o755)
+ cephfs.write(fd, b"2222", 4)
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-1', 'r', 0o755)
+ assert_equal(cephfs.read(fd, 0, 8), b"11112222")
+ cephfs.close(fd)
+ cephfs.unlink(b'file-2')
+
+def test_symlink(testdir):
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ cephfs.close(fd)
+ cephfs.symlink(b'file-1', b'file-2')
+ fd = cephfs.open(b'file-2', 'r', 0o755)
+ assert_equal(cephfs.read(fd, 0, 4), b"1111")
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-2', 'r+', 0o755)
+ cephfs.write(fd, b"2222", 4)
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-1', 'r', 0o755)
+ assert_equal(cephfs.read(fd, 0, 8), b"11112222")
+ cephfs.close(fd)
+ cephfs.unlink(b'file-2')
+
+def test_readlink(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ cephfs.close(fd)
+ cephfs.symlink(b'/file-1', b'/file-2')
+ d = cephfs.readlink(b"/file-2",100)
+ assert_equal(d, b"/file-1")
+ cephfs.unlink(b'/file-2')
+ cephfs.unlink(b'/file-1')
+
+def test_delete_cwd(testdir):
+ assert_equal(b"/", cephfs.getcwd())
+
+ cephfs.mkdir(b"/temp-directory", 0o755)
+ cephfs.chdir(b"/temp-directory")
+ cephfs.rmdir(b"/temp-directory")
+
+ # getcwd gives you something stale here: it remembers the path string
+ # even when things are unlinked. It's up to the caller to find out
+ # whether it really still exists
+ assert_equal(b"/temp-directory", cephfs.getcwd())
+
+def test_flock(testdir):
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+
+ cephfs.flock(fd, fcntl.LOCK_EX, 123);
+ fd2 = cephfs.open(b'file-1', 'w', 0o755)
+
+ assert_raises(libcephfs.WouldBlock, cephfs.flock, fd2,
+ fcntl.LOCK_EX | fcntl.LOCK_NB, 456);
+ cephfs.close(fd2)
+
+ cephfs.close(fd)
+
+def test_mount_unmount(testdir):
+ test_directory(testdir)
+ cephfs.unmount()
+ cephfs.mount()
+ test_open(testdir)
+
+def test_lxattr(testdir):
+ fd = cephfs.open(b'/file-lxattr', 'w', 0o755)
+ cephfs.close(fd)
+ cephfs.setxattr(b"/file-lxattr", "user.key", b"value", 0)
+ cephfs.symlink(b"/file-lxattr", b"/file-sym-lxattr")
+ assert_equal(b"value", cephfs.getxattr(b"/file-sym-lxattr", "user.key"))
+ assert_raises(libcephfs.NoData, cephfs.lgetxattr, b"/file-sym-lxattr", "user.key")
+
+ cephfs.lsetxattr(b"/file-sym-lxattr", "trusted.key-sym", b"value-sym", 0)
+ assert_equal(b"value-sym", cephfs.lgetxattr(b"/file-sym-lxattr", "trusted.key-sym"))
+ cephfs.lsetxattr(b"/file-sym-lxattr", "trusted.big", b"x" * 300, 0)
+
+ # Default size is 255, get ERANGE
+ assert_raises(libcephfs.OutOfRange, cephfs.lgetxattr, b"/file-sym-lxattr", "trusted.big")
+
+ # Pass explicit size, and we'll get the value
+ assert_equal(300, len(cephfs.lgetxattr(b"/file-sym-lxattr", "trusted.big", 300)))
+
+ cephfs.lremovexattr(b"/file-sym-lxattr", "trusted.key-sym")
+ # trusted.key-sym is already removed
+ assert_raises(libcephfs.NoData, cephfs.lgetxattr, b"/file-sym-lxattr", "trusted.key-sym")
+
+ # trusted.big is only listed
+ ret_val, ret_buff = cephfs.llistxattr(b"/file-sym-lxattr")
+ assert_equal(12, ret_val)
+ assert_equal("trusted.big\x00", ret_buff.decode('utf-8'))
+ cephfs.unlink(b'/file-lxattr')
+ cephfs.unlink(b'/file-sym-lxattr')
+
+def test_mount_root(testdir):
+ cephfs.mkdir(b"/mount-directory", 0o755)
+ cephfs.unmount()
+ cephfs.mount(mount_root = b"/mount-directory")
+
+ assert_raises(libcephfs.Error, cephfs.mount, mount_root = b"/nowhere")
+ cephfs.unmount()
+ cephfs.mount()
+
+def test_utime(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+ cephfs.close(fd)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.utime(b'/file-1')
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = int(time.mktime(stx_pre['atime'].timetuple()))
+ mtime_pre = int(time.mktime(stx_pre['mtime'].timetuple()))
+
+ cephfs.utime(b'/file-1', (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.unlink(b'/file-1')
+
+def test_futime(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.futime(fd)
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = int(time.mktime(stx_pre['atime'].timetuple()))
+ mtime_pre = int(time.mktime(stx_pre['mtime'].timetuple()))
+
+ cephfs.futime(fd, (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-1')
+
+def test_utimes(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+ cephfs.close(fd)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.utimes(b'/file-1')
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = time.mktime(stx_pre['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre['mtime'].timetuple())
+
+ cephfs.utimes(b'/file-1', (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.unlink(b'/file-1')
+
+def test_lutimes(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+ cephfs.close(fd)
+
+ cephfs.symlink(b'/file-1', b'/file-2')
+
+ stx_pre_t = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+ stx_pre_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ time.sleep(1)
+ cephfs.lutimes(b'/file-2')
+
+ stx_post_t = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+ stx_post_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ assert_equal(stx_post_t['atime'], stx_pre_t['atime'])
+ assert_equal(stx_post_t['mtime'], stx_pre_t['mtime'])
+
+ assert_greater(stx_post_s['atime'], stx_pre_s['atime'])
+ assert_greater(stx_post_s['mtime'], stx_pre_s['mtime'])
+
+ atime_pre = time.mktime(stx_pre_s['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre_s['mtime'].timetuple())
+
+ cephfs.lutimes(b'/file-2', (atime_pre, mtime_pre))
+ stx_post_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ assert_equal(stx_post_s['atime'], stx_pre_s['atime'])
+ assert_equal(stx_post_s['mtime'], stx_pre_s['mtime'])
+
+ cephfs.unlink(b'/file-2')
+ cephfs.unlink(b'/file-1')
+
+def test_futimes(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.futimes(fd)
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = time.mktime(stx_pre['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre['mtime'].timetuple())
+
+ cephfs.futimes(fd, (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-1')
+
+def test_futimens(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.futimens(fd)
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = time.mktime(stx_pre['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre['mtime'].timetuple())
+
+ cephfs.futimens(fd, (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-1')
+
+def test_lchmod(testdir):
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+ cephfs.close(fd)
+
+ cephfs.symlink(b'/file-1', b'/file-2')
+
+ stx_pre_t = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_MODE, 0)
+ stx_pre_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_MODE, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ time.sleep(1)
+ cephfs.lchmod(b'/file-2', 0o400)
+
+ stx_post_t = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_MODE, 0)
+ stx_post_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_MODE, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ assert_equal(stx_post_t['mode'], stx_pre_t['mode'])
+ assert_not_equal(stx_post_s['mode'], stx_pre_s['mode'])
+ stx_post_s_perm_bits = stx_post_s['mode'] & ~stat.S_IFMT(stx_post_s["mode"])
+ assert_equal(stx_post_s_perm_bits, 0o400)
+
+ cephfs.unlink(b'/file-2')
+ cephfs.unlink(b'/file-1')
+
+def test_fchmod(testdir):
+ fd = cephfs.open(b'/file-fchmod', 'w', 0o655)
+ st = cephfs.statx(b'/file-fchmod', libcephfs.CEPH_STATX_MODE, 0)
+ mode = st["mode"] | stat.S_IXUSR
+ cephfs.fchmod(fd, mode)
+ st = cephfs.statx(b'/file-fchmod', libcephfs.CEPH_STATX_MODE, 0)
+ assert_equal(st["mode"] & stat.S_IRWXU, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
+ assert_raises(TypeError, cephfs.fchmod, "/file-fchmod", stat.S_IXUSR)
+ assert_raises(TypeError, cephfs.fchmod, fd, "stat.S_IXUSR")
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-fchmod')
+
+def test_fchown(testdir):
+ fd = cephfs.open(b'/file-fchown', 'w', 0o655)
+ uid = os.getuid()
+ gid = os.getgid()
+ assert_raises(TypeError, cephfs.fchown, b'/file-fchown', uid, gid)
+ assert_raises(TypeError, cephfs.fchown, fd, "uid", "gid")
+ cephfs.fchown(fd, uid, gid)
+ st = cephfs.statx(b'/file-fchown', libcephfs.CEPH_STATX_UID | libcephfs.CEPH_STATX_GID, 0)
+ assert_equal(st["uid"], uid)
+ assert_equal(st["gid"], gid)
+ cephfs.fchown(fd, 9999, 9999)
+ st = cephfs.statx(b'/file-fchown', libcephfs.CEPH_STATX_UID | libcephfs.CEPH_STATX_GID, 0)
+ assert_equal(st["uid"], 9999)
+ assert_equal(st["gid"], 9999)
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-fchown')
+
+def test_truncate(testdir):
+ fd = cephfs.open(b'/file-truncate', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ cephfs.truncate(b'/file-truncate', 0)
+ stat = cephfs.fsync(fd, 0)
+ st = cephfs.statx(b'/file-truncate', libcephfs.CEPH_STATX_SIZE, 0)
+ assert_equal(st["size"], 0)
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-truncate')
+
+def test_ftruncate(testdir):
+ fd = cephfs.open(b'/file-ftruncate', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ assert_raises(TypeError, cephfs.ftruncate, b'/file-ftruncate', 0)
+ cephfs.ftruncate(fd, 0)
+ stat = cephfs.fsync(fd, 0)
+ st = cephfs.fstat(fd)
+ assert_equal(st.st_size, 0)
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-ftruncate')
+
+def test_fallocate(testdir):
+ fd = cephfs.open(b'/file-fallocate', 'w', 0o755)
+ assert_raises(TypeError, cephfs.fallocate, b'/file-fallocate', 0, 10)
+ cephfs.fallocate(fd, 0, 10)
+ stat = cephfs.fsync(fd, 0)
+ st = cephfs.fstat(fd)
+ assert_equal(st.st_size, 10)
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-fallocate')
+
+def test_mknod(testdir):
+ mode = stat.S_IFIFO | stat.S_IRUSR | stat.S_IWUSR
+ cephfs.mknod(b'/file-fifo', mode)
+ st = cephfs.statx(b'/file-fifo', libcephfs.CEPH_STATX_MODE, 0)
+ assert_equal(st["mode"] & mode, mode)
+ cephfs.unlink(b'/file-fifo')
+
+def test_lazyio(testdir):
+ fd = cephfs.open(b'/file-lazyio', 'w', 0o755)
+ assert_raises(TypeError, cephfs.lazyio, "fd", 1)
+ assert_raises(TypeError, cephfs.lazyio, fd, "1")
+ cephfs.lazyio(fd, 1)
+ cephfs.write(fd, b"1111", 0)
+ assert_raises(TypeError, cephfs.lazyio_propagate, "fd", 0, 4)
+ assert_raises(TypeError, cephfs.lazyio_propagate, fd, "0", 4)
+ assert_raises(TypeError, cephfs.lazyio_propagate, fd, 0, "4")
+ cephfs.lazyio_propagate(fd, 0, 4)
+ st = cephfs.fstat(fd)
+ assert_equal(st.st_size, 4)
+ cephfs.write(fd, b"2222", 4)
+ assert_raises(TypeError, cephfs.lazyio_synchronize, "fd", 0, 8)
+ assert_raises(TypeError, cephfs.lazyio_synchronize, fd, "0", 8)
+ assert_raises(TypeError, cephfs.lazyio_synchronize, fd, 0, "8")
+ cephfs.lazyio_synchronize(fd, 0, 8)
+ st = cephfs.fstat(fd)
+ assert_equal(st.st_size, 8)
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-lazyio')
+
+def test_replication(testdir):
+ fd = cephfs.open(b'/file-rep', 'w', 0o755)
+ assert_raises(TypeError, cephfs.get_file_replication, "fd")
+ l_dict = cephfs.get_layout(fd)
+ assert('pool_name' in l_dict.keys())
+ cnt = cephfs.get_file_replication(fd)
+ get_rep_cnt_cmd = "ceph osd pool get " + l_dict["pool_name"] + " size"
+ s=os.popen(get_rep_cnt_cmd).read().strip('\n')
+ size=int(s.split(" ")[-1])
+ assert_equal(cnt, size)
+ cnt = cephfs.get_path_replication(b'/file-rep')
+ assert_equal(cnt, size)
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-rep')
+
+def test_caps(testdir):
+ fd = cephfs.open(b'/file-caps', 'w', 0o755)
+ timeout = cephfs.get_cap_return_timeout()
+ assert_equal(timeout, 300)
+ fd_caps = cephfs.debug_get_fd_caps(fd)
+ file_caps = cephfs.debug_get_file_caps(b'/file-caps')
+ assert_equal(fd_caps, file_caps)
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-caps')
+
+def test_setuuid(testdir):
+ ses_id_uid = uuid.uuid1()
+ ses_id_str = str(ses_id_uid)
+ cephfs.set_uuid(ses_id_str)
+
+def test_session_timeout(testdir):
+ assert_raises(TypeError, cephfs.set_session_timeout, "300")
+ cephfs.set_session_timeout(300)
+
+def test_readdirops(testdir):
+ cephfs.chdir(b"/")
+ dirs = [b"dir-1", b"dir-2", b"dir-3"]
+ for i in dirs:
+ cephfs.mkdir(i, 0o755)
+ handler = cephfs.opendir(b"/")
+ d1 = cephfs.readdir(handler)
+ d2 = cephfs.readdir(handler)
+ d3 = cephfs.readdir(handler)
+ offset_d4 = cephfs.telldir(handler)
+ d4 = cephfs.readdir(handler)
+ cephfs.rewinddir(handler)
+ d = cephfs.readdir(handler)
+ assert_equal(d.d_name, d1.d_name)
+ cephfs.seekdir(handler, offset_d4)
+ d = cephfs.readdir(handler)
+ assert_equal(d.d_name, d4.d_name)
+ dirs += [b".", b".."]
+ cephfs.rewinddir(handler)
+ d = cephfs.readdir(handler)
+ while d:
+ assert(d.d_name in dirs)
+ dirs.remove(d.d_name)
+ d = cephfs.readdir(handler)
+ assert(len(dirs) == 0)
+ dirs = [b"/dir-1", b"/dir-2", b"/dir-3"]
+ for i in dirs:
+ cephfs.rmdir(i)
+ cephfs.closedir(handler)
+
+def test_preadv_pwritev():
+ fd = cephfs.open(b'file-1', 'w', 0o755)
+ cephfs.pwritev(fd, [b"asdf", b"zxcvb"], 0)
+ cephfs.close(fd)
+ fd = cephfs.open(b'file-1', 'r', 0o755)
+ buf = [bytearray(i) for i in [4, 5]]
+ cephfs.preadv(fd, buf, 0)
+ assert_equal([b"asdf", b"zxcvb"], list(buf))
+ cephfs.close(fd)
+ cephfs.unlink(b'file-1')
+
+def test_setattrx(testdir):
+ fd = cephfs.open(b'file-setattrx', 'w', 0o655)
+ cephfs.write(fd, b"1111", 0)
+ cephfs.close(fd)
+ st = cephfs.statx(b'file-setattrx', libcephfs.CEPH_STATX_MODE, 0)
+ mode = st["mode"] | stat.S_IXUSR
+ assert_raises(TypeError, cephfs.setattrx, b'file-setattrx', "dict", 0, 0)
+
+ time.sleep(1)
+ statx_dict = dict()
+ statx_dict["mode"] = mode
+ statx_dict["uid"] = 9999
+ statx_dict["gid"] = 9999
+ dt = datetime.now()
+ statx_dict["mtime"] = dt
+ statx_dict["atime"] = dt
+ statx_dict["ctime"] = dt
+ statx_dict["size"] = 10
+ statx_dict["btime"] = dt
+ cephfs.setattrx(b'file-setattrx', statx_dict, libcephfs.CEPH_SETATTR_MODE | libcephfs.CEPH_SETATTR_UID |
+ libcephfs.CEPH_SETATTR_GID | libcephfs.CEPH_SETATTR_MTIME |
+ libcephfs.CEPH_SETATTR_ATIME | libcephfs.CEPH_SETATTR_CTIME |
+ libcephfs.CEPH_SETATTR_SIZE | libcephfs.CEPH_SETATTR_BTIME, 0)
+ st1 = cephfs.statx(b'file-setattrx', libcephfs.CEPH_STATX_MODE | libcephfs.CEPH_STATX_UID |
+ libcephfs.CEPH_STATX_GID | libcephfs.CEPH_STATX_MTIME |
+ libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_CTIME |
+ libcephfs.CEPH_STATX_SIZE | libcephfs.CEPH_STATX_BTIME, 0)
+ assert_equal(mode, st1["mode"])
+ assert_equal(9999, st1["uid"])
+ assert_equal(9999, st1["gid"])
+ assert_equal(int(dt.timestamp()), int(st1["mtime"].timestamp()))
+ assert_equal(int(dt.timestamp()), int(st1["atime"].timestamp()))
+ assert_equal(int(dt.timestamp()), int(st1["ctime"].timestamp()))
+ assert_equal(int(dt.timestamp()), int(st1["btime"].timestamp()))
+ assert_equal(10, st1["size"])
+ cephfs.unlink(b'file-setattrx')
+
+def test_fsetattrx(testdir):
+ fd = cephfs.open(b'file-fsetattrx', 'w', 0o655)
+ cephfs.write(fd, b"1111", 0)
+ st = cephfs.statx(b'file-fsetattrx', libcephfs.CEPH_STATX_MODE, 0)
+ mode = st["mode"] | stat.S_IXUSR
+ assert_raises(TypeError, cephfs.fsetattrx, fd, "dict", 0, 0)
+
+ time.sleep(1)
+ statx_dict = dict()
+ statx_dict["mode"] = mode
+ statx_dict["uid"] = 9999
+ statx_dict["gid"] = 9999
+ dt = datetime.now()
+ statx_dict["mtime"] = dt
+ statx_dict["atime"] = dt
+ statx_dict["ctime"] = dt
+ statx_dict["size"] = 10
+ statx_dict["btime"] = dt
+ cephfs.fsetattrx(fd, statx_dict, libcephfs.CEPH_SETATTR_MODE | libcephfs.CEPH_SETATTR_UID |
+ libcephfs.CEPH_SETATTR_GID | libcephfs.CEPH_SETATTR_MTIME |
+ libcephfs.CEPH_SETATTR_ATIME | libcephfs.CEPH_SETATTR_CTIME |
+ libcephfs.CEPH_SETATTR_SIZE | libcephfs.CEPH_SETATTR_BTIME)
+ st1 = cephfs.statx(b'file-fsetattrx', libcephfs.CEPH_STATX_MODE | libcephfs.CEPH_STATX_UID |
+ libcephfs.CEPH_STATX_GID | libcephfs.CEPH_STATX_MTIME |
+ libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_CTIME |
+ libcephfs.CEPH_STATX_SIZE | libcephfs.CEPH_STATX_BTIME, 0)
+ assert_equal(mode, st1["mode"])
+ assert_equal(9999, st1["uid"])
+ assert_equal(9999, st1["gid"])
+ assert_equal(int(dt.timestamp()), int(st1["mtime"].timestamp()))
+ assert_equal(int(dt.timestamp()), int(st1["atime"].timestamp()))
+ assert_equal(int(dt.timestamp()), int(st1["ctime"].timestamp()))
+ assert_equal(int(dt.timestamp()), int(st1["btime"].timestamp()))
+ assert_equal(10, st1["size"])
+ cephfs.close(fd)
+ cephfs.unlink(b'file-fsetattrx')
+
+def test_get_layout(testdir):
+ fd = cephfs.open(b'file-get-layout', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ assert_raises(TypeError, cephfs.get_layout, "fd")
+ l_dict = cephfs.get_layout(fd)
+ assert('stripe_unit' in l_dict.keys())
+ assert('stripe_count' in l_dict.keys())
+ assert('object_size' in l_dict.keys())
+ assert('pool_id' in l_dict.keys())
+ assert('pool_name' in l_dict.keys())
+
+ cephfs.close(fd)
+ cephfs.unlink(b'file-get-layout')
+
+def test_get_default_pool(testdir):
+ dp_dict = cephfs.get_default_pool()
+ assert('pool_id' in dp_dict.keys())
+ assert('pool_name' in dp_dict.keys())
+
+def test_get_pool(testdir):
+ dp_dict = cephfs.get_default_pool()
+ assert('pool_id' in dp_dict.keys())
+ assert('pool_name' in dp_dict.keys())
+ assert_equal(cephfs.get_pool_id(dp_dict["pool_name"]), dp_dict["pool_id"])
+ get_rep_cnt_cmd = "ceph osd pool get " + dp_dict["pool_name"] + " size"
+ s=os.popen(get_rep_cnt_cmd).read().strip('\n')
+ size=int(s.split(" ")[-1])
+ assert_equal(cephfs.get_pool_replication(dp_dict["pool_id"]), size)
+
+def test_disk_quota_exceeeded_error(testdir):
+ cephfs.mkdir("/dir-1", 0o755)
+ cephfs.setxattr("/dir-1", "ceph.quota.max_bytes", b"5", 0)
+ fd = cephfs.open(b'/dir-1/file-1', 'w', 0o755)
+ assert_raises(libcephfs.DiskQuotaExceeded, cephfs.write, fd, b"abcdeghiklmnopqrstuvwxyz", 0)
+ cephfs.close(fd)
+ cephfs.unlink(b"/dir-1/file-1")
+
+def test_empty_snapshot_info(testdir):
+ cephfs.mkdir("/dir-1", 0o755)
+
+ # snap without metadata
+ cephfs.mkdir("/dir-1/.snap/snap0", 0o755)
+ snap_info = cephfs.snap_info("/dir-1/.snap/snap0")
+ assert_equal(snap_info["metadata"], {})
+ assert_greater(snap_info["id"], 0)
+ cephfs.rmdir("/dir-1/.snap/snap0")
+
+ # remove directory
+ cephfs.rmdir("/dir-1")
+
+def test_snapshot_info(testdir):
+ cephfs.mkdir("/dir-1", 0o755)
+
+ # snap with custom metadata
+ md = {"foo": "bar", "zig": "zag", "abcdefg": "12345"}
+ cephfs.mksnap("/dir-1", "snap0", 0o755, metadata=md)
+ snap_info = cephfs.snap_info("/dir-1/.snap/snap0")
+ assert_equal(snap_info["metadata"]["foo"], md["foo"])
+ assert_equal(snap_info["metadata"]["zig"], md["zig"])
+ assert_equal(snap_info["metadata"]["abcdefg"], md["abcdefg"])
+ assert_greater(snap_info["id"], 0)
+ cephfs.rmsnap("/dir-1", "snap0")
+
+ # remove directory
+ cephfs.rmdir("/dir-1")
+
+def test_set_mount_timeout_post_mount(testdir):
+ assert_raises(libcephfs.LibCephFSStateError, cephfs.set_mount_timeout, 5)
+
+def test_set_mount_timeout(testdir):
+ cephfs.unmount()
+ cephfs.set_mount_timeout(5)
+ cephfs.mount()
+
+def test_set_mount_timeout_lt0(testdir):
+ cephfs.unmount()
+ assert_raises(libcephfs.InvalidValue, cephfs.set_mount_timeout, -5)
+ cephfs.mount()
+
+def test_snapdiff(testdir):
+ cephfs.mkdir("/snapdiff_test", 0o755)
+ fd = cephfs.open('/snapdiff_test/file-1', 'w', 0o755)
+ cephfs.write(fd, b"1111", 0)
+ cephfs.close(fd)
+ fd = cephfs.open('/snapdiff_test/file-2', 'w', 0o755)
+ cephfs.write(fd, b"2222", 0)
+ cephfs.close(fd)
+ cephfs.mksnap("/snapdiff_test", "snap1", 0o755)
+ fd = cephfs.open('/snapdiff_test/file-1', 'w', 0o755)
+ cephfs.write(fd, b"1222", 0)
+ cephfs.close(fd)
+ cephfs.unlink('/snapdiff_test/file-2')
+ cephfs.mksnap("/snapdiff_test", "snap2", 0o755)
+ snap1id = cephfs.snap_info(b"/snapdiff_test/.snap/snap1")['id']
+ snap2id = cephfs.snap_info(b"/snapdiff_test/.snap/snap2")['id']
+ diff = cephfs.opensnapdiff(b"/snapdiff_test", b"/", b"snap2", b"snap1")
+ cnt = 0
+ e = diff.readdir()
+ while e is not None:
+ if (e.d_name == b"file-1"):
+ cnt = cnt + 1
+ assert_equal(snap2id, e.d_snapid)
+ elif (e.d_name == b"file-2"):
+ cnt = cnt + 1
+ assert_equal(snap1id, e.d_snapid)
+ elif (e.d_name != b"." and e.d_name != b".."):
+ cnt = cnt + 1
+ e = diff.readdir()
+ assert_equal(cnt, 2)
+ diff.close()
+
+ # remove directory
+ purge_dir(b"/snapdiff_test");
diff --git a/src/test/pybind/test_rados.py b/src/test/pybind/test_rados.py
new file mode 100644
index 000000000..236b2f1d5
--- /dev/null
+++ b/src/test/pybind/test_rados.py
@@ -0,0 +1,1543 @@
+from __future__ import print_function
+from assertions import assert_equal as eq, assert_raises
+from rados import (Rados, Error, RadosStateError, Object, ObjectExists,
+ ObjectNotFound, ObjectBusy, NotConnected,
+ LIBRADOS_ALL_NSPACES, WriteOpCtx, ReadOpCtx, LIBRADOS_CREATE_EXCLUSIVE,
+ LIBRADOS_CMPXATTR_OP_EQ, LIBRADOS_CMPXATTR_OP_GT, LIBRADOS_CMPXATTR_OP_LT, OSError,
+ LIBRADOS_SNAP_HEAD, LIBRADOS_OPERATION_BALANCE_READS, LIBRADOS_OPERATION_SKIPRWLOCKS, MonitorLog, MAX_ERRNO, NoData, ExtendMismatch)
+from datetime import timedelta
+import time
+import threading
+import json
+import errno
+import os
+import pytest
+import re
+import sys
+
+def test_rados_init_error():
+ assert_raises(Error, Rados, conffile='', rados_id='admin',
+ name='client.admin')
+ assert_raises(Error, Rados, conffile='', name='invalid')
+ assert_raises(Error, Rados, conffile='', name='bad.invalid')
+
+def test_rados_init():
+ with Rados(conffile='', rados_id='admin'):
+ pass
+ with Rados(conffile='', name='client.admin'):
+ pass
+ with Rados(conffile='', name='client.admin'):
+ pass
+ with Rados(conffile='', name='client.admin'):
+ pass
+
+def test_ioctx_context_manager():
+ with Rados(conffile='', rados_id='admin') as conn:
+ with conn.open_ioctx('rbd') as ioctx:
+ pass
+
+def test_parse_argv():
+ args = ['osd', 'pool', 'delete', 'foobar', 'foobar', '--yes-i-really-really-mean-it']
+ r = Rados()
+ eq(args, r.conf_parse_argv(args))
+
+def test_parse_argv_empty_str():
+ args = ['']
+ r = Rados()
+ eq(args, r.conf_parse_argv(args))
+
+class TestRadosStateError(object):
+ def _requires_configuring(self, rados):
+ assert_raises(RadosStateError, rados.connect)
+
+ def _requires_configuring_or_connected(self, rados):
+ assert_raises(RadosStateError, rados.conf_read_file)
+ assert_raises(RadosStateError, rados.conf_parse_argv, None)
+ assert_raises(RadosStateError, rados.conf_parse_env)
+ assert_raises(RadosStateError, rados.conf_get, 'opt')
+ assert_raises(RadosStateError, rados.conf_set, 'opt', 'val')
+ assert_raises(RadosStateError, rados.ping_monitor, '0')
+
+ def _requires_connected(self, rados):
+ assert_raises(RadosStateError, rados.pool_exists, 'foo')
+ assert_raises(RadosStateError, rados.pool_lookup, 'foo')
+ assert_raises(RadosStateError, rados.pool_reverse_lookup, 0)
+ assert_raises(RadosStateError, rados.create_pool, 'foo')
+ assert_raises(RadosStateError, rados.get_pool_base_tier, 0)
+ assert_raises(RadosStateError, rados.delete_pool, 'foo')
+ assert_raises(RadosStateError, rados.list_pools)
+ assert_raises(RadosStateError, rados.get_fsid)
+ assert_raises(RadosStateError, rados.open_ioctx, 'foo')
+ assert_raises(RadosStateError, rados.mon_command, '', b'')
+ assert_raises(RadosStateError, rados.osd_command, 0, '', b'')
+ assert_raises(RadosStateError, rados.pg_command, '', '', b'')
+ assert_raises(RadosStateError, rados.wait_for_latest_osdmap)
+ assert_raises(RadosStateError, rados.blocklist_add, '127.0.0.1/123', 0)
+
+ def test_configuring(self):
+ rados = Rados(conffile='')
+ eq('configuring', rados.state)
+ self._requires_connected(rados)
+
+ def test_connected(self):
+ rados = Rados(conffile='')
+ with rados:
+ eq('connected', rados.state)
+ self._requires_configuring(rados)
+
+ def test_shutdown(self):
+ rados = Rados(conffile='')
+ with rados:
+ pass
+ eq('shutdown', rados.state)
+ self._requires_configuring(rados)
+ self._requires_configuring_or_connected(rados)
+ self._requires_connected(rados)
+
+
+class TestRados(object):
+
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.conf_parse_env('FOO_DOES_NOT_EXIST_BLAHBLAH')
+ self.rados.conf_parse_env()
+ self.rados.connect()
+
+ # Assume any pre-existing pools are the cluster's defaults
+ self.default_pools = self.rados.list_pools()
+
+ def teardown_method(self, method):
+ self.rados.shutdown()
+
+ def test_ping_monitor(self):
+ assert_raises(ObjectNotFound, self.rados.ping_monitor, 'not_exists_monitor')
+ cmd = {'prefix': 'mon dump', 'format':'json'}
+ ret, buf, out = self.rados.mon_command(json.dumps(cmd), b'')
+ for mon in json.loads(buf.decode('utf8'))['mons']:
+ while True:
+ output = self.rados.ping_monitor(mon['name'])
+ if output is None:
+ continue
+ buf = json.loads(output)
+ if buf.get('health'):
+ break
+
+ def test_annotations(self):
+ with pytest.raises(TypeError):
+ self.rados.create_pool(0xf00)
+
+ def test_create(self):
+ self.rados.create_pool('foo')
+ self.rados.delete_pool('foo')
+
+ def test_create_utf8(self):
+ poolname = "\u9ec4"
+ self.rados.create_pool(poolname)
+ assert self.rados.pool_exists(u"\u9ec4")
+ self.rados.delete_pool(poolname)
+
+ def test_pool_lookup_utf8(self):
+ poolname = '\u9ec4'
+ self.rados.create_pool(poolname)
+ try:
+ poolid = self.rados.pool_lookup(poolname)
+ eq(poolname, self.rados.pool_reverse_lookup(poolid))
+ finally:
+ self.rados.delete_pool(poolname)
+
+ def test_eexist(self):
+ self.rados.create_pool('foo')
+ assert_raises(ObjectExists, self.rados.create_pool, 'foo')
+ self.rados.delete_pool('foo')
+
+ def list_non_default_pools(self):
+ pools = self.rados.list_pools()
+ for p in self.default_pools:
+ pools.remove(p)
+ return set(pools)
+
+ def test_list_pools(self):
+ eq(set(), self.list_non_default_pools())
+ self.rados.create_pool('foo')
+ eq(set(['foo']), self.list_non_default_pools())
+ self.rados.create_pool('bar')
+ eq(set(['foo', 'bar']), self.list_non_default_pools())
+ self.rados.create_pool('baz')
+ eq(set(['foo', 'bar', 'baz']), self.list_non_default_pools())
+ self.rados.delete_pool('foo')
+ eq(set(['bar', 'baz']), self.list_non_default_pools())
+ self.rados.delete_pool('baz')
+ eq(set(['bar']), self.list_non_default_pools())
+ self.rados.delete_pool('bar')
+ eq(set(), self.list_non_default_pools())
+ self.rados.create_pool('a' * 500)
+ eq(set(['a' * 500]), self.list_non_default_pools())
+ self.rados.delete_pool('a' * 500)
+
+ @pytest.mark.tier
+ def test_get_pool_base_tier(self):
+ self.rados.create_pool('foo')
+ try:
+ self.rados.create_pool('foo-cache')
+ try:
+ pool_id = self.rados.pool_lookup('foo')
+ tier_pool_id = self.rados.pool_lookup('foo-cache')
+
+ cmd = {"prefix":"osd tier add", "pool":"foo", "tierpool":"foo-cache", "force_nonempty":""}
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ eq(ret, 0)
+
+ try:
+ cmd = {"prefix":"osd tier cache-mode", "pool":"foo-cache", "tierpool":"foo-cache", "mode":"readonly", "yes_i_really_mean_it": True}
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ eq(ret, 0)
+
+ eq(self.rados.wait_for_latest_osdmap(), 0)
+
+ eq(pool_id, self.rados.get_pool_base_tier(pool_id))
+ eq(pool_id, self.rados.get_pool_base_tier(tier_pool_id))
+ finally:
+ cmd = {"prefix":"osd tier remove", "pool":"foo", "tierpool":"foo-cache"}
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ eq(ret, 0)
+ finally:
+ self.rados.delete_pool('foo-cache')
+ finally:
+ self.rados.delete_pool('foo')
+
+ def test_get_fsid(self):
+ fsid = self.rados.get_fsid()
+ assert re.match('[0-9a-f\-]{36}', fsid, re.I)
+
+ def test_blocklist_add(self):
+ self.rados.blocklist_add("1.2.3.4/123", 1)
+
+ @pytest.mark.stats
+ def test_get_cluster_stats(self):
+ stats = self.rados.get_cluster_stats()
+ assert stats['kb'] > 0
+ assert stats['kb_avail'] > 0
+ assert stats['kb_used'] > 0
+ assert stats['num_objects'] >= 0
+
+ def test_monitor_log(self):
+ lock = threading.Condition()
+ def cb(arg, line, who, sec, nsec, seq, level, msg):
+ # NOTE(sileht): the old pyrados API was received the pointer as int
+ # instead of the value of arg
+ eq(arg, "arg")
+ with lock:
+ lock.notify()
+ return 0
+
+ # NOTE(sileht): force don't save the monitor into local var
+ # to ensure all references are correctly tracked into the lib
+ MonitorLog(self.rados, "debug", cb, "arg")
+ with lock:
+ lock.wait()
+ MonitorLog(self.rados, "debug", None, None)
+ eq(None, self.rados.monitor_callback)
+
+class TestIoctx(object):
+
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.connect()
+ self.rados.create_pool('test_pool')
+ assert self.rados.pool_exists('test_pool')
+ self.ioctx = self.rados.open_ioctx('test_pool')
+
+ def teardown_method(self, method):
+ cmd = {"prefix":"osd unset", "key":"noup"}
+ self.rados.mon_command(json.dumps(cmd), b'')
+ self.ioctx.close()
+ self.rados.delete_pool('test_pool')
+ self.rados.shutdown()
+
+ def test_get_last_version(self):
+ version = self.ioctx.get_last_version()
+ assert version >= 0
+
+ def test_get_stats(self):
+ stats = self.ioctx.get_stats()
+ eq(stats, {'num_objects_unfound': 0,
+ 'num_objects_missing_on_primary': 0,
+ 'num_object_clones': 0,
+ 'num_objects': 0,
+ 'num_object_copies': 0,
+ 'num_bytes': 0,
+ 'num_rd_kb': 0,
+ 'num_wr_kb': 0,
+ 'num_kb': 0,
+ 'num_wr': 0,
+ 'num_objects_degraded': 0,
+ 'num_rd': 0})
+
+ def test_write(self):
+ self.ioctx.write('abc', b'abc')
+ eq(self.ioctx.read('abc'), b'abc')
+
+ def test_write_full(self):
+ self.ioctx.write('abc', b'abc')
+ eq(self.ioctx.read('abc'), b'abc')
+ self.ioctx.write_full('abc', b'd')
+ eq(self.ioctx.read('abc'), b'd')
+
+ def test_writesame(self):
+ self.ioctx.writesame('ob', b'rzx', 9)
+ eq(self.ioctx.read('ob'), b'rzxrzxrzx')
+
+ def test_append(self):
+ self.ioctx.write('abc', b'a')
+ self.ioctx.append('abc', b'b')
+ self.ioctx.append('abc', b'c')
+ eq(self.ioctx.read('abc'), b'abc')
+
+ def test_write_zeros(self):
+ self.ioctx.write('abc', b'a\0b\0c')
+ eq(self.ioctx.read('abc'), b'a\0b\0c')
+
+ def test_trunc(self):
+ self.ioctx.write('abc', b'abc')
+ self.ioctx.trunc('abc', 2)
+ eq(self.ioctx.read('abc'), b'ab')
+ size = self.ioctx.stat('abc')[0]
+ eq(size, 2)
+
+ def test_cmpext(self):
+ self.ioctx.write('test_object', b'abcdefghi')
+ eq(0, self.ioctx.cmpext('test_object', b'abcdefghi', 0))
+ eq(-MAX_ERRNO - 4, self.ioctx.cmpext('test_object', b'abcdxxxxx', 0))
+
+ def test_list_objects_empty(self):
+ eq(list(self.ioctx.list_objects()), [])
+
+ def test_list_objects(self):
+ self.ioctx.write('a', b'')
+ self.ioctx.write('b', b'foo')
+ self.ioctx.write_full('c', b'bar')
+ self.ioctx.append('d', b'jazz')
+ object_names = [obj.key for obj in self.ioctx.list_objects()]
+ eq(sorted(object_names), ['a', 'b', 'c', 'd'])
+
+ def test_list_ns_objects(self):
+ self.ioctx.write('a', b'')
+ self.ioctx.write('b', b'foo')
+ self.ioctx.write_full('c', b'bar')
+ self.ioctx.append('d', b'jazz')
+ self.ioctx.set_namespace("ns1")
+ self.ioctx.write('ns1-a', b'')
+ self.ioctx.write('ns1-b', b'foo')
+ self.ioctx.write_full('ns1-c', b'bar')
+ self.ioctx.append('ns1-d', b'jazz')
+ self.ioctx.append('d', b'jazz')
+ self.ioctx.set_namespace(LIBRADOS_ALL_NSPACES)
+ object_names = [(obj.nspace, obj.key) for obj in self.ioctx.list_objects()]
+ eq(sorted(object_names), [('', 'a'), ('','b'), ('','c'), ('','d'),\
+ ('ns1', 'd'), ('ns1', 'ns1-a'), ('ns1', 'ns1-b'),\
+ ('ns1', 'ns1-c'), ('ns1', 'ns1-d')])
+
+ def test_xattrs(self):
+ xattrs = dict(a=b'1', b=b'2', c=b'3', d=b'a\0b', e=b'\0', f=b'')
+ self.ioctx.write('abc', b'')
+ for key, value in xattrs.items():
+ self.ioctx.set_xattr('abc', key, value)
+ eq(self.ioctx.get_xattr('abc', key), value)
+ stored_xattrs = {}
+ for key, value in self.ioctx.get_xattrs('abc'):
+ stored_xattrs[key] = value
+ eq(stored_xattrs, xattrs)
+
+ def test_obj_xattrs(self):
+ xattrs = dict(a=b'1', b=b'2', c=b'3', d=b'a\0b', e=b'\0', f=b'')
+ self.ioctx.write('abc', b'')
+ obj = list(self.ioctx.list_objects())[0]
+ for key, value in xattrs.items():
+ obj.set_xattr(key, value)
+ eq(obj.get_xattr(key), value)
+ stored_xattrs = {}
+ for key, value in obj.get_xattrs():
+ stored_xattrs[key] = value
+ eq(stored_xattrs, xattrs)
+
+ def test_get_pool_id(self):
+ eq(self.ioctx.get_pool_id(), self.rados.pool_lookup('test_pool'))
+
+ def test_get_pool_name(self):
+ eq(self.ioctx.get_pool_name(), 'test_pool')
+
+ def test_create_snap(self):
+ assert_raises(ObjectNotFound, self.ioctx.remove_snap, 'foo')
+ self.ioctx.create_snap('foo')
+ self.ioctx.remove_snap('foo')
+
+ def test_list_snaps_empty(self):
+ eq(list(self.ioctx.list_snaps()), [])
+
+ def test_list_snaps(self):
+ snaps = ['snap1', 'snap2', 'snap3']
+ for snap in snaps:
+ self.ioctx.create_snap(snap)
+ listed_snaps = [snap.name for snap in self.ioctx.list_snaps()]
+ eq(snaps, listed_snaps)
+
+ def test_lookup_snap(self):
+ self.ioctx.create_snap('foo')
+ snap = self.ioctx.lookup_snap('foo')
+ eq(snap.name, 'foo')
+
+ def test_snap_timestamp(self):
+ self.ioctx.create_snap('foo')
+ snap = self.ioctx.lookup_snap('foo')
+ snap.get_timestamp()
+
+ def test_remove_snap(self):
+ self.ioctx.create_snap('foo')
+ (snap,) = self.ioctx.list_snaps()
+ eq(snap.name, 'foo')
+ self.ioctx.remove_snap('foo')
+ eq(list(self.ioctx.list_snaps()), [])
+
+ @pytest.mark.rollback
+ def test_snap_rollback(self):
+ self.ioctx.write("insnap", b"contents1")
+ self.ioctx.create_snap("snap1")
+ self.ioctx.remove_object("insnap")
+ self.ioctx.snap_rollback("insnap", "snap1")
+ eq(self.ioctx.read("insnap"), b"contents1")
+ self.ioctx.remove_snap("snap1")
+ self.ioctx.remove_object("insnap")
+
+ @pytest.mark.rollback
+ def test_snap_rollback_removed(self):
+ self.ioctx.write("insnap", b"contents1")
+ self.ioctx.create_snap("snap1")
+ self.ioctx.write("insnap", b"contents2")
+ self.ioctx.snap_rollback("insnap", "snap1")
+ eq(self.ioctx.read("insnap"), b"contents1")
+ self.ioctx.remove_snap("snap1")
+ self.ioctx.remove_object("insnap")
+
+ def test_snap_read(self):
+ self.ioctx.write("insnap", b"contents1")
+ self.ioctx.create_snap("snap1")
+ self.ioctx.remove_object("insnap")
+ snap = self.ioctx.lookup_snap("snap1")
+ self.ioctx.set_read(snap.snap_id)
+ eq(self.ioctx.read("insnap"), b"contents1")
+ self.ioctx.set_read(LIBRADOS_SNAP_HEAD)
+ self.ioctx.write("inhead", b"contents2")
+ eq(self.ioctx.read("inhead"), b"contents2")
+ self.ioctx.remove_snap("snap1")
+ self.ioctx.remove_object("inhead")
+
+ def test_set_omap(self):
+ keys = ("1", "2", "3", "4", b"\xff")
+ values = (b"aaa", b"bbb", b"ccc", b"\x04\x04\x04\x04", b"5")
+ with WriteOpCtx() as write_op:
+ self.ioctx.set_omap(write_op, keys, values)
+ write_op.set_flags(LIBRADOS_OPERATION_SKIPRWLOCKS)
+ self.ioctx.operate_write_op(write_op, "hw")
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals(read_op, "", "", 5, omap_key_type=bytes)
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, "hw")
+ next(iter)
+ eq(list(iter), [(b"2", b"bbb"), (b"3", b"ccc"), (b"4", b"\x04\x04\x04\x04"), (b"\xff", b"5")])
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals(read_op, b"2", "", 4, omap_key_type=bytes)
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, "hw")
+ eq((b"3", b"ccc"), next(iter))
+ eq(list(iter), [(b"4", b"\x04\x04\x04\x04"), (b"\xff", b"5")])
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals(read_op, "", "2", 4, omap_key_type=bytes)
+ eq(ret, 0)
+ read_op.set_flags(LIBRADOS_OPERATION_BALANCE_READS)
+ self.ioctx.operate_read_op(read_op, "hw")
+ eq(list(iter), [(b"2", b"bbb")])
+
+ def test_set_omap_aio(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+
+ keys = ("1", "2", "3", "4")
+ values = (b"aaa", b"bbb", b"ccc", b"\x04\x04\x04\x04")
+ with WriteOpCtx() as write_op:
+ self.ioctx.set_omap(write_op, keys, values)
+ comp = self.ioctx.operate_aio_write_op(write_op, "hw", cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals(read_op, "", "", 4)
+ eq(ret, 0)
+ comp = self.ioctx.operate_aio_read_op(read_op, "hw", cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 4:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ next(iter)
+ eq(list(iter), [("2", b"bbb"), ("3", b"ccc"), ("4", b"\x04\x04\x04\x04")])
+
+ def test_write_ops(self):
+ with WriteOpCtx() as write_op:
+ write_op.new(0)
+ self.ioctx.operate_write_op(write_op, "write_ops")
+ eq(self.ioctx.read('write_ops'), b'')
+
+ write_op.write_full(b'1')
+ write_op.append(b'2')
+ self.ioctx.operate_write_op(write_op, "write_ops")
+ eq(self.ioctx.read('write_ops'), b'12')
+
+ write_op.write_full(b'12345')
+ write_op.write(b'x', 2)
+ self.ioctx.operate_write_op(write_op, "write_ops")
+ eq(self.ioctx.read('write_ops'), b'12x45')
+
+ write_op.write_full(b'12345')
+ write_op.zero(2, 2)
+ self.ioctx.operate_write_op(write_op, "write_ops")
+ eq(self.ioctx.read('write_ops'), b'12\x00\x005')
+
+ write_op.write_full(b'12345')
+ write_op.truncate(2)
+ self.ioctx.operate_write_op(write_op, "write_ops")
+ eq(self.ioctx.read('write_ops'), b'12')
+
+ write_op.remove()
+ self.ioctx.operate_write_op(write_op, "write_ops")
+ with pytest.raises(ObjectNotFound):
+ self.ioctx.read('write_ops')
+
+ def test_execute_op(self):
+ with WriteOpCtx() as write_op:
+ write_op.execute("hello", "record_hello", b"ebs")
+ self.ioctx.operate_write_op(write_op, "object")
+ eq(self.ioctx.read('object'), b"Hello, ebs!")
+
+ def test_writesame_op(self):
+ with WriteOpCtx() as write_op:
+ write_op.writesame(b'rzx', 9)
+ self.ioctx.operate_write_op(write_op, 'abc')
+ eq(self.ioctx.read('abc'), b'rzxrzxrzx')
+
+ def test_get_omap_vals_by_keys(self):
+ keys = ("1", "2", "3", "4", b"\xff")
+ values = (b"aaa", b"bbb", b"ccc", b"\x04\x04\x04\x04", b"5")
+ with WriteOpCtx() as write_op:
+ self.ioctx.set_omap(write_op, keys, values)
+ self.ioctx.operate_write_op(write_op, "hw")
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op,("3","4",b"\xff"), omap_key_type=bytes)
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, "hw")
+ eq(list(iter), [(b"3", b"ccc"), (b"4", b"\x04\x04\x04\x04"), (b"\xff", b"5")])
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op,("3","4",), omap_key_type=bytes)
+ eq(ret, 0)
+ with pytest.raises(ObjectNotFound):
+ self.ioctx.operate_read_op(read_op, "no_such")
+
+ def test_get_omap_keys(self):
+ keys = ("1", "2", "3")
+ values = (b"aaa", b"bbb", b"ccc")
+ with WriteOpCtx() as write_op:
+ self.ioctx.set_omap(write_op, keys, values)
+ self.ioctx.operate_write_op(write_op, "hw")
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_keys(read_op,"",2)
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, "hw")
+ eq(list(iter), [("1", None), ("2", None)])
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_keys(read_op,"",2)
+ eq(ret, 0)
+ with pytest.raises(ObjectNotFound):
+ self.ioctx.operate_read_op(read_op, "no_such")
+
+ def test_clear_omap(self):
+ keys = ("1", "2", "3")
+ values = (b"aaa", b"bbb", b"ccc")
+ with WriteOpCtx() as write_op:
+ self.ioctx.set_omap(write_op, keys, values)
+ self.ioctx.operate_write_op(write_op, "hw")
+ with WriteOpCtx() as write_op_1:
+ self.ioctx.clear_omap(write_op_1)
+ self.ioctx.operate_write_op(write_op_1, "hw")
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op,("1",))
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, "hw")
+ eq(list(iter), [])
+
+ def test_remove_omap_range2(self):
+ keys = ("1", "2", "3", "4")
+ values = (b"a", b"bb", b"ccc", b"dddd")
+ with WriteOpCtx() as write_op:
+ self.ioctx.set_omap(write_op, keys, values)
+ self.ioctx.operate_write_op(write_op, "test_obj")
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, keys)
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, "test_obj")
+ eq(list(iter), list(zip(keys, values)))
+ with WriteOpCtx() as write_op:
+ self.ioctx.remove_omap_range2(write_op, "1", "4")
+ self.ioctx.operate_write_op(write_op, "test_obj")
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, keys)
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, "test_obj")
+ eq(list(iter), [("4", b"dddd")])
+
+ def test_omap_cmp(self):
+ object_id = 'test'
+ self.ioctx.write(object_id, b'omap_cmp')
+ with WriteOpCtx() as write_op:
+ self.ioctx.set_omap(write_op, ('key1',), ('1',))
+ self.ioctx.operate_write_op(write_op, object_id)
+ with WriteOpCtx() as write_op:
+ write_op.omap_cmp('key1', '1', LIBRADOS_CMPXATTR_OP_EQ)
+ self.ioctx.set_omap(write_op, ('key1',), ('2',))
+ self.ioctx.operate_write_op(write_op, object_id)
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, ('key1',))
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, object_id)
+ eq(list(iter), [('key1', b'2')])
+ with WriteOpCtx() as write_op:
+ write_op.omap_cmp('key1', '1', LIBRADOS_CMPXATTR_OP_GT)
+ self.ioctx.set_omap(write_op, ('key1',), ('3',))
+ self.ioctx.operate_write_op(write_op, object_id)
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, ('key1',))
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, object_id)
+ eq(list(iter), [('key1', b'3')])
+ with WriteOpCtx() as write_op:
+ write_op.omap_cmp('key1', '4', LIBRADOS_CMPXATTR_OP_LT)
+ self.ioctx.set_omap(write_op, ('key1',), ('4',))
+ self.ioctx.operate_write_op(write_op, object_id)
+ with ReadOpCtx() as read_op:
+ iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, ('key1',))
+ eq(ret, 0)
+ self.ioctx.operate_read_op(read_op, object_id)
+ eq(list(iter), [('key1', b'4')])
+ with WriteOpCtx() as write_op:
+ write_op.omap_cmp('key1', '1', LIBRADOS_CMPXATTR_OP_EQ)
+ self.ioctx.set_omap(write_op, ('key1',), ('5',))
+ try:
+ self.ioctx.operate_write_op(write_op, object_id)
+ except (OSError, ExtendMismatch) as e:
+ eq(e.errno, 125)
+ else:
+ message = "omap_cmp did not raise Exception when omap content does not match"
+ raise AssertionError(message)
+
+ def test_cmpext_op(self):
+ object_id = 'test'
+ with WriteOpCtx() as write_op:
+ write_op.write(b'12345', 0)
+ self.ioctx.operate_write_op(write_op, object_id)
+ with WriteOpCtx() as write_op:
+ write_op.cmpext(b'12345', 0)
+ write_op.write(b'54321', 0)
+ self.ioctx.operate_write_op(write_op, object_id)
+ eq(self.ioctx.read(object_id), b'54321')
+ with WriteOpCtx() as write_op:
+ write_op.cmpext(b'56789', 0)
+ write_op.write(b'12345', 0)
+ try:
+ self.ioctx.operate_write_op(write_op, object_id)
+ except ExtendMismatch as e:
+ # the cmpext_result compare with expected error number, it should be (-MAX_ERRNO - 1)
+ # where "1" is the offset of the first unmatched byte
+ eq(-e.errno, -MAX_ERRNO - 1)
+ eq(e.offset, 1)
+ else:
+ message = "cmpext did not raise Exception when object content does not match"
+ raise AssertionError(message)
+ with ReadOpCtx() as read_op:
+ read_op.cmpext(b'54321', 0)
+ self.ioctx.operate_read_op(read_op, object_id)
+ with ReadOpCtx() as read_op:
+ read_op.cmpext(b'54789', 0)
+ try:
+ self.ioctx.operate_read_op(read_op, object_id)
+ except ExtendMismatch as e:
+ # the cmpext_result compare with expected error number, it should be (-MAX_ERRNO - 2)
+ # where "2" is the offset of the first unmatched byte
+ eq(-e.errno, -MAX_ERRNO - 2)
+ eq(e.offset, 2)
+ else:
+ message = "cmpext did not raise Exception when object content does not match"
+ raise AssertionError(message)
+
+ def test_xattrs_op(self):
+ xattrs = dict(a=b'1', b=b'2', c=b'3', d=b'a\0b', e=b'\0')
+ with WriteOpCtx() as write_op:
+ write_op.new(LIBRADOS_CREATE_EXCLUSIVE)
+ for key, value in xattrs.items():
+ write_op.set_xattr(key, value)
+ self.ioctx.operate_write_op(write_op, 'abc')
+ eq(self.ioctx.get_xattr('abc', key), value)
+
+ stored_xattrs_1 = {}
+ for key, value in self.ioctx.get_xattrs('abc'):
+ stored_xattrs_1[key] = value
+ eq(stored_xattrs_1, xattrs)
+
+ for key in xattrs.keys():
+ write_op.rm_xattr(key)
+ self.ioctx.operate_write_op(write_op, 'abc')
+ stored_xattrs_2 = {}
+ for key, value in self.ioctx.get_xattrs('abc'):
+ stored_xattrs_2[key] = value
+ eq(stored_xattrs_2, {})
+
+ write_op.remove()
+ self.ioctx.operate_write_op(write_op, 'abc')
+
+ def test_locator(self):
+ self.ioctx.set_locator_key("bar")
+ self.ioctx.write('foo', b'contents1')
+ objects = [i for i in self.ioctx.list_objects()]
+ eq(len(objects), 1)
+ eq(self.ioctx.get_locator_key(), "bar")
+ self.ioctx.set_locator_key("")
+ objects[0].seek(0)
+ objects[0].write(b"contents2")
+ eq(self.ioctx.get_locator_key(), "")
+ self.ioctx.set_locator_key("bar")
+ contents = self.ioctx.read("foo")
+ eq(contents, b"contents2")
+ eq(self.ioctx.get_locator_key(), "bar")
+ objects[0].remove()
+ objects = [i for i in self.ioctx.list_objects()]
+ eq(objects, [])
+ self.ioctx.set_locator_key("")
+
+ def test_operate_aio_write_op(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ with WriteOpCtx() as write_op:
+ write_op.write(b'rzx')
+ comp = self.ioctx.operate_aio_write_op(write_op, "object", cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ eq(self.ioctx.read('object'), b'rzx')
+
+ def test_aio_write(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ comp = self.ioctx.aio_write("foo", b"bar", 0, cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ contents = self.ioctx.read("foo")
+ eq(contents, b"bar")
+ [i.remove() for i in self.ioctx.list_objects()]
+
+ def test_aio_cmpext(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+
+ self.ioctx.write('test_object', b'abcdefghi')
+ comp = self.ioctx.aio_cmpext('test_object', b'abcdefghi', 0, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 1:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+
+ def test_aio_rmxattr(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ self.ioctx.set_xattr("xyz", "key", b'value')
+ eq(self.ioctx.get_xattr("xyz", "key"), b'value')
+ comp = self.ioctx.aio_rmxattr("xyz", "key", cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 1:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ with pytest.raises(NoData):
+ self.ioctx.get_xattr("xyz", "key")
+
+ def test_aio_write_no_comp_ref(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ # NOTE(sileht): force don't save the comp into local var
+ # to ensure all references are correctly tracked into the lib
+ self.ioctx.aio_write("foo", b"bar", 0, cb, cb)
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ contents = self.ioctx.read("foo")
+ eq(contents, b"bar")
+ [i.remove() for i in self.ioctx.list_objects()]
+
+ def test_aio_append(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ comp = self.ioctx.aio_write("foo", b"bar", 0, cb, cb)
+ comp2 = self.ioctx.aio_append("foo", b"baz", cb, cb)
+ comp.wait_for_complete()
+ contents = self.ioctx.read("foo")
+ eq(contents, b"barbaz")
+ with lock:
+ while count[0] < 4:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ eq(comp2.get_return_value(), 0)
+ [i.remove() for i in self.ioctx.list_objects()]
+
+ def test_aio_write_full(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ self.ioctx.aio_write("foo", b"barbaz", 0, cb, cb)
+ comp = self.ioctx.aio_write_full("foo", b"bar", cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ contents = self.ioctx.read("foo")
+ eq(contents, b"bar")
+ [i.remove() for i in self.ioctx.list_objects()]
+
+ def test_aio_writesame(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ comp = self.ioctx.aio_writesame("abc", b"rzx", 9, 0, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 1:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ eq(self.ioctx.read("abc"), b"rzxrzxrzx")
+ [i.remove() for i in self.ioctx.list_objects()]
+
+ def test_aio_stat(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(_, size, mtime):
+ with lock:
+ count[0] += 1
+ lock.notify()
+
+ comp = self.ioctx.aio_stat("foo", cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 1:
+ lock.wait()
+ eq(comp.get_return_value(), -2)
+
+ self.ioctx.write("foo", b"bar")
+
+ comp = self.ioctx.aio_stat("foo", cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+
+ [i.remove() for i in self.ioctx.list_objects()]
+
+ def test_aio_remove(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ self.ioctx.write('foo', b'wrx')
+ eq(self.ioctx.read('foo'), b'wrx')
+ comp = self.ioctx.aio_remove('foo', cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ eq(list(self.ioctx.list_objects()), [])
+
+ def _take_down_acting_set(self, pool, objectname):
+ # find acting_set for pool:objectname and take it down; used to
+ # verify that async reads don't complete while acting set is missing
+ cmd = {
+ "prefix":"osd map",
+ "pool":pool,
+ "object":objectname,
+ "format":"json",
+ }
+ r, jsonout, _ = self.rados.mon_command(json.dumps(cmd), b'')
+ objmap = json.loads(jsonout.decode("utf-8"))
+ acting_set = objmap['acting']
+ cmd = {"prefix":"osd set", "key":"noup"}
+ r, _, _ = self.rados.mon_command(json.dumps(cmd), b'')
+ eq(r, 0)
+ cmd = {"prefix":"osd down", "ids":[str(i) for i in acting_set]}
+ r, _, _ = self.rados.mon_command(json.dumps(cmd), b'')
+ eq(r, 0)
+
+ # wait for OSDs to acknowledge the down
+ eq(self.rados.wait_for_latest_osdmap(), 0)
+
+ def _let_osds_back_up(self):
+ cmd = {"prefix":"osd unset", "key":"noup"}
+ r, _, _ = self.rados.mon_command(json.dumps(cmd), b'')
+ eq(r, 0)
+
+ @pytest.mark.wait
+ def test_aio_read_wait_for_complete(self):
+ # use wait_for_complete() and wait for cb by
+ # watching retval[0]
+
+ # this is a list so that the local cb() can modify it
+ payload = b"bar\000frob"
+ self.ioctx.write("foo", payload)
+ self._take_down_acting_set('test_pool', 'foo')
+
+ retval = [None]
+ lock = threading.Condition()
+ def cb(_, buf):
+ with lock:
+ retval[0] = buf
+ lock.notify()
+
+ comp = self.ioctx.aio_read("foo", len(payload), 0, cb)
+ eq(False, comp.is_complete())
+ time.sleep(3)
+ eq(False, comp.is_complete())
+ with lock:
+ eq(None, retval[0])
+
+ self._let_osds_back_up()
+ comp.wait_for_complete()
+ loops = 0
+ with lock:
+ while retval[0] is None and loops <= 10:
+ lock.wait(timeout=5)
+ loops += 1
+ assert(loops <= 10)
+
+ eq(retval[0], payload)
+ eq(sys.getrefcount(comp), 2)
+
+ @pytest.mark.wait
+ def test_aio_read_wait_for_complete_and_cb(self):
+ # use wait_for_complete_and_cb(), verify retval[0] is
+ # set by the time we regain control
+ payload = b"bar\000frob"
+ self.ioctx.write("foo", payload)
+
+ self._take_down_acting_set('test_pool', 'foo')
+ # this is a list so that the local cb() can modify it
+ retval = [None]
+ lock = threading.Condition()
+ def cb(_, buf):
+ with lock:
+ retval[0] = buf
+ lock.notify()
+ comp = self.ioctx.aio_read("foo", len(payload), 0, cb)
+ eq(False, comp.is_complete())
+ time.sleep(3)
+ eq(False, comp.is_complete())
+ with lock:
+ eq(None, retval[0])
+
+ self._let_osds_back_up()
+ comp.wait_for_complete_and_cb()
+ assert(retval[0] is not None)
+ eq(retval[0], payload)
+ eq(sys.getrefcount(comp), 2)
+
+ @pytest.mark.wait
+ def test_aio_read_wait_for_complete_and_cb_error(self):
+ # error case, use wait_for_complete_and_cb(), verify retval[0] is
+ # set by the time we regain control
+ self._take_down_acting_set('test_pool', 'bar')
+
+ # this is a list so that the local cb() can modify it
+ retval = [1]
+ lock = threading.Condition()
+ def cb(_, buf):
+ with lock:
+ retval[0] = buf
+ lock.notify()
+
+ # read from a DNE object
+ comp = self.ioctx.aio_read("bar", 3, 0, cb)
+ eq(False, comp.is_complete())
+ time.sleep(3)
+ eq(False, comp.is_complete())
+ with lock:
+ eq(1, retval[0])
+ self._let_osds_back_up()
+
+ comp.wait_for_complete_and_cb()
+ eq(None, retval[0])
+ assert(comp.get_return_value() < 0)
+ eq(sys.getrefcount(comp), 2)
+
+ def test_lock(self):
+ self.ioctx.lock_exclusive("foo", "lock", "locker", "desc_lock",
+ 10000, 0)
+ assert_raises(ObjectExists,
+ self.ioctx.lock_exclusive,
+ "foo", "lock", "locker", "desc_lock", 10000, 0)
+ self.ioctx.unlock("foo", "lock", "locker")
+ assert_raises(ObjectNotFound, self.ioctx.unlock, "foo", "lock", "locker")
+
+ self.ioctx.lock_shared("foo", "lock", "locker1", "tag", "desc_lock",
+ 10000, 0)
+ self.ioctx.lock_shared("foo", "lock", "locker2", "tag", "desc_lock",
+ 10000, 0)
+ assert_raises(ObjectBusy,
+ self.ioctx.lock_exclusive,
+ "foo", "lock", "locker3", "desc_lock", 10000, 0)
+ self.ioctx.unlock("foo", "lock", "locker1")
+ self.ioctx.unlock("foo", "lock", "locker2")
+ assert_raises(ObjectNotFound, self.ioctx.unlock, "foo", "lock", "locker1")
+ assert_raises(ObjectNotFound, self.ioctx.unlock, "foo", "lock", "locker2")
+
+ def test_execute(self):
+ self.ioctx.write("foo", b"") # ensure object exists
+
+ ret, buf = self.ioctx.execute("foo", "hello", "say_hello", b"")
+ eq(buf, b"Hello, world!")
+
+ ret, buf = self.ioctx.execute("foo", "hello", "say_hello", b"nose")
+ eq(buf, b"Hello, nose!")
+
+ def test_aio_execute(self):
+ count = [0]
+ retval = [None]
+ lock = threading.Condition()
+ def cb(_, buf):
+ with lock:
+ if retval[0] is None:
+ retval[0] = buf
+ count[0] += 1
+ lock.notify()
+ self.ioctx.write("foo", b"") # ensure object exists
+
+ comp = self.ioctx.aio_execute("foo", "hello", "say_hello", b"", 32, cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 2:
+ lock.wait()
+ eq(comp.get_return_value(), 13)
+ eq(retval[0], b"Hello, world!")
+
+ retval[0] = None
+ comp = self.ioctx.aio_execute("foo", "hello", "say_hello", b"nose", 32, cb, cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 4:
+ lock.wait()
+ eq(comp.get_return_value(), 12)
+ eq(retval[0], b"Hello, nose!")
+
+ [i.remove() for i in self.ioctx.list_objects()]
+
+ def test_aio_setxattr(self):
+ lock = threading.Condition()
+ count = [0]
+ def cb(blah):
+ with lock:
+ count[0] += 1
+ lock.notify()
+ return 0
+ comp = self.ioctx.aio_setxattr("obj", "key", b'value', cb)
+ comp.wait_for_complete()
+ with lock:
+ while count[0] < 1:
+ lock.wait()
+ eq(comp.get_return_value(), 0)
+ eq(self.ioctx.get_xattr("obj", "key"), b'value')
+
+ def test_applications(self):
+ cmd = {"prefix":"osd dump", "format":"json"}
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'')
+ eq(ret, 0)
+ assert len(buf) > 0
+ release = json.loads(buf.decode("utf-8")).get("require_osd_release",
+ None)
+ if not release or release[0] < 'l':
+ pytest.skip('required_osd_release >= l')
+
+ eq([], self.ioctx.application_list())
+
+ self.ioctx.application_enable("app1")
+ assert_raises(Error, self.ioctx.application_enable, "app2")
+ self.ioctx.application_enable("app2", True)
+
+ assert_raises(Error, self.ioctx.application_metadata_list, "dne")
+ eq([], self.ioctx.application_metadata_list("app1"))
+
+ assert_raises(Error, self.ioctx.application_metadata_set, "dne", "key",
+ "key")
+ self.ioctx.application_metadata_set("app1", "key1", "val1")
+ eq("val1", self.ioctx.application_metadata_get("app1", "key1"))
+ self.ioctx.application_metadata_set("app1", "key2", "val2")
+ eq("val2", self.ioctx.application_metadata_get("app1", "key2"))
+ self.ioctx.application_metadata_set("app2", "key1", "val1")
+ eq("val1", self.ioctx.application_metadata_get("app2", "key1"))
+
+ eq([("key1", "val1"), ("key2", "val2")],
+ self.ioctx.application_metadata_list("app1"))
+
+ self.ioctx.application_metadata_remove("app1", "key1")
+ eq([("key2", "val2")], self.ioctx.application_metadata_list("app1"))
+
+ def test_service_daemon(self):
+ name = "pid-" + str(os.getpid())
+ metadata = {'version': '3.14', 'memory': '42'}
+ self.rados.service_daemon_register("laundry", name, metadata)
+ status = {'result': 'unknown', 'test': 'running'}
+ self.rados.service_daemon_update(status)
+
+ def test_alignment(self):
+ eq(self.ioctx.alignment(), None)
+
+
+@pytest.mark.ec
+class TestIoctxEc(object):
+
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.connect()
+ self.pool = 'test-ec'
+ self.profile = 'testprofile-%s' % self.pool
+ cmd = {"prefix": "osd erasure-code-profile set",
+ "name": self.profile, "profile": ["k=2", "m=1", "crush-failure-domain=osd"]}
+ ret, buf, out = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ assert ret == 0, out
+ # create ec pool with profile created above
+ cmd = {'prefix': 'osd pool create', 'pg_num': 8, 'pgp_num': 8,
+ 'pool': self.pool, 'pool_type': 'erasure',
+ 'erasure_code_profile': self.profile}
+ ret, buf, out = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ assert ret == 0, out
+ assert self.rados.pool_exists(self.pool)
+ self.ioctx = self.rados.open_ioctx(self.pool)
+
+ def teardown_method(self, method):
+ cmd = {"prefix": "osd unset", "key": "noup"}
+ self.rados.mon_command(json.dumps(cmd), b'')
+ self.ioctx.close()
+ self.rados.delete_pool(self.pool)
+ self.rados.shutdown()
+
+ def test_alignment(self):
+ eq(self.ioctx.alignment(), 8192)
+
+
+class TestIoctx2(object):
+
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.connect()
+ self.rados.create_pool('test_pool')
+ assert self.rados.pool_exists('test_pool')
+ pool_id = self.rados.pool_lookup('test_pool')
+ assert pool_id > 0
+ self.ioctx2 = self.rados.open_ioctx2(pool_id)
+
+ def teardown_method(self, method):
+ cmd = {"prefix": "osd unset", "key": "noup"}
+ self.rados.mon_command(json.dumps(cmd), b'')
+ self.ioctx2.close()
+ self.rados.delete_pool('test_pool')
+ self.rados.shutdown()
+
+ def test_get_last_version(self):
+ version = self.ioctx2.get_last_version()
+ assert version >= 0
+
+ def test_get_stats(self):
+ stats = self.ioctx2.get_stats()
+ eq(stats, {'num_objects_unfound': 0,
+ 'num_objects_missing_on_primary': 0,
+ 'num_object_clones': 0,
+ 'num_objects': 0,
+ 'num_object_copies': 0,
+ 'num_bytes': 0,
+ 'num_rd_kb': 0,
+ 'num_wr_kb': 0,
+ 'num_kb': 0,
+ 'num_wr': 0,
+ 'num_objects_degraded': 0,
+ 'num_rd': 0})
+
+
+class TestObject(object):
+
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.connect()
+ self.rados.create_pool('test_pool')
+ assert self.rados.pool_exists('test_pool')
+ self.ioctx = self.rados.open_ioctx('test_pool')
+ self.ioctx.write('foo', b'bar')
+ self.object = Object(self.ioctx, 'foo')
+
+ def teardown_method(self, method):
+ self.ioctx.close()
+ self.ioctx = None
+ self.rados.delete_pool('test_pool')
+ self.rados.shutdown()
+ self.rados = None
+
+ def test_read(self):
+ eq(self.object.read(3), b'bar')
+ eq(self.object.read(100), b'')
+
+ def test_seek(self):
+ self.object.write(b'blah')
+ self.object.seek(0)
+ eq(self.object.read(4), b'blah')
+ self.object.seek(1)
+ eq(self.object.read(3), b'lah')
+
+ def test_write(self):
+ self.object.write(b'barbaz')
+ self.object.seek(0)
+ eq(self.object.read(3), b'bar')
+ eq(self.object.read(3), b'baz')
+
+class TestIoCtxSelfManagedSnaps(object):
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.connect()
+ self.rados.create_pool('test_pool')
+ assert self.rados.pool_exists('test_pool')
+ self.ioctx = self.rados.open_ioctx('test_pool')
+
+ def teardown_method(self, method):
+ cmd = {"prefix":"osd unset", "key":"noup"}
+ self.rados.mon_command(json.dumps(cmd), b'')
+ self.ioctx.close()
+ self.rados.delete_pool('test_pool')
+ self.rados.shutdown()
+
+ @pytest.mark.rollback
+ def test(self):
+ # cannot mix-and-match pool and self-managed snapshot mode
+ self.ioctx.set_self_managed_snap_write([])
+ self.ioctx.write('abc', b'abc')
+ snap_id_1 = self.ioctx.create_self_managed_snap()
+ self.ioctx.set_self_managed_snap_write([snap_id_1])
+
+ self.ioctx.write('abc', b'def')
+ snap_id_2 = self.ioctx.create_self_managed_snap()
+ self.ioctx.set_self_managed_snap_write([snap_id_1, snap_id_2])
+
+ self.ioctx.write('abc', b'ghi')
+
+ self.ioctx.rollback_self_managed_snap('abc', snap_id_1)
+ eq(self.ioctx.read('abc'), b'abc')
+
+ self.ioctx.rollback_self_managed_snap('abc', snap_id_2)
+ eq(self.ioctx.read('abc'), b'def')
+
+ self.ioctx.remove_self_managed_snap(snap_id_1)
+ self.ioctx.remove_self_managed_snap(snap_id_2)
+
+class TestCommand(object):
+
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.connect()
+
+ def teardown_method(self, method):
+ self.rados.shutdown()
+
+ def test_monmap_dump(self):
+
+ # check for success and some plain output with epoch in it
+ cmd = {"prefix":"mon dump"}
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ eq(ret, 0)
+ assert len(buf) > 0
+ assert(b'epoch' in buf)
+
+ # JSON, and grab current epoch
+ cmd['format'] = 'json'
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ eq(ret, 0)
+ assert len(buf) > 0
+ d = json.loads(buf.decode("utf-8"))
+ assert('epoch' in d)
+ epoch = d['epoch']
+
+ # assume epoch + 1000 does not exist; test for ENOENT
+ cmd['epoch'] = epoch + 1000
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30)
+ eq(ret, -errno.ENOENT)
+ eq(len(buf), 0)
+ del cmd['epoch']
+
+ # send to specific target by name, rank
+ cmd = {"prefix": "version"}
+
+ target = d['mons'][0]['name']
+ print(target)
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30,
+ target=target)
+ eq(ret, 0)
+ assert len(buf) > 0
+ e = json.loads(buf.decode("utf-8"))
+ assert('release' in e)
+
+ target = d['mons'][0]['rank']
+ print(target)
+ ret, buf, errs = self.rados.mon_command(json.dumps(cmd), b'', timeout=30,
+ target=target)
+ eq(ret, 0)
+ assert len(buf) > 0
+ e = json.loads(buf.decode("utf-8"))
+ assert('release' in e)
+
+ @pytest.mark.bench
+ def test_osd_bench(self):
+ cmd = dict(prefix='bench', size=4096, count=8192)
+ ret, buf, err = self.rados.osd_command(0, json.dumps(cmd), b'',
+ timeout=30)
+ eq(ret, 0)
+ assert len(buf) > 0
+ out = json.loads(buf.decode('utf-8'))
+ eq(out['blocksize'], cmd['size'])
+ eq(out['bytes_written'], cmd['count'])
+
+ def test_ceph_osd_pool_create_utf8(self):
+ poolname = "\u9ec5"
+
+ cmd = {"prefix": "osd pool create", "pg_num": 16, "pool": poolname}
+ ret, buf, out = self.rados.mon_command(json.dumps(cmd), b'')
+ eq(ret, 0)
+ assert len(out) > 0
+ eq(u"pool '\u9ec5' created", out)
+
+
+@pytest.mark.watch
+class TestWatchNotify(object):
+ OID = "test_watch_notify"
+
+ def setup_method(self, method):
+ self.rados = Rados(conffile='')
+ self.rados.connect()
+ self.rados.create_pool('test_pool')
+ assert self.rados.pool_exists('test_pool')
+ self.ioctx = self.rados.open_ioctx('test_pool')
+ self.ioctx.write(self.OID, b'test watch notify')
+ self.lock = threading.Condition()
+ self.notify_cnt = {}
+ self.notify_data = {}
+ self.notify_error = {}
+ # aio related
+ self.ack_cnt = {}
+ self.ack_data = {}
+ self.instance_id = self.rados.get_instance_id()
+
+ def teardown_method(self, method):
+ self.ioctx.close()
+ self.rados.delete_pool('test_pool')
+ self.rados.shutdown()
+
+ def make_callback(self):
+ def callback(notify_id, notifier_id, watch_id, data):
+ with self.lock:
+ if watch_id not in self.notify_cnt:
+ self.notify_cnt[watch_id] = 1
+ elif self.notify_data[watch_id] != data:
+ self.notify_cnt[watch_id] += 1
+ self.notify_data[watch_id] = data
+ return callback
+
+ def make_error_callback(self):
+ def callback(watch_id, error):
+ with self.lock:
+ self.notify_error[watch_id] = error
+ return callback
+
+
+ def test(self):
+ with self.ioctx.watch(self.OID, self.make_callback(),
+ self.make_error_callback()) as watch1:
+ watch_id1 = watch1.get_id()
+ assert(watch_id1 > 0)
+
+ with self.rados.open_ioctx('test_pool') as ioctx:
+ watch2 = ioctx.watch(self.OID, self.make_callback(),
+ self.make_error_callback())
+ watch_id2 = watch2.get_id()
+ assert(watch_id2 > 0)
+
+ assert(self.ioctx.notify(self.OID, 'test'))
+ with self.lock:
+ assert(watch_id1 in self.notify_cnt)
+ assert(watch_id2 in self.notify_cnt)
+ eq(self.notify_cnt[watch_id1], 1)
+ eq(self.notify_cnt[watch_id2], 1)
+ eq(self.notify_data[watch_id1], b'test')
+ eq(self.notify_data[watch_id2], b'test')
+
+ assert(watch1.check() >= timedelta())
+ assert(watch2.check() >= timedelta())
+
+ assert(self.ioctx.notify(self.OID, 'best'))
+ with self.lock:
+ eq(self.notify_cnt[watch_id1], 2)
+ eq(self.notify_cnt[watch_id2], 2)
+ eq(self.notify_data[watch_id1], b'best')
+ eq(self.notify_data[watch_id2], b'best')
+
+ watch2.close()
+
+ assert(self.ioctx.notify(self.OID, 'rest'))
+ with self.lock:
+ eq(self.notify_cnt[watch_id1], 3)
+ eq(self.notify_cnt[watch_id2], 2)
+ eq(self.notify_data[watch_id1], b'rest')
+ eq(self.notify_data[watch_id2], b'best')
+
+ assert(watch1.check() >= timedelta())
+
+ self.ioctx.remove_object(self.OID)
+
+ for i in range(10):
+ with self.lock:
+ if watch_id1 in self.notify_error:
+ break
+ time.sleep(1)
+ eq(self.notify_error[watch_id1], -errno.ENOTCONN)
+ assert_raises(NotConnected, watch1.check)
+
+ assert_raises(ObjectNotFound, self.ioctx.notify, self.OID, 'test')
+
+ def make_callback_reply(self):
+ def callback(notify_id, notifier_id, watch_id, data):
+ with self.lock:
+ return data
+ return callback
+
+ def notify_callback(self, _, r, ack_list, timeout_list):
+ eq(r, 0)
+ with self.lock:
+ for notifier_id, _, notifier_data in ack_list:
+ if notifier_id not in self.ack_cnt:
+ self.ack_cnt[notifier_id] = 0
+ self.ack_cnt[notifier_id] += 1
+ self.ack_data[notifier_id] = notifier_data
+
+ def notify_callback_err(self, _, r, ack_list, timeout_list):
+ eq(r, -errno.ENOENT)
+
+ def test_aio_notify(self):
+ with self.ioctx.watch(self.OID, self.make_callback_reply(),
+ self.make_error_callback()) as watch1:
+ watch_id1 = watch1.get_id()
+ assert watch_id1 > 0
+
+ with self.rados.open_ioctx('test_pool') as ioctx:
+ watch2 = ioctx.watch(self.OID, self.make_callback_reply(),
+ self.make_error_callback())
+ watch_id2 = watch2.get_id()
+ assert watch_id2 > 0
+
+ comp = self.ioctx.aio_notify(self.OID, self.notify_callback, msg='test')
+ comp.wait_for_complete_and_cb()
+ with self.lock:
+ assert self.instance_id in self.ack_cnt
+ eq(self.ack_cnt[self.instance_id], 2)
+ eq(self.ack_data[self.instance_id], b'test')
+
+ assert watch1.check() >= timedelta()
+ assert watch2.check() >= timedelta()
+
+ comp = self.ioctx.aio_notify(self.OID, self.notify_callback, msg='best')
+ comp.wait_for_complete_and_cb()
+ with self.lock:
+ eq(self.ack_cnt[self.instance_id], 4)
+ eq(self.ack_data[self.instance_id], b'best')
+
+ watch2.close()
+
+ comp = self.ioctx.aio_notify(self.OID, self.notify_callback, msg='rest')
+ comp.wait_for_complete_and_cb()
+ with self.lock:
+ eq(self.ack_cnt[self.instance_id], 5)
+ eq(self.ack_data[self.instance_id], b'rest')
+
+ assert(watch1.check() >= timedelta())
+ self.ioctx.remove_object(self.OID)
+
+ for i in range(10):
+ with self.lock:
+ if watch_id1 in self.notify_error:
+ break
+ time.sleep(1)
+ eq(self.notify_error[watch_id1], -errno.ENOTCONN)
+ assert_raises(NotConnected, watch1.check)
+
+ comp = self.ioctx.aio_notify(self.OID, self.notify_callback_err, msg='test')
+ comp.wait_for_complete_and_cb()
diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py
new file mode 100644
index 000000000..7b5f31b57
--- /dev/null
+++ b/src/test/pybind/test_rbd.py
@@ -0,0 +1,2810 @@
+# vim: expandtab smarttab shiftwidth=4 softtabstop=4
+import base64
+import copy
+import errno
+import functools
+import json
+import socket
+import os
+import platform
+import pytest
+import time
+import sys
+
+from assertions import (assert_equal as eq, assert_raises, assert_not_equal,
+ assert_greater_equal)
+from datetime import datetime, timedelta
+from rados import (Rados,
+ LIBRADOS_OP_FLAG_FADVISE_DONTNEED,
+ LIBRADOS_OP_FLAG_FADVISE_NOCACHE,
+ LIBRADOS_OP_FLAG_FADVISE_RANDOM)
+from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
+ ImageBusy, ImageHasSnapshots, ReadOnlyImage,
+ FunctionNotSupported, ArgumentOutOfRange,
+ ECANCELED, OperationCanceled,
+ DiskQuotaExceeded, ConnectionShutdown, PermissionError,
+ RBD_FEATURE_LAYERING, RBD_FEATURE_STRIPINGV2,
+ RBD_FEATURE_EXCLUSIVE_LOCK, RBD_FEATURE_JOURNALING,
+ RBD_FEATURE_DEEP_FLATTEN, RBD_FEATURE_FAST_DIFF,
+ RBD_FEATURE_OBJECT_MAP,
+ RBD_MIRROR_MODE_DISABLED, RBD_MIRROR_MODE_IMAGE,
+ RBD_MIRROR_MODE_POOL, RBD_MIRROR_IMAGE_ENABLED,
+ RBD_MIRROR_IMAGE_DISABLED, MIRROR_IMAGE_STATUS_STATE_UNKNOWN,
+ RBD_MIRROR_IMAGE_MODE_JOURNAL, RBD_MIRROR_IMAGE_MODE_SNAPSHOT,
+ RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP,
+ RBD_SNAP_NAMESPACE_TYPE_TRASH,
+ RBD_SNAP_NAMESPACE_TYPE_MIRROR,
+ RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG,
+ RBD_CONFIG_SOURCE_POOL, RBD_CONFIG_SOURCE_IMAGE,
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST,
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY,
+ RBD_MIRROR_PEER_DIRECTION_RX, RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ RBD_SNAP_REMOVE_UNPROTECT, RBD_SNAP_MIRROR_STATE_PRIMARY,
+ RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED,
+ RBD_SNAP_CREATE_SKIP_QUIESCE,
+ RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR,
+ RBD_WRITE_ZEROES_FLAG_THICK_PROVISION,
+ RBD_ENCRYPTION_FORMAT_LUKS1, RBD_ENCRYPTION_FORMAT_LUKS2,
+ RBD_ENCRYPTION_FORMAT_LUKS)
+
+rados = None
+ioctx = None
+features = None
+image_idx = 0
+group_idx = 0
+snap_idx = 0
+image_name = None
+group_name = None
+snap_name = None
+pool_idx = 0
+pool_name = None
+IMG_SIZE = 8 << 20 # 8 MiB
+IMG_ORDER = 22 # 4 MiB objects
+
+os.environ["RBD_FORCE_ALLOW_V1"] = "1"
+
+def setup_module():
+ global rados
+ rados = Rados(conffile='')
+ rados.connect()
+ global pool_name
+ pool_name = get_temp_pool_name()
+ rados.create_pool(pool_name)
+ global ioctx
+ ioctx = rados.open_ioctx(pool_name)
+ RBD().pool_init(ioctx, True)
+ global features
+ features = os.getenv("RBD_FEATURES")
+ if features is not None:
+ features = int(features)
+
+def teardown_module():
+ global ioctx
+ ioctx.close()
+ global rados
+ rados.delete_pool(pool_name)
+ rados.shutdown()
+
+def get_temp_pool_name():
+ global pool_idx
+ pool_idx += 1
+ return "test-rbd-api-" + socket.gethostname() + '-' + str(os.getpid()) + \
+ '-' + str(pool_idx)
+
+def get_temp_image_name():
+ global image_idx
+ image_idx += 1
+ return "image" + str(image_idx)
+
+def get_temp_group_name():
+ global group_idx
+ group_idx += 1
+ return "group" + str(group_idx)
+
+def get_temp_snap_name():
+ global snap_idx
+ snap_idx += 1
+ return "snap" + str(snap_idx)
+
+def create_image():
+ global image_name
+ image_name = get_temp_image_name()
+ if features is not None:
+ RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=False,
+ features=int(features))
+ else:
+ RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=True)
+ return image_name
+
+def remove_image():
+ if image_name is not None:
+ RBD().remove(ioctx, image_name)
+
+@pytest.fixture
+def tmp_image():
+ create_image()
+ yield
+ remove_image()
+
+def create_group():
+ global group_name
+ group_name = get_temp_group_name()
+ RBD().group_create(ioctx, group_name)
+
+def remove_group():
+ if group_name is not None:
+ RBD().group_remove(ioctx, group_name)
+
+@pytest.fixture
+def tmp_group():
+ create_group()
+ yield
+ remove_group()
+
+def rename_group():
+ new_group_name = "new" + group_name
+ RBD().group_rename(ioctx, group_name, new_group_name)
+
+def require_new_format():
+ def wrapper(fn):
+ def _require_new_format(*args, **kwargs):
+ global features
+ if features is None:
+ pytest.skip('requires new format')
+ return fn(*args, **kwargs)
+ return functools.wraps(fn)(_require_new_format)
+ return wrapper
+
+def require_features(required_features):
+ def wrapper(fn):
+ def _require_features(*args, **kwargs):
+ global features
+ if features is None:
+ pytest.skip('requires new format')
+ for feature in required_features:
+ if feature & features != feature:
+ pytest.skip('missing required feature')
+ return fn(*args, **kwargs)
+ return functools.wraps(fn)(_require_features)
+ return wrapper
+
+def require_linux():
+ def wrapper(fn):
+ def _require_linux(*args, **kwargs):
+ if platform.system() != "Linux":
+ pytest.skip('requires linux')
+ return fn(*args, **kwargs)
+ return functools.wraps(fn)(_require_linux)
+ return wrapper
+
+def blocklist_features(blocklisted_features):
+ def wrapper(fn):
+ def _blocklist_features(*args, **kwargs):
+ global features
+ for feature in blocklisted_features:
+ if features is not None and feature & features == feature:
+ pytest.skip('blocklisted feature enabled')
+ return fn(*args, **kwargs)
+ return functools.wraps(fn)(_blocklist_features)
+ return wrapper
+
+def test_version():
+ RBD().version()
+
+def test_create():
+ create_image()
+ remove_image()
+
+def check_default_params(format, order=None, features=None, stripe_count=None,
+ stripe_unit=None, exception=None):
+ global rados
+ global ioctx
+ orig_vals = {}
+ for k in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
+ 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
+ orig_vals[k] = rados.conf_get(k)
+ try:
+ rados.conf_set('rbd_default_format', str(format))
+ if order is not None:
+ rados.conf_set('rbd_default_order', str(order or 0))
+ if features is not None:
+ rados.conf_set('rbd_default_features', str(features or 0))
+ if stripe_count is not None:
+ rados.conf_set('rbd_default_stripe_count', str(stripe_count or 0))
+ if stripe_unit is not None:
+ rados.conf_set('rbd_default_stripe_unit', str(stripe_unit or 0))
+ feature_data_pool = 0
+ datapool = rados.conf_get('rbd_default_data_pool')
+ if not len(datapool) == 0:
+ feature_data_pool = 128
+ image_name = get_temp_image_name()
+ if exception is None:
+ RBD().create(ioctx, image_name, IMG_SIZE, old_format=(format == 1))
+ try:
+ with Image(ioctx, image_name) as image:
+ eq(format == 1, image.old_format())
+
+ expected_order = int(rados.conf_get('rbd_default_order'))
+ actual_order = image.stat()['order']
+ eq(expected_order, actual_order)
+
+ expected_features = features
+ if format == 1:
+ expected_features = 0
+ elif expected_features is None:
+ expected_features = 61 | feature_data_pool
+ else:
+ expected_features |= feature_data_pool
+ eq(expected_features, image.features())
+
+ expected_stripe_count = stripe_count
+ if not expected_stripe_count or format == 1 or \
+ features & RBD_FEATURE_STRIPINGV2 == 0:
+ expected_stripe_count = 1
+ eq(expected_stripe_count, image.stripe_count())
+
+ expected_stripe_unit = stripe_unit
+ if not expected_stripe_unit or format == 1 or \
+ features & RBD_FEATURE_STRIPINGV2 == 0:
+ expected_stripe_unit = 1 << actual_order
+ eq(expected_stripe_unit, image.stripe_unit())
+ finally:
+ RBD().remove(ioctx, image_name)
+ else:
+ assert_raises(exception, RBD().create, ioctx, image_name, IMG_SIZE)
+ finally:
+ for k, v in orig_vals.items():
+ rados.conf_set(k, v)
+
+def test_create_defaults():
+ # basic format 1 and 2
+ check_default_params(1)
+ check_default_params(2)
+ # invalid order
+ check_default_params(1, 0, exception=ArgumentOutOfRange)
+ check_default_params(2, 0, exception=ArgumentOutOfRange)
+ check_default_params(1, 11, exception=ArgumentOutOfRange)
+ check_default_params(2, 11, exception=ArgumentOutOfRange)
+ check_default_params(1, 65, exception=ArgumentOutOfRange)
+ check_default_params(2, 65, exception=ArgumentOutOfRange)
+ # striping and features are ignored for format 1
+ check_default_params(1, 20, 0, 1, 1)
+ check_default_params(1, 20, 3, 1, 1)
+ check_default_params(1, 20, 0, 0, 0)
+ # striping is ignored if stripingv2 is not set
+ check_default_params(2, 20, 0, 1, 1 << 20)
+ check_default_params(2, 20, RBD_FEATURE_LAYERING, 1, 1 << 20)
+ check_default_params(2, 20, 0, 0, 0)
+ # striping with stripingv2 is fine
+ check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 1, 1 << 16)
+ check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 20)
+ check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 16)
+ check_default_params(2, 20, 0, 0, 0)
+ # make sure invalid combinations of stripe unit and order are still invalid
+ check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 1 << 50, exception=InvalidArgument)
+ check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 100, exception=InvalidArgument)
+ check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 0, 1, exception=InvalidArgument)
+ check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 1, 0, exception=InvalidArgument)
+ # 0 stripe unit and count are still ignored
+ check_default_params(2, 22, 0, 0, 0)
+
+def test_context_manager():
+ with Rados(conffile='') as cluster:
+ with cluster.open_ioctx(pool_name) as ioctx:
+ image_name = get_temp_image_name()
+ RBD().create(ioctx, image_name, IMG_SIZE)
+ with Image(ioctx, image_name) as image:
+ data = rand_data(256)
+ image.write(data, 0)
+ read = image.read(0, 256)
+ RBD().remove(ioctx, image_name)
+ eq(data, read)
+
+def test_open_read_only():
+ with Rados(conffile='') as cluster:
+ with cluster.open_ioctx(pool_name) as ioctx:
+ image_name = get_temp_image_name()
+ RBD().create(ioctx, image_name, IMG_SIZE)
+ data = rand_data(256)
+ with Image(ioctx, image_name) as image:
+ image.write(data, 0)
+ image.create_snap('snap')
+ with Image(ioctx, image_name, read_only=True) as image:
+ read = image.read(0, 256)
+ eq(data, read)
+ assert_raises(ReadOnlyImage, image.write, data, 0)
+ assert_raises(ReadOnlyImage, image.create_snap, 'test')
+ assert_raises(ReadOnlyImage, image.remove_snap, 'snap')
+ assert_raises(ReadOnlyImage, image.rollback_to_snap, 'snap')
+ assert_raises(ReadOnlyImage, image.protect_snap, 'snap')
+ assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
+ assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
+ assert_raises(ReadOnlyImage, image.flatten)
+ with Image(ioctx, image_name) as image:
+ image.remove_snap('snap')
+ RBD().remove(ioctx, image_name)
+ eq(data, read)
+
+def test_open_dne():
+ for i in range(100):
+ image_name = get_temp_image_name()
+ assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne')
+ assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap')
+
+def test_open_readonly_dne():
+ for i in range(100):
+ image_name = get_temp_image_name()
+ assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne',
+ read_only=True)
+ assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap',
+ read_only=True)
+
+@require_new_format()
+def test_open_by_id():
+ with Rados(conffile='') as cluster:
+ with cluster.open_ioctx(pool_name) as ioctx:
+ image_name = get_temp_image_name()
+ RBD().create(ioctx, image_name, IMG_SIZE)
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ with Image(ioctx, image_id=image_id) as image:
+ eq(image.get_name(), image_name)
+ RBD().remove(ioctx, image_name)
+
+def test_aio_open():
+ with Rados(conffile='') as cluster:
+ with cluster.open_ioctx(pool_name) as ioctx:
+ image_name = get_temp_image_name()
+ order = 20
+ RBD().create(ioctx, image_name, IMG_SIZE, order)
+
+ # this is a list so that the open_cb() can modify it
+ image = [None]
+ def open_cb(_, image_):
+ image[0] = image_
+
+ comp = RBD().aio_open_image(open_cb, ioctx, image_name)
+ comp.wait_for_complete_and_cb()
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+ assert_not_equal(image[0], None)
+
+ image = image[0]
+ eq(image.get_name(), image_name)
+ check_stat(image.stat(), IMG_SIZE, order)
+
+ closed = [False]
+ def close_cb(_):
+ closed[0] = True
+
+ comp = image.aio_close(close_cb)
+ comp.wait_for_complete_and_cb()
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+ eq(closed[0], True)
+
+ RBD().remove(ioctx, image_name)
+
+def test_remove_dne():
+ assert_raises(ImageNotFound, remove_image)
+
+def test_list_empty():
+ eq([], RBD().list(ioctx))
+
+def test_list(tmp_image):
+ eq([image_name], RBD().list(ioctx))
+
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ eq([{'id': image_id, 'name': image_name}], list(RBD().list2(ioctx)))
+
+def test_remove_with_progress():
+ create_image()
+ d = {'received_callback': False}
+ def progress_cb(current, total):
+ d['received_callback'] = True
+ return 0
+
+ RBD().remove(ioctx, image_name, on_progress=progress_cb)
+ eq(True, d['received_callback'])
+
+def test_remove_canceled(tmp_image):
+ def progress_cb(current, total):
+ return -ECANCELED
+
+ assert_raises(OperationCanceled, RBD().remove, ioctx, image_name,
+ on_progress=progress_cb)
+
+def test_rename(tmp_image):
+ rbd = RBD()
+ image_name2 = get_temp_image_name()
+ rbd.rename(ioctx, image_name, image_name2)
+ eq([image_name2], rbd.list(ioctx))
+ rbd.rename(ioctx, image_name2, image_name)
+ eq([image_name], rbd.list(ioctx))
+
+def test_pool_metadata():
+ rbd = RBD()
+ metadata = list(rbd.pool_metadata_list(ioctx))
+ eq(len(metadata), 0)
+ assert_raises(KeyError, rbd.pool_metadata_get, ioctx, "key1")
+ rbd.pool_metadata_set(ioctx, "key1", "value1")
+ rbd.pool_metadata_set(ioctx, "key2", "value2")
+ value = rbd.pool_metadata_get(ioctx, "key1")
+ eq(value, "value1")
+ value = rbd.pool_metadata_get(ioctx, "key2")
+ eq(value, "value2")
+ metadata = list(rbd.pool_metadata_list(ioctx))
+ eq(len(metadata), 2)
+ rbd.pool_metadata_remove(ioctx, "key1")
+ metadata = list(rbd.pool_metadata_list(ioctx))
+ eq(len(metadata), 1)
+ eq(metadata[0], ("key2", "value2"))
+ rbd.pool_metadata_remove(ioctx, "key2")
+ assert_raises(KeyError, rbd.pool_metadata_remove, ioctx, "key2")
+ metadata = list(rbd.pool_metadata_list(ioctx))
+ eq(len(metadata), 0)
+
+ N = 65
+ for i in range(N):
+ rbd.pool_metadata_set(ioctx, "key" + str(i), "X" * 1025)
+ metadata = list(rbd.pool_metadata_list(ioctx))
+ eq(len(metadata), N)
+ for i in range(N):
+ rbd.pool_metadata_remove(ioctx, "key" + str(i))
+ metadata = list(rbd.pool_metadata_list(ioctx))
+ eq(len(metadata), N - i - 1)
+
+def test_config_list():
+ rbd = RBD()
+
+ for option in rbd.config_list(ioctx):
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "true")
+
+ for option in rbd.config_list(ioctx):
+ if option['name'] == "rbd_cache":
+ eq(option['source'], RBD_CONFIG_SOURCE_POOL)
+ else:
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ rbd.pool_metadata_remove(ioctx, "conf_rbd_cache")
+
+ for option in rbd.config_list(ioctx):
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+def test_pool_config_set_and_get_and_remove():
+ rbd = RBD()
+
+ for option in rbd.config_list(ioctx):
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ rbd.config_set(ioctx, "rbd_request_timed_out_seconds", "100")
+ new_value = rbd.config_get(ioctx, "rbd_request_timed_out_seconds")
+ eq(new_value, "100")
+ rbd.config_remove(ioctx, "rbd_request_timed_out_seconds")
+
+ for option in rbd.config_list(ioctx):
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+def test_namespaces():
+ rbd = RBD()
+
+ eq(False, rbd.namespace_exists(ioctx, 'ns1'))
+ eq([], rbd.namespace_list(ioctx))
+ assert_raises(ImageNotFound, rbd.namespace_remove, ioctx, 'ns1')
+
+ rbd.namespace_create(ioctx, 'ns1')
+ eq(True, rbd.namespace_exists(ioctx, 'ns1'))
+
+ assert_raises(ImageExists, rbd.namespace_create, ioctx, 'ns1')
+ eq(['ns1'], rbd.namespace_list(ioctx))
+ rbd.namespace_remove(ioctx, 'ns1')
+ eq([], rbd.namespace_list(ioctx))
+
+@require_new_format()
+def test_pool_stats():
+ rbd = RBD()
+
+ try:
+ image1 = create_image()
+ image2 = create_image()
+ image3 = create_image()
+ image4 = create_image()
+ with Image(ioctx, image4) as image:
+ image.create_snap('snap')
+ image.resize(0)
+
+ stats = rbd.pool_stats_get(ioctx)
+ eq(stats['image_count'], 4)
+ eq(stats['image_provisioned_bytes'], 3 * IMG_SIZE)
+ eq(stats['image_max_provisioned_bytes'], 4 * IMG_SIZE)
+ eq(stats['image_snap_count'], 1)
+ eq(stats['trash_count'], 0)
+ eq(stats['trash_provisioned_bytes'], 0)
+ eq(stats['trash_max_provisioned_bytes'], 0)
+ eq(stats['trash_snap_count'], 0)
+ finally:
+ rbd.remove(ioctx, image1)
+ rbd.remove(ioctx, image2)
+ rbd.remove(ioctx, image3)
+ with Image(ioctx, image4) as image:
+ image.remove_snap('snap')
+ rbd.remove(ioctx, image4)
+
+def rand_data(size):
+ return os.urandom(size)
+
+def check_stat(info, size, order):
+ assert 'block_name_prefix' in info
+ eq(info['size'], size)
+ eq(info['order'], order)
+ eq(info['num_objs'], size // (1 << order))
+ eq(info['obj_size'], 1 << order)
+
+@require_new_format()
+def test_features_to_string():
+ rbd = RBD()
+ features = RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
+ | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
+ expected_features_string = "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
+ features_string = rbd.features_to_string(features)
+ eq(expected_features_string, features_string)
+
+ features = RBD_FEATURE_LAYERING
+ features_string = rbd.features_to_string(features)
+ eq(features_string, "layering")
+
+ features = 16777216
+ assert_raises(InvalidArgument, rbd.features_to_string, features)
+
+@require_new_format()
+def test_features_from_string():
+ rbd = RBD()
+ features_string = "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
+ expected_features_bitmask = RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
+ | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
+ features = rbd.features_from_string(features_string)
+ eq(expected_features_bitmask, features)
+
+ features_string = "layering"
+ features = rbd.features_from_string(features_string)
+ eq(features, RBD_FEATURE_LAYERING)
+
+class TestImage(object):
+
+ def setup_method(self, method):
+ self.rbd = RBD()
+ create_image()
+ self.image = Image(ioctx, image_name)
+
+ def teardown_method(self, method):
+ self.image.close()
+ remove_image()
+ self.image = None
+
+ @require_new_format()
+ @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
+ def test_update_features(self):
+ features = self.image.features()
+ self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True)
+ eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features())
+
+ @require_features([RBD_FEATURE_STRIPINGV2])
+ def test_create_with_params(self):
+ global features
+ image_name = get_temp_image_name()
+ order = 20
+ stripe_unit = 1 << 20
+ stripe_count = 10
+ self.rbd.create(ioctx, image_name, IMG_SIZE, order,
+ False, features, stripe_unit, stripe_count)
+ image = Image(ioctx, image_name)
+ info = image.stat()
+ check_stat(info, IMG_SIZE, order)
+ eq(image.features(), features)
+ eq(image.stripe_unit(), stripe_unit)
+ eq(image.stripe_count(), stripe_count)
+ image.close()
+ RBD().remove(ioctx, image_name)
+
+ @require_new_format()
+ def test_id(self):
+ assert_not_equal(b'', self.image.id())
+
+ def test_block_name_prefix(self):
+ assert_not_equal(b'', self.image.block_name_prefix())
+
+ def test_data_pool_id(self):
+ assert_greater_equal(self.image.data_pool_id(), 0)
+
+ def test_create_timestamp(self):
+ timestamp = self.image.create_timestamp()
+ assert_not_equal(0, timestamp.year)
+ assert_not_equal(1970, timestamp.year)
+
+ def test_access_timestamp(self):
+ timestamp = self.image.access_timestamp()
+ assert_not_equal(0, timestamp.year)
+ assert_not_equal(1970, timestamp.year)
+
+ def test_modify_timestamp(self):
+ timestamp = self.image.modify_timestamp()
+ assert_not_equal(0, timestamp.year)
+ assert_not_equal(1970, timestamp.year)
+
+ def test_invalidate_cache(self):
+ self.image.write(b'abc', 0)
+ eq(b'abc', self.image.read(0, 3))
+ self.image.invalidate_cache()
+ eq(b'abc', self.image.read(0, 3))
+
+ def test_stat(self):
+ info = self.image.stat()
+ check_stat(info, IMG_SIZE, IMG_ORDER)
+
+ def test_flags(self):
+ flags = self.image.flags()
+ eq(0, flags)
+
+ def test_image_auto_close(self):
+ image = Image(ioctx, image_name)
+
+ def test_use_after_close(self):
+ self.image.close()
+ assert_raises(InvalidArgument, self.image.stat)
+
+ def test_write(self):
+ data = rand_data(256)
+ self.image.write(data, 0)
+
+ def test_write_with_fadvise_flags(self):
+ data = rand_data(256)
+ self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
+ self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE)
+
+ def test_write_zeroes(self):
+ data = rand_data(256)
+ self.image.write(data, 0)
+ self.image.write_zeroes(0, 256)
+ eq(self.image.read(256, 256), b'\0' * 256)
+ check_diff(self.image, 0, IMG_SIZE, None, [])
+
+ def test_write_zeroes_thick_provision(self):
+ data = rand_data(256)
+ self.image.write(data, 0)
+ self.image.write_zeroes(0, 256, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION)
+ eq(self.image.read(256, 256), b'\0' * 256)
+ check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)])
+
+ def test_read(self):
+ data = self.image.read(0, 20)
+ eq(data, b'\0' * 20)
+
+ def test_read_with_fadvise_flags(self):
+ data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
+ eq(data, b'\0' * 20)
+ data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM)
+ eq(data, b'\0' * 20)
+
+ def test_large_write(self):
+ data = rand_data(IMG_SIZE)
+ self.image.write(data, 0)
+
+ def test_large_read(self):
+ data = self.image.read(0, IMG_SIZE)
+ eq(data, b'\0' * IMG_SIZE)
+
+ def test_write_read(self):
+ data = rand_data(256)
+ offset = 50
+ self.image.write(data, offset)
+ read = self.image.read(offset, 256)
+ eq(data, read)
+
+ def test_read_bad_offset(self):
+ assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE)
+
+ def test_resize(self):
+ new_size = IMG_SIZE * 2
+ self.image.resize(new_size)
+ info = self.image.stat()
+ check_stat(info, new_size, IMG_ORDER)
+
+ def test_resize_allow_shrink_False(self):
+ new_size = IMG_SIZE * 2
+ self.image.resize(new_size)
+ info = self.image.stat()
+ check_stat(info, new_size, IMG_ORDER)
+ assert_raises(InvalidArgument, self.image.resize, IMG_SIZE, False)
+
+ def test_size(self):
+ eq(IMG_SIZE, self.image.size())
+ self.image.create_snap('snap1')
+ new_size = IMG_SIZE * 2
+ self.image.resize(new_size)
+ eq(new_size, self.image.size())
+ self.image.create_snap('snap2')
+ self.image.set_snap('snap2')
+ eq(new_size, self.image.size())
+ self.image.set_snap('snap1')
+ eq(IMG_SIZE, self.image.size())
+ self.image.set_snap(None)
+ eq(new_size, self.image.size())
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+
+ def test_resize_down(self):
+ new_size = IMG_SIZE // 2
+ data = rand_data(256)
+ self.image.write(data, IMG_SIZE // 2);
+ self.image.resize(new_size)
+ self.image.resize(IMG_SIZE)
+ read = self.image.read(IMG_SIZE // 2, 256)
+ eq(b'\0' * 256, read)
+
+ def test_resize_bytes(self):
+ new_size = IMG_SIZE // 2 - 5
+ data = rand_data(256)
+ self.image.write(data, IMG_SIZE // 2 - 10);
+ self.image.resize(new_size)
+ self.image.resize(IMG_SIZE)
+ read = self.image.read(IMG_SIZE // 2 - 10, 5)
+ eq(data[:5], read)
+ read = self.image.read(IMG_SIZE // 2 - 5, 251)
+ eq(b'\0' * 251, read)
+
+ def _test_copy(self, features=None, order=None, stripe_unit=None,
+ stripe_count=None):
+ global ioctx
+ data = rand_data(256)
+ self.image.write(data, 256)
+ image_name = get_temp_image_name()
+ if features is None:
+ self.image.copy(ioctx, image_name)
+ elif order is None:
+ self.image.copy(ioctx, image_name, features)
+ elif stripe_unit is None:
+ self.image.copy(ioctx, image_name, features, order)
+ elif stripe_count is None:
+ self.image.copy(ioctx, image_name, features, order, stripe_unit)
+ else:
+ self.image.copy(ioctx, image_name, features, order, stripe_unit,
+ stripe_count)
+ assert_raises(ImageExists, self.image.copy, ioctx, image_name)
+ copy = Image(ioctx, image_name)
+ copy_data = copy.read(256, 256)
+ copy.close()
+ self.rbd.remove(ioctx, image_name)
+ eq(data, copy_data)
+
+ def test_copy(self):
+ self._test_copy()
+
+ def test_copy2(self):
+ self._test_copy(self.image.features(), self.image.stat()['order'])
+
+ @require_features([RBD_FEATURE_STRIPINGV2])
+ def test_copy3(self):
+ global features
+ self._test_copy(features, self.image.stat()['order'],
+ self.image.stripe_unit(), self.image.stripe_count())
+
+ @pytest.mark.skip_if_crimson
+ def test_deep_copy(self):
+ global ioctx
+ global features
+ self.image.write(b'a' * 256, 0)
+ self.image.create_snap('snap1')
+ self.image.write(b'b' * 256, 0)
+ dst_name = get_temp_image_name()
+ self.image.deep_copy(ioctx, dst_name, features=features,
+ order=self.image.stat()['order'],
+ stripe_unit=self.image.stripe_unit(),
+ stripe_count=self.image.stripe_count(),
+ data_pool=None)
+ self.image.remove_snap('snap1')
+ with Image(ioctx, dst_name, 'snap1') as copy:
+ copy_data = copy.read(0, 256)
+ eq(b'a' * 256, copy_data)
+ with Image(ioctx, dst_name) as copy:
+ copy_data = copy.read(0, 256)
+ eq(b'b' * 256, copy_data)
+ copy.remove_snap('snap1')
+ self.rbd.remove(ioctx, dst_name)
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_deep_copy_clone(self):
+ global ioctx
+ global features
+ self.image.write(b'a' * 256, 0)
+ self.image.create_snap('snap1')
+ self.image.write(b'b' * 256, 0)
+ self.image.protect_snap('snap1')
+ clone_name = get_temp_image_name()
+ dst_name = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name)
+ with Image(ioctx, clone_name) as child:
+ child.create_snap('snap1')
+ child.deep_copy(ioctx, dst_name, features=features,
+ order=self.image.stat()['order'],
+ stripe_unit=self.image.stripe_unit(),
+ stripe_count=self.image.stripe_count(),
+ data_pool=None)
+ child.remove_snap('snap1')
+
+ with Image(ioctx, dst_name) as copy:
+ copy_data = copy.read(0, 256)
+ eq(b'a' * 256, copy_data)
+ copy.remove_snap('snap1')
+ self.rbd.remove(ioctx, dst_name)
+ self.rbd.remove(ioctx, clone_name)
+ self.image.unprotect_snap('snap1')
+ self.image.remove_snap('snap1')
+
+ def test_create_snap(self):
+ global ioctx
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ at_snapshot = Image(ioctx, image_name, 'snap1')
+ snap_data = at_snapshot.read(0, 256)
+ at_snapshot.close()
+ eq(snap_data, b'\0' * 256)
+ self.image.remove_snap('snap1')
+
+ def test_create_snap_exists(self):
+ self.image.create_snap('snap1')
+ assert_raises(ImageExists, self.image.create_snap, 'snap1')
+ self.image.remove_snap('snap1')
+
+ def test_create_snap_flags(self):
+ self.image.create_snap('snap1', 0)
+ self.image.remove_snap('snap1')
+ self.image.create_snap('snap1', RBD_SNAP_CREATE_SKIP_QUIESCE)
+ self.image.remove_snap('snap1')
+ self.image.create_snap('snap1', RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR)
+ self.image.remove_snap('snap1')
+
+ def test_list_snaps(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
+ self.image.create_snap('snap2')
+ eq(['snap1', 'snap2'], [snap['name'] for snap in self.image.list_snaps()])
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+
+ def test_list_snaps_iterator_auto_close(self):
+ self.image.create_snap('snap1')
+ self.image.list_snaps()
+ self.image.remove_snap('snap1')
+
+ def test_remove_snap(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
+ self.image.remove_snap('snap1')
+ eq([], list(self.image.list_snaps()))
+
+ def test_remove_snap_not_found(self):
+ assert_raises(ImageNotFound, self.image.remove_snap, 'snap1')
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_remove_snap2(self):
+ self.image.create_snap('snap1')
+ self.image.protect_snap('snap1')
+ assert(self.image.is_protected_snap('snap1'))
+ self.image.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT)
+ eq([], list(self.image.list_snaps()))
+
+ def test_remove_snap_by_id(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
+ for snap in self.image.list_snaps():
+ snap_id = snap["id"]
+ self.image.remove_snap_by_id(snap_id)
+ eq([], list(self.image.list_snaps()))
+
+ def test_rename_snap(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
+ self.image.rename_snap("snap1", "snap1-rename")
+ eq(['snap1-rename'], [snap['name'] for snap in self.image.list_snaps()])
+ self.image.remove_snap('snap1-rename')
+ eq([], list(self.image.list_snaps()))
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_protect_snap(self):
+ self.image.create_snap('snap1')
+ assert(not self.image.is_protected_snap('snap1'))
+ self.image.protect_snap('snap1')
+ assert(self.image.is_protected_snap('snap1'))
+ assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
+ self.image.unprotect_snap('snap1')
+ assert(not self.image.is_protected_snap('snap1'))
+ self.image.remove_snap('snap1')
+ assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1')
+ assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1')
+
+ def test_snap_exists(self):
+ self.image.create_snap('snap1')
+ eq(self.image.snap_exists('snap1'), True)
+ self.image.remove_snap('snap1')
+ eq(self.image.snap_exists('snap1'), False)
+
+ def test_snap_timestamp(self):
+ self.image.create_snap('snap1')
+ eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
+ for snap in self.image.list_snaps():
+ snap_id = snap["id"]
+ time = self.image.get_snap_timestamp(snap_id)
+ assert_not_equal(b'', time.year)
+ assert_not_equal(0, time.year)
+ assert_not_equal(time.year, '1970')
+ self.image.remove_snap('snap1')
+
+ def test_limit_snaps(self):
+ self.image.set_snap_limit(2)
+ eq(2, self.image.get_snap_limit())
+ self.image.create_snap('snap1')
+ self.image.create_snap('snap2')
+ assert_raises(DiskQuotaExceeded, self.image.create_snap, 'snap3')
+ self.image.remove_snap_limit()
+ self.image.create_snap('snap3')
+
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+ self.image.remove_snap('snap3')
+
+ @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
+ def test_remove_with_exclusive_lock(self):
+ assert_raises(ImageBusy, remove_image)
+
+ @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
+ def test_remove_with_snap(self):
+ self.image.create_snap('snap1')
+ assert_raises(ImageHasSnapshots, remove_image)
+ self.image.remove_snap('snap1')
+
+ @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
+ def test_remove_with_watcher(self):
+ data = rand_data(256)
+ self.image.write(data, 0)
+ assert_raises(ImageBusy, remove_image)
+ read = self.image.read(0, 256)
+ eq(read, data)
+
+ def test_rollback_to_snap(self):
+ self.image.write(b'\0' * 256, 0)
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.rollback_to_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ self.image.remove_snap('snap1')
+
+ def test_rollback_to_snap_sparse(self):
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.rollback_to_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ self.image.remove_snap('snap1')
+
+ def test_rollback_with_resize(self):
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, data)
+ new_size = IMG_SIZE * 2
+ self.image.resize(new_size)
+ check_stat(self.image.stat(), new_size, IMG_ORDER)
+ self.image.write(data, new_size - 256)
+ self.image.create_snap('snap2')
+ read = self.image.read(new_size - 256, 256)
+ eq(read, data)
+ self.image.rollback_to_snap('snap1')
+ check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER)
+ assert_raises(InvalidArgument, self.image.read, new_size - 256, 256)
+ self.image.rollback_to_snap('snap2')
+ check_stat(self.image.stat(), new_size, IMG_ORDER)
+ read = self.image.read(new_size - 256, 256)
+ eq(read, data)
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+
+ def test_set_snap(self):
+ self.image.write(b'\0' * 256, 0)
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.set_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ assert_raises(ReadOnlyImage, self.image.write, data, 0)
+ self.image.remove_snap('snap1')
+
+ def test_set_no_snap(self):
+ self.image.write(b'\0' * 256, 0)
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.set_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ assert_raises(ReadOnlyImage, self.image.write, data, 0)
+ self.image.set_snap(None)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.remove_snap('snap1')
+
+ def test_set_snap_by_id(self):
+ self.image.write(b'\0' * 256, 0)
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ snaps = list(self.image.list_snaps())
+ self.image.set_snap_by_id(snaps[0]['id'])
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ assert_raises(ReadOnlyImage, self.image.write, data, 0)
+ self.image.set_snap_by_id(None)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.remove_snap('snap1')
+
+ def test_snap_get_name(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ self.image.create_snap('snap2')
+ self.image.create_snap('snap3')
+
+ for snap in self.image.list_snaps():
+ expected_snap_name = self.image.snap_get_name(snap['id'])
+ eq(expected_snap_name, snap['name'])
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+ self.image.remove_snap('snap3')
+ eq([], list(self.image.list_snaps()))
+
+ assert_raises(ImageNotFound, self.image.snap_get_name, 1)
+
+ def test_snap_get_id(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ self.image.create_snap('snap2')
+ self.image.create_snap('snap3')
+
+ for snap in self.image.list_snaps():
+ expected_snap_id = self.image.snap_get_id(snap['name'])
+ eq(expected_snap_id, snap['id'])
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+ self.image.remove_snap('snap3')
+ eq([], list(self.image.list_snaps()))
+
+ assert_raises(ImageNotFound, self.image.snap_get_id, 'snap1')
+
+ def test_set_snap_sparse(self):
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.set_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ assert_raises(ReadOnlyImage, self.image.write, data, 0)
+ self.image.remove_snap('snap1')
+
+ def test_many_snaps(self):
+ num_snaps = 200
+ for i in range(num_snaps):
+ self.image.create_snap(str(i))
+ snaps = sorted(self.image.list_snaps(),
+ key=lambda snap: int(snap['name']))
+ eq(len(snaps), num_snaps)
+ for i, snap in enumerate(snaps):
+ eq(snap['size'], IMG_SIZE)
+ eq(snap['name'], str(i))
+ for i in range(num_snaps):
+ self.image.remove_snap(str(i))
+
+ def test_set_snap_deleted(self):
+ self.image.write(b'\0' * 256, 0)
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.set_snap('snap1')
+ self.image.remove_snap('snap1')
+ assert_raises(ImageNotFound, self.image.read, 0, 256)
+ self.image.set_snap(None)
+ read = self.image.read(0, 256)
+ eq(read, data)
+
+ def test_set_snap_recreated(self):
+ self.image.write(b'\0' * 256, 0)
+ self.image.create_snap('snap1')
+ read = self.image.read(0, 256)
+ eq(read, b'\0' * 256)
+ data = rand_data(256)
+ self.image.write(data, 0)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.set_snap('snap1')
+ self.image.remove_snap('snap1')
+ self.image.create_snap('snap1')
+ assert_raises(ImageNotFound, self.image.read, 0, 256)
+ self.image.set_snap(None)
+ read = self.image.read(0, 256)
+ eq(read, data)
+ self.image.remove_snap('snap1')
+
+ def test_lock_unlock(self):
+ assert_raises(ImageNotFound, self.image.unlock, '')
+ self.image.lock_exclusive('')
+ assert_raises(ImageExists, self.image.lock_exclusive, '')
+ assert_raises(ImageBusy, self.image.lock_exclusive, 'test')
+ assert_raises(ImageExists, self.image.lock_shared, '', '')
+ assert_raises(ImageBusy, self.image.lock_shared, 'foo', '')
+ self.image.unlock('')
+
+ def test_list_lockers(self):
+ eq([], self.image.list_lockers())
+ self.image.lock_exclusive('test')
+ lockers = self.image.list_lockers()
+ eq(1, len(lockers['lockers']))
+ _, cookie, _ = lockers['lockers'][0]
+ eq(cookie, 'test')
+ eq('', lockers['tag'])
+ assert lockers['exclusive']
+ self.image.unlock('test')
+ eq([], self.image.list_lockers())
+
+ num_shared = 10
+ for i in range(num_shared):
+ self.image.lock_shared(str(i), 'tag')
+ lockers = self.image.list_lockers()
+ eq('tag', lockers['tag'])
+ assert not lockers['exclusive']
+ eq(num_shared, len(lockers['lockers']))
+ cookies = sorted(map(lambda x: x[1], lockers['lockers']))
+ for i in range(num_shared):
+ eq(str(i), cookies[i])
+ self.image.unlock(str(i))
+ eq([], self.image.list_lockers())
+
+ def test_diff_iterate(self):
+ check_diff(self.image, 0, IMG_SIZE, None, [])
+ self.image.write(b'a' * 256, 0)
+ check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)])
+ self.image.write(b'b' * 256, 256)
+ check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
+ self.image.discard(128, 256)
+ check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
+
+ self.image.create_snap('snap1')
+ self.image.discard(0, 1 << IMG_ORDER)
+ self.image.create_snap('snap2')
+ self.image.set_snap('snap2')
+ check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)])
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+
+ def test_aio_read(self):
+ # this is a list so that the local cb() can modify it
+ retval = [None]
+ def cb(_, buf):
+ retval[0] = buf
+
+ # test1: success case
+ comp = self.image.aio_read(0, 20, cb)
+ comp.wait_for_complete_and_cb()
+ eq(retval[0], b'\0' * 20)
+ eq(comp.get_return_value(), 20)
+ eq(sys.getrefcount(comp), 2)
+
+ # test2: error case
+ retval[0] = 1
+ comp = self.image.aio_read(IMG_SIZE, 20, cb)
+ comp.wait_for_complete_and_cb()
+ eq(None, retval[0])
+ assert(comp.get_return_value() < 0)
+ eq(sys.getrefcount(comp), 2)
+
+ def test_aio_write(self):
+ retval = [None]
+ def cb(comp):
+ retval[0] = comp.get_return_value()
+
+ data = rand_data(256)
+ comp = self.image.aio_write(data, 256, cb)
+ comp.wait_for_complete_and_cb()
+ eq(retval[0], 0)
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+ eq(self.image.read(256, 256), data)
+
+ def test_aio_discard(self):
+ retval = [None]
+ def cb(comp):
+ retval[0] = comp.get_return_value()
+
+ data = rand_data(256)
+ self.image.write(data, 0)
+ comp = self.image.aio_discard(0, 256, cb)
+ comp.wait_for_complete_and_cb()
+ eq(retval[0], 0)
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+ eq(self.image.read(256, 256), b'\0' * 256)
+
+ def test_aio_write_zeroes(self):
+ retval = [None]
+ def cb(comp):
+ retval[0] = comp.get_return_value()
+
+ data = rand_data(256)
+ self.image.write(data, 0)
+ comp = self.image.aio_write_zeroes(0, 256, cb)
+ comp.wait_for_complete_and_cb()
+ eq(retval[0], 0)
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+ eq(self.image.read(256, 256), b'\0' * 256)
+
+ def test_aio_flush(self):
+ retval = [None]
+ def cb(comp):
+ retval[0] = comp.get_return_value()
+
+ comp = self.image.aio_flush(cb)
+ comp.wait_for_complete_and_cb()
+ eq(retval[0], 0)
+ eq(sys.getrefcount(comp), 2)
+
+ def test_metadata(self):
+ metadata = list(self.image.metadata_list())
+ eq(len(metadata), 0)
+ assert_raises(KeyError, self.image.metadata_get, "key1")
+ self.image.metadata_set("key1", "value1")
+ self.image.metadata_set("key2", "value2")
+ value = self.image.metadata_get("key1")
+ eq(value, "value1")
+ value = self.image.metadata_get("key2")
+ eq(value, "value2")
+ metadata = list(self.image.metadata_list())
+ eq(len(metadata), 2)
+ self.image.metadata_remove("key1")
+ metadata = list(self.image.metadata_list())
+ eq(len(metadata), 1)
+ eq(metadata[0], ("key2", "value2"))
+ self.image.metadata_remove("key2")
+ assert_raises(KeyError, self.image.metadata_remove, "key2")
+ metadata = list(self.image.metadata_list())
+ eq(len(metadata), 0)
+
+ N = 65
+ for i in range(N):
+ self.image.metadata_set("key" + str(i), "X" * 1025)
+ metadata = list(self.image.metadata_list())
+ eq(len(metadata), N)
+ for i in range(N):
+ self.image.metadata_remove("key" + str(i))
+ metadata = list(self.image.metadata_list())
+ eq(len(metadata), N - i - 1)
+
+ def test_watchers_list(self):
+ watchers = list(self.image.watchers_list())
+ # The image is open (in r/w mode) from setup, so expect there to be one
+ # watcher.
+ eq(len(watchers), 1)
+
+ def test_config_list(self):
+ with Image(ioctx, image_name) as image:
+ for option in image.config_list():
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ image.metadata_set("conf_rbd_cache", "true")
+
+ for option in image.config_list():
+ if option['name'] == "rbd_cache":
+ eq(option['source'], RBD_CONFIG_SOURCE_IMAGE)
+ else:
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ image.metadata_remove("conf_rbd_cache")
+
+ for option in image.config_list():
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ def test_image_config_set_and_get_and_remove(self):
+ with Image(ioctx, image_name) as image:
+ for option in image.config_list():
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ image.config_set("rbd_request_timed_out_seconds", "100")
+ modify_value = image.config_get("rbd_request_timed_out_seconds")
+ eq(modify_value, '100')
+
+ image.config_remove("rbd_request_timed_out_seconds")
+
+ for option in image.config_list():
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ def test_sparsify(self):
+ assert_raises(InvalidArgument, self.image.sparsify, 16)
+ self.image.sparsify(4096)
+
+ @require_linux()
+ @blocklist_features([RBD_FEATURE_JOURNALING])
+ def test_encryption_luks1(self):
+ data = b'hello world'
+ offset = 16<<20
+ image_size = 32<<20
+
+ with Image(ioctx, image_name) as image:
+ image.resize(image_size)
+ image.write(data, offset)
+ image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, "password")
+ assert_not_equal(data, image.read(offset, len(data)))
+ with Image(ioctx, image_name) as image:
+ image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS1, "password")
+ assert_not_equal(data, image.read(offset, len(data)))
+ image.write(data, offset)
+ with Image(ioctx, image_name) as image:
+ image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, "password")
+ eq(data, image.read(offset, len(data)))
+
+ @require_linux()
+ @blocklist_features([RBD_FEATURE_JOURNALING])
+ def test_encryption_luks2(self):
+ data = b'hello world'
+ offset = 16<<20
+ image_size = 256<<20
+
+ with Image(ioctx, image_name) as image:
+ image.resize(image_size)
+ image.write(data, offset)
+ image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, "password")
+ assert_not_equal(data, image.read(offset, len(data)))
+ with Image(ioctx, image_name) as image:
+ image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS2, "password")
+ assert_not_equal(data, image.read(offset, len(data)))
+ image.write(data, offset)
+ with Image(ioctx, image_name) as image:
+ image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS, "password")
+ eq(data, image.read(offset, len(data)))
+
+
+class TestImageId(object):
+
+ def setup_method(self, method):
+ self.rbd = RBD()
+ create_image()
+ self.image = Image(ioctx, image_name)
+ self.image2 = Image(ioctx, None, None, False, self.image.id())
+
+ def teardown_method(self, method):
+ self.image.close()
+ self.image2.close()
+ remove_image()
+ self.image = None
+ self.image2 = None
+
+ def test_read(self):
+ data = self.image2.read(0, 20)
+ eq(data, b'\0' * 20)
+
+ def test_write(self):
+ data = rand_data(256)
+ self.image2.write(data, 0)
+
+ def test_resize(self):
+ new_size = IMG_SIZE * 2
+ self.image2.resize(new_size)
+ info = self.image2.stat()
+ check_stat(info, new_size, IMG_ORDER)
+
+def check_diff(image, offset, length, from_snapshot, expected):
+ extents = []
+ def cb(offset, length, exists):
+ extents.append((offset, length, exists))
+ image.diff_iterate(0, IMG_SIZE, None, cb)
+ eq(extents, expected)
+
+class TestClone(object):
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def setup_method(self, method):
+ global ioctx
+ global features
+ self.rbd = RBD()
+ create_image()
+ self.image = Image(ioctx, image_name)
+ data = rand_data(256)
+ self.image.write(data, IMG_SIZE // 2)
+ self.image.create_snap('snap1')
+ global features
+ self.image.protect_snap('snap1')
+ self.clone_name = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
+ features)
+ self.clone = Image(ioctx, self.clone_name)
+
+ def teardown_method(self, method):
+ global ioctx
+ self.clone.close()
+ self.rbd.remove(ioctx, self.clone_name)
+ self.image.unprotect_snap('snap1')
+ self.image.remove_snap('snap1')
+ self.image.close()
+ remove_image()
+
+ def _test_with_params(self, features=None, order=None, stripe_unit=None,
+ stripe_count=None):
+ self.image.create_snap('snap2')
+ self.image.protect_snap('snap2')
+ clone_name2 = get_temp_image_name()
+ if features is None:
+ self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2)
+ elif order is None:
+ self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
+ features)
+ elif stripe_unit is None:
+ self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
+ features, order)
+ elif stripe_count is None:
+ self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
+ features, order, stripe_unit)
+ else:
+ self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
+ features, order, stripe_unit, stripe_count)
+ self.rbd.remove(ioctx, clone_name2)
+ self.image.unprotect_snap('snap2')
+ self.image.remove_snap('snap2')
+
+ def test_with_params(self):
+ self._test_with_params()
+
+ def test_with_params2(self):
+ global features
+ self._test_with_params(features, self.image.stat()['order'])
+
+ @require_features([RBD_FEATURE_STRIPINGV2])
+ def test_with_params3(self):
+ global features
+ self._test_with_params(features, self.image.stat()['order'],
+ self.image.stripe_unit(),
+ self.image.stripe_count())
+
+ def test_stripe_unit_and_count(self):
+ global features
+ global ioctx
+ image_name = get_temp_image_name()
+ RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=False,
+ features=int(features), stripe_unit=1048576, stripe_count=8)
+ image = Image(ioctx, image_name)
+ image.create_snap('snap1')
+ image.protect_snap('snap1')
+ clone_name = get_temp_image_name()
+ RBD().clone(ioctx, image_name, 'snap1', ioctx, clone_name)
+ clone = Image(ioctx, clone_name)
+
+ eq(1048576, clone.stripe_unit())
+ eq(8, clone.stripe_count())
+
+ clone.close()
+ RBD().remove(ioctx, clone_name)
+ image.unprotect_snap('snap1')
+ image.remove_snap('snap1')
+ image.close()
+ RBD().remove(ioctx, image_name)
+
+
+ def test_unprotected(self):
+ self.image.create_snap('snap2')
+ global features
+ clone_name2 = get_temp_image_name()
+ rados.conf_set("rbd_default_clone_format", "1")
+ assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
+ 'snap2', ioctx, clone_name2, features)
+ rados.conf_set("rbd_default_clone_format", "auto")
+ self.image.remove_snap('snap2')
+
+ def test_unprotect_with_children(self):
+ global features
+ # can't remove a snapshot that has dependent clones
+ assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
+
+ # validate parent info of clone created by TestClone.setup_method
+ (pool, image, snap) = self.clone.parent_info()
+ eq(pool, pool_name)
+ eq(image, image_name)
+ eq(snap, 'snap1')
+ eq(self.image.id(), self.clone.parent_id())
+
+ # create a new pool...
+ pool_name2 = get_temp_pool_name()
+ rados.create_pool(pool_name2)
+ other_ioctx = rados.open_ioctx(pool_name2)
+ other_ioctx.application_enable('rbd')
+
+ # ...with a clone of the same parent
+ other_clone_name = get_temp_image_name()
+ rados.conf_set("rbd_default_clone_format", "1")
+ self.rbd.clone(ioctx, image_name, 'snap1', other_ioctx,
+ other_clone_name, features)
+ rados.conf_set("rbd_default_clone_format", "auto")
+ self.other_clone = Image(other_ioctx, other_clone_name)
+ # validate its parent info
+ (pool, image, snap) = self.other_clone.parent_info()
+ eq(pool, pool_name)
+ eq(image, image_name)
+ eq(snap, 'snap1')
+ eq(self.image.id(), self.other_clone.parent_id())
+
+ # can't unprotect snap with children
+ assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1')
+
+ # 2 children, check that cannot remove the parent snap
+ assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
+
+ # close and remove other pool's clone
+ self.other_clone.close()
+ self.rbd.remove(other_ioctx, other_clone_name)
+
+ # check that we cannot yet remove the parent snap
+ assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
+
+ other_ioctx.close()
+ rados.delete_pool(pool_name2)
+
+ # unprotect, remove parent snap happen in cleanup, and should succeed
+
+ def test_stat(self):
+ image_info = self.image.stat()
+ clone_info = self.clone.stat()
+ eq(clone_info['size'], image_info['size'])
+ eq(clone_info['size'], self.clone.overlap())
+
+ def test_resize_stat(self):
+ self.clone.resize(IMG_SIZE // 2)
+ image_info = self.image.stat()
+ clone_info = self.clone.stat()
+ eq(clone_info['size'], IMG_SIZE // 2)
+ eq(image_info['size'], IMG_SIZE)
+ eq(self.clone.overlap(), IMG_SIZE // 2)
+
+ self.clone.resize(IMG_SIZE * 2)
+ image_info = self.image.stat()
+ clone_info = self.clone.stat()
+ eq(clone_info['size'], IMG_SIZE * 2)
+ eq(image_info['size'], IMG_SIZE)
+ eq(self.clone.overlap(), IMG_SIZE // 2)
+
+ def test_resize_io(self):
+ parent_data = self.image.read(IMG_SIZE // 2, 256)
+ self.image.resize(0)
+ self.clone.resize(IMG_SIZE // 2 + 128)
+ child_data = self.clone.read(IMG_SIZE // 2, 128)
+ eq(child_data, parent_data[:128])
+ self.clone.resize(IMG_SIZE)
+ child_data = self.clone.read(IMG_SIZE // 2, 256)
+ eq(child_data, parent_data[:128] + (b'\0' * 128))
+ self.clone.resize(IMG_SIZE // 2 + 1)
+ child_data = self.clone.read(IMG_SIZE // 2, 1)
+ eq(child_data, parent_data[0:1])
+ self.clone.resize(0)
+ self.clone.resize(IMG_SIZE)
+ child_data = self.clone.read(IMG_SIZE // 2, 256)
+ eq(child_data, b'\0' * 256)
+
+ def test_read(self):
+ parent_data = self.image.read(IMG_SIZE // 2, 256)
+ child_data = self.clone.read(IMG_SIZE // 2, 256)
+ eq(child_data, parent_data)
+
+ def test_write(self):
+ parent_data = self.image.read(IMG_SIZE // 2, 256)
+ new_data = rand_data(256)
+ self.clone.write(new_data, IMG_SIZE // 2 + 256)
+ child_data = self.clone.read(IMG_SIZE // 2 + 256, 256)
+ eq(child_data, new_data)
+ child_data = self.clone.read(IMG_SIZE // 2, 256)
+ eq(child_data, parent_data)
+ parent_data = self.image.read(IMG_SIZE // 2 + 256, 256)
+ eq(parent_data, b'\0' * 256)
+
+ def check_children(self, expected):
+ actual = self.image.list_children()
+ # dedup for cache pools until
+ # http://tracker.ceph.com/issues/8187 is fixed
+ deduped = set([(pool_name, image[1]) for image in actual])
+ eq(deduped, set(expected))
+
+ def check_children2(self, expected):
+ actual = [{k:v for k,v in x.items() if k in expected[0]} \
+ for x in self.image.list_children2()]
+ eq(actual, expected)
+
+ def check_descendants(self, expected):
+ eq(list(self.image.list_descendants()), expected)
+
+ def get_image_id(self, ioctx, name):
+ with Image(ioctx, name) as image:
+ return image.id()
+
+ def test_list_children(self):
+ global ioctx
+ global features
+ self.image.set_snap('snap1')
+ self.check_children([(pool_name, self.clone_name)])
+ self.check_children2(
+ [{'pool': pool_name, 'pool_namespace': '',
+ 'image': self.clone_name, 'trash': False,
+ 'id': self.get_image_id(ioctx, self.clone_name)}])
+ self.check_descendants(
+ [{'pool': pool_name, 'pool_namespace': '',
+ 'image': self.clone_name, 'trash': False,
+ 'id': self.get_image_id(ioctx, self.clone_name)}])
+ self.clone.close()
+ self.rbd.remove(ioctx, self.clone_name)
+ eq(self.image.list_children(), [])
+ eq(list(self.image.list_children2()), [])
+ eq(list(self.image.list_descendants()), [])
+
+ clone_name = get_temp_image_name() + '_'
+ expected_children = []
+ expected_children2 = []
+ for i in range(10):
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx,
+ clone_name + str(i), features)
+ expected_children.append((pool_name, clone_name + str(i)))
+ expected_children2.append(
+ {'pool': pool_name, 'pool_namespace': '',
+ 'image': clone_name + str(i), 'trash': False,
+ 'id': self.get_image_id(ioctx, clone_name + str(i))})
+ self.check_children(expected_children)
+ self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
+
+ image6_id = self.get_image_id(ioctx, clone_name + str(5))
+ RBD().trash_move(ioctx, clone_name + str(5), 0)
+ expected_children.remove((pool_name, clone_name + str(5)))
+ for item in expected_children2:
+ for k, v in item.items():
+ if v == image6_id:
+ item["trash"] = True
+ self.check_children(expected_children)
+ self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
+
+ RBD().trash_restore(ioctx, image6_id, clone_name + str(5))
+ expected_children.append((pool_name, clone_name + str(5)))
+ for item in expected_children2:
+ for k, v in item.items():
+ if v == image6_id:
+ item["trash"] = False
+ self.check_children(expected_children)
+ self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
+
+ for i in range(10):
+ self.rbd.remove(ioctx, clone_name + str(i))
+ expected_children.remove((pool_name, clone_name + str(i)))
+ expected_children2.pop(0)
+ self.check_children(expected_children)
+ self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
+
+ eq(self.image.list_children(), [])
+ eq(list(self.image.list_children2()), [])
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
+ features)
+ self.check_children([(pool_name, self.clone_name)])
+ self.check_children2(
+ [{'pool': pool_name, 'pool_namespace': '',
+ 'image': self.clone_name, 'trash': False,
+ 'id': self.get_image_id(ioctx, self.clone_name)}])
+ self.check_descendants(
+ [{'pool': pool_name, 'pool_namespace': '',
+ 'image': self.clone_name, 'trash': False,
+ 'id': self.get_image_id(ioctx, self.clone_name)}])
+ self.clone = Image(ioctx, self.clone_name)
+
+ def test_flatten_errors(self):
+ # test that we can't flatten a non-clone
+ assert_raises(InvalidArgument, self.image.flatten)
+
+ # test that we can't flatten a snapshot
+ self.clone.create_snap('snap2')
+ self.clone.set_snap('snap2')
+ assert_raises(ReadOnlyImage, self.clone.flatten)
+ self.clone.remove_snap('snap2')
+
+ def check_flatten_with_order(self, new_order, stripe_unit=None,
+ stripe_count=None):
+ global ioctx
+ global features
+ clone_name2 = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
+ features, new_order, stripe_unit, stripe_count)
+ #with Image(ioctx, 'clone2') as clone:
+ clone2 = Image(ioctx, clone_name2)
+ clone2.flatten()
+ eq(clone2.overlap(), 0)
+ clone2.close()
+ self.rbd.remove(ioctx, clone_name2)
+
+ # flatten after resizing to non-block size
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
+ features, new_order, stripe_unit, stripe_count)
+ with Image(ioctx, clone_name2) as clone:
+ clone.resize(IMG_SIZE // 2 - 1)
+ clone.flatten()
+ eq(0, clone.overlap())
+ self.rbd.remove(ioctx, clone_name2)
+
+ # flatten after resizing to non-block size
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
+ features, new_order, stripe_unit, stripe_count)
+ with Image(ioctx, clone_name2) as clone:
+ clone.resize(IMG_SIZE // 2 + 1)
+ clone.flatten()
+ eq(clone.overlap(), 0)
+ self.rbd.remove(ioctx, clone_name2)
+
+ def test_flatten_basic(self):
+ self.check_flatten_with_order(IMG_ORDER)
+
+ def test_flatten_smaller_order(self):
+ self.check_flatten_with_order(IMG_ORDER - 2, 1048576, 1)
+
+ def test_flatten_larger_order(self):
+ self.check_flatten_with_order(IMG_ORDER + 2)
+
+ def test_flatten_drops_cache(self):
+ global ioctx
+ global features
+ clone_name2 = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
+ features, IMG_ORDER)
+ with Image(ioctx, clone_name2) as clone:
+ with Image(ioctx, clone_name2) as clone2:
+ # cache object non-existence
+ data = clone.read(IMG_SIZE // 2, 256)
+ clone2_data = clone2.read(IMG_SIZE // 2, 256)
+ eq(data, clone2_data)
+ clone.flatten()
+ assert_raises(ImageNotFound, clone.parent_info)
+ assert_raises(ImageNotFound, clone2.parent_info)
+ assert_raises(ImageNotFound, clone.parent_id)
+ assert_raises(ImageNotFound, clone2.parent_id)
+ after_flatten = clone.read(IMG_SIZE // 2, 256)
+ eq(data, after_flatten)
+ after_flatten = clone2.read(IMG_SIZE // 2, 256)
+ eq(data, after_flatten)
+ self.rbd.remove(ioctx, clone_name2)
+
+ def test_flatten_multi_level(self):
+ self.clone.create_snap('snap2')
+ self.clone.protect_snap('snap2')
+ clone_name3 = get_temp_image_name()
+ self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
+ features)
+ self.clone.flatten()
+ with Image(ioctx, clone_name3) as clone3:
+ clone3.flatten()
+ self.clone.unprotect_snap('snap2')
+ self.clone.remove_snap('snap2')
+ self.rbd.remove(ioctx, clone_name3)
+
+ def test_flatten_with_progress(self):
+ d = {'received_callback': False}
+ def progress_cb(current, total):
+ d['received_callback'] = True
+ return 0
+
+ global ioctx
+ global features
+ clone_name = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name,
+ features, 0)
+ with Image(ioctx, clone_name) as clone:
+ clone.flatten(on_progress=progress_cb)
+ self.rbd.remove(ioctx, clone_name)
+ eq(True, d['received_callback'])
+
+ def test_resize_flatten_multi_level(self):
+ self.clone.create_snap('snap2')
+ self.clone.protect_snap('snap2')
+ clone_name3 = get_temp_image_name()
+ self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
+ features)
+ self.clone.resize(1)
+ orig_data = self.image.read(0, 256)
+ with Image(ioctx, clone_name3) as clone3:
+ clone3_data = clone3.read(0, 256)
+ eq(orig_data, clone3_data)
+ self.clone.flatten()
+ with Image(ioctx, clone_name3) as clone3:
+ clone3_data = clone3.read(0, 256)
+ eq(orig_data, clone3_data)
+ self.rbd.remove(ioctx, clone_name3)
+ self.clone.unprotect_snap('snap2')
+ self.clone.remove_snap('snap2')
+
+ def test_trash_snapshot(self):
+ self.image.create_snap('snap2')
+ global features
+ clone_name = get_temp_image_name()
+ rados.conf_set("rbd_default_clone_format", "2")
+ self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name, features)
+ rados.conf_set("rbd_default_clone_format", "auto")
+
+ self.image.remove_snap('snap2')
+
+ snaps = [s for s in self.image.list_snaps() if s['name'] != 'snap1']
+ eq([RBD_SNAP_NAMESPACE_TYPE_TRASH], [s['namespace'] for s in snaps])
+ eq([{'original_name' : 'snap2'}], [s['trash'] for s in snaps])
+
+ self.rbd.remove(ioctx, clone_name)
+ eq([], [s for s in self.image.list_snaps() if s['name'] != 'snap1'])
+
+ @require_linux()
+ @blocklist_features([RBD_FEATURE_JOURNALING])
+ def test_encryption_luks1(self):
+ data = b'hello world'
+ offset = 16<<20
+ image_size = 32<<20
+
+ self.clone.resize(image_size)
+ self.clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, "password")
+ self.clone.encryption_load2(
+ ((RBD_ENCRYPTION_FORMAT_LUKS1, "password"),))
+ self.clone.write(data, offset)
+ eq(self.clone.read(0, 16), self.image.read(0, 16))
+
+ @require_linux()
+ @blocklist_features([RBD_FEATURE_JOURNALING])
+ def test_encryption_luks2(self):
+ data = b'hello world'
+ offset = 16<<20
+ image_size = 64<<20
+
+ self.clone.resize(image_size)
+ self.clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, "password")
+ self.clone.encryption_load2(
+ ((RBD_ENCRYPTION_FORMAT_LUKS2, "password"),))
+ self.clone.write(data, offset)
+ eq(self.clone.read(0, 16), self.image.read(0, 16))
+
+class TestExclusiveLock(object):
+
+ @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
+ def setup_method(self, method):
+ global rados2
+ rados2 = Rados(conffile='')
+ rados2.connect()
+ global ioctx2
+ ioctx2 = rados2.open_ioctx(pool_name)
+ create_image()
+
+ def teardown_method(self, method):
+ remove_image()
+ global ioctx2
+ ioctx2.close()
+ global rados2
+ rados2.shutdown()
+
+ def test_ownership(self):
+ with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
+ image1.write(b'0'*256, 0)
+ eq(image1.is_exclusive_lock_owner(), True)
+ eq(image2.is_exclusive_lock_owner(), False)
+
+ def test_snapshot_leadership(self):
+ with Image(ioctx, image_name) as image:
+ image.create_snap('snap')
+ eq(image.is_exclusive_lock_owner(), True)
+ try:
+ with Image(ioctx, image_name) as image:
+ image.write(b'0'*256, 0)
+ eq(image.is_exclusive_lock_owner(), True)
+ image.set_snap('snap')
+ eq(image.is_exclusive_lock_owner(), False)
+ with Image(ioctx, image_name, snapshot='snap') as image:
+ eq(image.is_exclusive_lock_owner(), False)
+ finally:
+ with Image(ioctx, image_name) as image:
+ image.remove_snap('snap')
+
+ def test_read_only_leadership(self):
+ with Image(ioctx, image_name, read_only=True) as image:
+ eq(image.is_exclusive_lock_owner(), False)
+
+ def test_follower_flatten(self):
+ with Image(ioctx, image_name) as image:
+ image.create_snap('snap')
+ image.protect_snap('snap')
+ try:
+ RBD().clone(ioctx, image_name, 'snap', ioctx, 'clone', features)
+ with Image(ioctx, 'clone') as image1, Image(ioctx2, 'clone') as image2:
+ data = rand_data(256)
+ image1.write(data, 0)
+ image2.flatten()
+ assert_raises(ImageNotFound, image1.parent_info)
+ assert_raises(ImageNotFound, image1.parent_id)
+ parent = True
+ for x in range(30):
+ try:
+ image2.parent_info()
+ except ImageNotFound:
+ parent = False
+ break
+ eq(False, parent)
+ finally:
+ RBD().remove(ioctx, 'clone')
+ with Image(ioctx, image_name) as image:
+ image.unprotect_snap('snap')
+ image.remove_snap('snap')
+
+ def test_follower_resize(self):
+ with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
+ image1.write(b'0'*256, 0)
+ for new_size in [IMG_SIZE * 2, IMG_SIZE // 2]:
+ image2.resize(new_size);
+ eq(new_size, image1.size())
+ for x in range(30):
+ if new_size == image2.size():
+ break
+ time.sleep(1)
+ eq(new_size, image2.size())
+
+ def test_follower_snap_create(self):
+ with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
+ image2.create_snap('snap1')
+ image1.remove_snap('snap1')
+
+ def test_follower_snap_rollback(self):
+ with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
+ image1.create_snap('snap')
+ try:
+ assert_raises(ReadOnlyImage, image2.rollback_to_snap, 'snap')
+ image1.rollback_to_snap('snap')
+ finally:
+ image1.remove_snap('snap')
+
+ def test_follower_discard(self):
+ global rados
+ with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
+ data = rand_data(256)
+ image1.write(data, 0)
+ image2.discard(0, 256)
+ eq(image1.is_exclusive_lock_owner(), False)
+ eq(image2.is_exclusive_lock_owner(), True)
+ read = image2.read(0, 256)
+ if rados.conf_get('rbd_skip_partial_discard') == 'false':
+ eq(256 * b'\0', read)
+ else:
+ eq(data, read)
+
+ def test_follower_write(self):
+ with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
+ data = rand_data(256)
+ image1.write(data, 0)
+ image2.write(data, IMG_SIZE // 2)
+ eq(image1.is_exclusive_lock_owner(), False)
+ eq(image2.is_exclusive_lock_owner(), True)
+ for offset in [0, IMG_SIZE // 2]:
+ read = image2.read(offset, 256)
+ eq(data, read)
+ def test_acquire_release_lock(self):
+ with Image(ioctx, image_name) as image:
+ image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
+ image.lock_release()
+
+ @pytest.mark.skip_if_crimson
+ def test_break_lock(self):
+ blocklist_rados = Rados(conffile='')
+ blocklist_rados.connect()
+ try:
+ blocklist_ioctx = blocklist_rados.open_ioctx(pool_name)
+ try:
+ rados2.conf_set('rbd_blocklist_on_break_lock', 'true')
+ with Image(ioctx2, image_name) as image, \
+ Image(blocklist_ioctx, image_name) as blocklist_image:
+
+ lock_owners = list(image.lock_get_owners())
+ eq(0, len(lock_owners))
+
+ blocklist_image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
+ assert_raises(ReadOnlyImage, image.lock_acquire,
+ RBD_LOCK_MODE_EXCLUSIVE)
+ lock_owners = list(image.lock_get_owners())
+ eq(1, len(lock_owners))
+ eq(RBD_LOCK_MODE_EXCLUSIVE, lock_owners[0]['mode'])
+ image.lock_break(RBD_LOCK_MODE_EXCLUSIVE,
+ lock_owners[0]['owner'])
+
+ assert_raises(ConnectionShutdown,
+ blocklist_image.is_exclusive_lock_owner)
+
+ blocklist_rados.wait_for_latest_osdmap()
+ data = rand_data(256)
+ assert_raises(ConnectionShutdown,
+ blocklist_image.write, data, 0)
+
+ image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
+
+ try:
+ blocklist_image.close()
+ except ConnectionShutdown:
+ pass
+ finally:
+ blocklist_ioctx.close()
+ finally:
+ blocklist_rados.shutdown()
+
+class TestMirroring(object):
+
+ @staticmethod
+ def check_info(info, global_id, state, primary=None):
+ eq(global_id, info['global_id'])
+ eq(state, info['state'])
+ if primary is not None:
+ eq(primary, info['primary'])
+
+ def setup_method(self, method):
+ self.rbd = RBD()
+ self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx)
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
+ create_image()
+ self.image = Image(ioctx, image_name)
+
+ def teardown_method(self, method):
+ self.image.close()
+ remove_image()
+ self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode)
+
+ def test_uuid(self):
+ mirror_uuid = self.rbd.mirror_uuid_get(ioctx)
+ assert(mirror_uuid)
+
+ def test_site_name(self):
+ site_name = "us-west-1"
+ self.rbd.mirror_site_name_set(rados, site_name)
+ eq(site_name, self.rbd.mirror_site_name_get(rados))
+ self.rbd.mirror_site_name_set(rados, "")
+ eq(rados.get_fsid(), self.rbd.mirror_site_name_get(rados))
+
+ def test_mirror_peer_bootstrap(self):
+ eq([], list(self.rbd.mirror_peer_list(ioctx)))
+
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED)
+ assert_raises(InvalidArgument, self.rbd.mirror_peer_bootstrap_create,
+ ioctx);
+
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
+ token_b64 = self.rbd.mirror_peer_bootstrap_create(ioctx)
+ token = base64.b64decode(token_b64)
+ token_dict = json.loads(token)
+ eq(sorted(['fsid', 'client_id', 'key', 'mon_host']),
+ sorted(list(token_dict.keys())))
+
+ # requires different cluster
+ assert_raises(InvalidArgument, self.rbd.mirror_peer_bootstrap_import,
+ ioctx, RBD_MIRROR_PEER_DIRECTION_RX, token_b64)
+
+ def test_mirror_peer(self):
+ eq([], list(self.rbd.mirror_peer_list(ioctx)))
+ site_name = "test_site"
+ client_name = "test_client"
+ uuid = self.rbd.mirror_peer_add(ioctx, site_name, client_name,
+ direction=RBD_MIRROR_PEER_DIRECTION_RX_TX)
+ assert(uuid)
+ peer = {
+ 'uuid' : uuid,
+ 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ 'site_name' : site_name,
+ 'cluster_name' : site_name,
+ 'mirror_uuid': '',
+ 'client_name' : client_name,
+ }
+ eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
+ cluster_name = "test_cluster1"
+ self.rbd.mirror_peer_set_cluster(ioctx, uuid, cluster_name)
+ client_name = "test_client1"
+ self.rbd.mirror_peer_set_client(ioctx, uuid, client_name)
+ peer = {
+ 'uuid' : uuid,
+ 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ 'site_name' : cluster_name,
+ 'cluster_name' : cluster_name,
+ 'mirror_uuid': '',
+ 'client_name' : client_name,
+ }
+ eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
+
+ attribs = {
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST: 'host1',
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY: 'abc'
+ }
+ self.rbd.mirror_peer_set_attributes(ioctx, uuid, attribs)
+ eq(attribs, self.rbd.mirror_peer_get_attributes(ioctx, uuid))
+
+ self.rbd.mirror_peer_remove(ioctx, uuid)
+ eq([], list(self.rbd.mirror_peer_list(ioctx)))
+
+ @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
+ RBD_FEATURE_JOURNALING])
+ def test_mirror_image(self):
+
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
+ self.image.mirror_image_disable(True)
+ info = self.image.mirror_image_get_info()
+ self.check_info(info, '', RBD_MIRROR_IMAGE_DISABLED, False)
+
+ self.image.mirror_image_enable()
+ info = self.image.mirror_image_get_info()
+ global_id = info['global_id']
+ self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
+
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
+ fail = False
+ try:
+ self.image.mirror_image_disable(True)
+ except InvalidArgument:
+ fail = True
+ eq(True, fail) # Fails because of mirror mode pool
+
+ self.image.mirror_image_demote()
+ info = self.image.mirror_image_get_info()
+ self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, False)
+
+ entries = dict(self.rbd.mirror_image_info_list(ioctx))
+ info['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL;
+ eq(info, entries[self.image.id()])
+
+ self.image.mirror_image_resync()
+
+ self.image.mirror_image_promote(True)
+ info = self.image.mirror_image_get_info()
+ self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
+
+ entries = dict(self.rbd.mirror_image_info_list(ioctx))
+ info['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL;
+ eq(info, entries[self.image.id()])
+
+ fail = False
+ try:
+ self.image.mirror_image_resync()
+ except InvalidArgument:
+ fail = True
+ eq(True, fail) # Fails because it is primary
+
+ status = self.image.mirror_image_get_status()
+ eq(image_name, status['name'])
+ eq(False, status['up'])
+ eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
+ info = status['info']
+ self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
+
+ @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
+ RBD_FEATURE_JOURNALING])
+ def test_mirror_image_status(self):
+ info = self.image.mirror_image_get_info()
+ global_id = info['global_id']
+ state = info['state']
+ primary = info['primary']
+
+ status = self.image.mirror_image_get_status()
+ eq(image_name, status['name'])
+ eq(False, status['up'])
+ eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
+ eq([], status['remote_statuses'])
+ info = status['info']
+ self.check_info(info, global_id, state, primary)
+
+ images = list(self.rbd.mirror_image_status_list(ioctx))
+ eq(1, len(images))
+ status = images[0]
+ eq(image_name, status['name'])
+ eq(False, status['up'])
+ eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
+ info = status['info']
+ self.check_info(info, global_id, state)
+
+ states = self.rbd.mirror_image_status_summary(ioctx)
+ eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, 1)], states)
+
+ assert_raises(ImageNotFound, self.image.mirror_image_get_instance_id)
+ instance_ids = list(self.rbd.mirror_image_instance_id_list(ioctx))
+ eq(0, len(instance_ids))
+
+ N = 65
+ for i in range(N):
+ self.rbd.create(ioctx, image_name + str(i), IMG_SIZE, IMG_ORDER,
+ old_format=False, features=int(features))
+ images = list(self.rbd.mirror_image_status_list(ioctx))
+ eq(N + 1, len(images))
+ for i in range(N):
+ self.rbd.remove(ioctx, image_name + str(i))
+
+ def test_mirror_image_create_snapshot(self):
+ assert_raises(InvalidArgument, self.image.mirror_image_create_snapshot)
+
+ peer1_uuid = self.rbd.mirror_peer_add(ioctx, "cluster1", "client")
+ peer2_uuid = self.rbd.mirror_peer_add(ioctx, "cluster2", "client")
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
+ self.image.mirror_image_disable(False)
+ self.image.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT)
+ mode = self.image.mirror_image_get_mode()
+ eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mode)
+
+ snaps = list(self.image.list_snaps())
+ eq(1, len(snaps))
+ snap = snaps[0]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+
+ info = self.image.mirror_image_get_info()
+ eq(True, info['primary'])
+ entries = dict(
+ self.rbd.mirror_image_info_list(ioctx,
+ RBD_MIRROR_IMAGE_MODE_SNAPSHOT))
+ info['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT;
+ eq(info, entries[self.image.id()])
+
+ snap_id = self.image.mirror_image_create_snapshot(
+ RBD_SNAP_CREATE_SKIP_QUIESCE)
+
+ snaps = list(self.image.list_snaps())
+ eq(2, len(snaps))
+ snap = snaps[0]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+ snap = snaps[1]
+ eq(snap['id'], snap_id)
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+ eq(sorted([peer1_uuid, peer2_uuid]),
+ sorted(snap['mirror']['mirror_peer_uuids']))
+
+ eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR,
+ self.image.snap_get_namespace_type(snap_id))
+ mirror_snap = self.image.snap_get_mirror_namespace(snap_id)
+ eq(mirror_snap, snap['mirror'])
+
+ self.image.mirror_image_demote()
+
+ assert_raises(InvalidArgument, self.image.mirror_image_create_snapshot)
+
+ snaps = list(self.image.list_snaps())
+ eq(3, len(snaps))
+ snap = snaps[0]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ snap = snaps[1]
+ eq(snap['id'], snap_id)
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ snap = snaps[2]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED, snap['mirror']['state'])
+ eq(sorted([peer1_uuid, peer2_uuid]),
+ sorted(snap['mirror']['mirror_peer_uuids']))
+
+ self.rbd.mirror_peer_remove(ioctx, peer1_uuid)
+ self.rbd.mirror_peer_remove(ioctx, peer2_uuid)
+ self.image.mirror_image_promote(False)
+
+ def test_aio_mirror_image_create_snapshot(self):
+ peer_uuid = self.rbd.mirror_peer_add(ioctx, "cluster", "client")
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
+ self.image.mirror_image_disable(False)
+ self.image.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT)
+
+ snaps = list(self.image.list_snaps())
+ eq(1, len(snaps))
+ snap = snaps[0]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+
+ # this is a list so that the local cb() can modify it
+ info = [None]
+ def cb(_, _info):
+ info[0] = _info
+
+ comp = self.image.aio_mirror_image_get_info(cb)
+ comp.wait_for_complete_and_cb()
+ assert_not_equal(info[0], None)
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+ info = info[0]
+ global_id = info['global_id']
+ self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
+
+ mode = [None]
+ def cb(_, _mode):
+ mode[0] = _mode
+
+ comp = self.image.aio_mirror_image_get_mode(cb)
+ comp.wait_for_complete_and_cb()
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+ eq(mode[0], RBD_MIRROR_IMAGE_MODE_SNAPSHOT)
+
+ snap_id = [None]
+ def cb(_, _snap_id):
+ snap_id[0] = _snap_id
+
+ comp = self.image.aio_mirror_image_create_snapshot(0, cb)
+ comp.wait_for_complete_and_cb()
+ assert_not_equal(snap_id[0], None)
+ eq(comp.get_return_value(), 0)
+ eq(sys.getrefcount(comp), 2)
+
+ snaps = list(self.image.list_snaps())
+ eq(2, len(snaps))
+ snap = snaps[1]
+ eq(snap['id'], snap_id[0])
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+ eq([peer_uuid], snap['mirror']['mirror_peer_uuids'])
+
+ self.rbd.mirror_peer_remove(ioctx, peer_uuid)
+
+class TestTrash(object):
+
+ def setup_method(self, method):
+ global rados2
+ rados2 = Rados(conffile='')
+ rados2.connect()
+ global ioctx2
+ ioctx2 = rados2.open_ioctx(pool_name)
+
+ def teardown_method(self, method):
+ global ioctx2
+ ioctx2.close()
+ global rados2
+ rados2.shutdown()
+
+ def test_move(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+
+ RBD().trash_move(ioctx, image_name, 1000)
+ RBD().trash_remove(ioctx, image_id, True)
+
+ def test_purge(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_name1 = image_name
+ image_id1 = image.id()
+
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_name2 = image_name
+ image_id2 = image.id()
+
+ RBD().trash_move(ioctx, image_name1, 0)
+ RBD().trash_move(ioctx, image_name2, 1000)
+ RBD().trash_purge(ioctx, datetime.now())
+
+ entries = list(RBD().trash_list(ioctx))
+ eq([image_id2], [x['id'] for x in entries])
+ RBD().trash_remove(ioctx, image_id2, True)
+
+ def test_remove_denied(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+
+ RBD().trash_move(ioctx, image_name, 1000)
+ assert_raises(PermissionError, RBD().trash_remove, ioctx, image_id)
+ RBD().trash_remove(ioctx, image_id, True)
+
+ def test_remove(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+
+ RBD().trash_move(ioctx, image_name, 0)
+ RBD().trash_remove(ioctx, image_id)
+
+ def test_remove_with_progress(self):
+ d = {'received_callback': False}
+ def progress_cb(current, total):
+ d['received_callback'] = True
+ return 0
+
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+
+ RBD().trash_move(ioctx, image_name, 0)
+ RBD().trash_remove(ioctx, image_id, on_progress=progress_cb)
+ eq(True, d['received_callback'])
+
+ def test_get(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+
+ RBD().trash_move(ioctx, image_name, 1000)
+
+ info = RBD().trash_get(ioctx, image_id)
+ eq(image_id, info['id'])
+ eq(image_name, info['name'])
+ eq('USER', info['source'])
+ assert(info['deferment_end_time'] > info['deletion_time'])
+
+ RBD().trash_remove(ioctx, image_id, True)
+
+ def test_list(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id1 = image.id()
+ image_name1 = image_name
+ RBD().trash_move(ioctx, image_name, 1000)
+
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id2 = image.id()
+ image_name2 = image_name
+ RBD().trash_move(ioctx, image_name, 1000)
+
+ entries = list(RBD().trash_list(ioctx))
+ for e in entries:
+ if e['id'] == image_id1:
+ eq(e['name'], image_name1)
+ elif e['id'] == image_id2:
+ eq(e['name'], image_name2)
+ else:
+ assert False
+ eq(e['source'], 'USER')
+ assert e['deferment_end_time'] > e['deletion_time']
+
+ RBD().trash_remove(ioctx, image_id1, True)
+ RBD().trash_remove(ioctx, image_id2, True)
+
+ def test_restore(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ RBD().trash_move(ioctx, image_name, 1000)
+ RBD().trash_restore(ioctx, image_id, image_name)
+ remove_image()
+
+def test_create_group():
+ create_group()
+ remove_group()
+
+def test_rename_group():
+ create_group()
+ if group_name is not None:
+ rename_group()
+ eq(["new" + group_name], RBD().group_list(ioctx))
+ RBD().group_remove(ioctx, "new" + group_name)
+ else:
+ remove_group()
+
+def test_list_groups_empty():
+ eq([], RBD().group_list(ioctx))
+
+def test_list_groups(tmp_group):
+ eq([group_name], RBD().group_list(ioctx))
+
+def test_list_groups_after_removed():
+ create_group()
+ remove_group()
+ eq([], RBD().group_list(ioctx))
+
+class TestGroups(object):
+
+ def setup_method(self, method):
+ global snap_name
+ self.rbd = RBD()
+ create_image()
+ self.image_names = [image_name]
+ self.image = Image(ioctx, image_name)
+
+ create_group()
+ snap_name = get_temp_snap_name()
+ self.group = Group(ioctx, group_name)
+
+ def teardown_method(self, method):
+ remove_group()
+ self.image = None
+ for name in self.image_names:
+ RBD().remove(ioctx, name)
+
+ def test_group_image_add(self):
+ self.group.add_image(ioctx, image_name)
+
+ def test_group_image_list_empty(self):
+ eq([], list(self.group.list_images()))
+
+ def test_group_image_list(self):
+ eq([], list(self.group.list_images()))
+ self.group.add_image(ioctx, image_name)
+ eq([image_name], [img['name'] for img in self.group.list_images()])
+
+ def test_group_image_list_move_to_trash(self):
+ eq([], list(self.group.list_images()))
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ self.group.add_image(ioctx, image_name)
+ eq([image_name], [img['name'] for img in self.group.list_images()])
+ RBD().trash_move(ioctx, image_name, 0)
+ eq([], list(self.group.list_images()))
+ RBD().trash_restore(ioctx, image_id, image_name)
+
+ def test_group_image_many_images(self):
+ eq([], list(self.group.list_images()))
+ self.group.add_image(ioctx, image_name)
+
+ for x in range(0, 20):
+ create_image()
+ self.image_names.append(image_name)
+ self.group.add_image(ioctx, image_name)
+
+ self.image_names.sort()
+ answer = [img['name'] for img in self.group.list_images()]
+ answer.sort()
+ eq(self.image_names, answer)
+
+ def test_group_image_remove(self):
+ eq([], list(self.group.list_images()))
+ self.group.add_image(ioctx, image_name)
+ with Image(ioctx, image_name) as image:
+ eq(RBD_OPERATION_FEATURE_GROUP,
+ image.op_features() & RBD_OPERATION_FEATURE_GROUP)
+ group = image.group()
+ eq(group_name, group['name'])
+
+ eq([image_name], [img['name'] for img in self.group.list_images()])
+ self.group.remove_image(ioctx, image_name)
+ eq([], list(self.group.list_images()))
+ with Image(ioctx, image_name) as image:
+ eq(0, image.op_features() & RBD_OPERATION_FEATURE_GROUP)
+
+ def test_group_snap(self):
+ global snap_name
+ eq([], list(self.group.list_snaps()))
+ self.group.create_snap(snap_name)
+ eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
+
+ for snap in self.image.list_snaps():
+ eq(rbd.RBD_SNAP_NAMESPACE_TYPE_GROUP, snap['namespace'])
+ info = snap['group']
+ eq(group_name, info['group_name'])
+ eq(snap_name, info['group_snap_name'])
+
+ self.group.remove_snap(snap_name)
+ eq([], list(self.group.list_snaps()))
+
+ def test_group_snap_flags(self):
+ global snap_name
+ eq([], list(self.group.list_snaps()))
+
+ self.group.create_snap(snap_name, 0)
+ eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
+ self.group.remove_snap(snap_name)
+
+ self.group.create_snap(snap_name, RBD_SNAP_CREATE_SKIP_QUIESCE)
+ eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
+ self.group.remove_snap(snap_name)
+
+ self.group.create_snap(snap_name, RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR)
+ eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
+ self.group.remove_snap(snap_name)
+
+ assert_raises(InvalidArgument, self.group.create_snap, snap_name,
+ RBD_SNAP_CREATE_SKIP_QUIESCE |
+ RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR)
+ eq([], list(self.group.list_snaps()))
+
+ def test_group_snap_list_many(self):
+ global snap_name
+ eq([], list(self.group.list_snaps()))
+ snap_names = []
+ for x in range(0, 20):
+ snap_names.append(snap_name)
+ self.group.create_snap(snap_name)
+ snap_name = get_temp_snap_name()
+
+ snap_names.sort()
+ answer = [snap['name'] for snap in self.group.list_snaps()]
+ answer.sort()
+ eq(snap_names, answer)
+
+ def test_group_snap_namespace(self):
+ global snap_name
+ eq([], list(self.group.list_snaps()))
+ self.group.add_image(ioctx, image_name)
+ self.group.create_snap(snap_name)
+ eq(1, len([snap['name'] for snap in self.image.list_snaps()]))
+ self.group.remove_image(ioctx, image_name)
+ self.group.remove_snap(snap_name)
+ eq([], list(self.group.list_snaps()))
+
+ def test_group_snap_rename(self):
+ global snap_name
+ new_snap_name = "new" + snap_name
+
+ eq([], list(self.group.list_snaps()))
+ self.group.create_snap(snap_name)
+ eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
+ self.group.rename_snap(snap_name, new_snap_name)
+ eq([new_snap_name], [snap['name'] for snap in self.group.list_snaps()])
+ self.group.remove_snap(new_snap_name)
+ eq([], list(self.group.list_snaps()))
+
+ def test_group_snap_rollback(self):
+ eq([], list(self.group.list_images()))
+ self.group.add_image(ioctx, image_name)
+ with Image(ioctx, image_name) as image:
+ image.write(b'\0' * 256, 0)
+ read = image.read(0, 256)
+ eq(read, b'\0' * 256)
+
+ global snap_name
+ eq([], list(self.group.list_snaps()))
+ self.group.create_snap(snap_name)
+ eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
+
+ with Image(ioctx, image_name) as image:
+ data = rand_data(256)
+ image.write(data, 0)
+ read = image.read(0, 256)
+ eq(read, data)
+
+ self.group.rollback_to_snap(snap_name)
+ with Image(ioctx, image_name) as image:
+ read = image.read(0, 256)
+ eq(read, b'\0' * 256)
+
+ self.group.remove_image(ioctx, image_name)
+ eq([], list(self.group.list_images()))
+ self.group.remove_snap(snap_name)
+ eq([], list(self.group.list_snaps()))
+
+class TestMigration(object):
+
+ def test_migration(self):
+ create_image()
+ RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ data_pool=None)
+
+ status = RBD().migration_status(ioctx, image_name)
+ eq(image_name, status['source_image_name'])
+ eq(image_name, status['dest_image_name'])
+ eq(RBD_IMAGE_MIGRATION_STATE_PREPARED, status['state'])
+
+ with Image(ioctx, image_name) as image:
+ source_spec = image.migration_source_spec()
+ eq("native", source_spec["type"])
+
+ RBD().migration_execute(ioctx, image_name)
+ RBD().migration_commit(ioctx, image_name)
+ remove_image()
+
+ def test_migration_import(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ image.create_snap('snap')
+
+ source_spec = json.dumps(
+ {'type': 'native',
+ 'pool_id': ioctx.get_pool_id(),
+ 'pool_namespace': '',
+ 'image_name': image_name,
+ 'image_id': image_id,
+ 'snap_name': 'snap'})
+ dst_image_name = get_temp_image_name()
+ RBD().migration_prepare_import(source_spec, ioctx, dst_image_name,
+ features=63, order=23, stripe_unit=1<<23,
+ stripe_count=1, data_pool=None)
+
+ status = RBD().migration_status(ioctx, dst_image_name)
+ eq('', status['source_image_name'])
+ eq(dst_image_name, status['dest_image_name'])
+ eq(RBD_IMAGE_MIGRATION_STATE_PREPARED, status['state'])
+
+ with Image(ioctx, dst_image_name) as image:
+ source_spec = image.migration_source_spec()
+ eq("native", source_spec["type"])
+
+ RBD().migration_execute(ioctx, dst_image_name)
+ RBD().migration_commit(ioctx, dst_image_name)
+
+ with Image(ioctx, image_name) as image:
+ image.remove_snap('snap')
+ with Image(ioctx, dst_image_name) as image:
+ image.remove_snap('snap')
+
+ RBD().remove(ioctx, dst_image_name)
+ RBD().remove(ioctx, image_name)
+
+ def test_migration_with_progress(self):
+ d = {'received_callback': False}
+ def progress_cb(current, total):
+ d['received_callback'] = True
+ return 0
+
+ create_image()
+ RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ data_pool=None)
+ RBD().migration_execute(ioctx, image_name, on_progress=progress_cb)
+ eq(True, d['received_callback'])
+ d['received_callback'] = False
+
+ RBD().migration_commit(ioctx, image_name, on_progress=progress_cb)
+ eq(True, d['received_callback'])
+ remove_image()
+
+ def test_migrate_abort(self):
+ create_image()
+ RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ data_pool=None)
+ RBD().migration_abort(ioctx, image_name)
+ remove_image()
+
+ def test_migrate_abort_with_progress(self):
+ d = {'received_callback': False}
+ def progress_cb(current, total):
+ d['received_callback'] = True
+ return 0
+
+ create_image()
+ RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ data_pool=None)
+ RBD().migration_abort(ioctx, image_name, on_progress=progress_cb)
+ eq(True, d['received_callback'])
+ remove_image()
diff --git a/src/test/pybind/test_rgwfs.py b/src/test/pybind/test_rgwfs.py
new file mode 100644
index 000000000..0e0fcce44
--- /dev/null
+++ b/src/test/pybind/test_rgwfs.py
@@ -0,0 +1,137 @@
+# vim: expandtab smarttab shiftwidth=4 softtabstop=4
+import pytest
+from assertions import assert_raises, assert_equal
+import rgw as librgwfs
+
+rgwfs = None
+root_handler = None
+root_dir_handler = None
+
+
+def setup_module():
+ global rgwfs
+ global root_handler
+ rgwfs = librgwfs.LibRGWFS("testid", "", "")
+ root_handler = rgwfs.mount()
+
+
+def teardown_module():
+ global rgwfs
+ rgwfs.shutdown()
+
+
+@pytest.fixture
+def testdir():
+ global root_dir_handler
+
+ names = []
+
+ try:
+ root_dir_handler = rgwfs.opendir(root_handler, b"bucket", 0)
+ except Exception:
+ root_dir_handler = rgwfs.mkdir(root_handler, b"bucket", 0)
+
+ def cb(name, offset, flags):
+ names.append(name)
+ rgwfs.readdir(root_dir_handler, cb, 0, 0)
+ for name in names:
+ rgwfs.unlink(root_dir_handler, name, 0)
+
+
+def test_version():
+ rgwfs.version()
+
+
+def test_fstat(testdir):
+ stat = rgwfs.fstat(root_dir_handler)
+ assert(len(stat) == 13)
+ file_handler = rgwfs.create(root_dir_handler, b'file-1', 0)
+ stat = rgwfs.fstat(file_handler)
+ assert(len(stat) == 13)
+ rgwfs.close(file_handler)
+
+
+def test_statfs(testdir):
+ stat = rgwfs.statfs()
+ assert(len(stat) == 11)
+
+
+def test_fsync(testdir):
+ fd = rgwfs.create(root_dir_handler, b'file-1', 0)
+ rgwfs.write(fd, 0, b"asdf")
+ rgwfs.fsync(fd, 0)
+ rgwfs.write(fd, 4, b"qwer")
+ rgwfs.fsync(fd, 1)
+ rgwfs.close(fd)
+
+
+def test_directory(testdir):
+ dir_handler = rgwfs.mkdir(root_dir_handler, b"temp-directory", 0)
+ rgwfs.close(dir_handler)
+ rgwfs.unlink(root_dir_handler, b"temp-directory")
+
+
+def test_walk_dir(testdir):
+ dirs = [b"dir-1", b"dir-2", b"dir-3"]
+ handles = []
+ for i in dirs:
+ d = rgwfs.mkdir(root_dir_handler, i, 0)
+ handles.append(d)
+ entries = []
+
+ def cb(name, offset):
+ entries.append((name, offset))
+
+ offset, eof = rgwfs.readdir(root_dir_handler, cb, 0)
+
+ for i in handles:
+ rgwfs.close(i)
+
+ for name, _ in entries:
+ assert(name in dirs)
+ rgwfs.unlink(root_dir_handler, name)
+
+
+def test_rename(testdir):
+ file_handler = rgwfs.create(root_dir_handler, b"a", 0)
+ rgwfs.close(file_handler)
+ rgwfs.rename(root_dir_handler, b"a", root_dir_handler, b"b")
+ file_handler = rgwfs.open(root_dir_handler, b"b", 0)
+ rgwfs.fstat(file_handler)
+ rgwfs.close(file_handler)
+ rgwfs.unlink(root_dir_handler, b"b")
+
+
+def test_open(testdir):
+ assert_raises(librgwfs.ObjectNotFound, rgwfs.open,
+ root_dir_handler, b'file-1', 0)
+ assert_raises(librgwfs.ObjectNotFound, rgwfs.open,
+ root_dir_handler, b'file-1', 0)
+ fd = rgwfs.create(root_dir_handler, b'file-1', 0)
+ rgwfs.write(fd, 0, b"asdf")
+ rgwfs.close(fd)
+ fd = rgwfs.open(root_dir_handler, b'file-1', 0)
+ assert_equal(rgwfs.read(fd, 0, 4), b"asdf")
+ rgwfs.close(fd)
+ fd = rgwfs.open(root_dir_handler, b'file-1', 0)
+ rgwfs.write(fd, 0, b"aaaazxcv")
+ rgwfs.close(fd)
+ fd = rgwfs.open(root_dir_handler, b'file-1', 0)
+ assert_equal(rgwfs.read(fd, 4, 4), b"zxcv")
+ rgwfs.close(fd)
+ fd = rgwfs.open(root_dir_handler, b'file-1', 0)
+ assert_equal(rgwfs.read(fd, 0, 4), b"aaaa")
+ rgwfs.close(fd)
+ rgwfs.unlink(root_dir_handler, b"file-1")
+
+
+def test_mount_unmount(testdir):
+ global root_handler
+ global root_dir_handler
+ test_directory()
+ rgwfs.close(root_dir_handler)
+ rgwfs.close(root_handler)
+ rgwfs.unmount()
+ root_handler = rgwfs.mount()
+ root_dir_handler = rgwfs.opendir(root_handler, b"bucket", 0)
+ test_open()
diff --git a/src/test/rbd-ggate.sh b/src/test/rbd-ggate.sh
new file mode 100755
index 000000000..da1217e10
--- /dev/null
+++ b/src/test/rbd-ggate.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2014, 2015 Red Hat <contact@redhat.com>
+# Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+set -ex
+source $(dirname $0)/detect-build-env-vars.sh
+
+test `uname` = FreeBSD
+
+CEPH_CLI_TEST_DUP_COMMAND=1 \
+MON=1 OSD=3 MDS=0 MGR=1 CEPH_PORT=7206 $CEPH_ROOT/src/test/vstart_wrapper.sh \
+ $CEPH_ROOT/qa/workunits/rbd/rbd-ggate.sh \
diff --git a/src/test/rbd_mirror/CMakeLists.txt b/src/test/rbd_mirror/CMakeLists.txt
new file mode 100644
index 000000000..1226735d5
--- /dev/null
+++ b/src/test/rbd_mirror/CMakeLists.txt
@@ -0,0 +1,106 @@
+set(rbd_mirror_test_srcs
+ test_ClusterWatcher.cc
+ test_PoolWatcher.cc
+ test_ImageDeleter.cc
+ test_ImageReplayer.cc
+ test_ImageSync.cc
+ test_InstanceWatcher.cc
+ test_Instances.cc
+ test_LeaderWatcher.cc
+ test_fixture.cc
+ image_map/test_Policy.cc
+ )
+add_library(rbd_mirror_test STATIC ${rbd_mirror_test_srcs})
+target_link_libraries(rbd_mirror_test
+ rbd_test_support
+ GTest::GTest)
+
+add_executable(unittest_rbd_mirror
+ test_main.cc
+ test_mock_fixture.cc
+ test_mock_ImageMap.cc
+ test_mock_ImageReplayer.cc
+ test_mock_ImageSync.cc
+ test_mock_InstanceReplayer.cc
+ test_mock_InstanceWatcher.cc
+ test_mock_LeaderWatcher.cc
+ test_mock_MirrorStatusUpdater.cc
+ test_mock_NamespaceReplayer.cc
+ test_mock_PoolReplayer.cc
+ test_mock_PoolWatcher.cc
+ test_mock_Throttler.cc
+ image_deleter/test_mock_SnapshotPurgeRequest.cc
+ image_deleter/test_mock_TrashMoveRequest.cc
+ image_deleter/test_mock_TrashRemoveRequest.cc
+ image_deleter/test_mock_TrashWatcher.cc
+ image_replayer/test_mock_BootstrapRequest.cc
+ image_replayer/test_mock_CreateImageRequest.cc
+ image_replayer/test_mock_GetMirrorImageIdRequest.cc
+ image_replayer/test_mock_PrepareLocalImageRequest.cc
+ image_replayer/test_mock_PrepareRemoteImageRequest.cc
+ image_replayer/journal/test_mock_CreateLocalImageRequest.cc
+ image_replayer/journal/test_mock_PrepareReplayRequest.cc
+ image_replayer/journal/test_mock_EventPreprocessor.cc
+ image_replayer/journal/test_mock_Replayer.cc
+ image_replayer/snapshot/test_mock_ApplyImageStateRequest.cc
+ image_replayer/snapshot/test_mock_CreateLocalImageRequest.cc
+ image_replayer/snapshot/test_mock_Replayer.cc
+ image_sync/test_mock_SyncPointCreateRequest.cc
+ image_sync/test_mock_SyncPointPruneRequest.cc
+ pool_watcher/test_mock_RefreshImagesRequest.cc
+ )
+add_ceph_unittest(unittest_rbd_mirror)
+
+add_dependencies(unittest_rbd_mirror
+ cls_journal
+ cls_lock
+ cls_rbd)
+target_link_libraries(unittest_rbd_mirror
+ rbd_mirror_test
+ rbd_mirror_internal
+ rbd_mirror_types
+ rbd_api
+ rbd_internal
+ rbd_test_mock
+ journal
+ journal_test_mock
+ cls_rbd_client
+ cls_lock_client
+ cls_journal_client
+ rbd_types
+ rados_test_stub
+ librados
+ osdc
+ global
+ radostest-cxx
+ )
+
+add_executable(ceph_test_rbd_mirror
+ test_main.cc
+ )
+
+target_link_libraries(ceph_test_rbd_mirror
+ rbd_mirror_test
+ rbd_mirror_internal
+ rbd_mirror_types
+ rbd_api
+ rbd_internal
+ journal
+ cls_rbd_client
+ cls_journal_client
+ rbd_types
+ libneorados
+ librados
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ )
+
+add_executable(ceph_test_rbd_mirror_random_write
+ random_write.cc)
+target_link_libraries(ceph_test_rbd_mirror_random_write
+ librbd librados global)
+
+install(TARGETS
+ ceph_test_rbd_mirror
+ ceph_test_rbd_mirror_random_write
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/rbd_mirror/image_deleter/test_mock_SnapshotPurgeRequest.cc b/src/test/rbd_mirror/image_deleter/test_mock_SnapshotPurgeRequest.cc
new file mode 100644
index 000000000..2f14854de
--- /dev/null
+++ b/src/test/rbd_mirror/image_deleter/test_mock_SnapshotPurgeRequest.cc
@@ -0,0 +1,430 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_deleter/SnapshotPurgeRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockExclusiveLock.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
+#include "test/librbd/mock/MockOperations.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ static MockTestImageCtx *s_instance;
+ static MockTestImageCtx *create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ s_instance = this;
+ }
+};
+
+MockTestImageCtx *MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "tools/rbd_mirror/image_deleter/SnapshotPurgeRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_deleter {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::WithArg;
+
+class TestMockImageDeleterSnapshotPurgeRequest : public TestMockFixture {
+public:
+ typedef SnapshotPurgeRequest<librbd::MockTestImageCtx> MockSnapshotPurgeRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ void expect_set_journal_policy(librbd::MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, set_journal_policy(_))
+ .WillOnce(Invoke([](librbd::journal::Policy* policy) {
+ delete policy;
+ }));
+ }
+
+ void expect_open(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, open(true, _))
+ .WillOnce(WithArg<1>(Invoke([this, &mock_image_ctx, r](Context* ctx) {
+ EXPECT_EQ(0U, mock_image_ctx.read_only_mask &
+ librbd::IMAGE_READ_ONLY_FLAG_NON_PRIMARY);
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_close(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, close(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_acquire_lock(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, acquire_lock(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_get_snap_namespace(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id,
+ const cls::rbd::SnapshotNamespace &snap_namespace,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, get_snap_namespace(snap_id, _))
+ .WillOnce(WithArg<1>(Invoke([snap_namespace, r](cls::rbd::SnapshotNamespace *ns) {
+ *ns = snap_namespace;
+ return r;
+ })));
+ }
+
+ void expect_get_snap_name(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, const std::string& name,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, get_snap_name(snap_id, _))
+ .WillOnce(WithArg<1>(Invoke([name, r](std::string *n) {
+ *n = name;
+ return r;
+ })));
+ }
+
+ void expect_is_snap_protected(librbd::MockTestImageCtx &mock_image_ctx,
+ uint64_t snap_id, bool is_protected, int r) {
+ EXPECT_CALL(mock_image_ctx, is_snap_protected(snap_id, _))
+ .WillOnce(WithArg<1>(Invoke([is_protected, r](bool *prot) {
+ *prot = is_protected;
+ return r;
+ })));
+ }
+
+ void expect_snap_unprotect(librbd::MockTestImageCtx &mock_image_ctx,
+ const cls::rbd::SnapshotNamespace& ns,
+ const std::string& name, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_unprotect(ns, name, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_snap_remove(librbd::MockTestImageCtx &mock_image_ctx,
+ const cls::rbd::SnapshotNamespace& ns,
+ const std::string& name, int r) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_snap_remove(ns, name, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_start_op(librbd::MockTestImageCtx &mock_image_ctx, bool success) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, start_op(_))
+ .WillOnce(Invoke([success](int* r) {
+ auto f = [](int r) {};
+ if (!success) {
+ *r = -EROFS;
+ return static_cast<LambdaContext<decltype(f)>*>(nullptr);
+ }
+ return new LambdaContext(std::move(f));
+ }));
+ }
+
+ librbd::ImageCtx *m_local_image_ctx;
+};
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, SuccessJournal) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {});
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap2", 2,
+ 0, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0,
+ {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 2,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 2, "snap2", 0);
+ expect_is_snap_protected(mock_image_ctx, 2, false, 0);
+ expect_start_op(mock_image_ctx, true);
+ expect_snap_remove(mock_image_ctx, cls::rbd::UserSnapshotNamespace{}, "snap2",
+ 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 1,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 1, "snap1", 0);
+ expect_is_snap_protected(mock_image_ctx, 1, true, 0);
+ expect_start_op(mock_image_ctx, true);
+ expect_snap_unprotect(mock_image_ctx, cls::rbd::UserSnapshotNamespace{},
+ "snap1", 0);
+ expect_start_op(mock_image_ctx, true);
+ expect_snap_remove(mock_image_ctx, cls::rbd::UserSnapshotNamespace{}, "snap1",
+ 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, SuccessSnapshot) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {});
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap2", 2,
+ 0, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0,
+ {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 2,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 2, "snap2", 0);
+ expect_is_snap_protected(mock_image_ctx, 2, false, 0);
+ expect_snap_remove(mock_image_ctx, cls::rbd::UserSnapshotNamespace{}, "snap2",
+ 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 1,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 1, "snap1", 0);
+ expect_is_snap_protected(mock_image_ctx, 1, true, 0);
+ expect_snap_unprotect(mock_image_ctx, cls::rbd::UserSnapshotNamespace{},
+ "snap1", 0);
+ expect_snap_remove(mock_image_ctx, cls::rbd::UserSnapshotNamespace{}, "snap1",
+ 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, OpenError) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0,
+ {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, AcquireLockError) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0,
+ {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+ expect_acquire_lock(mock_image_ctx, -EPERM);
+ expect_close(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, SnapUnprotectBusy) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 1,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 1, "snap1", 0);
+ expect_is_snap_protected(mock_image_ctx, 1, true, 0);
+ expect_start_op(mock_image_ctx, true);
+ expect_snap_unprotect(mock_image_ctx, cls::rbd::UserSnapshotNamespace{},
+ "snap1", -EBUSY);
+
+ expect_close(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, SnapUnprotectError) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 1,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 1, "snap1", 0);
+ expect_is_snap_protected(mock_image_ctx, 1, true, 0);
+ expect_start_op(mock_image_ctx, true);
+ expect_snap_unprotect(mock_image_ctx, cls::rbd::UserSnapshotNamespace{},
+ "snap1", -EPERM);
+
+ expect_close(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, SnapRemoveError) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0,
+ {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 1,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 1, "snap1", 0);
+ expect_is_snap_protected(mock_image_ctx, 1, false, 0);
+ expect_start_op(mock_image_ctx, true);
+ expect_snap_remove(mock_image_ctx, cls::rbd::UserSnapshotNamespace{}, "snap1",
+ -EINVAL);
+
+ expect_close(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterSnapshotPurgeRequest, CloseError) {
+ {
+ std::unique_lock image_locker{m_local_image_ctx->image_lock};
+ m_local_image_ctx->add_snap(cls::rbd::UserSnapshotNamespace{}, "snap1", 1,
+ 0, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0,
+ {});
+ }
+
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ expect_get_snap_namespace(mock_image_ctx, 1,
+ cls::rbd::UserSnapshotNamespace{}, 0);
+ expect_get_snap_name(mock_image_ctx, 1, "snap1", 0);
+ expect_is_snap_protected(mock_image_ctx, 1, false, 0);
+ expect_start_op(mock_image_ctx, true);
+ expect_snap_remove(mock_image_ctx, cls::rbd::UserSnapshotNamespace{}, "snap1",
+ 0);
+
+ expect_close(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image_deleter
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_deleter/test_mock_TrashMoveRequest.cc b/src/test/rbd_mirror/image_deleter/test_mock_TrashMoveRequest.cc
new file mode 100644
index 000000000..e1a52c876
--- /dev/null
+++ b/src/test/rbd_mirror/image_deleter/test_mock_TrashMoveRequest.cc
@@ -0,0 +1,901 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/TrashWatcher.h"
+#include "librbd/journal/ResetRequest.h"
+#include "librbd/mirror/GetInfoRequest.h"
+#include "librbd/mirror/ImageRemoveRequest.h"
+#include "librbd/trash/MoveRequest.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_deleter/TrashMoveRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockExclusiveLock.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
+#include "test/librbd/mock/MockOperations.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ static MockTestImageCtx *s_instance;
+ static MockTestImageCtx *create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ s_instance = this;
+ }
+};
+
+MockTestImageCtx *MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+
+template<>
+struct TrashWatcher<MockTestImageCtx> {
+ static TrashWatcher* s_instance;
+ static void notify_image_added(librados::IoCtx&, const std::string& image_id,
+ const cls::rbd::TrashImageSpec& spec,
+ Context *ctx) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->notify_image_added(image_id, spec, ctx);
+ }
+
+ MOCK_METHOD3(notify_image_added, void(const std::string&,
+ const cls::rbd::TrashImageSpec&,
+ Context*));
+
+ TrashWatcher() {
+ s_instance = this;
+ }
+};
+
+TrashWatcher<MockTestImageCtx>* TrashWatcher<MockTestImageCtx>::s_instance = nullptr;
+
+namespace journal {
+
+template <>
+struct ResetRequest<MockTestImageCtx> {
+ static ResetRequest* s_instance;
+ Context* on_finish = nullptr;
+
+ static ResetRequest* create(librados::IoCtx &io_ctx,
+ const std::string &image_id,
+ const std::string &client_id,
+ const std::string &mirror_uuid,
+ ContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ EXPECT_EQ(librbd::Journal<>::LOCAL_MIRROR_UUID, mirror_uuid);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ ResetRequest() {
+ s_instance = this;
+ }
+};
+
+ResetRequest<MockTestImageCtx>* ResetRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+
+namespace mirror {
+
+template<>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+ static GetInfoRequest* s_instance;
+ cls::rbd::MirrorImage *mirror_image;
+ PromotionState *promotion_state;
+ std::string *primary_mirror_uuid;
+ Context *on_finish = nullptr;
+
+ static GetInfoRequest* create(librados::IoCtx& io_ctx,
+ librbd::asio::ContextWQ* context_wq,
+ const std::string& image_id,
+ cls::rbd::MirrorImage *mirror_image,
+ PromotionState *promotion_state,
+ std::string* primary_mirror_uuid,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->mirror_image = mirror_image;
+ s_instance->promotion_state = promotion_state;
+ s_instance->primary_mirror_uuid = primary_mirror_uuid;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetInfoRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~GetInfoRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+GetInfoRequest<librbd::MockTestImageCtx>* GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct ImageRemoveRequest<librbd::MockTestImageCtx> {
+ static ImageRemoveRequest* s_instance;
+ std::string global_image_id;
+ std::string image_id;
+ Context* on_finish;
+
+ static ImageRemoveRequest *create(librados::IoCtx& io_ctx,
+ const std::string& global_image_id,
+ const std::string& image_id,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->global_image_id = global_image_id;
+ s_instance->image_id = image_id;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ImageRemoveRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~ImageRemoveRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+ImageRemoveRequest<librbd::MockTestImageCtx>* ImageRemoveRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+namespace trash {
+
+template <>
+struct MoveRequest<MockTestImageCtx> {
+ static MoveRequest* s_instance;
+ Context* on_finish = nullptr;
+
+ typedef boost::optional<utime_t> DefermentEndTime;
+
+ static MoveRequest* create(librados::IoCtx& io_ctx,
+ const std::string& image_id,
+ const cls::rbd::TrashImageSpec& trash_image_spec,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->construct(image_id, trash_image_spec);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD2(construct, void(const std::string&,
+ const cls::rbd::TrashImageSpec&));
+ MOCK_METHOD0(send, void());
+
+ MoveRequest() {
+ s_instance = this;
+ }
+};
+
+MoveRequest<MockTestImageCtx>* MoveRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace trash
+} // namespace librbd
+
+#include "tools/rbd_mirror/image_deleter/TrashMoveRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_deleter {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class TestMockImageDeleterTrashMoveRequest : public TestMockFixture {
+public:
+ typedef TrashMoveRequest<librbd::MockTestImageCtx> MockTrashMoveRequest;
+ typedef librbd::journal::ResetRequest<librbd::MockTestImageCtx> MockJournalResetRequest;
+ typedef librbd::mirror::GetInfoRequest<librbd::MockTestImageCtx> MockGetMirrorInfoRequest;
+ typedef librbd::mirror::ImageRemoveRequest<librbd::MockTestImageCtx> MockImageRemoveRequest;
+ typedef librbd::trash::MoveRequest<librbd::MockTestImageCtx> MockLibrbdTrashMoveRequest;
+ typedef librbd::TrashWatcher<librbd::MockTestImageCtx> MockTrashWatcher;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ void expect_mirror_image_get_image_id(const std::string& image_id, int r) {
+ bufferlist bl;
+ encode(image_id, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"),
+ StrEq("mirror_image_get_image_id"), _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+ *out_bl = bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_get_mirror_info(
+ MockGetMirrorInfoRequest &mock_get_mirror_info_request,
+ const cls::rbd::MirrorImage &mirror_image,
+ librbd::mirror::PromotionState promotion_state,
+ const std::string& primary_mirror_uuid, int r) {
+ EXPECT_CALL(mock_get_mirror_info_request, send())
+ .WillOnce(Invoke([this, &mock_get_mirror_info_request, mirror_image,
+ promotion_state, primary_mirror_uuid, r]() {
+ *mock_get_mirror_info_request.mirror_image = mirror_image;
+ *mock_get_mirror_info_request.promotion_state = promotion_state;
+ *mock_get_mirror_info_request.primary_mirror_uuid =
+ primary_mirror_uuid;
+ m_threads->work_queue->queue(
+ mock_get_mirror_info_request.on_finish, r);
+ }));
+ }
+
+ void expect_set_journal_policy(librbd::MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, set_journal_policy(_))
+ .WillOnce(Invoke([](librbd::journal::Policy* policy) {
+ delete policy;
+ }));
+ }
+
+ void expect_open(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, open(true, _))
+ .WillOnce(WithArg<1>(Invoke([this, &mock_image_ctx, r](Context* ctx) {
+ EXPECT_EQ(0U, mock_image_ctx.read_only_mask &
+ librbd::IMAGE_READ_ONLY_FLAG_NON_PRIMARY);
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_close(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, close(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_block_requests(librbd::MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, block_requests(0)).Times(1);
+ }
+
+ void expect_acquire_lock(librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, acquire_lock(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_mirror_image_set(const std::string& image_id,
+ const cls::rbd::MirrorImage& mirror_image,
+ int r) {
+ bufferlist bl;
+ encode(image_id, bl);
+ encode(mirror_image, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"),
+ StrEq("mirror_image_set"), ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_mirror_image_remove_request(
+ MockImageRemoveRequest& mock_image_remove_request, int r) {
+ EXPECT_CALL(mock_image_remove_request, send())
+ .WillOnce(Invoke([this, &mock_image_remove_request, r]() {
+ m_threads->work_queue->queue(mock_image_remove_request.on_finish, r);
+ }));
+ }
+
+ void expect_journal_reset(MockJournalResetRequest& mock_journal_reset_request,
+ int r) {
+ EXPECT_CALL(mock_journal_reset_request, send())
+ .WillOnce(Invoke([this, &mock_journal_reset_request, r]() {
+ m_threads->work_queue->queue(mock_journal_reset_request.on_finish, r);
+ }));
+ }
+
+ void expect_trash_move(MockLibrbdTrashMoveRequest& mock_trash_move_request,
+ const std::string& image_name,
+ const std::string& image_id,
+ const boost::optional<uint32_t>& delay, int r) {
+ EXPECT_CALL(mock_trash_move_request, construct(image_id, _))
+ .WillOnce(WithArg<1>(Invoke([image_name, delay](const cls::rbd::TrashImageSpec& spec) {
+ ASSERT_EQ(cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING,
+ spec.source);
+ ASSERT_EQ(image_name, spec.name);
+ if (delay) {
+ utime_t time{spec.deletion_time};
+ time += *delay;
+ ASSERT_TRUE(time == spec.deferment_end_time);
+ } else {
+ ASSERT_EQ(spec.deletion_time, spec.deferment_end_time);
+ }
+ })));
+ EXPECT_CALL(mock_trash_move_request, send())
+ .WillOnce(Invoke([this, &mock_trash_move_request, r]() {
+ m_threads->work_queue->queue(mock_trash_move_request.on_finish, r);
+ }));
+ }
+
+ void expect_notify_image_added(MockTrashWatcher& mock_trash_watcher,
+ const std::string& image_id) {
+ EXPECT_CALL(mock_trash_watcher, notify_image_added(image_id, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](Context *ctx) {
+ m_threads->work_queue->queue(ctx, 0);
+ })));
+ }
+
+ librbd::ImageCtx *m_local_image_ctx;
+};
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, SuccessJournal) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockJournalResetRequest mock_journal_reset_request;
+ expect_journal_reset(mock_journal_reset_request, 0);
+
+ expect_block_requests(mock_image_ctx);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ MockLibrbdTrashMoveRequest mock_librbd_trash_move_request;
+ expect_trash_move(mock_librbd_trash_move_request, m_image_name, "image id",
+ {}, 0);
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove_request(mock_image_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockTrashWatcher mock_trash_watcher;
+ expect_notify_image_added(mock_trash_watcher, "image id");
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, SuccessSnapshot) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockLibrbdTrashMoveRequest mock_librbd_trash_move_request;
+ expect_trash_move(mock_librbd_trash_move_request, m_image_name, "image id",
+ {}, 0);
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove_request(mock_image_remove_request, 0);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockTrashWatcher mock_trash_watcher;
+ expect_notify_image_added(mock_trash_watcher, "image id");
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ false,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, GetImageIdDNE) {
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", -ENOENT);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, GetImageIdError) {
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, GetMirrorInfoLocalPrimary) {
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, GetMirrorInfoOrphan) {
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ false,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, GetMirrorInfoDNE) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", -ENOENT);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, GetMirrorInfoError) {
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, DisableMirrorImageError) {
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, OpenImageError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, ResetJournalError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockJournalResetRequest mock_journal_reset_request;
+ expect_journal_reset(mock_journal_reset_request, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, AcquireLockError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockJournalResetRequest mock_journal_reset_request;
+ expect_journal_reset(mock_journal_reset_request, 0);
+
+ expect_block_requests(mock_image_ctx);
+ expect_acquire_lock(mock_image_ctx, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, TrashMoveError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockJournalResetRequest mock_journal_reset_request;
+ expect_journal_reset(mock_journal_reset_request, 0);
+
+ expect_block_requests(mock_image_ctx);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ MockLibrbdTrashMoveRequest mock_librbd_trash_move_request;
+ expect_trash_move(mock_librbd_trash_move_request, m_image_name, "image id",
+ {}, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, RemoveMirrorImageError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockJournalResetRequest mock_journal_reset_request;
+ expect_journal_reset(mock_journal_reset_request, 0);
+
+ expect_block_requests(mock_image_ctx);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ MockLibrbdTrashMoveRequest mock_librbd_trash_move_request;
+ expect_trash_move(mock_librbd_trash_move_request, m_image_name, "image id",
+ {}, 0);
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove_request(mock_image_remove_request, -EINVAL);
+
+ expect_close(mock_image_ctx, 0);
+
+ MockTrashWatcher mock_trash_watcher;
+ expect_notify_image_added(mock_trash_watcher, "image id");
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, CloseImageError) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_ORPHAN,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockJournalResetRequest mock_journal_reset_request;
+ expect_journal_reset(mock_journal_reset_request, 0);
+
+ expect_block_requests(mock_image_ctx);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ MockLibrbdTrashMoveRequest mock_librbd_trash_move_request;
+ expect_trash_move(mock_librbd_trash_move_request, m_image_name, "image id",
+ {}, 0);
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove_request(mock_image_remove_request, 0);
+
+ expect_close(mock_image_ctx, -EINVAL);
+
+ MockTrashWatcher mock_trash_watcher;
+ expect_notify_image_added(mock_trash_watcher, "image id");
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashMoveRequest, DelayedDelation) {
+ librbd::MockTestImageCtx mock_image_ctx(*m_local_image_ctx);
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.config.set_val("rbd_mirroring_delete_delay", "600");
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ InSequence seq;
+ expect_mirror_image_get_image_id("image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", 0);
+
+ expect_mirror_image_set("image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_set_journal_policy(mock_image_ctx);
+ expect_open(mock_image_ctx, 0);
+
+ MockJournalResetRequest mock_journal_reset_request;
+ expect_journal_reset(mock_journal_reset_request, 0);
+
+ expect_block_requests(mock_image_ctx);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ MockLibrbdTrashMoveRequest mock_librbd_trash_move_request;
+ expect_trash_move(mock_librbd_trash_move_request, m_image_name, "image id",
+ 600, 0);
+
+ MockImageRemoveRequest mock_image_remove_request;
+ expect_mirror_image_remove_request(mock_image_remove_request, 0);
+ expect_close(mock_image_ctx, 0);
+
+ MockTrashWatcher mock_trash_watcher;
+ expect_notify_image_added(mock_trash_watcher, "image id");
+
+ C_SaferCond ctx;
+ auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
+ true,
+ m_local_image_ctx->op_work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace image_deleter
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_deleter/test_mock_TrashRemoveRequest.cc b/src/test/rbd_mirror/image_deleter/test_mock_TrashRemoveRequest.cc
new file mode 100644
index 000000000..f69a74f64
--- /dev/null
+++ b/src/test/rbd_mirror/image_deleter/test_mock_TrashRemoveRequest.cc
@@ -0,0 +1,453 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/TrashWatcher.h"
+#include "librbd/Utils.h"
+#include "librbd/trash/RemoveRequest.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_deleter/SnapshotPurgeRequest.h"
+#include "tools/rbd_mirror/image_deleter/TrashRemoveRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+template<>
+struct TrashWatcher<MockTestImageCtx> {
+ static TrashWatcher* s_instance;
+ static void notify_image_removed(librados::IoCtx&,
+ const std::string& image_id, Context *ctx) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->notify_image_removed(image_id, ctx);
+ }
+
+ MOCK_METHOD2(notify_image_removed, void(const std::string&, Context*));
+
+ TrashWatcher() {
+ s_instance = this;
+ }
+};
+
+TrashWatcher<MockTestImageCtx>* TrashWatcher<MockTestImageCtx>::s_instance = nullptr;
+
+namespace trash {
+
+template <>
+struct RemoveRequest<librbd::MockTestImageCtx> {
+ static RemoveRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static RemoveRequest *create(librados::IoCtx &io_ctx,
+ const std::string &image_id,
+ librbd::asio::ContextWQ *work_queue,
+ bool force,
+ librbd::ProgressContext &progress_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ EXPECT_TRUE(force);
+ s_instance->construct(image_id);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD1(construct, void(const std::string&));
+ MOCK_METHOD0(send, void());
+
+ RemoveRequest() {
+ s_instance = this;
+ }
+};
+
+RemoveRequest<librbd::MockTestImageCtx>* RemoveRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace trash
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+namespace image_deleter {
+
+template <>
+struct SnapshotPurgeRequest<librbd::MockTestImageCtx> {
+ static SnapshotPurgeRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static SnapshotPurgeRequest *create(librados::IoCtx &io_ctx,
+ const std::string &image_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->construct(image_id);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD1(construct, void(const std::string&));
+ MOCK_METHOD0(send, void());
+
+ SnapshotPurgeRequest() {
+ s_instance = this;
+ }
+};
+
+SnapshotPurgeRequest<librbd::MockTestImageCtx>* SnapshotPurgeRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image_deleter
+} // namespace mirror
+} // namespace rbd
+
+#include "tools/rbd_mirror/image_deleter/TrashRemoveRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_deleter {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class TestMockImageDeleterTrashRemoveRequest : public TestMockFixture {
+public:
+ typedef TrashRemoveRequest<librbd::MockTestImageCtx> MockTrashRemoveRequest;
+ typedef SnapshotPurgeRequest<librbd::MockTestImageCtx> MockSnapshotPurgeRequest;
+ typedef librbd::TrashWatcher<librbd::MockTestImageCtx> MockTrashWatcher;
+ typedef librbd::trash::RemoveRequest<librbd::MockTestImageCtx> MockLibrbdTrashRemoveRequest;
+
+ void expect_trash_get(const cls::rbd::TrashImageSpec& trash_spec, int r) {
+ using ceph::encode;
+ EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx),
+ exec(StrEq(RBD_TRASH), _, StrEq("rbd"),
+ StrEq("trash_get"), _, _, _, _))
+ .WillOnce(WithArg<5>(Invoke([trash_spec, r](bufferlist* bl) {
+ encode(trash_spec, *bl);
+ return r;
+ })));
+ }
+
+ void expect_trash_state_set(const std::string& image_id, int r) {
+ bufferlist in_bl;
+ encode(image_id, in_bl);
+ encode(cls::rbd::TRASH_IMAGE_STATE_REMOVING, in_bl);
+ encode(cls::rbd::TRASH_IMAGE_STATE_NORMAL, in_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx),
+ exec(StrEq(RBD_TRASH), _, StrEq("rbd"),
+ StrEq("trash_state_set"),
+ ContentsEqual(in_bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_get_snapcontext(const std::string& image_id,
+ const ::SnapContext &snapc, int r) {
+ bufferlist bl;
+ encode(snapc, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx),
+ exec(librbd::util::header_name(image_id), _, StrEq("rbd"),
+ StrEq("get_snapcontext"), _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+ *out_bl = bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_snapshot_purge(MockSnapshotPurgeRequest &snapshot_purge_request,
+ const std::string &image_id, int r) {
+ EXPECT_CALL(snapshot_purge_request, construct(image_id));
+ EXPECT_CALL(snapshot_purge_request, send())
+ .WillOnce(Invoke([this, &snapshot_purge_request, r]() {
+ m_threads->work_queue->queue(
+ snapshot_purge_request.on_finish, r);
+ }));
+ }
+
+ void expect_image_remove(MockLibrbdTrashRemoveRequest &image_remove_request,
+ const std::string &image_id, int r) {
+ EXPECT_CALL(image_remove_request, construct(image_id));
+ EXPECT_CALL(image_remove_request, send())
+ .WillOnce(Invoke([this, &image_remove_request, r]() {
+ m_threads->work_queue->queue(
+ image_remove_request.on_finish, r);
+ }));
+ }
+
+ void expect_notify_image_removed(MockTrashWatcher& mock_trash_watcher,
+ const std::string& image_id) {
+ EXPECT_CALL(mock_trash_watcher, notify_image_removed(image_id, _))
+ .WillOnce(WithArg<1>(Invoke([this](Context *ctx) {
+ m_threads->work_queue->queue(ctx, 0);
+ })));
+ }
+
+};
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, Success) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", 0);
+
+ expect_get_snapcontext("image id", {1, {1}}, 0);
+
+ MockSnapshotPurgeRequest mock_snapshot_purge_request;
+ expect_snapshot_purge(mock_snapshot_purge_request, "image id", 0);
+
+ MockLibrbdTrashRemoveRequest mock_image_remove_request;
+ expect_image_remove(mock_image_remove_request, "image id", 0);
+
+ MockTrashWatcher mock_trash_watcher;
+ expect_notify_image_removed(mock_trash_watcher, "image id");
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashDNE) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, -ENOENT);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashError) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, -EPERM);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashSourceIncorrect) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashStateIncorrect) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_RESTORING;
+ expect_trash_get(trash_image_spec, 0);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+ ASSERT_EQ(ERROR_RESULT_RETRY_IMMEDIATELY, error_result);
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashSetStateDNE) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", -ENOENT);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashSetStateError) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", -EPERM);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, GetSnapContextDNE) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", 0);
+
+ expect_get_snapcontext("image id", {1, {1}}, -ENOENT);
+
+ MockLibrbdTrashRemoveRequest mock_image_remove_request;
+ expect_image_remove(mock_image_remove_request, "image id", 0);
+
+ MockTrashWatcher mock_trash_watcher;
+ expect_notify_image_removed(mock_trash_watcher, "image id");
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, GetSnapContextError) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", 0);
+
+ expect_get_snapcontext("image id", {1, {1}}, -EINVAL);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, PurgeSnapshotBusy) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", 0);
+
+ expect_get_snapcontext("image id", {1, {1}}, 0);
+
+ MockSnapshotPurgeRequest mock_snapshot_purge_request;
+ expect_snapshot_purge(mock_snapshot_purge_request, "image id", -EBUSY);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EBUSY, ctx.wait());
+ ASSERT_EQ(ERROR_RESULT_RETRY_IMMEDIATELY, error_result);
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, PurgeSnapshotError) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", 0);
+
+ expect_get_snapcontext("image id", {1, {1}}, 0);
+
+ MockSnapshotPurgeRequest mock_snapshot_purge_request;
+ expect_snapshot_purge(mock_snapshot_purge_request, "image id", -EINVAL);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageDeleterTrashRemoveRequest, RemoveError) {
+ InSequence seq;
+
+ cls::rbd::TrashImageSpec trash_image_spec{
+ cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}};
+ expect_trash_get(trash_image_spec, 0);
+
+ expect_trash_state_set("image id", 0);
+
+ expect_get_snapcontext("image id", {1, {1}}, 0);
+
+ MockSnapshotPurgeRequest mock_snapshot_purge_request;
+ expect_snapshot_purge(mock_snapshot_purge_request, "image id", 0);
+
+ MockLibrbdTrashRemoveRequest mock_image_remove_request;
+ expect_image_remove(mock_image_remove_request, "image id", -EINVAL);
+
+ C_SaferCond ctx;
+ ErrorResult error_result;
+ auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id",
+ &error_result,
+ m_threads->work_queue, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image_deleter
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_deleter/test_mock_TrashWatcher.cc b/src/test/rbd_mirror/image_deleter/test_mock_TrashWatcher.cc
new file mode 100644
index 000000000..a612dec14
--- /dev/null
+++ b/src/test/rbd_mirror/image_deleter/test_mock_TrashWatcher.cc
@@ -0,0 +1,519 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include "librbd/TrashWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_deleter/TrashWatcher.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+struct MockTrashWatcher {
+ static MockTrashWatcher *s_instance;
+ static MockTrashWatcher &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockTrashWatcher() {
+ s_instance = this;
+ }
+
+ MOCK_CONST_METHOD0(is_unregistered, bool());
+ MOCK_METHOD1(register_watch, void(Context*));
+ MOCK_METHOD1(unregister_watch, void(Context*));
+};
+
+template <>
+struct TrashWatcher<MockTestImageCtx> {
+ static TrashWatcher *s_instance;
+
+ TrashWatcher(librados::IoCtx &io_ctx, ::MockContextWQ *work_queue) {
+ s_instance = this;
+ }
+ virtual ~TrashWatcher() {
+ }
+
+ static TrashWatcher<MockTestImageCtx> &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ virtual void handle_rewatch_complete(int r) = 0;
+
+ virtual void handle_image_added(const std::string &image_id,
+ const cls::rbd::TrashImageSpec& spec) = 0;
+ virtual void handle_image_removed(const std::string &image_id) = 0;
+
+ bool is_unregistered() const {
+ return MockTrashWatcher::get_instance().is_unregistered();
+ }
+ void register_watch(Context *ctx) {
+ MockTrashWatcher::get_instance().register_watch(ctx);
+ }
+ void unregister_watch(Context *ctx) {
+ MockTrashWatcher::get_instance().unregister_watch(ctx);
+ }
+};
+
+MockTrashWatcher *MockTrashWatcher::s_instance = nullptr;
+TrashWatcher<MockTestImageCtx> *TrashWatcher<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+} // namespace mirror
+} // namespace rbd
+
+#include "tools/rbd_mirror/image_deleter/TrashWatcher.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_deleter {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnArg;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageDeleterTrashWatcher : public TestMockFixture {
+public:
+ typedef TrashWatcher<librbd::MockTestImageCtx> MockTrashWatcher;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef librbd::MockTrashWatcher MockLibrbdTrashWatcher;
+ typedef librbd::TrashWatcher<librbd::MockTestImageCtx> LibrbdTrashWatcher;
+
+ struct MockListener : TrashListener {
+ MOCK_METHOD2(handle_trash_image, void(const std::string&,
+ const ceph::real_clock::time_point&));
+ };
+
+ void expect_work_queue(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_trash_watcher_is_unregistered(MockLibrbdTrashWatcher &mock_trash_watcher,
+ bool unregistered) {
+ EXPECT_CALL(mock_trash_watcher, is_unregistered())
+ .WillOnce(Return(unregistered));
+ }
+
+ void expect_trash_watcher_register(MockLibrbdTrashWatcher &mock_trash_watcher,
+ int r) {
+ EXPECT_CALL(mock_trash_watcher, register_watch(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_trash_watcher_unregister(MockLibrbdTrashWatcher &mock_trash_watcher,
+ int r) {
+ EXPECT_CALL(mock_trash_watcher, unregister_watch(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_create_trash(librados::IoCtx &io_ctx, int r) {
+ EXPECT_CALL(get_mock_io_ctx(io_ctx), create(RBD_TRASH, false, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_trash_list(librados::IoCtx &io_ctx,
+ const std::string& last_image_id,
+ std::map<std::string, cls::rbd::TrashImageSpec>&& images,
+ int r) {
+ bufferlist bl;
+ encode(last_image_id, bl);
+ encode(static_cast<size_t>(1024), bl);
+
+ bufferlist out_bl;
+ encode(images, out_bl);
+
+ EXPECT_CALL(get_mock_io_ctx(io_ctx),
+ exec(RBD_TRASH, _, StrEq("rbd"), StrEq("trash_list"),
+ ContentsEqual(bl), _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([out_bl](bufferlist *bl) {
+ *bl = out_bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_timer_add_event(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillOnce(DoAll(WithArg<1>(Invoke([this](Context *ctx) {
+ auto wrapped_ctx =
+ new LambdaContext([this, ctx](int r) {
+ std::lock_guard timer_locker{m_threads->timer_lock};
+ ctx->complete(r);
+ });
+ m_threads->work_queue->queue(wrapped_ctx, 0);
+ })),
+ ReturnArg<1>()));
+ }
+
+ void expect_handle_trash_image(MockListener& mock_listener,
+ const std::string& global_image_id) {
+ EXPECT_CALL(mock_listener, handle_trash_image(global_image_id, _));
+ }
+
+ int when_shut_down(MockTrashWatcher &mock_trash_watcher) {
+ C_SaferCond ctx;
+ mock_trash_watcher.shut_down(&ctx);
+ return ctx.wait();
+ }
+
+};
+
+TEST_F(TestMockImageDeleterTrashWatcher, EmptyPool) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+ expect_trash_list(m_local_io_ctx, "", {}, 0);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, NonEmptyPool) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ MockListener mock_listener;
+ expect_handle_trash_image(mock_listener, "image0");
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+
+ std::map<std::string, cls::rbd::TrashImageSpec> images;
+ images["image0"] = {cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "name", {}, {}};
+ for (auto idx = 1; idx < 1024; ++idx) {
+ images["image" + stringify(idx)] = {};
+ }
+ expect_trash_list(m_local_io_ctx, "", std::move(images), 0);
+
+ images.clear();
+ for (auto idx = 1024; idx < 2000; ++idx) {
+ images["image" + stringify(idx)] = {};
+ }
+ expect_trash_list(m_local_io_ctx, "image999", std::move(images), 0);
+
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ m_threads->work_queue->drain();
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, Notify) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ MockListener mock_listener;
+ expect_handle_trash_image(mock_listener, "image1");
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+ expect_trash_list(m_local_io_ctx, "", {}, 0);
+
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ LibrbdTrashWatcher::get_instance().handle_image_added(
+ "image1", {cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "name", {}, {}});
+ m_threads->work_queue->drain();
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, CreateBlocklist) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, -EBLOCKLISTED);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, CreateDNE) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, -ENOENT);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(-ENOENT, ctx.wait());
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, CreateError) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, -EINVAL);
+
+ expect_timer_add_event(mock_threads);
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, RegisterWatcherBlocklist) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, -EBLOCKLISTED);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, RegisterWatcherError) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, -EINVAL);
+ expect_timer_add_event(mock_threads);
+
+ expect_create_trash(m_local_io_ctx, 0);
+
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, TrashListBlocklist) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+ expect_trash_list(m_local_io_ctx, "", {}, -EBLOCKLISTED);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, TrashListError) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+ expect_trash_list(m_local_io_ctx, "", {}, -EINVAL);
+
+ expect_timer_add_event(mock_threads);
+ expect_create_trash(m_local_io_ctx, 0);
+
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, false);
+ expect_trash_list(m_local_io_ctx, "", {}, 0);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, Rewatch) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+ expect_trash_list(m_local_io_ctx, "", {}, 0);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ expect_timer_add_event(mock_threads);
+ expect_create_trash(m_local_io_ctx, 0);
+
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, false);
+ expect_trash_list(m_local_io_ctx, "", {}, 0);
+ LibrbdTrashWatcher::get_instance().handle_rewatch_complete(0);
+ m_threads->work_queue->drain();
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+TEST_F(TestMockImageDeleterTrashWatcher, RewatchBlocklist) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ expect_create_trash(m_local_io_ctx, 0);
+
+ MockLibrbdTrashWatcher mock_librbd_trash_watcher;
+ expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
+ expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
+ expect_trash_list(m_local_io_ctx, "", {}, 0);
+
+ MockListener mock_listener;
+ MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
+ mock_listener);
+ C_SaferCond ctx;
+ mock_trash_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ LibrbdTrashWatcher::get_instance().handle_rewatch_complete(-EBLOCKLISTED);
+ m_threads->work_queue->drain();
+
+ expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
+}
+
+} // namespace image_deleter
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_map/test_Policy.cc b/src/test/rbd_mirror/image_map/test_Policy.cc
new file mode 100644
index 000000000..e60ffbfd5
--- /dev/null
+++ b/src/test/rbd_mirror/image_map/test_Policy.cc
@@ -0,0 +1,377 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/Context.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "tools/rbd_mirror/image_map/Types.h"
+#include "tools/rbd_mirror/image_map/SimplePolicy.h"
+#include "include/stringify.h"
+#include "common/Thread.h"
+
+void register_test_image_policy() {
+}
+
+namespace rbd {
+namespace mirror {
+namespace image_map {
+
+class TestImageMapPolicy : public TestFixture {
+public:
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_image_policy_migration_throttle",
+ "0"));
+
+ CephContext *cct = reinterpret_cast<CephContext *>(m_local_io_ctx.cct());
+ std::string policy_type = cct->_conf.get_val<std::string>("rbd_mirror_image_policy_type");
+
+ if (policy_type == "none" || policy_type == "simple") {
+ m_policy = image_map::SimplePolicy::create(m_local_io_ctx);
+ } else {
+ ceph_abort();
+ }
+
+ m_policy->init({});
+ }
+
+ void TearDown() override {
+ TestFixture::TearDown();
+ delete m_policy;
+ }
+
+ void map_image(const std::string &global_image_id) {
+ ASSERT_TRUE(m_policy->add_image(global_image_id));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+ }
+
+ void unmap_image(const std::string &global_image_id) {
+ ASSERT_TRUE(m_policy->remove_image(global_image_id));
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_REMOVE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+ }
+
+ void shuffle_image(const std::string &global_image_id) {
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+ }
+
+ Policy *m_policy;
+};
+
+TEST_F(TestImageMapPolicy, NegativeLookup) {
+ const std::string global_image_id = "global id 1";
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id == UNMAPPED_INSTANCE_ID);
+}
+
+TEST_F(TestImageMapPolicy, Init) {
+ const std::string global_image_id = "global id 1";
+
+ m_policy->init({{global_image_id, {"9876", {}, {}}}});
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+}
+
+TEST_F(TestImageMapPolicy, MapImage) {
+ const std::string global_image_id = "global id 1";
+
+ map_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+}
+
+TEST_F(TestImageMapPolicy, UnmapImage) {
+ const std::string global_image_id = "global id 1";
+
+ // map image
+ map_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+
+ // unmap image
+ unmap_image(global_image_id);
+
+ info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id == UNMAPPED_INSTANCE_ID);
+}
+
+TEST_F(TestImageMapPolicy, ShuffleImageAddInstance) {
+ std::set<std::string> global_image_ids {
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5", "global id 6"
+ };
+
+ for (auto const &global_image_id : global_image_ids) {
+ // map image
+ map_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+ }
+
+ std::set<std::string> shuffle_global_image_ids;
+ m_policy->add_instances({"9876"}, &shuffle_global_image_ids);
+
+ for (auto const &global_image_id : shuffle_global_image_ids) {
+ shuffle_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+ }
+}
+
+TEST_F(TestImageMapPolicy, ShuffleImageRemoveInstance) {
+ std::set<std::string> global_image_ids {
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5"
+ };
+
+ std::set<std::string> shuffle_global_image_ids;
+ m_policy->add_instances({stringify(m_local_io_ctx.get_instance_id())},
+ &shuffle_global_image_ids);
+ for (auto const &global_image_id : global_image_ids) {
+ // map image
+ map_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+ }
+
+ m_policy->add_instances({"9876"}, &shuffle_global_image_ids);
+
+ for (auto const &global_image_id : shuffle_global_image_ids) {
+ shuffle_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+ }
+
+ // record which of the images got migrated to the new instance
+ std::set<std::string> remapped_global_image_ids;
+ for (auto const &global_image_id: shuffle_global_image_ids) {
+ LookupInfo info = m_policy->lookup(global_image_id);
+ if (info.instance_id == "9876") {
+ remapped_global_image_ids.emplace(global_image_id);
+ }
+ }
+
+ shuffle_global_image_ids.clear();
+ m_policy->remove_instances({"9876"}, &shuffle_global_image_ids);
+
+ ASSERT_TRUE(shuffle_global_image_ids == remapped_global_image_ids);
+
+ for (auto const &global_image_id : shuffle_global_image_ids) {
+ shuffle_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+ }
+}
+
+TEST_F(TestImageMapPolicy, RetryMapUpdate) {
+ const std::string global_image_id = "global id 1";
+
+ ASSERT_TRUE(m_policy->add_image(global_image_id));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ // on-disk map update failed
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, -EIO));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+}
+
+TEST_F(TestImageMapPolicy, MapFailureAndUnmap) {
+ const std::string global_image_id = "global id 1";
+
+ ASSERT_TRUE(m_policy->add_image(global_image_id));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+
+ std::set<std::string> shuffle_global_image_ids;
+ m_policy->add_instances({"9876"}, &shuffle_global_image_ids);
+ ASSERT_TRUE(shuffle_global_image_ids.empty());
+
+ m_policy->remove_instances({stringify(m_local_io_ctx.get_instance_id())},
+ &shuffle_global_image_ids);
+ ASSERT_TRUE(shuffle_global_image_ids.empty());
+
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, -EBLOCKLISTED));
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, -ENOENT));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_TRUE(m_policy->remove_image(global_image_id));
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_REMOVE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+}
+
+TEST_F(TestImageMapPolicy, ReshuffleWithMapFailure) {
+ std::set<std::string> global_image_ids {
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5",
+ "global id 6"
+ };
+
+ std::set<std::string> shuffle_global_image_ids;
+ m_policy->add_instances({stringify(m_local_io_ctx.get_instance_id())},
+ &shuffle_global_image_ids);
+ for (auto const &global_image_id : global_image_ids) {
+ // map image
+ map_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+ }
+
+ m_policy->add_instances({"9876"}, &shuffle_global_image_ids);
+ ASSERT_FALSE(shuffle_global_image_ids.empty());
+
+ const std::string global_image_id = *(shuffle_global_image_ids.begin());
+ shuffle_global_image_ids.clear();
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+
+ // peer unavailable
+ m_policy->remove_instances({"9876"}, &shuffle_global_image_ids);
+ ASSERT_TRUE(shuffle_global_image_ids.empty());
+
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, -EBLOCKLISTED));
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+}
+
+TEST_F(TestImageMapPolicy, ShuffleFailureAndRemove) {
+ std::set<std::string> global_image_ids {
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5",
+ "global id 6"
+ };
+
+ std::set<std::string> shuffle_global_image_ids;
+ m_policy->add_instances({stringify(m_local_io_ctx.get_instance_id())},
+ &shuffle_global_image_ids);
+ for (auto const &global_image_id : global_image_ids) {
+ // map image
+ map_image(global_image_id);
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id != UNMAPPED_INSTANCE_ID);
+ }
+
+ m_policy->add_instances({"9876"}, &shuffle_global_image_ids);
+ ASSERT_FALSE(shuffle_global_image_ids.empty());
+
+ std::string global_image_id = *(shuffle_global_image_ids.begin());
+ shuffle_global_image_ids.clear();
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+
+ // peer unavailable
+ m_policy->remove_instances({"9876"}, &shuffle_global_image_ids);
+ ASSERT_TRUE(shuffle_global_image_ids.empty());
+
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, -EBLOCKLISTED));
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_TRUE(m_policy->remove_image(global_image_id));
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_REMOVE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+
+ LookupInfo info = m_policy->lookup(global_image_id);
+ ASSERT_TRUE(info.instance_id == UNMAPPED_INSTANCE_ID);
+}
+
+TEST_F(TestImageMapPolicy, InitialInstanceUpdate) {
+ const std::string global_image_id = "global id 1";
+
+ m_policy->init({{global_image_id, {"9876", {}, {}}}});
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+
+ auto instance_id = stringify(m_local_io_ctx.get_instance_id());
+ std::set<std::string> shuffle_global_image_ids;
+ m_policy->add_instances({instance_id}, &shuffle_global_image_ids);
+
+ ASSERT_EQ(0U, shuffle_global_image_ids.size());
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, -ENOENT));
+
+ ASSERT_EQ(ACTION_TYPE_RELEASE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_MAP_UPDATE, m_policy->start_action(global_image_id));
+ ASSERT_TRUE(m_policy->finish_action(global_image_id, 0));
+
+ ASSERT_EQ(ACTION_TYPE_ACQUIRE, m_policy->start_action(global_image_id));
+ ASSERT_FALSE(m_policy->finish_action(global_image_id, 0));
+}
+
+} // namespace image_map
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/journal/test_mock_CreateLocalImageRequest.cc b/src/test/rbd_mirror/image_replayer/journal/test_mock_CreateLocalImageRequest.cc
new file mode 100644
index 000000000..cc2267160
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/journal/test_mock_CreateLocalImageRequest.cc
@@ -0,0 +1,341 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/CreateImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/journal/CreateLocalImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/journal/StateBuilder.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<librbd::MockTestImageCtx> {
+ typedef ::journal::MockJournaler Journaler;
+};
+
+} // namespace journal
+
+namespace util {
+
+static std::string s_image_id;
+
+template <>
+std::string generate_image_id<MockTestImageCtx>(librados::IoCtx&) {
+ ceph_assert(!s_image_id.empty());
+ return s_image_id;
+}
+
+} // namespace util
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+};
+
+namespace image_replayer {
+
+template<>
+struct CreateImageRequest<librbd::MockTestImageCtx> {
+ static CreateImageRequest* s_instance;
+ Context *on_finish = nullptr;
+
+ static CreateImageRequest* create(Threads<librbd::MockTestImageCtx>* threads,
+ librados::IoCtx &local_io_ctx,
+ const std::string &global_image_id,
+ const std::string &remote_mirror_uuid,
+ const std::string &local_image_name,
+ const std::string &local_image_id,
+ librbd::MockTestImageCtx *remote_image_ctx,
+ PoolMetaCache* pool_meta_cache,
+ cls::rbd::MirrorImageMode mirror_image_mode,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ s_instance->construct(local_image_id);
+ return s_instance;
+ }
+
+ CreateImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~CreateImageRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD1(construct, void(const std::string&));
+ MOCK_METHOD0(send, void());
+};
+
+CreateImageRequest<librbd::MockTestImageCtx>*
+ CreateImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace journal {
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ std::string local_image_id;
+
+ std::string remote_mirror_uuid;
+ ::journal::MockJournalerProxy* remote_journaler = nullptr;
+ cls::journal::ClientState remote_client_state;
+ librbd::journal::MirrorPeerClientMeta remote_client_meta;
+};
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+#include "tools/rbd_mirror/image_replayer/journal/CreateLocalImageRequest.cc"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::WithArg;
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace journal {
+
+class TestMockImageReplayerJournalCreateLocalImageRequest : public TestMockFixture {
+public:
+ typedef CreateLocalImageRequest<librbd::MockTestImageCtx> MockCreateLocalImageRequest;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef CreateImageRequest<librbd::MockTestImageCtx> MockCreateImageRequest;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+ m_mock_remote_image_ctx = new librbd::MockTestImageCtx(*m_remote_image_ctx);
+ }
+
+ void TearDown() override {
+ delete m_mock_remote_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_journaler_register_client(
+ ::journal::MockJournaler& mock_journaler,
+ const librbd::journal::ClientData& client_data, int r) {
+ bufferlist bl;
+ encode(client_data, bl);
+
+ EXPECT_CALL(mock_journaler, register_client(ContentsEqual(bl), _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ })));
+ }
+
+ void expect_journaler_unregister_client(
+ ::journal::MockJournaler& mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, unregister_client(_))
+ .WillOnce(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ }));
+ }
+
+ void expect_journaler_update_client(
+ ::journal::MockJournaler& mock_journaler,
+ const librbd::journal::ClientData& client_data, int r) {
+ bufferlist bl;
+ encode(client_data, bl);
+
+ EXPECT_CALL(mock_journaler, update_client(ContentsEqual(bl), _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ })));
+ }
+
+ void expect_create_image(MockCreateImageRequest& mock_create_image_request,
+ const std::string& image_id, int r) {
+ EXPECT_CALL(mock_create_image_request, construct(image_id));
+ EXPECT_CALL(mock_create_image_request, send())
+ .WillOnce(Invoke([this, &mock_create_image_request, r]() {
+ m_threads->work_queue->queue(mock_create_image_request.on_finish, r);
+ }));
+ }
+
+ MockCreateLocalImageRequest* create_request(
+ MockThreads& mock_threads,
+ MockStateBuilder& mock_state_builder,
+ const std::string& global_image_id,
+ Context* on_finish) {
+ return new MockCreateLocalImageRequest(
+ &mock_threads, m_local_io_ctx, m_mock_remote_image_ctx,
+ global_image_id, nullptr, nullptr, &mock_state_builder,
+ on_finish);
+ }
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ librbd::MockTestImageCtx *m_mock_remote_image_ctx = nullptr;
+};
+
+TEST_F(TestMockImageReplayerJournalCreateLocalImageRequest, Success) {
+ InSequence seq;
+
+ // re-register the client
+ ::journal::MockJournaler mock_journaler;
+ expect_journaler_unregister_client(mock_journaler, 0);
+
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ librbd::util::s_image_id = "local image id";
+ mirror_peer_client_meta.image_id = "local image id";
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ librbd::journal::ClientData client_data;
+ client_data.client_meta = mirror_peer_client_meta;
+ expect_journaler_register_client(mock_journaler, client_data, 0);
+
+ // create the missing local image
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, "local image id", 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads;
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ("local image id", mock_state_builder.local_image_id);
+ ASSERT_EQ("local image id", mock_state_builder.remote_client_meta.image_id);
+ ASSERT_EQ(librbd::journal::MIRROR_PEER_STATE_SYNCING,
+ mock_state_builder.remote_client_meta.state);
+}
+
+TEST_F(TestMockImageReplayerJournalCreateLocalImageRequest, UnregisterError) {
+ InSequence seq;
+
+ // re-register the client
+ ::journal::MockJournaler mock_journaler;
+ expect_journaler_unregister_client(mock_journaler, -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads;
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalCreateLocalImageRequest, RegisterError) {
+ InSequence seq;
+
+ // re-register the client
+ ::journal::MockJournaler mock_journaler;
+ expect_journaler_unregister_client(mock_journaler, 0);
+
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ librbd::util::s_image_id = "local image id";
+ mirror_peer_client_meta.image_id = "local image id";
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ librbd::journal::ClientData client_data;
+ client_data.client_meta = mirror_peer_client_meta;
+ expect_journaler_register_client(mock_journaler, client_data, -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads;
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalCreateLocalImageRequest, CreateImageError) {
+ InSequence seq;
+
+ // re-register the client
+ ::journal::MockJournaler mock_journaler;
+ expect_journaler_unregister_client(mock_journaler, 0);
+
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ librbd::util::s_image_id = "local image id";
+ mirror_peer_client_meta.image_id = "local image id";
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ librbd::journal::ClientData client_data;
+ client_data.client_meta = mirror_peer_client_meta;
+ expect_journaler_register_client(mock_journaler, client_data, 0);
+
+ // create the missing local image
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, "local image id", -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads;
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalCreateLocalImageRequest, CreateImageDuplicate) {
+ InSequence seq;
+
+ // re-register the client
+ ::journal::MockJournaler mock_journaler;
+ expect_journaler_unregister_client(mock_journaler, 0);
+
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ librbd::util::s_image_id = "local image id";
+ mirror_peer_client_meta.image_id = "local image id";
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ librbd::journal::ClientData client_data;
+ client_data.client_meta = mirror_peer_client_meta;
+ expect_journaler_register_client(mock_journaler, client_data, 0);
+
+ // create the missing local image
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, "local image id", -EBADF);
+
+ // re-register the client
+ expect_journaler_unregister_client(mock_journaler, 0);
+ expect_journaler_register_client(mock_journaler, client_data, 0);
+
+ // re-create the local image
+ expect_create_image(mock_create_image_request, "local image id", 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads;
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/journal/test_mock_EventPreprocessor.cc b/src/test/rbd_mirror/image_replayer/journal/test_mock_EventPreprocessor.cc
new file mode 100644
index 000000000..ad0055281
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/journal/test_mock_EventPreprocessor.cc
@@ -0,0 +1,266 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/journal/EventPreprocessor.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<librbd::MockTestImageCtx> {
+ typedef ::journal::MockJournaler Journaler;
+};
+
+} // namespace journal
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/image_replayer/journal/EventPreprocessor.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace journal {
+
+using testing::_;
+using testing::WithArg;
+
+class TestMockImageReplayerJournalEventPreprocessor : public TestMockFixture {
+public:
+ typedef EventPreprocessor<librbd::MockTestImageCtx> MockEventPreprocessor;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ void expect_image_refresh(librbd::MockTestImageCtx &mock_remote_image_ctx, int r) {
+ EXPECT_CALL(*mock_remote_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_update_client(::journal::MockJournaler &mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, update_client(_, _))
+ .WillOnce(WithArg<1>(CompleteContext(r)));
+ }
+
+ librbd::ImageCtx *m_local_image_ctx;
+ librbd::journal::MirrorPeerClientMeta m_client_meta;
+
+};
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, IsNotRequired) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{librbd::journal::RenameEvent{}};
+ ASSERT_FALSE(event_preprocessor.is_required(event_entry));
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, IsRequiredSnapMapPrune) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ m_client_meta.snap_seqs = {{1, 2}, {3, 4}};
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{librbd::journal::RenameEvent{}};
+ ASSERT_TRUE(event_preprocessor.is_required(event_entry));
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, IsRequiredSnapRename) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{librbd::journal::SnapRenameEvent{}};
+ ASSERT_TRUE(event_preprocessor.is_required(event_entry));
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, PreprocessSnapMapPrune) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ expect_image_refresh(mock_local_image_ctx, 0);
+ expect_update_client(mock_remote_journaler, 0);
+
+ mock_local_image_ctx.snap_info = {
+ {6, librbd::SnapInfo{"snap", cls::rbd::UserSnapshotNamespace(), 0U, {}, 0U, 0U, utime_t()}}};
+ m_client_meta.snap_seqs = {{1, 2}, {3, 4}, {5, 6}};
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{librbd::journal::RenameEvent{}};
+ C_SaferCond ctx;
+ event_preprocessor.preprocess(&event_entry, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ librbd::SnapSeqs expected_snap_seqs = {{5, 6}};
+ ASSERT_EQ(expected_snap_seqs, m_client_meta.snap_seqs);
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, PreprocessSnapRename) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ expect_image_refresh(mock_local_image_ctx, 0);
+ expect_update_client(mock_remote_journaler, 0);
+
+ mock_local_image_ctx.snap_ids = {{{cls::rbd::UserSnapshotNamespace(), "snap"}, 6}};
+ mock_local_image_ctx.snap_info = {
+ {6, librbd::SnapInfo{"snap", cls::rbd::UserSnapshotNamespace(), 0U, {}, 0U, 0U, utime_t()}}};
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{
+ librbd::journal::SnapRenameEvent{0, 5, "snap", "new_snap"}};
+ C_SaferCond ctx;
+ event_preprocessor.preprocess(&event_entry, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ librbd::SnapSeqs expected_snap_seqs = {{5, 6}};
+ ASSERT_EQ(expected_snap_seqs, m_client_meta.snap_seqs);
+
+ librbd::journal::SnapRenameEvent *event =
+ boost::get<librbd::journal::SnapRenameEvent>(&event_entry.event);
+ ASSERT_EQ(6U, event->snap_id);
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, PreprocessSnapRenameMissing) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ expect_image_refresh(mock_local_image_ctx, 0);
+
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{
+ librbd::journal::SnapRenameEvent{0, 5, "snap", "new_snap"}};
+ C_SaferCond ctx;
+ event_preprocessor.preprocess(&event_entry, &ctx);
+ ASSERT_EQ(-ENOENT, ctx.wait());
+
+ librbd::journal::SnapRenameEvent *event =
+ boost::get<librbd::journal::SnapRenameEvent>(&event_entry.event);
+ ASSERT_EQ(CEPH_NOSNAP, event->snap_id);
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, PreprocessSnapRenameKnown) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ expect_image_refresh(mock_local_image_ctx, 0);
+
+ mock_local_image_ctx.snap_info = {
+ {6, librbd::SnapInfo{"snap", cls::rbd::UserSnapshotNamespace(), 0U, {}, 0U, 0U, utime_t()}}};
+ m_client_meta.snap_seqs = {{5, 6}};
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{
+ librbd::journal::SnapRenameEvent{0, 5, "snap", "new_snap"}};
+ C_SaferCond ctx;
+ event_preprocessor.preprocess(&event_entry, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ librbd::SnapSeqs expected_snap_seqs = {{5, 6}};
+ ASSERT_EQ(expected_snap_seqs, m_client_meta.snap_seqs);
+
+ librbd::journal::SnapRenameEvent *event =
+ boost::get<librbd::journal::SnapRenameEvent>(&event_entry.event);
+ ASSERT_EQ(6U, event->snap_id);
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, PreprocessRefreshError) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ expect_image_refresh(mock_local_image_ctx, -EINVAL);
+
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{librbd::journal::RenameEvent{}};
+ C_SaferCond ctx;
+ event_preprocessor.preprocess(&event_entry, &ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalEventPreprocessor, PreprocessClientUpdateError) {
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ ::journal::MockJournaler mock_remote_journaler;
+
+ expect_image_refresh(mock_local_image_ctx, 0);
+ expect_update_client(mock_remote_journaler, -EINVAL);
+
+ mock_local_image_ctx.snap_ids = {{{cls::rbd::UserSnapshotNamespace(), "snap"}, 6}};
+ mock_local_image_ctx.snap_info = {
+ {6, librbd::SnapInfo{"snap", cls::rbd::UserSnapshotNamespace(), 0U, {}, 0U, 0U, utime_t()}}};
+ MockEventPreprocessor event_preprocessor(mock_local_image_ctx,
+ mock_remote_journaler,
+ "local mirror uuid",
+ &m_client_meta,
+ m_threads->work_queue);
+
+ librbd::journal::EventEntry event_entry{
+ librbd::journal::SnapRenameEvent{0, 5, "snap", "new_snap"}};
+ C_SaferCond ctx;
+ event_preprocessor.preprocess(&event_entry, &ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/journal/test_mock_PrepareReplayRequest.cc b/src/test/rbd_mirror/image_replayer/journal/test_mock_PrepareReplayRequest.cc
new file mode 100644
index 000000000..4aa951629
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/journal/test_mock_PrepareReplayRequest.cc
@@ -0,0 +1,751 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/journal/PrepareReplayRequest.h"
+#include "tools/rbd_mirror/image_replayer/journal/StateBuilder.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<librbd::MockTestImageCtx> {
+ typedef ::journal::MockJournaler Journaler;
+};
+
+} // namespace journal
+} // namespace rbd
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace journal {
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ StateBuilder(librbd::MockTestImageCtx& local_image_ctx,
+ ::journal::MockJournaler& remote_journaler,
+ const librbd::journal::MirrorPeerClientMeta& remote_client_meta)
+ : local_image_ctx(&local_image_ctx),
+ local_image_id(local_image_ctx.id),
+ remote_journaler(&remote_journaler),
+ remote_client_meta(remote_client_meta) {
+ }
+
+ librbd::MockTestImageCtx* local_image_ctx;
+ std::string local_image_id;
+
+ std::string remote_mirror_uuid = "remote mirror uuid";
+ ::journal::MockJournaler* remote_journaler = nullptr;
+ librbd::journal::MirrorPeerClientMeta remote_client_meta;
+};
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/image_replayer/journal/PrepareReplayRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace journal {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageReplayerJournalPrepareReplayRequest : public TestMockFixture {
+public:
+ typedef PrepareReplayRequest<librbd::MockTestImageCtx> MockPrepareReplayRequest;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+ typedef std::list<cls::journal::Tag> Tags;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ void expect_journaler_get_client(::journal::MockJournaler &mock_journaler,
+ const std::string &client_id,
+ cls::journal::Client &client, int r) {
+ EXPECT_CALL(mock_journaler, get_client(StrEq(client_id), _, _))
+ .WillOnce(DoAll(WithArg<1>(Invoke([client](cls::journal::Client *out_client) {
+ *out_client = client;
+ })),
+ WithArg<2>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ }))));
+ }
+
+ void expect_journaler_update_client(::journal::MockJournaler &mock_journaler,
+ const librbd::journal::ClientData &client_data,
+ int r) {
+ bufferlist bl;
+ encode(client_data, bl);
+
+ EXPECT_CALL(mock_journaler, update_client(ContentsEqual(bl), _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ })));
+ }
+
+ void expect_journaler_get_tags(::journal::MockJournaler &mock_journaler,
+ uint64_t tag_class, const Tags& tags,
+ int r) {
+ EXPECT_CALL(mock_journaler, get_tags(tag_class, _, _))
+ .WillOnce(DoAll(WithArg<1>(Invoke([tags](Tags *out_tags) {
+ *out_tags = tags;
+ })),
+ WithArg<2>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ }))));
+ }
+
+ void expect_journal_get_tag_tid(librbd::MockJournal &mock_journal,
+ uint64_t tag_tid) {
+ EXPECT_CALL(mock_journal, get_tag_tid()).WillOnce(Return(tag_tid));
+ }
+
+ void expect_journal_get_tag_data(librbd::MockJournal &mock_journal,
+ const librbd::journal::TagData &tag_data) {
+ EXPECT_CALL(mock_journal, get_tag_data()).WillOnce(Return(tag_data));
+ }
+
+ void expect_is_resync_requested(librbd::MockJournal &mock_journal,
+ bool do_resync, int r) {
+ EXPECT_CALL(mock_journal, is_resync_requested(_))
+ .WillOnce(DoAll(SetArgPointee<0>(do_resync),
+ Return(r)));
+ }
+
+ bufferlist encode_tag_data(const librbd::journal::TagData &tag_data) {
+ bufferlist bl;
+ encode(tag_data, bl);
+ return bl;
+ }
+
+ MockPrepareReplayRequest* create_request(
+ MockStateBuilder& mock_state_builder,
+ const std::string& local_mirror_uuid,
+ bool* resync_requested, bool* syncing, Context* on_finish) {
+ return new MockPrepareReplayRequest(
+ local_mirror_uuid, nullptr, &mock_state_builder, resync_requested,
+ syncing, on_finish);
+ }
+
+ librbd::ImageCtx *m_local_image_ctx = nullptr;
+};
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, Success) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {"remote mirror uuid"});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // single promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 344, 99})},
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, 0);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_FALSE(resync_requested);
+ ASSERT_FALSE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, NoLocalJournal) {
+ InSequence seq;
+
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ C_SaferCond ctx;
+ ::journal::MockJournaler mock_remote_journaler;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, ResyncRequested) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, true, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {"remote mirror uuid"});
+
+ C_SaferCond ctx;
+ ::journal::MockJournaler mock_remote_journaler;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(resync_requested);
+ ASSERT_FALSE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, ResyncRequestedError) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, -EINVAL);
+
+ C_SaferCond ctx;
+ ::journal::MockJournaler mock_remote_journaler;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, Syncing) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {"remote mirror uuid"});
+
+ C_SaferCond ctx;
+ ::journal::MockJournaler mock_remote_journaler;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_FALSE(resync_requested);
+ ASSERT_TRUE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, GetRemoteTagClassError) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {"remote mirror uuid"});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, -EINVAL);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, GetRemoteTagsError) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {"remote mirror uuid"});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // single promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 344, 99})},
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, -EINVAL);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, LocalDemotedRemoteSyncingState) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ "remote mirror uuid", true, 4, 1});
+
+ // update client state
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta{
+ mock_local_image_ctx.id};
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ librbd::journal::ClientData client_data;
+ client_data.client_meta = mirror_peer_client_meta;
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_update_client(mock_remote_journaler, client_data, 0);
+
+ // lookup remote image tag class
+ client_data = {librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // remote demotion / promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 1, 99})},
+ {3, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 2, 1})},
+ {4, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 3, 1})},
+ {5, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 4, 1})},
+ {6, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 5, 1})},
+ {7, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 6, 1})},
+ {8, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 7, 1})}
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, 0);
+
+ C_SaferCond ctx;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_FALSE(resync_requested);
+ ASSERT_FALSE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, UpdateClientError) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {"remote mirror uuid"});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // single promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 344, 99})},
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, 0);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_FALSE(resync_requested);
+ ASSERT_FALSE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, RemoteDemotePromote) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {"remote mirror uuid"});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // remote demotion / promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 1, 99})},
+ {3, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 2, 1})},
+ {4, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 2, 1})},
+ {5, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 4, 369})}
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, 0);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_FALSE(resync_requested);
+ ASSERT_FALSE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, MultipleRemoteDemotePromotes) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ "remote mirror uuid", true, 4, 1});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // remote demotion / promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 1, 99})},
+ {3, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 2, 1})},
+ {4, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 3, 1})},
+ {5, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 4, 1})},
+ {6, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 5, 1})},
+ {7, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 6, 1})},
+ {8, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 7, 1})}
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, 0);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_FALSE(resync_requested);
+ ASSERT_FALSE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, LocalDemoteRemotePromote) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 346);
+ expect_journal_get_tag_data(mock_journal,
+ {librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 345, 1});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // remote demotion / promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({"local mirror uuid", "local mirror uuid",
+ true, 344, 99})},
+ {3, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ "local mirror uuid", true, 345, 1})},
+ {4, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 3, 1})}
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, 0);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_FALSE(resync_requested);
+ ASSERT_FALSE(syncing);
+}
+
+TEST_F(TestMockImageReplayerJournalPrepareReplayRequest, SplitBrainForcePromote) {
+ InSequence seq;
+
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ // check initial state
+ expect_is_resync_requested(mock_journal, false, 0);
+ expect_journal_get_tag_tid(mock_journal, 345);
+ expect_journal_get_tag_data(mock_journal, {librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ true, 344, 0});
+
+ // lookup remote image tag class
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ cls::journal::Client client;
+ encode(client_data, client.data);
+ ::journal::MockJournaler mock_remote_journaler;
+ expect_journaler_get_client(mock_remote_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // remote demotion / promotion event
+ Tags tags = {
+ {2, 123, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 1, 99})},
+ {3, 123, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 2, 1})}
+ };
+ expect_journaler_get_tags(mock_remote_journaler, 123, tags, 0);
+
+ C_SaferCond ctx;
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ mirror_peer_client_meta.image_id = mock_local_image_ctx.id;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ mirror_peer_client_meta);
+ bool resync_requested;
+ bool syncing;
+ auto request = create_request(mock_state_builder, "local mirror uuid",
+ &resync_requested, &syncing, &ctx);
+ request->send();
+ ASSERT_EQ(-EEXIST, ctx.wait());
+}
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/journal/test_mock_Replayer.cc b/src/test/rbd_mirror/image_replayer/journal/test_mock_Replayer.cc
new file mode 100644
index 000000000..7c8defb3d
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/journal/test_mock_Replayer.cc
@@ -0,0 +1,2162 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/ReplayerListener.h"
+#include "tools/rbd_mirror/image_replayer/Utils.h"
+#include "tools/rbd_mirror/image_replayer/journal/Replayer.h"
+#include "tools/rbd_mirror/image_replayer/journal/EventPreprocessor.h"
+#include "tools/rbd_mirror/image_replayer/journal/ReplayStatusFormatter.h"
+#include "tools/rbd_mirror/image_replayer/journal/StateBuilder.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include <boost/intrusive_ptr.hpp>
+
+using namespace std::chrono_literals;
+
+namespace librbd {
+
+namespace {
+
+struct MockTestJournal;
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx,
+ MockTestJournal& mock_test_journal)
+ : librbd::MockImageCtx(image_ctx), journal(&mock_test_journal) {
+ }
+
+ MockTestJournal* journal = nullptr;
+};
+
+struct MockTestJournal : public MockJournal {
+ MOCK_METHOD2(start_external_replay, void(journal::Replay<MockTestImageCtx> **,
+ Context *on_start));
+ MOCK_METHOD0(stop_external_replay, void());
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<librbd::MockTestImageCtx> {
+ typedef ::journal::MockJournaler Journaler;
+ typedef ::journal::MockReplayEntryProxy ReplayEntry;
+};
+
+template<>
+struct Replay<MockTestImageCtx> {
+ MOCK_METHOD2(decode, int(bufferlist::const_iterator *, EventEntry *));
+ MOCK_METHOD3(process, void(const EventEntry &, Context *, Context *));
+ MOCK_METHOD1(flush, void(Context*));
+ MOCK_METHOD2(shut_down, void(bool, Context*));
+};
+
+} // namespace journal
+} // namespace librbd
+
+namespace boost {
+
+template<>
+struct intrusive_ptr<librbd::MockTestJournal> {
+ intrusive_ptr() {
+ }
+ intrusive_ptr(librbd::MockTestJournal* mock_test_journal)
+ : mock_test_journal(mock_test_journal) {
+ }
+
+ librbd::MockTestJournal* operator->() {
+ return mock_test_journal;
+ }
+
+ void reset() {
+ mock_test_journal = nullptr;
+ }
+
+ const librbd::MockTestJournal* get() const {
+ return mock_test_journal;
+ }
+
+ template<typename T>
+ bool operator==(T* t) const {
+ return (mock_test_journal == t);
+ }
+
+ librbd::MockTestJournal* mock_test_journal = nullptr;
+};
+
+} // namespace boost
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx>* threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+namespace {
+
+struct MockReplayerListener : public image_replayer::ReplayerListener {
+ MOCK_METHOD0(handle_notification, void());
+};
+
+} // anonymous namespace
+
+namespace image_replayer {
+
+template<>
+struct CloseImageRequest<librbd::MockTestImageCtx> {
+ static CloseImageRequest* s_instance;
+ librbd::MockTestImageCtx **image_ctx = nullptr;
+ Context *on_finish = nullptr;
+
+ static CloseImageRequest* create(librbd::MockTestImageCtx **image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_ctx = image_ctx;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ CloseImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~CloseImageRequest() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+CloseImageRequest<librbd::MockTestImageCtx>* CloseImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace journal {
+
+template <>
+struct EventPreprocessor<librbd::MockTestImageCtx> {
+ static EventPreprocessor *s_instance;
+
+ static EventPreprocessor *create(librbd::MockTestImageCtx &local_image_ctx,
+ ::journal::MockJournaler &remote_journaler,
+ const std::string &local_mirror_uuid,
+ librbd::journal::MirrorPeerClientMeta *client_meta,
+ MockContextWQ *work_queue) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ static void destroy(EventPreprocessor* processor) {
+ }
+
+ EventPreprocessor() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~EventPreprocessor() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD1(is_required, bool(const librbd::journal::EventEntry &));
+ MOCK_METHOD2(preprocess, void(librbd::journal::EventEntry *, Context *));
+};
+
+template<>
+struct ReplayStatusFormatter<librbd::MockTestImageCtx> {
+ static ReplayStatusFormatter* s_instance;
+
+ static ReplayStatusFormatter* create(::journal::MockJournaler *journaler,
+ const std::string &mirror_uuid) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ static void destroy(ReplayStatusFormatter* formatter) {
+ }
+
+ ReplayStatusFormatter() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~ReplayStatusFormatter() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD1(handle_entry_processed, void(uint64_t));
+ MOCK_METHOD2(get_or_send_update, bool(std::string *description, Context *on_finish));
+};
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ StateBuilder(librbd::MockTestImageCtx& local_image_ctx,
+ ::journal::MockJournaler& remote_journaler,
+ const librbd::journal::MirrorPeerClientMeta& remote_client_meta)
+ : local_image_ctx(&local_image_ctx),
+ remote_journaler(&remote_journaler),
+ remote_client_meta(remote_client_meta) {
+ }
+
+ librbd::MockTestImageCtx* local_image_ctx;
+ std::string remote_mirror_uuid = "remote mirror uuid";
+ ::journal::MockJournaler* remote_journaler = nullptr;
+ librbd::journal::MirrorPeerClientMeta remote_client_meta;
+};
+
+EventPreprocessor<librbd::MockTestImageCtx>* EventPreprocessor<librbd::MockTestImageCtx>::s_instance = nullptr;
+ReplayStatusFormatter<librbd::MockTestImageCtx>* ReplayStatusFormatter<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+#include "tools/rbd_mirror/image_replayer/journal/Replayer.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace journal {
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::MatcherCast;
+using ::testing::Return;
+using ::testing::ReturnArg;
+using ::testing::SaveArg;
+using ::testing::SetArgPointee;
+using ::testing::WithArg;
+
+class TestMockImageReplayerJournalReplayer : public TestMockFixture {
+public:
+ typedef Replayer<librbd::MockTestImageCtx> MockReplayer;
+ typedef EventPreprocessor<librbd::MockTestImageCtx> MockEventPreprocessor;
+ typedef ReplayStatusFormatter<librbd::MockTestImageCtx> MockReplayStatusFormatter;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest;
+ typedef librbd::journal::Replay<librbd::MockTestImageCtx> MockReplay;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ bufferlist encode_tag_data(const librbd::journal::TagData &tag_data) {
+ bufferlist bl;
+ encode(tag_data, bl);
+ return bl;
+ }
+
+ void expect_work_queue_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_add_event_after_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillRepeatedly(
+ DoAll(Invoke([this](double seconds, Context *ctx) {
+ m_threads->timer->add_event_after(seconds, ctx);
+ }),
+ ReturnArg<1>()));
+ EXPECT_CALL(*mock_threads.timer, cancel_event(_))
+ .WillRepeatedly(
+ Invoke([this](Context *ctx) {
+ return m_threads->timer->cancel_event(ctx);
+ }));
+ }
+
+ void expect_init(::journal::MockJournaler &mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, init(_))
+ .WillOnce(CompleteContext(m_threads->work_queue, r));
+ }
+
+ void expect_stop_replay(::journal::MockJournaler &mock_journaler, int r) {
+ EXPECT_CALL(mock_journaler, stop_replay(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_shut_down(MockReplay &mock_replay, bool cancel_ops, int r) {
+ EXPECT_CALL(mock_replay, shut_down(cancel_ops, _))
+ .WillOnce(WithArg<1>(CompleteContext(m_threads->work_queue, r)));
+ }
+
+ void expect_get_cached_client(::journal::MockJournaler &mock_journaler,
+ const std::string& client_id,
+ const cls::journal::Client& client,
+ const librbd::journal::ClientMeta& client_meta,
+ int r) {
+ librbd::journal::ClientData client_data;
+ client_data.client_meta = client_meta;
+
+ cls::journal::Client client_copy{client};
+ encode(client_data, client_copy.data);
+
+ EXPECT_CALL(mock_journaler, get_cached_client(client_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(client_copy),
+ Return(r)));
+ }
+
+ void expect_start_external_replay(librbd::MockTestJournal &mock_journal,
+ MockReplay *mock_replay, int r) {
+ EXPECT_CALL(mock_journal, start_external_replay(_, _))
+ .WillOnce(DoAll(SetArgPointee<0>(mock_replay),
+ WithArg<1>(CompleteContext(m_threads->work_queue, r))));
+ }
+
+ void expect_is_tag_owner(librbd::MockTestJournal &mock_journal,
+ bool is_owner) {
+ EXPECT_CALL(mock_journal, is_tag_owner()).WillOnce(Return(is_owner));
+ }
+
+ void expect_is_resync_requested(librbd::MockTestJournal &mock_journal,
+ int r, bool resync_requested) {
+ EXPECT_CALL(mock_journal, is_resync_requested(_)).WillOnce(
+ DoAll(SetArgPointee<0>(resync_requested),
+ Return(r)));
+ }
+
+ void expect_get_commit_tid_in_debug(
+ ::journal::MockReplayEntry &mock_replay_entry) {
+ // It is used in debug messages and depends on debug level
+ EXPECT_CALL(mock_replay_entry, get_commit_tid())
+ .Times(AtLeast(0))
+ .WillRepeatedly(Return(0));
+ }
+
+ void expect_get_tag_tid_in_debug(librbd::MockTestJournal &mock_journal) {
+ // It is used in debug messages and depends on debug level
+ EXPECT_CALL(mock_journal, get_tag_tid()).Times(AtLeast(0))
+ .WillRepeatedly(Return(0));
+ }
+
+ void expect_committed(::journal::MockReplayEntry &mock_replay_entry,
+ ::journal::MockJournaler &mock_journaler, int times) {
+ EXPECT_CALL(mock_replay_entry, get_data()).Times(times);
+ EXPECT_CALL(mock_journaler, committed(
+ MatcherCast<const ::journal::MockReplayEntryProxy&>(_)))
+ .Times(times);
+ }
+
+ void expect_try_pop_front(::journal::MockJournaler &mock_journaler,
+ uint64_t replay_tag_tid, bool entries_available) {
+ EXPECT_CALL(mock_journaler, try_pop_front(_, _))
+ .WillOnce(DoAll(SetArgPointee<0>(::journal::MockReplayEntryProxy()),
+ SetArgPointee<1>(replay_tag_tid),
+ Return(entries_available)));
+ }
+
+ void expect_try_pop_front_return_no_entries(
+ ::journal::MockJournaler &mock_journaler, Context *on_finish) {
+ EXPECT_CALL(mock_journaler, try_pop_front(_, _))
+ .WillOnce(DoAll(Invoke([on_finish](::journal::MockReplayEntryProxy *e,
+ uint64_t *t) {
+ on_finish->complete(0);
+ }),
+ Return(false)));
+ }
+
+ void expect_get_tag(::journal::MockJournaler &mock_journaler,
+ const cls::journal::Tag &tag, int r) {
+ EXPECT_CALL(mock_journaler, get_tag(_, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(tag),
+ WithArg<2>(CompleteContext(r))));
+ }
+
+ void expect_allocate_tag(librbd::MockTestJournal &mock_journal, int r) {
+ EXPECT_CALL(mock_journal, allocate_tag(_, _, _))
+ .WillOnce(WithArg<2>(CompleteContext(r)));
+ }
+
+ void expect_preprocess(MockEventPreprocessor &mock_event_preprocessor,
+ bool required, int r) {
+ EXPECT_CALL(mock_event_preprocessor, is_required(_))
+ .WillOnce(Return(required));
+ if (required) {
+ EXPECT_CALL(mock_event_preprocessor, preprocess(_, _))
+ .WillOnce(WithArg<1>(CompleteContext(r)));
+ }
+ }
+
+ void expect_process(MockReplay &mock_replay,
+ int on_ready_r, int on_commit_r) {
+ EXPECT_CALL(mock_replay, process(_, _, _))
+ .WillOnce(DoAll(WithArg<1>(CompleteContext(on_ready_r)),
+ WithArg<2>(CompleteContext(on_commit_r))));
+ }
+
+ void expect_flush(MockReplay& mock_replay, int r) {
+ EXPECT_CALL(mock_replay, flush(_))
+ .WillOnce(CompleteContext(m_threads->work_queue, r));
+ }
+
+ void expect_flush_commit_position(::journal::MockJournaler& mock_journal,
+ int r) {
+ EXPECT_CALL(mock_journal, flush_commit_position(_))
+ .WillOnce(CompleteContext(m_threads->work_queue, r));
+ }
+
+ void expect_get_tag_data(librbd::MockTestJournal& mock_local_journal,
+ const librbd::journal::TagData& tag_data) {
+ EXPECT_CALL(mock_local_journal, get_tag_data())
+ .WillOnce(Return(tag_data));
+ }
+
+ void expect_send(MockCloseImageRequest &mock_close_image_request, int r) {
+ EXPECT_CALL(mock_close_image_request, send())
+ .WillOnce(Invoke([this, &mock_close_image_request, r]() {
+ *mock_close_image_request.image_ctx = nullptr;
+ m_threads->work_queue->queue(mock_close_image_request.on_finish, r);
+ }));
+ }
+
+ void expect_notification(MockThreads& mock_threads,
+ MockReplayerListener& mock_replayer_listener) {
+ EXPECT_CALL(mock_replayer_listener, handle_notification())
+ .WillOnce(Invoke([this]() {
+ std::unique_lock locker{m_lock};
+ m_notified = true;
+ m_cond.notify_all();
+ }));
+ }
+
+ int wait_for_notification() {
+ std::unique_lock locker{m_lock};
+ while (!m_notified) {
+ if (m_cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ return -ETIMEDOUT;
+ }
+ }
+ m_notified = false;
+ return 0;
+ }
+
+ void expect_local_journal_add_listener(
+ librbd::MockTestJournal& mock_local_journal,
+ librbd::journal::Listener** local_journal_listener) {
+ EXPECT_CALL(mock_local_journal, add_listener(_))
+ .WillOnce(SaveArg<0>(local_journal_listener));
+ expect_is_tag_owner(mock_local_journal, false);
+ expect_is_resync_requested(mock_local_journal, 0, false);
+ }
+
+ int init_entry_replayer(MockReplayer& mock_replayer,
+ MockThreads& mock_threads,
+ MockReplayerListener& mock_replayer_listener,
+ librbd::MockTestJournal& mock_local_journal,
+ ::journal::MockJournaler& mock_remote_journaler,
+ MockReplay& mock_local_journal_replay,
+ librbd::journal::Listener** local_journal_listener,
+ ::journal::ReplayHandler** remote_replay_handler,
+ ::journal::JournalMetadataListener** remote_journal_listener) {
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_))
+ .WillOnce(SaveArg<0>(remote_journal_listener));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid", {},
+ {librbd::journal::MirrorPeerClientMeta{}}, 0);
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ local_journal_listener);
+ EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _))
+ .WillOnce(SaveArg<0>(remote_replay_handler));
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ int r = init_ctx.wait();
+ if (r < 0) {
+ return r;
+ }
+
+ return wait_for_notification();
+ }
+
+ int shut_down_entry_replayer(MockReplayer& mock_replayer,
+ MockThreads& mock_threads,
+ librbd::MockTestJournal& mock_local_journal,
+ ::journal::MockJournaler& mock_remote_journaler,
+ MockReplay& mock_local_journal_replay) {
+ expect_shut_down(mock_local_journal_replay, true, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ expect_stop_replay(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ return shutdown_ctx.wait();
+ }
+
+ librbd::ImageCtx* m_local_image_ctx = nullptr;
+
+ ceph::mutex m_lock = ceph::make_mutex(
+ "TestMockImageReplayerJournalReplayer");
+ ceph::condition_variable m_cond;
+ bool m_notified = false;
+};
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitShutDown) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitRemoteJournalerError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, -EINVAL);
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-EINVAL, init_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitRemoteJournalerGetClientError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid", {},
+ {librbd::journal::MirrorPeerClientMeta{}}, -EINVAL);
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-EINVAL, init_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitNoLocalJournal) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+
+ mock_local_image_ctx.journal = nullptr;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid", {},
+ {librbd::journal::MirrorPeerClientMeta{}}, 0);
+
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-EINVAL, init_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitLocalJournalStartExternalReplayError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid", {},
+ {librbd::journal::MirrorPeerClientMeta{}}, 0);
+ expect_start_external_replay(mock_local_journal, nullptr, -EINVAL);
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-EINVAL, init_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitIsPromoted) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid", {},
+ {librbd::journal::MirrorPeerClientMeta{}}, 0);
+ MockReplay mock_local_journal_replay;
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ EXPECT_CALL(mock_local_journal, add_listener(_));
+ expect_is_tag_owner(mock_local_journal, true);
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(0, init_ctx.wait());
+ ASSERT_EQ(0, wait_for_notification());
+
+ expect_shut_down(mock_local_journal_replay, true, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitDisconnected) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ mock_local_image_ctx.config.set_val("rbd_mirroring_resync_after_disconnect",
+ "false");
+
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid",
+ {{}, {}, {},
+ cls::journal::CLIENT_STATE_DISCONNECTED},
+ {librbd::journal::MirrorPeerClientMeta{
+ mock_local_image_ctx.id}}, 0);
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-ENOTCONN, init_ctx.wait());
+ ASSERT_FALSE(mock_replayer.is_resync_requested());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitDisconnectedResync) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ mock_local_image_ctx.config.set_val("rbd_mirroring_resync_after_disconnect",
+ "true");
+
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid",
+ {{}, {}, {},
+ cls::journal::CLIENT_STATE_DISCONNECTED},
+ {librbd::journal::MirrorPeerClientMeta{
+ mock_local_image_ctx.id}}, 0);
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-ENOTCONN, init_ctx.wait());
+ ASSERT_TRUE(mock_replayer.is_resync_requested());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitResyncRequested) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid", {},
+ {librbd::journal::MirrorPeerClientMeta{}}, 0);
+ MockReplay mock_local_journal_replay;
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ EXPECT_CALL(mock_local_journal, add_listener(_));
+ expect_is_tag_owner(mock_local_journal, false);
+ expect_is_resync_requested(mock_local_journal, 0, true);
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(0, init_ctx.wait());
+ ASSERT_EQ(0, wait_for_notification());
+
+ expect_shut_down(mock_local_journal_replay, true, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, InitResyncRequestedError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ expect_init(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, add_listener(_));
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid", {},
+ {librbd::journal::MirrorPeerClientMeta{}}, 0);
+ MockReplay mock_local_journal_replay;
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ EXPECT_CALL(mock_local_journal, add_listener(_));
+ expect_is_tag_owner(mock_local_journal, false);
+ expect_is_resync_requested(mock_local_journal, -EINVAL, false);
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(0, init_ctx.wait());
+ ASSERT_EQ(0, wait_for_notification());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ expect_shut_down(mock_local_journal_replay, true, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, ShutDownLocalJournalReplayError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_shut_down(mock_local_journal_replay, true, -EINVAL);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ expect_stop_replay(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(-EINVAL, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, CloseLocalImageError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_shut_down(mock_local_journal_replay, true, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, -EINVAL);
+ expect_stop_replay(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(-EINVAL, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, StopRemoteJournalerError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_shut_down(mock_local_journal_replay, true, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ expect_stop_replay(mock_remote_journaler, -EPERM);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(-EPERM, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, Replay) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+ expect_committed(mock_replay_entry, mock_remote_journaler, 2);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+
+ // replay_flush
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_allocate_tag(mock_local_journal, 0);
+
+ // process
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _)).WillOnce(Return(0));
+ expect_preprocess(mock_event_preprocessor, false, 0);
+ expect_process(mock_local_journal_replay, 0, 0);
+ EXPECT_CALL(mock_replay_status_formatter, handle_entry_processed(_));
+
+ // the next event with preprocess
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _)).WillOnce(Return(0));
+ expect_preprocess(mock_event_preprocessor, true, 0);
+ expect_process(mock_local_journal_replay, 0, 0);
+ EXPECT_CALL(mock_replay_status_formatter, handle_entry_processed(_));
+
+ // attempt to process the next event
+ C_SaferCond replay_ctx;
+ expect_try_pop_front_return_no_entries(mock_remote_journaler, &replay_ctx);
+
+ // fire
+ remote_replay_handler->handle_entries_available();
+ ASSERT_EQ(0, replay_ctx.wait());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, DecodeError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+
+ // replay_flush
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_allocate_tag(mock_local_journal, 0);
+
+ // process
+ EXPECT_CALL(mock_replay_entry, get_data());
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _))
+ .WillOnce(Return(-EINVAL));
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ // fire
+ remote_replay_handler->handle_entries_available();
+ wait_for_notification();
+
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, DelayedReplay) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+ expect_committed(mock_replay_entry, mock_remote_journaler, 1);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+
+ // replay_flush
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_allocate_tag(mock_local_journal, 0);
+
+ // process with delay
+ EXPECT_CALL(mock_replay_entry, get_data());
+ librbd::journal::EventEntry event_entry(
+ librbd::journal::AioDiscardEvent(123, 345, 0), ceph_clock_now());
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(event_entry),
+ Return(0)));
+
+ Context* delayed_task_ctx = nullptr;
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillOnce(
+ DoAll(Invoke([this, &delayed_task_ctx](double seconds, Context *ctx) {
+ std::unique_lock locker{m_lock};
+ delayed_task_ctx = ctx;
+ m_cond.notify_all();
+ }),
+ ReturnArg<1>()));
+ expect_preprocess(mock_event_preprocessor, false, 0);
+ expect_process(mock_local_journal_replay, 0, 0);
+ EXPECT_CALL(mock_replay_status_formatter, handle_entry_processed(_));
+
+ // attempt to process the next event
+ C_SaferCond replay_ctx;
+ expect_try_pop_front_return_no_entries(mock_remote_journaler, &replay_ctx);
+
+ // fire
+ mock_local_image_ctx.mirroring_replay_delay = 600;
+ remote_replay_handler->handle_entries_available();
+ {
+ std::unique_lock locker{m_lock};
+ while (delayed_task_ctx == nullptr) {
+ if (m_cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ FAIL() << "timed out waiting for task";
+ break;
+ }
+ }
+ }
+ {
+ std::unique_lock timer_locker{mock_threads.timer_lock};
+ delayed_task_ctx->complete(0);
+ }
+ ASSERT_EQ(0, replay_ctx.wait());
+
+ // add a pending (delayed) entry before stop
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ C_SaferCond decode_ctx;
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _))
+ .WillOnce(DoAll(Invoke([&decode_ctx](bufferlist::const_iterator* it,
+ librbd::journal::EventEntry *e) {
+ decode_ctx.complete(0);
+ }),
+ Return(0)));
+
+ remote_replay_handler->handle_entries_available();
+ ASSERT_EQ(0, decode_ctx.wait());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, ReplayNoMemoryError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_notification(mock_threads, mock_replayer_listener);
+ remote_replay_handler->handle_complete(-ENOMEM);
+
+ wait_for_notification();
+ ASSERT_EQ(false, mock_replayer.is_replaying());
+ ASSERT_EQ(-ENOMEM, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, LocalJournalForcePromoted) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_notification(mock_threads, mock_replayer_listener);
+ local_journal_listener->handle_promoted();
+ wait_for_notification();
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, LocalJournalResyncRequested) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_notification(mock_threads, mock_replayer_listener);
+ local_journal_listener->handle_resync();
+ wait_for_notification();
+
+ ASSERT_TRUE(mock_replayer.is_resync_requested());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, RemoteJournalDisconnected) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ mock_local_image_ctx.config.set_val("rbd_mirroring_resync_after_disconnect",
+ "true");
+
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_get_cached_client(mock_remote_journaler, "local mirror uuid",
+ {{}, {}, {},
+ cls::journal::CLIENT_STATE_DISCONNECTED},
+ {librbd::journal::MirrorPeerClientMeta{
+ mock_local_image_ctx.id}}, 0);
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ remote_journaler_listener->handle_update(nullptr);
+ wait_for_notification();
+
+ ASSERT_EQ(-ENOTCONN, mock_replayer.get_error_code());
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_TRUE(mock_replayer.is_resync_requested());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, Flush) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_flush(mock_local_journal_replay, 0);
+ expect_flush_commit_position(mock_remote_journaler, 0);
+
+ C_SaferCond ctx;
+ mock_replayer.flush(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, FlushError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_flush(mock_local_journal_replay, -EINVAL);
+
+ C_SaferCond ctx;
+ mock_replayer.flush(&ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, FlushCommitPositionError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_flush(mock_local_journal_replay, 0);
+ expect_flush_commit_position(mock_remote_journaler, -EINVAL);
+
+ C_SaferCond ctx;
+ mock_replayer.flush(&ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+
+TEST_F(TestMockImageReplayerJournalReplayer, ReplayFlushShutDownError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_try_pop_front(mock_remote_journaler, 1, true);
+ expect_shut_down(mock_local_journal_replay, false, -EINVAL);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_notification(mock_threads, mock_replayer_listener);
+ remote_replay_handler->handle_entries_available();
+
+ wait_for_notification();
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ expect_stop_replay(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, ReplayFlushStartError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ expect_try_pop_front(mock_remote_journaler, 1, true);
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, nullptr, -EINVAL);
+ expect_notification(mock_threads, mock_replayer_listener);
+ remote_replay_handler->handle_entries_available();
+
+ wait_for_notification();
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ MockCloseImageRequest mock_close_image_request;
+ expect_send(mock_close_image_request, 0);
+ expect_stop_replay(mock_remote_journaler, 0);
+ EXPECT_CALL(mock_remote_journaler, remove_listener(_));
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, GetTagError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, -EINVAL);
+ expect_notification(mock_threads, mock_replayer_listener);
+ remote_replay_handler->handle_entries_available();
+
+ wait_for_notification();
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, AllocateTagDemotion) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_notification(mock_threads, mock_replayer_listener);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+ expect_committed(mock_replay_entry, mock_remote_journaler, 1);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::ORPHAN_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_get_tag_data(mock_local_journal, {});
+ expect_allocate_tag(mock_local_journal, 0);
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _)).WillOnce(Return(0));
+ expect_preprocess(mock_event_preprocessor, false, 0);
+ expect_process(mock_local_journal_replay, 0, 0);
+ EXPECT_CALL(mock_replay_status_formatter, handle_entry_processed(_));
+
+ remote_replay_handler->handle_entries_available();
+ wait_for_notification();
+ ASSERT_FALSE(mock_replayer.is_replaying());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, AllocateTagError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_allocate_tag(mock_local_journal, -EINVAL);
+ expect_notification(mock_threads, mock_replayer_listener);
+ remote_replay_handler->handle_entries_available();
+
+ wait_for_notification();
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, PreprocessError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_allocate_tag(mock_local_journal, 0);
+ EXPECT_CALL(mock_replay_entry, get_data());
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _)).WillOnce(Return(0));
+ expect_preprocess(mock_event_preprocessor, true, -EINVAL);
+
+ expect_notification(mock_threads, mock_replayer_listener);
+ remote_replay_handler->handle_entries_available();
+
+ wait_for_notification();
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, ProcessError) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_allocate_tag(mock_local_journal, 0);
+ EXPECT_CALL(mock_replay_entry, get_data());
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _)).WillOnce(Return(0));
+ expect_preprocess(mock_event_preprocessor, false, 0);
+ expect_process(mock_local_journal_replay, 0, -EINVAL);
+ EXPECT_CALL(mock_replay_status_formatter, handle_entry_processed(_));
+
+ // attempt to process the next event
+ C_SaferCond replay_ctx;
+ expect_try_pop_front_return_no_entries(mock_remote_journaler, &replay_ctx);
+ remote_replay_handler->handle_entries_available();
+
+ wait_for_notification();
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, replay_ctx.wait());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+TEST_F(TestMockImageReplayerJournalReplayer, ImageNameUpdated) {
+ librbd::MockTestJournal mock_local_journal;
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx,
+ mock_local_journal};
+ ::journal::MockJournaler mock_remote_journaler;
+ MockReplayerListener mock_replayer_listener;
+ MockThreads mock_threads{m_threads};
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_journaler,
+ {});
+ MockReplayer mock_replayer{
+ &mock_threads, "local mirror uuid", &mock_state_builder,
+ &mock_replayer_listener};
+
+ ::journal::MockReplayEntry mock_replay_entry;
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+ expect_get_commit_tid_in_debug(mock_replay_entry);
+ expect_get_tag_tid_in_debug(mock_local_journal);
+ expect_committed(mock_replay_entry, mock_remote_journaler, 1);
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockReplay mock_local_journal_replay;
+ MockEventPreprocessor mock_event_preprocessor;
+ MockReplayStatusFormatter mock_replay_status_formatter;
+ librbd::journal::Listener* local_journal_listener = nullptr;
+ ::journal::ReplayHandler* remote_replay_handler = nullptr;
+ ::journal::JournalMetadataListener* remote_journaler_listener = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_replayer_listener, mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay,
+ &local_journal_listener,
+ &remote_replay_handler,
+ &remote_journaler_listener));
+
+ mock_local_image_ctx.name = "NEW NAME";
+ cls::journal::Tag tag =
+ {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID,
+ librbd::Journal<>::LOCAL_MIRROR_UUID,
+ true, 0, 0})};
+
+ expect_try_pop_front(mock_remote_journaler, tag.tid, true);
+ expect_shut_down(mock_local_journal_replay, false, 0);
+ EXPECT_CALL(mock_local_journal, remove_listener(_));
+ EXPECT_CALL(mock_local_journal, stop_external_replay());
+ expect_start_external_replay(mock_local_journal, &mock_local_journal_replay,
+ 0);
+ expect_local_journal_add_listener(mock_local_journal,
+ &local_journal_listener);
+ expect_get_tag(mock_remote_journaler, tag, 0);
+ expect_allocate_tag(mock_local_journal, 0);
+ EXPECT_CALL(mock_local_journal_replay, decode(_, _)).WillOnce(Return(0));
+ expect_preprocess(mock_event_preprocessor, false, 0);
+ expect_process(mock_local_journal_replay, 0, 0);
+ EXPECT_CALL(mock_replay_status_formatter, handle_entry_processed(_));
+
+ // attempt to process the next event
+ C_SaferCond replay_ctx;
+ expect_try_pop_front_return_no_entries(mock_remote_journaler, &replay_ctx);
+
+ remote_replay_handler->handle_entries_available();
+ wait_for_notification();
+
+ auto image_spec = util::compute_image_spec(m_local_io_ctx, "NEW NAME");
+ ASSERT_EQ(image_spec, mock_replayer.get_image_spec());
+
+ ASSERT_EQ(0, replay_ctx.wait());
+ ASSERT_TRUE(mock_replayer.is_replaying());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_journal,
+ mock_remote_journaler,
+ mock_local_journal_replay));
+}
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/snapshot/test_mock_ApplyImageStateRequest.cc b/src/test/rbd_mirror/image_replayer/snapshot/test_mock_ApplyImageStateRequest.cc
new file mode 100644
index 000000000..904d36854
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/snapshot/test_mock_ApplyImageStateRequest.cc
@@ -0,0 +1,641 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/internal.h"
+#include "librbd/Operations.h"
+#include "librbd/image/GetMetadataRequest.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/ApplyImageStateRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct GetMetadataRequest<MockTestImageCtx> {
+ std::map<std::string, bufferlist>* pairs = nullptr;
+ Context* on_finish = nullptr;
+
+ static GetMetadataRequest* s_instance;
+ static GetMetadataRequest* create(librados::IoCtx& io_ctx,
+ const std::string& oid,
+ bool filter_internal,
+ const std::string& filter_key_prefix,
+ const std::string& last_key,
+ size_t max_results,
+ std::map<std::string, bufferlist>* pairs,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->pairs = pairs;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetMetadataRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+GetMetadataRequest<MockTestImageCtx>* GetMetadataRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+} // namespace librbd
+
+#include "tools/rbd_mirror/image_replayer/snapshot/ApplyImageStateRequest.cc"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace snapshot {
+
+class TestMockImageReplayerSnapshotApplyImageStateRequest : public TestMockFixture {
+public:
+ typedef ApplyImageStateRequest<librbd::MockTestImageCtx> MockApplyImageStateRequest;
+ typedef librbd::image::GetMetadataRequest<librbd::MockTestImageCtx> MockGetMetadataRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+
+ m_mock_local_image_ctx = new librbd::MockTestImageCtx(*m_local_image_ctx);
+ m_mock_remote_image_ctx = new librbd::MockTestImageCtx(*m_remote_image_ctx);
+ }
+
+ void TearDown() override {
+ delete m_mock_remote_image_ctx;
+ delete m_mock_local_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_rename_image(const std::string& name, int r) {
+ EXPECT_CALL(*m_mock_local_image_ctx->operations, execute_rename(name, _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_update_features(uint64_t features, bool enable, int r) {
+ EXPECT_CALL(*m_mock_local_image_ctx->operations,
+ execute_update_features(features, enable, _, 0U))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_get_metadata(MockGetMetadataRequest& mock_get_metadata_request,
+ const std::map<std::string, bufferlist>& pairs,
+ int r) {
+ EXPECT_CALL(mock_get_metadata_request, send())
+ .WillOnce(Invoke([this, &mock_get_metadata_request, pairs, r]() {
+ *mock_get_metadata_request.pairs = pairs;
+ m_threads->work_queue->queue(mock_get_metadata_request.on_finish, r);
+ }));
+ }
+
+ void expect_update_metadata(const std::vector<std::string>& remove,
+ const std::map<std::string, bufferlist>& pairs,
+ int r) {
+ for (auto& key : remove) {
+ bufferlist bl;
+ ceph::encode(key, bl);
+ EXPECT_CALL(get_mock_io_ctx(m_mock_local_image_ctx->md_ctx),
+ exec(m_mock_local_image_ctx->header_oid, _, StrEq("rbd"),
+ StrEq("metadata_remove"), ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ if (r < 0) {
+ return;
+ }
+ }
+
+ if (!pairs.empty()) {
+ bufferlist bl;
+ ceph::encode(pairs, bl);
+ EXPECT_CALL(get_mock_io_ctx(m_mock_local_image_ctx->md_ctx),
+ exec(m_mock_local_image_ctx->header_oid, _, StrEq("rbd"),
+ StrEq("metadata_set"), ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+ }
+
+ void expect_unprotect_snapshot(const std::string& name, int r) {
+ EXPECT_CALL(*m_mock_local_image_ctx->operations,
+ execute_snap_unprotect({cls::rbd::UserSnapshotNamespace{}},
+ name, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_remove_snapshot(const std::string& name, int r) {
+ EXPECT_CALL(*m_mock_local_image_ctx->operations,
+ execute_snap_remove({cls::rbd::UserSnapshotNamespace{}},
+ name, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_protect_snapshot(const std::string& name, int r) {
+ EXPECT_CALL(*m_mock_local_image_ctx->operations,
+ execute_snap_protect({cls::rbd::UserSnapshotNamespace{}},
+ name, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_rename_snapshot(uint64_t snap_id, const std::string& name,
+ int r) {
+ EXPECT_CALL(*m_mock_local_image_ctx->operations,
+ execute_snap_rename(snap_id, name, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+
+ void expect_set_snap_limit(uint64_t limit, int r) {
+ EXPECT_CALL(*m_mock_local_image_ctx->operations,
+ execute_snap_set_limit(limit, _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ librbd::ImageCtx *m_local_image_ctx;
+ librbd::ImageCtx *m_remote_image_ctx;
+
+ librbd::MockTestImageCtx *m_mock_local_image_ctx = nullptr;
+ librbd::MockTestImageCtx *m_mock_remote_image_ctx = nullptr;
+
+};
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, NoChanges) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, RenameImage) {
+ InSequence seq;
+
+ expect_rename_image("new name", 0);
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = "new name";
+ image_state.features = m_remote_image_ctx->features;
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, RenameImageError) {
+ InSequence seq;
+
+ expect_rename_image("new name", -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = "new name";
+ image_state.features = m_remote_image_ctx->features;
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, UpdateFeatures) {
+ InSequence seq;
+
+ expect_update_features(RBD_FEATURE_DEEP_FLATTEN, false, 0);
+ expect_update_features(RBD_FEATURE_OBJECT_MAP, true, 0);
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_OBJECT_MAP;
+ m_mock_local_image_ctx->features = RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_DEEP_FLATTEN;
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, UpdateFeaturesError) {
+ InSequence seq;
+
+ expect_update_features(RBD_FEATURE_DEEP_FLATTEN, false, -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_OBJECT_MAP;
+ m_mock_local_image_ctx->features = RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_DEEP_FLATTEN;
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, UpdateImageMeta) {
+ InSequence seq;
+
+ bufferlist data_bl;
+ ceph::encode("data", data_bl);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request,
+ {{"key1", {}}, {"key2", {}}}, 0);
+ expect_update_metadata({"key2"}, {{"key1", data_bl}}, 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.metadata = {{"key1", data_bl}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, GetImageMetaError) {
+ InSequence seq;
+
+ bufferlist data_bl;
+ ceph::encode("data", data_bl);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request,
+ {{"key1", {}}, {"key2", {}}}, -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.metadata = {{"key1", data_bl}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, UpdateImageMetaError) {
+ InSequence seq;
+
+ bufferlist data_bl;
+ ceph::encode("data", data_bl);
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request,
+ {{"key1", {}}, {"key2", {}}}, 0);
+ expect_update_metadata({"key2"}, {{"key1", data_bl}}, -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.metadata = {{"key1", data_bl}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, UnprotectSnapshot) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_unprotect_snapshot("snap1", 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.snapshots = {
+ {1U, {cls::rbd::UserSnapshotNamespace{}, "snap1",
+ RBD_PROTECTION_STATUS_UNPROTECTED}}};
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, UnprotectSnapshotError) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_unprotect_snapshot("snap1", -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.snapshots = {
+ {1U, {cls::rbd::UserSnapshotNamespace{}, "snap1",
+ RBD_PROTECTION_STATUS_UNPROTECTED}}};
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, RemoveSnapshot) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_remove_snapshot("snap1", 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, RemoveSnapshotError) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_remove_snapshot("snap1", -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, ProtectSnapshot) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_protect_snapshot("snap1", 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.snapshots = {
+ {1U, {cls::rbd::UserSnapshotNamespace{}, "snap1",
+ RBD_PROTECTION_STATUS_PROTECTED}}};
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, ProtectSnapshotError) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_protect_snapshot("snap1", -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.snapshots = {
+ {1U, {cls::rbd::UserSnapshotNamespace{}, "snap1",
+ RBD_PROTECTION_STATUS_PROTECTED}}};
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_UNPROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, RenameSnapshot) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_rename_snapshot(11, "snap1-renamed", 0);
+
+ expect_set_snap_limit(0, 0);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.snapshots = {
+ {1U, {cls::rbd::UserSnapshotNamespace{}, "snap1-renamed",
+ RBD_PROTECTION_STATUS_PROTECTED}}};
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, RenameSnapshotError) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_rename_snapshot(11, "snap1-renamed", -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+ image_state.snapshots = {
+ {1U, {cls::rbd::UserSnapshotNamespace{}, "snap1-renamed",
+ RBD_PROTECTION_STATUS_PROTECTED}}};
+ m_mock_local_image_ctx->snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0U, {}, RBD_PROTECTION_STATUS_PROTECTED, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, 11}, {2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotApplyImageStateRequest, SetSnapshotLimitError) {
+ InSequence seq;
+
+ MockGetMetadataRequest mock_get_metadata_request;
+ expect_get_metadata(mock_get_metadata_request, {}, 0);
+
+ expect_set_snap_limit(0, -EINVAL);
+
+ librbd::mirror::snapshot::ImageState image_state;
+ image_state.name = m_image_name;
+ image_state.features = m_remote_image_ctx->features;
+
+ C_SaferCond ctx;
+ auto req = MockApplyImageStateRequest::create(
+ "local mirror uuid", "remote mirror uuid", m_mock_local_image_ctx,
+ m_mock_remote_image_ctx, image_state, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/snapshot/test_mock_CreateLocalImageRequest.cc b/src/test/rbd_mirror/image_replayer/snapshot/test_mock_CreateLocalImageRequest.cc
new file mode 100644
index 000000000..58214f3e8
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/snapshot/test_mock_CreateLocalImageRequest.cc
@@ -0,0 +1,356 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/internal.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "tools/rbd_mirror/PoolMetaCache.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/CreateImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/CreateLocalImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+static std::string s_image_id;
+
+template <>
+std::string generate_image_id<MockTestImageCtx>(librados::IoCtx&) {
+ ceph_assert(!s_image_id.empty());
+ return s_image_id;
+}
+
+} // namespace util
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue) {
+ }
+};
+
+namespace image_replayer {
+
+template<>
+struct CreateImageRequest<librbd::MockTestImageCtx> {
+ static CreateImageRequest* s_instance;
+ Context *on_finish = nullptr;
+
+ static CreateImageRequest* create(Threads<librbd::MockTestImageCtx>* threads,
+ librados::IoCtx &local_io_ctx,
+ const std::string &global_image_id,
+ const std::string &remote_mirror_uuid,
+ const std::string &local_image_name,
+ const std::string &local_image_id,
+ librbd::MockTestImageCtx *remote_image_ctx,
+ PoolMetaCache* pool_meta_cache,
+ cls::rbd::MirrorImageMode mirror_image_mode,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ s_instance->construct(local_image_id);
+ return s_instance;
+ }
+
+ CreateImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~CreateImageRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD1(construct, void(const std::string&));
+ MOCK_METHOD0(send, void());
+};
+
+CreateImageRequest<librbd::MockTestImageCtx>*
+ CreateImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace snapshot {
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ std::string local_image_id;
+ std::string remote_mirror_uuid;
+};
+
+} // namespace snapshot
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+#include "tools/rbd_mirror/image_replayer/snapshot/CreateLocalImageRequest.cc"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace snapshot {
+
+class TestMockImageReplayerSnapshotCreateLocalImageRequest : public TestMockFixture {
+public:
+ typedef CreateLocalImageRequest<librbd::MockTestImageCtx> MockCreateLocalImageRequest;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef CreateImageRequest<librbd::MockTestImageCtx> MockCreateImageRequest;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+ m_mock_remote_image_ctx = new librbd::MockTestImageCtx(*m_remote_image_ctx);
+ }
+
+ void TearDown() override {
+ delete m_mock_remote_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void snap_create(librbd::ImageCtx *image_ctx, const std::string &snap_name) {
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, image_ctx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ snap_name, 0, prog_ctx));
+ ASSERT_EQ(0, image_ctx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ snap_name));
+ ASSERT_EQ(0, image_ctx->state->refresh());
+ }
+
+ int clone_image(librbd::ImageCtx *parent_image_ctx,
+ const std::string &snap_name, const std::string &clone_name) {
+ snap_create(parent_image_ctx, snap_name);
+
+ int order = 0;
+ return librbd::clone(m_remote_io_ctx, parent_image_ctx->name.c_str(),
+ snap_name.c_str(), m_remote_io_ctx,
+ clone_name.c_str(), parent_image_ctx->features,
+ &order, 0, 0);
+ }
+
+ void expect_mirror_image_set(const std::string& image_id,
+ const cls::rbd::MirrorImage& mirror_image,
+ int r) {
+ bufferlist bl;
+ encode(image_id, bl);
+ encode(mirror_image, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"),
+ StrEq("mirror_image_set"), ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_mirror_image_remove(const std::string& image_id, int r) {
+ bufferlist bl;
+ encode(image_id, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx),
+ exec(StrEq("rbd_mirroring"), _, StrEq("rbd"),
+ StrEq("mirror_image_remove"),
+ ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_create_image(MockCreateImageRequest& mock_create_image_request,
+ const std::string& image_id, int r) {
+ EXPECT_CALL(mock_create_image_request, construct(image_id));
+ EXPECT_CALL(mock_create_image_request, send())
+ .WillOnce(Invoke([this, &mock_create_image_request, r]() {
+ m_threads->work_queue->queue(mock_create_image_request.on_finish, r);
+ }));
+ }
+
+ MockCreateLocalImageRequest* create_request(
+ MockThreads& mock_threads,
+ MockStateBuilder& mock_state_builder,
+ const std::string& global_image_id,
+ Context* on_finish) {
+ return new MockCreateLocalImageRequest(
+ &mock_threads, m_local_io_ctx, m_mock_remote_image_ctx,
+ global_image_id, &m_pool_meta_cache, nullptr, &mock_state_builder,
+ on_finish);
+ }
+
+ PoolMetaCache m_pool_meta_cache{g_ceph_context};
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ librbd::MockTestImageCtx *m_mock_remote_image_ctx = nullptr;
+};
+
+TEST_F(TestMockImageReplayerSnapshotCreateLocalImageRequest, Success) {
+ InSequence seq;
+
+ librbd::util::s_image_id = "local image id";
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_CREATING}, 0);
+
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, "local image id", 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ("local image id", mock_state_builder.local_image_id);
+}
+
+TEST_F(TestMockImageReplayerSnapshotCreateLocalImageRequest, AddMirrorImageError) {
+ InSequence seq;
+
+ librbd::util::s_image_id = "local image id";
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_CREATING}, -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotCreateLocalImageRequest, CreateImageError) {
+ InSequence seq;
+
+ librbd::util::s_image_id = "local image id";
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_CREATING}, 0);
+
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, "local image id", -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotCreateLocalImageRequest, CreateImageDuplicate) {
+ InSequence seq;
+
+ librbd::util::s_image_id = "local image id";
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_CREATING}, 0);
+
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, "local image id", -EBADF);
+
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_mirror_image_remove("local image id", 0);
+
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_CREATING}, 0);
+
+ expect_create_image(mock_create_image_request, "local image id", 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockStateBuilder mock_state_builder;
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ("local image id", mock_state_builder.local_image_id);
+}
+
+TEST_F(TestMockImageReplayerSnapshotCreateLocalImageRequest, DisableMirrorImageError) {
+ InSequence seq;
+
+ librbd::util::s_image_id = "local image id";
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockStateBuilder mock_state_builder;
+ mock_state_builder.local_image_id = "local image id";
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotCreateLocalImageRequest, RemoveMirrorImageError) {
+ InSequence seq;
+
+ librbd::util::s_image_id = "local image id";
+ expect_mirror_image_set("local image id",
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING}, 0);
+
+ expect_mirror_image_remove("local image id", -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockStateBuilder mock_state_builder;
+ mock_state_builder.local_image_id = "local image id";
+ auto request = create_request(
+ mock_threads, mock_state_builder, "global image id", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace journal
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/snapshot/test_mock_Replayer.cc b/src/test/rbd_mirror/image_replayer/snapshot/test_mock_Replayer.cc
new file mode 100644
index 000000000..75141e5a7
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/snapshot/test_mock_Replayer.cc
@@ -0,0 +1,3327 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/deep_copy/ImageCopyRequest.h"
+#include "librbd/deep_copy/SnapshotCopyRequest.h"
+#include "librbd/mirror/ImageStateUpdateRequest.h"
+#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
+#include "librbd/mirror/snapshot/GetImageStateRequest.h"
+#include "librbd/mirror/snapshot/ImageMeta.h"
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/ReplayerListener.h"
+#include "tools/rbd_mirror/image_replayer/Utils.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/ApplyImageStateRequest.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/Replayer.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+
+using namespace std::chrono_literals;
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace deep_copy {
+
+template <>
+struct ImageCopyRequest<MockTestImageCtx> {
+ uint64_t src_snap_id_start;
+ uint64_t src_snap_id_end;
+ uint64_t dst_snap_id_start;
+ librbd::deep_copy::ObjectNumber object_number;
+ librbd::SnapSeqs snap_seqs;
+
+ static ImageCopyRequest* s_instance;
+ static ImageCopyRequest* create(MockTestImageCtx *src_image_ctx,
+ MockTestImageCtx *dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ bool flatten,
+ const ObjectNumber &object_number,
+ const SnapSeqs &snap_seqs,
+ Handler *handler,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->src_snap_id_start = src_snap_id_start;
+ s_instance->src_snap_id_end = src_snap_id_end;
+ s_instance->dst_snap_id_start = dst_snap_id_start;
+ s_instance->object_number = object_number;
+ s_instance->snap_seqs = snap_seqs;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context* on_finish = nullptr;
+
+ ImageCopyRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct SnapshotCopyRequest<MockTestImageCtx> {
+ librados::snap_t src_snap_id_start;
+ librados::snap_t src_snap_id_end;
+ librados::snap_t dst_snap_id_start;
+ SnapSeqs* snap_seqs = nullptr;
+
+ static SnapshotCopyRequest* s_instance;
+ static SnapshotCopyRequest* create(MockTestImageCtx *src_image_ctx,
+ MockTestImageCtx *dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ bool flatten,
+ ::MockContextWQ *work_queue,
+ SnapSeqs *snap_seqs,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->src_snap_id_start = src_snap_id_start;
+ s_instance->src_snap_id_end = src_snap_id_end;
+ s_instance->dst_snap_id_start = dst_snap_id_start;
+ s_instance->snap_seqs = snap_seqs;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context* on_finish = nullptr;
+
+ SnapshotCopyRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+ImageCopyRequest<MockTestImageCtx>* ImageCopyRequest<MockTestImageCtx>::s_instance = nullptr;
+SnapshotCopyRequest<MockTestImageCtx>* SnapshotCopyRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace deep_copy
+
+namespace mirror {
+
+template <>
+struct ImageStateUpdateRequest<MockTestImageCtx> {
+ static ImageStateUpdateRequest* s_instance;
+ static ImageStateUpdateRequest* create(
+ librados::IoCtx& io_ctx,
+ const std::string& image_id,
+ cls::rbd::MirrorImageState mirror_image_state,
+ const cls::rbd::MirrorImage& mirror_image,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ EXPECT_EQ(cls::rbd::MIRROR_IMAGE_STATE_ENABLED,
+ mirror_image_state);
+ EXPECT_EQ(cls::rbd::MirrorImage{}, mirror_image);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context* on_finish = nullptr;
+ ImageStateUpdateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+ImageStateUpdateRequest<MockTestImageCtx>* ImageStateUpdateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+namespace snapshot {
+
+template <>
+struct CreateNonPrimaryRequest<MockTestImageCtx> {
+ bool demoted = false;
+ std::string primary_mirror_uuid;
+ uint64_t primary_snap_id;
+ SnapSeqs snap_seqs;
+ uint64_t* snap_id = nullptr;
+
+ static CreateNonPrimaryRequest* s_instance;
+ static CreateNonPrimaryRequest* create(MockTestImageCtx *image_ctx,
+ bool demoted,
+ const std::string &primary_mirror_uuid,
+ uint64_t primary_snap_id,
+ const SnapSeqs& snap_seqs,
+ const ImageState &image_state,
+ uint64_t *snap_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->demoted = demoted;
+ s_instance->primary_mirror_uuid = primary_mirror_uuid;
+ s_instance->primary_snap_id = primary_snap_id;
+ s_instance->snap_seqs = snap_seqs;
+ s_instance->snap_id = snap_id;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context* on_finish = nullptr;
+
+ CreateNonPrimaryRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct GetImageStateRequest<MockTestImageCtx> {
+ uint64_t snap_id = CEPH_NOSNAP;
+
+ static GetImageStateRequest* s_instance;
+ static GetImageStateRequest* create(MockTestImageCtx *image_ctx,
+ uint64_t snap_id,
+ ImageState *image_state,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->snap_id = snap_id;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context* on_finish = nullptr;
+
+ GetImageStateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+struct ImageMeta<MockTestImageCtx> {
+ MOCK_METHOD1(load, void(Context*));
+
+ bool resync_requested = false;
+};
+
+template <>
+struct UnlinkPeerRequest<MockTestImageCtx> {
+ uint64_t snap_id;
+ std::string mirror_peer_uuid;
+ bool allow_remove;
+
+ static UnlinkPeerRequest* s_instance;
+ static UnlinkPeerRequest*create (MockTestImageCtx *image_ctx,
+ uint64_t snap_id,
+ const std::string &mirror_peer_uuid,
+ bool allow_remove,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->snap_id = snap_id;
+ s_instance->mirror_peer_uuid = mirror_peer_uuid;
+ s_instance->allow_remove = allow_remove;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context* on_finish = nullptr;
+
+ UnlinkPeerRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+CreateNonPrimaryRequest<MockTestImageCtx>* CreateNonPrimaryRequest<MockTestImageCtx>::s_instance = nullptr;
+GetImageStateRequest<MockTestImageCtx>* GetImageStateRequest<MockTestImageCtx>::s_instance = nullptr;
+UnlinkPeerRequest<MockTestImageCtx>* UnlinkPeerRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct InstanceWatcher<librbd::MockTestImageCtx> {
+ MOCK_METHOD1(cancel_sync_request, void(const std::string&));
+ MOCK_METHOD2(notify_sync_request, void(const std::string&,
+ Context*));
+ MOCK_METHOD1(notify_sync_complete, void(const std::string&));
+};
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx>* threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+namespace {
+
+struct MockReplayerListener : public image_replayer::ReplayerListener {
+ MOCK_METHOD0(handle_notification, void());
+};
+
+} // anonymous namespace
+
+namespace image_replayer {
+
+template<>
+struct CloseImageRequest<librbd::MockTestImageCtx> {
+ static CloseImageRequest* s_instance;
+ librbd::MockTestImageCtx **image_ctx = nullptr;
+ Context *on_finish = nullptr;
+
+ static CloseImageRequest* create(librbd::MockTestImageCtx **image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_ctx = image_ctx;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ CloseImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~CloseImageRequest() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+CloseImageRequest<librbd::MockTestImageCtx>* CloseImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace snapshot {
+
+template <>
+struct ApplyImageStateRequest<librbd::MockTestImageCtx> {
+ Context* on_finish = nullptr;
+
+ static ApplyImageStateRequest* s_instance;
+ static ApplyImageStateRequest* create(
+ const std::string& local_mirror_uuid,
+ const std::string& remote_mirror_uuid,
+ librbd::MockTestImageCtx* local_image_ctx,
+ librbd::MockTestImageCtx* remote_image_ctx,
+ const librbd::mirror::snapshot::ImageState& image_state,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ApplyImageStateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ StateBuilder(librbd::MockTestImageCtx& local_image_ctx,
+ librbd::MockTestImageCtx& remote_image_ctx,
+ librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx>&
+ local_image_meta)
+ : local_image_ctx(&local_image_ctx),
+ remote_image_ctx(&remote_image_ctx),
+ local_image_meta(&local_image_meta) {
+ }
+
+ librbd::MockTestImageCtx* local_image_ctx;
+ librbd::MockTestImageCtx* remote_image_ctx;
+
+ std::string remote_mirror_uuid = "remote mirror uuid";
+
+ librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx>*
+ local_image_meta = nullptr;
+};
+
+ApplyImageStateRequest<librbd::MockTestImageCtx>* ApplyImageStateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+#include "tools/rbd_mirror/image_replayer/snapshot/Replayer.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnArg;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageReplayerSnapshotReplayer : public TestMockFixture {
+public:
+ typedef Replayer<librbd::MockTestImageCtx> MockReplayer;
+ typedef ApplyImageStateRequest<librbd::MockTestImageCtx> MockApplyImageStateRequest;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+ typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest;
+ typedef librbd::deep_copy::ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
+ typedef librbd::deep_copy::SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest;
+ typedef librbd::mirror::ImageStateUpdateRequest<librbd::MockTestImageCtx> MockImageStateUpdateRequest;
+ typedef librbd::mirror::snapshot::CreateNonPrimaryRequest<librbd::MockTestImageCtx> MockCreateNonPrimaryRequest;
+ typedef librbd::mirror::snapshot::GetImageStateRequest<librbd::MockTestImageCtx> MockGetImageStateRequest;
+ typedef librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx> MockImageMeta;
+ typedef librbd::mirror::snapshot::UnlinkPeerRequest<librbd::MockTestImageCtx> MockUnlinkPeerRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name,
+ m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name,
+ &m_remote_image_ctx));
+ }
+
+ void expect_work_queue_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_add_event_after_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillRepeatedly(
+ DoAll(Invoke([this](double seconds, Context *ctx) {
+ m_threads->timer->add_event_after(seconds, ctx);
+ }),
+ ReturnArg<1>()));
+ EXPECT_CALL(*mock_threads.timer, cancel_event(_))
+ .WillRepeatedly(
+ Invoke([this](Context *ctx) {
+ return m_threads->timer->cancel_event(ctx);
+ }));
+ }
+
+ void expect_register_update_watcher(librbd::MockTestImageCtx& mock_image_ctx,
+ librbd::UpdateWatchCtx** update_watch_ctx,
+ uint64_t watch_handle, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, register_update_watcher(_, _))
+ .WillOnce(Invoke([update_watch_ctx, watch_handle, r]
+ (librbd::UpdateWatchCtx* ctx, uint64_t* handle) {
+ if (r >= 0) {
+ *update_watch_ctx = ctx;
+ *handle = watch_handle;
+ }
+ return r;
+ }));
+ }
+
+ void expect_unregister_update_watcher(librbd::MockTestImageCtx& mock_image_ctx,
+ uint64_t watch_handle, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, unregister_update_watcher(watch_handle, _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_load_image_meta(MockImageMeta& mock_image_meta,
+ bool resync_requested, int r) {
+ EXPECT_CALL(mock_image_meta, load(_))
+ .WillOnce(Invoke([this, &mock_image_meta, resync_requested, r](Context* ctx) {
+ mock_image_meta.resync_requested = resync_requested;
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_is_refresh_required(librbd::MockTestImageCtx& mock_image_ctx,
+ bool is_required) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(is_required));
+ }
+
+ void expect_refresh(librbd::MockTestImageCtx& mock_image_ctx,
+ const std::map<uint64_t, librbd::SnapInfo>& snaps,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(Invoke([this, &mock_image_ctx, snaps, r](Context* ctx) {
+ mock_image_ctx.snap_info = snaps;
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_notify_update(librbd::MockTestImageCtx& mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, notify_update(_))
+ .WillOnce(Invoke([this](Context* ctx) {
+ m_threads->work_queue->queue(ctx, 0);
+ }));
+ }
+
+ void expect_prune_non_primary_snapshot(librbd::MockTestImageCtx& mock_image_ctx,
+ uint64_t snap_id, int r) {
+ EXPECT_CALL(mock_image_ctx, get_snap_info(snap_id))
+ .WillOnce(Invoke([&mock_image_ctx](uint64_t snap_id) -> librbd::SnapInfo* {
+ auto it = mock_image_ctx.snap_info.find(snap_id);
+ if (it == mock_image_ctx.snap_info.end()) {
+ return nullptr;
+ }
+ return &it->second;
+ }));
+ EXPECT_CALL(*mock_image_ctx.operations, snap_remove(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_snapshot_copy(MockSnapshotCopyRequest& mock_snapshot_copy_request,
+ uint64_t src_snap_id_start,
+ uint64_t src_snap_id_end,
+ uint64_t dst_snap_id_start,
+ const librbd::SnapSeqs& snap_seqs, int r) {
+ EXPECT_CALL(mock_snapshot_copy_request, send())
+ .WillOnce(Invoke([this, &req=mock_snapshot_copy_request,
+ src_snap_id_start, src_snap_id_end, dst_snap_id_start,
+ snap_seqs, r]() {
+ ASSERT_EQ(src_snap_id_start, req.src_snap_id_start);
+ ASSERT_EQ(src_snap_id_end, req.src_snap_id_end);
+ ASSERT_EQ(dst_snap_id_start, req.dst_snap_id_start);
+ *req.snap_seqs = snap_seqs;
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
+ void expect_get_image_state(MockGetImageStateRequest& mock_get_image_state_request,
+ uint64_t snap_id, int r) {
+ EXPECT_CALL(mock_get_image_state_request, send())
+ .WillOnce(Invoke([this, &req=mock_get_image_state_request, snap_id, r]() {
+ ASSERT_EQ(snap_id, req.snap_id);
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
+ void expect_create_non_primary_request(MockCreateNonPrimaryRequest& mock_create_non_primary_request,
+ bool demoted,
+ const std::string& primary_mirror_uuid,
+ uint64_t primary_snap_id,
+ const librbd::SnapSeqs& snap_seqs,
+ uint64_t snap_id, int r) {
+ EXPECT_CALL(mock_create_non_primary_request, send())
+ .WillOnce(Invoke([this, &req=mock_create_non_primary_request, demoted,
+ primary_mirror_uuid, primary_snap_id, snap_seqs,
+ snap_id, r]() {
+ ASSERT_EQ(demoted, req.demoted);
+ ASSERT_EQ(primary_mirror_uuid, req.primary_mirror_uuid);
+ ASSERT_EQ(primary_snap_id, req.primary_snap_id);
+ ASSERT_EQ(snap_seqs, req.snap_seqs);
+ *req.snap_id = snap_id;
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
+ void expect_update_mirror_image_state(MockImageStateUpdateRequest& mock_image_state_update_request,
+ int r) {
+ EXPECT_CALL(mock_image_state_update_request, send())
+ .WillOnce(Invoke([this, &req=mock_image_state_update_request, r]() {
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
+ void expect_notify_sync_request(MockInstanceWatcher& mock_instance_watcher,
+ const std::string& image_id, int r) {
+ EXPECT_CALL(mock_instance_watcher, notify_sync_request(image_id, _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ void expect_notify_sync_complete(MockInstanceWatcher& mock_instance_watcher,
+ const std::string& image_id) {
+ EXPECT_CALL(mock_instance_watcher, notify_sync_complete(image_id));
+ }
+
+ void expect_cancel_sync_request(MockInstanceWatcher& mock_instance_watcher,
+ const std::string& image_id) {
+ EXPECT_CALL(mock_instance_watcher, cancel_sync_request(image_id));
+ }
+
+ void expect_image_copy(MockImageCopyRequest& mock_image_copy_request,
+ uint64_t src_snap_id_start, uint64_t src_snap_id_end,
+ uint64_t dst_snap_id_start,
+ const librbd::deep_copy::ObjectNumber& object_number,
+ const librbd::SnapSeqs& snap_seqs, int r) {
+ EXPECT_CALL(mock_image_copy_request, send())
+ .WillOnce(Invoke([this, &req=mock_image_copy_request, src_snap_id_start,
+ src_snap_id_end, dst_snap_id_start, object_number,
+ snap_seqs, r]() {
+ ASSERT_EQ(src_snap_id_start, req.src_snap_id_start);
+ ASSERT_EQ(src_snap_id_end, req.src_snap_id_end);
+ ASSERT_EQ(dst_snap_id_start, req.dst_snap_id_start);
+ ASSERT_EQ(object_number, req.object_number);
+ ASSERT_EQ(snap_seqs, req.snap_seqs);
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
+ void expect_unlink_peer(MockUnlinkPeerRequest& mock_unlink_peer_request,
+ uint64_t snap_id, const std::string& mirror_peer_uuid,
+ bool allow_remove, int r) {
+ EXPECT_CALL(mock_unlink_peer_request, send())
+ .WillOnce(Invoke([this, &req=mock_unlink_peer_request, snap_id,
+ mirror_peer_uuid, allow_remove, r]() {
+ ASSERT_EQ(snap_id, req.snap_id);
+ ASSERT_EQ(mirror_peer_uuid, req.mirror_peer_uuid);
+ ASSERT_EQ(allow_remove, req.allow_remove);
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
+ void expect_apply_image_state(
+ MockApplyImageStateRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &req=mock_request, r]() {
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
+ void expect_mirror_image_snapshot_set_copy_progress(
+ librbd::MockTestImageCtx& mock_test_image_ctx, uint64_t snap_id,
+ bool completed, uint64_t last_copied_object, int r) {
+ bufferlist bl;
+ encode(snap_id, bl);
+ encode(completed, bl);
+ encode(last_copied_object, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(mock_test_image_ctx.md_ctx),
+ exec(mock_test_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("mirror_image_snapshot_set_copy_progress"),
+ ContentsEqual(bl), _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_send(MockCloseImageRequest &mock_close_image_request, int r) {
+ EXPECT_CALL(mock_close_image_request, send())
+ .WillOnce(Invoke([this, &mock_close_image_request, r]() {
+ *mock_close_image_request.image_ctx = nullptr;
+ m_threads->work_queue->queue(mock_close_image_request.on_finish, r);
+ }));
+ }
+
+ void expect_notification(MockThreads& mock_threads,
+ MockReplayerListener& mock_replayer_listener) {
+ EXPECT_CALL(mock_replayer_listener, handle_notification())
+ .WillRepeatedly(Invoke([this]() {
+ std::unique_lock locker{m_lock};
+ ++m_notifications;
+ m_cond.notify_all();
+ }));
+ }
+
+ int wait_for_notification(uint32_t count) {
+ std::unique_lock locker{m_lock};
+ for (uint32_t idx = 0; idx < count; ++idx) {
+ while (m_notifications == 0) {
+ if (m_cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ return -ETIMEDOUT;
+ }
+ }
+ --m_notifications;
+ }
+ return 0;
+ }
+
+ int init_entry_replayer(MockReplayer& mock_replayer,
+ MockThreads& mock_threads,
+ librbd::MockTestImageCtx& mock_local_image_ctx,
+ librbd::MockTestImageCtx& mock_remote_image_ctx,
+ MockReplayerListener& mock_replayer_listener,
+ MockImageMeta& mock_image_meta,
+ librbd::UpdateWatchCtx** update_watch_ctx) {
+ expect_register_update_watcher(mock_local_image_ctx, update_watch_ctx, 123,
+ 0);
+ expect_register_update_watcher(mock_remote_image_ctx, update_watch_ctx, 234,
+ 0);
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ int r = init_ctx.wait();
+ if (r < 0) {
+ return r;
+ }
+
+ return wait_for_notification(2);
+ }
+
+ int shut_down_entry_replayer(MockReplayer& mock_replayer,
+ MockThreads& mock_threads,
+ librbd::MockTestImageCtx& mock_local_image_ctx,
+ librbd::MockTestImageCtx& mock_remote_image_ctx) {
+ expect_unregister_update_watcher(mock_remote_image_ctx, 234, 0);
+ expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ return shutdown_ctx.wait();
+ }
+
+ librbd::ImageCtx* m_local_image_ctx = nullptr;
+ librbd::ImageCtx* m_remote_image_ctx = nullptr;
+
+ PoolMetaCache m_pool_meta_cache{g_ceph_context};
+
+ ceph::mutex m_lock = ceph::make_mutex(
+ "TestMockImageReplayerSnapshotReplayer");
+ ceph::condition_variable m_cond;
+ uint32_t m_notifications = 0;
+};
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, InitShutDown) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ // it should sync two snapshots and skip two (user and mirror w/o matching
+ // peer uuid)
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {""},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {4U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+
+ // init
+ expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
+ 0);
+ expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 234,
+ 0);
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
+ {{1, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 11, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // sync snap4
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {""},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {4U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {5U, librbd::SnapInfo{"snap5", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}
+ }, 0);
+ expect_snapshot_copy(mock_snapshot_copy_request, 1, 4, 11,
+ {{1, 11}, {2, 12}, {4, CEPH_NOSNAP}}, 0);
+ expect_get_image_state(mock_get_image_state_request, 4, 0);
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 4,
+ {{1, 11}, {2, 12}, {4, CEPH_NOSNAP}}, 14,
+ 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ expect_image_copy(mock_image_copy_request, 1, 4, 11, {},
+ {{1, 11}, {2, 12}, {4, CEPH_NOSNAP}}, 0);
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 14, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
+ false, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // prune non-primary snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {14U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 4, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {""},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {4U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}
+ }, 0);
+ expect_prune_non_primary_snapshot(mock_local_image_ctx, 11, 0);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {14U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 4, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {4U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}
+ }, 0);
+
+ // fire init
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(0, init_ctx.wait());
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(4));
+
+ // shut down
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSyncInitial) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject an incomplete sync snapshot with last_copied_object_number > 0
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, false, 123, {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ // re-sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 11, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0,
+ librbd::deep_copy::ObjectNumber{123U},
+ {{1, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 11, true, 123, 0);
+ expect_notify_update(mock_local_image_ctx);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(2));
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSyncDelta) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject an incomplete sync snapshot with last_copied_object_number > 0
+ // after a complete snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, false, 123, {{2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ // re-sync snap2
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 12, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 1, 2, 11,
+ librbd::deep_copy::ObjectNumber{123U},
+ {{2, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 12, true, 123, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
+ false, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // prune non-primary snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_prune_non_primary_snapshot(mock_local_image_ctx, 11, 0);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(2));
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSyncDeltaDemote) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject an incomplete sync snapshot with last_copied_object_number > 0
+ // after a primary demotion snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "local mirror uuid", 11, true, 0,
+ {{11, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, false, 123, {{2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ // re-sync snap2
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 12, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 1, 2, 11,
+ librbd::deep_copy::ObjectNumber{123U},
+ {{2, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 12, true, 123, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
+ false, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(2));
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedPendingSyncInitial) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject an incomplete sync snapshot with last_copied_object_number == 0
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, false, 0, {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ // re-sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 11, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
+ {{1, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 11, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(2));
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedPendingSyncDelta) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject an incomplete sync snapshot with last_copied_object_number == 0
+ // after a complete snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, false, 0, {{2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ // prune non-primary snap2
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ expect_prune_non_primary_snapshot(mock_local_image_ctx, 12, 0);
+
+ // sync snap2
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 1, 2, 11,
+ {{2, CEPH_NOSNAP}}, 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 2, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 2,
+ {{2, CEPH_NOSNAP}}, 13, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 1, 2, 11, {},
+ {{2, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 13, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
+ false, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // prune non-primary snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {13U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_prune_non_primary_snapshot(mock_local_image_ctx, 11, 0);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {13U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(2));
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedPendingSyncDeltaDemote) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject an incomplete sync snapshot with last_copied_object_number == 0
+ // after a primary demotion snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "local mirror uuid", 11, true, 0,
+ {{11, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, false, 0, {{2, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+
+ // prune non-primary snap2
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ expect_prune_non_primary_snapshot(mock_local_image_ctx, 12, 0);
+
+ // sync snap2
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 1, 2, 11,
+ {{2, CEPH_NOSNAP}}, 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 2, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 2,
+ {{2, CEPH_NOSNAP}}, 13, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 1, 2, 11, {},
+ {{2, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 13, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
+ false, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {13U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 2, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(2));
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteImageDemoted) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject a demotion snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ true, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
+ {{1, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 11, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(2));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, LocalImagePromoted) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject a promotion snapshot
+ mock_local_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY,
+ {"remote mirror peer uuid"}, "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, ResyncRequested) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // idle
+ expect_load_image_meta(mock_image_meta, true, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RegisterLocalUpdateWatcherError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayerListener mock_replayer_listener;
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ // init
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
+ -EINVAL);
+
+ // fire init
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-EINVAL, init_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RegisterRemoteUpdateWatcherError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayerListener mock_replayer_listener;
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ // init
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
+ 0);
+ expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 234,
+ -EINVAL);
+
+ expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
+
+ // fire init
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(-EINVAL, init_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, UnregisterRemoteUpdateWatcherError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+
+ // shut down
+ expect_unregister_update_watcher(mock_remote_image_ctx, 234, -EINVAL);
+ expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, UnregisterLocalUpdateWatcherError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+
+ // shut down
+ expect_unregister_update_watcher(mock_remote_image_ctx, 234, 0);
+ expect_unregister_update_watcher(mock_local_image_ctx, 123, -EINVAL);
+
+ C_SaferCond shutdown_ctx;
+ mock_replayer.shut_down(&shutdown_ctx);
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, LoadImageMetaError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // sync
+ expect_load_image_meta(mock_image_meta, false, -EINVAL);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshLocalImageError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // sync
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(mock_local_image_ctx, {}, -EINVAL);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshRemoteImageError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // sync
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(mock_remote_image_ctx, {}, -EINVAL);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, CopySnapshotsError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ -EINVAL);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, GetImageStateError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, -EINVAL);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, CreateNonPrimarySnapshotError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, -EINVAL);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, UpdateMirrorImageStateError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, -EIO);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EIO, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RequestSyncError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
+ -ECANCELED);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-ECANCELED, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, CopyImageError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP,true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
+ {{1, CEPH_NOSNAP}}, -EINVAL);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, UpdateNonPrimarySnapshotError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
+ {{1, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 11, true, 0, -EINVAL);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkPeerError) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap2
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 1, 2, 11, {{2, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 2, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 2,
+ {{2, CEPH_NOSNAP}}, 12, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 1, 2, 11, {},
+ {{2, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 12, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
+ false, -EINVAL);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, SplitBrain) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject a primary demote to local image
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {}, "", CEPH_NOSNAP,
+ true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // detect split-brain
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EEXIST, mock_replayer.get_error_code());
+ ASSERT_EQ(std::string{"split-brain"}, mock_replayer.get_error_description());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteSnapshotMissingSplitBrain) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject a missing remote start snap (deleted)
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {},
+ "remote mirror uuid", 1, true, 0,
+ {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}}};
+ mock_remote_image_ctx.snap_info = {
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // split-brain due to missing snapshot 1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EEXIST, mock_replayer.get_error_code());
+ ASSERT_EQ(std::string{"split-brain"}, mock_replayer.get_error_description());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteFailover) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject a primary demote to local image
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "local mirror uuid", 12U, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_ids = {
+ {{cls::rbd::UserSnapshotNamespace{}, "snap1"}, 11},
+ {{cls::rbd::MirrorSnapshotNamespace{}, "snap2"}, 12}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {}, "", CEPH_NOSNAP,
+ true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // attach to promoted remote image
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 2, 3, 12,
+ {{2, 12}, {3, CEPH_NOSNAP}}, 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 3, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 3,
+ {{1, 11}, {2, 12}, {3, CEPH_NOSNAP}}, 13,
+ 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 2, 3, 12, {},
+ {{1, 11}, {2, 12}, {3, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 13, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 2, "remote mirror peer uuid",
+ false, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {}, "", CEPH_NOSNAP,
+ true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {13U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {},
+ "remote mirror uuid", 3, true, 0,
+ {{1, 11}, {2, 12}, {3, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "local mirror uuid", 12U, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {}, "", CEPH_NOSNAP, true, 0,
+ {}},
+ 0, {}, 0, 0, {}}}
+ }, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(2));
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkRemoteSnapshot) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ // it should attempt to unlink from remote snap1 since we don't need it
+ // anymore
+ mock_local_image_ctx.snap_info = {
+ {14U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 4, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {4U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+
+ // init
+ expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
+ 0);
+ expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 234,
+ 0);
+
+ // unlink snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
+ false, 0);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, true);
+ expect_refresh(
+ mock_remote_image_ctx, {
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {""},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {4U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}
+ }, 0);
+
+ // fire init
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(0, init_ctx.wait());
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(3));
+
+ // shut down
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, SkipImageSync) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", 0U, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+
+ // init
+ expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
+ 0);
+ expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 234,
+ 0);
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 11, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
+ 1, true, 0, {{1, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // fire init
+ C_SaferCond init_ctx;
+ mock_replayer.init(&init_ctx);
+ ASSERT_EQ(0, init_ctx.wait());
+
+ // wait for sync to complete
+ ASSERT_EQ(0, wait_for_notification(3));
+
+ // shut down
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, ImageNameUpdated) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // change the name of the image
+ mock_local_image_ctx.name = "NEW NAME";
+
+ // idle
+ expect_load_image_meta(mock_image_meta, true, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(2));
+ auto image_spec = image_replayer::util::compute_image_spec(m_local_io_ctx,
+ "NEW NAME");
+ ASSERT_EQ(image_spec, mock_replayer.get_image_spec());
+ ASSERT_FALSE(mock_replayer.is_replaying());
+
+ // shut down
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, ApplyImageStatePendingShutdown) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ C_SaferCond shutdown_ctx;
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
+ {{1, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ EXPECT_CALL(mock_apply_state_request, send())
+ .WillOnce(Invoke([this, &req=mock_apply_state_request,
+ &replayer=mock_replayer, &ctx=shutdown_ctx]() {
+ // inject a shutdown, to be pended due to STATE_REPLAYING
+ replayer.shut_down(&ctx);
+ m_threads->work_queue->queue(req.on_finish, 0);
+ }));
+ expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 11, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // shutdown should be resumed
+ expect_unregister_update_watcher(mock_remote_image_ctx, 234, 0);
+ expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(0, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerSnapshotReplayer, ApplyImageStateErrorPendingShutdown) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ C_SaferCond shutdown_ctx;
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject snapshot
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"}, "",
+ CEPH_NOSNAP, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+
+ // sync snap1
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 0, 1, 0, {{1, CEPH_NOSNAP}},
+ 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 1, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 1,
+ {{1, CEPH_NOSNAP}}, 11, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_update_mirror_image_state(mock_image_state_update_request, 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
+ {{1, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ EXPECT_CALL(mock_apply_state_request, send())
+ .WillOnce(Invoke([this, &req=mock_apply_state_request,
+ &replayer=mock_replayer, &ctx=shutdown_ctx]() {
+ // inject a shutdown, to be pended due to STATE_REPLAYING
+ replayer.shut_down(&ctx);
+ m_threads->work_queue->queue(req.on_finish, -EINVAL);
+ }));
+ expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // shutdown should be resumed
+ expect_unregister_update_watcher(mock_remote_image_ctx, 234, 0);
+ expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ ASSERT_EQ(0, shutdown_ctx.wait());
+}
+
+} // namespace snapshot
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc b/src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc
new file mode 100644
index 000000000..d8d7ed2da
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc
@@ -0,0 +1,1248 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "librbd/journal/TypeTraits.h"
+#include "tools/rbd_mirror/BaseRequest.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/BootstrapRequest.h"
+#include "tools/rbd_mirror/image_replayer/OpenImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/PrepareLocalImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/PrepareRemoteImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/StateBuilder.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h"
+#include "test/rbd_mirror/mock/MockBaseRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+class ProgressContext;
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue) {
+ }
+};
+
+template<>
+struct ImageSync<librbd::MockTestImageCtx> {
+ static ImageSync* s_instance;
+ Context *on_finish = nullptr;
+
+ static ImageSync* create(
+ Threads<librbd::MockTestImageCtx>* threads,
+ librbd::MockTestImageCtx *local_image_ctx,
+ librbd::MockTestImageCtx *remote_image_ctx,
+ const std::string &local_mirror_uuid,
+ image_sync::SyncPointHandler* sync_point_handler,
+ InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher,
+ ProgressContext *progress_ctx, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ImageSync() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~ImageSync() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(get, void());
+ MOCK_METHOD0(put, void());
+ MOCK_METHOD0(send, void());
+ MOCK_METHOD0(cancel, void());
+};
+
+ImageSync<librbd::MockTestImageCtx>*
+ ImageSync<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct InstanceWatcher<librbd::MockTestImageCtx> {
+};
+
+namespace image_replayer {
+
+template<>
+struct OpenImageRequest<librbd::MockTestImageCtx> {
+ static OpenImageRequest* s_instance;
+ librbd::MockTestImageCtx **image_ctx = nullptr;
+ Context *on_finish = nullptr;
+
+ static OpenImageRequest* create(librados::IoCtx &io_ctx,
+ librbd::MockTestImageCtx **image_ctx,
+ const std::string &image_id,
+ bool read_only, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_ctx = image_ctx;
+ s_instance->on_finish = on_finish;
+ s_instance->construct(io_ctx, image_id);
+ return s_instance;
+ }
+
+ OpenImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~OpenImageRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD2(construct, void(librados::IoCtx &io_ctx,
+ const std::string &image_id));
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct OpenLocalImageRequest<librbd::MockTestImageCtx> {
+ static OpenLocalImageRequest* s_instance;
+ librbd::MockTestImageCtx **image_ctx = nullptr;
+ Context *on_finish = nullptr;
+
+ static OpenLocalImageRequest* create(librados::IoCtx &local_io_ctx,
+ librbd::MockTestImageCtx **local_image_ctx,
+ const std::string &local_image_id,
+ librbd::asio::ContextWQ *work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_ctx = local_image_ctx;
+ s_instance->on_finish = on_finish;
+ s_instance->construct(local_io_ctx, local_image_id);
+ return s_instance;
+ }
+
+ OpenLocalImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~OpenLocalImageRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD2(construct, void(librados::IoCtx &io_ctx,
+ const std::string &image_id));
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct PrepareLocalImageRequest<librbd::MockTestImageCtx> {
+ static PrepareLocalImageRequest* s_instance;
+ std::string *local_image_name = nullptr;
+ StateBuilder<librbd::MockTestImageCtx>** state_builder = nullptr;
+ Context *on_finish = nullptr;
+
+ static PrepareLocalImageRequest* create(librados::IoCtx &,
+ const std::string &global_image_id,
+ std::string *local_image_name,
+ StateBuilder<librbd::MockTestImageCtx>** state_builder,
+ librbd::asio::ContextWQ *work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->local_image_name = local_image_name;
+ s_instance->state_builder = state_builder;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ PrepareLocalImageRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct PrepareRemoteImageRequest<librbd::MockTestImageCtx> {
+ static PrepareRemoteImageRequest* s_instance;
+ StateBuilder<librbd::MockTestImageCtx>** state_builder = nullptr;
+ Context *on_finish = nullptr;
+
+ static PrepareRemoteImageRequest* create(Threads<librbd::MockTestImageCtx> *threads,
+ librados::IoCtx &,
+ librados::IoCtx &,
+ const std::string &global_image_id,
+ const std::string &local_mirror_uuid,
+ const RemotePoolMeta& remote_pool_meta,
+ ::journal::CacheManagerHandler *cache_manager_handler,
+ StateBuilder<librbd::MockTestImageCtx>** state_builder,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->state_builder = state_builder;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ PrepareRemoteImageRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ static StateBuilder* s_instance;
+
+ image_sync::MockSyncPointHandler mock_sync_point_handler;
+ MockBaseRequest mock_base_request;
+
+ librbd::MockTestImageCtx* local_image_ctx = nullptr;
+ librbd::MockTestImageCtx* remote_image_ctx = nullptr;
+ std::string local_image_id;
+ std::string remote_mirror_uuid;
+ std::string remote_image_id;
+
+ static StateBuilder* create(const std::string&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ image_sync::MockSyncPointHandler* create_sync_point_handler() {
+ return &mock_sync_point_handler;
+ }
+
+ StateBuilder() {
+ s_instance = this;
+ }
+
+ MOCK_CONST_METHOD0(is_disconnected, bool());
+ MOCK_CONST_METHOD0(is_local_primary, bool());
+ MOCK_CONST_METHOD0(is_remote_primary, bool());
+ MOCK_CONST_METHOD0(is_linked, bool());
+
+ MOCK_CONST_METHOD0(replay_requires_remote_image, bool());
+ MOCK_METHOD1(close_remote_image, void(Context*));
+
+ MOCK_METHOD6(create_local_image_request,
+ BaseRequest*(Threads<librbd::MockTestImageCtx>*,
+ librados::IoCtx&,
+ const std::string&,
+ PoolMetaCache*,
+ ProgressContext*,
+ Context*));
+ MOCK_METHOD5(create_prepare_replay_request,
+ BaseRequest*(const std::string&,
+ ProgressContext*,
+ bool*, bool*, Context*));
+
+ void destroy_sync_point_handler() {
+ }
+ void destroy() {
+ }
+};
+
+OpenImageRequest<librbd::MockTestImageCtx>*
+ OpenImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+OpenLocalImageRequest<librbd::MockTestImageCtx>*
+ OpenLocalImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+PrepareLocalImageRequest<librbd::MockTestImageCtx>*
+ PrepareLocalImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+PrepareRemoteImageRequest<librbd::MockTestImageCtx>*
+ PrepareRemoteImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+StateBuilder<librbd::MockTestImageCtx>*
+ StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/image_replayer/BootstrapRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+MATCHER_P(IsSameIoCtx, io_ctx, "") {
+ return &get_mock_io_ctx(arg) == &get_mock_io_ctx(*io_ctx);
+}
+
+class TestMockImageReplayerBootstrapRequest : public TestMockFixture {
+public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef BootstrapRequest<librbd::MockTestImageCtx> MockBootstrapRequest;
+ typedef ImageSync<librbd::MockTestImageCtx> MockImageSync;
+ typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
+ typedef OpenImageRequest<librbd::MockTestImageCtx> MockOpenImageRequest;
+ typedef OpenLocalImageRequest<librbd::MockTestImageCtx> MockOpenLocalImageRequest;
+ typedef PrepareLocalImageRequest<librbd::MockTestImageCtx> MockPrepareLocalImageRequest;
+ typedef PrepareRemoteImageRequest<librbd::MockTestImageCtx> MockPrepareRemoteImageRequest;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+ typedef std::list<cls::journal::Tag> Tags;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ void expect_send(MockPrepareLocalImageRequest &mock_request,
+ MockStateBuilder& mock_state_builder,
+ const std::string& local_image_id,
+ const std::string& local_image_name, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([&mock_request, &mock_state_builder, local_image_id,
+ local_image_name, r]() {
+ if (r == 0) {
+ *mock_request.state_builder = &mock_state_builder;
+ mock_state_builder.local_image_id = local_image_id;
+ *mock_request.local_image_name = local_image_name;
+ }
+ mock_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_send(MockPrepareRemoteImageRequest& mock_request,
+ MockStateBuilder& mock_state_builder,
+ const std::string& remote_mirror_uuid,
+ const std::string& remote_image_id,
+ int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([&mock_request, &mock_state_builder, remote_mirror_uuid,
+ remote_image_id, r]() {
+ if (r >= 0) {
+ *mock_request.state_builder = &mock_state_builder;
+ mock_state_builder.remote_image_id = remote_image_id;
+ }
+
+ mock_state_builder.remote_mirror_uuid = remote_mirror_uuid;
+ mock_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_is_local_primary(MockStateBuilder& mock_state_builder,
+ bool is_primary) {
+ EXPECT_CALL(mock_state_builder, is_local_primary())
+ .WillOnce(Return(is_primary));
+ }
+
+ void expect_is_remote_primary(MockStateBuilder& mock_state_builder,
+ bool is_primary) {
+ EXPECT_CALL(mock_state_builder, is_remote_primary())
+ .WillOnce(Return(is_primary));
+ }
+
+ void expect_is_linked(MockStateBuilder& mock_state_builder, bool is_linked) {
+ EXPECT_CALL(mock_state_builder, is_linked())
+ .WillOnce(Return(is_linked));
+ }
+
+ void expect_is_disconnected(MockStateBuilder& mock_state_builder,
+ bool is_disconnected) {
+ EXPECT_CALL(mock_state_builder, is_disconnected())
+ .WillOnce(Return(is_disconnected));
+ }
+
+ void expect_replay_requires_remote_image(MockStateBuilder& mock_state_builder,
+ bool requires_image) {
+ EXPECT_CALL(mock_state_builder, replay_requires_remote_image())
+ .WillOnce(Return(requires_image));
+ }
+
+ void expect_open_image(MockOpenImageRequest &mock_open_image_request,
+ librados::IoCtx &io_ctx, const std::string &image_id,
+ librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(mock_open_image_request,
+ construct(IsSameIoCtx(&io_ctx), image_id));
+ EXPECT_CALL(mock_open_image_request, send())
+ .WillOnce(Invoke([this, &mock_open_image_request, &mock_image_ctx, r]() {
+ *mock_open_image_request.image_ctx = &mock_image_ctx;
+ m_threads->work_queue->queue(mock_open_image_request.on_finish, r);
+ }));
+ }
+
+ void expect_open_local_image(MockOpenLocalImageRequest &mock_open_local_image_request,
+ librados::IoCtx &io_ctx, const std::string &image_id,
+ librbd::MockTestImageCtx *mock_image_ctx, int r) {
+ EXPECT_CALL(mock_open_local_image_request,
+ construct(IsSameIoCtx(&io_ctx), image_id));
+ EXPECT_CALL(mock_open_local_image_request, send())
+ .WillOnce(Invoke([this, &mock_open_local_image_request, mock_image_ctx, r]() {
+ if (r >= 0) {
+ *mock_open_local_image_request.image_ctx = mock_image_ctx;
+ }
+ m_threads->work_queue->queue(mock_open_local_image_request.on_finish,
+ r);
+ }));
+ }
+
+ void expect_close_remote_image(
+ MockStateBuilder& mock_state_builder, int r) {
+ EXPECT_CALL(mock_state_builder, close_remote_image(_))
+ .WillOnce(Invoke([&mock_state_builder, r]
+ (Context* on_finish) {
+ mock_state_builder.remote_image_ctx = nullptr;
+ on_finish->complete(r);
+ }));
+ }
+
+ void expect_create_local_image(MockStateBuilder& mock_state_builder,
+ const std::string& local_image_id, int r) {
+ EXPECT_CALL(mock_state_builder,
+ create_local_image_request(_, _, _, _, _, _))
+ .WillOnce(WithArg<5>(
+ Invoke([&mock_state_builder, local_image_id, r](Context* ctx) {
+ if (r >= 0) {
+ mock_state_builder.local_image_id = local_image_id;
+ }
+ mock_state_builder.mock_base_request.on_finish = ctx;
+ return &mock_state_builder.mock_base_request;
+ })));
+ EXPECT_CALL(mock_state_builder.mock_base_request, send())
+ .WillOnce(Invoke([this, &mock_state_builder, r]() {
+ m_threads->work_queue->queue(
+ mock_state_builder.mock_base_request.on_finish, r);
+ }));
+ }
+
+ void expect_prepare_replay(MockStateBuilder& mock_state_builder,
+ bool resync_requested, bool syncing, int r) {
+ EXPECT_CALL(mock_state_builder,
+ create_prepare_replay_request(_, _, _, _, _))
+ .WillOnce(WithArgs<2, 3, 4>(
+ Invoke([&mock_state_builder, resync_requested, syncing, r]
+ (bool* resync, bool* sync, Context* ctx) {
+ if (r >= 0) {
+ *resync = resync_requested;
+ *sync = syncing;
+ }
+ mock_state_builder.mock_base_request.on_finish = ctx;
+ return &mock_state_builder.mock_base_request;
+ })));
+ EXPECT_CALL(mock_state_builder.mock_base_request, send())
+ .WillOnce(Invoke([this, &mock_state_builder, r]() {
+ m_threads->work_queue->queue(
+ mock_state_builder.mock_base_request.on_finish, r);
+ }));
+ }
+
+ void expect_image_sync(MockImageSync &mock_image_sync, int r) {
+ EXPECT_CALL(mock_image_sync, get());
+ EXPECT_CALL(mock_image_sync, send())
+ .WillOnce(Invoke([this, &mock_image_sync, r]() {
+ m_threads->work_queue->queue(mock_image_sync.on_finish, r);
+ }));
+ EXPECT_CALL(mock_image_sync, put());
+ }
+
+ MockBootstrapRequest *create_request(MockThreads* mock_threads,
+ MockInstanceWatcher *mock_instance_watcher,
+ const std::string &global_image_id,
+ const std::string &local_mirror_uuid,
+ Context *on_finish) {
+ return new MockBootstrapRequest(mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ mock_instance_watcher,
+ global_image_id,
+ local_mirror_uuid,
+ {"remote mirror uuid",
+ "remote mirror peer uuid"},
+ nullptr, nullptr, nullptr,
+ &m_mock_state_builder,
+ &m_do_resync, on_finish);
+ }
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ librbd::ImageCtx *m_local_image_ctx = nullptr;
+
+ MockStateBuilder* m_mock_state_builder = nullptr;
+ bool m_do_resync = false;
+};
+
+TEST_F(TestMockImageReplayerBootstrapRequest, Success) {
+ InSequence seq;
+
+ // prepare local image
+ MockStateBuilder mock_state_builder;
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, false, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareRemoteImageNotPrimaryLocalDNE) {
+ InSequence seq;
+
+ // prepare local image
+ MockStateBuilder mock_state_builder;
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, -ENOENT);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, false);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-EREMOTEIO, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareRemoteImageNotPrimaryLocalUnlinked) {
+ InSequence seq;
+
+ // prepare local image
+ MockStateBuilder mock_state_builder;
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, false);
+ expect_is_linked(mock_state_builder, false);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-EREMOTEIO, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareRemoteImageNotPrimaryLocalLinked) {
+ InSequence seq;
+
+ // prepare local image
+ MockStateBuilder mock_state_builder;
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, false);
+ expect_is_linked(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, false, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareRemoteImageDNELocalLinked) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, -ENOENT);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_linked(mock_state_builder, true);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-ENOLINK, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareRemoteImageDNELocalLinkedCanceled) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, -ENOENT);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_linked(mock_state_builder, true);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->cancel();
+ request->send();
+ ASSERT_EQ(-ENOLINK, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, OpenLocalImageError) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx,
+ -EINVAL);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, OpenLocalImageDNE) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx,
+ -ENOENT);
+
+ // create local image
+ expect_create_local_image(mock_state_builder, "local image id", 0);
+
+ // re-open the local image
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ "local image id", &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, false, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, OpenLocalImagePrimary) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx,
+ -EREMOTEIO);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-EREMOTEIO, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, CreateLocalImageError) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder, "", "",
+ -ENOENT);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // create local image
+ expect_create_local_image(mock_state_builder, "local image id", -EINVAL);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareReplayError) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, false, -EINVAL);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareReplayResyncRequested) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, true, false, 0);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(m_do_resync);
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareReplaySyncing) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, true, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // image sync
+ MockImageSync mock_image_sync;
+ expect_image_sync(mock_image_sync, 0);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrepareReplayDisconnected) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, false, 0);
+ expect_is_disconnected(mock_state_builder, true);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, ImageSyncError) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, true, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // image sync
+ MockImageSync mock_image_sync;
+ expect_image_sync(mock_image_sync, -EINVAL);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, ImageSyncCanceled) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, true, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->cancel();
+ request->send();
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, CloseRemoteImageError) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, false, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // attempt to close remote image
+ expect_replay_requires_remote_image(mock_state_builder, false);
+ expect_close_remote_image(mock_state_builder, -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, ReplayRequiresRemoteImage) {
+ InSequence seq;
+
+ // prepare local image
+ MockPrepareLocalImageRequest mock_prepare_local_image_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_prepare_local_image_request, mock_state_builder,
+ m_local_image_ctx->id, m_local_image_ctx->name, 0);
+
+ // prepare remote image
+ MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
+ expect_send(mock_prepare_remote_image_request, mock_state_builder,
+ "remote mirror uuid", m_remote_image_ctx->id, 0);
+ expect_is_local_primary(mock_state_builder, false);
+ expect_is_remote_primary(mock_state_builder, true);
+
+ // open the remote image
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+
+ // open the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+
+ // prepare replay
+ expect_prepare_replay(mock_state_builder, false, false, 0);
+ expect_is_disconnected(mock_state_builder, false);
+
+ // remote image is left open
+ expect_replay_requires_remote_image(mock_state_builder, true);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockInstanceWatcher mock_instance_watcher;
+ MockBootstrapRequest *request = create_request(
+ &mock_threads, &mock_instance_watcher, "global image id",
+ "local mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/test_mock_CreateImageRequest.cc b/src/test/rbd_mirror/image_replayer/test_mock_CreateImageRequest.cc
new file mode 100644
index 000000000..ed2cf8f96
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/test_mock_CreateImageRequest.cc
@@ -0,0 +1,614 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "tools/rbd_mirror/PoolMetaCache.h"
+#include "tools/rbd_mirror/image_replayer/CreateImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/OpenImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h"
+#include "librbd/image/CreateRequest.h"
+#include "librbd/image/CloneRequest.h"
+#include "tools/rbd_mirror/Threads.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace image {
+
+template<>
+struct CreateRequest<librbd::MockTestImageCtx> {
+ static CreateRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static CreateRequest *create(const ConfigProxy& config, IoCtx &ioctx,
+ const std::string &imgname,
+ const std::string &imageid, uint64_t size,
+ const librbd::ImageOptions &image_options,
+ bool skip_mirror_enable,
+ cls::rbd::MirrorImageMode mode,
+ const std::string &non_primary_global_image_id,
+ const std::string &primary_mirror_uuid,
+ MockContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ EXPECT_FALSE(non_primary_global_image_id.empty());
+ EXPECT_FALSE(primary_mirror_uuid.empty());
+ EXPECT_FALSE(skip_mirror_enable);
+ s_instance->on_finish = on_finish;
+ s_instance->construct(ioctx);
+ return s_instance;
+ }
+
+ CreateRequest() {
+ s_instance = this;
+ }
+
+ ~CreateRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+ MOCK_METHOD1(construct, void(librados::IoCtx &ioctx));
+};
+
+CreateRequest<librbd::MockTestImageCtx>*
+ CreateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct CloneRequest<librbd::MockTestImageCtx> {
+ static CloneRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static CloneRequest *create(ConfigProxy& config, IoCtx &p_ioctx,
+ const std::string &p_id,
+ const std::string &p_snap_name,
+ const cls::rbd::SnapshotNamespace& snap_ns,
+ uint64_t p_snap_id,
+ IoCtx &c_ioctx, const std::string &c_name,
+ const std::string &c_id, ImageOptions c_options,
+ cls::rbd::MirrorImageMode mode,
+ const std::string &non_primary_global_image_id,
+ const std::string &primary_mirror_uuid,
+ MockContextWQ *op_work_queue,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ s_instance->construct();
+ return s_instance;
+ }
+
+ CloneRequest() {
+ s_instance = this;
+ }
+
+ ~CloneRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+ MOCK_METHOD0(construct, void());
+};
+
+CloneRequest<librbd::MockTestImageCtx>*
+ CloneRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue) {
+ }
+};
+
+namespace image_replayer {
+
+template<>
+struct CloseImageRequest<librbd::MockTestImageCtx> {
+ static CloseImageRequest* s_instance;
+ Context *on_finish = nullptr;
+
+ static CloseImageRequest* create(librbd::MockTestImageCtx **image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->construct(*image_ctx);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ CloseImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~CloseImageRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD1(construct, void(librbd::MockTestImageCtx *image_ctx));
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct OpenImageRequest<librbd::MockTestImageCtx> {
+ static OpenImageRequest* s_instance;
+ librbd::MockTestImageCtx **image_ctx = nullptr;
+ Context *on_finish = nullptr;
+
+ static OpenImageRequest* create(librados::IoCtx &io_ctx,
+ librbd::MockTestImageCtx **image_ctx,
+ const std::string &image_id,
+ bool read_only, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_ctx = image_ctx;
+ s_instance->on_finish = on_finish;
+ s_instance->construct(io_ctx, image_id);
+ return s_instance;
+ }
+
+ OpenImageRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~OpenImageRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD2(construct, void(librados::IoCtx &io_ctx,
+ const std::string &image_id));
+ MOCK_METHOD0(send, void());
+};
+
+CloseImageRequest<librbd::MockTestImageCtx>*
+ CloseImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+OpenImageRequest<librbd::MockTestImageCtx>*
+ OpenImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/image_replayer/CreateImageRequest.cc"
+template class rbd::mirror::image_replayer::CreateImageRequest<librbd::MockTestImageCtx>;
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+MATCHER_P(IsSameIoCtx, io_ctx, "") {
+ return &get_mock_io_ctx(arg) == &get_mock_io_ctx(*io_ctx);
+}
+
+class TestMockImageReplayerCreateImageRequest : public TestMockFixture {
+public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef librbd::image::CreateRequest<librbd::MockTestImageCtx> MockCreateRequest;
+ typedef librbd::image::CloneRequest<librbd::MockTestImageCtx> MockCloneRequest;
+ typedef CreateImageRequest<librbd::MockTestImageCtx> MockCreateImageRequest;
+ typedef OpenImageRequest<librbd::MockTestImageCtx> MockOpenImageRequest;
+ typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+ }
+
+ void snap_create(librbd::ImageCtx *image_ctx, const std::string &snap_name) {
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, image_ctx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ snap_name, 0, prog_ctx));
+ ASSERT_EQ(0, image_ctx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ snap_name));
+ ASSERT_EQ(0, image_ctx->state->refresh());
+ }
+
+ int clone_image(librbd::ImageCtx *parent_image_ctx,
+ const std::string &snap_name, const std::string &clone_name) {
+ snap_create(parent_image_ctx, snap_name);
+
+ int order = 0;
+ return librbd::clone(m_remote_io_ctx, parent_image_ctx->name.c_str(),
+ snap_name.c_str(), m_remote_io_ctx,
+ clone_name.c_str(), parent_image_ctx->features,
+ &order, 0, 0);
+ }
+
+ void expect_create_image(MockCreateRequest &mock_create_request,
+ librados::IoCtx &ioctx, int r) {
+ EXPECT_CALL(mock_create_request, construct(IsSameIoCtx(&ioctx)));
+ EXPECT_CALL(mock_create_request, send())
+ .WillOnce(Invoke([this, &mock_create_request, r]() {
+ m_threads->work_queue->queue(mock_create_request.on_finish, r);
+ }));
+ }
+
+ void expect_ioctx_create(librados::IoCtx &io_ctx) {
+ librados::MockTestMemIoCtxImpl &io_ctx_impl = get_mock_io_ctx(io_ctx);
+ EXPECT_CALL(*get_mock_io_ctx(io_ctx).get_mock_rados_client(), create_ioctx(_, _))
+ .WillOnce(DoAll(GetReference(&io_ctx_impl),
+ Return(&get_mock_io_ctx(io_ctx))));
+ }
+
+ void expect_get_parent_global_image_id(librados::IoCtx &io_ctx,
+ const std::string &global_id, int r) {
+ cls::rbd::MirrorImage mirror_image;
+ mirror_image.global_image_id = global_id;
+
+ bufferlist bl;
+ encode(mirror_image, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(io_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_get"),
+ _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+ *out_bl = bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_mirror_image_get_image_id(librados::IoCtx &io_ctx,
+ const std::string &image_id, int r) {
+ bufferlist bl;
+ encode(image_id, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(io_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"),
+ StrEq("mirror_image_get_image_id"), _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+ *out_bl = bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_open_image(MockOpenImageRequest &mock_open_image_request,
+ librados::IoCtx &io_ctx, const std::string &image_id,
+ librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(mock_open_image_request, construct(IsSameIoCtx(&io_ctx), image_id));
+ EXPECT_CALL(mock_open_image_request, send())
+ .WillOnce(Invoke([this, &mock_open_image_request, &mock_image_ctx, r]() {
+ *mock_open_image_request.image_ctx = &mock_image_ctx;
+ m_threads->work_queue->queue(mock_open_image_request.on_finish, r);
+ }));
+ }
+
+ void expect_test_op_features(librbd::MockTestImageCtx& mock_image_ctx,
+ bool enabled) {
+ EXPECT_CALL(mock_image_ctx,
+ test_op_features(RBD_OPERATION_FEATURE_CLONE_CHILD))
+ .WillOnce(Return(enabled));
+ }
+
+ void expect_clone_image(MockCloneRequest &mock_clone_request,
+ int r) {
+ EXPECT_CALL(mock_clone_request, construct());
+ EXPECT_CALL(mock_clone_request, send())
+ .WillOnce(Invoke([this, &mock_clone_request, r]() {
+ m_threads->work_queue->queue(mock_clone_request.on_finish, r);
+ }));
+ }
+
+ void expect_close_image(MockCloseImageRequest &mock_close_image_request,
+ librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(mock_close_image_request, construct(&mock_image_ctx));
+ EXPECT_CALL(mock_close_image_request, send())
+ .WillOnce(Invoke([this, &mock_close_image_request, r]() {
+ m_threads->work_queue->queue(mock_close_image_request.on_finish, r);
+ }));
+ }
+
+ MockCreateImageRequest *create_request(MockThreads* mock_threads,
+ const std::string &global_image_id,
+ const std::string &remote_mirror_uuid,
+ const std::string &local_image_name,
+ const std::string &local_image_id,
+ librbd::MockTestImageCtx &mock_remote_image_ctx,
+ Context *on_finish) {
+ return new MockCreateImageRequest(mock_threads, m_local_io_ctx,
+ global_image_id, remote_mirror_uuid,
+ local_image_name, local_image_id,
+ &mock_remote_image_ctx,
+ &m_pool_meta_cache,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ on_finish);
+ }
+
+ PoolMetaCache m_pool_meta_cache{g_ceph_context};
+ librbd::ImageCtx *m_remote_image_ctx;
+};
+
+TEST_F(TestMockImageReplayerCreateImageRequest, Create) {
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockCreateRequest mock_create_request;
+
+ InSequence seq;
+ expect_create_image(mock_create_request, m_local_io_ctx, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_image_ctx, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerCreateImageRequest, CreateError) {
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockCreateRequest mock_create_request;
+
+ InSequence seq;
+ expect_create_image(mock_create_request, m_local_io_ctx, -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_image_ctx, &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerCreateImageRequest, CloneGetGlobalImageIdError) {
+ std::string clone_image_name = get_temp_image_name();
+ ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
+
+ librbd::ImageCtx *remote_clone_image_ctx;
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
+ &remote_clone_image_ctx));
+
+ librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
+
+ InSequence seq;
+ expect_ioctx_create(m_remote_io_ctx);
+ expect_ioctx_create(m_local_io_ctx);
+ expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", -ENOENT);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_clone_image_ctx,
+ &ctx);
+ request->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerCreateImageRequest, CloneGetLocalParentImageIdError) {
+ std::string clone_image_name = get_temp_image_name();
+ ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
+
+ librbd::ImageCtx *remote_clone_image_ctx;
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
+ &remote_clone_image_ctx));
+
+ librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
+
+ InSequence seq;
+ expect_ioctx_create(m_remote_io_ctx);
+ expect_ioctx_create(m_local_io_ctx);
+ expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
+ expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", -ENOENT);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_clone_image_ctx,
+ &ctx);
+ request->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerCreateImageRequest, CloneOpenRemoteParentError) {
+ std::string clone_image_name = get_temp_image_name();
+ ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
+
+ librbd::ImageCtx *remote_clone_image_ctx;
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
+ &remote_clone_image_ctx));
+
+ librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+
+ InSequence seq;
+ expect_ioctx_create(m_remote_io_ctx);
+ expect_ioctx_create(m_local_io_ctx);
+ expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
+ expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", 0);
+
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ m_remote_image_ctx->id, mock_remote_parent_image_ctx,
+ -ENOENT);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_clone_image_ctx,
+ &ctx);
+ request->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerCreateImageRequest, CloneParentImageSyncing) {
+ librbd::RBD rbd;
+ librbd::ImageCtx *local_image_ctx;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &local_image_ctx));
+ snap_create(local_image_ctx, "snap");
+ snap_create(m_remote_image_ctx, ".rbd-mirror.local parent uuid.1234");
+
+ std::string clone_image_name = get_temp_image_name();
+ ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
+
+ librbd::ImageCtx *remote_clone_image_ctx;
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
+ &remote_clone_image_ctx));
+
+ m_pool_meta_cache.set_local_pool_meta(
+ m_local_io_ctx.get_id(), {"local parent uuid"});
+
+ librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ MockCloseImageRequest mock_close_image_request;
+
+ InSequence seq;
+ expect_ioctx_create(m_remote_io_ctx);
+ expect_ioctx_create(m_local_io_ctx);
+ expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
+ expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", 0);
+
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
+ expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_clone_image_ctx,
+ &ctx);
+ request->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerCreateImageRequest, CloneError) {
+ librbd::RBD rbd;
+ librbd::ImageCtx *local_image_ctx;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &local_image_ctx));
+ snap_create(local_image_ctx, "snap");
+
+ std::string clone_image_name = get_temp_image_name();
+ ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
+
+ librbd::ImageCtx *remote_clone_image_ctx;
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
+ &remote_clone_image_ctx));
+
+ m_pool_meta_cache.set_local_pool_meta(
+ m_local_io_ctx.get_id(), {"local parent uuid"});
+
+ librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
+ MockCloneRequest mock_clone_request;
+ MockOpenImageRequest mock_open_image_request;
+ MockCloseImageRequest mock_close_image_request;
+
+ InSequence seq;
+ expect_ioctx_create(m_remote_io_ctx);
+ expect_ioctx_create(m_local_io_ctx);
+ expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
+ expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", 0);
+
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
+ expect_test_op_features(mock_remote_clone_image_ctx, false);
+ expect_clone_image(mock_clone_request, -EINVAL);
+ expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_clone_image_ctx,
+ &ctx);
+ request->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerCreateImageRequest, CloneRemoteParentCloseError) {
+ librbd::RBD rbd;
+ librbd::ImageCtx *local_image_ctx;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &local_image_ctx));
+ snap_create(local_image_ctx, "snap");
+
+ std::string clone_image_name = get_temp_image_name();
+ ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
+
+ librbd::ImageCtx *remote_clone_image_ctx;
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
+ &remote_clone_image_ctx));
+
+ m_pool_meta_cache.set_local_pool_meta(
+ m_local_io_ctx.get_id(), {"local parent uuid"});
+
+ librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
+ MockCloneRequest mock_clone_request;
+ MockOpenImageRequest mock_open_image_request;
+ MockCloseImageRequest mock_close_image_request;
+
+ InSequence seq;
+ expect_ioctx_create(m_remote_io_ctx);
+ expect_ioctx_create(m_local_io_ctx);
+ expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
+ expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", 0);
+
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
+ expect_test_op_features(mock_remote_clone_image_ctx, false);
+ expect_clone_image(mock_clone_request, 0);
+ expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx,
+ -EINVAL);
+
+ C_SaferCond ctx;
+ MockThreads mock_threads(m_threads);
+ MockCreateImageRequest *request = create_request(&mock_threads, "global uuid",
+ "remote uuid", "image name",
+ "101241a7c4c9",
+ mock_remote_clone_image_ctx,
+ &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/test_mock_GetMirrorImageIdRequest.cc b/src/test/rbd_mirror/image_replayer/test_mock_GetMirrorImageIdRequest.cc
new file mode 100644
index 000000000..4a238d282
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/test_mock_GetMirrorImageIdRequest.cc
@@ -0,0 +1,107 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "tools/rbd_mirror/image_replayer/GetMirrorImageIdRequest.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/image_replayer/GetMirrorImageIdRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class TestMockImageReplayerGetMirrorImageIdRequest : public TestMockFixture {
+public:
+ typedef GetMirrorImageIdRequest<librbd::MockTestImageCtx> MockGetMirrorImageIdRequest;
+
+ void expect_mirror_image_get_image_id(librados::IoCtx &io_ctx,
+ const std::string &image_id, int r) {
+ bufferlist bl;
+ encode(image_id, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(io_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"),
+ StrEq("mirror_image_get_image_id"), _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+ *out_bl = bl;
+ })),
+ Return(r)));
+ }
+
+};
+
+TEST_F(TestMockImageReplayerGetMirrorImageIdRequest, Success) {
+ InSequence seq;
+ expect_mirror_image_get_image_id(m_local_io_ctx, "image id", 0);
+
+ std::string image_id;
+ C_SaferCond ctx;
+ auto req = MockGetMirrorImageIdRequest::create(m_local_io_ctx,
+ "global image id",
+ &image_id, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(std::string("image id"), image_id);
+}
+
+TEST_F(TestMockImageReplayerGetMirrorImageIdRequest, MirrorImageIdDNE) {
+ InSequence seq;
+ expect_mirror_image_get_image_id(m_local_io_ctx, "", -ENOENT);
+
+ std::string image_id;
+ C_SaferCond ctx;
+ auto req = MockGetMirrorImageIdRequest::create(m_local_io_ctx,
+ "global image id",
+ &image_id, &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerGetMirrorImageIdRequest, MirrorImageIdError) {
+ InSequence seq;
+ expect_mirror_image_get_image_id(m_local_io_ctx, "", -EINVAL);
+
+ std::string image_id;
+ C_SaferCond ctx;
+ auto req = MockGetMirrorImageIdRequest::create(m_local_io_ctx,
+ "global image id",
+ &image_id, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/test_mock_PrepareLocalImageRequest.cc b/src/test/rbd_mirror/image_replayer/test_mock_PrepareLocalImageRequest.cc
new file mode 100644
index 000000000..54c3b24ef
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/test_mock_PrepareLocalImageRequest.cc
@@ -0,0 +1,505 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "librbd/mirror/GetInfoRequest.h"
+#include "tools/rbd_mirror/ImageDeleter.h"
+#include "tools/rbd_mirror/image_replayer/GetMirrorImageIdRequest.h"
+#include "tools/rbd_mirror/image_replayer/PrepareLocalImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/StateBuilder.h"
+#include "tools/rbd_mirror/image_replayer/journal/StateBuilder.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace mirror {
+
+template<>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+ static GetInfoRequest* s_instance;
+ cls::rbd::MirrorImage *mirror_image;
+ PromotionState *promotion_state;
+ std::string *primary_mirror_uuid;
+ Context *on_finish = nullptr;
+
+ static GetInfoRequest* create(librados::IoCtx& io_ctx,
+ librbd::asio::ContextWQ* context_wq,
+ const std::string& image_id,
+ cls::rbd::MirrorImage *mirror_image,
+ PromotionState *promotion_state,
+ std::string* primary_mirror_uuid,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->mirror_image = mirror_image;
+ s_instance->promotion_state = promotion_state;
+ s_instance->primary_mirror_uuid = primary_mirror_uuid;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetInfoRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~GetInfoRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+GetInfoRequest<librbd::MockTestImageCtx>* GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct ImageDeleter<librbd::MockTestImageCtx> {
+ static ImageDeleter* s_instance;
+
+ static void trash_move(librados::IoCtx& local_io_ctx,
+ const std::string& global_image_id, bool resync,
+ librbd::asio::ContextWQ* work_queue,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->trash_move(global_image_id, resync, on_finish);
+ }
+
+ MOCK_METHOD3(trash_move, void(const std::string&, bool, Context*));
+
+ ImageDeleter() {
+ s_instance = this;
+ }
+};
+
+ImageDeleter<librbd::MockTestImageCtx>* ImageDeleter<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace image_replayer {
+
+template <>
+struct GetMirrorImageIdRequest<librbd::MockTestImageCtx> {
+ static GetMirrorImageIdRequest* s_instance;
+ std::string* image_id = nullptr;
+ Context* on_finish = nullptr;
+
+ static GetMirrorImageIdRequest* create(librados::IoCtx& io_ctx,
+ const std::string& global_image_id,
+ std::string* image_id,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_id = image_id;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetMirrorImageIdRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ virtual ~StateBuilder() {}
+
+ std::string local_image_id;
+ librbd::mirror::PromotionState local_promotion_state;
+};
+
+GetMirrorImageIdRequest<librbd::MockTestImageCtx>* GetMirrorImageIdRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace journal {
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx>
+ : public image_replayer::StateBuilder<librbd::MockTestImageCtx> {
+ static StateBuilder* s_instance;
+
+ cls::rbd::MirrorImageMode mirror_image_mode =
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL;
+
+ std::string local_primary_mirror_uuid;
+
+ static StateBuilder* create(const std::string&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ StateBuilder() {
+ s_instance = this;
+ }
+};
+
+StateBuilder<librbd::MockTestImageCtx>* StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+
+namespace snapshot {
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx>
+ : public image_replayer::StateBuilder<librbd::MockTestImageCtx> {
+ static StateBuilder* s_instance;
+
+ cls::rbd::MirrorImageMode mirror_image_mode =
+ cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT;
+
+ static StateBuilder* create(const std::string&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ StateBuilder() {
+ s_instance = this;
+ }
+};
+
+StateBuilder<librbd::MockTestImageCtx>* StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/image_replayer/PrepareLocalImageRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class TestMockImageReplayerPrepareLocalImageRequest : public TestMockFixture {
+public:
+ typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter;
+ typedef PrepareLocalImageRequest<librbd::MockTestImageCtx> MockPrepareLocalImageRequest;
+ typedef GetMirrorImageIdRequest<librbd::MockTestImageCtx> MockGetMirrorImageIdRequest;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+ typedef journal::StateBuilder<librbd::MockTestImageCtx> MockJournalStateBuilder;
+ typedef snapshot::StateBuilder<librbd::MockTestImageCtx> MockSnapshotStateBuilder;
+ typedef librbd::mirror::GetInfoRequest<librbd::MockTestImageCtx> MockGetMirrorInfoRequest;
+
+ void expect_get_mirror_image_id(MockGetMirrorImageIdRequest& mock_get_mirror_image_id_request,
+ const std::string& image_id, int r) {
+ EXPECT_CALL(mock_get_mirror_image_id_request, send())
+ .WillOnce(Invoke([&mock_get_mirror_image_id_request, image_id, r]() {
+ *mock_get_mirror_image_id_request.image_id = image_id;
+ mock_get_mirror_image_id_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_dir_get_name(librados::IoCtx &io_ctx,
+ const std::string &image_name, int r) {
+ bufferlist bl;
+ encode(image_name, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(io_ctx),
+ exec(RBD_DIRECTORY, _, StrEq("rbd"), StrEq("dir_get_name"), _,
+ _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+ *out_bl = bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_get_mirror_info(
+ MockGetMirrorInfoRequest &mock_get_mirror_info_request,
+ const cls::rbd::MirrorImage &mirror_image,
+ librbd::mirror::PromotionState promotion_state,
+ const std::string& primary_mirror_uuid, int r) {
+ EXPECT_CALL(mock_get_mirror_info_request, send())
+ .WillOnce(Invoke([this, &mock_get_mirror_info_request, mirror_image,
+ promotion_state, primary_mirror_uuid, r]() {
+ *mock_get_mirror_info_request.mirror_image = mirror_image;
+ *mock_get_mirror_info_request.promotion_state = promotion_state;
+ *mock_get_mirror_info_request.primary_mirror_uuid =
+ primary_mirror_uuid;
+ m_threads->work_queue->queue(
+ mock_get_mirror_info_request.on_finish, r);
+ }));
+ }
+
+ void expect_trash_move(MockImageDeleter& mock_image_deleter,
+ const std::string& global_image_id,
+ bool ignore_orphan, int r) {
+ EXPECT_CALL(mock_image_deleter,
+ trash_move(global_image_id, ignore_orphan, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+};
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, SuccessJournal) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "local image id",
+ 0);
+ expect_dir_get_name(m_local_io_ctx, "local image name", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(mock_state_builder != nullptr);
+ ASSERT_EQ(std::string("local image name"), local_image_name);
+ ASSERT_EQ(std::string("local image id"),
+ mock_journal_state_builder.local_image_id);
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ mock_journal_state_builder.mirror_image_mode);
+ ASSERT_EQ(librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ mock_journal_state_builder.local_promotion_state);
+ ASSERT_EQ(std::string("remote mirror uuid"),
+ mock_journal_state_builder.local_primary_mirror_uuid);
+}
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, SuccessSnapshot) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "local image id",
+ 0);
+ expect_dir_get_name(m_local_io_ctx, "local image name", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockSnapshotStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(mock_state_builder != nullptr);
+ ASSERT_EQ(std::string("local image name"), local_image_name);
+ ASSERT_EQ(std::string("local image id"),
+ mock_journal_state_builder.local_image_id);
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ mock_journal_state_builder.mirror_image_mode);
+ ASSERT_EQ(librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ mock_journal_state_builder.local_promotion_state);
+}
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, MirrorImageIdError) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "", -EINVAL);
+
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, DirGetNameDNE) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "local image id",
+ 0);
+ expect_dir_get_name(m_local_io_ctx, "", -ENOENT);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, DirGetNameError) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "local image id",
+ 0);
+ expect_dir_get_name(m_local_io_ctx, "", -EPERM);
+
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, MirrorImageInfoError) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "local image id",
+ 0);
+ expect_dir_get_name(m_local_io_ctx, "local image name", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", -EINVAL);
+
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, ImageCreating) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "local image id",
+ 0);
+ expect_dir_get_name(m_local_io_ctx, "local image name", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_CREATING},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockImageDeleter mock_image_deleter;
+ expect_trash_move(mock_image_deleter, "global image id", false, 0);
+
+ MockSnapshotStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_TRUE(mock_state_builder == nullptr);
+}
+
+TEST_F(TestMockImageReplayerPrepareLocalImageRequest, ImageDisabling) {
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "local image id",
+ 0);
+ expect_dir_get_name(m_local_io_ctx, "local image name", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockSnapshotStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ std::string local_image_name;
+ C_SaferCond ctx;
+ auto req = MockPrepareLocalImageRequest::create(m_local_io_ctx,
+ "global image id",
+ &local_image_name,
+ &mock_state_builder,
+ m_threads->work_queue,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ERESTART, ctx.wait());
+ ASSERT_TRUE(mock_state_builder == nullptr);
+}
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_replayer/test_mock_PrepareRemoteImageRequest.cc b/src/test/rbd_mirror/image_replayer/test_mock_PrepareRemoteImageRequest.cc
new file mode 100644
index 000000000..e5b473c0f
--- /dev/null
+++ b/src/test/rbd_mirror/image_replayer/test_mock_PrepareRemoteImageRequest.cc
@@ -0,0 +1,811 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "librbd/mirror/GetInfoRequest.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/GetMirrorImageIdRequest.h"
+#include "tools/rbd_mirror/image_replayer/PrepareRemoteImageRequest.h"
+#include "tools/rbd_mirror/image_replayer/StateBuilder.h"
+#include "tools/rbd_mirror/image_replayer/journal/StateBuilder.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
+#include "test/journal/mock/MockJournaler.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace journal {
+
+template <>
+struct TypeTraits<MockTestImageCtx> {
+ typedef ::journal::MockJournalerProxy Journaler;
+};
+
+} // namespace journal
+
+namespace mirror {
+
+template<>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+ static GetInfoRequest* s_instance;
+ cls::rbd::MirrorImage *mirror_image;
+ PromotionState *promotion_state;
+ std::string *primary_mirror_uuid;
+ Context *on_finish = nullptr;
+
+ static GetInfoRequest* create(librados::IoCtx& io_ctx,
+ librbd::asio::ContextWQ* context_wq,
+ const std::string& image_id,
+ cls::rbd::MirrorImage *mirror_image,
+ PromotionState *promotion_state,
+ std::string* primary_mirror_uuid,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->mirror_image = mirror_image;
+ s_instance->promotion_state = promotion_state;
+ s_instance->primary_mirror_uuid = primary_mirror_uuid;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetInfoRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~GetInfoRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+GetInfoRequest<librbd::MockTestImageCtx>* GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue) {
+ }
+};
+
+namespace image_replayer {
+
+template <>
+struct GetMirrorImageIdRequest<librbd::MockTestImageCtx> {
+ static GetMirrorImageIdRequest* s_instance;
+ std::string* image_id = nullptr;
+ Context* on_finish = nullptr;
+
+ static GetMirrorImageIdRequest* create(librados::IoCtx& io_ctx,
+ const std::string& global_image_id,
+ std::string* image_id,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_id = image_id;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetMirrorImageIdRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ std::string local_image_id;
+ librbd::mirror::PromotionState local_promotion_state =
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY;
+ std::string remote_image_id;
+ std::string remote_mirror_uuid;
+ librbd::mirror::PromotionState remote_promotion_state;
+
+ virtual ~StateBuilder() {}
+
+ MOCK_CONST_METHOD0(get_mirror_image_mode, cls::rbd::MirrorImageMode());
+};
+
+GetMirrorImageIdRequest<librbd::MockTestImageCtx>* GetMirrorImageIdRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+namespace journal {
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx>
+ : public image_replayer::StateBuilder<librbd::MockTestImageCtx> {
+ static StateBuilder* s_instance;
+
+ cls::rbd::MirrorImageMode mirror_image_mode =
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL;
+
+ ::journal::MockJournalerProxy* remote_journaler = nullptr;
+ cls::journal::ClientState remote_client_state;
+ librbd::journal::MirrorPeerClientMeta remote_client_meta;
+
+ static StateBuilder* create(const std::string&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ StateBuilder() {
+ s_instance = this;
+ }
+};
+
+StateBuilder<librbd::MockTestImageCtx>* StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace journal
+
+namespace snapshot {
+
+template<>
+struct StateBuilder<librbd::MockTestImageCtx>
+ : public image_replayer::StateBuilder<librbd::MockTestImageCtx> {
+ static StateBuilder* s_instance;
+
+ cls::rbd::MirrorImageMode mirror_image_mode =
+ cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT;
+
+ std::string remote_mirror_peer_uuid;
+
+ static StateBuilder* create(const std::string&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ StateBuilder() {
+ s_instance = this;
+ }
+};
+
+StateBuilder<librbd::MockTestImageCtx>* StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/image_replayer/PrepareRemoteImageRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_replayer {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageReplayerPrepareRemoteImageRequest : public TestMockFixture {
+public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef PrepareRemoteImageRequest<librbd::MockTestImageCtx> MockPrepareRemoteImageRequest;
+ typedef GetMirrorImageIdRequest<librbd::MockTestImageCtx> MockGetMirrorImageIdRequest;
+ typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+ typedef journal::StateBuilder<librbd::MockTestImageCtx> MockJournalStateBuilder;
+ typedef snapshot::StateBuilder<librbd::MockTestImageCtx> MockSnapshotStateBuilder;
+ typedef librbd::mirror::GetInfoRequest<librbd::MockTestImageCtx> MockGetMirrorInfoRequest;
+
+ void expect_get_mirror_image_mode(MockStateBuilder& mock_state_builder,
+ cls::rbd::MirrorImageMode mirror_image_mode) {
+ EXPECT_CALL(mock_state_builder, get_mirror_image_mode())
+ .WillOnce(Return(mirror_image_mode));
+ }
+
+ void expect_get_mirror_image_id(MockGetMirrorImageIdRequest& mock_get_mirror_image_id_request,
+ const std::string& image_id, int r) {
+ EXPECT_CALL(mock_get_mirror_image_id_request, send())
+ .WillOnce(Invoke([&mock_get_mirror_image_id_request, image_id, r]() {
+ *mock_get_mirror_image_id_request.image_id = image_id;
+ mock_get_mirror_image_id_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_get_mirror_info(
+ MockGetMirrorInfoRequest &mock_get_mirror_info_request,
+ const cls::rbd::MirrorImage &mirror_image,
+ librbd::mirror::PromotionState promotion_state,
+ const std::string& primary_mirror_uuid, int r) {
+ EXPECT_CALL(mock_get_mirror_info_request, send())
+ .WillOnce(Invoke([this, &mock_get_mirror_info_request, mirror_image,
+ promotion_state, primary_mirror_uuid, r]() {
+ *mock_get_mirror_info_request.mirror_image = mirror_image;
+ *mock_get_mirror_info_request.promotion_state = promotion_state;
+ *mock_get_mirror_info_request.primary_mirror_uuid =
+ primary_mirror_uuid;
+ m_threads->work_queue->queue(
+ mock_get_mirror_info_request.on_finish, r);
+ }));
+ }
+
+ void expect_journaler_get_client(::journal::MockJournaler &mock_journaler,
+ const std::string &client_id,
+ cls::journal::Client &client, int r) {
+ EXPECT_CALL(mock_journaler, get_client(StrEq(client_id), _, _))
+ .WillOnce(DoAll(WithArg<1>(Invoke([client](cls::journal::Client *out_client) {
+ *out_client = client;
+ })),
+ WithArg<2>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ }))));
+ }
+
+ void expect_journaler_register_client(::journal::MockJournaler &mock_journaler,
+ const librbd::journal::ClientData &client_data,
+ int r) {
+ bufferlist bl;
+ encode(client_data, bl);
+
+ EXPECT_CALL(mock_journaler, register_client(ContentsEqual(bl), _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ })));
+ }
+};
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, SuccessJournal) {
+ ::journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ EXPECT_CALL(mock_remote_journaler, construct());
+
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.image_id = "local image id";
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ librbd::journal::ClientData client_data{mirror_peer_client_meta};
+ cls::journal::Client client;
+ client.state = cls::journal::CLIENT_STATE_DISCONNECTED;
+ encode(client_data, client.data);
+ expect_journaler_get_client(mock_remote_journaler, "local mirror uuid",
+ client, 0);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(mock_state_builder != nullptr);
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ mock_journal_state_builder.mirror_image_mode);
+ ASSERT_EQ(std::string("remote mirror uuid"),
+ mock_journal_state_builder.remote_mirror_uuid);
+ ASSERT_EQ(std::string("remote image id"),
+ mock_journal_state_builder.remote_image_id);
+ ASSERT_EQ(librbd::mirror::PROMOTION_STATE_PRIMARY,
+ mock_journal_state_builder.remote_promotion_state);
+ ASSERT_TRUE(mock_journal_state_builder.remote_journaler != nullptr);
+ ASSERT_EQ(cls::journal::CLIENT_STATE_DISCONNECTED,
+ mock_journal_state_builder.remote_client_state);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, SuccessSnapshot) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockSnapshotStateBuilder mock_snapshot_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid",
+ "remote mirror peer uuid"},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(mock_state_builder != nullptr);
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ mock_snapshot_state_builder.mirror_image_mode);
+ ASSERT_EQ(std::string("remote mirror uuid"),
+ mock_snapshot_state_builder.remote_mirror_uuid);
+ ASSERT_EQ(std::string("remote mirror peer uuid"),
+ mock_snapshot_state_builder.remote_mirror_peer_uuid);
+ ASSERT_EQ(std::string("remote image id"),
+ mock_snapshot_state_builder.remote_image_id);
+ ASSERT_EQ(librbd::mirror::PROMOTION_STATE_PRIMARY,
+ mock_snapshot_state_builder.remote_promotion_state);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, SuccessNotRegistered) {
+ ::journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ expect_get_mirror_image_mode(mock_journal_state_builder,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL);
+
+ EXPECT_CALL(mock_remote_journaler, construct());
+
+ cls::journal::Client client;
+ expect_journaler_get_client(mock_remote_journaler, "local mirror uuid",
+ client, -ENOENT);
+
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.image_id = "local image id";
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ librbd::journal::ClientData client_data{mirror_peer_client_meta};
+ expect_journaler_register_client(mock_remote_journaler, client_data, 0);
+
+ mock_journal_state_builder.local_image_id = "local image id";
+ MockStateBuilder* mock_state_builder = &mock_journal_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(mock_state_builder != nullptr);
+ ASSERT_EQ(std::string("remote image id"),
+ mock_journal_state_builder.remote_image_id);
+ ASSERT_EQ(librbd::mirror::PROMOTION_STATE_PRIMARY,
+ mock_journal_state_builder.remote_promotion_state);
+ ASSERT_TRUE(mock_journal_state_builder.remote_journaler != nullptr);
+ ASSERT_EQ(cls::journal::CLIENT_STATE_CONNECTED,
+ mock_journal_state_builder.remote_client_state);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, GetMirrorImageIdError) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "", -EINVAL);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = &mock_journal_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+ ASSERT_TRUE(mock_journal_state_builder.remote_journaler == nullptr);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, GetMirrorInfoError) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", -EINVAL);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+ ASSERT_TRUE(mock_state_builder == nullptr);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, GetClientError) {
+ ::journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ EXPECT_CALL(mock_remote_journaler, construct());
+
+ cls::journal::Client client;
+ expect_journaler_get_client(mock_remote_journaler, "local mirror uuid",
+ client, -EINVAL);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = nullptr;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+ ASSERT_TRUE(mock_state_builder == nullptr);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, RegisterClientError) {
+ ::journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ expect_get_mirror_image_mode(mock_journal_state_builder,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL);
+
+ EXPECT_CALL(mock_remote_journaler, construct());
+
+ cls::journal::Client client;
+ expect_journaler_get_client(mock_remote_journaler, "local mirror uuid",
+ client, -ENOENT);
+
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ mirror_peer_client_meta.image_id = "local image id";
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ librbd::journal::ClientData client_data{mirror_peer_client_meta};
+ expect_journaler_register_client(mock_remote_journaler, client_data, -EINVAL);
+
+ mock_journal_state_builder.local_image_id = "local image id";
+ MockStateBuilder* mock_state_builder = &mock_journal_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, MirrorImageIdDNEJournal) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "", -ENOENT);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = &mock_journal_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ mock_journal_state_builder.mirror_image_mode);
+ ASSERT_EQ("remote mirror uuid",
+ mock_journal_state_builder.remote_mirror_uuid);
+ ASSERT_EQ("", mock_journal_state_builder.remote_image_id);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, MirrorImageIdDNESnapshot) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request, "", -ENOENT);
+
+ MockSnapshotStateBuilder mock_snapshot_state_builder;
+ MockStateBuilder* mock_state_builder = &mock_snapshot_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid",
+ "remote mirror peer uuid"},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ mock_snapshot_state_builder.mirror_image_mode);
+ ASSERT_EQ("remote mirror uuid",
+ mock_snapshot_state_builder.remote_mirror_uuid);
+ ASSERT_EQ("remote mirror peer uuid",
+ mock_snapshot_state_builder.remote_mirror_peer_uuid);
+ ASSERT_EQ("", mock_snapshot_state_builder.remote_image_id);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, MirrorInfoDNEJournal) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", -ENOENT);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ MockStateBuilder* mock_state_builder = &mock_journal_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ mock_journal_state_builder.mirror_image_mode);
+ ASSERT_EQ("remote mirror uuid",
+ mock_journal_state_builder.remote_mirror_uuid);
+ ASSERT_EQ("", mock_journal_state_builder.remote_image_id);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, MirrorInfoDNESnapshot) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", -ENOENT);
+
+ MockSnapshotStateBuilder mock_snapshot_state_builder;
+ MockStateBuilder* mock_state_builder = &mock_snapshot_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid",
+ "remote mirror peer uuid"},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ mock_snapshot_state_builder.mirror_image_mode);
+ ASSERT_EQ("remote mirror uuid",
+ mock_snapshot_state_builder.remote_mirror_uuid);
+ ASSERT_EQ("remote mirror peer uuid",
+ mock_snapshot_state_builder.remote_mirror_peer_uuid);
+ ASSERT_EQ("", mock_snapshot_state_builder.remote_image_id);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, MirrorInfoDisablingJournal) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockJournalStateBuilder mock_journal_state_builder;
+ expect_get_mirror_image_mode(mock_journal_state_builder,
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL);
+ MockStateBuilder* mock_state_builder = &mock_journal_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid", ""},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ mock_journal_state_builder.mirror_image_mode);
+ ASSERT_EQ("remote mirror uuid",
+ mock_journal_state_builder.remote_mirror_uuid);
+ ASSERT_EQ("", mock_journal_state_builder.remote_image_id);
+}
+
+TEST_F(TestMockImageReplayerPrepareRemoteImageRequest, MirrorInfoDisablingSnapshot) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockGetMirrorImageIdRequest mock_get_mirror_image_id_request;
+ expect_get_mirror_image_id(mock_get_mirror_image_id_request,
+ "remote image id", 0);
+
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ "global image id",
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING},
+ librbd::mirror::PROMOTION_STATE_PRIMARY,
+ "remote mirror uuid", 0);
+
+ MockSnapshotStateBuilder mock_snapshot_state_builder;
+ expect_get_mirror_image_mode(mock_snapshot_state_builder,
+ cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT);
+ MockStateBuilder* mock_state_builder = &mock_snapshot_state_builder;
+ C_SaferCond ctx;
+ auto req = MockPrepareRemoteImageRequest::create(&mock_threads,
+ m_local_io_ctx,
+ m_remote_io_ctx,
+ "global image id",
+ "local mirror uuid",
+ {"remote mirror uuid",
+ "remote mirror peer uuid"},
+ nullptr,
+ &mock_state_builder,
+ &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOENT, ctx.wait());
+ ASSERT_EQ(cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT,
+ mock_snapshot_state_builder.mirror_image_mode);
+ ASSERT_EQ("remote mirror uuid",
+ mock_snapshot_state_builder.remote_mirror_uuid);
+ ASSERT_EQ("remote mirror peer uuid",
+ mock_snapshot_state_builder.remote_mirror_peer_uuid);
+ ASSERT_EQ("", mock_snapshot_state_builder.remote_image_id);
+}
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_sync/test_mock_SyncPointCreateRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_SyncPointCreateRequest.cc
new file mode 100644
index 000000000..9a4d920f8
--- /dev/null
+++ b/src/test/rbd_mirror/image_sync/test_mock_SyncPointCreateRequest.cc
@@ -0,0 +1,195 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h"
+#include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.cc"
+
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockImageSyncSyncPointCreateRequest : public TestMockFixture {
+public:
+ typedef SyncPointCreateRequest<librbd::MockTestImageCtx> MockSyncPointCreateRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+ }
+
+ void expect_get_snap_seqs(MockSyncPointHandler& mock_sync_point_handler) {
+ EXPECT_CALL(mock_sync_point_handler, get_snap_seqs())
+ .WillRepeatedly(Return(librbd::SnapSeqs{}));
+ }
+
+ void expect_get_sync_points(MockSyncPointHandler& mock_sync_point_handler) {
+ EXPECT_CALL(mock_sync_point_handler, get_sync_points())
+ .WillRepeatedly(Invoke([this]() {
+ return m_sync_points;
+ }));
+ }
+
+ void expect_update_sync_points(MockSyncPointHandler& mock_sync_point_handler,
+ int r) {
+ EXPECT_CALL(mock_sync_point_handler, update_sync_points(_, _, false, _))
+ .WillOnce(DoAll(WithArg<1>(Invoke([this, r](const SyncPoints& sync_points) {
+ if (r >= 0) {
+ m_sync_points = sync_points;
+ }
+ })),
+ WithArg<3>(CompleteContext(r))));
+ }
+
+ void expect_image_refresh(librbd::MockTestImageCtx &mock_remote_image_ctx, int r) {
+ EXPECT_CALL(*mock_remote_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_snap_create(librbd::MockTestImageCtx &mock_remote_image_ctx, int r) {
+ EXPECT_CALL(*mock_remote_image_ctx.operations, snap_create(_, _, _, _, _))
+ .WillOnce(WithArg<4>(CompleteContext(r)));
+ }
+
+ MockSyncPointCreateRequest *create_request(librbd::MockTestImageCtx &mock_remote_image_ctx,
+ MockSyncPointHandler& mock_sync_point_handler,
+ Context *ctx) {
+ return new MockSyncPointCreateRequest(&mock_remote_image_ctx, "uuid",
+ &mock_sync_point_handler, ctx);
+ }
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ SyncPoints m_sync_points;
+};
+
+TEST_F(TestMockImageSyncSyncPointCreateRequest, Success) {
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_update_sync_points(mock_sync_point_handler, 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_snap_create(mock_remote_image_ctx, 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointCreateRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(1U, m_sync_points.size());
+}
+
+TEST_F(TestMockImageSyncSyncPointCreateRequest, ResyncSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "start snap",
+ "", boost::none);
+ auto sync_point = m_sync_points.front();
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_update_sync_points(mock_sync_point_handler, 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_snap_create(mock_remote_image_ctx, 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointCreateRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(2U, m_sync_points.size());
+ ASSERT_EQ(sync_point, m_sync_points.front());
+ ASSERT_EQ("start snap", m_sync_points.back().from_snap_name);
+}
+
+TEST_F(TestMockImageSyncSyncPointCreateRequest, SnapshotExists) {
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_update_sync_points(mock_sync_point_handler, 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_snap_create(mock_remote_image_ctx, -EEXIST);
+ expect_update_sync_points(mock_sync_point_handler, 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_snap_create(mock_remote_image_ctx, 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointCreateRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(1U, m_sync_points.size());
+}
+
+TEST_F(TestMockImageSyncSyncPointCreateRequest, ClientUpdateError) {
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_update_sync_points(mock_sync_point_handler, -EINVAL);
+
+ C_SaferCond ctx;
+ MockSyncPointCreateRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+
+ ASSERT_TRUE(m_sync_points.empty());
+}
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/image_sync/test_mock_SyncPointPruneRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_SyncPointPruneRequest.cc
new file mode 100644
index 000000000..bd13f3cd0
--- /dev/null
+++ b/src/test/rbd_mirror/image_sync/test_mock_SyncPointPruneRequest.cc
@@ -0,0 +1,347 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h"
+#include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.cc"
+template class rbd::mirror::image_sync::SyncPointPruneRequest<librbd::MockTestImageCtx>;
+
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageSyncSyncPointPruneRequest : public TestMockFixture {
+public:
+ typedef SyncPointPruneRequest<librbd::MockTestImageCtx> MockSyncPointPruneRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+ }
+
+ void expect_get_snap_seqs(MockSyncPointHandler& mock_sync_point_handler) {
+ EXPECT_CALL(mock_sync_point_handler, get_snap_seqs())
+ .WillRepeatedly(Return(librbd::SnapSeqs{}));
+ }
+
+ void expect_get_sync_points(MockSyncPointHandler& mock_sync_point_handler) {
+ EXPECT_CALL(mock_sync_point_handler, get_sync_points())
+ .WillRepeatedly(Invoke([this]() {
+ return m_sync_points;
+ }));
+ }
+
+ void expect_update_sync_points(MockSyncPointHandler& mock_sync_point_handler,
+ bool complete, int r) {
+ EXPECT_CALL(mock_sync_point_handler, update_sync_points(_, _, complete, _))
+ .WillOnce(DoAll(WithArg<1>(Invoke([this, r](const SyncPoints& sync_points) {
+ if (r >= 0) {
+ m_sync_points = sync_points;
+ }
+ })),
+ WithArg<3>(CompleteContext(r))));
+ }
+
+ void expect_get_snap_id(librbd::MockTestImageCtx &mock_remote_image_ctx,
+ const std::string &snap_name, uint64_t snap_id) {
+ EXPECT_CALL(mock_remote_image_ctx, get_snap_id(_, StrEq(snap_name)))
+ .WillOnce(Return(snap_id));
+ }
+
+ void expect_image_refresh(librbd::MockTestImageCtx &mock_remote_image_ctx, int r) {
+ EXPECT_CALL(*mock_remote_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_snap_remove(librbd::MockTestImageCtx &mock_remote_image_ctx,
+ const std::string &snap_name, int r) {
+ EXPECT_CALL(*mock_remote_image_ctx.operations, snap_remove(_, StrEq(snap_name), _))
+ .WillOnce(WithArg<2>(CompleteContext(r)));
+ }
+
+ MockSyncPointPruneRequest *create_request(librbd::MockTestImageCtx &mock_remote_image_ctx,
+ MockSyncPointHandler& mock_sync_point_handler,
+ bool sync_complete, Context *ctx) {
+ return new MockSyncPointPruneRequest(&mock_remote_image_ctx, sync_complete,
+ &mock_sync_point_handler, ctx);
+ }
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ SyncPoints m_sync_points;
+};
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, SyncInProgressSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap1",
+ "", boost::none);
+ auto sync_points = m_sync_points;
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_get_snap_id(mock_remote_image_ctx, "snap1", 123);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, false, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_EQ(sync_points, m_sync_points);
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, RestartedSyncInProgressSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap2",
+ "snap1", boost::none);
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap1", "",
+ boost::none);
+ auto sync_points = m_sync_points;
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_get_snap_id(mock_remote_image_ctx, "snap1", 123);
+ expect_snap_remove(mock_remote_image_ctx, "snap2", 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, false, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ sync_points.pop_back();
+ ASSERT_EQ(sync_points, m_sync_points);
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, SyncInProgressMissingSnapSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap2",
+ "snap1", boost::none);
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap1", "",
+ boost::none);
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_get_snap_id(mock_remote_image_ctx, "snap1", CEPH_NOSNAP);
+ expect_snap_remove(mock_remote_image_ctx, "snap2", 0);
+ expect_snap_remove(mock_remote_image_ctx, "snap1", 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, false, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(SyncPoints{}, m_sync_points);
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, SyncInProgressUnexpectedFromSnapSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap2",
+ "snap1", boost::none);
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_get_snap_id(mock_remote_image_ctx, "snap2", 124);
+ expect_snap_remove(mock_remote_image_ctx, "snap2", 0);
+ expect_snap_remove(mock_remote_image_ctx, "snap1", 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, false, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ false, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(SyncPoints(), m_sync_points);
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, SyncCompleteSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap1",
+ "", boost::none);
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_snap_remove(mock_remote_image_ctx, "snap1", 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, true, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(m_sync_points.empty());
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, RestartedSyncCompleteSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap2",
+ "snap1", boost::none);
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap1",
+ "", boost::none);
+ auto sync_points = m_sync_points;
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, true, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ sync_points.pop_front();
+ ASSERT_EQ(sync_points, m_sync_points);
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, RestartedCatchUpSyncCompleteSuccess) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap3",
+ "snap2", boost::none);
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap2",
+ "snap1", boost::none);
+ auto sync_points = m_sync_points;
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_snap_remove(mock_remote_image_ctx, "snap1", 0);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, true, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ sync_points.pop_front();
+ ASSERT_EQ(sync_points, m_sync_points);
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, SnapshotDNE) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap1",
+ "", boost::none);
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_snap_remove(mock_remote_image_ctx, "snap1", -ENOENT);
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, true, 0);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(m_sync_points.empty());
+}
+
+TEST_F(TestMockImageSyncSyncPointPruneRequest, ClientUpdateError) {
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap2",
+ "snap1", boost::none);
+ m_sync_points.emplace_front(cls::rbd::UserSnapshotNamespace(), "snap1",
+ "", boost::none);
+ auto sync_points = m_sync_points;
+
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_image_refresh(mock_remote_image_ctx, 0);
+ expect_update_sync_points(mock_sync_point_handler, true, -EINVAL);
+
+ C_SaferCond ctx;
+ MockSyncPointPruneRequest *req = create_request(mock_remote_image_ctx,
+ mock_sync_point_handler,
+ true, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+
+ ASSERT_EQ(sync_points, m_sync_points);
+}
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/mock/MockBaseRequest.h b/src/test/rbd_mirror/mock/MockBaseRequest.h
new file mode 100644
index 000000000..c85eab434
--- /dev/null
+++ b/src/test/rbd_mirror/mock/MockBaseRequest.h
@@ -0,0 +1,26 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_MOCK_BASE_REQUEST_H
+#define CEPH_MOCK_BASE_REQUEST_H
+
+#include "tools/rbd_mirror/BaseRequest.h"
+#include <gmock/gmock.h>
+
+struct Context;
+
+namespace rbd {
+namespace mirror {
+
+struct MockBaseRequest : public BaseRequest {
+ MockBaseRequest() : BaseRequest(nullptr) {}
+
+ Context* on_finish = nullptr;
+
+ MOCK_METHOD0(send, void());
+};
+
+} // namespace mirror
+} // namepace rbd
+
+#endif // CEPH_MOCK_BASE_REQUEST_H
diff --git a/src/test/rbd_mirror/mock/MockContextWQ.h b/src/test/rbd_mirror/mock/MockContextWQ.h
new file mode 100644
index 000000000..1c0ee88f5
--- /dev/null
+++ b/src/test/rbd_mirror/mock/MockContextWQ.h
@@ -0,0 +1,18 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_MOCK_CONTEXT_WQ_H
+#define CEPH_MOCK_CONTEXT_WQ_H
+
+#include <gmock/gmock.h>
+
+struct Context;
+
+struct MockContextWQ {
+ void queue(Context *ctx) {
+ queue(ctx, 0);
+ }
+ MOCK_METHOD2(queue, void(Context *, int));
+};
+
+#endif // CEPH_MOCK_CONTEXT_WQ_H
diff --git a/src/test/rbd_mirror/mock/MockSafeTimer.h b/src/test/rbd_mirror/mock/MockSafeTimer.h
new file mode 100644
index 000000000..32d58471d
--- /dev/null
+++ b/src/test/rbd_mirror/mock/MockSafeTimer.h
@@ -0,0 +1,16 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_MOCK_SAFE_TIMER_H
+#define CEPH_MOCK_SAFE_TIMER_H
+
+#include <gmock/gmock.h>
+
+struct Context;
+
+struct MockSafeTimer {
+ MOCK_METHOD2(add_event_after, Context*(double, Context*));
+ MOCK_METHOD1(cancel_event, bool(Context *));
+};
+
+#endif // CEPH_MOCK_SAFE_TIMER_H
diff --git a/src/test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h b/src/test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h
new file mode 100644
index 000000000..b6263cbdf
--- /dev/null
+++ b/src/test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h
@@ -0,0 +1,29 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_MOCK_IMAGE_SYNC_SYNC_POINT_HANDLER_H
+#define CEPH_MOCK_IMAGE_SYNC_SYNC_POINT_HANDLER_H
+
+#include "tools/rbd_mirror/image_sync/Types.h"
+#include <gmock/gmock.h>
+
+struct Context;
+
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+struct MockSyncPointHandler : public SyncPointHandler{
+ MOCK_CONST_METHOD0(get_sync_points, SyncPoints());
+ MOCK_CONST_METHOD0(get_snap_seqs, librbd::SnapSeqs());
+
+ MOCK_METHOD4(update_sync_points, void(const librbd::SnapSeqs&,
+ const SyncPoints&,
+ bool, Context*));
+};
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
+
+#endif // CEPH_MOCK_IMAGE_SYNC_SYNC_POINT_HANDLER_H
diff --git a/src/test/rbd_mirror/pool_watcher/test_mock_RefreshImagesRequest.cc b/src/test/rbd_mirror/pool_watcher/test_mock_RefreshImagesRequest.cc
new file mode 100644
index 000000000..3347a6cd4
--- /dev/null
+++ b/src/test/rbd_mirror/pool_watcher/test_mock_RefreshImagesRequest.cc
@@ -0,0 +1,117 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "tools/rbd_mirror/pool_watcher/RefreshImagesRequest.h"
+#include "include/stringify.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/pool_watcher/RefreshImagesRequest.cc"
+template class rbd::mirror::pool_watcher::RefreshImagesRequest<librbd::MockTestImageCtx>;
+
+namespace rbd {
+namespace mirror {
+namespace pool_watcher {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockPoolWatcherRefreshImagesRequest : public TestMockFixture {
+public:
+ typedef RefreshImagesRequest<librbd::MockTestImageCtx> MockRefreshImagesRequest;
+
+ void expect_mirror_image_list(librados::IoCtx &io_ctx,
+ const std::map<std::string, std::string> &ids,
+ int r) {
+ bufferlist bl;
+ encode(ids, bl);
+
+ EXPECT_CALL(get_mock_io_ctx(io_ctx),
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_list"),
+ _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+ *out_bl = bl;
+ })),
+ Return(r)));
+ }
+
+};
+
+TEST_F(TestMockPoolWatcherRefreshImagesRequest, Success) {
+ InSequence seq;
+ expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
+
+ C_SaferCond ctx;
+ ImageIds image_ids;
+ MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
+ m_remote_io_ctx, &image_ids, &ctx);
+
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ImageIds expected_image_ids = {{"global id", "local id"}};
+ ASSERT_EQ(expected_image_ids, image_ids);
+}
+
+TEST_F(TestMockPoolWatcherRefreshImagesRequest, LargeDirectory) {
+ InSequence seq;
+ std::map<std::string, std::string> mirror_list;
+ ImageIds expected_image_ids;
+ for (uint32_t idx = 1; idx <= 1024; ++idx) {
+ mirror_list.insert(std::make_pair("local id " + stringify(idx),
+ "global id " + stringify(idx)));
+ expected_image_ids.insert({{"global id " + stringify(idx),
+ "local id " + stringify(idx)}});
+ }
+
+ expect_mirror_image_list(m_remote_io_ctx, mirror_list, 0);
+ expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
+
+ C_SaferCond ctx;
+ ImageIds image_ids;
+ MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
+ m_remote_io_ctx, &image_ids, &ctx);
+
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ expected_image_ids.insert({"global id", "local id"});
+ ASSERT_EQ(expected_image_ids, image_ids);
+}
+
+TEST_F(TestMockPoolWatcherRefreshImagesRequest, MirrorImageListError) {
+ InSequence seq;
+ expect_mirror_image_list(m_remote_io_ctx, {}, -EINVAL);
+
+ C_SaferCond ctx;
+ ImageIds image_ids;
+ MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
+ m_remote_io_ctx, &image_ids, &ctx);
+
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace pool_watcher
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/random_write.cc b/src/test/rbd_mirror/random_write.cc
new file mode 100644
index 000000000..cf5315048
--- /dev/null
+++ b/src/test/rbd_mirror/random_write.cc
@@ -0,0 +1,210 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/ceph_argparse.h"
+#include "common/config.h"
+#include "common/debug.h"
+#include "common/errno.h"
+#include "common/Cond.h"
+#include "include/rados/librados.hpp"
+#include "include/rbd/librbd.hpp"
+#include "global/global_init.h"
+#include <string>
+#include <vector>
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rbd_mirror
+#undef dout_prefix
+#define dout_prefix *_dout << "random-write: "
+
+namespace {
+
+const uint32_t NUM_THREADS = 8;
+const uint32_t MAX_IO_SIZE = 24576;
+const uint32_t MIN_IO_SIZE = 4;
+
+void usage() {
+ std::cout << "usage: ceph_test_rbd_mirror_random_write [options...] \\" << std::endl;
+ std::cout << " <pool> <image>" << std::endl;
+ std::cout << std::endl;
+ std::cout << " pool image pool" << std::endl;
+ std::cout << " image image to write" << std::endl;
+ std::cout << std::endl;
+ std::cout << "options:\n";
+ std::cout << " -m monaddress[:port] connect to specified monitor\n";
+ std::cout << " --keyring=<path> path to keyring for local cluster\n";
+ std::cout << " --log-file=<logfile> file to log debug output\n";
+ std::cout << " --debug-rbd-mirror=<log-level>/<memory-level> set rbd-mirror debug level\n";
+ generic_server_usage();
+}
+
+void rbd_bencher_completion(void *c, void *pc);
+
+struct rbd_bencher {
+ librbd::Image *image;
+ ceph::mutex lock = ceph::make_mutex("rbd_bencher::lock");
+ ceph::condition_variable cond;
+ int in_flight;
+
+ explicit rbd_bencher(librbd::Image *i)
+ : image(i),
+ in_flight(0) {
+ }
+
+ bool start_write(int max, uint64_t off, uint64_t len, bufferlist& bl,
+ int op_flags) {
+ {
+ std::lock_guard l{lock};
+ if (in_flight >= max)
+ return false;
+ in_flight++;
+ }
+ librbd::RBD::AioCompletion *c =
+ new librbd::RBD::AioCompletion((void *)this, rbd_bencher_completion);
+ image->aio_write2(off, len, bl, c, op_flags);
+ //cout << "start " << c << " at " << off << "~" << len << std::endl;
+ return true;
+ }
+
+ void wait_for(int max) {
+ using namespace std::chrono_literals;
+ std::unique_lock l{lock};
+ while (in_flight > max) {
+ cond.wait_for(l, 200ms);
+ }
+ }
+
+};
+
+void rbd_bencher_completion(void *vc, void *pc) {
+ librbd::RBD::AioCompletion *c = (librbd::RBD::AioCompletion *)vc;
+ rbd_bencher *b = static_cast<rbd_bencher *>(pc);
+ //cout << "complete " << c << std::endl;
+ int ret = c->get_return_value();
+ if (ret != 0) {
+ std::cout << "write error: " << cpp_strerror(ret) << std::endl;
+ exit(ret < 0 ? -ret : ret);
+ }
+ b->lock.lock();
+ b->in_flight--;
+ b->cond.notify_all();
+ b->lock.unlock();
+ c->release();
+}
+
+void write_image(librbd::Image &image) {
+ srand(time(NULL) % (unsigned long) -1);
+
+ uint64_t max_io_bytes = MAX_IO_SIZE * 1024;
+ bufferptr bp(max_io_bytes);
+ memset(bp.c_str(), rand() & 0xff, bp.length());
+ bufferlist bl;
+ bl.push_back(bp);
+
+ uint64_t size = 0;
+ image.size(&size);
+ ceph_assert(size != 0);
+
+ std::vector<uint64_t> thread_offset;
+ uint64_t i;
+ uint64_t start_pos;
+
+ // disturb all thread's offset, used by seq write
+ for (i = 0; i < NUM_THREADS; i++) {
+ start_pos = (rand() % (size / max_io_bytes)) * max_io_bytes;
+ thread_offset.push_back(start_pos);
+ }
+
+ uint64_t total_ios = 0;
+ uint64_t total_bytes = 0;
+ rbd_bencher b(&image);
+ while (true) {
+ b.wait_for(NUM_THREADS - 1);
+ for (uint32_t i = 0; i < NUM_THREADS; ++i) {
+ // mostly small writes with a small chance of large writes
+ uint32_t io_modulo = MIN_IO_SIZE + 1;
+ if (rand() % 30 == 0) {
+ io_modulo += MAX_IO_SIZE;
+ }
+
+ uint32_t io_size = (((rand() % io_modulo) + MIN_IO_SIZE) * 1024);
+ thread_offset[i] = (rand() % (size / io_size)) * io_size;
+ if (!b.start_write(NUM_THREADS, thread_offset[i], io_size, bl,
+ LIBRADOS_OP_FLAG_FADVISE_RANDOM)) {
+ break;
+ }
+ ++i;
+
+ ++total_ios;
+ total_bytes += io_size;
+ if (total_ios % 100 == 0) {
+ std::cout << total_ios << " IOs, " << total_bytes << " bytes"
+ << std::endl;
+ }
+ }
+ }
+ b.wait_for(0);
+}
+
+} // anonymous namespace
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ if (args.empty()) {
+ std::cerr << argv[0] << ": -h or --help for usage" << std::endl;
+ exit(1);
+ }
+ if (ceph_argparse_need_usage(args)) {
+ usage();
+ exit(0);
+ }
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+
+ if (args.size() < 2) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ std::string pool_name = args[0];
+ std::string image_name = args[1];
+
+ common_init_finish(g_ceph_context);
+
+ dout(5) << "connecting to cluster" << dendl;
+ librados::Rados rados;
+ librados::IoCtx io_ctx;
+ librbd::RBD rbd;
+ librbd::Image image;
+ int r = rados.init_with_context(g_ceph_context);
+ if (r < 0) {
+ derr << "could not initialize RADOS handle" << dendl;
+ return EXIT_FAILURE;
+ }
+
+ r = rados.connect();
+ if (r < 0) {
+ derr << "error connecting to local cluster" << dendl;
+ return EXIT_FAILURE;
+ }
+
+ r = rados.ioctx_create(pool_name.c_str(), io_ctx);
+ if (r < 0) {
+ derr << "error finding local pool " << pool_name << ": "
+ << cpp_strerror(r) << dendl;
+ return EXIT_FAILURE;
+ }
+
+ r = rbd.open(io_ctx, image, image_name.c_str());
+ if (r < 0) {
+ derr << "error opening image " << image_name << ": "
+ << cpp_strerror(r) << dendl;
+ return EXIT_FAILURE;
+ }
+
+ write_image(image);
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/rbd_mirror/test_ClusterWatcher.cc b/src/test/rbd_mirror/test_ClusterWatcher.cc
new file mode 100644
index 000000000..005a83c1d
--- /dev/null
+++ b/src/test/rbd_mirror/test_ClusterWatcher.cc
@@ -0,0 +1,265 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "include/rados/librados.hpp"
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "common/ceph_mutex.h"
+#include "librbd/internal.h"
+#include "librbd/api/Mirror.h"
+#include "tools/rbd_mirror/ClusterWatcher.h"
+#include "tools/rbd_mirror/ServiceDaemon.h"
+#include "tools/rbd_mirror/Types.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "test/librados/test_cxx.h"
+#include "test/librbd/test_support.h"
+#include "gtest/gtest.h"
+#include <boost/scope_exit.hpp>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+
+using rbd::mirror::ClusterWatcher;
+using rbd::mirror::PeerSpec;
+using rbd::mirror::RadosRef;
+using std::map;
+using std::set;
+using std::string;
+
+void register_test_cluster_watcher() {
+}
+
+class TestClusterWatcher : public ::rbd::mirror::TestFixture {
+public:
+
+ TestClusterWatcher() {
+ m_cluster = std::make_shared<librados::Rados>();
+ EXPECT_EQ("", connect_cluster_pp(*m_cluster));
+ }
+
+ ~TestClusterWatcher() override {
+ m_cluster->wait_for_latest_osdmap();
+ for (auto& pool : m_pools) {
+ EXPECT_EQ(0, m_cluster->pool_delete(pool.c_str()));
+ }
+ }
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ m_service_daemon.reset(new rbd::mirror::ServiceDaemon<>(g_ceph_context,
+ m_cluster,
+ m_threads));
+ m_cluster_watcher.reset(new ClusterWatcher(m_cluster, m_lock,
+ m_service_daemon.get()));
+ }
+
+ void TearDown() override {
+ m_service_daemon.reset();
+ m_cluster_watcher.reset();
+ TestFixture::TearDown();
+ }
+
+ void create_pool(bool enable_mirroring, const PeerSpec &peer,
+ string *uuid = nullptr, string *name=nullptr) {
+ string pool_name = get_temp_pool_name("test-rbd-mirror-");
+ ASSERT_EQ(0, m_cluster->pool_create(pool_name.c_str()));
+
+ int64_t pool_id = m_cluster->pool_lookup(pool_name.c_str());
+ ASSERT_GE(pool_id, 0);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, m_cluster->ioctx_create2(pool_id, ioctx));
+ ioctx.application_enable("rbd", true);
+
+ m_pools.insert(pool_name);
+ if (enable_mirroring) {
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(ioctx,
+ RBD_MIRROR_MODE_POOL));
+
+ std::string gen_uuid;
+ ASSERT_EQ(0, librbd::api::Mirror<>::peer_site_add(
+ ioctx, uuid != nullptr ? uuid : &gen_uuid,
+ RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ peer.cluster_name, peer.client_name));
+ m_pool_peers[pool_id].insert(peer);
+ }
+ if (name != nullptr) {
+ *name = pool_name;
+ }
+ }
+
+ void delete_pool(const string &name, const PeerSpec &peer) {
+ int64_t pool_id = m_cluster->pool_lookup(name.c_str());
+ ASSERT_GE(pool_id, 0);
+ if (m_pool_peers.find(pool_id) != m_pool_peers.end()) {
+ m_pool_peers[pool_id].erase(peer);
+ if (m_pool_peers[pool_id].empty()) {
+ m_pool_peers.erase(pool_id);
+ }
+ }
+ m_pools.erase(name);
+ ASSERT_EQ(0, m_cluster->pool_delete(name.c_str()));
+ }
+
+ void set_peer_config_key(const std::string& pool_name,
+ const PeerSpec &peer) {
+ int64_t pool_id = m_cluster->pool_lookup(pool_name.c_str());
+ ASSERT_GE(pool_id, 0);
+
+ std::string json =
+ "{"
+ "\\\"mon_host\\\": \\\"" + peer.mon_host + "\\\", "
+ "\\\"key\\\": \\\"" + peer.key + "\\\""
+ "}";
+
+ bufferlist in_bl;
+ ASSERT_EQ(0, m_cluster->mon_command(
+ "{"
+ "\"prefix\": \"config-key set\","
+ "\"key\": \"" RBD_MIRROR_PEER_CONFIG_KEY_PREFIX + stringify(pool_id) +
+ "/" + peer.uuid + "\","
+ "\"val\": \"" + json + "\"" +
+ "}", in_bl, nullptr, nullptr));
+ }
+
+ void create_cache_pool(const string &base_pool, string *cache_pool_name) {
+ bufferlist inbl;
+ *cache_pool_name = get_temp_pool_name("test-rbd-mirror-");
+ ASSERT_EQ(0, m_cluster->pool_create(cache_pool_name->c_str()));
+
+ ASSERT_EQ(0, m_cluster->mon_command(
+ "{\"prefix\": \"osd tier add\", \"pool\": \"" + base_pool +
+ "\", \"tierpool\": \"" + *cache_pool_name +
+ "\", \"force_nonempty\": \"--force-nonempty\" }",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, m_cluster->mon_command(
+ "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + base_pool +
+ "\", \"overlaypool\": \"" + *cache_pool_name + "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, m_cluster->mon_command(
+ "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + *cache_pool_name +
+ "\", \"mode\": \"writeback\"}",
+ inbl, NULL, NULL));
+ m_cluster->wait_for_latest_osdmap();
+ }
+
+ void remove_cache_pool(const string &base_pool, const string &cache_pool) {
+ bufferlist inbl;
+ // tear down tiers
+ ASSERT_EQ(0, m_cluster->mon_command(
+ "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + base_pool +
+ "\"}",
+ inbl, NULL, NULL));
+ ASSERT_EQ(0, m_cluster->mon_command(
+ "{\"prefix\": \"osd tier remove\", \"pool\": \"" + base_pool +
+ "\", \"tierpool\": \"" + cache_pool + "\"}",
+ inbl, NULL, NULL));
+ m_cluster->wait_for_latest_osdmap();
+ m_cluster->pool_delete(cache_pool.c_str());
+ }
+
+ void check_peers() {
+ m_cluster_watcher->refresh_pools();
+ std::lock_guard l{m_lock};
+ ASSERT_EQ(m_pool_peers, m_cluster_watcher->get_pool_peers());
+ }
+
+ RadosRef m_cluster;
+ ceph::mutex m_lock = ceph::make_mutex("TestClusterWatcherLock");
+ std::unique_ptr<rbd::mirror::ServiceDaemon<>> m_service_daemon;
+ std::unique_ptr<ClusterWatcher> m_cluster_watcher;
+
+ set<string> m_pools;
+ ClusterWatcher::PoolPeers m_pool_peers;
+};
+
+TEST_F(TestClusterWatcher, NoPools) {
+ check_peers();
+}
+
+TEST_F(TestClusterWatcher, NoMirroredPools) {
+ check_peers();
+ create_pool(false, PeerSpec());
+ check_peers();
+ create_pool(false, PeerSpec());
+ check_peers();
+ create_pool(false, PeerSpec());
+ check_peers();
+}
+
+TEST_F(TestClusterWatcher, ReplicatedPools) {
+ PeerSpec site1("", "site1", "mirror1");
+ PeerSpec site2("", "site2", "mirror2");
+ string first_pool, last_pool;
+ check_peers();
+ create_pool(true, site1, &site1.uuid, &first_pool);
+ check_peers();
+ create_pool(false, PeerSpec());
+ check_peers();
+ create_pool(false, PeerSpec());
+ check_peers();
+ create_pool(false, PeerSpec());
+ check_peers();
+ create_pool(true, site2, &site2.uuid);
+ check_peers();
+ create_pool(true, site2, &site2.uuid);
+ check_peers();
+ create_pool(true, site2, &site2.uuid, &last_pool);
+ check_peers();
+ delete_pool(first_pool, site1);
+ check_peers();
+ delete_pool(last_pool, site2);
+ check_peers();
+}
+
+TEST_F(TestClusterWatcher, CachePools) {
+ PeerSpec site1("", "site1", "mirror1");
+ string base1, base2, cache1, cache2;
+ create_pool(true, site1, &site1.uuid, &base1);
+ check_peers();
+
+ create_cache_pool(base1, &cache1);
+ BOOST_SCOPE_EXIT( base1, cache1, this_ ) {
+ this_->remove_cache_pool(base1, cache1);
+ } BOOST_SCOPE_EXIT_END;
+ check_peers();
+
+ create_pool(false, PeerSpec(), nullptr, &base2);
+ create_cache_pool(base2, &cache2);
+ BOOST_SCOPE_EXIT( base2, cache2, this_ ) {
+ this_->remove_cache_pool(base2, cache2);
+ } BOOST_SCOPE_EXIT_END;
+ check_peers();
+}
+
+TEST_F(TestClusterWatcher, ConfigKey) {
+ REQUIRE(!is_librados_test_stub(*m_cluster));
+
+ std::string pool_name;
+ check_peers();
+
+ PeerSpec site1("", "site1", "mirror1");
+ create_pool(true, site1, &site1.uuid, &pool_name);
+ check_peers();
+
+ PeerSpec site2("", "site2", "mirror2");
+ site2.mon_host = "abc";
+ site2.key = "xyz";
+ create_pool(false, site2, &site2.uuid);
+ set_peer_config_key(pool_name, site2);
+
+ check_peers();
+}
+
+TEST_F(TestClusterWatcher, SiteName) {
+ REQUIRE(!is_librados_test_stub(*m_cluster));
+
+ std::string site_name;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.mirror_site_name_get(*m_cluster, &site_name));
+
+ m_cluster_watcher->refresh_pools();
+
+ std::lock_guard l{m_lock};
+ ASSERT_EQ(site_name, m_cluster_watcher->get_site_name());
+}
diff --git a/src/test/rbd_mirror/test_ImageDeleter.cc b/src/test/rbd_mirror/test_ImageDeleter.cc
new file mode 100644
index 000000000..5fa5d6db5
--- /dev/null
+++ b/src/test/rbd_mirror/test_ImageDeleter.cc
@@ -0,0 +1,313 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "include/rados/librados.hpp"
+#include "include/rbd/librbd.hpp"
+#include "include/stringify.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "tools/rbd_mirror/ImageDeleter.h"
+#include "tools/rbd_mirror/ServiceDaemon.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/Throttler.h"
+#include "tools/rbd_mirror/Types.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/Journal.h"
+#include "librbd/internal.h"
+#include "librbd/Utils.h"
+#include "librbd/api/Image.h"
+#include "librbd/api/Mirror.h"
+#include "librbd/journal/DisabledPolicy.h"
+#include "test/rbd_mirror/test_fixture.h"
+
+#include "test/librados/test.h"
+#include "gtest/gtest.h"
+
+#define GLOBAL_IMAGE_ID "global_image_id"
+#define GLOBAL_CLONE_IMAGE_ID "global_image_id_clone"
+
+#define dout_subsys ceph_subsys_rbd_mirror
+
+using rbd::mirror::RadosRef;
+using rbd::mirror::TestFixture;
+using namespace librbd;
+using cls::rbd::MirrorImageMode;
+using cls::rbd::MirrorImageState;
+
+
+void register_test_rbd_mirror_image_deleter() {
+}
+
+class TestImageDeleter : public TestFixture {
+public:
+ const std::string m_local_mirror_uuid = "local mirror uuid";
+ const std::string m_remote_mirror_uuid = "remote mirror uuid";
+
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ m_image_deletion_throttler.reset(
+ new rbd::mirror::Throttler<>(g_ceph_context,
+ "rbd_mirror_concurrent_image_deletions"));
+
+ m_service_daemon.reset(new rbd::mirror::ServiceDaemon<>(g_ceph_context,
+ _rados, m_threads));
+
+ librbd::api::Mirror<>::mode_set(m_local_io_ctx, RBD_MIRROR_MODE_IMAGE);
+
+ m_deleter = new rbd::mirror::ImageDeleter<>(
+ m_local_io_ctx, m_threads, m_image_deletion_throttler.get(),
+ m_service_daemon.get());
+
+ m_local_image_id = librbd::util::generate_image_id(m_local_io_ctx);
+ librbd::ImageOptions image_opts;
+ image_opts.set(RBD_IMAGE_OPTION_FEATURES, RBD_FEATURES_ALL);
+ EXPECT_EQ(0, librbd::create(m_local_io_ctx, m_image_name, m_local_image_id,
+ 1 << 20, image_opts, GLOBAL_IMAGE_ID,
+ m_remote_mirror_uuid, true));
+
+ cls::rbd::MirrorImage mirror_image(
+ MirrorImageMode::MIRROR_IMAGE_MODE_JOURNAL, GLOBAL_IMAGE_ID,
+ MirrorImageState::MIRROR_IMAGE_STATE_ENABLED);
+ EXPECT_EQ(0, cls_client::mirror_image_set(&m_local_io_ctx, m_local_image_id,
+ mirror_image));
+ }
+
+ void TearDown() override {
+ remove_image();
+
+ C_SaferCond ctx;
+ m_deleter->shut_down(&ctx);
+ ctx.wait();
+
+ delete m_deleter;
+ m_service_daemon.reset();
+
+ TestFixture::TearDown();
+ }
+
+ void init_image_deleter() {
+ C_SaferCond ctx;
+ m_deleter->init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ void remove_image() {
+ cls::rbd::MirrorImage mirror_image;
+ int r = cls_client::mirror_image_get(&m_local_io_ctx, m_local_image_id,
+ &mirror_image);
+ EXPECT_EQ(1, r == 0 || r == -ENOENT);
+ if (r != -ENOENT) {
+ mirror_image.state = MirrorImageState::MIRROR_IMAGE_STATE_ENABLED;
+ EXPECT_EQ(0, cls_client::mirror_image_set(&m_local_io_ctx,
+ m_local_image_id,
+ mirror_image));
+ }
+ promote_image();
+
+ NoOpProgressContext ctx;
+ r = librbd::api::Image<>::remove(m_local_io_ctx, m_image_name, ctx);
+ EXPECT_EQ(1, r == 0 || r == -ENOENT);
+ }
+
+ void promote_image(ImageCtx *ictx=nullptr) {
+ bool close = false;
+ int r = 0;
+ if (!ictx) {
+ ictx = new ImageCtx("", m_local_image_id, "", m_local_io_ctx,
+ false);
+ r = ictx->state->open(0);
+ close = (r == 0);
+ }
+
+ EXPECT_EQ(1, r == 0 || r == -ENOENT);
+
+ if (r == 0) {
+ int r2 = librbd::api::Mirror<>::image_promote(ictx, true);
+ EXPECT_EQ(1, r2 == 0 || r2 == -EINVAL);
+ }
+
+ if (close) {
+ EXPECT_EQ(0, ictx->state->close());
+ }
+ }
+
+ void demote_image(ImageCtx *ictx=nullptr) {
+ bool close = false;
+ if (!ictx) {
+ ictx = new ImageCtx("", m_local_image_id, "", m_local_io_ctx,
+ false);
+ EXPECT_EQ(0, ictx->state->open(0));
+ close = true;
+ }
+
+ EXPECT_EQ(0, librbd::api::Mirror<>::image_demote(ictx));
+
+ if (close) {
+ EXPECT_EQ(0, ictx->state->close());
+ }
+ }
+
+ void create_snapshot(std::string snap_name="snap1", bool protect=false) {
+ ImageCtx *ictx = new ImageCtx("", m_local_image_id, "", m_local_io_ctx,
+ false);
+ EXPECT_EQ(0, ictx->state->open(0));
+ {
+ std::unique_lock image_locker{ictx->image_lock};
+ ictx->set_journal_policy(new librbd::journal::DisabledPolicy());
+ }
+
+ librbd::NoOpProgressContext prog_ctx;
+ EXPECT_EQ(0, ictx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace(), snap_name, 0, prog_ctx));
+
+ if (protect) {
+ EXPECT_EQ(0, ictx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace(), snap_name));
+ }
+
+ EXPECT_EQ(0, ictx->state->close());
+ }
+
+ std::string create_clone() {
+ ImageCtx *ictx = new ImageCtx("", m_local_image_id, "", m_local_io_ctx,
+ false);
+ EXPECT_EQ(0, ictx->state->open(0));
+ {
+ std::unique_lock image_locker{ictx->image_lock};
+ ictx->set_journal_policy(new librbd::journal::DisabledPolicy());
+ }
+
+ librbd::NoOpProgressContext prog_ctx;
+ EXPECT_EQ(0, ictx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace(), "snap1", 0, prog_ctx));
+ EXPECT_EQ(0, ictx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace(), "snap1"));
+ EXPECT_EQ(0, librbd::api::Image<>::snap_set(
+ ictx, cls::rbd::UserSnapshotNamespace(), "snap1"));
+
+ std::string clone_id = librbd::util::generate_image_id(m_local_io_ctx);
+ librbd::ImageOptions clone_opts;
+ clone_opts.set(RBD_IMAGE_OPTION_FEATURES, ictx->features);
+ EXPECT_EQ(0, librbd::clone(m_local_io_ctx, m_local_image_id.c_str(),
+ nullptr, "snap1", m_local_io_ctx,
+ clone_id.c_str(), "clone1", clone_opts,
+ GLOBAL_CLONE_IMAGE_ID, m_remote_mirror_uuid));
+
+ cls::rbd::MirrorImage mirror_image(
+ MirrorImageMode::MIRROR_IMAGE_MODE_JOURNAL, GLOBAL_CLONE_IMAGE_ID,
+ MirrorImageState::MIRROR_IMAGE_STATE_ENABLED);
+ EXPECT_EQ(0, cls_client::mirror_image_set(&m_local_io_ctx, clone_id,
+ mirror_image));
+ EXPECT_EQ(0, ictx->state->close());
+ return clone_id;
+ }
+
+ void check_image_deleted() {
+ ImageCtx *ictx = new ImageCtx("", m_local_image_id, "", m_local_io_ctx,
+ false);
+ EXPECT_EQ(-ENOENT, ictx->state->open(0));
+
+ cls::rbd::MirrorImage mirror_image;
+ EXPECT_EQ(-ENOENT, cls_client::mirror_image_get(&m_local_io_ctx,
+ m_local_image_id,
+ &mirror_image));
+ }
+
+ int trash_move(const std::string& global_image_id) {
+ C_SaferCond ctx;
+ rbd::mirror::ImageDeleter<>::trash_move(m_local_io_ctx, global_image_id,
+ true, m_threads->work_queue, &ctx);
+ return ctx.wait();
+ }
+
+ librbd::RBD rbd;
+ std::string m_local_image_id;
+ std::unique_ptr<rbd::mirror::Throttler<>> m_image_deletion_throttler;
+ std::unique_ptr<rbd::mirror::ServiceDaemon<>> m_service_daemon;
+ rbd::mirror::ImageDeleter<> *m_deleter;
+};
+
+TEST_F(TestImageDeleter, ExistingTrashMove) {
+ ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID));
+
+ C_SaferCond ctx;
+ m_deleter->wait_for_deletion(m_local_image_id, false, &ctx);
+ init_image_deleter();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestImageDeleter, LiveTrashMove) {
+ init_image_deleter();
+
+ C_SaferCond ctx;
+ m_deleter->wait_for_deletion(m_local_image_id, false, &ctx);
+
+ ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID));
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestImageDeleter, Delete_Image_With_Snapshots) {
+ init_image_deleter();
+ create_snapshot("snap1");
+ create_snapshot("snap2");
+
+ C_SaferCond ctx;
+ m_deleter->wait_for_deletion(m_local_image_id, false, &ctx);
+ ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID));
+ EXPECT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(0u, m_deleter->get_delete_queue_items().size());
+ ASSERT_EQ(0u, m_deleter->get_failed_queue_items().size());
+}
+
+TEST_F(TestImageDeleter, Delete_Image_With_ProtectedSnapshots) {
+ init_image_deleter();
+ create_snapshot("snap1", true);
+ create_snapshot("snap2", true);
+
+ C_SaferCond ctx;
+ m_deleter->wait_for_deletion(m_local_image_id, false, &ctx);
+ ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID));
+ EXPECT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(0u, m_deleter->get_delete_queue_items().size());
+ ASSERT_EQ(0u, m_deleter->get_failed_queue_items().size());
+}
+
+TEST_F(TestImageDeleter, Delete_Image_With_Clone) {
+ init_image_deleter();
+ std::string clone_id = create_clone();
+
+ C_SaferCond ctx1;
+ m_deleter->set_busy_timer_interval(0.1);
+ m_deleter->wait_for_deletion(m_local_image_id, false, &ctx1);
+ ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID));
+ EXPECT_EQ(-EBUSY, ctx1.wait());
+
+ C_SaferCond ctx2;
+ m_deleter->wait_for_deletion(clone_id, false, &ctx2);
+ ASSERT_EQ(0, trash_move(GLOBAL_CLONE_IMAGE_ID));
+ EXPECT_EQ(0, ctx2.wait());
+
+ C_SaferCond ctx3;
+ m_deleter->wait_for_deletion(m_local_image_id, true, &ctx3);
+ EXPECT_EQ(0, ctx3.wait());
+
+ ASSERT_EQ(0u, m_deleter->get_delete_queue_items().size());
+ ASSERT_EQ(0u, m_deleter->get_failed_queue_items().size());
+}
+
diff --git a/src/test/rbd_mirror/test_ImageReplayer.cc b/src/test/rbd_mirror/test_ImageReplayer.cc
new file mode 100644
index 000000000..abe163cfd
--- /dev/null
+++ b/src/test/rbd_mirror/test_ImageReplayer.cc
@@ -0,0 +1,1664 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2016 Mirantis Inc
+ *
+ * Author: Mykola Golub <mgolub@mirantis.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include "include/rados/librados.hpp"
+#include "include/rbd/librbd.hpp"
+#include "include/stringify.h"
+#include "test/librbd/test_support.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "cls/journal/cls_journal_types.h"
+#include "cls/journal/cls_journal_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "journal/Journaler.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Journal.h"
+#include "librbd/Operations.h"
+#include "librbd/Utils.h"
+#include "librbd/internal.h"
+#include "librbd/api/Io.h"
+#include "librbd/api/Mirror.h"
+#include "librbd/api/Snapshot.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ReadResult.h"
+#include "tools/rbd_mirror/ImageReplayer.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/MirrorStatusUpdater.h"
+#include "tools/rbd_mirror/PoolMetaCache.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/Throttler.h"
+#include "tools/rbd_mirror/Types.h"
+
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+void register_test_rbd_mirror() {
+}
+
+#define TEST_IO_SIZE 512
+#define TEST_IO_COUNT 11
+
+namespace rbd {
+namespace mirror {
+
+template <typename T>
+class TestImageReplayer : public TestFixture {
+public:
+ static const cls::rbd::MirrorImageMode MIRROR_IMAGE_MODE =
+ T::MIRROR_IMAGE_MODE;
+ static const uint64_t FEATURES = T::FEATURES;
+
+ struct C_WatchCtx : public librados::WatchCtx2 {
+ TestImageReplayer *test;
+ std::string oid;
+ ceph::mutex lock = ceph::make_mutex("C_WatchCtx::lock");
+ ceph::condition_variable cond;
+ bool notified;
+
+ C_WatchCtx(TestImageReplayer *test, const std::string &oid)
+ : test(test), oid(oid), notified(false) {
+ }
+
+ void handle_notify(uint64_t notify_id, uint64_t cookie,
+ uint64_t notifier_id, bufferlist& bl_) override {
+ bufferlist bl;
+ test->m_remote_ioctx.notify_ack(oid, notify_id, cookie, bl);
+
+ std::lock_guard locker{lock};
+ notified = true;
+ cond.notify_all();
+ }
+
+ void handle_error(uint64_t cookie, int err) override {
+ ASSERT_EQ(0, err);
+ }
+ };
+
+ TestImageReplayer()
+ : m_local_cluster(new librados::Rados()), m_watch_handle(0)
+ {
+ EXPECT_EQ("", connect_cluster_pp(*m_local_cluster.get()));
+ EXPECT_EQ(0, m_local_cluster->conf_set("rbd_cache", "false"));
+ EXPECT_EQ(0, m_local_cluster->conf_set("rbd_mirror_journal_poll_age", "1"));
+ EXPECT_EQ(0, m_local_cluster->conf_set("rbd_mirror_journal_commit_age",
+ "0.1"));
+ m_local_pool_name = get_temp_pool_name();
+ EXPECT_EQ(0, m_local_cluster->pool_create(m_local_pool_name.c_str()));
+ EXPECT_EQ(0, m_local_cluster->ioctx_create(m_local_pool_name.c_str(),
+ m_local_ioctx));
+ m_local_ioctx.application_enable("rbd", true);
+
+ EXPECT_EQ("", connect_cluster_pp(m_remote_cluster));
+ EXPECT_EQ(0, m_remote_cluster.conf_set("rbd_cache", "false"));
+
+ m_remote_pool_name = get_temp_pool_name();
+ EXPECT_EQ(0, m_remote_cluster.pool_create(m_remote_pool_name.c_str()));
+ m_remote_pool_id = m_remote_cluster.pool_lookup(m_remote_pool_name.c_str());
+ EXPECT_GE(m_remote_pool_id, 0);
+
+ EXPECT_EQ(0, m_remote_cluster.ioctx_create(m_remote_pool_name.c_str(),
+ m_remote_ioctx));
+ m_remote_ioctx.application_enable("rbd", true);
+
+ // make snap id debugging easier when local/remote have different mappings
+ uint64_t snap_id;
+ EXPECT_EQ(0, m_remote_ioctx.selfmanaged_snap_create(&snap_id));
+
+ uint64_t features = FEATURES;
+ if (MIRROR_IMAGE_MODE == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_remote_ioctx,
+ RBD_MIRROR_MODE_POOL));
+ EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_local_ioctx,
+ RBD_MIRROR_MODE_POOL));
+ } else {
+ EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_remote_ioctx,
+ RBD_MIRROR_MODE_IMAGE));
+ EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_local_ioctx,
+ RBD_MIRROR_MODE_IMAGE));
+
+
+ uuid_d uuid_gen;
+ uuid_gen.generate_random();
+ std::string remote_peer_uuid = uuid_gen.to_string();
+
+ EXPECT_EQ(0, librbd::cls_client::mirror_peer_add(
+ &m_remote_ioctx, {remote_peer_uuid,
+ cls::rbd::MIRROR_PEER_DIRECTION_RX_TX,
+ "siteA", "client", m_local_mirror_uuid}));
+
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_ioctx.get_id(), {m_remote_mirror_uuid, remote_peer_uuid});
+ }
+
+ EXPECT_EQ(0, librbd::api::Mirror<>::uuid_get(m_remote_ioctx,
+ &m_remote_mirror_uuid));
+ EXPECT_EQ(0, librbd::api::Mirror<>::uuid_get(m_local_ioctx,
+ &m_local_mirror_uuid));
+
+ m_image_name = get_temp_image_name();
+ int order = 0;
+ EXPECT_EQ(0, librbd::create(m_remote_ioctx, m_image_name.c_str(), 1 << 22,
+ false, features, &order, 0, 0));
+
+ if (MIRROR_IMAGE_MODE != cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ librbd::ImageCtx* remote_image_ctx;
+ open_remote_image(&remote_image_ctx);
+ EXPECT_EQ(0,
+ librbd::api::Mirror<>::image_enable(
+ remote_image_ctx,
+ static_cast<rbd_mirror_image_mode_t>(MIRROR_IMAGE_MODE),
+ false));
+ close_image(remote_image_ctx);
+ }
+
+ m_remote_image_id = get_image_id(m_remote_ioctx, m_image_name);
+ m_global_image_id = get_global_image_id(m_remote_ioctx, m_remote_image_id);
+
+ auto cct = reinterpret_cast<CephContext*>(m_local_ioctx.cct());
+ m_threads.reset(new Threads<>(m_local_cluster));
+
+ m_image_sync_throttler.reset(new Throttler<>(
+ cct, "rbd_mirror_concurrent_image_syncs"));
+
+ m_instance_watcher = InstanceWatcher<>::create(
+ m_local_ioctx, *m_threads->asio_engine, nullptr,
+ m_image_sync_throttler.get());
+ m_instance_watcher->handle_acquire_leader();
+
+ EXPECT_EQ(0, m_local_ioctx.create(RBD_MIRRORING, false));
+
+ m_local_status_updater = MirrorStatusUpdater<>::create(
+ m_local_ioctx, m_threads.get(), "");
+ C_SaferCond status_updater_ctx;
+ m_local_status_updater->init(&status_updater_ctx);
+ EXPECT_EQ(0, status_updater_ctx.wait());
+ }
+
+ ~TestImageReplayer() override
+ {
+ unwatch();
+
+ m_instance_watcher->handle_release_leader();
+
+ delete m_replayer;
+ delete m_instance_watcher;
+
+ C_SaferCond status_updater_ctx;
+ m_local_status_updater->shut_down(&status_updater_ctx);
+ EXPECT_EQ(0, status_updater_ctx.wait());
+ delete m_local_status_updater;
+
+ EXPECT_EQ(0, m_remote_cluster.pool_delete(m_remote_pool_name.c_str()));
+ EXPECT_EQ(0, m_local_cluster->pool_delete(m_local_pool_name.c_str()));
+ }
+
+ void create_replayer() {
+ m_replayer = new ImageReplayer<>(m_local_ioctx, m_local_mirror_uuid,
+ m_global_image_id, m_threads.get(),
+ m_instance_watcher, m_local_status_updater,
+ nullptr, &m_pool_meta_cache);
+ m_replayer->add_peer({"peer uuid", m_remote_ioctx,
+ {m_remote_mirror_uuid, "remote mirror peer uuid"},
+ nullptr});
+ }
+
+ void start()
+ {
+ C_SaferCond cond;
+ m_replayer->start(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ create_watch_ctx();
+ }
+
+ void create_watch_ctx() {
+ std::string oid;
+ if (MIRROR_IMAGE_MODE == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ oid = ::journal::Journaler::header_oid(m_remote_image_id);
+ } else {
+ oid = librbd::util::header_name(m_remote_image_id);
+ }
+
+ ASSERT_EQ(0U, m_watch_handle);
+ ASSERT_TRUE(m_watch_ctx == nullptr);
+ m_watch_ctx = new C_WatchCtx(this, oid);
+ ASSERT_EQ(0, m_remote_ioctx.watch2(oid, &m_watch_handle, m_watch_ctx));
+ }
+
+ void unwatch() {
+ if (m_watch_handle != 0) {
+ m_remote_ioctx.unwatch2(m_watch_handle);
+ delete m_watch_ctx;
+ m_watch_ctx = nullptr;
+ m_watch_handle = 0;
+ }
+ }
+
+ void stop()
+ {
+ unwatch();
+
+ C_SaferCond cond;
+ m_replayer->stop(&cond);
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ void bootstrap()
+ {
+ create_replayer();
+
+ start();
+ wait_for_replay_complete();
+ stop();
+ }
+
+ std::string get_temp_image_name()
+ {
+ return "image" + stringify(++_image_number);
+ }
+
+ std::string get_image_id(librados::IoCtx &ioctx, const std::string &image_name)
+ {
+ std::string obj = librbd::util::id_obj_name(image_name);
+ std::string id;
+ EXPECT_EQ(0, librbd::cls_client::get_id(&ioctx, obj, &id));
+ return id;
+ }
+
+ std::string get_global_image_id(librados::IoCtx& io_ctx,
+ const std::string& image_id) {
+ cls::rbd::MirrorImage mirror_image;
+ EXPECT_EQ(0, librbd::cls_client::mirror_image_get(&io_ctx, image_id,
+ &mirror_image));
+ return mirror_image.global_image_id;
+ }
+
+ void open_image(librados::IoCtx &ioctx, const std::string &image_name,
+ bool readonly, librbd::ImageCtx **ictxp)
+ {
+ librbd::ImageCtx *ictx = new librbd::ImageCtx(image_name.c_str(),
+ "", "", ioctx, readonly);
+ EXPECT_EQ(0, ictx->state->open(0));
+ *ictxp = ictx;
+ }
+
+ void open_local_image(librbd::ImageCtx **ictxp)
+ {
+ open_image(m_local_ioctx, m_image_name, true, ictxp);
+ }
+
+ void open_remote_image(librbd::ImageCtx **ictxp)
+ {
+ open_image(m_remote_ioctx, m_image_name, false, ictxp);
+ }
+
+ void close_image(librbd::ImageCtx *ictx)
+ {
+ ictx->state->close();
+ }
+
+ void get_commit_positions(cls::journal::ObjectPosition *master_position,
+ cls::journal::ObjectPosition *mirror_position)
+ {
+ std::string master_client_id = "";
+ std::string mirror_client_id = m_local_mirror_uuid;
+
+ m_replayer->flush();
+
+ C_SaferCond cond;
+ uint64_t minimum_set;
+ uint64_t active_set;
+ std::set<cls::journal::Client> registered_clients;
+ std::string oid = ::journal::Journaler::header_oid(m_remote_image_id);
+ cls::journal::client::get_mutable_metadata(m_remote_ioctx, oid,
+ &minimum_set, &active_set,
+ &registered_clients, &cond);
+ ASSERT_EQ(0, cond.wait());
+
+ *master_position = cls::journal::ObjectPosition();
+ *mirror_position = cls::journal::ObjectPosition();
+
+ std::set<cls::journal::Client>::const_iterator c;
+ for (c = registered_clients.begin(); c != registered_clients.end(); ++c) {
+ std::cout << __func__ << ": client: " << *c << std::endl;
+ if (c->state != cls::journal::CLIENT_STATE_CONNECTED) {
+ continue;
+ }
+ cls::journal::ObjectPositions object_positions =
+ c->commit_position.object_positions;
+ cls::journal::ObjectPositions::const_iterator p =
+ object_positions.begin();
+ if (p != object_positions.end()) {
+ if (c->id == master_client_id) {
+ ASSERT_EQ(cls::journal::ObjectPosition(), *master_position);
+ *master_position = *p;
+ } else if (c->id == mirror_client_id) {
+ ASSERT_EQ(cls::journal::ObjectPosition(), *mirror_position);
+ *mirror_position = *p;
+ }
+ }
+ }
+ }
+
+ bool wait_for_watcher_notify(int seconds)
+ {
+ if (m_watch_handle == 0) {
+ return false;
+ }
+
+ std::unique_lock locker{m_watch_ctx->lock};
+ while (!m_watch_ctx->notified) {
+ if (m_watch_ctx->cond.wait_for(locker,
+ std::chrono::seconds(seconds)) ==
+ std::cv_status::timeout) {
+ return false;
+ }
+ }
+ m_watch_ctx->notified = false;
+ return true;
+ }
+
+ int get_last_mirror_snapshot(librados::IoCtx& io_ctx,
+ const std::string& image_id,
+ uint64_t* mirror_snap_id,
+ cls::rbd::MirrorSnapshotNamespace* mirror_ns) {
+ auto header_oid = librbd::util::header_name(image_id);
+ ::SnapContext snapc;
+ int r = librbd::cls_client::get_snapcontext(&io_ctx, header_oid, &snapc);
+ if (r < 0) {
+ return r;
+ }
+
+ // stored in reverse order
+ for (auto snap_id : snapc.snaps) {
+ cls::rbd::SnapshotInfo snap_info;
+ r = librbd::cls_client::snapshot_get(&io_ctx, header_oid, snap_id,
+ &snap_info);
+ if (r < 0) {
+ return r;
+ }
+
+ auto ns = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+ &snap_info.snapshot_namespace);
+ if (ns != nullptr) {
+ *mirror_snap_id = snap_id;
+ *mirror_ns = *ns;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+ }
+
+ void wait_for_journal_synced() {
+ cls::journal::ObjectPosition master_position;
+ cls::journal::ObjectPosition mirror_position;
+ for (int i = 0; i < 100; i++) {
+ get_commit_positions(&master_position, &mirror_position);
+ if (master_position == mirror_position) {
+ break;
+ }
+ wait_for_watcher_notify(1);
+ }
+
+ ASSERT_EQ(master_position, mirror_position);
+ }
+
+ void wait_for_snapshot_synced() {
+ uint64_t remote_snap_id = CEPH_NOSNAP;
+ cls::rbd::MirrorSnapshotNamespace remote_mirror_ns;
+ ASSERT_EQ(0, get_last_mirror_snapshot(m_remote_ioctx, m_remote_image_id,
+ &remote_snap_id, &remote_mirror_ns));
+
+ std::cout << "remote_snap_id=" << remote_snap_id << std::endl;
+
+ std::string local_image_id;
+ ASSERT_EQ(0, librbd::cls_client::mirror_image_get_image_id(
+ &m_local_ioctx, m_global_image_id, &local_image_id));
+
+ uint64_t local_snap_id = CEPH_NOSNAP;
+ cls::rbd::MirrorSnapshotNamespace local_mirror_ns;
+ for (int i = 0; i < 100; i++) {
+ int r = get_last_mirror_snapshot(m_local_ioctx, local_image_id,
+ &local_snap_id, &local_mirror_ns);
+ if (r == 0 &&
+ ((remote_mirror_ns.state ==
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY &&
+ local_mirror_ns.state ==
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY) ||
+ (remote_mirror_ns.state ==
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED &&
+ local_mirror_ns.state ==
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED)) &&
+ local_mirror_ns.primary_mirror_uuid == m_remote_mirror_uuid &&
+ local_mirror_ns.primary_snap_id == remote_snap_id &&
+ local_mirror_ns.complete) {
+
+ std::cout << "local_snap_id=" << local_snap_id << ", "
+ << "local_snap_ns=" << local_mirror_ns << std::endl;
+ return;
+ }
+
+ wait_for_watcher_notify(1);
+ }
+
+ ADD_FAILURE() << "failed to locate matching snapshot: "
+ << "remote_snap_id=" << remote_snap_id << ", "
+ << "remote_snap_ns=" << remote_mirror_ns << ", "
+ << "local_snap_id=" << local_snap_id << ", "
+ << "local_snap_ns=" << local_mirror_ns;
+ }
+
+ void wait_for_replay_complete()
+ {
+ if (MIRROR_IMAGE_MODE == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ wait_for_journal_synced();
+ } else {
+ wait_for_snapshot_synced();
+ }
+ }
+
+ void wait_for_stopped() {
+ for (int i = 0; i < 100; i++) {
+ if (m_replayer->is_stopped()) {
+ break;
+ }
+ wait_for_watcher_notify(1);
+ }
+ ASSERT_TRUE(m_replayer->is_stopped());
+ }
+
+ void write_test_data(librbd::ImageCtx *ictx, const char *test_data, off_t off,
+ size_t len)
+ {
+ size_t written;
+ bufferlist bl;
+ bl.append(std::string(test_data, len));
+ written = librbd::api::Io<>::write(*ictx, off, len, std::move(bl), 0);
+ printf("wrote: %d\n", (int)written);
+ ASSERT_EQ(len, written);
+ }
+
+ void read_test_data(librbd::ImageCtx *ictx, const char *expected, off_t off,
+ size_t len)
+ {
+ ssize_t read;
+ char *result = (char *)malloc(len + 1);
+
+ ASSERT_NE(static_cast<char *>(NULL), result);
+ read = librbd::api::Io<>::read(
+ *ictx, off, len, librbd::io::ReadResult{result, len}, 0);
+ printf("read: %d\n", (int)read);
+ ASSERT_EQ(len, static_cast<size_t>(read));
+ result[len] = '\0';
+ if (memcmp(result, expected, len)) {
+ printf("read: %s\nexpected: %s\n", result, expected);
+ ASSERT_EQ(0, memcmp(result, expected, len));
+ }
+ free(result);
+ }
+
+ void generate_test_data() {
+ for (int i = 0; i < TEST_IO_SIZE; ++i) {
+ m_test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ m_test_data[TEST_IO_SIZE] = '\0';
+ }
+
+ void flush(librbd::ImageCtx *ictx)
+ {
+ C_SaferCond aio_flush_ctx;
+ auto c = librbd::io::AioCompletion::create(&aio_flush_ctx);
+ c->get();
+ librbd::api::Io<>::aio_flush(*ictx, c, true);
+ ASSERT_EQ(0, c->wait_for_complete());
+ c->put();
+
+ if (MIRROR_IMAGE_MODE == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ C_SaferCond journal_flush_ctx;
+ ictx->journal->flush_commit_position(&journal_flush_ctx);
+ ASSERT_EQ(0, journal_flush_ctx.wait());
+ } else {
+ uint64_t snap_id = CEPH_NOSNAP;
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_snapshot_create(
+ ictx, 0, &snap_id));
+ }
+
+ printf("flushed\n");
+ }
+
+ static int _image_number;
+
+ PoolMetaCache m_pool_meta_cache{g_ceph_context};
+
+ std::shared_ptr<librados::Rados> m_local_cluster;
+ std::unique_ptr<Threads<>> m_threads;
+ std::unique_ptr<Throttler<>> m_image_sync_throttler;
+ librados::Rados m_remote_cluster;
+ InstanceWatcher<> *m_instance_watcher;
+ MirrorStatusUpdater<> *m_local_status_updater;
+ std::string m_local_mirror_uuid = "local mirror uuid";
+ std::string m_remote_mirror_uuid = "remote mirror uuid";
+ std::string m_local_pool_name, m_remote_pool_name;
+ librados::IoCtx m_local_ioctx, m_remote_ioctx;
+ std::string m_image_name;
+ int64_t m_remote_pool_id;
+ std::string m_remote_image_id;
+ std::string m_global_image_id;
+ ImageReplayer<> *m_replayer = nullptr;
+ C_WatchCtx *m_watch_ctx = nullptr;
+ uint64_t m_watch_handle = 0;
+ char m_test_data[TEST_IO_SIZE + 1];
+ std::string m_journal_commit_age;
+};
+
+template <typename T>
+int TestImageReplayer<T>::_image_number;
+
+template <cls::rbd::MirrorImageMode _mirror_image_mode, uint64_t _features>
+class TestImageReplayerParams {
+public:
+ static const cls::rbd::MirrorImageMode MIRROR_IMAGE_MODE = _mirror_image_mode;
+ static const uint64_t FEATURES = _features;
+};
+
+typedef ::testing::Types<TestImageReplayerParams<
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, 125>,
+ TestImageReplayerParams<
+ cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, 1>,
+ TestImageReplayerParams<
+ cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, 5>,
+ TestImageReplayerParams<
+ cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, 61>,
+ TestImageReplayerParams<
+ cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, 125>>
+ TestImageReplayerTypes;
+
+TYPED_TEST_SUITE(TestImageReplayer, TestImageReplayerTypes);
+
+TYPED_TEST(TestImageReplayer, Bootstrap)
+{
+ this->bootstrap();
+}
+
+typedef TestImageReplayer<TestImageReplayerParams<
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, 125>> TestImageReplayerJournal;
+
+TYPED_TEST(TestImageReplayer, BootstrapErrorLocalImageExists)
+{
+ int order = 0;
+ EXPECT_EQ(0, librbd::create(this->m_local_ioctx, this->m_image_name.c_str(),
+ 1 << 22, false, 0, &order, 0, 0));
+
+ this->create_replayer();
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(-EEXIST, cond.wait());
+}
+
+TEST_F(TestImageReplayerJournal, BootstrapErrorNoJournal)
+{
+ ASSERT_EQ(0, librbd::Journal<>::remove(this->m_remote_ioctx,
+ this->m_remote_image_id));
+
+ this->create_replayer();
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(-ENOENT, cond.wait());
+}
+
+TYPED_TEST(TestImageReplayer, BootstrapErrorMirrorDisabled)
+{
+ // disable remote image mirroring
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(this->m_remote_ioctx,
+ RBD_MIRROR_MODE_IMAGE));
+ librbd::ImageCtx *ictx;
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_disable(ictx, true));
+ this->close_image(ictx);
+
+ this->create_replayer();
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(-ENOENT, cond.wait());
+}
+
+TYPED_TEST(TestImageReplayer, BootstrapMirrorDisabling)
+{
+ // set remote image mirroring state to DISABLING
+ if (gtest_TypeParam_::MIRROR_IMAGE_MODE ==
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(this->m_remote_ioctx,
+ RBD_MIRROR_MODE_IMAGE));
+ librbd::ImageCtx *ictx;
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(
+ ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false));
+ this->close_image(ictx);
+ }
+
+ cls::rbd::MirrorImage mirror_image;
+ ASSERT_EQ(0, librbd::cls_client::mirror_image_get(&this->m_remote_ioctx,
+ this->m_remote_image_id,
+ &mirror_image));
+ mirror_image.state = cls::rbd::MirrorImageState::MIRROR_IMAGE_STATE_DISABLING;
+ ASSERT_EQ(0, librbd::cls_client::mirror_image_set(&this->m_remote_ioctx,
+ this->m_remote_image_id,
+ mirror_image));
+
+ this->create_replayer();
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(-ENOENT, cond.wait());
+ ASSERT_TRUE(this->m_replayer->is_stopped());
+}
+
+TYPED_TEST(TestImageReplayer, BootstrapDemoted)
+{
+ // demote remote image
+ librbd::ImageCtx *ictx;
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_demote(ictx));
+ this->close_image(ictx);
+
+ this->create_replayer();
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(-EREMOTEIO, cond.wait());
+ ASSERT_TRUE(this->m_replayer->is_stopped());
+}
+
+TYPED_TEST(TestImageReplayer, StartInterrupted)
+{
+ this->create_replayer();
+ C_SaferCond start_cond, stop_cond;
+ this->m_replayer->start(&start_cond);
+ this->m_replayer->stop(&stop_cond);
+ int r = start_cond.wait();
+ printf("start returned %d\n", r);
+ // TODO: improve the test to avoid this race
+ ASSERT_TRUE(r == -ECANCELED || r == 0);
+ ASSERT_EQ(0, stop_cond.wait());
+}
+
+TEST_F(TestImageReplayerJournal, JournalReset)
+{
+ this->bootstrap();
+ delete this->m_replayer;
+
+ ASSERT_EQ(0, librbd::Journal<>::reset(this->m_remote_ioctx,
+ this->m_remote_image_id));
+
+ // try to recover
+ this->bootstrap();
+}
+
+TEST_F(TestImageReplayerJournal, ErrorNoJournal)
+{
+ this->bootstrap();
+
+ // disable remote journal journaling
+ // (reset before disabling, so it does not fail with EBUSY)
+ ASSERT_EQ(0, librbd::Journal<>::reset(this->m_remote_ioctx,
+ this->m_remote_image_id));
+ librbd::ImageCtx *ictx;
+ this->open_remote_image(&ictx);
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
+ false));
+ this->close_image(ictx);
+
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(0, cond.wait());
+}
+
+TYPED_TEST(TestImageReplayer, StartStop)
+{
+ this->bootstrap();
+
+ this->start();
+ this->wait_for_replay_complete();
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, WriteAndStartReplay)
+{
+ this->bootstrap();
+
+ // Write to remote image and start replay
+
+ librbd::ImageCtx *ictx;
+
+ this->generate_test_data();
+ this->open_remote_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ this->start();
+ this->wait_for_replay_complete();
+ this->stop();
+
+ this->open_local_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->read_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+}
+
+TYPED_TEST(TestImageReplayer, StartReplayAndWrite)
+{
+ this->bootstrap();
+
+ // Start replay and write to remote image
+
+ librbd::ImageCtx *ictx;
+
+ this->start();
+
+ this->generate_test_data();
+ this->open_remote_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+
+ this->wait_for_replay_complete();
+
+ for (int i = TEST_IO_COUNT; i < 2 * TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ for (int i = 0; i < 2 * TEST_IO_COUNT; ++i) {
+ this->read_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+
+ this->stop();
+}
+
+TEST_F(TestImageReplayerJournal, NextTag)
+{
+ this->bootstrap();
+
+ // write, reopen, and write again to test switch to the next tag
+
+ librbd::ImageCtx *ictx;
+
+ this->start();
+
+ this->generate_test_data();
+
+ const int N = 10;
+
+ for (int j = 0; j < N; j++) {
+ this->open_remote_image(&ictx);
+ for (int i = j * TEST_IO_COUNT; i < (j + 1) * TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+ }
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ for (int i = 0; i < N * TEST_IO_COUNT; ++i) {
+ this->read_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, Resync)
+{
+ this->bootstrap();
+
+ librbd::ImageCtx *ictx;
+
+ this->start();
+
+ this->generate_test_data();
+
+ this->open_remote_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+
+ this->wait_for_replay_complete();
+
+ for (int i = TEST_IO_COUNT; i < 2 * TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ this->open_local_image(&ictx);
+ EXPECT_EQ(0, librbd::api::Mirror<>::image_resync(ictx));
+ this->close_image(ictx);
+
+ this->wait_for_stopped();
+
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ ASSERT_TRUE(this->m_replayer->is_replaying());
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ for (int i = 0; i < 2 * TEST_IO_COUNT; ++i) {
+ this->read_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, Resync_While_Stop)
+{
+ this->bootstrap();
+
+ this->start();
+
+ this->generate_test_data();
+
+ librbd::ImageCtx *ictx;
+ this->open_remote_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+
+ this->wait_for_replay_complete();
+
+ for (int i = TEST_IO_COUNT; i < 2 * TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ C_SaferCond cond;
+ this->m_replayer->stop(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ this->open_local_image(&ictx);
+ EXPECT_EQ(0, librbd::api::Mirror<>::image_resync(ictx));
+ this->close_image(ictx);
+
+ C_SaferCond cond2;
+ this->m_replayer->start(&cond2);
+ ASSERT_EQ(0, cond2.wait());
+
+ ASSERT_TRUE(this->m_replayer->is_stopped());
+
+ C_SaferCond cond3;
+ this->m_replayer->start(&cond3);
+ ASSERT_EQ(0, cond3.wait());
+
+ ASSERT_TRUE(this->m_replayer->is_replaying());
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ for (int i = 0; i < 2 * TEST_IO_COUNT; ++i) {
+ this->read_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, Resync_StartInterrupted)
+{
+ this->bootstrap();
+
+ librbd::ImageCtx *ictx;
+ this->open_local_image(&ictx);
+ EXPECT_EQ(0, librbd::api::Mirror<>::image_resync(ictx));
+ this->close_image(ictx);
+
+ C_SaferCond cond;
+ this->m_replayer->start(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ ASSERT_TRUE(this->m_replayer->is_stopped());
+
+ C_SaferCond cond2;
+ this->m_replayer->start(&cond2);
+ ASSERT_EQ(0, cond2.wait());
+
+ this->create_watch_ctx();
+
+ ASSERT_TRUE(this->m_replayer->is_replaying());
+
+ this->generate_test_data();
+ this->open_remote_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+
+ this->wait_for_replay_complete();
+
+ for (int i = TEST_IO_COUNT; i < 2 * TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ for (int i = 0; i < 2 * TEST_IO_COUNT; ++i) {
+ this->read_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+
+ this->stop();
+}
+
+TEST_F(TestImageReplayerJournal, MultipleReplayFailures_SingleEpoch) {
+ this->bootstrap();
+
+ // inject a snapshot that cannot be unprotected
+ librbd::ImageCtx *ictx;
+ this->open_image(this->m_local_ioctx, this->m_image_name, false, &ictx);
+ ictx->features &= ~RBD_FEATURE_JOURNALING;
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "foo", 0, prog_ctx));
+ ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "foo"));
+ ASSERT_EQ(0, librbd::cls_client::add_child(&ictx->md_ctx, RBD_CHILDREN,
+ {ictx->md_ctx.get_id(), "",
+ ictx->id,
+ ictx->snap_ids[{cls::rbd::UserSnapshotNamespace(), "foo"}]},
+ "dummy child id"));
+ this->close_image(ictx);
+
+ // race failed op shut down with new ops
+ this->open_remote_image(&ictx);
+ for (uint64_t i = 0; i < 10; ++i) {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ C_SaferCond request_lock;
+ ictx->exclusive_lock->acquire_lock(&request_lock);
+ ASSERT_EQ(0, request_lock.wait());
+
+ C_SaferCond append_ctx;
+ ictx->journal->append_op_event(
+ i,
+ librbd::journal::EventEntry{
+ librbd::journal::SnapUnprotectEvent{i,
+ cls::rbd::UserSnapshotNamespace(),
+ "foo"}},
+ &append_ctx);
+ ASSERT_EQ(0, append_ctx.wait());
+
+ C_SaferCond commit_ctx;
+ ictx->journal->commit_op_event(i, 0, &commit_ctx);
+ ASSERT_EQ(0, commit_ctx.wait());
+
+ C_SaferCond release_ctx;
+ ictx->exclusive_lock->release_lock(&release_ctx);
+ ASSERT_EQ(0, release_ctx.wait());
+ }
+
+ for (uint64_t i = 0; i < 5; ++i) {
+ this->start();
+ this->wait_for_stopped();
+ this->unwatch();
+ }
+ this->close_image(ictx);
+}
+
+TEST_F(TestImageReplayerJournal, MultipleReplayFailures_MultiEpoch) {
+ this->bootstrap();
+
+ // inject a snapshot that cannot be unprotected
+ librbd::ImageCtx *ictx;
+ this->open_image(this->m_local_ioctx, this->m_image_name, false, &ictx);
+ ictx->features &= ~RBD_FEATURE_JOURNALING;
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ "foo", 0, prog_ctx));
+ ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ "foo"));
+ ASSERT_EQ(0, librbd::cls_client::add_child(&ictx->md_ctx, RBD_CHILDREN,
+ {ictx->md_ctx.get_id(), "",
+ ictx->id,
+ ictx->snap_ids[{cls::rbd::UserSnapshotNamespace(),
+ "foo"}]},
+ "dummy child id"));
+ this->close_image(ictx);
+
+ // race failed op shut down with new tag flush
+ this->open_remote_image(&ictx);
+ {
+ std::shared_lock owner_locker{ictx->owner_lock};
+ C_SaferCond request_lock;
+ ictx->exclusive_lock->acquire_lock(&request_lock);
+ ASSERT_EQ(0, request_lock.wait());
+
+ C_SaferCond append_ctx;
+ ictx->journal->append_op_event(
+ 1U,
+ librbd::journal::EventEntry{
+ librbd::journal::SnapUnprotectEvent{1U,
+ cls::rbd::UserSnapshotNamespace(),
+ "foo"}},
+ &append_ctx);
+ ASSERT_EQ(0, append_ctx.wait());
+
+ C_SaferCond commit_ctx;
+ ictx->journal->commit_op_event(1U, 0, &commit_ctx);
+ ASSERT_EQ(0, commit_ctx.wait());
+
+ C_SaferCond release_ctx;
+ ictx->exclusive_lock->release_lock(&release_ctx);
+ ASSERT_EQ(0, release_ctx.wait());
+ }
+
+ this->generate_test_data();
+ this->write_test_data(ictx, this->m_test_data, 0, TEST_IO_SIZE);
+
+ for (uint64_t i = 0; i < 5; ++i) {
+ this->start();
+ this->wait_for_stopped();
+ this->unwatch();
+ }
+ this->close_image(ictx);
+}
+
+TEST_F(TestImageReplayerJournal, Disconnect)
+{
+ this->bootstrap();
+
+ // Make sure rbd_mirroring_resync_after_disconnect is not set
+ EXPECT_EQ(0, this->m_local_cluster->conf_set("rbd_mirroring_resync_after_disconnect", "false"));
+
+ // Test start fails if disconnected
+
+ librbd::ImageCtx *ictx;
+
+ this->generate_test_data();
+ this->open_remote_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ std::string oid = ::journal::Journaler::header_oid(this->m_remote_image_id);
+ ASSERT_EQ(0,
+ cls::journal::client::client_update_state(this->m_remote_ioctx,
+ oid, this->m_local_mirror_uuid,
+ cls::journal::CLIENT_STATE_DISCONNECTED));
+
+ C_SaferCond cond1;
+ this->m_replayer->start(&cond1);
+ ASSERT_EQ(-ENOTCONN, cond1.wait());
+
+ // Test start succeeds after resync
+
+ this->open_local_image(&ictx);
+ librbd::Journal<>::request_resync(ictx);
+ this->close_image(ictx);
+ C_SaferCond cond2;
+ this->m_replayer->start(&cond2);
+ ASSERT_EQ(0, cond2.wait());
+
+ this->start();
+ this->wait_for_replay_complete();
+
+ // Test replay stopped after disconnect
+
+ this->open_remote_image(&ictx);
+ for (int i = TEST_IO_COUNT; i < 2 * TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ ASSERT_EQ(0,
+ cls::journal::client::client_update_state(this->m_remote_ioctx, oid,
+ this->m_local_mirror_uuid,
+ cls::journal::CLIENT_STATE_DISCONNECTED));
+ bufferlist bl;
+ ASSERT_EQ(0, this->m_remote_ioctx.notify2(oid, bl, 5000, NULL));
+
+ this->wait_for_stopped();
+
+ // Test start fails after disconnect
+
+ C_SaferCond cond3;
+ this->m_replayer->start(&cond3);
+ ASSERT_EQ(-ENOTCONN, cond3.wait());
+ C_SaferCond cond4;
+ this->m_replayer->start(&cond4);
+ ASSERT_EQ(-ENOTCONN, cond4.wait());
+
+ // Test automatic resync if rbd_mirroring_resync_after_disconnect is set
+
+ EXPECT_EQ(0, this->m_local_cluster->conf_set("rbd_mirroring_resync_after_disconnect", "true"));
+
+ // Resync is flagged on first start attempt
+ C_SaferCond cond5;
+ this->m_replayer->start(&cond5);
+ ASSERT_EQ(-ENOTCONN, cond5.wait());
+
+ C_SaferCond cond6;
+ this->m_replayer->start(&cond6);
+ ASSERT_EQ(0, cond6.wait());
+ this->wait_for_replay_complete();
+
+ this->stop();
+}
+
+TEST_F(TestImageReplayerJournal, UpdateFeatures)
+{
+ // TODO add support to snapshot-based mirroring
+ const uint64_t FEATURES_TO_UPDATE =
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF;
+
+ uint64_t features;
+ librbd::ImageCtx *ictx;
+
+ // Make sure the features we will update are disabled initially
+
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ features &= FEATURES_TO_UPDATE;
+ if (features) {
+ ASSERT_EQ(0, ictx->operations->update_features(FEATURES_TO_UPDATE,
+ false));
+ }
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
+ this->close_image(ictx);
+
+ this->bootstrap();
+
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
+ this->close_image(ictx);
+
+ this->open_local_image(&ictx);
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
+ this->close_image(ictx);
+
+ // Start replay and update features
+
+ this->start();
+
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, ictx->operations->update_features(FEATURES_TO_UPDATE,
+ true));
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(FEATURES_TO_UPDATE, features & FEATURES_TO_UPDATE);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(FEATURES_TO_UPDATE, features & FEATURES_TO_UPDATE);
+ this->close_image(ictx);
+
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, ictx->operations->update_features(FEATURES_TO_UPDATE,
+ false));
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
+ this->close_image(ictx);
+
+ // Test update_features error does not stop replication
+
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_NE(0U, features & RBD_FEATURE_EXCLUSIVE_LOCK);
+ ASSERT_EQ(-EINVAL, ictx->operations->update_features(RBD_FEATURE_EXCLUSIVE_LOCK,
+ false));
+ this->generate_test_data();
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->read_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+
+ this->stop();
+}
+
+TEST_F(TestImageReplayerJournal, MetadataSetRemove)
+{
+ // TODO add support to snapshot-based mirroring
+ const std::string KEY = "test_key";
+ const std::string VALUE = "test_value";
+
+ librbd::ImageCtx *ictx;
+ std::string value;
+
+ this->bootstrap();
+
+ this->start();
+
+ // Test metadata_set replication
+
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, ictx->operations->metadata_set(KEY, VALUE));
+ value.clear();
+ ASSERT_EQ(0, librbd::metadata_get(ictx, KEY, &value));
+ ASSERT_EQ(VALUE, value);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ value.clear();
+ ASSERT_EQ(0, librbd::metadata_get(ictx, KEY, &value));
+ ASSERT_EQ(VALUE, value);
+ this->close_image(ictx);
+
+ // Test metadata_remove replication
+
+ this->open_remote_image(&ictx);
+ ASSERT_EQ(0, ictx->operations->metadata_remove(KEY));
+ ASSERT_EQ(-ENOENT, librbd::metadata_get(ictx, KEY, &value));
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+
+ this->open_local_image(&ictx);
+ ASSERT_EQ(-ENOENT, librbd::metadata_get(ictx, KEY, &value));
+ this->close_image(ictx);
+
+ this->stop();
+}
+
+TEST_F(TestImageReplayerJournal, MirroringDelay)
+{
+ // TODO add support to snapshot-based mirroring
+ const double DELAY = 10; // set less than wait_for_replay_complete timeout
+
+ librbd::ImageCtx *ictx;
+ utime_t start_time;
+ double delay;
+
+ this->bootstrap();
+
+ ASSERT_EQ(0, this->m_local_cluster->conf_set("rbd_mirroring_replay_delay",
+ stringify(DELAY).c_str()));
+ this->open_local_image(&ictx);
+ ASSERT_EQ(DELAY, ictx->mirroring_replay_delay);
+ this->close_image(ictx);
+
+ this->start();
+
+ // Test delay
+
+ this->generate_test_data();
+ this->open_remote_image(&ictx);
+ start_time = ceph_clock_now();
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->flush(ictx);
+ this->close_image(ictx);
+
+ this->wait_for_replay_complete();
+ delay = ceph_clock_now() - start_time;
+ ASSERT_GE(delay, DELAY);
+
+ // Test stop when delaying replay
+
+ this->open_remote_image(&ictx);
+ start_time = ceph_clock_now();
+ for (int i = 0; i < TEST_IO_COUNT; ++i) {
+ this->write_test_data(ictx, this->m_test_data, TEST_IO_SIZE * i,
+ TEST_IO_SIZE);
+ }
+ this->close_image(ictx);
+
+ sleep(DELAY / 2);
+ this->stop();
+ this->start();
+
+ this->wait_for_replay_complete();
+ delay = ceph_clock_now() - start_time;
+ ASSERT_GE(delay, DELAY);
+
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, ImageRename) {
+ this->create_replayer();
+ this->start();
+
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+ auto image_name = this->get_temp_image_name();
+ ASSERT_EQ(0, remote_image_ctx->operations->rename(image_name.c_str()));
+ this->flush(remote_image_ctx);
+
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_image(this->m_local_ioctx, image_name, true, &local_image_ctx);
+ ASSERT_EQ(image_name, local_image_ctx->name);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, UpdateFeatures) {
+ const uint64_t FEATURES_TO_UPDATE =
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF | RBD_FEATURE_DEEP_FLATTEN;
+ REQUIRE((this->FEATURES & FEATURES_TO_UPDATE) == FEATURES_TO_UPDATE);
+
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+ (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF), false));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ ASSERT_EQ(0U, local_image_ctx->features & (
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF));
+
+ // enable object-map/fast-diff
+ ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+ (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF), true));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ ASSERT_EQ(RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF,
+ local_image_ctx->features & (
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF));
+
+ // disable deep-flatten
+ ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+ RBD_FEATURE_DEEP_FLATTEN, false));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ ASSERT_EQ(0, local_image_ctx->features & RBD_FEATURE_DEEP_FLATTEN);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotUnprotect) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create a protected snapshot
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1", 0, prog_ctx));
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+ auto local_snap_id = local_snap_id_it->second;
+ auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ // unprotect the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_unprotect(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotProtect) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create an unprotected snapshot
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1", 0, prog_ctx));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+ auto local_snap_id = local_snap_id_it->second;
+ auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ // protect the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotRemove) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create a user snapshot
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1", 0, prog_ctx));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+
+ // remove the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_remove(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_EQ(local_image_ctx->snap_ids.end(), local_snap_id_it);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotRename) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create a user snapshot
+ librbd::NoOpProgressContext prog_ctx;
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1", 0, prog_ctx));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+ auto local_snap_id = local_snap_id_it->second;
+ auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ // rename the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_rename(
+ "snap1", "snap1-renamed"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ("snap1-renamed", local_snap_info_it->second.name);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotLimit) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ // update the snap limit
+ ASSERT_EQ(0, librbd::api::Snapshot<>::set_limit(remote_image_ctx, 123U));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ uint64_t local_snap_limit;
+ ASSERT_EQ(0, librbd::api::Snapshot<>::get_limit(local_image_ctx,
+ &local_snap_limit));
+ ASSERT_EQ(123U, local_snap_limit);
+
+ // update the limit again
+ ASSERT_EQ(0, librbd::api::Snapshot<>::set_limit(
+ remote_image_ctx, std::numeric_limits<uint64_t>::max()));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, librbd::api::Snapshot<>::get_limit(local_image_ctx,
+ &local_snap_limit));
+ ASSERT_EQ(std::numeric_limits<uint64_t>::max(), local_snap_limit);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_ImageSync.cc b/src/test/rbd_mirror/test_ImageSync.cc
new file mode 100644
index 000000000..93349ca11
--- /dev/null
+++ b/src/test/rbd_mirror/test_ImageSync.cc
@@ -0,0 +1,374 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_fixture.h"
+#include "include/stringify.h"
+#include "include/rbd/librbd.hpp"
+#include "common/Cond.h"
+#include "journal/Journaler.h"
+#include "journal/Settings.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/Journal.h"
+#include "librbd/Operations.h"
+#include "librbd/api/Io.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/ReadResult.h"
+#include "librbd/journal/Types.h"
+#include "tools/rbd_mirror/ImageSync.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/Throttler.h"
+#include "tools/rbd_mirror/image_replayer/journal/StateBuilder.h"
+
+void register_test_image_sync() {
+}
+
+namespace rbd {
+namespace mirror {
+
+namespace {
+
+int flush(librbd::ImageCtx *image_ctx) {
+ C_SaferCond ctx;
+ auto aio_comp = librbd::io::AioCompletion::create_and_start(
+ &ctx, image_ctx, librbd::io::AIO_TYPE_FLUSH);
+ auto req = librbd::io::ImageDispatchSpec::create_flush(
+ *image_ctx, librbd::io::IMAGE_DISPATCH_LAYER_INTERNAL_START, aio_comp,
+ librbd::io::FLUSH_SOURCE_INTERNAL, {});
+ req->send();
+ return ctx.wait();
+}
+
+void scribble(librbd::ImageCtx *image_ctx, int num_ops, uint64_t max_size)
+{
+ max_size = std::min<uint64_t>(image_ctx->size, max_size);
+ for (int i=0; i<num_ops; i++) {
+ uint64_t off = rand() % (image_ctx->size - max_size + 1);
+ uint64_t len = 1 + rand() % max_size;
+
+ if (rand() % 4 == 0) {
+ ASSERT_EQ((int)len,
+ librbd::api::Io<>::discard(
+ *image_ctx, off, len, image_ctx->discard_granularity_bytes));
+ } else {
+ bufferlist bl;
+ bl.append(std::string(len, '1'));
+ ASSERT_EQ((int)len, librbd::api::Io<>::write(
+ *image_ctx, off, len, std::move(bl), 0));
+ }
+ }
+
+ std::shared_lock owner_locker{image_ctx->owner_lock};
+ ASSERT_EQ(0, flush(image_ctx));
+}
+
+} // anonymous namespace
+class TestImageSync : public TestFixture {
+public:
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ create_and_open(m_local_io_ctx, &m_local_image_ctx);
+ create_and_open(m_remote_io_ctx, &m_remote_image_ctx);
+
+ auto cct = reinterpret_cast<CephContext*>(m_local_io_ctx.cct());
+ m_image_sync_throttler = rbd::mirror::Throttler<>::create(
+ cct, "rbd_mirror_concurrent_image_syncs");
+
+ m_instance_watcher = rbd::mirror::InstanceWatcher<>::create(
+ m_local_io_ctx, *m_threads->asio_engine, nullptr, m_image_sync_throttler);
+ m_instance_watcher->handle_acquire_leader();
+
+ ContextWQ* context_wq;
+ librbd::Journal<>::get_work_queue(cct, &context_wq);
+
+ m_remote_journaler = new ::journal::Journaler(
+ context_wq, m_threads->timer, &m_threads->timer_lock,
+ m_remote_io_ctx, m_remote_image_ctx->id, "mirror-uuid", {}, nullptr);
+
+ m_client_meta = {"image-id"};
+
+ librbd::journal::ClientData client_data(m_client_meta);
+ bufferlist client_data_bl;
+ encode(client_data, client_data_bl);
+
+ ASSERT_EQ(0, m_remote_journaler->register_client(client_data_bl));
+
+ m_state_builder = rbd::mirror::image_replayer::journal::StateBuilder<
+ librbd::ImageCtx>::create("global image id");
+ m_state_builder->remote_journaler = m_remote_journaler;
+ m_state_builder->remote_client_meta = m_client_meta;
+ m_sync_point_handler = m_state_builder->create_sync_point_handler();
+ }
+
+ void TearDown() override {
+ m_instance_watcher->handle_release_leader();
+
+ m_state_builder->remote_journaler = nullptr;
+ m_state_builder->destroy_sync_point_handler();
+ m_state_builder->destroy();
+
+ delete m_remote_journaler;
+ delete m_instance_watcher;
+ delete m_image_sync_throttler;
+
+ TestFixture::TearDown();
+ }
+
+ void create_and_open(librados::IoCtx &io_ctx, librbd::ImageCtx **image_ctx) {
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(io_ctx, m_image_name, image_ctx));
+
+ C_SaferCond ctx;
+ {
+ std::shared_lock owner_locker{(*image_ctx)->owner_lock};
+ (*image_ctx)->exclusive_lock->try_acquire_lock(&ctx);
+ }
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE((*image_ctx)->exclusive_lock->is_lock_owner());
+ }
+
+ ImageSync<> *create_request(Context *ctx) {
+ return new ImageSync<>(m_threads, m_local_image_ctx, m_remote_image_ctx,
+ "mirror-uuid", m_sync_point_handler,
+ m_instance_watcher, nullptr, ctx);
+ }
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ librbd::ImageCtx *m_local_image_ctx;
+ rbd::mirror::Throttler<> *m_image_sync_throttler;
+ rbd::mirror::InstanceWatcher<> *m_instance_watcher;
+ ::journal::Journaler *m_remote_journaler;
+ librbd::journal::MirrorPeerClientMeta m_client_meta;
+ rbd::mirror::image_replayer::journal::StateBuilder<librbd::ImageCtx>* m_state_builder = nullptr;
+ rbd::mirror::image_sync::SyncPointHandler* m_sync_point_handler = nullptr;
+};
+
+TEST_F(TestImageSync, Empty) {
+ C_SaferCond ctx;
+ ImageSync<> *request = create_request(&ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_EQ(0U, m_client_meta.sync_points.size());
+ ASSERT_EQ(0, m_remote_image_ctx->state->refresh());
+ ASSERT_EQ(0U, m_remote_image_ctx->snap_ids.size());
+ ASSERT_EQ(0, m_local_image_ctx->state->refresh());
+ ASSERT_EQ(1U, m_local_image_ctx->snap_ids.size()); // deleted on journal replay
+}
+
+TEST_F(TestImageSync, Simple) {
+ scribble(m_remote_image_ctx, 10, 102400);
+
+ C_SaferCond ctx;
+ ImageSync<> *request = create_request(&ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ int64_t object_size = std::min<int64_t>(
+ m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
+ bufferlist read_remote_bl;
+ read_remote_bl.append(std::string(object_size, '1'));
+ bufferlist read_local_bl;
+ read_local_bl.append(std::string(object_size, '1'));
+
+ for (uint64_t offset = 0; offset < m_remote_image_ctx->size;
+ offset += object_size) {
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_remote_image_ctx, offset, object_size,
+ librbd::io::ReadResult{&read_remote_bl}, 0));
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_local_image_ctx, offset, object_size,
+ librbd::io::ReadResult{&read_local_bl}, 0));
+ ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
+ }
+}
+
+TEST_F(TestImageSync, Resize) {
+ int64_t object_size = std::min<int64_t>(
+ m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
+
+ uint64_t off = 0;
+ uint64_t len = object_size / 10;
+
+ bufferlist bl;
+ bl.append(std::string(len, '1'));
+ ASSERT_EQ((int)len, librbd::api::Io<>::write(
+ *m_remote_image_ctx, off, len, std::move(bl), 0));
+ {
+ std::shared_lock owner_locker{m_remote_image_ctx->owner_lock};
+ ASSERT_EQ(0, flush(m_remote_image_ctx));
+ }
+
+ ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap", nullptr));
+
+ uint64_t size = object_size - 1;
+ librbd::NoOpProgressContext no_op_progress_ctx;
+ ASSERT_EQ(0, m_remote_image_ctx->operations->resize(size, true,
+ no_op_progress_ctx));
+
+ C_SaferCond ctx;
+ ImageSync<> *request = create_request(&ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ bufferlist read_remote_bl;
+ read_remote_bl.append(std::string(len, '\0'));
+ bufferlist read_local_bl;
+ read_local_bl.append(std::string(len, '\0'));
+
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_remote_image_ctx, off, len,
+ librbd::io::ReadResult{&read_remote_bl}, 0));
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_local_image_ctx, off, len,
+ librbd::io::ReadResult{&read_local_bl}, 0));
+
+ ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
+}
+
+TEST_F(TestImageSync, Discard) {
+ int64_t object_size = std::min<int64_t>(
+ m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
+
+ uint64_t off = 0;
+ uint64_t len = object_size / 10;
+
+ bufferlist bl;
+ bl.append(std::string(len, '1'));
+ ASSERT_EQ((int)len, librbd::api::Io<>::write(
+ *m_remote_image_ctx, off, len, std::move(bl), 0));
+ {
+ std::shared_lock owner_locker{m_remote_image_ctx->owner_lock};
+ ASSERT_EQ(0, flush(m_remote_image_ctx));
+ }
+
+ ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap", nullptr));
+
+ ASSERT_EQ((int)len - 2,
+ librbd::api::Io<>::discard(
+ *m_remote_image_ctx, off + 1, len - 2,
+ m_remote_image_ctx->discard_granularity_bytes));
+ {
+ std::shared_lock owner_locker{m_remote_image_ctx->owner_lock};
+ ASSERT_EQ(0, flush(m_remote_image_ctx));
+ }
+
+ C_SaferCond ctx;
+ ImageSync<> *request = create_request(&ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ bufferlist read_remote_bl;
+ read_remote_bl.append(std::string(object_size, '\0'));
+ bufferlist read_local_bl;
+ read_local_bl.append(std::string(object_size, '\0'));
+
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_remote_image_ctx, off, len,
+ librbd::io::ReadResult{&read_remote_bl}, 0));
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_local_image_ctx, off, len,
+ librbd::io::ReadResult{&read_local_bl}, 0));
+
+ ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
+}
+
+TEST_F(TestImageSync, SnapshotStress) {
+ std::list<std::string> snap_names;
+
+ const int num_snaps = 4;
+ for (int idx = 0; idx <= num_snaps; ++idx) {
+ scribble(m_remote_image_ctx, 10, 102400);
+
+ librbd::NoOpProgressContext no_op_progress_ctx;
+ uint64_t size = 1 + rand() % m_image_size;
+ ASSERT_EQ(0, m_remote_image_ctx->operations->resize(size, true,
+ no_op_progress_ctx));
+ ASSERT_EQ(0, m_remote_image_ctx->state->refresh());
+
+ if (idx < num_snaps) {
+ snap_names.push_back("snap" + stringify(idx + 1));
+ ASSERT_EQ(0, create_snap(m_remote_image_ctx, snap_names.back().c_str(),
+ nullptr));
+ } else {
+ snap_names.push_back("");
+ }
+ }
+
+ C_SaferCond ctx;
+ ImageSync<> *request = create_request(&ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ int64_t object_size = std::min<int64_t>(
+ m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
+ bufferlist read_remote_bl;
+ read_remote_bl.append(std::string(object_size, '1'));
+ bufferlist read_local_bl;
+ read_local_bl.append(std::string(object_size, '1'));
+
+ for (auto &snap_name : snap_names) {
+ uint64_t remote_snap_id;
+ {
+ std::shared_lock remote_image_locker{m_remote_image_ctx->image_lock};
+ remote_snap_id = m_remote_image_ctx->get_snap_id(
+ cls::rbd::UserSnapshotNamespace{}, snap_name);
+ }
+
+ uint64_t remote_size;
+ {
+ C_SaferCond ctx;
+ m_remote_image_ctx->state->snap_set(remote_snap_id, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ std::shared_lock remote_image_locker{m_remote_image_ctx->image_lock};
+ remote_size = m_remote_image_ctx->get_image_size(
+ m_remote_image_ctx->snap_id);
+ }
+
+ uint64_t local_snap_id;
+ {
+ std::shared_lock image_locker{m_local_image_ctx->image_lock};
+ local_snap_id = m_local_image_ctx->get_snap_id(
+ cls::rbd::UserSnapshotNamespace{}, snap_name);
+ }
+
+ uint64_t local_size;
+ {
+ C_SaferCond ctx;
+ m_local_image_ctx->state->snap_set(local_snap_id, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ std::shared_lock image_locker{m_local_image_ctx->image_lock};
+ local_size = m_local_image_ctx->get_image_size(
+ m_local_image_ctx->snap_id);
+ bool flags_set;
+ ASSERT_EQ(0, m_local_image_ctx->test_flags(m_local_image_ctx->snap_id,
+ RBD_FLAG_OBJECT_MAP_INVALID,
+ m_local_image_ctx->image_lock,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+ }
+
+ ASSERT_EQ(remote_size, local_size);
+
+ for (uint64_t offset = 0; offset < remote_size; offset += object_size) {
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_remote_image_ctx, offset, object_size,
+ librbd::io::ReadResult{&read_remote_bl}, 0));
+ ASSERT_LE(0, librbd::api::Io<>::read(
+ *m_local_image_ctx, offset, object_size,
+ librbd::io::ReadResult{&read_local_bl}, 0));
+ ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
+ }
+ }
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_InstanceWatcher.cc b/src/test/rbd_mirror/test_InstanceWatcher.cc
new file mode 100644
index 000000000..6b8176d8a
--- /dev/null
+++ b/src/test/rbd_mirror/test_InstanceWatcher.cc
@@ -0,0 +1,132 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/Utils.h"
+#include "librbd/internal.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "common/Cond.h"
+
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+using rbd::mirror::InstanceWatcher;
+
+void register_test_instance_watcher() {
+}
+
+class TestInstanceWatcher : public ::rbd::mirror::TestFixture {
+public:
+ std::string m_instance_id;
+ std::string m_oid;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ m_local_io_ctx.remove(RBD_MIRROR_LEADER);
+ EXPECT_EQ(0, m_local_io_ctx.create(RBD_MIRROR_LEADER, true));
+
+ m_instance_id = stringify(m_local_io_ctx.get_instance_id());
+ m_oid = RBD_MIRROR_INSTANCE_PREFIX + m_instance_id;
+ }
+
+ void get_instances(std::vector<std::string> *instance_ids) {
+ instance_ids->clear();
+ C_SaferCond on_get;
+ InstanceWatcher<>::get_instances(m_local_io_ctx, instance_ids, &on_get);
+ EXPECT_EQ(0, on_get.wait());
+ }
+};
+
+TEST_F(TestInstanceWatcher, InitShutdown)
+{
+ InstanceWatcher<> instance_watcher(m_local_io_ctx, *m_threads->asio_engine,
+ nullptr, nullptr, m_instance_id);
+ std::vector<std::string> instance_ids;
+ get_instances(&instance_ids);
+ ASSERT_EQ(0U, instance_ids.size());
+
+ uint64_t size;
+ ASSERT_EQ(-ENOENT, m_local_io_ctx.stat(m_oid, &size, nullptr));
+
+ // Init
+ ASSERT_EQ(0, instance_watcher.init());
+
+ get_instances(&instance_ids);
+ ASSERT_EQ(1U, instance_ids.size());
+ ASSERT_EQ(m_instance_id, instance_ids[0]);
+
+ ASSERT_EQ(0, m_local_io_ctx.stat(m_oid, &size, nullptr));
+ std::list<obj_watch_t> watchers;
+ ASSERT_EQ(0, m_local_io_ctx.list_watchers(m_oid, &watchers));
+ ASSERT_EQ(1U, watchers.size());
+ ASSERT_EQ(m_instance_id, stringify(watchers.begin()->watcher_id));
+
+ get_instances(&instance_ids);
+ ASSERT_EQ(1U, instance_ids.size());
+
+ // Shutdown
+ instance_watcher.shut_down();
+
+ ASSERT_EQ(-ENOENT, m_local_io_ctx.stat(m_oid, &size, nullptr));
+ get_instances(&instance_ids);
+ ASSERT_EQ(0U, instance_ids.size());
+}
+
+TEST_F(TestInstanceWatcher, Remove)
+{
+ std::string instance_id = "instance_id";
+ std::string oid = RBD_MIRROR_INSTANCE_PREFIX + instance_id;
+
+ std::vector<std::string> instance_ids;
+ get_instances(&instance_ids);
+ ASSERT_EQ(0U, instance_ids.size());
+
+ uint64_t size;
+ ASSERT_EQ(-ENOENT, m_local_io_ctx.stat(oid, &size, nullptr));
+
+ librados::Rados cluster;
+ librados::IoCtx io_ctx;
+ ASSERT_EQ("", connect_cluster_pp(cluster));
+ ASSERT_EQ(0, cluster.ioctx_create(_local_pool_name.c_str(), io_ctx));
+ InstanceWatcher<> instance_watcher(m_local_io_ctx, *m_threads->asio_engine,
+ nullptr, nullptr, "instance_id");
+ // Init
+ ASSERT_EQ(0, instance_watcher.init());
+
+ get_instances(&instance_ids);
+ ASSERT_EQ(1U, instance_ids.size());
+ ASSERT_EQ(instance_id, instance_ids[0]);
+
+ ASSERT_EQ(0, m_local_io_ctx.stat(oid, &size, nullptr));
+ std::list<obj_watch_t> watchers;
+ ASSERT_EQ(0, m_local_io_ctx.list_watchers(oid, &watchers));
+ ASSERT_EQ(1U, watchers.size());
+
+ // Remove
+ C_SaferCond on_remove;
+ InstanceWatcher<>::remove_instance(m_local_io_ctx, *m_threads->asio_engine,
+ "instance_id", &on_remove);
+ ASSERT_EQ(0, on_remove.wait());
+
+ ASSERT_EQ(-ENOENT, m_local_io_ctx.stat(oid, &size, nullptr));
+ get_instances(&instance_ids);
+ ASSERT_EQ(0U, instance_ids.size());
+
+ // Shutdown
+ instance_watcher.shut_down();
+
+ ASSERT_EQ(-ENOENT, m_local_io_ctx.stat(m_oid, &size, nullptr));
+ get_instances(&instance_ids);
+ ASSERT_EQ(0U, instance_ids.size());
+
+ // Remove NOENT
+ C_SaferCond on_remove_noent;
+ InstanceWatcher<>::remove_instance(m_local_io_ctx, *m_threads->asio_engine,
+ instance_id, &on_remove_noent);
+ ASSERT_EQ(0, on_remove_noent.wait());
+}
diff --git a/src/test/rbd_mirror/test_Instances.cc b/src/test/rbd_mirror/test_Instances.cc
new file mode 100644
index 000000000..4b189d903
--- /dev/null
+++ b/src/test/rbd_mirror/test_Instances.cc
@@ -0,0 +1,164 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "cls/rbd/cls_rbd_client.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/Instances.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "common/Cond.h"
+
+#include "test/librados/test.h"
+#include "gtest/gtest.h"
+#include <vector>
+
+using rbd::mirror::InstanceWatcher;
+using rbd::mirror::Instances;
+
+void register_test_instances() {
+}
+
+class TestInstances : public ::rbd::mirror::TestFixture {
+public:
+ struct Listener : public rbd::mirror::instances::Listener {
+ std::mutex lock;
+
+ struct Instance {
+ uint32_t count = 0;
+ std::set<std::string> ids;
+ C_SaferCond ctx;
+ };
+
+ Instance add;
+ Instance remove;
+
+ void handle(const InstanceIds& instance_ids, Instance* instance) {
+ std::unique_lock<std::mutex> locker(lock);
+ for (auto& instance_id : instance_ids) {
+ ceph_assert(instance->count > 0);
+ --instance->count;
+
+ instance->ids.insert(instance_id);
+ if (instance->count == 0) {
+ instance->ctx.complete(0);
+ }
+ }
+ }
+
+ void handle_added(const InstanceIds& instance_ids) override {
+ handle(instance_ids, &add);
+ }
+
+ void handle_removed(const InstanceIds& instance_ids) override {
+ handle(instance_ids, &remove);
+ }
+ };
+
+ virtual void SetUp() {
+ TestFixture::SetUp();
+ m_local_io_ctx.remove(RBD_MIRROR_LEADER);
+ EXPECT_EQ(0, m_local_io_ctx.create(RBD_MIRROR_LEADER, true));
+
+ m_instance_id = stringify(m_local_io_ctx.get_instance_id());
+ }
+
+ Listener m_listener;
+ std::string m_instance_id;
+};
+
+TEST_F(TestInstances, InitShutdown)
+{
+ m_listener.add.count = 1;
+ Instances<> instances(m_threads, m_local_io_ctx, m_instance_id, m_listener);
+
+ std::string instance_id = "instance_id";
+ ASSERT_EQ(0, librbd::cls_client::mirror_instances_add(&m_local_io_ctx,
+ instance_id));
+
+ C_SaferCond on_init;
+ instances.init(&on_init);
+ ASSERT_EQ(0, on_init.wait());
+
+ ASSERT_LT(0U, m_listener.add.count);
+ instances.unblock_listener();
+
+ ASSERT_EQ(0, m_listener.add.ctx.wait());
+ ASSERT_EQ(std::set<std::string>({instance_id}), m_listener.add.ids);
+
+ C_SaferCond on_shut_down;
+ instances.shut_down(&on_shut_down);
+ ASSERT_EQ(0, on_shut_down.wait());
+}
+
+TEST_F(TestInstances, InitEnoent)
+{
+ Instances<> instances(m_threads, m_local_io_ctx, m_instance_id, m_listener);
+
+ m_local_io_ctx.remove(RBD_MIRROR_LEADER);
+
+ C_SaferCond on_init;
+ instances.init(&on_init);
+ ASSERT_EQ(0, on_init.wait());
+
+ C_SaferCond on_shut_down;
+ instances.shut_down(&on_shut_down);
+ ASSERT_EQ(0, on_shut_down.wait());
+}
+
+TEST_F(TestInstances, NotifyRemove)
+{
+ // speed testing up a little
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_heartbeat_interval", "1"));
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_max_missed_heartbeats",
+ "2"));
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_max_acquire_attempts_before_break",
+ "0"));
+
+ m_listener.add.count = 2;
+ m_listener.remove.count = 1;
+ Instances<> instances(m_threads, m_local_io_ctx, m_instance_id, m_listener);
+
+ std::string instance_id1 = "instance_id1";
+ std::string instance_id2 = "instance_id2";
+
+ ASSERT_EQ(0, librbd::cls_client::mirror_instances_add(&m_local_io_ctx,
+ instance_id1));
+
+ C_SaferCond on_init;
+ instances.init(&on_init);
+ ASSERT_EQ(0, on_init.wait());
+
+ instances.acked({instance_id2});
+
+ ASSERT_LT(0U, m_listener.add.count);
+ instances.unblock_listener();
+
+ ASSERT_EQ(0, m_listener.add.ctx.wait());
+ ASSERT_EQ(std::set<std::string>({instance_id1, instance_id2}),
+ m_listener.add.ids);
+
+ std::vector<std::string> instance_ids;
+ for (int i = 0; i < 100; i++) {
+ instances.acked({instance_id1});
+ if (m_listener.remove.count > 0) {
+ usleep(250000);
+ }
+ }
+
+ instances.acked({instance_id1});
+ ASSERT_EQ(0, m_listener.remove.ctx.wait());
+ ASSERT_EQ(std::set<std::string>({instance_id2}),
+ m_listener.remove.ids);
+
+ C_SaferCond on_get;
+ instances.acked({instance_id1});
+ InstanceWatcher<>::get_instances(m_local_io_ctx, &instance_ids, &on_get);
+ EXPECT_EQ(0, on_get.wait());
+ EXPECT_EQ(1U, instance_ids.size());
+ ASSERT_EQ(instance_ids[0], instance_id1);
+
+ C_SaferCond on_shut_down;
+ instances.shut_down(&on_shut_down);
+ ASSERT_EQ(0, on_shut_down.wait());
+}
diff --git a/src/test/rbd_mirror/test_LeaderWatcher.cc b/src/test/rbd_mirror/test_LeaderWatcher.cc
new file mode 100644
index 000000000..ac8f1bb0a
--- /dev/null
+++ b/src/test/rbd_mirror/test_LeaderWatcher.cc
@@ -0,0 +1,318 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "librbd/internal.h"
+#include "librbd/Utils.h"
+#include "librbd/api/Mirror.h"
+#include "test/librbd/test_support.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "tools/rbd_mirror/LeaderWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "common/Cond.h"
+
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+using librbd::util::unique_lock_name;
+using rbd::mirror::LeaderWatcher;
+
+void register_test_leader_watcher() {
+}
+
+class TestLeaderWatcher : public ::rbd::mirror::TestFixture {
+public:
+ class Listener : public rbd::mirror::leader_watcher::Listener {
+ public:
+ Listener()
+ : m_test_lock(ceph::make_mutex(
+ unique_lock_name("LeaderWatcher::m_test_lock", this))) {
+ }
+
+ void on_acquire(int r, Context *ctx) {
+ std::lock_guard locker{m_test_lock};
+ m_on_acquire_r = r;
+ m_on_acquire = ctx;
+ }
+
+ void on_release(int r, Context *ctx) {
+ std::lock_guard locker{m_test_lock};
+ m_on_release_r = r;
+ m_on_release = ctx;
+ }
+
+ int acquire_count() const {
+ std::lock_guard locker{m_test_lock};
+ return m_acquire_count;
+ }
+
+ int release_count() const {
+ std::lock_guard locker{m_test_lock};
+ return m_release_count;
+ }
+
+ void post_acquire_handler(Context *on_finish) override {
+ std::lock_guard locker{m_test_lock};
+ m_acquire_count++;
+ on_finish->complete(m_on_acquire_r);
+ m_on_acquire_r = 0;
+ if (m_on_acquire != nullptr) {
+ m_on_acquire->complete(0);
+ m_on_acquire = nullptr;
+ }
+ }
+
+ void pre_release_handler(Context *on_finish) override {
+ std::lock_guard locker{m_test_lock};
+ m_release_count++;
+ on_finish->complete(m_on_release_r);
+ m_on_release_r = 0;
+ if (m_on_release != nullptr) {
+ m_on_release->complete(0);
+ m_on_release = nullptr;
+ }
+ }
+
+ void update_leader_handler(const std::string &leader_instance_id) override {
+ }
+
+ void handle_instances_added(const InstanceIds& instance_ids) override {
+ }
+ void handle_instances_removed(const InstanceIds& instance_ids) override {
+ }
+
+ private:
+ mutable ceph::mutex m_test_lock;
+ int m_acquire_count = 0;
+ int m_release_count = 0;
+ int m_on_acquire_r = 0;
+ int m_on_release_r = 0;
+ Context *m_on_acquire = nullptr;
+ Context *m_on_release = nullptr;
+ };
+
+ struct Connection {
+ librados::Rados cluster;
+ librados::IoCtx io_ctx;
+ };
+
+ std::list<std::unique_ptr<Connection> > m_connections;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_local_io_ctx,
+ RBD_MIRROR_MODE_POOL));
+
+ if (is_librados_test_stub(*_rados)) {
+ // speed testing up a little
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_heartbeat_interval",
+ "1"));
+ }
+ }
+
+ librados::IoCtx &create_connection(bool no_heartbeats = false) {
+ m_connections.push_back(std::unique_ptr<Connection>(new Connection()));
+ Connection *c = m_connections.back().get();
+
+ EXPECT_EQ("", connect_cluster_pp(c->cluster));
+ if (no_heartbeats) {
+ EXPECT_EQ(0, c->cluster.conf_set("rbd_mirror_leader_heartbeat_interval",
+ "3600"));
+ } else if (is_librados_test_stub(*_rados)) {
+ EXPECT_EQ(0, c->cluster.conf_set("rbd_mirror_leader_heartbeat_interval",
+ "1"));
+ }
+ EXPECT_EQ(0, c->cluster.ioctx_create(_local_pool_name.c_str(), c->io_ctx));
+
+ return c->io_ctx;
+ }
+};
+
+TEST_F(TestLeaderWatcher, InitShutdown)
+{
+ Listener listener;
+ LeaderWatcher<> leader_watcher(m_threads, m_local_io_ctx, &listener);
+
+ C_SaferCond on_init_acquire;
+ listener.on_acquire(0, &on_init_acquire);
+ ASSERT_EQ(0, leader_watcher.init());
+ ASSERT_EQ(0, on_init_acquire.wait());
+ ASSERT_TRUE(leader_watcher.is_leader());
+
+ leader_watcher.shut_down();
+ ASSERT_EQ(1, listener.acquire_count());
+ ASSERT_EQ(1, listener.release_count());
+ ASSERT_FALSE(leader_watcher.is_leader());
+}
+
+TEST_F(TestLeaderWatcher, Release)
+{
+ Listener listener;
+ LeaderWatcher<> leader_watcher(m_threads, m_local_io_ctx, &listener);
+
+ C_SaferCond on_init_acquire;
+ listener.on_acquire(0, &on_init_acquire);
+ ASSERT_EQ(0, leader_watcher.init());
+ ASSERT_EQ(0, on_init_acquire.wait());
+ ASSERT_TRUE(leader_watcher.is_leader());
+
+ C_SaferCond on_release;
+ C_SaferCond on_acquire;
+ listener.on_release(0, &on_release);
+ listener.on_acquire(0, &on_acquire);
+ leader_watcher.release_leader();
+ ASSERT_EQ(0, on_release.wait());
+ ASSERT_FALSE(leader_watcher.is_leader());
+
+ // wait for lock re-acquired due to no another locker
+ ASSERT_EQ(0, on_acquire.wait());
+ ASSERT_TRUE(leader_watcher.is_leader());
+
+ C_SaferCond on_release2;
+ listener.on_release(0, &on_release2);
+ leader_watcher.release_leader();
+ ASSERT_EQ(0, on_release2.wait());
+
+ leader_watcher.shut_down();
+ ASSERT_EQ(2, listener.acquire_count());
+ ASSERT_EQ(2, listener.release_count());
+}
+
+TEST_F(TestLeaderWatcher, ListenerError)
+{
+ Listener listener;
+ LeaderWatcher<> leader_watcher(m_threads, m_local_io_ctx, &listener);
+
+ // make listener return error on acquire
+ C_SaferCond on_init_acquire, on_init_release;
+ listener.on_acquire(-EINVAL, &on_init_acquire);
+ listener.on_release(0, &on_init_release);
+ ASSERT_EQ(0, leader_watcher.init());
+ ASSERT_EQ(0, on_init_acquire.wait());
+ ASSERT_EQ(0, on_init_release.wait());
+ ASSERT_FALSE(leader_watcher.is_leader());
+
+ // wait for lock re-acquired due to no another locker
+ C_SaferCond on_acquire;
+ listener.on_acquire(0, &on_acquire);
+ ASSERT_EQ(0, on_acquire.wait());
+ ASSERT_TRUE(leader_watcher.is_leader());
+
+ // make listener return error on release
+ C_SaferCond on_release;
+ listener.on_release(-EINVAL, &on_release);
+ leader_watcher.release_leader();
+ ASSERT_EQ(0, on_release.wait());
+ ASSERT_FALSE(leader_watcher.is_leader());
+
+ leader_watcher.shut_down();
+ ASSERT_EQ(2, listener.acquire_count());
+ ASSERT_EQ(2, listener.release_count());
+ ASSERT_FALSE(leader_watcher.is_leader());
+}
+
+TEST_F(TestLeaderWatcher, Two)
+{
+ Listener listener1;
+ LeaderWatcher<> leader_watcher1(m_threads, create_connection(), &listener1);
+
+ C_SaferCond on_init_acquire;
+ listener1.on_acquire(0, &on_init_acquire);
+ ASSERT_EQ(0, leader_watcher1.init());
+ ASSERT_EQ(0, on_init_acquire.wait());
+
+ Listener listener2;
+ LeaderWatcher<> leader_watcher2(m_threads, create_connection(), &listener2);
+
+ ASSERT_EQ(0, leader_watcher2.init());
+ ASSERT_TRUE(leader_watcher1.is_leader());
+ ASSERT_FALSE(leader_watcher2.is_leader());
+
+ C_SaferCond on_release;
+ C_SaferCond on_acquire;
+ listener1.on_release(0, &on_release);
+ listener2.on_acquire(0, &on_acquire);
+ leader_watcher1.release_leader();
+ ASSERT_EQ(0, on_release.wait());
+ ASSERT_FALSE(leader_watcher1.is_leader());
+
+ // wait for lock acquired by another watcher
+ ASSERT_EQ(0, on_acquire.wait());
+ ASSERT_TRUE(leader_watcher2.is_leader());
+
+ leader_watcher1.shut_down();
+ leader_watcher2.shut_down();
+
+ ASSERT_EQ(1, listener1.acquire_count());
+ ASSERT_EQ(1, listener1.release_count());
+ ASSERT_EQ(1, listener2.acquire_count());
+ ASSERT_EQ(1, listener2.release_count());
+}
+
+TEST_F(TestLeaderWatcher, Break)
+{
+ Listener listener1, listener2;
+ LeaderWatcher<> leader_watcher1(m_threads,
+ create_connection(true /* no heartbeats */),
+ &listener1);
+ LeaderWatcher<> leader_watcher2(m_threads, create_connection(), &listener2);
+
+ C_SaferCond on_init_acquire;
+ listener1.on_acquire(0, &on_init_acquire);
+ ASSERT_EQ(0, leader_watcher1.init());
+ ASSERT_EQ(0, on_init_acquire.wait());
+
+ C_SaferCond on_acquire;
+ listener2.on_acquire(0, &on_acquire);
+ ASSERT_EQ(0, leader_watcher2.init());
+ ASSERT_FALSE(leader_watcher2.is_leader());
+
+ // wait for lock broken due to no heartbeats and re-acquired
+ ASSERT_EQ(0, on_acquire.wait());
+ ASSERT_TRUE(leader_watcher2.is_leader());
+
+ leader_watcher1.shut_down();
+ leader_watcher2.shut_down();
+}
+
+TEST_F(TestLeaderWatcher, Stress)
+{
+ const int WATCHERS_COUNT = 20;
+ std::list<LeaderWatcher<> *> leader_watchers;
+ Listener listener;
+
+ for (int i = 0; i < WATCHERS_COUNT; i++) {
+ auto leader_watcher =
+ new LeaderWatcher<>(m_threads, create_connection(), &listener);
+ leader_watchers.push_back(leader_watcher);
+ }
+
+ C_SaferCond on_init_acquire;
+ listener.on_acquire(0, &on_init_acquire);
+ for (auto &leader_watcher : leader_watchers) {
+ ASSERT_EQ(0, leader_watcher->init());
+ }
+ ASSERT_EQ(0, on_init_acquire.wait());
+
+ while (true) {
+ C_SaferCond on_acquire;
+ listener.on_acquire(0, &on_acquire);
+ std::unique_ptr<LeaderWatcher<> > leader_watcher;
+ for (auto it = leader_watchers.begin(); it != leader_watchers.end(); ) {
+ if ((*it)->is_leader()) {
+ ASSERT_FALSE(leader_watcher);
+ leader_watcher.reset(*it);
+ it = leader_watchers.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ ASSERT_TRUE(leader_watcher);
+ leader_watcher->shut_down();
+ if (leader_watchers.empty()) {
+ break;
+ }
+ ASSERT_EQ(0, on_acquire.wait());
+ }
+}
diff --git a/src/test/rbd_mirror/test_PoolWatcher.cc b/src/test/rbd_mirror/test_PoolWatcher.cc
new file mode 100644
index 000000000..351bba217
--- /dev/null
+++ b/src/test/rbd_mirror/test_PoolWatcher.cc
@@ -0,0 +1,256 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/rados/librados.hpp"
+#include "include/rbd/librbd.hpp"
+#include "include/stringify.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "include/rbd_types.h"
+#include "librbd/internal.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/Utils.h"
+#include "librbd/api/Mirror.h"
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "common/ceph_mutex.h"
+#include "tools/rbd_mirror/PoolWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/Types.h"
+#include "tools/rbd_mirror/pool_watcher/Types.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+#include <boost/scope_exit.hpp>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+using namespace std::chrono_literals;
+
+using rbd::mirror::ImageId;
+using rbd::mirror::ImageIds;
+using rbd::mirror::PoolWatcher;
+using rbd::mirror::PeerSpec;
+using rbd::mirror::RadosRef;
+using std::map;
+using std::set;
+using std::string;
+
+void register_test_pool_watcher() {
+}
+
+class TestPoolWatcher : public ::rbd::mirror::TestFixture {
+public:
+
+ TestPoolWatcher()
+ : m_pool_watcher_listener(this),
+ m_image_number(0), m_snap_number(0)
+ {
+ m_cluster = std::make_shared<librados::Rados>();
+ EXPECT_EQ("", connect_cluster_pp(*m_cluster));
+ }
+
+ void TearDown() override {
+ if (m_pool_watcher) {
+ C_SaferCond ctx;
+ m_pool_watcher->shut_down(&ctx);
+ EXPECT_EQ(0, ctx.wait());
+ }
+
+ m_cluster->wait_for_latest_osdmap();
+ for (auto& pool : m_pools) {
+ EXPECT_EQ(0, m_cluster->pool_delete(pool.c_str()));
+ }
+
+ TestFixture::TearDown();
+ }
+
+ struct PoolWatcherListener : public rbd::mirror::pool_watcher::Listener {
+ TestPoolWatcher *test;
+ ceph::condition_variable cond;
+ ImageIds image_ids;
+
+ explicit PoolWatcherListener(TestPoolWatcher *test) : test(test) {
+ }
+
+ void handle_update(const std::string &mirror_uuid,
+ ImageIds &&added_image_ids,
+ ImageIds &&removed_image_ids) override {
+ std::lock_guard locker{test->m_lock};
+ for (auto &image_id : removed_image_ids) {
+ image_ids.erase(image_id);
+ }
+ image_ids.insert(added_image_ids.begin(), added_image_ids.end());
+ cond.notify_all();
+ }
+ };
+
+ void create_pool(bool enable_mirroring, const PeerSpec &peer, string *name=nullptr) {
+ string pool_name = get_temp_pool_name("test-rbd-mirror-");
+ ASSERT_EQ(0, m_cluster->pool_create(pool_name.c_str()));
+
+ int64_t pool_id = m_cluster->pool_lookup(pool_name.c_str());
+ ASSERT_GE(pool_id, 0);
+ m_pools.insert(pool_name);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, m_cluster->ioctx_create2(pool_id, ioctx));
+ ioctx.application_enable("rbd", true);
+
+ m_pool_watcher.reset(new PoolWatcher<>(m_threads, ioctx, "mirror uuid",
+ m_pool_watcher_listener));
+
+ if (enable_mirroring) {
+ ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(ioctx,
+ RBD_MIRROR_MODE_POOL));
+ std::string uuid;
+ ASSERT_EQ(0, librbd::api::Mirror<>::peer_site_add(
+ ioctx, &uuid, RBD_MIRROR_PEER_DIRECTION_RX_TX, peer.cluster_name,
+ peer.client_name));
+ }
+ if (name != nullptr) {
+ *name = pool_name;
+ }
+
+ m_pool_watcher->init();
+ }
+
+ string get_image_id(librados::IoCtx *ioctx, const string &image_name) {
+ string obj = librbd::util::id_obj_name(image_name);
+ string id;
+ EXPECT_EQ(0, librbd::cls_client::get_id(ioctx, obj, &id));
+ return id;
+ }
+
+ void create_image(const string &pool_name, bool mirrored=true,
+ string *image_name=nullptr) {
+ uint64_t features = librbd::util::get_rbd_default_features(g_ceph_context);
+ string name = "image" + stringify(++m_image_number);
+ if (mirrored) {
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ }
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, m_cluster->ioctx_create(pool_name.c_str(), ioctx));
+ int order = 0;
+ ASSERT_EQ(0, librbd::create(ioctx, name.c_str(), 1 << 22, false,
+ features, &order, 0, 0));
+ if (mirrored) {
+ librbd::Image image;
+ librbd::RBD rbd;
+ rbd.open(ioctx, image, name.c_str());
+ image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_JOURNAL);
+
+ librbd::mirror_image_info_t mirror_image_info;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image_info,
+ sizeof(mirror_image_info)));
+ image.close();
+
+ m_mirrored_images.insert(ImageId(
+ mirror_image_info.global_id, get_image_id(&ioctx, name)));
+ }
+ if (image_name != nullptr)
+ *image_name = name;
+ }
+
+ void clone_image(const string &parent_pool_name,
+ const string &parent_image_name,
+ const string &clone_pool_name,
+ bool mirrored=true,
+ string *image_name=nullptr) {
+ librados::IoCtx pioctx, cioctx;
+ ASSERT_EQ(0, m_cluster->ioctx_create(parent_pool_name.c_str(), pioctx));
+ ASSERT_EQ(0, m_cluster->ioctx_create(clone_pool_name.c_str(), cioctx));
+
+ string snap_name = "snap" + stringify(++m_snap_number);
+ {
+ librbd::ImageCtx *ictx = new librbd::ImageCtx(parent_image_name.c_str(),
+ "", "", pioctx, false);
+ ictx->state->open(0);
+ librbd::NoOpProgressContext prog_ctx;
+ EXPECT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ snap_name, 0, prog_ctx));
+ EXPECT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
+ snap_name));
+ ictx->state->close();
+ }
+
+ uint64_t features = librbd::util::get_rbd_default_features(g_ceph_context);
+ string name = "clone" + stringify(++m_image_number);
+ if (mirrored) {
+ features |= RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ }
+ int order = 0;
+ librbd::clone(pioctx, parent_image_name.c_str(), snap_name.c_str(),
+ cioctx, name.c_str(), features, &order, 0, 0);
+ if (mirrored) {
+ librbd::Image image;
+ librbd::RBD rbd;
+ rbd.open(cioctx, image, name.c_str());
+ image.mirror_image_enable2(RBD_MIRROR_IMAGE_MODE_JOURNAL);
+
+ librbd::mirror_image_info_t mirror_image_info;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image_info,
+ sizeof(mirror_image_info)));
+ image.close();
+
+ m_mirrored_images.insert(ImageId(
+ mirror_image_info.global_id, get_image_id(&cioctx, name)));
+ }
+ if (image_name != nullptr)
+ *image_name = name;
+ }
+
+ void check_images() {
+ std::unique_lock l{m_lock};
+ while (m_mirrored_images != m_pool_watcher_listener.image_ids) {
+ if (m_pool_watcher_listener.cond.wait_for(l, 10s) == std::cv_status::timeout) {
+ break;
+ }
+ }
+
+ ASSERT_EQ(m_mirrored_images, m_pool_watcher_listener.image_ids);
+ }
+
+ ceph::mutex m_lock = ceph::make_mutex("TestPoolWatcherLock");
+ RadosRef m_cluster;
+ PoolWatcherListener m_pool_watcher_listener;
+ std::unique_ptr<PoolWatcher<> > m_pool_watcher;
+
+ set<string> m_pools;
+ ImageIds m_mirrored_images;
+
+ uint64_t m_image_number;
+ uint64_t m_snap_number;
+};
+
+TEST_F(TestPoolWatcher, EmptyPool) {
+ string uuid1 = "00000000-0000-0000-0000-000000000001";
+ PeerSpec site1(uuid1, "site1", "mirror1");
+ create_pool(true, site1);
+ check_images();
+}
+
+TEST_F(TestPoolWatcher, ReplicatedPools) {
+ string uuid1 = "00000000-0000-0000-0000-000000000001";
+ PeerSpec site1(uuid1, "site1", "mirror1");
+ string first_pool, local_pool, last_pool;
+ create_pool(true, site1, &first_pool);
+ check_images();
+ create_image(first_pool);
+ check_images();
+ string parent_image, parent_image2;
+ create_image(first_pool, true, &parent_image);
+ check_images();
+ clone_image(first_pool, parent_image, first_pool);
+ check_images();
+ clone_image(first_pool, parent_image, first_pool, true, &parent_image2);
+ check_images();
+ create_image(first_pool, false);
+ check_images();
+}
diff --git a/src/test/rbd_mirror/test_fixture.cc b/src/test/rbd_mirror/test_fixture.cc
new file mode 100644
index 000000000..f2fd3551f
--- /dev/null
+++ b/src/test/rbd_mirror/test_fixture.cc
@@ -0,0 +1,161 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/rbd/cls_rbd_types.h"
+#include "test/rbd_mirror/test_fixture.h"
+#include "include/stringify.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/internal.h"
+#include "test/librados/test_cxx.h"
+#include "tools/rbd_mirror/Threads.h"
+
+namespace rbd {
+namespace mirror {
+
+std::string TestFixture::_local_pool_name;
+std::string TestFixture::_remote_pool_name;
+std::shared_ptr<librados::Rados> TestFixture::_rados;
+uint64_t TestFixture::_image_number = 0;
+std::string TestFixture::_data_pool;
+
+TestFixture::TestFixture() {
+}
+
+void TestFixture::SetUpTestCase() {
+ _rados = std::shared_ptr<librados::Rados>(new librados::Rados());
+ ASSERT_EQ("", connect_cluster_pp(*_rados.get()));
+ ASSERT_EQ(0, _rados->conf_set("rbd_cache", "false"));
+
+ _local_pool_name = get_temp_pool_name("test-rbd-mirror-");
+ ASSERT_EQ(0, _rados->pool_create(_local_pool_name.c_str()));
+
+ librados::IoCtx local_ioctx;
+ ASSERT_EQ(0, _rados->ioctx_create(_local_pool_name.c_str(), local_ioctx));
+ local_ioctx.application_enable("rbd", true);
+
+ _remote_pool_name = get_temp_pool_name("test-rbd-mirror-");
+ ASSERT_EQ(0, _rados->pool_create(_remote_pool_name.c_str()));
+
+ librados::IoCtx remote_ioctx;
+ ASSERT_EQ(0, _rados->ioctx_create(_remote_pool_name.c_str(), remote_ioctx));
+ remote_ioctx.application_enable("rbd", true);
+
+ ASSERT_EQ(0, create_image_data_pool(_data_pool));
+ if (!_data_pool.empty()) {
+ printf("using image data pool: %s\n", _data_pool.c_str());
+ }
+}
+
+void TestFixture::TearDownTestCase() {
+ if (!_data_pool.empty()) {
+ ASSERT_EQ(0, _rados->pool_delete(_data_pool.c_str()));
+ }
+
+ ASSERT_EQ(0, _rados->pool_delete(_remote_pool_name.c_str()));
+ ASSERT_EQ(0, _rados->pool_delete(_local_pool_name.c_str()));
+ _rados->shutdown();
+}
+
+void TestFixture::SetUp() {
+ static bool seeded = false;
+ if (!seeded) {
+ seeded = true;
+ int seed = getpid();
+ std::cout << "seed " << seed << std::endl;
+ srand(seed);
+ }
+
+ ASSERT_EQ(0, _rados->ioctx_create(_local_pool_name.c_str(), m_local_io_ctx));
+ ASSERT_EQ(0, _rados->ioctx_create(_remote_pool_name.c_str(), m_remote_io_ctx));
+ m_image_name = get_temp_image_name();
+
+ m_threads = new rbd::mirror::Threads<>(_rados);
+}
+
+void TestFixture::TearDown() {
+ for (auto image_ctx : m_image_ctxs) {
+ image_ctx->state->close();
+ }
+
+ m_remote_io_ctx.close();
+ m_local_io_ctx.close();
+
+ delete m_threads;
+}
+
+int TestFixture::create_image(librbd::RBD &rbd, librados::IoCtx &ioctx,
+ const std::string &name, uint64_t size) {
+ int order = 18;
+ return rbd.create2(ioctx, name.c_str(), size, RBD_FEATURES_ALL, &order);
+}
+
+int TestFixture::open_image(librados::IoCtx &io_ctx,
+ const std::string &image_name,
+ librbd::ImageCtx **image_ctx) {
+ *image_ctx = new librbd::ImageCtx(image_name.c_str(), "", nullptr, io_ctx,
+ false);
+ m_image_ctxs.insert(*image_ctx);
+ return (*image_ctx)->state->open(0);
+}
+
+int TestFixture::create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
+ librados::snap_t *snap_id) {
+ librbd::NoOpProgressContext prog_ctx;
+ int r = image_ctx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
+ snap_name, 0, prog_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ r = image_ctx->state->refresh();
+ if (r < 0) {
+ return r;
+ }
+
+ if (image_ctx->snap_ids.count({cls::rbd::UserSnapshotNamespace(),
+ snap_name}) == 0) {
+ return -ENOENT;
+ }
+
+ if (snap_id != nullptr) {
+ *snap_id = image_ctx->snap_ids[{cls::rbd::UserSnapshotNamespace(),
+ snap_name}];
+ }
+ return 0;
+}
+
+std::string TestFixture::get_temp_image_name() {
+ ++_image_number;
+ return "image" + stringify(_image_number);
+}
+
+int TestFixture::create_image_data_pool(std::string &data_pool) {
+ std::string pool;
+ int r = _rados->conf_get("rbd_default_data_pool", pool);
+ if (r != 0) {
+ return r;
+ } else if (pool.empty()) {
+ return 0;
+ }
+
+ r = _rados->pool_create(pool.c_str());
+ if (r < 0) {
+ return r;
+ }
+
+ librados::IoCtx data_ioctx;
+ r = _rados->ioctx_create(pool.c_str(), data_ioctx);
+ if (r < 0) {
+ return r;
+ }
+
+ data_ioctx.application_enable("rbd", true);
+ data_pool = pool;
+ return 0;
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_fixture.h b/src/test/rbd_mirror/test_fixture.h
new file mode 100644
index 000000000..217ae8102
--- /dev/null
+++ b/src/test/rbd_mirror/test_fixture.h
@@ -0,0 +1,65 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_RBD_MIRROR_TEST_FIXTURE_H
+#define CEPH_TEST_RBD_MIRROR_TEST_FIXTURE_H
+
+#include "include/int_types.h"
+#include "include/rados/librados.hpp"
+#include <gtest/gtest.h>
+#include <memory>
+#include <set>
+
+namespace librbd {
+class ImageCtx;
+class RBD;
+}
+
+namespace rbd {
+namespace mirror {
+
+template <typename> class Threads;
+
+class TestFixture : public ::testing::Test {
+public:
+ TestFixture();
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ void SetUp() override;
+ void TearDown() override;
+
+ librados::IoCtx m_local_io_ctx;
+ librados::IoCtx m_remote_io_ctx;
+
+ std::string m_image_name;
+ uint64_t m_image_size = 1 << 24;
+
+ std::set<librbd::ImageCtx *> m_image_ctxs;
+
+ Threads<librbd::ImageCtx> *m_threads = nullptr;
+
+
+ int create_image(librbd::RBD &rbd, librados::IoCtx &ioctx,
+ const std::string &name, uint64_t size);
+ int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
+ librbd::ImageCtx **image_ctx);
+
+ int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
+ librados::snap_t *snap_id = nullptr);
+
+ static std::string get_temp_image_name();
+ static int create_image_data_pool(std::string &data_pool);
+
+ static std::string _local_pool_name;
+ static std::string _remote_pool_name;
+ static std::shared_ptr<librados::Rados> _rados;
+ static uint64_t _image_number;
+ static std::string _data_pool;
+};
+
+} // namespace mirror
+} // namespace rbd
+
+#endif // CEPH_TEST_RBD_MIRROR_TEST_FIXTURE_H
diff --git a/src/test/rbd_mirror/test_main.cc b/src/test/rbd_mirror/test_main.cc
new file mode 100644
index 000000000..ed6641e2e
--- /dev/null
+++ b/src/test/rbd_mirror/test_main.cc
@@ -0,0 +1,53 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/perf_counters.h"
+#include "include/rados/librados.hpp"
+#include "global/global_context.h"
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+#include <iostream>
+#include <string>
+
+PerfCounters *g_journal_perf_counters = nullptr;
+PerfCounters *g_snapshot_perf_counters = nullptr;
+
+extern void register_test_cluster_watcher();
+extern void register_test_image_policy();
+extern void register_test_image_sync();
+extern void register_test_instance_watcher();
+extern void register_test_instances();
+extern void register_test_leader_watcher();
+extern void register_test_pool_watcher();
+extern void register_test_rbd_mirror();
+extern void register_test_rbd_mirror_image_deleter();
+
+int main(int argc, char **argv)
+{
+ register_test_cluster_watcher();
+ register_test_image_policy();
+ register_test_image_sync();
+ register_test_instance_watcher();
+ register_test_instances();
+ register_test_leader_watcher();
+ register_test_pool_watcher();
+ register_test_rbd_mirror();
+ register_test_rbd_mirror_image_deleter();
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ librados::Rados rados;
+ std::string result = connect_cluster_pp(rados);
+ if (result != "" ) {
+ std::cerr << result << std::endl;
+ return 1;
+ }
+
+ g_ceph_context = reinterpret_cast<CephContext*>(rados.cct());
+
+ int r = rados.conf_set("lockdep", "true");
+ if (r < 0) {
+ std::cerr << "warning: failed to enable lockdep" << std::endl;
+ }
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/rbd_mirror/test_mock_ImageMap.cc b/src/test/rbd_mirror/test_mock_ImageMap.cc
new file mode 100644
index 000000000..ac4ddb792
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_ImageMap.cc
@@ -0,0 +1,1587 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include "librbd/MirroringWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/ImageMap.h"
+#include "tools/rbd_mirror/image_map/LoadRequest.h"
+#include "tools/rbd_mirror/image_map/UpdateRequest.h"
+#include "tools/rbd_mirror/image_map/Types.h"
+#include "include/stringify.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+namespace image_map {
+
+template <>
+struct LoadRequest<librbd::MockTestImageCtx> {
+ std::map<std::string, cls::rbd::MirrorImageMap> *image_map;
+ Context *on_finish = nullptr;
+
+ static LoadRequest *s_instance;
+ static LoadRequest *create(librados::IoCtx &ioctx,
+ std::map<std::string, cls::rbd::MirrorImageMap> *image_map,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_map = image_map;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ LoadRequest() {
+ s_instance = this;
+ }
+};
+
+template <>
+struct UpdateRequest<librbd::MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static UpdateRequest *s_instance;
+ static UpdateRequest *create(librados::IoCtx &ioctx,
+ std::map<std::string, cls::rbd::MirrorImageMap> &&update_mapping,
+ std::set<std::string> &&global_image_ids,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ UpdateRequest() {
+ s_instance = this;
+ }
+};
+
+LoadRequest<librbd::MockTestImageCtx> *
+LoadRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+UpdateRequest<librbd::MockTestImageCtx> *
+UpdateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image_map
+
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/ImageMap.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::WithArg;
+using ::testing::AtLeast;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::ReturnArg;
+using ::testing::StrEq;
+
+using image_map::Listener;
+using image_map::LoadRequest;
+using image_map::UpdateRequest;
+
+using ::rbd::mirror::Threads;
+
+class TestMockImageMap : public TestMockFixture {
+public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef ImageMap<librbd::MockTestImageCtx> MockImageMap;
+ typedef LoadRequest<librbd::MockTestImageCtx> MockLoadRequest;
+ typedef UpdateRequest<librbd::MockTestImageCtx> MockUpdateRequest;
+
+ struct MockListener : Listener {
+ TestMockImageMap *test_mock_image_map;
+
+ MockListener(TestMockImageMap *test_mock_image_map)
+ : test_mock_image_map(test_mock_image_map) {
+ }
+
+ MOCK_METHOD2(mock_acquire_image, void(const std::string &, Context*));
+ MOCK_METHOD2(mock_release_image, void(const std::string &, Context*));
+ MOCK_METHOD3(mock_remove_image, void(const std::string &,
+ const std::string &, Context*));
+
+ void acquire_image(const std::string &global_image_id,
+ const std::string &instance_id, Context* on_finish) {
+ mock_acquire_image(global_image_id, on_finish);
+ }
+
+ void release_image(const std::string &global_image_id,
+ const std::string &instance_id, Context* on_finish) {
+ mock_release_image(global_image_id, on_finish);
+ }
+
+ void remove_image(const std::string &mirror_uuid,
+ const std::string &global_image_id,
+ const std::string &instance_id, Context* on_finish) {
+ mock_remove_image(mirror_uuid, global_image_id, on_finish);
+ }
+ };
+
+ TestMockImageMap() = default;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ m_local_instance_id = stringify(m_local_io_ctx.get_instance_id());
+
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_image_policy_migration_throttle",
+ "0"));
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_image_policy_type", "simple"));
+ }
+
+ void TearDown() override {
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_image_policy_type", "none"));
+
+ TestFixture::TearDown();
+ }
+
+ void expect_work_queue(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_add_event(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_,_))
+ .WillOnce(DoAll(WithArg<1>(Invoke([this](Context *ctx) {
+ auto wrapped_ctx = new LambdaContext([this, ctx](int r) {
+ std::lock_guard timer_locker{m_threads->timer_lock};
+ ctx->complete(r);
+ });
+ m_threads->work_queue->queue(wrapped_ctx, 0);
+ })), ReturnArg<1>()));
+ }
+
+ void expect_rebalance_event(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_,_))
+ .WillOnce(DoAll(WithArg<1>(Invoke([this](Context *ctx) {
+ // disable rebalance so as to not reschedule it again
+ CephContext *cct = reinterpret_cast<CephContext *>(m_local_io_ctx.cct());
+ cct->_conf.set_val("rbd_mirror_image_policy_rebalance_timeout", "0");
+
+ auto wrapped_ctx = new LambdaContext([this, ctx](int r) {
+ std::lock_guard timer_locker{m_threads->timer_lock};
+ ctx->complete(r);
+ });
+ m_threads->work_queue->queue(wrapped_ctx, 0);
+ })), ReturnArg<1>()));
+ }
+
+ void expect_load_request(MockLoadRequest &request, int r) {
+ EXPECT_CALL(request, send())
+ .WillOnce(Invoke([&request, r]() {
+ request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_update_request(MockUpdateRequest &request, int r) {
+ EXPECT_CALL(request, send())
+ .WillOnce(Invoke([this, &request, r]() {
+ request.on_finish->complete(r);
+ if (r == 0) {
+ std::lock_guard locker{m_lock};
+ ++m_map_update_count;
+ m_cond.notify_all();
+ }
+ }));
+ }
+
+ void expect_listener_acquire_image(MockListener &mock_listener,
+ const std::string &global_image_id,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ EXPECT_CALL(mock_listener, mock_acquire_image(global_image_id, _))
+ .WillOnce(WithArg<1>(Invoke([this, global_image_id, peer_ack_ctxs](Context* ctx) {
+ std::lock_guard locker{m_lock};
+ peer_ack_ctxs->insert({global_image_id, ctx});
+ ++m_notify_update_count;
+ m_cond.notify_all();
+ })));
+ }
+
+ void expect_listener_release_image(MockListener &mock_listener,
+ const std::string &global_image_id,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ EXPECT_CALL(mock_listener, mock_release_image(global_image_id, _))
+ .WillOnce(WithArg<1>(Invoke([this, global_image_id, peer_ack_ctxs](Context* ctx) {
+ std::lock_guard locker{m_lock};
+ peer_ack_ctxs->insert({global_image_id, ctx});
+ ++m_notify_update_count;
+ m_cond.notify_all();
+ })));
+ }
+
+ void expect_listener_remove_image(MockListener &mock_listener,
+ const std::string &mirror_uuid,
+ const std::string &global_image_id,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ EXPECT_CALL(mock_listener,
+ mock_remove_image(mirror_uuid, global_image_id, _))
+ .WillOnce(WithArg<2>(Invoke([this, global_image_id, peer_ack_ctxs](Context* ctx) {
+ std::lock_guard locker{m_lock};
+ peer_ack_ctxs->insert({global_image_id, ctx});
+ ++m_notify_update_count;
+ m_cond.notify_all();
+ })));
+ }
+
+ void expect_listener_images_unmapped(MockListener &mock_listener, size_t count,
+ std::set<std::string> *global_image_ids,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ EXPECT_CALL(mock_listener, mock_release_image(_, _))
+ .Times(count)
+ .WillRepeatedly(Invoke([this, global_image_ids, peer_ack_ctxs](std::string global_image_id, Context* ctx) {
+ std::lock_guard locker{m_lock};
+ global_image_ids->emplace(global_image_id);
+ peer_ack_ctxs->insert({global_image_id, ctx});
+ ++m_notify_update_count;
+ m_cond.notify_all();
+ }));
+ }
+
+ void remote_peer_ack_nowait(MockImageMap *image_map,
+ const std::set<std::string> &global_image_ids,
+ int ret,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ for (auto& global_image_id : global_image_ids) {
+ auto it = peer_ack_ctxs->find(global_image_id);
+ ASSERT_TRUE(it != peer_ack_ctxs->end());
+ auto ack_ctx = it->second;
+ peer_ack_ctxs->erase(it);
+ ack_ctx->complete(ret);
+ wait_for_scheduled_task();
+ }
+ }
+
+ void remote_peer_ack_wait(MockImageMap *image_map,
+ const std::set<std::string> &global_image_ids,
+ int ret,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ for (auto& global_image_id : global_image_ids) {
+ auto it = peer_ack_ctxs->find(global_image_id);
+ ASSERT_TRUE(it != peer_ack_ctxs->end());
+ auto ack_ctx = it->second;
+ peer_ack_ctxs->erase(it);
+ ack_ctx->complete(ret);
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_map_update(1));
+ }
+ }
+
+ void remote_peer_ack_listener_wait(MockImageMap *image_map,
+ const std::set<std::string> &global_image_ids,
+ int ret,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ for (auto& global_image_id : global_image_ids) {
+ auto it = peer_ack_ctxs->find(global_image_id);
+ ASSERT_TRUE(it != peer_ack_ctxs->end());
+ auto ack_ctx = it->second;
+ peer_ack_ctxs->erase(it);
+ ack_ctx->complete(ret);
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(1));
+ }
+ }
+
+ void update_map_and_acquire(MockThreads &mock_threads,
+ MockUpdateRequest &mock_update_request,
+ MockListener &mock_listener,
+ const std::set<std::string> &global_image_ids,
+ int ret,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ for (auto const &global_image_id : global_image_ids) {
+ expect_add_event(mock_threads);
+ expect_update_request(mock_update_request, ret);
+ expect_add_event(mock_threads);
+ expect_listener_acquire_image(mock_listener, global_image_id,
+ peer_ack_ctxs);
+ }
+ }
+
+ void update_map_request(MockThreads &mock_threads,
+ MockUpdateRequest &mock_update_request,
+ const std::set<std::string> &global_image_ids, int ret) {
+ for (uint32_t i = 0; i < global_image_ids.size(); ++i) {
+ expect_add_event(mock_threads);
+ expect_update_request(mock_update_request, ret);
+ }
+ }
+
+ void wait_for_scheduled_task() {
+ m_threads->work_queue->drain();
+ }
+
+ bool wait_for_listener_notify(uint32_t count) {
+ std::unique_lock locker{m_lock};
+ while (m_notify_update_count < count) {
+ if (m_cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ break;
+ }
+ }
+
+ if (m_notify_update_count < count) {
+ return false;
+ }
+
+ m_notify_update_count -= count;
+ return true;
+ }
+
+ bool wait_for_map_update(uint32_t count) {
+ std::unique_lock locker{m_lock};
+ while (m_map_update_count < count) {
+ if (m_cond.wait_for(locker, 10s) == std::cv_status::timeout) {
+ break;
+ }
+ }
+
+ if (m_map_update_count < count) {
+ return false;
+ }
+
+ m_map_update_count -= count;
+ return true;
+ }
+
+ int when_shut_down(MockImageMap *image_map) {
+ C_SaferCond ctx;
+ image_map->shut_down(&ctx);
+ return ctx.wait();
+ }
+
+ void listener_acquire_images(MockListener &mock_listener,
+ const std::set<std::string> &global_image_ids,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ for (auto const &global_image_id : global_image_ids) {
+ expect_listener_acquire_image(mock_listener, global_image_id,
+ peer_ack_ctxs);
+ }
+ }
+
+ void listener_release_images(MockListener &mock_listener,
+ const std::set<std::string> &global_image_ids,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ for (auto const &global_image_id : global_image_ids) {
+ expect_listener_release_image(mock_listener, global_image_id,
+ peer_ack_ctxs);
+ }
+ }
+
+ void listener_remove_images(MockListener &mock_listener,
+ const std::string &mirror_uuid,
+ std::set<std::string> &global_image_ids,
+ std::map<std::string, Context*> *peer_ack_ctxs) {
+ for (auto const &global_image_id : global_image_ids) {
+ expect_listener_remove_image(mock_listener, mirror_uuid, global_image_id,
+ peer_ack_ctxs);
+ }
+ }
+
+ ceph::mutex m_lock = ceph::make_mutex("TestMockImageMap::m_lock");
+ ceph::condition_variable m_cond;
+ uint32_t m_notify_update_count = 0;
+ uint32_t m_map_update_count = 0;
+ std::string m_local_instance_id;
+};
+
+TEST_F(TestMockImageMap, SetLocalImages) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> global_image_ids_ack(global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, global_image_ids, &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("", std::move(global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, AddRemoveLocalImage) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> initial_global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> initial_global_image_ids_ack(initial_global_image_ids);
+
+ std::set<std::string> remove_global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> remove_global_image_ids_ack(remove_global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, initial_global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("", std::move(initial_global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(initial_global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), initial_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ // RELEASE+REMOVE_MAPPING
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, remove_global_image_ids,
+ &peer_ack_ctxs);
+ update_map_request(mock_threads, mock_update_request, remove_global_image_ids,
+ 0);
+
+ // remove images
+ mock_image_map->update_images("", {}, std::move(remove_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(remove_global_image_ids_ack.size()));
+
+ remote_peer_ack_wait(mock_image_map.get(), remove_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, AddRemoveRemoteImage) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> initial_global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> initial_global_image_ids_ack(initial_global_image_ids);
+
+ std::set<std::string> remove_global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> remove_global_image_ids_ack(remove_global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, initial_global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("uuid1", std::move(initial_global_image_ids),
+ {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(initial_global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), initial_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ // RELEASE+REMOVE_MAPPING
+ std::map<std::string, Context*> peer_remove_ack_ctxs;
+ listener_remove_images(mock_listener, "uuid1", remove_global_image_ids,
+ &peer_remove_ack_ctxs);
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, remove_global_image_ids,
+ &peer_ack_ctxs);
+ update_map_request(mock_threads, mock_update_request, remove_global_image_ids,
+ 0);
+
+ // remove images
+ mock_image_map->update_images("uuid1", {}, std::move(remove_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(remove_global_image_ids_ack.size() * 2));
+
+ remote_peer_ack_nowait(mock_image_map.get(), remove_global_image_ids_ack, 0,
+ &peer_remove_ack_ctxs);
+ remote_peer_ack_wait(mock_image_map.get(), remove_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, AddRemoveRemoteImageDuplicateNotification) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> initial_global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> initial_global_image_ids_dup(initial_global_image_ids);
+ std::set<std::string> initial_global_image_ids_ack(initial_global_image_ids);
+
+ std::set<std::string> remove_global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> remove_global_image_ids_dup(remove_global_image_ids);
+ std::set<std::string> remove_global_image_ids_ack(remove_global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, initial_global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("uuid1", std::move(initial_global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(initial_global_image_ids_ack.size()));
+
+ // trigger duplicate "add" event
+ wait_for_scheduled_task();
+ mock_image_map->update_images("uuid1", std::move(initial_global_image_ids_dup), {});
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), initial_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ // RELEASE+REMOVE_MAPPING
+ std::map<std::string, Context*> peer_remove_ack_ctxs;
+ listener_remove_images(mock_listener, "uuid1", remove_global_image_ids,
+ &peer_remove_ack_ctxs);
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, remove_global_image_ids,
+ &peer_ack_ctxs);
+ update_map_request(mock_threads, mock_update_request, remove_global_image_ids, 0);
+
+ // remove images
+ mock_image_map->update_images("uuid1", {}, std::move(remove_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(remove_global_image_ids_ack.size() * 2));
+
+ remote_peer_ack_nowait(mock_image_map.get(), remove_global_image_ids_ack, 0,
+ &peer_remove_ack_ctxs);
+ remote_peer_ack_wait(mock_image_map.get(), remove_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ // trigger duplicate "remove" notification
+ mock_image_map->update_images("uuid1", {}, std::move(remove_global_image_ids_dup));
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, AcquireImageErrorRetry) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> initial_global_image_ids{
+ "global id 1", "global id 2"
+ };
+ std::set<std::string> initial_global_image_ids_ack(initial_global_image_ids);
+
+ // UPDATE_MAPPING failure
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, -EIO);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, initial_global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("uuid1", std::move(initial_global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(initial_global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), initial_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, RemoveRemoteAndLocalImage) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ // remote image set
+ std::set<std::string> initial_remote_global_image_ids{
+ "global id 1"
+ };
+ std::set<std::string> initial_remote_global_image_ids_ack(initial_remote_global_image_ids);
+
+ // local image set
+ std::set<std::string> initial_local_global_image_ids{
+ "global id 1"
+ };
+
+ // remote/local images to remove
+ std::set<std::string> remote_remove_global_image_ids{
+ "global id 1"
+ };
+ std::set<std::string> remote_remove_global_image_ids_ack(remote_remove_global_image_ids);
+
+ std::set<std::string> local_remove_global_image_ids{
+ "global id 1"
+ };
+ std::set<std::string> local_remove_global_image_ids_ack(local_remove_global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, initial_remote_global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial remote image list
+ mock_image_map->update_images("uuid1", std::move(initial_remote_global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(initial_remote_global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(),
+ initial_remote_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ // set initial local image list -- this is a no-op from policy pov
+ mock_image_map->update_images("", std::move(initial_local_global_image_ids), {});
+
+ // remove remote images -- this should be a no-op from policy pov
+ // except the listener notification
+ std::map<std::string, Context*> peer_ack_remove_ctxs;
+ listener_remove_images(mock_listener, "uuid1", remote_remove_global_image_ids,
+ &peer_ack_remove_ctxs);
+
+ mock_image_map->update_images("uuid1", {}, std::move(remote_remove_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(remote_remove_global_image_ids_ack.size()));
+
+ // RELEASE+REMOVE_MAPPING
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, local_remove_global_image_ids,
+ &peer_ack_ctxs);
+ update_map_request(mock_threads, mock_update_request, local_remove_global_image_ids, 0);
+
+ // remove local images
+ mock_image_map->update_images("", {}, std::move(local_remove_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(local_remove_global_image_ids_ack.size()));
+
+ remote_peer_ack_nowait(mock_image_map.get(), local_remove_global_image_ids_ack,
+ 0, &peer_ack_remove_ctxs);
+ remote_peer_ack_wait(mock_image_map.get(), local_remove_global_image_ids_ack,
+ 0, &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, AddInstance) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> global_image_ids{
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5"
+ };
+ std::set<std::string> global_image_ids_ack(global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("uuid1", std::move(global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ mock_image_map->update_instances_added({m_local_instance_id});
+
+ std::set<std::string> shuffled_global_image_ids;
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 3, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_instances_added({"9876"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, RemoveInstance) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> global_image_ids{
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5"
+ };
+ std::set<std::string> global_image_ids_ack(global_image_ids);
+
+ expect_add_event(mock_threads);
+
+ // UPDATE_MAPPING+ACQUIRE
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, global_image_ids,
+ &peer_ack_ctxs);
+
+ // set initial image list
+ mock_image_map->update_images("uuid1", std::move(global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request -- completing action
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ mock_image_map->update_instances_added({m_local_instance_id});
+
+ std::set<std::string> shuffled_global_image_ids;
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 3, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_instances_added({"9876"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ shuffled_global_image_ids.clear();
+
+ // remove added instance
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 2, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_instances_removed({"9876"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, AddInstancePingPongImageTest) {
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_image_policy_migration_throttle", "600"));
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ std::set<std::string> global_image_ids{
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5",
+ "global id 6", "global id 7", "global id 8", "global id 9", "global id 10",
+ "global id 11", "global id 12", "global id 13", "global id 14"
+ };
+
+ std::map<std::string, cls::rbd::MirrorImageMap> image_mapping;
+ for (auto& global_image_id : global_image_ids) {
+ image_mapping[global_image_id] = {m_local_instance_id, {}, {}};
+ }
+
+ // ACQUIRE
+ MockLoadRequest mock_load_request;
+ EXPECT_CALL(mock_load_request, send()).WillOnce(
+ Invoke([&mock_load_request, &image_mapping]() {
+ *mock_load_request.image_map = image_mapping;
+ mock_load_request.on_finish->complete(0);
+ }));
+
+ expect_add_event(mock_threads);
+ MockListener mock_listener(this);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, global_image_ids,
+ &peer_ack_ctxs);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ mock_image_map->update_instances_added({m_local_instance_id});
+
+ std::set<std::string> global_image_ids_ack(global_image_ids);
+
+ // remote peer ACKs image acquire request -- completing action
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ listener_acquire_images(mock_listener, global_image_ids,
+ &peer_ack_ctxs);
+
+ // set initial image list
+ mock_image_map->update_images("uuid1", std::move(global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request -- completing action
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ std::set<std::string> shuffled_global_image_ids;
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 7, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_instances_added({"9876"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ std::set<std::string> migrated_global_image_ids(shuffled_global_image_ids);
+ shuffled_global_image_ids.clear();
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 3, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ // add another instance
+ mock_image_map->update_instances_added({"5432"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+
+ // shuffle set should be distinct
+ std::set<std::string> reshuffled;
+ std::set_intersection(migrated_global_image_ids.begin(), migrated_global_image_ids.end(),
+ shuffled_global_image_ids.begin(), shuffled_global_image_ids.end(),
+ std::inserter(reshuffled, reshuffled.begin()));
+ ASSERT_TRUE(reshuffled.empty());
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, RemoveInstanceWithRemoveImage) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> global_image_ids{
+ "global id 1", "global id 2", "global id 3", "remote id 4",
+ };
+ std::set<std::string> global_image_ids_ack(global_image_ids);
+
+ std::set<std::string> remove_global_image_ids{
+ "global id 1"
+ };
+ std::set<std::string> remove_global_image_ids_ack(remove_global_image_ids);
+
+ expect_add_event(mock_threads);
+ // UPDATE_MAPPING+ACQUIRE
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("uuid1", std::move(global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ mock_image_map->update_instances_added({m_local_instance_id});
+
+ std::set<std::string> shuffled_global_image_ids;
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 2, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_instances_added({"9876"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ std::set<std::string> shuffled_global_image_ids_ack(shuffled_global_image_ids);
+
+ // RELEASE
+
+ std::map<std::string, Context*> peer_ack_remove_ctxs;
+ listener_remove_images(mock_listener, "uuid1", shuffled_global_image_ids,
+ &peer_ack_remove_ctxs);
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, shuffled_global_image_ids,
+ &peer_ack_ctxs);
+ expect_add_event(mock_threads);
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ expect_update_request(mock_update_request, 0);
+
+ mock_image_map->update_images("uuid1", {}, std::move(shuffled_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids_ack.size() * 2));
+
+ // instance failed -- update policy for instance removal
+ mock_image_map->update_instances_removed({"9876"});
+
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids,
+ -ENOENT, &peer_ack_remove_ctxs);
+ remote_peer_ack_wait(mock_image_map.get(), shuffled_global_image_ids,
+ -EBLOCKLISTED, &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, AddErrorAndRemoveImage) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ mock_image_map->update_instances_added({m_local_instance_id});
+
+ std::set<std::string> global_image_ids{
+ "global id 1", "global id 2", "global id 3", "remote id 4",
+ };
+ std::set<std::string> global_image_ids_ack(global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("uuid1", std::move(global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ std::set<std::string> shuffled_global_image_ids;
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 2, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_instances_added({"9876"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ mock_image_map->update_instances_removed({"9876"});
+
+ std::set<std::string> released_global_image_ids;
+ std::map<std::string, Context*> release_peer_ack_ctxs;
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 1, &released_global_image_ids,
+ &release_peer_ack_ctxs);
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 1, &released_global_image_ids,
+ &release_peer_ack_ctxs);
+
+ // instance blocklisted -- ACQUIRE request fails
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids,
+ -EBLOCKLISTED, &peer_ack_ctxs);
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ std::map<std::string, Context*> remap_peer_ack_ctxs;
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &remap_peer_ack_ctxs);
+
+ // instance blocklisted -- RELEASE request fails
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ -ENOENT, &release_peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ // new peer acks acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &remap_peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ std::set<std::string> shuffled_global_image_ids_ack(shuffled_global_image_ids);
+
+ // remove image
+ std::map<std::string, Context*> peer_ack_remove_ctxs;
+ listener_remove_images(mock_listener, "uuid1", shuffled_global_image_ids,
+ &peer_ack_remove_ctxs);
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, shuffled_global_image_ids,
+ &peer_ack_ctxs);
+ update_map_request(mock_threads, mock_update_request, shuffled_global_image_ids, 0);
+
+ mock_image_map->update_images("uuid1", {}, std::move(shuffled_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids_ack.size() * 2));
+
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids_ack, 0,
+ &peer_ack_remove_ctxs);
+ remote_peer_ack_wait(mock_image_map.get(), shuffled_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, MirrorUUIDUpdated) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ // remote image set
+ std::set<std::string> initial_remote_global_image_ids{
+ "global id 1", "global id 2", "global id 3"
+ };
+ std::set<std::string> initial_remote_global_image_ids_ack(initial_remote_global_image_ids);
+
+ // remote/local images to remove
+ std::set<std::string> remote_removed_global_image_ids{
+ "global id 1", "global id 2", "global id 3"
+ };
+ std::set<std::string> remote_removed_global_image_ids_ack(remote_removed_global_image_ids);
+
+ std::set<std::string> remote_added_global_image_ids{
+ "global id 1", "global id 2", "global id 3"
+ };
+ std::set<std::string> remote_added_global_image_ids_ack(remote_added_global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, initial_remote_global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial remote image list
+ mock_image_map->update_images("uuid1", std::move(initial_remote_global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(initial_remote_global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(),
+ initial_remote_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ // RELEASE+REMOVE_MAPPING
+ std::map<std::string, Context*> peer_remove_ack_ctxs;
+ listener_remove_images(mock_listener, "uuid1", remote_removed_global_image_ids,
+ &peer_remove_ack_ctxs);
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, remote_removed_global_image_ids,
+ &peer_ack_ctxs);
+ update_map_request(mock_threads, mock_update_request, remote_removed_global_image_ids, 0);
+
+ mock_image_map->update_images("uuid1", {}, std::move(remote_removed_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(remote_removed_global_image_ids_ack.size() * 2));
+
+ remote_peer_ack_nowait(mock_image_map.get(),
+ remote_removed_global_image_ids_ack, 0,
+ &peer_remove_ack_ctxs);
+ remote_peer_ack_wait(mock_image_map.get(),
+ remote_removed_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ listener_acquire_images(mock_listener, remote_added_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_images("uuid2", std::move(remote_added_global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(remote_added_global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(),
+ remote_added_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+TEST_F(TestMockImageMap, RebalanceImageMap) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+
+ MockLoadRequest mock_load_request;
+ expect_load_request(mock_load_request, 0);
+
+ MockListener mock_listener(this);
+
+ std::unique_ptr<MockImageMap> mock_image_map{
+ MockImageMap::create(m_local_io_ctx, &mock_threads, m_local_instance_id,
+ mock_listener)};
+
+ C_SaferCond cond;
+ mock_image_map->init(&cond);
+ ASSERT_EQ(0, cond.wait());
+
+ std::set<std::string> global_image_ids{
+ "global id 1", "global id 2", "global id 3", "global id 4", "global id 5",
+ "global id 6", "global id 7", "global id 8", "global id 9", "global id 10",
+ };
+ std::set<std::string> global_image_ids_ack(global_image_ids);
+
+ // UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ MockUpdateRequest mock_update_request;
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ std::map<std::string, Context*> peer_ack_ctxs;
+ listener_acquire_images(mock_listener, global_image_ids,
+ &peer_ack_ctxs);
+
+ // initial image list
+ mock_image_map->update_images("", std::move(global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(global_image_ids_ack.size()));
+
+ // remote peer ACKs image acquire request
+ remote_peer_ack_nowait(mock_image_map.get(), global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ mock_image_map->update_instances_added({m_local_instance_id});
+
+ std::set<std::string> shuffled_global_image_ids;
+
+ // RELEASE+UPDATE_MAPPING+ACQUIRE
+ expect_add_event(mock_threads);
+ expect_listener_images_unmapped(mock_listener, 5, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_instances_added({"9876"});
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+
+ // remove all shuffled images -- make way for rebalance
+ std::set<std::string> shuffled_global_image_ids_ack(shuffled_global_image_ids);
+
+ // RELEASE+REMOVE_MAPPING
+ expect_add_event(mock_threads);
+ listener_release_images(mock_listener, shuffled_global_image_ids,
+ &peer_ack_ctxs);
+ update_map_request(mock_threads, mock_update_request, shuffled_global_image_ids,
+ 0);
+
+ mock_image_map->update_images("", {}, std::move(shuffled_global_image_ids));
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids_ack.size()));
+
+ remote_peer_ack_wait(mock_image_map.get(), shuffled_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+ wait_for_scheduled_task();
+
+ shuffled_global_image_ids.clear();
+ shuffled_global_image_ids_ack.clear();
+
+ std::set<std::string> new_global_image_ids = {
+ "global id 11"
+ };
+ std::set<std::string> new_global_image_ids_ack(new_global_image_ids);
+
+ expect_add_event(mock_threads);
+ expect_update_request(mock_update_request, 0);
+ expect_add_event(mock_threads);
+ listener_acquire_images(mock_listener, new_global_image_ids, &peer_ack_ctxs);
+
+ expect_rebalance_event(mock_threads); // rebalance task
+ expect_add_event(mock_threads); // update task scheduled by
+ // rebalance task
+ expect_listener_images_unmapped(mock_listener, 2, &shuffled_global_image_ids,
+ &peer_ack_ctxs);
+
+ mock_image_map->update_images("", std::move(new_global_image_ids), {});
+
+ ASSERT_TRUE(wait_for_map_update(1));
+ ASSERT_TRUE(wait_for_listener_notify(new_global_image_ids_ack.size()));
+
+ // set rebalance interval
+ CephContext *cct = reinterpret_cast<CephContext *>(m_local_io_ctx.cct());
+ cct->_conf.set_val("rbd_mirror_image_policy_rebalance_timeout", "5");
+ remote_peer_ack_nowait(mock_image_map.get(), new_global_image_ids_ack, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_TRUE(wait_for_listener_notify(shuffled_global_image_ids.size()));
+
+ update_map_and_acquire(mock_threads, mock_update_request,
+ mock_listener, shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+ remote_peer_ack_listener_wait(mock_image_map.get(), shuffled_global_image_ids,
+ 0, &peer_ack_ctxs);
+
+ // completion shuffle action for now (re)mapped images
+ remote_peer_ack_nowait(mock_image_map.get(), shuffled_global_image_ids, 0,
+ &peer_ack_ctxs);
+
+ wait_for_scheduled_task();
+ ASSERT_EQ(0, when_shut_down(mock_image_map.get()));
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_ImageReplayer.cc b/src/test/rbd_mirror/test_mock_ImageReplayer.cc
new file mode 100644
index 000000000..32a4f82b5
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_ImageReplayer.cc
@@ -0,0 +1,989 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/journal/cls_journal_types.h"
+#include "librbd/journal/Types.h"
+#include "librbd/journal/TypeTraits.h"
+#include "tools/rbd_mirror/ImageDeleter.h"
+#include "tools/rbd_mirror/ImageReplayer.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/MirrorStatusUpdater.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/BootstrapRequest.h"
+#include "tools/rbd_mirror/image_replayer/Replayer.h"
+#include "tools/rbd_mirror/image_replayer/ReplayerListener.h"
+#include "tools/rbd_mirror/image_replayer/StateBuilder.h"
+#include "tools/rbd_mirror/image_replayer/Utils.h"
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct ImageDeleter<librbd::MockTestImageCtx> {
+ static ImageDeleter* s_instance;
+
+ static void trash_move(librados::IoCtx& local_io_ctx,
+ const std::string& global_image_id, bool resync,
+ MockContextWQ* work_queue, Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->trash_move(global_image_id, resync, on_finish);
+ }
+
+ MOCK_METHOD3(trash_move, void(const std::string&, bool, Context*));
+
+ ImageDeleter() {
+ s_instance = this;
+ }
+};
+
+ImageDeleter<librbd::MockTestImageCtx>* ImageDeleter<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct MirrorStatusUpdater<librbd::MockTestImageCtx> {
+
+ MOCK_METHOD1(exists, bool(const std::string&));
+ MOCK_METHOD3(set_mirror_image_status,
+ void(const std::string&, const cls::rbd::MirrorImageSiteStatus&,
+ bool));
+ MOCK_METHOD2(remove_refresh_mirror_image_status, void(const std::string&,
+ Context*));
+ MOCK_METHOD3(remove_mirror_image_status, void(const std::string&, bool,
+ Context*));
+};
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+template<>
+class InstanceWatcher<librbd::MockTestImageCtx> {
+};
+
+namespace image_replayer {
+
+template<>
+struct BootstrapRequest<librbd::MockTestImageCtx> {
+ static BootstrapRequest* s_instance;
+
+ StateBuilder<librbd::MockTestImageCtx>** state_builder = nullptr;
+ bool *do_resync = nullptr;
+ Context *on_finish = nullptr;
+
+ static BootstrapRequest* create(
+ Threads<librbd::MockTestImageCtx>* threads,
+ librados::IoCtx &local_io_ctx,
+ librados::IoCtx& remote_io_ctx,
+ rbd::mirror::InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher,
+ const std::string &global_image_id,
+ const std::string &local_mirror_uuid,
+ const RemotePoolMeta& remote_pool_meta,
+ ::journal::CacheManagerHandler *cache_manager_handler,
+ PoolMetaCache* pool_meta_cache,
+ rbd::mirror::ProgressContext *progress_ctx,
+ StateBuilder<librbd::MockTestImageCtx>** state_builder,
+ bool *do_resync, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->state_builder = state_builder;
+ s_instance->do_resync = do_resync;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ BootstrapRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~BootstrapRequest() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ void put() {
+ }
+
+ void get() {
+ }
+
+ std::string get_local_image_name() const {
+ return "local image name";
+ }
+
+ inline bool is_syncing() const {
+ return false;
+ }
+
+ MOCK_METHOD0(send, void());
+ MOCK_METHOD0(cancel, void());
+};
+
+struct MockReplayer : public Replayer {
+ image_replayer::ReplayerListener* replayer_listener;
+
+ MOCK_METHOD0(destroy, void());
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+ MOCK_METHOD1(flush, void(Context*));
+
+ MOCK_METHOD2(get_replay_status, bool(std::string*, Context*));
+
+ MOCK_CONST_METHOD0(is_replaying, bool());
+ MOCK_CONST_METHOD0(is_resync_requested, bool());
+ MOCK_CONST_METHOD0(get_error_code, int());
+ MOCK_CONST_METHOD0(get_error_description, std::string());
+};
+
+template <>
+struct StateBuilder<librbd::MockTestImageCtx> {
+ static StateBuilder* s_instance;
+
+ librbd::MockTestImageCtx* local_image_ctx = nullptr;
+ std::string local_image_id;
+ std::string remote_image_id;
+
+ void destroy() {
+ }
+
+ MOCK_METHOD1(close, void(Context*));
+ MOCK_METHOD5(create_replayer, Replayer*(Threads<librbd::MockTestImageCtx>*,
+ InstanceWatcher<librbd::MockTestImageCtx>*,
+ const std::string&, PoolMetaCache*,
+ ReplayerListener*));
+
+ StateBuilder() {
+ s_instance = this;
+ }
+};
+
+BootstrapRequest<librbd::MockTestImageCtx>* BootstrapRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+StateBuilder<librbd::MockTestImageCtx>* StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image_replayer
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/ImageReplayer.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::MatcherCast;
+using ::testing::Return;
+using ::testing::ReturnArg;
+using ::testing::SetArgPointee;
+using ::testing::WithArg;
+
+class TestMockImageReplayer : public TestMockFixture {
+public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter;
+ typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater;
+ typedef image_replayer::BootstrapRequest<librbd::MockTestImageCtx> MockBootstrapRequest;
+ typedef image_replayer::StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
+ typedef image_replayer::MockReplayer MockReplayer;
+ typedef ImageReplayer<librbd::MockTestImageCtx> MockImageReplayer;
+ typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+ }
+
+ void TearDown() override {
+ delete m_image_replayer;
+
+ TestMockFixture::TearDown();
+ }
+
+ void create_local_image() {
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ void expect_work_queue_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_add_event_after_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillRepeatedly(
+ DoAll(Invoke([this](double seconds, Context *ctx) {
+ m_threads->timer->add_event_after(seconds, ctx);
+ }),
+ ReturnArg<1>()));
+ EXPECT_CALL(*mock_threads.timer, cancel_event(_))
+ .WillRepeatedly(
+ Invoke([this](Context *ctx) {
+ return m_threads->timer->cancel_event(ctx);
+ }));
+ }
+
+ void expect_trash_move(MockImageDeleter& mock_image_deleter,
+ const std::string& global_image_id,
+ bool ignore_orphan, int r) {
+ EXPECT_CALL(mock_image_deleter,
+ trash_move(global_image_id, ignore_orphan, _))
+ .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ })));
+ }
+
+ bufferlist encode_tag_data(const librbd::journal::TagData &tag_data) {
+ bufferlist bl;
+ encode(tag_data, bl);
+ return bl;
+ }
+
+ void expect_send(MockBootstrapRequest& mock_bootstrap_request,
+ MockStateBuilder& mock_state_builder,
+ librbd::MockTestImageCtx& mock_local_image_ctx,
+ bool do_resync, bool set_local_image, int r) {
+ EXPECT_CALL(mock_bootstrap_request, send())
+ .WillOnce(Invoke([this, &mock_bootstrap_request, &mock_state_builder,
+ &mock_local_image_ctx, set_local_image, do_resync,
+ r]() {
+ if (r == 0 || r == -ENOLINK) {
+ mock_state_builder.local_image_id = mock_local_image_ctx.id;
+ mock_state_builder.remote_image_id = m_remote_image_ctx->id;
+ *mock_bootstrap_request.state_builder = &mock_state_builder;
+ }
+ if (r == 0) {
+ mock_state_builder.local_image_ctx = &mock_local_image_ctx;
+ *mock_bootstrap_request.do_resync = do_resync;
+ }
+ if (r < 0 && r != -ENOENT) {
+ mock_state_builder.remote_image_id = "";
+ }
+ if (r == -ENOENT) {
+ *mock_bootstrap_request.state_builder = &mock_state_builder;
+ }
+ if (set_local_image) {
+ mock_state_builder.local_image_id = mock_local_image_ctx.id;
+ }
+ mock_bootstrap_request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_create_replayer(MockStateBuilder& mock_state_builder,
+ MockReplayer& mock_replayer) {
+ EXPECT_CALL(mock_state_builder, create_replayer(_, _, _, _, _))
+ .WillOnce(WithArg<4>(
+ Invoke([&mock_replayer]
+ (image_replayer::ReplayerListener* replayer_listener) {
+ mock_replayer.replayer_listener = replayer_listener;
+ return &mock_replayer;
+ })));
+ }
+
+ void expect_close(MockStateBuilder& mock_state_builder, int r) {
+ EXPECT_CALL(mock_state_builder, close(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_init(MockReplayer& mock_replayer, int r) {
+ EXPECT_CALL(mock_replayer, init(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_shut_down(MockReplayer& mock_replayer, int r) {
+ EXPECT_CALL(mock_replayer, shut_down(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ EXPECT_CALL(mock_replayer, destroy());
+ }
+
+ void expect_get_replay_status(MockReplayer& mock_replayer) {
+ EXPECT_CALL(mock_replayer, get_replay_status(_, _))
+ .WillRepeatedly(DoAll(WithArg<1>(CompleteContext(-EEXIST)),
+ Return(true)));
+ }
+
+ void expect_set_mirror_image_status_repeatedly() {
+ EXPECT_CALL(m_local_status_updater, set_mirror_image_status(_, _, _))
+ .WillRepeatedly(Invoke([](auto, auto, auto){}));
+ EXPECT_CALL(m_remote_status_updater, set_mirror_image_status(_, _, _))
+ .WillRepeatedly(Invoke([](auto, auto, auto){}));
+ }
+
+ void expect_mirror_image_status_exists(bool exists) {
+ EXPECT_CALL(m_local_status_updater, exists(_))
+ .WillOnce(Return(exists));
+ EXPECT_CALL(m_remote_status_updater, exists(_))
+ .WillOnce(Return(exists));
+ }
+
+ void create_image_replayer(MockThreads &mock_threads) {
+ m_image_replayer = new MockImageReplayer(
+ m_local_io_ctx, "local_mirror_uuid", "global image id",
+ &mock_threads, &m_instance_watcher, &m_local_status_updater, nullptr,
+ nullptr);
+ m_image_replayer->add_peer({"peer_uuid", m_remote_io_ctx,
+ {"remote mirror uuid",
+ "remote mirror peer uuid"},
+ &m_remote_status_updater});
+ }
+
+ void wait_for_stopped() {
+ for (int i = 0; i < 10000; i++) {
+ if (m_image_replayer->is_stopped()) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_TRUE(m_image_replayer->is_stopped());
+ }
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ librbd::ImageCtx *m_local_image_ctx = nullptr;
+ MockInstanceWatcher m_instance_watcher;
+ MockMirrorStatusUpdater m_local_status_updater;
+ MockMirrorStatusUpdater m_remote_status_updater;
+ MockImageReplayer *m_image_replayer = nullptr;
+};
+
+TEST_F(TestMockImageReplayer, StartStop) {
+ // START
+
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockReplayer mock_replayer;
+
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, 0);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+ ASSERT_EQ(image_replayer::HEALTH_STATE_OK,
+ m_image_replayer->get_health_state());
+
+ // STOP
+ expect_shut_down(mock_replayer, 0);
+ expect_close(mock_state_builder, 0);
+ expect_mirror_image_status_exists(false);
+
+ C_SaferCond stop_ctx;
+ m_image_replayer->stop(&stop_ctx);
+ ASSERT_EQ(0, stop_ctx.wait());
+ ASSERT_EQ(image_replayer::HEALTH_STATE_OK,
+ m_image_replayer->get_health_state());
+}
+
+TEST_F(TestMockImageReplayer, LocalImagePrimary) {
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, -ENOMSG);
+
+ expect_mirror_image_status_exists(false);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, MetadataCleanup) {
+ // START
+
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockReplayer mock_replayer;
+
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, true, -ENOLINK);
+
+ expect_close(mock_state_builder, 0);
+ expect_trash_move(mock_image_deleter, "global image id", false, 0);
+ expect_mirror_image_status_exists(false);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, BootstrapRemoteDeleted) {
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+
+ MockBootstrapRequest mock_bootstrap_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, -ENOLINK);
+
+ expect_close(mock_state_builder, 0);
+
+ expect_trash_move(mock_image_deleter, "global image id", false, 0);
+ expect_mirror_image_status_exists(false);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, BootstrapResyncRequested) {
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+
+ MockBootstrapRequest mock_bootstrap_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ true, false, 0);
+
+ expect_close(mock_state_builder, 0);
+
+ expect_trash_move(mock_image_deleter, "global image id", true, 0);
+ expect_mirror_image_status_exists(false);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, BootstrapError) {
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, -EINVAL);
+
+ expect_mirror_image_status_exists(false);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(-EINVAL, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, BootstrapCancel) {
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+
+ create_image_replayer(mock_threads);
+
+ MockBootstrapRequest mock_bootstrap_request;
+ MockStateBuilder mock_state_builder;
+ EXPECT_CALL(mock_bootstrap_request, send())
+ .WillOnce(Invoke([this, &mock_bootstrap_request]() {
+ m_image_replayer->stop(nullptr);
+ mock_bootstrap_request.on_finish->complete(-ECANCELED);
+ }));
+ EXPECT_CALL(mock_bootstrap_request, cancel());
+
+ expect_mirror_image_status_exists(false);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(-ECANCELED, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, BootstrapRemoteDeletedCancel) {
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+
+ MockBootstrapRequest mock_bootstrap_request;
+ MockStateBuilder mock_state_builder;
+ EXPECT_CALL(mock_bootstrap_request, send())
+ .WillOnce(Invoke([this, &mock_bootstrap_request, &mock_state_builder,
+ &mock_local_image_ctx]() {
+ mock_state_builder.local_image_id = mock_local_image_ctx.id;
+ mock_state_builder.remote_image_id = "";
+ *mock_bootstrap_request.state_builder = &mock_state_builder;
+ m_image_replayer->stop(nullptr);
+ mock_bootstrap_request.on_finish->complete(-ENOLINK);
+ }));
+ EXPECT_CALL(mock_bootstrap_request, cancel());
+
+ expect_close(mock_state_builder, 0);
+
+ expect_trash_move(mock_image_deleter, "global image id", false, 0);
+ expect_mirror_image_status_exists(false);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(-ECANCELED, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, StopError) {
+ // START
+
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockReplayer mock_replayer;
+
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, 0);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+
+ // STOP (errors are ignored)
+
+ expect_shut_down(mock_replayer, -EINVAL);
+ expect_close(mock_state_builder, -EINVAL);
+ expect_mirror_image_status_exists(false);
+
+ C_SaferCond stop_ctx;
+ m_image_replayer->stop(&stop_ctx);
+ ASSERT_EQ(0, stop_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, ReplayerError) {
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockReplayer mock_replayer;
+
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, -EINVAL);
+ EXPECT_CALL(mock_replayer, get_error_description())
+ .WillOnce(Return("FAIL"));
+
+ EXPECT_CALL(mock_replayer, destroy());
+ expect_close(mock_state_builder, -EINVAL);
+
+ expect_mirror_image_status_exists(false);
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(-EINVAL, start_ctx.wait());
+}
+
+TEST_F(TestMockImageReplayer, ReplayerResync) {
+ // START
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockReplayer mock_replayer;
+
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, 0);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+
+ // NOTIFY
+ EXPECT_CALL(mock_replayer, is_resync_requested())
+ .WillOnce(Return(true));
+ expect_shut_down(mock_replayer, 0);
+ expect_close(mock_state_builder, 0);
+ expect_trash_move(mock_image_deleter, "global image id", true, 0);
+ expect_mirror_image_status_exists(false);
+ mock_replayer.replayer_listener->handle_notification();
+ ASSERT_FALSE(m_image_replayer->is_running());
+
+ wait_for_stopped();
+}
+
+TEST_F(TestMockImageReplayer, ReplayerInterrupted) {
+ // START
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockReplayer mock_replayer;
+
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, 0);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+
+ // NOTIFY
+ EXPECT_CALL(mock_replayer, is_resync_requested())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_replayer, is_replaying())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_replayer, get_error_code())
+ .WillOnce(Return(-EINVAL));
+ EXPECT_CALL(mock_replayer, get_error_description())
+ .WillOnce(Return("INVALID"));
+ expect_shut_down(mock_replayer, 0);
+ expect_close(mock_state_builder, 0);
+ expect_mirror_image_status_exists(false);
+ mock_replayer.replayer_listener->handle_notification();
+ ASSERT_FALSE(m_image_replayer->is_running());
+
+ wait_for_stopped();
+}
+
+TEST_F(TestMockImageReplayer, ReplayerRenamed) {
+ // START
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockImageDeleter mock_image_deleter;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockReplayer mock_replayer;
+
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, 0);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+
+ // NOTIFY
+ EXPECT_CALL(mock_replayer, is_resync_requested())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_replayer, is_replaying())
+ .WillOnce(Return(true));
+ mock_local_image_ctx.name = "NEW NAME";
+ mock_replayer.replayer_listener->handle_notification();
+
+ // STOP
+ expect_shut_down(mock_replayer, 0);
+ expect_close(mock_state_builder, 0);
+ expect_mirror_image_status_exists(false);
+
+ C_SaferCond stop_ctx;
+ m_image_replayer->stop(&stop_ctx);
+ ASSERT_EQ(0, stop_ctx.wait());
+
+ auto image_spec = image_replayer::util::compute_image_spec(
+ m_local_io_ctx, "NEW NAME");
+ ASSERT_EQ(image_spec, m_image_replayer->get_name());
+}
+
+TEST_F(TestMockImageReplayer, StopJoinInterruptedReplayer) {
+ // START
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockReplayer mock_replayer;
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, 0);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+
+ // NOTIFY
+ EXPECT_CALL(mock_replayer, is_resync_requested())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_replayer, is_replaying())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_replayer, get_error_code())
+ .WillOnce(Return(-EINVAL));
+ EXPECT_CALL(mock_replayer, get_error_description())
+ .WillOnce(Return("INVALID"));
+ const double DELAY = 10;
+ EXPECT_CALL(mock_replayer, shut_down(_))
+ .WillOnce(Invoke([this, DELAY](Context* ctx) {
+ std::lock_guard l(m_threads->timer_lock);
+ m_threads->timer->add_event_after(DELAY, ctx);
+ }));
+ EXPECT_CALL(mock_replayer, destroy());
+ expect_close(mock_state_builder, 0);
+ expect_mirror_image_status_exists(false);
+
+ mock_replayer.replayer_listener->handle_notification();
+ ASSERT_FALSE(m_image_replayer->is_running());
+
+ C_SaferCond stop_ctx;
+ m_image_replayer->stop(&stop_ctx);
+ ASSERT_EQ(ETIMEDOUT, stop_ctx.wait_for(DELAY * 3 / 4));
+ ASSERT_EQ(0, stop_ctx.wait_for(DELAY));
+}
+
+TEST_F(TestMockImageReplayer, StopJoinRequestedStop) {
+ // START
+ create_local_image();
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
+ MockReplayer mock_replayer;
+ expect_get_replay_status(mock_replayer);
+ expect_set_mirror_image_status_repeatedly();
+
+ InSequence seq;
+ MockBootstrapRequest mock_bootstrap_request;
+ MockStateBuilder mock_state_builder;
+ expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx,
+ false, false, 0);
+
+ expect_create_replayer(mock_state_builder, mock_replayer);
+ expect_init(mock_replayer, 0);
+
+ create_image_replayer(mock_threads);
+
+ C_SaferCond start_ctx;
+ m_image_replayer->start(&start_ctx);
+ ASSERT_EQ(0, start_ctx.wait());
+
+ // STOP
+ const double DELAY = 10;
+ EXPECT_CALL(mock_replayer, shut_down(_))
+ .WillOnce(Invoke([this, DELAY](Context* ctx) {
+ std::lock_guard l(m_threads->timer_lock);
+ m_threads->timer->add_event_after(DELAY, ctx);
+ }));
+ EXPECT_CALL(mock_replayer, destroy());
+ expect_close(mock_state_builder, 0);
+ expect_mirror_image_status_exists(false);
+
+ C_SaferCond stop_ctx1;
+ m_image_replayer->stop(&stop_ctx1);
+
+ C_SaferCond stop_ctx2;
+ m_image_replayer->stop(&stop_ctx2);
+ ASSERT_EQ(ETIMEDOUT, stop_ctx2.wait_for(DELAY * 3 / 4));
+ ASSERT_EQ(0, stop_ctx2.wait_for(DELAY));
+
+ ASSERT_EQ(0, stop_ctx1.wait_for(0));
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_ImageSync.cc b/src/test/rbd_mirror/test_mock_ImageSync.cc
new file mode 100644
index 000000000..bd6a29078
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_ImageSync.cc
@@ -0,0 +1,468 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/DeepCopyRequest.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/image_sync/MockSyncPointHandler.h"
+#include "tools/rbd_mirror/ImageSync.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h"
+#include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+template <>
+class DeepCopyRequest<librbd::MockTestImageCtx> {
+public:
+ static DeepCopyRequest* s_instance;
+ Context *on_finish;
+
+ static DeepCopyRequest* create(
+ librbd::MockTestImageCtx *src_image_ctx,
+ librbd::MockTestImageCtx *dst_image_ctx,
+ librados::snap_t src_snap_id_start, librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start, bool flatten,
+ const librbd::deep_copy::ObjectNumber &object_number,
+ librbd::asio::ContextWQ *work_queue, SnapSeqs *snap_seqs,
+ deep_copy::Handler *handler, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ DeepCopyRequest() {
+ s_instance = this;
+ }
+
+ void put() {
+ }
+
+ void get() {
+ }
+
+ MOCK_METHOD0(cancel, void());
+ MOCK_METHOD0(send, void());
+};
+
+DeepCopyRequest<librbd::MockTestImageCtx>* DeepCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/ImageSync.cc"
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue) {
+ }
+};
+
+template<>
+struct InstanceWatcher<librbd::MockTestImageCtx> {
+ MOCK_METHOD2(notify_sync_request, void(const std::string, Context *));
+ MOCK_METHOD1(cancel_sync_request, bool(const std::string &));
+ MOCK_METHOD1(notify_sync_complete, void(const std::string &));
+};
+
+namespace image_sync {
+
+template <>
+class SyncPointCreateRequest<librbd::MockTestImageCtx> {
+public:
+ static SyncPointCreateRequest *s_instance;
+ Context *on_finish;
+
+ static SyncPointCreateRequest* create(librbd::MockTestImageCtx *remote_image_ctx,
+ const std::string &mirror_uuid,
+ image_sync::SyncPointHandler* sync_point_handler,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SyncPointCreateRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+template <>
+class SyncPointPruneRequest<librbd::MockTestImageCtx> {
+public:
+ static SyncPointPruneRequest *s_instance;
+ Context *on_finish;
+ bool sync_complete;
+
+ static SyncPointPruneRequest* create(librbd::MockTestImageCtx *remote_image_ctx,
+ bool sync_complete,
+ image_sync::SyncPointHandler* sync_point_handler,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ s_instance->sync_complete = sync_complete;
+ return s_instance;
+ }
+
+ SyncPointPruneRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+SyncPointCreateRequest<librbd::MockTestImageCtx>* SyncPointCreateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+SyncPointPruneRequest<librbd::MockTestImageCtx>* SyncPointPruneRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image_sync
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::InvokeWithoutArgs;
+
+class TestMockImageSync : public TestMockFixture {
+public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef ImageSync<librbd::MockTestImageCtx> MockImageSync;
+ typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
+ typedef image_sync::SyncPointCreateRequest<librbd::MockTestImageCtx> MockSyncPointCreateRequest;
+ typedef image_sync::SyncPointPruneRequest<librbd::MockTestImageCtx> MockSyncPointPruneRequest;
+ typedef image_sync::MockSyncPointHandler MockSyncPointHandler;
+ typedef librbd::DeepCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+
+ ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+ ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+ }
+
+ void expect_get_snap_id(librbd::MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, get_snap_id(_, _))
+ .WillOnce(Return(123));
+ }
+
+ void expect_notify_sync_request(MockInstanceWatcher &mock_instance_watcher,
+ const std::string &sync_id, int r) {
+ EXPECT_CALL(mock_instance_watcher, notify_sync_request(sync_id, _))
+ .WillOnce(Invoke([this, r](const std::string &, Context *on_sync_start) {
+ m_threads->work_queue->queue(on_sync_start, r);
+ }));
+ }
+
+ void expect_cancel_sync_request(MockInstanceWatcher &mock_instance_watcher,
+ const std::string &sync_id, bool canceled) {
+ EXPECT_CALL(mock_instance_watcher, cancel_sync_request(sync_id))
+ .WillOnce(Return(canceled));
+ }
+
+ void expect_notify_sync_complete(MockInstanceWatcher &mock_instance_watcher,
+ const std::string &sync_id) {
+ EXPECT_CALL(mock_instance_watcher, notify_sync_complete(sync_id));
+ }
+
+ void expect_create_sync_point(librbd::MockTestImageCtx &mock_local_image_ctx,
+ MockSyncPointCreateRequest &mock_sync_point_create_request,
+ int r) {
+ EXPECT_CALL(mock_sync_point_create_request, send())
+ .WillOnce(Invoke([this, &mock_local_image_ctx, &mock_sync_point_create_request, r]() {
+ if (r == 0) {
+ mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(),
+ "snap1"}] = 123;
+ m_sync_points.emplace_back(cls::rbd::UserSnapshotNamespace(),
+ "snap1", "", boost::none);
+ }
+ m_threads->work_queue->queue(mock_sync_point_create_request.on_finish, r);
+ }));
+ }
+
+ void expect_copy_image(MockImageCopyRequest &mock_image_copy_request, int r) {
+ EXPECT_CALL(mock_image_copy_request, send())
+ .WillOnce(Invoke([this, &mock_image_copy_request, r]() {
+ m_threads->work_queue->queue(mock_image_copy_request.on_finish, r);
+ }));
+ }
+
+ void expect_flush_sync_point(MockSyncPointHandler& mock_sync_point_handler,
+ int r) {
+ EXPECT_CALL(mock_sync_point_handler, update_sync_points(_, _, false, _))
+ .WillOnce(WithArg<3>(CompleteContext(r)));
+ }
+
+ void expect_prune_sync_point(MockSyncPointPruneRequest &mock_sync_point_prune_request,
+ bool sync_complete, int r) {
+ EXPECT_CALL(mock_sync_point_prune_request, send())
+ .WillOnce(Invoke([this, &mock_sync_point_prune_request, sync_complete, r]() {
+ ASSERT_EQ(sync_complete, mock_sync_point_prune_request.sync_complete);
+ if (r == 0 && !m_sync_points.empty()) {
+ if (sync_complete) {
+ m_sync_points.pop_front();
+ } else {
+ while (m_sync_points.size() > 1) {
+ m_sync_points.pop_back();
+ }
+ }
+ }
+ m_threads->work_queue->queue(mock_sync_point_prune_request.on_finish, r);
+ }));
+ }
+
+ void expect_get_snap_seqs(MockSyncPointHandler& mock_sync_point_handler) {
+ EXPECT_CALL(mock_sync_point_handler, get_snap_seqs())
+ .WillRepeatedly(Return(librbd::SnapSeqs{}));
+ }
+
+ void expect_get_sync_points(MockSyncPointHandler& mock_sync_point_handler) {
+ EXPECT_CALL(mock_sync_point_handler, get_sync_points())
+ .WillRepeatedly(Invoke([this]() {
+ return m_sync_points;
+ }));
+ }
+
+ MockImageSync *create_request(MockThreads& mock_threads,
+ librbd::MockTestImageCtx &mock_remote_image_ctx,
+ librbd::MockTestImageCtx &mock_local_image_ctx,
+ MockSyncPointHandler& mock_sync_point_handler,
+ MockInstanceWatcher &mock_instance_watcher,
+ Context *ctx) {
+ return new MockImageSync(&mock_threads, &mock_local_image_ctx,
+ &mock_remote_image_ctx,
+ "mirror-uuid", &mock_sync_point_handler,
+ &mock_instance_watcher, nullptr, ctx);
+ }
+
+ librbd::ImageCtx *m_remote_image_ctx;
+ librbd::ImageCtx *m_local_image_ctx;
+
+ image_sync::SyncPoints m_sync_points;
+};
+
+TEST_F(TestMockImageSync, SimpleSync) {
+ MockThreads mock_threads(m_threads);
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageCopyRequest mock_image_copy_request;
+ MockSyncPointCreateRequest mock_sync_point_create_request;
+ MockSyncPointPruneRequest mock_sync_point_prune_request;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
+ expect_get_snap_id(mock_remote_image_ctx);
+ expect_copy_image(mock_image_copy_request, 0);
+ expect_flush_sync_point(mock_sync_point_handler, 0);
+ expect_prune_sync_point(mock_sync_point_prune_request, true, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ C_SaferCond ctx;
+ MockImageSync *request = create_request(mock_threads, mock_remote_image_ctx,
+ mock_local_image_ctx,
+ mock_sync_point_handler,
+ mock_instance_watcher, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageSync, RestartSync) {
+ MockThreads mock_threads(m_threads);
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageCopyRequest mock_image_copy_request;
+ MockSyncPointCreateRequest mock_sync_point_create_request;
+ MockSyncPointPruneRequest mock_sync_point_prune_request;
+
+ m_sync_points = {{cls::rbd::UserSnapshotNamespace(), "snap1", "", boost::none},
+ {cls::rbd::UserSnapshotNamespace(), "snap2", "snap1", boost::none}};
+ mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(), "snap1"}] = 123;
+ mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(), "snap2"}] = 234;
+
+ expect_test_features(mock_local_image_ctx);
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ expect_prune_sync_point(mock_sync_point_prune_request, false, 0);
+ expect_get_snap_id(mock_remote_image_ctx);
+ expect_copy_image(mock_image_copy_request, 0);
+ expect_flush_sync_point(mock_sync_point_handler, 0);
+ expect_prune_sync_point(mock_sync_point_prune_request, true, 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ C_SaferCond ctx;
+ MockImageSync *request = create_request(mock_threads, mock_remote_image_ctx,
+ mock_local_image_ctx,
+ mock_sync_point_handler,
+ mock_instance_watcher, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageSync, CancelNotifySyncRequest) {
+ MockThreads mock_threads(m_threads);
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+ MockInstanceWatcher mock_instance_watcher;
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ Context *on_sync_start = nullptr;
+ C_SaferCond notify_sync_ctx;
+ EXPECT_CALL(mock_instance_watcher,
+ notify_sync_request(mock_local_image_ctx.id, _))
+ .WillOnce(Invoke([&on_sync_start, &notify_sync_ctx](
+ const std::string &, Context *ctx) {
+ on_sync_start = ctx;
+ notify_sync_ctx.complete(0);
+ }));
+ EXPECT_CALL(mock_instance_watcher,
+ cancel_sync_request(mock_local_image_ctx.id))
+ .WillOnce(Invoke([&on_sync_start](const std::string &) {
+ EXPECT_NE(nullptr, on_sync_start);
+ on_sync_start->complete(-ECANCELED);
+ return true;
+ }));
+
+ C_SaferCond ctx;
+ MockImageSync *request = create_request(mock_threads, mock_remote_image_ctx,
+ mock_local_image_ctx,
+ mock_sync_point_handler,
+ mock_instance_watcher, &ctx);
+ request->get();
+ request->send();
+
+ // cancel the notify sync request once it starts
+ ASSERT_EQ(0, notify_sync_ctx.wait());
+ request->cancel();
+ request->put();
+
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockImageSync, CancelImageCopy) {
+ MockThreads mock_threads(m_threads);
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageCopyRequest mock_image_copy_request;
+ MockSyncPointCreateRequest mock_sync_point_create_request;
+ MockSyncPointPruneRequest mock_sync_point_prune_request;
+
+ m_sync_points = {{cls::rbd::UserSnapshotNamespace(), "snap1", "", boost::none}};
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ expect_prune_sync_point(mock_sync_point_prune_request, false, 0);
+ expect_get_snap_id(mock_remote_image_ctx);
+
+ C_SaferCond image_copy_ctx;
+ EXPECT_CALL(mock_image_copy_request, send())
+ .WillOnce(Invoke([&image_copy_ctx]() {
+ image_copy_ctx.complete(0);
+ }));
+ expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
+ false);
+ EXPECT_CALL(mock_image_copy_request, cancel());
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ C_SaferCond ctx;
+ MockImageSync *request = create_request(mock_threads, mock_remote_image_ctx,
+ mock_local_image_ctx,
+ mock_sync_point_handler,
+ mock_instance_watcher, &ctx);
+ request->get();
+ request->send();
+
+ // cancel the image copy once it starts
+ ASSERT_EQ(0, image_copy_ctx.wait());
+ request->cancel();
+ request->put();
+ m_threads->work_queue->queue(mock_image_copy_request.on_finish, 0);
+
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+TEST_F(TestMockImageSync, CancelAfterCopyImage) {
+ MockThreads mock_threads(m_threads);
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockSyncPointHandler mock_sync_point_handler;
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageCopyRequest mock_image_copy_request;
+ MockSyncPointCreateRequest mock_sync_point_create_request;
+ MockSyncPointPruneRequest mock_sync_point_prune_request;
+
+ C_SaferCond ctx;
+ MockImageSync *request = create_request(mock_threads, mock_remote_image_ctx,
+ mock_local_image_ctx,
+ mock_sync_point_handler,
+ mock_instance_watcher, &ctx);
+
+ expect_get_snap_seqs(mock_sync_point_handler);
+ expect_get_sync_points(mock_sync_point_handler);
+
+ InSequence seq;
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
+ expect_get_snap_id(mock_remote_image_ctx);
+ EXPECT_CALL(mock_image_copy_request, send())
+ .WillOnce((DoAll(InvokeWithoutArgs([request]() {
+ request->cancel();
+ }),
+ Invoke([this, &mock_image_copy_request]() {
+ m_threads->work_queue->queue(mock_image_copy_request.on_finish, 0);
+ }))));
+ expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
+ false);
+ EXPECT_CALL(mock_image_copy_request, cancel());
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ request->send();
+ ASSERT_EQ(-ECANCELED, ctx.wait());
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_InstanceReplayer.cc b/src/test/rbd_mirror/test_mock_InstanceReplayer.cc
new file mode 100644
index 000000000..1cc64be60
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_InstanceReplayer.cc
@@ -0,0 +1,382 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include "tools/rbd_mirror/ImageReplayer.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/InstanceReplayer.h"
+#include "tools/rbd_mirror/ServiceDaemon.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/image_replayer/Types.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+ ceph::condition_variable timer_cond;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+template<>
+struct ServiceDaemon<librbd::MockTestImageCtx> {
+ MOCK_METHOD4(add_or_update_namespace_attribute,
+ void(int64_t, const std::string&, const std::string&,
+ const service_daemon::AttributeValue&));
+};
+
+template<>
+struct InstanceWatcher<librbd::MockTestImageCtx> {
+};
+
+template<>
+struct ImageReplayer<librbd::MockTestImageCtx> {
+ static ImageReplayer* s_instance;
+ std::string global_image_id;
+
+ static ImageReplayer *create(
+ librados::IoCtx &local_io_ctx, const std::string &local_mirror_uuid,
+ const std::string &global_image_id,
+ Threads<librbd::MockTestImageCtx> *threads,
+ InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher,
+ MirrorStatusUpdater<librbd::MockTestImageCtx>* local_status_updater,
+ journal::CacheManagerHandler *cache_manager_handler,
+ PoolMetaCache* pool_meta_cache) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->global_image_id = global_image_id;
+ return s_instance;
+ }
+
+ ImageReplayer() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ virtual ~ImageReplayer() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(destroy, void());
+ MOCK_METHOD2(start, void(Context *, bool));
+ MOCK_METHOD2(stop, void(Context *, bool));
+ MOCK_METHOD1(restart, void(Context*));
+ MOCK_METHOD0(flush, void());
+ MOCK_METHOD1(print_status, void(Formatter *));
+ MOCK_METHOD1(add_peer, void(const Peer<librbd::MockTestImageCtx>& peer));
+ MOCK_METHOD0(get_global_image_id, const std::string &());
+ MOCK_METHOD0(get_local_image_id, const std::string &());
+ MOCK_METHOD0(is_running, bool());
+ MOCK_METHOD0(is_stopped, bool());
+ MOCK_METHOD0(is_blocklisted, bool());
+
+ MOCK_CONST_METHOD0(is_finished, bool());
+ MOCK_METHOD1(set_finished, void(bool));
+
+ MOCK_CONST_METHOD0(get_health_state, image_replayer::HealthState());
+};
+
+ImageReplayer<librbd::MockTestImageCtx>* ImageReplayer<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct MirrorStatusUpdater<librbd::MockTestImageCtx> {
+};
+
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/InstanceReplayer.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnArg;
+using ::testing::ReturnRef;
+using ::testing::WithArg;
+
+class TestMockInstanceReplayer : public TestMockFixture {
+public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef ImageReplayer<librbd::MockTestImageCtx> MockImageReplayer;
+ typedef InstanceReplayer<librbd::MockTestImageCtx> MockInstanceReplayer;
+ typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
+ typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater;
+ typedef ServiceDaemon<librbd::MockTestImageCtx> MockServiceDaemon;
+
+ void expect_work_queue(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillOnce(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_add_event_after(MockThreads &mock_threads,
+ Context** timer_ctx = nullptr) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillOnce(DoAll(
+ WithArg<1>(Invoke([this, &mock_threads, timer_ctx](Context *ctx) {
+ ceph_assert(ceph_mutex_is_locked(mock_threads.timer_lock));
+ if (timer_ctx != nullptr) {
+ *timer_ctx = ctx;
+ mock_threads.timer_cond.notify_one();
+ } else {
+ m_threads->work_queue->queue(
+ new LambdaContext([&mock_threads, ctx](int) {
+ std::lock_guard timer_lock{mock_threads.timer_lock};
+ ctx->complete(0);
+ }), 0);
+ }
+ })),
+ ReturnArg<1>()));
+ }
+
+ void expect_cancel_event(MockThreads &mock_threads, bool canceled) {
+ EXPECT_CALL(*mock_threads.timer, cancel_event(_))
+ .WillOnce(Return(canceled));
+ }
+};
+
+TEST_F(TestMockInstanceReplayer, AcquireReleaseImage) {
+ MockThreads mock_threads(m_threads);
+ MockServiceDaemon mock_service_daemon;
+ MockMirrorStatusUpdater mock_status_updater;
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageReplayer mock_image_replayer;
+ MockInstanceReplayer instance_replayer(
+ m_local_io_ctx, "local_mirror_uuid",
+ &mock_threads, &mock_service_daemon, &mock_status_updater, nullptr,
+ nullptr);
+ std::string global_image_id("global_image_id");
+
+ EXPECT_CALL(mock_image_replayer, get_global_image_id())
+ .WillRepeatedly(ReturnRef(global_image_id));
+
+ InSequence seq;
+ expect_work_queue(mock_threads);
+ Context *timer_ctx = nullptr;
+ expect_add_event_after(mock_threads, &timer_ctx);
+ instance_replayer.init();
+ instance_replayer.add_peer({"peer_uuid", m_remote_io_ctx, {}, nullptr});
+
+ // Acquire
+
+ C_SaferCond on_acquire;
+ EXPECT_CALL(mock_image_replayer, add_peer(_));
+ EXPECT_CALL(mock_image_replayer, is_stopped()).WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, is_blocklisted()).WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, is_finished()).WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, start(_, false))
+ .WillOnce(CompleteContext(0));
+ expect_work_queue(mock_threads);
+
+ instance_replayer.acquire_image(&mock_instance_watcher, global_image_id,
+ &on_acquire);
+ ASSERT_EQ(0, on_acquire.wait());
+
+ // Release
+
+ C_SaferCond on_release;
+
+ EXPECT_CALL(mock_image_replayer, is_stopped())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, is_running())
+ .WillOnce(Return(false));
+ expect_work_queue(mock_threads);
+ expect_add_event_after(mock_threads);
+ expect_work_queue(mock_threads);
+ EXPECT_CALL(mock_image_replayer, is_stopped())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, is_running())
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, stop(_, false))
+ .WillOnce(CompleteContext(0));
+ expect_work_queue(mock_threads);
+ EXPECT_CALL(mock_image_replayer, is_stopped())
+ .WillOnce(Return(true));
+ expect_work_queue(mock_threads);
+ EXPECT_CALL(mock_image_replayer, destroy());
+
+ instance_replayer.release_image("global_image_id", &on_release);
+ ASSERT_EQ(0, on_release.wait());
+
+ expect_work_queue(mock_threads);
+ expect_cancel_event(mock_threads, true);
+ expect_work_queue(mock_threads);
+ instance_replayer.shut_down();
+ ASSERT_TRUE(timer_ctx != nullptr);
+ delete timer_ctx;
+}
+
+TEST_F(TestMockInstanceReplayer, RemoveFinishedImage) {
+ MockThreads mock_threads(m_threads);
+ MockServiceDaemon mock_service_daemon;
+ MockMirrorStatusUpdater mock_status_updater;
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageReplayer mock_image_replayer;
+ MockInstanceReplayer instance_replayer(
+ m_local_io_ctx, "local_mirror_uuid",
+ &mock_threads, &mock_service_daemon, &mock_status_updater, nullptr,
+ nullptr);
+ std::string global_image_id("global_image_id");
+
+ EXPECT_CALL(mock_image_replayer, get_global_image_id())
+ .WillRepeatedly(ReturnRef(global_image_id));
+
+ InSequence seq;
+ expect_work_queue(mock_threads);
+ Context *timer_ctx1 = nullptr;
+ expect_add_event_after(mock_threads, &timer_ctx1);
+ instance_replayer.init();
+ instance_replayer.add_peer({"peer_uuid", m_remote_io_ctx, {}, nullptr});
+
+ // Acquire
+
+ C_SaferCond on_acquire;
+ EXPECT_CALL(mock_image_replayer, add_peer(_));
+ EXPECT_CALL(mock_image_replayer, is_stopped()).WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, is_blocklisted()).WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, is_finished()).WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, start(_, false))
+ .WillOnce(CompleteContext(0));
+ expect_work_queue(mock_threads);
+
+ instance_replayer.acquire_image(&mock_instance_watcher, global_image_id,
+ &on_acquire);
+ ASSERT_EQ(0, on_acquire.wait());
+
+ // periodic start timer
+ Context *timer_ctx2 = nullptr;
+ expect_add_event_after(mock_threads, &timer_ctx2);
+
+ Context *start_image_replayers_ctx = nullptr;
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, 0))
+ .WillOnce(Invoke([&start_image_replayers_ctx](Context *ctx, int r) {
+ start_image_replayers_ctx = ctx;
+ }));
+
+ ASSERT_TRUE(timer_ctx1 != nullptr);
+ {
+ std::lock_guard timer_locker{mock_threads.timer_lock};
+ timer_ctx1->complete(0);
+ }
+
+ // remove finished image replayer
+ EXPECT_CALL(mock_image_replayer, get_health_state()).WillOnce(
+ Return(image_replayer::HEALTH_STATE_OK));
+ EXPECT_CALL(mock_image_replayer, is_stopped()).WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, is_blocklisted()).WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, is_finished()).WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, destroy());
+ EXPECT_CALL(mock_service_daemon,
+ add_or_update_namespace_attribute(_, _, _, _)).Times(3);
+
+ ASSERT_TRUE(start_image_replayers_ctx != nullptr);
+ start_image_replayers_ctx->complete(0);
+
+ // shut down
+ expect_work_queue(mock_threads);
+ expect_cancel_event(mock_threads, true);
+ expect_work_queue(mock_threads);
+ instance_replayer.shut_down();
+ ASSERT_TRUE(timer_ctx2 != nullptr);
+ delete timer_ctx2;
+}
+
+TEST_F(TestMockInstanceReplayer, Reacquire) {
+ MockThreads mock_threads(m_threads);
+ MockServiceDaemon mock_service_daemon;
+ MockMirrorStatusUpdater mock_status_updater;
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageReplayer mock_image_replayer;
+ MockInstanceReplayer instance_replayer(
+ m_local_io_ctx, "local_mirror_uuid",
+ &mock_threads, &mock_service_daemon, &mock_status_updater, nullptr,
+ nullptr);
+ std::string global_image_id("global_image_id");
+
+ EXPECT_CALL(mock_image_replayer, get_global_image_id())
+ .WillRepeatedly(ReturnRef(global_image_id));
+
+ InSequence seq;
+ expect_work_queue(mock_threads);
+ Context *timer_ctx = nullptr;
+ expect_add_event_after(mock_threads, &timer_ctx);
+ instance_replayer.init();
+ instance_replayer.add_peer({"peer_uuid", m_remote_io_ctx, {}, nullptr});
+
+ // Acquire
+
+ EXPECT_CALL(mock_image_replayer, add_peer(_));
+ EXPECT_CALL(mock_image_replayer, is_stopped()).WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, is_blocklisted()).WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, is_finished()).WillOnce(Return(false));
+ EXPECT_CALL(mock_image_replayer, start(_, false))
+ .WillOnce(CompleteContext(0));
+ expect_work_queue(mock_threads);
+
+ C_SaferCond on_acquire1;
+ instance_replayer.acquire_image(&mock_instance_watcher, global_image_id,
+ &on_acquire1);
+ ASSERT_EQ(0, on_acquire1.wait());
+
+ // Re-acquire
+ EXPECT_CALL(mock_image_replayer, set_finished(false));
+ EXPECT_CALL(mock_image_replayer, restart(_))
+ .WillOnce(CompleteContext(0));
+ expect_work_queue(mock_threads);
+
+ C_SaferCond on_acquire2;
+ instance_replayer.acquire_image(&mock_instance_watcher, global_image_id,
+ &on_acquire2);
+ ASSERT_EQ(0, on_acquire2.wait());
+
+ expect_work_queue(mock_threads);
+ expect_cancel_event(mock_threads, true);
+ EXPECT_CALL(mock_image_replayer, is_stopped()).WillOnce(Return(true));
+ expect_work_queue(mock_threads);
+ expect_work_queue(mock_threads);
+ EXPECT_CALL(mock_image_replayer, is_stopped()).WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, destroy());
+ instance_replayer.shut_down();
+ ASSERT_TRUE(timer_ctx != nullptr);
+ delete timer_ctx;
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_InstanceWatcher.cc b/src/test/rbd_mirror/test_mock_InstanceWatcher.cc
new file mode 100644
index 000000000..f57654b36
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_InstanceWatcher.cc
@@ -0,0 +1,987 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librados/AioCompletionImpl.h"
+#include "librbd/ManagedLock.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "tools/rbd_mirror/InstanceReplayer.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+template <>
+struct ManagedLock<MockTestImageCtx> {
+ static ManagedLock* s_instance;
+
+ static ManagedLock *create(librados::IoCtx& ioctx,
+ librbd::AsioEngine& asio_engine,
+ const std::string& oid, librbd::Watcher *watcher,
+ managed_lock::Mode mode,
+ bool blocklist_on_break_lock,
+ uint32_t blocklist_expire_seconds) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ ManagedLock() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~ManagedLock() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(destroy, void());
+ MOCK_METHOD1(shut_down, void(Context *));
+ MOCK_METHOD1(acquire_lock, void(Context *));
+ MOCK_METHOD2(get_locker, void(managed_lock::Locker *, Context *));
+ MOCK_METHOD3(break_lock, void(const managed_lock::Locker &, bool, Context *));
+};
+
+ManagedLock<MockTestImageCtx> *ManagedLock<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+ librbd::AsioEngine* asio_engine;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue), asio_engine(threads->asio_engine) {
+ }
+};
+
+template <>
+struct InstanceReplayer<librbd::MockTestImageCtx> {
+ MOCK_METHOD3(acquire_image, void(InstanceWatcher<librbd::MockTestImageCtx> *,
+ const std::string &, Context *));
+ MOCK_METHOD2(release_image, void(const std::string &, Context *));
+ MOCK_METHOD3(remove_peer_image, void(const std::string&, const std::string&,
+ Context *));
+};
+
+template <>
+struct Throttler<librbd::MockTestImageCtx> {
+ static Throttler* s_instance;
+
+ Throttler() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ virtual ~Throttler() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD3(start_op, void(const std::string &, const std::string &,
+ Context *));
+ MOCK_METHOD2(finish_op, void(const std::string &, const std::string &));
+ MOCK_METHOD2(drain, void(const std::string &, int));
+};
+
+Throttler<librbd::MockTestImageCtx>* Throttler<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/InstanceWatcher.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockInstanceWatcher : public TestMockFixture {
+public:
+ typedef librbd::ManagedLock<librbd::MockTestImageCtx> MockManagedLock;
+ typedef InstanceReplayer<librbd::MockTestImageCtx> MockInstanceReplayer;
+ typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+
+ std::string m_instance_id;
+ std::string m_oid;
+ MockThreads *m_mock_threads;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ m_local_io_ctx.remove(RBD_MIRROR_LEADER);
+ EXPECT_EQ(0, m_local_io_ctx.create(RBD_MIRROR_LEADER, true));
+
+ m_instance_id = stringify(m_local_io_ctx.get_instance_id());
+ m_oid = RBD_MIRROR_INSTANCE_PREFIX + m_instance_id;
+
+ m_mock_threads = new MockThreads(m_threads);
+ }
+
+ void TearDown() override {
+ delete m_mock_threads;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_register_watch(librados::MockTestMemIoCtxImpl &mock_io_ctx) {
+ EXPECT_CALL(mock_io_ctx, aio_watch(m_oid, _, _, _));
+ }
+
+ void expect_register_watch(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ const std::string &instance_id) {
+ std::string oid = RBD_MIRROR_INSTANCE_PREFIX + instance_id;
+ EXPECT_CALL(mock_io_ctx, aio_watch(oid, _, _, _));
+ }
+
+ void expect_unregister_watch(librados::MockTestMemIoCtxImpl &mock_io_ctx) {
+ EXPECT_CALL(mock_io_ctx, aio_unwatch(_, _));
+ }
+
+ void expect_register_instance(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ int r) {
+ EXPECT_CALL(mock_io_ctx, exec(RBD_MIRROR_LEADER, _, StrEq("rbd"),
+ StrEq("mirror_instances_add"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_unregister_instance(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+ int r) {
+ EXPECT_CALL(mock_io_ctx, exec(RBD_MIRROR_LEADER, _, StrEq("rbd"),
+ StrEq("mirror_instances_remove"), _, _, _, _))
+ .WillOnce(Return(r));
+ }
+
+ void expect_acquire_lock(MockManagedLock &mock_managed_lock, int r) {
+ EXPECT_CALL(mock_managed_lock, acquire_lock(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_release_lock(MockManagedLock &mock_managed_lock, int r) {
+ EXPECT_CALL(mock_managed_lock, shut_down(_)).WillOnce(CompleteContext(r));
+ }
+
+ void expect_destroy_lock(MockManagedLock &mock_managed_lock,
+ Context *ctx = nullptr) {
+ EXPECT_CALL(mock_managed_lock, destroy())
+ .WillOnce(Invoke([ctx]() {
+ if (ctx != nullptr) {
+ ctx->complete(0);
+ }
+ }));
+ }
+
+ void expect_get_locker(MockManagedLock &mock_managed_lock,
+ const librbd::managed_lock::Locker &locker, int r) {
+ EXPECT_CALL(mock_managed_lock, get_locker(_, _))
+ .WillOnce(Invoke([r, locker](librbd::managed_lock::Locker *out,
+ Context *ctx) {
+ if (r == 0) {
+ *out = locker;
+ }
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_break_lock(MockManagedLock &mock_managed_lock,
+ const librbd::managed_lock::Locker &locker, int r) {
+ EXPECT_CALL(mock_managed_lock, break_lock(locker, true, _))
+ .WillOnce(WithArg<2>(CompleteContext(r)));
+ }
+};
+
+TEST_F(TestMockInstanceWatcher, InitShutdown) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ auto instance_watcher = new MockInstanceWatcher(
+ m_local_io_ctx, *m_mock_threads->asio_engine, nullptr, nullptr,
+ m_instance_id);
+ InSequence seq;
+
+ // Init
+ expect_register_instance(mock_io_ctx, 0);
+ expect_register_watch(mock_io_ctx);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher->init());
+
+ // Shutdown
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx);
+ expect_unregister_instance(mock_io_ctx, 0);
+ instance_watcher->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher;
+}
+
+TEST_F(TestMockInstanceWatcher, InitError) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ auto instance_watcher = new MockInstanceWatcher(
+ m_local_io_ctx, *m_mock_threads->asio_engine, nullptr, nullptr,
+ m_instance_id);
+ InSequence seq;
+
+ expect_register_instance(mock_io_ctx, 0);
+ expect_register_watch(mock_io_ctx);
+ expect_acquire_lock(mock_managed_lock, -EINVAL);
+ expect_unregister_watch(mock_io_ctx);
+ expect_unregister_instance(mock_io_ctx, 0);
+
+ ASSERT_EQ(-EINVAL, instance_watcher->init());
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher;
+}
+
+TEST_F(TestMockInstanceWatcher, ShutdownError) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ auto instance_watcher = new MockInstanceWatcher(
+ m_local_io_ctx, *m_mock_threads->asio_engine, nullptr, nullptr,
+ m_instance_id);
+ InSequence seq;
+
+ // Init
+ expect_register_instance(mock_io_ctx, 0);
+ expect_register_watch(mock_io_ctx);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher->init());
+
+ // Shutdown
+ expect_release_lock(mock_managed_lock, -EINVAL);
+ expect_unregister_watch(mock_io_ctx);
+ expect_unregister_instance(mock_io_ctx, 0);
+ instance_watcher->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher;
+}
+
+
+TEST_F(TestMockInstanceWatcher, Remove) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+ librbd::managed_lock::Locker
+ locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+
+ InSequence seq;
+
+ expect_get_locker(mock_managed_lock, locker, 0);
+ expect_break_lock(mock_managed_lock, locker, 0);
+ expect_unregister_instance(mock_io_ctx, 0);
+ C_SaferCond on_destroy;
+ expect_destroy_lock(mock_managed_lock, &on_destroy);
+
+ C_SaferCond on_remove;
+ MockInstanceWatcher::remove_instance(m_local_io_ctx,
+ *m_mock_threads->asio_engine,
+ "instance_id", &on_remove);
+ ASSERT_EQ(0, on_remove.wait());
+ ASSERT_EQ(0, on_destroy.wait());
+}
+
+TEST_F(TestMockInstanceWatcher, RemoveNoent) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ InSequence seq;
+
+ expect_get_locker(mock_managed_lock, librbd::managed_lock::Locker(), -ENOENT);
+ expect_unregister_instance(mock_io_ctx, 0);
+ C_SaferCond on_destroy;
+ expect_destroy_lock(mock_managed_lock, &on_destroy);
+
+ C_SaferCond on_remove;
+ MockInstanceWatcher::remove_instance(m_local_io_ctx,
+ *m_mock_threads->asio_engine,
+ "instance_id", &on_remove);
+ ASSERT_EQ(0, on_remove.wait());
+ ASSERT_EQ(0, on_destroy.wait());
+}
+
+TEST_F(TestMockInstanceWatcher, ImageAcquireRelease) {
+ MockManagedLock mock_managed_lock;
+
+ librados::IoCtx& io_ctx1 = m_local_io_ctx;
+ std::string instance_id1 = m_instance_id;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx1(get_mock_io_ctx(io_ctx1));
+ MockInstanceReplayer mock_instance_replayer1;
+ auto instance_watcher1 = MockInstanceWatcher::create(
+ io_ctx1, *m_mock_threads->asio_engine, &mock_instance_replayer1, nullptr);
+
+ librados::Rados cluster;
+ librados::IoCtx io_ctx2;
+ EXPECT_EQ("", connect_cluster_pp(cluster));
+ EXPECT_EQ(0, cluster.ioctx_create(_local_pool_name.c_str(), io_ctx2));
+ std::string instance_id2 = stringify(io_ctx2.get_instance_id());
+ librados::MockTestMemIoCtxImpl &mock_io_ctx2(get_mock_io_ctx(io_ctx2));
+ MockInstanceReplayer mock_instance_replayer2;
+ auto instance_watcher2 = MockInstanceWatcher::create(
+ io_ctx2, *m_mock_threads->asio_engine, &mock_instance_replayer2, nullptr);
+
+ InSequence seq;
+
+ // Init instance watcher 1
+ expect_register_instance(mock_io_ctx1, 0);
+ expect_register_watch(mock_io_ctx1, instance_id1);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher1->init());
+
+ // Init instance watcher 2
+ expect_register_instance(mock_io_ctx2, 0);
+ expect_register_watch(mock_io_ctx2, instance_id2);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher2->init());
+
+ // Acquire Image on the the same instance
+ EXPECT_CALL(mock_instance_replayer1, acquire_image(instance_watcher1, "gid",
+ _))
+ .WillOnce(WithArg<2>(CompleteContext(0)));
+ C_SaferCond on_acquire1;
+ instance_watcher1->notify_image_acquire(instance_id1, "gid", &on_acquire1);
+ ASSERT_EQ(0, on_acquire1.wait());
+
+ // Acquire Image on the other instance
+ EXPECT_CALL(mock_instance_replayer2, acquire_image(instance_watcher2, "gid",
+ _))
+ .WillOnce(WithArg<2>(CompleteContext(0)));
+ C_SaferCond on_acquire2;
+ instance_watcher1->notify_image_acquire(instance_id2, "gid", &on_acquire2);
+ ASSERT_EQ(0, on_acquire2.wait());
+
+ // Release Image on the the same instance
+ EXPECT_CALL(mock_instance_replayer1, release_image("gid", _))
+ .WillOnce(WithArg<1>(CompleteContext(0)));
+ C_SaferCond on_release1;
+ instance_watcher1->notify_image_release(instance_id1, "gid", &on_release1);
+ ASSERT_EQ(0, on_release1.wait());
+
+ // Release Image on the other instance
+ EXPECT_CALL(mock_instance_replayer2, release_image("gid", _))
+ .WillOnce(WithArg<1>(CompleteContext(0)));
+ C_SaferCond on_release2;
+ instance_watcher1->notify_image_release(instance_id2, "gid", &on_release2);
+ ASSERT_EQ(0, on_release2.wait());
+
+ // Shutdown instance watcher 1
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx1);
+ expect_unregister_instance(mock_io_ctx1, 0);
+ instance_watcher1->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher1;
+
+ // Shutdown instance watcher 2
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx2);
+ expect_unregister_instance(mock_io_ctx2, 0);
+ instance_watcher2->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher2;
+}
+
+TEST_F(TestMockInstanceWatcher, PeerImageRemoved) {
+ MockManagedLock mock_managed_lock;
+
+ librados::IoCtx& io_ctx1 = m_local_io_ctx;
+ std::string instance_id1 = m_instance_id;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx1(get_mock_io_ctx(io_ctx1));
+ MockInstanceReplayer mock_instance_replayer1;
+ auto instance_watcher1 = MockInstanceWatcher::create(
+ io_ctx1, *m_mock_threads->asio_engine, &mock_instance_replayer1, nullptr);
+
+ librados::Rados cluster;
+ librados::IoCtx io_ctx2;
+ EXPECT_EQ("", connect_cluster_pp(cluster));
+ EXPECT_EQ(0, cluster.ioctx_create(_local_pool_name.c_str(), io_ctx2));
+ std::string instance_id2 = stringify(io_ctx2.get_instance_id());
+ librados::MockTestMemIoCtxImpl &mock_io_ctx2(get_mock_io_ctx(io_ctx2));
+ MockInstanceReplayer mock_instance_replayer2;
+ auto instance_watcher2 = MockInstanceWatcher::create(
+ io_ctx2, *m_mock_threads->asio_engine, &mock_instance_replayer2, nullptr);
+
+ InSequence seq;
+
+ // Init instance watcher 1
+ expect_register_instance(mock_io_ctx1, 0);
+ expect_register_watch(mock_io_ctx1, instance_id1);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher1->init());
+
+ // Init instance watcher 2
+ expect_register_instance(mock_io_ctx2, 0);
+ expect_register_watch(mock_io_ctx2, instance_id2);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher2->init());
+
+ // Peer Image Removed on the same instance
+ EXPECT_CALL(mock_instance_replayer1, remove_peer_image("gid", "uuid", _))
+ .WillOnce(WithArg<2>(CompleteContext(0)));
+ C_SaferCond on_removed1;
+ instance_watcher1->notify_peer_image_removed(instance_id1, "gid", "uuid",
+ &on_removed1);
+ ASSERT_EQ(0, on_removed1.wait());
+
+ // Peer Image Removed on the other instance
+ EXPECT_CALL(mock_instance_replayer2, remove_peer_image("gid", "uuid", _))
+ .WillOnce(WithArg<2>(CompleteContext(0)));
+ C_SaferCond on_removed2;
+ instance_watcher1->notify_peer_image_removed(instance_id2, "gid", "uuid",
+ &on_removed2);
+ ASSERT_EQ(0, on_removed2.wait());
+
+ // Shutdown instance watcher 1
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx1);
+ expect_unregister_instance(mock_io_ctx1, 0);
+ instance_watcher1->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher1;
+
+ // Shutdown instance watcher 2
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx2);
+ expect_unregister_instance(mock_io_ctx2, 0);
+ instance_watcher2->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher2;
+}
+
+TEST_F(TestMockInstanceWatcher, ImageAcquireReleaseCancel) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ auto instance_watcher = new MockInstanceWatcher(
+ m_local_io_ctx, *m_mock_threads->asio_engine, nullptr, nullptr,
+ m_instance_id);
+ InSequence seq;
+
+ // Init
+ expect_register_instance(mock_io_ctx, 0);
+ expect_register_watch(mock_io_ctx);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher->init());
+
+ // Send Acquire Image and cancel
+ EXPECT_CALL(mock_io_ctx, aio_notify(_, _, _, _, _))
+ .WillOnce(Invoke(
+ [this, instance_watcher, &mock_io_ctx](
+ const std::string& o, librados::AioCompletionImpl *c,
+ bufferlist& bl, uint64_t timeout_ms, bufferlist *pbl) {
+ c->get();
+ auto ctx = new LambdaContext(
+ [instance_watcher, &mock_io_ctx, c, pbl](int r) {
+ instance_watcher->cancel_notify_requests("other");
+ encode(librbd::watcher::NotifyResponse(), *pbl);
+ mock_io_ctx.get_mock_rados_client()->
+ finish_aio_completion(c, -ETIMEDOUT);
+ });
+ m_threads->work_queue->queue(ctx, 0);
+ }));
+
+ C_SaferCond on_acquire;
+ instance_watcher->notify_image_acquire("other", "gid", &on_acquire);
+ ASSERT_EQ(-ECANCELED, on_acquire.wait());
+
+ // Send Release Image and cancel
+ EXPECT_CALL(mock_io_ctx, aio_notify(_, _, _, _, _))
+ .WillOnce(Invoke(
+ [this, instance_watcher, &mock_io_ctx](
+ const std::string& o, librados::AioCompletionImpl *c,
+ bufferlist& bl, uint64_t timeout_ms, bufferlist *pbl) {
+ c->get();
+ auto ctx = new LambdaContext(
+ [instance_watcher, &mock_io_ctx, c, pbl](int r) {
+ instance_watcher->cancel_notify_requests("other");
+ encode(librbd::watcher::NotifyResponse(), *pbl);
+ mock_io_ctx.get_mock_rados_client()->
+ finish_aio_completion(c, -ETIMEDOUT);
+ });
+ m_threads->work_queue->queue(ctx, 0);
+ }));
+
+ C_SaferCond on_release;
+ instance_watcher->notify_image_release("other", "gid", &on_release);
+ ASSERT_EQ(-ECANCELED, on_release.wait());
+
+ // Shutdown
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx);
+ expect_unregister_instance(mock_io_ctx, 0);
+ instance_watcher->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher;
+}
+
+TEST_F(TestMockInstanceWatcher, PeerImageAcquireWatchDNE) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ MockInstanceReplayer mock_instance_replayer;
+ auto instance_watcher = new MockInstanceWatcher(
+ m_local_io_ctx, *m_mock_threads->asio_engine, &mock_instance_replayer,
+ nullptr, m_instance_id);
+ InSequence seq;
+
+ // Init
+ expect_register_instance(mock_io_ctx, 0);
+ expect_register_watch(mock_io_ctx);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher->init());
+
+ // Acquire image on dead (blocklisted) instance
+ C_SaferCond on_acquire;
+ instance_watcher->notify_image_acquire("dead instance", "global image id",
+ &on_acquire);
+ ASSERT_EQ(-ENOENT, on_acquire.wait());
+
+ // Shutdown
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx);
+ expect_unregister_instance(mock_io_ctx, 0);
+ instance_watcher->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher;
+}
+
+TEST_F(TestMockInstanceWatcher, PeerImageReleaseWatchDNE) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ MockInstanceReplayer mock_instance_replayer;
+ auto instance_watcher = new MockInstanceWatcher(
+ m_local_io_ctx, *m_mock_threads->asio_engine, &mock_instance_replayer,
+ nullptr, m_instance_id);
+ InSequence seq;
+
+ // Init
+ expect_register_instance(mock_io_ctx, 0);
+ expect_register_watch(mock_io_ctx);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher->init());
+
+ // Release image on dead (blocklisted) instance
+ C_SaferCond on_acquire;
+ instance_watcher->notify_image_release("dead instance", "global image id",
+ &on_acquire);
+ ASSERT_EQ(-ENOENT, on_acquire.wait());
+
+ // Shutdown
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx);
+ expect_unregister_instance(mock_io_ctx, 0);
+ instance_watcher->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher;
+}
+
+TEST_F(TestMockInstanceWatcher, PeerImageRemovedCancel) {
+ MockManagedLock mock_managed_lock;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_local_io_ctx));
+
+ auto instance_watcher = new MockInstanceWatcher(
+ m_local_io_ctx, *m_mock_threads->asio_engine, nullptr, nullptr,
+ m_instance_id);
+ InSequence seq;
+
+ // Init
+ expect_register_instance(mock_io_ctx, 0);
+ expect_register_watch(mock_io_ctx);
+ expect_acquire_lock(mock_managed_lock, 0);
+ ASSERT_EQ(0, instance_watcher->init());
+
+ // Send Acquire Image and cancel
+ EXPECT_CALL(mock_io_ctx, aio_notify(_, _, _, _, _))
+ .WillOnce(Invoke(
+ [this, instance_watcher, &mock_io_ctx](
+ const std::string& o, librados::AioCompletionImpl *c,
+ bufferlist& bl, uint64_t timeout_ms, bufferlist *pbl) {
+ c->get();
+ auto ctx = new LambdaContext(
+ [instance_watcher, &mock_io_ctx, c, pbl](int r) {
+ instance_watcher->cancel_notify_requests("other");
+ encode(librbd::watcher::NotifyResponse(), *pbl);
+ mock_io_ctx.get_mock_rados_client()->
+ finish_aio_completion(c, -ETIMEDOUT);
+ });
+ m_threads->work_queue->queue(ctx, 0);
+ }));
+
+ C_SaferCond on_acquire;
+ instance_watcher->notify_peer_image_removed("other", "gid", "uuid",
+ &on_acquire);
+ ASSERT_EQ(-ECANCELED, on_acquire.wait());
+
+ // Shutdown
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx);
+ expect_unregister_instance(mock_io_ctx, 0);
+ instance_watcher->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher;
+}
+
+class TestMockInstanceWatcher_NotifySync : public TestMockInstanceWatcher {
+public:
+ typedef Throttler<librbd::MockTestImageCtx> MockThrottler;
+
+ MockManagedLock mock_managed_lock;
+ MockThrottler mock_image_sync_throttler;
+ std::string instance_id1;
+ std::string instance_id2;
+
+ librados::Rados cluster;
+ librados::IoCtx io_ctx2;
+
+ MockInstanceWatcher *instance_watcher1;
+ MockInstanceWatcher *instance_watcher2;
+
+ void SetUp() override {
+ TestMockInstanceWatcher::SetUp();
+
+ instance_id1 = m_instance_id;
+ librados::IoCtx& io_ctx1 = m_local_io_ctx;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx1(get_mock_io_ctx(io_ctx1));
+ instance_watcher1 = MockInstanceWatcher::create(io_ctx1,
+ *m_mock_threads->asio_engine,
+ nullptr,
+ &mock_image_sync_throttler);
+ EXPECT_EQ("", connect_cluster_pp(cluster));
+ EXPECT_EQ(0, cluster.ioctx_create(_local_pool_name.c_str(), io_ctx2));
+ instance_id2 = stringify(io_ctx2.get_instance_id());
+ librados::MockTestMemIoCtxImpl &mock_io_ctx2(get_mock_io_ctx(io_ctx2));
+ instance_watcher2 = MockInstanceWatcher::create(io_ctx2,
+ *m_mock_threads->asio_engine,
+ nullptr,
+ &mock_image_sync_throttler);
+ InSequence seq;
+
+ // Init instance watcher 1 (leader)
+ expect_register_instance(mock_io_ctx1, 0);
+ expect_register_watch(mock_io_ctx1, instance_id1);
+ expect_acquire_lock(mock_managed_lock, 0);
+ EXPECT_EQ(0, instance_watcher1->init());
+ instance_watcher1->handle_acquire_leader();
+
+ // Init instance watcher 2
+ expect_register_instance(mock_io_ctx2, 0);
+ expect_register_watch(mock_io_ctx2, instance_id2);
+ expect_acquire_lock(mock_managed_lock, 0);
+ EXPECT_EQ(0, instance_watcher2->init());
+ instance_watcher2->handle_update_leader(instance_id1);
+ }
+
+ void TearDown() override {
+ librados::IoCtx& io_ctx1 = m_local_io_ctx;
+ librados::MockTestMemIoCtxImpl &mock_io_ctx1(get_mock_io_ctx(io_ctx1));
+ librados::MockTestMemIoCtxImpl &mock_io_ctx2(get_mock_io_ctx(io_ctx2));
+
+ InSequence seq;
+
+ expect_throttler_drain();
+ instance_watcher1->handle_release_leader();
+
+ // Shutdown instance watcher 1
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx1);
+ expect_unregister_instance(mock_io_ctx1, 0);
+ instance_watcher1->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher1;
+
+ // Shutdown instance watcher 2
+ expect_release_lock(mock_managed_lock, 0);
+ expect_unregister_watch(mock_io_ctx2);
+ expect_unregister_instance(mock_io_ctx2, 0);
+ instance_watcher2->shut_down();
+
+ expect_destroy_lock(mock_managed_lock);
+ delete instance_watcher2;
+
+ TestMockInstanceWatcher::TearDown();
+ }
+
+ void expect_throttler_start_op(const std::string &sync_id,
+ Context *on_call = nullptr,
+ Context **on_start_ctx = nullptr) {
+ EXPECT_CALL(mock_image_sync_throttler, start_op("", sync_id, _))
+ .WillOnce(Invoke([on_call, on_start_ctx] (const std::string &,
+ const std::string &,
+ Context *ctx) {
+ if (on_start_ctx != nullptr) {
+ *on_start_ctx = ctx;
+ } else {
+ ctx->complete(0);
+ }
+ if (on_call != nullptr) {
+ on_call->complete(0);
+ }
+ }));
+ }
+
+ void expect_throttler_finish_op(const std::string &sync_id,
+ Context *on_finish) {
+ EXPECT_CALL(mock_image_sync_throttler, finish_op("", "sync_id"))
+ .WillOnce(Invoke([on_finish](const std::string &, const std::string &) {
+ on_finish->complete(0);
+ }));
+ }
+
+ void expect_throttler_drain() {
+ EXPECT_CALL(mock_image_sync_throttler, drain("", -ESTALE));
+ }
+};
+
+TEST_F(TestMockInstanceWatcher_NotifySync, StartStopOnLeader) {
+ InSequence seq;
+
+ expect_throttler_start_op("sync_id");
+ C_SaferCond on_start;
+ instance_watcher1->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+
+ C_SaferCond on_finish;
+ expect_throttler_finish_op("sync_id", &on_finish);
+ instance_watcher1->notify_sync_complete("sync_id");
+ ASSERT_EQ(0, on_finish.wait());
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, CancelStartedOnLeader) {
+ InSequence seq;
+
+ expect_throttler_start_op("sync_id");
+ C_SaferCond on_start;
+ instance_watcher1->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+
+ ASSERT_FALSE(instance_watcher1->cancel_sync_request("sync_id"));
+
+ C_SaferCond on_finish;
+ expect_throttler_finish_op("sync_id", &on_finish);
+ instance_watcher1->notify_sync_complete("sync_id");
+ ASSERT_EQ(0, on_finish.wait());
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, StartStopOnNonLeader) {
+ InSequence seq;
+
+ expect_throttler_start_op("sync_id");
+ C_SaferCond on_start;
+ instance_watcher2->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+
+ C_SaferCond on_finish;
+ expect_throttler_finish_op("sync_id", &on_finish);
+ instance_watcher2->notify_sync_complete("sync_id");
+ ASSERT_EQ(0, on_finish.wait());
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, CancelStartedOnNonLeader) {
+ InSequence seq;
+
+ expect_throttler_start_op("sync_id");
+ C_SaferCond on_start;
+ instance_watcher2->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+
+ ASSERT_FALSE(instance_watcher2->cancel_sync_request("sync_id"));
+
+ C_SaferCond on_finish;
+ expect_throttler_finish_op("sync_id", &on_finish);
+ instance_watcher2->notify_sync_complete("sync_id");
+ ASSERT_EQ(0, on_finish.wait());
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, CancelWaitingOnNonLeader) {
+ InSequence seq;
+
+ C_SaferCond on_start_op_called;
+ Context *on_start_ctx;
+ expect_throttler_start_op("sync_id", &on_start_op_called,
+ &on_start_ctx);
+ C_SaferCond on_start;
+ instance_watcher2->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start_op_called.wait());
+
+ ASSERT_TRUE(instance_watcher2->cancel_sync_request("sync_id"));
+ // emulate watcher timeout
+ on_start_ctx->complete(-ETIMEDOUT);
+ ASSERT_EQ(-ECANCELED, on_start.wait());
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, InFlightPrevNotification) {
+ // start sync when previous notification is still in flight
+
+ InSequence seq;
+
+ expect_throttler_start_op("sync_id");
+ C_SaferCond on_start1;
+ instance_watcher2->notify_sync_request("sync_id", &on_start1);
+ ASSERT_EQ(0, on_start1.wait());
+
+ C_SaferCond on_start2;
+ EXPECT_CALL(mock_image_sync_throttler, finish_op("", "sync_id"))
+ .WillOnce(Invoke([this, &on_start2](const std::string &,
+ const std::string &) {
+ instance_watcher2->notify_sync_request("sync_id", &on_start2);
+ }));
+ expect_throttler_start_op("sync_id");
+ instance_watcher2->notify_sync_complete("sync_id");
+
+ ASSERT_EQ(0, on_start2.wait());
+ C_SaferCond on_finish;
+ expect_throttler_finish_op("sync_id", &on_finish);
+ instance_watcher2->notify_sync_complete("sync_id");
+ ASSERT_EQ(0, on_finish.wait());
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, NoInFlightReleaseAcquireLeader) {
+ InSequence seq;
+
+ expect_throttler_drain();
+ instance_watcher1->handle_release_leader();
+ instance_watcher1->handle_acquire_leader();
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, StartedOnLeaderReleaseLeader) {
+ InSequence seq;
+
+ expect_throttler_drain();
+ instance_watcher1->handle_release_leader();
+ instance_watcher2->handle_acquire_leader();
+
+ expect_throttler_start_op("sync_id");
+ C_SaferCond on_start;
+ instance_watcher2->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+ expect_throttler_drain();
+ instance_watcher2->handle_release_leader();
+ instance_watcher2->notify_sync_complete("sync_id");
+
+ instance_watcher1->handle_acquire_leader();
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, WaitingOnLeaderReleaseLeader) {
+ InSequence seq;
+
+ C_SaferCond on_start_op_called;
+ Context *on_start_ctx;
+ expect_throttler_start_op("sync_id", &on_start_op_called, &on_start_ctx);
+ C_SaferCond on_start;
+ instance_watcher1->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start_op_called.wait());
+
+ expect_throttler_drain();
+ instance_watcher1->handle_release_leader();
+ // emulate throttler queue drain on leader release
+ on_start_ctx->complete(-ESTALE);
+
+ expect_throttler_start_op("sync_id");
+ instance_watcher2->handle_acquire_leader();
+ instance_watcher1->handle_update_leader(instance_id2);
+
+ ASSERT_EQ(0, on_start.wait());
+ C_SaferCond on_finish;
+ expect_throttler_finish_op("sync_id", &on_finish);
+ instance_watcher1->notify_sync_complete("sync_id");
+ ASSERT_EQ(0, on_finish.wait());
+
+ expect_throttler_drain();
+ instance_watcher2->handle_release_leader();
+ instance_watcher1->handle_acquire_leader();
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, StartedOnNonLeaderAcquireLeader) {
+ InSequence seq;
+
+ expect_throttler_drain();
+ instance_watcher1->handle_release_leader();
+ instance_watcher2->handle_acquire_leader();
+ instance_watcher1->handle_update_leader(instance_id2);
+
+ expect_throttler_start_op("sync_id");
+ C_SaferCond on_start;
+ instance_watcher1->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+
+ expect_throttler_drain();
+ instance_watcher2->handle_release_leader();
+ instance_watcher1->handle_acquire_leader();
+ instance_watcher2->handle_update_leader(instance_id1);
+
+ instance_watcher1->notify_sync_complete("sync_id");
+}
+
+TEST_F(TestMockInstanceWatcher_NotifySync, WaitingOnNonLeaderAcquireLeader) {
+ InSequence seq;
+
+ C_SaferCond on_start_op_called;
+ Context *on_start_ctx;
+ expect_throttler_start_op("sync_id", &on_start_op_called,
+ &on_start_ctx);
+ C_SaferCond on_start;
+ instance_watcher2->notify_sync_request("sync_id", &on_start);
+ ASSERT_EQ(0, on_start_op_called.wait());
+
+ expect_throttler_drain();
+ instance_watcher1->handle_release_leader();
+ // emulate throttler queue drain on leader release
+ on_start_ctx->complete(-ESTALE);
+
+ EXPECT_CALL(mock_image_sync_throttler, start_op("", "sync_id", _))
+ .WillOnce(WithArg<2>(CompleteContext(0)));
+ instance_watcher2->handle_acquire_leader();
+ instance_watcher1->handle_update_leader(instance_id2);
+
+ ASSERT_EQ(0, on_start.wait());
+
+ C_SaferCond on_finish;
+ expect_throttler_finish_op("sync_id", &on_finish);
+ instance_watcher2->notify_sync_complete("sync_id");
+ ASSERT_EQ(0, on_finish.wait());
+
+ expect_throttler_drain();
+ instance_watcher2->handle_release_leader();
+ instance_watcher1->handle_acquire_leader();
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_LeaderWatcher.cc b/src/test/rbd_mirror/test_mock_LeaderWatcher.cc
new file mode 100644
index 000000000..d4710f9de
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_LeaderWatcher.cc
@@ -0,0 +1,614 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/AsioEngine.h"
+#include "librbd/Utils.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "tools/rbd_mirror/LeaderWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+
+using librbd::util::create_async_context_callback;
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+struct MockManagedLock {
+ static MockManagedLock *s_instance;
+ static MockManagedLock &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockManagedLock() {
+ s_instance = this;
+ }
+
+ bool m_release_lock_on_shutdown = false;
+ Context *m_on_released = nullptr;
+
+ MOCK_METHOD0(construct, void());
+ MOCK_METHOD0(destroy, void());
+
+ MOCK_CONST_METHOD0(is_lock_owner, bool());
+
+ MOCK_METHOD1(shut_down, void(Context *));
+ MOCK_METHOD1(try_acquire_lock, void(Context *));
+ MOCK_METHOD1(release_lock, void(Context *));
+ MOCK_METHOD0(reacquire_lock, void());
+ MOCK_METHOD3(break_lock, void(const managed_lock::Locker &, bool, Context *));
+ MOCK_METHOD2(get_locker, void(managed_lock::Locker *, Context *));
+
+ MOCK_METHOD0(set_state_post_acquiring, void());
+
+ MOCK_CONST_METHOD0(is_shutdown, bool());
+
+ MOCK_CONST_METHOD0(is_state_post_acquiring, bool());
+ MOCK_CONST_METHOD0(is_state_pre_releasing, bool());
+ MOCK_CONST_METHOD0(is_state_locked, bool());
+};
+
+MockManagedLock *MockManagedLock::s_instance = nullptr;
+
+template <>
+struct ManagedLock<MockTestImageCtx> {
+ ManagedLock(librados::IoCtx& ioctx, librbd::AsioEngine& asio_engine,
+ const std::string& oid, librbd::Watcher *watcher,
+ managed_lock::Mode mode, bool blocklist_on_break_lock,
+ uint32_t blocklist_expire_seconds)
+ : m_work_queue(asio_engine.get_work_queue()) {
+ MockManagedLock::get_instance().construct();
+ }
+
+ virtual ~ManagedLock() {
+ MockManagedLock::get_instance().destroy();
+ }
+
+ librbd::asio::ContextWQ *m_work_queue;
+
+ mutable ceph::mutex m_lock = ceph::make_mutex("ManagedLock::m_lock");
+
+ bool is_lock_owner() const {
+ return MockManagedLock::get_instance().is_lock_owner();
+ }
+
+ void shut_down(Context *on_shutdown) {
+ if (MockManagedLock::get_instance().m_release_lock_on_shutdown) {
+ on_shutdown = new LambdaContext(
+ [this, on_shutdown](int r) {
+ MockManagedLock::get_instance().m_release_lock_on_shutdown = false;
+ shut_down(on_shutdown);
+ });
+ release_lock(on_shutdown);
+ return;
+ }
+
+ MockManagedLock::get_instance().shut_down(on_shutdown);
+ }
+
+ void try_acquire_lock(Context *on_acquired) {
+ Context *post_acquire_ctx = create_async_context_callback(
+ m_work_queue, new LambdaContext(
+ [this, on_acquired](int r) {
+ post_acquire_lock_handler(r, on_acquired);
+ }));
+ MockManagedLock::get_instance().try_acquire_lock(post_acquire_ctx);
+ }
+
+ void release_lock(Context *on_released) {
+ ceph_assert(MockManagedLock::get_instance().m_on_released == nullptr);
+ MockManagedLock::get_instance().m_on_released = on_released;
+
+ Context *post_release_ctx = new LambdaContext(
+ [this](int r) {
+ ceph_assert(MockManagedLock::get_instance().m_on_released != nullptr);
+ post_release_lock_handler(false, r,
+ MockManagedLock::get_instance().m_on_released);
+ MockManagedLock::get_instance().m_on_released = nullptr;
+ });
+
+ Context *release_ctx = new LambdaContext(
+ [post_release_ctx](int r) {
+ if (r < 0) {
+ MockManagedLock::get_instance().m_on_released->complete(r);
+ } else {
+ MockManagedLock::get_instance().release_lock(post_release_ctx);
+ }
+ });
+
+ Context *pre_release_ctx = new LambdaContext(
+ [this, release_ctx](int r) {
+ bool shutting_down =
+ MockManagedLock::get_instance().m_release_lock_on_shutdown;
+ pre_release_lock_handler(shutting_down, release_ctx);
+ });
+
+ m_work_queue->queue(pre_release_ctx, 0);
+ }
+
+ void reacquire_lock(Context* on_finish) {
+ MockManagedLock::get_instance().reacquire_lock();
+ }
+
+ void get_locker(managed_lock::Locker *locker, Context *on_finish) {
+ MockManagedLock::get_instance().get_locker(locker, on_finish);
+ }
+
+ void break_lock(const managed_lock::Locker &locker, bool force_break_lock,
+ Context *on_finish) {
+ MockManagedLock::get_instance().break_lock(locker, force_break_lock,
+ on_finish);
+ }
+
+ void set_state_post_acquiring() {
+ MockManagedLock::get_instance().set_state_post_acquiring();
+ }
+
+ bool is_shutdown() const {
+ return MockManagedLock::get_instance().is_shutdown();
+ }
+
+ bool is_state_post_acquiring() const {
+ return MockManagedLock::get_instance().is_state_post_acquiring();
+ }
+
+ bool is_state_pre_releasing() const {
+ return MockManagedLock::get_instance().is_state_pre_releasing();
+ }
+
+ bool is_state_locked() const {
+ return MockManagedLock::get_instance().is_state_locked();
+ }
+
+ virtual void post_acquire_lock_handler(int r, Context *on_finish) = 0;
+ virtual void pre_release_lock_handler(bool shutting_down,
+ Context *on_finish) = 0;
+ virtual void post_release_lock_handler(bool shutting_down, int r,
+ Context *on_finish) = 0;
+};
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+ librbd::AsioEngine* asio_engine;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue), asio_engine(threads->asio_engine) {
+ }
+};
+
+template <>
+struct Instances<librbd::MockTestImageCtx> {
+ static Instances* s_instance;
+
+ static Instances *create(Threads<librbd::MockTestImageCtx> *threads,
+ librados::IoCtx &ioctx,
+ const std::string& instance_id,
+ instances::Listener&) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ Instances() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~Instances() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(destroy, void());
+ MOCK_METHOD1(init, void(Context *));
+ MOCK_METHOD1(shut_down, void(Context *));
+ MOCK_METHOD1(acked, void(const std::vector<std::string> &));
+ MOCK_METHOD0(unblock_listener, void());
+};
+
+Instances<librbd::MockTestImageCtx> *Instances<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+} // namespace rbd
+
+
+// template definitions
+#include "tools/rbd_mirror/LeaderWatcher.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+
+using librbd::MockManagedLock;
+
+struct MockListener : public leader_watcher::Listener {
+ static MockListener* s_instance;
+
+ MockListener() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ ~MockListener() override {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD1(post_acquire_handler, void(Context *));
+ MOCK_METHOD1(pre_release_handler, void(Context *));
+
+ MOCK_METHOD1(update_leader_handler, void(const std::string &));
+ MOCK_METHOD1(handle_instances_added, void(const InstanceIds&));
+ MOCK_METHOD1(handle_instances_removed, void(const InstanceIds&));
+};
+
+MockListener *MockListener::s_instance = nullptr;
+
+class TestMockLeaderWatcher : public TestMockFixture {
+public:
+ typedef Instances<librbd::MockTestImageCtx> MockInstances;
+ typedef LeaderWatcher<librbd::MockTestImageCtx> MockLeaderWatcher;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+ m_mock_threads = new MockThreads(m_threads);
+ }
+
+ void TearDown() override {
+ delete m_mock_threads;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_construct(MockManagedLock &mock_managed_lock) {
+ EXPECT_CALL(mock_managed_lock, construct());
+ }
+
+ void expect_destroy(MockManagedLock &mock_managed_lock) {
+ EXPECT_CALL(mock_managed_lock, destroy());
+ }
+
+ void expect_is_lock_owner(MockManagedLock &mock_managed_lock, bool owner) {
+ EXPECT_CALL(mock_managed_lock, is_lock_owner())
+ .WillOnce(Return(owner));
+ }
+
+ void expect_shut_down(MockManagedLock &mock_managed_lock,
+ bool release_lock_on_shutdown, int r) {
+ mock_managed_lock.m_release_lock_on_shutdown = release_lock_on_shutdown;
+ EXPECT_CALL(mock_managed_lock, shut_down(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_try_acquire_lock(MockManagedLock &mock_managed_lock, int r) {
+ EXPECT_CALL(mock_managed_lock, try_acquire_lock(_))
+ .WillOnce(CompleteContext(r));
+ if (r == 0) {
+ expect_set_state_post_acquiring(mock_managed_lock);
+ }
+ }
+
+ void expect_release_lock(MockManagedLock &mock_managed_lock, int r,
+ Context *on_finish = nullptr) {
+ EXPECT_CALL(mock_managed_lock, release_lock(_))
+ .WillOnce(Invoke([on_finish, &mock_managed_lock, r](Context *ctx) {
+ if (on_finish != nullptr) {
+ auto on_released = mock_managed_lock.m_on_released;
+ ceph_assert(on_released != nullptr);
+ mock_managed_lock.m_on_released = new LambdaContext(
+ [on_released, on_finish](int r) {
+ on_released->complete(r);
+ on_finish->complete(r);
+ });
+ }
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_get_locker(MockManagedLock &mock_managed_lock,
+ const librbd::managed_lock::Locker &locker, int r) {
+ EXPECT_CALL(mock_managed_lock, get_locker(_, _))
+ .WillOnce(Invoke([r, locker](librbd::managed_lock::Locker *out,
+ Context *ctx) {
+ if (r == 0) {
+ *out = locker;
+ }
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_break_lock(MockManagedLock &mock_managed_lock,
+ const librbd::managed_lock::Locker &locker, int r,
+ Context *on_finish) {
+ EXPECT_CALL(mock_managed_lock, break_lock(locker, true, _))
+ .WillOnce(Invoke([on_finish, r](const librbd::managed_lock::Locker &,
+ bool, Context *ctx) {
+ ctx->complete(r);
+ on_finish->complete(0);
+ }));
+ }
+
+ void expect_set_state_post_acquiring(MockManagedLock &mock_managed_lock) {
+ EXPECT_CALL(mock_managed_lock, set_state_post_acquiring());
+ }
+
+ void expect_is_shutdown(MockManagedLock &mock_managed_lock) {
+ EXPECT_CALL(mock_managed_lock, is_shutdown())
+ .Times(AtLeast(0)).WillRepeatedly(Return(false));
+ }
+
+ void expect_is_leader(MockManagedLock &mock_managed_lock, bool post_acquiring,
+ bool locked) {
+ EXPECT_CALL(mock_managed_lock, is_state_post_acquiring())
+ .WillOnce(Return(post_acquiring));
+ if (!post_acquiring) {
+ EXPECT_CALL(mock_managed_lock, is_state_locked())
+ .WillOnce(Return(locked));
+ }
+ }
+
+ void expect_is_leader(MockManagedLock &mock_managed_lock) {
+ EXPECT_CALL(mock_managed_lock, is_state_post_acquiring())
+ .Times(AtLeast(0)).WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_managed_lock, is_state_locked())
+ .Times(AtLeast(0)).WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_managed_lock, is_state_pre_releasing())
+ .Times(AtLeast(0)).WillRepeatedly(Return(false));
+ }
+
+ void expect_notify_heartbeat(MockManagedLock &mock_managed_lock,
+ Context *on_finish) {
+ // is_leader in notify_heartbeat
+ EXPECT_CALL(mock_managed_lock, is_state_post_acquiring())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_managed_lock, is_state_locked())
+ .WillOnce(Return(true));
+
+ // is_leader in handle_notify_heartbeat
+ EXPECT_CALL(mock_managed_lock, is_state_post_acquiring())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_managed_lock, is_state_locked())
+ .WillOnce(DoAll(Invoke([on_finish]() {
+ on_finish->complete(0);
+ }),
+ Return(true)));
+ }
+
+ void expect_destroy(MockInstances &mock_instances) {
+ EXPECT_CALL(mock_instances, destroy());
+ }
+
+ void expect_init(MockInstances &mock_instances, int r) {
+ EXPECT_CALL(mock_instances, init(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ }
+
+ void expect_shut_down(MockInstances &mock_instances, int r) {
+ EXPECT_CALL(mock_instances, shut_down(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ expect_destroy(mock_instances);
+ }
+
+ void expect_acquire_notify(MockManagedLock &mock_managed_lock,
+ MockListener &mock_listener, int r) {
+ expect_is_leader(mock_managed_lock, true, false);
+ EXPECT_CALL(mock_listener, post_acquire_handler(_))
+ .WillOnce(CompleteContext(r));
+ expect_is_leader(mock_managed_lock, true, false);
+ }
+
+ void expect_release_notify(MockManagedLock &mock_managed_lock,
+ MockListener &mock_listener, int r) {
+ expect_is_leader(mock_managed_lock, false, false);
+ EXPECT_CALL(mock_listener, pre_release_handler(_))
+ .WillOnce(CompleteContext(r));
+ expect_is_leader(mock_managed_lock, false, false);
+ }
+
+ void expect_unblock_listener(MockInstances& mock_instances) {
+ EXPECT_CALL(mock_instances, unblock_listener());
+ }
+
+ void expect_instances_acked(MockInstances& mock_instances) {
+ EXPECT_CALL(mock_instances, acked(_));
+ }
+
+ MockThreads *m_mock_threads;
+};
+
+TEST_F(TestMockLeaderWatcher, InitShutdown) {
+ MockManagedLock mock_managed_lock;
+ MockInstances mock_instances;
+ MockListener listener;
+
+ expect_is_shutdown(mock_managed_lock);
+ expect_destroy(mock_managed_lock);
+
+ InSequence seq;
+
+ expect_construct(mock_managed_lock);
+ MockLeaderWatcher leader_watcher(m_mock_threads, m_local_io_ctx, &listener);
+
+ // Init
+ C_SaferCond on_heartbeat_finish;
+ expect_is_leader(mock_managed_lock, false, false);
+ expect_try_acquire_lock(mock_managed_lock, 0);
+ expect_init(mock_instances, 0);
+ expect_acquire_notify(mock_managed_lock, listener, 0);
+ expect_unblock_listener(mock_instances);
+ expect_notify_heartbeat(mock_managed_lock, &on_heartbeat_finish);
+ expect_instances_acked(mock_instances);
+
+ ASSERT_EQ(0, leader_watcher.init());
+ ASSERT_EQ(0, on_heartbeat_finish.wait());
+
+ // Shutdown
+ expect_release_notify(mock_managed_lock, listener, 0);
+ expect_shut_down(mock_instances, 0);
+ expect_release_lock(mock_managed_lock, 0);
+ expect_shut_down(mock_managed_lock, true, 0);
+ expect_is_leader(mock_managed_lock, false, false);
+
+ leader_watcher.shut_down();
+}
+
+TEST_F(TestMockLeaderWatcher, InitReleaseShutdown) {
+ MockManagedLock mock_managed_lock;
+ MockInstances mock_instances;
+ MockListener listener;
+
+ expect_is_shutdown(mock_managed_lock);
+ expect_destroy(mock_managed_lock);
+
+ InSequence seq;
+
+ expect_construct(mock_managed_lock);
+ MockLeaderWatcher leader_watcher(m_mock_threads, m_local_io_ctx, &listener);
+
+ // Init
+ C_SaferCond on_heartbeat_finish;
+ expect_is_leader(mock_managed_lock, false, false);
+ expect_try_acquire_lock(mock_managed_lock, 0);
+ expect_init(mock_instances, 0);
+ expect_acquire_notify(mock_managed_lock, listener, 0);
+ expect_unblock_listener(mock_instances);
+ expect_notify_heartbeat(mock_managed_lock, &on_heartbeat_finish);
+ expect_instances_acked(mock_instances);
+
+ ASSERT_EQ(0, leader_watcher.init());
+ ASSERT_EQ(0, on_heartbeat_finish.wait());
+
+ // Release
+ expect_is_leader(mock_managed_lock, false, true);
+ expect_release_notify(mock_managed_lock, listener, 0);
+ expect_shut_down(mock_instances, 0);
+ C_SaferCond on_release;
+ expect_release_lock(mock_managed_lock, 0, &on_release);
+
+ leader_watcher.release_leader();
+ ASSERT_EQ(0, on_release.wait());
+
+ // Shutdown
+ expect_shut_down(mock_managed_lock, false, 0);
+ expect_is_leader(mock_managed_lock, false, false);
+
+ leader_watcher.shut_down();
+}
+
+TEST_F(TestMockLeaderWatcher, AcquireError) {
+ MockManagedLock mock_managed_lock;
+ MockInstances mock_instances;
+ MockListener listener;
+
+ expect_is_shutdown(mock_managed_lock);
+ expect_is_leader(mock_managed_lock);
+ expect_destroy(mock_managed_lock);
+
+ InSequence seq;
+
+ expect_construct(mock_managed_lock);
+ MockLeaderWatcher leader_watcher(m_mock_threads, m_local_io_ctx, &listener);
+
+ // Init
+ C_SaferCond on_heartbeat_finish;
+ expect_is_leader(mock_managed_lock, false, false);
+ expect_try_acquire_lock(mock_managed_lock, -EAGAIN);
+ expect_get_locker(mock_managed_lock, librbd::managed_lock::Locker(), -ENOENT);
+ expect_try_acquire_lock(mock_managed_lock, 0);
+ expect_init(mock_instances, 0);
+ expect_acquire_notify(mock_managed_lock, listener, 0);
+ expect_unblock_listener(mock_instances);
+ expect_notify_heartbeat(mock_managed_lock, &on_heartbeat_finish);
+ expect_instances_acked(mock_instances);
+
+ ASSERT_EQ(0, leader_watcher.init());
+ ASSERT_EQ(0, on_heartbeat_finish.wait());
+
+ // Shutdown
+ expect_release_notify(mock_managed_lock, listener, 0);
+ expect_shut_down(mock_instances, 0);
+ expect_release_lock(mock_managed_lock, 0);
+ expect_shut_down(mock_managed_lock, true, 0);
+ expect_is_leader(mock_managed_lock, false, false);
+
+ leader_watcher.shut_down();
+}
+
+TEST_F(TestMockLeaderWatcher, Break) {
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_heartbeat_interval", "1"));
+ EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_max_missed_heartbeats",
+ "1"));
+ CephContext *cct = reinterpret_cast<CephContext *>(m_local_io_ctx.cct());
+ int max_acquire_attempts = cct->_conf.get_val<uint64_t>(
+ "rbd_mirror_leader_max_acquire_attempts_before_break");
+
+ MockManagedLock mock_managed_lock;
+ MockInstances mock_instances;
+ MockListener listener;
+ librbd::managed_lock::Locker
+ locker{entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123};
+
+ expect_is_shutdown(mock_managed_lock);
+ expect_is_leader(mock_managed_lock);
+ expect_destroy(mock_managed_lock);
+ EXPECT_CALL(listener, update_leader_handler(_));
+
+ InSequence seq;
+
+ expect_construct(mock_managed_lock);
+ MockLeaderWatcher leader_watcher(m_mock_threads, m_local_io_ctx, &listener);
+
+ // Init
+ expect_is_leader(mock_managed_lock, false, false);
+ for (int i = 0; i < max_acquire_attempts; i++) {
+ expect_try_acquire_lock(mock_managed_lock, -EAGAIN);
+ expect_get_locker(mock_managed_lock, locker, 0);
+ }
+ C_SaferCond on_break;
+ expect_break_lock(mock_managed_lock, locker, 0, &on_break);
+ C_SaferCond on_heartbeat_finish;
+ expect_try_acquire_lock(mock_managed_lock, 0);
+ expect_init(mock_instances, 0);
+ expect_acquire_notify(mock_managed_lock, listener, 0);
+ expect_unblock_listener(mock_instances);
+ expect_notify_heartbeat(mock_managed_lock, &on_heartbeat_finish);
+ expect_instances_acked(mock_instances);
+
+ ASSERT_EQ(0, leader_watcher.init());
+ ASSERT_EQ(0, on_heartbeat_finish.wait());
+
+ // Shutdown
+ expect_release_notify(mock_managed_lock, listener, 0);
+ expect_shut_down(mock_instances, 0);
+ expect_release_lock(mock_managed_lock, 0);
+ expect_shut_down(mock_managed_lock, true, 0);
+ expect_is_leader(mock_managed_lock, false, false);
+
+ leader_watcher.shut_down();
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc b/src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc
new file mode 100644
index 000000000..ef2af00f2
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc
@@ -0,0 +1,706 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "include/stringify.h"
+#include "tools/rbd_mirror/MirrorStatusUpdater.h"
+#include "tools/rbd_mirror/MirrorStatusWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include <map>
+#include <string>
+#include <utility>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct MirrorStatusWatcher<librbd::MockTestImageCtx> {
+ static MirrorStatusWatcher* s_instance;
+ static MirrorStatusWatcher* create(librados::IoCtx& io_ctx,
+ MockContextWQ* mock_context_wq) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ MirrorStatusWatcher() {
+ s_instance = this;
+ }
+};
+
+MirrorStatusWatcher<librbd::MockTestImageCtx>* MirrorStatusWatcher<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+} // namespace mirror
+} // namespace rbd
+
+#include "tools/rbd_mirror/MirrorStatusUpdater.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::StrEq;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockMirrorStatusUpdater : public TestMockFixture {
+public:
+ typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater;
+ typedef MirrorStatusWatcher<librbd::MockTestImageCtx> MockMirrorStatusWatcher;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+
+ typedef std::map<std::string, cls::rbd::MirrorImageSiteStatus>
+ MirrorImageSiteStatuses;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ m_mock_local_io_ctx = &get_mock_io_ctx(m_local_io_ctx);
+ m_mock_threads = new MockThreads(m_threads);
+ }
+
+ void TearDown() override {
+ delete m_mock_threads;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_timer_add_event(Context** timer_event) {
+ EXPECT_CALL(*m_mock_threads->timer, add_event_after(_, _))
+ .WillOnce(WithArg<1>(Invoke([timer_event](Context *ctx) {
+ *timer_event = ctx;
+ return ctx;
+ })));
+ }
+
+ void expect_timer_cancel_event() {
+ EXPECT_CALL(*m_mock_threads->timer, cancel_event(_))
+ .WillOnce(Invoke([](Context* ctx) {
+ delete ctx;
+ return false;
+ }));
+ }
+
+ void expect_work_queue(bool async) {
+ EXPECT_CALL(*m_mock_threads->work_queue, queue(_, _))
+ .WillOnce(Invoke([this, async](Context *ctx, int r) {
+ if (async) {
+ m_threads->work_queue->queue(ctx, r);
+ } else {
+ ctx->complete(r);
+ }
+ }));
+ }
+
+ void expect_mirror_status_watcher_init(
+ MockMirrorStatusWatcher& mock_mirror_status_watcher, int r) {
+ EXPECT_CALL(*mock_mirror_status_watcher.s_instance, init(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_mirror_status_watcher_shut_down(
+ MockMirrorStatusWatcher& mock_mirror_status_watcher, int r) {
+ EXPECT_CALL(*mock_mirror_status_watcher.s_instance, shut_down(_))
+ .WillOnce(Invoke([this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_mirror_status_update(
+ const std::string& global_image_id,
+ const cls::rbd::MirrorImageSiteStatus& mirror_image_status, int r) {
+ EXPECT_CALL(*m_mock_local_io_ctx,
+ exec(RBD_MIRRORING, _, StrEq("rbd"),
+ StrEq("mirror_image_status_set"), _, _, _, _))
+ .WillOnce(WithArg<4>(Invoke(
+ [r, global_image_id, mirror_image_status](bufferlist& in_bl) {
+ auto bl_it = in_bl.cbegin();
+ std::string decode_global_image_id;
+ decode(decode_global_image_id, bl_it);
+ EXPECT_EQ(global_image_id, decode_global_image_id);
+
+ cls::rbd::MirrorImageSiteStatus decode_mirror_image_status;
+ decode(decode_mirror_image_status, bl_it);
+ EXPECT_EQ(mirror_image_status, decode_mirror_image_status);
+ return r;
+ })));
+ }
+
+ void expect_mirror_status_update(
+ const MirrorImageSiteStatuses& mirror_image_site_statuses,
+ const std::string& mirror_uuid, int r) {
+ EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _, _))
+ .WillOnce(Invoke([this](auto&&... args) {
+ int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...);
+ m_mock_local_io_ctx->aio_flush();
+ return r;
+ }));
+
+ for (auto [global_image_id, mirror_image_status] :
+ mirror_image_site_statuses) {
+ mirror_image_status.mirror_uuid = mirror_uuid;
+ expect_mirror_status_update(global_image_id, mirror_image_status, r);
+ if (r < 0) {
+ break;
+ }
+ }
+ }
+
+ void expect_mirror_status_remove(const std::string& global_image_id, int r) {
+ EXPECT_CALL(*m_mock_local_io_ctx,
+ exec(RBD_MIRRORING, _, StrEq("rbd"),
+ StrEq("mirror_image_status_remove"), _, _, _, _))
+ .WillOnce(WithArg<4>(Invoke(
+ [r, global_image_id](bufferlist& in_bl) {
+ auto bl_it = in_bl.cbegin();
+ std::string decode_global_image_id;
+ decode(decode_global_image_id, bl_it);
+ EXPECT_EQ(global_image_id, decode_global_image_id);
+
+ return r;
+ })));
+ }
+
+ void expect_mirror_status_removes(const std::set<std::string>& mirror_images,
+ int r) {
+ EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _, _))
+ .WillOnce(Invoke([this](auto&&... args) {
+ int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...);
+ m_mock_local_io_ctx->aio_flush();
+ return r;
+ }));
+
+ for (auto global_image_id : mirror_images) {
+ expect_mirror_status_remove(global_image_id, r);
+ if (r < 0) {
+ break;
+ }
+ }
+ }
+
+ void fire_timer_event(Context** timer_event,
+ Context** update_task) {
+ expect_timer_add_event(timer_event);
+
+ // timer queues the update task
+ EXPECT_CALL(*m_mock_threads->work_queue, queue(_, _))
+ .WillOnce(WithArg<0>(Invoke([update_task](Context* ctx) mutable {
+ *update_task = ctx;
+ })));
+
+ // fire the timer task
+ {
+ std::lock_guard timer_locker{m_mock_threads->timer_lock};
+ ceph_assert(*timer_event != nullptr);
+ (*timer_event)->complete(0);
+ }
+ }
+
+ void init_mirror_status_updater(
+ MockMirrorStatusUpdater& mock_mirror_status_updater,
+ MockMirrorStatusWatcher& mock_mirror_status_watcher,
+ Context** timer_event) {
+ expect_timer_add_event(timer_event);
+ expect_mirror_status_watcher_init(mock_mirror_status_watcher, 0);
+ expect_work_queue(true);
+
+ C_SaferCond ctx;
+ mock_mirror_status_updater.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ void shut_down_mirror_status_updater(
+ MockMirrorStatusUpdater& mock_mirror_status_updater,
+ MockMirrorStatusWatcher& mock_mirror_status_watcher) {
+ expect_timer_cancel_event();
+ expect_mirror_status_watcher_shut_down(mock_mirror_status_watcher, 0);
+ expect_work_queue(true);
+
+ C_SaferCond ctx;
+ mock_mirror_status_updater.shut_down(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ }
+
+ librados::MockTestMemIoCtxImpl* m_mock_local_io_ctx = nullptr;
+ MockThreads* m_mock_threads = nullptr;
+};
+
+TEST_F(TestMockMirrorStatusUpdater, InitShutDown) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, InitStatusWatcherError) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ Context* timer_event = nullptr;
+ expect_timer_add_event(&timer_event);
+ expect_mirror_status_watcher_init(*mock_mirror_status_watcher, -EINVAL);
+ expect_timer_cancel_event();
+ expect_work_queue(true);
+
+ C_SaferCond ctx;
+ mock_mirror_status_updater.init(&ctx);
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorStatusUpdater, ShutDownStatusWatcherError) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ C_SaferCond on_shutdown;
+ expect_timer_cancel_event();
+ expect_mirror_status_watcher_shut_down(*mock_mirror_status_watcher, -EINVAL);
+ expect_work_queue(true);
+ mock_mirror_status_updater.shut_down(&on_shutdown);
+
+ ASSERT_EQ(-EINVAL, on_shutdown.wait());
+}
+
+TEST_F(TestMockMirrorStatusUpdater, SmallBatch) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ MirrorImageSiteStatuses mirror_image_site_statuses;
+ for (auto i = 0; i < 100; ++i) {
+ auto pair = mirror_image_site_statuses.emplace(
+ stringify(i), cls::rbd::MirrorImageSiteStatus{});
+ mock_mirror_status_updater.set_mirror_image_status(pair.first->first,
+ pair.first->second,
+ false);
+ }
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ expect_mirror_status_update(mirror_image_site_statuses, "", 0);
+ update_task->complete(0);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, LargeBatch) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ MirrorImageSiteStatuses mirror_image_site_statuses;
+ for (auto i = 0; i < 200; ++i) {
+ auto pair = mirror_image_site_statuses.emplace(
+ stringify(i), cls::rbd::MirrorImageSiteStatus{});
+ mock_mirror_status_updater.set_mirror_image_status(pair.first->first,
+ pair.first->second,
+ false);
+ }
+
+ auto it_1 = mirror_image_site_statuses.begin();
+ auto it_2 = mirror_image_site_statuses.begin();
+ std::advance(it_2, 100);
+ MirrorImageSiteStatuses mirror_image_site_statuses_1{it_1, it_2};
+
+ it_1 = it_2;
+ std::advance(it_2, 100);
+ MirrorImageSiteStatuses mirror_image_site_statuses_2{it_1, it_2};
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ expect_mirror_status_update(mirror_image_site_statuses_1, "", 0);
+ expect_mirror_status_update(mirror_image_site_statuses_2, "", 0);
+ update_task->complete(0);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, OverwriteStatus) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+ mock_mirror_status_updater.set_mirror_image_status(
+ "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"},
+ false);
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ expect_mirror_status_update(
+ {{"1", cls::rbd::MirrorImageSiteStatus{
+ "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}},
+ "", 0);
+ update_task->complete(0);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, RemoveStatus) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ C_SaferCond ctx;
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+ expect_work_queue(false);
+ mock_mirror_status_updater.remove_mirror_image_status("1", false, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ C_SaferCond remove_flush_ctx;
+ EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _, _))
+ .WillOnce(Invoke([this, &remove_flush_ctx](auto&&... args) {
+ int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...);
+ m_mock_local_io_ctx->aio_flush();
+ remove_flush_ctx.complete(r);
+ return r;
+ }));
+ expect_mirror_status_remove("1", 0);
+ update_task->complete(0);
+ ASSERT_EQ(0, remove_flush_ctx.wait());
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, OverwriteRemoveStatus) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ C_SaferCond ctx;
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+ expect_work_queue(false);
+ mock_mirror_status_updater.remove_mirror_image_status("1", false, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+ mock_mirror_status_updater.set_mirror_image_status(
+ "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"},
+ false);
+
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ expect_mirror_status_update(
+ {{"1", cls::rbd::MirrorImageSiteStatus{
+ "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}},
+ "", 0);
+ update_task->complete(0);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, OverwriteStatusInFlight) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _, _))
+ .WillOnce(Invoke([this, &mock_mirror_status_updater](auto&&... args) {
+ mock_mirror_status_updater.set_mirror_image_status(
+ "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING,
+ "description"},
+ true);
+
+ int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...);
+ m_mock_local_io_ctx->aio_flush();
+ return r;
+ }));
+ expect_mirror_status_update("1", cls::rbd::MirrorImageSiteStatus{}, 0);
+ expect_work_queue(false);
+ expect_mirror_status_update(
+ {{"1", cls::rbd::MirrorImageSiteStatus{
+ "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}},
+ "", 0);
+
+ update_task->complete(0);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, ImmediateUpdate) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ expect_work_queue(false);
+ expect_mirror_status_update({{"1", cls::rbd::MirrorImageSiteStatus{}}},
+ "", 0);
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, true);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, RemoveImmediateUpdate) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+
+ C_SaferCond ctx;
+ expect_work_queue(false);
+ expect_mirror_status_removes({"1"}, 0);
+ expect_work_queue(false);
+ mock_mirror_status_updater.remove_mirror_image_status("1", true, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, RemoveRefreshIdleStatus) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+
+ C_SaferCond ctx;
+ expect_work_queue(true);
+ mock_mirror_status_updater.remove_refresh_mirror_image_status("1", &ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, RemoveRefreshInFlightStatus) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ C_SaferCond on_removed;
+ EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _, _))
+ .WillOnce(Invoke(
+ [this, &mock_mirror_status_updater, &on_removed](auto&&... args) {
+ mock_mirror_status_updater.remove_refresh_mirror_image_status(
+ "1", &on_removed);
+
+ int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...);
+ m_mock_local_io_ctx->aio_flush();
+ return r;
+ }));
+ update_task->complete(0);
+ ASSERT_EQ(0, on_removed.wait());
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+TEST_F(TestMockMirrorStatusUpdater, ShutDownWhileUpdating) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads, "");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ mock_mirror_status_updater.set_mirror_image_status("1", {}, false);
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ C_SaferCond on_shutdown;
+ EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _, _))
+ .WillOnce(Invoke(
+ [this, &mock_mirror_status_updater, &on_shutdown](auto&&... args) {
+ mock_mirror_status_updater.shut_down(&on_shutdown);
+ m_threads->work_queue->drain();
+
+ int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...);
+ m_mock_local_io_ctx->aio_flush();
+ return r;
+ }));
+
+ expect_timer_cancel_event();
+ expect_mirror_status_watcher_shut_down(*mock_mirror_status_watcher, 0);
+
+ update_task->complete(0);
+ ASSERT_EQ(0, on_shutdown.wait());
+}
+
+TEST_F(TestMockMirrorStatusUpdater, MirrorPeerSitePing) {
+ MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx,
+ m_mock_threads,
+ "mirror uuid");
+ MockMirrorStatusWatcher* mock_mirror_status_watcher =
+ new MockMirrorStatusWatcher();
+
+ InSequence seq;
+
+ Context* timer_event = nullptr;
+ init_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher, &timer_event);
+
+ MirrorImageSiteStatuses mirror_image_site_statuses;
+ for (auto i = 0; i < 100; ++i) {
+ auto pair = mirror_image_site_statuses.emplace(
+ stringify(i), cls::rbd::MirrorImageSiteStatus{});
+ mock_mirror_status_updater.set_mirror_image_status(pair.first->first,
+ pair.first->second,
+ false);
+ }
+
+ Context* update_task = nullptr;
+ fire_timer_event(&timer_event, &update_task);
+
+ expect_mirror_status_update(mirror_image_site_statuses, "mirror uuid", 0);
+ update_task->complete(0);
+
+ shut_down_mirror_status_updater(mock_mirror_status_updater,
+ *mock_mirror_status_watcher);
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_NamespaceReplayer.cc b/src/test/rbd_mirror/test_mock_NamespaceReplayer.cc
new file mode 100644
index 000000000..ece1a3396
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_NamespaceReplayer.cc
@@ -0,0 +1,611 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/api/Config.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include "tools/rbd_mirror/NamespaceReplayer.h"
+#include "tools/rbd_mirror/ImageDeleter.h"
+#include "tools/rbd_mirror/ImageMap.h"
+#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/InstanceReplayer.h"
+#include "tools/rbd_mirror/MirrorStatusUpdater.h"
+#include "tools/rbd_mirror/PoolWatcher.h"
+#include "tools/rbd_mirror/ServiceDaemon.h"
+#include "tools/rbd_mirror/Threads.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct ImageDeleter<librbd::MockTestImageCtx> {
+ static ImageDeleter* s_instance;
+
+ static ImageDeleter* create(
+ librados::IoCtx &ioctx, Threads<librbd::MockTestImageCtx> *threads,
+ Throttler<librbd::MockTestImageCtx> *image_deletion_throttler,
+ ServiceDaemon<librbd::MockTestImageCtx> *service_daemon) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+ MOCK_METHOD2(print_status, void(Formatter*, std::stringstream*));
+
+ ImageDeleter() {
+ s_instance = this;
+ }
+};
+
+ImageDeleter<librbd::MockTestImageCtx>* ImageDeleter<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct ImageMap<librbd::MockTestImageCtx> {
+ static ImageMap* s_instance;
+
+ static ImageMap *create(librados::IoCtx &ioctx,
+ Threads<librbd::MockTestImageCtx> *threads,
+ const std::string& instance_id,
+ image_map::Listener &listener) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ MOCK_METHOD1(update_instances_added, void(const std::vector<std::string>&));
+ MOCK_METHOD1(update_instances_removed, void(const std::vector<std::string>&));
+
+ MOCK_METHOD3(update_images_mock, void(const std::string&,
+ const std::set<std::string>&,
+ const std::set<std::string>&));
+ void update_images(const std::string& mirror_uuid,
+ std::set<std::string>&& added,
+ std::set<std::string>&& removed) {
+ update_images_mock(mirror_uuid, added, removed);
+ }
+
+ ImageMap() {
+ s_instance = this;
+ }
+};
+
+ImageMap<librbd::MockTestImageCtx>* ImageMap<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct InstanceReplayer<librbd::MockTestImageCtx> {
+ static InstanceReplayer* s_instance;
+
+ static InstanceReplayer* create(
+ librados::IoCtx &local_io_ctx, const std::string &local_mirror_uuid,
+ Threads<librbd::MockTestImageCtx> *threads,
+ ServiceDaemon<librbd::MockTestImageCtx> *service_daemon,
+ MirrorStatusUpdater<librbd::MockTestImageCtx>* local_status_updater,
+ journal::CacheManagerHandler *cache_manager_handler,
+ PoolMetaCache* pool_meta_cache) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MOCK_METHOD0(start, void());
+ MOCK_METHOD0(stop, void());
+ MOCK_METHOD0(restart, void());
+ MOCK_METHOD0(flush, void());
+
+ MOCK_METHOD1(stop, void(Context *));
+
+ MOCK_METHOD2(print_status, void(Formatter*, std::stringstream*));
+
+ MOCK_METHOD1(add_peer, void(const Peer<librbd::MockTestImageCtx>&));
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+ MOCK_METHOD1(release_all, void(Context*));
+
+ InstanceReplayer() {
+ s_instance = this;
+ }
+};
+
+InstanceReplayer<librbd::MockTestImageCtx>* InstanceReplayer<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct InstanceWatcher<librbd::MockTestImageCtx> {
+ static InstanceWatcher* s_instance;
+
+ static InstanceWatcher* create(
+ librados::IoCtx &ioctx, librbd::AsioEngine& asio_engine,
+ InstanceReplayer<librbd::MockTestImageCtx>* instance_replayer,
+ Throttler<librbd::MockTestImageCtx> *image_sync_throttler) {
+ ceph_assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ MOCK_METHOD0(handle_acquire_leader, void());
+ MOCK_METHOD0(handle_release_leader, void());
+
+ MOCK_METHOD0(get_instance_id, std::string());
+
+ MOCK_METHOD2(print_sync_status, void(Formatter*, std::stringstream*));
+
+ MOCK_METHOD1(init, void(Context *));
+ MOCK_METHOD1(shut_down, void(Context *));
+
+ MOCK_METHOD3(notify_image_acquire, void(const std::string&,
+ const std::string&,
+ Context*));
+ MOCK_METHOD3(notify_image_release, void(const std::string&,
+ const std::string&,
+ Context*));
+ MOCK_METHOD4(notify_peer_image_removed, void(const std::string&,
+ const std::string&,
+ const std::string&,
+ Context*));
+
+ MOCK_METHOD1(handle_update_leader, void(const std::string&));
+
+ InstanceWatcher() {
+ s_instance = this;
+ }
+
+};
+
+InstanceWatcher<librbd::MockTestImageCtx>* InstanceWatcher<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct MirrorStatusUpdater<librbd::MockTestImageCtx> {
+ std::string local_mirror_uuid;
+
+ static std::map<std::string, MirrorStatusUpdater*> s_instance;
+
+ static MirrorStatusUpdater *create(librados::IoCtx &io_ctx,
+ Threads<librbd::MockTestImageCtx> *threads,
+ const std::string& local_mirror_uuid) {
+ ceph_assert(s_instance[local_mirror_uuid] != nullptr);
+ return s_instance[local_mirror_uuid];
+ }
+
+ MirrorStatusUpdater(const std::string_view& local_mirror_uuid)
+ : local_mirror_uuid(local_mirror_uuid) {
+ s_instance[std::string{local_mirror_uuid}] = this;
+ }
+ ~MirrorStatusUpdater() {
+ s_instance.erase(local_mirror_uuid);
+ }
+
+ MOCK_METHOD1(init, void(Context *));
+ MOCK_METHOD1(shut_down, void(Context *));
+};
+
+std::map<std::string, MirrorStatusUpdater<librbd::MockTestImageCtx> *>
+ MirrorStatusUpdater<librbd::MockTestImageCtx>::s_instance;
+
+template<>
+struct PoolWatcher<librbd::MockTestImageCtx> {
+ int64_t pool_id = -1;
+
+ static std::map<int64_t, PoolWatcher *> s_instances;
+
+ static PoolWatcher *create(Threads<librbd::MockTestImageCtx> *threads,
+ librados::IoCtx &ioctx,
+ const std::string& mirror_uuid,
+ pool_watcher::Listener& listener) {
+ auto pool_id = ioctx.get_id();
+ ceph_assert(s_instances.count(pool_id));
+ return s_instances[pool_id];
+ }
+
+ MOCK_METHOD0(is_blocklisted, bool());
+
+ MOCK_METHOD0(get_image_count, uint64_t());
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ PoolWatcher(int64_t pool_id) : pool_id(pool_id) {
+ ceph_assert(!s_instances.count(pool_id));
+ s_instances[pool_id] = this;
+ }
+ ~PoolWatcher() {
+ s_instances.erase(pool_id);
+ }
+};
+
+std::map<int64_t, PoolWatcher<librbd::MockTestImageCtx> *> PoolWatcher<librbd::MockTestImageCtx>::s_instances;
+
+template<>
+struct ServiceDaemon<librbd::MockTestImageCtx> {
+ MOCK_METHOD4(add_or_update_namespace_attribute,
+ void(int64_t, const std::string&, const std::string&,
+ const service_daemon::AttributeValue&));
+ MOCK_METHOD2(remove_attribute,
+ void(int64_t, const std::string&));
+
+ MOCK_METHOD4(add_or_update_callout, uint64_t(int64_t, uint64_t,
+ service_daemon::CalloutLevel,
+ const std::string&));
+ MOCK_METHOD2(remove_callout, void(int64_t, uint64_t));
+};
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ ceph::mutex &timer_lock;
+ SafeTimer *timer;
+ librbd::asio::ContextWQ *work_queue;
+ librbd::AsioEngine* asio_engine;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer_lock(threads->timer_lock), timer(threads->timer),
+ work_queue(threads->work_queue), asio_engine(threads->asio_engine) {
+ }
+};
+
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/NamespaceReplayer.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockNamespaceReplayer : public TestMockFixture {
+public:
+ typedef NamespaceReplayer<librbd::MockTestImageCtx> MockNamespaceReplayer;
+ typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter;
+ typedef ImageMap<librbd::MockTestImageCtx> MockImageMap;
+ typedef InstanceReplayer<librbd::MockTestImageCtx> MockInstanceReplayer;
+ typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
+ typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater;
+ typedef PoolWatcher<librbd::MockTestImageCtx> MockPoolWatcher;
+ typedef ServiceDaemon<librbd::MockTestImageCtx> MockServiceDaemon;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+ m_mock_threads = new MockThreads(m_threads);
+ }
+
+ void TearDown() override {
+ delete m_mock_threads;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_mirror_status_updater_init(
+ MockMirrorStatusUpdater &mock_mirror_status_updater, int r) {
+ EXPECT_CALL(mock_mirror_status_updater, init(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ }
+
+ void expect_mirror_status_updater_shut_down(
+ MockMirrorStatusUpdater &mock_mirror_status_updater) {
+ EXPECT_CALL(mock_mirror_status_updater, shut_down(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ void expect_instance_replayer_init(
+ MockInstanceReplayer& mock_instance_replayer, int r) {
+ EXPECT_CALL(mock_instance_replayer, init(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ }
+
+ void expect_instance_replayer_shut_down(
+ MockInstanceReplayer& mock_instance_replayer) {
+ EXPECT_CALL(mock_instance_replayer, shut_down(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ void expect_instance_replayer_stop(
+ MockInstanceReplayer& mock_instance_replayer) {
+ EXPECT_CALL(mock_instance_replayer, stop(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ void expect_instance_replayer_add_peer(
+ MockInstanceReplayer& mock_instance_replayer) {
+ EXPECT_CALL(mock_instance_replayer, add_peer(_));
+ }
+
+ void expect_instance_replayer_release_all(
+ MockInstanceReplayer& mock_instance_replayer) {
+ EXPECT_CALL(mock_instance_replayer, release_all(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ void expect_instance_watcher_get_instance_id(
+ MockInstanceWatcher& mock_instance_watcher,
+ const std::string &instance_id) {
+ EXPECT_CALL(mock_instance_watcher, get_instance_id())
+ .WillOnce(Return(instance_id));
+ }
+
+ void expect_instance_watcher_init(
+ MockInstanceWatcher& mock_instance_watcher, int r) {
+ EXPECT_CALL(mock_instance_watcher, init(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ }
+
+ void expect_instance_watcher_shut_down(
+ MockInstanceWatcher& mock_instance_watcher) {
+ EXPECT_CALL(mock_instance_watcher, shut_down(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ void expect_instance_watcher_handle_acquire_leader(
+ MockInstanceWatcher& mock_instance_watcher) {
+ EXPECT_CALL(mock_instance_watcher, handle_acquire_leader());
+ }
+
+ void expect_instance_watcher_handle_release_leader(
+ MockInstanceWatcher& mock_instance_watcher) {
+ EXPECT_CALL(mock_instance_watcher, handle_release_leader());
+ }
+
+ void expect_image_map_init(MockInstanceWatcher &mock_instance_watcher,
+ MockImageMap& mock_image_map, int r) {
+ expect_instance_watcher_get_instance_id(mock_instance_watcher, "1234");
+ EXPECT_CALL(mock_image_map, init(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ }
+
+ void expect_image_map_shut_down(MockImageMap& mock_image_map) {
+ EXPECT_CALL(mock_image_map, shut_down(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ void expect_pool_watcher_init(MockPoolWatcher& mock_pool_watcher, int r) {
+ EXPECT_CALL(mock_pool_watcher, init(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ }
+
+ void expect_pool_watcher_shut_down(MockPoolWatcher& mock_pool_watcher) {
+ EXPECT_CALL(mock_pool_watcher, shut_down(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ void expect_image_deleter_init(MockImageDeleter& mock_image_deleter, int r) {
+ EXPECT_CALL(mock_image_deleter, init(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, r));
+ }
+
+ void expect_image_deleter_shut_down(MockImageDeleter& mock_image_deleter) {
+ EXPECT_CALL(mock_image_deleter, shut_down(_))
+ .WillOnce(CompleteContext(m_mock_threads->work_queue, 0));
+ }
+
+ MockThreads *m_mock_threads;
+};
+
+TEST_F(TestMockNamespaceReplayer, Init_LocalMirrorStatusUpdaterError) {
+ InSequence seq;
+
+ auto mock_local_mirror_status_updater = new MockMirrorStatusUpdater{""};
+ expect_mirror_status_updater_init(*mock_local_mirror_status_updater, -EINVAL);
+
+ MockNamespaceReplayer namespace_replayer(
+ {}, m_local_io_ctx, m_remote_io_ctx, "local mirror uuid",
+ "local peer uuid", {"remote mirror uuid", ""}, m_mock_threads,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+
+ C_SaferCond on_init;
+ namespace_replayer.init(&on_init);
+ ASSERT_EQ(-EINVAL, on_init.wait());
+}
+
+TEST_F(TestMockNamespaceReplayer, Init_RemoteMirrorStatusUpdaterError) {
+ InSequence seq;
+
+ auto mock_local_mirror_status_updater = new MockMirrorStatusUpdater{""};
+ expect_mirror_status_updater_init(*mock_local_mirror_status_updater, 0);
+
+ auto mock_remote_mirror_status_updater = new MockMirrorStatusUpdater{
+ "local mirror uuid"};
+ expect_mirror_status_updater_init(*mock_remote_mirror_status_updater,
+ -EINVAL);
+
+ expect_mirror_status_updater_shut_down(*mock_local_mirror_status_updater);
+
+ MockNamespaceReplayer namespace_replayer(
+ {}, m_local_io_ctx, m_remote_io_ctx, "local mirror uuid",
+ "local peer uuid", {"remote mirror uuid", ""}, m_mock_threads,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+
+ C_SaferCond on_init;
+ namespace_replayer.init(&on_init);
+ ASSERT_EQ(-EINVAL, on_init.wait());
+}
+
+TEST_F(TestMockNamespaceReplayer, Init_InstanceReplayerError) {
+ InSequence seq;
+
+ auto mock_local_mirror_status_updater = new MockMirrorStatusUpdater{""};
+ expect_mirror_status_updater_init(*mock_local_mirror_status_updater, 0);
+
+ auto mock_remote_mirror_status_updater = new MockMirrorStatusUpdater{
+ "local mirror uuid"};
+ expect_mirror_status_updater_init(*mock_remote_mirror_status_updater, 0);
+
+ auto mock_instance_replayer = new MockInstanceReplayer();
+ expect_instance_replayer_init(*mock_instance_replayer, -EINVAL);
+
+ expect_mirror_status_updater_shut_down(*mock_remote_mirror_status_updater);
+ expect_mirror_status_updater_shut_down(*mock_local_mirror_status_updater);
+
+ MockNamespaceReplayer namespace_replayer(
+ {}, m_local_io_ctx, m_remote_io_ctx, "local mirror uuid",
+ "local peer uuid", {"remote mirror uuid", ""}, m_mock_threads,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+
+ C_SaferCond on_init;
+ namespace_replayer.init(&on_init);
+ ASSERT_EQ(-EINVAL, on_init.wait());
+}
+
+TEST_F(TestMockNamespaceReplayer, Init_InstanceWatcherError) {
+ InSequence seq;
+
+ auto mock_local_mirror_status_updater = new MockMirrorStatusUpdater{""};
+ expect_mirror_status_updater_init(*mock_local_mirror_status_updater, 0);
+
+ auto mock_remote_mirror_status_updater = new MockMirrorStatusUpdater{
+ "local mirror uuid"};
+ expect_mirror_status_updater_init(*mock_remote_mirror_status_updater, 0);
+
+ auto mock_instance_replayer = new MockInstanceReplayer();
+ expect_instance_replayer_init(*mock_instance_replayer, 0);
+ expect_instance_replayer_add_peer(*mock_instance_replayer);
+
+ auto mock_instance_watcher = new MockInstanceWatcher();
+ expect_instance_watcher_init(*mock_instance_watcher, -EINVAL);
+
+ expect_instance_replayer_shut_down(*mock_instance_replayer);
+ expect_mirror_status_updater_shut_down(*mock_remote_mirror_status_updater);
+ expect_mirror_status_updater_shut_down(*mock_local_mirror_status_updater);
+
+ MockNamespaceReplayer namespace_replayer(
+ {}, m_local_io_ctx, m_remote_io_ctx, "local mirror uuid",
+ "local peer uuid", {"remote mirror uuid", ""}, m_mock_threads,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+
+ C_SaferCond on_init;
+ namespace_replayer.init(&on_init);
+ ASSERT_EQ(-EINVAL, on_init.wait());
+}
+
+TEST_F(TestMockNamespaceReplayer, Init) {
+ InSequence seq;
+
+ auto mock_local_mirror_status_updater = new MockMirrorStatusUpdater{""};
+ expect_mirror_status_updater_init(*mock_local_mirror_status_updater, 0);
+
+ auto mock_remote_mirror_status_updater = new MockMirrorStatusUpdater{
+ "local mirror uuid"};
+ expect_mirror_status_updater_init(*mock_remote_mirror_status_updater, 0);
+
+ auto mock_instance_replayer = new MockInstanceReplayer();
+ expect_instance_replayer_init(*mock_instance_replayer, 0);
+ expect_instance_replayer_add_peer(*mock_instance_replayer);
+
+ auto mock_instance_watcher = new MockInstanceWatcher();
+ expect_instance_watcher_init(*mock_instance_watcher, 0);
+
+ MockServiceDaemon mock_service_daemon;
+ MockNamespaceReplayer namespace_replayer(
+ {}, m_local_io_ctx, m_remote_io_ctx, "local mirror uuid",
+ "local peer uuid", {"remote mirror uuid", ""}, m_mock_threads,
+ nullptr, nullptr, &mock_service_daemon, nullptr, nullptr);
+
+ C_SaferCond on_init;
+ namespace_replayer.init(&on_init);
+ ASSERT_EQ(0, on_init.wait());
+
+ expect_instance_replayer_stop(*mock_instance_replayer);
+ expect_instance_watcher_shut_down(*mock_instance_watcher);
+ expect_instance_replayer_shut_down(*mock_instance_replayer);
+ expect_mirror_status_updater_shut_down(*mock_remote_mirror_status_updater);
+ expect_mirror_status_updater_shut_down(*mock_local_mirror_status_updater);
+
+ C_SaferCond on_shut_down;
+ namespace_replayer.shut_down(&on_shut_down);
+ ASSERT_EQ(0, on_shut_down.wait());
+}
+
+TEST_F(TestMockNamespaceReplayer, AcquireLeader) {
+ InSequence seq;
+
+ // init
+
+ auto mock_local_mirror_status_updater = new MockMirrorStatusUpdater{""};
+ expect_mirror_status_updater_init(*mock_local_mirror_status_updater, 0);
+
+ auto mock_remote_mirror_status_updater = new MockMirrorStatusUpdater{
+ "local mirror uuid"};
+ expect_mirror_status_updater_init(*mock_remote_mirror_status_updater, 0);
+
+ auto mock_instance_replayer = new MockInstanceReplayer();
+ expect_instance_replayer_init(*mock_instance_replayer, 0);
+ expect_instance_replayer_add_peer(*mock_instance_replayer);
+
+ auto mock_instance_watcher = new MockInstanceWatcher();
+ expect_instance_watcher_init(*mock_instance_watcher, 0);
+
+ MockServiceDaemon mock_service_daemon;
+ MockNamespaceReplayer namespace_replayer(
+ {}, m_local_io_ctx, m_remote_io_ctx, "local mirror uuid",
+ "local peer uuid", {"remote mirror uuid", ""}, m_mock_threads,
+ nullptr, nullptr, &mock_service_daemon, nullptr, nullptr);
+
+ C_SaferCond on_init;
+ namespace_replayer.init(&on_init);
+ ASSERT_EQ(0, on_init.wait());
+
+ // acquire leader
+
+ expect_instance_watcher_handle_acquire_leader(*mock_instance_watcher);
+
+ auto mock_image_map = new MockImageMap();
+ expect_image_map_init(*mock_instance_watcher, *mock_image_map, 0);
+
+ auto mock_local_pool_watcher = new MockPoolWatcher(m_local_io_ctx.get_id());
+ expect_pool_watcher_init(*mock_local_pool_watcher, 0);
+
+ auto mock_remote_pool_watcher = new MockPoolWatcher(m_remote_io_ctx.get_id());
+ expect_pool_watcher_init(*mock_remote_pool_watcher, 0);
+
+ auto mock_image_deleter = new MockImageDeleter();
+ expect_image_deleter_init(*mock_image_deleter, 0);
+
+ C_SaferCond on_acquire;
+ namespace_replayer.handle_acquire_leader(&on_acquire);
+ ASSERT_EQ(0, on_acquire.wait());
+
+ // release leader
+
+ expect_instance_watcher_handle_release_leader(*mock_instance_watcher);
+ expect_image_deleter_shut_down(*mock_image_deleter);
+ expect_pool_watcher_shut_down(*mock_local_pool_watcher);
+ expect_pool_watcher_shut_down(*mock_remote_pool_watcher);
+ expect_image_map_shut_down(*mock_image_map);
+ expect_instance_replayer_release_all(*mock_instance_replayer);
+
+ // shut down
+
+ expect_instance_replayer_stop(*mock_instance_replayer);
+ expect_instance_watcher_shut_down(*mock_instance_watcher);
+ expect_instance_replayer_shut_down(*mock_instance_replayer);
+ expect_mirror_status_updater_shut_down(*mock_remote_mirror_status_updater);
+ expect_mirror_status_updater_shut_down(*mock_local_mirror_status_updater);
+
+ C_SaferCond on_shut_down;
+ namespace_replayer.shut_down(&on_shut_down);
+ ASSERT_EQ(0, on_shut_down.wait());
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_PoolReplayer.cc b/src/test/rbd_mirror/test_mock_PoolReplayer.cc
new file mode 100644
index 000000000..ebd27d7e1
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_PoolReplayer.cc
@@ -0,0 +1,934 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/api/Config.h"
+#include "librbd/api/Namespace.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemCluster.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include "tools/rbd_mirror/Throttler.h"
+#include "tools/rbd_mirror/LeaderWatcher.h"
+#include "tools/rbd_mirror/NamespaceReplayer.h"
+#include "tools/rbd_mirror/PoolMetaCache.h"
+#include "tools/rbd_mirror/PoolReplayer.h"
+#include "tools/rbd_mirror/RemotePoolPoller.h"
+#include "tools/rbd_mirror/ServiceDaemon.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "common/Formatter.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace api {
+
+template <>
+class Config<MockTestImageCtx> {
+public:
+ static void apply_pool_overrides(librados::IoCtx& io_ctx,
+ ConfigProxy* config_proxy) {
+ }
+};
+
+template <>
+class Namespace<MockTestImageCtx> {
+public:
+ static Namespace* s_instance;
+
+ static int list(librados::IoCtx& io_ctx, std::vector<std::string> *names) {
+ if (s_instance) {
+ return s_instance->list(names);
+ }
+
+ return 0;
+ }
+
+ Namespace() {
+ s_instance = this;
+ }
+
+ void add(const std::string &name) {
+ std::lock_guard locker{m_lock};
+
+ m_names.insert(name);
+ }
+
+ void remove(const std::string &name) {
+ std::lock_guard locker{m_lock};
+
+ m_names.erase(name);
+ }
+
+ void clear() {
+ std::lock_guard locker{m_lock};
+
+ m_names.clear();
+ }
+
+private:
+ ceph::mutex m_lock = ceph::make_mutex("Namespace");
+ std::set<std::string> m_names;
+
+ int list(std::vector<std::string> *names) {
+ std::lock_guard locker{m_lock};
+
+ names->clear();
+ names->insert(names->begin(), m_names.begin(), m_names.end());
+ return 0;
+ }
+};
+
+Namespace<librbd::MockTestImageCtx>* Namespace<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace api
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Throttler<librbd::MockTestImageCtx> {
+ static Throttler* s_instance;
+
+ static Throttler *create(
+ CephContext *cct,
+ const std::string &max_concurrent_ops_config_param_name) {
+ return s_instance;
+ }
+
+ Throttler() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+
+ virtual ~Throttler() {
+ ceph_assert(s_instance == this);
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD1(print_status, void(Formatter*));
+};
+
+Throttler<librbd::MockTestImageCtx>* Throttler<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct NamespaceReplayer<librbd::MockTestImageCtx> {
+ static std::map<std::string, NamespaceReplayer *> s_instances;
+
+ static NamespaceReplayer *create(
+ const std::string &name,
+ librados::IoCtx &local_ioctx,
+ librados::IoCtx &remote_ioctx,
+ const std::string &local_mirror_uuid,
+ const std::string& local_mirror_peer_uuid,
+ const RemotePoolMeta& remote_pool_meta,
+ Threads<librbd::MockTestImageCtx> *threads,
+ Throttler<librbd::MockTestImageCtx> *image_sync_throttler,
+ Throttler<librbd::MockTestImageCtx> *image_deletion_throttler,
+ ServiceDaemon<librbd::MockTestImageCtx> *service_daemon,
+ journal::CacheManagerHandler *cache_manager_handler,
+ PoolMetaCache* pool_meta_cache) {
+ ceph_assert(s_instances.count(name));
+ auto namespace_replayer = s_instances[name];
+ s_instances.erase(name);
+ return namespace_replayer;
+ }
+
+ MOCK_METHOD0(is_blocklisted, bool());
+ MOCK_METHOD0(get_instance_id, std::string());
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ MOCK_METHOD1(handle_acquire_leader, void(Context *));
+ MOCK_METHOD1(handle_release_leader, void(Context *));
+ MOCK_METHOD1(handle_update_leader, void(const std::string &));
+ MOCK_METHOD1(handle_instances_added, void(const std::vector<std::string> &));
+ MOCK_METHOD1(handle_instances_removed, void(const std::vector<std::string> &));
+
+ MOCK_METHOD1(print_status, void(Formatter*));
+ MOCK_METHOD0(start, void());
+ MOCK_METHOD0(stop, void());
+ MOCK_METHOD0(restart, void());
+ MOCK_METHOD0(flush, void());
+
+ NamespaceReplayer(const std::string &name = "") {
+ ceph_assert(!s_instances.count(name));
+ s_instances[name] = this;
+ }
+};
+
+std::map<std::string, NamespaceReplayer<librbd::MockTestImageCtx> *> NamespaceReplayer<librbd::MockTestImageCtx>::s_instances;
+
+template<>
+struct LeaderWatcher<librbd::MockTestImageCtx> {
+ static LeaderWatcher* s_instance;
+ leader_watcher::Listener* listener = nullptr;
+
+ static LeaderWatcher *create(Threads<librbd::MockTestImageCtx> *threads,
+ librados::IoCtx &ioctx,
+ leader_watcher::Listener* listener) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->listener = listener;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(is_blocklisted, bool());
+ MOCK_METHOD0(is_leader, bool());
+ MOCK_METHOD0(release_leader, void());
+
+ MOCK_METHOD1(get_leader_instance_id, bool(std::string*));
+ MOCK_METHOD1(list_instances, void(std::vector<std::string>*));
+
+ MOCK_METHOD0(init, int());
+ MOCK_METHOD0(shut_down, int());
+
+ LeaderWatcher() {
+ s_instance = this;
+ }
+
+};
+
+LeaderWatcher<librbd::MockTestImageCtx>* LeaderWatcher<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct RemotePoolPoller<librbd::MockTestImageCtx> {
+ static RemotePoolPoller* s_instance;
+
+ remote_pool_poller::Listener* listener = nullptr;
+
+ static RemotePoolPoller* create(
+ Threads<librbd::MockTestImageCtx>* threads,
+ librados::IoCtx& remote_io_ctx,
+ const std::string& local_site_name,
+ const std::string& local_mirror_uuid,
+ remote_pool_poller::Listener& listener) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->listener = &listener;
+ return s_instance;
+ }
+
+ MOCK_METHOD1(init, void(Context*));
+ MOCK_METHOD1(shut_down, void(Context*));
+
+ RemotePoolPoller() {
+ s_instance = this;
+ }
+};
+
+RemotePoolPoller<librbd::MockTestImageCtx>* RemotePoolPoller<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct ServiceDaemon<librbd::MockTestImageCtx> {
+ MOCK_METHOD2(add_namespace, void(int64_t, const std::string &));
+ MOCK_METHOD2(remove_namespace, void(int64_t, const std::string &));
+
+ MOCK_METHOD3(add_or_update_attribute,
+ void(int64_t, const std::string&,
+ const service_daemon::AttributeValue&));
+ MOCK_METHOD2(remove_attribute,
+ void(int64_t, const std::string&));
+
+ MOCK_METHOD4(add_or_update_callout, uint64_t(int64_t, uint64_t,
+ service_daemon::CalloutLevel,
+ const std::string&));
+ MOCK_METHOD2(remove_callout, void(int64_t, uint64_t));
+};
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+ ceph::condition_variable timer_cond;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/PoolReplayer.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockPoolReplayer : public TestMockFixture {
+public:
+ typedef librbd::api::Namespace<librbd::MockTestImageCtx> MockNamespace;
+ typedef PoolReplayer<librbd::MockTestImageCtx> MockPoolReplayer;
+ typedef Throttler<librbd::MockTestImageCtx> MockThrottler;
+ typedef NamespaceReplayer<librbd::MockTestImageCtx> MockNamespaceReplayer;
+ typedef RemotePoolPoller<librbd::MockTestImageCtx> MockRemotePoolPoller;
+ typedef LeaderWatcher<librbd::MockTestImageCtx> MockLeaderWatcher;
+ typedef ServiceDaemon<librbd::MockTestImageCtx> MockServiceDaemon;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+
+ void expect_work_queue(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_connect(librados::MockTestMemCluster& mock_cluster,
+ librados::MockTestMemRadosClient* mock_rados_client,
+ const std::string& cluster_name, CephContext** cct_ref) {
+ EXPECT_CALL(mock_cluster, create_rados_client(_))
+ .WillOnce(Invoke([cluster_name, mock_rados_client, cct_ref](CephContext* cct) {
+ EXPECT_EQ(cluster_name, cct->_conf->cluster);
+ if (cct_ref != nullptr) {
+ cct->get();
+ *cct_ref = cct;
+ }
+ return mock_rados_client;
+ }));
+ }
+
+ void expect_create_ioctx(librados::MockTestMemRadosClient* mock_rados_client,
+ librados::MockTestMemIoCtxImpl* mock_io_ctx_impl) {
+ EXPECT_CALL(*mock_rados_client, create_ioctx(_, _))
+ .WillOnce(Invoke([mock_io_ctx_impl](int64_t id, const std::string& name) {
+ return mock_io_ctx_impl;
+ }));
+ }
+
+ void expect_mirror_uuid_get(librados::MockTestMemIoCtxImpl *io_ctx_impl,
+ const std::string &uuid, int r) {
+ bufferlist out_bl;
+ encode(uuid, out_bl);
+
+ EXPECT_CALL(*io_ctx_impl,
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_uuid_get"),
+ _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([out_bl](bufferlist *bl) {
+ *bl = out_bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_mirror_mode_get(librados::MockTestMemIoCtxImpl *io_ctx_impl,
+ cls::rbd::MirrorMode mirror_mode, int r) {
+ bufferlist out_bl;
+ encode(mirror_mode, out_bl);
+
+ EXPECT_CALL(*io_ctx_impl,
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_mode_get"),
+ _, _, _, _))
+ .WillOnce(DoAll(WithArg<5>(Invoke([out_bl](bufferlist *bl) {
+ *bl = out_bl;
+ })),
+ Return(r)));
+ }
+
+ void expect_mirror_mode_get(librados::MockTestMemIoCtxImpl *io_ctx_impl) {
+ EXPECT_CALL(*io_ctx_impl,
+ exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_mode_get"),
+ _, _, _, _))
+ .WillRepeatedly(DoAll(WithArg<5>(Invoke([](bufferlist *bl) {
+ encode(cls::rbd::MIRROR_MODE_POOL, *bl);
+ })),
+ Return(0)));
+ }
+
+ void expect_leader_watcher_init(MockLeaderWatcher& mock_leader_watcher,
+ int r) {
+ EXPECT_CALL(mock_leader_watcher, init())
+ .WillOnce(Return(r));
+ }
+
+ void expect_leader_watcher_shut_down(MockLeaderWatcher& mock_leader_watcher) {
+ EXPECT_CALL(mock_leader_watcher, shut_down());
+ }
+
+ void expect_leader_watcher_get_leader_instance_id(
+ MockLeaderWatcher& mock_leader_watcher) {
+ EXPECT_CALL(mock_leader_watcher, get_leader_instance_id(_))
+ .WillRepeatedly(Return(true));
+ }
+
+ void expect_leader_watcher_list_instances(
+ MockLeaderWatcher& mock_leader_watcher) {
+ EXPECT_CALL(mock_leader_watcher, list_instances(_))
+ .Times(AtLeast(0));
+ }
+
+ void expect_remote_pool_poller_init(
+ MockRemotePoolPoller& mock_remote_pool_poller,
+ const RemotePoolMeta& remote_pool_meta, int r) {
+ EXPECT_CALL(mock_remote_pool_poller, init(_))
+ .WillOnce(Invoke(
+ [this, &mock_remote_pool_poller, remote_pool_meta, r]
+ (Context* ctx) {
+ if (r >= 0) {
+ mock_remote_pool_poller.listener->handle_updated(
+ remote_pool_meta);
+ }
+
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_remote_pool_poller_shut_down(
+ MockRemotePoolPoller& mock_remote_pool_poller, int r) {
+ EXPECT_CALL(mock_remote_pool_poller, shut_down(_))
+ .WillOnce(Invoke(
+ [this, r](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_leader_watcher_is_blocklisted(
+ MockLeaderWatcher &mock_leader_watcher, bool blocklisted) {
+ EXPECT_CALL(mock_leader_watcher, is_blocklisted())
+ .WillRepeatedly(Return(blocklisted));
+ }
+
+ void expect_namespace_replayer_is_blocklisted(
+ MockNamespaceReplayer &mock_namespace_replayer,
+ bool blocklisted) {
+ EXPECT_CALL(mock_namespace_replayer, is_blocklisted())
+ .WillRepeatedly(Return(blocklisted));
+ }
+
+ void expect_namespace_replayer_get_instance_id(
+ MockNamespaceReplayer &mock_namespace_replayer,
+ const std::string &instance_id) {
+ EXPECT_CALL(mock_namespace_replayer, get_instance_id())
+ .WillOnce(Return(instance_id));
+ }
+
+ void expect_namespace_replayer_init(
+ MockNamespaceReplayer &mock_namespace_replayer, int r,
+ Context *on_init = nullptr) {
+
+ EXPECT_CALL(mock_namespace_replayer, init(_))
+ .WillOnce(Invoke([this, r, on_init](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ if (on_init != nullptr) {
+ m_threads->work_queue->queue(on_init, r);
+ }
+ }));
+ }
+
+ void expect_namespace_replayer_shut_down(
+ MockNamespaceReplayer &mock_namespace_replayer,
+ Context *on_shut_down = nullptr) {
+ EXPECT_CALL(mock_namespace_replayer, shut_down(_))
+ .WillOnce(Invoke([this, on_shut_down](Context* ctx) {
+ m_threads->work_queue->queue(ctx);
+ if (on_shut_down != nullptr) {
+ m_threads->work_queue->queue(on_shut_down);
+ }
+ }));
+ }
+
+ void expect_namespace_replayer_handle_acquire_leader(
+ MockNamespaceReplayer &mock_namespace_replayer, int r,
+ Context *on_acquire = nullptr) {
+ EXPECT_CALL(mock_namespace_replayer, handle_acquire_leader(_))
+ .WillOnce(Invoke([this, r, on_acquire](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ if (on_acquire != nullptr) {
+ m_threads->work_queue->queue(on_acquire, r);
+ }
+ }));
+ }
+
+ void expect_namespace_replayer_handle_release_leader(
+ MockNamespaceReplayer &mock_namespace_replayer, int r,
+ Context *on_release = nullptr) {
+ EXPECT_CALL(mock_namespace_replayer, handle_release_leader(_))
+ .WillOnce(Invoke([this, r, on_release](Context* ctx) {
+ m_threads->work_queue->queue(ctx, r);
+ if (on_release != nullptr) {
+ m_threads->work_queue->queue(on_release, r);
+ }
+ }));
+ }
+
+ void expect_namespace_replayer_handle_update_leader(
+ MockNamespaceReplayer &mock_namespace_replayer,
+ const std::string &leader_instance_id,
+ Context *on_update = nullptr) {
+ EXPECT_CALL(mock_namespace_replayer,
+ handle_update_leader(leader_instance_id))
+ .WillOnce(Invoke([on_update](const std::string &) {
+ if (on_update != nullptr) {
+ on_update->complete(0);
+ }
+ }));
+ }
+
+ void expect_namespace_replayer_handle_instances_added(
+ MockNamespaceReplayer &mock_namespace_replayer) {
+ EXPECT_CALL(mock_namespace_replayer, handle_instances_added(_));
+ }
+
+ void expect_namespace_replayer_handle_instances_removed(
+ MockNamespaceReplayer &mock_namespace_replayer) {
+ EXPECT_CALL(mock_namespace_replayer, handle_instances_removed(_));
+ }
+
+ void expect_service_daemon_add_namespace(
+ MockServiceDaemon &mock_service_daemon,
+ const std::string& namespace_name) {
+ EXPECT_CALL(mock_service_daemon,
+ add_namespace(m_local_io_ctx.get_id(), namespace_name));
+ }
+
+ void expect_service_daemon_remove_namespace(
+ MockServiceDaemon &mock_service_daemon,
+ const std::string& namespace_name) {
+ EXPECT_CALL(mock_service_daemon,
+ remove_namespace(m_local_io_ctx.get_id(), namespace_name));
+ }
+
+ void expect_service_daemon_add_or_update_attribute(
+ MockServiceDaemon &mock_service_daemon, const std::string& key,
+ const service_daemon::AttributeValue& value) {
+ EXPECT_CALL(mock_service_daemon, add_or_update_attribute(_, key, value));
+ }
+
+ void expect_service_daemon_remove_attribute(
+ MockServiceDaemon &mock_service_daemon, const std::string& key) {
+ EXPECT_CALL(mock_service_daemon, remove_attribute(_, key));
+ }
+
+ void expect_service_daemon_add_or_update_instance_id_attribute(
+ MockServiceDaemon &mock_service_daemon, const std::string &instance_id) {
+ expect_service_daemon_add_or_update_attribute(
+ mock_service_daemon, "instance_id", {instance_id});
+ }
+
+ PoolMetaCache m_pool_meta_cache{g_ceph_context};
+};
+
+TEST_F(TestMockPoolReplayer, ConfigKeyOverride) {
+ PeerSpec peer_spec{"uuid", "cluster name", "client.name"};
+ peer_spec.mon_host = "123";
+ peer_spec.key = "234";
+
+ auto mock_default_namespace_replayer = new MockNamespaceReplayer();
+ expect_namespace_replayer_is_blocklisted(*mock_default_namespace_replayer,
+ false);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ auto mock_leader_watcher = new MockLeaderWatcher();
+ expect_leader_watcher_get_leader_instance_id(*mock_leader_watcher);
+ expect_leader_watcher_is_blocklisted(*mock_leader_watcher, false);
+
+ InSequence seq;
+
+ auto& mock_cluster = get_mock_cluster();
+ auto mock_local_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+ expect_connect(mock_cluster, mock_local_rados_client, "ceph", nullptr);
+
+ auto mock_remote_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+ CephContext* remote_cct = nullptr;
+ expect_connect(mock_cluster, mock_remote_rados_client, "cluster name",
+ &remote_cct);
+
+ auto mock_local_io_ctx = mock_local_rados_client->do_create_ioctx(
+ m_local_io_ctx.get_id(), m_local_io_ctx.get_pool_name());
+ expect_create_ioctx(mock_local_rados_client, mock_local_io_ctx);
+
+ expect_mirror_uuid_get(mock_local_io_ctx, "uuid", 0);
+ auto mock_remote_pool_poller = new MockRemotePoolPoller();
+ expect_remote_pool_poller_init(*mock_remote_pool_poller,
+ {"remote mirror uuid", ""}, 0);
+ expect_namespace_replayer_init(*mock_default_namespace_replayer, 0);
+ expect_leader_watcher_init(*mock_leader_watcher, 0);
+
+ MockServiceDaemon mock_service_daemon;
+ std::string instance_id = stringify(mock_local_io_ctx->get_instance_id());
+ expect_service_daemon_add_or_update_instance_id_attribute(
+ mock_service_daemon, instance_id);
+
+ MockPoolReplayer pool_replayer(&mock_threads, &mock_service_daemon, nullptr,
+ &m_pool_meta_cache,
+ m_local_io_ctx.get_id(), peer_spec, {});
+ pool_replayer.init("siteA");
+
+ ASSERT_TRUE(remote_cct != nullptr);
+ ASSERT_EQ("123", remote_cct->_conf.get_val<std::string>("mon_host"));
+ ASSERT_EQ("234", remote_cct->_conf.get_val<std::string>("key"));
+ remote_cct->put();
+
+ expect_leader_watcher_shut_down(*mock_leader_watcher);
+ expect_namespace_replayer_shut_down(*mock_default_namespace_replayer);
+ expect_remote_pool_poller_shut_down(*mock_remote_pool_poller, 0);
+
+ pool_replayer.shut_down();
+}
+
+TEST_F(TestMockPoolReplayer, AcquireReleaseLeader) {
+ PeerSpec peer_spec{"uuid", "cluster name", "client.name"};
+ peer_spec.mon_host = "123";
+ peer_spec.key = "234";
+
+ auto mock_default_namespace_replayer = new MockNamespaceReplayer();
+ expect_namespace_replayer_is_blocklisted(*mock_default_namespace_replayer,
+ false);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ auto mock_leader_watcher = new MockLeaderWatcher();
+ expect_leader_watcher_get_leader_instance_id(*mock_leader_watcher);
+ expect_leader_watcher_list_instances(*mock_leader_watcher);
+ expect_leader_watcher_is_blocklisted(*mock_leader_watcher, false);
+
+ InSequence seq;
+
+ auto& mock_cluster = get_mock_cluster();
+ auto mock_local_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+ expect_connect(mock_cluster, mock_local_rados_client, "ceph", nullptr);
+
+ auto mock_remote_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+ expect_connect(mock_cluster, mock_remote_rados_client, "cluster name",
+ nullptr);
+
+ auto mock_local_io_ctx = mock_local_rados_client->do_create_ioctx(
+ m_local_io_ctx.get_id(), m_local_io_ctx.get_pool_name());
+ expect_create_ioctx(mock_local_rados_client, mock_local_io_ctx);
+
+ expect_mirror_uuid_get(mock_local_io_ctx, "uuid", 0);
+ auto mock_remote_pool_poller = new MockRemotePoolPoller();
+ expect_remote_pool_poller_init(*mock_remote_pool_poller,
+ {"remote mirror uuid", ""}, 0);
+ expect_namespace_replayer_init(*mock_default_namespace_replayer, 0);
+ expect_leader_watcher_init(*mock_leader_watcher, 0);
+
+ MockServiceDaemon mock_service_daemon;
+ std::string instance_id = stringify(mock_local_io_ctx->get_instance_id());
+ expect_service_daemon_add_or_update_instance_id_attribute(
+ mock_service_daemon, instance_id);
+
+ MockPoolReplayer pool_replayer(&mock_threads, &mock_service_daemon, nullptr,
+ &m_pool_meta_cache,
+ m_local_io_ctx.get_id(), peer_spec, {});
+ pool_replayer.init("siteA");
+
+ expect_service_daemon_add_or_update_attribute(
+ mock_service_daemon, SERVICE_DAEMON_LEADER_KEY, true);
+ expect_namespace_replayer_handle_acquire_leader(
+ *mock_default_namespace_replayer, 0);
+
+ C_SaferCond on_acquire;
+ mock_leader_watcher->listener->post_acquire_handler(&on_acquire);
+ ASSERT_EQ(0, on_acquire.wait());
+
+ expect_service_daemon_remove_attribute(mock_service_daemon,
+ SERVICE_DAEMON_LEADER_KEY);
+ expect_namespace_replayer_handle_release_leader(
+ *mock_default_namespace_replayer, 0);
+
+ C_SaferCond on_release;
+ mock_leader_watcher->listener->pre_release_handler(&on_release);
+ ASSERT_EQ(0, on_release.wait());
+
+ expect_leader_watcher_shut_down(*mock_leader_watcher);
+ expect_namespace_replayer_shut_down(*mock_default_namespace_replayer);
+ expect_remote_pool_poller_shut_down(*mock_remote_pool_poller, 0);
+
+ pool_replayer.shut_down();
+}
+
+TEST_F(TestMockPoolReplayer, Namespaces) {
+ PeerSpec peer_spec{"uuid", "cluster name", "client.name"};
+ peer_spec.mon_host = "123";
+ peer_spec.key = "234";
+
+ g_ceph_context->_conf.set_val(
+ "rbd_mirror_pool_replayers_refresh_interval", "1");
+
+ MockNamespace mock_namespace;
+
+ auto mock_default_namespace_replayer = new MockNamespaceReplayer();
+ expect_namespace_replayer_is_blocklisted(*mock_default_namespace_replayer,
+ false);
+
+ auto mock_ns1_namespace_replayer = new MockNamespaceReplayer("ns1");
+ expect_namespace_replayer_is_blocklisted(*mock_ns1_namespace_replayer,
+ false);
+
+ auto mock_ns2_namespace_replayer = new MockNamespaceReplayer("ns2");
+ expect_namespace_replayer_is_blocklisted(*mock_ns2_namespace_replayer,
+ false);
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ auto mock_leader_watcher = new MockLeaderWatcher();
+ expect_leader_watcher_get_leader_instance_id(*mock_leader_watcher);
+ expect_leader_watcher_list_instances(*mock_leader_watcher);
+ expect_leader_watcher_is_blocklisted(*mock_leader_watcher, false);
+
+ auto& mock_cluster = get_mock_cluster();
+ auto mock_local_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+ auto mock_local_io_ctx = mock_local_rados_client->do_create_ioctx(
+ m_local_io_ctx.get_id(), m_local_io_ctx.get_pool_name());
+ auto mock_remote_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+
+ expect_mirror_mode_get(mock_local_io_ctx);
+
+ InSequence seq;
+
+ expect_connect(mock_cluster, mock_local_rados_client, "ceph", nullptr);
+ expect_connect(mock_cluster, mock_remote_rados_client, "cluster name",
+ nullptr);
+ expect_create_ioctx(mock_local_rados_client, mock_local_io_ctx);
+ expect_mirror_uuid_get(mock_local_io_ctx, "uuid", 0);
+ auto mock_remote_pool_poller = new MockRemotePoolPoller();
+ expect_remote_pool_poller_init(*mock_remote_pool_poller,
+ {"remote mirror uuid", ""}, 0);
+ expect_namespace_replayer_init(*mock_default_namespace_replayer, 0);
+ expect_leader_watcher_init(*mock_leader_watcher, 0);
+
+ MockServiceDaemon mock_service_daemon;
+ std::string instance_id = stringify(mock_local_io_ctx->get_instance_id());
+ expect_service_daemon_add_or_update_instance_id_attribute(
+ mock_service_daemon, instance_id);
+
+ MockPoolReplayer pool_replayer(&mock_threads, &mock_service_daemon, nullptr,
+ &m_pool_meta_cache,
+ m_local_io_ctx.get_id(), peer_spec, {});
+ pool_replayer.init("siteA");
+
+ C_SaferCond on_ns1_init;
+ expect_namespace_replayer_init(*mock_ns1_namespace_replayer, 0);
+ expect_service_daemon_add_namespace(mock_service_daemon, "ns1");
+ expect_namespace_replayer_handle_update_leader(*mock_ns1_namespace_replayer,
+ "", &on_ns1_init);
+
+ mock_namespace.add("ns1");
+ ASSERT_EQ(0, on_ns1_init.wait());
+
+ expect_service_daemon_add_or_update_attribute(
+ mock_service_daemon, SERVICE_DAEMON_LEADER_KEY, true);
+ expect_namespace_replayer_handle_acquire_leader(
+ *mock_default_namespace_replayer, 0);
+ expect_namespace_replayer_handle_acquire_leader(
+ *mock_ns1_namespace_replayer, 0);
+
+ C_SaferCond on_acquire;
+ mock_leader_watcher->listener->post_acquire_handler(&on_acquire);
+ ASSERT_EQ(0, on_acquire.wait());
+
+ expect_namespace_replayer_init(*mock_ns2_namespace_replayer, 0);
+ expect_service_daemon_add_namespace(mock_service_daemon, "ns2");
+ C_SaferCond on_ns2_acquire;
+ expect_namespace_replayer_handle_acquire_leader(
+ *mock_ns2_namespace_replayer, 0, &on_ns2_acquire);
+ expect_namespace_replayer_handle_instances_added(
+ *mock_ns2_namespace_replayer);
+
+ mock_namespace.add("ns2");
+ ASSERT_EQ(0, on_ns2_acquire.wait());
+
+ C_SaferCond on_ns2_shut_down;
+ expect_service_daemon_remove_namespace(mock_service_daemon, "ns2");
+ expect_namespace_replayer_shut_down(*mock_ns2_namespace_replayer,
+ &on_ns2_shut_down);
+ mock_namespace.remove("ns2");
+ ASSERT_EQ(0, on_ns2_shut_down.wait());
+
+ expect_service_daemon_remove_attribute(mock_service_daemon,
+ SERVICE_DAEMON_LEADER_KEY);
+ expect_namespace_replayer_handle_release_leader(
+ *mock_default_namespace_replayer, 0);
+ expect_namespace_replayer_handle_release_leader(
+ *mock_ns1_namespace_replayer, 0);
+
+ C_SaferCond on_release;
+ mock_leader_watcher->listener->pre_release_handler(&on_release);
+ ASSERT_EQ(0, on_release.wait());
+
+ expect_service_daemon_remove_namespace(mock_service_daemon, "ns1");
+ expect_namespace_replayer_shut_down(*mock_ns1_namespace_replayer);
+ expect_leader_watcher_shut_down(*mock_leader_watcher);
+ expect_namespace_replayer_shut_down(*mock_default_namespace_replayer);
+ expect_remote_pool_poller_shut_down(*mock_remote_pool_poller, 0);
+
+ pool_replayer.shut_down();
+}
+
+TEST_F(TestMockPoolReplayer, NamespacesError) {
+ PeerSpec peer_spec{"uuid", "cluster name", "client.name"};
+ peer_spec.mon_host = "123";
+ peer_spec.key = "234";
+
+ g_ceph_context->_conf.set_val(
+ "rbd_mirror_pool_replayers_refresh_interval", "1");
+
+ MockNamespace mock_namespace;
+
+ auto mock_default_namespace_replayer = new MockNamespaceReplayer();
+ expect_namespace_replayer_is_blocklisted(*mock_default_namespace_replayer,
+ false);
+ auto mock_ns1_namespace_replayer = new MockNamespaceReplayer("ns1");
+ auto mock_ns2_namespace_replayer = new MockNamespaceReplayer("ns2");
+ expect_namespace_replayer_is_blocklisted(*mock_ns2_namespace_replayer,
+ false);
+ auto mock_ns3_namespace_replayer = new MockNamespaceReplayer("ns3");
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ auto mock_leader_watcher = new MockLeaderWatcher();
+ expect_leader_watcher_get_leader_instance_id(*mock_leader_watcher);
+ expect_leader_watcher_list_instances(*mock_leader_watcher);
+ expect_leader_watcher_is_blocklisted(*mock_leader_watcher, false);
+
+ auto& mock_cluster = get_mock_cluster();
+ auto mock_local_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+ auto mock_local_io_ctx = mock_local_rados_client->do_create_ioctx(
+ m_local_io_ctx.get_id(), m_local_io_ctx.get_pool_name());
+ auto mock_remote_rados_client = mock_cluster.do_create_rados_client(
+ g_ceph_context);
+
+ expect_mirror_mode_get(mock_local_io_ctx);
+
+ InSequence seq;
+
+ expect_connect(mock_cluster, mock_local_rados_client, "ceph", nullptr);
+ expect_connect(mock_cluster, mock_remote_rados_client, "cluster name",
+ nullptr);
+ expect_create_ioctx(mock_local_rados_client, mock_local_io_ctx);
+ expect_mirror_uuid_get(mock_local_io_ctx, "uuid", 0);
+ auto mock_remote_pool_poller = new MockRemotePoolPoller();
+ expect_remote_pool_poller_init(*mock_remote_pool_poller,
+ {"remote mirror uuid", ""}, 0);
+ expect_namespace_replayer_init(*mock_default_namespace_replayer, 0);
+ expect_leader_watcher_init(*mock_leader_watcher, 0);
+
+ MockServiceDaemon mock_service_daemon;
+ std::string instance_id = stringify(mock_local_io_ctx->get_instance_id());
+ expect_service_daemon_add_or_update_instance_id_attribute(
+ mock_service_daemon, instance_id);
+
+ MockPoolReplayer pool_replayer(&mock_threads, &mock_service_daemon, nullptr,
+ &m_pool_meta_cache,
+ m_local_io_ctx.get_id(), peer_spec, {});
+ pool_replayer.init("siteA");
+
+ // test namespace replayer init fails for non leader
+
+ C_SaferCond on_ns1_init;
+ Context* ctx = new LambdaContext(
+ [&mock_namespace, &on_ns1_init](int r) {
+ mock_namespace.remove("ns1");
+ on_ns1_init.complete(r);
+ });
+ expect_namespace_replayer_init(*mock_ns1_namespace_replayer, -EINVAL, ctx);
+ mock_namespace.add("ns1");
+ ASSERT_EQ(-EINVAL, on_ns1_init.wait());
+
+ // test acquire leader fails when default namespace replayer fails
+
+ expect_service_daemon_add_or_update_attribute(
+ mock_service_daemon, SERVICE_DAEMON_LEADER_KEY, true);
+ expect_namespace_replayer_handle_acquire_leader(
+ *mock_default_namespace_replayer, -EINVAL);
+
+ C_SaferCond on_acquire1;
+ mock_leader_watcher->listener->post_acquire_handler(&on_acquire1);
+ ASSERT_EQ(-EINVAL, on_acquire1.wait());
+
+ // test acquire leader succeeds when non-default namespace replayer fails
+
+ C_SaferCond on_ns2_init;
+ expect_namespace_replayer_init(*mock_ns2_namespace_replayer, 0);
+ expect_service_daemon_add_namespace(mock_service_daemon, "ns2");
+ expect_namespace_replayer_handle_update_leader(*mock_ns2_namespace_replayer,
+ "", &on_ns2_init);
+ mock_namespace.add("ns2");
+ ASSERT_EQ(0, on_ns2_init.wait());
+
+ expect_service_daemon_add_or_update_attribute(
+ mock_service_daemon, SERVICE_DAEMON_LEADER_KEY, true);
+ expect_namespace_replayer_handle_acquire_leader(
+ *mock_default_namespace_replayer, 0);
+
+ expect_namespace_replayer_handle_acquire_leader(*mock_ns2_namespace_replayer,
+ -EINVAL);
+ ctx = new LambdaContext(
+ [&mock_namespace](int) {
+ mock_namespace.remove("ns2");
+ });
+ expect_service_daemon_remove_namespace(mock_service_daemon, "ns2");
+ expect_namespace_replayer_shut_down(*mock_ns2_namespace_replayer, ctx);
+ mock_namespace.add("ns2");
+
+ C_SaferCond on_acquire2;
+ mock_leader_watcher->listener->post_acquire_handler(&on_acquire2);
+ ASSERT_EQ(0, on_acquire2.wait());
+
+ // test namespace replayer init fails on acquire leader
+
+ C_SaferCond on_ns3_shut_down;
+ ctx = new LambdaContext(
+ [&mock_namespace, &on_ns3_shut_down](int) {
+ mock_namespace.remove("ns3");
+ on_ns3_shut_down.complete(0);
+ });
+ expect_namespace_replayer_init(*mock_ns3_namespace_replayer, 0);
+ expect_service_daemon_add_namespace(mock_service_daemon, "ns3");
+ expect_namespace_replayer_handle_acquire_leader(*mock_ns3_namespace_replayer,
+ -EINVAL);
+ expect_service_daemon_remove_namespace(mock_service_daemon, "ns3");
+ expect_namespace_replayer_shut_down(*mock_ns3_namespace_replayer, ctx);
+ mock_namespace.add("ns3");
+ ASSERT_EQ(0, on_ns3_shut_down.wait());
+
+ expect_leader_watcher_shut_down(*mock_leader_watcher);
+ expect_namespace_replayer_shut_down(*mock_default_namespace_replayer);
+ expect_remote_pool_poller_shut_down(*mock_remote_pool_poller, 0);
+
+ pool_replayer.shut_down();
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_PoolWatcher.cc b/src/test/rbd_mirror/test_mock_PoolWatcher.cc
new file mode 100644
index 000000000..958d93459
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_PoolWatcher.cc
@@ -0,0 +1,730 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
+#include "librbd/MirroringWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
+#include "tools/rbd_mirror/PoolWatcher.h"
+#include "tools/rbd_mirror/pool_watcher/RefreshImagesRequest.h"
+#include "include/stringify.h"
+
+using namespace std::chrono_literals;
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+struct MockMirroringWatcher {
+ static MockMirroringWatcher *s_instance;
+ static MockMirroringWatcher &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ MockMirroringWatcher() {
+ s_instance = this;
+ }
+
+ MOCK_CONST_METHOD0(is_unregistered, bool());
+ MOCK_METHOD1(register_watch, void(Context*));
+ MOCK_METHOD1(unregister_watch, void(Context*));
+
+ MOCK_CONST_METHOD0(get_oid, std::string());
+};
+
+template <>
+struct MirroringWatcher<MockTestImageCtx> {
+ static MirroringWatcher *s_instance;
+
+ MirroringWatcher(librados::IoCtx &io_ctx, ::MockContextWQ *work_queue) {
+ s_instance = this;
+ }
+ virtual ~MirroringWatcher() {
+ }
+
+ static MirroringWatcher<MockTestImageCtx> &get_instance() {
+ ceph_assert(s_instance != nullptr);
+ return *s_instance;
+ }
+
+ virtual void handle_rewatch_complete(int r) = 0;
+
+ virtual void handle_mode_updated(cls::rbd::MirrorMode mirror_mode) = 0;
+ virtual void handle_image_updated(cls::rbd::MirrorImageState state,
+ const std::string &remote_image_id,
+ const std::string &global_image_id) = 0;
+
+ bool is_unregistered() const {
+ return MockMirroringWatcher::get_instance().is_unregistered();
+ }
+ void register_watch(Context *ctx) {
+ MockMirroringWatcher::get_instance().register_watch(ctx);
+ }
+ void unregister_watch(Context *ctx) {
+ MockMirroringWatcher::get_instance().unregister_watch(ctx);
+ }
+ std::string get_oid() const {
+ return MockMirroringWatcher::get_instance().get_oid();
+ }
+};
+
+MockMirroringWatcher *MockMirroringWatcher::s_instance = nullptr;
+MirroringWatcher<MockTestImageCtx> *MirroringWatcher<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace librbd
+
+namespace rbd {
+namespace mirror {
+
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ ceph::mutex &timer_lock;
+
+ MockContextWQ *work_queue;
+
+ Threads(Threads<librbd::ImageCtx> *threads)
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
+ }
+};
+
+namespace pool_watcher {
+
+template <>
+struct RefreshImagesRequest<librbd::MockTestImageCtx> {
+ ImageIds *image_ids = nullptr;
+ Context *on_finish = nullptr;
+ static RefreshImagesRequest *s_instance;
+ static RefreshImagesRequest *create(librados::IoCtx &io_ctx,
+ ImageIds *image_ids,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->image_ids = image_ids;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ RefreshImagesRequest() {
+ s_instance = this;
+ }
+};
+
+RefreshImagesRequest<librbd::MockTestImageCtx> *RefreshImagesRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace pool_watcher
+
+} // namespace mirror
+} // namespace rbd
+
+// template definitions
+#include "tools/rbd_mirror/PoolWatcher.cc"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnArg;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::WithoutArgs;
+
+class TestMockPoolWatcher : public TestMockFixture {
+public:
+ typedef PoolWatcher<librbd::MockTestImageCtx> MockPoolWatcher;
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
+ typedef pool_watcher::RefreshImagesRequest<librbd::MockTestImageCtx> MockRefreshImagesRequest;
+ typedef librbd::MockMirroringWatcher MockMirroringWatcher;
+ typedef librbd::MirroringWatcher<librbd::MockTestImageCtx> MirroringWatcher;
+
+ struct MockListener : pool_watcher::Listener {
+ TestMockPoolWatcher *test;
+
+ MockListener(TestMockPoolWatcher *test) : test(test) {
+ }
+
+ MOCK_METHOD3(mock_handle_update, void(const std::string &, const ImageIds &,
+ const ImageIds &));
+ void handle_update(const std::string &mirror_uuid,
+ ImageIds &&added_image_ids,
+ ImageIds &&removed_image_ids) override {
+ mock_handle_update(mirror_uuid, added_image_ids, removed_image_ids);
+ }
+ };
+
+ TestMockPoolWatcher() = default;
+
+ void expect_work_queue(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_mirroring_watcher_is_unregistered(MockMirroringWatcher &mock_mirroring_watcher,
+ bool unregistered) {
+ EXPECT_CALL(mock_mirroring_watcher, is_unregistered())
+ .WillOnce(Return(unregistered));
+ }
+
+ void expect_mirroring_watcher_register(MockMirroringWatcher &mock_mirroring_watcher,
+ int r) {
+ EXPECT_CALL(mock_mirroring_watcher, register_watch(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_mirroring_watcher_unregister(MockMirroringWatcher &mock_mirroring_watcher,
+ int r) {
+ EXPECT_CALL(mock_mirroring_watcher, unregister_watch(_))
+ .WillOnce(CompleteContext(r));
+ }
+
+ void expect_refresh_images(MockRefreshImagesRequest &request,
+ const ImageIds &image_ids, int r) {
+ EXPECT_CALL(request, send())
+ .WillOnce(Invoke([&request, image_ids, r]() {
+ *request.image_ids = image_ids;
+ request.on_finish->complete(r);
+ }));
+ }
+
+ void expect_listener_handle_update(MockListener &mock_listener,
+ const std::string &mirror_uuid,
+ const ImageIds &added_image_ids,
+ const ImageIds &removed_image_ids) {
+ EXPECT_CALL(mock_listener, mock_handle_update(mirror_uuid, added_image_ids,
+ removed_image_ids))
+ .WillOnce(WithoutArgs(Invoke([this]() {
+ std::lock_guard locker{m_lock};
+ ++m_update_count;
+ m_cond.notify_all();
+ })));
+ }
+
+ void expect_timer_add_event(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillOnce(DoAll(WithArg<1>(Invoke([this](Context *ctx) {
+ auto wrapped_ctx =
+ new LambdaContext([this, ctx](int r) {
+ std::lock_guard timer_locker{m_threads->timer_lock};
+ ctx->complete(r);
+ });
+ m_threads->work_queue->queue(wrapped_ctx, 0);
+ })),
+ ReturnArg<1>()));
+ }
+
+ int when_shut_down(MockPoolWatcher &mock_pool_watcher) {
+ C_SaferCond ctx;
+ mock_pool_watcher.shut_down(&ctx);
+ return ctx.wait();
+ }
+
+ bool wait_for_update(uint32_t count) {
+ std::unique_lock locker{m_lock};
+ if (m_cond.wait_for(locker, 10s,
+ [count, this] { return m_update_count >= count; })) {
+ m_update_count -= count;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ ceph::mutex m_lock = ceph::make_mutex("TestMockPoolWatcher::m_lock");
+ ceph::condition_variable m_cond;
+ uint32_t m_update_count = 0;
+};
+
+TEST_F(TestMockPoolWatcher, EmptyPool) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_TRUE(wait_for_update(1));
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, NonEmptyPool) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ ImageIds image_ids{
+ {"global id 1", "remote id 1"},
+ {"global id 2", "remote id 2"}};
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, image_ids, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", image_ids, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_TRUE(wait_for_update(1));
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, NotifyDuringRefresh) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ ImageIds image_ids{
+ {"global id 1", "remote id 1"},
+ {"global id 2", "remote id 2"}};
+ MockRefreshImagesRequest mock_refresh_images_request;
+ bool refresh_sent = false;
+ EXPECT_CALL(mock_refresh_images_request, send())
+ .WillOnce(Invoke([this, &mock_refresh_images_request, &image_ids,
+ &refresh_sent]() {
+ *mock_refresh_images_request.image_ids = image_ids;
+
+ std::lock_guard locker{m_lock};
+ refresh_sent = true;
+ m_cond.notify_all();
+ }));
+
+
+ MockListener mock_listener(this);
+ image_ids = {
+ {"global id 1", "remote id 1a"},
+ {"global id 3", "remote id 3"}};
+ expect_listener_handle_update(mock_listener, "remote uuid", image_ids, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ mock_pool_watcher.init(nullptr);
+
+ {
+ std::unique_lock locker{m_lock};
+ m_cond.wait(locker, [&] { return refresh_sent; });
+ }
+
+ MirroringWatcher::get_instance().handle_image_updated(
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING, "remote id 2", "global id 2");
+ MirroringWatcher::get_instance().handle_image_updated(
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 1a", "global id 1");
+ MirroringWatcher::get_instance().handle_image_updated(
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 3", "global id 3");
+
+ mock_refresh_images_request.on_finish->complete(0);
+ ASSERT_TRUE(wait_for_update(1));
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, Notify) {
+ MockThreads mock_threads(m_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ ImageIds image_ids{
+ {"global id 1", "remote id 1"},
+ {"global id 2", "remote id 2"}};
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, image_ids, 0);
+
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillOnce(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", image_ids, {});
+
+ Context *notify_ctx = nullptr;
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillOnce(Invoke([this, &notify_ctx](Context *ctx, int r) {
+ std::lock_guard locker{m_lock};
+ ASSERT_EQ(nullptr, notify_ctx);
+ notify_ctx = ctx;
+ m_cond.notify_all();
+ }));
+ expect_listener_handle_update(
+ mock_listener, "remote uuid",
+ {{"global id 1", "remote id 1a"}, {"global id 3", "remote id 3"}},
+ {{"global id 1", "remote id 1"}, {"global id 2", "remote id 2"}});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(wait_for_update(1));
+
+ C_SaferCond flush_ctx;
+ m_threads->work_queue->queue(&flush_ctx, 0);
+ ASSERT_EQ(0, flush_ctx.wait());
+
+ MirroringWatcher::get_instance().handle_image_updated(
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING, "remote id 2", "global id 2");
+ MirroringWatcher::get_instance().handle_image_updated(
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLED, "remote id 2", "global id 2");
+ MirroringWatcher::get_instance().handle_image_updated(
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 1a", "global id 1");
+ MirroringWatcher::get_instance().handle_image_updated(
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 3", "global id 3");
+ notify_ctx->complete(0);
+
+ ASSERT_TRUE(wait_for_update(1));
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RegisterWatcherBlocklist) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, -EBLOCKLISTED);
+
+ MockListener mock_listener(this);
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+ ASSERT_TRUE(mock_pool_watcher.is_blocklisted());
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RegisterWatcherMissing) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, -ENOENT);
+ expect_timer_add_event(mock_threads);
+
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(-ENOENT, ctx.wait());
+
+ ASSERT_TRUE(wait_for_update(1));
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RegisterWatcherError) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, -EINVAL);
+ expect_timer_add_event(mock_threads);
+
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_TRUE(wait_for_update(1));
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RefreshBlocklist) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, -EBLOCKLISTED);
+
+ MockListener mock_listener(this);
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
+ ASSERT_TRUE(mock_pool_watcher.is_blocklisted());
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RefreshMissing) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, -ENOENT);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_TRUE(wait_for_update(1));
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RefreshError) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, -EINVAL);
+ expect_timer_add_event(mock_threads);
+
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
+ ASSERT_TRUE(wait_for_update(1));
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, Rewatch) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ expect_timer_add_event(mock_threads);
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
+ expect_refresh_images(mock_refresh_images_request, {{"global id", "image id"}}, 0);
+ expect_listener_handle_update(mock_listener, "remote uuid",
+ {{"global id", "image id"}}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(wait_for_update(1));
+
+ MirroringWatcher::get_instance().handle_rewatch_complete(0);
+ ASSERT_TRUE(wait_for_update(1));
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RewatchBlocklist) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(wait_for_update(1));
+
+ MirroringWatcher::get_instance().handle_rewatch_complete(-EBLOCKLISTED);
+ ASSERT_TRUE(mock_pool_watcher.is_blocklisted());
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, RewatchError) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ expect_timer_add_event(mock_threads);
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
+ expect_refresh_images(mock_refresh_images_request, {{"global id", "image id"}}, 0);
+ expect_listener_handle_update(mock_listener, "remote uuid",
+ {{"global id", "image id"}}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(wait_for_update(1));
+
+ MirroringWatcher::get_instance().handle_rewatch_complete(-EINVAL);
+ ASSERT_TRUE(wait_for_update(1));
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+TEST_F(TestMockPoolWatcher, DeferredRefresh) {
+ MockThreads mock_threads(m_threads);
+ expect_work_queue(mock_threads);
+
+ InSequence seq;
+ MockMirroringWatcher mock_mirroring_watcher;
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
+ expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
+
+ MockRefreshImagesRequest mock_refresh_images_request;
+
+ EXPECT_CALL(mock_refresh_images_request, send())
+ .WillOnce(Invoke([&mock_refresh_images_request]() {
+ *mock_refresh_images_request.image_ids = {};
+ MirroringWatcher::get_instance().handle_rewatch_complete(0);
+ mock_refresh_images_request.on_finish->complete(0);
+ }));
+ expect_timer_add_event(mock_threads);
+
+ expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
+ expect_refresh_images(mock_refresh_images_request, {}, 0);
+
+ MockListener mock_listener(this);
+ expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
+
+ MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
+ "remote uuid", mock_listener);
+ C_SaferCond ctx;
+ mock_pool_watcher.init(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(wait_for_update(1));
+
+ expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
+ ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_Throttler.cc b/src/test/rbd_mirror/test_mock_Throttler.cc
new file mode 100644
index 000000000..ab562c18d
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_Throttler.cc
@@ -0,0 +1,253 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+ MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ : librbd::MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/Throttler.cc"
+
+namespace rbd {
+namespace mirror {
+
+class TestMockThrottler : public TestMockFixture {
+public:
+ typedef Throttler<librbd::MockTestImageCtx> MockThrottler;
+
+};
+
+TEST_F(TestMockThrottler, Single_Sync) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ C_SaferCond on_start;
+ throttler.start_op("ns", "id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+ throttler.finish_op("ns", "id");
+}
+
+TEST_F(TestMockThrottler, Multiple_Syncs) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(2);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id2", &on_start2);
+ C_SaferCond on_start3;
+ throttler.start_op("ns", "id3", &on_start3);
+ C_SaferCond on_start4;
+ throttler.start_op("ns", "id4", &on_start4);
+
+ ASSERT_EQ(0, on_start2.wait());
+ throttler.finish_op("ns", "id2");
+ ASSERT_EQ(0, on_start3.wait());
+ throttler.finish_op("ns", "id3");
+ ASSERT_EQ(0, on_start1.wait());
+ throttler.finish_op("ns", "id1");
+ ASSERT_EQ(0, on_start4.wait());
+ throttler.finish_op("ns", "id4");
+}
+
+TEST_F(TestMockThrottler, Cancel_Running_Sync) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ C_SaferCond on_start;
+ throttler.start_op("ns", "id", &on_start);
+ ASSERT_EQ(0, on_start.wait());
+ ASSERT_FALSE(throttler.cancel_op("ns", "id"));
+ throttler.finish_op("ns", "id");
+}
+
+TEST_F(TestMockThrottler, Cancel_Waiting_Sync) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(1);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id2", &on_start2);
+
+ ASSERT_EQ(0, on_start1.wait());
+ ASSERT_TRUE(throttler.cancel_op("ns", "id2"));
+ ASSERT_EQ(-ECANCELED, on_start2.wait());
+ throttler.finish_op("ns", "id1");
+}
+
+TEST_F(TestMockThrottler, Cancel_Running_Sync_Start_Waiting) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(1);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id2", &on_start2);
+
+ ASSERT_EQ(0, on_start1.wait());
+ ASSERT_FALSE(throttler.cancel_op("ns", "id1"));
+ throttler.finish_op("ns", "id1");
+ ASSERT_EQ(0, on_start2.wait());
+ throttler.finish_op("ns", "id2");
+}
+
+TEST_F(TestMockThrottler, Duplicate) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(1);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ ASSERT_EQ(0, on_start1.wait());
+
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id1", &on_start2);
+ ASSERT_EQ(0, on_start2.wait());
+
+ C_SaferCond on_start3;
+ throttler.start_op("ns", "id2", &on_start3);
+ C_SaferCond on_start4;
+ throttler.start_op("ns", "id2", &on_start4);
+ ASSERT_EQ(-ENOENT, on_start3.wait());
+
+ throttler.finish_op("ns", "id1");
+ ASSERT_EQ(0, on_start4.wait());
+ throttler.finish_op("ns", "id2");
+}
+
+TEST_F(TestMockThrottler, Duplicate2) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(2);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ ASSERT_EQ(0, on_start1.wait());
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id2", &on_start2);
+ ASSERT_EQ(0, on_start2.wait());
+
+ C_SaferCond on_start3;
+ throttler.start_op("ns", "id3", &on_start3);
+ C_SaferCond on_start4;
+ throttler.start_op("ns", "id3", &on_start4); // dup
+ ASSERT_EQ(-ENOENT, on_start3.wait());
+
+ C_SaferCond on_start5;
+ throttler.start_op("ns", "id4", &on_start5);
+
+ throttler.finish_op("ns", "id1");
+ ASSERT_EQ(0, on_start4.wait());
+
+ throttler.finish_op("ns", "id2");
+ ASSERT_EQ(0, on_start5.wait());
+
+ C_SaferCond on_start6;
+ throttler.start_op("ns", "id5", &on_start6);
+
+ throttler.finish_op("ns", "id3");
+ ASSERT_EQ(0, on_start6.wait());
+
+ throttler.finish_op("ns", "id4");
+ throttler.finish_op("ns", "id5");
+}
+
+TEST_F(TestMockThrottler, Increase_Max_Concurrent_Syncs) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(2);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id2", &on_start2);
+ C_SaferCond on_start3;
+ throttler.start_op("ns", "id3", &on_start3);
+ C_SaferCond on_start4;
+ throttler.start_op("ns", "id4", &on_start4);
+ C_SaferCond on_start5;
+ throttler.start_op("ns", "id5", &on_start5);
+
+ ASSERT_EQ(0, on_start1.wait());
+ ASSERT_EQ(0, on_start2.wait());
+
+ throttler.set_max_concurrent_ops(4);
+
+ ASSERT_EQ(0, on_start3.wait());
+ ASSERT_EQ(0, on_start4.wait());
+
+ throttler.finish_op("ns", "id4");
+ ASSERT_EQ(0, on_start5.wait());
+
+ throttler.finish_op("ns", "id1");
+ throttler.finish_op("ns", "id2");
+ throttler.finish_op("ns", "id3");
+ throttler.finish_op("ns", "id5");
+}
+
+TEST_F(TestMockThrottler, Decrease_Max_Concurrent_Syncs) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(4);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id2", &on_start2);
+ C_SaferCond on_start3;
+ throttler.start_op("ns", "id3", &on_start3);
+ C_SaferCond on_start4;
+ throttler.start_op("ns", "id4", &on_start4);
+ C_SaferCond on_start5;
+ throttler.start_op("ns", "id5", &on_start5);
+
+ ASSERT_EQ(0, on_start1.wait());
+ ASSERT_EQ(0, on_start2.wait());
+ ASSERT_EQ(0, on_start3.wait());
+ ASSERT_EQ(0, on_start4.wait());
+
+ throttler.set_max_concurrent_ops(2);
+
+ throttler.finish_op("ns", "id1");
+ throttler.finish_op("ns", "id2");
+ throttler.finish_op("ns", "id3");
+
+ ASSERT_EQ(0, on_start5.wait());
+
+ throttler.finish_op("ns", "id4");
+ throttler.finish_op("ns", "id5");
+}
+
+TEST_F(TestMockThrottler, Drain) {
+ MockThrottler throttler(g_ceph_context, "rbd_mirror_concurrent_image_syncs");
+ throttler.set_max_concurrent_ops(1);
+
+ C_SaferCond on_start1;
+ throttler.start_op("ns", "id1", &on_start1);
+ C_SaferCond on_start2;
+ throttler.start_op("ns", "id2", &on_start2);
+
+ ASSERT_EQ(0, on_start1.wait());
+ throttler.drain("ns", -ESTALE);
+ ASSERT_EQ(-ESTALE, on_start2.wait());
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_mock_fixture.cc b/src/test/rbd_mirror/test_mock_fixture.cc
new file mode 100644
index 000000000..9e308a63b
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_fixture.cc
@@ -0,0 +1,64 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "include/rbd/librbd.hpp"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "test/librados_test_stub/MockTestMemCluster.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+
+namespace rbd {
+namespace mirror {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::WithArg;
+
+TestMockFixture::TestClusterRef TestMockFixture::s_test_cluster;
+
+void TestMockFixture::SetUpTestCase() {
+ s_test_cluster = librados_test_stub::get_cluster();
+
+ // use a mock version of the in-memory rados client
+ librados_test_stub::set_cluster(boost::shared_ptr<librados::TestCluster>(
+ new ::testing::NiceMock<librados::MockTestMemCluster>()));
+ TestFixture::SetUpTestCase();
+}
+
+void TestMockFixture::TearDownTestCase() {
+ TestFixture::TearDownTestCase();
+ librados_test_stub::set_cluster(s_test_cluster);
+}
+
+void TestMockFixture::TearDown() {
+ // Mock rados client lives across tests -- reset it to initial state
+ librados::MockTestMemRadosClient *mock_rados_client =
+ get_mock_io_ctx(m_local_io_ctx).get_mock_rados_client();
+ ASSERT_TRUE(mock_rados_client != nullptr);
+
+ ::testing::Mock::VerifyAndClear(mock_rados_client);
+ mock_rados_client->default_to_dispatch();
+ dynamic_cast<librados::MockTestMemCluster*>(
+ librados_test_stub::get_cluster().get())->default_to_dispatch();
+
+ TestFixture::TearDown();
+}
+
+void TestMockFixture::expect_test_features(librbd::MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_, _))
+ .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) {
+ return (mock_image_ctx.features & features) != 0;
+ })));
+}
+
+librados::MockTestMemCluster& TestMockFixture::get_mock_cluster() {
+ librados::MockTestMemCluster* mock_cluster = dynamic_cast<
+ librados::MockTestMemCluster*>(librados_test_stub::get_cluster().get());
+ ceph_assert(mock_cluster != nullptr);
+ return *mock_cluster;
+}
+
+} // namespace mirror
+} // namespace rbd
+
diff --git a/src/test/rbd_mirror/test_mock_fixture.h b/src/test/rbd_mirror/test_mock_fixture.h
new file mode 100644
index 000000000..16b6dc6b8
--- /dev/null
+++ b/src/test/rbd_mirror/test_mock_fixture.h
@@ -0,0 +1,72 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_RBD_MIRROR_TEST_MOCK_FIXTURE_H
+#define CEPH_TEST_RBD_MIRROR_TEST_MOCK_FIXTURE_H
+
+#include "test/rbd_mirror/test_fixture.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "common/WorkQueue.h"
+#include "librbd/asio/ContextWQ.h"
+#include <boost/shared_ptr.hpp>
+#include <gmock/gmock.h>
+#include "include/ceph_assert.h"
+
+namespace librados {
+class TestRadosClient;
+class MockTestMemCluster;
+class MockTestMemIoCtxImpl;
+class MockTestMemRadosClient;
+}
+
+namespace librbd {
+class MockImageCtx;
+}
+
+ACTION_P(CopyInBufferlist, str) {
+ arg0->append(str);
+}
+
+ACTION_P(CompleteContext, r) {
+ arg0->complete(r);
+}
+
+ACTION_P2(CompleteContext, wq, r) {
+ auto context_wq = reinterpret_cast<librbd::asio::ContextWQ *>(wq);
+ context_wq->queue(arg0, r);
+}
+
+ACTION_P(GetReference, ref_object) {
+ ref_object->get();
+}
+
+MATCHER_P(ContentsEqual, bl, "") {
+ // TODO fix const-correctness of bufferlist
+ return const_cast<bufferlist &>(arg).contents_equal(
+ const_cast<bufferlist &>(bl));
+}
+
+namespace rbd {
+namespace mirror {
+
+class TestMockFixture : public TestFixture {
+public:
+ typedef boost::shared_ptr<librados::TestCluster> TestClusterRef;
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ void TearDown() override;
+
+ void expect_test_features(librbd::MockImageCtx &mock_image_ctx);
+
+ librados::MockTestMemCluster& get_mock_cluster();
+
+private:
+ static TestClusterRef s_test_cluster;
+};
+
+} // namespace mirror
+} // namespace rbd
+
+#endif // CEPH_TEST_RBD_MIRROR_TEST_MOCK_FIXTURE_H
diff --git a/src/test/rgw/CMakeLists.txt b/src/test/rgw/CMakeLists.txt
new file mode 100644
index 000000000..57d6696c7
--- /dev/null
+++ b/src/test/rgw/CMakeLists.txt
@@ -0,0 +1,282 @@
+if(WITH_RADOSGW_AMQP_ENDPOINT)
+ # amqp mock library
+ set(amqp_mock_src
+ amqp_mock.cc amqp_url.c)
+ add_library(amqp_mock STATIC ${amqp_mock_src})
+endif()
+
+if(WITH_RADOSGW_KAFKA_ENDPOINT)
+ # kafka stub library
+ set(kafka_stub_src
+ kafka_stub.cc)
+ add_library(kafka_stub STATIC ${kafka_stub_src})
+endif()
+
+if(WITH_RADOSGW_LUA_PACKAGES)
+ list(APPEND rgw_libs Boost::filesystem)
+endif()
+
+if(WITH_JAEGER)
+ list(APPEND rgw_libs ${jaeger_base})
+endif()
+
+#unittest_rgw_bencode
+add_executable(unittest_rgw_bencode test_rgw_bencode.cc)
+add_ceph_unittest(unittest_rgw_bencode)
+target_link_libraries(unittest_rgw_bencode ${rgw_libs})
+
+# unittest_rgw_bucket_sync_cache
+add_executable(unittest_rgw_bucket_sync_cache test_rgw_bucket_sync_cache.cc)
+add_ceph_unittest(unittest_rgw_bucket_sync_cache)
+target_link_libraries(unittest_rgw_bucket_sync_cache ${rgw_libs})
+
+#unitttest_rgw_period_history
+add_executable(unittest_rgw_period_history test_rgw_period_history.cc)
+add_ceph_unittest(unittest_rgw_period_history)
+target_link_libraries(unittest_rgw_period_history ${rgw_libs})
+
+# unitttest_rgw_compression
+add_executable(unittest_rgw_compression
+ test_rgw_compression.cc
+ $<TARGET_OBJECTS:unit-main>)
+add_ceph_unittest(unittest_rgw_compression)
+target_link_libraries(unittest_rgw_compression ${rgw_libs})
+
+# unitttest_http_manager
+add_executable(unittest_http_manager test_http_manager.cc)
+add_ceph_unittest(unittest_http_manager)
+target_link_libraries(unittest_http_manager ${rgw_libs})
+
+# unitttest_rgw_reshard_wait
+add_executable(unittest_rgw_reshard_wait test_rgw_reshard_wait.cc)
+add_ceph_unittest(unittest_rgw_reshard_wait)
+target_link_libraries(unittest_rgw_reshard_wait ${rgw_libs})
+
+set(test_rgw_a_src test_rgw_common.cc)
+add_library(test_rgw_a STATIC ${test_rgw_a_src})
+target_link_libraries(test_rgw_a ${rgw_libs})
+
+add_executable(bench_rgw_ratelimit bench_rgw_ratelimit.cc)
+target_link_libraries(bench_rgw_ratelimit ${rgw_libs})
+
+add_executable(bench_rgw_ratelimit_gc bench_rgw_ratelimit_gc.cc )
+target_link_libraries(bench_rgw_ratelimit_gc ${rgw_libs})
+
+add_executable(unittest_rgw_ratelimit test_rgw_ratelimit.cc $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(unittest_rgw_ratelimit ${rgw_libs})
+add_ceph_unittest(unittest_rgw_ratelimit)
+
+# ceph_test_rgw_manifest
+set(test_rgw_manifest_srcs test_rgw_manifest.cc)
+add_executable(ceph_test_rgw_manifest
+ ${test_rgw_manifest_srcs}
+ )
+target_link_libraries(ceph_test_rgw_manifest
+ test_rgw_a
+ cls_rgw_client
+ cls_lock_client
+ cls_refcount_client
+ cls_log_client
+ cls_timeindex_client
+ cls_version_client
+ cls_user_client
+ librados
+ global
+ ${BLKID_LIBRARIES}
+ ${CURL_LIBRARIES}
+ ${EXPAT_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ${CRYPTO_LIBS})
+
+set(test_rgw_obj_srcs test_rgw_obj.cc)
+add_executable(ceph_test_rgw_obj
+ ${test_rgw_obj_srcs}
+ )
+target_link_libraries(ceph_test_rgw_obj
+ test_rgw_a
+ cls_rgw_client
+ cls_lock_client
+ cls_refcount_client
+ cls_log_client
+ cls_version_client
+ cls_user_client
+ librados
+ global
+ ceph-common
+ ${CURL_LIBRARIES}
+ ${EXPAT_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ${CRYPTO_LIBS}
+ )
+install(TARGETS ceph_test_rgw_obj DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+set(test_rgw_crypto_srcs test_rgw_crypto.cc)
+add_executable(unittest_rgw_crypto
+ ${test_rgw_crypto_srcs}
+ )
+add_ceph_unittest(unittest_rgw_crypto)
+target_link_libraries(unittest_rgw_crypto
+ ${rgw_libs}
+ cls_rgw_client
+ cls_lock_client
+ cls_refcount_client
+ cls_log_client
+ cls_version_client
+ cls_user_client
+ librados
+ global
+ ${CURL_LIBRARIES}
+ ${EXPAT_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ${CRYPTO_LIBS}
+ )
+
+set(test_rgw_reshard_srcs test_rgw_reshard.cc)
+add_executable(unittest_rgw_reshard
+ ${test_rgw_reshard_srcs}
+ )
+add_ceph_unittest(unittest_rgw_reshard)
+target_link_libraries(unittest_rgw_reshard
+ ${rgw_libs}
+ )
+
+add_executable(unittest_rgw_putobj test_rgw_putobj.cc)
+add_ceph_unittest(unittest_rgw_putobj)
+target_link_libraries(unittest_rgw_putobj ${rgw_libs} ${UNITTEST_LIBS})
+
+add_executable(ceph_test_rgw_throttle
+ test_rgw_throttle.cc
+ $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(ceph_test_rgw_throttle ${rgw_libs}
+ librados global ${UNITTEST_LIBS})
+install(TARGETS ceph_test_rgw_throttle DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(unittest_rgw_iam_policy test_rgw_iam_policy.cc)
+add_ceph_unittest(unittest_rgw_iam_policy)
+target_link_libraries(unittest_rgw_iam_policy
+ ${rgw_libs}
+ cls_rgw_client
+ cls_lock_client
+ cls_refcount_client
+ cls_log_client
+ cls_version_client
+ cls_user_client
+ librados
+ global
+ ${CURL_LIBRARIES}
+ ${EXPAT_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ${CRYPTO_LIBS}
+ )
+
+add_executable(unittest_rgw_string test_rgw_string.cc)
+add_ceph_unittest(unittest_rgw_string)
+target_include_directories(unittest_rgw_string
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+
+# unitttest_rgw_dmclock_queue
+add_executable(unittest_rgw_dmclock_scheduler test_rgw_dmclock_scheduler.cc $<TARGET_OBJECTS:unit-main>)
+add_ceph_unittest(unittest_rgw_dmclock_scheduler)
+target_include_directories(unittest_rgw_dmclock_scheduler
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+
+target_link_libraries(unittest_rgw_dmclock_scheduler rgw_schedulers global ${UNITTEST_LIBS})
+
+if(WITH_RADOSGW_AMQP_ENDPOINT)
+ add_executable(unittest_rgw_amqp test_rgw_amqp.cc)
+ add_ceph_unittest(unittest_rgw_amqp)
+ target_include_directories(unittest_rgw_amqp
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+ target_link_libraries(unittest_rgw_amqp ${rgw_libs})
+endif()
+
+# unittest_rgw_xml
+add_executable(unittest_rgw_xml test_rgw_xml.cc)
+add_ceph_unittest(unittest_rgw_xml)
+target_include_directories(unittest_rgw_xml
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_rgw_xml ${rgw_libs} ${EXPAT_LIBRARIES})
+
+# unittest_rgw_lc
+add_executable(unittest_rgw_lc test_rgw_lc.cc)
+add_ceph_unittest(unittest_rgw_lc)
+target_include_directories(unittest_rgw_lc
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_rgw_lc
+ rgw_common ${rgw_libs} ${EXPAT_LIBRARIES})
+
+# unittest_rgw_arn
+add_executable(unittest_rgw_arn test_rgw_arn.cc)
+add_ceph_unittest(unittest_rgw_arn)
+target_include_directories(unittest_rgw_arn
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_rgw_arn ${rgw_libs})
+
+# unittest_rgw_kms
+add_executable(unittest_rgw_kms test_rgw_kms.cc)
+add_ceph_unittest(unittest_rgw_kms)
+target_include_directories(unittest_rgw_kms
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_rgw_kms ${rgw_libs})
+
+# unittest_rgw_url
+add_executable(unittest_rgw_url test_rgw_url.cc)
+add_ceph_unittest(unittest_rgw_url)
+target_include_directories(unittest_rgw_url
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_rgw_url ${rgw_libs})
+
+add_executable(ceph_test_rgw_gc_log test_rgw_gc_log.cc $<TARGET_OBJECTS:unit-main>)
+target_include_directories(ceph_test_rgw_gc_log
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(ceph_test_rgw_gc_log ${rgw_libs} radostest-cxx)
+install(TARGETS ceph_test_rgw_gc_log DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_ceph_test(test-ceph-diff-sorted.sh
+ ${CMAKE_CURRENT_SOURCE_DIR}/test-ceph-diff-sorted.sh)
+
+# unittest_cls_fifo_legacy
+add_executable(unittest_cls_fifo_legacy test_cls_fifo_legacy.cc)
+target_include_directories(unittest_cls_fifo_legacy
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_cls_fifo_legacy radostest-cxx ${UNITTEST_LIBS}
+ ${rgw_libs})
+
+# unittest_log_backing
+add_executable(unittest_log_backing test_log_backing.cc)
+target_include_directories(unittest_log_backing
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_log_backing radostest-cxx ${UNITTEST_LIBS}
+ ${rgw_libs})
+
+add_executable(unittest_rgw_lua test_rgw_lua.cc)
+add_ceph_unittest(unittest_rgw_lua)
+target_include_directories(unittest_rgw_lua
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw"
+ SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/rados")
+target_link_libraries(unittest_rgw_lua ${rgw_libs})
+
+add_executable(radosgw-cr-test rgw_cr_test.cc)
+target_link_libraries(radosgw-cr-test ${rgw_libs} librados
+ cls_rgw_client cls_otp_client cls_lock_client cls_refcount_client
+ cls_log_client cls_timeindex_client
+ cls_version_client cls_user_client
+ global ${LIB_RESOLV}
+ OATH::OATH
+ ${CURL_LIBRARIES} ${EXPAT_LIBRARIES} ${BLKID_LIBRARIES}
+ GTest::GTest)
diff --git a/src/test/rgw/amqp_mock.cc b/src/test/rgw/amqp_mock.cc
new file mode 100644
index 000000000..8674e5026
--- /dev/null
+++ b/src/test/rgw/amqp_mock.cc
@@ -0,0 +1,391 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "amqp_mock.h"
+#include <amqp.h>
+#include <amqp_ssl_socket.h>
+#include <amqp_tcp_socket.h>
+#include <string>
+#include <stdarg.h>
+#include <mutex>
+#include <boost/lockfree/queue.hpp>
+#include <openssl/ssl.h>
+
+namespace amqp_mock {
+
+std::mutex set_valid_lock;
+int VALID_PORT(5672);
+std::string VALID_HOST("localhost");
+std::string VALID_VHOST("/");
+std::string VALID_USER("guest");
+std::string VALID_PASSWORD("guest");
+
+void set_valid_port(int port) {
+ std::lock_guard<std::mutex> lock(set_valid_lock);
+ VALID_PORT = port;
+}
+
+void set_valid_host(const std::string& host) {
+ std::lock_guard<std::mutex> lock(set_valid_lock);
+ VALID_HOST = host;
+}
+
+void set_valid_vhost(const std::string& vhost) {
+ std::lock_guard<std::mutex> lock(set_valid_lock);
+ VALID_VHOST = vhost;
+}
+
+void set_valid_user(const std::string& user, const std::string& password) {
+ std::lock_guard<std::mutex> lock(set_valid_lock);
+ VALID_USER = user;
+ VALID_PASSWORD = password;
+}
+
+std::atomic<unsigned> g_tag_skip = 0;
+std::atomic<int> g_multiple = 0;
+
+void set_multiple(unsigned tag_skip) {
+ g_multiple = 1;
+ g_tag_skip = tag_skip;
+}
+
+void reset_multiple() {
+ g_multiple = 0;
+ g_tag_skip = 0;
+}
+
+bool FAIL_NEXT_WRITE(false);
+bool FAIL_NEXT_READ(false);
+bool REPLY_ACK(true);
+}
+
+using namespace amqp_mock;
+
+struct amqp_connection_state_t_ {
+ amqp_socket_t* socket;
+ amqp_channel_open_ok_t* channel1;
+ amqp_channel_open_ok_t* channel2;
+ amqp_exchange_declare_ok_t* exchange;
+ amqp_queue_declare_ok_t* queue;
+ amqp_confirm_select_ok_t* confirm;
+ amqp_basic_consume_ok_t* consume;
+ bool login_called;
+ boost::lockfree::queue<amqp_basic_ack_t> ack_list;
+ boost::lockfree::queue<amqp_basic_nack_t> nack_list;
+ std::atomic<uint64_t> delivery_tag;
+ amqp_rpc_reply_t reply;
+ amqp_basic_ack_t ack;
+ amqp_basic_nack_t nack;
+ bool use_ssl;
+ // ctor
+ amqp_connection_state_t_() :
+ socket(nullptr),
+ channel1(nullptr),
+ channel2(nullptr),
+ exchange(nullptr),
+ queue(nullptr),
+ confirm(nullptr),
+ consume(nullptr),
+ login_called(false),
+ ack_list(1024),
+ nack_list(1024),
+ delivery_tag(1),
+ use_ssl(false) {
+ reply.reply_type = AMQP_RESPONSE_NONE;
+ }
+};
+
+struct amqp_socket_t_ {
+ void *klass;
+ void *ssl_ctx;
+ bool open_called;
+ // ctor
+ amqp_socket_t_() : klass(nullptr), ssl_ctx(nullptr), open_called(false) {
+ }
+};
+
+extern "C" {
+
+amqp_connection_state_t AMQP_CALL amqp_new_connection(void) {
+ auto s = new amqp_connection_state_t_;
+ return s;
+}
+
+int amqp_destroy_connection(amqp_connection_state_t state) {
+ delete state->socket;
+ delete state->channel1;
+ delete state->channel2;
+ delete state->exchange;
+ delete state->queue;
+ delete state->confirm;
+ delete state->consume;
+ delete state;
+ return 0;
+}
+
+amqp_socket_t* amqp_tcp_socket_new(amqp_connection_state_t state) {
+ state->socket = new amqp_socket_t;
+ return state->socket;
+}
+
+amqp_socket_t* amqp_ssl_socket_new(amqp_connection_state_t state) {
+ state->socket = new amqp_socket_t;
+ state->use_ssl = true;
+ return state->socket;
+}
+
+int amqp_ssl_socket_set_cacert(amqp_socket_t *self, const char *cacert) {
+ // do nothing
+ return AMQP_STATUS_OK;
+}
+
+void amqp_ssl_socket_set_verify_peer(amqp_socket_t *self, amqp_boolean_t verify) {
+ // do nothing
+}
+
+void amqp_ssl_socket_set_verify_hostname(amqp_socket_t *self, amqp_boolean_t verify) {
+ // do nothing
+}
+
+#if AMQP_VERSION >= AMQP_VERSION_CODE(0, 10, 0, 1)
+void* amqp_ssl_socket_get_context(amqp_socket_t *self) {
+ return nullptr;
+}
+#endif
+
+int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) {
+ return 1;
+}
+
+int amqp_socket_open(amqp_socket_t *self, const char *host, int port) {
+ if (!self) {
+ return -1;
+ }
+ {
+ std::lock_guard<std::mutex> lock(set_valid_lock);
+ if (std::string(host) != VALID_HOST) {
+ return -2;
+ }
+ if (port != VALID_PORT) {
+ return -3;
+ }
+ }
+ self->open_called = true;
+ return 0;
+}
+
+amqp_rpc_reply_t amqp_login(
+ amqp_connection_state_t state,
+ char const *vhost,
+ int channel_max,
+ int frame_max,
+ int heartbeat,
+ amqp_sasl_method_enum sasl_method, ...) {
+ state->reply.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
+ state->reply.library_error = 0;
+ state->reply.reply.decoded = nullptr;
+ state->reply.reply.id = 0;
+ if (std::string(vhost) != VALID_VHOST) {
+ return state->reply;
+ }
+ if (sasl_method != AMQP_SASL_METHOD_PLAIN) {
+ return state->reply;
+ }
+ va_list args;
+ va_start(args, sasl_method);
+ char* user = va_arg(args, char*);
+ char* password = va_arg(args, char*);
+ va_end(args);
+ if (std::string(user) != VALID_USER) {
+ return state->reply;
+ }
+ if (std::string(password) != VALID_PASSWORD) {
+ return state->reply;
+ }
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ state->login_called = true;
+ return state->reply;
+}
+
+amqp_channel_open_ok_t* amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel) {
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ if (state->channel1 == nullptr) {
+ state->channel1 = new amqp_channel_open_ok_t;
+ return state->channel1;
+ }
+
+ state->channel2 = new amqp_channel_open_ok_t;
+ return state->channel2;
+}
+
+amqp_exchange_declare_ok_t* amqp_exchange_declare(
+ amqp_connection_state_t state,
+ amqp_channel_t channel,
+ amqp_bytes_t exchange,
+ amqp_bytes_t type,
+ amqp_boolean_t passive,
+ amqp_boolean_t durable,
+ amqp_boolean_t auto_delete,
+ amqp_boolean_t internal,
+ amqp_table_t arguments) {
+ state->exchange = new amqp_exchange_declare_ok_t;
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ return state->exchange;
+}
+
+amqp_rpc_reply_t amqp_get_rpc_reply(amqp_connection_state_t state) {
+ return state->reply;
+}
+
+int amqp_basic_publish(
+ amqp_connection_state_t state,
+ amqp_channel_t channel,
+ amqp_bytes_t exchange,
+ amqp_bytes_t routing_key,
+ amqp_boolean_t mandatory,
+ amqp_boolean_t immediate,
+ struct amqp_basic_properties_t_ const *properties,
+ amqp_bytes_t body) {
+ // make sure that all calls happened before publish
+ if (state->socket && state->socket->open_called &&
+ state->login_called && state->channel1 && state->channel2 && state->exchange &&
+ !FAIL_NEXT_WRITE) {
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ if (properties) {
+ if (REPLY_ACK) {
+ state->ack_list.push(amqp_basic_ack_t{state->delivery_tag++, 0});
+ } else {
+ state->nack_list.push(amqp_basic_nack_t{state->delivery_tag++, 0});
+ }
+ }
+ return AMQP_STATUS_OK;
+ }
+ return AMQP_STATUS_CONNECTION_CLOSED;
+}
+
+const amqp_table_t amqp_empty_table = {0, NULL};
+const amqp_bytes_t amqp_empty_bytes = {0, NULL};
+
+const char* amqp_error_string2(int code) {
+ static const char* str = "mock error";
+ return str;
+}
+
+char const* amqp_method_name(amqp_method_number_t methodNumber) {
+ static const char* str = "mock method";
+ return str;
+}
+
+amqp_queue_declare_ok_t* amqp_queue_declare(
+ amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue,
+ amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive,
+ amqp_boolean_t auto_delete, amqp_table_t arguments) {
+ state->queue = new amqp_queue_declare_ok_t;
+ static const char* str = "tmp-queue";
+ state->queue->queue = amqp_cstring_bytes(str);
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ return state->queue;
+}
+
+amqp_confirm_select_ok_t* amqp_confirm_select(amqp_connection_state_t state, amqp_channel_t channel) {
+ state->confirm = new amqp_confirm_select_ok_t;
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ return state->confirm;
+}
+
+#if AMQP_VERSION >= AMQP_VERSION_CODE(0, 11, 0, 1)
+int amqp_simple_wait_frame_noblock(amqp_connection_state_t state, amqp_frame_t *decoded_frame, const struct timeval* tv) {
+#else
+int amqp_simple_wait_frame_noblock(amqp_connection_state_t state, amqp_frame_t *decoded_frame, struct timeval* tv) {
+#endif
+ if (state->socket && state->socket->open_called &&
+ state->login_called && state->channel1 && state->channel2 && state->exchange &&
+ state->queue && state->consume && state->confirm && !FAIL_NEXT_READ) {
+ // "wait" for queue
+ usleep(tv->tv_sec*1000000+tv->tv_usec);
+ // read from queue
+ if (g_multiple) {
+ // pop multiples and reply once at the end
+ for (auto i = 0U; i < g_tag_skip; ++i) {
+ if (REPLY_ACK && !state->ack_list.pop(state->ack)) {
+ // queue is empty
+ return AMQP_STATUS_TIMEOUT;
+ } else if (!REPLY_ACK && !state->nack_list.pop(state->nack)) {
+ // queue is empty
+ return AMQP_STATUS_TIMEOUT;
+ }
+ }
+ if (REPLY_ACK) {
+ state->ack.multiple = g_multiple;
+ decoded_frame->payload.method.id = AMQP_BASIC_ACK_METHOD;
+ decoded_frame->payload.method.decoded = &state->ack;
+ } else {
+ state->nack.multiple = g_multiple;
+ decoded_frame->payload.method.id = AMQP_BASIC_NACK_METHOD;
+ decoded_frame->payload.method.decoded = &state->nack;
+ }
+ decoded_frame->frame_type = AMQP_FRAME_METHOD;
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ reset_multiple();
+ return AMQP_STATUS_OK;
+ }
+ // pop replies one by one
+ if (REPLY_ACK && state->ack_list.pop(state->ack)) {
+ state->ack.multiple = g_multiple;
+ decoded_frame->frame_type = AMQP_FRAME_METHOD;
+ decoded_frame->payload.method.id = AMQP_BASIC_ACK_METHOD;
+ decoded_frame->payload.method.decoded = &state->ack;
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ return AMQP_STATUS_OK;
+ } else if (!REPLY_ACK && state->nack_list.pop(state->nack)) {
+ state->nack.multiple = g_multiple;
+ decoded_frame->frame_type = AMQP_FRAME_METHOD;
+ decoded_frame->payload.method.id = AMQP_BASIC_NACK_METHOD;
+ decoded_frame->payload.method.decoded = &state->nack;
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ return AMQP_STATUS_OK;
+ } else {
+ // queue is empty
+ return AMQP_STATUS_TIMEOUT;
+ }
+ }
+ return AMQP_STATUS_CONNECTION_CLOSED;
+}
+
+amqp_basic_consume_ok_t* amqp_basic_consume(
+ amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue,
+ amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack,
+ amqp_boolean_t exclusive, amqp_table_t arguments) {
+ state->consume = new amqp_basic_consume_ok_t;
+ state->reply.reply_type = AMQP_RESPONSE_NORMAL;
+ return state->consume;
+}
+
+} // extern "C"
+
+// amqp_parse_url() is linked via the actual rabbitmq-c library code. see: amqp_url.c
+
+// following functions are the actual implementation copied from rabbitmq-c library
+
+#include <string.h>
+
+amqp_bytes_t amqp_cstring_bytes(const char* cstr) {
+ amqp_bytes_t result;
+ result.len = strlen(cstr);
+ result.bytes = (void *)cstr;
+ return result;
+}
+
+void amqp_bytes_free(amqp_bytes_t bytes) { free(bytes.bytes); }
+
+amqp_bytes_t amqp_bytes_malloc_dup(amqp_bytes_t src) {
+ amqp_bytes_t result;
+ result.len = src.len;
+ result.bytes = malloc(src.len);
+ if (result.bytes != NULL) {
+ memcpy(result.bytes, src.bytes, src.len);
+ }
+ return result;
+}
+
+
diff --git a/src/test/rgw/amqp_mock.h b/src/test/rgw/amqp_mock.h
new file mode 100644
index 000000000..94fdfdddc
--- /dev/null
+++ b/src/test/rgw/amqp_mock.h
@@ -0,0 +1,19 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+
+#include <string>
+
+namespace amqp_mock {
+void set_valid_port(int port);
+void set_valid_host(const std::string& host);
+void set_valid_vhost(const std::string& vhost);
+void set_valid_user(const std::string& user, const std::string& password);
+void set_multiple(unsigned tag);
+void reset_multiple();
+
+extern bool FAIL_NEXT_WRITE; // default "false"
+extern bool FAIL_NEXT_READ; // default "false"
+extern bool REPLY_ACK; // default "true"
+}
+
diff --git a/src/test/rgw/amqp_url.c b/src/test/rgw/amqp_url.c
new file mode 100644
index 000000000..520e95c69
--- /dev/null
+++ b/src/test/rgw/amqp_url.c
@@ -0,0 +1,221 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * 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.
+ * ***** END LICENSE BLOCK *****
+ */
+
+// this version of the file is slightly modified from the original one
+// as it is only used to mock amqp libraries
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "amqp.h"
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void amqp_default_connection_info(struct amqp_connection_info *ci) {
+ /* Apply defaults */
+ ci->user = "guest";
+ ci->password = "guest";
+ ci->host = "localhost";
+ ci->port = 5672;
+ ci->vhost = "/";
+ ci->ssl = 0;
+}
+
+/* Scan for the next delimiter, handling percent-encodings on the way. */
+static char find_delim(char **pp, int colon_and_at_sign_are_delims) {
+ char *from = *pp;
+ char *to = from;
+
+ for (;;) {
+ char ch = *from++;
+
+ switch (ch) {
+ case ':':
+ case '@':
+ if (!colon_and_at_sign_are_delims) {
+ *to++ = ch;
+ break;
+ }
+
+ /* fall through */
+ case 0:
+ case '/':
+ case '?':
+ case '#':
+ case '[':
+ case ']':
+ *to = 0;
+ *pp = from;
+ return ch;
+
+ case '%': {
+ unsigned int val;
+ int chars;
+ int res = sscanf(from, "%2x%n", &val, &chars);
+
+ if (res == EOF || res < 1 || chars != 2 || val > CHAR_MAX)
+ /* Return a surprising delimiter to
+ force an error. */
+ {
+ return '%';
+ }
+
+ *to++ = (char)val;
+ from += 2;
+ break;
+ }
+
+ default:
+ *to++ = ch;
+ break;
+ }
+ }
+}
+
+/* Parse an AMQP URL into its component parts. */
+int amqp_parse_url(char *url, struct amqp_connection_info *parsed) {
+ int res = AMQP_STATUS_BAD_URL;
+ char delim;
+ char *start;
+ char *host;
+ char *port = NULL;
+
+ amqp_default_connection_info(parsed);
+
+ parsed->port = 5672;
+ parsed->ssl = 0;
+ /* check the prefix */
+ if (!strncmp(url, "amqp://", 7)) {
+ /* do nothing */
+ } else if (!strncmp(url, "amqps://", 8)) {
+ parsed->port = 5671;
+ parsed->ssl = 1;
+ } else {
+ goto out;
+ }
+
+ host = start = url += (parsed->ssl ? 8 : 7);
+ delim = find_delim(&url, 1);
+
+ if (delim == ':') {
+ /* The colon could be introducing the port or the
+ password part of the userinfo. We don't know yet,
+ so stash the preceding component. */
+ port = start = url;
+ delim = find_delim(&url, 1);
+ }
+
+ if (delim == '@') {
+ /* What might have been the host and port were in fact
+ the username and password */
+ parsed->user = host;
+ if (port) {
+ parsed->password = port;
+ }
+
+ port = NULL;
+ host = start = url;
+ delim = find_delim(&url, 1);
+ }
+
+ if (delim == '[') {
+ /* IPv6 address. The bracket should be the first
+ character in the host. */
+ if (host != start || *host != 0) {
+ goto out;
+ }
+
+ start = url;
+ delim = find_delim(&url, 0);
+
+ if (delim != ']') {
+ goto out;
+ }
+
+ parsed->host = start;
+ start = url;
+ delim = find_delim(&url, 1);
+
+ /* Closing bracket should be the last character in the
+ host. */
+ if (*start != 0) {
+ goto out;
+ }
+ } else {
+ /* If we haven't seen the host yet, this is it. */
+ if (*host != 0) {
+ parsed->host = host;
+ }
+ }
+
+ if (delim == ':') {
+ port = start = url;
+ delim = find_delim(&url, 1);
+ }
+
+ if (port) {
+ char *end;
+ long portnum = strtol(port, &end, 10);
+
+ if (port == end || *end != 0 || portnum < 0 || portnum > 65535) {
+ goto out;
+ }
+
+ parsed->port = portnum;
+ }
+
+ if (delim == '/') {
+ start = url;
+ delim = find_delim(&url, 1);
+
+ if (delim != 0) {
+ goto out;
+ }
+
+ parsed->vhost = start;
+ res = AMQP_STATUS_OK;
+ } else if (delim == 0) {
+ res = AMQP_STATUS_OK;
+ }
+
+/* Any other delimiter is bad, and we will return AMQP_STATUS_BAD_AMQP_URL. */
+
+out:
+ return res;
+}
diff --git a/src/test/rgw/bench_rgw_ratelimit.cc b/src/test/rgw/bench_rgw_ratelimit.cc
new file mode 100644
index 000000000..2bf7753ad
--- /dev/null
+++ b/src/test/rgw/bench_rgw_ratelimit.cc
@@ -0,0 +1,247 @@
+#include "rgw_ratelimit.h"
+#include "rgw_common.h"
+#include "random"
+#include <cstdlib>
+#include <string>
+#include <boost/asio.hpp>
+#include <spawn/spawn.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <chrono>
+#include <mutex>
+#include <unordered_map>
+#include <atomic>
+#include <boost/program_options.hpp>
+
+
+using Executor = boost::asio::io_context::executor_type;
+std::uniform_int_distribution<unsigned int> dist(0, 1);
+std::random_device rd;
+std::default_random_engine rng{rd()};
+std::uniform_int_distribution<unsigned long long> disttenant(2, 100000000);
+struct client_info {
+ uint64_t accepted = 0;
+ uint64_t rejected = 0;
+ uint64_t ops = 0;
+ uint64_t bytes = 0;
+ uint64_t num_retries = 0;
+ std::string tenant;
+};
+
+struct parameters {
+ int64_t req_size = 1;
+ int64_t backend_bandwidth = 1;
+ size_t wait_between_retries_ms = 1;
+ int num_clients = 1;
+};
+std::shared_ptr<std::vector<client_info>> ds = std::make_shared<std::vector<client_info>>(std::vector<client_info>());
+
+std::string method[2] = {"PUT", "GET"};
+void simulate_transfer(client_info& it, const RGWRateLimitInfo* info, std::shared_ptr<RateLimiter> ratelimit, const parameters& params, spawn::yield_context& yield, boost::asio::io_context& ioctx)
+{
+ auto dout = DoutPrefix(g_ceph_context, ceph_subsys_rgw, "rate limiter: ");
+ boost::asio::steady_timer timer(ioctx);
+ int rw = 0; // will always use PUT method as there is no difference
+ std::string methodop(method[rw]);
+ auto req_size = params.req_size;
+ auto backend_bandwidth = params.backend_bandwidth;
+// the 4 * 1024 * 1024 is the RGW default we are sending in a typical environment
+ while (req_size) {
+ if (req_size <= backend_bandwidth) {
+ while (req_size > 0) {
+ if(req_size > 4*1024*1024) {
+ ratelimit->decrease_bytes(methodop.c_str(),it.tenant, 4*1024*1024, info);
+ it.bytes += 4*1024*1024;
+ req_size = req_size - 4*1024*1024;
+ }
+ else {
+ ratelimit->decrease_bytes(methodop.c_str(),it.tenant, req_size, info);
+ req_size = 0;
+ }
+ }
+ } else {
+ int64_t total_bytes = 0;
+ while (req_size > 0) {
+ if (req_size >= 4*1024*1024) {
+ if (total_bytes >= backend_bandwidth)
+ {
+ timer.expires_after(std::chrono::seconds(1));
+ timer.async_wait(yield);
+ total_bytes = 0;
+ }
+ ratelimit->decrease_bytes(methodop.c_str(),it.tenant, 4*1024*1024, info);
+ it.bytes += 4*1024*1024;
+ req_size = req_size - 4*1024*1024;
+ total_bytes += 4*1024*1024;
+ }
+ else {
+ ratelimit->decrease_bytes(methodop.c_str(),it.tenant, req_size, info);
+ it.bytes += req_size;
+ total_bytes += req_size;
+ req_size = 0;
+ }
+ }
+ }
+ }
+}
+bool simulate_request(client_info& it, const RGWRateLimitInfo& info, std::shared_ptr<RateLimiter> ratelimit)
+{
+ boost::asio::io_context context;
+ auto time = ceph::coarse_real_clock::now();
+ int rw = 0; // will always use PUT method as there is no different
+ std::string methodop = method[rw];
+ auto dout = DoutPrefix(g_ceph_context, ceph_subsys_rgw, "rate limiter: ");
+ bool to_fail = ratelimit->should_rate_limit(methodop.c_str(), it.tenant, time, &info);
+ if(to_fail)
+ {
+ it.rejected++;
+ it.ops++;
+ return true;
+ }
+ it.accepted++;
+ return false;
+}
+void simulate_client(client_info& it, const RGWRateLimitInfo& info, std::shared_ptr<RateLimiter> ratelimit, const parameters& params, spawn::yield_context& ctx, bool& to_run, boost::asio::io_context& ioctx)
+{
+ for (;;)
+ {
+ bool to_retry = simulate_request(it, info, ratelimit);
+ while (to_retry && to_run)
+ {
+ if (params.wait_between_retries_ms)
+ {
+ boost::asio::steady_timer timer(ioctx);
+ timer.expires_after(std::chrono::milliseconds(params.wait_between_retries_ms));
+ timer.async_wait(ctx);
+ }
+ to_retry = simulate_request(it, info, ratelimit);
+ }
+ if (!to_run)
+ {
+ return;
+ }
+ simulate_transfer(it, &info, ratelimit, params, ctx, ioctx);
+ }
+}
+void simulate_clients(boost::asio::io_context& context, std::string tenant, const RGWRateLimitInfo& info, std::shared_ptr<RateLimiter> ratelimit, const parameters& params, bool& to_run)
+{
+ for (int i = 0; i < params.num_clients; i++)
+ {
+ auto& it = ds->emplace_back(client_info());
+ it.tenant = tenant;
+ int x = ds->size() - 1;
+ spawn::spawn(context,
+ [&to_run ,x, ratelimit, info, params, &context](spawn::yield_context ctx)
+ {
+ auto& it = ds.get()->operator[](x);
+ simulate_client(it, info, ratelimit, params, ctx, to_run, context);
+ });
+ }
+}
+int main(int argc, char **argv)
+{
+ int num_ratelimit_classes = 1;
+ int64_t ops_limit = 1;
+ int64_t bw_limit = 1;
+ int thread_count = 512;
+ int runtime = 60;
+ parameters params;
+ try
+ {
+ using namespace boost::program_options;
+ options_description desc{"Options"};
+ desc.add_options()
+ ("help,h", "Help screen")
+ ("num_ratelimit_classes", value<int>()->default_value(1), "how many ratelimit tenants")
+ ("request_size", value<int64_t>()->default_value(1), "what is the request size we are testing if 0, it will be randomized")
+ ("backend_bandwidth", value<int64_t>()->default_value(1), "what is the backend bandwidth, so there will be wait between decrease_bytes")
+ ("wait_between_retries_ms", value<size_t>()->default_value(1), "time in seconds to wait between retries")
+ ("ops_limit", value<int64_t>()->default_value(1), "ops limit for the tenants")
+ ("bw_limit", value<int64_t>()->default_value(1), "bytes per second limit")
+ ("threads", value<int>()->default_value(512), "server's threads count")
+ ("runtime", value<int>()->default_value(60), "For how many seconds the test will run")
+ ("num_clients", value<int>()->default_value(1), "number of clients per tenant to run");
+ variables_map vm;
+ store(parse_command_line(argc, argv, desc), vm);
+ if (vm.count("help")) {
+ std::cout << desc << std::endl;
+ return EXIT_SUCCESS;
+ }
+ num_ratelimit_classes = vm["num_ratelimit_classes"].as<int>();
+ params.req_size = vm["request_size"].as<int64_t>();
+ params.backend_bandwidth = vm["backend_bandwidth"].as<int64_t>();
+ params.wait_between_retries_ms = vm["wait_between_retries_ms"].as<size_t>();
+ params.num_clients = vm["num_clients"].as<int>();
+ ops_limit = vm["ops_limit"].as<int64_t>();
+ bw_limit = vm["bw_limit"].as<int64_t>();
+ thread_count = vm["threads"].as<int>();
+ runtime = vm["runtime"].as<int>();
+ }
+ catch (const boost::program_options::error &ex)
+ {
+ std::cerr << ex.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = bw_limit;
+ info.max_write_bytes = bw_limit;
+ info.max_read_ops = ops_limit;
+ info.max_write_ops = ops_limit;
+ std::unique_ptr<CephContext> cct = std::make_unique<CephContext>(CEPH_ENTITY_TYPE_ANY);
+ if (!g_ceph_context)
+ {
+ g_ceph_context = cct.get();
+ }
+ std::shared_ptr<ActiveRateLimiter> ratelimit(new ActiveRateLimiter(g_ceph_context));
+ ratelimit->start();
+ std::vector<std::thread> threads;
+ using Executor = boost::asio::io_context::executor_type;
+ std::optional<boost::asio::executor_work_guard<Executor>> work;
+ threads.reserve(thread_count);
+ boost::asio::io_context context;
+ boost::asio::io_context stopme;
+ work.emplace(boost::asio::make_work_guard(context));
+ // server execution
+ for (int i = 0; i < thread_count; i++) {
+ threads.emplace_back([&]() noexcept {
+ context.run();
+ });
+ }
+ //client execution
+ bool to_run = true;
+ ds->reserve(num_ratelimit_classes*params.num_clients);
+ for (int i = 0; i < num_ratelimit_classes; i++)
+ {
+ unsigned long long tenantid = disttenant(rng);
+ std::string tenantuser = "uuser" + std::to_string(tenantid);
+ simulate_clients(context, tenantuser, info, ratelimit->get_active(), params, to_run);
+ }
+ boost::asio::steady_timer timer_runtime(stopme);
+ timer_runtime.expires_after(std::chrono::seconds(runtime));
+ timer_runtime.wait();
+ work.reset();
+ context.stop();
+ to_run = false;
+
+ for (auto& i : threads)
+ {
+ i.join();
+ }
+ std::unordered_map<std::string,client_info> metrics_by_tenant;
+ for(auto& i : *ds.get())
+ {
+ auto it = metrics_by_tenant.emplace(i.tenant, client_info()).first;
+ std::cout << i.accepted << std::endl;
+ it->second.accepted += i.accepted;
+ it->second.rejected += i.rejected;
+ }
+ // TODO sum the results by tenant
+ for(auto& i : metrics_by_tenant)
+ {
+ std::cout << "Tenant is: " << i.first << std::endl;
+ std::cout << "Simulator finished accepted sum : " << i.second.accepted << std::endl;
+ std::cout << "Simulator finished rejected sum : " << i.second.rejected << std::endl;
+ }
+
+ return 0;
+}
diff --git a/src/test/rgw/bench_rgw_ratelimit_gc.cc b/src/test/rgw/bench_rgw_ratelimit_gc.cc
new file mode 100644
index 000000000..ae422e1da
--- /dev/null
+++ b/src/test/rgw/bench_rgw_ratelimit_gc.cc
@@ -0,0 +1,52 @@
+#include "rgw_ratelimit.h"
+#include "rgw_common.h"
+#include "random"
+#include <cstdlib>
+#include <string>
+#include <chrono>
+#include <boost/program_options.hpp>
+int main(int argc, char **argv)
+{
+ int num_qos_classes = 1;
+ try
+ {
+ using namespace boost::program_options;
+ options_description desc{"Options"};
+ desc.add_options()
+ ("help,h", "Help screen")
+ ("num_qos_classes", value<int>()->default_value(1), "how many qos tenants");
+ variables_map vm;
+ store(parse_command_line(argc, argv, desc), vm);
+ if (vm.count("help")) {
+ std::cout << desc << std::endl;
+ return EXIT_SUCCESS;
+ }
+ num_qos_classes = vm["num_qos_classes"].as<int>();
+ }
+ catch (const boost::program_options::error &ex)
+ {
+ std::cerr << ex.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = 0;
+ info.max_write_bytes = 0;
+ info.max_read_ops = 0;
+ info.max_write_ops = 0;
+ std::unique_ptr<CephContext> cct = std::make_unique<CephContext>(CEPH_ENTITY_TYPE_ANY);
+ if (!g_ceph_context)
+ {
+ g_ceph_context = cct.get();
+ }
+ std::shared_ptr<ActiveRateLimiter> ratelimit(new ActiveRateLimiter(g_ceph_context));
+ ratelimit->start();
+ auto dout = DoutPrefix(g_ceph_context, ceph_subsys_rgw, "rate limiter: ");
+ for(int i = 0; i < num_qos_classes; i++)
+ {
+ std::string tenant = "uuser" + std::to_string(i);
+ auto time = ceph::coarse_real_clock::now();
+ ratelimit->get_active()->should_rate_limit("PUT", tenant, time, &info);
+ }
+
+}
diff --git a/src/test/rgw/bucket_notification/README.rst b/src/test/rgw/bucket_notification/README.rst
new file mode 100644
index 000000000..9686bef71
--- /dev/null
+++ b/src/test/rgw/bucket_notification/README.rst
@@ -0,0 +1,96 @@
+==========================
+ Bucket Notification Tests
+==========================
+
+You will need to use the sample configuration file named ``bntests.conf.SAMPLE``
+that has been provided at ``/path/to/ceph/src/test/rgw/bucket_notification/``. You can also copy this file to the directory where you are
+running the tests and modify it if needed. This file can be used to run the bucket notification tests on a Ceph cluster started
+with vstart.
+For the tests covering Kafka and RabbitMQ security, the RGW will need to accept use/password without TLS connection between the client and the RGW.
+So, the cluster will have to be started with the following ``rgw_allow_notification_secrets_in_cleartext`` parameter set to ``true``.
+For example::
+
+ MON=1 OSD=1 MDS=0 MGR=1 RGW=1 ../src/vstart.sh -n -d -o "rgw_allow_notification_secrets_in_cleartext=true"
+
+===========
+Kafka Tests
+===========
+
+You also need to install Kafka which can be downloaded from: https://kafka.apache.org/downloads
+
+To test Kafka security, you should first run the ``kafka-security.sh`` script inside the Kafka directory.
+
+Then edit the Kafka server properties file (``/path/to/kafka/config/server.properties``)
+to have the following lines::
+
+ listeners=PLAINTEXT://localhost:9092,SSL://localhost:9093,SASL_SSL://localhost:9094
+ ssl.keystore.location=/home/ylifshit/kafka-3.3.1-src/server.keystore.jks
+ ssl.keystore.password=mypassword
+ ssl.key.password=mypassword
+ ssl.truststore.location=/home/ylifshit/kafka-3.3.1-src/server.truststore.jks
+ ssl.truststore.password=mypassword
+ sasl.enabled.mechanisms=PLAIN
+ listener.name.sasl_ssl.plain.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
+ username="alice" \
+ password="alice-secret" \
+ user_alice="alice-secret";
+
+After following the above steps, start the Zookeeper and Kafka services.
+For starting Zookeeper service run::
+
+ bin/zookeeper-server-start.sh config/zookeeper.properties
+
+and then start the Kafka service::
+
+ bin/kafka-server-start.sh config/server.properties
+
+If you want to run Zookeeper and Kafka services in the background add ``-daemon`` at the end of the command like::
+
+ bin/zookeeper-server-start.sh -daemon config/zookeeper.properties
+
+and::
+
+ bin/kafka-server-start.sh -daemon config/server.properties
+
+After running vstart, Zookeeper, and Kafka services you're ready to run the Kafka tests::
+
+ BNTESTS_CONF=bntests.conf python -m nose -s /path/to/ceph/src/test/rgw/bucket_notification/test_bn.py -v -a 'kafka_test'
+
+To run the Kafka security test, you also need to provide the test with the location of the Kafka directory::
+
+ KAFKA_DIR=/path/to/kafkaBNTESTS_CONF=bntests.conf python -m nose -s /path/to/ceph/src/test/rgw/bucket_notification/test_bn.py -v -a 'kafka_ssl_test'
+
+==============
+RabbitMQ Tests
+==============
+
+You need to install RabbitMQ in the following way::
+
+ sudo dnf install rabbitmq-server
+
+Then you need to run the following command::
+
+ sudo chkconfig rabbitmq-server on
+
+Finally, to start the RabbitMQ server you need to run the following command::
+
+ sudo /sbin/service rabbitmq-server start
+
+To confirm that the RabbitMQ server is running you can run the following command to check the status of the server::
+
+ sudo /sbin/service rabbitmq-server status
+
+After running vstart and RabbitMQ server you're ready to run the AMQP tests::
+
+ BNTESTS_CONF=bntests.conf python -m nose -s /path/to/ceph/src/test/rgw/bucket_notification/test_bn.py -v -a 'amqp_test'
+
+After running the tests you need to stop the vstart cluster (``/path/to/ceph/src/stop.sh``) and the RabbitMQ server by running the following command::
+
+ sudo /sbin/service rabbitmq-server stop
+
+To run the RabbitMQ SSL security tests use the following::
+
+ BNTESTS_CONF=bntests.conf python -m nose -s /path/to/ceph/src/test/rgw/bucket_notification/test_bn.py -v -a 'amqp_ssl_test'
+
+During these tests, the test script will restart the RabbitMQ server with the correct security configuration (``sudo`` privileges will be needed).
+
diff --git a/src/test/rgw/bucket_notification/__init__.py b/src/test/rgw/bucket_notification/__init__.py
new file mode 100644
index 000000000..6785fce92
--- /dev/null
+++ b/src/test/rgw/bucket_notification/__init__.py
@@ -0,0 +1,48 @@
+import configparser
+import os
+
+def setup():
+ cfg = configparser.RawConfigParser()
+ try:
+ path = os.environ['BNTESTS_CONF']
+ except KeyError:
+ raise RuntimeError(
+ 'To run tests, point environment '
+ + 'variable BNTESTS_CONF to a config file.',
+ )
+ cfg.read(path)
+
+ if not cfg.defaults():
+ raise RuntimeError('Your config file is missing the DEFAULT section!')
+ if not cfg.has_section("s3 main"):
+ raise RuntimeError('Your config file is missing the "s3 main" section!')
+
+ defaults = cfg.defaults()
+
+ global default_host
+ default_host = defaults.get("host")
+
+ global default_port
+ default_port = int(defaults.get("port"))
+
+ global main_access_key
+ main_access_key = cfg.get('s3 main',"access_key")
+
+ global main_secret_key
+ main_secret_key = cfg.get('s3 main',"secret_key")
+
+def get_config_host():
+ global default_host
+ return default_host
+
+def get_config_port():
+ global default_port
+ return default_port
+
+def get_access_key():
+ global main_access_key
+ return main_access_key
+
+def get_secret_key():
+ global main_secret_key
+ return main_secret_key
diff --git a/src/test/rgw/bucket_notification/api.py b/src/test/rgw/bucket_notification/api.py
new file mode 100644
index 000000000..fe38576fb
--- /dev/null
+++ b/src/test/rgw/bucket_notification/api.py
@@ -0,0 +1,234 @@
+import logging
+import ssl
+import urllib
+import hmac
+import hashlib
+import base64
+import xmltodict
+from http import client as http_client
+from urllib import parse as urlparse
+from time import gmtime, strftime
+import boto3
+from botocore.client import Config
+import os
+import subprocess
+
+log = logging.getLogger('bucket_notification.tests')
+
+NO_HTTP_BODY = ''
+
+def put_object_tagging(conn, bucket_name, key, tags):
+ client = boto3.client('s3',
+ endpoint_url='http://'+conn.host+':'+str(conn.port),
+ aws_access_key_id=conn.aws_access_key_id,
+ aws_secret_access_key=conn.aws_secret_access_key)
+ return client.put_object(Body='aaaaaaaaaaa', Bucket=bucket_name, Key=key, Tagging=tags)
+
+def make_request(conn, method, resource, parameters=None, sign_parameters=False, extra_parameters=None):
+ """generic request sending to pubsub radogw
+ should cover: topics, notificatios and subscriptions
+ """
+ url_params = ''
+ if parameters is not None:
+ url_params = urlparse.urlencode(parameters)
+ # remove 'None' from keys with no values
+ url_params = url_params.replace('=None', '')
+ url_params = '?' + url_params
+ if extra_parameters is not None:
+ url_params = url_params + '&' + extra_parameters
+ string_date = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
+ string_to_sign = method + '\n\n\n' + string_date + '\n' + resource
+ if sign_parameters:
+ string_to_sign += url_params
+ signature = base64.b64encode(hmac.new(conn.aws_secret_access_key.encode('utf-8'),
+ string_to_sign.encode('utf-8'),
+ hashlib.sha1).digest()).decode('ascii')
+ headers = {'Authorization': 'AWS '+conn.aws_access_key_id+':'+signature,
+ 'Date': string_date,
+ 'Host': conn.host+':'+str(conn.port)}
+ http_conn = http_client.HTTPConnection(conn.host, conn.port)
+ if log.getEffectiveLevel() <= 10:
+ http_conn.set_debuglevel(5)
+ http_conn.request(method, resource+url_params, NO_HTTP_BODY, headers)
+ response = http_conn.getresponse()
+ data = response.read()
+ status = response.status
+ http_conn.close()
+ return data.decode('utf-8'), status
+
+
+def delete_all_objects(conn, bucket_name):
+ client = boto3.client('s3',
+ endpoint_url='http://'+conn.host+':'+str(conn.port),
+ aws_access_key_id=conn.aws_access_key_id,
+ aws_secret_access_key=conn.aws_secret_access_key)
+
+ objects = []
+ for key in client.list_objects(Bucket=bucket_name)['Contents']:
+ objects.append({'Key': key['Key']})
+ # delete objects from the bucket
+ response = client.delete_objects(Bucket=bucket_name,
+ Delete={'Objects': objects})
+
+
+class PSTopicS3:
+ """class to set/list/get/delete a topic
+ POST ?Action=CreateTopic&Name=<topic name>[&OpaqueData=<data>[&push-endpoint=<endpoint>&[<arg1>=<value1>...]]]
+ POST ?Action=ListTopics
+ POST ?Action=GetTopic&TopicArn=<topic-arn>
+ POST ?Action=DeleteTopic&TopicArn=<topic-arn>
+ """
+ def __init__(self, conn, topic_name, region, endpoint_args=None, opaque_data=None):
+ self.conn = conn
+ self.topic_name = topic_name.strip()
+ assert self.topic_name
+ self.topic_arn = ''
+ self.attributes = {}
+ if endpoint_args is not None:
+ self.attributes = {nvp[0] : nvp[1] for nvp in urlparse.parse_qsl(endpoint_args, keep_blank_values=True)}
+ if opaque_data is not None:
+ self.attributes['OpaqueData'] = opaque_data
+ protocol = 'https' if conn.is_secure else 'http'
+ self.client = boto3.client('sns',
+ endpoint_url=protocol+'://'+conn.host+':'+str(conn.port),
+ aws_access_key_id=conn.aws_access_key_id,
+ aws_secret_access_key=conn.aws_secret_access_key,
+ region_name=region,
+ verify='./cert.pem')
+
+ def get_config(self):
+ """get topic info"""
+ parameters = {'Action': 'GetTopic', 'TopicArn': self.topic_arn}
+ body = urlparse.urlencode(parameters)
+ string_date = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
+ content_type = 'application/x-www-form-urlencoded; charset=utf-8'
+ resource = '/'
+ method = 'POST'
+ string_to_sign = method + '\n\n' + content_type + '\n' + string_date + '\n' + resource
+ log.debug('StringTosign: %s', string_to_sign)
+ signature = base64.b64encode(hmac.new(self.conn.aws_secret_access_key.encode('utf-8'),
+ string_to_sign.encode('utf-8'),
+ hashlib.sha1).digest()).decode('ascii')
+ headers = {'Authorization': 'AWS '+self.conn.aws_access_key_id+':'+signature,
+ 'Date': string_date,
+ 'Host': self.conn.host+':'+str(self.conn.port),
+ 'Content-Type': content_type}
+ if self.conn.is_secure:
+ http_conn = http_client.HTTPSConnection(self.conn.host, self.conn.port,
+ context=ssl.create_default_context(cafile='./cert.pem'))
+ else:
+ http_conn = http_client.HTTPConnection(self.conn.host, self.conn.port)
+ http_conn.request(method, resource, body, headers)
+ response = http_conn.getresponse()
+ data = response.read()
+ status = response.status
+ http_conn.close()
+ dict_response = xmltodict.parse(data)
+ return dict_response, status
+
+ def set_config(self):
+ """set topic"""
+ result = self.client.create_topic(Name=self.topic_name, Attributes=self.attributes)
+ self.topic_arn = result['TopicArn']
+ return self.topic_arn
+
+ def del_config(self, topic_arn=None):
+ """delete topic"""
+ result = self.client.delete_topic(TopicArn=(topic_arn if topic_arn is not None else self.topic_arn))
+ return result['ResponseMetadata']['HTTPStatusCode']
+
+ def get_list(self):
+ """list all topics"""
+ # note that boto3 supports list_topics(), however, the result only show ARNs
+ parameters = {'Action': 'ListTopics'}
+ body = urlparse.urlencode(parameters)
+ string_date = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
+ content_type = 'application/x-www-form-urlencoded; charset=utf-8'
+ resource = '/'
+ method = 'POST'
+ string_to_sign = method + '\n\n' + content_type + '\n' + string_date + '\n' + resource
+ log.debug('StringTosign: %s', string_to_sign)
+ signature = base64.b64encode(hmac.new(self.conn.aws_secret_access_key.encode('utf-8'),
+ string_to_sign.encode('utf-8'),
+ hashlib.sha1).digest()).decode('ascii')
+ headers = {'Authorization': 'AWS '+self.conn.aws_access_key_id+':'+signature,
+ 'Date': string_date,
+ 'Host': self.conn.host+':'+str(self.conn.port),
+ 'Content-Type': content_type}
+ if self.conn.is_secure:
+ http_conn = http_client.HTTPSConnection(self.conn.host, self.conn.port,
+ context=ssl.create_default_context(cafile='./cert.pem'))
+ else:
+ http_conn = http_client.HTTPConnection(self.conn.host, self.conn.port)
+ http_conn.request(method, resource, body, headers)
+ response = http_conn.getresponse()
+ data = response.read()
+ status = response.status
+ http_conn.close()
+ dict_response = xmltodict.parse(data)
+ return dict_response, status
+
+class PSNotificationS3:
+ """class to set/get/delete an S3 notification
+ PUT /<bucket>?notification
+ GET /<bucket>?notification[=<notification>]
+ DELETE /<bucket>?notification[=<notification>]
+ """
+ def __init__(self, conn, bucket_name, topic_conf_list):
+ self.conn = conn
+ assert bucket_name.strip()
+ self.bucket_name = bucket_name
+ self.resource = '/'+bucket_name
+ self.topic_conf_list = topic_conf_list
+ self.client = boto3.client('s3',
+ endpoint_url='http://'+conn.host+':'+str(conn.port),
+ aws_access_key_id=conn.aws_access_key_id,
+ aws_secret_access_key=conn.aws_secret_access_key)
+
+ def send_request(self, method, parameters=None):
+ """send request to radosgw"""
+ return make_request(self.conn, method, self.resource,
+ parameters=parameters, sign_parameters=True)
+
+ def get_config(self, notification=None):
+ """get notification info"""
+ parameters = None
+ if notification is None:
+ response = self.client.get_bucket_notification_configuration(Bucket=self.bucket_name)
+ status = response['ResponseMetadata']['HTTPStatusCode']
+ return response, status
+ parameters = {'notification': notification}
+ response, status = self.send_request('GET', parameters=parameters)
+ dict_response = xmltodict.parse(response)
+ return dict_response, status
+
+ def set_config(self):
+ """set notification"""
+ response = self.client.put_bucket_notification_configuration(Bucket=self.bucket_name,
+ NotificationConfiguration={
+ 'TopicConfigurations': self.topic_conf_list
+ })
+ status = response['ResponseMetadata']['HTTPStatusCode']
+ return response, status
+
+ def del_config(self, notification=None):
+ """delete notification"""
+ parameters = {'notification': notification}
+
+ return self.send_request('DELETE', parameters)
+
+
+test_path = os.path.normpath(os.path.dirname(os.path.realpath(__file__))) + '/../'
+
+def bash(cmd, **kwargs):
+ log.debug('running command: %s', ' '.join(cmd))
+ kwargs['stdout'] = subprocess.PIPE
+ process = subprocess.Popen(cmd, **kwargs)
+ s = process.communicate()[0].decode('utf-8')
+ return (s, process.returncode)
+
+def admin(args, **kwargs):
+ """ radosgw-admin command """
+ cmd = [test_path + 'test-rgw-call.sh', 'call_rgw_admin', 'noname'] + args
+ return bash(cmd, **kwargs)
+
diff --git a/src/test/rgw/bucket_notification/bntests.conf.SAMPLE b/src/test/rgw/bucket_notification/bntests.conf.SAMPLE
new file mode 100644
index 000000000..eb3291daf
--- /dev/null
+++ b/src/test/rgw/bucket_notification/bntests.conf.SAMPLE
@@ -0,0 +1,10 @@
+[DEFAULT]
+port = 8000
+host = localhost
+
+[s3 main]
+access_key = 0555b35654ad1656d804
+secret_key = h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q==
+display_name = M. Tester
+user_id = testid
+email = tester@ceph.com
diff --git a/src/test/rgw/bucket_notification/bootstrap b/src/test/rgw/bucket_notification/bootstrap
new file mode 100755
index 000000000..4d4a5a748
--- /dev/null
+++ b/src/test/rgw/bucket_notification/bootstrap
@@ -0,0 +1,45 @@
+#!/bin/sh
+set -e
+
+if [ -f /etc/debian_version ]; then
+ for package in python3-pip python3-dev python3-xmltodict python3-pika libevent-dev libxml2-dev libxslt-dev zlib1g-dev; do
+ if [ "$(dpkg --status -- $package 2>/dev/null|sed -n 's/^Status: //p')" != "install ok installed" ]; then
+ # add a space after old values
+ missing="${missing:+$missing }$package"
+ fi
+ done
+ if [ -n "$missing" ]; then
+ echo "$0: missing required DEB packages. Installing via sudo." 1>&2
+ sudo apt-get -y install $missing
+ fi
+fi
+if [ -f /etc/redhat-release ]; then
+ for package in python3-pip python3-devel python3-xmltodict python3-pika libevent-devel libxml2-devel libxslt-devel zlib-devel; do
+ if [ "$(rpm -qa $package 2>/dev/null)" == "" ]; then
+ missing="${missing:+$missing }$package"
+ fi
+ done
+ if [ -n "$missing" ]; then
+ echo "$0: missing required RPM packages. Installing via sudo." 1>&2
+ sudo yum -y install $missing
+ fi
+fi
+
+python3 -m venv --system-site-packages virtualenv
+
+# avoid pip bugs
+./virtualenv/bin/pip install --upgrade pip
+#pip3 install --upgrade setuptools cffi # address pip issue: https://github.com/pypa/pip/issues/6264
+
+# work-around change in pip 1.5
+#./virtualenv/bin/pip install six
+#./virtualenv/bin/pip install -I nose
+#./virtualenv/bin/pip install setuptools
+
+./virtualenv/bin/pip install -U -r requirements.txt
+
+# forbid setuptools from using the network because it'll try to use
+# easy_install, and we really wanted pip; next line will fail if pip
+# requirements.txt does not match setup.py requirements -- sucky but
+# good enough for now
+./virtualenv/bin/python setup.py develop
diff --git a/src/test/rgw/bucket_notification/kafka-security.sh b/src/test/rgw/bucket_notification/kafka-security.sh
new file mode 100755
index 000000000..6c6f3e261
--- /dev/null
+++ b/src/test/rgw/bucket_notification/kafka-security.sh
@@ -0,0 +1,49 @@
+FQDN=localhost
+KEYFILE=server.keystore.jks
+TRUSTFILE=server.truststore.jks
+CAFILE=y-ca.crt
+CAKEYFILE=y-ca.key
+REQFILE=$FQDN.req
+CERTFILE=$FQDN.crt
+MYPW=mypassword
+VALIDITY=36500
+
+rm -f $KEYFILE
+rm -f $TRUSTFILE
+rm -f $CAFILE
+rm -f $REQFILE
+rm -f $CERTFILE
+
+echo "########## create the request in key store '$KEYFILE'"
+keytool -keystore $KEYFILE -alias localhost \
+ -dname "CN=$FQDN, OU=Michigan Engineering, O=Red Hat Inc, \
+ L=Ann Arbor, ST=Michigan, C=US" \
+ -storepass $MYPW -keypass $MYPW \
+ -validity $VALIDITY -genkey -keyalg RSA -ext SAN=DNS:"$FQDN"
+
+echo "########## create the CA '$CAFILE'"
+openssl req -new -nodes -x509 -keyout $CAKEYFILE -out $CAFILE \
+ -days $VALIDITY -subj \
+ '/C=US/ST=Michigan/L=Ann Arbor/O=Red Hat Inc/OU=Michigan Engineering/CN=yuval-1'
+
+echo "########## store the CA in trust store '$TRUSTFILE'"
+keytool -keystore $TRUSTFILE -storepass $MYPW -alias CARoot \
+ -noprompt -importcert -file $CAFILE
+
+echo "########## create a request '$REQFILE' for signing in key store '$KEYFILE'"
+keytool -storepass $MYPW -keystore $KEYFILE \
+ -alias localhost -certreq -file $REQFILE
+
+echo "########## sign and create certificate '$CERTFILE'"
+openssl x509 -req -CA $CAFILE -CAkey $CAKEYFILE -CAcreateserial \
+ -days $VALIDITY \
+ -in $REQFILE -out $CERTFILE
+
+echo "########## store CA '$CAFILE' in key store '$KEYFILE'"
+keytool -storepass $MYPW -keystore $KEYFILE -alias CARoot \
+ -noprompt -importcert -file $CAFILE
+
+echo "########## store certificate '$CERTFILE' in key store '$KEYFILE'"
+keytool -storepass $MYPW -keystore $KEYFILE -alias localhost \
+ -import -file $CERTFILE
+
diff --git a/src/test/rgw/bucket_notification/requirements.txt b/src/test/rgw/bucket_notification/requirements.txt
new file mode 100644
index 000000000..a3cff2bed
--- /dev/null
+++ b/src/test/rgw/bucket_notification/requirements.txt
@@ -0,0 +1,8 @@
+nose >=1.0.0
+boto >=2.6.0
+boto3 >=1.0.0
+configparser >=5.0.0
+kafka-python >=2.0.0
+pika
+cloudevents
+xmltodict
diff --git a/src/test/rgw/bucket_notification/setup.py b/src/test/rgw/bucket_notification/setup.py
new file mode 100644
index 000000000..189ab27b4
--- /dev/null
+++ b/src/test/rgw/bucket_notification/setup.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+from setuptools import setup, find_packages
+
+setup(
+ name='bn_tests',
+ version='0.0.1',
+ packages=find_packages(),
+
+ author='Kalpesh Pandya',
+ author_email='kapandya@redhat.com',
+ description='Bucket Notification compatibility tests',
+ license='MIT',
+ keywords='bn web testing',
+
+ install_requires=[
+ 'boto >=2.0b4',
+ 'boto3 >=1.0.0'
+ ],
+ )
diff --git a/src/test/rgw/bucket_notification/test_bn.py b/src/test/rgw/bucket_notification/test_bn.py
new file mode 100644
index 000000000..87a2acb76
--- /dev/null
+++ b/src/test/rgw/bucket_notification/test_bn.py
@@ -0,0 +1,4128 @@
+import logging
+import json
+import tempfile
+import random
+import threading
+import subprocess
+import socket
+import time
+import os
+import string
+import boto
+from botocore.exceptions import ClientError
+from http import server as http_server
+from random import randint
+import hashlib
+from nose.plugins.attrib import attr
+import boto3
+import datetime
+from cloudevents.http import from_http
+from dateutil import parser
+
+from boto.s3.connection import S3Connection
+
+from . import(
+ get_config_host,
+ get_config_port,
+ get_access_key,
+ get_secret_key
+ )
+
+from .api import PSTopicS3, \
+ PSNotificationS3, \
+ delete_all_objects, \
+ put_object_tagging, \
+ admin
+
+from nose import SkipTest
+from nose.tools import assert_not_equal, assert_equal, assert_in
+import boto.s3.tagging
+
+# configure logging for the tests module
+log = logging.getLogger(__name__)
+
+TOPIC_SUFFIX = "_topic"
+NOTIFICATION_SUFFIX = "_notif"
+
+
+num_buckets = 0
+run_prefix=''.join(random.choice(string.ascii_lowercase) for _ in range(6))
+
+def gen_bucket_name():
+ global num_buckets
+
+ num_buckets += 1
+ return run_prefix + '-' + str(num_buckets)
+
+
+def set_contents_from_string(key, content):
+ try:
+ key.set_contents_from_string(content)
+ except Exception as e:
+ print('Error: ' + str(e))
+
+
+class HTTPPostHandler(http_server.BaseHTTPRequestHandler):
+ """HTTP POST hanler class storing the received events in its http server"""
+ def do_POST(self):
+ """implementation of POST handler"""
+ content_length = int(self.headers['Content-Length'])
+ body = self.rfile.read(content_length)
+ if self.server.cloudevents:
+ event = from_http(self.headers, body)
+ record = json.loads(body)['Records'][0]
+ assert_equal(event['specversion'], '1.0')
+ assert_equal(event['id'], record['responseElements']['x-amz-request-id'] + '.' + record['responseElements']['x-amz-id-2'])
+ assert_equal(event['source'], 'ceph:s3.' + record['awsRegion'] + '.' + record['s3']['bucket']['name'])
+ assert_equal(event['type'], 'com.amazonaws.' + record['eventName'])
+ assert_equal(event['datacontenttype'], 'application/json')
+ assert_equal(event['subject'], record['s3']['object']['key'])
+ assert_equal(parser.parse(event['time']), parser.parse(record['eventTime']))
+ log.info('HTTP Server (%d) received event: %s', self.server.worker_id, str(body))
+ self.server.append(json.loads(body))
+ if self.headers.get('Expect') == '100-continue':
+ self.send_response(100)
+ else:
+ self.send_response(200)
+ if self.server.delay > 0:
+ time.sleep(self.server.delay)
+ self.end_headers()
+
+
+class HTTPServerWithEvents(http_server.HTTPServer):
+ """HTTP server used by the handler to store events"""
+ def __init__(self, addr, handler, worker_id, delay=0, cloudevents=False):
+ http_server.HTTPServer.__init__(self, addr, handler, False)
+ self.worker_id = worker_id
+ self.events = []
+ self.delay = delay
+ self.cloudevents = cloudevents
+
+ def append(self, event):
+ self.events.append(event)
+
+class HTTPServerThread(threading.Thread):
+ """thread for running the HTTP server. reusing the same socket for all threads"""
+ def __init__(self, i, sock, addr, delay=0, cloudevents=False):
+ threading.Thread.__init__(self)
+ self.i = i
+ self.daemon = True
+ self.httpd = HTTPServerWithEvents(addr, HTTPPostHandler, i, delay, cloudevents)
+ self.httpd.socket = sock
+ # prevent the HTTP server from re-binding every handler
+ self.httpd.server_bind = self.server_close = lambda self: None
+ self.start()
+
+ def run(self):
+ try:
+ log.info('HTTP Server (%d) started on: %s', self.i, self.httpd.server_address)
+ self.httpd.serve_forever()
+ log.info('HTTP Server (%d) ended', self.i)
+ except Exception as error:
+ # could happen if the server r/w to a closing socket during shutdown
+ log.info('HTTP Server (%d) ended unexpectedly: %s', self.i, str(error))
+
+ def close(self):
+ self.httpd.shutdown()
+
+ def get_events(self):
+ return self.httpd.events
+
+ def reset_events(self):
+ self.httpd.events = []
+
+class StreamingHTTPServer:
+ """multi-threaded http server class also holding list of events received into the handler
+ each thread has its own server, and all servers share the same socket"""
+ def __init__(self, host, port, num_workers=100, delay=0, cloudevents=False):
+ addr = (host, port)
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.sock.bind(addr)
+ self.sock.listen(num_workers)
+ self.workers = [HTTPServerThread(i, self.sock, addr, delay, cloudevents) for i in range(num_workers)]
+
+ def verify_s3_events(self, keys, exact_match=False, deletions=False, expected_sizes={}):
+ """verify stored s3 records agains a list of keys"""
+ events = []
+ for worker in self.workers:
+ events += worker.get_events()
+ worker.reset_events()
+ verify_s3_records_by_elements(events, keys, exact_match=exact_match, deletions=deletions, expected_sizes=expected_sizes)
+
+ def verify_events(self, keys, exact_match=False, deletions=False):
+ """verify stored events agains a list of keys"""
+ events = []
+ for worker in self.workers:
+ events += worker.get_events()
+ worker.reset_events()
+ verify_events_by_elements(events, keys, exact_match=exact_match, deletions=deletions)
+
+ def get_and_reset_events(self):
+ events = []
+ for worker in self.workers:
+ events += worker.get_events()
+ worker.reset_events()
+ return events
+
+ def close(self):
+ """close all workers in the http server and wait for it to finish"""
+ # make sure that the shared socket is closed
+ # this is needed in case that one of the threads is blocked on the socket
+ self.sock.shutdown(socket.SHUT_RDWR)
+ self.sock.close()
+ # wait for server threads to finish
+ for worker in self.workers:
+ worker.close()
+ worker.join()
+
+# AMQP endpoint functions
+
+class AMQPReceiver(object):
+ """class for receiving and storing messages on a topic from the AMQP broker"""
+ def __init__(self, exchange, topic, external_endpoint_address=None, ca_location=None):
+ import pika
+ import ssl
+
+ if ca_location:
+ ssl_context = ssl.create_default_context()
+ ssl_context.load_verify_locations(cafile=ca_location)
+ ssl_options = pika.SSLOptions(ssl_context)
+ rabbitmq_port = 5671
+ else:
+ rabbitmq_port = 5672
+ ssl_options = None
+
+ if external_endpoint_address:
+ if ssl_options:
+ # this is currently not working due to: https://github.com/pika/pika/issues/1192
+ params = pika.URLParameters(external_endpoint_address, ssl_options=ssl_options)
+ else:
+ params = pika.URLParameters(external_endpoint_address)
+ else:
+ hostname = get_ip()
+ params = pika.ConnectionParameters(host=hostname, port=rabbitmq_port, ssl_options=ssl_options)
+
+ remaining_retries = 10
+ while remaining_retries > 0:
+ try:
+ connection = pika.BlockingConnection(params)
+ break
+ except Exception as error:
+ remaining_retries -= 1
+ print('failed to connect to rabbitmq (remaining retries '
+ + str(remaining_retries) + '): ' + str(error))
+ time.sleep(1)
+
+ if remaining_retries == 0:
+ raise Exception('failed to connect to rabbitmq - no retries left')
+
+ self.channel = connection.channel()
+ self.channel.exchange_declare(exchange=exchange, exchange_type='topic', durable=True)
+ result = self.channel.queue_declare('', exclusive=True)
+ queue_name = result.method.queue
+ self.channel.queue_bind(exchange=exchange, queue=queue_name, routing_key=topic)
+ self.channel.basic_consume(queue=queue_name,
+ on_message_callback=self.on_message,
+ auto_ack=True)
+ self.events = []
+ self.topic = topic
+
+ def on_message(self, ch, method, properties, body):
+ """callback invoked when a new message arrive on the topic"""
+ log.info('AMQP received event for topic %s:\n %s', self.topic, body)
+ self.events.append(json.loads(body))
+
+ # TODO create a base class for the AMQP and HTTP cases
+ def verify_s3_events(self, keys, exact_match=False, deletions=False, expected_sizes={}):
+ """verify stored s3 records agains a list of keys"""
+ verify_s3_records_by_elements(self.events, keys, exact_match=exact_match, deletions=deletions, expected_sizes=expected_sizes)
+ self.events = []
+
+ def verify_events(self, keys, exact_match=False, deletions=False):
+ """verify stored events agains a list of keys"""
+ verify_events_by_elements(self.events, keys, exact_match=exact_match, deletions=deletions)
+ self.events = []
+
+ def get_and_reset_events(self):
+ tmp = self.events
+ self.events = []
+ return tmp
+
+
+def amqp_receiver_thread_runner(receiver):
+ """main thread function for the amqp receiver"""
+ try:
+ log.info('AMQP receiver started')
+ receiver.channel.start_consuming()
+ log.info('AMQP receiver ended')
+ except Exception as error:
+ log.info('AMQP receiver ended unexpectedly: %s', str(error))
+
+
+def create_amqp_receiver_thread(exchange, topic, external_endpoint_address=None, ca_location=None):
+ """create amqp receiver and thread"""
+ receiver = AMQPReceiver(exchange, topic, external_endpoint_address, ca_location)
+ task = threading.Thread(target=amqp_receiver_thread_runner, args=(receiver,))
+ task.daemon = True
+ return task, receiver
+
+def stop_amqp_receiver(receiver, task):
+ """stop the receiver thread and wait for it to finis"""
+ try:
+ receiver.channel.stop_consuming()
+ log.info('stopping AMQP receiver')
+ except Exception as error:
+ log.info('failed to gracefuly stop AMQP receiver: %s', str(error))
+ task.join(5)
+
+
+def init_rabbitmq():
+ """ start a rabbitmq broker """
+ hostname = get_ip()
+ try:
+ # first try to stop any existing process
+ subprocess.call(['sudo', 'rabbitmqctl', 'stop'])
+ time.sleep(5)
+ proc = subprocess.Popen(['sudo', '--preserve-env=RABBITMQ_CONFIG_FILE', 'rabbitmq-server'])
+ except Exception as error:
+ log.info('failed to execute rabbitmq-server: %s', str(error))
+ print('failed to execute rabbitmq-server: %s' % str(error))
+ return None
+ # TODO add rabbitmq checkpoint instead of sleep
+ time.sleep(5)
+ return proc
+
+
+def clean_rabbitmq(proc):
+ """ stop the rabbitmq broker """
+ try:
+ subprocess.call(['sudo', 'rabbitmqctl', 'stop'])
+ time.sleep(5)
+ proc.terminate()
+ except:
+ log.info('rabbitmq server already terminated')
+
+
+def verify_events_by_elements(events, keys, exact_match=False, deletions=False):
+ """ verify there is at least one event per element """
+ err = ''
+ for key in keys:
+ key_found = False
+ if type(events) is list:
+ for event_list in events:
+ if key_found:
+ break
+ for event in event_list['events']:
+ if event['info']['bucket']['name'] == key.bucket.name and \
+ event['info']['key']['name'] == key.name:
+ if deletions and event['event'] == 'OBJECT_DELETE':
+ key_found = True
+ break
+ elif not deletions and event['event'] == 'OBJECT_CREATE':
+ key_found = True
+ break
+ else:
+ for event in events['events']:
+ if event['info']['bucket']['name'] == key.bucket.name and \
+ event['info']['key']['name'] == key.name:
+ if deletions and event['event'] == 'OBJECT_DELETE':
+ key_found = True
+ break
+ elif not deletions and event['event'] == 'OBJECT_CREATE':
+ key_found = True
+ break
+
+ if not key_found:
+ err = 'no ' + ('deletion' if deletions else 'creation') + ' event found for key: ' + str(key)
+ log.error(events)
+ assert False, err
+
+ if not len(events) == len(keys):
+ err = 'superfluous events are found'
+ log.debug(err)
+ if exact_match:
+ log.error(events)
+ assert False, err
+
+META_PREFIX = 'x-amz-meta-'
+
+def verify_s3_records_by_elements(records, keys, exact_match=False, deletions=False, expected_sizes={}, etags=[]):
+ """ verify there is at least one record per element """
+ err = ''
+ for key in keys:
+ key_found = False
+ object_size = 0
+ if type(records) is list:
+ for record_list in records:
+ if key_found:
+ break
+ for record in record_list['Records']:
+ assert_in('eTag', record['s3']['object'])
+ if record['s3']['bucket']['name'] == key.bucket.name and \
+ record['s3']['object']['key'] == key.name:
+ # Assertion Error needs to be fixed
+ #assert_equal(key.etag[1:-1], record['s3']['object']['eTag'])
+ if etags:
+ assert_in(key.etag[1:-1], etags)
+ if len(record['s3']['object']['metadata']) > 0:
+ for meta in record['s3']['object']['metadata']:
+ assert(meta['key'].startswith(META_PREFIX))
+ if deletions and record['eventName'].startswith('ObjectRemoved'):
+ key_found = True
+ object_size = record['s3']['object']['size']
+ break
+ elif not deletions and record['eventName'].startswith('ObjectCreated'):
+ key_found = True
+ object_size = record['s3']['object']['size']
+ break
+ else:
+ for record in records['Records']:
+ assert_in('eTag', record['s3']['object'])
+ if record['s3']['bucket']['name'] == key.bucket.name and \
+ record['s3']['object']['key'] == key.name:
+ assert_equal(key.etag, record['s3']['object']['eTag'])
+ if etags:
+ assert_in(key.etag[1:-1], etags)
+ if len(record['s3']['object']['metadata']) > 0:
+ for meta in record['s3']['object']['metadata']:
+ assert(meta['key'].startswith(META_PREFIX))
+ if deletions and record['eventName'].startswith('ObjectRemoved'):
+ key_found = True
+ object_size = record['s3']['object']['size']
+ break
+ elif not deletions and record['eventName'].startswith('ObjectCreated'):
+ key_found = True
+ object_size = record['s3']['object']['size']
+ break
+
+ if not key_found:
+ err = 'no ' + ('deletion' if deletions else 'creation') + ' event found for key: ' + str(key)
+ assert False, err
+ elif expected_sizes:
+ assert_equal(object_size, expected_sizes.get(key.name))
+
+ if not len(records) == len(keys):
+ err = 'superfluous records are found'
+ log.warning(err)
+ if exact_match:
+ for record_list in records:
+ for record in record_list['Records']:
+ log.error(str(record['s3']['bucket']['name']) + ',' + str(record['s3']['object']['key']))
+ assert False, err
+
+
+# Kafka endpoint functions
+
+kafka_server = 'localhost'
+
+class KafkaReceiver(object):
+ """class for receiving and storing messages on a topic from the kafka broker"""
+ def __init__(self, topic, security_type):
+ from kafka import KafkaConsumer
+ remaining_retries = 10
+ port = 9092
+ if security_type != 'PLAINTEXT':
+ security_type = 'SSL'
+ port = 9093
+ while remaining_retries > 0:
+ try:
+ self.consumer = KafkaConsumer(topic,
+ bootstrap_servers = kafka_server+':'+str(port),
+ security_protocol=security_type,
+ consumer_timeout_ms=16000)
+ print('Kafka consumer created on topic: '+topic)
+ break
+ except Exception as error:
+ remaining_retries -= 1
+ print('failed to connect to kafka (remaining retries '
+ + str(remaining_retries) + '): ' + str(error))
+ time.sleep(1)
+
+ if remaining_retries == 0:
+ raise Exception('failed to connect to kafka - no retries left')
+
+ self.events = []
+ self.topic = topic
+ self.stop = False
+
+ def verify_s3_events(self, keys, exact_match=False, deletions=False, etags=[]):
+ """verify stored s3 records agains a list of keys"""
+ verify_s3_records_by_elements(self.events, keys, exact_match=exact_match, deletions=deletions, etags=etags)
+ self.events = []
+
+def kafka_receiver_thread_runner(receiver):
+ """main thread function for the kafka receiver"""
+ try:
+ log.info('Kafka receiver started')
+ print('Kafka receiver started')
+ while not receiver.stop:
+ for msg in receiver.consumer:
+ receiver.events.append(json.loads(msg.value))
+ time.sleep(0.1)
+ log.info('Kafka receiver ended')
+ print('Kafka receiver ended')
+ except Exception as error:
+ log.info('Kafka receiver ended unexpectedly: %s', str(error))
+ print('Kafka receiver ended unexpectedly: ' + str(error))
+
+
+def create_kafka_receiver_thread(topic, security_type='PLAINTEXT'):
+ """create kafka receiver and thread"""
+ receiver = KafkaReceiver(topic, security_type)
+ task = threading.Thread(target=kafka_receiver_thread_runner, args=(receiver,))
+ task.daemon = True
+ return task, receiver
+
+def stop_kafka_receiver(receiver, task):
+ """stop the receiver thread and wait for it to finis"""
+ receiver.stop = True
+ task.join(1)
+ try:
+ receiver.consumer.unsubscribe()
+ receiver.consumer.close()
+ except Exception as error:
+ log.info('failed to gracefuly stop Kafka receiver: %s', str(error))
+
+
+def get_ip():
+ return 'localhost'
+
+
+def get_ip_http():
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ # address should not be reachable
+ s.connect(('10.255.255.255', 1))
+ ip = s.getsockname()[0]
+ finally:
+ s.close()
+ return ip
+
+
+def connection():
+ hostname = get_config_host()
+ port_no = get_config_port()
+ vstart_access_key = get_access_key()
+ vstart_secret_key = get_secret_key()
+
+ conn = S3Connection(aws_access_key_id=vstart_access_key,
+ aws_secret_access_key=vstart_secret_key,
+ is_secure=False, port=port_no, host=hostname,
+ calling_format='boto.s3.connection.OrdinaryCallingFormat')
+
+ return conn
+
+
+def connection2():
+ hostname = get_config_host()
+ port_no = 8001
+ vstart_access_key = get_access_key()
+ vstart_secret_key = get_secret_key()
+
+ conn = S3Connection(aws_access_key_id=vstart_access_key,
+ aws_secret_access_key=vstart_secret_key,
+ is_secure=False, port=port_no, host=hostname,
+ calling_format='boto.s3.connection.OrdinaryCallingFormat')
+
+ return conn
+
+
+def another_user(tenant=None):
+ access_key = str(time.time())
+ secret_key = str(time.time())
+ uid = 'superman' + str(time.time())
+ if tenant:
+ _, result = admin(['user', 'create', '--uid', uid, '--tenant', tenant, '--access-key', access_key, '--secret-key', secret_key, '--display-name', '"Super Man"'])
+ else:
+ _, result = admin(['user', 'create', '--uid', uid, '--access-key', access_key, '--secret-key', secret_key, '--display-name', '"Super Man"'])
+
+ assert_equal(result, 0)
+ conn = S3Connection(aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key,
+ is_secure=False, port=get_config_port(), host=get_config_host(),
+ calling_format='boto.s3.connection.OrdinaryCallingFormat')
+ return conn
+
+##############
+# bucket notifications tests
+##############
+
+
+@attr('basic_test')
+def test_ps_s3_topic_on_master():
+ """ test s3 topics set/get/delete on master """
+ tenant = 'kaboom'
+ conn = another_user(tenant)
+ zonegroup = 'default'
+ bucket_name = gen_bucket_name()
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topics
+ endpoint_address = 'amqp://127.0.0.1:7001/vhost_1'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ topic_conf1 = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ # clean all topics
+ try:
+ result = topic_conf1.get_list()[0]['ListTopicsResponse']['ListTopicsResult']['Topics']
+ topics = []
+ if result is not None:
+ topics = result['member']
+ for topic in topics:
+ topic_conf1.del_config(topic_arn=topic['TopicArn'])
+ except Exception as err:
+ print('failed to do topic cleanup: ' + str(err))
+
+ topic_arn = topic_conf1.set_config()
+ assert_equal(topic_arn,
+ 'arn:aws:sns:' + zonegroup + ':' + tenant + ':' + topic_name + '_1')
+
+ endpoint_address = 'http://127.0.0.1:9001'
+ endpoint_args = 'push-endpoint='+endpoint_address
+ topic_conf2 = PSTopicS3(conn, topic_name+'_2', zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf2.set_config()
+ assert_equal(topic_arn,
+ 'arn:aws:sns:' + zonegroup + ':' + tenant + ':' + topic_name + '_2')
+ endpoint_address = 'http://127.0.0.1:9002'
+ endpoint_args = 'push-endpoint='+endpoint_address
+ topic_conf3 = PSTopicS3(conn, topic_name+'_3', zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf3.set_config()
+ assert_equal(topic_arn,
+ 'arn:aws:sns:' + zonegroup + ':' + tenant + ':' + topic_name + '_3')
+
+ # get topic 3
+ result, status = topic_conf3.get_config()
+ assert_equal(status, 200)
+ assert_equal(topic_arn, result['GetTopicResponse']['GetTopicResult']['Topic']['TopicArn'])
+ assert_equal(endpoint_address, result['GetTopicResponse']['GetTopicResult']['Topic']['EndPoint']['EndpointAddress'])
+
+ # Note that endpoint args may be ordered differently in the result
+ # delete topic 1
+ result = topic_conf1.del_config()
+ assert_equal(status, 200)
+
+ # try to get a deleted topic
+ _, status = topic_conf1.get_config()
+ assert_equal(status, 404)
+
+ # get the remaining 2 topics
+ result, status = topic_conf1.get_list()
+ assert_equal(status, 200)
+ assert_equal(len(result['ListTopicsResponse']['ListTopicsResult']['Topics']['member']), 2)
+
+ # delete topics
+ result = topic_conf2.del_config()
+ assert_equal(status, 200)
+ result = topic_conf3.del_config()
+ assert_equal(status, 200)
+
+ # get topic list, make sure it is empty
+ result, status = topic_conf1.get_list()
+ assert_equal(result['ListTopicsResponse']['ListTopicsResult']['Topics'], None)
+
+
+@attr('basic_test')
+def test_ps_s3_topic_admin_on_master():
+ """ test s3 topics set/get/delete on master """
+ tenant = 'kaboom'
+ conn = another_user(tenant)
+ zonegroup = 'default'
+ bucket_name = gen_bucket_name()
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topics
+ endpoint_address = 'amqp://127.0.0.1:7001/vhost_1'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ topic_conf1 = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ # clean all topics
+ try:
+ result = topic_conf1.get_list()[0]['ListTopicsResponse']['ListTopicsResult']['Topics']
+ topics = []
+ if result is not None:
+ topics = result['member']
+ for topic in topics:
+ topic_conf1.del_config(topic_arn=topic['TopicArn'])
+ except Exception as err:
+ print('failed to do topic cleanup: ' + str(err))
+
+ topic_arn1 = topic_conf1.set_config()
+ assert_equal(topic_arn1,
+ 'arn:aws:sns:' + zonegroup + ':' + tenant + ':' + topic_name + '_1')
+
+ endpoint_address = 'http://127.0.0.1:9001'
+ endpoint_args = 'push-endpoint='+endpoint_address
+ topic_conf2 = PSTopicS3(conn, topic_name+'_2', zonegroup, endpoint_args=endpoint_args)
+ topic_arn2 = topic_conf2.set_config()
+ assert_equal(topic_arn2,
+ 'arn:aws:sns:' + zonegroup + ':' + tenant + ':' + topic_name + '_2')
+ endpoint_address = 'http://127.0.0.1:9002'
+ endpoint_args = 'push-endpoint='+endpoint_address
+ topic_conf3 = PSTopicS3(conn, topic_name+'_3', zonegroup, endpoint_args=endpoint_args)
+ topic_arn3 = topic_conf3.set_config()
+ assert_equal(topic_arn3,
+ 'arn:aws:sns:' + zonegroup + ':' + tenant + ':' + topic_name + '_3')
+
+ # get topic 3 via commandline
+ result = admin(['topic', 'get', '--topic', topic_name+'_3', '--tenant', tenant])
+ parsed_result = json.loads(result[0])
+ assert_equal(parsed_result['arn'], topic_arn3)
+
+ # delete topic 3
+ _, result = admin(['topic', 'rm', '--topic', topic_name+'_3', '--tenant', tenant])
+ assert_equal(result, 0)
+
+ # try to get a deleted topic
+ _, result = admin(['topic', 'get', '--topic', topic_name+'_3', '--tenant', tenant])
+ print('"topic not found" error is expected')
+ assert_equal(result, 2)
+
+ # get the remaining 2 topics
+ result = admin(['topic', 'list', '--tenant', tenant])
+ parsed_result = json.loads(result[0])
+ assert_equal(len(parsed_result['topics']), 2)
+
+ # delete topics
+ _, result = admin(['topic', 'rm', '--topic', topic_name+'_1', '--tenant', tenant])
+ assert_equal(result, 0)
+ _, result = admin(['topic', 'rm', '--topic', topic_name+'_2', '--tenant', tenant])
+ assert_equal(result, 0)
+
+ # get topic list, make sure it is empty
+ result = admin(['topic', 'list', '--tenant', tenant])
+ parsed_result = json.loads(result[0])
+ assert_equal(len(parsed_result['topics']), 0)
+
+
+@attr('basic_test')
+def test_ps_s3_notification_configuration_admin_on_master():
+ """ test s3 notification list/get/delete on master """
+ conn = connection()
+ zonegroup = 'default'
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topics
+ endpoint_address = 'amqp://127.0.0.1:7001/vhost_1'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ topic_conf = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ # clean all topics
+ try:
+ result = topic_conf.get_list()[0]['ListTopicsResponse']['ListTopicsResult']['Topics']
+ topics = []
+ if result is not None:
+ topics = result['member']
+ for topic in topics:
+ topic_conf.del_config(topic_arn=topic['TopicArn'])
+ except Exception as err:
+ print('failed to do topic cleanup: ' + str(err))
+
+ topic_arn = topic_conf.set_config()
+ assert_equal(topic_arn,
+ 'arn:aws:sns:' + zonegroup + '::' + topic_name + '_1')
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*']
+ },
+ {'Id': notification_name+'_2',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectRemoved:*']
+ },
+ {'Id': notification_name+'_3',
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # list notification
+ result = admin(['notification', 'list', '--bucket', bucket_name])
+ parsed_result = json.loads(result[0])
+ assert_equal(len(parsed_result['notifications']), 3)
+ assert_equal(result[1], 0)
+
+ # get notification 1
+ result = admin(['notification', 'get', '--bucket', bucket_name, '--notification-id', notification_name+'_1'])
+ parsed_result = json.loads(result[0])
+ assert_equal(parsed_result['Id'], notification_name+'_1')
+ assert_equal(result[1], 0)
+
+ # remove notification 3
+ _, result = admin(['notification', 'rm', '--bucket', bucket_name, '--notification-id', notification_name+'_3'])
+ assert_equal(result, 0)
+
+ # list notification
+ result = admin(['notification', 'list', '--bucket', bucket_name])
+ parsed_result = json.loads(result[0])
+ assert_equal(len(parsed_result['notifications']), 2)
+ assert_equal(result[1], 0)
+
+ # delete notifications
+ _, result = admin(['notification', 'rm', '--bucket', bucket_name])
+ assert_equal(result, 0)
+
+ # list notification, make sure it is empty
+ result = admin(['notification', 'list', '--bucket', bucket_name])
+ parsed_result = json.loads(result[0])
+ assert_equal(len(parsed_result['notifications']), 0)
+ assert_equal(result[1], 0)
+
+
+@attr('modification_required')
+def test_ps_s3_topic_with_secret_on_master():
+ """ test s3 topics with secret set/get/delete on master """
+ return SkipTest('secure connection is needed to test topic with secrets')
+
+ conn = connection1()
+ if conn.secure_conn is None:
+ return SkipTest('secure connection is needed to test topic with secrets')
+
+ zonegroup = 'default'
+ bucket_name = gen_bucket_name()
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # clean all topics
+ delete_all_s3_topics(conn, zonegroup)
+
+ # create s3 topics
+ endpoint_address = 'amqp://user:password@127.0.0.1:7001'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ bad_topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ try:
+ result = bad_topic_conf.set_config()
+ except Exception as err:
+ print('Error is expected: ' + str(err))
+ else:
+ assert False, 'user password configuration set allowed only over HTTPS'
+ topic_conf = PSTopicS3(conn.secure_conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+
+ assert_equal(topic_arn,
+ 'arn:aws:sns:' + zonegroup + ':' + get_tenant() + ':' + topic_name)
+
+ _, status = bad_topic_conf.get_config()
+ assert_equal(status/100, 4)
+
+ # get topic
+ result, status = topic_conf.get_config()
+ assert_equal(status, 200)
+ assert_equal(topic_arn, result['GetTopicResponse']['GetTopicResult']['Topic']['TopicArn'])
+ assert_equal(endpoint_address, result['GetTopicResponse']['GetTopicResult']['Topic']['EndPoint']['EndpointAddress'])
+
+ _, status = bad_topic_conf.get_config()
+ assert_equal(status/100, 4)
+
+ _, status = topic_conf.get_list()
+ assert_equal(status/100, 2)
+
+ # delete topics
+ result = topic_conf.del_config()
+
+
+@attr('basic_test')
+def test_ps_s3_notification_on_master():
+ """ test s3 notification set/get/delete on master """
+ conn = connection()
+ zonegroup = 'default'
+ bucket_name = gen_bucket_name()
+ # create bucket
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+ # create s3 topic
+ endpoint_address = 'amqp://127.0.0.1:7001'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*']
+ },
+ {'Id': notification_name+'_2',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectRemoved:*']
+ },
+ {'Id': notification_name+'_3',
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # get notifications on a bucket
+ response, status = s3_notification_conf.get_config(notification=notification_name+'_1')
+ assert_equal(status/100, 2)
+ assert_equal(response['NotificationConfiguration']['TopicConfiguration']['Topic'], topic_arn)
+
+ # delete specific notifications
+ _, status = s3_notification_conf.del_config(notification=notification_name+'_1')
+ assert_equal(status/100, 2)
+
+ # get the remaining 2 notifications on a bucket
+ response, status = s3_notification_conf.get_config()
+ assert_equal(status/100, 2)
+ assert_equal(len(response['TopicConfigurations']), 2)
+ assert_equal(response['TopicConfigurations'][0]['TopicArn'], topic_arn)
+ assert_equal(response['TopicConfigurations'][1]['TopicArn'], topic_arn)
+
+ # delete remaining notifications
+ _, status = s3_notification_conf.del_config()
+ assert_equal(status/100, 2)
+
+ # make sure that the notifications are now deleted
+ _, status = s3_notification_conf.get_config()
+
+ # cleanup
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+
+@attr('basic_test')
+def test_ps_s3_notification_on_master_empty_config():
+ """ test s3 notification set/get/delete on master with empty config """
+ hostname = get_ip()
+
+ conn = connection()
+
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'amqp://127.0.0.1:7001'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1',
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # get notifications on a bucket
+ response, status = s3_notification_conf.get_config(notification=notification_name+'_1')
+ assert_equal(status/100, 2)
+ assert_equal(response['NotificationConfiguration']['TopicConfiguration']['Topic'], topic_arn)
+
+ # create s3 notification again with empty configuration to check if it deletes or not
+ topic_conf_list = []
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # make sure that the notification is now deleted
+ response, status = s3_notification_conf.get_config()
+ try:
+ check = response['NotificationConfiguration']
+ except KeyError as e:
+ assert_equal(status/100, 2)
+ else:
+ assert False
+
+ # cleanup
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+
+@attr('amqp_test')
+def test_ps_s3_notification_filter_on_master():
+ """ test s3 notification filter on master """
+
+ hostname = get_ip()
+
+ conn = connection()
+ ps_zone = conn
+
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receivers
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=broker'
+
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*'],
+ 'Filter': {
+ 'Key': {
+ 'FilterRules': [{'Name': 'prefix', 'Value': 'hello'}]
+ }
+ }
+ },
+ {'Id': notification_name+'_2',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*'],
+ 'Filter': {
+ 'Key': {
+ 'FilterRules': [{'Name': 'prefix', 'Value': 'world'},
+ {'Name': 'suffix', 'Value': 'log'}]
+ }
+ }
+ },
+ {'Id': notification_name+'_3',
+ 'TopicArn': topic_arn,
+ 'Events': [],
+ 'Filter': {
+ 'Key': {
+ 'FilterRules': [{'Name': 'regex', 'Value': '([a-z]+)\\.txt'}]
+ }
+ }
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ result, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ topic_conf_list = [{'Id': notification_name+'_4',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*', 's3:ObjectRemoved:*'],
+ 'Filter': {
+ 'Metadata': {
+ 'FilterRules': [{'Name': 'x-amz-meta-foo', 'Value': 'bar'},
+ {'Name': 'x-amz-meta-hello', 'Value': 'world'}]
+ },
+ 'Key': {
+ 'FilterRules': [{'Name': 'regex', 'Value': '([a-z]+)'}]
+ }
+ }
+ }]
+
+ try:
+ s3_notification_conf4 = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf4.set_config()
+ assert_equal(status/100, 2)
+ skip_notif4 = False
+ except Exception as error:
+ print('note: metadata filter is not supported by boto3 - skipping test')
+ skip_notif4 = True
+
+
+ # get all notifications
+ result, status = s3_notification_conf.get_config()
+ assert_equal(status/100, 2)
+ for conf in result['TopicConfigurations']:
+ filter_name = conf['Filter']['Key']['FilterRules'][0]['Name']
+ assert filter_name == 'prefix' or filter_name == 'suffix' or filter_name == 'regex', filter_name
+
+ if not skip_notif4:
+ result, status = s3_notification_conf4.get_config(notification=notification_name+'_4')
+ assert_equal(status/100, 2)
+ filter_name = result['NotificationConfiguration']['TopicConfiguration']['Filter']['S3Metadata']['FilterRule'][0]['Name']
+ assert filter_name == 'x-amz-meta-foo' or filter_name == 'x-amz-meta-hello'
+
+ expected_in1 = ['hello.kaboom', 'hello.txt', 'hello123.txt', 'hello']
+ expected_in2 = ['world1.log', 'world2log', 'world3.log']
+ expected_in3 = ['hello.txt', 'hell.txt', 'worldlog.txt']
+ expected_in4 = ['foo', 'bar', 'hello', 'world']
+ filtered = ['hell.kaboom', 'world.og', 'world.logg', 'he123ll.txt', 'wo', 'log', 'h', 'txt', 'world.log.txt']
+ filtered_with_attr = ['nofoo', 'nobar', 'nohello', 'noworld']
+ # create objects in bucket
+ for key_name in expected_in1:
+ key = bucket.new_key(key_name)
+ key.set_contents_from_string('bar')
+ for key_name in expected_in2:
+ key = bucket.new_key(key_name)
+ key.set_contents_from_string('bar')
+ for key_name in expected_in3:
+ key = bucket.new_key(key_name)
+ key.set_contents_from_string('bar')
+ if not skip_notif4:
+ for key_name in expected_in4:
+ key = bucket.new_key(key_name)
+ key.set_metadata('foo', 'bar')
+ key.set_metadata('hello', 'world')
+ key.set_metadata('goodbye', 'cruel world')
+ key.set_contents_from_string('bar')
+ for key_name in filtered:
+ key = bucket.new_key(key_name)
+ key.set_contents_from_string('bar')
+ for key_name in filtered_with_attr:
+ key.set_metadata('foo', 'nobar')
+ key.set_metadata('hello', 'noworld')
+ key.set_metadata('goodbye', 'cruel world')
+ key = bucket.new_key(key_name)
+ key.set_contents_from_string('bar')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ found_in1 = []
+ found_in2 = []
+ found_in3 = []
+ found_in4 = []
+
+ for event in receiver.get_and_reset_events():
+ notif_id = event['Records'][0]['s3']['configurationId']
+ key_name = event['Records'][0]['s3']['object']['key']
+ awsRegion = event['Records'][0]['awsRegion']
+ assert_equal(awsRegion, zonegroup)
+ bucket_arn = event['Records'][0]['s3']['bucket']['arn']
+ assert_equal(bucket_arn, "arn:aws:s3:"+awsRegion+"::"+bucket_name)
+ if notif_id == notification_name+'_1':
+ found_in1.append(key_name)
+ elif notif_id == notification_name+'_2':
+ found_in2.append(key_name)
+ elif notif_id == notification_name+'_3':
+ found_in3.append(key_name)
+ elif not skip_notif4 and notif_id == notification_name+'_4':
+ found_in4.append(key_name)
+ else:
+ assert False, 'invalid notification: ' + notif_id
+
+ assert_equal(set(found_in1), set(expected_in1))
+ assert_equal(set(found_in2), set(expected_in2))
+ assert_equal(set(found_in3), set(expected_in3))
+ if not skip_notif4:
+ assert_equal(set(found_in4), set(expected_in4))
+
+ # cleanup
+ s3_notification_conf.del_config()
+ if not skip_notif4:
+ s3_notification_conf4.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ for key in bucket.list():
+ key.delete()
+ conn.delete_bucket(bucket_name)
+ stop_amqp_receiver(receiver, task)
+
+
+@attr('basic_test')
+def test_ps_s3_notification_errors_on_master():
+ """ test s3 notification set/get/delete on master """
+ conn = connection()
+ zonegroup = 'default'
+ bucket_name = gen_bucket_name()
+ # create bucket
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+ # create s3 topic
+ endpoint_address = 'amqp://127.0.0.1:7001'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+
+ # create s3 notification with invalid event name
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:Kaboom']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ try:
+ result, status = s3_notification_conf.set_config()
+ except Exception as error:
+ print(str(error) + ' - is expected')
+ else:
+ assert False, 'invalid event name is expected to fail'
+
+ # create s3 notification with missing name
+ topic_conf_list = [{'Id': '',
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:Put']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ try:
+ _, _ = s3_notification_conf.set_config()
+ except Exception as error:
+ print(str(error) + ' - is expected')
+ else:
+ assert False, 'missing notification name is expected to fail'
+
+ # create s3 notification with invalid topic ARN
+ invalid_topic_arn = 'kaboom'
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': invalid_topic_arn,
+ 'Events': ['s3:ObjectCreated:Put']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ try:
+ _, _ = s3_notification_conf.set_config()
+ except Exception as error:
+ print(str(error) + ' - is expected')
+ else:
+ assert False, 'invalid ARN is expected to fail'
+
+ # create s3 notification with unknown topic ARN
+ invalid_topic_arn = 'arn:aws:sns:a::kaboom'
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': invalid_topic_arn ,
+ 'Events': ['s3:ObjectCreated:Put']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ try:
+ _, _ = s3_notification_conf.set_config()
+ except Exception as error:
+ print(str(error) + ' - is expected')
+ else:
+ assert False, 'unknown topic is expected to fail'
+
+ # create s3 notification with wrong bucket
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:Put']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, 'kaboom', topic_conf_list)
+ try:
+ _, _ = s3_notification_conf.set_config()
+ except Exception as error:
+ print(str(error) + ' - is expected')
+ else:
+ assert False, 'unknown bucket is expected to fail'
+
+ topic_conf.del_config()
+
+ status = topic_conf.del_config()
+ # deleting an unknown notification is not considered an error
+ assert_equal(status, 200)
+
+ _, status = topic_conf.get_config()
+ assert_equal(status, 404)
+
+ # cleanup
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+@attr('basic_test')
+def test_ps_s3_notification_permissions():
+ """ test s3 notification set/get/delete permissions """
+ conn1 = connection()
+ conn2 = another_user()
+ zonegroup = 'default'
+ bucket_name = gen_bucket_name()
+ # create bucket
+ bucket = conn1.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+ # create s3 topic
+ endpoint_address = 'amqp://127.0.0.1:7001'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=amqp.direct&amqp-ack-level=none'
+ topic_conf = PSTopicS3(conn1, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+
+ # one user create a notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf1 = PSNotificationS3(conn1, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf1.set_config()
+ assert_equal(status, 200)
+ # another user try to fetch it
+ s3_notification_conf2 = PSNotificationS3(conn2, bucket_name, topic_conf_list)
+ try:
+ _, _ = s3_notification_conf2.get_config()
+ assert False, "'AccessDenied' error is expected"
+ except ClientError as error:
+ assert_equal(error.response['Error']['Code'], 'AccessDenied')
+ # other user try to delete the notification
+ _, status = s3_notification_conf2.del_config()
+ assert_equal(status, 403)
+
+ # bucket policy is added by the 1st user
+ client = boto3.client('s3',
+ endpoint_url='http://'+conn1.host+':'+str(conn1.port),
+ aws_access_key_id=conn1.aws_access_key_id,
+ aws_secret_access_key=conn1.aws_secret_access_key)
+ bucket_policy = json.dumps({
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "Statement",
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": ["s3:GetBucketNotification", "s3:PutBucketNotification"],
+ "Resource": f"arn:aws:s3:::{bucket_name}"
+ }
+ ]
+ })
+ response = client.put_bucket_policy(Bucket=bucket_name, Policy=bucket_policy)
+ assert_equal(int(response['ResponseMetadata']['HTTPStatusCode']/100), 2)
+ result = client.get_bucket_policy(Bucket=bucket_name)
+ print(result['Policy'])
+
+ # 2nd user try to fetch it again
+ _, status = s3_notification_conf2.get_config()
+ assert_equal(status, 200)
+
+ # 2nd user try to delete it again
+ result, status = s3_notification_conf2.del_config()
+ assert_equal(status, 200)
+
+ # 2nd user try to add another notification
+ topic_conf_list = [{'Id': notification_name+"2",
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf2 = PSNotificationS3(conn2, bucket_name, topic_conf_list)
+ result, status = s3_notification_conf2.set_config()
+ assert_equal(status, 200)
+
+ # cleanup
+ s3_notification_conf1.del_config()
+ s3_notification_conf2.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn1.delete_bucket(bucket_name)
+
+@attr('amqp_test')
+def test_ps_s3_notification_push_amqp_on_master():
+ """ test pushing amqp s3 notification on master """
+
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name1 = bucket_name + TOPIC_SUFFIX + '_1'
+ topic_name2 = bucket_name + TOPIC_SUFFIX + '_2'
+
+ # start amqp receivers
+ exchange = 'ex1'
+ task1, receiver1 = create_amqp_receiver_thread(exchange, topic_name1)
+ task2, receiver2 = create_amqp_receiver_thread(exchange, topic_name2)
+ task1.start()
+ task2.start()
+
+ # create two s3 topic
+ endpoint_address = 'amqp://' + hostname
+ # with acks from broker
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=broker'
+ topic_conf1 = PSTopicS3(conn, topic_name1, zonegroup, endpoint_args=endpoint_args)
+ topic_arn1 = topic_conf1.set_config()
+ # without acks from broker
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=routable'
+ topic_conf2 = PSTopicS3(conn, topic_name2, zonegroup, endpoint_args=endpoint_args)
+ topic_arn2 = topic_conf2.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1', 'TopicArn': topic_arn1,
+ 'Events': []
+ },
+ {'Id': notification_name+'_2', 'TopicArn': topic_arn2,
+ 'Events': ['s3:ObjectCreated:*']
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket (async)
+ number_of_objects = 100
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + qmqp notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver
+ keys = list(bucket.list())
+ print('total number of objects: ' + str(len(keys)))
+ receiver1.verify_s3_events(keys, exact_match=True)
+ receiver2.verify_s3_events(keys, exact_match=True)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + amqp notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver 1 for deletions
+ receiver1.verify_s3_events(keys, exact_match=True, deletions=True)
+ # check amqp receiver 2 has no deletions
+ try:
+ receiver1.verify_s3_events(keys, exact_match=False, deletions=True)
+ except:
+ pass
+ else:
+ err = 'amqp receiver 2 should have no deletions'
+ assert False, err
+
+ # cleanup
+ stop_amqp_receiver(receiver1, task1)
+ stop_amqp_receiver(receiver2, task2)
+ s3_notification_conf.del_config()
+ topic_conf1.del_config()
+ topic_conf2.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+
+@attr('manual_test')
+def test_ps_s3_notification_push_amqp_idleness_check():
+ """ test pushing amqp s3 notification and checking for connection idleness """
+ return SkipTest("only used in manual testing")
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name1 = bucket_name + TOPIC_SUFFIX + '_1'
+
+ # start amqp receivers
+ exchange = 'ex1'
+ task1, receiver1 = create_amqp_receiver_thread(exchange, topic_name1)
+ task1.start()
+
+ # create two s3 topic
+ endpoint_address = 'amqp://' + hostname
+ # with acks from broker
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=broker'
+ topic_conf1 = PSTopicS3(conn, topic_name1, zonegroup, endpoint_args=endpoint_args)
+ topic_arn1 = topic_conf1.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1', 'TopicArn': topic_arn1,
+ 'Events': []
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket (async)
+ number_of_objects = 10
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + amqp notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver
+ keys = list(bucket.list())
+ print('total number of objects: ' + str(len(keys)))
+ receiver1.verify_s3_events(keys, exact_match=True)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + amqp notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver 1 for deletions
+ receiver1.verify_s3_events(keys, exact_match=True, deletions=True)
+
+ print('waiting for 40sec for checking idleness')
+ time.sleep(40)
+
+ os.system("netstat -nnp | grep 5672");
+
+ # do the process of uploading an object and checking for notification again
+ number_of_objects = 10
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + amqp notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver
+ keys = list(bucket.list())
+ print('total number of objects: ' + str(len(keys)))
+ receiver1.verify_s3_events(keys, exact_match=True)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + amqp notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver 1 for deletions
+ receiver1.verify_s3_events(keys, exact_match=True, deletions=True)
+
+ os.system("netstat -nnp | grep 5672");
+
+ # cleanup
+ stop_amqp_receiver(receiver1, task1)
+ s3_notification_conf.del_config()
+ topic_conf1.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+
+@attr('kafka_test')
+def test_ps_s3_notification_push_kafka_on_master():
+ """ test pushing kafka s3 notification on master """
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ # name is constant for manual testing
+ topic_name = bucket_name+'_topic'
+ # create consumer on the topic
+
+ try:
+ s3_notification_conf = None
+ topic_conf1 = None
+ topic_conf2 = None
+ receiver = None
+ task, receiver = create_kafka_receiver_thread(topic_name+'_1')
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'kafka://' + kafka_server
+ # without acks from broker
+ endpoint_args = 'push-endpoint='+endpoint_address+'&kafka-ack-level=broker'
+ topic_conf1 = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ topic_arn1 = topic_conf1.set_config()
+ endpoint_args = 'push-endpoint='+endpoint_address+'&kafka-ack-level=none'
+ topic_conf2 = PSTopicS3(conn, topic_name+'_2', zonegroup, endpoint_args=endpoint_args)
+ topic_arn2 = topic_conf2.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name + '_1', 'TopicArn': topic_arn1,
+ 'Events': []
+ },
+ {'Id': notification_name + '_2', 'TopicArn': topic_arn2,
+ 'Events': []
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket (async)
+ number_of_objects = 10
+ client_threads = []
+ etags = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ etag = hashlib.md5(content.encode()).hexdigest()
+ etags.append(etag)
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ keys = list(bucket.list())
+ receiver.verify_s3_events(keys, exact_match=True, etags=etags)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ receiver.verify_s3_events(keys, exact_match=True, deletions=True, etags=etags)
+ except Exception as e:
+ print(e)
+ assert False
+ finally:
+ # cleanup
+ if s3_notification_conf is not None:
+ s3_notification_conf.del_config()
+ if topic_conf1 is not None:
+ topic_conf1.del_config()
+ if topic_conf2 is not None:
+ topic_conf2.del_config()
+ # delete the bucket
+ for key in bucket.list():
+ key.delete()
+ conn.delete_bucket(bucket_name)
+ if receiver is not None:
+ stop_kafka_receiver(receiver, task)
+
+
+@attr('http_test')
+def test_ps_s3_notification_multi_delete_on_master():
+ """ test deletion of multiple keys on master """
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectRemoved:*']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ client_threads = []
+ objects_size = {}
+ for i in range(number_of_objects):
+ content = str(os.urandom(randint(1, 1024)))
+ object_size = len(content)
+ key = bucket.new_key(str(i))
+ objects_size[key.name] = object_size
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ keys = list(bucket.list())
+
+ start_time = time.time()
+ delete_all_objects(conn, bucket_name)
+ time_diff = time.time() - start_time
+ print('average time for deletion + http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check http receiver
+ http_server.verify_s3_events(keys, exact_match=True, deletions=True, expected_sizes=objects_size)
+
+ # cleanup
+ topic_conf.del_config()
+ s3_notification_conf.del_config(notification=notification_name)
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+
+@attr('http_test')
+def test_ps_s3_notification_push_http_on_master():
+ """ test pushing http s3 notification on master """
+ hostname = get_ip_http()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ client_threads = []
+ objects_size = {}
+ start_time = time.time()
+ for i in range(number_of_objects):
+ content = str(os.urandom(randint(1, 1024)))
+ object_size = len(content)
+ key = bucket.new_key(str(i))
+ objects_size[key.name] = object_size
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check http receiver
+ keys = list(bucket.list())
+ http_server.verify_s3_events(keys, exact_match=True, deletions=False, expected_sizes=objects_size)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check http receiver
+ http_server.verify_s3_events(keys, exact_match=True, deletions=True, expected_sizes=objects_size)
+
+ # cleanup
+ topic_conf.del_config()
+ s3_notification_conf.del_config(notification=notification_name)
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+
+@attr('http_test')
+def test_ps_s3_notification_push_cloudevents_on_master():
+ """ test pushing cloudevents notification on master """
+ hostname = get_ip_http()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects, cloudevents=True)
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&cloudevents=true'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ client_threads = []
+ objects_size = {}
+ start_time = time.time()
+ for i in range(number_of_objects):
+ content = str(os.urandom(randint(1, 1024)))
+ object_size = len(content)
+ key = bucket.new_key(str(i))
+ objects_size[key.name] = object_size
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check http receiver
+ keys = list(bucket.list())
+ http_server.verify_s3_events(keys, exact_match=True, deletions=False, expected_sizes=objects_size)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check http receiver
+ http_server.verify_s3_events(keys, exact_match=True, deletions=True, expected_sizes=objects_size)
+
+ # cleanup
+ topic_conf.del_config()
+ s3_notification_conf.del_config(notification=notification_name)
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+
+@attr('http_test')
+def test_ps_s3_opaque_data_on_master():
+ """ test that opaque id set in topic, is sent in notification on master """
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address
+ opaque_data = 'http://1.2.3.4:8888'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args, opaque_data=opaque_data)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ client_threads = []
+ start_time = time.time()
+ content = 'bar'
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check http receiver
+ keys = list(bucket.list())
+ print('total number of objects: ' + str(len(keys)))
+ events = http_server.get_and_reset_events()
+ for event in events:
+ assert_equal(event['Records'][0]['opaqueData'], opaque_data)
+
+ # cleanup
+ for key in keys:
+ key.delete()
+ [thr.join() for thr in client_threads]
+ topic_conf.del_config()
+ s3_notification_conf.del_config(notification=notification_name)
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+@attr('http_test')
+def test_ps_s3_lifecycle_on_master():
+ """ test that when object is deleted due to lifecycle policy, notification is sent on master """
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address
+ opaque_data = 'http://1.2.3.4:8888'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args, opaque_data=opaque_data)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectLifecycle:Expiration:*']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ obj_prefix = 'ooo'
+ client_threads = []
+ start_time = time.time()
+ content = 'bar'
+ for i in range(number_of_objects):
+ key = bucket.new_key(obj_prefix + str(i))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ # create lifecycle policy
+ client = boto3.client('s3',
+ endpoint_url='http://'+conn.host+':'+str(conn.port),
+ aws_access_key_id=conn.aws_access_key_id,
+ aws_secret_access_key=conn.aws_secret_access_key)
+ yesterday = datetime.date.today() - datetime.timedelta(days=1)
+ response = client.put_bucket_lifecycle_configuration(Bucket=bucket_name,
+ LifecycleConfiguration={'Rules': [
+ {
+ 'ID': 'rule1',
+ 'Expiration': {'Date': yesterday.isoformat()},
+ 'Filter': {'Prefix': obj_prefix},
+ 'Status': 'Enabled',
+ }
+ ]
+ }
+ )
+
+ # start lifecycle processing
+ admin(['lc', 'process'])
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check http receiver does not have messages
+ keys = list(bucket.list())
+ print('total number of objects: ' + str(len(keys)))
+ event_keys = []
+ events = http_server.get_and_reset_events()
+ for event in events:
+ assert_equal(event['Records'][0]['eventName'], 'ObjectLifecycle:Expiration:Current')
+ event_keys.append(event['Records'][0]['s3']['object']['key'])
+ for key in keys:
+ key_found = False
+ for event_key in event_keys:
+ if event_key == key:
+ key_found = True
+ break
+ if not key_found:
+ err = 'no lifecycle event found for key: ' + str(key)
+ log.error(events)
+ assert False, err
+
+ # cleanup
+ for key in keys:
+ key.delete()
+ [thr.join() for thr in client_threads]
+ topic_conf.del_config()
+ s3_notification_conf.del_config(notification=notification_name)
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+
+def ps_s3_creation_triggers_on_master(external_endpoint_address=None, ca_location=None, verify_ssl='true'):
+ """ test object creation s3 notifications in using put/copy/post on master"""
+
+ if not external_endpoint_address:
+ hostname = 'localhost'
+ proc = init_rabbitmq()
+ if proc is None:
+ return SkipTest('end2end amqp tests require rabbitmq-server installed')
+ else:
+ proc = None
+
+ conn = connection()
+ hostname = 'localhost'
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receiver
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name, external_endpoint_address, ca_location)
+ task.start()
+
+ # create s3 topic
+ if external_endpoint_address:
+ endpoint_address = external_endpoint_address
+ elif ca_location:
+ endpoint_address = 'amqps://' + hostname
+ else:
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=broker&verify-ssl='+verify_ssl
+ if ca_location:
+ endpoint_args += '&ca-location={}'.format(ca_location)
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:Put', 's3:ObjectCreated:Copy', 's3:ObjectCreated:CompleteMultipartUpload']
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ objects_size = {}
+ # create objects in the bucket using PUT
+ content = str(os.urandom(randint(1, 1024)))
+ key_name = 'put'
+ key = bucket.new_key(key_name)
+ objects_size[key_name] = len(content)
+ key.set_contents_from_string(content)
+ # create objects in the bucket using COPY
+ key_name = 'copy'
+ bucket.copy_key(key_name, bucket.name, key.name)
+ objects_size[key_name] = len(content)
+
+ # create objects in the bucket using multi-part upload
+ fp = tempfile.NamedTemporaryFile(mode='w+b')
+ content = bytearray(os.urandom(10*1024*1024))
+ key_name = 'multipart'
+ objects_size[key_name] = len(content)
+ fp.write(content)
+ fp.flush()
+ fp.seek(0)
+ uploader = bucket.initiate_multipart_upload(key_name)
+ uploader.upload_part_from_file(fp, 1)
+ uploader.complete_upload()
+ fp.close()
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver
+ keys = list(bucket.list())
+ receiver.verify_s3_events(keys, exact_match=True, expected_sizes=objects_size)
+
+ # cleanup
+ stop_amqp_receiver(receiver, task)
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ for key in bucket.list():
+ key.delete()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ if proc:
+ clean_rabbitmq(proc)
+
+
+@attr('amqp_test')
+def test_ps_s3_creation_triggers_on_master():
+ ps_s3_creation_triggers_on_master(external_endpoint_address="amqp://localhost:5672")
+
+
+@attr('amqp_ssl_test')
+def test_ps_s3_creation_triggers_on_master_external():
+
+ from distutils.util import strtobool
+
+ if 'AMQP_EXTERNAL_ENDPOINT' in os.environ:
+ try:
+ if strtobool(os.environ['AMQP_VERIFY_SSL']):
+ verify_ssl = 'true'
+ else:
+ verify_ssl = 'false'
+ except Exception as e:
+ verify_ssl = 'true'
+
+ ps_s3_creation_triggers_on_master(
+ external_endpoint_address=os.environ['AMQP_EXTERNAL_ENDPOINT'],
+ verify_ssl=verify_ssl)
+ else:
+ return SkipTest("Set AMQP_EXTERNAL_ENDPOINT to a valid external AMQP endpoint url for this test to run")
+
+
+def generate_private_key(tempdir):
+
+ import datetime
+ import stat
+ from cryptography import x509
+ from cryptography.x509.oid import NameOID
+ from cryptography.hazmat.primitives import hashes
+ from cryptography.hazmat.backends import default_backend
+ from cryptography.hazmat.primitives import serialization
+ from cryptography.hazmat.primitives.asymmetric import rsa
+
+ # modify permissions to ensure that the broker user can access them
+ os.chmod(tempdir, mode=stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
+ CACERTFILE = os.path.join(tempdir, 'ca_certificate.pem')
+ CERTFILE = os.path.join(tempdir, 'server_certificate.pem')
+ KEYFILE = os.path.join(tempdir, 'server_key.pem')
+
+ root_key = rsa.generate_private_key(
+ public_exponent=65537,
+ key_size=2048,
+ backend=default_backend()
+ )
+ subject = issuer = x509.Name([
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u"UK"),
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Oxfordshire"),
+ x509.NameAttribute(NameOID.LOCALITY_NAME, u"Harwell"),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"Rosalind Franklin Institute"),
+ x509.NameAttribute(NameOID.COMMON_NAME, u"RFI CA"),
+ ])
+ root_cert = x509.CertificateBuilder().subject_name(
+ subject
+ ).issuer_name(
+ issuer
+ ).public_key(
+ root_key.public_key()
+ ).serial_number(
+ x509.random_serial_number()
+ ).not_valid_before(
+ datetime.datetime.utcnow()
+ ).not_valid_after(
+ datetime.datetime.utcnow() + datetime.timedelta(days=3650)
+ ).add_extension(
+ x509.BasicConstraints(ca=True, path_length=None), critical=True
+ ).sign(root_key, hashes.SHA256(), default_backend())
+ with open(CACERTFILE, "wb") as f:
+ f.write(root_cert.public_bytes(serialization.Encoding.PEM))
+
+ # Now we want to generate a cert from that root
+ cert_key = rsa.generate_private_key(
+ public_exponent=65537,
+ key_size=2048,
+ backend=default_backend(),
+ )
+ with open(KEYFILE, "wb") as f:
+ f.write(cert_key.private_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PrivateFormat.PKCS8,
+ encryption_algorithm=serialization.NoEncryption(),
+ ))
+ new_subject = x509.Name([
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u"UK"),
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Oxfordshire"),
+ x509.NameAttribute(NameOID.LOCALITY_NAME, u"Harwell"),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"Rosalind Franklin Institute"),
+ ])
+ cert = x509.CertificateBuilder().subject_name(
+ new_subject
+ ).issuer_name(
+ root_cert.issuer
+ ).public_key(
+ cert_key.public_key()
+ ).serial_number(
+ x509.random_serial_number()
+ ).not_valid_before(
+ datetime.datetime.utcnow()
+ ).not_valid_after(
+ datetime.datetime.utcnow() + datetime.timedelta(days=30)
+ ).add_extension(
+ x509.SubjectAlternativeName([x509.DNSName(u"localhost")]),
+ critical=False,
+ ).sign(root_key, hashes.SHA256(), default_backend())
+ # Write our certificate out to disk.
+ with open(CERTFILE, "wb") as f:
+ f.write(cert.public_bytes(serialization.Encoding.PEM))
+
+ print("\n\n********private key generated********")
+ print(CACERTFILE, CERTFILE, KEYFILE)
+ print("\n\n")
+ return CACERTFILE, CERTFILE, KEYFILE
+
+
+@attr('amqp_ssl_test')
+def test_ps_s3_creation_triggers_on_master_ssl():
+
+ import textwrap
+ from tempfile import TemporaryDirectory
+
+ with TemporaryDirectory() as tempdir:
+ CACERTFILE, CERTFILE, KEYFILE = generate_private_key(tempdir)
+ RABBITMQ_CONF_FILE = os.path.join(tempdir, 'rabbitmq.config')
+ with open(RABBITMQ_CONF_FILE, "w") as f:
+ # use the old style config format to ensure it also runs on older RabbitMQ versions.
+ f.write(textwrap.dedent(f'''
+ [
+ {{rabbit, [
+ {{ssl_listeners, [5671]}},
+ {{ssl_options, [{{cacertfile, "{CACERTFILE}"}},
+ {{certfile, "{CERTFILE}"}},
+ {{keyfile, "{KEYFILE}"}},
+ {{verify, verify_peer}},
+ {{fail_if_no_peer_cert, false}}]}}]}}
+ ].
+ '''))
+ os.environ['RABBITMQ_CONFIG_FILE'] = os.path.splitext(RABBITMQ_CONF_FILE)[0]
+
+ ps_s3_creation_triggers_on_master(ca_location=CACERTFILE)
+
+ del os.environ['RABBITMQ_CONFIG_FILE']
+
+
+@attr('amqp_test')
+def test_http_post_object_upload():
+ """ test that uploads object using HTTP POST """
+
+ import boto3
+ from collections import OrderedDict
+ import requests
+
+ hostname = get_ip()
+ zonegroup = 'default'
+ conn = connection()
+
+ endpoint = "http://%s:%d" % (get_config_host(), get_config_port())
+
+ conn1 = boto3.client(service_name='s3',
+ aws_access_key_id=get_access_key(),
+ aws_secret_access_key=get_secret_key(),
+ endpoint_url=endpoint,
+ )
+
+ bucket_name = gen_bucket_name()
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ key_name = 'foo.txt'
+
+ resp = conn1.generate_presigned_post(Bucket=bucket_name, Key=key_name,)
+
+ url = resp['url']
+
+ bucket = conn1.create_bucket(ACL='public-read-write', Bucket=bucket_name)
+
+ # start amqp receivers
+ exchange = 'ex1'
+ task1, receiver1 = create_amqp_receiver_thread(exchange, topic_name+'_1')
+ task1.start()
+
+ # create s3 topics
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint=' + endpoint_address + '&amqp-exchange=' + exchange + '&amqp-ack-level=broker'
+ topic_conf1 = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ topic_arn1 = topic_conf1.set_config()
+
+ # create s3 notifications
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1', 'TopicArn': topic_arn1,
+ 'Events': ['s3:ObjectCreated:Post']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ payload = OrderedDict([("key" , "foo.txt"),("acl" , "public-read"),\
+ ("Content-Type" , "text/plain"),('file', ('bar'))])
+
+ # POST upload
+ r = requests.post(url, files=payload, verify=True)
+ assert_equal(r.status_code, 204)
+
+ # check amqp receiver
+ events = receiver1.get_and_reset_events()
+ assert_equal(len(events), 1)
+
+ # cleanup
+ stop_amqp_receiver(receiver1, task1)
+ s3_notification_conf.del_config()
+ topic_conf1.del_config()
+ conn1.delete_object(Bucket=bucket_name, Key=key_name)
+ # delete the bucket
+ conn1.delete_bucket(Bucket=bucket_name)
+
+
+@attr('amqp_test')
+def test_ps_s3_multipart_on_master():
+ """ test multipart object upload on master"""
+
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receivers
+ exchange = 'ex1'
+ task1, receiver1 = create_amqp_receiver_thread(exchange, topic_name+'_1')
+ task1.start()
+ task2, receiver2 = create_amqp_receiver_thread(exchange, topic_name+'_2')
+ task2.start()
+ task3, receiver3 = create_amqp_receiver_thread(exchange, topic_name+'_3')
+ task3.start()
+
+ # create s3 topics
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint=' + endpoint_address + '&amqp-exchange=' + exchange + '&amqp-ack-level=broker'
+ topic_conf1 = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ topic_arn1 = topic_conf1.set_config()
+ topic_conf2 = PSTopicS3(conn, topic_name+'_2', zonegroup, endpoint_args=endpoint_args)
+ topic_arn2 = topic_conf2.set_config()
+ topic_conf3 = PSTopicS3(conn, topic_name+'_3', zonegroup, endpoint_args=endpoint_args)
+ topic_arn3 = topic_conf3.set_config()
+
+ # create s3 notifications
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1', 'TopicArn': topic_arn1,
+ 'Events': ['s3:ObjectCreated:*']
+ },
+ {'Id': notification_name+'_2', 'TopicArn': topic_arn2,
+ 'Events': ['s3:ObjectCreated:Post']
+ },
+ {'Id': notification_name+'_3', 'TopicArn': topic_arn3,
+ 'Events': ['s3:ObjectCreated:CompleteMultipartUpload']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket using multi-part upload
+ fp = tempfile.NamedTemporaryFile(mode='w+b')
+ object_size = 1024
+ content = bytearray(os.urandom(object_size))
+ fp.write(content)
+ fp.flush()
+ fp.seek(0)
+ uploader = bucket.initiate_multipart_upload('multipart')
+ uploader.upload_part_from_file(fp, 1)
+ uploader.complete_upload()
+ fp.close()
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver
+ events = receiver1.get_and_reset_events()
+ assert_equal(len(events), 1)
+
+ events = receiver2.get_and_reset_events()
+ assert_equal(len(events), 0)
+
+ events = receiver3.get_and_reset_events()
+ assert_equal(len(events), 1)
+ assert_equal(events[0]['Records'][0]['eventName'], 'ObjectCreated:CompleteMultipartUpload')
+ assert_equal(events[0]['Records'][0]['s3']['configurationId'], notification_name+'_3')
+ assert_equal(events[0]['Records'][0]['s3']['object']['size'], object_size)
+ assert events[0]['Records'][0]['eventTime'] != '0.000000', 'invalid eventTime'
+
+ # cleanup
+ stop_amqp_receiver(receiver1, task1)
+ stop_amqp_receiver(receiver2, task2)
+ stop_amqp_receiver(receiver3, task3)
+ s3_notification_conf.del_config()
+ topic_conf1.del_config()
+ topic_conf2.del_config()
+ topic_conf3.del_config()
+ for key in bucket.list():
+ key.delete()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+@attr('amqp_test')
+def test_ps_s3_metadata_filter_on_master():
+ """ test s3 notification of metadata on master """
+
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receivers
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=routable'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ meta_key = 'meta1'
+ meta_value = 'This is my metadata value'
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*', 's3:ObjectRemoved:*'],
+ 'Filter': {
+ 'Metadata': {
+ 'FilterRules': [{'Name': META_PREFIX+meta_key, 'Value': meta_value}]
+ }
+ }
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ expected_keys = []
+ # create objects in the bucket
+ key_name = 'foo'
+ key = bucket.new_key(key_name)
+ key.set_metadata(meta_key, meta_value)
+ key.set_contents_from_string('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
+ expected_keys.append(key_name)
+
+ # create objects in the bucket using COPY
+ key_name = 'copy_of_foo'
+ bucket.copy_key(key_name, bucket.name, key.name)
+ expected_keys.append(key_name)
+
+ # create another objects in the bucket using COPY
+ # but override the metadata value
+ key_name = 'another_copy_of_foo'
+ bucket.copy_key(key_name, bucket.name, key.name, metadata={meta_key: 'kaboom'})
+ # this key is not in the expected keys due to the different meta value
+
+ # create objects in the bucket using multi-part upload
+ fp = tempfile.NamedTemporaryFile(mode='w+b')
+ chunk_size = 1024*1024*5 # 5MB
+ object_size = 10*chunk_size
+ content = bytearray(os.urandom(object_size))
+ fp.write(content)
+ fp.flush()
+ fp.seek(0)
+ key_name = 'multipart_foo'
+ uploader = bucket.initiate_multipart_upload(key_name,
+ metadata={meta_key: meta_value})
+ for i in range(1,5):
+ uploader.upload_part_from_file(fp, i, size=chunk_size)
+ fp.seek(i*chunk_size)
+ uploader.complete_upload()
+ fp.close()
+ expected_keys.append(key_name)
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ # check amqp receiver
+ events = receiver.get_and_reset_events()
+ assert_equal(len(events), len(expected_keys))
+ for event in events:
+ assert(event['Records'][0]['s3']['object']['key'] in expected_keys)
+
+ # delete objects
+ for key in bucket.list():
+ key.delete()
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ # check amqp receiver
+ events = receiver.get_and_reset_events()
+ assert_equal(len(events), len(expected_keys))
+ for event in events:
+ assert(event['Records'][0]['s3']['object']['key'] in expected_keys)
+
+ # cleanup
+ stop_amqp_receiver(receiver, task)
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+
+@attr('amqp_test')
+def test_ps_s3_metadata_on_master():
+ """ test s3 notification of metadata on master """
+
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receivers
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=routable'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ meta_key = 'meta1'
+ meta_value = 'This is my metadata value'
+ meta_prefix = META_PREFIX
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*', 's3:ObjectRemoved:*'],
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ key_name = 'foo'
+ key = bucket.new_key(key_name)
+ key.set_metadata(meta_key, meta_value)
+ key.set_contents_from_string('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
+ # update the object
+ another_meta_key = 'meta2'
+ key.set_metadata(another_meta_key, meta_value)
+ key.set_contents_from_string('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')
+
+ # create objects in the bucket using COPY
+ key_name = 'copy_of_foo'
+ bucket.copy_key(key_name, bucket.name, key.name)
+
+ # create objects in the bucket using multi-part upload
+ fp = tempfile.NamedTemporaryFile(mode='w+b')
+ chunk_size = 1024*1024*5 # 5MB
+ object_size = 10*chunk_size
+ content = bytearray(os.urandom(object_size))
+ fp.write(content)
+ fp.flush()
+ fp.seek(0)
+ key_name = 'multipart_foo'
+ uploader = bucket.initiate_multipart_upload(key_name,
+ metadata={meta_key: meta_value})
+ for i in range(1,5):
+ uploader.upload_part_from_file(fp, i, size=chunk_size)
+ fp.seek(i*chunk_size)
+ uploader.complete_upload()
+ fp.close()
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ # check amqp receiver
+ events = receiver.get_and_reset_events()
+ for event in events:
+ value = [x['val'] for x in event['Records'][0]['s3']['object']['metadata'] if x['key'] == META_PREFIX+meta_key]
+ assert_equal(value[0], meta_value)
+
+ # delete objects
+ for key in bucket.list():
+ key.delete()
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ # check amqp receiver
+ events = receiver.get_and_reset_events()
+ for event in events:
+ value = [x['val'] for x in event['Records'][0]['s3']['object']['metadata'] if x['key'] == META_PREFIX+meta_key]
+ assert_equal(value[0], meta_value)
+
+ # cleanup
+ stop_amqp_receiver(receiver, task)
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+
+@attr('amqp_test')
+def test_ps_s3_tags_on_master():
+ """ test s3 notification of tags on master """
+
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receiver
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=routable'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*', 's3:ObjectRemoved:*'],
+ 'Filter': {
+ 'Tags': {
+ 'FilterRules': [{'Name': 'hello', 'Value': 'world'}, {'Name': 'ka', 'Value': 'boom'}]
+ }
+ }
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ expected_keys = []
+ # create objects in the bucket with tags
+ # key 1 has all the tags in the filter
+ tags = 'hello=world&ka=boom&hello=helloworld'
+ key_name1 = 'key1'
+ put_object_tagging(conn, bucket_name, key_name1, tags)
+ expected_keys.append(key_name1)
+ # key 2 has an additional tag not in the filter
+ tags = 'hello=world&foo=bar&ka=boom&hello=helloworld'
+ key_name = 'key2'
+ put_object_tagging(conn, bucket_name, key_name, tags)
+ expected_keys.append(key_name)
+ # key 3 has no tags
+ key_name3 = 'key3'
+ key = bucket.new_key(key_name3)
+ key.set_contents_from_string('bar')
+ # key 4 has the wrong of the multi value tags
+ tags = 'hello=helloworld&ka=boom'
+ key_name = 'key4'
+ put_object_tagging(conn, bucket_name, key_name, tags)
+ # key 5 has the right of the multi value tags
+ tags = 'hello=world&ka=boom'
+ key_name = 'key5'
+ put_object_tagging(conn, bucket_name, key_name, tags)
+ expected_keys.append(key_name)
+ # key 6 is missing a tag
+ tags = 'hello=world'
+ key_name = 'key6'
+ put_object_tagging(conn, bucket_name, key_name, tags)
+ # create objects in the bucket using COPY
+ key_name = 'copy_of_'+key_name1
+ bucket.copy_key(key_name, bucket.name, key_name1)
+ expected_keys.append(key_name)
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ event_count = 0
+ expected_tags1 = [{'key': 'hello', 'val': 'world'}, {'key': 'hello', 'val': 'helloworld'}, {'key': 'ka', 'val': 'boom'}]
+ expected_tags1 = sorted(expected_tags1, key=lambda k: k['key']+k['val'])
+ for event in receiver.get_and_reset_events():
+ key = event['Records'][0]['s3']['object']['key']
+ if (key == key_name1):
+ obj_tags = sorted(event['Records'][0]['s3']['object']['tags'], key=lambda k: k['key']+k['val'])
+ assert_equal(obj_tags, expected_tags1)
+ event_count += 1
+ assert(key in expected_keys)
+
+ assert_equal(event_count, len(expected_keys))
+
+ # delete the objects
+ for key in bucket.list():
+ key.delete()
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ event_count = 0
+ # check amqp receiver
+ for event in receiver.get_and_reset_events():
+ key = event['Records'][0]['s3']['object']['key']
+ if (key == key_name1):
+ obj_tags = sorted(event['Records'][0]['s3']['object']['tags'], key=lambda k: k['key']+k['val'])
+ assert_equal(obj_tags, expected_tags1)
+ event_count += 1
+ assert(key in expected_keys)
+
+ assert(event_count == len(expected_keys))
+
+ # cleanup
+ stop_amqp_receiver(receiver, task)
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+@attr('amqp_test')
+def test_ps_s3_versioning_on_master():
+ """ test s3 notification of object versions """
+
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ bucket.configure_versioning(True)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receiver
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=broker'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ key_name = 'foo'
+ key = bucket.new_key(key_name)
+ key.set_contents_from_string('hello')
+ ver1 = key.version_id
+ key.set_contents_from_string('world')
+ ver2 = key.version_id
+ copy_of_key = bucket.copy_key('copy_of_foo', bucket.name, key_name, src_version_id=ver1)
+ ver3 = copy_of_key.version_id
+ versions = [ver1, ver2, ver3]
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver
+ events = receiver.get_and_reset_events()
+ num_of_versions = 0
+ for event_list in events:
+ for event in event_list['Records']:
+ assert event['s3']['object']['key'] in (key_name, copy_of_key.name)
+ version = event['s3']['object']['versionId']
+ num_of_versions += 1
+ if version not in versions:
+ print('version mismatch: '+version+' not in: '+str(versions))
+ # TODO: copy_key() does not return the version of the copied object
+ #assert False
+ else:
+ print('version ok: '+version+' in: '+str(versions))
+
+ assert_equal(num_of_versions, 3)
+
+ # cleanup
+ stop_amqp_receiver(receiver, task)
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ bucket.delete_key(copy_of_key, version_id=ver3)
+ bucket.delete_key(key.name, version_id=ver2)
+ bucket.delete_key(key.name, version_id=ver1)
+ #conn.delete_bucket(bucket_name)
+
+
+@attr('amqp_test')
+def test_ps_s3_versioned_deletion_on_master():
+ """ test s3 notification of deletion markers on master """
+
+ hostname = get_ip()
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ bucket.configure_versioning(True)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # start amqp receiver
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'amqp://' + hostname
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange=' + exchange +'&amqp-ack-level=broker'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name+'_1', 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectRemoved:*']
+ },
+ {'Id': notification_name+'_2', 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectRemoved:DeleteMarkerCreated']
+ },
+ {'Id': notification_name+'_3', 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectRemoved:Delete']
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket
+ key = bucket.new_key('foo')
+ content = str(os.urandom(512))
+ size1 = len(content)
+ key.set_contents_from_string(content)
+ ver1 = key.version_id
+ content = str(os.urandom(511))
+ size2 = len(content)
+ key.set_contents_from_string(content)
+ ver2 = key.version_id
+ # create delete marker (non versioned deletion)
+ delete_marker_key = bucket.delete_key(key.name)
+ versions = [ver1, ver2, delete_marker_key.version_id]
+
+ time.sleep(1)
+
+ # versioned deletion
+ bucket.delete_key(key.name, version_id=ver2)
+ bucket.delete_key(key.name, version_id=ver1)
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+
+ # check amqp receiver
+ events = receiver.get_and_reset_events()
+ delete_events = 0
+ delete_marker_create_events = 0
+ for event_list in events:
+ for event in event_list['Records']:
+ version = event['s3']['object']['versionId']
+ size = event['s3']['object']['size']
+ if version not in versions:
+ print('version mismatch: '+version+' not in: '+str(versions))
+ assert False
+ else:
+ print('version ok: '+version+' in: '+str(versions))
+ if event['eventName'] == 'ObjectRemoved:Delete':
+ delete_events += 1
+ assert size in [size1, size2]
+ assert event['s3']['configurationId'] in [notification_name+'_1', notification_name+'_3']
+ if event['eventName'] == 'ObjectRemoved:DeleteMarkerCreated':
+ delete_marker_create_events += 1
+ assert size == size2
+ assert event['s3']['configurationId'] in [notification_name+'_1', notification_name+'_2']
+
+ # 2 key versions were deleted
+ # notified over the same topic via 2 notifications (1,3)
+ assert_equal(delete_events, 2*2)
+ # 1 deletion marker was created
+ # notified over the same topic over 2 notifications (1,2)
+ assert_equal(delete_marker_create_events, 1*2)
+
+ # cleanup
+ delete_marker_key.delete()
+ stop_amqp_receiver(receiver, task)
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+
+
+@attr('manual_test')
+def test_ps_s3_persistent_cleanup():
+ """ test reservation cleanup after gateway crash """
+ return SkipTest("only used in manual testing")
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 200
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+
+ gw = conn
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = gw.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true'
+ topic_conf = PSTopicS3(gw, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:Put']
+ }]
+ s3_notification_conf = PSNotificationS3(gw, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ # stop gateway while clients are sending
+ os.system("killall -9 radosgw");
+ print('wait for 10 sec for before restarting the gateway')
+ time.sleep(10)
+ # TODO: start the radosgw
+ [thr.join() for thr in client_threads]
+
+ keys = list(bucket.list())
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ # check http receiver
+ events = http_server.get_and_reset_events()
+
+ print(str(len(events) ) + " events found out of " + str(number_of_objects))
+
+ # make sure that things are working now
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ keys = list(bucket.list())
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ print('wait for 180 sec for reservations to be stale before queue deletion')
+ time.sleep(180)
+
+ # check http receiver
+ events = http_server.get_and_reset_events()
+
+ print(str(len(events)) + " events found out of " + str(number_of_objects))
+
+ # cleanup
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ gw.delete_bucket(bucket_name)
+ http_server.close()
+
+
+@attr('manual_test')
+def test_ps_s3_persistent_notification_pushback():
+ """ test pushing persistent notification pushback """
+ return SkipTest("only used in manual testing")
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ http_server = StreamingHTTPServer(host, port, num_workers=10, delay=0.5)
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create s3 topic
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true'
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket (async)
+ for j in range(100):
+ number_of_objects = randint(500, 1000)
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(j)+'-'+str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+ time_diff = time.time() - start_time
+ print('average time for creation + async http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ keys = list(bucket.list())
+
+ delay = 30
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ count = 0
+ for key in bucket.list():
+ count += 1
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ if count%100 == 0:
+ [thr.join() for thr in client_threads]
+ time_diff = time.time() - start_time
+ print('average time for deletion + async http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+ client_threads = []
+ start_time = time.time()
+
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ # cleanup
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ time.sleep(delay)
+ http_server.close()
+
+
+@attr('kafka_test')
+def test_ps_s3_notification_kafka_idle_behaviour():
+ """ test pushing kafka s3 notification idle behaviour check """
+ # TODO convert this test to actual running test by changing
+ # os.system call to verify the process idleness
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ # name is constant for manual testing
+ topic_name = bucket_name+'_topic'
+ # create consumer on the topic
+
+ task, receiver = create_kafka_receiver_thread(topic_name+'_1')
+ task.start()
+
+ # create s3 topic
+ endpoint_address = 'kafka://' + kafka_server
+ # with acks from broker
+ endpoint_args = 'push-endpoint='+endpoint_address+'&kafka-ack-level=broker'
+ topic_conf1 = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ topic_arn1 = topic_conf1.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name + '_1', 'TopicArn': topic_arn1,
+ 'Events': []
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket (async)
+ number_of_objects = 10
+ client_threads = []
+ etags = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ etag = hashlib.md5(content.encode()).hexdigest()
+ etags.append(etag)
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ keys = list(bucket.list())
+ receiver.verify_s3_events(keys, exact_match=True, etags=etags)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ receiver.verify_s3_events(keys, exact_match=True, deletions=True, etags=etags)
+
+ is_idle = False
+
+ while not is_idle:
+ print('waiting for 10sec for checking idleness')
+ time.sleep(10)
+ cmd = "netstat -nnp | grep 9092 | grep radosgw"
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
+ out = proc.communicate()[0]
+ if len(out) == 0:
+ is_idle = True
+ else:
+ print("radosgw<->kafka connection is not idle")
+ print(out.decode('utf-8'))
+
+ # do the process of uploading an object and checking for notification again
+ number_of_objects = 10
+ client_threads = []
+ etags = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ etag = hashlib.md5(content.encode()).hexdigest()
+ etags.append(etag)
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ keys = list(bucket.list())
+ receiver.verify_s3_events(keys, exact_match=True, etags=etags)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ receiver.verify_s3_events(keys, exact_match=True, deletions=True, etags=etags)
+
+ # cleanup
+ s3_notification_conf.del_config()
+ topic_conf1.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ stop_kafka_receiver(receiver, task)
+
+
+@attr('modification_required')
+def test_ps_s3_persistent_gateways_recovery():
+ """ test gateway recovery of persistent notifications """
+ return SkipTest('This test requires two gateways.')
+
+ conn = connection()
+ zonegroup = 'default'
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+ gw1 = conn
+ gw2 = connection2()
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = gw1.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+ # create two s3 topics
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true'
+ topic_conf1 = PSTopicS3(gw1, topic_name+'_1', zonegroup, endpoint_args=endpoint_args+'&OpaqueData=fromgw1')
+ topic_arn1 = topic_conf1.set_config()
+ topic_conf2 = PSTopicS3(gw2, topic_name+'_2', zonegroup, endpoint_args=endpoint_args+'&OpaqueData=fromgw2')
+ topic_arn2 = topic_conf2.set_config()
+ # create two s3 notifications
+ notification_name = bucket_name + NOTIFICATION_SUFFIX+'_1'
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn1,
+ 'Events': ['s3:ObjectCreated:Put']
+ }]
+ s3_notification_conf1 = PSNotificationS3(gw1, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf1.set_config()
+ assert_equal(status/100, 2)
+ notification_name = bucket_name + NOTIFICATION_SUFFIX+'_2'
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn2,
+ 'Events': ['s3:ObjectRemoved:Delete']
+ }]
+ s3_notification_conf2 = PSNotificationS3(gw2, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf2.set_config()
+ assert_equal(status/100, 2)
+ # stop gateway 2
+ print('stopping gateway2...')
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+ keys = list(bucket.list())
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+ print('wait for 60 sec for before restarting the gateway')
+ time.sleep(60)
+ # check http receiver
+ events = http_server.get_and_reset_events()
+ for key in keys:
+ creations = 0
+ deletions = 0
+ for event in events:
+ if event['Records'][0]['eventName'] == 'ObjectCreated:Put' and \
+ key.name == event['Records'][0]['s3']['object']['key']:
+ creations += 1
+ elif event['Records'][0]['eventName'] == 'ObjectRemoved:Delete' and \
+ key.name == event['Records'][0]['s3']['object']['key']:
+ deletions += 1
+ assert_equal(creations, 1)
+ assert_equal(deletions, 1)
+ # cleanup
+ s3_notification_conf1.del_config()
+ topic_conf1.del_config()
+ gw1.delete_bucket(bucket_name)
+ time.sleep(10)
+ s3_notification_conf2.del_config()
+ topic_conf2.del_config()
+ http_server.close()
+
+
+@attr('modification_required')
+def test_ps_s3_persistent_multiple_gateways():
+ """ test pushing persistent notification via two gateways """
+ return SkipTest('This test requires two gateways.')
+
+ conn = connection()
+ zonegroup = 'default'
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+ gw1 = conn
+ gw2 = connection2()
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket1 = gw1.create_bucket(bucket_name)
+ bucket2 = gw2.get_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+ # create two s3 topics
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true'
+ topic1_opaque = 'fromgw1'
+ topic_conf1 = PSTopicS3(gw1, topic_name+'_1', zonegroup, endpoint_args=endpoint_args+'&OpaqueData='+topic1_opaque)
+ topic_arn1 = topic_conf1.set_config()
+ topic2_opaque = 'fromgw2'
+ topic_conf2 = PSTopicS3(gw2, topic_name+'_2', zonegroup, endpoint_args=endpoint_args+'&OpaqueData='+topic2_opaque)
+ topic_arn2 = topic_conf2.set_config()
+ # create two s3 notifications
+ notification_name = bucket_name + NOTIFICATION_SUFFIX+'_1'
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn1,
+ 'Events': []
+ }]
+ s3_notification_conf1 = PSNotificationS3(gw1, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf1.set_config()
+ assert_equal(status/100, 2)
+ notification_name = bucket_name + NOTIFICATION_SUFFIX+'_2'
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn2,
+ 'Events': []
+ }]
+ s3_notification_conf2 = PSNotificationS3(gw2, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf2.set_config()
+ assert_equal(status/100, 2)
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket1.new_key('gw1_'+str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ key = bucket2.new_key('gw2_'+str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+ keys = list(bucket1.list())
+ delay = 30
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+ events = http_server.get_and_reset_events()
+ for key in keys:
+ topic1_count = 0
+ topic2_count = 0
+ for event in events:
+ if event['Records'][0]['eventName'] == 'ObjectCreated:Put' and \
+ key.name == event['Records'][0]['s3']['object']['key'] and \
+ topic1_opaque == event['Records'][0]['opaqueData']:
+ topic1_count += 1
+ elif event['Records'][0]['eventName'] == 'ObjectCreated:Put' and \
+ key.name == event['Records'][0]['s3']['object']['key'] and \
+ topic2_opaque == event['Records'][0]['opaqueData']:
+ topic2_count += 1
+ assert_equal(topic1_count, 1)
+ assert_equal(topic2_count, 1)
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket1.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+ events = http_server.get_and_reset_events()
+ for key in keys:
+ topic1_count = 0
+ topic2_count = 0
+ for event in events:
+ if event['Records'][0]['eventName'] == 'ObjectRemoved:Delete' and \
+ key.name == event['Records'][0]['s3']['object']['key'] and \
+ topic1_opaque == event['Records'][0]['opaqueData']:
+ topic1_count += 1
+ elif event['Records'][0]['eventName'] == 'ObjectRemoved:Delete' and \
+ key.name == event['Records'][0]['s3']['object']['key'] and \
+ topic2_opaque == event['Records'][0]['opaqueData']:
+ topic2_count += 1
+ assert_equal(topic1_count, 1)
+ assert_equal(topic2_count, 1)
+ # cleanup
+ s3_notification_conf1.del_config()
+ topic_conf1.del_config()
+ s3_notification_conf2.del_config()
+ topic_conf2.del_config()
+ gw1.delete_bucket(bucket_name)
+ http_server.close()
+
+
+@attr('http_test')
+def test_ps_s3_persistent_multiple_endpoints():
+ """ test pushing persistent notification when one of the endpoints has error """
+ conn = connection()
+ zonegroup = 'default'
+
+ # create random port for the http server
+ host = get_ip()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ number_of_objects = 10
+ http_server = StreamingHTTPServer(host, port, num_workers=number_of_objects)
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ # create two s3 topics
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true'
+ topic_conf1 = PSTopicS3(conn, topic_name+'_1', zonegroup, endpoint_args=endpoint_args)
+ topic_arn1 = topic_conf1.set_config()
+ endpoint_address = 'http://kaboom:9999'
+ endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true'
+ topic_conf2 = PSTopicS3(conn, topic_name+'_2', zonegroup, endpoint_args=endpoint_args)
+ topic_arn2 = topic_conf2.set_config()
+
+ # create two s3 notifications
+ notification_name = bucket_name + NOTIFICATION_SUFFIX+'_1'
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn1,
+ 'Events': []
+ }]
+ s3_notification_conf1 = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf1.set_config()
+ assert_equal(status/100, 2)
+ notification_name = bucket_name + NOTIFICATION_SUFFIX+'_2'
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn2,
+ 'Events': []
+ }]
+ s3_notification_conf2 = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf2.set_config()
+ assert_equal(status/100, 2)
+
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ keys = list(bucket.list())
+
+ delay = 30
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ http_server.verify_s3_events(keys, exact_match=False, deletions=False)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ http_server.verify_s3_events(keys, exact_match=False, deletions=True)
+
+ # cleanup
+ s3_notification_conf1.del_config()
+ topic_conf1.del_config()
+ s3_notification_conf2.del_config()
+ topic_conf2.del_config()
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+def persistent_notification(endpoint_type):
+ """ test pushing persistent notification """
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ receiver = {}
+ host = get_ip()
+ if endpoint_type == 'http':
+ # create random port for the http server
+ host = get_ip_http()
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ receiver = StreamingHTTPServer(host, port, num_workers=10)
+ endpoint_address = 'http://'+host+':'+str(port)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true'
+ # the http server does not guarantee order, so duplicates are expected
+ exact_match = False
+ elif endpoint_type == 'amqp':
+ # start amqp receiver
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+ endpoint_address = 'amqp://' + host
+ endpoint_args = 'push-endpoint='+endpoint_address+'&amqp-exchange='+exchange+'&amqp-ack-level=broker'+'&persistent=true'
+ # amqp broker guarantee ordering
+ exact_match = True
+ elif endpoint_type == 'kafka':
+ # start amqp receiver
+ task, receiver = create_kafka_receiver_thread(topic_name)
+ task.start()
+ endpoint_address = 'kafka://' + host
+ endpoint_args = 'push-endpoint='+endpoint_address+'&kafka-ack-level=broker'+'&persistent=true'
+ # amqp broker guarantee ordering
+ exact_match = True
+ else:
+ return SkipTest('Unknown endpoint type: ' + endpoint_type)
+
+
+ # create s3 topic
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket (async)
+ number_of_objects = 100
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + async http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ keys = list(bucket.list())
+
+ delay = 40
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ receiver.verify_s3_events(keys, exact_match=exact_match, deletions=False)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + async http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ receiver.verify_s3_events(keys, exact_match=exact_match, deletions=True)
+
+ # cleanup
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ if endpoint_type == 'http':
+ receiver.close()
+ else:
+ stop_amqp_receiver(receiver, task)
+
+
+@attr('http_test')
+def test_ps_s3_persistent_notification_http():
+ """ test pushing persistent notification http """
+ persistent_notification('http')
+
+
+@attr('amqp_test')
+def test_ps_s3_persistent_notification_amqp():
+ """ test pushing persistent notification amqp """
+ persistent_notification('amqp')
+
+
+@attr('kafka_test')
+def test_ps_s3_persistent_notification_kafka():
+ """ test pushing persistent notification kafka """
+ persistent_notification('kafka')
+
+
+def random_string(length):
+ import string
+ letters = string.ascii_letters
+ return ''.join(random.choice(letters) for i in range(length))
+
+
+@attr('amqp_test')
+def test_ps_s3_persistent_notification_large():
+ """ test pushing persistent notification of large notifications """
+
+ conn = connection()
+ zonegroup = 'default'
+
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ topic_name = bucket_name + TOPIC_SUFFIX
+
+ receiver = {}
+ host = get_ip()
+ # start amqp receiver
+ exchange = 'ex1'
+ task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ task.start()
+ endpoint_address = 'amqp://' + host
+ opaque_data = random_string(1024*2)
+ endpoint_args = 'push-endpoint='+endpoint_address+'&OpaqueData='+opaque_data+'&amqp-exchange='+exchange+'&amqp-ack-level=broker'+'&persistent=true'
+ # amqp broker guarantee ordering
+ exact_match = True
+
+ # create s3 topic
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ response, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+
+ # create objects in the bucket (async)
+ number_of_objects = 100
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key_value = random_string(63)
+ key = bucket.new_key(key_value)
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for creation + async http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ keys = list(bucket.list())
+
+ delay = 40
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ receiver.verify_s3_events(keys, exact_match=exact_match, deletions=False)
+
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+
+ time_diff = time.time() - start_time
+ print('average time for deletion + async http notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+
+ print('wait for '+str(delay)+'sec for the messages...')
+ time.sleep(delay)
+
+ receiver.verify_s3_events(keys, exact_match=exact_match, deletions=True)
+
+ # cleanup
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ conn.delete_bucket(bucket_name)
+ stop_amqp_receiver(receiver, task)
+
+
+@attr('modification_required')
+def test_ps_s3_topic_update():
+ """ test updating topic associated with a notification"""
+ return SkipTest('This test is yet to be modified.')
+
+ conn = connection()
+ ps_zone = None
+ bucket_name = gen_bucket_name()
+ topic_name = bucket_name+TOPIC_SUFFIX
+ # create amqp topic
+ hostname = get_ip()
+ exchange = 'ex1'
+ amqp_task, receiver = create_amqp_receiver_thread(exchange, topic_name)
+ amqp_task.start()
+ #topic_conf = PSTopic(ps_zone.conn, topic_name,endpoint='amqp://' + hostname,endpoint_args='amqp-exchange=' + exchange + '&amqp-ack-level=none')
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args='amqp-exchange=' + exchange + '&amqp-ack-level=none')
+
+ topic_arn = topic_conf.set_config()
+ #result, status = topic_conf.set_config()
+ #assert_equal(status/100, 2)
+ parsed_result = json.loads(result)
+ topic_arn = parsed_result['arn']
+ # get topic
+ result, _ = topic_conf.get_config()
+ # verify topic content
+ parsed_result = json.loads(result)
+ assert_equal(parsed_result['topic']['name'], topic_name)
+ assert_equal(parsed_result['topic']['dest']['push_endpoint'], topic_conf.parameters['push-endpoint'])
+ # create http server
+ port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ http_server = StreamingHTTPServer(hostname, port)
+ # create bucket on the first of the rados zones
+ bucket = conn.create_bucket(bucket_name)
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*']
+ }]
+ s3_notification_conf = PSNotificationS3(ps_zone.conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+ # create objects in the bucket
+ number_of_objects = 10
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ key.set_contents_from_string('bar')
+ # wait for sync
+ #zone_bucket_checkpoint(ps_zone.zone, master_zone.zone, bucket_name)
+ keys = list(bucket.list())
+ # TODO: use exact match
+ receiver.verify_s3_events(keys, exact_match=False)
+ # update the same topic with new endpoint
+ #topic_conf = PSTopic(ps_zone.conn, topic_name,endpoint='http://'+ hostname + ':' + str(port))
+ topic_conf = PSTopicS3(conn, topic_name, endpoint_args='http://'+ hostname + ':' + str(port))
+ _, status = topic_conf.set_config()
+ assert_equal(status/100, 2)
+ # get topic
+ result, _ = topic_conf.get_config()
+ # verify topic content
+ parsed_result = json.loads(result)
+ assert_equal(parsed_result['topic']['name'], topic_name)
+ assert_equal(parsed_result['topic']['dest']['push_endpoint'], topic_conf.parameters['push-endpoint'])
+ # delete current objects and create new objects in the bucket
+ for key in bucket.list():
+ key.delete()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i+100))
+ key.set_contents_from_string('bar')
+ # wait for sync
+ #zone_meta_checkpoint(ps_zone.zone)
+ #zone_bucket_checkpoint(ps_zone.zone, master_zone.zone, bucket_name)
+ keys = list(bucket.list())
+ # verify that notifications are still sent to amqp
+ # TODO: use exact match
+ receiver.verify_s3_events(keys, exact_match=False)
+ # update notification to update the endpoint from the topic
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*']
+ }]
+ s3_notification_conf = PSNotificationS3(ps_zone.conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+ # delete current objects and create new objects in the bucket
+ for key in bucket.list():
+ key.delete()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i+200))
+ key.set_contents_from_string('bar')
+ # wait for sync
+ #zone_meta_checkpoint(ps_zone.zone)
+ #zone_bucket_checkpoint(ps_zone.zone, master_zone.zone, bucket_name)
+ keys = list(bucket.list())
+ # check that updates switched to http
+ # TODO: use exact match
+ http_server.verify_s3_events(keys, exact_match=False)
+ # cleanup
+ # delete objects from the bucket
+ stop_amqp_receiver(receiver, amqp_task)
+ for key in bucket.list():
+ key.delete()
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+
+@attr('modification_required')
+def test_ps_s3_notification_update():
+ """ test updating the topic of a notification"""
+ return SkipTest('This test is yet to be modified.')
+
+ hostname = get_ip()
+ conn = connection()
+ ps_zone = None
+ bucket_name = gen_bucket_name()
+ topic_name1 = bucket_name+'amqp'+TOPIC_SUFFIX
+ topic_name2 = bucket_name+'http'+TOPIC_SUFFIX
+ zonegroup = 'default'
+ # create topics
+ # start amqp receiver in a separate thread
+ exchange = 'ex1'
+ amqp_task, receiver = create_amqp_receiver_thread(exchange, topic_name1)
+ amqp_task.start()
+ # create random port for the http server
+ http_port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ http_server = StreamingHTTPServer(hostname, http_port)
+ #topic_conf1 = PSTopic(ps_zone.conn, topic_name1,endpoint='amqp://' + hostname,endpoint_args='amqp-exchange=' + exchange + '&amqp-ack-level=none')
+ topic_conf1 = PSTopicS3(conn, topic_name1, zonegroup, endpoint_args='amqp-exchange=' + exchange + '&amqp-ack-level=none')
+ result, status = topic_conf1.set_config()
+ parsed_result = json.loads(result)
+ topic_arn1 = parsed_result['arn']
+ assert_equal(status/100, 2)
+ #topic_conf2 = PSTopic(ps_zone.conn, topic_name2,endpoint='http://'+hostname+':'+str(http_port))
+ topic_conf2 = PSTopicS3(conn, topic_name2, endpoint_args='http://'+hostname+':'+str(http_port))
+ result, status = topic_conf2.set_config()
+ parsed_result = json.loads(result)
+ topic_arn2 = parsed_result['arn']
+ assert_equal(status/100, 2)
+ # create bucket on the first of the rados zones
+ bucket = conn.create_bucket(bucket_name)
+ # wait for sync
+ #zone_meta_checkpoint(ps_zone.zone)
+ # create s3 notification with topic1
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn1,
+ 'Events': ['s3:ObjectCreated:*']
+ }]
+ s3_notification_conf = PSNotificationS3(ps_zone.conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+ # create objects in the bucket
+ number_of_objects = 10
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ key.set_contents_from_string('bar')
+ # wait for sync
+ #zone_bucket_checkpoint(ps_zone.zone, master_zone.zone, bucket_name)
+ keys = list(bucket.list())
+ # TODO: use exact match
+ receiver.verify_s3_events(keys, exact_match=False);
+ # update notification to use topic2
+ topic_conf_list = [{'Id': notification_name,
+ 'TopicArn': topic_arn2,
+ 'Events': ['s3:ObjectCreated:*']
+ }]
+ s3_notification_conf = PSNotificationS3(ps_zone.conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+ # delete current objects and create new objects in the bucket
+ for key in bucket.list():
+ key.delete()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i+100))
+ key.set_contents_from_string('bar')
+ # wait for sync
+ #zone_meta_checkpoint(ps_zone.zone)
+ #zone_bucket_checkpoint(ps_zone.zone, master_zone.zone, bucket_name)
+ keys = list(bucket.list())
+ # check that updates switched to http
+ # TODO: use exact match
+ http_server.verify_s3_events(keys, exact_match=False)
+ # cleanup
+ # delete objects from the bucket
+ stop_amqp_receiver(receiver, amqp_task)
+ for key in bucket.list():
+ key.delete()
+ s3_notification_conf.del_config()
+ topic_conf1.del_config()
+ topic_conf2.del_config()
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+
+@attr('modification_required')
+def test_ps_s3_multiple_topics_notification():
+ """ test notification creation with multiple topics"""
+ return SkipTest('This test is yet to be modified.')
+
+ hostname = get_ip()
+ zonegroup = 'default'
+ conn = connection()
+ ps_zone = None
+ bucket_name = gen_bucket_name()
+ topic_name1 = bucket_name+'amqp'+TOPIC_SUFFIX
+ topic_name2 = bucket_name+'http'+TOPIC_SUFFIX
+ # create topics
+ # start amqp receiver in a separate thread
+ exchange = 'ex1'
+ amqp_task, receiver = create_amqp_receiver_thread(exchange, topic_name1)
+ amqp_task.start()
+ # create random port for the http server
+ http_port = random.randint(10000, 20000)
+ # start an http server in a separate thread
+ http_server = StreamingHTTPServer(hostname, http_port)
+ #topic_conf1 = PSTopic(ps_zone.conn, topic_name1,endpoint='amqp://' + hostname,endpoint_args='amqp-exchange=' + exchange + '&amqp-ack-level=none')
+ topic_conf1 = PSTopicS3(conn, topic_name1, zonegroup, endpoint_args='amqp-exchange=' + exchange + '&amqp-ack-level=none')
+ result, status = topic_conf1.set_config()
+ parsed_result = json.loads(result)
+ topic_arn1 = parsed_result['arn']
+ assert_equal(status/100, 2)
+ #topic_conf2 = PSTopic(ps_zone.conn, topic_name2,endpoint='http://'+hostname+':'+str(http_port))
+ topic_conf2 = PSTopicS3(conn, topic_name2, zonegroup, endpoint_args='http://'+hostname+':'+str(http_port))
+ result, status = topic_conf2.set_config()
+ parsed_result = json.loads(result)
+ topic_arn2 = parsed_result['arn']
+ assert_equal(status/100, 2)
+ # create bucket on the first of the rados zones
+ bucket = conn.create_bucket(bucket_name)
+ # wait for sync
+ #zone_meta_checkpoint(ps_zone.zone)
+ # create s3 notification
+ notification_name1 = bucket_name + NOTIFICATION_SUFFIX + '_1'
+ notification_name2 = bucket_name + NOTIFICATION_SUFFIX + '_2'
+ topic_conf_list = [
+ {
+ 'Id': notification_name1,
+ 'TopicArn': topic_arn1,
+ 'Events': ['s3:ObjectCreated:*']
+ },
+ {
+ 'Id': notification_name2,
+ 'TopicArn': topic_arn2,
+ 'Events': ['s3:ObjectCreated:*']
+ }]
+ s3_notification_conf = PSNotificationS3(ps_zone.conn, bucket_name, topic_conf_list)
+ _, status = s3_notification_conf.set_config()
+ assert_equal(status/100, 2)
+ result, _ = s3_notification_conf.get_config()
+ assert_equal(len(result['TopicConfigurations']), 2)
+ assert_equal(result['TopicConfigurations'][0]['Id'], notification_name1)
+ assert_equal(result['TopicConfigurations'][1]['Id'], notification_name2)
+ # get auto-generated subscriptions
+ sub_conf1 = PSSubscription(ps_zone.conn, notification_name1,
+ topic_name1)
+ _, status = sub_conf1.get_config()
+ assert_equal(status/100, 2)
+ sub_conf2 = PSSubscription(ps_zone.conn, notification_name2,
+ topic_name2)
+ _, status = sub_conf2.get_config()
+ assert_equal(status/100, 2)
+ # create objects in the bucket
+ number_of_objects = 10
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ key.set_contents_from_string('bar')
+ # wait for sync
+ #zone_bucket_checkpoint(ps_zone.zone, master_zone.zone, bucket_name)
+ # get the events from both of the subscription
+ result, _ = sub_conf1.get_events()
+ records = json.loads(result)
+ for record in records['Records']:
+ log.debug(record)
+ keys = list(bucket.list())
+ # TODO: use exact match
+ verify_s3_records_by_elements(records, keys, exact_match=False)
+ receiver.verify_s3_events(keys, exact_match=False)
+ result, _ = sub_conf2.get_events()
+ parsed_result = json.loads(result)
+ for record in parsed_result['Records']:
+ log.debug(record)
+ keys = list(bucket.list())
+ # TODO: use exact match
+ verify_s3_records_by_elements(records, keys, exact_match=False)
+ http_server.verify_s3_events(keys, exact_match=False)
+ # cleanup
+ stop_amqp_receiver(receiver, amqp_task)
+ s3_notification_conf.del_config()
+ topic_conf1.del_config()
+ topic_conf2.del_config()
+ # delete objects from the bucket
+ for key in bucket.list():
+ key.delete()
+ conn.delete_bucket(bucket_name)
+ http_server.close()
+
+
+def kafka_security(security_type):
+ """ test pushing kafka s3 notification securly to master """
+ conn = connection()
+ zonegroup = 'default'
+ # create bucket
+ bucket_name = gen_bucket_name()
+ bucket = conn.create_bucket(bucket_name)
+ # name is constant for manual testing
+ topic_name = bucket_name+'_topic'
+ # create s3 topic
+ if security_type == 'SSL_SASL':
+ endpoint_address = 'kafka://alice:alice-secret@' + kafka_server + ':9094'
+ elif security_type == 'SSL':
+ endpoint_address = 'kafka://' + kafka_server + ':9093'
+ else:
+ assert False, 'unknown security method '+security_type
+
+ KAFKA_DIR = os.environ['KAFKA_DIR']
+ endpoint_args = 'push-endpoint='+endpoint_address+'&kafka-ack-level=broker&use-ssl=true&ca-location='+KAFKA_DIR+"/y-ca.crt"
+
+ topic_conf = PSTopicS3(conn, topic_name, zonegroup, endpoint_args=endpoint_args)
+
+ # create consumer on the topic
+ task, receiver = create_kafka_receiver_thread(topic_name)
+ task.start()
+
+ topic_arn = topic_conf.set_config()
+ # create s3 notification
+ notification_name = bucket_name + NOTIFICATION_SUFFIX
+ topic_conf_list = [{'Id': notification_name, 'TopicArn': topic_arn,
+ 'Events': []
+ }]
+ s3_notification_conf = PSNotificationS3(conn, bucket_name, topic_conf_list)
+ s3_notification_conf.set_config()
+ # create objects in the bucket (async)
+ number_of_objects = 10
+ client_threads = []
+ start_time = time.time()
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ content = str(os.urandom(1024*1024))
+ thr = threading.Thread(target = set_contents_from_string, args=(key, content,))
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+ time_diff = time.time() - start_time
+ print('average time for creation + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+ try:
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ keys = list(bucket.list())
+ receiver.verify_s3_events(keys, exact_match=True)
+ # delete objects from the bucket
+ client_threads = []
+ start_time = time.time()
+ for key in bucket.list():
+ thr = threading.Thread(target = key.delete, args=())
+ thr.start()
+ client_threads.append(thr)
+ [thr.join() for thr in client_threads]
+ time_diff = time.time() - start_time
+ print('average time for deletion + kafka notification is: ' + str(time_diff*1000/number_of_objects) + ' milliseconds')
+ print('wait for 5sec for the messages...')
+ time.sleep(5)
+ receiver.verify_s3_events(keys, exact_match=True, deletions=True)
+ except Exception as err:
+ assert False, str(err)
+ finally:
+ # cleanup
+ s3_notification_conf.del_config()
+ topic_conf.del_config()
+ # delete the bucket
+ for key in bucket.list():
+ key.delete()
+ conn.delete_bucket(bucket_name)
+ stop_kafka_receiver(receiver, task)
+
+
+@attr('kafka_ssl_test')
+def test_ps_s3_notification_push_kafka_security_ssl():
+ kafka_security('SSL')
+
+
+@attr('kafka_ssl_test')
+def test_ps_s3_notification_push_kafka_security_ssl_sasl():
+ kafka_security('SSL_SASL')
+
diff --git a/src/test/rgw/kafka_stub.cc b/src/test/rgw/kafka_stub.cc
new file mode 100644
index 000000000..6125a94cb
--- /dev/null
+++ b/src/test/rgw/kafka_stub.cc
@@ -0,0 +1,68 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <librdkafka/rdkafka.h>
+
+const char *rd_kafka_topic_name(const rd_kafka_topic_t *rkt) {
+ return "";
+}
+
+rd_kafka_resp_err_t rd_kafka_last_error() {
+ return rd_kafka_resp_err_t();
+}
+
+const char *rd_kafka_err2str(rd_kafka_resp_err_t err) {
+ return "";
+}
+
+rd_kafka_conf_t *rd_kafka_conf_new() {
+ return nullptr;
+}
+
+rd_kafka_conf_res_t rd_kafka_conf_set(rd_kafka_conf_t *conf,
+ const char *name,
+ const char *value,
+ char *errstr, size_t errstr_size) {
+ return rd_kafka_conf_res_t();
+}
+
+void rd_kafka_conf_set_dr_msg_cb(rd_kafka_conf_t *conf,
+ void (*dr_msg_cb) (rd_kafka_t *rk,
+ const rd_kafka_message_t *
+ rkmessage,
+ void *opaque)) {}
+
+void rd_kafka_conf_set_opaque(rd_kafka_conf_t *conf, void *opaque) {}
+
+rd_kafka_t *rd_kafka_new(rd_kafka_type_t type, rd_kafka_conf_t *conf,
+ char *errstr, size_t errstr_size) {
+ return nullptr;
+}
+
+void rd_kafka_conf_destroy(rd_kafka_conf_t *conf) {}
+
+rd_kafka_resp_err_t rd_kafka_flush (rd_kafka_t *rk, int timeout_ms) {
+ return rd_kafka_resp_err_t();
+}
+
+void rd_kafka_destroy(rd_kafka_t *rk) {}
+
+rd_kafka_topic_t *rd_kafka_topic_new(rd_kafka_t *rk, const char *topic,
+ rd_kafka_topic_conf_t *conf) {
+ return nullptr;
+}
+
+int rd_kafka_produce(rd_kafka_topic_t *rkt, int32_t partition,
+ int msgflags,
+ void *payload, size_t len,
+ const void *key, size_t keylen,
+ void *msg_opaque) {
+ return 0;
+}
+
+int rd_kafka_poll(rd_kafka_t *rk, int timeout_ms) {
+ return 0;
+}
+
+void rd_kafka_topic_destroy(rd_kafka_topic_t *rkt) {}
+
diff --git a/src/test/rgw/rgw_cr_test.cc b/src/test/rgw/rgw_cr_test.cc
new file mode 100644
index 000000000..dc5d25d23
--- /dev/null
+++ b/src/test/rgw/rgw_cr_test.cc
@@ -0,0 +1,343 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+#include <cerrno>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <fmt/format.h>
+
+#include "include/rados/librados.hpp"
+
+#include "common/common_init.h"
+#include "common/config.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+
+#include "rgw_coroutine.h"
+#include "rgw_cr_rados.h"
+#include "rgw_sal.h"
+#include "rgw_sal_rados.h"
+
+#include "gtest/gtest.h"
+
+using namespace std::literals;
+
+static constexpr auto dout_subsys = ceph_subsys_rgw;
+
+static rgw::sal::RadosStore* store = nullptr;
+
+static const DoutPrefixProvider* dpp() {
+ struct GlobalPrefix : public DoutPrefixProvider {
+ CephContext *get_cct() const override { return g_ceph_context; }
+ unsigned get_subsys() const override { return dout_subsys; }
+ std::ostream& gen_prefix(std::ostream& out) const override { return out; }
+ };
+ static GlobalPrefix global_dpp;
+ return &global_dpp;
+}
+
+class StoreDestructor {
+ rgw::sal::Driver* driver;
+public:
+ explicit StoreDestructor(rgw::sal::RadosStore* _s) : driver(_s) {}
+ ~StoreDestructor() {
+ DriverManager::close_storage(store);
+ }
+};
+
+struct TempPool {
+ inline static uint64_t num = 0;
+ std::string name =
+ fmt::format("{}-{}-{}", ::time(nullptr), ::getpid(),num++);
+
+ TempPool() {
+ auto r = store->getRados()->get_rados_handle()->pool_create(name.c_str());
+ assert(r == 0);
+ }
+
+ ~TempPool() {
+ auto r = store->getRados()->get_rados_handle()->pool_delete(name.c_str());
+ assert(r == 0);
+ }
+
+ operator rgw_pool() {
+ return { name };
+ }
+
+ operator librados::IoCtx() {
+ librados::IoCtx ioctx;
+ auto r = store->getRados()->get_rados_handle()->ioctx_create(name.c_str(),
+ ioctx);
+ assert(r == 0);
+ return ioctx;
+ }
+};
+
+int run(RGWCoroutine* cr) {
+ RGWCoroutinesManager cr_mgr{store->ctx(),
+ store->getRados()->get_cr_registry()};
+ std::list<RGWCoroutinesStack *> stacks;
+ auto stack = new RGWCoroutinesStack(store->ctx(), &cr_mgr);
+ stack->call(cr);
+ stacks.push_back(stack);
+ return cr_mgr.run(dpp(), stacks);
+}
+
+TEST(ReadAttrs, Unfiltered) {
+ TempPool pool;
+ ceph::bufferlist bl;
+ auto dummy = "Dummy attribute value"s;
+ encode(dummy, bl);
+ const std::map<std::string, ceph::bufferlist> ref_attrs{
+ { "foo"s, bl }, { "bar"s, bl }, { "baz"s, bl }
+ };
+ auto oid = "object"s;
+ {
+ librados::IoCtx ioctx(pool);
+ librados::ObjectWriteOperation op;
+ op.setxattr("foo", bl);
+ op.setxattr("bar", bl);
+ op.setxattr("baz", bl);
+ auto r = ioctx.operate(oid, &op);
+ ASSERT_EQ(0, r);
+ }
+ std::map<std::string, ceph::bufferlist> attrs;
+ auto r = run(new RGWSimpleRadosReadAttrsCR(dpp(), store, {pool, oid}, &attrs,
+ true));
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(ref_attrs, attrs);
+}
+
+TEST(ReadAttrs, Filtered) {
+ TempPool pool;
+ ceph::bufferlist bl;
+ auto dummy = "Dummy attribute value"s;
+ encode(dummy, bl);
+ const std::map<std::string, ceph::bufferlist> ref_attrs{
+ { RGW_ATTR_PREFIX "foo"s, bl },
+ { RGW_ATTR_PREFIX "bar"s, bl },
+ { RGW_ATTR_PREFIX "baz"s, bl }
+ };
+ auto oid = "object"s;
+ {
+ librados::IoCtx ioctx(pool);
+ librados::ObjectWriteOperation op;
+ op.setxattr(RGW_ATTR_PREFIX "foo", bl);
+ op.setxattr(RGW_ATTR_PREFIX "bar", bl);
+ op.setxattr(RGW_ATTR_PREFIX "baz", bl);
+ op.setxattr("oneOfTheseThingsIsNotLikeTheOthers", bl);
+ auto r = ioctx.operate(oid, &op);
+ ASSERT_EQ(0, r);
+ }
+ std::map<std::string, ceph::bufferlist> attrs;
+ auto r = run(new RGWSimpleRadosReadAttrsCR(dpp(), store, {pool, oid}, &attrs,
+ false));
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(ref_attrs, attrs);
+}
+
+TEST(Read, Dne) {
+ TempPool pool;
+ std::string result;
+ auto r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, "doesnotexist"},
+ &result, false));
+ ASSERT_EQ(-ENOENT, r);
+}
+
+TEST(Read, Read) {
+ TempPool pool;
+ auto data = "I am test data!"sv;
+ auto oid = "object"s;
+ {
+ bufferlist bl;
+ encode(data, bl);
+ librados::IoCtx ioctx(pool);
+ auto r = ioctx.write_full(oid, bl);
+ ASSERT_EQ(0, r);
+ }
+ std::string result;
+ auto r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result,
+ false));
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(result, data);
+}
+
+TEST(Read, ReadVersion) {
+ TempPool pool;
+ auto data = "I am test data!"sv;
+ auto oid = "object"s;
+ RGWObjVersionTracker wobjv;
+ {
+ bufferlist bl;
+ encode(data, bl);
+ librados::IoCtx ioctx(pool);
+ librados::ObjectWriteOperation op;
+ wobjv.generate_new_write_ver(store->ctx());
+ wobjv.prepare_op_for_write(&op);
+ op.write_full(bl);
+ auto r = ioctx.operate(oid, &op);
+ EXPECT_EQ(0, r);
+ wobjv.apply_write();
+ }
+ RGWObjVersionTracker robjv;
+ std::string result;
+ auto r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result,
+ false, &robjv));
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(result, data);
+ data = "I am NEW test data!";
+ {
+ bufferlist bl;
+ encode(data, bl);
+ librados::IoCtx ioctx(pool);
+ librados::ObjectWriteOperation op;
+ wobjv.generate_new_write_ver(store->ctx());
+ wobjv.prepare_op_for_write(&op);
+ op.write_full(bl);
+ r = ioctx.operate(oid, &op);
+ EXPECT_EQ(0, r);
+ wobjv.apply_write();
+ }
+ result.clear();
+ r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result, false,
+ &robjv));
+ ASSERT_EQ(-ECANCELED, r);
+ ASSERT_TRUE(result.empty());
+
+ robjv.clear();
+ r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result, false,
+ &robjv));
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(result, data);
+ ASSERT_EQ(wobjv.read_version, robjv.read_version);
+}
+
+TEST(Write, Exclusive) {
+ TempPool pool;
+ auto oid = "object"s;
+ {
+ bufferlist bl;
+ bl.append("I'm some data!"s);
+ librados::IoCtx ioctx(pool);
+ auto r = ioctx.write_full(oid, bl);
+ ASSERT_EQ(0, r);
+ }
+ auto r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid},
+ "I am some DIFFERENT data!"s, nullptr,
+ true));
+ ASSERT_EQ(-EEXIST, r);
+}
+
+TEST(Write, Write) {
+ TempPool pool;
+ auto oid = "object"s;
+ auto data = "I'm some data!"s;
+ auto r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid},
+ data, nullptr, true));
+ ASSERT_EQ(0, r);
+ bufferlist bl;
+ librados::IoCtx ioctx(pool);
+ ioctx.read(oid, bl, 0, 0);
+ ASSERT_EQ(0, r);
+ std::string result;
+ decode(result, bl);
+ ASSERT_EQ(data, result);
+}
+
+TEST(Write, ObjV) {
+ TempPool pool;
+ auto oid = "object"s;
+ RGWObjVersionTracker objv;
+ objv.generate_new_write_ver(store->ctx());
+ auto r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid},
+ "I'm some data!"s, &objv,
+ true));
+ RGWObjVersionTracker interfering_objv(objv);
+ r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid},
+ "I'm some newer, better data!"s,
+ &interfering_objv, false));
+ ASSERT_EQ(0, r);
+ r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid},
+ "I'm some treacherous, obsolete data!"s,
+ &objv, false));
+ ASSERT_EQ(-ECANCELED, r);
+}
+
+TEST(WriteAttrs, Attrs) {
+ TempPool pool;
+ auto oid = "object"s;
+ bufferlist bl;
+ bl.append("I'm some data.");
+ std::map<std::string, bufferlist> wrattrs {
+ { "foo", bl }, { "bar", bl }, { "baz", bl }
+ };
+ auto r = run(new RGWSimpleRadosWriteAttrsCR(dpp(), store, {pool, oid},
+ wrattrs, nullptr, true));
+ ASSERT_EQ(0, r);
+ std::map<std::string, bufferlist> rdattrs;
+ librados::IoCtx ioctx(pool);
+ r = ioctx.getxattrs(oid, rdattrs);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(wrattrs, rdattrs);
+}
+
+TEST(WriteAttrs, Empty) {
+ TempPool pool;
+ auto oid = "object"s;
+ bufferlist bl;
+ std::map<std::string, bufferlist> wrattrs {
+ { "foo", bl }, { "bar", bl }, { "baz", bl }
+ };
+ // With an empty bufferlist all attributes should be skipped.
+ auto r = run(new RGWSimpleRadosWriteAttrsCR(dpp(), store, {pool, oid},
+ wrattrs, nullptr, true));
+ ASSERT_EQ(0, r);
+ std::map<std::string, bufferlist> rdattrs;
+ librados::IoCtx ioctx(pool);
+ r = ioctx.getxattrs(oid, rdattrs);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(rdattrs.empty());
+}
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ auto cct = rgw_global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY, 0);
+
+ // for region -> zonegroup conversion (must happen before common_init_finish())
+ if (!g_conf()->rgw_region.empty() && g_conf()->rgw_zonegroup.empty()) {
+ g_conf().set_val_or_die("rgw_zonegroup", g_conf()->rgw_region.c_str());
+ }
+
+ /* common_init_finish needs to be called after g_conf().set_val() */
+ common_init_finish(g_ceph_context);
+
+
+ DriverManager::Config cfg = DriverManager::get_config(true, g_ceph_context);
+
+ store = static_cast<rgw::sal::RadosStore*>(
+ DriverManager::get_storage(dpp(),
+ g_ceph_context,
+ cfg,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false));
+ if (!store) {
+ std::cerr << "couldn't init storage provider" << std::endl;
+ return 5; //EIO
+ }
+ StoreDestructor store_destructor(static_cast<rgw::sal::RadosStore*>(store));
+
+ std::string pool{"rgw_cr_test"};
+ store->getRados()->create_pool(dpp(), pool);
+
+ testing::InitGoogleTest();
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/rgw/rgw_multi/__init__.py b/src/test/rgw/rgw_multi/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/test/rgw/rgw_multi/__init__.py
diff --git a/src/test/rgw/rgw_multi/conn.py b/src/test/rgw/rgw_multi/conn.py
new file mode 100644
index 000000000..59bc2fdd3
--- /dev/null
+++ b/src/test/rgw/rgw_multi/conn.py
@@ -0,0 +1,41 @@
+import boto
+import boto.s3.connection
+import boto.iam.connection
+
+def get_gateway_connection(gateway, credentials):
+ """ connect to the given gateway """
+ if gateway.connection is None:
+ gateway.connection = boto.connect_s3(
+ aws_access_key_id = credentials.access_key,
+ aws_secret_access_key = credentials.secret,
+ host = gateway.host,
+ port = gateway.port,
+ is_secure = False,
+ calling_format = boto.s3.connection.OrdinaryCallingFormat())
+ return gateway.connection
+
+def get_gateway_secure_connection(gateway, credentials):
+ """ secure connect to the given gateway """
+ if gateway.ssl_port == 0:
+ return None
+ if gateway.secure_connection is None:
+ gateway.secure_connection = boto.connect_s3(
+ aws_access_key_id = credentials.access_key,
+ aws_secret_access_key = credentials.secret,
+ host = gateway.host,
+ port = gateway.ssl_port,
+ is_secure = True,
+ validate_certs=False,
+ calling_format = boto.s3.connection.OrdinaryCallingFormat())
+ return gateway.secure_connection
+
+def get_gateway_iam_connection(gateway, credentials):
+ """ connect to iam api of the given gateway """
+ if gateway.iam_connection is None:
+ gateway.iam_connection = boto.connect_iam(
+ aws_access_key_id = credentials.access_key,
+ aws_secret_access_key = credentials.secret,
+ host = gateway.host,
+ port = gateway.port,
+ is_secure = False)
+ return gateway.iam_connection
diff --git a/src/test/rgw/rgw_multi/multisite.py b/src/test/rgw/rgw_multi/multisite.py
new file mode 100644
index 000000000..5d4dcd1aa
--- /dev/null
+++ b/src/test/rgw/rgw_multi/multisite.py
@@ -0,0 +1,407 @@
+from abc import ABCMeta, abstractmethod
+from io import StringIO
+
+import json
+
+from .conn import get_gateway_connection, get_gateway_iam_connection, get_gateway_secure_connection
+
+class Cluster:
+ """ interface to run commands against a distinct ceph cluster """
+ __metaclass__ = ABCMeta
+
+ @abstractmethod
+ def admin(self, args = None, **kwargs):
+ """ execute a radosgw-admin command """
+ pass
+
+class Gateway:
+ """ interface to control a single radosgw instance """
+ __metaclass__ = ABCMeta
+
+ def __init__(self, host = None, port = None, cluster = None, zone = None, ssl_port = 0):
+ self.host = host
+ self.port = port
+ self.cluster = cluster
+ self.zone = zone
+ self.connection = None
+ self.secure_connection = None
+ self.ssl_port = ssl_port
+ self.iam_connection = None
+
+ @abstractmethod
+ def start(self, args = []):
+ """ start the gateway with the given args """
+ pass
+
+ @abstractmethod
+ def stop(self):
+ """ stop the gateway """
+ pass
+
+ def endpoint(self):
+ return 'http://%s:%d' % (self.host, self.port)
+
+class SystemObject:
+ """ interface for system objects, represented in json format and
+ manipulated with radosgw-admin commands """
+ __metaclass__ = ABCMeta
+
+ def __init__(self, data = None, uuid = None):
+ self.data = data
+ self.id = uuid
+ if data:
+ self.load_from_json(data)
+
+ @abstractmethod
+ def build_command(self, command):
+ """ return the command line for the given command, including arguments
+ to specify this object """
+ pass
+
+ @abstractmethod
+ def load_from_json(self, data):
+ """ update internal state based on json data """
+ pass
+
+ def command(self, cluster, cmd, args = None, **kwargs):
+ """ run the given command and return the output and retcode """
+ args = self.build_command(cmd) + (args or [])
+ return cluster.admin(args, **kwargs)
+
+ def json_command(self, cluster, cmd, args = None, **kwargs):
+ """ run the given command, parse the output and return the resulting
+ data and retcode """
+ s, r = self.command(cluster, cmd, args or [], **kwargs)
+ if r == 0:
+ data = json.loads(s)
+ self.load_from_json(data)
+ self.data = data
+ return self.data, r
+
+ # mixins for supported commands
+ class Create(object):
+ def create(self, cluster, args = None, **kwargs):
+ """ create the object with the given arguments """
+ return self.json_command(cluster, 'create', args, **kwargs)
+
+ class Delete(object):
+ def delete(self, cluster, args = None, **kwargs):
+ """ delete the object """
+ # not json_command() because delete has no output
+ _, r = self.command(cluster, 'delete', args, **kwargs)
+ if r == 0:
+ self.data = None
+ return r
+
+ class Get(object):
+ def get(self, cluster, args = None, **kwargs):
+ """ read the object from storage """
+ kwargs['read_only'] = True
+ return self.json_command(cluster, 'get', args, **kwargs)
+
+ class Set(object):
+ def set(self, cluster, data, args = None, **kwargs):
+ """ set the object by json """
+ kwargs['stdin'] = StringIO(json.dumps(data))
+ return self.json_command(cluster, 'set', args, **kwargs)
+
+ class Modify(object):
+ def modify(self, cluster, args = None, **kwargs):
+ """ modify the object with the given arguments """
+ return self.json_command(cluster, 'modify', args, **kwargs)
+
+ class CreateDelete(Create, Delete): pass
+ class GetSet(Get, Set): pass
+
+class Zone(SystemObject, SystemObject.CreateDelete, SystemObject.GetSet, SystemObject.Modify):
+ def __init__(self, name, zonegroup = None, cluster = None, data = None, zone_id = None, gateways = None):
+ self.name = name
+ self.zonegroup = zonegroup
+ self.cluster = cluster
+ self.gateways = gateways or []
+ super(Zone, self).__init__(data, zone_id)
+
+ def zone_arg(self):
+ """ command-line argument to specify this zone """
+ return ['--rgw-zone', self.name]
+
+ def zone_args(self):
+ """ command-line arguments to specify this zone/zonegroup/realm """
+ args = self.zone_arg()
+ if self.zonegroup:
+ args += self.zonegroup.zonegroup_args()
+ return args
+
+ def build_command(self, command):
+ """ build a command line for the given command and args """
+ return ['zone', command] + self.zone_args()
+
+ def load_from_json(self, data):
+ """ load the zone from json """
+ self.id = data['id']
+ self.name = data['name']
+
+ def start(self, args = None):
+ """ start all gateways """
+ for g in self.gateways:
+ g.start(args)
+
+ def stop(self):
+ """ stop all gateways """
+ for g in self.gateways:
+ g.stop()
+
+ def period(self):
+ return self.zonegroup.period if self.zonegroup else None
+
+ def realm(self):
+ return self.zonegroup.realm() if self.zonegroup else None
+
+ def is_read_only(self):
+ return False
+
+ def tier_type(self):
+ raise NotImplementedError
+
+ def syncs_from(self, zone_name):
+ return zone_name != self.name
+
+ def has_buckets(self):
+ return True
+
+ def has_roles(self):
+ return True
+
+ def get_conn(self, credentials):
+ return ZoneConn(self, credentials) # not implemented, but can be used
+
+class ZoneConn(object):
+ def __init__(self, zone, credentials):
+ self.zone = zone
+ self.name = zone.name
+ """ connect to the zone's first gateway """
+ if isinstance(credentials, list):
+ self.credentials = credentials[0]
+ else:
+ self.credentials = credentials
+
+ if self.zone.gateways is not None:
+ self.conn = get_gateway_connection(self.zone.gateways[0], self.credentials)
+ self.secure_conn = get_gateway_secure_connection(self.zone.gateways[0], self.credentials)
+
+ self.iam_conn = get_gateway_iam_connection(self.zone.gateways[0], self.credentials)
+
+ # create connections for the rest of the gateways (if exist)
+ for gw in list(self.zone.gateways):
+ get_gateway_connection(gw, self.credentials)
+ get_gateway_secure_connection(gw, self.credentials)
+
+ get_gateway_iam_connection(gw, self.credentials)
+
+
+ def get_connection(self):
+ return self.conn
+
+ def get_iam_connection(self):
+ return self.iam_conn
+
+ def get_bucket(self, bucket_name, credentials):
+ raise NotImplementedError
+
+ def check_bucket_eq(self, zone, bucket_name):
+ raise NotImplementedError
+
+class ZoneGroup(SystemObject, SystemObject.CreateDelete, SystemObject.GetSet, SystemObject.Modify):
+ def __init__(self, name, period = None, data = None, zonegroup_id = None, zones = None, master_zone = None):
+ self.name = name
+ self.period = period
+ self.zones = zones or []
+ self.master_zone = master_zone
+ super(ZoneGroup, self).__init__(data, zonegroup_id)
+ self.rw_zones = []
+ self.ro_zones = []
+ self.zones_by_type = {}
+ for z in self.zones:
+ if z.is_read_only():
+ self.ro_zones.append(z)
+ else:
+ self.rw_zones.append(z)
+
+ def zonegroup_arg(self):
+ """ command-line argument to specify this zonegroup """
+ return ['--rgw-zonegroup', self.name]
+
+ def zonegroup_args(self):
+ """ command-line arguments to specify this zonegroup/realm """
+ args = self.zonegroup_arg()
+ realm = self.realm()
+ if realm:
+ args += realm.realm_arg()
+ return args
+
+ def build_command(self, command):
+ """ build a command line for the given command and args """
+ return ['zonegroup', command] + self.zonegroup_args()
+
+ def zone_by_id(self, zone_id):
+ """ return the matching zone by id """
+ for zone in self.zones:
+ if zone.id == zone_id:
+ return zone
+ return None
+
+ def load_from_json(self, data):
+ """ load the zonegroup from json """
+ self.id = data['id']
+ self.name = data['name']
+ master_id = data['master_zone']
+ if not self.master_zone or master_id != self.master_zone.id:
+ self.master_zone = self.zone_by_id(master_id)
+
+ def add(self, cluster, zone, args = None, **kwargs):
+ """ add an existing zone to the zonegroup """
+ args = zone.zone_arg() + (args or [])
+ data, r = self.json_command(cluster, 'add', args, **kwargs)
+ if r == 0:
+ zone.zonegroup = self
+ self.zones.append(zone)
+ return data, r
+
+ def remove(self, cluster, zone, args = None, **kwargs):
+ """ remove an existing zone from the zonegroup """
+ args = zone.zone_arg() + (args or [])
+ data, r = self.json_command(cluster, 'remove', args, **kwargs)
+ if r == 0:
+ zone.zonegroup = None
+ self.zones.remove(zone)
+ return data, r
+
+ def realm(self):
+ return self.period.realm if self.period else None
+
+class Period(SystemObject, SystemObject.Get):
+ def __init__(self, realm = None, data = None, period_id = None, zonegroups = None, master_zonegroup = None):
+ self.realm = realm
+ self.zonegroups = zonegroups or []
+ self.master_zonegroup = master_zonegroup
+ super(Period, self).__init__(data, period_id)
+
+ def zonegroup_by_id(self, zonegroup_id):
+ """ return the matching zonegroup by id """
+ for zonegroup in self.zonegroups:
+ if zonegroup.id == zonegroup_id:
+ return zonegroup
+ return None
+
+ def build_command(self, command):
+ """ build a command line for the given command and args """
+ return ['period', command]
+
+ def load_from_json(self, data):
+ """ load the period from json """
+ self.id = data['id']
+ master_id = data['master_zonegroup']
+ if not self.master_zonegroup or master_id != self.master_zonegroup.id:
+ self.master_zonegroup = self.zonegroup_by_id(master_id)
+
+ def update(self, zone, args = None, **kwargs):
+ """ run 'radosgw-admin period update' on the given zone """
+ assert(zone.cluster)
+ args = zone.zone_args() + (args or [])
+ if kwargs.pop('commit', False):
+ args.append('--commit')
+ return self.json_command(zone.cluster, 'update', args, **kwargs)
+
+ def commit(self, zone, args = None, **kwargs):
+ """ run 'radosgw-admin period commit' on the given zone """
+ assert(zone.cluster)
+ args = zone.zone_args() + (args or [])
+ return self.json_command(zone.cluster, 'commit', args, **kwargs)
+
+class Realm(SystemObject, SystemObject.CreateDelete, SystemObject.GetSet):
+ def __init__(self, name, period = None, data = None, realm_id = None):
+ self.name = name
+ self.current_period = period
+ super(Realm, self).__init__(data, realm_id)
+
+ def realm_arg(self):
+ """ return the command-line arguments that specify this realm """
+ return ['--rgw-realm', self.name]
+
+ def build_command(self, command):
+ """ build a command line for the given command and args """
+ return ['realm', command] + self.realm_arg()
+
+ def load_from_json(self, data):
+ """ load the realm from json """
+ self.id = data['id']
+
+ def pull(self, cluster, gateway, credentials, args = [], **kwargs):
+ """ pull an existing realm from the given gateway """
+ args += ['--url', gateway.endpoint()]
+ args += credentials.credential_args()
+ return self.json_command(cluster, 'pull', args, **kwargs)
+
+ def master_zonegroup(self):
+ """ return the current period's master zonegroup """
+ if self.current_period is None:
+ return None
+ return self.current_period.master_zonegroup
+
+ def meta_master_zone(self):
+ """ return the current period's metadata master zone """
+ zonegroup = self.master_zonegroup()
+ if zonegroup is None:
+ return None
+ return zonegroup.master_zone
+
+class Credentials:
+ def __init__(self, access_key, secret):
+ self.access_key = access_key
+ self.secret = secret
+
+ def credential_args(self):
+ return ['--access-key', self.access_key, '--secret', self.secret]
+
+class User(SystemObject):
+ def __init__(self, uid, data = None, name = None, credentials = None, tenant = None):
+ self.name = name
+ self.credentials = credentials or []
+ self.tenant = tenant
+ super(User, self).__init__(data, uid)
+
+ def user_arg(self):
+ """ command-line argument to specify this user """
+ args = ['--uid', self.id]
+ if self.tenant:
+ args += ['--tenant', self.tenant]
+ return args
+
+ def build_command(self, command):
+ """ build a command line for the given command and args """
+ return ['user', command] + self.user_arg()
+
+ def load_from_json(self, data):
+ """ load the user from json """
+ self.id = data['user_id']
+ self.name = data['display_name']
+ self.credentials = [Credentials(k['access_key'], k['secret_key']) for k in data['keys']]
+
+ def create(self, zone, args = None, **kwargs):
+ """ create the user with the given arguments """
+ assert(zone.cluster)
+ args = zone.zone_args() + (args or [])
+ return self.json_command(zone.cluster, 'create', args, **kwargs)
+
+ def info(self, zone, args = None, **kwargs):
+ """ read the user from storage """
+ assert(zone.cluster)
+ args = zone.zone_args() + (args or [])
+ kwargs['read_only'] = True
+ return self.json_command(zone.cluster, 'info', args, **kwargs)
+
+ def delete(self, zone, args = None, **kwargs):
+ """ delete the user """
+ assert(zone.cluster)
+ args = zone.zone_args() + (args or [])
+ return self.command(zone.cluster, 'delete', args, **kwargs)
diff --git a/src/test/rgw/rgw_multi/tests.py b/src/test/rgw/rgw_multi/tests.py
new file mode 100644
index 000000000..156fac12e
--- /dev/null
+++ b/src/test/rgw/rgw_multi/tests.py
@@ -0,0 +1,2861 @@
+import json
+import random
+import string
+import sys
+import time
+import logging
+import errno
+import dateutil.parser
+
+from itertools import combinations
+from itertools import zip_longest
+from io import StringIO
+
+import boto
+import boto.s3.connection
+from boto.s3.website import WebsiteConfiguration
+from boto.s3.cors import CORSConfiguration
+
+from nose.tools import eq_ as eq
+from nose.tools import assert_not_equal, assert_equal
+from nose.plugins.attrib import attr
+from nose.plugins.skip import SkipTest
+
+from .multisite import Zone, ZoneGroup, Credentials
+
+from .conn import get_gateway_connection
+from .tools import assert_raises
+
+class Config:
+ """ test configuration """
+ def __init__(self, **kwargs):
+ # by default, wait up to 5 minutes before giving up on a sync checkpoint
+ self.checkpoint_retries = kwargs.get('checkpoint_retries', 60)
+ self.checkpoint_delay = kwargs.get('checkpoint_delay', 5)
+ # allow some time for realm reconfiguration after changing master zone
+ self.reconfigure_delay = kwargs.get('reconfigure_delay', 5)
+ self.tenant = kwargs.get('tenant', '')
+
+# rgw multisite tests, written against the interfaces provided in rgw_multi.
+# these tests must be initialized and run by another module that provides
+# implementations of these interfaces by calling init_multi()
+realm = None
+user = None
+config = None
+def init_multi(_realm, _user, _config=None):
+ global realm
+ realm = _realm
+ global user
+ user = _user
+ global config
+ config = _config or Config()
+ realm_meta_checkpoint(realm)
+
+def get_user():
+ return user.id if user is not None else ''
+
+def get_tenant():
+ return config.tenant if config is not None and config.tenant is not None else ''
+
+def get_realm():
+ return realm
+
+log = logging.getLogger('rgw_multi.tests')
+
+num_buckets = 0
+run_prefix=''.join(random.choice(string.ascii_lowercase) for _ in range(6))
+
+num_roles = 0
+
+def get_zone_connection(zone, credentials):
+ """ connect to the zone's first gateway """
+ if isinstance(credentials, list):
+ credentials = credentials[0]
+ return get_gateway_connection(zone.gateways[0], credentials)
+
+def mdlog_list(zone, period = None):
+ cmd = ['mdlog', 'list']
+ if period:
+ cmd += ['--period', period]
+ (mdlog_json, _) = zone.cluster.admin(cmd, read_only=True)
+ return json.loads(mdlog_json)
+
+def mdlog_autotrim(zone):
+ zone.cluster.admin(['mdlog', 'autotrim'])
+
+def datalog_list(zone, args = None):
+ cmd = ['datalog', 'list'] + (args or [])
+ (datalog_json, _) = zone.cluster.admin(cmd, read_only=True)
+ return json.loads(datalog_json)
+
+def datalog_status(zone):
+ cmd = ['datalog', 'status']
+ (datalog_json, _) = zone.cluster.admin(cmd, read_only=True)
+ return json.loads(datalog_json)
+
+def datalog_autotrim(zone):
+ zone.cluster.admin(['datalog', 'autotrim'])
+
+def bilog_list(zone, bucket, args = None):
+ cmd = ['bilog', 'list', '--bucket', bucket] + (args or [])
+ cmd += ['--tenant', config.tenant, '--uid', user.name] if config.tenant else []
+ bilog, _ = zone.cluster.admin(cmd, read_only=True)
+ return json.loads(bilog)
+
+def bilog_autotrim(zone, args = None):
+ zone.cluster.admin(['bilog', 'autotrim'] + (args or []))
+
+def bucket_layout(zone, bucket, args = None):
+ (bl_output,_) = zone.cluster.admin(['bucket', 'layout', '--bucket', bucket] + (args or []))
+ return json.loads(bl_output)
+
+def parse_meta_sync_status(meta_sync_status_json):
+ log.debug('current meta sync status=%s', meta_sync_status_json)
+ sync_status = json.loads(meta_sync_status_json)
+
+ sync_info = sync_status['sync_status']['info']
+ global_sync_status = sync_info['status']
+ num_shards = sync_info['num_shards']
+ period = sync_info['period']
+ realm_epoch = sync_info['realm_epoch']
+
+ sync_markers=sync_status['sync_status']['markers']
+ log.debug('sync_markers=%s', sync_markers)
+ assert(num_shards == len(sync_markers))
+
+ markers={}
+ for i in range(num_shards):
+ # get marker, only if it's an incremental marker for the same realm epoch
+ if realm_epoch > sync_markers[i]['val']['realm_epoch'] or sync_markers[i]['val']['state'] == 0:
+ markers[i] = ''
+ else:
+ markers[i] = sync_markers[i]['val']['marker']
+
+ return period, realm_epoch, num_shards, markers
+
+def meta_sync_status(zone):
+ for _ in range(config.checkpoint_retries):
+ cmd = ['metadata', 'sync', 'status'] + zone.zone_args()
+ meta_sync_status_json, retcode = zone.cluster.admin(cmd, check_retcode=False, read_only=True)
+ if retcode == 0:
+ return parse_meta_sync_status(meta_sync_status_json)
+ assert(retcode == 2) # ENOENT
+ time.sleep(config.checkpoint_delay)
+
+ assert False, 'failed to read metadata sync status for zone=%s' % zone.name
+
+def meta_master_log_status(master_zone):
+ cmd = ['mdlog', 'status'] + master_zone.zone_args()
+ mdlog_status_json, retcode = master_zone.cluster.admin(cmd, read_only=True)
+ mdlog_status = json.loads(mdlog_status_json)
+
+ markers = {i: s['marker'] for i, s in enumerate(mdlog_status)}
+ log.debug('master meta markers=%s', markers)
+ return markers
+
+def compare_meta_status(zone, log_status, sync_status):
+ if len(log_status) != len(sync_status):
+ log.error('len(log_status)=%d, len(sync_status)=%d', len(log_status), len(sync_status))
+ return False
+
+ msg = ''
+ for i, l, s in zip(log_status, log_status.values(), sync_status.values()):
+ if l > s:
+ if len(msg):
+ msg += ', '
+ msg += 'shard=' + str(i) + ' master=' + l + ' target=' + s
+
+ if len(msg) > 0:
+ log.warning('zone %s behind master: %s', zone.name, msg)
+ return False
+
+ return True
+
+def zone_meta_checkpoint(zone, meta_master_zone = None, master_status = None):
+ if not meta_master_zone:
+ meta_master_zone = zone.realm().meta_master_zone()
+ if not master_status:
+ master_status = meta_master_log_status(meta_master_zone)
+
+ current_realm_epoch = realm.current_period.data['realm_epoch']
+
+ log.info('starting meta checkpoint for zone=%s', zone.name)
+
+ for _ in range(config.checkpoint_retries):
+ period, realm_epoch, num_shards, sync_status = meta_sync_status(zone)
+ if realm_epoch < current_realm_epoch:
+ log.warning('zone %s is syncing realm epoch=%d, behind current realm epoch=%d',
+ zone.name, realm_epoch, current_realm_epoch)
+ else:
+ log.debug('log_status=%s', master_status)
+ log.debug('sync_status=%s', sync_status)
+ if compare_meta_status(zone, master_status, sync_status):
+ log.info('finish meta checkpoint for zone=%s', zone.name)
+ return
+
+ time.sleep(config.checkpoint_delay)
+ assert False, 'failed meta checkpoint for zone=%s' % zone.name
+
+def zonegroup_meta_checkpoint(zonegroup, meta_master_zone = None, master_status = None):
+ if not meta_master_zone:
+ meta_master_zone = zonegroup.realm().meta_master_zone()
+ if not master_status:
+ master_status = meta_master_log_status(meta_master_zone)
+
+ for zone in zonegroup.zones:
+ if zone == meta_master_zone:
+ continue
+ zone_meta_checkpoint(zone, meta_master_zone, master_status)
+
+def realm_meta_checkpoint(realm):
+ log.info('meta checkpoint')
+
+ meta_master_zone = realm.meta_master_zone()
+ master_status = meta_master_log_status(meta_master_zone)
+
+ for zonegroup in realm.current_period.zonegroups:
+ zonegroup_meta_checkpoint(zonegroup, meta_master_zone, master_status)
+
+def parse_data_sync_status(data_sync_status_json):
+ log.debug('current data sync status=%s', data_sync_status_json)
+ sync_status = json.loads(data_sync_status_json)
+
+ global_sync_status=sync_status['sync_status']['info']['status']
+ num_shards=sync_status['sync_status']['info']['num_shards']
+
+ sync_markers=sync_status['sync_status']['markers']
+ log.debug('sync_markers=%s', sync_markers)
+ assert(num_shards == len(sync_markers))
+
+ markers={}
+ for i in range(num_shards):
+ markers[i] = sync_markers[i]['val']['marker']
+
+ return (num_shards, markers)
+
+def data_sync_status(target_zone, source_zone):
+ if target_zone == source_zone:
+ return None
+
+ for _ in range(config.checkpoint_retries):
+ cmd = ['data', 'sync', 'status'] + target_zone.zone_args()
+ cmd += ['--source-zone', source_zone.name]
+ data_sync_status_json, retcode = target_zone.cluster.admin(cmd, check_retcode=False, read_only=True)
+ if retcode == 0:
+ return parse_data_sync_status(data_sync_status_json)
+
+ assert(retcode == 2) # ENOENT
+ time.sleep(config.checkpoint_delay)
+
+ assert False, 'failed to read data sync status for target_zone=%s source_zone=%s' % \
+ (target_zone.name, source_zone.name)
+
+def bucket_sync_status(target_zone, source_zone, bucket_name):
+ if target_zone == source_zone:
+ return None
+
+ cmd = ['bucket', 'sync', 'markers'] + target_zone.zone_args()
+ cmd += ['--source-zone', source_zone.name]
+ cmd += ['--bucket', bucket_name]
+ cmd += ['--tenant', config.tenant, '--uid', user.name] if config.tenant else []
+ while True:
+ bucket_sync_status_json, retcode = target_zone.cluster.admin(cmd, check_retcode=False, read_only=True)
+ if retcode == 0:
+ break
+
+ assert(retcode == 2) # ENOENT
+
+ sync_status = json.loads(bucket_sync_status_json)
+
+ markers={}
+ for entry in sync_status:
+ val = entry['val']
+ pos = val['inc_marker']['position'].split('#')[-1] # get rid of shard id; e.g., 6#00000000002.132.3 -> 00000000002.132.3
+ markers[entry['key']] = pos
+
+ return markers
+
+def data_source_log_status(source_zone):
+ source_cluster = source_zone.cluster
+ cmd = ['datalog', 'status'] + source_zone.zone_args()
+ datalog_status_json, retcode = source_cluster.admin(cmd, read_only=True)
+ datalog_status = json.loads(datalog_status_json)
+
+ markers = {i: s['marker'] for i, s in enumerate(datalog_status)}
+ log.debug('data markers for zone=%s markers=%s', source_zone.name, markers)
+ return markers
+
+def bucket_source_log_status(source_zone, bucket_name):
+ cmd = ['bilog', 'status'] + source_zone.zone_args()
+ cmd += ['--bucket', bucket_name]
+ cmd += ['--tenant', config.tenant, '--uid', user.name] if config.tenant else []
+ source_cluster = source_zone.cluster
+ bilog_status_json, retcode = source_cluster.admin(cmd, read_only=True)
+ bilog_status = json.loads(bilog_status_json)
+
+ m={}
+ markers={}
+ try:
+ m = bilog_status['markers']
+ except:
+ pass
+
+ for s in m:
+ key = s['key']
+ val = s['val']
+ markers[key] = val
+
+ log.debug('bilog markers for zone=%s bucket=%s markers=%s', source_zone.name, bucket_name, markers)
+ return markers
+
+def compare_data_status(target_zone, source_zone, log_status, sync_status):
+ if len(log_status) != len(sync_status):
+ log.error('len(log_status)=%d len(sync_status)=%d', len(log_status), len(sync_status))
+ return False
+
+ msg = ''
+ for i, l, s in zip(log_status, log_status.values(), sync_status.values()):
+ if l > s:
+ if len(msg):
+ msg += ', '
+ msg += 'shard=' + str(i) + ' master=' + l + ' target=' + s
+
+ if len(msg) > 0:
+ log.warning('data of zone %s behind zone %s: %s', target_zone.name, source_zone.name, msg)
+ return False
+
+ return True
+
+def compare_bucket_status(target_zone, source_zone, bucket_name, log_status, sync_status):
+ if len(log_status) != len(sync_status):
+ log.error('len(log_status)=%d len(sync_status)=%d', len(log_status), len(sync_status))
+ return False
+
+ msg = ''
+ for i, l, s in zip(log_status, log_status.values(), sync_status.values()):
+ if l > s:
+ if len(msg):
+ msg += ', '
+ msg += 'shard=' + str(i) + ' master=' + l + ' target=' + s
+
+ if len(msg) > 0:
+ log.warning('bucket %s zone %s behind zone %s: %s', bucket_name, target_zone.name, source_zone.name, msg)
+ return False
+
+ return True
+
+def zone_data_checkpoint(target_zone, source_zone):
+ if not target_zone.syncs_from(source_zone.name):
+ return
+
+ log_status = data_source_log_status(source_zone)
+ log.info('starting data checkpoint for target_zone=%s source_zone=%s', target_zone.name, source_zone.name)
+
+ for _ in range(config.checkpoint_retries):
+ num_shards, sync_status = data_sync_status(target_zone, source_zone)
+
+ log.debug('log_status=%s', log_status)
+ log.debug('sync_status=%s', sync_status)
+
+ if compare_data_status(target_zone, source_zone, log_status, sync_status):
+ log.info('finished data checkpoint for target_zone=%s source_zone=%s',
+ target_zone.name, source_zone.name)
+ return
+ time.sleep(config.checkpoint_delay)
+
+ assert False, 'failed data checkpoint for target_zone=%s source_zone=%s' % \
+ (target_zone.name, source_zone.name)
+
+def zonegroup_data_checkpoint(zonegroup_conns):
+ for source_conn in zonegroup_conns.rw_zones:
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+ log.debug('data checkpoint: source=%s target=%s', source_conn.zone.name, target_conn.zone.name)
+ zone_data_checkpoint(target_conn.zone, source_conn.zone)
+
+def zone_bucket_checkpoint(target_zone, source_zone, bucket_name):
+ if not target_zone.syncs_from(source_zone.name):
+ return
+
+ cmd = ['bucket', 'sync', 'checkpoint']
+ cmd += ['--bucket', bucket_name, '--source-zone', source_zone.name]
+ retry_delay_ms = config.checkpoint_delay * 1000
+ timeout_sec = config.checkpoint_retries * config.checkpoint_delay
+ cmd += ['--retry-delay-ms', str(retry_delay_ms), '--timeout-sec', str(timeout_sec)]
+ cmd += target_zone.zone_args()
+ target_zone.cluster.admin(cmd, debug_rgw=1)
+
+def zonegroup_bucket_checkpoint(zonegroup_conns, bucket_name):
+ for source_conn in zonegroup_conns.rw_zones:
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+ log.debug('bucket checkpoint: source=%s target=%s bucket=%s', source_conn.zone.name, target_conn.zone.name, bucket_name)
+ zone_bucket_checkpoint(target_conn.zone, source_conn.zone, bucket_name)
+ for source_conn, target_conn in combinations(zonegroup_conns.zones, 2):
+ if target_conn.zone.has_buckets():
+ target_conn.check_bucket_eq(source_conn, bucket_name)
+
+def set_master_zone(zone):
+ zone.modify(zone.cluster, ['--master'])
+ zonegroup = zone.zonegroup
+ zonegroup.period.update(zone, commit=True)
+ zonegroup.master_zone = zone
+ log.info('Set master zone=%s, waiting %ds for reconfiguration..', zone.name, config.reconfigure_delay)
+ time.sleep(config.reconfigure_delay)
+
+def set_sync_from_all(zone, flag):
+ s = 'true' if flag else 'false'
+ zone.modify(zone.cluster, ['--sync-from-all={}'.format(s)])
+ zonegroup = zone.zonegroup
+ zonegroup.period.update(zone, commit=True)
+ log.info('Set sync_from_all flag on zone %s to %s', zone.name, s)
+ time.sleep(config.reconfigure_delay)
+
+def set_redirect_zone(zone, redirect_zone):
+ id_str = redirect_zone.id if redirect_zone else ''
+ zone.modify(zone.cluster, ['--redirect-zone={}'.format(id_str)])
+ zonegroup = zone.zonegroup
+ zonegroup.period.update(zone, commit=True)
+ log.info('Set redirect_zone zone %s to "%s"', zone.name, id_str)
+ time.sleep(config.reconfigure_delay)
+
+def enable_bucket_sync(zone, bucket_name):
+ cmd = ['bucket', 'sync', 'enable', '--bucket', bucket_name] + zone.zone_args()
+ zone.cluster.admin(cmd)
+
+def disable_bucket_sync(zone, bucket_name):
+ cmd = ['bucket', 'sync', 'disable', '--bucket', bucket_name] + zone.zone_args()
+ zone.cluster.admin(cmd)
+
+def check_buckets_sync_status_obj_not_exist(zone, buckets):
+ for _ in range(config.checkpoint_retries):
+ cmd = ['log', 'list'] + zone.zone_arg()
+ log_list, ret = zone.cluster.admin(cmd, check_retcode=False, read_only=True)
+ for bucket in buckets:
+ if log_list.find(':'+bucket+":") >= 0:
+ break
+ else:
+ return
+ time.sleep(config.checkpoint_delay)
+ assert False
+
+def gen_bucket_name():
+ global num_buckets
+
+ num_buckets += 1
+ return run_prefix + '-' + str(num_buckets)
+
+def gen_role_name():
+ global num_roles
+
+ num_roles += 1
+ return "roles" + '-' + run_prefix + '-' + str(num_roles)
+
+class ZonegroupConns:
+ def __init__(self, zonegroup):
+ self.zonegroup = zonegroup
+ self.zones = []
+ self.ro_zones = []
+ self.rw_zones = []
+ self.master_zone = None
+
+ for z in zonegroup.zones:
+ zone_conn = z.get_conn(user.credentials)
+ self.zones.append(zone_conn)
+ if z.is_read_only():
+ self.ro_zones.append(zone_conn)
+ else:
+ self.rw_zones.append(zone_conn)
+
+ if z == zonegroup.master_zone:
+ self.master_zone = zone_conn
+
+def check_all_buckets_exist(zone_conn, buckets):
+ if not zone_conn.zone.has_buckets():
+ return True
+
+ for b in buckets:
+ try:
+ zone_conn.get_bucket(b)
+ except:
+ log.critical('zone %s does not contain bucket %s', zone_conn.zone.name, b)
+ return False
+
+ return True
+
+def check_all_buckets_dont_exist(zone_conn, buckets):
+ if not zone_conn.zone.has_buckets():
+ return True
+
+ for b in buckets:
+ try:
+ zone_conn.get_bucket(b)
+ except:
+ continue
+
+ log.critical('zone %s contains bucket %s', zone.zone, b)
+ return False
+
+ return True
+
+def create_role_per_zone(zonegroup_conns, roles_per_zone = 1):
+ roles = []
+ zone_role = []
+ for zone in zonegroup_conns.rw_zones:
+ for i in range(roles_per_zone):
+ role_name = gen_role_name()
+ log.info('create role zone=%s name=%s', zone.name, role_name)
+ policy_document = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"arn:aws:iam:::user/testuser\"]},\"Action\":[\"sts:AssumeRole\"]}]}"
+ role = zone.create_role("", role_name, policy_document, "")
+ roles.append(role_name)
+ zone_role.append((zone, role))
+
+ return roles, zone_role
+
+def create_bucket_per_zone(zonegroup_conns, buckets_per_zone = 1):
+ buckets = []
+ zone_bucket = []
+ for zone in zonegroup_conns.rw_zones:
+ for i in range(buckets_per_zone):
+ bucket_name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', zone.name, bucket_name)
+ bucket = zone.create_bucket(bucket_name)
+ buckets.append(bucket_name)
+ zone_bucket.append((zone, bucket))
+
+ return buckets, zone_bucket
+
+def create_bucket_per_zone_in_realm():
+ buckets = []
+ zone_bucket = []
+ for zonegroup in realm.current_period.zonegroups:
+ zg_conn = ZonegroupConns(zonegroup)
+ b, z = create_bucket_per_zone(zg_conn)
+ buckets.extend(b)
+ zone_bucket.extend(z)
+ return buckets, zone_bucket
+
+def test_bucket_create():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, _ = create_bucket_per_zone(zonegroup_conns)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for zone in zonegroup_conns.zones:
+ assert check_all_buckets_exist(zone, buckets)
+
+def test_bucket_recreate():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, _ = create_bucket_per_zone(zonegroup_conns)
+ zonegroup_meta_checkpoint(zonegroup)
+
+
+ for zone in zonegroup_conns.zones:
+ assert check_all_buckets_exist(zone, buckets)
+
+ # recreate buckets on all zones, make sure they weren't removed
+ for zone in zonegroup_conns.rw_zones:
+ for bucket_name in buckets:
+ bucket = zone.create_bucket(bucket_name)
+
+ for zone in zonegroup_conns.zones:
+ assert check_all_buckets_exist(zone, buckets)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for zone in zonegroup_conns.zones:
+ assert check_all_buckets_exist(zone, buckets)
+
+def test_bucket_remove():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for zone in zonegroup_conns.zones:
+ assert check_all_buckets_exist(zone, buckets)
+
+ for zone, bucket_name in zone_bucket:
+ zone.conn.delete_bucket(bucket_name)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for zone in zonegroup_conns.zones:
+ assert check_all_buckets_dont_exist(zone, buckets)
+
+def get_bucket(zone, bucket_name):
+ return zone.conn.get_bucket(bucket_name)
+
+def get_key(zone, bucket_name, obj_name):
+ b = get_bucket(zone, bucket_name)
+ return b.get_key(obj_name)
+
+def new_key(zone, bucket_name, obj_name):
+ b = get_bucket(zone, bucket_name)
+ return b.new_key(obj_name)
+
+def check_bucket_eq(zone_conn1, zone_conn2, bucket):
+ if zone_conn2.zone.has_buckets():
+ zone_conn2.check_bucket_eq(zone_conn1, bucket.name)
+
+def check_role_eq(zone_conn1, zone_conn2, role):
+ if zone_conn2.zone.has_roles():
+ zone_conn2.check_role_eq(zone_conn1, role['create_role_response']['create_role_result']['role']['role_name'])
+
+def test_object_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ objnames = [ 'myobj', '_myobj', ':', '&' ]
+ content = 'asdasd'
+
+ # don't wait for meta sync just yet
+ for zone, bucket_name in zone_bucket:
+ for objname in objnames:
+ k = new_key(zone, bucket_name, objname)
+ k.set_contents_from_string(content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for source_conn, bucket in zone_bucket:
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+
+ zone_bucket_checkpoint(target_conn.zone, source_conn.zone, bucket.name)
+ check_bucket_eq(source_conn, target_conn, bucket)
+
+def test_object_delete():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ objname = 'myobj'
+ content = 'asdasd'
+
+ # don't wait for meta sync just yet
+ for zone, bucket in zone_bucket:
+ k = new_key(zone, bucket, objname)
+ k.set_contents_from_string(content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # check object exists
+ for source_conn, bucket in zone_bucket:
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+
+ zone_bucket_checkpoint(target_conn.zone, source_conn.zone, bucket.name)
+ check_bucket_eq(source_conn, target_conn, bucket)
+
+ # check object removal
+ for source_conn, bucket in zone_bucket:
+ k = get_key(source_conn, bucket, objname)
+ k.delete()
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+
+ zone_bucket_checkpoint(target_conn.zone, source_conn.zone, bucket.name)
+ check_bucket_eq(source_conn, target_conn, bucket)
+
+def get_latest_object_version(key):
+ for k in key.bucket.list_versions(key.name):
+ if k.is_latest:
+ return k
+ return None
+
+def test_versioned_object_incremental_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ # enable versioning
+ for _, bucket in zone_bucket:
+ bucket.configure_versioning(True)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # upload a dummy object to each bucket and wait for sync. this forces each
+ # bucket to finish a full sync and switch to incremental
+ for source_conn, bucket in zone_bucket:
+ new_key(source_conn, bucket, 'dummy').set_contents_from_string('')
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+ zone_bucket_checkpoint(target_conn.zone, source_conn.zone, bucket.name)
+
+ for _, bucket in zone_bucket:
+ # create and delete multiple versions of an object from each zone
+ for zone_conn in zonegroup_conns.rw_zones:
+ obj = 'obj-' + zone_conn.name
+ k = new_key(zone_conn, bucket, obj)
+
+ k.set_contents_from_string('version1')
+ log.debug('version1 id=%s', k.version_id)
+ # don't delete version1 - this tests that the initial version
+ # doesn't get squashed into later versions
+
+ # create and delete the following object versions to test that
+ # the operations don't race with each other during sync
+ k.set_contents_from_string('version2')
+ log.debug('version2 id=%s', k.version_id)
+ k.bucket.delete_key(obj, version_id=k.version_id)
+
+ k.set_contents_from_string('version3')
+ log.debug('version3 id=%s', k.version_id)
+ k.bucket.delete_key(obj, version_id=k.version_id)
+
+ for _, bucket in zone_bucket:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ for _, bucket in zone_bucket:
+ # overwrite the acls to test that metadata-only entries are applied
+ for zone_conn in zonegroup_conns.rw_zones:
+ obj = 'obj-' + zone_conn.name
+ k = new_key(zone_conn, bucket.name, obj)
+ v = get_latest_object_version(k)
+ v.make_public()
+
+ for _, bucket in zone_bucket:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def test_concurrent_versioned_object_incremental_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ zone = zonegroup_conns.rw_zones[0]
+
+ # create a versioned bucket
+ bucket = zone.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ bucket.configure_versioning(True)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # upload a dummy object and wait for sync. this forces each zone to finish
+ # a full sync and switch to incremental
+ new_key(zone, bucket, 'dummy').set_contents_from_string('')
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ # create several concurrent versions on each zone and let them race to sync
+ obj = 'obj'
+ for i in range(10):
+ for zone_conn in zonegroup_conns.rw_zones:
+ k = new_key(zone_conn, bucket, obj)
+ k.set_contents_from_string('version1')
+ log.debug('zone=%s version=%s', zone_conn.zone.name, k.version_id)
+
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+def test_version_suspended_incremental_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ zone = zonegroup_conns.rw_zones[0]
+
+ # create a non-versioned bucket
+ bucket = zone.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # upload an initial object
+ key1 = new_key(zone, bucket, 'obj')
+ key1.set_contents_from_string('')
+ log.debug('created initial version id=%s', key1.version_id)
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ # enable versioning
+ bucket.configure_versioning(True)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # re-upload the object as a new version
+ key2 = new_key(zone, bucket, 'obj')
+ key2.set_contents_from_string('')
+ log.debug('created new version id=%s', key2.version_id)
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ # suspend versioning
+ bucket.configure_versioning(False)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # re-upload the object as a 'null' version
+ key3 = new_key(zone, bucket, 'obj')
+ key3.set_contents_from_string('')
+ log.debug('created null version id=%s', key3.version_id)
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def test_delete_marker_full_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ # enable versioning
+ for _, bucket in zone_bucket:
+ bucket.configure_versioning(True)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for zone, bucket in zone_bucket:
+ # upload an initial object
+ key1 = new_key(zone, bucket, 'obj')
+ key1.set_contents_from_string('')
+
+ # create a delete marker
+ key2 = new_key(zone, bucket, 'obj')
+ key2.delete()
+
+ # wait for full sync
+ for _, bucket in zone_bucket:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def test_suspended_delete_marker_full_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ # enable/suspend versioning
+ for _, bucket in zone_bucket:
+ bucket.configure_versioning(True)
+ bucket.configure_versioning(False)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for zone, bucket in zone_bucket:
+ # upload an initial object
+ key1 = new_key(zone, bucket, 'obj')
+ key1.set_contents_from_string('')
+
+ # create a delete marker
+ key2 = new_key(zone, bucket, 'obj')
+ key2.delete()
+
+ # wait for full sync
+ for _, bucket in zone_bucket:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def test_bucket_versioning():
+ buckets, zone_bucket = create_bucket_per_zone_in_realm()
+ for _, bucket in zone_bucket:
+ bucket.configure_versioning(True)
+ res = bucket.get_versioning_status()
+ key = 'Versioning'
+ assert(key in res and res[key] == 'Enabled')
+
+def test_bucket_acl():
+ buckets, zone_bucket = create_bucket_per_zone_in_realm()
+ for _, bucket in zone_bucket:
+ assert(len(bucket.get_acl().acl.grants) == 1) # single grant on owner
+ bucket.set_acl('public-read')
+ assert(len(bucket.get_acl().acl.grants) == 2) # new grant on AllUsers
+
+def test_bucket_cors():
+ buckets, zone_bucket = create_bucket_per_zone_in_realm()
+ for _, bucket in zone_bucket:
+ cors_cfg = CORSConfiguration()
+ cors_cfg.add_rule(['DELETE'], 'https://www.example.com', allowed_header='*', max_age_seconds=3000)
+ bucket.set_cors(cors_cfg)
+ assert(bucket.get_cors().to_xml() == cors_cfg.to_xml())
+
+def test_bucket_delete_notempty():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for zone_conn, bucket_name in zone_bucket:
+ # upload an object to each bucket on its own zone
+ conn = zone_conn.get_connection()
+ bucket = conn.get_bucket(bucket_name)
+ k = bucket.new_key('foo')
+ k.set_contents_from_string('bar')
+ # attempt to delete the bucket before this object can sync
+ try:
+ conn.delete_bucket(bucket_name)
+ except boto.exception.S3ResponseError as e:
+ assert(e.error_code == 'BucketNotEmpty')
+ continue
+ assert False # expected 409 BucketNotEmpty
+
+ # assert that each bucket still exists on the master
+ c1 = zonegroup_conns.master_zone.conn
+ for _, bucket_name in zone_bucket:
+ assert c1.get_bucket(bucket_name)
+
+def test_multi_period_incremental_sync():
+ zonegroup = realm.master_zonegroup()
+ if len(zonegroup.zones) < 3:
+ raise SkipTest("test_multi_period_incremental_sync skipped. Requires 3 or more zones in master zonegroup.")
+
+ # periods to include in mdlog comparison
+ mdlog_periods = [realm.current_period.id]
+
+ # create a bucket in each zone
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ z1, z2, z3 = zonegroup.zones[0:3]
+ assert(z1 == zonegroup.master_zone)
+
+ # kill zone 3 gateways to freeze sync status to incremental in first period
+ z3.stop()
+
+ # change master to zone 2 -> period 2
+ set_master_zone(z2)
+ mdlog_periods += [realm.current_period.id]
+
+ for zone_conn, _ in zone_bucket:
+ if zone_conn.zone == z3:
+ continue
+ bucket_name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', zone_conn.name, bucket_name)
+ bucket = zone_conn.conn.create_bucket(bucket_name)
+ buckets.append(bucket_name)
+
+ # wait for zone 1 to sync
+ zone_meta_checkpoint(z1)
+
+ # change master back to zone 1 -> period 3
+ set_master_zone(z1)
+ mdlog_periods += [realm.current_period.id]
+
+ for zone_conn, bucket_name in zone_bucket:
+ if zone_conn.zone == z3:
+ continue
+ bucket_name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', zone_conn.name, bucket_name)
+ zone_conn.conn.create_bucket(bucket_name)
+ buckets.append(bucket_name)
+
+ # restart zone 3 gateway and wait for sync
+ z3.start()
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # verify that we end up with the same objects
+ for bucket_name in buckets:
+ for source_conn, _ in zone_bucket:
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+
+ if target_conn.zone.has_buckets():
+ target_conn.check_bucket_eq(source_conn, bucket_name)
+
+ # verify that mdlogs are not empty and match for each period
+ for period in mdlog_periods:
+ master_mdlog = mdlog_list(z1, period)
+ assert len(master_mdlog) > 0
+ for zone in zonegroup.zones:
+ if zone == z1:
+ continue
+ mdlog = mdlog_list(zone, period)
+ assert len(mdlog) == len(master_mdlog)
+
+ # autotrim mdlogs for master zone
+ mdlog_autotrim(z1)
+
+ # autotrim mdlogs for peers
+ for zone in zonegroup.zones:
+ if zone == z1:
+ continue
+ mdlog_autotrim(zone)
+
+ # verify that mdlogs are empty for each period
+ for period in mdlog_periods:
+ for zone in zonegroup.zones:
+ mdlog = mdlog_list(zone, period)
+ assert len(mdlog) == 0
+
+def test_datalog_autotrim():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ # upload an object to each zone to generate a datalog entry
+ for zone, bucket in zone_bucket:
+ k = new_key(zone, bucket.name, 'key')
+ k.set_contents_from_string('body')
+
+ # wait for metadata and data sync to catch up
+ zonegroup_meta_checkpoint(zonegroup)
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+ # trim each datalog
+ for zone, _ in zone_bucket:
+ # read max markers for each shard
+ status = datalog_status(zone.zone)
+
+ datalog_autotrim(zone.zone)
+
+ for shard_id, shard_status in enumerate(status):
+ try:
+ before_trim = dateutil.parser.isoparse(shard_status['last_update'])
+ except: # empty timestamps look like "0.000000" and will fail here
+ continue
+ entries = datalog_list(zone.zone, ['--shard-id', str(shard_id), '--max-entries', '1'])
+ if not len(entries):
+ continue
+ after_trim = dateutil.parser.isoparse(entries[0]['timestamp'])
+ assert before_trim < after_trim, "any datalog entries must be newer than trim"
+
+def test_multi_zone_redirect():
+ zonegroup = realm.master_zonegroup()
+ if len(zonegroup.rw_zones) < 2:
+ raise SkipTest("test_multi_period_incremental_sync skipped. Requires 3 or more zones in master zonegroup.")
+
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ (zc1, zc2) = zonegroup_conns.rw_zones[0:2]
+
+ z1, z2 = (zc1.zone, zc2.zone)
+
+ set_sync_from_all(z2, False)
+
+ # create a bucket on the first zone
+ bucket_name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', z1.name, bucket_name)
+ bucket = zc1.conn.create_bucket(bucket_name)
+ obj = 'testredirect'
+
+ key = bucket.new_key(obj)
+ data = 'A'*512
+ key.set_contents_from_string(data)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # try to read object from second zone (should fail)
+ bucket2 = get_bucket(zc2, bucket_name)
+ assert_raises(boto.exception.S3ResponseError, bucket2.get_key, obj)
+
+ set_redirect_zone(z2, z1)
+
+ key2 = bucket2.get_key(obj)
+
+ eq(data, key2.get_contents_as_string(encoding='ascii'))
+
+ key = bucket.new_key(obj)
+
+ for x in ['a', 'b', 'c', 'd']:
+ data = x*512
+ key.set_contents_from_string(data)
+ eq(data, key2.get_contents_as_string(encoding='ascii'))
+
+ # revert config changes
+ set_sync_from_all(z2, True)
+ set_redirect_zone(z2, None)
+
+def test_zonegroup_remove():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ if len(zonegroup.zones) < 2:
+ raise SkipTest("test_zonegroup_remove skipped. Requires 2 or more zones in master zonegroup.")
+
+ zonegroup_meta_checkpoint(zonegroup)
+ z1, z2 = zonegroup.zones[0:2]
+ c1, c2 = (z1.cluster, z2.cluster)
+
+ # get admin credentials out of existing zone
+ system_key = z1.data['system_key']
+ admin_creds = Credentials(system_key['access_key'], system_key['secret_key'])
+
+ # create a new zone in zonegroup on c2 and commit
+ zone = Zone('remove', zonegroup, c2)
+ zone.create(c2, admin_creds.credential_args())
+ zonegroup.zones.append(zone)
+ zonegroup.period.update(zone, commit=True)
+
+ zonegroup.remove(c1, zone)
+
+ # another 'zonegroup remove' should fail with ENOENT
+ _, retcode = zonegroup.remove(c1, zone, check_retcode=False)
+ assert(retcode == 2) # ENOENT
+
+ # delete the new zone
+ zone.delete(c2)
+
+ # validate the resulting period
+ zonegroup.period.update(z1, commit=True)
+
+
+def test_zg_master_zone_delete():
+
+ master_zg = realm.master_zonegroup()
+ master_zone = master_zg.master_zone
+
+ assert(len(master_zg.zones) >= 1)
+ master_cluster = master_zg.zones[0].cluster
+
+ rm_zg = ZoneGroup('remove_zg')
+ rm_zg.create(master_cluster)
+
+ rm_zone = Zone('remove', rm_zg, master_cluster)
+ rm_zone.create(master_cluster)
+ master_zg.period.update(master_zone, commit=True)
+
+
+ rm_zone.delete(master_cluster)
+ # Period update: This should now fail as the zone will be the master zone
+ # in that zg
+ _, retcode = master_zg.period.update(master_zone, check_retcode=False)
+ assert(retcode == errno.EINVAL)
+
+ # Proceed to delete the zonegroup as well, previous period now does not
+ # contain a dangling master_zone, this must succeed
+ rm_zg.delete(master_cluster)
+ master_zg.period.update(master_zone, commit=True)
+
+def test_set_bucket_website():
+ buckets, zone_bucket = create_bucket_per_zone_in_realm()
+ for _, bucket in zone_bucket:
+ website_cfg = WebsiteConfiguration(suffix='index.html',error_key='error.html')
+ try:
+ bucket.set_website_configuration(website_cfg)
+ except boto.exception.S3ResponseError as e:
+ if e.error_code == 'MethodNotAllowed':
+ raise SkipTest("test_set_bucket_website skipped. Requires rgw_enable_static_website = 1.")
+ assert(bucket.get_website_configuration_with_xml()[1] == website_cfg.to_xml())
+
+def test_set_bucket_policy():
+ policy = '''{
+ "Version": "2012-10-17",
+ "Statement": [{
+ "Effect": "Allow",
+ "Principal": "*"
+ }]
+}'''
+ buckets, zone_bucket = create_bucket_per_zone_in_realm()
+ for _, bucket in zone_bucket:
+ bucket.set_policy(policy)
+ assert(bucket.get_policy().decode('ascii') == policy)
+
+@attr('bucket_sync_disable')
+def test_bucket_sync_disable():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for bucket_name in buckets:
+ disable_bucket_sync(realm.meta_master_zone(), bucket_name)
+
+ for zone in zonegroup.zones:
+ check_buckets_sync_status_obj_not_exist(zone, buckets)
+
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+@attr('bucket_sync_disable')
+def test_bucket_sync_enable_right_after_disable():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ objnames = ['obj1', 'obj2', 'obj3', 'obj4']
+ content = 'asdasd'
+
+ for zone, bucket in zone_bucket:
+ for objname in objnames:
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string(content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for bucket_name in buckets:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket_name)
+
+ for bucket_name in buckets:
+ disable_bucket_sync(realm.meta_master_zone(), bucket_name)
+ enable_bucket_sync(realm.meta_master_zone(), bucket_name)
+
+ objnames_2 = ['obj5', 'obj6', 'obj7', 'obj8']
+
+ for zone, bucket in zone_bucket:
+ for objname in objnames_2:
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string(content)
+
+ for bucket_name in buckets:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket_name)
+
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+@attr('bucket_sync_disable')
+def test_bucket_sync_disable_enable():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ objnames = [ 'obj1', 'obj2', 'obj3', 'obj4' ]
+ content = 'asdasd'
+
+ for zone, bucket in zone_bucket:
+ for objname in objnames:
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string(content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for bucket_name in buckets:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket_name)
+
+ for bucket_name in buckets:
+ disable_bucket_sync(realm.meta_master_zone(), bucket_name)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ objnames_2 = [ 'obj5', 'obj6', 'obj7', 'obj8' ]
+
+ for zone, bucket in zone_bucket:
+ for objname in objnames_2:
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string(content)
+
+ for bucket_name in buckets:
+ enable_bucket_sync(realm.meta_master_zone(), bucket_name)
+
+ for bucket_name in buckets:
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket_name)
+
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+def test_multipart_object_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns)
+
+ _, bucket = zone_bucket[0]
+
+ # initiate a multipart upload
+ upload = bucket.initiate_multipart_upload('MULTIPART')
+ mp = boto.s3.multipart.MultiPartUpload(bucket)
+ mp.key_name = upload.key_name
+ mp.id = upload.id
+ part_size = 5 * 1024 * 1024 # 5M min part size
+ mp.upload_part_from_file(StringIO('a' * part_size), 1)
+ mp.upload_part_from_file(StringIO('b' * part_size), 2)
+ mp.upload_part_from_file(StringIO('c' * part_size), 3)
+ mp.upload_part_from_file(StringIO('d' * part_size), 4)
+ mp.complete_upload()
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def test_encrypted_object_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ if len(zonegroup.rw_zones) < 2:
+ raise SkipTest("test_zonegroup_remove skipped. Requires 2 or more zones in master zonegroup.")
+
+ (zone1, zone2) = zonegroup_conns.rw_zones[0:2]
+
+ # create a bucket on the first zone
+ bucket_name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', zone1.name, bucket_name)
+ bucket = zone1.conn.create_bucket(bucket_name)
+
+ # upload an object with sse-c encryption
+ sse_c_headers = {
+ 'x-amz-server-side-encryption-customer-algorithm': 'AES256',
+ 'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
+ 'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw=='
+ }
+ key = bucket.new_key('testobj-sse-c')
+ data = 'A'*512
+ key.set_contents_from_string(data, headers=sse_c_headers)
+
+ # upload an object with sse-kms encryption
+ sse_kms_headers = {
+ 'x-amz-server-side-encryption': 'aws:kms',
+ # testkey-1 must be present in 'rgw crypt s3 kms encryption keys' (vstart.sh adds this)
+ 'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-1',
+ }
+ key = bucket.new_key('testobj-sse-kms')
+ key.set_contents_from_string(data, headers=sse_kms_headers)
+
+ # wait for the bucket metadata and data to sync
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_bucket_checkpoint(zone2.zone, zone1.zone, bucket_name)
+
+ # read the encrypted objects from the second zone
+ bucket2 = get_bucket(zone2, bucket_name)
+ key = bucket2.get_key('testobj-sse-c', headers=sse_c_headers)
+ eq(data, key.get_contents_as_string(headers=sse_c_headers, encoding='ascii'))
+
+ key = bucket2.get_key('testobj-sse-kms')
+ eq(data, key.get_contents_as_string(encoding='ascii'))
+
+def test_bucket_index_log_trim():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ zone = zonegroup_conns.rw_zones[0]
+
+ # create a test bucket, upload some objects, and wait for sync
+ def make_test_bucket():
+ name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', zone.name, name)
+ bucket = zone.conn.create_bucket(name)
+ for objname in ('a', 'b', 'c', 'd'):
+ k = new_key(zone, name, objname)
+ k.set_contents_from_string('foo')
+ zonegroup_meta_checkpoint(zonegroup)
+ zonegroup_bucket_checkpoint(zonegroup_conns, name)
+ return bucket
+
+ # create a 'cold' bucket
+ cold_bucket = make_test_bucket()
+
+ # trim with max-buckets=0 to clear counters for cold bucket. this should
+ # prevent it from being considered 'active' by the next autotrim
+ bilog_autotrim(zone.zone, [
+ '--rgw-sync-log-trim-max-buckets', '0',
+ ])
+
+ # create an 'active' bucket
+ active_bucket = make_test_bucket()
+
+ # trim with max-buckets=1 min-cold-buckets=0 to trim active bucket only
+ bilog_autotrim(zone.zone, [
+ '--rgw-sync-log-trim-max-buckets', '1',
+ '--rgw-sync-log-trim-min-cold-buckets', '0',
+ ])
+
+ # verify active bucket has empty bilog
+ active_bilog = bilog_list(zone.zone, active_bucket.name)
+ assert(len(active_bilog) == 0)
+
+ # verify cold bucket has nonempty bilog
+ cold_bilog = bilog_list(zone.zone, cold_bucket.name)
+ assert(len(cold_bilog) > 0)
+
+ # trim with min-cold-buckets=999 to trim all buckets
+ bilog_autotrim(zone.zone, [
+ '--rgw-sync-log-trim-max-buckets', '999',
+ '--rgw-sync-log-trim-min-cold-buckets', '999',
+ ])
+
+ # verify cold bucket has empty bilog
+ cold_bilog = bilog_list(zone.zone, cold_bucket.name)
+ assert(len(cold_bilog) == 0)
+
+def test_bucket_reshard_index_log_trim():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ zone = zonegroup_conns.rw_zones[0]
+
+ # create a test bucket, upload some objects, and wait for sync
+ def make_test_bucket():
+ name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', zone.name, name)
+ bucket = zone.conn.create_bucket(name)
+ for objname in ('a', 'b', 'c', 'd'):
+ k = new_key(zone, name, objname)
+ k.set_contents_from_string('foo')
+ zonegroup_meta_checkpoint(zonegroup)
+ zonegroup_bucket_checkpoint(zonegroup_conns, name)
+ return bucket
+
+ # create a 'test' bucket
+ test_bucket = make_test_bucket()
+
+ # checking bucket layout before resharding
+ json_obj_1 = bucket_layout(zone.zone, test_bucket.name)
+ assert(len(json_obj_1['layout']['logs']) == 1)
+
+ first_gen = json_obj_1['layout']['current_index']['gen']
+
+ before_reshard_bilog = bilog_list(zone.zone, test_bucket.name, ['--gen', str(first_gen)])
+ assert(len(before_reshard_bilog) == 4)
+
+ # Resharding the bucket
+ zone.zone.cluster.admin(['bucket', 'reshard',
+ '--bucket', test_bucket.name,
+ '--num-shards', '3',
+ '--yes-i-really-mean-it'])
+
+ # checking bucket layout after 1st resharding
+ json_obj_2 = bucket_layout(zone.zone, test_bucket.name)
+ assert(len(json_obj_2['layout']['logs']) == 2)
+
+ second_gen = json_obj_2['layout']['current_index']['gen']
+
+ after_reshard_bilog = bilog_list(zone.zone, test_bucket.name, ['--gen', str(second_gen)])
+ assert(len(after_reshard_bilog) == 0)
+
+ # upload more objects
+ for objname in ('e', 'f', 'g', 'h'):
+ k = new_key(zone, test_bucket.name, objname)
+ k.set_contents_from_string('foo')
+ zonegroup_bucket_checkpoint(zonegroup_conns, test_bucket.name)
+
+ # Resharding the bucket again
+ zone.zone.cluster.admin(['bucket', 'reshard',
+ '--bucket', test_bucket.name,
+ '--num-shards', '3',
+ '--yes-i-really-mean-it'])
+
+ # checking bucket layout after 2nd resharding
+ json_obj_3 = bucket_layout(zone.zone, test_bucket.name)
+ assert(len(json_obj_3['layout']['logs']) == 3)
+
+ zonegroup_bucket_checkpoint(zonegroup_conns, test_bucket.name)
+
+ bilog_autotrim(zone.zone)
+
+ # checking bucket layout after 1st bilog autotrim
+ json_obj_4 = bucket_layout(zone.zone, test_bucket.name)
+ assert(len(json_obj_4['layout']['logs']) == 2)
+
+ bilog_autotrim(zone.zone)
+
+ # checking bucket layout after 2nd bilog autotrim
+ json_obj_5 = bucket_layout(zone.zone, test_bucket.name)
+ assert(len(json_obj_5['layout']['logs']) == 1)
+
+ bilog_autotrim(zone.zone)
+
+ # upload more objects
+ for objname in ('i', 'j', 'k', 'l'):
+ k = new_key(zone, test_bucket.name, objname)
+ k.set_contents_from_string('foo')
+ zonegroup_bucket_checkpoint(zonegroup_conns, test_bucket.name)
+
+ # verify the bucket has non-empty bilog
+ test_bilog = bilog_list(zone.zone, test_bucket.name)
+ assert(len(test_bilog) > 0)
+
+@attr('bucket_reshard')
+def test_bucket_reshard_incremental():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ zone = zonegroup_conns.rw_zones[0]
+
+ # create a bucket
+ bucket = zone.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # upload some objects
+ for objname in ('a', 'b', 'c', 'd'):
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string('foo')
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ # reshard in each zone
+ for z in zonegroup_conns.rw_zones:
+ z.zone.cluster.admin(['bucket', 'reshard',
+ '--bucket', bucket.name,
+ '--num-shards', '3',
+ '--yes-i-really-mean-it'])
+
+ # upload more objects
+ for objname in ('e', 'f', 'g', 'h'):
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string('foo')
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+@attr('bucket_reshard')
+def test_bucket_reshard_full():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ zone = zonegroup_conns.rw_zones[0]
+
+ # create a bucket
+ bucket = zone.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # stop gateways in other zones so we can force the bucket to full sync
+ for z in zonegroup_conns.rw_zones[1:]:
+ z.zone.stop()
+
+ # use try-finally to restart gateways even if something fails
+ try:
+ # upload some objects
+ for objname in ('a', 'b', 'c', 'd'):
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string('foo')
+
+ # reshard on first zone
+ zone.zone.cluster.admin(['bucket', 'reshard',
+ '--bucket', bucket.name,
+ '--num-shards', '3',
+ '--yes-i-really-mean-it'])
+
+ # upload more objects
+ for objname in ('e', 'f', 'g', 'h'):
+ k = new_key(zone, bucket.name, objname)
+ k.set_contents_from_string('foo')
+ finally:
+ for z in zonegroup_conns.rw_zones[1:]:
+ z.zone.start()
+
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def test_bucket_creation_time():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ zone_buckets = [zone.get_connection().get_all_buckets() for zone in zonegroup_conns.rw_zones]
+ for z1, z2 in combinations(zone_buckets, 2):
+ for a, b in zip(z1, z2):
+ eq(a.name, b.name)
+ eq(a.creation_date, b.creation_date)
+
+def get_bucket_shard_objects(zone, num_shards):
+ """
+ Get one object for each shard of the bucket index log
+ """
+ cmd = ['bucket', 'shard', 'objects'] + zone.zone_args()
+ cmd += ['--num-shards', str(num_shards)]
+ shardobjs_json, ret = zone.cluster.admin(cmd, read_only=True)
+ assert ret == 0
+ shardobjs = json.loads(shardobjs_json)
+ return shardobjs['objs']
+
+def write_most_shards(zone, bucket_name, num_shards):
+ """
+ Write one object to most (but not all) bucket index shards.
+ """
+ objs = get_bucket_shard_objects(zone.zone, num_shards)
+ random.shuffle(objs)
+ del objs[-(len(objs)//10):]
+ for obj in objs:
+ k = new_key(zone, bucket_name, obj)
+ k.set_contents_from_string('foo')
+
+def reshard_bucket(zone, bucket_name, num_shards):
+ """
+ Reshard a bucket
+ """
+ cmd = ['bucket', 'reshard'] + zone.zone_args()
+ cmd += ['--bucket', bucket_name]
+ cmd += ['--num-shards', str(num_shards)]
+ cmd += ['--yes-i-really-mean-it']
+ zone.cluster.admin(cmd)
+
+def get_obj_names(zone, bucket_name, maxobjs):
+ """
+ Get names of objects in a bucket.
+ """
+ cmd = ['bucket', 'list'] + zone.zone_args()
+ cmd += ['--bucket', bucket_name]
+ cmd += ['--max-entries', str(maxobjs)]
+ objs_json, _ = zone.cluster.admin(cmd, read_only=True)
+ objs = json.loads(objs_json)
+ return [o['name'] for o in objs]
+
+def bucket_keys_eq(zone1, zone2, bucket_name):
+ """
+ Ensure that two buckets have the same keys, but get the lists through
+ radosgw-admin rather than S3 so it can be used when radosgw isn't running.
+ Only works for buckets of 10,000 objects since the tests calling it don't
+ need more, and the output from bucket list doesn't have an obvious marker
+ with which to continue.
+ """
+ keys1 = get_obj_names(zone1, bucket_name, 10000)
+ keys2 = get_obj_names(zone2, bucket_name, 10000)
+ for key1, key2 in zip_longest(keys1, keys2):
+ if key1 is None:
+ log.critical('key=%s is missing from zone=%s', key1.name,
+ zone1.name)
+ assert False
+ if key2 is None:
+ log.critical('key=%s is missing from zone=%s', key2.name,
+ zone2.name)
+ assert False
+
+@attr('bucket_reshard')
+def test_bucket_sync_run_basic_incremental():
+ """
+ Create several generations of objects, then run bucket sync
+ run to ensure they're all processed.
+ """
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ primary = zonegroup_conns.rw_zones[0]
+
+ # create a bucket write objects to it and wait for them to sync, ensuring
+ # we are in incremental.
+ bucket = primary.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+ write_most_shards(primary, bucket.name, 11)
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ try:
+ # stop gateways in other zones so we can rely on bucket sync run
+ for secondary in zonegroup_conns.rw_zones[1:]:
+ secondary.zone.stop()
+
+ # build up multiple generations each with some objects written to
+ # them.
+ generations = [17, 19, 23, 29, 31, 37]
+ for num_shards in generations:
+ reshard_bucket(primary.zone, bucket.name, num_shards)
+ write_most_shards(primary, bucket.name, num_shards)
+
+ # bucket sync run on every secondary
+ for secondary in zonegroup_conns.rw_zones[1:]:
+ cmd = ['bucket', 'sync', 'run'] + secondary.zone.zone_args()
+ cmd += ['--bucket', bucket.name, '--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+
+ bucket_keys_eq(primary.zone, secondary.zone, bucket.name)
+
+ finally:
+ # Restart so bucket_checkpoint can actually fetch things from the
+ # secondaries. Put this in a finally block so they restart even on
+ # error.
+ for secondary in zonegroup_conns.rw_zones[1:]:
+ secondary.zone.start()
+
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def trash_bucket(zone, bucket_name):
+ """
+ Remove objects through radosgw-admin, zapping bilog to prevent the deletes
+ from replicating.
+ """
+ objs = get_obj_names(zone, bucket_name, 10000)
+ # Delete the objects
+ for obj in objs:
+ cmd = ['object', 'rm'] + zone.zone_args()
+ cmd += ['--bucket', bucket_name]
+ cmd += ['--object', obj]
+ zone.cluster.admin(cmd)
+
+ # Zap the bilog
+ cmd = ['bilog', 'trim'] + zone.zone_args()
+ cmd += ['--bucket', bucket_name]
+ zone.cluster.admin(cmd)
+
+@attr('bucket_reshard')
+def test_zap_init_bucket_sync_run():
+ """
+ Create several generations of objects, trash them, then run bucket sync init
+ and bucket sync run.
+ """
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ primary = zonegroup_conns.rw_zones[0]
+
+ bucket = primary.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # Write zeroth generation
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * 11}')
+ k.set_contents_from_string('foo')
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ # Write several more generations
+ generations = [17, 19, 23, 29, 31, 37]
+ for num_shards in generations:
+ reshard_bucket(primary.zone, bucket.name, num_shards)
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * num_shards}')
+ k.set_contents_from_string('foo')
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+
+ # Stop gateways, trash bucket, init, sync, and restart for every secondary
+ for secondary in zonegroup_conns.rw_zones[1:]:
+ try:
+ secondary.zone.stop()
+
+ trash_bucket(secondary.zone, bucket.name)
+
+ cmd = ['bucket', 'sync', 'init'] + secondary.zone.zone_args()
+ cmd += ['--bucket', bucket.name]
+ cmd += ['--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+
+ cmd = ['bucket', 'sync', 'run'] + secondary.zone.zone_args()
+ cmd += ['--bucket', bucket.name, '--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+
+ bucket_keys_eq(primary.zone, secondary.zone, bucket.name)
+
+ finally:
+ # Do this as a finally so we bring the zone back up even on error.
+ secondary.zone.start()
+
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+def test_role_sync():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ roles, zone_role = create_role_per_zone(zonegroup_conns)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ for source_conn, role in zone_role:
+ for target_conn in zonegroup_conns.zones:
+ if source_conn.zone == target_conn.zone:
+ continue
+
+ check_role_eq(source_conn, target_conn, role)
+
+@attr('data_sync_init')
+def test_bucket_full_sync_after_data_sync_init():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ primary = zonegroup_conns.rw_zones[0]
+ secondary = zonegroup_conns.rw_zones[1]
+
+ bucket = primary.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ try:
+ # stop secondary zone before it starts a bucket full sync
+ secondary.zone.stop()
+
+ # write some objects that don't sync yet
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * 11}')
+ k.set_contents_from_string('foo')
+
+ cmd = ['data', 'sync', 'init'] + secondary.zone.zone_args()
+ cmd += ['--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+ finally:
+ # Do this as a finally so we bring the zone back up even on error.
+ secondary.zone.start()
+
+ # expect all objects to replicate via 'bucket full sync'
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+@attr('data_sync_init')
+@attr('bucket_reshard')
+def test_resharded_bucket_full_sync_after_data_sync_init():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ primary = zonegroup_conns.rw_zones[0]
+ secondary = zonegroup_conns.rw_zones[1]
+
+ bucket = primary.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ try:
+ # stop secondary zone before it starts a bucket full sync
+ secondary.zone.stop()
+
+ # Write zeroth generation
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * 11}')
+ k.set_contents_from_string('foo')
+
+ # Write several more generations
+ generations = [17, 19, 23, 29, 31, 37]
+ for num_shards in generations:
+ reshard_bucket(primary.zone, bucket.name, num_shards)
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * num_shards}')
+ k.set_contents_from_string('foo')
+
+ cmd = ['data', 'sync', 'init'] + secondary.zone.zone_args()
+ cmd += ['--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+ finally:
+ # Do this as a finally so we bring the zone back up even on error.
+ secondary.zone.start()
+
+ # expect all objects to replicate via 'bucket full sync'
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+@attr('data_sync_init')
+def test_bucket_incremental_sync_after_data_sync_init():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ primary = zonegroup_conns.rw_zones[0]
+ secondary = zonegroup_conns.rw_zones[1]
+
+ bucket = primary.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # upload a dummy object and wait for sync. this forces each zone to finish
+ # a full sync and switch to incremental
+ k = new_key(primary, bucket, 'dummy')
+ k.set_contents_from_string('foo')
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ try:
+ # stop secondary zone before it syncs the rest
+ secondary.zone.stop()
+
+ # Write more objects to primary
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * 11}')
+ k.set_contents_from_string('foo')
+
+ cmd = ['data', 'sync', 'init'] + secondary.zone.zone_args()
+ cmd += ['--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+ finally:
+ # Do this as a finally so we bring the zone back up even on error.
+ secondary.zone.start()
+
+ # expect remaining objects to replicate via 'bucket incremental sync'
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+@attr('data_sync_init')
+@attr('bucket_reshard')
+def test_resharded_bucket_incremental_sync_latest_after_data_sync_init():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ primary = zonegroup_conns.rw_zones[0]
+ secondary = zonegroup_conns.rw_zones[1]
+
+ bucket = primary.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # Write zeroth generation to primary
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * 11}')
+ k.set_contents_from_string('foo')
+
+ # Write several more generations
+ generations = [17, 19, 23, 29, 31, 37]
+ for num_shards in generations:
+ reshard_bucket(primary.zone, bucket.name, num_shards)
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * num_shards}')
+ k.set_contents_from_string('foo')
+
+ # wait for the secondary to catch up to the latest gen
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ try:
+ # stop secondary zone before it syncs the rest
+ secondary.zone.stop()
+
+ # write some more objects to the last gen
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * generations[-1]}')
+ k.set_contents_from_string('foo')
+
+ cmd = ['data', 'sync', 'init'] + secondary.zone.zone_args()
+ cmd += ['--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+ finally:
+ # Do this as a finally so we bring the zone back up even on error.
+ secondary.zone.start()
+
+ # expect remaining objects in last gen to replicate via 'bucket incremental sync'
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+@attr('data_sync_init')
+@attr('bucket_reshard')
+def test_resharded_bucket_incremental_sync_oldest_after_data_sync_init():
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ primary = zonegroup_conns.rw_zones[0]
+ secondary = zonegroup_conns.rw_zones[1]
+
+ bucket = primary.create_bucket(gen_bucket_name())
+ log.debug('created bucket=%s', bucket.name)
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # Write zeroth generation to primary
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * 11}')
+ k.set_contents_from_string('foo')
+
+ # wait for the secondary to catch up
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+
+ try:
+ # stop secondary zone before it syncs later generations
+ secondary.zone.stop()
+
+ # Write several more generations
+ generations = [17, 19, 23, 29, 31, 37]
+ for num_shards in generations:
+ reshard_bucket(primary.zone, bucket.name, num_shards)
+ for obj in range(1, 6):
+ k = new_key(primary, bucket.name, f'obj{obj * num_shards}')
+ k.set_contents_from_string('foo')
+
+ cmd = ['data', 'sync', 'init'] + secondary.zone.zone_args()
+ cmd += ['--source-zone', primary.name]
+ secondary.zone.cluster.admin(cmd)
+ finally:
+ # Do this as a finally so we bring the zone back up even on error.
+ secondary.zone.start()
+
+ # expect all generations to replicate via 'bucket incremental sync'
+ zonegroup_bucket_checkpoint(zonegroup_conns, bucket.name)
+ zonegroup_data_checkpoint(zonegroup_conns)
+
+def sync_info(cluster, bucket = None):
+ cmd = ['sync', 'info']
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to get sync policy'
+
+ return json.loads(result_json)
+
+def get_sync_policy(cluster, bucket = None):
+ cmd = ['sync', 'policy', 'get']
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to get sync policy'
+
+ return json.loads(result_json)
+
+def create_sync_policy_group(cluster, group, status = "allowed", bucket = None):
+ cmd = ['sync', 'group', 'create', '--group-id', group, '--status' , status]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to create sync policy group id=%s, bucket=%s' % (group, bucket)
+ return json.loads(result_json)
+
+def set_sync_policy_group_status(cluster, group, status, bucket = None):
+ cmd = ['sync', 'group', 'modify', '--group-id', group, '--status' , status]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to set sync policy group id=%s, bucket=%s' % (group, bucket)
+ return json.loads(result_json)
+
+def get_sync_policy_group(cluster, group, bucket = None):
+ cmd = ['sync', 'group', 'get', '--group-id', group]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to get sync policy group id=%s, bucket=%s' % (group, bucket)
+ return json.loads(result_json)
+
+def remove_sync_policy_group(cluster, group, bucket = None):
+ cmd = ['sync', 'group', 'remove', '--group-id', group]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to remove sync policy group id=%s, bucket=%s' % (group, bucket)
+ return json.loads(result_json)
+
+def create_sync_group_flow_symmetrical(cluster, group, flow_id, zones, bucket = None):
+ cmd = ['sync', 'group', 'flow', 'create', '--group-id', group, '--flow-id' , flow_id, '--flow-type', 'symmetrical', '--zones=%s' % zones]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to create sync group flow symmetrical groupid=%s, flow_id=%s, zones=%s, bucket=%s' % (group, flow_id, zones, bucket)
+ return json.loads(result_json)
+
+def create_sync_group_flow_directional(cluster, group, flow_id, src_zones, dest_zones, bucket = None):
+ cmd = ['sync', 'group', 'flow', 'create', '--group-id', group, '--flow-id' , flow_id, '--flow-type', 'directional', '--source-zone=%s' % src_zones, '--dest-zone=%s' % dest_zones]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to create sync group flow directional groupid=%s, flow_id=%s, src_zones=%s, dest_zones=%s, bucket=%s' % (group, flow_id, src_zones, dest_zones, bucket)
+ return json.loads(result_json)
+
+def remove_sync_group_flow_symmetrical(cluster, group, flow_id, zones = None, bucket = None):
+ cmd = ['sync', 'group', 'flow', 'remove', '--group-id', group, '--flow-id' , flow_id, '--flow-type', 'symmetrical']
+ if zones:
+ cmd += ['--zones=%s' % zones]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to remove sync group flow symmetrical groupid=%s, flow_id=%s, zones=%s, bucket=%s' % (group, flow_id, zones, bucket)
+ return json.loads(result_json)
+
+def remove_sync_group_flow_directional(cluster, group, flow_id, src_zones, dest_zones, bucket = None):
+ cmd = ['sync', 'group', 'flow', 'remove', '--group-id', group, '--flow-id' , flow_id, '--flow-type', 'directional', '--source-zone=%s' % src_zones, '--dest-zone=%s' % dest_zones]
+ if bucket:
+ cmd += ['--bucket', bucket]
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to remove sync group flow directional groupid=%s, flow_id=%s, src_zones=%s, dest_zones=%s, bucket=%s' % (group, flow_id, src_zones, dest_zones, bucket)
+ return json.loads(result_json)
+
+def create_sync_group_pipe(cluster, group, pipe_id, src_zones, dest_zones, bucket = None, args = []):
+ cmd = ['sync', 'group', 'pipe', 'create', '--group-id', group, '--pipe-id' , pipe_id, '--source-zones=%s' % src_zones, '--dest-zones=%s' % dest_zones]
+ if bucket:
+ b_args = '--bucket=' + bucket
+ cmd.append(b_args)
+ if args:
+ cmd += args
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to create sync group pipe groupid=%s, pipe_id=%s, src_zones=%s, dest_zones=%s, bucket=%s' % (group, pipe_id, src_zones, dest_zones, bucket)
+ return json.loads(result_json)
+
+def remove_sync_group_pipe(cluster, group, pipe_id, bucket = None, args = None):
+ cmd = ['sync', 'group', 'pipe', 'remove', '--group-id', group, '--pipe-id' , pipe_id]
+ if bucket:
+ b_args = '--bucket=' + bucket
+ cmd.append(b_args)
+ if args:
+ cmd.append(args)
+ (result_json, retcode) = cluster.admin(cmd)
+ if retcode != 0:
+ assert False, 'failed to remove sync group pipe groupid=%s, pipe_id=%s, src_zones=%s, dest_zones=%s, bucket=%s' % (group, pipe_id, src_zones, dest_zones, bucket)
+ return json.loads(result_json)
+
+def create_zone_bucket(zone):
+ b_name = gen_bucket_name()
+ log.info('create bucket zone=%s name=%s', zone.name, b_name)
+ bucket = zone.create_bucket(b_name)
+ return bucket
+
+def create_object(zone_conn, bucket, objname, content):
+ k = new_key(zone_conn, bucket.name, objname)
+ k.set_contents_from_string(content)
+
+def create_objects(zone_conn, bucket, obj_arr, content):
+ for objname in obj_arr:
+ create_object(zone_conn, bucket, objname, content)
+
+def check_object_exists(bucket, objname, content = None):
+ k = bucket.get_key(objname)
+ assert_not_equal(k, None)
+ if (content != None):
+ assert_equal(k.get_contents_as_string(encoding='ascii'), content)
+
+def check_objects_exist(bucket, obj_arr, content = None):
+ for objname in obj_arr:
+ check_object_exists(bucket, objname, content)
+
+def check_object_not_exists(bucket, objname):
+ k = bucket.get_key(objname)
+ assert_equal(k, None)
+
+def check_objects_not_exist(bucket, obj_arr):
+ for objname in obj_arr:
+ check_object_not_exists(bucket, objname)
+
+@attr('sync_policy')
+def test_sync_policy_config_zonegroup():
+ """
+ test_sync_policy_config_zonegroup:
+ test configuration of all sync commands
+ """
+ zonegroup = realm.master_zonegroup()
+ zonegroup_meta_checkpoint(zonegroup)
+
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ z1, z2 = zonegroup.zones[0:2]
+ c1, c2 = (z1.cluster, z2.cluster)
+
+ zones = z1.name+","+z2.name
+
+ c1.admin(['sync', 'policy', 'get'])
+
+ # (a) zonegroup level
+ create_sync_policy_group(c1, "sync-group")
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+ get_sync_policy_group(c1, "sync-group")
+
+ get_sync_policy(c1)
+
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1", zones)
+ create_sync_group_flow_directional(c1, "sync-group", "sync-flow2", z1.name, z2.name)
+
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones)
+ get_sync_policy_group(c1, "sync-group")
+
+ zonegroup.period.update(z1, commit=True)
+
+ # (b) bucket level
+ zc1, zc2 = zonegroup_conns.zones[0:2]
+ bucket = create_zone_bucket(zc1)
+ bucket_name = bucket.name
+
+ create_sync_policy_group(c1, "sync-bucket", "allowed", bucket_name)
+ set_sync_policy_group_status(c1, "sync-bucket", "enabled", bucket_name)
+ get_sync_policy_group(c1, "sync-bucket", bucket_name)
+
+ get_sync_policy(c1, bucket_name)
+
+ create_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flow1", zones, bucket_name)
+ create_sync_group_flow_directional(c1, "sync-bucket", "sync-flow2", z1.name, z2.name, bucket_name)
+
+ create_sync_group_pipe(c1, "sync-bucket", "sync-pipe", zones, zones, bucket_name)
+ get_sync_policy_group(c1, "sync-bucket", bucket_name)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ remove_sync_group_pipe(c1, "sync-bucket", "sync-pipe", bucket_name)
+ remove_sync_group_flow_directional(c1, "sync-bucket", "sync-flow2", z1.name, z2.name, bucket_name)
+ remove_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flow1", zones, bucket_name)
+ remove_sync_policy_group(c1, "sync-bucket", bucket_name)
+
+ get_sync_policy(c1, bucket_name)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ remove_sync_group_pipe(c1, "sync-group", "sync-pipe")
+ remove_sync_group_flow_directional(c1, "sync-group", "sync-flow2", z1.name, z2.name)
+ remove_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1")
+ remove_sync_policy_group(c1, "sync-group")
+
+ get_sync_policy(c1)
+
+ zonegroup.period.update(z1, commit=True)
+
+ return
+
+@attr('sync_policy')
+def test_sync_flow_symmetrical_zonegroup_all():
+ """
+ test_sync_flow_symmetrical_zonegroup_all:
+ allows sync from all the zones to all other zones (default case)
+ """
+
+ zonegroup = realm.master_zonegroup()
+ zonegroup_meta_checkpoint(zonegroup)
+
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ (zoneA, zoneB) = zonegroup.zones[0:2]
+ (zcA, zcB) = zonegroup_conns.zones[0:2]
+
+ c1 = zoneA.cluster
+
+ c1.admin(['sync', 'policy', 'get'])
+
+ zones = zoneA.name + ',' + zoneB.name
+ create_sync_policy_group(c1, "sync-group")
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1", zones)
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones)
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ objnames = [ 'obj1', 'obj2' ]
+ content = 'asdasd'
+ buckets = []
+
+ # create bucket & object in all zones
+ bucketA = create_zone_bucket(zcA)
+ buckets.append(bucketA)
+ create_object(zcA, bucketA, objnames[0], content)
+
+ bucketB = create_zone_bucket(zcB)
+ buckets.append(bucketB)
+ create_object(zcB, bucketB, objnames[1], content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ # 'zonegroup_data_checkpoint' currently fails for the zones not
+ # allowed to sync. So as a workaround, data checkpoint is done
+ # for only the ones configured.
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify if objects are synced accross the zone
+ bucket = get_bucket(zcB, bucketA.name)
+ check_object_exists(bucket, objnames[0], content)
+
+ bucket = get_bucket(zcA, bucketB.name)
+ check_object_exists(bucket, objnames[1], content)
+
+ remove_sync_policy_group(c1, "sync-group")
+ return
+
+@attr('sync_policy')
+def test_sync_flow_symmetrical_zonegroup_select():
+ """
+ test_sync_flow_symmetrical_zonegroup_select:
+ allow sync between zoneA & zoneB
+ verify zoneC doesnt sync the data
+ """
+
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ if len(zonegroup.zones) < 3:
+ raise SkipTest("test_sync_flow_symmetrical_zonegroup_select skipped. Requires 3 or more zones in master zonegroup.")
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ (zoneA, zoneB, zoneC) = zonegroup.zones[0:3]
+ (zcA, zcB, zcC) = zonegroup_conns.zones[0:3]
+
+ c1 = zoneA.cluster
+
+ # configure sync policy
+ zones = zoneA.name + ',' + zoneB.name
+ c1.admin(['sync', 'policy', 'get'])
+ create_sync_policy_group(c1, "sync-group")
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow", zones)
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones)
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ buckets = []
+ content = 'asdasd'
+
+ # create bucketA & objects in zoneA
+ objnamesA = [ 'obj1', 'obj2', 'obj3' ]
+ bucketA = create_zone_bucket(zcA)
+ buckets.append(bucketA)
+ create_objects(zcA, bucketA, objnamesA, content)
+
+ # create bucketB & objects in zoneB
+ objnamesB = [ 'obj4', 'obj5', 'obj6' ]
+ bucketB = create_zone_bucket(zcB)
+ buckets.append(bucketB)
+ create_objects(zcB, bucketB, objnamesB, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+ zone_data_checkpoint(zoneA, zoneB)
+
+ # verify if objnamesA synced to only zoneB but not zoneC
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_exist(bucket, objnamesA, content)
+
+ bucket = get_bucket(zcC, bucketA.name)
+ check_objects_not_exist(bucket, objnamesA)
+
+ # verify if objnamesB synced to only zoneA but not zoneC
+ bucket = get_bucket(zcA, bucketB.name)
+ check_objects_exist(bucket, objnamesB, content)
+
+ bucket = get_bucket(zcC, bucketB.name)
+ check_objects_not_exist(bucket, objnamesB)
+
+ remove_sync_policy_group(c1, "sync-group")
+ return
+
+@attr('sync_policy')
+def test_sync_flow_directional_zonegroup_select():
+ """
+ test_sync_flow_directional_zonegroup_select:
+ allow sync from only zoneA to zoneB
+
+ verify that data doesn't get synced to zoneC and
+ zoneA shouldn't sync data from zoneB either
+ """
+
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ if len(zonegroup.zones) < 3:
+ raise SkipTest("test_sync_flow_symmetrical_zonegroup_select skipped. Requires 3 or more zones in master zonegroup.")
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ (zoneA, zoneB, zoneC) = zonegroup.zones[0:3]
+ (zcA, zcB, zcC) = zonegroup_conns.zones[0:3]
+
+ c1 = zoneA.cluster
+
+ # configure sync policy
+ zones = zoneA.name + ',' + zoneB.name
+ c1.admin(['sync', 'policy', 'get'])
+ create_sync_policy_group(c1, "sync-group")
+ create_sync_group_flow_directional(c1, "sync-group", "sync-flow", zoneA.name, zoneB.name)
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zoneA.name, zoneB.name)
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ buckets = []
+ content = 'asdasd'
+
+ # create bucketA & objects in zoneA
+ objnamesA = [ 'obj1', 'obj2', 'obj3' ]
+ bucketA = create_zone_bucket(zcA)
+ buckets.append(bucketA)
+ create_objects(zcA, bucketA, objnamesA, content)
+
+ # create bucketB & objects in zoneB
+ objnamesB = [ 'obj4', 'obj5', 'obj6' ]
+ bucketB = create_zone_bucket(zcB)
+ buckets.append(bucketB)
+ create_objects(zcB, bucketB, objnamesB, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify if objnamesA synced to only zoneB but not zoneC
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_exist(bucket, objnamesA, content)
+
+ bucket = get_bucket(zcC, bucketA.name)
+ check_objects_not_exist(bucket, objnamesA)
+
+ # verify if objnamesB are not synced to either zoneA or zoneC
+ bucket = get_bucket(zcA, bucketB.name)
+ check_objects_not_exist(bucket, objnamesB)
+
+ bucket = get_bucket(zcC, bucketB.name)
+ check_objects_not_exist(bucket, objnamesB)
+
+ """
+ verify the same at bucketA level
+ configure another policy at bucketA level with src and dest
+ zones specified to zoneA and zoneB resp.
+
+ verify zoneA bucketA syncs to zoneB BucketA but not viceversa.
+ """
+ # reconfigure zonegroup pipe & flow
+ remove_sync_group_pipe(c1, "sync-group", "sync-pipe")
+ remove_sync_group_flow_directional(c1, "sync-group", "sync-flow", zoneA.name, zoneB.name)
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1", zones)
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones)
+
+ # change state to allowed
+ set_sync_policy_group_status(c1, "sync-group", "allowed")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ # configure sync policy for only bucketA and enable it
+ create_sync_policy_group(c1, "sync-bucket", "allowed", bucketA.name)
+ create_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flowA", zones, bucketA.name)
+ args = ['--source-bucket=*', '--dest-bucket=*']
+ create_sync_group_pipe(c1, "sync-bucket", "sync-pipe", zoneA.name, zoneB.name, bucketA.name, args)
+ set_sync_policy_group_status(c1, "sync-bucket", "enabled", bucketA.name)
+
+ get_sync_policy(c1, bucketA.name)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # create objects in bucketA in zoneA and zoneB
+ objnamesC = [ 'obj7', 'obj8', 'obj9' ]
+ objnamesD = [ 'obj10', 'obj11', 'obj12' ]
+ create_objects(zcA, bucketA, objnamesC, content)
+ create_objects(zcB, bucketA, objnamesD, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify that objnamesC are synced to bucketA in zoneB
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_exist(bucket, objnamesC, content)
+
+ # verify that objnamesD are not synced to bucketA in zoneA
+ bucket = get_bucket(zcA, bucketA.name)
+ check_objects_not_exist(bucket, objnamesD)
+
+ remove_sync_policy_group(c1, "sync-bucket", bucketA.name)
+ remove_sync_policy_group(c1, "sync-group")
+ return
+
+@attr('sync_policy')
+def test_sync_single_bucket():
+ """
+ test_sync_single_bucket:
+ Allow data sync for only bucketA but not for other buckets via
+ below 2 methods
+
+ (a) zonegroup: symmetrical flow but configure pipe for only bucketA.
+ (b) bucket level: configure policy for bucketA
+ """
+
+ zonegroup = realm.master_zonegroup()
+ zonegroup_meta_checkpoint(zonegroup)
+
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ (zoneA, zoneB) = zonegroup.zones[0:2]
+ (zcA, zcB) = zonegroup_conns.zones[0:2]
+
+ c1 = zoneA.cluster
+
+ c1.admin(['sync', 'policy', 'get'])
+
+ zones = zoneA.name + ',' + zoneB.name
+ get_sync_policy(c1)
+
+ objnames = [ 'obj1', 'obj2', 'obj3' ]
+ content = 'asdasd'
+ buckets = []
+
+ # create bucketA & bucketB in zoneA
+ bucketA = create_zone_bucket(zcA)
+ buckets.append(bucketA)
+ bucketB = create_zone_bucket(zcA)
+ buckets.append(bucketB)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ """
+ Method (a): configure pipe for only bucketA
+ """
+ # configure sync policy & pipe for only bucketA
+ create_sync_policy_group(c1, "sync-group")
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1", zones)
+ args = ['--source-bucket=' + bucketA.name, '--dest-bucket=' + bucketA.name]
+
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones, None, args)
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+ get_sync_policy(c1)
+ zonegroup.period.update(zoneA, commit=True)
+
+ sync_info(c1)
+
+ # create objects in bucketA & bucketB
+ create_objects(zcA, bucketA, objnames, content)
+ create_object(zcA, bucketB, objnames, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify if bucketA objects are synced
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_exist(bucket, objnames, content)
+
+ # bucketB objects should not be synced
+ bucket = get_bucket(zcB, bucketB.name)
+ check_objects_not_exist(bucket, objnames)
+
+
+ """
+ Method (b): configure policy at only bucketA level
+ """
+ # reconfigure group pipe
+ remove_sync_group_pipe(c1, "sync-group", "sync-pipe")
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones)
+
+ # change state to allowed
+ set_sync_policy_group_status(c1, "sync-group", "allowed")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+
+ # configure sync policy for only bucketA and enable it
+ create_sync_policy_group(c1, "sync-bucket", "allowed", bucketA.name)
+ create_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flowA", zones, bucketA.name)
+ create_sync_group_pipe(c1, "sync-bucket", "sync-pipe", zones, zones, bucketA.name)
+ set_sync_policy_group_status(c1, "sync-bucket", "enabled", bucketA.name)
+
+ get_sync_policy(c1, bucketA.name)
+
+ # create object in bucketA
+ create_object(zcA, bucketA, objnames[2], content)
+
+ # create object in bucketA too
+ create_object(zcA, bucketB, objnames[2], content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify if bucketA objects are synced
+ bucket = get_bucket(zcB, bucketA.name)
+ check_object_exists(bucket, objnames[2], content)
+
+ # bucketB objects should not be synced
+ bucket = get_bucket(zcB, bucketB.name)
+ check_object_not_exists(bucket, objnames[2])
+
+ remove_sync_policy_group(c1, "sync-bucket", bucketA.name)
+ remove_sync_policy_group(c1, "sync-group")
+ return
+
+@attr('sync_policy')
+def test_sync_different_buckets():
+ """
+ test_sync_different_buckets:
+ sync zoneA bucketA to zoneB bucketB via below methods
+
+ (a) zonegroup: directional flow but configure pipe for zoneA bucketA to zoneB bucketB
+ (b) bucket: configure another policy at bucketA level with pipe set to
+ another bucket(bucketB) in target zone.
+
+ sync zoneA bucketA from zoneB bucketB
+ (c) configure another policy at bucketA level with pipe set from
+ another bucket(bucketB) in source zone.
+
+ """
+
+ zonegroup = realm.master_zonegroup()
+ zonegroup_meta_checkpoint(zonegroup)
+
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ (zoneA, zoneB) = zonegroup.zones[0:2]
+ (zcA, zcB) = zonegroup_conns.zones[0:2]
+ zones = zoneA.name + ',' + zoneB.name
+
+ c1 = zoneA.cluster
+
+ c1.admin(['sync', 'policy', 'get'])
+
+ objnames = [ 'obj1', 'obj2' ]
+ objnamesB = [ 'obj3', 'obj4' ]
+ content = 'asdasd'
+ buckets = []
+
+ # create bucketA & bucketB in zoneA
+ bucketA = create_zone_bucket(zcA)
+ buckets.append(bucketA)
+ bucketB = create_zone_bucket(zcA)
+ buckets.append(bucketB)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ """
+ Method (a): zonegroup - configure pipe for only bucketA
+ """
+ # configure pipe from zoneA bucketA to zoneB bucketB
+ create_sync_policy_group(c1, "sync-group")
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1", zones)
+ args = ['--source-bucket=' + bucketA.name, '--dest-bucket=' + bucketB.name]
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zoneA.name, zoneB.name, None, args)
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ # create objects in bucketA
+ create_objects(zcA, bucketA, objnames, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify that objects are synced to bucketB in zoneB
+ # but not to bucketA
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_not_exist(bucket, objnames)
+
+ bucket = get_bucket(zcB, bucketB.name)
+ check_objects_exist(bucket, objnames, content)
+ """
+ Method (b): configure policy at only bucketA level with pipe
+ set to bucketB in target zone
+ """
+
+ remove_sync_group_pipe(c1, "sync-group", "sync-pipe")
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones)
+
+ # change state to allowed
+ set_sync_policy_group_status(c1, "sync-group", "allowed")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ # configure sync policy for only bucketA and enable it
+ create_sync_policy_group(c1, "sync-bucket", "allowed", bucketA.name)
+ create_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flowA", zones, bucketA.name)
+ args = ['--source-bucket=*', '--dest-bucket=' + bucketB.name]
+ create_sync_group_pipe(c1, "sync-bucket", "sync-pipeA", zones, zones, bucketA.name, args)
+ set_sync_policy_group_status(c1, "sync-bucket", "enabled", bucketA.name)
+
+ get_sync_policy(c1, bucketA.name)
+
+ objnamesC = [ 'obj5', 'obj6' ]
+
+ zonegroup_meta_checkpoint(zonegroup)
+ # create objects in bucketA
+ create_objects(zcA, bucketA, objnamesC, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ """
+ # verify that objects are synced to bucketB in zoneB
+ # but not to bucketA
+ """
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_not_exist(bucket, objnamesC)
+
+ bucket = get_bucket(zcB, bucketB.name)
+ check_objects_exist(bucket, objnamesC, content)
+
+ remove_sync_policy_group(c1, "sync-bucket", bucketA.name)
+ zonegroup_meta_checkpoint(zonegroup)
+ get_sync_policy(c1, bucketA.name)
+
+ """
+ Method (c): configure policy at only bucketA level with pipe
+ set from bucketB in source zone
+ verify zoneA bucketA syncs from zoneB BucketB but not bucketA
+ """
+
+ # configure sync policy for only bucketA and enable it
+ create_sync_policy_group(c1, "sync-bucket", "allowed", bucketA.name)
+ create_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flowA", zones, bucketA.name)
+ args = ['--source-bucket=' + bucketB.name, '--dest-bucket=' + '*']
+ create_sync_group_pipe(c1, "sync-bucket", "sync-pipe", zones, zones, bucketA.name, args)
+ set_sync_policy_group_status(c1, "sync-bucket", "enabled", bucketA.name)
+
+ get_sync_policy(c1, bucketA.name)
+
+ # create objects in bucketA & B in ZoneB
+ objnamesD = [ 'obj7', 'obj8' ]
+ objnamesE = [ 'obj9', 'obj10' ]
+
+ create_objects(zcB, bucketA, objnamesD, content)
+ create_objects(zcB, bucketB, objnamesE, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneA, zoneB)
+ """
+ # verify that objects from only bucketB are synced to
+ # bucketA in zoneA
+ """
+ bucket = get_bucket(zcA, bucketA.name)
+ check_objects_not_exist(bucket, objnamesD)
+ check_objects_exist(bucket, objnamesE, content)
+
+ remove_sync_policy_group(c1, "sync-bucket", bucketA.name)
+ remove_sync_policy_group(c1, "sync-group")
+ return
+
+@attr('sync_policy')
+def test_sync_multiple_buckets_to_single():
+ """
+ test_sync_multiple_buckets_to_single:
+ directional flow
+ (a) pipe: sync zoneA bucketA,bucketB to zoneB bucketB
+
+ (b) configure another policy at bucketA level with pipe configured
+ to sync from multiple buckets (bucketA & bucketB)
+
+ verify zoneA bucketA & bucketB syncs to zoneB BucketB
+ """
+
+ zonegroup = realm.master_zonegroup()
+ zonegroup_meta_checkpoint(zonegroup)
+
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ (zoneA, zoneB) = zonegroup.zones[0:2]
+ (zcA, zcB) = zonegroup_conns.zones[0:2]
+ zones = zoneA.name + ',' + zoneB.name
+
+ c1 = zoneA.cluster
+
+ c1.admin(['sync', 'policy', 'get'])
+
+ objnamesA = [ 'obj1', 'obj2' ]
+ objnamesB = [ 'obj3', 'obj4' ]
+ content = 'asdasd'
+ buckets = []
+
+ # create bucketA & bucketB in zoneA
+ bucketA = create_zone_bucket(zcA)
+ buckets.append(bucketA)
+ bucketB = create_zone_bucket(zcA)
+ buckets.append(bucketB)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # configure pipe from zoneA bucketA,bucketB to zoneB bucketB
+ create_sync_policy_group(c1, "sync-group")
+ create_sync_group_flow_directional(c1, "sync-group", "sync-flow", zoneA.name, zoneB.name)
+ source_buckets = [ bucketA.name, bucketB.name ]
+ for source_bucket in source_buckets:
+ args = ['--source-bucket=' + source_bucket, '--dest-bucket=' + bucketB.name]
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe-%s" % source_bucket, zoneA.name, zoneB.name, None, args)
+
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ # create objects in bucketA & bucketB
+ create_objects(zcA, bucketA, objnamesA, content)
+ create_objects(zcA, bucketB, objnamesB, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify that both zoneA bucketA & bucketB objects are synced to
+ # bucketB in zoneB but not to bucketA
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_not_exist(bucket, objnamesA)
+ check_objects_not_exist(bucket, objnamesB)
+
+ bucket = get_bucket(zcB, bucketB.name)
+ check_objects_exist(bucket, objnamesA, content)
+ check_objects_exist(bucket, objnamesB, content)
+
+ """
+ Method (b): configure at bucket level
+ """
+ # reconfigure pipe & flow
+ for source_bucket in source_buckets:
+ remove_sync_group_pipe(c1, "sync-group", "sync-pipe-%s" % source_bucket)
+ remove_sync_group_flow_directional(c1, "sync-group", "sync-flow", zoneA.name, zoneB.name)
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1", zones)
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zones, zones)
+
+ # change state to allowed
+ set_sync_policy_group_status(c1, "sync-group", "allowed")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ objnamesC = [ 'obj5', 'obj6' ]
+ objnamesD = [ 'obj7', 'obj8' ]
+
+ # configure sync policy for only bucketA and enable it
+ create_sync_policy_group(c1, "sync-bucket", "allowed", bucketA.name)
+ create_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flowA", zones, bucketA.name)
+ source_buckets = [ bucketA.name, bucketB.name ]
+ for source_bucket in source_buckets:
+ args = ['--source-bucket=' + source_bucket, '--dest-bucket=' + '*']
+ create_sync_group_pipe(c1, "sync-bucket", "sync-pipe-%s" % source_bucket, zoneA.name, zoneB.name, bucketA.name, args)
+
+ set_sync_policy_group_status(c1, "sync-bucket", "enabled", bucketA.name)
+
+ get_sync_policy(c1)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ # create objects in bucketA
+ create_objects(zcA, bucketA, objnamesC, content)
+ create_objects(zcA, bucketB, objnamesD, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify that both zoneA bucketA & bucketB objects are synced to
+ # bucketA in zoneB but not to bucketB
+ bucket = get_bucket(zcB, bucketB.name)
+ check_objects_not_exist(bucket, objnamesC)
+ check_objects_not_exist(bucket, objnamesD)
+
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_exist(bucket, objnamesD, content)
+ check_objects_exist(bucket, objnamesD, content)
+
+ remove_sync_policy_group(c1, "sync-bucket", bucketA.name)
+ remove_sync_policy_group(c1, "sync-group")
+ return
+
+@attr('sync_policy')
+def test_sync_single_bucket_to_multiple():
+ """
+ test_sync_single_bucket_to_multiple:
+ directional flow
+ (a) pipe: sync zoneA bucketA to zoneB bucketA & bucketB
+
+ (b) configure another policy at bucketA level with pipe configured
+ to sync to multiple buckets (bucketA & bucketB)
+
+ verify zoneA bucketA syncs to zoneB bucketA & bucketB
+ """
+
+ zonegroup = realm.master_zonegroup()
+ zonegroup_meta_checkpoint(zonegroup)
+
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ (zoneA, zoneB) = zonegroup.zones[0:2]
+ (zcA, zcB) = zonegroup_conns.zones[0:2]
+ zones = zoneA.name + ',' + zoneB.name
+
+ c1 = zoneA.cluster
+
+ c1.admin(['sync', 'policy', 'get'])
+
+ objnamesA = [ 'obj1', 'obj2' ]
+ content = 'asdasd'
+ buckets = []
+
+ # create bucketA & bucketB in zoneA
+ bucketA = create_zone_bucket(zcA)
+ buckets.append(bucketA)
+ bucketB = create_zone_bucket(zcA)
+ buckets.append(bucketB)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ # configure pipe from zoneA bucketA to zoneB bucketA, bucketB
+ create_sync_policy_group(c1, "sync-group")
+ create_sync_group_flow_symmetrical(c1, "sync-group", "sync-flow1", zones)
+
+ dest_buckets = [ bucketA.name, bucketB.name ]
+ for dest_bucket in dest_buckets:
+ args = ['--source-bucket=' + bucketA.name, '--dest-bucket=' + dest_bucket]
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe-%s" % dest_bucket, zoneA.name, zoneB.name, None, args)
+
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", zoneA.name, zoneB.name, None, args)
+ set_sync_policy_group_status(c1, "sync-group", "enabled")
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ # create objects in bucketA
+ create_objects(zcA, bucketA, objnamesA, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify that objects from zoneA bucketA are synced to both
+ # bucketA & bucketB in zoneB
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_exist(bucket, objnamesA, content)
+
+ bucket = get_bucket(zcB, bucketB.name)
+ check_objects_exist(bucket, objnamesA, content)
+
+ """
+ Method (b): configure at bucket level
+ """
+ remove_sync_group_pipe(c1, "sync-group", "sync-pipe")
+ create_sync_group_pipe(c1, "sync-group", "sync-pipe", '*', '*')
+
+ # change state to allowed
+ set_sync_policy_group_status(c1, "sync-group", "allowed")
+
+ zonegroup.period.update(zoneA, commit=True)
+ get_sync_policy(c1)
+
+ objnamesB = [ 'obj3', 'obj4' ]
+
+ # configure sync policy for only bucketA and enable it
+ create_sync_policy_group(c1, "sync-bucket", "allowed", bucketA.name)
+ create_sync_group_flow_symmetrical(c1, "sync-bucket", "sync-flowA", zones, bucketA.name)
+ dest_buckets = [ bucketA.name, bucketB.name ]
+ for dest_bucket in dest_buckets:
+ args = ['--source-bucket=' + '*', '--dest-bucket=' + dest_bucket]
+ create_sync_group_pipe(c1, "sync-bucket", "sync-pipe-%s" % dest_bucket, zoneA.name, zoneB.name, bucketA.name, args)
+
+ set_sync_policy_group_status(c1, "sync-bucket", "enabled", bucketA.name)
+
+ get_sync_policy(c1)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ # create objects in bucketA
+ create_objects(zcA, bucketA, objnamesB, content)
+
+ zonegroup_meta_checkpoint(zonegroup)
+ zone_data_checkpoint(zoneB, zoneA)
+
+ # verify that objects from zoneA bucketA are synced to both
+ # bucketA & bucketB in zoneB
+ bucket = get_bucket(zcB, bucketA.name)
+ check_objects_exist(bucket, objnamesB, content)
+
+ bucket = get_bucket(zcB, bucketB.name)
+ check_objects_exist(bucket, objnamesB, content)
+
+ remove_sync_policy_group(c1, "sync-bucket", bucketA.name)
+ remove_sync_policy_group(c1, "sync-group")
+ return
diff --git a/src/test/rgw/rgw_multi/tests_az.py b/src/test/rgw/rgw_multi/tests_az.py
new file mode 100644
index 000000000..13ec832a2
--- /dev/null
+++ b/src/test/rgw/rgw_multi/tests_az.py
@@ -0,0 +1,597 @@
+import logging
+
+from nose import SkipTest
+from nose.tools import assert_not_equal, assert_equal
+
+from boto.s3.deletemarker import DeleteMarker
+
+from .tests import get_realm, \
+ ZonegroupConns, \
+ zonegroup_meta_checkpoint, \
+ zone_meta_checkpoint, \
+ zone_bucket_checkpoint, \
+ zone_data_checkpoint, \
+ zonegroup_bucket_checkpoint, \
+ check_bucket_eq, \
+ gen_bucket_name, \
+ get_user, \
+ get_tenant
+
+from .zone_az import print_connection_info
+
+
+# configure logging for the tests module
+log = logging.getLogger(__name__)
+
+
+##########################################
+# utility functions for archive zone tests
+##########################################
+
+def check_az_configured():
+ """check if at least one archive zone exist"""
+ realm = get_realm()
+ zonegroup = realm.master_zonegroup()
+
+ az_zones = zonegroup.zones_by_type.get("archive")
+ if az_zones is None or len(az_zones) != 1:
+ raise SkipTest("Requires one archive zone")
+
+
+def is_az_zone(zone_conn):
+ """check if a specific zone is archive zone"""
+ if not zone_conn:
+ return False
+ return zone_conn.zone.tier_type() == "archive"
+
+
+def init_env():
+ """initialize the environment"""
+ check_az_configured()
+
+ realm = get_realm()
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ az_zones = []
+ zones = []
+ for conn in zonegroup_conns.zones:
+ if is_az_zone(conn):
+ zone_meta_checkpoint(conn.zone)
+ az_zones.append(conn)
+ elif not conn.zone.is_read_only():
+ zones.append(conn)
+
+ assert_not_equal(len(zones), 0)
+ assert_not_equal(len(az_zones), 0)
+ return zones, az_zones
+
+
+def zone_full_checkpoint(target_zone, source_zone):
+ zone_meta_checkpoint(target_zone)
+ zone_data_checkpoint(target_zone, source_zone)
+
+
+def check_bucket_exists_on_zone(zone, bucket_name):
+ try:
+ zone.conn.get_bucket(bucket_name)
+ except:
+ return False
+ return True
+
+
+def check_key_exists(key):
+ try:
+ key.get_contents_as_string()
+ except:
+ return False
+ return True
+
+
+def get_versioning_status(bucket):
+ res = bucket.get_versioning_status()
+ key = 'Versioning'
+ if not key in res:
+ return None
+ else:
+ return res[key]
+
+
+def get_versioned_objs(bucket):
+ b = []
+ for b_entry in bucket.list_versions():
+ if isinstance(b_entry, DeleteMarker):
+ continue
+ d = {}
+ d['version_id'] = b_entry.version_id
+ d['size'] = b_entry.size
+ d['etag'] = b_entry.etag
+ d['is_latest'] = b_entry.is_latest
+ b.append({b_entry.key:d})
+ return b
+
+
+def get_versioned_entries(bucket):
+ dm = []
+ ver = []
+ for b_entry in bucket.list_versions():
+ if isinstance(b_entry, DeleteMarker):
+ d = {}
+ d['version_id'] = b_entry.version_id
+ d['is_latest'] = b_entry.is_latest
+ dm.append({b_entry.name:d})
+ else:
+ d = {}
+ d['version_id'] = b_entry.version_id
+ d['size'] = b_entry.size
+ d['etag'] = b_entry.etag
+ d['is_latest'] = b_entry.is_latest
+ ver.append({b_entry.key:d})
+ return (dm, ver)
+
+
+def get_number_buckets_by_zone(zone):
+ return len(zone.conn.get_all_buckets())
+
+
+def get_bucket_names_by_zone(zone):
+ return [b.name for b in zone.conn.get_all_buckets()]
+
+
+def get_full_bucket_name(partial_bucket_name, bucket_names_az):
+ full_bucket_name = None
+ for bucket_name in bucket_names_az:
+ if bucket_name.startswith(partial_bucket_name):
+ full_bucket_name = bucket_name
+ break
+ return full_bucket_name
+
+
+####################
+# archive zone tests
+####################
+
+
+def test_az_info():
+ """ log information for manual testing """
+ return SkipTest("only used in manual testing")
+ zones, az_zones = init_env()
+ realm = get_realm()
+ zonegroup = realm.master_zonegroup()
+ bucket_name = gen_bucket_name()
+ # create bucket on the first of the rados zones
+ bucket = zones[0].create_bucket(bucket_name)
+ # create objects in the bucket
+ number_of_objects = 3
+ for i in range(number_of_objects):
+ key = bucket.new_key(str(i))
+ key.set_contents_from_string('bar')
+ print('Zonegroup: ' + zonegroup.name)
+ print('user: ' + get_user())
+ print('tenant: ' + get_tenant())
+ print('Master Zone')
+ print_connection_info(zones[0].conn)
+ print('Archive Zone')
+ print_connection_info(az_zones[0].conn)
+ print('Bucket: ' + bucket_name)
+
+
+def test_az_create_empty_bucket():
+ """ test empty bucket replication """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create bucket on the non archive zone
+ zones[0].create_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # bucket exist on the archive zone
+ p = check_bucket_exists_on_zone(az_zones[0], bucket_name)
+ assert_equal(p, True)
+
+
+def test_az_check_empty_bucket_versioning():
+ """ test bucket vesioning with empty bucket """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create bucket on the non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # get bucket on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ # check for non bucket versioning
+ p1 = get_versioning_status(bucket) is None
+ assert_equal(p1, True)
+ p2 = get_versioning_status(bucket_az) is None
+ assert_equal(p2, True)
+
+
+def test_az_object_replication():
+ """ test object replication """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create bucket on the non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("bar")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check object on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ key_az = bucket_az.get_key("foo")
+ p1 = key_az.get_contents_as_string(encoding='ascii') == "bar"
+ assert_equal(p1, True)
+
+
+def test_az_object_replication_versioning():
+ """ test object replication versioning """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create object on the non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("bar")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check object content on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ key_az = bucket_az.get_key("foo")
+ p1 = key_az.get_contents_as_string(encoding='ascii') == "bar"
+ assert_equal(p1, True)
+ # grab object versioning and etag
+ for b_version in bucket.list_versions():
+ b_version_id = b_version.version_id
+ b_version_etag = b_version.etag
+ for b_az_version in bucket_az.list_versions():
+ b_az_version_id = b_az_version.version_id
+ b_az_version_etag = b_az_version.etag
+ # check
+ p2 = b_version_id == 'null'
+ assert_equal(p2, True)
+ p3 = b_az_version_id != 'null'
+ assert_equal(p3, True)
+ p4 = b_version_etag == b_az_version_etag
+ assert_equal(p4, True)
+
+
+def test_az_lazy_activation_of_versioned_bucket():
+ """ test lazy activation of versioned bucket """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create object on the non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # get bucket on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ # check for non bucket versioning
+ p1 = get_versioning_status(bucket) is None
+ assert_equal(p1, True)
+ p2 = get_versioning_status(bucket_az) is None
+ assert_equal(p2, True)
+ # create object on non archive zone
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("bar")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check lazy versioned buckets
+ p3 = get_versioning_status(bucket) is None
+ assert_equal(p3, True)
+ p4 = get_versioning_status(bucket_az) == 'Enabled'
+ assert_equal(p4, True)
+
+
+def test_az_archive_zone_double_object_replication_versioning():
+ """ test archive zone double object replication versioning """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create object on the non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("bar")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # get bucket on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ # check for non bucket versioning
+ p1 = get_versioning_status(bucket) is None
+ assert_equal(p1, True)
+ p2 = get_versioning_status(bucket_az) == 'Enabled'
+ assert_equal(p2, True)
+ # overwrite object on non archive zone
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("ouch")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check lazy versioned buckets
+ p3 = get_versioning_status(bucket) is None
+ assert_equal(p3, True)
+ p4 = get_versioning_status(bucket_az) == 'Enabled'
+ assert_equal(p4, True)
+ # get versioned objects
+ objs = get_versioned_objs(bucket)
+ objs_az = get_versioned_objs(bucket_az)
+ # check version_id, size, and is_latest on non archive zone
+ p5 = objs[0]['foo']['version_id'] == 'null'
+ assert_equal(p5, True)
+ p6 = objs[0]['foo']['size'] == 4
+ assert_equal(p6, True)
+ p7 = objs[0]['foo']['is_latest'] == True
+ assert_equal(p7, True)
+ # check version_id, size, is_latest on archive zone
+ latest_obj_az_etag = None
+ for obj_az in objs_az:
+ current_obj_az = obj_az['foo']
+ if current_obj_az['is_latest'] == True:
+ p8 = current_obj_az['size'] == 4
+ assert_equal(p8, True)
+ latest_obj_az_etag = current_obj_az['etag']
+ else:
+ p9 = current_obj_az['size'] == 3
+ assert_equal(p9, True)
+ assert_not_equal(current_obj_az['version_id'], 'null')
+ # check last versions' etags
+ p10 = objs[0]['foo']['etag'] == latest_obj_az_etag
+ assert_equal(p10, True)
+
+
+def test_az_deleted_object_replication():
+ """ test zone deleted object replication """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create object on the non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("bar")
+ p1 = key.get_contents_as_string(encoding='ascii') == "bar"
+ assert_equal(p1, True)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # update object on non archive zone
+ key.set_contents_from_string("soup")
+ p2 = key.get_contents_as_string(encoding='ascii') == "soup"
+ assert_equal(p2, True)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # delete object on non archive zone
+ key.delete()
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check object on non archive zone
+ p3 = check_key_exists(key) == False
+ assert_equal(p3, True)
+ # check objects on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ key_az = bucket_az.get_key("foo")
+ p4 = check_key_exists(key_az) == True
+ assert_equal(p4, True)
+ p5 = key_az.get_contents_as_string(encoding='ascii') == "soup"
+ assert_equal(p5, True)
+ b_ver_az = get_versioned_objs(bucket_az)
+ p6 = len(b_ver_az) == 2
+ assert_equal(p6, True)
+
+
+def test_az_bucket_renaming_on_empty_bucket_deletion():
+ """ test bucket renaming on empty bucket deletion """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # grab number of buckets on non archive zone
+ num_buckets = get_number_buckets_by_zone(zones[0])
+ # grab number of buckets on archive zone
+ num_buckets_az = get_number_buckets_by_zone(az_zones[0])
+ # create bucket on non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # delete bucket in non archive zone
+ zones[0].delete_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check no new buckets on non archive zone
+ p1 = get_number_buckets_by_zone(zones[0]) == num_buckets
+ assert_equal(p1, True)
+ # check non deletion on bucket on archive zone
+ p2 = get_number_buckets_by_zone(az_zones[0]) == (num_buckets_az + 1)
+ assert_equal(p2, True)
+ # check bucket renaming
+ bucket_names_az = get_bucket_names_by_zone(az_zones[0])
+ new_bucket_name = bucket_name + '-deleted-'
+ p3 = any(bucket_name.startswith(new_bucket_name) for bucket_name in bucket_names_az)
+ assert_equal(p3, True)
+
+
+def test_az_old_object_version_in_archive_zone():
+ """ test old object version in archive zone """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # grab number of buckets on non archive zone
+ num_buckets = get_number_buckets_by_zone(zones[0])
+ # grab number of buckets on archive zone
+ num_buckets_az = get_number_buckets_by_zone(az_zones[0])
+ # create bucket on non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ # create object on non archive zone
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("zero")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # save object version on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ b_ver_az = get_versioned_objs(bucket_az)
+ obj_az_version_id = b_ver_az[0]['foo']['version_id']
+ # update object on non archive zone
+ key.set_contents_from_string("one")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # delete object on non archive zone
+ key.delete()
+ # delete bucket on non archive zone
+ zones[0].delete_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check same buckets on non archive zone
+ p1 = get_number_buckets_by_zone(zones[0]) == num_buckets
+ assert_equal(p1, True)
+ # check for new bucket on archive zone
+ p2 = get_number_buckets_by_zone(az_zones[0]) == (num_buckets_az + 1)
+ assert_equal(p2, True)
+ # get new bucket name on archive zone
+ bucket_names_az = get_bucket_names_by_zone(az_zones[0])
+ new_bucket_name_az = get_full_bucket_name(bucket_name + '-deleted-', bucket_names_az)
+ p3 = new_bucket_name_az is not None
+ assert_equal(p3, True)
+ # check number of objects on archive zone
+ new_bucket_az = az_zones[0].conn.get_bucket(new_bucket_name_az)
+ new_b_ver_az = get_versioned_objs(new_bucket_az)
+ p4 = len(new_b_ver_az) == 2
+ assert_equal(p4, True)
+ # check versioned objects on archive zone
+ new_key_az = new_bucket_az.get_key("foo", version_id=obj_az_version_id)
+ p5 = new_key_az.get_contents_as_string(encoding='ascii') == "zero"
+ assert_equal(p5, True)
+ new_key_latest_az = new_bucket_az.get_key("foo")
+ p6 = new_key_latest_az.get_contents_as_string(encoding='ascii') == "one"
+ assert_equal(p6, True)
+
+
+def test_az_force_bucket_renaming_if_same_bucket_name():
+ """ test force bucket renaming if same bucket name """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # grab number of buckets on non archive zone
+ num_buckets = get_number_buckets_by_zone(zones[0])
+ # grab number of buckets on archive zone
+ num_buckets_az = get_number_buckets_by_zone(az_zones[0])
+ # create bucket on non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check same buckets on non archive zone
+ p1 = get_number_buckets_by_zone(zones[0]) == (num_buckets + 1)
+ assert_equal(p1, True)
+ # check for new bucket on archive zone
+ p2 = get_number_buckets_by_zone(az_zones[0]) == (num_buckets_az + 1)
+ assert_equal(p2, True)
+ # delete bucket on non archive zone
+ zones[0].delete_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check number of buckets on non archive zone
+ p3 = get_number_buckets_by_zone(zones[0]) == num_buckets
+ assert_equal(p3, True)
+ # check number of buckets on archive zone
+ p4 = get_number_buckets_by_zone(az_zones[0]) == (num_buckets_az + 1)
+ assert_equal(p4, True)
+ # get new bucket name on archive zone
+ bucket_names_az = get_bucket_names_by_zone(az_zones[0])
+ new_bucket_name_az = get_full_bucket_name(bucket_name + '-deleted-', bucket_names_az)
+ p5 = new_bucket_name_az is not None
+ assert_equal(p5, True)
+ # create bucket on non archive zone
+ _ = zones[0].create_bucket(new_bucket_name_az)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check number of buckets on non archive zone
+ p6 = get_number_buckets_by_zone(zones[0]) == (num_buckets + 1)
+ assert_equal(p6, True)
+ # check number of buckets on archive zone
+ p7 = get_number_buckets_by_zone(az_zones[0]) == (num_buckets_az + 2)
+ assert_equal(p7, True)
+
+
+def test_az_versioning_support_in_zones():
+ """ test versioning support on zones """
+ zones, az_zones = init_env()
+ bucket_name = gen_bucket_name()
+ # create bucket on non archive zone
+ bucket = zones[0].create_bucket(bucket_name)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # get bucket on archive zone
+ bucket_az = az_zones[0].conn.get_bucket(bucket_name)
+ # check non versioned buckets
+ p1 = get_versioning_status(bucket) is None
+ assert_equal(p1, True)
+ p2 = get_versioning_status(bucket_az) is None
+ assert_equal(p2, True)
+ # create object on non archive zone
+ key = bucket.new_key("foo")
+ key.set_contents_from_string("zero")
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check bucket versioning
+ p3 = get_versioning_status(bucket) is None
+ assert_equal(p3, True)
+ p4 = get_versioning_status(bucket_az) == 'Enabled'
+ assert_equal(p4, True)
+ # enable bucket versioning on non archive zone
+ bucket.configure_versioning(True)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check bucket versioning
+ p5 = get_versioning_status(bucket) == 'Enabled'
+ assert_equal(p5, True)
+ p6 = get_versioning_status(bucket_az) == 'Enabled'
+ assert_equal(p6, True)
+ # delete object on non archive zone
+ key.delete()
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check delete-markers and versions on non archive zone
+ (b_dm, b_ver) = get_versioned_entries(bucket)
+ p7 = len(b_dm) == 1
+ assert_equal(p7, True)
+ p8 = len(b_ver) == 1
+ assert_equal(p8, True)
+ # check delete-markers and versions on archive zone
+ (b_dm_az, b_ver_az) = get_versioned_entries(bucket_az)
+ p9 = len(b_dm_az) == 1
+ assert_equal(p9, True)
+ p10 = len(b_ver_az) == 1
+ assert_equal(p10, True)
+ # delete delete-marker on non archive zone
+ dm_version_id = b_dm[0]['foo']['version_id']
+ bucket.delete_key("foo", version_id=dm_version_id)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check delete-markers and versions on non archive zone
+ (b_dm, b_ver) = get_versioned_entries(bucket)
+ p11 = len(b_dm) == 0
+ assert_equal(p11, True)
+ p12 = len(b_ver) == 1
+ assert_equal(p12, True)
+ # check delete-markers and versions on archive zone
+ (b_dm_az, b_ver_az) = get_versioned_entries(bucket_az)
+ p13 = len(b_dm_az) == 1
+ assert_equal(p13, True)
+ p14 = len(b_ver_az) == 1
+ assert_equal(p14, True)
+ # delete delete-marker on archive zone
+ dm_az_version_id = b_dm_az[0]['foo']['version_id']
+ bucket_az.delete_key("foo", version_id=dm_az_version_id)
+ # sync
+ zone_full_checkpoint(az_zones[0].zone, zones[0].zone)
+ # check delete-markers and versions on non archive zone
+ (b_dm, b_ver) = get_versioned_entries(bucket)
+ p15 = len(b_dm) == 0
+ assert_equal(p15, True)
+ p16 = len(b_ver) == 1
+ assert_equal(p16, True)
+ # check delete-markers and versions on archive zone
+ (b_dm_az, b_ver_az) = get_versioned_entries(bucket_az)
+ p17 = len(b_dm_az) == 0
+ assert_equal(p17, True)
+ p17 = len(b_ver_az) == 1
+ assert_equal(p17, True)
+ # check body in zones
+ obj_version_id = b_ver[0]['foo']['version_id']
+ key = bucket.get_key("foo", version_id=obj_version_id)
+ p18 = key.get_contents_as_string(encoding='ascii') == "zero"
+ assert_equal(p18, True)
+ obj_az_version_id = b_ver_az[0]['foo']['version_id']
+ key_az = bucket_az.get_key("foo", version_id=obj_az_version_id)
+ p19 = key_az.get_contents_as_string(encoding='ascii') == "zero"
+ assert_equal(p19, True)
diff --git a/src/test/rgw/rgw_multi/tests_es.py b/src/test/rgw/rgw_multi/tests_es.py
new file mode 100644
index 000000000..08c11718b
--- /dev/null
+++ b/src/test/rgw/rgw_multi/tests_es.py
@@ -0,0 +1,276 @@
+import json
+import logging
+
+import boto
+import boto.s3.connection
+
+import datetime
+import dateutil
+
+from itertools import zip_longest # type: ignore
+
+from nose.tools import eq_ as eq
+
+from .multisite import *
+from .tests import *
+from .zone_es import *
+
+log = logging.getLogger(__name__)
+
+
+def check_es_configured():
+ realm = get_realm()
+ zonegroup = realm.master_zonegroup()
+
+ es_zones = zonegroup.zones_by_type.get("elasticsearch")
+ if not es_zones:
+ raise SkipTest("Requires at least one ES zone")
+
+def is_es_zone(zone_conn):
+ if not zone_conn:
+ return False
+
+ return zone_conn.zone.tier_type() == "elasticsearch"
+
+def verify_search(bucket_name, src_keys, result_keys, f):
+ check_keys = []
+ for k in src_keys:
+ if bucket_name:
+ if bucket_name != k.bucket.name:
+ continue
+ if f(k):
+ check_keys.append(k)
+ check_keys.sort(key = lambda l: (l.bucket.name, l.name, l.version_id))
+
+ log.debug('check keys:' + dump_json(check_keys))
+ log.debug('result keys:' + dump_json(result_keys))
+
+ for k1, k2 in zip_longest(check_keys, result_keys):
+ assert k1
+ assert k2
+ check_object_eq(k1, k2)
+
+def do_check_mdsearch(conn, bucket, src_keys, req_str, src_filter):
+ if bucket:
+ bucket_name = bucket.name
+ else:
+ bucket_name = ''
+ req = MDSearch(conn, bucket_name, req_str)
+ result_keys = req.search(sort_key = lambda k: (k.bucket.name, k.name, k.version_id))
+ verify_search(bucket_name, src_keys, result_keys, src_filter)
+
+def init_env(create_obj, num_keys = 5, buckets_per_zone = 1, bucket_init_cb = None):
+ check_es_configured()
+
+ realm = get_realm()
+ zonegroup = realm.master_zonegroup()
+ zonegroup_conns = ZonegroupConns(zonegroup)
+ buckets, zone_bucket = create_bucket_per_zone(zonegroup_conns, buckets_per_zone = buckets_per_zone)
+
+ if bucket_init_cb:
+ for zone_conn, bucket in zone_bucket:
+ bucket_init_cb(zone_conn, bucket)
+
+ src_keys = []
+
+ owner = None
+
+ obj_prefix=''.join(random.choice(string.ascii_lowercase) for _ in range(6))
+
+ # don't wait for meta sync just yet
+ for zone, bucket in zone_bucket:
+ for count in range(num_keys):
+ objname = obj_prefix + str(count)
+ k = new_key(zone, bucket.name, objname)
+ # k.set_contents_from_string(content + 'x' * count)
+ if not create_obj:
+ continue
+
+ create_obj(k, count)
+
+ if not owner:
+ for list_key in bucket.list_versions():
+ owner = list_key.owner
+ break
+
+ k = bucket.get_key(k.name, version_id = k.version_id)
+ k.owner = owner # owner is not set when doing get_key()
+
+ src_keys.append(k)
+
+ zonegroup_meta_checkpoint(zonegroup)
+
+ sources = []
+ targets = []
+ for target_conn in zonegroup_conns.zones:
+ if not is_es_zone(target_conn):
+ sources.append(target_conn)
+ continue
+
+ targets.append(target_conn)
+
+ buckets = []
+ # make sure all targets are synced
+ for source_conn, bucket in zone_bucket:
+ buckets.append(bucket)
+ for target_conn in targets:
+ zone_bucket_checkpoint(target_conn.zone, source_conn.zone, bucket.name)
+
+ return targets, sources, buckets, src_keys
+
+def test_es_object_search():
+ min_size = 10
+ content = 'a' * min_size
+
+ def create_obj(k, i):
+ k.set_contents_from_string(content + 'x' * i)
+
+ targets, _, buckets, src_keys = init_env(create_obj, num_keys = 5, buckets_per_zone = 2)
+
+ for target_conn in targets:
+
+ # bucket checks
+ for bucket in buckets:
+ # check name
+ do_check_mdsearch(target_conn.conn, None, src_keys , 'bucket == ' + bucket.name, lambda k: k.bucket.name == bucket.name)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'bucket == ' + bucket.name, lambda k: k.bucket.name == bucket.name)
+
+ # check on all buckets
+ for key in src_keys:
+ # limiting to checking specific key name, otherwise could get results from
+ # other runs / tests
+ do_check_mdsearch(target_conn.conn, None, src_keys , 'name == ' + key.name, lambda k: k.name == key.name)
+
+ # check on specific bucket
+ for bucket in buckets:
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'name < ' + key.name, lambda k: k.name < key.name)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'name <= ' + key.name, lambda k: k.name <= key.name)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'name == ' + key.name, lambda k: k.name == key.name)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'name >= ' + key.name, lambda k: k.name >= key.name)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'name > ' + key.name, lambda k: k.name > key.name)
+
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'name == ' + src_keys[0].name + ' or name >= ' + src_keys[2].name,
+ lambda k: k.name == src_keys[0].name or k.name >= src_keys[2].name)
+
+ # check etag
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'etag < ' + key.etag[1:-1], lambda k: k.etag < key.etag)
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'etag == ' + key.etag[1:-1], lambda k: k.etag == key.etag)
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'etag > ' + key.etag[1:-1], lambda k: k.etag > key.etag)
+
+ # check size
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'size < ' + str(key.size), lambda k: k.size < key.size)
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'size <= ' + str(key.size), lambda k: k.size <= key.size)
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'size == ' + str(key.size), lambda k: k.size == key.size)
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'size >= ' + str(key.size), lambda k: k.size >= key.size)
+ for key in src_keys:
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'size > ' + str(key.size), lambda k: k.size > key.size)
+
+def date_from_str(s):
+ return dateutil.parser.parse(s)
+
+def test_es_object_search_custom():
+ min_size = 10
+ content = 'a' * min_size
+
+ def bucket_init(zone_conn, bucket):
+ req = MDSearchConfig(zone_conn.conn, bucket.name)
+ req.set_config('x-amz-meta-foo-str; string, x-amz-meta-foo-int; int, x-amz-meta-foo-date; date')
+
+ def create_obj(k, i):
+ date = datetime.datetime.now() + datetime.timedelta(seconds=1) * i
+ date_str = date.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
+ k.set_contents_from_string(content + 'x' * i, headers = { 'X-Amz-Meta-Foo-Str': str(i * 5),
+ 'X-Amz-Meta-Foo-Int': str(i * 5),
+ 'X-Amz-Meta-Foo-Date': date_str})
+
+ targets, _, buckets, src_keys = init_env(create_obj, num_keys = 5, buckets_per_zone = 1, bucket_init_cb = bucket_init)
+
+
+ for target_conn in targets:
+
+ # bucket checks
+ for bucket in buckets:
+ str_vals = []
+ for key in src_keys:
+ # check string values
+ val = key.get_metadata('foo-str')
+ str_vals.append(val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-str < ' + val, lambda k: k.get_metadata('foo-str') < val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-str <= ' + val, lambda k: k.get_metadata('foo-str') <= val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-str == ' + val, lambda k: k.get_metadata('foo-str') == val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-str >= ' + val, lambda k: k.get_metadata('foo-str') >= val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-str > ' + val, lambda k: k.get_metadata('foo-str') > val)
+
+ # check int values
+ sval = key.get_metadata('foo-int')
+ val = int(sval)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-int < ' + sval, lambda k: int(k.get_metadata('foo-int')) < val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-int <= ' + sval, lambda k: int(k.get_metadata('foo-int')) <= val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-int == ' + sval, lambda k: int(k.get_metadata('foo-int')) == val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-int >= ' + sval, lambda k: int(k.get_metadata('foo-int')) >= val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-int > ' + sval, lambda k: int(k.get_metadata('foo-int')) > val)
+
+ # check int values
+ sval = key.get_metadata('foo-date')
+ val = date_from_str(sval)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-date < ' + sval, lambda k: date_from_str(k.get_metadata('foo-date')) < val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-date <= ' + sval, lambda k: date_from_str(k.get_metadata('foo-date')) <= val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-date == ' + sval, lambda k: date_from_str(k.get_metadata('foo-date')) == val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-date >= ' + sval, lambda k: date_from_str(k.get_metadata('foo-date')) >= val)
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-date > ' + sval, lambda k: date_from_str(k.get_metadata('foo-date')) > val)
+
+ # 'or' query
+ for i in range(len(src_keys) // 2):
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-str <= ' + str_vals[i] + ' or x-amz-meta-foo-str >= ' + str_vals[-i],
+ lambda k: k.get_metadata('foo-str') <= str_vals[i] or k.get_metadata('foo-str') >= str_vals[-i] )
+
+ # 'and' query
+ for i in range(len(src_keys) // 2):
+ do_check_mdsearch(target_conn.conn, bucket, src_keys , 'x-amz-meta-foo-str >= ' + str_vals[i] + ' and x-amz-meta-foo-str <= ' + str_vals[i + 1],
+ lambda k: k.get_metadata('foo-str') >= str_vals[i] and k.get_metadata('foo-str') <= str_vals[i + 1] )
+ # more complicated query
+ for i in range(len(src_keys) // 2):
+ do_check_mdsearch(target_conn.conn, None, src_keys , 'bucket == ' + bucket.name + ' and x-amz-meta-foo-str >= ' + str_vals[i] +
+ ' and (x-amz-meta-foo-str <= ' + str_vals[i + 1] + ')',
+ lambda k: k.bucket.name == bucket.name and (k.get_metadata('foo-str') >= str_vals[i] and
+ k.get_metadata('foo-str') <= str_vals[i + 1]) )
+
+def test_es_bucket_conf():
+ min_size = 0
+
+ def bucket_init(zone_conn, bucket):
+ req = MDSearchConfig(zone_conn.conn, bucket.name)
+ req.set_config('x-amz-meta-foo-str; string, x-amz-meta-foo-int; int, x-amz-meta-foo-date; date')
+
+ targets, sources, buckets, _ = init_env(None, num_keys = 5, buckets_per_zone = 1, bucket_init_cb = bucket_init)
+
+ for source_conn in sources:
+ for bucket in buckets:
+ req = MDSearchConfig(source_conn.conn, bucket.name)
+ conf = req.get_config()
+
+ d = {}
+
+ for entry in conf:
+ d[entry['Key']] = entry['Type']
+
+ eq(len(d), 3)
+ eq(d['x-amz-meta-foo-str'], 'str')
+ eq(d['x-amz-meta-foo-int'], 'int')
+ eq(d['x-amz-meta-foo-date'], 'date')
+
+ req.del_config()
+
+ conf = req.get_config()
+
+ eq(len(conf), 0)
+
+ break # no need to iterate over all zones
diff --git a/src/test/rgw/rgw_multi/tools.py b/src/test/rgw/rgw_multi/tools.py
new file mode 100644
index 000000000..dd7f91ade
--- /dev/null
+++ b/src/test/rgw/rgw_multi/tools.py
@@ -0,0 +1,97 @@
+import json
+import boto
+
+def append_attr_value(d, attr, attrv):
+ if attrv and len(str(attrv)) > 0:
+ d[attr] = attrv
+
+def append_attr(d, k, attr):
+ try:
+ attrv = getattr(k, attr)
+ except:
+ return
+ append_attr_value(d, attr, attrv)
+
+def get_attrs(k, attrs):
+ d = {}
+ for a in attrs:
+ append_attr(d, k, a)
+
+ return d
+
+def append_query_arg(s, n, v):
+ if not v:
+ return s
+ nv = '{n}={v}'.format(n=n, v=v)
+ if not s:
+ return nv
+ return '{s}&{nv}'.format(s=s, nv=nv)
+
+class KeyJSONEncoder(boto.s3.key.Key):
+ @staticmethod
+ def default(k, versioned=False):
+ attrs = ['bucket', 'name', 'size', 'last_modified', 'metadata', 'cache_control',
+ 'content_type', 'content_disposition', 'content_language',
+ 'owner', 'storage_class', 'md5', 'version_id', 'encrypted',
+ 'delete_marker', 'expiry_date', 'VersionedEpoch', 'RgwxTag']
+ d = get_attrs(k, attrs)
+ d['etag'] = k.etag[1:-1]
+ if versioned:
+ d['is_latest'] = k.is_latest
+ return d
+
+class DeleteMarkerJSONEncoder(boto.s3.key.Key):
+ @staticmethod
+ def default(k):
+ attrs = ['name', 'version_id', 'last_modified', 'owner']
+ d = get_attrs(k, attrs)
+ d['delete_marker'] = True
+ d['is_latest'] = k.is_latest
+ return d
+
+class UserJSONEncoder(boto.s3.user.User):
+ @staticmethod
+ def default(k):
+ attrs = ['id', 'display_name']
+ return get_attrs(k, attrs)
+
+class BucketJSONEncoder(boto.s3.bucket.Bucket):
+ @staticmethod
+ def default(k):
+ attrs = ['name', 'creation_date']
+ return get_attrs(k, attrs)
+
+class BotoJSONEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, boto.s3.key.Key):
+ return KeyJSONEncoder.default(obj)
+ if isinstance(obj, boto.s3.deletemarker.DeleteMarker):
+ return DeleteMarkerJSONEncoder.default(obj)
+ if isinstance(obj, boto.s3.user.User):
+ return UserJSONEncoder.default(obj)
+ if isinstance(obj, boto.s3.prefix.Prefix):
+ return (lambda x: {'prefix': x.name})(obj)
+ if isinstance(obj, boto.s3.bucket.Bucket):
+ return BucketJSONEncoder.default(obj)
+ return json.JSONEncoder.default(self, obj)
+
+
+def dump_json(o, cls=BotoJSONEncoder):
+ return json.dumps(o, cls=cls, indent=4)
+
+def assert_raises(excClass, callableObj, *args, **kwargs):
+ """
+ Like unittest.TestCase.assertRaises, but returns the exception.
+ """
+ try:
+ callableObj(*args, **kwargs)
+ except excClass as e:
+ return e
+ else:
+ if hasattr(excClass, '__name__'):
+ excName = excClass.__name__
+ else:
+ excName = str(excClass)
+ raise AssertionError("%s not raised" % excName)
+
+
diff --git a/src/test/rgw/rgw_multi/zone_az.py b/src/test/rgw/rgw_multi/zone_az.py
new file mode 100644
index 000000000..f9cd43574
--- /dev/null
+++ b/src/test/rgw/rgw_multi/zone_az.py
@@ -0,0 +1,42 @@
+import logging
+
+from .multisite import Zone
+
+
+log = logging.getLogger('rgw_multi.tests')
+
+
+class AZone(Zone): # pylint: disable=too-many-ancestors
+ """ archive zone class """
+ def __init__(self, name, zonegroup=None, cluster=None, data=None, zone_id=None, gateways=None):
+ super(AZone, self).__init__(name, zonegroup, cluster, data, zone_id, gateways)
+
+ def is_read_only(self):
+ return False
+
+ def tier_type(self):
+ return "archive"
+
+ def create(self, cluster, args=None, **kwargs):
+ if args is None:
+ args = ''
+ args += ['--tier-type', self.tier_type()]
+ return self.json_command(cluster, 'create', args)
+
+ def has_buckets(self):
+ return False
+
+ def has_roles(self):
+ return True
+
+class AZoneConfig:
+ """ archive zone configuration """
+ def __init__(self, cfg, section):
+ pass
+
+
+def print_connection_info(conn):
+ """print info of connection"""
+ print("Host: " + conn.host+':'+str(conn.port))
+ print("AWS Secret Key: " + conn.aws_secret_access_key)
+ print("AWS Access Key: " + conn.aws_access_key_id)
diff --git a/src/test/rgw/rgw_multi/zone_cloud.py b/src/test/rgw/rgw_multi/zone_cloud.py
new file mode 100644
index 000000000..dd5640cf2
--- /dev/null
+++ b/src/test/rgw/rgw_multi/zone_cloud.py
@@ -0,0 +1,326 @@
+import json
+import requests.compat
+import logging
+
+import boto
+import boto.s3.connection
+
+import dateutil.parser
+import datetime
+
+import re
+
+from nose.tools import eq_ as eq
+from itertools import zip_longest # type: ignore
+from urllib.parse import urlparse
+
+from .multisite import *
+from .tools import *
+
+log = logging.getLogger(__name__)
+
+def get_key_ver(k):
+ if not k.version_id:
+ return 'null'
+ return k.version_id
+
+def unquote(s):
+ if s[0] == '"' and s[-1] == '"':
+ return s[1:-1]
+ return s
+
+def check_object_eq(k1, k2, check_extra = True):
+ assert k1
+ assert k2
+ log.debug('comparing key name=%s', k1.name)
+ eq(k1.name, k2.name)
+ eq(k1.metadata, k2.metadata)
+ # eq(k1.cache_control, k2.cache_control)
+ eq(k1.content_type, k2.content_type)
+ eq(k1.content_encoding, k2.content_encoding)
+ eq(k1.content_disposition, k2.content_disposition)
+ eq(k1.content_language, k2.content_language)
+
+ eq(unquote(k1.etag), unquote(k2.etag))
+
+ mtime1 = dateutil.parser.parse(k1.last_modified)
+ mtime2 = dateutil.parser.parse(k2.last_modified)
+ log.debug('k1.last_modified=%s k2.last_modified=%s', k1.last_modified, k2.last_modified)
+ assert abs((mtime1 - mtime2).total_seconds()) < 1 # handle different time resolution
+ # if check_extra:
+ # eq(k1.owner.id, k2.owner.id)
+ # eq(k1.owner.display_name, k2.owner.display_name)
+ # eq(k1.storage_class, k2.storage_class)
+ eq(k1.size, k2.size)
+ eq(get_key_ver(k1), get_key_ver(k2))
+ # eq(k1.encrypted, k2.encrypted)
+
+def make_request(conn, method, bucket, key, query_args, headers):
+ result = conn.make_request(method, bucket=bucket, key=key, query_args=query_args, headers=headers)
+ if result.status // 100 != 2:
+ raise boto.exception.S3ResponseError(result.status, result.reason, result.read())
+ return result
+
+class CloudKey:
+ def __init__(self, zone_bucket, k):
+ self.zone_bucket = zone_bucket
+
+ # we need two keys: when listing buckets, we get keys that only contain partial data
+ # but we need to have the full data so that we could use all the meta-rgwx- headers
+ # that are needed in order to create a correct representation of the object
+ self.key = k
+ self.rgwx_key = k # assuming k has all the meta info on, if not then we'll update it in update()
+ self.update()
+
+ def update(self):
+ k = self.key
+ rk = self.rgwx_key
+
+ self.size = rk.size
+ orig_name = rk.metadata.get('rgwx-source-key')
+ if not orig_name:
+ self.rgwx_key = self.zone_bucket.bucket.get_key(k.name, version_id = k.version_id)
+ rk = self.rgwx_key
+ orig_name = rk.metadata.get('rgwx-source-key')
+
+ self.name = orig_name
+ self.version_id = rk.metadata.get('rgwx-source-version-id')
+
+ ve = rk.metadata.get('rgwx-versioned-epoch')
+ if ve:
+ self.versioned_epoch = int(ve)
+ else:
+ self.versioned_epoch = 0
+
+ mt = rk.metadata.get('rgwx-source-mtime')
+ if mt:
+ self.last_modified = datetime.datetime.utcfromtimestamp(float(mt)).strftime('%a, %d %b %Y %H:%M:%S GMT')
+ else:
+ self.last_modified = k.last_modified
+
+ et = rk.metadata.get('rgwx-source-etag')
+ if rk.etag.find('-') >= 0 or et.find('-') >= 0:
+ # in this case we will use the source etag as it was uploaded via multipart upload
+ # in one of the zones, so there's no way to make sure etags are calculated the same
+ # way. In the other case we'd just want to keep the etag that was generated in the
+ # regular upload mechanism, which should be consistent in both ends
+ self.etag = et
+ else:
+ self.etag = rk.etag
+
+ if k.etag[0] == '"' and self.etag[0] != '"': # inconsistent etag quoting when listing bucket vs object get
+ self.etag = '"' + self.etag + '"'
+
+ new_meta = {}
+ for meta_key, meta_val in k.metadata.items():
+ if not meta_key.startswith('rgwx-'):
+ new_meta[meta_key] = meta_val
+
+ self.metadata = new_meta
+
+ self.cache_control = k.cache_control
+ self.content_type = k.content_type
+ self.content_encoding = k.content_encoding
+ self.content_disposition = k.content_disposition
+ self.content_language = k.content_language
+
+
+ def get_contents_as_string(self, encoding=None):
+ r = self.key.get_contents_as_string(encoding=encoding)
+
+ # the previous call changed the status of the source object, as it loaded
+ # its metadata
+
+ self.rgwx_key = self.key
+ self.update()
+
+ return r
+
+
+class CloudZoneBucket:
+ def __init__(self, zone_conn, target_path, name):
+ self.zone_conn = zone_conn
+ self.name = name
+ self.cloud_conn = zone_conn.zone.cloud_conn
+
+ target_path = target_path[:]
+ if target_path[-1] != '/':
+ target_path += '/'
+ target_path = target_path.replace('${bucket}', name)
+
+ tp = target_path.split('/', 1)
+
+ if len(tp) == 1:
+ self.target_bucket = target_path
+ self.target_prefix = ''
+ else:
+ self.target_bucket = tp[0]
+ self.target_prefix = tp[1]
+
+ log.debug('target_path=%s target_bucket=%s target_prefix=%s', target_path, self.target_bucket, self.target_prefix)
+ self.bucket = self.cloud_conn.get_bucket(self.target_bucket)
+
+ def get_all_versions(self):
+ l = []
+
+ for k in self.bucket.get_all_keys(prefix=self.target_prefix):
+ new_key = CloudKey(self, k)
+
+ log.debug('appending o=[\'%s\', \'%s\', \'%d\']', new_key.name, new_key.version_id, new_key.versioned_epoch)
+ l.append(new_key)
+
+
+ sort_key = lambda k: (k.name, -k.versioned_epoch)
+ l.sort(key = sort_key)
+
+ for new_key in l:
+ yield new_key
+
+ def get_key(self, name, version_id=None):
+ return CloudKey(self, self.bucket.get_key(name, version_id=version_id))
+
+
+def parse_endpoint(endpoint):
+ o = urlparse(endpoint)
+
+ netloc = o.netloc.split(':')
+
+ host = netloc[0]
+
+ if len(netloc) > 1:
+ port = int(netloc[1])
+ else:
+ port = o.port
+
+ is_secure = False
+
+ if o.scheme == 'https':
+ is_secure = True
+
+ if not port:
+ if is_secure:
+ port = 443
+ else:
+ port = 80
+
+ return host, port, is_secure
+
+
+class CloudZone(Zone):
+ def __init__(self, name, cloud_endpoint, credentials, source_bucket, target_path,
+ zonegroup = None, cluster = None, data = None, zone_id = None, gateways = None):
+ self.cloud_endpoint = cloud_endpoint
+ self.credentials = credentials
+ self.source_bucket = source_bucket
+ self.target_path = target_path
+
+ self.target_path = self.target_path.replace('${zone}', name)
+ # self.target_path = self.target_path.replace('${zone_id}', zone_id)
+ self.target_path = self.target_path.replace('${zonegroup}', zonegroup.name)
+ self.target_path = self.target_path.replace('${zonegroup_id}', zonegroup.id)
+
+ log.debug('target_path=%s', self.target_path)
+
+ host, port, is_secure = parse_endpoint(cloud_endpoint)
+
+ self.cloud_conn = boto.connect_s3(
+ aws_access_key_id = credentials.access_key,
+ aws_secret_access_key = credentials.secret,
+ host = host,
+ port = port,
+ is_secure = is_secure,
+ calling_format = boto.s3.connection.OrdinaryCallingFormat())
+ super(CloudZone, self).__init__(name, zonegroup, cluster, data, zone_id, gateways)
+
+
+ def is_read_only(self):
+ return True
+
+ def tier_type(self):
+ return "cloud"
+
+ def create(self, cluster, args = None, check_retcode = True):
+ """ create the object with the given arguments """
+
+ if args is None:
+ args = ''
+
+ tier_config = ','.join([ 'connection.endpoint=' + self.cloud_endpoint,
+ 'connection.access_key=' + self.credentials.access_key,
+ 'connection.secret=' + self.credentials.secret,
+ 'target_path=' + re.escape(self.target_path)])
+
+ args += [ '--tier-type', self.tier_type(), '--tier-config', tier_config ]
+
+ return self.json_command(cluster, 'create', args, check_retcode=check_retcode)
+
+ def has_buckets(self):
+ return False
+
+ def has_roles(self):
+ return False
+
+ class Conn(ZoneConn):
+ def __init__(self, zone, credentials):
+ super(CloudZone.Conn, self).__init__(zone, credentials)
+
+ def get_bucket(self, bucket_name):
+ return CloudZoneBucket(self, self.zone.target_path, bucket_name)
+
+ def create_bucket(self, name):
+ # should not be here, a bug in the test suite
+ log.critical('Conn.create_bucket() should not be called in cloud zone')
+ assert False
+
+ def check_bucket_eq(self, zone_conn, bucket_name):
+ assert(zone_conn.zone.tier_type() == "rados")
+
+ log.info('comparing bucket=%s zones={%s, %s}', bucket_name, self.name, self.name)
+ b1 = self.get_bucket(bucket_name)
+ b2 = zone_conn.get_bucket(bucket_name)
+
+ log.debug('bucket1 objects:')
+ for o in b1.get_all_versions():
+ log.debug('o=%s', o.name)
+ log.debug('bucket2 objects:')
+ for o in b2.get_all_versions():
+ log.debug('o=%s', o.name)
+
+ for k1, k2 in zip_longest(b1.get_all_versions(), b2.get_all_versions()):
+ if k1 is None:
+ log.critical('key=%s is missing from zone=%s', k2.name, self.name)
+ assert False
+ if k2 is None:
+ log.critical('key=%s is missing from zone=%s', k1.name, zone_conn.name)
+ assert False
+
+ check_object_eq(k1, k2)
+
+
+ log.info('success, bucket identical: bucket=%s zones={%s, %s}', bucket_name, self.name, zone_conn.name)
+
+ return True
+
+ def create_role(self, path, rolename, policy_document, tag_list):
+ assert False
+
+ def get_conn(self, credentials):
+ return self.Conn(self, credentials)
+
+
+class CloudZoneConfig:
+ def __init__(self, cfg, section):
+ self.endpoint = cfg.get(section, 'endpoint')
+ access_key = cfg.get(section, 'access_key')
+ secret = cfg.get(section, 'secret')
+ self.credentials = Credentials(access_key, secret)
+ try:
+ self.target_path = cfg.get(section, 'target_path')
+ except:
+ self.target_path = 'rgw-${zonegroup_id}/${bucket}'
+
+ try:
+ self.source_bucket = cfg.get(section, 'source_bucket')
+ except:
+ self.source_bucket = '*'
+
diff --git a/src/test/rgw/rgw_multi/zone_es.py b/src/test/rgw/rgw_multi/zone_es.py
new file mode 100644
index 000000000..e98b3fdd8
--- /dev/null
+++ b/src/test/rgw/rgw_multi/zone_es.py
@@ -0,0 +1,256 @@
+import json
+import requests.compat
+import logging
+
+import boto
+import boto.s3.connection
+
+import dateutil.parser
+
+from nose.tools import eq_ as eq
+from itertools import zip_longest # type: ignore
+
+from .multisite import *
+from .tools import *
+
+log = logging.getLogger(__name__)
+
+def get_key_ver(k):
+ if not k.version_id:
+ return 'null'
+ return k.version_id
+
+def check_object_eq(k1, k2, check_extra = True):
+ assert k1
+ assert k2
+ log.debug('comparing key name=%s', k1.name)
+ eq(k1.name, k2.name)
+ eq(k1.metadata, k2.metadata)
+ # eq(k1.cache_control, k2.cache_control)
+ eq(k1.content_type, k2.content_type)
+ # eq(k1.content_encoding, k2.content_encoding)
+ # eq(k1.content_disposition, k2.content_disposition)
+ # eq(k1.content_language, k2.content_language)
+ eq(k1.etag, k2.etag)
+ mtime1 = dateutil.parser.parse(k1.last_modified)
+ mtime2 = dateutil.parser.parse(k2.last_modified)
+ assert abs((mtime1 - mtime2).total_seconds()) < 1 # handle different time resolution
+ if check_extra:
+ eq(k1.owner.id, k2.owner.id)
+ eq(k1.owner.display_name, k2.owner.display_name)
+ # eq(k1.storage_class, k2.storage_class)
+ eq(k1.size, k2.size)
+ eq(get_key_ver(k1), get_key_ver(k2))
+ # eq(k1.encrypted, k2.encrypted)
+
+def make_request(conn, method, bucket, key, query_args, headers):
+ result = conn.make_request(method, bucket=bucket, key=key, query_args=query_args, headers=headers)
+ if result.status // 100 != 2:
+ raise boto.exception.S3ResponseError(result.status, result.reason, result.read())
+ return result
+
+
+class MDSearch:
+ def __init__(self, conn, bucket_name, query, query_args = None, marker = None):
+ self.conn = conn
+ self.bucket_name = bucket_name or ''
+ if bucket_name:
+ self.bucket = boto.s3.bucket.Bucket(name=bucket_name)
+ else:
+ self.bucket = None
+ self.query = query
+ self.query_args = query_args
+ self.max_keys = None
+ self.marker = marker
+
+ def raw_search(self):
+ q = self.query or ''
+ query_args = append_query_arg(self.query_args, 'query', requests.compat.quote_plus(q))
+ if self.max_keys is not None:
+ query_args = append_query_arg(query_args, 'max-keys', self.max_keys)
+ if self.marker:
+ query_args = append_query_arg(query_args, 'marker', self.marker)
+
+ query_args = append_query_arg(query_args, 'format', 'json')
+
+ headers = {}
+
+ result = make_request(self.conn, "GET", bucket=self.bucket_name, key='', query_args=query_args, headers=headers)
+
+ l = []
+
+ result_dict = json.loads(result.read())
+
+ for entry in result_dict['Objects']:
+ bucket = self.conn.get_bucket(entry['Bucket'], validate = False)
+ k = boto.s3.key.Key(bucket, entry['Key'])
+
+ k.version_id = entry['Instance']
+ k.etag = entry['ETag']
+ k.owner = boto.s3.user.User(id=entry['Owner']['ID'], display_name=entry['Owner']['DisplayName'])
+ k.last_modified = entry['LastModified']
+ k.size = entry['Size']
+ k.content_type = entry['ContentType']
+ k.versioned_epoch = entry['VersionedEpoch']
+
+ k.metadata = {}
+ for e in entry['CustomMetadata']:
+ k.metadata[e['Name']] = str(e['Value']) # int values will return as int, cast to string for compatibility with object meta response
+
+ l.append(k)
+
+ return result_dict, l
+
+ def search(self, drain = True, sort = True, sort_key = None):
+ l = []
+
+ is_done = False
+
+ while not is_done:
+ result, result_keys = self.raw_search()
+
+ l = l + result_keys
+
+ is_done = not (drain and (result['IsTruncated'] == "true"))
+ marker = result['Marker']
+
+ if sort:
+ if not sort_key:
+ sort_key = lambda k: (k.name, -k.versioned_epoch)
+ l.sort(key = sort_key)
+
+ return l
+
+
+class MDSearchConfig:
+ def __init__(self, conn, bucket_name):
+ self.conn = conn
+ self.bucket_name = bucket_name or ''
+ if bucket_name:
+ self.bucket = boto.s3.bucket.Bucket(name=bucket_name)
+ else:
+ self.bucket = None
+
+ def send_request(self, conf, method):
+ query_args = 'mdsearch'
+ headers = None
+ if conf:
+ headers = { 'X-Amz-Meta-Search': conf }
+
+ query_args = append_query_arg(query_args, 'format', 'json')
+
+ return make_request(self.conn, method, bucket=self.bucket_name, key='', query_args=query_args, headers=headers)
+
+ def get_config(self):
+ result = self.send_request(None, 'GET')
+ return json.loads(result.read())
+
+ def set_config(self, conf):
+ self.send_request(conf, 'POST')
+
+ def del_config(self):
+ self.send_request(None, 'DELETE')
+
+
+class ESZoneBucket:
+ def __init__(self, zone_conn, name, conn):
+ self.zone_conn = zone_conn
+ self.name = name
+ self.conn = conn
+
+ self.bucket = boto.s3.bucket.Bucket(name=name)
+
+ def get_all_versions(self):
+
+ marker = None
+ is_done = False
+
+ req = MDSearch(self.conn, self.name, 'bucket == ' + self.name, marker=marker)
+
+ for k in req.search():
+ yield k
+
+
+
+
+class ESZone(Zone):
+ def __init__(self, name, es_endpoint, zonegroup = None, cluster = None, data = None, zone_id = None, gateways = None):
+ self.es_endpoint = es_endpoint
+ super(ESZone, self).__init__(name, zonegroup, cluster, data, zone_id, gateways)
+
+ def is_read_only(self):
+ return True
+
+ def tier_type(self):
+ return "elasticsearch"
+
+ def create(self, cluster, args = None, check_retcode = True):
+ """ create the object with the given arguments """
+
+ if args is None:
+ args = ''
+
+ tier_config = ','.join([ 'endpoint=' + self.es_endpoint, 'explicit_custom_meta=false' ])
+
+ args += [ '--tier-type', self.tier_type(), '--tier-config', tier_config ]
+
+ return self.json_command(cluster, 'create', args, check_retcode=check_retcode)
+
+ def has_buckets(self):
+ return False
+
+ def has_roles(self):
+ return False
+
+ class Conn(ZoneConn):
+ def __init__(self, zone, credentials):
+ super(ESZone.Conn, self).__init__(zone, credentials)
+
+ def get_bucket(self, bucket_name):
+ return ESZoneBucket(self, bucket_name, self.conn)
+
+ def create_bucket(self, name):
+ # should not be here, a bug in the test suite
+ log.critical('Conn.create_bucket() should not be called in ES zone')
+ assert False
+
+ def check_bucket_eq(self, zone_conn, bucket_name):
+ assert(zone_conn.zone.tier_type() == "rados")
+
+ log.info('comparing bucket=%s zones={%s, %s}', bucket_name, self.name, self.name)
+ b1 = self.get_bucket(bucket_name)
+ b2 = zone_conn.get_bucket(bucket_name)
+
+ log.debug('bucket1 objects:')
+ for o in b1.get_all_versions():
+ log.debug('o=%s', o.name)
+ log.debug('bucket2 objects:')
+ for o in b2.get_all_versions():
+ log.debug('o=%s', o.name)
+
+ for k1, k2 in zip_longest(b1.get_all_versions(), b2.get_all_versions()):
+ if k1 is None:
+ log.critical('key=%s is missing from zone=%s', k2.name, self.name)
+ assert False
+ if k2 is None:
+ log.critical('key=%s is missing from zone=%s', k1.name, zone_conn.name)
+ assert False
+
+ check_object_eq(k1, k2)
+
+
+ log.info('success, bucket identical: bucket=%s zones={%s, %s}', bucket_name, self.name, zone_conn.name)
+
+ return True
+
+ def create_role(self, path, rolename, policy_document, tag_list):
+ assert False
+
+ def get_conn(self, credentials):
+ return self.Conn(self, credentials)
+
+
+class ESZoneConfig:
+ def __init__(self, cfg, section):
+ self.endpoint = cfg.get(section, 'endpoint')
+
diff --git a/src/test/rgw/rgw_multi/zone_rados.py b/src/test/rgw/rgw_multi/zone_rados.py
new file mode 100644
index 000000000..ac4edd004
--- /dev/null
+++ b/src/test/rgw/rgw_multi/zone_rados.py
@@ -0,0 +1,134 @@
+import logging
+from boto.s3.deletemarker import DeleteMarker
+
+from itertools import zip_longest # type: ignore
+
+from nose.tools import eq_ as eq
+
+from .multisite import *
+
+log = logging.getLogger(__name__)
+
+def check_object_eq(k1, k2, check_extra = True):
+ assert k1
+ assert k2
+ log.debug('comparing key name=%s', k1.name)
+ eq(k1.name, k2.name)
+ eq(k1.version_id, k2.version_id)
+ eq(k1.is_latest, k2.is_latest)
+ eq(k1.last_modified, k2.last_modified)
+ if isinstance(k1, DeleteMarker):
+ assert isinstance(k2, DeleteMarker)
+ return
+
+ eq(k1.get_contents_as_string(), k2.get_contents_as_string())
+ eq(k1.metadata, k2.metadata)
+ eq(k1.cache_control, k2.cache_control)
+ eq(k1.content_type, k2.content_type)
+ eq(k1.content_encoding, k2.content_encoding)
+ eq(k1.content_disposition, k2.content_disposition)
+ eq(k1.content_language, k2.content_language)
+ eq(k1.etag, k2.etag)
+ if check_extra:
+ eq(k1.owner.id, k2.owner.id)
+ eq(k1.owner.display_name, k2.owner.display_name)
+ eq(k1.storage_class, k2.storage_class)
+ eq(k1.size, k2.size)
+ eq(k1.encrypted, k2.encrypted)
+
+class RadosZone(Zone):
+ def __init__(self, name, zonegroup = None, cluster = None, data = None, zone_id = None, gateways = None):
+ super(RadosZone, self).__init__(name, zonegroup, cluster, data, zone_id, gateways)
+
+ def tier_type(self):
+ return "rados"
+
+
+ class Conn(ZoneConn):
+ def __init__(self, zone, credentials):
+ super(RadosZone.Conn, self).__init__(zone, credentials)
+
+ def get_bucket(self, name):
+ return self.conn.get_bucket(name)
+
+ def create_bucket(self, name):
+ return self.conn.create_bucket(name)
+
+ def delete_bucket(self, name):
+ return self.conn.delete_bucket(name)
+
+ def check_bucket_eq(self, zone_conn, bucket_name):
+ log.info('comparing bucket=%s zones={%s, %s}', bucket_name, self.name, zone_conn.name)
+ b1 = self.get_bucket(bucket_name)
+ b2 = zone_conn.get_bucket(bucket_name)
+
+ b1_versions = b1.list_versions()
+ log.debug('bucket1 objects:')
+ for o in b1_versions:
+ log.debug('o=%s', o.name)
+
+ b2_versions = b2.list_versions()
+ log.debug('bucket2 objects:')
+ for o in b2_versions:
+ log.debug('o=%s', o.name)
+
+ for k1, k2 in zip_longest(b1_versions, b2_versions):
+ if k1 is None:
+ log.critical('key=%s is missing from zone=%s', k2.name, self.name)
+ assert False
+ if k2 is None:
+ log.critical('key=%s is missing from zone=%s', k1.name, zone_conn.name)
+ assert False
+
+ check_object_eq(k1, k2)
+
+ if isinstance(k1, DeleteMarker):
+ # verify that HEAD sees a delete marker
+ assert b1.get_key(k1.name) is None
+ assert b2.get_key(k2.name) is None
+ else:
+ # now get the keys through a HEAD operation, verify that the available data is the same
+ k1_head = b1.get_key(k1.name, version_id=k1.version_id)
+ k2_head = b2.get_key(k2.name, version_id=k2.version_id)
+ check_object_eq(k1_head, k2_head, False)
+
+ if k1.version_id:
+ # compare the olh to make sure they agree about the current version
+ k1_olh = b1.get_key(k1.name)
+ k2_olh = b2.get_key(k2.name)
+ # if there's a delete marker, HEAD will return None
+ if k1_olh or k2_olh:
+ check_object_eq(k1_olh, k2_olh, False)
+
+ log.info('success, bucket identical: bucket=%s zones={%s, %s}', bucket_name, self.name, zone_conn.name)
+
+ return True
+
+ def get_role(self, role_name):
+ return self.iam_conn.get_role(role_name)
+
+ def check_role_eq(self, zone_conn, role_name):
+ log.info('comparing role=%s zones={%s, %s}', role_name, self.name, zone_conn.name)
+ r1 = self.get_role(role_name)
+ r2 = zone_conn.get_role(role_name)
+
+ assert r1
+ assert r2
+ log.debug('comparing role name=%s', r1['get_role_response']['get_role_result']['role']['role_name'])
+ eq(r1['get_role_response']['get_role_result']['role']['role_name'], r2['get_role_response']['get_role_result']['role']['role_name'])
+ eq(r1['get_role_response']['get_role_result']['role']['role_id'], r2['get_role_response']['get_role_result']['role']['role_id'])
+ eq(r1['get_role_response']['get_role_result']['role']['path'], r2['get_role_response']['get_role_result']['role']['path'])
+ eq(r1['get_role_response']['get_role_result']['role']['arn'], r2['get_role_response']['get_role_result']['role']['arn'])
+ eq(r1['get_role_response']['get_role_result']['role']['max_session_duration'], r2['get_role_response']['get_role_result']['role']['max_session_duration'])
+ eq(r1['get_role_response']['get_role_result']['role']['assume_role_policy_document'], r2['get_role_response']['get_role_result']['role']['assume_role_policy_document'])
+
+ log.info('success, role identical: role=%s zones={%s, %s}', role_name, self.name, zone_conn.name)
+
+ return True
+
+ def create_role(self, path, rolename, policy_document, tag_list):
+ return self.iam_conn.create_role(rolename, policy_document, path)
+
+ def get_conn(self, credentials):
+ return self.Conn(self, credentials)
+
diff --git a/src/test/rgw/test-ceph-diff-sorted.sh b/src/test/rgw/test-ceph-diff-sorted.sh
new file mode 100755
index 000000000..dddf4ae1b
--- /dev/null
+++ b/src/test/rgw/test-ceph-diff-sorted.sh
@@ -0,0 +1,108 @@
+#!/usr/bin/env bash
+
+# set -e -x
+
+. "`dirname $0`/test-rgw-common.sh"
+
+temp_prefix="/tmp/`basename $0`-$$"
+
+short=${temp_prefix}-short
+short_w_blank=${temp_prefix}-short-w-blank
+long=${temp_prefix}-long
+unsorted=${temp_prefix}-unsorted
+empty=${temp_prefix}-empty
+fake=${temp_prefix}-fake
+
+out1=${temp_prefix}-out1
+out2=${temp_prefix}-out2
+
+cat >"${short}" <<EOF
+bear
+fox
+hippo
+zebra
+EOF
+
+cat >"${short_w_blank}" <<EOF
+bear
+fox
+hippo
+
+zebra
+EOF
+
+cat >"${long}" <<EOF
+badger
+cuttlefish
+fox
+llama
+octopus
+penguine
+seal
+squid
+whale
+yak
+zebra
+EOF
+
+cat >"${unsorted}" <<EOF
+bear
+hippo
+fox
+zebra
+EOF
+
+touch $empty
+
+#### testing ####
+
+# test perfect match
+ceph-diff-sorted $long $long >"${out1}"
+$assert $? -eq 0
+$assert $(cat $out1 | wc -l) -eq 0
+
+# test non-match; use /bin/diff to verify
+/bin/diff $short $long >"${out2}"
+ceph-diff-sorted $short $long >"${out1}"
+$assert $? -eq 1
+$assert $(cat $out1 | grep '^<' | wc -l) -eq $(cat $out2 | grep '^<' | wc -l)
+$assert $(cat $out1 | grep '^>' | wc -l) -eq $(cat $out2 | grep '^>' | wc -l)
+
+/bin/diff $long $short >"${out2}"
+ceph-diff-sorted $long $short >"${out1}"
+$assert $? -eq 1
+$assert $(cat $out1 | grep '^<' | wc -l) -eq $(cat $out2 | grep '^<' | wc -l)
+$assert $(cat $out1 | grep '^>' | wc -l) -eq $(cat $out2 | grep '^>' | wc -l)
+
+# test w blank line
+ceph-diff-sorted $short $short_w_blank 2>/dev/null
+$assert $? -eq 4
+
+ceph-diff-sorted $short_w_blank $short 2>/dev/null
+$assert $? -eq 4
+
+# test unsorted input
+ceph-diff-sorted $short $unsorted >"${out2}" 2>/dev/null
+$assert $? -eq 4
+
+ceph-diff-sorted $unsorted $short >"${out2}" 2>/dev/null
+$assert $? -eq 4
+
+# test bad # of args
+ceph-diff-sorted 2>/dev/null
+$assert $? -eq 2
+
+ceph-diff-sorted $short 2>/dev/null
+$assert $? -eq 2
+
+# test bad file path
+
+ceph-diff-sorted $short $fake 2>/dev/null
+$assert $? -eq 3
+
+ceph-diff-sorted $fake $short 2>/dev/null
+$assert $? -eq 3
+
+#### clean-up ####
+
+/bin/rm -f $short $short_w_blank $long $unsorted $empty $out1 $out2
diff --git a/src/test/rgw/test-rgw-call.sh b/src/test/rgw/test-rgw-call.sh
new file mode 100755
index 000000000..49399ebc3
--- /dev/null
+++ b/src/test/rgw/test-rgw-call.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+. "`dirname $0`/test-rgw-common.sh"
+. "`dirname $0`/test-rgw-meta-sync.sh"
+
+# Do not use eval here. We have eval in test-rgw-common.sh:x(), so adding
+# one here creates a double-eval situation. Passing arguments with spaces
+# becomes impossible when double-eval strips escaping and quotes.
+$@
diff --git a/src/test/rgw/test-rgw-common.sh b/src/test/rgw/test-rgw-common.sh
new file mode 100644
index 000000000..da263c6d9
--- /dev/null
+++ b/src/test/rgw/test-rgw-common.sh
@@ -0,0 +1,195 @@
+#!/usr/bin/env bash
+
+rgw_flags="--debug-rgw=20 --debug-ms=1"
+
+function _assert {
+ src=$1; shift
+ lineno=$1; shift
+ [ "$@" ] || echo "$src: $lineno: assert failed: $@" || exit 1
+}
+
+assert="eval _assert \$BASH_SOURCE \$LINENO"
+
+function var_to_python_json_index {
+ echo "['$1']" | sed "s/\./'\]\['/g"
+}
+
+function json_extract {
+var=""
+[ "$1" != "" ] && var=$(var_to_python_json_index $1)
+shift
+python3 - <<END
+import json
+s='$@'
+data = json.loads(s)
+print(data$var)
+END
+}
+
+function python_array_len {
+python3 - <<END
+arr=$@
+print(len(arr))
+END
+}
+
+function project_python_array_field {
+var=$(var_to_python_json_index $1)
+shift
+python3 - <<END
+arr=$@
+s='( '
+for x in arr:
+ s += '"' + str(x$var) + '" '
+s += ')'
+print(s)
+END
+}
+
+
+x() {
+ # echo "x " "$@" >&2
+ eval "$@"
+}
+
+
+script_dir=`dirname $0`
+root_path=`(cd $script_dir/../..; pwd)`
+
+mstart=$root_path/mstart.sh
+mstop=$root_path/mstop.sh
+mrun=$root_path/mrun
+mrgw=$root_path/mrgw.sh
+
+url=http://localhost
+
+function start_ceph_cluster {
+ [ $# -ne 1 ] && echo "start_ceph_cluster() needs 1 param" && exit 1
+
+ echo "$mstart $1"
+}
+
+function rgw_admin {
+ [ $# -lt 1 ] && echo "rgw_admin() needs 1 param" && exit 1
+
+ echo "$mrun $1 radosgw-admin"
+}
+
+function rgw {
+ [ $# -lt 2 ] && echo "rgw() needs at least 2 params" && exit 1
+
+ name=$1
+ port=$2
+ ssl_port=0 #ssl port not used
+ shift 2
+
+ echo "$mrgw $name $port $ssl_port $rgw_flags $@"
+}
+
+function init_first_zone {
+ [ $# -ne 7 ] && echo "init_first_zone() needs 7 params" && exit 1
+
+ cid=$1
+ realm=$2
+ zg=$3
+ zone=$4
+ endpoints=$5
+
+ access_key=$6
+ secret=$7
+
+# initialize realm
+ x $(rgw_admin $cid) realm create --rgw-realm=$realm
+
+# create zonegroup, zone
+ x $(rgw_admin $cid) zonegroup create --rgw-zonegroup=$zg --master --default
+ x $(rgw_admin $cid) zone create --rgw-zonegroup=$zg --rgw-zone=$zone --access-key=${access_key} --secret=${secret} --endpoints=$endpoints --default
+ x $(rgw_admin $cid) user create --uid=zone.user --display-name=ZoneUser --access-key=${access_key} --secret=${secret} --system
+
+ x $(rgw_admin $cid) period update --commit
+}
+
+function init_zone_in_existing_zg {
+ [ $# -ne 8 ] && echo "init_zone_in_existing_zg() needs 8 params" && exit 1
+
+ cid=$1
+ realm=$2
+ zg=$3
+ zone=$4
+ master_zg_zone1_port=$5
+ endpoints=$6
+
+ access_key=$7
+ secret=$8
+
+ x $(rgw_admin $cid) realm pull --url=$url:$master_zg_zone1_port --access-key=${access_key} --secret=${secret} --default
+ x $(rgw_admin $cid) zonegroup default --rgw-zonegroup=$zg
+ x $(rgw_admin $cid) zone create --rgw-zonegroup=$zg --rgw-zone=$zone --access-key=${access_key} --secret=${secret} --endpoints=$endpoints
+ x $(rgw_admin $cid) period update --commit
+}
+
+function init_first_zone_in_slave_zg {
+ [ $# -ne 8 ] && echo "init_first_zone_in_slave_zg() needs 8 params" && exit 1
+
+ cid=$1
+ realm=$2
+ zg=$3
+ zone=$4
+ master_zg_zone1_port=$5
+ endpoints=$6
+
+ access_key=$7
+ secret=$8
+
+# create zonegroup, zone
+ x $(rgw_admin $cid) realm pull --url=$url:$master_zg_zone1_port --access-key=${access_key} --secret=${secret}
+ x $(rgw_admin $cid) realm default --rgw-realm=$realm
+ x $(rgw_admin $cid) zonegroup create --rgw-realm=$realm --rgw-zonegroup=$zg --endpoints=$endpoints --default
+ x $(rgw_admin $cid) zonegroup default --rgw-zonegroup=$zg
+
+ x $(rgw_admin $cid) zone create --rgw-zonegroup=$zg --rgw-zone=$zone --access-key=${access_key} --secret=${secret} --endpoints=$endpoints
+ x $(rgw_admin $cid) zone default --rgw-zone=$zone
+ x $(rgw_admin $cid) zonegroup add --rgw-zonegroup=$zg --rgw-zone=$zone
+
+ x $(rgw_admin $cid) period update --commit
+
+}
+
+function call_rgw_admin {
+ cid=$1
+ shift 1
+ x $(rgw_admin $cid) "$@"
+}
+
+function get_mstart_parameters {
+ [ $# -ne 1 ] && echo "get_mstart_parameters() needs 1 param" && exit 1
+ # bash arrays start from zero
+ index="$1"
+ index=$((index-1))
+ if [ -n "$DEV_LIST" ]; then
+ IFS=', ' read -r -a dev_list <<< "$DEV_LIST"
+ if [ ${#dev_list[@]} -gt "$index" ]; then
+ local dev_name=${dev_list["$index"]}
+ parameters="--bluestore-devs $dev_name"
+ fi
+ fi
+
+ if [ -n "$DB_DEV_LIST" ]; then
+ IFS=', ' read -r -a db_dev_list <<< "$DB_DEV_LIST"
+ if [ ${#db_dev_list[@]} -gt "$index" ]; then
+ local dev_name=${db_dev_list["$index"]}
+ parameters="$parameters"" -o bluestore_block_db_path=$dev_name"
+ fi
+ fi
+
+ if [ -n "$WAL_DEV_LIST" ]; then
+ IFS=', ' read -r -a wal_dev_list <<< "$WAL_DEV_LIST"
+ if [ ${#wal_dev_list[@]} -gt "$index" ]; then
+ local dev_name=${wal_dev_list["$index"]}
+ parameters="$parameters"" -o bluestore_block_wal_path=$dev_name"
+ fi
+ fi
+
+ echo "$parameters"
+}
+
diff --git a/src/test/rgw/test-rgw-meta-sync.sh b/src/test/rgw/test-rgw-meta-sync.sh
new file mode 100755
index 000000000..18f425298
--- /dev/null
+++ b/src/test/rgw/test-rgw-meta-sync.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+
+. "`dirname $0`/test-rgw-common.sh"
+
+set -e
+
+function get_metadata_sync_status {
+ cid=$1
+ realm=$2
+
+ meta_sync_status_json=`$(rgw_admin $cid) --rgw-realm=$realm metadata sync status`
+
+ global_sync_status=$(json_extract sync_status.info.status $meta_sync_status_json)
+ num_shards=$(json_extract sync_status.info.num_shards $meta_sync_status_json)
+
+ echo "sync_status: $global_sync_status"
+
+ sync_markers=$(json_extract sync_status.markers $meta_sync_status_json)
+
+ num_shards2=$(python_array_len $sync_markers)
+
+ [ "$global_sync_status" == "sync" ] && $assert $num_shards2 -eq $num_shards
+
+ sync_states=$(project_python_array_field val.state $sync_markers)
+ eval secondary_status=$(project_python_array_field val.marker $sync_markers)
+}
+
+function get_metadata_log_status {
+ cid=$1
+ realm=$2
+
+ master_mdlog_status_json=`$(rgw_admin $cid) --rgw-realm=$realm mdlog status`
+ master_meta_status=$(json_extract "" $master_mdlog_status_json)
+
+ eval master_status=$(project_python_array_field marker $master_meta_status)
+}
+
+function wait_for_meta_sync {
+ master_id=$1
+ cid=$2
+ realm=$3
+
+ get_metadata_log_status $master_id $realm
+ echo "master_status=${master_status[*]}"
+
+ while true; do
+ get_metadata_sync_status $cid $realm
+
+ echo "secondary_status=${secondary_status[*]}"
+
+ fail=0
+ for i in `seq 0 $((num_shards-1))`; do
+ if [ "${master_status[$i]}" \> "${secondary_status[$i]}" ]; then
+ echo "shard $i not done syncing (${master_status[$i]} > ${secondary_status[$i]})"
+ fail=1
+ break
+ fi
+ done
+
+ [ $fail -eq 0 ] && echo "Success" && return || echo "Sync not complete"
+
+ sleep 5
+ done
+}
+
diff --git a/src/test/rgw/test-rgw-multisite.sh b/src/test/rgw/test-rgw-multisite.sh
new file mode 100755
index 000000000..a005b19e3
--- /dev/null
+++ b/src/test/rgw/test-rgw-multisite.sh
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+
+[ $# -lt 1 ] && echo "usage: $0 <num-clusters> [rgw parameters...]" && exit 1
+
+num_clusters=$1
+shift
+
+[ $num_clusters -lt 1 ] && echo "clusters num must be at least 1" && exit 1
+
+. "`dirname $0`/test-rgw-common.sh"
+. "`dirname $0`/test-rgw-meta-sync.sh"
+
+set -e
+
+realm_name=earth
+zg=zg1
+
+system_access_key="1234567890"
+system_secret="pencil"
+
+# bring up first cluster
+x $(start_ceph_cluster c1) -n $(get_mstart_parameters 1)
+
+if [ -n "$RGW_PER_ZONE" ]; then
+ rgws="$RGW_PER_ZONE"
+else
+ rgws=1
+fi
+
+url=http://localhost
+
+i=1
+while [ $i -le $rgws ]; do
+ port=$((8100+i))
+ endpoints="$endpoints""$url:$port,"
+ i=$((i+1))
+done
+
+# create realm, zonegroup, zone, start rgws
+init_first_zone c1 $realm_name $zg ${zg}-1 $endpoints $system_access_key $system_secret
+i=1
+while [ $i -le $rgws ]; do
+ port=$((8100+i))
+ x $(rgw c1 "$port" "$@")
+ i="$((i+1))"
+done
+
+output=`$(rgw_admin c1) realm get`
+
+echo realm_status=$output
+
+# bring up next clusters
+
+endpoints=""
+i=2
+while [ $i -le $num_clusters ]; do
+ x $(start_ceph_cluster c$i) -n $(get_mstart_parameters $i)
+ j=1
+ endpoints=""
+ while [ $j -le $rgws ]; do
+ port=$((8000+i*100+j))
+ endpoints="$endpoints""$url:$port,"
+ j=$((j+1))
+ done
+
+ # create new zone, start rgw
+ init_zone_in_existing_zg c$i $realm_name $zg ${zg}-${i} 8101 $endpoints $zone_port $system_access_key $system_secret
+ j=1
+ while [ $j -le $rgws ]; do
+ port=$((8000+i*100+j))
+ x $(rgw c$i "$port" "$@")
+ j="$((j+1))"
+ done
+ i=$((i+1))
+done
+
+i=2
+while [ $i -le $num_clusters ]; do
+ wait_for_meta_sync c1 c$i $realm_name
+
+ i=$((i+1))
+done
+
diff --git a/src/test/rgw/test_cls_fifo_legacy.cc b/src/test/rgw/test_cls_fifo_legacy.cc
new file mode 100644
index 000000000..1fa5f8681
--- /dev/null
+++ b/src/test/rgw/test_cls_fifo_legacy.cc
@@ -0,0 +1,1184 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <cerrno>
+#include <iostream>
+#include <string_view>
+
+#include "include/scope_guard.h"
+#include "include/types.h"
+#include "include/rados/librados.hpp"
+#include "common/ceph_context.h"
+
+#include "cls/fifo/cls_fifo_ops.h"
+#include "test/librados/test_cxx.h"
+#include "global/global_context.h"
+
+#include "rgw_tools.h"
+#include "cls_fifo_legacy.h"
+
+#include "gtest/gtest.h"
+
+using namespace std::literals;
+using namespace std::string_literals;
+
+namespace R = librados;
+namespace cb = ceph::buffer;
+namespace fifo = rados::cls::fifo;
+namespace RCf = rgw::cls::fifo;
+
+auto cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+const DoutPrefix dp(cct, 1, "test legacy cls fifo: ");
+
+namespace {
+int fifo_create(const DoutPrefixProvider *dpp, R::IoCtx& ioctx,
+ const std::string& oid,
+ std::string_view id,
+ optional_yield y,
+ std::optional<fifo::objv> objv = std::nullopt,
+ std::optional<std::string_view> oid_prefix = std::nullopt,
+ bool exclusive = false,
+ std::uint64_t max_part_size = RCf::default_max_part_size,
+ std::uint64_t max_entry_size = RCf::default_max_entry_size)
+{
+ R::ObjectWriteOperation op;
+ RCf::create_meta(&op, id, objv, oid_prefix, exclusive, max_part_size,
+ max_entry_size);
+ return rgw_rados_operate(dpp, ioctx, oid, &op, y);
+}
+}
+
+class LegacyFIFO : public testing::Test {
+protected:
+ const std::string pool_name = get_temp_pool_name();
+ const std::string fifo_id = "fifo";
+ R::Rados rados;
+ librados::IoCtx ioctx;
+
+ void SetUp() override {
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+ void TearDown() override {
+ destroy_one_pool_pp(pool_name, rados);
+ }
+};
+
+using LegacyClsFIFO = LegacyFIFO;
+using AioLegacyFIFO = LegacyFIFO;
+
+
+TEST_F(LegacyClsFIFO, TestCreate)
+{
+ auto r = fifo_create(&dp, ioctx, fifo_id, ""s, null_yield);
+ EXPECT_EQ(-EINVAL, r);
+ r = fifo_create(&dp, ioctx, fifo_id, fifo_id, null_yield, std::nullopt,
+ std::nullopt, false, 0);
+ EXPECT_EQ(-EINVAL, r);
+ r = fifo_create(&dp, ioctx, fifo_id, {}, null_yield,
+ std::nullopt, std::nullopt,
+ false, RCf::default_max_part_size, 0);
+ EXPECT_EQ(-EINVAL, r);
+ r = fifo_create(&dp, ioctx, fifo_id, fifo_id, null_yield);
+ EXPECT_EQ(0, r);
+ std::uint64_t size;
+ ioctx.stat(fifo_id, &size, nullptr);
+ EXPECT_GT(size, 0);
+ /* test idempotency */
+ r = fifo_create(&dp, ioctx, fifo_id, fifo_id, null_yield);
+ EXPECT_EQ(0, r);
+ r = fifo_create(&dp, ioctx, fifo_id, {}, null_yield, std::nullopt,
+ std::nullopt, false);
+ EXPECT_EQ(-EINVAL, r);
+ r = fifo_create(&dp, ioctx, fifo_id, {}, null_yield, std::nullopt,
+ "myprefix"sv, false);
+ EXPECT_EQ(-EINVAL, r);
+ r = fifo_create(&dp, ioctx, fifo_id, "foo"sv, null_yield,
+ std::nullopt, std::nullopt, false);
+ EXPECT_EQ(-EEXIST, r);
+}
+
+TEST_F(LegacyClsFIFO, TestGetInfo)
+{
+ auto r = fifo_create(&dp, ioctx, fifo_id, fifo_id, null_yield);
+ fifo::info info;
+ std::uint32_t part_header_size;
+ std::uint32_t part_entry_overhead;
+ r = RCf::get_meta(&dp, ioctx, fifo_id, std::nullopt, &info, &part_header_size,
+ &part_entry_overhead, 0, null_yield);
+ EXPECT_EQ(0, r);
+ EXPECT_GT(part_header_size, 0);
+ EXPECT_GT(part_entry_overhead, 0);
+ EXPECT_FALSE(info.version.instance.empty());
+
+ r = RCf::get_meta(&dp, ioctx, fifo_id, info.version, &info, &part_header_size,
+ &part_entry_overhead, 0, null_yield);
+ EXPECT_EQ(0, r);
+ fifo::objv objv;
+ objv.instance = "foo";
+ objv.ver = 12;
+ r = RCf::get_meta(&dp, ioctx, fifo_id, objv, &info, &part_header_size,
+ &part_entry_overhead, 0, null_yield);
+ EXPECT_EQ(-ECANCELED, r);
+}
+
+TEST_F(LegacyFIFO, TestOpenDefault)
+{
+ std::unique_ptr<RCf::FIFO> fifo;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &fifo, null_yield);
+ ASSERT_EQ(0, r);
+ // force reading from backend
+ r = fifo->read_meta(&dp, null_yield);
+ EXPECT_EQ(0, r);
+ auto info = fifo->meta();
+ EXPECT_EQ(info.id, fifo_id);
+}
+
+TEST_F(LegacyFIFO, TestOpenParams)
+{
+ const std::uint64_t max_part_size = 10 * 1024;
+ const std::uint64_t max_entry_size = 128;
+ auto oid_prefix = "foo.123."sv;
+ fifo::objv objv;
+ objv.instance = "fooz"s;
+ objv.ver = 10;
+
+ /* first successful create */
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, objv, oid_prefix,
+ false, max_part_size, max_entry_size);
+ ASSERT_EQ(0, r);
+
+ /* force reading from backend */
+ r = f->read_meta(&dp, null_yield);
+ auto info = f->meta();
+ EXPECT_EQ(info.id, fifo_id);
+ EXPECT_EQ(info.params.max_part_size, max_part_size);
+ EXPECT_EQ(info.params.max_entry_size, max_entry_size);
+ EXPECT_EQ(info.version, objv);
+}
+
+namespace {
+template<class T>
+std::pair<T, std::string> decode_entry(const RCf::list_entry& entry)
+{
+ T val;
+ auto iter = entry.data.cbegin();
+ decode(val, iter);
+ return std::make_pair(std::move(val), entry.marker);
+}
+}
+
+
+TEST_F(LegacyFIFO, TestPushListTrim)
+{
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield);
+ ASSERT_EQ(0, r);
+ static constexpr auto max_entries = 10u;
+ for (uint32_t i = 0; i < max_entries; ++i) {
+ cb::list bl;
+ encode(i, bl);
+ r = f->push(&dp, bl, null_yield);
+ ASSERT_EQ(0, r);
+ }
+
+ std::optional<std::string> marker;
+ /* get entries one by one */
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ for (auto i = 0u; i < max_entries; ++i) {
+
+ r = f->list(&dp, 1, marker, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+
+ bool expected_more = (i != (max_entries - 1));
+ ASSERT_EQ(expected_more, more);
+ ASSERT_EQ(1, result.size());
+
+ std::uint32_t val;
+ std::tie(val, marker) = decode_entry<std::uint32_t>(result.front());
+
+ ASSERT_EQ(i, val);
+ result.clear();
+ }
+
+ /* get all entries at once */
+ std::string markers[max_entries];
+ std::uint32_t min_entry = 0;
+ r = f->list(&dp, max_entries * 10, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+
+ ASSERT_FALSE(more);
+ ASSERT_EQ(max_entries, result.size());
+ for (auto i = 0u; i < max_entries; ++i) {
+ std::uint32_t val;
+ std::tie(val, markers[i]) = decode_entry<std::uint32_t>(result[i]);
+ ASSERT_EQ(i, val);
+ }
+
+ /* trim one entry */
+ r = f->trim(&dp, markers[min_entry], false, null_yield);
+ ASSERT_EQ(0, r);
+ ++min_entry;
+
+ r = f->list(&dp, max_entries * 10, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_FALSE(more);
+ ASSERT_EQ(max_entries - min_entry, result.size());
+
+ for (auto i = min_entry; i < max_entries; ++i) {
+ std::uint32_t val;
+ std::tie(val, markers[i - min_entry]) =
+ decode_entry<std::uint32_t>(result[i - min_entry]);
+ EXPECT_EQ(i, val);
+ }
+}
+
+
+TEST_F(LegacyFIFO, TestPushTooBig)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size, max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size + 1];
+ memset(buf, 0, sizeof(buf));
+
+ cb::list bl;
+ bl.append(buf, sizeof(buf));
+
+ r = f->push(&dp, bl, null_yield);
+ EXPECT_EQ(-E2BIG, r);
+}
+
+
+TEST_F(LegacyFIFO, TestMultipleParts)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+ const auto [part_header_size, part_entry_overhead] =
+ f->get_part_layout_info();
+ const auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ const auto max_entries = entries_per_part * 4 + 1;
+ /* push enough entries */
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ r = f->push(&dp, bl, null_yield);
+ ASSERT_EQ(0, r);
+ }
+
+ auto info = f->meta();
+ ASSERT_EQ(info.id, fifo_id);
+ /* head should have advanced */
+ ASSERT_GT(info.head_part_num, 0);
+
+ /* list all at once */
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ r = f->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ EXPECT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+
+ std::optional<std::string> marker;
+ /* get entries one by one */
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ r = f->list(&dp, 1, marker, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(result.size(), 1);
+ const bool expected_more = (i != (max_entries - 1));
+ ASSERT_EQ(expected_more, more);
+
+ std::uint32_t val;
+ std::tie(val, marker) = decode_entry<std::uint32_t>(result.front());
+
+ auto& entry = result.front();
+ auto& bl = entry.data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ marker = entry.marker;
+ }
+
+ /* trim one at a time */
+ marker.reset();
+ for (auto i = 0u; i < max_entries; ++i) {
+ /* read single entry */
+ r = f->list(&dp, 1, marker, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(result.size(), 1);
+ const bool expected_more = (i != (max_entries - 1));
+ ASSERT_EQ(expected_more, more);
+
+ marker = result.front().marker;
+ r = f->trim(&dp, *marker, false, null_yield);
+ ASSERT_EQ(0, r);
+
+ /* check tail */
+ info = f->meta();
+ ASSERT_EQ(info.tail_part_num, i / entries_per_part);
+
+ /* try to read all again, see how many entries left */
+ r = f->list(&dp, max_entries, marker, &result, &more, null_yield);
+ ASSERT_EQ(max_entries - i - 1, result.size());
+ ASSERT_EQ(false, more);
+ }
+
+ /* tail now should point at head */
+ info = f->meta();
+ ASSERT_EQ(info.head_part_num, info.tail_part_num);
+
+ RCf::part_info partinfo;
+ /* check old tails are removed */
+ for (auto i = 0; i < info.tail_part_num; ++i) {
+ r = f->get_part_info(&dp, i, &partinfo, null_yield);
+ ASSERT_EQ(-ENOENT, r);
+ }
+ /* check current tail exists */
+ r = f->get_part_info(&dp, info.tail_part_num, &partinfo, null_yield);
+ ASSERT_EQ(0, r);
+}
+
+TEST_F(LegacyFIFO, TestTwoPushers)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+
+ auto [part_header_size, part_entry_overhead] = f->get_part_layout_info();
+ const auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ const auto max_entries = entries_per_part * 4 + 1;
+ std::unique_ptr<RCf::FIFO> f2;
+ r = RCf::FIFO::open(&dp, ioctx, fifo_id, &f2, null_yield);
+ std::vector fifos{&f, &f2};
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ auto& f = *fifos[i % fifos.size()];
+ r = f->push(&dp, bl, null_yield);
+ ASSERT_EQ(0, r);
+ }
+
+ /* list all by both */
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ r = f2->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+
+ r = f2->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+}
+
+TEST_F(LegacyFIFO, TestTwoPushersTrim)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+ std::unique_ptr<RCf::FIFO> f1;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f1, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+
+ auto [part_header_size, part_entry_overhead] = f1->get_part_layout_info();
+ const auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ const auto max_entries = entries_per_part * 4 + 1;
+
+ std::unique_ptr<RCf::FIFO> f2;
+ r = RCf::FIFO::open(&dp, ioctx, fifo_id, &f2, null_yield);
+ ASSERT_EQ(0, r);
+
+ /* push one entry to f2 and the rest to f1 */
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ auto& f = (i < 1 ? f2 : f1);
+ r = f->push(&dp, bl, null_yield);
+ ASSERT_EQ(0, r);
+ }
+
+ /* trim half by fifo1 */
+ auto num = max_entries / 2;
+ std::string marker;
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ r = f1->list(&dp, num, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(true, more);
+ ASSERT_EQ(num, result.size());
+
+ for (auto i = 0u; i < num; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+
+ auto& entry = result[num - 1];
+ marker = entry.marker;
+ r = f1->trim(&dp, marker, false, null_yield);
+ /* list what's left by fifo2 */
+
+ const auto left = max_entries - num;
+ f2->list(&dp, left, marker, &result, &more, null_yield);
+ ASSERT_EQ(left, result.size());
+ ASSERT_EQ(false, more);
+
+ for (auto i = num; i < max_entries; ++i) {
+ auto& bl = result[i - num].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+}
+
+TEST_F(LegacyFIFO, TestPushBatch)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+ auto [part_header_size, part_entry_overhead] = f->get_part_layout_info();
+ auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ auto max_entries = entries_per_part * 4 + 1; /* enough entries to span multiple parts */
+ std::vector<cb::list> bufs;
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ bufs.push_back(bl);
+ }
+ ASSERT_EQ(max_entries, bufs.size());
+
+ r = f->push(&dp, bufs, null_yield);
+ ASSERT_EQ(0, r);
+
+ /* list all */
+
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ r = f->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+ for (auto i = 0u; i < max_entries; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+ auto& info = f->meta();
+ ASSERT_EQ(info.head_part_num, 4);
+}
+
+TEST_F(LegacyFIFO, TestAioTrim)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+ const auto [part_header_size, part_entry_overhead] =
+ f->get_part_layout_info();
+ const auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ const auto max_entries = entries_per_part * 4 + 1;
+ /* push enough entries */
+ std::vector<cb::list> bufs;
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ bufs.push_back(std::move(bl));
+ }
+ ASSERT_EQ(max_entries, bufs.size());
+
+ r = f->push(&dp, bufs, null_yield);
+ ASSERT_EQ(0, r);
+
+ auto info = f->meta();
+ ASSERT_EQ(info.id, fifo_id);
+ /* head should have advanced */
+ ASSERT_GT(info.head_part_num, 0);
+
+ /* list all at once */
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ r = f->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+
+ std::optional<std::string> marker;
+ /* trim one at a time */
+ result.clear();
+ more = false;
+ marker.reset();
+ for (auto i = 0u; i < max_entries; ++i) {
+ /* read single entry */
+ r = f->list(&dp, 1, marker, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(result.size(), 1);
+ const bool expected_more = (i != (max_entries - 1));
+ ASSERT_EQ(expected_more, more);
+
+ marker = result.front().marker;
+ std::unique_ptr<R::AioCompletion> c(rados.aio_create_completion(nullptr,
+ nullptr));
+ f->trim(&dp, *marker, false, c.get());
+ c->wait_for_complete();
+ r = c->get_return_value();
+ ASSERT_EQ(0, r);
+
+ /* check tail */
+ info = f->meta();
+ ASSERT_EQ(info.tail_part_num, i / entries_per_part);
+
+ /* try to read all again, see how many entries left */
+ r = f->list(&dp, max_entries, marker, &result, &more, null_yield);
+ ASSERT_EQ(max_entries - i - 1, result.size());
+ ASSERT_EQ(false, more);
+ }
+
+ /* tail now should point at head */
+ info = f->meta();
+ ASSERT_EQ(info.head_part_num, info.tail_part_num);
+
+ RCf::part_info partinfo;
+ /* check old tails are removed */
+ for (auto i = 0; i < info.tail_part_num; ++i) {
+ r = f->get_part_info(&dp, i, &partinfo, null_yield);
+ ASSERT_EQ(-ENOENT, r);
+ }
+ /* check current tail exists */
+ r = f->get_part_info(&dp, info.tail_part_num, &partinfo, null_yield);
+ ASSERT_EQ(0, r);
+}
+
+TEST_F(LegacyFIFO, TestTrimExclusive) {
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield);
+ ASSERT_EQ(0, r);
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+
+ static constexpr auto max_entries = 10u;
+ for (uint32_t i = 0; i < max_entries; ++i) {
+ cb::list bl;
+ encode(i, bl);
+ f->push(&dp, bl, null_yield);
+ }
+
+ f->list(&dp, 1, std::nullopt, &result, &more, null_yield);
+ auto [val, marker] = decode_entry<std::uint32_t>(result.front());
+ ASSERT_EQ(0, val);
+ f->trim(&dp, marker, true, null_yield);
+
+ result.clear();
+ f->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ std::tie(val, marker) = decode_entry<std::uint32_t>(result.front());
+ ASSERT_EQ(0, val);
+ f->trim(&dp, result[4].marker, true, null_yield);
+
+ result.clear();
+ f->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ std::tie(val, marker) = decode_entry<std::uint32_t>(result.front());
+ ASSERT_EQ(4, val);
+ f->trim(&dp, result.back().marker, true, null_yield);
+
+ result.clear();
+ f->list(&dp, max_entries, std::nullopt, &result, &more, null_yield);
+ std::tie(val, marker) = decode_entry<std::uint32_t>(result.front());
+ ASSERT_EQ(result.size(), 1);
+ ASSERT_EQ(max_entries - 1, val);
+}
+
+TEST_F(AioLegacyFIFO, TestPushListTrim)
+{
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield);
+ ASSERT_EQ(0, r);
+ static constexpr auto max_entries = 10u;
+ for (uint32_t i = 0; i < max_entries; ++i) {
+ cb::list bl;
+ encode(i, bl);
+ auto c = R::Rados::aio_create_completion();
+ f->push(&dp, bl, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ }
+
+ std::optional<std::string> marker;
+ /* get entries one by one */
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ for (auto i = 0u; i < max_entries; ++i) {
+ auto c = R::Rados::aio_create_completion();
+ f->list(&dp, 1, marker, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+
+ bool expected_more = (i != (max_entries - 1));
+ ASSERT_EQ(expected_more, more);
+ ASSERT_EQ(1, result.size());
+
+ std::uint32_t val;
+ std::tie(val, marker) = decode_entry<std::uint32_t>(result.front());
+
+ ASSERT_EQ(i, val);
+ result.clear();
+ }
+
+ /* get all entries at once */
+ std::string markers[max_entries];
+ std::uint32_t min_entry = 0;
+ auto c = R::Rados::aio_create_completion();
+ f->list(&dp, max_entries * 10, std::nullopt, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+
+ ASSERT_FALSE(more);
+ ASSERT_EQ(max_entries, result.size());
+ for (auto i = 0u; i < max_entries; ++i) {
+ std::uint32_t val;
+ std::tie(val, markers[i]) = decode_entry<std::uint32_t>(result[i]);
+ ASSERT_EQ(i, val);
+ }
+
+ /* trim one entry */
+ c = R::Rados::aio_create_completion();
+ f->trim(&dp, markers[min_entry], false, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ ++min_entry;
+
+ c = R::Rados::aio_create_completion();
+ f->list(&dp, max_entries * 10, std::nullopt, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ ASSERT_FALSE(more);
+ ASSERT_EQ(max_entries - min_entry, result.size());
+
+ for (auto i = min_entry; i < max_entries; ++i) {
+ std::uint32_t val;
+ std::tie(val, markers[i - min_entry]) =
+ decode_entry<std::uint32_t>(result[i - min_entry]);
+ EXPECT_EQ(i, val);
+ }
+}
+
+
+TEST_F(AioLegacyFIFO, TestPushTooBig)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size, max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size + 1];
+ memset(buf, 0, sizeof(buf));
+
+ cb::list bl;
+ bl.append(buf, sizeof(buf));
+
+ auto c = R::Rados::aio_create_completion();
+ f->push(&dp, bl, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ ASSERT_EQ(-E2BIG, r);
+ c->release();
+
+ c = R::Rados::aio_create_completion();
+ f->push(&dp, std::vector<cb::list>{}, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ EXPECT_EQ(0, r);
+}
+
+
+TEST_F(AioLegacyFIFO, TestMultipleParts)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+
+ {
+ auto c = R::Rados::aio_create_completion();
+ f->get_head_info(&dp, [&](int r, RCf::part_info&& p) {
+ ASSERT_EQ(0, p.magic);
+ ASSERT_EQ(0, p.min_ofs);
+ ASSERT_EQ(0, p.last_ofs);
+ ASSERT_EQ(0, p.next_ofs);
+ ASSERT_EQ(0, p.min_index);
+ ASSERT_EQ(0, p.max_index);
+ ASSERT_EQ(ceph::real_time{}, p.max_time);
+ }, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ }
+
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+ const auto [part_header_size, part_entry_overhead] =
+ f->get_part_layout_info();
+ const auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ const auto max_entries = entries_per_part * 4 + 1;
+ /* push enough entries */
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ auto c = R::Rados::aio_create_completion();
+ f->push(&dp, bl, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ EXPECT_EQ(0, r);
+ }
+
+ auto info = f->meta();
+ ASSERT_EQ(info.id, fifo_id);
+ /* head should have advanced */
+ ASSERT_GT(info.head_part_num, 0);
+
+ /* list all at once */
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ auto c = R::Rados::aio_create_completion();
+ f->list(&dp, max_entries, std::nullopt, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+
+ std::optional<std::string> marker;
+ /* get entries one by one */
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ c = R::Rados::aio_create_completion();
+ f->list(&dp, 1, marker, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ EXPECT_EQ(0, r);
+ ASSERT_EQ(result.size(), 1);
+ const bool expected_more = (i != (max_entries - 1));
+ ASSERT_EQ(expected_more, more);
+
+ std::uint32_t val;
+ std::tie(val, marker) = decode_entry<std::uint32_t>(result.front());
+
+ auto& entry = result.front();
+ auto& bl = entry.data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ marker = entry.marker;
+ }
+
+ /* trim one at a time */
+ marker.reset();
+ for (auto i = 0u; i < max_entries; ++i) {
+ /* read single entry */
+ c = R::Rados::aio_create_completion();
+ f->list(&dp, 1, marker, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ EXPECT_EQ(0, r);
+ ASSERT_EQ(result.size(), 1);
+ const bool expected_more = (i != (max_entries - 1));
+ ASSERT_EQ(expected_more, more);
+
+ marker = result.front().marker;
+ c = R::Rados::aio_create_completion();
+ f->trim(&dp, *marker, false, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ EXPECT_EQ(0, r);
+ ASSERT_EQ(result.size(), 1);
+
+ /* check tail */
+ info = f->meta();
+ ASSERT_EQ(info.tail_part_num, i / entries_per_part);
+
+ /* try to read all again, see how many entries left */
+ c = R::Rados::aio_create_completion();
+ f->list(&dp, max_entries, marker, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ EXPECT_EQ(0, r);
+ ASSERT_EQ(max_entries - i - 1, result.size());
+ ASSERT_EQ(false, more);
+ }
+
+ /* tail now should point at head */
+ info = f->meta();
+ ASSERT_EQ(info.head_part_num, info.tail_part_num);
+
+ /* check old tails are removed */
+ for (auto i = 0; i < info.tail_part_num; ++i) {
+ c = R::Rados::aio_create_completion();
+ RCf::part_info partinfo;
+ f->get_part_info(i, &partinfo, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(-ENOENT, r);
+ }
+ /* check current tail exists */
+ std::uint64_t next_ofs;
+ {
+ c = R::Rados::aio_create_completion();
+ RCf::part_info partinfo;
+ f->get_part_info(info.tail_part_num, &partinfo, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ next_ofs = partinfo.next_ofs;
+ }
+ ASSERT_EQ(0, r);
+
+ c = R::Rados::aio_create_completion();
+ f->get_head_info(&dp, [&](int r, RCf::part_info&& p) {
+ ASSERT_EQ(next_ofs, p.next_ofs);
+ }, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+}
+
+TEST_F(AioLegacyFIFO, TestTwoPushers)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+
+ auto [part_header_size, part_entry_overhead] = f->get_part_layout_info();
+ const auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ const auto max_entries = entries_per_part * 4 + 1;
+ std::unique_ptr<RCf::FIFO> f2;
+ r = RCf::FIFO::open(&dp, ioctx, fifo_id, &f2, null_yield);
+ std::vector fifos{&f, &f2};
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ auto& f = *fifos[i % fifos.size()];
+ auto c = R::Rados::aio_create_completion();
+ f->push(&dp, bl, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ }
+
+ /* list all by both */
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ auto c = R::Rados::aio_create_completion();
+ f2->list(&dp, max_entries, std::nullopt, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+
+ c = R::Rados::aio_create_completion();
+ f2->list(&dp, max_entries, std::nullopt, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+
+ for (auto i = 0u; i < max_entries; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+}
+
+TEST_F(AioLegacyFIFO, TestTwoPushersTrim)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+ std::unique_ptr<RCf::FIFO> f1;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f1, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+
+ auto [part_header_size, part_entry_overhead] = f1->get_part_layout_info();
+ const auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ const auto max_entries = entries_per_part * 4 + 1;
+
+ std::unique_ptr<RCf::FIFO> f2;
+ r = RCf::FIFO::open(&dp, ioctx, fifo_id, &f2, null_yield);
+ ASSERT_EQ(0, r);
+
+ /* push one entry to f2 and the rest to f1 */
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ auto& f = (i < 1 ? f2 : f1);
+ auto c = R::Rados::aio_create_completion();
+ f->push(&dp, bl, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ }
+
+ /* trim half by fifo1 */
+ auto num = max_entries / 2;
+ std::string marker;
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ auto c = R::Rados::aio_create_completion();
+ f1->list(&dp, num, std::nullopt, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(true, more);
+ ASSERT_EQ(num, result.size());
+
+ for (auto i = 0u; i < num; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+
+ auto& entry = result[num - 1];
+ marker = entry.marker;
+ c = R::Rados::aio_create_completion();
+ f1->trim(&dp, marker, false, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ /* list what's left by fifo2 */
+
+ const auto left = max_entries - num;
+ c = R::Rados::aio_create_completion();
+ f2->list(&dp, left, marker, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(left, result.size());
+ ASSERT_EQ(false, more);
+
+ for (auto i = num; i < max_entries; ++i) {
+ auto& bl = result[i - num].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+}
+
+TEST_F(AioLegacyFIFO, TestPushBatch)
+{
+ static constexpr auto max_part_size = 2048ull;
+ static constexpr auto max_entry_size = 128ull;
+
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield, std::nullopt,
+ std::nullopt, false, max_part_size,
+ max_entry_size);
+ ASSERT_EQ(0, r);
+
+ char buf[max_entry_size];
+ memset(buf, 0, sizeof(buf));
+ auto [part_header_size, part_entry_overhead] = f->get_part_layout_info();
+ auto entries_per_part = ((max_part_size - part_header_size) /
+ (max_entry_size + part_entry_overhead));
+ auto max_entries = entries_per_part * 4 + 1; /* enough entries to span multiple parts */
+ std::vector<cb::list> bufs;
+ for (auto i = 0u; i < max_entries; ++i) {
+ cb::list bl;
+ *(int *)buf = i;
+ bl.append(buf, sizeof(buf));
+ bufs.push_back(bl);
+ }
+ ASSERT_EQ(max_entries, bufs.size());
+
+ auto c = R::Rados::aio_create_completion();
+ f->push(&dp, bufs, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+
+ /* list all */
+
+ std::vector<RCf::list_entry> result;
+ bool more = false;
+ c = R::Rados::aio_create_completion();
+ f->list(&dp, max_entries, std::nullopt, &result, &more, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(false, more);
+ ASSERT_EQ(max_entries, result.size());
+ for (auto i = 0u; i < max_entries; ++i) {
+ auto& bl = result[i].data;
+ ASSERT_EQ(i, *(int *)bl.c_str());
+ }
+ auto& info = f->meta();
+ ASSERT_EQ(info.head_part_num, 4);
+}
+
+TEST_F(LegacyFIFO, TrimAll)
+{
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield);
+ ASSERT_EQ(0, r);
+ static constexpr auto max_entries = 10u;
+ for (uint32_t i = 0; i < max_entries; ++i) {
+ cb::list bl;
+ encode(i, bl);
+ r = f->push(&dp, bl, null_yield);
+ ASSERT_EQ(0, r);
+ }
+
+ /* trim one entry */
+ r = f->trim(&dp, RCf::marker::max().to_string(), false, null_yield);
+ ASSERT_EQ(-ENODATA, r);
+
+ std::vector<RCf::list_entry> result;
+ bool more;
+ r = f->list(&dp, 1, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(result.empty());
+}
+
+TEST_F(LegacyFIFO, AioTrimAll)
+{
+ std::unique_ptr<RCf::FIFO> f;
+ auto r = RCf::FIFO::create(&dp, ioctx, fifo_id, &f, null_yield);
+ ASSERT_EQ(0, r);
+ static constexpr auto max_entries = 10u;
+ for (uint32_t i = 0; i < max_entries; ++i) {
+ cb::list bl;
+ encode(i, bl);
+ r = f->push(&dp, bl, null_yield);
+ ASSERT_EQ(0, r);
+ }
+
+ auto c = R::Rados::aio_create_completion();
+ f->trim(&dp, RCf::marker::max().to_string(), false, c);
+ c->wait_for_complete();
+ r = c->get_return_value();
+ c->release();
+ ASSERT_EQ(-ENODATA, r);
+
+ std::vector<RCf::list_entry> result;
+ bool more;
+ r = f->list(&dp, 1, std::nullopt, &result, &more, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(result.empty());
+}
diff --git a/src/test/rgw/test_http_manager.cc b/src/test/rgw/test_http_manager.cc
new file mode 100644
index 000000000..f2daeddca
--- /dev/null
+++ b/src/test/rgw/test_http_manager.cc
@@ -0,0 +1,148 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "rgw_rados.h"
+#include "rgw_http_client.h"
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include <unistd.h>
+#include <curl/curl.h>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/write.hpp>
+#include <thread>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+ using tcp = boost::asio::ip::tcp;
+
+ // if we have a racing where another thread manages to bind and listen the
+ // port picked by this acceptor, try again.
+ static constexpr int MAX_BIND_RETRIES = 60;
+
+ tcp::acceptor try_bind(boost::asio::io_context& ioctx) {
+ using tcp = boost::asio::ip::tcp;
+ tcp::endpoint endpoint(tcp::v4(), 0);
+ tcp::acceptor acceptor(ioctx);
+ acceptor.open(endpoint.protocol());
+ for (int retries = 0;; retries++) {
+ try {
+ acceptor.bind(endpoint);
+ // yay!
+ break;
+ } catch (const boost::system::system_error& e) {
+ if (retries == MAX_BIND_RETRIES) {
+ throw;
+ }
+ if (e.code() != boost::system::errc::address_in_use) {
+ throw;
+ }
+ }
+ // backoff a little bit
+ sleep(1);
+ }
+ return acceptor;
+ }
+}
+
+TEST(HTTPManager, ReadTruncated)
+{
+ using tcp = boost::asio::ip::tcp;
+ boost::asio::io_context ioctx;
+ auto acceptor = try_bind(ioctx);
+ acceptor.listen();
+
+ std::thread server{[&] {
+ tcp::socket socket{ioctx};
+ acceptor.accept(socket);
+ std::string_view response =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 1024\r\n"
+ "\r\n"
+ "short body";
+ boost::asio::write(socket, boost::asio::buffer(response));
+ }};
+ const auto url = std::string{"http://127.0.0.1:"} + std::to_string(acceptor.local_endpoint().port());
+
+ RGWHTTPClient client{g_ceph_context, "GET", url};
+ EXPECT_EQ(-EAGAIN, RGWHTTP::process(&client, null_yield));
+
+ server.join();
+}
+
+TEST(HTTPManager, Head)
+{
+ using tcp = boost::asio::ip::tcp;
+ boost::asio::io_context ioctx;
+ auto acceptor = try_bind(ioctx);
+ acceptor.listen();
+
+ std::thread server{[&] {
+ tcp::socket socket{ioctx};
+ acceptor.accept(socket);
+ std::string_view response =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 1024\r\n"
+ "\r\n";
+ boost::asio::write(socket, boost::asio::buffer(response));
+ }};
+ const auto url = std::string{"http://127.0.0.1:"} + std::to_string(acceptor.local_endpoint().port());
+
+ RGWHTTPClient client{g_ceph_context, "HEAD", url};
+ EXPECT_EQ(0, RGWHTTP::process(&client, null_yield));
+
+ server.join();
+}
+
+TEST(HTTPManager, SignalThread)
+{
+ auto cct = g_ceph_context;
+ RGWHTTPManager http(cct);
+
+ ASSERT_EQ(0, http.start());
+
+ // default pipe buffer size according to man pipe
+ constexpr size_t max_pipe_buffer_size = 65536;
+ // each signal writes 4 bytes to the pipe
+ constexpr size_t max_pipe_signals = max_pipe_buffer_size / sizeof(uint32_t);
+ // add_request and unregister_request
+ constexpr size_t pipe_signals_per_request = 2;
+ // number of http requests to fill the pipe buffer
+ constexpr size_t max_requests = max_pipe_signals / pipe_signals_per_request;
+
+ // send one extra request to test that we don't deadlock
+ constexpr size_t num_requests = max_requests + 1;
+
+ for (size_t i = 0; i < num_requests; i++) {
+ RGWHTTPClient client{cct, "PUT", "http://127.0.0.1:80"};
+ http.add_request(&client);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ rgw_http_client_init(cct->get());
+ rgw_setup_saved_curl_handles();
+ ::testing::InitGoogleTest(&argc, argv);
+ int r = RUN_ALL_TESTS();
+ rgw_release_all_curl_handles();
+ rgw_http_client_cleanup();
+ return r;
+}
diff --git a/src/test/rgw/test_log_backing.cc b/src/test/rgw/test_log_backing.cc
new file mode 100644
index 000000000..e4109d535
--- /dev/null
+++ b/src/test/rgw/test_log_backing.cc
@@ -0,0 +1,365 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_log_backing.h"
+
+#include <cerrno>
+#include <iostream>
+#include <string_view>
+
+#include <fmt/format.h>
+
+#include "include/types.h"
+#include "include/rados/librados.hpp"
+
+#include "test/librados/test_cxx.h"
+#include "global/global_context.h"
+
+#include "cls/log/cls_log_client.h"
+
+#include "rgw_tools.h"
+#include "cls_fifo_legacy.h"
+
+#include "gtest/gtest.h"
+
+namespace lr = librados;
+namespace cb = ceph::buffer;
+namespace fifo = rados::cls::fifo;
+namespace RCf = rgw::cls::fifo;
+
+auto cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+const DoutPrefix dp(cct, 1, "test log backing: ");
+
+class LogBacking : public testing::Test {
+protected:
+ static constexpr int SHARDS = 3;
+ const std::string pool_name = get_temp_pool_name();
+ lr::Rados rados;
+ lr::IoCtx ioctx;
+ lr::Rados rados2;
+ lr::IoCtx ioctx2;
+
+ void SetUp() override {
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ connect_cluster_pp(rados2);
+ ASSERT_EQ(0, rados2.ioctx_create(pool_name.c_str(), ioctx2));
+ }
+ void TearDown() override {
+ destroy_one_pool_pp(pool_name, rados);
+ }
+
+ std::string get_oid(uint64_t gen_id, int i) const {
+ return (gen_id > 0 ?
+ fmt::format("shard@G{}.{}", gen_id, i) :
+ fmt::format("shard.{}", i));
+ }
+
+ void make_omap() {
+ for (int i = 0; i < SHARDS; ++i) {
+ using ceph::encode;
+ lr::ObjectWriteOperation op;
+ cb::list bl;
+ encode(i, bl);
+ cls_log_add(op, ceph_clock_now(), {}, "meow", bl);
+ auto r = rgw_rados_operate(&dp, ioctx, get_oid(0, i), &op, null_yield);
+ ASSERT_GE(r, 0);
+ }
+ }
+
+ void add_omap(int i) {
+ using ceph::encode;
+ lr::ObjectWriteOperation op;
+ cb::list bl;
+ encode(i, bl);
+ cls_log_add(op, ceph_clock_now(), {}, "meow", bl);
+ auto r = rgw_rados_operate(&dp, ioctx, get_oid(0, i), &op, null_yield);
+ ASSERT_GE(r, 0);
+ }
+
+ void empty_omap() {
+ for (int i = 0; i < SHARDS; ++i) {
+ auto oid = get_oid(0, i);
+ std::string to_marker;
+ {
+ lr::ObjectReadOperation op;
+ std::list<cls_log_entry> entries;
+ bool truncated = false;
+ cls_log_list(op, {}, {}, {}, 1, entries, &to_marker, &truncated);
+ auto r = rgw_rados_operate(&dp, ioctx, oid, &op, nullptr, null_yield);
+ ASSERT_GE(r, 0);
+ ASSERT_FALSE(entries.empty());
+ }
+ {
+ lr::ObjectWriteOperation op;
+ cls_log_trim(op, {}, {}, {}, to_marker);
+ auto r = rgw_rados_operate(&dp, ioctx, oid, &op, null_yield);
+ ASSERT_GE(r, 0);
+ }
+ {
+ lr::ObjectReadOperation op;
+ std::list<cls_log_entry> entries;
+ bool truncated = false;
+ cls_log_list(op, {}, {}, {}, 1, entries, &to_marker, &truncated);
+ auto r = rgw_rados_operate(&dp, ioctx, oid, &op, nullptr, null_yield);
+ ASSERT_GE(r, 0);
+ ASSERT_TRUE(entries.empty());
+ }
+ }
+ }
+
+ void make_fifo()
+ {
+ for (int i = 0; i < SHARDS; ++i) {
+ std::unique_ptr<RCf::FIFO> fifo;
+ auto r = RCf::FIFO::create(&dp, ioctx, get_oid(0, i), &fifo, null_yield);
+ ASSERT_EQ(0, r);
+ ASSERT_TRUE(fifo);
+ }
+ }
+
+ void add_fifo(int i)
+ {
+ using ceph::encode;
+ std::unique_ptr<RCf::FIFO> fifo;
+ auto r = RCf::FIFO::open(&dp, ioctx, get_oid(0, i), &fifo, null_yield);
+ ASSERT_GE(0, r);
+ ASSERT_TRUE(fifo);
+ cb::list bl;
+ encode(i, bl);
+ r = fifo->push(&dp, bl, null_yield);
+ ASSERT_GE(0, r);
+ }
+
+ void assert_empty() {
+ std::vector<lr::ObjectItem> result;
+ lr::ObjectCursor next;
+ auto r = ioctx.object_list(ioctx.object_list_begin(), ioctx.object_list_end(),
+ 100, {}, &result, &next);
+ ASSERT_GE(r, 0);
+ ASSERT_TRUE(result.empty());
+ }
+};
+
+TEST_F(LogBacking, TestOmap)
+{
+ make_omap();
+ auto stat = log_backing_type(&dp, ioctx, log_type::fifo, SHARDS,
+ [this](int shard){ return get_oid(0, shard); },
+ null_yield);
+ ASSERT_EQ(log_type::omap, *stat);
+}
+
+TEST_F(LogBacking, TestOmapEmpty)
+{
+ auto stat = log_backing_type(&dp, ioctx, log_type::omap, SHARDS,
+ [this](int shard){ return get_oid(0, shard); },
+ null_yield);
+ ASSERT_EQ(log_type::omap, *stat);
+}
+
+TEST_F(LogBacking, TestFIFO)
+{
+ make_fifo();
+ auto stat = log_backing_type(&dp, ioctx, log_type::fifo, SHARDS,
+ [this](int shard){ return get_oid(0, shard); },
+ null_yield);
+ ASSERT_EQ(log_type::fifo, *stat);
+}
+
+TEST_F(LogBacking, TestFIFOEmpty)
+{
+ auto stat = log_backing_type(&dp, ioctx, log_type::fifo, SHARDS,
+ [this](int shard){ return get_oid(0, shard); },
+ null_yield);
+ ASSERT_EQ(log_type::fifo, *stat);
+}
+
+TEST(CursorGen, RoundTrip) {
+ const std::string_view pcurs = "fded";
+ {
+ auto gc = gencursor(0, pcurs);
+ ASSERT_EQ(pcurs, gc);
+ auto [gen, cursor] = cursorgen(gc);
+ ASSERT_EQ(0, gen);
+ ASSERT_EQ(pcurs, cursor);
+ }
+ {
+ auto gc = gencursor(53, pcurs);
+ ASSERT_NE(pcurs, gc);
+ auto [gen, cursor] = cursorgen(gc);
+ ASSERT_EQ(53, gen);
+ ASSERT_EQ(pcurs, cursor);
+ }
+}
+
+class generations final : public logback_generations {
+public:
+
+ entries_t got_entries;
+ std::optional<uint64_t> tail;
+
+ using logback_generations::logback_generations;
+
+ bs::error_code handle_init(entries_t e) noexcept {
+ got_entries = e;
+ return {};
+ }
+
+ bs::error_code handle_new_gens(entries_t e) noexcept {
+ got_entries = e;
+ return {};
+ }
+
+ bs::error_code handle_empty_to(uint64_t new_tail) noexcept {
+ tail = new_tail;
+ return {};
+ }
+};
+
+TEST_F(LogBacking, GenerationSingle)
+{
+ auto lgr = logback_generations::init<generations>(
+ &dp, ioctx, "foobar", [this](uint64_t gen_id, int shard) {
+ return get_oid(gen_id, shard);
+ }, SHARDS, log_type::fifo, null_yield);
+ ASSERT_TRUE(lgr);
+
+ auto lg = std::move(*lgr);
+
+ ASSERT_EQ(0, lg->got_entries.begin()->first);
+
+ ASSERT_EQ(0, lg->got_entries[0].gen_id);
+ ASSERT_EQ(log_type::fifo, lg->got_entries[0].type);
+ ASSERT_FALSE(lg->got_entries[0].pruned);
+
+ auto ec = lg->empty_to(&dp, 0, null_yield);
+ ASSERT_TRUE(ec);
+
+ lg.reset();
+
+ lg = *logback_generations::init<generations>(
+ &dp, ioctx, "foobar", [this](uint64_t gen_id, int shard) {
+ return get_oid(gen_id, shard);
+ }, SHARDS, log_type::fifo, null_yield);
+
+ ASSERT_EQ(0, lg->got_entries.begin()->first);
+
+ ASSERT_EQ(0, lg->got_entries[0].gen_id);
+ ASSERT_EQ(log_type::fifo, lg->got_entries[0].type);
+ ASSERT_FALSE(lg->got_entries[0].pruned);
+
+ lg->got_entries.clear();
+
+ ec = lg->new_backing(&dp, log_type::omap, null_yield);
+ ASSERT_FALSE(ec);
+
+ ASSERT_EQ(1, lg->got_entries.size());
+ ASSERT_EQ(1, lg->got_entries[1].gen_id);
+ ASSERT_EQ(log_type::omap, lg->got_entries[1].type);
+ ASSERT_FALSE(lg->got_entries[1].pruned);
+
+ lg.reset();
+
+ lg = *logback_generations::init<generations>(
+ &dp, ioctx, "foobar", [this](uint64_t gen_id, int shard) {
+ return get_oid(gen_id, shard);
+ }, SHARDS, log_type::fifo, null_yield);
+
+ ASSERT_EQ(2, lg->got_entries.size());
+ ASSERT_EQ(0, lg->got_entries[0].gen_id);
+ ASSERT_EQ(log_type::fifo, lg->got_entries[0].type);
+ ASSERT_FALSE(lg->got_entries[0].pruned);
+
+ ASSERT_EQ(1, lg->got_entries[1].gen_id);
+ ASSERT_EQ(log_type::omap, lg->got_entries[1].type);
+ ASSERT_FALSE(lg->got_entries[1].pruned);
+
+ ec = lg->empty_to(&dp, 0, null_yield);
+ ASSERT_FALSE(ec);
+
+ ASSERT_EQ(0, *lg->tail);
+
+ lg.reset();
+
+ lg = *logback_generations::init<generations>(
+ &dp, ioctx, "foobar", [this](uint64_t gen_id, int shard) {
+ return get_oid(gen_id, shard);
+ }, SHARDS, log_type::fifo, null_yield);
+
+ ASSERT_EQ(1, lg->got_entries.size());
+ ASSERT_EQ(1, lg->got_entries[1].gen_id);
+ ASSERT_EQ(log_type::omap, lg->got_entries[1].type);
+ ASSERT_FALSE(lg->got_entries[1].pruned);
+}
+
+TEST_F(LogBacking, GenerationWN)
+{
+ auto lg1 = *logback_generations::init<generations>(
+ &dp, ioctx, "foobar", [this](uint64_t gen_id, int shard) {
+ return get_oid(gen_id, shard);
+ }, SHARDS, log_type::fifo, null_yield);
+
+ auto ec = lg1->new_backing(&dp, log_type::omap, null_yield);
+ ASSERT_FALSE(ec);
+
+ ASSERT_EQ(1, lg1->got_entries.size());
+ ASSERT_EQ(1, lg1->got_entries[1].gen_id);
+ ASSERT_EQ(log_type::omap, lg1->got_entries[1].type);
+ ASSERT_FALSE(lg1->got_entries[1].pruned);
+
+ lg1->got_entries.clear();
+
+ auto lg2 = *logback_generations::init<generations>(
+ &dp, ioctx2, "foobar", [this](uint64_t gen_id, int shard) {
+ return get_oid(gen_id, shard);
+ }, SHARDS, log_type::fifo, null_yield);
+
+ ASSERT_EQ(2, lg2->got_entries.size());
+
+ ASSERT_EQ(0, lg2->got_entries[0].gen_id);
+ ASSERT_EQ(log_type::fifo, lg2->got_entries[0].type);
+ ASSERT_FALSE(lg2->got_entries[0].pruned);
+
+ ASSERT_EQ(1, lg2->got_entries[1].gen_id);
+ ASSERT_EQ(log_type::omap, lg2->got_entries[1].type);
+ ASSERT_FALSE(lg2->got_entries[1].pruned);
+
+ lg2->got_entries.clear();
+
+ ec = lg1->new_backing(&dp, log_type::fifo, null_yield);
+ ASSERT_FALSE(ec);
+
+ ASSERT_EQ(1, lg1->got_entries.size());
+ ASSERT_EQ(2, lg1->got_entries[2].gen_id);
+ ASSERT_EQ(log_type::fifo, lg1->got_entries[2].type);
+ ASSERT_FALSE(lg1->got_entries[2].pruned);
+
+ ASSERT_EQ(1, lg2->got_entries.size());
+ ASSERT_EQ(2, lg2->got_entries[2].gen_id);
+ ASSERT_EQ(log_type::fifo, lg2->got_entries[2].type);
+ ASSERT_FALSE(lg2->got_entries[2].pruned);
+
+ lg1->got_entries.clear();
+ lg2->got_entries.clear();
+
+ ec = lg2->empty_to(&dp, 1, null_yield);
+ ASSERT_FALSE(ec);
+
+ ASSERT_EQ(1, *lg1->tail);
+ ASSERT_EQ(1, *lg2->tail);
+
+ lg1->tail.reset();
+ lg2->tail.reset();
+}
diff --git a/src/test/rgw/test_multen.py b/src/test/rgw/test_multen.py
new file mode 100644
index 000000000..91464d333
--- /dev/null
+++ b/src/test/rgw/test_multen.py
@@ -0,0 +1,400 @@
+# Test of mult-tenancy
+
+import json
+import sys
+
+from boto.s3.connection import S3Connection, OrdinaryCallingFormat
+
+# XXX once we're done, break out the common code into a library module
+# See https://github.com/ceph/ceph/pull/8646
+import test_multi as t
+
+class TestException(Exception):
+ pass
+
+#
+# Create a traditional user, S3-only, global (empty) tenant
+#
+def test2(cluster):
+ uid = "tester2"
+ display_name = "'Test User 2'"
+ access_key = "tester2KEY"
+ s3_secret = "test3pass"
+ cmd = t.build_cmd('--uid', uid,
+ '--display-name', display_name,
+ '--access-key', access_key,
+ '--secret', s3_secret,
+ "user create")
+ out, ret = cluster.rgw_admin(cmd, check_retcode=False)
+ if ret != 0:
+ raise TestException("failed command: user create --uid %s" % uid)
+
+ try:
+ outj = json.loads(out.decode('utf-8'))
+ except ValueError:
+ raise TestException("invalid json after: user create --uid %s" % uid)
+ if not isinstance(outj, dict):
+ raise TestException("bad json after: user create --uid %s" % uid)
+ if outj['user_id'] != uid:
+ raise TestException(
+ "command: user create --uid %s, returned user_id %s" %
+ (uid, outj['user_id']))
+
+#
+# Create a tenantized user with --tenant foo
+#
+def test3(cluster):
+ tid = "testx3"
+ uid = "tester3"
+ display_name = "Test_User_3"
+ access_key = "tester3KEY"
+ s3_secret = "test3pass"
+ cmd = t.build_cmd(
+ '--tenant', tid,
+ '--uid', uid,
+ '--display-name', display_name,
+ '--access-key', access_key,
+ '--secret', s3_secret,
+ "user create")
+ out, ret = cluster.rgw_admin(cmd, check_retcode=False)
+ if ret != 0:
+ raise TestException("failed command: user create --uid %s" % uid)
+
+ try:
+ outj = json.loads(out.decode('utf-8'))
+ except ValueError:
+ raise TestException("invalid json after: user create --uid %s" % uid)
+ if not isinstance(outj, dict):
+ raise TestException("bad json after: user create --uid %s" % uid)
+ tid_uid = "%s$%s" % (tid, uid)
+ if outj['user_id'] != tid_uid:
+ raise TestException(
+ "command: user create --uid %s, returned user_id %s" %
+ (tid_uid, outj['user_id']))
+
+#
+# Create a tenantized user with a subuser
+#
+# N.B. The aim of this test is not just to create a subuser, but to create
+# the key with a separate command, which does not use --tenant, but extracts
+# the tenant from the subuser. No idea why we allow this. There was some kind
+# of old script that did this.
+#
+def test4(cluster):
+ tid = "testx4"
+ uid = "tester4"
+ subid = "test4"
+
+ display_name = "Test_User_4"
+ cmd = t.build_cmd(
+ '--tenant', tid,
+ '--uid', uid,
+ '--display-name', display_name,
+ '--subuser', '%s:%s' % (uid, subid),
+ '--key-type', 'swift',
+ '--access', 'full',
+ "user create")
+ out, ret = cluster.rgw_admin(cmd, check_retcode=False)
+ if ret != 0:
+ raise TestException("failed command: user create --uid %s" % uid)
+
+ try:
+ outj = json.loads(out.decode('utf-8'))
+ except ValueError:
+ raise TestException("invalid json after: user create --uid %s" % uid)
+ if not isinstance(outj, dict):
+ raise TestException("bad json after: user create --uid %s" % uid)
+ tid_uid = "%s$%s" % (tid, uid)
+ if outj['user_id'] != tid_uid:
+ raise TestException(
+ "command: user create --uid %s, returned user_id %s" %
+ (tid_uid, outj['user_id']))
+
+ # Note that this tests a way to identify a fully-qualified subuser
+ # without --tenant and --uid. This is a historic use that we support.
+ swift_secret = "test3pass"
+ cmd = t.build_cmd(
+ '--subuser', "'%s$%s:%s'" % (tid, uid, subid),
+ '--key-type', 'swift',
+ '--secret', swift_secret,
+ "key create")
+ out, ret = cluster.rgw_admin(cmd, check_retcode=False)
+ if ret != 0:
+ raise TestException("failed command: key create --uid %s" % uid)
+
+ try:
+ outj = json.loads(out.decode('utf-8'))
+ except ValueError:
+ raise TestException("invalid json after: key create --uid %s" % uid)
+ if not isinstance(outj, dict):
+ raise TestException("bad json after: key create --uid %s" % uid)
+ tid_uid = "%s$%s" % (tid, uid)
+ if outj['user_id'] != tid_uid:
+ raise TestException(
+ "command: key create --uid %s, returned user_id %s" %
+ (tid_uid, outj['user_id']))
+ # These tests easily can throw KeyError, needs a try: XXX
+ skj = outj['swift_keys'][0]
+ if skj['secret_key'] != swift_secret:
+ raise TestException(
+ "command: key create --uid %s, returned swift key %s" %
+ (tid_uid, skj['secret_key']))
+
+#
+# Access the cluster, create containers in two tenants, verify it all works.
+#
+
+def test5_add_s3_key(cluster, tid, uid):
+ secret = "%spass" % uid
+ if tid:
+ tid_uid = "%s$%s" % (tid, uid)
+ else:
+ tid_uid = uid
+
+ cmd = t.build_cmd(
+ '--uid', "'%s'" % (tid_uid,),
+ '--access-key', uid,
+ '--secret', secret,
+ "key create")
+ out, ret = cluster.rgw_admin(cmd, check_retcode=False)
+ if ret != 0:
+ raise TestException("failed command: key create --uid %s" % uid)
+
+ try:
+ outj = json.loads(out.decode('utf-8'))
+ except ValueError:
+ raise TestException("invalid json after: key create --uid %s" % uid)
+ if not isinstance(outj, dict):
+ raise TestException("bad json after: key create --uid %s" % uid)
+ if outj['user_id'] != tid_uid:
+ raise TestException(
+ "command: key create --uid %s, returned user_id %s" %
+ (uid, outj['user_id']))
+ skj = outj['keys'][0]
+ if skj['secret_key'] != secret:
+ raise TestException(
+ "command: key create --uid %s, returned s3 key %s" %
+ (uid, skj['secret_key']))
+
+def test5_add_swift_key(cluster, tid, uid, subid):
+ secret = "%spass" % uid
+ if tid:
+ tid_uid = "%s$%s" % (tid, uid)
+ else:
+ tid_uid = uid
+
+ cmd = t.build_cmd(
+ '--subuser', "'%s:%s'" % (tid_uid, subid),
+ '--key-type', 'swift',
+ '--secret', secret,
+ "key create")
+ out, ret = cluster.rgw_admin(cmd, check_retcode=False)
+ if ret != 0:
+ raise TestException("failed command: key create --uid %s" % uid)
+
+ try:
+ outj = json.loads(out.decode('utf-8'))
+ except ValueError:
+ raise TestException("invalid json after: key create --uid %s" % uid)
+ if not isinstance(outj, dict):
+ raise TestException("bad json after: key create --uid %s" % uid)
+ if outj['user_id'] != tid_uid:
+ raise TestException(
+ "command: key create --uid %s, returned user_id %s" %
+ (uid, outj['user_id']))
+ # XXX checking wrong thing here (S3 key)
+ skj = outj['keys'][0]
+ if skj['secret_key'] != secret:
+ raise TestException(
+ "command: key create --uid %s, returned s3 key %s" %
+ (uid, skj['secret_key']))
+
+def test5_make_user(cluster, tid, uid, subid):
+ """
+ :param tid: Tenant ID string or None for the legacy tenant
+ :param uid: User ID string
+ :param subid: Subuser ID, may be None for S3-only users
+ """
+ display_name = "'Test User %s'" % uid
+
+ cmd = ""
+ if tid:
+ cmd = t.build_cmd(cmd,
+ '--tenant', tid)
+ cmd = t.build_cmd(cmd,
+ '--uid', uid,
+ '--display-name', display_name)
+ if subid:
+ cmd = t.build_cmd(cmd,
+ '--subuser', '%s:%s' % (uid, subid),
+ '--key-type', 'swift')
+ cmd = t.build_cmd(cmd,
+ '--access', 'full',
+ "user create")
+
+ out, ret = cluster.rgw_admin(cmd, check_retcode=False)
+ if ret != 0:
+ raise TestException("failed command: user create --uid %s" % uid)
+ try:
+ outj = json.loads(out.decode('utf-8'))
+ except ValueError:
+ raise TestException("invalid json after: user create --uid %s" % uid)
+ if not isinstance(outj, dict):
+ raise TestException("bad json after: user create --uid %s" % uid)
+ if tid:
+ tid_uid = "%s$%s" % (tid, uid)
+ else:
+ tid_uid = uid
+ if outj['user_id'] != tid_uid:
+ raise TestException(
+ "command: user create --uid %s, returned user_id %s" %
+ (tid_uid, outj['user_id']))
+
+ #
+ # For now, this uses hardcoded passwords based on uid.
+ # They are all different for ease of debugging in case something crosses.
+ #
+ test5_add_s3_key(cluster, tid, uid)
+ if subid:
+ test5_add_swift_key(cluster, tid, uid, subid)
+
+def test5_poke_s3(cluster):
+
+ bucketname = "test5cont1"
+ objname = "obj1"
+
+ # Not sure if we like useless information printed, but the rest of the
+ # test framework is insanely talkative when it executes commands.
+ # So, to keep it in line and have a marker when things go wrong, this.
+ print("PUT bucket %s object %s for tenant A (empty)" %
+ (bucketname, objname))
+ c = S3Connection(
+ aws_access_key_id="tester5a",
+ aws_secret_access_key="tester5apass",
+ is_secure=False,
+ host="localhost",
+ port = cluster.port,
+ calling_format = OrdinaryCallingFormat())
+
+ bucket = c.create_bucket(bucketname)
+
+ key = bucket.new_key(objname)
+ headers = { "Content-Type": "text/plain" }
+ key.set_contents_from_string(b"Test5A\n", headers)
+ key.set_acl('public-read')
+
+ #
+ # Now it's getting interesting. We're logging into a tenantized user.
+ #
+ print("PUT bucket %s object %s for tenant B" % (bucketname, objname))
+ c = S3Connection(
+ aws_access_key_id="tester5b1",
+ aws_secret_access_key="tester5b1pass",
+ is_secure=False,
+ host="localhost",
+ port = cluster.port,
+ calling_format = OrdinaryCallingFormat())
+
+ bucket = c.create_bucket(bucketname)
+ bucket.set_canned_acl('public-read')
+
+ key = bucket.new_key(objname)
+ headers = { "Content-Type": "text/plain" }
+ key.set_contents_from_string(b"Test5B\n", headers)
+ key.set_acl('public-read')
+
+ #
+ # Finally, let's fetch a couple of objects and verify that they
+ # are what they should be and we didn't get them overwritten.
+ # Note that we access one of objects across tenants using the colon.
+ #
+ print("GET bucket %s object %s for tenants A and B" %
+ (bucketname, objname))
+ c = S3Connection(
+ aws_access_key_id="tester5a",
+ aws_secret_access_key="tester5apass",
+ is_secure=False,
+ host="localhost",
+ port = cluster.port,
+ calling_format = OrdinaryCallingFormat())
+
+ bucket = c.get_bucket(bucketname)
+
+ key = bucket.get_key(objname)
+ body = key.get_contents_as_string()
+ if body != b"Test5A\n":
+ raise TestException("failed body check, bucket %s object %s" %
+ (bucketname, objname))
+
+ bucket = c.get_bucket("test5b:"+bucketname)
+ key = bucket.get_key(objname)
+ body = key.get_contents_as_string()
+ if body != b"Test5B\n":
+ raise TestException(
+ "failed body check, tenant %s bucket %s object %s" %
+ ("test5b", bucketname, objname))
+
+ print("Poke OK")
+
+
+def test5(cluster):
+ # Plan:
+ # 0. create users tester5a and test5b$tester5b1 test5b$tester5b2
+ # 1. create buckets "test5cont" under test5a and test5b
+ # 2. create objects in the buckets
+ # 3. access objects (across users in container test5b)
+
+ test5_make_user(cluster, None, "tester5a", "test5a")
+ test5_make_user(cluster, "test5b", "tester5b1", "test5b1")
+ test5_make_user(cluster, "test5b", "tester5b2", "test5b2")
+
+ test5_poke_s3(cluster)
+
+
+# XXX this parse_args boolean makes no sense. we should pass argv[] instead,
+# possibly empty. (copied from test_multi, correct it there too)
+def init(parse_args):
+
+ #argv = []
+ #if parse_args:
+ # argv = sys.argv[1:]
+ #args = parser.parse_args(argv)
+
+ #rgw_multi = RGWMulti(int(args.num_zones))
+ #rgw_multi.setup(not args.no_bootstrap)
+
+ # __init__():
+ port = 8001
+ clnum = 1 # number of clusters
+ clid = 1 # 1-based
+ cluster = t.RGWCluster(clid, port)
+
+ # setup():
+ cluster.start()
+ cluster.start_rgw()
+
+ # The cluster is always reset at this point, so we don't need to list
+ # users or delete pre-existing users.
+
+ try:
+ test2(cluster)
+ test3(cluster)
+ test4(cluster)
+ test5(cluster)
+ except TestException as e:
+ cluster.stop_rgw()
+ cluster.stop()
+ sys.stderr.write("FAIL\n")
+ sys.stderr.write("%s\n" % str(e))
+ return 1
+
+ # teardown():
+ cluster.stop_rgw()
+ cluster.stop()
+ return 0
+
+def setup_module():
+ return init(False)
+
+if __name__ == "__main__":
+ sys.exit(init(True))
diff --git a/src/test/rgw/test_multi.md b/src/test/rgw/test_multi.md
new file mode 100644
index 000000000..f2c128530
--- /dev/null
+++ b/src/test/rgw/test_multi.md
@@ -0,0 +1,56 @@
+# Multi Site Test Framework
+This framework allows you to write and run tests against a **local** multi-cluster environment. The framework is using the `mstart.sh` script in order to setup the environment according to a configuration file, and then uses the [nose](https://nose.readthedocs.io/en/latest/) test framework to actually run the tests.
+Tests are written in python2.7, but can invoke shell scripts, binaries etc.
+## Running Tests
+Entry point for all tests is `/path/to/ceph/src/test/rgw/test_multi.py`. And the actual tests are located inside the `/path/to/ceph/src/test/rgw/rgw_multi` subdirectory.
+So, to run all tests use:
+```
+$ cd /path/to/ceph/src/test/rgw/
+$ nosetests test_multi.py
+```
+This will assume a configuration file called `/path/to/ceph/src/test/rgw/test_multi.conf` exists.
+To use a different configuration file, set the `RGW_MULTI_TEST_CONF` environment variable to point to that file.
+Since we use the same entry point file for all tests, running specific tests is possible using the following format:
+```
+$ nosetests test_multi.py:<specific_test_name>
+```
+To run miltiple tests based on wildcard string, use the following format:
+```
+$ nosetests test_multi.py -m "<wildcard string>"
+```
+Note that the test to run, does not have to be inside the `test_multi.py` file.
+Note that different options for running specific and multiple tests exists in the [nose documentation](https://nose.readthedocs.io/en/latest/usage.html#options), as well as other options to control the execution of the tests.
+## Configuration
+### Environment Variables
+Following RGW environment variables are taken into consideration when running the tests:
+ - `RGW_FRONTEND`: used to change frontend to 'civetweb' or 'beast' (default)
+ - `RGW_VALGRIND`: used to run the radosgw under valgrind. e.g. RGW_VALGRIND=yes
+Other environment variables used to configure elements other than RGW can also be used as they are used in vstart.sh. E.g. MON, OSD, MGR, MSD
+The configuration file for the run has 3 sections:
+### Default
+This section holds the following parameters:
+ - `num_zonegroups`: number of zone groups (integer, default 1)
+ - `num_zones`: number of regular zones in each group (integer, default 3)
+ - `num_az_zones`: number of archive zones (integer, default 0, max value 1)
+ - `gateways_per_zone`: number of RADOS gateways per zone (integer, default 2)
+ - `no_bootstrap`: whether to assume that the cluster is already up and does not need to be setup again. If set to "false", it will try to re-run the cluster, so, `mstop.sh` must be called beforehand. Should be set to false, anytime the configuration is changed. Otherwise, and assuming the cluster is already up, it should be set to "true" to save on execution time (boolean, default false)
+ - `log_level`: console log level of the logs in the tests, note that any program invoked from the test my emit logs regardless of that setting (integer, default 20)
+ - 20 and up -> DEBUG
+ - 10 and up -> INFO
+ - 5 and up -> WARNING
+ - 1 and up -> ERROR
+ - CRITICAL is always logged
+- `log_file`: log file name. If not set, only console logs exists (string, default None)
+- `file_log_level`: file log level of the logs in the tests. Similar to `log_level`
+- `tenant`: name of tenant (string, default None)
+- `checkpoint_retries`: *TODO* (integer, default 60)
+- `checkpoint_delay`: *TODO* (integer, default 5)
+- `reconfigure_delay`: *TODO* (integer, default 5)
+### Elasticsearch
+*TODO*
+### Cloud
+*TODO*
+## Writing Tests
+New tests should be added into the `/path/to/ceph/src/test/rgw/rgw_multi` subdirectory.
+- Base classes are in: `/path/to/ceph/src/test/rgw/rgw_multi/multisite.py`
+- `/path/to/ceph/src/test/rgw/rgw_multi/tests.py` holds the majority of the tests, but also many utility and infrastructure functions that could be used in other tests files
diff --git a/src/test/rgw/test_multi.py b/src/test/rgw/test_multi.py
new file mode 100644
index 000000000..57d27343e
--- /dev/null
+++ b/src/test/rgw/test_multi.py
@@ -0,0 +1,410 @@
+import subprocess
+import os
+import random
+import string
+import argparse
+import sys
+import logging
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
+
+import nose.core
+
+from rgw_multi import multisite
+from rgw_multi.zone_rados import RadosZone as RadosZone
+from rgw_multi.zone_es import ESZone as ESZone
+from rgw_multi.zone_es import ESZoneConfig as ESZoneConfig
+from rgw_multi.zone_cloud import CloudZone as CloudZone
+from rgw_multi.zone_cloud import CloudZoneConfig as CloudZoneConfig
+from rgw_multi.zone_az import AZone as AZone
+from rgw_multi.zone_az import AZoneConfig as AZoneConfig
+
+# make tests from rgw_multi.tests available to nose
+from rgw_multi.tests import *
+from rgw_multi.tests_es import *
+from rgw_multi.tests_az import *
+
+mstart_path = os.getenv('MSTART_PATH')
+if mstart_path is None:
+ mstart_path = os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + '/../..') + '/'
+
+test_path = os.path.normpath(os.path.dirname(os.path.realpath(__file__))) + '/'
+
+# configure logging for the tests module
+log = logging.getLogger('rgw_multi.tests')
+
+def bash(cmd, **kwargs):
+ log.debug('running cmd: %s', ' '.join(cmd))
+ check_retcode = kwargs.pop('check_retcode', True)
+ kwargs['stdout'] = subprocess.PIPE
+ process = subprocess.Popen(cmd, **kwargs)
+ s = process.communicate()[0].decode('utf-8')
+ log.debug('command returned status=%d stdout=%s', process.returncode, s)
+ if check_retcode:
+ assert(process.returncode == 0)
+ return (s, process.returncode)
+
+class Cluster(multisite.Cluster):
+ """ cluster implementation based on mstart/mrun scripts """
+ def __init__(self, cluster_id):
+ super(Cluster, self).__init__()
+ self.cluster_id = cluster_id
+ self.needs_reset = True
+
+ def admin(self, args = None, **kwargs):
+ """ radosgw-admin command """
+ cmd = [test_path + 'test-rgw-call.sh', 'call_rgw_admin', self.cluster_id]
+ if args:
+ cmd += args
+ cmd += ['--debug-rgw=' + str(kwargs.pop('debug_rgw', 0))]
+ cmd += ['--debug-ms=' + str(kwargs.pop('debug_ms', 0))]
+ if kwargs.pop('read_only', False):
+ cmd += ['--rgw-cache-enabled=false']
+ return bash(cmd, **kwargs)
+
+ def start(self):
+ cmd = [mstart_path + 'mstart.sh', self.cluster_id]
+ env = None
+ if self.needs_reset:
+ env = os.environ.copy()
+ env['CEPH_NUM_MDS'] = '0'
+ cmd += ['-n']
+ # cmd += ['-o']
+ # cmd += ['rgw_cache_enabled=false']
+ bash(cmd, env=env)
+ self.needs_reset = False
+
+ def stop(self):
+ cmd = [mstart_path + 'mstop.sh', self.cluster_id]
+ bash(cmd)
+
+class Gateway(multisite.Gateway):
+ """ gateway implementation based on mrgw/mstop scripts """
+ def __init__(self, client_id = None, *args, **kwargs):
+ super(Gateway, self).__init__(*args, **kwargs)
+ self.id = client_id
+
+ def start(self, args = None):
+ """ start the gateway """
+ assert(self.cluster)
+ env = os.environ.copy()
+ # to change frontend, set RGW_FRONTEND env variable
+ # e.g. RGW_FRONTEND=civetweb
+ # to run test under valgrind memcheck, set RGW_VALGRIND to 'yes'
+ # e.g. RGW_VALGRIND=yes
+ cmd = [mstart_path + 'mrgw.sh', self.cluster.cluster_id, str(self.port), str(self.ssl_port)]
+ if self.id:
+ cmd += ['-i', self.id]
+ cmd += ['--debug-rgw=20', '--debug-ms=1']
+ if args:
+ cmd += args
+ bash(cmd, env=env)
+
+ def stop(self):
+ """ stop the gateway """
+ assert(self.cluster)
+ cmd = [mstart_path + 'mstop.sh', self.cluster.cluster_id, 'radosgw', str(self.port)]
+ bash(cmd)
+
+def gen_access_key():
+ return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(16))
+
+def gen_secret():
+ return ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(32))
+
+def gen_credentials():
+ return multisite.Credentials(gen_access_key(), gen_secret())
+
+def cluster_name(cluster_num):
+ return 'c' + str(cluster_num)
+
+def zonegroup_name(zonegroup_num):
+ return string.ascii_lowercase[zonegroup_num]
+
+def zone_name(zonegroup_num, zone_num):
+ return zonegroup_name(zonegroup_num) + str(zone_num + 1)
+
+def gateway_port(zonegroup_num, gateway_num):
+ return 8000 + 100 * zonegroup_num + gateway_num
+
+def gateway_name(zonegroup_num, zone_num, gateway_num):
+ return zone_name(zonegroup_num, zone_num) + '-' + str(gateway_num + 1)
+
+def zone_endpoints(zonegroup_num, zone_num, gateways_per_zone):
+ endpoints = []
+ base = gateway_port(zonegroup_num, zone_num * gateways_per_zone)
+ for i in range(0, gateways_per_zone):
+ endpoints.append('http://localhost:' + str(base + i))
+ return endpoints
+
+def get_log_level(log_level):
+ if log_level >= 20:
+ return logging.DEBUG
+ if log_level >= 10:
+ return logging.INFO
+ if log_level >= 5:
+ return logging.WARN
+ if log_level >= 1:
+ return logging.ERROR
+ return logging.CRITICAL
+
+def setup_logging(log_level_console, log_file, log_level_file):
+ if log_file:
+ formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
+ fh = logging.FileHandler(log_file)
+ fh.setFormatter(formatter)
+ fh.setLevel(get_log_level(log_level_file))
+ log.addHandler(fh)
+
+ formatter = logging.Formatter('%(levelname)s %(message)s')
+ ch = logging.StreamHandler()
+ ch.setFormatter(formatter)
+ ch.setLevel(get_log_level(log_level_console))
+ log.addHandler(ch)
+ log.setLevel(get_log_level(log_level_console))
+
+def init(parse_args):
+ cfg = configparser.RawConfigParser({
+ 'num_zonegroups': 1,
+ 'num_zones': 3,
+ 'num_az_zones': 0,
+ 'gateways_per_zone': 2,
+ 'no_bootstrap': 'false',
+ 'log_level': 20,
+ 'log_file': None,
+ 'file_log_level': 20,
+ 'tenant': None,
+ 'checkpoint_retries': 60,
+ 'checkpoint_delay': 5,
+ 'reconfigure_delay': 5,
+ 'use_ssl': 'false',
+ })
+ try:
+ path = os.environ['RGW_MULTI_TEST_CONF']
+ except KeyError:
+ path = test_path + 'test_multi.conf'
+
+ try:
+ with open(path) as f:
+ cfg.readfp(f)
+ except:
+ print('WARNING: error reading test config. Path can be set through the RGW_MULTI_TEST_CONF env variable')
+ pass
+
+ parser = argparse.ArgumentParser(
+ description='Run rgw multi-site tests',
+ usage='test_multi [--num-zonegroups <num>] [--num-zones <num>] [--no-bootstrap]')
+
+ section = 'DEFAULT'
+ parser.add_argument('--num-zonegroups', type=int, default=cfg.getint(section, 'num_zonegroups'))
+ parser.add_argument('--num-zones', type=int, default=cfg.getint(section, 'num_zones'))
+ parser.add_argument('--gateways-per-zone', type=int, default=cfg.getint(section, 'gateways_per_zone'))
+ parser.add_argument('--no-bootstrap', action='store_true', default=cfg.getboolean(section, 'no_bootstrap'))
+ parser.add_argument('--log-level', type=int, default=cfg.getint(section, 'log_level'))
+ parser.add_argument('--log-file', type=str, default=cfg.get(section, 'log_file'))
+ parser.add_argument('--file-log-level', type=int, default=cfg.getint(section, 'file_log_level'))
+ parser.add_argument('--tenant', type=str, default=cfg.get(section, 'tenant'))
+ parser.add_argument('--checkpoint-retries', type=int, default=cfg.getint(section, 'checkpoint_retries'))
+ parser.add_argument('--checkpoint-delay', type=int, default=cfg.getint(section, 'checkpoint_delay'))
+ parser.add_argument('--reconfigure-delay', type=int, default=cfg.getint(section, 'reconfigure_delay'))
+ parser.add_argument('--use-ssl', type=bool, default=cfg.getboolean(section, 'use_ssl'))
+
+
+ es_cfg = []
+ cloud_cfg = []
+ az_cfg = []
+
+ for s in cfg.sections():
+ if s.startswith('elasticsearch'):
+ es_cfg.append(ESZoneConfig(cfg, s))
+ elif s.startswith('cloud'):
+ cloud_cfg.append(CloudZoneConfig(cfg, s))
+ elif s.startswith('archive'):
+ az_cfg.append(AZoneConfig(cfg, s))
+
+
+ argv = []
+
+ if parse_args:
+ argv = sys.argv[1:]
+
+ args = parser.parse_args(argv)
+ bootstrap = not args.no_bootstrap
+
+ setup_logging(args.log_level, args.log_file, args.file_log_level)
+
+ # start first cluster
+ c1 = Cluster(cluster_name(1))
+ if bootstrap:
+ c1.start()
+ clusters = []
+ clusters.append(c1)
+
+ admin_creds = gen_credentials()
+ admin_user = multisite.User('zone.user')
+
+ user_creds = gen_credentials()
+ user = multisite.User('tester', tenant=args.tenant)
+
+ realm = multisite.Realm('r')
+ if bootstrap:
+ # create the realm on c1
+ realm.create(c1)
+ else:
+ realm.get(c1)
+ period = multisite.Period(realm=realm)
+ realm.current_period = period
+
+ num_es_zones = len(es_cfg)
+ num_cloud_zones = len(cloud_cfg)
+ num_az_zones = cfg.getint(section, 'num_az_zones')
+
+ num_zones = args.num_zones + num_es_zones + num_cloud_zones + num_az_zones
+
+ use_ssl = cfg.getboolean(section, 'use_ssl')
+
+ if use_ssl and bootstrap:
+ cmd = ['openssl', 'req',
+ '-x509',
+ '-newkey', 'rsa:4096',
+ '-sha256',
+ '-nodes',
+ '-keyout', 'key.pem',
+ '-out', 'cert.pem',
+ '-subj', '/CN=localhost',
+ '-days', '3650']
+ bash(cmd)
+ # append key to cert
+ fkey = open('./key.pem', 'r')
+ if fkey.mode == 'r':
+ fcert = open('./cert.pem', 'a')
+ fcert.write(fkey.read())
+ fcert.close()
+ fkey.close()
+
+ for zg in range(0, args.num_zonegroups):
+ zonegroup = multisite.ZoneGroup(zonegroup_name(zg), period)
+ period.zonegroups.append(zonegroup)
+
+ is_master_zg = zg == 0
+ if is_master_zg:
+ period.master_zonegroup = zonegroup
+
+ for z in range(0, num_zones):
+ is_master = z == 0
+ # start a cluster, or use c1 for first zone
+ cluster = None
+ if is_master_zg and is_master:
+ cluster = c1
+ else:
+ cluster = Cluster(cluster_name(len(clusters) + 1))
+ clusters.append(cluster)
+ if bootstrap:
+ cluster.start()
+ # pull realm configuration from the master's gateway
+ gateway = realm.meta_master_zone().gateways[0]
+ realm.pull(cluster, gateway, admin_creds)
+
+ endpoints = zone_endpoints(zg, z, args.gateways_per_zone)
+ if is_master:
+ if bootstrap:
+ # create the zonegroup on its first zone's cluster
+ arg = []
+ if is_master_zg:
+ arg += ['--master']
+ if len(endpoints): # use master zone's endpoints
+ arg += ['--endpoints', ','.join(endpoints)]
+ zonegroup.create(cluster, arg)
+ else:
+ zonegroup.get(cluster)
+
+ es_zone = (z >= args.num_zones and z < args.num_zones + num_es_zones)
+ cloud_zone = (z >= args.num_zones + num_es_zones and z < args.num_zones + num_es_zones + num_cloud_zones)
+ az_zone = (z >= args.num_zones + num_es_zones + num_cloud_zones)
+
+ # create the zone in its zonegroup
+ zone = multisite.Zone(zone_name(zg, z), zonegroup, cluster)
+ if es_zone:
+ zone_index = z - args.num_zones
+ zone = ESZone(zone_name(zg, z), es_cfg[zone_index].endpoint, zonegroup, cluster)
+ elif cloud_zone:
+ zone_index = z - args.num_zones - num_es_zones
+ ccfg = cloud_cfg[zone_index]
+ zone = CloudZone(zone_name(zg, z), ccfg.endpoint, ccfg.credentials, ccfg.source_bucket,
+ ccfg.target_path, zonegroup, cluster)
+ elif az_zone:
+ zone_index = z - args.num_zones - num_es_zones - num_cloud_zones
+ zone = AZone(zone_name(zg, z), zonegroup, cluster)
+ else:
+ zone = RadosZone(zone_name(zg, z), zonegroup, cluster)
+
+ if bootstrap:
+ arg = admin_creds.credential_args()
+ if is_master:
+ arg += ['--master']
+ if len(endpoints):
+ arg += ['--endpoints', ','.join(endpoints)]
+ zone.create(cluster, arg)
+ else:
+ zone.get(cluster)
+ zonegroup.zones.append(zone)
+ if is_master:
+ zonegroup.master_zone = zone
+
+ zonegroup.zones_by_type.setdefault(zone.tier_type(), []).append(zone)
+
+ if zone.is_read_only():
+ zonegroup.ro_zones.append(zone)
+ else:
+ zonegroup.rw_zones.append(zone)
+
+ # update/commit the period
+ if bootstrap:
+ period.update(zone, commit=True)
+
+ ssl_port_offset = 1000
+ # start the gateways
+ for g in range(0, args.gateways_per_zone):
+ port = gateway_port(zg, g + z * args.gateways_per_zone)
+ client_id = gateway_name(zg, z, g)
+ gateway = Gateway(client_id, 'localhost', port, cluster, zone,
+ ssl_port = port+ssl_port_offset if use_ssl else 0)
+ if bootstrap:
+ gateway.start()
+ zone.gateways.append(gateway)
+
+ if is_master_zg and is_master:
+ if bootstrap:
+ # create admin user
+ arg = ['--display-name', '"Zone User"', '--system']
+ arg += admin_creds.credential_args()
+ admin_user.create(zone, arg)
+ # create test user
+ arg = ['--display-name', '"Test User"', '--caps', 'roles=*']
+ arg += user_creds.credential_args()
+ user.create(zone, arg)
+ else:
+ # read users and update keys
+ admin_user.info(zone)
+ admin_creds = admin_user.credentials[0]
+ arg = []
+ user.info(zone, arg)
+ user_creds = user.credentials[0]
+
+ if not bootstrap:
+ period.get(c1)
+
+ config = Config(checkpoint_retries=args.checkpoint_retries,
+ checkpoint_delay=args.checkpoint_delay,
+ reconfigure_delay=args.reconfigure_delay,
+ tenant=args.tenant)
+ init_multi(realm, user, config)
+
+def setup_module():
+ init(False)
+
+if __name__ == "__main__":
+ init(True)
+
diff --git a/src/test/rgw/test_rgw_amqp.cc b/src/test/rgw/test_rgw_amqp.cc
new file mode 100644
index 000000000..f49d309c7
--- /dev/null
+++ b/src/test/rgw/test_rgw_amqp.cc
@@ -0,0 +1,529 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "rgw_amqp.h"
+#include "common/ceph_context.h"
+#include "amqp_mock.h"
+#include <gtest/gtest.h>
+#include <chrono>
+#include <thread>
+#include <atomic>
+
+using namespace rgw;
+
+const std::chrono::milliseconds wait_time(10);
+const std::chrono::milliseconds long_wait_time = wait_time*50;
+const std::chrono::seconds idle_time(35);
+
+
+class CctCleaner {
+ CephContext* cct;
+public:
+ CctCleaner(CephContext* _cct) : cct(_cct) {}
+ ~CctCleaner() {
+#ifdef WITH_SEASTAR
+ delete cct;
+#else
+ cct->put();
+#endif
+ }
+};
+
+auto cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+
+CctCleaner cleaner(cct);
+
+class TestAMQP : public ::testing::Test {
+protected:
+ amqp::connection_id_t conn_id;
+ unsigned current_dequeued = 0U;
+
+ void SetUp() override {
+ ASSERT_TRUE(amqp::init(cct));
+ }
+
+ void TearDown() override {
+ amqp::shutdown();
+ }
+
+ // wait for at least one new (since last drain) message to be dequeueud
+ // and then wait for all pending answers to be received
+ void wait_until_drained() {
+ while (amqp::get_dequeued() == current_dequeued) {
+ std::this_thread::sleep_for(wait_time);
+ }
+ while (amqp::get_inflight() > 0) {
+ std::this_thread::sleep_for(wait_time);
+ }
+ current_dequeued = amqp::get_dequeued();
+ }
+};
+
+std::atomic<bool> callback_invoked = false;
+
+std::atomic<int> callbacks_invoked = 0;
+
+// note: because these callback are shared among different "publish" calls
+// they should be used on different connections
+
+void my_callback_expect_ack(int rc) {
+ EXPECT_EQ(0, rc);
+ callback_invoked = true;
+}
+
+void my_callback_expect_nack(int rc) {
+ EXPECT_LT(rc, 0);
+ callback_invoked = true;
+}
+
+void my_callback_expect_multiple_acks(int rc) {
+ EXPECT_EQ(0, rc);
+ ++callbacks_invoked;
+}
+
+class dynamic_callback_wrapper {
+ dynamic_callback_wrapper() = default;
+public:
+ static dynamic_callback_wrapper* create() {
+ return new dynamic_callback_wrapper;
+ }
+ void callback(int rc) {
+ EXPECT_EQ(0, rc);
+ ++callbacks_invoked;
+ delete this;
+ }
+};
+
+void my_callback_expect_close_or_ack(int rc) {
+ // deleting the connection should trigger the callback with -4098
+ // but due to race conditions, some my get an ack
+ EXPECT_TRUE(-4098 == rc || 0 == rc);
+}
+
+TEST_F(TestAMQP, ConnectionOK)
+{
+ const auto connection_number = amqp::get_connection_count();
+ auto rc = amqp::connect(conn_id, "amqp://localhost", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = amqp::publish(conn_id, "topic", "message");
+ EXPECT_EQ(rc, 0);
+}
+
+TEST_F(TestAMQP, SSLConnectionOK)
+{
+ const int port = 5671;
+ const auto connection_number = amqp::get_connection_count();
+ amqp_mock::set_valid_port(port);
+ auto rc = amqp::connect(conn_id, "amqps://localhost", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = amqp::publish(conn_id, "topic", "message");
+ EXPECT_EQ(rc, 0);
+ amqp_mock::set_valid_port(5672);
+}
+
+TEST_F(TestAMQP, PlainAndSSLConnectionsOK)
+{
+ const int port = 5671;
+ const auto connection_number = amqp::get_connection_count();
+ amqp_mock::set_valid_port(port);
+ amqp::connection_id_t conn_id1;
+ auto rc = amqp::connect(conn_id1, "amqps://localhost", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = amqp::publish(conn_id1, "topic", "message");
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(amqp::to_string(conn_id1), "amqps://localhost:5671/?exchange=ex1");
+ amqp_mock::set_valid_port(5672);
+ amqp::connection_id_t conn_id2;
+ rc = amqp::connect(conn_id2, "amqp://localhost", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::to_string(conn_id2), "amqp://localhost:5672/?exchange=ex1");
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 2);
+ rc = amqp::publish(conn_id2, "topic", "message");
+ EXPECT_EQ(rc, 0);
+}
+
+TEST_F(TestAMQP, ConnectionReuse)
+{
+ amqp::connection_id_t conn_id1;
+ auto rc = amqp::connect(conn_id1, "amqp://localhost", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id2;
+ rc = amqp::connect(conn_id2, "amqp://localhost", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number);
+ rc = amqp::publish(conn_id1, "topic", "message");
+ EXPECT_EQ(rc, 0);
+}
+
+TEST_F(TestAMQP, NameResolutionFail)
+{
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://kaboom", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+}
+
+TEST_F(TestAMQP, InvalidPort)
+{
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://localhost:1234", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+}
+
+TEST_F(TestAMQP, InvalidHost)
+{
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://0.0.0.1", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+}
+
+TEST_F(TestAMQP, InvalidVhost)
+{
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://localhost/kaboom", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+}
+
+TEST_F(TestAMQP, UserPassword)
+{
+ amqp_mock::set_valid_host("127.0.0.1");
+ {
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://foo:bar@127.0.0.1", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ }
+ // now try the same connection with default user/password
+ amqp_mock::set_valid_host("127.0.0.2");
+ {
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://guest:guest@127.0.0.2", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_ack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ }
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, URLParseError)
+{
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "http://localhost", "ex1", false, false, boost::none);
+ EXPECT_FALSE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+}
+
+TEST_F(TestAMQP, ExchangeMismatch)
+{
+ callback_invoked = false;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "http://localhost", "ex2", false, false, boost::none);
+ EXPECT_FALSE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+}
+
+TEST_F(TestAMQP, MaxConnections)
+{
+ // fill up all connections
+ std::vector<amqp::connection_id_t> connections;
+ auto remaining_connections = amqp::get_max_connections() - amqp::get_connection_count();
+ while (remaining_connections > 0) {
+ const auto host = "127.10.0." + std::to_string(remaining_connections);
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_ack);
+ EXPECT_EQ(rc, 0);
+ --remaining_connections;
+ connections.push_back(conn_id);
+ }
+ EXPECT_EQ(amqp::get_connection_count(), amqp::get_max_connections());
+ wait_until_drained();
+ // try to add another connection
+ {
+ const std::string host = "toomany";
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_FALSE(rc);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ }
+ EXPECT_EQ(amqp::get_connection_count(), amqp::get_max_connections());
+ amqp_mock::set_valid_host("localhost");
+}
+
+
+TEST_F(TestAMQP, ReceiveAck)
+{
+ callback_invoked = false;
+ const std::string host("localhost1");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_ack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, ImplicitConnectionClose)
+{
+ callback_invoked = false;
+ const std::string host("localhost1");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ const auto NUMBER_OF_CALLS = 2000;
+ for (auto i = 0; i < NUMBER_OF_CALLS; ++i) {
+ auto rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_close_or_ack);
+ EXPECT_EQ(rc, 0);
+ }
+ wait_until_drained();
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, ReceiveMultipleAck)
+{
+ callbacks_invoked = 0;
+ const std::string host("localhost1");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ const auto NUMBER_OF_CALLS = 100;
+ for (auto i=0; i < NUMBER_OF_CALLS; ++i) {
+ auto rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_multiple_acks);
+ EXPECT_EQ(rc, 0);
+ }
+ wait_until_drained();
+ EXPECT_EQ(callbacks_invoked, NUMBER_OF_CALLS);
+ callbacks_invoked = 0;
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, ReceiveAckForMultiple)
+{
+ callbacks_invoked = 0;
+ const std::string host("localhost1");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ amqp_mock::set_multiple(59);
+ const auto NUMBER_OF_CALLS = 100;
+ for (auto i=0; i < NUMBER_OF_CALLS; ++i) {
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_multiple_acks);
+ EXPECT_EQ(rc, 0);
+ }
+ wait_until_drained();
+ EXPECT_EQ(callbacks_invoked, NUMBER_OF_CALLS);
+ callbacks_invoked = 0;
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, DynamicCallback)
+{
+ callbacks_invoked = 0;
+ const std::string host("localhost1");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ amqp_mock::set_multiple(59);
+ const auto NUMBER_OF_CALLS = 100;
+ for (auto i=0; i < NUMBER_OF_CALLS; ++i) {
+ rc = publish_with_confirm(conn_id, "topic", "message",
+ std::bind(&dynamic_callback_wrapper::callback, dynamic_callback_wrapper::create(), std::placeholders::_1));
+ EXPECT_EQ(rc, 0);
+ }
+ wait_until_drained();
+ EXPECT_EQ(callbacks_invoked, NUMBER_OF_CALLS);
+ callbacks_invoked = 0;
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, ReceiveNack)
+{
+ callback_invoked = false;
+ amqp_mock::REPLY_ACK = false;
+ const std::string host("localhost2");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ amqp_mock::REPLY_ACK = true;
+ callback_invoked = false;
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, FailWrite)
+{
+ callback_invoked = false;
+ amqp_mock::FAIL_NEXT_WRITE = true;
+ const std::string host("localhost2");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ amqp_mock::FAIL_NEXT_WRITE = false;
+ callback_invoked = false;
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, RetryInvalidHost)
+{
+ callback_invoked = false;
+ const std::string host = "192.168.0.1";
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://"+host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ // now next retry should be ok
+ callback_invoked = false;
+ amqp_mock::set_valid_host(host);
+ std::this_thread::sleep_for(long_wait_time);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_ack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, RetryInvalidPort)
+{
+ callback_invoked = false;
+ const int port = 9999;
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://localhost:" + std::to_string(port), "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ // now next retry should be ok
+ callback_invoked = false;
+ amqp_mock::set_valid_port(port);
+ std::this_thread::sleep_for(long_wait_time);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_ack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ amqp_mock::set_valid_port(5672);
+}
+
+TEST_F(TestAMQP, RetryFailWrite)
+{
+ callback_invoked = false;
+ amqp_mock::FAIL_NEXT_WRITE = true;
+ const std::string host("localhost2");
+ amqp_mock::set_valid_host(host);
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://" + host, "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ // now next retry should be ok
+ amqp_mock::FAIL_NEXT_WRITE = false;
+ callback_invoked = false;
+ std::this_thread::sleep_for(long_wait_time);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_ack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+ amqp_mock::set_valid_host("localhost");
+}
+
+TEST_F(TestAMQP, IdleConnection)
+{
+ // this test is skipped since it takes 30seconds
+ //GTEST_SKIP();
+ const auto connection_number = amqp::get_connection_count();
+ amqp::connection_id_t conn_id;
+ auto rc = amqp::connect(conn_id, "amqp://localhost", "ex1", false, false, boost::none);
+ EXPECT_TRUE(rc);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number + 1);
+ std::this_thread::sleep_for(idle_time);
+ EXPECT_EQ(amqp::get_connection_count(), connection_number);
+ rc = publish_with_confirm(conn_id, "topic", "message", my_callback_expect_nack);
+ EXPECT_EQ(rc, 0);
+ wait_until_drained();
+ EXPECT_TRUE(callback_invoked);
+}
+
diff --git a/src/test/rgw/test_rgw_arn.cc b/src/test/rgw/test_rgw_arn.cc
new file mode 100644
index 000000000..83445a275
--- /dev/null
+++ b/src/test/rgw/test_rgw_arn.cc
@@ -0,0 +1,107 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "rgw_arn.h"
+#include <gtest/gtest.h>
+
+using namespace rgw;
+
+const int BASIC_ENTRIES = 6;
+
+const std::string basic_str[BASIC_ENTRIES] = {"arn:aws:s3:us-east-1:12345:resource",
+ "arn:aws:s3:us-east-1:12345:resourceType/resource",
+ "arn:aws:s3:us-east-1:12345:resourceType/resource/qualifier",
+ "arn:aws:s3:us-east-1:12345:resourceType/resource:qualifier",
+ "arn:aws:s3:us-east-1:12345:resourceType:resource",
+ "arn:aws:s3:us-east-1:12345:resourceType:resource/qualifier"};
+
+const std::string expected_basic_resource[BASIC_ENTRIES] = {"resource",
+ "resourceType/resource",
+ "resourceType/resource/qualifier",
+ "resourceType/resource:qualifier",
+ "resourceType:resource",
+ "resourceType:resource/qualifier"};
+TEST(TestARN, Basic)
+{
+ for (auto i = 0; i < BASIC_ENTRIES; ++i) {
+ boost::optional<ARN> arn = ARN::parse(basic_str[i]);
+ ASSERT_TRUE(arn);
+ EXPECT_EQ(arn->partition, Partition::aws);
+ EXPECT_EQ(arn->service, Service::s3);
+ EXPECT_STREQ(arn->region.c_str(), "us-east-1");
+ EXPECT_STREQ(arn->account.c_str(), "12345");
+ EXPECT_STREQ(arn->resource.c_str(), expected_basic_resource[i].c_str());
+ }
+}
+
+TEST(TestARN, ToString)
+{
+ for (auto i = 0; i < BASIC_ENTRIES; ++i) {
+ boost::optional<ARN> arn = ARN::parse(basic_str[i]);
+ ASSERT_TRUE(arn);
+ EXPECT_STREQ(to_string(*arn).c_str(), basic_str[i].c_str());
+ }
+}
+
+const std::string expected_basic_resource_type[BASIC_ENTRIES] =
+ {"", "resourceType", "resourceType", "resourceType", "resourceType", "resourceType"};
+const std::string expected_basic_qualifier[BASIC_ENTRIES] =
+ {"", "", "qualifier", "qualifier", "", "qualifier"};
+
+TEST(TestARNResource, Basic)
+{
+ for (auto i = 0; i < BASIC_ENTRIES; ++i) {
+ boost::optional<ARN> arn = ARN::parse(basic_str[i]);
+ ASSERT_TRUE(arn);
+ ASSERT_FALSE(arn->resource.empty());
+ boost::optional<ARNResource> resource = ARNResource::parse(arn->resource);
+ ASSERT_TRUE(resource);
+ EXPECT_STREQ(resource->resource.c_str(), "resource");
+ EXPECT_STREQ(resource->resource_type.c_str(), expected_basic_resource_type[i].c_str());
+ EXPECT_STREQ(resource->qualifier.c_str(), expected_basic_qualifier[i].c_str());
+ }
+}
+
+const int EMPTY_ENTRIES = 4;
+
+const std::string empty_str[EMPTY_ENTRIES] = {"arn:aws:s3:::resource",
+ "arn:aws:s3::12345:resource",
+ "arn:aws:s3:us-east-1::resource",
+ "arn:aws:s3:us-east-1:12345:"};
+
+TEST(TestARN, Empty)
+{
+ for (auto i = 0; i < EMPTY_ENTRIES; ++i) {
+ boost::optional<ARN> arn = ARN::parse(empty_str[i]);
+ ASSERT_TRUE(arn);
+ EXPECT_EQ(arn->partition, Partition::aws);
+ EXPECT_EQ(arn->service, Service::s3);
+ EXPECT_TRUE(arn->region.empty() || arn->region == "us-east-1");
+ EXPECT_TRUE(arn->account.empty() || arn->account == "12345");
+ EXPECT_TRUE(arn->resource.empty() || arn->resource == "resource");
+ }
+}
+
+const int WILDCARD_ENTRIES = 3;
+
+const std::string wildcard_str[WILDCARD_ENTRIES] = {"arn:aws:s3:*:*:resource",
+ "arn:aws:s3:*:12345:resource",
+ "arn:aws:s3:us-east-1:*:resource"};
+
+// FIXME: currently the following: "arn:aws:s3:us-east-1:12345:*"
+// does not fail, even if "wildcard" is not set to "true"
+
+TEST(TestARN, Wildcard)
+{
+ for (auto i = 0; i < WILDCARD_ENTRIES; ++i) {
+ EXPECT_FALSE(ARN::parse(wildcard_str[i]));
+ boost::optional<ARN> arn = ARN::parse(wildcard_str[i], true);
+ ASSERT_TRUE(arn);
+ EXPECT_EQ(arn->partition, Partition::aws);
+ EXPECT_EQ(arn->service, Service::s3);
+ EXPECT_TRUE(arn->region == "*" || arn->region == "us-east-1");
+ EXPECT_TRUE(arn->account == "*" || arn->account == "12345");
+ EXPECT_TRUE(arn->resource == "*" || arn->resource == "resource");
+ }
+}
+
diff --git a/src/test/rgw/test_rgw_bencode.cc b/src/test/rgw/test_rgw_bencode.cc
new file mode 100644
index 000000000..c149d532c
--- /dev/null
+++ b/src/test/rgw/test_rgw_bencode.cc
@@ -0,0 +1,65 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "rgw_torrent.h"
+
+using namespace std;
+
+TEST(Bencode, String)
+{
+ TorrentBencode decode;
+ bufferlist bl;
+
+ decode.bencode("foo", bl);
+ decode.bencode("bar", bl);
+ decode.bencode("baz", bl);
+
+ string s(bl.c_str(), bl.length());
+
+ ASSERT_STREQ("3:foo3:bar3:baz", s.c_str());
+}
+
+TEST(Bencode, Integers)
+{
+ TorrentBencode decode;
+ bufferlist bl;
+
+ decode.bencode(0, bl);
+ decode.bencode(-3, bl);
+ decode.bencode(7, bl);
+
+ string s(bl.c_str(), bl.length());
+
+ ASSERT_STREQ("i0ei-3ei7e", s.c_str());
+}
+
+TEST(Bencode, Dict)
+{
+ TorrentBencode decode;
+ bufferlist bl;
+
+ decode.bencode_dict(bl);
+ decode.bencode("foo", 5, bl);
+ decode.bencode("bar", "baz", bl);
+ decode.bencode_end(bl);
+
+ string s(bl.c_str(), bl.length());
+
+ ASSERT_STREQ("d3:fooi5e3:bar3:baze", s.c_str());
+}
+
+TEST(Bencode, List)
+{
+ TorrentBencode decode;
+ bufferlist bl;
+
+ decode.bencode_list(bl);
+ decode.bencode("foo", 5, bl);
+ decode.bencode("bar", "baz", bl);
+ decode.bencode_end(bl);
+
+ string s(bl.c_str(), bl.length());
+
+ ASSERT_STREQ("l3:fooi5e3:bar3:baze", s.c_str());
+}
diff --git a/src/test/rgw/test_rgw_bucket_sync_cache.cc b/src/test/rgw/test_rgw_bucket_sync_cache.cc
new file mode 100644
index 000000000..22ec1005e
--- /dev/null
+++ b/src/test/rgw/test_rgw_bucket_sync_cache.cc
@@ -0,0 +1,189 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 Red Hat, Inc
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include "rgw_bucket_sync_cache.h"
+#include <gtest/gtest.h>
+
+using namespace rgw::bucket_sync;
+
+// helper function to construct rgw_bucket_shard
+static rgw_bucket_shard make_key(const std::string& tenant,
+ const std::string& bucket, int shard)
+{
+ auto key = rgw_bucket_key{tenant, bucket};
+ return rgw_bucket_shard{std::move(key), shard};
+}
+
+TEST(BucketSyncCache, ReturnCachedPinned)
+{
+ auto cache = Cache::create(0);
+ const auto key = make_key("", "1", 0);
+ auto h1 = cache->get(key, std::nullopt); // pin
+ h1->counter = 1;
+ auto h2 = cache->get(key, std::nullopt);
+ EXPECT_EQ(1, h2->counter);
+}
+
+TEST(BucketSyncCache, ReturnNewUnpinned)
+{
+ auto cache = Cache::create(0);
+ const auto key = make_key("", "1", 0);
+ cache->get(key, std::nullopt)->counter = 1; // pin+unpin
+ EXPECT_EQ(0, cache->get(key, std::nullopt)->counter);
+}
+
+TEST(BucketSyncCache, DistinctTenant)
+{
+ auto cache = Cache::create(2);
+ const auto key1 = make_key("a", "bucket", 0);
+ const auto key2 = make_key("b", "bucket", 0);
+ cache->get(key1, std::nullopt)->counter = 1;
+ EXPECT_EQ(0, cache->get(key2, std::nullopt)->counter);
+}
+
+TEST(BucketSyncCache, DistinctShards)
+{
+ auto cache = Cache::create(2);
+ const auto key1 = make_key("", "bucket", 0);
+ const auto key2 = make_key("", "bucket", 1);
+ cache->get(key1, std::nullopt)->counter = 1;
+ EXPECT_EQ(0, cache->get(key2, std::nullopt)->counter);
+}
+
+TEST(BucketSyncCache, DistinctGen)
+{
+ auto cache = Cache::create(2);
+ const auto key = make_key("", "bucket", 0);
+ std::optional<uint64_t> gen1; // empty
+ std::optional<uint64_t> gen2 = 5;
+ cache->get(key, gen1)->counter = 1;
+ EXPECT_EQ(0, cache->get(key, gen2)->counter);
+}
+
+TEST(BucketSyncCache, DontEvictPinned)
+{
+ auto cache = Cache::create(0);
+
+ const auto key1 = make_key("", "1", 0);
+ const auto key2 = make_key("", "2", 0);
+
+ auto h1 = cache->get(key1, std::nullopt);
+ EXPECT_EQ(key1, h1->key.first);
+ auto h2 = cache->get(key2, std::nullopt);
+ EXPECT_EQ(key2, h2->key.first);
+ EXPECT_EQ(key1, h1->key.first); // h1 unchanged
+}
+
+TEST(BucketSyncCache, HandleLifetime)
+{
+ const auto key = make_key("", "1", 0);
+
+ Handle h; // test that handles keep the cache referenced
+ {
+ auto cache = Cache::create(0);
+ h = cache->get(key, std::nullopt);
+ }
+ EXPECT_EQ(key, h->key.first);
+}
+
+TEST(BucketSyncCache, TargetSize)
+{
+ auto cache = Cache::create(2);
+
+ const auto key1 = make_key("", "1", 0);
+ const auto key2 = make_key("", "2", 0);
+ const auto key3 = make_key("", "3", 0);
+
+ // fill cache up to target_size=2
+ cache->get(key1, std::nullopt)->counter = 1;
+ cache->get(key2, std::nullopt)->counter = 2;
+ // test that each unpinned entry is still cached
+ EXPECT_EQ(1, cache->get(key1, std::nullopt)->counter);
+ EXPECT_EQ(2, cache->get(key2, std::nullopt)->counter);
+ // overflow the cache and recycle key1
+ cache->get(key3, std::nullopt)->counter = 3;
+ // test that the oldest entry was recycled
+ EXPECT_EQ(0, cache->get(key1, std::nullopt)->counter);
+}
+
+TEST(BucketSyncCache, HandleMoveAssignEmpty)
+{
+ auto cache = Cache::create(0);
+
+ const auto key1 = make_key("", "1", 0);
+ const auto key2 = make_key("", "2", 0);
+
+ Handle j1;
+ {
+ auto h1 = cache->get(key1, std::nullopt);
+ j1 = std::move(h1); // assign over empty handle
+ EXPECT_EQ(key1, j1->key.first);
+ }
+ auto h2 = cache->get(key2, std::nullopt);
+ EXPECT_EQ(key1, j1->key.first); // j1 stays pinned
+}
+
+TEST(BucketSyncCache, HandleMoveAssignExisting)
+{
+ const auto key1 = make_key("", "1", 0);
+ const auto key2 = make_key("", "2", 0);
+
+ Handle h1;
+ {
+ auto cache1 = Cache::create(0);
+ h1 = cache1->get(key1, std::nullopt);
+ } // j1 has the last ref to cache1
+ {
+ auto cache2 = Cache::create(0);
+ auto h2 = cache2->get(key2, std::nullopt);
+ h1 = std::move(h2); // assign over existing handle
+ }
+ EXPECT_EQ(key2, h1->key.first);
+}
+
+TEST(BucketSyncCache, HandleCopyAssignEmpty)
+{
+ auto cache = Cache::create(0);
+
+ const auto key1 = make_key("", "1", 0);
+ const auto key2 = make_key("", "2", 0);
+
+ Handle j1;
+ {
+ auto h1 = cache->get(key1, std::nullopt);
+ j1 = h1; // assign over empty handle
+ EXPECT_EQ(&*h1, &*j1);
+ }
+ auto h2 = cache->get(key2, std::nullopt);
+ EXPECT_EQ(key1, j1->key.first); // j1 stays pinned
+}
+
+TEST(BucketSyncCache, HandleCopyAssignExisting)
+{
+ const auto key1 = make_key("", "1", 0);
+ const auto key2 = make_key("", "2", 0);
+
+ Handle h1;
+ {
+ auto cache1 = Cache::create(0);
+ h1 = cache1->get(key1, std::nullopt);
+ } // j1 has the last ref to cache1
+ {
+ auto cache2 = Cache::create(0);
+ auto h2 = cache2->get(key2, std::nullopt);
+ h1 = h2; // assign over existing handle
+ EXPECT_EQ(&*h1, &*h2);
+ }
+ EXPECT_EQ(key2, h1->key.first);
+}
diff --git a/src/test/rgw/test_rgw_common.cc b/src/test/rgw/test_rgw_common.cc
new file mode 100644
index 000000000..731d624e2
--- /dev/null
+++ b/src/test/rgw/test_rgw_common.cc
@@ -0,0 +1,91 @@
+#include "test_rgw_common.h"
+
+void test_rgw_add_placement(RGWZoneGroup *zonegroup, RGWZoneParams *zone_params, const std::string& name, bool is_default)
+{
+ zonegroup->placement_targets[name] = { name };
+
+ RGWZonePlacementInfo& pinfo = zone_params->placement_pools[name];
+ pinfo.index_pool = rgw_pool(name + ".index").to_str();
+
+ rgw_pool data_pool(name + ".data");
+ pinfo.storage_classes.set_storage_class(RGW_STORAGE_CLASS_STANDARD, &data_pool, nullptr);
+ pinfo.data_extra_pool = rgw_pool(name + ".extra").to_str();
+
+ if (is_default) {
+ zonegroup->default_placement = rgw_placement_rule(name, RGW_STORAGE_CLASS_STANDARD);
+ }
+}
+
+void test_rgw_init_env(RGWZoneGroup *zonegroup, RGWZoneParams *zone_params)
+{
+ test_rgw_add_placement(zonegroup, zone_params, "default-placement", true);
+
+}
+
+void test_rgw_populate_explicit_placement_bucket(rgw_bucket *b, const char *t, const char *n, const char *dp, const char *ip, const char *m, const char *id)
+{
+ b->tenant = t;
+ b->name = n;
+ b->marker = m;
+ b->bucket_id = id;
+ b->explicit_placement.data_pool = rgw_pool(dp);
+ b->explicit_placement.index_pool = rgw_pool(ip);
+}
+
+void test_rgw_populate_old_bucket(old_rgw_bucket *b, const char *t, const char *n, const char *dp, const char *ip, const char *m, const char *id)
+{
+ b->tenant = t;
+ b->name = n;
+ b->marker = m;
+ b->bucket_id = id;
+ b->data_pool = dp;
+ b->index_pool = ip;
+}
+
+std::string test_rgw_get_obj_oid(const rgw_obj& obj)
+{
+ std::string oid;
+ std::string loc;
+
+ get_obj_bucket_and_oid_loc(obj, oid, loc);
+ return oid;
+}
+
+void test_rgw_init_explicit_placement_bucket(rgw_bucket *bucket, const char *name)
+{
+ test_rgw_populate_explicit_placement_bucket(bucket, "", name, ".data-pool", ".index-pool", "marker", "bucket-id");
+}
+
+void test_rgw_init_old_bucket(old_rgw_bucket *bucket, const char *name)
+{
+ test_rgw_populate_old_bucket(bucket, "", name, ".data-pool", ".index-pool", "marker", "bucket-id");
+}
+
+void test_rgw_populate_bucket(rgw_bucket *b, const char *t, const char *n, const char *m, const char *id)
+{
+ b->tenant = t;
+ b->name = n;
+ b->marker = m;
+ b->bucket_id = id;
+}
+
+void test_rgw_init_bucket(rgw_bucket *bucket, const char *name)
+{
+ test_rgw_populate_bucket(bucket, "", name, "marker", "bucket-id");
+}
+
+rgw_obj test_rgw_create_obj(const rgw_bucket& bucket, const std::string& name, const std::string& instance, const std::string& ns)
+{
+ rgw_obj obj(bucket, name);
+ if (!instance.empty()) {
+ obj.key.set_instance(instance);
+ }
+ if (!ns.empty()) {
+ obj.key.ns = ns;
+ }
+ obj.bucket = bucket;
+
+ return obj;
+}
+
+
diff --git a/src/test/rgw/test_rgw_common.h b/src/test/rgw/test_rgw_common.h
new file mode 100644
index 000000000..664e0b22e
--- /dev/null
+++ b/src/test/rgw/test_rgw_common.h
@@ -0,0 +1,506 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include "common/ceph_json.h"
+#include "common/Formatter.h"
+#include "rgw_common.h"
+#include "rgw_rados.h"
+#include "rgw_zone.h"
+
+#ifndef CEPH_TEST_RGW_COMMON_H
+#define CEPH_TEST_RGW_COMMON_H
+
+struct old_rgw_bucket {
+ std::string tenant;
+ std::string name;
+ std::string data_pool;
+ std::string data_extra_pool; /* if not set, then we should use data_pool instead */
+ std::string index_pool;
+ std::string marker;
+ std::string bucket_id;
+
+ std::string oid; /*
+ * runtime in-memory only info. If not empty, points to the bucket instance object
+ */
+
+ old_rgw_bucket() { }
+ // cppcheck-suppress noExplicitConstructor
+ old_rgw_bucket(const std::string& s) : name(s) {
+ data_pool = index_pool = s;
+ marker = "";
+ }
+ explicit old_rgw_bucket(const char *n) : name(n) {
+ data_pool = index_pool = n;
+ marker = "";
+ }
+ old_rgw_bucket(const char *t, const char *n, const char *dp, const char *ip, const char *m, const char *id, const char *h) :
+ tenant(t), name(n), data_pool(dp), index_pool(ip), marker(m), bucket_id(id) {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(8, 3, bl);
+ encode(name, bl);
+ encode(data_pool, bl);
+ encode(marker, bl);
+ encode(bucket_id, bl);
+ encode(index_pool, bl);
+ encode(data_extra_pool, bl);
+ encode(tenant, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(8, 3, 3, bl);
+ decode(name, bl);
+ decode(data_pool, bl);
+ if (struct_v >= 2) {
+ decode(marker, bl);
+ if (struct_v <= 3) {
+ uint64_t id;
+ decode(id, bl);
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%llu", (long long)id);
+ bucket_id = buf;
+ } else {
+ decode(bucket_id, bl);
+ }
+ }
+ if (struct_v >= 5) {
+ decode(index_pool, bl);
+ } else {
+ index_pool = data_pool;
+ }
+ if (struct_v >= 7) {
+ decode(data_extra_pool, bl);
+ }
+ if (struct_v >= 8) {
+ decode(tenant, bl);
+ }
+ DECODE_FINISH(bl);
+ }
+
+ // format a key for the bucket/instance. pass delim=0 to skip a field
+ std::string get_key(char tenant_delim = '/',
+ char id_delim = ':') const;
+
+ const std::string& get_data_extra_pool() {
+ if (data_extra_pool.empty()) {
+ return data_pool;
+ }
+ return data_extra_pool;
+ }
+
+ void dump(Formatter *f) const;
+ void decode_json(JSONObj *obj);
+ static void generate_test_instances(std::list<old_rgw_bucket*>& o);
+
+ bool operator<(const old_rgw_bucket& b) const {
+ return name.compare(b.name) < 0;
+ }
+};
+WRITE_CLASS_ENCODER(old_rgw_bucket)
+
+class old_rgw_obj {
+ std::string orig_obj;
+ std::string loc;
+ std::string object;
+ std::string instance;
+public:
+ const std::string& get_object() const { return object; }
+ const std::string& get_orig_obj() const { return orig_obj; }
+ const std::string& get_loc() const { return loc; }
+ const std::string& get_instance() const { return instance; }
+ old_rgw_bucket bucket;
+ std::string ns;
+
+ bool in_extra_data; /* in-memory only member, does not serialize */
+
+ // Represents the hash index source for this object once it is set (non-empty)
+ std::string index_hash_source;
+
+ old_rgw_obj() : in_extra_data(false) {}
+ old_rgw_obj(old_rgw_bucket& b, const std::string& o) : in_extra_data(false) {
+ init(b, o);
+ }
+ old_rgw_obj(old_rgw_bucket& b, const rgw_obj_key& k) : in_extra_data(false) {
+ from_index_key(b, k);
+ }
+ void init(old_rgw_bucket& b, const std::string& o) {
+ bucket = b;
+ set_obj(o);
+ reset_loc();
+ }
+ void init_ns(old_rgw_bucket& b, const std::string& o, const std::string& n) {
+ bucket = b;
+ set_ns(n);
+ set_obj(o);
+ reset_loc();
+ }
+ int set_ns(const char *n) {
+ if (!n)
+ return -EINVAL;
+ std::string ns_str(n);
+ return set_ns(ns_str);
+ }
+ int set_ns(const std::string& n) {
+ if (n[0] == '_')
+ return -EINVAL;
+ ns = n;
+ set_obj(orig_obj);
+ return 0;
+ }
+ int set_instance(const std::string& i) {
+ if (i[0] == '_')
+ return -EINVAL;
+ instance = i;
+ set_obj(orig_obj);
+ return 0;
+ }
+
+ int clear_instance() {
+ return set_instance(std::string());
+ }
+
+ void set_loc(const std::string& k) {
+ loc = k;
+ }
+
+ void reset_loc() {
+ loc.clear();
+ /*
+ * For backward compatibility. Older versions used to have object locator on all objects,
+ * however, the orig_obj was the effective object locator. This had the same effect as not
+ * having object locator at all for most objects but the ones that started with underscore as
+ * these were escaped.
+ */
+ if (orig_obj[0] == '_' && ns.empty()) {
+ loc = orig_obj;
+ }
+ }
+
+ bool have_null_instance() {
+ return instance == "null";
+ }
+
+ bool have_instance() {
+ return !instance.empty();
+ }
+
+ bool need_to_encode_instance() {
+ return have_instance() && !have_null_instance();
+ }
+
+ void set_obj(const std::string& o) {
+ object.reserve(128);
+
+ orig_obj = o;
+ if (ns.empty() && !need_to_encode_instance()) {
+ if (o.empty()) {
+ return;
+ }
+ if (o.size() < 1 || o[0] != '_') {
+ object = o;
+ return;
+ }
+ object = "_";
+ object.append(o);
+ } else {
+ object = "_";
+ object.append(ns);
+ if (need_to_encode_instance()) {
+ object.append(std::string(":") + instance);
+ }
+ object.append("_");
+ object.append(o);
+ }
+ reset_loc();
+ }
+
+ /*
+ * get the object's key name as being referred to by the bucket index.
+ */
+ std::string get_index_key_name() const {
+ if (ns.empty()) {
+ if (orig_obj.size() < 1 || orig_obj[0] != '_') {
+ return orig_obj;
+ }
+ return std::string("_") + orig_obj;
+ };
+
+ char buf[ns.size() + 16];
+ snprintf(buf, sizeof(buf), "_%s_", ns.c_str());
+ return std::string(buf) + orig_obj;
+ };
+
+ void from_index_key(old_rgw_bucket& b, const rgw_obj_key& key) {
+ if (key.name[0] != '_') {
+ init(b, key.name);
+ set_instance(key.instance);
+ return;
+ }
+ if (key.name[1] == '_') {
+ init(b, key.name.substr(1));
+ set_instance(key.instance);
+ return;
+ }
+ ssize_t pos = key.name.find('_', 1);
+ if (pos < 0) {
+ /* shouldn't happen, just use key */
+ init(b, key.name);
+ set_instance(key.instance);
+ return;
+ }
+
+ init_ns(b, key.name.substr(pos + 1), key.name.substr(1, pos -1));
+ set_instance(key.instance);
+ }
+
+ void get_index_key(rgw_obj_key *key) const {
+ key->name = get_index_key_name();
+ key->instance = instance;
+ }
+
+ static void parse_ns_field(std::string& ns, std::string& instance) {
+ int pos = ns.find(':');
+ if (pos >= 0) {
+ instance = ns.substr(pos + 1);
+ ns = ns.substr(0, pos);
+ } else {
+ instance.clear();
+ }
+ }
+
+ std::string& get_hash_object() {
+ return index_hash_source.empty() ? orig_obj : index_hash_source;
+ }
+ /**
+ * Translate a namespace-mangled object name to the user-facing name
+ * existing in the given namespace.
+ *
+ * If the object is part of the given namespace, it returns true
+ * and cuts down the name to the unmangled version. If it is not
+ * part of the given namespace, it returns false.
+ */
+ static bool translate_raw_obj_to_obj_in_ns(std::string& obj, std::string& instance, std::string& ns) {
+ if (obj[0] != '_') {
+ if (ns.empty()) {
+ return true;
+ }
+ return false;
+ }
+
+ std::string obj_ns;
+ bool ret = parse_raw_oid(obj, &obj, &instance, &obj_ns);
+ if (!ret) {
+ return ret;
+ }
+
+ return (ns == obj_ns);
+ }
+
+ static bool parse_raw_oid(const std::string& oid, std::string *obj_name, std::string *obj_instance, std::string *obj_ns) {
+ obj_instance->clear();
+ obj_ns->clear();
+ if (oid[0] != '_') {
+ *obj_name = oid;
+ return true;
+ }
+
+ if (oid.size() >= 2 && oid[1] == '_') {
+ *obj_name = oid.substr(1);
+ return true;
+ }
+
+ if (oid[0] != '_' || oid.size() < 3) // for namespace, min size would be 3: _x_
+ return false;
+
+ int pos = oid.find('_', 1);
+ if (pos <= 1) // if it starts with __, it's not in our namespace
+ return false;
+
+ *obj_ns = oid.substr(1, pos - 1);
+ parse_ns_field(*obj_ns, *obj_instance);
+
+ *obj_name = oid.substr(pos + 1);
+ return true;
+ }
+
+ /**
+ * Given a mangled object name and an empty namespace string, this
+ * function extracts the namespace into the string and sets the object
+ * name to be the unmangled version.
+ *
+ * It returns true after successfully doing so, or
+ * false if it fails.
+ */
+ static bool strip_namespace_from_object(std::string& obj, std::string& ns, std::string& instance) {
+ ns.clear();
+ instance.clear();
+ if (obj[0] != '_') {
+ return true;
+ }
+
+ size_t pos = obj.find('_', 1);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ if (obj[1] == '_') {
+ obj = obj.substr(1);
+ return true;
+ }
+
+ size_t period_pos = obj.find('.');
+ if (period_pos < pos) {
+ return false;
+ }
+
+ ns = obj.substr(1, pos-1);
+ obj = obj.substr(pos+1, std::string::npos);
+
+ parse_ns_field(ns, instance);
+ return true;
+ }
+
+ void set_in_extra_data(bool val) {
+ in_extra_data = val;
+ }
+
+ bool is_in_extra_data() const {
+ return in_extra_data;
+ }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(5, 3, bl);
+ encode(bucket.name, bl);
+ encode(loc, bl);
+ encode(ns, bl);
+ encode(object, bl);
+ encode(bucket, bl);
+ encode(instance, bl);
+ if (!ns.empty() || !instance.empty()) {
+ encode(orig_obj, bl);
+ }
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(5, 3, 3, bl);
+ decode(bucket.name, bl);
+ decode(loc, bl);
+ decode(ns, bl);
+ decode(object, bl);
+ if (struct_v >= 2)
+ decode(bucket, bl);
+ if (struct_v >= 4)
+ decode(instance, bl);
+ if (ns.empty() && instance.empty()) {
+ if (object[0] != '_') {
+ orig_obj = object;
+ } else {
+ orig_obj = object.substr(1);
+ }
+ } else {
+ if (struct_v >= 5) {
+ decode(orig_obj, bl);
+ } else {
+ ssize_t pos = object.find('_', 1);
+ if (pos < 0) {
+ throw buffer::malformed_input();
+ }
+ orig_obj = object.substr(pos);
+ }
+ }
+ DECODE_FINISH(bl);
+ }
+
+ bool operator==(const old_rgw_obj& o) const {
+ return (object.compare(o.object) == 0) &&
+ (bucket.name.compare(o.bucket.name) == 0) &&
+ (ns.compare(o.ns) == 0) &&
+ (instance.compare(o.instance) == 0);
+ }
+ bool operator<(const old_rgw_obj& o) const {
+ int r = bucket.name.compare(o.bucket.name);
+ if (r == 0) {
+ r = bucket.bucket_id.compare(o.bucket.bucket_id);
+ if (r == 0) {
+ r = object.compare(o.object);
+ if (r == 0) {
+ r = ns.compare(o.ns);
+ if (r == 0) {
+ r = instance.compare(o.instance);
+ }
+ }
+ }
+ }
+
+ return (r < 0);
+ }
+};
+WRITE_CLASS_ENCODER(old_rgw_obj)
+
+static inline void prepend_old_bucket_marker(const old_rgw_bucket& bucket, const std::string& orig_oid, std::string& oid)
+{
+ if (bucket.marker.empty() || orig_oid.empty()) {
+ oid = orig_oid;
+ } else {
+ oid = bucket.marker;
+ oid.append("_");
+ oid.append(orig_oid);
+ }
+}
+
+void test_rgw_init_env(RGWZoneGroup *zonegroup, RGWZoneParams *zone_params);
+
+struct test_rgw_env {
+ RGWZoneGroup zonegroup;
+ RGWZoneParams zone_params;
+ rgw_data_placement_target default_placement;
+
+ test_rgw_env() {
+ test_rgw_init_env(&zonegroup, &zone_params);
+ default_placement.data_pool = rgw_pool(zone_params.placement_pools[zonegroup.default_placement.name].get_standard_data_pool());
+ default_placement.data_extra_pool = rgw_pool(zone_params.placement_pools[zonegroup.default_placement.name].data_extra_pool);
+ }
+
+ rgw_data_placement_target get_placement(const std::string& placement_id) {
+ const RGWZonePlacementInfo& pi = zone_params.placement_pools[placement_id];
+ rgw_data_placement_target pt;
+ pt.index_pool = pi.index_pool;
+ pt.data_pool = pi.get_standard_data_pool();
+ pt.data_extra_pool = pi.data_extra_pool;
+ return pt;
+ }
+
+ rgw_raw_obj get_raw(const rgw_obj& obj) {
+ rgw_obj_select s(obj);
+ return s.get_raw_obj(zonegroup, zone_params);
+ }
+
+ rgw_raw_obj get_raw(const rgw_obj_select& os) {
+ return os.get_raw_obj(zonegroup, zone_params);
+ }
+};
+
+void test_rgw_add_placement(RGWZoneGroup *zonegroup, RGWZoneParams *zone_params, const std::string& name, bool is_default);
+void test_rgw_populate_explicit_placement_bucket(rgw_bucket *b, const char *t, const char *n, const char *dp, const char *ip, const char *m, const char *id);
+void test_rgw_populate_old_bucket(old_rgw_bucket *b, const char *t, const char *n, const char *dp, const char *ip, const char *m, const char *id);
+
+std::string test_rgw_get_obj_oid(const rgw_obj& obj);
+void test_rgw_init_explicit_placement_bucket(rgw_bucket *bucket, const char *name);
+void test_rgw_init_old_bucket(old_rgw_bucket *bucket, const char *name);
+void test_rgw_populate_bucket(rgw_bucket *b, const char *t, const char *n, const char *m, const char *id);
+void test_rgw_init_bucket(rgw_bucket *bucket, const char *name);
+rgw_obj test_rgw_create_obj(const rgw_bucket& bucket, const std::string& name, const std::string& instance, const std::string& ns);
+
+#endif
+
diff --git a/src/test/rgw/test_rgw_compression.cc b/src/test/rgw/test_rgw_compression.cc
new file mode 100644
index 000000000..a34653530
--- /dev/null
+++ b/src/test/rgw/test_rgw_compression.cc
@@ -0,0 +1,186 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "rgw_compression.h"
+
+class ut_get_sink : public RGWGetObj_Filter {
+ bufferlist sink;
+public:
+ ut_get_sink() {}
+ virtual ~ut_get_sink() {}
+
+ int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override
+ {
+ auto& bl_buffers = bl.buffers();
+ auto i = bl_buffers.begin();
+ while (bl_len > 0)
+ {
+ ceph_assert(i != bl_buffers.end());
+ off_t len = std::min<off_t>(bl_len, i->length());
+ sink.append(*i, 0, len);
+ bl_len -= len;
+ i++;
+ }
+ return 0;
+ }
+ bufferlist& get_sink()
+ {
+ return sink;
+ }
+};
+
+class ut_get_sink_size : public RGWGetObj_Filter {
+ size_t max_size = 0;
+public:
+ ut_get_sink_size() {}
+ virtual ~ut_get_sink_size() {}
+
+ int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override
+ {
+ if (bl_len > (off_t)max_size)
+ max_size = bl_len;
+ return 0;
+ }
+ size_t get_size()
+ {
+ return max_size;
+ }
+};
+
+class ut_put_sink: public rgw::sal::DataProcessor
+{
+ bufferlist sink;
+public:
+ int process(bufferlist&& bl, uint64_t ofs) override
+ {
+ sink.claim_append(bl);
+ return 0;
+ }
+ bufferlist& get_sink()
+ {
+ return sink;
+ }
+};
+
+
+struct MockGetDataCB : public RGWGetObj_Filter {
+ int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override {
+ return 0;
+ }
+} cb;
+
+using range_t = std::pair<off_t, off_t>;
+
+// call filter->fixup_range() and return the range as a pair. this makes it easy
+// to fit on a single line for ASSERT_EQ()
+range_t fixup_range(RGWGetObj_Decompress *filter, off_t ofs, off_t end)
+{
+ filter->fixup_range(ofs, end);
+ return {ofs, end};
+}
+
+
+TEST(Decompress, FixupRangePartial)
+{
+ RGWCompressionInfo cs_info;
+
+ // array of blocks with original len=8, compressed to len=6
+ auto& blocks = cs_info.blocks;
+ blocks.emplace_back(compression_block{0, 0, 6});
+ blocks.emplace_back(compression_block{8, 6, 6});
+ blocks.emplace_back(compression_block{16, 12, 6});
+ blocks.emplace_back(compression_block{24, 18, 6});
+
+ const bool partial = true;
+ RGWGetObj_Decompress decompress(g_ceph_context, &cs_info, partial, &cb);
+
+ // test translation from logical ranges to compressed ranges
+ ASSERT_EQ(range_t(0, 5), fixup_range(&decompress, 0, 1));
+ ASSERT_EQ(range_t(0, 5), fixup_range(&decompress, 1, 7));
+ ASSERT_EQ(range_t(0, 11), fixup_range(&decompress, 7, 8));
+ ASSERT_EQ(range_t(0, 11), fixup_range(&decompress, 0, 9));
+ ASSERT_EQ(range_t(0, 11), fixup_range(&decompress, 7, 9));
+ ASSERT_EQ(range_t(6, 11), fixup_range(&decompress, 8, 9));
+ ASSERT_EQ(range_t(6, 17), fixup_range(&decompress, 8, 16));
+ ASSERT_EQ(range_t(6, 17), fixup_range(&decompress, 8, 17));
+ ASSERT_EQ(range_t(12, 23), fixup_range(&decompress, 16, 24));
+ ASSERT_EQ(range_t(12, 23), fixup_range(&decompress, 16, 999));
+ ASSERT_EQ(range_t(18, 23), fixup_range(&decompress, 998, 999));
+}
+
+TEST(Compress, LimitedChunkSize)
+{
+ CompressorRef plugin;
+ plugin = Compressor::create(g_ceph_context, Compressor::COMP_ALG_ZLIB);
+ ASSERT_NE(plugin.get(), nullptr);
+
+ for (size_t s = 100 ; s < 10000000 ; s = s*5/4)
+ {
+ bufferptr bp(s);
+ bufferlist bl;
+ bl.append(bp);
+
+ ut_put_sink c_sink;
+ RGWPutObj_Compress compressor(g_ceph_context, plugin, &c_sink);
+ compressor.process(std::move(bl), 0);
+ compressor.process({}, s); // flush
+
+ RGWCompressionInfo cs_info;
+ cs_info.compression_type = plugin->get_type_name();
+ cs_info.orig_size = s;
+ cs_info.compressor_message = compressor.get_compressor_message();
+ cs_info.blocks = move(compressor.get_compression_blocks());
+
+ ut_get_sink_size d_sink;
+ RGWGetObj_Decompress decompress(g_ceph_context, &cs_info, false, &d_sink);
+
+ off_t f_begin = 0;
+ off_t f_end = s - 1;
+ decompress.fixup_range(f_begin, f_end);
+
+ decompress.handle_data(c_sink.get_sink(), 0, c_sink.get_sink().length());
+ bufferlist empty;
+ decompress.handle_data(empty, 0, 0);
+
+ ASSERT_LE(d_sink.get_size(), (size_t)g_ceph_context->_conf->rgw_max_chunk_size);
+ }
+}
+
+
+TEST(Compress, BillionZeros)
+{
+ CompressorRef plugin;
+ ut_put_sink c_sink;
+ plugin = Compressor::create(g_ceph_context, Compressor::COMP_ALG_ZLIB);
+ ASSERT_NE(plugin.get(), nullptr);
+ RGWPutObj_Compress compressor(g_ceph_context, plugin, &c_sink);
+
+ constexpr size_t size = 1000000;
+ bufferptr bp(size);
+ bufferlist bl;
+ bl.append(bp);
+
+ for (int i=0; i<1000;i++)
+ compressor.process(bufferlist{bl}, size*i);
+ compressor.process({}, size*1000); // flush
+
+ RGWCompressionInfo cs_info;
+ cs_info.compression_type = plugin->get_type_name();
+ cs_info.orig_size = size*1000;
+ cs_info.compressor_message = compressor.get_compressor_message();
+ cs_info.blocks = move(compressor.get_compression_blocks());
+
+ ut_get_sink d_sink;
+ RGWGetObj_Decompress decompress(g_ceph_context, &cs_info, false, &d_sink);
+
+ off_t f_begin = 0;
+ off_t f_end = size*1000 - 1;
+ decompress.fixup_range(f_begin, f_end);
+
+ decompress.handle_data(c_sink.get_sink(), 0, c_sink.get_sink().length());
+ bufferlist empty;
+ decompress.handle_data(empty, 0, 0);
+
+ ASSERT_EQ(d_sink.get_sink().length() , size*1000);
+}
diff --git a/src/test/rgw/test_rgw_crypto.cc b/src/test/rgw/test_rgw_crypto.cc
new file mode 100644
index 000000000..b85c78eb2
--- /dev/null
+++ b/src/test/rgw/test_rgw_crypto.cc
@@ -0,0 +1,816 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Mirantis <akupczyk@mirantis.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "rgw_common.h"
+#include "rgw_rados.h"
+#include "rgw_crypt.h"
+#include <gtest/gtest.h>
+#include "include/ceph_assert.h"
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+
+std::unique_ptr<BlockCrypt> AES_256_CBC_create(const DoutPrefixProvider *dpp, CephContext* cct, const uint8_t* key, size_t len);
+
+class ut_get_sink : public RGWGetObj_Filter {
+ std::stringstream sink;
+public:
+ ut_get_sink() {}
+ virtual ~ut_get_sink() {}
+
+ int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override
+ {
+ sink << std::string_view(bl.c_str()+bl_ofs, bl_len);
+ return 0;
+ }
+ std::string get_sink()
+ {
+ return sink.str();
+ }
+};
+
+class ut_put_sink: public rgw::sal::DataProcessor
+{
+ std::stringstream sink;
+public:
+ int process(bufferlist&& bl, uint64_t ofs) override
+ {
+ sink << std::string_view(bl.c_str(),bl.length());
+ return 0;
+ }
+ std::string get_sink()
+ {
+ return sink.str();
+ }
+};
+
+
+class BlockCryptNone: public BlockCrypt {
+ size_t block_size = 256;
+public:
+ BlockCryptNone(){};
+ BlockCryptNone(size_t sz) : block_size(sz) {}
+ virtual ~BlockCryptNone(){};
+ size_t get_block_size() override
+ {
+ return block_size;
+ }
+ bool encrypt(bufferlist& input,
+ off_t in_ofs,
+ size_t size,
+ bufferlist& output,
+ off_t stream_offset) override
+ {
+ output.clear();
+ output.append(input.c_str(), input.length());
+ return true;
+ }
+ bool decrypt(bufferlist& input,
+ off_t in_ofs,
+ size_t size,
+ bufferlist& output,
+ off_t stream_offset) override
+ {
+ output.clear();
+ output.append(input.c_str(), input.length());
+ return true;
+ }
+};
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0u);
+
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = (r*r*r*r*r % test_range);
+ begin = begin - begin % block_size;
+ off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
+ if (r % 3)
+ end = end - end % block_size;
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0u);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0u);
+
+ bufferlist encrypted;
+ ASSERT_TRUE(aes->encrypt(input, begin, end - begin, encrypted, offset));
+ bufferlist decrypted;
+ ASSERT_TRUE(aes->decrypt(encrypted, 0, end - begin, decrypted, offset));
+
+ ASSERT_EQ(decrypted.length(), end - begin);
+ ASSERT_EQ(std::string_view(input.c_str() + begin, end - begin),
+ std::string_view(decrypted.c_str(), end - begin) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0u);
+
+ for (off_t end = 1; end < 6096 ; end+=3)
+ {
+ off_t begin = 0;
+ off_t offset = end*end*end*end*end % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0u);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0u);
+
+ bufferlist encrypted;
+ ASSERT_TRUE(aes->encrypt(input, begin, end, encrypted, offset));
+ bufferlist decrypted;
+ ASSERT_TRUE(aes->decrypt(encrypted, 0, end, decrypted, offset));
+
+ ASSERT_EQ(decrypted.length(), end);
+ ASSERT_EQ(std::string_view(input.c_str(), end),
+ std::string_view(decrypted.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0u);
+ size_t rr = 111;
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = 0;
+ off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
+ //sometimes make aligned
+ if (r % 3)
+ end = end - end % block_size;
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0u);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0u);
+
+ bufferlist encrypted1;
+ bufferlist encrypted2;
+
+ off_t pos = begin;
+ off_t chunk;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted1.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+
+ pos = begin;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted2.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+ ASSERT_EQ(encrypted1.length(), end);
+ ASSERT_EQ(encrypted2.length(), end);
+ ASSERT_EQ(std::string_view(encrypted1.c_str(), end),
+ std::string_view(encrypted2.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0u);
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = 0;
+ off_t end = begin + r*r*r*r*r*r*r % (16);
+
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0u);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0u);
+
+ bufferlist encrypted;
+ bufferlist decrypted;
+ ASSERT_TRUE(aes->encrypt(input, 0, end, encrypted, offset));
+ ASSERT_TRUE(aes->encrypt(encrypted, 0, end, decrypted, offset));
+ ASSERT_EQ(encrypted.length(), end);
+ ASSERT_EQ(decrypted.length(), end);
+ ASSERT_EQ(std::string_view(input.c_str(), end),
+ std::string_view(decrypted.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0u);
+ size_t rr = 111;
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = 0;
+ off_t end = r*r*r*r*r*r*r % (test_range - 16);
+ end = end - end % block_size;
+ end = end + (r+3)*(r+5)*(r+7) % 16;
+
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0u);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0u);
+
+ bufferlist encrypted1;
+ bufferlist encrypted2;
+
+ off_t pos = begin;
+ off_t chunk;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted1.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+ pos = begin;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted2.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+ ASSERT_EQ(encrypted1.length(), end);
+ ASSERT_EQ(encrypted2.length(), end);
+ ASSERT_EQ(std::string_view(encrypted1.c_str(), end),
+ std::string_view(encrypted2.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ bufferptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i] = i;
+
+ auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ bufferlist encrypted;
+ ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
+
+
+ for (off_t r = 93; r < 150; r++ )
+ {
+ ut_get_sink get_sink;
+ auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, std::move(cbc), {});
+
+ //random ranges
+ off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range;
+ off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1;
+
+ off_t f_begin = begin;
+ off_t f_end = end;
+ decrypt.fixup_range(f_begin, f_end);
+ decrypt.handle_data(encrypted, f_begin, f_end - f_begin + 1);
+ decrypt.flush();
+ const std::string& decrypted = get_sink.get_sink();
+ size_t expected_len = end - begin + 1;
+ ASSERT_EQ(decrypted.length(), expected_len);
+ ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len));
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ bufferptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i] = i;
+
+ auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ bufferlist encrypted;
+ ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
+
+ for (off_t r = 93; r < 150; r++ )
+ {
+ ut_get_sink get_sink;
+ auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, std::move(cbc), {});
+
+ //random
+ off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range;
+ off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1;
+
+ off_t f_begin = begin;
+ off_t f_end = end;
+ decrypt.fixup_range(f_begin, f_end);
+ off_t pos = f_begin;
+ do
+ {
+ off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16);
+ size = (pos + 1117) * (pos + 2229) % size + 1;
+ if (pos + size > f_end + 1)
+ size = f_end + 1 - pos;
+
+ decrypt.handle_data(encrypted, pos, size);
+ pos = pos + size;
+ } while (pos < f_end + 1);
+ decrypt.flush();
+
+ const std::string& decrypted = get_sink.get_sink();
+ size_t expected_len = end - begin + 1;
+ ASSERT_EQ(decrypted.length(), expected_len);
+ ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len));
+ }
+}
+
+
+using range_t = std::pair<off_t, off_t>;
+
+// call filter->fixup_range() and return the range as a pair. this makes it easy
+// to fit on a single line for ASSERT_EQ()
+range_t fixup_range(RGWGetObj_BlockDecrypt *decrypt, off_t ofs, off_t end)
+{
+ decrypt->fixup_range(ofs, end);
+ return {ofs, end};
+}
+
+TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ ut_get_sink get_sink;
+ auto nonecrypt = std::unique_ptr<BlockCrypt>(new BlockCryptNone);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink,
+ std::move(nonecrypt), {});
+ ASSERT_EQ(fixup_range(&decrypt,0,0), range_t(0,255));
+ ASSERT_EQ(fixup_range(&decrypt,1,256), range_t(0,511));
+ ASSERT_EQ(fixup_range(&decrypt,0,255), range_t(0,255));
+ ASSERT_EQ(fixup_range(&decrypt,255,256), range_t(0,511));
+ ASSERT_EQ(fixup_range(&decrypt,511,1023), range_t(256,1023));
+ ASSERT_EQ(fixup_range(&decrypt,513,1024), range_t(512,1024+255));
+}
+
+std::vector<size_t> create_mp_parts(size_t obj_size, size_t mp_part_len){
+ std::vector<size_t> parts_len;
+ size_t part_size;
+ size_t ofs=0;
+
+ while (ofs < obj_size){
+ part_size = std::min(mp_part_len, (obj_size - ofs));
+ ofs += part_size;
+ parts_len.push_back(part_size);
+ }
+ return parts_len;
+}
+
+const size_t part_size = 5*1024*1024;
+const size_t obj_size = 30*1024*1024;
+
+TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_simple)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+
+ ut_get_sink get_sink;
+ auto nonecrypt = std::make_unique<BlockCryptNone>(4096);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink,
+ std::move(nonecrypt),
+ create_mp_parts(obj_size, part_size));
+ ASSERT_EQ(fixup_range(&decrypt,0,0), range_t(0,4095));
+ ASSERT_EQ(fixup_range(&decrypt,1,4096), range_t(0,8191));
+ ASSERT_EQ(fixup_range(&decrypt,0,4095), range_t(0,4095));
+ ASSERT_EQ(fixup_range(&decrypt,4095,4096), range_t(0,8191));
+
+ // ranges are end-end inclusive, we request bytes just spanning short of first
+ // part to exceeding the first part, part_size - 1 is aligned to a 4095 boundary
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 2), range_t(0, part_size -1));
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 1), range_t(0, part_size -1));
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size), range_t(0, part_size + 4095));
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size + 1), range_t(0, part_size + 4095));
+
+ // request bytes spanning 2 parts
+ ASSERT_EQ(fixup_range(&decrypt, part_size -2, part_size + 2),
+ range_t(part_size - 4096, part_size + 4095));
+
+ // request last byte
+ ASSERT_EQ(fixup_range(&decrypt, obj_size - 1, obj_size -1),
+ range_t(obj_size - 4096, obj_size -1));
+
+}
+
+TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_non_aligned_obj_size)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+
+ const size_t na_obj_size = obj_size + 1;
+
+ ut_get_sink get_sink;
+ auto nonecrypt = std::make_unique<BlockCryptNone>(4096);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink,
+ std::move(nonecrypt),
+ create_mp_parts(na_obj_size, part_size));
+
+ // these should be unaffected here
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 2), range_t(0, part_size -1));
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 1), range_t(0, part_size -1));
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size), range_t(0, part_size + 4095));
+ ASSERT_EQ(fixup_range(&decrypt, 0, part_size + 1), range_t(0, part_size + 4095));
+
+
+ // request last 2 bytes; spanning 2 parts
+ ASSERT_EQ(fixup_range(&decrypt, na_obj_size -2 , na_obj_size -1),
+ range_t(na_obj_size - 1 - 4096, na_obj_size - 1));
+
+ // request last byte, spans last 1B part only
+ ASSERT_EQ(fixup_range(&decrypt, na_obj_size -1, na_obj_size - 1),
+ range_t(na_obj_size - 1, na_obj_size -1));
+
+}
+
+TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_non_aligned_part_size)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+
+ const size_t na_part_size = part_size + 1;
+
+ ut_get_sink get_sink;
+ auto nonecrypt = std::make_unique<BlockCryptNone>(4096);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink,
+ std::move(nonecrypt),
+ create_mp_parts(obj_size, na_part_size));
+
+ // na_part_size -2, ie. part_size -1 is aligned to 4095 boundary
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 2), range_t(0, na_part_size -2));
+ // even though na_part_size -1 should not align to a 4095 boundary, the range
+ // should not span the next part
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 1), range_t(0, na_part_size -1));
+
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size), range_t(0, na_part_size + 4095));
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size + 1), range_t(0, na_part_size + 4095));
+
+ // request spanning 2 parts
+ ASSERT_EQ(fixup_range(&decrypt, na_part_size - 2, na_part_size + 2),
+ range_t(na_part_size - 1 - 4096, na_part_size + 4095));
+
+ // request last byte, this will be interesting, since this a multipart upload
+ // with 5MB+1 size, the last part is actually 5 bytes short of 5 MB, which
+ // should be considered for the ranges alignment; an easier way to look at
+ // this will be that the last offset aligned to a 5MiB part will be 5MiB -
+ // 4095, this is a part that is 5MiB - 5 B
+ ASSERT_EQ(fixup_range(&decrypt, obj_size - 1, obj_size -1),
+ range_t(obj_size +5 -4096, obj_size -1));
+
+}
+
+TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_non_aligned)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+
+ const size_t na_part_size = part_size + 1;
+ const size_t na_obj_size = obj_size + 7; // (6*(5MiB + 1) + 1) for the last 1B overflow
+
+ ut_get_sink get_sink;
+ auto nonecrypt = std::make_unique<BlockCryptNone>(4096);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink,
+ std::move(nonecrypt),
+ create_mp_parts(na_obj_size, na_part_size));
+
+ // na_part_size -2, ie. part_size -1 is aligned to 4095 boundary
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 2), range_t(0, na_part_size -2));
+ // even though na_part_size -1 should not align to a 4095 boundary, the range
+ // should not span the next part
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 1), range_t(0, na_part_size -1));
+
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size), range_t(0, na_part_size + 4095));
+ ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size + 1), range_t(0, na_part_size + 4095));
+
+ // request last byte, spans last 1B part only
+ ASSERT_EQ(fixup_range(&decrypt, na_obj_size -1, na_obj_size - 1),
+ range_t(na_obj_size - 1, na_obj_size -1));
+
+ ASSERT_EQ(fixup_range(&decrypt, na_obj_size -2, na_obj_size -1),
+ range_t(na_obj_size - 2, na_obj_size -1));
+
+}
+
+TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_invalid_ranges)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+
+ ut_get_sink get_sink;
+ auto nonecrypt = std::make_unique<BlockCryptNone>(4096);
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink,
+ std::move(nonecrypt),
+ create_mp_parts(obj_size, part_size));
+
+
+ // the ranges below would be mostly unreachable in current code as rgw
+ // would've returned a 411 before reaching, but we're just doing this to make
+ // sure we don't have invalid access
+ ASSERT_EQ(fixup_range(&decrypt, obj_size - 1, obj_size + 100),
+ range_t(obj_size - 4096, obj_size - 1));
+ ASSERT_EQ(fixup_range(&decrypt, obj_size, obj_size + 1),
+ range_t(obj_size - 1, obj_size - 1));
+ ASSERT_EQ(fixup_range(&decrypt, obj_size+1, obj_size + 100),
+ range_t(obj_size - 1, obj_size - 1));
+
+}
+
+TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ //create some input for encryption
+ const off_t test_range = 1024*1024;
+ bufferptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i] = i;
+
+ for (off_t r = 93; r < 150; r++ )
+ {
+ ut_put_sink put_sink;
+ auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ RGWPutObj_BlockEncrypt encrypt(&no_dpp, g_ceph_context, &put_sink,
+ std::move(cbc));
+
+ off_t test_size = (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - 1) + 1;
+ off_t pos = 0;
+ do
+ {
+ off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16);
+ size = (pos + 1117) * (pos + 2229) % size + 1;
+ if (pos + size > test_size)
+ size = test_size - pos;
+
+ bufferlist bl;
+ bl.append(input.c_str()+pos, size);
+ encrypt.process(std::move(bl), pos);
+
+ pos = pos + size;
+ } while (pos < test_size);
+ encrypt.process({}, pos);
+
+ ASSERT_EQ(put_sink.get_sink().length(), static_cast<size_t>(test_size));
+
+ cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+
+ bufferlist encrypted;
+ bufferlist decrypted;
+ encrypted.append(put_sink.get_sink());
+ ASSERT_TRUE(cbc->decrypt(encrypted, 0, test_size, decrypted, 0));
+
+ ASSERT_EQ(decrypted.length(), test_size);
+ ASSERT_EQ(std::string_view(decrypted.c_str(), test_size),
+ std::string_view(input.c_str(), test_size));
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_Encrypt_Decrypt)
+{
+ const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys);
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i;
+
+ size_t fi_a = 0;
+ size_t fi_b = 1;
+ size_t test_size;
+ do
+ {
+ //fibonacci
+ size_t tmp = fi_b;
+ fi_b = fi_a + fi_b;
+ fi_a = tmp;
+
+ test_size = fi_b;
+
+ uint8_t* test_in = new uint8_t[test_size];
+ //fill with something
+ memset(test_in, test_size & 0xff, test_size);
+
+ ut_put_sink put_sink;
+ RGWPutObj_BlockEncrypt encrypt(&no_dpp, g_ceph_context, &put_sink,
+ AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32));
+ bufferlist bl;
+ bl.append((char*)test_in, test_size);
+ encrypt.process(std::move(bl), 0);
+ encrypt.process({}, test_size);
+ ASSERT_EQ(put_sink.get_sink().length(), test_size);
+
+ bl.append(put_sink.get_sink().data(), put_sink.get_sink().length());
+ ASSERT_EQ(bl.length(), test_size);
+
+ ut_get_sink get_sink;
+ RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink,
+ AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32),
+ {});
+
+ off_t bl_ofs = 0;
+ off_t bl_end = test_size - 1;
+ decrypt.fixup_range(bl_ofs, bl_end);
+ decrypt.handle_data(bl, 0, bl.length());
+ decrypt.flush();
+ ASSERT_EQ(get_sink.get_sink().length(), test_size);
+ ASSERT_EQ(get_sink.get_sink(), std::string_view((char*)test_in,test_size));
+ }
+ while (test_size < 20000);
+}
+
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
diff --git a/src/test/rgw/test_rgw_dmclock_scheduler.cc b/src/test/rgw/test_rgw_dmclock_scheduler.cc
new file mode 100644
index 000000000..92800767c
--- /dev/null
+++ b/src/test/rgw/test_rgw_dmclock_scheduler.cc
@@ -0,0 +1,428 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+//#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
+
+#include "rgw_dmclock_sync_scheduler.h"
+#include "rgw_dmclock_async_scheduler.h"
+
+#include <optional>
+#include <spawn/spawn.hpp>
+#include <gtest/gtest.h>
+#include "acconfig.h"
+#include "global/global_context.h"
+
+namespace rgw::dmclock {
+
+using boost::system::error_code;
+
+// return a lambda that can be used as a callback to capture its arguments
+auto capture(std::optional<error_code>& opt_ec,
+ std::optional<PhaseType>& opt_phase)
+{
+ return [&] (error_code ec, PhaseType phase) {
+ opt_ec = ec;
+ opt_phase = phase;
+ };
+}
+
+TEST(Queue, SyncRequest)
+{
+ ClientCounters counters(g_ceph_context);
+ auto client_info_f = [] (client_id client) -> ClientInfo* {
+ static ClientInfo clients[] = {
+ {1, 1, 1}, //admin: satisfy by reservation
+ {0, 1, 1}, //auth: satisfy by priority
+ };
+ return &clients[static_cast<size_t>(client)];
+ };
+ std::atomic <bool> ready = false;
+ auto server_ready_f = [&ready]() -> bool { return ready.load();};
+
+ SyncScheduler queue(g_ceph_context, std::ref(counters),
+ client_info_f, server_ready_f,
+ std::ref(SyncScheduler::handle_request_cb)
+ );
+
+
+ auto now = get_time();
+ ready = true;
+ queue.add_request(client_id::admin, {}, now, 1);
+ queue.add_request(client_id::auth, {}, now, 1);
+
+ // We can't see the queue at length 1 as the queue len is decremented as the
+ //request is processed
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_limit));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_cancel));
+
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_res));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_limit));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_cancel));
+}
+
+TEST(Queue, RateLimit)
+{
+ boost::asio::io_context context;
+ ClientCounters counters(g_ceph_context);
+ AsyncScheduler queue(g_ceph_context, context, std::ref(counters), nullptr,
+ [] (client_id client) -> ClientInfo* {
+ static ClientInfo clients[] = {
+ {1, 1, 1}, // admin
+ {0, 1, 1}, // auth
+ };
+ return &clients[static_cast<size_t>(client)];
+ }, AtLimit::Reject);
+
+ std::optional<error_code> ec1, ec2, ec3, ec4;
+ std::optional<PhaseType> p1, p2, p3, p4;
+
+ auto now = get_time();
+ queue.async_request(client_id::admin, {}, now, 1, capture(ec1, p1));
+ queue.async_request(client_id::admin, {}, now, 1, capture(ec2, p2));
+ queue.async_request(client_id::auth, {}, now, 1, capture(ec3, p3));
+ queue.async_request(client_id::auth, {}, now, 1, capture(ec4, p4));
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+ EXPECT_FALSE(ec3);
+ EXPECT_FALSE(ec4);
+
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
+
+ context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(p1);
+ EXPECT_EQ(PhaseType::reservation, *p1);
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::resource_unavailable_try_again, *ec2);
+
+ ASSERT_TRUE(ec3);
+ EXPECT_EQ(boost::system::errc::success, *ec3);
+ ASSERT_TRUE(p3);
+ EXPECT_EQ(PhaseType::priority, *p3);
+
+ ASSERT_TRUE(ec4);
+ EXPECT_EQ(boost::system::errc::resource_unavailable_try_again, *ec4);
+
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_prio));
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_limit));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_cancel));
+
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_res));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_prio));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_limit));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_cancel));
+}
+
+TEST(Queue, AsyncRequest)
+{
+ boost::asio::io_context context;
+ ClientCounters counters(g_ceph_context);
+ AsyncScheduler queue(g_ceph_context, context, std::ref(counters), nullptr,
+ [] (client_id client) -> ClientInfo* {
+ static ClientInfo clients[] = {
+ {1, 1, 1}, // admin: satisfy by reservation
+ {0, 1, 1}, // auth: satisfy by priority
+ };
+ return &clients[static_cast<size_t>(client)];
+ }, AtLimit::Reject
+ );
+
+ std::optional<error_code> ec1, ec2;
+ std::optional<PhaseType> p1, p2;
+
+ auto now = get_time();
+ queue.async_request(client_id::admin, {}, now, 1, capture(ec1, p1));
+ queue.async_request(client_id::auth, {}, now, 1, capture(ec2, p2));
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
+
+ context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(p1);
+ EXPECT_EQ(PhaseType::reservation, *p1);
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(p2);
+ EXPECT_EQ(PhaseType::priority, *p2);
+
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_limit));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_cancel));
+
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_res));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_limit));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_cancel));
+}
+
+
+TEST(Queue, Cancel)
+{
+ boost::asio::io_context context;
+ ClientCounters counters(g_ceph_context);
+ AsyncScheduler queue(g_ceph_context, context, std::ref(counters), nullptr,
+ [] (client_id client) -> ClientInfo* {
+ static ClientInfo info{0, 1, 1};
+ return &info;
+ });
+
+ std::optional<error_code> ec1, ec2;
+ std::optional<PhaseType> p1, p2;
+
+ auto now = get_time();
+ queue.async_request(client_id::admin, {}, now, 1, capture(ec1, p1));
+ queue.async_request(client_id::auth, {}, now, 1, capture(ec2, p2));
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
+
+ queue.cancel();
+
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec1);
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec2);
+
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_limit));
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_cancel));
+
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_limit));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_cancel));
+}
+
+TEST(Queue, CancelClient)
+{
+ boost::asio::io_context context;
+ ClientCounters counters(g_ceph_context);
+ AsyncScheduler queue(g_ceph_context, context, std::ref(counters), nullptr,
+ [] (client_id client) -> ClientInfo* {
+ static ClientInfo info{0, 1, 1};
+ return &info;
+ });
+
+ std::optional<error_code> ec1, ec2;
+ std::optional<PhaseType> p1, p2;
+
+ auto now = get_time();
+ queue.async_request(client_id::admin, {}, now, 1, capture(ec1, p1));
+ queue.async_request(client_id::auth, {}, now, 1, capture(ec2, p2));
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
+
+ queue.cancel(client_id::admin);
+
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec1);
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(p2);
+ EXPECT_EQ(PhaseType::priority, *p2);
+
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_limit));
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_cancel));
+
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_res));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_limit));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_cancel));
+}
+
+TEST(Queue, CancelOnDestructor)
+{
+ boost::asio::io_context context;
+
+ std::optional<error_code> ec1, ec2;
+ std::optional<PhaseType> p1, p2;
+
+ ClientCounters counters(g_ceph_context);
+ {
+ AsyncScheduler queue(g_ceph_context, context, std::ref(counters), nullptr,
+ [] (client_id client) -> ClientInfo* {
+ static ClientInfo info{0, 1, 1};
+ return &info;
+ });
+
+ auto now = get_time();
+ queue.async_request(client_id::admin, {}, now, 1, capture(ec1, p1));
+ queue.async_request(client_id::auth, {}, now, 1, capture(ec2, p2));
+
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
+ }
+
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec1);
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::asio::error::operation_aborted, *ec2);
+
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::admin)->get(queue_counters::l_limit));
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_cancel));
+
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_qlen));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_res));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_prio));
+ EXPECT_EQ(0u, counters(client_id::auth)->get(queue_counters::l_limit));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_cancel));
+}
+
+// return a lambda from capture() that's bound to run on the given executor
+template <typename Executor>
+auto capture(const Executor& ex, std::optional<error_code>& opt_ec,
+ std::optional<PhaseType>& opt_res)
+{
+ return boost::asio::bind_executor(ex, capture(opt_ec, opt_res));
+}
+
+TEST(Queue, CrossExecutorRequest)
+{
+ boost::asio::io_context queue_context;
+ ClientCounters counters(g_ceph_context);
+ AsyncScheduler queue(g_ceph_context, queue_context, std::ref(counters), nullptr,
+ [] (client_id client) -> ClientInfo* {
+ static ClientInfo info{0, 1, 1};
+ return &info;
+ });
+
+ // create a separate execution context to use for all callbacks to test that
+ // pending requests maintain executor work guards on both executors
+ boost::asio::io_context callback_context;
+ auto ex2 = callback_context.get_executor();
+
+ std::optional<error_code> ec1, ec2;
+ std::optional<PhaseType> p1, p2;
+
+ auto now = get_time();
+ queue.async_request(client_id::admin, {}, now, 1, capture(ex2, ec1, p1));
+ queue.async_request(client_id::auth, {}, now, 1, capture(ex2, ec2, p2));
+
+ EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
+ EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
+
+ callback_context.run_for(std::chrono::milliseconds(1));
+ // maintains work on callback executor while in queue
+ EXPECT_FALSE(callback_context.stopped());
+
+ EXPECT_FALSE(ec1);
+ EXPECT_FALSE(ec2);
+
+ queue_context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(queue_context.stopped());
+
+ EXPECT_FALSE(ec1); // no callbacks until callback executor runs
+ EXPECT_FALSE(ec2);
+
+ callback_context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(callback_context.stopped());
+
+ ASSERT_TRUE(ec1);
+ EXPECT_EQ(boost::system::errc::success, *ec1);
+ ASSERT_TRUE(p1);
+ EXPECT_EQ(PhaseType::priority, *p1);
+
+ ASSERT_TRUE(ec2);
+ EXPECT_EQ(boost::system::errc::success, *ec2);
+ ASSERT_TRUE(p2);
+ EXPECT_EQ(PhaseType::priority, *p2);
+}
+
+TEST(Queue, SpawnAsyncRequest)
+{
+ boost::asio::io_context context;
+
+ spawn::spawn(context, [&] (yield_context yield) {
+ ClientCounters counters(g_ceph_context);
+ AsyncScheduler queue(g_ceph_context, context, std::ref(counters), nullptr,
+ [] (client_id client) -> ClientInfo* {
+ static ClientInfo clients[] = {
+ {1, 1, 1}, // admin: satisfy by reservation
+ {0, 1, 1}, // auth: satisfy by priority
+ };
+ return &clients[static_cast<size_t>(client)];
+ });
+
+ error_code ec1, ec2;
+ auto p1 = queue.async_request(client_id::admin, {}, get_time(), 1, yield[ec1]);
+ EXPECT_EQ(boost::system::errc::success, ec1);
+ EXPECT_EQ(PhaseType::reservation, p1);
+
+ auto p2 = queue.async_request(client_id::auth, {}, get_time(), 1, yield[ec2]);
+ EXPECT_EQ(boost::system::errc::success, ec2);
+ EXPECT_EQ(PhaseType::priority, p2);
+ });
+
+ context.run_for(std::chrono::milliseconds(1));
+ EXPECT_TRUE(context.stopped());
+}
+
+} // namespace rgw::dmclock
diff --git a/src/test/rgw/test_rgw_gc_log.cc b/src/test/rgw/test_rgw_gc_log.cc
new file mode 100644
index 000000000..ae8c4d372
--- /dev/null
+++ b/src/test/rgw/test_rgw_gc_log.cc
@@ -0,0 +1,144 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "rgw_gc_log.h"
+
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+// creates a rados client and temporary pool
+struct RadosEnv : public ::testing::Environment {
+ static std::optional<std::string> pool_name;
+ public:
+ static std::optional<librados::Rados> rados;
+
+ void SetUp() override {
+ rados.emplace();
+ // create pool
+ std::string name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(name, *rados));
+ pool_name = name;
+ }
+ void TearDown() override {
+ if (pool_name) {
+ ASSERT_EQ(0, destroy_one_pool_pp(*pool_name, *rados));
+ }
+ rados.reset();
+ }
+
+ static int ioctx_create(librados::IoCtx& ioctx) {
+ return rados->ioctx_create(pool_name->c_str(), ioctx);
+ }
+};
+std::optional<std::string> RadosEnv::pool_name;
+std::optional<librados::Rados> RadosEnv::rados;
+
+auto *const rados_env = ::testing::AddGlobalTestEnvironment(new RadosEnv);
+
+class rgw_gc_log : public ::testing::Test {
+ protected:
+ static librados::IoCtx ioctx;
+
+ static void SetUpTestSuite() {
+ ASSERT_EQ(0, RadosEnv::ioctx_create(ioctx));
+ }
+ static void TearDownTestSuite() {
+ ioctx.close();
+ }
+
+ // use the test's name as the oid so different tests don't conflict
+ std::string get_test_oid() const {
+ return ::testing::UnitTest::GetInstance()->current_test_info()->name();
+ }
+};
+librados::IoCtx rgw_gc_log::ioctx;
+
+
+TEST_F(rgw_gc_log, init_existing_queue)
+{
+ const std::string oid = get_test_oid();
+ {
+ // successfully inits new object
+ librados::ObjectWriteOperation op;
+ gc_log_init2(op, 1, 1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ }
+ {
+ // version check fails on second init
+ librados::ObjectWriteOperation op;
+ gc_log_init2(op, 1, 1);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, &op));
+ }
+}
+
+TEST_F(rgw_gc_log, init_existing_omap)
+{
+ const std::string oid = get_test_oid();
+ {
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+ gc_log_enqueue1(op, 5, info);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ }
+ {
+ // init succeeds with existing omap entries
+ librados::ObjectWriteOperation op;
+ gc_log_init2(op, 1, 1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ }
+}
+
+TEST_F(rgw_gc_log, enqueue1_after_init)
+{
+ const std::string oid = get_test_oid();
+ {
+ librados::ObjectWriteOperation op;
+ gc_log_init2(op, 1, 1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ }
+ {
+ // version check fails on omap enqueue
+ librados::ObjectWriteOperation op;
+ cls_rgw_gc_obj_info info;
+ gc_log_enqueue1(op, 5, info);
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, &op));
+ }
+}
+
+TEST_F(rgw_gc_log, enqueue2_before_init)
+{
+ const std::string oid = get_test_oid();
+ {
+ // version check fails on cls_rgw_gc enqueue
+ librados::ObjectWriteOperation op;
+ gc_log_enqueue2(op, 5, {});
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, &op));
+ }
+}
+
+TEST_F(rgw_gc_log, defer1_after_init)
+{
+ const std::string oid = get_test_oid();
+ {
+ librados::ObjectWriteOperation op;
+ gc_log_init2(op, 1, 1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ }
+ {
+ // version check fails on omap defer
+ librados::ObjectWriteOperation op;
+ gc_log_defer1(op, 5, {});
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, &op));
+ }
+}
+
+TEST_F(rgw_gc_log, defer2_before_init)
+{
+ const std::string oid = get_test_oid();
+ {
+ // version check fails on cls_rgw_gc defer
+ librados::ObjectWriteOperation op;
+ gc_log_defer2(op, 5, {});
+ ASSERT_EQ(-ECANCELED, ioctx.operate(oid, &op));
+ }
+}
diff --git a/src/test/rgw/test_rgw_iam_policy.cc b/src/test/rgw/test_rgw_iam_policy.cc
new file mode 100644
index 000000000..f4c3c6aff
--- /dev/null
+++ b/src/test/rgw/test_rgw_iam_policy.cc
@@ -0,0 +1,1321 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <string>
+
+#include <boost/intrusive_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include <gtest/gtest.h>
+
+#include "include/stringify.h"
+#include "common/code_environment.h"
+#include "common/ceph_context.h"
+#include "global/global_init.h"
+#include "rgw_auth.h"
+#include "rgw_auth_registry.h"
+#include "rgw_iam_policy.h"
+#include "rgw_op.h"
+#include "rgw_process_env.h"
+#include "rgw_sal_rados.h"
+
+
+using std::string;
+using std::vector;
+
+using boost::container::flat_set;
+using boost::intrusive_ptr;
+using boost::make_optional;
+using boost::none;
+
+using rgw::auth::Identity;
+using rgw::auth::Principal;
+
+using rgw::ARN;
+using rgw::IAM::Effect;
+using rgw::IAM::Environment;
+using rgw::Partition;
+using rgw::IAM::Policy;
+using rgw::IAM::s3All;
+using rgw::IAM::s3Count;
+using rgw::IAM::s3GetAccelerateConfiguration;
+using rgw::IAM::s3GetBucketAcl;
+using rgw::IAM::s3GetBucketCORS;
+using rgw::IAM::s3GetBucketLocation;
+using rgw::IAM::s3GetBucketLogging;
+using rgw::IAM::s3GetBucketNotification;
+using rgw::IAM::s3GetBucketPolicy;
+using rgw::IAM::s3GetBucketPolicyStatus;
+using rgw::IAM::s3GetBucketPublicAccessBlock;
+using rgw::IAM::s3GetBucketEncryption;
+using rgw::IAM::s3GetBucketRequestPayment;
+using rgw::IAM::s3GetBucketTagging;
+using rgw::IAM::s3GetBucketVersioning;
+using rgw::IAM::s3GetBucketWebsite;
+using rgw::IAM::s3GetLifecycleConfiguration;
+using rgw::IAM::s3GetObject;
+using rgw::IAM::s3GetObjectAcl;
+using rgw::IAM::s3GetObjectVersionAcl;
+using rgw::IAM::s3GetObjectTorrent;
+using rgw::IAM::s3GetObjectTagging;
+using rgw::IAM::s3GetObjectVersion;
+using rgw::IAM::s3GetObjectVersionTagging;
+using rgw::IAM::s3GetObjectVersionTorrent;
+using rgw::IAM::s3GetPublicAccessBlock;
+using rgw::IAM::s3GetReplicationConfiguration;
+using rgw::IAM::s3ListAllMyBuckets;
+using rgw::IAM::s3ListBucket;
+using rgw::IAM::s3ListBucketMultipartUploads;
+using rgw::IAM::s3ListBucketVersions;
+using rgw::IAM::s3ListMultipartUploadParts;
+using rgw::IAM::None;
+using rgw::IAM::s3PutBucketAcl;
+using rgw::IAM::s3PutBucketPolicy;
+using rgw::IAM::s3GetBucketObjectLockConfiguration;
+using rgw::IAM::s3GetObjectRetention;
+using rgw::IAM::s3GetObjectLegalHold;
+using rgw::Service;
+using rgw::IAM::TokenID;
+using rgw::IAM::Version;
+using rgw::IAM::Action_t;
+using rgw::IAM::NotAction_t;
+using rgw::IAM::iamCreateRole;
+using rgw::IAM::iamDeleteRole;
+using rgw::IAM::iamAll;
+using rgw::IAM::stsAll;
+using rgw::IAM::allCount;
+
+class FakeIdentity : public Identity {
+ const Principal id;
+public:
+
+ explicit FakeIdentity(Principal&& id) : id(std::move(id)) {}
+ uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override {
+ ceph_abort();
+ return 0;
+ };
+
+ bool is_admin_of(const rgw_user& uid) const override {
+ ceph_abort();
+ return false;
+ }
+
+ bool is_owner_of(const rgw_user& uid) const override {
+ ceph_abort();
+ return false;
+ }
+
+ virtual uint32_t get_perm_mask() const override {
+ ceph_abort();
+ return 0;
+ }
+
+ string get_acct_name() const override {
+ abort();
+ return 0;
+ }
+
+ string get_subuser() const override {
+ abort();
+ return 0;
+ }
+
+ void to_str(std::ostream& out) const override {
+ out << id;
+ }
+
+ bool is_identity(const flat_set<Principal>& ids) const override {
+ if (id.is_wildcard() && (!ids.empty())) {
+ return true;
+ }
+ return ids.find(id) != ids.end() || ids.find(Principal::wildcard()) != ids.end();
+ }
+
+ uint32_t get_identity_type() const override {
+ return TYPE_RGW;
+ }
+};
+
+class PolicyTest : public ::testing::Test {
+protected:
+ intrusive_ptr<CephContext> cct;
+ static const string arbitrary_tenant;
+ static string example1;
+ static string example2;
+ static string example3;
+ static string example4;
+ static string example5;
+ static string example6;
+ static string example7;
+public:
+ PolicyTest() {
+ cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+ }
+};
+
+TEST_F(PolicyTest, Parse1) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example1),
+ true));
+ ASSERT_TRUE(p);
+
+ EXPECT_EQ(p->text, example1);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ EXPECT_FALSE(p->id);
+ EXPECT_FALSE(p->statements[0].sid);
+ EXPECT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 1U);
+ EXPECT_TRUE(p->statements[0].princ.empty());
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ act[s3ListBucket] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 1U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::aws);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::s3);
+ EXPECT_TRUE(p->statements[0].resource.begin()->region.empty());
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "example_bucket");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ EXPECT_TRUE(p->statements[0].conditions.empty());
+}
+
+TEST_F(PolicyTest, Eval1) {
+ auto p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example1), true);
+ Environment e;
+
+ ARN arn1(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn1),
+ Effect::Allow);
+
+ ARN arn2(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(p.eval(e, none, s3PutBucketAcl, arn2),
+ Effect::Pass);
+
+ ARN arn3(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "erroneous_bucket");
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn3),
+ Effect::Pass);
+
+}
+
+TEST_F(PolicyTest, Parse2) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example2),
+ true));
+ ASSERT_TRUE(p);
+
+ EXPECT_EQ(p->text, example2);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ EXPECT_EQ(*p->id, "S3-Account-Permissions");
+ ASSERT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 1U);
+ EXPECT_EQ(*p->statements[0].sid, "1");
+ EXPECT_FALSE(p->statements[0].princ.empty());
+ EXPECT_EQ(p->statements[0].princ.size(), 1U);
+ EXPECT_EQ(*p->statements[0].princ.begin(),
+ Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS"));
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ for (auto i = 0ULL; i < s3Count; i++)
+ act[i] = 1;
+ act[s3All] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 2U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::aws);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::s3);
+ EXPECT_TRUE(p->statements[0].resource.begin()->region.empty());
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "mybucket");
+ EXPECT_EQ((p->statements[0].resource.begin() + 1)->partition,
+ Partition::aws);
+ EXPECT_EQ((p->statements[0].resource.begin() + 1)->service,
+ Service::s3);
+ EXPECT_TRUE((p->statements[0].resource.begin() + 1)->region.empty());
+ EXPECT_EQ((p->statements[0].resource.begin() + 1)->account,
+ arbitrary_tenant);
+ EXPECT_EQ((p->statements[0].resource.begin() + 1)->resource, "mybucket/*");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ EXPECT_TRUE(p->statements[0].conditions.empty());
+}
+
+TEST_F(PolicyTest, Eval2) {
+ auto p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example2), true);
+ Environment e;
+
+ auto trueacct = FakeIdentity(
+ Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS"));
+
+ auto notacct = FakeIdentity(
+ Principal::tenant("some-other-account"));
+ for (auto i = 0ULL; i < s3Count; ++i) {
+ ARN arn1(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket");
+ EXPECT_EQ(p.eval(e, trueacct, i, arn1),
+ Effect::Allow);
+ ARN arn2(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket/myobject");
+ EXPECT_EQ(p.eval(e, trueacct, i, arn2),
+ Effect::Allow);
+ ARN arn3(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket");
+ EXPECT_EQ(p.eval(e, notacct, i, arn3),
+ Effect::Pass);
+ ARN arn4(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket/myobject");
+ EXPECT_EQ(p.eval(e, notacct, i, arn4),
+ Effect::Pass);
+ ARN arn5(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "notyourbucket");
+ EXPECT_EQ(p.eval(e, trueacct, i, arn5),
+ Effect::Pass);
+ ARN arn6(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "notyourbucket/notyourobject");
+ EXPECT_EQ(p.eval(e, trueacct, i, arn6),
+ Effect::Pass);
+
+ }
+}
+
+TEST_F(PolicyTest, Parse3) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example3), true));
+ ASSERT_TRUE(p);
+
+ EXPECT_EQ(p->text, example3);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ EXPECT_FALSE(p->id);
+ ASSERT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 3U);
+
+ EXPECT_EQ(*p->statements[0].sid, "FirstStatement");
+ EXPECT_TRUE(p->statements[0].princ.empty());
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ act[s3PutBucketPolicy] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 1U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::wildcard);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::wildcard);
+ EXPECT_EQ(p->statements[0].resource.begin()->region, "*");
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "*");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ EXPECT_TRUE(p->statements[0].conditions.empty());
+
+ EXPECT_EQ(*p->statements[1].sid, "SecondStatement");
+ EXPECT_TRUE(p->statements[1].princ.empty());
+ EXPECT_TRUE(p->statements[1].noprinc.empty());
+ EXPECT_EQ(p->statements[1].effect, Effect::Allow);
+ Action_t act1;
+ act1[s3ListAllMyBuckets] = 1;
+ EXPECT_EQ(p->statements[1].action, act1);
+ EXPECT_EQ(p->statements[1].notaction, None);
+ ASSERT_FALSE(p->statements[1].resource.empty());
+ ASSERT_EQ(p->statements[1].resource.size(), 1U);
+ EXPECT_EQ(p->statements[1].resource.begin()->partition, Partition::wildcard);
+ EXPECT_EQ(p->statements[1].resource.begin()->service, Service::wildcard);
+ EXPECT_EQ(p->statements[1].resource.begin()->region, "*");
+ EXPECT_EQ(p->statements[1].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[1].resource.begin()->resource, "*");
+ EXPECT_TRUE(p->statements[1].notresource.empty());
+ EXPECT_TRUE(p->statements[1].conditions.empty());
+
+ EXPECT_EQ(*p->statements[2].sid, "ThirdStatement");
+ EXPECT_TRUE(p->statements[2].princ.empty());
+ EXPECT_TRUE(p->statements[2].noprinc.empty());
+ EXPECT_EQ(p->statements[2].effect, Effect::Allow);
+ Action_t act2;
+ act2[s3ListMultipartUploadParts] = 1;
+ act2[s3ListBucket] = 1;
+ act2[s3ListBucketVersions] = 1;
+ act2[s3ListAllMyBuckets] = 1;
+ act2[s3ListBucketMultipartUploads] = 1;
+ act2[s3GetObject] = 1;
+ act2[s3GetObjectVersion] = 1;
+ act2[s3GetObjectAcl] = 1;
+ act2[s3GetObjectVersionAcl] = 1;
+ act2[s3GetObjectTorrent] = 1;
+ act2[s3GetObjectVersionTorrent] = 1;
+ act2[s3GetAccelerateConfiguration] = 1;
+ act2[s3GetBucketAcl] = 1;
+ act2[s3GetBucketCORS] = 1;
+ act2[s3GetBucketVersioning] = 1;
+ act2[s3GetBucketRequestPayment] = 1;
+ act2[s3GetBucketLocation] = 1;
+ act2[s3GetBucketPolicy] = 1;
+ act2[s3GetBucketNotification] = 1;
+ act2[s3GetBucketLogging] = 1;
+ act2[s3GetBucketTagging] = 1;
+ act2[s3GetBucketWebsite] = 1;
+ act2[s3GetLifecycleConfiguration] = 1;
+ act2[s3GetReplicationConfiguration] = 1;
+ act2[s3GetObjectTagging] = 1;
+ act2[s3GetObjectVersionTagging] = 1;
+ act2[s3GetBucketObjectLockConfiguration] = 1;
+ act2[s3GetObjectRetention] = 1;
+ act2[s3GetObjectLegalHold] = 1;
+ act2[s3GetBucketPolicyStatus] = 1;
+ act2[s3GetBucketPublicAccessBlock] = 1;
+ act2[s3GetPublicAccessBlock] = 1;
+ act2[s3GetBucketEncryption] = 1;
+
+ EXPECT_EQ(p->statements[2].action, act2);
+ EXPECT_EQ(p->statements[2].notaction, None);
+ ASSERT_FALSE(p->statements[2].resource.empty());
+ ASSERT_EQ(p->statements[2].resource.size(), 2U);
+ EXPECT_EQ(p->statements[2].resource.begin()->partition, Partition::aws);
+ EXPECT_EQ(p->statements[2].resource.begin()->service, Service::s3);
+ EXPECT_TRUE(p->statements[2].resource.begin()->region.empty());
+ EXPECT_EQ(p->statements[2].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[2].resource.begin()->resource, "confidential-data");
+ EXPECT_EQ((p->statements[2].resource.begin() + 1)->partition,
+ Partition::aws);
+ EXPECT_EQ((p->statements[2].resource.begin() + 1)->service, Service::s3);
+ EXPECT_TRUE((p->statements[2].resource.begin() + 1)->region.empty());
+ EXPECT_EQ((p->statements[2].resource.begin() + 1)->account,
+ arbitrary_tenant);
+ EXPECT_EQ((p->statements[2].resource.begin() + 1)->resource,
+ "confidential-data/*");
+ EXPECT_TRUE(p->statements[2].notresource.empty());
+ ASSERT_FALSE(p->statements[2].conditions.empty());
+ ASSERT_EQ(p->statements[2].conditions.size(), 1U);
+ EXPECT_EQ(p->statements[2].conditions[0].op, TokenID::Bool);
+ EXPECT_EQ(p->statements[2].conditions[0].key, "aws:MultiFactorAuthPresent");
+ EXPECT_FALSE(p->statements[2].conditions[0].ifexists);
+ ASSERT_FALSE(p->statements[2].conditions[0].vals.empty());
+ EXPECT_EQ(p->statements[2].conditions[0].vals.size(), 1U);
+ EXPECT_EQ(p->statements[2].conditions[0].vals[0], "true");
+}
+
+TEST_F(PolicyTest, Eval3) {
+ auto p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example3), true);
+ Environment em;
+ Environment tr = { { "aws:MultiFactorAuthPresent", "true" } };
+ Environment fa = { { "aws:MultiFactorAuthPresent", "false" } };
+
+ Action_t s3allow;
+ s3allow[s3ListMultipartUploadParts] = 1;
+ s3allow[s3ListBucket] = 1;
+ s3allow[s3ListBucketVersions] = 1;
+ s3allow[s3ListAllMyBuckets] = 1;
+ s3allow[s3ListBucketMultipartUploads] = 1;
+ s3allow[s3GetObject] = 1;
+ s3allow[s3GetObjectVersion] = 1;
+ s3allow[s3GetObjectAcl] = 1;
+ s3allow[s3GetObjectVersionAcl] = 1;
+ s3allow[s3GetObjectTorrent] = 1;
+ s3allow[s3GetObjectVersionTorrent] = 1;
+ s3allow[s3GetAccelerateConfiguration] = 1;
+ s3allow[s3GetBucketAcl] = 1;
+ s3allow[s3GetBucketCORS] = 1;
+ s3allow[s3GetBucketVersioning] = 1;
+ s3allow[s3GetBucketRequestPayment] = 1;
+ s3allow[s3GetBucketLocation] = 1;
+ s3allow[s3GetBucketPolicy] = 1;
+ s3allow[s3GetBucketNotification] = 1;
+ s3allow[s3GetBucketLogging] = 1;
+ s3allow[s3GetBucketTagging] = 1;
+ s3allow[s3GetBucketWebsite] = 1;
+ s3allow[s3GetLifecycleConfiguration] = 1;
+ s3allow[s3GetReplicationConfiguration] = 1;
+ s3allow[s3GetObjectTagging] = 1;
+ s3allow[s3GetObjectVersionTagging] = 1;
+ s3allow[s3GetBucketObjectLockConfiguration] = 1;
+ s3allow[s3GetObjectRetention] = 1;
+ s3allow[s3GetObjectLegalHold] = 1;
+ s3allow[s3GetBucketPolicyStatus] = 1;
+ s3allow[s3GetBucketPublicAccessBlock] = 1;
+ s3allow[s3GetPublicAccessBlock] = 1;
+ s3allow[s3GetBucketEncryption] = 1;
+
+ ARN arn1(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket");
+ EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy, arn1),
+ Effect::Allow);
+
+ ARN arn2(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket");
+ EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy, arn2),
+ Effect::Allow);
+
+
+ for (auto op = 0ULL; op < s3Count; ++op) {
+ if ((op == s3ListAllMyBuckets) || (op == s3PutBucketPolicy)) {
+ continue;
+ }
+ ARN arn3(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "confidential-data");
+ EXPECT_EQ(p.eval(em, none, op, arn3),
+ Effect::Pass);
+ ARN arn4(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "confidential-data");
+ EXPECT_EQ(p.eval(tr, none, op, arn4),
+ s3allow[op] ? Effect::Allow : Effect::Pass);
+ ARN arn5(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "confidential-data");
+ EXPECT_EQ(p.eval(fa, none, op, arn5),
+ Effect::Pass);
+ ARN arn6(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "confidential-data/moo");
+ EXPECT_EQ(p.eval(em, none, op, arn6),
+ Effect::Pass);
+ ARN arn7(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "confidential-data/moo");
+ EXPECT_EQ(p.eval(tr, none, op, arn7),
+ s3allow[op] ? Effect::Allow : Effect::Pass);
+ ARN arn8(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "confidential-data/moo");
+ EXPECT_EQ(p.eval(fa, none, op, arn8),
+ Effect::Pass);
+ ARN arn9(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "really-confidential-data");
+ EXPECT_EQ(p.eval(em, none, op, arn9),
+ Effect::Pass);
+ ARN arn10(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "really-confidential-data");
+ EXPECT_EQ(p.eval(tr, none, op, arn10),
+ Effect::Pass);
+ ARN arn11(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "really-confidential-data");
+ EXPECT_EQ(p.eval(fa, none, op, arn11),
+ Effect::Pass);
+ ARN arn12(Partition::aws, Service::s3,
+ "", arbitrary_tenant,
+ "really-confidential-data/moo");
+ EXPECT_EQ(p.eval(em, none, op, arn12), Effect::Pass);
+ ARN arn13(Partition::aws, Service::s3,
+ "", arbitrary_tenant,
+ "really-confidential-data/moo");
+ EXPECT_EQ(p.eval(tr, none, op, arn13), Effect::Pass);
+ ARN arn14(Partition::aws, Service::s3,
+ "", arbitrary_tenant,
+ "really-confidential-data/moo");
+ EXPECT_EQ(p.eval(fa, none, op, arn14), Effect::Pass);
+
+ }
+}
+
+TEST_F(PolicyTest, Parse4) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example4), true));
+ ASSERT_TRUE(p);
+
+ EXPECT_EQ(p->text, example4);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ EXPECT_FALSE(p->id);
+ EXPECT_FALSE(p->statements[0].sid);
+ EXPECT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 1U);
+ EXPECT_TRUE(p->statements[0].princ.empty());
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ act[iamCreateRole] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 1U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::wildcard);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::wildcard);
+ EXPECT_EQ(p->statements[0].resource.begin()->region, "*");
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "*");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ EXPECT_TRUE(p->statements[0].conditions.empty());
+}
+
+TEST_F(PolicyTest, Eval4) {
+ auto p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example4), true);
+ Environment e;
+
+ ARN arn1(Partition::aws, Service::iam,
+ "", arbitrary_tenant, "role/example_role");
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1),
+ Effect::Allow);
+
+ ARN arn2(Partition::aws, Service::iam,
+ "", arbitrary_tenant, "role/example_role");
+ EXPECT_EQ(p.eval(e, none, iamDeleteRole, arn2),
+ Effect::Pass);
+}
+
+TEST_F(PolicyTest, Parse5) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example5), true));
+ ASSERT_TRUE(p);
+ EXPECT_EQ(p->text, example5);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ EXPECT_FALSE(p->id);
+ EXPECT_FALSE(p->statements[0].sid);
+ EXPECT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 1U);
+ EXPECT_TRUE(p->statements[0].princ.empty());
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ for (auto i = s3All+1; i <= iamAll; i++)
+ act[i] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 1U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::aws);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::iam);
+ EXPECT_EQ(p->statements[0].resource.begin()->region, "");
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "role/example_role");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ EXPECT_TRUE(p->statements[0].conditions.empty());
+}
+
+TEST_F(PolicyTest, Eval5) {
+ auto p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example5), true);
+ Environment e;
+
+ ARN arn1(Partition::aws, Service::iam,
+ "", arbitrary_tenant, "role/example_role");
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1),
+ Effect::Allow);
+
+ ARN arn2(Partition::aws, Service::iam,
+ "", arbitrary_tenant, "role/example_role");
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn2),
+ Effect::Pass);
+
+ ARN arn3(Partition::aws, Service::iam,
+ "", "", "role/example_role");
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn3),
+ Effect::Pass);
+}
+
+TEST_F(PolicyTest, Parse6) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example6), true));
+ ASSERT_TRUE(p);
+ EXPECT_EQ(p->text, example6);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ EXPECT_FALSE(p->id);
+ EXPECT_FALSE(p->statements[0].sid);
+ EXPECT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 1U);
+ EXPECT_TRUE(p->statements[0].princ.empty());
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ for (auto i = 0U; i <= stsAll; i++)
+ act[i] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 1U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::aws);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::iam);
+ EXPECT_EQ(p->statements[0].resource.begin()->region, "");
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "user/A");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ EXPECT_TRUE(p->statements[0].conditions.empty());
+}
+
+TEST_F(PolicyTest, Eval6) {
+ auto p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example6), true);
+ Environment e;
+
+ ARN arn1(Partition::aws, Service::iam,
+ "", arbitrary_tenant, "user/A");
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1),
+ Effect::Allow);
+
+ ARN arn2(Partition::aws, Service::iam,
+ "", arbitrary_tenant, "user/A");
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn2),
+ Effect::Allow);
+}
+
+TEST_F(PolicyTest, Parse7) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example7), true));
+ ASSERT_TRUE(p);
+
+ EXPECT_EQ(p->text, example7);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ ASSERT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 1U);
+ EXPECT_FALSE(p->statements[0].princ.empty());
+ EXPECT_EQ(p->statements[0].princ.size(), 1U);
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ act[s3ListBucket] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 1U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::aws);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::s3);
+ EXPECT_TRUE(p->statements[0].resource.begin()->region.empty());
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "mybucket/*");
+ EXPECT_TRUE(p->statements[0].princ.begin()->is_user());
+ EXPECT_FALSE(p->statements[0].princ.begin()->is_wildcard());
+ EXPECT_EQ(p->statements[0].princ.begin()->get_tenant(), "");
+ EXPECT_EQ(p->statements[0].princ.begin()->get_id(), "A:subA");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ EXPECT_TRUE(p->statements[0].conditions.empty());
+}
+
+TEST_F(PolicyTest, Eval7) {
+ auto p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(example7), true);
+ Environment e;
+
+ auto subacct = FakeIdentity(
+ Principal::user(std::move(""), "A:subA"));
+ auto parentacct = FakeIdentity(
+ Principal::user(std::move(""), "A"));
+ auto sub2acct = FakeIdentity(
+ Principal::user(std::move(""), "A:sub2A"));
+
+ ARN arn1(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket/*");
+ EXPECT_EQ(p.eval(e, subacct, s3ListBucket, arn1),
+ Effect::Allow);
+
+ ARN arn2(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket/*");
+ EXPECT_EQ(p.eval(e, parentacct, s3ListBucket, arn2),
+ Effect::Pass);
+
+ ARN arn3(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "mybucket/*");
+ EXPECT_EQ(p.eval(e, sub2acct, s3ListBucket, arn3),
+ Effect::Pass);
+}
+
+const string PolicyTest::arbitrary_tenant = "arbitrary_tenant";
+string PolicyTest::example1 = R"(
+{
+ "Version": "2012-10-17",
+ "Statement": {
+ "Effect": "Allow",
+ "Action": "s3:ListBucket",
+ "Resource": "arn:aws:s3:::example_bucket"
+ }
+}
+)";
+
+string PolicyTest::example2 = R"(
+{
+ "Version": "2012-10-17",
+ "Id": "S3-Account-Permissions",
+ "Statement": [{
+ "Sid": "1",
+ "Effect": "Allow",
+ "Principal": {"AWS": ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:root"]},
+ "Action": "s3:*",
+ "Resource": [
+ "arn:aws:s3:::mybucket",
+ "arn:aws:s3:::mybucket/*"
+ ]
+ }]
+}
+)";
+
+string PolicyTest::example3 = R"(
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "FirstStatement",
+ "Effect": "Allow",
+ "Action": ["s3:PutBucketPolicy"],
+ "Resource": "*"
+ },
+ {
+ "Sid": "SecondStatement",
+ "Effect": "Allow",
+ "Action": "s3:ListAllMyBuckets",
+ "Resource": "*"
+ },
+ {
+ "Sid": "ThirdStatement",
+ "Effect": "Allow",
+ "Action": [
+ "s3:List*",
+ "s3:Get*"
+ ],
+ "Resource": [
+ "arn:aws:s3:::confidential-data",
+ "arn:aws:s3:::confidential-data/*"
+ ],
+ "Condition": {"Bool": {"aws:MultiFactorAuthPresent": "true"}}
+ }
+ ]
+}
+)";
+
+string PolicyTest::example4 = R"(
+{
+ "Version": "2012-10-17",
+ "Statement": {
+ "Effect": "Allow",
+ "Action": "iam:CreateRole",
+ "Resource": "*"
+ }
+}
+)";
+
+string PolicyTest::example5 = R"(
+{
+ "Version": "2012-10-17",
+ "Statement": {
+ "Effect": "Allow",
+ "Action": "iam:*",
+ "Resource": "arn:aws:iam:::role/example_role"
+ }
+}
+)";
+
+string PolicyTest::example6 = R"(
+{
+ "Version": "2012-10-17",
+ "Statement": {
+ "Effect": "Allow",
+ "Action": "*",
+ "Resource": "arn:aws:iam:::user/A"
+ }
+}
+)";
+
+string PolicyTest::example7 = R"(
+{
+ "Version": "2012-10-17",
+ "Statement": {
+ "Effect": "Allow",
+ "Principal": {"AWS": ["arn:aws:iam:::user/A:subA"]},
+ "Action": "s3:ListBucket",
+ "Resource": "arn:aws:s3:::mybucket/*"
+ }
+}
+)";
+class IPPolicyTest : public ::testing::Test {
+protected:
+ intrusive_ptr<CephContext> cct;
+ static const string arbitrary_tenant;
+ static string ip_address_allow_example;
+ static string ip_address_deny_example;
+ static string ip_address_full_example;
+ // 192.168.1.0/24
+ const rgw::IAM::MaskedIP allowedIPv4Range = { false, rgw::IAM::Address("11000000101010000000000100000000"), 24 };
+ // 192.168.1.1/32
+ const rgw::IAM::MaskedIP blocklistedIPv4 = { false, rgw::IAM::Address("11000000101010000000000100000001"), 32 };
+ // 2001:db8:85a3:0:0:8a2e:370:7334/128
+ const rgw::IAM::MaskedIP allowedIPv6 = { true, rgw::IAM::Address("00100000000000010000110110111000100001011010001100000000000000000000000000000000100010100010111000000011011100000111001100110100"), 128 };
+ // ::1
+ const rgw::IAM::MaskedIP blocklistedIPv6 = { true, rgw::IAM::Address(1), 128 };
+ // 2001:db8:85a3:0:0:8a2e:370:7330/124
+ const rgw::IAM::MaskedIP allowedIPv6Range = { true, rgw::IAM::Address("00100000000000010000110110111000100001011010001100000000000000000000000000000000100010100010111000000011011100000111001100110000"), 124 };
+public:
+ IPPolicyTest() {
+ cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+ }
+};
+const string IPPolicyTest::arbitrary_tenant = "arbitrary_tenant";
+
+TEST_F(IPPolicyTest, MaskedIPOperations) {
+ EXPECT_EQ(stringify(allowedIPv4Range), "192.168.1.0/24");
+ EXPECT_EQ(stringify(blocklistedIPv4), "192.168.1.1/32");
+ EXPECT_EQ(stringify(allowedIPv6), "2001:db8:85a3:0:0:8a2e:370:7334/128");
+ EXPECT_EQ(stringify(allowedIPv6Range), "2001:db8:85a3:0:0:8a2e:370:7330/124");
+ EXPECT_EQ(stringify(blocklistedIPv6), "0:0:0:0:0:0:0:1/128");
+ EXPECT_EQ(allowedIPv4Range, blocklistedIPv4);
+ EXPECT_EQ(allowedIPv6Range, allowedIPv6);
+}
+
+TEST_F(IPPolicyTest, asNetworkIPv4Range) {
+ auto actualIPv4Range = rgw::IAM::Condition::as_network("192.168.1.0/24");
+ ASSERT_TRUE(actualIPv4Range.is_initialized());
+ EXPECT_EQ(*actualIPv4Range, allowedIPv4Range);
+}
+
+TEST_F(IPPolicyTest, asNetworkIPv4) {
+ auto actualIPv4 = rgw::IAM::Condition::as_network("192.168.1.1");
+ ASSERT_TRUE(actualIPv4.is_initialized());
+ EXPECT_EQ(*actualIPv4, blocklistedIPv4);
+}
+
+TEST_F(IPPolicyTest, asNetworkIPv6Range) {
+ auto actualIPv6Range = rgw::IAM::Condition::as_network("2001:db8:85a3:0:0:8a2e:370:7330/124");
+ ASSERT_TRUE(actualIPv6Range.is_initialized());
+ EXPECT_EQ(*actualIPv6Range, allowedIPv6Range);
+}
+
+TEST_F(IPPolicyTest, asNetworkIPv6) {
+ auto actualIPv6 = rgw::IAM::Condition::as_network("2001:db8:85a3:0:0:8a2e:370:7334");
+ ASSERT_TRUE(actualIPv6.is_initialized());
+ EXPECT_EQ(*actualIPv6, allowedIPv6);
+}
+
+TEST_F(IPPolicyTest, asNetworkInvalid) {
+ EXPECT_FALSE(rgw::IAM::Condition::as_network(""));
+ EXPECT_FALSE(rgw::IAM::Condition::as_network("192.168.1.1/33"));
+ EXPECT_FALSE(rgw::IAM::Condition::as_network("2001:db8:85a3:0:0:8a2e:370:7334/129"));
+ EXPECT_FALSE(rgw::IAM::Condition::as_network("192.168.1.1:"));
+ EXPECT_FALSE(rgw::IAM::Condition::as_network("1.2.3.10000"));
+}
+
+TEST_F(IPPolicyTest, IPEnvironment) {
+ RGWProcessEnv penv;
+ // Unfortunately RGWCivetWeb is too tightly tied to civetweb to test RGWCivetWeb::init_env.
+ RGWEnv rgw_env;
+ rgw::sal::RadosStore store;
+ std::unique_ptr<rgw::sal::User> user = store.get_user(rgw_user());
+ rgw_env.set("REMOTE_ADDR", "192.168.1.1");
+ rgw_env.set("HTTP_HOST", "1.2.3.4");
+ req_state rgw_req_state(cct.get(), penv, &rgw_env, 0);
+ rgw_req_state.set_user(user);
+ rgw_build_iam_environment(&store, &rgw_req_state);
+ auto ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
+ EXPECT_EQ(ip->second, "192.168.1.1");
+
+ ASSERT_EQ(cct.get()->_conf.set_val("rgw_remote_addr_param", "SOME_VAR"), 0);
+ EXPECT_EQ(cct.get()->_conf->rgw_remote_addr_param, "SOME_VAR");
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&store, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ EXPECT_EQ(ip, rgw_req_state.env.end());
+
+ rgw_env.set("SOME_VAR", "192.168.1.2");
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&store, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
+ EXPECT_EQ(ip->second, "192.168.1.2");
+
+ ASSERT_EQ(cct.get()->_conf.set_val("rgw_remote_addr_param", "HTTP_X_FORWARDED_FOR"), 0);
+ rgw_env.set("HTTP_X_FORWARDED_FOR", "192.168.1.3");
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&store, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
+ EXPECT_EQ(ip->second, "192.168.1.3");
+
+ rgw_env.set("HTTP_X_FORWARDED_FOR", "192.168.1.4, 4.3.2.1, 2001:db8:85a3:8d3:1319:8a2e:370:7348");
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&store, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
+ EXPECT_EQ(ip->second, "192.168.1.4");
+}
+
+TEST_F(IPPolicyTest, ParseIPAddress) {
+ boost::optional<Policy> p;
+
+ ASSERT_NO_THROW(
+ p = Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(ip_address_full_example), true));
+ ASSERT_TRUE(p);
+
+ EXPECT_EQ(p->text, ip_address_full_example);
+ EXPECT_EQ(p->version, Version::v2012_10_17);
+ EXPECT_EQ(*p->id, "S3IPPolicyTest");
+ EXPECT_FALSE(p->statements.empty());
+ EXPECT_EQ(p->statements.size(), 1U);
+ EXPECT_EQ(*p->statements[0].sid, "IPAllow");
+ EXPECT_FALSE(p->statements[0].princ.empty());
+ EXPECT_EQ(p->statements[0].princ.size(), 1U);
+ EXPECT_EQ(*p->statements[0].princ.begin(),
+ Principal::wildcard());
+ EXPECT_TRUE(p->statements[0].noprinc.empty());
+ EXPECT_EQ(p->statements[0].effect, Effect::Allow);
+ Action_t act;
+ act[s3ListBucket] = 1;
+ EXPECT_EQ(p->statements[0].action, act);
+ EXPECT_EQ(p->statements[0].notaction, None);
+ ASSERT_FALSE(p->statements[0].resource.empty());
+ ASSERT_EQ(p->statements[0].resource.size(), 2U);
+ EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::aws);
+ EXPECT_EQ(p->statements[0].resource.begin()->service, Service::s3);
+ EXPECT_TRUE(p->statements[0].resource.begin()->region.empty());
+ EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant);
+ EXPECT_EQ(p->statements[0].resource.begin()->resource, "example_bucket");
+ EXPECT_EQ((p->statements[0].resource.begin() + 1)->resource, "example_bucket/*");
+ EXPECT_TRUE(p->statements[0].notresource.empty());
+ ASSERT_FALSE(p->statements[0].conditions.empty());
+ ASSERT_EQ(p->statements[0].conditions.size(), 2U);
+ EXPECT_EQ(p->statements[0].conditions[0].op, TokenID::IpAddress);
+ EXPECT_EQ(p->statements[0].conditions[0].key, "aws:SourceIp");
+ ASSERT_FALSE(p->statements[0].conditions[0].vals.empty());
+ EXPECT_EQ(p->statements[0].conditions[0].vals.size(), 2U);
+ EXPECT_EQ(p->statements[0].conditions[0].vals[0], "192.168.1.0/24");
+ EXPECT_EQ(p->statements[0].conditions[0].vals[1], "::1");
+ boost::optional<rgw::IAM::MaskedIP> convertedIPv4 = rgw::IAM::Condition::as_network(p->statements[0].conditions[0].vals[0]);
+ EXPECT_TRUE(convertedIPv4.is_initialized());
+ if (convertedIPv4.is_initialized()) {
+ EXPECT_EQ(*convertedIPv4, allowedIPv4Range);
+ }
+
+ EXPECT_EQ(p->statements[0].conditions[1].op, TokenID::NotIpAddress);
+ EXPECT_EQ(p->statements[0].conditions[1].key, "aws:SourceIp");
+ ASSERT_FALSE(p->statements[0].conditions[1].vals.empty());
+ EXPECT_EQ(p->statements[0].conditions[1].vals.size(), 2U);
+ EXPECT_EQ(p->statements[0].conditions[1].vals[0], "192.168.1.1/32");
+ EXPECT_EQ(p->statements[0].conditions[1].vals[1], "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ boost::optional<rgw::IAM::MaskedIP> convertedIPv6 = rgw::IAM::Condition::as_network(p->statements[0].conditions[1].vals[1]);
+ EXPECT_TRUE(convertedIPv6.is_initialized());
+ if (convertedIPv6.is_initialized()) {
+ EXPECT_EQ(*convertedIPv6, allowedIPv6);
+ }
+}
+
+TEST_F(IPPolicyTest, EvalIPAddress) {
+ auto allowp =
+ Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(ip_address_allow_example), true);
+ auto denyp =
+ Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(ip_address_deny_example), true);
+ auto fullp =
+ Policy(cct.get(), arbitrary_tenant,
+ bufferlist::static_from_string(ip_address_full_example), true);
+ Environment e;
+ Environment allowedIP, blocklistedIP, allowedIPv6, blocklistedIPv6;
+ allowedIP.emplace("aws:SourceIp","192.168.1.2");
+ allowedIPv6.emplace("aws:SourceIp", "::1");
+ blocklistedIP.emplace("aws:SourceIp", "192.168.1.1");
+ blocklistedIPv6.emplace("aws:SourceIp", "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+
+ auto trueacct = FakeIdentity(
+ Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS"));
+ // Without an IP address in the environment then evaluation will always pass
+ ARN arn1(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(allowp.eval(e, trueacct, s3ListBucket, arn1),
+ Effect::Pass);
+ ARN arn2(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(fullp.eval(e, trueacct, s3ListBucket, arn2),
+ Effect::Pass);
+
+ ARN arn3(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(allowp.eval(allowedIP, trueacct, s3ListBucket, arn3),
+ Effect::Allow);
+ ARN arn4(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(allowp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn4),
+ Effect::Pass);
+
+ ARN arn5(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, arn5),
+ Effect::Deny);
+ ARN arn6(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, arn6),
+ Effect::Deny);
+
+ ARN arn7(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(denyp.eval(blocklistedIP, trueacct, s3ListBucket, arn7),
+ Effect::Pass);
+ ARN arn8(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(denyp.eval(blocklistedIP, trueacct, s3ListBucket, arn8),
+ Effect::Pass);
+
+ ARN arn9(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(denyp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn9),
+ Effect::Pass);
+ ARN arn10(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(denyp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn10),
+ Effect::Pass);
+ ARN arn11(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, arn11),
+ Effect::Deny);
+ ARN arn12(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, arn12),
+ Effect::Deny);
+
+ ARN arn13(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, arn13),
+ Effect::Allow);
+ ARN arn14(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, arn14),
+ Effect::Allow);
+
+ ARN arn15(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(fullp.eval(blocklistedIP, trueacct, s3ListBucket, arn15),
+ Effect::Pass);
+ ARN arn16(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(fullp.eval(blocklistedIP, trueacct, s3ListBucket, arn16),
+ Effect::Pass);
+
+ ARN arn17(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, arn17),
+ Effect::Allow);
+ ARN arn18(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, arn18),
+ Effect::Allow);
+
+ ARN arn19(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket");
+ EXPECT_EQ(fullp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn19),
+ Effect::Pass);
+ ARN arn20(Partition::aws, Service::s3,
+ "", arbitrary_tenant, "example_bucket/myobject");
+ EXPECT_EQ(fullp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn20),
+ Effect::Pass);
+}
+
+string IPPolicyTest::ip_address_allow_example = R"(
+{
+ "Version": "2012-10-17",
+ "Id": "S3SimpleIPPolicyTest",
+ "Statement": [{
+ "Sid": "1",
+ "Effect": "Allow",
+ "Principal": {"AWS": ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:root"]},
+ "Action": "s3:ListBucket",
+ "Resource": [
+ "arn:aws:s3:::example_bucket"
+ ],
+ "Condition": {
+ "IpAddress": {"aws:SourceIp": "192.168.1.0/24"}
+ }
+ }]
+}
+)";
+
+string IPPolicyTest::ip_address_deny_example = R"(
+{
+ "Version": "2012-10-17",
+ "Id": "S3IPPolicyTest",
+ "Statement": {
+ "Effect": "Deny",
+ "Sid": "IPDeny",
+ "Action": "s3:ListBucket",
+ "Principal": {"AWS": ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:root"]},
+ "Resource": [
+ "arn:aws:s3:::example_bucket",
+ "arn:aws:s3:::example_bucket/*"
+ ],
+ "Condition": {
+ "NotIpAddress": {"aws:SourceIp": ["192.168.1.1/32", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"]}
+ }
+ }
+}
+)";
+
+string IPPolicyTest::ip_address_full_example = R"(
+{
+ "Version": "2012-10-17",
+ "Id": "S3IPPolicyTest",
+ "Statement": {
+ "Effect": "Allow",
+ "Sid": "IPAllow",
+ "Action": "s3:ListBucket",
+ "Principal": "*",
+ "Resource": [
+ "arn:aws:s3:::example_bucket",
+ "arn:aws:s3:::example_bucket/*"
+ ],
+ "Condition": {
+ "IpAddress": {"aws:SourceIp": ["192.168.1.0/24", "::1"]},
+ "NotIpAddress": {"aws:SourceIp": ["192.168.1.1/32", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"]}
+ }
+ }
+}
+)";
+
+TEST(MatchWildcards, Simple)
+{
+ EXPECT_TRUE(match_wildcards("", ""));
+ EXPECT_TRUE(match_wildcards("", "", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("", "abc"));
+ EXPECT_FALSE(match_wildcards("", "abc", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("abc", ""));
+ EXPECT_FALSE(match_wildcards("abc", "", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("abc", "abc"));
+ EXPECT_TRUE(match_wildcards("abc", "abc", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("abc", "abC"));
+ EXPECT_TRUE(match_wildcards("abc", "abC", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("abC", "abc"));
+ EXPECT_TRUE(match_wildcards("abC", "abc", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("abc", "abcd"));
+ EXPECT_FALSE(match_wildcards("abc", "abcd", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("abcd", "abc"));
+ EXPECT_FALSE(match_wildcards("abcd", "abc", MATCH_CASE_INSENSITIVE));
+}
+
+TEST(MatchWildcards, QuestionMark)
+{
+ EXPECT_FALSE(match_wildcards("?", ""));
+ EXPECT_FALSE(match_wildcards("?", "", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("?", "a"));
+ EXPECT_TRUE(match_wildcards("?", "a", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("?bc", "abc"));
+ EXPECT_TRUE(match_wildcards("?bc", "abc", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("a?c", "abc"));
+ EXPECT_TRUE(match_wildcards("a?c", "abc", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("abc", "a?c"));
+ EXPECT_FALSE(match_wildcards("abc", "a?c", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("a?c", "abC"));
+ EXPECT_TRUE(match_wildcards("a?c", "abC", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("ab?", "abc"));
+ EXPECT_TRUE(match_wildcards("ab?", "abc", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("a?c?e", "abcde"));
+ EXPECT_TRUE(match_wildcards("a?c?e", "abcde", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("???", "abc"));
+ EXPECT_TRUE(match_wildcards("???", "abc", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("???", "abcd"));
+ EXPECT_FALSE(match_wildcards("???", "abcd", MATCH_CASE_INSENSITIVE));
+}
+
+TEST(MatchWildcards, Asterisk)
+{
+ EXPECT_TRUE(match_wildcards("*", ""));
+ EXPECT_TRUE(match_wildcards("*", "", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("", "*"));
+ EXPECT_FALSE(match_wildcards("", "*", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("*a", ""));
+ EXPECT_FALSE(match_wildcards("*a", "", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("*a", "a"));
+ EXPECT_TRUE(match_wildcards("*a", "a", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("a*", "a"));
+ EXPECT_TRUE(match_wildcards("a*", "a", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("a*c", "ac"));
+ EXPECT_TRUE(match_wildcards("a*c", "ac", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("a*c", "abbc"));
+ EXPECT_TRUE(match_wildcards("a*c", "abbc", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("a*c", "abbC"));
+ EXPECT_TRUE(match_wildcards("a*c", "abbC", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("a*c*e", "abBce"));
+ EXPECT_TRUE(match_wildcards("a*c*e", "abBce", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("http://*.example.com",
+ "http://www.example.com"));
+ EXPECT_TRUE(match_wildcards("http://*.example.com",
+ "http://www.example.com", MATCH_CASE_INSENSITIVE));
+ EXPECT_FALSE(match_wildcards("http://*.example.com",
+ "http://www.Example.com"));
+ EXPECT_TRUE(match_wildcards("http://*.example.com",
+ "http://www.Example.com", MATCH_CASE_INSENSITIVE));
+ EXPECT_TRUE(match_wildcards("http://example.com/*",
+ "http://example.com/index.html"));
+ EXPECT_TRUE(match_wildcards("http://example.com/*/*.jpg",
+ "http://example.com/fun/smiley.jpg"));
+ // note: parsing of * is not greedy, so * does not match 'bc' here
+ EXPECT_FALSE(match_wildcards("a*c", "abcc"));
+ EXPECT_FALSE(match_wildcards("a*c", "abcc", MATCH_CASE_INSENSITIVE));
+}
+
+TEST(MatchPolicy, Action)
+{
+ constexpr auto flag = MATCH_POLICY_ACTION;
+ EXPECT_TRUE(match_policy("a:b:c", "a:b:c", flag));
+ EXPECT_TRUE(match_policy("a:b:c", "A:B:C", flag)); // case insensitive
+ EXPECT_TRUE(match_policy("a:*:e", "a:bcd:e", flag));
+ EXPECT_FALSE(match_policy("a:*", "a:b:c", flag)); // cannot span segments
+}
+
+TEST(MatchPolicy, Resource)
+{
+ constexpr auto flag = MATCH_POLICY_RESOURCE;
+ EXPECT_TRUE(match_policy("a:b:c", "a:b:c", flag));
+ EXPECT_FALSE(match_policy("a:b:c", "A:B:C", flag)); // case sensitive
+ EXPECT_TRUE(match_policy("a:*:e", "a:bcd:e", flag));
+ EXPECT_TRUE(match_policy("a:*", "a:b:c", flag)); // can span segments
+}
+
+TEST(MatchPolicy, ARN)
+{
+ constexpr auto flag = MATCH_POLICY_ARN;
+ EXPECT_TRUE(match_policy("a:b:c", "a:b:c", flag));
+ EXPECT_TRUE(match_policy("a:b:c", "A:B:C", flag)); // case insensitive
+ EXPECT_TRUE(match_policy("a:*:e", "a:bcd:e", flag));
+ EXPECT_FALSE(match_policy("a:*", "a:b:c", flag)); // cannot span segments
+}
+
+TEST(MatchPolicy, String)
+{
+ constexpr auto flag = MATCH_POLICY_STRING;
+ EXPECT_TRUE(match_policy("a:b:c", "a:b:c", flag));
+ EXPECT_FALSE(match_policy("a:b:c", "A:B:C", flag)); // case sensitive
+ EXPECT_TRUE(match_policy("a:*:e", "a:bcd:e", flag));
+ EXPECT_TRUE(match_policy("a:*", "a:b:c", flag)); // can span segments
+}
+
+Action_t set_range_bits(std::uint64_t start, std::uint64_t end)
+{
+ Action_t result;
+ for (uint64_t i = start; i < end; i++) {
+ result.set(i);
+ }
+ return result;
+}
+
+using rgw::IAM::s3AllValue;
+using rgw::IAM::stsAllValue;
+using rgw::IAM::allValue;
+using rgw::IAM::iamAllValue;
+TEST(set_cont_bits, iamconsts)
+{
+ EXPECT_EQ(s3AllValue, set_range_bits(0, s3All));
+ EXPECT_EQ(iamAllValue, set_range_bits(s3All+1, iamAll));
+ EXPECT_EQ(stsAllValue, set_range_bits(iamAll+1, stsAll));
+ EXPECT_EQ(allValue , set_range_bits(0, allCount));
+}
diff --git a/src/test/rgw/test_rgw_kms.cc b/src/test/rgw/test_rgw_kms.cc
new file mode 100644
index 000000000..49ee747f7
--- /dev/null
+++ b/src/test/rgw/test_rgw_kms.cc
@@ -0,0 +1,294 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include "common/ceph_context.h"
+#include "rgw_common.h"
+#define FORTEST_VIRTUAL virtual
+#include "rgw_kms.cc"
+
+using ::testing::_;
+using ::testing::Action;
+using ::testing::ActionInterface;
+using ::testing::MakeAction;
+using ::testing::StrEq;
+
+
+class MockTransitSecretEngine : public TransitSecretEngine {
+
+public:
+ MockTransitSecretEngine(CephContext *cct, SSEContext & kctx, EngineParmMap parms) : TransitSecretEngine(cct, kctx, parms){}
+
+ MOCK_METHOD(int, send_request, (const DoutPrefixProvider *dpp, const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override));
+
+};
+
+class MockKvSecretEngine : public KvSecretEngine {
+
+public:
+ MockKvSecretEngine(CephContext *cct, SSEContext & kctx, EngineParmMap parms) : KvSecretEngine(cct, kctx, parms){}
+
+ MOCK_METHOD(int, send_request, (const DoutPrefixProvider *dpp, const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override));
+
+};
+
+class TestSSEKMS : public ::testing::Test {
+
+protected:
+ CephContext *cct;
+ MockTransitSecretEngine* old_engine;
+ MockKvSecretEngine* kv_engine;
+ MockTransitSecretEngine* transit_engine;
+
+ void SetUp() override {
+ EngineParmMap old_parms, kv_parms, new_parms;
+ cct = (new CephContext(CEPH_ENTITY_TYPE_ANY))->get();
+ KMSContext kctx { cct };
+ old_parms["compat"] = "2";
+ old_engine = new MockTransitSecretEngine(cct, kctx, std::move(old_parms));
+ kv_engine = new MockKvSecretEngine(cct, kctx, std::move(kv_parms));
+ new_parms["compat"] = "1";
+ transit_engine = new MockTransitSecretEngine(cct, kctx, std::move(new_parms));
+ }
+
+ void TearDown() {
+ delete old_engine;
+ delete kv_engine;
+ delete transit_engine;
+ }
+
+};
+
+
+TEST_F(TestSSEKMS, vault_token_file_unset)
+{
+ cct->_conf.set_val("rgw_crypt_vault_auth", "token");
+ EngineParmMap old_parms, kv_parms;
+ KMSContext kctx { cct };
+ TransitSecretEngine te(cct, kctx, std::move(old_parms));
+ KvSecretEngine kv(cct, kctx, std::move(kv_parms));
+ const NoDoutPrefix no_dpp(cct, 1);
+
+ std::string_view key_id("my_key");
+ std::string actual_key;
+
+ ASSERT_EQ(te.get_key(&no_dpp, key_id, actual_key), -EINVAL);
+ ASSERT_EQ(kv.get_key(&no_dpp, key_id, actual_key), -EINVAL);
+}
+
+
+TEST_F(TestSSEKMS, non_existent_vault_token_file)
+{
+ cct->_conf.set_val("rgw_crypt_vault_auth", "token");
+ cct->_conf.set_val("rgw_crypt_vault_token_file", "/nonexistent/file");
+ EngineParmMap old_parms, kv_parms;
+ KMSContext kctx { cct };
+ TransitSecretEngine te(cct, kctx, std::move(old_parms));
+ KvSecretEngine kv(cct, kctx, std::move(kv_parms));
+ const NoDoutPrefix no_dpp(cct, 1);
+
+ std::string_view key_id("my_key/1");
+ std::string actual_key;
+
+ ASSERT_EQ(te.get_key(&no_dpp, key_id, actual_key), -ENOENT);
+ ASSERT_EQ(kv.get_key(&no_dpp, key_id, actual_key), -ENOENT);
+}
+
+
+typedef int SendRequestMethod(const DoutPrefixProvider *dpp, const char *,
+ std::string_view, std::string_view,
+ const std::string &, bufferlist &);
+
+class SetPointedValueAction : public ActionInterface<SendRequestMethod> {
+ public:
+ std::string json;
+
+ SetPointedValueAction(std::string json){
+ this->json = json;
+ }
+
+ int Perform(const ::std::tuple<const DoutPrefixProvider*, const char *, std::string_view, std::string_view, const std::string &, bufferlist &>& args) override {
+// const DoutPrefixProvider *dpp = ::std::get<0>(args);
+// const char *method = ::std::get<1>(args);
+// std::string_view infix = ::std::get<2>(args);
+// std::string_view key_id = ::std::get<3>(args);
+// const std::string& postdata = ::std::get<4>(args);
+ bufferlist& bl = ::std::get<5>(args);
+
+// std::cout << "method = " << method << " infix = " << infix << " key_id = " << key_id
+// << " postdata = " << postdata
+// << " => json = " << json
+// << std::endl;
+
+ bl.append(json);
+ // note: in the bufferlist, the string is not
+ // necessarily 0 terminated at this point. Logic in
+ // rgw_kms.cc must handle this (by appending a 0.)
+ return 0;
+ }
+};
+
+Action<SendRequestMethod> SetPointedValue(std::string json) {
+ return MakeAction(new SetPointedValueAction(json));
+}
+
+
+TEST_F(TestSSEKMS, test_transit_key_version_extraction){
+ const NoDoutPrefix no_dpp(cct, 1);
+ string json = R"({"data": {"keys": {"6": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
+ EXPECT_CALL(*old_engine, send_request(&no_dpp, StrEq("GET"), StrEq(""), StrEq("1/2/3/4/5/6"), StrEq(""), _)).WillOnce(SetPointedValue(json));
+
+ std::string actual_key;
+ std::string tests[11] {"/", "my_key/", "my_key", "", "my_key/a", "my_key/1a",
+ "my_key/a1", "my_key/1a1", "my_key/1/a", "1", "my_key/1/"
+ };
+
+ int res;
+ for (const auto &test: tests) {
+ res = old_engine->get_key(&no_dpp, std::string_view(test), actual_key);
+ ASSERT_EQ(res, -EINVAL);
+ }
+
+ res = old_engine->get_key(&no_dpp, std::string_view("1/2/3/4/5/6"), actual_key);
+ ASSERT_EQ(res, 0);
+ ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
+}
+
+
+TEST_F(TestSSEKMS, test_transit_backend){
+
+ std::string_view my_key("my_key/1");
+ std::string actual_key;
+
+ // Mocks the expected return Value from Vault Server using custom Argument Action
+ string json = R"({"data": {"keys": {"1": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
+ const NoDoutPrefix no_dpp(cct, 1);
+ EXPECT_CALL(*old_engine, send_request(&no_dpp, StrEq("GET"), StrEq(""), StrEq("my_key/1"), StrEq(""), _)).WillOnce(SetPointedValue(json));
+
+ int res = old_engine->get_key(&no_dpp, my_key, actual_key);
+
+ ASSERT_EQ(res, 0);
+ ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
+}
+
+
+TEST_F(TestSSEKMS, test_transit_makekey){
+
+ std::string_view my_key("my_key");
+ std::string actual_key;
+ map<string, bufferlist> attrs;
+ const NoDoutPrefix no_dpp(cct, 1);
+
+ // Mocks the expected return Value from Vault Server using custom Argument Action
+ string post_json = R"({"data": {"ciphertext": "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ","key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})";
+ EXPECT_CALL(*transit_engine, send_request(&no_dpp, StrEq("POST"), StrEq("/datakey/plaintext/"), StrEq("my_key"), _, _))
+ .WillOnce(SetPointedValue(post_json));
+
+ set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})");
+ set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key);
+
+ int res = transit_engine->make_actual_key(&no_dpp, attrs, actual_key);
+ std::string cipher_text { get_str_attribute(attrs,RGW_ATTR_CRYPT_DATAKEY) };
+
+ ASSERT_EQ(res, 0);
+ ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="));
+ ASSERT_EQ(cipher_text, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ");
+}
+
+TEST_F(TestSSEKMS, test_transit_reconstitutekey){
+
+ std::string_view my_key("my_key");
+ std::string actual_key;
+ map<string, bufferlist> attrs;
+ const NoDoutPrefix no_dpp(cct, 1);
+
+ // Mocks the expected return Value from Vault Server using custom Argument Action
+ set_attr(attrs, RGW_ATTR_CRYPT_DATAKEY, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ");
+ string post_json = R"({"data": {"key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})";
+ EXPECT_CALL(*transit_engine, send_request(&no_dpp, StrEq("POST"), StrEq("/decrypt/"), StrEq("my_key"), _, _))
+ .WillOnce(SetPointedValue(post_json));
+
+ set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})");
+ set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key);
+
+ int res = transit_engine->reconstitute_actual_key(&no_dpp, attrs, actual_key);
+
+ ASSERT_EQ(res, 0);
+ ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="));
+}
+
+TEST_F(TestSSEKMS, test_kv_backend){
+
+ std::string_view my_key("my_key");
+ std::string actual_key;
+ const NoDoutPrefix no_dpp(cct, 1);
+
+ // Mocks the expected return value from Vault Server using custom Argument Action
+ string json = R"({"data": {"data": {"key": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
+ EXPECT_CALL(*kv_engine, send_request(&no_dpp, StrEq("GET"), StrEq(""), StrEq("my_key"), StrEq(""), _))
+ .WillOnce(SetPointedValue(json));
+
+ int res = kv_engine->get_key(&no_dpp, my_key, actual_key);
+
+ ASSERT_EQ(res, 0);
+ ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
+}
+
+
+TEST_F(TestSSEKMS, concat_url)
+{
+ // Each test has 3 strings:
+ // * the base URL
+ // * the path we want to concatenate
+ // * the exepected final URL
+ std::string tests[9][3] ={
+ {"", "", ""},
+ {"", "bar", "/bar"},
+ {"", "/bar", "/bar"},
+ {"foo", "", "foo"},
+ {"foo", "bar", "foo/bar"},
+ {"foo", "/bar", "foo/bar"},
+ {"foo/", "", "foo/"},
+ {"foo/", "bar", "foo/bar"},
+ {"foo/", "/bar", "foo/bar"},
+ };
+ for (const auto &test: tests) {
+ std::string url(test[0]), path(test[1]), expected(test[2]);
+ concat_url(url, path);
+ ASSERT_EQ(url, expected);
+ }
+}
+
+
+TEST_F(TestSSEKMS, string_ends_maybe_slash)
+{
+ struct { std::string hay, needle; bool expected; } tests[] ={
+ {"jack here", "fred", false},
+ {"here is a fred", "fred", true},
+ {"and a fred/", "fred", true},
+ {"no fred here", "fred", false},
+ {"double fred//", "fred", true},
+ };
+ for (const auto &test: tests) {
+ bool expected { string_ends_maybe_slash(test.hay, test.needle) };
+ ASSERT_EQ(expected, test.expected);
+ }
+}
+
+
+TEST_F(TestSSEKMS, test_transit_backend_empty_response)
+{
+ std::string_view my_key("/key/nonexistent/1");
+ std::string actual_key;
+ const NoDoutPrefix no_dpp(cct, 1);
+
+ // Mocks the expected return Value from Vault Server using custom Argument Action
+ string json = R"({"errors": ["version does not exist or cannot be found"]})";
+ EXPECT_CALL(*old_engine, send_request(&no_dpp, StrEq("GET"), StrEq(""), StrEq("/key/nonexistent/1"), StrEq(""), _)).WillOnce(SetPointedValue(json));
+
+ int res = old_engine->get_key(&no_dpp, my_key, actual_key);
+
+ ASSERT_EQ(res, -EINVAL);
+ ASSERT_EQ(actual_key, from_base64(""));
+}
diff --git a/src/test/rgw/test_rgw_lc.cc b/src/test/rgw/test_rgw_lc.cc
new file mode 100644
index 000000000..83a4cac67
--- /dev/null
+++ b/src/test/rgw/test_rgw_lc.cc
@@ -0,0 +1,109 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "rgw_xml.h"
+#include "rgw_lc.h"
+#include "rgw_lc_s3.h"
+#include <gtest/gtest.h>
+//#include <spawn/spawn.hpp>
+#include <string>
+#include <vector>
+#include <stdexcept>
+
+static const char* xmldoc_1 =
+R"(<Filter>
+ <And>
+ <Prefix>tax/</Prefix>
+ <Tag>
+ <Key>key1</Key>
+ <Value>value1</Value>
+ </Tag>
+ <Tag>
+ <Key>key2</Key>
+ <Value>value2</Value>
+ </Tag>
+ </And>
+</Filter>
+)";
+
+TEST(TestLCFilterDecoder, XMLDoc1)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(xmldoc_1, strlen(xmldoc_1), 1));
+ LCFilter_S3 filter;
+ auto result = RGWXMLDecoder::decode_xml("Filter", filter, &parser, true);
+ ASSERT_TRUE(result);
+ /* check repeated Tag element */
+ auto tag_map = filter.get_tags().get_tags();
+ auto val1 = tag_map.find("key1");
+ ASSERT_EQ(val1->second, "value1");
+ auto val2 = tag_map.find("key2");
+ ASSERT_EQ(val2->second, "value2");
+ /* check our flags */
+ ASSERT_EQ(filter.get_flags(), 0);
+}
+
+static const char* xmldoc_2 =
+R"(<Filter>
+ <And>
+ <ArchiveZone />
+ <Tag>
+ <Key>spongebob</Key>
+ <Value>squarepants</Value>
+ </Tag>
+ </And>
+</Filter>
+)";
+
+TEST(TestLCFilterDecoder, XMLDoc2)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(xmldoc_2, strlen(xmldoc_2), 1));
+ LCFilter_S3 filter;
+ auto result = RGWXMLDecoder::decode_xml("Filter", filter, &parser, true);
+ ASSERT_TRUE(result);
+ /* check tags */
+ auto tag_map = filter.get_tags().get_tags();
+ auto val1 = tag_map.find("spongebob");
+ ASSERT_EQ(val1->second, "squarepants");
+ /* check our flags */
+ ASSERT_EQ(filter.get_flags(), LCFilter::make_flag(LCFlagType::ArchiveZone));
+}
+
+// invalid And element placement
+static const char* xmldoc_3 =
+R"(<Filter>
+ <And>
+ <Tag>
+ <Key>miles</Key>
+ <Value>davis</Value>
+ </Tag>
+ </And>
+ <Tag>
+ <Key>spongebob</Key>
+ <Value>squarepants</Value>
+ </Tag>
+</Filter>
+)";
+
+TEST(TestLCFilterInvalidAnd, XMLDoc3)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(xmldoc_3, strlen(xmldoc_3), 1));
+ LCFilter_S3 filter;
+ auto result = RGWXMLDecoder::decode_xml("Filter", filter, &parser, true);
+ ASSERT_TRUE(result);
+ /* check repeated Tag element */
+ auto tag_map = filter.get_tags().get_tags();
+ auto val1 = tag_map.find("spongebob");
+ ASSERT_TRUE(val1 == tag_map.end());
+ /* because the invalid 2nd tag element was not recognized,
+ * we cannot access it:
+ ASSERT_EQ(val1->second, "squarepants");
+ */
+ /* check our flags */
+ ASSERT_EQ(filter.get_flags(), uint32_t(LCFlagType::none));
+}
diff --git a/src/test/rgw/test_rgw_lua.cc b/src/test/rgw/test_rgw_lua.cc
new file mode 100644
index 000000000..a539c025b
--- /dev/null
+++ b/src/test/rgw/test_rgw_lua.cc
@@ -0,0 +1,1338 @@
+#include <gtest/gtest.h>
+#include "common/ceph_context.h"
+#include "rgw_common.h"
+#include "rgw_auth_registry.h"
+#include "rgw_process_env.h"
+#include "rgw_sal_rados.h"
+#include "rgw_lua_request.h"
+#include "rgw_lua_background.h"
+#include "rgw_lua_data_filter.h"
+
+using namespace std;
+using namespace rgw;
+using boost::container::flat_set;
+using rgw::auth::Identity;
+using rgw::auth::Principal;
+
+class CctCleaner {
+ CephContext* cct;
+public:
+ CctCleaner(CephContext* _cct) : cct(_cct) {}
+ ~CctCleaner() {
+#ifdef WITH_SEASTAR
+ delete cct;
+#else
+ cct->put();
+#endif
+ }
+};
+
+class FakeIdentity : public Identity {
+public:
+ FakeIdentity() = default;
+
+ uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override {
+ return 0;
+ };
+
+ bool is_admin_of(const rgw_user& uid) const override {
+ return false;
+ }
+
+ bool is_owner_of(const rgw_user& uid) const override {
+ return false;
+ }
+
+ virtual uint32_t get_perm_mask() const override {
+ return 0;
+ }
+
+ uint32_t get_identity_type() const override {
+ return TYPE_RGW;
+ }
+
+ string get_acct_name() const override {
+ return "";
+ }
+
+ string get_subuser() const override {
+ return "";
+ }
+
+ void to_str(std::ostream& out) const override {
+ return;
+ }
+
+ bool is_identity(const flat_set<Principal>& ids) const override {
+ return false;
+ }
+};
+
+class TestUser : public sal::StoreUser {
+public:
+ virtual std::unique_ptr<User> clone() override {
+ return std::unique_ptr<User>(new TestUser(*this));
+ }
+
+ virtual int list_buckets(const DoutPrefixProvider *dpp, const string&, const string&, uint64_t, bool, sal::BucketList&, optional_yield y) override {
+ return 0;
+ }
+
+ virtual int create_bucket(const DoutPrefixProvider* dpp, const rgw_bucket& b, const std::string& zonegroup_id, rgw_placement_rule& placement_rule, std::string& swift_ver_location, const RGWQuotaInfo* pquota_info, const RGWAccessControlPolicy& policy, sal::Attrs& attrs, RGWBucketInfo& info, obj_version& ep_objv, bool exclusive, bool obj_lock_enabled, bool* existed, req_info& req_info, std::unique_ptr<sal::Bucket>* bucket, optional_yield y) override {
+ return 0;
+ }
+
+ virtual int read_attrs(const DoutPrefixProvider *dpp, optional_yield y) override {
+ return 0;
+ }
+
+ virtual int read_stats(const DoutPrefixProvider *dpp, optional_yield y, RGWStorageStats* stats, ceph::real_time *last_stats_sync, ceph::real_time *last_stats_update) override {
+ return 0;
+ }
+
+ virtual int read_stats_async(const DoutPrefixProvider *dpp, RGWGetUserStats_CB *cb) override {
+ return 0;
+ }
+
+ virtual int complete_flush_stats(const DoutPrefixProvider *dpp, optional_yield y) override {
+ return 0;
+ }
+
+ virtual int read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries, bool *is_truncated, RGWUsageIter& usage_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage) override {
+ return 0;
+ }
+
+ virtual int trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) override {
+ return 0;
+ }
+
+ virtual int load_user(const DoutPrefixProvider *dpp, optional_yield y) override {
+ return 0;
+ }
+
+ virtual int store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info) override {
+ return 0;
+ }
+
+ virtual int remove_user(const DoutPrefixProvider* dpp, optional_yield y) override {
+ return 0;
+ }
+ virtual int merge_and_store_attrs(const DoutPrefixProvider *dpp, rgw::sal::Attrs& attrs, optional_yield y) override {
+ return 0;
+ }
+ virtual int verify_mfa(const std::string& mfa_str, bool* verified, const DoutPrefixProvider* dpp, optional_yield y) override {
+ return 0;
+ }
+ virtual ~TestUser() = default;
+};
+
+class TestAccounter : public io::Accounter, public io::BasicClient {
+ RGWEnv env;
+
+protected:
+ virtual int init_env(CephContext *cct) override {
+ return 0;
+ }
+
+public:
+ ~TestAccounter() = default;
+
+ virtual void set_account(bool enabled) override {
+ }
+
+ virtual uint64_t get_bytes_sent() const override {
+ return 0;
+ }
+
+ virtual uint64_t get_bytes_received() const override {
+ return 0;
+ }
+
+ virtual RGWEnv& get_env() noexcept override {
+ return env;
+ }
+
+ virtual size_t complete_request() override {
+ return 0;
+ }
+};
+
+auto g_cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+
+CctCleaner cleaner(g_cct);
+
+tracing::Tracer tracer;
+
+#define DEFINE_REQ_STATE RGWProcessEnv pe; RGWEnv e; req_state s(g_cct, pe, &e, 0);
+#define INIT_TRACE tracer.init("test"); \
+ s.trace = tracer.start_trace("test", true);
+
+TEST(TestRGWLua, EmptyScript)
+{
+ const std::string script;
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, SyntaxError)
+{
+ const std::string script = R"(
+ if 3 < 5 then
+ RGWDebugLog("missing 'end'")
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, -1);
+}
+
+TEST(TestRGWLua, Hello)
+{
+ const std::string script = R"(
+ RGWDebugLog("hello from lua")
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, RGWDebugLogNumber)
+{
+ const std::string script = R"(
+ RGWDebugLog(1234567890)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, RGWDebugNil)
+{
+ const std::string script = R"(
+ RGWDebugLog(nil)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, -1);
+}
+
+TEST(TestRGWLua, URI)
+{
+ const std::string script = R"(
+ RGWDebugLog(Request.DecodedURI)
+ assert(Request.DecodedURI == "http://hello.world/")
+ )";
+
+ DEFINE_REQ_STATE;
+ s.decoded_uri = "http://hello.world/";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, Response)
+{
+ const std::string script = R"(
+ assert(Request.Response.Message == "This is a bad request")
+ assert(Request.Response.HTTPStatus == "Bad Request")
+ assert(Request.Response.RGWCode == 4000)
+ assert(Request.Response.HTTPStatusCode == 400)
+ )";
+
+ DEFINE_REQ_STATE;
+ s.err.http_ret = 400;
+ s.err.ret = 4000;
+ s.err.err_code = "Bad Request";
+ s.err.message = "This is a bad request";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, SetResponse)
+{
+ const std::string script = R"(
+ assert(Request.Response.Message == "this is a bad request")
+ Request.Response.Message = "this is a good request"
+ assert(Request.Response.Message == "this is a good request")
+ )";
+
+ DEFINE_REQ_STATE;
+ s.err.message = "this is a bad request";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, RGWIdNotWriteable)
+{
+ const std::string script = R"(
+ assert(Request.RGWId == "foo")
+ Request.RGWId = "bar"
+ )";
+
+ DEFINE_REQ_STATE;
+ s.host_id = "foo";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLua, InvalidField)
+{
+ const std::string script = R"(
+ RGWDebugLog(Request.Kaboom)
+ )";
+
+ DEFINE_REQ_STATE;
+ s.host_id = "foo";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, -1);
+}
+
+TEST(TestRGWLua, InvalidSubField)
+{
+ const std::string script = R"(
+ RGWDebugLog(Request.Error.Kaboom)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, -1);
+}
+
+TEST(TestRGWLua, Bucket)
+{
+ const std::string script = R"(
+ assert(Request.Bucket)
+ RGWDebugLog("Bucket Id: " .. Request.Bucket.Id)
+ assert(Request.Bucket.Marker == "mymarker")
+ assert(Request.Bucket.Name == "myname")
+ assert(Request.Bucket.Tenant == "mytenant")
+ assert(Request.Bucket.Count == 0)
+ assert(Request.Bucket.Size == 0)
+ assert(Request.Bucket.ZoneGroupId)
+ assert(Request.Bucket.CreationTime)
+ assert(Request.Bucket.MTime)
+ assert(Request.Bucket.Quota.MaxSize == -1)
+ assert(Request.Bucket.Quota.MaxObjects == -1)
+ assert(tostring(Request.Bucket.Quota.Enabled))
+ assert(tostring(Request.Bucket.Quota.Rounded))
+ assert(Request.Bucket.User.Id)
+ assert(Request.Bucket.User.Tenant)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ rgw_bucket b;
+ b.tenant = "mytenant";
+ b.name = "myname";
+ b.marker = "mymarker";
+ b.bucket_id = "myid";
+ s.bucket.reset(new sal::RadosBucket(nullptr, b));
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, WriteBucket)
+{
+ const std::string script = R"(
+ assert(Request.Bucket)
+ assert(Request.Bucket.Name == "myname")
+ Request.Bucket.Name = "othername"
+ )";
+
+ DEFINE_REQ_STATE;
+ s.init_state.url_bucket = "myname";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+ ASSERT_EQ(s.init_state.url_bucket, "othername");
+}
+
+TEST(TestRGWLua, WriteBucketFail)
+{
+ const std::string script = R"(
+ assert(Request.Bucket)
+ assert(Request.Bucket.Name == "myname")
+ Request.Bucket.Name = "othername"
+ )";
+
+ DEFINE_REQ_STATE;
+ rgw_bucket b;
+ b.name = "myname";
+ s.bucket.reset(new sal::RadosBucket(nullptr, b));
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLua, GenericAttributes)
+{
+ const std::string script = R"(
+ assert(Request.GenericAttributes["hello"] == "world")
+ assert(Request.GenericAttributes["foo"] == "bar")
+ assert(Request.GenericAttributes["kaboom"] == nil)
+ assert(#Request.GenericAttributes == 4)
+ for k, v in pairs(Request.GenericAttributes) do
+ assert(k)
+ assert(v)
+ end
+ )";
+
+ DEFINE_REQ_STATE;
+ s.generic_attrs["hello"] = "world";
+ s.generic_attrs["foo"] = "bar";
+ s.generic_attrs["goodbye"] = "cruel world";
+ s.generic_attrs["ka"] = "boom";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, Environment)
+{
+ const std::string script = R"(
+ assert(Request.Environment[""] == "bar")
+ assert(Request.Environment["goodbye"] == "cruel world")
+ assert(Request.Environment["ka"] == "boom")
+ assert(#Request.Environment == 3, #Request.Environment)
+ for k, v in pairs(Request.Environment) do
+ assert(k)
+ assert(v)
+ end
+ )";
+
+ DEFINE_REQ_STATE;
+ s.env.emplace("", "bar");
+ s.env.emplace("goodbye", "cruel world");
+ s.env.emplace("ka", "boom");
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, Tags)
+{
+ const std::string script = R"(
+ assert(#Request.Tags == 4)
+ assert(Request.Tags["foo"] == "bar")
+ for k, v in pairs(Request.Tags) do
+ assert(k)
+ assert(v)
+ end
+ )";
+
+ DEFINE_REQ_STATE;
+ s.tagset.add_tag("hello", "world");
+ s.tagset.add_tag("foo", "bar");
+ s.tagset.add_tag("goodbye", "cruel world");
+ s.tagset.add_tag("ka", "boom");
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, TagsNotWriteable)
+{
+ const std::string script = R"(
+ Request.Tags["hello"] = "goodbye"
+ )";
+
+ DEFINE_REQ_STATE;
+ s.tagset.add_tag("hello", "world");
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLua, Metadata)
+{
+ const std::string script = R"(
+ assert(#Request.HTTP.Metadata == 3)
+ for k, v in pairs(Request.HTTP.Metadata) do
+ assert(k)
+ assert(v)
+ end
+ assert(Request.HTTP.Metadata["hello"] == "world")
+ assert(Request.HTTP.Metadata["kaboom"] == nil)
+ Request.HTTP.Metadata["hello"] = "goodbye"
+ Request.HTTP.Metadata["kaboom"] = "boom"
+ assert(#Request.HTTP.Metadata == 4)
+ assert(Request.HTTP.Metadata["hello"] == "goodbye")
+ assert(Request.HTTP.Metadata["kaboom"] == "boom")
+ )";
+
+ DEFINE_REQ_STATE;
+ s.info.x_meta_map["hello"] = "world";
+ s.info.x_meta_map["foo"] = "bar";
+ s.info.x_meta_map["ka"] = "boom";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, Acl)
+{
+ const std::string script = R"(
+ function print_grant(g)
+ print("Grant Type: " .. g.Type)
+ print("Grant Group Type: " .. g.GroupType)
+ print("Grant Referer: " .. g.Referer)
+ if (g.User) then
+ print("Grant User.Tenant: " .. g.User.Tenant)
+ print("Grant User.Id: " .. g.User.Id)
+ end
+ end
+
+ assert(Request.UserAcl.Owner.DisplayName == "jack black", Request.UserAcl.Owner.DisplayName)
+ assert(Request.UserAcl.Owner.User.Id == "black", Request.UserAcl.Owner.User.Id)
+ assert(Request.UserAcl.Owner.User.Tenant == "jack", Request.UserAcl.Owner.User.Tenant)
+ assert(#Request.UserAcl.Grants == 5)
+ print_grant(Request.UserAcl.Grants[""])
+ for k, v in pairs(Request.UserAcl.Grants) do
+ print_grant(v)
+ if k == "john$doe" then
+ assert(v.Permission == 4)
+ elseif k == "jane$doe" then
+ assert(v.Permission == 1)
+ else
+ assert(false)
+ end
+ end
+ )";
+
+ DEFINE_REQ_STATE;
+ ACLOwner owner;
+ owner.set_id(rgw_user("jack", "black"));
+ owner.set_name("jack black");
+ s.user_acl.reset(new RGWAccessControlPolicy(g_cct));
+ s.user_acl->set_owner(owner);
+ ACLGrant grant1, grant2, grant3, grant4, grant5;
+ grant1.set_canon(rgw_user("jane", "doe"), "her grant", 1);
+ grant2.set_group(ACL_GROUP_ALL_USERS ,2);
+ grant3.set_referer("http://localhost/ref2", 3);
+ grant4.set_canon(rgw_user("john", "doe"), "his grant", 4);
+ grant5.set_group(ACL_GROUP_AUTHENTICATED_USERS, 5);
+ s.user_acl->get_acl().add_grant(&grant1);
+ s.user_acl->get_acl().add_grant(&grant2);
+ s.user_acl->get_acl().add_grant(&grant3);
+ s.user_acl->get_acl().add_grant(&grant4);
+ s.user_acl->get_acl().add_grant(&grant5);
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, User)
+{
+ const std::string script = R"(
+ assert(Request.User)
+ assert(Request.User.Id == "myid")
+ assert(Request.User.Tenant == "mytenant")
+ )";
+
+ DEFINE_REQ_STATE;
+
+ rgw_user u;
+ u.tenant = "mytenant";
+ u.id = "myid";
+ s.user.reset(new sal::RadosUser(nullptr, u));
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+
+TEST(TestRGWLua, UseFunction)
+{
+ const std::string script = R"(
+ function print_owner(owner)
+ print("Owner Dispaly Name: " .. owner.DisplayName)
+ print("Owner Id: " .. owner.User.Id)
+ print("Owner Tenanet: " .. owner.User.Tenant)
+ end
+
+ print_owner(Request.ObjectOwner)
+
+ function print_acl(acl_type)
+ index = acl_type .. "ACL"
+ acl = Request[index]
+ if acl then
+ print(acl_type .. "ACL Owner")
+ print_owner(acl.Owner)
+ else
+ print("no " .. acl_type .. " ACL in request: " .. Request.Id)
+ end
+ end
+
+ print_acl("User")
+ print_acl("Bucket")
+ print_acl("Object")
+ )";
+
+ DEFINE_REQ_STATE;
+ s.owner.set_name("user two");
+ s.owner.set_id(rgw_user("tenant2", "user2"));
+ s.user_acl.reset(new RGWAccessControlPolicy());
+ s.user_acl->get_owner().set_name("user three");
+ s.user_acl->get_owner().set_id(rgw_user("tenant3", "user3"));
+ s.bucket_acl.reset(new RGWAccessControlPolicy());
+ s.bucket_acl->get_owner().set_name("user four");
+ s.bucket_acl->get_owner().set_id(rgw_user("tenant4", "user4"));
+ s.object_acl.reset(new RGWAccessControlPolicy());
+ s.object_acl->get_owner().set_name("user five");
+ s.object_acl->get_owner().set_id(rgw_user("tenant5", "user5"));
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, WithLib)
+{
+ const std::string script = R"(
+ expected_result = {"my", "bucket", "name", "is", "fish"}
+ i = 1
+ for p in string.gmatch(Request.Bucket.Name, "%a+") do
+ assert(p == expected_result[i])
+ i = i + 1
+ end
+ )";
+
+ DEFINE_REQ_STATE;
+
+ rgw_bucket b;
+ b.name = "my-bucket-name-is-fish";
+ s.bucket.reset(new sal::RadosBucket(nullptr, b));
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, NotAllowedInLib)
+{
+ const std::string script = R"(
+ os.clock() -- this should be ok
+ os.exit() -- this should fail (os.exit() is removed)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_NE(rc, 0);
+}
+
+#define MAKE_STORE auto store = std::unique_ptr<sal::RadosStore>(new sal::RadosStore); \
+ store->setRados(new RGWRados);
+
+TEST(TestRGWLua, OpsLog)
+{
+ const std::string script = R"(
+ if Request.Response.HTTPStatusCode == 200 then
+ assert(Request.Response.Message == "Life is great")
+ else
+ assert(Request.Bucket)
+ assert(Request.Log() == 0)
+ end
+ )";
+
+ MAKE_STORE;
+
+ struct MockOpsLogSink : OpsLogSink {
+ bool logged = false;
+ int log(req_state*, rgw_log_entry&) override { logged = true; return 0; }
+ };
+ MockOpsLogSink olog;
+
+ DEFINE_REQ_STATE;
+ s.err.http_ret = 200;
+ s.err.ret = 0;
+ s.err.err_code = "200OK";
+ s.err.message = "Life is great";
+ rgw_bucket b;
+ b.tenant = "tenant";
+ b.name = "name";
+ b.marker = "marker";
+ b.bucket_id = "id";
+ s.bucket.reset(new sal::RadosBucket(nullptr, b));
+ s.bucket_name = "name";
+ s.enable_ops_log = true;
+ s.enable_usage_log = false;
+ s.user.reset(new TestUser());
+ TestAccounter ac;
+ s.cio = &ac;
+ s.cct->_conf->rgw_ops_log_rados = false;
+
+ s.auth.identity = std::unique_ptr<rgw::auth::Identity>(
+ new FakeIdentity());
+
+ auto rc = lua::request::execute(store.get(), nullptr, &olog, &s, nullptr, script);
+ EXPECT_EQ(rc, 0);
+ EXPECT_FALSE(olog.logged); // don't log http_ret=200
+
+ s.err.http_ret = 400;
+ rc = lua::request::execute(store.get(), nullptr, &olog, &s, nullptr, script);
+ EXPECT_EQ(rc, 0);
+ EXPECT_TRUE(olog.logged);
+}
+
+class TestBackground : public rgw::lua::Background {
+ const unsigned read_time;
+
+protected:
+ int read_script() override {
+ // don't read the object from the store
+ std::this_thread::sleep_for(std::chrono::seconds(read_time));
+ return 0;
+ }
+
+public:
+ TestBackground(sal::RadosStore* store, const std::string& script, unsigned read_time = 0) :
+ rgw::lua::Background(store, g_cct, "", /* luarocks path */ 1 /* run every second */),
+ read_time(read_time) {
+ // the script is passed in the constructor
+ rgw_script = script;
+ }
+
+ ~TestBackground() override {
+ shutdown();
+ }
+};
+
+TEST(TestRGWLuaBackground, Start)
+{
+ MAKE_STORE;
+ {
+ // ctr and dtor without running
+ TestBackground lua_background(store.get(), "");
+ }
+ {
+ // ctr and dtor with running
+ TestBackground lua_background(store.get(), "");
+ lua_background.start();
+ }
+}
+
+
+constexpr auto wait_time = std::chrono::seconds(3);
+
+template<typename T>
+const T& get_table_value(const TestBackground& b, const std::string& index) {
+ try {
+ return std::get<T>(b.get_table_value(index));
+ } catch (std::bad_variant_access const& ex) {
+ std::cout << "expected RGW[" << index << "] to be: " << typeid(T).name() << std::endl;
+ throw(ex);
+ }
+}
+
+TEST(TestRGWLuaBackground, Script)
+{
+ const std::string script = R"(
+ local key = "hello"
+ local value = "world"
+ RGW[key] = value
+ )";
+
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), script);
+ lua_background.start();
+ std::this_thread::sleep_for(wait_time);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "world");
+}
+
+TEST(TestRGWLuaBackground, RequestScript)
+{
+ const std::string background_script = R"(
+ local key = "hello"
+ local value = "from background"
+ RGW[key] = value
+ )";
+
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), background_script);
+ lua_background.start();
+ std::this_thread::sleep_for(wait_time);
+
+ const std::string request_script = R"(
+ local key = "hello"
+ assert(RGW[key] == "from background")
+ local value = "from request"
+ RGW[key] = value
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ // to make sure test is consistent we have to puase the background
+ lua_background.pause();
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "from request");
+ // now we resume and let the background set the value
+ lua_background.resume(store.get());
+ std::this_thread::sleep_for(wait_time);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "from background");
+}
+
+TEST(TestRGWLuaBackground, Pause)
+{
+ const std::string script = R"(
+ local key = "hello"
+ local value = "1"
+ if RGW[key] then
+ RGW[key] = value..RGW[key]
+ else
+ RGW[key] = value
+ end
+ )";
+
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), script);
+ lua_background.start();
+ std::this_thread::sleep_for(wait_time);
+ const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+ EXPECT_GT(value_len, 0);
+ lua_background.pause();
+ std::this_thread::sleep_for(wait_time);
+ // no change in len
+ EXPECT_EQ(value_len, get_table_value<std::string>(lua_background, "hello").size());
+}
+
+TEST(TestRGWLuaBackground, PauseWhileReading)
+{
+ const std::string script = R"(
+ local key = "hello"
+ local value = "world"
+ RGW[key] = value
+ if RGW[key] then
+ RGW[key] = value..RGW[key]
+ else
+ RGW[key] = value
+ end
+ )";
+
+ MAKE_STORE;
+ constexpr auto long_wait_time = std::chrono::seconds(6);
+ TestBackground lua_background(store.get(), script, 2);
+ lua_background.start();
+ std::this_thread::sleep_for(long_wait_time);
+ const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+ EXPECT_GT(value_len, 0);
+ lua_background.pause();
+ std::this_thread::sleep_for(long_wait_time);
+ // one execution might occur after pause
+ EXPECT_TRUE(value_len + 1 >= get_table_value<std::string>(lua_background, "hello").size());
+}
+
+TEST(TestRGWLuaBackground, ReadWhilePaused)
+{
+ const std::string script = R"(
+ local key = "hello"
+ local value = "world"
+ RGW[key] = value
+ )";
+
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), script);
+ lua_background.pause();
+ lua_background.start();
+ std::this_thread::sleep_for(wait_time);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "");
+ lua_background.resume(store.get());
+ std::this_thread::sleep_for(wait_time);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "world");
+}
+
+TEST(TestRGWLuaBackground, PauseResume)
+{
+ const std::string script = R"(
+ local key = "hello"
+ local value = "1"
+ if RGW[key] then
+ RGW[key] = value..RGW[key]
+ else
+ RGW[key] = value
+ end
+ )";
+
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), script);
+ lua_background.start();
+ std::this_thread::sleep_for(wait_time);
+ const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+ EXPECT_GT(value_len, 0);
+ lua_background.pause();
+ std::this_thread::sleep_for(wait_time);
+ // no change in len
+ EXPECT_EQ(value_len, get_table_value<std::string>(lua_background, "hello").size());
+ lua_background.resume(store.get());
+ std::this_thread::sleep_for(wait_time);
+ // should be a change in len
+ EXPECT_GT(get_table_value<std::string>(lua_background, "hello").size(), value_len);
+}
+
+TEST(TestRGWLuaBackground, MultipleStarts)
+{
+ const std::string script = R"(
+ local key = "hello"
+ local value = "1"
+ if RGW[key] then
+ RGW[key] = value..RGW[key]
+ else
+ RGW[key] = value
+ end
+ )";
+
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), script);
+ lua_background.start();
+ std::this_thread::sleep_for(wait_time);
+ const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+ EXPECT_GT(value_len, 0);
+ lua_background.start();
+ lua_background.shutdown();
+ lua_background.shutdown();
+ std::this_thread::sleep_for(wait_time);
+ lua_background.start();
+ std::this_thread::sleep_for(wait_time);
+ // should be a change in len
+ EXPECT_GT(get_table_value<std::string>(lua_background, "hello").size(), value_len);
+}
+
+TEST(TestRGWLuaBackground, TableValues)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ const std::string request_script = R"(
+ RGW["key1"] = "string value"
+ RGW["key2"] = 42
+ RGW["key3"] = 42.2
+ RGW["key4"] = true
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+ EXPECT_EQ(get_table_value<double>(lua_background, "key3"), 42.2);
+ EXPECT_TRUE(get_table_value<bool>(lua_background, "key4"));
+}
+
+TEST(TestRGWLuaBackground, TablePersist)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ std::string request_script = R"(
+ RGW["key1"] = "string value"
+ RGW["key2"] = 42
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+
+ request_script = R"(
+ RGW["key3"] = RGW["key1"]
+ RGW["key4"] = RGW["key2"]
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key3"), "string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key4"), 42);
+}
+
+TEST(TestRGWLuaBackground, TableValuesFromRequest)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+ lua_background.start();
+
+ const std::string request_script = R"(
+ RGW["key1"] = Request.Response.RGWCode
+ RGW["key2"] = Request.Response.Message
+ RGW["key3"] = Request.Response.RGWCode*0.1
+ RGW["key4"] = Request.Tags["key1"] == Request.Tags["key2"]
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ s.tagset.add_tag("key1", "val1");
+ s.tagset.add_tag("key2", "val1");
+ s.err.ret = -99;
+ s.err.message = "hi";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key1"), -99);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key2"), "hi");
+ EXPECT_EQ(get_table_value<double>(lua_background, "key3"), -9.9);
+ EXPECT_EQ(get_table_value<bool>(lua_background, "key4"), true);
+}
+
+TEST(TestRGWLuaBackground, TableInvalidValue)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+ lua_background.start();
+
+ const std::string request_script = R"(
+ RGW["key1"] = "val1"
+ RGW["key2"] = 42
+ RGW["key3"] = 42.2
+ RGW["key4"] = true
+ RGW["key5"] = Request.Tags
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+ s.tagset.add_tag("key1", "val1");
+ s.tagset.add_tag("key2", "val2");
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_NE(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "val1");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+ EXPECT_EQ(get_table_value<double>(lua_background, "key3"), 42.2);
+ EXPECT_EQ(get_table_value<bool>(lua_background, "key4"), true);
+}
+
+TEST(TestRGWLuaBackground, TableErase)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ std::string request_script = R"(
+ RGW["size"] = 0
+ RGW["key1"] = "string value"
+ RGW["key2"] = 42
+ RGW["key3"] = "another string value"
+ RGW["size"] = #RGW
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key3"), "another string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "size"), 4);
+
+ request_script = R"(
+ -- erase key1
+ RGW["key1"] = nil
+ -- following should be a no op
+ RGW["key4"] = nil
+ RGW["size"] = #RGW
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key3"), "another string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "size"), 3);
+}
+
+TEST(TestRGWLuaBackground, TableIterate)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ const std::string request_script = R"(
+ RGW["key1"] = "string value"
+ RGW["key2"] = 42
+ RGW["key3"] = 42.2
+ RGW["key4"] = true
+ RGW["size"] = 0
+ for k, v in pairs(RGW) do
+ RGW["size"] = RGW["size"] + 1
+ end
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+ EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+ EXPECT_EQ(get_table_value<double>(lua_background, "key3"), 42.2);
+ EXPECT_TRUE(get_table_value<bool>(lua_background, "key4"));
+ EXPECT_EQ(get_table_value<long long int>(lua_background, "size"), 5);
+}
+
+TEST(TestRGWLuaBackground, TableIncrement)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.increment("key1")
+ assert(RGW["key1"] == 43)
+ RGW.increment("key2")
+ assert(RGW["key2"] == 43.2)
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementBy)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.increment("key1", 10)
+ assert(RGW["key1"] == 52)
+ RGW.increment("key2", 10)
+ assert(RGW["key2"] == 52.2)
+ RGW.increment("key1", 0.2)
+ assert(RGW["key1"] == 52.2)
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableDecrement)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.decrement("key1")
+ assert(RGW["key1"] == 41)
+ RGW.decrement("key2")
+ assert(RGW["key2"] == 41.2)
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableDecrementBy)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.decrement("key1", 10)
+ assert(RGW["key1"] == 32)
+ RGW.decrement("key2", 10)
+ assert(RGW["key2"] == 32.2)
+ RGW.decrement("key1", 0.8)
+ assert(RGW["key1"] == 31.2)
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementValueError)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ std::string request_script = R"(
+ -- cannot increment string values
+ RGW["key1"] = "hello"
+ RGW.increment("key1")
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_NE(rc, 0);
+
+ request_script = R"(
+ -- cannot increment bool values
+ RGW["key1"] = true
+ RGW.increment("key1")
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_NE(rc, 0);
+
+ request_script = R"(
+ -- cannot increment by string values
+ RGW["key1"] = 99
+ RGW.increment("key1", "kaboom")
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementError)
+{
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+
+ std::string request_script = R"(
+ -- missing argument
+ RGW["key1"] = 11
+ RGW.increment()
+ )";
+
+ DEFINE_REQ_STATE;
+ pe.lua.background = &lua_background;
+
+ auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_NE(rc, 0);
+
+ request_script = R"(
+ -- used as settable field
+ RGW.increment = 11
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+ ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLua, TracingSetAttribute)
+{
+ const std::string script = R"(
+ Request.Trace.SetAttribute("str-attr", "value")
+ Request.Trace.SetAttribute("int-attr", 42)
+ Request.Trace.SetAttribute("double-attr", 42.5)
+ )";
+
+ DEFINE_REQ_STATE;
+ INIT_TRACE;
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, TracingSetBadAttribute)
+{
+ const std::string script = R"(
+ Request.Trace.SetAttribute("attr", nil)
+ )";
+
+ DEFINE_REQ_STATE;
+ INIT_TRACE;
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ #ifdef HAVE_JAEGER
+ ASSERT_NE(rc, 0);
+ #else
+ ASSERT_EQ(rc, 0);
+ #endif
+}
+
+TEST(TestRGWLua, TracingAddEvent)
+{
+ const std::string script = R"(
+ event_attrs = {}
+ event_attrs["x"] = "value-x"
+ event_attrs[42] = 42
+ event_attrs[42.5] = 42.5
+ event_attrs["y"] = "value-y"
+
+ Request.Trace.AddEvent("my_event", event_attrs)
+ )";
+
+ DEFINE_REQ_STATE;
+ INIT_TRACE;
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, Data)
+{
+ const std::string script = R"(
+ local expected = "The quick brown fox jumps over the lazy dog"
+ local actual = ""
+ RGW["key1"] = 0
+
+ for i, c in pairs(Data) do
+ actual = actual .. c
+ RGW.increment("key1")
+ end
+ assert(expected == actual)
+ assert(#Data == #expected);
+ assert(RGW["key1"] == #Data)
+ assert(Request.RGWId == "foo")
+ assert(Offset == 12345678)
+ )";
+
+ MAKE_STORE;
+ TestBackground lua_background(store.get(), "");
+ DEFINE_REQ_STATE;
+ s.host_id = "foo";
+ pe.lua.background = &lua_background;
+ lua::RGWObjFilter filter(&s, script);
+ bufferlist bl;
+ bl.append("The quick brown fox jumps over the lazy dog");
+ off_t offset = 12345678;
+ const auto rc = filter.execute(bl, offset, "put_obj");
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, WriteDataFail)
+{
+ const std::string script = R"(
+ Data[1] = "h"
+ Data[2] = "e"
+ Data[3] = "l"
+ Data[4] = "l"
+ Data[5] = "o"
+ )";
+
+ DEFINE_REQ_STATE;
+ lua::RGWObjFilter filter(&s, script);
+ bufferlist bl;
+ bl.append("The quick brown fox jumps over the lazy dog");
+ const auto rc = filter.execute(bl, 0, "put_obj");
+ ASSERT_NE(rc, 0);
+}
+
diff --git a/src/test/rgw/test_rgw_manifest.cc b/src/test/rgw/test_rgw_manifest.cc
new file mode 100644
index 000000000..acde46d44
--- /dev/null
+++ b/src/test/rgw/test_rgw_manifest.cc
@@ -0,0 +1,397 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "rgw_common.h"
+#include "rgw_rados.h"
+#include "test_rgw_common.h"
+#include <gtest/gtest.h>
+
+using namespace std;
+
+auto cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+const DoutPrefix dp(cct, 1, "test rgw manifest: ");
+
+struct OldObjManifestPart {
+ old_rgw_obj loc; /* the object where the data is located */
+ uint64_t loc_ofs; /* the offset at that object where the data is located */
+ uint64_t size; /* the part size */
+
+ OldObjManifestPart() : loc_ofs(0), size(0) {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(2, 2, bl);
+ encode(loc, bl);
+ encode(loc_ofs, bl);
+ encode(size, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START_LEGACY_COMPAT_LEN_32(2, 2, 2, bl);
+ decode(loc, bl);
+ decode(loc_ofs, bl);
+ decode(size, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<OldObjManifestPart*>& o);
+};
+WRITE_CLASS_ENCODER(OldObjManifestPart)
+
+class OldObjManifest {
+protected:
+ map<uint64_t, OldObjManifestPart> objs;
+
+ uint64_t obj_size;
+public:
+
+ OldObjManifest() : obj_size(0) {}
+ OldObjManifest(const OldObjManifest& rhs) {
+ *this = rhs;
+ }
+ OldObjManifest& operator=(const OldObjManifest& rhs) {
+ objs = rhs.objs;
+ obj_size = rhs.obj_size;
+ return *this;
+ }
+
+ const map<uint64_t, OldObjManifestPart>& get_objs() {
+ return objs;
+ }
+
+ void append(uint64_t ofs, const OldObjManifestPart& part) {
+ objs[ofs] = part;
+ obj_size = std::max(obj_size, ofs + part.size);
+ }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(2, 2, bl);
+ encode(obj_size, bl);
+ encode(objs, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START_LEGACY_COMPAT_LEN_32(6, 2, 2, bl);
+ decode(obj_size, bl);
+ decode(objs, bl);
+ DECODE_FINISH(bl);
+ }
+
+ bool empty() {
+ return objs.empty();
+ }
+};
+WRITE_CLASS_ENCODER(OldObjManifest)
+
+void append_head(list<rgw_obj> *objs, rgw_obj& head)
+{
+ objs->push_back(head);
+}
+
+void append_stripes(list<rgw_obj> *objs, RGWObjManifest& manifest, uint64_t obj_size, uint64_t stripe_size)
+{
+ string prefix = manifest.get_prefix();
+ rgw_bucket bucket = manifest.get_obj().bucket;
+
+ int i = 0;
+ for (uint64_t ofs = manifest.get_max_head_size(); ofs < obj_size; ofs += stripe_size) {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", ++i);
+ string oid = prefix + buf;
+ cout << "oid=" << oid << std::endl;
+ rgw_obj obj;
+ obj.init_ns(bucket, oid, "shadow");
+ objs->push_back(obj);
+ }
+}
+
+static void gen_obj(test_rgw_env& env, uint64_t obj_size, uint64_t head_max_size, uint64_t stripe_size,
+ RGWObjManifest *manifest, const rgw_placement_rule& placement_rule, rgw_bucket *bucket, rgw_obj *head, RGWObjManifest::generator *gen,
+ list<rgw_obj> *test_objs)
+{
+ manifest->set_trivial_rule(head_max_size, stripe_size);
+
+ test_rgw_init_bucket(bucket, "buck");
+
+ *head = rgw_obj(*bucket, "oid");
+ gen->create_begin(g_ceph_context, manifest, placement_rule, nullptr, *bucket, *head);
+
+ append_head(test_objs, *head);
+ cout << "test_objs.size()=" << test_objs->size() << std::endl;
+ append_stripes(test_objs, *manifest, obj_size, stripe_size);
+
+ cout << "test_objs.size()=" << test_objs->size() << std::endl;
+
+ ASSERT_EQ((int)manifest->get_obj_size(), 0);
+ ASSERT_EQ((int)manifest->get_head_size(), 0);
+ ASSERT_EQ(manifest->has_tail(), false);
+
+ uint64_t ofs = 0;
+ list<rgw_obj>::iterator iter = test_objs->begin();
+
+ while (ofs < obj_size) {
+ rgw_raw_obj obj = gen->get_cur_obj(env.zonegroup, env.zone_params);
+ cout << "obj=" << obj << std::endl;
+ rgw_raw_obj test_raw = rgw_obj_select(*iter).get_raw_obj(env.zonegroup, env.zone_params);
+ ASSERT_TRUE(obj == test_raw);
+
+ ofs = std::min(ofs + gen->cur_stripe_max_size(), obj_size);
+ gen->create_next(ofs);
+
+ cout << "obj=" << obj << " *iter=" << *iter << std::endl;
+ cout << "test_objs.size()=" << test_objs->size() << std::endl;
+ ++iter;
+
+ }
+
+ if (manifest->has_tail()) {
+ rgw_raw_obj obj = gen->get_cur_obj(env.zonegroup, env.zone_params);
+ rgw_raw_obj test_raw = rgw_obj_select(*iter).get_raw_obj(env.zonegroup, env.zone_params);
+ ASSERT_TRUE(obj == test_raw);
+ ++iter;
+ }
+ ASSERT_TRUE(iter == test_objs->end());
+ ASSERT_EQ(manifest->get_obj_size(), obj_size);
+ ASSERT_EQ(manifest->get_head_size(), std::min(obj_size, head_max_size));
+ ASSERT_EQ(manifest->has_tail(), (obj_size > head_max_size));
+}
+
+static void gen_old_obj(test_rgw_env& env, uint64_t obj_size, uint64_t head_max_size, uint64_t stripe_size,
+ OldObjManifest *manifest, old_rgw_bucket *bucket, old_rgw_obj *head,
+ list<old_rgw_obj> *test_objs)
+{
+ test_rgw_init_old_bucket(bucket, "buck");
+
+ *head = old_rgw_obj(*bucket, "obj");
+
+ OldObjManifestPart part;
+ part.loc = *head;
+ part.size = head_max_size;
+ part.loc_ofs = 0;
+
+ manifest->append(0, part);
+ test_objs->push_back(part.loc);
+
+ string prefix;
+ append_rand_alpha(g_ceph_context, prefix, prefix, 16);
+
+ int i = 0;
+ for (uint64_t ofs = head_max_size; ofs < obj_size; ofs += stripe_size, i++) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%s.%d", prefix.c_str(), i);
+ old_rgw_obj loc(*bucket, buf);
+ loc.set_ns("shadow");
+ OldObjManifestPart part;
+ part.loc = loc;
+ part.size = min(stripe_size, obj_size - ofs);
+ part.loc_ofs = 0;
+
+ manifest->append(ofs, part);
+
+ test_objs->push_back(loc);
+ }
+}
+
+TEST(TestRGWManifest, head_only_obj) {
+ test_rgw_env env;
+ RGWObjManifest manifest;
+ rgw_bucket bucket;
+ rgw_obj head;
+ RGWObjManifest::generator gen;
+
+ int obj_size = 256 * 1024;
+
+ list<rgw_obj> objs;
+
+ gen_obj(env, obj_size, 512 * 1024, 4 * 1024 * 1024, &manifest, env.zonegroup.default_placement, &bucket, &head, &gen, &objs);
+
+ cout << " manifest.get_obj_size()=" << manifest.get_obj_size() << std::endl;
+ cout << " manifest.get_head_size()=" << manifest.get_head_size() << std::endl;
+ list<rgw_obj>::iterator liter;
+
+ RGWObjManifest::obj_iterator iter;
+ for (iter = manifest.obj_begin(&dp), liter = objs.begin();
+ iter != manifest.obj_end(&dp) && liter != objs.end();
+ ++iter, ++liter) {
+ ASSERT_TRUE(env.get_raw(*liter) == env.get_raw(iter.get_location()));
+ }
+
+ ASSERT_TRUE(iter == manifest.obj_end(&dp));
+ ASSERT_TRUE(liter == objs.end());
+
+ rgw_raw_obj raw_head;
+
+ iter = manifest.obj_find(&dp, 100 * 1024);
+ ASSERT_TRUE(env.get_raw(iter.get_location()) == env.get_raw(head));
+ ASSERT_EQ((int)iter.get_stripe_size(), obj_size);
+}
+
+TEST(TestRGWManifest, obj_with_head_and_tail) {
+ test_rgw_env env;
+ RGWObjManifest manifest;
+ rgw_bucket bucket;
+ rgw_obj head;
+ RGWObjManifest::generator gen;
+
+ list<rgw_obj> objs;
+
+ int obj_size = 21 * 1024 * 1024 + 1000;
+ int stripe_size = 4 * 1024 * 1024;
+ int head_size = 512 * 1024;
+
+ gen_obj(env, obj_size, head_size, stripe_size, &manifest, env.zonegroup.default_placement, &bucket, &head, &gen, &objs);
+
+ list<rgw_obj>::iterator liter;
+
+ rgw_obj_select last_obj;
+
+ RGWObjManifest::obj_iterator iter;
+ for (iter = manifest.obj_begin(&dp), liter = objs.begin();
+ iter != manifest.obj_end(&dp) && liter != objs.end();
+ ++iter, ++liter) {
+ cout << "*liter=" << *liter << " iter.get_location()=" << env.get_raw(iter.get_location()) << std::endl;
+ ASSERT_TRUE(env.get_raw(*liter) == env.get_raw(iter.get_location()));
+
+ last_obj = iter.get_location();
+ }
+
+ ASSERT_TRUE(iter == manifest.obj_end(&dp));
+ ASSERT_TRUE(liter == objs.end());
+
+ iter = manifest.obj_find(&dp, 100 * 1024);
+ ASSERT_TRUE(env.get_raw(iter.get_location()) == env.get_raw(head));
+ ASSERT_EQ((int)iter.get_stripe_size(), head_size);
+
+ uint64_t ofs = 20 * 1024 * 1024 + head_size;
+ iter = manifest.obj_find(&dp, ofs + 100);
+
+ ASSERT_TRUE(env.get_raw(iter.get_location()) == env.get_raw(last_obj));
+ ASSERT_EQ(iter.get_stripe_ofs(), ofs);
+ ASSERT_EQ(iter.get_stripe_size(), obj_size - ofs);
+}
+
+TEST(TestRGWManifest, multipart) {
+ test_rgw_env env;
+ int num_parts = 16;
+ vector <RGWObjManifest> pm(num_parts);
+ rgw_bucket bucket;
+ uint64_t part_size = 10 * 1024 * 1024;
+ uint64_t stripe_size = 4 * 1024 * 1024;
+
+ string upload_id = "abc123";
+
+ for (int i = 0; i < num_parts; ++i) {
+ RGWObjManifest& manifest = pm[i];
+ RGWObjManifest::generator gen;
+ manifest.set_prefix(upload_id);
+
+ manifest.set_multipart_part_rule(stripe_size, i + 1);
+
+ uint64_t ofs;
+ rgw_obj head;
+ for (ofs = 0; ofs < part_size; ofs += stripe_size) {
+ if (ofs == 0) {
+ rgw_placement_rule rule(env.zonegroup.default_placement.name, RGW_STORAGE_CLASS_STANDARD);
+ int r = gen.create_begin(g_ceph_context, &manifest, rule, nullptr, bucket, head);
+ ASSERT_EQ(r, 0);
+ continue;
+ }
+ gen.create_next(ofs);
+ }
+
+ if (ofs > part_size) {
+ gen.create_next(part_size);
+ }
+ }
+
+ RGWObjManifest m;
+
+ for (int i = 0; i < num_parts; i++) {
+ m.append(&dp, pm[i], env.zonegroup, env.zone_params);
+ }
+ RGWObjManifest::obj_iterator iter;
+ for (iter = m.obj_begin(&dp); iter != m.obj_end(&dp); ++iter) {
+ RGWObjManifest::obj_iterator fiter = m.obj_find(&dp, iter.get_ofs());
+ ASSERT_TRUE(env.get_raw(fiter.get_location()) == env.get_raw(iter.get_location()));
+ }
+
+ ASSERT_EQ(m.get_obj_size(), num_parts * part_size);
+}
+
+TEST(TestRGWManifest, old_obj_manifest) {
+ test_rgw_env env;
+ OldObjManifest old_manifest;
+ old_rgw_bucket old_bucket;
+ old_rgw_obj old_head;
+
+ int obj_size = 40 * 1024 * 1024;
+ uint64_t stripe_size = 4 * 1024 * 1024;
+ uint64_t head_size = 512 * 1024;
+
+ list<old_rgw_obj> old_objs;
+
+ gen_old_obj(env, obj_size, head_size, stripe_size, &old_manifest, &old_bucket, &old_head, &old_objs);
+
+ ASSERT_EQ(old_objs.size(), 11u);
+
+
+ bufferlist bl;
+ encode(old_manifest , bl);
+
+ RGWObjManifest manifest;
+
+ try {
+ auto iter = bl.cbegin();
+ decode(manifest, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(false);
+ }
+
+ rgw_raw_obj last_obj;
+
+ RGWObjManifest::obj_iterator iter;
+ auto liter = old_objs.begin();
+ for (iter = manifest.obj_begin(&dp);
+ iter != manifest.obj_end(&dp) && liter != old_objs.end();
+ ++iter, ++liter) {
+ rgw_pool old_pool(liter->bucket.data_pool);
+ string old_oid;
+ prepend_old_bucket_marker(old_bucket, liter->get_object(), old_oid);
+ rgw_raw_obj raw_old(old_pool, old_oid);
+ cout << "*liter=" << raw_old << " iter.get_location()=" << env.get_raw(iter.get_location()) << std::endl;
+ ASSERT_EQ(raw_old, env.get_raw(iter.get_location()));
+
+ last_obj = env.get_raw(iter.get_location());
+ }
+
+ ASSERT_TRUE(liter == old_objs.end());
+ ASSERT_TRUE(iter == manifest.obj_end(&dp));
+
+}
+
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
diff --git a/src/test/rgw/test_rgw_obj.cc b/src/test/rgw/test_rgw_obj.cc
new file mode 100644
index 000000000..53d7897ae
--- /dev/null
+++ b/src/test/rgw/test_rgw_obj.cc
@@ -0,0 +1,272 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include "common/ceph_json.h"
+#include "common/Formatter.h"
+#include "rgw_common.h"
+#include "rgw_rados.h"
+#include "services/svc_tier_rados.h"
+#include "test_rgw_common.h"
+#include <gtest/gtest.h>
+
+using namespace std;
+
+void check_parsed_correctly(rgw_obj& obj, const string& name, const string& ns, const string& instance)
+{
+ /* parse_raw_oid() */
+ rgw_obj_key parsed_key;
+ ASSERT_EQ(true, rgw_obj_key::parse_raw_oid(obj.get_oid(), &parsed_key));
+
+ cout << "parsed: " << parsed_key << std::endl;
+
+ ASSERT_EQ(name, parsed_key.name);
+ ASSERT_EQ(ns, parsed_key.ns);
+ ASSERT_EQ(instance, parsed_key.instance);
+
+ /* translate_raw_obj_to_obj_in_ns() */
+ rgw_obj_key tkey = parsed_key;
+ string tns = ns + "foo";
+ ASSERT_EQ(0, rgw_obj_key::oid_to_key_in_ns(obj.get_oid(), &tkey, tns));
+
+ tkey = rgw_obj_key();
+ tns = ns;
+ ASSERT_EQ(true, rgw_obj_key::oid_to_key_in_ns(obj.get_oid(), &tkey, tns));
+
+ cout << "parsed: " << tkey << std::endl;
+
+ ASSERT_EQ(obj.key, tkey);
+
+ /* strip_namespace_from_object() */
+
+ string strip_name = obj.get_oid();
+ string strip_ns, strip_instance;
+
+ ASSERT_EQ(true, rgw_obj_key::strip_namespace_from_name(strip_name, strip_ns, strip_instance));
+
+ cout << "stripped: " << strip_name << " ns=" << strip_ns << " i=" << strip_instance << std::endl;
+
+ ASSERT_EQ(name, strip_name);
+ ASSERT_EQ(ns, strip_ns);
+ ASSERT_EQ(instance, strip_instance);
+}
+
+void test_obj(const string& name, const string& ns, const string& instance)
+{
+ rgw_bucket b;
+ test_rgw_init_bucket(&b, "test");
+
+ JSONFormatter *formatter = new JSONFormatter(true);
+
+ formatter->open_object_section("test");
+ rgw_obj o(b, name);
+ rgw_obj obj1(o);
+
+ if (!instance.empty()) {
+ obj1.key.instance = instance;
+ }
+ if (!ns.empty()) {
+ obj1.key.ns = ns;
+ }
+
+ check_parsed_correctly(obj1, name, ns, instance);
+ encode_json("obj1", obj1, formatter);
+
+ bufferlist bl;
+ encode(obj1, bl);
+
+ rgw_obj obj2;
+ decode(obj2, bl);
+ check_parsed_correctly(obj2, name, ns, instance);
+
+ encode_json("obj2", obj2, formatter);
+
+ rgw_obj obj3(o);
+ bufferlist bl3;
+ encode(obj3, bl3);
+ decode(obj3, bl3);
+ encode_json("obj3", obj3, formatter);
+
+ if (!instance.empty()) {
+ obj3.key.instance = instance;
+ }
+ if (!ns.empty()) {
+ obj3.key.ns = ns;
+ }
+ check_parsed_correctly(obj3, name, ns, instance);
+
+ encode_json("obj3-2", obj3, formatter);
+
+ formatter->close_section();
+
+ formatter->flush(cout);
+
+ ASSERT_EQ(obj1, obj2);
+ ASSERT_EQ(obj1, obj3);
+
+
+ /* rgw_obj_key conversion */
+ rgw_obj_index_key k;
+ obj1.key.get_index_key(&k);
+
+ rgw_obj new_obj(b, k);
+
+ ASSERT_EQ(obj1, new_obj);
+
+ delete formatter;
+}
+
+TEST(TestRGWObj, underscore) {
+ test_obj("_obj", "", "");
+ test_obj("_obj", "ns", "");
+ test_obj("_obj", "", "v1");
+ test_obj("_obj", "ns", "v1");
+}
+
+TEST(TestRGWObj, no_underscore) {
+ test_obj("obj", "", "");
+ test_obj("obj", "ns", "");
+ test_obj("obj", "", "v1");
+ test_obj("obj", "ns", "v1");
+}
+
+template <class T>
+void dump(JSONFormatter& f, const string& name, const T& entity)
+{
+ f.open_object_section(name.c_str());
+ ::encode_json(name.c_str(), entity, &f);
+ f.close_section();
+ f.flush(cout);
+}
+
+static void test_obj_to_raw(test_rgw_env& env, const rgw_bucket& b,
+ const string& name, const string& instance, const string& ns,
+ const string& placement_id)
+{
+ JSONFormatter f(true);
+ dump(f, "bucket", b);
+ rgw_obj obj = test_rgw_create_obj(b, name, instance, ns);
+ dump(f, "obj", obj);
+
+ rgw_obj_select s(obj);
+ rgw_raw_obj raw_obj = s.get_raw_obj(env.zonegroup, env.zone_params);
+ dump(f, "raw_obj", raw_obj);
+
+ if (!placement_id.empty()) {
+ ASSERT_EQ(raw_obj.pool, env.get_placement(placement_id).data_pool);
+ } else {
+ ASSERT_EQ(raw_obj.pool, b.explicit_placement.data_pool);
+ }
+ ASSERT_EQ(raw_obj.oid, test_rgw_get_obj_oid(obj));
+
+ rgw_obj new_obj;
+ RGWSI_Tier_RADOS::raw_obj_to_obj(b, raw_obj, &new_obj);
+
+ dump(f, "new_obj", new_obj);
+
+ ASSERT_EQ(obj, new_obj);
+
+}
+
+TEST(TestRGWObj, obj_to_raw) {
+ test_rgw_env env;
+
+ rgw_bucket b;
+ test_rgw_init_bucket(&b, "test");
+
+ rgw_bucket eb;
+ test_rgw_init_explicit_placement_bucket(&eb, "ebtest");
+
+ for (auto name : { "myobj", "_myobj", "_myobj_"}) {
+ for (auto inst : { "", "inst"}) {
+ for (auto ns : { "", "ns"}) {
+ test_obj_to_raw(env, b, name, inst, ns, env.zonegroup.default_placement.name);
+ test_obj_to_raw(env, eb, name, inst, ns, string());
+ }
+ }
+ }
+}
+
+TEST(TestRGWObj, old_to_raw) {
+ JSONFormatter f(true);
+ test_rgw_env env;
+
+ old_rgw_bucket eb;
+ test_rgw_init_old_bucket(&eb, "ebtest");
+
+ for (auto name : { "myobj", "_myobj", "_myobj_"}) {
+ for (string inst : { "", "inst"}) {
+ for (string ns : { "", "ns"}) {
+ old_rgw_obj old(eb, name);
+ if (!inst.empty()) {
+ old.set_instance(inst);
+ }
+ if (!ns.empty()) {
+ old.set_ns(ns);
+ }
+
+ bufferlist bl;
+
+ encode(old, bl);
+
+ rgw_obj new_obj;
+ rgw_raw_obj raw_obj;
+
+ try {
+ auto iter = bl.cbegin();
+ decode(new_obj, iter);
+
+ iter = bl.begin();
+ decode(raw_obj, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(false);
+ }
+
+ bl.clear();
+
+ rgw_obj new_obj2;
+ rgw_raw_obj raw_obj2;
+
+ encode(new_obj, bl);
+
+ dump(f, "raw_obj", raw_obj);
+ dump(f, "new_obj", new_obj);
+ cout << "raw=" << raw_obj << std::endl;
+
+ try {
+ auto iter = bl.cbegin();
+ decode(new_obj2, iter);
+
+ /*
+ can't decode raw obj here, because we didn't encode an old versioned
+ object
+ */
+
+ bl.clear();
+ encode(raw_obj, bl);
+ iter = bl.begin();
+ decode(raw_obj2, iter);
+ } catch (buffer::error& err) {
+ ASSERT_TRUE(false);
+ }
+
+ dump(f, "raw_obj2", raw_obj2);
+ dump(f, "new_obj2", new_obj2);
+ cout << "raw2=" << raw_obj2 << std::endl;
+
+ ASSERT_EQ(new_obj, new_obj2);
+ ASSERT_EQ(raw_obj, raw_obj2);
+ }
+ }
+ }
+}
diff --git a/src/test/rgw/test_rgw_period_history.cc b/src/test/rgw/test_rgw_period_history.cc
new file mode 100644
index 000000000..25ea87d3a
--- /dev/null
+++ b/src/test/rgw/test_rgw_period_history.cc
@@ -0,0 +1,336 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "rgw_period_history.h"
+#include "rgw_rados.h"
+#include "rgw_zone.h"
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include <boost/lexical_cast.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+namespace {
+
+// construct a period with the given fields
+RGWPeriod make_period(const std::string& id, epoch_t realm_epoch,
+ const std::string& predecessor)
+{
+ RGWPeriod period(id);
+ period.set_realm_epoch(realm_epoch);
+ period.set_predecessor(predecessor);
+ return period;
+}
+
+const auto current_period = make_period("5", 5, "4");
+
+// mock puller that throws an exception if it's called
+struct ErrorPuller : public RGWPeriodHistory::Puller {
+ int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override {
+ throw std::runtime_error("unexpected call to pull");
+ }
+};
+ErrorPuller puller; // default puller
+
+// mock puller that records the period ids requested and returns an error
+using Ids = std::vector<std::string>;
+class RecordingPuller : public RGWPeriodHistory::Puller {
+ const int error;
+ public:
+ explicit RecordingPuller(int error) : error(error) {}
+ Ids ids;
+ int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override {
+ ids.push_back(id);
+ return error;
+ }
+};
+
+// mock puller that returns a fake period by parsing the period id
+struct NumericPuller : public RGWPeriodHistory::Puller {
+ int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override {
+ // relies on numeric period ids to divine the realm_epoch
+ auto realm_epoch = boost::lexical_cast<epoch_t>(id);
+ auto predecessor = boost::lexical_cast<std::string>(realm_epoch-1);
+ period = make_period(id, realm_epoch, predecessor);
+ return 0;
+ }
+};
+
+} // anonymous namespace
+
+// for ASSERT_EQ()
+bool operator==(const RGWPeriod& lhs, const RGWPeriod& rhs)
+{
+ return lhs.get_id() == rhs.get_id()
+ && lhs.get_realm_epoch() == rhs.get_realm_epoch();
+}
+
+TEST(PeriodHistory, InsertBefore)
+{
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+
+ // inserting right before current_period 5 will attach to history
+ auto c = history.insert(make_period("4", 4, "3"));
+ ASSERT_TRUE(c);
+ ASSERT_FALSE(c.has_prev());
+ ASSERT_TRUE(c.has_next());
+
+ // cursor can traverse forward to current_period
+ c.next();
+ ASSERT_EQ(5u, c.get_epoch());
+ ASSERT_EQ(current_period, c.get_period());
+}
+
+TEST(PeriodHistory, InsertAfter)
+{
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+
+ // inserting right after current_period 5 will attach to history
+ auto c = history.insert(make_period("6", 6, "5"));
+ ASSERT_TRUE(c);
+ ASSERT_TRUE(c.has_prev());
+ ASSERT_FALSE(c.has_next());
+
+ // cursor can traverse back to current_period
+ c.prev();
+ ASSERT_EQ(5u, c.get_epoch());
+ ASSERT_EQ(current_period, c.get_period());
+}
+
+TEST(PeriodHistory, InsertWayBefore)
+{
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+
+ // inserting way before current_period 5 will not attach to history
+ auto c = history.insert(make_period("1", 1, ""));
+ ASSERT_FALSE(c);
+ ASSERT_EQ(0, c.get_error());
+}
+
+TEST(PeriodHistory, InsertWayAfter)
+{
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+
+ // inserting way after current_period 5 will not attach to history
+ auto c = history.insert(make_period("9", 9, "8"));
+ ASSERT_FALSE(c);
+ ASSERT_EQ(0, c.get_error());
+}
+
+TEST(PeriodHistory, PullPredecessorsBeforeCurrent)
+{
+ RecordingPuller puller{-EFAULT};
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+ const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
+
+ // create a disjoint history at 1 and verify that periods are requested
+ // backwards from current_period
+ auto c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
+ ASSERT_FALSE(c1);
+ ASSERT_EQ(-EFAULT, c1.get_error());
+ ASSERT_EQ(Ids{"4"}, puller.ids);
+
+ auto c4 = history.insert(make_period("4", 4, "3"));
+ ASSERT_TRUE(c4);
+
+ c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
+ ASSERT_FALSE(c1);
+ ASSERT_EQ(-EFAULT, c1.get_error());
+ ASSERT_EQ(Ids({"4", "3"}), puller.ids);
+
+ auto c3 = history.insert(make_period("3", 3, "2"));
+ ASSERT_TRUE(c3);
+
+ c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
+ ASSERT_FALSE(c1);
+ ASSERT_EQ(-EFAULT, c1.get_error());
+ ASSERT_EQ(Ids({"4", "3", "2"}), puller.ids);
+
+ auto c2 = history.insert(make_period("2", 2, "1"));
+ ASSERT_TRUE(c2);
+
+ c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
+ ASSERT_TRUE(c1);
+ ASSERT_EQ(Ids({"4", "3", "2"}), puller.ids);
+}
+
+TEST(PeriodHistory, PullPredecessorsAfterCurrent)
+{
+ RecordingPuller puller{-EFAULT};
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+ const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
+
+ // create a disjoint history at 9 and verify that periods are requested
+ // backwards down to current_period
+ auto c9 = history.attach(&dp, make_period("9", 9, "8"), null_yield);
+ ASSERT_FALSE(c9);
+ ASSERT_EQ(-EFAULT, c9.get_error());
+ ASSERT_EQ(Ids{"8"}, puller.ids);
+
+ auto c8 = history.attach(&dp, make_period("8", 8, "7"), null_yield);
+ ASSERT_FALSE(c8);
+ ASSERT_EQ(-EFAULT, c8.get_error());
+ ASSERT_EQ(Ids({"8", "7"}), puller.ids);
+
+ auto c7 = history.attach(&dp, make_period("7", 7, "6"), null_yield);
+ ASSERT_FALSE(c7);
+ ASSERT_EQ(-EFAULT, c7.get_error());
+ ASSERT_EQ(Ids({"8", "7", "6"}), puller.ids);
+
+ auto c6 = history.attach(&dp, make_period("6", 6, "5"), null_yield);
+ ASSERT_TRUE(c6);
+ ASSERT_EQ(Ids({"8", "7", "6"}), puller.ids);
+}
+
+TEST(PeriodHistory, MergeBeforeCurrent)
+{
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+
+ auto c = history.get_current();
+ ASSERT_FALSE(c.has_prev());
+
+ // create a disjoint history at 3
+ auto c3 = history.insert(make_period("3", 3, "2"));
+ ASSERT_FALSE(c3);
+
+ // insert the missing period to merge 3 and 5
+ auto c4 = history.insert(make_period("4", 4, "3"));
+ ASSERT_TRUE(c4);
+ ASSERT_TRUE(c4.has_prev());
+ ASSERT_TRUE(c4.has_next());
+
+ // verify that the merge didn't destroy the original cursor's history
+ ASSERT_EQ(current_period, c.get_period());
+ ASSERT_TRUE(c.has_prev());
+}
+
+TEST(PeriodHistory, MergeAfterCurrent)
+{
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+
+ auto c = history.get_current();
+ ASSERT_FALSE(c.has_next());
+
+ // create a disjoint history at 7
+ auto c7 = history.insert(make_period("7", 7, "6"));
+ ASSERT_FALSE(c7);
+
+ // insert the missing period to merge 5 and 7
+ auto c6 = history.insert(make_period("6", 6, "5"));
+ ASSERT_TRUE(c6);
+ ASSERT_TRUE(c6.has_prev());
+ ASSERT_TRUE(c6.has_next());
+
+ // verify that the merge didn't destroy the original cursor's history
+ ASSERT_EQ(current_period, c.get_period());
+ ASSERT_TRUE(c.has_next());
+}
+
+TEST(PeriodHistory, MergeWithoutCurrent)
+{
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+
+ // create a disjoint history at 7
+ auto c7 = history.insert(make_period("7", 7, "6"));
+ ASSERT_FALSE(c7);
+
+ // create a disjoint history at 9
+ auto c9 = history.insert(make_period("9", 9, "8"));
+ ASSERT_FALSE(c9);
+
+ // insert the missing period to merge 7 and 9
+ auto c8 = history.insert(make_period("8", 8, "7"));
+ ASSERT_FALSE(c8); // not connected to current_period yet
+
+ // insert the missing period to merge 5 and 7-9
+ auto c = history.insert(make_period("6", 6, "5"));
+ ASSERT_TRUE(c);
+ ASSERT_TRUE(c.has_next());
+
+ // verify that we merged all periods from 5-9
+ c.next();
+ ASSERT_EQ(7u, c.get_epoch());
+ ASSERT_TRUE(c.has_next());
+ c.next();
+ ASSERT_EQ(8u, c.get_epoch());
+ ASSERT_TRUE(c.has_next());
+ c.next();
+ ASSERT_EQ(9u, c.get_epoch());
+ ASSERT_FALSE(c.has_next());
+}
+
+TEST(PeriodHistory, AttachBefore)
+{
+ NumericPuller puller;
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+ const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
+
+ auto c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
+ ASSERT_TRUE(c1);
+
+ // verify that we pulled and merged all periods from 1-5
+ auto c = history.get_current();
+ ASSERT_TRUE(c);
+ ASSERT_TRUE(c.has_prev());
+ c.prev();
+ ASSERT_EQ(4u, c.get_epoch());
+ ASSERT_TRUE(c.has_prev());
+ c.prev();
+ ASSERT_EQ(3u, c.get_epoch());
+ ASSERT_TRUE(c.has_prev());
+ c.prev();
+ ASSERT_EQ(2u, c.get_epoch());
+ ASSERT_TRUE(c.has_prev());
+ c.prev();
+ ASSERT_EQ(1u, c.get_epoch());
+ ASSERT_FALSE(c.has_prev());
+}
+
+TEST(PeriodHistory, AttachAfter)
+{
+ NumericPuller puller;
+ RGWPeriodHistory history(g_ceph_context, &puller, current_period);
+ const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
+
+ auto c9 = history.attach(&dp, make_period("9", 9, "8"), null_yield);
+ ASSERT_TRUE(c9);
+
+ // verify that we pulled and merged all periods from 5-9
+ auto c = history.get_current();
+ ASSERT_TRUE(c);
+ ASSERT_TRUE(c.has_next());
+ c.next();
+ ASSERT_EQ(6u, c.get_epoch());
+ ASSERT_TRUE(c.has_next());
+ c.next();
+ ASSERT_EQ(7u, c.get_epoch());
+ ASSERT_TRUE(c.has_next());
+ c.next();
+ ASSERT_EQ(8u, c.get_epoch());
+ ASSERT_TRUE(c.has_next());
+ c.next();
+ ASSERT_EQ(9u, c.get_epoch());
+ ASSERT_FALSE(c.has_next());
+}
+
+int main(int argc, char** argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/rgw/test_rgw_putobj.cc b/src/test/rgw/test_rgw_putobj.cc
new file mode 100644
index 000000000..35abc3036
--- /dev/null
+++ b/src/test/rgw/test_rgw_putobj.cc
@@ -0,0 +1,196 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_putobj.h"
+#include <gtest/gtest.h>
+
+inline bufferlist string_buf(const char* buf) {
+ bufferlist bl;
+ bl.append(buffer::create_static(strlen(buf), (char*)buf));
+ return bl;
+}
+
+struct Op {
+ std::string data;
+ uint64_t offset;
+};
+inline bool operator==(const Op& lhs, const Op& rhs) {
+ return lhs.data == rhs.data && lhs.offset == rhs.offset;
+}
+inline std::ostream& operator<<(std::ostream& out, const Op& op) {
+ return out << "{off=" << op.offset << " data='" << op.data << "'}";
+}
+
+struct MockProcessor : rgw::sal::DataProcessor {
+ std::vector<Op> ops;
+
+ int process(bufferlist&& data, uint64_t offset) override {
+ ops.push_back({data.to_str(), offset});
+ return {};
+ }
+};
+
+TEST(PutObj_Chunk, FlushHalf)
+{
+ MockProcessor mock;
+ rgw::putobj::ChunkProcessor chunk(&mock, 4);
+
+ ASSERT_EQ(0, chunk.process(string_buf("22"), 0));
+ ASSERT_TRUE(mock.ops.empty()); // no writes
+
+ ASSERT_EQ(0, chunk.process({}, 2)); // flush
+ ASSERT_EQ(2u, mock.ops.size());
+ EXPECT_EQ(Op({"22", 0}), mock.ops[0]);
+ EXPECT_EQ(Op({"", 2}), mock.ops[1]);
+}
+
+TEST(PutObj_Chunk, One)
+{
+ MockProcessor mock;
+ rgw::putobj::ChunkProcessor chunk(&mock, 4);
+
+ ASSERT_EQ(0, chunk.process(string_buf("4444"), 0));
+ ASSERT_EQ(1u, mock.ops.size());
+ EXPECT_EQ(Op({"4444", 0}), mock.ops[0]);
+
+ ASSERT_EQ(0, chunk.process({}, 4)); // flush
+ ASSERT_EQ(2u, mock.ops.size());
+ EXPECT_EQ(Op({"", 4}), mock.ops[1]);
+}
+
+TEST(PutObj_Chunk, OneAndFlushHalf)
+{
+ MockProcessor mock;
+ rgw::putobj::ChunkProcessor chunk(&mock, 4);
+
+ ASSERT_EQ(0, chunk.process(string_buf("22"), 0));
+ ASSERT_TRUE(mock.ops.empty());
+
+ ASSERT_EQ(0, chunk.process(string_buf("4444"), 2));
+ ASSERT_EQ(1u, mock.ops.size());
+ EXPECT_EQ(Op({"2244", 0}), mock.ops[0]);
+
+ ASSERT_EQ(0, chunk.process({}, 6)); // flush
+ ASSERT_EQ(3u, mock.ops.size());
+ EXPECT_EQ(Op({"44", 4}), mock.ops[1]);
+ EXPECT_EQ(Op({"", 6}), mock.ops[2]);
+}
+
+TEST(PutObj_Chunk, Two)
+{
+ MockProcessor mock;
+ rgw::putobj::ChunkProcessor chunk(&mock, 4);
+
+ ASSERT_EQ(0, chunk.process(string_buf("88888888"), 0));
+ ASSERT_EQ(2u, mock.ops.size());
+ EXPECT_EQ(Op({"8888", 0}), mock.ops[0]);
+ EXPECT_EQ(Op({"8888", 4}), mock.ops[1]);
+
+ ASSERT_EQ(0, chunk.process({}, 8)); // flush
+ ASSERT_EQ(3u, mock.ops.size());
+ EXPECT_EQ(Op({"", 8}), mock.ops[2]);
+}
+
+TEST(PutObj_Chunk, TwoAndFlushHalf)
+{
+ MockProcessor mock;
+ rgw::putobj::ChunkProcessor chunk(&mock, 4);
+
+ ASSERT_EQ(0, chunk.process(string_buf("22"), 0));
+ ASSERT_TRUE(mock.ops.empty());
+
+ ASSERT_EQ(0, chunk.process(string_buf("88888888"), 2));
+ ASSERT_EQ(2u, mock.ops.size());
+ EXPECT_EQ(Op({"2288", 0}), mock.ops[0]);
+ EXPECT_EQ(Op({"8888", 4}), mock.ops[1]);
+
+ ASSERT_EQ(0, chunk.process({}, 10)); // flush
+ ASSERT_EQ(4u, mock.ops.size());
+ EXPECT_EQ(Op({"88", 8}), mock.ops[2]);
+ EXPECT_EQ(Op({"", 10}), mock.ops[3]);
+}
+
+
+using StripeMap = std::map<uint64_t, uint64_t>; // offset, stripe_size
+
+class StripeMapGen : public rgw::putobj::StripeGenerator {
+ const StripeMap& stripes;
+ public:
+ StripeMapGen(const StripeMap& stripes) : stripes(stripes) {}
+
+ int next(uint64_t offset, uint64_t *stripe_size) override {
+ auto i = stripes.find(offset);
+ if (i == stripes.end()) {
+ return -ENOENT;
+ }
+ *stripe_size = i->second;
+ return 0;
+ }
+};
+
+TEST(PutObj_Stripe, DifferentStripeSize)
+{
+ MockProcessor mock;
+ StripeMap stripes{
+ { 0, 4},
+ { 4, 6},
+ {10, 2}
+ };
+ StripeMapGen gen(stripes);
+ rgw::putobj::StripeProcessor processor(&mock, &gen, stripes.begin()->second);
+
+ ASSERT_EQ(0, processor.process(string_buf("22"), 0));
+ ASSERT_EQ(1u, mock.ops.size());
+ EXPECT_EQ(Op({"22", 0}), mock.ops[0]);
+
+ ASSERT_EQ(0, processor.process(string_buf("4444"), 2));
+ ASSERT_EQ(4u, mock.ops.size());
+ EXPECT_EQ(Op({"44", 2}), mock.ops[1]);
+ EXPECT_EQ(Op({"", 4}), mock.ops[2]); // flush
+ EXPECT_EQ(Op({"44", 0}), mock.ops[3]);
+
+ ASSERT_EQ(0, processor.process(string_buf("666666"), 6));
+ ASSERT_EQ(7u, mock.ops.size());
+ EXPECT_EQ(Op({"6666", 2}), mock.ops[4]);
+ EXPECT_EQ(Op({"", 6}), mock.ops[5]); // flush
+ EXPECT_EQ(Op({"66", 0}), mock.ops[6]);
+
+ ASSERT_EQ(0, processor.process({}, 12));
+ ASSERT_EQ(8u, mock.ops.size());
+ EXPECT_EQ(Op({"", 2}), mock.ops[7]); // flush
+
+ // gen returns an error past this
+ ASSERT_EQ(-ENOENT, processor.process(string_buf("1"), 12));
+}
+
+TEST(PutObj_Stripe, SkipFirstChunk)
+{
+ MockProcessor mock;
+ StripeMap stripes{
+ {0, 4},
+ {4, 4},
+ };
+ StripeMapGen gen(stripes);
+ rgw::putobj::StripeProcessor processor(&mock, &gen, stripes.begin()->second);
+
+ ASSERT_EQ(0, processor.process(string_buf("666666"), 2));
+ ASSERT_EQ(3u, mock.ops.size());
+ EXPECT_EQ(Op({"66", 2}), mock.ops[0]);
+ EXPECT_EQ(Op({"", 4}), mock.ops[1]); // flush
+ EXPECT_EQ(Op({"6666", 0}), mock.ops[2]);
+
+ ASSERT_EQ(0, processor.process({}, 8));
+ ASSERT_EQ(4u, mock.ops.size());
+ EXPECT_EQ(Op({"", 4}), mock.ops[3]); // flush
+}
diff --git a/src/test/rgw/test_rgw_ratelimit.cc b/src/test/rgw/test_rgw_ratelimit.cc
new file mode 100644
index 000000000..01be4df48
--- /dev/null
+++ b/src/test/rgw/test_rgw_ratelimit.cc
@@ -0,0 +1,376 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+#include <gtest/gtest.h>
+#include "rgw_ratelimit.h"
+
+
+using namespace std::chrono_literals;
+
+TEST(RGWRateLimit, op_limit_not_enabled)
+{
+ // info.enabled = false, so no limit
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("PUT", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimit, reject_op_over_limit)
+{
+ // check that request is being rejected because there are not enough tokens
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ time = ceph::coarse_real_clock::now();
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(true, success);
+}
+TEST(RGWRateLimit, accept_op_after_giveback)
+{
+ // check that giveback is working fine
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ ratelimit.giveback_tokens("GET", key);
+ time = ceph::coarse_real_clock::now();
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimit, accept_op_after_refill)
+{
+ // check that tokens are being filled properly
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ time += 61s;
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimit, reject_bw_over_limit)
+{
+ // check that a newer request is rejected if there is no enough tokens (bw)
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = 1;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ ratelimit.decrease_bytes("GET",key, 2, &info);
+ time = ceph::coarse_real_clock::now();
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(true, success);
+}
+TEST(RGWRateLimit, accept_bw)
+{
+ // check that when there are enough tokens (bw) the request is still being served
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = 2;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ ratelimit.decrease_bytes("GET",key, 1, &info);
+ time = ceph::coarse_real_clock::now();
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimit, check_bw_debt_at_max_120secs)
+{
+ // check that the bandwidth debt is not larger than 120 seconds
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = 2;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ ratelimit.decrease_bytes("GET",key, 100, &info);
+ time += 121s;
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimit, check_that_bw_limit_not_affect_ops)
+{
+ // check that high read bytes limit, does not affect ops limit
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ info.max_read_bytes = 100000000;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ ratelimit.decrease_bytes("GET",key, 10000, &info);
+ time = ceph::coarse_real_clock::now();
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(true, success);
+}
+TEST(RGWRateLimit, read_limit_does_not_affect_writes)
+{
+ // read limit does not affect writes
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ info.max_read_bytes = 100000000;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("PUT", key, time, &info);
+ ratelimit.decrease_bytes("PUT",key, 10000, &info);
+ time = ceph::coarse_real_clock::now();
+ success = ratelimit.should_rate_limit("PUT", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimit, write_limit_does_not_affect_reads)
+{
+ // write limit does not affect reads
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_write_ops = 1;
+ info.max_write_bytes = 100000000;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ ratelimit.decrease_bytes("GET",key, 10000, &info);
+ time = ceph::coarse_real_clock::now();
+ success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+
+TEST(RGWRateLimit, allow_unlimited_access)
+{
+ // 0 values in RGWRateLimitInfo should allow unlimited access
+ std::atomic_bool replacing;
+ std::condition_variable cv;
+ RateLimiter ratelimit(replacing, cv);
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ bool success = ratelimit.should_rate_limit("GET", key, time, &info);
+ EXPECT_EQ(false, success);
+}
+
+TEST(RGWRateLimitGC, NO_GC_AHEAD_OF_TIME)
+{
+ // Test if GC is not starting the replace before getting to map_size * 0.9
+ // Please make sure to change those values when you change the map_size in the code
+
+ std::shared_ptr<ActiveRateLimiter> ratelimit(new ActiveRateLimiter(g_ceph_context));
+ ratelimit->start();
+ auto active = ratelimit->get_active();
+ RGWRateLimitInfo info;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "uuser123";
+ active->should_rate_limit("GET", key, time, &info);
+ auto activegc = ratelimit->get_active();
+ EXPECT_EQ(activegc, active);
+}
+TEST(RGWRateLimiterGC, GC_IS_WORKING)
+{
+ // Test if GC is replacing the active RateLimiter
+ // Please make sure to change those values when you change the map_size in the code
+
+ std::shared_ptr<ActiveRateLimiter> ratelimit(new ActiveRateLimiter(g_ceph_context));
+ ratelimit->start();
+ auto active = ratelimit->get_active();
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ auto time = ceph::coarse_real_clock::now();
+ std::string key = "-1";
+ for(int i = 0; i < 2000000; i++)
+ {
+ active->should_rate_limit("GET", key, time, &info);
+ key = std::to_string(i);
+ }
+ auto activegc = ratelimit->get_active();
+ EXPECT_NE(activegc, active);
+}
+
+
+TEST(RGWRateLimitEntry, op_limit_not_enabled)
+{
+ // info.enabled = false, so no limit
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(false, &info, time);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimitEntry, reject_op_over_limit)
+{
+ // check that request is being rejected because there are not enough tokens
+
+ RGWRateLimitInfo info;
+ RateLimiterEntry entry;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ time = ceph::coarse_real_clock::now().time_since_epoch();
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(true, success);
+}
+TEST(RGWRateLimitEntry, accept_op_after_giveback)
+{
+ // check that giveback is working fine
+ RGWRateLimitInfo info;
+ RateLimiterEntry entry;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ entry.giveback_tokens(true);
+ time = ceph::coarse_real_clock::now().time_since_epoch();
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimitEntry, accept_op_after_refill)
+{
+ // check that tokens are being filled properly
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ time += 61s;
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimitEntry, reject_bw_over_limit)
+{
+ // check that a newer request is rejected if there is no enough tokens (bw)
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = 1;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ entry.decrease_bytes(true, 2, &info);
+ time = ceph::coarse_real_clock::now().time_since_epoch();
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(true, success);
+}
+TEST(RGWRateLimitEntry, accept_bw)
+{
+ // check that when there are enough tokens (bw) the request is still being served
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = 2;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ entry.decrease_bytes(true, 1, &info);
+ time = ceph::coarse_real_clock::now().time_since_epoch();
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimitEntry, check_bw_debt_at_max_120secs)
+{
+ // check that the bandwidth debt is not larger than 120 seconds
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_bytes = 2;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ entry.decrease_bytes(true, 100, &info);
+ time += 121s;
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimitEntry, check_that_bw_limit_not_affect_ops)
+{
+ // check that high read bytes limit, does not affect ops limit
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ info.max_read_bytes = 100000000;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ entry.decrease_bytes(true, 10000, &info);
+ time = ceph::coarse_real_clock::now().time_since_epoch();
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(true, success);
+}
+TEST(RGWRateLimitEntry, read_limit_does_not_affect_writes)
+{
+ // read limit does not affect writes
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_read_ops = 1;
+ info.max_read_bytes = 100000000;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(false, &info, time);
+ entry.decrease_bytes(false, 10000, &info);
+ time = ceph::coarse_real_clock::now().time_since_epoch();
+ success = entry.should_rate_limit(false, &info, time);
+ EXPECT_EQ(false, success);
+}
+TEST(RGWRateLimitEntry, write_limit_does_not_affect_reads)
+{
+ // write limit does not affect reads
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ info.max_write_ops = 1;
+ info.max_write_bytes = 100000000;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ std::string key = "uuser123";
+ bool success = entry.should_rate_limit(true, &info, time);
+ entry.decrease_bytes(true, 10000, &info);
+ time = ceph::coarse_real_clock::now().time_since_epoch();
+ success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(false, success);
+}
+
+TEST(RGWRateLimitEntry, allow_unlimited_access)
+{
+ // 0 values in RGWRateLimitInfo should allow unlimited access (default value)
+ RateLimiterEntry entry;
+ RGWRateLimitInfo info;
+ info.enabled = true;
+ auto time = ceph::coarse_real_clock::now().time_since_epoch();
+ bool success = entry.should_rate_limit(true, &info, time);
+ EXPECT_EQ(false, success);
+}
diff --git a/src/test/rgw/test_rgw_reshard.cc b/src/test/rgw/test_rgw_reshard.cc
new file mode 100644
index 000000000..da41b967f
--- /dev/null
+++ b/src/test/rgw/test_rgw_reshard.cc
@@ -0,0 +1,68 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_reshard.h"
+#include <gtest/gtest.h>
+
+
+TEST(TestRGWReshard, dynamic_reshard_shard_count)
+{
+ // assuming we have prime numbers up to 1999
+ ASSERT_EQ(1999u, RGWBucketReshard::get_max_prime_shards()) <<
+ "initial list has primes up to 1999";
+
+ ASSERT_EQ(1u, RGWBucketReshard::get_prime_shards_greater_or_equal(1)) <<
+ "we allow for 1 shard even though it's not prime";
+ ASSERT_EQ(809u, RGWBucketReshard::get_prime_shards_greater_or_equal(808)) <<
+ "809 is prime";
+ ASSERT_EQ(809u, RGWBucketReshard::get_prime_shards_greater_or_equal(809)) <<
+ "809 is prime";
+ ASSERT_EQ(811u, RGWBucketReshard::get_prime_shards_greater_or_equal(810)) <<
+ "811 is prime";
+ ASSERT_EQ(811u, RGWBucketReshard::get_prime_shards_greater_or_equal(811)) <<
+ "811 is prime";
+ ASSERT_EQ(821u, RGWBucketReshard::get_prime_shards_greater_or_equal(812)) <<
+ "821 is prime";
+
+ ASSERT_EQ(1u, RGWBucketReshard::get_prime_shards_less_or_equal(1)) <<
+ "we allow for 1 shard even though it's not prime";
+ ASSERT_EQ(797u, RGWBucketReshard::get_prime_shards_less_or_equal(808)) <<
+ "809 is prime";
+ ASSERT_EQ(809u, RGWBucketReshard::get_prime_shards_less_or_equal(809)) <<
+ "809 is prime";
+ ASSERT_EQ(809u, RGWBucketReshard::get_prime_shards_less_or_equal(810)) <<
+ "811 is prime";
+ ASSERT_EQ(811u, RGWBucketReshard::get_prime_shards_less_or_equal(811)) <<
+ "811 is prime";
+ ASSERT_EQ(811u, RGWBucketReshard::get_prime_shards_less_or_equal(812)) <<
+ "821 is prime";
+
+ // tests when max dynamic shards is equal to end of prime list
+ ASSERT_EQ(1999u, RGWBucketReshard::get_preferred_shards(1998, 1999));
+ ASSERT_EQ(1999u, RGWBucketReshard::get_preferred_shards(1999, 1999));
+ ASSERT_EQ(1999u, RGWBucketReshard::get_preferred_shards(2000, 1999));
+ ASSERT_EQ(1999u, RGWBucketReshard::get_preferred_shards(2001, 1999));
+
+ // tests when max dynamic shards is above end of prime list
+ ASSERT_EQ(1999u, RGWBucketReshard::get_preferred_shards(1998, 3000));
+ ASSERT_EQ(1999u, RGWBucketReshard::get_preferred_shards(1999, 3000));
+ ASSERT_EQ(2000u, RGWBucketReshard::get_preferred_shards(2000, 3000));
+ ASSERT_EQ(2001u, RGWBucketReshard::get_preferred_shards(2001, 3000));
+
+ // tests when max dynamic shards is below end of prime list
+ ASSERT_EQ(499u, RGWBucketReshard::get_preferred_shards(1998, 500));
+ ASSERT_EQ(499u, RGWBucketReshard::get_preferred_shards(1999, 500));
+ ASSERT_EQ(499u, RGWBucketReshard::get_preferred_shards(2000, 500));
+ ASSERT_EQ(499u, RGWBucketReshard::get_preferred_shards(2001, 500));
+}
diff --git a/src/test/rgw/test_rgw_reshard_wait.cc b/src/test/rgw/test_rgw_reshard_wait.cc
new file mode 100644
index 000000000..06caae34a
--- /dev/null
+++ b/src/test/rgw/test_rgw_reshard_wait.cc
@@ -0,0 +1,164 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_reshard.h"
+#include <spawn/spawn.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+using Clock = RGWReshardWait::Clock;
+
+TEST(ReshardWait, wait_block)
+{
+ constexpr ceph::timespan wait_duration = 10ms;
+ RGWReshardWait waiter(wait_duration);
+
+ const auto start = Clock::now();
+ EXPECT_EQ(0, waiter.wait(null_yield));
+ const ceph::timespan elapsed = Clock::now() - start;
+
+ EXPECT_LE(wait_duration, elapsed); // waited at least 10ms
+ waiter.stop();
+}
+
+TEST(ReshardWait, stop_block)
+{
+ constexpr ceph::timespan short_duration = 10ms;
+ constexpr ceph::timespan long_duration = 10s;
+
+ RGWReshardWait long_waiter(long_duration);
+ RGWReshardWait short_waiter(short_duration);
+
+ const auto start = Clock::now();
+ std::thread thread([&long_waiter] {
+ EXPECT_EQ(-ECANCELED, long_waiter.wait(null_yield));
+ });
+
+ EXPECT_EQ(0, short_waiter.wait(null_yield));
+
+ long_waiter.stop(); // cancel long waiter
+
+ thread.join();
+ const ceph::timespan elapsed = Clock::now() - start;
+
+ EXPECT_LE(short_duration, elapsed); // waited at least 10ms
+ EXPECT_GT(long_duration, elapsed); // waited less than 10s
+ short_waiter.stop();
+}
+
+TEST(ReshardWait, wait_yield)
+{
+ constexpr ceph::timespan wait_duration = 50ms;
+ RGWReshardWait waiter(wait_duration);
+
+ boost::asio::io_context context;
+ spawn::spawn(context, [&] (yield_context yield) {
+ EXPECT_EQ(0, waiter.wait(optional_yield{context, yield}));
+ });
+
+ const auto start = Clock::now();
+ EXPECT_EQ(1u, context.poll()); // spawn
+ EXPECT_FALSE(context.stopped());
+
+ EXPECT_EQ(1u, context.run_one()); // timeout
+ EXPECT_TRUE(context.stopped());
+ const ceph::timespan elapsed = Clock::now() - start;
+
+ EXPECT_LE(wait_duration, elapsed); // waited at least 10ms
+ waiter.stop();
+}
+
+TEST(ReshardWait, stop_yield)
+{
+ constexpr ceph::timespan short_duration = 50ms;
+ constexpr ceph::timespan long_duration = 10s;
+
+ RGWReshardWait long_waiter(long_duration);
+ RGWReshardWait short_waiter(short_duration);
+
+ boost::asio::io_context context;
+ spawn::spawn(context,
+ [&] (yield_context yield) {
+ EXPECT_EQ(-ECANCELED, long_waiter.wait(optional_yield{context, yield}));
+ });
+
+ const auto start = Clock::now();
+ EXPECT_EQ(1u, context.poll()); // spawn
+ EXPECT_FALSE(context.stopped());
+
+ EXPECT_EQ(0, short_waiter.wait(null_yield));
+
+ long_waiter.stop(); // cancel long waiter
+
+ EXPECT_EQ(1u, context.run_one_for(short_duration)); // timeout
+ EXPECT_TRUE(context.stopped());
+ const ceph::timespan elapsed = Clock::now() - start;
+
+ EXPECT_LE(short_duration, elapsed); // waited at least 10ms
+ EXPECT_GT(long_duration, elapsed); // waited less than 10s
+ short_waiter.stop();
+}
+
+TEST(ReshardWait, stop_multiple)
+{
+ constexpr ceph::timespan short_duration = 50ms;
+ constexpr ceph::timespan long_duration = 10s;
+
+ RGWReshardWait long_waiter(long_duration);
+ RGWReshardWait short_waiter(short_duration);
+
+ // spawn 4 threads
+ std::vector<std::thread> threads;
+ {
+ auto sync_waiter([&long_waiter] {
+ EXPECT_EQ(-ECANCELED, long_waiter.wait(null_yield));
+ });
+ threads.emplace_back(sync_waiter);
+ threads.emplace_back(sync_waiter);
+ threads.emplace_back(sync_waiter);
+ threads.emplace_back(sync_waiter);
+ }
+ // spawn 4 coroutines
+ boost::asio::io_context context;
+ {
+ auto async_waiter = [&] (yield_context yield) {
+ EXPECT_EQ(-ECANCELED, long_waiter.wait(optional_yield{context, yield}));
+ };
+ spawn::spawn(context, async_waiter);
+ spawn::spawn(context, async_waiter);
+ spawn::spawn(context, async_waiter);
+ spawn::spawn(context, async_waiter);
+ }
+
+ const auto start = Clock::now();
+ EXPECT_EQ(4u, context.poll()); // spawn
+ EXPECT_FALSE(context.stopped());
+
+ EXPECT_EQ(0, short_waiter.wait(null_yield));
+
+ long_waiter.stop(); // cancel long waiter
+
+ EXPECT_EQ(4u, context.run_for(short_duration)); // timeout
+ EXPECT_TRUE(context.stopped());
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ const ceph::timespan elapsed = Clock::now() - start;
+
+ EXPECT_LE(short_duration, elapsed); // waited at least 10ms
+ EXPECT_GT(long_duration, elapsed); // waited less than 10s
+ short_waiter.stop();
+}
diff --git a/src/test/rgw/test_rgw_string.cc b/src/test/rgw/test_rgw_string.cc
new file mode 100644
index 000000000..90a0b00c8
--- /dev/null
+++ b/src/test/rgw/test_rgw_string.cc
@@ -0,0 +1,76 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_string.h"
+#include <gtest/gtest.h>
+
+const std::string abc{"abc"};
+const char *def{"def"}; // const char*
+char ghi_arr[] = {'g', 'h', 'i', '\0'};
+char *ghi{ghi_arr}; // char*
+constexpr std::string_view jkl{"jkl", 3};
+#define mno "mno" // string literal (char[4])
+char pqr[] = {'p', 'q', 'r', '\0'};
+
+TEST(string_size, types)
+{
+ ASSERT_EQ(3u, string_size(abc));
+ ASSERT_EQ(3u, string_size(def));
+ ASSERT_EQ(3u, string_size(ghi));
+ ASSERT_EQ(3u, string_size(jkl));
+ ASSERT_EQ(3u, string_size(mno));
+ ASSERT_EQ(3u, string_size(pqr));
+
+ constexpr auto compile_time_string_view_size = string_size(jkl);
+ ASSERT_EQ(3u, compile_time_string_view_size);
+ constexpr auto compile_time_string_literal_size = string_size(mno);
+ ASSERT_EQ(3u, compile_time_string_literal_size);
+
+ char arr[] = {'a', 'b', 'c'}; // not null-terminated
+ ASSERT_THROW(string_size(arr), std::invalid_argument);
+}
+
+TEST(string_cat_reserve, types)
+{
+ ASSERT_EQ("abcdefghijklmnopqr",
+ string_cat_reserve(abc, def, ghi, jkl, mno, pqr));
+}
+
+TEST(string_cat_reserve, count)
+{
+ ASSERT_EQ("", string_cat_reserve());
+ ASSERT_EQ("abc", string_cat_reserve(abc));
+ ASSERT_EQ("abcdef", string_cat_reserve(abc, def));
+}
+
+TEST(string_join_reserve, types)
+{
+ ASSERT_EQ("abc, def, ghi, jkl, mno, pqr",
+ string_join_reserve(", ", abc, def, ghi, jkl, mno, pqr));
+}
+
+TEST(string_join_reserve, count)
+{
+ ASSERT_EQ("", string_join_reserve(", "));
+ ASSERT_EQ("abc", string_join_reserve(", ", abc));
+ ASSERT_EQ("abc, def", string_join_reserve(", ", abc, def));
+}
+
+TEST(string_join_reserve, delim)
+{
+ ASSERT_EQ("abcdef", string_join_reserve("", abc, def));
+ ASSERT_EQ("abc def", string_join_reserve(' ', abc, def));
+ ASSERT_EQ("abc\ndef", string_join_reserve('\n', abc, def));
+ ASSERT_EQ("abcfoodef", string_join_reserve(std::string{"foo"}, abc, def));
+}
diff --git a/src/test/rgw/test_rgw_throttle.cc b/src/test/rgw/test_rgw_throttle.cc
new file mode 100644
index 000000000..d67f2c6ce
--- /dev/null
+++ b/src/test/rgw/test_rgw_throttle.cc
@@ -0,0 +1,221 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_aio_throttle.h"
+
+#include <optional>
+#include <thread>
+#include "include/scope_guard.h"
+
+#include <spawn/spawn.hpp>
+#include <gtest/gtest.h>
+
+struct RadosEnv : public ::testing::Environment {
+ public:
+ static constexpr auto poolname = "ceph_test_rgw_throttle";
+
+ static std::optional<RGWSI_RADOS> rados;
+
+ void SetUp() override {
+ rados.emplace(g_ceph_context);
+ const NoDoutPrefix no_dpp(g_ceph_context, 1);
+ ASSERT_EQ(0, rados->start(null_yield, &no_dpp));
+ int r = rados->pool({poolname}).create(&no_dpp);
+ if (r == -EEXIST)
+ r = 0;
+ ASSERT_EQ(0, r);
+ }
+ void TearDown() override {
+ ASSERT_EQ(0, rados->get_rados_handle()->pool_delete(poolname));
+ rados->shutdown();
+ rados.reset();
+ }
+};
+std::optional<RGWSI_RADOS> RadosEnv::rados;
+
+auto *const rados_env = ::testing::AddGlobalTestEnvironment(new RadosEnv);
+
+// test fixture for global setup/teardown
+class RadosFixture : public ::testing::Test {
+ protected:
+ RGWSI_RADOS::Obj make_obj(const std::string& oid) {
+ auto obj = RadosEnv::rados->obj({{RadosEnv::poolname}, oid});
+ const NoDoutPrefix no_dpp(g_ceph_context, 1);
+ ceph_assert_always(0 == obj.open(&no_dpp));
+ return obj;
+ }
+};
+
+using Aio_Throttle = RadosFixture;
+
+namespace rgw {
+
+struct scoped_completion {
+ Aio* aio = nullptr;
+ AioResult* result = nullptr;
+ ~scoped_completion() { if (aio) { complete(-ECANCELED); } }
+ void complete(int r) {
+ result->result = r;
+ aio->put(*result);
+ aio = nullptr;
+ }
+};
+
+auto wait_on(scoped_completion& c) {
+ return [&c] (Aio* aio, AioResult& r) { c.aio = aio; c.result = &r; };
+}
+
+auto wait_for(boost::asio::io_context& context, ceph::timespan duration) {
+ return [&context, duration] (Aio* aio, AioResult& r) {
+ using Clock = ceph::coarse_mono_clock;
+ using Timer = boost::asio::basic_waitable_timer<Clock>;
+ auto t = std::make_unique<Timer>(context);
+ t->expires_after(duration);
+ t->async_wait([aio, &r, t=std::move(t)] (boost::system::error_code ec) {
+ if (ec != boost::asio::error::operation_aborted) {
+ aio->put(r);
+ }
+ });
+ };
+}
+
+TEST_F(Aio_Throttle, NoThrottleUpToMax)
+{
+ BlockingAioThrottle throttle(4);
+ auto obj = make_obj(__PRETTY_FUNCTION__);
+ {
+ scoped_completion op1;
+ auto c1 = throttle.get(obj, wait_on(op1), 1, 0);
+ EXPECT_TRUE(c1.empty());
+ scoped_completion op2;
+ auto c2 = throttle.get(obj, wait_on(op2), 1, 0);
+ EXPECT_TRUE(c2.empty());
+ scoped_completion op3;
+ auto c3 = throttle.get(obj, wait_on(op3), 1, 0);
+ EXPECT_TRUE(c3.empty());
+ scoped_completion op4;
+ auto c4 = throttle.get(obj, wait_on(op4), 1, 0);
+ EXPECT_TRUE(c4.empty());
+ // no completions because no ops had to wait
+ auto c5 = throttle.poll();
+ EXPECT_TRUE(c5.empty());
+ }
+ auto completions = throttle.drain();
+ ASSERT_EQ(4u, completions.size());
+ for (auto& c : completions) {
+ EXPECT_EQ(-ECANCELED, c.result);
+ }
+}
+
+TEST_F(Aio_Throttle, CostOverWindow)
+{
+ BlockingAioThrottle throttle(4);
+ auto obj = make_obj(__PRETTY_FUNCTION__);
+
+ scoped_completion op;
+ auto c = throttle.get(obj, wait_on(op), 8, 0);
+ ASSERT_EQ(1u, c.size());
+ EXPECT_EQ(-EDEADLK, c.front().result);
+}
+
+TEST_F(Aio_Throttle, ThrottleOverMax)
+{
+ constexpr uint64_t window = 4;
+ BlockingAioThrottle throttle(window);
+
+ auto obj = make_obj(__PRETTY_FUNCTION__);
+
+ // issue 32 writes, and verify that max_outstanding <= window
+ constexpr uint64_t total = 32;
+ uint64_t max_outstanding = 0;
+ uint64_t outstanding = 0;
+
+ // timer thread
+ boost::asio::io_context context;
+ using Executor = boost::asio::io_context::executor_type;
+ using Work = boost::asio::executor_work_guard<Executor>;
+ std::optional<Work> work(context.get_executor());
+ std::thread worker([&context] { context.run(); });
+ auto g = make_scope_guard([&work, &worker] {
+ work.reset();
+ worker.join();
+ });
+
+ for (uint64_t i = 0; i < total; i++) {
+ using namespace std::chrono_literals;
+ auto c = throttle.get(obj, wait_for(context, 10ms), 1, 0);
+ outstanding++;
+ outstanding -= c.size();
+ if (max_outstanding < outstanding) {
+ max_outstanding = outstanding;
+ }
+ }
+ auto c = throttle.drain();
+ outstanding -= c.size();
+ EXPECT_EQ(0u, outstanding);
+ EXPECT_EQ(window, max_outstanding);
+}
+
+TEST_F(Aio_Throttle, YieldCostOverWindow)
+{
+ auto obj = make_obj(__PRETTY_FUNCTION__);
+
+ boost::asio::io_context context;
+ spawn::spawn(context,
+ [&] (yield_context yield) {
+ YieldingAioThrottle throttle(4, context, yield);
+ scoped_completion op;
+ auto c = throttle.get(obj, wait_on(op), 8, 0);
+ ASSERT_EQ(1u, c.size());
+ EXPECT_EQ(-EDEADLK, c.front().result);
+ });
+ context.run();
+}
+
+TEST_F(Aio_Throttle, YieldingThrottleOverMax)
+{
+ constexpr uint64_t window = 4;
+
+ auto obj = make_obj(__PRETTY_FUNCTION__);
+
+ // issue 32 writes, and verify that max_outstanding <= window
+ constexpr uint64_t total = 32;
+ uint64_t max_outstanding = 0;
+ uint64_t outstanding = 0;
+
+ boost::asio::io_context context;
+ spawn::spawn(context,
+ [&] (yield_context yield) {
+ YieldingAioThrottle throttle(window, context, yield);
+ for (uint64_t i = 0; i < total; i++) {
+ using namespace std::chrono_literals;
+ auto c = throttle.get(obj, wait_for(context, 10ms), 1, 0);
+ outstanding++;
+ outstanding -= c.size();
+ if (max_outstanding < outstanding) {
+ max_outstanding = outstanding;
+ }
+ }
+ auto c = throttle.drain();
+ outstanding -= c.size();
+ });
+ context.poll(); // run until we block
+ EXPECT_EQ(window, outstanding);
+
+ context.run();
+ EXPECT_EQ(0u, outstanding);
+ EXPECT_EQ(window, max_outstanding);
+}
+
+} // namespace rgw
diff --git a/src/test/rgw/test_rgw_url.cc b/src/test/rgw/test_rgw_url.cc
new file mode 100644
index 000000000..92731dfad
--- /dev/null
+++ b/src/test/rgw/test_rgw_url.cc
@@ -0,0 +1,111 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "rgw_url.h"
+#include <string>
+#include <gtest/gtest.h>
+
+using namespace rgw;
+
+TEST(TestURL, SimpleAuthority)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "http://example.com";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+ ASSERT_TRUE(user.empty());
+ ASSERT_TRUE(password.empty());
+ EXPECT_STREQ(host.c_str(), "example.com");
+}
+
+TEST(TestURL, SimpleAuthority_1)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "http://example.com/";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+ ASSERT_TRUE(user.empty());
+ ASSERT_TRUE(password.empty());
+ EXPECT_STREQ(host.c_str(), "example.com");
+}
+
+TEST(TestURL, IPAuthority)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "http://1.2.3.4";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+ ASSERT_TRUE(user.empty());
+ ASSERT_TRUE(password.empty());
+ EXPECT_STREQ(host.c_str(), "1.2.3.4");
+}
+
+TEST(TestURL, IPv6Authority)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "http://FE80:CD00:0000:0CDE:1257:0000:211E:729C";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+ ASSERT_TRUE(user.empty());
+ ASSERT_TRUE(password.empty());
+ EXPECT_STREQ(host.c_str(), "FE80:CD00:0000:0CDE:1257:0000:211E:729C");
+}
+
+TEST(TestURL, AuthorityWithUserinfo)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "https://user:password@example.com";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+ EXPECT_STREQ(host.c_str(), "example.com");
+ EXPECT_STREQ(user.c_str(), "user");
+ EXPECT_STREQ(password.c_str(), "password");
+}
+
+TEST(TestURL, AuthorityWithPort)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "http://user:password@example.com:1234";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+ EXPECT_STREQ(host.c_str(), "example.com:1234");
+ EXPECT_STREQ(user.c_str(), "user");
+ EXPECT_STREQ(password.c_str(), "password");
+}
+
+TEST(TestURL, DifferentSchema)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "kafka://example.com";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+ ASSERT_TRUE(user.empty());
+ ASSERT_TRUE(password.empty());
+ EXPECT_STREQ(host.c_str(), "example.com");
+}
+
+TEST(TestURL, InvalidHost)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "http://exa_mple.com";
+ ASSERT_FALSE(parse_url_authority(url, host, user, password));
+}
+
+TEST(TestURL, WithPath)
+{
+ std::string host;
+ std::string user;
+ std::string password;
+ const std::string url = "amqps://www.example.com:1234/vhost_name";
+ ASSERT_TRUE(parse_url_authority(url, host, user, password));
+}
+
diff --git a/src/test/rgw/test_rgw_xml.cc b/src/test/rgw/test_rgw_xml.cc
new file mode 100644
index 000000000..fa9f21157
--- /dev/null
+++ b/src/test/rgw/test_rgw_xml.cc
@@ -0,0 +1,463 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "rgw_xml.h"
+#include <gtest/gtest.h>
+#include <list>
+#include <stdexcept>
+
+struct NameAndStatus {
+ // these are sub-tags
+ std::string name;
+ bool status;
+
+ // intrusive XML decoding API
+ bool decode_xml(XMLObj *obj) {
+ if (!RGWXMLDecoder::decode_xml("Name", name, obj, true)) {
+ // name is mandatory
+ return false;
+ }
+ if (!RGWXMLDecoder::decode_xml("Status", status, obj, false)) {
+ // status is optional and defaults to True
+ status = true;
+ }
+ return true;
+ }
+};
+
+struct Item {
+ // these are sub-tags
+ NameAndStatus name_and_status;
+ int value;
+ int extra_value;
+
+ // these are attributes
+ std::string date;
+ std::string comment;
+
+ // intrusive XML decoding API
+ bool decode_xml(XMLObj *obj) {
+ if (!RGWXMLDecoder::decode_xml("NameAndStatus", name_and_status, obj, true)) {
+ // name amd status are mandatory
+ return false;
+ }
+ if (!RGWXMLDecoder::decode_xml("Value", value, obj, true)) {
+ // value is mandatory
+ return false;
+ }
+ if (!RGWXMLDecoder::decode_xml("ExtraValue", extra_value, obj, false)) {
+ // extra value is optional and defaults to zero
+ extra_value = 0;
+ }
+
+ // date attribute is optional
+ if (!obj->get_attr("Date", date)) {
+ date = "no date";
+ }
+ // comment attribute is optional
+ if (!obj->get_attr("Comment", comment)) {
+ comment = "no comment";
+ }
+
+ return true;
+ }
+};
+
+struct Items {
+ // these are sub-tags
+ std::list<Item> item_list;
+
+ // intrusive XML decoding API
+ bool decode_xml(XMLObj *obj) {
+ do_decode_xml_obj(item_list, "Item", obj);
+ return true;
+ }
+};
+
+// in case of non-intrusive decoding class
+// hierarchy should reflect the XML hierarchy
+
+class NameXMLObj: public XMLObj {
+protected:
+ void xml_handle_data(const char *s, int len) override {
+ // no need to set "data", setting "name" directly
+ value.append(s, len);
+ }
+
+public:
+ std::string value;
+ ~NameXMLObj() override = default;
+};
+
+class StatusXMLObj: public XMLObj {
+protected:
+ void xml_handle_data(const char *s, int len) override {
+ std::istringstream is(std::string(s, len));
+ is >> std::boolalpha >> value;
+ }
+
+public:
+ bool value;
+ ~StatusXMLObj() override = default;
+};
+
+class NameAndStatusXMLObj: public NameAndStatus, public XMLObj {
+public:
+ ~NameAndStatusXMLObj() override = default;
+
+ bool xml_end(const char *el) override {
+ XMLObjIter iter = find("Name");
+ NameXMLObj* _name = static_cast<NameXMLObj*>(iter.get_next());
+ if (!_name) {
+ // name is mandatory
+ return false;
+ }
+ name = _name->value;
+ iter = find("Status");
+ StatusXMLObj* _status = static_cast<StatusXMLObj*>(iter.get_next());
+ if (!_status) {
+ // status is optional and defaults to True
+ status = true;
+ } else {
+ status = _status->value;
+ }
+ return true;
+ }
+};
+
+class ItemXMLObj: public Item, public XMLObj {
+public:
+ ~ItemXMLObj() override = default;
+
+ bool xml_end(const char *el) override {
+ XMLObjIter iter = find("NameAndStatus");
+ NameAndStatusXMLObj* _name_and_status = static_cast<NameAndStatusXMLObj*>(iter.get_next());
+ if (!_name_and_status) {
+ // name and status are mandatory
+ return false;
+ }
+ name_and_status = *static_cast<NameAndStatus*>(_name_and_status);
+ iter = find("Value");
+ XMLObj* _value = iter.get_next();
+ if (!_value) {
+ // value is mandatory
+ return false;
+ }
+ try {
+ value = std::stoi(_value->get_data());
+ } catch (const std::exception& e) {
+ return false;
+ }
+ iter = find("ExtraValue");
+ XMLObj* _extra_value = iter.get_next();
+ if (_extra_value) {
+ // extra value is optional but cannot contain garbage
+ try {
+ extra_value = std::stoi(_extra_value->get_data());
+ } catch (const std::exception& e) {
+ return false;
+ }
+ } else {
+ // if not set, it defaults to zero
+ extra_value = 0;
+ }
+
+ // date attribute is optional
+ if (!get_attr("Date", date)) {
+ date = "no date";
+ }
+ // comment attribute is optional
+ if (!get_attr("Comment", comment)) {
+ comment = "no comment";
+ }
+
+ return true;
+ }
+};
+
+class ItemsXMLObj: public Items, public XMLObj {
+public:
+ ~ItemsXMLObj() override = default;
+
+ bool xml_end(const char *el) override {
+ XMLObjIter iter = find("Item");
+ ItemXMLObj* item_ptr = static_cast<ItemXMLObj*>(iter.get_next());
+ // mandatory to have at least one item
+ bool item_found = false;
+ while (item_ptr) {
+ item_list.push_back(*static_cast<Item*>(item_ptr));
+ item_ptr = static_cast<ItemXMLObj*>(iter.get_next());
+ item_found = true;
+ }
+ return item_found;
+ }
+};
+
+class ItemsXMLParser: public RGWXMLParser {
+ static const int MAX_NAME_LEN = 16;
+public:
+ XMLObj *alloc_obj(const char *el) override {
+ if (strncmp(el, "Items", MAX_NAME_LEN) == 0) {
+ items = new ItemsXMLObj;
+ return items;
+ } else if (strncmp(el, "Item", MAX_NAME_LEN) == 0) {
+ return new ItemXMLObj;
+ } else if (strncmp(el, "NameAndStatus", MAX_NAME_LEN) == 0) {
+ return new NameAndStatusXMLObj;
+ } else if (strncmp(el, "Name", MAX_NAME_LEN) == 0) {
+ return new NameXMLObj;
+ } else if (strncmp(el, "Status", MAX_NAME_LEN) == 0) {
+ return new StatusXMLObj;
+ }
+ return nullptr;
+ }
+ // this is a pointer to the parsed results
+ ItemsXMLObj* items;
+};
+
+static const char* good_input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Items>"
+ "<Item><NameAndStatus><Name>hello</Name></NameAndStatus><Value>1</Value></Item>"
+ "<Item><ExtraValue>99</ExtraValue><NameAndStatus><Name>world</Name></NameAndStatus><Value>2</Value></Item>"
+ "<Item><Value>3</Value><NameAndStatus><Name>foo</Name></NameAndStatus></Item>"
+ "<Item><Value>4</Value><ExtraValue>42</ExtraValue><NameAndStatus><Name>bar</Name><Status>False</Status></NameAndStatus></Item>"
+ "</Items>";
+
+static const char* expected_output = "((hello,1),1,0),((world,1),2,99),((foo,1),3,0),((bar,0),4,42),";
+
+std::string to_string(const Items& items) {
+ std::stringstream ss;
+ for (const auto& item : items.item_list) {
+ ss << "((" << item.name_and_status.name << "," << item.name_and_status.status << ")," << item.value << "," << item.extra_value << ")" << ",";
+ }
+ return ss.str();
+}
+
+std::string to_string_with_attributes(const Items& items) {
+ std::stringstream ss;
+ for (const auto& item : items.item_list) {
+ ss << "(" << item.date << "," << item.comment << ",(" << item.name_and_status.name << "," << item.name_and_status.status << "),"
+ << item.value << "," << item.extra_value << ")" << ",";
+ }
+ return ss.str();
+}
+
+TEST(TestParser, BasicParsing)
+{
+ ItemsXMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(good_input, strlen(good_input), 1));
+ ASSERT_EQ(parser.items->item_list.size(), 4U);
+ ASSERT_STREQ(to_string(*parser.items).c_str(), expected_output);
+}
+
+static const char* malformed_input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Items>"
+ "<Item><NameAndStatus><Name>hello</Name></NameAndStatus><Value>1</Value><Item>"
+ "<Item><ExtraValue>99</ExtraValue><NameAndStatus><Name>world</Name></NameAndStatus><Value>2</Value></Item>"
+ "<Item><Value>3</Value><NameAndStatus><Name>foo</Name></NameAndStatus></Item>"
+ "<Item><Value>4</Value><ExtraValue>42</ExtraValue><NameAndStatus><Name>bar</Name><Status>False</Status></NameAndStatus></Item>"
+ "</Items>";
+
+TEST(TestParser, MalformedInput)
+{
+ ItemsXMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_FALSE(parser.parse(good_input, strlen(malformed_input), 1));
+}
+
+static const char* missing_value_input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Items>"
+ "<Item><NameAndStatus><Name>hello</Name></NameAndStatus><Value>1</Value></Item>"
+ "<Item><ExtraValue>99</ExtraValue><NameAndStatus><Name>world</Name></NameAndStatus><Value>2</Value></Item>"
+ "<Item><Value>3</Value><NameAndStatus><Name>foo</Name></NameAndStatus></Item>"
+ "<Item><ExtraValue>42</ExtraValue><NameAndStatus><Name>bar</Name><Status>False</Status></NameAndStatus></Item>"
+ "</Items>";
+
+TEST(TestParser, MissingMandatoryTag)
+{
+ ItemsXMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_FALSE(parser.parse(missing_value_input, strlen(missing_value_input), 1));
+}
+
+static const char* unknown_tag_input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Items>"
+ "<Item><NameAndStatus><Name>hello</Name></NameAndStatus><Value>1</Value></Item>"
+ "<Item><ExtraValue>99</ExtraValue><NameAndStatus><Name>world</Name></NameAndStatus><Value>2</Value></Item>"
+ "<Item><Value>3</Value><NameAndStatus><Name>foo</Name></NameAndStatus><Kaboom>0</Kaboom></Item>"
+ "<Item><Value>4</Value><ExtraValue>42</ExtraValue><NameAndStatus><Name>bar</Name><Status>False</Status></NameAndStatus></Item>"
+ "<Kaboom>0</Kaboom>"
+ "</Items>";
+
+TEST(TestParser, UnknownTag)
+{
+ ItemsXMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(unknown_tag_input, strlen(unknown_tag_input), 1));
+ ASSERT_EQ(parser.items->item_list.size(), 4U);
+ ASSERT_STREQ(to_string(*parser.items).c_str(), expected_output);
+}
+
+static const char* invalid_value_input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Items>"
+ "<Item><NameAndStatus><Name>hello</Name></NameAndStatus><Value>1</Value></Item>"
+ "<Item><ExtraValue>kaboom</ExtraValue><NameAndStatus><Name>world</Name></NameAndStatus><Value>2</Value></Item>"
+ "<Item><Value>3</Value><NameAndStatus><Name>foo</Name></NameAndStatus></Item>"
+ "<Item><Value>4</Value><ExtraValue>42</ExtraValue><NameAndStatus><Name>bar</Name><Status>False</Status></NameAndStatus></Item>"
+ "</Items>";
+
+TEST(TestParser, InvalidValue)
+{
+ ItemsXMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_FALSE(parser.parse(invalid_value_input, strlen(invalid_value_input), 1));
+}
+
+static const char* good_input1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Items>"
+ "<Item><NameAndStatus><Name>hello</Name></NameAndStatus><Value>1</Value></Item>"
+ "<Item><ExtraValue>99</ExtraValue><NameAndStatus><Name>world</Name>";
+
+static const char* good_input2 = "</NameAndStatus><Value>2</Value></Item>"
+ "<Item><Value>3</Value><NameAndStatus><Name>foo</Name></NameAndStatus></Item>"
+ "<Item><Value>4</Value><ExtraValue>42</ExtraValue><NameAndStatus><Name>bar</Name><Status>False</Status></NameAndStatus></Item>"
+ "</Items>";
+
+TEST(TestParser, MultipleChunks)
+{
+ ItemsXMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(good_input1, strlen(good_input1), 0));
+ ASSERT_TRUE(parser.parse(good_input2, strlen(good_input2), 1));
+ ASSERT_EQ(parser.items->item_list.size(), 4U);
+ ASSERT_STREQ(to_string(*parser.items).c_str(), expected_output);
+}
+
+static const char* input_with_attributes = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Items>"
+ "<Item Date=\"Tue Dec 27 17:21:29 2011\" Kaboom=\"just ignore\">"
+ "<NameAndStatus><Name>hello</Name></NameAndStatus><Value>1</Value>"
+ "</Item>"
+ "<Item Comment=\"hello world\">"
+ "<ExtraValue>99</ExtraValue><NameAndStatus><Name>world</Name></NameAndStatus><Value>2</Value>"
+ "</Item>"
+ "<Item><Value>3</Value><NameAndStatus><Name>foo</Name></NameAndStatus></Item>"
+ "<Item Comment=\"goodbye\" Date=\"Thu Feb 28 10:00:18 UTC 2019 \">"
+ "<Value>4</Value><ExtraValue>42</ExtraValue><NameAndStatus><Name>bar</Name><Status>False</Status></NameAndStatus>"
+ "</Item>"
+ "</Items>";
+
+static const char* expected_output_with_attributes = "(Tue Dec 27 17:21:29 2011,no comment,(hello,1),1,0),"
+ "(no date,hello world,(world,1),2,99),"
+ "(no date,no comment,(foo,1),3,0),"
+ "(Thu Feb 28 10:00:18 UTC 2019 ,goodbye,(bar,0),4,42),";
+
+TEST(TestParser, Attributes)
+{
+ ItemsXMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(input_with_attributes, strlen(input_with_attributes), 1));
+ ASSERT_EQ(parser.items->item_list.size(), 4U);
+ ASSERT_STREQ(to_string_with_attributes(*parser.items).c_str(),
+ expected_output_with_attributes);
+}
+
+TEST(TestDecoder, BasicParsing)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(good_input, strlen(good_input), 1));
+ Items result;
+ ASSERT_NO_THROW({
+ ASSERT_TRUE(RGWXMLDecoder::decode_xml("Items", result, &parser, true));
+ });
+ ASSERT_EQ(result.item_list.size(), 4U);
+ ASSERT_STREQ(to_string(result).c_str(), expected_output);
+}
+
+TEST(TestDecoder, MalfomedInput)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_FALSE(parser.parse(good_input, strlen(malformed_input), 1));
+}
+
+TEST(TestDecoder, MissingMandatoryTag)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(missing_value_input, strlen(missing_value_input), 1));
+ Items result;
+ ASSERT_ANY_THROW({
+ ASSERT_TRUE(RGWXMLDecoder::decode_xml("Items", result, &parser, true));
+ });
+}
+
+TEST(TestDecoder, InvalidValue)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(invalid_value_input, strlen(invalid_value_input), 1));
+ Items result;
+ ASSERT_ANY_THROW({
+ ASSERT_TRUE(RGWXMLDecoder::decode_xml("Items", result, &parser, true));
+ });
+}
+
+TEST(TestDecoder, MultipleChunks)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(good_input1, strlen(good_input1), 0));
+ ASSERT_TRUE(parser.parse(good_input2, strlen(good_input2), 1));
+ Items result;
+ ASSERT_NO_THROW({
+ ASSERT_TRUE(RGWXMLDecoder::decode_xml("Items", result, &parser, true));
+ });
+ ASSERT_EQ(result.item_list.size(), 4U);
+ ASSERT_STREQ(to_string(result).c_str(), expected_output);
+}
+
+TEST(TestDecoder, Attributes)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(input_with_attributes, strlen(input_with_attributes), 1));
+ Items result;
+ ASSERT_NO_THROW({
+ ASSERT_TRUE(RGWXMLDecoder::decode_xml("Items", result, &parser, true));
+ });
+ ASSERT_EQ(result.item_list.size(), 4U);
+ ASSERT_STREQ(to_string_with_attributes(result).c_str(),
+ expected_output_with_attributes);
+}
+
+static const char* expected_xml_output = "<Items xmlns=\"https://www.ceph.com/doc/\">"
+ "<Item Order=\"0\"><NameAndStatus><Name>hello</Name><Status>True</Status></NameAndStatus><Value>0</Value></Item>"
+ "<Item Order=\"1\"><NameAndStatus><Name>hello</Name><Status>False</Status></NameAndStatus><Value>1</Value></Item>"
+ "<Item Order=\"2\"><NameAndStatus><Name>hello</Name><Status>True</Status></NameAndStatus><Value>2</Value></Item>"
+ "<Item Order=\"3\"><NameAndStatus><Name>hello</Name><Status>False</Status></NameAndStatus><Value>3</Value></Item>"
+ "<Item Order=\"4\"><NameAndStatus><Name>hello</Name><Status>True</Status></NameAndStatus><Value>4</Value></Item>"
+ "</Items>";
+TEST(TestEncoder, ListWithAttrsAndNS)
+{
+ XMLFormatter f;
+ const auto array_size = 5;
+ f.open_array_section_in_ns("Items", "https://www.ceph.com/doc/");
+ for (auto i = 0; i < array_size; ++i) {
+ FormatterAttrs item_attrs("Order", std::to_string(i).c_str(), NULL);
+ f.open_object_section_with_attrs("Item", item_attrs);
+ f.open_object_section("NameAndStatus");
+ encode_xml("Name", "hello", &f);
+ encode_xml("Status", (i%2 == 0), &f);
+ f.close_section();
+ encode_xml("Value", i, &f);
+ f.close_section();
+ }
+ f.close_section();
+ std::stringstream ss;
+ f.flush(ss);
+ ASSERT_STREQ(ss.str().c_str(), expected_xml_output);
+}
+
diff --git a/src/test/run-cli-tests b/src/test/run-cli-tests
new file mode 100755
index 000000000..9572c6834
--- /dev/null
+++ b/src/test/run-cli-tests
@@ -0,0 +1,64 @@
+#!/bin/sh
+set -e
+
+SRCDIR="$(dirname "$0")"
+
+# build directory, if different, can be passed as an argument;
+# it is expected to point to the equivalent subdirectory of the
+# tree as where this script is stored
+BUILDDIR="$SRCDIR"
+case "$1" in
+ ''|-*)
+ # not set or looks like a flag to cram
+ ;;
+ *)
+ # looks like the builddir
+ BUILDDIR="$1"
+ shift
+ ;;
+esac
+
+VENV="$BUILDDIR/virtualenv"
+CRAM_BIN="$VENV/bin/cram"
+if [ ! -e "$CRAM_BIN" ]; then
+ # With "make distcheck", the source directory must be read-only. I
+ # patched cram to support that. See upstream ticket at
+ # https://bitbucket.org/brodie/cram/issue/9/allow-read-only-directories-for-t
+ # -- tv@inktank.com
+ python3 -m venv "$VENV" && $VENV/bin/pip --log "$VENV"/log.txt \
+ install git+https://github.com/ceph/cram.git@0.7-error-dir#egg=cram
+fi
+
+SRCDIR_ABS="$(readlink -f "$SRCDIR")"
+BUILDDIR_ABS="$(readlink -f "$BUILDDIR")"
+FAKE_HOME="$BUILDDIR_ABS/fake_home"
+mkdir -p "$FAKE_HOME"
+
+# cram doesn't like seeing the same foo.t basename twice on the same
+# run, so run it once per directory
+FAILED=0
+FAILEDTOOLS=""
+for tool in "$SRCDIR"/cli/*; do
+ toolname="$(basename "$tool")"
+ install -d -m0755 -- "$BUILDDIR/cli/$toolname"
+ if ! env -i \
+ PATH="$BUILDDIR_ABS/..:$SRCDIR_ABS/..:$PATH" \
+ CEPH_CONF=/dev/null \
+ CEPH_ARGS="--no-mon-config" \
+ CCACHE_DIR="$CCACHE_DIR" \
+ CC="$CC" \
+ CXX="$CXX" \
+ HOME="$FAKE_HOME" \
+ "$SRCDIR/run-cli-tests-maybe-unset-ccache" \
+ "$CRAM_BIN" -v "$@" --error-dir="$BUILDDIR/cli/$toolname" -- "$tool"/*.t
+ then
+ FAILED=1
+ FAILEDTOOLS="$FAILEDTOOLS $toolname"
+ fi
+done
+
+if [ $FAILED -eq 1 ]; then
+ echo "Tests that failed: $FAILEDTOOLS"
+fi
+
+exit "$FAILED"
diff --git a/src/test/run-cli-tests-maybe-unset-ccache b/src/test/run-cli-tests-maybe-unset-ccache
new file mode 100755
index 000000000..ba72b0622
--- /dev/null
+++ b/src/test/run-cli-tests-maybe-unset-ccache
@@ -0,0 +1,20 @@
+#!/bin/sh
+set -e
+
+# ccache breaks if it sees CCACHE_DIR="", yet due to clumsiness of
+# /usr/bin/env, it's hard to avoid setting env vars for the parent;
+# unset them if they're empty
+
+if [ -z "$CCACHE_DIR" ]; then
+ unset CCACHE_DIR
+fi
+
+if [ -z "$CC" ]; then
+ unset CC
+fi
+
+if [ -z "$CXX" ]; then
+ unset CXX
+fi
+
+exec "$@"
diff --git a/src/test/run-rbd-tests b/src/test/run-rbd-tests
new file mode 100755
index 000000000..14bd24de8
--- /dev/null
+++ b/src/test/run-rbd-tests
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+set -ex
+
+# this should be run from the src directory in the ceph.git
+
+source $(dirname $0)/detect-build-env-vars.sh
+CEPH_SRC=$CEPH_ROOT/src
+if [ -e CMakeCache.txt ]; then
+ CYTHON_MODULES_DIR=$CEPH_LIB/cython_modules
+else
+ CYTHON_MODULES_DIR=$CEPH_SRC/build
+fi
+
+export PYTHONPATH="$CEPH_SRC/pybind:$CEPH_SRC/test/pybind:$CYTHON_MODULES_DIR/lib.3"
+
+recreate_pool() {
+ POOL_NAME=$1
+ PG_NUM=100
+ ceph osd pool delete $POOL_NAME $POOL_NAME --yes-i-really-really-mean-it
+ ceph osd pool create $POOL_NAME $PG_NUM
+ rbd pool init $POOL_NAME
+}
+
+run_cli_tests() {
+ recreate_pool rbd
+ $CEPH_SRC/../qa/workunits/rbd/import_export.sh
+ recreate_pool rbd
+ $CEPH_SRC/../qa/workunits/rbd/cli_generic.sh
+ recreate_pool rbd
+ $CEPH_SRC/../qa/workunits/rbd/journal.sh
+ recreate_pool rbd
+ $CEPH_SRC/../qa/workunits/rbd/luks-encryption.sh
+}
+
+# tests that do not depend on image format / features
+run_generic_tests() {
+ $CEPH_SRC/../qa/workunits/rbd/verify_pool.sh
+
+ recreate_pool rbd
+ $CEPH_SRC/../qa/workunits/rbd/journal.sh
+ recreate_pool rbd
+ $CEPH_SRC/../qa/workunits/rbd/test_admin_socket.sh
+}
+
+run_api_tests() {
+ # skip many_snaps since it takes several minutes
+ python3 -m nose -v test_rbd -e '.*many_snaps'
+ # ceph_test_librbd creates its own pools
+ ceph_test_librbd
+}
+
+ceph_test_cls_rbd
+run_generic_tests
+run_api_tests
+run_cli_tests
+
+export RBD_CREATE_ARGS="--image-format 2"
+run_cli_tests
+
+for i in 0 1 61 109
+do
+ export RBD_FEATURES=$i
+ run_api_tests
+done
+
+echo OK
diff --git a/src/test/run-rbd-unit-tests.sh b/src/test/run-rbd-unit-tests.sh
new file mode 100755
index 000000000..8c5a29ee6
--- /dev/null
+++ b/src/test/run-rbd-unit-tests.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -ex
+
+# this should be run from the src directory in the ceph.git
+
+source $(dirname $0)/detect-build-env-vars.sh
+PATH="$CEPH_BIN:$PATH"
+
+if [ $# = 0 ]; then
+ # mimic the old behaviour
+ TESTS='0 1 61 109 127'
+ unset RBD_FEATURES; unittest_librbd
+elif [ $# = 1 -a "${1}" = N ] ; then
+ # new style no feature request
+ unset RBD_FEATURES; unittest_librbd
+else
+ TESTS="$*"
+fi
+
+for i in ${TESTS}
+do
+ RBD_FEATURES=$i unittest_librbd
+done
+
+echo OK
diff --git a/src/test/run-rbd-valgrind-unit-tests.sh b/src/test/run-rbd-valgrind-unit-tests.sh
new file mode 100755
index 000000000..463403923
--- /dev/null
+++ b/src/test/run-rbd-valgrind-unit-tests.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+set -ex
+
+# this should be run from the src directory in the ceph.git (when built with
+# automake) or cmake build directory
+
+source $(dirname $0)/detect-build-env-vars.sh
+
+RBD_FEATURES=13 valgrind --tool=memcheck --leak-check=full --error-exitcode=1 \
+ --suppressions=${CEPH_ROOT}/qa/valgrind.supp unittest_librbd
+
+echo OK
diff --git a/src/test/run_cmd.cc b/src/test/run_cmd.cc
new file mode 100644
index 000000000..ada624e8b
--- /dev/null
+++ b/src/test/run_cmd.cc
@@ -0,0 +1,26 @@
+#include "common/config.h"
+#include "common/run_cmd.h"
+
+#include "gtest/gtest.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+TEST(RunCommand, StringSimple)
+{
+ char temp_file_name[] = "run_cmd_temp_file_XXXXXX";
+
+ int fd = ::mkstemp(temp_file_name);
+ ASSERT_GE(fd, 0);
+ ::close(fd);
+
+ std::string ret = run_cmd("touch", temp_file_name, (char*)NULL);
+ ASSERT_EQ(ret, "");
+
+ ASSERT_EQ(access(temp_file_name, R_OK), 0);
+
+ ret = run_cmd("rm", "-f", temp_file_name, (char*)NULL);
+ ASSERT_EQ(ret, "");
+
+ ASSERT_NE(access(temp_file_name, R_OK), 0);
+}
diff --git a/src/test/signals.cc b/src/test/signals.cc
new file mode 100644
index 000000000..dc24900a8
--- /dev/null
+++ b/src/test/signals.cc
@@ -0,0 +1,158 @@
+#include "common/config.h"
+#include "common/signal.h"
+#include "global/signal_handler.h"
+#include "common/debug.h"
+#include "include/coredumpctl.h"
+
+#include "gtest/gtest.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "include/ceph_assert.h"
+
+#define dout_context g_ceph_context
+static volatile sig_atomic_t got_sigusr1 = 0;
+
+static void handle_sigusr1(int signo)
+{
+ got_sigusr1 = 1;
+}
+
+TEST(SignalApi, SimpleInstall)
+{
+ install_sighandler(SIGPIPE, handle_sigusr1, 0);
+}
+
+TEST(SignalApi, SimpleInstallAndTest)
+{
+ install_sighandler(SIGPIPE, handle_sigusr1, 0);
+
+ // SIGPIPE starts out blocked
+ int ret = kill(getpid(), SIGPIPE);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(got_sigusr1, 0);
+
+ // handle SIGPIPE
+ sigset_t mask;
+ sigemptyset(&mask);
+ ret = sigsuspend(&mask);
+ if (ret == -1)
+ ret = errno;
+
+ // we should have gotten it
+ ASSERT_EQ(ret, EINTR);
+ ASSERT_EQ(got_sigusr1, 1);
+}
+
+TEST(SignalEffects, ErrnoTest1)
+{
+}
+
+bool usr1 = false;
+bool usr2 = false;
+
+void reset()
+{
+ usr1 = false;
+ usr2 = false;
+}
+
+void testhandler(int signal)
+{
+ switch (signal) {
+ case SIGUSR1:
+ usr1 = true;
+ break;
+ case SIGUSR2:
+ usr2 = true;
+ break;
+ default:
+ ceph_abort_msg("unexpected signal");
+ }
+}
+
+TEST(SignalHandler, Single)
+{
+ reset();
+ init_async_signal_handler();
+ register_async_signal_handler(SIGUSR1, testhandler);
+ ASSERT_TRUE(usr1 == false);
+
+ int ret = kill(getpid(), SIGUSR1);
+ ASSERT_EQ(ret, 0);
+
+ sleep(1);
+ ASSERT_TRUE(usr1 == true);
+
+ unregister_async_signal_handler(SIGUSR1, testhandler);
+ shutdown_async_signal_handler();
+}
+
+TEST(SignalHandler, Multiple)
+{
+ int ret;
+
+ reset();
+ init_async_signal_handler();
+ register_async_signal_handler(SIGUSR1, testhandler);
+ register_async_signal_handler(SIGUSR2, testhandler);
+ ASSERT_TRUE(usr1 == false);
+ ASSERT_TRUE(usr2 == false);
+
+ ret = kill(getpid(), SIGUSR1);
+ ASSERT_EQ(ret, 0);
+ ret = kill(getpid(), SIGUSR2);
+ ASSERT_EQ(ret, 0);
+
+ sleep(1);
+ ASSERT_TRUE(usr1 == true);
+ ASSERT_TRUE(usr2 == true);
+
+ unregister_async_signal_handler(SIGUSR1, testhandler);
+ unregister_async_signal_handler(SIGUSR2, testhandler);
+ shutdown_async_signal_handler();
+}
+
+TEST(SignalHandler, LogInternal)
+{
+ g_ceph_context->_log->inject_segv();
+ {
+ PrCtl unset_dumpable;
+ ASSERT_DEATH(derr << "foo" << dendl, ".*");
+ }
+ g_ceph_context->_log->reset_segv();
+}
+
+
+/*
+TEST(SignalHandler, MultipleBigFd)
+{
+ int ret;
+
+ for (int i = 0; i < 1500; i++)
+ ::open(".", O_RDONLY);
+
+ reset();
+ init_async_signal_handler();
+ register_async_signal_handler(SIGUSR1, testhandler);
+ register_async_signal_handler(SIGUSR2, testhandler);
+ ASSERT_TRUE(usr1 == false);
+ ASSERT_TRUE(usr2 == false);
+
+ ret = kill(getpid(), SIGUSR1);
+ ASSERT_EQ(ret, 0);
+ ret = kill(getpid(), SIGUSR2);
+ ASSERT_EQ(ret, 0);
+
+ sleep(1);
+ ASSERT_TRUE(usr1 == true);
+ ASSERT_TRUE(usr2 == true);
+
+ unregister_async_signal_handler(SIGUSR1, testhandler);
+ unregister_async_signal_handler(SIGUSR2, testhandler);
+ shutdown_async_signal_handler();
+}
+*/
diff --git a/src/test/simple_spin.cc b/src/test/simple_spin.cc
new file mode 100644
index 000000000..4afa67074
--- /dev/null
+++ b/src/test/simple_spin.cc
@@ -0,0 +1,135 @@
+
+#include <future>
+
+#include "gtest/gtest.h"
+
+#include "include/spinlock.h"
+
+using ceph::spin_lock;
+using ceph::spin_unlock;
+
+static std::atomic_flag lock = ATOMIC_FLAG_INIT;
+static int64_t counter = 0;
+
+TEST(SimpleSpin, Test0)
+{
+ std::atomic_flag lock0 = ATOMIC_FLAG_INIT;
+ spin_lock(&lock0);
+ spin_unlock(&lock0);
+}
+
+static void* mythread(void *v)
+{
+ for (int j = 0; j < 1000000; ++j) {
+ spin_lock(&lock);
+ counter++;
+ spin_unlock(&lock);
+ }
+ return NULL;
+}
+
+TEST(SimpleSpin, Test1)
+{
+ counter = 0;
+ const auto n = 2000000U;
+
+ int ret;
+ pthread_t thread1;
+ pthread_t thread2;
+ ret = pthread_create(&thread1, NULL, mythread, NULL);
+ ASSERT_EQ(0, ret);
+ ret = pthread_create(&thread2, NULL, mythread, NULL);
+ ASSERT_EQ(0, ret);
+ ret = pthread_join(thread1, NULL);
+ ASSERT_EQ(0, ret);
+ ret = pthread_join(thread2, NULL);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(n, counter);
+
+ // Should also work with pass-by-reference:
+ // (Note that we don't care about cross-threading here as-such.)
+ counter = 0;
+ auto f = async(std::launch::async, []() {
+ for(int i = 0; n != i; ++i) {
+ spin_lock(lock);
+ counter++;
+ spin_unlock(lock);
+ }
+ });
+ f.wait();
+ ASSERT_EQ(n, counter);
+}
+
+template <typename LockT>
+int64_t check_lock_unlock(const int64_t n, int64_t& cntr, LockT& lock)
+{
+ auto do_lock_unlock = [&]() -> int64_t {
+ int64_t i = 0;
+
+ for(; n != i; ++i) {
+ spin_lock(lock);
+ cntr++;
+ spin_unlock(lock);
+ }
+
+ return i;
+ };
+
+ auto fone = async(std::launch::async, do_lock_unlock);
+ auto ftwo = async(std::launch::async, do_lock_unlock);
+ auto fthree = async(std::launch::async, do_lock_unlock);
+
+ auto one = fone.get();
+ auto two = ftwo.get();
+ auto three = fthree.get();
+
+ // Google test doesn't like us using its macros out of individual tests, so:
+ if(n != one || n != two || n != three)
+ return 0;
+
+ return one + two + three;
+}
+
+TEST(SimpleSpin, Test2)
+{
+ const auto n = 2000000U;
+
+ // ceph::spinlock:
+ {
+ counter = 0;
+ ceph::spinlock l;
+
+ ASSERT_EQ(0, counter);
+ auto result = check_lock_unlock(n, counter, l);
+ ASSERT_NE(0, counter);
+ ASSERT_EQ(counter, result);
+ }
+}
+
+// ceph::spinlock should work with std::lock_guard<>:
+TEST(SimpleSpin, spinlock_guard)
+{
+ const auto n = 2000000U;
+
+ ceph::spinlock sl;
+
+ counter = 0;
+ auto f = async(std::launch::async, [&sl]() {
+ for(int i = 0; n != i; ++i) {
+ std::lock_guard<ceph::spinlock> g(sl);
+ counter++;
+ }
+ });
+
+ auto g = async(std::launch::async, [&sl]() {
+ for(int i = 0; n != i; ++i) {
+ std::lock_guard<ceph::spinlock> g(sl);
+ counter++;
+ }
+ });
+
+ f.wait();
+ g.wait();
+ ASSERT_EQ(2*n, counter);
+}
+
diff --git a/src/test/smoke.sh b/src/test/smoke.sh
new file mode 100755
index 000000000..b66266e6b
--- /dev/null
+++ b/src/test/smoke.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+
+source $CEPH_ROOT/qa/standalone/ceph-helpers.sh
+
+mon_port=$(get_unused_port)
+
+function run() {
+ local dir=$1
+ shift
+
+ export CEPH_MON="127.0.0.1:$mon_port"
+ export CEPH_ARGS
+ CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
+ CEPH_ARGS+="--mon-host=$CEPH_MON "
+ set -e
+
+ local funcs=${@:-$(set | sed -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')}
+ for func in $funcs ; do
+ setup $dir || return 1
+ $func $dir || return 1
+ teardown $dir || return 1
+ done
+}
+
+function TEST_minimal() {
+ local dir=$1
+
+ run_mon $dir a
+ run_mgr $dir x
+ run_osd $dir 0
+ run_osd $dir 1
+ run_osd $dir 2
+ create_rbd_pool
+ wait_for_clean
+}
+
+function TEST_multimon() {
+ local dir=$1
+ MONA="127.0.0.1:$((mon_port++))"
+ MONB="127.0.0.1:$((mon_port++))"
+ MONC="127.0.0.1:$((mon_port++))"
+
+ run_mon $dir a --public-addr $MONA
+ run_mon $dir b --public-addr $MONB
+ run_mon $dir c --public_addr $MONC
+ run_mgr $dir x
+ run_mgr $dir y
+ run_osd $dir 0
+ run_osd $dir 1
+ run_osd $dir 2
+
+ ceph osd pool create foo 32
+ ceph osd out 0
+ wait_for_clean
+
+ timeout 20 rados -p foo bench 4 write -b 4096 --no-cleanup || return 1
+ wait_for_clean
+
+ ceph osd in 0
+ flush_pg_stats
+ wait_for_clean
+}
+
+main smoke "$@"
diff --git a/src/test/strtol.cc b/src/test/strtol.cc
new file mode 100644
index 000000000..ec3f6715b
--- /dev/null
+++ b/src/test/strtol.cc
@@ -0,0 +1,649 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 Dreamhost
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <cmath>
+#include <string>
+#include <map>
+
+#include "common/strtol.h"
+
+#include "gtest/gtest.h"
+
+static void test_strict_strtoll(const char *str, long long expected, int base)
+{
+ std::string err;
+ long long val = strict_strtoll(str, base, &err);
+ if (!err.empty()) {
+ ASSERT_EQ(err, "");
+ }
+ else {
+ ASSERT_EQ(val, expected);
+ }
+}
+
+static void test_strict_strtol(const char *str, long expected)
+{
+ std::string err;
+ long val = strict_strtol(str, 10, &err);
+ if (!err.empty()) {
+ ASSERT_EQ(err, "");
+ }
+ else {
+ ASSERT_EQ(val, expected);
+ }
+}
+
+static void test_strict_strtod(const char *str, double expected)
+{
+ std::string err;
+ double val = strict_strtod(str, &err);
+ if (!err.empty()) {
+ ASSERT_EQ(err, "");
+ }
+ else {
+ // when comparing floats, use a margin of error
+ if ((expected - 0.001 > val) || (expected + 0.001 < val)) {
+ ASSERT_EQ(val, expected);
+ }
+ }
+}
+
+static void test_strict_strtof(const char *str, float expected)
+{
+ std::string err;
+ float val = strict_strtof(str, &err);
+ if (!err.empty()) {
+ ASSERT_EQ(err, "");
+ }
+ else {
+ // when comparing floats, use a margin of error
+ if ((expected - 0.001 > val) || (expected + 0.001 < val)) {
+ ASSERT_EQ(val, expected);
+ }
+ }
+}
+
+TEST(StrToL, Simple1) {
+ test_strict_strtoll("123", 123, 10);
+ test_strict_strtoll("0", 0, 10);
+ test_strict_strtoll("-123", -123, 10);
+ test_strict_strtoll("8796093022208", 8796093022208LL, 10);
+ test_strict_strtoll("-8796093022208", -8796093022208LL, 10);
+ test_strict_strtoll("123", 123, 0);
+ test_strict_strtoll("0x7b", 123, 0);
+ test_strict_strtoll("4d2", 1234, 16);
+
+ test_strict_strtol("208", 208);
+ test_strict_strtol("-4", -4);
+ test_strict_strtol("0", 0);
+ test_strict_strtol("2147483646", 2147483646);
+
+ test_strict_strtof("0.05", 0.05);
+ test_strict_strtof("0", 0.0);
+ test_strict_strtof("-0", 0.0);
+ test_strict_strtof("10000000.5", 10000000.5);
+
+ test_strict_strtod("-0.2", -0.2);
+ test_strict_strtod("0.1", 0.1);
+ test_strict_strtod("0", 0.0);
+}
+
+static void test_strict_strtoll_err(const char *str)
+{
+ std::string err;
+ strict_strtoll(str, 10, &err);
+ ASSERT_NE(err, "");
+}
+
+static void test_strict_strtol_err(const char *str)
+{
+ std::string err;
+ strict_strtol(str, 10, &err);
+ ASSERT_NE(err, "");
+}
+
+static void test_strict_strtod_err(const char *str)
+{
+ std::string err;
+ strict_strtod(str, &err);
+ ASSERT_NE(err, "");
+}
+
+static void test_strict_strtof_err(const char *str)
+{
+ std::string err;
+ strict_strtof(str, &err);
+ ASSERT_NE(err, "");
+}
+
+TEST(StrToL, Error1) {
+ test_strict_strtoll_err("604462909807314587353088"); // overflow
+ test_strict_strtoll_err("aw shucks"); // invalid
+ test_strict_strtoll_err("343245 aw shucks"); // invalid chars at end
+ test_strict_strtoll_err("-"); // invalid
+
+ test_strict_strtol_err("35 aw shucks"); // invalid chars at end
+ test_strict_strtol_err("--0");
+ test_strict_strtol_err("-");
+
+ test_strict_strtod_err("345345.0-");
+ test_strict_strtod_err("34.0 garbo");
+
+ test_strict_strtof_err("0.05.0");
+}
+
+
+static void test_strict_iecstrtoll(const char *str)
+{
+ std::string err;
+ strict_iecstrtoll(str, &err);
+ ASSERT_EQ(err, "");
+}
+
+static void test_strict_iecstrtoll_units(const std::string& foo,
+ std::string u, const int m)
+{
+ std::string s(foo);
+ s.append(u);
+ const char *str = s.c_str();
+ std::string err;
+ uint64_t r = strict_iecstrtoll(str, &err);
+ ASSERT_EQ(err, "");
+
+ str = foo.c_str();
+ std::string err2;
+ long long tmp = strict_strtoll(str, 10, &err2);
+ ASSERT_EQ(err2, "");
+ tmp = (tmp << m);
+ ASSERT_EQ(tmp, (long long)r);
+}
+
+TEST(IECStrToLL, WithUnits) {
+ std::map<std::string,int> units;
+ units["B"] = 0;
+ units["K"] = 10;
+ units["M"] = 20;
+ units["G"] = 30;
+ units["T"] = 40;
+ units["P"] = 50;
+ units["E"] = 60;
+ units["Ki"] = 10;
+ units["Mi"] = 20;
+ units["Gi"] = 30;
+ units["Ti"] = 40;
+ units["Pi"] = 50;
+ units["Ei"] = 60;
+
+ for (std::map<std::string,int>::iterator p = units.begin();
+ p != units.end(); ++p) {
+ // the upper bound of uint64_t is 2^64 = 4E
+ test_strict_iecstrtoll_units("4", p->first, p->second);
+ test_strict_iecstrtoll_units("1", p->first, p->second);
+ test_strict_iecstrtoll_units("0", p->first, p->second);
+ }
+}
+
+TEST(IECStrToLL, WithoutUnits) {
+ test_strict_iecstrtoll("1024");
+ test_strict_iecstrtoll("1152921504606846976");
+ test_strict_iecstrtoll("0");
+}
+
+static void test_strict_iecstrtoll_err(const char *str)
+{
+ std::string err;
+ strict_iecstrtoll(str, &err);
+ ASSERT_NE(err, "");
+}
+
+TEST(IECStrToLL, Error) {
+ test_strict_iecstrtoll_err("1024F");
+ test_strict_iecstrtoll_err("QDDSA");
+ test_strict_iecstrtoll_err("1b");
+ test_strict_iecstrtoll_err("100k");
+ test_strict_iecstrtoll_err("1000m");
+ test_strict_iecstrtoll_err("1g");
+ test_strict_iecstrtoll_err("20t");
+ test_strict_iecstrtoll_err("100p");
+ test_strict_iecstrtoll_err("1000e");
+ test_strict_iecstrtoll_err("B");
+ test_strict_iecstrtoll_err("M");
+ test_strict_iecstrtoll_err("BM");
+ test_strict_iecstrtoll_err("B0wef");
+ test_strict_iecstrtoll_err("0m");
+ test_strict_iecstrtoll_err("-1"); // it returns uint64_t
+ test_strict_iecstrtoll_err("-1K");
+ test_strict_iecstrtoll_err("1Bi");
+ test_strict_iecstrtoll_err("Bi");
+ test_strict_iecstrtoll_err("bi");
+ test_strict_iecstrtoll_err("gi");
+ test_strict_iecstrtoll_err("100ki");
+ test_strict_iecstrtoll_err("1000mi");
+ test_strict_iecstrtoll_err("1gi");
+ test_strict_iecstrtoll_err("20ti");
+ test_strict_iecstrtoll_err("100pi");
+ test_strict_iecstrtoll_err("1000ei");
+ // the upper bound of uint64_t is 2^64 = 4E, so 1024E overflows
+ test_strict_iecstrtoll_err("1024E"); // overflows after adding the suffix
+}
+
+// since strict_iecstrtoll is an alias of strict_iec_cast<uint64_t>(), quite a few
+// of cases are covered by existing test cases of strict_iecstrtoll already.
+TEST(StrictIECCast, Error) {
+ {
+ std::string err;
+ // the SI prefix is way too large for `int`.
+ (void)strict_iec_cast<int>("2E", &err);
+ ASSERT_NE(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_iec_cast<int>("-2E", &err);
+ ASSERT_NE(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_iec_cast<int>("1T", &err);
+ ASSERT_NE(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_iec_cast<int64_t>("2E", &err);
+ ASSERT_EQ(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_iec_cast<int64_t>("-2E", &err);
+ ASSERT_EQ(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_iec_cast<int64_t>("1T", &err);
+ ASSERT_EQ(err, "");
+ }
+}
+
+
+static void test_strict_sistrtoll(const char *str)
+{
+ std::string err;
+ strict_si_cast<uint64_t>(str, &err);
+ ASSERT_EQ(err, "");
+}
+
+static void test_strict_sistrtoll_units(const std::string& foo,
+ std::string u, const long long m)
+{
+ std::string s(foo);
+ s.append(u);
+ const char *str = s.c_str();
+ std::string err;
+ uint64_t r = strict_si_cast<uint64_t>(str, &err);
+ ASSERT_EQ(err, "");
+
+ str = foo.c_str();
+ std::string err2;
+ long long tmp = strict_strtoll(str, 10, &err2);
+ ASSERT_EQ(err2, "");
+ tmp = (tmp * m);
+ ASSERT_EQ(tmp, (long long)r);
+}
+
+TEST(SIStrToLL, WithUnits) {
+ std::map<std::string,long long> units;
+ units["K"] = pow(10, 3);
+ units["M"] = pow(10, 6);
+ units["G"] = pow(10, 9);
+ units["T"] = pow(10, 12);
+ units["P"] = pow(10, 15);
+ units["E"] = pow(10, 18);
+
+ for (std::map<std::string,long long>::iterator p = units.begin();
+ p != units.end(); ++p) {
+ // the upper bound of uint64_t is 2^64 = 4E
+ test_strict_sistrtoll_units("4", p->first, p->second);
+ test_strict_sistrtoll_units("1", p->first, p->second);
+ test_strict_sistrtoll_units("0", p->first, p->second);
+ }
+}
+
+TEST(SIStrToLL, WithoutUnits) {
+ test_strict_sistrtoll("1024");
+ test_strict_sistrtoll("1152921504606846976");
+ test_strict_sistrtoll("0");
+}
+
+static void test_strict_sistrtoll_err(const char *str)
+{
+ std::string err;
+ strict_si_cast<uint64_t>(str, &err);
+ ASSERT_NE(err, "");
+}
+
+TEST(SIStrToLL, Error) {
+ test_strict_sistrtoll_err("1024F");
+ test_strict_sistrtoll_err("QDDSA");
+ test_strict_sistrtoll_err("1b");
+ test_strict_sistrtoll_err("100k");
+ test_strict_sistrtoll_err("1000m");
+ test_strict_sistrtoll_err("1g");
+ test_strict_sistrtoll_err("20t");
+ test_strict_sistrtoll_err("100p");
+ test_strict_sistrtoll_err("1000e");
+ test_strict_sistrtoll_err("B");
+ test_strict_sistrtoll_err("M");
+ test_strict_sistrtoll_err("BM");
+ test_strict_sistrtoll_err("B0wef");
+ test_strict_sistrtoll_err("0m");
+ test_strict_sistrtoll_err("-1"); // it returns uint64_t
+ test_strict_sistrtoll_err("-1K");
+ test_strict_sistrtoll_err("1Bi");
+ test_strict_sistrtoll_err("Bi");
+ test_strict_sistrtoll_err("bi");
+ test_strict_sistrtoll_err("gi");
+ test_strict_sistrtoll_err("100ki");
+ test_strict_sistrtoll_err("1000mi");
+ test_strict_sistrtoll_err("1gi");
+ test_strict_sistrtoll_err("20ti");
+ test_strict_sistrtoll_err("100pi");
+ test_strict_sistrtoll_err("1000ei");
+ test_strict_sistrtoll_err("1B");
+ // the upper bound of uint64_t is 2^64 = 4E, so 1024E overflows
+ test_strict_sistrtoll_err("1024E"); // overflows after adding the suffix
+}
+
+// since strict_sistrtoll is an alias of strict_si_cast<uint64_t>(), quite a few
+// of cases are covered by existing test cases of strict_sistrtoll already.
+TEST(StrictSICast, Error) {
+ {
+ std::string err;
+ // the SI prefix is way too large for `int`.
+ (void)strict_si_cast<int>("2E", &err);
+ ASSERT_NE(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_si_cast<int>("-2E", &err);
+ ASSERT_NE(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_si_cast<int>("1T", &err);
+ ASSERT_NE(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_si_cast<int64_t>("2E", &err);
+ ASSERT_EQ(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_si_cast<int64_t>("-2E", &err);
+ ASSERT_EQ(err, "");
+ }
+ {
+ std::string err;
+ (void)strict_si_cast<int64_t>("1T", &err);
+ ASSERT_EQ(err, "");
+ }
+}
+
+
+using ceph::parse;
+using ceph::consume;
+using namespace std::literals;
+
+template<typename T>
+inline void test_parse() {
+ auto r = parse<T>("23"sv);
+ ASSERT_TRUE(r);
+ EXPECT_EQ(*r, 23);
+
+ r = parse<T>(" 23"sv);
+ EXPECT_FALSE(r);
+
+ r = parse<T>("-5"sv);
+ if constexpr (std::is_signed_v<T>) {
+ ASSERT_TRUE(r);
+ EXPECT_EQ(*r, -5);
+ } else {
+ EXPECT_FALSE(r);
+ }
+
+ r = parse<T>("meow"sv);
+ EXPECT_FALSE(r);
+
+ r = parse<T>("9yards"sv);
+ EXPECT_FALSE(r);
+}
+
+TEST(Parse, Char) {
+ test_parse<char>();
+}
+
+TEST(Parse, UChar) {
+ test_parse<unsigned char>();
+}
+
+TEST(Parse, SChar) {
+ test_parse<signed char>();
+}
+
+TEST(Parse, UInt8) {
+ test_parse<std::uint8_t>();
+}
+
+TEST(Parse, Int8) {
+ test_parse<std::int8_t>();
+}
+
+TEST(Parse, UInt16) {
+ test_parse<std::uint16_t>();
+}
+
+TEST(Parse, Int16) {
+ test_parse<std::int16_t>();
+}
+
+TEST(Parse, UInt32) {
+ test_parse<std::uint32_t>();
+}
+
+TEST(Parse, Int32) {
+ test_parse<std::int32_t>();
+}
+
+TEST(Parse, UInt64) {
+ test_parse<std::uint64_t>();
+}
+
+TEST(Parse, Int64) {
+ test_parse<std::int64_t>();
+}
+
+TEST(Parse, UIntMax) {
+ test_parse<std::uintmax_t>();
+}
+
+TEST(Parse, IntMax) {
+ test_parse<std::intmax_t>();
+}
+
+TEST(Parse, UIntPtr) {
+ test_parse<std::uintptr_t>();
+}
+
+TEST(Parse, IntPtr) {
+ test_parse<std::intptr_t>();
+}
+
+TEST(Parse, UShort) {
+ test_parse<unsigned short>();
+}
+
+TEST(Parse, Short) {
+ test_parse<short>();
+}
+
+TEST(Parse, ULong) {
+ test_parse<unsigned long>();
+}
+
+TEST(Parse, Long) {
+ test_parse<long>();
+}
+
+TEST(Parse, ULongLong) {
+ test_parse<unsigned long long>();
+}
+
+TEST(Parse, LongLong) {
+ test_parse<long long>();
+}
+
+template<typename T>
+inline void test_consume() {
+ auto pos = "23"sv;
+ auto spacepos = " 23"sv;
+ auto neg = "-5"sv;
+ auto meow = "meow"sv;
+ auto trail = "9yards"sv;
+
+ auto v = pos;
+ auto r = consume<T>(v);
+ ASSERT_TRUE(r);
+ EXPECT_EQ(*r, 23);
+ EXPECT_TRUE(v.empty());
+
+ v = spacepos;
+ r = consume<T>(v);
+ EXPECT_FALSE(r);
+ EXPECT_EQ(v, spacepos);
+
+ v = neg;
+ r = consume<T>(v);
+ if constexpr (std::is_signed_v<T>) {
+ ASSERT_TRUE(r);
+ EXPECT_EQ(*r, -5);
+ EXPECT_TRUE(v.empty());
+ } else {
+ EXPECT_FALSE(r);
+ EXPECT_EQ(v, neg);
+ }
+
+ v = meow;
+ r = consume<T>(v);
+ EXPECT_FALSE(r);
+ EXPECT_EQ(v, meow);
+
+ v = trail;
+ r = consume<T>(v);
+ ASSERT_TRUE(r);
+ EXPECT_EQ(*r, 9);
+ auto w = trail;
+ w.remove_prefix(1);
+ EXPECT_EQ(v, w);
+}
+
+TEST(Consume, Char) {
+ test_consume<char>();
+}
+
+TEST(Consume, UChar) {
+ test_consume<unsigned char>();
+}
+
+TEST(Consume, SChar) {
+ test_consume<signed char>();
+}
+
+TEST(Consume, UInt8) {
+ test_consume<std::uint8_t>();
+}
+
+TEST(Consume, Int8) {
+ test_consume<std::int8_t>();
+}
+
+TEST(Consume, UInt16) {
+ test_consume<std::uint16_t>();
+}
+
+TEST(Consume, Int16) {
+ test_consume<std::int16_t>();
+}
+
+TEST(Consume, UInt32) {
+ test_consume<std::uint32_t>();
+}
+
+TEST(Consume, Int32) {
+ test_consume<std::int32_t>();
+}
+
+TEST(Consume, UInt64) {
+ test_consume<std::uint64_t>();
+}
+
+TEST(Consume, Int64) {
+ test_consume<std::int64_t>();
+}
+
+TEST(Consume, UIntMax) {
+ test_consume<std::uintmax_t>();
+}
+
+TEST(Consume, IntMax) {
+ test_consume<std::intmax_t>();
+}
+
+TEST(Consume, UIntPtr) {
+ test_consume<std::uintptr_t>();
+}
+
+TEST(Consume, IntPtr) {
+ test_consume<std::intptr_t>();
+}
+
+TEST(Consume, UShort) {
+ test_consume<unsigned short>();
+}
+
+TEST(Consume, Short) {
+ test_consume<short>();
+}
+
+TEST(Consume, ULong) {
+ test_consume<unsigned long>();
+}
+
+TEST(Consume, Long) {
+ test_consume<long>();
+}
+
+TEST(Consume, ULongLong) {
+ test_consume<unsigned long long>();
+}
+
+TEST(Consume, LongLong) {
+ test_consume<long long>();
+}
+
+
+
+/*
+ * Local Variables:
+ * compile-command: "cd .. ; make unittest_strtol && ./unittest_strtol"
+ * End:
+ */
diff --git a/src/test/system/CMakeLists.txt b/src/test/system/CMakeLists.txt
new file mode 100644
index 000000000..f2b69fb3e
--- /dev/null
+++ b/src/test/system/CMakeLists.txt
@@ -0,0 +1,50 @@
+## System tests
+
+set(libsystest_srcs
+ cross_process_sem.cc
+ systest_runnable.cc
+ systest_settings.cc
+ st_rados_create_pool.cc
+ st_rados_delete_pool.cc
+ st_rados_list_objects.cc)
+add_library(systest STATIC ${libsystest_srcs})
+
+if(NOT WIN32)
+ set(RT_LIB rt)
+endif()
+
+# test_rados_list_parallel
+add_executable(ceph_test_rados_list_parallel
+ rados_list_parallel.cc
+ )
+target_link_libraries(ceph_test_rados_list_parallel librados systest global pthread
+ ${RT_LIB} ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+
+# test_rados_open_pools_parallel
+set(test_rados_open_pools_parallel_srcs
+ rados_open_pools_parallel.cc)
+add_executable(ceph_test_rados_open_pools_parallel
+ ${test_rados_open_pools_parallel_srcs}
+ )
+target_link_libraries(ceph_test_rados_open_pools_parallel
+ librados systest global
+ pthread ${RT_LIB} ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+
+# test_rados_delete_pools_parallel
+set(test_rados_delete_pools_parallel_srcs
+ rados_delete_pools_parallel.cc
+ st_rados_create_pool.cc
+ st_rados_delete_pool.cc
+ st_rados_list_objects.cc
+ )
+add_executable(ceph_test_rados_delete_pools_parallel
+ ${test_rados_delete_pools_parallel_srcs}
+ )
+target_link_libraries(ceph_test_rados_delete_pools_parallel librados systest global
+ pthread ${RT_LIB} ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+
+install(TARGETS
+ ceph_test_rados_delete_pools_parallel
+ ceph_test_rados_list_parallel
+ ceph_test_rados_open_pools_parallel
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/system/cross_process_sem.cc b/src/test/system/cross_process_sem.cc
new file mode 100644
index 000000000..d73259d25
--- /dev/null
+++ b/src/test/system/cross_process_sem.cc
@@ -0,0 +1,122 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+
+#include <errno.h>
+#include <semaphore.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
+
+#include "include/ceph_assert.h"
+
+/* We put our cross-process semaphore into a page of memory mapped with mmap. */
+struct cross_process_sem_data_t
+{
+ sem_t sem;
+};
+
+/* A factory function is a good choice here because we want to be able to
+ * return an error code. It does force heap allocation, but that is the
+ * easiest way to use synchronization primitives anyway. Most programmers don't
+ * care about destroying semaphores before the process finishes. It's pretty
+ * difficult to get it right and there is usually no benefit.
+ */
+int CrossProcessSem::
+create(int initial_val, CrossProcessSem** res)
+{
+ #ifndef _WIN32
+ struct cross_process_sem_data_t *data = static_cast < cross_process_sem_data_t*> (
+ mmap(NULL, sizeof(struct cross_process_sem_data_t),
+ PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0));
+ if (data == MAP_FAILED) {
+ int err = errno;
+ return err;
+ }
+ int ret = sem_init(&data->sem, 1, initial_val);
+ #else
+ // We can't use multiple processes on Windows for the time being.
+ struct cross_process_sem_data_t *data = (cross_process_sem_data_t*)malloc(
+ sizeof(cross_process_sem_data_t));
+ int ret = sem_init(&data->sem, 0, initial_val);
+ #endif /* _WIN32 */
+ if (ret) {
+ return ret;
+ }
+ *res = new CrossProcessSem(data);
+ return 0;
+}
+
+CrossProcessSem::
+~CrossProcessSem()
+{
+ #ifndef _WIN32
+ munmap(m_data, sizeof(struct cross_process_sem_data_t));
+ #else
+ free(m_data);
+ #endif
+ m_data = NULL;
+}
+
+void CrossProcessSem::
+wait()
+{
+ while(true) {
+ int ret = sem_wait(&m_data->sem);
+ if (ret == 0)
+ return;
+ int err = errno;
+ if (err == -EINTR)
+ continue;
+ ceph_abort();
+ }
+}
+
+void CrossProcessSem::
+post()
+{
+ int ret = sem_post(&m_data->sem);
+ if (ret == -1) {
+ ceph_abort();
+ }
+}
+
+int CrossProcessSem::
+reinit(int dval)
+{
+ if (dval < 0)
+ return -EINVAL;
+ int cval;
+ if (sem_getvalue(&m_data->sem, &cval) == -1)
+ return errno;
+ if (cval < dval) {
+ int diff = dval - cval;
+ for (int i = 0; i < diff; ++i)
+ sem_post(&m_data->sem);
+ }
+ else {
+ int diff = cval - dval;
+ for (int i = 0; i < diff; ++i)
+ sem_wait(&m_data->sem);
+ }
+ return 0;
+}
+
+CrossProcessSem::
+CrossProcessSem(struct cross_process_sem_data_t *data)
+ : m_data(data)
+{
+}
diff --git a/src/test/system/cross_process_sem.h b/src/test/system/cross_process_sem.h
new file mode 100644
index 000000000..0cbedf475
--- /dev/null
+++ b/src/test/system/cross_process_sem.h
@@ -0,0 +1,40 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+struct cross_process_sem_data_t;
+
+class CrossProcessSem
+{
+public:
+ static int create(int initial_val, CrossProcessSem** ret);
+ ~CrossProcessSem();
+
+ /* Initialize the semaphore. Must be called before any operations */
+ int init();
+
+ /* Semaphore wait */
+ void wait();
+
+ /* Semaphore post */
+ void post();
+
+ /* Reinitialize the semaphore to the desired value.
+ * NOT thread-safe if it is in use at the time!
+ */
+ int reinit(int dval);
+
+private:
+ explicit CrossProcessSem(struct cross_process_sem_data_t *data);
+ struct cross_process_sem_data_t *m_data;
+};
diff --git a/src/test/system/rados_delete_pools_parallel.cc b/src/test/system/rados_delete_pools_parallel.cc
new file mode 100644
index 000000000..dc42d649a
--- /dev/null
+++ b/src/test/system/rados_delete_pools_parallel.cc
@@ -0,0 +1,112 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/rados/librados.h"
+#include "st_rados_create_pool.h"
+#include "st_rados_delete_pool.h"
+#include "st_rados_list_objects.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sstream>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <vector>
+
+using std::ostringstream;
+using std::string;
+using std::vector;
+
+static int g_num_objects = 50;
+
+/*
+ * rados_delete_pools_parallel
+ *
+ * This tests creation and deletion races.
+ *
+ * EXPECT: * can delete a pool while another user is using it
+ * * operations on pools return error codes after the pools
+ * are deleted
+ *
+ * DO NOT EXPECT * hangs, crashes
+ */
+
+const char *get_id_str()
+{
+ return "main";
+}
+
+int main(int argc, const char **argv)
+{
+ const char *num_objects = getenv("NUM_OBJECTS");
+ const std::string pool = get_temp_pool_name(argv[0]);
+ if (num_objects) {
+ g_num_objects = atoi(num_objects);
+ if (g_num_objects == 0)
+ return 100;
+ }
+
+ CrossProcessSem *pool_setup_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &pool_setup_sem));
+ CrossProcessSem *delete_pool_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &delete_pool_sem));
+ CrossProcessSem *deleted_pool_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &deleted_pool_sem));
+
+ // first test: create a pool, then delete that pool
+ {
+ StRadosCreatePool r1(argc, argv, NULL, pool_setup_sem, NULL,
+ pool, 50, ".obj");
+ StRadosDeletePool r2(argc, argv, pool_setup_sem, deleted_pool_sem, pool);
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ std::string error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("test1: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ // second test: create a pool, the list objects in that pool while it's
+ // being deleted.
+ RETURN1_IF_NONZERO(pool_setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(delete_pool_sem->reinit(0));
+ {
+ StRadosCreatePool r1(argc, argv, deleted_pool_sem, pool_setup_sem, NULL,
+ pool, g_num_objects, ".obj");
+ StRadosDeletePool r2(argc, argv, delete_pool_sem, NULL, pool);
+ StRadosListObjects r3(argc, argv, pool, true, g_num_objects / 2,
+ pool_setup_sem, NULL, delete_pool_sem);
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ std::string error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("test2: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ printf("******* SUCCESS **********\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/system/rados_list_parallel.cc b/src/test/system/rados_list_parallel.cc
new file mode 100644
index 000000000..d6b3dfc05
--- /dev/null
+++ b/src/test/system/rados_list_parallel.cc
@@ -0,0 +1,348 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/rados/librados.h"
+#include "include/stringify.h"
+#include "st_rados_create_pool.h"
+#include "st_rados_list_objects.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+#include <map>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sstream>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <vector>
+#include <sys/types.h>
+#include <unistd.h>
+
+using std::ostringstream;
+using std::string;
+using std::vector;
+
+static int g_num_objects = 50;
+
+static CrossProcessSem *pool_setup_sem = NULL;
+static CrossProcessSem *modify_sem = NULL;
+
+class RadosDeleteObjectsR : public SysTestRunnable
+{
+public:
+ RadosDeleteObjectsR(int argc, const char **argv,
+ const std::string &pool_name)
+ : SysTestRunnable(argc, argv), m_pool_name(pool_name)
+ {
+ }
+
+ ~RadosDeleteObjectsR() override
+ {
+ }
+
+ int run(void) override
+ {
+ int ret_val = 0;
+ rados_t cl;
+ RETURN1_IF_NONZERO(rados_create(&cl, NULL));
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ RETURN1_IF_NONZERO(rados_conf_read_file(cl, NULL));
+ rados_conf_parse_env(cl, NULL);
+ std::string log_name = SysTestSettings::inst().get_log_name(get_id_str());
+ if (!log_name.empty())
+ rados_conf_set(cl, "log_file", log_name.c_str());
+ RETURN1_IF_NONZERO(rados_connect(cl));
+ pool_setup_sem->wait();
+ pool_setup_sem->post();
+
+ rados_ioctx_t io_ctx;
+ rados_pool_create(cl, m_pool_name.c_str());
+ RETURN1_IF_NONZERO(rados_ioctx_create(cl, m_pool_name.c_str(), &io_ctx));
+
+ std::map <int, std::string> to_delete;
+ for (int i = 0; i < g_num_objects; ++i) {
+ char oid[128];
+ snprintf(oid, sizeof(oid), "%d.obj", i);
+ to_delete[i] = oid;
+ }
+
+ int removed = 0;
+ while (true) {
+ if (to_delete.empty())
+ break;
+ int r = rand() % to_delete.size();
+ std::map <int, std::string>::iterator d = to_delete.begin();
+ for (int i = 0; i < r; ++i)
+ ++d;
+ if (d == to_delete.end()) {
+ ret_val = -EDOM;
+ goto out;
+ }
+ std::string oid(d->second);
+ to_delete.erase(d);
+ int ret = rados_remove(io_ctx, oid.c_str());
+ if (ret != 0) {
+ printf("%s: rados_remove(%s) failed with error %d\n",
+ get_id_str(), oid.c_str(), ret);
+ ret_val = ret;
+ goto out;
+ }
+ ++removed;
+ if ((removed % 25) == 0) {
+ printf("%s: removed %d objects...\n", get_id_str(), removed);
+ }
+ if (removed == g_num_objects / 2) {
+ printf("%s: removed half of the objects\n", get_id_str());
+ modify_sem->post();
+ }
+ }
+
+ printf("%s: removed %d objects\n", get_id_str(), removed);
+
+out:
+ rados_ioctx_destroy(io_ctx);
+ rados_shutdown(cl);
+
+ return ret_val;
+ }
+private:
+ std::string m_pool_name;
+};
+
+class RadosAddObjectsR : public SysTestRunnable
+{
+public:
+ RadosAddObjectsR(int argc, const char **argv,
+ const std::string &pool_name,
+ const std::string &suffix)
+ : SysTestRunnable(argc, argv),
+ m_pool_name(pool_name),
+ m_suffix(suffix)
+ {
+ }
+
+ ~RadosAddObjectsR() override
+ {
+ }
+
+ int run(void) override
+ {
+ int ret_val = 0;
+ rados_t cl;
+ RETURN1_IF_NONZERO(rados_create(&cl, NULL));
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ RETURN1_IF_NONZERO(rados_conf_read_file(cl, NULL));
+ rados_conf_parse_env(cl, NULL);
+ std::string log_name = SysTestSettings::inst().get_log_name(get_id_str());
+ if (!log_name.empty())
+ rados_conf_set(cl, "log_file", log_name.c_str());
+ RETURN1_IF_NONZERO(rados_connect(cl));
+ pool_setup_sem->wait();
+ pool_setup_sem->post();
+
+ rados_ioctx_t io_ctx;
+ rados_pool_create(cl, m_pool_name.c_str());
+ RETURN1_IF_NONZERO(rados_ioctx_create(cl, m_pool_name.c_str(), &io_ctx));
+
+ std::map <int, std::string> to_add;
+ for (int i = 0; i < g_num_objects; ++i) {
+ char oid[128];
+ snprintf(oid, sizeof(oid), "%d%s", i, m_suffix.c_str());
+ to_add[i] = oid;
+ }
+
+ int added = 0;
+ while (true) {
+ if (to_add.empty())
+ break;
+ int r = rand() % to_add.size();
+ std::map <int, std::string>::iterator d = to_add.begin();
+ for (int i = 0; i < r; ++i)
+ ++d;
+ if (d == to_add.end()) {
+ ret_val = -EDOM;
+ goto out;
+ }
+ std::string oid(d->second);
+ to_add.erase(d);
+
+ std::string buf(StRadosCreatePool::get_random_buf(256));
+ int ret = rados_write(io_ctx, oid.c_str(), buf.c_str(), buf.size(), 0);
+ if (ret != 0) {
+ printf("%s: rados_write(%s) failed with error %d\n",
+ get_id_str(), oid.c_str(), ret);
+ ret_val = ret;
+ goto out;
+ }
+ ++added;
+ if ((added % 25) == 0) {
+ printf("%s: added %d objects...\n", get_id_str(), added);
+ }
+ if (added == g_num_objects / 2) {
+ printf("%s: added half of the objects\n", get_id_str());
+ modify_sem->post();
+ }
+ }
+
+ printf("%s: added %d objects\n", get_id_str(), added);
+
+ out:
+ rados_ioctx_destroy(io_ctx);
+ rados_shutdown(cl);
+
+ return ret_val;
+ }
+private:
+ std::string m_pool_name;
+ std::string m_suffix;
+};
+
+const char *get_id_str()
+{
+ return "main";
+}
+
+int main(int argc, const char **argv)
+{
+ const char *num_objects = getenv("NUM_OBJECTS");
+ const std::string pool = get_temp_pool_name(argv[0]);
+ if (num_objects) {
+ g_num_objects = atoi(num_objects);
+ if (g_num_objects == 0)
+ return 100;
+ }
+
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &pool_setup_sem));
+ RETURN1_IF_NONZERO(CrossProcessSem::create(1, &modify_sem));
+
+ std::string error;
+
+ // Test 1... list objects
+ {
+ StRadosCreatePool r1(argc, argv, NULL, pool_setup_sem, NULL,
+ pool, g_num_objects, ".obj");
+ StRadosListObjects r2(argc, argv, pool, false, g_num_objects,
+ pool_setup_sem, modify_sem, NULL);
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Test 2... list objects while they're being deleted
+ RETURN1_IF_NONZERO(pool_setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(modify_sem->reinit(0));
+ {
+ StRadosCreatePool r1(argc, argv, NULL, pool_setup_sem, NULL,
+ pool, g_num_objects, ".obj");
+ StRadosListObjects r2(argc, argv, pool, false, g_num_objects / 2,
+ pool_setup_sem, modify_sem, NULL);
+ RadosDeleteObjectsR r3(argc, argv, pool);
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Test 3... list objects while others are being added
+ RETURN1_IF_NONZERO(pool_setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(modify_sem->reinit(0));
+ {
+ StRadosCreatePool r1(argc, argv, NULL, pool_setup_sem, NULL,
+ pool, g_num_objects, ".obj");
+ StRadosListObjects r2(argc, argv, pool, false, g_num_objects / 2,
+ pool_setup_sem, modify_sem, NULL);
+ RadosAddObjectsR r3(argc, argv, pool, ".obj2");
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Test 4... list objects while others are being added and deleted
+ RETURN1_IF_NONZERO(pool_setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(modify_sem->reinit(0));
+ {
+ StRadosCreatePool r1(argc, argv, NULL, pool_setup_sem, NULL,
+ pool, g_num_objects, ".obj");
+ StRadosListObjects r2(argc, argv, pool, false, g_num_objects / 2,
+ pool_setup_sem, modify_sem, NULL);
+ RadosAddObjectsR r3(argc, argv, pool, ".obj2");
+ RadosAddObjectsR r4(argc, argv, pool, ".obj3");
+ RadosDeleteObjectsR r5(argc, argv, pool);
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ vec.push_back(&r4);
+ vec.push_back(&r5);
+ error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Test 5... list objects while they are being modified
+ RETURN1_IF_NONZERO(pool_setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(modify_sem->reinit(0));
+ {
+ StRadosCreatePool r1(argc, argv, NULL, pool_setup_sem, NULL,
+ pool, g_num_objects, ".obj");
+ StRadosListObjects r2(argc, argv, pool, false, g_num_objects / 2,
+ pool_setup_sem, modify_sem, NULL);
+ // AddObjects with the same 'suffix' as used in StRadosCreatePool
+ RadosAddObjectsR r3(argc, argv, pool, ".obj");
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ rados_t cl;
+ rados_create(&cl, NULL);
+ rados_conf_parse_argv(cl, argc, argv);
+ rados_conf_parse_argv(cl, argc, argv);
+ rados_conf_read_file(cl, NULL);
+ rados_conf_parse_env(cl, NULL);
+ rados_connect(cl);
+ rados_pool_delete(cl, pool.c_str());
+
+ printf("******* SUCCESS **********\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/system/rados_open_pools_parallel.cc b/src/test/system/rados_open_pools_parallel.cc
new file mode 100644
index 000000000..6f99c4270
--- /dev/null
+++ b/src/test/system/rados_open_pools_parallel.cc
@@ -0,0 +1,143 @@
+
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/rados/librados.h"
+#include "st_rados_create_pool.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sstream>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <vector>
+
+using std::ostringstream;
+using std::string;
+using std::vector;
+
+/*
+ * rados_open_pools_parallel
+ *
+ * This tests creating a pool in one Runnable, and then opening an io context
+ * based on that pool in another.
+ *
+ * EXPECT: * can't create the same pool twice
+ * * one Runnable can use the pool after the other one creates it
+ *
+ * DO NOT EXPECT * hangs, crashes
+ */
+class StRadosOpenPool : public SysTestRunnable
+{
+public:
+ StRadosOpenPool(int argc, const char **argv,
+ CrossProcessSem *pool_setup_sem,
+ CrossProcessSem *open_pool_sem,
+ const std::string& pool_name)
+ : SysTestRunnable(argc, argv),
+ m_pool_setup_sem(pool_setup_sem),
+ m_open_pool_sem(open_pool_sem),
+ m_pool_name(pool_name)
+ {
+ }
+
+ ~StRadosOpenPool() override
+ {
+ }
+
+ int run() override
+ {
+ rados_t cl;
+ RETURN1_IF_NONZERO(rados_create(&cl, NULL));
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ std::string log_name = SysTestSettings::inst().get_log_name(get_id_str());
+ if (!log_name.empty())
+ rados_conf_set(cl, "log_file", log_name.c_str());
+ RETURN1_IF_NONZERO(rados_conf_read_file(cl, NULL));
+ rados_conf_parse_env(cl, NULL);
+ RETURN1_IF_NONZERO(rados_connect(cl));
+ if (m_pool_setup_sem)
+ m_pool_setup_sem->wait();
+
+ printf("%s: rados_pool_create.\n", get_id_str());
+ rados_pool_create(cl, m_pool_name.c_str());
+ rados_ioctx_t io_ctx;
+ printf("%s: rados_ioctx_create.\n", get_id_str());
+ RETURN1_IF_NONZERO(rados_ioctx_create(cl, m_pool_name.c_str(), &io_ctx));
+ if (m_open_pool_sem)
+ m_open_pool_sem->post();
+ rados_ioctx_destroy(io_ctx);
+ rados_pool_delete(cl, m_pool_name.c_str());
+ rados_shutdown(cl);
+ return 0;
+ }
+
+private:
+ CrossProcessSem *m_pool_setup_sem;
+ CrossProcessSem *m_open_pool_sem;
+ std::string m_pool_name;
+};
+
+const char *get_id_str()
+{
+ return "main";
+}
+
+int main(int argc, const char **argv)
+{
+ const std::string pool = get_temp_pool_name(argv[0]);
+ // first test: create a pool, shut down the client, access that
+ // pool in a different process.
+ CrossProcessSem *pool_setup_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &pool_setup_sem));
+ StRadosCreatePool r1(argc, argv, NULL, pool_setup_sem, NULL,
+ pool, 50, ".obj");
+ StRadosOpenPool r2(argc, argv, pool_setup_sem, NULL, pool);
+ vector < SysTestRunnable* > vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ std::string error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("test1: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+
+ // second test: create a pool, access that
+ // pool in a different process, THEN shut down the first client.
+ CrossProcessSem *pool_setup_sem2 = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &pool_setup_sem2));
+ CrossProcessSem *open_pool_sem2 = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &open_pool_sem2));
+ StRadosCreatePool r3(argc, argv, NULL, pool_setup_sem2, open_pool_sem2,
+ pool, 50, ".obj");
+ StRadosOpenPool r4(argc, argv, pool_setup_sem2, open_pool_sem2, pool);
+ vector < SysTestRunnable* > vec2;
+ vec2.push_back(&r3);
+ vec2.push_back(&r4);
+ error = SysTestRunnable::run_until_finished(vec2);
+ if (!error.empty()) {
+ printf("test2: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+
+ printf("******* SUCCESS **********\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/system/rados_watch_notify.cc b/src/test/system/rados_watch_notify.cc
new file mode 100644
index 000000000..e69e932ec
--- /dev/null
+++ b/src/test/system/rados_watch_notify.cc
@@ -0,0 +1,195 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/rados/librados.h"
+#include "st_rados_create_pool.h"
+#include "st_rados_delete_pool.h"
+#include "st_rados_delete_objs.h"
+#include "st_rados_watch.h"
+#include "st_rados_notify.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+#include "include/stringify.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sstream>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <vector>
+#include <sys/types.h>
+#include <unistd.h>
+
+using std::ostringstream;
+using std::string;
+using std::vector;
+
+/*
+ * rados_watch_notify
+ *
+ * This tests watch/notify with pool and object deletion.
+ *
+ * EXPECT: * notifies to a deleted object or pool are not received
+ * * notifies to existing objects are received
+ *
+ * DO NOT EXPECT * hangs, crashes
+ */
+
+const char *get_id_str()
+{
+ return "main";
+}
+
+int main(int argc, const char **argv)
+{
+ std::string pool = "foo." + stringify(getpid());
+ CrossProcessSem *setup_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &setup_sem));
+ CrossProcessSem *watch_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &watch_sem));
+ CrossProcessSem *notify_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &notify_sem));
+
+ // create a pool and an object, watch the object, notify.
+ {
+ StRadosCreatePool r1(argc, argv, NULL, setup_sem, NULL, pool, 1, ".obj");
+ StRadosWatch r2(argc, argv, setup_sem, watch_sem, notify_sem,
+ 1, 0, pool, "0.obj");
+ StRadosNotify r3(argc, argv, setup_sem, watch_sem, notify_sem,
+ 0, pool, "0.obj");
+ StRadosDeletePool r4(argc, argv, notify_sem, NULL, pool);
+ vector<SysTestRunnable*> vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ vec.push_back(&r4);
+ std::string error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("test1: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ RETURN1_IF_NONZERO(setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(watch_sem->reinit(0));
+ RETURN1_IF_NONZERO(notify_sem->reinit(0));
+
+ // create a pool and an object, watch a non-existent object,
+ // notify non-existent object.watch
+ pool += ".";
+ {
+ StRadosCreatePool r1(argc, argv, NULL, setup_sem, NULL, pool, 0, ".obj");
+ StRadosWatch r2(argc, argv, setup_sem, watch_sem, notify_sem,
+ 0, -ENOENT, pool, "0.obj");
+ StRadosNotify r3(argc, argv, setup_sem, watch_sem, notify_sem,
+ -ENOENT, pool, "0.obj");
+ StRadosDeletePool r4(argc, argv, notify_sem, NULL, pool);
+ vector<SysTestRunnable*> vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ vec.push_back(&r4);
+ std::string error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("test2: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ RETURN1_IF_NONZERO(setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(watch_sem->reinit(0));
+ RETURN1_IF_NONZERO(notify_sem->reinit(0));
+
+ CrossProcessSem *finished_notifies_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &finished_notifies_sem));
+ CrossProcessSem *deleted_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &deleted_sem));
+ CrossProcessSem *second_pool_sem = NULL;
+ RETURN1_IF_NONZERO(CrossProcessSem::create(0, &second_pool_sem));
+
+ // create a pool and an object, watch the object, notify,
+ // then delete the pool.
+ // Create a new pool and write to it to make the osd get the updated map,
+ // then try notifying on the deleted pool.
+ pool += ".";
+ {
+ StRadosCreatePool r1(argc, argv, NULL, setup_sem, NULL, pool, 1, ".obj");
+ StRadosWatch r2(argc, argv, setup_sem, watch_sem, finished_notifies_sem,
+ 1, 0, pool, "0.obj");
+ StRadosNotify r3(argc, argv, setup_sem, watch_sem, notify_sem,
+ 0, pool, "0.obj");
+ StRadosDeletePool r4(argc, argv, notify_sem, deleted_sem, pool);
+ StRadosCreatePool r5(argc, argv, deleted_sem, second_pool_sem, NULL,
+ "bar", 1, ".obj");
+ StRadosNotify r6(argc, argv, second_pool_sem, NULL, finished_notifies_sem,
+ 0, "bar", "0.obj");
+ StRadosDeletePool r7(argc, argv, finished_notifies_sem, NULL, "bar");
+ vector<SysTestRunnable*> vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ vec.push_back(&r4);
+ vec.push_back(&r5);
+ vec.push_back(&r6);
+ vec.push_back(&r7);
+ std::string error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("test3: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ RETURN1_IF_NONZERO(setup_sem->reinit(0));
+ RETURN1_IF_NONZERO(watch_sem->reinit(0));
+ RETURN1_IF_NONZERO(notify_sem->reinit(0));
+ RETURN1_IF_NONZERO(finished_notifies_sem->reinit(0));
+ RETURN1_IF_NONZERO(deleted_sem->reinit(0));
+
+ // create a pool and an object, watch the object, notify,
+ // then delete the object, notify
+ // this test is enabled for the resolution of bug #2339.
+ pool += ".";
+ {
+ StRadosCreatePool r1(argc, argv, NULL, setup_sem, NULL, pool, 1, ".obj");
+ StRadosWatch r2(argc, argv, setup_sem, watch_sem, finished_notifies_sem,
+ 1, 0, pool, "0.obj");
+ StRadosNotify r3(argc, argv, setup_sem, watch_sem, notify_sem,
+ 0, pool, "0.obj");
+ StRadosDeleteObjs r4(argc, argv, notify_sem, deleted_sem, 1, pool, ".obj");
+ StRadosNotify r5(argc, argv, setup_sem, deleted_sem, finished_notifies_sem,
+ -ENOENT, pool, "0.obj");
+ StRadosDeletePool r6(argc, argv, finished_notifies_sem, NULL, pool);
+
+ vector<SysTestRunnable*> vec;
+ vec.push_back(&r1);
+ vec.push_back(&r2);
+ vec.push_back(&r3);
+ vec.push_back(&r4);
+ vec.push_back(&r5);
+ vec.push_back(&r6);
+ std::string error = SysTestRunnable::run_until_finished(vec);
+ if (!error.empty()) {
+ printf("test4: got error: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ printf("******* SUCCESS **********\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/system/rerun.sh b/src/test/system/rerun.sh
new file mode 100755
index 000000000..5d7d4635b
--- /dev/null
+++ b/src/test/system/rerun.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+[ -z $ITERATIONS ] && ITERATIONS=10
+
+TMPDIR=`mktemp -d -t rerun_logs.XXXXXXXXXX` || exit 1
+
+rm -rf $TMPDIR/logs
+mkdir $TMPDIR/logs
+
+for i in `seq 1 $ITERATIONS`; do
+ echo "********************* iteration $i *********************"
+ LOG_FILE_BASE=$TMPDIR/logs $EXE "$@"
+ if [ $? -ne 0 ]; then
+ die "failed! logs are in $TMPDIR/logs"
+ fi
+done
+
+echo "********************* success *********************"
+rm -rf $TMPDIR
+exit 0
diff --git a/src/test/system/st_rados_create_pool.cc b/src/test/system/st_rados_create_pool.cc
new file mode 100644
index 000000000..a802d9da4
--- /dev/null
+++ b/src/test/system/st_rados_create_pool.cc
@@ -0,0 +1,132 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/ceph_assert.h"
+#include "include/rados/librados.h"
+#include "st_rados_create_pool.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sstream>
+#include <string>
+
+using std::ostringstream;
+
+std::string StRadosCreatePool::
+get_random_buf(int sz)
+{
+ ostringstream oss;
+ int size = rand() % sz; // yep, it's not very random
+ for (int i = 0; i < size; ++i) {
+ oss << ".";
+ }
+ return oss.str();
+}
+
+StRadosCreatePool::
+StRadosCreatePool(int argc, const char **argv,
+ CrossProcessSem *setup_sem,
+ CrossProcessSem *pool_setup_sem,
+ CrossProcessSem *close_create_pool,
+ const std::string &pool_name,
+ int num_objects,
+ const std::string &suffix)
+ : SysTestRunnable(argc, argv),
+ m_setup_sem(setup_sem),
+ m_pool_setup_sem(pool_setup_sem),
+ m_close_create_pool(close_create_pool),
+ m_pool_name(pool_name),
+ m_num_objects(num_objects),
+ m_suffix(suffix)
+{
+}
+
+StRadosCreatePool::
+~StRadosCreatePool()
+{
+}
+
+int StRadosCreatePool::
+run()
+{
+ int ret_val = 0;
+ rados_t cl;
+ RETURN1_IF_NONZERO(rados_create(&cl, NULL));
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ RETURN1_IF_NONZERO(rados_conf_read_file(cl, NULL));
+ std::string log_name = SysTestSettings::inst().get_log_name(get_id_str());
+ if (!log_name.empty())
+ rados_conf_set(cl, "log_file", log_name.c_str());
+ rados_conf_parse_env(cl, NULL);
+
+ if (m_setup_sem) {
+ m_setup_sem->wait();
+ m_setup_sem->post();
+ }
+
+ RETURN1_IF_NONZERO(rados_connect(cl));
+
+ printf("%s: creating pool %s\n", get_id_str(), m_pool_name.c_str());
+ rados_pool_create(cl, m_pool_name.c_str());
+ rados_ioctx_t io_ctx;
+ RETURN1_IF_NONZERO(rados_ioctx_create(cl, m_pool_name.c_str(), &io_ctx));
+
+ for (int i = 0; i < m_num_objects; ++i) {
+ char oid[128];
+ snprintf(oid, sizeof(oid), "%d%s", i, m_suffix.c_str());
+ std::string buf(get_random_buf(256));
+ int ret = rados_write(io_ctx, oid, buf.c_str(), buf.size(), 0);
+ if (ret != 0) {
+ printf("%s: rados_write(%s) failed with error: %d\n",
+ get_id_str(), oid, ret);
+ ret_val = ret;
+ goto out;
+ }
+ if (((i % 25) == 0) || (i == m_num_objects - 1)) {
+ printf("%s: created object %d...\n", get_id_str(), i);
+ }
+ }
+
+out:
+ printf("%s: finishing.\n", get_id_str());
+ if (m_pool_setup_sem)
+ m_pool_setup_sem->post();
+ if (m_close_create_pool)
+ m_close_create_pool->wait();
+ rados_ioctx_destroy(io_ctx);
+ rados_shutdown(cl);
+ return ret_val;
+}
+
+std::string get_temp_pool_name(const char* prefix)
+{
+ ceph_assert(prefix);
+ char hostname[80];
+ int ret = 0;
+ ret = gethostname(hostname, sizeof(hostname));
+ ceph_assert(!ret);
+ char poolname[256];
+ ret = snprintf(poolname, sizeof(poolname),
+ "%s.%s-%d", prefix, hostname, getpid());
+ ceph_assert(ret > 0);
+ ceph_assert((unsigned int)ret < sizeof(poolname));
+ return poolname;
+}
diff --git a/src/test/system/st_rados_create_pool.h b/src/test/system/st_rados_create_pool.h
new file mode 100644
index 000000000..ee65b22c6
--- /dev/null
+++ b/src/test/system/st_rados_create_pool.h
@@ -0,0 +1,53 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#ifndef TEST_SYSTEM_ST_RADOS_CREATE_POOL_H
+#define TEST_SYSTEM_ST_RADOS_CREATE_POOL_H
+
+#include "systest_runnable.h"
+
+class CrossProcessSem;
+
+/*
+ * st_rados_create_pool
+ *
+ * Waits, then posts to setup_sem.
+ * Creates a pool and populates it with some objects.
+ * Then, calls pool_setup_sem->post()
+ */
+class StRadosCreatePool : public SysTestRunnable
+{
+public:
+ static std::string get_random_buf(int sz);
+ StRadosCreatePool(int argc, const char **argv,
+ CrossProcessSem *setup_sem,
+ CrossProcessSem *pool_setup_sem,
+ CrossProcessSem *close_create_pool_sem,
+ const std::string &pool_name,
+ int num_objects,
+ const std::string &suffix);
+ ~StRadosCreatePool() override;
+ int run() override;
+private:
+ CrossProcessSem *m_setup_sem;
+ CrossProcessSem *m_pool_setup_sem;
+ CrossProcessSem *m_close_create_pool;
+ std::string m_pool_name;
+ int m_num_objects;
+ std::string m_suffix;
+};
+
+std::string get_temp_pool_name(const char* prefix);
+
+#endif
diff --git a/src/test/system/st_rados_delete_objs.cc b/src/test/system/st_rados_delete_objs.cc
new file mode 100644
index 000000000..b43ffdda6
--- /dev/null
+++ b/src/test/system/st_rados_delete_objs.cc
@@ -0,0 +1,71 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/rados/librados.h"
+#include "st_rados_delete_objs.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+
+StRadosDeleteObjs::StRadosDeleteObjs(int argc, const char **argv,
+ CrossProcessSem *setup_sem,
+ CrossProcessSem *deleted_sem,
+ int num_objs,
+ const std::string &pool_name,
+ const std::string &suffix)
+ : SysTestRunnable(argc, argv),
+ m_setup_sem(setup_sem),
+ m_deleted_sem(deleted_sem),
+ m_num_objs(num_objs),
+ m_pool_name(pool_name),
+ m_suffix(suffix)
+{
+}
+
+StRadosDeleteObjs::~StRadosDeleteObjs()
+{
+}
+
+int StRadosDeleteObjs::run()
+{
+ rados_t cl;
+ RETURN1_IF_NONZERO(rados_create(&cl, NULL));
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ RETURN1_IF_NONZERO(rados_conf_read_file(cl, NULL));
+ rados_conf_parse_env(cl, NULL);
+ RETURN1_IF_NONZERO(rados_connect(cl));
+ m_setup_sem->wait();
+ m_setup_sem->post();
+
+ rados_ioctx_t io_ctx;
+ rados_pool_create(cl, m_pool_name.c_str());
+ RETURN1_IF_NONZERO(rados_ioctx_create(cl, m_pool_name.c_str(), &io_ctx));
+
+ for (int i = 0; i < m_num_objs; ++i) {
+ char oid[128];
+ snprintf(oid, sizeof(oid), "%d%s", i, m_suffix.c_str());
+ RETURN1_IF_NONZERO(rados_remove(io_ctx, oid));
+ if (((i % 25) == 0) || (i == m_num_objs - 1)) {
+ printf("%s: deleted object %d...\n", get_id_str(), i);
+ }
+ }
+
+ rados_ioctx_destroy(io_ctx);
+ if (m_deleted_sem)
+ m_deleted_sem->post();
+ rados_shutdown(cl);
+ return 0;
+}
diff --git a/src/test/system/st_rados_delete_objs.h b/src/test/system/st_rados_delete_objs.h
new file mode 100644
index 000000000..40771ee45
--- /dev/null
+++ b/src/test/system/st_rados_delete_objs.h
@@ -0,0 +1,48 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#ifndef TEST_SYSTEM_ST_RADOS_DELETE_OBJS_H
+#define TEST_SYSTEM_ST_RADOS_DELETE_OBJS_H
+
+#include "systest_runnable.h"
+
+class CrossProcessSem;
+
+/*
+ * st_rados_delete_objs
+ *
+ * Waits on setup_sem, posts to it,
+ * deletes num_objs objects from the pool,
+ * and posts to deleted_sem.
+ */
+class StRadosDeleteObjs : public SysTestRunnable
+{
+public:
+ StRadosDeleteObjs(int argc, const char **argv,
+ CrossProcessSem *setup_sem,
+ CrossProcessSem *deleted_sem,
+ int num_objs,
+ const std::string &pool_name,
+ const std::string &suffix);
+ ~StRadosDeleteObjs() override;
+ int run() override;
+private:
+ CrossProcessSem *m_setup_sem;
+ CrossProcessSem *m_deleted_sem;
+ int m_num_objs;
+ std::string m_pool_name;
+ std::string m_suffix;
+};
+
+#endif
diff --git a/src/test/system/st_rados_delete_pool.cc b/src/test/system/st_rados_delete_pool.cc
new file mode 100644
index 000000000..de553e98e
--- /dev/null
+++ b/src/test/system/st_rados_delete_pool.cc
@@ -0,0 +1,59 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/rados/librados.h"
+#include "st_rados_delete_pool.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+
+StRadosDeletePool::StRadosDeletePool(int argc, const char **argv,
+ CrossProcessSem *pool_setup_sem,
+ CrossProcessSem *delete_pool_sem,
+ const std::string &pool_name)
+ : SysTestRunnable(argc, argv),
+ m_pool_setup_sem(pool_setup_sem),
+ m_delete_pool_sem(delete_pool_sem),
+ m_pool_name(pool_name)
+{
+}
+
+StRadosDeletePool::~StRadosDeletePool()
+{
+}
+
+int StRadosDeletePool::run()
+{
+ rados_t cl;
+ RETURN1_IF_NONZERO(rados_create(&cl, NULL));
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ RETURN1_IF_NONZERO(rados_conf_read_file(cl, NULL));
+ rados_conf_parse_env(cl, NULL);
+ RETURN1_IF_NONZERO(rados_connect(cl));
+ m_pool_setup_sem->wait();
+ m_pool_setup_sem->post();
+
+ rados_ioctx_t io_ctx;
+ rados_pool_create(cl, m_pool_name.c_str());
+ RETURN1_IF_NONZERO(rados_ioctx_create(cl, m_pool_name.c_str(), &io_ctx));
+ rados_ioctx_destroy(io_ctx);
+ printf("%s: deleting pool %s\n", get_id_str(), m_pool_name.c_str());
+ RETURN1_IF_NONZERO(rados_pool_delete(cl, m_pool_name.c_str()));
+ if (m_delete_pool_sem)
+ m_delete_pool_sem->post();
+ rados_shutdown(cl);
+ return 0;
+}
diff --git a/src/test/system/st_rados_delete_pool.h b/src/test/system/st_rados_delete_pool.h
new file mode 100644
index 000000000..cc48af9b9
--- /dev/null
+++ b/src/test/system/st_rados_delete_pool.h
@@ -0,0 +1,43 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#ifndef TEST_SYSTEM_ST_RADOS_DELETE_POOL_H
+#define TEST_SYSTEM_ST_RADOS_DELETE_POOL_H
+
+#include "systest_runnable.h"
+
+class CrossProcessSem;
+
+/*
+ * st_rados_delete_pool
+ *
+ * Waits on pool_setup_sem, posts to it,
+ * deletes a pool, and posts to delete_pool_sem.
+ */
+class StRadosDeletePool : public SysTestRunnable
+{
+public:
+ StRadosDeletePool(int argc, const char **argv,
+ CrossProcessSem *pool_setup_sem,
+ CrossProcessSem *delete_pool_sem,
+ const std::string &pool_name);
+ ~StRadosDeletePool() override;
+ int run() override;
+private:
+ CrossProcessSem *m_pool_setup_sem;
+ CrossProcessSem *m_delete_pool_sem;
+ std::string m_pool_name;
+};
+
+#endif
diff --git a/src/test/system/st_rados_list_objects.cc b/src/test/system/st_rados_list_objects.cc
new file mode 100644
index 000000000..514dafe65
--- /dev/null
+++ b/src/test/system/st_rados_list_objects.cc
@@ -0,0 +1,107 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#include "cross_process_sem.h"
+#include "include/rados/librados.h"
+#include "st_rados_list_objects.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sstream>
+#include <string>
+
+using std::ostringstream;
+
+StRadosListObjects::
+StRadosListObjects(int argc, const char **argv,
+ const std::string &pool_name,
+ bool accept_list_errors,
+ int midway_cnt,
+ CrossProcessSem *pool_setup_sem,
+ CrossProcessSem *midway_sem_wait,
+ CrossProcessSem *midway_sem_post)
+ : SysTestRunnable(argc, argv),
+ m_pool_name(pool_name),
+ m_accept_list_errors(accept_list_errors),
+ m_midway_cnt(midway_cnt),
+ m_pool_setup_sem(pool_setup_sem),
+ m_midway_sem_wait(midway_sem_wait),
+ m_midway_sem_post(midway_sem_post)
+{
+}
+
+StRadosListObjects::
+~StRadosListObjects()
+{
+}
+
+int StRadosListObjects::
+run()
+{
+ int retval = 0;
+ rados_t cl;
+ RETURN1_IF_NONZERO(rados_create(&cl, NULL));
+ rados_conf_parse_argv(cl, m_argc, m_argv);
+ RETURN1_IF_NONZERO(rados_conf_read_file(cl, NULL));
+ rados_conf_parse_env(cl, NULL);
+ RETURN1_IF_NONZERO(rados_connect(cl));
+ m_pool_setup_sem->wait();
+ m_pool_setup_sem->post();
+
+ rados_ioctx_t io_ctx;
+ rados_pool_create(cl, m_pool_name.c_str());
+ RETURN1_IF_NONZERO(rados_ioctx_create(cl, m_pool_name.c_str(), &io_ctx));
+
+ int saw = 0;
+ const char *obj_name;
+ rados_list_ctx_t h;
+ printf("%s: listing objects.\n", get_id_str());
+ RETURN1_IF_NONZERO(rados_nobjects_list_open(io_ctx, &h));
+ while (true) {
+ int ret = rados_nobjects_list_next(h, &obj_name, NULL, NULL);
+ if (ret == -ENOENT) {
+ break;
+ }
+ else if (ret != 0) {
+ if (m_accept_list_errors && (!m_midway_sem_post || saw > m_midway_cnt))
+ break;
+ printf("%s: rados_objects_list_next error: %d\n", get_id_str(), ret);
+ retval = ret;
+ goto out;
+ }
+ if ((saw % 25) == 0) {
+ printf("%s: listed object %d...\n", get_id_str(), saw);
+ }
+ ++saw;
+ if (saw == m_midway_cnt) {
+ if (m_midway_sem_wait)
+ m_midway_sem_wait->wait();
+ if (m_midway_sem_post)
+ m_midway_sem_post->post();
+ }
+ }
+
+ printf("%s: saw %d objects\n", get_id_str(), saw);
+
+out:
+ rados_nobjects_list_close(h);
+ rados_ioctx_destroy(io_ctx);
+ rados_shutdown(cl);
+
+ return retval;
+}
diff --git a/src/test/system/st_rados_list_objects.h b/src/test/system/st_rados_list_objects.h
new file mode 100644
index 000000000..b26d51844
--- /dev/null
+++ b/src/test/system/st_rados_list_objects.h
@@ -0,0 +1,53 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#ifndef TEST_SYSTEM_ST_RADOS_LIST_OBJECTS_H
+#define TEST_SYSTEM_ST_RADOS_LIST_OBJECTS_H
+
+#include "systest_runnable.h"
+
+class CrossProcessSem;
+
+/*
+ * st_rados_list_objects
+ *
+ * 1. calls pool_setup_sem->wait()
+ * 2. calls pool_setup_sem->post()
+ * 3. list some objects
+ * 4. modify_sem->wait()
+ * 5. list some objects
+ */
+class StRadosListObjects : public SysTestRunnable
+{
+public:
+ static std::string get_random_buf(int sz);
+ StRadosListObjects(int argc, const char **argv,
+ const std::string &pool_name,
+ bool accept_list_errors,
+ int midway_cnt,
+ CrossProcessSem *pool_setup_sem,
+ CrossProcessSem *midway_sem_wait,
+ CrossProcessSem *midway_sem_post);
+ ~StRadosListObjects() override;
+ int run() override;
+private:
+ std::string m_pool_name;
+ bool m_accept_list_errors;
+ int m_midway_cnt;
+ CrossProcessSem *m_pool_setup_sem;
+ CrossProcessSem *m_midway_sem_wait;
+ CrossProcessSem *m_midway_sem_post;
+};
+
+#endif
diff --git a/src/test/system/st_rados_notify.h b/src/test/system/st_rados_notify.h
new file mode 100644
index 000000000..1d61b4d12
--- /dev/null
+++ b/src/test/system/st_rados_notify.h
@@ -0,0 +1,52 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#ifndef TEST_SYSTEM_ST_RADOS_NOTIFY_H
+#define TEST_SYSTEM_ST_RADOS_NOTIFY_H
+
+#include "systest_runnable.h"
+
+class CrossProcessSem;
+
+/*
+ * st_rados_notify
+ *
+ * 1. waits on and then posts to setup_sem
+ * 2. connects and opens the pool
+ * 3. waits on and then posts to notify_sem
+ * 4. notifies on the object
+ * 5. posts to notified_sem
+ */
+class StRadosNotify : public SysTestRunnable
+{
+public:
+ StRadosNotify(int argc, const char **argv,
+ CrossProcessSem *setup_sem,
+ CrossProcessSem *notify_sem,
+ CrossProcessSem *notified_sem,
+ int notify_retcode,
+ const std::string &pool_name,
+ const std::string &obj_name);
+ ~StRadosNotify() override;
+ int run() override;
+private:
+ CrossProcessSem *m_setup_sem;
+ CrossProcessSem *m_notify_sem;
+ CrossProcessSem *m_notified_sem;
+ int m_notify_retcode;
+ std::string m_pool_name;
+ std::string m_obj_name;
+};
+
+#endif
diff --git a/src/test/system/st_rados_watch.h b/src/test/system/st_rados_watch.h
new file mode 100644
index 000000000..366be3f87
--- /dev/null
+++ b/src/test/system/st_rados_watch.h
@@ -0,0 +1,56 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2011 New Dream Network
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*
+*/
+
+#ifndef TEST_SYSTEM_ST_RADOS_WATCH_H
+#define TEST_SYSTEM_ST_RADOS_WATCH_H
+
+#include "systest_runnable.h"
+
+class CrossProcessSem;
+
+/*
+ * st_rados_watch
+ *
+ * 1. waits on setup_sem
+ * 2. posts to setup_sem
+ * 3. watches an object
+ * 4. posts to watch_sem
+ * 5. waits on notify_sem
+ * 6. posts to notify_sem
+ * 7. checks that the correct number of notifies were received
+ */
+class StRadosWatch : public SysTestRunnable
+{
+public:
+ StRadosWatch(int argc, const char **argv,
+ CrossProcessSem *setup_sem,
+ CrossProcessSem *watch_sem,
+ CrossProcessSem *notify_sem,
+ int num_notifies,
+ int watch_retcode,
+ const std::string &pool_name,
+ const std::string &obj_name);
+ ~StRadosWatch() override;
+ int run() override;
+private:
+ CrossProcessSem *m_setup_sem;
+ CrossProcessSem *m_watch_sem;
+ CrossProcessSem *m_notify_sem;
+ int m_num_notifies;
+ int m_watch_retcode;
+ std::string m_pool_name;
+ std::string m_obj_name;
+};
+
+#endif
diff --git a/src/test/system/systest_runnable.cc b/src/test/system/systest_runnable.cc
new file mode 100644
index 000000000..40f94c403
--- /dev/null
+++ b/src/test/system/systest_runnable.cc
@@ -0,0 +1,233 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/compat.h"
+#include "common/errno.h"
+#include "systest_runnable.h"
+#include "systest_settings.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <sstream>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#ifndef _WIN32
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+#include <atomic>
+#include <limits>
+#include <vector>
+
+using std::ostringstream;
+using std::string;
+
+static pid_t do_gettid(void)
+{
+#if defined(__linux__)
+ return static_cast < pid_t >(syscall(SYS_gettid));
+#elif defined(_WIN32)
+ return static_cast < pid_t >(GetCurrentThreadId());
+#else
+ return static_cast < pid_t >(pthread_getthreadid_np());
+#endif
+}
+
+std::atomic<unsigned> m_highest_id = { 0 };
+
+SysTestRunnable::
+SysTestRunnable(int argc, const char **argv)
+ : m_argc(0),
+ m_argv(NULL),
+ m_argv_orig(NULL)
+{
+ m_started = false;
+ m_id = ++m_highest_id;
+ memset(&m_pthread, 0, sizeof(m_pthread));
+ update_id_str(false);
+ set_argv(argc, argv);
+}
+
+SysTestRunnable::
+~SysTestRunnable()
+{
+ set_argv(0, NULL);
+}
+
+const char* SysTestRunnable::
+get_id_str(void) const
+{
+ return m_id_str;
+}
+
+int SysTestRunnable::
+start()
+{
+ if (m_started) {
+ return -EDOM;
+ }
+ int ret;
+ bool use_threads = SysTestSettings::inst().use_threads();
+ if (use_threads) {
+ ret = pthread_create(&m_pthread, NULL, systest_runnable_pthread_helper,
+ static_cast<void*>(this));
+ if (ret)
+ return ret;
+ m_started = true;
+ } else {
+ #ifdef _WIN32
+ printf("Using separate processes is not supported on Windows.\n");
+ return -1;
+ #else
+ std::string err_msg;
+ ret = preforker.prefork(err_msg);
+ if (ret < 0) {
+ printf("prefork failed: %s\n", err_msg.c_str());
+ return ret;
+ }
+
+ if (preforker.is_child()) {
+ m_started = true;
+ void *retptr = systest_runnable_pthread_helper(static_cast<void*>(this));
+ preforker.exit((int)(uintptr_t)retptr);
+ } else {
+ m_started = true;
+ }
+ #endif
+ }
+ return 0;
+}
+
+std::string SysTestRunnable::
+join()
+{
+ if (!m_started) {
+ return "SysTestRunnable was never started.";
+ }
+ int ret;
+ bool use_threads = SysTestSettings::inst().use_threads();
+ if (use_threads) {
+ void *ptrretval;
+ ret = pthread_join(m_pthread, &ptrretval);
+ if (ret) {
+ ostringstream oss;
+ oss << "pthread_join failed with error " << ret;
+ return oss.str();
+ }
+ int retval = (int)(uintptr_t)ptrretval;
+ if (retval != 0) {
+ ostringstream oss;
+ oss << "ERROR " << retval;
+ return oss.str();
+ }
+ return "";
+ } else {
+ #ifdef _WIN32
+ return "Using separate processes is not supported on Windows.\n";
+ #else
+ std::string err_msg;
+ ret = preforker.parent_wait(err_msg);
+ return err_msg;
+ #endif
+ }
+}
+
+std::string SysTestRunnable::
+run_until_finished(std::vector < SysTestRunnable * > &runnables)
+{
+ int index = 0;
+ for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin();
+ r != runnables.end(); ++r) {
+ int ret = (*r)->start();
+ if (ret) {
+ ostringstream oss;
+ oss << "run_until_finished: got error " << ret
+ << " when starting runnable " << index;
+ return oss.str();
+ }
+ ++index;
+ }
+
+ for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin();
+ r != runnables.end(); ++r) {
+ std::string rstr = (*r)->join();
+ if (!rstr.empty()) {
+ ostringstream oss;
+ oss << "run_until_finished: runnable " << (*r)->get_id_str()
+ << ": got error: " << rstr;
+ return oss.str();
+ }
+ }
+ printf("*******************************\n");
+ return "";
+}
+
+void *systest_runnable_pthread_helper(void *arg)
+{
+ SysTestRunnable *st = static_cast < SysTestRunnable * >(arg);
+ st->update_id_str(true);
+ printf("%s: starting.\n", st->get_id_str());
+ int ret = st->run();
+ printf("%s: shutting down.\n", st->get_id_str());
+ return (void*)(uintptr_t)ret;
+}
+
+void SysTestRunnable::
+update_id_str(bool started)
+{
+ bool use_threads = SysTestSettings::inst().use_threads();
+ char extra[std::numeric_limits<int>::digits10 + 1];
+ extra[0] = '\0';
+
+ if (started) {
+ if (use_threads)
+ snprintf(extra, sizeof(extra), "_[%d]", do_gettid());
+ else
+ snprintf(extra, sizeof(extra), "_[%d]", getpid());
+ }
+ if (use_threads)
+ snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "thread_%d%s", m_id, extra);
+ else
+ snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "process_%d%s", m_id, extra);
+}
+
+// Copy argv so that if some fiend decides to modify it, it's ok.
+void SysTestRunnable::
+set_argv(int argc, const char **argv)
+{
+ if (m_argv_orig != NULL) {
+ for (int i = 0; i < m_argc; ++i)
+ free((void*)(m_argv_orig[i]));
+ delete[] m_argv_orig;
+ m_argv_orig = NULL;
+ delete[] m_argv;
+ m_argv = NULL;
+ m_argc = 0;
+ }
+ if (argv == NULL)
+ return;
+ m_argc = argc;
+ m_argv_orig = new const char*[m_argc+1];
+ for (int i = 0; i < m_argc; ++i)
+ m_argv_orig[i] = strdup(argv[i]);
+ m_argv_orig[argc] = NULL;
+ m_argv = new const char*[m_argc+1];
+ for (int i = 0; i <= m_argc; ++i)
+ m_argv[i] = m_argv_orig[i];
+}
diff --git a/src/test/system/systest_runnable.h b/src/test/system/systest_runnable.h
new file mode 100644
index 000000000..bfa9130ed
--- /dev/null
+++ b/src/test/system/systest_runnable.h
@@ -0,0 +1,94 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_SYSTEM_TEST_H
+#define CEPH_SYSTEM_TEST_H
+
+#include <pthread.h>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+#ifndef _WIN32
+#include "common/Preforker.h"
+#endif
+
+#define RETURN1_IF_NOT_VAL(expected, expr) \
+ do {\
+ int _rinv_ret = expr;\
+ if (_rinv_ret != expected) {\
+ printf("%s: file %s, line %d: expected %d, got %d\n",\
+ get_id_str(), __FILE__, __LINE__, expected, _rinv_ret);\
+ return 1; \
+ }\
+ } while(0);
+
+#define RETURN1_IF_NONZERO(expr) \
+ RETURN1_IF_NOT_VAL(0, expr)
+
+extern void* systest_runnable_pthread_helper(void *arg);
+std::string get_temp_pool_name(const char* prefix);
+/* Represents a single test thread / process.
+ *
+ * Inherit from this class and implement the test body in run().
+*/
+class SysTestRunnable
+{
+public:
+ static const int ID_STR_SZ = 196;
+
+ SysTestRunnable(int argc, const char **argv);
+ virtual ~SysTestRunnable();
+
+ /* Returns 0 on success; error code otherwise. */
+ virtual int run() = 0;
+
+ /* Return a string identifying the runnable. */
+ const char* get_id_str(void) const;
+
+ /* Start the Runnable */
+ int start();
+
+ /* Wait until the Runnable is finished. Returns an error string on failure. */
+ std::string join();
+
+ /* Starts a bunch of SystemTestRunnables and waits until they're done.
+ *
+ * Returns an error string on failure. */
+ static std::string run_until_finished(std::vector < SysTestRunnable * >&
+ runnables);
+
+protected:
+ int m_argc;
+ const char **m_argv;
+
+private:
+ explicit SysTestRunnable(const SysTestRunnable &rhs);
+ SysTestRunnable& operator=(const SysTestRunnable &rhs);
+ void update_id_str(bool started);
+ void set_argv(int argc, const char **argv);
+
+ friend void* systest_runnable_pthread_helper(void *arg);
+
+ #ifndef _WIN32
+ Preforker preforker;
+ #endif
+ const char **m_argv_orig;
+ bool m_started;
+ int m_id;
+ pthread_t m_pthread;
+ char m_id_str[ID_STR_SZ];
+};
+
+#endif
diff --git a/src/test/system/systest_settings.cc b/src/test/system/systest_settings.cc
new file mode 100644
index 000000000..787d763fe
--- /dev/null
+++ b/src/test/system/systest_settings.cc
@@ -0,0 +1,71 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "systest_settings.h"
+
+#include <pthread.h>
+#include <sstream>
+#include <stdlib.h>
+
+pthread_mutex_t g_system_test_settings_lock = PTHREAD_MUTEX_INITIALIZER;
+
+SysTestSettings& SysTestSettings::
+inst()
+{
+ pthread_mutex_lock(&g_system_test_settings_lock);
+ if (!m_inst)
+ m_inst = new SysTestSettings();
+ pthread_mutex_unlock(&g_system_test_settings_lock);
+ return *m_inst;
+}
+
+bool SysTestSettings::
+use_threads() const
+{
+ #ifdef _WIN32
+ // We can't use multiple processes on Windows for the time being.
+ // We'd need a mechanism for spawning those procecesses and also handle
+ // the inter-process communication.
+ return true;
+ #else
+ return m_use_threads;
+ #endif
+}
+
+std::string SysTestSettings::
+get_log_name(const std::string &suffix) const
+{
+ if (m_log_file_base.empty())
+ return "";
+ std::ostringstream oss;
+ oss << m_log_file_base << "." << suffix;
+ return oss.str();
+}
+
+SysTestSettings* SysTestSettings::
+m_inst = NULL;
+
+SysTestSettings::
+SysTestSettings()
+{
+ m_use_threads = !!getenv("USE_THREADS");
+ const char *lfb = getenv("LOG_FILE_BASE");
+ if (lfb)
+ m_log_file_base.assign(lfb);
+}
+
+SysTestSettings::
+~SysTestSettings()
+{
+}
diff --git a/src/test/system/systest_settings.h b/src/test/system/systest_settings.h
new file mode 100644
index 000000000..4c32143f7
--- /dev/null
+++ b/src/test/system/systest_settings.h
@@ -0,0 +1,36 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_SYSTEM_TEST_SETTINGS_H
+#define CEPH_SYSTEM_TEST_SETTINGS_H
+
+#include <string>
+
+/* Singleton with settings grabbed from environment variables */
+class SysTestSettings
+{
+public:
+ static SysTestSettings& inst();
+ bool use_threads() const;
+ std::string get_log_name(const std::string &suffix) const;
+private:
+ static SysTestSettings* m_inst;
+ SysTestSettings();
+ ~SysTestSettings();
+
+ bool m_use_threads;
+ std::string m_log_file_base;
+};
+
+#endif
diff --git a/src/test/test_addrs.cc b/src/test/test_addrs.cc
new file mode 100644
index 000000000..4062d0431
--- /dev/null
+++ b/src/test/test_addrs.cc
@@ -0,0 +1,334 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "include/types.h"
+#include "include/stringify.h"
+#include "msg/msg_types.h"
+#include "gtest/gtest.h"
+
+#include <sstream>
+
+using namespace std;
+
+// input, parsed+printed addr output, leftover
+// if the parse fails, output + leftover should both be blank.
+const char *addr_checks[][3] = {
+ { "127.0.0.1", "v2:127.0.0.1:0/0", "" },
+ { "127.0.0.1 foo", "v2:127.0.0.1:0/0", " foo" },
+ { "127.0.0.1:1234 foo", "v2:127.0.0.1:1234/0", " foo" },
+ { "127.0.0.1:1234/5678 foo", "v2:127.0.0.1:1234/5678", " foo" },
+ { "1.2.3:4 a", "", "1.2.3:4 a" },
+ { "2607:f298:4:2243::5522", "v2:[2607:f298:4:2243::5522]:0/0", "" },
+ { "[2607:f298:4:2243::5522]", "v2:[2607:f298:4:2243::5522]:0/0", "" },
+ { "2607:f298:4:2243::5522a", "", "2607:f298:4:2243::5522a" },
+ { "[2607:f298:4:2243::5522]a", "v2:[2607:f298:4:2243::5522]:0/0", "a" },
+ { "[2607:f298:4:2243::5522]:1234a", "v2:[2607:f298:4:2243::5522]:1234/0", "a" },
+ { "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "v2:[2001:db8:85a3::8a2e:370:7334]:0/0", "" },
+ { "2001:2db8:85a3:4334:4324:8a2e:1370:7334", "v2:[2001:2db8:85a3:4334:4324:8a2e:1370:7334]:0/0", "" },
+ { "::", "v2:[::]:0/0", "" },
+ { "::zz", "v2:[::]:0/0", "zz" },
+ { ":: 12:34", "v2:[::]:0/0", " 12:34" },
+ { "-", "-", "" },
+ { "-asdf", "-", "asdf" },
+ { "v1:1.2.3.4", "v1:1.2.3.4:0/0", "" },
+ { "v1:1.2.3.4:12", "v1:1.2.3.4:12/0", "" },
+ { "v1:1.2.3.4:12/34", "v1:1.2.3.4:12/34", "" },
+ { "v2:1.2.3.4", "v2:1.2.3.4:0/0", "" },
+ { "v2:1.2.3.4:12", "v2:1.2.3.4:12/0", "" },
+ { "v2:1.2.3.4:12/34", "v2:1.2.3.4:12/34", "" },
+ { NULL, NULL, NULL },
+};
+
+const char *addr_only_checks[][3] = {
+ // we shouldn't parse an addrvec...
+ { "[v2:1.2.3.4:111/0,v1:5.6.7.8:222/0]", "", "[v2:1.2.3.4:111/0,v1:5.6.7.8:222/0]" },
+ { NULL, NULL, NULL },
+};
+
+
+
+TEST(Msgr, TestAddrParsing)
+{
+ for (auto& addr_checks : { addr_checks, addr_only_checks }) {
+ for (unsigned i = 0; addr_checks[i][0]; ++i) {
+ entity_addr_t a;
+ const char *end = "";
+ bool ok = a.parse(addr_checks[i][0], &end);
+ string out;
+ if (ok) {
+ stringstream ss;
+ ss << a;
+ getline(ss, out);
+ }
+ string left = end;
+
+ cout << "'" << addr_checks[i][0] << "' -> '" << out << "' + '" << left << "'" << std::endl;
+
+ ASSERT_EQ(out, addr_checks[i][1]);
+ ASSERT_EQ(left, addr_checks[i][2]);
+ if (addr_checks[i][0] == end) {
+ ASSERT_FALSE(ok);
+ } else {
+ ASSERT_TRUE(ok);
+ }
+ }
+ }
+}
+
+// check that legacy encoding to new decoding behaves
+
+const char *addr_checks2[][3] = {
+ { "v1:127.0.0.1", "v1:127.0.0.1:0/0", "" },
+ { "v1:127.0.0.1 foo", "v1:127.0.0.1:0/0", " foo" },
+ { "v1:127.0.0.1:1234 foo", "v1:127.0.0.1:1234/0", " foo" },
+ { "v1:127.0.0.1:1234/5678 foo", "v1:127.0.0.1:1234/5678", " foo" },
+ { "v1:2607:f298:4:2243::5522", "v1:[2607:f298:4:2243::5522]:0/0", "" },
+ { "v1:[2607:f298:4:2243::5522]", "v1:[2607:f298:4:2243::5522]:0/0", "" },
+ { "v1:[2607:f298:4:2243::5522]a", "v1:[2607:f298:4:2243::5522]:0/0", "a" },
+ { "v1:[2607:f298:4:2243::5522]:1234a", "v1:[2607:f298:4:2243::5522]:1234/0", "a" },
+ { "v1:2001:0db8:85a3:0000:0000:8a2e:0370:7334", "v1:[2001:db8:85a3::8a2e:370:7334]:0/0", "" },
+ { "v1:2001:2db8:85a3:4334:4324:8a2e:1370:7334", "v1:[2001:2db8:85a3:4334:4324:8a2e:1370:7334]:0/0", "" },
+ { "v1:1.2.3.4", "v1:1.2.3.4:0/0", "" },
+ { "v1:1.2.3.4:12", "v1:1.2.3.4:12/0", "" },
+ { "v1:1.2.3.4:12/34", "v1:1.2.3.4:12/34", "" },
+ { NULL, NULL, NULL },
+};
+
+TEST(Msgr, TestAddrEncodeAddrvecDecode)
+{
+ for (unsigned i = 0; addr_checks2[i][0]; ++i) {
+ entity_addr_t addr;
+ entity_addrvec_t addrvec;
+ const char *end = "";
+ bool ok = addr.parse(addr_checks2[i][0], &end);
+ ASSERT_TRUE(ok);
+ bufferlist bl;
+ addr.encode(bl, 0);
+ auto bli = bl.cbegin();
+ addrvec.decode(bli);
+ cout << addr_checks2[i][0] << " " << addr << " " << addrvec << std::endl;
+ ASSERT_EQ(addr, addrvec.v[0]);
+ if (addr_checks2[i][0] == end) {
+ ASSERT_FALSE(ok);
+ } else {
+ ASSERT_TRUE(ok);
+ }
+ }
+}
+
+TEST(Msgr, TestAddrvec0EncodeAddrDecode)
+{
+ for (unsigned i = 0; addr_checks2[i][0]; ++i) {
+ entity_addr_t addr;
+ entity_addrvec_t addrvec;
+ bufferlist bl;
+ const char *end = "";
+ bool ok = addr.parse(addr_checks2[i][0], &end);
+ ASSERT_TRUE(ok);
+ addrvec.v.push_back(addr);
+ addrvec.encode(bl, 0);
+ auto bli = bl.cbegin();
+ entity_addr_t a;
+ a.decode(bli);
+ ASSERT_EQ(addr, a);
+ }
+}
+
+TEST(Msgr, TestEmptyAddrvecEncodeAddrDecode)
+{
+ entity_addrvec_t addrvec;
+ entity_addr_t addr;
+ bufferlist bl;
+ addrvec.encode(bl, 0);
+ auto bli = bl.cbegin();
+ addr.decode(bli);
+ ASSERT_EQ(addr, entity_addr_t());
+}
+
+const char *addrvec_checks[][4] = {
+ { "v1:1.2.3.4", "v2:1.2.3.4", "v1:1.2.3.4", "v2:1.2.3.4" },
+ { "v2:1.2.3.5", "v1:1.2.3.5", "v1:1.2.3.5", "v2:1.2.3.5" },
+ { "v2:1.2.3.6", "v2:1.2.3.6", "v1:1.2.3.6", "v2:1.2.3.6" },
+ { "v2:1.2.3.7", "v1:1.2.3.7", "v1:1.2.3.7", "v2:1.2.3.7" },
+ { NULL, NULL, NULL, NULL },
+};
+
+/*
+ * multiple addrs where one is legacy and others are not
+ * legacy addr is in position 0
+ */
+TEST(Msgr, TestAddrvecEncodeAddrDecode0)
+{
+ entity_addr_t addr;
+ entity_addrvec_t addrvec;
+ bufferlist bl;
+
+ for (unsigned i = 0; addrvec_checks[i][0]; ++i) {
+ const char *end = "";
+ bool ok = addr.parse(addrvec_checks[i][0], &end);
+ ASSERT_TRUE(ok);
+ addrvec.v.push_back(addr);
+ }
+
+ addrvec.encode(bl, 0);
+ auto bli = bl.cbegin();
+
+ addr.decode(bli);
+
+ ASSERT_EQ(addr, addrvec.v[0]);
+}
+
+/*
+ * multiple addrs where one is legacy and others are not
+ * legacy addr is not in position 0
+ */
+TEST(Msgr, TestAddrvecEncodeAddrDecode1)
+{
+ entity_addr_t addr, a;
+ entity_addrvec_t addrvec;
+ bufferlist bl;
+ bool flag = true;
+
+ for (unsigned i = 0; addrvec_checks[i][1]; ++i) {
+ const char *end = "";
+ bool ok = addr.parse(addrvec_checks[i][1], &end);
+ ASSERT_TRUE(ok);
+ if (addr.type == entity_addr_t::TYPE_LEGACY && flag) {
+ a = addr;
+ flag = !flag;
+ }
+ addrvec.v.push_back(addr);
+ }
+
+ addrvec.encode(bl, 0);
+ auto bli = bl.cbegin();
+
+ addr.decode(bli);
+
+ ASSERT_EQ(addr, a);
+}
+
+/* multiple legacy addrs */
+TEST(Msgr, TestAddrvecEncodeAddrDecode2)
+{
+ entity_addr_t addr;
+ entity_addrvec_t addrvec;
+ bufferlist bl;
+
+ for (unsigned i = 0; addrvec_checks[i][2]; ++i) {
+ const char *end = "";
+ bool ok = addr.parse(addrvec_checks[i][2], &end);
+ ASSERT_TRUE(ok);
+ addrvec.v.push_back(addr);
+ }
+
+ addrvec.encode(bl, 0);
+ auto bli = bl.cbegin();
+
+ addr.decode(bli);
+
+ ASSERT_EQ(addr, addrvec.v[0]);
+}
+
+/* all non-legacy addrs */
+TEST(Msgr, TestAddrvecEncodeAddrDecode3)
+{
+ entity_addr_t addr;
+ entity_addrvec_t addrvec;
+ bufferlist bl;
+
+ for (unsigned i = 0; addrvec_checks[i][3]; ++i) {
+ const char *end = "";
+ bool ok = addr.parse(addrvec_checks[i][3], &end);
+ ASSERT_TRUE(ok);
+ addrvec.v.push_back(addr);
+ }
+
+ addrvec.encode(bl, 0);
+ auto bli = bl.cbegin();
+
+ addr.decode(bli);
+ //cout << addrvec << " (legacy " << addrvec.legacy_addr()
+ //<< ") -> " << addr << std::endl;
+
+ ASSERT_NE(addr, addrvec.v[0]); // it's not the first addr(which is non-legacy)
+ ASSERT_EQ(addr, entity_addr_t()); // it's not a blank addr either
+}
+
+const char *addrvec_parse_checks[][3] = {
+ { "", "", "" },
+ { "foo", "", "foo" },
+ { " foo", "", " foo" },
+ { "127.0.0.1", "v2:127.0.0.1:0/0", "" },
+ { "127.0.0.1 foo", "v2:127.0.0.1:0/0", " foo" },
+ { "[127.0.0.1]", "v2:127.0.0.1:0/0", "" },
+ { "[127.0.0.1] foo", "v2:127.0.0.1:0/0", " foo" },
+ { "127.0.0.1,::,- foo", "v2:127.0.0.1:0/0", ",::,- foo" },
+ { "[127.0.0.1,::,-] foo", "[v2:127.0.0.1:0/0,v2:[::]:0/0,-]", " foo" },
+ { "[127.0.0.1,::],- foo", "[v2:127.0.0.1:0/0,v2:[::]:0/0]", ",- foo" },
+ { "[1.2.3.4,::,foo]", "", "[1.2.3.4,::,foo]" },
+ { "[1.2.3.4,::,- foo", "", "[1.2.3.4,::,- foo" },
+ { "[[::],1.2.3.4]", "[v2:[::]:0/0,v2:1.2.3.4:0/0]", "" },
+ { "[::],1.2.3.4", "v2:[::]:0/0", ",1.2.3.4" },
+ { NULL, NULL, NULL },
+};
+
+TEST(entity_addrvec_t, parse)
+{
+ entity_addrvec_t addrvec;
+
+ for (auto v : { addr_checks, addr_checks2, addrvec_parse_checks }) {
+ for (unsigned i = 0; v[i][0]; ++i) {
+ const char *end = "";
+ bool ret = addrvec.parse(v[i][0], &end);
+ string out = stringify(addrvec);
+ string left = end;
+ cout << "'" << v[i][0] << "' -> '" << out << "' + '" << left << "'"
+ << std::endl;
+ ASSERT_EQ(out, v[i][1]);
+ ASSERT_EQ(left, v[i][2]);
+ ASSERT_TRUE(out.empty() || ret);
+ }
+ }
+}
+
+TEST(entity_addrvec_t, legacy_equals)
+{
+ entity_addr_t a1, a2;
+ ASSERT_TRUE(a1.parse("v1:1.2.3.4:567/890"));
+ ASSERT_TRUE(a2.parse("v2:1.2.3.4:567/890"));
+ entity_addrvec_t av1(a1);
+ entity_addrvec_t av21;
+ av21.v.push_back(a2);
+ av21.v.push_back(a1);
+ ASSERT_TRUE(av1.legacy_equals(av1));
+ ASSERT_TRUE(av21.legacy_equals(av21));
+ ASSERT_TRUE(av1.legacy_equals(av21));
+ ASSERT_TRUE(av21.legacy_equals(av1));
+
+ entity_addr_t b1, b2;
+ ASSERT_TRUE(b1.parse("v1:1.2.3.5:567/8"));
+ ASSERT_TRUE(b2.parse("v2:1.2.3.5:567/8"));
+ entity_addrvec_t bv1(b1);
+ entity_addrvec_t bv21;
+ bv21.v.push_back(b2);
+ bv21.v.push_back(b1);
+ ASSERT_TRUE(bv1.legacy_equals(bv21));
+ ASSERT_TRUE(bv21.legacy_equals(bv1));
+
+ ASSERT_FALSE(av1.legacy_equals(bv1));
+ ASSERT_FALSE(av21.legacy_equals(bv21));
+ ASSERT_FALSE(av21.legacy_equals(bv1));
+ ASSERT_FALSE(av1.legacy_equals(bv21));
+}
diff --git a/src/test/test_admin_socket_output.cc b/src/test/test_admin_socket_output.cc
new file mode 100644
index 000000000..9ce244e63
--- /dev/null
+++ b/src/test/test_admin_socket_output.cc
@@ -0,0 +1,129 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <algorithm> // for move
+#include <iostream> // for ostream
+#include <memory> // for unique_ptr
+#include <string> // for operator<<
+#include <vector> // for vector
+#include <boost/program_options/option.hpp> // for program_opt...
+#include <boost/program_options/options_description.hpp> // for options_des...
+#include <boost/program_options/parsers.hpp> // for basic_comma...
+#include <boost/program_options/variables_map.hpp> // for variables_map
+#include <boost/program_options/parsers.hpp> // for basic_comma...
+
+#include "admin_socket_output.h"
+#include "admin_socket_output_tests.h"
+
+namespace po = boost::program_options;
+
+void usage(po::options_description desc) {
+ std::cout << desc << std::endl;
+}
+
+void handle_unrecognised(std::vector<std::string>&& unrecognised) {
+ for (auto& un : unrecognised) {
+ std::cout << "Unrecognized Parameter: " << un << std::endl;
+ }
+}
+
+// Test functions:
+// See admin_socket_output_tests.h
+
+int main(int argc, char** argv) {
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help,h", "produce help message")
+ ("all", "implies"
+ " --osd"
+ " --mon"
+ " --mgr"
+ " --mds"
+ " --client"
+ " --vstart")
+ ("osd", "Test osd admin socket output")
+ ("mon", "Test mon admin socket output")
+ ("mgr", "Test mgr admin socket output")
+ ("mds", "Test mds admin socket output")
+ ("client", "Test client (includes rgw) admin socket output")
+ ("vstart", po::value<std::string>()->implicit_value("./out"),
+ "Modify to run in vstart environment")
+ ;
+ auto parsed =
+ po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
+ po::variables_map vm;
+ po::store(parsed, vm);
+ po::notify(vm);
+
+ auto unrecognised = collect_unrecognized(parsed.options, po::include_positional);
+ if(!unrecognised.empty()) {
+ handle_unrecognised(std::move(unrecognised));
+ usage(desc);
+ return 1;
+ }
+ if (vm.count("help") || vm.empty()) {
+ usage(desc);
+ return 2;
+ }
+
+ std::unique_ptr<AdminSocketOutput> asockout(new AdminSocketOutput);
+
+ if (vm.count("vstart")) {
+ asockout->mod_for_vstart(vm["vstart"].as<std::string>());
+ }
+
+ if(vm.count("all")) {
+ asockout->add_target("all");
+ } else {
+ if (vm.count("osd")) {
+ asockout->add_target("osd");
+ }
+ if (vm.count("mon")) {
+ asockout->add_target("mon");
+ }
+ if (vm.count("mgr")) {
+ asockout->add_target("mgr");
+ }
+ if (vm.count("mds")) {
+ asockout->add_target("mds");
+ }
+ if (vm.count("client")) {
+ asockout->add_target("client");
+ }
+ }
+
+ // Postpone commands that may affect later commands
+
+ asockout->postpone("mds", "force_readonly");
+
+ // Custom commands
+
+ //Example:
+ //asockout->add_command("osd", R"({"prefix":"config get", "var":"admin_socket"})");
+
+ // End custom commands
+
+ // Tests
+ //Example:
+ //asockout->add_test("osd", R"({"prefix":"config get", "var":"admin_socket"})", test_config_get_admin_socket);
+
+ asockout->add_test("osd", R"({"prefix":"dump_pgstate_history"})", test_dump_pgstate_history);
+
+ // End tests
+
+ asockout->exec();
+
+ return 0;
+}
diff --git a/src/test/test_any.cc b/src/test/test_any.cc
new file mode 100644
index 000000000..82d4a43f1
--- /dev/null
+++ b/src/test/test_any.cc
@@ -0,0 +1,812 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Adam C. Emerson <aemerson@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <initializer_list>
+#include <optional>
+
+#include "gtest/gtest.h"
+
+#include "include/any.h"
+
+using std::optional;
+using std::bad_any_cast;
+
+using ceph::immobile_any;
+using ceph::unique_any;
+using ceph::shared_any;
+
+using ceph::make_immobile_any;
+using ceph::make_unique_any;
+using ceph::make_shared_any;
+
+using ceph::any_cast;
+using std::swap;
+
+template<typename A>
+static void test_empty() {
+ A a;
+ EXPECT_FALSE(a.has_value());
+ EXPECT_EQ(typeid(void), a.type());
+ a.reset();
+ EXPECT_FALSE(a.has_value());
+ EXPECT_EQ(typeid(void), a.type());
+
+}
+
+TEST(Empty, Immobile) {
+ static_assert(std::is_nothrow_default_constructible_v<immobile_any<1024>>);
+ test_empty<immobile_any<1024>>();
+}
+
+TEST(Empty, Unique) {
+ static_assert(std::is_nothrow_default_constructible_v<unique_any>);
+ test_empty<unique_any>();
+}
+
+TEST(Empty, Shared) {
+ static_assert(std::is_nothrow_default_constructible_v<shared_any>);
+ test_empty<shared_any>();
+}
+
+struct cmd_tattler {
+ static thread_local bool copied;
+ static thread_local bool moved;
+ static thread_local bool destructed;
+
+ static void reset() {
+ copied = false;
+ moved = false;
+ destructed = false;
+ }
+
+ cmd_tattler() noexcept = default;
+ ~cmd_tattler() noexcept {
+ if (destructed) {
+ std::terminate();
+ }
+ destructed = true;
+ }
+ cmd_tattler(const cmd_tattler&) noexcept {
+ if (copied) {
+ std::terminate();
+ }
+ copied = true;
+ }
+ cmd_tattler& operator =(const cmd_tattler&) noexcept {
+ if (copied) {
+ std::terminate();
+ }
+ copied = true;
+ return *this;
+ }
+
+ cmd_tattler(cmd_tattler&&) noexcept {
+ if (moved) {
+ std::terminate();
+ }
+ moved = true;
+ }
+ cmd_tattler& operator =(cmd_tattler&&) noexcept {
+ if (moved) {
+ std::terminate();
+ }
+ moved = true;
+ return *this;
+ }
+};
+
+thread_local bool cmd_tattler::copied = false;
+thread_local bool cmd_tattler::moved = false;
+thread_local bool cmd_tattler::destructed = false;
+
+struct not_noexcept {
+ not_noexcept() = default;
+
+ not_noexcept(const not_noexcept&) noexcept(false) {
+ }
+ not_noexcept& operator =(const not_noexcept&) noexcept(false) {
+ return *this;
+ }
+
+ not_noexcept(not_noexcept&&) noexcept(false) {
+ }
+ not_noexcept& operator =(not_noexcept&&) noexcept(false) {
+ return *this;
+ }
+
+ template<typename ...Args>
+ explicit not_noexcept(Args&& ...) noexcept(false) {
+ }
+
+ template<typename U, typename ...Args>
+ not_noexcept(std::initializer_list<U>, Args&& ...) noexcept(false) {
+ }
+};
+
+template<typename A>
+static void test_value_CMD() {
+ {
+ cmd_tattler::reset();
+ cmd_tattler c;
+ A a(c);
+ EXPECT_TRUE(cmd_tattler::copied);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+ a.reset();
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_FALSE(a.has_value());
+ EXPECT_EQ(typeid(void), a.type());
+
+ cmd_tattler::reset();
+ a = c;
+ EXPECT_TRUE(cmd_tattler::copied);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+
+ cmd_tattler::reset();
+ a = c;
+ EXPECT_TRUE(cmd_tattler::copied);
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+ cmd_tattler::reset();
+ a.reset();
+ EXPECT_TRUE(cmd_tattler::destructed);
+ cmd_tattler::reset();
+ }
+ {
+ cmd_tattler::reset();
+ cmd_tattler c;
+ A a(std::move(c));
+ EXPECT_TRUE(cmd_tattler::moved);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+ a.reset();
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_FALSE(a.has_value());
+ EXPECT_EQ(typeid(void), a.type());
+
+ cmd_tattler::reset();
+ a = std::move(c);
+ EXPECT_TRUE(cmd_tattler::moved);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+
+ cmd_tattler::reset();
+ a = std::move(c);
+ EXPECT_TRUE(cmd_tattler::moved);
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+ cmd_tattler::reset();
+ a.reset();
+ EXPECT_TRUE(cmd_tattler::destructed);
+ cmd_tattler::reset();
+ }
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+ EXPECT_TRUE(cmd_tattler::moved);
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+
+ cmd_tattler::reset();
+ a.reset();
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_FALSE(a.has_value());
+ EXPECT_EQ(typeid(void), a.type());
+
+ cmd_tattler::reset();
+ a = cmd_tattler{};
+ EXPECT_TRUE(cmd_tattler::moved);
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(cmd_tattler), a.type());
+ cmd_tattler::reset();
+ a.reset();
+ EXPECT_TRUE(cmd_tattler::destructed);
+ cmd_tattler::reset();
+ }
+}
+
+
+
+TEST(Value_CMD, Immobile) {
+ static_assert(std::is_nothrow_constructible_v<
+ immobile_any<1024>, const cmd_tattler&>);
+ static_assert(std::is_nothrow_assignable_v<
+ immobile_any<1024>, const cmd_tattler&>);
+ static_assert(std::is_nothrow_constructible_v<
+ immobile_any<1024>, cmd_tattler&&>);
+ static_assert(std::is_nothrow_assignable_v<
+ immobile_any<1024>, cmd_tattler&&>);
+
+ static_assert(!std::is_nothrow_constructible_v<
+ immobile_any<1024>, const not_noexcept&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ immobile_any<1024>, const not_noexcept&>);
+ static_assert(!std::is_nothrow_constructible_v<
+ immobile_any<1024>, not_noexcept&&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ immobile_any<1024>, not_noexcept&&>);
+
+ test_value_CMD<immobile_any<1024>>();
+}
+
+TEST(Value_CMD, Unique) {
+ static_assert(!std::is_nothrow_constructible_v<
+ unique_any, const cmd_tattler&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ unique_any, const cmd_tattler&>);
+ static_assert(!std::is_nothrow_constructible_v<
+ unique_any, cmd_tattler&&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ unique_any, cmd_tattler&&>);
+
+ static_assert(!std::is_nothrow_constructible_v<
+ unique_any, const not_noexcept&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ unique_any, const not_noexcept&>);
+ static_assert(!std::is_nothrow_constructible_v<
+ unique_any, not_noexcept&&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ unique_any, not_noexcept&&>);
+
+ test_value_CMD<unique_any>();
+}
+
+TEST(Value_CMD, Shared) {
+ static_assert(!std::is_nothrow_constructible_v<
+ shared_any, const cmd_tattler&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ shared_any, const cmd_tattler&>);
+ static_assert(!std::is_nothrow_constructible_v<
+ shared_any, cmd_tattler&&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ shared_any, cmd_tattler&&>);
+
+ static_assert(!std::is_nothrow_constructible_v<
+ shared_any, const not_noexcept&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ shared_any, const not_noexcept&>);
+ static_assert(!std::is_nothrow_constructible_v<
+ shared_any, not_noexcept&&>);
+ static_assert(!std::is_nothrow_assignable_v<
+ shared_any, not_noexcept&&>);
+
+ test_value_CMD<shared_any>();
+}
+
+template<typename A>
+static void test_move() {
+ {
+ A a(5);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(int), a.type());
+
+ A b(std::move(a));
+ EXPECT_TRUE(b.has_value());
+ EXPECT_EQ(typeid(int), b.type());
+
+ EXPECT_FALSE(a.has_value());
+ EXPECT_EQ(typeid(void), a.type());
+ }
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+
+ A b(5);
+ EXPECT_TRUE(b.has_value());
+ EXPECT_EQ(typeid(int), b.type());
+ cmd_tattler::reset();
+
+ a = std::move(b);
+ EXPECT_TRUE(cmd_tattler::destructed);
+
+ EXPECT_FALSE(b.has_value());
+ EXPECT_EQ(typeid(void), b.type());
+
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(int), a.type());
+ }
+}
+
+static_assert(!std::is_move_constructible_v<immobile_any<1024>>);
+static_assert(!std::is_move_assignable_v<immobile_any<1024>>);
+
+TEST(Move, Unique) {
+ static_assert(std::is_nothrow_move_constructible_v<unique_any>);
+ static_assert(std::is_nothrow_move_assignable_v<unique_any>);
+
+ test_move<unique_any>();
+}
+
+TEST(Move, Shared) {
+ static_assert(std::is_nothrow_move_constructible_v<shared_any>);
+ static_assert(std::is_nothrow_move_assignable_v<shared_any>);
+
+ test_move<shared_any>();
+}
+
+template<typename A>
+static void test_copy() {
+ {
+ const A a(5);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(int), a.type());
+
+ A b(a);
+ EXPECT_TRUE(b.has_value());
+ EXPECT_EQ(typeid(int), b.type());
+
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(int), a.type());
+
+ EXPECT_EQ(any_cast<int>(a), any_cast<int>(b));
+ }
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+
+ const A b(5);
+ EXPECT_TRUE(b.has_value());
+ EXPECT_EQ(typeid(int), b.type());
+ cmd_tattler::reset();
+
+ a = b;
+ EXPECT_TRUE(cmd_tattler::destructed);
+
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(int), a.type());
+
+ EXPECT_TRUE(b.has_value());
+ EXPECT_EQ(typeid(int), b.type());
+
+ EXPECT_EQ(any_cast<int>(a), any_cast<int>(b));
+ }
+}
+
+static_assert(!std::is_copy_constructible_v<immobile_any<1024>>);
+static_assert(!std::is_copy_assignable_v<immobile_any<1024>>);
+
+static_assert(!std::is_copy_constructible_v<unique_any>);
+static_assert(!std::is_copy_assignable_v<unique_any>);
+
+TEST(Copy, Shared) {
+ static_assert(std::is_nothrow_copy_constructible_v<shared_any>);
+ test_copy<shared_any>();
+}
+
+struct unmoving {
+ optional<int> a;
+
+ unmoving() noexcept {}
+
+ template<typename... Args>
+ explicit unmoving(Args&& ...args) noexcept
+ : a(sizeof...(Args)) {}
+
+ template<typename U, typename... Args>
+ explicit unmoving(std::initializer_list<U> l) noexcept
+ : a(-l.size()) {}
+
+ template<typename U, typename... Args>
+ unmoving(std::initializer_list<U> l, Args&& ...args) noexcept
+ : a(-l.size() * sizeof...(Args)) {}
+
+ unmoving(const unmoving&) = delete;
+ unmoving& operator =(const unmoving&) = delete;
+
+ unmoving(unmoving&&) = delete;
+ unmoving& operator =(unmoving&&) = delete;
+};
+
+template<typename A>
+static void test_unmoving_pack_il() {
+ // Nothing!
+ {
+ const A a(std::in_place_type<unmoving>);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_FALSE(any_cast<const unmoving&>(a).a);
+ }
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+
+ cmd_tattler::reset();
+ a.template emplace<unmoving>();
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_FALSE(any_cast<unmoving&>(a).a);
+ }
+
+ // Pack!
+ {
+ const A a(std::in_place_type<unmoving>, nullptr, 5, 3.1);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(3, *any_cast<const unmoving&>(a).a);
+ }
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+
+ cmd_tattler::reset();
+ a.template emplace<unmoving>(nullptr, 5, 3.1);
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_EQ(3, *any_cast<unmoving&>(a).a);
+ }
+
+ // List!
+ {
+ const A a(std::in_place_type<unmoving>, {true, true, true, true});
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-4, *any_cast<const unmoving&>(a).a);
+ }
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+
+ cmd_tattler::reset();
+ a.template emplace<unmoving>({true, true, true, true});
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_EQ(-4, *any_cast<unmoving&>(a).a);
+ }
+
+ // List + pack!!
+ {
+ const A a(std::in_place_type<unmoving>, {true, true, true, true},
+ nullptr, 5, 3.1);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-12, *any_cast<const unmoving&>(a).a);
+ }
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+
+ cmd_tattler::reset();
+ a.template emplace<unmoving>({true, true, true, true}, nullptr, 5, 3.1);
+ EXPECT_TRUE(cmd_tattler::destructed);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_EQ(-12, *any_cast<unmoving&>(a).a);
+ }
+}
+
+TEST(UmovingPackIl, Immobile) {
+ static_assert(std::is_nothrow_constructible_v<immobile_any<1024>,
+ std::in_place_type_t<unmoving>>);
+ static_assert(noexcept(immobile_any<1024>{}.emplace<unmoving>()));
+
+ static_assert(std::is_nothrow_constructible_v<immobile_any<1024>,
+ std::in_place_type_t<unmoving>, std::nullptr_t, int, double>);
+ static_assert(noexcept(immobile_any<1024>{}.emplace<unmoving>(
+ nullptr, 5, 3.1)));
+
+ static_assert(std::is_nothrow_constructible_v<immobile_any<1024>,
+ std::in_place_type_t<unmoving>, std::initializer_list<int>>);
+ static_assert(noexcept(immobile_any<1024>{}.emplace<unmoving>(
+ {true, true, true, true})));
+
+ static_assert(std::is_nothrow_constructible_v<immobile_any<1024>,
+ std::in_place_type_t<unmoving>, std::initializer_list<int>,
+ std::nullptr_t, int, double>);
+ static_assert(noexcept(immobile_any<1024>{}.emplace<unmoving>(
+ {true, true, true, true}, nullptr, 5, 3.1)));
+
+ test_unmoving_pack_il<immobile_any<1024>>();
+}
+
+TEST(UmovingPackIl, Unique) {
+ static_assert(!std::is_nothrow_constructible_v<unique_any,
+ std::in_place_type_t<unmoving>>);
+ static_assert(!noexcept(unique_any{}.emplace<unmoving>()));
+
+ static_assert(!std::is_nothrow_constructible_v<unique_any,
+ std::in_place_type_t<unmoving>, std::nullptr_t, int, double>);
+ static_assert(!noexcept(unique_any{}.emplace<unmoving>(
+ nullptr, 5, 3.1)));
+
+ static_assert(!std::is_nothrow_constructible_v<unique_any,
+ std::in_place_type_t<unmoving>, std::initializer_list<int>>);
+ static_assert(!noexcept(unique_any{}.emplace<unmoving>(
+ {true, true, true, true})));
+
+ static_assert(!std::is_nothrow_constructible_v<unique_any,
+ std::in_place_type_t<unmoving>, std::initializer_list<int>,
+ std::nullptr_t, int, double>);
+ static_assert(!noexcept(unique_any{}.emplace<unmoving>(
+ {true, true, true, true}, nullptr, 5, 3.1)));
+
+ test_unmoving_pack_il<unique_any>();
+}
+
+TEST(UmovingPackIl, Shared) {
+ static_assert(!std::is_nothrow_constructible_v<shared_any,
+ std::in_place_type_t<unmoving>>);
+ static_assert(!noexcept(shared_any{}.emplace<unmoving>()));
+
+ static_assert(!std::is_nothrow_constructible_v<shared_any,
+ std::in_place_type_t<unmoving>, std::nullptr_t, int, double>);
+ static_assert(!noexcept(shared_any{}.emplace<unmoving>(
+ nullptr, 5, 3.1)));
+
+ static_assert(!std::is_nothrow_constructible_v<shared_any,
+ std::in_place_type_t<unmoving>, std::initializer_list<int>>);
+ static_assert(!noexcept(shared_any{}.emplace<unmoving>(
+ {true, true, true, true})));
+
+ static_assert(!std::is_nothrow_constructible_v<shared_any,
+ std::in_place_type_t<unmoving>, std::initializer_list<int>,
+ std::nullptr_t, int, double>);
+ static_assert(!noexcept(shared_any{}.emplace<unmoving>(
+ {true, true, true, true}, nullptr, 5, 3.1)));
+
+ test_unmoving_pack_il<shared_any>();
+}
+
+template<typename A>
+static void test_swap() {
+ A a(true);
+ ASSERT_TRUE(a.has_value());
+ ASSERT_EQ(typeid(bool), a.type());
+ ASSERT_EQ(true, any_cast<bool>(a));
+
+ A b(5);
+ ASSERT_TRUE(b.has_value());
+ ASSERT_EQ(typeid(int), b.type());
+ ASSERT_EQ(5, any_cast<int>(b));
+
+ a.swap(b);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(int), a.type());
+ EXPECT_EQ(5, any_cast<int>(a));
+
+ EXPECT_TRUE(b.has_value());
+ ASSERT_EQ(typeid(bool), b.type());
+ ASSERT_EQ(true, any_cast<bool>(b));
+
+ swap(a,b);
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(bool), a.type());
+ EXPECT_EQ(true, any_cast<bool>(a));
+
+ EXPECT_TRUE(b.has_value());
+ EXPECT_EQ(typeid(int), b.type());
+ EXPECT_EQ(5, any_cast<int>(b));
+}
+
+static_assert(!std::is_swappable_v<immobile_any<1024>>);
+
+TEST(Swap, Unique) {
+ static_assert(std::is_nothrow_swappable_v<unique_any>);
+ test_swap<unique_any>();
+}
+
+TEST(Swap, Shared) {
+ static_assert(std::is_nothrow_swappable_v<shared_any>);
+ test_swap<shared_any>();
+}
+
+template<typename A>
+static void test_cast() {
+ // Empty
+ {
+ A a;
+ EXPECT_EQ(nullptr, any_cast<int>(&a));
+ EXPECT_THROW({any_cast<int>(a);}, bad_any_cast);
+ EXPECT_THROW({any_cast<int&>(a);}, bad_any_cast);
+ EXPECT_THROW({any_cast<int>(std::move(a));}, bad_any_cast);
+ EXPECT_THROW({any_cast<int&&>(std::move(a));}, bad_any_cast);
+ }
+
+ // Constant Empty
+ {
+ const A a{};
+ EXPECT_EQ(nullptr, any_cast<int>(const_cast<const A*>(&a)));
+ EXPECT_THROW({any_cast<int>(a);}, bad_any_cast);
+ EXPECT_THROW({any_cast<const int&>(a);}, bad_any_cast);
+ }
+
+ // Filled!
+ {
+ A a(true);
+ EXPECT_TRUE(*any_cast<bool>(&a));
+ EXPECT_EQ(nullptr, any_cast<int>(&a));
+
+ EXPECT_TRUE(any_cast<bool>(a));
+ EXPECT_THROW({any_cast<int>(a);}, bad_any_cast);
+
+ EXPECT_TRUE(any_cast<bool&>(a));
+ EXPECT_THROW({any_cast<int&>(a);}, bad_any_cast);
+
+ EXPECT_TRUE(any_cast<bool>(std::move(a)));
+ EXPECT_THROW({any_cast<int>(std::move(a));}, bad_any_cast);
+
+ EXPECT_TRUE(any_cast<bool&&>(std::move(a)));
+ EXPECT_THROW({any_cast<int&&>(std::move(a));}, bad_any_cast);
+ }
+
+ // Constant filled
+ {
+ const A a(true);
+ EXPECT_TRUE(*any_cast<const bool>(&a));
+ EXPECT_EQ(nullptr, any_cast<const int>(&a));
+
+ EXPECT_TRUE(any_cast<bool>(a));
+ EXPECT_THROW({any_cast<int>(a);}, bad_any_cast);
+
+ EXPECT_TRUE(any_cast<const bool&>(a));
+ EXPECT_THROW({any_cast<const int&>(a);}, bad_any_cast);
+ }
+
+ // Move!
+ {
+ cmd_tattler::reset();
+ A a(cmd_tattler{});
+ cmd_tattler::reset();
+
+ auto q = any_cast<cmd_tattler>(std::move(a));
+ EXPECT_TRUE(cmd_tattler::moved);
+ cmd_tattler::reset();
+ a.reset();
+ cmd_tattler::reset();
+ }
+
+ // Move! Again!
+ {
+ cmd_tattler::reset();
+ auto q = any_cast<cmd_tattler>(A(std::in_place_type<cmd_tattler>));
+ EXPECT_TRUE(cmd_tattler::moved);
+ cmd_tattler::reset();
+ }
+}
+
+TEST(Cast, Immobile) {
+ test_cast<immobile_any<1024>>();
+}
+
+TEST(Cast, Unique) {
+ test_cast<unique_any>();
+}
+
+TEST(Cast, Shared) {
+ test_cast<shared_any>();
+}
+
+TEST(Make, Immobile) {
+ // Nothing!
+ {
+ auto a{make_immobile_any<unmoving, 1024>()};
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_FALSE(any_cast<const unmoving&>(a).a);
+ }
+
+ // Pack!
+ {
+ auto a(make_immobile_any<unmoving, 1024>(nullptr, 5, 3.1));
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(3, *any_cast<const unmoving&>(a).a);
+ }
+
+ // List!
+ {
+ auto a(make_immobile_any<unmoving, 1024>({true, true, true, true}));
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-4, *any_cast<const unmoving&>(a).a);
+ }
+
+ // List + pack!!
+ {
+ auto a{make_immobile_any<unmoving, 1024>({true, true, true, true},
+ nullptr, 5, 3.1)};
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-12, *any_cast<const unmoving&>(a).a);
+ }
+}
+
+TEST(Make, Unique) {
+ // Nothing!
+ {
+ auto a{make_unique_any<unmoving>()};
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_FALSE(any_cast<const unmoving&>(a).a);
+ }
+
+ // Pack!
+ {
+ auto a(make_unique_any<unmoving>(nullptr, 5, 3.1));
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(3, *any_cast<const unmoving&>(a).a);
+ }
+
+ // List!
+ {
+ auto a(make_unique_any<unmoving>({true, true, true, true}));
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-4, *any_cast<const unmoving&>(a).a);
+ }
+
+ // List + pack!!
+ {
+ auto a{make_unique_any<unmoving>({true, true, true, true},
+ nullptr, 5, 3.1)};
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-12, *any_cast<const unmoving&>(a).a);
+ }
+}
+
+TEST(Make, Shared) {
+ // Nothing!
+ {
+ auto a{make_shared_any<unmoving>()};
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_FALSE(any_cast<const unmoving&>(a).a);
+ }
+
+ // Pack!
+ {
+ auto a(make_shared_any<unmoving>(nullptr, 5, 3.1));
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(3, *any_cast<const unmoving&>(a).a);
+ }
+
+ // List!
+ {
+ auto a(make_shared_any<unmoving>({true, true, true, true}));
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-4, *any_cast<const unmoving&>(a).a);
+ }
+
+ // List + pack!!
+ {
+ auto a{make_shared_any<unmoving>({true, true, true, true},
+ nullptr, 5, 3.1)};
+ EXPECT_TRUE(a.has_value());
+ EXPECT_EQ(typeid(unmoving), a.type());
+ EXPECT_TRUE(any_cast<const unmoving&>(a).a);
+ EXPECT_EQ(-12, *any_cast<const unmoving&>(a).a);
+ }
+}
diff --git a/src/test/test_arch.cc b/src/test/test_arch.cc
new file mode 100644
index 000000000..373ee0b09
--- /dev/null
+++ b/src/test/test_arch.cc
@@ -0,0 +1,93 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+
+#include "arch/probe.h"
+#include "arch/intel.h"
+#include "arch/arm.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+
+#define FLAGS_SIZE 4096
+
+TEST(Arch, all)
+{
+ ceph_arch_probe();
+ EXPECT_TRUE(ceph_arch_probed);
+
+#if (__arm__ || __aarch64__ || __x86_64__) && __linux__
+ char flags[FLAGS_SIZE];
+ FILE *f = popen("grep '^\\(flags\\|Features\\)[ ]*:' "
+ "/proc/cpuinfo | head -1", "r");
+ if(f == NULL || fgets(flags, FLAGS_SIZE - 1, f) == NULL) {
+ // silently do nothing if /proc/cpuinfo does exist, is not
+ // readable or does not contain the expected information
+ if (f)
+ pclose(f);
+ return;
+ }
+ pclose(f);
+ flags[strlen(flags) - 1] = ' ';
+
+ int expected;
+
+#if (__arm__ || __aarch64__)
+
+ expected = (strstr(flags, " neon ") || strstr(flags, " asimd ")) ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_neon);
+
+#endif
+#if (__aarch64__)
+
+ expected = strstr(flags, " crc32 ") ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_aarch64_crc32);
+
+#endif
+#if (__x86_64__)
+
+ expected = strstr(flags, " pclmulqdq ") ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_intel_pclmul);
+
+ expected = strstr(flags, " sse4_2 ") ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_intel_sse42);
+
+ expected = strstr(flags, " sse4_1 ") ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_intel_sse41);
+
+ expected = (strstr(flags, " sse3 ") || strstr(flags, " ssse3 ") || strstr(flags, " pni ")) ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_intel_sse3);
+
+ expected = strstr(flags, " ssse3 ") ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_intel_ssse3);
+
+ expected = strstr(flags, " sse2 ") ? 1 : 0;
+ EXPECT_EQ(expected, ceph_arch_intel_sse2);
+
+#endif
+
+#endif
+}
+
+
+/*
+ * Local Variables:
+ * compile-command: "cd .. ; make -j4 &&
+ * make unittest_arch &&
+ * valgrind --tool=memcheck ./unittest_arch --gtest_filter=*.*"
+ * End:
+ */
diff --git a/src/test/test_auth.cc b/src/test/test_auth.cc
new file mode 100644
index 000000000..a14b3eb9b
--- /dev/null
+++ b/src/test/test_auth.cc
@@ -0,0 +1,247 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "include/stringify.h"
+#include "auth/Auth.h"
+#include "gtest/gtest.h"
+#include "common/ceph_context.h"
+#include "global/global_context.h"
+#include "auth/AuthRegistry.h"
+
+#include <sstream>
+
+TEST(AuthRegistry, con_modes)
+{
+ auto cct = g_ceph_context;
+ AuthRegistry reg(cct);
+ std::vector<uint32_t> modes;
+
+ const std::vector<uint32_t> crc_secure = { CEPH_CON_MODE_CRC,
+ CEPH_CON_MODE_SECURE };
+ const std::vector<uint32_t> secure_crc = { CEPH_CON_MODE_SECURE,
+ CEPH_CON_MODE_CRC };
+ const std::vector<uint32_t> secure = { CEPH_CON_MODE_SECURE };
+
+ cct->_conf.set_val(
+ "enable_experimental_unrecoverable_data_corrupting_features", "*");
+
+ // baseline: everybody agrees
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ cct->_conf.set_val("ms_cluster_mode", "crc secure");
+ cct->_conf.set_val("ms_service_mode", "crc secure");
+ cct->_conf.set_val("ms_client_mode", "crc secure");
+ cct->_conf.set_val("ms_mon_cluster_mode", "crc secure");
+ cct->_conf.set_val("ms_mon_service_mode", "crc secure");
+ cct->_conf.set_val("ms_mon_client_mode", "crc secure");
+ cct->_conf.apply_changes(NULL);
+
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+ ASSERT_EQ((uint32_t)CEPH_CON_MODE_CRC, reg.pick_mode(CEPH_ENTITY_TYPE_OSD,
+ CEPH_AUTH_CEPHX,
+ crc_secure));
+
+ // what mons prefer secure, internal to mon cluster only
+ cct->_conf.set_val("ms_mon_cluster_mode", "secure");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_OSD);
+
+ /* mon/mgr are treated the same, and relevant config is ms_mon_cluster_mode */
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MON);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ // how all cluster -> mon connections secure?
+ cct->_conf.set_val("ms_mon_service_mode", "secure");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_OSD);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MON);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MDS, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+
+ // how about client -> mon connections?
+ cct->_conf.set_val("ms_mon_client_mode", "secure");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ // ms_mon)client_mode doesn't does't affect daemons, though...
+ cct->_conf.set_val("ms_mon_service_mode", "crc secure");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MON);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MDS, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ // how about all internal cluster connection secure?
+ cct->_conf.set_val("ms_cluster_mode", "secure");
+ cct->_conf.set_val("ms_mon_service_mode", "secure");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_OSD);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_CLIENT, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MGR);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MDS, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_CLIENT, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MDS);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_CLIENT, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MON);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_CLIENT, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ // how about all connections to the cluster?
+ cct->_conf.set_val("ms_service_mode", "secure");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MDS, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, crc_secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_OSD);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_CLIENT, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MGR);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_CLIENT, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MDS, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_MDS);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_CLIENT, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ // client forcing things?
+ cct->_conf.set_val("ms_cluster_mode", "crc secure");
+ cct->_conf.set_val("ms_service_mode", "crc secure");
+ cct->_conf.set_val("ms_client_mode", "secure");
+ cct->_conf.set_val("ms_mon_cluster_mode", "crc secure");
+ cct->_conf.set_val("ms_mon_service_mode", "crc secure");
+ cct->_conf.set_val("ms_mon_client_mode", "secure");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MDS, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure);
+
+ // client *preferring* secure?
+ cct->_conf.set_val("ms_cluster_mode", "crc secure");
+ cct->_conf.set_val("ms_service_mode", "crc secure");
+ cct->_conf.set_val("ms_client_mode", "secure crc");
+ cct->_conf.set_val("ms_mon_cluster_mode", "crc secure");
+ cct->_conf.set_val("ms_mon_service_mode", "crc secure");
+ cct->_conf.set_val("ms_mon_client_mode", "secure crc");
+ cct->_conf.apply_changes(NULL);
+
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MON, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure_crc);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MGR, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure_crc);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_OSD, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure_crc);
+ reg.get_supported_modes(CEPH_ENTITY_TYPE_MDS, CEPH_AUTH_CEPHX, &modes);
+ ASSERT_EQ(modes, secure_crc);
+
+ // back to normalish, for the benefit of the next test(s)
+ cct->_set_module_type(CEPH_ENTITY_TYPE_CLIENT);
+}
diff --git a/src/test/test_backfill.sh b/src/test/test_backfill.sh
new file mode 100755
index 000000000..3565724f4
--- /dev/null
+++ b/src/test/test_backfill.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+TEST_POOL=rbd
+
+./stop.sh
+CEPH_NUM_OSD=3 ./vstart.sh -d -n -x -o 'osd min pg log entries = 5'
+./rados -p $TEST_POOL bench 15 write -b 4096
+./ceph osd out 0
+ ./init-ceph stop osd.0
+ ./ceph osd down 0
+./rados -p $TEST_POOL bench 600 write -b 4096
diff --git a/src/test/test_c2c.cc b/src/test/test_c2c.cc
new file mode 100644
index 000000000..5fe1ac2f1
--- /dev/null
+++ b/src/test/test_c2c.cc
@@ -0,0 +1,88 @@
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "common/config.h"
+#include "global/global_init.h"
+#include "global/signal_handler.h"
+
+#include "include/mempool.h"
+
+#include <iostream>
+#include <string>
+
+using std::cerr;
+using std::string;
+
+static void usage(void)
+{
+ cerr << "--threads number of threads (default 1)" << std::endl;
+ cerr << "--sharding activate sharding optimization" << std::endl;
+}
+
+
+mempool::shard_t shards[mempool::num_shards] = {0};
+
+void sigterm_handler(int signum)
+{
+ size_t total = 0;
+ for (auto& shard : shards) {
+ total += shard.bytes;
+ }
+ std::cout << total << std::endl;
+ exit(0);
+}
+
+int main(int argc, const char **argv)
+{
+ int ret = 0;
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ int threads = 1;
+ bool sharding = false;
+ for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
+ if (ceph_argparse_double_dash(args, i)) {
+ break;
+ }
+ else if (ceph_argparse_witharg(args, i, &threads, cerr, "--threads", "-t", (char*)NULL)) {
+ }
+ else if (ceph_argparse_flag(args, i, "--sharding", "-s", (char*)NULL)) {
+ sharding = true;
+ }
+ else {
+ cerr << "unknown command line option: " << *i << std::endl;
+ cerr << std::endl;
+ usage();
+ return 2;
+ }
+ }
+
+ init_async_signal_handler();
+ register_async_signal_handler(SIGTERM, sigterm_handler);
+
+
+ std::vector<std::thread> workers;
+ for (int i = 0; i < threads; i++) {
+ workers.push_back(
+ std::thread([&](){
+ while(1) {
+ size_t i;
+ if (sharding) {
+ i = mempool::pool_t::pick_a_shard_int();
+ } else {
+ i = 0;
+ }
+ shards[i].bytes++;
+ }
+ }));
+ }
+
+ for (auto& t:workers) {
+ t.join();
+ }
+ workers.clear();
+
+ return ret;
+}
diff --git a/src/test/test_c_headers.c b/src/test/test_c_headers.c
new file mode 100644
index 000000000..fc83a82a2
--- /dev/null
+++ b/src/test/test_c_headers.c
@@ -0,0 +1,30 @@
+// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=2 sw=2 smarttab
+
+#include <stdlib.h>
+#include "include/cephfs/libcephfs.h"
+#include "include/rados/librados.h"
+
+#ifdef __cplusplus
+#error "test invalid: only use C mode"
+#endif
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ /* librados.h */
+ rados_t cluster;
+ ret = rados_create(&cluster, NULL);
+ if (ret < 0) {
+ return EXIT_FAILURE;
+ }
+ /* libcephfs.h */
+ struct ceph_mount_info *cmount;
+ ret = ceph_create(&cmount, NULL);
+ if (ret < 0) {
+ return EXIT_FAILURE;
+ }
+
+ return 0;
+}
diff --git a/src/test/test_cfuse_cache_invalidate.cc b/src/test/test_cfuse_cache_invalidate.cc
new file mode 100644
index 000000000..4b45cb39c
--- /dev/null
+++ b/src/test/test_cfuse_cache_invalidate.cc
@@ -0,0 +1,54 @@
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "include/ceph_assert.h"
+
+#define REGION 1048576
+int main(int argc, char *argv[]) {
+
+ pid_t p = fork();
+ char buf[REGION];
+ memset(buf, 0, sizeof(buf));
+
+ if (p != 0) {
+ int done = 0;
+ int fd = open(argv[1], O_RDWR|O_CREAT, 0644);
+ if (fd < 0) {
+ perror(argv[1]);
+ return 1;
+ }
+
+ int i = 0;
+ while(!done) {
+ printf("writing %d\n", i++);
+ ceph_assert(pwrite(fd, buf, REGION, 0) == REGION);
+ int status;
+ int ret = waitpid(p, &status, WNOHANG);
+ ceph_assert(ret >= 0);
+ if (ret > 0) {
+ done = 1;
+ }
+ }
+ close(fd);
+ } else {
+ sleep(1);
+ int fd = open(argv[2], O_RDONLY, 0644);
+ if (fd < 0) {
+ perror(argv[2]);
+ return 1;
+ }
+
+ printf("reading\n");
+ ceph_assert(pread(fd, buf, REGION, 0) == REGION);
+ close(fd);
+ }
+
+ return 0;
+}
diff --git a/src/test/test_common.sh b/src/test/test_common.sh
new file mode 100755
index 000000000..1fea1f9fd
--- /dev/null
+++ b/src/test/test_common.sh
@@ -0,0 +1,174 @@
+#!/usr/bin/env bash
+set -x
+
+#
+# test_common.sh
+#
+# Common routines for tests
+#
+#
+# Environment variables that affect tests:
+# KEEP_TEMPDIR If set, the tempdir will not be deleted
+# when the test is over.
+#
+
+# Clean up the temporary directory
+cleanup() {
+ if [ -n ${TEMPDIR} ]; then
+ rm -rf "${TEMPDIR}"
+ fi
+}
+
+# Create a temporary directory where test files will be stored.
+setup_tempdir() {
+ TEMPDIR=`mktemp -d`
+ if [ -z $KEEP_TEMPDIR ]; then
+ trap cleanup INT TERM EXIT
+ fi
+}
+
+# Standard initialization function for tests
+init() {
+ setup_tempdir
+ cd `dirname $0`/..
+}
+
+# Exit with an error message.
+die() {
+ echo $@
+ exit 1
+}
+
+# Test that flag is set (the element is found in the list)
+is_set()
+{
+ local flag=$1; shift
+ local flags="$@"
+ local i
+
+ for i in ${flags}; do
+ if [ "${flag}" = "${i}" ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+# Stop an OSD started by vstart
+stop_osd() {
+ osd_index=$1
+ pidfile="out/osd.$osd_index.pid"
+ if [ -e $pidfile ]; then
+ if kill `cat $pidfile` ; then
+ poll_cmd "eval test -e $pidfile ; echo \$?" "1" 1 30
+ [ $? -eq 1 ] && return 0
+ echo "ceph-osd process did not terminate correctly"
+ else
+ echo "kill `cat $pidfile` failed"
+ fi
+ else
+ echo "ceph-osd process $osd_index is not running"
+ fi
+ return 1
+}
+
+# Restart an OSD started by vstart
+restart_osd() {
+ osd_index=$1
+ ./ceph-osd -i $osd_index -c ceph.conf &
+}
+
+# Ask the user a yes/no question and get the response
+yes_or_no_choice() {
+ while true; do
+ echo -n "${1} [y/n] "
+ read ans
+ case "${ans}" in
+ y|Y|yes|YES|Yes) return 0 ;;
+ n|N|no|NO|No) return 1 ;;
+ *) echo "Please type yes or no."
+ echo ;;
+ esac
+ done
+}
+
+# Block until the user says "continue" or "c"
+continue_prompt() {
+ prompt=${1:-"to go on"}
+ while true; do
+ echo "Please type 'c' or 'continue' ${prompt}."
+ read ans
+ case "${ans}" in
+ c|continue) return 0 ;;
+ *) echo ;;
+ esac
+ done
+}
+
+# Write a bunch of objects to rados
+write_objects() {
+ start_ver=$1
+ stop_ver=$2
+ num_objs=$3
+ obj_size=$4
+ pool=$5
+ [ -d "${TEMPDIR}" ] || die "must setup_tempdir"
+ for v in `seq $start_ver $stop_ver`; do
+ chr=`perl -e "print chr(48+$v)"`
+ head -c $obj_size /dev/zero | tr '\0' "$chr" > $TEMPDIR/ver$v
+ for i in `seq -w 1 $num_objs`; do
+ ./rados -c ./ceph.conf -p $pool put obj$i $TEMPDIR/ver$v || die "radostool failed"
+ done
+ done
+}
+
+read_objects() {
+ ver=$1
+ num_objs=$2
+ obj_size=$3
+ [ -d "${TEMPDIR}" ] || die "must setup_tempdir"
+ chr=`perl -e "print chr(48+$ver)"`
+ head -c $obj_size /dev/zero | tr '\0' "$chr" > $TEMPDIR/exemplar
+ for i in `seq -w 1 $num_objs`; do
+ ./rados -c ./ceph.conf -p $pool get obj$i $TEMPDIR/out$i || die "radostool failed"
+ cmp $TEMPDIR/out$i $TEMPDIR/exemplar || die "got back incorrect obj$i"
+ done
+}
+
+poll_cmd() {
+ command=$1
+ search_str=$2
+ polling_interval=$3
+ total_time=$4
+
+ t=0
+ while [ $t -lt $total_time ]; do
+ $command | grep "$search_str"
+ [ $? -eq 0 ] && return 1
+ sleep $polling_interval
+ t=$(($t+$polling_interval))
+ done
+
+ return 0
+}
+
+dump_osd_store() {
+ set +x
+ echo "dumping osd store..."
+ find ./dev/osd* -type f | grep obj | grep head$ | sort | while read file; do
+ echo $file
+ head -c 10 $file
+ echo
+ done
+}
+
+start_recovery() {
+ CEPH_NUM_OSD=$1
+ osd=0
+ while [ $osd -lt $CEPH_NUM_OSD ]; do
+ ./ceph -c ./ceph.conf tell osd.$osd debug kick_recovery_wq 0
+ osd=$((osd+1))
+ done
+}
+
+init
diff --git a/src/test/test_cors.cc b/src/test/test_cors.cc
new file mode 100644
index 000000000..186f47a73
--- /dev/null
+++ b/src/test/test_cors.cc
@@ -0,0 +1,901 @@
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+extern "C"{
+#include <curl/curl.h>
+}
+#include "common/ceph_crypto.h"
+#include <map>
+#include <list>
+#define S3_BUCKET_NAME "s3testgw.fcgi"
+#define SWIFT_BUCKET_NAME "swift3testgw.fcgi"
+#define BUCKET_URL \
+ ((g_test->get_key_type() == KEY_TYPE_S3)?(string("/" S3_BUCKET_NAME)):(string("/swift/v1/" SWIFT_BUCKET_NAME)))
+#include <gtest/gtest.h>
+#include "common/code_environment.h"
+#include "common/ceph_argparse.h"
+#include "common/Finisher.h"
+#include "global/global_init.h"
+#include "rgw_cors.h"
+#include "rgw_cors_s3.h"
+
+using namespace std;
+
+#define CURL_VERBOSE 0
+#define HTTP_RESPONSE_STR "RespCode"
+#define CEPH_CRYPTO_HMACSHA1_DIGESTSIZE 20
+
+extern "C" int ceph_armor(char *dst, const char *dst_end,
+ const char *src, const char *end);
+enum key_type {
+ KEY_TYPE_UNDEFINED = 0,
+ KEY_TYPE_SWIFT,
+ KEY_TYPE_S3
+};
+
+static void print_usage(char *exec){
+ cout << "Usage: " << exec << " <Options>\n";
+ cout << "Options:\n"
+ "-g <gw-ip> - The ip address of the gateway\n"
+ "-p <gw-port> - The port number of the gateway\n"
+ "-k <SWIFT|S3> - The key type, either SWIFT or S3\n"
+ "-s3 <AWSAccessKeyId:SecretAccessKeyID> - Only, if the key type is S3, gives S3 credentials\n"
+ "-swift <Auth-Token> - Only if the key type is SWIFT, and gives the SWIFT credentials\n";
+}
+class test_cors_helper {
+ private:
+ string host;
+ string port;
+ string creds;
+ CURL *curl_inst;
+ map<string, string> response;
+ list<string> extra_hdrs;
+ string *resp_data;
+ unsigned resp_code;
+ key_type kt;
+ public:
+ test_cors_helper() : curl_inst(NULL), resp_data(NULL), resp_code(0), kt(KEY_TYPE_UNDEFINED){
+ curl_global_init(CURL_GLOBAL_ALL);
+ }
+ ~test_cors_helper(){
+ curl_global_cleanup();
+ }
+ int send_request(string method, string uri,
+ size_t (*function)(void *,size_t,size_t,void *) = 0,
+ void *ud = 0, size_t length = 0);
+ int extract_input(unsigned argc, char *argv[]);
+ string& get_response(string hdr){
+ return response[hdr];
+ }
+ void set_extra_header(string hdr){
+ extra_hdrs.push_back(hdr);
+ }
+ void set_response(char *val);
+ void set_response_data(char *data, size_t len){
+ if(resp_data) delete resp_data;
+ resp_data = new string(data, len);
+ /*cout << resp_data->c_str() << "\n";*/
+ }
+ const string *get_response_data(){return resp_data;}
+ unsigned get_resp_code(){return resp_code;}
+ key_type get_key_type(){return kt;}
+};
+
+int test_cors_helper::extract_input(unsigned argc, char *argv[]){
+#define ERR_CHECK_NEXT_PARAM(o) \
+ if((loop + 1) >= argc)return -1; \
+ else o = argv[loop+1];
+
+ for(unsigned loop = 1;loop < argc; loop += 2){
+ if(strcmp(argv[loop], "-g") == 0){
+ ERR_CHECK_NEXT_PARAM(host);
+ }else if(strcmp(argv[loop], "-k") == 0){
+ string type;
+ ERR_CHECK_NEXT_PARAM(type);
+ if(type.compare("S3") == 0)kt = KEY_TYPE_S3;
+ else if(type.compare("SWIFT") == 0)kt = KEY_TYPE_SWIFT;
+ }else if(strcmp(argv[loop],"-s3") == 0){
+ ERR_CHECK_NEXT_PARAM(creds);
+ }else if(strcmp(argv[loop],"-swift") == 0){
+ ERR_CHECK_NEXT_PARAM(creds);
+ }else if(strcmp(argv[loop],"-p") == 0){
+ ERR_CHECK_NEXT_PARAM(port);
+ }else return -1;
+ }
+ if(host.empty() || creds.empty())
+ return -1;
+ return 0;
+}
+
+void test_cors_helper::set_response(char *r){
+ string sr(r), h, v;
+ size_t off = sr.find(": ");
+ if(off != string::npos){
+ h.assign(sr, 0, off);
+ v.assign(sr, off + 2, sr.find("\r\n") - (off+2));
+ }else{
+ /*Could be the status code*/
+ if(sr.find("HTTP/") != string::npos){
+ h.assign(HTTP_RESPONSE_STR);
+ off = sr.find(" ");
+ v.assign(sr, off + 1, sr.find("\r\n") - (off + 1));
+ resp_code = atoi((v.substr(0, 3)).c_str());
+ }
+ }
+ response[h] = v;
+}
+
+size_t write_header(void *ptr, size_t size, size_t nmemb, void *ud){
+ test_cors_helper *h = static_cast<test_cors_helper *>(ud);
+ h->set_response((char *)ptr);
+ return size*nmemb;
+}
+
+size_t write_data(void *ptr, size_t size, size_t nmemb, void *ud){
+ test_cors_helper *h = static_cast<test_cors_helper *>(ud);
+ h->set_response_data((char *)ptr, size*nmemb);
+ return size*nmemb;
+}
+static inline void buf_to_hex(const unsigned char *buf, int len, char *str)
+{
+ int i;
+ str[0] = '\0';
+ for (i = 0; i < len; i++) {
+ sprintf(&str[i*2], "%02x", (int)buf[i]);
+ }
+}
+
+static void calc_hmac_sha1(const char *key, int key_len,
+ const char *msg, int msg_len, char *dest)
+/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
+{
+ ceph::crypto::HMACSHA1 hmac((const unsigned char *)key, key_len);
+ hmac.Update((const unsigned char *)msg, msg_len);
+ hmac.Final((unsigned char *)dest);
+
+ char hex_str[(CEPH_CRYPTO_HMACSHA1_DIGESTSIZE * 2) + 1];
+ buf_to_hex((unsigned char *)dest, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE, hex_str);
+}
+
+static int get_s3_auth(const string &method, string creds, const string &date, const string &res, string& out){
+ string aid, secret, auth_hdr;
+ size_t off = creds.find(":");
+ out = "";
+ if(off != string::npos){
+ aid.assign(creds, 0, off);
+ secret.assign(creds, off + 1, string::npos);
+
+ /*sprintf(auth_hdr, "%s\n\n\n%s\n%s", req_type, date, res);*/
+ char hmac_sha1[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
+ char b64[65]; /* 64 is really enough */
+ auth_hdr.append(method + string("\n\n\n") + date + string("\n") + res);
+ calc_hmac_sha1(secret.c_str(), secret.length(), auth_hdr.c_str(), auth_hdr.length(), hmac_sha1);
+ int ret = ceph_armor(b64, b64 + 64, hmac_sha1,
+ hmac_sha1 + CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+ if (ret < 0) {
+ cout << "ceph_armor failed\n";
+ return -1;
+ }
+ b64[ret] = 0;
+ out.append(aid + string(":") + b64);
+ }else return -1;
+ return 0;
+}
+
+void get_date(string& d){
+ struct timeval tv;
+ char date[64];
+ struct tm tm;
+ char *days[] = {(char *)"Sun", (char *)"Mon", (char *)"Tue",
+ (char *)"Wed", (char *)"Thu", (char *)"Fri",
+ (char *)"Sat"};
+ char *months[] = {(char *)"Jan", (char *)"Feb", (char *)"Mar",
+ (char *)"Apr", (char *)"May", (char *)"Jun",
+ (char *)"Jul",(char *) "Aug", (char *)"Sep",
+ (char *)"Oct", (char *)"Nov", (char *)"Dec"};
+ gettimeofday(&tv, NULL);
+ gmtime_r(&tv.tv_sec, &tm);
+ sprintf(date, "%s, %d %s %d %d:%d:%d GMT",
+ days[tm.tm_wday],
+ tm.tm_mday, months[tm.tm_mon],
+ tm.tm_year + 1900,
+ tm.tm_hour, tm.tm_min, 0 /*tm.tm_sec*/);
+ d = date;
+}
+
+int test_cors_helper::send_request(string method, string res,
+ size_t (*read_function)( void *,size_t,size_t,void *),
+ void *ud,
+ size_t length){
+ string url;
+ string auth, date;
+ url.append(string("http://") + host);
+ if(port.length() > 0)url.append(string(":") + port);
+ url.append(res);
+ curl_inst = curl_easy_init();
+ if(curl_inst){
+ curl_easy_setopt(curl_inst, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_inst, CURLOPT_CUSTOMREQUEST, method.c_str());
+ curl_easy_setopt(curl_inst, CURLOPT_VERBOSE, CURL_VERBOSE);
+ curl_easy_setopt(curl_inst, CURLOPT_HEADERFUNCTION, write_header);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEHEADER, (void *)this);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEFUNCTION, write_data);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEDATA, (void *)this);
+ if(read_function){
+ curl_easy_setopt(curl_inst, CURLOPT_READFUNCTION, read_function);
+ curl_easy_setopt(curl_inst, CURLOPT_READDATA, (void *)ud);
+ curl_easy_setopt(curl_inst, CURLOPT_UPLOAD, 1L);
+ curl_easy_setopt(curl_inst, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
+ }
+
+ get_date(date);
+ string http_date;
+ http_date.append(string("Date: ") + date);
+ if(kt == KEY_TYPE_S3){
+ string s3auth;
+ if(get_s3_auth(method, creds, date, res, s3auth) < 0)return -1;
+ auth.append(string("Authorization: AWS ") + s3auth);
+ } else if(kt == KEY_TYPE_SWIFT){
+ auth.append(string("X-Auth-Token: ") + creds);
+ } else {
+ cout << "Unknown state (" << kt << ")\n";
+ return -1;
+ }
+
+ struct curl_slist *slist = NULL;
+ slist = curl_slist_append(slist, auth.c_str());
+ slist = curl_slist_append(slist, http_date.c_str());
+ for(list<string>::iterator it = extra_hdrs.begin();
+ it != extra_hdrs.end(); ++it){
+ slist = curl_slist_append(slist, (*it).c_str());
+ }
+ if(read_function)
+ curl_slist_append(slist, "Expect:");
+ curl_easy_setopt(curl_inst, CURLOPT_HTTPHEADER, slist);
+
+ response.erase(response.begin(), response.end());
+ extra_hdrs.erase(extra_hdrs.begin(), extra_hdrs.end());
+ CURLcode res = curl_easy_perform(curl_inst);
+ if(res != CURLE_OK){
+ cout << "Curl perform failed for " << url << ", res: " <<
+ curl_easy_strerror(res) << "\n";
+ return -1;
+ }
+ curl_slist_free_all(slist);
+ }
+ curl_easy_cleanup(curl_inst);
+ return 0;
+}
+
+test_cors_helper *g_test;
+Finisher *finisher;
+
+static int create_bucket(void){
+ if(g_test->get_key_type() == KEY_TYPE_S3){
+ g_test->send_request(string("PUT"), string("/" S3_BUCKET_NAME));
+ if(g_test->get_resp_code() != 200U){
+ cout << "Error creating bucket, http code " << g_test->get_resp_code();
+ return -1;
+ }
+ }else if(g_test->get_key_type() == KEY_TYPE_SWIFT){
+ g_test->send_request(string("PUT"), string("/swift/v1/" SWIFT_BUCKET_NAME));
+ if(g_test->get_resp_code() != 201U){
+ cout << "Error creating bucket, http code " << g_test->get_resp_code();
+ return -1;
+ }
+ }else return -1;
+ return 0;
+}
+
+static int delete_bucket(void){
+ if(g_test->get_key_type() == KEY_TYPE_S3){
+ g_test->send_request(string("DELETE"), string("/" S3_BUCKET_NAME));
+ if(g_test->get_resp_code() != 204U){
+ cout << "Error deleting bucket, http code " << g_test->get_resp_code();
+ return -1;
+ }
+ }else if(g_test->get_key_type() == KEY_TYPE_SWIFT){
+ g_test->send_request(string("DELETE"), string("/swift/v1/" SWIFT_BUCKET_NAME));
+ if(g_test->get_resp_code() != 204U){
+ cout << "Error deleting bucket, http code " << g_test->get_resp_code();
+ return -1;
+ }
+ }else return -1;
+ return 0;
+}
+
+RGWCORSRule *xml_to_cors_rule(string s){
+ RGWCORSConfiguration_S3 *cors_config;
+ const DoutPrefix dp(g_ceph_context, 1, "test cors: ");
+ RGWCORSXMLParser_S3 parser(&dp, g_ceph_context);
+ const string *data = g_test->get_response_data();
+ if (!parser.init()) {
+ return NULL;
+ }
+ if (!parser.parse(data->c_str(), data->length(), 1)) {
+ return NULL;
+ }
+ cors_config = (RGWCORSConfiguration_S3 *)parser.find_first("CORSConfiguration");
+ if (!cors_config) {
+ return NULL;
+ }
+ return cors_config->host_name_rule(s.c_str());
+}
+
+size_t cors_read_xml(void *ptr, size_t s, size_t n, void *ud){
+ stringstream *ss = (stringstream *)ud;
+ size_t len = ss->str().length();
+ if(s*n < len){
+ cout << "Cannot copy xml data, as len is not enough\n";
+ return 0;
+ }
+ memcpy(ptr, (void *)ss->str().c_str(), len);
+ return len;
+}
+
+void send_cors(set<string> o, set<string> h,
+ list<string> e, uint8_t flags,
+ unsigned max_age){
+ if(g_test->get_key_type() == KEY_TYPE_S3){
+ RGWCORSRule rule(o, h, e, flags, max_age);
+ RGWCORSConfiguration config;
+ config.stack_rule(rule);
+ stringstream ss;
+ RGWCORSConfiguration_S3 *s3;
+ s3 = static_cast<RGWCORSConfiguration_S3 *>(&config);
+ s3->to_xml(ss);
+
+ g_test->send_request(string("PUT"), string("/" S3_BUCKET_NAME "?cors"), cors_read_xml,
+ (void *)&ss, ss.str().length());
+ }else if(g_test->get_key_type() == KEY_TYPE_SWIFT){
+ set<string>::iterator it;
+ string a_o;
+ for(it = o.begin(); it != o.end(); ++it){
+ if(a_o.length() > 0)a_o.append(" ");
+ a_o.append(*it);
+ }
+ g_test->set_extra_header(string("X-Container-Meta-Access-Control-Allow-Origin: ") + a_o);
+
+ if(!h.empty()){
+ string a_h;
+ for(it = h.begin(); it != h.end(); ++it){
+ if(a_h.length() > 0)a_h.append(" ");
+ a_h.append(*it);
+ }
+ g_test->set_extra_header(string("X-Container-Meta-Access-Control-Allow-Headers: ") + a_h);
+ }
+ if(!e.empty()){
+ string e_h;
+ for(list<string>::iterator lit = e.begin(); lit != e.end(); ++lit){
+ if(e_h.length() > 0)e_h.append(" ");
+ e_h.append(*lit);
+ }
+ g_test->set_extra_header(string("X-Container-Meta-Access-Control-Expose-Headers: ") + e_h);
+ }
+ if(max_age != CORS_MAX_AGE_INVALID){
+ char age[32];
+ sprintf(age, "%u", max_age);
+ g_test->set_extra_header(string("X-Container-Meta-Access-Control-Max-Age: ") + string(age));
+ }
+ //const char *data = "1";
+ stringstream ss;
+ ss << "1";
+ g_test->send_request(string("POST"), string("/swift/v1/" SWIFT_BUCKET_NAME), cors_read_xml,
+ (void *)&ss, 1);
+ }
+}
+
+TEST(TestCORS, getcors_firsttime){
+ if(g_test->get_key_type() == KEY_TYPE_SWIFT)return;
+ ASSERT_EQ(0, create_bucket());
+ g_test->send_request(string("GET"), string("/" S3_BUCKET_NAME "?cors"));
+ EXPECT_EQ(404U, g_test->get_resp_code());
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, putcors_firsttime){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "example.com");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ /*Now get the CORS and check if its fine*/
+ if(g_test->get_key_type() == KEY_TYPE_S3){
+ g_test->send_request(string("GET"), string("/" S3_BUCKET_NAME "?cors"));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ RGWCORSRule *r = xml_to_cors_rule(string("example.com"));
+ EXPECT_TRUE(r != NULL);
+ if(!r)return;
+
+ EXPECT_TRUE((r->get_allowed_methods() & (RGW_CORS_GET | RGW_CORS_PUT))
+ == (RGW_CORS_GET | RGW_CORS_PUT));
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, putcors_invalid_hostname){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "*.example.*");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ((400U), g_test->get_resp_code());
+ origins.erase(origins.begin(), origins.end());
+
+ if((g_test->get_key_type() != KEY_TYPE_SWIFT)){
+ origins.insert(origins.end(), "");
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ((400U), g_test->get_resp_code());
+ origins.erase(origins.begin(), origins.end());
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, putcors_invalid_headers){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "www.example.com");
+ h.insert(h.end(), "*-Header-*");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ((400U), g_test->get_resp_code());
+ h.erase(h.begin(), h.end());
+
+ if((g_test->get_key_type() != KEY_TYPE_SWIFT)){
+ h.insert(h.end(), "");
+ flags = RGW_CORS_GET | RGW_CORS_PUT;
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ((400U), g_test->get_resp_code());
+ h.erase(h.begin(), h.end());
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, optionscors_test_options_1){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "*.example.com");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: a.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->set_extra_header(string("Access-Control-Allow-Headers: SomeHeader"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("a.example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0U, s.length());
+ s = g_test->get_response(string("Access-Control-Max-Age"));
+ EXPECT_EQ(0U, s.length());
+ s = g_test->get_response(string("Access-Control-Expose-Headers"));
+ EXPECT_EQ(0U, s.length());
+ }
+
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, optionscors_test_options_2){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "*.example.com");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT | RGW_CORS_DELETE | RGW_CORS_HEAD;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: a.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: HEAD"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("a.example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("HEAD"));
+ }
+
+ g_test->set_extra_header(string("Origin: foo.bar.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: HEAD"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("foo.bar.example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("HEAD"));
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, optionscors_test_options_3){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "*");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT | RGW_CORS_DELETE | RGW_CORS_HEAD;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ /*Check for HEAD in Access-Control-Allow-Methods*/
+ g_test->set_extra_header(string("Origin: a.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: HEAD"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("a.example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("HEAD"));
+ }
+
+ /*Check for DELETE in Access-Control-Allow-Methods*/
+ g_test->set_extra_header(string("Origin: foo.bar"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: DELETE"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("foo.bar"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("DELETE"));
+ }
+
+ /*Check for PUT in Access-Control-Allow-Methods*/
+ g_test->set_extra_header(string("Origin: foo.bar"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: PUT"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("foo.bar"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("PUT"));
+ }
+
+ /*Check for POST in Access-Control-Allow-Methods*/
+ g_test->set_extra_header(string("Origin: foo.bar"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: POST"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("foo.bar"));
+ if(g_test->get_key_type() == KEY_TYPE_S3){
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0U, s.length());
+ }else{
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("POST"));
+ }
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, optionscors_test_options_4){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "example.com");
+ h.insert(h.end(), "Header1");
+ h.insert(h.end(), "Header2");
+ h.insert(h.end(), "*");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0U, s.length());
+ }
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->set_extra_header(string("Access-Control-Allow-Headers: Header1"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0, s.compare("Header1"));
+ }
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->set_extra_header(string("Access-Control-Allow-Headers: Header2"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0, s.compare("Header2"));
+ }
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->set_extra_header(string("Access-Control-Allow-Headers: Header2, Header1"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0, s.compare("Header2,Header1"));
+ }
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->set_extra_header(string("Access-Control-Allow-Headers: Header1, Header2"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0, s.compare("Header1,Header2"));
+ }
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->set_extra_header(string("Access-Control-Allow-Headers: Header1, Header2, Header3"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0, s.compare("Header1,Header2,Header3"));
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, optionscors_test_options_5){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "example.com");
+ e.insert(e.end(), "Expose1");
+ e.insert(e.end(), "Expose2");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Expose-Headers"));
+ EXPECT_EQ(0, s.compare("Expose1,Expose2"));
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, optionscors_test_options_6){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+ unsigned err = (g_test->get_key_type() == KEY_TYPE_SWIFT)?401U:403U;
+
+ origins.insert(origins.end(), "http://www.example.com");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(err, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: http://example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(err, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: www.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(err, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: http://www.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ origins.erase(origins.begin(), origins.end());
+ origins.insert(origins.end(), "*.example.com");
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: .example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: http://example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(err, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: www.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: http://www.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: https://www.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ origins.erase(origins.begin(), origins.end());
+ origins.insert(origins.end(), "https://example*.com");
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: https://example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: http://example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(err, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: www.example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(err, g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: https://example.a.b.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, optionscors_test_options_7){
+ ASSERT_EQ(0, create_bucket());
+ set<string> origins, h;
+ list<string> e;
+
+ origins.insert(origins.end(), "example.com");
+ h.insert(h.end(), "Header*");
+ h.insert(h.end(), "Hdr-*-Length");
+ h.insert(h.end(), "*-Length");
+ h.insert(h.end(), "foo*foo");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->set_extra_header(string("Origin: example.com"));
+ g_test->set_extra_header(string("Access-Control-Request-Method: GET"));
+ g_test->set_extra_header(string("Access-Control-Allow-Headers: Header1, Header2, Header3, "
+ "Hdr--Length, Hdr-1-Length, Header-Length, Content-Length, foofoofoo"));
+ g_test->send_request(string("OPTIONS"), BUCKET_URL);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ if(g_test->get_resp_code() == 200){
+ string s = g_test->get_response(string("Access-Control-Allow-Origin"));
+ EXPECT_EQ(0, s.compare("example.com"));
+ s = g_test->get_response(string("Access-Control-Allow-Methods"));
+ EXPECT_EQ(0, s.compare("GET"));
+ s = g_test->get_response(string("Access-Control-Allow-Headers"));
+ EXPECT_EQ(0, s.compare("Header1,Header2,Header3,"
+ "Hdr--Length,Hdr-1-Length,Header-Length,Content-Length,foofoofoo"));
+ }
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, deletecors_firsttime){
+ if(g_test->get_key_type() == KEY_TYPE_SWIFT)return;
+ ASSERT_EQ(0, create_bucket());
+ g_test->send_request("DELETE", "/" S3_BUCKET_NAME "?cors");
+ EXPECT_EQ(204U, g_test->get_resp_code());
+ ASSERT_EQ(0, delete_bucket());
+}
+
+TEST(TestCORS, deletecors_test){
+ set<string> origins, h;
+ list<string> e;
+ if(g_test->get_key_type() == KEY_TYPE_SWIFT)return;
+ ASSERT_EQ(0, create_bucket());
+ origins.insert(origins.end(), "example.com");
+ uint8_t flags = RGW_CORS_GET | RGW_CORS_PUT;
+
+ send_cors(origins, h, e, flags, CORS_MAX_AGE_INVALID);
+ EXPECT_EQ(((g_test->get_key_type() == KEY_TYPE_SWIFT)?202U:200U), g_test->get_resp_code());
+
+ g_test->send_request("GET", "/" S3_BUCKET_NAME "?cors");
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ g_test->send_request("DELETE", "/" S3_BUCKET_NAME "?cors");
+ EXPECT_EQ(204U, g_test->get_resp_code());
+ g_test->send_request("GET", "/" S3_BUCKET_NAME "?cors");
+ EXPECT_EQ(404U, g_test->get_resp_code());
+ ASSERT_EQ(0, delete_bucket());
+}
+
+int main(int argc, char *argv[]){
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_test = new test_cors_helper();
+ finisher = new Finisher(g_ceph_context);
+#ifdef GTEST
+ ::testing::InitGoogleTest(&argc, argv);
+#endif
+ finisher->start();
+
+ if(g_test->extract_input((unsigned)argc, argv) < 0){
+ print_usage(argv[0]);
+ return -1;
+ }
+#ifdef GTEST
+ int r = RUN_ALL_TESTS();
+ if (r >= 0) {
+ cout << "There are failures in the test case\n";
+ return -1;
+ }
+#endif
+ finisher->stop();
+ delete g_test;
+ delete finisher;
+ return 0;
+}
+
diff --git a/src/test/test_crush_bucket.sh b/src/test/test_crush_bucket.sh
new file mode 100755
index 000000000..2a297f9f3
--- /dev/null
+++ b/src/test/test_crush_bucket.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+
+#Generic test_crush_bucket test
+#
+
+# Includes
+source $(dirname $0)/detect-build-env-vars.sh
+source ../qa/standalone/ceph-helpers.sh
+function run() {
+ local dir=$1
+ shift
+
+ export CEPH_MON="127.0.0.1:17119" # git grep '\<17119\>' : there must be only one
+ export CEPH_ARGS
+ CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
+ CEPH_ARGS+="--mon-host=$CEPH_MON "
+
+ local funcs=${@:-$(set | ${SED} -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')}
+ for func in $funcs ; do
+ $func $dir || return 1
+ done
+}
+
+function TEST_crush_bucket() {
+ local dir=$1
+ setup $dir || return 1
+ run_mon $dir a || return 1
+ run_osd $dir 0 || return 1
+ run_osd $dir 1 || return 1
+ run_osd $dir 2 || return 1
+
+
+ ceph osd getcrushmap -o "$dir/map1" || return 1
+ crushtool -d "$dir/map1" -o "$dir/map1.txt"|| return 1
+ local var=`ceph osd crush dump|grep -w id|grep '-'|grep -Eo '[0-9]+'|sort|uniq|${SED} -n '$p'`
+ local id=`expr $var + 1`
+ local item=`${SED} -n '/^root/,/}/p' $dir/map1.txt|grep 'item'|head -1`
+ local weight=`${SED} -n '/^root/,/}/p' $dir/map1.txt|grep 'item'|head -1|awk '{print $4}'`
+ local bucket="host test {\n id -$id\n # weight $weight\n alg straw \n hash 0 # rjenkins1 \n $item\n}\n"
+ ${SED} -i "/# buckets/a\ $bucket" "$dir/map1.txt"
+ crushtool -c "$dir/map1.txt" -o "$dir/map1.bin" 2>"$dir/rev"
+ local result=$(cat "$dir/rev")
+ if [ "$result" != "" ];
+ then
+ return 1
+ fi
+
+}
+
+main testcrushbucket
+
diff --git a/src/test/test_csyn.sh b/src/test/test_csyn.sh
new file mode 100644
index 000000000..04efcb2ac
--- /dev/null
+++ b/src/test/test_csyn.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -x
+
+#
+# Runs the synthetic client
+#
+
+# Includes
+source "`dirname $0`/test_common.sh"
+
+# Functions
+setup() {
+ export CEPH_NUM_OSD=$1
+
+ # Start ceph
+ ./stop.sh
+
+ # set recovery start to a really long time to ensure that we don't start recovery
+ ./vstart.sh -d -n -o 'osd recovery delay start = 10000
+osd max scrubs = 0' || die "vstart failed"
+}
+
+csyn_simple1_impl() {
+ ./ceph-syn -c ./ceph.conf --syn writefile 100 1000 --syn writefile 100 1000 || die "csyn failed"
+}
+
+csyn_simple1() {
+ setup 2
+ csyn_simple1_impl
+}
+
+run() {
+ csyn_simple1 || die "test failed"
+}
+
+$@
diff --git a/src/test/test_denc.cc b/src/test/test_denc.cc
new file mode 100644
index 000000000..0aae8dbbc
--- /dev/null
+++ b/src/test/test_denc.cc
@@ -0,0 +1,743 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2016 Red Hat
+ *
+ * Author: Sage Weil <sage@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+#include <numeric>
+
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+#include "include/denc.h"
+
+using namespace std;
+
+// test helpers
+
+template<typename T>
+void test_encode_decode(T v) {
+ bufferlist bl;
+ encode(v, bl);
+ auto p = bl.cbegin();
+ T out;
+ decode(out, p);
+ ASSERT_EQ(v, out);
+}
+
+template<typename T>
+void test_denc(T v) {
+ // estimate
+ size_t s = 0;
+ denc(v, s);
+ ASSERT_NE(s, 0u);
+
+ // encode
+ bufferlist bl;
+ {
+ auto a = bl.get_contiguous_appender(s);
+ denc(v, a);
+ }
+ ASSERT_LE(bl.length(), s);
+
+ // decode
+ bl.rebuild();
+ T out;
+ auto bpi = bl.front().begin();
+ denc(out, bpi);
+ ASSERT_EQ(v, out);
+ ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
+
+ // test glue
+ test_encode_decode(v);
+}
+
+template<typename T>
+void test_encode_decode_featured(T v) {
+ bufferlist bl;
+ encode(v, bl, 123);
+ auto p = bl.cbegin();
+ T out;
+ decode(out, p);
+ ASSERT_EQ(v, out);
+}
+
+template<typename T>
+void test_denc_featured(T v) {
+ // estimate
+ size_t s = 0;
+ denc(v, s, 0);
+ ASSERT_GT(s, 0u);
+
+ // encode
+ bufferlist bl;
+ {
+ auto a = bl.get_contiguous_appender(s);
+ denc(v, a, 1);
+ }
+ ASSERT_LE(bl.length(), s);
+
+ // decode
+ bl.rebuild();
+ T out;
+ auto bpi = bl.front().begin();
+ denc(out, bpi, 1);
+ ASSERT_EQ(v, out);
+ ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
+
+ // test glue
+ test_encode_decode_featured(v);
+}
+
+
+// hooks to count bound calls
+
+struct counts_t {
+ int num_bound_encode = 0;
+ int num_encode = 0;
+ int num_decode = 0;
+ void reset() {
+ num_bound_encode = 0;
+ num_encode = 0;
+ num_decode = 0;
+ }
+} counts;
+
+struct denc_counter_t {
+ void bound_encode(size_t& p) const {
+ ++counts.num_bound_encode;
+ ++p; // denc.h does not like 0-length objects
+ }
+ void encode(buffer::list::contiguous_appender& p) const {
+ p.append("a", 1);
+ ++counts.num_encode;
+ }
+ void decode(buffer::ptr::const_iterator &p) {
+ p += 1;
+ ++counts.num_decode;
+ }
+};
+WRITE_CLASS_DENC(denc_counter_t)
+
+struct denc_counter_bounded_t {
+ void bound_encode(size_t& p) const {
+ ++counts.num_bound_encode;
+ ++p; // denc.h does not like 0-length objects
+ }
+ void encode(buffer::list::contiguous_appender& p) const {
+ p.append("a", 1);
+ ++counts.num_encode;
+ }
+ void decode(buffer::ptr::const_iterator &p) {
+ p += 1;
+ ++counts.num_decode;
+ }
+};
+WRITE_CLASS_DENC_BOUNDED(denc_counter_bounded_t)
+
+TEST(denc, denc_counter)
+{
+ denc_counter_t single, single2;
+ {
+ bufferlist bl;
+ encode(single, bl);
+ decode(single2, bl);
+ }
+ ASSERT_EQ(counts.num_bound_encode, 1);
+ ASSERT_EQ(counts.num_encode, 1);
+ ASSERT_EQ(counts.num_decode, 1);
+ counts.reset();
+}
+
+TEST(denc, simple)
+{
+ test_denc((uint8_t)4);
+ test_denc((int8_t)-5);
+ test_denc((uint16_t)6);
+ test_denc((int16_t)-7);
+ test_denc((uint32_t)8);
+ test_denc((int32_t)-9);
+ test_denc((uint64_t)10);
+ test_denc((int64_t)-11);
+}
+
+TEST(denc, string)
+{
+ string a, b("hi"), c("multi\nline\n");
+ test_denc(a);
+ test_denc(b);
+ test_denc(c);
+}
+
+struct legacy_t {
+ int32_t a = 1;
+ void encode(bufferlist& bl) const {
+ using ceph::encode;
+ encode(a, bl);
+ }
+ void decode(bufferlist::const_iterator& p) {
+ using ceph::decode;
+ decode(a, p);
+ }
+ legacy_t() {}
+ explicit legacy_t(int32_t i) : a(i) {}
+ friend bool operator<(const legacy_t& l, const legacy_t& r) {
+ return l.a < r.a;
+ }
+ friend bool operator==(const legacy_t& l, const legacy_t& r) {
+ return l.a == r.a;
+ }
+};
+WRITE_CLASS_ENCODER(legacy_t)
+
+template<template<class> class C>
+void test_common_veclist(const char* c) {
+ {
+ cout << c << "<std::string>" << std::endl;
+ C<std::string> s;
+ s.push_back("foo");
+ s.push_back("bar");
+ s.push_back("baz");
+ counts.reset();
+ test_denc(s);
+ }
+ {
+ cout << c << "<int32_t>" << std::endl;
+ C<int32_t> s;
+ s.push_back(1);
+ s.push_back(2);
+ s.push_back(3);
+ test_denc(s);
+ }
+ {
+ cout << c << "<legacy_t>" << std::endl;
+ C<legacy_t> s;
+ s.push_back(legacy_t(1));
+ s.push_back(legacy_t(2));
+ test_encode_decode(s);
+ }
+}
+
+// We only care about specializing the type, all other template
+// parameters should have the default values. (Like first-class
+// functions, first-class templates do not bring their defaults.)
+
+template<typename T>
+using default_vector = std::vector<T>;
+
+TEST(denc, vector)
+{
+ test_common_veclist<default_vector>("std::vector");
+ {
+ counts.reset();
+ vector<denc_counter_t> v, v2;
+ v.resize(100);
+ {
+ bufferlist bl;
+ encode(v, bl);
+ decode(v2, bl);
+ }
+ ASSERT_EQ(counts.num_bound_encode, 100);
+ ASSERT_EQ(counts.num_encode, 100);
+ ASSERT_EQ(counts.num_decode, 100);
+ }
+ {
+ counts.reset();
+ vector<denc_counter_bounded_t> v, v2;
+ v.resize(100);
+ {
+ bufferlist bl;
+ encode(v, bl);
+ decode(v2, bl);
+ }
+ ASSERT_EQ(counts.num_bound_encode, 1);
+ ASSERT_EQ(counts.num_encode, 100);
+ ASSERT_EQ(counts.num_decode, 100);
+ }
+}
+
+template<typename T>
+using default_list = std::list<T>;
+
+TEST(denc, list)
+{
+ test_common_veclist<default_list>("std::list");
+ {
+ counts.reset();
+ list<denc_counter_bounded_t> l, l2;
+ for (unsigned i=0; i<100; ++i) {
+ l.emplace_back(denc_counter_bounded_t());
+ }
+ {
+ bufferlist bl;
+ encode(l, bl);
+ decode(l2, bl);
+ }
+ ASSERT_EQ(counts.num_bound_encode, 1);
+ ASSERT_EQ(counts.num_encode, 100);
+ ASSERT_EQ(counts.num_decode, 100);
+ }
+}
+
+template<template<class> class C>
+void test_setlike(const char* c) {
+ {
+ cout << c << "<std::string>" << std::endl;
+ C<std::string> s;
+ s.insert("foo");
+ s.insert("bar");
+ s.insert("baz");
+ test_denc(s);
+ }
+ {
+ cout << c << "<int32_t>" << std::endl;
+ C<int32_t> s;
+ s.insert(1);
+ s.insert(2);
+ s.insert(3);
+ test_denc(s);
+ }
+ {
+ cout << c << "<legacy_t>" << std::endl;
+ C<legacy_t> s;
+ s.insert(legacy_t(1));
+ s.insert(legacy_t(2));
+ test_encode_decode(s);
+ }
+}
+
+template<typename T>
+using default_set = std::set<T>;
+
+TEST(denc, set)
+{
+ test_setlike<default_set>("std::set");
+}
+
+template<typename T>
+using default_flat_set= boost::container::flat_set<T>;
+
+TEST(denc, flat_set)
+{
+ test_setlike<default_flat_set>("std::set");
+}
+
+struct foo_t {
+ int32_t a = 0;
+ uint64_t b = 123;
+
+ DENC(foo_t, v, p) {
+ DENC_START(1, 1, p);
+ ::denc(v.a, p);
+ ::denc(v.b, p);
+ DENC_FINISH(p);
+ }
+
+ friend bool operator==(const foo_t& l, const foo_t& r) {
+ return l.a == r.a && l.b == r.b;
+ }
+};
+WRITE_CLASS_DENC_BOUNDED(foo_t)
+
+struct foo2_t {
+ int32_t c = 0;
+ uint64_t d = 123;
+
+ DENC(foo2_t, v, p) {
+ DENC_START(1, 1, p);
+ ::denc(v.c, p);
+ ::denc(v.d, p);
+ DENC_FINISH(p);
+ }
+
+ friend bool operator==(const foo2_t& l, const foo2_t& r) {
+ return l.c == r.c && l.d == r.d;
+ }
+};
+WRITE_CLASS_DENC_BOUNDED(foo2_t)
+
+
+struct bar_t {
+ int32_t a = 0;
+ uint64_t b = 123;
+
+ DENC_FEATURED(bar_t, v, p, f) {
+ ::denc(v.a, p, f);
+ ::denc(v.b, p, f);
+ }
+
+ friend bool operator==(const bar_t& l, const bar_t& r) {
+ return l.a == r.a && l.b == r.b;
+ }
+};
+WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t)
+
+TEST(denc, foo)
+{
+ foo_t a;
+ test_denc(a);
+ bufferlist bl;
+ encode(a, bl);
+ bl.hexdump(cout);
+}
+
+TEST(denc, bar)
+{
+ bar_t a;
+ test_denc_featured(a);
+}
+
+
+
+TEST(denc, pair)
+{
+ pair<int32_t,std::string> p;
+ bufferlist bl;
+ {
+ auto a = bl.get_contiguous_appender(1000);
+ denc(p, a);
+ encode(p, bl);
+ }
+
+ pair<int32_t,legacy_t> lp;
+ encode(lp, bl);
+}
+
+template<template<class, class> class C>
+void test_common_maplike(const char* c) {
+ {
+ cout << c << "<std::string, foo_t>" << std::endl;
+ C<string, foo_t> s;
+ s["foo"] = foo_t();
+ s["bar"] = foo_t();
+ s["baz"] = foo_t();
+ test_denc(s);
+ }
+ {
+ cout << c << "<std::string, bar_t>" << std::endl;
+ C<string, bar_t> s;
+ s["foo"] = bar_t();
+ s["bar"] = bar_t();
+ s["baz"] = bar_t();
+ test_denc_featured(s);
+ }
+ {
+ cout << c << "<std::string, legacy_t>" << std::endl;
+ C<std::string, legacy_t> s;
+ s["foo"] = legacy_t(1);
+ s["bar"] = legacy_t(2);
+ test_encode_decode(s);
+ }
+}
+
+template<typename U, typename V>
+using default_map = std::map<U, V>;
+
+TEST(denc, map)
+{
+ test_common_maplike<default_map>("std::map");
+}
+
+template<typename U, typename V>
+using default_flat_map = boost::container::flat_map<U, V>;
+
+TEST(denc, flat_map)
+{
+ test_common_maplike<default_flat_map>("boost::container::flat_map");
+}
+
+TEST(denc, bufferptr_shallow_and_deep) {
+ // shallow encode
+ int32_t i = 1;
+ bufferptr p1("foo", 3);
+ bufferlist bl;
+ {
+ auto a = bl.get_contiguous_appender(100);
+ denc(i, a);
+ denc(p1, a);
+ denc(i, a);
+ }
+ cout << "bl is " << bl << std::endl;
+ bl.hexdump(cout);
+ ASSERT_EQ(3u, bl.get_num_buffers());
+
+ bufferlist bl2 = bl;
+ bl.rebuild();
+ bl2.rebuild();
+
+ // shallow decode
+ {
+ cout << "bl is " << bl << std::endl;
+ bl.hexdump(cout);
+ auto p = bl.front().begin();
+ bufferptr op;
+ int32_t i;
+ denc(i, p);
+ denc(op, p);
+ denc(i, p);
+ ASSERT_EQ(3u, op.length());
+ ASSERT_EQ('f', op[0]);
+ memset(bl.c_str(), 0, bl.length());
+ ASSERT_EQ(0, op[0]);
+ }
+
+ // deep decode
+ {
+ cout << "bl is " << bl2 << std::endl;
+ bl2.hexdump(cout);
+ auto p = bl2.front().begin_deep();
+ bufferptr op;
+ int32_t i;
+ denc(i, p);
+ denc(op, p);
+ denc(i, p);
+ ASSERT_EQ('f', op[0]);
+ memset(bl2.c_str(), 1, bl2.length());
+ ASSERT_EQ('f', op[0]);
+ }
+}
+
+TEST(denc, array)
+{
+ {
+ cout << "std::array<std::string, 3>" << std::endl;
+ std::array<std::string, 3> s = { "foo", "bar", "baz" };
+ counts.reset();
+ test_denc(s);
+ }
+ {
+ cout << "std::array<uint32_t, 3>" << std::endl;
+ std::array<uint32_t, 3> s = { 1UL, 2UL, 3UL };
+ test_denc(s);
+ }
+}
+
+TEST(denc, tuple)
+{
+ {
+ cout << "std::tuple<uint64_t, uint32_t>" << std::endl;
+ std::tuple<uint64_t, uint32_t> s(100ULL, 97UL);
+ counts.reset();
+ test_denc(s);
+ }
+ {
+ cout << "std::tuple<std::string, uint3_t>" << std::endl;
+ std::tuple<std::string, uint32_t> s("foo", 97);
+ test_denc(s);
+ }
+ {
+ cout << "std::tuple<std::string, std::set<uint32_t>>" << std::endl;
+ std::tuple<std::string, std::set<uint32_t>> s(
+ "bar", std::set<uint32_t>{uint32_t(1), uint32_t(2), uint32_t(3)});
+ test_denc(s);
+ }
+}
+
+TEST(denc, optional)
+{
+ {
+ cout << "boost::optional<uint64_t>" << std::endl;
+ boost::optional<uint64_t> s = 97, t = boost::none;
+ counts.reset();
+ test_denc(s);
+ test_denc(t);
+ }
+ {
+ cout << "boost::optional<std::string>" << std::endl;
+ boost::optional<std::string> s = std::string("Meow"), t = boost::none;
+ counts.reset();
+ test_denc(s);
+ test_denc(t);
+ }
+ {
+ size_t s = 0;
+ denc(boost::none, s);
+ ASSERT_NE(s, 0u);
+
+ // encode
+ bufferlist bl;
+ {
+ auto a = bl.get_contiguous_appender(s);
+ denc(boost::none, a);
+ }
+ ASSERT_LE(bl.length(), s);
+
+ bl.rebuild();
+ boost::optional<uint32_t> out = 5;
+ auto bpi = bl.front().begin();
+ denc(out, bpi);
+ ASSERT_FALSE(!!out);
+ ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
+ }
+}
+
+TEST(denc, stdoptional)
+{
+ {
+ cout << "std::optional<uint64_t>" << std::endl;
+ std::optional<uint64_t> s = 97, t = std::nullopt;
+ counts.reset();
+ test_denc(s);
+ test_denc(t);
+ }
+ {
+ cout << "std::optional<std::string>" << std::endl;
+ std::optional<std::string> s = std::string("Meow"), t = std::nullopt;
+ counts.reset();
+ test_denc(s);
+ test_denc(t);
+ }
+ {
+ size_t s = 0;
+ denc(std::nullopt, s);
+ ASSERT_NE(s, 0u);
+
+ // encode
+ bufferlist bl;
+ {
+ auto a = bl.get_contiguous_appender(s);
+ denc(std::nullopt, a);
+ }
+ ASSERT_LE(bl.length(), s);
+
+ bl.rebuild();
+ std::optional<uint32_t> out = 5;
+ auto bpi = bl.front().begin();
+ denc(out, bpi);
+ ASSERT_FALSE(!!out);
+ ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
+ }
+}
+
+// unlike legacy_t, Legacy supports denc() also.
+struct Legacy {
+ static unsigned n_denc;
+ static unsigned n_decode;
+ uint8_t value = 0;
+ DENC(Legacy, v, p) {
+ n_denc++;
+ denc(v.value, p);
+ }
+ void decode(buffer::list::const_iterator& p) {
+ n_decode++;
+ using ceph::decode;
+ decode(value, p);
+ }
+ static void reset() {
+ n_denc = n_decode = 0;
+ }
+ static bufferlist encode_n(unsigned n, const vector<unsigned>& segments);
+};
+WRITE_CLASS_DENC(Legacy)
+unsigned Legacy::n_denc = 0;
+unsigned Legacy::n_decode = 0;
+
+bufferlist Legacy::encode_n(unsigned n, const vector<unsigned>& segments) {
+ vector<Legacy> v;
+ for (unsigned i = 0; i < n; i++) {
+ v.push_back(Legacy());
+ }
+ bufferlist bl(n * sizeof(uint8_t));
+ using ceph::encode;
+ encode(v, bl);
+ bufferlist segmented;
+ auto p = bl.begin();
+
+ auto sum = std::accumulate(segments.begin(), segments.end(), 0u);
+ ceph_assert(sum != 0u);
+ for (auto i : segments) {
+ buffer::ptr seg;
+ p.copy_deep(bl.length() * i / sum, seg);
+ segmented.push_back(seg);
+ }
+ p.copy_all(segmented);
+ return segmented;
+}
+
+TEST(denc, no_copy_if_segmented_and_lengthy)
+{
+ static_assert(_denc::has_legacy_denc<Legacy>::value,
+ "Legacy do have legacy denc");
+ {
+ // use denc() which shallow_copy() if the buffer is small
+ constexpr unsigned N_COPIES = 42;
+ const vector<unsigned> segs{50, 50}; // half-half
+ bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
+ ASSERT_GT(segmented.get_num_buffers(), 1u);
+ ASSERT_LT(segmented.length(), CEPH_PAGE_SIZE);
+ auto p = segmented.cbegin();
+ vector<Legacy> v;
+ // denc() is shared by encode() and decode(), so reset() right before
+ // decode()
+ Legacy::reset();
+ decode(v, p);
+ ASSERT_EQ(N_COPIES, v.size());
+ ASSERT_EQ(N_COPIES, Legacy::n_denc);
+ ASSERT_EQ(0u, Legacy::n_decode);
+ }
+ {
+ // use denc() which shallow_copy() if the buffer is not segmented and large
+ const unsigned N_COPIES = CEPH_PAGE_SIZE * 2;
+ const vector<unsigned> segs{100};
+ bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
+ ASSERT_EQ(segmented.get_num_buffers(), 1u);
+ ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
+ auto p = segmented.cbegin();
+ vector<Legacy> v;
+ Legacy::reset();
+ decode(v, p);
+ ASSERT_EQ(N_COPIES, v.size());
+ ASSERT_EQ(N_COPIES, Legacy::n_denc);
+ ASSERT_EQ(0u, Legacy::n_decode);
+ }
+ {
+ // use denc() which shallow_copy() if the buffer is segmented and large,
+ // but the total size of the chunks to be decoded is smallish.
+ bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
+ bufferlist small_bl = Legacy::encode_n(100, {50, 50});
+ bufferlist segmented;
+ segmented.append(large_bl);
+ segmented.append(small_bl);
+ ASSERT_GT(segmented.get_num_buffers(), 1u);
+ ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
+ auto p = segmented.cbegin();
+ p += large_bl.length();
+ ASSERT_LT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
+ vector<Legacy> v;
+ Legacy::reset();
+ decode(v, p);
+ ASSERT_EQ(Legacy::n_denc, 100u);
+ ASSERT_EQ(0u, Legacy::n_decode);
+ }
+ {
+ // use decode() which avoids deep copy if the buffer is segmented and large
+ bufferlist small_bl = Legacy::encode_n(100, {50, 50});
+ bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
+ bufferlist segmented;
+ segmented.append(small_bl);
+ segmented.append(large_bl);
+ ASSERT_GT(segmented.get_num_buffers(), 1u);
+ ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
+ auto p = segmented.cbegin();
+ p += small_bl.length();
+ ASSERT_GT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
+ vector<Legacy> v;
+ Legacy::reset();
+ decode(v, p);
+ ASSERT_EQ(0u, Legacy::n_denc);
+ ASSERT_EQ(CEPH_PAGE_SIZE * 2, Legacy::n_decode);
+ }
+}
diff --git a/src/test/test_features.cc b/src/test/test_features.cc
new file mode 100644
index 000000000..1ae758bfb
--- /dev/null
+++ b/src/test/test_features.cc
@@ -0,0 +1,47 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <stdio.h>
+
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_releases.h"
+#include "common/ceph_strings.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+#include "include/ceph_features.h"
+
+using namespace std;
+
+TEST(features, release_features)
+{
+ for (int r = 1; r < CEPH_RELEASE_MAX; ++r) {
+ const char *name = ceph_release_name(r);
+ ASSERT_NE(string("unknown"), name);
+ ASSERT_EQ(ceph_release_t{static_cast<uint8_t>(r)},
+ ceph_release_from_name(name));
+ uint64_t features = ceph_release_features(r);
+ int rr = ceph_release_from_features(features);
+ cout << r << " " << name << " features 0x" << std::hex << features
+ << std::dec << " looks like " << ceph_release_name(rr) << std::endl;
+ EXPECT_LE(rr, r);
+ }
+}
+
+TEST(features, release_from_features) {
+ ASSERT_EQ(CEPH_RELEASE_JEWEL, ceph_release_from_features(575862587619852283));
+ ASSERT_EQ(CEPH_RELEASE_LUMINOUS,
+ ceph_release_from_features(1152323339925389307));
+}
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/test_get_blkdev_props.cc b/src/test/test_get_blkdev_props.cc
new file mode 100644
index 000000000..6acdddf96
--- /dev/null
+++ b/src/test/test_get_blkdev_props.cc
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "include/uuid.h"
+#include "common/blkdev.h"
+
+#define BUFSIZE 80
+
+int main(int argc, char **argv)
+{
+ int fd, ret;
+ int64_t size;
+ bool discard_support;
+ bool rotational;
+ char dev[BUFSIZE];
+ char model[BUFSIZE];
+ char serial[BUFSIZE];
+ char wholedisk[BUFSIZE];
+ char partition[BUFSIZE];
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <blkdev>\n", argv[0]);
+ return -1;
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return -1;
+ }
+
+ BlkDev blkdev(fd);
+
+ if ((ret = blkdev.get_size(&size)) < 0) {
+ fprintf(stderr, "get_size: %s\n", strerror(-ret));
+ return -1;
+ }
+
+ discard_support = blkdev.support_discard();
+
+ rotational = blkdev.is_rotational();
+
+ if ((ret = blkdev.dev(dev, BUFSIZE)) < 0) {
+ fprintf(stderr, "dev: %s\n", strerror(-ret));
+ return -1;
+ }
+
+ if ((ret = blkdev.partition(partition, BUFSIZE)) < 0) {
+ fprintf(stderr, "partition: %s\n", strerror(-ret));
+ return -1;
+ }
+
+ if ((ret = blkdev.wholedisk(wholedisk, BUFSIZE)) < 0) {
+ fprintf(stderr, "wholedisk: %s\n", strerror(-ret));
+ return -1;
+ }
+
+ ret = blkdev.model(model, BUFSIZE);
+ if (ret == -ENOENT) {
+ snprintf(model, BUFSIZE, "unknown");
+ } else if (ret < 0) {
+ fprintf(stderr, "model: %s\n", strerror(-ret));
+ return -1;
+ }
+
+ ret = blkdev.serial(serial, BUFSIZE);
+ if (ret == -ENOENT) {
+ snprintf(serial, BUFSIZE, "unknown");
+ } else if (ret < 0) {
+ fprintf(stderr, "serial: %s\n", strerror(-ret));
+ return -1;
+ }
+
+ fprintf(stdout, "Size:\t\t%" PRId64 "\n", size);
+ fprintf(stdout, "Discard:\t%s\n",
+ discard_support ? "supported" : "not supported");
+ fprintf(stdout, "Rotational:\t%s\n", rotational ? "yes" : "no");
+ fprintf(stdout, "Dev:\t\t%s\n", dev);
+ fprintf(stdout, "Whole disk:\t%s\n", wholedisk);
+ fprintf(stdout, "Partition:\t%s\n", partition);
+ fprintf(stdout, "Model:\t\t%s\n", model);
+ fprintf(stdout, "Serial:\t\t%s\n", serial);
+
+ return 0;
+}
diff --git a/src/test/test_intarith.cc b/src/test/test_intarith.cc
new file mode 100644
index 000000000..4457b9399
--- /dev/null
+++ b/src/test/test_intarith.cc
@@ -0,0 +1,40 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <climits>
+#include <gtest/gtest.h>
+#include "include/intarith.h"
+
+TEST(intarith, cbits) {
+ ASSERT_EQ(0u, cbits(0));
+ ASSERT_EQ(1u, cbits(1));
+ ASSERT_EQ(2u, cbits(2));
+ ASSERT_EQ(2u, cbits(3));
+ ASSERT_EQ(3u, cbits(4));
+ ASSERT_EQ(0u, cbits(0));
+ ASSERT_EQ(1u, cbits(1));
+ ASSERT_EQ(2u, cbits(2));
+ ASSERT_EQ(2u, cbits(3));
+ ASSERT_EQ(3u, cbits(4));
+ ASSERT_EQ(9u, cbits(0x100));
+ ASSERT_EQ(32u, cbits(0xffffffff));
+ ASSERT_EQ(32u, cbits(0xffffffff));
+ ASSERT_EQ(32u, cbits(0xffffffff));
+ ASSERT_EQ(64u, cbits(0xffffffffffffffff));
+}
+
+TEST(intarith, p2family) {
+ ASSERT_EQ(1024, p2align(1200, 1024));
+ ASSERT_EQ(1024, p2align(1024, 1024));
+ ASSERT_EQ(0x1200, p2align(0x1234, 0x100));
+ ASSERT_EQ(0x5600, p2align(0x5600, 0x100));
+
+ ASSERT_EQ(0x34, p2phase(0x1234, 0x100));
+ ASSERT_EQ(0x00, p2phase(0x5600, 0x100));
+
+ ASSERT_EQ(0xcc, p2nphase(0x1234, 0x100));
+ ASSERT_EQ(0x00, p2nphase(0x5600, 0x100));
+
+ ASSERT_EQ(0x1300, p2roundup(0x1234, 0x100));
+ ASSERT_EQ(0x5600, p2roundup(0x5600, 0x100));
+}
diff --git a/src/test/test_ipaddr.cc b/src/test/test_ipaddr.cc
new file mode 100644
index 000000000..bc8dbef70
--- /dev/null
+++ b/src/test/test_ipaddr.cc
@@ -0,0 +1,997 @@
+#include "include/ipaddr.h"
+#include "common/pick_address.h"
+#include "gtest/gtest.h"
+#include "include/stringify.h"
+#include "common/ceph_context.h"
+
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+
+#if defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <net/if.h>
+#endif
+
+using namespace std;
+
+static void ipv4(struct sockaddr_in *addr, const char *s) {
+ int err;
+
+ addr->sin_family = AF_INET;
+ err = inet_pton(AF_INET, s, &addr->sin_addr);
+ ASSERT_EQ(1, err);
+}
+
+static void ipv6(struct sockaddr_in6 *addr, const char *s) {
+ int err;
+
+ addr->sin6_family = AF_INET6;
+ err = inet_pton(AF_INET6, s, &addr->sin6_addr);
+ ASSERT_EQ(1, err);
+}
+
+static char eth0[] = "eth0";
+static char eth1[] = "eth1";
+
+TEST(CommonIPAddr, TestNotFound)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in a_one;
+ struct sockaddr_in6 a_two;
+ struct sockaddr_in net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv4(&a_one, "10.11.12.13");
+ ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+ ipv4(&net, "10.11.234.56");
+ ASSERT_FALSE(matches_ipv4_in_subnet(one, (struct sockaddr_in*)&net, 24));
+ ASSERT_FALSE(matches_ipv6_in_subnet(two, (struct sockaddr_in6*)&net, 24));
+}
+
+TEST(CommonIPAddr, TestV4_Simple)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in a_one;
+ struct sockaddr_in6 a_two;
+ struct sockaddr_in net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv4(&a_one, "10.11.12.13");
+ ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+ ipv4(&net, "10.11.12.42");
+
+ ASSERT_TRUE(matches_ipv4_in_subnet(one, (struct sockaddr_in*)&net, 24));
+ ASSERT_FALSE(matches_ipv4_in_subnet(two, (struct sockaddr_in*)&net, 24));
+}
+
+TEST(CommonIPAddr, TestV4_Prefix25)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in a_one;
+ struct sockaddr_in a_two;
+ struct sockaddr_in net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv4(&a_one, "10.11.12.13");
+ ipv4(&a_two, "10.11.12.129");
+ ipv4(&net, "10.11.12.128");
+
+ ASSERT_FALSE(matches_ipv4_in_subnet(one, (struct sockaddr_in*)&net, 25));
+ ASSERT_TRUE(matches_ipv4_in_subnet(two, (struct sockaddr_in*)&net, 25));
+}
+
+TEST(CommonIPAddr, TestV4_Prefix16)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in a_one;
+ struct sockaddr_in a_two;
+ struct sockaddr_in net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv4(&a_one, "10.1.1.2");
+ ipv4(&a_two, "10.2.1.123");
+ ipv4(&net, "10.2.0.0");
+
+ ASSERT_FALSE(matches_ipv4_in_subnet(one, (struct sockaddr_in*)&net, 16));
+ ASSERT_TRUE(matches_ipv4_in_subnet(two, (struct sockaddr_in*)&net, 16));
+}
+
+TEST(CommonIPAddr, TestV4_PrefixTooLong)
+{
+ struct ifaddrs one;
+ struct sockaddr_in a_one;
+ struct sockaddr_in net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = NULL;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ ipv4(&a_one, "10.11.12.13");
+ ipv4(&net, "10.11.12.12");
+
+ ASSERT_FALSE(matches_ipv4_in_subnet(one, (struct sockaddr_in*)&net, 42));
+}
+
+TEST(CommonIPAddr, TestV4_PrefixZero)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in6 a_one;
+ struct sockaddr_in a_two;
+ struct sockaddr_in net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv6(&a_one, "2001:1234:5678:900F::cdef");
+ ipv4(&a_two, "10.1.2.3");
+ ipv4(&net, "255.0.1.2");
+
+ ASSERT_FALSE(matches_ipv4_in_subnet(one, (struct sockaddr_in*)&net, 0));
+ ASSERT_TRUE(matches_ipv4_in_subnet(two, (struct sockaddr_in*)&net, 0));
+}
+
+static char lo[] = "lo";
+static char lo0[] = "lo:0";
+
+TEST(CommonIPAddr, TestV4_SkipLoopback)
+{
+ struct ifaddrs one, two, three;
+ struct sockaddr_in a_one;
+ struct sockaddr_in a_two;
+ struct sockaddr_in a_three;
+
+ one.ifa_next = &two;
+ one.ifa_flags &= ~IFF_UP;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = lo;
+
+ two.ifa_next = &three;
+ two.ifa_flags = IFF_UP;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = lo0;
+
+ three.ifa_next = NULL;
+ three.ifa_flags = IFF_UP;
+ three.ifa_addr = (struct sockaddr*)&a_three;
+ three.ifa_name = eth0;
+
+ ipv4(&a_one, "127.0.0.1");
+ ipv4(&a_two, "127.0.0.1");
+ ipv4(&a_three, "10.1.2.3");
+
+ const struct sockaddr *result = nullptr;
+ // we prefer the non-loopback address despite the loopback addresses
+ result =
+ find_ip_in_subnet_list(nullptr, (struct ifaddrs*)&one,
+ CEPH_PICK_ADDRESS_IPV4 | CEPH_PICK_ADDRESS_IPV6,
+ "", "");
+ ASSERT_EQ(three.ifa_addr, result);
+ // the subnet criteria leaves us no choice but the UP loopback address
+ result =
+ find_ip_in_subnet_list(nullptr, (struct ifaddrs*)&one,
+ CEPH_PICK_ADDRESS_IPV4 | CEPH_PICK_ADDRESS_IPV6,
+ "127.0.0.0/8", "");
+ ASSERT_EQ(two.ifa_addr, result);
+}
+
+TEST(CommonIPAddr, TestV6_Simple)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in a_one;
+ struct sockaddr_in6 a_two;
+ struct sockaddr_in6 net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv4(&a_one, "10.11.12.13");
+ ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+ ipv6(&net, "2001:1234:5678:90ab::dead:beef");
+
+ ASSERT_FALSE(matches_ipv6_in_subnet(one, (struct sockaddr_in6*)&net, 64));
+ ASSERT_TRUE(matches_ipv6_in_subnet(two, (struct sockaddr_in6*)&net, 64));
+}
+
+TEST(CommonIPAddr, TestV6_Prefix57)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in6 a_one;
+ struct sockaddr_in6 a_two;
+ struct sockaddr_in6 net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv6(&a_one, "2001:1234:5678:900F::cdef");
+ ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+ ipv6(&net, "2001:1234:5678:90ab::dead:beef");
+
+ ASSERT_FALSE(matches_ipv6_in_subnet(one, (struct sockaddr_in6*)&net, 57));
+ ASSERT_TRUE(matches_ipv6_in_subnet(two, (struct sockaddr_in6*)&net, 57));
+}
+
+TEST(CommonIPAddr, TestV6_PrefixTooLong)
+{
+ struct ifaddrs one;
+ struct sockaddr_in6 a_one;
+ struct sockaddr_in6 net;
+
+ memset(&net, 0, sizeof(net));
+
+ one.ifa_next = NULL;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ ipv6(&a_one, "2001:1234:5678:900F::cdef");
+ ipv6(&net, "2001:1234:5678:900F::cdee");
+
+ ASSERT_FALSE(matches_ipv6_in_subnet(one, (struct sockaddr_in6*)&net, 9000));
+}
+
+TEST(CommonIPAddr, TestV6_PrefixZero)
+{
+ struct ifaddrs one, two;
+ struct sockaddr_in a_one;
+ struct sockaddr_in6 a_two;
+ struct sockaddr_in6 net;
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = NULL;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ ipv4(&a_one, "10.2.3.4");
+ ipv6(&a_two, "2001:f00b::1");
+ ipv6(&net, "ff00::1");
+
+ ASSERT_FALSE(matches_ipv6_in_subnet(one, (struct sockaddr_in6*)&net, 0));
+ ASSERT_TRUE(matches_ipv6_in_subnet(two, (struct sockaddr_in6*)&net, 0));
+}
+
+TEST(CommonIPAddr, TestV6_SkipLoopback)
+{
+ struct ifaddrs one, two, three;
+ struct sockaddr_in6 a_one;
+ struct sockaddr_in6 a_two;
+ struct sockaddr_in6 a_three;
+
+ one.ifa_next = &two;
+ one.ifa_flags &= ~IFF_UP;
+ ipv6(&a_one, "::1");
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = lo;
+
+ two.ifa_next = &three;
+ two.ifa_flags = IFF_UP;
+ ipv6(&a_two, "::1");
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = lo0;
+
+ three.ifa_next = NULL;
+ three.ifa_flags = IFF_UP;
+ ipv6(&a_three, "2001:1234:5678:90ab::beef");
+ three.ifa_addr = (struct sockaddr*)&a_three;
+ three.ifa_name = eth0;
+
+ const struct sockaddr *result = nullptr;
+ // we prefer the non-loopback address despite the loopback addresses
+ result =
+ find_ip_in_subnet_list(nullptr, (struct ifaddrs*)&one,
+ CEPH_PICK_ADDRESS_IPV4 | CEPH_PICK_ADDRESS_IPV6,
+ "", "");
+ ASSERT_EQ(three.ifa_addr, result);
+ // the subnet criteria leaves us no choice but the UP loopback address
+ result =
+ find_ip_in_subnet_list(nullptr, (struct ifaddrs*)&one,
+ CEPH_PICK_ADDRESS_IPV4 | CEPH_PICK_ADDRESS_IPV6,
+ "::1/128", "");
+ ASSERT_EQ(two.ifa_addr, result);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Empty)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_Junk)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("foo", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_SlashNum)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("/24", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_Slash)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("/", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv4)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv4Slash)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123/", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv4SlashNegative)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123/-3", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv4SlashJunk)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123/foo", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv6)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv6Slash)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv6SlashNegative)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/-3", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_Bad_IPv6SlashJunk)
+{
+ struct sockaddr_storage network;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/foo", &network, &prefix_len);
+ ASSERT_EQ(ok, false);
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv4_0)
+{
+ struct sockaddr_in network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123/0", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(0U, prefix_len);
+ ASSERT_EQ(AF_INET, network.sin_family);
+ ASSERT_EQ(0, network.sin_port);
+ struct sockaddr_in want;
+ ipv4(&want, "123.123.123.123");
+ ASSERT_EQ(want.sin_addr.s_addr, network.sin_addr.s_addr);
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv4_13)
+{
+ struct sockaddr_in network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123/13", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(13U, prefix_len);
+ ASSERT_EQ(AF_INET, network.sin_family);
+ ASSERT_EQ(0, network.sin_port);
+ struct sockaddr_in want;
+ ipv4(&want, "123.123.123.123");
+ ASSERT_EQ(want.sin_addr.s_addr, network.sin_addr.s_addr);
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv4_32)
+{
+ struct sockaddr_in network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123/32", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(32U, prefix_len);
+ ASSERT_EQ(AF_INET, network.sin_family);
+ ASSERT_EQ(0, network.sin_port);
+ struct sockaddr_in want;
+ ipv4(&want, "123.123.123.123");
+ ASSERT_EQ(want.sin_addr.s_addr, network.sin_addr.s_addr);
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv4_42)
+{
+ struct sockaddr_in network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("123.123.123.123/42", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(42U, prefix_len);
+ ASSERT_EQ(AF_INET, network.sin_family);
+ ASSERT_EQ(0, network.sin_port);
+ struct sockaddr_in want;
+ ipv4(&want, "123.123.123.123");
+ ASSERT_EQ(want.sin_addr.s_addr, network.sin_addr.s_addr);
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv6_0)
+{
+ struct sockaddr_in6 network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/0", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in6 *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(0U, prefix_len);
+ ASSERT_EQ(AF_INET6, network.sin6_family);
+ ASSERT_EQ(0, network.sin6_port);
+ struct sockaddr_in6 want;
+ ipv6(&want, "2001:1234:5678:90ab::dead:beef");
+ ASSERT_EQ(0, memcmp(want.sin6_addr.s6_addr, network.sin6_addr.s6_addr, sizeof(network.sin6_addr.s6_addr)));
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv6_67)
+{
+ struct sockaddr_in6 network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/67", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in6 *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(67U, prefix_len);
+ ASSERT_EQ(AF_INET6, network.sin6_family);
+ ASSERT_EQ(0, network.sin6_port);
+ struct sockaddr_in6 want;
+ ipv6(&want, "2001:1234:5678:90ab::dead:beef");
+ ASSERT_EQ(0, memcmp(want.sin6_addr.s6_addr, network.sin6_addr.s6_addr, sizeof(network.sin6_addr.s6_addr)));
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv6_128)
+{
+ struct sockaddr_in6 network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/128", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in6 *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(128U, prefix_len);
+ ASSERT_EQ(AF_INET6, network.sin6_family);
+ ASSERT_EQ(0, network.sin6_port);
+ struct sockaddr_in6 want;
+ ipv6(&want, "2001:1234:5678:90ab::dead:beef");
+ ASSERT_EQ(0, memcmp(want.sin6_addr.s6_addr, network.sin6_addr.s6_addr, sizeof(network.sin6_addr.s6_addr)));
+}
+
+TEST(CommonIPAddr, ParseNetwork_IPv6_9000)
+{
+ struct sockaddr_in6 network;
+ struct sockaddr_storage net_storage;
+ unsigned int prefix_len;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/9000", &net_storage, &prefix_len);
+ network = *(struct sockaddr_in6 *) &net_storage;
+ ASSERT_EQ(ok, true);
+ ASSERT_EQ(9000U, prefix_len);
+ ASSERT_EQ(AF_INET6, network.sin6_family);
+ ASSERT_EQ(0, network.sin6_port);
+ struct sockaddr_in6 want;
+ ipv6(&want, "2001:1234:5678:90ab::dead:beef");
+ ASSERT_EQ(0, memcmp(want.sin6_addr.s6_addr, network.sin6_addr.s6_addr, sizeof(network.sin6_addr.s6_addr)));
+}
+
+TEST(CommonIPAddr, ambiguous)
+{
+ entity_addr_t a;
+ bool ok;
+
+ ok = a.parse("1.2.3.4", nullptr, entity_addr_t::TYPE_ANY);
+ ASSERT_TRUE(ok);
+ ASSERT_EQ(entity_addr_t::TYPE_ANY, a.get_type());
+
+ ok = a.parse("any:1.2.3.4", nullptr, entity_addr_t::TYPE_ANY);
+ ASSERT_TRUE(ok);
+ ASSERT_EQ(entity_addr_t::TYPE_ANY, a.get_type());
+
+ ok = a.parse("v1:1.2.3.4", nullptr, entity_addr_t::TYPE_ANY);
+ ASSERT_TRUE(ok);
+ ASSERT_EQ(entity_addr_t::TYPE_LEGACY, a.get_type());
+
+ ok = a.parse("v2:1.2.3.4", nullptr, entity_addr_t::TYPE_ANY);
+ ASSERT_TRUE(ok);
+ ASSERT_EQ(entity_addr_t::TYPE_MSGR2, a.get_type());
+}
+
+TEST(CommonIPAddr, network_contains)
+{
+ entity_addr_t network, addr;
+ unsigned int prefix;
+ bool ok;
+
+ ok = parse_network("2001:1234:5678:90ab::dead:beef/32", &network, &prefix);
+ ASSERT_TRUE(ok);
+ ASSERT_EQ(32U, prefix);
+ ok = addr.parse("2001:1234:5678:90ab::dead:beef", nullptr);
+ ASSERT_TRUE(ok);
+ ASSERT_TRUE(network_contains(network, prefix, addr));
+ ok = addr.parse("2001:1334:5678:90ab::dead:beef", nullptr);
+ ASSERT_TRUE(ok);
+ ASSERT_FALSE(network_contains(network, prefix, addr));
+ ok = addr.parse("127.0.0.1", nullptr);
+ ASSERT_TRUE(ok);
+ ASSERT_FALSE(network_contains(network, prefix, addr));
+
+ ok = parse_network("10.1.2.3/16", &network, &prefix);
+ ASSERT_TRUE(ok);
+ ASSERT_EQ(16U, prefix);
+ ok = addr.parse("2001:1234:5678:90ab::dead:beef", nullptr);
+ ASSERT_TRUE(ok);
+ ASSERT_FALSE(network_contains(network, prefix, addr));
+ ok = addr.parse("1.2.3.4", nullptr);
+ ASSERT_TRUE(ok);
+ ASSERT_FALSE(network_contains(network, prefix, addr));
+ ok = addr.parse("10.1.22.44", nullptr);
+ ASSERT_TRUE(ok);
+ ASSERT_TRUE(network_contains(network, prefix, addr));
+ ok = addr.parse("10.2.22.44", nullptr);
+ ASSERT_TRUE(ok);
+ ASSERT_FALSE(network_contains(network, prefix, addr));
+}
+
+TEST(pick_address, find_ip_in_subnet_list)
+{
+ struct ifaddrs one, two, three;
+ struct sockaddr_in a_one;
+ struct sockaddr_in a_two;
+ struct sockaddr_in6 a_three;
+ const struct sockaddr *result;
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = &three;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ three.ifa_next = NULL;
+ three.ifa_addr = (struct sockaddr*)&a_three;
+ three.ifa_name = eth1;
+
+ ipv4(&a_one, "10.1.1.2");
+ ipv4(&a_two, "10.2.1.123");
+ ipv6(&a_three, "2001:1234:5678:90ab::cdef");
+
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_OSD);
+
+ // match by network
+ result = find_ip_in_subnet_list(
+ cct.get(),
+ &one,
+ CEPH_PICK_ADDRESS_IPV4,
+ "10.1.0.0/16",
+ "eth0");
+ ASSERT_EQ(one.ifa_addr, result);
+
+ result = find_ip_in_subnet_list(
+ cct.get(),
+ &one,
+ CEPH_PICK_ADDRESS_IPV4,
+ "10.2.0.0/16",
+ "eth1");
+ ASSERT_EQ(two.ifa_addr, result);
+
+ // match by eth name
+ result = find_ip_in_subnet_list(
+ cct.get(),
+ &one,
+ CEPH_PICK_ADDRESS_IPV4,
+ "10.0.0.0/8",
+ "eth0");
+ ASSERT_EQ(one.ifa_addr, result);
+
+ result = find_ip_in_subnet_list(
+ cct.get(),
+ &one,
+ CEPH_PICK_ADDRESS_IPV4,
+ "10.0.0.0/8",
+ "eth1");
+ ASSERT_EQ(two.ifa_addr, result);
+
+ result = find_ip_in_subnet_list(
+ cct.get(),
+ &one,
+ CEPH_PICK_ADDRESS_IPV6,
+ "2001::/16",
+ "eth1");
+ ASSERT_EQ(three.ifa_addr, result);
+}
+
+TEST(pick_address, filtering)
+{
+ struct ifaddrs one, two, three;
+ struct sockaddr_in a_one;
+ struct sockaddr_in a_two;
+ struct sockaddr_in6 a_three;
+
+ one.ifa_next = &two;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ two.ifa_next = &three;
+ two.ifa_addr = (struct sockaddr*)&a_two;
+ two.ifa_name = eth1;
+
+ three.ifa_next = NULL;
+ three.ifa_addr = (struct sockaddr*)&a_three;
+ three.ifa_name = eth1;
+
+ ipv4(&a_one, "10.1.1.2");
+ ipv4(&a_two, "10.2.1.123");
+ ipv6(&a_three, "2001:1234:5678:90ab::cdef");
+
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_MON);
+ cct->_conf._clear_safe_to_start_threads(); // so we can set configs
+
+ cct->_conf.set_val("public_addr", "");
+ cct->_conf.set_val("public_network", "");
+ cct->_conf.set_val("public_network_interface", "");
+ cct->_conf.set_val("cluster_addr", "");
+ cct->_conf.set_val("cluster_network", "");
+ cct->_conf.set_val("cluster_network_interface", "");
+
+ entity_addrvec_t av;
+ {
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_MSGR1,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1u, av.v.size());
+ ASSERT_EQ(string("v1:0.0.0.0:0/0"), stringify(av.v[0]));
+ }
+ {
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV6 |
+ CEPH_PICK_ADDRESS_MSGR1,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1u, av.v.size());
+ ASSERT_EQ(string("v1:[::]:0/0"), stringify(av.v[0]));
+ }
+ {
+ cct->_conf.set_val("public_network", "10.2.0.0/16");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_MSGR1,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1u, av.v.size());
+ ASSERT_EQ(string("v1:10.2.1.123:0/0"), stringify(av.v[0]));
+ cct->_conf.set_val("public_network", "");
+ }
+ {
+ cct->_conf.set_val("public_network", "10.0.0.0/8");
+ cct->_conf.set_val("public_network_interface", "eth1");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_MSGR2,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1u, av.v.size());
+ ASSERT_EQ(string("v2:10.2.1.123:0/0"), stringify(av.v[0]));
+ cct->_conf.set_val("public_network", "");
+ cct->_conf.set_val("public_network_interface", "");
+ }
+ {
+ cct->_conf.set_val("public_network", "10.2.0.0/16");
+ cct->_conf.set_val("cluster_network", "10.1.0.0/16");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_MSGR2,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1u, av.v.size());
+ ASSERT_EQ(string("v2:10.2.1.123:0/0"), stringify(av.v[0]));
+ cct->_conf.set_val("public_network", "");
+ cct->_conf.set_val("cluster_network", "");
+ }
+ {
+ cct->_conf.set_val("public_network", "10.2.0.0/16");
+ cct->_conf.set_val("cluster_network", "10.1.0.0/16");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_CLUSTER |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_MSGR1,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1u, av.v.size());
+ ASSERT_EQ(string("v1:10.1.1.2:0/0"), stringify(av.v[0]));
+ cct->_conf.set_val("public_network", "");
+ cct->_conf.set_val("cluster_network", "");
+ }
+
+ {
+ cct->_conf.set_val("public_network", "2001::/16");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV6 |
+ CEPH_PICK_ADDRESS_MSGR2,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(1u, av.v.size());
+ ASSERT_EQ(string("v2:[2001:1234:5678:90ab::cdef]:0/0"), stringify(av.v[0]));
+ cct->_conf.set_val("public_network", "");
+ }
+ {
+ cct->_conf.set_val("public_network", "2001::/16 10.0.0.0/8");
+ cct->_conf.set_val("public_network_interface", "eth1");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_IPV6 |
+ CEPH_PICK_ADDRESS_MSGR2,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(2u, av.v.size());
+ ASSERT_EQ(string("v2:[2001:1234:5678:90ab::cdef]:0/0"), stringify(av.v[0]));
+ ASSERT_EQ(string("v2:10.2.1.123:0/0"), stringify(av.v[1]));
+ cct->_conf.set_val("public_network", "");
+ cct->_conf.set_val("public_network_interface", "");
+ }
+ {
+ cct->_conf.set_val("public_network", "2001::/16 10.0.0.0/8");
+ cct->_conf.set_val("public_network_interface", "eth1");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_IPV6 |
+ CEPH_PICK_ADDRESS_MSGR1 |
+ CEPH_PICK_ADDRESS_PREFER_IPV4,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(2u, av.v.size());
+ ASSERT_EQ(string("v1:10.2.1.123:0/0"), stringify(av.v[0]));
+ ASSERT_EQ(string("v1:[2001:1234:5678:90ab::cdef]:0/0"), stringify(av.v[1]));
+ cct->_conf.set_val("public_network", "");
+ cct->_conf.set_val("public_network_interface", "");
+ }
+
+ {
+ cct->_conf.set_val("public_network", "2001::/16");
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV6 |
+ CEPH_PICK_ADDRESS_MSGR1 |
+ CEPH_PICK_ADDRESS_MSGR2,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(2u, av.v.size());
+ ASSERT_EQ(string("v2:[2001:1234:5678:90ab::cdef]:0/0"), stringify(av.v[0]));
+ ASSERT_EQ(string("v1:[2001:1234:5678:90ab::cdef]:0/0"), stringify(av.v[1]));
+ cct->_conf.set_val("public_network", "");
+ }
+
+ {
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_IPV4 |
+ CEPH_PICK_ADDRESS_MSGR1 |
+ CEPH_PICK_ADDRESS_MSGR2,
+ &one, &av);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(2u, av.v.size());
+ ASSERT_EQ(string("v2:0.0.0.0:0/0"), stringify(av.v[0]));
+ ASSERT_EQ(string("v1:0.0.0.0:0/0"), stringify(av.v[1]));
+ }
+}
+
+TEST(pick_address, ipv4_ipv6_enabled)
+{
+ struct ifaddrs one;
+ struct sockaddr_in a_one;
+
+ one.ifa_next = NULL;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ ipv4(&a_one, "10.1.1.2");
+
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_OSD);
+ cct->_conf._clear_safe_to_start_threads(); // so we can set configs
+
+ cct->_conf.set_val("public_addr", "");
+ cct->_conf.set_val("public_network", "10.1.1.0/24");
+ cct->_conf.set_val("public_network_interface", "");
+ cct->_conf.set_val("cluster_addr", "");
+ cct->_conf.set_val("cluster_network", "");
+ cct->_conf.set_val("cluster_network_interface", "");
+ cct->_conf.set_val("ms_bind_ipv6", "true");
+
+ entity_addrvec_t av;
+ {
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_MSGR1,
+ &one, &av);
+ ASSERT_EQ(-1, r);
+ }
+}
+
+TEST(pick_address, ipv4_ipv6_enabled2)
+{
+ struct ifaddrs one;
+ struct sockaddr_in6 a_one;
+
+ one.ifa_next = NULL;
+ one.ifa_addr = (struct sockaddr*)&a_one;
+ one.ifa_name = eth0;
+
+ ipv6(&a_one, "2001:1234:5678:90ab::cdef");
+
+ boost::intrusive_ptr<CephContext> cct = new CephContext(CEPH_ENTITY_TYPE_OSD);
+ cct->_conf._clear_safe_to_start_threads(); // so we can set configs
+
+ cct->_conf.set_val("public_addr", "");
+ cct->_conf.set_val("public_network", "2001::/16");
+ cct->_conf.set_val("public_network_interface", "");
+ cct->_conf.set_val("cluster_addr", "");
+ cct->_conf.set_val("cluster_network", "");
+ cct->_conf.set_val("cluster_network_interface", "");
+ cct->_conf.set_val("ms_bind_ipv6", "true");
+
+ entity_addrvec_t av;
+ {
+ int r = pick_addresses(cct.get(),
+ CEPH_PICK_ADDRESS_PUBLIC |
+ CEPH_PICK_ADDRESS_MSGR1,
+ &one, &av);
+ ASSERT_EQ(-1, r);
+ }
+}
diff --git a/src/test/test_lost.sh b/src/test/test_lost.sh
new file mode 100755
index 000000000..ee04dd180
--- /dev/null
+++ b/src/test/test_lost.sh
@@ -0,0 +1,257 @@
+#!/usr/bin/env bash
+set -x
+
+#
+# Test the lost object logic
+#
+
+# Includes
+source "`dirname $0`/test_common.sh"
+
+TEST_POOL=rbd
+
+# Functions
+setup() {
+ export CEPH_NUM_OSD=$1
+ vstart_config=$2
+
+ # Start ceph
+ ./stop.sh
+
+ # set recovery start to a really long time to ensure that we don't start recovery
+ ./vstart.sh -d -n -o "$vstart_config" || die "vstart failed"
+
+ # for exiting pools set size not greater than number of OSDs,
+ # so recovery from degraded ps is possible
+ local changed=0
+ for pool in `./ceph osd pool ls`; do
+ local size=`./ceph osd pool get ${pool} size | awk '{print $2}'`
+ if [ "${size}" -gt "${CEPH_NUM_OSD}" ]; then
+ ./ceph osd pool set ${pool} size ${CEPH_NUM_OSD} --yes-i-really-mean-it
+ changed=1
+ fi
+ done
+ if [ ${changed} -eq 1 ]; then
+ # XXX: When a pool has degraded pgs due to size greater than number
+ # of OSDs, after decreasing the size the recovery still could stuck
+ # and requires an additional kick.
+ ./ceph osd out 0
+ ./ceph osd in 0
+ fi
+
+ poll_cmd "./ceph health" HEALTH_OK 1 30
+}
+
+recovery1_impl() {
+ # Write lots and lots of objects
+ write_objects 1 1 200 4000 $TEST_POOL
+
+ # Take down osd1
+ stop_osd 1
+
+ # Continue writing a lot of objects
+ write_objects 2 2 200 4000 $TEST_POOL
+
+ # Bring up osd1
+ restart_osd 1
+
+ # Finish peering.
+ sleep 15
+
+ # Stop osd0.
+ # At this point we have peered, but *NOT* recovered.
+ # Objects should be lost.
+ stop_osd 0
+
+ poll_cmd "./ceph pg debug degraded_pgs_exist" TRUE 3 120
+ [ $? -eq 1 ] || die "Failed to see degraded PGs."
+ poll_cmd "./ceph pg debug unfound_objects_exist" TRUE 3 120
+ [ $? -eq 1 ] || die "Failed to see unfound objects."
+ echo "Got unfound objects."
+
+ restart_osd 0
+ sleep 20
+ start_recovery 2
+
+ # Turn on recovery and wait for it to complete.
+ poll_cmd "./ceph pg debug unfound_objects_exist" FALSE 3 120
+ [ $? -eq 1 ] || die "Failed to recover unfound objects."
+ poll_cmd "./ceph pg debug degraded_pgs_exist" FALSE 3 120
+ [ $? -eq 1 ] || die "Recovery never finished."
+}
+
+recovery1() {
+ setup 2 'osd recovery delay start = 10000'
+ recovery1_impl
+}
+
+lost1_impl() {
+ local flags="$@"
+ local lost_action=delete
+ local pgs_unfound pg
+
+ if is_set revert_lost $flags; then
+ lost_action=revert
+ fi
+
+ # Write lots and lots of objects
+ write_objects 1 1 20 8000 $TEST_POOL
+
+ # Take down osd1
+ stop_osd 1
+
+ # Continue writing a lot of objects
+ write_objects 2 2 20 8000 $TEST_POOL
+
+ # Bring up osd1
+ restart_osd 1
+
+ # Finish peering.
+ sleep 15
+
+ # Stop osd0.
+ # At this point we have peered, but *NOT* recovered.
+ # Objects should be lost.
+ stop_osd 0
+
+ # Since recovery can't proceed, stuff should be unfound.
+ poll_cmd "./ceph pg debug unfound_objects_exist" TRUE 3 120
+ [ $? -eq 1 ] || die "Failed to see unfound objects."
+
+ pgs_unfound=`./ceph health detail |awk '$1 = "pg" && /[0-9] unfound$/ {print $2}'`
+
+ [ -n "$pgs_unfound" ] || die "no pg with unfound objects"
+
+ for pg in $pgs_unfound; do
+ ./ceph pg $pg mark_unfound_lost revert &&
+ die "mark_unfound_lost unexpectedly succeeded for pg $pg"
+ done
+
+ if ! is_set mark_osd_lost $flags && ! is_set rm_osd $flags; then
+ return
+ fi
+
+ if is_set try_to_fetch_unfound $flags; then
+ # Ask for an object while it's still unfound, and
+ # verify we get woken to an error when it's declared lost.
+ echo "trying to get one of the unfound objects"
+ (
+ ./rados -c ./ceph.conf -p $TEST_POOL get obj02 $TEMPDIR/obj02 &&\
+ die "expected radostool error"
+ ) &
+ fi
+
+ if is_set mark_osd_lost $flags; then
+ ./ceph osd lost 0 --yes-i-really-mean-it
+ fi
+
+ if is_set rm_osd $flags; then
+ ./ceph osd rm 0
+ fi
+
+ if ! is_set auto_mark_unfound_lost $flags; then
+ for pg in $pgs_unfound; do
+ ./ceph pg $pg mark_unfound_lost ${lost_action} ||
+ die "mark_unfound_lost failed for pg $pg"
+ done
+ fi
+
+ start_recovery 2
+
+ # Unfound objects go away and are turned into lost objects.
+ poll_cmd "./ceph pg debug unfound_objects_exist" FALSE 3 120
+ [ $? -eq 1 ] || die "Unfound objects didn't go away."
+
+ for pg in `ceph pg ls | awk '/^[0-9]/ {print $1}'`; do
+ ./ceph pg $pg mark_unfound_lost revert 2>&1 |
+ grep 'pg has no unfound objects' ||
+ die "pg $pg has unfound objects"
+ done
+
+ # Reading from a lost object gives back an error code.
+ # TODO: check error code
+ ./rados -c ./ceph.conf -p $TEST_POOL get obj01 $TEMPDIR/obj01
+ if [ lost_action = delete -a $? -eq 0 ]; then
+ die "expected radostool error"
+ elif [ lost_action = revert -a $? -ne 0 ]; then
+ die "unexpected radostool error"
+ fi
+
+ if is_set try_to_fetch_unfound $flags; then
+ echo "waiting for the try_to_fetch_unfound \
+radostool instance to finish"
+ wait
+ fi
+}
+
+lost1() {
+ setup 2 'osd recovery delay start = 10000'
+ lost1_impl mark_osd_lost revert_lost
+}
+
+lost2() {
+ setup 2 'osd recovery delay start = 10000'
+ lost1_impl mark_osd_lost try_to_fetch_unfound
+}
+
+lost3() {
+ setup 2 'osd recovery delay start = 10000'
+ lost1_impl rm_osd
+}
+
+lost4() {
+ setup 2 'osd recovery delay start = 10000'
+ lost1_impl mark_osd_lost rm_osd
+}
+
+lost5() {
+ setup 2 'osd recovery delay start = 10000'
+ lost1_impl mark_osd_lost auto_mark_unfound_lost
+}
+
+all_osds_die_impl() {
+ poll_cmd "./ceph osd stat" '3 up, 3 in' 20 240
+ [ $? -eq 1 ] || die "didn't start 3 osds"
+
+ stop_osd 0
+ stop_osd 1
+ stop_osd 2
+
+ # wait for the MOSDPGStat timeout
+ poll_cmd "./ceph osd stat" '0 up' 20 240
+ [ $? -eq 1 ] || die "all osds weren't marked as down"
+}
+
+all_osds_die() {
+ setup 3 'osd mon report interval = 3
+ mon osd report timeout = 60'
+
+ all_osds_die_impl
+}
+
+run() {
+ recovery1 || die "test failed"
+
+ lost1 || die "test failed"
+
+ # XXX: try_to_fetch_unfound test currently hangs on "waiting for the
+ # try_to_fetch_unfound radostool instance to finish"
+ #lost2 || die "test failed"
+
+ lost3 || die "test failed"
+
+ lost4 || die "test failed"
+
+ # XXX: automatically marking lost is not implemented
+ #lost5 || die "test failed"
+
+ all_osds_die || die "test failed"
+}
+
+if [ -z "$@" ]; then
+ run
+ echo OK
+ exit 0
+fi
+
+$@
diff --git a/src/test/test_mempool.cc b/src/test/test_mempool.cc
new file mode 100644
index 000000000..9dd96682f
--- /dev/null
+++ b/src/test/test_mempool.cc
@@ -0,0 +1,460 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2016 Western Digital Corporation
+ *
+ * Author: Allen Samuels <allen.samuels@sandisk.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+#include "include/btree_map.h"
+#include "include/mempool.h"
+
+using namespace std;
+
+void check_usage(mempool::pool_index_t ix)
+{
+ mempool::pool_t *pool = &mempool::get_pool(ix);
+ mempool::stats_t total;
+ map<std::string,mempool::stats_t> m;
+ pool->get_stats(&total, &m);
+ size_t usage = pool->allocated_bytes();
+ size_t sum = 0;
+ for (auto& p : m) {
+ sum += p.second.bytes;
+ }
+ if (sum != usage) {
+ ceph::TableFormatter jf;
+ pool->dump(&jf);
+ jf.flush(std::cout);
+ }
+ EXPECT_EQ(sum, usage);
+}
+
+template<typename A, typename B>
+void eq_elements(const A& a, const B& b)
+{
+ auto lhs = a.begin();
+ auto rhs = b.begin();
+ while (lhs != a.end()) {
+ EXPECT_EQ(*lhs,*rhs);
+ lhs++;
+ rhs++;
+ }
+ EXPECT_EQ(rhs,b.end());
+}
+
+template<typename A, typename B>
+void eq_pairs(const A& a, const B& b)
+{
+ auto lhs = a.begin();
+ auto rhs = b.begin();
+ while (lhs != a.end()) {
+ EXPECT_EQ(lhs->first,rhs->first);
+ EXPECT_EQ(lhs->second,rhs->second);
+ lhs++;
+ rhs++;
+ }
+ EXPECT_EQ(rhs,b.end());
+}
+
+#define MAKE_INSERTER(inserter) \
+ template<typename A,typename B> \
+void do_##inserter(A& a, B& b, int count, int base) { \
+ for (int i = 0; i < count; ++i) { \
+ a.inserter(base + i); \
+ b.inserter(base + i); \
+ } \
+}
+
+MAKE_INSERTER(push_back);
+MAKE_INSERTER(insert);
+
+template<typename A,typename B>
+void do_insert_key(A& a, B& b, int count, int base)
+{
+ for (int i = 0; i < count; ++i) {
+ a.insert(make_pair(base+i,base+i));
+ b.insert(make_pair(base+i,base+i));
+ check_usage(mempool::osd::id);
+ }
+}
+
+TEST(mempool, vector_context)
+{
+ check_usage(mempool::osd::id);
+ EXPECT_EQ(mempool::osd::allocated_bytes(), 0u);
+ EXPECT_EQ(mempool::osd::allocated_items(), 0u);
+ for (unsigned i = 0; i < 10; ++i) {
+ vector<int> a;
+ mempool::osd::vector<int> b,c;
+ eq_elements(a,b);
+ do_push_back(a,b,i,i);
+ eq_elements(a,b);
+ check_usage(mempool::osd::id);
+
+ mempool::stats_t total;
+ map<std::string,mempool::stats_t> by_type;
+ mempool::get_pool(mempool::osd::id).get_stats(&total, &by_type);
+ EXPECT_GE(mempool::osd::allocated_bytes(), i * 4u);
+ EXPECT_GE(mempool::osd::allocated_items(), i);
+
+ c.swap(b);
+ eq_elements(a,c);
+ check_usage(mempool::osd::id);
+ a.clear();
+ b.clear();
+ c.clear();
+ }
+}
+
+TEST(mempool, list_context)
+{
+ for (unsigned i = 1; i < 10; ++i) {
+ list<int> a;
+ mempool::osd::list<int> b,c;
+ eq_elements(a,b);
+ do_push_back(a,b,i,i);
+ eq_elements(a,b);
+ c.swap(b);
+ eq_elements(a,c);
+ a.erase(a.begin());
+ c.erase(c.begin());
+ eq_elements(a,c);
+ a.clear();
+ b.clear();
+ c.clear();
+ do_push_back(a,b,i,i);
+ c.splice(c.begin(),b,b.begin(),b.end());
+
+ mempool::stats_t total;
+ map<std::string,mempool::stats_t> by_type;
+ mempool::get_pool(mempool::osd::id).get_stats(&total, &by_type);
+ EXPECT_GE(mempool::osd::allocated_bytes(), i * 4u);
+ EXPECT_EQ(mempool::osd::allocated_items(), i);
+
+ eq_elements(a,c);
+ check_usage(mempool::osd::id);
+ }
+}
+
+TEST(mempool, set_context)
+{
+ for (int i = 0; i < 10; ++i) {
+ set<int> a;
+ mempool::osd::set<int> b;
+ do_insert(a,b,i,i);
+ eq_elements(a,b);
+ check_usage(mempool::osd::id);
+ }
+
+ for (int i = 1; i < 10; ++i) {
+ set<int> a;
+ mempool::osd::set<int> b;
+ do_insert(a,b,i,0);
+ EXPECT_NE(a.find(i/2),a.end());
+ EXPECT_NE(b.find(i/2),b.end());
+ a.erase(a.find(i/2));
+ b.erase(b.find(i/2));
+ eq_elements(a,b);
+ check_usage(mempool::osd::id);
+ }
+}
+
+struct obj {
+ MEMPOOL_CLASS_HELPERS();
+ int a;
+ int b;
+ obj() : a(1), b(1) {}
+ explicit obj(int _a) : a(_a), b(2) {}
+ obj(int _a,int _b) : a(_a), b(_b) {}
+ friend inline bool operator<(const obj& l, const obj& r) {
+ return l.a < r.a;
+ }
+};
+MEMPOOL_DEFINE_OBJECT_FACTORY(obj, obj, osdmap);
+
+TEST(mempool, test_factory)
+{
+ obj *o1 = new obj();
+ obj *o2 = new obj(10);
+ obj *o3 = new obj(20,30);
+ check_usage(mempool::osdmap::id);
+ EXPECT_NE(o1,nullptr);
+ EXPECT_EQ(o1->a,1);
+ EXPECT_EQ(o1->b,1);
+ EXPECT_EQ(o2->a,10);
+ EXPECT_EQ(o2->b,2);
+ EXPECT_EQ(o3->a,20);
+ EXPECT_EQ(o3->b,30);
+
+ delete o1;
+ delete o2;
+ delete o3;
+ check_usage(mempool::osdmap::id);
+}
+
+TEST(mempool, vector)
+{
+ {
+ mempool::osd::vector<int> v;
+ v.push_back(1);
+ v.push_back(2);
+ }
+ {
+ mempool::osdmap::vector<obj> v;
+ v.push_back(obj());
+ v.push_back(obj(1));
+ }
+}
+
+TEST(mempool, set)
+{
+ mempool::osd::set<int> set_int;
+ set_int.insert(1);
+ set_int.insert(2);
+ mempool::osdmap::set<obj> set_obj;
+ set_obj.insert(obj());
+ set_obj.insert(obj(1));
+ set_obj.insert(obj(1, 2));
+}
+
+TEST(mempool, map)
+{
+ {
+ mempool::osd::map<int,int> v;
+ v[1] = 2;
+ v[3] = 4;
+ }
+ {
+ mempool::osdmap::map<int,obj> v;
+ v[1] = obj();
+ v[2] = obj(2);
+ v[3] = obj(2, 3);
+ }
+}
+
+TEST(mempool, list)
+{
+ {
+ mempool::osd::list<int> v;
+ v.push_back(1);
+ v.push_back(2);
+ }
+ {
+ mempool::osdmap::list<obj> v;
+ v.push_back(obj());
+ v.push_back(obj(1));
+ }
+
+}
+
+TEST(mempool, dump)
+{
+ ostringstream ostr;
+
+ Formatter* f = Formatter::create("xml-pretty", "xml-pretty", "xml-pretty");
+ mempool::dump(f);
+ f->flush(ostr);
+
+ delete f;
+ ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
+ std::string::npos);
+
+ ostr.str("");
+
+ f = Formatter::create("html-pretty", "html-pretty", "html-pretty");
+ mempool::dump(f);
+ f->flush(ostr);
+
+ delete f;
+ ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
+ std::string::npos);
+
+ ostr.str("");
+ f = Formatter::create("table", "table", "table");
+ mempool::dump(f);
+ f->flush(ostr);
+
+ delete f;
+ ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
+ std::string::npos);
+
+ ostr.str("");
+
+ f = Formatter::create("json-pretty", "json-pretty", "json-pretty");
+ mempool::dump(f);
+ f->flush(ostr);
+ delete f;
+
+ ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
+ std::string::npos);
+}
+
+TEST(mempool, unordered_map)
+{
+ mempool::osdmap::unordered_map<int,obj> h;
+ h[1] = obj();
+ h[2] = obj(1);
+}
+
+TEST(mempool, string_test)
+{
+ mempool::osdmap::string s;
+ s.reserve(100);
+ EXPECT_GE(mempool::osdmap::allocated_items(), s.capacity() + 1u); // +1 for zero-byte termination :
+ for (size_t i = 0; i < 10; ++i) {
+ s += '1';
+ s.append(s);
+ EXPECT_GE(mempool::osdmap::allocated_items(), s.capacity() + 1u);
+ }
+}
+
+TEST(mempool, bufferlist)
+{
+ bufferlist bl;
+ int len = 1048576;
+ size_t before = mempool::buffer_anon::allocated_bytes();
+ cout << "before " << before << std::endl;
+ bl.append(buffer::create_aligned(len, 4096));
+ size_t after = mempool::buffer_anon::allocated_bytes();
+ cout << "after " << after << std::endl;
+ ASSERT_GE(after, before + len);
+}
+
+TEST(mempool, bufferlist_reassign)
+{
+ bufferlist bl;
+ size_t items_before = mempool::buffer_anon::allocated_items();
+ size_t bytes_before = mempool::buffer_anon::allocated_bytes();
+ bl.append("fooo");
+ ASSERT_EQ(items_before + 1, mempool::buffer_anon::allocated_items());
+ ASSERT_LT(bytes_before, mempool::buffer_anon::allocated_bytes());
+
+ // move existing bl
+ bl.reassign_to_mempool(mempool::mempool_osd);
+ ASSERT_EQ(items_before, mempool::buffer_anon::allocated_items());
+ ASSERT_EQ(bytes_before, mempool::buffer_anon::allocated_bytes());
+
+ // additional appends should go to the same pool
+ items_before = mempool::osd::allocated_items();
+ bytes_before = mempool::osd::allocated_bytes();
+ cout << "anon b " << mempool::buffer_anon::allocated_bytes() << std::endl;
+ for (unsigned i = 0; i < 1000; ++i) {
+ bl.append("asdfddddddddddddddddddddddasfdasdfasdfasdfasdfasdf");
+ }
+ cout << "anon a " << mempool::buffer_anon::allocated_bytes() << std::endl;
+ ASSERT_LT(items_before, mempool::osd::allocated_items());
+ ASSERT_LT(bytes_before, mempool::osd::allocated_bytes());
+
+ // try_.. won't
+ items_before = mempool::osd::allocated_items();
+ bytes_before = mempool::osd::allocated_bytes();
+ bl.try_assign_to_mempool(mempool::mempool_bloom_filter);
+ ASSERT_EQ(items_before, mempool::osd::allocated_items());
+ ASSERT_EQ(bytes_before, mempool::osd::allocated_bytes());
+}
+
+TEST(mempool, bufferlist_c_str)
+{
+ bufferlist bl;
+ int len = 1048576;
+ size_t before = mempool::osd::allocated_bytes();
+ bl.append(buffer::create_aligned(len, 4096));
+ bl.append(buffer::create_aligned(len, 4096));
+ bl.reassign_to_mempool(mempool::mempool_osd);
+ size_t after = mempool::osd::allocated_bytes();
+ ASSERT_GE(after, before + len * 2);
+ bl.c_str();
+ size_t after_c_str = mempool::osd::allocated_bytes();
+ ASSERT_EQ(after, after_c_str);
+}
+
+TEST(mempool, btree_map_test)
+{
+ typedef mempool::pool_allocator<mempool::mempool_osd,
+ pair<const uint64_t,uint64_t>> allocator_t;
+ typedef btree::btree_map<uint64_t,uint64_t,std::less<uint64_t>,allocator_t> btree_t;
+
+ {
+ btree_t btree;
+ ASSERT_EQ(0, mempool::osd::allocated_items());
+ ASSERT_EQ(0, mempool::osd::allocated_bytes());
+ for (size_t i = 0; i < 1000; ++i) {
+ btree[rand()] = rand();
+ }
+ ASSERT_LT(0, mempool::osd::allocated_items());
+ ASSERT_LT(0, mempool::osd::allocated_bytes());
+ }
+
+ ASSERT_EQ(0, mempool::osd::allocated_items());
+ ASSERT_EQ(0, mempool::osd::allocated_bytes());
+}
+
+TEST(mempool, check_shard_select)
+{
+ const size_t samples = mempool::num_shards * 100;
+ std::atomic_int shards[mempool::num_shards] = {0};
+ std::vector<std::thread> workers;
+ for (size_t i = 0; i < samples; i++) {
+ workers.push_back(
+ std::thread([&](){
+ size_t i = mempool::pool_t::pick_a_shard_int();
+ shards[i]++;
+ }));
+ }
+ for (auto& t:workers) {
+ t.join();
+ }
+ workers.clear();
+
+ size_t missed = 0;
+ for (size_t i = 0; i < mempool::num_shards; i++) {
+ if (shards[i] == 0) {
+ missed++;
+ }
+ }
+
+ // If more than half of the shards did not get anything,
+ // the distribution is bad enough to deserve a failure.
+ EXPECT_LT(missed, mempool::num_shards / 2);
+}
+
+
+int main(int argc, char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ // enable debug mode for the tests
+ mempool::set_debug_mode(true);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../../build ; make -j4 &&
+ * make unittest_mempool &&
+ * valgrind --tool=memcheck ./unittest_mempool --gtest_filter=*.*"
+ * End:
+ */
diff --git a/src/test/test_missing_unfound.sh b/src/test/test_missing_unfound.sh
new file mode 100755
index 000000000..d5db1e42d
--- /dev/null
+++ b/src/test/test_missing_unfound.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+CEPH_NUM_OSD=3 ./vstart.sh -d -n -x -o 'osd recovery max active = 1'
+
+TEST_POOL=rbd
+
+./ceph -c ./ceph.conf osd pool set $TEST_POOL size 3
+
+sleep 20
+
+./init-ceph stop osd.1
+./ceph osd down 1 # faster
+
+for f in `seq 1 100`
+do
+ ./rados -c ./ceph.conf -p $TEST_POOL put test_$f /etc/passwd
+done
+
+# zap some objects on both replicas
+#rm dev/osd[02]/current/*/test_40*
+
+# some on only one
+rm dev/osd0/current/*/test_*
+#rm dev/osd2/current/*/test_6*
+
+# ...and see how we fare!
+./init-ceph start osd.1
+
+
diff --git a/src/test/test_mutate.cc b/src/test/test_mutate.cc
new file mode 100644
index 000000000..04475f617
--- /dev/null
+++ b/src/test/test_mutate.cc
@@ -0,0 +1,112 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+/*
+ * Test Ioctx::operate
+ */
+
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "common/config.h"
+#include "global/global_init.h"
+#include "include/rados/librados.hpp"
+#include "include/types.h"
+
+#include <errno.h>
+#include <iostream>
+#include <string>
+
+using std::cerr;
+using std::string;
+
+using namespace librados;
+
+static void usage(void)
+{
+ cerr << "--oid set object id to 'operate' on" << std::endl;
+ cerr << "--pool set pool to 'operate' on" << std::endl;
+}
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ string val;
+ string oid("ceph_test_object");
+ string pool_name("test_pool");
+ for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
+ if (ceph_argparse_double_dash(args, i)) {
+ break;
+ }
+ else if (ceph_argparse_witharg(args, i, &val, "--oid", "-o", (char*)NULL)) {
+ oid = val;
+ }
+ else if (ceph_argparse_witharg(args, i, &val, "--pool", "-p", (char*)NULL)) {
+ pool_name = val;
+ }
+ else {
+ cerr << "unknown command line option: " << *i << std::endl;
+ cerr << std::endl;
+ usage();
+ return 2;
+ }
+ }
+
+ Rados rados;
+ if (rados.init_with_context(g_ceph_context) < 0) {
+ cerr << "couldn't initialize rados!" << std::endl;
+ return 1;
+ }
+ if (rados.conf_read_file(NULL) < 0) {
+ cerr << "failed to read rados configuration file!" << std::endl;
+ return 1;
+ }
+ if (rados.connect() < 0) {
+ cerr << "couldn't connect to cluster!" << std::endl;
+ return 1;
+ }
+
+ int ret = 0;
+ librados::ObjectWriteOperation o;
+ IoCtx ioctx;
+ if (rados.pool_lookup(pool_name.c_str()) <= 0) {
+ ret = rados.pool_create(pool_name.c_str());
+ if (ret) {
+ cerr << "failed to create pool named '" << pool_name
+ << "': error " << ret << std::endl;
+ return 1;
+ }
+ }
+ ret = rados.ioctx_create(pool_name.c_str(), ioctx);
+ if (ret) {
+ cerr << "failed to create ioctx for pool '" << pool_name
+ << "': error " << ret << std::endl;
+ return 1;
+ }
+ ioctx.application_enable("rados", true);
+
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ ret = ioctx.operate(oid, &op);
+ if (ret) {
+ cerr << "ioctx.operate failed: ret = " << ret << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/test/test_objectstore_memstore.sh b/src/test/test_objectstore_memstore.sh
new file mode 100755
index 000000000..78c323773
--- /dev/null
+++ b/src/test/test_objectstore_memstore.sh
@@ -0,0 +1,6 @@
+#!/bin/sh -ex
+
+rm -rf memstore.test_temp_dir
+ceph_test_objectstore --gtest_filter=\*/0
+
+echo OK
diff --git a/src/test/test_pageset.cc b/src/test/test_pageset.cc
new file mode 100644
index 000000000..a699e727a
--- /dev/null
+++ b/src/test/test_pageset.cc
@@ -0,0 +1,283 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "os/memstore/PageSet.h"
+
+template <typename T>
+bool is_aligned(T* ptr) {
+ const auto align_mask = alignof(T) - 1;
+ return (reinterpret_cast<uintptr_t>(ptr) & align_mask) == 0;
+}
+
+TEST(PageSet, AllocAligned)
+{
+ PageSet pages(1);
+ PageSet::page_vector range;
+
+ pages.alloc_range(0, 4, range);
+ ASSERT_EQ(4u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(1u, range[1]->offset);
+ ASSERT_EQ(2u, range[2]->offset);
+ ASSERT_EQ(3u, range[3]->offset);
+
+ // verify that the Page pointers are properly aligned
+ ASSERT_TRUE(is_aligned(range[0].get()));
+ ASSERT_TRUE(is_aligned(range[1].get()));
+ ASSERT_TRUE(is_aligned(range[2].get()));
+ ASSERT_TRUE(is_aligned(range[3].get()));
+}
+
+TEST(PageSet, AllocUnaligned)
+{
+ PageSet pages(2);
+ PageSet::page_vector range;
+
+ // front of first page
+ pages.alloc_range(0, 1, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ range.clear();
+
+ // back of first page
+ pages.alloc_range(1, 1, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ range.clear();
+
+ // back of first page and front of second
+ pages.alloc_range(1, 2, range);
+ ASSERT_EQ(2u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ range.clear();
+
+ // back of first page and all of second
+ pages.alloc_range(1, 3, range);
+ ASSERT_EQ(2u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ range.clear();
+
+ // back of first page, all of second, and front of third
+ pages.alloc_range(1, 4, range);
+ ASSERT_EQ(3u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ ASSERT_EQ(4u, range[2]->offset);
+}
+
+TEST(PageSet, GetAligned)
+{
+ // allocate 4 pages
+ PageSet pages(1);
+ PageSet::page_vector range;
+ pages.alloc_range(0, 4, range);
+ range.clear();
+
+ // get first page
+ pages.get_range(0, 1, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ range.clear();
+
+ // get second and third pages
+ pages.get_range(1, 2, range);
+ ASSERT_EQ(2u, range.size());
+ ASSERT_EQ(1u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ range.clear();
+
+ // get all four pages
+ pages.get_range(0, 4, range);
+ ASSERT_EQ(4u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(1u, range[1]->offset);
+ ASSERT_EQ(2u, range[2]->offset);
+ ASSERT_EQ(3u, range[3]->offset);
+ range.clear();
+}
+
+TEST(PageSet, GetUnaligned)
+{
+ // allocate 3 pages
+ PageSet pages(2);
+ PageSet::page_vector range;
+ pages.alloc_range(0, 6, range);
+ range.clear();
+
+ // front of first page
+ pages.get_range(0, 1, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ range.clear();
+
+ // back of first page
+ pages.get_range(1, 1, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ range.clear();
+
+ // back of first page and front of second
+ pages.get_range(1, 2, range);
+ ASSERT_EQ(2u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ range.clear();
+
+ // back of first page and all of second
+ pages.get_range(1, 3, range);
+ ASSERT_EQ(2u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ range.clear();
+
+ // back of first page, all of second, and front of third
+ pages.get_range(1, 4, range);
+ ASSERT_EQ(3u, range.size());
+ ASSERT_EQ(0u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ ASSERT_EQ(4u, range[2]->offset);
+ range.clear();
+
+ // back of third page with nothing beyond
+ pages.get_range(5, 999, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(4u, range[0]->offset);
+ range.clear();
+}
+
+TEST(PageSet, GetHoles)
+{
+ // allocate pages at offsets 1, 2, 5, and 7
+ PageSet pages(1);
+ PageSet::page_vector range;
+ for (uint64_t i : {1, 2, 5, 7})
+ pages.alloc_range(i, 1, range);
+ range.clear();
+
+ // nothing at offset 0, page at offset 1
+ pages.get_range(0, 2, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(1u, range[0]->offset);
+ range.clear();
+
+ // nothing at offset 0, pages at offset 1 and 2, nothing at offset 3
+ pages.get_range(0, 4, range);
+ ASSERT_EQ(2u, range.size());
+ ASSERT_EQ(1u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ range.clear();
+
+ // page at offset 2, nothing at offset 3 or 4
+ pages.get_range(2, 3, range);
+ ASSERT_EQ(1u, range.size());
+ ASSERT_EQ(2u, range[0]->offset);
+ range.clear();
+
+ // get the full range
+ pages.get_range(0, 999, range);
+ ASSERT_EQ(4u, range.size());
+ ASSERT_EQ(1u, range[0]->offset);
+ ASSERT_EQ(2u, range[1]->offset);
+ ASSERT_EQ(5u, range[2]->offset);
+ ASSERT_EQ(7u, range[3]->offset);
+ range.clear();
+}
+
+TEST(PageSet, FreeAligned)
+{
+ // allocate 4 pages
+ PageSet pages(1);
+ PageSet::page_vector range;
+ pages.alloc_range(0, 4, range);
+ range.clear();
+
+ // get the full range
+ pages.get_range(0, 4, range);
+ ASSERT_EQ(4u, range.size());
+ range.clear();
+
+ // free after offset 4 has no effect
+ pages.free_pages_after(4);
+ pages.get_range(0, 4, range);
+ ASSERT_EQ(4u, range.size());
+ range.clear();
+
+ // free page 4
+ pages.free_pages_after(3);
+ pages.get_range(0, 4, range);
+ ASSERT_EQ(3u, range.size());
+ range.clear();
+
+ // free pages 2 and 3
+ pages.free_pages_after(1);
+ pages.get_range(0, 4, range);
+ ASSERT_EQ(1u, range.size());
+ range.clear();
+}
+
+TEST(PageSet, FreeUnaligned)
+{
+ // allocate 4 pages
+ PageSet pages(2);
+ PageSet::page_vector range;
+ pages.alloc_range(0, 8, range);
+ range.clear();
+
+ // get the full range
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(4u, range.size());
+ range.clear();
+
+ // free after offset 7 has no effect
+ pages.free_pages_after(7);
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(4u, range.size());
+ range.clear();
+
+ // free page 4
+ pages.free_pages_after(5);
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(3u, range.size());
+ range.clear();
+
+ // free pages 2 and 3
+ pages.free_pages_after(1);
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(1u, range.size());
+ range.clear();
+}
+
+TEST(PageSet, FreeHoles)
+{
+ // allocate pages at offsets 1, 2, 5, and 7
+ PageSet pages(1);
+ PageSet::page_vector range;
+ for (uint64_t i : {1, 2, 5, 7})
+ pages.alloc_range(i, 1, range);
+ range.clear();
+
+ // get the full range
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(4u, range.size());
+ range.clear();
+
+ // free page 7
+ pages.free_pages_after(6);
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(3u, range.size());
+ range.clear();
+
+ // free page 5
+ pages.free_pages_after(3);
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(2u, range.size());
+ range.clear();
+
+ // free pages 1 and 2
+ pages.free_pages_after(0);
+ pages.get_range(0, 8, range);
+ ASSERT_EQ(0u, range.size());
+}
diff --git a/src/test/test_pidfile.sh b/src/test/test_pidfile.sh
new file mode 100755
index 000000000..704858d16
--- /dev/null
+++ b/src/test/test_pidfile.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+
+#
+# test pidfile here
+#
+
+# Includes
+source $(dirname $0)/detect-build-env-vars.sh
+source $CEPH_ROOT/qa/standalone/ceph-helpers.sh
+
+function run() {
+ local dir=$1
+ shift
+
+ export CEPH_MON="127.0.0.1:7124" # git grep '\<7124\>' : there must be only one
+ export CEPH_ARGS
+ CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
+ CEPH_ARGS+="--mon-host=$CEPH_MON "
+
+ local funcs=${@:-$(set | sed -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')}
+ for func in $funcs ; do
+ $func $dir || return 1
+ done
+}
+
+function TEST_without_pidfile() {
+ local dir=$1
+ setup $dir
+ local data=$dir/osd1
+ local id=1
+ ceph-mon \
+ --id $id \
+ --mkfs \
+ --mon-data=$data \
+ --run-dir=$dir || return 1
+ expect_failure $dir "ignore empty --pid-file" ceph-mon \
+ -f \
+ --log-to-stderr \
+ --log_flush_on_exit \
+ --pid-file= \
+ --id $id \
+ --mon-data=$data \
+ --run-dir=$dir || return 1
+ teardown $dir
+}
+
+function TEST_pidfile() {
+ local dir=$1
+ setup $dir
+
+ # no daemon can use a pidfile that is owned by another daemon
+ run_mon $dir a || return 1
+ sleep 5
+ run_mon $dir a --log-to-stderr -f 2>&1 | grep "failed to lock pidfile" || return 1
+
+ run_osd $dir 0 || return 1
+ sleep 5
+ activate_osd $dir 0 --log-to-stderr -f 2>&1 | grep "failed to lock pidfile" || return 1
+
+ # when a daemon shutdown, it will not unlink a path different from
+ # the one it owns
+ mv $dir/osd.0.pid $dir/osd.0.pid.old || return 1
+ cp $dir/osd.0.pid.old $dir/osd.0.pid || return 1
+ kill_daemons $dir TERM osd.0 || return 1
+ test -f $dir/osd.0.pid || return 1
+
+ # when a daemon starts, it re-uses the pid file if no other daemon
+ # has it locked
+ run_osd $dir 0 || return 1
+ ! cmp $dir/osd.0.pid $dir/osd.0.pid.old || return 1
+
+ # if the pid in the file is different from the pid of the daemon
+ # the file is not removed because it is assumed to be owned by
+ # another daemon
+ mkdir $dir/old
+ cp $dir/osd.0.pid $dir/old/osd.0.pid # so that kill_daemon finds the pid
+ echo 123 > $dir/osd.0.pid
+ kill_daemons $dir/old TERM osd.0 || return 1
+ test -f $dir/osd.0.pid || return 1
+
+ # when the daemon shutdown, it removes its own pid file
+ test -f $dir/mon.a.pid || return 1
+ kill_daemons $dir TERM mon.a || return 1
+ ! test -f $dir/mon.a.pid || return 1
+
+ teardown $dir || return 1
+}
+
+main pidfile "$@"
+
diff --git a/src/test/test_pools.sh b/src/test/test_pools.sh
new file mode 100755
index 000000000..fc3764774
--- /dev/null
+++ b/src/test/test_pools.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+set -x
+
+#
+# Test pools
+#
+
+# Includes
+source "`dirname $0`/test_common.sh"
+
+# Functions
+setup() {
+ export CEPH_NUM_OSD=$1
+
+ # Start ceph
+ ./stop.sh
+
+ ./vstart.sh -d -n || die "vstart failed"
+}
+
+test629_impl() {
+ # create the pool
+ ./ceph -c ./ceph.conf osd pool create foo 8 || die "pool create failed"
+
+ # Write lots and lots of objects
+ write_objects 1 1 10 1000000 foo
+
+ # Take down first osd
+ stop_osd 0
+
+ # Now degraded PGs should exist
+ poll_cmd "./ceph pg debug degraded_pgs_exist" TRUE 3 120
+
+ # delete the pool
+ ./ceph -c ./ceph.conf osd pool rm foo foo --yes-i-really-really-mean-it || die "pool rm failed"
+
+ # make sure the system is stable
+ sleep 10
+}
+
+test629(){
+ setup 3
+ test629_impl
+}
+
+run() {
+ test629 || die "test failed"
+}
+
+$@
diff --git a/src/test/test_rados_tool.sh b/src/test/test_rados_tool.sh
new file mode 120000
index 000000000..4c009dadb
--- /dev/null
+++ b/src/test/test_rados_tool.sh
@@ -0,0 +1 @@
+../../qa/workunits/rados/test_rados_tool.sh \ No newline at end of file
diff --git a/src/test/test_random_string.cc b/src/test/test_random_string.cc
new file mode 100644
index 000000000..fa198a346
--- /dev/null
+++ b/src/test/test_random_string.cc
@@ -0,0 +1,120 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/random_string.h"
+#include "common/ceph_context.h"
+#include "global/global_context.h"
+#include <gtest/gtest.h>
+
+inline bool is_alphanumeric_lower(char c) {
+ return std::islower(c) || std::isdigit(c);
+}
+inline bool is_alphanumeric_upper(char c) {
+ return std::isupper(c) || std::isdigit(c);
+}
+inline bool is_alphanumeric_plain(char c) {
+ return std::islower(c) || std::isupper(c) || std::isdigit(c);
+}
+inline bool is_alphanumeric_no_underscore(char c) {
+ return is_alphanumeric_plain(c) || c == '-' || c == '.';
+}
+inline bool is_alphanumeric(char c) {
+ return is_alphanumeric_plain(c) || c == '-' || c == '_';
+}
+inline bool is_base64(char c) {
+ return is_alphanumeric_plain(c) || c == '+' || c == '/';
+}
+
+TEST(RandomString, base64)
+{
+ char arr[65] = {};
+ ASSERT_EQ(0, gen_rand_base64(g_ceph_context, arr, sizeof(arr)));
+ EXPECT_EQ(0, arr[64]); // must be null terminated
+ EXPECT_TRUE(std::all_of(arr, arr + 64, is_base64));
+}
+
+TEST(RandomString, alphanumeric)
+{
+ char arr[65] = {};
+ gen_rand_alphanumeric(g_ceph_context, arr, sizeof(arr));
+ EXPECT_EQ(0, arr[64]);
+ EXPECT_TRUE(std::all_of(arr, arr + 64, is_alphanumeric));
+}
+
+TEST(RandomString, alphanumeric_string)
+{
+ std::string str = gen_rand_alphanumeric(g_ceph_context, 64);
+ EXPECT_EQ(64, str.size());
+ EXPECT_TRUE(std::all_of(str.begin(), str.end(), is_alphanumeric));
+}
+
+TEST(RandomString, alphanumeric_lower)
+{
+ char arr[65] = {};
+ gen_rand_alphanumeric_lower(g_ceph_context, arr, sizeof(arr));
+ EXPECT_EQ(0, arr[64]);
+ EXPECT_TRUE(std::all_of(arr, arr + 64, is_alphanumeric_lower));
+}
+
+TEST(RandomString, alphanumeric_lower_string)
+{
+ std::string str = gen_rand_alphanumeric_lower(g_ceph_context, 64);
+ EXPECT_EQ(64, str.size());
+ EXPECT_TRUE(std::all_of(str.begin(), str.end(), is_alphanumeric_lower));
+}
+
+TEST(RandomString, alphanumeric_upper)
+{
+ char arr[65] = {};
+ gen_rand_alphanumeric_upper(g_ceph_context, arr, sizeof(arr));
+ EXPECT_EQ(0, arr[64]);
+ EXPECT_TRUE(std::all_of(arr, arr + 64, is_alphanumeric_upper));
+}
+
+TEST(RandomString, alphanumeric_upper_string)
+{
+ std::string str = gen_rand_alphanumeric_upper(g_ceph_context, 64);
+ EXPECT_EQ(64, str.size());
+ EXPECT_TRUE(std::all_of(str.begin(), str.end(), is_alphanumeric_upper));
+}
+
+TEST(RandomString, alphanumeric_no_underscore)
+{
+ char arr[65] = {};
+ gen_rand_alphanumeric_no_underscore(g_ceph_context, arr, sizeof(arr));
+ EXPECT_EQ(0, arr[64]);
+ EXPECT_TRUE(std::all_of(arr, arr + 64, is_alphanumeric_no_underscore));
+}
+
+TEST(RandomString, alphanumeric_no_underscore_string)
+{
+ std::string str = gen_rand_alphanumeric_no_underscore(g_ceph_context, 64);
+ EXPECT_EQ(64, str.size());
+ EXPECT_TRUE(std::all_of(str.begin(), str.end(), is_alphanumeric_no_underscore));
+}
+
+TEST(RandomString, alphanumeric_plain)
+{
+ char arr[65] = {};
+ gen_rand_alphanumeric_plain(g_ceph_context, arr, sizeof(arr));
+ EXPECT_EQ(0, arr[64]);
+ EXPECT_TRUE(std::all_of(arr, arr + 64, is_alphanumeric_plain));
+}
+
+TEST(RandomString, alphanumeric_plain_string)
+{
+ std::string str = gen_rand_alphanumeric_plain(g_ceph_context, 64);
+ EXPECT_EQ(64, str.size());
+ EXPECT_TRUE(std::all_of(str.begin(), str.end(), is_alphanumeric_plain));
+}
diff --git a/src/test/test_rbd_replay.cc b/src/test/test_rbd_replay.cc
new file mode 100644
index 000000000..c4a0b3a9e
--- /dev/null
+++ b/src/test/test_rbd_replay.cc
@@ -0,0 +1,136 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Adam Crume <adamcrume@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/escape.h"
+#include "gtest/gtest.h"
+#include <stdint.h>
+#include <boost/foreach.hpp>
+#include <cstdarg>
+#include "rbd_replay/ImageNameMap.hpp"
+#include "rbd_replay/ios.hpp"
+#include "rbd_replay/rbd_loc.hpp"
+
+
+namespace rbd_replay {
+std::ostream& operator<<(std::ostream& o, const rbd_loc& name) {
+ return o << "('" << name.pool << "', '" << name.image << "', '" << name.snap << "')";
+}
+}
+
+using namespace rbd_replay;
+
+static void add_mapping(ImageNameMap *map, std::string mapping_string) {
+ ImageNameMap::Mapping mapping;
+ if (!map->parse_mapping(mapping_string, &mapping)) {
+ ASSERT_TRUE(false) << "Failed to parse mapping string '" << mapping_string << "'";
+ }
+ map->add_mapping(mapping);
+}
+
+TEST(RBDReplay, ImageNameMap) {
+ ImageNameMap m;
+ add_mapping(&m, "x@y=y@x");
+ add_mapping(&m, "a\\=b@c=h@i");
+ add_mapping(&m, "a@b\\=c=j@k");
+ add_mapping(&m, "a\\@b@c=d@e");
+ add_mapping(&m, "a@b\\@c=f@g");
+ add_mapping(&m, "image@snap_1=image_1");
+ ImageNameMap::Mapping mapping;
+ EXPECT_FALSE(m.parse_mapping("bad=@@@", &mapping));
+ EXPECT_FALSE(m.parse_mapping("bad==stuff", &mapping));
+ EXPECT_EQ(rbd_loc("", "y", "x"), m.map(rbd_loc("", "x", "y")));
+ EXPECT_EQ(rbd_loc("", "h", "i"), m.map(rbd_loc("", "a=b", "c")));
+ EXPECT_EQ(rbd_loc("", "j", "k"), m.map(rbd_loc("", "a", "b=c")));
+ EXPECT_EQ(rbd_loc("", "d", "e"), m.map(rbd_loc("", "a@b", "c")));
+ EXPECT_EQ(rbd_loc("", "f", "g"), m.map(rbd_loc("", "a", "b@c")));
+ EXPECT_EQ(rbd_loc("", "image_1", ""), m.map(rbd_loc("", "image", "snap_1")));
+}
+
+TEST(RBDReplay, rbd_loc_str) {
+ EXPECT_EQ("", rbd_loc("", "", "").str());
+ EXPECT_EQ("a/", rbd_loc("a", "", "").str());
+ EXPECT_EQ("b", rbd_loc("", "b", "").str());
+ EXPECT_EQ("a/b", rbd_loc("a", "b", "").str());
+ EXPECT_EQ("@c", rbd_loc("", "", "c").str());
+ EXPECT_EQ("a/@c", rbd_loc("a", "", "c").str());
+ EXPECT_EQ("b@c", rbd_loc("", "b", "c").str());
+ EXPECT_EQ("a/b@c", rbd_loc("a", "b", "c").str());
+ EXPECT_EQ("a\\@x/b\\@y@c\\@z", rbd_loc("a@x", "b@y", "c@z").str());
+ EXPECT_EQ("a\\/x/b\\/y@c\\/z", rbd_loc("a/x", "b/y", "c/z").str());
+ EXPECT_EQ("a\\\\x/b\\\\y@c\\\\z", rbd_loc("a\\x", "b\\y", "c\\z").str());
+}
+
+TEST(RBDReplay, rbd_loc_parse) {
+ rbd_loc m("x", "y", "z");
+
+ EXPECT_TRUE(m.parse(""));
+ EXPECT_EQ("", m.pool);
+ EXPECT_EQ("", m.image);
+ EXPECT_EQ("", m.snap);
+
+ EXPECT_TRUE(m.parse("a/"));
+ EXPECT_EQ("a", m.pool);
+ EXPECT_EQ("", m.image);
+ EXPECT_EQ("", m.snap);
+
+ EXPECT_TRUE(m.parse("b"));
+ EXPECT_EQ("", m.pool);
+ EXPECT_EQ("b", m.image);
+ EXPECT_EQ("", m.snap);
+
+ EXPECT_TRUE(m.parse("a/b"));
+ EXPECT_EQ("a", m.pool);
+ EXPECT_EQ("b", m.image);
+ EXPECT_EQ("", m.snap);
+
+ EXPECT_TRUE(m.parse("@c"));
+ EXPECT_EQ("", m.pool);
+ EXPECT_EQ("", m.image);
+ EXPECT_EQ("c", m.snap);
+
+ EXPECT_TRUE(m.parse("a/@c"));
+ EXPECT_EQ("a", m.pool);
+ EXPECT_EQ("", m.image);
+ EXPECT_EQ("c", m.snap);
+
+ EXPECT_TRUE(m.parse("b@c"));
+ EXPECT_EQ("", m.pool);
+ EXPECT_EQ("b", m.image);
+ EXPECT_EQ("c", m.snap);
+
+ EXPECT_TRUE(m.parse("a/b@c"));
+ EXPECT_EQ("a", m.pool);
+ EXPECT_EQ("b", m.image);
+ EXPECT_EQ("c", m.snap);
+
+ EXPECT_TRUE(m.parse("a\\@x/b\\@y@c\\@z"));
+ EXPECT_EQ("a@x", m.pool);
+ EXPECT_EQ("b@y", m.image);
+ EXPECT_EQ("c@z", m.snap);
+
+ EXPECT_TRUE(m.parse("a\\/x/b\\/y@c\\/z"));
+ EXPECT_EQ("a/x", m.pool);
+ EXPECT_EQ("b/y", m.image);
+ EXPECT_EQ("c/z", m.snap);
+
+ EXPECT_TRUE(m.parse("a\\\\x/b\\\\y@c\\\\z"));
+ EXPECT_EQ("a\\x", m.pool);
+ EXPECT_EQ("b\\y", m.image);
+ EXPECT_EQ("c\\z", m.snap);
+
+ EXPECT_FALSE(m.parse("a@b@c"));
+ EXPECT_FALSE(m.parse("a/b/c"));
+ EXPECT_FALSE(m.parse("a@b/c"));
+}
+
diff --git a/src/test/test_rewrite_latency.cc b/src/test/test_rewrite_latency.cc
new file mode 100644
index 000000000..348c8dde5
--- /dev/null
+++ b/src/test/test_rewrite_latency.cc
@@ -0,0 +1,47 @@
+
+#include <unistd.h>
+#include <map>
+#include <errno.h>
+
+#include "include/utime.h"
+#include "common/Clock.h"
+#include "common/errno.h"
+
+using namespace std;
+
+int main(int argc, const char **argv)
+{
+ const char *fn = argv[1];
+ multimap<utime_t, utime_t> latency;
+ unsigned max = 10;
+
+ int fd = ::open(fn, O_CREAT|O_RDWR, 0644);
+ if (fd < 1) {
+ int err = errno;
+ cerr << "failed to open " << fn << " with " << cpp_strerror(err) << std::endl;
+ return -1;
+ }
+
+ while (true) {
+ utime_t now = ceph_clock_now();
+ int r = ::pwrite(fd, fn, strlen(fn), 0);
+ ceph_assert(r >= 0);
+ utime_t lat = ceph_clock_now();
+ lat -= now;
+ utime_t oldmin;
+ if (!latency.empty())
+ oldmin = latency.begin()->first;
+ latency.insert(make_pair(lat, now));
+ utime_t newmin = latency.begin()->first;
+ while (latency.size() > max)
+ latency.erase(latency.begin());
+ if (oldmin == newmin) {
+ cout << "latency\tat" << std::endl;
+ for (multimap<utime_t,utime_t>::reverse_iterator p = latency.rbegin();
+ p != latency.rend();
+ ++p) {
+ cout << p->first << "\t" << p->second << std::endl;
+ }
+ }
+ }
+}
diff --git a/src/test/test_rgw_admin_log.cc b/src/test/test_rgw_admin_log.cc
new file mode 100644
index 000000000..fe072ead1
--- /dev/null
+++ b/src/test/test_rgw_admin_log.cc
@@ -0,0 +1,1590 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fstream>
+#include <map>
+#include <list>
+extern "C"{
+#include <curl/curl.h>
+}
+#include "common/ceph_crypto.h"
+#include "include/str_list.h"
+#include "common/ceph_json.h"
+#include "common/code_environment.h"
+#include "common/ceph_argparse.h"
+#include "common/Finisher.h"
+#include "global/global_init.h"
+#include "rgw_common.h"
+#include "rgw_datalog.h"
+#include "rgw_mdlog.h"
+#include "rgw_bucket.h"
+#include "rgw_rados.h"
+#include "include/utime.h"
+#include "include/object.h"
+#include <gtest/gtest.h>
+
+using namespace std;
+
+#define CURL_VERBOSE 0
+#define HTTP_RESPONSE_STR "RespCode"
+#define CEPH_CRYPTO_HMACSHA1_DIGESTSIZE 20
+#define RGW_ADMIN_RESP_PATH "/tmp/.test_rgw_admin_resp"
+#define TEST_BUCKET_NAME "test_bucket"
+#define TEST_BUCKET_OBJECT "test_object"
+#define TEST_BUCKET_OBJECT_1 "test_object1"
+#define TEST_BUCKET_OBJECT_SIZE 1024
+
+static string uid = "ceph";
+static string display_name = "CEPH";
+
+extern "C" int ceph_armor(char *dst, const char *dst_end,
+ const char *src, const char *end);
+static void print_usage(char *exec){
+ cout << "Usage: " << exec << " <Options>\n";
+ cout << "Options:\n"
+ "-g <gw-ip> - The ip address of the gateway\n"
+ "-p <gw-port> - The port number of the gateway\n"
+ "-c <ceph.conf> - Absolute path of ceph config file\n"
+ "-rgw-admin <path/to/radosgw-admin> - radosgw-admin absolute path\n";
+}
+
+namespace admin_log {
+class test_helper {
+ private:
+ string host;
+ string port;
+ string creds;
+ string rgw_admin_path;
+ string conf_path;
+ CURL *curl_inst;
+ map<string, string> response;
+ list<string> extra_hdrs;
+ string *resp_data;
+ unsigned resp_code;
+ public:
+ test_helper() : resp_data(NULL){
+ curl_global_init(CURL_GLOBAL_ALL);
+ }
+ ~test_helper(){
+ curl_global_cleanup();
+ }
+ int send_request(string method, string uri,
+ size_t (*function)(void *,size_t,size_t,void *) = 0,
+ void *ud = 0, size_t length = 0);
+ int extract_input(int argc, char *argv[]);
+ string& get_response(string hdr){
+ return response[hdr];
+ }
+ void set_extra_header(string hdr){
+ extra_hdrs.push_back(hdr);
+ }
+ void set_response(char *val);
+ void set_response_data(char *data, size_t len){
+ if(resp_data) delete resp_data;
+ resp_data = new string(data, len);
+ }
+ string& get_rgw_admin_path() {
+ return rgw_admin_path;
+ }
+ string& get_ceph_conf_path() {
+ return conf_path;
+ }
+ void set_creds(string& c) {
+ creds = c;
+ }
+ const string *get_response_data(){return resp_data;}
+ unsigned get_resp_code(){return resp_code;}
+};
+
+int test_helper::extract_input(int argc, char *argv[]){
+#define ERR_CHECK_NEXT_PARAM(o) \
+ if(((int)loop + 1) >= argc)return -1; \
+ else o = argv[loop+1];
+
+ for(unsigned loop = 1;loop < (unsigned)argc; loop += 2){
+ if(strcmp(argv[loop], "-g") == 0){
+ ERR_CHECK_NEXT_PARAM(host);
+ }else if(strcmp(argv[loop],"-p") == 0){
+ ERR_CHECK_NEXT_PARAM(port);
+ }else if(strcmp(argv[loop], "-c") == 0){
+ ERR_CHECK_NEXT_PARAM(conf_path);
+ }else if(strcmp(argv[loop], "-rgw-admin") == 0){
+ ERR_CHECK_NEXT_PARAM(rgw_admin_path);
+ }else return -1;
+ }
+ if(!host.length() || !rgw_admin_path.length())
+ return -1;
+ return 0;
+}
+
+void test_helper::set_response(char *r){
+ string sr(r), h, v;
+ size_t off = sr.find(": ");
+ if(off != string::npos){
+ h.assign(sr, 0, off);
+ v.assign(sr, off + 2, sr.find("\r\n") - (off+2));
+ }else{
+ /*Could be the status code*/
+ if(sr.find("HTTP/") != string::npos){
+ h.assign(HTTP_RESPONSE_STR);
+ off = sr.find(" ");
+ v.assign(sr, off + 1, sr.find("\r\n") - (off + 1));
+ resp_code = atoi((v.substr(0, 3)).c_str());
+ }
+ }
+ response[h] = v;
+}
+
+size_t write_header(void *ptr, size_t size, size_t nmemb, void *ud){
+ test_helper *h = static_cast<test_helper *>(ud);
+ h->set_response((char *)ptr);
+ return size*nmemb;
+}
+
+size_t write_data(void *ptr, size_t size, size_t nmemb, void *ud){
+ test_helper *h = static_cast<test_helper *>(ud);
+ h->set_response_data((char *)ptr, size*nmemb);
+ return size*nmemb;
+}
+
+static inline void buf_to_hex(const unsigned char *buf, int len, char *str)
+{
+ int i;
+ str[0] = '\0';
+ for (i = 0; i < len; i++) {
+ sprintf(&str[i*2], "%02x", (int)buf[i]);
+ }
+}
+
+static void calc_hmac_sha1(const char *key, int key_len,
+ const char *msg, int msg_len, char *dest)
+/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
+{
+ ceph::crypto::HMACSHA1 hmac((const unsigned char *)key, key_len);
+ hmac.Update((const unsigned char *)msg, msg_len);
+ hmac.Final((unsigned char *)dest);
+
+ char hex_str[(CEPH_CRYPTO_HMACSHA1_DIGESTSIZE * 2) + 1];
+ admin_log::buf_to_hex((unsigned char *)dest, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE, hex_str);
+}
+
+static int get_s3_auth(const string &method, string creds, const string &date, string res, string& out){
+ string aid, secret, auth_hdr;
+ string tmp_res;
+ size_t off = creds.find(":");
+ out = "";
+ if(off != string::npos){
+ aid.assign(creds, 0, off);
+ secret.assign(creds, off + 1, string::npos);
+
+ /*sprintf(auth_hdr, "%s\n\n\n%s\n%s", req_type, date, res);*/
+ char hmac_sha1[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
+ char b64[65]; /* 64 is really enough */
+ size_t off = res.find("?");
+ if(off == string::npos)
+ tmp_res = res;
+ else
+ tmp_res.assign(res, 0, off);
+ auth_hdr.append(method + string("\n\n\n") + date + string("\n") + tmp_res);
+ admin_log::calc_hmac_sha1(secret.c_str(), secret.length(),
+ auth_hdr.c_str(), auth_hdr.length(), hmac_sha1);
+ int ret = ceph_armor(b64, b64 + 64, hmac_sha1,
+ hmac_sha1 + CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+ if (ret < 0) {
+ cout << "ceph_armor failed\n";
+ return -1;
+ }
+ b64[ret] = 0;
+ out.append(aid + string(":") + b64);
+ }else return -1;
+ return 0;
+}
+
+void get_date(string& d){
+ struct timeval tv;
+ char date[64];
+ struct tm tm;
+ char *days[] = {(char *)"Sun", (char *)"Mon", (char *)"Tue",
+ (char *)"Wed", (char *)"Thu", (char *)"Fri",
+ (char *)"Sat"};
+ char *months[] = {(char *)"Jan", (char *)"Feb", (char *)"Mar",
+ (char *)"Apr", (char *)"May", (char *)"Jun",
+ (char *)"Jul",(char *) "Aug", (char *)"Sep",
+ (char *)"Oct", (char *)"Nov", (char *)"Dec"};
+ gettimeofday(&tv, NULL);
+ gmtime_r(&tv.tv_sec, &tm);
+ sprintf(date, "%s, %d %s %d %d:%d:%d GMT",
+ days[tm.tm_wday],
+ tm.tm_mday, months[tm.tm_mon],
+ tm.tm_year + 1900,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ d = date;
+}
+
+int test_helper::send_request(string method, string res,
+ size_t (*read_function)( void *,size_t,size_t,void *),
+ void *ud,
+ size_t length){
+ string url;
+ string auth, date;
+ url.append(string("http://") + host);
+ if(port.length() > 0)url.append(string(":") + port);
+ url.append(res);
+ curl_inst = curl_easy_init();
+ if(curl_inst){
+ curl_easy_setopt(curl_inst, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_inst, CURLOPT_CUSTOMREQUEST, method.c_str());
+ curl_easy_setopt(curl_inst, CURLOPT_VERBOSE, CURL_VERBOSE);
+ curl_easy_setopt(curl_inst, CURLOPT_HEADERFUNCTION, admin_log::write_header);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEHEADER, (void *)this);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEFUNCTION, admin_log::write_data);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEDATA, (void *)this);
+ if(read_function){
+ curl_easy_setopt(curl_inst, CURLOPT_READFUNCTION, read_function);
+ curl_easy_setopt(curl_inst, CURLOPT_READDATA, (void *)ud);
+ curl_easy_setopt(curl_inst, CURLOPT_UPLOAD, 1L);
+ curl_easy_setopt(curl_inst, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
+ }
+
+ get_date(date);
+ string http_date;
+ http_date.append(string("Date: ") + date);
+
+ string s3auth;
+ if (admin_log::get_s3_auth(method, creds, date, res, s3auth) < 0)
+ return -1;
+ auth.append(string("Authorization: AWS ") + s3auth);
+
+ struct curl_slist *slist = NULL;
+ slist = curl_slist_append(slist, auth.c_str());
+ slist = curl_slist_append(slist, http_date.c_str());
+ for(list<string>::iterator it = extra_hdrs.begin();
+ it != extra_hdrs.end(); ++it){
+ slist = curl_slist_append(slist, (*it).c_str());
+ }
+ if(read_function)
+ curl_slist_append(slist, "Expect:");
+ curl_easy_setopt(curl_inst, CURLOPT_HTTPHEADER, slist);
+
+ response.erase(response.begin(), response.end());
+ extra_hdrs.erase(extra_hdrs.begin(), extra_hdrs.end());
+ CURLcode res = curl_easy_perform(curl_inst);
+ if(res != CURLE_OK){
+ cout << "Curl perform failed for " << url << ", res: " <<
+ curl_easy_strerror(res) << "\n";
+ return -1;
+ }
+ curl_slist_free_all(slist);
+ }
+ curl_easy_cleanup(curl_inst);
+ return 0;
+}
+};
+
+admin_log::test_helper *g_test;
+Finisher *finisher;
+
+int run_rgw_admin(string& cmd, string& resp) {
+ pid_t pid;
+ pid = fork();
+ if (pid == 0) {
+ /* child */
+ list<string> l;
+ get_str_list(cmd, " \t", l);
+ char *argv[l.size()];
+ unsigned loop = 1;
+
+ argv[0] = (char *)"radosgw-admin";
+ for (list<string>::iterator it = l.begin();
+ it != l.end(); ++it) {
+ argv[loop++] = (char *)(*it).c_str();
+ }
+ argv[loop] = NULL;
+ if (!freopen(RGW_ADMIN_RESP_PATH, "w+", stdout)) {
+ cout << "Unable to open stdout file" << std::endl;
+ }
+ execv((g_test->get_rgw_admin_path()).c_str(), argv);
+ } else if (pid > 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ if(WEXITSTATUS(status) != 0) {
+ cout << "Child exited with status " << WEXITSTATUS(status) << std::endl;
+ return -1;
+ }
+ }
+ ifstream in;
+ struct stat st;
+
+ if (stat(RGW_ADMIN_RESP_PATH, &st) < 0) {
+ cout << "Error stating the admin response file, errno " << errno << std::endl;
+ return -1;
+ } else {
+ char *data = (char *)malloc(st.st_size + 1);
+ in.open(RGW_ADMIN_RESP_PATH);
+ in.read(data, st.st_size);
+ in.close();
+ data[st.st_size] = 0;
+ resp = data;
+ free(data);
+ unlink(RGW_ADMIN_RESP_PATH);
+ /* cout << "radosgw-admin " << cmd << ": " << resp << std::endl; */
+ }
+ } else
+ return -1;
+ return 0;
+}
+
+int get_creds(string& json, string& creds) {
+ JSONParser parser;
+ if(!parser.parse(json.c_str(), json.length())) {
+ cout << "Error parsing create user response" << std::endl;
+ return -1;
+ }
+
+ RGWUserInfo info;
+ decode_json_obj(info, &parser);
+ creds = "";
+ for(map<string, RGWAccessKey>::iterator it = info.access_keys.begin();
+ it != info.access_keys.end(); ++it) {
+ RGWAccessKey _k = it->second;
+ /*cout << "accesskeys [ " << it->first << " ] = " <<
+ "{ " << _k.id << ", " << _k.key << ", " << _k.subuser << "}" << std::endl;*/
+ creds.append(it->first + string(":") + _k.key);
+ break;
+ }
+ return 0;
+}
+
+int user_create(string& uid, string& display_name, bool set_creds = true) {
+ stringstream ss;
+ string creds;
+ ss << "-c " << g_test->get_ceph_conf_path() << " user create --uid=" << uid
+ << " --display-name=" << display_name;
+
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error creating user" << std::endl;
+ return -1;
+ }
+ get_creds(out, creds);
+ if(set_creds)
+ g_test->set_creds(creds);
+ return 0;
+}
+
+int user_info(string& uid, string& display_name, RGWUserInfo& uinfo) {
+ stringstream ss;
+ ss << "-c " << g_test->get_ceph_conf_path() << " user info --uid=" << uid
+ << " --display-name=" << display_name;
+
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error reading user information" << std::endl;
+ return -1;
+ }
+ JSONParser parser;
+ if(!parser.parse(out.c_str(), out.length())) {
+ cout << "Error parsing create user response" << std::endl;
+ return -1;
+ }
+ decode_json_obj(uinfo, &parser);
+ return 0;
+}
+
+int user_rm(string& uid, string& display_name) {
+ stringstream ss;
+ ss << "-c " << g_test->get_ceph_conf_path() <<
+ " metadata rm --metadata-key=user:" << uid;
+
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error removing user" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+int caps_add(const char * name, const char *perm) {
+ stringstream ss;
+
+ ss << "-c " << g_test->get_ceph_conf_path() << " caps add --caps=" <<
+ name << "=" << perm << " --uid=" << uid;
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error creating user" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+int caps_rm(const char * name, const char *perm) {
+ stringstream ss;
+
+ ss << "-c " << g_test->get_ceph_conf_path() << " caps rm --caps=" <<
+ name << "=" << perm << " --uid=" << uid;
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error creating user" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+static int create_bucket(void){
+ g_test->send_request(string("PUT"), string("/" TEST_BUCKET_NAME));
+ if(g_test->get_resp_code() != 200U){
+ cout << "Error creating bucket, http code " << g_test->get_resp_code();
+ return -1;
+ }
+ return 0;
+}
+
+static int delete_bucket(void){
+ g_test->send_request(string("DELETE"), string("/" TEST_BUCKET_NAME));
+ if(g_test->get_resp_code() != 204U){
+ cout << "Error deleting bucket, http code " << g_test->get_resp_code();
+ return -1;
+ }
+ return 0;
+}
+
+size_t read_dummy_post(void *ptr, size_t s, size_t n, void *ud) {
+ int dummy = 0;
+ memcpy(ptr, &dummy, sizeof(dummy));
+ return sizeof(dummy);
+}
+
+size_t read_bucket_object(void *ptr, size_t s, size_t n, void *ud) {
+ memcpy(ptr, ud, TEST_BUCKET_OBJECT_SIZE);
+ return TEST_BUCKET_OBJECT_SIZE;
+}
+
+static int put_bucket_obj(const char *obj_name, char *data, unsigned len) {
+ string req = "/" TEST_BUCKET_NAME"/";
+ req.append(obj_name);
+ g_test->send_request(string("PUT"), req,
+ read_bucket_object, (void *)data, (size_t)len);
+ if (g_test->get_resp_code() != 200U) {
+ cout << "Errror sending object to the bucket, http_code " << g_test->get_resp_code();
+ return -1;
+ }
+ return 0;
+}
+
+static int read_bucket_obj(const char *obj_name) {
+ string req = "/" TEST_BUCKET_NAME"/";
+ req.append(obj_name);
+ g_test->send_request(string("GET"), req);
+ if (g_test->get_resp_code() != 200U) {
+ cout << "Errror sending object to the bucket, http_code " << g_test->get_resp_code();
+ return -1;
+ }
+ return 0;
+}
+
+static int delete_obj(const char *obj_name) {
+ string req = "/" TEST_BUCKET_NAME"/";
+ req.append(obj_name);
+ g_test->send_request(string("DELETE"), req);
+ if (g_test->get_resp_code() != 204U) {
+ cout << "Errror deleting object from bucket, http_code " << g_test->get_resp_code();
+ return -1;
+ }
+ return 0;
+}
+
+int get_formatted_time(string& ret) {
+ struct tm *tm = NULL;
+ char str_time[200];
+ const char *format = "%Y-%m-%d%%20%H:%M:%S";
+ time_t t;
+
+ t = time(NULL);
+ tm = gmtime(&t);
+ if(!tm) {
+ cerr << "Error returned by gmtime\n";
+ return -1;
+ }
+ if (strftime(str_time, sizeof(str_time), format, tm) == 0) {
+ cerr << "Error returned by strftime\n";
+ return -1;
+ }
+ ret = str_time;
+ return 0;
+}
+
+int parse_json_resp(JSONParser &parser) {
+ string *resp;
+ resp = (string *)g_test->get_response_data();
+ if(!resp)
+ return -1;
+ if(!parser.parse(resp->c_str(), resp->length())) {
+ cout << "Error parsing create user response" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+struct cls_log_entry_json {
+ string section;
+ string name;
+ utime_t timestamp;
+ RGWMetadataLogData log_data;
+};
+
+static int decode_json(JSONObj *obj, RGWMetadataLogData &data) {
+ JSONObj *jo;
+
+ jo = obj->find_obj("read_version");
+ if (!jo)
+ return -1;
+ data.read_version.decode_json(obj);
+ data.write_version.decode_json(obj);
+
+ jo = obj->find_obj("status");
+ if (!jo)
+ return -1;
+ JSONDecoder::decode_json("status", data, jo);
+ return 0;
+}
+
+static int decode_json(JSONObj *obj, cls_log_entry_json& ret) {
+ JSONDecoder::decode_json("section", ret.section, obj);
+ JSONDecoder::decode_json("name", ret.name, obj);
+ JSONObj *jo = obj->find_obj("data");
+ if(!jo)
+ return 0;
+ return decode_json(jo, ret.log_data);
+}
+
+static int get_log_list(list<cls_log_entry_json> &entries) {
+ JSONParser parser;
+ if (parse_json_resp(parser) != 0)
+ return -1;
+ if (!parser.is_array())
+ return -1;
+
+ vector<string> l;
+ l = parser.get_array_elements();
+ int loop = 0;
+ for(vector<string>::iterator it = l.begin();
+ it != l.end(); ++it, loop++) {
+ JSONParser jp;
+ cls_log_entry_json entry;
+
+ if(!jp.parse((*it).c_str(), (*it).length())) {
+ cerr << "Error parsing log json object" << std::endl;
+ return -1;
+ }
+ EXPECT_EQ(decode_json((JSONObj *)&jp, entry), 0);
+ entries.push_back(entry);
+ }
+ return 0;
+}
+
+struct cls_bilog_entry {
+ string op_id;
+ string op_tag;
+ string op;
+ string object;
+ string status;
+ unsigned index_ver;
+};
+
+static int decode_json(JSONObj *obj, cls_bilog_entry& ret) {
+ JSONDecoder::decode_json("op_id", ret.op_id, obj);
+ JSONDecoder::decode_json("op_tag", ret.op_tag, obj);
+ JSONDecoder::decode_json("op", ret.op, obj);
+ JSONDecoder::decode_json("object", ret.object, obj);
+ JSONDecoder::decode_json("state", ret.status, obj);
+ JSONDecoder::decode_json("index_ver", ret.index_ver, obj);
+ return 0;
+}
+
+static int get_bilog_list(list<cls_bilog_entry> &entries) {
+ JSONParser parser;
+ if (parse_json_resp(parser) != 0)
+ return -1;
+ if (!parser.is_array())
+ return -1;
+
+ vector<string> l;
+ l = parser.get_array_elements();
+ int loop = 0;
+ for(vector<string>::iterator it = l.begin();
+ it != l.end(); ++it, loop++) {
+ JSONParser jp;
+ cls_bilog_entry entry;
+
+ if(!jp.parse((*it).c_str(), (*it).length())) {
+ cerr << "Error parsing log json object" << std::endl;
+ return -1;
+ }
+ EXPECT_EQ(decode_json((JSONObj *)&jp, entry), 0);
+ entries.push_back(entry);
+ }
+ return 0;
+}
+
+static int decode_json(JSONObj *obj, rgw_data_change& ret) {
+ string entity;
+
+ JSONDecoder::decode_json("entity_type", entity, obj);
+ if (entity.compare("bucket") == 0)
+ ret.entity_type = ENTITY_TYPE_BUCKET;
+ JSONDecoder::decode_json("key", ret.key, obj);
+ return 0;
+}
+
+static int get_datalog_list(list<rgw_data_change> &entries) {
+ JSONParser parser;
+
+ if (parse_json_resp(parser) != 0)
+ return -1;
+ if (!parser.is_array())
+ return -1;
+
+ vector<string> l;
+ l = parser.get_array_elements();
+ int loop = 0;
+ for(vector<string>::iterator it = l.begin();
+ it != l.end(); ++it, loop++) {
+ JSONParser jp;
+ rgw_data_change entry;
+
+ if(!jp.parse((*it).c_str(), (*it).length())) {
+ cerr << "Error parsing log json object" << std::endl;
+ return -1;
+ }
+ EXPECT_EQ(decode_json((JSONObj *)&jp, entry), 0);
+ entries.push_back(entry);
+ }
+ return 0;
+}
+
+unsigned get_mdlog_shard_id(string& key, int max_shards) {
+ string section = "user";
+ uint32_t val = ceph_str_hash_linux(key.c_str(), key.size());
+ val ^= ceph_str_hash_linux(section.c_str(), section.size());
+ return (unsigned)(val % max_shards);
+}
+
+unsigned get_datalog_shard_id(const char *bucket_name, int max_shards) {
+ uint32_t r = ceph_str_hash_linux(bucket_name, strlen(bucket_name)) % max_shards;
+ return (int)r;
+}
+
+TEST(TestRGWAdmin, datalog_list) {
+ string start_time,
+ end_time;
+ const char *cname = "datalog",
+ *perm = "*";
+ string rest_req;
+ unsigned shard_id = get_datalog_shard_id(TEST_BUCKET_NAME, g_ceph_context->_conf->rgw_data_log_num_shards);
+ stringstream ss;
+ list<rgw_data_change> entries;
+
+ ASSERT_EQ(get_formatted_time(start_time), 0);
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ rest_req = "/admin/log?type=data";
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ JSONParser parser;
+ int num_objects;
+ EXPECT_EQ (parse_json_resp(parser), 0);
+ JSONDecoder::decode_json("num_objects", num_objects, (JSONObj *)&parser);
+ ASSERT_EQ(num_objects,g_ceph_context->_conf->rgw_data_log_num_shards);
+
+ sleep(1);
+ ASSERT_EQ(0, create_bucket());
+
+ char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
+ ASSERT_TRUE(bucket_obj != NULL);
+ EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
+ sleep(1);
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_datalog_list(entries);
+ EXPECT_EQ(1U, entries.size());
+ if (entries.size() == 1) {
+ rgw_data_change entry = *(entries.begin());
+ EXPECT_EQ(entry.entity_type, ENTITY_TYPE_BUCKET);
+ EXPECT_EQ(entry.key.compare(TEST_BUCKET_NAME), 0);
+ }
+ ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
+ sleep(1);
+ ASSERT_EQ(get_formatted_time(end_time), 0);
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_datalog_list(entries);
+ EXPECT_EQ(1U, entries.size());
+ if (entries.size() == 1) {
+ list<rgw_data_change>::iterator it = (entries.begin());
+ EXPECT_EQ((*it).entity_type, ENTITY_TYPE_BUCKET);
+ EXPECT_EQ((*it).key.compare(TEST_BUCKET_NAME), 0);
+ }
+
+ sleep(1);
+ EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
+ free(bucket_obj);
+ sleep(20);
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_datalog_list(entries);
+ EXPECT_EQ(2U, entries.size());
+ if (entries.size() == 2) {
+ list<rgw_data_change>::iterator it = (entries.begin());
+ EXPECT_EQ((*it).entity_type, ENTITY_TYPE_BUCKET);
+ EXPECT_EQ((*it).key.compare(TEST_BUCKET_NAME), 0);
+ ++it;
+ EXPECT_EQ((*it).entity_type, ENTITY_TYPE_BUCKET);
+ EXPECT_EQ((*it).key.compare(TEST_BUCKET_NAME), 0);
+ }
+
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
+ << "&max-entries=1";
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_datalog_list(entries);
+ EXPECT_EQ(1U, entries.size());
+
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_datalog_list(entries);
+ EXPECT_EQ(1U, entries.size());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "read";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
+ ASSERT_EQ(0, delete_bucket());
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, datalog_lock_unlock) {
+ const char *cname = "datalog",
+ *perm = "*";
+ string rest_req;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ rest_req = "/admin/log?type=data&lock&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=data&lock&id=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=data&lock&length=3&id=1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=data&lock&length=3&id=1&locker-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=data&unlock&id=1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=data&unlock&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=data&unlock&locker-id=ceph&id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ utime_t sleep_time(3, 0);
+
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(500U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=2";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(500U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ sleep_time.sleep();
+
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "read";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "write";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, datalog_trim) {
+ string start_time,
+ end_time;
+ const char *cname = "datalog",
+ *perm = "*";
+ string rest_req;
+ unsigned shard_id = get_datalog_shard_id(TEST_BUCKET_NAME, g_ceph_context->_conf->rgw_data_log_num_shards);
+ stringstream ss;
+ list<rgw_data_change> entries;
+
+ ASSERT_EQ(get_formatted_time(start_time), 0);
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ rest_req = "/admin/log?type=data";
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ ss.str("");
+ ss << "/admin/log?type=data&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ ASSERT_EQ(0, create_bucket());
+
+ char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
+ ASSERT_TRUE(bucket_obj != NULL);
+ EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
+ ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
+ sleep(1);
+ EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
+ ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
+ sleep(20);
+ free(bucket_obj);
+
+ ASSERT_EQ(get_formatted_time(end_time), 0);
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_datalog_list(entries);
+ EXPECT_TRUE(!entries.empty());
+
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_datalog_list(entries);
+ EXPECT_TRUE(entries.empty());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "write";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ ss.str("");
+ ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, delete_bucket());
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, mdlog_list) {
+ string start_time,
+ end_time,
+ start_time_2;
+ const char *cname = "mdlog",
+ *perm = "*";
+ string rest_req;
+ unsigned shard_id = get_mdlog_shard_id(uid, g_ceph_context->_conf->rgw_md_log_max_shards);
+ stringstream ss;
+
+ sleep(2);
+ ASSERT_EQ(get_formatted_time(start_time), 0);
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ rest_req = "/admin/log?type=metadata";
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ JSONParser parser;
+ int num_objects;
+ EXPECT_EQ (parse_json_resp(parser), 0);
+ JSONDecoder::decode_json("num_objects", num_objects, (JSONObj *)&parser);
+ ASSERT_EQ(num_objects,g_ceph_context->_conf->rgw_md_log_max_shards);
+
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ list<cls_log_entry_json> entries;
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 4U);
+
+ if(entries.size() == 4) {
+ list<cls_log_entry_json>::iterator it = entries.begin();
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
+ }
+
+ sleep(1); /*To get a modified time*/
+ ASSERT_EQ(get_formatted_time(start_time_2), 0);
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm="read";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time_2;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ entries.clear();
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 4U);
+
+ if(entries.size() == 4) {
+ list<cls_log_entry_json>::iterator it = entries.begin();
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
+ }
+
+ sleep(1);
+ ASSERT_EQ(get_formatted_time(start_time_2), 0);
+ ASSERT_EQ(0, user_rm(uid, display_name));
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ perm = "*";
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time_2;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ entries.clear();
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 6U);
+ if(entries.size() == 6) {
+ list<cls_log_entry_json>::iterator it = entries.begin();
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_REMOVE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
+ ++it;
+ EXPECT_TRUE(it->section.compare("user") == 0);
+ EXPECT_TRUE(it->name.compare(uid) == 0);
+ EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
+ }
+
+ sleep(1);
+ ASSERT_EQ(get_formatted_time(end_time), 0);
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 14U);
+
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time
+ << "&max-entries=" << 1;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 1U);
+
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time
+ << "&max-entries=" << 6;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 6U);
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ /*cleanup*/
+ ASSERT_EQ(0, caps_add(cname, perm));
+ sleep(1);
+ ASSERT_EQ(get_formatted_time(end_time), 0);
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time
+ << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, mdlog_trim) {
+ string start_time,
+ end_time;
+ const char *cname = "mdlog",
+ *perm = "*";
+ string rest_req;
+ list<cls_log_entry_json> entries;
+ unsigned shard_id = get_mdlog_shard_id(uid, g_ceph_context->_conf->rgw_md_log_max_shards);
+ ostringstream ss;
+
+ sleep(1);
+ ASSERT_EQ(get_formatted_time(start_time), 0);
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 4U);
+
+ sleep(1);
+ ASSERT_EQ(get_formatted_time(end_time), 0);
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
+ rest_req = ss.str();
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ EXPECT_EQ(get_log_list(entries), 0);
+ EXPECT_EQ(entries.size(), 0U);
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm="write";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ ASSERT_EQ(get_formatted_time(end_time), 0);
+ ss.str("");
+ ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time << "&end-time=" << end_time;
+ rest_req = ss.str();
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(403U, g_test->get_resp_code());
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, mdlog_lock_unlock) {
+ const char *cname = "mdlog",
+ *perm = "*";
+ string rest_req;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ rest_req = "/admin/log?type=metadata&lock&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=metadata&lock&id=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=metadata&lock&length=3&id=1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=metadata&lock&id=3&locker-id=ceph&length=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=metadata&unlock&id=1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=metadata&unlock&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=metadata&unlock&locker-id=ceph&id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ utime_t sleep_time(3, 0);
+
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(500U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=2";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(500U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ sleep_time.sleep();
+
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph1&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "read";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "write";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, bilog_list) {
+ const char *cname = "bilog",
+ *perm = "*";
+ string rest_req;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ ASSERT_EQ(0, create_bucket());
+
+ char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
+ ASSERT_TRUE(bucket_obj != NULL);
+ EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
+ free(bucket_obj);
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ list<cls_bilog_entry> entries;
+ get_bilog_list(entries);
+ EXPECT_EQ(2U, entries.size());
+ if (entries.size() == 2) {
+ list<cls_bilog_entry>::iterator it = entries.begin();
+ EXPECT_EQ(it->op.compare("write"), 0);
+ EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
+ EXPECT_EQ(it->status.compare("pending"), 0);
+ EXPECT_EQ(it->index_ver, 1U);
+ ++it;
+ EXPECT_EQ(it->op.compare("write"), 0);
+ EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
+ EXPECT_EQ(it->status.compare("complete"), 0);
+ EXPECT_EQ(it->index_ver, 2U);
+ }
+ EXPECT_EQ(read_bucket_obj(TEST_BUCKET_OBJECT), 0);
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_bilog_list(entries);
+ EXPECT_EQ(2U, entries.size());
+
+ bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
+ ASSERT_TRUE(bucket_obj != NULL);
+ EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT_1, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
+ free(bucket_obj);
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_bilog_list(entries);
+ EXPECT_EQ(4U, entries.size());
+ if (entries.size() == 4) {
+ list<cls_bilog_entry>::iterator it = entries.begin();
+
+ ++it; ++it;
+ EXPECT_EQ(it->op.compare("write"), 0);
+ EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT_1), 0);
+ EXPECT_EQ(it->status.compare("pending"), 0);
+ EXPECT_EQ(it->index_ver, 3U);
+ ++it;
+ EXPECT_EQ(it->op.compare("write"), 0);
+ EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT_1), 0);
+ EXPECT_EQ(it->status.compare("complete"), 0);
+ EXPECT_EQ(it->index_ver, 4U);
+ }
+
+ ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_bilog_list(entries);
+
+ EXPECT_EQ(6U, entries.size());
+ string marker;
+ if (entries.size() == 6) {
+ list<cls_bilog_entry>::iterator it = entries.begin();
+
+ ++it; ++it; ++it; ++it;
+ marker = it->op_id;
+ EXPECT_EQ(it->op.compare("del"), 0);
+ EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
+ EXPECT_EQ(it->status.compare("pending"), 0);
+ EXPECT_EQ(it->index_ver, 5U);
+ ++it;
+ EXPECT_EQ(it->op.compare("del"), 0);
+ EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
+ EXPECT_EQ(it->status.compare("complete"), 0);
+ EXPECT_EQ(it->index_ver, 6U);
+ }
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ rest_req.append("&marker=");
+ rest_req.append(marker);
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_bilog_list(entries);
+ EXPECT_EQ(2U, entries.size());
+ if (entries.size() == 2U) {
+ list<cls_bilog_entry>::iterator it = entries.begin();
+ EXPECT_EQ(it->index_ver, 5U);
+ ++it;
+ EXPECT_EQ(it->index_ver, 6U);
+ EXPECT_EQ(it->op.compare("del"), 0);
+ }
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ rest_req.append("&marker=");
+ rest_req.append(marker);
+ rest_req.append("&max-entries=1");
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_bilog_list(entries);
+ EXPECT_EQ(1U, entries.size());
+ EXPECT_EQ((entries.begin())->index_ver, 5U);
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "read";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, caps_rm(cname, perm));
+ perm = "write";
+ ASSERT_EQ(0, caps_add(cname, perm));
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT_1));
+ ASSERT_EQ(0, delete_bucket());
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, bilog_trim) {
+ const char *cname = "bilog",
+ *perm = "*";
+ string rest_req, start_marker, end_marker;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, caps_add(cname, perm));
+
+ ASSERT_EQ(0, create_bucket());
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
+ ASSERT_TRUE(bucket_obj != NULL);
+ EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
+ free(bucket_obj);
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ list<cls_bilog_entry> entries;
+ get_bilog_list(entries);
+ EXPECT_EQ(2U, entries.size());
+
+ list<cls_bilog_entry>::iterator it = entries.begin();
+ start_marker = it->op_id;
+ ++it;
+ end_marker = it->op_id;
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ rest_req.append("&start-marker=");
+ rest_req.append(start_marker);
+ rest_req.append("&end-marker=");
+ rest_req.append(end_marker);
+ g_test->send_request(string("DELETE"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
+ g_test->send_request(string("GET"), rest_req);
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ entries.clear();
+ get_bilog_list(entries);
+ EXPECT_EQ(0U, entries.size());
+
+ ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
+ ASSERT_EQ(0, delete_bucket());
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+int main(int argc, char *argv[]){
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_test = new admin_log::test_helper();
+ finisher = new Finisher(g_ceph_context);
+#ifdef GTEST
+ ::testing::InitGoogleTest(&argc, argv);
+#endif
+ finisher->start();
+
+ if(g_test->extract_input(argc, argv) < 0){
+ print_usage(argv[0]);
+ return -1;
+ }
+#ifdef GTEST
+ int r = RUN_ALL_TESTS();
+ if (r >= 0) {
+ cout << "There are no failures in the test case\n";
+ } else {
+ cout << "There are some failures\n";
+ }
+#endif
+ finisher->stop();
+ return 0;
+}
diff --git a/src/test/test_rgw_admin_meta.cc b/src/test/test_rgw_admin_meta.cc
new file mode 100644
index 000000000..4699a876d
--- /dev/null
+++ b/src/test/test_rgw_admin_meta.cc
@@ -0,0 +1,924 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fstream>
+#include <map>
+#include <list>
+extern "C"{
+#include <curl/curl.h>
+}
+#include "common/ceph_crypto.h"
+#include "include/str_list.h"
+#include "common/ceph_json.h"
+#include "common/code_environment.h"
+#include "common/ceph_argparse.h"
+#include "common/Finisher.h"
+#include "global/global_init.h"
+#include "rgw_common.h"
+#include "rgw_rados.h"
+#include <gtest/gtest.h>
+
+using namespace std;
+
+#define CURL_VERBOSE 0
+#define HTTP_RESPONSE_STR "RespCode"
+#define CEPH_CRYPTO_HMACSHA1_DIGESTSIZE 20
+#define RGW_ADMIN_RESP_PATH "/tmp/.test_rgw_admin_resp"
+#define CEPH_UID "ceph"
+
+static string uid = CEPH_UID;
+static string display_name = "CEPH";
+static string meta_caps = "metadata";
+
+extern "C" int ceph_armor(char *dst, const char *dst_end,
+ const char *src, const char *end);
+static void print_usage(char *exec){
+ cout << "Usage: " << exec << " <Options>\n";
+ cout << "Options:\n"
+ "-g <gw-ip> - The ip address of the gateway\n"
+ "-p <gw-port> - The port number of the gateway\n"
+ "-c <ceph.conf> - Absolute path of ceph config file\n"
+ "-rgw-admin <path/to/radosgw-admin> - radosgw-admin absolute path\n";
+}
+
+namespace admin_meta {
+class test_helper {
+ private:
+ string host;
+ string port;
+ string creds;
+ string rgw_admin_path;
+ string conf_path;
+ CURL *curl_inst;
+ map<string, string> response;
+ list<string> extra_hdrs;
+ string *resp_data;
+ unsigned resp_code;
+ public:
+ test_helper() : curl_inst(0), resp_data(NULL), resp_code(0) {
+ curl_global_init(CURL_GLOBAL_ALL);
+ }
+ ~test_helper(){
+ curl_global_cleanup();
+ }
+ int send_request(string method, string uri,
+ size_t (*function)(void *,size_t,size_t,void *) = 0,
+ void *ud = 0, size_t length = 0);
+ int extract_input(int argc, char *argv[]);
+ string& get_response(string hdr){
+ return response[hdr];
+ }
+ void set_extra_header(string hdr){
+ extra_hdrs.push_back(hdr);
+ }
+ void set_response(char *val);
+ void set_response_data(char *data, size_t len){
+ if(resp_data) delete resp_data;
+ resp_data = new string(data, len);
+ }
+ string& get_rgw_admin_path() {
+ return rgw_admin_path;
+ }
+ string& get_ceph_conf_path() {
+ return conf_path;
+ }
+ void set_creds(string& c) {
+ creds = c;
+ }
+ const string *get_response_data(){return resp_data;}
+ unsigned get_resp_code(){return resp_code;}
+};
+
+int test_helper::extract_input(int argc, char *argv[]){
+#define ERR_CHECK_NEXT_PARAM(o) \
+ if(((int)loop + 1) >= argc)return -1; \
+ else o = argv[loop+1];
+
+ for(unsigned loop = 1;loop < (unsigned)argc; loop += 2){
+ if(strcmp(argv[loop], "-g") == 0){
+ ERR_CHECK_NEXT_PARAM(host);
+ }else if(strcmp(argv[loop],"-p") == 0){
+ ERR_CHECK_NEXT_PARAM(port);
+ }else if(strcmp(argv[loop], "-c") == 0){
+ ERR_CHECK_NEXT_PARAM(conf_path);
+ }else if(strcmp(argv[loop], "-rgw-admin") == 0){
+ ERR_CHECK_NEXT_PARAM(rgw_admin_path);
+ }else return -1;
+ }
+ if(host.empty() || rgw_admin_path.empty())
+ return -1;
+ return 0;
+}
+
+void test_helper::set_response(char *r){
+ string sr(r), h, v;
+ size_t off = sr.find(": ");
+ if(off != string::npos){
+ h.assign(sr, 0, off);
+ v.assign(sr, off + 2, sr.find("\r\n") - (off+2));
+ }else{
+ /*Could be the status code*/
+ if(sr.find("HTTP/") != string::npos){
+ h.assign(HTTP_RESPONSE_STR);
+ off = sr.find(" ");
+ v.assign(sr, off + 1, sr.find("\r\n") - (off + 1));
+ resp_code = atoi((v.substr(0, 3)).c_str());
+ }
+ }
+ response[h] = v;
+}
+
+size_t write_header(void *ptr, size_t size, size_t nmemb, void *ud){
+ test_helper *h = static_cast<test_helper *>(ud);
+ h->set_response((char *)ptr);
+ return size*nmemb;
+}
+
+size_t write_data(void *ptr, size_t size, size_t nmemb, void *ud){
+ test_helper *h = static_cast<test_helper *>(ud);
+ h->set_response_data((char *)ptr, size*nmemb);
+ return size*nmemb;
+}
+
+static inline void buf_to_hex(const unsigned char *buf, int len, char *str)
+{
+ int i;
+ str[0] = '\0';
+ for (i = 0; i < len; i++) {
+ sprintf(&str[i*2], "%02x", (int)buf[i]);
+ }
+}
+
+static void calc_hmac_sha1(const char *key, int key_len,
+ const char *msg, int msg_len, char *dest)
+/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
+{
+ ceph::crypto::HMACSHA1 hmac((const unsigned char *)key, key_len);
+ hmac.Update((const unsigned char *)msg, msg_len);
+ hmac.Final((unsigned char *)dest);
+
+ char hex_str[(CEPH_CRYPTO_HMACSHA1_DIGESTSIZE * 2) + 1];
+ admin_meta::buf_to_hex((unsigned char *)dest, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE, hex_str);
+}
+
+static int get_s3_auth(const string &method, string creds, const string &date, string res, string& out){
+ string aid, secret, auth_hdr;
+ string tmp_res;
+ size_t off = creds.find(":");
+ out = "";
+ if(off != string::npos){
+ aid.assign(creds, 0, off);
+ secret.assign(creds, off + 1, string::npos);
+
+ /*sprintf(auth_hdr, "%s\n\n\n%s\n%s", req_type, date, res);*/
+ char hmac_sha1[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
+ char b64[65]; /* 64 is really enough */
+ size_t off = res.find("?");
+ if(off == string::npos)
+ tmp_res = res;
+ else
+ tmp_res.assign(res, 0, off);
+ auth_hdr.append(method + string("\n\n\n") + date + string("\n") + tmp_res);
+ admin_meta::calc_hmac_sha1(secret.c_str(), secret.length(),
+ auth_hdr.c_str(), auth_hdr.length(), hmac_sha1);
+ int ret = ceph_armor(b64, b64 + 64, hmac_sha1,
+ hmac_sha1 + CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+ if (ret < 0) {
+ cout << "ceph_armor failed\n";
+ return -1;
+ }
+ b64[ret] = 0;
+ out.append(aid + string(":") + b64);
+ }else return -1;
+ return 0;
+}
+
+void get_date(string& d){
+ struct timeval tv;
+ char date[64];
+ struct tm tm;
+ char *days[] = {(char *)"Sun", (char *)"Mon", (char *)"Tue",
+ (char *)"Wed", (char *)"Thu", (char *)"Fri",
+ (char *)"Sat"};
+ char *months[] = {(char *)"Jan", (char *)"Feb", (char *)"Mar",
+ (char *)"Apr", (char *)"May", (char *)"Jun",
+ (char *)"Jul",(char *) "Aug", (char *)"Sep",
+ (char *)"Oct", (char *)"Nov", (char *)"Dec"};
+ gettimeofday(&tv, NULL);
+ gmtime_r(&tv.tv_sec, &tm);
+ sprintf(date, "%s, %d %s %d %d:%d:%d GMT",
+ days[tm.tm_wday],
+ tm.tm_mday, months[tm.tm_mon],
+ tm.tm_year + 1900,
+ tm.tm_hour, tm.tm_min, 0 /*tm.tm_sec*/);
+ d = date;
+}
+
+int test_helper::send_request(string method, string res,
+ size_t (*read_function)( void *,size_t,size_t,void *),
+ void *ud,
+ size_t length){
+ string url;
+ string auth, date;
+ url.append(string("http://") + host);
+ if(port.length() > 0)url.append(string(":") + port);
+ url.append(res);
+ curl_inst = curl_easy_init();
+ if(curl_inst){
+ curl_easy_setopt(curl_inst, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_inst, CURLOPT_CUSTOMREQUEST, method.c_str());
+ curl_easy_setopt(curl_inst, CURLOPT_VERBOSE, CURL_VERBOSE);
+ curl_easy_setopt(curl_inst, CURLOPT_HEADERFUNCTION, admin_meta::write_header);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEHEADER, (void *)this);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEFUNCTION, admin_meta::write_data);
+ curl_easy_setopt(curl_inst, CURLOPT_WRITEDATA, (void *)this);
+ if(read_function){
+ curl_easy_setopt(curl_inst, CURLOPT_READFUNCTION, read_function);
+ curl_easy_setopt(curl_inst, CURLOPT_READDATA, (void *)ud);
+ curl_easy_setopt(curl_inst, CURLOPT_UPLOAD, 1L);
+ curl_easy_setopt(curl_inst, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
+ }
+
+ get_date(date);
+ string http_date;
+ http_date.append(string("Date: ") + date);
+
+ string s3auth;
+ if (admin_meta::get_s3_auth(method, creds, date, res, s3auth) < 0)
+ return -1;
+ auth.append(string("Authorization: AWS ") + s3auth);
+
+ struct curl_slist *slist = NULL;
+ slist = curl_slist_append(slist, auth.c_str());
+ slist = curl_slist_append(slist, http_date.c_str());
+ for(list<string>::iterator it = extra_hdrs.begin();
+ it != extra_hdrs.end(); ++it){
+ slist = curl_slist_append(slist, (*it).c_str());
+ }
+ if(read_function)
+ curl_slist_append(slist, "Expect:");
+ curl_easy_setopt(curl_inst, CURLOPT_HTTPHEADER, slist);
+
+ response.erase(response.begin(), response.end());
+ extra_hdrs.erase(extra_hdrs.begin(), extra_hdrs.end());
+ CURLcode res = curl_easy_perform(curl_inst);
+ if(res != CURLE_OK){
+ cout << "Curl perform failed for " << url << ", res: " <<
+ curl_easy_strerror(res) << "\n";
+ return -1;
+ }
+ curl_slist_free_all(slist);
+ }
+ curl_easy_cleanup(curl_inst);
+ return 0;
+}
+};
+
+admin_meta::test_helper *g_test;
+Finisher *finisher;
+
+int run_rgw_admin(string& cmd, string& resp) {
+ pid_t pid;
+ pid = fork();
+ if (pid == 0) {
+ /* child */
+ list<string> l;
+ get_str_list(cmd, " \t", l);
+ char *argv[l.size()];
+ unsigned loop = 1;
+
+ argv[0] = (char *)"radosgw-admin";
+ for (list<string>::iterator it = l.begin();
+ it != l.end(); ++it) {
+ argv[loop++] = (char *)(*it).c_str();
+ }
+ argv[loop] = NULL;
+ if (!freopen(RGW_ADMIN_RESP_PATH, "w+", stdout)) {
+ cout << "Unable to open stdout file" << std::endl;
+ }
+ execv((g_test->get_rgw_admin_path()).c_str(), argv);
+ } else if (pid > 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ if(WEXITSTATUS(status) != 0) {
+ cout << "Child exited with status " << WEXITSTATUS(status) << std::endl;
+ return -1;
+ }
+ }
+ ifstream in;
+ struct stat st;
+
+ if (stat(RGW_ADMIN_RESP_PATH, &st) < 0) {
+ cout << "Error stating the admin response file, errno " << errno << std::endl;
+ return -1;
+ } else {
+ char *data = (char *)malloc(st.st_size + 1);
+ in.open(RGW_ADMIN_RESP_PATH);
+ in.read(data, st.st_size);
+ in.close();
+ data[st.st_size] = 0;
+ resp = data;
+ free(data);
+ unlink(RGW_ADMIN_RESP_PATH);
+ /* cout << "radosgw-admin " << cmd << ": " << resp << std::endl;*/
+ }
+ } else
+ return -1;
+ return 0;
+}
+
+int get_creds(string& json, string& creds) {
+ JSONParser parser;
+ if(!parser.parse(json.c_str(), json.length())) {
+ cout << "Error parsing create user response" << std::endl;
+ return -1;
+ }
+
+ RGWUserInfo info;
+ decode_json_obj(info, &parser);
+ creds = "";
+ for(map<string, RGWAccessKey>::iterator it = info.access_keys.begin();
+ it != info.access_keys.end(); ++it) {
+ RGWAccessKey _k = it->second;
+ /*cout << "accesskeys [ " << it->first << " ] = " <<
+ "{ " << _k.id << ", " << _k.key << ", " << _k.subuser << "}" << std::endl;*/
+ creds.append(it->first + string(":") + _k.key);
+ break;
+ }
+ return 0;
+}
+
+int user_create(string& uid, string& display_name, bool set_creds = true) {
+ stringstream ss;
+ string creds;
+ ss << "-c " << g_test->get_ceph_conf_path() << " user create --uid=" << uid
+ << " --display-name=" << display_name;
+
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error creating user" << std::endl;
+ return -1;
+ }
+ get_creds(out, creds);
+ if(set_creds)
+ g_test->set_creds(creds);
+ return 0;
+}
+
+int user_info(string& uid, string& display_name, RGWUserInfo& uinfo) {
+ stringstream ss;
+ ss << "-c " << g_test->get_ceph_conf_path() << " user info --uid=" << uid
+ << " --display-name=" << display_name;
+
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error reading user information" << std::endl;
+ return -1;
+ }
+ JSONParser parser;
+ if(!parser.parse(out.c_str(), out.length())) {
+ cout << "Error parsing create user response" << std::endl;
+ return -1;
+ }
+ decode_json_obj(uinfo, &parser);
+ return 0;
+}
+
+int user_rm(string& uid, string& display_name) {
+ stringstream ss;
+ ss << "-c " << g_test->get_ceph_conf_path() << " user rm --uid=" << uid
+ << " --display-name=" << display_name;
+
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error removing user" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+int meta_caps_add(const char *perm) {
+ stringstream ss;
+
+ ss << "-c " << g_test->get_ceph_conf_path() << " caps add --caps=" <<
+ meta_caps << "=" << perm << " --uid=" << uid;
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error creating user" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+int meta_caps_rm(const char *perm) {
+ stringstream ss;
+
+ ss << "-c " << g_test->get_ceph_conf_path() << " caps rm --caps=" <<
+ meta_caps << "=" << perm << " --uid=" << uid;
+ string out;
+ string cmd = ss.str();
+ if(run_rgw_admin(cmd, out) != 0) {
+ cout << "Error creating user" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+int compare_access_keys(RGWAccessKey& k1, RGWAccessKey& k2) {
+ if (k1.id.compare(k2.id) != 0)
+ return -1;
+ if (k1.key.compare(k2.key) != 0)
+ return -1;
+ if (k1.subuser.compare(k2.subuser) != 0)
+ return -1;
+
+ return 0;
+}
+
+int compare_user_info(RGWUserInfo& i1, RGWUserInfo& i2) {
+ int rv;
+
+ if ((rv = i1.user_id.compare(i2.user_id)) != 0)
+ return rv;
+ if ((rv = i1.display_name.compare(i2.display_name)) != 0)
+ return rv;
+ if ((rv = i1.user_email.compare(i2.user_email)) != 0)
+ return rv;
+ if (i1.access_keys.size() != i2.access_keys.size())
+ return -1;
+ for (map<string, RGWAccessKey>::iterator it = i1.access_keys.begin();
+ it != i1.access_keys.end(); ++it) {
+ RGWAccessKey k1, k2;
+ k1 = it->second;
+ if (i2.access_keys.count(it->first) == 0)
+ return -1;
+ k2 = i2.access_keys[it->first];
+ if (compare_access_keys(k1, k2) != 0)
+ return -1;
+ }
+ if (i1.swift_keys.size() != i2.swift_keys.size())
+ return -1;
+ for (map<string, RGWAccessKey>::iterator it = i1.swift_keys.begin();
+ it != i1.swift_keys.end(); ++it) {
+ RGWAccessKey k1, k2;
+ k1 = it->second;
+ if (i2.swift_keys.count(it->first) == 0)
+ return -1;
+ k2 = i2.swift_keys[it->first];
+ if (compare_access_keys(k1, k2) != 0)
+ return -1;
+ }
+ if (i1.subusers.size() != i2.subusers.size())
+ return -1;
+ for (map<string, RGWSubUser>::iterator it = i1.subusers.begin();
+ it != i1.subusers.end(); ++it) {
+ RGWSubUser k1, k2;
+ k1 = it->second;
+ if (!i2.subusers.count(it->first))
+ return -1;
+ k2 = i2.subusers[it->first];
+ if (k1.name.compare(k2.name) != 0)
+ return -1;
+ if (k1.perm_mask != k2.perm_mask)
+ return -1;
+ }
+ if (i1.suspended != i2.suspended)
+ return -1;
+ if (i1.max_buckets != i2.max_buckets)
+ return -1;
+ uint32_t p1, p2;
+ p1 = p2 = RGW_CAP_ALL;
+ if (i1.caps.check_cap(meta_caps, p1) != 0)
+ return -1;
+ if (i2.caps.check_cap(meta_caps, p2) != 0)
+ return -1;
+ return 0;
+}
+
+size_t read_dummy_post(void *ptr, size_t s, size_t n, void *ud) {
+ int dummy = 0;
+ memcpy(ptr, &dummy, sizeof(dummy));
+ return sizeof(dummy);
+}
+
+
+int parse_json_resp(JSONParser &parser) {
+ string *resp;
+ resp = (string *)g_test->get_response_data();
+ if(!resp)
+ return -1;
+ if(!parser.parse(resp->c_str(), resp->length())) {
+ cout << "Error parsing create user response" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+size_t meta_read_json(void *ptr, size_t s, size_t n, void *ud){
+ stringstream *ss = (stringstream *)ud;
+ size_t len = ss->str().length();
+ if(s*n < len){
+ cout << "Cannot copy json data, as len is not enough\n";
+ return 0;
+ }
+ memcpy(ptr, (void *)ss->str().c_str(), len);
+ return len;
+}
+
+TEST(TestRGWAdmin, meta_list){
+ JSONParser parser;
+ bool found = false;
+ const char *perm = "*";
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, meta_caps_add(perm));
+
+ /*Check the sections*/
+ g_test->send_request(string("GET"), string("/admin/metadata/"));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_TRUE(parse_json_resp(parser) == 0);
+ EXPECT_TRUE(parser.is_array());
+
+ vector<string> l;
+ l = parser.get_array_elements();
+ for(vector<string>::iterator it = l.begin();
+ it != l.end(); ++it) {
+ if((*it).compare("\"user\"") == 0) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+
+ /*Check with a wrong section*/
+ g_test->send_request(string("GET"), string("/admin/metadata/users"));
+ EXPECT_EQ(404U, g_test->get_resp_code());
+
+ /*Check the list of keys*/
+ g_test->send_request(string("GET"), string("/admin/metadata/user"));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_TRUE(parse_json_resp(parser) == 0);
+ EXPECT_TRUE(parser.is_array());
+
+ l = parser.get_array_elements();
+ EXPECT_EQ(1U, l.size());
+ for(vector<string>::iterator it = l.begin();
+ it != l.end(); ++it) {
+ if((*it).compare(string("\"") + uid + string("\"")) == 0) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+
+ /*Check with second user*/
+ string uid2 = "ceph1", display_name2 = "CEPH1";
+ ASSERT_EQ(0, user_create(uid2, display_name2, false));
+ /*Check the list of keys*/
+ g_test->send_request(string("GET"), string("/admin/metadata/user"));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_TRUE(parse_json_resp(parser) == 0);
+ EXPECT_TRUE(parser.is_array());
+
+ l = parser.get_array_elements();
+ EXPECT_EQ(2U, l.size());
+ bool found2 = false;
+ for(vector<string>::iterator it = l.begin();
+ it != l.end(); ++it) {
+ if((*it).compare(string("\"") + uid + string("\"")) == 0) {
+ found = true;
+ }
+ if((*it).compare(string("\"") + uid2 + string("\"")) == 0) {
+ found2 = true;
+ }
+ }
+ EXPECT_TRUE(found && found2);
+ ASSERT_EQ(0, user_rm(uid2, display_name2));
+
+ /*Remove the metadata caps*/
+ int rv = meta_caps_rm(perm);
+ EXPECT_EQ(0, rv);
+
+ if(rv == 0) {
+ g_test->send_request(string("GET"), string("/admin/metadata/"));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ g_test->send_request(string("GET"), string("/admin/metadata/user"));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+ }
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, meta_get){
+ JSONParser parser;
+ const char *perm = "*";
+ RGWUserInfo info;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, meta_caps_add(perm));
+
+ ASSERT_EQ(0, user_info(uid, display_name, info));
+
+ g_test->send_request(string("GET"), string("/admin/metadata/user?key=test"));
+ EXPECT_EQ(404U, g_test->get_resp_code());
+
+ g_test->send_request(string("GET"), (string("/admin/metadata/user?key=") + uid));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_TRUE(parse_json_resp(parser) == 0);
+ RGWObjVersionTracker objv_tracker;
+ string metadata_key;
+
+ obj_version *objv = &objv_tracker.read_version;
+
+ JSONDecoder::decode_json("key", metadata_key, &parser);
+ JSONDecoder::decode_json("ver", *objv, &parser);
+ JSONObj *jo = parser.find_obj("data");
+ ASSERT_TRUE(jo);
+ string exp_meta_key = "user:";
+ exp_meta_key.append(uid);
+ EXPECT_TRUE(metadata_key.compare(exp_meta_key) == 0);
+
+ RGWUserInfo obt_info;
+ decode_json_obj(obt_info, jo);
+
+ EXPECT_TRUE(compare_user_info(info, obt_info) == 0);
+
+ /*Make a modification and check if its reflected*/
+ ASSERT_EQ(0, meta_caps_rm(perm));
+ perm = "read";
+ ASSERT_EQ(0, meta_caps_add(perm));
+
+ JSONParser parser1;
+ g_test->send_request(string("GET"), (string("/admin/metadata/user?key=") + uid));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_TRUE(parse_json_resp(parser1) == 0);
+
+ RGWObjVersionTracker objv_tracker1;
+ obj_version *objv1 = &objv_tracker1.read_version;
+
+ JSONDecoder::decode_json("key", metadata_key, &parser1);
+ JSONDecoder::decode_json("ver", *objv1, &parser1);
+ jo = parser1.find_obj("data");
+ ASSERT_TRUE(jo);
+
+ decode_json_obj(obt_info, jo);
+ uint32_t p1, p2;
+ p1 = RGW_CAP_ALL;
+ p2 = RGW_CAP_READ;
+ EXPECT_TRUE (info.caps.check_cap(meta_caps, p1) == 0);
+ EXPECT_TRUE (obt_info.caps.check_cap(meta_caps, p2) == 0);
+ p2 = RGW_CAP_WRITE;
+ EXPECT_TRUE (obt_info.caps.check_cap(meta_caps, p2) != 0);
+
+ /*Version and tag infromation*/
+ EXPECT_TRUE(objv1->ver > objv->ver);
+ EXPECT_EQ(objv1->tag, objv->tag);
+
+ int rv = meta_caps_rm(perm);
+ EXPECT_EQ(0, rv);
+
+ if(rv == 0) {
+ g_test->send_request(string("GET"), (string("/admin/metadata/user?key=") + uid));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+ }
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, meta_put){
+ JSONParser parser;
+ const char *perm = "*";
+ RGWUserInfo info;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, meta_caps_add(perm));
+
+ g_test->send_request(string("GET"), (string("/admin/metadata/user?key=") + uid));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_TRUE(parse_json_resp(parser) == 0);
+ RGWObjVersionTracker objv_tracker;
+ string metadata_key;
+
+ obj_version *objv = &objv_tracker.read_version;
+
+ JSONDecoder::decode_json("key", metadata_key, &parser);
+ JSONDecoder::decode_json("ver", *objv, &parser);
+ JSONObj *jo = parser.find_obj("data");
+ ASSERT_TRUE(jo);
+ string exp_meta_key = "user:";
+ exp_meta_key.append(uid);
+ EXPECT_TRUE(metadata_key.compare(exp_meta_key) == 0);
+
+ RGWUserInfo obt_info;
+ decode_json_obj(obt_info, jo);
+
+ /*Change the cap and PUT */
+ RGWUserCaps caps;
+ string new_cap;
+ Formatter *f = new JSONFormatter();
+
+ new_cap = meta_caps + string("=write");
+ caps.add_from_string(new_cap);
+ obt_info.caps = caps;
+ f->open_object_section("metadata_info");
+ ::encode_json("key", metadata_key, f);
+ ::encode_json("ver", *objv, f);
+ ::encode_json("data", obt_info, f);
+ f->close_section();
+ std::stringstream ss;
+ f->flush(ss);
+
+ g_test->send_request(string("PUT"), (string("/admin/metadata/user?key=") + uid),
+ meta_read_json,
+ (void *)&ss, ss.str().length());
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, user_info(uid, display_name, obt_info));
+ uint32_t cp;
+ cp = RGW_CAP_WRITE;
+ EXPECT_TRUE (obt_info.caps.check_cap(meta_caps, cp) == 0);
+ cp = RGW_CAP_READ;
+ EXPECT_TRUE (obt_info.caps.check_cap(meta_caps, cp) != 0);
+
+ int rv = meta_caps_rm("write");
+ EXPECT_EQ(0, rv);
+ if(rv == 0) {
+ g_test->send_request(string("PUT"), (string("/admin/metadata/user?key=") + uid));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+ }
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, meta_lock_unlock) {
+ const char *perm = "*";
+ string rest_req;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, meta_caps_add(perm));
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/metadata/user?lock&length=3&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&unlock";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/metadata/user?unlock&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&unlock&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&unlock&lock_id=ceph1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+ utime_t sleep_time(3, 0);
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(500U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(409U, g_test->get_resp_code());
+ sleep_time.sleep();
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&unlock&lock_id=ceph1";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, meta_caps_rm(perm));
+ perm = "read";
+ ASSERT_EQ(0, meta_caps_add(perm));
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&unlock&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, meta_caps_rm(perm));
+ perm = "write";
+ ASSERT_EQ(0, meta_caps_add(perm));
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&unlock&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, meta_caps_rm(perm));
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&lock&length=3&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ rest_req = "/admin/metadata/user?key=" CEPH_UID "&unlock&lock_id=ceph";
+ g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+TEST(TestRGWAdmin, meta_delete){
+ JSONParser parser;
+ const char *perm = "*";
+ RGWUserInfo info;
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ ASSERT_EQ(0, meta_caps_add(perm));
+
+ g_test->send_request(string("DELETE"), (string("/admin/metadata/user?key=") + uid));
+ EXPECT_EQ(200U, g_test->get_resp_code());
+
+ ASSERT_TRUE(user_info(uid, display_name, info) != 0);
+
+ ASSERT_EQ(0, user_create(uid, display_name));
+ perm = "read";
+ ASSERT_EQ(0, meta_caps_add(perm));
+
+ g_test->send_request(string("DELETE"), (string("/admin/metadata/user?key=") + uid));
+ EXPECT_EQ(403U, g_test->get_resp_code());
+ ASSERT_EQ(0, user_rm(uid, display_name));
+}
+
+int main(int argc, char *argv[]){
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_test = new admin_meta::test_helper();
+ finisher = new Finisher(g_ceph_context);
+#ifdef GTEST
+ ::testing::InitGoogleTest(&argc, argv);
+#endif
+ finisher->start();
+
+ if(g_test->extract_input(argc, argv) < 0){
+ print_usage(argv[0]);
+ return -1;
+ }
+#ifdef GTEST
+ int r = RUN_ALL_TESTS();
+ if (r >= 0) {
+ cout << "There are no failures in the test case\n";
+ } else {
+ cout << "There are some failures\n";
+ }
+#endif
+ finisher->stop();
+ return 0;
+}
diff --git a/src/test/test_rgw_ldap.cc b/src/test/test_rgw_ldap.cc
new file mode 100644
index 000000000..8c1c30191
--- /dev/null
+++ b/src/test/test_rgw_ldap.cc
@@ -0,0 +1,116 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2015 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdint.h>
+#include <tuple>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <random>
+
+#include "rgw_ldap.h"
+#include "rgw_token.h"
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace {
+
+ struct {
+ int argc;
+ char **argv;
+ } saved_args;
+
+ bool do_hexdump = false;
+
+ string access_key("ewogICAgIlJHV19UT0tFTiI6IHsKICAgICAgICAidmVyc2lvbiI6IDEsCiAgICAgICAgInR5cGUiOiAibGRhcCIsCiAgICAgICAgImlkIjogImFkbWluIiwKICAgICAgICAia2V5IjogImxpbnV4Ym94IgogICAgfQp9Cg=="); // {admin,linuxbox}
+ string other_key("ewogICAgIlJHV19UT0tFTiI6IHsKICAgICAgICAidmVyc2lvbiI6IDEsCiAgICAgICAgInR5cGUiOiAibGRhcCIsCiAgICAgICAgImlkIjogImFkbWluIiwKICAgICAgICAia2V5IjogImJhZHBhc3MiCiAgICB9Cn0K"); // {admin,badpass}
+
+ string ldap_uri = "ldaps://f23-kdc.rgw.com";
+ string ldap_binddn = "uid=admin,cn=users,cn=accounts,dc=rgw,dc=com";
+ string ldap_bindpw = "supersecret";
+ string ldap_searchdn = "cn=users,cn=accounts,dc=rgw,dc=com";
+ string ldap_searchfilter = "";
+ string ldap_dnattr = "uid";
+
+ rgw::LDAPHelper ldh(ldap_uri, ldap_binddn, ldap_bindpw, ldap_searchdn,
+ ldap_searchfilter, ldap_dnattr);
+
+} /* namespace */
+
+TEST(RGW_LDAP, INIT) {
+ int ret = ldh.init();
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(RGW_LDAP, BIND) {
+ int ret = ldh.bind();
+ ASSERT_EQ(ret, 0);
+}
+
+TEST(RGW_LDAP, AUTH) {
+ using std::get;
+ using namespace rgw;
+ int ret = 0;
+ {
+ RGWToken token{from_base64(access_key)};
+ ret = ldh.auth(token.id, token.key);
+ ASSERT_EQ(ret, 0);
+ }
+ {
+ RGWToken token{from_base64(other_key)};
+ ret = ldh.auth(token.id, token.key);
+ ASSERT_NE(ret, 0);
+ }
+}
+
+TEST(RGW_LDAP, SHUTDOWN) {
+ // nothing
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
+ (char*) nullptr)) {
+ access_key = val;
+ } else if (ceph_argparse_flag(args, arg_iter, "--hexdump",
+ (char*) nullptr)) {
+ do_hexdump = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ /* don't accidentally run as anonymous */
+ if (access_key == "") {
+ std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
+ return EPERM;
+ }
+
+ saved_args.argc = argc;
+ saved_args.argv = argv;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/test_rgw_token.cc b/src/test/test_rgw_token.cc
new file mode 100644
index 000000000..6136b6043
--- /dev/null
+++ b/src/test/test_rgw_token.cc
@@ -0,0 +1,124 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <errno.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "common/config.h"
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "include/ceph_assert.h"
+#include "gtest/gtest.h"
+#include "rgw_token.h"
+#include "rgw_b64.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+namespace {
+
+ using namespace rgw;
+ using std::get;
+ using std::string;
+
+ string access_key{"Smonny"};
+ string secret_key{"Turjan of Miir"};
+
+ std::vector<RGWToken> tokens;
+
+ std::string enc_ad{"ewogICAgIlJHV19UT0tFTiI6IHsKICAgICAgICAidmVyc2lvbiI6IDEsCiAgICAgICAgInR5cGUiOiAiYWQiLAogICAgICAgICJpZCI6ICJTbW9ubnkiLAogICAgICAgICJrZXkiOiAiVHVyamFuIG9mIE1paXIiCiAgICB9Cn0K"};
+
+ std::string enc_ldap{"ewogICAgIlJHV19UT0tFTiI6IHsKICAgICAgICAidmVyc2lvbiI6IDEsCiAgICAgICAgInR5cGUiOiAibGRhcCIsCiAgICAgICAgImlkIjogIlNtb25ueSIsCiAgICAgICAgImtleSI6ICJUdXJqYW4gb2YgTWlpciIKICAgIH0KfQo="};
+
+ std::string non_base64{"stuff here"};
+ std::string non_base64_sploded{"90KLscc0Dz4U49HX-7Tx"};
+
+ Formatter* token_formatter{nullptr};
+ bool verbose {false};
+}
+
+using namespace std;
+
+TEST(TOKEN, INIT) {
+ token_formatter = new JSONFormatter(true /* pretty */);
+ ASSERT_NE(token_formatter, nullptr);
+}
+
+TEST(TOKEN, ENCODE) {
+ // encode the two supported types
+ RGWToken token_ad(RGWToken::TOKEN_AD, access_key, secret_key);
+ ASSERT_EQ(token_ad.encode_json_base64(token_formatter), enc_ad);
+ tokens.push_back(token_ad); // provies copiable
+
+ RGWToken token_ldap(RGWToken::TOKEN_LDAP, access_key, secret_key);
+ ASSERT_EQ(token_ldap.encode_json_base64(token_formatter), enc_ldap);
+ tokens.push_back(token_ldap);
+}
+
+TEST(TOKEN, DECODE) {
+ for (const auto& enc_tok : {enc_ad, enc_ldap}) {
+ RGWToken token{from_base64(enc_tok)}; // decode ctor
+ ASSERT_EQ(token.id, access_key);
+ ASSERT_EQ(token.key, secret_key);
+ }
+}
+
+TEST(TOKEN, EMPTY) {
+ std::string empty{""};
+ RGWToken token{from_base64(empty)}; // decode ctor
+ ASSERT_FALSE(token.valid());
+}
+
+TEST(TOKEN, BADINPUT) {
+ RGWToken token{from_base64(non_base64)}; // decode ctor
+ ASSERT_FALSE(token.valid());
+}
+
+TEST(TOKEN, BADINPUT2) {
+ RGWToken token{from_base64(non_base64_sploded)}; // decode ctor
+ ASSERT_FALSE(token.valid());
+}
+
+TEST(TOKEN, BADINPUT3) {
+ try {
+ std::string stuff = from_base64(non_base64_sploded); // decode
+ } catch(...) {
+ // do nothing
+ }
+ ASSERT_EQ(1, 1);
+}
+
+TEST(TOKEN, SHUTDOWN) {
+ delete token_formatter;
+}
+
+int main(int argc, char *argv[])
+{
+ auto args = argv_to_vec(argc, argv);
+ env_to_vec(args);
+
+ string val;
+ for (auto arg_iter = args.begin(); arg_iter != args.end();) {
+ if (ceph_argparse_flag(args, arg_iter, "--verbose",
+ (char*) nullptr)) {
+ verbose = true;
+ } else {
+ ++arg_iter;
+ }
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/test_rw.sh b/src/test/test_rw.sh
new file mode 100755
index 000000000..cb7dc7767
--- /dev/null
+++ b/src/test/test_rw.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+set -x
+
+#
+# Generic read/write from object store test
+#
+
+# Includes
+source "`dirname $0`/test_common.sh"
+
+TEST_POOL=rbd
+
+# Functions
+my_write_objects() {
+ write_objects $1 $2 10 1000000 $TEST_POOL
+}
+
+setup() {
+ export CEPH_NUM_OSD=$1
+
+ # Start ceph
+ ./stop.sh
+
+ ./vstart.sh -d -n || die "vstart.sh failed"
+}
+
+read_write_1_impl() {
+ write_objects 1 2 100 8192 $TEST_POOL
+ read_objects 2 100 8192
+
+ write_objects 3 3 10 81920 $TEST_POOL
+ read_objects 3 10 81920
+
+ write_objects 4 4 100 4 $TEST_POOL
+ read_objects 4 100 4
+
+ write_objects 1 2 100 8192 $TEST_POOL
+ read_objects 2 100 8192
+
+ # success
+ return 0
+}
+
+read_write_1() {
+ setup 3
+ read_write_1_impl
+}
+
+run() {
+ read_write_1 || die "test failed"
+}
+
+$@
diff --git a/src/test/test_snap_mapper.cc b/src/test/test_snap_mapper.cc
new file mode 100644
index 000000000..e502892cc
--- /dev/null
+++ b/src/test/test_snap_mapper.cc
@@ -0,0 +1,952 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include <iterator>
+#include <map>
+#include <set>
+#include <boost/scoped_ptr.hpp>
+#include <sys/types.h>
+#include <cstdlib>
+
+#include "include/buffer.h"
+#include "common/map_cacher.hpp"
+#include "osd/osd_types_fmt.h"
+#include "osd/SnapMapper.h"
+#include "common/Cond.h"
+
+#include "gtest/gtest.h"
+
+using namespace std;
+
+template <typename T>
+typename T::iterator rand_choose(T &cont) {
+ if (std::empty(cont)) {
+ return std::end(cont);
+ }
+ return std::next(std::begin(cont), rand() % cont.size());
+}
+
+string random_string(size_t size)
+{
+ string name;
+ for (size_t j = 0; j < size; ++j) {
+ name.push_back('a' + (rand() % 26));
+ }
+ return name;
+}
+
+class PausyAsyncMap : public MapCacher::StoreDriver<string, bufferlist> {
+ struct _Op {
+ virtual void operate(map<string, bufferlist> *store) = 0;
+ virtual ~_Op() {}
+ };
+ typedef std::shared_ptr<_Op> Op;
+ struct Remove : public _Op {
+ set<string> to_remove;
+ explicit Remove(const set<string> &to_remove) : to_remove(to_remove) {}
+ void operate(map<string, bufferlist> *store) override {
+ for (set<string>::iterator i = to_remove.begin();
+ i != to_remove.end();
+ ++i) {
+ store->erase(*i);
+ }
+ }
+ };
+ struct Insert : public _Op {
+ map<string, bufferlist> to_insert;
+ explicit Insert(const map<string, bufferlist> &to_insert) : to_insert(to_insert) {}
+ void operate(map<string, bufferlist> *store) override {
+ for (map<string, bufferlist>::iterator i = to_insert.begin();
+ i != to_insert.end();
+ ++i) {
+ store->erase(i->first);
+ store->insert(*i);
+ }
+ }
+ };
+ struct Callback : public _Op {
+ Context *context;
+ explicit Callback(Context *c) : context(c) {}
+ void operate(map<string, bufferlist> *store) override {
+ context->complete(0);
+ }
+ };
+public:
+ class Transaction : public MapCacher::Transaction<string, bufferlist> {
+ friend class PausyAsyncMap;
+ list<Op> ops;
+ list<Op> callbacks;
+ public:
+ void set_keys(const map<string, bufferlist> &i) override {
+ ops.push_back(Op(new Insert(i)));
+ }
+ void remove_keys(const set<string> &r) override {
+ ops.push_back(Op(new Remove(r)));
+ }
+ void add_callback(Context *c) override {
+ callbacks.push_back(Op(new Callback(c)));
+ }
+ };
+private:
+
+ ceph::mutex lock = ceph::make_mutex("PausyAsyncMap");
+ map<string, bufferlist> store;
+
+ class Doer : public Thread {
+ static const size_t MAX_SIZE = 100;
+ PausyAsyncMap *parent;
+ ceph::mutex lock = ceph::make_mutex("Doer lock");
+ ceph::condition_variable cond;
+ int stopping;
+ bool paused;
+ list<Op> queue;
+ public:
+ explicit Doer(PausyAsyncMap *parent) :
+ parent(parent), stopping(0), paused(false) {}
+ void *entry() override {
+ while (1) {
+ list<Op> ops;
+ {
+ std::unique_lock l{lock};
+ cond.wait(l, [this] {
+ return stopping || (!queue.empty() && !paused);
+ });
+ if (stopping && queue.empty()) {
+ stopping = 2;
+ cond.notify_all();
+ return 0;
+ }
+ ceph_assert(!queue.empty());
+ ceph_assert(!paused);
+ ops.swap(queue);
+ cond.notify_all();
+ }
+ ceph_assert(!ops.empty());
+
+ for (list<Op>::iterator i = ops.begin();
+ i != ops.end();
+ ops.erase(i++)) {
+ if (!(rand()%3))
+ usleep(1+(rand() % 5000));
+ std::lock_guard l{parent->lock};
+ (*i)->operate(&(parent->store));
+ }
+ }
+ }
+
+ void pause() {
+ std::lock_guard l{lock};
+ paused = true;
+ cond.notify_all();
+ }
+
+ void resume() {
+ std::lock_guard l{lock};
+ paused = false;
+ cond.notify_all();
+ }
+
+ void submit(list<Op> &in) {
+ std::unique_lock l{lock};
+ cond.wait(l, [this] { return queue.size() < MAX_SIZE;});
+ queue.splice(queue.end(), in, in.begin(), in.end());
+ cond.notify_all();
+ }
+
+ void stop() {
+ std::unique_lock l{lock};
+ stopping = 1;
+ cond.notify_all();
+ cond.wait(l, [this] { return stopping == 2; });
+ cond.notify_all();
+ }
+ } doer;
+
+public:
+ PausyAsyncMap() : doer(this) {
+ doer.create("doer");
+ }
+ ~PausyAsyncMap() override {
+ doer.join();
+ }
+ int get_keys(
+ const set<string> &keys,
+ map<string, bufferlist> *out) override {
+ std::lock_guard l{lock};
+ for (set<string>::const_iterator i = keys.begin();
+ i != keys.end();
+ ++i) {
+ map<string, bufferlist>::iterator j = store.find(*i);
+ if (j != store.end())
+ out->insert(*j);
+ }
+ return 0;
+ }
+ int get_next(
+ const string &key,
+ pair<string, bufferlist> *next) override {
+ std::lock_guard l{lock};
+ map<string, bufferlist>::iterator j = store.upper_bound(key);
+ if (j != store.end()) {
+ if (next)
+ *next = *j;
+ return 0;
+ } else {
+ return -ENOENT;
+ }
+ }
+ int get_next_or_current(
+ const string &key,
+ pair<string, bufferlist> *next_or_current) override {
+ std::lock_guard l{lock};
+ map<string, bufferlist>::iterator j = store.lower_bound(key);
+ if (j != store.end()) {
+ if (next_or_current)
+ *next_or_current = *j;
+ return 0;
+ } else {
+ return -ENOENT;
+ }
+ }
+ void submit(Transaction *t) {
+ doer.submit(t->ops);
+ doer.submit(t->callbacks);
+ }
+
+ void flush() {
+ ceph::mutex lock = ceph::make_mutex("flush lock");
+ ceph::condition_variable cond;
+ bool done = false;
+
+ class OnFinish : public Context {
+ ceph::mutex *lock;
+ ceph::condition_variable *cond;
+ bool *done;
+ public:
+ OnFinish(ceph::mutex *lock, ceph::condition_variable *cond, bool *done)
+ : lock(lock), cond(cond), done(done) {}
+ void finish(int) override {
+ std::lock_guard l{*lock};
+ *done = true;
+ cond->notify_all();
+ }
+ };
+ Transaction t;
+ t.add_callback(new OnFinish(&lock, &cond, &done));
+ submit(&t);
+ {
+ std::unique_lock l{lock};
+ cond.wait(l, [&] { return done; });
+ }
+ }
+
+ void pause() {
+ doer.pause();
+ }
+ void resume() {
+ doer.resume();
+ }
+ void stop() {
+ doer.stop();
+ }
+
+};
+
+class MapCacherTest : public ::testing::Test {
+protected:
+ boost::scoped_ptr< PausyAsyncMap > driver;
+ boost::scoped_ptr<MapCacher::MapCacher<string, bufferlist> > cache;
+ map<string, bufferlist> truth;
+ set<string> names;
+public:
+ void assert_bl_eq(bufferlist &bl1, bufferlist &bl2) {
+ ASSERT_EQ(bl1.length(), bl2.length());
+ bufferlist::iterator j = bl2.begin();
+ for (bufferlist::iterator i = bl1.begin();
+ !i.end();
+ ++i, ++j) {
+ ASSERT_TRUE(!j.end());
+ ASSERT_EQ(*i, *j);
+ }
+ }
+ void assert_bl_map_eq(map<string, bufferlist> &m1,
+ map<string, bufferlist> &m2) {
+ ASSERT_EQ(m1.size(), m2.size());
+ map<string, bufferlist>::iterator j = m2.begin();
+ for (map<string, bufferlist>::iterator i = m1.begin();
+ i != m1.end();
+ ++i, ++j) {
+ ASSERT_TRUE(j != m2.end());
+ ASSERT_EQ(i->first, j->first);
+ assert_bl_eq(i->second, j->second);
+ }
+
+ }
+ size_t random_num() {
+ return random() % 10;
+ }
+ size_t random_size() {
+ return random() % 1000;
+ }
+ void random_bl(size_t size, bufferlist *bl) {
+ for (size_t i = 0; i < size; ++i) {
+ bl->append(rand());
+ }
+ }
+ void do_set() {
+ size_t set_size = random_num();
+ map<string, bufferlist> to_set;
+ for (size_t i = 0; i < set_size; ++i) {
+ bufferlist bl;
+ random_bl(random_size(), &bl);
+ string key = *rand_choose(names);
+ to_set.insert(
+ make_pair(key, bl));
+ }
+ for (map<string, bufferlist>::iterator i = to_set.begin();
+ i != to_set.end();
+ ++i) {
+ truth.erase(i->first);
+ truth.insert(*i);
+ }
+ {
+ PausyAsyncMap::Transaction t;
+ cache->set_keys(to_set, &t);
+ driver->submit(&t);
+ }
+ }
+ void remove() {
+ size_t remove_size = random_num();
+ set<string> to_remove;
+ for (size_t i = 0; i < remove_size ; ++i) {
+ to_remove.insert(*rand_choose(names));
+ }
+ for (set<string>::iterator i = to_remove.begin();
+ i != to_remove.end();
+ ++i) {
+ truth.erase(*i);
+ }
+ {
+ PausyAsyncMap::Transaction t;
+ cache->remove_keys(to_remove, &t);
+ driver->submit(&t);
+ }
+ }
+ void get() {
+ set<string> to_get;
+ size_t get_size = random_num();
+ for (size_t i = 0; i < get_size; ++i) {
+ to_get.insert(*rand_choose(names));
+ }
+
+ map<string, bufferlist> got_truth;
+ for (set<string>::iterator i = to_get.begin();
+ i != to_get.end();
+ ++i) {
+ map<string, bufferlist>::iterator j = truth.find(*i);
+ if (j != truth.end())
+ got_truth.insert(*j);
+ }
+
+ map<string, bufferlist> got;
+ cache->get_keys(to_get, &got);
+
+ assert_bl_map_eq(got, got_truth);
+ }
+
+ void get_next() {
+ string cur;
+ while (true) {
+ pair<string, bufferlist> next;
+ int r = cache->get_next(cur, &next);
+
+ pair<string, bufferlist> next_truth;
+ map<string, bufferlist>::iterator i = truth.upper_bound(cur);
+ int r_truth = (i == truth.end()) ? -ENOENT : 0;
+ if (i != truth.end())
+ next_truth = *i;
+
+ ASSERT_EQ(r, r_truth);
+ if (r == -ENOENT)
+ break;
+
+ ASSERT_EQ(next.first, next_truth.first);
+ assert_bl_eq(next.second, next_truth.second);
+ cur = next.first;
+ }
+ }
+ void SetUp() override {
+ driver.reset(new PausyAsyncMap());
+ cache.reset(new MapCacher::MapCacher<string, bufferlist>(driver.get()));
+ names.clear();
+ truth.clear();
+ size_t names_size(random_num() + 10);
+ for (size_t i = 0; i < names_size; ++i) {
+ names.insert(random_string(1 + (random_size() % 10)));
+ }
+ }
+ void TearDown() override {
+ driver->stop();
+ cache.reset();
+ driver.reset();
+ }
+
+};
+
+TEST_F(MapCacherTest, Simple)
+{
+ driver->pause();
+ map<string, bufferlist> truth;
+ set<string> truth_keys;
+ string blah("asdf");
+ bufferlist bl;
+ encode(blah, bl);
+ truth[string("asdf")] = bl;
+ truth_keys.insert(truth.begin()->first);
+ {
+ PausyAsyncMap::Transaction t;
+ cache->set_keys(truth, &t);
+ driver->submit(&t);
+ cache->set_keys(truth, &t);
+ driver->submit(&t);
+ }
+
+ map<string, bufferlist> got;
+ cache->get_keys(truth_keys, &got);
+ assert_bl_map_eq(got, truth);
+
+ driver->resume();
+ sleep(1);
+
+ got.clear();
+ cache->get_keys(truth_keys, &got);
+ assert_bl_map_eq(got, truth);
+}
+
+TEST_F(MapCacherTest, Random)
+{
+ for (size_t i = 0; i < 5000; ++i) {
+ if (!(i % 50)) {
+ std::cout << "On iteration " << i << std::endl;
+ }
+ switch (rand() % 4) {
+ case 0:
+ get();
+ break;
+ case 1:
+ do_set();
+ break;
+ case 2:
+ get_next();
+ break;
+ case 3:
+ remove();
+ break;
+ }
+ }
+}
+
+class MapperVerifier {
+ PausyAsyncMap *driver;
+ boost::scoped_ptr< SnapMapper > mapper;
+ map<snapid_t, set<hobject_t> > snap_to_hobject;
+ map<hobject_t, set<snapid_t>> hobject_to_snap;
+ snapid_t next;
+ uint32_t mask;
+ uint32_t bits;
+ ceph::mutex lock = ceph::make_mutex("lock");
+public:
+
+ MapperVerifier(
+ PausyAsyncMap *driver,
+ uint32_t mask,
+ uint32_t bits)
+ : driver(driver),
+ mapper(new SnapMapper(g_ceph_context, driver, mask, bits, 0, shard_id_t(1))),
+ mask(mask), bits(bits) {}
+
+ hobject_t random_hobject() {
+ return hobject_t(
+ random_string(1+(rand() % 16)),
+ random_string(1+(rand() % 16)),
+ snapid_t(rand() % 1000),
+ (rand() & ((~0)<<bits)) | (mask & ~((~0)<<bits)),
+ 0, random_string(rand() % 16));
+ }
+
+ void choose_random_snaps(int num, set<snapid_t> *snaps) {
+ ceph_assert(snaps);
+ ceph_assert(!snap_to_hobject.empty());
+ for (int i = 0; i < num || snaps->empty(); ++i) {
+ snaps->insert(rand_choose(snap_to_hobject)->first);
+ }
+ }
+
+ void create_snap() {
+ snap_to_hobject[next];
+ ++next;
+ }
+
+ void create_object() {
+ std::lock_guard l{lock};
+ if (snap_to_hobject.empty())
+ return;
+ hobject_t obj;
+ do {
+ obj = random_hobject();
+ } while (hobject_to_snap.count(obj));
+
+ set<snapid_t> &snaps = hobject_to_snap[obj];
+ choose_random_snaps(1 + (rand() % 20), &snaps);
+ for (set<snapid_t>::iterator i = snaps.begin();
+ i != snaps.end();
+ ++i) {
+ map<snapid_t, set<hobject_t> >::iterator j = snap_to_hobject.find(*i);
+ ceph_assert(j != snap_to_hobject.end());
+ j->second.insert(obj);
+ }
+ {
+ PausyAsyncMap::Transaction t;
+ mapper->add_oid(obj, snaps, &t);
+ driver->submit(&t);
+ }
+ }
+
+ std::pair<std::string, ceph::buffer::list> to_raw(
+ const std::pair<snapid_t, hobject_t> &to_map) {
+ return mapper->to_raw(to_map);
+ }
+
+ std::string to_legacy_raw_key(
+ const std::pair<snapid_t, hobject_t> &to_map) {
+ return mapper->to_legacy_raw_key(to_map);
+ }
+
+ template <typename... Args>
+ std::string to_object_key(Args&&... args) {
+ return mapper->to_object_key(std::forward<Args>(args)...);
+ }
+
+ std::string to_raw_key(
+ const std::pair<snapid_t, hobject_t> &to_map) {
+ return mapper->to_raw_key(to_map);
+ }
+
+ template <typename... Args>
+ std::string make_purged_snap_key(Args&&... args) {
+ return mapper->make_purged_snap_key(std::forward<Args>(args)...);
+ }
+
+ void trim_snap() {
+ std::lock_guard l{lock};
+ if (snap_to_hobject.empty())
+ return;
+ map<snapid_t, set<hobject_t> >::iterator snap =
+ rand_choose(snap_to_hobject);
+ set<hobject_t> hobjects = snap->second;
+
+ vector<hobject_t> hoids;
+ while (mapper->get_next_objects_to_trim(
+ snap->first, rand() % 5 + 1, &hoids) == 0) {
+ for (auto &&hoid: hoids) {
+ ceph_assert(!hoid.is_max());
+ ceph_assert(hobjects.count(hoid));
+ hobjects.erase(hoid);
+
+ map<hobject_t, set<snapid_t>>::iterator j =
+ hobject_to_snap.find(hoid);
+ ceph_assert(j->second.count(snap->first));
+ set<snapid_t> old_snaps(j->second);
+ j->second.erase(snap->first);
+
+ {
+ PausyAsyncMap::Transaction t;
+ mapper->update_snaps(
+ hoid,
+ j->second,
+ &old_snaps,
+ &t);
+ driver->submit(&t);
+ }
+ if (j->second.empty()) {
+ hobject_to_snap.erase(j);
+ }
+ hoid = hobject_t::get_max();
+ }
+ hoids.clear();
+ }
+ ceph_assert(hobjects.empty());
+ snap_to_hobject.erase(snap);
+ }
+
+ void remove_oid() {
+ std::lock_guard l{lock};
+ if (hobject_to_snap.empty())
+ return;
+ map<hobject_t, set<snapid_t>>::iterator obj =
+ rand_choose(hobject_to_snap);
+ for (set<snapid_t>::iterator i = obj->second.begin();
+ i != obj->second.end();
+ ++i) {
+ map<snapid_t, set<hobject_t> >::iterator j =
+ snap_to_hobject.find(*i);
+ ceph_assert(j->second.count(obj->first));
+ j->second.erase(obj->first);
+ }
+ {
+ PausyAsyncMap::Transaction t;
+ mapper->remove_oid(
+ obj->first,
+ &t);
+ driver->submit(&t);
+ }
+ hobject_to_snap.erase(obj);
+ }
+
+ void check_oid() {
+ std::lock_guard l{lock};
+ if (hobject_to_snap.empty())
+ return;
+ map<hobject_t, set<snapid_t>>::iterator obj =
+ rand_choose(hobject_to_snap);
+ set<snapid_t> snaps;
+ int r = mapper->get_snaps(obj->first, &snaps);
+ ceph_assert(r == 0);
+ ASSERT_EQ(snaps, obj->second);
+ }
+};
+
+class SnapMapperTest : public ::testing::Test {
+protected:
+ boost::scoped_ptr< PausyAsyncMap > driver;
+ map<pg_t, std::shared_ptr<MapperVerifier> > mappers;
+ uint32_t pgnum;
+
+ void SetUp() override {
+ driver.reset(new PausyAsyncMap());
+ pgnum = 0;
+ }
+
+ void TearDown() override {
+ driver->stop();
+ mappers.clear();
+ driver.reset();
+ }
+
+ MapperVerifier &get_tester() {
+ //return *(mappers.begin()->second);
+ return *(rand_choose(mappers)->second);
+ }
+
+ void init(uint32_t to_set) {
+ pgnum = to_set;
+ for (uint32_t i = 0; i < pgnum; ++i) {
+ pg_t pgid(i, 0);
+ mappers[pgid].reset(
+ new MapperVerifier(
+ driver.get(),
+ i,
+ pgid.get_split_bits(pgnum)
+ )
+ );
+ }
+ }
+
+ void run() {
+ for (int i = 0; i < 5000; ++i) {
+ if (!(i % 50))
+ std::cout << i << std::endl;
+ switch (rand() % 5) {
+ case 0:
+ get_tester().create_snap();
+ break;
+ case 1:
+ get_tester().create_object();
+ break;
+ case 2:
+ get_tester().trim_snap();
+ break;
+ case 3:
+ get_tester().check_oid();
+ break;
+ case 4:
+ get_tester().remove_oid();
+ break;
+ }
+ }
+ }
+};
+
+TEST_F(SnapMapperTest, Simple) {
+ init(1);
+ get_tester().create_snap();
+ get_tester().create_object();
+ get_tester().trim_snap();
+}
+
+TEST_F(SnapMapperTest, More) {
+ init(1);
+ run();
+}
+
+TEST_F(SnapMapperTest, MultiPG) {
+ init(50);
+ run();
+}
+
+// Check to_object_key against current format to detect accidental changes in encoding
+TEST_F(SnapMapperTest, CheckObjectKeyFormat) {
+ init(1);
+ // <object, test_raw_key>
+ std::vector<std::tuple<hobject_t, std::string>> object_to_object_key({
+ {hobject_t{"test_object", "", 20, 0x01234567, 20, ""},
+ "OBJ_.1_0000000000000014.76543210.14.test%uobject.."},
+ {hobject_t{"test._ob.ject", "k.ey", 20, 0x01234567, 20, ""},
+ "OBJ_.1_0000000000000014.76543210.14.test%e%uob%eject.k%eey."},
+ {hobject_t{"test_object", "", 20, 0x01234567, 20, "namespace"},
+ "OBJ_.1_0000000000000014.76543210.14.test%uobject..namespace"},
+ {hobject_t{
+ "test_object", "", std::numeric_limits<snapid_t>::max() - 20, 0x01234567,
+ std::numeric_limits<int64_t>::max() - 20, "namespace"},
+ "OBJ_.1_7FFFFFFFFFFFFFEB.76543210.ffffffffffffffec.test%uobject..namespace"}
+ });
+
+ for (auto &[object, test_object_key]: object_to_object_key) {
+ auto object_key = get_tester().to_object_key(object);
+ if (object_key != test_object_key) {
+ std::cout << object << " should be "
+ << test_object_key << " is "
+ << get_tester().to_object_key(object)
+ << std::endl;
+ }
+ ASSERT_EQ(object_key, test_object_key);
+ }
+}
+
+
+// Check to_raw_key against current format to detect accidental changes in encoding
+TEST_F(SnapMapperTest, CheckRawKeyFormat) {
+ init(1);
+ // <object, snapid, test_raw_key>
+ std::vector<std::tuple<hobject_t, snapid_t, std::string>> object_to_raw_key({
+ {hobject_t{"test_object", "", 20, 0x01234567, 20, ""}, 25,
+ "SNA_20_0000000000000019_.1_0000000000000014.76543210.14.test%uobject.."},
+ {hobject_t{"test._ob.ject", "k.ey", 20, 0x01234567, 20, ""}, 25,
+ "SNA_20_0000000000000019_.1_0000000000000014.76543210.14.test%e%uob%eject.k%eey."},
+ {hobject_t{"test_object", "", 20, 0x01234567, 20, "namespace"}, 25,
+ "SNA_20_0000000000000019_.1_0000000000000014.76543210.14.test%uobject..namespace"},
+ {hobject_t{
+ "test_object", "", std::numeric_limits<snapid_t>::max() - 20, 0x01234567,
+ std::numeric_limits<int64_t>::max() - 20, "namespace"}, std::numeric_limits<snapid_t>::max() - 20,
+ "SNA_9223372036854775787_FFFFFFFFFFFFFFEC_.1_7FFFFFFFFFFFFFEB.76543210.ffffffffffffffec.test%uobject..namespace"}
+ });
+
+ for (auto &[object, snap, test_raw_key]: object_to_raw_key) {
+ auto raw_key = get_tester().to_raw_key(std::make_pair(snap, object));
+ if (raw_key != test_raw_key) {
+ std::cout << object << " " << snap << " should be "
+ << test_raw_key << " is "
+ << get_tester().to_raw_key(std::make_pair(snap, object))
+ << std::endl;
+ }
+ ASSERT_EQ(raw_key, test_raw_key);
+ }
+}
+
+// Check make_purged_snap_key against current format to detect accidental changes
+// in encoding
+TEST_F(SnapMapperTest, CheckMakePurgedSnapKeyFormat) {
+ init(1);
+ // <pool, snap, test_key>
+ std::vector<std::tuple<int64_t, snapid_t, std::string>> purged_snap_to_key({
+ {20, 30, "PSN__20_000000000000001e"},
+ {std::numeric_limits<int64_t>::max() - 20,
+ std::numeric_limits<snapid_t>::max() - 20,
+ "PSN__9223372036854775787_ffffffffffffffec"}
+ });
+
+ for (auto &[pool, snap, test_key]: purged_snap_to_key) {
+ auto raw_purged_snap_key = get_tester().make_purged_snap_key(pool, snap);
+ if (raw_purged_snap_key != test_key) {
+ std::cout << "<" << pool << ", " << snap << "> should be " << test_key
+ << " is " << raw_purged_snap_key << std::endl;
+ }
+ // retesting (mostly for test numbers accounting)
+ ASSERT_EQ(raw_purged_snap_key, test_key);
+ }
+}
+
+TEST_F(SnapMapperTest, LegacyKeyConvertion) {
+ init(1);
+ auto obj = get_tester().random_hobject();
+ snapid_t snapid = random() % 10;
+ auto snap_obj = make_pair(snapid, obj);
+ auto raw = get_tester().to_raw(snap_obj);
+ std::string old_key = get_tester().to_legacy_raw_key(snap_obj);
+ std::string converted_key =
+ SnapMapper::convert_legacy_key(old_key, raw.second);
+ std::string new_key = get_tester().to_raw_key(snap_obj);
+ if (converted_key != new_key) {
+ std::cout << "Converted: " << old_key << "\nTo: " << converted_key
+ << "\nNew key: " << new_key << std::endl;
+ }
+ ASSERT_EQ(converted_key, new_key);
+}
+
+/**
+ * 'DirectMapper' provides simple, controlled, interface to the underlying
+ * SnapMapper.
+ */
+class DirectMapper {
+public:
+ std::unique_ptr<PausyAsyncMap> driver{make_unique<PausyAsyncMap>()};
+ std::unique_ptr<SnapMapper> mapper;
+ uint32_t mask;
+ uint32_t bits;
+ ceph::mutex lock = ceph::make_mutex("lock");
+
+ DirectMapper(
+ uint32_t mask,
+ uint32_t bits)
+ : mapper(new SnapMapper(g_ceph_context, driver.get(), mask, bits, 0, shard_id_t(1))),
+ mask(mask), bits(bits) {}
+
+ hobject_t random_hobject() {
+ return hobject_t(
+ random_string(1+(rand() % 16)),
+ random_string(1+(rand() % 16)),
+ snapid_t(rand() % 1000),
+ (rand() & ((~0)<<bits)) | (mask & ~((~0)<<bits)),
+ 0, random_string(rand() % 16));
+ }
+
+ void create_object(const hobject_t& obj, const set<snapid_t> &snaps) {
+ std::lock_guard l{lock};
+ PausyAsyncMap::Transaction t;
+ mapper->add_oid(obj, snaps, &t);
+ driver->submit(&t);
+ }
+
+ std::pair<std::string, ceph::buffer::list> to_raw(
+ const std::pair<snapid_t, hobject_t> &to_map) {
+ return mapper->to_raw(to_map);
+ }
+
+ std::string to_legacy_raw_key(
+ const std::pair<snapid_t, hobject_t> &to_map) {
+ return mapper->to_legacy_raw_key(to_map);
+ }
+
+ std::string to_raw_key(
+ const std::pair<snapid_t, hobject_t> &to_map) {
+ return mapper->to_raw_key(to_map);
+ }
+
+ void shorten_mapping_key(snapid_t snap, const hobject_t &clone)
+ {
+ // calculate the relevant key
+ std::string k = mapper->to_raw_key(snap, clone);
+
+ // find the value for this key
+ map<string, bufferlist> kvmap;
+ auto r = mapper->backend.get_keys(set{k}, &kvmap);
+ ASSERT_GE(r, 0);
+
+ // replace the key with its shortened version
+ PausyAsyncMap::Transaction t;
+ mapper->backend.remove_keys(set{k}, &t);
+ auto short_k = k.substr(0, 10);
+ mapper->backend.set_keys(map<string, bufferlist>{{short_k, kvmap[k]}}, &t);
+ driver->submit(&t);
+ driver->flush();
+ }
+};
+
+class DirectMapperTest : public ::testing::Test {
+ public:
+ // ctor & initialization
+ DirectMapperTest() = default;
+ ~DirectMapperTest() = default;
+ void SetUp() override;
+ void TearDown() override;
+
+ protected:
+ std::unique_ptr<DirectMapper> direct;
+};
+
+void DirectMapperTest::SetUp()
+{
+ direct = std::make_unique<DirectMapper>(0, 0);
+}
+
+void DirectMapperTest::TearDown()
+{
+ direct->driver->stop();
+ direct->mapper.reset();
+ direct->driver.reset();
+}
+
+
+TEST_F(DirectMapperTest, BasciObject)
+{
+ auto obj = direct->random_hobject();
+ set<snapid_t> snaps{100, 200};
+ direct->create_object(obj, snaps);
+
+ // verify that the OBJ_ & SNA_ entries are there
+ auto osn1 = direct->mapper->get_snaps(obj);
+ ASSERT_EQ(snaps, osn1);
+ auto vsn1 = direct->mapper->get_snaps_check_consistency(obj);
+ ASSERT_EQ(snaps, vsn1);
+}
+
+TEST_F(DirectMapperTest, CorruptedSnaRecord)
+{
+ object_t base_name{"obj"};
+ std::string key{"key"};
+
+ hobject_t head{base_name, key, CEPH_NOSNAP, 0x17, 0, ""};
+ hobject_t cln1{base_name, key, 10, 0x17, 0, ""};
+ hobject_t cln2{base_name, key, 20, 0x17, 0, ""}; // the oldest version
+ set<snapid_t> head_snaps{400, 500};
+ set<snapid_t> cln1_snaps{300};
+ set<snapid_t> cln2_snaps{100, 200};
+
+ PausyAsyncMap::Transaction t;
+ direct->mapper->add_oid(head, head_snaps, &t);
+ direct->mapper->add_oid(cln1, cln1_snaps, &t);
+ direct->mapper->add_oid(cln2, cln2_snaps, &t);
+ direct->driver->submit(&t);
+ direct->driver->flush();
+
+ // verify that the OBJ_ & SNA_ entries are there
+ {
+ auto osn1 = direct->mapper->get_snaps(cln1);
+ EXPECT_EQ(cln1_snaps, osn1);
+ auto osn2 = direct->mapper->get_snaps(cln2);
+ EXPECT_EQ(cln2_snaps, osn2);
+ auto osnh = direct->mapper->get_snaps(head);
+ EXPECT_EQ(head_snaps, osnh);
+ }
+ {
+ auto vsn1 = direct->mapper->get_snaps_check_consistency(cln1);
+ EXPECT_EQ(cln1_snaps, vsn1);
+ auto vsn2 = direct->mapper->get_snaps_check_consistency(cln2);
+ EXPECT_EQ(cln2_snaps, vsn2);
+ auto vsnh = direct->mapper->get_snaps_check_consistency(head);
+ EXPECT_EQ(head_snaps, vsnh);
+ }
+
+ // corrupt the SNA_ entry for cln1
+ direct->shorten_mapping_key(300, cln1);
+ {
+ auto vsnh = direct->mapper->get_snaps_check_consistency(head);
+ EXPECT_EQ(head_snaps, vsnh);
+ auto vsn1 = direct->mapper->get_snaps(cln1);
+ EXPECT_EQ(cln1_snaps, vsn1);
+ auto osn1 = direct->mapper->get_snaps_check_consistency(cln1);
+ EXPECT_NE(cln1_snaps, osn1);
+ auto vsn2 = direct->mapper->get_snaps_check_consistency(cln2);
+ EXPECT_EQ(cln2_snaps, vsn2);
+ }
+}
+
+///\todo test the case of a corrupted OBJ_ entry
diff --git a/src/test/test_split.sh b/src/test/test_split.sh
new file mode 100755
index 000000000..d86a08af2
--- /dev/null
+++ b/src/test/test_split.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+set -x
+
+#
+# Add some objects to the data PGs, and then test splitting those PGs
+#
+
+# Includes
+source "`dirname $0`/test_common.sh"
+
+TEST_POOL=rbd
+
+# Constants
+my_write_objects() {
+ write_objects $1 $2 10 1000000 $TEST_POOL
+}
+
+setup() {
+ export CEPH_NUM_OSD=$1
+
+ # Start ceph
+ ./stop.sh
+
+ ./vstart.sh -d -n
+}
+
+get_pgp_num() {
+ ./ceph -c ./ceph.conf osd pool get $TEST_POOL pgp_num > $TEMPDIR/pgp_num
+ [ $? -eq 0 ] || die "failed to get pgp_num"
+ PGP_NUM=`grep PGP_NUM $TEMPDIR/pgp_num | sed 's/.*PGP_NUM:\([ 0123456789]*\).*$/\1/'`
+}
+
+split1_impl() {
+ # Write lots and lots of objects
+ my_write_objects 1 2
+
+ get_pgp_num
+ echo "\$PGP_NUM=$PGP_NUM"
+
+ # Double the number of PGs
+ PGP_NUM=$((PGP_NUM*2))
+ echo "doubling PGP_NUM to $PGP_NUM..."
+ ./ceph -c ./ceph.conf osd pool set $TEST_POOL pgp_num $PGP_NUM
+
+ sleep 30
+
+ # success
+ return 0
+}
+
+split1() {
+ setup 2
+ split1_impl
+}
+
+many_pools() {
+ setup 3
+ for i in `seq 1 3000`; do
+ ./ceph -c ./ceph.conf osd pool create "pool${i}" 8 || die "pool create failed"
+ done
+ my_write_objects 1 10
+}
+
+run() {
+ split1 || die "test failed"
+}
+
+$@
diff --git a/src/test/test_str_list.cc b/src/test/test_str_list.cc
new file mode 100644
index 000000000..363395011
--- /dev/null
+++ b/src/test/test_str_list.cc
@@ -0,0 +1,45 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/str_list.h"
+
+#include "gtest/gtest.h"
+
+// SplitTest is parameterized for list/vector/set
+using Types = ::testing::Types<std::list<std::string>,
+ std::vector<std::string>>;
+
+template <typename T>
+struct SplitTest : ::testing::Test {
+ void test(const char* input, const char *delim,
+ const std::list<std::string>& expected) {
+ EXPECT_EQ(expected, get_str_list(input, delim));
+ }
+ void test(const char* input, const char *delim,
+ const std::vector<std::string>& expected) {
+ EXPECT_EQ(expected, get_str_vec(input, delim));
+ }
+};
+
+TYPED_TEST_SUITE(SplitTest, Types);
+
+TYPED_TEST(SplitTest, Get)
+{
+ this->test("", " ", TypeParam{});
+ this->test(" ", " ", TypeParam{});
+ this->test("foo", " ", TypeParam{"foo"});
+ this->test("foo bar", " ", TypeParam{"foo","bar"});
+ this->test(" foo bar", " ", TypeParam{"foo","bar"});
+ this->test("foo bar ", " ", TypeParam{"foo","bar"});
+ this->test("foo bar ", " ", TypeParam{"foo","bar"});
+
+ // default delimiter
+ const char *delims = ";,= \t";
+ this->test(" ; , = \t ", delims, TypeParam{});
+ this->test(" ; foo = \t ", delims, TypeParam{"foo"});
+ this->test("a,b,c", delims, TypeParam{"a","b","c"});
+ this->test("a\tb\tc\t", delims, TypeParam{"a","b","c"});
+ this->test("a, b, c", delims, TypeParam{"a","b","c"});
+ this->test("a b c", delims, TypeParam{"a","b","c"});
+ this->test("a=b=c", delims, TypeParam{"a","b","c"});
+}
diff --git a/src/test/test_stress_watch.cc b/src/test/test_stress_watch.cc
new file mode 100644
index 000000000..2058bbf1a
--- /dev/null
+++ b/src/test/test_stress_watch.cc
@@ -0,0 +1,123 @@
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/utime.h"
+#include "common/Thread.h"
+#include "common/Clock.h"
+#include "test/librados/test_cxx.h"
+
+#include "gtest/gtest.h"
+#include <semaphore.h>
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <iostream>
+#include <string>
+#include <atomic>
+
+#include "test/librados/testcase_cxx.h"
+
+
+using namespace librados;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+static sem_t sem;
+static std::atomic<bool> stop_flag = { false };
+
+class WatchNotifyTestCtx : public WatchCtx
+{
+public:
+ void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) override
+ {
+ sem_post(&sem);
+ }
+};
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+struct WatcherUnwatcher : public Thread {
+ string pool;
+ explicit WatcherUnwatcher(string& _pool) : pool(_pool) {}
+
+ void *entry() override {
+ Rados cluster;
+ connect_cluster_pp(cluster);
+ while (!stop_flag) {
+ IoCtx ioctx;
+ cluster.ioctx_create(pool.c_str(), ioctx);
+
+ uint64_t handle;
+ WatchNotifyTestCtx watch_ctx;
+ int r = ioctx.watch("foo", 0, &handle, &watch_ctx);
+ if (r == 0)
+ ioctx.unwatch("foo", handle);
+ ioctx.close();
+ }
+ return NULL;
+ }
+};
+
+typedef RadosTestParamPP WatchStress;
+
+INSTANTIATE_TEST_SUITE_P(WatchStressTests, WatchStress,
+ ::testing::Values("", "cache"));
+
+TEST_P(WatchStress, Stress1) {
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+ Rados ncluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, ncluster));
+ IoCtx nioctx;
+ ncluster.ioctx_create(pool_name.c_str(), nioctx);
+
+ WatcherUnwatcher *thr = new WatcherUnwatcher(pool_name);
+ thr->create("watcher_unwatch");
+ ASSERT_EQ(0, nioctx.create("foo", false));
+
+ for (unsigned i = 0; i < 75; ++i) {
+ std::cerr << "Iteration " << i << std::endl;
+ uint64_t handle;
+ Rados cluster;
+ IoCtx ioctx;
+ WatchNotifyTestCtx ctx;
+
+ connect_cluster_pp(cluster);
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+ ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+
+ bool do_blocklist = i % 2;
+ if (do_blocklist) {
+ cluster.test_blocklist_self(true);
+ std::cerr << "blocklisted" << std::endl;
+ sleep(1);
+ }
+
+ bufferlist bl2;
+ ASSERT_EQ(0, nioctx.notify("foo", 0, bl2));
+
+ if (do_blocklist) {
+ sleep(1); // Give a change to see an incorrect notify
+ } else {
+ TestAlarm alarm;
+ sem_wait(&sem);
+ }
+
+ if (do_blocklist) {
+ cluster.test_blocklist_self(false);
+ }
+
+ ioctx.unwatch("foo", handle);
+ ioctx.close();
+ }
+ stop_flag = true;
+ thr->join();
+ nioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, ncluster));
+ sem_destroy(&sem);
+}
+
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"
diff --git a/src/test/test_striper.cc b/src/test/test_striper.cc
new file mode 100644
index 000000000..ee70304eb
--- /dev/null
+++ b/src/test/test_striper.cc
@@ -0,0 +1,89 @@
+#include "gtest/gtest.h"
+#include "global/global_context.h"
+
+#include "osdc/Striper.h"
+
+using namespace std;
+
+TEST(Striper, Stripe1)
+{
+ file_layout_t l;
+
+ l.object_size = 262144;
+ l.stripe_unit = 4096;
+ l.stripe_count = 3;
+
+ vector<ObjectExtent> ex;
+ Striper::file_to_extents(g_ceph_context, 1, &l, 5006035, 46419, 5006035, ex);
+
+ cout << "result " << ex << std::endl;
+
+ ASSERT_EQ(3u, ex.size());
+ ASSERT_EQ(98304u, ex[0].truncate_size);
+ ASSERT_EQ(ex[1].offset, ex[1].truncate_size);
+ ASSERT_EQ(94208u, ex[2].truncate_size);
+}
+
+TEST(Striper, EmptyPartialResult)
+{
+ file_layout_t l;
+
+ l.object_size = 4194304;
+ l.stripe_unit = 4194304;
+ l.stripe_count = 1;
+
+ vector<ObjectExtent> ex;
+ Striper::file_to_extents(g_ceph_context, 1, &l, 725549056, 131072, 72554905600, ex);
+ cout << "ex " << ex << std::endl;
+ ASSERT_EQ(2u, ex.size());
+
+ Striper::StripedReadResult r;
+
+ bufferlist bl;
+ r.add_partial_result(g_ceph_context, bl, ex[1].buffer_extents);
+
+ bufferptr bp(65536);
+ bp.zero();
+ bl.append(bp);
+
+ r.add_partial_result(g_ceph_context, bl, ex[0].buffer_extents);
+
+ bufferlist outbl;
+ r.assemble_result(g_ceph_context, outbl, false);
+
+ ASSERT_EQ(65536u, outbl.length());
+}
+
+TEST(Striper, GetNumObj)
+{
+ file_layout_t l;
+
+ l.object_size = 262144;
+ l.stripe_unit = 4096;
+ l.stripe_count = 3;
+ uint64_t size,numobjs;
+ size = 6999;
+ numobjs = Striper::get_num_objects(l, size);
+ ASSERT_EQ(2u, numobjs);
+ size = 793320;
+ numobjs = Striper::get_num_objects(l, size);
+ ASSERT_EQ(5u, numobjs);
+ size = 805608;
+ numobjs = Striper::get_num_objects(l, size);
+ ASSERT_EQ(6u, numobjs);
+}
+
+TEST(Striper, GetFileOffset)
+{
+ file_layout_t l;
+
+ l.object_size = 262144;
+ l.stripe_unit = 4096;
+ l.stripe_count = 3;
+
+ uint64_t object_no = 100;
+ uint64_t object_off = 200000;
+ uint64_t file_offset = Striper::get_file_offset(
+ g_ceph_context, &l, object_no, object_off);
+ ASSERT_EQ(26549568u, file_offset);
+}
diff --git a/src/test/test_subprocess.cc b/src/test/test_subprocess.cc
new file mode 100644
index 000000000..7e7693e4c
--- /dev/null
+++ b/src/test/test_subprocess.cc
@@ -0,0 +1,314 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2015 Mirantis Inc
+ *
+ * Author: Mykola Golub <mgolub@mirantis.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <unistd.h>
+
+#include <iostream>
+
+#include "common/SubProcess.h"
+#include "common/safe_io.h"
+#include "gtest/gtest.h"
+#include "common/fork_function.h"
+
+#ifdef _WIN32
+// Some of the tests expect GNU binaries to be available. We'll just rely on
+// the ones provided by Msys (which also comes with Git for Windows).
+#define SHELL "bash.exe"
+#else
+#define SHELL "/bin/sh"
+#endif
+
+bool read_from_fd(int fd, std::string &out) {
+ out.clear();
+ char buf[1024];
+ ssize_t n = safe_read(fd, buf, sizeof(buf) - 1);
+ if (n < 0)
+ return false;
+ buf[n] = '\0';
+ out = buf;
+ return true;
+}
+
+TEST(SubProcess, True)
+{
+ SubProcess p("true");
+ ASSERT_EQ(p.spawn(), 0);
+ ASSERT_EQ(p.join(), 0);
+ ASSERT_TRUE(p.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcess, False)
+{
+ SubProcess p("false");
+ ASSERT_EQ(p.spawn(), 0);
+ ASSERT_EQ(p.join(), 1);
+ ASSERT_FALSE(p.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcess, NotFound)
+{
+ SubProcess p("NOTEXISTENTBINARY", SubProcess::CLOSE, SubProcess::CLOSE, SubProcess::PIPE);
+ #ifdef _WIN32
+ // Windows will error out early.
+ ASSERT_EQ(p.spawn(), -1);
+ #else
+ ASSERT_EQ(p.spawn(), 0);
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(p.get_stderr(), buf));
+ std::cerr << "stderr: " << buf;
+ ASSERT_EQ(p.join(), 1);
+ std::cerr << "err: " << p.err() << std::endl;
+ ASSERT_FALSE(p.err().c_str()[0] == '\0');
+ #endif
+}
+
+TEST(SubProcess, Echo)
+{
+ SubProcess echo("echo", SubProcess::CLOSE, SubProcess::PIPE);
+ echo.add_cmd_args("1", "2", "3", NULL);
+
+ ASSERT_EQ(echo.spawn(), 0);
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(echo.get_stdout(), buf));
+ std::cerr << "stdout: " << buf;
+ ASSERT_EQ(buf, "1 2 3\n");
+ ASSERT_EQ(echo.join(), 0);
+ ASSERT_TRUE(echo.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcess, Cat)
+{
+ SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE);
+
+ ASSERT_EQ(cat.spawn(), 0);
+ std::string msg("to my, trociny!");
+ int n = write(cat.get_stdin(), msg.c_str(), msg.size());
+ ASSERT_EQ(n, (int)msg.size());
+ cat.close_stdin();
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(cat.get_stdout(), buf));
+ std::cerr << "stdout: " << buf << std::endl;
+ ASSERT_EQ(buf, msg);
+ ASSERT_TRUE(read_from_fd(cat.get_stderr(), buf));
+ ASSERT_EQ(buf, "");
+ ASSERT_EQ(cat.join(), 0);
+ ASSERT_TRUE(cat.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcess, CatDevNull)
+{
+ SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE);
+ cat.add_cmd_arg("/dev/null");
+
+ ASSERT_EQ(cat.spawn(), 0);
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(cat.get_stdout(), buf));
+ ASSERT_EQ(buf, "");
+ ASSERT_TRUE(read_from_fd(cat.get_stderr(), buf));
+ ASSERT_EQ(buf, "");
+ ASSERT_EQ(cat.join(), 0);
+ ASSERT_TRUE(cat.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcess, Killed)
+{
+ SubProcessTimed cat("cat", SubProcess::PIPE, SubProcess::PIPE);
+
+ ASSERT_EQ(cat.spawn(), 0);
+ cat.kill();
+ ASSERT_EQ(cat.join(), 128 + SIGTERM);
+ std::cerr << "err: " << cat.err() << std::endl;
+ ASSERT_FALSE(cat.err().c_str()[0] == '\0');
+}
+
+#ifndef _WIN32
+TEST(SubProcess, CatWithArgs)
+{
+ SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE);
+ cat.add_cmd_args("/dev/stdin", "/dev/null", "/NOTEXIST", NULL);
+
+ ASSERT_EQ(cat.spawn(), 0);
+ std::string msg("Hello, Word!");
+ int n = write(cat.get_stdin(), msg.c_str(), msg.size());
+ ASSERT_EQ(n, (int)msg.size());
+ cat.close_stdin();
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(cat.get_stdout(), buf));
+ std::cerr << "stdout: " << buf << std::endl;
+ ASSERT_EQ(buf, msg);
+ ASSERT_TRUE(read_from_fd(cat.get_stderr(), buf));
+ std::cerr << "stderr: " << buf;
+ ASSERT_FALSE(buf.empty());
+ ASSERT_EQ(cat.join(), 1);
+ std::cerr << "err: " << cat.err() << std::endl;
+ ASSERT_FALSE(cat.err().c_str()[0] == '\0');
+}
+#endif
+
+TEST(SubProcess, Subshell)
+{
+ SubProcess sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE);
+ sh.add_cmd_args("-c",
+ "sleep 0; "
+ "cat; "
+ "echo 'error from subshell' >&2; "
+ SHELL " -c 'exit 13'", NULL);
+ ASSERT_EQ(sh.spawn(), 0);
+ std::string msg("hello via subshell");
+ int n = write(sh.get_stdin(), msg.c_str(), msg.size());
+ ASSERT_EQ(n, (int)msg.size());
+ sh.close_stdin();
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(sh.get_stdout(), buf));
+ std::cerr << "stdout: " << buf << std::endl;
+ ASSERT_EQ(buf, msg);
+ ASSERT_TRUE(read_from_fd(sh.get_stderr(), buf));
+ std::cerr << "stderr: " << buf;
+ ASSERT_EQ(buf, "error from subshell\n");
+ ASSERT_EQ(sh.join(), 13);
+ std::cerr << "err: " << sh.err() << std::endl;
+ ASSERT_FALSE(sh.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcessTimed, True)
+{
+ SubProcessTimed p("true", SubProcess::CLOSE, SubProcess::CLOSE, SubProcess::CLOSE, 10);
+ ASSERT_EQ(p.spawn(), 0);
+ ASSERT_EQ(p.join(), 0);
+ ASSERT_TRUE(p.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcessTimed, SleepNoTimeout)
+{
+ SubProcessTimed sleep("sleep", SubProcess::CLOSE, SubProcess::CLOSE, SubProcess::CLOSE, 0);
+ sleep.add_cmd_arg("1");
+
+ ASSERT_EQ(sleep.spawn(), 0);
+ ASSERT_EQ(sleep.join(), 0);
+ ASSERT_TRUE(sleep.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcessTimed, Killed)
+{
+ SubProcessTimed cat("cat", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 5);
+
+ ASSERT_EQ(cat.spawn(), 0);
+ cat.kill();
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(cat.get_stdout(), buf));
+ ASSERT_TRUE(buf.empty());
+ ASSERT_TRUE(read_from_fd(cat.get_stderr(), buf));
+ ASSERT_TRUE(buf.empty());
+ ASSERT_EQ(cat.join(), 128 + SIGTERM);
+ std::cerr << "err: " << cat.err() << std::endl;
+ ASSERT_FALSE(cat.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcessTimed, SleepTimedout)
+{
+ SubProcessTimed sleep("sleep", SubProcess::CLOSE, SubProcess::CLOSE, SubProcess::PIPE, 1);
+ sleep.add_cmd_arg("10");
+
+ ASSERT_EQ(sleep.spawn(), 0);
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(sleep.get_stderr(), buf));
+ #ifndef _WIN32
+ std::cerr << "stderr: " << buf;
+ ASSERT_FALSE(buf.empty());
+ #endif
+ ASSERT_EQ(sleep.join(), 128 + SIGKILL);
+ std::cerr << "err: " << sleep.err() << std::endl;
+ ASSERT_FALSE(sleep.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcessTimed, SubshellNoTimeout)
+{
+ SubProcessTimed sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 0);
+ sh.add_cmd_args("-c", "cat >&2", NULL);
+ ASSERT_EQ(sh.spawn(), 0);
+ std::string msg("the quick brown fox jumps over the lazy dog");
+ int n = write(sh.get_stdin(), msg.c_str(), msg.size());
+ ASSERT_EQ(n, (int)msg.size());
+ sh.close_stdin();
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(sh.get_stdout(), buf));
+ std::cerr << "stdout: " << buf << std::endl;
+ ASSERT_TRUE(buf.empty());
+ ASSERT_TRUE(read_from_fd(sh.get_stderr(), buf));
+ std::cerr << "stderr: " << buf << std::endl;
+ ASSERT_EQ(buf, msg);
+ ASSERT_EQ(sh.join(), 0);
+ ASSERT_TRUE(sh.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcessTimed, SubshellKilled)
+{
+ SubProcessTimed sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 10);
+ sh.add_cmd_args("-c", SHELL "-c cat", NULL);
+ ASSERT_EQ(sh.spawn(), 0);
+ std::string msg("etaoin shrdlu");
+ int n = write(sh.get_stdin(), msg.c_str(), msg.size());
+ ASSERT_EQ(n, (int)msg.size());
+ sh.kill();
+ std::string buf;
+ ASSERT_TRUE(read_from_fd(sh.get_stderr(), buf));
+ ASSERT_TRUE(buf.empty());
+ ASSERT_EQ(sh.join(), 128 + SIGTERM);
+ std::cerr << "err: " << sh.err() << std::endl;
+ ASSERT_FALSE(sh.err().c_str()[0] == '\0');
+}
+
+TEST(SubProcessTimed, SubshellTimedout)
+{
+ SubProcessTimed sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 1, SIGTERM);
+ sh.add_cmd_args("-c", "sleep 1000& cat; NEVER REACHED", NULL);
+ ASSERT_EQ(sh.spawn(), 0);
+ std::string buf;
+ #ifndef _WIN32
+ ASSERT_TRUE(read_from_fd(sh.get_stderr(), buf));
+ std::cerr << "stderr: " << buf;
+ ASSERT_FALSE(buf.empty());
+ #endif
+ ASSERT_EQ(sh.join(), 128 + SIGTERM);
+ std::cerr << "err: " << sh.err() << std::endl;
+ ASSERT_FALSE(sh.err().c_str()[0] == '\0');
+}
+
+#ifndef _WIN32
+TEST(fork_function, normal)
+{
+ ASSERT_EQ(0, fork_function(10, std::cerr, [&]() { return 0; }));
+ ASSERT_EQ(1, fork_function(10, std::cerr, [&]() { return 1; }));
+ ASSERT_EQ(13, fork_function(10, std::cerr, [&]() { return 13; }));
+ ASSERT_EQ(-1, fork_function(10, std::cerr, [&]() { return -1; }));
+ ASSERT_EQ(-13, fork_function(10, std::cerr, [&]() { return -13; }));
+ ASSERT_EQ(-ETIMEDOUT,
+ fork_function(10, std::cerr, [&]() { return -ETIMEDOUT; }));
+}
+
+TEST(fork_function, timeout)
+{
+ ASSERT_EQ(-ETIMEDOUT, fork_function(2, std::cerr, [&]() {
+ sleep(60);
+ return 0; }));
+ ASSERT_EQ(-ETIMEDOUT, fork_function(2, std::cerr, [&]() {
+ sleep(60);
+ return 1; }));
+ ASSERT_EQ(-ETIMEDOUT, fork_function(2, std::cerr, [&]() {
+ sleep(60);
+ return -111; }));
+}
+#endif
diff --git a/src/test/test_texttable.cc b/src/test/test_texttable.cc
new file mode 100644
index 000000000..290f6f85f
--- /dev/null
+++ b/src/test/test_texttable.cc
@@ -0,0 +1,78 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2012 Inktank Storage, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/TextTable.h"
+#include <iostream>
+#include "gtest/gtest.h"
+#include "include/coredumpctl.h"
+
+TEST(TextTable, Alignment) {
+ TextTable t;
+
+ // test alignment
+ // 3 5-character columns
+ t.define_column("HEAD1", TextTable::LEFT, TextTable::LEFT);
+ t.define_column("HEAD2", TextTable::LEFT, TextTable::CENTER);
+ t.define_column("HEAD3", TextTable::LEFT, TextTable::RIGHT);
+
+ t << "1" << 2 << 3 << TextTable::endrow;
+ std::ostringstream oss;
+ oss << t;
+ ASSERT_STREQ("HEAD1 HEAD2 HEAD3\n1 2 3\n", oss.str().c_str());
+}
+
+TEST(TextTable, WidenAndClearShrink) {
+ TextTable t;
+
+ t.define_column("1", TextTable::LEFT, TextTable::LEFT);
+
+ // default column size is 1, widen to 5
+ t << "wider";
+
+ // validate wide output
+ std::ostringstream oss;
+ oss << t;
+ ASSERT_STREQ("1 \nwider\n", oss.str().c_str());
+ oss.str("");
+
+ // reset, validate single-char width output
+ t.clear();
+ t << "s";
+ oss << t;
+ ASSERT_STREQ("1\ns\n", oss.str().c_str());
+}
+
+TEST(TextTable, Indent) {
+ TextTable t;
+
+ t.define_column("1", TextTable::LEFT, TextTable::LEFT);
+ t.set_indent(10);
+ t << "s";
+ std::ostringstream oss;
+ oss << t;
+ ASSERT_STREQ(" 1\n s\n", oss.str().c_str());
+}
+
+
+TEST(TextTable, TooManyItems) {
+ TextTable t;
+
+ t.define_column("1", TextTable::LEFT, TextTable::LEFT);
+ t.define_column("2", TextTable::LEFT, TextTable::LEFT);
+ t.define_column("3", TextTable::LEFT, TextTable::LEFT);
+
+ // expect assertion failure on this, which throws FailedAssertion
+ PrCtl unset_dumpable;
+ ASSERT_DEATH((t << "1" << "2" << "3" << "4" << TextTable::endrow), "");
+}
diff --git a/src/test/test_trans.cc b/src/test/test_trans.cc
new file mode 100644
index 000000000..f843e0037
--- /dev/null
+++ b/src/test/test_trans.cc
@@ -0,0 +1,80 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include "common/ceph_argparse.h"
+#include "common/debug.h"
+#include "os/bluestore/BlueStore.h"
+#include "global/global_init.h"
+#include "include/ceph_assert.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_filestore
+#undef dout_prefix
+#define dout_prefix *_dout
+
+using namespace std;
+
+struct Foo : public Thread {
+ void *entry() override {
+ dout(0) << "foo started" << dendl;
+ sleep(1);
+ dout(0) << "foo asserting 0" << dendl;
+ ceph_abort();
+ }
+} foo;
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ // args
+ if (args.size() < 2) return -1;
+ const char *filename = args[0];
+ int mb = atoi(args[1]);
+
+ cout << "#dev " << filename << std::endl;
+ cout << "#mb " << mb << std::endl;
+
+ ObjectStore *fs = new BlueStore(cct.get(), filename);
+ if (fs->mount() < 0) {
+ cout << "mount failed" << std::endl;
+ return -1;
+ }
+
+ ObjectStore::Transaction t;
+ char buf[1 << 20];
+ bufferlist bl;
+ bl.append(buf, sizeof(buf));
+ auto ch = fs->create_new_collection(coll_t());
+ t.create_collection(coll_t(), 0);
+
+ for (int i=0; i<mb; i++) {
+ char f[30];
+ snprintf(f, sizeof(f), "foo%d\n", i);
+ sobject_t soid(f, CEPH_NOSNAP);
+ t.write(coll_t(), ghobject_t(hobject_t(soid)), 0, bl.length(), bl);
+ }
+
+ dout(0) << "starting thread" << dendl;
+ foo.create("foo");
+ dout(0) << "starting op" << dendl;
+ fs->queue_transaction(ch, std::move(t));
+}
+
diff --git a/src/test/test_unfound.sh b/src/test/test_unfound.sh
new file mode 100755
index 000000000..36a85aea0
--- /dev/null
+++ b/src/test/test_unfound.sh
@@ -0,0 +1,139 @@
+#!/usr/bin/env bash
+set -x
+
+#
+# Creates some unfound objects and then tests finding them.
+#
+
+# Includes
+source "`dirname $0`/test_common.sh"
+
+TEST_POOL=rbd
+
+# Functions
+my_write_objects() {
+ write_objects $1 $2 10 1000000 $TEST_POOL
+}
+
+setup() {
+ export CEPH_NUM_OSD=$1
+
+ # Start ceph
+ ./stop.sh
+
+ # set recovery start to a really long time to ensure that we don't start recovery
+ ./vstart.sh -d -n -o 'osd recovery delay start = 10000
+osd max scrubs = 0' || die "vstart failed"
+}
+
+osd_resurrection_1_impl() {
+ # Write lots and lots of objects
+ my_write_objects 1 2
+
+ # Take down osd1
+ stop_osd 1
+
+ # Continue writing a lot of objects
+ my_write_objects 3 4
+
+ # Bring up osd1
+ restart_osd 1
+
+ # Finish peering.
+ sleep 15
+
+ # Stop osd0.
+ # At this point we have peered, but *NOT* recovered.
+ # Objects should be lost.
+ stop_osd 0
+
+ poll_cmd "./ceph pg debug unfound_objects_exist" TRUE 3 120
+ [ $? -eq 1 ] || die "Failed to see unfound objects."
+ echo "Got unfound objects."
+
+ (
+ ./rados -c ./ceph.conf -p $TEST_POOL get obj01 $TEMPDIR/obj01 || die "radostool failed"
+ ) &
+ sleep 5
+ [ -e $TEMPDIR/obj01 ] && die "unexpected error: fetched unfound object?"
+
+ restart_osd 0
+
+ poll_cmd "./ceph pg debug unfound_objects_exist" FALSE 3 120
+ [ $? -eq 1 ] || die "Failed to recover unfound objects."
+
+ wait
+ [ -e $TEMPDIR/obj01 ] || die "unexpected error: failed to fetched newly-found object"
+
+ # Turn off recovery delay start and verify that every osd gets copies
+ # of the correct objects.
+ echo "starting recovery..."
+ start_recovery 2
+
+ # success
+ return 0
+}
+
+osd_resurrection_1() {
+ setup 2
+ osd_resurrection_1_impl
+}
+
+stray_test_impl() {
+ stop_osd 0
+ # 0:stopped 1:active 2:active
+
+ my_write_objects 1 1
+
+ stop_osd 1
+ sleep 15
+ # 0:stopped 1:stopped(ver1) 2:active(ver1)
+
+ my_write_objects 2 2
+
+ restart_osd 1
+ sleep 15
+ # 0:stopped 1:active(ver1) 2:active(ver2)
+
+ stop_osd 2
+ sleep 15
+ # 0:stopped 1:active(ver1) 2:stopped(ver2)
+
+ restart_osd 0
+ sleep 15
+ # 0:active 1:active(ver1) 2:stopped(ver2)
+
+ poll_cmd "./ceph pg debug unfound_objects_exist" TRUE 5 300
+ [ $? -eq 1 ] || die "Failed to see unfound objects."
+
+ #
+ # Now, when we bring up osd2, it will be considered a stray. However, it
+ # has the version that we need-- the very latest version of the
+ # objects.
+ #
+
+ restart_osd 2
+ sleep 15
+
+ poll_cmd "./ceph pg debug unfound_objects_exist" FALSE 4 240
+ [ $? -eq 1 ] || die "Failed to discover unfound objects."
+
+ echo "starting recovery..."
+ start_recovery 3
+
+ # success
+ return 0
+}
+
+stray_test() {
+ setup 3
+ stray_test_impl
+}
+
+run() {
+ osd_resurrection_1 || die "test failed"
+
+ stray_test || die "test failed"
+}
+
+$@
diff --git a/src/test/test_utime.cc b/src/test/test_utime.cc
new file mode 100644
index 000000000..b1cee0e80
--- /dev/null
+++ b/src/test/test_utime.cc
@@ -0,0 +1,67 @@
+#include "include/utime.h"
+#include "gtest/gtest.h"
+#include "include/stringify.h"
+#include "common/ceph_context.h"
+
+using namespace std;
+
+TEST(utime_t, localtime)
+{
+ utime_t t(1556122013, 839991182);
+ string s = stringify(t);
+ cout << s << std::endl;
+ // time zone may vary where unit test is run, so be cirsumspect...
+ ASSERT_EQ(s.size(), strlen("2019-04-24T11:06:53.839991-0500"));
+ ASSERT_TRUE(s[26] == '-' || s[26] == '+');
+ ASSERT_EQ(s.substr(0, 9), "2019-04-2");
+}
+
+TEST(utime_t, gmtime)
+{
+ utime_t t(1556122013, 39991182);
+ {
+ ostringstream ss;
+ t.gmtime(ss);
+ ASSERT_EQ(ss.str(), "2019-04-24T16:06:53.039991Z");
+ }
+ {
+ ostringstream ss;
+ t.gmtime_nsec(ss);
+ ASSERT_EQ(ss.str(), "2019-04-24T16:06:53.039991182Z");
+ }
+}
+
+TEST(utime_t, asctime)
+{
+ utime_t t(1556122013, 839991182);
+ ostringstream ss;
+ t.asctime(ss);
+ string s = ss.str();
+ ASSERT_EQ(s, "Wed Apr 24 16:06:53 2019");
+}
+
+const char *v[][2] = {
+ { "2019-04-24T16:06:53.039991Z", "2019-04-24T16:06:53.039991Z" },
+ { "2019-04-24 16:06:53.039991Z", "2019-04-24T16:06:53.039991Z" },
+ { "2019-04-24 16:06:53.039991+0000", "2019-04-24T16:06:53.039991Z" },
+ { "2019-04-24 16:06:53.039991-0100", "2019-04-24T17:06:53.039991Z" },
+ { "2019-04-24 16:06:53.039991+0430", "2019-04-24T11:36:53.039991Z" },
+ { "2019-04-24 16:06:53+0000", "2019-04-24T16:06:53.000000Z" },
+ { "2019-04-24T16:06:53-0100", "2019-04-24T17:06:53.000000Z" },
+ { "2019-04-24 16:06:53+0430", "2019-04-24T11:36:53.000000Z" },
+ { "2019-04-24", "2019-04-24T00:00:00.000000Z" },
+ { 0, 0 },
+};
+
+TEST(utime_t, parse_date)
+{
+ for (unsigned i = 0; v[i][0]; ++i) {
+ cout << v[i][0] << " -> " << v[i][1] << std::endl;
+ utime_t t;
+ bool r = t.parse(string(v[i][0]));
+ ASSERT_TRUE(r);
+ ostringstream ss;
+ t.gmtime(ss);
+ ASSERT_EQ(ss.str(), v[i][1]);
+ }
+}
diff --git a/src/test/test_weighted_shuffle.cc b/src/test/test_weighted_shuffle.cc
new file mode 100644
index 000000000..9f92cbdc0
--- /dev/null
+++ b/src/test/test_weighted_shuffle.cc
@@ -0,0 +1,39 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/weighted_shuffle.h"
+#include <array>
+#include <map>
+#include "gtest/gtest.h"
+
+TEST(WeightedShuffle, Basic) {
+ std::array<char, 5> choices{'a', 'b', 'c', 'd', 'e'};
+ std::array<int, 5> weights{100, 50, 25, 10, 1};
+ std::map<char, std::array<unsigned, 5>> frequency {
+ {'a', {0, 0, 0, 0, 0}},
+ {'b', {0, 0, 0, 0, 0}},
+ {'c', {0, 0, 0, 0, 0}},
+ {'d', {0, 0, 0, 0, 0}},
+ {'e', {0, 0, 0, 0, 0}}
+ }; // count each element appearing in each position
+ const int samples = 10000;
+ std::random_device rd;
+ for (auto i = 0; i < samples; i++) {
+ weighted_shuffle(begin(choices), end(choices),
+ begin(weights), end(weights),
+ std::mt19937{rd()});
+ for (size_t j = 0; j < choices.size(); ++j)
+ ++frequency[choices[j]][j];
+ }
+ // verify that the probability that the nth choice is selected as the first
+ // one is the nth weight divided by the sum of all weights
+ const auto total_weight = std::accumulate(weights.begin(), weights.end(), 0);
+ constexpr float epsilon = 0.02;
+ for (unsigned i = 0; i < choices.size(); i++) {
+ const auto& f = frequency[choices[i]];
+ const auto& w = weights[i];
+ ASSERT_NEAR(float(w) / total_weight,
+ float(f.front()) / samples,
+ epsilon);
+ }
+}
diff --git a/src/test/test_workqueue.cc b/src/test/test_workqueue.cc
new file mode 100644
index 000000000..d3354d633
--- /dev/null
+++ b/src/test/test_workqueue.cc
@@ -0,0 +1,98 @@
+#include "gtest/gtest.h"
+
+#include "common/WorkQueue.h"
+#include "common/ceph_argparse.h"
+
+using namespace std;
+
+TEST(WorkQueue, StartStop)
+{
+ ThreadPool tp(g_ceph_context, "foo", "tp_foo", 10, "");
+
+ tp.start();
+ tp.pause();
+ tp.pause_new();
+ tp.unpause();
+ tp.unpause();
+ tp.drain();
+ tp.stop();
+}
+
+TEST(WorkQueue, Resize)
+{
+ ThreadPool tp(g_ceph_context, "bar", "tp_bar", 2, "filestore_op_threads");
+
+ tp.start();
+
+ sleep(1);
+ ASSERT_EQ(2, tp.get_num_threads());
+
+ g_conf().set_val("filestore op threads", "5");
+ g_conf().apply_changes(&cout);
+ sleep(1);
+ ASSERT_EQ(5, tp.get_num_threads());
+
+ g_conf().set_val("filestore op threads", "3");
+ g_conf().apply_changes(&cout);
+ sleep(1);
+ ASSERT_EQ(3, tp.get_num_threads());
+
+ g_conf().set_val("filestore op threads", "0");
+ g_conf().apply_changes(&cout);
+ sleep(1);
+ ASSERT_EQ(0, tp.get_num_threads());
+
+ g_conf().set_val("filestore op threads", "15");
+ g_conf().apply_changes(&cout);
+ sleep(1);
+ ASSERT_EQ(15, tp.get_num_threads());
+
+ g_conf().set_val("filestore op threads", "-1");
+ g_conf().apply_changes(&cout);
+ sleep(1);
+ ASSERT_EQ(15, tp.get_num_threads());
+
+ sleep(1);
+ tp.stop();
+}
+
+class twq : public ThreadPool::WorkQueue<int> {
+public:
+ twq(time_t timeout, time_t suicide_timeout, ThreadPool *tp)
+ : ThreadPool::WorkQueue<int>("test_wq", ceph::make_timespan(timeout), ceph::make_timespan(suicide_timeout), tp) {}
+
+ bool _enqueue(int* item) override {
+ return true;
+ }
+ void _dequeue(int* item) override {
+ ceph_abort();
+ }
+ bool _empty() override {
+ return true;
+ }
+ int *_dequeue() override {
+ return nullptr;
+ }
+ void _process(int *osr, ThreadPool::TPHandle &handle) override {
+ }
+ void _process_finish(int *osr) override {
+ }
+ void _clear() override {
+ }
+};
+
+TEST(WorkQueue, change_timeout){
+ ThreadPool tp(g_ceph_context, "bar", "tp_bar", 2, "filestore_op_threads");
+ tp.start();
+ twq wq(2, 20, &tp);
+ // check timeout and suicide
+ ASSERT_EQ(ceph::make_timespan(2), wq.timeout_interval);
+ ASSERT_EQ(ceph::make_timespan(20), wq.suicide_interval);
+
+ // change the timeout and suicide and then check them
+ wq.set_timeout(4);
+ wq.set_suicide_timeout(40);
+ ASSERT_EQ(ceph::make_timespan(4), wq.timeout_interval);
+ ASSERT_EQ(ceph::make_timespan(40), wq.suicide_interval);
+ tp.stop();
+}
diff --git a/src/test/test_xlist.cc b/src/test/test_xlist.cc
new file mode 100644
index 000000000..35b62edcf
--- /dev/null
+++ b/src/test/test_xlist.cc
@@ -0,0 +1,118 @@
+#include <algorithm>
+#include <iterator>
+#include <vector>
+#include "include/xlist.h"
+
+#include "gtest/gtest.h"
+
+
+struct Item {
+ xlist<Item*>::item xitem;
+ int val;
+
+ explicit Item(int v) :
+ xitem(this),
+ val(v)
+ {}
+};
+
+class XlistTest : public testing::Test
+{
+protected:
+ typedef xlist<Item*> ItemList;
+ typedef std::vector<Item*> Items;
+ typedef std::vector<ItemList::item*> Refs;
+ Items items;
+ // for filling up an ItemList
+ Refs refs;
+
+ void SetUp() override {
+ for (int i = 0; i < 13; i++) {
+ items.push_back(new Item(i));
+ refs.push_back(&items.back()->xitem);
+ }
+ }
+ void TearDown() override {
+ for (Items::iterator i = items.begin(); i != items.end(); ++i) {
+ delete *i;
+ }
+ items.clear();
+ }
+};
+
+TEST_F(XlistTest, capability) {
+ ItemList list;
+ ASSERT_TRUE(list.empty());
+ ASSERT_EQ(0u, list.size());
+
+ std::copy(refs.begin(), refs.end(), std::back_inserter(list));
+ ASSERT_EQ((size_t)list.size(), refs.size());
+
+ list.clear();
+ ASSERT_TRUE(list.empty());
+ ASSERT_EQ(0u, list.size());
+}
+
+TEST_F(XlistTest, traverse) {
+ ItemList list;
+ std::copy(refs.begin(), refs.end(), std::back_inserter(list));
+
+ // advance until iterator::end()
+ size_t index = 0;
+ for (ItemList::iterator i = list.begin(); !i.end(); ++i) {
+ ASSERT_EQ(*i, items[index]);
+ index++;
+ }
+ // advance until i == v.end()
+ index = 0;
+ for (ItemList::iterator i = list.begin(); i != list.end(); ++i) {
+ ASSERT_EQ(*i, items[index]);
+ index++;
+ }
+ list.clear();
+}
+
+TEST_F(XlistTest, move_around) {
+ Item item1(42), item2(17);
+ ItemList list;
+
+ // only a single element in the list
+ list.push_back(&item1.xitem);
+ ASSERT_EQ(&item1, list.front());
+ ASSERT_EQ(&item1, list.back());
+
+ list.push_back(&item2.xitem);
+ ASSERT_EQ(&item1, list.front());
+ ASSERT_EQ(&item2, list.back());
+
+ // move item2 to the front
+ list.push_front(&item2.xitem);
+ ASSERT_EQ(&item2, list.front());
+ ASSERT_EQ(&item1, list.back());
+
+ // and move it back
+ list.push_back(&item2.xitem);
+ ASSERT_EQ(&item1, list.front());
+ ASSERT_EQ(&item2, list.back());
+
+ list.clear();
+}
+
+TEST_F(XlistTest, item_queries) {
+ Item item(42);
+ ItemList list;
+ list.push_back(&item.xitem);
+
+ ASSERT_TRUE(item.xitem.is_on_list());
+ ASSERT_EQ(&list, item.xitem.get_list());
+
+ ASSERT_TRUE(item.xitem.remove_myself());
+ ASSERT_FALSE(item.xitem.is_on_list());
+ ASSERT_TRUE(item.xitem.get_list() == NULL);
+}
+
+// Local Variables:
+// compile-command: "cd .. ;
+// make unittest_xlist &&
+// ./unittest_xlist"
+// End:
diff --git a/src/test/testclass.cc b/src/test/testclass.cc
new file mode 100644
index 000000000..22a97be6d
--- /dev/null
+++ b/src/test/testclass.cc
@@ -0,0 +1,57 @@
+
+
+
+#include <iostream>
+#include <string.h>
+#include <stdlib.h>
+
+#include "objclass/objclass.h"
+
+CLS_VER(1,0)
+CLS_NAME(test)
+
+cls_handle_t h_class;
+
+cls_method_handle_t h_foo;
+
+int foo_method(cls_method_context_t ctx, char *indata, int datalen,
+ char **outdata, int *outdatalen)
+{
+ int i, r;
+
+ cls_log("hello world");
+ cls_log("indata=%s", indata);
+
+ *outdata = (char *)cls_alloc(128);
+ for (i=0; i<strlen(indata) + 1; i++) {
+ if (indata[i] == '0') {
+ (*outdata)[i] = '*';
+ } else {
+ (*outdata)[i] = indata[i];
+ }
+ }
+ *outdatalen = strlen(*outdata) + 1;
+ cls_log("outdata=%s", *outdata);
+
+ r = cls_call(ctx, "foo", "foo", *outdata, *outdatalen, outdata, outdatalen);
+
+ return r;
+}
+
+static cls_deps_t depend[] = {{"foo", "1.0"}, {"bar", "1.0"}, {NULL, NULL}};
+
+extern "C" cls_deps_t *class_deps()
+{
+ return depend;
+};
+
+void class_init()
+{
+ cls_log("Loaded class test!");
+
+ cls_register("test", &h_class);
+ cls_register_method(h_class, "foo", foo_method, &h_foo);
+
+ return;
+}
+
diff --git a/src/test/testcrypto.cc b/src/test/testcrypto.cc
new file mode 100644
index 000000000..2efb9b219
--- /dev/null
+++ b/src/test/testcrypto.cc
@@ -0,0 +1,60 @@
+#include "auth/Crypto.h"
+#include "common/Clock.h"
+
+#include "common/config.h"
+#include "common/debug.h"
+
+#define dout_subsys ceph_subsys_auth
+
+#define AES_KEY_LEN 16
+
+#define dout_context g_ceph_context
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ char aes_key[AES_KEY_LEN];
+ memset(aes_key, 0x77, sizeof(aes_key));
+ bufferptr keybuf(aes_key, sizeof(aes_key));
+ CryptoKey key(CEPH_CRYPTO_AES, ceph_clock_now(), keybuf);
+
+ const char *msg="hello! this is a message\n";
+ char pad[16];
+ memset(pad, 0, 16);
+ bufferptr ptr(msg, strlen(msg));
+ bufferlist enc_in;
+ enc_in.append(ptr);
+ enc_in.append(msg, strlen(msg));
+
+ bufferlist enc_out;
+ std::string error;
+ if (key.encrypt(g_ceph_context, enc_in, enc_out, &error) < 0) {
+ ceph_assert(!error.empty());
+ dout(0) << "couldn't encode! error " << error << dendl;
+ exit(1);
+ }
+
+ const char *enc_buf = enc_out.c_str();
+ for (unsigned i=0; i<enc_out.length(); i++) {
+ std::cout << hex << (int)(unsigned char)enc_buf[i] << dec << " ";
+ if (i && !(i%16))
+ std::cout << std::endl;
+ }
+
+ bufferlist dec_in, dec_out;
+
+ dec_in = enc_out;
+
+ if (key.decrypt(g_ceph_context, dec_in, dec_out, &error) < 0) {
+ ceph_assert(!error.empty());
+ dout(0) << "couldn't decode! error " << error << dendl;
+ exit(1);
+ }
+
+ dout(0) << "decoded len: " << dec_out.length() << dendl;
+ dout(0) << "decoded msg: " << dec_out.c_str() << dendl;
+
+ return 0;
+}
+
diff --git a/src/test/testkeys.cc b/src/test/testkeys.cc
new file mode 100644
index 000000000..85d0b5667
--- /dev/null
+++ b/src/test/testkeys.cc
@@ -0,0 +1,69 @@
+#include "auth/cephx/CephxKeyServer.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "common/config.h"
+#include "common/debug.h"
+
+#define dout_context g_ceph_context
+
+#define AES_KEY_LEN 16
+
+using namespace std;
+
+int main(int argc, const char **argv)
+{
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ KeyRing extra;
+ KeyServer server(g_ceph_context, &extra);
+
+ generic_dout(0) << "server created" << dendl;
+
+ getchar();
+
+#if 0
+ char aes_key[AES_KEY_LEN];
+ memset(aes_key, 0x77, sizeof(aes_key));
+ bufferptr keybuf(aes_key, sizeof(aes_key));
+ CryptoKey key(CEPH_CRYPTO_AES, ceph_clock_now(), keybuf);
+
+ const char *msg="hello! this is a message\n";
+ char pad[16];
+ memset(pad, 0, 16);
+ bufferptr ptr(msg, strlen(msg));
+ bufferlist enc_in;
+ enc_in.append(ptr);
+ enc_in.append(msg, strlen(msg));
+
+ bufferlist enc_out;
+ if (key.encrypt(enc_in, enc_out) < 0) {
+ derr(0) << "couldn't encode!" << dendl;
+ exit(1);
+ }
+
+ const char *enc_buf = enc_out.c_str();
+ for (unsigned i=0; i<enc_out.length(); i++) {
+ std::cout << hex << (int)(unsigned char)enc_buf[i] << dec << " ";
+ if (i && !(i%16))
+ std::cout << std::endl;
+ }
+
+ bufferlist dec_in, dec_out;
+
+ dec_in = enc_out;
+
+ if (key.decrypt(dec_in, dec_out) < 0) {
+ derr(0) << "couldn't decode!" << dendl;
+ }
+
+ dout(0) << "decoded len: " << dec_out.length() << dendl;
+ dout(0) << "decoded msg: " << dec_out.c_str() << dendl;
+
+ return 0;
+#endif
+}
+
diff --git a/src/test/testmsgr.cc b/src/test/testmsgr.cc
new file mode 100644
index 000000000..ae162a290
--- /dev/null
+++ b/src/test/testmsgr.cc
@@ -0,0 +1,144 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <sys/stat.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+#include "common/config.h"
+
+#include "mon/MonMap.h"
+#include "mon/MonClient.h"
+#include "msg/Messenger.h"
+#include "messages/MPing.h"
+
+#include "common/Timer.h"
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#define dout_subsys ceph_subsys_ms
+
+Messenger *messenger = 0;
+
+ceph::mutex test_lock = ceph::make_mutex("mylock");
+ceph::condition_variable cond;
+
+uint64_t received = 0;
+
+class Admin : public Dispatcher {
+public:
+ Admin()
+ : Dispatcher(g_ceph_context)
+ {
+ }
+private:
+ bool ms_dispatch(Message *m) {
+
+ //cerr << "got ping from " << m->get_source() << std::endl;
+ dout(0) << "got ping from " << m->get_source() << dendl;
+ test_lock.lock();
+ ++received;
+ cond.notify_all();
+ test_lock.unlock();
+
+ m->put();
+ return true;
+ }
+
+ bool ms_handle_reset(Connection *con) { return false; }
+ void ms_handle_remote_reset(Connection *con) {}
+ bool ms_handle_refused(Connection *con) { return false; }
+
+} dispatcher;
+
+
+int main(int argc, const char **argv, const char *envp[]) {
+
+ auto args = argv_to_vec(argc, argv);
+
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ dout(0) << "i am mon " << args[0] << dendl;
+
+ // get monmap
+ MonClient mc(g_ceph_context);
+ if (mc.build_initial_monmap() < 0)
+ return -1;
+
+ // start up network
+ int whoami = mc.monmap.get_rank(args[0]);
+ ceph_assert(whoami >= 0);
+ ostringstream ss;
+ ss << mc.monmap.get_addr(whoami);
+ std::string sss(ss.str());
+ g_ceph_context->_conf.set_val("public_addr", sss.c_str());
+ g_ceph_context->_conf.apply_changes(nullptr);
+ std::string public_msgr_type = g_conf()->ms_public_type.empty() ? g_conf().get_val<std::string>("ms_type") : g_conf()->ms_public_type;
+ Messenger *rank = Messenger::create(g_ceph_context,
+ public_msgr_type,
+ entity_name_t::MON(whoami), "tester",
+ getpid());
+ int err = rank->bind(g_ceph_context->_conf->public_addr);
+ if (err < 0)
+ return 1;
+
+ // start monitor
+ messenger = rank;
+ messenger->set_default_send_priority(CEPH_MSG_PRIO_HIGH);
+ messenger->add_dispatcher_head(&dispatcher);
+
+ rank->start();
+
+ int isend = 0;
+ if (whoami == 0)
+ isend = 100;
+
+ std::unique_lock l{test_lock};
+ uint64_t sent = 0;
+ while (1) {
+ while (received + isend <= sent) {
+ //cerr << "wait r " << received << " s " << sent << " is " << isend << std::endl;
+ dout(0) << "wait r " << received << " s " << sent << " is " << isend << dendl;
+ cond.wait(l);
+ }
+
+ int t = rand() % mc.get_num_mon();
+ if (t == whoami)
+ continue;
+
+ if (rand() % 10 == 0) {
+ //cerr << "mark_down " << t << std::endl;
+ dout(0) << "mark_down " << t << dendl;
+ messenger->mark_down_addrs(mc.get_mon_addrs(t));
+ }
+ //cerr << "pinging " << t << std::endl;
+ dout(0) << "pinging " << t << dendl;
+ messenger->send_to_mon(new MPing, mc.get_mon_addrs(t));
+ cerr << isend << "\t" << ++sent << "\t" << received << "\r";
+ }
+ l.unlock();
+
+ // wait for messenger to finish
+ rank->wait();
+
+ return 0;
+}
+
diff --git a/src/test/ubuntu-18.04/Dockerfile.in b/src/test/ubuntu-18.04/Dockerfile.in
new file mode 100644
index 000000000..fa9de967e
--- /dev/null
+++ b/src/test/ubuntu-18.04/Dockerfile.in
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2016 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+# Environment variables are substituted via envsubst(1)
+#
+# user_id=$(id -u)
+# os_version= the desired REPOSITORY TAG
+#
+FROM ubuntu:%%os_version%%
+
+COPY install-deps.sh /root/
+RUN mkdir /root/debian
+COPY debian /root/debian/
+RUN apt-get update
+# build dependencies
+RUN cd /root ; DEBIAN_FRONTEND=noninteractive ./install-deps.sh
+# development tools
+RUN apt-get install -y ccache valgrind gdb gdisk kpartx jq xmlstarlet sudo
+RUN if test %%USER%% != root ; then useradd -M --uid %%user_id%% %%USER%% && echo '%%USER%% ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ; fi
diff --git a/src/test/ubuntu-18.04/debian b/src/test/ubuntu-18.04/debian
new file mode 120000
index 000000000..61c3c9f50
--- /dev/null
+++ b/src/test/ubuntu-18.04/debian
@@ -0,0 +1 @@
+../../../debian/ \ No newline at end of file
diff --git a/src/test/ubuntu-18.04/install-deps.sh b/src/test/ubuntu-18.04/install-deps.sh
new file mode 120000
index 000000000..fc9c78b27
--- /dev/null
+++ b/src/test/ubuntu-18.04/install-deps.sh
@@ -0,0 +1 @@
+../../../install-deps.sh \ No newline at end of file
diff --git a/src/test/ubuntu-20.04 b/src/test/ubuntu-20.04
new file mode 120000
index 000000000..1a2d4034e
--- /dev/null
+++ b/src/test/ubuntu-20.04
@@ -0,0 +1 @@
+ubuntu-18.04 \ No newline at end of file
diff --git a/src/test/unit.cc b/src/test/unit.cc
new file mode 100644
index 000000000..d845f8bee
--- /dev/null
+++ b/src/test/unit.cc
@@ -0,0 +1,48 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_UNIT_TEST_H
+#define CEPH_UNIT_TEST_H
+
+#include "include/types.h" // FIXME: ordering shouldn't be important, but right
+ // now, this include has to come before the others.
+
+#include "common/ceph_argparse.h"
+#include "common/code_environment.h"
+#include "common/config.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "include/msgr.h" // for CEPH_ENTITY_TYPE_CLIENT
+#include "gtest/gtest.h"
+
+#include <vector>
+
+/*
+ * You only need to include this file if you are testing Ceph internal code. If
+ * you are testing library code, the library init() interfaces will handle
+ * initialization for you.
+ */
+int main(int argc, char **argv) {
+ std::vector<const char*> args(argv, argv + argc);
+ auto cct = global_init(NULL, args,
+ CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_MON_CONFIG);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+#endif
diff --git a/src/test/utf8.cc b/src/test/utf8.cc
new file mode 100644
index 000000000..8e22e524e
--- /dev/null
+++ b/src/test/utf8.cc
@@ -0,0 +1,66 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include "common/utf8.h"
+#include "gtest/gtest.h"
+#include <stdint.h>
+
+TEST(IsValidUtf8, SimpleAscii) {
+ ASSERT_EQ(0, check_utf8_cstr("Ascii ftw."));
+ ASSERT_EQ(0, check_utf8_cstr(""));
+ ASSERT_EQ(0, check_utf8_cstr("B"));
+ ASSERT_EQ(0, check_utf8_cstr("Badgers badgers badgers badgers "
+ "mushroom mushroom"));
+ ASSERT_EQ(0, check_utf8("foo", strlen("foo")));
+}
+
+TEST(IsValidUtf8, ControlChars) {
+ // Sadly, control characters are valid utf8...
+ uint8_t control_chars[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d };
+ ASSERT_EQ(0, check_utf8((char*)control_chars, sizeof(control_chars)));
+}
+
+TEST(IsValidUtf8, SimpleUtf8) {
+ uint8_t funkystr[] = { 0x66, 0xd1, 0x86, 0xd1, 0x9d, 0xd2, 0xa0, 0xd3,
+ 0xad, 0xd3, 0xae, 0x0a };
+ ASSERT_EQ(0, check_utf8((char*)funkystr, sizeof(funkystr)));
+
+ uint8_t valid2[] = { 0xc3, 0xb1 };
+ ASSERT_EQ(0, check_utf8((char*)valid2, sizeof(valid2)));
+}
+
+TEST(IsValidUtf8, InvalidUtf8) {
+ uint8_t inval[] = { 0xe2, 0x28, 0xa1 };
+ ASSERT_NE(0, check_utf8((char*)inval, sizeof(inval)));
+
+ uint8_t invalid2[] = { 0xc3, 0x28 };
+ ASSERT_NE(0, check_utf8((char*)invalid2, sizeof(invalid2)));
+}
+
+TEST(HasControlChars, HasControlChars1) {
+ uint8_t has_control_chars[] = { 0x41, 0x01, 0x00 };
+ ASSERT_NE(0, check_for_control_characters_cstr((const char*)has_control_chars));
+ uint8_t has_control_chars2[] = { 0x7f, 0x41, 0x00 };
+ ASSERT_NE(0, check_for_control_characters_cstr((const char*)has_control_chars2));
+
+ char has_newline[] = "blah blah\n";
+ ASSERT_NE(0, check_for_control_characters_cstr(has_newline));
+
+ char no_control_chars[] = "blah blah";
+ ASSERT_EQ(0, check_for_control_characters_cstr(no_control_chars));
+
+ uint8_t validutf[] = { 0x66, 0xd1, 0x86, 0xd1, 0x9d, 0xd2, 0xa0, 0xd3,
+ 0xad, 0xd3, 0xae, 0x0 };
+ ASSERT_EQ(0, check_for_control_characters_cstr((const char*)validutf));
+}
diff --git a/src/test/vstart_wrapper.sh b/src/test/vstart_wrapper.sh
new file mode 100755
index 000000000..7bd3dbe33
--- /dev/null
+++ b/src/test/vstart_wrapper.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+# Copyright (C) 2015 Red Hat <contact@redhat.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+
+source $CEPH_ROOT/qa/standalone/ceph-helpers.sh
+
+export CEPH_VSTART_WRAPPER=1
+export CEPH_DIR="${TMPDIR:-$PWD}/td/t-$CEPH_PORT"
+export CEPH_DEV_DIR="$CEPH_DIR/dev"
+export CEPH_OUT_DIR="$CEPH_DIR/out"
+export CEPH_ASOK_DIR="$CEPH_DIR/out"
+
+export MGR_PYTHON_PATH=$CEPH_ROOT/src/pybind/mgr
+
+function vstart_setup()
+{
+ rm -fr $CEPH_DEV_DIR $CEPH_OUT_DIR
+ mkdir -p $CEPH_DEV_DIR
+ trap "teardown $CEPH_DIR" EXIT
+ export LC_ALL=C # some tests are vulnerable to i18n
+ export PATH="$(pwd):${PATH}"
+ OBJSTORE_ARGS=""
+ if [ "bluestore" = "${CEPH_OBJECTSTORE}" ]; then
+ OBJSTORE_ARGS="-b"
+ fi
+ $CEPH_ROOT/src/vstart.sh \
+ --short \
+ $OBJSTORE_ARGS \
+ -o 'paxos propose interval = 0.01' \
+ -d -n -l || return 1
+ export CEPH_CONF=$CEPH_DIR/ceph.conf
+
+ crit=$(expr 100 - $(ceph-conf --show-config-value mon_data_avail_crit))
+ if [ $(df . | perl -ne 'print if(s/.*\s(\d+)%.*/\1/)') -ge $crit ] ; then
+ df .
+ cat <<EOF
+error: not enough free disk space for mon to run
+The mon will shutdown with a message such as
+ "reached critical levels of available space on local monitor storage -- shutdown!"
+as soon as it finds the disk has is more than ${crit}% full.
+This is a limit determined by
+ ceph-conf --show-config-value mon_data_avail_crit
+EOF
+ return 1
+ fi
+}
+
+function main()
+{
+ teardown $CEPH_DIR
+ vstart_setup || return 1
+ if CEPH_CONF=$CEPH_DIR/ceph.conf "$@"; then
+ code=0
+ else
+ code=1
+ display_logs $CEPH_OUT_DIR
+ fi
+ return $code
+}
+
+main "$@"
diff --git a/src/test/xattr_bench.cc b/src/test/xattr_bench.cc
new file mode 100644
index 000000000..b117e16f2
--- /dev/null
+++ b/src/test/xattr_bench.cc
@@ -0,0 +1,196 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include "os/bluestore/BlueStore.h"
+#include "include/Context.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "global/global_init.h"
+#include <boost/scoped_ptr.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/binomial_distribution.hpp>
+#include <gtest/gtest.h>
+
+#include "include/unordered_map.h"
+
+void usage(const string &name) {
+ std::cerr << "Usage: " << name << " [xattr|omap] store_path"
+ << std::endl;
+}
+
+const int THREADS = 5;
+
+template <typename T>
+typename T::iterator rand_choose(T &cont) {
+ if (std::empty(cont) == 0) {
+ return std::end(cont);
+ }
+ return std::next(std::begin(cont), rand() % cont.size());
+}
+
+class OnApplied : public Context {
+public:
+ ceph::mutex *lock;
+ ceph::condition_variable *cond;
+ int *in_progress;
+ ObjectStore::Transaction *t;
+ OnApplied(ceph::mutex *lock,
+ ceph::condition_variable *cond,
+ int *in_progress,
+ ObjectStore::Transaction *t)
+ : lock(lock), cond(cond),
+ in_progress(in_progress), t(t) {
+ std::lock_guard l{*lock};
+ (*in_progress)++;
+ }
+
+ void finish(int r) override {
+ std::lock_guard l{*lock};
+ (*in_progress)--;
+ cond->notify_all();
+ }
+};
+
+uint64_t get_time() {
+ time_t start;
+ time(&start);
+ return start * 1000;
+}
+
+double print_time(uint64_t ms) {
+ return ((double)ms)/1000;
+}
+
+uint64_t do_run(ObjectStore *store, int attrsize, int numattrs,
+ int run,
+ int transsize, int ops,
+ ostream &out) {
+ ceph::mutex lock = ceph::make_mutex("lock");
+ ceph::condition_variable cond;
+ int in_flight = 0;
+ ObjectStore::Sequencer osr(__func__);
+ ObjectStore::Transaction t;
+ map<coll_t, pair<set<string>, ObjectStore::Sequencer*> > collections;
+ for (int i = 0; i < 3*THREADS; ++i) {
+ coll_t coll(spg_t(pg_t(0, i + 1000*run), shard_id_t::NO_SHARD));
+ t.create_collection(coll, 0);
+ set<string> objects;
+ for (int i = 0; i < transsize; ++i) {
+ stringstream obj_str;
+ obj_str << i;
+ t.touch(coll,
+ ghobject_t(hobject_t(sobject_t(obj_str.str(), CEPH_NOSNAP))));
+ objects.insert(obj_str.str());
+ }
+ collections[coll] = make_pair(objects, new ObjectStore::Sequencer(coll.to_str()));
+ }
+ store->queue_transaction(&osr, std::move(t));
+
+ bufferlist bl;
+ for (int i = 0; i < attrsize; ++i) {
+ bl.append('\0');
+ }
+
+ uint64_t start = get_time();
+ for (int i = 0; i < ops; ++i) {
+ {
+ std::unique_lock l{lock};
+ cond.wait(l, [&] { in_flight < THREADS; });
+ }
+ ObjectStore::Transaction *t = new ObjectStore::Transaction;
+ map<coll_t, pair<set<string>, ObjectStore::Sequencer*> >::iterator iter =
+ rand_choose(collections);
+ for (set<string>::iterator obj = iter->second.first.begin();
+ obj != iter->second.first.end();
+ ++obj) {
+ for (int j = 0; j < numattrs; ++j) {
+ stringstream ss;
+ ss << i << ", " << j << ", " << *obj;
+ t->setattr(iter->first,
+ ghobject_t(hobject_t(sobject_t(*obj, CEPH_NOSNAP))),
+ ss.str().c_str(),
+ bl);
+ }
+ }
+ store->queue_transaction(iter->second.second, std::move(*t),
+ new OnApplied(&lock, &cond, &in_flight,
+ t));
+ delete t;
+ }
+ {
+ std::unique_lock l{lock};
+ cond.wait(l, [&] { return in_flight == 0; });
+ }
+ return get_time() - start;
+}
+
+int main(int argc, char **argv) {
+ auto args = argv_to_vec(argc, argv);
+ if (args.empty()) {
+ cerr << argv[0] << ": -h or --help for usage" << std::endl;
+ exit(1);
+ }
+ if (ceph_argparse_need_usage(args)) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ auto cct = global_init(0, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ std::cerr << "args: " << args << std::endl;
+ if (args.size() < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ string store_path(args[1]);
+
+ boost::scoped_ptr<ObjectStore> store(new BlueStore(cct.get(), store_path));
+
+ std::cerr << "mkfs starting" << std::endl;
+ ceph_assert(!store->mkfs());
+ ceph_assert(!store->mount());
+ std::cerr << "mounted" << std::endl;
+
+ std::cerr << "attrsize\tnumattrs\ttranssize\tops\ttime" << std::endl;
+ int runs = 0;
+ int total_size = 11;
+ for (int i = 6; i < total_size; ++i) {
+ for (int j = (total_size - i); j >= 0; --j) {
+ std::cerr << "starting run " << runs << std::endl;
+ ++runs;
+ uint64_t time = do_run(store.get(), (1 << i), (1 << j), runs,
+ 10,
+ 1000, std::cout);
+ std::cout << (1 << i) << "\t"
+ << (1 << j) << "\t"
+ << 10 << "\t"
+ << 1000 << "\t"
+ << print_time(time) << std::endl;
+ }
+ }
+ store->umount();
+ return 0;
+}